LTTP/SM/SMZ3: Show correct item icon for cross-game items (#1112)
Co-authored-by: lordlou <87331798+lordlou@users.noreply.github.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
This commit is contained in:
parent
18e0d25051
commit
776b5fab7c
|
@ -762,7 +762,9 @@ bonk_addresses = [0x4CF6C, 0x4CFBA, 0x4CFE0, 0x4CFFB, 0x4D018, 0x4D01B, 0x4D028,
|
||||||
0x4D504, 0x4D507, 0x4D55E, 0x4D56A]
|
0x4D504, 0x4D507, 0x4D55E, 0x4D56A]
|
||||||
|
|
||||||
|
|
||||||
def get_nonnative_item_sprite(item: str) -> int:
|
def get_nonnative_item_sprite(code: int) -> int:
|
||||||
|
if 84173 >= code >= 84007: # LttP item in SMZ3
|
||||||
|
return code - 84000
|
||||||
return 0x6B # set all non-native sprites to Power Star as per 13 to 2 vote at
|
return 0x6B # set all non-native sprites to Power Star as per 13 to 2 vote at
|
||||||
# https://discord.com/channels/731205301247803413/827141303330406408/852102450822905886
|
# https://discord.com/channels/731205301247803413/827141303330406408/852102450822905886
|
||||||
|
|
||||||
|
@ -785,7 +787,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
|
||||||
if location.item.trap:
|
if location.item.trap:
|
||||||
itemid = 0x5A # Nothing, which disguises
|
itemid = 0x5A # Nothing, which disguises
|
||||||
else:
|
else:
|
||||||
itemid = get_nonnative_item_sprite(location.item.name)
|
itemid = get_nonnative_item_sprite(location.item.code)
|
||||||
# Keys in their native dungeon should use the orignal item code for keys
|
# Keys in their native dungeon should use the orignal item code for keys
|
||||||
elif location.parent_region.dungeon:
|
elif location.parent_region.dungeon:
|
||||||
if location.parent_region.dungeon.is_dungeon_item(location.item):
|
if location.parent_region.dungeon.is_dungeon_item(location.item):
|
||||||
|
@ -1739,7 +1741,7 @@ def write_custom_shops(rom, world, player):
|
||||||
replacement_price_data = get_price_data(item['replacement_price'], item['replacement_price_type'])
|
replacement_price_data = get_price_data(item['replacement_price'], item['replacement_price_type'])
|
||||||
slot = 0 if shop.type == ShopType.TakeAny else index
|
slot = 0 if shop.type == ShopType.TakeAny else index
|
||||||
if item['player'] and world.game[item['player']] != "A Link to the Past": # item not native to ALTTP
|
if item['player'] and world.game[item['player']] != "A Link to the Past": # item not native to ALTTP
|
||||||
item_code = get_nonnative_item_sprite(item['item'])
|
item_code = get_nonnative_item_sprite(world.worlds[item['player']].item_name_to_id[item['item']])
|
||||||
else:
|
else:
|
||||||
item_code = ItemFactory(item['item'], player).code
|
item_code = ItemFactory(item['item'], player).code
|
||||||
if item['item'] == 'Single Arrow' and item['player'] == 0 and world.retro_bow[player]:
|
if item['item'] == 'Single Arrow' and item['player'] == 0 and world.retro_bow[player]:
|
||||||
|
|
|
@ -174,12 +174,12 @@ class SMWorld(World):
|
||||||
isAdvancement = False
|
isAdvancement = False
|
||||||
|
|
||||||
itemClass = ItemManager.Items[item.Type].Class
|
itemClass = ItemManager.Items[item.Type].Class
|
||||||
smitem = SMItem(item.Name,
|
smitem = SMItem(item.Name,
|
||||||
ItemClassification.progression if isAdvancement else ItemClassification.filler,
|
ItemClassification.progression if isAdvancement else ItemClassification.filler,
|
||||||
item.Type,
|
item.Type,
|
||||||
None if itemClass == 'Boss' else self.item_name_to_id[item.Name],
|
None if itemClass == 'Boss' else self.item_name_to_id[item.Name],
|
||||||
player=self.player)
|
player=self.player)
|
||||||
|
|
||||||
if itemClass == 'Boss':
|
if itemClass == 'Boss':
|
||||||
self.locked_items[item.Name] = smitem
|
self.locked_items[item.Name] = smitem
|
||||||
elif item.Category == 'Nothing':
|
elif item.Category == 'Nothing':
|
||||||
|
@ -192,10 +192,10 @@ class SMWorld(World):
|
||||||
for (location, item) in self.locked_items.items():
|
for (location, item) in self.locked_items.items():
|
||||||
self.multiworld.get_location(location, self.player).place_locked_item(item)
|
self.multiworld.get_location(location, self.player).place_locked_item(item)
|
||||||
self.multiworld.get_location(location, self.player).address = None
|
self.multiworld.get_location(location, self.player).address = None
|
||||||
|
|
||||||
def evalSMBool(self, smbool, maxDiff):
|
def evalSMBool(self, smbool, maxDiff):
|
||||||
return smbool.bool == True and smbool.difficulty <= maxDiff
|
return smbool.bool == True and smbool.difficulty <= maxDiff
|
||||||
|
|
||||||
def add_entrance_rule(self, entrance, player, func):
|
def add_entrance_rule(self, entrance, player, func):
|
||||||
add_rule(entrance, lambda state: self.evalSMBool(func(state.smbm[player]), state.smbm[player].maxDiff))
|
add_rule(entrance, lambda state: self.evalSMBool(func(state.smbm[player]), state.smbm[player].maxDiff))
|
||||||
|
|
||||||
|
@ -221,12 +221,12 @@ class SMWorld(World):
|
||||||
add_accessFrom_rule(location, self.player, value.AccessFrom)
|
add_accessFrom_rule(location, self.player, value.AccessFrom)
|
||||||
if value.PostAvailable is not None:
|
if value.PostAvailable is not None:
|
||||||
add_postAvailable_rule(location, self.player, value.PostAvailable)
|
add_postAvailable_rule(location, self.player, value.PostAvailable)
|
||||||
|
|
||||||
for accessPoint in Logic.accessPoints:
|
for accessPoint in Logic.accessPoints:
|
||||||
if not accessPoint.Escape:
|
if not accessPoint.Escape:
|
||||||
for key, value1 in accessPoint.intraTransitions.items():
|
for key, value1 in accessPoint.intraTransitions.items():
|
||||||
set_entrance_rule(self.multiworld.get_entrance(accessPoint.Name + "->" + key, self.player), self.player, value1)
|
set_entrance_rule(self.multiworld.get_entrance(accessPoint.Name + "->" + key, self.player), self.player, value1)
|
||||||
|
|
||||||
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||||
ret = Region(name, player, world)
|
ret = Region(name, player, world)
|
||||||
if locations:
|
if locations:
|
||||||
|
@ -238,7 +238,7 @@ class SMWorld(World):
|
||||||
for exit in exits:
|
for exit in exits:
|
||||||
ret.exits.append(Entrance(player, exit, ret))
|
ret.exits.append(Entrance(player, exit, ret))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
# create locations
|
# create locations
|
||||||
for name in locationsDict:
|
for name in locationsDict:
|
||||||
|
@ -248,9 +248,9 @@ class SMWorld(World):
|
||||||
regions = []
|
regions = []
|
||||||
for accessPoint in Logic.accessPoints:
|
for accessPoint in Logic.accessPoints:
|
||||||
if not accessPoint.Escape:
|
if not accessPoint.Escape:
|
||||||
regions.append(self.create_region( self.multiworld,
|
regions.append(self.create_region( self.multiworld,
|
||||||
self.player,
|
self.player,
|
||||||
accessPoint.Name,
|
accessPoint.Name,
|
||||||
None,
|
None,
|
||||||
[accessPoint.Name + "->" + key for key in accessPoint.intraTransitions.keys()]))
|
[accessPoint.Name + "->" + key for key in accessPoint.intraTransitions.keys()]))
|
||||||
|
|
||||||
|
@ -261,9 +261,9 @@ class SMWorld(World):
|
||||||
# this is required in AP because a location cant have multiple parent regions
|
# this is required in AP because a location cant have multiple parent regions
|
||||||
locationRegions = []
|
locationRegions = []
|
||||||
for locationName, value in locationsDict.items():
|
for locationName, value in locationsDict.items():
|
||||||
locationRegions.append(self.create_region( self.multiworld,
|
locationRegions.append(self.create_region( self.multiworld,
|
||||||
self.player,
|
self.player,
|
||||||
locationName,
|
locationName,
|
||||||
[locationName]))
|
[locationName]))
|
||||||
for key in value.AccessFrom.keys():
|
for key in value.AccessFrom.keys():
|
||||||
currentRegion = self.multiworld.get_region(key, self.player)
|
currentRegion = self.multiworld.get_region(key, self.player)
|
||||||
|
@ -320,7 +320,7 @@ class SMWorld(World):
|
||||||
return "Super Missile"
|
return "Super Missile"
|
||||||
else:
|
else:
|
||||||
return "Nothing"
|
return "Nothing"
|
||||||
|
|
||||||
def pre_fill(self):
|
def pre_fill(self):
|
||||||
if len(self.NothingPool) > 0:
|
if len(self.NothingPool) > 0:
|
||||||
nonChozoLoc = []
|
nonChozoLoc = []
|
||||||
|
@ -371,8 +371,8 @@ class SMWorld(World):
|
||||||
itemLoc.item.type if isinstance(itemLoc.item, SMItem) and itemLoc.item.type in ItemManager.Items else
|
itemLoc.item.type if isinstance(itemLoc.item, SMItem) and itemLoc.item.type in ItemManager.Items else
|
||||||
'ArchipelagoItem']),
|
'ArchipelagoItem']),
|
||||||
copy.copy(locationsDict[itemLoc.name] if itemLoc.game == self.game else
|
copy.copy(locationsDict[itemLoc.name] if itemLoc.game == self.game else
|
||||||
locationsDict[first_local_collected_loc.name]),
|
locationsDict[first_local_collected_loc.name]),
|
||||||
itemLoc.item.player,
|
itemLoc.item.player,
|
||||||
True)
|
True)
|
||||||
for itemLoc in SMWorld.spheres if itemLoc.item.player == self.player
|
for itemLoc in SMWorld.spheres if itemLoc.item.player == self.player
|
||||||
]
|
]
|
||||||
|
@ -387,7 +387,7 @@ class SMWorld(World):
|
||||||
escapeOk = self.variaRando.randoExec.graphBuilder.escapeGraph(self.variaRando.container, self.variaRando.randoExec.areaGraph, self.variaRando.randoExec.randoSettings.maxDiff, escapeTrigger)
|
escapeOk = self.variaRando.randoExec.graphBuilder.escapeGraph(self.variaRando.container, self.variaRando.randoExec.areaGraph, self.variaRando.randoExec.randoSettings.maxDiff, escapeTrigger)
|
||||||
if (not escapeOk):
|
if (not escapeOk):
|
||||||
logger.warning(f"Escape Rando forced to 'Off' for player {self.multiworld.get_player_name(self.player)} because could not find a solution for escape")
|
logger.warning(f"Escape Rando forced to 'Off' for player {self.multiworld.get_player_name(self.player)} because could not find a solution for escape")
|
||||||
|
|
||||||
# if we couldn't find an area layout then the escape graph is not created either
|
# if we couldn't find an area layout then the escape graph is not created either
|
||||||
# and getDoorConnections will crash if random escape is activated.
|
# and getDoorConnections will crash if random escape is activated.
|
||||||
self.variaRando.doors = GraphUtils.getDoorConnections(self.variaRando.randoExec.areaGraph,
|
self.variaRando.doors = GraphUtils.getDoorConnections(self.variaRando.randoExec.areaGraph,
|
||||||
|
@ -406,7 +406,7 @@ class SMWorld(World):
|
||||||
|
|
||||||
for item in progitempool:
|
for item in progitempool:
|
||||||
new_state.collect(item, True)
|
new_state.collect(item, True)
|
||||||
|
|
||||||
bossesLoc = ['Draygon', 'Kraid', 'Ridley', 'Phantoon', 'Mother Brain']
|
bossesLoc = ['Draygon', 'Kraid', 'Ridley', 'Phantoon', 'Mother Brain']
|
||||||
for player in world.get_game_players("Super Metroid"):
|
for player in world.get_game_players("Super Metroid"):
|
||||||
for bossLoc in bossesLoc:
|
for bossLoc in bossesLoc:
|
||||||
|
@ -548,9 +548,17 @@ class SMWorld(World):
|
||||||
vanillaItemTypesCount = 21
|
vanillaItemTypesCount = 21
|
||||||
for itemLoc in self.multiworld.get_locations():
|
for itemLoc in self.multiworld.get_locations():
|
||||||
if itemLoc.player == self.player and "Boss" not in locationsDict[itemLoc.name].Class:
|
if itemLoc.player == self.player and "Boss" not in locationsDict[itemLoc.name].Class:
|
||||||
# item to place in this SM world: write full item data to tables
|
SMZ3NameToSMType = {
|
||||||
|
"ETank": "ETank", "Missile": "Missile", "Super": "Super", "PowerBomb": "PowerBomb", "Bombs": "Bomb",
|
||||||
|
"Charge": "Charge", "Ice": "Ice", "HiJump": "HiJump", "SpeedBooster": "SpeedBooster",
|
||||||
|
"Wave": "Wave", "Spazer": "Spazer", "SpringBall": "SpringBall", "Varia": "Varia", "Plasma": "Plasma",
|
||||||
|
"Grapple": "Grapple", "Morph": "Morph", "ReserveTank": "Reserve", "Gravity": "Gravity",
|
||||||
|
"XRay": "XRayScope", "SpaceJump": "SpaceJump", "ScrewAttack": "ScrewAttack"
|
||||||
|
}
|
||||||
if isinstance(itemLoc.item, SMItem) and itemLoc.item.type in ItemManager.Items:
|
if isinstance(itemLoc.item, SMItem) and itemLoc.item.type in ItemManager.Items:
|
||||||
itemId = ItemManager.Items[itemLoc.item.type].Id
|
itemId = ItemManager.Items[itemLoc.item.type].Id
|
||||||
|
elif itemLoc.item.game == "SMZ3" and itemLoc.item.name in SMZ3NameToSMType.keys():
|
||||||
|
itemId = ItemManager.Items[SMZ3NameToSMType[itemLoc.item.name]].Id
|
||||||
else:
|
else:
|
||||||
itemId = ItemManager.Items["ArchipelagoItem"].Id + idx
|
itemId = ItemManager.Items["ArchipelagoItem"].Id + idx
|
||||||
multiWorldItems.append({"sym": symbols["message_item_names"],
|
multiWorldItems.append({"sym": symbols["message_item_names"],
|
||||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any, Callable, List, Sequence
|
||||||
import random
|
import random
|
||||||
import typing
|
import typing
|
||||||
from BaseClasses import Location
|
from BaseClasses import Location
|
||||||
from .Item import Item, ItemType
|
from .Item import Item, ItemType, lookup_id_to_name
|
||||||
from .Location import LocationType
|
from .Location import LocationType
|
||||||
from .Region import IReward, RewardType, SMRegion, Z3Region
|
from .Region import IReward, RewardType, SMRegion, Z3Region
|
||||||
from .Regions.Zelda.EasternPalace import EasternPalace
|
from .Regions.Zelda.EasternPalace import EasternPalace
|
||||||
|
@ -351,6 +351,29 @@ class Patch:
|
||||||
not (item.IsDungeonItem() and location.Region.IsRegionItem(item) and item.World == self.myWorld) else itemDungeon
|
not (item.IsDungeonItem() and location.Region.IsRegionItem(item) and item.World == self.myWorld) else itemDungeon
|
||||||
|
|
||||||
return value.value
|
return value.value
|
||||||
|
elif (location.APLocation.item.game == "A Link to the Past"):
|
||||||
|
if location.APLocation.item.code + 84000 in lookup_id_to_name:
|
||||||
|
ALTTPBottleContentCodeToSMZ3ItemCode = {
|
||||||
|
ItemType.RedContent.value: ItemType.BottleWithRedPotion.value,
|
||||||
|
ItemType.GreenContent.value: ItemType.BottleWithGreenPotion.value,
|
||||||
|
ItemType.BlueContent.value: ItemType.BottleWithBluePotion.value,
|
||||||
|
ItemType.BeeContent.value: ItemType.BottleWithBee.value,
|
||||||
|
}
|
||||||
|
return ALTTPBottleContentCodeToSMZ3ItemCode.get(location.APLocation.item.code, location.APLocation.item.code)
|
||||||
|
else:
|
||||||
|
return ItemType.Something.value
|
||||||
|
elif (location.APLocation.item.game == "Super Metroid"):
|
||||||
|
SMNameToSMZ3Code = {
|
||||||
|
"Energy Tank": ItemType.ETank, "Missile": ItemType.Missile, "Super Missile": ItemType.Super,
|
||||||
|
"Power Bomb": ItemType.PowerBomb, "Bomb": ItemType.Bombs, "Charge Beam": ItemType.Charge,
|
||||||
|
"Ice Beam": ItemType.Ice, "Hi-Jump Boots": ItemType.HiJump, "Speed Booster": ItemType.SpeedBooster,
|
||||||
|
"Wave Beam": ItemType.Wave, "Spazer": ItemType.Spazer, "Spring Ball": ItemType.SpringBall,
|
||||||
|
"Varia Suit": ItemType.Varia, "Plasma Beam": ItemType.Plasma, "Grappling Beam": ItemType.Grapple,
|
||||||
|
"Morph Ball": ItemType.Morph, "Reserve Tank": ItemType.ReserveTank, "Gravity Suit": ItemType.Gravity,
|
||||||
|
"X-Ray Scope": ItemType.XRay, "Space Jump": ItemType.SpaceJump, "Screw Attack": ItemType.ScrewAttack,
|
||||||
|
"Nothing": ItemType.Something, "No Energy": ItemType.Something, "Generic": ItemType.Something
|
||||||
|
}
|
||||||
|
return SMNameToSMZ3Code.get(location.APLocation.item.name, ItemType.Something).value
|
||||||
else:
|
else:
|
||||||
return ItemType.Something.value
|
return ItemType.Something.value
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue