1152ba056fac2112a0ba98cbb1f3430ee3616d03
[sicpelago] / apworld / sicp / __init__.py
1 import itertools
2 import logging
3 import os
4 import sys
5 import yaml
6
7 from typing import Any
8
9 import BaseClasses
10 from BaseClasses import ItemClassification, Region
11
12 from worlds.AutoWorld import World
13 from worlds.LauncherComponents import Component, SuffixIdentifier, components, Type, launch
14
15 from .locations import all_locations, Location, LocationData, OriginRegionName, set_up_rules
16
17 GAME = "SICPelago"
18
19 class Item(BaseClasses.Item):
20     game = GAME
21
22 class ItemData:
23     def __init__(self, data: dict[str, Any]):
24         self.data = data
25     def __str__(self):
26         return self.data['name']
27     def id(self):
28         return self.data['id']
29     def kind(self) -> ItemClassification:
30         kind = self.data.get('kind')
31         if kind == 'progression':
32             return ItemClassification.progression
33         if kind == 'useful_progression':
34             return ItemClassification.progression | ItemClassification.useful
35         if kind == 'useful':
36             return ItemClassification.useful
37         if kind == 'progression_skip_balancing':
38             return ItemClassification.progression_skip_balancing
39         if kind == 'trap':
40             return ItemClassification.trap
41         return ItemClassification.filler
42
43 with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'items.yml'), 'r') as input:
44     all_items = {data['name']: ItemData(data) for data in yaml.safe_load(input)}
45
46
47 class SicpWorld(World):
48     game = GAME
49     topology_present = True
50     item_name_to_id = {k: v.id() for k,v in all_items.items()}
51     location_name_to_id = {**{f"{loc} A": 10 * loc.id() for loc in all_locations},
52                            **{f"{loc} B": 10 * loc.id() + 1 for loc in all_locations}}
53     origin_region_name = OriginRegionName
54
55     def create_item(self, name: str) -> Item:
56         if name == 'junk':
57             return Item(name, ItemClassification.filler, 0, self.player)
58         data = all_items[name]
59         return Item(name, data.kind(), data.id(), self.player)
60
61     def create_regions(self) -> None:
62         repl = Region(self.origin_region_name, self.player, self.multiworld)
63         self.multiworld.regions.append(repl)
64
65         for loc in all_locations:
66             region = Region(str(loc), self.player, self.multiworld)
67             self.multiworld.regions.append(region)
68             region.locations = [
69                 Location(self.player, f"{loc} A", 10 * loc.id(), region),
70                 Location(self.player, f"{loc} B", 10 * loc.id() + 1, region)
71             ]
72             set_up_rules(loc, repl, region, self.player)
73
74     def create_items(self) -> None:
75         exclude = [item for item in self.multiworld.precollected_items[self.player]]
76
77         for item in map(self.create_item, all_items):
78             if item in exclude:
79                 exclude.remove(item)  # this is destructive. create unique list above
80                 self.multiworld.itempool.append(self.create_item("junk"))
81             else:
82                 self.multiworld.itempool.append(item)
83
84         all_macguffins = [str(x) for x in all_items.values() if x.id() < 32]
85         self.multiworld.completion_condition[self.player] = lambda state: state.has_all(all_macguffins, self.player)
86
87 def launch_client(*args) -> None:
88     from . import client
89     launch(client.run, name="SICPelago", args=args)
90
91 components.append(Component('SICPelago', 'sicp', func=launch_client))