mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00
SMW: v2.1 Feature Update (#4652)
### Features: - Trap Link - When you receive a trap, you send a copy of it to every other player with Trap Link enabled - Ring Link - Any coin amounts gained and lost by a linked player will be instantly shared with all other active linked players Co-authored-by: TheLX5 <luisyuregi@gmail.com>
This commit is contained in:
@@ -1,6 +1,15 @@
|
||||
# Super Mario World - Changelog
|
||||
|
||||
|
||||
## v2.1
|
||||
|
||||
### Features:
|
||||
- Trap Link
|
||||
- When you receive a trap, you send a copy of it to every other player with Trap Link enabled
|
||||
- Ring Link
|
||||
- Any coin amounts gained and lost by a linked player will be instantly shared with all other active linked players
|
||||
|
||||
|
||||
## v2.0
|
||||
|
||||
### Features:
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import logging
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from NetUtils import ClientStatus, color
|
||||
from NetUtils import ClientStatus, NetworkItem, color
|
||||
from worlds.AutoSNIClient import SNIClient
|
||||
from .Names.TextBox import generate_received_text
|
||||
from .Names.TextBox import generate_received_text, generate_received_trap_link_text
|
||||
from .Items import trap_value_to_name, trap_name_to_value
|
||||
|
||||
snes_logger = logging.getLogger("SNES")
|
||||
|
||||
@@ -42,10 +44,13 @@ SMW_MOON_ACTIVE_ADDR = ROM_START + 0x01BFA8
|
||||
SMW_HIDDEN_1UP_ACTIVE_ADDR = ROM_START + 0x01BFA9
|
||||
SMW_BONUS_BLOCK_ACTIVE_ADDR = ROM_START + 0x01BFAA
|
||||
SMW_BLOCKSANITY_ACTIVE_ADDR = ROM_START + 0x01BFAB
|
||||
SMW_TRAP_LINK_ACTIVE_ADDR = ROM_START + 0x01BFB7
|
||||
SMW_RING_LINK_ACTIVE_ADDR = ROM_START + 0x01BFB8
|
||||
|
||||
|
||||
SMW_GAME_STATE_ADDR = WRAM_START + 0x100
|
||||
SMW_MARIO_STATE_ADDR = WRAM_START + 0x71
|
||||
SMW_COIN_COUNT_ADDR = WRAM_START + 0xDBF
|
||||
SMW_BOSS_STATE_ADDR = WRAM_START + 0xD9B
|
||||
SMW_ACTIVE_BOSS_ADDR = WRAM_START + 0x13FC
|
||||
SMW_CURRENT_LEVEL_ADDR = WRAM_START + 0x13BF
|
||||
@@ -76,6 +81,7 @@ SMW_UNCOLLECTABLE_DRAGON_COINS = [0x24]
|
||||
class SMWSNIClient(SNIClient):
|
||||
game = "Super Mario World"
|
||||
patch_suffix = ".apsmw"
|
||||
slot_data: dict[str, Any] | None
|
||||
|
||||
async def deathlink_kill_player(self, ctx):
|
||||
from SNIClient import DeathState, snes_buffered_write, snes_flush_writes, snes_read
|
||||
@@ -111,6 +117,84 @@ class SMWSNIClient(SNIClient):
|
||||
ctx.last_death_link = time.time()
|
||||
|
||||
|
||||
def on_package(self, ctx: SNIClient, cmd: str, args: dict[str, Any]) -> None:
|
||||
super().on_package(ctx, cmd, args)
|
||||
|
||||
if cmd == "Connected":
|
||||
self.slot_data = args.get("slot_data", None)
|
||||
|
||||
if cmd != "Bounced":
|
||||
return
|
||||
if "tags" not in args:
|
||||
return
|
||||
|
||||
if not hasattr(self, "instance_id"):
|
||||
self.instance_id = time.time()
|
||||
|
||||
source_name = args["data"]["source"]
|
||||
if "TrapLink" in ctx.tags and "TrapLink" in args["tags"] and source_name != ctx.slot_info[ctx.slot].name:
|
||||
trap_name: str = args["data"]["trap_name"]
|
||||
if trap_name not in trap_name_to_value:
|
||||
# We don't know how to handle this trap, ignore it
|
||||
return
|
||||
|
||||
trap_id: int = trap_name_to_value[trap_name]
|
||||
|
||||
if "trap_weights" not in self.slot_data:
|
||||
return
|
||||
|
||||
if f"{trap_id}" not in self.slot_data["trap_weights"]:
|
||||
return
|
||||
|
||||
if self.slot_data["trap_weights"][f"{trap_id}"] == 0:
|
||||
# The player disabled this trap type
|
||||
return
|
||||
|
||||
self.priority_trap = NetworkItem(trap_id, None, None)
|
||||
self.priority_trap_message = generate_received_trap_link_text(trap_name, source_name)
|
||||
self.priority_trap_message_str = f"Received linked {trap_name} from {source_name}"
|
||||
elif "RingLink" in ctx.tags and "RingLink" in args["tags"] and source_name != self.instance_id:
|
||||
if not hasattr(self, "pending_ring_link"):
|
||||
self.pending_ring_link = 0
|
||||
self.pending_ring_link += args["data"]["amount"]
|
||||
|
||||
async def send_trap_link(self, ctx: SNIClient, trap_name: str):
|
||||
if "TrapLink" not in ctx.tags or ctx.slot == None:
|
||||
return
|
||||
|
||||
await ctx.send_msgs([{
|
||||
"cmd": "Bounce", "tags": ["TrapLink"],
|
||||
"data": {
|
||||
"time": time.time(),
|
||||
"source": ctx.player_names[ctx.slot],
|
||||
"trap_name": trap_name
|
||||
}
|
||||
}])
|
||||
snes_logger.info(f"Sent linked {trap_name}")
|
||||
|
||||
async def send_ring_link(self, ctx: SNIClient, amount: int):
|
||||
from SNIClient import DeathState, snes_buffered_write, snes_flush_writes, snes_read
|
||||
|
||||
if "RingLink" not in ctx.tags or ctx.slot == None:
|
||||
return
|
||||
|
||||
game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1)
|
||||
if game_state[0] != 0x14:
|
||||
return
|
||||
|
||||
if not hasattr(self, "instance_id"):
|
||||
self.instance_id = time.time()
|
||||
|
||||
await ctx.send_msgs([{
|
||||
"cmd": "Bounce", "tags": ["RingLink"],
|
||||
"data": {
|
||||
"time": time.time(),
|
||||
"source": self.instance_id,
|
||||
"amount": amount
|
||||
}
|
||||
}])
|
||||
|
||||
|
||||
async def validate_rom(self, ctx):
|
||||
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
|
||||
|
||||
@@ -123,9 +207,11 @@ class SMWSNIClient(SNIClient):
|
||||
|
||||
receive_option = await snes_read(ctx, SMW_RECEIVE_MSG_DATA, 0x1)
|
||||
send_option = await snes_read(ctx, SMW_SEND_MSG_DATA, 0x1)
|
||||
trap_link = await snes_read(ctx, SMW_TRAP_LINK_ACTIVE_ADDR, 0x1)
|
||||
|
||||
ctx.receive_option = receive_option[0]
|
||||
ctx.send_option = send_option[0]
|
||||
ctx.trap_link = trap_link[0]
|
||||
|
||||
ctx.allow_collect = True
|
||||
|
||||
@@ -133,6 +219,15 @@ class SMWSNIClient(SNIClient):
|
||||
if death_link:
|
||||
await ctx.update_death_link(bool(death_link[0] & 0b1))
|
||||
|
||||
if trap_link and bool(trap_link[0] & 0b1) and "TrapLink" not in ctx.tags:
|
||||
ctx.tags.add("TrapLink")
|
||||
await ctx.send_msgs([{"cmd": "ConnectUpdate", "tags": ctx.tags}])
|
||||
|
||||
ring_link = await snes_read(ctx, SMW_RING_LINK_ACTIVE_ADDR, 1)
|
||||
if ring_link and bool(ring_link[0] & 0b1) and "RingLink" not in ctx.tags:
|
||||
ctx.tags.add("RingLink")
|
||||
await ctx.send_msgs([{"cmd": "ConnectUpdate", "tags": ctx.tags}])
|
||||
|
||||
if ctx.rom != rom_name:
|
||||
ctx.current_sublevel_value = 0
|
||||
|
||||
@@ -142,12 +237,17 @@ class SMWSNIClient(SNIClient):
|
||||
|
||||
|
||||
def add_message_to_queue(self, new_message):
|
||||
|
||||
if not hasattr(self, "message_queue"):
|
||||
self.message_queue = []
|
||||
|
||||
self.message_queue.append(new_message)
|
||||
|
||||
def add_message_to_queue_front(self, new_message):
|
||||
if not hasattr(self, "message_queue"):
|
||||
self.message_queue = []
|
||||
|
||||
self.message_queue.insert(0, new_message)
|
||||
|
||||
|
||||
async def handle_message_queue(self, ctx):
|
||||
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
|
||||
@@ -206,7 +306,8 @@ class SMWSNIClient(SNIClient):
|
||||
async def handle_trap_queue(self, ctx):
|
||||
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
|
||||
|
||||
if not hasattr(self, "trap_queue") or len(self.trap_queue) == 0:
|
||||
if (not hasattr(self, "trap_queue") or len(self.trap_queue) == 0) and\
|
||||
(not hasattr(self, "priority_trap") or self.priority_trap == 0):
|
||||
return
|
||||
|
||||
game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1)
|
||||
@@ -221,7 +322,24 @@ class SMWSNIClient(SNIClient):
|
||||
if pause_state[0] != 0x00:
|
||||
return
|
||||
|
||||
next_trap, message = self.trap_queue.pop(0)
|
||||
|
||||
next_trap = None
|
||||
message = bytearray()
|
||||
message_str = ""
|
||||
from_queue = False
|
||||
|
||||
if getattr(self, "priority_trap", None) and self.priority_trap.item != 0:
|
||||
next_trap = self.priority_trap
|
||||
message = self.priority_trap_message
|
||||
message_str = self.priority_trap_message_str
|
||||
self.priority_trap = None
|
||||
self.priority_trap_message = bytearray()
|
||||
self.priority_trap_message_str = ""
|
||||
elif hasattr(self, "trap_queue") and len(self.trap_queue) > 0:
|
||||
from_queue = True
|
||||
next_trap, message = self.trap_queue.pop(0)
|
||||
else:
|
||||
return
|
||||
|
||||
from .Rom import trap_rom_data
|
||||
if next_trap.item in trap_rom_data:
|
||||
@@ -231,16 +349,22 @@ class SMWSNIClient(SNIClient):
|
||||
# Timer Trap
|
||||
if trap_active[0] == 0 or (trap_active[0] == 1 and trap_active[1] == 0 and trap_active[2] == 0):
|
||||
# Trap already active
|
||||
self.add_trap_to_queue(next_trap, message)
|
||||
if from_queue:
|
||||
self.add_trap_to_queue(next_trap, message)
|
||||
return
|
||||
else:
|
||||
if len(message_str) > 0:
|
||||
snes_logger.info(message_str)
|
||||
if "TrapLink" in ctx.tags and from_queue:
|
||||
await self.send_trap_link(ctx, trap_value_to_name[next_trap.item])
|
||||
snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0], bytes([0x01]))
|
||||
snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0] + 1, bytes([0x00]))
|
||||
snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0] + 2, bytes([0x00]))
|
||||
else:
|
||||
if trap_active[0] > 0:
|
||||
# Trap already active
|
||||
self.add_trap_to_queue(next_trap, message)
|
||||
if from_queue:
|
||||
self.add_trap_to_queue(next_trap, message)
|
||||
return
|
||||
else:
|
||||
if next_trap.item == 0xBC001D:
|
||||
@@ -248,12 +372,18 @@ class SMWSNIClient(SNIClient):
|
||||
# Do not fire if the previous thwimp hasn't reached the player's Y pos
|
||||
active_thwimp = await snes_read(ctx, SMW_ACTIVE_THWIMP_ADDR, 0x1)
|
||||
if active_thwimp[0] != 0xFF:
|
||||
self.add_trap_to_queue(next_trap, message)
|
||||
if from_queue:
|
||||
self.add_trap_to_queue(next_trap, message)
|
||||
return
|
||||
verify_game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1)
|
||||
if verify_game_state[0] == 0x14 and len(trap_rom_data[next_trap.item]) > 2:
|
||||
snes_buffered_write(ctx, SMW_SFX_ADDR, bytes([trap_rom_data[next_trap.item][2]]))
|
||||
|
||||
if len(message_str) > 0:
|
||||
snes_logger.info(message_str)
|
||||
if "TrapLink" in ctx.tags and from_queue:
|
||||
await self.send_trap_link(ctx, trap_value_to_name[next_trap.item])
|
||||
|
||||
new_item_count = trap_rom_data[next_trap.item][1]
|
||||
snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0], bytes([new_item_count]))
|
||||
|
||||
@@ -270,9 +400,75 @@ class SMWSNIClient(SNIClient):
|
||||
return
|
||||
|
||||
if self.should_show_message(ctx, next_trap):
|
||||
self.add_message_to_queue_front(message)
|
||||
elif next_trap.item == 0xBC0015:
|
||||
if self.should_show_message(ctx, next_trap):
|
||||
self.add_message_to_queue_front(message)
|
||||
if len(message_str) > 0:
|
||||
snes_logger.info(message_str)
|
||||
if "TrapLink" in ctx.tags and from_queue:
|
||||
await self.send_trap_link(ctx, trap_value_to_name[next_trap.item])
|
||||
|
||||
# Handle Literature Trap
|
||||
from .Names.LiteratureTrap import lit_trap_text_list
|
||||
import random
|
||||
rand_trap = random.choice(lit_trap_text_list)
|
||||
|
||||
for message in rand_trap:
|
||||
self.add_message_to_queue(message)
|
||||
|
||||
|
||||
async def handle_ring_link(self, ctx):
|
||||
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
|
||||
|
||||
if "RingLink" not in ctx.tags:
|
||||
return
|
||||
|
||||
if not hasattr(self, "prev_coins"):
|
||||
self.prev_coins = 0
|
||||
|
||||
curr_coins_byte = await snes_read(ctx, SMW_COIN_COUNT_ADDR, 0x1)
|
||||
curr_coins = curr_coins_byte[0]
|
||||
|
||||
if curr_coins < self.prev_coins:
|
||||
# Coins rolled over from 1-Up
|
||||
curr_coins += 100
|
||||
|
||||
coins_diff = curr_coins - self.prev_coins
|
||||
if coins_diff > 0:
|
||||
await self.send_ring_link(ctx, coins_diff)
|
||||
self.prev_coins = curr_coins % 100
|
||||
|
||||
new_coins = curr_coins
|
||||
if not hasattr(self, "pending_ring_link"):
|
||||
self.pending_ring_link = 0
|
||||
|
||||
if self.pending_ring_link != 0:
|
||||
new_coins += self.pending_ring_link
|
||||
new_coins = max(new_coins, 0)
|
||||
|
||||
new_1_ups = 0
|
||||
while new_coins >= 100:
|
||||
new_1_ups += 1
|
||||
new_coins -= 100
|
||||
|
||||
if new_1_ups > 0:
|
||||
curr_lives_inc_byte = await snes_read(ctx, WRAM_START + 0x18E4, 0x1)
|
||||
curr_lives_inc = curr_lives_inc_byte[0]
|
||||
new_lives_inc = curr_lives_inc + new_1_ups
|
||||
snes_buffered_write(ctx, WRAM_START + 0x18E4, bytes([new_lives_inc]))
|
||||
|
||||
snes_buffered_write(ctx, SMW_COIN_COUNT_ADDR, bytes([new_coins]))
|
||||
if self.pending_ring_link > 0:
|
||||
snes_buffered_write(ctx, SMW_SFX_ADDR, bytes([0x01]))
|
||||
else:
|
||||
snes_buffered_write(ctx, SMW_SFX_ADDR, bytes([0x2A]))
|
||||
self.pending_ring_link = 0
|
||||
self.prev_coins = new_coins
|
||||
|
||||
await snes_flush_writes(ctx)
|
||||
|
||||
|
||||
async def game_watcher(self, ctx):
|
||||
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
|
||||
|
||||
@@ -333,6 +529,7 @@ class SMWSNIClient(SNIClient):
|
||||
|
||||
await self.handle_message_queue(ctx)
|
||||
await self.handle_trap_queue(ctx)
|
||||
await self.handle_ring_link(ctx)
|
||||
|
||||
new_checks = []
|
||||
event_data = await snes_read(ctx, SMW_EVENT_ROM_DATA, 0x60)
|
||||
@@ -506,7 +703,7 @@ class SMWSNIClient(SNIClient):
|
||||
ctx.location_names.lookup_in_slot(item.location, item.player), recv_index, len(ctx.items_received)))
|
||||
|
||||
if self.should_show_message(ctx, item):
|
||||
if item.item != 0xBC0012 and item.item not in trap_rom_data:
|
||||
if item.item != 0xBC0012 and item.item != 0xBC0015 and item.item not in trap_rom_data:
|
||||
# Don't send messages for Boss Tokens
|
||||
item_name = ctx.item_names.lookup_in_game(item.item)
|
||||
player_name = ctx.player_names[item.player]
|
||||
@@ -515,7 +712,7 @@ class SMWSNIClient(SNIClient):
|
||||
self.add_message_to_queue(receive_message)
|
||||
|
||||
snes_buffered_write(ctx, SMW_RECV_PROGRESS_ADDR, bytes([recv_index&0xFF, (recv_index>>8)&0xFF]))
|
||||
if item.item in trap_rom_data:
|
||||
if item.item in trap_rom_data or item.item == 0xBC0015:
|
||||
item_name = ctx.item_names.lookup_in_game(item.item)
|
||||
player_name = ctx.player_names[item.player]
|
||||
|
||||
@@ -572,14 +769,6 @@ class SMWSNIClient(SNIClient):
|
||||
else:
|
||||
# Extra Powerup?
|
||||
pass
|
||||
elif item.item == 0xBC0015:
|
||||
# Handle Literature Trap
|
||||
from .Names.LiteratureTrap import lit_trap_text_list
|
||||
import random
|
||||
rand_trap = random.choice(lit_trap_text_list)
|
||||
|
||||
for message in rand_trap:
|
||||
self.add_message_to_queue(message)
|
||||
|
||||
await snes_flush_writes(ctx)
|
||||
|
||||
|
@@ -75,3 +75,49 @@ item_table = {
|
||||
}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code}
|
||||
|
||||
|
||||
trap_value_to_name: typing.Dict[int, str] = {
|
||||
0xBC0013: ItemName.ice_trap,
|
||||
0xBC0014: ItemName.stun_trap,
|
||||
0xBC0015: ItemName.literature_trap,
|
||||
0xBC0016: ItemName.timer_trap,
|
||||
0xBC001C: ItemName.reverse_controls_trap,
|
||||
0xBC001D: ItemName.thwimp_trap,
|
||||
}
|
||||
|
||||
trap_name_to_value: typing.Dict[str, int] = {
|
||||
# Our native Traps
|
||||
ItemName.ice_trap: 0xBC0013,
|
||||
ItemName.stun_trap: 0xBC0014,
|
||||
ItemName.literature_trap: 0xBC0015,
|
||||
ItemName.timer_trap: 0xBC0016,
|
||||
ItemName.reverse_controls_trap: 0xBC001C,
|
||||
ItemName.thwimp_trap: 0xBC001D,
|
||||
|
||||
# Common other trap names
|
||||
"Chaos Control Trap": 0xBC0014, # Stun Trap
|
||||
"Confuse Trap": 0xBC001C, # Reverse Trap
|
||||
"Exposition Trap": 0xBC0015, # Literature Trap
|
||||
"Cutscene Trap": 0xBC0015, # Literature Trap
|
||||
"Freeze Trap": 0xBC0014, # Stun Trap
|
||||
"Frozen Trap": 0xBC0014, # Stun Trap
|
||||
"Paralyze Trap": 0xBC0014, # Stun Trap
|
||||
"Reversal Trap": 0xBC001C, # Reverse Trap
|
||||
"Fuzzy Trap": 0xBC001C, # Reverse Trap
|
||||
"Confound Trap": 0xBC001C, # Reverse Trap
|
||||
"Confusion Trap": 0xBC001C, # Reverse Trap
|
||||
"Police Trap": 0xBC001D, # Thwimp Trap
|
||||
"Buyon Trap": 0xBC001D, # Thwimp Trap
|
||||
"Gooey Bag": 0xBC001D, # Thwimp Trap
|
||||
"TNT Barrel Trap": 0xBC001D, # Thwimp Trap
|
||||
"Honey Trap": 0xBC0014, # Stun Trap
|
||||
"Screen Flip Trap": 0xBC001C, # Reverse Trap
|
||||
"Banana Trap": 0xBC0013, # Ice Trap
|
||||
"Bomb": 0xBC001D, # Thwimp Trap
|
||||
"Bonk Trap": 0xBC0014, # Stun Trap
|
||||
"Ghost": 0xBC001D, # Thwimp Trap
|
||||
"Fast Trap": 0xBC0016, # Timer Trap
|
||||
"Nut Trap": 0xBC001D, # Thwimp Trap
|
||||
"Army Trap": 0xBC001D, # Thwimp Trap
|
||||
}
|
||||
|
@@ -117,6 +117,31 @@ def generate_received_text(item_name: str, player_name: str):
|
||||
return out_array
|
||||
|
||||
|
||||
def generate_received_trap_link_text(item_name: str, player_name: str):
|
||||
out_array = bytearray()
|
||||
|
||||
item_name = item_name[:18]
|
||||
player_name = player_name[:18]
|
||||
|
||||
item_buffer = max(0, math.floor((18 - len(item_name)) / 2))
|
||||
player_buffer = max(0, math.floor((18 - len(player_name)) / 2))
|
||||
|
||||
out_array += bytearray([0x9F, 0x9F])
|
||||
out_array += string_to_bytes(" Received linked")
|
||||
out_array[-1] += 0x80
|
||||
out_array += bytearray([0x1F] * item_buffer)
|
||||
out_array += string_to_bytes(item_name)
|
||||
out_array[-1] += 0x80
|
||||
out_array += string_to_bytes(" from")
|
||||
out_array[-1] += 0x80
|
||||
out_array += bytearray([0x1F] * player_buffer)
|
||||
out_array += string_to_bytes(player_name)
|
||||
out_array[-1] += 0x80
|
||||
out_array += bytearray([0x9F, 0x9F])
|
||||
|
||||
return out_array
|
||||
|
||||
|
||||
def generate_sent_text(item_name: str, player_name: str):
|
||||
out_array = bytearray()
|
||||
|
||||
|
@@ -398,6 +398,20 @@ class StartingLifeCount(Range):
|
||||
default = 5
|
||||
|
||||
|
||||
class RingLink(Toggle):
|
||||
"""
|
||||
Whether your in-level coin gain/loss is linked to other players
|
||||
"""
|
||||
display_name = "Ring Link"
|
||||
|
||||
|
||||
class TrapLink(Toggle):
|
||||
"""
|
||||
Whether your received traps are linked to other players
|
||||
"""
|
||||
display_name = "Trap Link"
|
||||
|
||||
|
||||
smw_option_groups = [
|
||||
OptionGroup("Goal Options", [
|
||||
Goal,
|
||||
@@ -447,6 +461,8 @@ smw_option_groups = [
|
||||
@dataclass
|
||||
class SMWOptions(PerGameCommonOptions):
|
||||
death_link: DeathLink
|
||||
ring_link: RingLink
|
||||
trap_link: TrapLink
|
||||
goal: Goal
|
||||
bosses_required: BossesRequired
|
||||
max_yoshi_egg_cap: NumberOfYoshiEggs
|
||||
|
@@ -719,8 +719,8 @@ def handle_vertical_scroll(rom):
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, # Levels 0D0-0DF
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, # Levels 0E0-0EF
|
||||
0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, # Levels 0F0-0FF
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x01, # Levels 100-10F
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 110-11F
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x01, # Levels 100-10F
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, # Levels 110-11F
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 120-12F
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 130-13F
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 140-14F
|
||||
@@ -3160,6 +3160,8 @@ def patch_rom(world: World, rom, player, active_level_dict):
|
||||
rom.write_byte(0x01BFA9, world.options.hidden_1up_checks.value)
|
||||
rom.write_byte(0x01BFAA, world.options.bonus_block_checks.value)
|
||||
rom.write_byte(0x01BFAB, world.options.blocksanity.value)
|
||||
rom.write_byte(0x01BFB7, world.options.trap_link.value)
|
||||
rom.write_byte(0x01BFB8, world.options.ring_link.value)
|
||||
|
||||
|
||||
from Utils import __version__
|
||||
|
@@ -90,6 +90,7 @@ class SMWWorld(World):
|
||||
"blocksanity",
|
||||
)
|
||||
slot_data["active_levels"] = self.active_level_dict
|
||||
slot_data["trap_weights"] = self.output_trap_weights()
|
||||
|
||||
return slot_data
|
||||
|
||||
@@ -322,3 +323,15 @@ class SMWWorld(World):
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self)
|
||||
|
||||
def output_trap_weights(self) -> dict[int, int]:
|
||||
trap_data = {}
|
||||
|
||||
trap_data[0xBC0013] = self.options.ice_trap_weight.value
|
||||
trap_data[0xBC0014] = self.options.stun_trap_weight.value
|
||||
trap_data[0xBC0015] = self.options.literature_trap_weight.value
|
||||
trap_data[0xBC0016] = self.options.timer_trap_weight.value
|
||||
trap_data[0xBC001C] = self.options.reverse_trap_weight.value
|
||||
trap_data[0xBC001D] = self.options.thwimp_trap_weight.value
|
||||
|
||||
return trap_data
|
||||
|
Reference in New Issue
Block a user