Skip to content

Commit

Permalink
Merge branch 'ArchipelagoMW:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
CookieCat45 authored Jan 3, 2024
2 parents ae58cd9 + 0df0955 commit 185f5b5
Show file tree
Hide file tree
Showing 11 changed files with 609 additions and 538 deletions.
505 changes: 5 additions & 500 deletions ZillionClient.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions data/lua/connector_bizhawk_generic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ function send_receive ()
failed_guard_response = response
end
else
if type(response) ~= "string" then response = "Unknown error" end
res[i] = {type = "ERROR", err = response}
end
end
Expand Down
1 change: 1 addition & 0 deletions worlds/ff1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def create_regions(self):
items = get_options(self.multiworld, 'items', self.player)
goal_rule = generate_rule([[name for name in items.keys() if name in FF1_PROGRESSION_LIST and name != "Shard"]],
self.player)
terminated_event.access_rule = goal_rule
if "Shard" in items.keys():
def goal_rule_and_shards(state):
return goal_rule(state) and state.has("Shard", self.player, 32)
Expand Down
12 changes: 8 additions & 4 deletions worlds/generic/Rules.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import collections
import logging
import typing

from BaseClasses import LocationProgressType, MultiWorld, Location, Region, Entrance
Expand Down Expand Up @@ -81,15 +82,18 @@ def forbid(sender: int, receiver: int, items: typing.Set[str]):
i.name not in sending_blockers[i.player] and old_rule(i)


def exclusion_rules(world: MultiWorld, player: int, exclude_locations: typing.Set[str]) -> None:
def exclusion_rules(multiworld: MultiWorld, player: int, exclude_locations: typing.Set[str]) -> None:
for loc_name in exclude_locations:
try:
location = world.get_location(loc_name, player)
location = multiworld.get_location(loc_name, player)
except KeyError as e: # failed to find the given location. Check if it's a legitimate location
if loc_name not in world.worlds[player].location_name_to_id:
if loc_name not in multiworld.worlds[player].location_name_to_id:
raise Exception(f"Unable to exclude location {loc_name} in player {player}'s world.") from e
else:
location.progress_type = LocationProgressType.EXCLUDED
if not location.event:
location.progress_type = LocationProgressType.EXCLUDED
else:
logging.warning(f"Unable to exclude location {loc_name} in player {player}'s world.")


def set_rule(spot: typing.Union["BaseClasses.Location", "BaseClasses.Entrance"], rule: CollectionRule):
Expand Down
95 changes: 77 additions & 18 deletions worlds/kh2/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,6 @@ def __init__(self, server_address, password):
},
},
}
self.front_of_inventory = {
"Sora": 0x2546,
"Donald": 0x2658,
"Goofy": 0x276C,
}
self.kh2seedname = None
self.kh2slotdata = None
self.itemamount = {}
Expand Down Expand Up @@ -169,6 +164,14 @@ def __init__(self, server_address, password):
self.ability_code_list = None
self.master_growth = {"High Jump", "Quick Run", "Dodge Roll", "Aerial Dodge", "Glide"}

self.base_hp = 20
self.base_mp = 100
self.base_drive = 5
self.base_accessory_slots = 1
self.base_armor_slots = 1
self.base_item_slots = 3
self.front_ability_slots = [0x2546, 0x2658, 0x276C, 0x2548, 0x254A, 0x254C, 0x265A, 0x265C, 0x265E, 0x276E, 0x2770, 0x2772]

async def server_auth(self, password_requested: bool = False):
if password_requested and not self.password:
await super(KH2Context, self).server_auth(password_requested)
Expand Down Expand Up @@ -219,6 +222,12 @@ def kh2_write_byte(self, address, value):
def kh2_read_byte(self, address):
return int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + address, 1), "big")

def kh2_read_int(self, address):
return self.kh2.read_int(self.kh2.base_address + address)

def kh2_write_int(self, address, value):
self.kh2.write_int(self.kh2.base_address + address, value)

def on_package(self, cmd: str, args: dict):
if cmd in {"RoomInfo"}:
self.kh2seedname = args['seed_name']
Expand Down Expand Up @@ -476,7 +485,7 @@ async def verifyLevel(self):

async def give_item(self, item, location):
try:
# todo: ripout all the itemtype stuff and just have one dictionary. the only thing that needs to be tracked from the server/local is abilites
# todo: ripout all the itemtype stuff and just have one dictionary. the only thing that needs to be tracked from the server/local is abilites
itemname = self.lookup_id_to_item[item]
itemdata = self.item_name_to_data[itemname]
# itemcode = self.kh2_item_name_to_id[itemname]
Expand Down Expand Up @@ -507,6 +516,8 @@ async def give_item(self, item, location):
ability_slot = self.kh2_seed_save_cache["GoofyInvo"][1]
self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname].append(ability_slot)
self.kh2_seed_save_cache["GoofyInvo"][1] -= 2
if ability_slot in self.front_ability_slots:
self.front_ability_slots.remove(ability_slot)

elif len(self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname]) < \
self.AbilityQuantityDict[itemname]:
Expand All @@ -518,11 +529,14 @@ async def give_item(self, item, location):
ability_slot = self.kh2_seed_save_cache["DonaldInvo"][0]
self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname].append(ability_slot)
self.kh2_seed_save_cache["DonaldInvo"][0] -= 2
elif itemname in self.goofy_ability_set:
else:
ability_slot = self.kh2_seed_save_cache["GoofyInvo"][0]
self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname].append(ability_slot)
self.kh2_seed_save_cache["GoofyInvo"][0] -= 2

if ability_slot in self.front_ability_slots:
self.front_ability_slots.remove(ability_slot)

elif itemdata.memaddr in {0x36C4, 0x36C5, 0x36C6, 0x36C0, 0x36CA}:
# if memaddr is in a bitmask location in memory
if itemname not in self.kh2_seed_save_cache["AmountInvo"]["Bitmask"]:
Expand Down Expand Up @@ -615,7 +629,7 @@ async def verifyItems(self):
master_sell = master_equipment | master_staff | master_shield

await asyncio.create_task(self.IsInShop(master_sell))

# print(self.kh2_seed_save_cache["AmountInvo"]["Ability"])
for item_name in master_amount:
item_data = self.item_name_to_data[item_name]
amount_of_items = 0
Expand Down Expand Up @@ -673,10 +687,10 @@ async def verifyItems(self):
self.kh2_write_short(self.Save + slot, item_data.memaddr)
# removes the duped ability if client gave faster than the game.

for charInvo in {"Sora", "Donald", "Goofy"}:
if self.kh2_read_short(self.Save + self.front_of_inventory[charInvo]) != 0:
print(f"removed {self.Save + self.front_of_inventory[charInvo]} from {charInvo}")
self.kh2_write_short(self.Save + self.front_of_inventory[charInvo], 0)
for ability in self.front_ability_slots:
if self.kh2_read_short(self.Save + ability) != 0:
print(f"removed {self.Save + ability} from {ability}")
self.kh2_write_short(self.Save + ability, 0)

# remove the dummy level 1 growths if they are in these invo slots.
for inventorySlot in {0x25CE, 0x25D0, 0x25D2, 0x25D4, 0x25D6, 0x25D8}:
Expand Down Expand Up @@ -740,15 +754,60 @@ async def verifyItems(self):
self.kh2_write_byte(self.Save + item_data.memaddr, amount_of_items)

for item_name in master_stat:
item_data = self.item_name_to_data[item_name]
amount_of_items = 0
amount_of_items += self.kh2_seed_save_cache["AmountInvo"]["StatIncrease"][item_name]
if self.kh2_read_byte(self.Slot1 + 0x1B2) >= 5:
if item_name == ItemName.MaxHPUp:
if self.kh2_read_byte(self.Save + 0x2498) < 3: # Non-Critical
Bonus = 5
else: # Critical
Bonus = 2
if self.kh2_read_int(self.Slot1 + 0x004) != self.base_hp + (Bonus * amount_of_items):
self.kh2_write_int(self.Slot1 + 0x004, self.base_hp + (Bonus * amount_of_items))

elif item_name == ItemName.MaxMPUp:
if self.kh2_read_byte(self.Save + 0x2498) < 3: # Non-Critical
Bonus = 10
else: # Critical
Bonus = 5
if self.kh2_read_int(self.Slot1 + 0x184) != self.base_mp + (Bonus * amount_of_items):
self.kh2_write_int(self.Slot1 + 0x184, self.base_mp + (Bonus * amount_of_items))

elif item_name == ItemName.DriveGaugeUp:
current_max_drive = self.kh2_read_byte(self.Slot1 + 0x1B2)
# change when max drive is changed from 6 to 4
if current_max_drive < 9 and current_max_drive != self.base_drive + amount_of_items:
self.kh2_write_byte(self.Slot1 + 0x1B2, self.base_drive + amount_of_items)

elif item_name == ItemName.AccessorySlotUp:
current_accessory = self.kh2_read_byte(self.Save + 0x2501)
if current_accessory != self.base_accessory_slots + amount_of_items:
if 4 > current_accessory < self.base_accessory_slots + amount_of_items:
self.kh2_write_byte(self.Save + 0x2501, current_accessory + 1)
elif self.base_accessory_slots + amount_of_items < 4:
self.kh2_write_byte(self.Save + 0x2501, self.base_accessory_slots + amount_of_items)

elif item_name == ItemName.ArmorSlotUp:
current_armor_slots = self.kh2_read_byte(self.Save + 0x2500)
if current_armor_slots != self.base_armor_slots + amount_of_items:
if 4 > current_armor_slots < self.base_armor_slots + amount_of_items:
self.kh2_write_byte(self.Save + 0x2500, current_armor_slots + 1)
elif self.base_armor_slots + amount_of_items < 4:
self.kh2_write_byte(self.Save + 0x2500, self.base_armor_slots + amount_of_items)

elif item_name == ItemName.ItemSlotUp:
current_item_slots = self.kh2_read_byte(self.Save + 0x2502)
if current_item_slots != self.base_item_slots + amount_of_items:
if 8 > current_item_slots < self.base_item_slots + amount_of_items:
self.kh2_write_byte(self.Save + 0x2502, current_item_slots + 1)
elif self.base_item_slots + amount_of_items < 8:
self.kh2_write_byte(self.Save + 0x2502, self.base_item_slots + amount_of_items)

# if self.kh2_read_byte(self.Save + item_data.memaddr) != amount_of_items \
# and self.kh2_read_byte(self.Slot1 + 0x1B2) >= 5 and \
# self.kh2_read_byte(self.Save + 0x23DF) & 0x1 << 3 > 0 and self.kh2_read_byte(0x741320) in {10, 8}:
# self.kh2_write_byte(self.Save + item_data.memaddr, amount_of_items)

# if slot1 has 5 drive gauge and goa lost illusion is checked and they are not in a cutscene
if self.kh2_read_byte(self.Save + item_data.memaddr) != amount_of_items \
and self.kh2_read_byte(self.Slot1 + 0x1B2) >= 5 and \
self.kh2_read_byte(self.Save + 0x23DF) & 0x1 << 3 > 0 and self.kh2_read_byte(0x741320) in {10, 8}:
self.kh2_write_byte(self.Save + item_data.memaddr, amount_of_items)
if "PoptrackerVersionCheck" in self.kh2slotdata:
if self.kh2slotdata["PoptrackerVersionCheck"] > 4.2 and self.kh2_read_byte(self.Save + 0x3607) != 1: # telling the goa they are on version 4.3
self.kh2_write_byte(self.Save + 0x3607, 1)
Expand Down
5 changes: 2 additions & 3 deletions worlds/kh2/Rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ def set_kh2_rules(self) -> None:
add_item_rule(location, lambda item: item.player == self.player and item.name in DonaldAbility_Table.keys())

def set_kh2_goal(self):

final_xemnas_location = self.multiworld.get_location(LocationName.FinalXemnas, self.player)
if self.multiworld.Goal[self.player] == "three_proofs":
final_xemnas_location.access_rule = lambda state: self.kh2_has_all(three_proofs, state)
Expand All @@ -291,8 +290,8 @@ def set_kh2_goal(self):
else:
self.multiworld.completion_condition[self.player] = lambda state: state.has(ItemName.Bounty, self.player, self.multiworld.BountyRequired[self.player].value)
else:
final_xemnas_location.access_rule = lambda state: state.has(ItemName.Bounty, self.player, self.multiworld.BountyRequired[self.player].value) and\
state.has(ItemName.LuckyEmblem, self.player, self.multiworld.LuckyEmblemsRequired[self.player].value)
final_xemnas_location.access_rule = lambda state: state.has(ItemName.Bounty, self.player, self.multiworld.BountyRequired[self.player].value) and \
state.has(ItemName.LuckyEmblem, self.player, self.multiworld.LuckyEmblemsRequired[self.player].value)
if self.multiworld.FinalXemnas[self.player]:
self.multiworld.completion_condition[self.player] = lambda state: state.has(ItemName.Victory, self.player, 1)
else:
Expand Down
2 changes: 1 addition & 1 deletion worlds/shivers/Rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def get_rules_lookup(player: int):
"Puzzle Solved Underground Elevator": lambda state: ((state.can_reach("Underground Lake", "Region", player) or state.can_reach("Office", "Region", player)
and state.has("Key for Office Elevator", player))),
"Puzzle Solved Bedroom Elevator": lambda state: (state.can_reach("Office", "Region", player) and state.has_all({"Key for Bedroom Elevator","Crawling"}, player)),
"Puzzle Solved Three Floor Elevator": lambda state: ((state.can_reach("Maintenance Tunnels", "Region", player) or state.can_reach("Blue Maze", "Region", player)
"Puzzle Solved Three Floor Elevator": lambda state: (((state.can_reach("Maintenance Tunnels", "Region", player) or state.can_reach("Blue Maze", "Region", player))
and state.has("Key for Three Floor Elevator", player)))
},
"lightning": {
Expand Down
22 changes: 11 additions & 11 deletions worlds/smz3/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,22 @@ async def game_watcher(self, ctx):
ctx.finished_game = True
return

data = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x680, 4)
data = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0xD3C, 4)
if data is None:
return

recv_index = data[0] | (data[1] << 8)
recv_item = data[2] | (data[3] << 8)

while (recv_index < recv_item):
item_address = recv_index * 8
message = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x700 + item_address, 8)
is_z3_item = ((message[5] & 0x80) != 0)
masked_part = (message[5] & 0x7F) if is_z3_item else message[5]
item_index = ((message[4] | (masked_part << 8)) >> 3) + (256 if is_z3_item else 0)
item_address = recv_index * 2
message = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0xDA0 + item_address, 2)
is_z3_item = ((message[1] & 0x80) != 0)
masked_part = (message[1] & 0x7F) if is_z3_item else message[1]
item_index = ((message[0] | (masked_part << 8)) >> 3) + (256 if is_z3_item else 0)

recv_index += 1
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x680, bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF]))
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + 0xD3C, bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF]))

from .TotalSMZ3.Location import locations_start_id
from . import convertLocSMZ3IDToAPID
Expand All @@ -95,7 +95,7 @@ async def game_watcher(self, ctx):
snes_logger.info(f'New Check: {location} ({len(ctx.locations_checked)}/{len(ctx.missing_locations) + len(ctx.checked_locations)})')
await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": [location_id]}])

data = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x600, 4)
data = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0xD36, 4)
if data is None:
return

Expand All @@ -106,10 +106,10 @@ async def game_watcher(self, ctx):
item = ctx.items_received[item_out_ptr]
item_id = item.item - items_start_id

player_id = item.player if item.player <= SMZ3_ROM_PLAYER_LIMIT else 0
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + item_out_ptr * 4, bytes([player_id & 0xFF, (player_id >> 8) & 0xFF, item_id & 0xFF, (item_id >> 8) & 0xFF]))
player_id = item.player if item.player < SMZ3_ROM_PLAYER_LIMIT else 0
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + item_out_ptr * 2, bytes([player_id, item_id]))
item_out_ptr += 1
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x602, bytes([item_out_ptr & 0xFF, (item_out_ptr >> 8) & 0xFF]))
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + 0xD38, bytes([item_out_ptr & 0xFF, (item_out_ptr >> 8) & 0xFF]))
logging.info('Received %s from %s (%s) (%d/%d in list)' % (
color(ctx.item_names[item.item], 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'),
ctx.location_names[item.location], item_out_ptr, len(ctx.items_received)))
Expand Down
3 changes: 2 additions & 1 deletion worlds/smz3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class SMZ3World(World):
locationNamesGT: Set[str] = {loc.Name for loc in GanonsTower(None, None).Locations}

# first added for 0.2.6
required_client_version = (0, 2, 6)
# optimized message queues for 0.4.4
required_client_version = (0, 4, 4)

def __init__(self, world: MultiWorld, player: int):
self.rom_name_available_event = threading.Event()
Expand Down
Binary file modified worlds/smz3/data/zsm.ips
Binary file not shown.
Loading

0 comments on commit 185f5b5

Please sign in to comment.