diff --git a/worlds/clique/Items.py b/worlds/clique/Items.py new file mode 100644 index 00000000..5474f58b --- /dev/null +++ b/worlds/clique/Items.py @@ -0,0 +1,35 @@ +from typing import Callable, Dict, NamedTuple, Optional + +from BaseClasses import Item, ItemClassification, MultiWorld + + +class CliqueItem(Item): + game = "Clique" + + +class CliqueItemData(NamedTuple): + code: Optional[int] = None + type: ItemClassification = ItemClassification.filler + can_create: Callable[[MultiWorld, int], bool] = lambda multiworld, player: True + + +item_data_table: Dict[str, CliqueItemData] = { + "Feeling of Satisfaction": CliqueItemData( + code=69696969, + type=ItemClassification.progression, + ), + "Button Activation": CliqueItemData( + code=69696968, + type=ItemClassification.progression, + can_create=lambda multiworld, player: bool(getattr(multiworld, "hard_mode")[player]), + ), + "A Cool Filler Item (No Satisfaction Guaranteed)": CliqueItemData( + code=69696967, + can_create=lambda multiworld, player: False # Only created from `get_filler_item_name`. + ), + "The Urge to Push": CliqueItemData( + type=ItemClassification.progression, + ), +} + +item_table = {name: data.code for name, data in item_data_table.items() if data.code is not None} diff --git a/worlds/clique/Locations.py b/worlds/clique/Locations.py new file mode 100644 index 00000000..144becae --- /dev/null +++ b/worlds/clique/Locations.py @@ -0,0 +1,34 @@ +from typing import Callable, Dict, NamedTuple, Optional + +from BaseClasses import Location, MultiWorld + + +class CliqueLocation(Location): + game = "Clique" + + +class CliqueLocationData(NamedTuple): + region: str + address: Optional[int] = None + can_create: Callable[[MultiWorld, int], bool] = lambda multiworld, player: True + locked_item: Optional[str] = None + + +location_data_table: Dict[str, CliqueLocationData] = { + "The Big Red Button": CliqueLocationData( + region="The Button Realm", + address=69696969, + ), + "The Item on the Desk": CliqueLocationData( + region="The Button Realm", + address=69696968, + can_create=lambda multiworld, player: bool(getattr(multiworld, "hard_mode")[player]), + ), + "In the Player's Mind": CliqueLocationData( + region="The Button Realm", + locked_item="The Urge to Push", + ), +} + +location_table = {name: data.address for name, data in location_data_table.items() if data.address is not None} +locked_locations = {name: data for name, data in location_data_table.items() if data.locked_item} diff --git a/worlds/clique/Options.py b/worlds/clique/Options.py index 6636d2a0..7976dcb6 100644 --- a/worlds/clique/Options.py +++ b/worlds/clique/Options.py @@ -1,6 +1,6 @@ from typing import Dict -from Options import Option, Toggle +from Options import Choice, Option, Toggle class HardMode(Toggle): @@ -8,6 +8,27 @@ class HardMode(Toggle): display_name = "Hard Mode" +class ButtonColor(Choice): + """Customize your button! Now available in 12 unique colors.""" + display_name = "Button Color" + option_red = 0 + option_orange = 1 + option_yellow = 2 + option_green = 3 + option_cyan = 4 + option_blue = 5 + option_magenta = 6 + option_purple = 7 + option_pink = 8 + option_brown = 9 + option_white = 10 + option_black = 11 + + clique_options: Dict[str, type(Option)] = { - "hard_mode": HardMode + "color": ButtonColor, + "hard_mode": HardMode, + + # DeathLink is always on. Always. + # "death_link": DeathLink, } diff --git a/worlds/clique/Regions.py b/worlds/clique/Regions.py new file mode 100644 index 00000000..04e31706 --- /dev/null +++ b/worlds/clique/Regions.py @@ -0,0 +1,11 @@ +from typing import Dict, List, NamedTuple + + +class CliqueRegionData(NamedTuple): + connecting_regions: List[str] = [] + + +region_data_table: Dict[str, CliqueRegionData] = { + "Menu": CliqueRegionData(["The Button Realm"]), + "The Button Realm": CliqueRegionData(), +} diff --git a/worlds/clique/Rules.py b/worlds/clique/Rules.py new file mode 100644 index 00000000..5ae1d2c6 --- /dev/null +++ b/worlds/clique/Rules.py @@ -0,0 +1,10 @@ +from typing import Callable + +from BaseClasses import CollectionState, MultiWorld + + +def get_button_rule(multiworld: MultiWorld, player: int) -> Callable[[CollectionState], bool]: + if getattr(multiworld, "hard_mode")[player]: + return lambda state: state.has("Button Activation", player) + + return lambda state: True diff --git a/worlds/clique/__init__.py b/worlds/clique/__init__.py index 449399c9..58383890 100644 --- a/worlds/clique/__init__.py +++ b/worlds/clique/__init__.py @@ -1,15 +1,12 @@ -from BaseClasses import Entrance, Item, ItemClassification, Location, MultiWorld, Region, Tutorial +from typing import List + +from BaseClasses import Region, Tutorial from worlds.AutoWorld import WebWorld, World -from worlds.generic.Rules import set_rule +from .Items import CliqueItem, item_data_table, item_table +from .Locations import CliqueLocation, location_data_table, location_table, locked_locations from .Options import clique_options - - -class CliqueItem(Item): - game = "Clique" - - -class CliqueLocation(Location): - game = "Clique" +from .Regions import region_data_table +from .Rules import get_button_rule class CliqueWebWorld(WebWorld): @@ -27,71 +24,69 @@ class CliqueWebWorld(WebWorld): class CliqueWorld(World): - """The greatest game ever designed. Full of exciting gameplay!""" + """The greatest game of all time.""" game = "Clique" - data_version = 2 + data_version = 3 web = CliqueWebWorld() option_definitions = clique_options - - # Yes, I'm like 12 for this. - location_name_to_id = { - "The Big Red Button": 69696969, - "The Item on the Desk": 69696968, - } - - item_name_to_id = { - "Feeling of Satisfaction": 69696969, - "Button Activation": 69696968, - } + location_name_to_id = location_table + item_name_to_id = item_table def create_item(self, name: str) -> CliqueItem: - return CliqueItem(name, ItemClassification.progression, self.item_name_to_id[name], self.player) + return CliqueItem(name, item_data_table[name].type, item_data_table[name].code, self.player) def create_items(self) -> None: - self.multiworld.itempool.append(self.create_item("Feeling of Satisfaction")) - self.multiworld.priority_locations[self.player].value.add("The Big Red Button") + item_pool: List[CliqueItem] = [] + for name, item in item_data_table.items(): + if item.code and item.can_create(self.multiworld, self.player): + item_pool.append(self.create_item(name)) - if self.multiworld.hard_mode[self.player]: - self.multiworld.itempool.append(self.create_item("Button Activation")) + self.multiworld.itempool += item_pool def create_regions(self) -> None: - if self.multiworld.hard_mode[self.player]: - self.multiworld.regions += [ - create_region(self.multiworld, self.player, "Menu", None, ["The entrance to the button."]), - create_region(self.multiworld, self.player, "The realm of the button.", self.location_name_to_id) - ] - else: - self.multiworld.regions += [ - create_region(self.multiworld, self.player, "Menu", None, ["The entrance to the button."]), - create_region(self.multiworld, self.player, "The realm of the button.", { - "The Big Red Button": 69696969 - })] + # Create regions. + for region_name in region_data_table.keys(): + region = Region(region_name, self.player, self.multiworld) + self.multiworld.regions.append(region) - self.multiworld.get_entrance("The entrance to the button.", self.player) \ - .connect(self.multiworld.get_region("The realm of the button.", self.player)) + # Create locations. + for region_name, region_data in region_data_table.items(): + region = self.multiworld.get_region(region_name, self.player) + region.add_locations({ + location_name: location_data.address for location_name, location_data in location_data_table.items() + if location_data.region == region_name and location_data.can_create(self.multiworld, self.player) + }, CliqueLocation) + region.add_exits(region_data_table[region_name].connecting_regions) + + # Place locked locations. + for location_name, location_data in locked_locations.items(): + # Ignore locations we never created. + if not location_data.can_create(self.multiworld, self.player): + continue + + locked_item = self.create_item(location_data_table[location_name].locked_item) + self.multiworld.get_location(location_name, self.player).place_locked_item(locked_item) + + # Set priority location for the Big Red Button! + self.multiworld.priority_locations[self.player].value.add("The Big Red Button") def get_filler_item_name(self) -> str: - return self.multiworld.random.choice(self.item_name_to_id) + return "A Cool Filler Item (No Satisfaction Guaranteed)" def set_rules(self) -> None: - if self.multiworld.hard_mode[self.player]: - set_rule( - self.multiworld.get_location("The Big Red Button", self.player), - lambda state: state.has("Button Activation", self.player)) + button_rule = get_button_rule(self.multiworld, self.player) + self.multiworld.get_location("The Big Red Button", self.player).access_rule = button_rule + self.multiworld.get_location("In the Player's Mind", self.player).access_rule = button_rule - self.multiworld.completion_condition[self.player] = lambda state: \ - state.has("Feeling of Satisfaction", self.player) + # Do not allow button activations on buttons. + self.multiworld.get_location("The Big Red Button", self.player).item_rule =\ + lambda item: item.name != "Button Activation" + # Completion condition. + self.multiworld.completion_condition[self.player] = lambda state: state.has("The Urge to Push", self.player) -def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): - region = Region(name, player, world) - if locations: - for location_name in locations.keys(): - region.locations.append(CliqueLocation(player, location_name, locations[location_name], region)) - - if exits: - for _exit in exits: - region.exits.append(Entrance(player, _exit, region)) - - return region + def fill_slot_data(self): + return { + "color": getattr(self.multiworld, "color")[self.player].current_key + } diff --git a/worlds/clique/docs/en_Clique.md b/worlds/clique/docs/en_Clique.md index b63ec44d..862454f5 100644 --- a/worlds/clique/docs/en_Clique.md +++ b/worlds/clique/docs/en_Clique.md @@ -8,7 +8,7 @@ Clique is a joke game developed for Archipelago in March 2023 to showcase how ea Archipelago. The objective of the game is to press the big red button. If a player is playing on `hard_mode`, they must wait for someone else in the multiworld to "activate" their button before they can press it. -Clique can be played on any HTML5-capable browser. +Clique can be played on most modern HTML5-capable browsers. ## Where is the settings page? diff --git a/worlds/clique/docs/guide_en.md b/worlds/clique/docs/guide_en.md index b2af48a5..c3c113fe 100644 --- a/worlds/clique/docs/guide_en.md +++ b/worlds/clique/docs/guide_en.md @@ -6,7 +6,7 @@ slot name, and a room password if one is required. Then click "Connect". If you're playing on "easy mode", just click the button and receive "Satisfaction". If you're playing on "hard mode", you may need to wait for activation before you can complete your objective. Luckily, -Clique runs in all the major browsers that support HTML5, so you can load Clique on your phone and be productive while +Clique runs in most major browsers that support HTML5, so you can load Clique on your phone and be productive while you wait! If you need some ideas for what to do while waiting for button activation, give the following a try: @@ -19,4 +19,4 @@ If you need some ideas for what to do while waiting for button activation, give - Do your school work. -~~If you run into any issues with this game, definitely do not contact Phar#4444 on discord. *wink* *wink*~~ +~~If you run into any issues with this game, definitely do not contact **thephar** on discord. *wink* *wink*~~