Smz3 updated to version 11.3 (#886)
This commit is contained in:
parent
c02c6ee58c
commit
898fa203ad
|
@ -1,5 +1,5 @@
|
||||||
import typing
|
import typing
|
||||||
from Options import Choice, Option
|
from Options import Choice, Option, Toggle, DefaultOnToggle, Range
|
||||||
|
|
||||||
class SMLogic(Choice):
|
class SMLogic(Choice):
|
||||||
"""This option selects what kind of logic to use for item placement inside
|
"""This option selects what kind of logic to use for item placement inside
|
||||||
|
@ -45,6 +45,22 @@ class MorphLocation(Choice):
|
||||||
option_Original = 2
|
option_Original = 2
|
||||||
default = 0
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
|
class Goal(Choice):
|
||||||
|
"""This option decides what goal is required to finish the randomizer.
|
||||||
|
Defeat Ganon and Mother Brain - Find the required crystals and boss tokens kill both bosses.
|
||||||
|
Fast Ganon and Defeat Mother Brain - The hole to ganon is open without having to defeat Agahnim in
|
||||||
|
Ganon's Tower and Ganon can be defeat as soon you have the required
|
||||||
|
crystals to make Ganon vulnerable. For keysanity, this mode also removes
|
||||||
|
the Crateria Boss Key requirement from Tourian to allow faster access.
|
||||||
|
All Dungeons and Defeat Mother Brain - Similar to "Defeat Ganon and Mother Brain", but also requires all dungeons
|
||||||
|
to be beaten including Castle Tower and Agahnim."""
|
||||||
|
display_name = "Goal"
|
||||||
|
option_DefeatBoth = 0
|
||||||
|
option_FastGanonDefeatMotherBrain = 1
|
||||||
|
option_AllDungeonsDefeatMotherBrain = 2
|
||||||
|
default = 0
|
||||||
|
|
||||||
class KeyShuffle(Choice):
|
class KeyShuffle(Choice):
|
||||||
"""This option decides how dungeon items such as keys are shuffled.
|
"""This option decides how dungeon items such as keys are shuffled.
|
||||||
None - A Link to the Past dungeon items can only be placed inside the
|
None - A Link to the Past dungeon items can only be placed inside the
|
||||||
|
@ -55,9 +71,75 @@ class KeyShuffle(Choice):
|
||||||
option_Keysanity = 1
|
option_Keysanity = 1
|
||||||
default = 0
|
default = 0
|
||||||
|
|
||||||
|
class OpenTower(Range):
|
||||||
|
"""The amount of crystals required to be able to enter Ganon's Tower.
|
||||||
|
If this is set to Random, the amount can be found in-game on a sign next to Ganon's Tower."""
|
||||||
|
display_name = "Open Tower"
|
||||||
|
range_start = 0
|
||||||
|
range_end = 7
|
||||||
|
default = 7
|
||||||
|
|
||||||
|
class GanonVulnerable(Range):
|
||||||
|
"""The amount of crystals required to be able to harm Ganon. The amount can be found
|
||||||
|
in-game on a sign near the top of the Pyramid."""
|
||||||
|
display_name = "Ganon Vulnerable"
|
||||||
|
range_start = 0
|
||||||
|
range_end = 7
|
||||||
|
default = 7
|
||||||
|
|
||||||
|
class OpenTourian(Range):
|
||||||
|
"""The amount of boss tokens required to enter Tourian. The amount can be found in-game
|
||||||
|
on a sign above the door leading to the Tourian entrance."""
|
||||||
|
display_name = "Open Tourian"
|
||||||
|
range_start = 0
|
||||||
|
range_end = 4
|
||||||
|
default = 4
|
||||||
|
|
||||||
|
class SpinJumpsAnimation(Toggle):
|
||||||
|
"""Enable separate space/screw jump animations"""
|
||||||
|
display_name = "Spin Jumps Animation"
|
||||||
|
|
||||||
|
class HeartBeepSpeed(Choice):
|
||||||
|
"""Sets the speed of the heart beep sound in A Link to the Past."""
|
||||||
|
display_name = "Heart Beep Speed"
|
||||||
|
option_Off = 0
|
||||||
|
option_Quarter = 1
|
||||||
|
option_Half = 2
|
||||||
|
option_Normal = 3
|
||||||
|
option_Double = 4
|
||||||
|
alias_false = 0
|
||||||
|
default = 3
|
||||||
|
|
||||||
|
class HeartColor(Choice):
|
||||||
|
"""Changes the color of the hearts in the HUD for A Link to the Past."""
|
||||||
|
display_name = "Heart Color"
|
||||||
|
option_Red = 0
|
||||||
|
option_Green = 1
|
||||||
|
option_Blue = 2
|
||||||
|
option_Yellow = 3
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
class QuickSwap(Toggle):
|
||||||
|
"""When enabled, lets you switch items in ALTTP with L/R"""
|
||||||
|
display_name = "Quick Swap"
|
||||||
|
|
||||||
|
class EnergyBeep(DefaultOnToggle):
|
||||||
|
"""Toggles the low health energy beep in Super Metroid."""
|
||||||
|
display_name = "Energy Beep"
|
||||||
|
|
||||||
|
|
||||||
smz3_options: typing.Dict[str, type(Option)] = {
|
smz3_options: typing.Dict[str, type(Option)] = {
|
||||||
"sm_logic": SMLogic,
|
"sm_logic": SMLogic,
|
||||||
"sword_location": SwordLocation,
|
"sword_location": SwordLocation,
|
||||||
"morph_location": MorphLocation,
|
"morph_location": MorphLocation,
|
||||||
"key_shuffle": KeyShuffle
|
"goal": Goal,
|
||||||
|
"key_shuffle": KeyShuffle,
|
||||||
|
"open_tower": OpenTower,
|
||||||
|
"ganon_vulnerable": GanonVulnerable,
|
||||||
|
"open_tourian": OpenTourian,
|
||||||
|
"spin_jumps_animation": SpinJumpsAnimation,
|
||||||
|
"heart_beep_speed": HeartBeepSpeed,
|
||||||
|
"heart_color": HeartColor,
|
||||||
|
"quick_swap": QuickSwap,
|
||||||
|
"energy_beep": EnergyBeep
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,16 +26,42 @@ class MorphLocation(Enum):
|
||||||
|
|
||||||
class Goal(Enum):
|
class Goal(Enum):
|
||||||
DefeatBoth = 0
|
DefeatBoth = 0
|
||||||
|
FastGanonDefeatMotherBrain = 1
|
||||||
|
AllDungeonsDefeatMotherBrain = 2
|
||||||
|
|
||||||
class KeyShuffle(Enum):
|
class KeyShuffle(Enum):
|
||||||
Null = 0
|
Null = 0
|
||||||
Keysanity = 1
|
Keysanity = 1
|
||||||
|
|
||||||
class GanonInvincible(Enum):
|
class OpenTower(Enum):
|
||||||
Never = 0
|
Random = -1
|
||||||
BeforeCrystals = 1
|
NoCrystals = 0
|
||||||
BeforeAllDungeons = 2
|
OneCrystal = 1
|
||||||
Always = 3
|
TwoCrystals = 2
|
||||||
|
ThreeCrystals = 3
|
||||||
|
FourCrystals = 4
|
||||||
|
FiveCrystals = 5
|
||||||
|
SixCrystals = 6
|
||||||
|
SevenCrystals = 7
|
||||||
|
|
||||||
|
class GanonVulnerable(Enum):
|
||||||
|
Random = -1
|
||||||
|
NoCrystals = 0
|
||||||
|
OneCrystal = 1
|
||||||
|
TwoCrystals = 2
|
||||||
|
ThreeCrystals = 3
|
||||||
|
FourCrystals = 4
|
||||||
|
FiveCrystals = 5
|
||||||
|
SixCrystals = 6
|
||||||
|
SevenCrystals = 7
|
||||||
|
|
||||||
|
class OpenTourian(Enum):
|
||||||
|
Random = -1
|
||||||
|
NoBosses = 0
|
||||||
|
OneBoss = 1
|
||||||
|
TwoBosses = 2
|
||||||
|
ThreeBosses = 3
|
||||||
|
FourBosses = 4
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
GameMode: GameMode = GameMode.Multiworld
|
GameMode: GameMode = GameMode.Multiworld
|
||||||
|
@ -45,63 +71,20 @@ class Config:
|
||||||
MorphLocation: MorphLocation = MorphLocation.Randomized
|
MorphLocation: MorphLocation = MorphLocation.Randomized
|
||||||
Goal: Goal = Goal.DefeatBoth
|
Goal: Goal = Goal.DefeatBoth
|
||||||
KeyShuffle: KeyShuffle = KeyShuffle.Null
|
KeyShuffle: KeyShuffle = KeyShuffle.Null
|
||||||
Keysanity: bool = KeyShuffle != KeyShuffle.Null
|
|
||||||
Race: bool = False
|
Race: bool = False
|
||||||
GanonInvincible: GanonInvincible = GanonInvincible.BeforeCrystals
|
|
||||||
|
|
||||||
def __init__(self, options: Dict[str, str]):
|
OpenTower: OpenTower = OpenTower.SevenCrystals
|
||||||
self.GameMode = self.ParseOption(options, GameMode.Multiworld)
|
GanonVulnerable: GanonVulnerable = GanonVulnerable.SevenCrystals
|
||||||
self.Z3Logic = self.ParseOption(options, Z3Logic.Normal)
|
OpenTourian: OpenTourian = OpenTourian.FourBosses
|
||||||
self.SMLogic = self.ParseOption(options, SMLogic.Normal)
|
|
||||||
self.SwordLocation = self.ParseOption(options, SwordLocation.Randomized)
|
|
||||||
self.MorphLocation = self.ParseOption(options, MorphLocation.Randomized)
|
|
||||||
self.Goal = self.ParseOption(options, Goal.DefeatBoth)
|
|
||||||
self.GanonInvincible = self.ParseOption(options, GanonInvincible.BeforeCrystals)
|
|
||||||
self.KeyShuffle = self.ParseOption(options, KeyShuffle.Null)
|
|
||||||
self.Keysanity = self.KeyShuffle != KeyShuffle.Null
|
|
||||||
self.Race = self.ParseOptionWith(options, "Race", False)
|
|
||||||
|
|
||||||
def ParseOption(self, options:Dict[str, str], defaultValue:Enum):
|
@property
|
||||||
enumKey = defaultValue.__class__.__name__.lower()
|
def SingleWorld(self) -> bool:
|
||||||
if (enumKey in options):
|
return self.GameMode == GameMode.Normal
|
||||||
return defaultValue.__class__[options[enumKey]]
|
|
||||||
return defaultValue
|
@property
|
||||||
|
def Multiworld(self) -> bool:
|
||||||
|
return self.GameMode == GameMode.Multiworld
|
||||||
|
|
||||||
def ParseOptionWith(self, options:Dict[str, str], option:str, defaultValue:bool):
|
@property
|
||||||
if (option.lower() in options):
|
def Keysanity(self) -> bool:
|
||||||
return options[option.lower()]
|
return self.KeyShuffle != KeyShuffle.Null
|
||||||
return defaultValue
|
|
||||||
|
|
||||||
""" public static RandomizerOption GetRandomizerOption<T>(string description, string defaultOption = "") where T : Enum {
|
|
||||||
var enumType = typeof(T);
|
|
||||||
var values = Enum.GetValues(enumType).Cast<Enum>();
|
|
||||||
|
|
||||||
return new RandomizerOption {
|
|
||||||
Key = enumType.Name.ToLower(),
|
|
||||||
Description = description,
|
|
||||||
Type = RandomizerOptionType.Dropdown,
|
|
||||||
Default = string.IsNullOrEmpty(defaultOption) ? GetDefaultValue<T>().ToLString() : defaultOption,
|
|
||||||
Values = values.ToDictionary(k => k.ToLString(), v => v.GetDescription())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RandomizerOption GetRandomizerOption(string name, string description, bool defaultOption = false) {
|
|
||||||
return new RandomizerOption {
|
|
||||||
Key = name.ToLower(),
|
|
||||||
Description = description,
|
|
||||||
Type = RandomizerOptionType.Checkbox,
|
|
||||||
Default = defaultOption.ToString().ToLower(),
|
|
||||||
Values = new Dictionary<string, string>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TEnum GetDefaultValue<TEnum>() where TEnum : Enum {
|
|
||||||
Type t = typeof(TEnum);
|
|
||||||
var attributes = (DefaultValueAttribute[])t.GetCustomAttributes(typeof(DefaultValueAttribute), false);
|
|
||||||
if ((attributes?.Length ?? 0) > 0) {
|
|
||||||
return (TEnum)attributes.First().Value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
} """
|
|
|
@ -130,6 +130,11 @@ class ItemType(Enum):
|
||||||
CardLowerNorfairL1 = 0xDE
|
CardLowerNorfairL1 = 0xDE
|
||||||
CardLowerNorfairBoss = 0xDF
|
CardLowerNorfairBoss = 0xDF
|
||||||
|
|
||||||
|
SmMapBrinstar = 0xCA
|
||||||
|
SmMapWreckedShip = 0xCB
|
||||||
|
SmMapMaridia = 0xCC
|
||||||
|
SmMapLowerNorfair = 0xCD
|
||||||
|
|
||||||
Missile = 0xC2
|
Missile = 0xC2
|
||||||
Super = 0xC3
|
Super = 0xC3
|
||||||
PowerBomb = 0xC4
|
PowerBomb = 0xC4
|
||||||
|
@ -174,6 +179,7 @@ class Item:
|
||||||
map = re.compile("^Map")
|
map = re.compile("^Map")
|
||||||
compass = re.compile("^Compass")
|
compass = re.compile("^Compass")
|
||||||
keycard = re.compile("^Card")
|
keycard = re.compile("^Card")
|
||||||
|
smMap = re.compile("^SmMap")
|
||||||
|
|
||||||
def IsDungeonItem(self): return self.dungeon.match(self.Type.name)
|
def IsDungeonItem(self): return self.dungeon.match(self.Type.name)
|
||||||
def IsBigKey(self): return self.bigKey.match(self.Type.name)
|
def IsBigKey(self): return self.bigKey.match(self.Type.name)
|
||||||
|
@ -181,6 +187,7 @@ class Item:
|
||||||
def IsMap(self): return self.map.match(self.Type.name)
|
def IsMap(self): return self.map.match(self.Type.name)
|
||||||
def IsCompass(self): return self.compass.match(self.Type.name)
|
def IsCompass(self): return self.compass.match(self.Type.name)
|
||||||
def IsKeycard(self): return self.keycard.match(self.Type.name)
|
def IsKeycard(self): return self.keycard.match(self.Type.name)
|
||||||
|
def IsSmMap(self): return self.smMap.match(self.Type.name)
|
||||||
|
|
||||||
def Is(self, type: ItemType, world):
|
def Is(self, type: ItemType, world):
|
||||||
return self.Type == type and self.World == world
|
return self.Type == type and self.World == world
|
||||||
|
@ -313,7 +320,7 @@ class Item:
|
||||||
Item.AddRange(itemPool, 4, Item(ItemType.BombUpgrade5))
|
Item.AddRange(itemPool, 4, Item(ItemType.BombUpgrade5))
|
||||||
Item.AddRange(itemPool, 2, Item(ItemType.OneRupee))
|
Item.AddRange(itemPool, 2, Item(ItemType.OneRupee))
|
||||||
Item.AddRange(itemPool, 4, Item(ItemType.FiveRupees))
|
Item.AddRange(itemPool, 4, Item(ItemType.FiveRupees))
|
||||||
Item.AddRange(itemPool, 25 if world.Config.Keysanity else 28, Item(ItemType.TwentyRupees))
|
Item.AddRange(itemPool, 21 if world.Config.Keysanity else 28, Item(ItemType.TwentyRupees))
|
||||||
Item.AddRange(itemPool, 7, Item(ItemType.FiftyRupees))
|
Item.AddRange(itemPool, 7, Item(ItemType.FiftyRupees))
|
||||||
Item.AddRange(itemPool, 5, Item(ItemType.ThreeHundredRupees))
|
Item.AddRange(itemPool, 5, Item(ItemType.ThreeHundredRupees))
|
||||||
|
|
||||||
|
@ -421,6 +428,21 @@ class Item:
|
||||||
|
|
||||||
return itemPool
|
return itemPool
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def CreateSmMaps(world):
|
||||||
|
itemPool = [
|
||||||
|
Item(ItemType.SmMapBrinstar, world),
|
||||||
|
Item(ItemType.SmMapWreckedShip, world),
|
||||||
|
Item(ItemType.SmMapMaridia, world),
|
||||||
|
Item(ItemType.SmMapLowerNorfair, world)
|
||||||
|
]
|
||||||
|
|
||||||
|
for item in itemPool:
|
||||||
|
item.Progression = True
|
||||||
|
item.World = world
|
||||||
|
|
||||||
|
return itemPool
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def Get(items, itemType:ItemType):
|
def Get(items, itemType:ItemType):
|
||||||
item = next((i for i in items if i.Type == itemType), None)
|
item = next((i for i in items if i.Type == itemType), None)
|
||||||
|
@ -725,7 +747,7 @@ class Progression:
|
||||||
|
|
||||||
def CanAccessMiseryMirePortal(self, config: Config):
|
def CanAccessMiseryMirePortal(self, config: Config):
|
||||||
if (config.SMLogic == SMLogic.Normal):
|
if (config.SMLogic == SMLogic.Normal):
|
||||||
return (self.CardNorfairL2 or (self.SpeedBooster and self.Wave)) and self.Varia and self.Super and (self.Gravity and self.SpaceJump) and self.CanUsePowerBombs()
|
return (self.CardNorfairL2 or (self.SpeedBooster and self.Wave)) and self.Varia and self.Super and self.Gravity and self.SpaceJump and self.CanUsePowerBombs()
|
||||||
else:
|
else:
|
||||||
return (self.CardNorfairL2 or self.SpeedBooster) and self.Varia and self.Super and \
|
return (self.CardNorfairL2 or self.SpeedBooster) and self.Varia and self.Super and \
|
||||||
(self.CanFly() or self.HiJump or self.SpeedBooster or self.CanSpringBallJump() or self.Ice) \
|
(self.CanFly() or self.HiJump or self.SpeedBooster or self.CanSpringBallJump() or self.Ice) \
|
||||||
|
@ -769,11 +791,11 @@ class Progression:
|
||||||
if (world.Config.SMLogic == SMLogic.Normal):
|
if (world.Config.SMLogic == SMLogic.Normal):
|
||||||
return self.MoonPearl and self.Flippers and \
|
return self.MoonPearl and self.Flippers and \
|
||||||
self.Gravity and self.Morph and \
|
self.Gravity and self.Morph and \
|
||||||
(world.CanAquire(self, worlds.smz3.TotalSMZ3.Region.RewardType.Agahnim) or self.Hammer and self.CanLiftLight() or self.CanLiftHeavy())
|
(world.CanAcquire(self, worlds.smz3.TotalSMZ3.Region.RewardType.Agahnim) or self.Hammer and self.CanLiftLight() or self.CanLiftHeavy())
|
||||||
else:
|
else:
|
||||||
return self.MoonPearl and self.Flippers and \
|
return self.MoonPearl and self.Flippers and \
|
||||||
(self.CanSpringBallJump() or self.HiJump or self.Gravity) and self.Morph and \
|
(self.CanSpringBallJump() or self.HiJump or self.Gravity) and self.Morph and \
|
||||||
(world.CanAquire(self, worlds.smz3.TotalSMZ3.Region.RewardType.Agahnim) or self.Hammer and self.CanLiftLight() or self.CanLiftHeavy())
|
(world.CanAcquire(self, worlds.smz3.TotalSMZ3.Region.RewardType.Agahnim) or self.Hammer and self.CanLiftLight() or self.CanLiftHeavy())
|
||||||
|
|
||||||
# Start of AP integration
|
# Start of AP integration
|
||||||
items_start_id = 84000
|
items_start_id = 84000
|
||||||
|
|
|
@ -6,7 +6,7 @@ import typing
|
||||||
from BaseClasses import Location
|
from BaseClasses import Location
|
||||||
from worlds.smz3.TotalSMZ3.Item import Item, ItemType
|
from worlds.smz3.TotalSMZ3.Item import Item, ItemType
|
||||||
from worlds.smz3.TotalSMZ3.Location import LocationType
|
from worlds.smz3.TotalSMZ3.Location import LocationType
|
||||||
from worlds.smz3.TotalSMZ3.Region import IMedallionAccess, IReward, RewardType, SMRegion, Z3Region
|
from worlds.smz3.TotalSMZ3.Region import IReward, RewardType, SMRegion, Z3Region
|
||||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.EasternPalace import EasternPalace
|
from worlds.smz3.TotalSMZ3.Regions.Zelda.EasternPalace import EasternPalace
|
||||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DesertPalace import DesertPalace
|
from worlds.smz3.TotalSMZ3.Regions.Zelda.DesertPalace import DesertPalace
|
||||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.TowerOfHera import TowerOfHera
|
from worlds.smz3.TotalSMZ3.Regions.Zelda.TowerOfHera import TowerOfHera
|
||||||
|
@ -18,10 +18,14 @@ from worlds.smz3.TotalSMZ3.Regions.Zelda.IcePalace import IcePalace
|
||||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.MiseryMire import MiseryMire
|
from worlds.smz3.TotalSMZ3.Regions.Zelda.MiseryMire import MiseryMire
|
||||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.TurtleRock import TurtleRock
|
from worlds.smz3.TotalSMZ3.Regions.Zelda.TurtleRock import TurtleRock
|
||||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
||||||
|
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Brinstar.Kraid import Kraid
|
||||||
|
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.WreckedShip import WreckedShip
|
||||||
|
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Maridia.Inner import Inner
|
||||||
|
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.NorfairLower.East import East
|
||||||
from worlds.smz3.TotalSMZ3.Text.StringTable import StringTable
|
from worlds.smz3.TotalSMZ3.Text.StringTable import StringTable
|
||||||
|
|
||||||
from worlds.smz3.TotalSMZ3.World import World
|
from worlds.smz3.TotalSMZ3.World import World
|
||||||
from worlds.smz3.TotalSMZ3.Config import Config, GameMode, GanonInvincible
|
from worlds.smz3.TotalSMZ3.Config import Config, OpenTourian, Goal
|
||||||
from worlds.smz3.TotalSMZ3.Text.Texts import Texts
|
from worlds.smz3.TotalSMZ3.Text.Texts import Texts
|
||||||
from worlds.smz3.TotalSMZ3.Text.Dialog import Dialog
|
from worlds.smz3.TotalSMZ3.Text.Dialog import Dialog
|
||||||
|
|
||||||
|
@ -30,6 +34,11 @@ class KeycardPlaque:
|
||||||
Level2 = 0xe1
|
Level2 = 0xe1
|
||||||
Boss = 0xe2
|
Boss = 0xe2
|
||||||
Null = 0x00
|
Null = 0x00
|
||||||
|
Zero = 0xe3
|
||||||
|
One = 0xe4
|
||||||
|
Two = 0xe5
|
||||||
|
Three = 0xe6
|
||||||
|
Four = 0xe7
|
||||||
|
|
||||||
class KeycardDoors:
|
class KeycardDoors:
|
||||||
Left = 0xd414
|
Left = 0xd414
|
||||||
|
@ -73,8 +82,8 @@ class DropPrize(Enum):
|
||||||
Fairy = 0xE3
|
Fairy = 0xE3
|
||||||
|
|
||||||
class Patch:
|
class Patch:
|
||||||
Major = 0
|
Major = 11
|
||||||
Minor = 1
|
Minor = 3
|
||||||
allWorlds: List[World]
|
allWorlds: List[World]
|
||||||
myWorld: World
|
myWorld: World
|
||||||
seedGuid: str
|
seedGuid: str
|
||||||
|
@ -105,13 +114,16 @@ class Patch:
|
||||||
|
|
||||||
self.WriteDiggingGameRng()
|
self.WriteDiggingGameRng()
|
||||||
|
|
||||||
self.WritePrizeShuffle()
|
self.WritePrizeShuffle(self.myWorld.WorldState.DropPrizes)
|
||||||
|
|
||||||
self.WriteRemoveEquipmentFromUncle( self.myWorld.GetLocation("Link's Uncle").APLocation.item.item if
|
self.WriteRemoveEquipmentFromUncle( self.myWorld.GetLocation("Link's Uncle").APLocation.item.item if
|
||||||
self.myWorld.GetLocation("Link's Uncle").APLocation.item.game == "SMZ3" else
|
self.myWorld.GetLocation("Link's Uncle").APLocation.item.game == "SMZ3" else
|
||||||
Item(ItemType.Something))
|
Item(ItemType.Something))
|
||||||
|
|
||||||
self.WriteGanonInvicible(config.GanonInvincible)
|
self.WriteGanonInvicible(config.Goal)
|
||||||
|
self.WritePreOpenPyramid(config.Goal)
|
||||||
|
self.WriteCrystalsNeeded(self.myWorld.TowerCrystals, self.myWorld.GanonCrystals)
|
||||||
|
self.WriteBossesNeeded(self.myWorld.TourianBossTokens)
|
||||||
self.WriteRngBlock()
|
self.WriteRngBlock()
|
||||||
|
|
||||||
self.WriteSaveAndQuitFromBossRoom()
|
self.WriteSaveAndQuitFromBossRoom()
|
||||||
|
@ -135,26 +147,27 @@ class Patch:
|
||||||
return {patch[0]:patch[1] for patch in self.patches}
|
return {patch[0]:patch[1] for patch in self.patches}
|
||||||
|
|
||||||
def WriteMedallions(self):
|
def WriteMedallions(self):
|
||||||
|
from worlds.smz3.TotalSMZ3.WorldState import Medallion
|
||||||
turtleRock = next(region for region in self.myWorld.Regions if isinstance(region, TurtleRock))
|
turtleRock = next(region for region in self.myWorld.Regions if isinstance(region, TurtleRock))
|
||||||
miseryMire = next(region for region in self.myWorld.Regions if isinstance(region, MiseryMire))
|
miseryMire = next(region for region in self.myWorld.Regions if isinstance(region, MiseryMire))
|
||||||
|
|
||||||
turtleRockAddresses = [0x308023, 0xD020, 0xD0FF, 0xD1DE ]
|
turtleRockAddresses = [0x308023, 0xD020, 0xD0FF, 0xD1DE ]
|
||||||
miseryMireAddresses = [ 0x308022, 0xCFF2, 0xD0D1, 0xD1B0 ]
|
miseryMireAddresses = [ 0x308022, 0xCFF2, 0xD0D1, 0xD1B0 ]
|
||||||
|
|
||||||
if turtleRock.Medallion == ItemType.Bombos:
|
if turtleRock.Medallion == Medallion.Bombos:
|
||||||
turtleRockValues = [0x00, 0x51, 0x10, 0x00]
|
turtleRockValues = [0x00, 0x51, 0x10, 0x00]
|
||||||
elif turtleRock.Medallion == ItemType.Ether:
|
elif turtleRock.Medallion == Medallion.Ether:
|
||||||
turtleRockValues = [0x01, 0x51, 0x18, 0x00]
|
turtleRockValues = [0x01, 0x51, 0x18, 0x00]
|
||||||
elif turtleRock.Medallion == ItemType.Quake:
|
elif turtleRock.Medallion == Medallion.Quake:
|
||||||
turtleRockValues = [0x02, 0x14, 0xEF, 0xC4]
|
turtleRockValues = [0x02, 0x14, 0xEF, 0xC4]
|
||||||
else:
|
else:
|
||||||
raise exception(f"Tried using {turtleRock.Medallion} in place of Turtle Rock medallion")
|
raise exception(f"Tried using {turtleRock.Medallion} in place of Turtle Rock medallion")
|
||||||
|
|
||||||
if miseryMire.Medallion == ItemType.Bombos:
|
if miseryMire.Medallion == Medallion.Bombos:
|
||||||
miseryMireValues = [0x00, 0x51, 0x00, 0x00]
|
miseryMireValues = [0x00, 0x51, 0x00, 0x00]
|
||||||
elif miseryMire.Medallion == ItemType.Ether:
|
elif miseryMire.Medallion == Medallion.Ether:
|
||||||
miseryMireValues = [0x01, 0x13, 0x9F, 0xF1]
|
miseryMireValues = [0x01, 0x13, 0x9F, 0xF1]
|
||||||
elif miseryMire.Medallion == ItemType.Quake:
|
elif miseryMire.Medallion == Medallion.Quake:
|
||||||
miseryMireValues = [0x02, 0x51, 0x08, 0x00]
|
miseryMireValues = [0x02, 0x51, 0x08, 0x00]
|
||||||
else:
|
else:
|
||||||
raise exception(f"Tried using {miseryMire.Medallion} in place of Misery Mire medallion")
|
raise exception(f"Tried using {miseryMire.Medallion} in place of Misery Mire medallion")
|
||||||
|
@ -174,12 +187,19 @@ class Patch:
|
||||||
self.rnd.shuffle(pendantsBlueRed)
|
self.rnd.shuffle(pendantsBlueRed)
|
||||||
pendantRewards = pendantsGreen + pendantsBlueRed
|
pendantRewards = pendantsGreen + pendantsBlueRed
|
||||||
|
|
||||||
|
bossTokens = [ 1, 2, 3, 4 ]
|
||||||
|
|
||||||
regions = [region for region in self.myWorld.Regions if isinstance(region, IReward)]
|
regions = [region for region in self.myWorld.Regions if isinstance(region, IReward)]
|
||||||
crystalRegions = [region for region in regions if region.Reward == RewardType.CrystalBlue] + [region for region in regions if region.Reward == RewardType.CrystalRed]
|
crystalRegions = [region for region in regions if region.Reward == RewardType.CrystalBlue] + [region for region in regions if region.Reward == RewardType.CrystalRed]
|
||||||
pendantRegions = [region for region in regions if region.Reward == RewardType.PendantGreen] + [region for region in regions if region.Reward == RewardType.PendantNonGreen]
|
pendantRegions = [region for region in regions if region.Reward == RewardType.PendantGreen] + [region for region in regions if region.Reward == RewardType.PendantNonGreen]
|
||||||
|
bossRegions = [region for region in regions if region.Reward == RewardType.BossTokenKraid] + \
|
||||||
|
[region for region in regions if region.Reward == RewardType.BossTokenPhantoon] + \
|
||||||
|
[region for region in regions if region.Reward == RewardType.BossTokenDraygon] + \
|
||||||
|
[region for region in regions if region.Reward == RewardType.BossTokenRidley]
|
||||||
|
|
||||||
self.patches += self.RewardPatches(crystalRegions, crystalRewards, self.CrystalValues)
|
self.patches += self.RewardPatches(crystalRegions, crystalRewards, self.CrystalValues)
|
||||||
self.patches += self.RewardPatches(pendantRegions, pendantRewards, self.PendantValues)
|
self.patches += self.RewardPatches(pendantRegions, pendantRewards, self.PendantValues)
|
||||||
|
self.patches += self.RewardPatches(bossRegions, bossTokens, self.BossTokenValues)
|
||||||
|
|
||||||
def RewardPatches(self, regions: List[IReward], rewards: List[int], rewardValues: Callable):
|
def RewardPatches(self, regions: List[IReward], rewards: List[int], rewardValues: Callable):
|
||||||
addresses = [self.RewardAddresses(region) for region in regions]
|
addresses = [self.RewardAddresses(region) for region in regions]
|
||||||
|
@ -189,17 +209,22 @@ class Patch:
|
||||||
|
|
||||||
def RewardAddresses(self, region: IReward):
|
def RewardAddresses(self, region: IReward):
|
||||||
regionType = {
|
regionType = {
|
||||||
EasternPalace : [ 0x2A09D, 0xABEF8, 0xABEF9, 0x308052, 0x30807C, 0x1C6FE ],
|
EasternPalace : [ 0x2A09D, 0xABEF8, 0xABEF9, 0x308052, 0x30807C, 0x1C6FE, 0x30D100],
|
||||||
DesertPalace : [ 0x2A09E, 0xABF1C, 0xABF1D, 0x308053, 0x308078, 0x1C6FF ],
|
DesertPalace : [ 0x2A09E, 0xABF1C, 0xABF1D, 0x308053, 0x308078, 0x1C6FF, 0x30D101 ],
|
||||||
TowerOfHera : [ 0x2A0A5, 0xABF0A, 0xABF0B, 0x30805A, 0x30807A, 0x1C706 ],
|
TowerOfHera : [ 0x2A0A5, 0xABF0A, 0xABF0B, 0x30805A, 0x30807A, 0x1C706, 0x30D102 ],
|
||||||
PalaceOfDarkness : [ 0x2A0A1, 0xABF00, 0xABF01, 0x308056, 0x30807D, 0x1C702 ],
|
PalaceOfDarkness : [ 0x2A0A1, 0xABF00, 0xABF01, 0x308056, 0x30807D, 0x1C702, 0x30D103 ],
|
||||||
SwampPalace : [ 0x2A0A0, 0xABF6C, 0xABF6D, 0x308055, 0x308071, 0x1C701 ],
|
SwampPalace : [ 0x2A0A0, 0xABF6C, 0xABF6D, 0x308055, 0x308071, 0x1C701, 0x30D104 ],
|
||||||
SkullWoods : [ 0x2A0A3, 0xABF12, 0xABF13, 0x308058, 0x30807B, 0x1C704 ],
|
SkullWoods : [ 0x2A0A3, 0xABF12, 0xABF13, 0x308058, 0x30807B, 0x1C704, 0x30D105 ],
|
||||||
ThievesTown : [ 0x2A0A6, 0xABF36, 0xABF37, 0x30805B, 0x308077, 0x1C707 ],
|
ThievesTown : [ 0x2A0A6, 0xABF36, 0xABF37, 0x30805B, 0x308077, 0x1C707, 0x30D106 ],
|
||||||
IcePalace : [ 0x2A0A4, 0xABF5A, 0xABF5B, 0x308059, 0x308073, 0x1C705 ],
|
IcePalace : [ 0x2A0A4, 0xABF5A, 0xABF5B, 0x308059, 0x308073, 0x1C705, 0x30D107 ],
|
||||||
MiseryMire : [ 0x2A0A2, 0xABF48, 0xABF49, 0x308057, 0x308075, 0x1C703 ],
|
MiseryMire : [ 0x2A0A2, 0xABF48, 0xABF49, 0x308057, 0x308075, 0x1C703, 0x30D108 ],
|
||||||
TurtleRock : [ 0x2A0A7, 0xABF24, 0xABF25, 0x30805C, 0x308079, 0x1C708 ]
|
TurtleRock : [ 0x2A0A7, 0xABF24, 0xABF25, 0x30805C, 0x308079, 0x1C708, 0x30D109 ],
|
||||||
|
Kraid : [ 0xF26002, 0xF26004, 0xF26005, 0xF26000, 0xF26006, 0xF26007, 0x82FD36 ],
|
||||||
|
WreckedShip : [ 0xF2600A, 0xF2600C, 0xF2600D, 0xF26008, 0xF2600E, 0xF2600F, 0x82FE26 ],
|
||||||
|
Inner : [ 0xF26012, 0xF26014, 0xF26015, 0xF26010, 0xF26016, 0xF26017, 0x82FE76 ],
|
||||||
|
East : [ 0xF2601A, 0xF2601C, 0xF2601D, 0xF26018, 0xF2601E, 0xF2601F, 0x82FDD6 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
result = regionType.get(type(region), None)
|
result = regionType.get(type(region), None)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise exception(f"Region {region} should not be a dungeon reward region")
|
raise exception(f"Region {region} should not be a dungeon reward region")
|
||||||
|
@ -208,13 +233,13 @@ class Patch:
|
||||||
|
|
||||||
def CrystalValues(self, crystal: int):
|
def CrystalValues(self, crystal: int):
|
||||||
crystalMap = {
|
crystalMap = {
|
||||||
1 : [ 0x02, 0x34, 0x64, 0x40, 0x7F, 0x06 ],
|
1 : [ 0x02, 0x34, 0x64, 0x40, 0x7F, 0x06, 0x10 ],
|
||||||
2 : [ 0x10, 0x34, 0x64, 0x40, 0x79, 0x06 ],
|
2 : [ 0x10, 0x34, 0x64, 0x40, 0x79, 0x06, 0x10 ],
|
||||||
3 : [ 0x40, 0x34, 0x64, 0x40, 0x6C, 0x06 ],
|
3 : [ 0x40, 0x34, 0x64, 0x40, 0x6C, 0x06, 0x10 ],
|
||||||
4 : [ 0x20, 0x34, 0x64, 0x40, 0x6D, 0x06 ],
|
4 : [ 0x20, 0x34, 0x64, 0x40, 0x6D, 0x06, 0x10 ],
|
||||||
5 : [ 0x04, 0x32, 0x64, 0x40, 0x6E, 0x06 ],
|
5 : [ 0x04, 0x32, 0x64, 0x40, 0x6E, 0x06, 0x11 ],
|
||||||
6 : [ 0x01, 0x32, 0x64, 0x40, 0x6F, 0x06 ],
|
6 : [ 0x01, 0x32, 0x64, 0x40, 0x6F, 0x06, 0x11 ],
|
||||||
7 : [ 0x08, 0x34, 0x64, 0x40, 0x7C, 0x06 ],
|
7 : [ 0x08, 0x34, 0x64, 0x40, 0x7C, 0x06, 0x10 ],
|
||||||
}
|
}
|
||||||
result = crystalMap.get(crystal, None)
|
result = crystalMap.get(crystal, None)
|
||||||
if result is None:
|
if result is None:
|
||||||
|
@ -224,15 +249,28 @@ class Patch:
|
||||||
|
|
||||||
def PendantValues(self, pendant: int):
|
def PendantValues(self, pendant: int):
|
||||||
pendantMap = {
|
pendantMap = {
|
||||||
1 : [ 0x04, 0x38, 0x62, 0x00, 0x69, 0x01 ],
|
1 : [ 0x04, 0x38, 0x62, 0x00, 0x69, 0x01, 0x12 ],
|
||||||
2 : [ 0x01, 0x32, 0x60, 0x00, 0x69, 0x03 ],
|
2 : [ 0x01, 0x32, 0x60, 0x00, 0x69, 0x03, 0x14 ],
|
||||||
3 : [ 0x02, 0x34, 0x60, 0x00, 0x69, 0x02 ],
|
3 : [ 0x02, 0x34, 0x60, 0x00, 0x69, 0x02, 0x13 ]
|
||||||
}
|
}
|
||||||
result = pendantMap.get(pendant, None)
|
result = pendantMap.get(pendant, None)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise exception(f"Tried using {pendant} as a pendant number")
|
raise exception(f"Tried using {pendant} as a pendant number")
|
||||||
else:
|
else:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def BossTokenValues(self, token: int):
|
||||||
|
tokenMap = {
|
||||||
|
1 : [ 0x01, 0x38, 0x40, 0x80, 0x69, 0x80, 0x15 ],
|
||||||
|
2 : [ 0x02, 0x34, 0x42, 0x80, 0x69, 0x81, 0x16 ],
|
||||||
|
3 : [ 0x04, 0x34, 0x44, 0x80, 0x69, 0x82, 0x17 ],
|
||||||
|
4 : [ 0x08, 0x32, 0x46, 0x80, 0x69, 0x83, 0x18 ]
|
||||||
|
}
|
||||||
|
result = tokenMap.get(token, None)
|
||||||
|
if result is None:
|
||||||
|
raise exception(f"Tried using {token} as a boss token number")
|
||||||
|
else:
|
||||||
|
return result
|
||||||
|
|
||||||
def WriteSMLocations(self, locations: List[Location]):
|
def WriteSMLocations(self, locations: List[Location]):
|
||||||
def GetSMItemPLM(location:Location):
|
def GetSMItemPLM(location:Location):
|
||||||
|
@ -259,7 +297,7 @@ class Patch:
|
||||||
ItemType.SpaceJump : 0xEF1B,
|
ItemType.SpaceJump : 0xEF1B,
|
||||||
ItemType.ScrewAttack : 0xEF1F
|
ItemType.ScrewAttack : 0xEF1F
|
||||||
}
|
}
|
||||||
plmId = 0xEFE0 if self.myWorld.Config.GameMode == GameMode.Multiworld else \
|
plmId = 0xEFE0 if self.myWorld.Config.Multiworld else \
|
||||||
itemMap.get(location.APLocation.item.item.Type, 0xEFE0)
|
itemMap.get(location.APLocation.item.item.Type, 0xEFE0)
|
||||||
if (plmId == 0xEFE0):
|
if (plmId == 0xEFE0):
|
||||||
plmId += 4 if location.Type == LocationType.Chozo else 8 if location.Type == LocationType.Hidden else 0
|
plmId += 4 if location.Type == LocationType.Chozo else 8 if location.Type == LocationType.Hidden else 0
|
||||||
|
@ -268,7 +306,7 @@ class Patch:
|
||||||
return plmId
|
return plmId
|
||||||
|
|
||||||
for location in locations:
|
for location in locations:
|
||||||
if (self.myWorld.Config.GameMode == GameMode.Multiworld):
|
if (self.myWorld.Config.Multiworld):
|
||||||
self.patches.append((Snes(location.Address), getWordArray(GetSMItemPLM(location))))
|
self.patches.append((Snes(location.Address), getWordArray(GetSMItemPLM(location))))
|
||||||
self.patches.append(self.ItemTablePatch(location, self.GetZ3ItemId(location)))
|
self.patches.append(self.ItemTablePatch(location, self.GetZ3ItemId(location)))
|
||||||
else:
|
else:
|
||||||
|
@ -283,18 +321,14 @@ class Patch:
|
||||||
self.patches.append((Snes(0x9E3BB), [0xE4] if location.APLocation.item.game == "SMZ3" and location.APLocation.item.item.Type == ItemType.KeyTH else [0xEB]))
|
self.patches.append((Snes(0x9E3BB), [0xE4] if location.APLocation.item.game == "SMZ3" and location.APLocation.item.item.Type == ItemType.KeyTH else [0xEB]))
|
||||||
elif (location.Type in [LocationType.Pedestal, LocationType.Ether, LocationType.Bombos]):
|
elif (location.Type in [LocationType.Pedestal, LocationType.Ether, LocationType.Bombos]):
|
||||||
text = Texts.ItemTextbox(location.APLocation.item.item if location.APLocation.item.game == "SMZ3" else Item(ItemType.Something))
|
text = Texts.ItemTextbox(location.APLocation.item.item if location.APLocation.item.game == "SMZ3" else Item(ItemType.Something))
|
||||||
dialog = Dialog.Simple(text)
|
|
||||||
if (location.Type == LocationType.Pedestal):
|
if (location.Type == LocationType.Pedestal):
|
||||||
self.stringTable.SetPedestalText(text)
|
self.stringTable.SetPedestalText(text)
|
||||||
self.patches.append((Snes(0x308300), dialog))
|
|
||||||
elif (location.Type == LocationType.Ether):
|
elif (location.Type == LocationType.Ether):
|
||||||
self.stringTable.SetEtherText(text)
|
self.stringTable.SetEtherText(text)
|
||||||
self.patches.append((Snes(0x308F00), dialog))
|
|
||||||
elif (location.Type == LocationType.Bombos):
|
elif (location.Type == LocationType.Bombos):
|
||||||
self.stringTable.SetBombosText(text)
|
self.stringTable.SetBombosText(text)
|
||||||
self.patches.append((Snes(0x309000), dialog))
|
|
||||||
|
|
||||||
if (self.myWorld.Config.GameMode == GameMode.Multiworld):
|
if (self.myWorld.Config.Multiworld):
|
||||||
self.patches.append((Snes(location.Address), [(location.Id - 256)]))
|
self.patches.append((Snes(location.Address), [(location.Id - 256)]))
|
||||||
self.patches.append(self.ItemTablePatch(location, self.GetZ3ItemId(location)))
|
self.patches.append(self.ItemTablePatch(location, self.GetZ3ItemId(location)))
|
||||||
else:
|
else:
|
||||||
|
@ -305,11 +339,11 @@ class Patch:
|
||||||
item = location.APLocation.item.item
|
item = location.APLocation.item.item
|
||||||
itemDungeon = None
|
itemDungeon = None
|
||||||
if item.IsKey():
|
if item.IsKey():
|
||||||
itemDungeon = ItemType.Key if (not item.World.Config.Keysanity or item.Type != ItemType.KeyHC) else ItemType.KeyHC
|
itemDungeon = ItemType.Key
|
||||||
elif item.IsBigKey():
|
elif item.IsBigKey():
|
||||||
itemDungeon = ItemType.BigKey
|
itemDungeon = ItemType.BigKey
|
||||||
elif item.IsMap():
|
elif item.IsMap():
|
||||||
itemDungeon = ItemType.Map if (not item.World.Config.Keysanity or item.Type != ItemType.MapHC) else ItemType.MapHC
|
itemDungeon = ItemType.Map
|
||||||
elif item.IsCompass():
|
elif item.IsCompass():
|
||||||
itemDungeon = ItemType.Compass
|
itemDungeon = ItemType.Compass
|
||||||
|
|
||||||
|
@ -327,15 +361,11 @@ class Patch:
|
||||||
|
|
||||||
def WriteDungeonMusic(self, keysanity: bool):
|
def WriteDungeonMusic(self, keysanity: bool):
|
||||||
if (not keysanity):
|
if (not keysanity):
|
||||||
regions = [region for region in self.myWorld.Regions if isinstance(region, IReward)]
|
regions = [region for region in self.myWorld.Regions if isinstance(region, Z3Region) and isinstance(region, IReward) and
|
||||||
music = []
|
region.Reward != None and region.Reward != RewardType.Agahnim]
|
||||||
pendantRegions = [region for region in regions if region.Reward in [RewardType.PendantGreen, RewardType.PendantNonGreen]]
|
pendantRegions = [region for region in regions if region.Reward in [RewardType.PendantGreen, RewardType.PendantNonGreen]]
|
||||||
crystalRegions = [region for region in regions if region.Reward in [RewardType.CrystalBlue, RewardType.CrystalRed]]
|
crystalRegions = [region for region in regions if region.Reward in [RewardType.CrystalBlue, RewardType.CrystalRed]]
|
||||||
regions = pendantRegions + crystalRegions
|
music = [0x11 if (region.Reward == RewardType.PendantGreen or region.Reward == RewardType.PendantNonGreen) else 0x16 for region in regions]
|
||||||
music = [
|
|
||||||
0x11, 0x11, 0x11, 0x16, 0x16,
|
|
||||||
0x16, 0x16, 0x16, 0x16, 0x16,
|
|
||||||
]
|
|
||||||
self.patches += self.MusicPatches(regions, music)
|
self.patches += self.MusicPatches(regions, music)
|
||||||
|
|
||||||
#IEnumerable<byte> RandomDungeonMusic() {
|
#IEnumerable<byte> RandomDungeonMusic() {
|
||||||
|
@ -366,51 +396,13 @@ class Patch:
|
||||||
else:
|
else:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def WritePrizeShuffle(self):
|
def WritePrizeShuffle(self, dropPrizes):
|
||||||
prizePackItems = 56
|
self.patches.append((Snes(0x6FA78), [e.value for e in dropPrizes.Packs]))
|
||||||
treePullItems = 3
|
self.patches.append((Snes(0x1DFBD4), [e.value for e in dropPrizes.TreePulls]))
|
||||||
|
self.patches.append((Snes(0x6A9C8), [dropPrizes.CrabContinous.value]))
|
||||||
bytes = []
|
self.patches.append((Snes(0x6A9C4), [dropPrizes.CrabFinal.value]))
|
||||||
drop = 0
|
self.patches.append((Snes(0x6F993), [dropPrizes.Stun.value]))
|
||||||
final = 0
|
self.patches.append((Snes(0x1D82CC), [dropPrizes.Fish.value]))
|
||||||
|
|
||||||
pool = [
|
|
||||||
DropPrize.Heart, DropPrize.Heart, DropPrize.Heart, DropPrize.Heart, DropPrize.Green, DropPrize.Heart, DropPrize.Heart, DropPrize.Green, #// pack 1
|
|
||||||
DropPrize.Blue, DropPrize.Green, DropPrize.Blue, DropPrize.Red, DropPrize.Blue, DropPrize.Green, DropPrize.Blue, DropPrize.Blue, #// pack 2
|
|
||||||
DropPrize.FullMagic, DropPrize.Magic, DropPrize.Magic, DropPrize.Blue, DropPrize.FullMagic, DropPrize.Magic, DropPrize.Heart, DropPrize.Magic, #// pack 3
|
|
||||||
DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb4, DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb8, DropPrize.Bomb1, #// pack 4
|
|
||||||
DropPrize.Arrow5, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Arrow10, DropPrize.Arrow5, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Arrow10,#// pack 5
|
|
||||||
DropPrize.Magic, DropPrize.Green, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Magic, DropPrize.Bomb1, DropPrize.Green, DropPrize.Heart, #// pack 6
|
|
||||||
DropPrize.Heart, DropPrize.Fairy, DropPrize.FullMagic, DropPrize.Red, DropPrize.Bomb8, DropPrize.Heart, DropPrize.Red, DropPrize.Arrow10, #// pack 7
|
|
||||||
DropPrize.Green, DropPrize.Blue, DropPrize.Red,#// from pull trees
|
|
||||||
DropPrize.Green, DropPrize.Red,#// from prize crab
|
|
||||||
DropPrize.Green, #// stunned prize
|
|
||||||
DropPrize.Red,#// saved fish prize
|
|
||||||
]
|
|
||||||
|
|
||||||
prizes = pool
|
|
||||||
self.rnd.shuffle(prizes)
|
|
||||||
|
|
||||||
#/* prize pack drop order */
|
|
||||||
(bytes, prizes) = SplitOff(prizes, prizePackItems)
|
|
||||||
self.patches.append((Snes(0x6FA78), [byte.value for byte in bytes]))
|
|
||||||
|
|
||||||
#/* tree pull prizes */
|
|
||||||
(bytes, prizes) = SplitOff(prizes, treePullItems)
|
|
||||||
self.patches.append((Snes(0x1DFBD4), [byte.value for byte in bytes]))
|
|
||||||
|
|
||||||
#/* crab prizes */
|
|
||||||
(drop, final, prizes) = (prizes[0], prizes[1], prizes[2:])
|
|
||||||
self.patches.append((Snes(0x6A9C8), [ drop.value ]))
|
|
||||||
self.patches.append((Snes(0x6A9C4), [ final.value ]))
|
|
||||||
|
|
||||||
#/* stun prize */
|
|
||||||
(drop, prizes) = (prizes[0], prizes[1:])
|
|
||||||
self.patches.append((Snes(0x6F993), [ drop.value ]))
|
|
||||||
|
|
||||||
#/* fish prize */
|
|
||||||
drop = prizes[0]
|
|
||||||
self.patches.append((Snes(0x1D82CC), [ drop.value ]))
|
|
||||||
|
|
||||||
self.patches += self.EnemyPrizePackDistribution()
|
self.patches += self.EnemyPrizePackDistribution()
|
||||||
|
|
||||||
|
@ -524,46 +516,29 @@ class Patch:
|
||||||
redCrystalDungeons = [region for region in regions if region.Reward == RewardType.CrystalRed]
|
redCrystalDungeons = [region for region in regions if region.Reward == RewardType.CrystalRed]
|
||||||
|
|
||||||
sahasrahla = Texts.SahasrahlaReveal(greenPendantDungeon)
|
sahasrahla = Texts.SahasrahlaReveal(greenPendantDungeon)
|
||||||
self.patches.append((Snes(0x308A00), Dialog.Simple(sahasrahla)))
|
|
||||||
self.stringTable.SetSahasrahlaRevealText(sahasrahla)
|
self.stringTable.SetSahasrahlaRevealText(sahasrahla)
|
||||||
|
|
||||||
bombShop = Texts.BombShopReveal(redCrystalDungeons)
|
bombShop = Texts.BombShopReveal(redCrystalDungeons)
|
||||||
self.patches.append((Snes(0x308E00), Dialog.Simple(bombShop)))
|
|
||||||
self.stringTable.SetBombShopRevealText(bombShop)
|
self.stringTable.SetBombShopRevealText(bombShop)
|
||||||
|
|
||||||
blind = Texts.Blind(self.rnd)
|
blind = Texts.Blind(self.rnd)
|
||||||
self.patches.append((Snes(0x308800), Dialog.Simple(blind)))
|
|
||||||
self.stringTable.SetBlindText(blind)
|
self.stringTable.SetBlindText(blind)
|
||||||
|
|
||||||
tavernMan = Texts.TavernMan(self.rnd)
|
tavernMan = Texts.TavernMan(self.rnd)
|
||||||
self.patches.append((Snes(0x308C00), Dialog.Simple(tavernMan)))
|
|
||||||
self.stringTable.SetTavernManText(tavernMan)
|
self.stringTable.SetTavernManText(tavernMan)
|
||||||
|
|
||||||
ganon = Texts.GanonFirstPhase(self.rnd)
|
ganon = Texts.GanonFirstPhase(self.rnd)
|
||||||
self.patches.append((Snes(0x308600), Dialog.Simple(ganon)))
|
|
||||||
self.stringTable.SetGanonFirstPhaseText(ganon)
|
self.stringTable.SetGanonFirstPhaseText(ganon)
|
||||||
|
|
||||||
#// Todo: Verify these two are correct if ganon invincible patch is ever added
|
|
||||||
#// ganon_fall_in_alt in v30
|
|
||||||
ganonFirstPhaseInvincible = "You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!"
|
|
||||||
self.patches.append((Snes(0x309100), Dialog.Simple(ganonFirstPhaseInvincible)))
|
|
||||||
|
|
||||||
#// ganon_phase_3_alt in v30
|
|
||||||
ganonThirdPhaseInvincible = "Got wax in\nyour ears?\nI cannot die!"
|
|
||||||
self.patches.append((Snes(0x309200), Dialog.Simple(ganonThirdPhaseInvincible)))
|
|
||||||
#// ---
|
|
||||||
|
|
||||||
silversLocation = [loc for world in self.allWorlds for loc in world.Locations if loc.ItemIs(ItemType.SilverArrows, self.myWorld)]
|
silversLocation = [loc for world in self.allWorlds for loc in world.Locations if loc.ItemIs(ItemType.SilverArrows, self.myWorld)]
|
||||||
if len(silversLocation) == 0:
|
if len(silversLocation) == 0:
|
||||||
silvers = Texts.GanonThirdPhaseMulti(None, self.myWorld, self.silversWorldID, self.playerIDToNames[self.silversWorldID])
|
silvers = Texts.GanonThirdPhaseMulti(None, self.myWorld, self.silversWorldID, self.playerIDToNames[self.silversWorldID])
|
||||||
else:
|
else:
|
||||||
silvers = Texts.GanonThirdPhaseMulti(silversLocation[0].Region, self.myWorld) if config.GameMode == GameMode.Multiworld else \
|
silvers = Texts.GanonThirdPhaseMulti(silversLocation[0].Region, self.myWorld) if config.Multiworld else \
|
||||||
Texts.GanonThirdPhaseSingle(silversLocation[0].Region)
|
Texts.GanonThirdPhaseSingle(silversLocation[0].Region)
|
||||||
self.patches.append((Snes(0x308700), Dialog.Simple(silvers)))
|
|
||||||
self.stringTable.SetGanonThirdPhaseText(silvers)
|
self.stringTable.SetGanonThirdPhaseText(silvers)
|
||||||
|
|
||||||
triforceRoom = Texts.TriforceRoom(self.rnd)
|
triforceRoom = Texts.TriforceRoom(self.rnd)
|
||||||
self.patches.append((Snes(0x308400), Dialog.Simple(triforceRoom)))
|
|
||||||
self.stringTable.SetTriforceRoomText(triforceRoom)
|
self.stringTable.SetTriforceRoomText(triforceRoom)
|
||||||
|
|
||||||
def WriteStringTable(self):
|
def WriteStringTable(self):
|
||||||
|
@ -579,26 +554,32 @@ class Patch:
|
||||||
return bytearray(name, 'utf8')
|
return bytearray(name, 'utf8')
|
||||||
|
|
||||||
def WriteSeedData(self):
|
def WriteSeedData(self):
|
||||||
configField = \
|
configField1 = \
|
||||||
((1 if self.myWorld.Config.Race else 0) << 15) | \
|
((1 if self.myWorld.Config.Race else 0) << 15) | \
|
||||||
((1 if self.myWorld.Config.Keysanity else 0) << 13) | \
|
((1 if self.myWorld.Config.Keysanity else 0) << 13) | \
|
||||||
((1 if self.myWorld.Config.GameMode == Config.GameMode.Multiworld else 0) << 12) | \
|
((1 if self.myWorld.Config.Multiworld else 0) << 12) | \
|
||||||
(self.myWorld.Config.Z3Logic.value << 10) | \
|
(self.myWorld.Config.Z3Logic.value << 10) | \
|
||||||
(self.myWorld.Config.SMLogic.value << 8) | \
|
(self.myWorld.Config.SMLogic.value << 8) | \
|
||||||
(Patch.Major << 4) | \
|
(Patch.Major << 4) | \
|
||||||
(Patch.Minor << 0)
|
(Patch.Minor << 0)
|
||||||
|
|
||||||
|
configField2 = \
|
||||||
|
((1 if self.myWorld.Config.SwordLocation else 0) << 14) | \
|
||||||
|
((1 if self.myWorld.Config.MorphLocation else 0) << 12) | \
|
||||||
|
((1 if self.myWorld.Config.Goal else 0) << 8)
|
||||||
|
|
||||||
self.patches.append((Snes(0x80FF50), getWordArray(self.myWorld.Id)))
|
self.patches.append((Snes(0x80FF50), getWordArray(self.myWorld.Id)))
|
||||||
self.patches.append((Snes(0x80FF52), getWordArray(configField)))
|
self.patches.append((Snes(0x80FF52), getWordArray(configField1)))
|
||||||
self.patches.append((Snes(0x80FF54), getDoubleWordArray(self.seed)))
|
self.patches.append((Snes(0x80FF54), getDoubleWordArray(self.seed)))
|
||||||
|
self.patches.append((Snes(0x80FF58), getWordArray(configField2)))
|
||||||
#/* Reserve the rest of the space for future use */
|
#/* Reserve the rest of the space for future use */
|
||||||
self.patches.append((Snes(0x80FF58), [0x00] * 8))
|
self.patches.append((Snes(0x80FF5A), [0x00] * 6))
|
||||||
self.patches.append((Snes(0x80FF60), bytearray(self.seedGuid, 'utf8')))
|
self.patches.append((Snes(0x80FF60), bytearray(self.seedGuid, 'utf8')))
|
||||||
self.patches.append((Snes(0x80FF80), bytearray(self.myWorld.Guid, 'utf8')))
|
self.patches.append((Snes(0x80FF80), bytearray(self.myWorld.Guid, 'utf8')))
|
||||||
|
|
||||||
def WriteCommonFlags(self):
|
def WriteCommonFlags(self):
|
||||||
#/* Common Combo Configuration flags at [asm]/config.asm */
|
#/* Common Combo Configuration flags at [asm]/config.asm */
|
||||||
if (self.myWorld.Config.GameMode == GameMode.Multiworld):
|
if (self.myWorld.Config.Multiworld):
|
||||||
self.patches.append((Snes(0xF47000), getWordArray(0x0001)))
|
self.patches.append((Snes(0xF47000), getWordArray(0x0001)))
|
||||||
if (self.myWorld.Config.Keysanity):
|
if (self.myWorld.Config.Keysanity):
|
||||||
self.patches.append((Snes(0xF47006), getWordArray(0x0001)))
|
self.patches.append((Snes(0xF47006), getWordArray(0x0001)))
|
||||||
|
@ -619,97 +600,104 @@ class Patch:
|
||||||
if (self.myWorld.Config.Keysanity):
|
if (self.myWorld.Config.Keysanity):
|
||||||
self.patches.append((Snes(0x40003B), [ 1 ])) #// MapMode #$00 = Always On (default) - #$01 = Require Map Item
|
self.patches.append((Snes(0x40003B), [ 1 ])) #// MapMode #$00 = Always On (default) - #$01 = Require Map Item
|
||||||
self.patches.append((Snes(0x400045), [ 0x0f ])) #// display ----dcba a: Small Keys, b: Big Key, c: Map, d: Compass
|
self.patches.append((Snes(0x400045), [ 0x0f ])) #// display ----dcba a: Small Keys, b: Big Key, c: Map, d: Compass
|
||||||
self.patches.append((Snes(0x40016A), [ 0x01 ])) #// enable local item dialog boxes for dungeon and keycard items
|
self.patches.append((Snes(0x40016A), [ 0x01 ])) #// FreeItemText: db #$01 ; #00 = Off (default) - #$01 = On
|
||||||
|
|
||||||
def WriteSMKeyCardDoors(self):
|
def WriteSMKeyCardDoors(self):
|
||||||
if (not self.myWorld.Config.Keysanity):
|
plaquePlm = 0xd410
|
||||||
return
|
|
||||||
|
|
||||||
plaquePLm = 0xd410
|
|
||||||
|
|
||||||
doorList = [
|
|
||||||
#// RoomId Door Facing yyxx Keycard Event Type Plaque type yyxx, Address (if 0 a dynamic PLM is created)
|
|
||||||
#// Crateria
|
|
||||||
[ 0x91F8, KeycardDoors.Right, 0x2601, KeycardEvents.CrateriaLevel1, KeycardPlaque.Level1, 0x2400, 0x0000 ], #// Crateria - Landing Site - Door to gauntlet
|
|
||||||
[ 0x91F8, KeycardDoors.Left, 0x168E, KeycardEvents.CrateriaLevel1, KeycardPlaque.Level1, 0x148F, 0x801E ], #// Crateria - Landing Site - Door to landing site PB
|
|
||||||
[ 0x948C, KeycardDoors.Left, 0x062E, KeycardEvents.CrateriaLevel2, KeycardPlaque.Level2, 0x042F, 0x8222 ], #// Crateria - Before Moat - Door to moat (overwrite PB door)
|
|
||||||
[ 0x99BD, KeycardDoors.Left, 0x660E, KeycardEvents.CrateriaBoss, KeycardPlaque.Boss, 0x640F, 0x8470 ], #// Crateria - Before G4 - Door to G4
|
|
||||||
[ 0x9879, KeycardDoors.Left, 0x062E, KeycardEvents.CrateriaBoss, KeycardPlaque.Boss, 0x042F, 0x8420 ], #// Crateria - Before BT - Door to Bomb Torizo
|
|
||||||
|
|
||||||
#// Brinstar
|
|
||||||
[ 0x9F11, KeycardDoors.Left, 0x060E, KeycardEvents.BrinstarLevel1, KeycardPlaque.Level1, 0x040F, 0x8784 ], #// Brinstar - Blue Brinstar - Door to ceiling e-tank room
|
|
||||||
|
|
||||||
[ 0x9AD9, KeycardDoors.Right, 0xA601, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0xA400, 0x0000 ], #// Brinstar - Green Brinstar - Door to etecoon area
|
|
||||||
[ 0x9D9C, KeycardDoors.Down, 0x0336, KeycardEvents.BrinstarBoss, KeycardPlaque.Boss, 0x0234, 0x863A ], #// Brinstar - Pink Brinstar - Door to spore spawn
|
|
||||||
[ 0xA130, KeycardDoors.Left, 0x161E, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0x141F, 0x881C ], #// Brinstar - Pink Brinstar - Door to wave gate e-tank
|
|
||||||
[ 0xA0A4, KeycardDoors.Left, 0x062E, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0x042F, 0x0000 ], #// Brinstar - Pink Brinstar - Door to spore spawn super
|
|
||||||
|
|
||||||
[ 0xA56B, KeycardDoors.Left, 0x161E, KeycardEvents.BrinstarBoss, KeycardPlaque.Boss, 0x141F, 0x8A1A ], #// Brinstar - Before Kraid - Door to Kraid
|
|
||||||
|
|
||||||
#// Upper Norfair
|
|
||||||
[ 0xA7DE, KeycardDoors.Right, 0x3601, KeycardEvents.NorfairLevel1, KeycardPlaque.Level1, 0x3400, 0x8B00 ], #// Norfair - Business Centre - Door towards Ice
|
|
||||||
[ 0xA923, KeycardDoors.Right, 0x0601, KeycardEvents.NorfairLevel1, KeycardPlaque.Level1, 0x0400, 0x0000 ], #// Norfair - Pre-Crocomire - Door towards Ice
|
|
||||||
|
|
||||||
[ 0xA788, KeycardDoors.Left, 0x162E, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x142F, 0x8AEA ], #// Norfair - Lava Missile Room - Door towards Bubble Mountain
|
|
||||||
[ 0xAF72, KeycardDoors.Left, 0x061E, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x041F, 0x0000 ], #// Norfair - After frog speedway - Door to Bubble Mountain
|
|
||||||
[ 0xAEDF, KeycardDoors.Down, 0x0206, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x0204, 0x0000 ], #// Norfair - Below bubble mountain - Door to Bubble Mountain
|
|
||||||
[ 0xAD5E, KeycardDoors.Right, 0x0601, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x0400, 0x0000 ], #// Norfair - LN Escape - Door to Bubble Mountain
|
|
||||||
|
|
||||||
[ 0xA923, KeycardDoors.Up, 0x2DC6, KeycardEvents.NorfairBoss, KeycardPlaque.Boss, 0x2EC4, 0x8B96 ], #// Norfair - Pre-Crocomire - Door to Crocomire
|
|
||||||
|
|
||||||
#// Lower Norfair
|
|
||||||
[ 0xB4AD, KeycardDoors.Left, 0x160E, KeycardEvents.LowerNorfairLevel1, KeycardPlaque.Level1, 0x140F, 0x0000 ], #// Lower Norfair - WRITG - Door to Amphitheatre
|
|
||||||
[ 0xAD5E, KeycardDoors.Left, 0x065E, KeycardEvents.LowerNorfairLevel1, KeycardPlaque.Level1, 0x045F, 0x0000 ], #// Lower Norfair - Exit - Door to "Reverse LN Entry"
|
|
||||||
[ 0xB37A, KeycardDoors.Right, 0x0601, KeycardEvents.LowerNorfairBoss, KeycardPlaque.Boss, 0x0400, 0x8EA6 ], #// Lower Norfair - Pre-Ridley - Door to Ridley
|
|
||||||
|
|
||||||
#// Maridia
|
|
||||||
[ 0xD0B9, KeycardDoors.Left, 0x065E, KeycardEvents.MaridiaLevel1, KeycardPlaque.Level1, 0x045F, 0x0000 ], #// Maridia - Mt. Everest - Door to Pink Maridia
|
|
||||||
[ 0xD5A7, KeycardDoors.Right, 0x1601, KeycardEvents.MaridiaLevel1, KeycardPlaque.Level1, 0x1400, 0x0000 ], #// Maridia - Aqueduct - Door towards Beach
|
|
||||||
|
|
||||||
[ 0xD617, KeycardDoors.Left, 0x063E, KeycardEvents.MaridiaLevel2, KeycardPlaque.Level2, 0x043F, 0x0000 ], #// Maridia - Pre-Botwoon - Door to Botwoon
|
|
||||||
[ 0xD913, KeycardDoors.Right, 0x2601, KeycardEvents.MaridiaLevel2, KeycardPlaque.Level2, 0x2400, 0x0000 ], #// Maridia - Pre-Colloseum - Door to post-botwoon
|
|
||||||
|
|
||||||
[ 0xD78F, KeycardDoors.Right, 0x2601, KeycardEvents.MaridiaBoss, KeycardPlaque.Boss, 0x2400, 0xC73B ], #// Maridia - Precious Room - Door to Draygon
|
|
||||||
|
|
||||||
[ 0xDA2B, KeycardDoors.BossLeft, 0x164E, 0x00f0, KeycardPlaque.Null, 0x144F, 0x0000 ], #// Maridia - Change Cac Alley Door to Boss Door (prevents key breaking)
|
|
||||||
|
|
||||||
#// Wrecked Ship
|
|
||||||
[ 0x93FE, KeycardDoors.Left, 0x167E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x147F, 0x0000 ], #// Wrecked Ship - Outside Wrecked Ship West - Door to Reserve Tank Check
|
|
||||||
[ 0x968F, KeycardDoors.Left, 0x060E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x040F, 0x0000 ], #// Wrecked Ship - Outside Wrecked Ship West - Door to Bowling Alley
|
|
||||||
[ 0xCE40, KeycardDoors.Left, 0x060E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x040F, 0x0000 ], #// Wrecked Ship - Gravity Suit - Door to Bowling Alley
|
|
||||||
|
|
||||||
[ 0xCC6F, KeycardDoors.Left, 0x064E, KeycardEvents.WreckedShipBoss, KeycardPlaque.Boss, 0x044F, 0xC29D ], #// Wrecked Ship - Pre-Phantoon - Door to Phantoon
|
|
||||||
]
|
|
||||||
|
|
||||||
doorId = 0x0000
|
|
||||||
plmTablePos = 0xf800
|
plmTablePos = 0xf800
|
||||||
for door in doorList:
|
|
||||||
doorArgs = doorId | door[3] if door[4] != KeycardPlaque.Null else door[3]
|
|
||||||
if (door[6] == 0):
|
|
||||||
#// Write dynamic door
|
|
||||||
doorData = []
|
|
||||||
for x in door[0:3]:
|
|
||||||
doorData += getWordArray(x)
|
|
||||||
doorData += getWordArray(doorArgs)
|
|
||||||
self.patches.append((Snes(0x8f0000 + plmTablePos), doorData))
|
|
||||||
plmTablePos += 0x08
|
|
||||||
else:
|
|
||||||
#// Overwrite existing door
|
|
||||||
doorData = []
|
|
||||||
for x in door[1:3]:
|
|
||||||
doorData += getWordArray(x)
|
|
||||||
doorData += getWordArray(doorArgs)
|
|
||||||
self.patches.append((Snes(0x8f0000 + door[6]), doorData))
|
|
||||||
if((door[3] == KeycardEvents.BrinstarBoss and door[0] != 0x9D9C) or door[3] == KeycardEvents.LowerNorfairBoss or door[3] == KeycardEvents.MaridiaBoss or door[3] == KeycardEvents.WreckedShipBoss):
|
|
||||||
#// Overwrite the extra parts of the Gadora with a PLM that just deletes itself
|
|
||||||
self.patches.append((Snes(0x8f0000 + door[6] + 0x06), [ 0x2F, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x2F, 0xB6, 0x00, 0x00, 0x00, 0x00 ]))
|
|
||||||
|
|
||||||
#// Plaque data
|
if ( self.myWorld.Config.Keysanity):
|
||||||
if (door[4] != KeycardPlaque.Null):
|
doorList = [
|
||||||
plaqueData = getWordArray(door[0]) + getWordArray(plaquePLm) + getWordArray(door[5]) + getWordArray(door[4])
|
#// RoomId Door Facing yyxx Keycard Event Type Plaque type yyxx, Address (if 0 a dynamic PLM is created)
|
||||||
self.patches.append((Snes(0x8f0000 + plmTablePos), plaqueData))
|
#// Crateria
|
||||||
plmTablePos += 0x08
|
[ 0x91F8, KeycardDoors.Right, 0x2601, KeycardEvents.CrateriaLevel1, KeycardPlaque.Level1, 0x2400, 0x0000 ], #// Crateria - Landing Site - Door to gauntlet
|
||||||
doorId += 1
|
[ 0x91F8, KeycardDoors.Left, 0x168E, KeycardEvents.CrateriaLevel1, KeycardPlaque.Level1, 0x148F, 0x801E ], #// Crateria - Landing Site - Door to landing site PB
|
||||||
|
[ 0x948C, KeycardDoors.Left, 0x062E, KeycardEvents.CrateriaLevel2, KeycardPlaque.Level2, 0x042F, 0x8222 ], #// Crateria - Before Moat - Door to moat (overwrite PB door)
|
||||||
|
[ 0x99BD, KeycardDoors.Left, 0x660E, KeycardEvents.CrateriaBoss, KeycardPlaque.Boss, 0x640F, 0x8470 ], #// Crateria - Before G4 - Door to G4
|
||||||
|
[ 0x9879, KeycardDoors.Left, 0x062E, KeycardEvents.CrateriaBoss, KeycardPlaque.Boss, 0x042F, 0x8420 ], #// Crateria - Before BT - Door to Bomb Torizo
|
||||||
|
|
||||||
|
#// Brinstar
|
||||||
|
[ 0x9F11, KeycardDoors.Left, 0x060E, KeycardEvents.BrinstarLevel1, KeycardPlaque.Level1, 0x040F, 0x8784 ], #// Brinstar - Blue Brinstar - Door to ceiling e-tank room
|
||||||
|
|
||||||
|
[ 0x9AD9, KeycardDoors.Right, 0xA601, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0xA400, 0x0000 ], #// Brinstar - Green Brinstar - Door to etecoon area
|
||||||
|
[ 0x9D9C, KeycardDoors.Down, 0x0336, KeycardEvents.BrinstarBoss, KeycardPlaque.Boss, 0x0234, 0x863A ], #// Brinstar - Pink Brinstar - Door to spore spawn
|
||||||
|
[ 0xA130, KeycardDoors.Left, 0x161E, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0x141F, 0x881C ], #// Brinstar - Pink Brinstar - Door to wave gate e-tank
|
||||||
|
[ 0xA0A4, KeycardDoors.Left, 0x062E, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0x042F, 0x0000 ], #// Brinstar - Pink Brinstar - Door to spore spawn super
|
||||||
|
|
||||||
|
[ 0xA56B, KeycardDoors.Left, 0x161E, KeycardEvents.BrinstarBoss, KeycardPlaque.Boss, 0x141F, 0x8A1A ], #// Brinstar - Before Kraid - Door to Kraid
|
||||||
|
|
||||||
|
#// Upper Norfair
|
||||||
|
[ 0xA7DE, KeycardDoors.Right, 0x3601, KeycardEvents.NorfairLevel1, KeycardPlaque.Level1, 0x3400, 0x8B00 ], #// Norfair - Business Centre - Door towards Ice
|
||||||
|
[ 0xA923, KeycardDoors.Right, 0x0601, KeycardEvents.NorfairLevel1, KeycardPlaque.Level1, 0x0400, 0x0000 ], #// Norfair - Pre-Crocomire - Door towards Ice
|
||||||
|
|
||||||
|
[ 0xA788, KeycardDoors.Left, 0x162E, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x142F, 0x8AEA ], #// Norfair - Lava Missile Room - Door towards Bubble Mountain
|
||||||
|
[ 0xAF72, KeycardDoors.Left, 0x061E, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x041F, 0x0000 ], #// Norfair - After frog speedway - Door to Bubble Mountain
|
||||||
|
[ 0xAEDF, KeycardDoors.Down, 0x0206, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x0204, 0x0000 ], #// Norfair - Below bubble mountain - Door to Bubble Mountain
|
||||||
|
[ 0xAD5E, KeycardDoors.Right, 0x0601, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x0400, 0x0000 ], #// Norfair - LN Escape - Door to Bubble Mountain
|
||||||
|
|
||||||
|
[ 0xA923, KeycardDoors.Up, 0x2DC6, KeycardEvents.NorfairBoss, KeycardPlaque.Boss, 0x2EC4, 0x8B96 ], #// Norfair - Pre-Crocomire - Door to Crocomire
|
||||||
|
|
||||||
|
#// Lower Norfair
|
||||||
|
[ 0xB4AD, KeycardDoors.Left, 0x160E, KeycardEvents.LowerNorfairLevel1, KeycardPlaque.Level1, 0x140F, 0x0000 ], #// Lower Norfair - WRITG - Door to Amphitheatre
|
||||||
|
[ 0xAD5E, KeycardDoors.Left, 0x065E, KeycardEvents.LowerNorfairLevel1, KeycardPlaque.Level1, 0x045F, 0x0000 ], #// Lower Norfair - Exit - Door to "Reverse LN Entry"
|
||||||
|
[ 0xB37A, KeycardDoors.Right, 0x0601, KeycardEvents.LowerNorfairBoss, KeycardPlaque.Boss, 0x0400, 0x8EA6 ], #// Lower Norfair - Pre-Ridley - Door to Ridley
|
||||||
|
|
||||||
|
#// Maridia
|
||||||
|
[ 0xD0B9, KeycardDoors.Left, 0x065E, KeycardEvents.MaridiaLevel1, KeycardPlaque.Level1, 0x045F, 0x0000 ], #// Maridia - Mt. Everest - Door to Pink Maridia
|
||||||
|
[ 0xD5A7, KeycardDoors.Right, 0x1601, KeycardEvents.MaridiaLevel1, KeycardPlaque.Level1, 0x1400, 0x0000 ], #// Maridia - Aqueduct - Door towards Beach
|
||||||
|
|
||||||
|
[ 0xD617, KeycardDoors.Left, 0x063E, KeycardEvents.MaridiaLevel2, KeycardPlaque.Level2, 0x043F, 0x0000 ], #// Maridia - Pre-Botwoon - Door to Botwoon
|
||||||
|
[ 0xD913, KeycardDoors.Right, 0x2601, KeycardEvents.MaridiaLevel2, KeycardPlaque.Level2, 0x2400, 0x0000 ], #// Maridia - Pre-Colloseum - Door to post-botwoon
|
||||||
|
|
||||||
|
[ 0xD78F, KeycardDoors.Right, 0x2601, KeycardEvents.MaridiaBoss, KeycardPlaque.Boss, 0x2400, 0xC73B ], #// Maridia - Precious Room - Door to Draygon
|
||||||
|
|
||||||
|
[ 0xDA2B, KeycardDoors.BossLeft, 0x164E, 0x00f0, KeycardPlaque.Null, 0x144F, 0x0000 ], #// Maridia - Change Cac Alley Door to Boss Door (prevents key breaking)
|
||||||
|
|
||||||
|
#// Wrecked Ship
|
||||||
|
[ 0x93FE, KeycardDoors.Left, 0x167E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x147F, 0x0000 ], #// Wrecked Ship - Outside Wrecked Ship West - Door to Reserve Tank Check
|
||||||
|
[ 0x968F, KeycardDoors.Left, 0x060E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x040F, 0x0000 ], #// Wrecked Ship - Outside Wrecked Ship West - Door to Bowling Alley
|
||||||
|
[ 0xCE40, KeycardDoors.Left, 0x060E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x040F, 0x0000 ], #// Wrecked Ship - Gravity Suit - Door to Bowling Alley
|
||||||
|
|
||||||
|
[ 0xCC6F, KeycardDoors.Left, 0x064E, KeycardEvents.WreckedShipBoss, KeycardPlaque.Boss, 0x044F, 0xC29D ], #// Wrecked Ship - Pre-Phantoon - Door to Phantoon
|
||||||
|
]
|
||||||
|
|
||||||
|
doorId = 0x0000
|
||||||
|
for door in doorList:
|
||||||
|
#/* When "Fast Ganon" is set, don't place the G4 Boss key door to enable faster games */
|
||||||
|
if (door[0] == 0x99BD and self.myWorld.Config.Goal == Goal.FastGanonDefeatMotherBrain):
|
||||||
|
continue
|
||||||
|
doorArgs = doorId | door[3] if door[4] != KeycardPlaque.Null else door[3]
|
||||||
|
if (door[6] == 0):
|
||||||
|
#// Write dynamic door
|
||||||
|
doorData = []
|
||||||
|
for x in door[0:3]:
|
||||||
|
doorData += getWordArray(x)
|
||||||
|
doorData += getWordArray(doorArgs)
|
||||||
|
self.patches.append((Snes(0x8f0000 + plmTablePos), doorData))
|
||||||
|
plmTablePos += 0x08
|
||||||
|
else:
|
||||||
|
#// Overwrite existing door
|
||||||
|
doorData = []
|
||||||
|
for x in door[1:3]:
|
||||||
|
doorData += getWordArray(x)
|
||||||
|
doorData += getWordArray(doorArgs)
|
||||||
|
self.patches.append((Snes(0x8f0000 + door[6]), doorData))
|
||||||
|
if((door[3] == KeycardEvents.BrinstarBoss and door[0] != 0x9D9C) or door[3] == KeycardEvents.LowerNorfairBoss or door[3] == KeycardEvents.MaridiaBoss or door[3] == KeycardEvents.WreckedShipBoss):
|
||||||
|
#// Overwrite the extra parts of the Gadora with a PLM that just deletes itself
|
||||||
|
self.patches.append((Snes(0x8f0000 + door[6] + 0x06), [ 0x2F, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x2F, 0xB6, 0x00, 0x00, 0x00, 0x00 ]))
|
||||||
|
|
||||||
|
#// Plaque data
|
||||||
|
if (door[4] != KeycardPlaque.Null):
|
||||||
|
plaqueData = getWordArray(door[0]) + getWordArray(plaquePlm) + getWordArray(door[5]) + getWordArray(door[4])
|
||||||
|
self.patches.append((Snes(0x8f0000 + plmTablePos), plaqueData))
|
||||||
|
plmTablePos += 0x08
|
||||||
|
doorId += 1
|
||||||
|
|
||||||
|
#/* Write plaque showing SM bosses that needs to be killed */
|
||||||
|
if (self.myWorld.Config.OpenTourian != OpenTourian.FourBosses):
|
||||||
|
plaqueData = getWordArray(0xA5ED) + getWordArray(plaquePlm) + getWordArray(0x044F) + getWordArray(KeycardPlaque.Zero + self.myWorld.TourianBossTokens)
|
||||||
|
self.patches.append((Snes(0x8f0000 + plmTablePos), plaqueData))
|
||||||
|
plmTablePos += 0x08
|
||||||
|
|
||||||
self.patches.append((Snes(0x8f0000 + plmTablePos), [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]))
|
self.patches.append((Snes(0x8f0000 + plmTablePos), [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]))
|
||||||
|
|
||||||
|
@ -745,20 +733,32 @@ class Patch:
|
||||||
(Snes(0xDD313), [ 0x00, 0x00, 0xE4, 0xFF, 0x08, 0x0E ]),
|
(Snes(0xDD313), [ 0x00, 0x00, 0xE4, 0xFF, 0x08, 0x0E ]),
|
||||||
]
|
]
|
||||||
|
|
||||||
def WriteGanonInvicible(self, invincible: GanonInvincible):
|
def WritePreOpenPyramid(self, goal: Goal):
|
||||||
|
if (goal == Goal.FastGanonDefeatMotherBrain):
|
||||||
|
self.patches.append((Snes(0x30808B), [0x01]))
|
||||||
|
|
||||||
|
def WriteGanonInvicible(self, goal: Goal):
|
||||||
#/* Defaults to $00 (never) at [asm]/z3/randomizer/tables.asm */
|
#/* Defaults to $00 (never) at [asm]/z3/randomizer/tables.asm */
|
||||||
invincibleMap = {
|
valueMap = {
|
||||||
GanonInvincible.Never : 0x00,
|
Goal.DefeatBoth : 0x03,
|
||||||
GanonInvincible.Always : 0x01,
|
Goal.FastGanonDefeatMotherBrain : 0x04,
|
||||||
GanonInvincible.BeforeAllDungeons : 0x02,
|
Goal.AllDungeonsDefeatMotherBrain : 0x02
|
||||||
GanonInvincible.BeforeCrystals : 0x03,
|
}
|
||||||
}
|
value = valueMap.get(goal, None)
|
||||||
value = invincibleMap.get(invincible, None)
|
|
||||||
if (value is None):
|
if (value is None):
|
||||||
raise exception(f"Unknown Ganon invincible value {invincible}")
|
raise exception(f"Unknown Ganon invincible value {goal}")
|
||||||
else:
|
else:
|
||||||
self.patches.append((Snes(0x30803E), [value]))
|
self.patches.append((Snes(0x30803E), [value]))
|
||||||
|
|
||||||
|
def WriteBossesNeeded(self, tourianBossTokens):
|
||||||
|
self.patches.append((Snes(0xF47200), getWordArray(tourianBossTokens)))
|
||||||
|
|
||||||
|
def WriteCrystalsNeeded(self, towerCrystals, ganonCrystals):
|
||||||
|
self.patches.append((Snes(0x30805E), [towerCrystals]))
|
||||||
|
self.patches.append((Snes(0x30805F), [ganonCrystals]))
|
||||||
|
|
||||||
|
self.stringTable.SetTowerRequirementText(f"You need {towerCrystals} crystals to enter Ganon's Tower.")
|
||||||
|
self.stringTable.SetGanonRequirementText(f"You need {ganonCrystals} crystals to defeat Ganon.")
|
||||||
|
|
||||||
def WriteRngBlock(self):
|
def WriteRngBlock(self):
|
||||||
#/* Repoint RNG Block */
|
#/* Repoint RNG Block */
|
||||||
|
|
|
@ -5,12 +5,19 @@ from worlds.smz3.TotalSMZ3.Item import Item, ItemType
|
||||||
|
|
||||||
class RewardType(Enum):
|
class RewardType(Enum):
|
||||||
Null = 0
|
Null = 0
|
||||||
Agahnim = 1
|
Agahnim = 1 << 0
|
||||||
PendantGreen = 2
|
PendantGreen = 1 << 1
|
||||||
PendantNonGreen = 3
|
PendantNonGreen = 1 << 2
|
||||||
CrystalBlue = 4
|
CrystalBlue = 1 << 3
|
||||||
CrystalRed = 5
|
CrystalRed = 1 << 4
|
||||||
GoldenFourBoss = 6
|
BossTokenKraid = 1 << 5
|
||||||
|
BossTokenPhantoon = 1 << 6
|
||||||
|
BossTokenDraygon = 1 << 7
|
||||||
|
BossTokenRidley = 1 << 8
|
||||||
|
|
||||||
|
AnyPendant = PendantGreen | PendantNonGreen
|
||||||
|
AnyCrystal = CrystalBlue | CrystalRed
|
||||||
|
AnyBossToken = BossTokenKraid | BossTokenPhantoon | BossTokenDraygon | BossTokenRidley
|
||||||
|
|
||||||
class IReward:
|
class IReward:
|
||||||
Reward: RewardType
|
Reward: RewardType
|
||||||
|
@ -18,7 +25,7 @@ class IReward:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class IMedallionAccess:
|
class IMedallionAccess:
|
||||||
Medallion: object
|
Medallion = None
|
||||||
|
|
||||||
class Region:
|
class Region:
|
||||||
import worlds.smz3.TotalSMZ3.Location as Location
|
import worlds.smz3.TotalSMZ3.Location as Location
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Kraid(SMRegion, IReward):
|
||||||
Name = "Brinstar Kraid"
|
Name = "Brinstar Kraid"
|
||||||
Area = "Brinstar"
|
Area = "Brinstar"
|
||||||
|
|
||||||
Reward = RewardType.GoldenFourBoss
|
Reward = RewardType.Null
|
||||||
|
|
||||||
def __init__(self, world, config: Config):
|
def __init__(self, world, config: Config):
|
||||||
super().__init__(world, config)
|
super().__init__(world, config)
|
||||||
|
|
|
@ -40,5 +40,5 @@ class Pink(SMRegion):
|
||||||
else:
|
else:
|
||||||
return items.CanOpenRedDoors() and (items.CanDestroyBombWalls() or items.SpeedBooster) or \
|
return items.CanOpenRedDoors() and (items.CanDestroyBombWalls() or items.SpeedBooster) or \
|
||||||
items.CanUsePowerBombs() or \
|
items.CanUsePowerBombs() or \
|
||||||
items.CanAccessNorfairUpperPortal() and items.Morph and (items.CanOpenRedDoors() or items.Wave) and \
|
items.CanAccessNorfairUpperPortal() and items.Morph and (items.Missile or items.Super or items.Wave ) and \
|
||||||
(items.Ice or items.HiJump or items.CanSpringBallJump() or items.CanFly())
|
(items.Ice or items.HiJump or items.CanSpringBallJump() or items.CanFly())
|
||||||
|
|
|
@ -17,9 +17,9 @@ class East(SMRegion):
|
||||||
self.world.CanEnter("Wrecked Ship", items)) if self.Logic == SMLogic.Normal else \
|
self.world.CanEnter("Wrecked Ship", items)) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: items.Morph),
|
lambda items: items.Morph),
|
||||||
Location(self, 2, 0x8F81EE, LocationType.Hidden, "Missile (outside Wrecked Ship top)",
|
Location(self, 2, 0x8F81EE, LocationType.Hidden, "Missile (outside Wrecked Ship top)",
|
||||||
lambda items: self.world.CanEnter("Wrecked Ship", items) and (not self.Config.Keysanity or items.CardWreckedShipBoss) and items.CanPassBombPassages()),
|
lambda items: self.world.CanEnter("Wrecked Ship", items) and items.CardWreckedShipBoss and items.CanPassBombPassages()),
|
||||||
Location(self, 3, 0x8F81F4, LocationType.Visible, "Missile (outside Wrecked Ship middle)",
|
Location(self, 3, 0x8F81F4, LocationType.Visible, "Missile (outside Wrecked Ship middle)",
|
||||||
lambda items: self.world.CanEnter("Wrecked Ship", items) and (not self.Config.Keysanity or items.CardWreckedShipBoss) and items.CanPassBombPassages()),
|
lambda items: self.world.CanEnter("Wrecked Ship", items) and items.CardWreckedShipBoss and items.CanPassBombPassages()),
|
||||||
Location(self, 4, 0x8F8248, LocationType.Visible, "Missile (Crateria moat)",
|
Location(self, 4, 0x8F8248, LocationType.Visible, "Missile (Crateria moat)",
|
||||||
lambda items: True)
|
lambda items: True)
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,20 +9,17 @@ class Inner(SMRegion, IReward):
|
||||||
|
|
||||||
def __init__(self, world, config: Config):
|
def __init__(self, world, config: Config):
|
||||||
super().__init__(world, config)
|
super().__init__(world, config)
|
||||||
self.Reward = RewardType.GoldenFourBoss
|
self.Reward = RewardType.Null
|
||||||
self.Locations = [
|
self.Locations = [
|
||||||
Location(self, 140, 0x8FC4AF, LocationType.Visible, "Super Missile (yellow Maridia)",
|
Location(self, 140, 0x8FC4AF, LocationType.Visible, "Super Missile (yellow Maridia)",
|
||||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and self.CanReachAqueduct(items) and
|
||||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and
|
(items.Gravity or items.Ice or items.HiJump and items.SpringBall)),
|
||||||
(items.Gravity or items.Ice or items.HiJump and items.CanSpringBallJump())),
|
|
||||||
Location(self, 141, 0x8FC4B5, LocationType.Visible, "Missile (yellow Maridia super missile)",
|
Location(self, 141, 0x8FC4B5, LocationType.Visible, "Missile (yellow Maridia super missile)",
|
||||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
|
||||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and
|
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and
|
||||||
(items.Gravity or items.Ice or items.HiJump and items.CanSpringBallJump())),
|
(items.Gravity or items.Ice or items.HiJump and items.SpringBall)),
|
||||||
Location(self, 142, 0x8FC533, LocationType.Visible, "Missile (yellow Maridia false wall)",
|
Location(self, 142, 0x8FC533, LocationType.Visible, "Missile (yellow Maridia false wall)",
|
||||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
|
||||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and
|
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and
|
||||||
(items.Gravity or items.Ice or items.HiJump and items.CanSpringBallJump())),
|
(items.Gravity or items.Ice or items.HiJump and items.SpringBall)),
|
||||||
Location(self, 143, 0x8FC559, LocationType.Chozo, "Plasma Beam",
|
Location(self, 143, 0x8FC559, LocationType.Chozo, "Plasma Beam",
|
||||||
lambda items: self.CanDefeatDraygon(items) and (items.ScrewAttack or items.Plasma) and (items.HiJump or items.CanFly()) if self.Logic == SMLogic.Normal else \
|
lambda items: self.CanDefeatDraygon(items) and (items.ScrewAttack or items.Plasma) and (items.HiJump or items.CanFly()) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: self.CanDefeatDraygon(items) and
|
lambda items: self.CanDefeatDraygon(items) and
|
||||||
|
@ -31,17 +28,17 @@ class Inner(SMRegion, IReward):
|
||||||
Location(self, 144, 0x8FC5DD, LocationType.Visible, "Missile (left Maridia sand pit room)",
|
Location(self, 144, 0x8FC5DD, LocationType.Visible, "Missile (left Maridia sand pit room)",
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Super and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
lambda items: self.CanReachAqueduct(items) and items.Super and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Super and
|
lambda items: self.CanReachAqueduct(items) and items.Super and
|
||||||
(items.HiJump and (items.SpaceJump or items.CanSpringBallJump()) or items.Gravity)),
|
(items.Gravity or items.HiJump and (items.SpaceJump or items.CanSpringBallJump()))),
|
||||||
Location(self, 145, 0x8FC5E3, LocationType.Chozo, "Reserve Tank, Maridia",
|
Location(self, 145, 0x8FC5E3, LocationType.Chozo, "Reserve Tank, Maridia",
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Super and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
lambda items: self.CanReachAqueduct(items) and items.Super and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Super and
|
lambda items: self.CanReachAqueduct(items) and items.Super and
|
||||||
(items.HiJump and (items.SpaceJump or items.CanSpringBallJump()) or items.Gravity)),
|
(items.Gravity or items.HiJump and (items.SpaceJump or items.CanSpringBallJump()))),
|
||||||
Location(self, 146, 0x8FC5EB, LocationType.Visible, "Missile (right Maridia sand pit room)",
|
Location(self, 146, 0x8FC5EB, LocationType.Visible, "Missile (right Maridia sand pit room)",
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Super) if self.Logic == SMLogic.Normal else \
|
lambda items: self.CanReachAqueduct(items) and items.Super) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Super and (items.HiJump or items.Gravity),
|
lambda items: self.CanReachAqueduct(items) and items.Super and (items.HiJump or items.Gravity),
|
||||||
Location(self, 147, 0x8FC5F1, LocationType.Visible, "Power Bomb (right Maridia sand pit room)",
|
Location(self, 147, 0x8FC5F1, LocationType.Visible, "Power Bomb (right Maridia sand pit room)",
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Super) if self.Logic == SMLogic.Normal else \
|
lambda items: self.CanReachAqueduct(items) and items.Super) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Super and (items.HiJump and items.CanSpringBallJump() or items.Gravity),
|
lambda items: self.CanReachAqueduct(items) and items.Super and (items.Gravity or items.HiJump and items.CanSpringBallJump()),
|
||||||
Location(self, 148, 0x8FC603, LocationType.Visible, "Missile (pink Maridia)",
|
Location(self, 148, 0x8FC603, LocationType.Visible, "Missile (pink Maridia)",
|
||||||
lambda items: self.CanReachAqueduct(items) and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
lambda items: self.CanReachAqueduct(items) and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: self.CanReachAqueduct(items) and items.Gravity),
|
lambda items: self.CanReachAqueduct(items) and items.Gravity),
|
||||||
|
|
|
@ -9,7 +9,7 @@ class East(SMRegion, IReward):
|
||||||
|
|
||||||
def __init__(self, world, config: Config):
|
def __init__(self, world, config: Config):
|
||||||
super().__init__(world, config)
|
super().__init__(world, config)
|
||||||
self.Reward = RewardType.GoldenFourBoss
|
self.Reward = RewardType.Null
|
||||||
self.Locations = [
|
self.Locations = [
|
||||||
Location(self, 74, 0x8F8FCA, LocationType.Visible, "Missile (lower Norfair above fire flea room)",
|
Location(self, 74, 0x8F8FCA, LocationType.Visible, "Missile (lower Norfair above fire flea room)",
|
||||||
lambda items: self.CanExit(items)),
|
lambda items: self.CanExit(items)),
|
||||||
|
@ -30,11 +30,11 @@ class East(SMRegion, IReward):
|
||||||
def CanExit(self, items:Progression):
|
def CanExit(self, items:Progression):
|
||||||
if self.Logic == SMLogic.Normal:
|
if self.Logic == SMLogic.Normal:
|
||||||
# /*Bubble Mountain*/
|
# /*Bubble Mountain*/
|
||||||
return items.CardNorfairL2 or (
|
return items.Morph and (items.CardNorfairL2 or (
|
||||||
# /* Volcano Room and Blue Gate */
|
# /* Volcano Room and Blue Gate */
|
||||||
items.Gravity) and items.Wave and (
|
items.Gravity) and items.Wave and (
|
||||||
# /*Spikey Acid Snakes and Croc Escape*/
|
# /*Spikey Acid Snakes and Croc Escape*/
|
||||||
items.Grapple or items.SpaceJump)
|
items.Grapple or items.SpaceJump))
|
||||||
else:
|
else:
|
||||||
# /*Vanilla LN Escape*/
|
# /*Vanilla LN Escape*/
|
||||||
return (items.Morph and (items.CardNorfairL2 or # /*Bubble Mountain*/
|
return (items.Morph and (items.CardNorfairL2 or # /*Bubble Mountain*/
|
||||||
|
|
|
@ -17,13 +17,13 @@ class West(SMRegion):
|
||||||
items.CanAccessNorfairLowerPortal() and (items.CanFly() or items.CanSpringBallJump() or items.SpeedBooster) and items.Super)),
|
items.CanAccessNorfairLowerPortal() and (items.CanFly() or items.CanSpringBallJump() or items.SpeedBooster) and items.Super)),
|
||||||
Location(self, 71, 0x8F8E74, LocationType.Hidden, "Super Missile (Gold Torizo)",
|
Location(self, 71, 0x8F8E74, LocationType.Hidden, "Super Missile (Gold Torizo)",
|
||||||
lambda items: items.CanDestroyBombWalls() and (items.Super or items.Charge) and
|
lambda items: items.CanDestroyBombWalls() and (items.Super or items.Charge) and
|
||||||
(items.CanAccessNorfairLowerPortal() or items.SpaceJump and items.CanUsePowerBombs()) if self.Logic == SMLogic.Normal else \
|
(items.CanAccessNorfairLowerPortal() or items.CanUsePowerBombs() and items.SpaceJump) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: items.CanDestroyBombWalls() and items.Varia and (items.Super or items.Charge)),
|
lambda items: items.CanDestroyBombWalls() and items.Varia and (items.Super or items.Charge)),
|
||||||
Location(self, 79, 0x8F9110, LocationType.Chozo, "Screw Attack",
|
Location(self, 79, 0x8F9110, LocationType.Chozo, "Screw Attack",
|
||||||
lambda items: items.CanDestroyBombWalls() and (items.SpaceJump and items.CanUsePowerBombs() or items.CanAccessNorfairLowerPortal()) if self.Logic == SMLogic.Normal else \
|
lambda items: items.CanDestroyBombWalls() and (items.CanAccessNorfairLowerPortal() or items.CanUsePowerBombs() and items.SpaceJump) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: items.CanDestroyBombWalls() and (items.Varia or items.CanAccessNorfairLowerPortal())),
|
lambda items: items.CanDestroyBombWalls() and (items.CanAccessNorfairLowerPortal() or items.Varia)),
|
||||||
Location(self, 73, 0x8F8F30, LocationType.Visible, "Missile (Mickey Mouse room)",
|
Location(self, 73, 0x8F8F30, LocationType.Visible, "Missile (Mickey Mouse room)",
|
||||||
lambda items: items.CanFly() and items.Morph and items.Super and (
|
lambda items: items.Morph and items.Super and items.CanFly() and items.CanUsePowerBombs() and (
|
||||||
# /*Exit to Upper Norfair*/
|
# /*Exit to Upper Norfair*/
|
||||||
(items.CardLowerNorfairL1 or
|
(items.CardLowerNorfairL1 or
|
||||||
# /*Vanilla or Reverse Lava Dive*/
|
# /*Vanilla or Reverse Lava Dive*/
|
||||||
|
@ -33,17 +33,20 @@ class West(SMRegion):
|
||||||
# /* Volcano Room and Blue Gate */
|
# /* Volcano Room and Blue Gate */
|
||||||
items.Gravity and items.Wave and
|
items.Gravity and items.Wave and
|
||||||
# /*Spikey Acid Snakes and Croc Escape*/
|
# /*Spikey Acid Snakes and Croc Escape*/
|
||||||
(items.Grapple or items.SpaceJump) or
|
(items.Grapple or items.SpaceJump) or
|
||||||
# /*Exit via GT fight and Portal*/
|
# /*Exit via GT fight and Portal*/
|
||||||
(items.CanUsePowerBombs() and items.SpaceJump and (items.Super or items.Charge))) if self.Logic == SMLogic.Normal else \
|
items.CanUsePowerBombs() and items.SpaceJump and (items.Super or items.Charge)) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items:
|
lambda items:
|
||||||
items.Morph and items.Varia and items.Super and ((items.CanFly() or items.CanSpringBallJump() or
|
items.Varia and items.Morph and items.Super and
|
||||||
items.SpeedBooster and (items.HiJump and items.CanUsePowerBombs() or items.Charge and items.Ice)) and
|
#/* Climb worst room (from LN East CanEnter) */
|
||||||
# /*Exit to Upper Norfair*/
|
(items.CanFly() or items.HiJump or items.CanSpringBallJump() or items.Ice and items.Charge) and
|
||||||
(items.CardNorfairL2 or (items.SpeedBooster or items.CanFly() or items.Grapple or items.HiJump and
|
(items.CanPassBombPassages() or items.ScrewAttack and items.SpaceJump) and (
|
||||||
(items.CanSpringBallJump() or items.Ice))) or
|
#/* Exit to Upper Norfair */
|
||||||
# /*Return to Portal*/
|
items.CardNorfairL2 or items.SpeedBooster or items.CanFly() or items.Grapple or
|
||||||
items.CanUsePowerBombs()))
|
items.HiJump and (items.CanSpringBallJump() or items.Ice) or
|
||||||
|
#/* Portal (with GGG) */
|
||||||
|
items.CanUsePowerBombs()
|
||||||
|
))
|
||||||
]
|
]
|
||||||
|
|
||||||
# // Todo: account for Croc Speedway once Norfair Upper East also do so, otherwise it would be inconsistent to do so here
|
# // Todo: account for Croc Speedway once Norfair Upper East also do so, otherwise it would be inconsistent to do so here
|
||||||
|
|
|
@ -45,11 +45,10 @@ class Crocomire(SMRegion):
|
||||||
# /* Cathedral -> through the floor or Vulcano */
|
# /* Cathedral -> through the floor or Vulcano */
|
||||||
items.CanOpenRedDoors() and (items.CardNorfairL2 if self.Config.Keysanity else items.Super) and
|
items.CanOpenRedDoors() and (items.CardNorfairL2 if self.Config.Keysanity else items.Super) and
|
||||||
(items.CanFly() or items.HiJump or items.SpeedBooster) and
|
(items.CanFly() or items.HiJump or items.SpeedBooster) and
|
||||||
(items.CanPassBombPassages() or items.Gravity and items.Morph) and items.Wave
|
(items.CanPassBombPassages() or items.Gravity and items.Morph) and items.Wave) or (
|
||||||
or
|
|
||||||
# /* Reverse Lava Dive */
|
# /* Reverse Lava Dive */
|
||||||
items.CanAccessNorfairLowerPortal() and items.ScrewAttack and items.SpaceJump and items.Super and
|
items.Varia) and items.CanAccessNorfairLowerPortal() and items.ScrewAttack and items.SpaceJump and items.Super and (
|
||||||
items.Gravity and items.Wave and (items.CardNorfairL2 or items.Morph))
|
items.Gravity) and items.Wave and (items.CardNorfairL2 or items.Morph)
|
||||||
else:
|
else:
|
||||||
return ((items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or items.CanAccessNorfairUpperPortal()) and (
|
return ((items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or items.CanAccessNorfairUpperPortal()) and (
|
||||||
# /* Ice Beam -> Croc Speedway */
|
# /* Ice Beam -> Croc Speedway */
|
||||||
|
@ -65,5 +64,5 @@ class Crocomire(SMRegion):
|
||||||
(items.Missile or items.Super or items.Wave) # /* Blue Gate */
|
(items.Missile or items.Super or items.Wave) # /* Blue Gate */
|
||||||
) or (
|
) or (
|
||||||
# /* Reverse Lava Dive */
|
# /* Reverse Lava Dive */
|
||||||
items.CanAccessNorfairLowerPortal()) and items.ScrewAttack and items.SpaceJump and items.Varia and items.Super and (
|
items.Varia and items.CanAccessNorfairLowerPortal()) and items.ScrewAttack and items.SpaceJump and items.Super and (
|
||||||
items.HasEnergyReserves(2)) and (items.CardNorfairL2 or items.Morph)
|
items.HasEnergyReserves(2)) and (items.CardNorfairL2 or items.Morph)
|
||||||
|
|
|
@ -9,12 +9,13 @@ class WreckedShip(SMRegion, IReward):
|
||||||
|
|
||||||
def __init__(self, world, config: Config):
|
def __init__(self, world, config: Config):
|
||||||
super().__init__(world, config)
|
super().__init__(world, config)
|
||||||
self.Reward = RewardType.GoldenFourBoss
|
self.Weight = 4
|
||||||
|
self.Reward = RewardType.Null
|
||||||
self.Locations = [
|
self.Locations = [
|
||||||
Location(self, 128, 0x8FC265, LocationType.Visible, "Missile (Wrecked Ship middle)",
|
Location(self, 128, 0x8FC265, LocationType.Visible, "Missile (Wrecked Ship middle)",
|
||||||
lambda items: items.CanPassBombPassages()),
|
lambda items: items.CanPassBombPassages()),
|
||||||
Location(self, 129, 0x8FC2E9, LocationType.Chozo, "Reserve Tank, Wrecked Ship",
|
Location(self, 129, 0x8FC2E9, LocationType.Chozo, "Reserve Tank, Wrecked Ship",
|
||||||
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and items.SpeedBooster and items.CanUsePowerBombs() and
|
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and items.CanUsePowerBombs() and items.SpeedBooster and
|
||||||
(items.Grapple or items.SpaceJump or items.Varia and items.HasEnergyReserves(2) or items.HasEnergyReserves(3)) if self.Logic == SMLogic.Normal else \
|
(items.Grapple or items.SpaceJump or items.Varia and items.HasEnergyReserves(2) or items.HasEnergyReserves(3)) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and items.CanUsePowerBombs() and items.SpeedBooster and
|
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and items.CanUsePowerBombs() and items.SpeedBooster and
|
||||||
(items.Varia or items.HasEnergyReserves(2))),
|
(items.Varia or items.HasEnergyReserves(2))),
|
||||||
|
@ -27,7 +28,7 @@ class WreckedShip(SMRegion, IReward):
|
||||||
Location(self, 132, 0x8FC337, LocationType.Visible, "Energy Tank, Wrecked Ship",
|
Location(self, 132, 0x8FC337, LocationType.Visible, "Energy Tank, Wrecked Ship",
|
||||||
lambda items: self.CanUnlockShip(items) and
|
lambda items: self.CanUnlockShip(items) and
|
||||||
(items.HiJump or items.SpaceJump or items.SpeedBooster or items.Gravity) if self.Logic == SMLogic.Normal else \
|
(items.HiJump or items.SpaceJump or items.SpeedBooster or items.Gravity) if self.Logic == SMLogic.Normal else \
|
||||||
lambda items: self.CanUnlockShip(items) and (items.Bombs or items.PowerBomb or items.CanSpringBallJump() or
|
lambda items: self.CanUnlockShip(items) and (items.Morph and (items.Bombs or items.PowerBomb) or items.CanSpringBallJump() or
|
||||||
items.HiJump or items.SpaceJump or items.SpeedBooster or items.Gravity)),
|
items.HiJump or items.SpaceJump or items.SpeedBooster or items.Gravity)),
|
||||||
Location(self, 133, 0x8FC357, LocationType.Visible, "Super Missile (Wrecked Ship left)",
|
Location(self, 133, 0x8FC357, LocationType.Visible, "Super Missile (Wrecked Ship left)",
|
||||||
lambda items: self.CanUnlockShip(items)),
|
lambda items: self.CanUnlockShip(items)),
|
||||||
|
|
|
@ -14,15 +14,15 @@ class NorthEast(Z3Region):
|
||||||
lambda items: items.MoonPearl and items.CanLiftLight()),
|
lambda items: items.MoonPearl and items.CanLiftLight()),
|
||||||
Location(self, 256+79, 0x308147, LocationType.Regular, "Pyramid"),
|
Location(self, 256+79, 0x308147, LocationType.Regular, "Pyramid"),
|
||||||
Location(self, 256+80, 0x1E980, LocationType.Regular, "Pyramid Fairy - Left",
|
Location(self, 256+80, 0x1E980, LocationType.Regular, "Pyramid Fairy - Left",
|
||||||
lambda items: self.world.CanAquireAll(items, RewardType.CrystalRed) and items.MoonPearl and self.world.CanEnter("Dark World South", items) and
|
lambda items: self.world.CanAcquireAll(items, RewardType.CrystalRed) and items.MoonPearl and self.world.CanEnter("Dark World South", items) and
|
||||||
(items.Hammer or items.Mirror and self.world.CanAquire(items, RewardType.Agahnim))),
|
(items.Hammer or items.Mirror and self.world.CanAcquire(items, RewardType.Agahnim))),
|
||||||
Location(self, 256+81, 0x1E983, LocationType.Regular, "Pyramid Fairy - Right",
|
Location(self, 256+81, 0x1E983, LocationType.Regular, "Pyramid Fairy - Right",
|
||||||
lambda items: self.world.CanAquireAll(items, RewardType.CrystalRed) and items.MoonPearl and self.world.CanEnter("Dark World South", items) and
|
lambda items: self.world.CanAcquireAll(items, RewardType.CrystalRed) and items.MoonPearl and self.world.CanEnter("Dark World South", items) and
|
||||||
(items.Hammer or items.Mirror and self.world.CanAquire(items, RewardType.Agahnim)))
|
(items.Hammer or items.Mirror and self.world.CanAcquire(items, RewardType.Agahnim)))
|
||||||
]
|
]
|
||||||
|
|
||||||
def CanEnter(self, items: Progression):
|
def CanEnter(self, items: Progression):
|
||||||
return self.world.CanAquire(items, RewardType.Agahnim) or items.MoonPearl and (
|
return self.world.CanAcquire(items, RewardType.Agahnim) or items.MoonPearl and (
|
||||||
items.Hammer and items.CanLiftLight() or
|
items.Hammer and items.CanLiftLight() or
|
||||||
items.CanLiftHeavy() and items.Flippers or
|
items.CanLiftHeavy() and items.Flippers or
|
||||||
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers)
|
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers)
|
||||||
|
|
|
@ -25,7 +25,7 @@ class NorthWest(Z3Region):
|
||||||
|
|
||||||
def CanEnter(self, items: Progression):
|
def CanEnter(self, items: Progression):
|
||||||
return items.MoonPearl and ((
|
return items.MoonPearl and ((
|
||||||
self.world.CanAquire(items, RewardType.Agahnim) or
|
self.world.CanAcquire(items, RewardType.Agahnim) or
|
||||||
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers
|
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers
|
||||||
) and items.Hookshot and (items.Flippers or items.CanLiftLight() or items.Hammer) or
|
) and items.Hookshot and (items.Flippers or items.CanLiftLight() or items.Hammer) or
|
||||||
items.Hammer and items.CanLiftLight() or
|
items.Hammer and items.CanLiftLight() or
|
||||||
|
|
|
@ -21,7 +21,7 @@ class South(Z3Region):
|
||||||
|
|
||||||
def CanEnter(self, items: Progression):
|
def CanEnter(self, items: Progression):
|
||||||
return items.MoonPearl and ((
|
return items.MoonPearl and ((
|
||||||
self.world.CanAquire(items, RewardType.Agahnim) or
|
self.world.CanAcquire(items, RewardType.Agahnim) or
|
||||||
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers
|
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers
|
||||||
) and (items.Hammer or items.Hookshot and (items.Flippers or items.CanLiftLight())) or
|
) and (items.Hammer or items.Hookshot and (items.Flippers or items.CanLiftLight())) or
|
||||||
items.Hammer and items.CanLiftLight() or
|
items.Hammer and items.CanLiftLight() or
|
||||||
|
|
|
@ -34,33 +34,33 @@ class GanonsTower(Z3Region):
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
||||||
]) or self.GetLocation("Ganon's Tower - Firesnake Room").ItemIs(ItemType.KeyGT, self.world) else 3)),
|
]) or self.GetLocation("Ganon's Tower - Firesnake Room").ItemIs(ItemType.KeyGT, self.world) else 3)),
|
||||||
Location(self, 256+196, 0x1EAC4, LocationType.Regular, "Ganon's Tower - Randomizer Room - Top Left",
|
Location(self, 256+230, 0x1EAC4, LocationType.Regular, "Ganon's Tower - Randomizer Room - Top Left",
|
||||||
lambda items: self.LeftSide(items, [
|
lambda items: self.LeftSide(items, [
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
||||||
])),
|
])),
|
||||||
Location(self, 256+197, 0x1EAC7, LocationType.Regular, "Ganon's Tower - Randomizer Room - Top Right",
|
Location(self, 256+231, 0x1EAC7, LocationType.Regular, "Ganon's Tower - Randomizer Room - Top Right",
|
||||||
lambda items: self.LeftSide(items, [
|
lambda items: self.LeftSide(items, [
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
||||||
])),
|
])),
|
||||||
Location(self, 256+198, 0x1EACA, LocationType.Regular, "Ganon's Tower - Randomizer Room - Bottom Left",
|
Location(self, 256+232, 0x1EACA, LocationType.Regular, "Ganon's Tower - Randomizer Room - Bottom Left",
|
||||||
lambda items: self.LeftSide(items, [
|
lambda items: self.LeftSide(items, [
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
||||||
])),
|
])),
|
||||||
Location(self, 256+199, 0x1EACD, LocationType.Regular, "Ganon's Tower - Randomizer Room - Bottom Right",
|
Location(self, 256+233, 0x1EACD, LocationType.Regular, "Ganon's Tower - Randomizer Room - Bottom Right",
|
||||||
lambda items: self.LeftSide(items, [
|
lambda items: self.LeftSide(items, [
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
||||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left")
|
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left")
|
||||||
])),
|
])),
|
||||||
Location(self, 256+200, 0x1EAD9, LocationType.Regular, "Ganon's Tower - Hope Room - Left"),
|
Location(self, 256+234, 0x1EAD9, LocationType.Regular, "Ganon's Tower - Hope Room - Left"),
|
||||||
Location(self, 256+201, 0x1EADC, LocationType.Regular, "Ganon's Tower - Hope Room - Right"),
|
Location(self, 256+235, 0x1EADC, LocationType.Regular, "Ganon's Tower - Hope Room - Right"),
|
||||||
Location(self, 256+202, 0x1EAE2, LocationType.Regular, "Ganon's Tower - Tile Room",
|
Location(self, 256+236, 0x1EAE2, LocationType.Regular, "Ganon's Tower - Tile Room",
|
||||||
lambda items: items.Somaria),
|
lambda items: items.Somaria),
|
||||||
Location(self, 256+203, 0x1EAE5, LocationType.Regular, "Ganon's Tower - Compass Room - Top Left",
|
Location(self, 256+203, 0x1EAE5, LocationType.Regular, "Ganon's Tower - Compass Room - Top Left",
|
||||||
lambda items: self.RightSide(items, [
|
lambda items: self.RightSide(items, [
|
||||||
|
@ -118,8 +118,9 @@ class GanonsTower(Z3Region):
|
||||||
return items.Somaria and items.Firerod and items.KeyGT >= (3 if any(l.ItemIs(ItemType.BigKeyGT, self.world) for l in locations) else 4)
|
return items.Somaria and items.Firerod and items.KeyGT >= (3 if any(l.ItemIs(ItemType.BigKeyGT, self.world) for l in locations) else 4)
|
||||||
|
|
||||||
def BigKeyRoom(self, items: Progression):
|
def BigKeyRoom(self, items: Progression):
|
||||||
return items.KeyGT >= 3 and self.CanBeatArmos(items) \
|
return items.KeyGT >= 3 and \
|
||||||
and (items.Hammer and items.Hookshot or items.Firerod and items.Somaria)
|
(items.Hammer and items.Hookshot or items.Firerod and items.Somaria) \
|
||||||
|
and self.CanBeatArmos(items)
|
||||||
|
|
||||||
def TowerAscend(self, items: Progression):
|
def TowerAscend(self, items: Progression):
|
||||||
return items.BigKeyGT and items.KeyGT >= 3 and items.Bow and items.CanLightTorches()
|
return items.BigKeyGT and items.KeyGT >= 3 and items.Bow and items.CanLightTorches()
|
||||||
|
@ -134,13 +135,14 @@ class GanonsTower(Z3Region):
|
||||||
|
|
||||||
def CanEnter(self, items: Progression):
|
def CanEnter(self, items: Progression):
|
||||||
return items.MoonPearl and self.world.CanEnter("Dark World Death Mountain East", items) and \
|
return items.MoonPearl and self.world.CanEnter("Dark World Death Mountain East", items) and \
|
||||||
self.world.CanAquireAll(items, RewardType.CrystalBlue, RewardType.CrystalRed, RewardType.GoldenFourBoss)
|
self.world.CanAcquireAtLeast(self.world.TowerCrystals, items, RewardType.AnyCrystal) and \
|
||||||
|
self.world.CanAcquireAtLeast(self.world.TourianBossTokens * (self.world.TowerCrystals / 7), items, RewardType.AnyBossToken)
|
||||||
|
|
||||||
def CanFill(self, item: Item):
|
def CanFill(self, item: Item):
|
||||||
if (self.Config.GameMode == GameMode.Multiworld):
|
if (self.Config.Multiworld):
|
||||||
if (item.World != self.world or item.Progression):
|
if (item.World != self.world or item.Progression):
|
||||||
return False
|
return False
|
||||||
if (self.Config.KeyShuffle == KeyShuffle.Keysanity and not ((item.Type == ItemType.BigKeyGT or item.Type == ItemType.KeyGT) and item.World == self.world) and (item.IsKey() or item.IsBigKey() or item.IsKeycard())):
|
if (self.Config.Keysanity and not ((item.Type == ItemType.BigKeyGT or item.Type == ItemType.KeyGT) and item.World == self.world) and (item.IsKey() or item.IsBigKey() or item.IsKeycard())):
|
||||||
return False
|
return False
|
||||||
return super().CanFill(item)
|
return super().CanFill(item)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ class IcePalace(Z3Region, IReward):
|
||||||
|
|
||||||
def __init__(self, world, config: Config):
|
def __init__(self, world, config: Config):
|
||||||
super().__init__(world, config)
|
super().__init__(world, config)
|
||||||
|
self.Weight = 4
|
||||||
self.RegionItems = [ ItemType.KeyIP, ItemType.BigKeyIP, ItemType.MapIP, ItemType.CompassIP]
|
self.RegionItems = [ ItemType.KeyIP, ItemType.BigKeyIP, ItemType.MapIP, ItemType.CompassIP]
|
||||||
self.Reward = RewardType.Null
|
self.Reward = RewardType.Null
|
||||||
self.Locations = [
|
self.Locations = [
|
||||||
|
@ -43,7 +44,7 @@ class IcePalace(Z3Region, IReward):
|
||||||
]
|
]
|
||||||
|
|
||||||
def CanNotWasteKeysBeforeAccessible(self, items: Progression, locations: List[Location]):
|
def CanNotWasteKeysBeforeAccessible(self, items: Progression, locations: List[Location]):
|
||||||
return not items.BigKeyIP or any(l.ItemIs(ItemType.BigKeyIP, self.world) for l in locations)
|
return self.world.ForwardSearch or not items.BigKeyIP or any(l.ItemIs(ItemType.BigKeyIP, self.world) for l in locations)
|
||||||
|
|
||||||
def CanEnter(self, items: Progression):
|
def CanEnter(self, items: Progression):
|
||||||
return items.MoonPearl and items.Flippers and items.CanLiftHeavy() and items.CanMeltFreezors()
|
return items.MoonPearl and items.Flippers and items.CanLiftHeavy() and items.CanMeltFreezors()
|
||||||
|
|
|
@ -24,5 +24,5 @@ class NorthEast(Z3Region):
|
||||||
Location(self, 256+42, 0x1EA85, LocationType.Regular, "Sahasrahla's Hut - Middle").Weighted(sphereOne),
|
Location(self, 256+42, 0x1EA85, LocationType.Regular, "Sahasrahla's Hut - Middle").Weighted(sphereOne),
|
||||||
Location(self, 256+43, 0x1EA88, LocationType.Regular, "Sahasrahla's Hut - Right").Weighted(sphereOne),
|
Location(self, 256+43, 0x1EA88, LocationType.Regular, "Sahasrahla's Hut - Right").Weighted(sphereOne),
|
||||||
Location(self, 256+44, 0x5F1FC, LocationType.Regular, "Sahasrahla",
|
Location(self, 256+44, 0x5F1FC, LocationType.Regular, "Sahasrahla",
|
||||||
lambda items: self.world.CanAquire(items, RewardType.PendantGreen))
|
lambda items: self.world.CanAcquire(items, RewardType.PendantGreen))
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,11 +11,11 @@ class NorthWest(Z3Region):
|
||||||
sphereOne = -14
|
sphereOne = -14
|
||||||
self.Locations = [
|
self.Locations = [
|
||||||
Location(self, 256+14, 0x589B0, LocationType.Pedestal, "Master Sword Pedestal",
|
Location(self, 256+14, 0x589B0, LocationType.Pedestal, "Master Sword Pedestal",
|
||||||
lambda items: self.world.CanAquireAll(items, RewardType.PendantGreen, RewardType.PendantNonGreen)),
|
lambda items: self.world.CanAcquireAll(items, RewardType.AnyPendant)),
|
||||||
Location(self, 256+15, 0x308013, LocationType.Regular, "Mushroom").Weighted(sphereOne),
|
Location(self, 256+15, 0x308013, LocationType.Regular, "Mushroom").Weighted(sphereOne),
|
||||||
Location(self, 256+16, 0x308000, LocationType.Regular, "Lost Woods Hideout").Weighted(sphereOne),
|
Location(self, 256+16, 0x308000, LocationType.Regular, "Lost Woods Hideout").Weighted(sphereOne),
|
||||||
Location(self, 256+17, 0x308001, LocationType.Regular, "Lumberjack Tree",
|
Location(self, 256+17, 0x308001, LocationType.Regular, "Lumberjack Tree",
|
||||||
lambda items: self.world.CanAquire(items, RewardType.Agahnim) and items.Boots),
|
lambda items: self.world.CanAcquire(items, RewardType.Agahnim) and items.Boots),
|
||||||
Location(self, 256+18, 0x1EB3F, LocationType.Regular, "Pegasus Rocks",
|
Location(self, 256+18, 0x1EB3F, LocationType.Regular, "Pegasus Rocks",
|
||||||
lambda items: items.Boots),
|
lambda items: items.Boots),
|
||||||
Location(self, 256+19, 0x308004, LocationType.Regular, "Graveyard Ledge",
|
Location(self, 256+19, 0x308004, LocationType.Regular, "Graveyard Ledge",
|
||||||
|
|
|
@ -10,9 +10,10 @@ class MiseryMire(Z3Region, IReward, IMedallionAccess):
|
||||||
|
|
||||||
def __init__(self, world, config: Config):
|
def __init__(self, world, config: Config):
|
||||||
super().__init__(world, config)
|
super().__init__(world, config)
|
||||||
|
self.Weight = 2
|
||||||
self.RegionItems = [ ItemType.KeyMM, ItemType.BigKeyMM, ItemType.MapMM, ItemType.CompassMM]
|
self.RegionItems = [ ItemType.KeyMM, ItemType.BigKeyMM, ItemType.MapMM, ItemType.CompassMM]
|
||||||
self.Reward = RewardType.Null
|
self.Reward = RewardType.Null
|
||||||
self.Medallion = ItemType.Nothing
|
self.Medallion = None
|
||||||
self.Locations = [
|
self.Locations = [
|
||||||
Location(self, 256+169, 0x1EA5E, LocationType.Regular, "Misery Mire - Main Lobby",
|
Location(self, 256+169, 0x1EA5E, LocationType.Regular, "Misery Mire - Main Lobby",
|
||||||
lambda items: items.BigKeyMM or items.KeyMM >= 1),
|
lambda items: items.BigKeyMM or items.KeyMM >= 1),
|
||||||
|
@ -34,8 +35,9 @@ class MiseryMire(Z3Region, IReward, IMedallionAccess):
|
||||||
|
|
||||||
# // Need "CanKillManyEnemies" if implementing swordless
|
# // Need "CanKillManyEnemies" if implementing swordless
|
||||||
def CanEnter(self, items: Progression):
|
def CanEnter(self, items: Progression):
|
||||||
return (items.Bombos if self.Medallion == ItemType.Bombos else (
|
from worlds.smz3.TotalSMZ3.WorldState import Medallion
|
||||||
items.Ether if self.Medallion == ItemType.Ether else items.Quake)) and items.Sword and \
|
return (items.Bombos if self.Medallion == Medallion.Bombos else (
|
||||||
|
items.Ether if self.Medallion == Medallion.Ether else items.Quake)) and items.Sword and \
|
||||||
items.MoonPearl and (items.Boots or items.Hookshot) and \
|
items.MoonPearl and (items.Boots or items.Hookshot) and \
|
||||||
self.world.CanEnter("Dark World Mire", items)
|
self.world.CanEnter("Dark World Mire", items)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ class SwampPalace(Z3Region, IReward):
|
||||||
|
|
||||||
def __init__(self, world, config: Config):
|
def __init__(self, world, config: Config):
|
||||||
super().__init__(world, config)
|
super().__init__(world, config)
|
||||||
|
self.Weight = 3
|
||||||
self.RegionItems = [ ItemType.KeySP, ItemType.BigKeySP, ItemType.MapSP, ItemType.CompassSP]
|
self.RegionItems = [ ItemType.KeySP, ItemType.BigKeySP, ItemType.MapSP, ItemType.CompassSP]
|
||||||
self.Reward = RewardType.Null
|
self.Reward = RewardType.Null
|
||||||
self.Locations = [
|
self.Locations = [
|
||||||
|
|
|
@ -10,9 +10,10 @@ class TurtleRock(Z3Region, IReward, IMedallionAccess):
|
||||||
|
|
||||||
def __init__(self, world, config: Config):
|
def __init__(self, world, config: Config):
|
||||||
super().__init__(world, config)
|
super().__init__(world, config)
|
||||||
|
self.Weight = 6
|
||||||
self.RegionItems = [ ItemType.KeyTR, ItemType.BigKeyTR, ItemType.MapTR, ItemType.CompassTR]
|
self.RegionItems = [ ItemType.KeyTR, ItemType.BigKeyTR, ItemType.MapTR, ItemType.CompassTR]
|
||||||
self.Reward = RewardType.Null
|
self.Reward = RewardType.Null
|
||||||
self.Medallion = ItemType.Nothing
|
self.Medallion = None
|
||||||
self.Locations = [
|
self.Locations = [
|
||||||
Location(self, 256+177, 0x1EA22, LocationType.Regular, "Turtle Rock - Compass Chest"),
|
Location(self, 256+177, 0x1EA22, LocationType.Regular, "Turtle Rock - Compass Chest"),
|
||||||
Location(self, 256+178, 0x1EA1C, LocationType.Regular, "Turtle Rock - Roller Room - Left",
|
Location(self, 256+178, 0x1EA1C, LocationType.Regular, "Turtle Rock - Roller Room - Left",
|
||||||
|
@ -46,8 +47,9 @@ class TurtleRock(Z3Region, IReward, IMedallionAccess):
|
||||||
return items.Firerod and items.Icerod
|
return items.Firerod and items.Icerod
|
||||||
|
|
||||||
def CanEnter(self, items: Progression):
|
def CanEnter(self, items: Progression):
|
||||||
return (items.Bombos if self.Medallion == ItemType.Bombos else (
|
from worlds.smz3.TotalSMZ3.WorldState import Medallion
|
||||||
items.Ether if self.Medallion == ItemType.Ether else items.Quake)) and items.Sword and \
|
return (items.Bombos if self.Medallion == Medallion.Bombos else (
|
||||||
|
items.Ether if self.Medallion == Medallion.Ether else items.Quake)) and items.Sword and \
|
||||||
items.MoonPearl and items.CanLiftHeavy() and items.Hammer and items.Somaria and \
|
items.MoonPearl and items.CanLiftHeavy() and items.Hammer and items.Somaria and \
|
||||||
self.world.CanEnter("Light World Death Mountain East", items)
|
self.world.CanEnter("Light World Death Mountain East", items)
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,7 @@ class Dialog:
|
||||||
|
|
||||||
command = re.compile(r"^\{[^}]*\}")
|
command = re.compile(r"^\{[^}]*\}")
|
||||||
invalid = re.compile(r"(?<!^)\{[^}]*\}(?!$)", re.MULTILINE)
|
invalid = re.compile(r"(?<!^)\{[^}]*\}(?!$)", re.MULTILINE)
|
||||||
digit = re.compile(r"\d")
|
character = re.compile(r"(?P<digit>[0-9])|(?P<upper>[A-Z])|(?P<lower>[a-z])")
|
||||||
uppercaseLetter = re.compile(r"[A-Z]")
|
|
||||||
lowercaseLetter = re.compile(r"[a-z]")
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def Simple(text: str):
|
def Simple(text: str):
|
||||||
|
@ -29,19 +27,16 @@ class Dialog:
|
||||||
|
|
||||||
lineIndex += 1
|
lineIndex += 1
|
||||||
|
|
||||||
if (lineIndex % 3 == 0 and lineIndex < len(lines)):
|
if (lineIndex < len(lines)):
|
||||||
bytes.append(0x7E)
|
if (lineIndex % 3 == 0):
|
||||||
if (lineIndex >= 3 and lineIndex < len(lines)):
|
bytes.append(0x7E) # pause for input
|
||||||
bytes.append(0x73)
|
if (lineIndex >= 3):
|
||||||
|
bytes.append(0x73) # scroll
|
||||||
|
|
||||||
bytes.append(0x7F)
|
return bytes[:maxBytes - 1].append(0x7F)
|
||||||
if (len(bytes) > maxBytes):
|
|
||||||
return bytes[:maxBytes - 1].append(0x7F)
|
|
||||||
|
|
||||||
return bytes
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def Compiled(text: str, pause = True):
|
def Compiled(text: str):
|
||||||
maxBytes = 2046
|
maxBytes = 2046
|
||||||
wrap = 19
|
wrap = 19
|
||||||
|
|
||||||
|
@ -49,6 +44,7 @@ class Dialog:
|
||||||
raise Exception("Dialog commands must be placed on separate lines", text)
|
raise Exception("Dialog commands must be placed on separate lines", text)
|
||||||
|
|
||||||
padOut = False
|
padOut = False
|
||||||
|
pause = True
|
||||||
|
|
||||||
bytes = [ 0xFB ]
|
bytes = [ 0xFB ]
|
||||||
lines = Dialog.Wordwrap(text, wrap)
|
lines = Dialog.Wordwrap(text, wrap)
|
||||||
|
@ -61,33 +57,11 @@ class Dialog:
|
||||||
return [ 0xFB, 0xFE, 0x6E, 0x00, 0xFE, 0x6B, 0x04 ]
|
return [ 0xFB, 0xFE, 0x6E, 0x00, 0xFE, 0x6B, 0x04 ]
|
||||||
if (match.string == "{INTRO}"):
|
if (match.string == "{INTRO}"):
|
||||||
padOut = True
|
padOut = True
|
||||||
|
if (match.string == "{NOPAUSE}"):
|
||||||
|
pause = False
|
||||||
|
continue
|
||||||
|
|
||||||
bytesMap = {
|
result = Dialog.CommandBytesFor(match.string)
|
||||||
"{SPEED0}" : [ 0xFC, 0x00 ],
|
|
||||||
"{SPEED2}" : [ 0xFC, 0x02 ],
|
|
||||||
"{SPEED6}" : [ 0xFC, 0x06 ],
|
|
||||||
"{PAUSE1}" : [ 0xFE, 0x78, 0x01 ],
|
|
||||||
"{PAUSE3}" : [ 0xFE, 0x78, 0x03 ],
|
|
||||||
"{PAUSE5}" : [ 0xFE, 0x78, 0x05 ],
|
|
||||||
"{PAUSE7}" : [ 0xFE, 0x78, 0x07 ],
|
|
||||||
"{PAUSE9}" : [ 0xFE, 0x78, 0x09 ],
|
|
||||||
"{INPUT}" : [ 0xFA ],
|
|
||||||
"{CHOICE}" : [ 0xFE, 0x68 ],
|
|
||||||
"{ITEMSELECT}" : [ 0xFE, 0x69 ],
|
|
||||||
"{CHOICE2}" : [ 0xFE, 0x71 ],
|
|
||||||
"{CHOICE3}" : [ 0xFE, 0x72 ],
|
|
||||||
"{C:GREEN}" : [ 0xFE, 0x77, 0x07 ],
|
|
||||||
"{C:YELLOW}" : [ 0xFE, 0x77, 0x02 ],
|
|
||||||
"{HARP}" : [ 0xFE, 0x79, 0x2D ],
|
|
||||||
"{MENU}" : [ 0xFE, 0x6D, 0x00 ],
|
|
||||||
"{BOTTOM}" : [ 0xFE, 0x6D, 0x01 ],
|
|
||||||
"{NOBORDER}" : [ 0xFE, 0x6B, 0x02 ],
|
|
||||||
"{CHANGEPIC}" : [ 0xFE, 0x67, 0xFE, 0x67 ],
|
|
||||||
"{CHANGEMUSIC}" : [ 0xFE, 0x67 ],
|
|
||||||
"{INTRO}" : [ 0xFE, 0x6E, 0x00, 0xFE, 0x77, 0x07, 0xFC, 0x03, 0xFE, 0x6B, 0x02, 0xFE, 0x67 ],
|
|
||||||
"{IBOX}" : [ 0xFE, 0x6B, 0x02, 0xFE, 0x77, 0x07, 0xFC, 0x03, 0xF7 ],
|
|
||||||
}
|
|
||||||
result = bytesMap.get(match.string, None)
|
|
||||||
if (result is None):
|
if (result is None):
|
||||||
raise Exception(f"Dialog text contained unknown command {match.string}", text)
|
raise Exception(f"Dialog text contained unknown command {match.string}", text)
|
||||||
else:
|
else:
|
||||||
|
@ -98,12 +72,10 @@ class Dialog:
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (lineIndex == 1):
|
if (lineIndex > 0):
|
||||||
bytes.append(0xF8); #// row 2
|
bytes.append(0xF8 if lineIndex == 1 else #// row 2
|
||||||
elif (lineIndex >= 3 and lineIndex < lineCount):
|
0xF9 if lineIndex == 2 else #// row 3
|
||||||
bytes.append(0xF6); #// scroll
|
0xF6) #// scroll
|
||||||
elif (lineIndex >= 2):
|
|
||||||
bytes.append(0xF9); #// row 3
|
|
||||||
|
|
||||||
#// The first box needs to fill the full width with spaces as the palette is loaded weird.
|
#// The first box needs to fill the full width with spaces as the palette is loaded weird.
|
||||||
letters = line + (" " * wrap) if padOut and lineIndex < 3 else line
|
letters = line + (" " * wrap) if padOut and lineIndex < 3 else line
|
||||||
|
@ -113,10 +85,39 @@ class Dialog:
|
||||||
lineIndex += 1
|
lineIndex += 1
|
||||||
|
|
||||||
if (pause and lineIndex % 3 == 0 and lineIndex < lineCount):
|
if (pause and lineIndex % 3 == 0 and lineIndex < lineCount):
|
||||||
bytes.append(0xFA) #// wait for input
|
bytes.append(0xFA) #// pause for input
|
||||||
|
|
||||||
return bytes[:maxBytes]
|
return bytes[:maxBytes]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def CommandBytesFor(text: str):
|
||||||
|
bytesMap = {
|
||||||
|
"{SPEED0}" : [ 0xFC, 0x00 ],
|
||||||
|
"{SPEED2}" : [ 0xFC, 0x02 ],
|
||||||
|
"{SPEED6}" : [ 0xFC, 0x06 ],
|
||||||
|
"{PAUSE1}" : [ 0xFE, 0x78, 0x01 ],
|
||||||
|
"{PAUSE3}" : [ 0xFE, 0x78, 0x03 ],
|
||||||
|
"{PAUSE5}" : [ 0xFE, 0x78, 0x05 ],
|
||||||
|
"{PAUSE7}" : [ 0xFE, 0x78, 0x07 ],
|
||||||
|
"{PAUSE9}" : [ 0xFE, 0x78, 0x09 ],
|
||||||
|
"{INPUT}" : [ 0xFA ],
|
||||||
|
"{CHOICE}" : [ 0xFE, 0x68 ],
|
||||||
|
"{ITEMSELECT}" : [ 0xFE, 0x69 ],
|
||||||
|
"{CHOICE2}" : [ 0xFE, 0x71 ],
|
||||||
|
"{CHOICE3}" : [ 0xFE, 0x72 ],
|
||||||
|
"{C:GREEN}" : [ 0xFE, 0x77, 0x07 ],
|
||||||
|
"{C:YELLOW}" : [ 0xFE, 0x77, 0x02 ],
|
||||||
|
"{HARP}" : [ 0xFE, 0x79, 0x2D ],
|
||||||
|
"{MENU}" : [ 0xFE, 0x6D, 0x00 ],
|
||||||
|
"{BOTTOM}" : [ 0xFE, 0x6D, 0x01 ],
|
||||||
|
"{NOBORDER}" : [ 0xFE, 0x6B, 0x02 ],
|
||||||
|
"{CHANGEPIC}" : [ 0xFE, 0x67, 0xFE, 0x67 ],
|
||||||
|
"{CHANGEMUSIC}" : [ 0xFE, 0x67 ],
|
||||||
|
"{INTRO}" : [ 0xFE, 0x6E, 0x00, 0xFE, 0x77, 0x07, 0xFC, 0x03, 0xFE, 0x6B, 0x02, 0xFE, 0x67 ],
|
||||||
|
"{IBOX}" : [ 0xFE, 0x6B, 0x02, 0xFE, 0x77, 0x07, 0xFC, 0x03, 0xF7 ],
|
||||||
|
}
|
||||||
|
return bytesMap.get(text, None)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def Wordwrap(text: str, width: int):
|
def Wordwrap(text: str, width: int):
|
||||||
result = []
|
result = []
|
||||||
|
@ -146,9 +147,13 @@ class Dialog:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def LetterToBytes(c: str):
|
def LetterToBytes(c: str):
|
||||||
if Dialog.digit.match(c): return [(ord(c) - ord('0') + 0xA0) ]
|
match = Dialog.character.match(c)
|
||||||
elif Dialog.uppercaseLetter.match(c): return [ (ord(c) - ord('A') + 0xAA) ]
|
if match is None:
|
||||||
elif Dialog.lowercaseLetter.match(c): return [ (ord(c) - ord('a') + 0x30) ]
|
value = Dialog.letters.get(c, None)
|
||||||
|
return value if value else [ 0xFF ]
|
||||||
|
elif match.group("digit") != None: return [(ord(c) - ord('0') + 0xA0) ]
|
||||||
|
elif match.group("upper") != None: return [ (ord(c) - ord('A') + 0xAA) ]
|
||||||
|
elif match.group("lower") != None: return [ (ord(c) - ord('a') + 0x30) ]
|
||||||
else:
|
else:
|
||||||
value = Dialog.letters.get(c, None)
|
value = Dialog.letters.get(c, None)
|
||||||
return value if value else [ 0xFF ]
|
return value if value else [ 0xFF ]
|
||||||
|
|
|
@ -377,9 +377,76 @@ Items:
|
||||||
THE GREEN
|
THE GREEN
|
||||||
BOOMERANG IS
|
BOOMERANG IS
|
||||||
THE FASTEST!
|
THE FASTEST!
|
||||||
Keycard: |-
|
|
||||||
A key from
|
CardCrateriaL1: |-
|
||||||
the future?
|
An Alien Key!
|
||||||
|
It says On top
|
||||||
|
of the world!
|
||||||
|
CardCrateriaL2: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says Lower
|
||||||
|
the drawbridge
|
||||||
|
CardCrateriaBoss: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says The First
|
||||||
|
and The Last
|
||||||
|
CardBrinstarL1: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says But wait
|
||||||
|
there's more!
|
||||||
|
CardBrinstarL2: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says
|
||||||
|
Green Monkeys
|
||||||
|
CardBrinstarBoss: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says
|
||||||
|
Metroid DLC
|
||||||
|
CardNorfairL1: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says ice?
|
||||||
|
In this heat?
|
||||||
|
CardNorfairL2: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says
|
||||||
|
THE BUBBLES!
|
||||||
|
CardNorfairBoss: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says
|
||||||
|
Place your bets
|
||||||
|
CardMaridiaL1: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says A
|
||||||
|
Day at the Beach
|
||||||
|
CardMaridiaL2: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says
|
||||||
|
That's a Moray
|
||||||
|
CardMaridiaBoss: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says Shrimp
|
||||||
|
for dinner?
|
||||||
|
CardWreckedShipL1: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says
|
||||||
|
Gutter Ball
|
||||||
|
CardWreckedShipBoss: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says The
|
||||||
|
Ghost of Arrghus
|
||||||
|
CardLowerNorfairL1: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says Worst
|
||||||
|
Key in the Game
|
||||||
|
CardLowerNorfairBoss: |-
|
||||||
|
An Alien Key!
|
||||||
|
It says
|
||||||
|
This guy again?
|
||||||
|
|
||||||
|
SmMap: |-
|
||||||
|
You can now
|
||||||
|
find your way
|
||||||
|
to the stars!
|
||||||
|
|
||||||
Something: |-
|
Something: |-
|
||||||
A small victory!
|
A small victory!
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
# The order of the dialog entries is significant
|
# The order of the dialog entries is significant
|
||||||
- set_cursor: [0xFB, 0xFC, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xE4, 0xFE, 0x68]
|
- set_cursor: [0xFB, 0xFC, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xE4, 0xFE, 0x68]
|
||||||
- set_cursor2: [0xFB, 0xFC, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xE4, 0xFE, 0x68]
|
- set_cursor2: [0xFB, 0xFC, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xE4, 0xFE, 0x68]
|
||||||
- game_over_menu: { NoPause: "{SPEED0}\nSave and Continue\nSave and Quit\nContinue" }
|
- game_over_menu: "{NOPAUSE}\n{SPEED0}\nSave and Continue\nSave and Quit\nContinue"
|
||||||
- var_test: { NoPause: "0= ᚋ, 1= ᚌ\n2= ᚍ, 3= ᚎ" }
|
- var_test: "{NOPAUSE}\n0= ᚋ, 1= ᚌ\n2= ᚍ, 3= ᚎ"
|
||||||
- follower_no_enter: "Can't you take me some place nice."
|
- follower_no_enter: "Can't you take me some place nice."
|
||||||
- choice_1_3: [0xFB, 0xFC, 0x00, 0xF7, 0xE4, 0xF8, 0xFF, 0xF9, 0xFF, 0xFE, 0x71]
|
- choice_1_3: [0xFB, 0xFC, 0x00, 0xF7, 0xE4, 0xF8, 0xFF, 0xF9, 0xFF, 0xFE, 0x71]
|
||||||
- choice_2_3: [0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xE4, 0xF9, 0xFF, 0xFE, 0x71]
|
- choice_2_3: [0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xE4, 0xF9, 0xFF, 0xFE, 0x71]
|
||||||
|
@ -290,10 +290,10 @@
|
||||||
# $110
|
# $110
|
||||||
- magic_bat_wake: "You bum! I was sleeping! Where's my magic bolts?"
|
- magic_bat_wake: "You bum! I was sleeping! Where's my magic bolts?"
|
||||||
- magic_bat_give_half_magic: "How you like me now?"
|
- magic_bat_give_half_magic: "How you like me now?"
|
||||||
- intro_main: { NoPause: "{INTRO}\n Episode III\n{PAUSE3}\n A Link to\n the Past\n{PAUSE3}\n Randomizer\n{PAUSE3}\nAfter mostly disregarding what happened in the first two games.\n{PAUSE3}\nLink awakens to his uncle leaving the house.\n{PAUSE3}\nHe just runs out the door,\n{PAUSE3}\ninto the rainy night.\n{PAUSE3}\n{CHANGEPIC}\nGanon has moved around all the items in Hyrule.\n{PAUSE7}\nYou will have to find all the items necessary to beat Ganon.\n{PAUSE7}\nThis is your chance to be a hero.\n{PAUSE3}\n{CHANGEPIC}\nYou must get the 7 crystals to beat Ganon.\n{PAUSE9}\n{CHANGEPIC}" }
|
- intro_main: "{NOPAUSE}\n{INTRO}\n Episode III\n{PAUSE3}\n A Link to\n the Past\n{PAUSE3}\n Randomizer\n{PAUSE3}\nAfter mostly disregarding what happened in the first two games.\n{PAUSE3}\nLink awakens to his uncle leaving the house.\n{PAUSE3}\nHe just runs out the door,\n{PAUSE3}\ninto the rainy night.\n{PAUSE3}\n{CHANGEPIC}\nGanon has moved around all the items in Hyrule.\n{PAUSE7}\nYou will have to find all the items necessary to beat Ganon.\n{PAUSE7}\nThis is your chance to be a hero.\n{PAUSE3}\n{CHANGEPIC}\nYou must get the 7 crystals to beat Ganon.\n{PAUSE9}\n{CHANGEPIC}"
|
||||||
- intro_throne_room: { NoPause: "{IBOX}\nLook at this Stalfos on the throne." }
|
- intro_throne_room: "{NOPAUSE}\n{IBOX}\nLook at this Stalfos on the throne."
|
||||||
- intro_zelda_cell: { NoPause: "{IBOX}\nIt is your time to shine!" }
|
- intro_zelda_cell: "{NOPAUSE}\n{IBOX}\nIt is your time to shine!"
|
||||||
- intro_agahnim: { NoPause: "{IBOX}\nAlso, you need to defeat this guy!" }
|
- intro_agahnim: "{NOPAUSE}\n{IBOX}\nAlso, you need to defeat this guy!"
|
||||||
- pickup_purple_chest: "A curious box. Let's take it with us!"
|
- pickup_purple_chest: "A curious box. Let's take it with us!"
|
||||||
- bomb_shop: "30 bombs for 100 rupees. Good deals all day!"
|
- bomb_shop: "30 bombs for 100 rupees. Good deals all day!"
|
||||||
- bomb_shop_big_bomb: "30 bombs for 100 rupees, 100 rupees 1 BIG bomb. Good deals all day!"
|
- bomb_shop_big_bomb: "30 bombs for 100 rupees, 100 rupees 1 BIG bomb. Good deals all day!"
|
||||||
|
@ -341,7 +341,6 @@
|
||||||
# $140
|
# $140
|
||||||
- agahnim_defeated: "Arrrgggghhh. Well you're coming with me!"
|
- agahnim_defeated: "Arrrgggghhh. Well you're coming with me!"
|
||||||
- agahnim_final_meeting: "You have done well to come this far. Now, die!"
|
- agahnim_final_meeting: "You have done well to come this far. Now, die!"
|
||||||
# $142
|
|
||||||
- zora_meeting: "What do you want?\n ≥ Flippers\n _Nothin'\n{CHOICE}"
|
- zora_meeting: "What do you want?\n ≥ Flippers\n _Nothin'\n{CHOICE}"
|
||||||
- zora_tells_cost: "Fine! But they aren't cheap. You got 500 rupees?\n ≥ Duh\n _Oh carp\n{CHOICE}"
|
- zora_tells_cost: "Fine! But they aren't cheap. You got 500 rupees?\n ≥ Duh\n _Oh carp\n{CHOICE}"
|
||||||
- zora_get_flippers: "Here's some Flippers for you! Swim little fish, swim."
|
- zora_get_flippers: "Here's some Flippers for you! Swim little fish, swim."
|
||||||
|
@ -396,14 +395,12 @@
|
||||||
- lost_woods_thief: "Have you seen Andy?\n\nHe was out looking for our prized Ether medallion.\nI wonder when he will be back?"
|
- lost_woods_thief: "Have you seen Andy?\n\nHe was out looking for our prized Ether medallion.\nI wonder when he will be back?"
|
||||||
- blinds_hut_dude: "I'm just some dude. This is Blind's hut."
|
- blinds_hut_dude: "I'm just some dude. This is Blind's hut."
|
||||||
- end_triforce: "{SPEED2}\n{MENU}\n{NOBORDER}\n G G"
|
- end_triforce: "{SPEED2}\n{MENU}\n{NOBORDER}\n G G"
|
||||||
# $174
|
|
||||||
- toppi_fallen: "Ouch!\n\nYou Jerk!"
|
- toppi_fallen: "Ouch!\n\nYou Jerk!"
|
||||||
- kakariko_tavern_fisherman: "Don't argue\nwith a frozen\nDeadrock.\nHe'll never\nchange his\nposition!"
|
- kakariko_tavern_fisherman: "Don't argue\nwith a frozen\nDeadrock.\nHe'll never\nchange his\nposition!"
|
||||||
- thief_money: "It's a secret to everyone."
|
- thief_money: "It's a secret to everyone."
|
||||||
- thief_desert_rupee_cave: "So you, like, busted down my door, and are being a jerk by talking to me? Normally I would be angry and make you pay for it, but I bet you're just going to break all my pots and steal my 50 rupees."
|
- thief_desert_rupee_cave: "So you, like, busted down my door, and are being a jerk by talking to me? Normally I would be angry and make you pay for it, but I bet you're just going to break all my pots and steal my 50 rupees."
|
||||||
- thief_ice_rupee_cave: "I'm a rupee pot farmer. One day I will take over the world with my skillz. Have you met my brother in the desert? He's way richer than I am."
|
- thief_ice_rupee_cave: "I'm a rupee pot farmer. One day I will take over the world with my skillz. Have you met my brother in the desert? He's way richer than I am."
|
||||||
- telepathic_tile_south_east_darkworld_cave: "~~ Dev cave ~~\n No farming\n required"
|
- telepathic_tile_south_east_darkworld_cave: "~~ Dev cave ~~\n No farming\n required"
|
||||||
# $17A
|
|
||||||
- cukeman: "Did you hear that Veetorp beat ajneb174 in a 1 on 1 race at AGDQ?"
|
- cukeman: "Did you hear that Veetorp beat ajneb174 in a 1 on 1 race at AGDQ?"
|
||||||
- cukeman_2: "You found Shabadoo, huh?\nNiiiiice."
|
- cukeman_2: "You found Shabadoo, huh?\nNiiiiice."
|
||||||
- potion_shop_no_cash: "Yo! I'm not running a charity here."
|
- potion_shop_no_cash: "Yo! I'm not running a charity here."
|
||||||
|
@ -415,19 +412,25 @@
|
||||||
- game_chest_lost_woods: "Pay 100 rupees, open 1 chest. Are you lucky?\nSo, Play game?\n ≥ Play\n Never!\n{CHOICE}"
|
- game_chest_lost_woods: "Pay 100 rupees, open 1 chest. Are you lucky?\nSo, Play game?\n ≥ Play\n Never!\n{CHOICE}"
|
||||||
- kakariko_flophouse_man_no_flippers: "I sure do have a lot of beds.\n\nZora is a cheapskate and will try to sell you his trash for 500 rupees…"
|
- kakariko_flophouse_man_no_flippers: "I sure do have a lot of beds.\n\nZora is a cheapskate and will try to sell you his trash for 500 rupees…"
|
||||||
- kakariko_flophouse_man: "I sure do have a lot of beds.\n\nDid you know if you played the flute in the center of town things could happen?"
|
- kakariko_flophouse_man: "I sure do have a lot of beds.\n\nDid you know if you played the flute in the center of town things could happen?"
|
||||||
- menu_start_2: { NoPause: "{MENU}\n{SPEED0}\n≥£'s House\n_Sanctuary\n{CHOICE3}" }
|
- menu_start_2: "{NOPAUSE}\n{MENU}\n{SPEED0}\n≥£'s House\n_Sanctuary\n{CHOICE3}"
|
||||||
- menu_start_3: { NoPause: "{MENU}\n{SPEED0}\n≥£'s House\n_Sanctuary\n_Mountain Cave\n{CHOICE2}" }
|
- menu_start_3: "{NOPAUSE}\n{MENU}\n{SPEED0}\n≥£'s House\n_Sanctuary\n_Mountain Cave\n{CHOICE2}"
|
||||||
- menu_pause: { NoPause: "{SPEED0}\n≥Continue Game\n_Save and Quit\n{CHOICE3}" }
|
- menu_pause: "{NOPAUSE}\n{SPEED0}\n≥Continue Game\n_Save and Quit\n{CHOICE3}"
|
||||||
- game_digging_choice: "Have 80 Rupees? Want to play digging game?\n ≥Yes\n _No\n{CHOICE}"
|
- game_digging_choice: "Have 80 Rupees? Want to play digging game?\n ≥Yes\n _No\n{CHOICE}"
|
||||||
- game_digging_start: "Okay, use the shovel with Y!"
|
- game_digging_start: "Okay, use the shovel with Y!"
|
||||||
- game_digging_no_cash: "Shovel rental is 80 rupees.\nI have all day"
|
- game_digging_no_cash: "Shovel rental is 80 rupees.\nI have all day"
|
||||||
- game_digging_end_time: "Time's up!\nTime for you to go."
|
- game_digging_end_time: "Time's up!\nTime for you to go."
|
||||||
- game_digging_come_back_later: "Come back later, I have to bury things."
|
- game_digging_come_back_later: "Come back later, I have to bury things."
|
||||||
- game_digging_no_follower: "Something is following you. I don't like."
|
- game_digging_no_follower: "Something is following you. I don't like."
|
||||||
- menu_start_4: { NoPause: "{MENU}\n{SPEED0}\n≥£'s House\n_Mountain Cave\n{CHOICE3}" }
|
- menu_start_4: "{NOPAUSE}\n{MENU}\n{SPEED0}\n≥£'s House\n_Mountain Cave\n{CHOICE3}"
|
||||||
- ganon_fall_in_alt: "You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!"
|
- ganon_fall_in_alt: "You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!"
|
||||||
- ganon_phase_3_alt: "Got wax in your ears? I cannot die!"
|
- ganon_phase_3_alt: "Got wax in your ears? I cannot die!"
|
||||||
# $190
|
# $190
|
||||||
- sign_east_death_mountain_bridge: "How did you get up here?"
|
- sign_east_death_mountain_bridge: "How did you get up here?"
|
||||||
- fish_money: "It's a secret to everyone."
|
- fish_money: "It's a secret to everyone."
|
||||||
|
- sign_ganons_tower: "You need all 7 crystals to enter."
|
||||||
|
- sign_ganon: "You need all 7 crystals to beat Ganon."
|
||||||
|
- ganon_phase_3_no_bow: "You have no bow. Dingus!"
|
||||||
|
- ganon_phase_3_no_silvers_alt: "You can't best me without silver arrows!"
|
||||||
|
- ganon_phase_3_no_silvers: "You can't best me without silver arrows!"
|
||||||
|
- ganon_phase_3_silvers: "Oh no! Silver! My one true weakness!"
|
||||||
- end_pad_data: ""
|
- end_pad_data: ""
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import Any, List
|
||||||
import copy
|
import copy
|
||||||
from worlds.smz3.TotalSMZ3.Text.Dialog import Dialog
|
from worlds.smz3.TotalSMZ3.Text.Dialog import Dialog
|
||||||
from worlds.smz3.TotalSMZ3.Text.Texts import text_folder
|
from worlds.smz3.TotalSMZ3.Text.Texts import text_folder
|
||||||
from yaml import load, Loader
|
from Utils import unsafe_parse_yaml
|
||||||
|
|
||||||
class StringTable:
|
class StringTable:
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ class StringTable:
|
||||||
def ParseEntries(resource: str):
|
def ParseEntries(resource: str):
|
||||||
with open(resource, 'rb') as f:
|
with open(resource, 'rb') as f:
|
||||||
yaml = str(f.read(), "utf-8")
|
yaml = str(f.read(), "utf-8")
|
||||||
content = load(yaml, Loader)
|
content = unsafe_parse_yaml(yaml)
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for entryValue in content:
|
for entryValue in content:
|
||||||
|
@ -20,8 +20,6 @@ class StringTable:
|
||||||
result.append((key, value))
|
result.append((key, value))
|
||||||
elif isinstance(value, str):
|
elif isinstance(value, str):
|
||||||
result.append((key, Dialog.Compiled(value)))
|
result.append((key, Dialog.Compiled(value)))
|
||||||
elif isinstance(value, dict):
|
|
||||||
result.append((key, Dialog.Compiled(value["NoPause"], False)))
|
|
||||||
else: raise Exception(f"Did not expect an object of type {type(value)}")
|
else: raise Exception(f"Did not expect an object of type {type(value)}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -47,9 +45,11 @@ class StringTable:
|
||||||
|
|
||||||
def SetGanonThirdPhaseText(self, text: str):
|
def SetGanonThirdPhaseText(self, text: str):
|
||||||
self.SetText("ganon_phase_3", text)
|
self.SetText("ganon_phase_3", text)
|
||||||
|
self.SetText("ganon_phase_3_no_silvers", text)
|
||||||
|
self.SetText("ganon_phase_3_no_silvers_alt", text)
|
||||||
|
|
||||||
def SetTriforceRoomText(self, text: str):
|
def SetTriforceRoomText(self, text: str):
|
||||||
self.SetText("end_triforce", "{NOBORDER}\n" + text)
|
self.SetText("end_triforce", f"{{NOBORDER}}\n{text}")
|
||||||
|
|
||||||
def SetPedestalText(self, text: str):
|
def SetPedestalText(self, text: str):
|
||||||
self.SetText("mastersword_pedestal_translated", text)
|
self.SetText("mastersword_pedestal_translated", text)
|
||||||
|
@ -60,6 +60,12 @@ class StringTable:
|
||||||
def SetBombosText(self, text: str):
|
def SetBombosText(self, text: str):
|
||||||
self.SetText("tablet_bombos_book", text)
|
self.SetText("tablet_bombos_book", text)
|
||||||
|
|
||||||
|
def SetTowerRequirementText(self, text: str):
|
||||||
|
self.SetText("sign_ganons_tower", text)
|
||||||
|
|
||||||
|
def SetGanonRequirementText(self, text: str):
|
||||||
|
self.SetText("sign_ganon", text)
|
||||||
|
|
||||||
def SetText(self, name: str, text: str):
|
def SetText(self, name: str, text: str):
|
||||||
count = 0
|
count = 0
|
||||||
for key, value in self.entries:
|
for key, value in self.entries:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from worlds.smz3.TotalSMZ3.Region import Region
|
from worlds.smz3.TotalSMZ3.Region import Region
|
||||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
||||||
from worlds.smz3.TotalSMZ3.Item import Item, ItemType
|
from worlds.smz3.TotalSMZ3.Item import Item, ItemType
|
||||||
from yaml import load, Loader
|
from Utils import unsafe_parse_yaml
|
||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ class Texts:
|
||||||
def ParseYamlScripts(resource: str):
|
def ParseYamlScripts(resource: str):
|
||||||
with open(resource, 'rb') as f:
|
with open(resource, 'rb') as f:
|
||||||
yaml = str(f.read(), "utf-8")
|
yaml = str(f.read(), "utf-8")
|
||||||
return load(yaml, Loader)
|
return unsafe_parse_yaml(yaml)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def ParseTextScript(resource: str):
|
def ParseTextScript(resource: str):
|
||||||
|
@ -75,7 +75,7 @@ class Texts:
|
||||||
}
|
}
|
||||||
if item.IsMap(): name = "Map"
|
if item.IsMap(): name = "Map"
|
||||||
elif item.IsCompass(): name = "Compass"
|
elif item.IsCompass(): name = "Compass"
|
||||||
elif item.IsKeycard(): name = "Keycard"
|
elif item.IsSmMap(): name = "SmMap"
|
||||||
else: name = nameMap[item.Type]
|
else: name = nameMap[item.Type]
|
||||||
|
|
||||||
items = Texts.scripts["Items"]
|
items = Texts.scripts["Items"]
|
||||||
|
|
|
@ -54,10 +54,26 @@ class World:
|
||||||
Player: str
|
Player: str
|
||||||
Guid: str
|
Guid: str
|
||||||
Id: int
|
Id: int
|
||||||
|
WorldState = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def TowerCrystals(self):
|
||||||
|
return 7 if self.WorldState is None else self.WorldState.TowerCrystals
|
||||||
|
|
||||||
|
@property
|
||||||
|
def GanonCrystals(self):
|
||||||
|
return 7 if self.WorldState is None else self.WorldState.GanonCrystals
|
||||||
|
|
||||||
|
@property
|
||||||
|
def TourianBossTokens(self):
|
||||||
|
return 4 if self.WorldState is None else self.WorldState.TourianBossTokens
|
||||||
|
|
||||||
def Items(self):
|
def Items(self):
|
||||||
return [l.Item for l in self.Locations if l.Item != None]
|
return [l.Item for l in self.Locations if l.Item != None]
|
||||||
|
|
||||||
|
ForwardSearch: bool = False
|
||||||
|
|
||||||
|
rewardLookup: Dict[int, List[Region.IReward]]
|
||||||
locationLookup: Dict[str, Location.Location]
|
locationLookup: Dict[str, Location.Location]
|
||||||
regionLookup: Dict[str, Region.Region]
|
regionLookup: Dict[str, Region.Region]
|
||||||
|
|
||||||
|
@ -95,22 +111,22 @@ class World:
|
||||||
DarkWorldNorthEast(self, self.Config),
|
DarkWorldNorthEast(self, self.Config),
|
||||||
DarkWorldSouth(self, self.Config),
|
DarkWorldSouth(self, self.Config),
|
||||||
DarkWorldMire(self, self.Config),
|
DarkWorldMire(self, self.Config),
|
||||||
Central(self, self.Config),
|
|
||||||
CrateriaWest(self, self.Config),
|
CrateriaWest(self, self.Config),
|
||||||
|
Central(self, self.Config),
|
||||||
CrateriaEast(self, self.Config),
|
CrateriaEast(self, self.Config),
|
||||||
Blue(self, self.Config),
|
Blue(self, self.Config),
|
||||||
Green(self, self.Config),
|
Green(self, self.Config),
|
||||||
Kraid(self, self.Config),
|
|
||||||
Pink(self, self.Config),
|
Pink(self, self.Config),
|
||||||
Red(self, self.Config),
|
Red(self, self.Config),
|
||||||
|
Kraid(self, self.Config),
|
||||||
|
WreckedShip(self, self.Config),
|
||||||
Outer(self, self.Config),
|
Outer(self, self.Config),
|
||||||
Inner(self, self.Config),
|
Inner(self, self.Config),
|
||||||
NorfairUpperWest(self, self.Config),
|
NorfairUpperWest(self, self.Config),
|
||||||
NorfairUpperEast(self, self.Config),
|
NorfairUpperEast(self, self.Config),
|
||||||
Crocomire(self, self.Config),
|
Crocomire(self, self.Config),
|
||||||
NorfairLowerWest(self, self.Config),
|
NorfairLowerWest(self, self.Config),
|
||||||
NorfairLowerEast(self, self.Config),
|
NorfairLowerEast(self, self.Config)
|
||||||
WreckedShip(self, self.Config)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
self.Locations = []
|
self.Locations = []
|
||||||
|
@ -130,37 +146,32 @@ class World:
|
||||||
raise Exception(f"World.CanEnter: Invalid region name {regionName}", f'{regionName=}'.partition('=')[0])
|
raise Exception(f"World.CanEnter: Invalid region name {regionName}", f'{regionName=}'.partition('=')[0])
|
||||||
return region.CanEnter(items)
|
return region.CanEnter(items)
|
||||||
|
|
||||||
def CanAquire(self, items: Item.Progression, reward: Region.RewardType):
|
def CanAcquire(self, items: Item.Progression, reward: Region.RewardType):
|
||||||
return next(iter([region for region in self.Regions if isinstance(region, Region.IReward) and region.Reward == reward])).CanComplete(items)
|
return next(iter([region for region in self.Regions if isinstance(region, Region.IReward) and region.Reward == reward])).CanComplete(items)
|
||||||
|
|
||||||
def CanAquireAll(self, items: Item.Progression, *rewards: Region.RewardType):
|
def CanAcquireAll(self, items: Item.Progression, rewardsMask: Region.RewardType):
|
||||||
for region in self.Regions:
|
return all(region.CanComplete(items) for region in self.rewardLookup[rewardsMask.value])
|
||||||
if issubclass(type(region), Region.IReward):
|
|
||||||
if (region.Reward in rewards):
|
|
||||||
if not region.CanComplete(items):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
# return all(region.CanComplete(items) for region in self.Regions if (isinstance(region, Region.IReward) and region.Reward in rewards))
|
def CanAcquireAtLeast(self, amount, items: Item.Progression, rewardsMask: Region.RewardType):
|
||||||
|
return len([region for region in self.rewardLookup[rewardsMask.value] if region.CanComplete(items)]) >= amount
|
||||||
|
|
||||||
def Setup(self, rnd: random):
|
def Setup(self, state):
|
||||||
self.SetMedallions(rnd)
|
self.WorldState = state
|
||||||
self.SetRewards(rnd)
|
self.SetMedallions(state.Medallions)
|
||||||
|
self.SetRewards(state.Rewards)
|
||||||
|
self.SetRewardLookup()
|
||||||
|
|
||||||
def SetMedallions(self, rnd: random):
|
def SetRewards(self, rewards: List):
|
||||||
medallionMap = {0: Item.ItemType.Bombos, 1: Item.ItemType.Ether, 2: Item.ItemType.Quake}
|
regions = [region for region in self.Regions if isinstance(region, Region.IReward) and region.Reward == Region.RewardType.Null]
|
||||||
regionList = [region for region in self.Regions if isinstance(region, Region.IMedallionAccess)]
|
for (region, reward) in zip(regions, rewards):
|
||||||
for region in regionList:
|
region.Reward = reward
|
||||||
region.Medallion = medallionMap[rnd.randint(0, 2)]
|
|
||||||
|
|
||||||
def SetRewards(self, rnd: random):
|
def SetMedallions(self, medallions: List):
|
||||||
rewards = [
|
self.GetRegion("Misery Mire").Medallion = medallions[0]
|
||||||
Region.RewardType.PendantGreen, Region.RewardType.PendantNonGreen, Region.RewardType.PendantNonGreen, Region.RewardType.CrystalRed, Region.RewardType.CrystalRed,
|
self.GetRegion("Turtle Rock").Medallion = medallions[1]
|
||||||
Region.RewardType.CrystalBlue, Region.RewardType.CrystalBlue, Region.RewardType.CrystalBlue, Region.RewardType.CrystalBlue, Region.RewardType.CrystalBlue
|
|
||||||
]
|
|
||||||
rnd.shuffle(rewards)
|
|
||||||
regionList = [region for region in self.Regions if isinstance(region, Region.IReward) and region.Reward == Region.RewardType.Null]
|
|
||||||
for region in regionList:
|
|
||||||
region.Reward = rewards[0]
|
|
||||||
rewards.remove(region.Reward)
|
|
||||||
|
|
||||||
|
def SetRewardLookup(self):
|
||||||
|
#/* Generate a lookup of all possible regions for any given reward combination for faster lookup later */
|
||||||
|
self.rewardLookup: Dict[int, Region.IReward] = {}
|
||||||
|
for i in range(0, 512):
|
||||||
|
self.rewardLookup[i] = [region for region in self.Regions if isinstance(region, Region.IReward) and (region.Reward.value & i) != 0]
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from worlds.smz3.TotalSMZ3.Patch import DropPrize
|
||||||
|
from worlds.smz3.TotalSMZ3.Region import RewardType
|
||||||
|
from worlds.smz3.TotalSMZ3.Config import OpenTower, GanonVulnerable, OpenTourian
|
||||||
|
|
||||||
|
class Medallion(Enum):
|
||||||
|
Bombos = 0
|
||||||
|
Ether = 1
|
||||||
|
Quake = 2
|
||||||
|
|
||||||
|
class DropPrizeRecord:
|
||||||
|
Packs: List[DropPrize]
|
||||||
|
TreePulls: List[DropPrize]
|
||||||
|
CrabContinous: DropPrize
|
||||||
|
CrabFinal: DropPrize
|
||||||
|
Stun: DropPrize
|
||||||
|
Fish: DropPrize
|
||||||
|
|
||||||
|
def __init__(self, Packs, TreePulls, CrabContinous, CrabFinal, Stun, Fish):
|
||||||
|
self.Packs = Packs
|
||||||
|
self.TreePulls = TreePulls
|
||||||
|
self.CrabContinous = CrabContinous
|
||||||
|
self.CrabFinal = CrabFinal
|
||||||
|
self.Stun = Stun
|
||||||
|
self.Fish = Fish
|
||||||
|
|
||||||
|
class WorldState:
|
||||||
|
Rewards: List[RewardType]
|
||||||
|
Medallions: List[Medallion]
|
||||||
|
|
||||||
|
TowerCrystals: int
|
||||||
|
GanonCrystals: int
|
||||||
|
TourianBossTokens: int
|
||||||
|
|
||||||
|
DropPrizes: DropPrizeRecord
|
||||||
|
|
||||||
|
def __init__(self, config, rnd):
|
||||||
|
self.Rewards = self.DistributeRewards(rnd)
|
||||||
|
self.Medallions = self.GenerateMedallions(rnd)
|
||||||
|
self.TowerCrystals = rnd.randint(0, 7) if config.OpenTower == OpenTower.Random else config.OpenTower.value
|
||||||
|
self.GanonCrystals = rnd.randint(0, 7) if config.GanonVulnerable == GanonVulnerable.Random else config.GanonVulnerable.value
|
||||||
|
self.TourianBossTokens = rnd.randint(0, 4) if config.OpenTourian == OpenTourian.Random else config.OpenTourian.value
|
||||||
|
self.DropPrizes = self.ShuffleDropPrizes(rnd)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def Generate(config, rnd):
|
||||||
|
return WorldState(config, rnd)
|
||||||
|
|
||||||
|
BaseRewards = [
|
||||||
|
RewardType.PendantGreen, RewardType.PendantNonGreen, RewardType.PendantNonGreen, RewardType.CrystalRed, RewardType.CrystalRed,
|
||||||
|
RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue,
|
||||||
|
RewardType.AnyBossToken, RewardType.AnyBossToken, RewardType.AnyBossToken, RewardType.AnyBossToken,
|
||||||
|
]
|
||||||
|
|
||||||
|
BossTokens = [
|
||||||
|
RewardType.BossTokenKraid, RewardType.BossTokenPhantoon, RewardType.BossTokenDraygon, RewardType.BossTokenRidley
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def DistributeRewards(rnd):
|
||||||
|
#// Assign four rewards for SM using a "loot table", randomized result
|
||||||
|
gen = WorldState.Distribution().Generate(lambda dist: dist.Hit(rnd.randrange(dist.Sum)))
|
||||||
|
smRewards = [next(gen) for x in range(4)]
|
||||||
|
|
||||||
|
#// Exclude the SM rewards to get the Z3 lineup
|
||||||
|
z3Rewards = WorldState.BaseRewards[:]
|
||||||
|
for reward in smRewards:
|
||||||
|
z3Rewards.remove(reward)
|
||||||
|
|
||||||
|
rnd.shuffle(z3Rewards)
|
||||||
|
#// Replace "any token" with random specific tokens
|
||||||
|
rewards = z3Rewards + smRewards
|
||||||
|
tokens = WorldState.BossTokens[:]
|
||||||
|
rnd.shuffle(tokens)
|
||||||
|
rewards = [tokens.pop() if reward == RewardType.AnyBossToken else reward for reward in rewards]
|
||||||
|
|
||||||
|
return rewards
|
||||||
|
|
||||||
|
|
||||||
|
class Distribution:
|
||||||
|
factor = 3
|
||||||
|
|
||||||
|
def __init__(self, distribution = None, boss = None, blue = None, red = None, pend = None, green = None):
|
||||||
|
self.Boss = 4 * self.factor
|
||||||
|
self.Blue = 5 * self.factor
|
||||||
|
self.Red = 2 * self.factor
|
||||||
|
self.Pend = 2
|
||||||
|
self.Green = 1
|
||||||
|
|
||||||
|
if (distribution is not None):
|
||||||
|
self = copy(distribution)
|
||||||
|
if (boss is not None):
|
||||||
|
self.Boss = boss
|
||||||
|
if (blue is not None):
|
||||||
|
self.Blue = blue
|
||||||
|
if (red is not None):
|
||||||
|
self.Red = red
|
||||||
|
if (pend is not None):
|
||||||
|
self.Pend = pend
|
||||||
|
if (green is not None):
|
||||||
|
self.Green = green
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Sum(self):
|
||||||
|
return self.Boss + self.Blue + self.Red + self.Pend + self.Green
|
||||||
|
|
||||||
|
def Hit(self, p):
|
||||||
|
p -= self.Boss
|
||||||
|
if (p < 0): return (RewardType.AnyBossToken, WorldState.Distribution(self, boss = self.Boss - WorldState.Distribution.factor))
|
||||||
|
p -= self.Blue
|
||||||
|
if (p - self.Blue < 0): return (RewardType.CrystalBlue, WorldState.Distribution(self, blue = self.Blue - WorldState.Distribution.factor))
|
||||||
|
p -= self.Red
|
||||||
|
if (p - self.Red < 0): return (RewardType.CrystalRed, WorldState.Distribution(self, red = self.Red - WorldState.Distribution.factor))
|
||||||
|
p -= self.Pend
|
||||||
|
if (p - self.Pend < 0): return (RewardType.PendantNonGreen, WorldState.Distribution(self, pend = self.Pend - 1))
|
||||||
|
return (RewardType.PendantGreen, WorldState.Distribution(self, green = self.Green - 1))
|
||||||
|
|
||||||
|
def Generate(self, func):
|
||||||
|
result = None
|
||||||
|
while (True):
|
||||||
|
(result, newSelf) = func(self)
|
||||||
|
self.Boss = newSelf.Boss
|
||||||
|
self.Blue = newSelf.Blue
|
||||||
|
self.Red = newSelf.Red
|
||||||
|
self.Pend = newSelf.Pend
|
||||||
|
self.Green = newSelf.Green
|
||||||
|
yield result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GenerateMedallions(rnd):
|
||||||
|
return [
|
||||||
|
Medallion(rnd.randrange(3)),
|
||||||
|
Medallion(rnd.randrange(3)),
|
||||||
|
]
|
||||||
|
|
||||||
|
BaseDropPrizes = [
|
||||||
|
DropPrize.Heart, DropPrize.Heart, DropPrize.Heart, DropPrize.Heart, DropPrize.Green, DropPrize.Heart, DropPrize.Heart, DropPrize.Green, #// pack 1
|
||||||
|
DropPrize.Blue, DropPrize.Green, DropPrize.Blue, DropPrize.Red, DropPrize.Blue, DropPrize.Green, DropPrize.Blue, DropPrize.Blue, #// pack 2
|
||||||
|
DropPrize.FullMagic, DropPrize.Magic, DropPrize.Magic, DropPrize.Blue, DropPrize.FullMagic, DropPrize.Magic, DropPrize.Heart, DropPrize.Magic, #// pack 3
|
||||||
|
DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb4, DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb8, DropPrize.Bomb1, #// pack 4
|
||||||
|
DropPrize.Arrow5, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Arrow10, DropPrize.Arrow5, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Arrow10,#// pack 5
|
||||||
|
DropPrize.Magic, DropPrize.Green, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Magic, DropPrize.Bomb1, DropPrize.Green, DropPrize.Heart, #// pack 6
|
||||||
|
DropPrize.Heart, DropPrize.Fairy, DropPrize.FullMagic, DropPrize.Red, DropPrize.Bomb8, DropPrize.Heart, DropPrize.Red, DropPrize.Arrow10, #// pack 7
|
||||||
|
DropPrize.Green, DropPrize.Blue, DropPrize.Red,#// from pull trees
|
||||||
|
DropPrize.Green, DropPrize.Red,#// from prize crab
|
||||||
|
DropPrize.Green, #// stunned prize
|
||||||
|
DropPrize.Red,#// saved fish prize
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ShuffleDropPrizes(rnd):
|
||||||
|
nrPackDrops = 8 * 7
|
||||||
|
nrTreePullDrops = 3
|
||||||
|
|
||||||
|
prizes = WorldState.BaseDropPrizes[:]
|
||||||
|
rnd.shuffle(prizes)
|
||||||
|
|
||||||
|
(packs, prizes) = (prizes[:nrPackDrops], prizes[nrPackDrops:])
|
||||||
|
(treePulls, prizes) = (prizes[:nrTreePullDrops], prizes[nrTreePullDrops:])
|
||||||
|
(crabContinous, crabFinalDrop, prizes) = (prizes[0], prizes[1], prizes[2:])
|
||||||
|
(stun, prizes) = (prizes[0], prizes[1:])
|
||||||
|
fish = prizes[0]
|
||||||
|
return DropPrizeRecord(packs, treePulls, crabContinous, crabFinalDrop, stun, fish)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def SplitOff(source, count):
|
||||||
|
return (source[:count], source[count:])
|
|
@ -12,9 +12,10 @@ from worlds.smz3.TotalSMZ3.Item import ItemType
|
||||||
import worlds.smz3.TotalSMZ3.Item as TotalSMZ3Item
|
import worlds.smz3.TotalSMZ3.Item as TotalSMZ3Item
|
||||||
from worlds.smz3.TotalSMZ3.World import World as TotalSMZ3World
|
from worlds.smz3.TotalSMZ3.World import World as TotalSMZ3World
|
||||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
||||||
from worlds.smz3.TotalSMZ3.Config import Config, GameMode, GanonInvincible, Goal, KeyShuffle, MorphLocation, SMLogic, SwordLocation, Z3Logic
|
from worlds.smz3.TotalSMZ3.Config import Config, GameMode, Goal, KeyShuffle, MorphLocation, SMLogic, SwordLocation, Z3Logic, OpenTower, GanonVulnerable, OpenTourian
|
||||||
from worlds.smz3.TotalSMZ3.Location import LocationType, locations_start_id, Location as TotalSMZ3Location
|
from worlds.smz3.TotalSMZ3.Location import LocationType, locations_start_id, Location as TotalSMZ3Location
|
||||||
from worlds.smz3.TotalSMZ3.Patch import Patch as TotalSMZ3Patch, getWord, getWordArray
|
from worlds.smz3.TotalSMZ3.Patch import Patch as TotalSMZ3Patch, getWord, getWordArray
|
||||||
|
from worlds.smz3.TotalSMZ3.WorldState import WorldState
|
||||||
from ..AutoWorld import World, AutoLogicRegister, WebWorld
|
from ..AutoWorld import World, AutoLogicRegister, WebWorld
|
||||||
from .Rom import get_base_rom_bytes, SMZ3DeltaPatch
|
from .Rom import get_base_rom_bytes, SMZ3DeltaPatch
|
||||||
from .ips import IPS_Patch
|
from .ips import IPS_Patch
|
||||||
|
@ -60,12 +61,12 @@ class SMZ3World(World):
|
||||||
"""
|
"""
|
||||||
game: str = "SMZ3"
|
game: str = "SMZ3"
|
||||||
topology_present = False
|
topology_present = False
|
||||||
data_version = 1
|
data_version = 2
|
||||||
options = smz3_options
|
options = smz3_options
|
||||||
item_names: Set[str] = frozenset(TotalSMZ3Item.lookup_name_to_id)
|
item_names: Set[str] = frozenset(TotalSMZ3Item.lookup_name_to_id)
|
||||||
location_names: Set[str]
|
location_names: Set[str]
|
||||||
item_name_to_id = TotalSMZ3Item.lookup_name_to_id
|
item_name_to_id = TotalSMZ3Item.lookup_name_to_id
|
||||||
location_name_to_id: Dict[str, int] = {key : locations_start_id + value.Id for key, value in TotalSMZ3World(Config({}), "", 0, "").locationLookup.items()}
|
location_name_to_id: Dict[str, int] = {key : locations_start_id + value.Id for key, value in TotalSMZ3World(Config(), "", 0, "").locationLookup.items()}
|
||||||
web = SMZ3Web()
|
web = SMZ3Web()
|
||||||
|
|
||||||
remote_items: bool = False
|
remote_items: bool = False
|
||||||
|
@ -180,30 +181,32 @@ class SMZ3World(World):
|
||||||
base_combined_rom = get_base_rom_bytes()
|
base_combined_rom = get_base_rom_bytes()
|
||||||
|
|
||||||
def generate_early(self):
|
def generate_early(self):
|
||||||
config = Config({})
|
self.config = Config()
|
||||||
config.GameMode = GameMode.Multiworld
|
self.config.GameMode = GameMode.Multiworld
|
||||||
config.Z3Logic = Z3Logic.Normal
|
self.config.Z3Logic = Z3Logic.Normal
|
||||||
config.SMLogic = SMLogic(self.world.sm_logic[self.player].value)
|
self.config.SMLogic = SMLogic(self.world.sm_logic[self.player].value)
|
||||||
config.SwordLocation = SwordLocation(self.world.sword_location[self.player].value)
|
self.config.SwordLocation = SwordLocation(self.world.sword_location[self.player].value)
|
||||||
config.MorphLocation = MorphLocation(self.world.morph_location[self.player].value)
|
self.config.MorphLocation = MorphLocation(self.world.morph_location[self.player].value)
|
||||||
config.Goal = Goal.DefeatBoth
|
self.config.Goal = Goal(self.world.goal[self.player].value)
|
||||||
config.KeyShuffle = KeyShuffle(self.world.key_shuffle[self.player].value)
|
self.config.KeyShuffle = KeyShuffle(self.world.key_shuffle[self.player].value)
|
||||||
config.Keysanity = config.KeyShuffle != KeyShuffle.Null
|
self.config.OpenTower = OpenTower(self.world.open_tower[self.player].value)
|
||||||
config.GanonInvincible = GanonInvincible.BeforeCrystals
|
self.config.GanonVulnerable = GanonVulnerable(self.world.ganon_vulnerable[self.player].value)
|
||||||
|
self.config.OpenTourian = OpenTourian(self.world.open_tourian[self.player].value)
|
||||||
|
|
||||||
self.local_random = random.Random(self.world.random.randint(0, 1000))
|
self.local_random = random.Random(self.world.random.randint(0, 1000))
|
||||||
self.smz3World = TotalSMZ3World(config, self.world.get_player_name(self.player), self.player, self.world.seed_name)
|
self.smz3World = TotalSMZ3World(self.config, self.world.get_player_name(self.player), self.player, self.world.seed_name)
|
||||||
self.smz3DungeonItems = []
|
self.smz3DungeonItems = []
|
||||||
SMZ3World.location_names = frozenset(self.smz3World.locationLookup.keys())
|
SMZ3World.location_names = frozenset(self.smz3World.locationLookup.keys())
|
||||||
|
|
||||||
self.world.state.smz3state[self.player] = TotalSMZ3Item.Progression([])
|
self.world.state.smz3state[self.player] = TotalSMZ3Item.Progression([])
|
||||||
|
|
||||||
def generate_basic(self):
|
def generate_basic(self):
|
||||||
self.smz3World.Setup(self.world.random)
|
self.smz3World.Setup(WorldState.Generate(self.config, self.world.random))
|
||||||
self.dungeon = TotalSMZ3Item.Item.CreateDungeonPool(self.smz3World)
|
self.dungeon = TotalSMZ3Item.Item.CreateDungeonPool(self.smz3World)
|
||||||
self.dungeon.reverse()
|
self.dungeon.reverse()
|
||||||
self.progression = TotalSMZ3Item.Item.CreateProgressionPool(self.smz3World)
|
self.progression = TotalSMZ3Item.Item.CreateProgressionPool(self.smz3World)
|
||||||
self.keyCardsItems = TotalSMZ3Item.Item.CreateKeycards(self.smz3World)
|
self.keyCardsItems = TotalSMZ3Item.Item.CreateKeycards(self.smz3World)
|
||||||
|
self.SmMapsItems = TotalSMZ3Item.Item.CreateSmMaps(self.smz3World)
|
||||||
|
|
||||||
niceItems = TotalSMZ3Item.Item.CreateNicePool(self.smz3World)
|
niceItems = TotalSMZ3Item.Item.CreateNicePool(self.smz3World)
|
||||||
junkItems = TotalSMZ3Item.Item.CreateJunkPool(self.smz3World)
|
junkItems = TotalSMZ3Item.Item.CreateJunkPool(self.smz3World)
|
||||||
|
@ -211,7 +214,7 @@ class SMZ3World(World):
|
||||||
self.junkItemsNames = [item.Type.name for item in junkItems]
|
self.junkItemsNames = [item.Type.name for item in junkItems]
|
||||||
|
|
||||||
if (self.smz3World.Config.Keysanity):
|
if (self.smz3World.Config.Keysanity):
|
||||||
progressionItems = self.progression + self.dungeon + self.keyCardsItems
|
progressionItems = self.progression + self.dungeon + self.keyCardsItems + self.SmMapsItems
|
||||||
else:
|
else:
|
||||||
progressionItems = self.progression
|
progressionItems = self.progression
|
||||||
for item in self.keyCardsItems:
|
for item in self.keyCardsItems:
|
||||||
|
@ -352,6 +355,49 @@ class SMZ3World(World):
|
||||||
|
|
||||||
return patch
|
return patch
|
||||||
|
|
||||||
|
def SnesCustomization(self, addr: int):
|
||||||
|
addr = (0x400000 if addr < 0x800000 else 0)| (addr & 0x3FFFFF)
|
||||||
|
return addr
|
||||||
|
|
||||||
|
def apply_customization(self):
|
||||||
|
patch = {}
|
||||||
|
|
||||||
|
# smSpinjumps
|
||||||
|
if (self.world.spin_jumps_animation[self.player].value == 1):
|
||||||
|
patch[self.SnesCustomization(0x9B93FE)] = bytearray([0x01])
|
||||||
|
|
||||||
|
# z3HeartBeep
|
||||||
|
values = [ 0x00, 0x80, 0x40, 0x20, 0x10]
|
||||||
|
index = self.world.heart_beep_speed[self.player].value
|
||||||
|
patch[0x400033] = bytearray([values[index if index < len(values) else 2]])
|
||||||
|
|
||||||
|
# z3HeartColor
|
||||||
|
values = [
|
||||||
|
[0x24, [0x18, 0x00]],
|
||||||
|
[0x3C, [0x04, 0x17]],
|
||||||
|
[0x2C, [0xC9, 0x69]],
|
||||||
|
[0x28, [0xBC, 0x02]]
|
||||||
|
]
|
||||||
|
index = self.world.heart_color[self.player].value
|
||||||
|
(hud, fileSelect) = values[index if index < len(values) else 0]
|
||||||
|
for i in range(0, 20, 2):
|
||||||
|
patch[self.SnesCustomization(0xDFA1E + i)] = bytearray([hud])
|
||||||
|
patch[self.SnesCustomization(0x1BD6AA)] = bytearray(fileSelect)
|
||||||
|
|
||||||
|
# z3QuickSwap
|
||||||
|
patch[0x40004B] = bytearray([0x01 if self.world.quick_swap[self.player].value else 0x00])
|
||||||
|
|
||||||
|
# smEnergyBeepOff
|
||||||
|
if (self.world.energy_beep[self.player].value == 0):
|
||||||
|
for ([addr, value]) in [
|
||||||
|
[0x90EA9B, 0x80],
|
||||||
|
[0x90F337, 0x80],
|
||||||
|
[0x91E6D5, 0x80]
|
||||||
|
]:
|
||||||
|
patch[self.SnesCustomization(addr)] = bytearray([value])
|
||||||
|
|
||||||
|
return patch
|
||||||
|
|
||||||
def generate_output(self, output_directory: str):
|
def generate_output(self, output_directory: str):
|
||||||
try:
|
try:
|
||||||
base_combined_rom = get_base_rom_bytes()
|
base_combined_rom = get_base_rom_bytes()
|
||||||
|
@ -368,6 +414,7 @@ class SMZ3World(World):
|
||||||
patches = patcher.Create(self.smz3World.Config)
|
patches = patcher.Create(self.smz3World.Config)
|
||||||
patches.update(self.apply_sm_custom_sprite())
|
patches.update(self.apply_sm_custom_sprite())
|
||||||
patches.update(self.apply_item_names())
|
patches.update(self.apply_item_names())
|
||||||
|
patches.update(self.apply_customization())
|
||||||
for addr, bytes in patches.items():
|
for addr, bytes in patches.items():
|
||||||
offset = 0
|
offset = 0
|
||||||
for byte in bytes:
|
for byte in bytes:
|
||||||
|
@ -463,7 +510,7 @@ class SMZ3World(World):
|
||||||
item.item.Progression = False
|
item.item.Progression = False
|
||||||
item.location.event = False
|
item.location.event = False
|
||||||
self.unreachable.append(item.location)
|
self.unreachable.append(item.location)
|
||||||
self.JunkFillGT()
|
self.JunkFillGT(0.5)
|
||||||
|
|
||||||
def get_pre_fill_items(self):
|
def get_pre_fill_items(self):
|
||||||
if (not self.smz3World.Config.Keysanity):
|
if (not self.smz3World.Config.Keysanity):
|
||||||
|
@ -477,21 +524,34 @@ class SMZ3World(World):
|
||||||
def write_spoiler(self, spoiler_handle: TextIO):
|
def write_spoiler(self, spoiler_handle: TextIO):
|
||||||
self.world.spoiler.unreachables.update(self.unreachable)
|
self.world.spoiler.unreachables.update(self.unreachable)
|
||||||
|
|
||||||
def JunkFillGT(self):
|
def JunkFillGT(self, factor):
|
||||||
|
poolLength = len(self.world.itempool)
|
||||||
|
junkPoolIdx = [i for i in range(0, poolLength)
|
||||||
|
if self.world.itempool[i].classification in (ItemClassification.filler, ItemClassification.trap) and
|
||||||
|
self.world.itempool[i].player == self.player]
|
||||||
|
toRemove = []
|
||||||
for loc in self.locations.values():
|
for loc in self.locations.values():
|
||||||
|
# commenting this for now since doing a partial GT pre fill would allow for non SMZ3 progression in GT
|
||||||
|
# which isnt desirable (SMZ3 logic only filters for SMZ3 items). Having progression in GT can only happen in Single Player.
|
||||||
|
# if len(toRemove) >= int(len(self.locationNamesGT) * factor * self.smz3World.TowerCrystals / 7):
|
||||||
|
# break
|
||||||
if loc.name in self.locationNamesGT and loc.item is None:
|
if loc.name in self.locationNamesGT and loc.item is None:
|
||||||
poolLength = len(self.world.itempool)
|
poolLength = len(junkPoolIdx)
|
||||||
# start looking at a random starting index and loop at start if no match found
|
# start looking at a random starting index and loop at start if no match found
|
||||||
start = self.world.random.randint(0, poolLength)
|
start = self.world.random.randint(0, poolLength)
|
||||||
for off in range(0, poolLength):
|
for off in range(0, poolLength):
|
||||||
i = (start + off) % poolLength
|
i = (start + off) % poolLength
|
||||||
if self.world.itempool[i].classification in (ItemClassification.filler, ItemClassification.trap) \
|
candidate = self.world.itempool[junkPoolIdx[i]]
|
||||||
and loc.can_fill(self.world.state, self.world.itempool[i], False):
|
if junkPoolIdx[i] not in toRemove and loc.can_fill(self.world.state, candidate, False):
|
||||||
itemFromPool = self.world.itempool.pop(i)
|
itemFromPool = candidate
|
||||||
|
toRemove.append(junkPoolIdx[i])
|
||||||
break
|
break
|
||||||
self.world.push_item(loc, itemFromPool, False)
|
self.world.push_item(loc, itemFromPool, False)
|
||||||
loc.event = False
|
loc.event = False
|
||||||
|
toRemove.sort(reverse = True)
|
||||||
|
for i in toRemove:
|
||||||
|
self.world.itempool.pop(i)
|
||||||
|
|
||||||
def FillItemAtLocation(self, itemPool, itemType, location):
|
def FillItemAtLocation(self, itemPool, itemType, location):
|
||||||
itemToPlace = TotalSMZ3Item.Item.Get(itemPool, itemType, self.smz3World)
|
itemToPlace = TotalSMZ3Item.Item.Get(itemPool, itemType, self.smz3World)
|
||||||
if (itemToPlace == None):
|
if (itemToPlace == None):
|
||||||
|
@ -524,7 +584,8 @@ class SMZ3World(World):
|
||||||
|
|
||||||
def InitialFillInOwnWorld(self):
|
def InitialFillInOwnWorld(self):
|
||||||
self.FillItemAtLocation(self.dungeon, TotalSMZ3Item.ItemType.KeySW, self.smz3World.GetLocation("Skull Woods - Pinball Room"))
|
self.FillItemAtLocation(self.dungeon, TotalSMZ3Item.ItemType.KeySW, self.smz3World.GetLocation("Skull Woods - Pinball Room"))
|
||||||
self.FillItemAtLocation(self.dungeon, TotalSMZ3Item.ItemType.KeySP, self.smz3World.GetLocation("Swamp Palace - Entrance"))
|
if (not self.smz3World.Config.Keysanity):
|
||||||
|
self.FillItemAtLocation(self.dungeon, TotalSMZ3Item.ItemType.KeySP, self.smz3World.GetLocation("Swamp Palace - Entrance"))
|
||||||
|
|
||||||
# /* Check Swords option and place as needed */
|
# /* Check Swords option and place as needed */
|
||||||
if self.smz3World.Config.SwordLocation == SwordLocation.Uncle:
|
if self.smz3World.Config.SwordLocation == SwordLocation.Uncle:
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue