Core: cleanup Item classes (#849)

This commit is contained in:
black-sliver 2022-08-06 00:49:54 +02:00 committed by GitHub
parent dd6e212519
commit f6da81ac70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 75 additions and 75 deletions

View File

@ -1144,31 +1144,21 @@ class ItemClassification(IntFlag):
class Item: 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" game: str = "Generic"
type: str = None __slots__ = ("name", "classification", "code", "player", "location")
name: str
classification: ItemClassification classification: ItemClassification
code: Optional[int]
# need to find a decent place for these to live and to allow other games to register texts if they want. """an item with code None is called an Event, and does not get written to multidata"""
pedestal_credit_text: str = "and the Unknown Item" player: int
sickkid_credit_text: Optional[str] = None location: Optional[Location]
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
def __init__(self, name: str, classification: ItemClassification, code: Optional[int], player: int): def __init__(self, name: str, classification: ItemClassification, code: Optional[int], player: int):
self.name = name self.name = name
self.classification = classification self.classification = classification
self.player = player self.player = player
self.code = code self.code = code
self.location = None
@property @property
def hint_text(self) -> str: def hint_text(self) -> str:

View File

@ -51,6 +51,11 @@ class ItemData(typing.NamedTuple):
flute_boy_credit: typing.Optional[str] flute_boy_credit: typing.Optional[str]
hint_text: 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) # 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'), 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'), '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), '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 = { progression_mapping = {
"Golden Sword": ("Progressive Sword", 4), "Golden Sword": ("Progressive Sword", 4),

View File

@ -2091,7 +2091,9 @@ def write_string_to_rom(rom, target, string):
def write_strings(rom, world, player): def write_strings(rom, world, player):
from . import ALTTPWorld
local_random = world.slot_seeds[player] local_random = world.slot_seeds[player]
w: ALTTPWorld = world.worlds[player]
tt = TextTable() tt = TextTable()
tt.removeUnwantedText() 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, 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' True) if pedestalitem.pedestal_hint_text is not None else 'Unknown Item'
tt['mastersword_pedestal_translated'] = pedestal_text 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 etheritem = world.get_location('Ether Tablet', player).item
ether_text = 'Some Hot Air' if etheritem is None else hint_text(etheritem, 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() credits = Credits()
sickkiditem = world.get_location('Sick Kid', player).item sickkiditem = world.get_location('Sick Kid', player).item
sickkiditem_text = local_random.choice( sickkiditem_text = local_random.choice(SickKid_texts) \
SickKid_texts) if sickkiditem is None or sickkiditem.sickkid_credit_text is None else sickkiditem.sickkid_credit_text 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 = world.get_location('King Zora', player).item
zoraitem_text = local_random.choice( zoraitem_text = local_random.choice(Zora_texts) \
Zora_texts) if zoraitem is None or zoraitem.zora_credit_text is None else zoraitem.zora_credit_text 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 = world.get_location('Potion Shop', player).item
magicshopitem_text = local_random.choice( magicshopitem_text = local_random.choice(MagicShop_texts) \
MagicShop_texts) if magicshopitem is None or magicshopitem.magicshop_credit_text is None else magicshopitem.magicshop_credit_text 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 = world.get_location('Flute Spot', player).item
fluteboyitem_text = local_random.choice( fluteboyitem_text = local_random.choice(FluteBoy_texts) \
FluteBoy_texts) if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text 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('castle', 0, local_random.choice(KingsReturn_texts))
credits.update_credits_line('sanctuary', 0, local_random.choice(Sanctuary_texts)) credits.update_credits_line('sanctuary', 0, local_random.choice(Sanctuary_texts))

View File

@ -18,19 +18,16 @@ class ALttPLocation(Location):
class ALttPItem(Item): class ALttPItem(Item):
game: str = "A Link to the Past" game: str = "A Link to the Past"
type: Optional[str]
_pedestal_hint_text: Optional[str]
_hint_text: Optional[str]
dungeon = None dungeon = None
def __init__(self, name, player, classification=ItemClassification.filler, type=None, item_code=None, pedestal_hint=None, def __init__(self, name, player, classification=ItemClassification.filler, type=None, item_code=None,
pedestal_credit=None, sick_kid_credit=None, zora_credit=None, witch_credit=None, pedestal_hint=None, hint_text=None):
flute_boy_credit=None, hint_text=None):
super(ALttPItem, self).__init__(name, classification, item_code, player) super(ALttPItem, self).__init__(name, classification, item_code, player)
self.type = type self.type = type
self._pedestal_hint_text = pedestal_hint 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 self._hint_text = hint_text
@property @property

View File

@ -8,7 +8,7 @@ from BaseClasses import Item, CollectionState, Tutorial
from .SubClasses import ALttPItem from .SubClasses import ALttPItem
from ..AutoWorld import World, WebWorld, LogicMixin from ..AutoWorld import World, WebWorld, LogicMixin
from .Options import alttp_options, smallkey_shuffle 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 .Regions import lookup_name_to_id, create_regions, mark_light_world_regions
from .Rules import set_rules from .Rules import set_rules
from .ItemPool import generate_itempool, difficulties from .ItemPool import generate_itempool, difficulties
@ -124,6 +124,17 @@ class ALTTPWorld(World):
required_client_version = (0, 3, 2) required_client_version = (0, 3, 2)
web = ALTTPWeb() 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 set_rules = set_rules
create_items = generate_itempool 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]] multidata["connect_names"][new_name] = multidata["connect_names"][self.world.player_name[self.player]]
def create_item(self, name: str) -> Item: 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 @classmethod
def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool, def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool,

View File

@ -47,13 +47,12 @@ class ArchipIDLEWorld(World):
item_pool = [] item_pool = []
for i in range(100): for i in range(100):
item = Item( item = ArchipIDLEItem(
item_table_copy[i], item_table_copy[i],
ItemClassification.progression if i < 20 else ItemClassification.filler, ItemClassification.progression if i < 20 else ItemClassification.filler,
self.item_name_to_id[item_table_copy[i]], self.item_name_to_id[item_table_copy[i]],
self.player self.player
) )
item.game = 'ArchipIDLE'
item_pool.append(item) item_pool.append(item)
self.world.itempool += item_pool self.world.itempool += item_pool
@ -93,6 +92,10 @@ def create_region(world: MultiWorld, player: int, name: str, locations=None, exi
return region return region
class ArchipIDLEItem(Item):
game = "ArchipIDLE"
class ArchipIDLELocation(Location): class ArchipIDLELocation(Location):
game: str = "ArchipIDLE" game: str = "ArchipIDLE"

View File

@ -632,8 +632,9 @@ class HKLocation(Location):
class HKItem(Item): class HKItem(Item):
game = "Hollow Knight" 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": if name == "Mimic_Grub":
classification = ItemClassification.trap classification = ItemClassification.trap
elif type in ("Grub", "DreamWarrior", "Root", "Egg"): elif type in ("Grub", "DreamWarrior", "Root", "Egg"):

View File

@ -6,14 +6,9 @@
import typing import typing
from BaseClasses import Item, ItemClassification 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): class MeritousLttPText(typing.NamedTuple):
pedestal: typing.Optional[str] pedestal: typing.Optional[str]
sickkid: typing.Optional[str] sickkid: typing.Optional[str]
@ -143,6 +138,7 @@ LttPCreditsText = {
class MeritousItem(Item): class MeritousItem(Item):
game: str = "Meritous" game: str = "Meritous"
type: str
def __init__(self, name, advancement, code, player): def __init__(self, name, advancement, code, player):
super(MeritousItem, self).__init__(name, super(MeritousItem, self).__init__(name,
@ -171,14 +167,6 @@ class MeritousItem(Item):
self.type = "Artifact" self.type = "Artifact"
self.classification = ItemClassification.useful 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 offset = 593_000
@ -217,3 +205,10 @@ item_groups = {
"Important Artifacts": ["Shield Boost", "Circuit Booster", "Metabolism", "Dodge Enhancer"], "Important Artifacts": ["Shield Boost", "Circuit Booster", "Metabolism", "Dodge Enhancer"],
"Crystals": ["Crystals x500", "Crystals x1000", "Crystals x2000"] "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()})

View File

@ -24,6 +24,7 @@ def ap_id_to_oot_data(ap_id):
class OOTItem(Item): class OOTItem(Item):
game: str = "Ocarina of Time" game: str = "Ocarina of Time"
type: str
def __init__(self, name, player, data, event, force_not_advancement): def __init__(self, name, player, data, event, force_not_advancement):
(type, advancement, index, special) = data (type, advancement, index, special) = data
@ -38,7 +39,6 @@ class OOTItem(Item):
classification = ItemClassification.progression classification = ItemClassification.progression
else: else:
classification = ItemClassification.filler 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) super(OOTItem, self).__init__(name, classification, oot_data_to_ap_id(data, event), player)
self.type = type self.type = type
self.index = index self.index = index
@ -47,24 +47,11 @@ class OOTItem(Item):
self.price = special.get('price', None) if special else None self.price = special.get('price', None) if special else None
self.internal = False 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 @property
def dungeonitem(self) -> bool: def dungeonitem(self) -> bool:
return self.type in ['SmallKey', 'HideoutSmallKey', 'BossKey', 'GanonBossKey', 'Map', 'Compass'] return self.type in ['SmallKey', 'HideoutSmallKey', 'BossKey', 'GanonBossKey', 'Map', 'Compass']
# Progressive: True -> Advancement # Progressive: True -> Advancement
# False -> Priority # False -> Priority
# None -> Normal # None -> Normal

View File

@ -2,6 +2,7 @@ import typing
from BaseClasses import Item, ItemClassification from BaseClasses import Item, ItemClassification
from .Names import ItemName from .Names import ItemName
from worlds.alttp import ALTTPWorld
class ItemData(typing.NamedTuple): class ItemData(typing.NamedTuple):
@ -18,9 +19,6 @@ class SA2BItem(Item):
def __init__(self, name, classification: ItemClassification, code: int = None, player: int = None): def __init__(self, name, classification: ItemClassification, code: int = None, player: int = None):
super(SA2BItem, self).__init__(name, classification, code, player) 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. # Separate tables for each type of item.
emblems_table = { 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} 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"

View File

@ -5,7 +5,7 @@ import copy
import os import os
import threading import threading
import base64 import base64
from typing import Set, List, TextIO from typing import Set, TextIO
from worlds.sm.variaRandomizer.graph.graph_utils import GraphUtils from worlds.sm.variaRandomizer.graph.graph_utils import GraphUtils
@ -735,7 +735,8 @@ class SMLocation(Location):
class SMItem(Item): class SMItem(Item):
game = "Super Metroid" 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) super(SMItem, self).__init__(name, classification, code, player)
self.type = type self.type = type

View File

@ -573,8 +573,10 @@ class SMZ3Location(Location):
class SMZ3Item(Item): class SMZ3Item(Item):
game = "SMZ3" 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.type = type
self.item = item self.item = item
super(SMZ3Item, self).__init__(name, classification, code, player)