151 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
Archipelago init file for Lingo
 | 
						|
"""
 | 
						|
from logging import warning
 | 
						|
 | 
						|
from BaseClasses import Item, ItemClassification, Tutorial
 | 
						|
from worlds.AutoWorld import WebWorld, World
 | 
						|
from .datatypes import Room, RoomEntrance
 | 
						|
from .items import ALL_ITEM_TABLE, ITEMS_BY_GROUP, TRAP_ITEMS, LingoItem
 | 
						|
from .locations import ALL_LOCATION_TABLE, LOCATIONS_BY_GROUP
 | 
						|
from .options import LingoOptions
 | 
						|
from .player_logic import LingoPlayerLogic
 | 
						|
from .regions import create_regions
 | 
						|
 | 
						|
 | 
						|
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()
 | 
						|
    }
 | 
						|
    item_name_groups = ITEMS_BY_GROUP
 | 
						|
    location_name_groups = LOCATIONS_BY_GROUP
 | 
						|
 | 
						|
    player_logic: LingoPlayerLogic
 | 
						|
 | 
						|
    def generate_early(self):
 | 
						|
        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.")
 | 
						|
 | 
						|
        self.player_logic = LingoPlayerLogic(self)
 | 
						|
 | 
						|
    def create_regions(self):
 | 
						|
        create_regions(self, self.player_logic)
 | 
						|
 | 
						|
    def create_items(self):
 | 
						|
        pool = [self.create_item(name) for name in self.player_logic.real_items]
 | 
						|
 | 
						|
        if self.player_logic.forced_good_item != "":
 | 
						|
            new_item = self.create_item(self.player_logic.forced_good_item)
 | 
						|
            location_obj = self.multiworld.get_location("Second Room - Good Luck", self.player)
 | 
						|
            location_obj.place_locked_item(new_item)
 | 
						|
 | 
						|
        item_difference = len(self.player_logic.real_locations) - len(pool)
 | 
						|
        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
 | 
						|
 | 
						|
                for i in range(0, non_skips):
 | 
						|
                    pool.append(self.create_item(self.get_filler_item_name()))
 | 
						|
 | 
						|
                for i in range(0, skips):
 | 
						|
                    pool.append(self.create_item("Puzzle Skip"))
 | 
						|
 | 
						|
            if traps:
 | 
						|
                total_weight = sum(self.options.trap_weights.values())
 | 
						|
 | 
						|
                if total_weight == 0:
 | 
						|
                    raise Exception("Sum of trap weights must be at least one.")
 | 
						|
 | 
						|
                trap_counts = {name: int(weight * traps / total_weight)
 | 
						|
                               for name, weight in self.options.trap_weights.items()}
 | 
						|
                
 | 
						|
                trap_difference = traps - sum(trap_counts.values())
 | 
						|
                if trap_difference > 0:
 | 
						|
                    allowed_traps = [name for name in TRAP_ITEMS if self.options.trap_weights[name] > 0]
 | 
						|
                    for i in range(0, trap_difference):
 | 
						|
                        trap_counts[allowed_traps[i % len(allowed_traps)]] += 1
 | 
						|
 | 
						|
                for name, count in trap_counts.items():
 | 
						|
                    for i in range(0, count):
 | 
						|
                        pool.append(self.create_item(name))
 | 
						|
 | 
						|
        self.multiworld.itempool += pool
 | 
						|
 | 
						|
    def create_item(self, name: str) -> Item:
 | 
						|
        item = ALL_ITEM_TABLE[name]
 | 
						|
 | 
						|
        classification = item.classification
 | 
						|
        if hasattr(self, "options") and self.options.shuffle_paintings and len(item.painting_ids) > 0 \
 | 
						|
                and not item.has_doors and all(painting_id not in self.player_logic.painting_mapping
 | 
						|
                                               for painting_id in item.painting_ids) \
 | 
						|
                and "pilgrim_painting2" not in item.painting_ids:
 | 
						|
            # If this is a "door" that just moves one or more paintings, and painting shuffle is on and those paintings
 | 
						|
            # go nowhere, then this item should not be progression. The Pilgrim Room painting is special and needs to be
 | 
						|
            # excluded from this.
 | 
						|
            classification = ItemClassification.filler
 | 
						|
 | 
						|
        return LingoItem(name, classification, item.code, self.player)
 | 
						|
 | 
						|
    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:
 | 
						|
            slot_data["painting_entrance_to_exit"] = self.player_logic.painting_mapping
 | 
						|
 | 
						|
        return slot_data
 | 
						|
 | 
						|
    def get_filler_item_name(self) -> str:
 | 
						|
        filler_list = [":)", "The Feeling of Being Lost", "Wanderlust", "Empty White Hallways"]
 | 
						|
        return self.random.choice(filler_list)
 |