diff options
73 files changed, 1419 insertions, 255 deletions
| diff --git a/README.md b/README.md index 60dc1b3..1da3c2b 100644 --- a/README.md +++ b/README.md | |||
| @@ -9,6 +9,94 @@ This is a project that modifies the game | |||
| 9 | [Lingo 2](https://www.lingothegame.com/lingo2.html) so that it can be played as | 9 | [Lingo 2](https://www.lingothegame.com/lingo2.html) so that it can be played as |
| 10 | part of an Archipelago multiworld game. | 10 | part of an Archipelago multiworld game. |
| 11 | 11 | ||
| 12 | ## How To Play | ||
| 13 | |||
| 14 | Here are the components needed in order to play: | ||
| 15 | |||
| 16 | - **Apworld**: This is used by Archipelago to generate randomized Lingo 2 | ||
| 17 | worlds. | ||
| 18 | - [Installation & FAQ](https://code.fourisland.com/lingo2-archipelago/about/apworld/README.md) | ||
| 19 | - [Downloads](https://code.fourisland.com/lingo2-archipelago/about/apworld/CHANGELOG.md) | ||
| 20 | - **Client**: This is how Lingo 2 connects to an Archipelago multiworld. | ||
| 21 | - [Installation & FAQ](https://code.fourisland.com/lingo2-archipelago/about/client/README.md) | ||
| 22 | - [Downloads](https://code.fourisland.com/lingo2-archipelago/about/client/CHANGELOG.md) | ||
| 23 | |||
| 24 | ## Frequently Asked Questions | ||
| 25 | |||
| 26 | ### Why aren't the starting room letters shuffled? | ||
| 27 | |||
| 28 | The letter requirements for solving puzzles are very restrictive, especially in | ||
| 29 | the early game. It is possible for the generator to find some subset of letters | ||
| 30 | and doors to place in the starting room such that you are not trapped, but this | ||
| 31 | places a lot of strain on generation and leads to significantly more generation | ||
| 32 | failures. | ||
| 33 | |||
| 34 | As a result, the starting room letters (H1, I1, N1, and T1) are always present | ||
| 35 | in the starting room, even when remote letter shuffle is enabled. These letters | ||
| 36 | will _also_ count as clearing a check, so you will send out another item at the | ||
| 37 | same time as collecting the letter. | ||
| 38 | |||
| 39 | ### What areas are randomized? | ||
| 40 | |||
| 41 | Almost all maps that you can access from the base game are randomized. The | ||
| 42 | exceptions are: | ||
| 43 | |||
| 44 | - Icarus (this will be randomized at some point, although it will be optional) | ||
| 45 | - Demo | ||
| 46 | - The Hinterlands (this will probably be repurposed) | ||
| 47 | - The beta tester gift maps | ||
| 48 | |||
| 49 | ### What about wall snipes? | ||
| 50 | |||
| 51 | "Wall sniping" refers to the fact that you are able to solve puzzles on the | ||
| 52 | other side of opaque walls. The player is never expected to or required to do | ||
| 53 | this in normal gameplay. This randomizer does not change how wall snipes work, | ||
| 54 | but it will likewise never require the use of them. | ||
| 55 | |||
| 56 | ### How do cyan doors work? | ||
| 57 | |||
| 58 | In the base game, there are a number of cyan-colored doors that ordinarily open | ||
| 59 | once you collect H2 in The Repetitive. There are also a handful of panels that | ||
| 60 | only appear upon getting H2 as well, which the apworld treats the same as the | ||
| 61 | cyan doors. | ||
| 62 | |||
| 63 | There is an option that lets you choose how these doors and panels behave. By | ||
| 64 | default, they act the same as in the base game: they only open or appear after | ||
| 65 | collecting H2. Note that this means the actual H2 collectable in The Repetitive. | ||
| 66 | Receiving H2 via remote letter shuffle does not count for this requirement. | ||
| 67 | However, you can also make cyan doors activate upon collecting or receiving your | ||
| 68 | first double letter, regardless of what it is or if it's remote. Finally, you | ||
| 69 | can lock cyan doors behind an item called "Cyan Doors". | ||
| 70 | |||
| 71 | It is important to note, however, that the Cyan Door Behavior option only | ||
| 72 | applies to cyan doors that are not already affected by another type of | ||
| 73 | shuffling. When door shuffle is on, the following cyan doors are activated by | ||
| 74 | individual items and are not impacted by your choice of Cyan Door Behavior: | ||
| 75 | |||
| 76 | - The entrance to The Tower from The Great (The Great - Tower Entrance) | ||
| 77 | - The entrance to The Butterfly from The Bearer (The Bearer - Butterfly | ||
| 78 | Entrance) | ||
| 79 | - The entrance to The Repetitive from The Entry (The Entry - Repetitive | ||
| 80 | Entrance) | ||
| 81 | - The eye painting near the yellow color hallway in Daedalus (Daedalus - Eye | ||
| 82 | Painting) | ||
| 83 | - The Red I room in The Repetitive (The Repetitive - Anti Collectable Room) | ||
| 84 | |||
| 85 | Additionally, when control center color shuffle is enabled, the orange door in | ||
| 86 | The Unkempt (which ordinarily doubles as a cyan door) opens upon receiving the | ||
| 87 | Control Center Orange Doors item, instead of following the Cyan Door Behavior | ||
| 88 | option. | ||
| 89 | |||
| 90 | ### Help! I lost C/G in The Congruent! | ||
| 91 | |||
| 92 | If you place C or G into the relevant keyholders in The Congruent, the keyholder | ||
| 93 | disappears. You can retrieve your letter immediately by pressing C or G again | ||
| 94 | before leaving solve mode, as the keyholder will still be considered to be | ||
| 95 | "focused", even though it has moved. If you have already moved, though, there is | ||
| 96 | another way to get your letters back: just use the Key Return in The Entry. | ||
| 97 | |||
| 98 | ## Project Details | ||
| 99 | |||
| 12 | There are multiple parts of this project: | 100 | There are multiple parts of this project: |
| 13 | 101 | ||
| 14 | - [Client](https://code.fourisland.com/lingo2-archipelago/about/client/README.md) | 102 | - [Client](https://code.fourisland.com/lingo2-archipelago/about/client/README.md) |
| diff --git a/apworld/CHANGELOG.md b/apworld/CHANGELOG.md new file mode 100644 index 0000000..af45992 --- /dev/null +++ b/apworld/CHANGELOG.md | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | # lingo2-archipelago Apworld Releases | ||
| 2 | |||
| 3 | ## v5.5 - 2025-09-16 | ||
| 4 | |||
| 5 | - Fixed a panel in The Ancient that was missing a symbol. | ||
| 6 | - Fixed an issue where you could be expected to get S1 in The Darkroom without | ||
| 7 | having U. | ||
| 8 | - Renamed a few locations. | ||
| 9 | |||
| 10 | Download: | ||
| 11 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/lingo2.apworld)<br/> | ||
| 12 | Template YAML: | ||
| 13 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/Lingo%202.yaml)<br/> | ||
| 14 | Source: | ||
| 15 | [v5.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v5.5) | ||
| 16 | |||
| 17 | ## v4.4 - 2025-09-14 | ||
| 18 | |||
| 19 | - Fixed panel set location names. | ||
| 20 | |||
| 21 | Download: | ||
| 22 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/lingo2.apworld)<br/> | ||
| 23 | Template YAML: | ||
| 24 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/Lingo%202.yaml)<br/> | ||
| 25 | Source: | ||
| 26 | [v4.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v4.4) | ||
| 27 | |||
| 28 | ## v4.3 - 2025-09-13 | ||
| 29 | |||
| 30 | - Added a location for the anti-collectable in The Repetitive. | ||
| 31 | - Added trap items. These remove letters from your keyboard until you use the | ||
| 32 | Key Return in The Entry, similar to the anti-collectable in The Repetitive. | ||
| 33 | This can be controlled using the `trap_percentage` option, which defaults to | ||
| 34 | zero. | ||
| 35 | - Fixed crash on load when using Python 3.11. | ||
| 36 | |||
| 37 | Download: | ||
| 38 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/lingo2.apworld)<br/> | ||
| 39 | Template YAML: | ||
| 40 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/Lingo%202.yaml)<br/> | ||
| 41 | Source: | ||
| 42 | [v4.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v4.3) | ||
| 43 | |||
| 44 | ## v3.2 - 2025-09-12 | ||
| 45 | |||
| 46 | - Initial release for testing. Features include door shuffle, letter shuffle, | ||
| 47 | and symbol shuffle. | ||
| 48 | |||
| 49 | Download: | ||
| 50 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/lingo2.apworld)<br/> | ||
| 51 | Template YAML: | ||
| 52 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/Lingo%202.yaml)<br/> | ||
| 53 | Source: | ||
| 54 | [v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v3.2) | ||
| diff --git a/apworld/__init__.py b/apworld/__init__.py index 4044d76..f1de503 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py | |||
| @@ -3,7 +3,7 @@ Archipelago init file for Lingo 2 | |||
| 3 | """ | 3 | """ |
| 4 | from BaseClasses import ItemClassification, Item, Tutorial | 4 | from BaseClasses import ItemClassification, Item, Tutorial |
| 5 | from worlds.AutoWorld import WebWorld, World | 5 | from worlds.AutoWorld import WebWorld, World |
| 6 | from .items import Lingo2Item | 6 | from .items import Lingo2Item, ANTI_COLLECTABLE_TRAPS |
| 7 | from .options import Lingo2Options | 7 | from .options import Lingo2Options |
| 8 | from .player_logic import Lingo2PlayerLogic | 8 | from .player_logic import Lingo2PlayerLogic |
| 9 | from .regions import create_regions | 9 | from .regions import create_regions |
| @@ -62,6 +62,20 @@ class Lingo2World(World): | |||
| 62 | total_locations = sum(len(locs) for locs in self.player_logic.locations_by_room.values()) | 62 | total_locations = sum(len(locs) for locs in self.player_logic.locations_by_room.values()) |
| 63 | 63 | ||
| 64 | item_difference = total_locations - len(pool) | 64 | item_difference = total_locations - len(pool) |
| 65 | |||
| 66 | if self.options.trap_percentage > 0: | ||
| 67 | num_traps = int(item_difference * self.options.trap_percentage / 100) | ||
| 68 | item_difference = item_difference - num_traps | ||
| 69 | |||
| 70 | trap_names = [] | ||
| 71 | trap_weights = [] | ||
| 72 | for letter_name, weight in self.static_logic.letter_weights.items(): | ||
| 73 | trap_names.append(f"Anti {letter_name}") | ||
| 74 | trap_weights.append(weight) | ||
| 75 | |||
| 76 | bad_letters = self.random.choices(trap_names, weights=trap_weights, k=num_traps) | ||
| 77 | pool += [self.create_item(trap_name) for trap_name in bad_letters] | ||
| 78 | |||
| 65 | for i in range(0, item_difference): | 79 | for i in range(0, item_difference): |
| 66 | pool.append(self.create_item(self.get_filler_item_name())) | 80 | pool.append(self.create_item(self.get_filler_item_name())) |
| 67 | 81 | ||
| @@ -69,6 +83,7 @@ class Lingo2World(World): | |||
| 69 | 83 | ||
| 70 | def create_item(self, name: str) -> Item: | 84 | def create_item(self, name: str) -> Item: |
| 71 | return Lingo2Item(name, ItemClassification.filler if name == self.get_filler_item_name() else | 85 | return Lingo2Item(name, ItemClassification.filler if name == self.get_filler_item_name() else |
| 86 | ItemClassification.trap if name in ANTI_COLLECTABLE_TRAPS else | ||
| 72 | ItemClassification.progression, | 87 | ItemClassification.progression, |
| 73 | self.item_name_to_id.get(name), self.player) | 88 | self.item_name_to_id.get(name), self.player) |
| 74 | 89 | ||
| @@ -82,8 +97,11 @@ class Lingo2World(World): | |||
| 82 | "keyholder_sanity", | 97 | "keyholder_sanity", |
| 83 | "shuffle_control_center_colors", | 98 | "shuffle_control_center_colors", |
| 84 | "shuffle_doors", | 99 | "shuffle_doors", |
| 100 | "shuffle_gallery_paintings", | ||
| 85 | "shuffle_letters", | 101 | "shuffle_letters", |
| 86 | "shuffle_symbols", | 102 | "shuffle_symbols", |
| 103 | "strict_cyan_ending", | ||
| 104 | "strict_purple_ending", | ||
| 87 | "victory_condition", | 105 | "victory_condition", |
| 88 | ] | 106 | ] |
| 89 | 107 | ||
| diff --git a/apworld/items.py b/apworld/items.py index 32568a3..28158c3 100644 --- a/apworld/items.py +++ b/apworld/items.py | |||
| @@ -27,3 +27,5 @@ SYMBOL_ITEMS: dict[data_pb2.PuzzleSymbol, str] = { | |||
| 27 | data_pb2.PuzzleSymbol.LINGO: "Lingo Symbol", | 27 | data_pb2.PuzzleSymbol.LINGO: "Lingo Symbol", |
| 28 | data_pb2.PuzzleSymbol.QUESTION: "Question Symbol", | 28 | data_pb2.PuzzleSymbol.QUESTION: "Question Symbol", |
| 29 | } | 29 | } |
| 30 | |||
| 31 | ANTI_COLLECTABLE_TRAPS: list[str] = [f"Anti {letter}" for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"] | ||
| diff --git a/apworld/options.py b/apworld/options.py index f72e826..3646eea 100644 --- a/apworld/options.py +++ b/apworld/options.py | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | from dataclasses import dataclass | 1 | from dataclasses import dataclass |
| 2 | 2 | ||
| 3 | from Options import PerGameCommonOptions, Toggle, Choice | 3 | from Options import PerGameCommonOptions, Toggle, Choice, DefaultOnToggle, Range |
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | class ShuffleDoors(Toggle): | 6 | class ShuffleDoors(DefaultOnToggle): |
| 7 | """If enabled, most doors will open from receiving an item rather than fulfilling the in-game requirements.""" | 7 | """If enabled, most doors will open from receiving an item rather than fulfilling the in-game requirements.""" |
| 8 | display_name = "Shuffle Doors" | 8 | display_name = "Shuffle Doors" |
| 9 | 9 | ||
| @@ -16,6 +16,11 @@ class ShuffleControlCenterColors(Toggle): | |||
| 16 | display_name = "Shuffle Control Center Colors" | 16 | display_name = "Shuffle Control Center Colors" |
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | class ShuffleGalleryPaintings(Toggle): | ||
| 20 | """If enabled, gallery paintings will appear from receiving an item rather than by triggering them normally.""" | ||
| 21 | display_name = "Shuffle Gallery Paintings" | ||
| 22 | |||
| 23 | |||
| 19 | class ShuffleLetters(Choice): | 24 | class ShuffleLetters(Choice): |
| 20 | """ | 25 | """ |
| 21 | Controls how letter unlocks are handled. Note that H1, I1, N1, and T1 will always be present at their vanilla | 26 | Controls how letter unlocks are handled. Note that H1, I1, N1, and T1 will always be present at their vanilla |
| @@ -87,8 +92,40 @@ class DaedalusRoofAccess(Toggle): | |||
| 87 | display_name = "Allow Daedalus Roof Access" | 92 | display_name = "Allow Daedalus Roof Access" |
| 88 | 93 | ||
| 89 | 94 | ||
| 95 | class StrictPurpleEnding(DefaultOnToggle): | ||
| 96 | """ | ||
| 97 | If enabled, the player will be required to have all purple (level 1) letters in order to get Purple Ending. | ||
| 98 | Otherwise, some of the letters may be skippable depending on the other options. | ||
| 99 | """ | ||
| 100 | display_name = "Strict Purple Ending" | ||
| 101 | |||
| 102 | |||
| 103 | class StrictCyanEnding(DefaultOnToggle): | ||
| 104 | """ | ||
| 105 | If enabled, the player will be required to have all cyan (level 2) letters in order to get Cyan Ending. Otherwise, | ||
| 106 | at least J2, Q2, and V2 are skippable. Others may also be skippable depending on the options chosen. | ||
| 107 | """ | ||
| 108 | display_name = "Strict Cyan Ending" | ||
| 109 | |||
| 110 | |||
| 90 | class VictoryCondition(Choice): | 111 | class VictoryCondition(Choice): |
| 91 | """Victory condition.""" | 112 | """ |
| 113 | This option determines what your goal is. | ||
| 114 | |||
| 115 | - **Gray Ending** (The Colorful) | ||
| 116 | - **Purple Ending** (The Sun Temple). This ordinarily requires all level 1 (purple) letters. | ||
| 117 | - **Mint Ending** (typing EXIT into the keyholders in Control Center) | ||
| 118 | - **Black Ending** (The Graveyard) | ||
| 119 | - **Blue Ending** (The Words) | ||
| 120 | - **Cyan Ending** (The Parthenon). This ordinarily requires almost all level 2 (cyan) letters. | ||
| 121 | - **Red Ending** (The Tower) | ||
| 122 | - **Plum Ending** (The Wondrous / The Door) | ||
| 123 | - **Orange Ending** (the castle in Daedalus) | ||
| 124 | - **Gold Ending** (The Gold). This involves going through the color rooms in Daedalus. | ||
| 125 | - **Yellow Ending** (The Gallery). This requires unlocking all gallery paintings. | ||
| 126 | - **Green Ending** (The Ancient). This requires filling all keyholders with specific letters. | ||
| 127 | - **White Ending** (Control Center). This combines every other ending. | ||
| 128 | """ | ||
| 92 | display_name = "Victory Condition" | 129 | display_name = "Victory Condition" |
| 93 | option_gray_ending = 0 | 130 | option_gray_ending = 0 |
| 94 | option_purple_ending = 1 | 131 | option_purple_ending = 1 |
| @@ -105,13 +142,25 @@ class VictoryCondition(Choice): | |||
| 105 | option_white_ending = 12 | 142 | option_white_ending = 12 |
| 106 | 143 | ||
| 107 | 144 | ||
| 145 | class TrapPercentage(Range): | ||
| 146 | """Replaces junk items with traps, at the specified rate.""" | ||
| 147 | display_name = "Trap Percentage" | ||
| 148 | range_start = 0 | ||
| 149 | range_end = 100 | ||
| 150 | default = 0 | ||
| 151 | |||
| 152 | |||
| 108 | @dataclass | 153 | @dataclass |
| 109 | class Lingo2Options(PerGameCommonOptions): | 154 | class Lingo2Options(PerGameCommonOptions): |
| 110 | shuffle_doors: ShuffleDoors | 155 | shuffle_doors: ShuffleDoors |
| 111 | shuffle_control_center_colors: ShuffleControlCenterColors | 156 | shuffle_control_center_colors: ShuffleControlCenterColors |
| 157 | shuffle_gallery_paintings: ShuffleGalleryPaintings | ||
| 112 | shuffle_letters: ShuffleLetters | 158 | shuffle_letters: ShuffleLetters |
| 113 | shuffle_symbols: ShuffleSymbols | 159 | shuffle_symbols: ShuffleSymbols |
| 114 | keyholder_sanity: KeyholderSanity | 160 | keyholder_sanity: KeyholderSanity |
| 115 | cyan_door_behavior: CyanDoorBehavior | 161 | cyan_door_behavior: CyanDoorBehavior |
| 116 | daedalus_roof_access: DaedalusRoofAccess | 162 | daedalus_roof_access: DaedalusRoofAccess |
| 163 | strict_purple_ending: StrictPurpleEnding | ||
| 164 | strict_cyan_ending: StrictCyanEnding | ||
| 117 | victory_condition: VictoryCondition | 165 | victory_condition: VictoryCondition |
| 166 | trap_percentage: TrapPercentage | ||
| diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 42b36e6..4aa481d 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py | |||
| @@ -30,6 +30,11 @@ class AccessRequirements: | |||
| 30 | # This is an AND of ORs. | 30 | # This is an AND of ORs. |
| 31 | or_logic: list[list["AccessRequirements"]] | 31 | or_logic: list[list["AccessRequirements"]] |
| 32 | 32 | ||
| 33 | # When complete_at is set, at least that many of the requirements in possibilities must be accessible. This should | ||
| 34 | # only be used for doors with complete_at > 1, as or_logic is more efficient for complete_at == 1. | ||
| 35 | complete_at: int | None | ||
| 36 | possibilities: list["AccessRequirements"] | ||
| 37 | |||
| 33 | def __init__(self): | 38 | def __init__(self): |
| 34 | self.items = set() | 39 | self.items = set() |
| 35 | self.progressives = dict() | 40 | self.progressives = dict() |
| @@ -37,6 +42,20 @@ class AccessRequirements: | |||
| 37 | self.letters = dict() | 42 | self.letters = dict() |
| 38 | self.cyans = False | 43 | self.cyans = False |
| 39 | self.or_logic = list() | 44 | self.or_logic = list() |
| 45 | self.complete_at = None | ||
| 46 | self.possibilities = list() | ||
| 47 | |||
| 48 | def copy(self) -> "AccessRequirements": | ||
| 49 | reqs = AccessRequirements() | ||
| 50 | reqs.items = self.items.copy() | ||
| 51 | reqs.progressives = self.progressives.copy() | ||
| 52 | reqs.rooms = self.rooms.copy() | ||
| 53 | reqs.letters = self.letters.copy() | ||
| 54 | reqs.cyans = self.cyans | ||
| 55 | reqs.or_logic = [[other_req.copy() for other_req in disjunction] for disjunction in self.or_logic] | ||
| 56 | reqs.complete_at = self.complete_at | ||
| 57 | reqs.possibilities = self.possibilities.copy() | ||
| 58 | return reqs | ||
| 40 | 59 | ||
| 41 | def merge(self, other: "AccessRequirements"): | 60 | def merge(self, other: "AccessRequirements"): |
| 42 | for item in other.items: | 61 | for item in other.items: |
| @@ -56,9 +75,97 @@ class AccessRequirements: | |||
| 56 | for disjunction in other.or_logic: | 75 | for disjunction in other.or_logic: |
| 57 | self.or_logic.append(disjunction) | 76 | self.or_logic.append(disjunction) |
| 58 | 77 | ||
| 78 | if other.complete_at is not None: | ||
| 79 | # Merging multiple requirements that use complete_at sucks, and is part of why we want to minimize use of | ||
| 80 | # it. If both requirements use complete_at, we will cheat by using the or_logic field, which supports | ||
| 81 | # conjunctions of requirements. | ||
| 82 | if self.complete_at is not None: | ||
| 83 | print("Merging requirements with complete_at > 1. This is messy and should be avoided!") | ||
| 84 | |||
| 85 | left_req = AccessRequirements() | ||
| 86 | left_req.complete_at = self.complete_at | ||
| 87 | left_req.possibilities = self.possibilities | ||
| 88 | self.or_logic.append([left_req]) | ||
| 89 | |||
| 90 | self.complete_at = None | ||
| 91 | self.possibilities = list() | ||
| 92 | |||
| 93 | right_req = AccessRequirements() | ||
| 94 | right_req.complete_at = other.complete_at | ||
| 95 | right_req.possibilities = other.possibilities | ||
| 96 | self.or_logic.append([right_req]) | ||
| 97 | else: | ||
| 98 | self.complete_at = other.complete_at | ||
| 99 | self.possibilities = other.possibilities | ||
| 100 | |||
| 59 | def is_empty(self) -> bool: | 101 | def is_empty(self) -> bool: |
| 60 | return (len(self.items) == 0 and len(self.progressives) == 0 and len(self.rooms) == 0 and len(self.letters) == 0 | 102 | return (len(self.items) == 0 and len(self.progressives) == 0 and len(self.rooms) == 0 and len(self.letters) == 0 |
| 61 | and not self.cyans and len(self.or_logic) == 0) | 103 | and not self.cyans and len(self.or_logic) == 0 and self.complete_at is None) |
| 104 | |||
| 105 | def __eq__(self, other: "AccessRequirements"): | ||
| 106 | return (self.items == other.items and self.progressives == other.progressives and self.rooms == other.rooms and | ||
| 107 | self.letters == other.letters and self.cyans == other.cyans and self.or_logic == other.or_logic and | ||
| 108 | self.complete_at == other.complete_at and self.possibilities == other.possibilities) | ||
| 109 | |||
| 110 | def simplify(self): | ||
| 111 | resimplify = False | ||
| 112 | |||
| 113 | if len(self.or_logic) > 0: | ||
| 114 | old_or_logic = self.or_logic | ||
| 115 | |||
| 116 | def remove_redundant(sub_reqs: "AccessRequirements"): | ||
| 117 | new_reqs = sub_reqs.copy() | ||
| 118 | new_reqs.letters = {l: v for l, v in new_reqs.letters.items() if self.letters.get(l, 0) < v} | ||
| 119 | if new_reqs != sub_reqs: | ||
| 120 | return new_reqs | ||
| 121 | else: | ||
| 122 | return sub_reqs | ||
| 123 | |||
| 124 | self.or_logic = [] | ||
| 125 | for disjunction in old_or_logic: | ||
| 126 | new_disjunction = [] | ||
| 127 | for ssr in disjunction: | ||
| 128 | new_ssr = remove_redundant(ssr) | ||
| 129 | if not new_ssr.is_empty(): | ||
| 130 | new_disjunction.append(new_ssr) | ||
| 131 | else: | ||
| 132 | new_disjunction.clear() | ||
| 133 | break | ||
| 134 | if len(new_disjunction) == 1: | ||
| 135 | self.merge(new_disjunction[0]) | ||
| 136 | resimplify = True | ||
| 137 | elif len(new_disjunction) > 1: | ||
| 138 | if all(cjr == new_disjunction[0] for cjr in new_disjunction): | ||
| 139 | self.merge(new_disjunction[0]) | ||
| 140 | resimplify = True | ||
| 141 | else: | ||
| 142 | self.or_logic.append(new_disjunction) | ||
| 143 | |||
| 144 | if resimplify: | ||
| 145 | self.simplify() | ||
| 146 | |||
| 147 | def get_referenced_rooms(self): | ||
| 148 | result = set(self.rooms) | ||
| 149 | |||
| 150 | for disjunction in self.or_logic: | ||
| 151 | for sub_req in disjunction: | ||
| 152 | result = result.union(sub_req.get_referenced_rooms()) | ||
| 153 | |||
| 154 | for sub_req in self.possibilities: | ||
| 155 | result = result.union(sub_req.get_referenced_rooms()) | ||
| 156 | |||
| 157 | return result | ||
| 158 | |||
| 159 | def remove_room(self, room: str): | ||
| 160 | if room in self.rooms: | ||
| 161 | self.rooms.remove(room) | ||
| 162 | |||
| 163 | for disjunction in self.or_logic: | ||
| 164 | for sub_req in disjunction: | ||
| 165 | sub_req.remove_room(room) | ||
| 166 | |||
| 167 | for sub_req in self.possibilities: | ||
| 168 | sub_req.remove_room(room) | ||
| 62 | 169 | ||
| 63 | def __repr__(self): | 170 | def __repr__(self): |
| 64 | parts = [] | 171 | parts = [] |
| @@ -74,7 +181,11 @@ class AccessRequirements: | |||
| 74 | parts.append(f"cyans=True") | 181 | parts.append(f"cyans=True") |
| 75 | if len(self.or_logic) > 0: | 182 | if len(self.or_logic) > 0: |
| 76 | parts.append(f"or_logic={self.or_logic}") | 183 | parts.append(f"or_logic={self.or_logic}") |
| 77 | return f"AccessRequirements({", ".join(parts)})" | 184 | if self.complete_at is not None: |
| 185 | parts.append(f"complete_at={self.complete_at}") | ||
| 186 | if len(self.possibilities) > 0: | ||
| 187 | parts.append(f"possibilities={self.possibilities}") | ||
| 188 | return "AccessRequirements(" + ", ".join(parts) + ")" | ||
| 78 | 189 | ||
| 79 | 190 | ||
| 80 | class PlayerLocation(NamedTuple): | 191 | class PlayerLocation(NamedTuple): |
| @@ -156,6 +267,9 @@ class Lingo2PlayerLogic: | |||
| 156 | not self.world.options.shuffle_control_center_colors): | 267 | not self.world.options.shuffle_control_center_colors): |
| 157 | continue | 268 | continue |
| 158 | 269 | ||
| 270 | if door.type == data_pb2.DoorType.GALLERY_PAINTING and not self.world.options.shuffle_gallery_paintings: | ||
| 271 | continue | ||
| 272 | |||
| 159 | door_item_name = self.world.static_logic.get_door_item_name(door) | 273 | door_item_name = self.world.static_logic.get_door_item_name(door) |
| 160 | self.item_by_door[door.id] = (door_item_name, 1) | 274 | self.item_by_door[door.id] = (door_item_name, 1) |
| 161 | self.real_items.append(door_item_name) | 275 | self.real_items.append(door_item_name) |
| @@ -306,7 +420,6 @@ class Lingo2PlayerLogic: | |||
| 306 | door = self.world.static_logic.objects.doors[door_id] | 420 | door = self.world.static_logic.objects.doors[door_id] |
| 307 | reqs = AccessRequirements() | 421 | reqs = AccessRequirements() |
| 308 | 422 | ||
| 309 | # TODO: lavender_cubes, endings | ||
| 310 | if not door.HasField("complete_at") or door.complete_at == 0: | 423 | if not door.HasField("complete_at") or door.complete_at == 0: |
| 311 | for proxy in door.panels: | 424 | for proxy in door.panels: |
| 312 | panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) | 425 | panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) |
| @@ -323,8 +436,10 @@ class Lingo2PlayerLogic: | |||
| 323 | if len(disjunction) > 0: | 436 | if len(disjunction) > 0: |
| 324 | reqs.or_logic.append(disjunction) | 437 | reqs.or_logic.append(disjunction) |
| 325 | else: | 438 | else: |
| 326 | # TODO: Handle complete_at > 1 | 439 | reqs.complete_at = door.complete_at |
| 327 | pass | 440 | for proxy in door.panels: |
| 441 | panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) | ||
| 442 | reqs.possibilities.append(panel_reqs) | ||
| 328 | 443 | ||
| 329 | if door.HasField("control_center_color"): | 444 | if door.HasField("control_center_color"): |
| 330 | # TODO: Logic for ensuring two CC states aren't needed at once. | 445 | # TODO: Logic for ensuring two CC states aren't needed at once. |
| @@ -365,6 +480,8 @@ class Lingo2PlayerLogic: | |||
| 365 | sub_reqs = self.get_door_open_reqs(sub_door_id) | 480 | sub_reqs = self.get_door_open_reqs(sub_door_id) |
| 366 | reqs.merge(sub_reqs) | 481 | reqs.merge(sub_reqs) |
| 367 | 482 | ||
| 483 | reqs.simplify() | ||
| 484 | |||
| 368 | return reqs | 485 | return reqs |
| 369 | 486 | ||
| 370 | # This gets the requirements to open a door within the world. When a door is shuffled, this means having the item | 487 | # This gets the requirements to open a door within the world. When a door is shuffled, this means having the item |
| diff --git a/apworld/regions.py b/apworld/regions.py index e30493c..993eec8 100644 --- a/apworld/regions.py +++ b/apworld/regions.py | |||
| @@ -11,12 +11,17 @@ if TYPE_CHECKING: | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | def create_region(room, world: "Lingo2World") -> Region: | 13 | def create_region(room, world: "Lingo2World") -> Region: |
| 14 | new_region = Region(world.static_logic.get_room_region_name(room.id), world.player, world.multiworld) | 14 | return Region(world.static_logic.get_room_region_name(room.id), world.player, world.multiworld) |
| 15 | 15 | ||
| 16 | |||
| 17 | def create_locations(room, new_region: Region, world: "Lingo2World", regions: dict[str, Region]): | ||
| 16 | for location in world.player_logic.locations_by_room.get(room.id, {}): | 18 | for location in world.player_logic.locations_by_room.get(room.id, {}): |
| 19 | reqs = location.reqs.copy() | ||
| 20 | reqs.remove_room(new_region.name) | ||
| 21 | |||
| 17 | new_location = Lingo2Location(world.player, world.static_logic.location_id_to_name[location.code], | 22 | new_location = Lingo2Location(world.player, world.static_logic.location_id_to_name[location.code], |
| 18 | location.code, new_region) | 23 | location.code, new_region) |
| 19 | new_location.access_rule = make_location_lambda(location.reqs, world) | 24 | new_location.access_rule = make_location_lambda(reqs, world, regions) |
| 20 | new_region.locations.append(new_location) | 25 | new_region.locations.append(new_location) |
| 21 | 26 | ||
| 22 | for event_name, item_name in world.player_logic.event_loc_item_by_room.get(room.id, {}).items(): | 27 | for event_name, item_name in world.player_logic.event_loc_item_by_room.get(room.id, {}).items(): |
| @@ -25,17 +30,23 @@ def create_region(room, world: "Lingo2World") -> Region: | |||
| 25 | new_location.place_locked_item(event_item) | 30 | new_location.place_locked_item(event_item) |
| 26 | new_region.locations.append(new_location) | 31 | new_region.locations.append(new_location) |
| 27 | 32 | ||
| 28 | return new_region | ||
| 29 | |||
| 30 | |||
| 31 | def create_regions(world: "Lingo2World"): | 33 | def create_regions(world: "Lingo2World"): |
| 32 | regions = { | 34 | regions = { |
| 33 | "Menu": Region("Menu", world.player, world.multiworld) | 35 | "Menu": Region("Menu", world.player, world.multiworld) |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 38 | region_and_room = [] | ||
| 39 | |||
| 40 | # Create the regions in two stages. First, make the actual region objects and memoize them. Then, add all of the | ||
| 41 | # locations. This allows us to reference the actual region objects in the access rules for the locations, which is | ||
| 42 | # faster than having to look them up during access checking. | ||
| 36 | for room in world.static_logic.objects.rooms: | 43 | for room in world.static_logic.objects.rooms: |
| 37 | region = create_region(room, world) | 44 | region = create_region(room, world) |
| 38 | regions[region.name] = region | 45 | regions[region.name] = region |
| 46 | region_and_room.append((region, room)) | ||
| 47 | |||
| 48 | for (region, room) in region_and_room: | ||
| 49 | create_locations(room, region, world, regions) | ||
| 39 | 50 | ||
| 40 | regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game") | 51 | regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game") |
| 41 | 52 | ||
| @@ -46,6 +57,10 @@ def create_regions(world: "Lingo2World"): | |||
| 46 | 57 | ||
| 47 | from_region = world.static_logic.get_room_region_name(connection.from_room) | 58 | from_region = world.static_logic.get_room_region_name(connection.from_room) |
| 48 | to_region = world.static_logic.get_room_region_name(connection.to_room) | 59 | to_region = world.static_logic.get_room_region_name(connection.to_room) |
| 60 | |||
| 61 | if from_region not in regions or to_region not in regions: | ||
| 62 | continue | ||
| 63 | |||
| 49 | connection_name = f"{from_region} -> {to_region}" | 64 | connection_name = f"{from_region} -> {to_region}" |
| 50 | 65 | ||
| 51 | reqs = AccessRequirements() | 66 | reqs = AccessRequirements() |
| @@ -82,14 +97,22 @@ def create_regions(world: "Lingo2World"): | |||
| 82 | else: | 97 | else: |
| 83 | connection_name = f"{connection_name} (via panel {panel.name})" | 98 | connection_name = f"{connection_name} (via panel {panel.name})" |
| 84 | 99 | ||
| 85 | if from_region in regions and to_region in regions: | 100 | if connection.HasField("purple_ending") and connection.purple_ending and world.options.strict_purple_ending: |
| 86 | connection = Entrance(world.player, connection_name, regions[from_region]) | 101 | world.player_logic.add_solution_reqs(reqs, "abcdefghijklmnopqrstuvwxyz") |
| 87 | connection.access_rule = make_location_lambda(reqs, world) | 102 | |
| 103 | if connection.HasField("cyan_ending") and connection.cyan_ending and world.options.strict_cyan_ending: | ||
| 104 | world.player_logic.add_solution_reqs(reqs, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz") | ||
| 105 | |||
| 106 | reqs.simplify() | ||
| 107 | reqs.remove_room(from_region) | ||
| 108 | |||
| 109 | connection = Entrance(world.player, connection_name, regions[from_region]) | ||
| 110 | connection.access_rule = make_location_lambda(reqs, world, regions) | ||
| 88 | 111 | ||
| 89 | regions[from_region].exits.append(connection) | 112 | regions[from_region].exits.append(connection) |
| 90 | connection.connect(regions[to_region]) | 113 | connection.connect(regions[to_region]) |
| 91 | 114 | ||
| 92 | for region in reqs.rooms: | 115 | for region in reqs.get_referenced_rooms(): |
| 93 | world.multiworld.register_indirect_condition(regions[region], connection) | 116 | world.multiworld.register_indirect_condition(regions[region], connection) |
| 94 | 117 | ||
| 95 | world.multiworld.regions += regions.values() | 118 | world.multiworld.regions += regions.values() |
| diff --git a/apworld/rules.py b/apworld/rules.py index 0bff056..c077858 100644 --- a/apworld/rules.py +++ b/apworld/rules.py | |||
| @@ -1,14 +1,15 @@ | |||
| 1 | from collections.abc import Callable | 1 | from collections.abc import Callable |
| 2 | from typing import TYPE_CHECKING | 2 | from typing import TYPE_CHECKING |
| 3 | 3 | ||
| 4 | from BaseClasses import CollectionState | 4 | from BaseClasses import CollectionState, Region |
| 5 | from .player_logic import AccessRequirements | 5 | from .player_logic import AccessRequirements |
| 6 | 6 | ||
| 7 | if TYPE_CHECKING: | 7 | if TYPE_CHECKING: |
| 8 | from . import Lingo2World | 8 | from . import Lingo2World |
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirements, world: "Lingo2World") -> bool: | 11 | def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirements, regions: list[Region], |
| 12 | world: "Lingo2World") -> bool: | ||
| 12 | if not all(state.has(item, world.player) for item in reqs.items): | 13 | if not all(state.has(item, world.player) for item in reqs.items): |
| 13 | return False | 14 | return False |
| 14 | 15 | ||
| @@ -18,6 +19,9 @@ def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirem | |||
| 18 | if not all(state.can_reach_region(region_name, world.player) for region_name in reqs.rooms): | 19 | if not all(state.can_reach_region(region_name, world.player) for region_name in reqs.rooms): |
| 19 | return False | 20 | return False |
| 20 | 21 | ||
| 22 | if not all(state.can_reach(region) for region in regions): | ||
| 23 | return False | ||
| 24 | |||
| 21 | for letter_key, letter_level in reqs.letters.items(): | 25 | for letter_key, letter_level in reqs.letters.items(): |
| 22 | if not state.has(letter_key, world.player, letter_level): | 26 | if not state.has(letter_key, world.player, letter_level): |
| 23 | return False | 27 | return False |
| @@ -28,11 +32,32 @@ def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirem | |||
| 28 | return False | 32 | return False |
| 29 | 33 | ||
| 30 | if len(reqs.or_logic) > 0: | 34 | if len(reqs.or_logic) > 0: |
| 31 | if not all(any(lingo2_can_satisfy_requirements(state, sub_reqs, world) for sub_reqs in subjunction) | 35 | if not all(any(lingo2_can_satisfy_requirements(state, sub_reqs, [], world) for sub_reqs in subjunction) |
| 32 | for subjunction in reqs.or_logic): | 36 | for subjunction in reqs.or_logic): |
| 33 | return False | 37 | return False |
| 34 | 38 | ||
| 39 | if reqs.complete_at is not None: | ||
| 40 | completed = 0 | ||
| 41 | checked = 0 | ||
| 42 | for possibility in reqs.possibilities: | ||
| 43 | checked += 1 | ||
| 44 | if lingo2_can_satisfy_requirements(state, possibility, [], world): | ||
| 45 | completed += 1 | ||
| 46 | if completed >= reqs.complete_at: | ||
| 47 | break | ||
| 48 | elif len(reqs.possibilities) - checked + completed < reqs.complete_at: | ||
| 49 | # There aren't enough remaining possibilities for the check to pass. | ||
| 50 | return False | ||
| 51 | if completed < reqs.complete_at: | ||
| 52 | return False | ||
| 53 | |||
| 35 | return True | 54 | return True |
| 36 | 55 | ||
| 37 | def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World") -> Callable[[CollectionState], bool]: | 56 | def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World", |
| 38 | return lambda state: lingo2_can_satisfy_requirements(state, reqs, world) | 57 | regions: dict[str, Region]) -> Callable[[CollectionState], bool]: |
| 58 | # Replace required rooms with regions for the top level requirement, which saves looking up the regions during rule | ||
| 59 | # checking. | ||
| 60 | required_regions = [regions[room_name] for room_name in reqs.rooms] | ||
| 61 | new_reqs = reqs.copy() | ||
| 62 | new_reqs.rooms.clear() | ||
| 63 | return lambda state: lingo2_can_satisfy_requirements(state, new_reqs, required_regions, world) | ||
| diff --git a/apworld/static_logic.py b/apworld/static_logic.py index 07800f8..e4d7d49 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | from .generated import data_pb2 as data_pb2 | 1 | from .generated import data_pb2 as data_pb2 |
| 2 | from .items import SYMBOL_ITEMS | 2 | from .items import SYMBOL_ITEMS, ANTI_COLLECTABLE_TRAPS |
| 3 | import pkgutil | 3 | import pkgutil |
| 4 | 4 | ||
| 5 | class Lingo2StaticLogic: | 5 | class Lingo2StaticLogic: |
| @@ -12,11 +12,14 @@ class Lingo2StaticLogic: | |||
| 12 | item_name_groups: dict[str, list[str]] | 12 | item_name_groups: dict[str, list[str]] |
| 13 | location_name_groups: dict[str, list[str]] | 13 | location_name_groups: dict[str, list[str]] |
| 14 | 14 | ||
| 15 | letter_weights: dict[str, int] | ||
| 16 | |||
| 15 | def __init__(self): | 17 | def __init__(self): |
| 16 | self.item_id_to_name = {} | 18 | self.item_id_to_name = {} |
| 17 | self.location_id_to_name = {} | 19 | self.location_id_to_name = {} |
| 18 | self.item_name_groups = {} | 20 | self.item_name_groups = {} |
| 19 | self.location_name_groups = {} | 21 | self.location_name_groups = {} |
| 22 | self.letter_weights = {} | ||
| 20 | 23 | ||
| 21 | file = pkgutil.get_data(__name__, "generated/data.binpb") | 24 | file = pkgutil.get_data(__name__, "generated/data.binpb") |
| 22 | self.objects = data_pb2.AllObjects() | 25 | self.objects = data_pb2.AllObjects() |
| @@ -68,9 +71,16 @@ class Lingo2StaticLogic: | |||
| 68 | for symbol_name in SYMBOL_ITEMS.values(): | 71 | for symbol_name in SYMBOL_ITEMS.values(): |
| 69 | self.item_id_to_name[self.objects.special_ids[symbol_name]] = symbol_name | 72 | self.item_id_to_name[self.objects.special_ids[symbol_name]] = symbol_name |
| 70 | 73 | ||
| 74 | for trap_name in ANTI_COLLECTABLE_TRAPS: | ||
| 75 | self.item_id_to_name[self.objects.special_ids[trap_name]] = trap_name | ||
| 76 | |||
| 71 | self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()} | 77 | self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()} |
| 72 | self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()} | 78 | self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()} |
| 73 | 79 | ||
| 80 | for panel in self.objects.panels: | ||
| 81 | for letter in panel.answer.upper(): | ||
| 82 | self.letter_weights[letter] = self.letter_weights.get(letter, 0) + 1 | ||
| 83 | |||
| 74 | def get_door_item_name(self, door: data_pb2.Door) -> str: | 84 | def get_door_item_name(self, door: data_pb2.Door) -> str: |
| 75 | return f"{self.get_map_object_map_name(door)} - {door.name}" | 85 | return f"{self.get_map_object_map_name(door)} - {door.name}" |
| 76 | 86 | ||
| @@ -94,7 +104,7 @@ class Lingo2StaticLogic: | |||
| 94 | if door.type != data_pb2.DoorType.STANDARD: | 104 | if door.type != data_pb2.DoorType.STANDARD: |
| 95 | return None | 105 | return None |
| 96 | 106 | ||
| 97 | if len(door.keyholders) > 0 or len(door.endings) > 0: | 107 | if len(door.keyholders) > 0 or len(door.endings) > 0 or door.HasField("complete_at"): |
| 98 | return None | 108 | return None |
| 99 | 109 | ||
| 100 | if len(door.panels) > 4: | 110 | if len(door.panels) > 4: |
| @@ -130,7 +140,7 @@ class Lingo2StaticLogic: | |||
| 130 | for panel_id in door.panels] | 140 | for panel_id in door.panels] |
| 131 | panel_names.sort() | 141 | panel_names.sort() |
| 132 | 142 | ||
| 133 | return f"{map_part} - {", ".join(panel_names)}" | 143 | return map_part + " - " + ", ".join(panel_names) |
| 134 | 144 | ||
| 135 | def get_door_location_name_by_id(self, door_id: int) -> str: | 145 | def get_door_location_name_by_id(self, door_id: int) -> str: |
| 136 | door = self.objects.doors[door_id] | 146 | door = self.objects.doors[door_id] |
| diff --git a/apworld/version.py b/apworld/version.py index 645cce6..ac799cd 100644 --- a/apworld/version.py +++ b/apworld/version.py | |||
| @@ -1 +1 @@ | |||
| APWORLD_VERSION = 0 | APWORLD_VERSION = 6 | ||
| diff --git a/client/Archipelago/client.gd b/client/Archipelago/client.gd index 2e080fd..843647d 100644 --- a/client/Archipelago/client.gd +++ b/client/Archipelago/client.gd | |||
| @@ -47,6 +47,8 @@ signal location_scout_received(item_id, location_id, player, flags) | |||
| 47 | func _init(): | 47 | func _init(): |
| 48 | set_process_mode(Node.PROCESS_MODE_ALWAYS) | 48 | set_process_mode(Node.PROCESS_MODE_ALWAYS) |
| 49 | 49 | ||
| 50 | _ws.inbound_buffer_size = 8388608 | ||
| 51 | |||
| 50 | global._print("Instantiated APClient") | 52 | global._print("Instantiated APClient") |
| 51 | 53 | ||
| 52 | # Read AP datapackages from file, if there are any | 54 | # Read AP datapackages from file, if there are any |
| @@ -225,7 +227,7 @@ func _process(_delta): | |||
| 225 | error_message = "Unknown error." | 227 | error_message = "Unknown error." |
| 226 | 228 | ||
| 227 | _initiated_disconnect = true | 229 | _initiated_disconnect = true |
| 228 | _ws.disconnect_from_host() | 230 | _ws.close() |
| 229 | 231 | ||
| 230 | emit_signal("could_not_connect", error_message) | 232 | emit_signal("could_not_connect", error_message) |
| 231 | global._print("Connection to AP refused") | 233 | global._print("Connection to AP refused") |
| @@ -309,7 +311,7 @@ func connectToServer(server, un, pw): | |||
| 309 | % err | 311 | % err |
| 310 | ) | 312 | ) |
| 311 | ) | 313 | ) |
| 312 | global._print("Could not connect to AP: " + err) | 314 | global._print("Could not connect to AP: %d" % err) |
| 313 | return | 315 | return |
| 314 | _should_process = true | 316 | _should_process = true |
| 315 | 317 | ||
| diff --git a/client/Archipelago/compass.gd b/client/Archipelago/compass.gd new file mode 100644 index 0000000..c90475a --- /dev/null +++ b/client/Archipelago/compass.gd | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | extends Node2D | ||
| 2 | |||
| 3 | const RADIUS = 48 | ||
| 4 | |||
| 5 | var _font | ||
| 6 | |||
| 7 | |||
| 8 | func _ready(): | ||
| 9 | _font = load("res://assets/fonts/Lingo2.ttf") | ||
| 10 | |||
| 11 | |||
| 12 | func _draw(): | ||
| 13 | draw_circle(Vector2.ZERO, RADIUS, Color(1.0, 1.0, 1.0, 0.8), true) | ||
| 14 | draw_circle(Vector2.ZERO, RADIUS, Color.BLACK, false) | ||
| 15 | draw_string( | ||
| 16 | _font, | ||
| 17 | Vector2(-4, -RADIUS * 3.0 / 4.0), | ||
| 18 | "N", | ||
| 19 | HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT, | ||
| 20 | -1, | ||
| 21 | 16, | ||
| 22 | Color.BLACK | ||
| 23 | ) | ||
| 24 | draw_set_transform(Vector2.ZERO, PI / 2) | ||
| 25 | draw_string( | ||
| 26 | _font, | ||
| 27 | Vector2(-4, -RADIUS * 3.0 / 4.0), | ||
| 28 | "E", | ||
| 29 | HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT, | ||
| 30 | -1, | ||
| 31 | 16, | ||
| 32 | Color.BLACK | ||
| 33 | ) | ||
| 34 | draw_set_transform(Vector2.ZERO, PI) | ||
| 35 | draw_string( | ||
| 36 | _font, | ||
| 37 | Vector2(-4, -RADIUS * 3.0 / 4.0), | ||
| 38 | "S", | ||
| 39 | HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT, | ||
| 40 | -1, | ||
| 41 | 16, | ||
| 42 | Color.BLACK | ||
| 43 | ) | ||
| 44 | draw_set_transform(Vector2.ZERO, PI * 3.0 / 2.0) | ||
| 45 | draw_string( | ||
| 46 | _font, | ||
| 47 | Vector2(-4, -RADIUS * 3.0 / 4.0), | ||
| 48 | "W", | ||
| 49 | HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT, | ||
| 50 | -1, | ||
| 51 | 16, | ||
| 52 | Color.BLACK | ||
| 53 | ) | ||
| 54 | draw_set_transform(Vector2.ZERO) | ||
| 55 | draw_colored_polygon( | ||
| 56 | PackedVector2Array( | ||
| 57 | [Vector2(0, -RADIUS * 5.0 / 8.0), Vector2(-RADIUS / 6.0, 0), Vector2(RADIUS / 6.0, 0)] | ||
| 58 | ), | ||
| 59 | Color.RED | ||
| 60 | ) | ||
| 61 | draw_colored_polygon( | ||
| 62 | PackedVector2Array( | ||
| 63 | [Vector2(0, RADIUS * 5.0 / 8.0), Vector2(-RADIUS / 6.0, 0), Vector2(RADIUS / 6.0, 0)] | ||
| 64 | ), | ||
| 65 | Color.GRAY | ||
| 66 | ) | ||
| diff --git a/client/Archipelago/compass_overlay.gd b/client/Archipelago/compass_overlay.gd new file mode 100644 index 0000000..56e81ff --- /dev/null +++ b/client/Archipelago/compass_overlay.gd | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | extends CanvasLayer | ||
| 2 | |||
| 3 | var SCRIPT_compass | ||
| 4 | |||
| 5 | var compass | ||
| 6 | |||
| 7 | |||
| 8 | func _ready(): | ||
| 9 | compass = SCRIPT_compass.new() | ||
| 10 | compass.position = Vector2(1840, 80) | ||
| 11 | add_child(compass) | ||
| 12 | |||
| 13 | visible = false | ||
| 14 | |||
| 15 | |||
| 16 | func update_rotation(ry): | ||
| 17 | compass.rotation = ry | ||
| diff --git a/client/Archipelago/door.gd b/client/Archipelago/door.gd index fead818..49f5728 100644 --- a/client/Archipelago/door.gd +++ b/client/Archipelago/door.gd | |||
| @@ -28,6 +28,14 @@ func _ready(): | |||
| 28 | 28 | ||
| 29 | call_deferred("_readier") | 29 | call_deferred("_readier") |
| 30 | 30 | ||
| 31 | if global.map == "the_sun_temple": | ||
| 32 | if name == "spe_EndPlatform" or name == "spe_entry_2": | ||
| 33 | senders = [NodePath("/root/scene/Panels/EndCheck_dog")] | ||
| 34 | |||
| 35 | if global.map == "the_parthenon": | ||
| 36 | if name == "spe_entry_1": | ||
| 37 | senders = [NodePath("/root/scene/Panels/EndCheck_dog")] | ||
| 38 | |||
| 31 | super._ready() | 39 | super._ready() |
| 32 | 40 | ||
| 33 | 41 | ||
| diff --git a/client/Archipelago/gamedata.gd b/client/Archipelago/gamedata.gd index d8d16ed..41d966a 100644 --- a/client/Archipelago/gamedata.gd +++ b/client/Archipelago/gamedata.gd | |||
| @@ -11,6 +11,7 @@ var map_id_by_name = {} | |||
| 11 | var progressive_id_by_ap_id = {} | 11 | var progressive_id_by_ap_id = {} |
| 12 | var letter_id_by_ap_id = {} | 12 | var letter_id_by_ap_id = {} |
| 13 | var symbol_item_ids = [] | 13 | var symbol_item_ids = [] |
| 14 | var anti_trap_ids = {} | ||
| 14 | 15 | ||
| 15 | var kSYMBOL_ITEMS | 16 | var kSYMBOL_ITEMS |
| 16 | 17 | ||
| @@ -97,6 +98,12 @@ func load(data_bytes): | |||
| 97 | for symbol_name in kSYMBOL_ITEMS.values(): | 98 | for symbol_name in kSYMBOL_ITEMS.values(): |
| 98 | symbol_item_ids.append(objects.get_special_ids()[symbol_name]) | 99 | symbol_item_ids.append(objects.get_special_ids()[symbol_name]) |
| 99 | 100 | ||
| 101 | for special_name in objects.get_special_ids().keys(): | ||
| 102 | if special_name.begins_with("Anti "): | ||
| 103 | anti_trap_ids[objects.get_special_ids()[special_name]] = ( | ||
| 104 | special_name.substr(5).to_lower() | ||
| 105 | ) | ||
| 106 | |||
| 100 | 107 | ||
| 101 | func get_door_for_map_node_path(map_name, node_path): | 108 | func get_door_for_map_node_path(map_name, node_path): |
| 102 | if not door_id_by_map_node_path.has(map_name): | 109 | if not door_id_by_map_node_path.has(map_name): |
| diff --git a/client/Archipelago/keyboard.gd b/client/Archipelago/keyboard.gd index 600a047..450566d 100644 --- a/client/Archipelago/keyboard.gd +++ b/client/Archipelago/keyboard.gd | |||
| @@ -4,6 +4,7 @@ const kALL_LETTERS = "abcdefghjiklmnopqrstuvwxyz" | |||
| 4 | 4 | ||
| 5 | var letters_saved = {} | 5 | var letters_saved = {} |
| 6 | var letters_in_keyholders = [] | 6 | var letters_in_keyholders = [] |
| 7 | var letters_blocked = [] | ||
| 7 | var letters_dynamic = {} | 8 | var letters_dynamic = {} |
| 8 | var keyholder_state = {} | 9 | var keyholder_state = {} |
| 9 | 10 | ||
| @@ -17,6 +18,7 @@ func _init(): | |||
| 17 | func reset(): | 18 | func reset(): |
| 18 | letters_saved.clear() | 19 | letters_saved.clear() |
| 19 | letters_in_keyholders.clear() | 20 | letters_in_keyholders.clear() |
| 21 | letters_blocked.clear() | ||
| 20 | letters_dynamic.clear() | 22 | letters_dynamic.clear() |
| 21 | keyholder_state.clear() | 23 | keyholder_state.clear() |
| 22 | 24 | ||
| @@ -91,6 +93,9 @@ func update_unlocks(): | |||
| 91 | level = 2 | 93 | level = 2 |
| 92 | has_doubles = true | 94 | has_doubles = true |
| 93 | 95 | ||
| 96 | if letters_blocked.has(k): | ||
| 97 | level = 0 | ||
| 98 | |||
| 94 | unlocks.unlockKey(k, level) | 99 | unlocks.unlockKey(k, level) |
| 95 | 100 | ||
| 96 | if has_doubles and unlocks.data["double_letters"] != "unlocked": | 101 | if has_doubles and unlocks.data["double_letters"] != "unlocked": |
| @@ -105,6 +110,9 @@ func collect_local_letter(key, level): | |||
| 105 | 110 | ||
| 106 | letters_saved[key] = level | 111 | letters_saved[key] = level |
| 107 | 112 | ||
| 113 | if letters_blocked.has(key): | ||
| 114 | letters_blocked.erase(key) | ||
| 115 | |||
| 108 | update_unlocks() | 116 | update_unlocks() |
| 109 | save() | 117 | save() |
| 110 | 118 | ||
| @@ -115,6 +123,9 @@ func collect_remote_letter(key, level): | |||
| 115 | 123 | ||
| 116 | letters_dynamic[key] = level | 124 | letters_dynamic[key] = level |
| 117 | 125 | ||
| 126 | if letters_blocked.has(key): | ||
| 127 | letters_blocked.erase(key) | ||
| 128 | |||
| 118 | update_unlocks() | 129 | update_unlocks() |
| 119 | save() | 130 | save() |
| 120 | 131 | ||
| @@ -148,6 +159,13 @@ func remove_from_keyholder(key, map, kh_path): | |||
| 148 | save() | 159 | save() |
| 149 | 160 | ||
| 150 | 161 | ||
| 162 | func block_letter(key): | ||
| 163 | if not letters_blocked.has(key): | ||
| 164 | letters_blocked.append(key) | ||
| 165 | |||
| 166 | update_unlocks() | ||
| 167 | |||
| 168 | |||
| 151 | func load_keyholders(map): | 169 | func load_keyholders(map): |
| 152 | if keyholder_state.has(map): | 170 | if keyholder_state.has(map): |
| 153 | var khs = keyholder_state[map] | 171 | var khs = keyholder_state[map] |
| @@ -160,9 +178,11 @@ func load_keyholders(map): | |||
| 160 | 178 | ||
| 161 | 179 | ||
| 162 | func reset_keyholders(): | 180 | func reset_keyholders(): |
| 163 | if letters_in_keyholders.is_empty(): | 181 | if letters_in_keyholders.is_empty() and letters_blocked.is_empty(): |
| 164 | return false | 182 | return false |
| 165 | 183 | ||
| 184 | var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty() | ||
| 185 | |||
| 166 | if keyholder_state.has(global.map): | 186 | if keyholder_state.has(global.map): |
| 167 | for path in keyholder_state[global.map]: | 187 | for path in keyholder_state[global.map]: |
| 168 | get_tree().get_root().get_node("scene").get_node(path).setFromAp( | 188 | get_tree().get_root().get_node("scene").get_node(path).setFromAp( |
| @@ -171,8 +191,9 @@ func reset_keyholders(): | |||
| 171 | 191 | ||
| 172 | keyholder_state.clear() | 192 | keyholder_state.clear() |
| 173 | letters_in_keyholders.clear() | 193 | letters_in_keyholders.clear() |
| 194 | letters_blocked.clear() | ||
| 174 | 195 | ||
| 175 | update_unlocks() | 196 | update_unlocks() |
| 176 | save() | 197 | save() |
| 177 | 198 | ||
| 178 | return true | 199 | return cleared_anything |
| diff --git a/client/Archipelago/manager.gd b/client/Archipelago/manager.gd index 6eea2bd..218870c 100644 --- a/client/Archipelago/manager.gd +++ b/client/Archipelago/manager.gd | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | extends Node | 1 | extends Node |
| 2 | 2 | ||
| 3 | const MOD_VERSION = 0 | 3 | const MOD_VERSION = 7 |
| 4 | 4 | ||
| 5 | var SCRIPT_client | 5 | var SCRIPT_client |
| 6 | var SCRIPT_keyboard | 6 | var SCRIPT_keyboard |
| @@ -12,6 +12,7 @@ var ap_server = "" | |||
| 12 | var ap_user = "" | 12 | var ap_user = "" |
| 13 | var ap_pass = "" | 13 | var ap_pass = "" |
| 14 | var connection_history = [] | 14 | var connection_history = [] |
| 15 | var show_compass = false | ||
| 15 | 16 | ||
| 16 | var client | 17 | var client |
| 17 | var keyboard | 18 | var keyboard |
| @@ -41,13 +42,17 @@ const kCYAN_DOOR_BEHAVIOR_H2 = 0 | |||
| 41 | const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1 | 42 | const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1 |
| 42 | const kCYAN_DOOR_BEHAVIOR_ITEM = 2 | 43 | const kCYAN_DOOR_BEHAVIOR_ITEM = 2 |
| 43 | 44 | ||
| 45 | var apworld_version = [0, 0] | ||
| 44 | var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 | 46 | var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 |
| 45 | var daedalus_roof_access = false | 47 | var daedalus_roof_access = false |
| 46 | var keyholder_sanity = false | 48 | var keyholder_sanity = false |
| 47 | var shuffle_control_center_colors = false | 49 | var shuffle_control_center_colors = false |
| 48 | var shuffle_doors = false | 50 | var shuffle_doors = false |
| 51 | var shuffle_gallery_paintings = false | ||
| 49 | var shuffle_letters = kSHUFFLE_LETTERS_VANILLA | 52 | var shuffle_letters = kSHUFFLE_LETTERS_VANILLA |
| 50 | var shuffle_symbols = false | 53 | var shuffle_symbols = false |
| 54 | var strict_cyan_ending = false | ||
| 55 | var strict_purple_ending = false | ||
| 51 | var victory_condition = -1 | 56 | var victory_condition = -1 |
| 52 | 57 | ||
| 53 | signal could_not_connect | 58 | signal could_not_connect |
| @@ -78,6 +83,9 @@ func _init(): | |||
| 78 | if data.size() > 3: | 83 | if data.size() > 3: |
| 79 | connection_history = data[3] | 84 | connection_history = data[3] |
| 80 | 85 | ||
| 86 | if data.size() > 4: | ||
| 87 | show_compass = data[4] | ||
| 88 | |||
| 81 | 89 | ||
| 82 | func _ready(): | 90 | func _ready(): |
| 83 | client = SCRIPT_client.new() | 91 | client = SCRIPT_client.new() |
| @@ -106,6 +114,7 @@ func saveSettings(): | |||
| 106 | ap_user, | 114 | ap_user, |
| 107 | ap_pass, | 115 | ap_pass, |
| 108 | connection_history, | 116 | connection_history, |
| 117 | show_compass, | ||
| 109 | ] | 118 | ] |
| 110 | file.store_var(data, true) | 119 | file.store_var(data, true) |
| 111 | file.close() | 120 | file.close() |
| @@ -213,6 +222,9 @@ func _process_item(item, index, from, flags, amount): | |||
| 213 | "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name] | 222 | "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name] |
| 214 | ) | 223 | ) |
| 215 | 224 | ||
| 225 | if gamedata.anti_trap_ids.has(item): | ||
| 226 | keyboard.block_letter(gamedata.anti_trap_ids[item]) | ||
| 227 | |||
| 216 | global._print(message) | 228 | global._print(message) |
| 217 | 229 | ||
| 218 | global.get_node("Messages").showMessage(message) | 230 | global.get_node("Messages").showMessage(message) |
| @@ -307,6 +319,10 @@ func parse_printjson_for_textclient(message): | |||
| 307 | func _process_location_scout(item_id, location_id, player, flags): | 319 | func _process_location_scout(item_id, location_id, player, flags): |
| 308 | _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags} | 320 | _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags} |
| 309 | 321 | ||
| 322 | if player == client._slot and flags & 4 != 0: | ||
| 323 | # This is a trap for us, so let's not display it. | ||
| 324 | return | ||
| 325 | |||
| 310 | var gamedata = global.get_node("Gamedata") | 326 | var gamedata = global.get_node("Gamedata") |
| 311 | var map_id = gamedata.map_id_by_name.get(global.map) | 327 | var map_id = gamedata.map_id_by_name.get(global.map) |
| 312 | 328 | ||
| @@ -327,8 +343,8 @@ func _process_location_scout(item_id, location_id, player, flags): | |||
| 327 | collectable.setScoutedText(item_name) | 343 | collectable.setScoutedText(item_name) |
| 328 | 344 | ||
| 329 | 345 | ||
| 330 | func _client_could_not_connect(): | 346 | func _client_could_not_connect(message): |
| 331 | emit_signal("could_not_connect") | 347 | emit_signal("could_not_connect", message) |
| 332 | 348 | ||
| 333 | 349 | ||
| 334 | func _client_connect_status(message): | 350 | func _client_connect_status(message): |
| @@ -361,10 +377,16 @@ func _client_connected(slot_data): | |||
| 361 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) | 377 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) |
| 362 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) | 378 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) |
| 363 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) | 379 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) |
| 380 | shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false)) | ||
| 364 | shuffle_letters = int(slot_data.get("shuffle_letters", 0)) | 381 | shuffle_letters = int(slot_data.get("shuffle_letters", 0)) |
| 365 | shuffle_symbols = bool(slot_data.get("shuffle_symbols", false)) | 382 | shuffle_symbols = bool(slot_data.get("shuffle_symbols", false)) |
| 383 | strict_cyan_ending = bool(slot_data.get("strict_cyan_ending", false)) | ||
| 384 | strict_purple_ending = bool(slot_data.get("strict_purple_ending", false)) | ||
| 366 | victory_condition = int(slot_data.get("victory_condition", 0)) | 385 | victory_condition = int(slot_data.get("victory_condition", 0)) |
| 367 | 386 | ||
| 387 | if slot_data.has("version"): | ||
| 388 | apworld_version = [int(slot_data["version"][0]), int(slot_data["version"][1])] | ||
| 389 | |||
| 368 | # Set up item locks. | 390 | # Set up item locks. |
| 369 | _item_locks = {} | 391 | _item_locks = {} |
| 370 | 392 | ||
| @@ -399,6 +421,11 @@ func _client_connected(slot_data): | |||
| 399 | for door in door_group.get_doors(): | 421 | for door in door_group.get_doors(): |
| 400 | _item_locks[door] = [door_group.get_ap_id(), 1] | 422 | _item_locks[door] = [door_group.get_ap_id(), 1] |
| 401 | 423 | ||
| 424 | if shuffle_gallery_paintings: | ||
| 425 | for door in gamedata.objects.get_doors(): | ||
| 426 | if door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING: | ||
| 427 | _item_locks[door.get_id()] = [door.get_ap_id(), 1] | ||
| 428 | |||
| 402 | if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM: | 429 | if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM: |
| 403 | for door_group in gamedata.objects.get_door_groups(): | 430 | for door_group in gamedata.objects.get_door_groups(): |
| 404 | if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS: | 431 | if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS: |
| @@ -511,4 +538,7 @@ func _process_key_item(key, level): | |||
| 511 | _held_letters[key] = max(_held_letters.get(key, 0), level) | 538 | _held_letters[key] = max(_held_letters.get(key, 0), level) |
| 512 | return | 539 | return |
| 513 | 540 | ||
| 541 | if shuffle_letters == kSHUFFLE_LETTERS_ITEM_CYAN: | ||
| 542 | level += 1 | ||
| 543 | |||
| 514 | keyboard.collect_remote_letter(key, level) | 544 | keyboard.collect_remote_letter(key, level) |
| diff --git a/client/Archipelago/messages.gd b/client/Archipelago/messages.gd index 52f38b9..82fdbc4 100644 --- a/client/Archipelago/messages.gd +++ b/client/Archipelago/messages.gd | |||
| @@ -48,10 +48,11 @@ func showMessage(text): | |||
| 48 | while !_ordered_labels.is_empty(): | 48 | while !_ordered_labels.is_empty(): |
| 49 | await get_tree().create_timer(timeout).timeout | 49 | await get_tree().create_timer(timeout).timeout |
| 50 | 50 | ||
| 51 | var to_remove = _ordered_labels.pop_front() | 51 | if !_ordered_labels.is_empty(): |
| 52 | var to_tween = get_tree().create_tween().bind_node(to_remove) | 52 | var to_remove = _ordered_labels.pop_front() |
| 53 | to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5) | 53 | var to_tween = get_tree().create_tween().bind_node(to_remove) |
| 54 | to_tween.tween_callback(to_remove.queue_free) | 54 | to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5) |
| 55 | to_tween.tween_callback(to_remove.queue_free) | ||
| 55 | 56 | ||
| 56 | if !_message_queue.is_empty(): | 57 | if !_message_queue.is_empty(): |
| 57 | var next_msg = _message_queue.pop_front() | 58 | var next_msg = _message_queue.pop_front() |
| @@ -59,3 +60,12 @@ func showMessage(text): | |||
| 59 | 60 | ||
| 60 | if timeout > 4: | 61 | if timeout > 4: |
| 61 | timeout -= 3 | 62 | timeout -= 3 |
| 63 | |||
| 64 | |||
| 65 | func clear(): | ||
| 66 | _message_queue.clear() | ||
| 67 | |||
| 68 | for message_label in _ordered_labels: | ||
| 69 | message_label.queue_free() | ||
| 70 | |||
| 71 | _ordered_labels.clear() | ||
| diff --git a/client/Archipelago/pauseMenu.gd b/client/Archipelago/pauseMenu.gd index 5da114a..cd1813c 100644 --- a/client/Archipelago/pauseMenu.gd +++ b/client/Archipelago/pauseMenu.gd | |||
| @@ -1,5 +1,26 @@ | |||
| 1 | extends "res://scripts/ui/pauseMenu.gd" | 1 | extends "res://scripts/ui/pauseMenu.gd" |
| 2 | 2 | ||
| 3 | var compass_button | ||
| 4 | |||
| 5 | |||
| 6 | func _ready(): | ||
| 7 | var ap_panel = Panel.new() | ||
| 8 | ap_panel.name = "Archipelago" | ||
| 9 | get_node("menu/settings/settingsInner/TabContainer").add_child(ap_panel) | ||
| 10 | |||
| 11 | var ap = global.get_node("Archipelago") | ||
| 12 | |||
| 13 | compass_button = CheckBox.new() | ||
| 14 | compass_button.text = "show compass" | ||
| 15 | compass_button.button_pressed = ap.show_compass | ||
| 16 | compass_button.position = Vector2(65, 100) | ||
| 17 | compass_button.theme = preload("res://assets/themes/baseUI.tres") | ||
| 18 | compass_button.add_theme_font_size_override("font_size", 60) | ||
| 19 | compass_button.pressed.connect(_toggle_compass) | ||
| 20 | ap_panel.add_child(compass_button) | ||
| 21 | |||
| 22 | super._ready() | ||
| 23 | |||
| 3 | 24 | ||
| 4 | func _pause_game(): | 25 | func _pause_game(): |
| 5 | global.get_node("Textclient").dismiss() | 26 | global.get_node("Textclient").dismiss() |
| @@ -9,4 +30,15 @@ func _pause_game(): | |||
| 9 | func _main_menu(): | 30 | func _main_menu(): |
| 10 | global.loaded = false | 31 | global.loaded = false |
| 11 | global.get_node("Archipelago").disconnect_from_ap() | 32 | global.get_node("Archipelago").disconnect_from_ap() |
| 33 | global.get_node("Messages").clear() | ||
| 34 | global.get_node("Compass").visible = false | ||
| 12 | super._main_menu() | 35 | super._main_menu() |
| 36 | |||
| 37 | |||
| 38 | func _toggle_compass(): | ||
| 39 | var ap = global.get_node("Archipelago") | ||
| 40 | ap.show_compass = compass_button.button_pressed | ||
| 41 | ap.saveSettings() | ||
| 42 | |||
| 43 | var compass = global.get_node("Compass") | ||
| 44 | compass.visible = compass_button.button_pressed | ||
| diff --git a/client/Archipelago/player.gd b/client/Archipelago/player.gd index 4b995bc..538830f 100644 --- a/client/Archipelago/player.gd +++ b/client/Archipelago/player.gd | |||
| @@ -18,6 +18,8 @@ const kEndingNameByVictoryValue = { | |||
| 18 | 18 | ||
| 19 | signal evaluate_solvability | 19 | signal evaluate_solvability |
| 20 | 20 | ||
| 21 | var compass | ||
| 22 | |||
| 21 | 23 | ||
| 22 | func _ready(): | 24 | func _ready(): |
| 23 | var khl_script = load("res://scripts/nodes/keyHolderListener.gd") | 25 | var khl_script = load("res://scripts/nodes/keyHolderListener.gd") |
| @@ -25,6 +27,9 @@ func _ready(): | |||
| 25 | var ap = global.get_node("Archipelago") | 27 | var ap = global.get_node("Archipelago") |
| 26 | var gamedata = global.get_node("Gamedata") | 28 | var gamedata = global.get_node("Gamedata") |
| 27 | 29 | ||
| 30 | compass = global.get_node("Compass") | ||
| 31 | compass.visible = ap.show_compass | ||
| 32 | |||
| 28 | ap.start_batching_locations() | 33 | ap.start_batching_locations() |
| 29 | 34 | ||
| 30 | # Set up door locations. | 35 | # Set up door locations. |
| @@ -36,7 +41,10 @@ func _ready(): | |||
| 36 | if not door.has_ap_id(): | 41 | if not door.has_ap_id(): |
| 37 | continue | 42 | continue |
| 38 | 43 | ||
| 39 | if door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY: | 44 | if ( |
| 45 | door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY | ||
| 46 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING | ||
| 47 | ): | ||
| 40 | continue | 48 | continue |
| 41 | 49 | ||
| 42 | var locationListener = ap.SCRIPT_locationListener.new() | 50 | var locationListener = ap.SCRIPT_locationListener.new() |
| @@ -68,6 +76,12 @@ func _ready(): | |||
| 68 | 76 | ||
| 69 | locationListener.senders.append(NodePath("../" + khl.name)) | 77 | locationListener.senders.append(NodePath("../" + khl.name)) |
| 70 | 78 | ||
| 79 | for sender in door.get_senders(): | ||
| 80 | locationListener.senders.append(NodePath("/root/scene/" + sender)) | ||
| 81 | |||
| 82 | if door.has_complete_at(): | ||
| 83 | locationListener.complete_at = door.get_complete_at() | ||
| 84 | |||
| 71 | get_parent().add_child.call_deferred(locationListener) | 85 | get_parent().add_child.call_deferred(locationListener) |
| 72 | 86 | ||
| 73 | # Set up letter locations. | 87 | # Set up letter locations. |
| @@ -88,7 +102,10 @@ func _ready(): | |||
| 88 | != ap.kLETTER_BEHAVIOR_VANILLA | 102 | != ap.kLETTER_BEHAVIOR_VANILLA |
| 89 | ): | 103 | ): |
| 90 | var scout = ap.scout_location(letter.get_ap_id()) | 104 | var scout = ap.scout_location(letter.get_ap_id()) |
| 91 | if scout != null: | 105 | if ( |
| 106 | scout != null | ||
| 107 | and not (scout["player"] == ap.client._slot and scout["flags"] & 4 != 0) | ||
| 108 | ): | ||
| 92 | var item_name = "Unknown" | 109 | var item_name = "Unknown" |
| 93 | var item_player_game = ap.client._game_by_player[float(scout["player"])] | 110 | var item_player_game = ap.client._game_by_player[float(scout["player"])] |
| 94 | if ap.client._item_id_to_name[item_player_game].has(scout["item"]): | 111 | if ap.client._item_id_to_name[item_player_game].has(scout["item"]): |
| @@ -190,11 +207,132 @@ func _ready(): | |||
| 190 | warp_enter.rotation_degrees.y = 90 | 207 | warp_enter.rotation_degrees.y = 90 |
| 191 | get_parent().add_child.call_deferred(warp_enter) | 208 | get_parent().add_child.call_deferred(warp_enter) |
| 192 | 209 | ||
| 193 | # Remove door behind X1. | ||
| 194 | if global.map == "the_entry": | 210 | if global.map == "the_entry": |
| 211 | # Remove door behind X1. | ||
| 195 | var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1") | 212 | var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1") |
| 196 | door_node.handleTriggered() | 213 | door_node.handleTriggered() |
| 197 | 214 | ||
| 215 | # Display win condition. | ||
| 216 | var sign_prefab = preload("res://objects/nodes/sign.tscn") | ||
| 217 | var sign1 = sign_prefab.instantiate() | ||
| 218 | sign1.position = Vector3(-7, 5, -15.01) | ||
| 219 | sign1.text = "victory" | ||
| 220 | get_parent().add_child.call_deferred(sign1) | ||
| 221 | |||
| 222 | var sign2 = sign_prefab.instantiate() | ||
| 223 | sign2.position = Vector3(-7, 4, -15.01) | ||
| 224 | sign2.text = "%s ending" % kEndingNameByVictoryValue.get(ap.victory_condition, "?") | ||
| 225 | |||
| 226 | var sign2_color = kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower() | ||
| 227 | if sign2_color == "white": | ||
| 228 | sign2_color = "silver" | ||
| 229 | |||
| 230 | sign2.material = load("res://assets/materials/%s.material" % sign2_color) | ||
| 231 | get_parent().add_child.call_deferred(sign2) | ||
| 232 | |||
| 233 | # Add the strict purple ending validation. | ||
| 234 | if global.map == "the_sun_temple" and ap.strict_purple_ending: | ||
| 235 | var panel_prefab = preload("res://objects/nodes/panel.tscn") | ||
| 236 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 237 | var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn") | ||
| 238 | |||
| 239 | var previous_panel = null | ||
| 240 | var next_y = -100 | ||
| 241 | var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"] | ||
| 242 | for word in words: | ||
| 243 | var panel = panel_prefab.instantiate() | ||
| 244 | panel.position = Vector3(0, next_y, 0) | ||
| 245 | next_y -= 10 | ||
| 246 | panel.clue = word | ||
| 247 | panel.symbol = "" | ||
| 248 | panel.answer = word | ||
| 249 | panel.name = "EndCheck_%s" % word | ||
| 250 | |||
| 251 | var tpl = tpl_prefab.instantiate() | ||
| 252 | tpl.teleport_point = Vector3(0, 1, 0) | ||
| 253 | tpl.teleport_rotate = Vector3(-45, 180, 0) | ||
| 254 | tpl.target_path = panel | ||
| 255 | tpl.name = "Teleport" | ||
| 256 | |||
| 257 | if previous_panel == null: | ||
| 258 | tpl.senders.append(NodePath("/root/scene/Panels/End/panel_24")) | ||
| 259 | else: | ||
| 260 | tpl.senders.append(NodePath("../../%s" % previous_panel.name)) | ||
| 261 | |||
| 262 | var reversing = reverse_prefab.instantiate() | ||
| 263 | reversing.senders.append(NodePath("..")) | ||
| 264 | reversing.name = "Reversing" | ||
| 265 | tpl.senders.append(NodePath("../Reversing")) | ||
| 266 | |||
| 267 | panel.add_child.call_deferred(tpl) | ||
| 268 | panel.add_child.call_deferred(reversing) | ||
| 269 | get_parent().get_node("Panels").add_child.call_deferred(panel) | ||
| 270 | |||
| 271 | previous_panel = panel | ||
| 272 | |||
| 273 | # Duplicate the doors that usually wait on EQUINOX. We can't set the senders | ||
| 274 | # here for some reason so we actually set them in the door ready function. | ||
| 275 | var endplat = get_node("/root/scene/Components/Doors/EndPlatform") | ||
| 276 | var endplat2 = endplat.duplicate() | ||
| 277 | endplat2.name = "spe_EndPlatform" | ||
| 278 | endplat.get_parent().add_child.call_deferred(endplat2) | ||
| 279 | endplat.queue_free() | ||
| 280 | |||
| 281 | var entry2 = get_node("/root/scene/Components/Doors/entry_2") | ||
| 282 | var entry22 = entry2.duplicate() | ||
| 283 | entry22.name = "spe_entry_2" | ||
| 284 | entry2.get_parent().add_child.call_deferred(entry22) | ||
| 285 | entry2.queue_free() | ||
| 286 | |||
| 287 | # Add the strict cyan ending validation. | ||
| 288 | if global.map == "the_parthenon" and ap.strict_cyan_ending: | ||
| 289 | var panel_prefab = preload("res://objects/nodes/panel.tscn") | ||
| 290 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 291 | var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn") | ||
| 292 | |||
| 293 | var previous_panel = null | ||
| 294 | var next_y = -100 | ||
| 295 | var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"] | ||
| 296 | for word in words: | ||
| 297 | var panel = panel_prefab.instantiate() | ||
| 298 | panel.position = Vector3(0, next_y, 0) | ||
| 299 | next_y -= 10 | ||
| 300 | panel.clue = word | ||
| 301 | panel.symbol = "." | ||
| 302 | panel.answer = "%s%s" % [word, word] | ||
| 303 | panel.name = "EndCheck_%s" % word | ||
| 304 | |||
| 305 | var tpl = tpl_prefab.instantiate() | ||
| 306 | tpl.teleport_point = Vector3(0, 1, -11) | ||
| 307 | tpl.teleport_rotate = Vector3(-45, 0, 0) | ||
| 308 | tpl.target_path = panel | ||
| 309 | tpl.name = "Teleport" | ||
| 310 | |||
| 311 | if previous_panel == null: | ||
| 312 | tpl.senderGroup.append(NodePath("/root/scene/Panels/Rulers")) | ||
| 313 | else: | ||
| 314 | tpl.senders.append(NodePath("../../%s" % previous_panel.name)) | ||
| 315 | |||
| 316 | var reversing = reverse_prefab.instantiate() | ||
| 317 | reversing.senders.append(NodePath("..")) | ||
| 318 | reversing.name = "Reversing" | ||
| 319 | tpl.senders.append(NodePath("../Reversing")) | ||
| 320 | |||
| 321 | panel.add_child.call_deferred(tpl) | ||
| 322 | panel.add_child.call_deferred(reversing) | ||
| 323 | get_parent().get_node("Panels").add_child.call_deferred(panel) | ||
| 324 | |||
| 325 | previous_panel = panel | ||
| 326 | |||
| 327 | # Duplicate the door that usually waits on the rulers. We can't set the | ||
| 328 | # senders here for some reason so we actually set them in the door ready | ||
| 329 | # function. | ||
| 330 | var entry1 = get_node("/root/scene/Components/Doors/entry_1") | ||
| 331 | var entry12 = entry1.duplicate() | ||
| 332 | entry12.name = "spe_entry_1" | ||
| 333 | entry1.get_parent().add_child.call_deferred(entry12) | ||
| 334 | entry1.queue_free() | ||
| 335 | |||
| 198 | super._ready() | 336 | super._ready() |
| 199 | 337 | ||
| 200 | await get_tree().process_frame | 338 | await get_tree().process_frame |
| @@ -218,3 +356,7 @@ func _set_up_invis_wall(x, y, z, sx, sy, sz): | |||
| 218 | newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF | 356 | newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF |
| 219 | newwall.skeleton = ".." | 357 | newwall.skeleton = ".." |
| 220 | get_parent().add_child.call_deferred(newwall) | 358 | get_parent().add_child.call_deferred(newwall) |
| 359 | |||
| 360 | |||
| 361 | func _process(_dt): | ||
| 362 | compass.update_rotation(global_rotation.y) | ||
| diff --git a/client/Archipelago/saver.gd b/client/Archipelago/saver.gd index 0fba9e7..44bc179 100644 --- a/client/Archipelago/saver.gd +++ b/client/Archipelago/saver.gd | |||
| @@ -7,3 +7,17 @@ func levelLoaded(): | |||
| 7 | ap.keyboard.load_keyholders.call_deferred(global.map) | 7 | ap.keyboard.load_keyholders.call_deferred(global.map) |
| 8 | else: | 8 | else: |
| 9 | reload.call_deferred() | 9 | reload.call_deferred() |
| 10 | |||
| 11 | |||
| 12 | func reload(): | ||
| 13 | # Just rewriting this whole thing so I can remove Chris's safeguard. | ||
| 14 | var file = FileAccess.open(path + type + ".save", FileAccess.READ) | ||
| 15 | if file: | ||
| 16 | var data = file.get_var(true) | ||
| 17 | file.close() | ||
| 18 | for datum in data: | ||
| 19 | var saveable = get_node_or_null(datum[0]) | ||
| 20 | if saveable != null: | ||
| 21 | saveable.is_complete = datum[1] | ||
| 22 | if saveable.is_complete: | ||
| 23 | saveable.loadData(saveable.is_complete) | ||
| diff --git a/client/Archipelago/settings_screen.gd b/client/Archipelago/settings_screen.gd index ff6f9df..b7bfacf 100644 --- a/client/Archipelago/settings_screen.gd +++ b/client/Archipelago/settings_screen.gd | |||
| @@ -44,8 +44,10 @@ func _ready(): | |||
| 44 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pauseMenu.gd")) | 44 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pauseMenu.gd")) |
| 45 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd")) | 45 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd")) |
| 46 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/saver.gd")) | 46 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/saver.gd")) |
| 47 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleport.gd")) | ||
| 47 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleportListener.gd")) | 48 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleportListener.gd")) |
| 48 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/visibilityListener.gd")) | 49 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/visibilityListener.gd")) |
| 50 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldport.gd")) | ||
| 49 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldportListener.gd")) | 51 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldportListener.gd")) |
| 50 | 52 | ||
| 51 | var proto_script = load("user://maps/Archipelago/generated/proto.gd") | 53 | var proto_script = load("user://maps/Archipelago/generated/proto.gd") |
| @@ -67,6 +69,12 @@ func _ready(): | |||
| 67 | textclient_instance.name = "Textclient" | 69 | textclient_instance.name = "Textclient" |
| 68 | global.add_child(textclient_instance) | 70 | global.add_child(textclient_instance) |
| 69 | 71 | ||
| 72 | var compass_overlay_script = load("user://maps/Archipelago/compass_overlay.gd") | ||
| 73 | var compass_overlay_instance = compass_overlay_script.new() | ||
| 74 | compass_overlay_instance.name = "Compass" | ||
| 75 | compass_overlay_instance.SCRIPT_compass = load("user://maps/Archipelago/compass.gd") | ||
| 76 | global.add_child(compass_overlay_instance) | ||
| 77 | |||
| 70 | var ap = global.get_node("Archipelago") | 78 | var ap = global.get_node("Archipelago") |
| 71 | var gamedata = global.get_node("Gamedata") | 79 | var gamedata = global.get_node("Gamedata") |
| 72 | ap.connect("ap_connected", connectionSuccessful) | 80 | ap.connect("ap_connected", connectionSuccessful) |
| @@ -99,6 +107,10 @@ func _ready(): | |||
| 99 | $Panel/player_box.add_theme_font_size_override("font_size", 36) | 107 | $Panel/player_box.add_theme_font_size_override("font_size", 36) |
| 100 | $Panel/password_box.add_theme_font_size_override("font_size", 36) | 108 | $Panel/password_box.add_theme_font_size_override("font_size", 36) |
| 101 | 109 | ||
| 110 | # Set up version mismatch dialog. | ||
| 111 | $Panel/VersionMismatch.connect("confirmed", startGame) | ||
| 112 | $Panel/VersionMismatch.get_cancel_button().pressed.connect(versionMismatchDeclined) | ||
| 113 | |||
| 102 | 114 | ||
| 103 | # Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd | 115 | # Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd |
| 104 | func installScriptExtension(childScript: Resource): | 116 | func installScriptExtension(childScript: Resource): |
| @@ -128,6 +140,33 @@ func connectionStatus(message): | |||
| 128 | 140 | ||
| 129 | func connectionSuccessful(): | 141 | func connectionSuccessful(): |
| 130 | var ap = global.get_node("Archipelago") | 142 | var ap = global.get_node("Archipelago") |
| 143 | var gamedata = global.get_node("Gamedata") | ||
| 144 | |||
| 145 | # Check for major version mismatch. | ||
| 146 | if ap.apworld_version[0] != gamedata.objects.get_version(): | ||
| 147 | $Panel/AcceptDialog.exclusive = false | ||
| 148 | |||
| 149 | var popup = self.get_node("Panel/VersionMismatch") | ||
| 150 | popup.title = "Version Mismatch!" | ||
| 151 | popup.dialog_text = ( | ||
| 152 | "This slot was generated using v%d.%d of the Lingo 2 apworld,\nwhich has a different major version than this client (v%d.%d).\nIt is highly recommended to play using the correct version of the client.\nYou may experience bugs or logic issues if you continue." | ||
| 153 | % [ | ||
| 154 | ap.apworld_version[0], | ||
| 155 | ap.apworld_version[1], | ||
| 156 | gamedata.objects.get_version(), | ||
| 157 | ap.MOD_VERSION | ||
| 158 | ] | ||
| 159 | ) | ||
| 160 | popup.exclusive = true | ||
| 161 | popup.popup_centered() | ||
| 162 | |||
| 163 | return | ||
| 164 | |||
| 165 | startGame() | ||
| 166 | |||
| 167 | |||
| 168 | func startGame(): | ||
| 169 | var ap = global.get_node("Archipelago") | ||
| 131 | 170 | ||
| 132 | # Save connection details | 171 | # Save connection details |
| 133 | var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass] | 172 | var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass] |
| @@ -166,6 +205,8 @@ func connectionSuccessful(): | |||
| 166 | clearResourceCache("res://objects/nodes/panel.tscn") | 205 | clearResourceCache("res://objects/nodes/panel.tscn") |
| 167 | clearResourceCache("res://objects/nodes/player.tscn") | 206 | clearResourceCache("res://objects/nodes/player.tscn") |
| 168 | clearResourceCache("res://objects/nodes/saver.tscn") | 207 | clearResourceCache("res://objects/nodes/saver.tscn") |
| 208 | clearResourceCache("res://objects/nodes/teleport.tscn") | ||
| 209 | clearResourceCache("res://objects/nodes/worldport.tscn") | ||
| 169 | clearResourceCache("res://objects/scenes/menus/pause_menu.tscn") | 210 | clearResourceCache("res://objects/scenes/menus/pause_menu.tscn") |
| 170 | 211 | ||
| 171 | var paintings_dir = DirAccess.open("res://objects/meshes/paintings") | 212 | var paintings_dir = DirAccess.open("res://objects/meshes/paintings") |
| @@ -190,6 +231,13 @@ func connectionUnsuccessful(error_message): | |||
| 190 | popup.get_ok_button().visible = true | 231 | popup.get_ok_button().visible = true |
| 191 | popup.popup_centered() | 232 | popup.popup_centered() |
| 192 | 233 | ||
| 234 | $Panel/connect_button.disabled = false | ||
| 235 | |||
| 236 | |||
| 237 | func versionMismatchDeclined(): | ||
| 238 | $Panel/AcceptDialog.hide() | ||
| 239 | $Panel/connect_button.disabled = false | ||
| 240 | |||
| 193 | 241 | ||
| 194 | func historySelected(index): | 242 | func historySelected(index): |
| 195 | var ap = global.get_node("Archipelago") | 243 | var ap = global.get_node("Archipelago") |
| diff --git a/client/Archipelago/teleport.gd b/client/Archipelago/teleport.gd new file mode 100644 index 0000000..428d50b --- /dev/null +++ b/client/Archipelago/teleport.gd | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | extends "res://scripts/nodes/teleport.gd" | ||
| 2 | |||
| 3 | var item_id | ||
| 4 | var item_amount | ||
| 5 | |||
| 6 | |||
| 7 | func _ready(): | ||
| 8 | var node_path = String( | ||
| 9 | get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names() | ||
| 10 | ) | ||
| 11 | |||
| 12 | var gamedata = global.get_node("Gamedata") | ||
| 13 | var door_id = gamedata.get_door_for_map_node_path(global.map, node_path) | ||
| 14 | if door_id != null: | ||
| 15 | var ap = global.get_node("Archipelago") | ||
| 16 | var item_lock = ap.get_item_id_for_door(door_id) | ||
| 17 | |||
| 18 | if item_lock != null: | ||
| 19 | item_id = item_lock[0] | ||
| 20 | item_amount = item_lock[1] | ||
| 21 | |||
| 22 | self.senders = [] | ||
| 23 | self.senderGroup = [] | ||
| 24 | self.nested = false | ||
| 25 | self.complete_at = 0 | ||
| 26 | self.max_length = 0 | ||
| 27 | self.excludeSenders = [] | ||
| 28 | |||
| 29 | call_deferred("_readier") | ||
| 30 | |||
| 31 | super._ready() | ||
| 32 | |||
| 33 | |||
| 34 | func _readier(): | ||
| 35 | var ap = global.get_node("Archipelago") | ||
| 36 | |||
| 37 | if ap.client.getItemAmount(item_id) >= item_amount: | ||
| 38 | handleTriggered() | ||
| diff --git a/client/Archipelago/teleportListener.gd b/client/Archipelago/teleportListener.gd index 4a7deec..6f363af 100644 --- a/client/Archipelago/teleportListener.gd +++ b/client/Archipelago/teleportListener.gd | |||
| @@ -9,6 +9,17 @@ func _ready(): | |||
| 9 | get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names() | 9 | get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names() |
| 10 | ) | 10 | ) |
| 11 | 11 | ||
| 12 | if ( | ||
| 13 | global.map == "daedalus" | ||
| 14 | and ( | ||
| 15 | node_path == "Components/Triggers/teleportListenerConnections" | ||
| 16 | or node_path == "Components/Triggers/teleportListenerConnections2" | ||
| 17 | ) | ||
| 18 | ): | ||
| 19 | # Effectively disable these. | ||
| 20 | teleport_point = target_path.position | ||
| 21 | return | ||
| 22 | |||
| 12 | var gamedata = global.get_node("Gamedata") | 23 | var gamedata = global.get_node("Gamedata") |
| 13 | var door_id = gamedata.get_door_for_map_node_path(global.map, node_path) | 24 | var door_id = gamedata.get_door_for_map_node_path(global.map, node_path) |
| 14 | if door_id != null: | 25 | if door_id != null: |
| diff --git a/client/Archipelago/worldport.gd b/client/Archipelago/worldport.gd new file mode 100644 index 0000000..d0fb6c9 --- /dev/null +++ b/client/Archipelago/worldport.gd | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | extends "res://scripts/nodes/worldport.gd" | ||
| 2 | |||
| 3 | |||
| 4 | func _ready(): | ||
| 5 | if global.map == "icarus" and exit == "daedalus": | ||
| 6 | var ap = global.get_node("Archipelago") | ||
| 7 | if not ap.daedalus_roof_access: | ||
| 8 | entry_point = Vector3(58, 10, 0) | ||
| 9 | |||
| 10 | super._ready() | ||
| diff --git a/client/Archipelago/worldportListener.gd b/client/Archipelago/worldportListener.gd index c31c825..5c2faff 100644 --- a/client/Archipelago/worldportListener.gd +++ b/client/Archipelago/worldportListener.gd | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | extends "res://scripts/nodes/listeners/worldportListener.gd" | 1 | extends "res://scripts/nodes/listeners/worldportListener.gd" |
| 2 | 2 | ||
| 3 | 3 | ||
| 4 | func changeScene(): | 4 | func handleTriggered(): |
| 5 | if exit == "menus/credits": | 5 | if exit == "menus/credits": |
| 6 | return | 6 | return |
| 7 | 7 | ||
| 8 | super.changeScene() | 8 | super.handleTriggered() |
| diff --git a/client/CHANGELOG.md b/client/CHANGELOG.md new file mode 100644 index 0000000..a0a538b --- /dev/null +++ b/client/CHANGELOG.md | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | # lingo2-archipelago Client Releases | ||
| 2 | |||
| 3 | ## v5.6 - 2025-09-17 | ||
| 4 | |||
| 5 | - Letter locations will no longer reappear after being collected. | ||
| 6 | - This also prevents a potential scenario in which it is impossible to access | ||
| 7 | the location "The Congruent - Obverse Yellow Puzzles" when door shuffle is | ||
| 8 | disabled. | ||
| 9 | |||
| 10 | Download: | ||
| 11 | [lingo2-archipelago-client-v5.6.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.6.zip)<br/> | ||
| 12 | Source: | ||
| 13 | [v5.6](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v5.6) | ||
| 14 | |||
| 15 | ## v5.5 - 2025-09-16 | ||
| 16 | |||
| 17 | - Compatability update for v5.5 of the apworld. | ||
| 18 | |||
| 19 | Download: | ||
| 20 | [lingo2-archipelago-client-v5.5.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.5.zip)<br/> | ||
| 21 | Source: | ||
| 22 | [v5.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v5.5) | ||
| 23 | |||
| 24 | ## v4.4 - 2025-09-13 | ||
| 25 | |||
| 26 | - Added support for anti-collectable trap items. | ||
| 27 | - Fixed entrance to The Jubilant not opening properly when using control center | ||
| 28 | color shuffle. | ||
| 29 | - Fixed the location "The Entry (Colored Doors Area) - OPEN" not sending. | ||
| 30 | - Fixed level 2 letters not activating properly when letter shuffle is set to | ||
| 31 | Item Cyan. | ||
| 32 | - Messages are now cleared out when returning to the main menu. | ||
| 33 | - The player is prevented from accidentally breaking roof access logic when | ||
| 34 | returning to Daedalus from Icarus. | ||
| 35 | |||
| 36 | Download: | ||
| 37 | [lingo2-archipelago-client-v4.4.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v4.4.zip)<br/> | ||
| 38 | Source: | ||
| 39 | [v4.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v4.4) | ||
| 40 | |||
| 41 | ## v3.3 - 2025-09-12 | ||
| 42 | |||
| 43 | - Fixed issue downloading large datapackages (such as TUNIC's). | ||
| 44 | - Connection failures now show error messages. | ||
| 45 | |||
| 46 | Download: | ||
| 47 | [lingo2-archipelago-client-v3.3.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.3.zip)<br/> | ||
| 48 | Source: | ||
| 49 | [v3.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v3.3) | ||
| 50 | |||
| 51 | ## v3.2 - 2025-09-12 | ||
| 52 | |||
| 53 | - Initial release for testing. Features include door shuffle, letter shuffle, | ||
| 54 | and symbol shuffle. | ||
| 55 | |||
| 56 | Download: | ||
| 57 | [lingo2-archipelago-client-v3.2.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.2.zip)<br/> | ||
| 58 | Source: | ||
| 59 | [v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v3.2) | ||
| diff --git a/client/README.md b/client/README.md index 1e94bdb..99589c5 100644 --- a/client/README.md +++ b/client/README.md | |||
| @@ -88,10 +88,3 @@ not recommended to load these save files outside of the randomizer. | |||
| 88 | 88 | ||
| 89 | A connection to Archipelago is required to resume playing a multiworld. This is | 89 | A connection to Archipelago is required to resume playing a multiworld. This is |
| 90 | because the set of items you have received is not stored locally. | 90 | because the set of items you have received is not stored locally. |
| 91 | |||
| 92 | ### What about wall snipes? | ||
| 93 | |||
| 94 | "Wall sniping" refers to the fact that you are able to solve puzzles on the | ||
| 95 | other side of opaque walls. The player is never expected to or required to do | ||
| 96 | this in normal gameplay. This randomizer does not change how wall snipes work, | ||
| 97 | but it will likewise never require the use of them. | ||
| diff --git a/client/archipelago.tscn b/client/archipelago.tscn index 40dd46f..da83b23 100644 --- a/client/archipelago.tscn +++ b/client/archipelago.tscn | |||
| @@ -40,6 +40,7 @@ offset_right = 1920.0 | |||
| 40 | offset_bottom = 225.0 | 40 | offset_bottom = 225.0 |
| 41 | text = "ARCHIPELAGO" | 41 | text = "ARCHIPELAGO" |
| 42 | valign = 1 | 42 | valign = 1 |
| 43 | horizontal_alignment = 1 | ||
| 43 | theme = ExtResource("2_g4bvn") | 44 | theme = ExtResource("2_g4bvn") |
| 44 | 45 | ||
| 45 | [node name="credit" parent="Panel" type="Label"] | 46 | [node name="credit" parent="Panel" type="Label"] |
| @@ -150,6 +151,10 @@ caret_blink = true | |||
| 150 | offset_right = 83.0 | 151 | offset_right = 83.0 |
| 151 | offset_bottom = 58.0 | 152 | offset_bottom = 58.0 |
| 152 | 153 | ||
| 154 | [node name="VersionMismatch" type="ConfirmationDialog" parent="Panel"] | ||
| 155 | offset_right = 83.0 | ||
| 156 | offset_bottom = 58.0 | ||
| 157 | |||
| 153 | [node name="connection_history" type="MenuButton" parent="Panel"] | 158 | [node name="connection_history" type="MenuButton" parent="Panel"] |
| 154 | offset_left = 1239.0 | 159 | offset_left = 1239.0 |
| 155 | offset_top = 276.0 | 160 | offset_top = 276.0 |
| diff --git a/data/connections.txtpb b/data/connections.txtpb index d718c96..17f71a3 100644 --- a/data/connections.txtpb +++ b/data/connections.txtpb | |||
| @@ -20,7 +20,7 @@ connections { | |||
| 20 | from { | 20 | from { |
| 21 | port { | 21 | port { |
| 22 | map: "the_entry" | 22 | map: "the_entry" |
| 23 | room: "Flipped Second Room" | 23 | room: "Four Rooms Entrance" |
| 24 | name: "FOUR" | 24 | name: "FOUR" |
| 25 | } | 25 | } |
| 26 | } | 26 | } |
| @@ -141,7 +141,7 @@ connections { | |||
| 141 | to { | 141 | to { |
| 142 | port { | 142 | port { |
| 143 | map: "the_darkroom" | 143 | map: "the_darkroom" |
| 144 | room: "First Room" | 144 | room: "Cyan Hallway" |
| 145 | name: "COLORFUL" | 145 | name: "COLORFUL" |
| 146 | } | 146 | } |
| 147 | } | 147 | } |
| @@ -157,7 +157,7 @@ connections { | |||
| 157 | to { | 157 | to { |
| 158 | port { | 158 | port { |
| 159 | map: "the_darkroom" | 159 | map: "the_darkroom" |
| 160 | room: "Second Room" | 160 | room: "Congruent Entrance" |
| 161 | name: "CONGRUENT" | 161 | name: "CONGRUENT" |
| 162 | } | 162 | } |
| 163 | } | 163 | } |
| @@ -233,7 +233,7 @@ connections { | |||
| 233 | from { | 233 | from { |
| 234 | port { | 234 | port { |
| 235 | map: "the_darkroom" | 235 | map: "the_darkroom" |
| 236 | room: "First Room" | 236 | room: "Double Sided Entrance" |
| 237 | name: "DOUBLESIDED" | 237 | name: "DOUBLESIDED" |
| 238 | } | 238 | } |
| 239 | } | 239 | } |
| @@ -308,6 +308,23 @@ connections { | |||
| 308 | name: "GALLERY" | 308 | name: "GALLERY" |
| 309 | } | 309 | } |
| 310 | } | 310 | } |
| 311 | oneway: true | ||
| 312 | } | ||
| 313 | connections { | ||
| 314 | from { | ||
| 315 | port { | ||
| 316 | map: "the_butterfly" | ||
| 317 | room: "Main Area" | ||
| 318 | name: "GALLERY" | ||
| 319 | } | ||
| 320 | } | ||
| 321 | to { | ||
| 322 | room { | ||
| 323 | map: "the_gallery" | ||
| 324 | name: "Main Area" | ||
| 325 | } | ||
| 326 | } | ||
| 327 | oneway: true | ||
| 311 | } | 328 | } |
| 312 | connections { | 329 | connections { |
| 313 | from { | 330 | from { |
| @@ -618,7 +635,7 @@ connections { | |||
| 618 | from { | 635 | from { |
| 619 | port { | 636 | port { |
| 620 | map: "the_entry" | 637 | map: "the_entry" |
| 621 | room: "Link Area" | 638 | room: "Liberated Entrance" |
| 622 | name: "BLUE" | 639 | name: "BLUE" |
| 623 | } | 640 | } |
| 624 | } | 641 | } |
| @@ -666,7 +683,7 @@ connections { | |||
| 666 | from { | 683 | from { |
| 667 | port { | 684 | port { |
| 668 | map: "the_entry" | 685 | map: "the_entry" |
| 669 | room: "Link Area" | 686 | room: "Literate Entrance" |
| 670 | name: "BROWN" | 687 | name: "BROWN" |
| 671 | } | 688 | } |
| 672 | } | 689 | } |
| @@ -876,6 +893,7 @@ connections { | |||
| 876 | } | 893 | } |
| 877 | } | 894 | } |
| 878 | oneway: true | 895 | oneway: true |
| 896 | bypass_target_door: true | ||
| 879 | } | 897 | } |
| 880 | connections { | 898 | connections { |
| 881 | from { | 899 | from { |
| @@ -1455,7 +1473,6 @@ connections { | |||
| 1455 | name: "GREAT" | 1473 | name: "GREAT" |
| 1456 | } | 1474 | } |
| 1457 | } | 1475 | } |
| 1458 | door { map: "the_great" name: "Daedalus Entrance" } | ||
| 1459 | oneway: true | 1476 | oneway: true |
| 1460 | } | 1477 | } |
| 1461 | connections { | 1478 | connections { |
| @@ -1474,6 +1491,7 @@ connections { | |||
| 1474 | } | 1491 | } |
| 1475 | } | 1492 | } |
| 1476 | oneway: true | 1493 | oneway: true |
| 1494 | bypass_target_door: true | ||
| 1477 | } | 1495 | } |
| 1478 | connections { | 1496 | connections { |
| 1479 | from { | 1497 | from { |
| @@ -1769,12 +1787,13 @@ connections { | |||
| 1769 | } | 1787 | } |
| 1770 | } | 1788 | } |
| 1771 | oneway: true | 1789 | oneway: true |
| 1790 | bypass_target_door: true | ||
| 1772 | } | 1791 | } |
| 1773 | connections { | 1792 | connections { |
| 1774 | from { | 1793 | from { |
| 1775 | port { | 1794 | port { |
| 1776 | map: "the_bearer" | 1795 | map: "the_bearer" |
| 1777 | room: "Back Area" | 1796 | room: "Tree Entrance" |
| 1778 | name: "TREE" | 1797 | name: "TREE" |
| 1779 | } | 1798 | } |
| 1780 | } | 1799 | } |
| @@ -1851,7 +1870,6 @@ connections { | |||
| 1851 | } | 1870 | } |
| 1852 | } | 1871 | } |
| 1853 | connections { | 1872 | connections { |
| 1854 | # Two one-way connections because the door only blocks one direction. | ||
| 1855 | from { | 1873 | from { |
| 1856 | port { | 1874 | port { |
| 1857 | map: "the_great" | 1875 | map: "the_great" |
| @@ -1868,6 +1886,7 @@ connections { | |||
| 1868 | } | 1886 | } |
| 1869 | } | 1887 | } |
| 1870 | connections { | 1888 | connections { |
| 1889 | # Two one-way connections because the door only blocks one direction. | ||
| 1871 | from { | 1890 | from { |
| 1872 | port { | 1891 | port { |
| 1873 | map: "the_unkempt" | 1892 | map: "the_unkempt" |
| @@ -1900,6 +1919,7 @@ connections { | |||
| 1900 | } | 1919 | } |
| 1901 | } | 1920 | } |
| 1902 | oneway: true | 1921 | oneway: true |
| 1922 | bypass_target_door: true | ||
| 1903 | } | 1923 | } |
| 1904 | connections { | 1924 | connections { |
| 1905 | from { | 1925 | from { |
| @@ -2425,3 +2445,19 @@ connections { | |||
| 2425 | } | 2445 | } |
| 2426 | } | 2446 | } |
| 2427 | } | 2447 | } |
| 2448 | connections { | ||
| 2449 | from { | ||
| 2450 | painting { | ||
| 2451 | map: "the_entry" | ||
| 2452 | room: "Eye Room" | ||
| 2453 | name: "GALLERY" | ||
| 2454 | } | ||
| 2455 | } | ||
| 2456 | to { | ||
| 2457 | room { | ||
| 2458 | map: "the_gallery" | ||
| 2459 | name: "Main Area" | ||
| 2460 | } | ||
| 2461 | } | ||
| 2462 | oneway: true | ||
| 2463 | } | ||
| diff --git a/data/ids.yaml b/data/ids.yaml index 30a400b..80ace1a 100644 --- a/data/ids.yaml +++ b/data/ids.yaml | |||
| @@ -748,7 +748,7 @@ maps: | |||
| 748 | Back (2): 2090 | 748 | Back (2): 2090 |
| 749 | Colors: 2097 | 749 | Colors: 2097 |
| 750 | FIR: 2095 | 750 | FIR: 2095 |
| 751 | Left: 2088 | 751 | Near Obscured Puzzles: 2088 |
| 752 | OAK: 2093 | 752 | OAK: 2093 |
| 753 | PINE: 2094 | 753 | PINE: 2094 |
| 754 | WALK BACK: 2091 | 754 | WALK BACK: 2091 |
| @@ -987,7 +987,6 @@ maps: | |||
| 987 | Blue Rainbow Room: 1538 | 987 | Blue Rainbow Room: 1538 |
| 988 | Blue Room: 1477 | 988 | Blue Room: 1477 |
| 989 | Blue Room Entrance: 1476 | 989 | Blue Room Entrance: 1476 |
| 990 | Blue Smiley Entrance: 1478 | ||
| 991 | Blue Smiley Exit To Red: 1547 | 990 | Blue Smiley Exit To Red: 1547 |
| 992 | Book Room Entrance: 1588 | 991 | Book Room Entrance: 1588 |
| 993 | Book Room Exit: 1592 | 992 | Book Room Exit: 1592 |
| @@ -1018,10 +1017,10 @@ maps: | |||
| 1018 | Eye Painting: 2751 | 1017 | Eye Painting: 2751 |
| 1019 | Eye Painting Exit: 1446 | 1018 | Eye Painting Exit: 1446 |
| 1020 | F Keyholder Door: 1551 | 1019 | F Keyholder Door: 1551 |
| 1021 | F2 Room Back Left Door: 1491 | 1020 | F2 Room Northwest Door: 1491 |
| 1022 | F2 Room Back Middle Door: 1492 | 1021 | F2 Room Southeast Door: 1487 |
| 1023 | F2 Room Back Right Door: 1490 | 1022 | F2 Room Southwest Door: 1490 |
| 1024 | F2 Room Entrance: 1487 | 1023 | F2 Room West Door: 1492 |
| 1025 | Flip Painting Blocker: 1552 | 1024 | Flip Painting Blocker: 1552 |
| 1026 | Globe Room East Door: 1589 | 1025 | Globe Room East Door: 1589 |
| 1027 | Globe Room South Door: 1591 | 1026 | Globe Room South Door: 1591 |
| @@ -1044,6 +1043,7 @@ maps: | |||
| 1044 | House Entrance: 1495 | 1043 | House Entrance: 1495 |
| 1045 | House Side Door: 1566 | 1044 | House Side Door: 1566 |
| 1046 | Intense Room Entrance: 1522 | 1045 | Intense Room Entrance: 1522 |
| 1046 | Lime Hexes: 2810 | ||
| 1047 | Magenta Hexes: 2272 | 1047 | Magenta Hexes: 2272 |
| 1048 | Magic Room Entrance: 1500 | 1048 | Magic Room Entrance: 1500 |
| 1049 | Magic Room Panels: 1499 | 1049 | Magic Room Panels: 1499 |
| @@ -1056,7 +1056,8 @@ maps: | |||
| 1056 | Near Sweet Brown Door: 1561 | 1056 | Near Sweet Brown Door: 1561 |
| 1057 | Near Yellow Room Door: 1565 | 1057 | Near Yellow Room Door: 1565 |
| 1058 | North Castle Panel: 2742 | 1058 | North Castle Panel: 2742 |
| 1059 | O2 Room Back Door: 1485 | 1059 | O2 Room Northeast Door: 1485 |
| 1060 | O2 Room Southeast Door: 1478 | ||
| 1060 | Orange Rainbow Panel: 2267 | 1061 | Orange Rainbow Panel: 2267 |
| 1061 | Orange Rainbow Room: 1535 | 1062 | Orange Rainbow Room: 1535 |
| 1062 | Orange Room: 1507 | 1063 | Orange Room: 1507 |
| @@ -1110,8 +1111,8 @@ maps: | |||
| 1110 | South Castle Area Entrance: 1575 | 1111 | South Castle Area Entrance: 1575 |
| 1111 | South Castle Panel: 2744 | 1112 | South Castle Panel: 2744 |
| 1112 | Southwest Area Intersection: 1475 | 1113 | Southwest Area Intersection: 1475 |
| 1113 | Splintering Exit Left Door: 1449 | 1114 | Splintering Exit North Door: 1449 |
| 1114 | Splintering Exit Right Door: 1450 | 1115 | Splintering Exit South Door: 1450 |
| 1115 | Starting Room East Wall Center Door: 1439 | 1116 | Starting Room East Wall Center Door: 1439 |
| 1116 | Starting Room East Wall North Door: 1440 | 1117 | Starting Room East Wall North Door: 1440 |
| 1117 | Starting Room North Wall Center Door: 1432 | 1118 | Starting Room North Wall Center Door: 1432 |
| @@ -1124,16 +1125,17 @@ maps: | |||
| 1124 | Starting Room West Wall South Door: 1433 | 1125 | Starting Room West Wall South Door: 1433 |
| 1125 | Sticks And Stones Door: 1593 | 1126 | Sticks And Stones Door: 1593 |
| 1126 | Temple of the Eyes Entrance: 1444 | 1127 | Temple of the Eyes Entrance: 1444 |
| 1127 | U2 Room Back Door: 1497 | 1128 | Theo Panels: 2811 |
| 1128 | U2 Room Back Right Door: 1496 | 1129 | U2 Room East Door: 1498 |
| 1129 | U2 Room Entrance: 1498 | 1130 | U2 Room Southeast Door: 1493 |
| 1130 | U2 Room Shortcut: 1493 | 1131 | U2 Room Southwest Door: 1496 |
| 1132 | U2 Room West Door: 1497 | ||
| 1131 | Welcome Back Door: 1435 | 1133 | Welcome Back Door: 1435 |
| 1132 | Welcome Back Secret Door: 1434 | 1134 | Welcome Back Secret Door: 1434 |
| 1133 | West Castle Panel: 2743 | 1135 | West Castle Panel: 2743 |
| 1134 | White Hallway From Entry: 1488 | 1136 | White Hallway From Entry: 1488 |
| 1135 | Wonderland Left Door: 1520 | 1137 | Wonderland North Door: 1520 |
| 1136 | Wonderland Right Door: 1504 | 1138 | Wonderland South Door: 1504 |
| 1137 | Yellow Rainbow Panel: 2268 | 1139 | Yellow Rainbow Panel: 2268 |
| 1138 | Yellow Rainbow Room: 1536 | 1140 | Yellow Rainbow Room: 1536 |
| 1139 | Yellow Room: 1568 | 1141 | Yellow Room: 1568 |
| @@ -2325,9 +2327,9 @@ maps: | |||
| 2325 | keyholders: | 2327 | keyholders: |
| 2326 | S: 2767 | 2328 | S: 2767 |
| 2327 | doors: | 2329 | doors: |
| 2328 | Left Room Puzzles: 763 | 2330 | Blue Side Puzzles: 763 |
| 2331 | Green Side Puzzles: 764 | ||
| 2329 | Main Room Door: 2750 | 2332 | Main Room Door: 2750 |
| 2330 | Right Room Puzzles: 764 | ||
| 2331 | the_orb: | 2333 | the_orb: |
| 2332 | rooms: | 2334 | rooms: |
| 2333 | Main Area: | 2335 | Main Area: |
| @@ -2557,17 +2559,17 @@ maps: | |||
| 2557 | TYPIST BEAR RIGHT WING: 968 | 2559 | TYPIST BEAR RIGHT WING: 968 |
| 2558 | WING: 950 | 2560 | WING: 950 |
| 2559 | doors: | 2561 | doors: |
| 2560 | Bottom Left Door: 894 | 2562 | Northeast Door: 893 |
| 2561 | Bottom Left Puzzles: 898 | 2563 | Northeast Puzzles: 897 |
| 2562 | Bottom Right Door: 895 | 2564 | Northwest Door: 892 |
| 2563 | Bottom Right Puzzles: 899 | 2565 | Northwest Puzzles: 896 |
| 2564 | Repetitive Entrance: 888 | 2566 | Repetitive Entrance: 888 |
| 2565 | Sirenic Entrance: 890 | 2567 | Sirenic Entrance: 890 |
| 2568 | Southeast Door: 895 | ||
| 2569 | Southeast Puzzles: 899 | ||
| 2570 | Southwest Door: 894 | ||
| 2571 | Southwest Puzzles: 898 | ||
| 2566 | Symbolic Entrance: 889 | 2572 | Symbolic Entrance: 889 |
| 2567 | Top Left Door: 892 | ||
| 2568 | Top Left Puzzles: 896 | ||
| 2569 | Top Right Door: 893 | ||
| 2570 | Top Right Puzzles: 897 | ||
| 2571 | Turtle Entrance: 891 | 2573 | Turtle Entrance: 891 |
| 2572 | the_quiet: | 2574 | the_quiet: |
| 2573 | rooms: | 2575 | rooms: |
| @@ -2650,7 +2652,8 @@ maps: | |||
| 2650 | rooms: | 2652 | rooms: |
| 2651 | Anti Room: | 2653 | Anti Room: |
| 2652 | panels: | 2654 | panels: |
| 2653 | EYE: 1041 | 2655 | EYE (1): 1041 |
| 2656 | EYE (2): 2813 | ||
| 2654 | HA (1): 1035 | 2657 | HA (1): 1035 |
| 2655 | HA (2): 1036 | 2658 | HA (2): 1036 |
| 2656 | HA (3): 1037 | 2659 | HA (3): 1037 |
| @@ -2757,6 +2760,7 @@ maps: | |||
| 2757 | W: 1117 | 2760 | W: 1117 |
| 2758 | ZEROING: 1118 | 2761 | ZEROING: 1118 |
| 2759 | doors: | 2762 | doors: |
| 2763 | Anti-Collectable: 2812 | ||
| 2760 | Anti-Collectable Room: 1025 | 2764 | Anti-Collectable Room: 1025 |
| 2761 | Black Hallway: 2780 | 2765 | Black Hallway: 2780 |
| 2762 | Cyan Door: 1028 | 2766 | Cyan Door: 1028 |
| @@ -3448,9 +3452,9 @@ maps: | |||
| 3448 | doors: | 3452 | doors: |
| 3449 | Cog Rhino Hug Rug: 2586 | 3453 | Cog Rhino Hug Rug: 2586 |
| 3450 | Control Center Orange Door: 2582 | 3454 | Control Center Orange Door: 2582 |
| 3455 | East Door: 2580 | ||
| 3451 | Honor Our Hint: 2585 | 3456 | Honor Our Hint: 2585 |
| 3452 | Let Untrue Tie: 2583 | 3457 | Let Untrue Tie: 2583 |
| 3453 | Right Door: 2580 | ||
| 3454 | Routine Out Chute: 2584 | 3458 | Routine Out Chute: 2584 |
| 3455 | W2 Room Door: 2581 | 3459 | W2 Room Door: 2581 |
| 3456 | the_unyielding: | 3460 | the_unyielding: |
| @@ -3838,6 +3842,32 @@ special: | |||
| 3838 | A Job Well Done: 1160 | 3842 | A Job Well Done: 1160 |
| 3839 | Age Symbol: 2791 | 3843 | Age Symbol: 2791 |
| 3840 | Anagram Symbol: 2792 | 3844 | Anagram Symbol: 2792 |
| 3845 | Anti A: 2814 | ||
| 3846 | Anti B: 2815 | ||
| 3847 | Anti C: 2816 | ||
| 3848 | Anti D: 2817 | ||
| 3849 | Anti E: 2818 | ||
| 3850 | Anti F: 2819 | ||
| 3851 | Anti G: 2820 | ||
| 3852 | Anti H: 2821 | ||
| 3853 | Anti I: 2822 | ||
| 3854 | Anti J: 2823 | ||
| 3855 | Anti K: 2824 | ||
| 3856 | Anti L: 2825 | ||
| 3857 | Anti M: 2826 | ||
| 3858 | Anti N: 2827 | ||
| 3859 | Anti O: 2828 | ||
| 3860 | Anti P: 2829 | ||
| 3861 | Anti Q: 2830 | ||
| 3862 | Anti R: 2831 | ||
| 3863 | Anti S: 2832 | ||
| 3864 | Anti T: 2833 | ||
| 3865 | Anti U: 2834 | ||
| 3866 | Anti V: 2835 | ||
| 3867 | Anti W: 2836 | ||
| 3868 | Anti X: 2837 | ||
| 3869 | Anti Y: 2838 | ||
| 3870 | Anti Z: 2839 | ||
| 3841 | Boxes Symbol: 2793 | 3871 | Boxes Symbol: 2793 |
| 3842 | Cross Symbol: 2794 | 3872 | Cross Symbol: 2794 |
| 3843 | Eval Symbol: 2795 | 3873 | Eval Symbol: 2795 |
| diff --git a/data/maps/daedalus/connections.txtpb b/data/maps/daedalus/connections.txtpb index 09613ae..cb27c38 100644 --- a/data/maps/daedalus/connections.txtpb +++ b/data/maps/daedalus/connections.txtpb | |||
| @@ -100,6 +100,11 @@ connections { | |||
| 100 | oneway: true | 100 | oneway: true |
| 101 | } | 101 | } |
| 102 | connections { | 102 | connections { |
| 103 | from_room: "Outside House" | ||
| 104 | to_room: "Blue Hallway Tall Side" | ||
| 105 | door { name: "House Side Door" } | ||
| 106 | } | ||
| 107 | connections { | ||
| 103 | from_room: "Purple SE Vestibule" | 108 | from_room: "Purple SE Vestibule" |
| 104 | to_room: "Welcome Back Area" | 109 | to_room: "Welcome Back Area" |
| 105 | oneway: true | 110 | oneway: true |
| @@ -222,12 +227,12 @@ connections { | |||
| 222 | connections { | 227 | connections { |
| 223 | from_room: "West Castle Area" | 228 | from_room: "West Castle Area" |
| 224 | to_room: "Post Orange Smiley Three Way" | 229 | to_room: "Post Orange Smiley Three Way" |
| 225 | door { name: "Splintering Exit Left Door" } | 230 | door { name: "Splintering Exit North Door" } |
| 226 | } | 231 | } |
| 227 | connections { | 232 | connections { |
| 228 | from_room: "West Castle Area" | 233 | from_room: "West Castle Area" |
| 229 | to_room: "Amber North 2" | 234 | to_room: "Amber North 2" |
| 230 | door { name: "Splintering Exit Right Door" } | 235 | door { name: "Splintering Exit South Door" } |
| 231 | } | 236 | } |
| 232 | connections { | 237 | connections { |
| 233 | from_room: "Z2 Room" | 238 | from_room: "Z2 Room" |
| @@ -378,7 +383,7 @@ connections { | |||
| 378 | connections { | 383 | connections { |
| 379 | from_room: "O2 Room" | 384 | from_room: "O2 Room" |
| 380 | to_room: "Blue Smiley" | 385 | to_room: "Blue Smiley" |
| 381 | door { name: "Blue Smiley Entrance" } | 386 | door { name: "O2 Room Southeast Door" } |
| 382 | } | 387 | } |
| 383 | connections { | 388 | connections { |
| 384 | from_room: "O2 Room" | 389 | from_room: "O2 Room" |
| @@ -408,7 +413,7 @@ connections { | |||
| 408 | connections { | 413 | connections { |
| 409 | from_room: "O2 Room" | 414 | from_room: "O2 Room" |
| 410 | to_room: "Blue Hallway" | 415 | to_room: "Blue Hallway" |
| 411 | door { name: "O2 Room Back Door" } | 416 | door { name: "O2 Room Northeast Door" } |
| 412 | } | 417 | } |
| 413 | connections { | 418 | connections { |
| 414 | from_room: "O2 Room" | 419 | from_room: "O2 Room" |
| @@ -423,7 +428,7 @@ connections { | |||
| 423 | connections { | 428 | connections { |
| 424 | from_room: "Sweet Foyer" | 429 | from_room: "Sweet Foyer" |
| 425 | to_room: "F2 Room" | 430 | to_room: "F2 Room" |
| 426 | door { name: "F2 Room Entrance" } | 431 | door { name: "F2 Room Southeast Door" } |
| 427 | } | 432 | } |
| 428 | connections { | 433 | connections { |
| 429 | from_room: "Globe Room" | 434 | from_room: "Globe Room" |
| @@ -438,17 +443,17 @@ connections { | |||
| 438 | connections { | 443 | connections { |
| 439 | from_room: "F2 Room" | 444 | from_room: "F2 Room" |
| 440 | to_room: "Blue Hallway" | 445 | to_room: "Blue Hallway" |
| 441 | door { name: "F2 Room Back Right Door" } | 446 | door { name: "F2 Room Southwest Door" } |
| 442 | } | 447 | } |
| 443 | connections { | 448 | connections { |
| 444 | from_room: "F2 Room" | 449 | from_room: "F2 Room" |
| 445 | to_room: "Outside Salt Room" | 450 | to_room: "Outside Salt Room" |
| 446 | door { name: "F2 Room Back Left Door" } | 451 | door { name: "F2 Room Northwest Door" } |
| 447 | } | 452 | } |
| 448 | connections { | 453 | connections { |
| 449 | from_room: "F2 Room" | 454 | from_room: "F2 Room" |
| 450 | to_room: "Red Color Door" | 455 | to_room: "Red Color Door" |
| 451 | door { name: "F2 Room Back Middle Door" } | 456 | door { name: "F2 Room West Door" } |
| 452 | oneway: true | 457 | oneway: true |
| 453 | # This is the red backside, which has nothing in it. Maybe could be its own | 458 | # This is the red backside, which has nothing in it. Maybe could be its own |
| 454 | # region at some point. | 459 | # region at some point. |
| @@ -461,7 +466,7 @@ connections { | |||
| 461 | connections { | 466 | connections { |
| 462 | from_room: "U2 Room" | 467 | from_room: "U2 Room" |
| 463 | to_room: "Maze Paintings Area" | 468 | to_room: "Maze Paintings Area" |
| 464 | door { name: "U2 Room Shortcut" } | 469 | door { name: "U2 Room Southeast Door" } |
| 465 | } | 470 | } |
| 466 | connections { | 471 | connections { |
| 467 | from_room: "Maze Paintings Area" | 472 | from_room: "Maze Paintings Area" |
| @@ -476,17 +481,17 @@ connections { | |||
| 476 | connections { | 481 | connections { |
| 477 | from_room: "U2 Room" | 482 | from_room: "U2 Room" |
| 478 | to_room: "Purple SE Vestibule" | 483 | to_room: "Purple SE Vestibule" |
| 479 | door { name: "U2 Room Back Right Door" } | 484 | door { name: "U2 Room Southwest Door" } |
| 480 | } | 485 | } |
| 481 | connections { | 486 | connections { |
| 482 | from_room: "U2 Room" | 487 | from_room: "U2 Room" |
| 483 | to_room: "Purple Room East" | 488 | to_room: "Purple Room East" |
| 484 | door { name: "U2 Room Back Door" } | 489 | door { name: "U2 Room West Door" } |
| 485 | } | 490 | } |
| 486 | connections { | 491 | connections { |
| 487 | from_room: "Maze" | 492 | from_room: "Maze" |
| 488 | to_room: "U2 Room" | 493 | to_room: "U2 Room" |
| 489 | door { name: "U2 Room Entrance" } | 494 | door { name: "U2 Room East Door" } |
| 490 | } | 495 | } |
| 491 | connections { | 496 | connections { |
| 492 | from_room: "Outside Magic Room" | 497 | from_room: "Outside Magic Room" |
| @@ -511,7 +516,7 @@ connections { | |||
| 511 | connections { | 516 | connections { |
| 512 | from_room: "Wonderland" | 517 | from_room: "Wonderland" |
| 513 | to_room: "Black Hex" | 518 | to_room: "Black Hex" |
| 514 | door { name: "Wonderland Right Door" } | 519 | door { name: "Wonderland South Door" } |
| 515 | } | 520 | } |
| 516 | connections { | 521 | connections { |
| 517 | from_room: "Outside Pyramid" | 522 | from_room: "Outside Pyramid" |
| @@ -601,7 +606,7 @@ connections { | |||
| 601 | connections { | 606 | connections { |
| 602 | from_room: "Wonderland" | 607 | from_room: "Wonderland" |
| 603 | to_room: "Number Paintings Area" | 608 | to_room: "Number Paintings Area" |
| 604 | door { name: "Wonderland Left Door" } | 609 | door { name: "Wonderland North Door" } |
| 605 | } | 610 | } |
| 606 | connections { | 611 | connections { |
| 607 | from_room: "Outside House" | 612 | from_room: "Outside House" |
| @@ -1857,3 +1862,15 @@ connections { | |||
| 1857 | oneway: true | 1862 | oneway: true |
| 1858 | roof_access: true | 1863 | roof_access: true |
| 1859 | } | 1864 | } |
| 1865 | connections { | ||
| 1866 | from_room: "Roof" | ||
| 1867 | to_room: "F Keyholder" | ||
| 1868 | oneway: true | ||
| 1869 | roof_access: true | ||
| 1870 | } | ||
| 1871 | connections { | ||
| 1872 | from_room: "Roof" | ||
| 1873 | to_room: "Yellow Color Backside" | ||
| 1874 | oneway: true | ||
| 1875 | roof_access: true | ||
| 1876 | } | ||
| diff --git a/data/maps/daedalus/doors.txtpb b/data/maps/daedalus/doors.txtpb index ccbf3f0..95a3537 100644 --- a/data/maps/daedalus/doors.txtpb +++ b/data/maps/daedalus/doors.txtpb | |||
| @@ -195,8 +195,8 @@ doors { | |||
| 195 | } | 195 | } |
| 196 | doors { | 196 | doors { |
| 197 | name: "Welcome Back Door" | 197 | name: "Welcome Back Door" |
| 198 | type: STANDARD | 198 | type: LOCATION_ONLY |
| 199 | receivers: "Components/Doors/Entry/entry_14" | 199 | #receivers: "Components/Doors/Entry/entry_14" |
| 200 | panels { room: "Welcome Back Area" name: "GREETINGS OLD FRIEND" } | 200 | panels { room: "Welcome Back Area" name: "GREETINGS OLD FRIEND" } |
| 201 | location_room: "Welcome Back Area" | 201 | location_room: "Welcome Back Area" |
| 202 | } | 202 | } |
| @@ -298,7 +298,7 @@ doors { | |||
| 298 | location_name: "Black Hex" | 298 | location_name: "Black Hex" |
| 299 | } | 299 | } |
| 300 | doors { | 300 | doors { |
| 301 | name: "Splintering Exit Left Door" | 301 | name: "Splintering Exit North Door" |
| 302 | type: STANDARD | 302 | type: STANDARD |
| 303 | receivers: "Components/Doors/Entry/gate_4" | 303 | receivers: "Components/Doors/Entry/gate_4" |
| 304 | panels { room: "West Castle Area" name: "EVER" } | 304 | panels { room: "West Castle Area" name: "EVER" } |
| @@ -308,7 +308,7 @@ doors { | |||
| 308 | location_room: "West Castle Area" | 308 | location_room: "West Castle Area" |
| 309 | } | 309 | } |
| 310 | doors { | 310 | doors { |
| 311 | name: "Splintering Exit Right Door" | 311 | name: "Splintering Exit South Door" |
| 312 | type: ITEM_ONLY | 312 | type: ITEM_ONLY |
| 313 | receivers: "Components/Doors/Entry/gate_5" | 313 | receivers: "Components/Doors/Entry/gate_5" |
| 314 | panels { room: "West Castle Area" name: "EVER" } | 314 | panels { room: "West Castle Area" name: "EVER" } |
| @@ -493,7 +493,6 @@ doors { | |||
| 493 | panels { room: "Outside House" name: "WALLS" } | 493 | panels { room: "Outside House" name: "WALLS" } |
| 494 | panels { room: "Outside House" name: "LOCK" } | 494 | panels { room: "Outside House" name: "LOCK" } |
| 495 | location_room: "Outside House" | 495 | location_room: "Outside House" |
| 496 | location_name: "North Purple Vestibules" | ||
| 497 | } | 496 | } |
| 498 | doors { | 497 | doors { |
| 499 | name: "Purple NW Vestibule" | 498 | name: "Purple NW Vestibule" |
| @@ -716,7 +715,7 @@ doors { | |||
| 716 | panels { room: "O2 Room" name: "UNBLOCKED" } | 715 | panels { room: "O2 Room" name: "UNBLOCKED" } |
| 717 | } | 716 | } |
| 718 | doors { | 717 | doors { |
| 719 | name: "Blue Smiley Entrance" | 718 | name: "O2 Room Southeast Door" |
| 720 | type: STANDARD | 719 | type: STANDARD |
| 721 | receivers: "Components/Doors/Halls/oroom_2" | 720 | receivers: "Components/Doors/Halls/oroom_2" |
| 722 | panels { room: "O2 Room" name: "HONEST" } | 721 | panels { room: "O2 Room" name: "HONEST" } |
| @@ -822,7 +821,7 @@ doors { | |||
| 822 | name: "Composite Room NW Entrance" | 821 | name: "Composite Room NW Entrance" |
| 823 | type: STANDARD | 822 | type: STANDARD |
| 824 | receivers: "Components/Doors/Halls/oroom_10" | 823 | receivers: "Components/Doors/Halls/oroom_10" |
| 825 | panels { room: "Red Color Door" name: "Left" } | 824 | panels { room: "Red Color Door" name: "Near Obscured Puzzles" } |
| 826 | location_room: "Red Color Door" | 825 | location_room: "Red Color Door" |
| 827 | } | 826 | } |
| 828 | doors { | 827 | doors { |
| @@ -857,7 +856,7 @@ doors { | |||
| 857 | location_name: "South Rooms" | 856 | location_name: "South Rooms" |
| 858 | } | 857 | } |
| 859 | doors { | 858 | doors { |
| 860 | name: "O2 Room Back Door" | 859 | name: "O2 Room Northeast Door" |
| 861 | type: STANDARD | 860 | type: STANDARD |
| 862 | receivers: "Components/Doors/Halls/oroom_4" | 861 | receivers: "Components/Doors/Halls/oroom_4" |
| 863 | panels { room: "O2 Room" name: "UNBLOCKED" } | 862 | panels { room: "O2 Room" name: "UNBLOCKED" } |
| @@ -884,7 +883,7 @@ doors { | |||
| 884 | panels { room: "F2 Room" name: "SHAPE" } | 883 | panels { room: "F2 Room" name: "SHAPE" } |
| 885 | } | 884 | } |
| 886 | doors { | 885 | doors { |
| 887 | name: "F2 Room Entrance" | 886 | name: "F2 Room Southeast Door" |
| 888 | type: STANDARD | 887 | type: STANDARD |
| 889 | receivers: "Components/Doors/Halls/froom_2" | 888 | receivers: "Components/Doors/Halls/froom_2" |
| 890 | panels { room: "Sweet Foyer" name: "RENT (1)" } | 889 | panels { room: "Sweet Foyer" name: "RENT (1)" } |
| @@ -903,21 +902,21 @@ doors { | |||
| 903 | control_center_color: "purple" | 902 | control_center_color: "purple" |
| 904 | } | 903 | } |
| 905 | doors { | 904 | doors { |
| 906 | name: "F2 Room Back Right Door" | 905 | name: "F2 Room Southwest Door" |
| 907 | type: STANDARD | 906 | type: STANDARD |
| 908 | receivers: "Components/Doors/Halls/froom_3" | 907 | receivers: "Components/Doors/Halls/froom_3" |
| 909 | panels { room: "F2 Room" name: "RISKY" } | 908 | panels { room: "F2 Room" name: "RISKY" } |
| 910 | location_room: "F2 Room" | 909 | location_room: "F2 Room" |
| 911 | } | 910 | } |
| 912 | doors { | 911 | doors { |
| 913 | name: "F2 Room Back Left Door" | 912 | name: "F2 Room Northwest Door" |
| 914 | type: STANDARD | 913 | type: STANDARD |
| 915 | receivers: "Components/Doors/Halls/froom_4" | 914 | receivers: "Components/Doors/Halls/froom_4" |
| 916 | panels { room: "F2 Room" name: "SHAPE" } | 915 | panels { room: "F2 Room" name: "SHAPE" } |
| 917 | location_room: "F2 Room" | 916 | location_room: "F2 Room" |
| 918 | } | 917 | } |
| 919 | doors { | 918 | doors { |
| 920 | name: "F2 Room Back Middle Door" | 919 | name: "F2 Room West Door" |
| 921 | type: STANDARD | 920 | type: STANDARD |
| 922 | receivers: "Components/Doors/Halls/froom_5" | 921 | receivers: "Components/Doors/Halls/froom_5" |
| 923 | panels { room: "F2 Room" name: "DIRT" } | 922 | panels { room: "F2 Room" name: "DIRT" } |
| @@ -938,7 +937,7 @@ doors { | |||
| 938 | panels { room: "U2 Room" name: "HEAVEN" } | 937 | panels { room: "U2 Room" name: "HEAVEN" } |
| 939 | } | 938 | } |
| 940 | doors { | 939 | doors { |
| 941 | name: "U2 Room Shortcut" | 940 | name: "U2 Room Southeast Door" |
| 942 | type: STANDARD | 941 | type: STANDARD |
| 943 | receivers: "Components/Doors/Halls/uroom_2" | 942 | receivers: "Components/Doors/Halls/uroom_2" |
| 944 | panels { room: "U2 Room" name: "WICKED" } | 943 | panels { room: "U2 Room" name: "WICKED" } |
| @@ -959,21 +958,21 @@ doors { | |||
| 959 | location_room: "House Entrance" | 958 | location_room: "House Entrance" |
| 960 | } | 959 | } |
| 961 | doors { | 960 | doors { |
| 962 | name: "U2 Room Back Right Door" | 961 | name: "U2 Room Southwest Door" |
| 963 | type: STANDARD | 962 | type: STANDARD |
| 964 | receivers: "Components/Doors/Halls/uroom_3" | 963 | receivers: "Components/Doors/Halls/uroom_3" |
| 965 | panels { room: "U2 Room" name: "HEAVEN" } | 964 | panels { room: "U2 Room" name: "HEAVEN" } |
| 966 | location_room: "U2 Room" | 965 | location_room: "U2 Room" |
| 967 | } | 966 | } |
| 968 | doors { | 967 | doors { |
| 969 | name: "U2 Room Back Door" | 968 | name: "U2 Room West Door" |
| 970 | type: ITEM_ONLY | 969 | type: ITEM_ONLY |
| 971 | receivers: "Components/Doors/Halls/uroom_5" | 970 | receivers: "Components/Doors/Halls/uroom_5" |
| 972 | panels { room: "Purple Room South" name: "ANY" } | 971 | panels { room: "Purple Room South" name: "ANY" } |
| 973 | panels { room: "Outside House" name: "A" } | 972 | panels { room: "Outside House" name: "A" } |
| 974 | } | 973 | } |
| 975 | doors { | 974 | doors { |
| 976 | name: "U2 Room Entrance" | 975 | name: "U2 Room East Door" |
| 977 | type: ITEM_ONLY | 976 | type: ITEM_ONLY |
| 978 | receivers: "Components/Doors/Halls/uroom_4" | 977 | receivers: "Components/Doors/Halls/uroom_4" |
| 979 | panels { room: "Outside Magic Room" name: "WIZARD" } | 978 | panels { room: "Outside Magic Room" name: "WIZARD" } |
| @@ -1017,7 +1016,7 @@ doors { | |||
| 1017 | panels { room: "Outside Magic Room" name: "WIZARD" } | 1016 | panels { room: "Outside Magic Room" name: "WIZARD" } |
| 1018 | } | 1017 | } |
| 1019 | doors { | 1018 | doors { |
| 1020 | name: "Wonderland Right Door" | 1019 | name: "Wonderland South Door" |
| 1021 | type: STANDARD | 1020 | type: STANDARD |
| 1022 | receivers: "Components/Doors/Halls/wonderland_1" | 1021 | receivers: "Components/Doors/Halls/wonderland_1" |
| 1023 | panels { room: "Wonderland" name: "APRIL" } | 1022 | panels { room: "Wonderland" name: "APRIL" } |
| @@ -1208,70 +1207,37 @@ doors { | |||
| 1208 | type: ITEM_ONLY | 1207 | type: ITEM_ONLY |
| 1209 | receivers: "Components/Doors/Halls/connections_1" | 1208 | receivers: "Components/Doors/Halls/connections_1" |
| 1210 | receivers: "Components/Doors/Halls/connections_3" | 1209 | receivers: "Components/Doors/Halls/connections_3" |
| 1210 | # These have the same effect as the above, but including them here prevents | ||
| 1211 | # them from opening in door shuffle when the J2 door opens. | ||
| 1212 | receivers: "Components/Triggers/teleportListenerConnections3" | ||
| 1213 | receivers: "Components/Triggers/teleportListenerConnections4" | ||
| 1214 | # This door can open from either solving all panels, or just the smiley ones, | ||
| 1215 | # and the latter is obviously a subset of the former so let's just check for | ||
| 1216 | # that. | ||
| 1211 | panels { room: "Hotel" name: "PARKA" } | 1217 | panels { room: "Hotel" name: "PARKA" } |
| 1212 | panels { room: "Hotel" name: "MARLIN" } | ||
| 1213 | panels { room: "Hotel" name: "WHO" } | ||
| 1214 | panels { room: "Hotel" name: "CLOAK" } | 1218 | panels { room: "Hotel" name: "CLOAK" } |
| 1215 | panels { room: "Hotel" name: "MANE" } | ||
| 1216 | panels { room: "Hotel" name: "WHAT" } | ||
| 1217 | panels { room: "Hotel" name: "BLAZER" } | ||
| 1218 | panels { room: "Hotel" name: "WHERE" } | ||
| 1219 | panels { room: "Hotel" name: "DOROTHY" } | 1219 | panels { room: "Hotel" name: "DOROTHY" } |
| 1220 | panels { room: "Hotel" name: "JACKET" } | ||
| 1221 | panels { room: "Hotel" name: "TAIL" } | ||
| 1222 | panels { room: "Hotel" name: "JAWS" } | 1220 | panels { room: "Hotel" name: "JAWS" } |
| 1223 | panels { room: "Hotel" name: "FLOUNDER" } | ||
| 1224 | panels { room: "Hotel" name: "WHEN" } | 1221 | panels { room: "Hotel" name: "WHEN" } |
| 1225 | panels { room: "Hotel" name: "CLAWS" } | 1222 | panels { room: "Hotel" name: "CLAWS" } |
| 1226 | panels { room: "Hotel" name: "BRUCE" } | ||
| 1227 | panels { room: "Hotel" name: "POTATO" } | 1223 | panels { room: "Hotel" name: "POTATO" } |
| 1228 | panels { room: "Hotel" name: "SALAD" } | ||
| 1229 | panels { room: "Hotel" name: "BATHING" } | ||
| 1230 | panels { room: "Hotel" name: "MICRO" } | 1224 | panels { room: "Hotel" name: "MICRO" } |
| 1231 | panels { room: "Hotel" name: "BUSINESS" } | ||
| 1232 | panels { room: "Hotel" name: "WEDDING" } | ||
| 1233 | panels { room: "Hotel" name: "TREE" } | ||
| 1234 | panels { room: "Hotel" name: "RIVER" } | ||
| 1235 | panels { room: "Hotel" name: "TUNING" } | 1225 | panels { room: "Hotel" name: "TUNING" } |
| 1236 | panels { room: "Hotel" name: "BOXING" } | ||
| 1237 | panels { room: "Hotel" name: "TELEPHONE" } | ||
| 1238 | panels { room: "Hotel" name: "LAW" } | 1226 | panels { room: "Hotel" name: "LAW" } |
| 1239 | panels { room: "Hotel" name: "POKER" } | ||
| 1240 | panels { room: "Hotel" name: "CARD" } | 1227 | panels { room: "Hotel" name: "CARD" } |
| 1241 | panels { room: "Hotel" name: "ROAD" } | 1228 | panels { room: "Hotel" name: "ROAD" } |
| 1242 | panels { room: "Hotel" name: "CHOCOLATE" } | ||
| 1243 | panels { room: "Hotel" name: "DEPART" } | 1229 | panels { room: "Hotel" name: "DEPART" } |
| 1244 | panels { room: "Hotel" name: "WITHDRAW" } | ||
| 1245 | panels { room: "Hotel" name: "QUIT" } | ||
| 1246 | panels { room: "Hotel" name: "LEAVE" } | 1230 | panels { room: "Hotel" name: "LEAVE" } |
| 1247 | panels { room: "Hotel" name: "PALE" } | ||
| 1248 | panels { room: "Hotel" name: "JUST" } | ||
| 1249 | panels { room: "Hotel" name: "NEW" } | ||
| 1250 | panels { room: "Hotel" name: "UNTALENTED" } | ||
| 1251 | panels { room: "Hotel" name: "SERVICE" } | 1231 | panels { room: "Hotel" name: "SERVICE" } |
| 1252 | panels { room: "Hotel" name: "FULL" } | ||
| 1253 | panels { room: "Hotel" name: "EVIL" } | ||
| 1254 | panels { room: "Hotel" name: "HONEY" } | 1232 | panels { room: "Hotel" name: "HONEY" } |
| 1255 | panels { room: "Hotel" name: "CRESCENT" } | ||
| 1256 | panels { room: "Hotel" name: "INVALID" } | 1233 | panels { room: "Hotel" name: "INVALID" } |
| 1257 | panels { room: "Hotel" name: "FESTIVAL" } | 1234 | panels { room: "Hotel" name: "FESTIVAL" } |
| 1258 | panels { room: "Hotel" name: "BEAUTIFUL" } | ||
| 1259 | panels { room: "Hotel" name: "WILTED" } | 1235 | panels { room: "Hotel" name: "WILTED" } |
| 1260 | panels { room: "Hotel" name: "DROOPED" } | ||
| 1261 | panels { room: "Hotel" name: "FADED" } | ||
| 1262 | panels { room: "Hotel" name: "WANED" } | 1236 | panels { room: "Hotel" name: "WANED" } |
| 1263 | panels { room: "Hotel" name: "TALL" } | ||
| 1264 | panels { room: "Hotel" name: "CANVAS" } | ||
| 1265 | panels { room: "Hotel" name: "LEVER" } | ||
| 1266 | panels { room: "Hotel" name: "SCULPTURE" } | ||
| 1267 | panels { room: "Hotel" name: "RAGE" } | 1237 | panels { room: "Hotel" name: "RAGE" } |
| 1268 | panels { room: "Hotel" name: "BALL" } | ||
| 1269 | panels { room: "Hotel" name: "FOOL" } | ||
| 1270 | panels { room: "Hotel" name: "VERGE" } | 1238 | panels { room: "Hotel" name: "VERGE" } |
| 1271 | panels { room: "Hotel" name: "ART" } | ||
| 1272 | panels { room: "Hotel" name: "EVER" } | 1239 | panels { room: "Hotel" name: "EVER" } |
| 1273 | panels { room: "Hotel" name: "PAIN" } | 1240 | panels { room: "Hotel" name: "PAIN" } |
| 1274 | panels { room: "Hotel" name: "FOOT" } | ||
| 1275 | } | 1241 | } |
| 1276 | doors { | 1242 | doors { |
| 1277 | name: "J2 Door 1" | 1243 | name: "J2 Door 1" |
| @@ -1307,7 +1273,7 @@ doors { | |||
| 1307 | panels { room: "J2 Vestibule" name: "COLORFUL" } | 1273 | panels { room: "J2 Vestibule" name: "COLORFUL" } |
| 1308 | } | 1274 | } |
| 1309 | doors { | 1275 | doors { |
| 1310 | name: "Wonderland Left Door" | 1276 | name: "Wonderland North Door" |
| 1311 | type: ITEM_ONLY | 1277 | type: ITEM_ONLY |
| 1312 | receivers: "Components/Doors/Halls/wonderland_2" | 1278 | receivers: "Components/Doors/Halls/wonderland_2" |
| 1313 | panels { room: "Wonderland" name: "APRIL" } | 1279 | panels { room: "Wonderland" name: "APRIL" } |
| @@ -1648,6 +1614,7 @@ doors { | |||
| 1648 | # Components/Doors/Smileys/blue_1 | 1614 | # Components/Doors/Smileys/blue_1 |
| 1649 | panels { room: "Blue Smiley" name: "SMILE" } | 1615 | panels { room: "Blue Smiley" name: "SMILE" } |
| 1650 | location_room: "Blue Smiley" | 1616 | location_room: "Blue Smiley" |
| 1617 | location_name: "Blue SMILE" | ||
| 1651 | } | 1618 | } |
| 1652 | doors { | 1619 | doors { |
| 1653 | name: "Blue Smiley Annex" | 1620 | name: "Blue Smiley Annex" |
| @@ -1679,6 +1646,7 @@ doors { | |||
| 1679 | receivers: "Components/Doors/Smileys/yellow_2" | 1646 | receivers: "Components/Doors/Smileys/yellow_2" |
| 1680 | panels { room: "Hedges" name: "SMILE" } | 1647 | panels { room: "Hedges" name: "SMILE" } |
| 1681 | location_room: "Hedges" | 1648 | location_room: "Hedges" |
| 1649 | location_name: "Yellow SMILE" | ||
| 1682 | } | 1650 | } |
| 1683 | doors { | 1651 | doors { |
| 1684 | name: "Green Smiley" | 1652 | name: "Green Smiley" |
| @@ -1687,6 +1655,7 @@ doors { | |||
| 1687 | receivers: "Components/Doors/Smileys/green_2" | 1655 | receivers: "Components/Doors/Smileys/green_2" |
| 1688 | panels { room: "Green Smiley" name: "SMILE" } | 1656 | panels { room: "Green Smiley" name: "SMILE" } |
| 1689 | location_room: "Green Smiley" | 1657 | location_room: "Green Smiley" |
| 1658 | location_name: "Green SMILE" | ||
| 1690 | } | 1659 | } |
| 1691 | doors { | 1660 | doors { |
| 1692 | name: "Orange Smiley Exit" | 1661 | name: "Orange Smiley Exit" |
| @@ -1694,6 +1663,7 @@ doors { | |||
| 1694 | receivers: "Components/Doors/Smileys/orange_1" | 1663 | receivers: "Components/Doors/Smileys/orange_1" |
| 1695 | panels { room: "Outside Orange Room" name: "SMILE" } | 1664 | panels { room: "Outside Orange Room" name: "SMILE" } |
| 1696 | location_room: "Outside Orange Room" | 1665 | location_room: "Outside Orange Room" |
| 1666 | location_name: "Orange SMILE" | ||
| 1697 | } | 1667 | } |
| 1698 | doors { | 1668 | doors { |
| 1699 | name: "F Keyholder Door" | 1669 | name: "F Keyholder Door" |
| @@ -1718,6 +1688,7 @@ doors { | |||
| 1718 | type: LOCATION_ONLY | 1688 | type: LOCATION_ONLY |
| 1719 | panels { room: "Red Smiley" name: "SMILE" } | 1689 | panels { room: "Red Smiley" name: "SMILE" } |
| 1720 | location_room: "Red Smiley" | 1690 | location_room: "Red Smiley" |
| 1691 | location_name: "Red SMILE" | ||
| 1721 | } | 1692 | } |
| 1722 | doors { | 1693 | doors { |
| 1723 | name: "Pink Hallway" | 1694 | name: "Pink Hallway" |
| @@ -2187,6 +2158,7 @@ doors { | |||
| 2187 | receivers: "Components/Doors/Unincorporated/temple_foyer_6" | 2158 | receivers: "Components/Doors/Unincorporated/temple_foyer_6" |
| 2188 | panels { room: "Globe Room" name: "WORD" } | 2159 | panels { room: "Globe Room" name: "WORD" } |
| 2189 | location_room: "Globe Room" | 2160 | location_room: "Globe Room" |
| 2161 | location_name: "Sticks and Stones" | ||
| 2190 | } | 2162 | } |
| 2191 | doors { | 2163 | doors { |
| 2192 | name: "Castle Numbers Puzzle" | 2164 | name: "Castle Numbers Puzzle" |
| @@ -2306,3 +2278,24 @@ doors { | |||
| 2306 | receivers: "Components/Paintings/Temple of the Eyes/eyeRedStart/teleportListener" | 2278 | receivers: "Components/Paintings/Temple of the Eyes/eyeRedStart/teleportListener" |
| 2307 | double_letters: true | 2279 | double_letters: true |
| 2308 | } | 2280 | } |
| 2281 | doors { | ||
| 2282 | name: "Lime Hexes" | ||
| 2283 | type: LOCATION_ONLY | ||
| 2284 | panels { room: "Tree Entrance" name: "RAT" } | ||
| 2285 | panels { room: "Tree Entrance" name: "DIFFERENCE" } | ||
| 2286 | panels { room: "Tree Entrance" name: "LEANS" } | ||
| 2287 | panels { room: "Tree Entrance" name: "QUESTION" } | ||
| 2288 | panels { room: "Tree Entrance" name: "WHERE" } | ||
| 2289 | panels { room: "Tree Entrance" name: "SUNDER" } | ||
| 2290 | location_room: "Tree Entrance" | ||
| 2291 | } | ||
| 2292 | doors { | ||
| 2293 | name: "Theo Panels" | ||
| 2294 | type: LOCATION_ONLY | ||
| 2295 | panels { room: "House" name: "GOAT" } | ||
| 2296 | panels { room: "House" name: "AMAZE" } | ||
| 2297 | panels { room: "House" name: "SKINNYHIM" } | ||
| 2298 | panels { room: "House" name: "THEO" } | ||
| 2299 | location_room: "House" | ||
| 2300 | location_name: "All Puzzles" | ||
| 2301 | } | ||
| diff --git a/data/maps/daedalus/rooms/C Keyholder.txtpb b/data/maps/daedalus/rooms/C Keyholder.txtpb index ef10a90..28793b2 100644 --- a/data/maps/daedalus/rooms/C Keyholder.txtpb +++ b/data/maps/daedalus/rooms/C Keyholder.txtpb | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | name: "C Keyholder" | 1 | name: "C Keyholder" |
| 2 | panel_display_name: "North Area" | 2 | panel_display_name: "East Area" |
| 3 | keyholders { | 3 | keyholders { |
| 4 | name: "C" | 4 | name: "C" |
| 5 | path: "Components/KeyHolders/keyHolderC" | 5 | path: "Components/KeyHolders/keyHolderC" |
| diff --git a/data/maps/daedalus/rooms/Red Color Door.txtpb b/data/maps/daedalus/rooms/Red Color Door.txtpb index f7eab21..344193e 100644 --- a/data/maps/daedalus/rooms/Red Color Door.txtpb +++ b/data/maps/daedalus/rooms/Red Color Door.txtpb | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | name: "Red Color Door" | 1 | name: "Red Color Door" |
| 2 | panel_display_name: "Southwest Area" | 2 | panel_display_name: "Southwest Area" |
| 3 | panels { | 3 | panels { |
| 4 | name: "Left" | 4 | name: "Near Obscured Puzzles" |
| 5 | path: "Panels/Halls/wb_1" | 5 | path: "Panels/Halls/wb_1" |
| 6 | clue: "" | 6 | clue: "" |
| 7 | answer: "sidewalk" | 7 | answer: "sidewalk" |
| diff --git a/data/maps/daedalus/rooms/Wonderland.txtpb b/data/maps/daedalus/rooms/Wonderland.txtpb index 4b69e99..ae9b3f1 100644 --- a/data/maps/daedalus/rooms/Wonderland.txtpb +++ b/data/maps/daedalus/rooms/Wonderland.txtpb | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | name: "Wonderland" | 1 | name: "Wonderland" |
| 2 | panel_display_name: "Northwest Area" | 2 | panel_display_name: "Northwest Area" |
| 3 | # TODO: There's a warp from The Entry into here. | ||
| 4 | panels { | 3 | panels { |
| 5 | name: "APRIL" | 4 | name: "APRIL" |
| 6 | path: "Panels/Wonderland/wonderland_1" | 5 | path: "Panels/Wonderland/wonderland_1" |
| diff --git a/data/maps/the_ancient/rooms/Outside.txtpb b/data/maps/the_ancient/rooms/Outside.txtpb index a3372af..1458357 100644 --- a/data/maps/the_ancient/rooms/Outside.txtpb +++ b/data/maps/the_ancient/rooms/Outside.txtpb | |||
| @@ -4,4 +4,5 @@ panels { | |||
| 4 | path: "Panels/panel_1" | 4 | path: "Panels/panel_1" |
| 5 | clue: "this" | 5 | clue: "this" |
| 6 | answer: "sphinx" | 6 | answer: "sphinx" |
| 7 | symbols: QUESTION | ||
| 7 | } | 8 | } |
| diff --git a/data/maps/the_bearer/connections.txtpb b/data/maps/the_bearer/connections.txtpb index 23410f0..ba14d83 100644 --- a/data/maps/the_bearer/connections.txtpb +++ b/data/maps/the_bearer/connections.txtpb | |||
| @@ -263,3 +263,8 @@ connections { | |||
| 263 | to_room: "Butterfly Room" | 263 | to_room: "Butterfly Room" |
| 264 | door { name: "Butterfly Entrance" } | 264 | door { name: "Butterfly Entrance" } |
| 265 | } | 265 | } |
| 266 | connections { | ||
| 267 | from_room: "Back Area" | ||
| 268 | to_room: "Tree Entrance" | ||
| 269 | door { name: "Control Center Brown Door" } | ||
| 270 | } | ||
| diff --git a/data/maps/the_bearer/rooms/Back Area.txtpb b/data/maps/the_bearer/rooms/Back Area.txtpb index 27e175c..b1860de 100644 --- a/data/maps/the_bearer/rooms/Back Area.txtpb +++ b/data/maps/the_bearer/rooms/Back Area.txtpb | |||
| @@ -7,12 +7,6 @@ panels { | |||
| 7 | symbols: EXAMPLE | 7 | symbols: EXAMPLE |
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "TREE" | ||
| 11 | path: "Components/Warps/worldport3" | ||
| 12 | orientation: "north" | ||
| 13 | required_door { name: "Control Center Brown Door" } | ||
| 14 | } | ||
| 15 | ports { | ||
| 16 | name: "DAEDALUS" | 10 | name: "DAEDALUS" |
| 17 | path: "Components/Warps/worldport2" | 11 | path: "Components/Warps/worldport2" |
| 18 | orientation: "north" | 12 | orientation: "north" |
| diff --git a/data/maps/the_bearer/rooms/Tree Entrance.txtpb b/data/maps/the_bearer/rooms/Tree Entrance.txtpb new file mode 100644 index 0000000..97a07da --- /dev/null +++ b/data/maps/the_bearer/rooms/Tree Entrance.txtpb | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | name: "Tree Entrance" | ||
| 2 | ports { | ||
| 3 | name: "TREE" | ||
| 4 | path: "Components/Warps/worldport3" | ||
| 5 | orientation: "north" | ||
| 6 | } | ||
| diff --git a/data/maps/the_darkroom/connections.txtpb b/data/maps/the_darkroom/connections.txtpb index 4093585..1b7ad05 100644 --- a/data/maps/the_darkroom/connections.txtpb +++ b/data/maps/the_darkroom/connections.txtpb | |||
| @@ -33,3 +33,18 @@ connections { | |||
| 33 | to_room: "S Room" | 33 | to_room: "S Room" |
| 34 | door { name: "S1 Door" } | 34 | door { name: "S1 Door" } |
| 35 | } | 35 | } |
| 36 | connections { | ||
| 37 | from_room: "First Room" | ||
| 38 | to_room: "Cyan Hallway" | ||
| 39 | door { name: "Colorful Entrance" } | ||
| 40 | } | ||
| 41 | connections { | ||
| 42 | from_room: "Second Room" | ||
| 43 | to_room: "Congruent Entrance" | ||
| 44 | door { name: "Congruent Entrance" } | ||
| 45 | } | ||
| 46 | connections { | ||
| 47 | from_room: "First Room" | ||
| 48 | to_room: "Double Sided Entrance" | ||
| 49 | door { name: "Double Sided Entrance" } | ||
| 50 | } | ||
| diff --git a/data/maps/the_darkroom/rooms/Congruent Entrance.txtpb b/data/maps/the_darkroom/rooms/Congruent Entrance.txtpb new file mode 100644 index 0000000..7ea1286 --- /dev/null +++ b/data/maps/the_darkroom/rooms/Congruent Entrance.txtpb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | name: "Congruent Entrance" | ||
| 2 | panel_display_name: "Second Room" | ||
| 3 | ports { | ||
| 4 | name: "CONGRUENT" | ||
| 5 | path: "Components/Warps/worldport7" | ||
| 6 | orientation: "east" | ||
| 7 | } | ||
| diff --git a/data/maps/the_darkroom/rooms/Cyan Hallway.txtpb b/data/maps/the_darkroom/rooms/Cyan Hallway.txtpb new file mode 100644 index 0000000..308efb1 --- /dev/null +++ b/data/maps/the_darkroom/rooms/Cyan Hallway.txtpb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | name: "Cyan Hallway" | ||
| 2 | panel_display_name: "First Room" | ||
| 3 | ports { | ||
| 4 | name: "COLORFUL" | ||
| 5 | path: "Components/Warps/worldport8" | ||
| 6 | orientation: "north" | ||
| 7 | } | ||
| diff --git a/data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb b/data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb new file mode 100644 index 0000000..9d25108 --- /dev/null +++ b/data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | name: "Double Sided Entrance" | ||
| 2 | panel_display_name: "First Room" | ||
| 3 | ports { | ||
| 4 | name: "DOUBLESIDED" | ||
| 5 | path: "Components/Warps/worldport6" | ||
| 6 | orientation: "east" | ||
| 7 | } | ||
| diff --git a/data/maps/the_darkroom/rooms/First Room.txtpb b/data/maps/the_darkroom/rooms/First Room.txtpb index c93f5b4..c635757 100644 --- a/data/maps/the_darkroom/rooms/First Room.txtpb +++ b/data/maps/the_darkroom/rooms/First Room.txtpb | |||
| @@ -42,15 +42,3 @@ ports { | |||
| 42 | orientation: "north" | 42 | orientation: "north" |
| 43 | required_door { name: "Second Room Entrance" } | 43 | required_door { name: "Second Room Entrance" } |
| 44 | } | 44 | } |
| 45 | ports { | ||
| 46 | name: "COLORFUL" | ||
| 47 | path: "Components/Warps/worldport8" | ||
| 48 | orientation: "north" | ||
| 49 | required_door { name: "Colorful Entrance" } | ||
| 50 | } | ||
| 51 | ports { | ||
| 52 | name: "DOUBLESIDED" | ||
| 53 | path: "Components/Warps/worldport6" | ||
| 54 | orientation: "east" | ||
| 55 | required_door { name: "Double Sided Entrance" } | ||
| 56 | } | ||
| diff --git a/data/maps/the_darkroom/rooms/Second Room.txtpb b/data/maps/the_darkroom/rooms/Second Room.txtpb index baeea12..a3964ea 100644 --- a/data/maps/the_darkroom/rooms/Second Room.txtpb +++ b/data/maps/the_darkroom/rooms/Second Room.txtpb | |||
| @@ -47,9 +47,3 @@ ports { | |||
| 47 | orientation: "north" | 47 | orientation: "north" |
| 48 | required_door { name: "Third Room Entrance" } | 48 | required_door { name: "Third Room Entrance" } |
| 49 | } | 49 | } |
| 50 | ports { | ||
| 51 | name: "CONGRUENT" | ||
| 52 | path: "Components/Warps/worldport7" | ||
| 53 | orientation: "east" | ||
| 54 | required_door { name: "Congruent Entrance" } | ||
| 55 | } | ||
| diff --git a/data/maps/the_entry/connections.txtpb b/data/maps/the_entry/connections.txtpb index a2e325a..ca0207e 100644 --- a/data/maps/the_entry/connections.txtpb +++ b/data/maps/the_entry/connections.txtpb | |||
| @@ -97,6 +97,12 @@ connections { | |||
| 97 | from_room: "Red Blue Halls" | 97 | from_room: "Red Blue Halls" |
| 98 | to_room: "Wrath Room" | 98 | to_room: "Wrath Room" |
| 99 | door { name: "Noon Door" } | 99 | door { name: "Noon Door" } |
| 100 | oneway: true | ||
| 101 | } | ||
| 102 | connections { | ||
| 103 | from_room: "Wrath Room" | ||
| 104 | to_room: "Least Blue Last" | ||
| 105 | oneway: true | ||
| 100 | } | 106 | } |
| 101 | connections { | 107 | connections { |
| 102 | from_room: "Red Blue Halls" | 108 | from_room: "Red Blue Halls" |
| @@ -199,3 +205,18 @@ connections { | |||
| 199 | to_room: "White Hallway To Daedalus" | 205 | to_room: "White Hallway To Daedalus" |
| 200 | door { name: "Control Center White Door" } | 206 | door { name: "Control Center White Door" } |
| 201 | } | 207 | } |
| 208 | connections { | ||
| 209 | from_room: "Flipped Second Room" | ||
| 210 | to_room: "Four Rooms Entrance" | ||
| 211 | door { name: "Flipped Second Room Right Door" } | ||
| 212 | } | ||
| 213 | connections { | ||
| 214 | from_room: "Link Area" | ||
| 215 | to_room: "Liberated Entrance" | ||
| 216 | door { name: "Liberated Entrance" } | ||
| 217 | } | ||
| 218 | connections { | ||
| 219 | from_room: "Link Area" | ||
| 220 | to_room: "Literate Entrance" | ||
| 221 | door { name: "Literate Entrance" } | ||
| 222 | } | ||
| diff --git a/data/maps/the_entry/doors.txtpb b/data/maps/the_entry/doors.txtpb index 6bef160..466f5ce 100644 --- a/data/maps/the_entry/doors.txtpb +++ b/data/maps/the_entry/doors.txtpb | |||
| @@ -137,8 +137,10 @@ doors { | |||
| 137 | type: STANDARD | 137 | type: STANDARD |
| 138 | receivers: "Components/Doors/back_left_2" | 138 | receivers: "Components/Doors/back_left_2" |
| 139 | panels { room: "Colored Doors Area" name: "OPEN" answer: "orange" } | 139 | panels { room: "Colored Doors Area" name: "OPEN" answer: "orange" } |
| 140 | # "wall" is supposed to also work. idk man | 140 | panels { room: "Colored Doors Area" name: "OPEN" answer: "wall" } |
| 141 | complete_at: 1 | ||
| 141 | location_room: "Colored Doors Area" | 142 | location_room: "Colored Doors Area" |
| 143 | location_name: "OPEN" | ||
| 142 | } | 144 | } |
| 143 | doors { | 145 | doors { |
| 144 | name: "Lime Room Entrance" | 146 | name: "Lime Room Entrance" |
| diff --git a/data/maps/the_entry/rooms/Flipped Second Room.txtpb b/data/maps/the_entry/rooms/Flipped Second Room.txtpb index 5841ca1..0d518bb 100644 --- a/data/maps/the_entry/rooms/Flipped Second Room.txtpb +++ b/data/maps/the_entry/rooms/Flipped Second Room.txtpb | |||
| @@ -21,10 +21,3 @@ paintings { | |||
| 21 | gravity: Y_PLUS | 21 | gravity: Y_PLUS |
| 22 | display_name: "Eye Painting" | 22 | display_name: "Eye Painting" |
| 23 | } | 23 | } |
| 24 | ports { | ||
| 25 | name: "FOUR" | ||
| 26 | path: "Components/Warps/worldport9" | ||
| 27 | orientation: "south" | ||
| 28 | gravity: Y_PLUS | ||
| 29 | required_door { name: "Flipped Second Room Right Door" } | ||
| 30 | } \ No newline at end of file | ||
| diff --git a/data/maps/the_entry/rooms/Four Rooms Entrance.txtpb b/data/maps/the_entry/rooms/Four Rooms Entrance.txtpb new file mode 100644 index 0000000..689d23e --- /dev/null +++ b/data/maps/the_entry/rooms/Four Rooms Entrance.txtpb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | name: "Four Rooms Entrance" | ||
| 2 | ports { | ||
| 3 | name: "FOUR" | ||
| 4 | path: "Components/Warps/worldport9" | ||
| 5 | orientation: "south" | ||
| 6 | gravity: Y_PLUS | ||
| 7 | } | ||
| diff --git a/data/maps/the_entry/rooms/Liberated Entrance.txtpb b/data/maps/the_entry/rooms/Liberated Entrance.txtpb new file mode 100644 index 0000000..f0176a0 --- /dev/null +++ b/data/maps/the_entry/rooms/Liberated Entrance.txtpb | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | name: "Liberated Entrance" | ||
| 2 | ports { | ||
| 3 | name: "BLUE" | ||
| 4 | path: "worldport8" | ||
| 5 | orientation: "west" | ||
| 6 | } | ||
| diff --git a/data/maps/the_entry/rooms/Link Area.txtpb b/data/maps/the_entry/rooms/Link Area.txtpb index 689f57a..5b68279 100644 --- a/data/maps/the_entry/rooms/Link Area.txtpb +++ b/data/maps/the_entry/rooms/Link Area.txtpb | |||
| @@ -26,15 +26,3 @@ paintings { | |||
| 26 | orientation: "south" | 26 | orientation: "south" |
| 27 | display_name: "Center Painting" | 27 | display_name: "Center Painting" |
| 28 | } | 28 | } |
| 29 | ports { | ||
| 30 | name: "BLUE" | ||
| 31 | path: "worldport8" | ||
| 32 | orientation: "west" | ||
| 33 | required_door { name: "Liberated Entrance" } | ||
| 34 | } | ||
| 35 | ports { | ||
| 36 | name: "BROWN" | ||
| 37 | path: "worldport9" | ||
| 38 | orientation: "east" | ||
| 39 | required_door { name: "Literate Entrance" } | ||
| 40 | } \ No newline at end of file | ||
| diff --git a/data/maps/the_entry/rooms/Literate Entrance.txtpb b/data/maps/the_entry/rooms/Literate Entrance.txtpb new file mode 100644 index 0000000..4ec402f --- /dev/null +++ b/data/maps/the_entry/rooms/Literate Entrance.txtpb | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | name: "Literate Entrance" | ||
| 2 | ports { | ||
| 3 | name: "BROWN" | ||
| 4 | path: "worldport9" | ||
| 5 | orientation: "east" | ||
| 6 | } | ||
| diff --git a/data/maps/the_entry/rooms/Shop Entrance.txtpb b/data/maps/the_entry/rooms/Shop Entrance.txtpb index f793da3..67aa6de 100644 --- a/data/maps/the_entry/rooms/Shop Entrance.txtpb +++ b/data/maps/the_entry/rooms/Shop Entrance.txtpb | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | name: "Shop Entrance" | 1 | name: "Shop Entrance" |
| 2 | panel_display_name: "Starting Room" | 2 | panel_display_name: "Shop Entrance" |
| 3 | panels { | 3 | panels { |
| 4 | name: "TURN" | 4 | name: "TURN" |
| 5 | path: "Panels/Entry/l_opener_2" | 5 | path: "Panels/Entry/l_opener_2" |
| diff --git a/data/maps/the_gallery/doors.txtpb b/data/maps/the_gallery/doors.txtpb index a7a5d85..adbc766 100644 --- a/data/maps/the_gallery/doors.txtpb +++ b/data/maps/the_gallery/doors.txtpb | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | # The Gallery is interesting because there's so many cross-map requirements. | 1 | # The Gallery is interesting because there's so many cross-map requirements. |
| 2 | doors { | 2 | doors { |
| 3 | name: "Darkroom Painting" | 3 | name: "Darkroom Painting" |
| 4 | type: ITEM_ONLY | 4 | type: GALLERY_PAINTING |
| 5 | #move_paintings { room: "Main Area" name: "DARKROOM" } | 5 | #move_paintings { room: "Main Area" name: "DARKROOM" } |
| 6 | receivers: "Components/Paintings/darkroom/teleportListener" | 6 | receivers: "Components/Paintings/darkroom/teleportListener" |
| 7 | panels { map: "the_darkroom" room: "First Room" name: "BISON" } | 7 | panels { map: "the_darkroom" room: "First Room" name: "BISON" } |
| @@ -27,14 +27,14 @@ doors { | |||
| 27 | } | 27 | } |
| 28 | doors { | 28 | doors { |
| 29 | name: "Butterfly Painting" | 29 | name: "Butterfly Painting" |
| 30 | type: ITEM_ONLY | 30 | type: GALLERY_PAINTING |
| 31 | #move_paintings { room: "Main Area" name: "BUTTERFLY" } | 31 | #move_paintings { room: "Main Area" name: "BUTTERFLY" } |
| 32 | receivers: "Components/Paintings/butterfly/teleportListener" | 32 | receivers: "Components/Paintings/butterfly/teleportListener" |
| 33 | rooms { map: "the_butterfly" name: "Main Area" } | 33 | rooms { map: "the_butterfly" name: "Main Area" } |
| 34 | } | 34 | } |
| 35 | doors { | 35 | doors { |
| 36 | name: "Between Painting" | 36 | name: "Between Painting" |
| 37 | type: ITEM_ONLY | 37 | type: GALLERY_PAINTING |
| 38 | #move_paintings { room: "Main Area" name: "BETWEEN" } | 38 | #move_paintings { room: "Main Area" name: "BETWEEN" } |
| 39 | receivers: "Components/Paintings/between/teleportListener" | 39 | receivers: "Components/Paintings/between/teleportListener" |
| 40 | panels { map: "the_between" room: "Main Area" name: "SUN" } | 40 | panels { map: "the_between" room: "Main Area" name: "SUN" } |
| @@ -70,14 +70,14 @@ doors { | |||
| 70 | } | 70 | } |
| 71 | doors { | 71 | doors { |
| 72 | name: "Entry Painting" | 72 | name: "Entry Painting" |
| 73 | type: ITEM_ONLY | 73 | type: GALLERY_PAINTING |
| 74 | #move_paintings { room: "Main Area" name: "ENTRY" } | 74 | #move_paintings { room: "Main Area" name: "ENTRY" } |
| 75 | receivers: "Components/Paintings/eyes/teleportListener" | 75 | receivers: "Components/Paintings/eyes/teleportListener" |
| 76 | panels { map: "the_entry" room: "Eye Room" name: "I" } | 76 | panels { map: "the_entry" room: "Eye Room" name: "I" } |
| 77 | } | 77 | } |
| 78 | doors { | 78 | doors { |
| 79 | name: "Wise Painting" | 79 | name: "Wise Painting" |
| 80 | type: ITEM_ONLY | 80 | type: GALLERY_PAINTING |
| 81 | #move_paintings { room: "Main Area" name: "WISE" } | 81 | #move_paintings { room: "Main Area" name: "WISE" } |
| 82 | receivers: "Components/Paintings/triangle/teleportListener" | 82 | receivers: "Components/Paintings/triangle/teleportListener" |
| 83 | panels { map: "the_wise" room: "Entry" name: "INK" } | 83 | panels { map: "the_wise" room: "Entry" name: "INK" } |
| @@ -105,7 +105,7 @@ doors { | |||
| 105 | } | 105 | } |
| 106 | doors { | 106 | doors { |
| 107 | name: "Tree Painting" | 107 | name: "Tree Painting" |
| 108 | type: ITEM_ONLY | 108 | type: GALLERY_PAINTING |
| 109 | #move_paintings { room: "Main Area" name: "TREE" } | 109 | #move_paintings { room: "Main Area" name: "TREE" } |
| 110 | receivers: "Components/Paintings/Clue Maps/tree/teleportListener" | 110 | receivers: "Components/Paintings/Clue Maps/tree/teleportListener" |
| 111 | panels { map: "the_tree" room: "Main Area" name: "COLOR" } | 111 | panels { map: "the_tree" room: "Main Area" name: "COLOR" } |
| @@ -142,35 +142,35 @@ doors { | |||
| 142 | } | 142 | } |
| 143 | doors { | 143 | doors { |
| 144 | name: "Unyielding Painting" | 144 | name: "Unyielding Painting" |
| 145 | type: ITEM_ONLY | 145 | type: GALLERY_PAINTING |
| 146 | #move_paintings { room: "Main Area" name: "UNYIELDING" } | 146 | #move_paintings { room: "Main Area" name: "UNYIELDING" } |
| 147 | receivers: "Components/Paintings/Clue Maps/unyielding/teleportListener" | 147 | receivers: "Components/Paintings/Clue Maps/unyielding/teleportListener" |
| 148 | rooms { map: "the_unyielding" name: "Digital Entrance" } | 148 | rooms { map: "the_unyielding" name: "Digital Entrance" } |
| 149 | } | 149 | } |
| 150 | doors { | 150 | doors { |
| 151 | name: "Graveyard Painting" | 151 | name: "Graveyard Painting" |
| 152 | type: ITEM_ONLY | 152 | type: GALLERY_PAINTING |
| 153 | #move_paintings { room: "Main Area" name: "GRAVEYARD" } | 153 | #move_paintings { room: "Main Area" name: "GRAVEYARD" } |
| 154 | receivers: "Components/Paintings/Endings/grave/teleportListener" | 154 | receivers: "Components/Paintings/Endings/grave/teleportListener" |
| 155 | rooms { map: "the_graveyard" name: "Outside" } | 155 | rooms { map: "the_graveyard" name: "Outside" } |
| 156 | } | 156 | } |
| 157 | doors { | 157 | doors { |
| 158 | name: "Control Center Painting" | 158 | name: "Control Center Painting" |
| 159 | type: ITEM_ONLY | 159 | type: GALLERY_PAINTING |
| 160 | #move_paintings { room: "Main Area" name: "CC" } | 160 | #move_paintings { room: "Main Area" name: "CC" } |
| 161 | receivers: "Components/Paintings/Endings/desert/teleportListener" | 161 | receivers: "Components/Paintings/Endings/desert/teleportListener" |
| 162 | rooms { map: "the_impressive" name: "M2 Room" } | 162 | rooms { map: "the_impressive" name: "M2 Room" } |
| 163 | } | 163 | } |
| 164 | doors { | 164 | doors { |
| 165 | name: "Tower Painting" | 165 | name: "Tower Painting" |
| 166 | type: ITEM_ONLY | 166 | type: GALLERY_PAINTING |
| 167 | #move_paintings { room: "Main Area" name: "TOWER" } | 167 | #move_paintings { room: "Main Area" name: "TOWER" } |
| 168 | receivers: "Components/Paintings/Endings/red/teleportListener" | 168 | receivers: "Components/Paintings/Endings/red/teleportListener" |
| 169 | rooms { map: "the_tower" name: "First Floor" } | 169 | rooms { map: "the_tower" name: "First Floor" } |
| 170 | } | 170 | } |
| 171 | doors { | 171 | doors { |
| 172 | name: "Wondrous Painting" | 172 | name: "Wondrous Painting" |
| 173 | type: ITEM_ONLY | 173 | type: GALLERY_PAINTING |
| 174 | #move_paintings { room: "Main Area" name: "WONDROUS" } | 174 | #move_paintings { room: "Main Area" name: "WONDROUS" } |
| 175 | receivers: "Components/Paintings/Endings/window/teleportListener" | 175 | receivers: "Components/Paintings/Endings/window/teleportListener" |
| 176 | panels { map: "the_wondrous" room: "Entry" name: "WONDER" } | 176 | panels { map: "the_wondrous" room: "Entry" name: "WONDER" } |
| @@ -187,42 +187,42 @@ doors { | |||
| 187 | } | 187 | } |
| 188 | doors { | 188 | doors { |
| 189 | name: "Rainbow Painting" | 189 | name: "Rainbow Painting" |
| 190 | type: ITEM_ONLY | 190 | type: GALLERY_PAINTING |
| 191 | #move_paintings { room: "Main Area" name: "RAINBOW" } | 191 | #move_paintings { room: "Main Area" name: "RAINBOW" } |
| 192 | receivers: "Components/Paintings/Endings/rainbow/teleportListener" | 192 | receivers: "Components/Paintings/Endings/rainbow/teleportListener" |
| 193 | rooms { map: "daedalus" name: "Rainbow Start" } | 193 | rooms { map: "daedalus" name: "Rainbow Start" } |
| 194 | } | 194 | } |
| 195 | doors { | 195 | doors { |
| 196 | name: "Words Painting" | 196 | name: "Words Painting" |
| 197 | type: ITEM_ONLY | 197 | type: GALLERY_PAINTING |
| 198 | #move_paintings { room: "Main Area" name: "WORDS" } | 198 | #move_paintings { room: "Main Area" name: "WORDS" } |
| 199 | receivers: "Components/Paintings/Endings/words/teleportListener" | 199 | receivers: "Components/Paintings/Endings/words/teleportListener" |
| 200 | rooms { map: "the_words" name: "Main Area" } | 200 | rooms { map: "the_words" name: "Main Area" } |
| 201 | } | 201 | } |
| 202 | doors { | 202 | doors { |
| 203 | name: "Colorful Painting" | 203 | name: "Colorful Painting" |
| 204 | type: ITEM_ONLY | 204 | type: GALLERY_PAINTING |
| 205 | #move_paintings { room: "Main Area" name: "COLORFUL" } | 205 | #move_paintings { room: "Main Area" name: "COLORFUL" } |
| 206 | receivers: "Components/Paintings/Endings/colorful/teleportListener" | 206 | receivers: "Components/Paintings/Endings/colorful/teleportListener" |
| 207 | rooms { map: "the_colorful" name: "White Room" } | 207 | rooms { map: "the_colorful" name: "White Room" } |
| 208 | } | 208 | } |
| 209 | doors { | 209 | doors { |
| 210 | name: "Castle Painting" | 210 | name: "Castle Painting" |
| 211 | type: ITEM_ONLY | 211 | type: GALLERY_PAINTING |
| 212 | #move_paintings { room: "Main Area" name: "CASTLE" } | 212 | #move_paintings { room: "Main Area" name: "CASTLE" } |
| 213 | receivers: "Components/Paintings/Endings/castle/teleportListener" | 213 | receivers: "Components/Paintings/Endings/castle/teleportListener" |
| 214 | rooms { map: "daedalus" name: "Castle" } | 214 | rooms { map: "daedalus" name: "Castle" } |
| 215 | } | 215 | } |
| 216 | doors { | 216 | doors { |
| 217 | name: "Sun Temple Painting" | 217 | name: "Sun Temple Painting" |
| 218 | type: ITEM_ONLY | 218 | type: GALLERY_PAINTING |
| 219 | #move_paintings { room: "Main Area" name: "SUNTEMPLE" } | 219 | #move_paintings { room: "Main Area" name: "SUNTEMPLE" } |
| 220 | receivers: "Components/Paintings/Endings/temple/teleportListener" | 220 | receivers: "Components/Paintings/Endings/temple/teleportListener" |
| 221 | rooms { map: "the_sun_temple" name: "Entrance" } | 221 | rooms { map: "the_sun_temple" name: "Entrance" } |
| 222 | } | 222 | } |
| 223 | doors { | 223 | doors { |
| 224 | name: "Ancient Painting" | 224 | name: "Ancient Painting" |
| 225 | type: ITEM_ONLY | 225 | type: GALLERY_PAINTING |
| 226 | #move_paintings { room: "Main Area" name: "ANCIENT" } | 226 | #move_paintings { room: "Main Area" name: "ANCIENT" } |
| 227 | receivers: "Components/Paintings/Endings/cubes/teleportListener" | 227 | receivers: "Components/Paintings/Endings/cubes/teleportListener" |
| 228 | rooms { map: "the_ancient" name: "Outside" } | 228 | rooms { map: "the_ancient" name: "Outside" } |
| diff --git a/data/maps/the_great/doors.txtpb b/data/maps/the_great/doors.txtpb index 5d0e90d..abbbc11 100644 --- a/data/maps/the_great/doors.txtpb +++ b/data/maps/the_great/doors.txtpb | |||
| @@ -66,7 +66,7 @@ doors { | |||
| 66 | doors { | 66 | doors { |
| 67 | name: "Control Center Red Door" | 67 | name: "Control Center Red Door" |
| 68 | type: CONTROL_CENTER_COLOR | 68 | type: CONTROL_CENTER_COLOR |
| 69 | receivers: "Components/Doors/Gates/Gate" | 69 | receivers: "Components/Doors/entry_18" |
| 70 | control_center_color: "red" | 70 | control_center_color: "red" |
| 71 | } | 71 | } |
| 72 | doors { | 72 | doors { |
| diff --git a/data/maps/the_nuanced/doors.txtpb b/data/maps/the_nuanced/doors.txtpb index 9b58001..cd29766 100644 --- a/data/maps/the_nuanced/doors.txtpb +++ b/data/maps/the_nuanced/doors.txtpb | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | doors { | 1 | doors { |
| 2 | name: "Left Room Puzzles" | 2 | name: "Blue Side Puzzles" |
| 3 | type: LOCATION_ONLY | 3 | type: LOCATION_ONLY |
| 4 | panels { room: "Main Room" name: "HOARSE" } | 4 | panels { room: "Main Room" name: "HOARSE" } |
| 5 | panels { room: "Main Room" name: "NAY" } | 5 | panels { room: "Main Room" name: "NAY" } |
| @@ -11,7 +11,7 @@ doors { | |||
| 11 | location_room: "Main Room" | 11 | location_room: "Main Room" |
| 12 | } | 12 | } |
| 13 | doors { | 13 | doors { |
| 14 | name: "Right Room Puzzles" | 14 | name: "Green Side Puzzles" |
| 15 | type: LOCATION_ONLY | 15 | type: LOCATION_ONLY |
| 16 | panels { room: "Main Room" name: "HOSE" } | 16 | panels { room: "Main Room" name: "HOSE" } |
| 17 | panels { room: "Main Room" name: "NIGH" } | 17 | panels { room: "Main Room" name: "NIGH" } |
| diff --git a/data/maps/the_parthenon/connections.txtpb b/data/maps/the_parthenon/connections.txtpb index a07d858..331ac66 100644 --- a/data/maps/the_parthenon/connections.txtpb +++ b/data/maps/the_parthenon/connections.txtpb | |||
| @@ -7,6 +7,7 @@ connections { | |||
| 7 | from_room: "Main Area" | 7 | from_room: "Main Area" |
| 8 | to_room: "Ending" | 8 | to_room: "Ending" |
| 9 | door { name: "Ending Door" } | 9 | door { name: "Ending Door" } |
| 10 | cyan_ending: true | ||
| 10 | } | 11 | } |
| 11 | connections { | 12 | connections { |
| 12 | from_room: "Main Area" | 13 | from_room: "Main Area" |
| diff --git a/data/maps/the_plaza/connections.txtpb b/data/maps/the_plaza/connections.txtpb index 44586be..6da201c 100644 --- a/data/maps/the_plaza/connections.txtpb +++ b/data/maps/the_plaza/connections.txtpb | |||
| @@ -21,22 +21,22 @@ connections { | |||
| 21 | connections { | 21 | connections { |
| 22 | from_room: "Center Room" | 22 | from_room: "Center Room" |
| 23 | to_room: "Top Left Room" | 23 | to_room: "Top Left Room" |
| 24 | door { name: "Top Left Door" } | 24 | door { name: "Northwest Door" } |
| 25 | } | 25 | } |
| 26 | connections { | 26 | connections { |
| 27 | from_room: "Center Room" | 27 | from_room: "Center Room" |
| 28 | to_room: "Top Right Room" | 28 | to_room: "Top Right Room" |
| 29 | door { name: "Top Right Door" } | 29 | door { name: "Northeast Door" } |
| 30 | } | 30 | } |
| 31 | connections { | 31 | connections { |
| 32 | from_room: "Center Room" | 32 | from_room: "Center Room" |
| 33 | to_room: "Bottom Left Room" | 33 | to_room: "Bottom Left Room" |
| 34 | door { name: "Bottom Left Door" } | 34 | door { name: "Southwest Door" } |
| 35 | } | 35 | } |
| 36 | connections { | 36 | connections { |
| 37 | from_room: "Center Room" | 37 | from_room: "Center Room" |
| 38 | to_room: "Bottom Right Room" | 38 | to_room: "Bottom Right Room" |
| 39 | door { name: "Bottom Right Door" } | 39 | door { name: "Southeast Door" } |
| 40 | } | 40 | } |
| 41 | connections { | 41 | connections { |
| 42 | from_room: "Center Room" | 42 | from_room: "Center Room" |
| diff --git a/data/maps/the_plaza/doors.txtpb b/data/maps/the_plaza/doors.txtpb index 322fe39..d95273c 100644 --- a/data/maps/the_plaza/doors.txtpb +++ b/data/maps/the_plaza/doors.txtpb | |||
| @@ -31,7 +31,7 @@ doors { | |||
| 31 | location_room: "Main Area" | 31 | location_room: "Main Area" |
| 32 | } | 32 | } |
| 33 | doors { | 33 | doors { |
| 34 | name: "Top Left Door" | 34 | name: "Northwest Door" |
| 35 | type: STANDARD | 35 | type: STANDARD |
| 36 | receivers: "Components/Doors/entry_6" | 36 | receivers: "Components/Doors/entry_6" |
| 37 | panels { room: "Center Room" name: "REPORTER" } | 37 | panels { room: "Center Room" name: "REPORTER" } |
| @@ -44,7 +44,7 @@ doors { | |||
| 44 | location_name: "First Room" | 44 | location_name: "First Room" |
| 45 | } | 45 | } |
| 46 | doors { | 46 | doors { |
| 47 | name: "Top Right Door" | 47 | name: "Northeast Door" |
| 48 | type: ITEM_ONLY | 48 | type: ITEM_ONLY |
| 49 | receivers: "Components/Doors/entry_7" | 49 | receivers: "Components/Doors/entry_7" |
| 50 | panels { room: "Center Room" name: "REPORTER" } | 50 | panels { room: "Center Room" name: "REPORTER" } |
| @@ -55,7 +55,7 @@ doors { | |||
| 55 | panels { room: "Center Room" name: "SQUIRREL" } | 55 | panels { room: "Center Room" name: "SQUIRREL" } |
| 56 | } | 56 | } |
| 57 | doors { | 57 | doors { |
| 58 | name: "Bottom Left Door" | 58 | name: "Southwest Door" |
| 59 | type: ITEM_ONLY | 59 | type: ITEM_ONLY |
| 60 | receivers: "Components/Doors/entry_5" | 60 | receivers: "Components/Doors/entry_5" |
| 61 | panels { room: "Center Room" name: "REPORTER" } | 61 | panels { room: "Center Room" name: "REPORTER" } |
| @@ -66,7 +66,7 @@ doors { | |||
| 66 | panels { room: "Center Room" name: "SQUIRREL" } | 66 | panels { room: "Center Room" name: "SQUIRREL" } |
| 67 | } | 67 | } |
| 68 | doors { | 68 | doors { |
| 69 | name: "Bottom Right Door" | 69 | name: "Southeast Door" |
| 70 | type: ITEM_ONLY | 70 | type: ITEM_ONLY |
| 71 | receivers: "Components/Doors/entry_4" | 71 | receivers: "Components/Doors/entry_4" |
| 72 | panels { room: "Center Room" name: "REPORTER" } | 72 | panels { room: "Center Room" name: "REPORTER" } |
| @@ -77,7 +77,7 @@ doors { | |||
| 77 | panels { room: "Center Room" name: "SQUIRREL" } | 77 | panels { room: "Center Room" name: "SQUIRREL" } |
| 78 | } | 78 | } |
| 79 | doors { | 79 | doors { |
| 80 | name: "Top Left Puzzles" | 80 | name: "Northwest Puzzles" |
| 81 | type: LOCATION_ONLY | 81 | type: LOCATION_ONLY |
| 82 | panels { room: "Top Left Room" name: "BARE SOD" } | 82 | panels { room: "Top Left Room" name: "BARE SOD" } |
| 83 | panels { room: "Top Left Room" name: "SOD" } | 83 | panels { room: "Top Left Room" name: "SOD" } |
| @@ -104,7 +104,7 @@ doors { | |||
| 104 | location_room: "Top Left Room" | 104 | location_room: "Top Left Room" |
| 105 | } | 105 | } |
| 106 | doors { | 106 | doors { |
| 107 | name: "Top Right Puzzles" | 107 | name: "Northeast Puzzles" |
| 108 | type: LOCATION_ONLY | 108 | type: LOCATION_ONLY |
| 109 | panels { room: "Top Right Room" name: "RIGHT WING" } | 109 | panels { room: "Top Right Room" name: "RIGHT WING" } |
| 110 | panels { room: "Top Right Room" name: "WING" } | 110 | panels { room: "Top Right Room" name: "WING" } |
| @@ -130,7 +130,7 @@ doors { | |||
| 130 | location_room: "Top Right Room" | 130 | location_room: "Top Right Room" |
| 131 | } | 131 | } |
| 132 | doors { | 132 | doors { |
| 133 | name: "Bottom Left Puzzles" | 133 | name: "Southwest Puzzles" |
| 134 | type: LOCATION_ONLY | 134 | type: LOCATION_ONLY |
| 135 | panels { room: "Bottom Left Room" name: "SHELL (1)" } | 135 | panels { room: "Bottom Left Room" name: "SHELL (1)" } |
| 136 | panels { room: "Bottom Left Room" name: "SHELL (2)" } | 136 | panels { room: "Bottom Left Room" name: "SHELL (2)" } |
| @@ -141,7 +141,7 @@ doors { | |||
| 141 | location_room: "Bottom Left Room" | 141 | location_room: "Bottom Left Room" |
| 142 | } | 142 | } |
| 143 | doors { | 143 | doors { |
| 144 | name: "Bottom Right Puzzles" | 144 | name: "Southeast Puzzles" |
| 145 | type: LOCATION_ONLY | 145 | type: LOCATION_ONLY |
| 146 | panels { room: "Bottom Right Room" name: "FLY" } | 146 | panels { room: "Bottom Right Room" name: "FLY" } |
| 147 | panels { room: "Bottom Right Room" name: "DECLOG" } | 147 | panels { room: "Bottom Right Room" name: "DECLOG" } |
| diff --git a/data/maps/the_repetitive/doors.txtpb b/data/maps/the_repetitive/doors.txtpb index 8171dc4..d964928 100644 --- a/data/maps/the_repetitive/doors.txtpb +++ b/data/maps/the_repetitive/doors.txtpb | |||
| @@ -194,3 +194,9 @@ doors { | |||
| 194 | panels { room: "Yellow Room" name: "ASSESSES" } | 194 | panels { room: "Yellow Room" name: "ASSESSES" } |
| 195 | panels { room: "Yellow Room" name: "TINTING" } | 195 | panels { room: "Yellow Room" name: "TINTING" } |
| 196 | } | 196 | } |
| 197 | doors { | ||
| 198 | name: "Anti-Collectable" | ||
| 199 | type: LOCATION_ONLY | ||
| 200 | senders: "Components/Collectables/anticollectable" | ||
| 201 | location_room: "Anti Room" | ||
| 202 | } | ||
| diff --git a/data/maps/the_repetitive/metadata.txtpb b/data/maps/the_repetitive/metadata.txtpb index 6f5c459..76a0f50 100644 --- a/data/maps/the_repetitive/metadata.txtpb +++ b/data/maps/the_repetitive/metadata.txtpb | |||
| @@ -1,10 +1,6 @@ | |||
| 1 | display_name: "The Repetitive" | 1 | display_name: "The Repetitive" |
| 2 | # The anti-collectable doesn't fit into our system right now so let's ignore it. | ||
| 3 | excluded_nodes: "Components/Collectables/anticollectable" | ||
| 4 | # These paintings are directly above/behind panels and thus can't be entered. | 2 | # These paintings are directly above/behind panels and thus can't be entered. |
| 5 | excluded_nodes: "Meshes/eyeRed3" | 3 | excluded_nodes: "Meshes/eyeRed3" |
| 6 | excluded_nodes: "Meshes/eyeRed4" | 4 | excluded_nodes: "Meshes/eyeRed4" |
| 7 | # I do not know what this is. | ||
| 8 | excluded_nodes: "Components/Doors/Door3/Hinge/panel_i" | ||
| 9 | # This has something to do with the magenta room entrance proxy panel. | 5 | # This has something to do with the magenta room entrance proxy panel. |
| 10 | excluded_nodes: "Panels/Eval/panel_26_proxyied_fake" | 6 | excluded_nodes: "Panels/Eval/panel_26_proxyied_fake" |
| diff --git a/data/maps/the_repetitive/rooms/Anti Room.txtpb b/data/maps/the_repetitive/rooms/Anti Room.txtpb index 641fede..65a99ff 100644 --- a/data/maps/the_repetitive/rooms/Anti Room.txtpb +++ b/data/maps/the_repetitive/rooms/Anti Room.txtpb | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | name: "Anti Room" | 1 | name: "Anti Room" |
| 2 | # Ignore the collectible. The mod should remove it and the back wall too. | ||
| 3 | panels { | 2 | panels { |
| 4 | name: "HA (1)" | 3 | name: "HA (1)" |
| 5 | path: "Panels/Entry/panel_7" | 4 | path: "Panels/Entry/panel_7" |
| @@ -38,9 +37,17 @@ panels { | |||
| 38 | symbols: EXAMPLE | 37 | symbols: EXAMPLE |
| 39 | } | 38 | } |
| 40 | panels { | 39 | panels { |
| 41 | name: "EYE" | 40 | name: "EYE (1)" |
| 42 | path: "Panels/Entry/panel4" | 41 | path: "Panels/Entry/panel4" |
| 43 | clue: "eye" | 42 | clue: "eye" |
| 44 | answer: "iris" | 43 | answer: "iris" |
| 45 | symbols: BOXES | 44 | symbols: BOXES |
| 46 | } | 45 | } |
| 46 | panels { | ||
| 47 | # This appears after grabbing the anti-collectable. | ||
| 48 | name: "EYE (2)" | ||
| 49 | path: "Components/Doors/Door3/Hinge/panel_i" | ||
| 50 | clue: "eye" | ||
| 51 | answer: "i" | ||
| 52 | symbols: ZERO | ||
| 53 | } | ||
| diff --git a/data/maps/the_sun_temple/connections.txtpb b/data/maps/the_sun_temple/connections.txtpb index b0b3a0a..ffe4d5d 100644 --- a/data/maps/the_sun_temple/connections.txtpb +++ b/data/maps/the_sun_temple/connections.txtpb | |||
| @@ -7,6 +7,7 @@ connections { | |||
| 7 | from_room: "Temple" | 7 | from_room: "Temple" |
| 8 | to_room: "Ending" | 8 | to_room: "Ending" |
| 9 | door { name: "Ending" } | 9 | door { name: "Ending" } |
| 10 | purple_ending: true | ||
| 10 | } | 11 | } |
| 11 | connections { | 12 | connections { |
| 12 | from_room: "Temple" | 13 | from_room: "Temple" |
| diff --git a/data/maps/the_unkempt/connections.txtpb b/data/maps/the_unkempt/connections.txtpb index a9e30db..d4a046c 100644 --- a/data/maps/the_unkempt/connections.txtpb +++ b/data/maps/the_unkempt/connections.txtpb | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | connections { | 1 | connections { |
| 2 | from_room: "Main Area" | 2 | from_room: "Main Area" |
| 3 | to_room: "Right Area" | 3 | to_room: "Right Area" |
| 4 | door { name: "Right Door" } | 4 | door { name: "East Door" } |
| 5 | } | 5 | } |
| 6 | connections { | 6 | connections { |
| 7 | from_room: "Middle Room" | 7 | from_room: "Middle Room" |
| diff --git a/data/maps/the_unkempt/doors.txtpb b/data/maps/the_unkempt/doors.txtpb index 2349913..29065ec 100644 --- a/data/maps/the_unkempt/doors.txtpb +++ b/data/maps/the_unkempt/doors.txtpb | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | doors { | 1 | doors { |
| 2 | name: "Right Door" | 2 | name: "East Door" |
| 3 | type: STANDARD | 3 | type: STANDARD |
| 4 | receivers: "Components/Doors/entry_2" | 4 | receivers: "Components/Doors/entry_2" |
| 5 | panels { room: "Main Area" name: "EYE" } | 5 | panels { room: "Main Area" name: "EYE" } |
| diff --git a/data/metadata.txtpb b/data/metadata.txtpb index d0b3229..b7ba807 100644 --- a/data/metadata.txtpb +++ b/data/metadata.txtpb | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | version: 1 | 1 | version: 6 |
| 2 | # Filler item. | 2 | # Filler item. |
| 3 | special_names: "A Job Well Done" | 3 | special_names: "A Job Well Done" |
| 4 | # Symbol items. | 4 | # Symbol items. |
| @@ -21,3 +21,30 @@ special_names: "Stars Symbol" | |||
| 21 | special_names: "Sun Symbol" | 21 | special_names: "Sun Symbol" |
| 22 | special_names: "Sweet Symbol" | 22 | special_names: "Sweet Symbol" |
| 23 | special_names: "Zero Symbol" | 23 | special_names: "Zero Symbol" |
| 24 | # Anti collectable traps | ||
| 25 | special_names: "Anti A" | ||
| 26 | special_names: "Anti B" | ||
| 27 | special_names: "Anti C" | ||
| 28 | special_names: "Anti D" | ||
| 29 | special_names: "Anti E" | ||
| 30 | special_names: "Anti F" | ||
| 31 | special_names: "Anti G" | ||
| 32 | special_names: "Anti H" | ||
| 33 | special_names: "Anti I" | ||
| 34 | special_names: "Anti J" | ||
| 35 | special_names: "Anti K" | ||
| 36 | special_names: "Anti L" | ||
| 37 | special_names: "Anti M" | ||
| 38 | special_names: "Anti N" | ||
| 39 | special_names: "Anti O" | ||
| 40 | special_names: "Anti P" | ||
| 41 | special_names: "Anti Q" | ||
| 42 | special_names: "Anti R" | ||
| 43 | special_names: "Anti S" | ||
| 44 | special_names: "Anti T" | ||
| 45 | special_names: "Anti U" | ||
| 46 | special_names: "Anti V" | ||
| 47 | special_names: "Anti W" | ||
| 48 | special_names: "Anti X" | ||
| 49 | special_names: "Anti Y" | ||
| 50 | special_names: "Anti Z" | ||
| diff --git a/proto/data.proto b/proto/data.proto index 827e639..64e3ddc 100644 --- a/proto/data.proto +++ b/proto/data.proto | |||
| @@ -27,6 +27,9 @@ enum DoorType { | |||
| 27 | 27 | ||
| 28 | // This door is an item if gravestone shuffle is enabled, and is a location as long as panelsanity is not on. | 28 | // This door is an item if gravestone shuffle is enabled, and is a location as long as panelsanity is not on. |
| 29 | GRAVESTONE = 6; | 29 | GRAVESTONE = 6; |
| 30 | |||
| 31 | // This door is never a location, and is an item as long as gallery painting shuffle is on. | ||
| 32 | GALLERY_PAINTING = 7; | ||
| 30 | } | 33 | } |
| 31 | 34 | ||
| 32 | enum DoorGroupType { | 35 | enum DoorGroupType { |
| @@ -106,6 +109,8 @@ message Connection { | |||
| 106 | } | 109 | } |
| 107 | 110 | ||
| 108 | optional bool roof_access = 7; | 111 | optional bool roof_access = 7; |
| 112 | optional bool purple_ending = 8; | ||
| 113 | optional bool cyan_ending = 9; | ||
| 109 | } | 114 | } |
| 110 | 115 | ||
| 111 | message Door { | 116 | message Door { |
| @@ -127,6 +132,7 @@ message Door { | |||
| 127 | repeated uint64 doors = 15; | 132 | repeated uint64 doors = 15; |
| 128 | repeated uint64 endings = 16; | 133 | repeated uint64 endings = 16; |
| 129 | optional bool double_letters = 18; | 134 | optional bool double_letters = 18; |
| 135 | repeated string senders = 19; | ||
| 130 | 136 | ||
| 131 | optional DoorType type = 8; | 137 | optional DoorType type = 8; |
| 132 | 138 | ||
| diff --git a/proto/human.proto b/proto/human.proto index e5335e7..c247edf 100644 --- a/proto/human.proto +++ b/proto/human.proto | |||
| @@ -66,6 +66,18 @@ message HumanConnection { | |||
| 66 | // If true, this connection will only be logically allowed if the Daedalus | 66 | // If true, this connection will only be logically allowed if the Daedalus |
| 67 | // Roof Access option is enabled. | 67 | // Roof Access option is enabled. |
| 68 | optional bool roof_access = 7; | 68 | optional bool roof_access = 7; |
| 69 | |||
| 70 | // This means that the connection intentionally skips the target object's | ||
| 71 | // required door. | ||
| 72 | optional bool bypass_target_door = 8; | ||
| 73 | |||
| 74 | // This means that the connection should additionally require all purple | ||
| 75 | // letters when the Strict Purple Ending option is on. | ||
| 76 | optional bool purple_ending = 9; | ||
| 77 | |||
| 78 | // This means that the connection should additionally require all cyan letters | ||
| 79 | // when the Strict Cyan Ending option is on. | ||
| 80 | optional bool cyan_ending = 10; | ||
| 69 | } | 81 | } |
| 70 | 82 | ||
| 71 | message HumanConnections { | 83 | message HumanConnections { |
| @@ -92,6 +104,10 @@ message HumanDoor { | |||
| 92 | repeated string endings = 13; | 104 | repeated string endings = 13; |
| 93 | optional bool double_letters = 15; | 105 | optional bool double_letters = 15; |
| 94 | 106 | ||
| 107 | // Sender nodes to be added to the list of requirements for triggering the | ||
| 108 | // location. Only for senders that have no logic requirements. | ||
| 109 | repeated string senders = 16; | ||
| 110 | |||
| 95 | optional DoorType type = 4; | 111 | optional DoorType type = 4; |
| 96 | optional string location_room = 5; | 112 | optional string location_room = 5; |
| 97 | optional string location_name = 14; | 113 | optional string location_name = 14; |
| diff --git a/tools/datapacker/main.cpp b/tools/datapacker/main.cpp index 6bbb461..596259b 100644 --- a/tools/datapacker/main.cpp +++ b/tools/datapacker/main.cpp | |||
| @@ -345,6 +345,9 @@ class DataPacker { | |||
| 345 | std::copy( | 345 | std::copy( |
| 346 | h_door.receivers().begin(), h_door.receivers().end(), | 346 | h_door.receivers().begin(), h_door.receivers().end(), |
| 347 | google::protobuf::RepeatedFieldBackInserter(door.mutable_receivers())); | 347 | google::protobuf::RepeatedFieldBackInserter(door.mutable_receivers())); |
| 348 | std::copy( | ||
| 349 | h_door.senders().begin(), h_door.senders().end(), | ||
| 350 | google::protobuf::RepeatedFieldBackInserter(door.mutable_senders())); | ||
| 348 | 351 | ||
| 349 | for (const PaintingIdentifier& pi : h_door.move_paintings()) { | 352 | for (const PaintingIdentifier& pi : h_door.move_paintings()) { |
| 350 | std::optional<std::string> map_name = | 353 | std::optional<std::string> map_name = |
| @@ -470,6 +473,16 @@ class DataPacker { | |||
| 470 | r_connection.set_roof_access(human_connection.roof_access()); | 473 | r_connection.set_roof_access(human_connection.roof_access()); |
| 471 | } | 474 | } |
| 472 | 475 | ||
| 476 | if (human_connection.has_purple_ending()) { | ||
| 477 | f_connection.set_purple_ending(human_connection.purple_ending()); | ||
| 478 | r_connection.set_purple_ending(human_connection.purple_ending()); | ||
| 479 | } | ||
| 480 | |||
| 481 | if (human_connection.has_cyan_ending()) { | ||
| 482 | f_connection.set_cyan_ending(human_connection.cyan_ending()); | ||
| 483 | r_connection.set_cyan_ending(human_connection.cyan_ending()); | ||
| 484 | } | ||
| 485 | |||
| 473 | container_.AddConnection(f_connection); | 486 | container_.AddConnection(f_connection); |
| 474 | if (!human_connection.oneway()) { | 487 | if (!human_connection.oneway()) { |
| 475 | container_.AddConnection(r_connection); | 488 | container_.AddConnection(r_connection); |
| diff --git a/tools/validator/human_processor.cpp b/tools/validator/human_processor.cpp index 561225e..2c978bf 100644 --- a/tools/validator/human_processor.cpp +++ b/tools/validator/human_processor.cpp | |||
| @@ -394,7 +394,9 @@ class HumanProcessor { | |||
| 394 | } | 394 | } |
| 395 | } else if (human_connection.has_from()) { | 395 | } else if (human_connection.has_from()) { |
| 396 | ProcessSingleConnection(human_connection, human_connection.from(), | 396 | ProcessSingleConnection(human_connection, human_connection.from(), |
| 397 | current_map_name); | 397 | current_map_name, |
| 398 | /*is_target=*/!human_connection.oneway() && | ||
| 399 | !human_connection.bypass_target_door()); | ||
| 398 | } | 400 | } |
| 399 | 401 | ||
| 400 | if (human_connection.has_to_room()) { | 402 | if (human_connection.has_to_room()) { |
| @@ -410,8 +412,9 @@ class HumanProcessor { | |||
| 410 | std::cout << "A global connection used to_room." << std::endl; | 412 | std::cout << "A global connection used to_room." << std::endl; |
| 411 | } | 413 | } |
| 412 | } else if (human_connection.has_to()) { | 414 | } else if (human_connection.has_to()) { |
| 413 | ProcessSingleConnection(human_connection, human_connection.to(), | 415 | ProcessSingleConnection( |
| 414 | current_map_name); | 416 | human_connection, human_connection.to(), current_map_name, |
| 417 | /*is_target=*/!human_connection.bypass_target_door()); | ||
| 415 | } | 418 | } |
| 416 | 419 | ||
| 417 | if (human_connection.has_door()) { | 420 | if (human_connection.has_door()) { |
| @@ -432,7 +435,7 @@ class HumanProcessor { | |||
| 432 | void ProcessSingleConnection( | 435 | void ProcessSingleConnection( |
| 433 | const HumanConnection& human_connection, | 436 | const HumanConnection& human_connection, |
| 434 | const HumanConnection::Endpoint& endpoint, | 437 | const HumanConnection::Endpoint& endpoint, |
| 435 | const std::optional<std::string>& current_map_name) { | 438 | const std::optional<std::string>& current_map_name, bool is_target) { |
| 436 | if (endpoint.has_room()) { | 439 | if (endpoint.has_room()) { |
| 437 | auto room_identifier = | 440 | auto room_identifier = |
| 438 | GetCompleteRoomIdentifier(endpoint.room(), current_map_name); | 441 | GetCompleteRoomIdentifier(endpoint.room(), current_map_name); |
| @@ -451,6 +454,11 @@ class HumanProcessor { | |||
| 451 | if (painting_identifier) { | 454 | if (painting_identifier) { |
| 452 | PaintingInfo& painting_info = info_.paintings[*painting_identifier]; | 455 | PaintingInfo& painting_info = info_.paintings[*painting_identifier]; |
| 453 | painting_info.connections_referenced_by.push_back(human_connection); | 456 | painting_info.connections_referenced_by.push_back(human_connection); |
| 457 | |||
| 458 | if (is_target) { | ||
| 459 | painting_info.target_connections_referenced_by.push_back( | ||
| 460 | human_connection); | ||
| 461 | } | ||
| 454 | } else { | 462 | } else { |
| 455 | // Not sure where else to store this right now. | 463 | // Not sure where else to store this right now. |
| 456 | std::cout | 464 | std::cout |
| @@ -463,6 +471,11 @@ class HumanProcessor { | |||
| 463 | if (port_identifier) { | 471 | if (port_identifier) { |
| 464 | PortInfo& port_info = info_.ports[*port_identifier]; | 472 | PortInfo& port_info = info_.ports[*port_identifier]; |
| 465 | port_info.connections_referenced_by.push_back(human_connection); | 473 | port_info.connections_referenced_by.push_back(human_connection); |
| 474 | |||
| 475 | if (is_target) { | ||
| 476 | port_info.target_connections_referenced_by.push_back( | ||
| 477 | human_connection); | ||
| 478 | } | ||
| 466 | } else { | 479 | } else { |
| 467 | // Not sure where else to store this right now. | 480 | // Not sure where else to store this right now. |
| 468 | std::cout | 481 | std::cout |
| @@ -480,6 +493,11 @@ class HumanProcessor { | |||
| 480 | panel_info.proxies[endpoint.panel().answer()] | 493 | panel_info.proxies[endpoint.panel().answer()] |
| 481 | .connections_referenced_by.push_back(human_connection); | 494 | .connections_referenced_by.push_back(human_connection); |
| 482 | } | 495 | } |
| 496 | |||
| 497 | if (is_target) { | ||
| 498 | panel_info.target_connections_referenced_by.push_back( | ||
| 499 | human_connection); | ||
| 500 | } | ||
| 483 | } | 501 | } |
| 484 | } | 502 | } |
| 485 | } | 503 | } |
| diff --git a/tools/validator/structs.h b/tools/validator/structs.h index 17ed33a..d1d45f2 100644 --- a/tools/validator/structs.h +++ b/tools/validator/structs.h | |||
| @@ -56,12 +56,14 @@ struct PortInfo { | |||
| 56 | std::vector<HumanPort> definitions; | 56 | std::vector<HumanPort> definitions; |
| 57 | 57 | ||
| 58 | std::vector<HumanConnection> connections_referenced_by; | 58 | std::vector<HumanConnection> connections_referenced_by; |
| 59 | std::vector<HumanConnection> target_connections_referenced_by; | ||
| 59 | }; | 60 | }; |
| 60 | 61 | ||
| 61 | struct PaintingInfo { | 62 | struct PaintingInfo { |
| 62 | std::vector<HumanPainting> definitions; | 63 | std::vector<HumanPainting> definitions; |
| 63 | 64 | ||
| 64 | std::vector<HumanConnection> connections_referenced_by; | 65 | std::vector<HumanConnection> connections_referenced_by; |
| 66 | std::vector<HumanConnection> target_connections_referenced_by; | ||
| 65 | std::vector<DoorIdentifier> doors_referenced_by; | 67 | std::vector<DoorIdentifier> doors_referenced_by; |
| 66 | }; | 68 | }; |
| 67 | 69 | ||
| @@ -79,6 +81,7 @@ struct PanelInfo { | |||
| 79 | std::string map_area_name; | 81 | std::string map_area_name; |
| 80 | 82 | ||
| 81 | std::vector<HumanConnection> connections_referenced_by; | 83 | std::vector<HumanConnection> connections_referenced_by; |
| 84 | std::vector<HumanConnection> target_connections_referenced_by; | ||
| 82 | std::vector<DoorIdentifier> doors_referenced_by; | 85 | std::vector<DoorIdentifier> doors_referenced_by; |
| 83 | 86 | ||
| 84 | std::map<std::string, ProxyInfo> proxies; | 87 | std::map<std::string, ProxyInfo> proxies; |
| diff --git a/tools/validator/validator.cpp b/tools/validator/validator.cpp index 4149caa..dd41f5c 100644 --- a/tools/validator/validator.cpp +++ b/tools/validator/validator.cpp | |||
| @@ -106,7 +106,8 @@ class Validator { | |||
| 106 | return false; | 106 | return false; |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | if (h_door.keyholders_size() > 0 || h_door.endings_size() > 0) { | 109 | if (h_door.keyholders_size() > 0 || h_door.endings_size() > 0 || |
| 110 | h_door.complete_at() > 0) { | ||
| 110 | return true; | 111 | return true; |
| 111 | } | 112 | } |
| 112 | 113 | ||
| @@ -256,6 +257,22 @@ class Validator { | |||
| 256 | std::cout << "Port " << port_identifier.ShortDebugString() | 257 | std::cout << "Port " << port_identifier.ShortDebugString() |
| 257 | << " was defined multiple times." << std::endl; | 258 | << " was defined multiple times." << std::endl; |
| 258 | } | 259 | } |
| 260 | |||
| 261 | if (!port_info.target_connections_referenced_by.empty()) { | ||
| 262 | for (const HumanPort& port : port_info.definitions) { | ||
| 263 | if (port.has_required_door()) { | ||
| 264 | std::cout << "Port " << port_identifier.ShortDebugString() | ||
| 265 | << " has a required door but is the target of a connection:" | ||
| 266 | << std::endl; | ||
| 267 | |||
| 268 | for (const HumanConnection& connection : | ||
| 269 | port_info.target_connections_referenced_by) { | ||
| 270 | std::cout << " CONNECTION " << connection.ShortDebugString() | ||
| 271 | << std::endl; | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | } | ||
| 259 | } | 276 | } |
| 260 | 277 | ||
| 261 | void ValidatePainting(const PaintingIdentifier& painting_identifier, | 278 | void ValidatePainting(const PaintingIdentifier& painting_identifier, |
| @@ -279,6 +296,22 @@ class Validator { | |||
| 279 | std::cout << "Painting " << painting_identifier.ShortDebugString() | 296 | std::cout << "Painting " << painting_identifier.ShortDebugString() |
| 280 | << " was defined multiple times." << std::endl; | 297 | << " was defined multiple times." << std::endl; |
| 281 | } | 298 | } |
| 299 | |||
| 300 | if (!painting_info.target_connections_referenced_by.empty()) { | ||
| 301 | for (const HumanPainting& painting : painting_info.definitions) { | ||
| 302 | if (painting.has_required_door()) { | ||
| 303 | std::cout << "Painting " << painting_identifier.ShortDebugString() | ||
| 304 | << " has a required door but is the target of a connection:" | ||
| 305 | << std::endl; | ||
| 306 | |||
| 307 | for (const HumanConnection& connection : | ||
| 308 | painting_info.target_connections_referenced_by) { | ||
| 309 | std::cout << " CONNECTION " << connection.ShortDebugString() | ||
| 310 | << std::endl; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | } | ||
| 314 | } | ||
| 282 | } | 315 | } |
| 283 | 316 | ||
| 284 | void ValidatePanel(const PanelIdentifier& panel_identifier, | 317 | void ValidatePanel(const PanelIdentifier& panel_identifier, |
| @@ -340,6 +373,22 @@ class Validator { | |||
| 340 | std::cout << "Panel " << panel_identifier.ShortDebugString() | 373 | std::cout << "Panel " << panel_identifier.ShortDebugString() |
| 341 | << " is missing an AP ID." << std::endl; | 374 | << " is missing an AP ID." << std::endl; |
| 342 | } | 375 | } |
| 376 | |||
| 377 | if (!panel_info.target_connections_referenced_by.empty()) { | ||
| 378 | for (const HumanPanel& panel : panel_info.definitions) { | ||
| 379 | if (panel.has_required_door()) { | ||
| 380 | std::cout << "Panel " << panel_identifier.ShortDebugString() | ||
| 381 | << " has a required door but is the target of a connection:" | ||
| 382 | << std::endl; | ||
| 383 | |||
| 384 | for (const HumanConnection& connection : | ||
| 385 | panel_info.target_connections_referenced_by) { | ||
| 386 | std::cout << " CONNECTION " << connection.ShortDebugString() | ||
| 387 | << std::endl; | ||
| 388 | } | ||
| 389 | } | ||
| 390 | } | ||
| 391 | } | ||
| 343 | } | 392 | } |
| 344 | 393 | ||
| 345 | void ValidateKeyholder(const KeyholderIdentifier& keyholder_identifier, | 394 | void ValidateKeyholder(const KeyholderIdentifier& keyholder_identifier, |
