From f6da81ac7044cb0d40ddc89c9e7008a408cdbe23 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sat, 6 Aug 2022 00:49:54 +0200 Subject: [PATCH] Core: cleanup Item classes (#849) --- BaseClasses.py | 24 +++++++----------------- worlds/alttp/Items.py | 7 ++++++- worlds/alttp/Rom.py | 25 ++++++++++++++++--------- worlds/alttp/SubClasses.py | 13 +++++-------- worlds/alttp/__init__.py | 15 +++++++++++++-- worlds/archipidle/__init__.py | 7 +++++-- worlds/hk/__init__.py | 3 ++- worlds/meritous/Items.py | 23 +++++++++-------------- worlds/oot/Items.py | 15 +-------------- worlds/sa2b/Items.py | 7 ++++--- worlds/sm/__init__.py | 5 +++-- worlds/smz3/__init__.py | 6 ++++-- 12 files changed, 75 insertions(+), 75 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 66e96824..b550569e 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1144,31 +1144,21 @@ class ItemClassification(IntFlag): class Item: - location: Optional[Location] = None - code: Optional[int] = None # an item with ID None is called an Event, and does not get written to multidata - name: str game: str = "Generic" - type: str = None + __slots__ = ("name", "classification", "code", "player", "location") + name: str classification: ItemClassification - - # need to find a decent place for these to live and to allow other games to register texts if they want. - pedestal_credit_text: str = "and the Unknown Item" - sickkid_credit_text: Optional[str] = None - magicshop_credit_text: Optional[str] = None - zora_credit_text: Optional[str] = None - fluteboy_credit_text: Optional[str] = None - - # hopefully temporary attributes to satisfy legacy LttP code, proper implementation in subclass ALttPItem - smallkey: bool = False - bigkey: bool = False - map: bool = False - compass: bool = False + code: Optional[int] + """an item with code None is called an Event, and does not get written to multidata""" + player: int + location: Optional[Location] def __init__(self, name: str, classification: ItemClassification, code: Optional[int], player: int): self.name = name self.classification = classification self.player = player self.code = code + self.location = None @property def hint_text(self) -> str: diff --git a/worlds/alttp/Items.py b/worlds/alttp/Items.py index d1d372bf..3663db5c 100644 --- a/worlds/alttp/Items.py +++ b/worlds/alttp/Items.py @@ -51,6 +51,11 @@ class ItemData(typing.NamedTuple): flute_boy_credit: typing.Optional[str] hint_text: typing.Optional[str] + def as_init_dict(self) -> typing.Dict[str, typing.Any]: + return {key: getattr(self, key) for key in + ('classification', 'type', 'item_code', 'pedestal_hint', 'hint_text')} + + # Format: Name: (Advancement, Type, ItemCode, Pedestal Hint Text, Pedestal Credit Text, Sick Kid Credit Text, Zora Credit Text, Witch Credit Text, Flute Boy Credit Text, Hint Text) item_table = {'Bow': ItemData(IC.progression, None, 0x0B, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the Bow'), 'Progressive Bow': ItemData(IC.progression, None, 0x64, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), @@ -218,7 +223,7 @@ item_table = {'Bow': ItemData(IC.progression, None, 0x0B, 'You have\nchosen the\ 'Open Floodgate': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None), } -as_dict_item_table = {name: data._asdict() for name, data in item_table.items()} +item_init_table = {name: data.as_init_dict() for name, data in item_table.items()} progression_mapping = { "Golden Sword": ("Progressive Sword", 4), diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index c16bbf53..dd5cc8c4 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -2091,7 +2091,9 @@ def write_string_to_rom(rom, target, string): def write_strings(rom, world, player): + from . import ALTTPWorld local_random = world.slot_seeds[player] + w: ALTTPWorld = world.worlds[player] tt = TextTable() tt.removeUnwantedText() @@ -2420,7 +2422,8 @@ def write_strings(rom, world, player): pedestal_text = 'Some Hot Air' if pedestalitem is None else hint_text(pedestalitem, True) if pedestalitem.pedestal_hint_text is not None else 'Unknown Item' tt['mastersword_pedestal_translated'] = pedestal_text - pedestal_credit_text = 'and the Hot Air' if pedestalitem is None else pedestalitem.pedestal_credit_text if pedestalitem.pedestal_credit_text is not None else 'and the Unknown Item' + pedestal_credit_text = 'and the Hot Air' if pedestalitem is None else \ + w.pedestal_credit_texts.get(pedestalitem.code, 'and the Unknown Item') etheritem = world.get_location('Ether Tablet', player).item ether_text = 'Some Hot Air' if etheritem is None else hint_text(etheritem, @@ -2448,20 +2451,24 @@ def write_strings(rom, world, player): credits = Credits() sickkiditem = world.get_location('Sick Kid', player).item - sickkiditem_text = local_random.choice( - SickKid_texts) if sickkiditem is None or sickkiditem.sickkid_credit_text is None else sickkiditem.sickkid_credit_text + sickkiditem_text = local_random.choice(SickKid_texts) \ + if sickkiditem is None or sickkiditem.code not in w.sickkid_credit_texts \ + else w.sickkid_credit_texts[sickkiditem.code] zoraitem = world.get_location('King Zora', player).item - zoraitem_text = local_random.choice( - Zora_texts) if zoraitem is None or zoraitem.zora_credit_text is None else zoraitem.zora_credit_text + zoraitem_text = local_random.choice(Zora_texts) \ + if zoraitem is None or zoraitem.code not in w.zora_credit_texts \ + else w.zora_credit_texts[zoraitem.code] magicshopitem = world.get_location('Potion Shop', player).item - magicshopitem_text = local_random.choice( - MagicShop_texts) if magicshopitem is None or magicshopitem.magicshop_credit_text is None else magicshopitem.magicshop_credit_text + magicshopitem_text = local_random.choice(MagicShop_texts) \ + if magicshopitem is None or magicshopitem.code not in w.magicshop_credit_texts \ + else w.magicshop_credit_texts[magicshopitem.code] fluteboyitem = world.get_location('Flute Spot', player).item - fluteboyitem_text = local_random.choice( - FluteBoy_texts) if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text + fluteboyitem_text = local_random.choice(FluteBoy_texts) \ + if fluteboyitem is None or fluteboyitem.code not in w.fluteboy_credit_texts \ + else w.fluteboy_credit_texts[fluteboyitem.code] credits.update_credits_line('castle', 0, local_random.choice(KingsReturn_texts)) credits.update_credits_line('sanctuary', 0, local_random.choice(Sanctuary_texts)) diff --git a/worlds/alttp/SubClasses.py b/worlds/alttp/SubClasses.py index 87f10f48..b933c074 100644 --- a/worlds/alttp/SubClasses.py +++ b/worlds/alttp/SubClasses.py @@ -18,19 +18,16 @@ class ALttPLocation(Location): class ALttPItem(Item): game: str = "A Link to the Past" + type: Optional[str] + _pedestal_hint_text: Optional[str] + _hint_text: Optional[str] dungeon = None - def __init__(self, name, player, classification=ItemClassification.filler, type=None, item_code=None, pedestal_hint=None, - pedestal_credit=None, sick_kid_credit=None, zora_credit=None, witch_credit=None, - flute_boy_credit=None, hint_text=None): + def __init__(self, name, player, classification=ItemClassification.filler, type=None, item_code=None, + pedestal_hint=None, hint_text=None): super(ALttPItem, self).__init__(name, classification, item_code, player) self.type = type self._pedestal_hint_text = pedestal_hint - self.pedestal_credit_text = pedestal_credit - self.sickkid_credit_text = sick_kid_credit - self.zora_credit_text = zora_credit - self.magicshop_credit_text = witch_credit - self.fluteboy_credit_text = flute_boy_credit self._hint_text = hint_text @property diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index 871c4468..64b1bf8d 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -8,7 +8,7 @@ from BaseClasses import Item, CollectionState, Tutorial from .SubClasses import ALttPItem from ..AutoWorld import World, WebWorld, LogicMixin from .Options import alttp_options, smallkey_shuffle -from .Items import as_dict_item_table, item_name_groups, item_table, GetBeemizerItem +from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions from .Rules import set_rules from .ItemPool import generate_itempool, difficulties @@ -124,6 +124,17 @@ class ALTTPWorld(World): required_client_version = (0, 3, 2) web = ALTTPWeb() + pedestal_credit_texts: typing.Dict[int, str] = \ + {data.item_code: data.pedestal_credit for data in item_table.values() if data.pedestal_credit} + sickkid_credit_texts: typing.Dict[int, str] = \ + {data.item_code: data.sick_kid_credit for data in item_table.values() if data.sick_kid_credit} + zora_credit_texts: typing.Dict[int, str] = \ + {data.item_code: data.zora_credit for data in item_table.values() if data.zora_credit} + magicshop_credit_texts: typing.Dict[int, str] = \ + {data.item_code: data.witch_credit for data in item_table.values() if data.witch_credit} + fluteboy_credit_texts: typing.Dict[int, str] = \ + {data.item_code: data.flute_boy_credit for data in item_table.values() if data.flute_boy_credit} + set_rules = set_rules create_items = generate_itempool @@ -400,7 +411,7 @@ class ALTTPWorld(World): multidata["connect_names"][new_name] = multidata["connect_names"][self.world.player_name[self.player]] def create_item(self, name: str) -> Item: - return ALttPItem(name, self.player, **as_dict_item_table[name]) + return ALttPItem(name, self.player, **item_init_table[name]) @classmethod def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool, diff --git a/worlds/archipidle/__init__.py b/worlds/archipidle/__init__.py index 0ddb8248..8b1061b5 100644 --- a/worlds/archipidle/__init__.py +++ b/worlds/archipidle/__init__.py @@ -47,13 +47,12 @@ class ArchipIDLEWorld(World): item_pool = [] for i in range(100): - item = Item( + item = ArchipIDLEItem( item_table_copy[i], ItemClassification.progression if i < 20 else ItemClassification.filler, self.item_name_to_id[item_table_copy[i]], self.player ) - item.game = 'ArchipIDLE' item_pool.append(item) self.world.itempool += item_pool @@ -93,6 +92,10 @@ def create_region(world: MultiWorld, player: int, name: str, locations=None, exi return region +class ArchipIDLEItem(Item): + game = "ArchipIDLE" + + class ArchipIDLELocation(Location): game: str = "ArchipIDLE" diff --git a/worlds/hk/__init__.py b/worlds/hk/__init__.py index bc9b2951..6869e14b 100644 --- a/worlds/hk/__init__.py +++ b/worlds/hk/__init__.py @@ -632,8 +632,9 @@ class HKLocation(Location): class HKItem(Item): game = "Hollow Knight" + type: str - def __init__(self, name, advancement, code, type, player: int = None): + def __init__(self, name, advancement, code, type: str, player: int = None): if name == "Mimic_Grub": classification = ItemClassification.trap elif type in ("Grub", "DreamWarrior", "Root", "Egg"): diff --git a/worlds/meritous/Items.py b/worlds/meritous/Items.py index 1b5228e5..9f28c5d1 100644 --- a/worlds/meritous/Items.py +++ b/worlds/meritous/Items.py @@ -6,14 +6,9 @@ import typing from BaseClasses import Item, ItemClassification +from worlds.alttp import ALTTPWorld -# pedestal_credit_text: str = "and the Unknown Item" -# sickkid_credit_text: Optional[str] = None -# magicshop_credit_text: Optional[str] = None -# zora_credit_text: Optional[str] = None -# fluteboy_credit_text: Optional[str] = None - class MeritousLttPText(typing.NamedTuple): pedestal: typing.Optional[str] sickkid: typing.Optional[str] @@ -143,6 +138,7 @@ LttPCreditsText = { class MeritousItem(Item): game: str = "Meritous" + type: str def __init__(self, name, advancement, code, player): super(MeritousItem, self).__init__(name, @@ -171,14 +167,6 @@ class MeritousItem(Item): self.type = "Artifact" self.classification = ItemClassification.useful - if name in LttPCreditsText: - lttp = LttPCreditsText[name] - self.pedestal_credit_text = f"and the {lttp.pedestal}" - self.sickkid_credit_text = lttp.sickkid - self.magicshop_credit_text = lttp.magicshop - self.zora_credit_text = lttp.zora - self.fluteboy_credit_text = lttp.fluteboy - offset = 593_000 @@ -217,3 +205,10 @@ item_groups = { "Important Artifacts": ["Shield Boost", "Circuit Booster", "Metabolism", "Dodge Enhancer"], "Crystals": ["Crystals x500", "Crystals x1000", "Crystals x2000"] } + +ALTTPWorld.pedestal_credit_texts.update({item_table[name]: f"and the {texts.pedestal}" + for name, texts in LttPCreditsText.items()}) +ALTTPWorld.sickkid_credit_texts.update({item_table[name]: texts.sickkid for name, texts in LttPCreditsText.items()}) +ALTTPWorld.magicshop_credit_texts.update({item_table[name]: texts.magicshop for name, texts in LttPCreditsText.items()}) +ALTTPWorld.zora_credit_texts.update({item_table[name]: texts.zora for name, texts in LttPCreditsText.items()}) +ALTTPWorld.fluteboy_credit_texts.update({item_table[name]: texts.fluteboy for name, texts in LttPCreditsText.items()}) diff --git a/worlds/oot/Items.py b/worlds/oot/Items.py index f07d91a8..b4c07197 100644 --- a/worlds/oot/Items.py +++ b/worlds/oot/Items.py @@ -24,6 +24,7 @@ def ap_id_to_oot_data(ap_id): class OOTItem(Item): game: str = "Ocarina of Time" + type: str def __init__(self, name, player, data, event, force_not_advancement): (type, advancement, index, special) = data @@ -38,7 +39,6 @@ class OOTItem(Item): classification = ItemClassification.progression else: classification = ItemClassification.filler - adv = bool(advancement) and not force_not_advancement super(OOTItem, self).__init__(name, classification, oot_data_to_ap_id(data, event), player) self.type = type self.index = index @@ -46,25 +46,12 @@ class OOTItem(Item): self.looks_like_item = None self.price = special.get('price', None) if special else None self.internal = False - - # The playthrough calculation calls a function that uses "sweep_for_events(key_only=True)" - # This checks if the item it's looking for is a small key, using the small key property. - # Because of overlapping item fields, this means that OoT small keys are technically counted, unless we do this. - # This causes them to be double-collected during playthrough and generation. - @property - def smallkey(self) -> bool: - return False - - @property - def bigkey(self) -> bool: - return False @property def dungeonitem(self) -> bool: return self.type in ['SmallKey', 'HideoutSmallKey', 'BossKey', 'GanonBossKey', 'Map', 'Compass'] - # Progressive: True -> Advancement # False -> Priority # None -> Normal diff --git a/worlds/sa2b/Items.py b/worlds/sa2b/Items.py index bebfa44c..d11178f5 100644 --- a/worlds/sa2b/Items.py +++ b/worlds/sa2b/Items.py @@ -2,6 +2,7 @@ import typing from BaseClasses import Item, ItemClassification from .Names import ItemName +from worlds.alttp import ALTTPWorld class ItemData(typing.NamedTuple): @@ -18,9 +19,6 @@ class SA2BItem(Item): def __init__(self, name, classification: ItemClassification, code: int = None, player: int = None): super(SA2BItem, self).__init__(name, classification, code, player) - if self.name == ItemName.sonic_light_shoes or self.name == ItemName.shadow_air_shoes: - self.pedestal_credit_text = "and the Soap Shoes" - # Separate tables for each type of item. emblems_table = { @@ -94,3 +92,6 @@ item_table = { } lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code} + +ALTTPWorld.pedestal_credit_texts[item_table[ItemName.sonic_light_shoes].code] = "and the Soap Shoes" +ALTTPWorld.pedestal_credit_texts[item_table[ItemName.shadow_air_shoes].code] = "and the Soap Shoes" diff --git a/worlds/sm/__init__.py b/worlds/sm/__init__.py index a7445c01..d2728132 100644 --- a/worlds/sm/__init__.py +++ b/worlds/sm/__init__.py @@ -5,7 +5,7 @@ import copy import os import threading import base64 -from typing import Set, List, TextIO +from typing import Set, TextIO from worlds.sm.variaRandomizer.graph.graph_utils import GraphUtils @@ -735,7 +735,8 @@ class SMLocation(Location): class SMItem(Item): game = "Super Metroid" + type: str - def __init__(self, name, classification, type, code, player: int = None): + def __init__(self, name, classification, type: str, code, player: int): super(SMItem, self).__init__(name, classification, code, player) self.type = type diff --git a/worlds/smz3/__init__.py b/worlds/smz3/__init__.py index 58e8b656..fcedfd45 100644 --- a/worlds/smz3/__init__.py +++ b/worlds/smz3/__init__.py @@ -573,8 +573,10 @@ class SMZ3Location(Location): class SMZ3Item(Item): game = "SMZ3" + type: ItemType + item: Item - def __init__(self, name, classification, type, code, player: int = None, item=None): + def __init__(self, name, classification, type: ItemType, code, player: int, item: Item): + super(SMZ3Item, self).__init__(name, classification, code, player) self.type = type self.item = item - super(SMZ3Item, self).__init__(name, classification, code, player)