✨
This commit is contained in:
383
Sly3Interface.py
383
Sly3Interface.py
@@ -16,60 +16,60 @@ class Sly3Episode(IntEnum):
|
|||||||
Honor_Among_Thieves = 6
|
Honor_Among_Thieves = 6
|
||||||
|
|
||||||
class PowerUps(NamedTuple):
|
class PowerUps(NamedTuple):
|
||||||
attack = False
|
attack: bool = False
|
||||||
binocucom = False
|
binocucom: bool = False
|
||||||
bombs = False
|
bombs: bool = False
|
||||||
unknown = False
|
unknown: bool = False
|
||||||
trigger_Bomb = False
|
trigger_Bomb: bool = False
|
||||||
fishing_pole = False
|
fishing_pole: bool = False
|
||||||
|
|
||||||
alarm_clock = False
|
alarm_clock: bool = False
|
||||||
adrenaline_burst = False
|
adrenaline_burst: bool = False
|
||||||
health_extractor = False
|
health_extractor: bool = False
|
||||||
hover_pack = False
|
hover_pack: bool = False
|
||||||
insanity_strike = False
|
insanity_strike: bool = False
|
||||||
grapple_cam = False
|
grapple_cam: bool = False
|
||||||
size_destabilizer = False
|
size_destabilizer: bool = False
|
||||||
rage_bomb = False
|
rage_bomb: bool = False
|
||||||
|
|
||||||
reduction_bomb = False
|
reduction_bomb: bool = False
|
||||||
ball_form = False
|
ball_form: bool = False
|
||||||
berserker_charge = False
|
berserker_charge: bool = False
|
||||||
juggernaut_throw = False
|
juggernaut_throw: bool = False
|
||||||
guttural_roar = False
|
guttural_roar: bool = False
|
||||||
fists_of_flame = False
|
fists_of_flame: bool = False
|
||||||
temporal_lock = False
|
temporal_lock: bool = False
|
||||||
raging_inferno_flop = False
|
raging_inferno_flop: bool = False
|
||||||
|
|
||||||
diablo_fire_slam = False
|
diablo_fire_slam: bool = False
|
||||||
smoke_bomb = False
|
smoke_bomb: bool = False
|
||||||
combat_dodge = False
|
combat_dodge: bool = False
|
||||||
paraglider = False
|
paraglider: bool = False
|
||||||
silent_obliteration = False
|
silent_obliteration: bool = False
|
||||||
feral_pounce = False
|
feral_pounce: bool = False
|
||||||
mega_jump = False
|
mega_jump: bool = False
|
||||||
knockout_dive = False
|
knockout_dive: bool = False
|
||||||
|
|
||||||
shadow_power_1 = False
|
shadow_power_1: bool = False
|
||||||
thief_reflexes = False
|
thief_reflexes: bool = False
|
||||||
shadow_power_2 = False
|
shadow_power_2: bool = False
|
||||||
rocket_boots = False
|
rocket_boots: bool = False
|
||||||
treasure_map = False
|
treasure_map: bool = False
|
||||||
shield = False
|
shield: bool = False
|
||||||
venice_disguise = False
|
venice_disguise: bool = False
|
||||||
photographer_disguise = False
|
photographer_disguise: bool = False
|
||||||
|
|
||||||
pirate_disguise = False
|
pirate_disguise: bool = False
|
||||||
spin_1 = False
|
spin_1: bool = False
|
||||||
spin_2 = False
|
spin_2: bool = False
|
||||||
spin_3 = False
|
spin_3: bool = False
|
||||||
jump_1 = False
|
jump_1: bool = False
|
||||||
jump_2 = False
|
jump_2: bool = False
|
||||||
jump_3 = False
|
jump_3: bool = False
|
||||||
push_1 = False
|
push_1: bool = False
|
||||||
|
|
||||||
push_2 = False
|
push_2: bool = False
|
||||||
push_3 = False
|
push_3: bool = False
|
||||||
|
|
||||||
class GameInterface():
|
class GameInterface():
|
||||||
"""
|
"""
|
||||||
@@ -204,175 +204,136 @@ class Sly3Interface(GameInterface):
|
|||||||
if self.in_cutscene() and pressing_x:
|
if self.in_cutscene() and pressing_x:
|
||||||
self._write32(self.addresses["skip cutscene"],0)
|
self._write32(self.addresses["skip cutscene"],0)
|
||||||
|
|
||||||
|
def load_powerups(self, powerups: PowerUps):
|
||||||
|
booleans = list(powerups)
|
||||||
|
byte_list = [
|
||||||
|
[False]*2+booleans[0:6],
|
||||||
|
booleans[6:14],
|
||||||
|
booleans[14:22],
|
||||||
|
booleans[22:30],
|
||||||
|
booleans[30:38],
|
||||||
|
booleans[38:46],
|
||||||
|
booleans[46:48]+[False]*2,
|
||||||
|
[False]*8
|
||||||
|
]
|
||||||
|
data = b''.join(
|
||||||
|
int(''.join(str(int(i)) for i in byte[::-1]),2).to_bytes(1,"big")
|
||||||
|
for byte in byte_list
|
||||||
|
)
|
||||||
|
|
||||||
|
self._write_bytes(self.addresses["gadgets"], data)
|
||||||
|
|
||||||
|
def read_powerups(self):
|
||||||
|
data = self._read_bytes(self.addresses["gadgets"], 8)
|
||||||
|
bits = [
|
||||||
|
bool(int(b))
|
||||||
|
for byte in data
|
||||||
|
for b in f"{byte:08b}"[::-1]
|
||||||
|
]
|
||||||
|
|
||||||
|
relevant_bits = bits[2:48]
|
||||||
|
return PowerUps(*relevant_bits)
|
||||||
|
|
||||||
|
def add_coins(self, to_add: int):
|
||||||
|
current_amount = self._read32(self.addresses["coins"])
|
||||||
|
new_amount = max(current_amount + to_add,0)
|
||||||
|
self._write32(self.addresses["coins"],new_amount)
|
||||||
|
|
||||||
|
#### TESTING ZONE ####
|
||||||
|
|
||||||
|
def read_text(interf: Sly3Interface, address: int):
|
||||||
|
"""Reads text at a specific address"""
|
||||||
|
text = ""
|
||||||
|
|
||||||
|
while True:
|
||||||
|
character = interf._read_bytes(address,2)
|
||||||
|
|
||||||
|
if character == b"\x00\x00":
|
||||||
|
break
|
||||||
|
|
||||||
|
text += character.decode("utf-16-le")
|
||||||
|
|
||||||
|
address += 2
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
def find_string_id(interf: Sly3Interface, _id: int):
|
||||||
|
"""Searches for a specific string by ID"""
|
||||||
|
|
||||||
|
# String table starts at 0x47A2D8
|
||||||
|
|
||||||
|
# Each entry in the string table has 4 bytes of its ID and then 4 bytes of an
|
||||||
|
# address to the string
|
||||||
|
|
||||||
|
string_table_address = interf._read32(0x47A2D8)
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
string_id = interf._read32(string_table_address+i*8)
|
||||||
|
if string_id == _id:
|
||||||
|
return interf._read32(string_table_address+i*8+4)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
def print_thiefnet_addresses(interf: Sly3Interface):
|
||||||
|
print(" {")
|
||||||
|
for i in range(44):
|
||||||
|
address = 0x343208+i*0x3c
|
||||||
|
interf._write32(address,i+1)
|
||||||
|
interf._write32(address+0xC,0)
|
||||||
|
|
||||||
|
name_id = interf._read32(address+0x14)
|
||||||
|
name_address = find_string_id(interf, name_id)
|
||||||
|
name_text = read_text(interf, name_address)
|
||||||
|
|
||||||
|
description_id = interf._read32(address+0x18)
|
||||||
|
description_address = find_string_id(interf, description_id)
|
||||||
|
|
||||||
|
print(
|
||||||
|
" " +
|
||||||
|
f"\"{name_text}\": "+
|
||||||
|
f"({hex(name_address)},{hex(description_address)}),"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(" }")
|
||||||
|
|
||||||
|
def current_job_info(interf: Sly3Interface):
|
||||||
|
current_job = interf._read32(0x36DB98)
|
||||||
|
|
||||||
|
address = interf._read32(interf.addresses["DAG root"])
|
||||||
|
i = 0
|
||||||
|
while address != 0:
|
||||||
|
job_pointer = interf._read32(address+0x6c)
|
||||||
|
job_id = interf._read32(job_pointer+0x18)
|
||||||
|
if job_id == current_job:
|
||||||
|
break
|
||||||
|
|
||||||
|
address = interf._read32(address+0x20)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
print("Job ID:", current_job)
|
||||||
|
print("Job address:", hex(address))
|
||||||
|
print("Job index:", i)
|
||||||
|
print("Job state (should be 2):", interf._read32(address+0x44))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
interf = Sly3Interface(Logger("test"))
|
interf = Sly3Interface(Logger("test"))
|
||||||
interf.connect_to_game()
|
interf.connect_to_game()
|
||||||
|
# interf.to_episode_menu()
|
||||||
|
# interf.unlock_episodes()
|
||||||
|
# interf.skip_cutscene()
|
||||||
|
|
||||||
byte_list = [
|
# Loading all power-ups (except the one I don't know)
|
||||||
[
|
# power_ups = PowerUps(True, True, True, False, *[True]*44)
|
||||||
False, # ???
|
# interf.load_powerups(power_ups)
|
||||||
False, # ???
|
|
||||||
True, # Sly/Bentley square attack
|
|
||||||
True, # Binocucom
|
|
||||||
True, # Bentley's bombs
|
|
||||||
False, # ???
|
|
||||||
True, # Trigger Bomb
|
|
||||||
True # Fishing Pole
|
|
||||||
],
|
|
||||||
[
|
|
||||||
True, # Alarm Clock
|
|
||||||
True, # Adrenaline Burst
|
|
||||||
True, # Health Extractor
|
|
||||||
True, # Hover Pack (Doesn't activate until you reload)
|
|
||||||
True, # Insanity Strike
|
|
||||||
True, # Grapple Came
|
|
||||||
True, # Size Destabilizer
|
|
||||||
True # Rage Bomb
|
|
||||||
],
|
|
||||||
[
|
|
||||||
True, # Reduction Bomb
|
|
||||||
True, # Ball Form
|
|
||||||
True, # Berskerker Charge
|
|
||||||
True, # Juggernaut Throw
|
|
||||||
True, # Gutteral Roar
|
|
||||||
True, # Fists of Flame
|
|
||||||
True, # Temporal Lock
|
|
||||||
True # Raging Inferno Flop
|
|
||||||
],
|
|
||||||
[
|
|
||||||
True, # Diablo Fire Slam
|
|
||||||
True, # Smoke Bomb
|
|
||||||
True, # Combat Dodge
|
|
||||||
True, # Paraglider
|
|
||||||
True, # Silent Obliteration
|
|
||||||
True, # Feral Pounce
|
|
||||||
True, # Mega Jump
|
|
||||||
True # Knockout Dive
|
|
||||||
],
|
|
||||||
[
|
|
||||||
True, # Shadow Power Lvl 1
|
|
||||||
True, # Thief Reflexes
|
|
||||||
True, # Shadow Power Lvl 2
|
|
||||||
True, # Rocket Boots
|
|
||||||
True, # Treasure Map
|
|
||||||
False, # ???
|
|
||||||
True, # Venice Disguise
|
|
||||||
True # Photographer Disguise
|
|
||||||
],
|
|
||||||
[
|
|
||||||
True, # Pirate Disguise
|
|
||||||
True, # Spin Attack lvl 1
|
|
||||||
True, # Spin Attack lvl 2
|
|
||||||
True, # Spin Attack lvl 3
|
|
||||||
True, # Jump Attack lvl 1
|
|
||||||
True, # Jump Attack lvl 2
|
|
||||||
True, # Jump Attack lvl 3
|
|
||||||
True # Push Attack lvl 1
|
|
||||||
],
|
|
||||||
[
|
|
||||||
True, # Push Attack lvl 1
|
|
||||||
True, # Push Attack lvl 1
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False
|
|
||||||
],
|
|
||||||
[
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
False
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
# byte_list = [[False for _ in range(8)] for _ in range(8)]
|
# Adding 10000 coins
|
||||||
|
# interf.add_coins(10000)
|
||||||
|
|
||||||
data = b''.join(
|
# === Testing Zone ===
|
||||||
int(''.join(str(int(i)) for i in byte[::-1]),2).to_bytes(1,"big")
|
|
||||||
for byte in byte_list
|
|
||||||
)
|
|
||||||
|
|
||||||
interf._write_bytes(0x468DCC,data)
|
# power_ups = PowerUps()
|
||||||
|
# interf.load_powerups(power_ups)
|
||||||
|
# print_thiefnet_addresses(interf)
|
||||||
|
|
||||||
# data = interf._read_bytes(0x468DCC, 8)
|
# interf._write32(0x1335d10+0x44, 0)
|
||||||
# bits = [
|
|
||||||
# bool(int(b))
|
|
||||||
# for byte in data
|
|
||||||
# for b in f"{byte:08b}"[::-1]
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# print(bits)
|
# current_job_info(interf)
|
||||||
|
|
||||||
interf._write32(0x468DDC, 10000)
|
|
||||||
|
|
||||||
# thiefnet_values = list(range(9)) + list(range(10,))
|
|
||||||
|
|
||||||
|
|
||||||
# def read_text(address: int):
|
|
||||||
# text = ""
|
|
||||||
|
|
||||||
# while True:
|
|
||||||
# character = interf._read_bytes(address,2)
|
|
||||||
|
|
||||||
# if character == b"\x00\x00":
|
|
||||||
# break
|
|
||||||
|
|
||||||
# text += character.decode("utf-16-le")
|
|
||||||
|
|
||||||
# address += 2
|
|
||||||
|
|
||||||
# return text
|
|
||||||
|
|
||||||
# def find_string_id(_id: int):
|
|
||||||
# string_table_address = interf._read32(0x47A2D8)
|
|
||||||
# i = 0
|
|
||||||
# while True:
|
|
||||||
# string_id = interf._read32(string_table_address+i*8)
|
|
||||||
# if string_id == _id:
|
|
||||||
# return interf._read32(string_table_address+i*8+4)
|
|
||||||
# i += 1
|
|
||||||
|
|
||||||
|
|
||||||
# print(" {")
|
|
||||||
# for i in range(44):
|
|
||||||
# address = 0x343208+i*0x3c
|
|
||||||
# interf._write32(address,i+1)
|
|
||||||
# interf._write32(address+0xC,0)
|
|
||||||
|
|
||||||
# name_id = interf._read32(address+0x14)
|
|
||||||
# name_address = find_string_id(name_id)
|
|
||||||
# name_text = read_text(name_address)
|
|
||||||
|
|
||||||
# description_id = interf._read32(address+0x18)
|
|
||||||
# description_address = find_string_id(description_id)
|
|
||||||
|
|
||||||
# print(
|
|
||||||
# " " +
|
|
||||||
# f"\"{name_text}\": "+
|
|
||||||
# f"({hex(name_address)},{hex(description_address)}),"
|
|
||||||
# )
|
|
||||||
|
|
||||||
# print(" }")
|
|
||||||
|
|
||||||
# string_table_address = interf._read32(0x47A2D8)
|
|
||||||
# for i in range(10):
|
|
||||||
# print("----")
|
|
||||||
# string_id = interf._read32(string_table_address+i*8)
|
|
||||||
# print(string_id)
|
|
||||||
# string_address = interf._read32(string_table_address+i*8+4)
|
|
||||||
# print(hex(string_address))
|
|
||||||
# print(read_text(string_address))
|
|
||||||
|
|
||||||
# print(interf._read32(0x6b4110+0x44))
|
|
||||||
print(interf._read32(0x1365be0+0x44))
|
|
||||||
print()
|
|
||||||
print(interf._read32(0x1357f80+0x44))
|
|
||||||
print(interf._read32(0x1350560+0x44))
|
|
||||||
print(interf._read32(0x135aba0+0x44))
|
|
||||||
print(interf._read32(0x36DB98))
|
|
||||||
@@ -69,6 +69,15 @@ class CoinsMaximum(Range):
|
|||||||
range_end = 1000
|
range_end = 1000
|
||||||
default = 200
|
default = 200
|
||||||
|
|
||||||
|
class ThiefNetLocations(Range):
|
||||||
|
"""
|
||||||
|
The number ThiefNet locations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
display_name = "ThiefNet Locations"
|
||||||
|
range_start = 0
|
||||||
|
range_end = 37
|
||||||
|
default = 25
|
||||||
|
|
||||||
class ThiefNetCostMinimum(Range):
|
class ThiefNetCostMinimum(Range):
|
||||||
"""
|
"""
|
||||||
@@ -100,6 +109,7 @@ class Sly3Options(PerGameCommonOptions):
|
|||||||
include_mega_jump: IncludeMegaJump
|
include_mega_jump: IncludeMegaJump
|
||||||
coins_minimum: CoinsMinimum
|
coins_minimum: CoinsMinimum
|
||||||
coins_maximum: CoinsMaximum
|
coins_maximum: CoinsMaximum
|
||||||
|
thiefnet_locations: ThiefNetLocations
|
||||||
thiefnet_minimum: ThiefNetCostMinimum
|
thiefnet_minimum: ThiefNetCostMinimum
|
||||||
thiefnet_maximum: ThiefNetCostMaximum
|
thiefnet_maximum: ThiefNetCostMaximum
|
||||||
|
|
||||||
@@ -113,6 +123,7 @@ sly3_option_groups = [
|
|||||||
CoinsMaximum
|
CoinsMaximum
|
||||||
]),
|
]),
|
||||||
OptionGroup("Locations",[
|
OptionGroup("Locations",[
|
||||||
|
ThiefNetLocations,
|
||||||
ThiefNetCostMinimum,
|
ThiefNetCostMinimum,
|
||||||
ThiefNetCostMaximum
|
ThiefNetCostMaximum
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -35,15 +35,14 @@ def gen_crew(world: "Sly3World") -> list[Item]:
|
|||||||
return crew
|
return crew
|
||||||
|
|
||||||
def gen_episodes(world: "Sly3World") -> list[Item]:
|
def gen_episodes(world: "Sly3World") -> list[Item]:
|
||||||
"""Generate the progressive episodes items for the item pool"""
|
"""Generate the episodes items for the item pool"""
|
||||||
all_episodes = [
|
all_episodes = [
|
||||||
item_name for item_name in item_groups["Episode"]
|
item_name for item_name in item_groups["Episode"]
|
||||||
for _ in range(4)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Make sure the starting episode is precollected
|
# Make sure the starting episode is precollected
|
||||||
starting_episode_n = world.options.starting_episode.value
|
starting_episode_n = world.options.starting_episode.value
|
||||||
starting_episode = f"Progressive {list(EPISODES.keys())[starting_episode_n]}"
|
starting_episode = list(EPISODES.keys())[starting_episode_n]
|
||||||
all_episodes.remove(starting_episode)
|
all_episodes.remove(starting_episode)
|
||||||
world.multiworld.push_precollected(world.create_item(starting_episode))
|
world.multiworld.push_precollected(world.create_item(starting_episode))
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import typing
|
import typing
|
||||||
|
|
||||||
from BaseClasses import Region, CollectionState, Location
|
from BaseClasses import Region, CollectionState
|
||||||
|
|
||||||
from .data.Locations import location_dict
|
from .data.Locations import location_dict
|
||||||
from .data.Constants import EPISODES, CHALLENGES
|
from .data.Constants import EPISODES, CHALLENGES, REQUIREMENTS
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from . import Sly3World
|
from . import Sly3World
|
||||||
@@ -13,40 +13,21 @@ def create_access_rule(episode: str, n: int, options: "Sly3Options", player: int
|
|||||||
"""Returns a function that checks if the player has access to a specific region"""
|
"""Returns a function that checks if the player has access to a specific region"""
|
||||||
def rule(state: CollectionState):
|
def rule(state: CollectionState):
|
||||||
access = True
|
access = True
|
||||||
item_name = f"Progressive {episode}"
|
|
||||||
if episode == "Honor Among Thieves":
|
if episode == "Honor Among Thieves":
|
||||||
access = access and state.count_group("Crew", player) == 7
|
access = access and state.count_group("Crew", player) == 7
|
||||||
else:
|
else:
|
||||||
access = access and state.count(item_name, player) >= n
|
access = access and state.count(episode, player) == 1
|
||||||
|
|
||||||
if n > 1:
|
if n > 1:
|
||||||
requirements = sum({
|
section_requirements = [
|
||||||
"An Opera of Fear": [
|
sum(
|
||||||
[],
|
ep_reqs,
|
||||||
["Binocucom", "Bentley"],
|
[]
|
||||||
["Carmelita", "Murray", "Ball Form", "Disguise (Venice)"]
|
)
|
||||||
],
|
for ep_reqs
|
||||||
"Rumble Down Under": [
|
in REQUIREMENTS["Jobs"][episode][:n-1]
|
||||||
[],
|
]
|
||||||
["Murray", "Guru"],
|
requirements = list(set(sum(section_requirements, [])))
|
||||||
["Bentley"]
|
|
||||||
],
|
|
||||||
"Flight of Fancy": [
|
|
||||||
[],
|
|
||||||
["Murray", "Bentley", "Guru", "Fishing Pole", "Penelope"],
|
|
||||||
["Hover Pack", "Carmelita", "Binocucom"]
|
|
||||||
],
|
|
||||||
"A Cold Alliance": [
|
|
||||||
["Bentley", "Murray", "Guru", "Penelope", "Binocucom"],
|
|
||||||
["Disguise (Photographer)", "Grapple-Cam", "Panda King"],
|
|
||||||
["Carmelita"]
|
|
||||||
],
|
|
||||||
"Dead Men Tell No Tales": [
|
|
||||||
["Disguise (Pirate)"],
|
|
||||||
["Bentley", "Penelope", "Grapple-Cam", "Murray", "Silent Obliteration", "Treasure Map"],
|
|
||||||
["Panda King", "Dimitri"]
|
|
||||||
]
|
|
||||||
}[episode][:n-1], [])
|
|
||||||
access = access and all(state.has(i, player) for i in requirements)
|
access = access and all(state.has(i, player) for i in requirements)
|
||||||
|
|
||||||
return access
|
return access
|
||||||
@@ -59,12 +40,13 @@ def create_regions_sly3(world: "Sly3World"):
|
|||||||
menu = Region("Menu", world.player, world.multiworld)
|
menu = Region("Menu", world.player, world.multiworld)
|
||||||
menu.add_locations({
|
menu.add_locations({
|
||||||
f"ThiefNet {i+1:02}": location_dict[f"ThiefNet {i+1:02}"].code
|
f"ThiefNet {i+1:02}": location_dict[f"ThiefNet {i+1:02}"].code
|
||||||
for i in range(37)
|
for i in range(world.options.thiefnet_locations)
|
||||||
})
|
})
|
||||||
|
|
||||||
world.multiworld.regions.append(menu)
|
world.multiworld.regions.append(menu)
|
||||||
|
|
||||||
for i, episode in enumerate(EPISODES.keys()):
|
for i, episode in enumerate(EPISODES.keys()):
|
||||||
|
print(f"==={episode}===")
|
||||||
for n in range(1,5):
|
for n in range(1,5):
|
||||||
if n == 2 and episode == "Honor Among Thieves":
|
if n == 2 and episode == "Honor Among Thieves":
|
||||||
break
|
break
|
||||||
@@ -80,6 +62,7 @@ def create_regions_sly3(world: "Sly3World"):
|
|||||||
})
|
})
|
||||||
|
|
||||||
world.multiworld.regions.append(region)
|
world.multiworld.regions.append(region)
|
||||||
|
|
||||||
menu.connect(
|
menu.connect(
|
||||||
region,
|
region,
|
||||||
None,
|
None,
|
||||||
|
|||||||
189
Sly3Rules.py
189
Sly3Rules.py
@@ -4,177 +4,68 @@ from math import ceil
|
|||||||
from BaseClasses import CollectionState
|
from BaseClasses import CollectionState
|
||||||
|
|
||||||
from worlds.generic.Rules import add_rule
|
from worlds.generic.Rules import add_rule
|
||||||
from .data.Constants import EPISODES
|
from .data.Constants import EPISODES, CHALLENGES, REQUIREMENTS
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from . import Sly3World
|
from . import Sly3World
|
||||||
|
|
||||||
def set_rules_sly3(world: "Sly3World"):
|
def set_rules_sly3(world: "Sly3World"):
|
||||||
player = world.player
|
player = world.player
|
||||||
|
thiefnet_items = world.options.thiefnet_locations.value
|
||||||
|
|
||||||
# Putting ThiefNet stuff out of logic, to make early game less slow.
|
# Putting ThiefNet stuff out of logic, to make early game less slow.
|
||||||
# Divides the items into 8 groups of 3. First groups requires 2 episodes
|
# Divides the items into groups that require a number of episode and crew
|
||||||
# items to be in logic, second group requires 4, etc.
|
# items to be in logic
|
||||||
for i in range(1,38):
|
for i in range(1,thiefnet_items):
|
||||||
episode_items_n = ceil(i/4)*2
|
divisor = ceil(thiefnet_items/12)
|
||||||
|
episode_items_n = ceil(i/divisor)
|
||||||
add_rule(
|
add_rule(
|
||||||
world.get_location(f"ThiefNet {i:02}"),
|
world.get_location(f"ThiefNet {i:02}"),
|
||||||
lambda state, n=episode_items_n: (
|
lambda state, n=episode_items_n: (
|
||||||
state.has_group("Episode", player, n)
|
(
|
||||||
|
state.count_group("Episode", player) +
|
||||||
|
state.count_group("Crew", player)
|
||||||
|
) >= n
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def require(location: str, item: str|list[str]):
|
def require(location: str, item: str|list[str]):
|
||||||
if isinstance(item,str):
|
add_rule(
|
||||||
add_rule(
|
world.get_location(location),
|
||||||
world.get_location(location),
|
lambda state, i=item: (
|
||||||
lambda state, i=item: (
|
all(state.has(j, player) for j in i)
|
||||||
state.has(i, player)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
add_rule(
|
|
||||||
world.get_location(location),
|
|
||||||
lambda state, i=item: (
|
|
||||||
all(state.has(j, player) for j in i)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
### Job requirements
|
### Job requirements
|
||||||
## An Opera of fear
|
for episode, sections in EPISODES.items():
|
||||||
# An Opera of Fear - Police HQ
|
if episode == "Honor Among Thieves":
|
||||||
|
continue
|
||||||
|
|
||||||
require("An Opera of Fear - Octavio Snap", "Binocucom")
|
for i, s in enumerate(sections):
|
||||||
# An Opera of Fear - Into the Depths
|
for j, job in enumerate(s):
|
||||||
require("An Opera of Fear - Canal Chase", "Bentley")
|
reqs = REQUIREMENTS["Jobs"][episode][i][j]
|
||||||
|
add_rule(
|
||||||
require("An Opera of Fear - Turf War!", "Carmelita")
|
world.get_location(f"{episode} - {job}"),
|
||||||
require("An Opera of Fear - Tar Ball", ["Murray", "Ball Form"])
|
lambda state, items=reqs: (
|
||||||
# An Opera of Fear - Run 'n Bomb
|
all(state.has(item, player) for item in items)
|
||||||
require("An Opera of Fear - Guard Duty", "Disguise (Venice)")
|
)
|
||||||
|
)
|
||||||
require("An Opera of Fear - Operation: Tar-Be Gone!", "Bombs")
|
|
||||||
|
|
||||||
## Rumble Down Under
|
|
||||||
# Rumble Down Under - Search for the Guru
|
|
||||||
|
|
||||||
require("Rumble Down Under - Spelunking", "Murray")
|
|
||||||
# Rumble Down Under - Dark Caves
|
|
||||||
# Rumble Down Under - Big Truck
|
|
||||||
require("Rumble Down Under - Unleash the Guru", "Guru")
|
|
||||||
|
|
||||||
# Rumble Down Under - The Claw
|
|
||||||
require("Rumble Down Under - Lemon Rage", "Bentley")
|
|
||||||
# Rumble Down Under - Hungry Croc
|
|
||||||
|
|
||||||
# Rumble Down Under - Operation: Moon Crash
|
|
||||||
|
|
||||||
## Flight of Fancy
|
|
||||||
# Flight of Fancy - Hidden Flight Roster
|
|
||||||
|
|
||||||
require("Flight of Fancy - Frame Team Belgium", ["Murray", "Bentley", "Guru", "Fishing Pole"])
|
|
||||||
require("Flight of Fancy - Frame Team Iceland", "Murray")
|
|
||||||
require("Flight of Fancy - Cooper Hangar Defense", "Penelope")
|
|
||||||
require("Flight of Fancy - ACES Semifinals", ["Murray", "Bentley", "Guru", "Fishing Pole", "Penelope"])
|
|
||||||
|
|
||||||
require("Flight of Fancy - Giant Wolf Massacre", "Binocucom")
|
|
||||||
require("Flight of Fancy - Windmill Firewall", "Hover Pack")
|
|
||||||
require("Flight of Fancy - Beauty and the Beast", "Carmelita")
|
|
||||||
|
|
||||||
require("Flight of Fancy - Operation: Turbo Dominant Eagle", "Paraglider")
|
|
||||||
|
|
||||||
## A Cold Alliance
|
|
||||||
require("A Cold Alliance - King of Fire", ["Bentley", "Murray", "Guru", "Penelope", "Binocucom"])
|
|
||||||
|
|
||||||
require("A Cold Alliance - Get a Job", "Disguise (Photographer)")
|
|
||||||
require("A Cold Alliance - Tearful Reunion", "Panda King")
|
|
||||||
require("A Cold Alliance - Grapple-Cam Break-In", "Grapple-Cam")
|
|
||||||
require("A Cold Alliance - Laptop Retrieval", ["Disguise (Photographer)", "Panda King", "Grapple-Cam"])
|
|
||||||
|
|
||||||
# A Cold Alliance - Vampiric Defense
|
|
||||||
# A Cold Alliance - Down the Line
|
|
||||||
require("A Cold Alliance - A Battery of Peril", "Carmelita")
|
|
||||||
|
|
||||||
# A Cold Alliance - Operation: Wedding Crasher
|
|
||||||
|
|
||||||
## Dead Men Tell No Tales
|
|
||||||
require("Dead Men Tell No Tales - The Talk of Pirates", "Disguise (Pirate)")
|
|
||||||
|
|
||||||
require("Dead Men Tell No Tales - Dynamic Duo", ["Bentley", "Penelope", "Grapple-Cam"])
|
|
||||||
require("Dead Men Tell No Tales - Jollyboat of Destruction", "Murray")
|
|
||||||
require("Dead Men Tell No Tales - X Marks the Spot", ["Bentley", "Penelope", "Grapple-Cam", "Murray", "Silent Obliteration", "Treasure Map"])
|
|
||||||
|
|
||||||
require("Dead Men Tell No Tales - Crusher from the Depths", "Panda King")
|
|
||||||
require("Dead Men Tell No Tales - Deep Sea Danger", "Dimitri")
|
|
||||||
# Dead Men Tell No Tales - Battle on the High Seas
|
|
||||||
|
|
||||||
require("Dead Men Tell No Tales - Operation: Reverse Double-Cross", "Guru")
|
|
||||||
|
|
||||||
## Honor Among Thieves
|
|
||||||
# Honor Among Thieves - Carmelita to the Rescue
|
|
||||||
# Honor Among Thieves - A Deadly Bite
|
|
||||||
# Honor Among Thieves - The Dark Current
|
|
||||||
# Honor Among Thieves - Bump-Charge-Jump
|
|
||||||
# Honor Among Thieves - Danger in the Skie
|
|
||||||
# Honor Among Thieves - The Ancestors' Gauntlet
|
|
||||||
# Honor Among Thieves - Stand your Ground
|
|
||||||
# Honor Among Thieves - Final Legacy
|
|
||||||
|
|
||||||
### Challenge requirements
|
### Challenge requirements
|
||||||
## An Opera of Fear
|
for episode, sections in CHALLENGES.items():
|
||||||
require("An Opera of Fear - Canal Chase - Expert Course", "Bentley")
|
if episode == "Honor Among Thieves":
|
||||||
require("An Opera of Fear - Air Time", ["Murray", "Ball Form"])
|
continue
|
||||||
# An Opera of Fear - Tower Scramble
|
|
||||||
# An Opera of Fear - Coin Chase
|
|
||||||
require("An Opera of Fear - Speed Bombing", "Bombs")
|
|
||||||
# An Opera of Fear - Octavio Canal Challenge
|
|
||||||
# An Opera of Fear - Octavio's Last Stand
|
|
||||||
require("An Opera of Fear - Venice Treasure Hunt", "Treasure Map")
|
|
||||||
|
|
||||||
## Rumble Down Under
|
for i, s in enumerate(sections):
|
||||||
# Rumble Down Under - Rock Run
|
for j, challenge in enumerate(s):
|
||||||
# Rumble Down Under - Cave Sprint
|
reqs = REQUIREMENTS["Challenges"][episode][i][j]
|
||||||
# Rumble Down Under - Cave Mayhem
|
add_rule(
|
||||||
# Rumble Down Under - Scaling the Drill
|
world.get_location(f"{episode} - {challenge}"),
|
||||||
require("Rumble Down Under - Guard Swappin'", "Guru")
|
lambda state, items=reqs: (
|
||||||
# Rumble Down Under - Quick Claw
|
all(state.has(item, player) for item in items)
|
||||||
require("Rumble Down Under - Pressure Brawl", "Bentley")
|
)
|
||||||
# Rumble Down Under - Croc and Coins
|
)
|
||||||
# Rumble Down Under - Carmelita Climb
|
|
||||||
require("Rumble Down Under - Outback Treasure Hunt", "Treasure Map")
|
|
||||||
|
|
||||||
## Flight of Fancy
|
|
||||||
# Flight of Fancy - Castle Quick Climb
|
|
||||||
require("Flight of Fancy - Muggshot Goon Attack", "Penelope")
|
|
||||||
require("Flight of Fancy - Security Breach", "Penelope")
|
|
||||||
require("Flight of Fancy - Defend the Hangar", "Penelope")
|
|
||||||
require("Flight of Fancy - Precision Air Duel", ["Murray", "Bentley", "Guru", "Fishing Pole", "Penelope"])
|
|
||||||
require("Flight of Fancy - Wolf Rampage", "Guru")
|
|
||||||
require("Flight of Fancy - One Woman Army", "Carmelita")
|
|
||||||
require("Flight of Fancy - Going Out On A Wing", "Paraglider")
|
|
||||||
require("Flight of Fancy - Holland Treasure Hunt", "Treasure Map")
|
|
||||||
|
|
||||||
## A Cold Alliance
|
|
||||||
require("A Cold Alliance - Big Air in China", ["Bentley", "Murray", "Guru", "Penelope", "Binocucom"])
|
|
||||||
|
|
||||||
require("A Cold Alliance - Sharpshooter", "Panda King")
|
|
||||||
# A Cold Alliance - Treetop Tangle
|
|
||||||
# A Cold Alliance - Tsao Showdown
|
|
||||||
require("A Cold Alliance - China Treasure Hunt", "Treasure Map")
|
|
||||||
|
|
||||||
## Dead Men Tell No Tales
|
|
||||||
require("Dead Men Tell No Tales - Patch Grab", "Disguise (Pirate)")
|
|
||||||
require("Dead Men Tell No Tales - Stealth Challenge", "Disguise (Pirate)")
|
|
||||||
require("Dead Men Tell No Tales - Boat Bash", "Murray")
|
|
||||||
require("Dead Men Tell No Tales - Last Ship Sailing", ["Bentley", "Penelope", "Grapple-Cam", "Murray", "Silent Obliteration", "Treasure Map"])
|
|
||||||
# Dead Men Tell No Tales - Pirate Treasure Hunt
|
|
||||||
|
|
||||||
## Honor Among Thieves
|
|
||||||
# Beauty versus the Beast
|
|
||||||
# Road Rage
|
|
||||||
# Dr. M Dogfight
|
|
||||||
# Ultimate Gauntlet
|
|
||||||
# Battle Against Time
|
|
||||||
|
|
||||||
if world.options.goal.value < 6:
|
if world.options.goal.value < 6:
|
||||||
victory_condition = [
|
victory_condition = [
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ class Sly3World(World):
|
|||||||
self.options.include_mega_jump.value = slot_data["include_mega_jump"]
|
self.options.include_mega_jump.value = slot_data["include_mega_jump"]
|
||||||
self.options.coins_minimum.value = slot_data["coins_minimum"]
|
self.options.coins_minimum.value = slot_data["coins_minimum"]
|
||||||
self.options.coins_maximum.value = slot_data["coins_maximum"]
|
self.options.coins_maximum.value = slot_data["coins_maximum"]
|
||||||
|
self.options.thiefnet_locations.value = slot_data["thiefnet_locations"]
|
||||||
self.options.thiefnet_minimum.value = slot_data["thiefnet_minimum"]
|
self.options.thiefnet_minimum.value = slot_data["thiefnet_minimum"]
|
||||||
self.options.thiefnet_maximum.value = slot_data["thiefnet_maximum"]
|
self.options.thiefnet_maximum.value = slot_data["thiefnet_maximum"]
|
||||||
return
|
return
|
||||||
@@ -155,6 +156,7 @@ class Sly3World(World):
|
|||||||
"include_mega_jump",
|
"include_mega_jump",
|
||||||
"coins_minimum",
|
"coins_minimum",
|
||||||
"coins_maximum",
|
"coins_maximum",
|
||||||
|
"thiefnet_locations",
|
||||||
"thiefnet_minimum",
|
"thiefnet_minimum",
|
||||||
"thiefnet_maximum",
|
"thiefnet_maximum",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -203,6 +203,178 @@ CHALLENGES = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Jobs/Challenges -> episode -> section -> job
|
||||||
|
# dict[ list[ list[ list[]]]]
|
||||||
|
REQUIREMENTS = {
|
||||||
|
"Jobs": {
|
||||||
|
"An Opera of Fear": [
|
||||||
|
[[]],
|
||||||
|
[
|
||||||
|
["Binocucom"],
|
||||||
|
[],
|
||||||
|
["Bentley"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Carmelita"],
|
||||||
|
["Murray", "Ball Form"],
|
||||||
|
[],
|
||||||
|
["Disguise (Venice)"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Bombs"]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"Rumble Down Under" :[
|
||||||
|
[[]],
|
||||||
|
[
|
||||||
|
["Murray"],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
["Guru"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
["Bentley"],
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
[[]]
|
||||||
|
],
|
||||||
|
"Flight of Fancy": [
|
||||||
|
[[]],
|
||||||
|
[
|
||||||
|
["Murray", "Bentley", "Guru", "Fishing Pole"],
|
||||||
|
["Murray"],
|
||||||
|
["Penelope"],
|
||||||
|
["Murray", "Bentley", "Guru", "Fishing Pole", "Penelope"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Binocucom"],
|
||||||
|
["Hover Pack"],
|
||||||
|
["Carmelita"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Paraglider"]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"A Cold Alliance": [
|
||||||
|
[
|
||||||
|
["Bentley", "Murray", "Guru", "Penelope", "Binocucom"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Disguise (Photographer)"],
|
||||||
|
["Panda King"],
|
||||||
|
["Grapple-Cam"],
|
||||||
|
["Disguise (Photographer)", "Panda King", "Grapple-Cam"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
["Carmelita"]
|
||||||
|
],
|
||||||
|
[[]]
|
||||||
|
],
|
||||||
|
"Dead Men Tell No Tales": [
|
||||||
|
[
|
||||||
|
["Disguise (Pirate)"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Bentley", "Penelope", "Grapple-Cam"],
|
||||||
|
["Murray"],
|
||||||
|
["Bentley", "Penelope", "Grapple-Cam", "Murray", "Silent Obliteration", "Treasure Map"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Panda King"],
|
||||||
|
["Dimitri"],
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Guru"]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"Challenges": {
|
||||||
|
"An Opera of Fear": [
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
["Bentley"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Murray", "Ball Form"],
|
||||||
|
[],
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Bombs"],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
["Treasure Map"]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"Rumble Down Under" :[
|
||||||
|
[[]],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
["Guru"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
["Bentley"],
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
["Treasure Map"]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"Flight of Fancy": [
|
||||||
|
[[]],
|
||||||
|
[
|
||||||
|
["Penelope"],
|
||||||
|
["Penelope"],
|
||||||
|
["Penelope"],
|
||||||
|
["Murray", "Bentley", "Guru", "Fishing Pole", "Penelope"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
["Carmelita"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Paraglider"],
|
||||||
|
["Treasure Map"]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"A Cold Alliance": [
|
||||||
|
[
|
||||||
|
["Bentley", "Murray", "Guru", "Penelope", "Binocucom"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Panda King"],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
["Treasure Map"]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"Dead Men Tell No Tales": [
|
||||||
|
[
|
||||||
|
["Disguise (Pirate)"],
|
||||||
|
["Disguise (Pirate)"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["Murray"],
|
||||||
|
["Bentley", "Penelope", "Grapple-Cam", "Murray", "Silent Obliteration", "Treasure Map"]
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
[[]]
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ADDRESSES = {
|
ADDRESSES = {
|
||||||
"SCUS-97464" : {
|
"SCUS-97464" : {
|
||||||
"map id": 0x47989C,
|
"map id": 0x47989C,
|
||||||
@@ -213,6 +385,9 @@ ADDRESSES = {
|
|||||||
"frame counter": 0x389BE0,
|
"frame counter": 0x389BE0,
|
||||||
"x pressed": 0x36E78E,
|
"x pressed": 0x36E78E,
|
||||||
"skip cutscene": 0x389C20,
|
"skip cutscene": 0x389C20,
|
||||||
|
"gadgets": 0x468DCC,
|
||||||
|
"coins": 0x468DDC,
|
||||||
|
"DAG root": 0x478C8C,
|
||||||
"jobs": [
|
"jobs": [
|
||||||
[
|
[
|
||||||
[0x1335d10]
|
[0x1335d10]
|
||||||
@@ -227,7 +402,52 @@ ADDRESSES = {
|
|||||||
],
|
],
|
||||||
"text": {
|
"text": {
|
||||||
"powerups": [
|
"powerups": [
|
||||||
{},
|
{
|
||||||
|
"Trigger Bomb": (0x58db60,0x58dcf0),
|
||||||
|
"Fishing Pole": (0x595da0,0x595fc0),
|
||||||
|
"Alarm Clock": (0x591db0,0x591f40),
|
||||||
|
"Adrenaline Burst": (0x58e800,0x58e9c0),
|
||||||
|
"Health Extractor": (0x58ebe0,0x58ee00),
|
||||||
|
"Hover Pack": (0x58ef90,0x58f1b0),
|
||||||
|
"Insanity Strike": (0x593a40,0x593b70),
|
||||||
|
"Grapple-Cam": (0x5957d0,0x595ae0),
|
||||||
|
"Size Destabilizer": (0x58df70,0x58e170),
|
||||||
|
"Rage Bomb": (0x594160,0x5942d0),
|
||||||
|
"Reduction Bomb": (0x58f260,0x58f390),
|
||||||
|
"Be The Ball": (0x5955c0,0x595730),
|
||||||
|
"Berserker Charge": (0x5912d0,0x591380),
|
||||||
|
"Juggernaut Throw": (0x590730,0x590850),
|
||||||
|
"Guttural Roar": (0x5914e0,0x591610),
|
||||||
|
"Fists of Flame": (0x58f960,0x5900b0),
|
||||||
|
"Temporal Lock": (0x58f440,0x58f5a0),
|
||||||
|
"Raging Inferno Flop": (0x5916c0,0x5917f0),
|
||||||
|
"Diablo Fire Slam": (0x590fa0,0x591090),
|
||||||
|
"Smoke Bomb": (0x5918f0,0x591a00),
|
||||||
|
"Combat Dodge": (0x591b40,0x591c90),
|
||||||
|
"Paraglide": (0x5921f0,0x5924c0),
|
||||||
|
"Silent Obliteration": (0x592690,0x592870),
|
||||||
|
"Feral Pounce": (0x592c50,0x592de0),
|
||||||
|
"Mega Jump": (0x592fc0,0x593180),
|
||||||
|
"Knockout Dive": (0x5936d0,0x5938e0),
|
||||||
|
"Shadow Power Level 1": (0x594770,0x594880),
|
||||||
|
"Thief Reflexes": (0x592a10,0x592b50),
|
||||||
|
"Shadow Power Level 2": (0x5949e0,0x594d00),
|
||||||
|
"Rocket Boots": (0x577060,0x577300),
|
||||||
|
"Treasure Map": (0x576af0,0x576dc0),
|
||||||
|
"ENGLISHpowerup_shield_name": (0x596280,0x576450),
|
||||||
|
"Venice Disguise": (0x577510,0x577670),
|
||||||
|
"Photographer Disguise": (0x5778f0,0x577ac0),
|
||||||
|
"Pirate Disguise": (0x577ca0,0x577e20),
|
||||||
|
"Spin Attack Level 1": (0x577fe0,0x5781b0),
|
||||||
|
"Spin Attack Level 2": (0x578350,0x578500),
|
||||||
|
"Spin Attack Level 3": (0x578770,0x578af0),
|
||||||
|
"Jump Attack Level 1": (0x578d80,0x579070),
|
||||||
|
"Jump Attack Level 2": (0x579390,0x579620),
|
||||||
|
"Jump Attack Level 3": (0x5797b0,0x579950),
|
||||||
|
"Push Attack Level 1": (0x579ae0,0x579d70),
|
||||||
|
"Push Attack Level 2": (0x579f70,0x57a1f0),
|
||||||
|
"Push Attack Level 3": (0x57a670,0x57a940),
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
"Trigger Bomb": (0x592c40,0x592e00),
|
"Trigger Bomb": (0x592c40,0x592e00),
|
||||||
|
|||||||
@@ -70,16 +70,16 @@ crew_list = [
|
|||||||
("Carmelita", ItemClassification.progression, "Crew")
|
("Carmelita", ItemClassification.progression, "Crew")
|
||||||
]
|
]
|
||||||
|
|
||||||
progressive_episode_list = [
|
episode_list = [
|
||||||
(f"Progressive {e}", ItemClassification.progression, "Episode")
|
(episode, ItemClassification.progression, "Episode")
|
||||||
for e in list(EPISODES.keys())[:-1]
|
for episode in list(EPISODES.keys())[:-1]
|
||||||
]
|
]
|
||||||
|
|
||||||
item_list = (
|
item_list = (
|
||||||
filler_list +
|
filler_list +
|
||||||
powerup_list +
|
powerup_list +
|
||||||
crew_list +
|
crew_list +
|
||||||
progressive_episode_list
|
episode_list
|
||||||
)
|
)
|
||||||
|
|
||||||
base_code = 5318008
|
base_code = 5318008
|
||||||
|
|||||||
Reference in New Issue
Block a user