Skip to content

Commit

Permalink
Improve options, fix all issues I hope
Browse files Browse the repository at this point in the history
  • Loading branch information
agilbert1412 committed Oct 9, 2023
1 parent 16ae3ad commit 228dfaa
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 134 deletions.
2 changes: 1 addition & 1 deletion worlds/stardew_valley/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def force_change_options_if_incompatible(self):
goal_is_island_related = goal_is_walnut_hunter or goal_is_perfection
exclude_ginger_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true
if goal_is_island_related and exclude_ginger_island:
self.options.exclude_ginger_island = options.ExcludeGingerIsland.option_false
self.options.exclude_ginger_island.value = options.ExcludeGingerIsland.option_false
goal_name = self.options.goal.current_key
player_name = self.multiworld.player_name[self.player]
logging.warning(f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})")
Expand Down
79 changes: 40 additions & 39 deletions worlds/stardew_valley/options.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass
from typing import Dict

from Options import Range, SpecialRange, Toggle, Choice, OptionSet, PerGameCommonOptions, DeathLink
from Options import Range, SpecialRange, Toggle, Choice, OptionSet, PerGameCommonOptions, DeathLink, Option
from .mods.mod_data import ModNames


Expand Down Expand Up @@ -569,41 +570,41 @@ class StardewValleyOptions(PerGameCommonOptions):
mods: Mods
death_link: DeathLink


stardew_valley_option_classes = {
Goal,
StartingMoney,
ProfitMargin,
BundleRandomization,
BundlePrice,
EntranceRandomization,
SeasonRandomization,
Cropsanity,
BackpackProgression,
ToolProgression,
SkillProgression,
BuildingProgression,
FestivalLocations,
ElevatorProgression,
ArcadeMachineLocations,
SpecialOrderLocations,
HelpWantedLocations,
Fishsanity,
Museumsanity,
Friendsanity,
FriendsanityHeartSize,
NumberOfMovementBuffs,
NumberOfLuckBuffs,
ExcludeGingerIsland,
TrapItems,
MultipleDaySleepEnabled,
MultipleDaySleepCost,
ExperienceMultiplier,
FriendshipMultiplier,
DebrisMultiplier,
QuickStart,
Gifting,
Mods,
}

stardew_valley_option_names = {option.internal_name for option in stardew_valley_option_classes}
def get_option_value(self, name: str) -> int:
return self.get_options_by_name()[name].value

def get_options_by_name(self):
return {
self.goal.internal_name: self.goal,
self.starting_money.internal_name: self.starting_money,
self.profit_margin.internal_name: self.profit_margin,
self.bundle_randomization.internal_name: self.bundle_randomization,
self.bundle_price.internal_name: self.bundle_price,
self.entrance_randomization.internal_name: self.entrance_randomization,
self.season_randomization.internal_name: self.season_randomization,
self.cropsanity.internal_name: self.cropsanity,
self.backpack_progression.internal_name: self.backpack_progression,
self.tool_progression.internal_name: self.tool_progression,
self.skill_progression.internal_name: self.skill_progression,
self.building_progression.internal_name: self.building_progression,
self.festival_locations.internal_name: self.festival_locations,
self.elevator_progression.internal_name: self.elevator_progression,
self.arcade_machine_locations.internal_name: self.arcade_machine_locations,
self.special_order_locations.internal_name: self.special_order_locations,
self.help_wanted_locations.internal_name: self.help_wanted_locations,
self.fishsanity.internal_name: self.fishsanity,
self.museumsanity.internal_name: self.museumsanity,
self.friendsanity.internal_name: self.friendsanity,
self.friendsanity_heart_size.internal_name: self.friendsanity_heart_size,
self.number_of_movement_buffs.internal_name: self.number_of_movement_buffs,
self.number_of_luck_buffs.internal_name: self.number_of_luck_buffs,
self.exclude_ginger_island.internal_name: self.exclude_ginger_island,
self.trap_items.internal_name: self.trap_items,
self.multiple_day_sleep_enabled.internal_name: self.multiple_day_sleep_enabled,
self.multiple_day_sleep_cost.internal_name: self.multiple_day_sleep_cost,
self.experience_multiplier.internal_name: self.experience_multiplier,
self.friendship_multiplier.internal_name: self.friendship_multiplier,
self.debris_multiplier.internal_name: self.debris_multiplier,
self.quick_start.internal_name: self.quick_start,
self.gifting.internal_name: self.gifting,
}
94 changes: 48 additions & 46 deletions worlds/stardew_valley/test/TestOptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from BaseClasses import ItemClassification, MultiWorld
from Options import SpecialRange
from . import setup_solo_multiworld, SVTestBase
from .. import StardewItem, options, items_by_group, Group, StardewValleyWorld
from .. import StardewItem, items_by_group, Group, StardewValleyWorld
from ..locations import locations_by_tag, LocationTags, location_table
from ..options import stardew_valley_option_classes
from ..strings.goal_names import Goal
from ..options import ExcludeGingerIsland, ToolProgression, Goal, SeasonRandomization, TrapItems, SpecialOrderLocations, ArcadeMachineLocations
from ..strings.goal_names import Goal as GoalName
from ..strings.season_names import Season
from ..strings.special_order_names import SpecialOrder
from ..strings.tool_names import ToolMaterial, Tool
Expand Down Expand Up @@ -50,9 +50,10 @@ def get_option_choices(option) -> Dict[str, int]:

class TestGenerateDynamicOptions(SVTestBase):
def test_given_special_range_when_generate_then_basic_checks(self):
for option in stardew_valley_option_classes:
option_name = option.internal_name
if not issubclass(option, SpecialRange):
options = self.world.options.get_options_by_name()
for option_name in options:
option = options[option_name]
if not isinstance(option, SpecialRange):
continue
for value in option.special_range_names:
with self.subTest(f"{option_name}: {value}"):
Expand All @@ -62,8 +63,9 @@ def test_given_special_range_when_generate_then_basic_checks(self):

def test_given_choice_when_generate_then_basic_checks(self):
seed = int(random() * pow(10, 18) - 1)
for option in stardew_valley_option_classes:
option_name = option.internal_name
options = self.world.options.get_options_by_name()
for option_name in options:
option = options[option_name]
if not option.options:
continue
for value in option.options:
Expand All @@ -75,39 +77,39 @@ def test_given_choice_when_generate_then_basic_checks(self):

class TestGoal(SVTestBase):
def test_given_goal_when_generate_then_victory_is_in_correct_location(self):
for goal, location in [("community_center", Goal.community_center),
("grandpa_evaluation", Goal.grandpa_evaluation),
("bottom_of_the_mines", Goal.bottom_of_the_mines),
("cryptic_note", Goal.cryptic_note),
("master_angler", Goal.master_angler),
("complete_collection", Goal.complete_museum),
("full_house", Goal.full_house),
("perfection", Goal.perfection)]:
for goal, location in [("community_center", GoalName.community_center),
("grandpa_evaluation", GoalName.grandpa_evaluation),
("bottom_of_the_mines", GoalName.bottom_of_the_mines),
("cryptic_note", GoalName.cryptic_note),
("master_angler", GoalName.master_angler),
("complete_collection", GoalName.complete_museum),
("full_house", GoalName.full_house),
("perfection", GoalName.perfection)]:
with self.subTest(msg=f"Goal: {goal}, Location: {location}"):
world_options = {options.Goal.internal_name: options.Goal.options[goal]}
world_options = {Goal.internal_name: Goal.options[goal]}
multi_world = setup_solo_multiworld(world_options)
victory = multi_world.find_item("Victory", 1)
self.assertEqual(victory.name, location)


class TestSeasonRandomization(SVTestBase):
def test_given_disabled_when_generate_then_all_seasons_are_precollected(self):
world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_disabled}
world_options = {SeasonRandomization.internal_name: SeasonRandomization.option_disabled}
multi_world = setup_solo_multiworld(world_options)

precollected_items = {item.name for item in multi_world.precollected_items[1]}
self.assertTrue(all([season in precollected_items for season in SEASONS]))

def test_given_randomized_when_generate_then_all_seasons_are_in_the_pool_or_precollected(self):
world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized}
world_options = {SeasonRandomization.internal_name: SeasonRandomization.option_randomized}
multi_world = setup_solo_multiworld(world_options)
precollected_items = {item.name for item in multi_world.precollected_items[1]}
items = {item.name for item in multi_world.get_items()} | precollected_items
self.assertTrue(all([season in items for season in SEASONS]))
self.assertEqual(len(SEASONS.intersection(precollected_items)), 1)

def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_pool(self):
world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive}
world_options = {SeasonRandomization.internal_name: SeasonRandomization.option_progressive}
multi_world = setup_solo_multiworld(world_options)

items = [item.name for item in multi_world.get_items()]
Expand All @@ -116,23 +118,23 @@ def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_p

class TestToolProgression(SVTestBase):
def test_given_vanilla_when_generate_then_no_tool_in_pool(self):
world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_vanilla}
world_options = {ToolProgression.internal_name: ToolProgression.option_vanilla}
multi_world = setup_solo_multiworld(world_options)

items = {item.name for item in multi_world.get_items()}
for tool in TOOLS:
self.assertNotIn(tool, items)

def test_given_progressive_when_generate_then_progressive_tool_of_each_is_in_pool_four_times(self):
world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_progressive}
world_options = {ToolProgression.internal_name: ToolProgression.option_progressive}
multi_world = setup_solo_multiworld(world_options)

items = [item.name for item in multi_world.get_items()]
for tool in TOOLS:
self.assertEqual(items.count("Progressive " + tool), 4)

def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self):
world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_progressive}
world_options = {ToolProgression.internal_name: ToolProgression.option_progressive}
multi_world = setup_solo_multiworld(world_options)

locations = {locations.name for locations in multi_world.get_locations(1)}
Expand All @@ -149,44 +151,44 @@ def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self):

class TestGenerateAllOptionsWithExcludeGingerIsland(SVTestBase):
def test_given_special_range_when_generate_exclude_ginger_island(self):
for option in stardew_valley_option_classes:
option_name = option.internal_name
if not issubclass(option,
SpecialRange) or option_name == options.ExcludeGingerIsland.internal_name:
options = self.world.options.get_options_by_name()
for option_name in options:
option = options[option_name]
if not isinstance(option, SpecialRange) or option_name == ExcludeGingerIsland.internal_name:
continue
for value in option.special_range_names:
with self.subTest(f"{option_name}: {value}"):
multiworld = setup_solo_multiworld(
{options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
{ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
option_name: option.special_range_names[value]})
check_no_ginger_island(self, multiworld)

def test_given_choice_when_generate_exclude_ginger_island(self):
seed = int(random() * pow(10, 18) - 1)
island_option = options.ExcludeGingerIsland
for option in stardew_valley_option_classes:
option_name = option.internal_name
if not option.options or option_name == island_option.internal_name:
options = self.world.options.get_options_by_name()
for option_name in options:
option = options[option_name]
if not option.options or option_name == ExcludeGingerIsland.internal_name:
continue
for value in option.options:
with self.subTest(f"{option_name}: {value} [Seed: {seed}]"):
multiworld = setup_solo_multiworld(
{island_option.internal_name: island_option.option_true,
{ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
option_name: option.options[value]}, seed)
stardew_world: StardewValleyWorld = multiworld.worlds[self.player]
if stardew_world.options.exclude_ginger_island != island_option.option_true:
if stardew_world.options.exclude_ginger_island != ExcludeGingerIsland.option_true:
continue
basic_checks(self, multiworld)
check_no_ginger_island(self, multiworld)

def test_given_island_related_goal_then_override_exclude_ginger_island(self):
island_goals = [value for value in options.Goal.options if value in ["walnut_hunter", "perfection"]]
island_option = options.ExcludeGingerIsland
island_goals = [value for value in Goal.options if value in ["walnut_hunter", "perfection"]]
island_option = ExcludeGingerIsland
for goal in island_goals:
for value in island_option.options:
with self.subTest(f"Goal: {goal}, {island_option.internal_name}: {value}"):
multiworld = setup_solo_multiworld(
{options.Goal.internal_name: options.Goal.options[goal],
{Goal.internal_name: Goal.options[goal],
island_option.internal_name: island_option.options[value]})
stardew_world: StardewValleyWorld = multiworld.worlds[self.player]
self.assertEqual(stardew_world.options.exclude_ginger_island, island_option.option_false)
Expand All @@ -196,7 +198,7 @@ def test_given_island_related_goal_then_override_exclude_ginger_island(self):
class TestTraps(SVTestBase):
def test_given_no_traps_when_generate_then_no_trap_in_pool(self):
world_options = self.allsanity_options_without_mods()
world_options.update({options.TrapItems.internal_name: options.TrapItems.option_no_traps})
world_options.update({TrapItems.internal_name: TrapItems.option_no_traps})
multi_world = setup_solo_multiworld(world_options)

trap_items = [item_data.name for item_data in items_by_group[Group.TRAP]]
Expand All @@ -207,12 +209,12 @@ def test_given_no_traps_when_generate_then_no_trap_in_pool(self):
self.assertNotIn(item, multiworld_items)

def test_given_traps_when_generate_then_all_traps_in_pool(self):
trap_option = options.TrapItems
trap_option = TrapItems
for value in trap_option.options:
if value == "no_traps":
continue
world_options = self.allsanity_options_with_mods()
world_options.update({options.TrapItems.internal_name: trap_option.options[value]})
world_options.update({TrapItems.internal_name: trap_option.options[value]})
multi_world = setup_solo_multiworld(world_options)
trap_items = [item_data.name for item_data in items_by_group[Group.TRAP] if Group.DEPRECATED not in item_data.groups and item_data.mod_name is None]
multiworld_items = [item.name for item in multi_world.get_items()]
Expand All @@ -223,7 +225,7 @@ def test_given_traps_when_generate_then_all_traps_in_pool(self):

class TestSpecialOrders(SVTestBase):
def test_given_disabled_then_no_order_in_pool(self):
world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_disabled}
world_options = {SpecialOrderLocations.internal_name: SpecialOrderLocations.option_disabled}
multi_world = setup_solo_multiworld(world_options)

locations_in_pool = {location.name for location in multi_world.get_locations() if location.name in location_table}
Expand All @@ -233,7 +235,7 @@ def test_given_disabled_then_no_order_in_pool(self):
self.assertNotIn(LocationTags.SPECIAL_ORDER_QI, location.tags)

def test_given_board_only_then_no_qi_order_in_pool(self):
world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_only}
world_options = {SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_only}
multi_world = setup_solo_multiworld(world_options)

locations_in_pool = {location.name for location in multi_world.get_locations() if location.name in location_table}
Expand All @@ -247,8 +249,8 @@ def test_given_board_only_then_no_qi_order_in_pool(self):
self.assertIn(board_location.name, locations_in_pool)

def test_given_board_and_qi_then_all_orders_in_pool(self):
world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_victories}
world_options = {SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_victories}
multi_world = setup_solo_multiworld(world_options)

locations_in_pool = {location.name for location in multi_world.get_locations()}
Expand All @@ -263,8 +265,8 @@ def test_given_board_and_qi_then_all_orders_in_pool(self):
self.assertIn(board_location.name, locations_in_pool)

def test_given_board_and_qi_without_arcade_machines_then_lets_play_a_game_not_in_pool(self):
world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled}
world_options = {SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_disabled}
multi_world = setup_solo_multiworld(world_options)

locations_in_pool = {location.name for location in multi_world.get_locations()}
Expand Down
Loading

0 comments on commit 228dfaa

Please sign in to comment.