about summary refs log tree commit diff stats
path: root/apworld/locations.py
blob: c92215e5622d0aa18a86bc455f7e5bac4ad6940b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from enum import Enum
from typing import TYPE_CHECKING

from BaseClasses import Location, Item, Region, CollectionState, Entrance
from .items import Lingo2Item
from .rules import AccessRequirements

if TYPE_CHECKING:
    from . import Lingo2World


class LetterPlacementType(Enum):
    ANY = 0
    DISALLOW = 1
    FORCE = 2


def get_required_regions(reqs: AccessRequirements, world: "Lingo2World",
                         regions: dict[str, Region] | None) -> list[Region]:
    # Replace required rooms with regions for the top level requirement, which saves looking up the regions during rule
    # checking.
    if regions is not None:
        return [regions[room_name] for room_name in reqs.rooms]
    else:
        return [world.multiworld.get_region(room_name, world.player) for room_name in reqs.rooms]


class Lingo2Location(Location):
    game: str = "Lingo 2"

    reqs: AccessRequirements | None
    world: "Lingo2World"
    required_regions: list[Region]

    port_id: int
    goal: bool
    letter_placement_type: LetterPlacementType

    @classmethod
    def non_event_location(cls, world: "Lingo2World", code: int, region: Region):
        result = cls(world.player, world.static_logic.location_id_to_name[code], code, region)
        result.reqs = None
        result.world = world
        result.required_regions = []

        return result

    @classmethod
    def event_location(cls, world: "Lingo2World", name: str, region: Region):
        result = cls(world.player, name, None, region)
        result.reqs = None
        result.world = world
        result.required_regions = []

        return result

    def set_access_rule(self, reqs: AccessRequirements, regions: dict[str, Region] | None):
        self.reqs = reqs
        self.required_regions = get_required_regions(reqs, self.world, regions)
        self.access_rule = self._l2_access_rule

    def _l2_access_rule(self, state: CollectionState) -> bool:
        if self.reqs is not None and not self.reqs.check_access(state, self.world):
            return False

        if not all(state.can_reach(region) for region in self.required_regions):
            return False

        return True

    def set_up_letter_rule(self, lpt: LetterPlacementType):
        self.letter_placement_type = lpt
        self.item_rule = self._l2_item_rule

    def _l2_item_rule(self, item: Item) -> bool:
        if not isinstance(item, Lingo2Item):
            return True

        if self.letter_placement_type == LetterPlacementType.FORCE:
            return item.is_letter
        elif self.letter_placement_type == LetterPlacementType.DISALLOW:
            return not item.is_letter

        return True


class Lingo2Entrance(Entrance):
    reqs: AccessRequirements | None
    world: "Lingo2World"
    required_regions: list[Region]

    def __init__(self, world: "Lingo2World", description: str, region: Region):
        super().__init__(world.player, description, region)

        self.reqs = None
        self.world = world
        self.required_regions = []

    def set_access_rule(self, reqs: AccessRequirements, regions: dict[str, Region] | None):
        self.reqs = reqs
        self.required_regions = get_required_regions(reqs, self.world, regions)
        self.access_rule = self._l2_access_rule

    def _l2_access_rule(self, state: CollectionState) -> bool:
        if self.reqs is not None and not self.reqs.check_access(state, self.world):
            return False

        if not all(state.can_reach(region) for region in self.required_regions):
            return False

        return True