KDL3: RC1 Fixes and Enhancement (#3022)
* fix cloudy park 4 rule, zero deathlink message * remove redundant door_shuffle bool when generic ER gets in, this whole function gets rewritten. So just clean it a little now. * properly fix deathlink messages, fix fill error * update docs
This commit is contained in:
parent
77311719fa
commit
80d7ac4164
|
@ -36,8 +36,10 @@ KDL3_STARS_FLAG = SRAM_1_START + 0x901A
|
|||
KDL3_GIFTING_FLAG = SRAM_1_START + 0x901C
|
||||
KDL3_LEVEL_ADDR = SRAM_1_START + 0x9020
|
||||
KDL3_IS_DEMO = SRAM_1_START + 0x5AD5
|
||||
KDL3_GAME_STATE = SRAM_1_START + 0x36D0
|
||||
KDL3_GAME_SAVE = SRAM_1_START + 0x3617
|
||||
KDL3_CURRENT_WORLD = SRAM_1_START + 0x363F
|
||||
KDL3_CURRENT_LEVEL = SRAM_1_START + 0x3641
|
||||
KDL3_GAME_STATE = SRAM_1_START + 0x36D0
|
||||
KDL3_LIFE_COUNT = SRAM_1_START + 0x39CF
|
||||
KDL3_KIRBY_HP = SRAM_1_START + 0x39D1
|
||||
KDL3_BOSS_HP = SRAM_1_START + 0x39D5
|
||||
|
@ -46,8 +48,6 @@ KDL3_LIFE_VISUAL = SRAM_1_START + 0x39E3
|
|||
KDL3_HEART_STARS = SRAM_1_START + 0x53A7
|
||||
KDL3_WORLD_UNLOCK = SRAM_1_START + 0x53CB
|
||||
KDL3_LEVEL_UNLOCK = SRAM_1_START + 0x53CD
|
||||
KDL3_CURRENT_WORLD = SRAM_1_START + 0x53CF
|
||||
KDL3_CURRENT_LEVEL = SRAM_1_START + 0x53D3
|
||||
KDL3_BOSS_STATUS = SRAM_1_START + 0x53D5
|
||||
KDL3_INVINCIBILITY_TIMER = SRAM_1_START + 0x54B1
|
||||
KDL3_MG5_STATUS = SRAM_1_START + 0x5EE4
|
||||
|
@ -74,7 +74,9 @@ deathlink_messages = defaultdict(lambda: " was defeated.", {
|
|||
0x0202: " was out-numbered by Pon & Con.",
|
||||
0x0203: " was defeated by Ado's powerful paintings.",
|
||||
0x0204: " was clobbered by King Dedede.",
|
||||
0x0205: " lost their battle against Dark Matter."
|
||||
0x0205: " lost their battle against Dark Matter.",
|
||||
0x0300: " couldn't overcome the Boss Butch.",
|
||||
0x0400: " is bad at jumping.",
|
||||
})
|
||||
|
||||
|
||||
|
@ -281,6 +283,11 @@ class KDL3SNIClient(SNIClient):
|
|||
for i in range(5):
|
||||
level_data = await snes_read(ctx, KDL3_LEVEL_ADDR + (14 * i), 14)
|
||||
self.levels[i] = unpack("HHHHHHH", level_data)
|
||||
self.levels[5] = [0x0205, # Hyper Zone
|
||||
0, # MG-5, can't send from here
|
||||
0x0300, # Boss Butch
|
||||
0x0400, # Jumping
|
||||
0, 0, 0]
|
||||
|
||||
if self.consumables is None:
|
||||
consumables = await snes_read(ctx, KDL3_CONSUMABLE_FLAG, 1)
|
||||
|
@ -314,7 +321,7 @@ class KDL3SNIClient(SNIClient):
|
|||
current_world = struct.unpack("H", await snes_read(ctx, KDL3_CURRENT_WORLD, 2))[0]
|
||||
current_level = struct.unpack("H", await snes_read(ctx, KDL3_CURRENT_LEVEL, 2))[0]
|
||||
currently_dead = current_hp[0] == 0x00
|
||||
message = deathlink_messages[self.levels[current_world][current_level - 1]]
|
||||
message = deathlink_messages[self.levels[current_world][current_level]]
|
||||
await ctx.handle_deathlink_state(currently_dead, f"{ctx.player_names[ctx.slot]}{message}")
|
||||
|
||||
recv_count = await snes_read(ctx, KDL3_RECV_COUNT, 2)
|
||||
|
|
|
@ -28,16 +28,30 @@ first_stage_blacklist = {
|
|||
0x77001C, # 5-4 needs Burning
|
||||
}
|
||||
|
||||
first_world_limit = {
|
||||
# We need to limit the number of very restrictive stages in level 1 on solo gens
|
||||
*first_stage_blacklist, # all three of the blacklist stages need 2+ items for both checks
|
||||
0x770007,
|
||||
0x770008,
|
||||
0x770013,
|
||||
0x77001E,
|
||||
|
||||
def generate_valid_level(level, stage, possible_stages, slot_random):
|
||||
new_stage = slot_random.choice(possible_stages)
|
||||
if level == 1 and stage == 0 and new_stage in first_stage_blacklist:
|
||||
return generate_valid_level(level, stage, possible_stages, slot_random)
|
||||
else:
|
||||
}
|
||||
|
||||
|
||||
def generate_valid_level(world: "KDL3World", level, stage, possible_stages, placed_stages):
|
||||
new_stage = world.random.choice(possible_stages)
|
||||
if level == 1:
|
||||
if stage == 0 and new_stage in first_stage_blacklist:
|
||||
return generate_valid_level(world, level, stage, possible_stages, placed_stages)
|
||||
elif not (world.multiworld.players > 1 or world.options.consumables or world.options.starsanity) and \
|
||||
new_stage in first_world_limit and \
|
||||
sum(p_stage in first_world_limit for p_stage in placed_stages) >= 2:
|
||||
return generate_valid_level(world, level, stage, possible_stages, placed_stages)
|
||||
return new_stage
|
||||
|
||||
|
||||
def generate_rooms(world: "KDL3World", door_shuffle: bool, level_regions: typing.Dict[int, Region]):
|
||||
def generate_rooms(world: "KDL3World", level_regions: typing.Dict[int, Region]):
|
||||
level_names = {LocationName.level_names[level]: level for level in LocationName.level_names}
|
||||
room_data = orjson.loads(get_data(__name__, os.path.join("data", "Rooms.json")))
|
||||
rooms: typing.Dict[str, KDL3Room] = dict()
|
||||
|
@ -62,14 +76,6 @@ def generate_rooms(world: "KDL3World", door_shuffle: bool, level_regions: typing
|
|||
world.multiworld.regions.extend(world.rooms)
|
||||
|
||||
first_rooms: typing.Dict[int, KDL3Room] = dict()
|
||||
if door_shuffle:
|
||||
# first, we need to generate the notable edge cases
|
||||
# 5-6 is the first, being the most restrictive
|
||||
# half of its rooms are required to be vanilla, but can be in different orders
|
||||
# the room before it *must* contain the copy ability required to unlock the room's goal
|
||||
|
||||
raise NotImplementedError()
|
||||
else:
|
||||
for name, room in rooms.items():
|
||||
if room.room == 0:
|
||||
if room.stage == 7:
|
||||
|
@ -102,7 +108,7 @@ def generate_rooms(world: "KDL3World", door_shuffle: bool, level_regions: typing
|
|||
if world.options.open_world or stage == 0:
|
||||
level_regions[level].add_exits([first_rooms[proper_stage].name])
|
||||
else:
|
||||
world.multiworld.get_location(world.location_id_to_name[world.player_levels[level][stage-1]],
|
||||
world.multiworld.get_location(world.location_id_to_name[world.player_levels[level][stage - 1]],
|
||||
world.player).parent_region.add_exits([first_rooms[proper_stage].name])
|
||||
level_regions[level].add_exits([first_rooms[0x770200 + level - 1].name])
|
||||
|
||||
|
@ -141,8 +147,7 @@ def generate_valid_levels(world: "KDL3World", enforce_world: bool, enforce_patte
|
|||
or (enforce_pattern and ((candidate - 1) & 0x00FFFF) % 6 == stage)
|
||||
or (enforce_pattern == enforce_world)
|
||||
]
|
||||
new_stage = generate_valid_level(level, stage, stage_candidates,
|
||||
world.random)
|
||||
new_stage = generate_valid_level(world, level, stage, stage_candidates, levels[level])
|
||||
possible_stages.remove(new_stage)
|
||||
levels[level][stage] = new_stage
|
||||
except Exception:
|
||||
|
@ -218,7 +223,7 @@ def create_levels(world: "KDL3World") -> None:
|
|||
level_shuffle == 1,
|
||||
level_shuffle == 2)
|
||||
|
||||
generate_rooms(world, False, levels)
|
||||
generate_rooms(world, levels)
|
||||
|
||||
level6.add_locations({LocationName.goals[world.options.goal]: None}, KDL3Location)
|
||||
|
||||
|
|
|
@ -264,7 +264,7 @@ def set_rules(world: "KDL3World") -> None:
|
|||
for r in [range(1, 31), range(44, 51)]:
|
||||
for i in r:
|
||||
set_rule(world.multiworld.get_location(f"Cloudy Park 4 - Star {i}", world.player),
|
||||
lambda state: can_reach_clean(state, world.player))
|
||||
lambda state: can_reach_coo(state, world.player))
|
||||
for i in [18, *list(range(20, 25))]:
|
||||
set_rule(world.multiworld.get_location(f"Cloudy Park 6 - Star {i}", world.player),
|
||||
lambda state: can_reach_ice(state, world.player))
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Kirby's Dream Land 3
|
||||
|
||||
## Where is the settings page?
|
||||
## Where is the options page?
|
||||
|
||||
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
|
||||
The [player options page for this game](../player-options) contains all the options you need to configure and export a
|
||||
config file.
|
||||
|
||||
## What does randomization do to this game?
|
||||
|
@ -15,6 +15,7 @@ as Heart Stars, 1-Ups, and Invincibility Candy will be shuffled into the pool fo
|
|||
- Purifying a boss after acquiring a certain number of Heart Stars
|
||||
(indicated by their portrait flashing in the level select)
|
||||
- If enabled, 1-Ups and Maxim Tomatoes
|
||||
- If enabled, every single Star Piece within a stage
|
||||
|
||||
## When the player receives an item, what happens?
|
||||
A sound effect will play, and Kirby will immediately receive the effects of that item, such as being able to receive Copy Abilities from enemies that
|
||||
|
|
|
@ -43,8 +43,8 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
|
|||
|
||||
### Where do I get a config file?
|
||||
|
||||
The [Player Settings](/games/Kirby's%20Dream%20Land%203/player-settings) page on the website allows you to configure
|
||||
your personal settings and export a config file from them.
|
||||
The [Player Options](/games/Kirby's%20Dream%20Land%203/player-options) page on the website allows you to configure
|
||||
your personal options and export a config file from them.
|
||||
|
||||
### Verifying your config file
|
||||
|
||||
|
@ -53,7 +53,7 @@ If you would like to validate your config file to make sure it works, you may do
|
|||
|
||||
## Generating a Single-Player Game
|
||||
|
||||
1. Navigate to the [Player Settings](/games/Kirby's%20Dream%20Land%203/player-settings) page, configure your options,
|
||||
1. Navigate to the [Player Options](/games/Kirby's%20Dream%20Land%203/player-options) page, configure your options,
|
||||
and click the "Generate Game" button.
|
||||
2. You will be presented with a "Seed Info" page.
|
||||
3. Click the "Create New Room" link.
|
||||
|
|
|
@ -33,7 +33,8 @@ class TestLocations(KDL3TestBase):
|
|||
self.run_location_test(LocationName.iceberg_kogoesou, ["Burning"])
|
||||
self.run_location_test(LocationName.iceberg_samus, ["Ice"])
|
||||
self.run_location_test(LocationName.iceberg_name, ["Burning", "Coo", "ChuChu"])
|
||||
self.run_location_test(LocationName.iceberg_angel, ["Cutter", "Burning", "Spark", "Parasol", "Needle", "Clean", "Stone", "Ice"])
|
||||
self.run_location_test(LocationName.iceberg_angel, ["Cutter", "Burning", "Spark", "Parasol", "Needle", "Clean",
|
||||
"Stone", "Ice"])
|
||||
|
||||
def run_location_test(self, location: str, itempool: typing.List[str]):
|
||||
items = itempool.copy()
|
||||
|
|
Loading…
Reference in New Issue