|
| 1 | +from typing import Dict, Any, Iterable, Optional, Union |
| 2 | + |
| 3 | +from BaseClasses import Region, Entrance, Location, Item, Tutorial |
| 4 | +from worlds.AutoWorld import World, WebWorld |
| 5 | +from . import rules, logic, options |
| 6 | +from .bundles import get_all_bundles, Bundle |
| 7 | +from .items import item_table, create_items, ItemData, Group |
| 8 | +from .locations import location_table, create_locations, LocationData |
| 9 | +from .logic import StardewLogic, StardewRule, _True, _And |
| 10 | +from .options import stardew_valley_options, StardewOptions, fetch_options |
| 11 | +from .regions import create_regions |
| 12 | +from .rules import set_rules |
| 13 | + |
| 14 | +client_version = 0 |
| 15 | + |
| 16 | + |
| 17 | +class StardewLocation(Location): |
| 18 | + game: str = "Stardew Valley" |
| 19 | + |
| 20 | + def __init__(self, player: int, name: str, address: Optional[int], parent=None): |
| 21 | + super().__init__(player, name, address, parent) |
| 22 | + self.event = not address |
| 23 | + |
| 24 | + |
| 25 | +class StardewItem(Item): |
| 26 | + game: str = "Stardew Valley" |
| 27 | + |
| 28 | + |
| 29 | +class StardewWebWorld(WebWorld): |
| 30 | + theme = "dirt" |
| 31 | + bug_report_page = "https://github.com/agilbert1412/StardewArchipelago/issues/new?labels=bug&title=%5BBug%5D%3A+Brief+Description+of+bug+here" |
| 32 | + |
| 33 | + tutorials = [Tutorial( |
| 34 | + "Multiworld Setup Guide", |
| 35 | + "A guide to playing Stardew Valley with Archipelago.", |
| 36 | + "English", |
| 37 | + "setup_en.md", |
| 38 | + "setup/en", |
| 39 | + ["KaitoKid", "Jouramie"] |
| 40 | + )] |
| 41 | + |
| 42 | + |
| 43 | +class StardewValleyWorld(World): |
| 44 | + """ |
| 45 | + Stardew Valley farming simulator game where the objective is basically to spend the least possible time on your farm. |
| 46 | + """ |
| 47 | + game = "Stardew Valley" |
| 48 | + option_definitions = stardew_valley_options |
| 49 | + topology_present = False |
| 50 | + |
| 51 | + item_name_to_id = {name: data.code for name, data in item_table.items()} |
| 52 | + location_name_to_id = {name: data.code for name, data in location_table.items()} |
| 53 | + |
| 54 | + data_version = 1 |
| 55 | + required_client_version = (0, 3, 9) |
| 56 | + |
| 57 | + options: StardewOptions |
| 58 | + logic: StardewLogic |
| 59 | + |
| 60 | + web = StardewWebWorld() |
| 61 | + modified_bundles: Dict[str, Bundle] |
| 62 | + randomized_entrances: Dict[str, str] |
| 63 | + |
| 64 | + def generate_early(self): |
| 65 | + self.options = fetch_options(self.multiworld, self.player) |
| 66 | + self.logic = StardewLogic(self.player, self.options) |
| 67 | + self.modified_bundles = get_all_bundles(self.multiworld.random, |
| 68 | + self.logic, |
| 69 | + self.options[options.BundleRandomization], |
| 70 | + self.options[options.BundlePrice]) |
| 71 | + |
| 72 | + def create_regions(self): |
| 73 | + def create_region(name: str, exits: Iterable[str]) -> Region: |
| 74 | + region = Region(name, self.player, self.multiworld) |
| 75 | + region.exits = [Entrance(self.player, exit_name, region) for exit_name in exits] |
| 76 | + return region |
| 77 | + |
| 78 | + world_regions, self.randomized_entrances = create_regions(create_region, self.multiworld.random, self.options) |
| 79 | + self.multiworld.regions.extend(world_regions) |
| 80 | + |
| 81 | + def add_location(name: str, code: Optional[int], region: str): |
| 82 | + region = self.multiworld.get_region(region, self.player) |
| 83 | + location = StardewLocation(self.player, name, code, region) |
| 84 | + location.access_rule = lambda _: True |
| 85 | + region.locations.append(location) |
| 86 | + |
| 87 | + create_locations(add_location, self.options, self.multiworld.random) |
| 88 | + |
| 89 | + def create_items(self): |
| 90 | + locations_count = len([location |
| 91 | + for location in self.multiworld.get_locations(self.player) |
| 92 | + if not location.event]) |
| 93 | + items_to_exclude = [excluded_items |
| 94 | + for excluded_items in self.multiworld.precollected_items[self.player] |
| 95 | + if not item_table[excluded_items.name].has_any_group(Group.RESOURCE_PACK, |
| 96 | + Group.FRIENDSHIP_PACK)] |
| 97 | + created_items = create_items(self.create_item, locations_count + len(items_to_exclude), self.options, |
| 98 | + self.multiworld.random) |
| 99 | + self.multiworld.itempool += created_items |
| 100 | + |
| 101 | + for item in items_to_exclude: |
| 102 | + self.multiworld.itempool.remove(item) |
| 103 | + |
| 104 | + self.setup_season_events() |
| 105 | + self.setup_victory() |
| 106 | + |
| 107 | + def set_rules(self): |
| 108 | + set_rules(self.multiworld, self.player, self.options, self.logic, self.modified_bundles) |
| 109 | + |
| 110 | + def create_item(self, item: Union[str, ItemData]) -> StardewItem: |
| 111 | + if isinstance(item, str): |
| 112 | + item = item_table[item] |
| 113 | + |
| 114 | + return StardewItem(item.name, item.classification, item.code, self.player) |
| 115 | + |
| 116 | + def setup_season_events(self): |
| 117 | + self.multiworld.push_precollected(self.create_item("Spring")) |
| 118 | + self.create_event_location(location_table["Summer"], self.logic.received("Spring"), "Summer") |
| 119 | + self.create_event_location(location_table["Fall"], self.logic.received("Summer"), "Fall") |
| 120 | + self.create_event_location(location_table["Winter"], self.logic.received("Fall"), "Winter") |
| 121 | + self.create_event_location(location_table["Year Two"], self.logic.received("Winter"), "Year Two") |
| 122 | + |
| 123 | + def setup_victory(self): |
| 124 | + if self.options[options.Goal] == options.Goal.option_community_center: |
| 125 | + self.create_event_location(location_table["Complete Community Center"], |
| 126 | + self.logic.can_complete_community_center().simplify(), |
| 127 | + "Victory") |
| 128 | + elif self.options[options.Goal] == options.Goal.option_grandpa_evaluation: |
| 129 | + self.create_event_location(location_table["Succeed Grandpa's Evaluation"], |
| 130 | + self.logic.can_finish_grandpa_evaluation().simplify(), |
| 131 | + "Victory") |
| 132 | + elif self.options[options.Goal] == options.Goal.option_bottom_of_the_mines: |
| 133 | + self.create_event_location(location_table["Reach the Bottom of The Mines"], |
| 134 | + self.logic.can_mine_to_floor(120).simplify(), |
| 135 | + "Victory") |
| 136 | + elif self.options[options.Goal] == options.Goal.option_cryptic_note: |
| 137 | + self.create_event_location(location_table["Complete Quest Cryptic Note"], |
| 138 | + self.logic.can_complete_quest("Cryptic Note").simplify(), |
| 139 | + "Victory") |
| 140 | + elif self.options[options.Goal] == options.Goal.option_master_angler: |
| 141 | + self.create_event_location(location_table["Catch Every Fish"], |
| 142 | + self.logic.can_catch_every_fish().simplify(), |
| 143 | + "Victory") |
| 144 | + |
| 145 | + self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player) |
| 146 | + |
| 147 | + def create_event_location(self, location_data: LocationData, rule: StardewRule, item: str): |
| 148 | + region = self.multiworld.get_region(location_data.region, self.player) |
| 149 | + location = StardewLocation(self.player, location_data.name, None, region) |
| 150 | + location.access_rule = rule |
| 151 | + region.locations.append(location) |
| 152 | + location.place_locked_item(self.create_item(item)) |
| 153 | + |
| 154 | + def get_filler_item_name(self) -> str: |
| 155 | + return "Joja Cola" |
| 156 | + |
| 157 | + def fill_slot_data(self) -> Dict[str, Any]: |
| 158 | + |
| 159 | + modified_bundles = {} |
| 160 | + for bundle_key in self.modified_bundles: |
| 161 | + key, value = self.modified_bundles[bundle_key].to_pair() |
| 162 | + modified_bundles[key] = value |
| 163 | + |
| 164 | + return { |
| 165 | + "starting_money": self.options[options.StartingMoney], |
| 166 | + "entrance_randomization": self.options[options.EntranceRandomization], |
| 167 | + "backpack_progression": self.options[options.BackpackProgression], |
| 168 | + "tool_progression": self.options[options.ToolProgression], |
| 169 | + "elevator_progression": self.options[options.TheMinesElevatorsProgression], |
| 170 | + "skill_progression": self.options[options.SkillProgression], |
| 171 | + "building_progression": self.options[options.BuildingProgression], |
| 172 | + "arcade_machine_progression": self.options[options.ArcadeMachineLocations], |
| 173 | + "help_wanted_locations": self.options[options.HelpWantedLocations], |
| 174 | + "fishsanity": self.options[options.Fishsanity], |
| 175 | + "death_link": self.options["death_link"], |
| 176 | + "goal": self.options[options.Goal], |
| 177 | + "seed": self.multiworld.per_slot_randoms[self.player].randrange(1000000000), # Seed should be max 9 digits |
| 178 | + "multiple_day_sleep_enabled": self.options[options.MultipleDaySleepEnabled], |
| 179 | + "multiple_day_sleep_cost": self.options[options.MultipleDaySleepCost], |
| 180 | + "experience_multiplier": self.options[options.ExperienceMultiplier], |
| 181 | + "debris_multiplier": self.options[options.DebrisMultiplier], |
| 182 | + "quick_start": self.options[options.QuickStart], |
| 183 | + "gifting": self.options[options.Gifting], |
| 184 | + "gift_tax": self.options[options.GiftTax], |
| 185 | + "modified_bundles": modified_bundles, |
| 186 | + "randomized_entrances": self.randomized_entrances, |
| 187 | + "client_version": "2.2.2", |
| 188 | + } |
0 commit comments