diff --git a/args/advanced_checks.py b/args/advanced_checks.py new file mode 100644 index 00000000..405a8378 --- /dev/null +++ b/args/advanced_checks.py @@ -0,0 +1,139 @@ + +def name(): + return "Forced Check Rewards" + +def parse(parser): + advanced_checks = parser.add_argument_group("Forced Check Rewards") + # check_rewards = advanced_checks.add_mutually_exclusive_group() + # advanced_checks.name = "Check Rewards" + advanced_checks.add_argument("-nfce", "--no-free-characters-espers", action = "store_true", + help = "Remove character/esper rewards from: Auction House, Collapsing House, Figaro Castle Throne, Gau's Father's House, Kohlingen Inn, Mt. Zozo, Narshe Weapon Shop, Sealed Gate, South Figaro Basement, Tzen Thief, Zone Eater") + + advanced_checks.add_argument("-firr", "--force-item-rewards", type = str, + help = "Forces list of checks to give an ITEM reward. Maximum of 12 checks.") + + advanced_checks.add_argument("-ferr", "--force-esper-rewards", type = str, + help = "Forces list of checks to give an ESPER reward. Maximum of 26 checks") + + advanced_checks.add_argument("-feirr", "--force-esper-item-rewards", type = str, + help = "Forces list of checks to give an (ESPER | ITEM) reward. Maximum of 20 checks") + + +esper_item_title = "Esper+Item Checks" +esper_title = "Esper Checks" +item_title = "Item Checks" + +def process(args): + from constants.checks import ( + AUCTION1, AUCTION2, COLLAPSING_HOUSE, FIGARO_CASTLE_THRONE, + GAUS_FATHERS_HOUSE,KOHLINGEN_CAFE, MT_ZOZO, NARSHE_WEAPON_SHOP, + SEALED_GATE, SOUTH_FIGARO_PRISONER, TZEN_THIEF, ZONE_EATER + ) + args.esper_item_rewards = [] + args.esper_rewards = [] + args.item_rewards = [] + + if args.force_esper_item_rewards: + args.esper_item_rewards = [int(check) for check in args.force_esper_item_rewards.split(',')] + + if args.force_esper_rewards: + args.esper_rewards = [int(check) for check in args.force_esper_rewards.split(',')] + + if args.force_item_rewards: + args.item_rewards = [int(check) for check in args.force_item_rewards.split(',')] + elif args.no_free_characters_espers: + args.item_rewards = [ + AUCTION1.bit, + AUCTION2.bit, + COLLAPSING_HOUSE.bit, + FIGARO_CASTLE_THRONE.bit, + GAUS_FATHERS_HOUSE.bit, + KOHLINGEN_CAFE.bit, + MT_ZOZO.bit, + NARSHE_WEAPON_SHOP.bit, + SEALED_GATE.bit, + SOUTH_FIGARO_PRISONER.bit, + TZEN_THIEF.bit, + ZONE_EATER.bit, + ] + + # assert that no items in item_reward_checks is CHAR | ESPER only reward + assert len(args.item_rewards) < 13 + +def flags(args): + flags = "" + + if args.force_esper_item_rewards: + flags += f" -feir {args.force_esper_item_rewards}" + + if args.force_esper_rewards: + flags += f" -fer {args.force_esper_rewards}" + + if args.force_item_rewards: + flags += f" -fir {args.force_item_rewards}" + + + return flags + +def options(args): + options = [] + if args.esper_item_rewards: + options.append((esper_item_title, args.esper_item_rewards)) + if args.esper_rewards: + options.append((esper_title, args.esper_rewards)) + if args.item_rewards: + options.append((item_title, args.item_rewards)) + return options + +def _format_check_log_entries(check_ids): + from constants.checks import check_name + check_entries = [] + for check_id in check_ids: + check_entries.append(("", check_name[check_id])) + return check_entries + +def menu(args): + from menus.submenu_force_item_reward_checks import FlagsForceEsperItemRewardChecks, FlagsForceEsperRewardChecks, FlagsForceItemRewardChecks + + entries = options(args) + for index, entry in enumerate(entries): + key, value = entry + if key == esper_item_title: + if value: + entries[index] = (esper_item_title, FlagsForceEsperItemRewardChecks(esper_item_title, value, False)) # flags sub-menu + else: + entries[index] = (esper_item_title, "None") + + if key == esper_title: + if value: + entries[index] = (esper_title, FlagsForceEsperRewardChecks(esper_title, value, False)) # flags sub-menu + else: + entries[index] = (esper_title, "None") + + if key == item_title: + if value: + entries[index] = (item_title, FlagsForceItemRewardChecks(item_title, value, args.no_free_characters_espers)) # flags sub-menu + else: + entries[index] = (item_title, "None") + + return (name(), entries) + +def log(args): + from log import format_option + log = [name()] + + entries = options(args) + for entry in entries: + key, value = entry + if key == esper_item_title or key == esper_title or key == item_title: + if len(value) == 0: + entry = (key, "None") + else: + entry = (key, "") # The entries will show up on subsequent lines + log.append(format_option(*entry)) + for check_entry in _format_check_log_entries(value): + log.append(format_option(*check_entry)) + else: + log.append(format_option(*entry)) + + return log diff --git a/args/arguments.py b/args/arguments.py index 0f78b26c..9e126d22 100644 --- a/args/arguments.py +++ b/args/arguments.py @@ -10,6 +10,7 @@ def __init__(self): "starting_gold_items", "items", "shops", "chests", "graphics", "coliseum", "auction_house", "challenges", "bug_fixes", "misc", + "advanced_checks", ] self.group_modules = {} for group in self.groups: diff --git a/args/challenges.py b/args/challenges.py index 43629683..fa1e8f22 100644 --- a/args/challenges.py +++ b/args/challenges.py @@ -1,3 +1,6 @@ +from constants.checks import FIGARO_CASTLE_THRONE, KOHLINGEN_CAFE +from data.npc_bit import WEAPON_ELDER_NARSHE + def name(): return "Challenges" @@ -18,8 +21,6 @@ def parse(parser): help = "Ultima costs 254 MP") challenges.add_argument("-nfps", "--no-free-paladin-shields", action = "store_true", help = "Paladin/Cursed Shields will not appear in coliseum/auction/shops/chests/events (Narshe WOR exclusive)") - challenges.add_argument("-nfce", "--no-free-characters-espers", action = "store_true", - help = "Remove character/esper rewards from: Auction House, Collapsing House, Figaro Castle Throne, Gau's Father's House, Kohlingen Inn, Narshe Weapon Shop, Sealed Gate, South Figaro Basement") challenges.add_argument("-pd", "--permadeath", action = "store_true", help = "Life spells cannot be learned. Fenix Downs unavailable (except from starting items). Buckets/inns/tents/events do not revive characters. Phoenix casts Life 3 on party instead of Life") challenges.add_argument("-np", "--no-peeking", action = "store_true", @@ -82,8 +83,6 @@ def flags(args): if args.no_free_paladin_shields: flags += " -nfps" - if args.no_free_characters_espers: - flags += " -nfce" if args.permadeath: flags += " -pd" if args.no_peeking: @@ -107,7 +106,6 @@ def options(args): ("No Sprint Shoes", args.no_sprint_shoes), ("Ultima", ultima), ("No Free Paladin Shields", args.no_free_paladin_shields), - ("No Free Characters/Espers", args.no_free_characters_espers), ("Permadeath", args.permadeath), ("No Peeking", args.no_peeking), ("Remove Learnable Spells", args.remove_learnable_spell_ids), diff --git a/args/log.py b/args/log.py index 504be767..e74ddd58 100644 --- a/args/log.py +++ b/args/log.py @@ -22,4 +22,5 @@ def log(): _log_tab("Magic", ["espers", "misc_magic"], ["natural_magic"]) _log_tab("Items", ["starting_gold_items", "items"], ["shops", "chests"]) args.group_modules["graphics"].log(args) - _log_tab("Other", ["coliseum", "auction_house", "misc"], ["challenges", "bug_fixes"]) + _log_tab("Other", ["coliseum", "misc", "challenges"], ["auction_house", "bug_fixes"]) + _log_tab("Advanced", ["advanced_checks"], []) diff --git a/constants/checks.py b/constants/checks.py new file mode 100644 index 00000000..c6f5cccc --- /dev/null +++ b/constants/checks.py @@ -0,0 +1,246 @@ +from collections import namedtuple +import data.event_bit as event_bit +import data.npc_bit as npc_bit +from event.event_reward import RewardType + +NameBit = namedtuple("NameBit", ["name", "bit", "reward_types"]) +CHAR_ESPER_REWARD = RewardType.CHARACTER | RewardType.ESPER +ANY_REWARD = RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM +ESPER_ITEM_REWARD = RewardType.ESPER | RewardType.ITEM +ITEM_REWARD = RewardType.ITEM + +AUCTION1 = NameBit("Auction1", event_bit.AUCTION_BOUGHT_ESPER1, ESPER_ITEM_REWARD) +AUCTION2 = NameBit("Auction2", event_bit.AUCTION_BOUGHT_ESPER2, ESPER_ITEM_REWARD) +ANCIENT_CASTLE = NameBit("Ancient Castle", event_bit.GOT_RAIDEN, ANY_REWARD) +BAREN_FALLS = NameBit("Baren Falls", event_bit.NAMED_GAU, ANY_REWARD) +BURNING_HOUSE = NameBit("Burning House", event_bit.DEFEATED_FLAME_EATER, ANY_REWARD) +COLLAPSING_HOUSE = NameBit("Collapsing House", event_bit.FINISHED_COLLAPSING_HOUSE, ANY_REWARD) +DARYLS_TOMB = NameBit("Daryl's Tomb", event_bit.DEFEATED_DULLAHAN, ANY_REWARD) +DOMA_SIEGE = NameBit("Doma Siege", event_bit.FINISHED_DOMA_WOB, ANY_REWARD) +DOMA_DREAM_DOOR = NameBit("Doma Dream Door", event_bit.DEFEATED_STOOGES, ESPER_ITEM_REWARD) +DOMA_DREAM_AWAKEN = NameBit("Doma Dream Awaken", event_bit.FINISHED_DOMA_WOR, CHAR_ESPER_REWARD) +DOMA_DREAM_THRONE = NameBit("Doma Dream Throne", event_bit.GOT_ALEXANDR, ESPER_ITEM_REWARD) +EBOTS_ROCK = NameBit("Ebot's Rock", event_bit.DEFEATED_HIDON, ANY_REWARD) +ESPER_MOUNTAIN = NameBit("Esper Mountain", event_bit.DEFEATED_ULTROS_ESPER_MOUNTAIN, ANY_REWARD) +FANATICS_TOWER_LEADER = NameBit("Fanatic's Tower Leader", event_bit.DEFEATED_MAGIMASTER, ESPER_ITEM_REWARD) +FANATICS_TOWER_FOLLOWER = NameBit("Fanatic's Tower Follower", event_bit.RECRUITED_STRAGO_FANATICS_TOWER, CHAR_ESPER_REWARD) +FIGARO_CASTLE_THRONE = NameBit("Figaro Castle Throne", event_bit.NAMED_EDGAR, ANY_REWARD) +FIGARO_CASTLE_ENGINE = NameBit("Figaro Castle Engine", event_bit.DEFEATED_TENTACLES_FIGARO, ANY_REWARD) +FLOATING_CONT_ARRIVE = NameBit("Floating Cont. Arrive", event_bit.RECRUITED_SHADOW_FLOATING_CONTINENT, CHAR_ESPER_REWARD) +FLOATING_CONT_BEAST = NameBit("Floating Cont. Beast", event_bit.DEFEATED_ATMAWEAPON, ESPER_ITEM_REWARD) +FLOATING_CONT_ESCAPE = NameBit("Floating Cont. Escape", event_bit.FINISHED_FLOATING_CONTINENT, CHAR_ESPER_REWARD) +GAUS_FATHERS_HOUSE = NameBit("Gau's Father's House", event_bit.RECRUITED_SHADOW_GAU_FATHER_HOUSE, ANY_REWARD) +IMPERIAL_CAMP = NameBit("Imperial Camp", event_bit.FINISHED_IMPERIAL_CAMP, ANY_REWARD) +KEFKAS_TOWER_CELL_BEAST = NameBit("Kefka's Tower Cell Beast", event_bit.DEFEATED_ATMA, ITEM_REWARD) +KOHLINGEN_CAFE = NameBit("Kohlingen Cafe", event_bit.RECRUITED_SHADOW_KOHLINGEN, ANY_REWARD) +LETE_RIVER = NameBit("Lete River", event_bit.RODE_RAFT_LETE_RIVER, ANY_REWARD) +LONE_WOLF_CHASE = NameBit("Lone Wolf Chase", event_bit.CHASING_LONE_WOLF7, ANY_REWARD) +LONE_WOLF_MOOGLE_ROOM = NameBit("Lone Wolf Moogle Room", event_bit.GOT_BOTH_REWARDS_LONE_WOLF, ESPER_ITEM_REWARD) +MAGITEK_FACTORY_TRASH = NameBit("Magitek Factory Trash", event_bit.GOT_IFRIT_SHIVA, ESPER_ITEM_REWARD) +MAGITEK_FACTORY_GUARD = NameBit("Magitek Factory Guard", event_bit.DEFEATED_NUMBER_024, ESPER_ITEM_REWARD) +MAGITEK_FACTORY_FINISH = NameBit("Magitek Factory Finish", event_bit.DEFEATED_CRANES, CHAR_ESPER_REWARD) +MOBLIZ_ATTACK = NameBit("Mobliz Attack", event_bit.RECRUITED_TERRA_MOBLIZ, ANY_REWARD) +MT_KOLTS = NameBit("Mt. Kolts", event_bit.DEFEATED_VARGAS, ANY_REWARD) +MT_ZOZO = NameBit("Mt. Zozo", event_bit.FINISHED_MT_ZOZO, ANY_REWARD) +NARSHE_BATTLE = NameBit("Narshe Battle", event_bit.FINISHED_NARSHE_BATTLE, ANY_REWARD) +NARSHE_WEAPON_SHOP = NameBit("Narshe Weapon Shop", event_bit.GOT_RAGNAROK, ESPER_ITEM_REWARD) +NARSHE_WEAPON_SHOP_MINES = NameBit("Narshe Weapon Shop Mines", event_bit.GOT_BOTH_REWARDS_WEAPON_SHOP, ESPER_ITEM_REWARD) +OPERA_HOUSE_DISRUPTION = NameBit("Opera House Disruption", event_bit.FINISHED_OPERA_DISRUPTION, ANY_REWARD) +OWZERS_MANSION = NameBit("Owzer's Mansion", event_bit.DEFEATED_CHADARNOOK, ANY_REWARD) +PHANTOM_TRAIN = NameBit("Phantom Train", event_bit.GOT_PHANTOM_TRAIN_REWARD, ANY_REWARD) +PHOENIX_CAVE = NameBit("Phoenix Cave", event_bit.RECRUITED_LOCKE_PHOENIX_CAVE, ANY_REWARD) +SEALED_GATE = NameBit("Sealed Gate", npc_bit.BLOCK_SEALED_GATE, ANY_REWARD) +SEARCH_THE_SKIES = NameBit("Search The Skies", event_bit.DEFEATED_DOOM_GAZE, ESPER_ITEM_REWARD) +SERPENT_TRENCH = NameBit("Serpent Trench", event_bit.GOT_SERPENT_TRENCH_REWARD, ANY_REWARD) +SOUTH_FIGARO_PRISONER = NameBit("South Figaro Prisoner", event_bit.FREED_CELES, ANY_REWARD) +SOUTH_FIGARO_CAVE = NameBit("South Figaro Cave", event_bit.DEFEATED_TUNNEL_ARMOR, ANY_REWARD) +TRITOCH_CLIFF = NameBit("Tritoch Cliff", event_bit.GOT_TRITOCH, ESPER_ITEM_REWARD) +TZEN_THIEF = NameBit("Tzen Thief", event_bit.BOUGHT_ESPER_TZEN, ESPER_ITEM_REWARD) +UMAROS_CAVE = NameBit("Umaro's Cave", event_bit.RECRUITED_UMARO_WOR, ANY_REWARD) +VELDT = NameBit("Veldt", event_bit.VELDT_REWARD_OBTAINED, ESPER_ITEM_REWARD) +VELDT_CAVE = NameBit("Veldt Cave", event_bit.DEFEATED_SR_BEHEMOTH, ANY_REWARD) +WHELK_GATE = NameBit("Whelk Gate", event_bit.DEFEATED_WHELK, ANY_REWARD) +ZONE_EATER = NameBit("Zone Eater", event_bit.RECRUITED_GOGO_WOR, ANY_REWARD) +ZOZO_TOWER = NameBit("Zozo Tower", event_bit.GOT_ZOZO_REWARD, ANY_REWARD) + +ANCIENT_CASTLE_DRAGON = NameBit("Ancient Castle Dragon", event_bit.DEFEATED_ANCIENT_CASTLE_DRAGON, ITEM_REWARD) +FANATICS_TOWER_DRAGON = NameBit("Fanatic's Tower Dragon", event_bit.DEFEATED_FANATICS_TOWER_DRAGON, ITEM_REWARD) +KEFKAS_TOWER_DRAGON_G = NameBit("Kefka's Tower Dragon G", event_bit.DEFEATED_KEFKA_TOWER_DRAGON_G, ITEM_REWARD) +KEFKAS_TOWER_DRAGON_S = NameBit("Kefka's Tower Dragon S", event_bit.DEFEATED_KEFKA_TOWER_DRAGON_S, ITEM_REWARD) +MT_ZOZO_DRAGON = NameBit("Mt. Zozo Dragon", event_bit.DEFEATED_MT_ZOZO_DRAGON, ITEM_REWARD) +NARSHE_DRAGON = NameBit("Narshe Dragon", event_bit.DEFEATED_NARSHE_DRAGON, ITEM_REWARD) +OPERA_HOUSE_DRAGON = NameBit("Opera House Dragon", event_bit.DEFEATED_OPERA_HOUSE_DRAGON, ITEM_REWARD) +PHOENIX_CAVE_DRAGON = NameBit("Phoenix Cave Dragon", event_bit.DEFEATED_PHOENIX_CAVE_DRAGON, ITEM_REWARD) + +# Checks +all_checks = [ + AUCTION1, + AUCTION2, + ANCIENT_CASTLE, + BAREN_FALLS, + BURNING_HOUSE, + COLLAPSING_HOUSE, + DARYLS_TOMB, + DOMA_SIEGE, + DOMA_DREAM_DOOR, + DOMA_DREAM_AWAKEN, + DOMA_DREAM_THRONE, + EBOTS_ROCK, + ESPER_MOUNTAIN, + FANATICS_TOWER_FOLLOWER, + FANATICS_TOWER_LEADER, + FIGARO_CASTLE_THRONE, + FIGARO_CASTLE_ENGINE, + FLOATING_CONT_ARRIVE, + FLOATING_CONT_BEAST, + FLOATING_CONT_ESCAPE, + GAUS_FATHERS_HOUSE, + IMPERIAL_CAMP, + KOHLINGEN_CAFE, + LETE_RIVER, + LONE_WOLF_CHASE, + LONE_WOLF_MOOGLE_ROOM, + MAGITEK_FACTORY_TRASH, + MAGITEK_FACTORY_GUARD, + MAGITEK_FACTORY_FINISH, + MOBLIZ_ATTACK, + MT_KOLTS, + MT_ZOZO, + NARSHE_BATTLE, + NARSHE_WEAPON_SHOP, + NARSHE_WEAPON_SHOP_MINES, + OPERA_HOUSE_DISRUPTION, + OWZERS_MANSION, + PHANTOM_TRAIN, + PHOENIX_CAVE, + SEALED_GATE, + SEARCH_THE_SKIES, + SERPENT_TRENCH, + SOUTH_FIGARO_PRISONER, + SOUTH_FIGARO_CAVE, + TRITOCH_CLIFF, + TZEN_THIEF, + UMAROS_CAVE, + VELDT, + VELDT_CAVE, + WHELK_GATE, + ZONE_EATER, + ZOZO_TOWER, + + # Dragons + ANCIENT_CASTLE_DRAGON, + FANATICS_TOWER_DRAGON, + KEFKAS_TOWER_DRAGON_G, + KEFKAS_TOWER_DRAGON_S, + MT_ZOZO_DRAGON, + NARSHE_DRAGON, + OPERA_HOUSE_DRAGON, + PHOENIX_CAVE_DRAGON, + + # KT + KEFKAS_TOWER_CELL_BEAST, +] + +check_name = {check.bit: check.name for (idx, check) in enumerate(all_checks)} +name_check = {check.name: check.bit for (idx, check) in enumerate(all_checks)} +check_reward = {check.bit: check.reward_types for (idx, check) in enumerate(all_checks)} + +CELES = [ + MAGITEK_FACTORY_TRASH, + MAGITEK_FACTORY_GUARD, + MAGITEK_FACTORY_FINISH, + OPERA_HOUSE_DISRUPTION, + SOUTH_FIGARO_PRISONER, +] +CYAN = [ + DOMA_SIEGE, + DOMA_DREAM_DOOR, + DOMA_DREAM_AWAKEN, + DOMA_DREAM_THRONE, + MT_ZOZO, +] +EDGAR = [ + ANCIENT_CASTLE, + FIGARO_CASTLE_THRONE, + FIGARO_CASTLE_ENGINE, +] +GAU = [ + VELDT, + SERPENT_TRENCH +] +GOGO = [ + ZONE_EATER +] +LOCKE = [ + NARSHE_WEAPON_SHOP, + NARSHE_WEAPON_SHOP_MINES, + SOUTH_FIGARO_CAVE, + PHOENIX_CAVE, +] +MOG = [ + LONE_WOLF_CHASE, + LONE_WOLF_MOOGLE_ROOM, +] +RELM = [ + ESPER_MOUNTAIN, + OWZERS_MANSION, +] +SABIN = [ + BAREN_FALLS, + COLLAPSING_HOUSE, + IMPERIAL_CAMP, + MT_KOLTS, + PHANTOM_TRAIN, +] +SETZER = [ + DARYLS_TOMB, + KOHLINGEN_CAFE, + SEARCH_THE_SKIES, +] +SHADOW = [ + GAUS_FATHERS_HOUSE, + FLOATING_CONT_ARRIVE, + FLOATING_CONT_BEAST, + FLOATING_CONT_ESCAPE, + VELDT_CAVE, +] +STRAGO = [ + BURNING_HOUSE, + EBOTS_ROCK, + FANATICS_TOWER_FOLLOWER, + FANATICS_TOWER_LEADER, # allow getting the chest at top without getting strago +] +TERRA = [ + LETE_RIVER, + MOBLIZ_ATTACK, + SEALED_GATE, + WHELK_GATE, + ZOZO_TOWER, +] +UMARO = [ + UMAROS_CAVE +] + +UNGATED = [ + AUCTION1, + AUCTION2, + TZEN_THIEF, + + ## Ungated Boss battled + NARSHE_BATTLE, + TRITOCH_CLIFF, + + # KT + KEFKAS_TOWER_CELL_BEAST, +] +# Dragons +DRAGONS = [ + ANCIENT_CASTLE_DRAGON, + FANATICS_TOWER_DRAGON, + KEFKAS_TOWER_DRAGON_G, + KEFKAS_TOWER_DRAGON_S, + MT_ZOZO_DRAGON, + NARSHE_DRAGON, + OPERA_HOUSE_DRAGON, + PHOENIX_CAVE_DRAGON, +] \ No newline at end of file diff --git a/constants/gates.py b/constants/gates.py index 266b975c..b2cd61cc 100644 --- a/constants/gates.py +++ b/constants/gates.py @@ -1,94 +1,25 @@ -character_checks = { - "Celes" : [ - "Magitek Factory Trash", - "Magitek Factory Guard", - "Magitek Factory Finish", - "Opera House Disruption", - "South Figaro Prisoner", - ], - "Cyan" : [ - "Doma Siege", - "Doma Dream Door", - "Doma Dream Awaken", - "Doma Dream Throne", - "Mt. Zozo", - "Mt. Zozo Dragon", - ], - "Edgar" : [ - "Ancient Castle", - "Ancient Castle Dragon", - "Figaro Castle Throne", - "Figaro Castle Engine", - ], - "Gau" : [ - "Serpent Trench", - "Veldt", - ], - "Gogo" : [ - "Zone Eater", - ], - "Locke" : [ - "Narshe Weapon Shop", - "Narshe Weapon Shop Mines", - "Phoenix Cave", - "South Figaro Cave", - ], - "Mog" : [ - "Lone Wolf Chase", - "Lone Wolf Moogle Room", - ], - "Relm" : [ - "Esper Mountain", - "Owzer's Mansion", - ], - "Sabin" : [ - "Baren Falls", - "Collapsing House", - "Imperial Camp", - "Mt. Kolts", - "Phantom Train", - ], - "Setzer" : [ - "Daryl's Tomb", - "Kohlingen Cafe", - "Search The Skies", - ], - "Shadow" : [ - "Floating Cont. Arrive", - "Floating Cont. Beast", - "Floating Cont. Escape", - "Gau's Father's House", - "Veldt Cave", - ], - "Strago" : [ - "Burning House", - "Ebot's Rock", - "Fanatic's Tower Follower", - ], - "Terra" : [ - "Lete River", - "Mobliz Attack", - "Sealed Gate", - "Whelk Gate", - "Zozo Tower", - ], - "Umaro" : [ - "Umaro's Cave", - ], +import constants.checks as c +from constants.checks import ( + CELES, CYAN, DRAGONS, EDGAR, GAU, + GOGO, LOCKE, MOG, RELM, + SABIN, SETZER, STRAGO, SHADOW, + TERRA, UMARO, UNGATED +) - "" : [ - "Auction1", - "Auction2", - "Fanatic's Tower Dragon", - "Fanatic's Tower Leader", - "Kefka's Tower Cell Beast", - "Kefka's Tower Dragon G", - "Kefka's Tower Dragon S", - "Narshe Battle", - "Narshe Dragon", - "Opera House Dragon", - "Phoenix Cave Dragon", - "Tritoch Cliff", - "Tzen Thief", - ], +character_checks = { + "Celes" : [c.name for c in CELES], + "Cyan" : [c.name for c in CYAN], + "Edgar" : [c.name for c in EDGAR], + "Gau" : [c.name for c in GAU], + "Gogo" : [c.name for c in GOGO], + "Locke" : [c.name for c in LOCKE], + "Mog" : [c.name for c in MOG], + "Relm" : [c.name for c in RELM], + "Sabin" : [c.name for c in SABIN], + "Setzer" : [c.name for c in SETZER], + "Shadow" : [c.name for c in SHADOW], + "Strago" : [c.name for c in STRAGO], + "Terra" : [c.name for c in TERRA], + "Umaro" : [c.name for c in UMARO], + "" : [c.name for c in DRAGONS] + [c.name for c in UNGATED], } diff --git a/constants/objectives/condition_bits.py b/constants/objectives/condition_bits.py index ec90ef74..73f36417 100644 --- a/constants/objectives/condition_bits.py +++ b/constants/objectives/condition_bits.py @@ -1,71 +1,72 @@ -import data.event_bit as event_bit -import data.npc_bit as npc_bit import data.battle_bit as battle_bit +import data.event_bit as event_bit +import constants.checks as checks +import constants.quests as quests from data.bosses import normal_formation_name, dragon_formation_name from collections import namedtuple NameBit = namedtuple("NameBit", ["name", "bit"]) check_bit = [ - NameBit("Ancient Castle", event_bit.GOT_RAIDEN), - NameBit("Ancient Castle Dragon", event_bit.DEFEATED_ANCIENT_CASTLE_DRAGON), - NameBit("Baren Falls", event_bit.NAMED_GAU), - NameBit("Burning House", event_bit.DEFEATED_FLAME_EATER), - NameBit("Collapsing House", event_bit.FINISHED_COLLAPSING_HOUSE), - NameBit("Daryl's Tomb", event_bit.DEFEATED_DULLAHAN), - NameBit("Doma Siege", event_bit.FINISHED_DOMA_WOB), - NameBit("Doma Dream Door", event_bit.DEFEATED_STOOGES), - NameBit("Doma Dream Awaken", event_bit.FINISHED_DOMA_WOR), - NameBit("Doma Dream Throne", event_bit.GOT_ALEXANDR), - NameBit("Ebot's Rock", event_bit.DEFEATED_HIDON), - NameBit("Esper Mountain", event_bit.DEFEATED_ULTROS_ESPER_MOUNTAIN), - NameBit("Fanatic's Tower Dragon", event_bit.DEFEATED_FANATICS_TOWER_DRAGON), - NameBit("Fanatic's Tower Leader", event_bit.DEFEATED_MAGIMASTER), - NameBit("Fanatic's Tower Follower", event_bit.RECRUITED_STRAGO_FANATICS_TOWER), - NameBit("Figaro Castle Throne", event_bit.NAMED_EDGAR), - NameBit("Figaro Castle Engine", event_bit.DEFEATED_TENTACLES_FIGARO), - NameBit("Floating Cont. Arrive", event_bit.RECRUITED_SHADOW_FLOATING_CONTINENT), - NameBit("Floating Cont. Beast", event_bit.DEFEATED_ATMAWEAPON), - 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 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("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), - NameBit("Lone Wolf Moogle Room", event_bit.GOT_BOTH_REWARDS_LONE_WOLF), - NameBit("Magitek Factory Trash", event_bit.GOT_IFRIT_SHIVA), - NameBit("Magitek Factory Guard", event_bit.DEFEATED_NUMBER_024), - NameBit("Magitek Factory Finish", event_bit.DEFEATED_CRANES), - NameBit("Mobliz Attack", event_bit.RECRUITED_TERRA_MOBLIZ), - NameBit("Mt. Kolts", event_bit.DEFEATED_VARGAS), - NameBit("Mt. Zozo", event_bit.FINISHED_MT_ZOZO), - NameBit("Mt. Zozo Dragon", event_bit.DEFEATED_MT_ZOZO_DRAGON), - NameBit("Narshe Battle", event_bit.FINISHED_NARSHE_BATTLE), - NameBit("Narshe Dragon", event_bit.DEFEATED_NARSHE_DRAGON), - NameBit("Narshe Weapon Shop", event_bit.GOT_RAGNAROK), - NameBit("Narshe Weapon Shop Mines", event_bit.GOT_BOTH_REWARDS_WEAPON_SHOP), - NameBit("Opera House Disruption", event_bit.FINISHED_OPERA_DISRUPTION), - NameBit("Opera House Dragon", event_bit.DEFEATED_OPERA_HOUSE_DRAGON), - NameBit("Owzer's Mansion", event_bit.DEFEATED_CHADARNOOK), - NameBit("Phantom Train", event_bit.GOT_PHANTOM_TRAIN_REWARD), - NameBit("Phoenix Cave", event_bit.RECRUITED_LOCKE_PHOENIX_CAVE), - NameBit("Phoenix Cave Dragon", event_bit.DEFEATED_PHOENIX_CAVE_DRAGON), - NameBit("Sealed Gate", npc_bit.BLOCK_SEALED_GATE), - NameBit("Search The Skies", event_bit.DEFEATED_DOOM_GAZE), - NameBit("Serpent Trench", event_bit.GOT_SERPENT_TRENCH_REWARD), - NameBit("South Figaro Prisoner", event_bit.FREED_CELES), - NameBit("South Figaro Cave", event_bit.DEFEATED_TUNNEL_ARMOR), - NameBit("Tritoch Cliff", event_bit.GOT_TRITOCH), - NameBit("Tzen Thief", event_bit.BOUGHT_ESPER_TZEN), - NameBit("Umaro's Cave", event_bit.RECRUITED_UMARO_WOR), - NameBit("Veldt", event_bit.VELDT_REWARD_OBTAINED), - NameBit("Veldt Cave", event_bit.DEFEATED_SR_BEHEMOTH), - NameBit("Whelk Gate", event_bit.DEFEATED_WHELK), - NameBit("Zone Eater", event_bit.RECRUITED_GOGO_WOR), - NameBit("Zozo Tower", event_bit.GOT_ZOZO_REWARD), + checks.ANCIENT_CASTLE, + checks.ANCIENT_CASTLE_DRAGON, + checks.BAREN_FALLS, + checks.BURNING_HOUSE, + checks.COLLAPSING_HOUSE, + checks.DARYLS_TOMB, + checks.DOMA_SIEGE, + checks.DOMA_DREAM_DOOR, + checks.DOMA_DREAM_AWAKEN, + checks.DOMA_DREAM_THRONE, + checks.EBOTS_ROCK, + checks.ESPER_MOUNTAIN, + checks.FANATICS_TOWER_DRAGON, + checks.FANATICS_TOWER_LEADER, + checks.FANATICS_TOWER_FOLLOWER, + checks.FIGARO_CASTLE_THRONE, + checks.FIGARO_CASTLE_ENGINE, + checks.FLOATING_CONT_ARRIVE, + checks.FLOATING_CONT_BEAST, + checks.FLOATING_CONT_ESCAPE, + checks.GAUS_FATHERS_HOUSE, + checks.IMPERIAL_CAMP, + checks.KEFKAS_TOWER_CELL_BEAST, + checks.KEFKAS_TOWER_DRAGON_G, + checks.KEFKAS_TOWER_DRAGON_S, + checks.KOHLINGEN_CAFE, + checks.LETE_RIVER, + checks.LONE_WOLF_CHASE, + checks.LONE_WOLF_MOOGLE_ROOM, + checks.MAGITEK_FACTORY_TRASH, + checks.MAGITEK_FACTORY_GUARD, + checks.MAGITEK_FACTORY_FINISH, + checks.MOBLIZ_ATTACK, + checks.MT_KOLTS, + checks.MT_ZOZO, + checks.MT_ZOZO_DRAGON, + checks.NARSHE_BATTLE, + checks.NARSHE_DRAGON, + checks.NARSHE_WEAPON_SHOP, + checks.NARSHE_WEAPON_SHOP_MINES, + checks.OPERA_HOUSE_DISRUPTION, + checks.OPERA_HOUSE_DRAGON, + checks.OWZERS_MANSION, + checks.PHANTOM_TRAIN, + checks.PHOENIX_CAVE, + checks.PHOENIX_CAVE_DRAGON, + checks.SEALED_GATE, + checks.SEARCH_THE_SKIES, + checks.SERPENT_TRENCH, + checks.SOUTH_FIGARO_PRISONER, + checks.SOUTH_FIGARO_CAVE, + checks.TRITOCH_CLIFF, + checks.TZEN_THIEF, + checks.UMAROS_CAVE, + checks.VELDT, + checks.VELDT_CAVE, + checks.WHELK_GATE, + checks.ZONE_EATER, + checks.ZOZO_TOWER, ] check_bit += [ # Index @@ -77,17 +78,17 @@ ] quest_bit = [ - NameBit("Defeat Sealed Cave Ninja", event_bit.DEFEATED_NINJA_CAVE_TO_SEALED_GATE), - NameBit("Help Injured Lad", event_bit.HELPED_INJURED_LAD), - NameBit("Let Cid Die", event_bit.CID_DIED), - NameBit("Pass Security Checkpoint", event_bit.FINISHED_NARSHE_CHECKPOINT), - NameBit("Perform In Opera", event_bit.FINISHED_OPERA_PERFORMANCE), - NameBit("Save Cid", event_bit.CID_SURVIVED), - NameBit("Set Zozo Clock", event_bit.SET_ZOZO_CLOCK), - 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("Reunite Gau and Father", event_bit.GAU_FATHER_REUNION), # 10 + quests.DEFEAT_SEALED_CAVE_NINJA, + quests.HELP_INJURED_LAD, + quests.LET_CID_DIE, + quests.PASS_SECURITY_CHECKPOINT, + quests.PERFORM_IN_OPERA, + quests.SAVE_CID, + quests.SET_ZOZO_CLOCK, + quests.SUPLEX_A_TRAIN, + quests.WIN_AN_AUCTION, + quests.WIN_A_COLISEUM_MATCH, + quests.REUNITE_GAU_AND_DAD, NameBit("Complete the KT Gauntlet", event_bit.COMPLETED_KT_GAUNTLET) # 11 ] diff --git a/constants/objectives/results.py b/constants/objectives/results.py index 6c0d0336..b7d72608 100644 --- a/constants/objectives/results.py +++ b/constants/objectives/results.py @@ -58,7 +58,6 @@ ResultType(36, "Dragoon", "Dragoon", None), ResultType(37, "Dried Meat", "Dried Meat", None), ResultType(38, "Exp. Egg", "Exp. Egg", None), - ResultType(58, "High Tier Item", "High Tier Item", None), ResultType(40, "Illumina", "Illumina", None), ResultType(39, "Imp Set", "Imp Set", None), ResultType(41, "Rename Cards", "Rename Cards", None), @@ -86,6 +85,7 @@ } #Additional results +category_types["Item"].append(ResultType(58, "High Tier Item", "High Tier Item", None)) category_types["Command"].append(ResultType(59, "Magitek Upgrade", "Magitek Upgrade", None)) category_types["Item"].append(ResultType(60, "Sprint Shoes", "Sprint Shoes", None)) category_types["Kefka's Tower"] += [ResultType(61, "Unlock Perma KT Skip", "Unlock Perma KT Skip", None)] diff --git a/constants/quests.py b/constants/quests.py new file mode 100644 index 00000000..826b3ab0 --- /dev/null +++ b/constants/quests.py @@ -0,0 +1,16 @@ +from collections import namedtuple +import data.event_bit as event_bit + +NameBit = namedtuple("NameBit", ["name", "bit"]) + +DEFEAT_SEALED_CAVE_NINJA = NameBit("Defeat Sealed Cave Ninja", event_bit.DEFEATED_NINJA_CAVE_TO_SEALED_GATE) +HELP_INJURED_LAD = NameBit("Help Injured Lad", event_bit.HELPED_INJURED_LAD) +LET_CID_DIE = NameBit("Let Cid Die", event_bit.CID_DIED) +PASS_SECURITY_CHECKPOINT = NameBit("Pass Security Checkpoint", event_bit.FINISHED_NARSHE_CHECKPOINT) +PERFORM_IN_OPERA = NameBit("Perform In Opera", event_bit.FINISHED_OPERA_PERFORMANCE) +SAVE_CID = NameBit("Save Cid", event_bit.CID_SURVIVED) +SET_ZOZO_CLOCK = NameBit("Set Zozo Clock", event_bit.SET_ZOZO_CLOCK) +SUPLEX_A_TRAIN = NameBit("Suplex A Train", event_bit.SUPLEXED_TRAIN) +WIN_AN_AUCTION = NameBit("Win An Auction", event_bit.WON_AN_AUCTION) +WIN_A_COLISEUM_MATCH = NameBit("Win A Coliseum Match", event_bit.WON_A_COLISEUM_MATCH) +REUNITE_GAU_AND_DAD = NameBit("Reunite Gau and Father", event_bit.GAU_FATHER_REUNION), diff --git a/data/event_bit.py b/data/event_bit.py index 22f29611..e89dbb05 100644 --- a/data/event_bit.py +++ b/data/event_bit.py @@ -138,6 +138,8 @@ GOT_RAGNAROK = 0x0b6 GOT_BOTH_REWARDS_WEAPON_SHOP = 0x0b7 # custom CHASING_LONE_WOLF1 = 0x239 +CHASING_LONE_WOLF5 = 0x23f +CHASING_LONE_WOLF6 = 0x23f CHASING_LONE_WOLF7 = 0x23f GOT_BOTH_REWARDS_LONE_WOLF = 0x241 MET_LONE_WOLF_WOR = 0x29b @@ -162,6 +164,7 @@ DEFEATED_STOOGES = 0x0d8 FINISHED_DOMA_WOR = 0x0da GOT_ALEXANDR = 0x0db +RECEIVED_FANATICS_TOWER_REWARD = 0x2da DEFEATED_MAGIMASTER = 0x2db RECRUITED_STRAGO_FANATICS_TOWER = 0x0ba DEFEATED_DOOM_GAZE = 0x2a1 # custom diff --git a/data/map_event.py b/data/map_event.py index b9edb8f4..4194be2c 100644 --- a/data/map_event.py +++ b/data/map_event.py @@ -26,5 +26,10 @@ def to_data(self): return data + def set_event_address(self, address): + from instruction.event import EVENT_CODE_START + self.event_address = address - EVENT_CODE_START + + def print(self): print("{}, {}: {}".format(self.x, self.y, hex(self.event_address))) diff --git a/data/npc_bit.py b/data/npc_bit.py index 46f86570..75f90ef3 100644 --- a/data/npc_bit.py +++ b/data/npc_bit.py @@ -147,6 +147,9 @@ GODDESS_STATUE_KEFKA_TOWER = 0x6b1 POLTERGEIST_STATUE_KEFKA_TOWER = 0x6b2 +# bits [571-5f5] are unused +FANATICS_TOWER_SECONDARY_REWARD = 0x571 # custom, used for secondary reward at top of fanatics tower (if esper only) + def byte(npc_bit): return npc_bit // 8 diff --git a/event/ancient_castle.py b/event/ancient_castle.py index ad66561f..9402ea05 100644 --- a/event/ancient_castle.py +++ b/event/ancient_castle.py @@ -1,3 +1,4 @@ +from constants.checks import ANCIENT_CASTLE from event.event import * class AncientCastle(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.EDGAR def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(ANCIENT_CASTLE) def init_event_bits(self, space): space.write( diff --git a/event/auction_house.py b/event/auction_house.py index 82fc0a53..0374ee78 100644 --- a/event/auction_house.py +++ b/event/auction_house.py @@ -44,12 +44,9 @@ def name(self): return "Auction House" def init_rewards(self): - if self.args.no_free_characters_espers: - self.reward1 = self.add_reward(RewardType.ITEM) - self.reward2 = self.add_reward(RewardType.ITEM) - else: - self.reward1 = self.add_reward(RewardType.ESPER | RewardType.ITEM) - self.reward2 = self.add_reward(RewardType.ESPER | RewardType.ITEM) + from constants.checks import AUCTION1, AUCTION2 + self.reward1 = self.add_reward(AUCTION1) + self.reward2 = self.add_reward(AUCTION2) def mod(self): self.requirements_mod() diff --git a/event/baren_falls.py b/event/baren_falls.py index ebafea06..31a70d50 100644 --- a/event/baren_falls.py +++ b/event/baren_falls.py @@ -1,3 +1,4 @@ +from constants.checks import BAREN_FALLS from event.event import * class BarenFalls(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.SABIN def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(BAREN_FALLS) def mod(self): # delete row of events that trigger sabin/cyan dialog and shadow leaving (if in party) diff --git a/event/burning_house.py b/event/burning_house.py index c67ad35f..0a4fe93a 100644 --- a/event/burning_house.py +++ b/event/burning_house.py @@ -1,3 +1,4 @@ +from constants.checks import BURNING_HOUSE from event.event import * # TODO: only trigger this event in wob @@ -10,7 +11,7 @@ def character_gate(self): return self.characters.STRAGO def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(BURNING_HOUSE) def init_event_bits(self, space): space.write( diff --git a/event/collapsing_house.py b/event/collapsing_house.py index 60e05e11..bf8e9438 100644 --- a/event/collapsing_house.py +++ b/event/collapsing_house.py @@ -1,3 +1,4 @@ +from constants.checks import COLLAPSING_HOUSE from event.event import * class CollapsingHouse(Event): @@ -8,10 +9,7 @@ def character_gate(self): return self.characters.SABIN def init_rewards(self): - if self.args.no_free_characters_espers: - self.reward = self.add_reward(RewardType.ITEM) - else: - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(COLLAPSING_HOUSE) def init_event_bits(self, space): pass diff --git a/event/daryl_tomb.py b/event/daryl_tomb.py index 2af01857..bb2b92d5 100644 --- a/event/daryl_tomb.py +++ b/event/daryl_tomb.py @@ -1,3 +1,4 @@ +from constants.checks import DARYLS_TOMB from event.event import * class DarylTomb(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.SETZER def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(DARYLS_TOMB) def mod(self): self.entrance_mod() diff --git a/event/doma_wob.py b/event/doma_wob.py index ba6f5f50..59784238 100644 --- a/event/doma_wob.py +++ b/event/doma_wob.py @@ -1,14 +1,15 @@ from event.event import * +from constants.checks import DOMA_SIEGE class DomaWOB(Event): def name(self): - return "Doma WOB" + return DOMA_SIEGE.name def character_gate(self): return self.characters.CYAN def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(DOMA_SIEGE) def init_event_bits(self, space): space.write( diff --git a/event/doma_wor.py b/event/doma_wor.py index a7afb0bc..8b05bb8c 100644 --- a/event/doma_wor.py +++ b/event/doma_wor.py @@ -2,15 +2,16 @@ class DomaWOR(Event): def name(self): - return "Doma WOR" + return "Doma Dream" def character_gate(self): return self.characters.CYAN def init_rewards(self): - self.reward1 = self.add_reward(RewardType.CHARACTER | RewardType.ESPER) - self.reward2 = self.add_reward(RewardType.ESPER | RewardType.ITEM) - self.reward3 = self.add_reward(RewardType.ESPER | RewardType.ITEM) + from constants.checks import DOMA_DREAM_DOOR, DOMA_DREAM_AWAKEN, DOMA_DREAM_THRONE + self.reward1 = self.add_reward(DOMA_DREAM_AWAKEN) + self.reward2 = self.add_reward(DOMA_DREAM_THRONE) + self.reward3 = self.add_reward(DOMA_DREAM_DOOR) def mod(self): self.cyan_phantom_train_npc_id = 0x10 @@ -45,6 +46,8 @@ def mod(self): self.cyan_character_mod(self.reward1.id) elif self.reward1.type == RewardType.ESPER: self.cyan_esper_mod(self.reward1.id) + elif self.reward1.type == RewardType.ITEM: + self.cyan_item_mod(self.reward1.id) self.finish_dream_awaken_mod() if self.reward2.type == RewardType.ESPER: @@ -268,6 +271,21 @@ def cyan_esper_mod(self, esper): field.Branch(space.end_address + 1), # skip nops ) + def cyan_item_mod(self, item): + self.random_cyan_npc_mod() + + space = Reserve(0xb9818, 0xb982f, "doma wor split up party after wrexsoul battle", field.NOP()) + space.write( + field.Branch(space.end_address + 1), # skip nops + ) + + space = Reserve(0xb99b4, 0xb99d4, "doma wor cyan touches sword", field.NOP()) + space.write( + field.AddItem(item), + field.Dialog(self.items.get_receive_dialog(item)), + field.Branch(space.end_address + 1), # skip nops + ) + def finish_dream_awaken_mod(self): if(self.args.flashes_remove_most or self.args.flashes_remove_worst): space = Reserve(0xb9a47, 0xb9a48, "doma wor peak swordmanship flash", field.FlashScreen(field.Flash.NONE)) diff --git a/event/doom_gaze.py b/event/doom_gaze.py index ca7e7cf8..675e5140 100644 --- a/event/doom_gaze.py +++ b/event/doom_gaze.py @@ -8,7 +8,8 @@ def character_gate(self): return self.characters.SETZER # gate for airship option def init_rewards(self): - self.reward = self.add_reward(RewardType.ESPER | RewardType.ITEM) + from constants.checks import SEARCH_THE_SKIES + self.reward = self.add_reward(SEARCH_THE_SKIES) def mod(self): self.magicite_npc_id = 0x12 diff --git a/event/ebots_rock.py b/event/ebots_rock.py index 21882030..e311f48f 100644 --- a/event/ebots_rock.py +++ b/event/ebots_rock.py @@ -1,3 +1,4 @@ +from constants.checks import EBOTS_ROCK from event.event import * class EbotsRock(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.STRAGO def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(EBOTS_ROCK) def init_event_bits(self, space): space.write( diff --git a/event/eight_dragons.py b/event/eight_dragons.py index de995128..4d5309c0 100644 --- a/event/eight_dragons.py +++ b/event/eight_dragons.py @@ -1,3 +1,4 @@ +from constants.checks import DRAGONS from event.event import * class EightDragons(Event): @@ -6,8 +7,8 @@ def name(self): def init_rewards(self): self.item_rewards = [] - for dragon_index in range(self.enemies.DRAGON_COUNT): - self.item_rewards.append(self.add_reward(RewardType.ITEM)) + for dragon_check in DRAGONS: + self.item_rewards.append(self.add_reward(dragon_check)) def init_event_bits(self, space): space.write( diff --git a/event/esper_mountain.py b/event/esper_mountain.py index 9587614d..1cd67c7b 100644 --- a/event/esper_mountain.py +++ b/event/esper_mountain.py @@ -1,3 +1,4 @@ +from constants.checks import ESPER_MOUNTAIN from event.event import * class EsperMountain(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.RELM def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(ESPER_MOUNTAIN) def init_event_bits(self, space): space.write( diff --git a/event/event.py b/event/event.py index 11f6ee13..0619d50f 100644 --- a/event/event.py +++ b/event/event.py @@ -1,3 +1,4 @@ +from data.item import Item from memory.space import Bank, Space, Reserve, Allocate, Free, Write, Read import data.direction as direction @@ -41,10 +42,39 @@ def character_gate(self): def characters_required(self): return 1 - def add_reward(self, possible_types): - new_reward = Reward(self, possible_types) - self.rewards.append(new_reward) - return new_reward + def get_reward_type(self, check_info = None): + bit = check_info.bit + + assert bit + + if bit in self.args.esper_item_rewards: + return RewardType.ESPER | RewardType.ITEM + + if bit in self.args.esper_rewards: + return RewardType.ESPER + + if bit in self.args.item_rewards: + return RewardType.ITEM + + return check_info.reward_types + + def add_item_reward(self): + reward = Reward(self, RewardType.ITEM) + self.rewards.append(reward) + return reward + + def add_character_reward(self): + reward = Reward(self, RewardType.CHARACTER) + self.rewards.append(reward) + return reward + + def add_reward(self, check_info): + possible_types = self.get_reward_type(check_info) + assert possible_types + + reward = Reward(self, possible_types) + self.rewards.append(reward) + return reward def init_rewards(self): pass @@ -65,7 +95,7 @@ def log_reward(self, reward, prefix = "", suffix = ""): if reward.type == RewardType.CHARACTER: reward_string += self.characters.get_name(reward.id) elif reward.type == RewardType.ESPER: - reward_string += self.espers.get_name(reward.id) + reward_string += "*" + self.espers.get_name(reward.id) elif reward.type == RewardType.ITEM: reward_string += self.items.get_name(reward.id) self.rewards_log.append(reward_string + suffix) @@ -79,6 +109,7 @@ def log_string(self): log_string += f" {', '.join(self.rewards_log)}" if self.changes_log: log_string += '\n' + '\n'.join(self.changes_log) + return log_string def mod(self): diff --git a/event/event_reward.py b/event/event_reward.py index f1063e2e..82abbb12 100644 --- a/event/event_reward.py +++ b/event/event_reward.py @@ -17,6 +17,21 @@ def __init__(self, event, possible_types): def single_possible_type(self): return self.possible_types in RewardType + def is_type(self, type): + return self.type == type + + def is_none(self): + return self.is_type(RewardType.NONE) + + def is_character(self): + return self.is_type(RewardType.CHARACTER) + + def is_esper(self): + return self.is_type(RewardType.ESPER) + + def is_item(self): + return self.is_type(RewardType.ITEM) + def __str__(self): result = f"{self.id} {self.type} {self.event.name()}" diff --git a/event/events.py b/event/events.py index f1d40477..9306e588 100644 --- a/event/events.py +++ b/event/events.py @@ -54,6 +54,8 @@ def mod(self): if self.args.spoiler_log and (event.rewards_log or event.changes_log): log_strings.append(event.log_string()) + + log_strings.append("* = Esper/Magicite") space.write(field.Return()) if self.args.spoiler_log: @@ -125,6 +127,10 @@ def character_gating_mod(self, events, name_event): unlocked_slots.append(slot) unlocked_slot_iterations.append(slot_iterations[slot]) + # this means an impossible start has occured. + # i.e. no character can be retrieved given the starting char + check availability + assert len(unlocked_slots) > 0 + # pick slot for the next character weighted by number of iterations each slot has been available slot_index = weighted_reward_choice(unlocked_slot_iterations, iteration) slot = unlocked_slots[slot_index] diff --git a/event/fanatics_tower.py b/event/fanatics_tower.py index 38d6fd3a..d5774e83 100644 --- a/event/fanatics_tower.py +++ b/event/fanatics_tower.py @@ -1,3 +1,6 @@ +from constants.checks import FANATICS_TOWER_FOLLOWER, FANATICS_TOWER_LEADER +from data.map_event import MapEvent +from data.npc import NPC from event.event import * class FanaticsTower(Event): @@ -7,26 +10,37 @@ def name(self): def character_gate(self): return self.characters.STRAGO + def init_event_bits(self, space): + space.write([ + field.SetEventBit(npc_bit.FANATICS_TOWER_SECONDARY_REWARD), + ]) + + def init_rewards(self): - self.reward1 = self.add_reward(RewardType.CHARACTER | RewardType.ESPER) - self.reward2 = self.add_reward(RewardType.ITEM) + self.reward1 = self.add_reward(FANATICS_TOWER_FOLLOWER) + self.reward2 = self.add_reward(FANATICS_TOWER_LEADER) def mod(self): + self.top_treasure_room_id = 0x16e self.strago_npc_id = 0x13 self.strago_npc = self.maps.get_npc(0x16a, self.strago_npc_id) self.gau_magic_mod() self.relm_event_mod() - self.tower_top_mod() self.magimaster_battle_mod() - if self.reward1.type == RewardType.CHARACTER: + if self.reward1.is_character(): self.character_mod(self.reward1.id) - elif self.reward1.type == RewardType.ESPER: + elif self.reward1.is_esper(): self.esper_mod(self.reward1.id) - elif self.reward1.type == RewardType.ITEM: + elif self.reward1.is_item(): self.item_mod(self.reward1.id) + if self.reward2.is_esper(): + self.tower_top_esper_mod() + elif self.reward2.is_item(): + self.tower_top_item_mod() + self.finish_magimaster_check_mod() self.finish_strago_check_mod() @@ -127,12 +141,62 @@ def relm_event_mod(self): space = Reserve(0xc5407, 0xc5408, "fanatics tower stop relm song before screen fade", field.NOP()) - def tower_top_mod(self): + def tower_top_esper_mod(self): + space = Reserve(0xc5548, 0xc554a, "fanatics tower master kefka's treasure", field.NOP()) + space = Reserve(0xc554d, 0xc554e, "fanatics tower long pause before magic master appears", field.NOP()) + + # Move the chest to inaccessible location, empty it.. + chest = self.maps.get_chests(0x16e)[0] + chest.type = chest.EMPTY + chest.contents = 255 + chest.x = 0 + chest.y = 0 + + # This event flips the bit to trigger the MagiMaster fight outside, + # And also spawns the cultists during the cutscene + # We copy the relevant code from this event to event_space below + self.maps.delete_event(self.top_treasure_room_id, 7, 8) + + # Place magicite in front of the chest + self.magicite_npc = NPC() + self.magicite_npc.sprite = 91 # Magicite + self.magicite_npc.palette = 2 + self.magicite_npc.split_sprite = 1 + self.magicite_npc.direction = direction.UP + self.magicite_npc.x = 7 + self.magicite_npc.y = 8 + self.magicite_npc.event_byte = npc_bit.event_byte(npc_bit.FANATICS_TOWER_SECONDARY_REWARD) + self.magicite_npc.event_bit = npc_bit.event_bit(npc_bit.FANATICS_TOWER_SECONDARY_REWARD) + + self.magicite_npc_id = self.maps.append_npc(self.top_treasure_room_id, self.magicite_npc) + + event_space = Allocate(Bank.CC, 40, "Give fanatics tower esper subroutine", asm.NOP()) + event_space.write([ + Read(0xc5440, 0xc5441), # Set npc bit hex(730) true (top treasure received) + Read(0xc5448, 0xc5449), # Set npc bit hex(1689) true (cultists outside treasure room) + field.AddEsper(self.reward2.id), + field.Dialog(self.espers.get_receive_esper_dialog(self.reward2.id)), + field.ClearEventBit(npc_bit.FANATICS_TOWER_SECONDARY_REWARD), + field.DeleteEntity(self.magicite_npc_id), + field.RefreshEntities(), + field.FinishCheck(), + field.Return(), + ]) + + # Re-using space from removed event above + space = Reserve(0xc5440, 0xc544a, "Call esper subroutine", asm.NOP()) + space.write([ + field.Call(event_space.start_address), + field.Return(), + ]) + self.magicite_npc.set_event_address(space.start_address) + + def tower_top_item_mod(self): space = Reserve(0xc5548, 0xc554a, "fanatics tower master kefka's treasure", field.NOP()) space = Reserve(0xc554d, 0xc554e, "fanatics tower long pause before magic master appears", field.NOP()) self.item = self.reward2.id - self.maps.set_chest_item(0x16e, 7, 7, self.item) + self.maps.set_chest_item(self.top_treasure_room_id, 7, 7, self.item) def magimaster_battle_mod(self): boss_pack_id = self.get_boss("MagiMaster") diff --git a/event/figaro_castle_wob.py b/event/figaro_castle_wob.py index 9bbbf644..d99dbdc9 100644 --- a/event/figaro_castle_wob.py +++ b/event/figaro_castle_wob.py @@ -1,17 +1,15 @@ from event.event import * +from constants.checks import FIGARO_CASTLE_THRONE class FigaroCastleWOB(Event): def name(self): - return "Figaro Castle WOB" + return FIGARO_CASTLE_THRONE.name def character_gate(self): return self.characters.EDGAR def init_rewards(self): - if self.args.no_free_characters_espers: - self.reward = self.add_reward(RewardType.ITEM) - else: - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(FIGARO_CASTLE_THRONE) def init_event_bits(self, space): space.write( diff --git a/event/figaro_castle_wor.py b/event/figaro_castle_wor.py index 1f54c9aa..2a0f89ff 100644 --- a/event/figaro_castle_wor.py +++ b/event/figaro_castle_wor.py @@ -1,14 +1,15 @@ from event.event import * +from constants.checks import FIGARO_CASTLE_ENGINE class FigaroCastleWOR(Event): def name(self): - return "Figaro Castle WOR" + return FIGARO_CASTLE_ENGINE.name def character_gate(self): return self.characters.EDGAR def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(FIGARO_CASTLE_ENGINE) def init_event_bits(self, space): if self.args.character_gating: diff --git a/event/floating_continent.py b/event/floating_continent.py index 2782a099..89f20e0d 100644 --- a/event/floating_continent.py +++ b/event/floating_continent.py @@ -15,9 +15,11 @@ def character_gate(self): return self.characters.SHADOW def init_rewards(self): - self.reward1 = self.add_reward(RewardType.CHARACTER | RewardType.ESPER) - self.reward2 = self.add_reward(RewardType.ESPER | RewardType.ITEM) - self.reward3 = self.add_reward(RewardType.CHARACTER | RewardType.ESPER) + from constants.checks import FLOATING_CONT_ARRIVE, FLOATING_CONT_BEAST, FLOATING_CONT_ESCAPE + + self.reward1 = self.add_reward(FLOATING_CONT_ARRIVE) + self.reward2 = self.add_reward(FLOATING_CONT_BEAST) + self.reward3 = self.add_reward(FLOATING_CONT_ESCAPE) def mod(self): self.shadow_leaves_mod() @@ -35,6 +37,8 @@ def mod(self): self.ground_character_mod(self.reward1.id) elif self.reward1.type == RewardType.ESPER: self.ground_esper_mod(self.reward1.id) + elif self.reward1.type == RewardType.ITEM: + self.ground_item_mod(self.reward1.id) self.finish_ground_check() self.save_point_hole_mod() @@ -54,6 +58,8 @@ def mod(self): self.escape_character_mod(self.reward3.id) elif self.reward3.type == RewardType.ESPER: self.escape_esper_mod(self.reward3.id) + elif self.reward3.type == RewardType.ITEM: + self.escape_item_mod(self.reward3.id) self.log_reward(self.reward1) self.log_reward(self.reward2) @@ -190,7 +196,7 @@ def ground_character_mod(self, character): field.FadeInScreen(), ) - def ground_esper_mod(self, esper): + def ground_esper_mod(self, esper_id): self.ground_shadow_npc.sprite = 91 self.ground_shadow_npc.palette = 2 self.ground_shadow_npc.split_sprite = 1 @@ -198,12 +204,27 @@ def ground_esper_mod(self, esper): space = Reserve(0xad9b1, 0xad9ed, "floating continent add esper on ground", field.NOP()) space.write( - field.AddEsper(esper), - field.Dialog(self.espers.get_receive_esper_dialog(esper)), + field.AddEsper(esper_id), + field.Dialog(self.espers.get_receive_esper_dialog(esper_id)), field.DeleteEntity(self.ground_shadow_npc_id), field.Branch(space.end_address + 1), ) + def ground_item_mod(self, item_id): + self.ground_shadow_npc.sprite = 106 + self.ground_shadow_npc.palette = 6 + self.ground_shadow_npc.split_sprite = 1 + self.ground_shadow_npc.direction = direction.DOWN + + space = Reserve(0xad9b1, 0xad9ed, "floating continent add item on ground", field.NOP()) + space.write( + field.AddItem(item_id), + field.Dialog(self.items.get_receive_dialog(item_id)), + field.DeleteEntity(self.ground_shadow_npc_id), + field.Branch(space.end_address + 1), + ) + + def finish_ground_check(self): src = [ Read(0xad9ee, 0xad9f2), # clear ground npc bit, set shadow recruited bit, update party leader @@ -456,10 +477,10 @@ def escape_character_mod(self, character): field.FadeInScreen(), ]) - def escape_esper_mod(self, esper): + def escape_esper_item_mod(self): # use guest character to give esper reward guest_char_id = 0x0f - guest_char = self.maps.get_npc(0x189, guest_char_id) + guest_char = self.maps.get_npc(0x189, guest_char_id) # is this necessary? random_sprite = self.characters.get_random_esper_item_sprite() random_sprite_palette = self.characters.get_palette(random_sprite) @@ -471,10 +492,26 @@ def escape_esper_mod(self, esper): field.RefreshEntities(), ) + return (guest_char_id) + + def escape_esper_mod(self, esper_id): + (guest_char_id) = self.escape_esper_item_mod() + self.escape_mod(guest_char_id, [ field.DeleteEntity(guest_char_id), field.RefreshEntities(), field.LoadMap(0x06, direction.DOWN, default_music = True, x = 16, y = 6, fade_in = True, entrance_event = True), - field.AddEsper(esper), - field.Dialog(self.espers.get_receive_esper_dialog(esper)), + field.AddEsper(esper_id), + field.Dialog(self.espers.get_receive_esper_dialog(esper_id)), + ]) + + def escape_item_mod(self, item_id): + (guest_char_id) = self.escape_esper_item_mod() + + self.escape_mod(guest_char_id, [ + field.DeleteEntity(guest_char_id), + field.RefreshEntities(), + field.LoadMap(0x06, direction.DOWN, default_music = True, x = 16, y = 6, fade_in = True, entrance_event = True), + field.AddItem(item_id), + field.Dialog(self.items.get_receive_dialog(item_id)), ]) diff --git a/event/gau_father_house.py b/event/gau_father_house.py index 1083c3d3..33dd464a 100644 --- a/event/gau_father_house.py +++ b/event/gau_father_house.py @@ -1,17 +1,15 @@ from event.event import * +from constants.checks import GAUS_FATHERS_HOUSE class GauFatherHouse(Event): def name(self): - return "Gau Father House" + return GAUS_FATHERS_HOUSE.name def character_gate(self): return self.characters.SHADOW def init_rewards(self): - if self.args.no_free_characters_espers: - self.reward = self.add_reward(RewardType.ITEM) - else: - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(GAUS_FATHERS_HOUSE) def mod(self): self.shadow_npc_id = 0x10 diff --git a/event/imperial_camp.py b/event/imperial_camp.py index 85f8bd38..e3b4310e 100644 --- a/event/imperial_camp.py +++ b/event/imperial_camp.py @@ -1,3 +1,4 @@ +from constants.checks import IMPERIAL_CAMP from event.event import * class ImperialCamp(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.SABIN def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(IMPERIAL_CAMP) def init_event_bits(self, space): space.write( diff --git a/event/kefka_tower.py b/event/kefka_tower.py index 21f100aa..bc1a7ea6 100644 --- a/event/kefka_tower.py +++ b/event/kefka_tower.py @@ -1,6 +1,10 @@ + +from constants.checks import KEFKAS_TOWER_CELL_BEAST + from asyncio import wait_for from data.map_event import MapEvent from data.npc import NPC + from event.event import * import args @@ -30,7 +34,7 @@ def name(self): return "Kefka's Tower" def init_rewards(self): - self.atma_reward = self.add_reward(RewardType.ITEM) + self.atma_reward = self.add_reward(KEFKAS_TOWER_CELL_BEAST) def init_event_bits(self, space): space.write( diff --git a/event/kohlingen.py b/event/kohlingen.py index 09897141..4f5e61bb 100644 --- a/event/kohlingen.py +++ b/event/kohlingen.py @@ -1,20 +1,18 @@ from event.event import * +from constants.checks import KOHLINGEN_CAFE # TODO use setzer npc instead of shadow's # for character reward show the animations setzer does in wor before daryl's tomb class Kohlingen(Event): def name(self): - return "Kohlingen" + return KOHLINGEN_CAFE.name def character_gate(self): return self.characters.SETZER def init_rewards(self): - if self.args.no_free_characters_espers: - self.reward = self.add_reward(RewardType.ITEM) - else: - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(KOHLINGEN_CAFE) def init_event_bits(self, space): space.write( diff --git a/event/lete_river.py b/event/lete_river.py index 09b9cf08..ffd239ba 100644 --- a/event/lete_river.py +++ b/event/lete_river.py @@ -1,3 +1,4 @@ +from constants.checks import LETE_RIVER from event.event import * class LeteRiver(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.TERRA def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(LETE_RIVER) def init_event_bits(self, space): space.write( diff --git a/event/lone_wolf.py b/event/lone_wolf.py index 11b22f9b..51134a64 100644 --- a/event/lone_wolf.py +++ b/event/lone_wolf.py @@ -1,3 +1,6 @@ +from email.policy import default +from constants.checks import LONE_WOLF_CHASE, LONE_WOLF_MOOGLE_ROOM +from data.map_event import MapEvent from event.event import * class LoneWolf(Event): @@ -8,8 +11,8 @@ def character_gate(self): return self.characters.MOG def init_rewards(self): - self.reward1 = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) - self.reward2 = self.add_reward(RewardType.ITEM) + self.reward1 = self.add_reward(LONE_WOLF_CHASE) + self.reward2 = self.add_reward(LONE_WOLF_MOOGLE_ROOM) def init_event_bits(self, space): space.write( @@ -17,6 +20,15 @@ def init_event_bits(self, space): field.ClearEventBit(npc_bit.MOG_MOOGLE_ROOM_WOR), ) + # Load into narshe cliffs and you can trigger lone wolf easily with these bits + # if self.args.debug: + # space.write( + # field.SetEventBit(572), # Spectate lone wolf cross the bridge to cliff + # field.ClearEventBit(573), # Haven't witnessed lone wolf event + # field.SetEventBit(831), # Visibility bits for lone wolf npcs + # field.SetEventBit(832), + # ) + def mod(self): self.mog_npc_id = 0x1c self.mog_npc = self.maps.get_npc(0x017, self.mog_npc_id) @@ -35,15 +47,23 @@ def mod(self): if self.reward1.type == RewardType.CHARACTER: self.character_mod(self.reward1.id) - elif self.reward1.type == RewardType.ESPER: + if self.reward1.type == RewardType.ESPER: self.esper_mod(self.reward1.id) elif self.reward1.type == RewardType.ITEM: self.item_mod(self.reward1.id) - self.alternative_item_mod() - self.finish_check_mod() + + if self.reward2.type == RewardType.ESPER: + self.alternative_esper_mod() + self.moogle_room_reward_mod([field.AddEsper(self.reward2.id)]) + elif self.reward2.type == RewardType.ITEM: + self.alternative_item_mod() + self.moogle_room_reward_mod([field.AddItem(self.reward2.id)]) self.moogle_room_entrance_event_mod() - self.moogle_room_reward_mod() + self.lone_wolf_hide_mod() + + self.finish_check_mod() + self.log_reward(self.reward1) self.log_reward(self.reward2) @@ -144,19 +164,35 @@ def item_mod(self, item): field.Dialog(self.items.get_receive_dialog(item)), ]) - def alternative_item_mod(self): - # item lone wolf will give as a reward for not picking self.reward1 - import data.text - item_name = data.text.convert(self.items.get_name(self.reward2.id), data.text.TEXT1) # item names are stored as TEXT2, dialogs are TEXT1 + def alternative_esper_mod(self): + # esper lone wolf will give as a reward for not picking self.reward1 + self.lone_wolf_dialog_esper_mod() + + esper_space = Allocate(Bank.CC, 50, "idk yet", field.NOP()) + esper_space.write([ + field.AddEsper(self.reward2.id, sound_effect=True), + field.Dialog(self.espers.get_receive_esper_dialog(self.reward2.id)), + Read(0xcd59d, 0xcd59d), # Wait 30 frames + field.Return() + ]) + + space = Reserve(0xcd59a, 0xcd59f, "received item dialog; wait 0.5s; take item from lone wolf", field.NOP()) + space.write([ + field.Call(esper_space.start_address) + ]) - self.dialogs.set_text(1765, "< >Grrrr…< >You'll never get this< >“" + item_name + "”!") - self.dialogs.set_text(1742, "< >Got “" + item_name + "”!") + def alternative_item_mod(self): + self.lone_wolf_dialog_item_mod() + + # item lone wolf will give as a reward for not picking self.reward1 space = Reserve(0xcd59f, 0xcd59f, "lone wolf item received", field.NOP()) space.write( self.reward2.id, ) + # add pause after lone wolf jumps to wait for falling sound effect + def lone_wolf_hide_mod(self): space = Reserve(0xcd5be, 0xcd5c0, "item chosen dialog before lone wolf falls", field.NOP()) space.write( field.SetEventBit(npc_bit.MOG_MOOGLE_ROOM_WOR), @@ -179,6 +215,20 @@ def alternative_item_mod(self): field.Pause(1.5), ) + def lone_wolf_dialog_esper_mod(self): + import data.text + esper_name = data.text.convert(self.espers.get_name(self.reward2.id), data.text.TEXT1) # item names are stored as TEXT2, dialogs are TEXT1 + + self.dialogs.set_text(1765, "< >Grrrr…< >You'll never get this< >“" + esper_name + "”!") + self.dialogs.set_text(1742, f" Received the Magicite “{esper_name}.”") + + def lone_wolf_dialog_item_mod(self): + import data.text + item_name = data.text.convert(self.items.get_name(self.reward2.id), data.text.TEXT1) # item names are stored as TEXT2, dialogs are TEXT1 + + self.dialogs.set_text(1765, "< >Grrrr…< >You'll never get this< >“" + item_name + "”!") + self.dialogs.set_text(1742, "< >Got “" + item_name + "”!") + def finish_check_mod(self): src = [ field.ClearEventBit(npc_bit.LONE_WOLF_MOG_NARSHE_CLIFF), @@ -242,7 +292,7 @@ def moogle_room_item_mod(self, item): field.Dialog(self.items.get_receive_dialog(item)), ]) - def moogle_room_reward_mod(self): + def moogle_room_reward_mod(self, reward_instructions): receive_reward = field.RETURN if self.reward1.type == RewardType.CHARACTER: receive_reward = self.moogle_room_character_mod(self.reward1.id) @@ -268,8 +318,12 @@ def moogle_room_reward_mod(self): field_entity.Pause(8), field_entity.SetSpeed(field_entity.Speed.FASTEST), field_entity.Move(direction.DOWN, 8), - ), - field.AddItem(self.reward2.id), + ) + ] + + src += reward_instructions + + src += [ field.Dialog(1742), field.HideEntity(self.mog_moogle_room_npc_id), field.ClearEventBit(npc_bit.MOG_MOOGLE_ROOM_WOR), diff --git a/event/magitek_factory.py b/event/magitek_factory.py index c23a9b1e..272ff562 100644 --- a/event/magitek_factory.py +++ b/event/magitek_factory.py @@ -8,9 +8,10 @@ def character_gate(self): return self.characters.CELES def init_rewards(self): - self.reward1 = self.add_reward(RewardType.ESPER | RewardType.ITEM) - self.reward2 = self.add_reward(RewardType.ESPER | RewardType.ITEM) - self.reward3 = self.add_reward(RewardType.CHARACTER | RewardType.ESPER) + from constants.checks import MAGITEK_FACTORY_TRASH, MAGITEK_FACTORY_GUARD, MAGITEK_FACTORY_FINISH + self.reward1 = self.add_reward(MAGITEK_FACTORY_TRASH) + self.reward2 = self.add_reward(MAGITEK_FACTORY_GUARD) + self.reward3 = self.add_reward(MAGITEK_FACTORY_FINISH) def init_event_bits(self, space): space.write( @@ -47,6 +48,8 @@ def mod(self): self.character_mod(self.reward3.id) elif self.reward3.type == RewardType.ESPER: self.esper_mod(self.reward3.id) + elif self.reward3.type == RewardType.ITEM: + self.item_mod(self.reward3.id) self.crane_battle_mod() self.after_cranes_mod() @@ -322,6 +325,34 @@ def esper_mod(self, esper): field.Branch(space.end_address + 1), # skip nops ) + def esper_item_mod(self): + self.setzer_npc.sprite = self.characters.get_random_esper_item_sprite() + self.setzer_npc.palette = self.characters.get_palette(self.setzer_npc.sprite) + + space = Reserve(0xc819b, 0xc8302, "magitek factory add char and kefka cranes scene", field.NOP()) + return (space) + + + def esper_mod(self, esper): + (space) = self.esper_item_mod() + space.write( + field.AddEsper(esper), + field.Dialog(self.espers.get_receive_esper_dialog(esper)), + field.FadeOutScreen(), + field.WaitForFade(), + field.Branch(space.end_address + 1), # skip nops + ) + + def item_mod(self, item): + (space) = self.esper_item_mod() + space.write( + field.AddItem(item), + field.Dialog(self.items.get_receive_dialog(item)), + field.FadeOutScreen(), + field.WaitForFade(), + field.Branch(space.end_address + 1), # skip nops + ) + def crane_battle_mod(self): boss_pack_id = self.get_boss("Cranes") diff --git a/event/mobliz_wor.py b/event/mobliz_wor.py index f90a5add..6564e51e 100644 --- a/event/mobliz_wor.py +++ b/event/mobliz_wor.py @@ -8,7 +8,8 @@ def character_gate(self): return self.characters.TERRA def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + from constants.checks import MOBLIZ_ATTACK + self.reward = self.add_reward(MOBLIZ_ATTACK) def init_event_bits(self, space): space.write( diff --git a/event/mt_kolts.py b/event/mt_kolts.py index d51e8c74..98b013f7 100644 --- a/event/mt_kolts.py +++ b/event/mt_kolts.py @@ -1,3 +1,4 @@ +from constants.checks import MT_KOLTS from event.event import * class MtKolts(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.SABIN def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(MT_KOLTS) def mod(self): self.vargas_npc_id = 0x10 diff --git a/event/mt_zozo.py b/event/mt_zozo.py index 4aed8fdc..92987ef1 100644 --- a/event/mt_zozo.py +++ b/event/mt_zozo.py @@ -1,3 +1,4 @@ +from constants.checks import MT_ZOZO from event.event import * class MtZozo(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.CYAN def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(MT_ZOZO) def init_event_bits(self, space): space.write( diff --git a/event/narshe_battle.py b/event/narshe_battle.py index 7cb0e5df..0204e4a8 100644 --- a/event/narshe_battle.py +++ b/event/narshe_battle.py @@ -1,3 +1,4 @@ +from constants.checks import NARSHE_BATTLE from event.event import * class NarsheBattle(Event): @@ -8,7 +9,7 @@ def characters_required(self): return 2 def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(NARSHE_BATTLE) def init_event_bits(self, space): space.write( diff --git a/event/narshe_wor.py b/event/narshe_wor.py index ce54f84d..b7b25ed8 100644 --- a/event/narshe_wor.py +++ b/event/narshe_wor.py @@ -1,18 +1,24 @@ from event.event import * +from constants.checks import NARSHE_WEAPON_SHOP, NARSHE_WEAPON_SHOP_MINES class NarsheWOR(Event): def name(self): - return "Narshe WOR" + return NARSHE_WEAPON_SHOP.name def character_gate(self): return self.characters.LOCKE def init_rewards(self): - if self.args.no_free_characters_espers: - self.reward1 = self.add_reward(RewardType.ITEM) - else: - self.reward1 = self.add_reward(RewardType.ESPER | RewardType.ITEM) - self.reward2 = self.add_reward(RewardType.ITEM) + + self.reward1 = self.add_reward(NARSHE_WEAPON_SHOP) + self.reward2 = self.add_reward(NARSHE_WEAPON_SHOP_MINES) + + # This is to make sure you don't ever get an esper in the secondary slot and an item in the first. + # This ensures if either slot rolls an esper, the old man will pull one out. + if self.reward1.type == RewardType.ITEM: + secondary = self.reward2 + self.reward2 = self.reward1 + self.reward1 = secondary def init_event_bits(self, space): space.write( @@ -30,11 +36,10 @@ def mod(self): # allow doors to be unlocked without any condition self.unlock_doors_condition_mod([]) - self.item = self.reward2.id if self.reward1.type == RewardType.ESPER: - self.weapon_shop_esper_mod(self.reward1.id) + self.weapon_shop_esper_mod() elif self.reward1.type == RewardType.ITEM: - self.weapon_shop_item_mod(self.reward1.id) + self.weapon_shop_item_mod() self.cursed_shield_mod() @@ -102,13 +107,24 @@ def add_gating_condition(self): field.BranchIfEventBitClear(event_bit.character_recruited(self.character_gate()), LOCKED), ]) - def weapon_shop_mod(self, dialog_first_choice_text, reward_instructions): + def weapon_shop_mod(self, dialog_first_line): space = Reserve(0xc0b24, 0xc0b26, "narshe wor i wanted to give you this", field.NOP()) import data.text + reward1_esper = self.reward1.type == RewardType.ESPER + reward2_esper = self.reward2.type == RewardType.ESPER # item names stored as TEXT2, dialogs are TEXT1 - item_name = data.text.convert(self.items.get_name(self.item), data.text.TEXT1) - self.dialogs.set_text(1519, dialog_first_choice_text + " Make it “" + item_name + "”") + reward1_esper_line = lambda esper: f" Leave it the stone “" + data.text.convert(self.espers.get_name(esper) + "”", data.text.TEXT1) + reward2_esper_line = lambda esper: f" Make it the stone “" + data.text.convert(self.espers.get_name(esper) + "”", data.text.TEXT1) + get_item_line = lambda item: f" Make it “" + data.text.convert(self.items.get_name(item) + "”", data.text.TEXT1) + + add_esper = lambda esper: field.AddEsper(esper, sound_effect = False) + add_item = lambda item: field.AddItem(item, sound_effect = False) + + reward1 = reward1_esper_line(self.reward1.id) if reward1_esper else get_item_line(self.reward1.id) + reward2 = reward2_esper_line(self.reward2.id) if reward2_esper else get_item_line(self.reward2.id) + + self.dialogs.set_text(1519, dialog_first_line + reward1 + reward2 + "") # if esper or first item chosen, set event bit to know second item should be given by guard space = Reserve(0xc0b42, 0xc0b44, "narshe wor ragnarok esper right", field.NOP()) @@ -121,7 +137,8 @@ def weapon_shop_mod(self, dialog_first_choice_text, reward_instructions): ) src = [ - reward_instructions, + # add reward 1 based on item type + add_esper(self.reward1.id) if reward1_esper else add_item(self.reward1.id), field.SetEventBit(event_bit.GOT_RAGNAROK), field.SetEventBit(event_bit.CHOSE_RAGNAROK_ESPER), field.FinishCheck(), @@ -136,7 +153,8 @@ def weapon_shop_mod(self, dialog_first_choice_text, reward_instructions): ) src = [ - field.AddItem(self.item, sound_effect = False), + # add reward 2 based on item type + add_esper(self.reward2.id) if reward2_esper else add_item(self.reward2.id), field.SetEventBit(event_bit.GOT_RAGNAROK), field.FinishCheck(), field.Return(), @@ -149,7 +167,7 @@ def weapon_shop_mod(self, dialog_first_choice_text, reward_instructions): field.Call(choose_second_option), ) - def esper_room_mod(self, esper_item_instructions): + def behind_whelk_mod(self): from data.npc import NPC guard_npc = NPC() guard_npc.x = 74 @@ -158,20 +176,38 @@ def esper_room_mod(self, esper_item_instructions): guard_npc.palette = 0 guard_npc.direction = direction.DOWN guard_npc.speed = 3 - guard_npc.event_byte = 0x60 - guard_npc.event_bit = 0x05 + guard_npc.event_byte = npc_bit.event_byte(npc_bit.WHELK_GUARD_TRITOCH_NARSHE_WOB) # 0x60 + guard_npc.event_bit = npc_bit.event_bit(npc_bit.WHELK_GUARD_TRITOCH_NARSHE_WOB) # 0x05 guard_npc_id = self.maps.append_npc(0x02b, guard_npc) + add_esper = lambda esper_id: [ + field.AddEsper(esper_id), + field.Dialog(self.espers.get_receive_esper_dialog(esper_id)), + ] + + add_item = lambda item_id: [ + field.AddItem(item_id), + field.Dialog(self.items.get_receive_dialog(item_id)) + ] src = [ - field.BranchIfEventBitSet(event_bit.CHOSE_RAGNAROK_ESPER, "RECEIVE_ITEM"), - esper_item_instructions, + field.BranchIfEventBitSet(event_bit.CHOSE_RAGNAROK_ESPER, "RECEIVE_SECONDARY") + ] + + # Add reward one based on reward type + src += add_esper(self.reward1.id) if self.reward1.type == RewardType.ESPER else add_item(self.reward1.id) + + src += [ field.Branch("DELETE_GUARD"), + "RECEIVE_SECONDARY", + ] - "RECEIVE_ITEM", - field.AddItem(self.item), - field.Dialog(self.items.get_receive_dialog(self.item)), + # Add reward two based on reward type + src += [ + add_esper(self.reward2.id) if self.reward2.type == RewardType.ESPER else add_item(self.reward2.id), + ] + src += [ "DELETE_GUARD", field.FadeOutScreen(), field.WaitForFade(), @@ -183,24 +219,17 @@ def esper_room_mod(self, esper_item_instructions): field.FinishCheck(), field.Return(), ] + space = Write(Bank.CC, src, "narshe wor second weapon shop reward guard npc event") guard_event = space.start_address guard_npc.set_event_address(guard_event) - def weapon_shop_esper_mod(self, esper): - dialog_text = "This stone gives off an eerie aura! Leave it the stone “" + self.espers.get_name(esper) + "”" + def weapon_shop_esper_mod(self): + self.weapon_shop_mod("This stone gives off an eerie aura!") + self.behind_whelk_mod() - self.weapon_shop_mod(dialog_text, [ - field.AddEsper(esper, sound_effect = False), - ]) - - self.esper_room_mod([ - field.AddEsper(esper), - field.Dialog(self.espers.get_receive_esper_dialog(esper)), - ]) - - def weapon_shop_item_mod(self, item): + def weapon_shop_item_mod(self): magicite_npc_id = 0x11 magicite_npc = self.maps.get_npc(0x18, magicite_npc_id) magicite_npc.sprite = 106 @@ -208,18 +237,9 @@ def weapon_shop_item_mod(self, item): magicite_npc.split_sprite = 1 magicite_npc.direction = direction.DOWN - import data.text - item_name = data.text.convert(self.items.get_name(item), data.text.TEXT1) # item names are stored as TEXT2, dialogs are TEXT1 - dialog_text = "This gives off an eerie aura! Leave it “" + item_name + "”" + self.weapon_shop_mod("This gives off an eerie aura!") + self.behind_whelk_mod() - self.weapon_shop_mod(dialog_text, [ - field.AddItem(item, sound_effect = False), - ]) - - self.esper_room_mod([ - field.AddItem(item), - field.Dialog(self.items.get_receive_dialog(item)), - ]) def cursed_shield_mod(self): self.dialogs.set_text(1523, f"“Cursed Shld”…{self.items.cursed_shield_battles}") diff --git a/event/opera_house_wob.py b/event/opera_house_wob.py index 326655b7..39250353 100644 --- a/event/opera_house_wob.py +++ b/event/opera_house_wob.py @@ -8,7 +8,8 @@ def character_gate(self): return self.characters.CELES def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + from constants.checks import OPERA_HOUSE_DISRUPTION + self.reward = self.add_reward(OPERA_HOUSE_DISRUPTION) def init_event_bits(self, space): space.write( @@ -298,7 +299,7 @@ def after_battle_mod(self): space.write( field.Call(show_celes), ) - + # do not animate the now hidden party leader space = Reserve(0xac28a, 0xac28d, "opera house do not turn party leader up", field.NOP()) space = Reserve(0xac30d, 0xac312, "opera house do not move party leader up", field.NOP()) diff --git a/event/owzer_mansion.py b/event/owzer_mansion.py index 99aa3d29..e505d945 100644 --- a/event/owzer_mansion.py +++ b/event/owzer_mansion.py @@ -2,13 +2,14 @@ class OwzerMansion(Event): def name(self): - return "Owzer Mansion" + return "Owzer's Mansion" def character_gate(self): return self.characters.RELM def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + from constants.checks import OWZERS_MANSION + self.reward = self.add_reward(OWZERS_MANSION) def mod(self): self.relm_npc_id = 0x13 diff --git a/event/phantom_train.py b/event/phantom_train.py index 654b590b..16b652ee 100644 --- a/event/phantom_train.py +++ b/event/phantom_train.py @@ -1,3 +1,4 @@ +from constants.checks import PHANTOM_TRAIN from event.event import * class PhantomTrain(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.SABIN def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(PHANTOM_TRAIN) def init_event_bits(self, space): space.write( diff --git a/event/phoenix_cave.py b/event/phoenix_cave.py index 41aa54d9..5b2124c3 100644 --- a/event/phoenix_cave.py +++ b/event/phoenix_cave.py @@ -1,3 +1,4 @@ +from constants.checks import PHOENIX_CAVE from event.event import * class PhoenixCave(Event): @@ -11,7 +12,7 @@ def characters_required(self): return 2 def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(PHOENIX_CAVE) def mod(self): self.locke_npc_id = 0x10 diff --git a/event/sealed_gate.py b/event/sealed_gate.py index 22c0015e..c2774c8e 100644 --- a/event/sealed_gate.py +++ b/event/sealed_gate.py @@ -1,3 +1,4 @@ +from constants.checks import SEALED_GATE from event.event import * class SealedGate(Event): @@ -8,10 +9,7 @@ def character_gate(self): return self.characters.TERRA def init_rewards(self): - if self.args.no_free_characters_espers: - self.reward = self.add_reward(RewardType.ITEM) - else: - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(SEALED_GATE) def init_event_bits(self, space): space.write( diff --git a/event/serpent_trench.py b/event/serpent_trench.py index 6afdeffa..01703efc 100644 --- a/event/serpent_trench.py +++ b/event/serpent_trench.py @@ -1,3 +1,4 @@ +from constants.checks import SERPENT_TRENCH from event.event import * class SerpentTrench(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.GAU def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(SERPENT_TRENCH) def mod(self): self.cave_mod() diff --git a/event/south_figaro.py b/event/south_figaro.py index 4ced7ba2..ce6fbe41 100644 --- a/event/south_figaro.py +++ b/event/south_figaro.py @@ -1,17 +1,15 @@ from event.event import * +from constants.checks import SOUTH_FIGARO_PRISONER class SouthFigaro(Event): def name(self): - return "South Figaro" + return SOUTH_FIGARO_PRISONER.name def character_gate(self): return self.characters.CELES def init_rewards(self): - if self.args.no_free_characters_espers: - self.reward = self.add_reward(RewardType.ITEM) - else: - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(SOUTH_FIGARO_PRISONER) def init_event_bits(self, space): space.write( diff --git a/event/south_figaro_cave_wob.py b/event/south_figaro_cave_wob.py index 225940bf..16ece62a 100644 --- a/event/south_figaro_cave_wob.py +++ b/event/south_figaro_cave_wob.py @@ -1,3 +1,4 @@ +from constants.checks import SOUTH_FIGARO_CAVE from event.event import * class SouthFigaroCaveWOB(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.LOCKE def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(SOUTH_FIGARO_CAVE) def init_event_bits(self, space): space.write( diff --git a/event/start.py b/event/start.py index 39f9b4b1..32f56cb3 100644 --- a/event/start.py +++ b/event/start.py @@ -25,7 +25,7 @@ def init_rewards(self): # assign chosen character rewards for character_id in party: if character_id is not None: - reward = self.add_reward(RewardType.CHARACTER) + reward = self.add_character_reward() reward.id = character_id reward.type = RewardType.CHARACTER @@ -226,14 +226,10 @@ def start_items_mod(self): ] if self.args.debug: - src += [ - field.AddItem("Dried Meat", sound_effect = False), - field.AddItem("Dried Meat", sound_effect = False), - field.AddItem("Dried Meat", sound_effect = False), - field.AddItem("Warp Stone", sound_effect = False), - field.AddItem("Warp Stone", sound_effect = False), - field.AddItem("Warp Stone", sound_effect = False), - ] + from constants.items import name_id + for char in self.characters.characters: + char.init_relic1 = name_id["Moogle Charm"] + src += [ field.Return(), ] @@ -248,7 +244,6 @@ def start_game_mod(self): vehicle.SetPosition(84, 34), vehicle.LoadMap(0x06, direction.DOWN, default_music = True, x = 16, y = 6, entrance_event = True), - field.EntityAct(field_entity.PARTY0, True, field_entity.CenterScreen(), ), @@ -259,4 +254,4 @@ def start_game_mod(self): field.Return(), ] space = Write(Bank.CC, src, "start game") - self.start_game = space.start_address + self.start_game = space.start_address \ No newline at end of file diff --git a/event/tritoch.py b/event/tritoch.py index 428d71bc..863b543d 100644 --- a/event/tritoch.py +++ b/event/tritoch.py @@ -5,7 +5,8 @@ def name(self): return "Tritoch" def init_rewards(self): - self.reward = self.add_reward(RewardType.ESPER | RewardType.ITEM) + from constants.checks import TRITOCH_CLIFF + self.reward = self.add_reward(TRITOCH_CLIFF) def mod(self): self.dialog_mod() diff --git a/event/tzen.py b/event/tzen.py index af8e1da9..10854c10 100644 --- a/event/tzen.py +++ b/event/tzen.py @@ -5,7 +5,8 @@ def name(self): return "Tzen" def init_rewards(self): - self.reward = self.add_reward(RewardType.ESPER | RewardType.ITEM) + from constants.checks import TZEN_THIEF + self.reward = self.add_reward(TZEN_THIEF) def init_event_bits(self, space): space.write( diff --git a/event/umaro_cave.py b/event/umaro_cave.py index 5bf13333..0b611e37 100644 --- a/event/umaro_cave.py +++ b/event/umaro_cave.py @@ -1,3 +1,4 @@ +from constants.checks import UMAROS_CAVE from event.event import * class UmaroCave(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.UMARO def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(UMAROS_CAVE) def mod(self): space = Reserve(0xcd6f5, 0xcd6f7, "umaro cave what's with this carving", field.NOP()) diff --git a/event/veldt.py b/event/veldt.py index 09327d29..01399909 100644 --- a/event/veldt.py +++ b/event/veldt.py @@ -1,3 +1,4 @@ +from constants.checks import VELDT from event.event import * from event.veldt_helpers import * @@ -14,7 +15,7 @@ def character_gate(self): return self.characters.GAU def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER) + self.reward = self.add_reward(VELDT) def init_event_bits(self, space): # abusing init event bits space here... diff --git a/event/veldt_cave_wor.py b/event/veldt_cave_wor.py index 6e69490c..2090777f 100644 --- a/event/veldt_cave_wor.py +++ b/event/veldt_cave_wor.py @@ -8,7 +8,8 @@ def character_gate(self): return self.characters.SHADOW def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + from constants.checks import VELDT_CAVE + self.reward = self.add_reward(VELDT_CAVE) def mod(self): self.shadow_npc_id = 0x12 diff --git a/event/whelk.py b/event/whelk.py index 48ac14b0..52a0c951 100644 --- a/event/whelk.py +++ b/event/whelk.py @@ -8,7 +8,8 @@ def character_gate(self): return self.characters.TERRA def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + from constants.checks import WHELK_GATE + self.reward = self.add_reward(WHELK_GATE) def init_event_bits(self, space): space.write( diff --git a/event/zone_eater.py b/event/zone_eater.py index e47b5319..64692c43 100644 --- a/event/zone_eater.py +++ b/event/zone_eater.py @@ -1,3 +1,4 @@ +from constants.checks import ZONE_EATER from event.event import * class ZoneEater(Event): @@ -8,7 +9,7 @@ def character_gate(self): return self.characters.GOGO def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + self.reward = self.add_reward(ZONE_EATER) def mod(self): self.gogo_npc_id = 0x10 diff --git a/event/zozo.py b/event/zozo.py index 5803f8f4..0db9cebc 100644 --- a/event/zozo.py +++ b/event/zozo.py @@ -8,7 +8,8 @@ def character_gate(self): return self.characters.TERRA def init_rewards(self): - self.reward = self.add_reward(RewardType.CHARACTER | RewardType.ESPER | RewardType.ITEM) + from constants.checks import ZOZO_TOWER + self.reward = self.add_reward(ZOZO_TOWER) def init_event_bits(self, space): space.write( diff --git a/instruction/field/instructions.py b/instruction/field/instructions.py index c9967eeb..4f1d2ce2 100644 --- a/instruction/field/instructions.py +++ b/instruction/field/instructions.py @@ -610,6 +610,12 @@ def __init__(self, event_bit): def __str__(self): return super().__str__(hex(self.event_bit)) +class ShowNPC(SetEventBit): + def __init__(self, npcbit): + from data.npc_bit import event_byte, event_bit + event_bit = event_byte(npcbit) + event_bit + super().__init__(event_bit) + class ClearEventBit(_Instruction): def __init__(self, event_bit): self.event_bit = event_bit @@ -622,6 +628,12 @@ def __init__(self, event_bit): def __str__(self): return super().__str__(hex(self.event_bit)) +class HideNPC(ClearEventBit): + def __init__(self, npcbit): + from data.npc_bit import event_byte, event_bit + event_bit = event_byte(npcbit) + event_bit + super().__init__(event_bit) + class BranchIfEventBitSet(_Branch): def __init__(self, event_bit, destination): self.event_bit = event_bit diff --git a/menus/pregame.py b/menus/pregame.py index 52dd93ed..5efcabe5 100644 --- a/menus/pregame.py +++ b/menus/pregame.py @@ -267,4 +267,4 @@ def mod(self): self.initialize_config_menu_mod() self.exit_config_menu_mod() self.invoke_load_game_mod() - self.menu_commands_jump_table() + self.menu_commands_jump_table() \ No newline at end of file diff --git a/menus/pregame_track.py b/menus/pregame_track.py index 975ea355..7bb310c9 100644 --- a/menus/pregame_track.py +++ b/menus/pregame_track.py @@ -277,7 +277,7 @@ def initialize_cursor_mod(self): asm.LDY(self.cursor_positions, asm.IMM16), c3.eggers_jump(0x0640), # update cursor position c3.eggers_jump(0x07b0), # add cursor to animation queue - asm.RTS(), + asm.RTS(), ] space = Write(Bank.F0, src, "pregame track update cursor position") self.update_cursor_position = space.start_address @@ -789,4 +789,4 @@ def mod(self): self.wait_for_fade_mod() self.fade_in_mod() self.fade_out_mod() - self.menu_commands_jump_table() + self.menu_commands_jump_table() \ No newline at end of file diff --git a/menus/submenu_force_item_reward_checks.py b/menus/submenu_force_item_reward_checks.py new file mode 100644 index 00000000..4d19dfd8 --- /dev/null +++ b/menus/submenu_force_item_reward_checks.py @@ -0,0 +1,117 @@ +from constants.checks import ( + FANATICS_TOWER_LEADER, + LONE_WOLF_MOOGLE_ROOM, + NARSHE_WEAPON_SHOP_MINES, + AUCTION1, + AUCTION2, + COLLAPSING_HOUSE, + FIGARO_CASTLE_THRONE, + GAUS_FATHERS_HOUSE, + KOHLINGEN_CAFE, + NARSHE_WEAPON_SHOP, + SEALED_GATE, + SOUTH_FIGARO_PRISONER, + CELES, + CYAN, + EDGAR, + GAU, + GOGO, + LOCKE, + MOG, + RELM, + SABIN, + SETZER, + SHADOW, + STRAGO, + TERRA, + UMARO, + UNGATED, +) +import menus.pregame_track_scroll_area as scroll_area +from data.text.text2 import text_value +import instruction.f0 as f0 + +groups = [ + ("Celes", CELES), + ("Cyan", CYAN), + ("Edgar", EDGAR), + ("Gau", GAU), + ("Gogo", GOGO), + ("Locke", LOCKE), + ("Mog", MOG), + ("Relm", RELM), + ("Sabin", SABIN), + ("Setzer", SETZER), + ("Shadow", SHADOW), + ("Strago", STRAGO), + ("Terra", TERRA), + ("Umaro", UMARO), + ("Ungated", UNGATED), +] + +nfce = [ + AUCTION1, + AUCTION2, + COLLAPSING_HOUSE, + FIGARO_CASTLE_THRONE, + GAUS_FATHERS_HOUSE, + KOHLINGEN_CAFE, + NARSHE_WEAPON_SHOP, + SEALED_GATE, + SOUTH_FIGARO_PRISONER, +] + +legacy = [ + FANATICS_TOWER_LEADER, + LONE_WOLF_MOOGLE_ROOM, + NARSHE_WEAPON_SHOP_MINES, +] +class FlagsForceRewardChecks(scroll_area.ScrollArea): + + def __init__(self, title, item_checks, is_nfce): + self.number_items = len(item_checks) + self.lines = [] + + self.lines.append(scroll_area.Line(title, f0.set_blue_text_color)) + + if is_nfce: + self.lines.append(scroll_area.Line("-------------------------", f0.set_user_text_color)) + self.lines.append(scroll_area.Line("No Free Characters Espers", f0.set_user_text_color)) + self.lines.append(scroll_area.Line("-------------------------", f0.set_user_text_color)) + for check in nfce: + self.lines.append(scroll_area.Line(f"{check.name}", f0.set_user_text_color)) + + self.lines.append(scroll_area.Line("", f0.set_user_text_color)) + + # Someone set the check rewards + if not (is_nfce): + check_lines = FlagsForceRewardChecks._format_check_list_menu(item_checks) + for check in check_lines: + self.lines.append(scroll_area.Line(f"{check}", f0.set_user_text_color)) + + super().__init__() + + def _format_check_list_menu(check_ids): + from constants.checks import check_name + check_lines = [] + + for (group_name, checks) in groups: + group_check_ids = [check.bit for check in checks] + group_checks = [check for check in check_ids if check in group_check_ids] + if len(group_checks) > 0: + check_lines.append(group_name) + for bit in group_checks: + check_lines.append(f"{' '}{check_name.get(bit)}") + check_lines.append("") + + return check_lines + + +class FlagsForceEsperRewardChecks(FlagsForceRewardChecks): + MENU_NUMBER = 16 + +class FlagsForceEsperItemRewardChecks(FlagsForceRewardChecks): + MENU_NUMBER = 17 + +class FlagsForceItemRewardChecks(FlagsForceRewardChecks): + MENU_NUMBER = 18 diff --git a/menus/track.py b/menus/track.py index 28fd2d3d..44854447 100644 --- a/menus/track.py +++ b/menus/track.py @@ -309,4 +309,4 @@ def mod(self): self.initialize_mod() self.sustain_mod() - self.menu_commands_jump_table() + self.menu_commands_jump_table() \ No newline at end of file