From 9f75313d25f05fa31440c573856f9335bcbc9bd5 Mon Sep 17 00:00:00 2001 From: Nolan <10077353+nolanlocke@users.noreply.github.com> Date: Mon, 17 Jan 2022 07:09:20 -0500 Subject: [PATCH 01/67] add -bmbs flag: adds statues to shuffle pool --- args/bosses.py | 5 +++++ data/enemy_packs.py | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/args/bosses.py b/args/bosses.py index 29bffd56..3713ae54 100644 --- a/args/bosses.py +++ b/args/bosses.py @@ -11,6 +11,8 @@ def parse(parser): help = "Boss battles randomized") bosses.add_argument("-bmbd", "--mix-bosses-dragons", action = "store_true", help = "Shuffle/randomize bosses and dragons together") + bosses.add_argument("-bmbs", "--mix-bosses-statues", action = "store_true", + help = "Shuffle/randomize bosses and statues together") bosses.add_argument("-srp3", "--shuffle-random-phunbaba3", action = "store_true", help = "Apply Shuffle/Random to Phunbaba 3 (otherwise he will only appear in Mobliz WOR)") bosses.add_argument("-bnds", "--boss-normalize-distort-stats", action = "store_true", @@ -33,6 +35,8 @@ def flags(args): if args.mix_bosses_dragons: flags += " -bmbd" + if args.mix_bosses_statues: + flags += " -bmbs" if args.shuffle_random_phunbaba3: flags += " -srp3" if args.boss_normalize_distort_stats: @@ -54,6 +58,7 @@ def options(args): return [ ("Boss Battles", boss_battles), ("Mix Bosses & Dragons", args.mix_bosses_dragons), + ("Mix Bosses & Statues", args.mix_bosses_statues), ("Shuffle/Random Phunbaba 3", args.shuffle_random_phunbaba3), ("Normalize & Distort Stats", args.boss_normalize_distort_stats), ("Boss Experience", args.boss_experience), diff --git a/data/enemy_packs.py b/data/enemy_packs.py index e4415010..ae8bee98 100644 --- a/data/enemy_packs.py +++ b/data/enemy_packs.py @@ -18,6 +18,10 @@ class EnemyPacks(): PHUNBABA3 = 386 DOOM_GAZE = 349 + DOOM=354 + GODDESS=355 + POLTERGEIST=356 + def __init__(self, rom, args, formations): self.rom = rom self.args = args @@ -37,6 +41,10 @@ def __init__(self, rom, args, formations): def _replaceable_bosses(self): replaceable = list(bosses.normal_pack_name) + if not self.args.mix_bosses_statues: + for boss in [self.DOOM, self.GODDESS, self.POLTERGEIST]: + replaceable.remove(boss) + self.event_boss_replacements[boss] = boss if not self.args.shuffle_random_phunbaba3: replaceable.remove(self.PHUNBABA3) self.event_boss_replacements[self.PHUNBABA3] = self.PHUNBABA3 From a0518cda5aff98d6b59dc16eec44b579a6c3bb60 Mon Sep 17 00:00:00 2001 From: Nolan <10077353+nolanlocke@users.noreply.github.com> Date: Mon, 17 Jan 2022 07:10:13 -0500 Subject: [PATCH 02/67] add context for statue ids being in EnemyPacks --- data/enemy_packs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/data/enemy_packs.py b/data/enemy_packs.py index ae8bee98..9f55055e 100644 --- a/data/enemy_packs.py +++ b/data/enemy_packs.py @@ -18,6 +18,7 @@ class EnemyPacks(): PHUNBABA3 = 386 DOOM_GAZE = 349 + # statues removed from shuffle pool with -bmbs flag DOOM=354 GODDESS=355 POLTERGEIST=356 From c46f27e6e614c5d05806c7138f6072cdc0ed6b75 Mon Sep 17 00:00:00 2001 From: Nolan <10077353+nolanlocke@users.noreply.github.com> Date: Wed, 19 Jan 2022 07:54:11 -0500 Subject: [PATCH 03/67] set default true ffor backwards compatibility --- args/bosses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/args/bosses.py b/args/bosses.py index 3713ae54..5909cd8f 100644 --- a/args/bosses.py +++ b/args/bosses.py @@ -11,7 +11,7 @@ def parse(parser): help = "Boss battles randomized") bosses.add_argument("-bmbd", "--mix-bosses-dragons", action = "store_true", help = "Shuffle/randomize bosses and dragons together") - bosses.add_argument("-bmbs", "--mix-bosses-statues", action = "store_true", + bosses.add_argument("-bmbs", "--mix-bosses-statues", action = "store_true", default = True, help = "Shuffle/randomize bosses and statues together") bosses.add_argument("-srp3", "--shuffle-random-phunbaba3", action = "store_true", help = "Apply Shuffle/Random to Phunbaba 3 (otherwise he will only appear in Mobliz WOR)") From 937a9fb450ebea341c280599f2a23676df85bfe8 Mon Sep 17 00:00:00 2001 From: Nolan <10077353+nolanlocke@users.noreply.github.com> Date: Wed, 19 Jan 2022 08:06:08 -0500 Subject: [PATCH 04/67] remove default as no way to turn it off --- args/bosses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/args/bosses.py b/args/bosses.py index 5909cd8f..3713ae54 100644 --- a/args/bosses.py +++ b/args/bosses.py @@ -11,7 +11,7 @@ def parse(parser): help = "Boss battles randomized") bosses.add_argument("-bmbd", "--mix-bosses-dragons", action = "store_true", help = "Shuffle/randomize bosses and dragons together") - bosses.add_argument("-bmbs", "--mix-bosses-statues", action = "store_true", default = True, + bosses.add_argument("-bmbs", "--mix-bosses-statues", action = "store_true", help = "Shuffle/randomize bosses and statues together") bosses.add_argument("-srp3", "--shuffle-random-phunbaba3", action = "store_true", help = "Apply Shuffle/Random to Phunbaba 3 (otherwise he will only appear in Mobliz WOR)") From 31a42fde3d6d64df6d9776597d156352a2f867e3 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:39:13 -0400 Subject: [PATCH 05/67] wip refactor shuffle to "boss location" flags --- args/bosses.py | 43 ++++++++++++++++++++++++++++++++++--------- data/bosses.py | 7 +++++++ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/args/bosses.py b/args/bosses.py index 3713ae54..767ecd92 100644 --- a/args/bosses.py +++ b/args/bosses.py @@ -1,3 +1,6 @@ +from data.bosses import BossLocations + + def name(): return "Bosses" @@ -9,10 +12,22 @@ def parse(parser): help = "Boss battles shuffled") bosses_battles.add_argument("-bbr", "--boss-battles-random", action = "store_true", help = "Boss battles randomized") - bosses.add_argument("-bmbd", "--mix-bosses-dragons", action = "store_true", + + dragons = bosses.add_mutually_exclusive_group() + dragons.add_argument("-drloc", "--dragon-boss-location", default = BossLocations.SHUFFLE, type = str.lower, choices = BossLocations.ALL, + help = "Decides which locations the eight dragon encounters can be fought") + + dragons.add_argument("-bmbd", "--mix-bosses-dragons", action = "store_true", help = "Shuffle/randomize bosses and dragons together") - bosses.add_argument("-bmbs", "--mix-bosses-statues", action = "store_true", - help = "Shuffle/randomize bosses and statues together") + # dragons.add_argument("-shud", "--shuffle-dragons", action = "store_true", + # help = "Shuffles the eight dragon encounters") + + statues = bosses.add_mutually_exclusive_group() + + + statues.add_argument("-stloc", "--statue-boss-location", default = BossLocations.MIX, type = str.lower, choices = BossLocations.ALL, + help = "Decides which locations the three statue encounters can be fought") + bosses.add_argument("-srp3", "--shuffle-random-phunbaba3", action = "store_true", help = "Apply Shuffle/Random to Phunbaba 3 (otherwise he will only appear in Mobliz WOR)") bosses.add_argument("-bnds", "--boss-normalize-distort-stats", action = "store_true", @@ -23,7 +38,8 @@ def parse(parser): help = "Undead status removed from bosses") def process(args): - pass + if args.mix_bosses_dragons: + args.dragon_boss_location = BossLocations.MIX def flags(args): flags = "" @@ -33,10 +49,16 @@ def flags(args): elif args.boss_battles_random: flags += " -bbr" + if args.dragon_boss_location: + flags += f" -drloc {args.statue_boss_location}" if args.mix_bosses_dragons: - flags += " -bmbd" - if args.mix_bosses_statues: - flags += " -bmbs" + flags += f" -drloc {BossLocations.MIX}" + else: + flags += f" -drloc {BossLocations.SHUFFLE}" + + if args.statue_boss_location: + flags += f" -stloc {args.statue_boss_location}" + if args.shuffle_random_phunbaba3: flags += " -srp3" if args.boss_normalize_distort_stats: @@ -55,10 +77,13 @@ def options(args): elif args.boss_battles_random: boss_battles = "Random" + dragon_battles = args.dragon_boss_location + statue_battles = args.statue_boss_location + return [ ("Boss Battles", boss_battles), - ("Mix Bosses & Dragons", args.mix_bosses_dragons), - ("Mix Bosses & Statues", args.mix_bosses_statues), + ("Dragons", dragon_battles), + ("Statues", statue_battles), ("Shuffle/Random Phunbaba 3", args.shuffle_random_phunbaba3), ("Normalize & Distort Stats", args.boss_normalize_distort_stats), ("Boss Experience", args.boss_experience), diff --git a/data/bosses.py b/data/bosses.py index 612fd968..fd592acb 100644 --- a/data/bosses.py +++ b/data/bosses.py @@ -255,3 +255,10 @@ enemy_name.update(removed_enemy_name) name_enemy = {v: k for k, v in enemy_name.items()} + +class BossLocations: + MIX = "mix" + ORIGINAL = "original" + SHUFFLE = "shuffle" + + ALL = [MIX, ORIGINAL, SHUFFLE] \ No newline at end of file From a833caaa52e8a9f87b2d8790089e9b5a258c8ae9 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 16 Apr 2022 17:28:21 -0400 Subject: [PATCH 06/67] bug fixes --- args/bosses.py | 10 +-- data/bosses.py | 19 ++++++ data/enemy_packs.py | 144 ++++++++++++++++++++++++-------------------- 3 files changed, 100 insertions(+), 73 deletions(-) diff --git a/args/bosses.py b/args/bosses.py index 767ecd92..efd0b800 100644 --- a/args/bosses.py +++ b/args/bosses.py @@ -16,15 +16,10 @@ def parse(parser): dragons = bosses.add_mutually_exclusive_group() dragons.add_argument("-drloc", "--dragon-boss-location", default = BossLocations.SHUFFLE, type = str.lower, choices = BossLocations.ALL, help = "Decides which locations the eight dragon encounters can be fought") - dragons.add_argument("-bmbd", "--mix-bosses-dragons", action = "store_true", help = "Shuffle/randomize bosses and dragons together") - # dragons.add_argument("-shud", "--shuffle-dragons", action = "store_true", - # help = "Shuffles the eight dragon encounters") statues = bosses.add_mutually_exclusive_group() - - statues.add_argument("-stloc", "--statue-boss-location", default = BossLocations.MIX, type = str.lower, choices = BossLocations.ALL, help = "Decides which locations the three statue encounters can be fought") @@ -40,6 +35,7 @@ def parse(parser): def process(args): if args.mix_bosses_dragons: args.dragon_boss_location = BossLocations.MIX + args.mix_bosses_dragons = None def flags(args): flags = "" @@ -50,8 +46,8 @@ def flags(args): flags += " -bbr" if args.dragon_boss_location: - flags += f" -drloc {args.statue_boss_location}" - if args.mix_bosses_dragons: + flags += f" -drloc {args.dragon_boss_location}" + elif args.mix_bosses_dragons: flags += f" -drloc {BossLocations.MIX}" else: flags += f" -drloc {BossLocations.SHUFFLE}" diff --git a/data/bosses.py b/data/bosses.py index fd592acb..079023af 100644 --- a/data/bosses.py +++ b/data/bosses.py @@ -92,6 +92,25 @@ 396 : "Guardian", # defeatable guardian in kefka's tower 401 : "MagiMaster", } + +# 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 = { + 354 : "Doom", + 355 : "Goddess", + 356 : "Poltrgeist", +} +statue_formation_name = { + 468 : "Doom", + 469 : "Goddess", + 470 : "Poltrgeist", +} +statue_enemy_name = { + 295 : "Doom", + 296 : "Goddess", + 297 : "Poltrgeist", +} + normal_formation_name = { 79 : "Rizopas", 354 : "MagiMaster", diff --git a/data/enemy_packs.py b/data/enemy_packs.py index 29b4e642..d04c39db 100644 --- a/data/enemy_packs.py +++ b/data/enemy_packs.py @@ -40,12 +40,13 @@ def __init__(self, rom, args, formations): pack = EnemyPack2(pack2_index, self.pack2_data[pack2_index]) self.packs.append(pack) + # Returns the list of all non-statue, non-dragon bosses def _replaceable_bosses(self): - replaceable = list(bosses.normal_pack_name) - if not self.args.mix_bosses_statues: - for boss in [self.DOOM, self.GODDESS, self.POLTERGEIST]: - replaceable.remove(boss) - self.event_boss_replacements[boss] = boss + dragon_packs = list(bosses.dragon_pack_name) + statue_packs = list(bosses.statue_pack_name) + boss_packs = list(bosses.normal_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: replaceable.remove(self.PHUNBABA3) self.event_boss_replacements[self.PHUNBABA3] = self.PHUNBABA3 @@ -55,8 +56,57 @@ def _replaceable_bosses(self): # how would that work with him being in his original spot and the others? How to know when to get bahamut esper? replaceable.remove(self.DOOM_GAZE) self.event_boss_replacements[self.DOOM_GAZE] = self.DOOM_GAZE - return replaceable + return replaceable + self._replaceable_dragons() + self._replaceable_statues() + + # statue locations that become available for the general boss pool + def _replaceable_statues(self): + import random + statues = list(bosses.statue_pack_name) + random.shuffle(statues) + return statues if self.args.statue_boss_location == bosses.BossLocations.MIX else [] + # dragon locations that become available for the general boss pool + def _replaceable_dragons(self): + import random + statues = list(bosses.dragon_pack_name) + random.shuffle(statues) + return statues if self.args.dragon_boss_location == bosses.BossLocations.MIX else [] + + def _handle_statues(self): + statues = list(bosses.statue_pack_name) + + if self.args.statue_boss_location == bosses.BossLocations.ORIGINAL: + for statue in statues: + self.event_boss_replacements[statue] = statue + elif self.args.statue_boss_location == bosses.BossLocations.SHUFFLE: + import random + replacements = statues.copy() + random.shuffle(statues) + random.shuffle(replacements) + + for statue in statues: + self.event_boss_replacements[replacements.pop()] = statue + else: + # boss assignment is handled in the shuffle/random functions + pass + + + def _handle_dragons(self): + dragons = list(bosses.dragon_pack_name) + if self.args.dragon_boss_location == bosses.BossLocations.ORIGINAL: + for dragon in dragons: + self.event_boss_replacements[dragon] = dragon + elif self.args.dragon_boss_location == bosses.BossLocations.SHUFFLE: + import random + replacements = dragons.copy() + random.shuffle(dragons) + random.shuffle(replacements) + + for dragon in dragons: + self.event_boss_replacements[replacements.pop()] = dragon + else: + # boss assignment is handled in the shuffle/random functions + pass def phunbaba3_safety_check(self, bosses_possible): import random @@ -83,31 +133,14 @@ def shuffle_event_bosses(self): import random self.event_boss_replacements = {} - if self.args.mix_bosses_dragons: - bosses_dragons_to_replace = self._replaceable_bosses() + list(bosses.dragon_pack_name) - bosses_dragons_possible = bosses_dragons_to_replace.copy() - - random.shuffle(bosses_dragons_possible) - for index, boss in enumerate(bosses_dragons_to_replace): - self.event_boss_replacements[boss] = bosses_dragons_possible[index] - - self.phunbaba3_safety_check(bosses_dragons_to_replace) - else: - bosses_to_replace = self._replaceable_bosses() - bosses_possible = bosses_to_replace.copy() - - random.shuffle(bosses_possible) - for index, boss in enumerate(bosses_to_replace): - self.event_boss_replacements[boss] = bosses_possible[index] + bosses_to_replace = self._replaceable_bosses() + bosses_possible = bosses_to_replace.copy() - dragons_to_replace = list(bosses.dragon_pack_name) - dragons_possible = dragons_to_replace.copy() + random.shuffle(bosses_possible) + for index, boss in enumerate(bosses_to_replace): + self.event_boss_replacements[boss] = bosses_possible[index] - random.shuffle(dragons_possible) - for index, dragon in enumerate(dragons_to_replace): - self.event_boss_replacements[dragon] = dragons_possible[index] - - self.phunbaba3_safety_check(bosses_to_replace) + self.phunbaba3_safety_check(bosses_to_replace) def randomize_event_bosses(self): import args, random, objectives @@ -155,46 +188,21 @@ def randomize_event_bosses(self): if formation_id in required_dragon_formations: required_dragon_packs.add(pack_id) - if self.args.mix_bosses_dragons: - bosses_dragons_to_replace = self._replaceable_bosses() + list(bosses.dragon_pack_name) - random.shuffle(bosses_dragons_to_replace) - - for pack in required_boss_packs: - self.event_boss_replacements[bosses_dragons_to_replace.pop()] = pack - - for pack in required_dragon_packs: - self.event_boss_replacements[bosses_dragons_to_replace.pop()] = pack - - # guarantee 8 dragons - dragons_possible = list(bosses.dragon_pack_name) - for index in range(len(required_dragon_packs), len(bosses.dragon_pack_name)): - self.event_boss_replacements[bosses_dragons_to_replace.pop()] = random.choice(dragons_possible) - - bosses_possible = self._replaceable_bosses() - for boss in bosses_dragons_to_replace: - self.event_boss_replacements[boss] = random.choice(bosses_possible) + # randomizing and shuffling + bosses_to_replace = self._replaceable_bosses() + random.shuffle(bosses_to_replace) + for pack in required_boss_packs: + self.event_boss_replacements[bosses_to_replace.pop()] = pack - self.phunbaba3_safety_check(bosses_possible + dragons_possible) - else: - bosses_to_replace = self._replaceable_bosses() - random.shuffle(bosses_to_replace) - for pack in required_boss_packs: - self.event_boss_replacements[bosses_to_replace.pop()] = pack - - bosses_possible = self._replaceable_bosses() - for boss in bosses_to_replace: - self.event_boss_replacements[boss] = random.choice(bosses_possible) - - dragons_to_replace = list(bosses.dragon_pack_name) - random.shuffle(dragons_to_replace) - for pack in required_dragon_packs: - self.event_boss_replacements[dragons_to_replace.pop()] = pack + for pack in required_dragon_packs: + self.event_boss_replacements[bosses_to_replace.pop()] = pack - dragons_possible = list(bosses.dragon_pack_name) - for dragon in dragons_to_replace: - self.event_boss_replacements[dragon] = random.choice(dragons_possible) + random.shuffle(bosses_to_replace) + bosses_possible = self._replaceable_bosses() + for boss in bosses_to_replace: + self.event_boss_replacements[boss] = random.choice(bosses_possible) - self.phunbaba3_safety_check(bosses_possible) + self.phunbaba3_safety_check(bosses_possible) def randomize_packs(self, packs, boss_percent, no_phunbaba3 = False): exclude_bosses = None @@ -318,6 +326,10 @@ def mod(self): else: self.event_boss_replacements = None + self._handle_dragons() + + self._handle_statues() + if not self.args.fixed_encounters_original: self.randomize_fixed() From aaeba39edf51428c332f0ea7410a407d176a9a64 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 16 Apr 2022 18:27:29 -0400 Subject: [PATCH 07/67] add boilerplate for adding future checks --- constants/gates.py | 6 ++++++ constants/objectives/condition_bits.py | 5 +++++ data/event_bit.py | 9 ++++++++- event/kefka_tower.py | 16 ++++++++++++++++ wc.py | 5 +++++ 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/constants/gates.py b/constants/gates.py index 266b975c..a384357a 100644 --- a/constants/gates.py +++ b/constants/gates.py @@ -81,6 +81,12 @@ "Auction2", "Fanatic's Tower Dragon", "Fanatic's Tower Leader", + # Add these once events are working + # "KT Left Triad Statue", + # "KT Mid Triad Statue", + # "KT Right Triad Statue", + # "Kefka's Tower Ambush", + # "Kefka's Tower Guardian", "Kefka's Tower Cell Beast", "Kefka's Tower Dragon G", "Kefka's Tower Dragon S", diff --git a/constants/objectives/condition_bits.py b/constants/objectives/condition_bits.py index bc46f131..691f8cee 100644 --- a/constants/objectives/condition_bits.py +++ b/constants/objectives/condition_bits.py @@ -29,9 +29,14 @@ NameBit("Floating Cont. Escape", event_bit.FINISHED_FLOATING_CONTINENT), NameBit("Gau's Father's House", event_bit.RECRUITED_SHADOW_GAU_FATHER_HOUSE), NameBit("Imperial Camp", event_bit.FINISHED_IMPERIAL_CAMP), + # NameBit("Kefka's Tower Ambush", event_bit.DEFEATED_INFERNO), + # NameBit("Kefka's Tower Guardian", event_bit.DEFEATED_GUARDIAN), NameBit("Kefka's Tower Cell Beast", event_bit.DEFEATED_ATMA), NameBit("Kefka's Tower Dragon G", event_bit.DEFEATED_KEFKA_TOWER_DRAGON_G), NameBit("Kefka's Tower Dragon S", event_bit.DEFEATED_KEFKA_TOWER_DRAGON_S), + # NameBit("KT Left Triad Statue", event_bit.DEFEATED_DOOM), + # NameBit("KT Mid Triad Statue", event_bit.DEFEATED_POLTERGEIST), + # NameBit("KT Right Triad Statue", event_bit.DEFEATED_GODDESS), NameBit("Kohlingen Cafe", event_bit.RECRUITED_SHADOW_KOHLINGEN), NameBit("Lete River", event_bit.RODE_RAFT_LETE_RIVER), NameBit("Lone Wolf Chase", event_bit.CHASING_LONE_WOLF7), diff --git a/data/event_bit.py b/data/event_bit.py index b8775c9d..3fab5490 100644 --- a/data/event_bit.py +++ b/data/event_bit.py @@ -1,5 +1,5 @@ # NOTE: (address - 1e80) * 0x8 + bit -# e.g. (1eb7 - 1e80) * 0x8 + 0x1 = 1b9 (airship visible) +# e.g. (1eb7 - 1e80) * 0x8 + 0x1 = 1b9 (airship visible) # (1f43 - 1e80) * 0x8 + 0x3 = 61b (characters on narshe battlefield) DISABLE_SAVE_POINT_TUTORIAL = 0x133 @@ -182,6 +182,13 @@ DEFEATED_PHOENIX_CAVE_DRAGON = 0x120 # custom DEFEATED_FANATICS_TOWER_DRAGON = 0x121 # custom +# KT Battles +DEFEATED_GUARDIAN = 0x0bc +DEFEATED_INFERNO = 0x0bd +DEFEATED_DOOM = 0x072 +DEFEATED_GODDESS = 0x073 +DEFEATED_POLTERGEIST = 0x074 + LEFT_WEIGHT_PUSHED_KEFKA_TOWER = 0x063 RIGHT_WEIGHT_PUSHED_KEFKA_TOWER = 0x064 WEST_PATH_BLOCKED_KEFKA_TOWER = 0x065 # path to doom in switch room diff --git a/event/kefka_tower.py b/event/kefka_tower.py index ba1b1264..b1189266 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -28,6 +28,11 @@ def mod(self): self.item = self.atma_reward.id self.atma_battle_mod() self.atma_mod() + self.inferno_mod() + self.guardian_mod() + self.doom_mod() + self.goddess_mod() + self.poltergeist_mod() self.inferno_battle_mod() if self.args.fix_boss_skip: @@ -243,6 +248,17 @@ def atma_battle_mod(self): field.InvokeBattle(boss_pack_id), ) + def guardian_mod(self): + pass + def inferno_mod(self): + pass + def doom_mod(self): + pass + def goddess_mod(self): + pass + def poltergeist_mod(self): + pass + def atma_mod(self): src = [ Read(0xc18d3, 0xc18d6), # show save point, set save point npc bit diff --git a/wc.py b/wc.py index a6d8ea49..0383207e 100644 --- a/wc.py +++ b/wc.py @@ -26,5 +26,10 @@ def main(): data.write() memory.write() + +import debugpy +debugpy.listen(5678) +debugpy.wait_for_client() # blocks execution until client is attached + if __name__ == '__main__': main() From d91cea48220d15533d1d88ebbb00272b00ada8bf Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 16 Apr 2022 18:29:28 -0400 Subject: [PATCH 08/67] chore: no longer appnd dragon locations to flags automatically --- args/bosses.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/args/bosses.py b/args/bosses.py index efd0b800..a834faa5 100644 --- a/args/bosses.py +++ b/args/bosses.py @@ -49,8 +49,6 @@ def flags(args): flags += f" -drloc {args.dragon_boss_location}" elif args.mix_bosses_dragons: flags += f" -drloc {BossLocations.MIX}" - else: - flags += f" -drloc {BossLocations.SHUFFLE}" if args.statue_boss_location: flags += f" -stloc {args.statue_boss_location}" From 953a3be75ee2b486fcc0444d825b58a92b9a7c42 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 16 Apr 2022 18:30:09 -0400 Subject: [PATCH 09/67] chore: add EOL --- data/bosses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/bosses.py b/data/bosses.py index 079023af..87eafbdd 100644 --- a/data/bosses.py +++ b/data/bosses.py @@ -280,4 +280,4 @@ class BossLocations: ORIGINAL = "original" SHUFFLE = "shuffle" - ALL = [MIX, ORIGINAL, SHUFFLE] \ No newline at end of file + ALL = [MIX, ORIGINAL, SHUFFLE] From 81bd325682bd6885def9420b5394192205bd90bf Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 16 Apr 2022 18:30:48 -0400 Subject: [PATCH 10/67] chore: remove unused ids from EnemyPacks --- data/enemy_packs.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/data/enemy_packs.py b/data/enemy_packs.py index d04c39db..d2662269 100644 --- a/data/enemy_packs.py +++ b/data/enemy_packs.py @@ -18,11 +18,6 @@ class EnemyPacks(): PHUNBABA3 = 386 DOOM_GAZE = 349 - # statues removed from shuffle pool with -bmbs flag - DOOM=354 - GODDESS=355 - POLTERGEIST=356 - def __init__(self, rom, args, formations): self.rom = rom self.args = args From f4f363ad26e12e50ac1e094ae4833a814ccc7cf4 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 16 Apr 2022 18:31:43 -0400 Subject: [PATCH 11/67] remove dbugpy code --- wc.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/wc.py b/wc.py index 0383207e..a6d8ea49 100644 --- a/wc.py +++ b/wc.py @@ -26,10 +26,5 @@ def main(): data.write() memory.write() - -import debugpy -debugpy.listen(5678) -debugpy.wait_for_client() # blocks execution until client is attached - if __name__ == '__main__': main() From 63809e48912096ae62ded908c55c108b8b49895b Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 17 Apr 2022 08:43:34 -0400 Subject: [PATCH 12/67] Clean up enemy_packs.randomize_event_bosses --- data/enemy_packs.py | 42 +++++++++++++++++++++++---------- objectives/conditions/boss.py | 9 +++---- objectives/conditions/dragon.py | 6 +++-- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/data/enemy_packs.py b/data/enemy_packs.py index d2662269..195a9240 100644 --- a/data/enemy_packs.py +++ b/data/enemy_packs.py @@ -35,7 +35,7 @@ def __init__(self, rom, args, formations): pack = EnemyPack2(pack2_index, self.pack2_data[pack2_index]) self.packs.append(pack) - # Returns the list of all non-statue, non-dragon bosses + # Returns the list of all boss packs that can be used during randomization def _replaceable_bosses(self): dragon_packs = list(bosses.dragon_pack_name) statue_packs = list(bosses.statue_pack_name) @@ -53,21 +53,22 @@ def _replaceable_bosses(self): self.event_boss_replacements[self.DOOM_GAZE] = self.DOOM_GAZE return replaceable + self._replaceable_dragons() + self._replaceable_statues() - # statue locations that become available for the general boss pool + # Statue locations that become available for the general boss pool def _replaceable_statues(self): import random statues = list(bosses.statue_pack_name) random.shuffle(statues) return statues if self.args.statue_boss_location == bosses.BossLocations.MIX else [] - # dragon locations that become available for the general boss pool + # Dragon locations that become available for the general boss pool def _replaceable_dragons(self): import random statues = list(bosses.dragon_pack_name) random.shuffle(statues) return statues if self.args.dragon_boss_location == bosses.BossLocations.MIX else [] - def _handle_statues(self): + # As MIX is handled in the shuffle/random functions, this is for handling the other options + def _handle_original_shuffle_statues(self): statues = list(bosses.statue_pack_name) if self.args.statue_boss_location == bosses.BossLocations.ORIGINAL: @@ -86,7 +87,8 @@ def _handle_statues(self): pass - def _handle_dragons(self): + # As MIX is handled in the shuffle/random functions, this is for handling the other options + def _handle_original_shuffle_dragons(self): dragons = list(bosses.dragon_pack_name) if self.args.dragon_boss_location == bosses.BossLocations.ORIGINAL: for dragon in dragons: @@ -102,6 +104,7 @@ def _handle_dragons(self): else: # boss assignment is handled in the shuffle/random functions pass + def phunbaba3_safety_check(self, bosses_possible): import random @@ -152,13 +155,19 @@ def randomize_event_bosses(self): required_boss_formations = set() required_dragon_formations = set() + required_statue_formations = set() + min_dragon_formations = 0 for objective in objectives: for condition in objective.conditions: if condition.NAME == boss_condition_name: - required_boss_formations.add(bosses.name_formation[condition.boss_name()]) + formation = condition.boss_formation + if formation in list(bosses.statue_formation_name): + required_statue_formations.add(formation) + else: + required_boss_formations.add(formation) elif condition.NAME == dragon_condition_name: - required_dragon_formations.add(bosses.name_formation[condition.dragon_name()]) + required_dragon_formations.add(condition.dragon_formation) elif condition.NAME == dragons_condition_name and condition.count > min_dragon_formations: min_dragon_formations = condition.count @@ -170,11 +179,14 @@ def randomize_event_bosses(self): required_dragon_formations |= set(random_dragon_formations) required_boss_packs = set() + required_statue_packs = set() for pack_id, pack_name in bosses.normal_pack_name.items(): formations = self.get_formations(pack_id) for formation_id in formations: if formation_id in required_boss_formations: required_boss_packs.add(pack_id) + elif formation_id in required_statue_formations: + required_statue_packs.add(pack_id) required_dragon_packs = set() for pack_id, pack_name in bosses.dragon_pack_name.items(): @@ -183,14 +195,21 @@ def randomize_event_bosses(self): if formation_id in required_dragon_formations: required_dragon_packs.add(pack_id) + # randomizing and shuffling bosses_to_replace = self._replaceable_bosses() random.shuffle(bosses_to_replace) for pack in required_boss_packs: self.event_boss_replacements[bosses_to_replace.pop()] = pack - for pack in required_dragon_packs: - self.event_boss_replacements[bosses_to_replace.pop()] = pack + # If statue locations are not mixed, they will always + if self.args.statue_boss_location == bosses.BossLocations.MIX: + for pack in required_statue_packs: + self.event_boss_replacements[bosses_to_replace.pop()] = pack + + if self.args.dragon_boss_location == bosses.BossLocations.MIX: + for pack in required_dragon_packs: + self.event_boss_replacements[bosses_to_replace.pop()] = pack random.shuffle(bosses_to_replace) bosses_possible = self._replaceable_bosses() @@ -321,9 +340,8 @@ def mod(self): else: self.event_boss_replacements = None - self._handle_dragons() - - self._handle_statues() + self._handle_original_shuffle_dragons() + self._handle_original_shuffle_statues() if not self.args.fixed_encounters_original: self.randomize_fixed() diff --git a/objectives/conditions/boss.py b/objectives/conditions/boss.py index 8e465871..7a852dbf 100644 --- a/objectives/conditions/boss.py +++ b/objectives/conditions/boss.py @@ -1,14 +1,15 @@ from objectives.conditions._objective_condition import * from constants.objectives.condition_bits import boss_bit +from data.bosses import name_formation, name_pack class Condition(ObjectiveCondition): NAME = "Boss" def __init__(self, boss): self.boss = boss + self.boss_name = boss_bit[self.boss].name + self.boss_formation = name_formation[self.boss_name] + self.boss_pack = name_pack[self.boss_name] super().__init__(ConditionType.BattleBit, boss_bit[self.boss].bit) def __str__(self): - return super().__str__(self.boss) - - def boss_name(self): - return boss_bit[self.boss].name + return super().__str__(self.boss) \ No newline at end of file diff --git a/objectives/conditions/dragon.py b/objectives/conditions/dragon.py index 90c0ca84..37a36664 100644 --- a/objectives/conditions/dragon.py +++ b/objectives/conditions/dragon.py @@ -1,14 +1,16 @@ from objectives.conditions._objective_condition import * from constants.objectives.condition_bits import dragon_bit +from data.bosses import name_formation, name_pack class Condition(ObjectiveCondition): NAME = "Dragon" def __init__(self, dragon): self.dragon = dragon + self.dragon_name = dragon_bit[self.dragon].name + self.dragon_formation = name_formation[self.dragon_name] + self.dragon_pack = name_pack[self.dragon_name] super().__init__(ConditionType.BattleBit, dragon_bit[self.dragon].bit) def __str__(self): return super().__str__(self.dragon) - def dragon_name(self): - return dragon_bit[self.dragon].name From 134012e5cf8f2b8c6716cf104141f1990c946ec2 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 17 Apr 2022 09:18:03 -0400 Subject: [PATCH 13/67] fix when original bosses with mixed dragon/statues --- args/bosses.py | 21 +++++++++++++++++---- data/enemy_packs.py | 26 ++++++++++++++++---------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/args/bosses.py b/args/bosses.py index a834faa5..7fe1b107 100644 --- a/args/bosses.py +++ b/args/bosses.py @@ -1,6 +1,8 @@ from data.bosses import BossLocations +DEFAULT_DRAGON_PROTOCOL = BossLocations.SHUFFLE +DEFAULT_STATUE_PROTOCOL = BossLocations.MIX def name(): return "Bosses" @@ -14,13 +16,13 @@ def parse(parser): help = "Boss battles randomized") dragons = bosses.add_mutually_exclusive_group() - dragons.add_argument("-drloc", "--dragon-boss-location", default = BossLocations.SHUFFLE, type = str.lower, choices = BossLocations.ALL, + dragons.add_argument("-drloc", "--dragon-boss-location", default = DEFAULT_DRAGON_PROTOCOL, type = str.lower, choices = BossLocations.ALL, help = "Decides which locations the eight dragon encounters can be fought") dragons.add_argument("-bmbd", "--mix-bosses-dragons", action = "store_true", help = "Shuffle/randomize bosses and dragons together") statues = bosses.add_mutually_exclusive_group() - statues.add_argument("-stloc", "--statue-boss-location", default = BossLocations.MIX, type = str.lower, choices = BossLocations.ALL, + statues.add_argument("-stloc", "--statue-boss-location", default = DEFAULT_STATUE_PROTOCOL, type = str.lower, choices = BossLocations.ALL, help = "Decides which locations the three statue encounters can be fought") bosses.add_argument("-srp3", "--shuffle-random-phunbaba3", action = "store_true", @@ -36,6 +38,12 @@ def process(args): if args.mix_bosses_dragons: 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) + 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: + args.statue_boss_location = BossLocations.SHUFFLE def flags(args): flags = "" @@ -71,8 +79,13 @@ def options(args): elif args.boss_battles_random: boss_battles = "Random" - dragon_battles = args.dragon_boss_location - statue_battles = args.statue_boss_location + dragon_battles = DEFAULT_DRAGON_PROTOCOL + if args.dragon_boss_location: + dragon_battles = args.dragon_boss_location.capitalize() + + statue_battles = DEFAULT_DRAGON_PROTOCOL + if args.statue_boss_location: + statue_battles = args.statue_boss_location.capitalize() return [ ("Boss Battles", boss_battles), diff --git a/data/enemy_packs.py b/data/enemy_packs.py index 195a9240..61a89618 100644 --- a/data/enemy_packs.py +++ b/data/enemy_packs.py @@ -15,8 +15,9 @@ class EnemyPacks(): ZONE_EATER = 32 VELDT = 255 # placeholder for veldt in wob/wor - PHUNBABA3 = 386 - DOOM_GAZE = 349 + + PHUNBABA3 = bosses.name_pack["Phunbaba 3"] + DOOM_GAZE = bosses.name_pack["Doom Gaze"] def __init__(self, rom, args, formations): self.rom = rom @@ -43,14 +44,19 @@ def _replaceable_bosses(self): 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: - replaceable.remove(self.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? - replaceable.remove(self.DOOM_GAZE) 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() # Statue locations that become available for the general boss pool @@ -129,7 +135,6 @@ def phunbaba3_safety_check(self, bosses_possible): def shuffle_event_bosses(self): import random - self.event_boss_replacements = {} bosses_to_replace = self._replaceable_bosses() bosses_possible = bosses_to_replace.copy() @@ -144,8 +149,6 @@ def randomize_event_bosses(self): import args, random, objectives from constants.objectives.conditions import names as possible_condition_names - self.event_boss_replacements = {} - boss_condition_name = "Boss" dragon_condition_name = "Dragon" dragons_condition_name = "Dragons" @@ -321,7 +324,7 @@ def has_enemy(self, pack_id, enemy_id): def get_event_boss_replacement(self, original_boss_name): original_boss_id = self.get_id(original_boss_name) - if self.event_boss_replacements is None: + if not original_boss_id in self.event_boss_replacements: return original_boss_id return self.event_boss_replacements[original_boss_id] @@ -333,12 +336,15 @@ def remove_extra_formations(self): pack.extra_formations[formation_index] = False def mod(self): + self.event_boss_replacements = { + self.DOOM_GAZE: self.DOOM_GAZE, + self.PHUNBABA3: self.PHUNBABA3 + } + if self.args.boss_battles_shuffle: self.shuffle_event_bosses() elif self.args.boss_battles_random: self.randomize_event_bosses() - else: - self.event_boss_replacements = None self._handle_original_shuffle_dragons() self._handle_original_shuffle_statues() From cd4e48c58234ab87055e2711c00a115c37b08f13 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 17 Apr 2022 12:24:38 -0400 Subject: [PATCH 14/67] now exclude statues when not mix from normal encs --- data/enemy_formations.py | 6 ++++-- data/enemy_packs.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/data/enemy_formations.py b/data/enemy_formations.py index 22765e34..e6d334be 100644 --- a/data/enemy_formations.py +++ b/data/enemy_formations.py @@ -11,8 +11,10 @@ class EnemyFormations(): ENEMIES_END = 0xf83bf ENEMIES_SIZE = 15 - PHUNBABA3 = 422 - DOOM_GAZE = 463 + PHUNBABA3 = bosses.name_formation["Phunbaba 3"] + DOOM_GAZE = bosses.name_formation["Doom Gaze"] + ALL_STATUES = list(bosses.statue_formation_name) + ALL_DRAGONS = list(bosses.dragon_formation_name) PRESENTER = 433 COLISEUM = 575 diff --git a/data/enemy_packs.py b/data/enemy_packs.py index 61a89618..6166c516 100644 --- a/data/enemy_packs.py +++ b/data/enemy_packs.py @@ -231,6 +231,18 @@ def randomize_packs(self, packs, boss_percent, no_phunbaba3 = False): else: exclude_bosses.append(self.formations.DOOM_GAZE) + # We only want statues and dragons to show up when they are intentionally + # mixed into the general boss pool + # Statues are currently seen as normal bosses in regards to scaling, + # but the long-term goal is to add their own scaling option so it + # makes most sense to begin treating these similarly to dragons. + if self.args.statue_boss_location != bosses.BossLocations.MIX: + exclude_bosses += self.formations.ALL_STATUES + + # This is more futureproofing in the event we consolidate dragons in the future + if self.args.dragon_boss_location != bosses.BossLocations.MIX: + exclude_bosses += self.formations.ALL_DRAGONS + import random for pack_id in packs: if random.random() < boss_percent: From 89a6157bd80ed413771289e0924a535796ed8cfe Mon Sep 17 00:00:00 2001 From: kielbasiago <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 27 May 2022 17:52:39 -0400 Subject: [PATCH 15/67] add KT bosses to condition checks (#2) Preview: https://youtu.be/wgBJHPq3p6o (Just me clearing KT while playing at 200-400%) Added the following KT boss locations as objective conditions: - Kefka's Tower Ambush - Inferno - Bit 59 - Kefka's Tower Guardian - Guardian - Bit 60 - KT Left Triad Statue - Doom - Bit 61 - KT Mid Triad Statue - Poltergeist - Bit 62 - KT Right Triad Statue - Goddess - Bit 63 ## Testing Used the following flags to test the five encounters: `-oa 40.1.1.11.59 -ob 40.1.1.11.60 -oc 40.1.1.11.61 -od 40.1.1.11.62 -oe 40.1.1.11.63` --- constants/gates.py | 6 --- constants/objectives/condition_bits.py | 13 +++--- event/kefka_tower.py | 60 +++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/constants/gates.py b/constants/gates.py index a384357a..266b975c 100644 --- a/constants/gates.py +++ b/constants/gates.py @@ -81,12 +81,6 @@ "Auction2", "Fanatic's Tower Dragon", "Fanatic's Tower Leader", - # Add these once events are working - # "KT Left Triad Statue", - # "KT Mid Triad Statue", - # "KT Right Triad Statue", - # "Kefka's Tower Ambush", - # "Kefka's Tower Guardian", "Kefka's Tower Cell Beast", "Kefka's Tower Dragon G", "Kefka's Tower Dragon S", diff --git a/constants/objectives/condition_bits.py b/constants/objectives/condition_bits.py index 691f8cee..8e45eebe 100644 --- a/constants/objectives/condition_bits.py +++ b/constants/objectives/condition_bits.py @@ -29,14 +29,9 @@ NameBit("Floating Cont. Escape", event_bit.FINISHED_FLOATING_CONTINENT), NameBit("Gau's Father's House", event_bit.RECRUITED_SHADOW_GAU_FATHER_HOUSE), NameBit("Imperial Camp", event_bit.FINISHED_IMPERIAL_CAMP), - # NameBit("Kefka's Tower Ambush", event_bit.DEFEATED_INFERNO), - # NameBit("Kefka's Tower Guardian", event_bit.DEFEATED_GUARDIAN), NameBit("Kefka's Tower Cell Beast", event_bit.DEFEATED_ATMA), NameBit("Kefka's Tower Dragon G", event_bit.DEFEATED_KEFKA_TOWER_DRAGON_G), NameBit("Kefka's Tower Dragon S", event_bit.DEFEATED_KEFKA_TOWER_DRAGON_S), - # NameBit("KT Left Triad Statue", event_bit.DEFEATED_DOOM), - # NameBit("KT Mid Triad Statue", event_bit.DEFEATED_POLTERGEIST), - # NameBit("KT Right Triad Statue", event_bit.DEFEATED_GODDESS), NameBit("Kohlingen Cafe", event_bit.RECRUITED_SHADOW_KOHLINGEN), NameBit("Lete River", event_bit.RODE_RAFT_LETE_RIVER), NameBit("Lone Wolf Chase", event_bit.CHASING_LONE_WOLF7), @@ -73,6 +68,14 @@ NameBit("Zozo Tower", event_bit.GOT_ZOZO_REWARD), ] +check_bit += [ # Index + NameBit("Kefka's Tower Ambush", event_bit.DEFEATED_INFERNO), # 59 + NameBit("Kefka's Tower Guardian", event_bit.DEFEATED_GUARDIAN), # 60 + NameBit("KT Left Triad Statue", event_bit.DEFEATED_DOOM), # 61 + NameBit("KT Mid Triad Statue", event_bit.DEFEATED_POLTERGEIST), # 62 + NameBit("KT Right Triad Statue", event_bit.DEFEATED_GODDESS), # 63 +] + quest_bit = [ NameBit("Defeat Sealed Cave Ninja", event_bit.DEFEATED_NINJA_CAVE_TO_SEALED_GATE), NameBit("Help Injured Lad", event_bit.HELPED_INJURED_LAD), diff --git a/event/kefka_tower.py b/event/kefka_tower.py index b1189266..4b31ec08 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -248,16 +248,66 @@ def atma_battle_mod(self): field.InvokeBattle(boss_pack_id), ) + # Copy no less than 4 bytes between start_target and end_target + # This will be called after one of the kt encounters has completed, but just prior to finishing the check + def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, description): + src = Read(start_target, end_target) + src += [ + field.SetEventBit(bit), + field.CheckObjectives(), + field.Return(), + ] + post_battle = Write(Bank['CC'], src, f"{boss_name} post-battle. 1) Set event bit. 2) Finish check") + + space = Reserve(start_target, end_target, description, asm.NOP()) + space.write([ + field.Call(post_battle.start_address) + ]) + def guardian_mod(self): - pass + self.kt_encounter_objective_mod( + "Guardian", + event_bit.DEFEATED_GUARDIAN, + 0xc186c, + 0xc186f, + "Guardian battle post-script, wait for fade, set bit", + ) + def inferno_mod(self): - pass + self.kt_encounter_objective_mod( + "Inferno", + event_bit.DEFEATED_INFERNO, + 0xc18ae, + 0xc18b1, + "Inferno battle post-script, fade in, wait, set bit", + ) + def doom_mod(self): - pass + self.kt_encounter_objective_mod( + "Doom", + event_bit.DEFEATED_DOOM, + 0xc16f0, + 0xc16f3, + "Doom battle post-script, Hide NPC 5, set npc bit", + ) + def goddess_mod(self): - pass + self.kt_encounter_objective_mod( + "Goddess", + event_bit.DEFEATED_GODDESS, + 0xc1730, + 0xc1733, + "Goddess battle post-script, Hide NPC 2, set npc bit", + ) + def poltergeist_mod(self): - pass + self.kt_encounter_objective_mod( + "Goddess", + event_bit.DEFEATED_POLTERGEIST, + 0xc1786, + 0xc1789, + "Poltergeist battle post-script, Hide NPCs, set npc bit", + ) def atma_mod(self): src = [ From 297efda5a24c8f4bb4a8b65dd165c75c600528b0 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 29 May 2022 13:37:31 -0400 Subject: [PATCH 16/67] Add "kt gauntlet" in replace of skip (temp) --- constants/maps.py | 12 +++ data/map_property.py | 15 +++- data/maps.py | 6 ++ event/kefka_tower.py | 185 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 constants/maps.py diff --git a/constants/maps.py b/constants/maps.py new file mode 100644 index 00000000..4e516f8d --- /dev/null +++ b/constants/maps.py @@ -0,0 +1,12 @@ + + +id_name = { + 0x123 : "Guardian Room", # 291 + 0x14e : "Poltergeist Room", # 334 + 0x162 : "Goddess Room", # 354 + 0x163 : "Doom Room", # 355 + 0x19a : "Inferno Room", # 410 + 0x19b : "KT Final Switch Room" # 411 +} + +name_id = {v: k for k, v in id_name.items()} diff --git a/data/map_property.py b/data/map_property.py index d6eae884..3a6855f2 100644 --- a/data/map_property.py +++ b/data/map_property.py @@ -1,3 +1,7 @@ + +ENABLE_WARP = 0x02 +ENABLE_RANDOM_ENCOUNTER = 0x80 + class MapProperty: DATA_SIZE = 33 DATA_START = 0x2d8f00 @@ -16,12 +20,21 @@ def read(self): self.data = self.rom.get_bytes(self.data_start, self.DATA_SIZE) self.name_index = self.data[0] - self.enable_random_encounters = (self.data[5] & 0x80) >> 7 + self.enable_warp = (self.data[1] & ENABLE_WARP) >> 1 + self.enable_random_encounters = (self.data[5] & ENABLE_RANDOM_ENCOUNTER) >> 7 self.song = self.data[28] def write(self): + self.data[1] &= self.enable_warp << 1 + self.data[5] &= self.enable_random_encounters << 7 self.data[28] = self.song self.rom.set_bytes(self.data_start, self.data) + def set_warp(self, value): + self.enable_warp = 1 if value else 0 + + def set_random_encounters(self, value): + self.enable_random_encounters = 1 if value else 0 + def print(self): print(f"{self.id}: {self.enable_random_encounters}") diff --git a/data/maps.py b/data/maps.py index 8c63f69f..913701e5 100644 --- a/data/maps.py +++ b/data/maps.py @@ -156,6 +156,12 @@ def print_long_exits(self, map_id): first_exit_id = (self.maps[map_id]["long_exits_ptr"] - self.maps[0]["long_exits_ptr"]) // LongMapExit.DATA_SIZE self.exits.print_long_exit_range(first_exit_id, self.get_long_exit_count(map_id)) + def disable_random_encounter(self, map_id): + self.properties[map_id].set_random_encounters(False) + + def disable_warp(self, map_id): + self.properties[map_id].set_warp(False) + def _fix_imperial_camp_boxes(self): # near the northern tent normally accessed by jumping over a wall # there is a box which can be walked into but not out of which causes the game to lock diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 4b31ec08..78eb638d 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -1,6 +1,14 @@ from event.event import * import args +from constants.maps import name_id +final_switch_map_id = name_id["KT Final Switch Room"] +inferno_room_id = name_id["Inferno Room"] +guardian_room_id = name_id["Guardian Room"] +poltergeist_room_id = name_id["Poltergeist Room"] +doom_room_id = name_id["Doom Room"] +goddess_room_id = name_id["Goddess Room"] + class KefkaTower(Event): def name(self): return "Kefka's Tower" @@ -21,7 +29,8 @@ def init_event_bits(self, space): ) def mod(self): - self.statue_landing_mod() + self.boss_rush_mod() + # self.statue_landing_mod() self.entrance_landing_mod() self.kefka_scene_mod() @@ -140,6 +149,180 @@ def statue_landing_mod(self): space = Reserve(0xa03ad, 0xa03af, "kefka tower the statues are up ahead", field.NOP()) + # Trigger five bosses back-to-back, with cutscenes playing for each one + def boss_rush_mod(self): + self.maps.disable_warp(final_switch_map_id) + from data.bosses import name_pack + change_party = lambda party : [ + field.SetParty(party), + field.RefreshEntities(), + field.UpdatePartyLeader(), + ] + + invoke_kt_battle = lambda party, pack_name : [ + change_party(party), + field.InvokeBattle(name_pack[pack_name]), + ] + + party1_x = 103 + party1_y_dest = 45 + party1_y_offset = 6 + party1_y_start = party1_y_dest - party1_y_offset + + party2_x = 109 + party2_y_dest = 42 + party2_y_offset = 9 + party2_y_start = party2_y_dest - party2_y_offset + + party3_x = 115 + party3_y_dest = 44 + party3_y_offset = 8 + party3_y_start = party3_y_dest - party3_y_offset + + src = [ + Read(0xa02d6, 0xa030a), + field.ClearEventBit(event_bit.UNLOCKED_KT_SKIP), + + field.SetEventBit(event_bit.LEFT_WEIGHT_PUSHED_KEFKA_TOWER), + field.SetEventBit(event_bit.RIGHT_WEIGHT_PUSHED_KEFKA_TOWER), + field.ClearEventBit(npc_bit.LEFT_UNPUSHED_WEIGHT_KEFKA_TOWER), + field.SetEventBit(npc_bit.LEFT_PUSHED_WEIGHT_KEFKA_TOWER), + field.ClearEventBit(npc_bit.RIGHT_UNPUSHED_WEIGHT_KEFKA_TOWER), + field.SetEventBit(npc_bit.RIGHT_PUSHED_WEIGHT_KEFKA_TOWER), + field.ClearEventBit(npc_bit.CENTER_DOOR_BLOCK_KEFKA_TOWER), + + field.SetEventBit(event_bit.WEST_PATH_BLOCKED_KEFKA_TOWER), + field.SetEventBit(event_bit.EAST_PATH_BLOCKED_KEFKA_TOWER), + field.SetEventBit(event_bit.NORTH_PATH_OPEN_KEFKA_TOWER), + field.SetEventBit(event_bit.SOUTH_PATH_OPEN_KEFKA_TOWER), + field.SetEventBit(event_bit.CENTER_DOOR_KEFKA_TOWER), + field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), + + field.ClearEventBit(event_bit.TEMP_SONG_OVERRIDE), + field.HoldScreen(), + field.HideEntity(field_entity.PARTY0), + field.SetPartyMap(1, final_switch_map_id), + field.SetPartyMap(2, final_switch_map_id), + field.SetPartyMap(3, final_switch_map_id), + # Fight Inferno + [ + field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, + x = 0, y = 0, fade_in = False, entrance_event = True), + invoke_kt_battle(3, "Inferno"), + ], + # Fight Guardian + [ + field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, + x = 0, y = 0, fade_in = False, entrance_event = True), + invoke_kt_battle(3, "Guardian"), + ], + # Fight Poltergeist + [ + field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, + x = 0, y = 0, fade_in = False, entrance_event = True), + invoke_kt_battle(3, "Poltrgeist"), + ], + # Fight Goddess + [ + field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, + x = 0, y = 0, fade_in = False, entrance_event = True), + invoke_kt_battle(2, "Goddess"), + ], + # Fight Doom + [ + field.LoadMap(doom_room_id, direction.DOWN, default_music = False, + x = 0, y = 0, fade_in = False, entrance_event = True), + invoke_kt_battle(1, "Doom"), + ], + # post battle, move to final room + [ + # Load final + field.LoadMap(final_switch_map_id, direction.DOWN, default_music = True, + x = 109, y = 43, fade_in = False, entrance_event = True), + + # Party 1 init position + [ + change_party(1), + Read(0xa0334, 0xa033c), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party1_x, party1_y_start), + field_entity.AnimateFrontHandsUp(), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + # Party 2 init position + [ + Read(0xa031e, 0xa0320), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party2_x, party2_y_start), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + # Party 3 init position + [ + Read(0xa0327, 0xa032d), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party3_x, party3_y_start), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + field.FadeInScreen(), + ], + # party 1 fall + [ + change_party(1), + field.EntityAct(field_entity.PARTY0, True, + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party1_y_offset), + field_entity.AnimateKneeling(), + field_entity.EnableWalkingAnimation(), + ), + ], + # party 2 fall + [ + change_party(2), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpriteLayer(2), + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party2_y_offset - 1), + field_entity.Move(direction.DOWN, 1), + field_entity.AnimateKneeling(), + field_entity.EnableWalkingAnimation(), + field_entity.SetSpriteLayer(0), + ), + ], + # party 3 fall + [ + change_party(3), + field.FadeOutSong(64), + field.Pause(0.75), + field.EntityAct(field_entity.PARTY0, True, + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party3_y_offset), + field_entity.AnimateKneeling(), + ), + ], + field.Pause(0.75), + + Read(0xa039c, 0xa039f), + field.LoadMap(final_switch_map_id, direction.DOWN, default_music = True, + x = party1_x, y = party1_y_dest, fade_in = True, entrance_event = True), + + field.FreeScreen(), + Read(0xa03b0, 0xa03b9), + ] + space = Write(Bank.CA, src, "kefka tower statue landing") + self.statue_landing = space.start_address + + space = Reserve(0xa03ad, 0xa03af, "kefka tower the statues are up ahead", field.NOP()) + + self.maps.delete_short_exit(final_switch_map_id, 103, 49) + self.maps.delete_short_exit(final_switch_map_id, 109, 46) + self.maps.delete_short_exit(final_switch_map_id, 115, 48) + def entrance_landing_mod(self): need_more_allies = 2982 self.dialogs.set_text(need_more_allies, "We need to find more allies.") From 9782abe9c3671d02b0ee9eec3371412a10ddb3de Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 29 May 2022 15:42:46 -0400 Subject: [PATCH 17/67] invoke location fight instead of actual fight --- event/kefka_tower.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 78eb638d..c9c2f259 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -152,7 +152,6 @@ def statue_landing_mod(self): # Trigger five bosses back-to-back, with cutscenes playing for each one def boss_rush_mod(self): self.maps.disable_warp(final_switch_map_id) - from data.bosses import name_pack change_party = lambda party : [ field.SetParty(party), field.RefreshEntities(), @@ -161,7 +160,7 @@ def boss_rush_mod(self): invoke_kt_battle = lambda party, pack_name : [ change_party(party), - field.InvokeBattle(name_pack[pack_name]), + field.InvokeBattle(self.get_boss(pack_name)), ] party1_x = 103 From ad9674e32a81d37ef2030b1a3c83dd48754bda63 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 29 May 2022 22:55:01 -0400 Subject: [PATCH 18/67] Disable boss music during fights --- event/kefka_tower.py | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index c9c2f259..803dfacf 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -152,16 +152,36 @@ def statue_landing_mod(self): # Trigger five bosses back-to-back, with cutscenes playing for each one def boss_rush_mod(self): self.maps.disable_warp(final_switch_map_id) - change_party = lambda party : [ - field.SetParty(party), - field.RefreshEntities(), - field.UpdatePartyLeader(), - ] - invoke_kt_battle = lambda party, pack_name : [ - change_party(party), - field.InvokeBattle(self.get_boss(pack_name)), - ] + def disable_battle_music(boss_name): + from data.bosses import pack_name + replacement = self.get_boss(boss_name, False) + location_boss = pack_name[replacement] + + formation_id = self.enemies.formations.get_id(location_boss) + formation = self.enemies.formations.formations[formation_id] + formation.disable_battle_music = 1 + + # disable + disable_battle_music("Inferno") + disable_battle_music("Doom") + disable_battle_music("Goddess") + disable_battle_music("Guardian") + disable_battle_music("Poltrgeist") + + def change_party(party): + return [ + field.SetParty(party), + field.RefreshEntities(), + field.UpdatePartyLeader(), + ] + + def invoke_kt_battle(party, original_pack_name, battle_sound = False): + from data.bosses import pack_name + return [ + change_party(party), + field.InvokeBattle(self.get_boss(original_pack_name, False), battle_sound = battle_sound), + ] party1_x = 103 party1_y_dest = 45 @@ -207,7 +227,7 @@ def boss_rush_mod(self): [ field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, x = 0, y = 0, fade_in = False, entrance_event = True), - invoke_kt_battle(3, "Inferno"), + invoke_kt_battle(3, "Inferno", True), ], # Fight Guardian [ From 97cd93f84aace3ad6ea11896fc7bc8f700aa6c64 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Mon, 30 May 2022 11:26:22 -0400 Subject: [PATCH 19/67] disable victory music for all but last gauntlet --- event/kefka_tower.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 803dfacf..ceda8a2d 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -149,25 +149,34 @@ def statue_landing_mod(self): space = Reserve(0xa03ad, 0xa03af, "kefka tower the statues are up ahead", field.NOP()) - # Trigger five bosses back-to-back, with cutscenes playing for each one + # Trigger five bosses back-to-back def boss_rush_mod(self): self.maps.disable_warp(final_switch_map_id) - def disable_battle_music(boss_name): + def get_replacement_formation(boss_name): from data.bosses import pack_name replacement = self.get_boss(boss_name, False) location_boss = pack_name[replacement] - formation_id = self.enemies.formations.get_id(location_boss) - formation = self.enemies.formations.formations[formation_id] + return self.enemies.formations.formations[formation_id] + + def disable_victory_dance(original_encounter_name): + formation = get_replacement_formation(original_encounter_name) + formation.disable_victory_dance = 1 + + def disable_battle_music(original_encounter_name): + formation = get_replacement_formation(original_encounter_name) formation.disable_battle_music = 1 - # disable - disable_battle_music("Inferno") - disable_battle_music("Doom") - disable_battle_music("Goddess") - disable_battle_music("Guardian") - disable_battle_music("Poltrgeist") + def disable_all(boss_name): + disable_victory_dance(boss_name) + disable_battle_music(boss_name) + + disable_all("Inferno") + disable_all("Guardian") + disable_all("Poltrgeist") + disable_all("Goddess") + disable_battle_music("Doom") # Keep victory music for defeating the gauntlet def change_party(party): return [ @@ -348,7 +357,7 @@ def entrance_landing_mod(self): statues_entrance = 1287 self.dialogs.set_text(statues_entrance, - " (Statues) (Entrance) (Not just yet)") + " (Gauntlet) (Entrance) (Not just yet)") space = Reserve(0xa01a2, 0xa02d5, "kefka tower first landing scene", field.NOP()) space.add_label("STATUE_LANDING", self.statue_landing) From 29ffc32a18e1887df4ec97e27bacac58757ba15b Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Mon, 30 May 2022 11:41:56 -0400 Subject: [PATCH 20/67] re-order fights --- event/kefka_tower.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index ceda8a2d..ef765474 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -232,6 +232,7 @@ def invoke_kt_battle(party, original_pack_name, battle_sound = False): field.SetPartyMap(1, final_switch_map_id), field.SetPartyMap(2, final_switch_map_id), field.SetPartyMap(3, final_switch_map_id), + # Fight Inferno [ field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, @@ -242,25 +243,25 @@ def invoke_kt_battle(party, original_pack_name, battle_sound = False): [ field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, x = 0, y = 0, fade_in = False, entrance_event = True), - invoke_kt_battle(3, "Guardian"), + invoke_kt_battle(2, "Guardian"), + ], + # Fight Doom + [ + field.LoadMap(doom_room_id, direction.DOWN, default_music = False, + x = 0, y = 0, fade_in = False, entrance_event = True), + invoke_kt_battle(1, "Doom"), ], # Fight Poltergeist [ field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, x = 0, y = 0, fade_in = False, entrance_event = True), - invoke_kt_battle(3, "Poltrgeist"), + invoke_kt_battle(2, "Poltrgeist"), ], # Fight Goddess [ field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, x = 0, y = 0, fade_in = False, entrance_event = True), - invoke_kt_battle(2, "Goddess"), - ], - # Fight Doom - [ - field.LoadMap(doom_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), - invoke_kt_battle(1, "Doom"), + invoke_kt_battle(3, "Goddess"), ], # post battle, move to final room [ From 45ff4d8b2b1da881c1010be0da21ca13dda5446a Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Mon, 30 May 2022 11:46:38 -0400 Subject: [PATCH 21/67] reorder doom/polty --- event/kefka_tower.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index ef765474..e5e925ac 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -174,9 +174,9 @@ def disable_all(boss_name): disable_all("Inferno") disable_all("Guardian") - disable_all("Poltrgeist") + disable_all("Doom") disable_all("Goddess") - disable_battle_music("Doom") # Keep victory music for defeating the gauntlet + disable_battle_music("Poltrgeist") # Keep victory music for defeating the gauntlet def change_party(party): return [ @@ -187,9 +187,10 @@ def change_party(party): def invoke_kt_battle(party, original_pack_name, battle_sound = False): from data.bosses import pack_name + boss_id = self.get_boss(original_pack_name, False) return [ change_party(party), - field.InvokeBattle(self.get_boss(original_pack_name, False), battle_sound = battle_sound), + field.InvokeBattle(boss_id, battle_sound = battle_sound, battle_animation = True), ] party1_x = 103 @@ -251,18 +252,18 @@ def invoke_kt_battle(party, original_pack_name, battle_sound = False): x = 0, y = 0, fade_in = False, entrance_event = True), invoke_kt_battle(1, "Doom"), ], - # Fight Poltergeist - [ - field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), - invoke_kt_battle(2, "Poltrgeist"), - ], # Fight Goddess [ field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, x = 0, y = 0, fade_in = False, entrance_event = True), invoke_kt_battle(3, "Goddess"), ], + # Fight Poltergeist + [ + field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, + x = 0, y = 0, fade_in = False, entrance_event = True), + invoke_kt_battle(2, "Poltrgeist"), + ], # post battle, move to final room [ # Load final From 6c576892cb96f08da558f8c1c13e9908d4dd3ab0 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Mon, 30 May 2022 17:40:52 -0400 Subject: [PATCH 22/67] make music flow better --- event/kefka_tower.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index e5e925ac..68ab627b 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -176,7 +176,8 @@ def disable_all(boss_name): disable_all("Guardian") disable_all("Doom") disable_all("Goddess") - disable_battle_music("Poltrgeist") # Keep victory music for defeating the gauntlet + disable_all("Poltrgeist") + # disable_battle_music("Poltrgeist") # Keep victory music for defeating the gauntlet def change_party(party): return [ @@ -186,7 +187,6 @@ def change_party(party): ] def invoke_kt_battle(party, original_pack_name, battle_sound = False): - from data.bosses import pack_name boss_id = self.get_boss(original_pack_name, False) return [ change_party(party), @@ -227,48 +227,46 @@ def invoke_kt_battle(party, original_pack_name, battle_sound = False): field.SetEventBit(event_bit.CENTER_DOOR_KEFKA_TOWER), field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), - field.ClearEventBit(event_bit.TEMP_SONG_OVERRIDE), - field.HoldScreen(), - field.HideEntity(field_entity.PARTY0), - field.SetPartyMap(1, final_switch_map_id), - field.SetPartyMap(2, final_switch_map_id), - field.SetPartyMap(3, final_switch_map_id), - + # field.HoldScreen(), + field.StartSong(0x33), # Play "Battle to the Death" throughout the entirety of the gauntlet + field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), # Fight Inferno [ field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), + x = 0, y = 0, fade_in = False, entrance_event = False), invoke_kt_battle(3, "Inferno", True), ], # Fight Guardian [ field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), + x = 0, y = 0, fade_in = False, entrance_event = False), invoke_kt_battle(2, "Guardian"), ], # Fight Doom [ field.LoadMap(doom_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), + x = 0, y = 0, fade_in = False, entrance_event = False), invoke_kt_battle(1, "Doom"), ], # Fight Goddess [ field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), + x = 0, y = 0, fade_in = False, entrance_event = False), invoke_kt_battle(3, "Goddess"), ], # Fight Poltergeist [ field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), + x = 0, y = 0, fade_in = False, entrance_event = False), invoke_kt_battle(2, "Poltrgeist"), ], # post battle, move to final room [ # Load final - field.LoadMap(final_switch_map_id, direction.DOWN, default_music = True, - x = 109, y = 43, fade_in = False, entrance_event = True), + field.LoadMap(final_switch_map_id, direction.DOWN, default_music = False, + x = 109, y = 43, fade_in = False, entrance_event = False), + + field.ClearEventBit(event_bit.TEMP_SONG_OVERRIDE), # Party 1 init position [ @@ -359,7 +357,7 @@ def entrance_landing_mod(self): statues_entrance = 1287 self.dialogs.set_text(statues_entrance, - " (Gauntlet) (Entrance) (Not just yet)") + " (Gauntlet) (Not just yet)") space = Reserve(0xa01a2, 0xa02d5, "kefka tower first landing scene", field.NOP()) space.add_label("STATUE_LANDING", self.statue_landing) From 535e10c8c1492145df6af9d075bb9b463c9b1c46 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Mon, 30 May 2022 21:01:57 -0400 Subject: [PATCH 23/67] Finish adding cutscenes 1/2/5 --- event/kefka_tower.py | 135 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 28 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 68ab627b..c31a83ce 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -149,6 +149,12 @@ def statue_landing_mod(self): space = Reserve(0xa03ad, 0xa03af, "kefka tower the statues are up ahead", field.NOP()) + def invoke_kt_battle(self, party, original_pack_name, battle_sound = False): + boss_id = self.get_boss(original_pack_name, False) + return [ + field.InvokeBattle(boss_id, battle_sound = True, battle_animation = True), + ] + # Trigger five bosses back-to-back def boss_rush_mod(self): self.maps.disable_warp(final_switch_map_id) @@ -177,7 +183,6 @@ def disable_all(boss_name): disable_all("Doom") disable_all("Goddess") disable_all("Poltrgeist") - # disable_battle_music("Poltrgeist") # Keep victory music for defeating the gauntlet def change_party(party): return [ @@ -186,13 +191,6 @@ def change_party(party): field.UpdatePartyLeader(), ] - def invoke_kt_battle(party, original_pack_name, battle_sound = False): - boss_id = self.get_boss(original_pack_name, False) - return [ - change_party(party), - field.InvokeBattle(boss_id, battle_sound = battle_sound, battle_animation = True), - ] - party1_x = 103 party1_y_dest = 45 party1_y_offset = 6 @@ -226,39 +224,113 @@ def invoke_kt_battle(party, original_pack_name, battle_sound = False): field.SetEventBit(event_bit.SOUTH_PATH_OPEN_KEFKA_TOWER), field.SetEventBit(event_bit.CENTER_DOOR_KEFKA_TOWER), field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), - - # field.HoldScreen(), - field.StartSong(0x33), # Play "Battle to the Death" throughout the entirety of the gauntlet field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), + # Fight Inferno [ + change_party(3), field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = False), - invoke_kt_battle(3, "Inferno", True), + x = 27, y = 18, fade_in = False, entrance_event = True), + + # Move main party to east of inferno + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(39, 18), + ), + field.FadeInScreen(3), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.RIGHT, 4), + ), + + field.Pause(1), + + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.LEFT, 8), + ), + field.Call(0xc1872), # Inferno event tile address ], - # Fight Guardian + + # # Fight Guardian [ + change_party(2), field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = False), - invoke_kt_battle(2, "Guardian"), + x = 12, y = 8, fade_in = False, entrance_event = True), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetPosition(0, 0) + ), + field.FadeInScreen(3), + + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.DOWN, 6), + ), + field.Pause(1.5), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetPosition(12, 17), + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + ), + field.Call(0xc1827), ], # Fight Doom [ + change_party(1), field.LoadMap(doom_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = False), - invoke_kt_battle(1, "Doom"), + x = 0, y = 0, fade_in = False, entrance_event = True), + + self.invoke_kt_battle(1, "Doom", True), ], # Fight Goddess [ field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = False), - invoke_kt_battle(3, "Goddess"), + x = 0, y = 0, fade_in = False, entrance_event = True), + change_party(3), + self.invoke_kt_battle(3, "Goddess", True), ], # Fight Poltergeist [ + change_party(2), field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = False), - invoke_kt_battle(2, "Poltrgeist"), + x = 35, y = 25, fade_in = False, entrance_event = True), + field.FadeInScreen(3), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(27, 23) + ), + + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.LEFT, 6), + field_entity.Pause(20), + field_entity.Move(direction.UP, 6), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 3), + ), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.DOWN, 2), + field_entity.Move(direction.RIGHT, 2), + field_entity.Move(direction.DOWN, 3), + field_entity.Pause(10), + field_entity.Move(direction.UP, 3), + field_entity.Move(direction.RIGHT, 1), + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 6), + ), + field.Pause(4), + field.Pause(1.5), + + field.Call(0xc174f), + + field.HoldScreen(), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 8), + ), + field.FadeOutScreen(4), + field.Pause(1), ], # post battle, move to final room [ @@ -476,6 +548,7 @@ def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, d ]) def guardian_mod(self): + self.rom.set_bytes(0xc186c, [asm.NOP(), asm.NOP()]) self.kt_encounter_objective_mod( "Guardian", event_bit.DEFEATED_GUARDIAN, @@ -485,6 +558,8 @@ def guardian_mod(self): ) def inferno_mod(self): + self.rom.set_bytes(0xc18ae, [asm.NOP(), asm.NOP()]) + self.kt_encounter_objective_mod( "Inferno", event_bit.DEFEATED_INFERNO, @@ -539,12 +614,16 @@ def atma_mod(self): ) def inferno_battle_mod(self): - boss_pack_id = self.get_boss("Inferno") - - space = Reserve(0xc18a3, 0xc18a9, "kefka tower invoke battle inferno", field.NOP()) - space.write( - field.InvokeBattle(boss_pack_id), - ) + boss_src = [ + field.StartSong(0x33), + self.invoke_kt_battle(3, 'Inferno', True), + field.Return(), + ] + boss_space = Write(Bank.CC, boss_src, "trigger inferno fight, ") + space = Reserve(0xc18a2, 0xc18a9, "call inferno fight subroutine", asm.NOP()) + space.write([ + field.Call(boss_space.start_address) + ]) def inferno_skip_fix(self): # not sure why (stairs?) but this npc only blocks skipping the inferno event tile when entering from the east From f2d2a76218f7dd8d4b19f146ff49bbbbf36fcac8 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Tue, 31 May 2022 12:23:53 -0400 Subject: [PATCH 24/67] organizing cutscene src --- constants/maps.py | 2 +- constants/sound_effects.py | 11 + event/kefka_tower.py | 607 ++++++++++++++++++++++++------------- 3 files changed, 403 insertions(+), 217 deletions(-) create mode 100644 constants/sound_effects.py diff --git a/constants/maps.py b/constants/maps.py index 4e516f8d..daed0525 100644 --- a/constants/maps.py +++ b/constants/maps.py @@ -1,7 +1,7 @@ id_name = { - 0x123 : "Guardian Room", # 291 + 0x123 : "Guardian Room", # 291 0x14e : "Poltergeist Room", # 334 0x162 : "Goddess Room", # 354 0x163 : "Doom Room", # 355 diff --git a/constants/sound_effects.py b/constants/sound_effects.py new file mode 100644 index 00000000..9762a988 --- /dev/null +++ b/constants/sound_effects.py @@ -0,0 +1,11 @@ + + +id_name = { + 0x9a : "Ground Breaking", # 154 + 0xa6 : "Chest/Switch", # 166 + 0xa9 : "Umaro Body Slam", # 169 + 0xb5 : "Landing On Floor", # 181 + 0xba : "Falling", # 186 +} + +name_id = {v: k for k, v in id_name.items()} diff --git a/event/kefka_tower.py b/event/kefka_tower.py index c31a83ce..eed491c0 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -1,13 +1,22 @@ from event.event import * import args -from constants.maps import name_id -final_switch_map_id = name_id["KT Final Switch Room"] -inferno_room_id = name_id["Inferno Room"] -guardian_room_id = name_id["Guardian Room"] -poltergeist_room_id = name_id["Poltergeist Room"] -doom_room_id = name_id["Doom Room"] -goddess_room_id = name_id["Goddess Room"] +from constants.sound_effects import name_id as sfx_name_id +from constants.maps import name_id as map_name_id +final_switch_map_id = map_name_id["KT Final Switch Room"] +inferno_room_id = map_name_id["Inferno Room"] +guardian_room_id = map_name_id["Guardian Room"] +poltergeist_room_id = map_name_id["Poltergeist Room"] +doom_room_id = map_name_id["Doom Room"] +goddess_room_id = map_name_id["Goddess Room"] + +def change_party(party): + return [ + field.SetParty(party), + field.RefreshEntities(), + field.UpdatePartyLeader(), + ] + class KefkaTower(Event): def name(self): @@ -184,27 +193,6 @@ def disable_all(boss_name): disable_all("Goddess") disable_all("Poltrgeist") - def change_party(party): - return [ - field.SetParty(party), - field.RefreshEntities(), - field.UpdatePartyLeader(), - ] - - party1_x = 103 - party1_y_dest = 45 - party1_y_offset = 6 - party1_y_start = party1_y_dest - party1_y_offset - - party2_x = 109 - party2_y_dest = 42 - party2_y_offset = 9 - party2_y_start = party2_y_dest - party2_y_offset - - party3_x = 115 - party3_y_dest = 44 - party3_y_offset = 8 - party3_y_start = party3_y_dest - party3_y_offset src = [ Read(0xa02d6, 0xa030a), @@ -226,193 +214,12 @@ def change_party(party): field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), - # Fight Inferno - [ - change_party(3), - field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, - x = 27, y = 18, fade_in = False, entrance_event = True), - - # Move main party to east of inferno - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(39, 18), - ), - field.FadeInScreen(3), - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.Move(direction.RIGHT, 4), - ), - - field.Pause(1), - - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.LEFT, 8), - ), - field.Call(0xc1872), # Inferno event tile address - ], - - # # Fight Guardian - [ - change_party(2), - field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, - x = 12, y = 8, fade_in = False, entrance_event = True), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetPosition(0, 0) - ), - field.FadeInScreen(3), - - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.Move(direction.DOWN, 6), - ), - field.Pause(1.5), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetPosition(12, 17), - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.UP, 3), - ), - field.Call(0xc1827), - ], - # Fight Doom - [ - change_party(1), - field.LoadMap(doom_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), - - self.invoke_kt_battle(1, "Doom", True), - ], - # Fight Goddess - [ - field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, - x = 0, y = 0, fade_in = False, entrance_event = True), - change_party(3), - self.invoke_kt_battle(3, "Goddess", True), - ], - # Fight Poltergeist - [ - change_party(2), - field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, - x = 35, y = 25, fade_in = False, entrance_event = True), - field.FadeInScreen(3), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(27, 23) - ), - - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.LEFT, 6), - field_entity.Pause(20), - field_entity.Move(direction.UP, 6), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 3), - ), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.DOWN, 2), - field_entity.Move(direction.RIGHT, 2), - field_entity.Move(direction.DOWN, 3), - field_entity.Pause(10), - field_entity.Move(direction.UP, 3), - field_entity.Move(direction.RIGHT, 1), - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.UP, 3), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 6), - ), - field.Pause(4), - field.Pause(1.5), - - field.Call(0xc174f), - - field.HoldScreen(), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 8), - ), - field.FadeOutScreen(4), - field.Pause(1), - ], - # post battle, move to final room - [ - # Load final - field.LoadMap(final_switch_map_id, direction.DOWN, default_music = False, - x = 109, y = 43, fade_in = False, entrance_event = False), - - field.ClearEventBit(event_bit.TEMP_SONG_OVERRIDE), - - # Party 1 init position - [ - change_party(1), - Read(0xa0334, 0xa033c), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(party1_x, party1_y_start), - field_entity.AnimateFrontHandsUp(), - field_entity.SetSpeed(field_entity.Speed.FAST), - ), - ], - # Party 2 init position - [ - Read(0xa031e, 0xa0320), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(party2_x, party2_y_start), - field_entity.SetSpeed(field_entity.Speed.FAST), - ), - ], - # Party 3 init position - [ - Read(0xa0327, 0xa032d), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(party3_x, party3_y_start), - field_entity.SetSpeed(field_entity.Speed.FAST), - ), - ], - field.FadeInScreen(), - ], - # party 1 fall - [ - change_party(1), - field.EntityAct(field_entity.PARTY0, True, - field_entity.DisableWalkingAnimation(), - field_entity.AnimateSurprised(), - field_entity.Move(direction.DOWN, party1_y_offset), - field_entity.AnimateKneeling(), - field_entity.EnableWalkingAnimation(), - ), - ], - # party 2 fall - [ - change_party(2), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpriteLayer(2), - field_entity.DisableWalkingAnimation(), - field_entity.AnimateSurprised(), - field_entity.Move(direction.DOWN, party2_y_offset - 1), - field_entity.Move(direction.DOWN, 1), - field_entity.AnimateKneeling(), - field_entity.EnableWalkingAnimation(), - field_entity.SetSpriteLayer(0), - ), - ], - # party 3 fall - [ - change_party(3), - field.FadeOutSong(64), - field.Pause(0.75), - field.EntityAct(field_entity.PARTY0, True, - field_entity.DisableWalkingAnimation(), - field_entity.AnimateSurprised(), - field_entity.Move(direction.DOWN, party3_y_offset), - field_entity.AnimateKneeling(), - ), - ], - field.Pause(0.75), - - Read(0xa039c, 0xa039f), - field.LoadMap(final_switch_map_id, direction.DOWN, default_music = True, - x = party1_x, y = party1_y_dest, fade_in = True, entrance_event = True), - - field.FreeScreen(), - Read(0xa03b0, 0xa03b9), + inferno_cutscene_src(), + guardian_cutscene_src(), + doom_cutscene_src(), + goddess_cutscene_src(), + poltrgeist_cutscene_src(), + post_gauntlet_cutscene_src(), ] space = Write(Bank.CA, src, "kefka tower statue landing") self.statue_landing = space.start_address @@ -558,7 +365,8 @@ def guardian_mod(self): ) def inferno_mod(self): - self.rom.set_bytes(0xc18ae, [asm.NOP(), asm.NOP()]) + self.rom.set_byte(0xc18a2, 0xea) + self.rom.set_bytes(0xc18ae, [0xea, 0xea]) self.kt_encounter_objective_mod( "Inferno", @@ -804,3 +612,370 @@ def exit_mod(self): # leaving with crane or warp stone # warp stone call trace: c0c670 -> ca0039 -> ca0108 -> ca014f -> cc0ff6 -> cc0f7d space = Reserve(0xc0fbf, 0xc0fc0, "kefka tower exit clear poltrgeist statue bit", asm.NOP()) + +def inferno_cutscene_src(): + return [ + change_party(3), + field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, + x = 27, y = 18, fade_in = False, entrance_event = True), + + # Move main party to east of inferno + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(39, 18), + ), + field.FadeInScreen(3), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.RIGHT, 4), + ), + + field.Pause(1), + + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.LEFT, 8), + ), + field.Call(0xc1872), # Inferno event tile address + ] + + +# Fight Guardian +def guardian_cutscene_src(): + return [ + field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, + x = 12, y = 8, fade_in = False, entrance_event = True), + change_party(1), + field.EntityAct(0x10, False, + field_entity.SetPosition(11, 9) + ), + field.EntityAct(0x13, True, + field_entity.SetPosition(12, 9) + ), + field.EntityAct(0x16, False, + field_entity.SetPosition(13, 9) + ), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetPosition(6, 15), + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + field_entity.Move(direction.LEFT, 1), + field_entity.AnimateKneeling(), + field_entity.Pause(20), + + field_entity.Move(direction.RIGHT, 2), + field_entity.AnimateKneeling(), + field_entity.Pause(20), + field_entity.Move(direction.DOWN, 1), + + field_entity.AnimateFrontRightHandUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + + field_entity.AnimateSurprised(), + field_entity.AnimateLowJump(), + field_entity.Pause(10), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.LEFT, 1), + field_entity.Move(direction.DOWN, 3), + field_entity.SetPosition(0, 0) + ), + + change_party(3), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.SetPosition(18, 15), + field_entity.Move(direction.UP, 3), + field_entity.Move(direction.LEFT, 1), + field_entity.Pause(15), + field_entity.Move(direction.DOWN, 1), + field_entity.Turn(direction.LEFT), + field_entity.Pause(15), + field_entity.AnimateFrontRightHandUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.AnimateSurprised(), + field_entity.AnimateLowJump(), + field_entity.Pause(10), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.RIGHT, 1), + field_entity.Move(direction.DOWN, 3), + field_entity.SetPosition(0, 0) + ), + + + field.FadeInScreen(3), + + change_party(2), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetPosition(12, 17) + ), + + + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.DOWN, 6), + ), + field.Pause(2), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + field_entity.AnimateFrontHandsUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.Pause(5), + field_entity.AnimateFrontHandsUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + ), + field.Pause(1), + field.EntityAct(field_entity.PARTY0, False, + field_entity.AnimateAttack(), + field_entity.AnimateLowJump(), + field_entity.Pause(5), + field_entity.AnimateKnockedOut(), + field_entity.Pause(10), + field_entity.AnimateFrontHandsUp(), + field_entity.AnimateLowJump(), + field_entity.Pause(3), + field_entity.AnimateKneeling(), + field_entity.Pause(5), + field_entity.AnimateKneeling(), + field_entity.Pause(5), + field_entity.AnimateStandingHeadDown(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.Pause(5), + field_entity.AnimateAttack(), + field_entity.AnimateLowJump(), + ), + field.Call(0xc1827), + ] + + + +# Fight Doom +def doom_cutscene_src(): + return [ + change_party(1), + field.LoadMap(doom_room_id, direction.DOWN, default_music = False, + x = 64, y = 15, fade_in = False, entrance_event = True), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(64, 21), + ), + field.FadeInScreen(3), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOWEST), + field_entity.Move(direction.UP, 3), + ), + field.Pause(2), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 8), + field_entity.Move(direction.UP, 1) + ), + + field.Call(0xc16d6), + + field.HoldScreen(), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.LEFT, 1), + field_entity.Move(direction.UP, 3), + ), + field.FadeOutScreen(3), + field.Pause(1), + ] + + +# Fight Goddess +def goddess_cutscene_src(): + return [ + change_party(3), + field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, + x = 12, y = 28, fade_in = False, entrance_event = True), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(12, 40) + ), + field.FadeInScreen(3), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.DOWN, 5) + ), + field.Pause(1.5), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 8) + ), + field.Call(0xc1716), + field.HoldScreen(), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Pause(8), + field_entity.Move(direction.UP, 2), + field_entity.EnableWalkingAnimation(), + field_entity.AnimateLowJump(), + ), + field.FadeOutScreen(4), + field.Pause(1), + ] + + +# Fight Poltrgeist +def poltrgeist_cutscene_src(): + return [ + change_party(2), + field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, + x = 35, y = 25, fade_in = False, entrance_event = True), + field.FadeInScreen(3), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(27, 23) + ), + + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.LEFT, 6), + field_entity.Pause(20), + field_entity.Move(direction.UP, 6), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 3), + ), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.DOWN, 2), + field_entity.Move(direction.RIGHT, 2), + field_entity.Move(direction.DOWN, 3), + field_entity.AnimateKneeling(), + field_entity.Pause(10), + field_entity.AnimateStandingFront(), + field_entity.Pause(2), + field_entity.Move(direction.UP, 3), + field_entity.Move(direction.RIGHT, 1), + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 6), + ), + field.Pause(2), + field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), + field.Pause(2), + field.Pause(1.5), + + field.Call(0xc174f), + + field.HoldScreen(), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 8), + ), + field.FadeOutScreen(4), + field.Pause(1), + ] + +def post_gauntlet_cutscene_src(): + party1_x = 103 + party1_y_dest = 45 + party1_y_offset = 8 + party1_y_start = party1_y_dest - party1_y_offset + + party2_x = 109 + party2_y_dest = 42 + party2_y_offset = 9 + party2_y_start = party2_y_dest - party2_y_offset + + party3_x = 115 + party3_y_dest = 44 + party3_y_offset = 8 + party3_y_start = party3_y_dest - party3_y_offset + return [ + [ + # Load final + field.LoadMap(final_switch_map_id, direction.DOWN, default_music = False, + x = 109, y = 43, fade_in = False, entrance_event = False), + + field.ClearEventBit(event_bit.TEMP_SONG_OVERRIDE), + + # Party 1 init position + [ + change_party(1), + Read(0xa0334, 0xa033c), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party1_x, party1_y_start), + field_entity.AnimateFrontHandsUp(), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + # Party 2 init position + [ + Read(0xa031e, 0xa0320), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party2_x, party2_y_start), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + # Party 3 init position + [ + Read(0xa0327, 0xa032d), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party3_x, party3_y_start), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + field.FadeInScreen(), + ], + # party 1 fall + [ + change_party(1), + field.PlaySoundEffect(sfx_name_id.get('Falling')), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party1_y_offset), + field_entity.AnimateKneeling(), + field_entity.EnableWalkingAnimation(), + ), + field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), + ], + # party 2 fall + [ + change_party(2), + field.Pause(0.25), + field.PlaySoundEffect(sfx_name_id.get('Falling')), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.SetSpriteLayer(2), + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party2_y_offset - 6), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.DOWN, 6), + field_entity.AnimateKneeling(), + field_entity.EnableWalkingAnimation(), + field_entity.SetSpriteLayer(0), + ), + field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), + ], + # party 3 fall + [ + change_party(3), + field.Pause(0.25), + field.PlaySoundEffect(sfx_name_id.get('Falling')), + + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party3_y_offset), + field_entity.AnimateKneeling(), + ), + field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), + ], + field.Pause(0.75), + + Read(0xa039c, 0xa039f), + field.LoadMap(final_switch_map_id, direction.DOWN, default_music = True, + x = party1_x, y = party1_y_dest, fade_in = True, entrance_event = True), + + field.FreeScreen(), + Read(0xa03b0, 0xa03b9), + ] \ No newline at end of file From 698b53109e5ebe649a890afc93c29efadf77db62 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 1 Jun 2022 11:49:26 -0400 Subject: [PATCH 25/67] General code cleanup, add sfx --- event/kefka_tower.py | 712 ++++++++++++++++++++++--------------------- 1 file changed, 364 insertions(+), 348 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index eed491c0..7ffafcc6 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -1,8 +1,10 @@ from event.event import * import args -from constants.sound_effects import name_id as sfx_name_id from constants.maps import name_id as map_name_id +from constants.songs import name_id as song_name_id +from constants.sound_effects import name_id as sfx_name_id + final_switch_map_id = map_name_id["KT Final Switch Room"] inferno_room_id = map_name_id["Inferno Room"] guardian_room_id = map_name_id["Guardian Room"] @@ -119,6 +121,7 @@ def statue_landing_mod(self): field_entity.AnimateKneeling(), field_entity.EnableWalkingAnimation(), ), + field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), field.SetParty(2), field.RefreshEntities(), @@ -136,6 +139,8 @@ def statue_landing_mod(self): field_entity.SetSpriteLayer(0), ), + field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), + field.SetParty(3), field.RefreshEntities(), field.UpdatePartyLeader(), @@ -145,13 +150,12 @@ def statue_landing_mod(self): field_entity.Move(direction.DOWN, 6), field_entity.AnimateKneeling(), ), + + field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), + field.Pause(0.75), - Read(0xa039c, 0xa039f), - field.LoadMap(0x163, direction.DOWN, default_music = True, - x = 35, y = 6, fade_in = True, entrance_event = True), - field.FreeScreen(), - Read(0xa03b0, 0xa03b9), + self.post_landing_src(0x163, 35, 6), ] space = Write(Bank.CA, src, "kefka tower statue landing") self.statue_landing = space.start_address @@ -214,12 +218,13 @@ def disable_all(boss_name): field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), - inferno_cutscene_src(), - guardian_cutscene_src(), - doom_cutscene_src(), - goddess_cutscene_src(), - poltrgeist_cutscene_src(), - post_gauntlet_cutscene_src(), + # self.gauntlet_inferno_cutscene_src(), + # self.gauntlet_guardian_cutscene_src(), + # self.gauntlet_doom_cutscene_src(), + # self.gauntlet_goddess_cutscene_src(), + # self.gauntlet_poltrgeist_cutscene_src(), + + self.gauntlet_post_battle_cutscene_src(), ] space = Write(Bank.CA, src, "kefka tower statue landing") self.statue_landing = space.start_address @@ -423,7 +428,7 @@ def atma_mod(self): def inferno_battle_mod(self): boss_src = [ - field.StartSong(0x33), + field.StartSong(song_name_id['FierceBattle']), self.invoke_kt_battle(3, 'Inferno', True), field.Return(), ] @@ -603,6 +608,17 @@ def final_scenes_mod(self): space = Reserve(0xa1330, 0xa1332, "kefka tower you mean terra too?", field.NOP()) space = Reserve(0xa133e, 0xa1340, "kefka tower come with me. i can lead you out", field.NOP()) + # Load character into the given map and position, then return control (with KT prompt) + def post_landing_src(self, map_id, map_x, map_y): + return [ + Read(0xa039c, 0xa039f), + field.LoadMap(map_id, direction.DOWN, default_music = True, + x = map_x, y = map_y, fade_in = True, entrance_event = True), + + field.FreeScreen(), + Read(0xa03b0, 0xa03b9), + ] + def exit_mod(self): # for some reason poltrgeist's statue bit was set when left/right parties opened center door # and it was cleared when leaving or warping out @@ -613,369 +629,369 @@ def exit_mod(self): # warp stone call trace: c0c670 -> ca0039 -> ca0108 -> ca014f -> cc0ff6 -> cc0f7d space = Reserve(0xc0fbf, 0xc0fc0, "kefka tower exit clear poltrgeist statue bit", asm.NOP()) -def inferno_cutscene_src(): - return [ - change_party(3), - field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, - x = 27, y = 18, fade_in = False, entrance_event = True), - - # Move main party to east of inferno - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(39, 18), - ), - field.FadeInScreen(3), - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.Move(direction.RIGHT, 4), - ), - - field.Pause(1), - - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.LEFT, 8), - ), - field.Call(0xc1872), # Inferno event tile address - ] + def gauntlet_inferno_cutscene_src(self): + return [ + change_party(3), + field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, + x = 27, y = 18, fade_in = False, entrance_event = True), + # Move main party to east of inferno + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(39, 18), + ), + field.FadeInScreen(3), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.RIGHT, 4), + ), -# Fight Guardian -def guardian_cutscene_src(): - return [ - field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, - x = 12, y = 8, fade_in = False, entrance_event = True), - change_party(1), - field.EntityAct(0x10, False, - field_entity.SetPosition(11, 9) - ), - field.EntityAct(0x13, True, - field_entity.SetPosition(12, 9) - ), - field.EntityAct(0x16, False, - field_entity.SetPosition(13, 9) - ), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetPosition(6, 15), - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.UP, 3), - field_entity.Move(direction.LEFT, 1), - field_entity.AnimateKneeling(), - field_entity.Pause(20), - - field_entity.Move(direction.RIGHT, 2), - field_entity.AnimateKneeling(), - field_entity.Pause(20), - field_entity.Move(direction.DOWN, 1), - - field_entity.AnimateFrontRightHandUp(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - - field_entity.AnimateSurprised(), - field_entity.AnimateLowJump(), - field_entity.Pause(10), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.LEFT, 1), - field_entity.Move(direction.DOWN, 3), - field_entity.SetPosition(0, 0) - ), - - change_party(3), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.SetPosition(18, 15), - field_entity.Move(direction.UP, 3), - field_entity.Move(direction.LEFT, 1), - field_entity.Pause(15), - field_entity.Move(direction.DOWN, 1), - field_entity.Turn(direction.LEFT), - field_entity.Pause(15), - field_entity.AnimateFrontRightHandUp(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - field_entity.AnimateSurprised(), - field_entity.AnimateLowJump(), - field_entity.Pause(10), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.RIGHT, 1), - field_entity.Move(direction.DOWN, 3), - field_entity.SetPosition(0, 0) - ), - - - field.FadeInScreen(3), - - change_party(2), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetPosition(12, 17) - ), - - - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.Move(direction.DOWN, 6), - ), - field.Pause(2), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.UP, 3), - field_entity.AnimateFrontHandsUp(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - field_entity.Pause(5), - field_entity.AnimateFrontHandsUp(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - ), - field.Pause(1), - field.EntityAct(field_entity.PARTY0, False, - field_entity.AnimateAttack(), - field_entity.AnimateLowJump(), - field_entity.Pause(5), - field_entity.AnimateKnockedOut(), - field_entity.Pause(10), - field_entity.AnimateFrontHandsUp(), - field_entity.AnimateLowJump(), - field_entity.Pause(3), - field_entity.AnimateKneeling(), - field_entity.Pause(5), - field_entity.AnimateKneeling(), - field_entity.Pause(5), - field_entity.AnimateStandingHeadDown(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - field_entity.Pause(5), - field_entity.AnimateAttack(), - field_entity.AnimateLowJump(), - ), - field.Call(0xc1827), - ] + field.Pause(1), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.LEFT, 8), + ), + field.Call(0xc1872), # Inferno event tile address + ] + def gauntlet_guardian_cutscene_src(self): + return [ + field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, + x = 12, y = 8, fade_in = False, entrance_event = True), + change_party(1), + field.EntityAct(0x10, False, + field_entity.SetPosition(11, 9) + ), + field.EntityAct(0x13, True, + field_entity.SetPosition(12, 9) + ), + field.EntityAct(0x16, False, + field_entity.SetPosition(13, 9) + ), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetPosition(6, 15), + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + field_entity.Move(direction.LEFT, 1), + field_entity.AnimateKneeling(), + field_entity.Pause(20), -# Fight Doom -def doom_cutscene_src(): - return [ - change_party(1), - field.LoadMap(doom_room_id, direction.DOWN, default_music = False, - x = 64, y = 15, fade_in = False, entrance_event = True), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(64, 21), - ), - field.FadeInScreen(3), - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.SLOWEST), - field_entity.Move(direction.UP, 3), - ), - field.Pause(2), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 8), - field_entity.Move(direction.UP, 1) - ), - - field.Call(0xc16d6), - - field.HoldScreen(), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.LEFT, 1), - field_entity.Move(direction.UP, 3), - ), - field.FadeOutScreen(3), - field.Pause(1), - ] + field_entity.Move(direction.RIGHT, 2), + field_entity.AnimateKneeling(), + field_entity.Pause(20), + field_entity.Move(direction.DOWN, 1), + field_entity.AnimateFrontRightHandUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), -# Fight Goddess -def goddess_cutscene_src(): - return [ - change_party(3), - field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, - x = 12, y = 28, fade_in = False, entrance_event = True), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(12, 40) - ), - field.FadeInScreen(3), - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.Move(direction.DOWN, 5) - ), - field.Pause(1.5), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 8) - ), - field.Call(0xc1716), - field.HoldScreen(), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Pause(8), - field_entity.Move(direction.UP, 2), - field_entity.EnableWalkingAnimation(), - field_entity.AnimateLowJump(), - ), - field.FadeOutScreen(4), - field.Pause(1), - ] + field_entity.AnimateSurprised(), + field_entity.AnimateLowJump(), + field_entity.Pause(10), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.LEFT, 1), + field_entity.Move(direction.DOWN, 3), + field_entity.SetPosition(0, 0) + ), + change_party(3), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.SetPosition(18, 15), + field_entity.Move(direction.UP, 3), + field_entity.Move(direction.LEFT, 1), + field_entity.Pause(15), + field_entity.Move(direction.DOWN, 1), + field_entity.Turn(direction.LEFT), + field_entity.Pause(15), + field_entity.AnimateFrontRightHandUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.AnimateSurprised(), + field_entity.AnimateLowJump(), + field_entity.Pause(10), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.RIGHT, 1), + field_entity.Move(direction.DOWN, 3), + field_entity.SetPosition(0, 0) + ), -# Fight Poltrgeist -def poltrgeist_cutscene_src(): - return [ - change_party(2), - field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, - x = 35, y = 25, fade_in = False, entrance_event = True), - field.FadeInScreen(3), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(27, 23) - ), - - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.LEFT, 6), - field_entity.Pause(20), - field_entity.Move(direction.UP, 6), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 3), - ), + + field.FadeInScreen(3), + + change_party(2), field.EntityAct(field_entity.PARTY0, False, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.DOWN, 2), - field_entity.Move(direction.RIGHT, 2), - field_entity.Move(direction.DOWN, 3), - field_entity.AnimateKneeling(), - field_entity.Pause(10), - field_entity.AnimateStandingFront(), - field_entity.Pause(2), - field_entity.Move(direction.UP, 3), - field_entity.Move(direction.RIGHT, 1), - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.UP, 3), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 6), - ), - field.Pause(2), - field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), - field.Pause(2), - field.Pause(1.5), - - field.Call(0xc174f), - - field.HoldScreen(), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 8), - ), - field.FadeOutScreen(4), - field.Pause(1), - ] + field_entity.SetPosition(12, 17) + ), -def post_gauntlet_cutscene_src(): - party1_x = 103 - party1_y_dest = 45 - party1_y_offset = 8 - party1_y_start = party1_y_dest - party1_y_offset - - party2_x = 109 - party2_y_dest = 42 - party2_y_offset = 9 - party2_y_start = party2_y_dest - party2_y_offset - - party3_x = 115 - party3_y_dest = 44 - party3_y_offset = 8 - party3_y_start = party3_y_dest - party3_y_offset - return [ - [ - # Load final - field.LoadMap(final_switch_map_id, direction.DOWN, default_music = False, - x = 109, y = 43, fade_in = False, entrance_event = False), - field.ClearEventBit(event_bit.TEMP_SONG_OVERRIDE), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.DOWN, 6), + ), + field.Pause(2), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + field_entity.AnimateFrontHandsUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.Pause(5), + field_entity.AnimateFrontHandsUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + ), + field.Pause(1), + field.EntityAct(field_entity.PARTY0, False, + field_entity.AnimateAttack(), + field_entity.AnimateLowJump(), + field_entity.Pause(5), + field_entity.AnimateKnockedOut(), + field_entity.Pause(10), + field_entity.AnimateFrontHandsUp(), + field_entity.AnimateLowJump(), + field_entity.Pause(3), + field_entity.AnimateKneeling(), + field_entity.Pause(5), + field_entity.AnimateKneeling(), + field_entity.Pause(5), + field_entity.AnimateStandingHeadDown(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.Pause(5), + field_entity.AnimateAttack(), + field_entity.AnimateLowJump(), + ), + field.Call(0xc1827), + ] + + def gauntlet_doom_cutscene_src(self): + return [ + change_party(1), + field.LoadMap(doom_room_id, direction.DOWN, default_music = False, + x = 64, y = 15, fade_in = False, entrance_event = True), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(64, 21), + ), + field.FadeInScreen(3), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOWEST), + field_entity.Move(direction.UP, 3), + ), + field.Pause(2), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 8), + field_entity.Move(direction.UP, 1) + ), - # Party 1 init position + field.Call(0xc16d6), + + field.HoldScreen(), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.LEFT, 1), + field_entity.Move(direction.UP, 3), + ), + field.FadeOutScreen(3), + field.Pause(1), + ] + + def gauntlet_goddess_cutscene_src(self): + return [ + change_party(3), + field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, + x = 12, y = 28, fade_in = False, entrance_event = True), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(12, 40) + ), + field.FadeInScreen(3), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.DOWN, 5) + ), + field.Pause(1.5), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 8) + ), + field.Call(0xc1716), + field.HoldScreen(), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Pause(8), + field_entity.Move(direction.UP, 2), + field_entity.EnableWalkingAnimation(), + field_entity.AnimateLowJump(), + ), + field.FadeOutScreen(4), + field.Pause(1), + ] + + def gauntlet_poltrgeist_cutscene_src(self): + + return [ + change_party(2), + field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, + x = 35, y = 25, fade_in = False, entrance_event = True), + field.FadeInScreen(3), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(27, 23) + ), + + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.LEFT, 6), + field_entity.Pause(20), + field_entity.Move(direction.UP, 6), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 3), + ), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.DOWN, 2), + field_entity.Move(direction.RIGHT, 2), + field_entity.Move(direction.DOWN, 3), + field_entity.AnimateKneeling(), + field_entity.Pause(10), + field_entity.AnimateStandingFront(), + field_entity.Pause(2), + field_entity.Move(direction.UP, 3), + field_entity.Move(direction.RIGHT, 1), + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 6), + ), + field.Pause(2), + field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), + field.Pause(2), + field.Pause(1.5), + + field.Call(0xc174f), + + field.HoldScreen(), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 8), + ), + field.FadeOutScreen(4), + field.Pause(1), + ] + + def gauntlet_post_battle_cutscene_src(self): + party1_x = 103 + party1_y_dest = 45 + party1_y_offset = 10 + party1_y_start = party1_y_dest - party1_y_offset + + party2_x = 109 + party2_y_dest = 42 + party2_y_offset = 9 + party2_y_start = party2_y_dest - party2_y_offset + + party3_x = 115 + party3_y_dest = 44 + party3_y_offset = 8 + party3_y_start = party3_y_dest - party3_y_offset + return [ + [ + # Load final + field.LoadMap(final_switch_map_id, direction.DOWN, default_music = False, + x = 109, y = 43, fade_in = False, entrance_event = False), + field.FadeOutSong(255), + field.ClearEventBit(event_bit.TEMP_SONG_OVERRIDE), + + # Party 1 init position + [ + change_party(1), + Read(0xa0334, 0xa033c), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party1_x, party1_y_start), + field_entity.AnimateFrontHandsUp(), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + # Party 2 init position + [ + Read(0xa031e, 0xa0320), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party2_x, party2_y_start), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + # Party 3 init position + [ + Read(0xa0327, 0xa032d), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(party3_x, party3_y_start), + field_entity.SetSpeed(field_entity.Speed.FAST), + ), + ], + field.FadeInScreen(), + ], + # party 1 fall [ change_party(1), - Read(0xa0334, 0xa033c), + field.PlaySoundEffect(sfx_name_id.get('Falling')), field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(party1_x, party1_y_start), - field_entity.AnimateFrontHandsUp(), field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party1_y_offset - 2), + field_entity.Move(direction.DOWN, 2), + field_entity.AnimateKneeling(), + field_entity.EnableWalkingAnimation(), ), + field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), ], - # Party 2 init position + field.Pause(0.5), + # party 2 fall [ - Read(0xa031e, 0xa0320), + change_party(2), + field.Pause(0.25), + field.PlaySoundEffect(sfx_name_id.get('Falling')), field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(party2_x, party2_y_start), field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.SetSpriteLayer(2), + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party2_y_offset - 2), # extra offset is added to the next entity script + field_entity.SetSpeed(field_entity.Speed.FASTEST), + field_entity.AnimateLowJump(), + field_entity.AnimateKnockedOut(), + ), + field.PauseUnits(3), + field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), + + field.EntityAct(field_entity.PARTY0, False, + field_entity.Pause(3), + field_entity.Move(direction.DOWN, 2), + field_entity.AnimateKneeling(), + field_entity.EnableWalkingAnimation(), + field_entity.SetSpriteLayer(0), ), + field.PauseUnits(20), + field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), ], - # Party 3 init position + field.Pause(0.5), + # party 3 fall [ - Read(0xa0327, 0xa032d), + change_party(3), + field.Pause(0.25), + field.PlaySoundEffect(sfx_name_id.get('Falling')), + field.EntityAct(field_entity.PARTY0, True, - field_entity.SetPosition(party3_x, party3_y_start), field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.DisableWalkingAnimation(), + field_entity.AnimateSurprised(), + field_entity.Move(direction.DOWN, party3_y_offset), + field_entity.AnimateKneeling(), ), + field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), ], - field.FadeInScreen(), - ], - # party 1 fall - [ - change_party(1), - field.PlaySoundEffect(sfx_name_id.get('Falling')), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.DisableWalkingAnimation(), - field_entity.AnimateSurprised(), - field_entity.Move(direction.DOWN, party1_y_offset), - field_entity.AnimateKneeling(), - field_entity.EnableWalkingAnimation(), - ), - field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), - ], - # party 2 fall - [ - change_party(2), - field.Pause(0.25), - field.PlaySoundEffect(sfx_name_id.get('Falling')), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.SetSpriteLayer(2), - field_entity.DisableWalkingAnimation(), - field_entity.AnimateSurprised(), - field_entity.Move(direction.DOWN, party2_y_offset - 6), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.DOWN, 6), - field_entity.AnimateKneeling(), - field_entity.EnableWalkingAnimation(), - field_entity.SetSpriteLayer(0), - ), - field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), - ], - # party 3 fall - [ - change_party(3), - field.Pause(0.25), - field.PlaySoundEffect(sfx_name_id.get('Falling')), - - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.DisableWalkingAnimation(), - field_entity.AnimateSurprised(), - field_entity.Move(direction.DOWN, party3_y_offset), - field_entity.AnimateKneeling(), - ), - field.PlaySoundEffect(sfx_name_id.get('Umaro Body Slam')), - ], - field.Pause(0.75), - Read(0xa039c, 0xa039f), - field.LoadMap(final_switch_map_id, direction.DOWN, default_music = True, - x = party1_x, y = party1_y_dest, fade_in = True, entrance_event = True), + field.Pause(0.75), - field.FreeScreen(), - Read(0xa03b0, 0xa03b9), - ] \ No newline at end of file + self.post_landing_src(final_switch_map_id, party1_x, party1_y_dest), + ] \ No newline at end of file From 62649f04b239b8f08d63a9a5f88f1c6755d62759 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:01:46 -0400 Subject: [PATCH 26/67] lookin clean --- event/kefka_tower.py | 251 +++++++++++++++++++++++++------------------ 1 file changed, 148 insertions(+), 103 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 7ffafcc6..8a078042 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -218,12 +218,11 @@ def disable_all(boss_name): field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), - # self.gauntlet_inferno_cutscene_src(), - # self.gauntlet_guardian_cutscene_src(), - # self.gauntlet_doom_cutscene_src(), - # self.gauntlet_goddess_cutscene_src(), - # self.gauntlet_poltrgeist_cutscene_src(), - + self.gauntlet_inferno_cutscene_src(), + self.gauntlet_guardian_cutscene_src(), + self.gauntlet_doom_cutscene_src(), + self.gauntlet_goddess_cutscene_src(), + self.gauntlet_poltrgeist_cutscene_src(), self.gauntlet_post_battle_cutscene_src(), ] space = Write(Bank.CA, src, "kefka tower statue landing") @@ -657,111 +656,157 @@ def gauntlet_inferno_cutscene_src(self): def gauntlet_guardian_cutscene_src(self): return [ field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, - x = 12, y = 8, fade_in = False, entrance_event = True), - change_party(1), - field.EntityAct(0x10, False, - field_entity.SetPosition(11, 9) - ), - field.EntityAct(0x13, True, - field_entity.SetPosition(12, 9) - ), - field.EntityAct(0x16, False, - field_entity.SetPosition(13, 9) - ), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetPosition(6, 15), - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.UP, 3), - field_entity.Move(direction.LEFT, 1), - field_entity.AnimateKneeling(), - field_entity.Pause(20), - - field_entity.Move(direction.RIGHT, 2), - field_entity.AnimateKneeling(), - field_entity.Pause(20), - field_entity.Move(direction.DOWN, 1), - - field_entity.AnimateFrontRightHandUp(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), + x = 12, y = 14, fade_in = False, entrance_event = True), + # Initialize party positions + [ + change_party(1), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.SetPosition(6, 15), + field_entity.Turn(direction.UP), + ), + change_party(2), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.SetPosition(12, 17), + field_entity.Turn(direction.UP), + ), - field_entity.AnimateSurprised(), - field_entity.AnimateLowJump(), - field_entity.Pause(10), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.LEFT, 1), - field_entity.Move(direction.DOWN, 3), - field_entity.SetPosition(0, 0) - ), + change_party(3), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.SetPosition(18, 15), + field_entity.Turn(direction.UP), + ), - change_party(3), - field.EntityAct(field_entity.PARTY0, False, + # Move Guardian back 1 cell + field.EntityAct(0x10, False, + field_entity.SetPosition(11, 9) + ), + field.EntityAct(0x13, True, + field_entity.SetPosition(12, 9) + ), + field.EntityAct(0x16, True, + field_entity.SetPosition(13, 9) + ), + ], + # Camera position + field.EntityAct(field_entity.CAMERA, False, field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.SetPosition(18, 15), - field_entity.Move(direction.UP, 3), - field_entity.Move(direction.LEFT, 1), - field_entity.Pause(15), - field_entity.Move(direction.DOWN, 1), - field_entity.Turn(direction.LEFT), - field_entity.Pause(15), - field_entity.AnimateFrontRightHandUp(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - field_entity.AnimateSurprised(), - field_entity.AnimateLowJump(), - field_entity.Pause(10), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.RIGHT, 1), - field_entity.Move(direction.DOWN, 3), - field_entity.SetPosition(0, 0) + field_entity.Move(direction.UP, 2), ), + # Party 1 picking magitek flowers + [ + change_party(1), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.UP, 3), + field_entity.Move(direction.LEFT, 1), + field_entity.AnimateKneeling(), + field_entity.Pause(12), + field_entity.Move(direction.DOWN, 2), + field_entity.AnimateKneeling(), + field_entity.Pause(12), + + field_entity.Move(direction.UP, 1), + field_entity.Move(direction.RIGHT, 2), + field_entity.Pause(8), + field_entity.AnimateStandingFront(), + field_entity.Pause(1), + field_entity.AnimateFrontRightHandOnHead(), + field_entity.Pause(4), + field_entity.AnimateFrontRightHandUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.Pause(5), + field_entity.AnimateSurprised(), + field_entity.AnimateLowJump(), + field_entity.Pause(10), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.LEFT, 1), + field_entity.Move(direction.DOWN, 3), + field_entity.SetPosition(0, 0) + ), + ], + # Party 3 walking around + [ + change_party(3), + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.SetPosition(18, 14), + field_entity.Move(direction.UP, 2), + field_entity.Move(direction.LEFT, 1), + field_entity.Pause(8), + field_entity.Move(direction.DOWN, 1), + field_entity.Turn(direction.LEFT), + field_entity.Pause(10), + field_entity.AnimateFrontRightHandOnHead(), + field_entity.Pause(3), + field_entity.AnimateFrontRightHandUp(), + field_entity.Pause(3), + field_entity.AnimateFrontRightHandOnHead(), + field_entity.Pause(3), + field_entity.AnimateFrontRightHandUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.Move(direction.RIGHT, 1), + field_entity.Move(direction.UP, 1), + field_entity.AnimateSurprised(), + field_entity.AnimateLowJump(), + field_entity.Pause(5), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.DOWN, 4), + field_entity.SetPosition(0, 0) + ), + ], field.FadeInScreen(3), + # Party 2, the initiator + [ + change_party(2), - change_party(2), - field.EntityAct(field_entity.PARTY0, False, - field_entity.SetPosition(12, 17) - ), - - - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.Move(direction.DOWN, 6), - ), - field.Pause(2), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.UP, 3), - field_entity.AnimateFrontHandsUp(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - field_entity.Pause(5), - field_entity.AnimateFrontHandsUp(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - ), - field.Pause(1), - field.EntityAct(field_entity.PARTY0, False, - field_entity.AnimateAttack(), - field_entity.AnimateLowJump(), - field_entity.Pause(5), - field_entity.AnimateKnockedOut(), - field_entity.Pause(10), - field_entity.AnimateFrontHandsUp(), - field_entity.AnimateLowJump(), - field_entity.Pause(3), - field_entity.AnimateKneeling(), - field_entity.Pause(5), - field_entity.AnimateKneeling(), - field_entity.Pause(5), - field_entity.AnimateStandingHeadDown(), - field_entity.Pause(5), - field_entity.AnimateStandingFront(), - field_entity.Pause(5), - field_entity.AnimateAttack(), - field_entity.AnimateLowJump(), - ), + field.Pause(2), + field.EntityAct(field_entity.PARTY0, True, + field_entity.Move(direction.UP, 3), + field_entity.AnimateFrontHandsUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.Pause(5), + field_entity.AnimateFrontHandsUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + ), + field.EntityAct(field_entity.PARTY0, False, + field_entity.Pause(5), + field_entity.AnimateFrontHandsUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + field_entity.Pause(5), + field_entity.AnimateFrontHandsUp(), + field_entity.Pause(5), + field_entity.AnimateStandingFront(), + ), + field.Pause(2), + # Reaction to alarm + field.EntityAct(field_entity.PARTY0, False, + field_entity.AnimateSurprised(), + field_entity.AnimateLowJump(), + field_entity.Pause(5), + field_entity.AnimateKnockedOut(), + field_entity.Pause(13), + field_entity.AnimateKneeling(), + field_entity.Pause(10), + field_entity.AnimateStandingHeadDown(), + field_entity.Pause(4), + field_entity.AnimateStandingFront(), + field_entity.Pause(4), + field_entity.AnimatePowerStance(), + field_entity.AnimateLowJump(), + field_entity.Pause(6), + field_entity.Move(direction.UP, 1), + ), + ], field.Call(0xc1827), ] From 04d6d85a9cea1db40a4e072c08162596a6935f41 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:01:57 -0400 Subject: [PATCH 27/67] Add some missing animations --- instruction/field/entity.py | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/instruction/field/entity.py b/instruction/field/entity.py index f8888923..eae10d60 100644 --- a/instruction/field/entity.py +++ b/instruction/field/entity.py @@ -72,18 +72,77 @@ class AnimateAttacked(_Instruction): def __init__(self): super().__init__(0x0b) +class AnimateBattleStanding(_Instruction): + def __init__(self): + super().__init__(0x0c) + +class AnimateAirborneHandAtSide(_Instruction): + def __init__(self): + super().__init__(0x0d) +class AnimateAirborneHandInAir(_Instruction): + def __init__(self): + super().__init__(0x0e) + class AnimateHandsUp(_Instruction): def __init__(self): super().__init__(0x0f) +class AnimateCastingMouthClosed(_Instruction): + def __init__(self): + super().__init__(0x10) + +class AnimateCastingMouthOpen(_Instruction): + def __init__(self): + super().__init__(0x11) + +class A12(_Instruction): + def __init__(self): + super().__init__(0x12) + +class A13(_Instruction): + def __init__(self): + super().__init__(0x13) + +class A14(_Instruction): + def __init__(self): + super().__init__(0x14) + +class A15(_Instruction): + def __init__(self): + super().__init__(0x15) class AnimateFrontHandsUp(_Instruction): def __init__(self): super().__init__(0x16) +class AnimateFacingUpHandsUp(_Instruction): + def __init__(self): + super().__init__(0x17) + +class AnimatePowerStance(_Instruction): + def __init__(self): + super().__init__(0x18) + + class AnimateFrontRightHandUp(_Instruction): def __init__(self): super().__init__(0x19) +class AnimateFrontRightHandOnHead(_Instruction): + def __init__(self): + super().__init__(0x1a) + +class AnimateBackRightHandOnHead(_Instruction): + def __init__(self): + super().__init__(0x1b) + +class AnimateBackRightHandUp(_Instruction): + def __init__(self): + super().__init__(0x1c) + +class AnimateHappy(_Instruction): + def __init__(self): + super().__init__(0x1d) + class AnimateSurprised(_Instruction): def __init__(self): super().__init__(0x1f) @@ -92,6 +151,34 @@ class AnimateStandingHeadDown(_Instruction): def __init__(self): super().__init__(0x20) +class AnimateFacingUpHeadDown(_Instruction): + def __init__(self): + super().__init__(0x21) + +class AnimateFacingLeftHeadDown(_Instruction): + def __init__(self): + super().__init__(0x22) + +class AnimateStandingLookingToSide(_Instruction): + def __init__(self): + super().__init__(0x23) + +class AnimateFingerWagToSide(_Instruction): + def __init__(self): + super().__init__(0x24) + +class AnimateFingerWagStraightUp(_Instruction): + def __init__(self): + super().__init__(0x25) + +class AnimateEmbracingSelf(_Instruction): + def __init__(self): + super().__init__(0x26) + +class AnimateTent(_Instruction): + def __init__(self): + super().__init__(0x27) + class AnimateKnockedOut(_Instruction): def __init__(self): super().__init__(0x28) From 42fceafa20ce025167f01b2801826ffb03a7c9c3 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:02:38 -0400 Subject: [PATCH 28/67] Add sonts constant, fix polty camera --- constants/songs.py | 5 +++++ event/kefka_tower.py | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 constants/songs.py diff --git a/constants/songs.py b/constants/songs.py new file mode 100644 index 00000000..4c147d9f --- /dev/null +++ b/constants/songs.py @@ -0,0 +1,5 @@ +id_name = { + 0x33 : "FierceBattle", # "Battle to the Death" - Atma/Statue battle theme +} + +name_id = {v: k for k, v in id_name.items()} diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 8a078042..0850135e 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -887,9 +887,8 @@ def gauntlet_poltrgeist_cutscene_src(self): field.EntityAct(field_entity.CAMERA, False, field_entity.SetSpeed(field_entity.Speed.NORMAL), field_entity.Move(direction.LEFT, 6), - field_entity.Pause(20), + field_entity.Pause(15), field_entity.Move(direction.UP, 6), - field_entity.SetSpeed(field_entity.Speed.FAST), field_entity.Move(direction.UP, 3), ), field.EntityAct(field_entity.PARTY0, False, From a037154e3c231b4eaef2dc2a7b9dbd0039fb30b9 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:17:17 -0400 Subject: [PATCH 29/67] polty cutscene cleanup --- event/kefka_tower.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 0850135e..b50cbf57 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -878,19 +878,23 @@ def gauntlet_poltrgeist_cutscene_src(self): return [ change_party(2), field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, - x = 35, y = 25, fade_in = False, entrance_event = True), - field.FadeInScreen(3), + x = 35, y = 22, fade_in = False, entrance_event = True), + field.EntityAct(field_entity.CAMERA, False, + field_entity.SetPosition(40, 15), + field_entity.SetSpeed(field_entity.Speed.SLOW), + field_entity.Move(direction.LEFT, 2), + field_entity.MoveDiagonal(direction.LEFT, 1, direction.UP, 1), + field_entity.MoveDiagonal(direction.LEFT, 1, direction.UP, 1), + field_entity.MoveDiagonal(direction.LEFT, 1, direction.UP, 1), + field_entity.MoveDiagonal(direction.LEFT, 1, direction.UP, 1), + field_entity.Move(direction.UP, 2), + ), field.EntityAct(field_entity.PARTY0, True, field_entity.SetPosition(27, 23) ), - field.EntityAct(field_entity.CAMERA, False, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.LEFT, 6), - field_entity.Pause(15), - field_entity.Move(direction.UP, 6), - field_entity.Move(direction.UP, 3), - ), + field.FadeInScreen(3), + field.EntityAct(field_entity.PARTY0, False, field_entity.SetSpeed(field_entity.Speed.NORMAL), field_entity.Move(direction.DOWN, 2), From 3e04bdd4cc796facf0313b7709f85d782e56d209 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 1 Jun 2022 15:29:23 -0400 Subject: [PATCH 30/67] add pause during guardian (lazy) --- event/kefka_tower.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index b50cbf57..bf609751 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -666,7 +666,7 @@ def gauntlet_guardian_cutscene_src(self): field_entity.Turn(direction.UP), ), change_party(2), - field.EntityAct(field_entity.PARTY0, False, + field.EntityAct(field_entity.PARTY0, True, field_entity.SetSpeed(field_entity.Speed.FAST), field_entity.SetPosition(12, 17), field_entity.Turn(direction.UP), @@ -690,6 +690,7 @@ def gauntlet_guardian_cutscene_src(self): field_entity.SetPosition(13, 9) ), ], + field.Pause(0.25), # Camera position field.EntityAct(field_entity.CAMERA, False, field_entity.SetSpeed(field_entity.Speed.SLOW), From e5113da484404f9690e23c091bf557d93859cda9 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Thu, 2 Jun 2022 14:04:22 -0400 Subject: [PATCH 31/67] Move gauntlet code to F0 --- constants/objectives/condition_bits.py | 2 +- data/event_bit.py | 2 +- event/kefka_tower.py | 77 +++++++++++++++++--------- 3 files changed, 54 insertions(+), 27 deletions(-) diff --git a/constants/objectives/condition_bits.py b/constants/objectives/condition_bits.py index 8e45eebe..e676ee62 100644 --- a/constants/objectives/condition_bits.py +++ b/constants/objectives/condition_bits.py @@ -72,7 +72,7 @@ NameBit("Kefka's Tower Ambush", event_bit.DEFEATED_INFERNO), # 59 NameBit("Kefka's Tower Guardian", event_bit.DEFEATED_GUARDIAN), # 60 NameBit("KT Left Triad Statue", event_bit.DEFEATED_DOOM), # 61 - NameBit("KT Mid Triad Statue", event_bit.DEFEATED_POLTERGEIST), # 62 + NameBit("KT Mid Triad Statue", event_bit.DEFEATED_POLTRGEIST), # 62 NameBit("KT Right Triad Statue", event_bit.DEFEATED_GODDESS), # 63 ] diff --git a/data/event_bit.py b/data/event_bit.py index 3fab5490..9136a26d 100644 --- a/data/event_bit.py +++ b/data/event_bit.py @@ -187,7 +187,7 @@ DEFEATED_INFERNO = 0x0bd DEFEATED_DOOM = 0x072 DEFEATED_GODDESS = 0x073 -DEFEATED_POLTERGEIST = 0x074 +DEFEATED_POLTRGEIST = 0x074 LEFT_WEIGHT_PUSHED_KEFKA_TOWER = 0x063 RIGHT_WEIGHT_PUSHED_KEFKA_TOWER = 0x064 diff --git a/event/kefka_tower.py b/event/kefka_tower.py index bf609751..1b987641 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -170,8 +170,6 @@ def invoke_kt_battle(self, party, original_pack_name, battle_sound = False): # Trigger five bosses back-to-back def boss_rush_mod(self): - self.maps.disable_warp(final_switch_map_id) - def get_replacement_formation(boss_name): from data.bosses import pack_name replacement = self.get_boss(boss_name, False) @@ -179,13 +177,14 @@ def get_replacement_formation(boss_name): formation_id = self.enemies.formations.get_id(location_boss) return self.enemies.formations.formations[formation_id] + # If encounters are random, it could be a tell when a fight loses its music/victory dance def disable_victory_dance(original_encounter_name): formation = get_replacement_formation(original_encounter_name) - formation.disable_victory_dance = 1 + formation.disable_victory_dance = formation.disable_victory_dance if self.args.random_encounters_random else 1 def disable_battle_music(original_encounter_name): formation = get_replacement_formation(original_encounter_name) - formation.disable_battle_music = 1 + formation.disable_battle_music = formation.disable_battle_music if self.args.random_encounters_random else 1 def disable_all(boss_name): disable_victory_dance(boss_name) @@ -197,7 +196,6 @@ def disable_all(boss_name): disable_all("Goddess") disable_all("Poltrgeist") - src = [ Read(0xa02d6, 0xa030a), field.ClearEventBit(event_bit.UNLOCKED_KT_SKIP), @@ -218,14 +216,24 @@ def disable_all(boss_name): field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), - self.gauntlet_inferno_cutscene_src(), - self.gauntlet_guardian_cutscene_src(), - self.gauntlet_doom_cutscene_src(), - self.gauntlet_goddess_cutscene_src(), - self.gauntlet_poltrgeist_cutscene_src(), - self.gauntlet_post_battle_cutscene_src(), + field.BranchIfEventBitSet(event_bit.DEFEATED_INFERNO, "GUARDIAN"), + field.Call(self.gauntlet_inferno_cutscene()), + "GUARDIAN", + field.BranchIfEventBitSet(event_bit.DEFEATED_GUARDIAN, "DOOM"), + field.Call(self.gauntlet_guardian_cutscene()), + "DOOM", + field.BranchIfEventBitSet(event_bit.DEFEATED_DOOM, "GODDESS"), + field.Call(self.gauntlet_doom_cutscene()), + "GODDESS", + field.BranchIfEventBitSet(event_bit.DEFEATED_GODDESS, "POLTRGEIST"), + field.Call(self.gauntlet_goddess_cutscene()), + "POLTRGEIST", + field.BranchIfEventBitSet(event_bit.DEFEATED_POLTRGEIST, "POST_GAUNTLET"), + field.Call(self.gauntlet_poltrgeist_cutscene_src()), + "POST_GAUNTLET", + field.Call(self.gauntlet_post_battle_cutscene_src()), ] - space = Write(Bank.CA, src, "kefka tower statue landing") + space = Write(Bank.F0, src, "kefka tower statue landing") self.statue_landing = space.start_address space = Reserve(0xa03ad, 0xa03af, "kefka tower the statues are up ahead", field.NOP()) @@ -401,7 +409,7 @@ def goddess_mod(self): def poltergeist_mod(self): self.kt_encounter_objective_mod( "Goddess", - event_bit.DEFEATED_POLTERGEIST, + event_bit.DEFEATED_POLTRGEIST, 0xc1786, 0xc1789, "Poltergeist battle post-script, Hide NPCs, set npc bit", @@ -628,8 +636,8 @@ def exit_mod(self): # warp stone call trace: c0c670 -> ca0039 -> ca0108 -> ca014f -> cc0ff6 -> cc0f7d space = Reserve(0xc0fbf, 0xc0fc0, "kefka tower exit clear poltrgeist statue bit", asm.NOP()) - def gauntlet_inferno_cutscene_src(self): - return [ + def gauntlet_inferno_cutscene(self): + src = [ change_party(3), field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, x = 27, y = 18, fade_in = False, entrance_event = True), @@ -653,8 +661,12 @@ def gauntlet_inferno_cutscene_src(self): field.Call(0xc1872), # Inferno event tile address ] - def gauntlet_guardian_cutscene_src(self): - return [ + space = Write(Bank.F0, src, "Inferno Gauntlet Cutscene") + return space.start_address + + + def gauntlet_guardian_cutscene(self): + src = [ field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, x = 12, y = 14, fade_in = False, entrance_event = True), # Initialize party positions @@ -810,9 +822,12 @@ def gauntlet_guardian_cutscene_src(self): ], field.Call(0xc1827), ] + space = Write(Bank.F0, src, "Guardian Gauntlet Cutscene") + return space.start_address - def gauntlet_doom_cutscene_src(self): - return [ + + def gauntlet_doom_cutscene(self): + src = [ change_party(1), field.LoadMap(doom_room_id, direction.DOWN, default_music = False, x = 64, y = 15, fade_in = False, entrance_event = True), @@ -841,10 +856,14 @@ def gauntlet_doom_cutscene_src(self): ), field.FadeOutScreen(3), field.Pause(1), + field.Return(), ] - def gauntlet_goddess_cutscene_src(self): - return [ + space = Write(Bank.F0, src, "Doom Gauntlet Cutscene") + return space.start_address + + def gauntlet_goddess_cutscene(self): + src = [ change_party(3), field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, x = 12, y = 28, fade_in = False, entrance_event = True), @@ -874,9 +893,11 @@ def gauntlet_goddess_cutscene_src(self): field.Pause(1), ] - def gauntlet_poltrgeist_cutscene_src(self): + space = Write(Bank.F0, src, "Goddess Gauntlet Cutscene") + return space.start_address - return [ + def gauntlet_poltrgeist_cutscene_src(self): + src = [ change_party(2), field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, x = 35, y = 22, fade_in = False, entrance_event = True), @@ -928,6 +949,9 @@ def gauntlet_poltrgeist_cutscene_src(self): field.Pause(1), ] + space = Write(Bank.F0, src, "Poltrgeist Gauntlet Cutscene") + return space.start_address + def gauntlet_post_battle_cutscene_src(self): party1_x = 103 party1_y_dest = 45 @@ -943,7 +967,7 @@ def gauntlet_post_battle_cutscene_src(self): party3_y_dest = 44 party3_y_offset = 8 party3_y_start = party3_y_dest - party3_y_offset - return [ + src = [ [ # Load final field.LoadMap(final_switch_map_id, direction.DOWN, default_music = False, @@ -1043,4 +1067,7 @@ def gauntlet_post_battle_cutscene_src(self): field.Pause(0.75), self.post_landing_src(final_switch_map_id, party1_x, party1_y_dest), - ] \ No newline at end of file + ] + + space = Write(Bank.F0, src, "Post-Gauntlet Cutscene") + return space.start_address From bcaaadc79f90c03e61753e25d826fdc2ed8f3808 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Thu, 2 Jun 2022 14:16:30 -0400 Subject: [PATCH 32/67] remove animations a12-a15 --- instruction/field/entity.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/instruction/field/entity.py b/instruction/field/entity.py index eae10d60..10618182 100644 --- a/instruction/field/entity.py +++ b/instruction/field/entity.py @@ -95,21 +95,6 @@ class AnimateCastingMouthOpen(_Instruction): def __init__(self): super().__init__(0x11) -class A12(_Instruction): - def __init__(self): - super().__init__(0x12) - -class A13(_Instruction): - def __init__(self): - super().__init__(0x13) - -class A14(_Instruction): - def __init__(self): - super().__init__(0x14) - -class A15(_Instruction): - def __init__(self): - super().__init__(0x15) class AnimateFrontHandsUp(_Instruction): def __init__(self): super().__init__(0x16) From 4e9871bf718d3e10c0c4d2299e37c0133069ebf5 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 3 Jun 2022 09:24:06 -0400 Subject: [PATCH 33/67] Move gauntlet to F0, big cleanup. --- constants/objectives/condition_bits.py | 2 +- data/event_bit.py | 2 +- data/npc_bit.py | 4 +- event/kefka_tower.py | 72 +++++++++++++++++--------- 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/constants/objectives/condition_bits.py b/constants/objectives/condition_bits.py index e676ee62..57c7c2f1 100644 --- a/constants/objectives/condition_bits.py +++ b/constants/objectives/condition_bits.py @@ -72,7 +72,7 @@ NameBit("Kefka's Tower Ambush", event_bit.DEFEATED_INFERNO), # 59 NameBit("Kefka's Tower Guardian", event_bit.DEFEATED_GUARDIAN), # 60 NameBit("KT Left Triad Statue", event_bit.DEFEATED_DOOM), # 61 - NameBit("KT Mid Triad Statue", event_bit.DEFEATED_POLTRGEIST), # 62 + NameBit("KT Mid Triad Statue", event_bit.DEFEATED_POLTERGEIST), # 62 NameBit("KT Right Triad Statue", event_bit.DEFEATED_GODDESS), # 63 ] diff --git a/data/event_bit.py b/data/event_bit.py index 9136a26d..3fab5490 100644 --- a/data/event_bit.py +++ b/data/event_bit.py @@ -187,7 +187,7 @@ DEFEATED_INFERNO = 0x0bd DEFEATED_DOOM = 0x072 DEFEATED_GODDESS = 0x073 -DEFEATED_POLTRGEIST = 0x074 +DEFEATED_POLTERGEIST = 0x074 LEFT_WEIGHT_PUSHED_KEFKA_TOWER = 0x063 RIGHT_WEIGHT_PUSHED_KEFKA_TOWER = 0x064 diff --git a/data/npc_bit.py b/data/npc_bit.py index da80622f..46f86570 100644 --- a/data/npc_bit.py +++ b/data/npc_bit.py @@ -1,5 +1,5 @@ # NOTE: (address - 1e80) * 0x8 + bit -# e.g. (1eb7 - 1e80) * 0x8 + 0x1 = 1b9 (airship visible) +# e.g. (1eb7 - 1e80) * 0x8 + 0x1 = 1b9 (airship visible) # (1f43 - 1e80) * 0x8 + 0x3 = 61b (characters on narshe battlefield) SOLDIER_DOORWAY_ARVIS_HOUSE = 0x688 @@ -145,7 +145,7 @@ DOOM_STATUE_KEFKA_TOWER = 0x6b0 GODDESS_STATUE_KEFKA_TOWER = 0x6b1 -POLTRGEIST_STATUE_KEFKA_TOWER = 0x6b2 +POLTERGEIST_STATUE_KEFKA_TOWER = 0x6b2 def byte(npc_bit): return npc_bit // 8 diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 1b987641..e2d4c835 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -29,7 +29,7 @@ def init_rewards(self): def init_event_bits(self, space): space.write( - field.SetEventBit(npc_bit.POLTRGEIST_STATUE_KEFKA_TOWER), + field.SetEventBit(npc_bit.POLTERGEIST_STATUE_KEFKA_TOWER), ) import objectives @@ -41,7 +41,7 @@ def init_event_bits(self, space): def mod(self): self.boss_rush_mod() - # self.statue_landing_mod() + self.statue_landing_mod() self.entrance_landing_mod() self.kefka_scene_mod() @@ -62,7 +62,7 @@ def mod(self): self.guardian_battle_mod() self.doom_battle_mod() self.goddess_battle_mod() - self.poltrgeist_battle_mod() + self.poltergeist_battle_mod() self.kefka_battle_mod() self.final_kefka_access_mod() @@ -157,7 +157,7 @@ def statue_landing_mod(self): self.post_landing_src(0x163, 35, 6), ] - space = Write(Bank.CA, src, "kefka tower statue landing") + space = Write(Bank.F0, src, "kefka tower statue landing") self.statue_landing = space.start_address space = Reserve(0xa03ad, 0xa03af, "kefka tower the statues are up ahead", field.NOP()) @@ -196,8 +196,21 @@ def disable_all(boss_name): disable_all("Goddess") disable_all("Poltrgeist") + # Uncomment these to debug/view specific cutscenes + # Must run with `-debug` flag to utilize this + debug_event_bits = [ + # field.SetEventBit(event_bit.DEFEATED_INFERNO), + # field.SetEventBit(event_bit.DEFEATED_GUARDIAN), + # field.SetEventBit(event_bit.DEFEATED_DOOM), + # field.SetEventBit(event_bit.DEFEATED_GODDESS), + # field.SetEventBit(event_bit.DEFEATED_POLTERGEIST), + ] if self.args.debug else [] + src = [ Read(0xa02d6, 0xa030a), + + debug_event_bits, + field.ClearEventBit(event_bit.UNLOCKED_KT_SKIP), field.SetEventBit(event_bit.LEFT_WEIGHT_PUSHED_KEFKA_TOWER), @@ -216,6 +229,7 @@ def disable_all(boss_name): field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), + field.BranchIfEventBitSet(event_bit.DEFEATED_INFERNO, "GUARDIAN"), field.Call(self.gauntlet_inferno_cutscene()), "GUARDIAN", @@ -225,18 +239,17 @@ def disable_all(boss_name): field.BranchIfEventBitSet(event_bit.DEFEATED_DOOM, "GODDESS"), field.Call(self.gauntlet_doom_cutscene()), "GODDESS", - field.BranchIfEventBitSet(event_bit.DEFEATED_GODDESS, "POLTRGEIST"), + field.BranchIfEventBitSet(event_bit.DEFEATED_GODDESS, "POLTERGEIST"), field.Call(self.gauntlet_goddess_cutscene()), - "POLTRGEIST", - field.BranchIfEventBitSet(event_bit.DEFEATED_POLTRGEIST, "POST_GAUNTLET"), - field.Call(self.gauntlet_poltrgeist_cutscene_src()), + "POLTERGEIST", + field.BranchIfEventBitSet(event_bit.DEFEATED_POLTERGEIST, "POST_GAUNTLET"), + field.Call(self.gauntlet_poltergeist_cutscene()), "POST_GAUNTLET", - field.Call(self.gauntlet_post_battle_cutscene_src()), + field.Call(self.gauntlet_post_battle_cutscene()), + self.post_landing_src(final_switch_map_id, 103, 45), ] space = Write(Bank.F0, src, "kefka tower statue landing") - self.statue_landing = space.start_address - - space = Reserve(0xa03ad, 0xa03af, "kefka tower the statues are up ahead", field.NOP()) + self.gauntlet_event = space.start_address self.maps.delete_short_exit(final_switch_map_id, 103, 49) self.maps.delete_short_exit(final_switch_map_id, 109, 46) @@ -248,9 +261,10 @@ def entrance_landing_mod(self): statues_entrance = 1287 self.dialogs.set_text(statues_entrance, - " (Gauntlet) (Not just yet)") + " (Gauntlet) (Statues) (Entrance) (Not just yet)") space = Reserve(0xa01a2, 0xa02d5, "kefka tower first landing scene", field.NOP()) + space.add_label("GAUNTLET", self.gauntlet_event) space.add_label("STATUE_LANDING", self.statue_landing) space.add_label("ENTRANCE_LANDING", space.end_address + 1) space.write( @@ -274,7 +288,7 @@ def entrance_landing_mod(self): "LANDING_MENU", field.DialogBranch(statues_entrance, - dest1 = "STATUE_LANDING", dest2 = "ENTRANCE_LANDING", dest3 = "CANCEL_LANDING"), + dest1 = "GAUNTLET", dest2 = "STATUE_LANDING", dest3 = "ENTRANCE_LANDING", dest4 = "CANCEL_LANDING"), ) def kefka_scene_mod(self): @@ -357,6 +371,7 @@ def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, d src += [ field.SetEventBit(bit), field.CheckObjectives(), + field.FreeScreen(), field.Return(), ] post_battle = Write(Bank['CC'], src, f"{boss_name} post-battle. 1) Set event bit. 2) Finish check") @@ -409,7 +424,7 @@ def goddess_mod(self): def poltergeist_mod(self): self.kt_encounter_objective_mod( "Goddess", - event_bit.DEFEATED_POLTRGEIST, + event_bit.DEFEATED_POLTERGEIST, 0xc1786, 0xc1789, "Poltergeist battle post-script, Hide NPCs, set npc bit", @@ -524,10 +539,10 @@ def goddess_battle_mod(self): field.InvokeBattle(boss_pack_id), ) - def poltrgeist_battle_mod(self): + def poltergeist_battle_mod(self): boss_pack_id = self.get_boss("Poltrgeist") - space = Reserve(0xc1755, 0xc175b, "kefka tower invoke battle poltrgeist", field.NOP()) + space = Reserve(0xc1755, 0xc175b, "kefka tower invoke battle poltergeist", field.NOP()) space.write( field.InvokeBattle(boss_pack_id), ) @@ -641,6 +656,7 @@ def gauntlet_inferno_cutscene(self): change_party(3), field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, x = 27, y = 18, fade_in = False, entrance_event = True), + field.HoldScreen(), # Move main party to east of inferno field.EntityAct(field_entity.PARTY0, True, @@ -659,6 +675,7 @@ def gauntlet_inferno_cutscene(self): field_entity.Move(direction.LEFT, 8), ), field.Call(0xc1872), # Inferno event tile address + field.Return(), ] space = Write(Bank.F0, src, "Inferno Gauntlet Cutscene") @@ -669,6 +686,7 @@ def gauntlet_guardian_cutscene(self): src = [ field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, x = 12, y = 14, fade_in = False, entrance_event = True), + field.HoldScreen(), # Initialize party positions [ change_party(1), @@ -821,6 +839,7 @@ def gauntlet_guardian_cutscene(self): ), ], field.Call(0xc1827), + field.Return(), ] space = Write(Bank.F0, src, "Guardian Gauntlet Cutscene") return space.start_address @@ -831,6 +850,7 @@ def gauntlet_doom_cutscene(self): change_party(1), field.LoadMap(doom_room_id, direction.DOWN, default_music = False, x = 64, y = 15, fade_in = False, entrance_event = True), + field.HoldScreen(), field.EntityAct(field_entity.PARTY0, True, field_entity.SetPosition(64, 21), ), @@ -848,7 +868,6 @@ def gauntlet_doom_cutscene(self): field.Call(0xc16d6), - field.HoldScreen(), field.EntityAct(field_entity.PARTY0, False, field_entity.SetSpeed(field_entity.Speed.NORMAL), field_entity.Move(direction.LEFT, 1), @@ -867,6 +886,7 @@ def gauntlet_goddess_cutscene(self): change_party(3), field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, x = 12, y = 28, fade_in = False, entrance_event = True), + field.HoldScreen(), field.EntityAct(field_entity.PARTY0, True, field_entity.SetPosition(12, 40) ), @@ -891,14 +911,17 @@ def gauntlet_goddess_cutscene(self): ), field.FadeOutScreen(4), field.Pause(1), + field.Return(), ] space = Write(Bank.F0, src, "Goddess Gauntlet Cutscene") return space.start_address - def gauntlet_poltrgeist_cutscene_src(self): + def gauntlet_poltergeist_cutscene(self): + chest = self.maps.get_chest(poltergeist_room_id, 29, 29) src = [ change_party(2), + field.HoldScreen(), field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, x = 35, y = 22, fade_in = False, entrance_event = True), field.EntityAct(field_entity.CAMERA, False, @@ -917,7 +940,7 @@ def gauntlet_poltrgeist_cutscene_src(self): field.FadeInScreen(3), - field.EntityAct(field_entity.PARTY0, False, + field.EntityAct(field_entity.PARTY0, False, field_entity.SetSpeed(field_entity.Speed.NORMAL), field_entity.Move(direction.DOWN, 2), field_entity.Move(direction.RIGHT, 2), @@ -940,19 +963,19 @@ def gauntlet_poltrgeist_cutscene_src(self): field.Call(0xc174f), - field.HoldScreen(), field.EntityAct(field_entity.PARTY0, False, field_entity.SetSpeed(field_entity.Speed.FAST), field_entity.Move(direction.UP, 8), ), field.FadeOutScreen(4), field.Pause(1), + field.Return(), ] - space = Write(Bank.F0, src, "Poltrgeist Gauntlet Cutscene") + space = Write(Bank.F0, src, "Poltergeist Gauntlet Cutscene") return space.start_address - def gauntlet_post_battle_cutscene_src(self): + def gauntlet_post_battle_cutscene(self): party1_x = 103 party1_y_dest = 45 party1_y_offset = 10 @@ -972,6 +995,7 @@ def gauntlet_post_battle_cutscene_src(self): # Load final field.LoadMap(final_switch_map_id, direction.DOWN, default_music = False, x = 109, y = 43, fade_in = False, entrance_event = False), + field.HoldScreen(), field.FadeOutSong(255), field.ClearEventBit(event_bit.TEMP_SONG_OVERRIDE), @@ -1066,7 +1090,7 @@ def gauntlet_post_battle_cutscene_src(self): field.Pause(0.75), - self.post_landing_src(final_switch_map_id, party1_x, party1_y_dest), + field.Return(), ] space = Write(Bank.F0, src, "Post-Gauntlet Cutscene") From 4079b0469f14ebc87a80102f9ffa248128612a06 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 5 Jun 2022 08:25:19 -0400 Subject: [PATCH 34/67] final for now --- event/kefka_tower.py | 58 ++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index e2d4c835..7f012d49 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -1,3 +1,6 @@ +from asyncio import wait_for +from data.map_event import MapEvent +from data.npc import NPC from event.event import * import args @@ -196,6 +199,13 @@ def disable_all(boss_name): disable_all("Goddess") disable_all("Poltrgeist") + self.inferno_cutscene = self.gauntlet_inferno_cutscene() + self.guardian_cutscene = self.gauntlet_guardian_cutscene() + self.doom_cutscene = self.gauntlet_doom_cutscene() + self.goddess_cutscene = self.gauntlet_goddess_cutscene() + self.poltergeist_cutscene = self.gauntlet_poltergeist_cutscene() + self.post_gauntlet_cutscene = self.gauntlet_post_battle_cutscene() + # Uncomment these to debug/view specific cutscenes # Must run with `-debug` flag to utilize this debug_event_bits = [ @@ -206,12 +216,35 @@ def disable_all(boss_name): # field.SetEventBit(event_bit.DEFEATED_POLTERGEIST), ] if self.args.debug else [] + src = [ + field.BranchIfEventBitSet(event_bit.DEFEATED_INFERNO, "GUARDIAN"), + field.Call(self.inferno_cutscene), + "GUARDIAN", + field.BranchIfEventBitSet(event_bit.DEFEATED_GUARDIAN, "DOOM"), + field.Call(self.guardian_cutscene), + "DOOM", + field.BranchIfEventBitSet(event_bit.DEFEATED_DOOM, "GODDESS"), + "GODDESS", + field.Call(self.doom_cutscene), + field.BranchIfEventBitSet(event_bit.DEFEATED_GODDESS, "POLTERGEIST"), + field.Call(self.goddess_cutscene), + "POLTERGEIST", + field.BranchIfEventBitSet(event_bit.DEFEATED_POLTERGEIST, "POST_GAUNTLET"), + field.Call(self.poltergeist_cutscene), + "POST_GAUNTLET", + field.Call(self.post_gauntlet_cutscene), + self.post_landing_src(final_switch_map_id, 103, 45), + ] + + self.next_gauntlet_event = Write(Bank.F0, src, "Eval bits to determine next fight") + next_event_address = self.next_gauntlet_event.start_address + src = [ Read(0xa02d6, 0xa030a), debug_event_bits, - field.ClearEventBit(event_bit.UNLOCKED_KT_SKIP), + # field.ClearEventBit(event_bit.UNLOCKED_KT_SKIP), field.SetEventBit(event_bit.LEFT_WEIGHT_PUSHED_KEFKA_TOWER), field.SetEventBit(event_bit.RIGHT_WEIGHT_PUSHED_KEFKA_TOWER), @@ -229,28 +262,13 @@ def disable_all(boss_name): field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), - - field.BranchIfEventBitSet(event_bit.DEFEATED_INFERNO, "GUARDIAN"), - field.Call(self.gauntlet_inferno_cutscene()), - "GUARDIAN", - field.BranchIfEventBitSet(event_bit.DEFEATED_GUARDIAN, "DOOM"), - field.Call(self.gauntlet_guardian_cutscene()), - "DOOM", - field.BranchIfEventBitSet(event_bit.DEFEATED_DOOM, "GODDESS"), - field.Call(self.gauntlet_doom_cutscene()), - "GODDESS", - field.BranchIfEventBitSet(event_bit.DEFEATED_GODDESS, "POLTERGEIST"), - field.Call(self.gauntlet_goddess_cutscene()), - "POLTERGEIST", - field.BranchIfEventBitSet(event_bit.DEFEATED_POLTERGEIST, "POST_GAUNTLET"), - field.Call(self.gauntlet_poltergeist_cutscene()), - "POST_GAUNTLET", - field.Call(self.gauntlet_post_battle_cutscene()), - self.post_landing_src(final_switch_map_id, 103, 45), + field.Call(next_event_address) ] + space = Write(Bank.F0, src, "kefka tower statue landing") self.gauntlet_event = space.start_address + # Delete the three exits of the final room and add a save point self.maps.delete_short_exit(final_switch_map_id, 103, 49) self.maps.delete_short_exit(final_switch_map_id, 109, 46) self.maps.delete_short_exit(final_switch_map_id, 115, 48) @@ -918,7 +936,6 @@ def gauntlet_goddess_cutscene(self): return space.start_address def gauntlet_poltergeist_cutscene(self): - chest = self.maps.get_chest(poltergeist_room_id, 29, 29) src = [ change_party(2), field.HoldScreen(), @@ -971,7 +988,6 @@ def gauntlet_poltergeist_cutscene(self): field.Pause(1), field.Return(), ] - space = Write(Bank.F0, src, "Poltergeist Gauntlet Cutscene") return space.start_address From 7aa43e3d127616ad08dfae454ad801e63a11d2ba Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Tue, 18 Oct 2022 07:44:08 -0400 Subject: [PATCH 35/67] add "add_save_point" method to maps.py kefka_tower F0 fix, append instead of backwards call --- data/maps.py | 31 +++++++++++++++++++++++++ event/kefka_tower.py | 54 +++++++++++++++++++++----------------------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/data/maps.py b/data/maps.py index 913701e5..285430a8 100644 --- a/data/maps.py +++ b/data/maps.py @@ -238,3 +238,34 @@ def write(self): npcs_ptr[0] = cur_map["npcs_ptr"] & 0xff npcs_ptr[1] = (cur_map["npcs_ptr"] & 0xff00) >> 8 self.rom.set_bytes(npcs_ptr_address, npcs_ptr) + + def add_save_point(self, map_id, x, y): + # npc for visual + npc = NPC() + npc.background_layer = 0 + npc.background_scrolls = 0 + npc.const_sprite = 1 + npc.direction = 3 + npc.event_bit = 3 + npc.event_byte = 102 + npc.map_layer = 1 + npc.movement = 0 + npc.no_face_on_trigger = 0 + npc.palette = 6 + npc.speed = 2 + npc.split_sprite = 1 + npc.sprite = 111 + npc.unknown1 = 0 + npc.unknown2 = 0 + npc.vehicle = 0 + npc.x = x + npc.y = y + + # event for save functionality + event = MapEvent() + event.set_event_address(0xc9aeb) # save script + event.x = x + event.y = y + + self.append_npc(map_id, npc) + self.add_event(map_id, event) \ No newline at end of file diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 7f012d49..dfc8b083 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -173,6 +173,8 @@ def invoke_kt_battle(self, party, original_pack_name, battle_sound = False): # Trigger five bosses back-to-back def boss_rush_mod(self): + # return the boss in place of the given boss_name + # e.g. bosses are shuffled, if Ultros is in Goddess spot, return Ultros def get_replacement_formation(boss_name): from data.bosses import pack_name replacement = self.get_boss(boss_name, False) @@ -183,11 +185,11 @@ def get_replacement_formation(boss_name): # If encounters are random, it could be a tell when a fight loses its music/victory dance def disable_victory_dance(original_encounter_name): formation = get_replacement_formation(original_encounter_name) - formation.disable_victory_dance = formation.disable_victory_dance if self.args.random_encounters_random else 1 + formation.disable_victory_dance = formation.disable_victory_dance if self.args.boss_battles_random else 1 def disable_battle_music(original_encounter_name): formation = get_replacement_formation(original_encounter_name) - formation.disable_battle_music = formation.disable_battle_music if self.args.random_encounters_random else 1 + formation.disable_battle_music = formation.disable_battle_music if self.args.boss_battles_random else 1 def disable_all(boss_name): disable_victory_dance(boss_name) @@ -216,35 +218,12 @@ def disable_all(boss_name): # field.SetEventBit(event_bit.DEFEATED_POLTERGEIST), ] if self.args.debug else [] - src = [ - field.BranchIfEventBitSet(event_bit.DEFEATED_INFERNO, "GUARDIAN"), - field.Call(self.inferno_cutscene), - "GUARDIAN", - field.BranchIfEventBitSet(event_bit.DEFEATED_GUARDIAN, "DOOM"), - field.Call(self.guardian_cutscene), - "DOOM", - field.BranchIfEventBitSet(event_bit.DEFEATED_DOOM, "GODDESS"), - "GODDESS", - field.Call(self.doom_cutscene), - field.BranchIfEventBitSet(event_bit.DEFEATED_GODDESS, "POLTERGEIST"), - field.Call(self.goddess_cutscene), - "POLTERGEIST", - field.BranchIfEventBitSet(event_bit.DEFEATED_POLTERGEIST, "POST_GAUNTLET"), - field.Call(self.poltergeist_cutscene), - "POST_GAUNTLET", - field.Call(self.post_gauntlet_cutscene), - self.post_landing_src(final_switch_map_id, 103, 45), - ] - - self.next_gauntlet_event = Write(Bank.F0, src, "Eval bits to determine next fight") - next_event_address = self.next_gauntlet_event.start_address - src = [ Read(0xa02d6, 0xa030a), debug_event_bits, - # field.ClearEventBit(event_bit.UNLOCKED_KT_SKIP), + field.ClearEventBit(event_bit.UNLOCKED_KT_SKIP), field.SetEventBit(event_bit.LEFT_WEIGHT_PUSHED_KEFKA_TOWER), field.SetEventBit(event_bit.RIGHT_WEIGHT_PUSHED_KEFKA_TOWER), @@ -261,11 +240,30 @@ def disable_all(boss_name): field.SetEventBit(event_bit.CENTER_DOOR_KEFKA_TOWER), field.SetEventBit(event_bit.LEFT_RIGHT_DOORS_KEFKA_TOWER), field.SetEventBit(event_bit.TEMP_SONG_OVERRIDE), + ] - field.Call(next_event_address) + # Gauntlet execution code - Breaking this up for visibility + src += [ + field.BranchIfEventBitSet(event_bit.DEFEATED_INFERNO, "GUARDIAN"), + field.Call(self.inferno_cutscene), + "GUARDIAN", + field.BranchIfEventBitSet(event_bit.DEFEATED_GUARDIAN, "DOOM"), + field.Call(self.guardian_cutscene), + "DOOM", + field.BranchIfEventBitSet(event_bit.DEFEATED_DOOM, "GODDESS"), + field.Call(self.doom_cutscene), + "GODDESS", + field.BranchIfEventBitSet(event_bit.DEFEATED_GODDESS, "POLTERGEIST"), + field.Call(self.goddess_cutscene), + "POLTERGEIST", + field.BranchIfEventBitSet(event_bit.DEFEATED_POLTERGEIST, "POST_GAUNTLET"), + field.Call(self.poltergeist_cutscene), + "POST_GAUNTLET", + field.Call(self.post_gauntlet_cutscene), + self.post_landing_src(final_switch_map_id, 103, 45), ] - space = Write(Bank.F0, src, "kefka tower statue landing") + space = Write(Bank.F0, src, "kefka tower gauntlet") self.gauntlet_event = space.start_address # Delete the three exits of the final room and add a save point From b3243be273f560f12efbf802f02a91cec3c05938 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Tue, 18 Oct 2022 07:49:22 -0400 Subject: [PATCH 36/67] add save poitns to end level --- event/kefka_tower.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index dfc8b083..0904f1f4 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -271,6 +271,11 @@ def disable_all(boss_name): self.maps.delete_short_exit(final_switch_map_id, 109, 46) self.maps.delete_short_exit(final_switch_map_id, 115, 48) + self.maps.add_save_point(final_switch_map_id, 103, 45) # left lane + self.maps.add_save_point(final_switch_map_id, 109, 42) # mid lane + self.maps.add_save_point(final_switch_map_id, 115, 44) # right lane + + def entrance_landing_mod(self): need_more_allies = 2982 self.dialogs.set_text(need_more_allies, "We need to find more allies.") From ecef00ef8df0364c24b9dec95c213c096a6d8484 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 19 Oct 2022 22:20:28 -0400 Subject: [PATCH 37/67] add kt gauntlet result, dialog rework --- constants/objectives/results.py | 3 ++ data/dialogs/dialogs.py | 6 ++++ data/dialogs/free.py | 6 ++++ data/event_bit.py | 9 ++++++ event/kefka_tower.py | 35 +++++++++++++++++------- objectives/results/unlock_kt_gauntlet.py | 19 +++++++++++++ 6 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 objectives/results/unlock_kt_gauntlet.py diff --git a/constants/objectives/results.py b/constants/objectives/results.py index 2606d9b2..9d529ffe 100644 --- a/constants/objectives/results.py +++ b/constants/objectives/results.py @@ -85,6 +85,9 @@ ], } +category_types["Kefka's Tower"] += [ResultType(90, "Unlock KT Gauntlet", None)] + + categories = list(category_types.keys()) id_type = {} diff --git a/data/dialogs/dialogs.py b/data/dialogs/dialogs.py index 79803299..4d5b3a3a 100644 --- a/data/dialogs/dialogs.py +++ b/data/dialogs/dialogs.py @@ -179,6 +179,12 @@ def objectives_mod(self): mlid = self.allocate_multi_line_battle(line1 + "" + line2 + "") self.multi_line_battle_objectives.append(mlid) + def create_dialog(self, text): + from dialogs.free import dialogs + id = dialogs.pop() + self.set_text(id, text) + return id + def mod(self): self.move_battle_messages() self.objectives_mod() diff --git a/data/dialogs/free.py b/data/dialogs/free.py index 758c73bb..e25bf3d3 100644 --- a/data/dialogs/free.py +++ b/data/dialogs/free.py @@ -1,3 +1,9 @@ +dialogs = ( + range(515, 528) + # kefka assaulting thamasa + range(550, 598) + # sabin scenario, cyan kefka doma event + range(1767, 1878) # BANQUET +) + multi_line_battle_dialogs = [ 0, # WEDGE:Hey! What's the matter?Do you know something wedon't……? ... 1, # GIRL:…… diff --git a/data/event_bit.py b/data/event_bit.py index 3fab5490..fab63d2d 100644 --- a/data/event_bit.py +++ b/data/event_bit.py @@ -209,6 +209,15 @@ ENABLE_Y_PARTY_SWITCHING = 0x1ce ALWAYS_CLEAR = 0x176 # this event_bit is always clear, used for branching +# Unused Bits +# bits 0x200-0x22e Used for banquet soldiers +# 2 bits 0x1bc-0x1bd Unused +# 3 bits 0x1c7-0x1c9 Unused +# 3 bits 0x2c1-0x2c3 Unused + +UNLOCKED_KT_GAUNTLET = 0x2c2 +# 8 bits 0x1e6-0x1ed Unused, as the SNES versions feature 20 rare item slots rather than 30 + from constants.objectives import MAX_OBJECTIVES for index in range(MAX_OBJECTIVES): globals()["OBJECTIVE" + str(index)] = 0xe0 + index diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 0904f1f4..c83ff6cf 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -7,7 +7,9 @@ from constants.maps import name_id as map_name_id from constants.songs import name_id as song_name_id from constants.sound_effects import name_id as sfx_name_id - +import data.event_bit as event_bits +from instruction.field.instructions import BranchIfEventBitSet +from instruction.vehicle import Branch final_switch_map_id = map_name_id["KT Final Switch Room"] inferno_room_id = map_name_id["Inferno Room"] guardian_room_id = map_name_id["Guardian Room"] @@ -280,9 +282,9 @@ def entrance_landing_mod(self): need_more_allies = 2982 self.dialogs.set_text(need_more_allies, "We need to find more allies.") - statues_entrance = 1287 - self.dialogs.set_text(statues_entrance, - " (Gauntlet) (Statues) (Entrance) (Not just yet)") + statues_entrance = self.dialogs.create_dialog(" (Statues) (Not just yet)") + gauntlet_entrance = self.dialogs.create_dialog(" (Gauntlet) (Not just yet)") + both_entrance = self.dialogs.create_dialog(" (Gauntlet) (Statues) (Not just yet)") space = Reserve(0xa01a2, 0xa02d5, "kefka tower first landing scene", field.NOP()) space.add_label("GAUNTLET", self.gauntlet_event) @@ -290,7 +292,8 @@ def entrance_landing_mod(self): space.add_label("ENTRANCE_LANDING", space.end_address + 1) space.write( field.BranchIfEventWordLess(event_word.CHARACTERS_AVAILABLE, 3, "NEED_MORE_ALLIES"), - field.BranchIfEventBitSet(event_bit.UNLOCKED_KT_SKIP, "LANDING_MENU"), + field.BranchIfEventBitSet(event_bit.UNLOCKED_KT_SKIP, "STATUE_MENU_EVAL"), + field.BranchIfEventBitSet(event_bit.UNLOCKED_KT_GAUNTLET, "GAUNTLET_DIALOG"), field.Pause(2), # NOTE: load-bearing pause, without a pause or dialog before party select the game # enters an infinite loop. it seems like the game needs time to finish @@ -307,9 +310,19 @@ def entrance_landing_mod(self): vehicle.End(), field.Return(), - "LANDING_MENU", + "STATUE_MENU_EVAL", + field.BranchIfEventBitSet(event_bit.event_bits.UNLOCKED_KT_GAUNTLET, "GAUNTLET_STATUES_DIALOG"), + field.Branch("STATUES_DIALOG"), + + "GAUNTLET_DIALOG", + field.DialogBranch(gauntlet_entrance, + dest1 = "GAUNTLET", dest2 = "CANCEL_LANDING"), + "STATUES_DIALOG", field.DialogBranch(statues_entrance, - dest1 = "GAUNTLET", dest2 = "STATUE_LANDING", dest3 = "ENTRANCE_LANDING", dest4 = "CANCEL_LANDING"), + dest1 = "STATUES", dest2 = "CANCEL_LANDING"), + "GAUNTLET_STATUES_DIALOG", + field.DialogBranch(both_entrance, + dest1 = "GAUNTLET", dest2 = "STATUES", dest3 = "CANCEL_LANDING"), ) def kefka_scene_mod(self): @@ -711,7 +724,7 @@ def gauntlet_guardian_cutscene(self): # Initialize party positions [ change_party(1), - field.EntityAct(field_entity.PARTY0, False, + field.EntityAct(field_entity.PARTY0, True, field_entity.SetSpeed(field_entity.Speed.NORMAL), field_entity.SetPosition(6, 15), field_entity.Turn(direction.UP), @@ -724,7 +737,7 @@ def gauntlet_guardian_cutscene(self): ), change_party(3), - field.EntityAct(field_entity.PARTY0, False, + field.EntityAct(field_entity.PARTY0, True, field_entity.SetSpeed(field_entity.Speed.SLOW), field_entity.SetPosition(18, 15), field_entity.Turn(direction.UP), @@ -819,6 +832,7 @@ def gauntlet_guardian_cutscene(self): change_party(2), field.Pause(2), + # getting in position field.EntityAct(field_entity.PARTY0, True, field_entity.Move(direction.UP, 3), field_entity.AnimateFrontHandsUp(), @@ -829,6 +843,7 @@ def gauntlet_guardian_cutscene(self): field_entity.Pause(5), field_entity.AnimateStandingFront(), ), + # grabbing other party attention field.EntityAct(field_entity.PARTY0, False, field_entity.Pause(5), field_entity.AnimateFrontHandsUp(), @@ -859,7 +874,7 @@ def gauntlet_guardian_cutscene(self): field_entity.Move(direction.UP, 1), ), ], - field.Call(0xc1827), + field.Call(0xc1827), # original guardian event code field.Return(), ] space = Write(Bank.F0, src, "Guardian Gauntlet Cutscene") diff --git a/objectives/results/unlock_kt_gauntlet.py b/objectives/results/unlock_kt_gauntlet.py new file mode 100644 index 00000000..768618e8 --- /dev/null +++ b/objectives/results/unlock_kt_gauntlet.py @@ -0,0 +1,19 @@ +from objectives.results._objective_result import * +import data.event_bit as event_bit + +class Field(field_result.Result): + def src(self): + return [ + field.SetEventBit(event_bit.UNLOCKED_KT_GAUNTLET), + ] + +class Battle(battle_result.Result): + def src(self): + return [ + battle_result.SetBit(event_bit.address(event_bit.UNLOCKED_KT_GAUNTLET), event_bit.UNLOCKED_KT_GAUNTLET), + ] + +class Result(ObjectiveResult): + NAME = "Unlock KT Gauntlet" + def __init__(self): + super().__init__(Field, Battle) From 65dc845b561039d92a1995b0034e146c14b19436 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 19 Oct 2022 22:46:34 -0400 Subject: [PATCH 38/67] fix dialog/kt event --- data/dialogs/dialogs.py | 6 +++--- data/dialogs/free.py | 10 +++++----- event/kefka_tower.py | 19 +++++++++++-------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/data/dialogs/dialogs.py b/data/dialogs/dialogs.py index 4d5b3a3a..3a5a13c6 100644 --- a/data/dialogs/dialogs.py +++ b/data/dialogs/dialogs.py @@ -106,10 +106,10 @@ def read_multi_line_battle_dialogs(self): self.multi_line_battle_dialogs.append(dialog) def free(self): - import data.dialogs.free as free + from data.dialogs.free import multi_line_battle_dialogs self.free_multi_line_battle_dialogs = [] - for dialog_id in free.multi_line_battle_dialogs: + for dialog_id in multi_line_battle_dialogs: self.multi_line_battle_dialogs[dialog_id].text = "" self.free_multi_line_battle_dialogs.append(dialog_id) @@ -180,7 +180,7 @@ def objectives_mod(self): self.multi_line_battle_objectives.append(mlid) def create_dialog(self, text): - from dialogs.free import dialogs + from data.dialogs.free import dialogs id = dialogs.pop() self.set_text(id, text) return id diff --git a/data/dialogs/free.py b/data/dialogs/free.py index e25bf3d3..cc803d54 100644 --- a/data/dialogs/free.py +++ b/data/dialogs/free.py @@ -1,8 +1,8 @@ -dialogs = ( - range(515, 528) + # kefka assaulting thamasa - range(550, 598) + # sabin scenario, cyan kefka doma event - range(1767, 1878) # BANQUET -) + +dialogs = [] +dialogs += range(515, 528) # kefka assaulting thamasa +dialogs += range(550, 598) # sabin scenario, cyan kefka doma event +dialogs +=range(1767, 1878) # BANQUET multi_line_battle_dialogs = [ 0, # WEDGE:Hey! What's the matter?Do you know something wedon't……? ... diff --git a/event/kefka_tower.py b/event/kefka_tower.py index c83ff6cf..ae311951 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -282,12 +282,12 @@ def entrance_landing_mod(self): need_more_allies = 2982 self.dialogs.set_text(need_more_allies, "We need to find more allies.") - statues_entrance = self.dialogs.create_dialog(" (Statues) (Not just yet)") - gauntlet_entrance = self.dialogs.create_dialog(" (Gauntlet) (Not just yet)") - both_entrance = self.dialogs.create_dialog(" (Gauntlet) (Statues) (Not just yet)") + statues_entrance = self.dialogs.create_dialog(" (Statues) (Entrance) (Not just yet)") + gauntlet_entrance = self.dialogs.create_dialog(" (Gauntlet) (Entrance) (Not just yet)") + both_entrance = self.dialogs.create_dialog(" (Gauntlet) (Statues) (Entrance) (Not just yet)") space = Reserve(0xa01a2, 0xa02d5, "kefka tower first landing scene", field.NOP()) - space.add_label("GAUNTLET", self.gauntlet_event) + space.add_label("GAUNTLET_LANDING", self.gauntlet_event) space.add_label("STATUE_LANDING", self.statue_landing) space.add_label("ENTRANCE_LANDING", space.end_address + 1) space.write( @@ -311,20 +311,23 @@ def entrance_landing_mod(self): field.Return(), "STATUE_MENU_EVAL", - field.BranchIfEventBitSet(event_bit.event_bits.UNLOCKED_KT_GAUNTLET, "GAUNTLET_STATUES_DIALOG"), + field.BranchIfEventBitSet(event_bit.UNLOCKED_KT_GAUNTLET, "GAUNTLET_STATUES_DIALOG"), field.Branch("STATUES_DIALOG"), "GAUNTLET_DIALOG", field.DialogBranch(gauntlet_entrance, - dest1 = "GAUNTLET", dest2 = "CANCEL_LANDING"), + dest1 = "GAUNTLET_LANDING", dest2 = "ENTRANCE_LANDING", dest3 = "CANCEL_LANDING"), "STATUES_DIALOG", field.DialogBranch(statues_entrance, - dest1 = "STATUES", dest2 = "CANCEL_LANDING"), + dest1 = "STATUE_LANDING", dest2 = "ENTRANCE_LANDING", dest3 = "CANCEL_LANDING"), "GAUNTLET_STATUES_DIALOG", field.DialogBranch(both_entrance, - dest1 = "GAUNTLET", dest2 = "STATUES", dest3 = "CANCEL_LANDING"), + dest1 = "GAUNTLET_LANDING", dest2 = "STATUE_LANDING", dest3 = "ENTRANCE_LANDING", dest4 = "CANCEL_LANDING"), ) + + + def kefka_scene_mod(self): space = Reserve(0xc17ff, 0xc1801, "kefka tower defeat the statues, and magical power will not disappear", field.NOP()) From d291ec4dbf0cd4c179c6103dfa9a8a27056f3758 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Thu, 20 Oct 2022 16:58:56 -0400 Subject: [PATCH 39/67] remove weird custom functionality (Save points, deleting exits) --- event/kefka_tower.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index ae311951..249b9eda 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -268,15 +268,6 @@ def disable_all(boss_name): space = Write(Bank.F0, src, "kefka tower gauntlet") self.gauntlet_event = space.start_address - # Delete the three exits of the final room and add a save point - self.maps.delete_short_exit(final_switch_map_id, 103, 49) - self.maps.delete_short_exit(final_switch_map_id, 109, 46) - self.maps.delete_short_exit(final_switch_map_id, 115, 48) - - self.maps.add_save_point(final_switch_map_id, 103, 45) # left lane - self.maps.add_save_point(final_switch_map_id, 109, 42) # mid lane - self.maps.add_save_point(final_switch_map_id, 115, 44) # right lane - def entrance_landing_mod(self): need_more_allies = 2982 From 977cba20a069996825a2f16499627dd863949346 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Thu, 20 Oct 2022 21:53:46 -0400 Subject: [PATCH 40/67] fix result type --- constants/objectives/results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/objectives/results.py b/constants/objectives/results.py index 9d529ffe..d0281e66 100644 --- a/constants/objectives/results.py +++ b/constants/objectives/results.py @@ -85,7 +85,7 @@ ], } -category_types["Kefka's Tower"] += [ResultType(90, "Unlock KT Gauntlet", None)] +category_types["Kefka's Tower"] += [ResultType(90, "Unlock KT Gauntlet", "Unlock KT Gauntlet", None)] categories = list(category_types.keys()) From 1c80db4d7f086b98e5df9b56f4b85e440c05b4db Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 21 Oct 2022 16:39:11 -0400 Subject: [PATCH 41/67] attempt to add field command to loot chest --- data/maps.py | 3 ++ event/narshe_wob.py | 41 +++++++++++++++++++++ instruction/c0.py | 13 +++++++ instruction/field/custom.py | 70 +++++++++++++++++++++++++++++++++++- objectives/results/elixir.py | 19 ++++++++++ wc.py | 4 +++ 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 objectives/results/elixir.py diff --git a/data/maps.py b/data/maps.py index 8c63f69f..0625ab85 100644 --- a/data/maps.py +++ b/data/maps.py @@ -107,6 +107,9 @@ def get_chest_count(self, map_id): def set_chest_item(self, map_id, x, y, item_id): self.chests.set_item(map_id, x, y, item_id) + def get_chests(self, map_id): + return self.chests.map_chests[map_id] + def get_event_count(self, map_id): return (self.maps[map_id + 1]["events_ptr"] - self.maps[map_id]["events_ptr"]) // MapEvent.DATA_SIZE diff --git a/event/narshe_wob.py b/event/narshe_wob.py index 70c885b8..a27a3666 100644 --- a/event/narshe_wob.py +++ b/event/narshe_wob.py @@ -1,3 +1,6 @@ +from constants.entities import IMP +from data.map_event import MapEvent +from data.npc import NPC from event.event import * class NarsheWOB(Event): @@ -20,6 +23,7 @@ def mod(self): self.terra_elder_scene_mod() self.security_checkpoint_mod() self.shop_mod() + self.doot_mod() def end_terra_scenario(self): # delete the end of terra's scenario event in arvis' house @@ -86,3 +90,40 @@ def shop_mod(self): space.write( field.Branch("INVOKE_SHOP"), ) + + def doot_mod(self): + map_id = 104 + chests = self.maps.get_chests(107) + chest0 = chests[0] + chest1 = chests[1] + + + + src = [ + field.CollectChest(77), + ] + + loot_chest_two = Write(Bank.CA, src, "Loot chest 2") + + src = [ + field.CollectChest(76), + ] + loot_chest_one = Write(Bank.CA, src, "Loot chest 1") + + # Place next to school in narshe WOB npc + + # Locke MIAB + # new_npc = NPC() + # new_npc.x = 32 + # new_npc.y = 56 + # new_npc.sprite = 1 + # new_npc.set_event_address(loot_chest_one.start_address) + # self.maps.append_npc(20, new_npc) + + # Terra Tincture + new_npc = NPC() + new_npc.x = 34 + new_npc.y = 56 + new_npc.sprite = 0 + new_npc.set_event_address(loot_chest_two.start_address) + self.maps.append_npc(20, new_npc) diff --git a/instruction/c0.py b/instruction/c0.py index 447a3c48..d729b706 100644 --- a/instruction/c0.py +++ b/instruction/c0.py @@ -334,3 +334,16 @@ def _add_item_mod(): space = Write(Bank.C0, src, "c0 add item") return space.start_address add_item = _add_item_mod() + +# X must be set as the treasure chest offset to the register +# Y must be set as the treasure chest bit +def _loot_chest_mod(): + src = [ + # Treasure subroutine + asm.JSR(0x4c06, asm.ABS), + asm.RTS(), + ] + space = Write(Bank.C0, src, "c0 loot chest") + return space.start_address + +loot_chest = _loot_chest_mod() diff --git a/instruction/field/custom.py b/instruction/field/custom.py index bdc6f5d5..798226b6 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -7,7 +7,7 @@ def _set_opcode_address(opcode, address): FIRST_OPCODE = 0x35 opcode_table_address = 0x098c4 + (opcode - FIRST_OPCODE) * 2 - space = Reserve(opcode_table_address, opcode_table_address + 1, "field opcode table, {opcode} {hex(address)}") + space = Reserve(opcode_table_address, opcode_table_address + 1, f"field opcode table, {opcode} {hex(address)}") space.write( (address & 0xffff).to_bytes(2, "little"), ) @@ -231,3 +231,71 @@ def __init__(self, function_address, arg = 0): LongCall.__init__ = (lambda self, function_address, arg = 0 : super().__init__(opcode, function_address.to_bytes(3, "little"), arg)) self.__init__(function_address, arg) + +CHEST_BLOCK_SIZE = 5 +class CollectChest(_Instruction): + def __init__(self, chest_id): + loot_treasure_chest_function = START_ADDRESS_SNES + c0.loot_chest + chest_index_offset = (chest_id) * CHEST_BLOCK_SIZE + chest_bit = chest_id // 8 + + src = [ + asm.PHY(), + asm.PHX(), + asm.LDX(0x0000, asm.IMM16), + asm.LDY(0x0000, asm.IMM16), + asm.LDX(chest_index_offset, asm.IMM16), + asm.LDY(chest_bit, asm.IMM16), + asm.JSR(loot_treasure_chest_function, asm.ABS), + asm.PLY(), + asm.PLX(), + asm.RTS(), + ] + space = Write(Bank.C0, src, "custom loot_chest command") + address = space.start_address + + opcode = 0xec + _set_opcode_address(opcode, address) + + CollectChest.__init__ = lambda self, chest_id : super().__init__(opcode, chest_id.to_bytes(2, "little")) + self.__init__(chest_id) + + def __str__(self): + return super().__str__(self.args[0]) + + +# C0/4C08: BF3886ED LDA $ED8638,X +# C0/4C0C: 851A STA $1A (now the contents of the chest) +# C0/4C0E: BF3686ED LDA $ED8636,X +# C0/4C12: 851E STA $1E (the bit of this chest) +class BranchIfChestCollected(_Branch): + def __init__(self, chest_id, destination): + + chest_offset = chest_id * CHEST_BLOCK_SIZE + + src = [ + asm.PHA(), + asm.PHX(), + asm.PHY(), + asm.LDX(chest_offset, asm.IMM16), + asm.LDA(0xed8636, asm.ABS_X), # Load + asm.LDA(0x1a, asm.DIR), + asm.AND(0x1a, asm.DIR), + asm.BRA(destination), + asm.PLA(), + asm.PLX(), + asm.PLY(), + asm.RTS(), + ] + space = Write(Bank.C0, src, "custom loot_chest command") + address = space.start_address + + opcode = 0xa4 + _set_opcode_address(opcode, address) + args = [chest_id.to_bytes(2, "little")] + + BranchIfChestCollected.__init__ = lambda self, chest_id, destination : super().__init__(opcode, args, destination) + self.__init__(chest_id, destination) + + def __str__(self): + return super().__str__(self.args[0]) diff --git a/objectives/results/elixir.py b/objectives/results/elixir.py new file mode 100644 index 00000000..49c04e5b --- /dev/null +++ b/objectives/results/elixir.py @@ -0,0 +1,19 @@ +from objectives.results._objective_result import * +from data.item_names import name_id as item_name_id + +class Field(field_result.Result): + def src(self): + return [ + field.AddItem(item_name_id["Elixir"]), + ] + +class Battle(battle_result.Result): + def src(self): + return [ + battle_result.AddItem(item_name_id["Elixir"]), + ] + +class Result(ObjectiveResult): + NAME = "Elixir" + def __init__(self): + super().__init__(Field, Battle) diff --git a/wc.py b/wc.py index a6d8ea49..f454e974 100644 --- a/wc.py +++ b/wc.py @@ -27,4 +27,8 @@ def main(): memory.write() if __name__ == '__main__': + import debugpy + debugpy.listen(5678) + debugpy.wait_for_client() # blocks execution until client is attached + main() From ac1da8a934c1395cbebdd4d652a4ca14e6e94ebf Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 21 Oct 2022 17:47:04 -0400 Subject: [PATCH 42/67] set more clear example of bug --- constants/chests.py | 4 ++++ event/narshe_wob.py | 45 +++++++++++++++++-------------------- instruction/c0.py | 4 ++-- instruction/field/custom.py | 8 +++---- 4 files changed, 30 insertions(+), 31 deletions(-) create mode 100644 constants/chests.py diff --git a/constants/chests.py b/constants/chests.py new file mode 100644 index 00000000..c9fa3441 --- /dev/null +++ b/constants/chests.py @@ -0,0 +1,4 @@ + +NARSHE_SCHOOL_MIAB = 76 +NARSHE_SCHOOL_TINCTURE = 77 + diff --git a/event/narshe_wob.py b/event/narshe_wob.py index a27a3666..3b612b12 100644 --- a/event/narshe_wob.py +++ b/event/narshe_wob.py @@ -1,3 +1,4 @@ +from constants.chests import NARSHE_SCHOOL_MIAB from constants.entities import IMP from data.map_event import MapEvent from data.npc import NPC @@ -23,7 +24,7 @@ def mod(self): self.terra_elder_scene_mod() self.security_checkpoint_mod() self.shop_mod() - self.doot_mod() + self.chest_test_mod() def end_terra_scenario(self): # delete the end of terra's scenario event in arvis' house @@ -91,39 +92,33 @@ def shop_mod(self): field.Branch("INVOKE_SHOP"), ) - def doot_mod(self): - map_id = 104 - chests = self.maps.get_chests(107) - chest0 = chests[0] - chest1 = chests[1] + def chest_test_mod(self): + NARSHE_SCHOOL_MIAB = 76 + NARSHE_SCHOOL_CHEST = 77 - - - src = [ - field.CollectChest(77), + miab_src = [ + field.CollectChest(NARSHE_SCHOOL_MIAB), ] - loot_chest_two = Write(Bank.CA, src, "Loot chest 2") + miab_chest = Write(Bank.CA, miab_src, "Trigger MIAB chest") - src = [ - field.CollectChest(76), + tincture_src = [ + field.CollectChest(NARSHE_SCHOOL_CHEST), ] - loot_chest_one = Write(Bank.CA, src, "Loot chest 1") + tincture_chest = Write(Bank.CA, tincture_src, "Trigger tincture chest") - # Place next to school in narshe WOB npc - - # Locke MIAB - # new_npc = NPC() - # new_npc.x = 32 - # new_npc.y = 56 - # new_npc.sprite = 1 - # new_npc.set_event_address(loot_chest_one.start_address) - # self.maps.append_npc(20, new_npc) + # Locke outside of narshe school (on left) should trigger the school MIAB lobo fight + new_npc = NPC() + new_npc.x = 32 + new_npc.y = 56 + new_npc.sprite = 1 + new_npc.set_event_address(miab_chest.start_address) + self.maps.append_npc(20, new_npc) - # Terra Tincture + # Terra outside of narshe school (on right) should trigger school chest opposite of the MIAB new_npc = NPC() new_npc.x = 34 new_npc.y = 56 new_npc.sprite = 0 - new_npc.set_event_address(loot_chest_two.start_address) + new_npc.set_event_address(tincture_chest.start_address) self.maps.append_npc(20, new_npc) diff --git a/instruction/c0.py b/instruction/c0.py index d729b706..3719a5ef 100644 --- a/instruction/c0.py +++ b/instruction/c0.py @@ -335,8 +335,8 @@ def _add_item_mod(): return space.start_address add_item = _add_item_mod() -# X must be set as the treasure chest offset to the register -# Y must be set as the treasure chest bit +# X must be set as the treasure chest offset to the register (id * 5) +# Y must be set as the treasure chest bit (id // 8) def _loot_chest_mod(): src = [ # Treasure subroutine diff --git a/instruction/field/custom.py b/instruction/field/custom.py index 798226b6..a64c06e1 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -236,7 +236,7 @@ def __init__(self, function_address, arg = 0): class CollectChest(_Instruction): def __init__(self, chest_id): loot_treasure_chest_function = START_ADDRESS_SNES + c0.loot_chest - chest_index_offset = (chest_id) * CHEST_BLOCK_SIZE + chest_offset = chest_id * CHEST_BLOCK_SIZE chest_bit = chest_id // 8 src = [ @@ -244,7 +244,7 @@ def __init__(self, chest_id): asm.PHX(), asm.LDX(0x0000, asm.IMM16), asm.LDY(0x0000, asm.IMM16), - asm.LDX(chest_index_offset, asm.IMM16), + asm.LDX(chest_offset, asm.IMM16), asm.LDY(chest_bit, asm.IMM16), asm.JSR(loot_treasure_chest_function, asm.ABS), asm.PLY(), @@ -257,7 +257,7 @@ def __init__(self, chest_id): opcode = 0xec _set_opcode_address(opcode, address) - CollectChest.__init__ = lambda self, chest_id : super().__init__(opcode, chest_id.to_bytes(2, "little")) + CollectChest.__init__ = lambda self, chest : super().__init__(opcode, chest.to_bytes(2, "little")) self.__init__(chest_id) def __str__(self): @@ -294,7 +294,7 @@ def __init__(self, chest_id, destination): _set_opcode_address(opcode, address) args = [chest_id.to_bytes(2, "little")] - BranchIfChestCollected.__init__ = lambda self, chest_id, destination : super().__init__(opcode, args, destination) + BranchIfChestCollected.__init__ = lambda self, chest_id, destination : super().__init__(opcode, chest_id.to_bytes(2, "little"), destination) self.__init__(chest_id, destination) def __str__(self): From 6ce2f9426298f2d0059875dc740d9a3201a6a04d Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 21 Oct 2022 18:18:35 -0400 Subject: [PATCH 43/67] fix CollectChest --- instruction/field/custom.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/instruction/field/custom.py b/instruction/field/custom.py index a64c06e1..a6e2d928 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -236,16 +236,14 @@ def __init__(self, function_address, arg = 0): class CollectChest(_Instruction): def __init__(self, chest_id): loot_treasure_chest_function = START_ADDRESS_SNES + c0.loot_chest - chest_offset = chest_id * CHEST_BLOCK_SIZE - chest_bit = chest_id // 8 src = [ asm.PHY(), asm.PHX(), asm.LDX(0x0000, asm.IMM16), asm.LDY(0x0000, asm.IMM16), - asm.LDX(chest_offset, asm.IMM16), - asm.LDY(chest_bit, asm.IMM16), + asm.LDX(0xeb, asm.DIR), # chest byte + asm.LDY(0xec, asm.DIR), # chest bit asm.JSR(loot_treasure_chest_function, asm.ABS), asm.PLY(), asm.PLX(), @@ -257,7 +255,7 @@ def __init__(self, chest_id): opcode = 0xec _set_opcode_address(opcode, address) - CollectChest.__init__ = lambda self, chest : super().__init__(opcode, chest.to_bytes(2, "little")) + CollectChest.__init__ = lambda self, chest_id : super().__init__(opcode, (chest_id * CHEST_BLOCK_SIZE).to_bytes(2, "little"), (chest_id // 8).to_bytes(2, "little")) self.__init__(chest_id) def __str__(self): From 74cf9bf8f7fb5a161a1390caec523091cb27c2d8 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 21 Oct 2022 21:28:45 -0400 Subject: [PATCH 44/67] add all attempts/flavors --- instruction/field/custom.py | 91 +++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/instruction/field/custom.py b/instruction/field/custom.py index a6e2d928..e1bd9e55 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -2,6 +2,7 @@ from instruction.event import _Instruction, _Branch import instruction.asm as asm import instruction.c0 as c0 +import instruction.c2 as c2 from enum import IntEnum def _set_opcode_address(opcode, address): @@ -237,13 +238,61 @@ class CollectChest(_Instruction): def __init__(self, chest_id): loot_treasure_chest_function = START_ADDRESS_SNES + c0.loot_chest + + # src = [ + # asm.PHA(), + # asm.PHY(), + # asm.PHX(), + # asm.A16(), + # asm.LDX(0x0000, asm.IMM16), + # asm.LDY(0x0000, asm.IMM16), + # asm.LDX(0xeb, asm.DIR), # x = chest_id + # asm.LDA(0x0005, asm.IMM16), # a = 5 + # asm.STA(0xe8, asm.DIR), # (overwriting value address for multiplication) + # asm.TXA(), # a = stat / level + # asm.JSR(c2.multiply_max_65535, asm.ABS), # a = 5 * chest id + # asm.TAX(), # x = 5 * chest id (indext for loot function) + # asm.JSR(loot_treasure_chest_function, asm.ABS), + # asm.A8(), + # asm.PLA(), + # asm.PLY(), + # asm.PLX(), + # asm.RTS(), + # ] + + # messing around 2 + # src = [ + # asm.PHA(), + # asm.PHY(), + # asm.PHX(), + # asm.A16(), + # asm.LDX(0x0000, asm.IMM16), + # asm.LDY(0x0000, asm.IMM16), + # asm.LDA(0xec, asm.DIR), # a = absolute_chest_bit + # asm.TAX(), # x = absolute_chest_bit + # asm.LSR(), + # asm.LSR(), + # asm.LSR(), + # asm.LSR(), # a = absolute_chest_bit // 8 + # asm.TAY(), # y = absolute_chest_bit // 8 + # asm.LDA(0x1e40, asm.ABS_X), # 1e40 + absolute chest bit = 1 if collected, else 0 + # asm.BEQ("CHEST_FOUND"), + # asm.LDX(0xeb, asm.DIR), # relative chest byte + # asm.JSR(loot_treasure_chest_function, asm.ABS), + # "CHEST_FOUND", + # asm.A8(), + # asm.PLA(), + # asm.PLY(), + # asm.PLX(), + # asm.RTS(), + # ] + src = [ asm.PHY(), asm.PHX(), asm.LDX(0x0000, asm.IMM16), asm.LDY(0x0000, asm.IMM16), asm.LDX(0xeb, asm.DIR), # chest byte - asm.LDY(0xec, asm.DIR), # chest bit asm.JSR(loot_treasure_chest_function, asm.ABS), asm.PLY(), asm.PLX(), @@ -255,45 +304,19 @@ def __init__(self, chest_id): opcode = 0xec _set_opcode_address(opcode, address) - CollectChest.__init__ = lambda self, chest_id : super().__init__(opcode, (chest_id * CHEST_BLOCK_SIZE).to_bytes(2, "little"), (chest_id // 8).to_bytes(2, "little")) + CollectChest.__init__ = lambda self, chest_id : super().__init__(opcode, (chest_id * CHEST_BLOCK_SIZE).to_bytes(2, "little")) self.__init__(chest_id) def __str__(self): return super().__str__(self.args[0]) -# C0/4C08: BF3886ED LDA $ED8638,X -# C0/4C0C: 851A STA $1A (now the contents of the chest) -# C0/4C0E: BF3686ED LDA $ED8636,X -# C0/4C12: 851E STA $1E (the bit of this chest) +# Tried to piggyback off of BattleEventBit branch command but didn't work :(||) class BranchIfChestCollected(_Branch): - def __init__(self, chest_id, destination): - - chest_offset = chest_id * CHEST_BLOCK_SIZE - - src = [ - asm.PHA(), - asm.PHX(), - asm.PHY(), - asm.LDX(chest_offset, asm.IMM16), - asm.LDA(0xed8636, asm.ABS_X), # Load - asm.LDA(0x1a, asm.DIR), - asm.AND(0x1a, asm.DIR), - asm.BRA(destination), - asm.PLA(), - asm.PLX(), - asm.PLY(), - asm.RTS(), - ] - space = Write(Bank.C0, src, "custom loot_chest command") - address = space.start_address - - opcode = 0xa4 - _set_opcode_address(opcode, address) - args = [chest_id.to_bytes(2, "little")] - - BranchIfChestCollected.__init__ = lambda self, chest_id, destination : super().__init__(opcode, chest_id.to_bytes(2, "little"), destination) - self.__init__(chest_id, destination) + def __init__(self, chest_bit, destination): + self.chest_bit = chest_bit + chest_bit_from_battle_bits = (0x1e40 - 0x1dc9) + chest_bit + super().__init__(0xb7, [chest_bit_from_battle_bits], destination) def __str__(self): - return super().__str__(self.args[0]) + return super().__str__(hex(self.chest_bit)) From 6a37c72e2d7d3442a6836c288a6ccdd32bde847a Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 21 Oct 2022 21:37:58 -0400 Subject: [PATCH 45/67] cleanup --- instruction/field/custom.py | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/instruction/field/custom.py b/instruction/field/custom.py index e1bd9e55..fb97d5ac 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -238,21 +238,25 @@ class CollectChest(_Instruction): def __init__(self, chest_id): loot_treasure_chest_function = START_ADDRESS_SNES + c0.loot_chest - + # how i thought it'd work # src = [ # asm.PHA(), # asm.PHY(), # asm.PHX(), # asm.A16(), + # asm.LDA(0x0000, asm.IMM16), # asm.LDX(0x0000, asm.IMM16), # asm.LDY(0x0000, asm.IMM16), # asm.LDX(0xeb, asm.DIR), # x = chest_id + # asm.LDA(0x1e40, asm.ABS_X), # 1e40 + absolute chest bit = 1 if collected, else 0 + # asm.BEQ("END"), # asm.LDA(0x0005, asm.IMM16), # a = 5 # asm.STA(0xe8, asm.DIR), # (overwriting value address for multiplication) # asm.TXA(), # a = stat / level # asm.JSR(c2.multiply_max_65535, asm.ABS), # a = 5 * chest id # asm.TAX(), # x = 5 * chest id (indext for loot function) # asm.JSR(loot_treasure_chest_function, asm.ABS), + # "END", # asm.A8(), # asm.PLA(), # asm.PLY(), @@ -260,39 +264,13 @@ def __init__(self, chest_id): # asm.RTS(), # ] - # messing around 2 - # src = [ - # asm.PHA(), - # asm.PHY(), - # asm.PHX(), - # asm.A16(), - # asm.LDX(0x0000, asm.IMM16), - # asm.LDY(0x0000, asm.IMM16), - # asm.LDA(0xec, asm.DIR), # a = absolute_chest_bit - # asm.TAX(), # x = absolute_chest_bit - # asm.LSR(), - # asm.LSR(), - # asm.LSR(), - # asm.LSR(), # a = absolute_chest_bit // 8 - # asm.TAY(), # y = absolute_chest_bit // 8 - # asm.LDA(0x1e40, asm.ABS_X), # 1e40 + absolute chest bit = 1 if collected, else 0 - # asm.BEQ("CHEST_FOUND"), - # asm.LDX(0xeb, asm.DIR), # relative chest byte - # asm.JSR(loot_treasure_chest_function, asm.ABS), - # "CHEST_FOUND", - # asm.A8(), - # asm.PLA(), - # asm.PLY(), - # asm.PLX(), - # asm.RTS(), - # ] - + # working, but attempting to loot an already looted chest will soft lock the game src = [ asm.PHY(), asm.PHX(), asm.LDX(0x0000, asm.IMM16), asm.LDY(0x0000, asm.IMM16), - asm.LDX(0xeb, asm.DIR), # chest byte + asm.LDX(0xeb, asm.DIR), # x = chest byte * 5 (index for loot table, used in chest fn) asm.JSR(loot_treasure_chest_function, asm.ABS), asm.PLY(), asm.PLX(), From e01ca33d0b683b94b1a19155b06ca05080bffc28 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 21 Oct 2022 21:39:00 -0400 Subject: [PATCH 46/67] bye exliri --- objectives/results/elixir.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 objectives/results/elixir.py diff --git a/objectives/results/elixir.py b/objectives/results/elixir.py deleted file mode 100644 index 49c04e5b..00000000 --- a/objectives/results/elixir.py +++ /dev/null @@ -1,19 +0,0 @@ -from objectives.results._objective_result import * -from data.item_names import name_id as item_name_id - -class Field(field_result.Result): - def src(self): - return [ - field.AddItem(item_name_id["Elixir"]), - ] - -class Battle(battle_result.Result): - def src(self): - return [ - battle_result.AddItem(item_name_id["Elixir"]), - ] - -class Result(ObjectiveResult): - NAME = "Elixir" - def __init__(self): - super().__init__(Field, Battle) From 15ee206390e9ba48601f83bb6a7ff66e26e5c6cd Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 21 Oct 2022 21:39:20 -0400 Subject: [PATCH 47/67] undo debug --- wc.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wc.py b/wc.py index f454e974..a6d8ea49 100644 --- a/wc.py +++ b/wc.py @@ -27,8 +27,4 @@ def main(): memory.write() if __name__ == '__main__': - import debugpy - debugpy.listen(5678) - debugpy.wait_for_client() # blocks execution until client is attached - main() From acfe3b66c36025010e57eaa32e857b7d3e47472f Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 21 Oct 2022 21:40:45 -0400 Subject: [PATCH 48/67] cleanup --- constants/chests.py | 4 ---- event/narshe_wob.py | 27 ++++++--------------------- 2 files changed, 6 insertions(+), 25 deletions(-) delete mode 100644 constants/chests.py diff --git a/constants/chests.py b/constants/chests.py deleted file mode 100644 index c9fa3441..00000000 --- a/constants/chests.py +++ /dev/null @@ -1,4 +0,0 @@ - -NARSHE_SCHOOL_MIAB = 76 -NARSHE_SCHOOL_TINCTURE = 77 - diff --git a/event/narshe_wob.py b/event/narshe_wob.py index 3b612b12..a7f0fc09 100644 --- a/event/narshe_wob.py +++ b/event/narshe_wob.py @@ -1,4 +1,3 @@ -from constants.chests import NARSHE_SCHOOL_MIAB from constants.entities import IMP from data.map_event import MapEvent from data.npc import NPC @@ -93,32 +92,18 @@ def shop_mod(self): ) def chest_test_mod(self): - NARSHE_SCHOOL_MIAB = 76 - NARSHE_SCHOOL_CHEST = 77 + NARSHE_SCHOOL_MIAB = 76 # MIAB + NARSHE_SCHOOL_CHEST = 77 # Tincture - miab_src = [ - field.CollectChest(NARSHE_SCHOOL_MIAB), - ] - - miab_chest = Write(Bank.CA, miab_src, "Trigger MIAB chest") - - tincture_src = [ + chest_src = [ field.CollectChest(NARSHE_SCHOOL_CHEST), ] - tincture_chest = Write(Bank.CA, tincture_src, "Trigger tincture chest") - - # Locke outside of narshe school (on left) should trigger the school MIAB lobo fight - new_npc = NPC() - new_npc.x = 32 - new_npc.y = 56 - new_npc.sprite = 1 - new_npc.set_event_address(miab_chest.start_address) - self.maps.append_npc(20, new_npc) + chest = Write(Bank.CA, chest_src, "Trigger treasure chest") - # Terra outside of narshe school (on right) should trigger school chest opposite of the MIAB + # Terra outside of narshe school, triggers new_npc = NPC() new_npc.x = 34 new_npc.y = 56 new_npc.sprite = 0 - new_npc.set_event_address(tincture_chest.start_address) + new_npc.set_event_address(chest.start_address) self.maps.append_npc(20, new_npc) From dee469f62c37ea187c4f6bc5a10f2d66b8f9aff3 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 22 Oct 2022 11:22:02 -0400 Subject: [PATCH 49/67] move npc to airship, move all code to custom.py --- event/airship.py | 31 ++++++++ event/narshe_wob.py | 18 ----- instruction/c0.py | 13 ---- instruction/field/custom.py | 148 ++++++++++++++++++++++++++---------- 4 files changed, 137 insertions(+), 73 deletions(-) diff --git a/event/airship.py b/event/airship.py index 89b74097..96cde371 100644 --- a/event/airship.py +++ b/event/airship.py @@ -1,3 +1,4 @@ +from data.npc import NPC from event.event import * class Airship(Event): @@ -19,6 +20,36 @@ def mod(self): self.unequip_party_members_npc_mod() self.inside_blackjack() self.return_to_airship() + self.chest_test_mod() + + + def chest_test_mod(self): + narshe_school_right_room = 107 # MIAB in chest 0, Tincture in chest 1 + chest = self.maps.get_chests(narshe_school_right_room)[1] + + chest_src = [ + field.CollectChest(narshe_school_right_room, chest.x, chest.y), + ] + chest = Write(Bank.CA, chest_src, "Trigger treasure chest") + + # Terra will loot the MIAB + new_npc = NPC() + new_npc.x = 15 + new_npc.y = 7 + new_npc.sprite = 0 + new_npc.direction = direction.DOWN + new_npc.set_event_address(chest.start_address) + self.maps.append_npc(6, new_npc) + + # Kefka will loot the Tincture + new_npc = NPC() + new_npc.x = 17 + new_npc.y = 7 + new_npc.sprite = 21 + new_npc.direction = direction.DOWN + new_npc.set_event_address(chest.start_address) + self.maps.append_npc(6, new_npc) + def controls_mod(self): fly_wor_fc_cancel_dialog = 1315 diff --git a/event/narshe_wob.py b/event/narshe_wob.py index a7f0fc09..59c6fa6d 100644 --- a/event/narshe_wob.py +++ b/event/narshe_wob.py @@ -1,6 +1,5 @@ from constants.entities import IMP from data.map_event import MapEvent -from data.npc import NPC from event.event import * class NarsheWOB(Event): @@ -23,7 +22,6 @@ def mod(self): self.terra_elder_scene_mod() self.security_checkpoint_mod() self.shop_mod() - self.chest_test_mod() def end_terra_scenario(self): # delete the end of terra's scenario event in arvis' house @@ -91,19 +89,3 @@ def shop_mod(self): field.Branch("INVOKE_SHOP"), ) - def chest_test_mod(self): - NARSHE_SCHOOL_MIAB = 76 # MIAB - NARSHE_SCHOOL_CHEST = 77 # Tincture - - chest_src = [ - field.CollectChest(NARSHE_SCHOOL_CHEST), - ] - chest = Write(Bank.CA, chest_src, "Trigger treasure chest") - - # Terra outside of narshe school, triggers - new_npc = NPC() - new_npc.x = 34 - new_npc.y = 56 - new_npc.sprite = 0 - new_npc.set_event_address(chest.start_address) - self.maps.append_npc(20, new_npc) diff --git a/instruction/c0.py b/instruction/c0.py index 3719a5ef..447a3c48 100644 --- a/instruction/c0.py +++ b/instruction/c0.py @@ -334,16 +334,3 @@ def _add_item_mod(): space = Write(Bank.C0, src, "c0 add item") return space.start_address add_item = _add_item_mod() - -# X must be set as the treasure chest offset to the register (id * 5) -# Y must be set as the treasure chest bit (id // 8) -def _loot_chest_mod(): - src = [ - # Treasure subroutine - asm.JSR(0x4c06, asm.ABS), - asm.RTS(), - ] - space = Write(Bank.C0, src, "c0 loot chest") - return space.start_address - -loot_chest = _loot_chest_mod() diff --git a/instruction/field/custom.py b/instruction/field/custom.py index fb97d5ac..22a81234 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -2,7 +2,6 @@ from instruction.event import _Instruction, _Branch import instruction.asm as asm import instruction.c0 as c0 -import instruction.c2 as c2 from enum import IntEnum def _set_opcode_address(opcode, address): @@ -235,55 +234,120 @@ def __init__(self, function_address, arg = 0): CHEST_BLOCK_SIZE = 5 class CollectChest(_Instruction): - def __init__(self, chest_id): - loot_treasure_chest_function = START_ADDRESS_SNES + c0.loot_chest - - # how i thought it'd work - # src = [ - # asm.PHA(), - # asm.PHY(), - # asm.PHX(), - # asm.A16(), - # asm.LDA(0x0000, asm.IMM16), - # asm.LDX(0x0000, asm.IMM16), - # asm.LDY(0x0000, asm.IMM16), - # asm.LDX(0xeb, asm.DIR), # x = chest_id - # asm.LDA(0x1e40, asm.ABS_X), # 1e40 + absolute chest bit = 1 if collected, else 0 - # asm.BEQ("END"), - # asm.LDA(0x0005, asm.IMM16), # a = 5 - # asm.STA(0xe8, asm.DIR), # (overwriting value address for multiplication) - # asm.TXA(), # a = stat / level - # asm.JSR(c2.multiply_max_65535, asm.ABS), # a = 5 * chest id - # asm.TAX(), # x = 5 * chest id (indext for loot function) - # asm.JSR(loot_treasure_chest_function, asm.ABS), - # "END", - # asm.A8(), - # asm.PLA(), - # asm.PLY(), - # asm.PLX(), - # asm.RTS(), - # ] - - # working, but attempting to loot an already looted chest will soft lock the game + def __init__(self, map_id, x, y): src = [ - asm.PHY(), - asm.PHX(), - asm.LDX(0x0000, asm.IMM16), - asm.LDY(0x0000, asm.IMM16), - asm.LDX(0xeb, asm.DIR), # x = chest byte * 5 (index for loot table, used in chest fn) - asm.JSR(loot_treasure_chest_function, asm.ABS), - asm.PLY(), - asm.PLX(), - asm.RTS(), + "REMOTE_TREASURE", + asm.A16(), # REP #$20 + asm.LDA(0xeb, asm.DIR), # LDA $EB ; load map + asm.ASL(), # ASL A + asm.TAX(), # TAX + asm.LDA(0xed82f6, asm.LNG_X), # LDA $ED82F6,X ; load the pointer for the *next room* + asm.STA(0x1e, asm.DIR), # STA $1E $1e = next room pointer + asm.LDA(0xed82f4, asm.LNG_X), # LDA $ED82F4,X ; load the pointer for our current room + asm.TAX(), # TAX x = current room pointer + asm.TDC(), # TDC + asm.A8(), # SEP #$20 + asm.CPX(0x1e, asm.DIR), # CPX $1E ; do the two pointers match? if they do, this map has no treasure + asm.BEQ("NO_CHEST_FOUND"), # BEQ no_chest + + "TREASURE_LOOP_AGAIN", # treasure_loop_again: + asm.LDA(0xed8634, asm.LNG_X), # LDA $ED8634,X ; load X coordinate of chest + asm.CMP(0xed, asm.DIR), # CMP $ED ; does it match? + asm.BNE("NO_CHEST"), # BNE no_chest ; we do have to have some kind of fail-safe in place + asm.LDA(0xed8635, asm.LNG_X), # LDA $ED8635,X ; load Y coordinate of chest + asm.CMP(0xee, asm.DIR), # CMP $EE ; does it match? + asm.BEQ("TREASURE_FOUND"), # BEQ treasure_found ; we have matched X and Y, let's get the contents + + "NO_CHEST", # no_chest: + asm.INX(), # INX + asm.INX(), # INX + asm.INX(), # INX + asm.INX(), # INX + asm.INX(), # INX + asm.CPX(0x1e, asm.DIR), # CPX $1E + asm.BNE("TREASURE_LOOP_AGAIN"), # BNE treasure_loop_again + # ; coming in, upper A is already 00 + + "NO_CHEST_FOUND", # Fix for infinite loop + asm.TDC(), # TDC + asm.LDA(0x05, asm.IMM8), # command size + asm.JMP(0x9b5c, asm.ABS), # next command + + "TREASURE_FOUND", # treasure_found: + asm.A16(), # REP #$20 + asm.LDA(0xed8638, asm.LNG_X), # LDA $ED8638,X ; load contents + asm.STA(0x1a, asm.DIR), # STA $1A + asm.LDA(0xed8636, asm.LNG_X), # LDA $ED8636,X ; load the byte and bit + asm.STA(0x1e, asm.DIR), # STA $1E + asm.AND(0x0007, asm.IMM16), # AND #$0007 + asm.TAX(), # TAX + asm.LDA(0x1e, asm.DIR), # LDA $1E + asm.AND(0x01f8, asm.IMM16), # AND #$01F8 + asm.LSR(), # LSR A + asm.LSR(), # LSR A + asm.LSR(), # LSR A + asm.TAY(), # TAY + asm.TDC(), # TDC + asm.A8(), # SEP #$20 + asm.LDA(0x1e40, asm.ABS_Y), # LDA $1E40,Y + asm.AND(0xc0bafc, asm.LNG_X), # AND $C0BAFC,X ; is this bit set? + asm.BNE("NO_CHEST"), # BNE no_chest ; branch and exit if so + asm.LDA(0x1e40, asm.ABS_Y), # LDA $1E40,Y + asm.ORA(0xc0bafc, asm.LNG_X), # ORA $C0BAFC,X ; set this bit, meaning we have now opened this box + asm.STA(0x1e40, asm.ABS_Y), # STA $1E40,Y + asm.LDA(0x1f, asm.DIR), # LDA $1F + asm.BPL("NOT_GIL_TREASURE"), # BPL not_gil_treasure + asm.LDA(0x1a, asm.DIR), # LDA $1A ; load amount + asm.STA(0x4202, asm.ABS), # STA $4202 + asm.LDA(0x64, asm.IMM8), # LDA #$64 + asm.STA(0x4203, asm.ABS), # STA $4203 ; multiply it by 100 + asm.NOP(), # NOP + asm.NOP(), # NOP + asm.NOP(), # NOP + asm.NOP(), # NOP + asm.REP(0x21), # REP #$21 + asm.LDA(0x4216, asm.ABS), # LDA $4216 ; load product + asm.ADC(0x1860, asm.ABS), # ADC $1860 + asm.STA(0x1860, asm.ABS), # STA $1860 + asm.TDC(), # TDC + asm.A8(), # SEP #$20 + asm.BCC("GIL_NO_WRAP"), # BCC gil_no_wrap ; branch if result didn't wrap. meaning whatever we picked up didn't add to the third byte + asm.INC(0x1862, asm.ABS), # INC $1862 + + "GIL_NO_WRAP", # gil_no_wrap: + asm.LDA(0x7f, asm.IMM8), # LDA #$7F + asm.CMP(0x1860, asm.ABS), # CMP $1860 + asm.LDA(0x96, asm.IMM8), # LDA #$96 + asm.SBC(0x1861, asm.ABS), # SBC $1861 + asm.LDA(0x98, asm.IMM8), # LDA #$98 + asm.SBC(0x1862, asm.ABS), # SBC $1862 + asm.BCS("TREASURE_WRAPUP"), # BCS treasure_wrapup ; if carry is still set, we didn't overflow our GP. time to finish up + asm.LDX(0x967f, asm.IMM16), # LDX #$967F + asm.STX(0x1860, asm.ABS), # STX $1860 + asm.LDA(0x98, asm.IMM8), # LDA #$98 + asm.STA(0x1862, asm.ABS), # STA $1862 + asm.BRA("TREASURE_WRAPUP"), # BRA treasure_wrapup + + "NOT_GIL_TREASURE", # not_gil_treasure: + asm.BIT(0x40, asm.IMM8), # BIT #$40 ; item? + asm.BEQ("TREASURE_WRAPUP"), # BEQ treasure_wrapup ; if it isn't an item, it's an "Empty" or a MiaB, neither of which we need to do anything about here. not handling MiaB may be an oversight, but let's assume the end-user is smart enough not to remotely open one of those + asm.LDA(0x1a, asm.DIR), # LDA $1A + asm.JSR(0xacfc, asm.ABS), # JSR $ACFC ; add the item to inventory + + "TREASURE_WRAPUP", # treasure_wrapup: + asm.TDC(), # TDC + asm.LDA(0x05, asm.IMM8), # command size + asm.JMP(0x9b5c, asm.ABS), # next command ] + space = Write(Bank.C0, src, "custom loot_chest command") address = space.start_address opcode = 0xec _set_opcode_address(opcode, address) - CollectChest.__init__ = lambda self, chest_id : super().__init__(opcode, (chest_id * CHEST_BLOCK_SIZE).to_bytes(2, "little")) - self.__init__(chest_id) + CollectChest.__init__ = lambda self, map_id, x, y : super().__init__(opcode, map_id.to_bytes(2, "little"), x, y) + self.__init__(map_id, x, y) def __str__(self): return super().__str__(self.args[0]) From 39484d0dd1f2570555b36a33e7c5941fda4724ff Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 22 Oct 2022 11:26:56 -0400 Subject: [PATCH 50/67] undo narshe_wob changes --- event/narshe_wob.py | 1 - 1 file changed, 1 deletion(-) diff --git a/event/narshe_wob.py b/event/narshe_wob.py index 59c6fa6d..7111195a 100644 --- a/event/narshe_wob.py +++ b/event/narshe_wob.py @@ -88,4 +88,3 @@ def shop_mod(self): space.write( field.Branch("INVOKE_SHOP"), ) - From 5a3825c33c95cee75bdf7b82290dfbeea9614ed2 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 22 Oct 2022 11:27:16 -0400 Subject: [PATCH 51/67] more undoing --- event/narshe_wob.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/event/narshe_wob.py b/event/narshe_wob.py index 7111195a..70c885b8 100644 --- a/event/narshe_wob.py +++ b/event/narshe_wob.py @@ -1,5 +1,3 @@ -from constants.entities import IMP -from data.map_event import MapEvent from event.event import * class NarsheWOB(Event): From 9a162539f849885ca20a361f82623b947e67d96b Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 22 Oct 2022 11:27:50 -0400 Subject: [PATCH 52/67] only show airship npcs in debug mode --- event/airship.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/event/airship.py b/event/airship.py index 96cde371..400ee6ca 100644 --- a/event/airship.py +++ b/event/airship.py @@ -20,7 +20,8 @@ def mod(self): self.unequip_party_members_npc_mod() self.inside_blackjack() self.return_to_airship() - self.chest_test_mod() + if self.args.debug: + self.chest_test_mod() def chest_test_mod(self): From b9ade9a2ca325bc42f42f950493b5017c967fa04 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 22 Oct 2022 11:54:52 -0400 Subject: [PATCH 53/67] It is working! THANK YOU LENOPHIS! --- event/airship.py | 18 +++++++++++------- instruction/field/custom.py | 24 ++++-------------------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/event/airship.py b/event/airship.py index 400ee6ca..f2cecaaf 100644 --- a/event/airship.py +++ b/event/airship.py @@ -23,32 +23,36 @@ def mod(self): if self.args.debug: self.chest_test_mod() - def chest_test_mod(self): narshe_school_right_room = 107 # MIAB in chest 0, Tincture in chest 1 - chest = self.maps.get_chests(narshe_school_right_room)[1] + miab_chest = self.maps.get_chests(narshe_school_right_room)[0] + tincture_chest = self.maps.get_chests(narshe_school_right_room)[1] chest_src = [ - field.CollectChest(narshe_school_right_room, chest.x, chest.y), + field.CollectChest(narshe_school_right_room, miab_chest.x, miab_chest.y), ] - chest = Write(Bank.CA, chest_src, "Trigger treasure chest") + miab = Write(Bank.CA, chest_src, "Trigger treasure chest") - # Terra will loot the MIAB + # Terra will loot the MIAB (will not work right now) new_npc = NPC() new_npc.x = 15 new_npc.y = 7 new_npc.sprite = 0 new_npc.direction = direction.DOWN - new_npc.set_event_address(chest.start_address) + new_npc.set_event_address(miab.start_address) self.maps.append_npc(6, new_npc) # Kefka will loot the Tincture + chest_src = [ + field.CollectChest(narshe_school_right_room, tincture_chest.x, tincture_chest.y), + ] + tincture = Write(Bank.CA, chest_src, "Trigger treasure chest") new_npc = NPC() new_npc.x = 17 new_npc.y = 7 new_npc.sprite = 21 new_npc.direction = direction.DOWN - new_npc.set_event_address(chest.start_address) + new_npc.set_event_address(tincture.start_address) self.maps.append_npc(6, new_npc) diff --git a/instruction/field/custom.py b/instruction/field/custom.py index 22a81234..0233525d 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -248,9 +248,9 @@ def __init__(self, map_id, x, y): asm.TDC(), # TDC asm.A8(), # SEP #$20 asm.CPX(0x1e, asm.DIR), # CPX $1E ; do the two pointers match? if they do, this map has no treasure - asm.BEQ("NO_CHEST_FOUND"), # BEQ no_chest + asm.BEQ("TREASURE_WRAPUP"), # We chose a map without a treasure chest - branch and exit if so - "TREASURE_LOOP_AGAIN", # treasure_loop_again: + "TREASURE_LOOP_AGAIN", # treasure_loop_again: asm.LDA(0xed8634, asm.LNG_X), # LDA $ED8634,X ; load X coordinate of chest asm.CMP(0xed, asm.DIR), # CMP $ED ; does it match? asm.BNE("NO_CHEST"), # BNE no_chest ; we do have to have some kind of fail-safe in place @@ -268,11 +268,6 @@ def __init__(self, map_id, x, y): asm.BNE("TREASURE_LOOP_AGAIN"), # BNE treasure_loop_again # ; coming in, upper A is already 00 - "NO_CHEST_FOUND", # Fix for infinite loop - asm.TDC(), # TDC - asm.LDA(0x05, asm.IMM8), # command size - asm.JMP(0x9b5c, asm.ABS), # next command - "TREASURE_FOUND", # treasure_found: asm.A16(), # REP #$20 asm.LDA(0xed8638, asm.LNG_X), # LDA $ED8638,X ; load contents @@ -291,7 +286,7 @@ def __init__(self, map_id, x, y): asm.A8(), # SEP #$20 asm.LDA(0x1e40, asm.ABS_Y), # LDA $1E40,Y asm.AND(0xc0bafc, asm.LNG_X), # AND $C0BAFC,X ; is this bit set? - asm.BNE("NO_CHEST"), # BNE no_chest ; branch and exit if so + asm.BNE("TREASURE_WRAPUP"), # chest has already been looted ; branch and exit if so asm.LDA(0x1e40, asm.ABS_Y), # LDA $1E40,Y asm.ORA(0xc0bafc, asm.LNG_X), # ORA $C0BAFC,X ; set this bit, meaning we have now opened this box asm.STA(0x1e40, asm.ABS_Y), # STA $1E40,Y @@ -350,15 +345,4 @@ def __init__(self, map_id, x, y): self.__init__(map_id, x, y) def __str__(self): - return super().__str__(self.args[0]) - - -# Tried to piggyback off of BattleEventBit branch command but didn't work :(||) -class BranchIfChestCollected(_Branch): - def __init__(self, chest_bit, destination): - self.chest_bit = chest_bit - chest_bit_from_battle_bits = (0x1e40 - 0x1dc9) + chest_bit - super().__init__(0xb7, [chest_bit_from_battle_bits], destination) - - def __str__(self): - return super().__str__(hex(self.chest_bit)) + return super().__str__(self.args) From 5cadbfed7421f78f00ba0312a0ca404077441ca2 Mon Sep 17 00:00:00 2001 From: kielbasiago <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 22 Oct 2022 11:56:00 -0400 Subject: [PATCH 54/67] Update event/airship.py --- event/airship.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event/airship.py b/event/airship.py index f2cecaaf..67fe0b51 100644 --- a/event/airship.py +++ b/event/airship.py @@ -33,7 +33,7 @@ def chest_test_mod(self): ] miab = Write(Bank.CA, chest_src, "Trigger treasure chest") - # Terra will loot the MIAB (will not work right now) + # Terra will loot the MIAB (currently does nothing, safely exits) new_npc = NPC() new_npc.x = 15 new_npc.y = 7 From 1c34d0caa8cdf93c2b995e566470d2b9864d8358 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 22 Oct 2022 17:56:52 -0400 Subject: [PATCH 55/67] add complete kt gauntlet quest --- constants/objectives/condition_bits.py | 1 + data/event_bit.py | 1 + data/items.py | 2 ++ event/kefka_tower.py | 13 +++++++++---- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/constants/objectives/condition_bits.py b/constants/objectives/condition_bits.py index 57c7c2f1..d0821e20 100644 --- a/constants/objectives/condition_bits.py +++ b/constants/objectives/condition_bits.py @@ -87,6 +87,7 @@ NameBit("Suplex A Train", event_bit.SUPLEXED_TRAIN), NameBit("Win An Auction", event_bit.WON_AN_AUCTION), NameBit("Win A Coliseum Match", event_bit.WON_A_COLISEUM_MATCH), + NameBit("Complete the KT Gauntlet", event_bit.COMPLETED_KT_GAUNTLET) ] boss_bit = [] diff --git a/data/event_bit.py b/data/event_bit.py index fab63d2d..9c1eec35 100644 --- a/data/event_bit.py +++ b/data/event_bit.py @@ -216,6 +216,7 @@ # 3 bits 0x2c1-0x2c3 Unused UNLOCKED_KT_GAUNTLET = 0x2c2 +COMPLETED_KT_GAUNTLET = 0x2c3 # 8 bits 0x1e6-0x1ed Unused, as the SNES versions feature 20 rare item slots rather than 30 from constants.objectives import MAX_OBJECTIVES diff --git a/data/items.py b/data/items.py index d3e0f5db..90e60615 100644 --- a/data/items.py +++ b/data/items.py @@ -318,6 +318,8 @@ def add_receive_dialog(self, item_id): self.dialogs.set_text(dialog_id, '< >Received “' + item_name + '”!') + return dialog_id + def print(self): for item in self.items: item.print() diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 249b9eda..1e38c71b 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -225,8 +225,6 @@ def disable_all(boss_name): debug_event_bits, - field.ClearEventBit(event_bit.UNLOCKED_KT_SKIP), - field.SetEventBit(event_bit.LEFT_WEIGHT_PUSHED_KEFKA_TOWER), field.SetEventBit(event_bit.RIGHT_WEIGHT_PUSHED_KEFKA_TOWER), field.ClearEventBit(npc_bit.LEFT_UNPUSHED_WEIGHT_KEFKA_TOWER), @@ -664,7 +662,8 @@ def post_landing_src(self, map_id, map_x, map_y): Read(0xa039c, 0xa039f), field.LoadMap(map_id, direction.DOWN, default_music = True, x = map_x, y = map_y, fade_in = True, entrance_event = True), - + field.SetEventBit(event_bit.COMPLETED_KT_GAUNTLET), + field.CheckObjectives(), field.FreeScreen(), Read(0xa03b0, 0xa03b9), ] @@ -948,6 +947,8 @@ def gauntlet_goddess_cutscene(self): return space.start_address def gauntlet_poltergeist_cutscene(self): + chests = self.maps.get_chests(poltergeist_room_id) + chest = chests[7] src = [ change_party(2), field.HoldScreen(), @@ -986,7 +987,11 @@ def gauntlet_poltergeist_cutscene(self): field_entity.Move(direction.UP, 6), ), field.Pause(2), - field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), + [ + field.Dialog(self.items.add_receive_dialog(chest.contents), wait_for_input=False), + field.CollectChest(poltergeist_room_id, chest.x, chest.y), + field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), + ], field.Pause(2), field.Pause(1.5), From a5b3e1eb33a05aa06080e0d9def6541bc348af18 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Mon, 24 Oct 2022 16:10:10 -0400 Subject: [PATCH 56/67] add redundant teasure wrapup --- instruction/field/custom.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/instruction/field/custom.py b/instruction/field/custom.py index 0233525d..a2dd56cd 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -248,7 +248,7 @@ def __init__(self, map_id, x, y): asm.TDC(), # TDC asm.A8(), # SEP #$20 asm.CPX(0x1e, asm.DIR), # CPX $1E ; do the two pointers match? if they do, this map has no treasure - asm.BEQ("TREASURE_WRAPUP"), # We chose a map without a treasure chest - branch and exit if so + asm.BEQ("TREASURE_WRAPUP1"), # We chose a map without a treasure chest - branch and exit if so "TREASURE_LOOP_AGAIN", # treasure_loop_again: asm.LDA(0xed8634, asm.LNG_X), # LDA $ED8634,X ; load X coordinate of chest @@ -267,6 +267,10 @@ def __init__(self, map_id, x, y): asm.CPX(0x1e, asm.DIR), # CPX $1E asm.BNE("TREASURE_LOOP_AGAIN"), # BNE treasure_loop_again # ; coming in, upper A is already 00 + "TREASURE_WRAPUP1", # treasure_wrapup: + asm.TDC(), # TDC + asm.LDA(0x05, asm.IMM8), # command size + asm.JMP(0x9b5c, asm.ABS), # next command "TREASURE_FOUND", # treasure_found: asm.A16(), # REP #$20 From c912272336193d4ce02f6a9ea35dfa15a0903957 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 30 Oct 2022 17:10:35 -0400 Subject: [PATCH 57/67] feature: Add BranchIfTrasureCollected Chore: rename CollectChest => CollectTreasure --- event/kefka_tower.py | 56 +++++++++++++++++++++++++--------- instruction/field/custom.py | 61 ++++++++++++++++++++++++++++++++++--- 2 files changed, 99 insertions(+), 18 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 1e38c71b..3ec81f4a 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -24,7 +24,6 @@ def change_party(party): field.UpdatePartyLeader(), ] - class KefkaTower(Event): def name(self): return "Kefka's Tower" @@ -970,31 +969,60 @@ def gauntlet_poltergeist_cutscene(self): field.FadeInScreen(3), - field.EntityAct(field_entity.PARTY0, False, + field.BranchIfTreasureCollected(chest.bit, "IGNORE_CHEST"), + "COLLECT_CHEST", + field.EntityAct(field_entity.PARTY0, True, field_entity.SetSpeed(field_entity.Speed.NORMAL), field_entity.Move(direction.DOWN, 2), field_entity.Move(direction.RIGHT, 2), field_entity.Move(direction.DOWN, 3), field_entity.AnimateKneeling(), - field_entity.Pause(10), + ), + + field.CollectTreasure(poltergeist_room_id, chest.x, chest.y), + field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), + field.Dialog(self.items.add_receive_dialog(chest.contents), wait_for_input=False), + + field.EntityAct(field_entity.PARTY0, True, field_entity.AnimateStandingFront(), field_entity.Pause(2), field_entity.Move(direction.UP, 3), field_entity.Move(direction.RIGHT, 1), field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.Move(direction.UP, 3), - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.Move(direction.UP, 6), + field_entity.Move(direction.UP, 5), + ), + field.Branch("POST_TREASURE"), + + "IGNORE_CHEST", + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.NORMAL), + field_entity.Move(direction.DOWN, 2), + field_entity.Move(direction.RIGHT, 3), + field_entity.Move(direction.UP, 5), ), - field.Pause(2), - [ - field.Dialog(self.items.add_receive_dialog(chest.contents), wait_for_input=False), - field.CollectChest(poltergeist_room_id, chest.x, chest.y), - field.PlaySoundEffect(sfx_name_id.get('Chest/Switch')), - ], - field.Pause(2), - field.Pause(1.5), + "POST_TREASURE", + field.EntityAct(field_entity.PARTY0, True, + field.Pause(4), + field_entity.Turn(direction.DOWN), + field_entity.Pause(4), + field_entity.AnimateCloseEyes(), + field_entity.Pause(4), + field_entity.AnimateStandingHeadDown(), + field_entity.Pause(24), + field_entity.Turn(direction.DOWN), + field_entity.Pause(1), + field_entity.AnimateCloseEyes(), + field_entity.Pause(1), + field_entity.Turn(direction.DOWN), + field_entity.Pause(4), + field_entity.AnimateLowJump(), + field_entity.AnimatePowerStance(), + field_entity.Pause(12), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.UP, 4), + + ), field.Call(0xc174f), field.EntityAct(field_entity.PARTY0, False, diff --git a/instruction/field/custom.py b/instruction/field/custom.py index a2dd56cd..f6132068 100644 --- a/instruction/field/custom.py +++ b/instruction/field/custom.py @@ -1,5 +1,5 @@ from memory.space import Bank, START_ADDRESS_SNES, Reserve, Write, Read -from instruction.event import _Instruction, _Branch +from instruction.event import EVENT_CODE_START, _Instruction, _Branch import instruction.asm as asm import instruction.c0 as c0 from enum import IntEnum @@ -233,7 +233,7 @@ def __init__(self, function_address, arg = 0): self.__init__(function_address, arg) CHEST_BLOCK_SIZE = 5 -class CollectChest(_Instruction): +class CollectTreasure(_Instruction): def __init__(self, map_id, x, y): src = [ "REMOTE_TREASURE", @@ -267,7 +267,7 @@ def __init__(self, map_id, x, y): asm.CPX(0x1e, asm.DIR), # CPX $1E asm.BNE("TREASURE_LOOP_AGAIN"), # BNE treasure_loop_again # ; coming in, upper A is already 00 - "TREASURE_WRAPUP1", # treasure_wrapup: + "TREASURE_WRAPUP1", # treasure_wrapup1: this is a duplicate as the jump to the other is too far to safely make asm.TDC(), # TDC asm.LDA(0x05, asm.IMM8), # command size asm.JMP(0x9b5c, asm.ABS), # next command @@ -345,8 +345,61 @@ def __init__(self, map_id, x, y): opcode = 0xec _set_opcode_address(opcode, address) - CollectChest.__init__ = lambda self, map_id, x, y : super().__init__(opcode, map_id.to_bytes(2, "little"), x, y) + CollectTreasure.__init__ = lambda self, map_id, x, y : super().__init__(opcode, map_id.to_bytes(2, "little"), x, y) self.__init__(map_id, x, y) def __str__(self): return super().__str__(self.args) + +# Collect the contents (only if it hasn't been collected) +# If it has been collected, make jump to target destination +class BranchIfTreasureCollected(_Branch): + def __init__(self, chest_bit, destination): + src = [ + "CHECK_TREASURE", # check_treasure: + # ED xxxx aa bb cc + # xxxx is treasure bit. there's only #$2F treasure bytes + # aa bb cc is the event to jump to should a treasure already be open + asm.A16(), # REP #$20 + asm.LDA(0xeb, asm.DIR), # LDA $EB ; load our treasure byte/bit that we want to check + asm.PHA(), # PHA + asm.AND(0x0007, asm.IMM16), # AND #$0007 ; mask out the byte, keep the bits + asm.TAX(), # TAX + asm.PLA(), # PLA + asm.AND(0x01F8, asm.IMM16), # AND #$01F8 ; now mask out the bits, keep the byte + asm.LSR(), # LSR A + asm.LSR(), # LSR A + asm.LSR(), # LSR A + asm.TAY(), # TAY + asm.LDA(0x1e40, asm.ABS_Y), # LDA $1E40,Y ; load the treasure we are checking for + asm.AND(0xc0bafc, asm.ABS_X), # AND $C0BAFC,X ; is the bit we are checking for set? meaning, is this box already open? + asm.A8(), # SEP #$20 + asm.BNE("BRANCH_TO_DEST"), # BNE check_succeeds ; branch if so! + asm.BRA("RETURN"), + "BRANCH_TO_DEST", # check_succeeds: + asm.TDC(), # TDC + asm.LDX(0xed, asm.DIR), # LDX $ED load address + asm.STX(0xe5, asm.DIR), # STX $E5 store first 4 bytes + asm.LDA(0xef, asm.DIR), # LDA $EF st + asm.CLC(), # CLC + asm.ADC(0xca, asm.IMM8), # ADC #$CA + asm.STA(0xe7, asm.DIR), # STA $E7 + asm.JMP(0x9a6d, asm.ABS), # JMP $9A6D + "RETURN", + asm.TDC(), # TDC + asm.LDA(0x06, asm.IMM8), # LDA #$06 + asm.JMP(0x9b5c, asm.ABS), # JMP $9B5C - next command + ] + + space = Write(Bank.C0, src, "custom loot_treasure command") + address = space.start_address + + opcode = 0xed + _set_opcode_address(opcode, address) + + BranchIfTreasureCollected.__init__ = lambda self, chest_bit, destination : super().__init__(opcode, [chest_bit.to_bytes(2, "little")], destination) + + self.__init__(chest_bit, destination) + + def __str__(self): + return super().__str__(self.args) From ff7d43644a6c153f51e385539305cb4ac3b58296 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Tue, 1 Nov 2022 21:32:25 -0400 Subject: [PATCH 58/67] comment out disabling victory/battle music for KT --- event/kefka_tower.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 3ec81f4a..09c37de1 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -196,11 +196,11 @@ def disable_all(boss_name): disable_victory_dance(boss_name) disable_battle_music(boss_name) - disable_all("Inferno") - disable_all("Guardian") - disable_all("Doom") - disable_all("Goddess") - disable_all("Poltrgeist") + # disable_all("Inferno") + # disable_all("Guardian") + # disable_all("Doom") + # disable_all("Goddess") + # disable_all("Poltrgeist") self.inferno_cutscene = self.gauntlet_inferno_cutscene() self.guardian_cutscene = self.gauntlet_guardian_cutscene() From b43de2d8f542dccb86f426497e77e7da29f0c0f3 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 2 Nov 2022 20:15:22 -0400 Subject: [PATCH 59/67] fix debug chest collect function --- event/airship.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/event/airship.py b/event/airship.py index 67fe0b51..9d98e222 100644 --- a/event/airship.py +++ b/event/airship.py @@ -29,7 +29,7 @@ def chest_test_mod(self): tincture_chest = self.maps.get_chests(narshe_school_right_room)[1] chest_src = [ - field.CollectChest(narshe_school_right_room, miab_chest.x, miab_chest.y), + field.CollectTreasure(narshe_school_right_room, miab_chest.x, miab_chest.y), ] miab = Write(Bank.CA, chest_src, "Trigger treasure chest") @@ -44,7 +44,7 @@ def chest_test_mod(self): # Kefka will loot the Tincture chest_src = [ - field.CollectChest(narshe_school_right_room, tincture_chest.x, tincture_chest.y), + field.CollectTreasure(narshe_school_right_room, tincture_chest.x, tincture_chest.y), ] tincture = Write(Bank.CA, chest_src, "Trigger treasure chest") new_npc = NPC() From 216cda4ddb70bed2e91e0d4aea22a337b1281f6e Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:10:06 -0400 Subject: [PATCH 60/67] Add back guardian/inferno fade, but only when gauntlet not in progress --- data/event_bit.py | 1 + data/maps.py | 32 +------------------------------- event/airship.py | 35 ----------------------------------- event/kefka_tower.py | 32 +++++++++++++++++++++++++++++--- 4 files changed, 31 insertions(+), 69 deletions(-) diff --git a/data/event_bit.py b/data/event_bit.py index 9c1eec35..f2c5cc77 100644 --- a/data/event_bit.py +++ b/data/event_bit.py @@ -218,6 +218,7 @@ UNLOCKED_KT_GAUNTLET = 0x2c2 COMPLETED_KT_GAUNTLET = 0x2c3 # 8 bits 0x1e6-0x1ed Unused, as the SNES versions feature 20 rare item slots rather than 30 +GAUNTLET_IN_PROGRESS = 0x1e6 from constants.objectives import MAX_OBJECTIVES for index in range(MAX_OBJECTIVES): diff --git a/data/maps.py b/data/maps.py index 221c8b9d..46bbdf59 100644 --- a/data/maps.py +++ b/data/maps.py @@ -241,34 +241,4 @@ def write(self): npcs_ptr[0] = cur_map["npcs_ptr"] & 0xff npcs_ptr[1] = (cur_map["npcs_ptr"] & 0xff00) >> 8 self.rom.set_bytes(npcs_ptr_address, npcs_ptr) - - def add_save_point(self, map_id, x, y): - # npc for visual - npc = NPC() - npc.background_layer = 0 - npc.background_scrolls = 0 - npc.const_sprite = 1 - npc.direction = 3 - npc.event_bit = 3 - npc.event_byte = 102 - npc.map_layer = 1 - npc.movement = 0 - npc.no_face_on_trigger = 0 - npc.palette = 6 - npc.speed = 2 - npc.split_sprite = 1 - npc.sprite = 111 - npc.unknown1 = 0 - npc.unknown2 = 0 - npc.vehicle = 0 - npc.x = x - npc.y = y - - # event for save functionality - event = MapEvent() - event.set_event_address(0xc9aeb) # save script - event.x = x - event.y = y - - self.append_npc(map_id, npc) - self.add_event(map_id, event) \ No newline at end of file + \ No newline at end of file diff --git a/event/airship.py b/event/airship.py index 9d98e222..9d51836f 100644 --- a/event/airship.py +++ b/event/airship.py @@ -20,41 +20,6 @@ def mod(self): self.unequip_party_members_npc_mod() self.inside_blackjack() self.return_to_airship() - if self.args.debug: - self.chest_test_mod() - - def chest_test_mod(self): - narshe_school_right_room = 107 # MIAB in chest 0, Tincture in chest 1 - miab_chest = self.maps.get_chests(narshe_school_right_room)[0] - tincture_chest = self.maps.get_chests(narshe_school_right_room)[1] - - chest_src = [ - field.CollectTreasure(narshe_school_right_room, miab_chest.x, miab_chest.y), - ] - miab = Write(Bank.CA, chest_src, "Trigger treasure chest") - - # Terra will loot the MIAB (currently does nothing, safely exits) - new_npc = NPC() - new_npc.x = 15 - new_npc.y = 7 - new_npc.sprite = 0 - new_npc.direction = direction.DOWN - new_npc.set_event_address(miab.start_address) - self.maps.append_npc(6, new_npc) - - # Kefka will loot the Tincture - chest_src = [ - field.CollectTreasure(narshe_school_right_room, tincture_chest.x, tincture_chest.y), - ] - tincture = Write(Bank.CA, chest_src, "Trigger treasure chest") - new_npc = NPC() - new_npc.x = 17 - new_npc.y = 7 - new_npc.sprite = 21 - new_npc.direction = direction.DOWN - new_npc.set_event_address(tincture.start_address) - self.maps.append_npc(6, new_npc) - def controls_mod(self): fly_wor_fc_cancel_dialog = 1315 diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 09c37de1..f9f134b7 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -224,6 +224,7 @@ def disable_all(boss_name): debug_event_bits, + field.SetEventBit(event_bit.GAUNTLET_IN_PROGRESS), field.SetEventBit(event_bit.LEFT_WEIGHT_PUSHED_KEFKA_TOWER), field.SetEventBit(event_bit.RIGHT_WEIGHT_PUSHED_KEFKA_TOWER), field.ClearEventBit(npc_bit.LEFT_UNPUSHED_WEIGHT_KEFKA_TOWER), @@ -259,6 +260,8 @@ def disable_all(boss_name): field.Call(self.poltergeist_cutscene), "POST_GAUNTLET", field.Call(self.post_gauntlet_cutscene), + field.ClearEventBit(event_bit.GAUNTLET_IN_PROGRESS), + field.SetEventBit(event_bit.COMPLETED_KT_GAUNTLET), self.post_landing_src(final_switch_map_id, 103, 45), ] @@ -280,6 +283,7 @@ def entrance_landing_mod(self): space.add_label("ENTRANCE_LANDING", space.end_address + 1) space.write( field.BranchIfEventWordLess(event_word.CHARACTERS_AVAILABLE, 3, "NEED_MORE_ALLIES"), + field.BranchIfEventBitSet(event_bit.UNLOCKED_PERMA_KT_SKIP, "STATUE_MENU_EVAL"), field.BranchIfEventBitSet(event_bit.UNLOCKED_KT_SKIP, "STATUE_MENU_EVAL"), field.BranchIfEventBitSet(event_bit.UNLOCKED_KT_GAUNTLET, "GAUNTLET_DIALOG"), @@ -391,9 +395,23 @@ def atma_battle_mod(self): # Copy no less than 4 bytes between start_target and end_target # This will be called after one of the kt encounters has completed, but just prior to finishing the check - def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, description): - src = Read(start_target, end_target) + def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, description, trigger_fade_in = False): + + src = [] + # Guardian/Inferno remove their fade outs for two reasons: + # 1) We need to make a map load before the fade in for the gauntlet after a battle completes + # 2) We need to make room to check for objective and set proper bit + # So we re-add the fade in, but only when the gauntlet isn't happening + if trigger_fade_in: + src += [ + field.BranchIfEventBitSet(event_bit.GAUNTLET_IN_PROGRESS, "FINISH_OBJECTIVE"), + field.FadeInScreen(), + field.WaitForFade() + ] + src += Read(start_target, end_target) + src += [ + "FINISH_OBJECTIVE", field.SetEventBit(bit), field.CheckObjectives(), field.FreeScreen(), @@ -407,6 +425,9 @@ def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, d ]) def guardian_mod(self): + # Clear fade out, will manually trigger this in kt_encounter_objective_mod + # CC/186C Fade in + # CC/186D Wait for fade self.rom.set_bytes(0xc186c, [asm.NOP(), asm.NOP()]) self.kt_encounter_objective_mod( "Guardian", @@ -414,10 +435,15 @@ def guardian_mod(self): 0xc186c, 0xc186f, "Guardian battle post-script, wait for fade, set bit", + trigger_fade_in= True ) def inferno_mod(self): self.rom.set_byte(0xc18a2, 0xea) + + # Clear fade out, will manually trigger this in kt_encounter_objective_mod + # CC/18AE - Fade in + # CC/18AF - Wait for fade self.rom.set_bytes(0xc18ae, [0xea, 0xea]) self.kt_encounter_objective_mod( @@ -426,6 +452,7 @@ def inferno_mod(self): 0xc18ae, 0xc18b1, "Inferno battle post-script, fade in, wait, set bit", + trigger_fade_in = True ) def doom_mod(self): @@ -661,7 +688,6 @@ def post_landing_src(self, map_id, map_x, map_y): Read(0xa039c, 0xa039f), field.LoadMap(map_id, direction.DOWN, default_music = True, x = map_x, y = map_y, fade_in = True, entrance_event = True), - field.SetEventBit(event_bit.COMPLETED_KT_GAUNTLET), field.CheckObjectives(), field.FreeScreen(), Read(0xa03b0, 0xa03b9), From e53949b0654a2dfe98b98b5315e3051a5a4515fc Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 5 Nov 2022 17:24:23 -0400 Subject: [PATCH 61/67] cleanup events. Only issue is guardian as a slight fade in --- data/event_bit.py | 1 + event/kefka_tower.py | 99 ++++++++++++++++++++++++-------------------- 2 files changed, 54 insertions(+), 46 deletions(-) diff --git a/data/event_bit.py b/data/event_bit.py index f2c5cc77..090b3f05 100644 --- a/data/event_bit.py +++ b/data/event_bit.py @@ -206,6 +206,7 @@ DISABLE_SPRINT = 0x1c1 DISABLE_MENU_ACCESS = 0x1c2 TEMP_SONG_OVERRIDE = 0x1cc +CONTINUE_MUSIC_DURING_BATTLE = 0x2bc ENABLE_Y_PARTY_SWITCHING = 0x1ce ALWAYS_CLEAR = 0x176 # this event_bit is always clear, used for branching diff --git a/event/kefka_tower.py b/event/kefka_tower.py index f9f134b7..2e73b9a2 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -166,7 +166,7 @@ def statue_landing_mod(self): space = Reserve(0xa03ad, 0xa03af, "kefka tower the statues are up ahead", field.NOP()) - def invoke_kt_battle(self, party, original_pack_name, battle_sound = False): + def invoke_kt_battle(self, original_pack_name): boss_id = self.get_boss(original_pack_name, False) return [ field.InvokeBattle(boss_id, battle_sound = True, battle_animation = True), @@ -174,33 +174,14 @@ def invoke_kt_battle(self, party, original_pack_name, battle_sound = False): # Trigger five bosses back-to-back def boss_rush_mod(self): - # return the boss in place of the given boss_name - # e.g. bosses are shuffled, if Ultros is in Goddess spot, return Ultros - def get_replacement_formation(boss_name): - from data.bosses import pack_name - replacement = self.get_boss(boss_name, False) - location_boss = pack_name[replacement] - formation_id = self.enemies.formations.get_id(location_boss) - return self.enemies.formations.formations[formation_id] - - # If encounters are random, it could be a tell when a fight loses its music/victory dance - def disable_victory_dance(original_encounter_name): - formation = get_replacement_formation(original_encounter_name) - formation.disable_victory_dance = formation.disable_victory_dance if self.args.boss_battles_random else 1 - - def disable_battle_music(original_encounter_name): - formation = get_replacement_formation(original_encounter_name) - formation.disable_battle_music = formation.disable_battle_music if self.args.boss_battles_random else 1 - - def disable_all(boss_name): - disable_victory_dance(boss_name) - disable_battle_music(boss_name) - - # disable_all("Inferno") - # disable_all("Guardian") - # disable_all("Doom") - # disable_all("Goddess") - # disable_all("Poltrgeist") + src = [ + field.ReturnIfEventBitClear(event_bit.GAUNTLET_IN_PROGRESS), # return if we're not in gauntlet + field.StartSong(song_name_id['FierceBattle']), # subroutine will return if song is already playing, so will be called repeatedly + field.Return(), + ] + + + self.trigger_gauntlet_music = Write(Bank.F0, src, "Trigger gauntlet music").start_address self.inferno_cutscene = self.gauntlet_inferno_cutscene() self.guardian_cutscene = self.gauntlet_guardian_cutscene() @@ -224,6 +205,7 @@ def disable_all(boss_name): debug_event_bits, + field.SetEventBit(event_bit.CONTINUE_MUSIC_DURING_BATTLE), field.SetEventBit(event_bit.GAUNTLET_IN_PROGRESS), field.SetEventBit(event_bit.LEFT_WEIGHT_PUSHED_KEFKA_TOWER), field.SetEventBit(event_bit.RIGHT_WEIGHT_PUSHED_KEFKA_TOWER), @@ -260,6 +242,7 @@ def disable_all(boss_name): field.Call(self.poltergeist_cutscene), "POST_GAUNTLET", field.Call(self.post_gauntlet_cutscene), + field.ClearEventBit(event_bit.CONTINUE_MUSIC_DURING_BATTLE), field.ClearEventBit(event_bit.GAUNTLET_IN_PROGRESS), field.SetEventBit(event_bit.COMPLETED_KT_GAUNTLET), self.post_landing_src(final_switch_map_id, 103, 45), @@ -283,7 +266,6 @@ def entrance_landing_mod(self): space.add_label("ENTRANCE_LANDING", space.end_address + 1) space.write( field.BranchIfEventWordLess(event_word.CHARACTERS_AVAILABLE, 3, "NEED_MORE_ALLIES"), - field.BranchIfEventBitSet(event_bit.UNLOCKED_PERMA_KT_SKIP, "STATUE_MENU_EVAL"), field.BranchIfEventBitSet(event_bit.UNLOCKED_KT_SKIP, "STATUE_MENU_EVAL"), field.BranchIfEventBitSet(event_bit.UNLOCKED_KT_GAUNTLET, "GAUNTLET_DIALOG"), @@ -404,9 +386,8 @@ def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, d # So we re-add the fade in, but only when the gauntlet isn't happening if trigger_fade_in: src += [ - field.BranchIfEventBitSet(event_bit.GAUNTLET_IN_PROGRESS, "FINISH_OBJECTIVE"), field.FadeInScreen(), - field.WaitForFade() + field.BranchIfEventBitSet(event_bit.GAUNTLET_IN_PROGRESS, "FINISH_OBJECTIVE"), ] src += Read(start_target, end_target) @@ -417,7 +398,7 @@ def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, d field.FreeScreen(), field.Return(), ] - post_battle = Write(Bank['CC'], src, f"{boss_name} post-battle. 1) Set event bit. 2) Finish check") + post_battle = Write(Bank.F0, src, f"{boss_name} post-battle. 1) Set event bit. 2) Finish check") space = Reserve(start_target, end_target, description, asm.NOP()) space.write([ @@ -492,7 +473,7 @@ def atma_mod(self): field.FinishCheck(), field.Return(), ] - space = Write(Bank.CC, src, "kefka tower atma reward") + space = Write(Bank.F0, src, "kefka tower atma reward") atma_reward = space.start_address space = Reserve(0xc18d3, 0xc18d6, "kefka tower after atma", field.NOP()) @@ -502,11 +483,11 @@ def atma_mod(self): def inferno_battle_mod(self): boss_src = [ - field.StartSong(song_name_id['FierceBattle']), - self.invoke_kt_battle(3, 'Inferno', True), + field.Call(self.trigger_gauntlet_music), + self.invoke_kt_battle('Inferno'), field.Return(), ] - boss_space = Write(Bank.CC, boss_src, "trigger inferno fight, ") + boss_space = Write(Bank.F0, boss_src, "trigger inferno fight, ") space = Reserve(0xc18a2, 0xc18a9, "call inferno fight subroutine", asm.NOP()) space.write([ field.Call(boss_space.start_address) @@ -568,35 +549,55 @@ def poltergeist_skip_fix(self): invisible_block_npc_id = self.maps.append_npc(0x11f, invisible_block_npc) def guardian_battle_mod(self): - boss_pack_id = self.get_boss("Guardian") + boss_src = [ + field.Call(self.trigger_gauntlet_music), + self.invoke_kt_battle('Guardian'), + field.Return(), + ] + boss_space = Write(Bank.F0, boss_src, "trigger inferno fight, ") space = Reserve(0xc184a, 0xc1850, "kefka tower invoke battle guardian", field.NOP()) space.write( - field.InvokeBattle(boss_pack_id), + field.Call(boss_space.start_address), ) def doom_battle_mod(self): - boss_pack_id = self.get_boss("Doom") + boss_src = [ + field.Call(self.trigger_gauntlet_music), + self.invoke_kt_battle('Doom'), + field.Return(), + ] + boss_space = Write(Bank.F0, boss_src, "trigger inferno fight, ") space = Reserve(0xc16dc, 0xc16e2, "kefka tower invoke battle doom", field.NOP()) space.write( - field.InvokeBattle(boss_pack_id), + field.Call(boss_space.start_address), ) def goddess_battle_mod(self): - boss_pack_id = self.get_boss("Goddess") + boss_src = [ + field.Call(self.trigger_gauntlet_music), + self.invoke_kt_battle('Goddess'), + field.Return(), + ] + boss_space = Write(Bank.F0, boss_src, "trigger inferno fight, ") space = Reserve(0xc171c, 0xc1722, "kefka tower invoke battle goddess", field.NOP()) space.write( - field.InvokeBattle(boss_pack_id), + field.Call(boss_space.start_address), ) def poltergeist_battle_mod(self): - boss_pack_id = self.get_boss("Poltrgeist") + boss_src = [ + field.Call(self.trigger_gauntlet_music), + self.invoke_kt_battle('Poltrgeist'), + field.Return(), + ] + boss_space = Write(Bank.F0, boss_src, "trigger inferno fight, ") space = Reserve(0xc1755, 0xc175b, "kefka tower invoke battle poltergeist", field.NOP()) space.write( - field.InvokeBattle(boss_pack_id), + field.Call(boss_space.start_address), ) def kefka_battle_mod(self): @@ -645,7 +646,7 @@ def final_kefka_access_mod(self): field.Branch(0xc1970), ] - space = Write(Bank.CC, src, "kefka tower 3 parties on final switches check") + space = Write(Bank.F0, src, "kefka tower 3 parties on final switches check") three_switches_check = space.start_address space = Reserve(0xc193f, 0xc196f, "kefka tower final kefka access check", field.NOP()) @@ -727,6 +728,9 @@ def gauntlet_inferno_cutscene(self): field_entity.Move(direction.LEFT, 8), ), field.Call(0xc1872), # Inferno event tile address + field.EntityAct(field_entity.PARTY0, False, + field_entity.AnimateAttack(), + ), field.Return(), ] @@ -742,19 +746,22 @@ def gauntlet_guardian_cutscene(self): # Initialize party positions [ change_party(1), + field.Pause(0.25), field.EntityAct(field_entity.PARTY0, True, field_entity.SetSpeed(field_entity.Speed.NORMAL), field_entity.SetPosition(6, 15), field_entity.Turn(direction.UP), ), + change_party(2), + field.Pause(0.25), field.EntityAct(field_entity.PARTY0, True, field_entity.SetSpeed(field_entity.Speed.FAST), field_entity.SetPosition(12, 17), field_entity.Turn(direction.UP), ), - change_party(3), + field.Pause(0.25), field.EntityAct(field_entity.PARTY0, True, field_entity.SetSpeed(field_entity.Speed.SLOW), field_entity.SetPosition(18, 15), From c30c6e7d6095264664f46574ecd228d15a762e4c Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 6 Nov 2022 19:57:11 -0500 Subject: [PATCH 62/67] Fix gauntlet music and objective interactions - Added a post-inferno/guardian cutscene so a fade-in will always occur - Previously we were not fading back in to transition between inferno > guardian, and guardian > doom --- event/kefka_tower.py | 68 ++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 2e73b9a2..08d25b38 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -184,7 +184,9 @@ def boss_rush_mod(self): self.trigger_gauntlet_music = Write(Bank.F0, src, "Trigger gauntlet music").start_address self.inferno_cutscene = self.gauntlet_inferno_cutscene() + self.post_inferno_cutscene = self.gauntlet_post_inferno_cutscene() self.guardian_cutscene = self.gauntlet_guardian_cutscene() + self.post_guardian_cutscene = self.gauntlet_post_guardian_cutscene() self.doom_cutscene = self.gauntlet_doom_cutscene() self.goddess_cutscene = self.gauntlet_goddess_cutscene() self.poltergeist_cutscene = self.gauntlet_poltergeist_cutscene() @@ -228,9 +230,11 @@ def boss_rush_mod(self): src += [ field.BranchIfEventBitSet(event_bit.DEFEATED_INFERNO, "GUARDIAN"), field.Call(self.inferno_cutscene), + field.Call(self.post_inferno_cutscene), "GUARDIAN", field.BranchIfEventBitSet(event_bit.DEFEATED_GUARDIAN, "DOOM"), field.Call(self.guardian_cutscene), + field.Call(self.post_guardian_cutscene), "DOOM", field.BranchIfEventBitSet(event_bit.DEFEATED_DOOM, "GODDESS"), field.Call(self.doom_cutscene), @@ -377,18 +381,8 @@ def atma_battle_mod(self): # Copy no less than 4 bytes between start_target and end_target # This will be called after one of the kt encounters has completed, but just prior to finishing the check - def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, description, trigger_fade_in = False): - + def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, description): src = [] - # Guardian/Inferno remove their fade outs for two reasons: - # 1) We need to make a map load before the fade in for the gauntlet after a battle completes - # 2) We need to make room to check for objective and set proper bit - # So we re-add the fade in, but only when the gauntlet isn't happening - if trigger_fade_in: - src += [ - field.FadeInScreen(), - field.BranchIfEventBitSet(event_bit.GAUNTLET_IN_PROGRESS, "FINISH_OBJECTIVE"), - ] src += Read(start_target, end_target) src += [ @@ -406,34 +400,21 @@ def kt_encounter_objective_mod(self, boss_name, bit, start_target, end_target, d ]) def guardian_mod(self): - # Clear fade out, will manually trigger this in kt_encounter_objective_mod - # CC/186C Fade in - # CC/186D Wait for fade - self.rom.set_bytes(0xc186c, [asm.NOP(), asm.NOP()]) self.kt_encounter_objective_mod( "Guardian", event_bit.DEFEATED_GUARDIAN, 0xc186c, 0xc186f, "Guardian battle post-script, wait for fade, set bit", - trigger_fade_in= True ) def inferno_mod(self): - self.rom.set_byte(0xc18a2, 0xea) - - # Clear fade out, will manually trigger this in kt_encounter_objective_mod - # CC/18AE - Fade in - # CC/18AF - Wait for fade - self.rom.set_bytes(0xc18ae, [0xea, 0xea]) - self.kt_encounter_objective_mod( "Inferno", event_bit.DEFEATED_INFERNO, 0xc18ae, 0xc18b1, "Inferno battle post-script, fade in, wait, set bit", - trigger_fade_in = True ) def doom_mod(self): @@ -737,6 +718,28 @@ def gauntlet_inferno_cutscene(self): space = Write(Bank.F0, src, "Inferno Gauntlet Cutscene") return space.start_address + # Need a cutscene for getting objectives after gauntlet, otherwise can show dialog behind black screen + def gauntlet_post_inferno_cutscene(self): + src = [ + field.EntityAct(field_entity.PARTY0, False, + field_entity.SetSpriteLayer(1), + field_entity.SetSpeed(field_entity.Speed.FAST), + field_entity.Move(direction.LEFT, 1), + field_entity.MoveDiagonal(direction.LEFT, 1, direction.DOWN, 1), + field_entity.MoveDiagonal(direction.LEFT, 1, direction.DOWN, 1), + field_entity.MoveDiagonal(direction.LEFT, 1, direction.DOWN, 1), + field_entity.Move(direction.DOWN, 3), + field_entity.Move(direction.RIGHT, 2), + field_entity.Move(direction.DOWN, 3), + ), + field.Pause(0.5), + field.FadeOutScreen(4), + field.WaitForFade(), + field.Return() + ] + + space = Write(Bank.F0, src, "Post-Inferno Gauntlet Cutscene") + return space.start_address def gauntlet_guardian_cutscene(self): src = [ @@ -905,6 +908,23 @@ def gauntlet_guardian_cutscene(self): space = Write(Bank.F0, src, "Guardian Gauntlet Cutscene") return space.start_address + # Need a cutscene for getting objectives after gauntlet, otherwise can show dialog behind black screen + def gauntlet_post_guardian_cutscene(self): + src = [ + field.EntityAct(field_entity.PARTY0, False, + field_entity.Move(direction.UP, 8), + ), + field.PauseUnits(1), + field.FlashScreen(field.Flash.BLUE), + field.PlaySoundEffect(141), + field.FadeOutScreen(4), + field.WaitForFade(), + field.Return() + ] + + space = Write(Bank.F0, src, "Post-Guardian Gauntlet Cutscene") + return space.start_address + def gauntlet_doom_cutscene(self): src = [ From c33c918cc7e0f783d337ef630ca68bc8ecaf67b9 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 6 Nov 2022 20:22:30 -0500 Subject: [PATCH 63/67] Fix sprite layer on party 3 after they're done with stairs --- event/kefka_tower.py | 1 + 1 file changed, 1 insertion(+) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 08d25b38..ff85c525 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -728,6 +728,7 @@ def gauntlet_post_inferno_cutscene(self): field_entity.MoveDiagonal(direction.LEFT, 1, direction.DOWN, 1), field_entity.MoveDiagonal(direction.LEFT, 1, direction.DOWN, 1), field_entity.MoveDiagonal(direction.LEFT, 1, direction.DOWN, 1), + field_entity.SetSpriteLayer(0), field_entity.Move(direction.DOWN, 3), field_entity.Move(direction.RIGHT, 2), field_entity.Move(direction.DOWN, 3), From 0b9fcf8751e9b908be9df2313d9b3d75b6f5a946 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Thu, 10 Nov 2022 07:20:33 -0500 Subject: [PATCH 64/67] Feature: change gauntlet character inits for position bug --- event/kefka_tower.py | 99 ++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/event/kefka_tower.py b/event/kefka_tower.py index ff85c525..24361041 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -687,11 +687,24 @@ def exit_mod(self): def gauntlet_inferno_cutscene(self): src = [ - change_party(3), field.LoadMap(inferno_room_id, direction.DOWN, default_music = False, x = 27, y = 18, fade_in = False, entrance_event = True), field.HoldScreen(), + field.SetPartyMap(1, inferno_room_id), + field.SetPartyMap(2, inferno_room_id), + field.SetPartyMap(3, inferno_room_id), + change_party(1), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(0, 0), + ), + + change_party(2), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(0, 0), + ), + + change_party(3), # Move main party to east of inferno field.EntityAct(field_entity.PARTY0, True, field_entity.SetPosition(39, 18), @@ -747,51 +760,33 @@ def gauntlet_guardian_cutscene(self): field.LoadMap(guardian_room_id, direction.DOWN, default_music = False, x = 12, y = 14, fade_in = False, entrance_event = True), field.HoldScreen(), - # Initialize party positions + # Initialize sprite positions [ - change_party(1), - field.Pause(0.25), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.NORMAL), - field_entity.SetPosition(6, 15), - field_entity.Turn(direction.UP), - ), - - change_party(2), - field.Pause(0.25), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.FAST), - field_entity.SetPosition(12, 17), - field_entity.Turn(direction.UP), - ), - change_party(3), - field.Pause(0.25), - field.EntityAct(field_entity.PARTY0, True, - field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.SetPosition(18, 15), - field_entity.Turn(direction.UP), - ), # Move Guardian back 1 cell field.EntityAct(0x10, False, field_entity.SetPosition(11, 9) ), - field.EntityAct(0x13, True, + field.EntityAct(0x13, False, field_entity.SetPosition(12, 9) ), - field.EntityAct(0x16, True, + field.EntityAct(0x16, False, field_entity.SetPosition(13, 9) ), ], - field.Pause(0.25), # Camera position field.EntityAct(field_entity.CAMERA, False, field_entity.SetSpeed(field_entity.Speed.SLOW), field_entity.Move(direction.UP, 2), ), + # init party 1 position + field.SetPartyMap(1, guardian_room_id), + change_party(1), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(6, 15), + ), # Party 1 picking magitek flowers [ - change_party(1), field.EntityAct(field_entity.PARTY0, False, field_entity.SetSpeed(field_entity.Speed.NORMAL), field_entity.Move(direction.UP, 3), @@ -824,12 +819,16 @@ def gauntlet_guardian_cutscene(self): field_entity.SetPosition(0, 0) ), ], + # init party 3 position + field.SetPartyMap(3, guardian_room_id), + change_party(3), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(18, 14), + ), # Party 3 walking around [ - change_party(3), field.EntityAct(field_entity.PARTY0, False, field_entity.SetSpeed(field_entity.Speed.SLOW), - field_entity.SetPosition(18, 14), field_entity.Move(direction.UP, 2), field_entity.Move(direction.LEFT, 1), field_entity.Pause(8), @@ -855,14 +854,19 @@ def gauntlet_guardian_cutscene(self): field_entity.SetPosition(0, 0) ), ], + # field.FadeInScreen(3), + field.SetPartyMap(2, guardian_room_id), + change_party(2), + field.EntityAct(field_entity.PARTY0, True, + field_entity.SetPosition(12, 17), + ), field.FadeInScreen(3), + field.Pause(2), # Party 2, the initiator [ - change_party(2), - - field.Pause(2), # getting in position field.EntityAct(field_entity.PARTY0, True, + field_entity.SetSpeed(field_entity.Speed.FAST), field_entity.Move(direction.UP, 3), field_entity.AnimateFrontHandsUp(), field_entity.Pause(5), @@ -888,18 +892,25 @@ def gauntlet_guardian_cutscene(self): field.EntityAct(field_entity.PARTY0, False, field_entity.AnimateSurprised(), field_entity.AnimateLowJump(), - field_entity.Pause(5), + field_entity.Pause(4), field_entity.AnimateKnockedOut(), - field_entity.Pause(13), + field_entity.Pause(4), field_entity.AnimateKneeling(), - field_entity.Pause(10), - field_entity.AnimateStandingHeadDown(), field_entity.Pause(4), - field_entity.AnimateStandingFront(), + field_entity.AnimateStandingHeadDown(), field_entity.Pause(4), - field_entity.AnimatePowerStance(), - field_entity.AnimateLowJump(), - field_entity.Pause(6), + field_entity.Turn(direction.DOWN), + field_entity.Pause(12), + field_entity.AnimateFingerWagStraightUp(), + field_entity.Pause(1), + field_entity.AnimateFingerWagToSide(), + field_entity.Pause(1), + field_entity.AnimateFingerWagStraightUp(), + field_entity.Pause(1), + field_entity.AnimateFingerWagToSide(), + field_entity.Pause(1), + field_entity.AnimateFingerWagStraightUp(), + field_entity.Pause(8), field_entity.Move(direction.UP, 1), ), ], @@ -929,6 +940,7 @@ def gauntlet_post_guardian_cutscene(self): def gauntlet_doom_cutscene(self): src = [ + field.SetPartyMap(1, doom_room_id), change_party(1), field.LoadMap(doom_room_id, direction.DOWN, default_music = False, x = 64, y = 15, fade_in = False, entrance_event = True), @@ -965,6 +977,7 @@ def gauntlet_doom_cutscene(self): def gauntlet_goddess_cutscene(self): src = [ + field.SetPartyMap(3, goddess_room_id), change_party(3), field.LoadMap(goddess_room_id, direction.DOWN, default_music = False, x = 12, y = 28, fade_in = False, entrance_event = True), @@ -1003,6 +1016,7 @@ def gauntlet_poltergeist_cutscene(self): chests = self.maps.get_chests(poltergeist_room_id) chest = chests[7] src = [ + field.SetPartyMap(2, goddess_room_id), change_party(2), field.HoldScreen(), field.LoadMap(poltergeist_room_id, direction.DOWN, default_music = False, @@ -1116,6 +1130,7 @@ def gauntlet_post_battle_cutscene(self): # Party 1 init position [ + field.SetPartyMap(1, final_switch_map_id), change_party(1), Read(0xa0334, 0xa033c), field.EntityAct(field_entity.PARTY0, True, @@ -1126,6 +1141,7 @@ def gauntlet_post_battle_cutscene(self): ], # Party 2 init position [ + field.SetPartyMap(2, final_switch_map_id), Read(0xa031e, 0xa0320), field.EntityAct(field_entity.PARTY0, True, field_entity.SetPosition(party2_x, party2_y_start), @@ -1134,6 +1150,7 @@ def gauntlet_post_battle_cutscene(self): ], # Party 3 init position [ + field.SetPartyMap(3, final_switch_map_id), Read(0xa0327, 0xa032d), field.EntityAct(field_entity.PARTY0, True, field_entity.SetPosition(party3_x, party3_y_start), From 78a31ae6ee2b2d557cd01b2361b93d57426b827a Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Fri, 2 Dec 2022 21:35:15 -0500 Subject: [PATCH 65/67] fix water --- data/map_property.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/map_property.py b/data/map_property.py index 3a6855f2..f9761124 100644 --- a/data/map_property.py +++ b/data/map_property.py @@ -20,8 +20,8 @@ def read(self): self.data = self.rom.get_bytes(self.data_start, self.DATA_SIZE) self.name_index = self.data[0] - self.enable_warp = (self.data[1] & ENABLE_WARP) >> 1 - self.enable_random_encounters = (self.data[5] & ENABLE_RANDOM_ENCOUNTER) >> 7 + self.enable_warp = (self.data[1] | ENABLE_WARP) >> 1 + self.enable_random_encounters = (self.data[5] | ENABLE_RANDOM_ENCOUNTER) >> 7 self.song = self.data[28] def write(self): From e4a6bacba382532561261e77c9add7ac8e1f06a0 Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sat, 3 Dec 2022 12:19:18 -0500 Subject: [PATCH 66/67] Cleanup map_property to restart water --- data/map_property.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/data/map_property.py b/data/map_property.py index f9761124..fa7ece89 100644 --- a/data/map_property.py +++ b/data/map_property.py @@ -1,6 +1,4 @@ -ENABLE_WARP = 0x02 -ENABLE_RANDOM_ENCOUNTER = 0x80 class MapProperty: DATA_SIZE = 33 @@ -20,13 +18,16 @@ def read(self): self.data = self.rom.get_bytes(self.data_start, self.DATA_SIZE) self.name_index = self.data[0] - self.enable_warp = (self.data[1] | ENABLE_WARP) >> 1 - self.enable_random_encounters = (self.data[5] | ENABLE_RANDOM_ENCOUNTER) >> 7 + + self.enable_warp = (self.data[1] & 0x02) >> 1 + self.enable_random_encounters = (self.data[5] & 0x80) >> 7 + self.song = self.data[28] def write(self): - self.data[1] &= self.enable_warp << 1 - self.data[5] &= self.enable_random_encounters << 7 + self.data[1] |= self.enable_warp << 1 + self.data[5] |= self.enable_random_encounters << 7 + self.data[28] = self.song self.rom.set_bytes(self.data_start, self.data) From d20d1379cf066605e9a631debec60b94a6bae54b Mon Sep 17 00:00:00 2001 From: Kiel <95580337+kielbasiago@users.noreply.github.com> Date: Sun, 4 Dec 2022 18:41:04 -0500 Subject: [PATCH 67/67] actually fix map_property --- data/map_property.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/map_property.py b/data/map_property.py index fa7ece89..8f267db6 100644 --- a/data/map_property.py +++ b/data/map_property.py @@ -25,9 +25,9 @@ def read(self): self.song = self.data[28] def write(self): - self.data[1] |= self.enable_warp << 1 - self.data[5] |= self.enable_random_encounters << 7 - + self.data[1] = (self.data[1] & ~0x02) | (self.enable_warp << 1) + self.data[5] = (self.data[5] & ~0x80) | (self.enable_random_encounters << 7) + self.data[28] = self.song self.rom.set_bytes(self.data_start, self.data)