diff --git a/args/espers.py b/args/espers.py index 6cfbb466..9d097c5d 100644 --- a/args/espers.py +++ b/args/espers.py @@ -1,3 +1,12 @@ +from data.espers import Espers +from event.event_reward import CHARACTER_ESPER_ONLY_REWARDS + +# If all 27 espers are allocated at start, there will be logic errors when it comes to +# assigning characters to character/esper only checks. +# We would have to ensure that a character is assigned to the {6} char/esper only rewards. +# We could account for this in the logic, but it would gentrify the routing logic a bit much. +MAX_STARTING_ESPERS = Espers.ESPER_COUNT - CHARACTER_ESPER_ONLY_REWARDS + def name(): return "Espers" @@ -6,7 +15,13 @@ def parse(parser): from data.characters import Characters espers = parser.add_argument_group("Espers") + esper_start = espers.add_mutually_exclusive_group() + esper_start.add_argument("-stesp", "--starting-espers", default = [0, 0], type = int, + nargs = 2, metavar = ("MIN", "MAX"), choices = range(MAX_STARTING_ESPERS + 1), + help = "Party starts with %(metavar) random espers") + esper_spells = espers.add_mutually_exclusive_group() + esper_spells.add_argument("-esrr", "--esper-spells-random-rates", action = "store_true", help = "Original esper spells with random learn rates") esper_spells.add_argument("-ess", "--esper-spells-shuffle", action = "store_true", @@ -50,6 +65,7 @@ def parse(parser): help = "Espers can be summoned multiple times in battle") def process(args): + args._process_min_max("starting_espers") args._process_min_max("esper_spells_random") args._process_min_max("esper_mp_random_value") args._process_min_max("esper_mp_random_percent") @@ -66,6 +82,9 @@ def process(args): def flags(args): flags = "" + if args.starting_espers_min or args.starting_espers_max: + flags += f" -stesp {args.starting_espers_min} {args.starting_espers_max}" + if args.esper_spells_random_rates: flags += " -esrr" elif args.esper_spells_shuffle: @@ -133,6 +152,7 @@ def options(args): equipable = f"Balanced Random {args.esper_equipable_balanced_random_value}" result = [] + result.append(("Starting Espers", f"{args.starting_espers_min}-{args.starting_espers_max}")) result.append(("Spells", spells)) result.append(("Bonuses", bonuses)) if args.esper_bonuses_random: diff --git a/data/espers.py b/data/espers.py index 8d41a950..8f0007f4 100644 --- a/data/espers.py +++ b/data/espers.py @@ -45,6 +45,11 @@ def __init__(self, rom, args, spells, characters): self.espers.append(esper) self.available_espers = set(range(self.ESPER_COUNT)) + self.starting_espers = [] + + if args.starting_espers_min > 0: + count = random.randint(args.starting_espers_min, args.starting_espers_max) + self.starting_espers = [self.get_random_esper() for _esp in range(count)] def receive_dialogs_mod(self, dialogs): self.receive_dialogs = [1133, 1380, 1381, 1134, 1535, 1082, 1091, 1092, 1136, 1534, 2618, 1093, 1087,\ @@ -271,6 +276,9 @@ def mod(self, dialogs): if self.args.esper_spells_random_rates or self.args.esper_spells_shuffle_random_rates: self.randomize_rates() + if len(self.starting_espers): + self.randomize_rates() + if self.args.esper_spells_shuffle or self.args.esper_spells_shuffle_random_rates: self.shuffle_spells() elif self.args.esper_spells_random: @@ -345,8 +353,10 @@ def log(self): for entry_index in range(self.ESPER_COUNT): esper_index = self.esper_menu_order[entry_index] esper = self.espers[esper_index] + prefix = "*" if esper.id in self.starting_espers else "" + + entry = [f"{prefix}{esper.get_name():<{self.NAME_SIZE}} {esper.mp:>3} MP"] - entry = [f"{esper.get_name():<{self.NAME_SIZE}} {esper.mp:>3} MP"] for spell_index in range(esper.spell_count): spell_name = self.spells.get_name(esper.spells[spell_index].id) learn_rate = esper.spells[spell_index].rate @@ -376,6 +386,9 @@ def log(self): else: lentries.append(entry) + lentries.append("") + lentries.append("* = Starting Esper") + section_entries("Espers", lentries, rentries) def print(self): diff --git a/event/event_reward.py b/event/event_reward.py index 0334886d..f1063e2e 100644 --- a/event/event_reward.py +++ b/event/event_reward.py @@ -6,6 +6,7 @@ class RewardType(Flag): ESPER = auto() ITEM = auto() +CHARACTER_ESPER_ONLY_REWARDS = 6 class Reward: def __init__(self, event, possible_types): self.id = None diff --git a/event/events.py b/event/events.py index cb0887b7..f1d40477 100644 --- a/event/events.py +++ b/event/events.py @@ -1,5 +1,5 @@ from memory.space import Bank, Allocate -from event.event_reward import RewardType, Reward, choose_reward, weighted_reward_choice +from event.event_reward import CHARACTER_ESPER_ONLY_REWARDS, RewardType, choose_reward, weighted_reward_choice import instruction.field as field class Events(): @@ -15,7 +15,9 @@ def __init__(self, rom, args, data): self.espers = data.espers self.shops = data.shops - self.mod() + events = self.mod() + + self.validate(events) def mod(self): # generate list of events from files @@ -58,6 +60,8 @@ def mod(self): from log import section section("Events", log_strings, []) + return events + def init_reward_slots(self, events): import random reward_slots = [] @@ -161,3 +165,10 @@ def open_world_mod(self, events): # choose the rest of the rewards, items given to events after all characters/events assigned self.choose_item_possible_rewards(reward_slots) + + def validate(self, events): + char_esper_checks = [] + for event in events: + char_esper_checks += [r for r in event.rewards if r.possible_types == (RewardType.CHARACTER | RewardType.ESPER)] + + assert len(char_esper_checks) == CHARACTER_ESPER_ONLY_REWARDS, "Number of char/esper only checks changed - Check usages of CHARACTER_ESPER_ONLY_REWARDS and ensure no breaking changes" \ No newline at end of file diff --git a/event/start.py b/event/start.py index 80f35b42..320ef8a2 100644 --- a/event/start.py +++ b/event/start.py @@ -63,6 +63,7 @@ def mod(self): self.intro_loop_mod() self.init_characters_mod() self.start_party_mod() + self.start_esper_mod() self.start_gold_mod() self.start_items_mod() self.start_game_mod() @@ -73,6 +74,7 @@ def mod(self): field.Call(self.event_bit_init), field.Call(self.character_init), field.Call(self.start_party), + field.Call(self.start_esper), field.Call(self.start_gold), field.Call(self.start_items), field.Call(self.start_game), @@ -142,6 +144,21 @@ def start_party_mod(self): space = Write(Bank.CC, src, "start party") self.start_party = space.start_address + def start_esper_mod(self): + src = [] + + for esper_id in self.espers.starting_espers: + src += [ + field.AddEsper(esper_id, sound_effect = False) + ] + + src += [ + field.Return() + ] + + space = Write(Bank.CC, src, "start espers") + self.start_esper = space.start_address + def start_gold_mod(self): gold = self.args.gold if self.args.debug: