Compare commits

...

2 Commits

Author SHA1 Message Date
6911df1f92 2025-12-13 21:53:24 +01:00
Nikolaj
fd1405ada0 Handful of logic fixes 2025-12-09 13:31:49 +01:00
8 changed files with 467 additions and 399 deletions

View File

@@ -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)

View File

@@ -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
])

View File

@@ -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))

View File

@@ -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,

View File

@@ -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 = [

View File

@@ -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",
)

View File

@@ -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),

View File

@@ -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