Compare commits

..

8 Commits

Author SHA1 Message Date
Exempt-Medic
3d1f9c630c Shivers: Fix get_pre_fill_items 2025-06-14 08:28:29 -04:00
Louis M
27a6770569 Aquaria: Fixing open waters urns not breakable with nature forms logic bug (#5072)
* Fixing open waters urns not breakable with nature forms logic bug

* Using list in comprehension only when useful

* Replacing damaging items by a constant

* Removing comprehension list creating from lambda
2025-06-14 13:17:33 +02:00
NewSoupVi
2ff611167a ALTTP: Fix take_any leaving a placed item in the multiworld itempool #5108 2025-06-14 12:21:25 +02:00
agilbert1412
e83e178b63 Stardew Valley: Fix 3 Logic Issues (#5094)
Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
2025-06-13 20:29:23 -04:00
Exempt-Medic
068a757373 Item Plando: Fix count value (#5101) 2025-06-13 20:29:06 -04:00
PoryGone
0ad4527719 SA2B: Logic Fixes (#5095)
- Fixed King Boom Boo being able to appear in multiple boss gates
- `Final Rush - 16 Animals (Expert)` no longer requires `Sonic - Bounce Bracelet`
- `Dry Lagoon - 5 (Standard)` now requires `Rouge - Pick Nails`
- `Sand Ocean - Extra Life Box 2 (Standard/Hard/Expert)` no longer requires `Eggman - Jet Engine`
- `Security Hall - 8 Animals (Expert)` no longer requires `Rouge - Pick Nails`
- `Sky Rail - Item Box 8 (Standard)` now requires `Shadow - Air Shoes` and `Shadow - Mystic Melody`
- `Cosmic Wall - Chao Key 1 (Standard/Hard/Expert)` no longer requires `Eggman - Mystic Melody`
- `Cannon's Core - Pipe 2 (Expert)` no longer requires `Tails - Booster`
- `Cannon's Core - Gold Beetle` no longer requires `Tails - Booster` nor `Knuckles - Hammer Gloves`
2025-06-13 22:01:19 +02:00
qwint
8c6327d024 LTTP/SDV: use .name when appropriate in subtests (#5107) 2025-06-13 21:56:09 +02:00
qwint
aecbb2ab02 fix saving princess's use of subprocess helpers (#5103) 2025-06-13 12:28:58 +02:00
13 changed files with 70 additions and 73 deletions

View File

@@ -548,10 +548,12 @@ def set_up_take_anys(multiworld, world, player):
old_man_take_any.shop = TakeAny(old_man_take_any, 0x0112, 0xE2, True, True, total_shop_slots)
multiworld.shops.append(old_man_take_any.shop)
swords = [item for item in multiworld.itempool if item.player == player and item.type == 'Sword']
if swords:
sword = multiworld.random.choice(swords)
multiworld.itempool.remove(sword)
sword_indices = [
index for index, item in enumerate(multiworld.itempool) if item.player == player and item.type == 'Sword'
]
if sword_indices:
sword_index = multiworld.random.choice(sword_indices)
sword = multiworld.itempool.pop(sword_index)
multiworld.itempool.append(item_factory('Rupees (20)', world))
old_man_take_any.shop.add_inventory(0, sword.name, 0, 0)
loc_name = "Old Man Sword Cave"

View File

@@ -38,7 +38,7 @@ class DungeonFillTestBase(TestCase):
def test_original_dungeons(self):
self.generate_with_options(DungeonItem.option_original_dungeon)
for location in self.multiworld.get_filled_locations():
with (self.subTest(location=location)):
with (self.subTest(location_name=location.name)):
if location.parent_region.dungeon is None:
self.assertIs(location.item.dungeon, None)
else:
@@ -52,7 +52,7 @@ class DungeonFillTestBase(TestCase):
def test_own_dungeons(self):
self.generate_with_options(DungeonItem.option_own_dungeons)
for location in self.multiworld.get_filled_locations():
with self.subTest(location=location):
with self.subTest(location_name=location.name):
if location.parent_region.dungeon is None:
self.assertIs(location.item.dungeon, None)
else:

View File

@@ -4,7 +4,7 @@ Date: Fri, 15 Mar 2024 18:41:40 +0000
Description: Used to manage Regions in the Aquaria game multiworld randomizer
"""
from typing import Dict, Optional
from typing import Dict, Optional, Iterable
from BaseClasses import MultiWorld, Region, Entrance, Item, ItemClassification, CollectionState
from .Items import AquariaItem, ItemNames
from .Locations import AquariaLocations, AquariaLocation, AquariaLocationNames
@@ -34,10 +34,15 @@ def _has_li(state: CollectionState, player: int) -> bool:
return state.has(ItemNames.LI_AND_LI_SONG, player)
def _has_damaging_item(state: CollectionState, player: int) -> bool:
"""`player` in `state` has the shield song item"""
return state.has_any({ItemNames.ENERGY_FORM, ItemNames.NATURE_FORM, ItemNames.BEAST_FORM, ItemNames.LI_AND_LI_SONG,
ItemNames.BABY_NAUTILUS, ItemNames.BABY_PIRANHA, ItemNames.BABY_BLASTER}, player)
DAMAGING_ITEMS:Iterable[str] = [
ItemNames.ENERGY_FORM, ItemNames.NATURE_FORM, ItemNames.BEAST_FORM,
ItemNames.LI_AND_LI_SONG, ItemNames.BABY_NAUTILUS, ItemNames.BABY_PIRANHA,
ItemNames.BABY_BLASTER
]
def _has_damaging_item(state: CollectionState, player: int, damaging_items:Iterable[str] = DAMAGING_ITEMS) -> bool:
"""`player` in `state` has the an item that do damage other than the ones in `to_remove`"""
return state.has_any(damaging_items, player)
def _has_energy_attack_item(state: CollectionState, player: int) -> bool:
@@ -566,9 +571,11 @@ class AquariaRegions:
self.__connect_one_way_regions(self.openwater_tr, self.openwater_tr_turtle,
lambda state: _has_beast_form_or_arnassi_armor(state, self.player))
self.__connect_one_way_regions(self.openwater_tr_turtle, self.openwater_tr)
damaging_items_minus_nature_form = [item for item in DAMAGING_ITEMS if item != ItemNames.NATURE_FORM]
self.__connect_one_way_regions(self.openwater_tr, self.openwater_tr_urns,
lambda state: _has_bind_song(state, self.player) or
_has_damaging_item(state, self.player))
_has_damaging_item(state, self.player,
damaging_items_minus_nature_form))
self.__connect_regions(self.openwater_tr, self.openwater_br)
self.__connect_regions(self.openwater_tr, self.mithalas_city)
self.__connect_regions(self.openwater_tr, self.veil_b)

View File

@@ -1,6 +1,7 @@
import typing
from BaseClasses import MultiWorld
from Options import OptionError
from worlds.AutoWorld import World
from .Names import LocationName
@@ -99,8 +100,9 @@ def get_gate_bosses(world: World):
pass
if boss in plando_bosses:
# TODO: Raise error here. Duplicates not allowed
pass
raise OptionError(f"Invalid input for option `plando_bosses`: "
f"No Duplicate Bosses permitted ({boss}) - for "
f"{world.player_name}")
plando_bosses[boss_num] = boss
@@ -108,13 +110,14 @@ def get_gate_bosses(world: World):
available_bosses.remove(boss)
for x in range(world.options.number_of_level_gates):
if ("king boom boo" not in selected_bosses) and ("king boom boo" not in available_bosses) and ((x + 1) / world.options.number_of_level_gates) > 0.5:
available_bosses.extend(gate_bosses_with_requirements_table)
if (10 not in selected_bosses) and (king_boom_boo not in available_bosses) and ((x + 1) / world.options.number_of_level_gates) > 0.5:
available_bosses.extend(gate_bosses_with_requirements_table.keys())
world.random.shuffle(available_bosses)
chosen_boss = available_bosses[0]
if plando_bosses[x] != "None":
available_bosses.append(plando_bosses[x])
if plando_bosses[x] not in available_bosses:
available_bosses.append(plando_bosses[x])
chosen_boss = plando_bosses[x]
selected_bosses.append(all_gate_bosses_table[chosen_boss])

View File

@@ -324,7 +324,8 @@ def set_mission_upgrade_rules_standard(multiworld: MultiWorld, world: World, pla
add_rule_safe(multiworld, LocationName.iron_gate_5, player,
lambda state: state.has(ItemName.eggman_large_cannon, player))
add_rule_safe(multiworld, LocationName.dry_lagoon_5, player,
lambda state: state.has(ItemName.rouge_treasure_scope, player))
lambda state: state.has(ItemName.rouge_pick_nails, player) and
state.has(ItemName.rouge_treasure_scope, player))
add_rule_safe(multiworld, LocationName.sand_ocean_5, player,
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule_safe(multiworld, LocationName.egg_quarters_5, player,
@@ -407,8 +408,7 @@ def set_mission_upgrade_rules_standard(multiworld: MultiWorld, world: World, pla
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(multiworld.get_location(LocationName.cosmic_wall_chao_1, player),
lambda state: state.has(ItemName.eggman_mystic_melody, player) and
state.has(ItemName.eggman_jet_engine, player))
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.cannon_core_chao_1, player),
lambda state: state.has(ItemName.tails_booster, player) and
@@ -1402,8 +1402,6 @@ def set_mission_upgrade_rules_standard(multiworld: MultiWorld, world: World, pla
state.has(ItemName.eggman_large_cannon, player)))
add_rule(multiworld.get_location(LocationName.dry_lagoon_lifebox_2, player),
lambda state: state.has(ItemName.rouge_treasure_scope, player))
add_rule(multiworld.get_location(LocationName.sand_ocean_lifebox_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.egg_quarters_lifebox_2, player),
lambda state: (state.has(ItemName.rouge_mystic_melody, player) and
state.has(ItemName.rouge_treasure_scope, player)))
@@ -1724,6 +1722,9 @@ def set_mission_upgrade_rules_standard(multiworld: MultiWorld, world: World, pla
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.white_jungle_itembox_8, player),
lambda state: state.has(ItemName.shadow_air_shoes, player))
add_rule(multiworld.get_location(LocationName.sky_rail_itembox_8, player),
lambda state: (state.has(ItemName.shadow_air_shoes, player) and
state.has(ItemName.shadow_mystic_melody, player)))
add_rule(multiworld.get_location(LocationName.mad_space_itembox_8, player),
lambda state: state.has(ItemName.rouge_iron_boots, player))
add_rule(multiworld.get_location(LocationName.cosmic_wall_itembox_8, player),
@@ -2308,8 +2309,7 @@ def set_mission_upgrade_rules_hard(multiworld: MultiWorld, world: World, player:
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.cosmic_wall_chao_1, player),
lambda state: state.has(ItemName.eggman_mystic_melody, player) and
state.has(ItemName.eggman_jet_engine, player))
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.cannon_core_chao_1, player),
lambda state: state.has(ItemName.tails_booster, player) and
@@ -2980,8 +2980,6 @@ def set_mission_upgrade_rules_hard(multiworld: MultiWorld, world: World, player:
state.has(ItemName.eggman_jet_engine, player)))
add_rule(multiworld.get_location(LocationName.dry_lagoon_lifebox_2, player),
lambda state: state.has(ItemName.rouge_treasure_scope, player))
add_rule(multiworld.get_location(LocationName.sand_ocean_lifebox_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.egg_quarters_lifebox_2, player),
lambda state: (state.has(ItemName.rouge_mystic_melody, player) and
state.has(ItemName.rouge_treasure_scope, player)))
@@ -3593,8 +3591,7 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.cosmic_wall_chao_1, player),
lambda state: state.has(ItemName.eggman_mystic_melody, player) and
state.has(ItemName.eggman_jet_engine, player))
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.cannon_core_chao_1, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) and
@@ -3643,9 +3640,6 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
add_rule(multiworld.get_location(LocationName.cosmic_wall_pipe_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.cannon_core_pipe_2, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.prison_lane_pipe_3, player),
lambda state: state.has(ItemName.tails_bazooka, player))
add_rule(multiworld.get_location(LocationName.mission_street_pipe_3, player),
@@ -3771,10 +3765,6 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
add_rule(multiworld.get_location(LocationName.cosmic_wall_beetle, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.cannon_core_beetle, player),
lambda state: state.has(ItemName.tails_booster, player) and
state.has(ItemName.knuckles_hammer_gloves, player))
# Animal Upgrade Requirements
if world.options.animalsanity:
add_rule(multiworld.get_location(LocationName.hidden_base_animal_2, player),
@@ -3839,8 +3829,7 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
add_rule(multiworld.get_location(LocationName.weapons_bed_animal_8, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.security_hall_animal_8, player),
lambda state: state.has(ItemName.rouge_pick_nails, player) and
state.has(ItemName.rouge_iron_boots, player))
lambda state: state.has(ItemName.rouge_iron_boots, player))
add_rule(multiworld.get_location(LocationName.cosmic_wall_animal_8, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
@@ -3976,8 +3965,6 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
state.has(ItemName.tails_bazooka, player))
add_rule(multiworld.get_location(LocationName.crazy_gadget_animal_16, player),
lambda state: state.has(ItemName.sonic_flame_ring, player))
add_rule(multiworld.get_location(LocationName.final_rush_animal_16, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(multiworld.get_location(LocationName.final_chase_animal_17, player),
lambda state: state.has(ItemName.shadow_flame_ring, player))
@@ -4035,8 +4022,6 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.dry_lagoon_lifebox_2, player),
lambda state: state.has(ItemName.rouge_treasure_scope, player))
add_rule(multiworld.get_location(LocationName.sand_ocean_lifebox_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.egg_quarters_lifebox_2, player),
lambda state: state.has(ItemName.rouge_treasure_scope, player))

View File

@@ -12,7 +12,7 @@ from .Constants import *
def launch_client(*args: str):
from .Client import launch
launch_subprocess(launch(*args), name=CLIENT_NAME)
launch_subprocess(launch, name=CLIENT_NAME, args=args)
components.append(

View File

@@ -261,13 +261,13 @@ class ShiversWorld(World):
data.type == ItemType.POT_DUPLICATE]
elif self.options.full_pots == "complete":
return [self.create_item(name) for name, data in item_table.items() if
data.type == ItemType.POT_COMPELTE_DUPLICATE]
data.type == ItemType.POT_COMPLETE_DUPLICATE]
else:
pool = []
pieces = [self.create_item(name) for name, data in item_table.items() if
data.type == ItemType.POT_DUPLICATE]
complete = [self.create_item(name) for name, data in item_table.items() if
data.type == ItemType.POT_COMPELTE_DUPLICATE]
data.type == ItemType.POT_COMPLETE_DUPLICATE]
for i in range(10):
if self.pot_completed_list[i] == 0:
pool.append(pieces[i])

View File

@@ -271,11 +271,11 @@ solar_essence = BundleItem(Loot.solar_essence)
void_essence = BundleItem(Loot.void_essence)
petrified_slime = BundleItem(Mineral.petrified_slime)
blue_slime_egg = BundleItem(Loot.blue_slime_egg)
red_slime_egg = BundleItem(Loot.red_slime_egg)
purple_slime_egg = BundleItem(Loot.purple_slime_egg)
green_slime_egg = BundleItem(Loot.green_slime_egg)
tiger_slime_egg = BundleItem(Loot.tiger_slime_egg, source=BundleItem.Sources.island)
blue_slime_egg = BundleItem(AnimalProduct.slime_egg_blue)
red_slime_egg = BundleItem(AnimalProduct.slime_egg_red)
purple_slime_egg = BundleItem(AnimalProduct.slime_egg_purple)
green_slime_egg = BundleItem(AnimalProduct.slime_egg_green)
tiger_slime_egg = BundleItem(AnimalProduct.slime_egg_tiger, source=BundleItem.Sources.island)
cherry_bomb = BundleItem(Bomb.cherry_bomb, 5)
bomb = BundleItem(Bomb.bomb, 2)

View File

@@ -168,15 +168,16 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
AnimalProduct.squid_ink: self.mine.can_mine_in_the_mines_floor_81_120() | (self.building.has_building(Building.fish_pond) & self.has(Fish.squid)),
AnimalProduct.sturgeon_roe: self.has(Fish.sturgeon) & self.building.has_building(Building.fish_pond),
AnimalProduct.truffle: self.animal.has_animal(Animal.pig) & self.season.has_any_not_winter(),
AnimalProduct.void_egg: self.has(AnimalProduct.void_egg_starter), # Should also check void chicken if there was an alternative to obtain it without void egg
AnimalProduct.void_egg: self.has(AnimalProduct.void_egg_starter), # Should also check void chicken if there was an alternative to obtain it without void egg
AnimalProduct.wool: self.animal.has_animal(Animal.rabbit) | self.animal.has_animal(Animal.sheep),
AnimalProduct.slime_egg_green: self.has(Machine.slime_egg_press) & self.has(Loot.slime),
AnimalProduct.slime_egg_blue: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(3),
AnimalProduct.slime_egg_red: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(6),
AnimalProduct.slime_egg_purple: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(9),
AnimalProduct.slime_egg_tiger: self.has(Fish.lionfish) & self.building.has_building(Building.fish_pond),
AnimalProduct.duck_egg_starter: self.logic.false_, # It could be purchased at the Feast of the Winter Star, but it's random every year, so not considering it yet...
AnimalProduct.dinosaur_egg_starter: self.logic.false_, # Dinosaur eggs are also part of the museum rules, and I don't want to touch them yet.
AnimalProduct.slime_egg_tiger: self.can_fish_pond(Fish.lionfish, *(Forageable.ginger, Fruit.pineapple, Fruit.mango)) & self.time.has_lived_months(12) &
self.building.has_building(Building.slime_hutch) & self.monster.can_kill(Monster.tiger_slime),
AnimalProduct.duck_egg_starter: self.logic.false_, # It could be purchased at the Feast of the Winter Star, but it's random every year, so not considering it yet...
AnimalProduct.dinosaur_egg_starter: self.logic.false_, # Dinosaur eggs are also part of the museum rules, and I don't want to touch them yet.
AnimalProduct.egg_starter: self.logic.false_, # It could be purchased at the Desert Festival, but festival logic is quite a mess, so not considering it yet...
AnimalProduct.golden_egg_starter: self.received(AnimalProduct.golden_egg) & (self.money.can_spend_at(Region.ranch, 100000) | self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 100)),
AnimalProduct.void_egg_starter: self.money.can_spend_at(Region.sewer, 5000) | (self.building.has_building(Building.fish_pond) & self.has(Fish.void_salmon)),
@@ -233,7 +234,7 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
Forageable.secret_note: self.quest.has_magnifying_glass() & (self.ability.can_chop_trees() | self.mine.can_mine_in_the_mines_floor_1_40()), #
Fossil.bone_fragment: (self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.pickaxe)) | self.monster.can_kill(Monster.skeleton),
Fossil.fossilized_leg: self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.pickaxe),
Fossil.fossilized_ribs: self.region.can_reach(Region.island_south) & self.tool.has_tool(Tool.hoe),
Fossil.fossilized_ribs: self.region.can_reach(Region.island_south) & self.tool.has_tool(Tool.hoe) & self.received("Open Professor Snail Cave"),
Fossil.fossilized_skull: self.action.can_open_geode(Geode.golden_coconut),
Fossil.fossilized_spine: self.fishing.can_fish_at(Region.dig_site),
Fossil.fossilized_tail: self.action.can_pan_at(Region.dig_site, ToolMaterial.copper),
@@ -288,9 +289,9 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
MetalBar.quartz: self.can_smelt(Mineral.quartz) | self.can_smelt("Fire Quartz") | (self.has(Machine.recycling_machine) & (self.has(Trash.broken_cd) | self.has(Trash.broken_glasses))),
MetalBar.radioactive: self.can_smelt(Ore.radioactive),
Ore.copper: self.mine.can_mine_in_the_mines_floor_1_40() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_tool(Tool.pan, ToolMaterial.copper),
Ore.gold: self.mine.can_mine_in_the_mines_floor_81_120() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_tool(Tool.pan, ToolMaterial.iron),
Ore.iridium: self.mine.can_mine_in_the_skull_cavern() | self.can_fish_pond(Fish.super_cucumber) | self.tool.has_tool(Tool.pan, ToolMaterial.gold),
Ore.iron: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_tool(Tool.pan, ToolMaterial.copper),
Ore.gold: self.mine.can_mine_in_the_mines_floor_81_120() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_tool(Tool.pan, ToolMaterial.gold),
Ore.iridium: self.count(2, *(self.mine.can_mine_in_the_skull_cavern(), self.can_fish_pond(Fish.super_cucumber), self.tool.has_tool(Tool.pan, ToolMaterial.iridium))),
Ore.iron: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_tool(Tool.pan, ToolMaterial.iron),
Ore.radioactive: self.ability.can_mine_perfectly() & self.region.can_reach(Region.qi_walnut_room),
RetainingSoil.basic: self.money.can_spend_at(Region.pierre_store, 100),
RetainingSoil.quality: self.time.has_year_two & self.money.can_spend_at(Region.pierre_store, 150),
@@ -381,5 +382,8 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
def can_use_obelisk(self, obelisk: str) -> StardewRule:
return self.region.can_reach(Region.farm) & self.received(obelisk)
def can_fish_pond(self, fish: str) -> StardewRule:
return self.building.has_building(Building.fish_pond) & self.has(fish)
def can_fish_pond(self, fish: str, *items: str) -> StardewRule:
rule = self.building.has_building(Building.fish_pond) & self.has(fish)
if items:
rule = rule & self.has_all(*items)
return rule

View File

@@ -1,9 +1,4 @@
class Loot:
blue_slime_egg = "Blue Slime Egg"
red_slime_egg = "Red Slime Egg"
purple_slime_egg = "Purple Slime Egg"
green_slime_egg = "Green Slime Egg"
tiger_slime_egg = "Tiger Slime Egg"
slime = "Slime"
bug_meat = "Bug Meat"
bat_wing = "Bat Wing"

View File

@@ -11,7 +11,7 @@ class EntranceRandomizationAssertMixin:
non_progression_connections = [connection for connection in all_connections.values() if RandomizationFlag.BIT_NON_PROGRESSION in connection.flag]
for non_progression_connections in non_progression_connections:
with self.subTest(connection=non_progression_connections):
with self.subTest(connection=non_progression_connections.name):
self.assert_can_reach_entrance(non_progression_connections.name)

View File

@@ -12,14 +12,14 @@ from ...regions.regions import create_all_regions, create_all_connections
class TestVanillaRegionsConnectionsWithGingerIsland(unittest.TestCase):
def test_region_exits_lead_somewhere(self):
for region in vanilla_data.regions_with_ginger_island_by_name.values():
with self.subTest(region=region):
with self.subTest(region=region.name):
for exit_ in region.exits:
self.assertIn(exit_, vanilla_data.connections_with_ginger_island_by_name,
f"{region.name} is leading to {exit_} but it does not exist.")
def test_connection_lead_somewhere(self):
for connection in vanilla_data.connections_with_ginger_island_by_name.values():
with self.subTest(connection=connection):
with self.subTest(connection=connection.name):
self.assertIn(connection.destination, vanilla_data.regions_with_ginger_island_by_name,
f"{connection.name} is leading to {connection.destination} but it does not exist.")
@@ -27,14 +27,14 @@ class TestVanillaRegionsConnectionsWithGingerIsland(unittest.TestCase):
class TestVanillaRegionsConnectionsWithoutGingerIsland(unittest.TestCase):
def test_region_exits_lead_somewhere(self):
for region in vanilla_data.regions_without_ginger_island_by_name.values():
with self.subTest(region=region):
with self.subTest(region=region.name):
for exit_ in region.exits:
self.assertIn(exit_, vanilla_data.connections_without_ginger_island_by_name,
f"{region.name} is leading to {exit_} but it does not exist.")
def test_connection_lead_somewhere(self):
for connection in vanilla_data.connections_without_ginger_island_by_name.values():
with self.subTest(connection=connection):
with self.subTest(connection=connection.name):
self.assertIn(connection.destination, vanilla_data.regions_without_ginger_island_by_name,
f"{connection.name} is leading to {connection.destination} but it does not exist.")

View File

@@ -8,7 +8,7 @@ class TestNeedRegionToCatchFish(SVTestBase):
SeasonRandomization.internal_name: SeasonRandomization.option_disabled,
ElevatorProgression.internal_name: ElevatorProgression.option_vanilla,
SkillProgression.internal_name: SkillProgression.option_vanilla,
ToolProgression.internal_name: ToolProgression.option_vanilla,
ToolProgression.internal_name: ToolProgression.option_progressive,
Fishsanity.internal_name: Fishsanity.option_all,
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
@@ -18,7 +18,7 @@ class TestNeedRegionToCatchFish(SVTestBase):
fish_and_items = {
Fish.crimsonfish: ["Beach Bridge"],
Fish.void_salmon: ["Railroad Boulder Removed", "Dark Talisman"],
Fish.woodskip: ["Glittering Boulder Removed", "Progressive Weapon"], # For the ores to get the axe upgrades
Fish.woodskip: ["Progressive Axe", "Progressive Axe", "Progressive Weapon"], # For the ores to get the axe upgrades
Fish.mutant_carp: ["Rusty Key"],
Fish.slimejack: ["Railroad Boulder Removed", "Rusty Key"],
Fish.lionfish: ["Boat Repair"],
@@ -26,8 +26,8 @@ class TestNeedRegionToCatchFish(SVTestBase):
Fish.stingray: ["Boat Repair", "Island Resort"],
Fish.ghostfish: ["Progressive Weapon"],
Fish.stonefish: ["Progressive Weapon"],
Fish.ice_pip: ["Progressive Weapon", "Progressive Weapon"],
Fish.lava_eel: ["Progressive Weapon", "Progressive Weapon", "Progressive Weapon"],
Fish.ice_pip: ["Progressive Weapon", "Progressive Weapon", "Progressive Pickaxe", "Progressive Pickaxe"],
Fish.lava_eel: ["Progressive Weapon", "Progressive Weapon", "Progressive Weapon", "Progressive Pickaxe", "Progressive Pickaxe", "Progressive Pickaxe"],
Fish.sandfish: ["Bus Repair"],
Fish.scorpion_carp: ["Desert Obelisk"],
# Starting the extended family quest requires having caught all the legendaries before, so they all have the rules of every other legendary
@@ -37,6 +37,7 @@ class TestNeedRegionToCatchFish(SVTestBase):
Fish.legend_ii: ["Beach Bridge", "Island Obelisk", "Island West Turtle", "Qi Walnut Room", "Rusty Key"],
Fish.ms_angler: ["Beach Bridge", "Island Obelisk", "Island West Turtle", "Qi Walnut Room", "Rusty Key"],
}
self.collect("Progressive Fishing Rod", 4)
self.original_state = self.multiworld.state.copy()
for fish in fish_and_items:
with self.subTest(f"Region rules for {fish}"):