forked from NikolajDanger/APSly3
Compare commits
2 Commits
92289523ad
...
6911df1f92
| Author | SHA1 | Date | |
|---|---|---|---|
| 6911df1f92 | |||
|
|
fd1405ada0 |
383
Sly3Interface.py
383
Sly3Interface.py
@@ -16,60 +16,60 @@ class Sly3Episode(IntEnum):
|
||||
Honor_Among_Thieves = 6
|
||||
|
||||
class PowerUps(NamedTuple):
|
||||
attack = False
|
||||
binocucom = False
|
||||
bombs = False
|
||||
unknown = False
|
||||
trigger_Bomb = False
|
||||
fishing_pole = False
|
||||
attack: bool = False
|
||||
binocucom: bool = False
|
||||
bombs: bool = False
|
||||
unknown: bool = False
|
||||
trigger_Bomb: bool = False
|
||||
fishing_pole: bool = False
|
||||
|
||||
alarm_clock = False
|
||||
adrenaline_burst = False
|
||||
health_extractor = False
|
||||
hover_pack = False
|
||||
insanity_strike = False
|
||||
grapple_cam = False
|
||||
size_destabilizer = False
|
||||
rage_bomb = False
|
||||
alarm_clock: bool = False
|
||||
adrenaline_burst: bool = False
|
||||
health_extractor: bool = False
|
||||
hover_pack: bool = False
|
||||
insanity_strike: bool = False
|
||||
grapple_cam: bool = False
|
||||
size_destabilizer: bool = False
|
||||
rage_bomb: bool = False
|
||||
|
||||
reduction_bomb = False
|
||||
ball_form = False
|
||||
berserker_charge = False
|
||||
juggernaut_throw = False
|
||||
guttural_roar = False
|
||||
fists_of_flame = False
|
||||
temporal_lock = False
|
||||
raging_inferno_flop = False
|
||||
reduction_bomb: bool = False
|
||||
ball_form: bool = False
|
||||
berserker_charge: bool = False
|
||||
juggernaut_throw: bool = False
|
||||
guttural_roar: bool = False
|
||||
fists_of_flame: bool = False
|
||||
temporal_lock: bool = False
|
||||
raging_inferno_flop: bool = False
|
||||
|
||||
diablo_fire_slam = False
|
||||
smoke_bomb = False
|
||||
combat_dodge = False
|
||||
paraglider = False
|
||||
silent_obliteration = False
|
||||
feral_pounce = False
|
||||
mega_jump = False
|
||||
knockout_dive = False
|
||||
diablo_fire_slam: bool = False
|
||||
smoke_bomb: bool = False
|
||||
combat_dodge: bool = False
|
||||
paraglider: bool = False
|
||||
silent_obliteration: bool = False
|
||||
feral_pounce: bool = False
|
||||
mega_jump: bool = False
|
||||
knockout_dive: bool = False
|
||||
|
||||
shadow_power_1 = False
|
||||
thief_reflexes = False
|
||||
shadow_power_2 = False
|
||||
rocket_boots = False
|
||||
treasure_map = False
|
||||
shield = False
|
||||
venice_disguise = False
|
||||
photographer_disguise = False
|
||||
shadow_power_1: bool = False
|
||||
thief_reflexes: bool = False
|
||||
shadow_power_2: bool = False
|
||||
rocket_boots: bool = False
|
||||
treasure_map: bool = False
|
||||
shield: bool = False
|
||||
venice_disguise: bool = False
|
||||
photographer_disguise: bool = False
|
||||
|
||||
pirate_disguise = False
|
||||
spin_1 = False
|
||||
spin_2 = False
|
||||
spin_3 = False
|
||||
jump_1 = False
|
||||
jump_2 = False
|
||||
jump_3 = False
|
||||
push_1 = False
|
||||
pirate_disguise: bool = False
|
||||
spin_1: bool = False
|
||||
spin_2: bool = False
|
||||
spin_3: bool = False
|
||||
jump_1: bool = False
|
||||
jump_2: bool = False
|
||||
jump_3: bool = False
|
||||
push_1: bool = False
|
||||
|
||||
push_2 = False
|
||||
push_3 = False
|
||||
push_2: bool = False
|
||||
push_3: bool = False
|
||||
|
||||
class GameInterface():
|
||||
"""
|
||||
@@ -204,175 +204,136 @@ class Sly3Interface(GameInterface):
|
||||
if self.in_cutscene() and pressing_x:
|
||||
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__":
|
||||
interf = Sly3Interface(Logger("test"))
|
||||
interf.connect_to_game()
|
||||
# interf.to_episode_menu()
|
||||
# interf.unlock_episodes()
|
||||
# interf.skip_cutscene()
|
||||
|
||||
byte_list = [
|
||||
[
|
||||
False, # ???
|
||||
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
|
||||
],
|
||||
]
|
||||
# Loading all power-ups (except the one I don't know)
|
||||
# power_ups = PowerUps(True, True, True, False, *[True]*44)
|
||||
# interf.load_powerups(power_ups)
|
||||
|
||||
# byte_list = [[False for _ in range(8)] for _ in range(8)]
|
||||
# Adding 10000 coins
|
||||
# interf.add_coins(10000)
|
||||
|
||||
data = b''.join(
|
||||
int(''.join(str(int(i)) for i in byte[::-1]),2).to_bytes(1,"big")
|
||||
for byte in byte_list
|
||||
)
|
||||
# === Testing Zone ===
|
||||
|
||||
interf._write_bytes(0x468DCC,data)
|
||||
# power_ups = PowerUps()
|
||||
# interf.load_powerups(power_ups)
|
||||
# print_thiefnet_addresses(interf)
|
||||
|
||||
# data = interf._read_bytes(0x468DCC, 8)
|
||||
# bits = [
|
||||
# bool(int(b))
|
||||
# for byte in data
|
||||
# for b in f"{byte:08b}"[::-1]
|
||||
# ]
|
||||
# interf._write32(0x1335d10+0x44, 0)
|
||||
|
||||
# print(bits)
|
||||
|
||||
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))
|
||||
# current_job_info(interf)
|
||||
@@ -69,6 +69,15 @@ class CoinsMaximum(Range):
|
||||
range_end = 1000
|
||||
default = 200
|
||||
|
||||
class ThiefNetLocations(Range):
|
||||
"""
|
||||
The number ThiefNet locations.
|
||||
"""
|
||||
|
||||
display_name = "ThiefNet Locations"
|
||||
range_start = 0
|
||||
range_end = 37
|
||||
default = 25
|
||||
|
||||
class ThiefNetCostMinimum(Range):
|
||||
"""
|
||||
@@ -100,6 +109,7 @@ class Sly3Options(PerGameCommonOptions):
|
||||
include_mega_jump: IncludeMegaJump
|
||||
coins_minimum: CoinsMinimum
|
||||
coins_maximum: CoinsMaximum
|
||||
thiefnet_locations: ThiefNetLocations
|
||||
thiefnet_minimum: ThiefNetCostMinimum
|
||||
thiefnet_maximum: ThiefNetCostMaximum
|
||||
|
||||
@@ -113,6 +123,7 @@ sly3_option_groups = [
|
||||
CoinsMaximum
|
||||
]),
|
||||
OptionGroup("Locations",[
|
||||
ThiefNetLocations,
|
||||
ThiefNetCostMinimum,
|
||||
ThiefNetCostMaximum
|
||||
])
|
||||
|
||||
@@ -35,15 +35,14 @@ def gen_crew(world: "Sly3World") -> list[Item]:
|
||||
return crew
|
||||
|
||||
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 = [
|
||||
item_name for item_name in item_groups["Episode"]
|
||||
for _ in range(4)
|
||||
]
|
||||
|
||||
# Make sure the starting episode is precollected
|
||||
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)
|
||||
world.multiworld.push_precollected(world.create_item(starting_episode))
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import Region, CollectionState, Location
|
||||
from BaseClasses import Region, CollectionState
|
||||
|
||||
from .data.Locations import location_dict
|
||||
from .data.Constants import EPISODES, CHALLENGES
|
||||
from .data.Constants import EPISODES, CHALLENGES, REQUIREMENTS
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
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"""
|
||||
def rule(state: CollectionState):
|
||||
access = True
|
||||
item_name = f"Progressive {episode}"
|
||||
if episode == "Honor Among Thieves":
|
||||
access = access and state.count_group("Crew", player) == 7
|
||||
else:
|
||||
access = access and state.count(item_name, player) >= n
|
||||
access = access and state.count(episode, player) == 1
|
||||
|
||||
if n > 1:
|
||||
requirements = sum({
|
||||
"An Opera of Fear": [
|
||||
[],
|
||||
["Binocucom", "Bentley"],
|
||||
["Carmelita", "Murray", "Ball Form", "Disguise (Venice)"]
|
||||
],
|
||||
"Rumble Down Under": [
|
||||
[],
|
||||
["Murray", "Guru"],
|
||||
["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": [
|
||||
[],
|
||||
["Bentley", "Penelope", "Grapple-Cam", "Murray", "Silent Obliteration", "Treasure Map"],
|
||||
["Panda King", "Dimitri"]
|
||||
]
|
||||
}[episode][:n-2], [])
|
||||
section_requirements = [
|
||||
sum(
|
||||
ep_reqs,
|
||||
[]
|
||||
)
|
||||
for ep_reqs
|
||||
in REQUIREMENTS["Jobs"][episode][:n-1]
|
||||
]
|
||||
requirements = list(set(sum(section_requirements, [])))
|
||||
access = access and all(state.has(i, player) for i in requirements)
|
||||
|
||||
return access
|
||||
@@ -59,12 +40,13 @@ def create_regions_sly3(world: "Sly3World"):
|
||||
menu = Region("Menu", world.player, world.multiworld)
|
||||
menu.add_locations({
|
||||
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)
|
||||
|
||||
for i, episode in enumerate(EPISODES.keys()):
|
||||
print(f"==={episode}===")
|
||||
for n in range(1,5):
|
||||
if n == 2 and episode == "Honor Among Thieves":
|
||||
break
|
||||
@@ -80,6 +62,7 @@ def create_regions_sly3(world: "Sly3World"):
|
||||
})
|
||||
|
||||
world.multiworld.regions.append(region)
|
||||
|
||||
menu.connect(
|
||||
region,
|
||||
None,
|
||||
|
||||
188
Sly3Rules.py
188
Sly3Rules.py
@@ -4,176 +4,68 @@ from math import ceil
|
||||
from BaseClasses import CollectionState
|
||||
|
||||
from worlds.generic.Rules import add_rule
|
||||
from .data.Constants import EPISODES
|
||||
from .data.Constants import EPISODES, CHALLENGES, REQUIREMENTS
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from . import Sly3World
|
||||
|
||||
def set_rules_sly3(world: "Sly3World"):
|
||||
player = world.player
|
||||
thiefnet_items = world.options.thiefnet_locations.value
|
||||
|
||||
# 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
|
||||
# items to be in logic, second group requires 4, etc.
|
||||
for i in range(1,35):
|
||||
episode_items_n = ceil(i/4)*2
|
||||
# Divides the items into groups that require a number of episode and crew
|
||||
# items to be in logic
|
||||
for i in range(1,thiefnet_items):
|
||||
divisor = ceil(thiefnet_items/12)
|
||||
episode_items_n = ceil(i/divisor)
|
||||
add_rule(
|
||||
world.get_location(f"ThiefNet {i:02}"),
|
||||
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]):
|
||||
if isinstance(item,str):
|
||||
add_rule(
|
||||
world.get_location(location),
|
||||
lambda state, i=item: (
|
||||
state.has(i, player)
|
||||
)
|
||||
)
|
||||
else:
|
||||
add_rule(
|
||||
world.get_location(location),
|
||||
lambda state, i=item: (
|
||||
all(state.has(j, player) for j in i)
|
||||
)
|
||||
add_rule(
|
||||
world.get_location(location),
|
||||
lambda state, i=item: (
|
||||
all(state.has(j, player) for j in i)
|
||||
)
|
||||
)
|
||||
|
||||
### Job requirements
|
||||
## An Opera of fear
|
||||
# An Opera of Fear - Police HQ
|
||||
for episode, sections in EPISODES.items():
|
||||
if episode == "Honor Among Thieves":
|
||||
continue
|
||||
|
||||
require("An Opera of Fear - Octavio Snap", "Binocucom")
|
||||
# An Opera of Fear - Into the Depths
|
||||
require("An Opera of Fear - Canal Chase", "Bentley")
|
||||
|
||||
require("An Opera of Fear - Turf War!", "Carmelita")
|
||||
require("An Opera of Fear - Tar Ball", ["Murray", "Ball Form"])
|
||||
# An Opera of Fear - Run 'n Bomb
|
||||
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
|
||||
for i, s in enumerate(sections):
|
||||
for j, job in enumerate(s):
|
||||
reqs = REQUIREMENTS["Jobs"][episode][i][j]
|
||||
add_rule(
|
||||
world.get_location(f"{episode} - {job}"),
|
||||
lambda state, items=reqs: (
|
||||
all(state.has(item, player) for item in items)
|
||||
)
|
||||
)
|
||||
|
||||
### Challenge requirements
|
||||
## An Opera of Fear
|
||||
require("An Opera of Fear - Canal Chase - Expert Course", "Bentley")
|
||||
require("An Opera of Fear - Air Time", ["Murray", "Ball Form"])
|
||||
# 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")
|
||||
for episode, sections in CHALLENGES.items():
|
||||
if episode == "Honor Among Thieves":
|
||||
continue
|
||||
|
||||
## Rumble Down Under
|
||||
# Rumble Down Under - Rock Run
|
||||
# Rumble Down Under - Cave Sprint
|
||||
# Rumble Down Under - Cave Mayhem
|
||||
# Rumble Down Under - Scaling the Drill
|
||||
require("Rumble Down Under - Guard Swappin'", "Guru")
|
||||
# Rumble Down Under - Quick Claw
|
||||
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
|
||||
# Dead Men Tell No Tales - Patch Grab
|
||||
# Dead Men Tell No Tales - Stealth Challenge
|
||||
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
|
||||
for i, s in enumerate(sections):
|
||||
for j, challenge in enumerate(s):
|
||||
reqs = REQUIREMENTS["Challenges"][episode][i][j]
|
||||
add_rule(
|
||||
world.get_location(f"{episode} - {challenge}"),
|
||||
lambda state, items=reqs: (
|
||||
all(state.has(item, player) for item in items)
|
||||
)
|
||||
)
|
||||
|
||||
if world.options.goal.value < 6:
|
||||
victory_condition = [
|
||||
|
||||
@@ -106,6 +106,7 @@ class Sly3World(World):
|
||||
self.options.include_mega_jump.value = slot_data["include_mega_jump"]
|
||||
self.options.coins_minimum.value = slot_data["coins_minimum"]
|
||||
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_maximum.value = slot_data["thiefnet_maximum"]
|
||||
return
|
||||
@@ -155,6 +156,7 @@ class Sly3World(World):
|
||||
"include_mega_jump",
|
||||
"coins_minimum",
|
||||
"coins_maximum",
|
||||
"thiefnet_locations",
|
||||
"thiefnet_minimum",
|
||||
"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 = {
|
||||
"SCUS-97464" : {
|
||||
"map id": 0x47989C,
|
||||
@@ -213,6 +385,9 @@ ADDRESSES = {
|
||||
"frame counter": 0x389BE0,
|
||||
"x pressed": 0x36E78E,
|
||||
"skip cutscene": 0x389C20,
|
||||
"gadgets": 0x468DCC,
|
||||
"coins": 0x468DDC,
|
||||
"DAG root": 0x478C8C,
|
||||
"jobs": [
|
||||
[
|
||||
[0x1335d10]
|
||||
@@ -227,7 +402,52 @@ ADDRESSES = {
|
||||
],
|
||||
"text": {
|
||||
"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),
|
||||
|
||||
@@ -70,16 +70,16 @@ crew_list = [
|
||||
("Carmelita", ItemClassification.progression, "Crew")
|
||||
]
|
||||
|
||||
progressive_episode_list = [
|
||||
(f"Progressive {e}", ItemClassification.progression, "Episode")
|
||||
for e in list(EPISODES.keys())[:-1]
|
||||
episode_list = [
|
||||
(episode, ItemClassification.progression, "Episode")
|
||||
for episode in list(EPISODES.keys())[:-1]
|
||||
]
|
||||
|
||||
item_list = (
|
||||
filler_list +
|
||||
powerup_list +
|
||||
crew_list +
|
||||
progressive_episode_list
|
||||
episode_list
|
||||
)
|
||||
|
||||
base_code = 5318008
|
||||
|
||||
Reference in New Issue
Block a user