18
18
from .rom_addresses import rom_addresses
19
19
from .text import encode_text
20
20
from .rom import generate_output , get_base_rom_bytes , get_base_rom_path , RedDeltaPatch , BlueDeltaPatch
21
- from .pokemon import process_pokemon_data , process_move_data
21
+ from .pokemon import process_pokemon_data , process_move_data , verify_hm_moves
22
22
from .encounters import process_pokemon_locations , process_trainer_data
23
23
from .rules import set_rules
24
24
from .level_scaling import level_scaling
@@ -279,12 +279,12 @@ def stage_fill_hook(cls, multiworld, progitempool, usefulitempool, filleritempoo
279
279
def fill_hook (self , progitempool , usefulitempool , filleritempool , fill_locations ):
280
280
if not self .multiworld .badgesanity [self .player ]:
281
281
# Door Shuffle options besides Simple place badges during door shuffling
282
- if not self .multiworld .door_shuffle [self .player ] not in ("off" , "simple" ):
282
+ if self .multiworld .door_shuffle [self .player ] in ("off" , "simple" ):
283
283
badges = [item for item in progitempool if "Badge" in item .name and item .player == self .player ]
284
284
for badge in badges :
285
285
self .multiworld .itempool .remove (badge )
286
286
progitempool .remove (badge )
287
- for _ in range (5 ):
287
+ for attempt in range (6 ):
288
288
badgelocs = [
289
289
self .multiworld .get_location (loc , self .player ) for loc in [
290
290
"Pewter Gym - Brock Prize" , "Cerulean Gym - Misty Prize" ,
@@ -293,6 +293,12 @@ def fill_hook(self, progitempool, usefulitempool, filleritempool, fill_locations
293
293
"Cinnabar Gym - Blaine Prize" , "Viridian Gym - Giovanni Prize"
294
294
] if self .multiworld .get_location (loc , self .player ).item is None ]
295
295
state = self .multiworld .get_all_state (False )
296
+ # Give it two tries to place badges with wild Pokemon and learnsets as-is.
297
+ # If it can't, then try with all Pokemon collected, and we'll try to fix HM move availability after.
298
+ if attempt > 1 :
299
+ for mon in poke_data .pokemon_data .keys ():
300
+ state .collect (self .create_item (mon ), True )
301
+ state .sweep_for_events ()
296
302
self .multiworld .random .shuffle (badges )
297
303
self .multiworld .random .shuffle (badgelocs )
298
304
badgelocs_copy = badgelocs .copy ()
@@ -312,6 +318,7 @@ def fill_hook(self, progitempool, usefulitempool, filleritempool, fill_locations
312
318
break
313
319
else :
314
320
raise FillError (f"Failed to place badges for player { self .player } " )
321
+ verify_hm_moves (self .multiworld , self , self .player )
315
322
316
323
if self .multiworld .key_items_only [self .player ]:
317
324
return
@@ -355,97 +362,14 @@ def pre_fill(self) -> None:
355
362
for location in self .multiworld .get_locations (self .player ):
356
363
if location .name in locs :
357
364
location .show_in_spoiler = False
358
-
359
- def intervene (move , test_state ):
360
- move_bit = pow (2 , poke_data .hm_moves .index (move ) + 2 )
361
- viable_mons = [mon for mon in self .local_poke_data if self .local_poke_data [mon ]["tms" ][6 ] & move_bit ]
362
- if self .multiworld .randomize_wild_pokemon [self .player ] and viable_mons :
363
- accessible_slots = [loc for loc in self .multiworld .get_reachable_locations (test_state , self .player ) if
364
- loc .type == "Wild Encounter" ]
365
-
366
- def number_of_zones (mon ):
367
- zones = set ()
368
- for loc in [slot for slot in accessible_slots if slot .item .name == mon ]:
369
- zones .add (loc .name .split (" - " )[0 ])
370
- return len (zones )
371
-
372
- placed_mons = [slot .item .name for slot in accessible_slots ]
373
-
374
- if self .multiworld .area_1_to_1_mapping [self .player ]:
375
- placed_mons .sort (key = lambda i : number_of_zones (i ))
376
- else :
377
- # this sort method doesn't work if you reference the same list being sorted in the lambda
378
- placed_mons_copy = placed_mons .copy ()
379
- placed_mons .sort (key = lambda i : placed_mons_copy .count (i ))
380
-
381
- placed_mon = placed_mons .pop ()
382
- replace_mon = self .multiworld .random .choice (viable_mons )
383
- replace_slot = self .multiworld .random .choice ([slot for slot in accessible_slots if slot .item .name
384
- == placed_mon ])
385
- if self .multiworld .area_1_to_1_mapping [self .player ]:
386
- zone = " - " .join (replace_slot .name .split (" - " )[:- 1 ])
387
- replace_slots = [slot for slot in accessible_slots if slot .name .startswith (zone ) and slot .item .name
388
- == placed_mon ]
389
- for replace_slot in replace_slots :
390
- replace_slot .item = self .create_item (replace_mon )
391
- else :
392
- replace_slot .item = self .create_item (replace_mon )
393
- else :
394
- tms_hms = self .local_tms + poke_data .hm_moves
395
- flag = tms_hms .index (move )
396
- mon_list = [mon for mon in poke_data .pokemon_data .keys () if test_state .has (mon , self .player )]
397
- self .multiworld .random .shuffle (mon_list )
398
- mon_list .sort (key = lambda mon : self .local_move_data [move ]["type" ] not in
399
- [self .local_poke_data [mon ]["type1" ], self .local_poke_data [mon ]["type2" ]])
400
- for mon in mon_list :
401
- if test_state .has (mon , self .player ):
402
- self .local_poke_data [mon ]["tms" ][int (flag / 8 )] |= 1 << (flag % 8 )
403
- break
404
-
405
- last_intervene = None
406
- while True :
407
- intervene_move = None
408
- test_state = self .multiworld .get_all_state (False )
409
- if not logic .can_learn_hm (test_state , "Surf" , self .player ):
410
- intervene_move = "Surf"
411
- elif not logic .can_learn_hm (test_state , "Strength" , self .player ):
412
- intervene_move = "Strength"
413
- # cut may not be needed if accessibility is minimal, unless you need all 8 badges and badgesanity is off,
414
- # as you will require cut to access celadon gyn
415
- elif ((not logic .can_learn_hm (test_state , "Cut" , self .player )) and
416
- (self .multiworld .accessibility [self .player ] != "minimal" or ((not
417
- self .multiworld .badgesanity [self .player ]) and max (
418
- self .multiworld .elite_four_badges_condition [self .player ],
419
- self .multiworld .route_22_gate_condition [self .player ],
420
- self .multiworld .victory_road_condition [self .player ])
421
- > 7 ) or (self .multiworld .door_shuffle [self .player ] not in ("off" , "simple" )))):
422
- intervene_move = "Cut"
423
- elif ((not logic .can_learn_hm (test_state , "Flash" , self .player ))
424
- and self .multiworld .dark_rock_tunnel_logic [self .player ]
425
- and (self .multiworld .accessibility [self .player ] != "minimal"
426
- or self .multiworld .door_shuffle [self .player ])):
427
- intervene_move = "Flash"
428
- # If no Pokémon can learn Fly, then during door shuffle it would simply not treat the free fly maps
429
- # as reachable, and if on no door shuffle or simple, fly is simply never necessary.
430
- # We only intervene if a Pokémon is able to learn fly but none are reachable, as that would have been
431
- # considered in door shuffle.
432
- elif ((not logic .can_learn_hm (test_state , "Fly" , self .player ))
433
- and self .multiworld .door_shuffle [self .player ] not in
434
- ("off" , "simple" ) and [self .fly_map , self .town_map_fly_map ] != ["Pallet Town" , "Pallet Town" ]):
435
- intervene_move = "Fly"
436
- if intervene_move :
437
- if intervene_move == last_intervene :
438
- raise Exception (f"Caught in infinite loop attempting to ensure { intervene_move } is available to player { self .player } " )
439
- intervene (intervene_move , test_state )
440
- last_intervene = intervene_move
441
- else :
442
- break
365
+ verify_hm_moves (self .multiworld , self , self .player )
443
366
444
367
# Delete evolution events for Pokémon that are not in logic in an all_state so that accessibility check does not
445
368
# fail. Re-use test_state from previous final loop.
369
+ all_state = self .multiworld .get_all_state (False )
446
370
evolutions_region = self .multiworld .get_region ("Evolution" , self .player )
447
371
for location in evolutions_region .locations .copy ():
448
- if not test_state .can_reach (location , player = self .player ):
372
+ if not all_state .can_reach (location , player = self .player ):
449
373
evolutions_region .locations .remove (location )
450
374
451
375
if self .multiworld .old_man [self .player ] == "early_parcel" :
0 commit comments