2023-11-08 23:35:12 +00:00
|
|
|
"""
|
|
|
|
Archipelago init file for Lingo
|
|
|
|
"""
|
2023-12-03 23:06:11 +00:00
|
|
|
from logging import warning
|
|
|
|
|
2023-11-24 16:30:15 +00:00
|
|
|
from BaseClasses import Item, ItemClassification, Tutorial
|
2023-11-08 23:35:12 +00:00
|
|
|
from worlds.AutoWorld import WebWorld, World
|
|
|
|
from .items import ALL_ITEM_TABLE, LingoItem
|
|
|
|
from .locations import ALL_LOCATION_TABLE
|
|
|
|
from .options import LingoOptions
|
|
|
|
from .player_logic import LingoPlayerLogic
|
|
|
|
from .regions import create_regions
|
|
|
|
from .static_logic import Room, RoomEntrance
|
|
|
|
|
|
|
|
|
|
|
|
class LingoWebWorld(WebWorld):
|
|
|
|
theme = "grass"
|
|
|
|
tutorials = [Tutorial(
|
|
|
|
"Multiworld Setup Guide",
|
|
|
|
"A guide to playing Lingo with Archipelago.",
|
|
|
|
"English",
|
|
|
|
"setup_en.md",
|
|
|
|
"setup/en",
|
|
|
|
["hatkirby"]
|
|
|
|
)]
|
|
|
|
|
|
|
|
|
|
|
|
class LingoWorld(World):
|
|
|
|
"""
|
|
|
|
Lingo is a first person indie puzzle game in the vein of The Witness. You find yourself in a mazelike, non-Euclidean
|
|
|
|
world filled with 800 word puzzles that use a variety of different mechanics.
|
|
|
|
"""
|
|
|
|
game = "Lingo"
|
|
|
|
web = LingoWebWorld()
|
|
|
|
|
|
|
|
base_id = 444400
|
|
|
|
topology_present = True
|
|
|
|
data_version = 1
|
|
|
|
|
|
|
|
options_dataclass = LingoOptions
|
|
|
|
options: LingoOptions
|
|
|
|
|
|
|
|
item_name_to_id = {
|
|
|
|
name: data.code for name, data in ALL_ITEM_TABLE.items()
|
|
|
|
}
|
|
|
|
location_name_to_id = {
|
|
|
|
name: data.code for name, data in ALL_LOCATION_TABLE.items()
|
|
|
|
}
|
|
|
|
|
|
|
|
player_logic: LingoPlayerLogic
|
|
|
|
|
|
|
|
def generate_early(self):
|
2023-12-03 23:06:11 +00:00
|
|
|
if not (self.options.shuffle_doors or self.options.shuffle_colors):
|
|
|
|
if self.multiworld.players == 1:
|
|
|
|
warning(f"{self.multiworld.get_player_name(self.player)}'s Lingo world doesn't have any progression"
|
|
|
|
f" items. Please turn on Door Shuffle or Color Shuffle if that doesn't seem right.")
|
|
|
|
else:
|
|
|
|
raise Exception(f"{self.multiworld.get_player_name(self.player)}'s Lingo world doesn't have any"
|
|
|
|
f" progression items. Please turn on Door Shuffle or Color Shuffle.")
|
|
|
|
|
2023-11-08 23:35:12 +00:00
|
|
|
self.player_logic = LingoPlayerLogic(self)
|
|
|
|
|
|
|
|
def create_regions(self):
|
|
|
|
create_regions(self, self.player_logic)
|
|
|
|
|
|
|
|
def create_items(self):
|
2023-11-25 12:09:08 +00:00
|
|
|
pool = [self.create_item(name) for name in self.player_logic.real_items]
|
2023-11-08 23:35:12 +00:00
|
|
|
|
2023-11-25 12:09:08 +00:00
|
|
|
if self.player_logic.forced_good_item != "":
|
|
|
|
new_item = self.create_item(self.player_logic.forced_good_item)
|
2023-11-08 23:35:12 +00:00
|
|
|
location_obj = self.multiworld.get_location("Second Room - Good Luck", self.player)
|
|
|
|
location_obj.place_locked_item(new_item)
|
|
|
|
|
2023-11-25 12:09:08 +00:00
|
|
|
item_difference = len(self.player_logic.real_locations) - len(pool)
|
2023-11-08 23:35:12 +00:00
|
|
|
if item_difference:
|
|
|
|
trap_percentage = self.options.trap_percentage
|
|
|
|
traps = int(item_difference * trap_percentage / 100.0)
|
|
|
|
non_traps = item_difference - traps
|
|
|
|
|
|
|
|
if non_traps:
|
|
|
|
skip_percentage = self.options.puzzle_skip_percentage
|
|
|
|
skips = int(non_traps * skip_percentage / 100.0)
|
|
|
|
non_skips = non_traps - skips
|
|
|
|
|
|
|
|
filler_list = [":)", "The Feeling of Being Lost", "Wanderlust", "Empty White Hallways"]
|
|
|
|
for i in range(0, non_skips):
|
|
|
|
pool.append(self.create_item(filler_list[i % len(filler_list)]))
|
|
|
|
|
|
|
|
for i in range(0, skips):
|
|
|
|
pool.append(self.create_item("Puzzle Skip"))
|
|
|
|
|
|
|
|
if traps:
|
|
|
|
traps_list = ["Slowness Trap", "Iceland Trap", "Atbash Trap"]
|
|
|
|
|
|
|
|
for i in range(0, traps):
|
|
|
|
pool.append(self.create_item(traps_list[i % len(traps_list)]))
|
|
|
|
|
|
|
|
self.multiworld.itempool += pool
|
|
|
|
|
|
|
|
def create_item(self, name: str) -> Item:
|
|
|
|
item = ALL_ITEM_TABLE[name]
|
2023-11-24 16:30:15 +00:00
|
|
|
|
|
|
|
classification = item.classification
|
|
|
|
if hasattr(self, "options") and self.options.shuffle_paintings and len(item.painting_ids) > 0\
|
2023-11-25 12:09:08 +00:00
|
|
|
and len(item.door_ids) == 0 and all(painting_id not in self.player_logic.painting_mapping
|
Lingo: Fix entrance checking being broken on default settings (#2506)
The most serious issue this PR addresses is that entrances that use doors without items (a small subset of doors when door shuffle is on, but *every* door when door shuffle is off, which is the default) underestimate the requirements needed to use that entrance. The logic would calculate the panels needed to open the door, but would neglect to keep track of the rooms those panels were in, meaning that doors would be considered openable if you had the colors needed to solve a panel that's in a room you have no access to.
Another issue is that, previously, logic would always consider the "ANOTHER TRY" panel accessible for the purposes of the LEVEL 2 panel hunt. This could result in seeds where the player is expected to have exactly the correct number of solves to reach LEVEL 2, but in reality is short by one because ANOTHER TRY itself is not revealed until the panel hunt is complete. This change marks ANOTHER TRY as non-counting, because even though it is technically a counting panel in-game, it can never contribute to the LEVEL 2 panel hunt. This issue could also apply to THE MASTER, since it is the only other counting panel with special access rules, although it is much less likely. This change adds special handling for counting THE MASTER. These issues were possible to manifest whenever the LEVEL 2 panel hunt was enabled, which it is by default.
Smaller logic issues also fixed in this PR:
* The Orange Tower Basement MASTERY panel was marked as requiring the mastery doors to be opened, when it was actually possible to get it without them by using a painting to get into the room.
* The Pilgrim Room painting item was incorrectly being marked as a filler item, despite it being progression.
* There has been another update to the game that adds connections between areas that were previously not connected. These changes were additive, which is why they are not critical.
* The panel stacks in the rhyme room now require both colours on each panel.
2023-12-10 18:15:42 +00:00
|
|
|
for painting_id in item.painting_ids)\
|
|
|
|
and "pilgrim_painting2" not in item.painting_ids:
|
2023-11-24 16:30:15 +00:00
|
|
|
# If this is a "door" that just moves one or more paintings, and painting shuffle is on and those paintings
|
Lingo: Fix entrance checking being broken on default settings (#2506)
The most serious issue this PR addresses is that entrances that use doors without items (a small subset of doors when door shuffle is on, but *every* door when door shuffle is off, which is the default) underestimate the requirements needed to use that entrance. The logic would calculate the panels needed to open the door, but would neglect to keep track of the rooms those panels were in, meaning that doors would be considered openable if you had the colors needed to solve a panel that's in a room you have no access to.
Another issue is that, previously, logic would always consider the "ANOTHER TRY" panel accessible for the purposes of the LEVEL 2 panel hunt. This could result in seeds where the player is expected to have exactly the correct number of solves to reach LEVEL 2, but in reality is short by one because ANOTHER TRY itself is not revealed until the panel hunt is complete. This change marks ANOTHER TRY as non-counting, because even though it is technically a counting panel in-game, it can never contribute to the LEVEL 2 panel hunt. This issue could also apply to THE MASTER, since it is the only other counting panel with special access rules, although it is much less likely. This change adds special handling for counting THE MASTER. These issues were possible to manifest whenever the LEVEL 2 panel hunt was enabled, which it is by default.
Smaller logic issues also fixed in this PR:
* The Orange Tower Basement MASTERY panel was marked as requiring the mastery doors to be opened, when it was actually possible to get it without them by using a painting to get into the room.
* The Pilgrim Room painting item was incorrectly being marked as a filler item, despite it being progression.
* There has been another update to the game that adds connections between areas that were previously not connected. These changes were additive, which is why they are not critical.
* The panel stacks in the rhyme room now require both colours on each panel.
2023-12-10 18:15:42 +00:00
|
|
|
# go nowhere, then this item should not be progression. The Pilgrim Room painting is special and needs to be
|
|
|
|
# excluded from this.
|
2023-11-24 16:30:15 +00:00
|
|
|
classification = ItemClassification.filler
|
|
|
|
|
|
|
|
return LingoItem(name, classification, item.code, self.player)
|
2023-11-08 23:35:12 +00:00
|
|
|
|
|
|
|
def set_rules(self):
|
|
|
|
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
|
|
|
|
|
|
|
|
def fill_slot_data(self):
|
|
|
|
slot_options = [
|
|
|
|
"death_link", "victory_condition", "shuffle_colors", "shuffle_doors", "shuffle_paintings", "shuffle_panels",
|
|
|
|
"mastery_achievements", "level_2_requirement", "location_checks", "early_color_hallways"
|
|
|
|
]
|
|
|
|
|
|
|
|
slot_data = {
|
|
|
|
"seed": self.random.randint(0, 1000000),
|
|
|
|
**self.options.as_dict(*slot_options),
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.options.shuffle_paintings:
|
2023-11-25 12:09:08 +00:00
|
|
|
slot_data["painting_entrance_to_exit"] = self.player_logic.painting_mapping
|
2023-11-08 23:35:12 +00:00
|
|
|
|
|
|
|
return slot_data
|