lufia2ac: rename starting_capsule/starting_party options to default_capsule/default_party

This commit is contained in:
el-u 2022-12-18 14:44:20 +01:00 committed by Fabian Dill
parent ff9c11d772
commit 4cfc73b582
3 changed files with 90 additions and 90 deletions

View File

@ -242,6 +242,81 @@ class CrowdedFloorChance(Range):
default = 16
class DefaultCapsule(Choice):
"""Preselect the active capsule monster.
(Only has an effect if shuffle_capsule_monsters is set to false.)
Supported values: jelze, flash, gusto, zeppy, darbi, sully, blaze
Default value: jelze
"""
display_name = "Default capsule monster"
option_jelze = 0x00
option_flash = 0x01
option_gusto = 0x02
option_zeppy = 0x03
option_darbi = 0x04
option_sully = 0x05
option_blaze = 0x06
default = option_jelze
class DefaultParty(RandomGroupsChoice, TextChoice):
"""Preselect the party lineup.
(Only has an effect if shuffle_party_members is set to false.)
Supported values:
Can be set to any valid combination of up to 4 party member initials, e.g.:
M Maxim
DGMA Dekar, Guy, Maxim, and Arty
MSTL Maxim, Selan, Tia, and Lexis
random-2p a random 2-person party
random-3p a random 3-person party
random-4p a random 4-person party
Default value: M
"""
display_name = "Default party lineup"
default = "M"
random_groups = {
"random-2p": ["M" + "".join(p) for p in combinations("ADGLST", 1)],
"random-3p": ["M" + "".join(p) for p in combinations("ADGLST", 2)],
"random-4p": ["M" + "".join(p) for p in combinations("ADGLST", 3)],
}
vars().update({f"option_{party}": party for party in (*random_groups, "M", *chain(*random_groups.values()))})
_valid_sorted_parties: List[List[str]] = [sorted(party) for party in ("M", *chain(*random_groups.values()))]
_members_to_bytes: bytes = bytes.maketrans(b"MSGATDL", bytes(range(7)))
def verify(self, *args, **kwargs) -> None:
if str(self.value).lower() in self.random_groups:
return
if sorted(str(self.value).upper()) in self._valid_sorted_parties:
return
raise ValueError(f"Could not find option '{self.value}' for '{self.__class__.__name__}', known options are:\n"
f"{', '.join(self.random_groups)}, {', '.join(('M', *chain(*self.random_groups.values())))} "
"as well as all permutations of these.")
@staticmethod
def _flip(i: int) -> int:
return {4: 5, 5: 4}.get(i, i)
@property
def event_script(self) -> bytes:
return bytes((*(b for i in bytes(self) if i != 0 for b in (0x2B, i, 0x2E, i + 0x65, 0x1A, self._flip(i) + 1)),
0x1E, 0x0B, len(self) - 1, 0x1C, 0x86, 0x03, *(0x00,) * (6 * (4 - len(self)))))
@property
def roster(self) -> bytes:
return bytes((len(self), *bytes(self), *(0xFF,) * (4 - len(self))))
def __bytes__(self) -> bytes:
return str(self.value).upper().encode("ASCII").translate(self._members_to_bytes)
def __len__(self) -> int:
return len(str(self.value))
class FinalFloor(Range):
"""The final floor, where the boss resides.
@ -439,81 +514,6 @@ class ShufflePartyMembers(Toggle):
return 0b00000000 if self.value else 0b11111100
class StartingCapsule(Choice):
"""The capsule monster you start the game with.
Only has an effect if shuffle_capsule_monsters is set to false.
Supported values: jelze, flash, gusto, zeppy, darbi, sully, blaze
Default value: jelze
"""
display_name = "Starting capsule monster"
option_jelze = 0x00
option_flash = 0x01
option_gusto = 0x02
option_zeppy = 0x03
option_darbi = 0x04
option_sully = 0x05
option_blaze = 0x06
default = option_jelze
class StartingParty(RandomGroupsChoice, TextChoice):
"""The party you start the game with.
Only has an effect if shuffle_party_members is set to false.
Supported values:
Can be set to any valid combination of up to 4 party member initials, e.g.:
M start with Maxim
DGMA start with Dekar, Guy, Maxim, and Arty
MSTL start with Maxim, Selan, Tia, and Lexis
random-2p a random 2-person party
random-3p a random 3-person party
random-4p a random 4-person party
Default value: M
"""
display_name = "Starting party"
default = "M"
random_groups = {
"random-2p": ["M" + "".join(p) for p in combinations("ADGLST", 1)],
"random-3p": ["M" + "".join(p) for p in combinations("ADGLST", 2)],
"random-4p": ["M" + "".join(p) for p in combinations("ADGLST", 3)],
}
vars().update({f"option_{party}": party for party in (*random_groups, "M", *chain(*random_groups.values()))})
_valid_sorted_parties: List[List[str]] = [sorted(party) for party in ("M", *chain(*random_groups.values()))]
_members_to_bytes: bytes = bytes.maketrans(b"MSGATDL", bytes(range(7)))
def verify(self, *args, **kwargs) -> None:
if str(self.value).lower() in self.random_groups:
return
if sorted(str(self.value).upper()) in self._valid_sorted_parties:
return
raise ValueError(f"Could not find option '{self.value}' for '{self.__class__.__name__}', known options are:\n"
f"{', '.join(self.random_groups)}, {', '.join(('M', *chain(*self.random_groups.values())))} "
"as well as all permutations of these.")
@staticmethod
def _flip(i: int) -> int:
return {4: 5, 5: 4}.get(i, i)
@property
def event_script(self) -> bytes:
return bytes((*(b for i in bytes(self) if i != 0 for b in (0x2B, i, 0x2E, i + 0x65, 0x1A, self._flip(i) + 1)),
0x1E, 0x0B, len(self) - 1, 0x1C, 0x86, 0x03, *(0x00,) * (6 * (4 - len(self)))))
@property
def roster(self) -> bytes:
return bytes((len(self), *bytes(self), *(0xFF,) * (4 - len(self))))
def __bytes__(self) -> bytes:
return str(self.value).upper().encode("ASCII").translate(self._members_to_bytes)
def __len__(self) -> int:
return len(str(self.value))
l2ac_option_definitions: Dict[str, type(Option)] = {
"blue_chest_chance": BlueChestChance,
"blue_chest_count": BlueChestCount,
@ -523,6 +523,8 @@ l2ac_option_definitions: Dict[str, type(Option)] = {
"capsule_starting_level": CapsuleStartingLevel,
"crowded_floor_chance": CrowdedFloorChance,
"death_link": DeathLink,
"default_capsule": DefaultCapsule,
"default_party": DefaultParty,
"final_floor": FinalFloor,
"gear_variety_after_b9": GearVarietyAfterB9,
"goal": Goal,
@ -535,6 +537,4 @@ l2ac_option_definitions: Dict[str, type(Option)] = {
"run_speed": RunSpeed,
"shuffle_capsule_monsters": ShuffleCapsuleMonsters,
"shuffle_party_members": ShufflePartyMembers,
"starting_capsule": StartingCapsule,
"starting_party": StartingParty,
}

View File

@ -12,8 +12,8 @@ from worlds.generic.Rules import add_rule, set_rule
from .Client import L2ACSNIClient # noqa: F401
from .Items import ItemData, ItemType, l2ac_item_name_to_id, l2ac_item_table, L2ACItem, start_id as items_start_id
from .Locations import l2ac_location_name_to_id, L2ACLocation
from .Options import Boss, CapsuleStartingForm, CapsuleStartingLevel, Goal, l2ac_option_definitions, MasterHp, \
PartyStartingLevel, ShuffleCapsuleMonsters, ShufflePartyMembers, StartingParty
from .Options import Boss, CapsuleStartingForm, CapsuleStartingLevel, DefaultParty, Goal, l2ac_option_definitions, \
MasterHp, PartyStartingLevel, ShuffleCapsuleMonsters, ShufflePartyMembers
from .Rom import get_base_rom_bytes, get_base_rom_path, L2ACDeltaPatch
from .basepatch import apply_basepatch
@ -64,6 +64,8 @@ class L2ACWorld(World):
capsule_starting_level: Optional[CapsuleStartingLevel]
crowded_floor_chance: Optional[int]
death_link: Optional[int]
default_capsule: Optional[int]
default_party: Optional[DefaultParty]
final_floor: Optional[int]
gear_variety_after_b9: Optional[int]
goal: Optional[int]
@ -76,8 +78,6 @@ class L2ACWorld(World):
run_speed: Optional[int]
shuffle_capsule_monsters: Optional[ShuffleCapsuleMonsters]
shuffle_party_members: Optional[ShufflePartyMembers]
starting_capsule: Optional[int]
starting_party: Optional[StartingParty]
@classmethod
def stage_assert_generate(cls, _multiworld: MultiWorld) -> None:
@ -103,6 +103,8 @@ class L2ACWorld(World):
self.capsule_starting_level = self.multiworld.capsule_starting_level[self.player]
self.crowded_floor_chance = self.multiworld.crowded_floor_chance[self.player].value
self.death_link = self.multiworld.death_link[self.player].value
self.default_capsule = self.multiworld.default_capsule[self.player].value
self.default_party = self.multiworld.default_party[self.player]
self.final_floor = self.multiworld.final_floor[self.player].value
self.gear_variety_after_b9 = self.multiworld.gear_variety_after_b9[self.player].value
self.goal = self.multiworld.goal[self.player].value
@ -115,8 +117,6 @@ class L2ACWorld(World):
self.run_speed = self.multiworld.run_speed[self.player].value
self.shuffle_capsule_monsters = self.multiworld.shuffle_capsule_monsters[self.player]
self.shuffle_party_members = self.multiworld.shuffle_party_members[self.player]
self.starting_capsule = self.multiworld.starting_capsule[self.player].value
self.starting_party = self.multiworld.starting_party[self.player]
if self.capsule_starting_level.value == CapsuleStartingLevel.special_range_names["party_starting_level"]:
self.capsule_starting_level.value = self.party_starting_level.value
@ -125,7 +125,7 @@ class L2ACWorld(World):
if self.master_hp == MasterHp.special_range_names["scale"]:
self.master_hp = MasterHp.scale(self.final_floor)
if self.shuffle_party_members:
self.starting_party.value = StartingParty.default
self.default_party.value = DefaultParty.default
def create_regions(self) -> None:
menu = Region("Menu", RegionType.Generic, "Menu", self.player, self.multiworld)
@ -234,13 +234,13 @@ class L2ACWorld(World):
rom_bytearray[0x019E82:0x019E82 + 1] = self.final_floor.to_bytes(1, "little")
rom_bytearray[0x01FC75:0x01FC75 + 1] = self.run_speed.to_bytes(1, "little")
rom_bytearray[0x01FC81:0x01FC81 + 1] = self.run_speed.to_bytes(1, "little")
rom_bytearray[0x02B2A1:0x02B2A1 + 5] = self.starting_party.roster
rom_bytearray[0x02B2A1:0x02B2A1 + 5] = self.default_party.roster
for offset in range(0x02B395, 0x02B452, 0x1B):
rom_bytearray[offset:offset + 1] = self.party_starting_level.value.to_bytes(1, "little")
for offset in range(0x02B39A, 0x02B457, 0x1B):
rom_bytearray[offset:offset + 3] = self.party_starting_level.xp.to_bytes(3, "little")
rom_bytearray[0x05699E:0x05699E + 147] = self.get_goal_text_bytes()
rom_bytearray[0x056AA3:0x056AA3 + 24] = self.starting_party.event_script
rom_bytearray[0x056AA3:0x056AA3 + 24] = self.default_party.event_script
rom_bytearray[0x072742:0x072742 + 1] = self.boss.value.to_bytes(1, "little")
rom_bytearray[0x072748:0x072748 + 1] = self.boss.flag.to_bytes(1, "little")
rom_bytearray[0x09D59B:0x09D59B + 256] = self.get_node_connection_table()
@ -248,7 +248,7 @@ class L2ACWorld(World):
rom_bytearray[0x280010:0x280010 + 2] = self.blue_chest_count.to_bytes(2, "little")
rom_bytearray[0x280012:0x280012 + 3] = self.capsule_starting_level.xp.to_bytes(3, "little")
rom_bytearray[0x280015:0x280015 + 1] = self.initial_floor.to_bytes(1, "little")
rom_bytearray[0x280016:0x280016 + 1] = self.starting_capsule.to_bytes(1, "little")
rom_bytearray[0x280016:0x280016 + 1] = self.default_capsule.to_bytes(1, "little")
rom_bytearray[0x280017:0x280017 + 1] = self.iris_treasures_required.to_bytes(1, "little")
rom_bytearray[0x280018:0x280018 + 1] = self.shuffle_party_members.unlock.to_bytes(1, "little")
rom_bytearray[0x280019:0x280019 + 1] = self.shuffle_capsule_monsters.unlock.to_bytes(1, "little")

View File

@ -42,9 +42,9 @@ Your Party Leader will hold up the item they received when not in a fight or in
Retrieve a (customizable) number of iris treasures from the cave; 4) Retrieve the iris treasures *and* defeat the boss
- You can also randomize the goal; The blue-haired NPC in front of the cafe can tell you about the selected objective
- Customize (or randomize) the chances of encountering blue chests, healing tiles, iris treasures, etc.
- Customize (or randomize) your starting party members and/or party level
- Customize (or randomize) your starting capsule monster and/or capsule monster level as well as form
- Customize (or randomize) the initial and/or final floor numbers
- Customize (or randomize) the default party lineup and capsule monster
- Customize (or randomize) the party starting level as well as capsule monster level and form
- Customize (or randomize) the initial and final floor numbers
- Customize (or randomize) the boss that resides on the final floor
- Customize start inventory, i.e., begin every run with certain items or spells of your choice
- Option to shuffle your party members and/or capsule monsters into the multiworld, meaning that someone will have to