Skip to content

Commit 741e3c0

Browse files
Zach ParksFlySniper
Zach Parks
authored andcommitted
Clique: Refactors and Additional Features supported by v1.5 (ArchipelagoMW#1989)
1 parent 287022e commit 741e3c0

File tree

8 files changed

+171
-65
lines changed

8 files changed

+171
-65
lines changed

worlds/clique/Items.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import Callable, Dict, NamedTuple, Optional
2+
3+
from BaseClasses import Item, ItemClassification, MultiWorld
4+
5+
6+
class CliqueItem(Item):
7+
game = "Clique"
8+
9+
10+
class CliqueItemData(NamedTuple):
11+
code: Optional[int] = None
12+
type: ItemClassification = ItemClassification.filler
13+
can_create: Callable[[MultiWorld, int], bool] = lambda multiworld, player: True
14+
15+
16+
item_data_table: Dict[str, CliqueItemData] = {
17+
"Feeling of Satisfaction": CliqueItemData(
18+
code=69696969,
19+
type=ItemClassification.progression,
20+
),
21+
"Button Activation": CliqueItemData(
22+
code=69696968,
23+
type=ItemClassification.progression,
24+
can_create=lambda multiworld, player: bool(getattr(multiworld, "hard_mode")[player]),
25+
),
26+
"A Cool Filler Item (No Satisfaction Guaranteed)": CliqueItemData(
27+
code=69696967,
28+
can_create=lambda multiworld, player: False # Only created from `get_filler_item_name`.
29+
),
30+
"The Urge to Push": CliqueItemData(
31+
type=ItemClassification.progression,
32+
),
33+
}
34+
35+
item_table = {name: data.code for name, data in item_data_table.items() if data.code is not None}

worlds/clique/Locations.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from typing import Callable, Dict, NamedTuple, Optional
2+
3+
from BaseClasses import Location, MultiWorld
4+
5+
6+
class CliqueLocation(Location):
7+
game = "Clique"
8+
9+
10+
class CliqueLocationData(NamedTuple):
11+
region: str
12+
address: Optional[int] = None
13+
can_create: Callable[[MultiWorld, int], bool] = lambda multiworld, player: True
14+
locked_item: Optional[str] = None
15+
16+
17+
location_data_table: Dict[str, CliqueLocationData] = {
18+
"The Big Red Button": CliqueLocationData(
19+
region="The Button Realm",
20+
address=69696969,
21+
),
22+
"The Item on the Desk": CliqueLocationData(
23+
region="The Button Realm",
24+
address=69696968,
25+
can_create=lambda multiworld, player: bool(getattr(multiworld, "hard_mode")[player]),
26+
),
27+
"In the Player's Mind": CliqueLocationData(
28+
region="The Button Realm",
29+
locked_item="The Urge to Push",
30+
),
31+
}
32+
33+
location_table = {name: data.address for name, data in location_data_table.items() if data.address is not None}
34+
locked_locations = {name: data for name, data in location_data_table.items() if data.locked_item}

worlds/clique/Options.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
from typing import Dict
22

3-
from Options import Option, Toggle
3+
from Options import Choice, Option, Toggle
44

55

66
class HardMode(Toggle):
77
"""Only for the most masochistically inclined... Requires button activation!"""
88
display_name = "Hard Mode"
99

1010

11+
class ButtonColor(Choice):
12+
"""Customize your button! Now available in 12 unique colors."""
13+
display_name = "Button Color"
14+
option_red = 0
15+
option_orange = 1
16+
option_yellow = 2
17+
option_green = 3
18+
option_cyan = 4
19+
option_blue = 5
20+
option_magenta = 6
21+
option_purple = 7
22+
option_pink = 8
23+
option_brown = 9
24+
option_white = 10
25+
option_black = 11
26+
27+
1128
clique_options: Dict[str, type(Option)] = {
12-
"hard_mode": HardMode
29+
"color": ButtonColor,
30+
"hard_mode": HardMode,
31+
32+
# DeathLink is always on. Always.
33+
# "death_link": DeathLink,
1334
}

worlds/clique/Regions.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from typing import Dict, List, NamedTuple
2+
3+
4+
class CliqueRegionData(NamedTuple):
5+
connecting_regions: List[str] = []
6+
7+
8+
region_data_table: Dict[str, CliqueRegionData] = {
9+
"Menu": CliqueRegionData(["The Button Realm"]),
10+
"The Button Realm": CliqueRegionData(),
11+
}

worlds/clique/Rules.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from typing import Callable
2+
3+
from BaseClasses import CollectionState, MultiWorld
4+
5+
6+
def get_button_rule(multiworld: MultiWorld, player: int) -> Callable[[CollectionState], bool]:
7+
if getattr(multiworld, "hard_mode")[player]:
8+
return lambda state: state.has("Button Activation", player)
9+
10+
return lambda state: True

worlds/clique/__init__.py

+55-60
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
from BaseClasses import Entrance, Item, ItemClassification, Location, MultiWorld, Region, Tutorial
1+
from typing import List
2+
3+
from BaseClasses import Region, Tutorial
24
from worlds.AutoWorld import WebWorld, World
3-
from worlds.generic.Rules import set_rule
5+
from .Items import CliqueItem, item_data_table, item_table
6+
from .Locations import CliqueLocation, location_data_table, location_table, locked_locations
47
from .Options import clique_options
5-
6-
7-
class CliqueItem(Item):
8-
game = "Clique"
9-
10-
11-
class CliqueLocation(Location):
12-
game = "Clique"
8+
from .Regions import region_data_table
9+
from .Rules import get_button_rule
1310

1411

1512
class CliqueWebWorld(WebWorld):
@@ -27,71 +24,69 @@ class CliqueWebWorld(WebWorld):
2724

2825

2926
class CliqueWorld(World):
30-
"""The greatest game ever designed. Full of exciting gameplay!"""
27+
"""The greatest game of all time."""
3128

3229
game = "Clique"
33-
data_version = 2
30+
data_version = 3
3431
web = CliqueWebWorld()
3532
option_definitions = clique_options
36-
37-
# Yes, I'm like 12 for this.
38-
location_name_to_id = {
39-
"The Big Red Button": 69696969,
40-
"The Item on the Desk": 69696968,
41-
}
42-
43-
item_name_to_id = {
44-
"Feeling of Satisfaction": 69696969,
45-
"Button Activation": 69696968,
46-
}
33+
location_name_to_id = location_table
34+
item_name_to_id = item_table
4735

4836
def create_item(self, name: str) -> CliqueItem:
49-
return CliqueItem(name, ItemClassification.progression, self.item_name_to_id[name], self.player)
37+
return CliqueItem(name, item_data_table[name].type, item_data_table[name].code, self.player)
5038

5139
def create_items(self) -> None:
52-
self.multiworld.itempool.append(self.create_item("Feeling of Satisfaction"))
53-
self.multiworld.priority_locations[self.player].value.add("The Big Red Button")
40+
item_pool: List[CliqueItem] = []
41+
for name, item in item_data_table.items():
42+
if item.code and item.can_create(self.multiworld, self.player):
43+
item_pool.append(self.create_item(name))
5444

55-
if self.multiworld.hard_mode[self.player]:
56-
self.multiworld.itempool.append(self.create_item("Button Activation"))
45+
self.multiworld.itempool += item_pool
5746

5847
def create_regions(self) -> None:
59-
if self.multiworld.hard_mode[self.player]:
60-
self.multiworld.regions += [
61-
create_region(self.multiworld, self.player, "Menu", None, ["The entrance to the button."]),
62-
create_region(self.multiworld, self.player, "The realm of the button.", self.location_name_to_id)
63-
]
64-
else:
65-
self.multiworld.regions += [
66-
create_region(self.multiworld, self.player, "Menu", None, ["The entrance to the button."]),
67-
create_region(self.multiworld, self.player, "The realm of the button.", {
68-
"The Big Red Button": 69696969
69-
})]
70-
71-
self.multiworld.get_entrance("The entrance to the button.", self.player) \
72-
.connect(self.multiworld.get_region("The realm of the button.", self.player))
48+
# Create regions.
49+
for region_name in region_data_table.keys():
50+
region = Region(region_name, self.player, self.multiworld)
51+
self.multiworld.regions.append(region)
52+
53+
# Create locations.
54+
for region_name, region_data in region_data_table.items():
55+
region = self.multiworld.get_region(region_name, self.player)
56+
region.add_locations({
57+
location_name: location_data.address for location_name, location_data in location_data_table.items()
58+
if location_data.region == region_name and location_data.can_create(self.multiworld, self.player)
59+
}, CliqueLocation)
60+
region.add_exits(region_data_table[region_name].connecting_regions)
61+
62+
# Place locked locations.
63+
for location_name, location_data in locked_locations.items():
64+
# Ignore locations we never created.
65+
if not location_data.can_create(self.multiworld, self.player):
66+
continue
67+
68+
locked_item = self.create_item(location_data_table[location_name].locked_item)
69+
self.multiworld.get_location(location_name, self.player).place_locked_item(locked_item)
70+
71+
# Set priority location for the Big Red Button!
72+
self.multiworld.priority_locations[self.player].value.add("The Big Red Button")
7373

7474
def get_filler_item_name(self) -> str:
75-
return self.multiworld.random.choice(self.item_name_to_id)
75+
return "A Cool Filler Item (No Satisfaction Guaranteed)"
7676

7777
def set_rules(self) -> None:
78-
if self.multiworld.hard_mode[self.player]:
79-
set_rule(
80-
self.multiworld.get_location("The Big Red Button", self.player),
81-
lambda state: state.has("Button Activation", self.player))
82-
83-
self.multiworld.completion_condition[self.player] = lambda state: \
84-
state.has("Feeling of Satisfaction", self.player)
85-
78+
button_rule = get_button_rule(self.multiworld, self.player)
79+
self.multiworld.get_location("The Big Red Button", self.player).access_rule = button_rule
80+
self.multiworld.get_location("In the Player's Mind", self.player).access_rule = button_rule
8681

87-
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
88-
region = Region(name, player, world)
89-
if locations:
90-
for location_name in locations.keys():
91-
region.locations.append(CliqueLocation(player, location_name, locations[location_name], region))
82+
# Do not allow button activations on buttons.
83+
self.multiworld.get_location("The Big Red Button", self.player).item_rule =\
84+
lambda item: item.name != "Button Activation"
9285

93-
if exits:
94-
for _exit in exits:
95-
region.exits.append(Entrance(player, _exit, region))
86+
# Completion condition.
87+
self.multiworld.completion_condition[self.player] = lambda state: state.has("The Urge to Push", self.player)
9688

97-
return region
89+
def fill_slot_data(self):
90+
return {
91+
"color": getattr(self.multiworld, "color")[self.player].current_key
92+
}

worlds/clique/docs/en_Clique.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Clique is a joke game developed for Archipelago in March 2023 to showcase how ea
88
Archipelago. The objective of the game is to press the big red button. If a player is playing on `hard_mode`, they must
99
wait for someone else in the multiworld to "activate" their button before they can press it.
1010

11-
Clique can be played on any HTML5-capable browser.
11+
Clique can be played on most modern HTML5-capable browsers.
1212

1313
## Where is the settings page?
1414

worlds/clique/docs/guide_en.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ slot name, and a room password if one is required. Then click "Connect".
66
If you're playing on "easy mode", just click the button and receive "Satisfaction".
77

88
If you're playing on "hard mode", you may need to wait for activation before you can complete your objective. Luckily,
9-
Clique runs in all the major browsers that support HTML5, so you can load Clique on your phone and be productive while
9+
Clique runs in most major browsers that support HTML5, so you can load Clique on your phone and be productive while
1010
you wait!
1111

1212
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
1919
- Do your school work.
2020

2121

22-
~~If you run into any issues with this game, definitely do not contact Phar#4444 on discord. *wink* *wink*~~
22+
~~If you run into any issues with this game, definitely do not contact **thephar** on discord. *wink* *wink*~~

0 commit comments

Comments
 (0)