diff --git a/args/bosses.py b/args/bosses.py index 2854fc1c..8552b3cc 100644 --- a/args/bosses.py +++ b/args/bosses.py @@ -14,6 +14,8 @@ def parse(parser): help = "Boss battles shuffled") bosses_battles.add_argument("-bbr", "--boss-battles-random", action = "store_true", help = "Boss battles randomized") + bosses_battles.add_argument("-bbws", "--boss-battles-world-shuffle", action = "store_true", + help = "Boss battles shuffled by world") dragons = bosses.add_mutually_exclusive_group() dragons.add_argument("-drloc", "--dragon-boss-location", default = DEFAULT_DRAGON_PROTOCOL, type = str.lower, choices = BossLocations.ALL, @@ -41,7 +43,7 @@ def process(args): args.dragon_boss_location = BossLocations.MIX args.mix_bosses_dragons = None # if neither shuffling or randomizing bosses, and we try to mix the dragons/statues, simply shuffle them instead - vanilla_locations = not (args.boss_battles_shuffle or args.boss_battles_random) + vanilla_locations = not (args.boss_battles_shuffle or args.boss_battles_random or args.boss_battles_world_shuffle) if vanilla_locations and args.dragon_boss_location == BossLocations.MIX: args.dragon_boss_location = BossLocations.SHUFFLE if vanilla_locations and args.statue_boss_location == BossLocations.MIX: @@ -54,6 +56,8 @@ def flags(args): flags += " -bbs" elif args.boss_battles_random: flags += " -bbr" + elif args.boss_battles_world_shuffle: + flags += " -bbws" if args.dragon_boss_location: flags += f" -drloc {args.dragon_boss_location}" @@ -82,6 +86,8 @@ def options(args): boss_battles = "Shuffle" elif args.boss_battles_random: boss_battles = "Random" + elif args.boss_battles_world_shuffle: + boss_battles = "WShuffle" dragon_battles = DEFAULT_DRAGON_PROTOCOL if args.dragon_boss_location: diff --git a/data/bosses.py b/data/bosses.py index 60f355e6..a7327dbe 100644 --- a/data/bosses.py +++ b/data/bosses.py @@ -99,6 +99,52 @@ 401 : "MagiMaster", } +wor_pack_name = { + 338 : "SrBehemoth", + 340 : "Tentacles", + 341 : "Dullahan", + 342 : "Chadarnook", + 346 : "Stooges", + 348 : "Wrexsoul", + 349 : "Doom Gaze", + 350 : "Hidon", + 354 : "Doom", + 355 : "Goddess", + 356 : "Poltrgeist", + 368 : "Atma", + 370 : "Inferno", + 373 : "Umaro", + 375 : "Tritoch", + 386 : "Phunbaba 3", + 387 : "Phunbaba 4", + 396 : "Guardian", # defeatable guardian in kefka's tower + 401 : "MagiMaster", +} + +wob_pack_name = { + 262 : "Marshal", + 274 : "Rizopas", + 302 : "Leader", + 313 : "Kefka (Narshe)", + 320 : "Whelk", + 322 : "Vargas", + 323 : "TunnelArmr", + 324 : "GhostTrain", + 325 : "Dadaluma", + 326 : "Ifrit/Shiva", + 327 : "Cranes", + 328 : "Number 024", + 329 : "Number 128", + 335 : "FlameEater", + 336 : "AtmaWeapon", + 337 : "Nerapa", + 345 : "Air Force", + 359 : "Ultros 1", + 360 : "Ultros 2", + 363 : "Ultros/Chupon", + 381 : "Ultros 3", +} + # These ids are repeated in normal_pack_name as well # This is intentional as they are used to iterate over ALL bosses for things like objective conditions statue_pack_name = { diff --git a/data/enemy_formations.py b/data/enemy_formations.py index 8c672e8b..9d4a9ec5 100644 --- a/data/enemy_formations.py +++ b/data/enemy_formations.py @@ -163,7 +163,7 @@ def mod(self): self.formations[387].event_script = 0 self.formations[387].enable_event_script = 0 - if self.args.boss_battles_shuffle or self.args.boss_battles_random: + if self.args.boss_battles_shuffle or self.args.boss_battles_random or self.args.boss_battles_world_shuffle: # second srbehemoth only appears as a front attack with shuffled/random boss battles self.formations[424].disable_front_attack = 0 self.formations[424].disable_back_attack = 1 diff --git a/data/enemy_packs.py b/data/enemy_packs.py index dae38805..0faee280 100644 --- a/data/enemy_packs.py +++ b/data/enemy_packs.py @@ -59,6 +59,34 @@ def _replaceable_bosses(self): return replaceable + self._replaceable_dragons() + self._replaceable_statues() + # Returns the list of all world of ruin boss packs that can be used during randomization + def _replaceable_wor_bosses(self): + dragon_packs = list(bosses.dragon_pack_name) + statue_packs = list(bosses.statue_pack_name) + boss_packs = list(bosses.wor_pack_name) + replaceable = [boss for boss in boss_packs if boss not in statue_packs and boss not in dragon_packs] + + if not self.args.shuffle_random_phunbaba3: + self.event_boss_replacements[self.PHUNBABA3] = self.PHUNBABA3 + if self.PHUNBABA3 in replaceable: + replaceable.remove(self.PHUNBABA3) + + if not self.args.doom_gaze_no_escape: + # if doom gaze can escape, don't shuffle/randomize him + # possibly having multiple doom gazes while trying to keep track of hp is awkward + # how would that work with him being in his original spot and the others? How to know when to get bahamut esper? + self.event_boss_replacements[self.DOOM_GAZE] = self.DOOM_GAZE + + if self.DOOM_GAZE in replaceable: + replaceable.remove(self.DOOM_GAZE) + + return replaceable + self._replaceable_dragons() + self._replaceable_statues() + + # Returns the list of all world of balance boss packs that can be used during randomization + def _replaceable_wob_bosses(self): + replaceable = list(bosses.wob_pack_name) + return replaceable + # Statue locations that become available for the general boss pool def _replaceable_statues(self): import random @@ -145,6 +173,26 @@ def shuffle_event_bosses(self): self.phunbaba3_safety_check(bosses_to_replace) + def world_shuffle_event_bosses(self): + import random + + wob_bosses_to_replace = self._replaceable_wob_bosses() + wob_bosses_possible = wob_bosses_to_replace.copy() + + random.shuffle(wob_bosses_possible) + for index, boss in enumerate(wob_bosses_to_replace): + self.event_boss_replacements[boss] = wob_bosses_possible[index] + + wor_bosses_to_replace = self._replaceable_wor_bosses() + wor_bosses_possible = wor_bosses_to_replace.copy() + + random.shuffle(wor_bosses_possible) + for index, boss in enumerate(wor_bosses_to_replace): + self.event_boss_replacements[boss] = wor_bosses_possible[index] + + # I don't think this is needed because phunbaba 3 should be in wor but just in case + self.phunbaba3_safety_check(wob_bosses_to_replace) + def randomize_event_bosses(self): import args, random, objectives from constants.objectives.conditions import names as possible_condition_names @@ -377,6 +425,9 @@ def mod(self): self.shuffle_event_bosses() elif self.args.boss_battles_random: self.randomize_event_bosses() + elif self.args.boss_battles_world_shuffle: + self.world_shuffle_event_bosses() + self._handle_original_shuffle_dragons() self._handle_original_shuffle_statues() diff --git a/data/enemy_scripts.py b/data/enemy_scripts.py index 0655e70a..890d8167 100644 --- a/data/enemy_scripts.py +++ b/data/enemy_scripts.py @@ -281,13 +281,13 @@ def mod(self): if self.args.doom_gaze_no_escape: self.doom_gaze_no_escape_mod() - if self.args.boss_battles_shuffle or self.args.boss_battles_random: + if self.args.boss_battles_shuffle or self.args.boss_battles_random or self.args.boss_battles_world_shuffle: self.doom_gaze_event_bit_mod() if self.args.wrexsoul_no_zinger: self.wrexsoul_no_zinger_mod() - if self.args.boss_battles_shuffle or self.args.boss_battles_random: + if self.args.boss_battles_shuffle or self.args.boss_battles_random or self.args.boss_battles_world_shuffle: # the animation chadarnook uses to switch between demon and painting # breaks with other battle backgrounds, they turn weird colors and look very glitchy self.chadarnook_flashing_mod() diff --git a/event/event.py b/event/event.py index ce27df83..90d433de 100644 --- a/event/event.py +++ b/event/event.py @@ -55,7 +55,7 @@ def init_event_bits(self, space): def get_boss(self, original_boss_name, log_change = True): pack_id = self.enemies.get_event_boss(original_boss_name) - if (self.args.boss_battles_shuffle or self.args.boss_battles_random) and log_change: + if (self.args.boss_battles_shuffle or self.args.boss_battles_random or self.args.boss_battles_world_shuffle) and log_change: boss_name = self.enemies.packs.get_name(pack_id) self.log_change(original_boss_name, boss_name) return pack_id