forked from NikolajDanger/APSly3
170 lines
5.5 KiB
Python
170 lines
5.5 KiB
Python
from typing import Dict, List, Any, Optional, Mapping
|
|
import logging
|
|
|
|
from BaseClasses import Item, ItemClassification
|
|
from worlds.AutoWorld import World, WebWorld
|
|
from worlds.LauncherComponents import (
|
|
Component,
|
|
Type,
|
|
components,
|
|
launch,
|
|
icon_paths,
|
|
)
|
|
|
|
from .Sly3Options import sly3_option_groups, Sly3Options
|
|
from .Sly3Regions import create_regions_sly3
|
|
from .Sly3Pool import gen_pool_sly3
|
|
from .Sly3Rules import set_rules_sly3
|
|
from .data.Items import item_dict, item_groups, Sly3Item
|
|
from .data.Locations import location_dict, location_groups
|
|
from .data.Constants import EPISODES
|
|
|
|
## Client stuff
|
|
def run_client():
|
|
from .Sly3Client import launch_client
|
|
launch(launch_client, name="Sly3Client")
|
|
|
|
icon_paths["sly3_ico"] = f"ap:{__name__}/icon.png"
|
|
components.append(
|
|
Component("Sly 3 Client", func=run_client, component_type=Type.CLIENT, icon="sly3_ico")
|
|
)
|
|
|
|
|
|
## UT Stuff
|
|
def map_page_index(episode: str) -> int:
|
|
mapping = {k: i for i,k in enumerate(EPISODES.keys())}
|
|
|
|
return mapping.get(episode,0)
|
|
|
|
## The world
|
|
class Sly3Web(WebWorld):
|
|
game = "Sly 3: Honor Among Thieves"
|
|
option_groups = sly3_option_groups
|
|
|
|
class Sly3World(World):
|
|
"""
|
|
Sly 3: Honor Among Thieves is a 2004 stealth action video game developed by
|
|
Sucker Punch Productions and published by Sony Computer Entertainment for
|
|
the PlayStation 2.
|
|
"""
|
|
|
|
game = "Sly 3: Honor Among Thieves"
|
|
web = Sly3Web()
|
|
|
|
options_dataclass = Sly3Options
|
|
options: Sly3Options
|
|
topology_present = True
|
|
|
|
item_name_to_id = {item.name: item.code for item in item_dict.values()}
|
|
item_name_groups = item_groups
|
|
location_name_to_id = {
|
|
location.name: location.code for location in location_dict.values()
|
|
}
|
|
location_name_groups = location_groups
|
|
|
|
thiefnet_costs: List[int] = []
|
|
|
|
# this is how we tell the Universal Tracker we want to use re_gen_passthrough
|
|
@staticmethod
|
|
def interpret_slot_data(slot_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
return slot_data
|
|
|
|
# and this is how we tell Universal Tracker we don't need the yaml
|
|
ut_can_gen_without_yaml = True
|
|
|
|
def validate_options(self, opt: Sly3Options):
|
|
if opt.coins_maximum < opt.coins_minimum:
|
|
logging.warning(
|
|
f"{self.player_name}: " +
|
|
f"Coins minimum cannot be larger than maximum (min: {opt.coins_minimum}, max: {opt.coins_maximum}). Swapping values."
|
|
)
|
|
temp = opt.coins_minimum.value
|
|
opt.coins_minimum.value = opt.coins_maximum.value
|
|
opt.coins_maximum.value = temp
|
|
|
|
if opt.thiefnet_maximum < opt.thiefnet_minimum:
|
|
logging.warning(
|
|
f"{self.player_name}: " +
|
|
f"Thiefnet minimum cannot be larger than maximum (min: {opt.thiefnet_minimum}, max: {opt.thiefnet_maximum}). Swapping values."
|
|
)
|
|
temp = opt.thiefnet_minimum.value
|
|
opt.thiefnet_minimum.value = opt.thiefnet_maximum.value
|
|
opt.thiefnet_maximum.value = temp
|
|
|
|
def generate_early(self) -> None:
|
|
# implement .yaml-less Universal Tracker support
|
|
if hasattr(self.multiworld, "generation_is_fake"):
|
|
if hasattr(self.multiworld, "re_gen_passthrough"):
|
|
# I'm doing getattr purely so pylance stops being mad at me
|
|
re_gen_passthrough = getattr(self.multiworld, "re_gen_passthrough")
|
|
|
|
if "Sly 3: Honor Among Thieves" in re_gen_passthrough:
|
|
slot_data = re_gen_passthrough["Sly 3: Honor Among Thieves"]
|
|
self.thiefnet_costs = slot_data["thiefnet_costs"]
|
|
self.options.starting_episode.value = slot_data["starting_episode"]
|
|
self.options.goal.value = slot_data["goal"]
|
|
self.options.include_mega_jump.value = slot_data["include_mega_jump"]
|
|
self.options.coins_minimum.value = slot_data["coins_minimum"]
|
|
self.options.coins_maximum.value = slot_data["coins_maximum"]
|
|
self.options.thiefnet_locations.value = slot_data["thiefnet_locations"]
|
|
self.options.thiefnet_minimum.value = slot_data["thiefnet_minimum"]
|
|
self.options.thiefnet_maximum.value = slot_data["thiefnet_maximum"]
|
|
return
|
|
|
|
self.validate_options(self.options)
|
|
|
|
thiefnet_min = self.options.thiefnet_minimum.value
|
|
thiefnet_max = self.options.thiefnet_maximum.value
|
|
self.thiefnet_costs = sorted([
|
|
self.random.randint(thiefnet_min,thiefnet_max)
|
|
for _ in range(37)
|
|
])
|
|
|
|
def create_regions(self) -> None:
|
|
create_regions_sly3(self)
|
|
|
|
def get_filler_item_name(self) -> str:
|
|
# Currently just coins
|
|
return self.random.choice(list(self.item_name_groups["Filler"]))
|
|
|
|
def create_item(
|
|
self, name: str, override: Optional[ItemClassification] = None
|
|
) -> Item:
|
|
item = item_dict[name]
|
|
|
|
if override is not None:
|
|
return Sly3Item(name, override, item.code, self.player)
|
|
|
|
return Sly3Item(name, item.classification, item.code, self.player)
|
|
|
|
def create_event(self, name: str):
|
|
return Sly3Item(name, ItemClassification.progression, None, self.player)
|
|
|
|
def create_items(self) -> None:
|
|
items_to_add = gen_pool_sly3(self)
|
|
|
|
self.multiworld.itempool += items_to_add
|
|
|
|
def set_rules(self) -> None:
|
|
set_rules_sly3(self)
|
|
|
|
def get_options_as_dict(self) -> Dict[str, Any]:
|
|
return self.options.as_dict(
|
|
"death_link",
|
|
"starting_episode",
|
|
"goal",
|
|
"include_mega_jump",
|
|
"coins_minimum",
|
|
"coins_maximum",
|
|
"thiefnet_locations",
|
|
"thiefnet_minimum",
|
|
"thiefnet_maximum",
|
|
)
|
|
|
|
def fill_slot_data(self) -> Mapping[str, Any]:
|
|
slot_data = self.get_options_as_dict()
|
|
slot_data["thiefnet_costs"] = self.thiefnet_costs
|
|
|
|
return slot_data
|
|
|