mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Stardew Valley: 5.x.x - The Allsanity Update (#2764)
Major Content update for Stardew Valley, including the following features - Major performance improvements all across the Stardew Valley apworld, including a significant reduction in the test time - Randomized Farm Type - Bundles rework (Remixed Bundles and Missing Bundle!) - New Settings: * Shipsanity - Shipping individual items * Monstersanity - Slaying monsters * Cooksanity - Cooking individual recipes * Chefsanity - Learning individual recipes * Craftsanity - Crafting individual items - New Goals: * Protector of the Valley - Complete every monster slayer goal * Full Shipment - Ship every item * Craftmaster - Craft every item * Gourmet Chef - Cook every recipe * Legend - Earn 10 000 000g * Mystery of the Stardrops - Find every stardrop (Maguffin Hunt) * Allsanity - Complete every check in your slot - Building Shuffle: Cheaper options - Tool Shuffle: Cheaper options - Money rework - New traps - New isolated checks and items, including the farm cave, the movie theater, etc - Mod Support: SVE [Albrekka] - Mod Support: Distant Lands [Albrekka] - Mod Support: Hat Mouse Lacey [Albrekka] - Mod Support: Boarding House [Albrekka] Co-authored-by: Witchybun <elnendil@gmail.com> Co-authored-by: Witchybun <96719127+Witchybun@users.noreply.github.com> Co-authored-by: Jouramie <jouramie@hotmail.com> Co-authored-by: Alchav <59858495+Alchav@users.noreply.github.com>
This commit is contained in:
@@ -1,21 +1,28 @@
|
||||
import logging
|
||||
from typing import Dict, Any, Iterable, Optional, Union, Set, List
|
||||
from typing import Dict, Any, Iterable, Optional, Union, List, TextIO
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, CollectionState, ItemClassification, MultiWorld, Group as ItemLinkGroup
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld
|
||||
from Options import PerGameCommonOptions
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from . import rules
|
||||
from .bundles import get_all_bundles, Bundle
|
||||
from .bundles.bundle_room import BundleRoom
|
||||
from .bundles.bundles import get_all_bundles
|
||||
from .early_items import setup_early_items
|
||||
from .items import item_table, create_items, ItemData, Group, items_by_group, get_all_filler_items, remove_limited_amount_packs
|
||||
from .locations import location_table, create_locations, LocationData
|
||||
from .logic import StardewLogic, StardewRule, True_, MAX_MONTHS
|
||||
from .locations import location_table, create_locations, LocationData, locations_by_tag
|
||||
from .logic.bundle_logic import BundleLogic
|
||||
from .logic.logic import StardewLogic
|
||||
from .logic.time_logic import MAX_MONTHS
|
||||
from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, BundlePrice, NumberOfLuckBuffs, NumberOfMovementBuffs, \
|
||||
BackpackProgression, BuildingProgression, ExcludeGingerIsland, TrapItems
|
||||
BackpackProgression, BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization
|
||||
from .presets import sv_options_presets
|
||||
from .regions import create_regions
|
||||
from .rules import set_rules
|
||||
from worlds.generic.Rules import set_rule
|
||||
from .stardew_rule import True_, StardewRule, HasProgressionPercent
|
||||
from .strings.ap_names.event_names import Event
|
||||
from .strings.entrance_names import Entrance as EntranceName
|
||||
from .strings.goal_names import Goal as GoalName
|
||||
from .strings.region_names import Region as RegionName
|
||||
|
||||
client_version = 0
|
||||
|
||||
@@ -59,6 +66,15 @@ class StardewValleyWorld(World):
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = {name: data.code for name, data in location_table.items()}
|
||||
|
||||
item_name_groups = {
|
||||
group.name.replace("_", " ").title() + (" Group" if group.name.replace("_", " ").title() in item_table else ""):
|
||||
[item.name for item in items] for group, items in items_by_group.items()
|
||||
}
|
||||
location_name_groups = {
|
||||
group.name.replace("_", " ").title() + (" Group" if group.name.replace("_", " ").title() in locations_by_tag else ""):
|
||||
[location.name for location in locations] for group, locations in locations_by_tag.items()
|
||||
}
|
||||
|
||||
data_version = 3
|
||||
required_client_version = (0, 4, 0)
|
||||
|
||||
@@ -67,24 +83,21 @@ class StardewValleyWorld(World):
|
||||
logic: StardewLogic
|
||||
|
||||
web = StardewWebWorld()
|
||||
modified_bundles: Dict[str, Bundle]
|
||||
modified_bundles: List[BundleRoom]
|
||||
randomized_entrances: Dict[str, str]
|
||||
all_progression_items: Set[str]
|
||||
total_progression_items: int
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
super().__init__(world, player)
|
||||
self.all_progression_items = set()
|
||||
# all_progression_items: Dict[str, int] # If you need to debug total_progression_items, uncommenting this will help tremendously
|
||||
|
||||
def __init__(self, multiworld: MultiWorld, player: int):
|
||||
super().__init__(multiworld, player)
|
||||
self.filler_item_pool_names = []
|
||||
self.total_progression_items = 0
|
||||
# self.all_progression_items = dict()
|
||||
|
||||
def generate_early(self):
|
||||
self.force_change_options_if_incompatible()
|
||||
|
||||
self.logic = StardewLogic(self.player, self.options)
|
||||
self.modified_bundles = get_all_bundles(self.multiworld.random,
|
||||
self.logic,
|
||||
self.options.bundle_randomization,
|
||||
self.options.bundle_price)
|
||||
|
||||
def force_change_options_if_incompatible(self):
|
||||
goal_is_walnut_hunter = self.options.goal == Goal.option_greatest_walnut_hunter
|
||||
goal_is_perfection = self.options.goal == Goal.option_perfection
|
||||
@@ -94,7 +107,8 @@ class StardewValleyWorld(World):
|
||||
self.options.exclude_ginger_island.value = 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})")
|
||||
logging.warning(
|
||||
f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})")
|
||||
|
||||
def create_regions(self):
|
||||
def create_region(name: str, exits: Iterable[str]) -> Region:
|
||||
@@ -102,15 +116,19 @@ class StardewValleyWorld(World):
|
||||
region.exits = [Entrance(self.player, exit_name, region) for exit_name in exits]
|
||||
return region
|
||||
|
||||
world_regions, self.randomized_entrances = create_regions(create_region, self.multiworld.random, self.options)
|
||||
world_regions, world_entrances, self.randomized_entrances = create_regions(create_region, self.random, self.options)
|
||||
|
||||
self.logic = StardewLogic(self.player, self.options, world_regions.keys())
|
||||
self.modified_bundles = get_all_bundles(self.random,
|
||||
self.logic,
|
||||
self.options)
|
||||
|
||||
def add_location(name: str, code: Optional[int], region: str):
|
||||
region = world_regions[region]
|
||||
location = StardewLocation(self.player, name, code, region)
|
||||
location.access_rule = lambda _: True
|
||||
region.locations.append(location)
|
||||
|
||||
create_locations(add_location, self.options, self.multiworld.random)
|
||||
create_locations(add_location, self.modified_bundles, self.options, self.random)
|
||||
self.multiworld.regions.extend(world_regions.values())
|
||||
|
||||
def create_items(self):
|
||||
@@ -128,16 +146,16 @@ class StardewValleyWorld(World):
|
||||
for location in self.multiworld.get_locations(self.player)
|
||||
if not location.event])
|
||||
|
||||
created_items = create_items(self.create_item, locations_count, items_to_exclude, self.options,
|
||||
self.multiworld.random)
|
||||
created_items = create_items(self.create_item, self.delete_item, locations_count, items_to_exclude, self.options,
|
||||
self.random)
|
||||
|
||||
self.multiworld.itempool += created_items
|
||||
|
||||
self.setup_early_items()
|
||||
self.setup_month_events()
|
||||
setup_early_items(self.multiworld, self.options, self.player, self.random)
|
||||
self.setup_player_events()
|
||||
self.setup_victory()
|
||||
|
||||
def precollect_starting_season(self) -> Optional[StardewItem]:
|
||||
def precollect_starting_season(self):
|
||||
if self.options.season_randomization == SeasonRandomization.option_progressive:
|
||||
return
|
||||
|
||||
@@ -145,7 +163,7 @@ class StardewValleyWorld(World):
|
||||
|
||||
if self.options.season_randomization == SeasonRandomization.option_disabled:
|
||||
for season in season_pool:
|
||||
self.multiworld.push_precollected(self.create_item(season))
|
||||
self.multiworld.push_precollected(self.create_starting_item(season))
|
||||
return
|
||||
|
||||
if [item for item in self.multiworld.precollected_items[self.player]
|
||||
@@ -155,75 +173,128 @@ class StardewValleyWorld(World):
|
||||
if self.options.season_randomization == SeasonRandomization.option_randomized_not_winter:
|
||||
season_pool = [season for season in season_pool if season.name != "Winter"]
|
||||
|
||||
starting_season = self.create_item(self.multiworld.random.choice(season_pool))
|
||||
starting_season = self.create_starting_item(self.random.choice(season_pool))
|
||||
self.multiworld.push_precollected(starting_season)
|
||||
|
||||
def setup_early_items(self):
|
||||
if (self.options.building_progression ==
|
||||
BuildingProgression.option_progressive_early_shipping_bin):
|
||||
self.multiworld.early_items[self.player]["Shipping Bin"] = 1
|
||||
def setup_player_events(self):
|
||||
self.setup_construction_events()
|
||||
self.setup_quest_events()
|
||||
self.setup_action_events()
|
||||
|
||||
if self.options.backpack_progression == BackpackProgression.option_early_progressive:
|
||||
self.multiworld.early_items[self.player]["Progressive Backpack"] = 1
|
||||
def setup_construction_events(self):
|
||||
can_construct_buildings = LocationData(None, RegionName.carpenter, Event.can_construct_buildings)
|
||||
self.create_event_location(can_construct_buildings, True_(), Event.can_construct_buildings)
|
||||
|
||||
def setup_month_events(self):
|
||||
for i in range(0, MAX_MONTHS):
|
||||
month_end = LocationData(None, "Stardew Valley", f"Month End {i + 1}")
|
||||
if i == 0:
|
||||
self.create_event_location(month_end, True_(), "Month End")
|
||||
continue
|
||||
def setup_quest_events(self):
|
||||
start_dark_talisman_quest = LocationData(None, RegionName.railroad, Event.start_dark_talisman_quest)
|
||||
self.create_event_location(start_dark_talisman_quest, self.logic.wallet.has_rusty_key(), Event.start_dark_talisman_quest)
|
||||
|
||||
self.create_event_location(month_end, self.logic.received("Month End", i).simplify(), "Month End")
|
||||
def setup_action_events(self):
|
||||
can_ship_event = LocationData(None, RegionName.shipping, Event.can_ship_items)
|
||||
self.create_event_location(can_ship_event, True_(), Event.can_ship_items)
|
||||
can_shop_pierre_event = LocationData(None, RegionName.pierre_store, Event.can_shop_at_pierre)
|
||||
self.create_event_location(can_shop_pierre_event, True_(), Event.can_shop_at_pierre)
|
||||
|
||||
def setup_victory(self):
|
||||
if self.options.goal == Goal.option_community_center:
|
||||
self.create_event_location(location_table[GoalName.community_center],
|
||||
self.logic.can_complete_community_center().simplify(),
|
||||
"Victory")
|
||||
self.logic.bundle.can_complete_community_center,
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_grandpa_evaluation:
|
||||
self.create_event_location(location_table[GoalName.grandpa_evaluation],
|
||||
self.logic.can_finish_grandpa_evaluation().simplify(),
|
||||
"Victory")
|
||||
self.logic.can_finish_grandpa_evaluation(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_bottom_of_the_mines:
|
||||
self.create_event_location(location_table[GoalName.bottom_of_the_mines],
|
||||
self.logic.can_mine_to_floor(120).simplify(),
|
||||
"Victory")
|
||||
True_(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_cryptic_note:
|
||||
self.create_event_location(location_table[GoalName.cryptic_note],
|
||||
self.logic.can_complete_quest("Cryptic Note").simplify(),
|
||||
"Victory")
|
||||
self.logic.quest.can_complete_quest("Cryptic Note"),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_master_angler:
|
||||
self.create_event_location(location_table[GoalName.master_angler],
|
||||
self.logic.can_catch_every_fish().simplify(),
|
||||
"Victory")
|
||||
self.logic.fishing.can_catch_every_fish_in_slot(self.get_all_location_names()),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_complete_collection:
|
||||
self.create_event_location(location_table[GoalName.complete_museum],
|
||||
self.logic.can_complete_museum().simplify(),
|
||||
"Victory")
|
||||
self.logic.museum.can_complete_museum(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_full_house:
|
||||
self.create_event_location(location_table[GoalName.full_house],
|
||||
(self.logic.has_children(2) & self.logic.can_reproduce()).simplify(),
|
||||
"Victory")
|
||||
(self.logic.relationship.has_children(2) & self.logic.relationship.can_reproduce()),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_greatest_walnut_hunter:
|
||||
self.create_event_location(location_table[GoalName.greatest_walnut_hunter],
|
||||
self.logic.has_walnut(130).simplify(),
|
||||
"Victory")
|
||||
self.logic.has_walnut(130),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_protector_of_the_valley:
|
||||
self.create_event_location(location_table[GoalName.protector_of_the_valley],
|
||||
self.logic.monster.can_complete_all_monster_slaying_goals(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_full_shipment:
|
||||
self.create_event_location(location_table[GoalName.full_shipment],
|
||||
self.logic.shipping.can_ship_everything_in_slot(self.get_all_location_names()),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_gourmet_chef:
|
||||
self.create_event_location(location_table[GoalName.gourmet_chef],
|
||||
self.logic.cooking.can_cook_everything,
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_craft_master:
|
||||
self.create_event_location(location_table[GoalName.craft_master],
|
||||
self.logic.crafting.can_craft_everything,
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_legend:
|
||||
self.create_event_location(location_table[GoalName.legend],
|
||||
self.logic.money.can_have_earned_total(10_000_000),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_mystery_of_the_stardrops:
|
||||
self.create_event_location(location_table[GoalName.mystery_of_the_stardrops],
|
||||
self.logic.has_all_stardrops(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_allsanity:
|
||||
self.create_event_location(location_table[GoalName.allsanity],
|
||||
HasProgressionPercent(self.player, 100),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_perfection:
|
||||
self.create_event_location(location_table[GoalName.perfection],
|
||||
self.logic.has_everything(self.all_progression_items).simplify(),
|
||||
"Victory")
|
||||
HasProgressionPercent(self.player, 100),
|
||||
Event.victory)
|
||||
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has(Event.victory, self.player)
|
||||
|
||||
def create_item(self, item: Union[str, ItemData]) -> StardewItem:
|
||||
def get_all_location_names(self) -> List[str]:
|
||||
return list(location.name for location in self.multiworld.get_locations(self.player))
|
||||
|
||||
def create_item(self, item: Union[str, ItemData], override_classification: ItemClassification = None) -> StardewItem:
|
||||
if isinstance(item, str):
|
||||
item = item_table[item]
|
||||
|
||||
if override_classification is None:
|
||||
override_classification = item.classification
|
||||
|
||||
if override_classification == ItemClassification.progression and item.name != Event.victory:
|
||||
self.total_progression_items += 1
|
||||
# if item.name not in self.all_progression_items:
|
||||
# self.all_progression_items[item.name] = 0
|
||||
# self.all_progression_items[item.name] += 1
|
||||
return StardewItem(item.name, override_classification, item.code, self.player)
|
||||
|
||||
def delete_item(self, item: Item):
|
||||
if item.classification & ItemClassification.progression:
|
||||
self.total_progression_items -= 1
|
||||
# if item.name in self.all_progression_items:
|
||||
# self.all_progression_items[item.name] -= 1
|
||||
|
||||
def create_starting_item(self, item: Union[str, ItemData]) -> StardewItem:
|
||||
if isinstance(item, str):
|
||||
item = item_table[item]
|
||||
|
||||
if item.classification == ItemClassification.progression:
|
||||
self.all_progression_items.add(item.name)
|
||||
return StardewItem(item.name, item.classification, item.code, self.player)
|
||||
|
||||
def create_event_location(self, location_data: LocationData, rule: StardewRule, item: Optional[str] = None):
|
||||
def create_event_location(self, location_data: LocationData, rule: StardewRule = None, item: Optional[str] = None):
|
||||
if rule is None:
|
||||
rule = True_()
|
||||
if item is None:
|
||||
item = location_data.name
|
||||
|
||||
@@ -235,37 +306,6 @@ class StardewValleyWorld(World):
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self)
|
||||
self.force_first_month_once_all_early_items_are_found()
|
||||
|
||||
def force_first_month_once_all_early_items_are_found(self):
|
||||
"""
|
||||
The Fill algorithm sweeps all event when calculating the early location. This causes an issue where
|
||||
location only locked behind event are considered early, which they are not really...
|
||||
|
||||
This patches the issue, by adding a dependency to the first month end on all early items, so all the locations
|
||||
that depends on it will not be considered early. This requires at least one early item to be progression, or
|
||||
it just won't work...
|
||||
"""
|
||||
|
||||
early_items = []
|
||||
for player, item_count in self.multiworld.early_items.items():
|
||||
for item, count in item_count.items():
|
||||
if self.multiworld.worlds[player].create_item(item).advancement:
|
||||
early_items.append((player, item, count))
|
||||
|
||||
for item, count in self.multiworld.local_early_items[self.player].items():
|
||||
if self.create_item(item).advancement:
|
||||
early_items.append((self.player, item, count))
|
||||
|
||||
def first_month_require_all_early_items(state: CollectionState) -> bool:
|
||||
for player, item, count in early_items:
|
||||
if not state.has(item, player, count):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
first_month_end = self.multiworld.get_location("Month End 1", self.player)
|
||||
set_rule(first_month_end, first_month_require_all_early_items)
|
||||
|
||||
def generate_basic(self):
|
||||
pass
|
||||
@@ -283,13 +323,12 @@ class StardewValleyWorld(World):
|
||||
|
||||
def get_filler_item_rules(self):
|
||||
if self.player in self.multiworld.groups:
|
||||
link_group: ItemLinkGroup = self.multiworld.groups[self.player]
|
||||
link_group = self.multiworld.groups[self.player]
|
||||
include_traps = True
|
||||
exclude_island = False
|
||||
for player in link_group["players"]:
|
||||
player_options = self.multiworld.worlds[player].options
|
||||
if self.multiworld.game[player] != self.game:
|
||||
|
||||
continue
|
||||
if player_options.trap_items == TrapItems.option_no_traps:
|
||||
include_traps = False
|
||||
@@ -299,24 +338,57 @@ class StardewValleyWorld(World):
|
||||
else:
|
||||
return self.options.trap_items != TrapItems.option_no_traps, self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
|
||||
def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
|
||||
"""Write to the spoiler header. If individual it's right at the end of that player's options,
|
||||
if as stage it's right under the common header before per-player options."""
|
||||
self.add_entrances_to_spoiler_log()
|
||||
|
||||
def write_spoiler(self, spoiler_handle: TextIO) -> None:
|
||||
"""Write to the spoiler "middle", this is after the per-player options and before locations,
|
||||
meant for useful or interesting info."""
|
||||
self.add_bundles_to_spoiler_log(spoiler_handle)
|
||||
|
||||
def add_bundles_to_spoiler_log(self, spoiler_handle: TextIO):
|
||||
if self.options.bundle_randomization == BundleRandomization.option_vanilla:
|
||||
return
|
||||
player_name = self.multiworld.get_player_name(self.player)
|
||||
spoiler_handle.write(f"\n\nCommunity Center ({player_name}):\n")
|
||||
for room in self.modified_bundles:
|
||||
for bundle in room.bundles:
|
||||
spoiler_handle.write(f"\t[{room.name}] {bundle.name} ({bundle.number_required} required):\n")
|
||||
for i, item in enumerate(bundle.items):
|
||||
if "Basic" in item.quality:
|
||||
quality = ""
|
||||
else:
|
||||
quality = f" ({item.quality.split(' ')[0]})"
|
||||
spoiler_handle.write(f"\t\t{item.amount}x {item.item_name}{quality}\n")
|
||||
|
||||
def add_entrances_to_spoiler_log(self):
|
||||
if self.options.entrance_randomization == EntranceRandomization.option_disabled:
|
||||
return
|
||||
for original_entrance, replaced_entrance in self.randomized_entrances.items():
|
||||
self.multiworld.spoiler.set_entrance(original_entrance, replaced_entrance, "entrance", self.player)
|
||||
|
||||
def fill_slot_data(self) -> Dict[str, Any]:
|
||||
bundles = dict()
|
||||
for room in self.modified_bundles:
|
||||
bundles[room.name] = dict()
|
||||
for bundle in room.bundles:
|
||||
bundles[room.name][bundle.name] = {"number_required": bundle.number_required}
|
||||
for i, item in enumerate(bundle.items):
|
||||
bundles[room.name][bundle.name][i] = f"{item.item_name}|{item.amount}|{item.quality}"
|
||||
|
||||
modified_bundles = {}
|
||||
for bundle_key in self.modified_bundles:
|
||||
key, value = self.modified_bundles[bundle_key].to_pair()
|
||||
modified_bundles[key] = value
|
||||
|
||||
excluded_options = [BundleRandomization, BundlePrice, NumberOfMovementBuffs, NumberOfLuckBuffs]
|
||||
excluded_options = [BundleRandomization, NumberOfMovementBuffs, NumberOfLuckBuffs]
|
||||
excluded_option_names = [option.internal_name for option in excluded_options]
|
||||
generic_option_names = [option_name for option_name in PerGameCommonOptions.type_hints]
|
||||
excluded_option_names.extend(generic_option_names)
|
||||
included_option_names: List[str] = [option_name for option_name in self.options_dataclass.type_hints if option_name not in excluded_option_names]
|
||||
slot_data = self.options.as_dict(*included_option_names)
|
||||
slot_data.update({
|
||||
"seed": self.multiworld.per_slot_randoms[self.player].randrange(1000000000), # Seed should be max 9 digits
|
||||
"seed": self.random.randrange(1000000000), # Seed should be max 9 digits
|
||||
"randomized_entrances": self.randomized_entrances,
|
||||
"modified_bundles": modified_bundles,
|
||||
"client_version": "4.0.0",
|
||||
"modified_bundles": bundles,
|
||||
"client_version": "5.0.0",
|
||||
})
|
||||
|
||||
return slot_data
|
||||
|
Reference in New Issue
Block a user