MLSS: Add new goal + Update basepatch to standalone equivalent (#4409)

* Item groups + small changes

* Add alternate goal

* New Locations and Logic Updates + Basepatch

* Update basepatch.bsdiff

* Update Basepatch

* Update basepatch.bsdiff

* Update bowsers castle logic with emblem hunt

* Update Archipelago Unittests.run.xml

* Update Archipelago Unittests.run.xml

* Fix for overlapping ROM addresses

* Update Rom.py

* Update __init__.py

* Update basepatch.bsdiff

* Update Rom.py

* Update client with new helper function

* Update basepatch.bsdiff

* Update worlds/mlss/__init__.py

Co-authored-by: qwint <qwint.42@gmail.com>

* Update worlds/mlss/__init__.py

Co-authored-by: qwint <qwint.42@gmail.com>

* Review Refactor

* Review Refactor

---------

Co-authored-by: qwint <qwint.42@gmail.com>
This commit is contained in:
jamesbrq
2025-03-09 11:37:15 -04:00
committed by GitHub
parent 4ebabc1208
commit 2639796255
12 changed files with 234 additions and 39 deletions

View File

@@ -269,7 +269,7 @@ class MLSSClient(BizHawkClient):
self.local_checked_locations = locs_to_send
if locs_to_send is not None:
await ctx.send_msgs([{"cmd": "LocationChecks", "locations": list(locs_to_send)}])
await ctx.check_locations(locs_to_send)
except bizhawk.RequestFailedError:
# Exit handler and return to main loop to reconnect.

View File

@@ -153,7 +153,6 @@ enemies = [
0x50458C,
0x5045AC,
0x50468C,
# 0x5046CC, 6 enemy formation
0x5046EC,
0x50470C
]
@@ -166,6 +165,7 @@ bosses = [
0x50360C,
0x5037AC,
0x5037CC,
0x50396C,
0x503A8C,
0x503D6C,
0x503F0C,

View File

@@ -160,6 +160,7 @@ itemList: typing.List[ItemData] = [
ItemData(77771142, "Game Boy Horror SP", ItemClassification.useful, 0xFE),
ItemData(77771143, "Woo Bean", ItemClassification.skip_balancing, 0x1C),
ItemData(77771144, "Hee Bean", ItemClassification.skip_balancing, 0x1F),
ItemData(77771145, "Beanstar Emblem", ItemClassification.progression, 0x3E),
]
item_frequencies: typing.Dict[str, int] = {
@@ -186,5 +187,12 @@ item_frequencies: typing.Dict[str, int] = {
"Hammers": 3,
}
mlss_item_name_groups = {
"Beanstar Piece": { "Beanstar Piece 1", "Beanstar Piece 2", "Beanstar Piece 3", "Beanstar Piece 4"},
"Beanfruit": { "Bean Fruit 1", "Bean Fruit 2", "Bean Fruit 3", "Bean Fruit 4", "Bean Fruit 5", "Bean Fruit 6", "Bean Fruit 7"},
"Neon Egg": { "Blue Neon Egg", "Red Neon Egg", "Green Neon Egg", "Yellow Neon Egg", "Purple Neon Egg", "Orange Neon Egg", "Azure Neon Egg"},
"Chuckola Fruit": { "Red Chuckola Fruit", "Purple Chuckola Fruit", "White Chuckola Fruit"}
}
item_table: typing.Dict[str, ItemData] = {item.itemName: item for item in itemList}
items_by_id: typing.Dict[int, ItemData] = {item.code: item for item in itemList}

View File

@@ -251,9 +251,9 @@ coins: typing.List[LocationData] = [
LocationData("Hoohoo Village North Cave Room 1 Coin Block", 0x39DAA0, 0),
LocationData("Hoohoo Village South Cave Coin Block 1", 0x39DAC5, 0),
LocationData("Hoohoo Village South Cave Coin Block 2", 0x39DAD5, 0),
LocationData("Hoohoo Mountain Base Boo Statue Cave Coin Block 1", 0x39DAE2, 0),
LocationData("Hoohoo Mountain Base Boo Statue Cave Coin Block 2", 0x39DAF2, 0),
LocationData("Hoohoo Mountain Base Boo Statue Cave Coin Block 3", 0x39DAFA, 0),
LocationData("Hoohoo Mountain Base Boostatue Cave Coin Block 1", 0x39DAE2, 0),
LocationData("Hoohoo Mountain Base Boostatue Cave Coin Block 2", 0x39DAF2, 0),
LocationData("Hoohoo Mountain Base Boostatue Cave Coin Block 3", 0x39DAFA, 0),
LocationData("Beanbean Outskirts NW Coin Block", 0x39DB8F, 0),
LocationData("Beanbean Outskirts S Room 1 Coin Block", 0x39DC18, 0),
LocationData("Beanbean Outskirts S Room 2 Coin Block", 0x39DC3D, 0),
@@ -262,6 +262,8 @@ coins: typing.List[LocationData] = [
LocationData("Chucklehuck Woods Cave Room 1 Coin Block", 0x39DD7A, 0),
LocationData("Chucklehuck Woods Cave Room 2 Coin Block", 0x39DD97, 0),
LocationData("Chucklehuck Woods Cave Room 3 Coin Block", 0x39DDB4, 0),
LocationData("Chucklehuck Woods Solo Luigi Cave Room 1 Coin Block 1", 0x39DB48, 0),
LocationData("Chucklehuck Woods Solo Luigi Cave Room 1 Coin Block 2", 0x39DB50, 0),
LocationData("Chucklehuck Woods Pipe 5 Room Coin Block", 0x39DDE6, 0),
LocationData("Chucklehuck Woods Room 7 Coin Block", 0x39DE31, 0),
LocationData("Chucklehuck Woods Past Chuckleroot Coin Block", 0x39DF14, 0),
@@ -289,6 +291,7 @@ baseUltraRocks: typing.List[LocationData] = [
LocationData("Teehee Valley Upper Maze Room 1 Block", 0x39E5E0, 0),
LocationData("Teehee Valley Upper Maze Room 2 Digspot 1", 0x39E5C8, 0),
LocationData("Teehee Valley Upper Maze Room 2 Digspot 2", 0x39E5D0, 0),
LocationData("Guffawha Ruins Block", 0x39E6A3, 0),
LocationData("Hoohoo Mountain Base Guffawha Ruins Entrance Digspot", 0x39DA0B, 0),
LocationData("Hoohoo Mountain Base Teehee Valley Entrance Digspot", 0x39DA20, 0),
LocationData("Hoohoo Mountain Base Teehee Valley Entrance Block", 0x39DA18, 0),
@@ -298,7 +301,7 @@ booStatue: typing.List[LocationData] = [
LocationData("Beanbean Outskirts Before Harhall Digspot 1", 0x39E951, 0),
LocationData("Beanbean Outskirts Before Harhall Digspot 2", 0x39E959, 0),
LocationData("Beanstar Piece Harhall", 0x1E9441, 2),
LocationData("Beanbean Outskirts Boo Statue Mole", 0x1E9434, 2),
LocationData("Beanbean Outskirts Boostatue Mole", 0x1E9434, 2),
LocationData("Harhall's Pants", 0x1E9444, 2),
LocationData("Beanbean Outskirts S Room 2 Digspot 1", 0x39DC65, 0),
LocationData("Beanbean Outskirts S Room 2 Digspot 2", 0x39DC5D, 0),
@@ -317,6 +320,9 @@ chucklehuck: typing.List[LocationData] = [
LocationData("Chucklehuck Woods Cave Room 1 Block 2", 0x39DD8A, 0),
LocationData("Chucklehuck Woods Cave Room 2 Block", 0x39DD9F, 0),
LocationData("Chucklehuck Woods Cave Room 3 Block", 0x39DDAC, 0),
LocationData("Chucklehuck Woods Solo Luigi Cave Room 2 Block", 0x39DB72, 0),
LocationData("Chucklehuck Woods Solo Luigi Cave Room 3 Block 1", 0x39DB5D, 0),
LocationData("Chucklehuck Woods Solo Luigi Cave Room 3 Block 2", 0x39DB65, 0),
LocationData("Chucklehuck Woods Room 2 Block", 0x39DDC1, 0),
LocationData("Chucklehuck Woods Room 2 Digspot", 0x39DDC9, 0),
LocationData("Chucklehuck Woods Pipe Room Block 1", 0x39DDD6, 0),
@@ -786,7 +792,7 @@ nonBlock = [
(0x4373, 0x10, 0x277A45), # Teehee Valley Mole
(0x434D, 0x8, 0x1E9444), # Harhall's Pants
(0x432E, 0x10, 0x1E9441), # Harhall Beanstar Piece
(0x434B, 0x8, 0x1E9434), # Outskirts Boo Statue Mole
(0x434B, 0x8, 0x1E9434), # Outskirts Boostatue Mole
(0x42FE, 0x2, 0x1E943E), # Red Goblet
(0x42FE, 0x4, 0x24E628), # Green Goblet
(0x4301, 0x10, 0x250621), # Red Chuckola Fruit

View File

@@ -59,7 +59,7 @@ class LocationName:
HoohooMountainBaseBoostatueRoomDigspot1 = "Hoohoo Mountain Base Boostatue Room Digspot 1"
HoohooMountainBaseBoostatueRoomDigspot2 = "Hoohoo Mountain Base Boostatue Room Digspot 2"
HoohooMountainBaseBoostatueRoomDigspot3 = "Hoohoo Mountain Base Boostatue Room Digspot 3"
BeanbeanOutskirtsBooStatueMole = "Beanbean Outskirts Boo Statue Mole"
BeanbeanOutskirtsBooStatueMole = "Beanbean Outskirts Boostatue Mole"
HoohooMountainBaseGrassyAreaBlock1 = "Hoohoo Mountain Base Grassy Area Block 1"
HoohooMountainBaseGrassyAreaBlock2 = "Hoohoo Mountain Base Grassy Area Block 2"
HoohooMountainBaseGuffawhaRuinsEntranceDigspot = "Hoohoo Mountain Base Guffawha Ruins Entrance Digspot"
@@ -533,9 +533,9 @@ class LocationName:
BadgeShopMomPiranhaFlag2 = "Badge Shop Mom Piranha Flag 2"
BadgeShopMomPiranhaFlag3 = "Badge Shop Mom Piranha Flag 3"
HarhallsPants = "Harhall's Pants"
HoohooMountainBaseBooStatueCaveCoinBlock1 = "Hoohoo Mountain Base Boo Statue Cave Coin Block 1"
HoohooMountainBaseBooStatueCaveCoinBlock2 = "Hoohoo Mountain Base Boo Statue Cave Coin Block 2"
HoohooMountainBaseBooStatueCaveCoinBlock3 = "Hoohoo Mountain Base Boo Statue Cave Coin Block 3"
HoohooMountainBaseBooStatueCaveCoinBlock1 = "Hoohoo Mountain Base Boostatue Cave Coin Block 1"
HoohooMountainBaseBooStatueCaveCoinBlock2 = "Hoohoo Mountain Base Boostatue Cave Coin Block 2"
HoohooMountainBaseBooStatueCaveCoinBlock3 = "Hoohoo Mountain Base Boostatue Cave Coin Block 3"
BeanbeanOutskirtsNWCoinBlock = "Beanbean Outskirts NW Coin Block"
BeanbeanOutskirtsSRoom1CoinBlock = "Beanbean Outskirts S Room 1 Coin Block"
BeanbeanOutskirtsSRoom2CoinBlock = "Beanbean Outskirts S Room 2 Coin Block"

View File

@@ -2,13 +2,13 @@ from Options import Choice, Toggle, StartInventoryPool, PerGameCommonOptions, Ra
from dataclasses import dataclass
class BowsersCastleSkip(Toggle):
class SkipBowsersCastle(Toggle):
"""
Skip straight from the entrance hall to Bowletta in Bowser's Castle.
Skip straight from the Entrance Hall to Bowletta in Bowser's Castle.
All Bowser's Castle locations will be removed from the location pool.
"""
display_name = "Bowser's Castle Skip"
display_name = "Skip Bowser's Castle"
class ExtraPipes(Toggle):
@@ -272,13 +272,47 @@ class ChuckleBeans(Choice):
option_all = 2
default = 2
class Goal(Choice):
"""
Vanilla: Complete jokes end with the required items and defeat Birdo to unlock Bowser's Castle.
Emblem Hunt: Find the required number of Beanstar Emblems to gain access to Bowser's Castle.
"""
display_name = "Goal"
option_vanilla = 0
option_emblem_hunt = 1
default = 0
class EmblemsRequired(Range):
"""
Number of Beanstar Emblems to collect to unlock Bowser's Castle.
If Goal is not Emblem Hunt, this does nothing.
"""
display_name = "Emblems Required"
range_start = 1
range_end = 100
default = 50
class EmblemsAmount(Range):
"""
Number of Beanstar Emblems that are in the pool.
If Goal is not Emblem Hunt, this does nothing.
"""
display_name = "Emblems Available"
range_start = 1
range_end = 150
default = 75
@dataclass
class MLSSOptions(PerGameCommonOptions):
start_inventory_from_pool: StartInventoryPool
coins: Coins
difficult_logic: DifficultLogic
castle_skip: BowsersCastleSkip
castle_skip: SkipBowsersCastle
extra_pipes: ExtraPipes
skip_minecart: SkipMinecart
disable_surf: DisableSurf
@@ -286,6 +320,9 @@ class MLSSOptions(PerGameCommonOptions):
harhalls_pants: Removed
block_visibility: HiddenVisible
chuckle_beans: ChuckleBeans
goal: Goal
emblems_required: EmblemsRequired
emblems_amount: EmblemsAmount
music_options: MusicOptions
randomize_sounds: RandomSounds
randomize_enemies: RandomizeEnemies

View File

@@ -91,6 +91,16 @@ def connect_regions(world: "MLSSWorld"):
connect(world, names, "Main Area", "BaseUltraRocks", lambda state: StateLogic.ultra(state, world.player))
connect(world, names, "Main Area", "Chucklehuck Woods", lambda state: StateLogic.brooch(state, world.player))
connect(world, names, "Main Area", "BooStatue", lambda state: StateLogic.canCrash(state, world.player))
if world.options.goal == "emblem_hunt":
if world.options.castle_skip:
connect(world, names, "Main Area", "Cackletta's Soul",
lambda state: state.has("Beanstar Emblem", world.player, world.options.emblems_required.value))
else:
connect(world, names, "Main Area", "Bowser's Castle", lambda state: state.has("Beanstar Emblem", world.player, world.options.emblems_required.value))
connect(world, names, "Bowser's Castle", "Bowser's Castle Mini", lambda state:
StateLogic.canMini(state, world.player)
and StateLogic.thunder(state,world.player))
connect(world, names, "Bowser's Castle Mini", "Cackletta's Soul", lambda state: StateLogic.soul(state, world.player))
connect(
world,
names,
@@ -213,8 +223,8 @@ def connect_regions(world: "MLSSWorld"):
connect(world, names, "Surfable", "GwarharEntrance")
connect(world, names, "Surfable", "Oasis")
connect(world, names, "Surfable", "JokesEntrance", lambda state: StateLogic.fire(state, world.player))
connect(world, names, "JokesMain", "PostJokes", lambda state: StateLogic.postJokes(state, world.player))
if not world.options.castle_skip:
connect(world, names, "JokesMain", "PostJokes", lambda state: StateLogic.postJokes(state, world.player, world.options.goal.value))
if not world.options.castle_skip and world.options.goal != "emblem_hunt":
connect(world, names, "PostJokes", "Bowser's Castle")
connect(
world,
@@ -224,7 +234,7 @@ def connect_regions(world: "MLSSWorld"):
lambda state: StateLogic.canMini(state, world.player) and StateLogic.thunder(state, world.player),
)
connect(world, names, "Bowser's Castle Mini", "Cackletta's Soul")
else:
elif world.options.goal != "emblem_hunt":
connect(world, names, "PostJokes", "Cackletta's Soul")
connect(world, names, "Chucklehuck Woods", "Winkle", lambda state: StateLogic.canDash(state, world.player))
connect(
@@ -247,14 +257,14 @@ def connect_regions(world: "MLSSWorld"):
names,
"Shop Starting Flag",
"Shop Birdo Flag",
lambda state: StateLogic.postJokes(state, world.player),
lambda state: StateLogic.postJokes(state, world.player, world.options.goal.value),
)
connect(
world,
names,
"Fungitown",
"Fungitown Shop Birdo Flag",
lambda state: StateLogic.postJokes(state, world.player),
lambda state: StateLogic.postJokes(state, world.player, world.options.goal.value),
)
else:
connect(
@@ -276,14 +286,14 @@ def connect_regions(world: "MLSSWorld"):
names,
"Shop Starting Flag",
"Shop Birdo Flag",
lambda state: StateLogic.canCrash(state, world.player) and StateLogic.postJokes(state, world.player),
lambda state: StateLogic.canCrash(state, world.player) and StateLogic.postJokes(state, world.player, world.options.goal.value),
)
connect(
world,
names,
"Fungitown",
"Fungitown Shop Birdo Flag",
lambda state: StateLogic.canCrash(state, world.player) and StateLogic.postJokes(state, world.player),
lambda state: StateLogic.canCrash(state, world.player) and StateLogic.postJokes(state, world.player, world.options.goal.value),
)

View File

@@ -177,10 +177,10 @@ class MLSSPatchExtension(APPatchExtension):
for pos in enemies:
stream.seek(pos + 8)
for _ in range(6):
enemy = int.from_bytes(stream.read(1))
enemy = int.from_bytes(stream.read(1), "little")
if enemy > 0:
stream.seek(1, 1)
flag = int.from_bytes(stream.read(1))
flag = int.from_bytes(stream.read(1), "little")
if flag == 0x7:
break
if flag in [0x0, 0x2, 0x4]:
@@ -196,12 +196,12 @@ class MLSSPatchExtension(APPatchExtension):
stream.seek(pos + 8)
for _ in range(6):
enemy = int.from_bytes(stream.read(1))
enemy = int.from_bytes(stream.read(1), "little")
if enemy > 0 and enemy not in Data.flying and enemy not in Data.pestnut:
if enemy == 0x52:
chomp = True
stream.seek(1, 1)
flag = int.from_bytes(stream.read(1))
flag = int.from_bytes(stream.read(1), "little")
if flag not in [0x0, 0x2, 0x4]:
stream.seek(1, 1)
continue
@@ -234,7 +234,7 @@ class MLSSPatchExtension(APPatchExtension):
stream.seek(pos)
temp = stream.read(1)
stream.seek(pos)
stream.write(bytes([temp[0] | 0x8]))
stream.write(bytes([temp[0] | 0x80]))
stream.seek(pos + 1)
stream.write(groups.pop())
@@ -316,6 +316,10 @@ def write_tokens(world: "MLSSWorld", patch: MLSSProcedurePatch) -> None:
patch.write_token(APTokenTypes.WRITE, 0xD00003, bytes([world.options.xp_multiplier.value]))
if world.options.goal == 1:
patch.write_token(APTokenTypes.WRITE, 0xD00008, bytes([world.options.goal.value]))
patch.write_token(APTokenTypes.WRITE, 0xD00009, bytes([world.options.emblems_required.value]))
if world.options.tattle_hp:
patch.write_token(APTokenTypes.WRITE, 0xD00000, bytes([0x1]))
@@ -427,4 +431,4 @@ def desc_inject(world: "MLSSWorld", patch: MLSSProcedurePatch, location: Locatio
index = value.index(location.address) + 66
dstring = f"{world.multiworld.player_name[item.player]}: {item.name}"
patch.write_token(APTokenTypes.WRITE, 0xD11000 + (index * 0x40), dstring.encode("UTF8"))
patch.write_token(APTokenTypes.WRITE, 0xD12000 + (index * 0x40), dstring.encode("UTF8"))

View File

@@ -28,11 +28,14 @@ def set_rules(world: "MLSSWorld", excluded):
lambda state: StateLogic.canDig(state, world.player),
)
if "Shop" in location.name and "Coffee" not in location.name and location.name not in excluded:
forbid_item(world.get_location(location.name), "Hammers", world.player)
if "Badge" in location.name or "Pants" in location.name:
add_rule(
world.get_location(location.name),
lambda state: StateLogic.brooch(state, world.player) or StateLogic.rose(state, world.player),
lambda state: (StateLogic.brooch(state, world.player) and StateLogic.fruits(state, world.player)
and (StateLogic.hammers(state, world.player)
or StateLogic.fire(state, world.player)
or StateLogic.thunder(state, world.player)))
or StateLogic.rose(state, world.player),
)
if location.itemType != 0 and location.name not in excluded:
if "Bowser" in location.name and world.options.castle_skip:
@@ -99,9 +102,86 @@ def set_rules(world: "MLSSWorld", excluded):
lambda state: StateLogic.ultra(state, world.player) and StateLogic.thunder(state, world.player),
)
forbid_item(
world.get_location(LocationName.SSChuckolaMembershipCard), "Nuts", world.player
) # Bandaid Fix
if world.options.goal == 1 and not world.options.castle_skip:
add_rule(
world.get_location(LocationName.BowsersCastleRoyCorridorBlock1),
lambda state: StateLogic.canDig(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleRoyCorridorBlock2),
lambda state: StateLogic.canDig(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleMiniMarioSidescrollerBlock1),
lambda state: StateLogic.canDig(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleMiniMarioSidescrollerBlock2),
lambda state: StateLogic.canDig(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleMiniMarioMazeBlock1),
lambda state: StateLogic.canDig(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleMiniMarioMazeBlock2),
lambda state: StateLogic.canDig(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleBeforeWendyFightBlock1),
lambda state: StateLogic.canDig(state, world.player)
and StateLogic.ultra(state, world.player)
and StateLogic.fire(state, world.player)
and StateLogic.canCrash(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleBeforeWendyFightBlock2),
lambda state: StateLogic.canDig(state, world.player)
and StateLogic.ultra(state, world.player)
and StateLogic.fire(state, world.player)
and StateLogic.canCrash(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleLarryRoomBlock),
lambda state: StateLogic.canDig(state, world.player)
and StateLogic.ultra(state, world.player)
and StateLogic.canDash(state, world.player)
and StateLogic.canCrash(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleWendyLarryHallwayDigspot),
lambda state: StateLogic.ultra(state, world.player)
and StateLogic.fire(state, world.player)
and StateLogic.canCrash(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleBeforeFawfulFightBlock1),
lambda state: StateLogic.canDig(state, world.player)
and StateLogic.ultra(state, world.player)
and StateLogic.canDash(state, world.player)
and StateLogic.canCrash(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleBeforeFawfulFightBlock2),
lambda state: StateLogic.canDig(state, world.player)
and StateLogic.ultra(state, world.player)
and StateLogic.canDash(state, world.player)
and StateLogic.canCrash(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleGreatDoorBlock1),
lambda state: StateLogic.canDig(state, world.player)
and StateLogic.ultra(state, world.player)
and StateLogic.canDash(state, world.player)
and StateLogic.canCrash(state, world.player)
)
add_rule(
world.get_location(LocationName.BowsersCastleGreatDoorBlock2),
lambda state: StateLogic.canDig(state, world.player)
and StateLogic.ultra(state, world.player)
and StateLogic.canDash(state, world.player)
and StateLogic.canCrash(state, world.player)
)
add_rule(
world.get_location(LocationName.HoohooVillageHammerHouseBlock),
@@ -398,6 +478,10 @@ def set_rules(world: "MLSSWorld", excluded):
world.get_location(LocationName.BeanstarPieceWinkleArea),
lambda state: StateLogic.winkle(state, world.player),
)
add_rule(
world.get_location("Guffawha Ruins Block"),
lambda state: StateLogic.thunder(state, world.player),
)
add_rule(
world.get_location(LocationName.GwarharLagoonSpangleReward),
lambda state: StateLogic.spangle(state, world.player),
@@ -406,6 +490,18 @@ def set_rules(world: "MLSSWorld", excluded):
world.get_location(LocationName.PantsShopMomPiranhaFlag1),
lambda state: StateLogic.brooch(state, world.player) or StateLogic.rose(state, world.player),
)
add_rule(
world.get_location("Chucklehuck Woods Solo Luigi Cave Room 2 Block"),
lambda state: StateLogic.brooch(state, world.player) and StateLogic.canDig(state, world.player),
)
add_rule(
world.get_location("Chucklehuck Woods Solo Luigi Cave Room 3 Block 1"),
lambda state: StateLogic.brooch(state, world.player) and StateLogic.canDig(state, world.player),
)
add_rule(
world.get_location("Chucklehuck Woods Solo Luigi Cave Room 3 Block 2"),
lambda state: StateLogic.brooch(state, world.player) and StateLogic.canDig(state, world.player),
)
add_rule(
world.get_location(LocationName.PantsShopMomPiranhaFlag2),
lambda state: StateLogic.brooch(state, world.player) or StateLogic.rose(state, world.player),
@@ -600,6 +696,14 @@ def set_rules(world: "MLSSWorld", excluded):
world.get_location(LocationName.HoohooMountainBaseBooStatueCaveCoinBlock1),
lambda state: StateLogic.canCrash(state, world.player) or StateLogic.super(state, world.player),
)
add_rule(
world.get_location("Chucklehuck Woods Solo Luigi Cave Room 1 Coin Block 1"),
lambda state: StateLogic.canDig(state, world.player) and StateLogic.brooch(state, world.player),
)
add_rule(
world.get_location("Chucklehuck Woods Solo Luigi Cave Room 1 Coin Block 2"),
lambda state: StateLogic.canDig(state, world.player) and StateLogic.brooch(state, world.player),
)
add_rule(
world.get_location(LocationName.HoohooMountainBaseBooStatueCaveCoinBlock2),
lambda state: StateLogic.canCrash(state, world.player) or StateLogic.super(state, world.player),
@@ -679,7 +783,7 @@ def set_rules(world: "MLSSWorld", excluded):
add_rule(
world.get_location(LocationName.GwarharLagoonFirstUnderwaterAreaRoom2CoinBlock),
lambda state: StateLogic.canDash(state, world.player)
and (StateLogic.membership(state, world.player) or StateLogic.surfable(state, world.player)),
and (StateLogic.membership(state, world.player) or StateLogic.surfable(state, world.player)),
)
add_rule(
world.get_location(LocationName.JokesEndSecondFloorWestRoomCoinBlock),

View File

@@ -1,3 +1,6 @@
from .Options import Goal
def canDig(state, player):
return state.has("Green Goblet", player) and state.has("Hammers", player)
@@ -105,8 +108,9 @@ def surfable(state, player):
)
def postJokes(state, player):
return (
def postJokes(state, player, goal):
if goal == Goal.option_vanilla: # Logic for beating jokes end without beanstar emblems
return (
surfable(state, player)
and canDig(state, player)
and dressBeanstar(state, player)
@@ -115,7 +119,13 @@ def postJokes(state, player):
and brooch(state, player)
and rose(state, player)
and canDash(state, player)
)
)
else: # Logic for beating jokes end with beanstar emblems
return (
surfable(state, player)
and canDig(state, player)
and canDash(state, player)
)
def teehee(state, player):
@@ -153,3 +163,10 @@ def birdo_shop(state, player):
def fungitown_birdo_shop(state, player):
return state.can_reach("Fungitown Shop Birdo Flag", "Region", player)
def soul(state, player):
return (ultra(state, player)
and canMini(state, player)
and canDig(state, player)
and canDash(state, player)
and canCrash(state, player))

View File

@@ -1,3 +1,4 @@
import logging
import os
import pkgutil
import typing
@@ -7,7 +8,7 @@ from worlds.AutoWorld import WebWorld, World
from typing import Set, Dict, Any
from .Locations import all_locations, location_table, bowsers, bowsersMini, hidden, coins
from .Options import MLSSOptions
from .Items import MLSSItem, itemList, item_frequencies, item_table
from .Items import MLSSItem, itemList, item_frequencies, item_table, mlss_item_name_groups
from .Names.LocationName import LocationName
from .Client import MLSSClient
from .Regions import create_regions, connect_regions
@@ -53,6 +54,7 @@ class MLSSWorld(World):
options_dataclass = MLSSOptions
options: MLSSOptions
settings: typing.ClassVar[MLSSSettings]
item_name_groups = mlss_item_name_groups
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = {loc_data.name: loc_data.id for loc_data in all_locations}
required_client_version = (0, 5, 0)
@@ -61,6 +63,12 @@ class MLSSWorld(World):
def generate_early(self) -> None:
self.disabled_locations = set()
if self.options.goal == "emblem_hunt":
if self.options.emblems_amount < self.options.emblems_required:
self.options.emblems_amount.value = self.options.emblems_required.value
logging.warning(
f"{self.player_name}'s number of emblems required is greater than the number of emblems available. "
f"Changing to {self.options.emblems_required.value}.")
if self.options.skip_minecart:
self.disabled_locations.update([LocationName.HoohooMountainBaseMinecartCaveDigspot])
if self.options.disable_surf:
@@ -111,6 +119,8 @@ class MLSSWorld(World):
for item in itemList:
if item.classification != ItemClassification.filler and item.classification != ItemClassification.skip_balancing:
freq = item_frequencies.get(item.itemName, 1)
if item.itemName == "Beanstar Emblem":
freq = (0 if self.options.goal != "emblem_hunt" else self.options.emblems_amount.value)
if item in precollected:
freq = max(freq - precollected.count(item), 0)
if self.options.disable_harhalls_pants and "Harhall's" in item.itemName:
@@ -138,7 +148,6 @@ class MLSSWorld(World):
# And finally take as many fillers as we need to have the same amount of items and locations.
remaining = len(all_locations) - len(required_items) - len(self.disabled_locations) - 5
self.multiworld.itempool += [
self.create_item(filler_item_name) for filler_item_name in self.random.sample(filler_items, remaining)
]

Binary file not shown.