diff options
431 files changed, 17050 insertions, 2481 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..578ad9d --- /dev/null +++ b/CHANGELOG.md | |||
| @@ -0,0 +1,307 @@ | |||
| 1 | # lingo2-archipelago Releases | ||
| 2 | |||
| 3 | ## v8.0.1 - 2025-11-05 | ||
| 4 | |||
| 5 | - Fixed issue where The Unkempt - COLOR would disappear when it became logical | ||
| 6 | to solve it. It is now always present, and does not logically require cyan | ||
| 7 | doors or the orange control center door. | ||
| 8 | - Fixed issue where you would be expected to solve The Owl - COLOR while it is | ||
| 9 | invisible. It is now considered a cyan door. | ||
| 10 | |||
| 11 | Compatability notes: | ||
| 12 | |||
| 13 | - This client should overall be compatible with worlds generated on v8.0.0. The | ||
| 14 | tracker will show the new logic for The Unkempt - COLOR, which reflects when | ||
| 15 | it is solvable in game, but may place it in an earlier sphere than it was in | ||
| 16 | the original generation. Conversely, The Owl - COLOR is now more restricted in | ||
| 17 | the new logic, so you may end up in a situation where you are required to | ||
| 18 | solve it but the tracker does not realize it is in logic. However, the panel | ||
| 19 | is simply invisible when you don't have cyan doors, meaning it is easy to | ||
| 20 | solve it early if necessary. | ||
| 21 | |||
| 22 | Download: | ||
| 23 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.1/lingo2.apworld)<br/> | ||
| 24 | Template YAML: | ||
| 25 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.1/Lingo%202.yaml)<br/> | ||
| 26 | Source: [v8.0.1](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.0.1) | ||
| 27 | |||
| 28 | ## v8.0.0 - 2025-11-02 | ||
| 29 | |||
| 30 | - ~50 new locations were added, such that almost every panel in the game is now | ||
| 31 | part of either a location or a connection. Some existing locations were also | ||
| 32 | modified or removed to make this cleaner. The only panels that are not part of | ||
| 33 | any location or connection are the ones in rooms that you are explicitly not | ||
| 34 | supposed to solve (e.g. the letter rooms in Daedalus where you are only | ||
| 35 | supposed to solve the panels indicated by the ceiling). | ||
| 36 | - Multiworld state is now saved in a way that should increase compatibility when | ||
| 37 | playing on a client that is a different version than the apworld that | ||
| 38 | generated the world, going forward. Complete compatibility is not guaranteed, | ||
| 39 | but this fixes some of the glaring issues. Also note that this client is _not_ | ||
| 40 | compatible with worlds generated on v7.x.x. | ||
| 41 | - Hinted locations are now prioritized in the tracker, and are displayed in a | ||
| 42 | different color. | ||
| 43 | - Locations can now be ignored in the tracker, which removes them from the | ||
| 44 | overlay and puts them at the bottom of the tab in the text client. | ||
| 45 | - Fixed the Yellow Ending door not opening properly when gallery paintings are | ||
| 46 | shuffled. | ||
| 47 | - The trigger for the gallery painting in The Unyielding is now smaller, so that | ||
| 48 | it matches the logical requirement. | ||
| 49 | - The DIRECTION panels near the Castle in Daedalus are now moved when the roof | ||
| 50 | access stairs are present, so that you don't lose access to them. | ||
| 51 | |||
| 52 | Download: | ||
| 53 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.0/lingo2.apworld)<br/> | ||
| 54 | Template YAML: | ||
| 55 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.0/Lingo%202.yaml)<br/> | ||
| 56 | Source: [v8.0.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.0.0) | ||
| 57 | |||
| 58 | ## v7.2.0 - 2025-10-25 | ||
| 59 | |||
| 60 | - Doors that rely on keyholders or the control center color panel are now | ||
| 61 | "latched". This means they will not close once they've been opened. Because of | ||
| 62 | this, the worldports near these doors are now eligible for randomization in | ||
| 63 | worldport shuffle. | ||
| 64 | - Icarus is now optionally randomizable. | ||
| 65 | - The requirements for accessing White Ending are now customizable. You can | ||
| 66 | choose to require a number of endings as well as a number of masteries. | ||
| 67 | - The "Return To" trigger in The Plaza is now outside of the turtle. | ||
| 68 | - Fixed a logic error regarding a couple of specific doors in vanilla doors | ||
| 69 | mode. | ||
| 70 | - Fixed a bug where unlocks would not persist if you were playing with all | ||
| 71 | letters pre-unlocked and cyan doors on "any double letter". | ||
| 72 | - Fixed a bug where the client would fail to connect properly when launched from | ||
| 73 | a URL if the player name had spaces in it. | ||
| 74 | |||
| 75 | Download: | ||
| 76 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.2.0/lingo2.apworld)<br/> | ||
| 77 | Template YAML: | ||
| 78 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.2.0/Lingo%202.yaml)<br/> | ||
| 79 | Source: [v7.2.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.2.0) | ||
| 80 | |||
| 81 | ## v7.1.0 - 2025-10-07 | ||
| 82 | |||
| 83 | - Added a "Get Path" button to the locations tracker. This shows you the path | ||
| 84 | you're currently expected to be able to take in order to reach that | ||
| 85 | location/worldport/goal. | ||
| 86 | - Worldport names in the spoiler log have been changed to be more descriptive. | ||
| 87 | - Jumping into The Graveyard from The Sun Temple is now in logic. | ||
| 88 | - Solving the FLIP panels above the Liberated and Literate entrances by looking | ||
| 89 | up is now in logic. | ||
| 90 | - Renamed some locations so that they're shorter. | ||
| 91 | - Fixed bug where White Ending would kick the player out of Archipelago. | ||
| 92 | - Fixed bug where the minimap would be completely white when a texture pack is | ||
| 93 | enabled. | ||
| 94 | - Generation failures while shuffling worldports should be significantly less | ||
| 95 | common. | ||
| 96 | |||
| 97 | Download: | ||
| 98 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.1.0/lingo2.apworld)<br/> | ||
| 99 | Template YAML: | ||
| 100 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.1.0/Lingo%202.yaml)<br/> | ||
| 101 | Source: [v7.1.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.1.0) | ||
| 102 | |||
| 103 | ## v7.0.2 - 2025-10-03 | ||
| 104 | |||
| 105 | - Fixed issue connecting to password-protected slots. | ||
| 106 | - Added instructions for using the client on Linux. | ||
| 107 | |||
| 108 | Download: | ||
| 109 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.2/lingo2.apworld)<br/> | ||
| 110 | Template YAML: | ||
| 111 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.2/Lingo%202.yaml)<br/> | ||
| 112 | Source: [v7.0.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.0.2) | ||
| 113 | |||
| 114 | ## v7.0.1 - 2025-10-01 | ||
| 115 | |||
| 116 | - Fixed logic error regarding the Plaza Entrance in The Repetitive. Going from | ||
| 117 | The Plaza to The Repetitive does not require the door to be open in vanilla | ||
| 118 | doors, but both directions require the door item when doors are shuffled. | ||
| 119 | - Fixed Worldports tracker tab getting messed up after disconnecting and | ||
| 120 | reconnecting to multiworld. | ||
| 121 | - Improved error messages when failing to connect. The game now also shows you | ||
| 122 | when your connection has dropped. | ||
| 123 | |||
| 124 | Download: | ||
| 125 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.1/lingo2.apworld)<br/> | ||
| 126 | Template YAML: | ||
| 127 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.1/Lingo%202.yaml)<br/> | ||
| 128 | Source: [v7.0.1](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.0.1) | ||
| 129 | |||
| 130 | ## v7.0.0 - 2025-09-30 | ||
| 131 | |||
| 132 | - Major update! First and foremost: the client and apworld are no longer | ||
| 133 | separate! There is only an apworld now, which you install into your | ||
| 134 | Archipelago custom worlds folder as per normal. In order to play a randomized | ||
| 135 | world, you open the Archipelago Launcher and click Lingo 2 Client. The first | ||
| 136 | time you do this, it will ask you for the location of your Lingo2.exe, which | ||
| 137 | you can find by right clicking on the game in Steam and clicking Browse Local | ||
| 138 | Files. | ||
| 139 | - **Built-in tracker**: The in-game text client has a new tab called "Locations" | ||
| 140 | which lists the currently accessible locations similar to how Universal | ||
| 141 | Tracker does it. There is also an optional overlay you can enable in the | ||
| 142 | settings, which shows you some of your accessible locations on screen while | ||
| 143 | you're playing. If you're playing with vanilla letters, the tracker won't show | ||
| 144 | you locations that are solvable with letters until you collect the actual | ||
| 145 | letters, in order to help direct you better. More features will be coming to | ||
| 146 | the tracker in the future! | ||
| 147 | - **Worldport shuffle**: The first part of entrance randomization is here! | ||
| 148 | Enabling this shuffles the destinations of the worldports, which are the | ||
| 149 | loading zones you walk into in order to change maps. Some restrictions apply, | ||
| 150 | which are noted in the option description. The tracker will show a list of | ||
| 151 | worldports you haven't entered yet, and will also not show you what lies | ||
| 152 | beyond a worldport until you've entered it. There is also a tab in the | ||
| 153 | textclient showing you the mapping between worldports you've already entered. | ||
| 154 | - **Minimap**: There is an option in the settings to show a minimap in the | ||
| 155 | corner of the screen. This shows an overhead view of the map you're on, and | ||
| 156 | where you are in it. More features will be coming to the minimap in the | ||
| 157 | future! | ||
| 158 | - Fixed the gate outside the Daedalus entrance in The Great not opening when | ||
| 159 | control center colors are shuffled. | ||
| 160 | |||
| 161 | Download: | ||
| 162 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.0/lingo2.apworld)<br/> | ||
| 163 | Template YAML: | ||
| 164 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.0/Lingo%202.yaml)<br/> | ||
| 165 | Source: [v7.0.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.0.0) | ||
| 166 | |||
| 167 | ## Legacy Client v6.7 - 2025-09-19 | ||
| 168 | |||
| 169 | - Added a compass overlay. This makes it clearer which direction corresponds to | ||
| 170 | which compass direction, which is useful since many location/item names | ||
| 171 | reference compass directions. It can be enabled in the settings screen on the | ||
| 172 | pause menu. | ||
| 173 | - Compatability update for the changes in v6.6 of the apworld. | ||
| 174 | |||
| 175 | Download: | ||
| 176 | [lingo2-archipelago-client-v6.7.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v6.7.zip)<br/> | ||
| 177 | Source: | ||
| 178 | [v6.7](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v6.7) | ||
| 179 | |||
| 180 | ## Legacy Apworld v6.6 - 2025-09-19 | ||
| 181 | |||
| 182 | - Added options that make the requirements for Purple Ending and Cyan Ending | ||
| 183 | stricter. With the strict options on, players are required to have all purple | ||
| 184 | (level 1) letters in order to get Purple Ending, and all cyan (level 2) | ||
| 185 | letters to get Cyan Ending. These options are on by default. | ||
| 186 | - Renamed several items and locations, mostly regarding changing relative | ||
| 187 | directions (left, right, etc) to compass directions. The colored SMILE panels | ||
| 188 | in Daedalus now have clearer names too. | ||
| 189 | - Fixed some minor logic errors. | ||
| 190 | |||
| 191 | Download: | ||
| 192 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v6.6/lingo2.apworld)<br/> | ||
| 193 | Template YAML: | ||
| 194 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v6.6/Lingo%202.yaml)<br/> | ||
| 195 | Source: | ||
| 196 | [v6.6](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v6.6) | ||
| 197 | |||
| 198 | ## Legacy Client v5.6 - 2025-09-17 | ||
| 199 | |||
| 200 | - Letter locations will no longer reappear after being collected. | ||
| 201 | - This also prevents a potential scenario in which it is impossible to access | ||
| 202 | the location "The Congruent - Obverse Yellow Puzzles" when door shuffle is | ||
| 203 | disabled. | ||
| 204 | |||
| 205 | Download: | ||
| 206 | [lingo2-archipelago-client-v5.6.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.6.zip)<br/> | ||
| 207 | Source: | ||
| 208 | [v5.6](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v5.6) | ||
| 209 | |||
| 210 | ## Legacy Client v5.5 - 2025-09-16 | ||
| 211 | |||
| 212 | - Compatability update for v5.5 of the apworld. | ||
| 213 | |||
| 214 | Download: | ||
| 215 | [lingo2-archipelago-client-v5.5.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.5.zip)<br/> | ||
| 216 | Source: | ||
| 217 | [v5.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v5.5) | ||
| 218 | |||
| 219 | ## Legacy Apworld v5.5 - 2025-09-16 | ||
| 220 | |||
| 221 | - Fixed a panel in The Ancient that was missing a symbol. | ||
| 222 | - Fixed an issue where you could be expected to get S1 in The Darkroom without | ||
| 223 | having U. | ||
| 224 | - Renamed a few locations. | ||
| 225 | |||
| 226 | Download: | ||
| 227 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/lingo2.apworld)<br/> | ||
| 228 | Template YAML: | ||
| 229 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/Lingo%202.yaml)<br/> | ||
| 230 | Source: | ||
| 231 | [v5.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v5.5) | ||
| 232 | |||
| 233 | ## Legacy Apworld v4.4 - 2025-09-14 | ||
| 234 | |||
| 235 | - Fixed panel set location names. | ||
| 236 | |||
| 237 | Download: | ||
| 238 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/lingo2.apworld)<br/> | ||
| 239 | Template YAML: | ||
| 240 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/Lingo%202.yaml)<br/> | ||
| 241 | Source: | ||
| 242 | [v4.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v4.4) | ||
| 243 | |||
| 244 | ## Legacy Client v4.4 - 2025-09-13 | ||
| 245 | |||
| 246 | - Added support for anti-collectable trap items. | ||
| 247 | - Fixed entrance to The Jubilant not opening properly when using control center | ||
| 248 | color shuffle. | ||
| 249 | - Fixed the location "The Entry (Colored Doors Area) - OPEN" not sending. | ||
| 250 | - Fixed level 2 letters not activating properly when letter shuffle is set to | ||
| 251 | Item Cyan. | ||
| 252 | - Messages are now cleared out when returning to the main menu. | ||
| 253 | - The player is prevented from accidentally breaking roof access logic when | ||
| 254 | returning to Daedalus from Icarus. | ||
| 255 | |||
| 256 | Download: | ||
| 257 | [lingo2-archipelago-client-v4.4.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v4.4.zip)<br/> | ||
| 258 | Source: | ||
| 259 | [v4.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v4.4) | ||
| 260 | |||
| 261 | ## Legacy Apworld v4.3 - 2025-09-13 | ||
| 262 | |||
| 263 | - Added a location for the anti-collectable in The Repetitive. | ||
| 264 | - Added trap items. These remove letters from your keyboard until you use the | ||
| 265 | Key Return in The Entry, similar to the anti-collectable in The Repetitive. | ||
| 266 | This can be controlled using the `trap_percentage` option, which defaults to | ||
| 267 | zero. | ||
| 268 | - Fixed crash on load when using Python 3.11. | ||
| 269 | |||
| 270 | Download: | ||
| 271 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/lingo2.apworld)<br/> | ||
| 272 | Template YAML: | ||
| 273 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/Lingo%202.yaml)<br/> | ||
| 274 | Source: | ||
| 275 | [v4.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v4.3) | ||
| 276 | |||
| 277 | ## Legacy Client v3.3 - 2025-09-12 | ||
| 278 | |||
| 279 | - Fixed issue downloading large datapackages (such as TUNIC's). | ||
| 280 | - Connection failures now show error messages. | ||
| 281 | |||
| 282 | Download: | ||
| 283 | [lingo2-archipelago-client-v3.3.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.3.zip)<br/> | ||
| 284 | Source: | ||
| 285 | [v3.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v3.3) | ||
| 286 | |||
| 287 | ## Legacy Client v3.2 - 2025-09-12 | ||
| 288 | |||
| 289 | - Initial release for testing. Features include door shuffle, letter shuffle, | ||
| 290 | and symbol shuffle. | ||
| 291 | |||
| 292 | Download: | ||
| 293 | [lingo2-archipelago-client-v3.2.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.2.zip)<br/> | ||
| 294 | Source: | ||
| 295 | [v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v3.2) | ||
| 296 | |||
| 297 | ## Legacy Apworld v3.2 - 2025-09-12 | ||
| 298 | |||
| 299 | - Initial release for testing. Features include door shuffle, letter shuffle, | ||
| 300 | and symbol shuffle. | ||
| 301 | |||
| 302 | Download: | ||
| 303 | [lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/lingo2.apworld)<br/> | ||
| 304 | Template YAML: | ||
| 305 | [Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/Lingo%202.yaml)<br/> | ||
| 306 | Source: | ||
| 307 | [v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v3.2) | ||
| diff --git a/README.md b/README.md new file mode 100644 index 0000000..24e9fa2 --- /dev/null +++ b/README.md | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | # lingo2-archipelago | ||
| 2 | |||
| 3 | [Archipelago](https://archipelago.gg/) is an open-source project that supports | ||
| 4 | randomizing a number of different games and combining them into one cooperative | ||
| 5 | experience. Items from each game are hidden in other games. For more information | ||
| 6 | about Archipelago, you can look at their website. | ||
| 7 | |||
| 8 | This is a project that modifies the game | ||
| 9 | [Lingo 2](https://www.lingothegame.com/lingo2.html) so that it can be played as | ||
| 10 | part of an Archipelago multiworld game. | ||
| 11 | |||
| 12 | ## Installation | ||
| 13 | |||
| 14 | 1. Download the Lingo 2 Apworld from | ||
| 15 | [the releases page](https://code.fourisland.com/lingo2-archipelago/about/CHANGELOG.md). | ||
| 16 | 2. If you do not already have it, download and install the | ||
| 17 | [Archipelago software](https://github.com/ArchipelagoMW/Archipelago/releases/). | ||
| 18 | 3. Double click on `lingo2.apworld` to install it, or copy it manually to the | ||
| 19 | `custom_worlds` folder of your Archipelago installation. | ||
| 20 | |||
| 21 | ## Joining a Multiworld game (Windows) | ||
| 22 | |||
| 23 | 1. Open the Archipelago Launcher. | ||
| 24 | 2. Select "Lingo 2 Client". | ||
| 25 | 3. The first time you do this, Archipelago will prompt you for the location of | ||
| 26 | the Lingo 2 executable file ("Lingo2.exe"). You can find this by | ||
| 27 | right-clicking on Lingo 2 in Steam, going to "Manage", and clicking "Browse | ||
| 28 | local files". | ||
| 29 | 4. Lingo 2 will open, and you will see a form asking for your connection | ||
| 30 | details. Enter the Archipelago address, slot name, and password into the | ||
| 31 | fields. | ||
| 32 | 5. Press Connect. | ||
| 33 | 6. Enjoy! | ||
| 34 | |||
| 35 | To continue an earlier game, you can perform the exact same steps as above. | ||
| 36 | |||
| 37 | ## Joining a Multiworld game (Non-Windows) | ||
| 38 | |||
| 39 | Lingo 2 only officially supports Windows, but has been known to work on Linux | ||
| 40 | using Proton. Archipelago can be played on a non-Windows system, but the process | ||
| 41 | is a little more complex. | ||
| 42 | |||
| 43 | 1. Download | ||
| 44 | [archipelago.tscn](https://code.fourisland.com/lingo2-archipelago/plain/client/archipelago.tscn) | ||
| 45 | and put it in your custom maps folder. You only have to do this once. | ||
| 46 | 2. Open Lingo 2, and select Archipelago from the level selection list. | ||
| 47 | 3. Put the path to your `lingo2.apworld` into the field provided. You only have | ||
| 48 | to do this once, as the game will remember what you put in. | ||
| 49 | 4. Click Start and wait for the connection settings screen to load. | ||
| 50 | 5. Open the Archipelago Launcher. | ||
| 51 | 6. Select "Lingo 2 Client". | ||
| 52 | 7. You should see "Connected to Lingo 2!" You can then return to Lingo 2 and | ||
| 53 | fill out your connection details. | ||
| 54 | 8. Press Connect. | ||
| 55 | 9. Enjoy! | ||
| 56 | |||
| 57 | ## Frequently Asked Questions | ||
| 58 | |||
| 59 | ### Why aren't the starting room letters shuffled? | ||
| 60 | |||
| 61 | The letter requirements for solving puzzles are very restrictive, especially in | ||
| 62 | the early game. It is possible for the generator to find some subset of letters | ||
| 63 | and doors to place in the starting room such that you are not trapped, but this | ||
| 64 | places a lot of strain on generation and leads to significantly more generation | ||
| 65 | failures. | ||
| 66 | |||
| 67 | As a result, the starting room letters (H1, I1, N1, and T1) are always present | ||
| 68 | in the starting room, even when remote letter shuffle is enabled. These letters | ||
| 69 | will _also_ count as clearing a check, so you will send out another item at the | ||
| 70 | same time as collecting the letter. | ||
| 71 | |||
| 72 | ### What areas are randomized? | ||
| 73 | |||
| 74 | Almost all maps that you can access from the base game are randomized. The only | ||
| 75 | exception is The Hinterlands, which will probably be repurposed into a hint | ||
| 76 | area. Some advanced/hidden maps are also disabled by default (as discussed | ||
| 77 | below). | ||
| 78 | |||
| 79 | ### Is my progress saved locally? | ||
| 80 | |||
| 81 | Lingo 2 autosaves your progress every time you solve a puzzle, get a | ||
| 82 | collectable, or interact with a keyholder. The randomizer generates a savefile | ||
| 83 | name based on your Multiworld seed and slot number, so you should be able to | ||
| 84 | seamlessly switch between multiworlds and even slots within a multiworld. | ||
| 85 | |||
| 86 | The exception to this is different rooms created from the same multiworld seed. | ||
| 87 | The client is unable to tell rooms in a seed apart (this is a limitation of the | ||
| 88 | Archipelago API), so the client will use the same save file for the same slot in | ||
| 89 | different rooms on the same seed. You can work around this by manually moving or | ||
| 90 | removing the save folder from the users directory in Lingo 2's game files. | ||
| 91 | |||
| 92 | If you play the base game again, you will see one or more save files with a long | ||
| 93 | name that begins with "zzAP\_". These are the saves for your multiworlds. They | ||
| 94 | can be safely deleted after you have completed the associated multiworld. It is | ||
| 95 | not recommended to load these save files outside of the randomizer. | ||
| 96 | |||
| 97 | A connection to Archipelago is required to resume playing a multiworld. This is | ||
| 98 | because the set of items you have received is not stored locally. | ||
| 99 | |||
| 100 | ### What about wall snipes? | ||
| 101 | |||
| 102 | "Wall sniping" refers to the fact that you are able to solve puzzles on the | ||
| 103 | other side of opaque walls. The player is never expected to or required to do | ||
| 104 | this in normal gameplay. This randomizer does not change how wall snipes work, | ||
| 105 | but it will likewise never require the use of them. | ||
| 106 | |||
| 107 | ### How do cyan doors work? | ||
| 108 | |||
| 109 | In the base game, there are a number of cyan-colored doors that ordinarily open | ||
| 110 | once you collect H2 in The Repetitive. There are also a handful of panels that | ||
| 111 | only appear upon getting H2 as well, which the apworld treats the same as the | ||
| 112 | cyan doors. | ||
| 113 | |||
| 114 | There is an option that lets you choose how these doors and panels behave. By | ||
| 115 | default, they act the same as in the base game: they only open or appear after | ||
| 116 | collecting H2. Note that this means the actual H2 collectable in The Repetitive. | ||
| 117 | Receiving H2 via remote letter shuffle does not count for this requirement. | ||
| 118 | However, you can also make cyan doors activate upon collecting or receiving your | ||
| 119 | first double letter, regardless of what it is or if it's remote. Finally, you | ||
| 120 | can lock cyan doors behind an item called "Cyan Doors". | ||
| 121 | |||
| 122 | It is important to note, however, that the Cyan Door Behavior option only | ||
| 123 | applies to cyan doors that are not already affected by another type of | ||
| 124 | shuffling. When door shuffle is on, the following cyan doors are activated by | ||
| 125 | individual items and are not impacted by your choice of Cyan Door Behavior: | ||
| 126 | |||
| 127 | - The entrance to The Tower from The Great (The Great - Tower Entrance) | ||
| 128 | - The entrance to The Butterfly from The Bearer (The Bearer - Butterfly | ||
| 129 | Entrance) | ||
| 130 | - The entrance to The Repetitive from The Entry (The Entry - Repetitive | ||
| 131 | Entrance) | ||
| 132 | - The eye painting near the yellow color hallway in Daedalus (Daedalus - Eye | ||
| 133 | Painting) | ||
| 134 | - The Red I room in The Repetitive (The Repetitive - Anti Collectable Room) | ||
| 135 | |||
| 136 | Additionally, when control center color shuffle is enabled, the orange door in | ||
| 137 | The Unkempt (which ordinarily doubles as a cyan door) opens upon receiving the | ||
| 138 | Control Center Orange Doors item, instead of following the Cyan Door Behavior | ||
| 139 | option. | ||
| 140 | |||
| 141 | ### Help! I lost C/G in The Congruent! | ||
| 142 | |||
| 143 | If you place C or G into the relevant keyholders in The Congruent, the keyholder | ||
| 144 | disappears. You can retrieve your letter immediately by pressing C or G again | ||
| 145 | before leaving solve mode, as the keyholder will still be considered to be | ||
| 146 | "focused", even though it has moved. If you have already moved, though, there is | ||
| 147 | another way to get your letters back: just use the Key Return in The Entry. | ||
| 148 | |||
| 149 | ### Why is the tracker telling me to solve a panel that's currently red? | ||
| 150 | |||
| 151 | Red usually indicates that a panel cannot be solved because of missing letters. | ||
| 152 | However, that only applies to the puzzle's main answer. If a puzzle has | ||
| 153 | alternate answers, you may be expected to use one of those instead of the main | ||
| 154 | one. As long as you have all of the necessary letters, an alternate answer can | ||
| 155 | be typed into a red panel even though it does not show you typing. When you | ||
| 156 | finish typing the answer, the panel will solve as normal. | ||
| 157 | |||
| 158 | ### Why does the tracker say "The Entry (Colored Doors Area) - OPEN" is in logic? | ||
| 159 | |||
| 160 | This is an infamous panel, both in the base game and in the randomizer. There | ||
| 161 | are _two_ valid answers that open the door / clear the location. These are | ||
| 162 | "ORANGE" and "WALL". | ||
| 163 | |||
| 164 | ### I can't solve the COLORS panel in The Sturdy! | ||
| 165 | |||
| 166 | The Sturdy contains a rainbow painting that leads to the Gold Ending area in | ||
| 167 | Daedalus. There are three ways to spawn this painting, which have different | ||
| 168 | logical requirements: | ||
| 169 | |||
| 170 | - Solve the COLORS panel that appears after collecting S2. This is the most | ||
| 171 | well-known way, and causes the most confusion because you may be expected to | ||
| 172 | enter the painting even if you are unable to solve the panel (e.g. if you are | ||
| 173 | missing letters or missing Boxes Symbol). | ||
| 174 | - Solve the panels in the order that you walk across the colors on the way | ||
| 175 | toward S2: Magenta, Red, Orange, Yellow, Green, Blue, Purple, Cyan. This has | ||
| 176 | the same logic as accessing S2. | ||
| 177 | - Type "MOVE" into the Green and Yellow panels, and none of the other ones. This | ||
| 178 | is a subset of the logic for accessing S2, so you may actually be expected to | ||
| 179 | use the rainbow painting before you can even collect S2. | ||
| 180 | |||
| 181 | ### How does Icarus work? | ||
| 182 | |||
| 183 | While Icarus is easily accessible during normal play, it is not randomized by | ||
| 184 | default. The main reason for this is that Icarus employs significantly more use | ||
| 185 | of gravity changing mechanics than the rest of the game and as a result tends to | ||
| 186 | cause motion sickness in a lot of players. It is also an infamously confusing | ||
| 187 | area to navigate. | ||
| 188 | |||
| 189 | Because of this, the player may enter and exit Icarus from the usual place in | ||
| 190 | Daedalus, but it will not contain any locations, and no items will be added to | ||
| 191 | the pool for it. The worldport will not be included in the randomization if | ||
| 192 | worldport shuffle is on. Icarus can also still be entered from The Crystalline, | ||
| 193 | but doing so (in order to then access Daedalus) will not be logically required. | ||
| 194 | |||
| 195 | However, Icarus can be randomized via the "Enable Icarus" option. Doing so | ||
| 196 | creates locations and items for the map, and includes the worldport in worldport | ||
| 197 | shuffle. The aforementioned connection from The Crystalline also becomes | ||
| 198 | logical, if The Crystalline is enabled. | ||
| 199 | |||
| 200 | It is not trivial to telegraph exactly what is logical within Icarus. It is very | ||
| 201 | easy to break logic because the gravity changers allow you to fall in almost any | ||
| 202 | direction you want to. In general, falling is only in logic if it is "guided", | ||
| 203 | i.e. falling through a hole or an open door to another platform, or using a | ||
| 204 | gravity inverter. You may also sometimes be required to solve panels that are | ||
| 205 | physically near you and easily visible, but not on your plane of gravity. The | ||
| 206 | tracker can help you determine what is considered logical, if you want to stay | ||
| 207 | within the randomizer's logic. | ||
| 208 | |||
| 209 | ### How do the gift maps work? | ||
| 210 | |||
| 211 | The beta tester gift maps are hidden levels intended for specific people. By | ||
| 212 | default, these are not accessible at all from within the randomizer. The "Enable | ||
| 213 | Gift Maps" option allows you to enter the maps, and creates items and locations | ||
| 214 | for them. If worldport shuffle is on, their worldports will be included in the | ||
| 215 | randomization. | ||
| 216 | |||
| 217 | The gift maps are accessed via a panel in The Entry's Starting Room, which only | ||
| 218 | appears if at least one gift map is enabled. It is also treated like a cyan | ||
| 219 | door, and will not appear until the condition specified in the Cyan Door | ||
| 220 | Behavior option is satisfied. Solving this panel with the name of one of the | ||
| 221 | beta testers will teleport you to their corresponding gift map. This README | ||
| 222 | purposefully does not list the names you need to enter the maps via the panel. | ||
| 223 | |||
| 224 | In the base game, nothing happens once you complete a gift map. Masteries have | ||
| 225 | been added to the gift maps in the randomizer so that the player can be rewarded | ||
| 226 | for completing them. | ||
| 227 | |||
| 228 | Note that the gift maps were originally only intended to be played by specific | ||
| 229 | people, and as a result may be frustrating or require knowledge of inside jokes. | ||
| 230 | The Crystalline is particularly difficult as it requires completing a parkour | ||
| 231 | course. It is highly recommended that you complete these maps vanilla or solo | ||
| 232 | before bringing them to a multiworld. It is also perfectly acceptable to never | ||
| 233 | enable them. | ||
| 234 | |||
| 235 | ## Running from source | ||
| 236 | |||
| 237 | The randomizer is mostly written in Python and GDScript, which do not need to be | ||
| 238 | compiled. However, there are three files that need to be generated before the | ||
| 239 | apworld can be used. | ||
| 240 | |||
| 241 | The first file is `data.binpb`, the datafile containing the randomizer logic. | ||
| 242 | You can read about how to generate it on | ||
| 243 | [its own README page](https://code.fourisland.com/lingo2-archipelago/about/data/README.md). | ||
| 244 | Once you have it, put it in a subfolder of `apworld` called `generated`. | ||
| 245 | |||
| 246 | The second generated file is `data_pb2.py`. This file allows Archipelago to read | ||
| 247 | the datafile. We use `protoc`, the Protocol Buffer compiler, to generate it. As | ||
| 248 | of 0.6.3, Archipelago has protobuf 3.20.3 packaged with it, which means we need | ||
| 249 | to compile our proto file with a similar version. | ||
| 250 | |||
| 251 | If you followed the steps to generate `data.binpb` and compiled the `datapacker` | ||
| 252 | tool yourself, you will already have protobuf version 3.21.12 installed through | ||
| 253 | vcpkg. You can then run a command similar to this in order to generate the | ||
| 254 | python file. | ||
| 255 | |||
| 256 | ```shell | ||
| 257 | .\out\build\x64-Debug\vcpkg_installed\x64-windows\tools\protobuf\protoc.exe -Iproto\ ^ | ||
| 258 | --python_out=apworld\generated\ .\proto\data.proto | ||
| 259 | ``` | ||
| 260 | |||
| 261 | The exact path to `protoc.exe` is going to depend on where vcpkg installed its | ||
| 262 | packages. The above location is where Visual Studio will probably put it. | ||
| 263 | |||
| 264 | The third generated file is `proto.gd`. This is the GDScript version of the | ||
| 265 | previous file. We use a Godot script to generate it, which means | ||
| 266 | [the Godot Editor](https://godotengine.org/download/) is required. From the root | ||
| 267 | of the repository: | ||
| 268 | |||
| 269 | ```shell | ||
| 270 | cd vendor\godobuf | ||
| 271 | godot --headless -s addons\protobuf\protobuf_cmdln.gd --input=..\..\proto\data.proto ^ | ||
| 272 | --output=..\..\apworld\generated\proto.gd | ||
| 273 | ``` | ||
| 274 | |||
| 275 | If you are not on Windows, replace the forward slashes with backslashes as | ||
| 276 | appropriate (and the caret with a forward slash). You will also probably need to | ||
| 277 | replace "godot" at the start of the second line with a path to a Godot Editor | ||
| 278 | executable. | ||
| 279 | |||
| 280 | After generating those three files, the apworld should be functional. You can | ||
| 281 | copy it into an Archipelago source tree (rename the folder `apworld` to `lingo2` | ||
| 282 | if you do so) if you want to edit/debug the code. Otherwise, you can zip up the | ||
| 283 | folder and rename it to `lingo2.apworld` in order to package it for | ||
| 284 | distribution. | ||
| diff --git a/apworld/__init__.py b/apworld/__init__.py index c45e8b3..3d2f075 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py | |||
| @@ -1,18 +1,40 @@ | |||
| 1 | """ | 1 | """ |
| 2 | Archipelago init file for Lingo 2 | 2 | Archipelago init file for Lingo 2 |
| 3 | """ | 3 | """ |
| 4 | from BaseClasses import ItemClassification, Item | 4 | from typing import ClassVar |
| 5 | |||
| 6 | from BaseClasses import ItemClassification, Item, Tutorial | ||
| 7 | from Options import OptionError | ||
| 8 | from settings import Group, UserFilePath | ||
| 5 | from worlds.AutoWorld import WebWorld, World | 9 | from worlds.AutoWorld import WebWorld, World |
| 6 | from .items import Lingo2Item | 10 | from .items import Lingo2Item, ANTI_COLLECTABLE_TRAPS |
| 7 | from .options import Lingo2Options | 11 | from .options import Lingo2Options |
| 8 | from .player_logic import Lingo2PlayerLogic | 12 | from .player_logic import Lingo2PlayerLogic |
| 9 | from .regions import create_regions | 13 | from .regions import create_regions, shuffle_entrances, connect_ports_from_ut |
| 10 | from .static_logic import Lingo2StaticLogic | 14 | from .static_logic import Lingo2StaticLogic |
| 15 | from worlds.LauncherComponents import Component, Type, components, launch as launch_component, icon_paths | ||
| 11 | 16 | ||
| 12 | 17 | ||
| 13 | class Lingo2WebWorld(WebWorld): | 18 | class Lingo2WebWorld(WebWorld): |
| 14 | rich_text_options_doc = True | 19 | rich_text_options_doc = True |
| 15 | theme = "grass" | 20 | theme = "grass" |
| 21 | tutorials = [Tutorial( | ||
| 22 | "Multiworld Setup Guide", | ||
| 23 | "A guide to playing Lingo 2 with Archipelago.", | ||
| 24 | "English", | ||
| 25 | "en_Lingo_2.md", | ||
| 26 | "setup/en", | ||
| 27 | ["hatkirby"] | ||
| 28 | )] | ||
| 29 | |||
| 30 | |||
| 31 | class Lingo2Settings(Group): | ||
| 32 | class ExecutableFile(UserFilePath): | ||
| 33 | """Path to the Lingo 2 executable""" | ||
| 34 | is_exe = True | ||
| 35 | |||
| 36 | exe_file: ExecutableFile = ExecutableFile() | ||
| 37 | start_game: bool = True | ||
| 16 | 38 | ||
| 17 | 39 | ||
| 18 | class Lingo2World(World): | 40 | class Lingo2World(World): |
| @@ -24,6 +46,9 @@ class Lingo2World(World): | |||
| 24 | game = "Lingo 2" | 46 | game = "Lingo 2" |
| 25 | web = Lingo2WebWorld() | 47 | web = Lingo2WebWorld() |
| 26 | 48 | ||
| 49 | settings: ClassVar[Lingo2Settings] | ||
| 50 | settings_key = "lingo2_options" | ||
| 51 | |||
| 27 | topology_present = True | 52 | topology_present = True |
| 28 | 53 | ||
| 29 | options_dataclass = Lingo2Options | 54 | options_dataclass = Lingo2Options |
| @@ -32,18 +57,38 @@ class Lingo2World(World): | |||
| 32 | static_logic = Lingo2StaticLogic() | 57 | static_logic = Lingo2StaticLogic() |
| 33 | item_name_to_id = static_logic.item_name_to_id | 58 | item_name_to_id = static_logic.item_name_to_id |
| 34 | location_name_to_id = static_logic.location_name_to_id | 59 | location_name_to_id = static_logic.location_name_to_id |
| 60 | item_name_groups = static_logic.item_name_groups | ||
| 61 | location_name_groups = static_logic.location_name_groups | ||
| 62 | |||
| 63 | for_tracker: ClassVar[bool] = False | ||
| 35 | 64 | ||
| 36 | player_logic: Lingo2PlayerLogic | 65 | player_logic: Lingo2PlayerLogic |
| 37 | 66 | ||
| 67 | port_pairings: dict[int, int] | ||
| 68 | |||
| 38 | def generate_early(self): | 69 | def generate_early(self): |
| 39 | self.player_logic = Lingo2PlayerLogic(self) | 70 | self.player_logic = Lingo2PlayerLogic(self) |
| 71 | self.port_pairings = {} | ||
| 40 | 72 | ||
| 41 | def create_regions(self): | 73 | def create_regions(self): |
| 42 | create_regions(self) | 74 | create_regions(self) |
| 43 | 75 | ||
| 44 | from Utils import visualize_regions | 76 | def connect_entrances(self): |
| 77 | if self.options.shuffle_worldports: | ||
| 78 | if hasattr(self.multiworld, "re_gen_passthrough") and "Lingo 2" in self.multiworld.re_gen_passthrough: | ||
| 79 | slot_value = self.multiworld.re_gen_passthrough["Lingo 2"]["port_pairings"] | ||
| 80 | self.port_pairings = { | ||
| 81 | self.static_logic.port_id_by_ap_id[int(fp)]: self.static_logic.port_id_by_ap_id[int(tp)] | ||
| 82 | for fp, tp in slot_value.items() | ||
| 83 | } | ||
| 84 | |||
| 85 | connect_ports_from_ut(self.port_pairings, self) | ||
| 86 | else: | ||
| 87 | shuffle_entrances(self) | ||
| 88 | |||
| 89 | #from Utils import visualize_regions | ||
| 45 | 90 | ||
| 46 | visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml") | 91 | #visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml") |
| 47 | 92 | ||
| 48 | def create_items(self): | 93 | def create_items(self): |
| 49 | pool = [self.create_item(name) for name in self.player_logic.real_items] | 94 | pool = [self.create_item(name) for name in self.player_logic.real_items] |
| @@ -51,13 +96,33 @@ class Lingo2World(World): | |||
| 51 | total_locations = sum(len(locs) for locs in self.player_logic.locations_by_room.values()) | 96 | total_locations = sum(len(locs) for locs in self.player_logic.locations_by_room.values()) |
| 52 | 97 | ||
| 53 | item_difference = total_locations - len(pool) | 98 | item_difference = total_locations - len(pool) |
| 99 | |||
| 100 | if self.options.trap_percentage > 0: | ||
| 101 | num_traps = int(item_difference * self.options.trap_percentage / 100) | ||
| 102 | item_difference = item_difference - num_traps | ||
| 103 | |||
| 104 | trap_names = [] | ||
| 105 | trap_weights = [] | ||
| 106 | for letter_name, weight in self.static_logic.letter_weights.items(): | ||
| 107 | trap_names.append(f"Anti {letter_name}") | ||
| 108 | trap_weights.append(weight) | ||
| 109 | |||
| 110 | bad_letters = self.random.choices(trap_names, weights=trap_weights, k=num_traps) | ||
| 111 | pool += [self.create_item(trap_name) for trap_name in bad_letters] | ||
| 112 | |||
| 54 | for i in range(0, item_difference): | 113 | for i in range(0, item_difference): |
| 55 | pool.append(self.create_item(self.get_filler_item_name())) | 114 | pool.append(self.create_item(self.get_filler_item_name())) |
| 56 | 115 | ||
| 116 | if not any(ItemClassification.progression in item.classification for item in pool): | ||
| 117 | raise OptionError(f"Lingo 2 player {self.player} has no progression items. Please enable at least one " | ||
| 118 | f"option that would add progression gating to your world, such as Shuffle Doors or " | ||
| 119 | f"Shuffle Letters.") | ||
| 120 | |||
| 57 | self.multiworld.itempool += pool | 121 | self.multiworld.itempool += pool |
| 58 | 122 | ||
| 59 | def create_item(self, name: str) -> Item: | 123 | def create_item(self, name: str) -> Item: |
| 60 | return Lingo2Item(name, ItemClassification.filler if name == self.get_filler_item_name() else | 124 | return Lingo2Item(name, ItemClassification.filler if name == self.get_filler_item_name() else |
| 125 | ItemClassification.trap if name in ANTI_COLLECTABLE_TRAPS else | ||
| 61 | ItemClassification.progression, | 126 | ItemClassification.progression, |
| 62 | self.item_name_to_id.get(name), self.player) | 127 | self.item_name_to_id.get(name), self.player) |
| 63 | 128 | ||
| @@ -68,18 +133,54 @@ class Lingo2World(World): | |||
| 68 | slot_options = [ | 133 | slot_options = [ |
| 69 | "cyan_door_behavior", | 134 | "cyan_door_behavior", |
| 70 | "daedalus_roof_access", | 135 | "daedalus_roof_access", |
| 136 | "enable_gift_maps", | ||
| 137 | "enable_icarus", | ||
| 138 | "endings_requirement", | ||
| 71 | "keyholder_sanity", | 139 | "keyholder_sanity", |
| 140 | "masteries_requirement", | ||
| 72 | "shuffle_control_center_colors", | 141 | "shuffle_control_center_colors", |
| 73 | "shuffle_doors", | 142 | "shuffle_doors", |
| 143 | "shuffle_gallery_paintings", | ||
| 74 | "shuffle_letters", | 144 | "shuffle_letters", |
| 145 | "shuffle_symbols", | ||
| 146 | "shuffle_worldports", | ||
| 147 | "strict_cyan_ending", | ||
| 148 | "strict_purple_ending", | ||
| 75 | "victory_condition", | 149 | "victory_condition", |
| 76 | ] | 150 | ] |
| 77 | 151 | ||
| 78 | slot_data = { | 152 | slot_data: dict[str, object] = { |
| 79 | **self.options.as_dict(*slot_options), | 153 | **self.options.as_dict(*slot_options), |
| 154 | "version": self.static_logic.get_data_version(), | ||
| 80 | } | 155 | } |
| 81 | 156 | ||
| 157 | if self.options.shuffle_worldports: | ||
| 158 | def get_port_ap_id(port_id): | ||
| 159 | return self.static_logic.objects.ports[port_id].ap_id | ||
| 160 | |||
| 161 | slot_data["port_pairings"] = {get_port_ap_id(from_id): get_port_ap_id(to_id) | ||
| 162 | for from_id, to_id in self.port_pairings.items()} | ||
| 163 | |||
| 82 | return slot_data | 164 | return slot_data |
| 83 | 165 | ||
| 84 | def get_filler_item_name(self) -> str: | 166 | def get_filler_item_name(self) -> str: |
| 85 | return "A Job Well Done" | 167 | return "A Job Well Done" |
| 168 | |||
| 169 | # for the universal tracker, doesn't get called in standard gen | ||
| 170 | # docs: https://github.com/FarisTheAncient/Archipelago/blob/tracker/worlds/tracker/docs/re-gen-passthrough.md | ||
| 171 | @staticmethod | ||
| 172 | def interpret_slot_data(slot_data: dict[str, object]) -> dict[str, object]: | ||
| 173 | # returning slot_data so it regens, giving it back in multiworld.re_gen_passthrough | ||
| 174 | # we are using re_gen_passthrough over modifying the world here due to complexities with ER | ||
| 175 | return slot_data | ||
| 176 | |||
| 177 | |||
| 178 | def launch_client(*args): | ||
| 179 | from .context import client_main | ||
| 180 | launch_component(client_main, name="Lingo2Client", args=args) | ||
| 181 | |||
| 182 | |||
| 183 | icon_paths["lingo2_ico"] = f"ap:{__name__}/logo.png" | ||
| 184 | component = Component("Lingo 2 Client", component_type=Type.CLIENT, func=launch_client, | ||
| 185 | description="Open Lingo 2.", supports_uri=True, game_name="Lingo 2", icon="lingo2_ico") | ||
| 186 | components.append(component) | ||
| diff --git a/apworld/client/allowNumbers.gd b/apworld/client/allowNumbers.gd new file mode 100644 index 0000000..d958b50 --- /dev/null +++ b/apworld/client/allowNumbers.gd | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | extends "res://scripts/nodes/allowNumbers.gd" | ||
| 2 | |||
| 3 | |||
| 4 | func _readier(): | ||
| 5 | var ap = global.get_node("Archipelago") | ||
| 6 | var gamedata = global.get_node("Gamedata") | ||
| 7 | |||
| 8 | var item_id = gamedata.objects.get_special_ids()["Numbers"] | ||
| 9 | if ap.client.getItemAmount(item_id) >= 1: | ||
| 10 | global.allow_numbers = true | ||
| diff --git a/client/Archipelago/animationListener.gd b/apworld/client/animationListener.gd index c3b26db..c3b26db 100644 --- a/client/Archipelago/animationListener.gd +++ b/apworld/client/animationListener.gd | |||
| diff --git a/apworld/client/apworld_runtime.gd b/apworld/client/apworld_runtime.gd new file mode 100644 index 0000000..03568bf --- /dev/null +++ b/apworld/client/apworld_runtime.gd | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | var apworld_reader | ||
| 4 | |||
| 5 | |||
| 6 | func _init(path): | ||
| 7 | apworld_reader = ZIPReader.new() | ||
| 8 | apworld_reader.open(path) | ||
| 9 | |||
| 10 | |||
| 11 | func _get_true_path(path): | ||
| 12 | if path.begins_with("../"): | ||
| 13 | return "lingo2/%s" % path.substr(3) | ||
| 14 | else: | ||
| 15 | return "lingo2/client/%s" % path | ||
| 16 | |||
| 17 | |||
| 18 | func path_exists(path): | ||
| 19 | var true_path = _get_true_path(path) | ||
| 20 | return apworld_reader.file_exists(true_path) | ||
| 21 | |||
| 22 | |||
| 23 | func load_script(path): | ||
| 24 | var true_path = _get_true_path(path) | ||
| 25 | |||
| 26 | var script = GDScript.new() | ||
| 27 | script.source_code = apworld_reader.read_file(true_path).get_string_from_utf8() | ||
| 28 | script.reload() | ||
| 29 | |||
| 30 | return script | ||
| 31 | |||
| 32 | |||
| 33 | func read_path(path): | ||
| 34 | var true_path = _get_true_path(path) | ||
| 35 | return apworld_reader.read_file(true_path) | ||
| 36 | |||
| 37 | |||
| 38 | func load_script_as_scene(path, scene_name): | ||
| 39 | var script = load_script(path) | ||
| 40 | var instance = script.new() | ||
| 41 | instance.name = scene_name | ||
| 42 | |||
| 43 | get_tree().unload_current_scene() | ||
| 44 | _load_scene.call_deferred(instance) | ||
| 45 | |||
| 46 | |||
| 47 | func _load_scene(instance): | ||
| 48 | get_tree().get_root().add_child(instance) | ||
| 49 | get_tree().current_scene = instance | ||
| diff --git a/apworld/client/assets/goal.png b/apworld/client/assets/goal.png new file mode 100644 index 0000000..bd1650d --- /dev/null +++ b/apworld/client/assets/goal.png | |||
| Binary files differ | |||
| diff --git a/apworld/client/assets/location.png b/apworld/client/assets/location.png new file mode 100644 index 0000000..5304deb --- /dev/null +++ b/apworld/client/assets/location.png | |||
| Binary files differ | |||
| diff --git a/apworld/client/assets/worldport.png b/apworld/client/assets/worldport.png new file mode 100644 index 0000000..19dfdc3 --- /dev/null +++ b/apworld/client/assets/worldport.png | |||
| Binary files differ | |||
| diff --git a/apworld/client/client.gd b/apworld/client/client.gd new file mode 100644 index 0000000..c149482 --- /dev/null +++ b/apworld/client/client.gd | |||
| @@ -0,0 +1,318 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | const ap_version = {"major": 0, "minor": 6, "build": 3, "class": "Version"} | ||
| 4 | |||
| 5 | var SCRIPT_websocketserver | ||
| 6 | |||
| 7 | var _server | ||
| 8 | var _should_process = false | ||
| 9 | |||
| 10 | var _remote_version = {"major": 0, "minor": 0, "build": 0} | ||
| 11 | var _gen_version = {"major": 0, "minor": 0, "build": 0} | ||
| 12 | |||
| 13 | var ap_server = "" | ||
| 14 | var ap_user = "" | ||
| 15 | var ap_pass = "" | ||
| 16 | |||
| 17 | var _seed = "" | ||
| 18 | var _team = 0 | ||
| 19 | var _slot = 0 | ||
| 20 | var _checked_locations = [] | ||
| 21 | var _checked_worldports = [] | ||
| 22 | var _received_indexes = [] | ||
| 23 | var _received_items = {} | ||
| 24 | var _slot_data = {} | ||
| 25 | var _accessible_locations = [] | ||
| 26 | var _accessible_worldports = [] | ||
| 27 | var _goal_accessible = false | ||
| 28 | var _latched_doors = [] | ||
| 29 | var _hinted_locations = [] | ||
| 30 | |||
| 31 | signal could_not_connect | ||
| 32 | signal connect_status | ||
| 33 | signal client_connected(slot_data) | ||
| 34 | signal item_received(item, amount) | ||
| 35 | signal location_scout_received(location_id, item_name, player_name, flags, for_self) | ||
| 36 | signal text_message_received(message) | ||
| 37 | signal item_sent_notification(message) | ||
| 38 | signal hint_received(message) | ||
| 39 | signal door_latched(id) | ||
| 40 | signal accessible_locations_updated | ||
| 41 | signal checked_locations_updated | ||
| 42 | signal ignored_locations_updated(locations) | ||
| 43 | signal checked_worldports_updated | ||
| 44 | signal keyboard_update_received | ||
| 45 | signal hinted_locations_updated | ||
| 46 | |||
| 47 | |||
| 48 | func _init(): | ||
| 49 | set_process_mode(Node.PROCESS_MODE_ALWAYS) | ||
| 50 | |||
| 51 | global._print("Instantiated APClient") | ||
| 52 | |||
| 53 | |||
| 54 | func _ready(): | ||
| 55 | _server = SCRIPT_websocketserver.new() | ||
| 56 | _server.client_connected.connect(_on_web_socket_server_client_connected) | ||
| 57 | _server.client_disconnected.connect(_on_web_socket_server_client_disconnected) | ||
| 58 | _server.message_received.connect(_on_web_socket_server_message_received) | ||
| 59 | add_child(_server) | ||
| 60 | _server.listen(43182) | ||
| 61 | |||
| 62 | |||
| 63 | func _reset_state(): | ||
| 64 | _should_process = false | ||
| 65 | _received_items = {} | ||
| 66 | _received_indexes = [] | ||
| 67 | _checked_worldports = [] | ||
| 68 | _accessible_locations = [] | ||
| 69 | _accessible_worldports = [] | ||
| 70 | _goal_accessible = false | ||
| 71 | |||
| 72 | |||
| 73 | func disconnect_from_ap(): | ||
| 74 | sendMessage([{"cmd": "Disconnect"}]) | ||
| 75 | |||
| 76 | |||
| 77 | func _on_web_socket_server_client_connected(peer_id: int) -> void: | ||
| 78 | var peer: WebSocketPeer = _server.peers[peer_id] | ||
| 79 | print("Remote client connected: %d. Protocol: %s" % [peer_id, peer.get_selected_protocol()]) | ||
| 80 | _server.send(-peer_id, "[%d] connected" % peer_id) | ||
| 81 | |||
| 82 | |||
| 83 | func _on_web_socket_server_client_disconnected(peer_id: int) -> void: | ||
| 84 | var peer: WebSocketPeer = _server.peers[peer_id] | ||
| 85 | print( | ||
| 86 | ( | ||
| 87 | "Remote client disconnected: %d. Code: %d, Reason: %s" | ||
| 88 | % [peer_id, peer.get_close_code(), peer.get_close_reason()] | ||
| 89 | ) | ||
| 90 | ) | ||
| 91 | _server.send(-peer_id, "[%d] disconnected" % peer_id) | ||
| 92 | |||
| 93 | |||
| 94 | func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> void: | ||
| 95 | global._print("Got data from server: " + packet) | ||
| 96 | var json = JSON.new() | ||
| 97 | var jserror = json.parse(packet) | ||
| 98 | if jserror != OK: | ||
| 99 | global._print("Error parsing packet from AP: " + jserror.error_string) | ||
| 100 | return | ||
| 101 | |||
| 102 | for message in json.data: | ||
| 103 | var cmd = message["cmd"] | ||
| 104 | global._print("Received command: " + cmd) | ||
| 105 | |||
| 106 | if cmd == "Connected": | ||
| 107 | _seed = message["seed_name"] | ||
| 108 | _remote_version = message["version"] | ||
| 109 | _gen_version = message["generator_version"] | ||
| 110 | _team = message["team"] | ||
| 111 | _slot = message["slot"] | ||
| 112 | _slot_data = message["slot_data"] | ||
| 113 | |||
| 114 | _checked_locations = [] | ||
| 115 | for location in message["checked_locations"]: | ||
| 116 | _checked_locations.append(int(location)) | ||
| 117 | |||
| 118 | client_connected.emit(_slot_data) | ||
| 119 | |||
| 120 | elif cmd == "ConnectionRefused": | ||
| 121 | could_not_connect.emit(message["text"]) | ||
| 122 | global._print("Connection to AP refused") | ||
| 123 | |||
| 124 | elif cmd == "UpdateLocations": | ||
| 125 | for location in message["locations"]: | ||
| 126 | var lint = int(location) | ||
| 127 | if not _checked_locations.has(lint): | ||
| 128 | _checked_locations.append(lint) | ||
| 129 | |||
| 130 | checked_locations_updated.emit() | ||
| 131 | |||
| 132 | elif cmd == "UpdateWorldports": | ||
| 133 | for port_id in message["worldports"]: | ||
| 134 | var lint = int(port_id) | ||
| 135 | if not _checked_worldports.has(lint): | ||
| 136 | _checked_worldports.append(lint) | ||
| 137 | |||
| 138 | checked_worldports_updated.emit() | ||
| 139 | |||
| 140 | elif cmd == "ItemReceived": | ||
| 141 | for item in message["items"]: | ||
| 142 | var index = int(item["index"]) | ||
| 143 | if _received_indexes.has(index): | ||
| 144 | # Do not re-process items. | ||
| 145 | continue | ||
| 146 | |||
| 147 | _received_indexes.append(index) | ||
| 148 | |||
| 149 | var item_id = int(item["id"]) | ||
| 150 | _received_items[item_id] = _received_items.get(item_id, 0) + 1 | ||
| 151 | |||
| 152 | item_received.emit(item, _received_items[item_id]) | ||
| 153 | |||
| 154 | elif cmd == "TextMessage": | ||
| 155 | text_message_received.emit(message["data"]) | ||
| 156 | |||
| 157 | elif cmd == "ItemSentNotif": | ||
| 158 | item_sent_notification.emit(message) | ||
| 159 | |||
| 160 | elif cmd == "HintReceived": | ||
| 161 | hint_received.emit(message) | ||
| 162 | |||
| 163 | elif cmd == "LocationInfo": | ||
| 164 | for loc in message["locations"]: | ||
| 165 | location_scout_received.emit( | ||
| 166 | int(loc["id"]), loc["item"], loc["player"], int(loc["flags"]), int(loc["self"]) | ||
| 167 | ) | ||
| 168 | |||
| 169 | elif cmd == "AccessibleLocations": | ||
| 170 | _accessible_locations.clear() | ||
| 171 | _accessible_worldports.clear() | ||
| 172 | |||
| 173 | for loc in message["locations"]: | ||
| 174 | _accessible_locations.append(int(loc)) | ||
| 175 | |||
| 176 | if "worldports" in message: | ||
| 177 | for port_id in message["worldports"]: | ||
| 178 | _accessible_worldports.append(int(port_id)) | ||
| 179 | |||
| 180 | _goal_accessible = bool(message.get("goal", false)) | ||
| 181 | |||
| 182 | accessible_locations_updated.emit() | ||
| 183 | |||
| 184 | elif cmd == "UpdateKeyboard": | ||
| 185 | var updates = {} | ||
| 186 | for k in message["updates"]: | ||
| 187 | updates[k] = int(message["updates"][k]) | ||
| 188 | |||
| 189 | keyboard_update_received.emit(updates) | ||
| 190 | |||
| 191 | elif cmd == "PathReply": | ||
| 192 | var textclient = global.get_node("Textclient") | ||
| 193 | textclient.display_logical_path( | ||
| 194 | message["type"], int(message.get("id", null)), message["path"] | ||
| 195 | ) | ||
| 196 | |||
| 197 | elif cmd == "UpdateLatches": | ||
| 198 | for id in message["latches"]: | ||
| 199 | var iid = int(id) | ||
| 200 | if not _latched_doors.has(iid): | ||
| 201 | _latched_doors.append(iid) | ||
| 202 | |||
| 203 | door_latched.emit(iid) | ||
| 204 | |||
| 205 | elif cmd == "SetIgnoredLocations": | ||
| 206 | var locs = [] | ||
| 207 | for id in message["locations"]: | ||
| 208 | locs.append(int(id)) | ||
| 209 | |||
| 210 | ignored_locations_updated.emit(locs) | ||
| 211 | |||
| 212 | elif cmd == "UpdateHintedLocations": | ||
| 213 | for id in message["locations"]: | ||
| 214 | var iid = int(id) | ||
| 215 | if !_hinted_locations.has(iid): | ||
| 216 | _hinted_locations.append(iid) | ||
| 217 | |||
| 218 | hinted_locations_updated.emit() | ||
| 219 | |||
| 220 | |||
| 221 | func connectToServer(server, un, pw): | ||
| 222 | sendMessage([{"cmd": "Connect", "server": server, "player": un, "password": pw}]) | ||
| 223 | |||
| 224 | ap_server = server | ||
| 225 | ap_user = un | ||
| 226 | ap_pass = pw | ||
| 227 | |||
| 228 | _should_process = true | ||
| 229 | |||
| 230 | connect_status.emit("Connecting...") | ||
| 231 | |||
| 232 | |||
| 233 | func sendMessage(msg): | ||
| 234 | var payload = JSON.stringify(msg) | ||
| 235 | _server.send(0, payload) | ||
| 236 | |||
| 237 | |||
| 238 | func connectToRoom(): | ||
| 239 | connect_status.emit("Authenticating...") | ||
| 240 | |||
| 241 | sendMessage( | ||
| 242 | [ | ||
| 243 | { | ||
| 244 | "cmd": "Connect", | ||
| 245 | "password": ap_pass, | ||
| 246 | "game": "Lingo 2", | ||
| 247 | "name": ap_user, | ||
| 248 | } | ||
| 249 | ] | ||
| 250 | ) | ||
| 251 | |||
| 252 | |||
| 253 | func requestSync(): | ||
| 254 | sendMessage([{"cmd": "Sync"}]) | ||
| 255 | |||
| 256 | |||
| 257 | func sendLocation(loc_id): | ||
| 258 | sendMessage([{"cmd": "LocationChecks", "locations": [loc_id]}]) | ||
| 259 | |||
| 260 | |||
| 261 | func sendLocations(loc_ids): | ||
| 262 | sendMessage([{"cmd": "LocationChecks", "locations": loc_ids}]) | ||
| 263 | |||
| 264 | |||
| 265 | func say(textdata): | ||
| 266 | sendMessage([{"cmd": "Say", "text": textdata}]) | ||
| 267 | |||
| 268 | |||
| 269 | func completedGoal(): | ||
| 270 | sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL | ||
| 271 | |||
| 272 | |||
| 273 | func scoutLocations(loc_ids): | ||
| 274 | sendMessage([{"cmd": "LocationScouts", "locations": loc_ids}]) | ||
| 275 | |||
| 276 | |||
| 277 | func updateKeyboard(updates): | ||
| 278 | sendMessage([{"cmd": "UpdateKeyboard", "keyboard": updates}]) | ||
| 279 | |||
| 280 | |||
| 281 | func checkWorldport(port_id): | ||
| 282 | if not _checked_worldports.has(port_id): | ||
| 283 | sendMessage([{"cmd": "CheckWorldport", "port_id": port_id}]) | ||
| 284 | |||
| 285 | |||
| 286 | func latchDoor(id): | ||
| 287 | if not _latched_doors.has(id): | ||
| 288 | _latched_doors.append(id) | ||
| 289 | |||
| 290 | sendMessage([{"cmd": "LatchDoor", "door": id}]) | ||
| 291 | |||
| 292 | |||
| 293 | func getLogicalPath(object_type, object_id): | ||
| 294 | var msg = {"cmd": "GetPath", "type": object_type} | ||
| 295 | if object_id != null: | ||
| 296 | msg["id"] = object_id | ||
| 297 | |||
| 298 | sendMessage([msg]) | ||
| 299 | |||
| 300 | |||
| 301 | func addIgnoredLocation(loc_id): | ||
| 302 | sendMessage([{"cmd": "IgnoreLocation", "id": loc_id}]) | ||
| 303 | |||
| 304 | |||
| 305 | func removeIgnoredLocation(loc_id): | ||
| 306 | sendMessage([{"cmd": "UnignoreLocation", "id": loc_id}]) | ||
| 307 | |||
| 308 | |||
| 309 | func sendQuit(): | ||
| 310 | sendMessage([{"cmd": "Quit"}]) | ||
| 311 | |||
| 312 | |||
| 313 | func hasItem(item_id): | ||
| 314 | return _received_items.has(item_id) | ||
| 315 | |||
| 316 | |||
| 317 | func getItemAmount(item_id): | ||
| 318 | return _received_items.get(item_id, 0) | ||
| diff --git a/client/Archipelago/collectable.gd b/apworld/client/collectable.gd index 4a17a2a..4a17a2a 100644 --- a/client/Archipelago/collectable.gd +++ b/apworld/client/collectable.gd | |||
| diff --git a/apworld/client/compass.gd b/apworld/client/compass.gd new file mode 100644 index 0000000..c90475a --- /dev/null +++ b/apworld/client/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/apworld/client/compass_overlay.gd b/apworld/client/compass_overlay.gd new file mode 100644 index 0000000..56e81ff --- /dev/null +++ b/apworld/client/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/apworld/client/door.gd b/apworld/client/door.gd new file mode 100644 index 0000000..63cfa99 --- /dev/null +++ b/apworld/client/door.gd | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | extends "res://scripts/nodes/door.gd" | ||
| 2 | |||
| 3 | var door_id | ||
| 4 | var item_id | ||
| 5 | var item_amount | ||
| 6 | var latched = false | ||
| 7 | |||
| 8 | |||
| 9 | func _ready(): | ||
| 10 | var node_path = String( | ||
| 11 | get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names() | ||
| 12 | ) | ||
| 13 | |||
| 14 | var gamedata = global.get_node("Gamedata") | ||
| 15 | door_id = gamedata.get_door_for_map_node_path(global.map, node_path) | ||
| 16 | if door_id != null: | ||
| 17 | var ap = global.get_node("Archipelago") | ||
| 18 | var item_lock = ap.get_item_id_for_door(door_id) | ||
| 19 | |||
| 20 | if item_lock != null: | ||
| 21 | item_id = item_lock[0] | ||
| 22 | item_amount = item_lock[1] | ||
| 23 | |||
| 24 | self.senders = [] | ||
| 25 | self.senderGroup = [] | ||
| 26 | self.nested = false | ||
| 27 | self.complete_at = 0 | ||
| 28 | self.max_length = 0 | ||
| 29 | self.excludeSenders = [] | ||
| 30 | |||
| 31 | call_deferred("_readier") | ||
| 32 | else: | ||
| 33 | var door_data = gamedata.objects.get_doors()[door_id] | ||
| 34 | if door_data.has_latch() and door_data.get_latch(): | ||
| 35 | _check_latched.call_deferred(door_id) | ||
| 36 | |||
| 37 | latched = true | ||
| 38 | |||
| 39 | if global.map == "the_sun_temple": | ||
| 40 | if name == "spe_EndPlatform" or name == "spe_entry_2": | ||
| 41 | senders = [NodePath("/root/scene/Panels/EndCheck_dog")] | ||
| 42 | |||
| 43 | if global.map == "the_parthenon": | ||
| 44 | if name == "spe_entry_1": | ||
| 45 | senders = [NodePath("/root/scene/Panels/EndCheck_dog")] | ||
| 46 | |||
| 47 | super._ready() | ||
| 48 | |||
| 49 | |||
| 50 | func _readier(): | ||
| 51 | var ap = global.get_node("Archipelago") | ||
| 52 | |||
| 53 | if ap.client.getItemAmount(item_id) >= item_amount: | ||
| 54 | handleTriggered() | ||
| 55 | |||
| 56 | |||
| 57 | func _check_latched(door_id): | ||
| 58 | var ap = global.get_node("Archipelago") | ||
| 59 | |||
| 60 | if ap.client._latched_doors.has(door_id): | ||
| 61 | triggered = total | ||
| 62 | handleTriggered() | ||
| 63 | |||
| 64 | |||
| 65 | func handleTriggered(): | ||
| 66 | super.handleTriggered() | ||
| 67 | |||
| 68 | if latched and ran: | ||
| 69 | var ap = global.get_node("Archipelago") | ||
| 70 | ap.client.latchDoor(door_id) | ||
| 71 | |||
| 72 | |||
| 73 | func handleUntriggered(): | ||
| 74 | if not latched or not ran: | ||
| 75 | super.handleUntriggered() | ||
| diff --git a/apworld/client/effects.gd b/apworld/client/effects.gd new file mode 100644 index 0000000..9dc1dd8 --- /dev/null +++ b/apworld/client/effects.gd | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | extends CanvasLayer | ||
| 2 | |||
| 3 | var _label | ||
| 4 | |||
| 5 | var _disconnected = false | ||
| 6 | |||
| 7 | |||
| 8 | func _ready(): | ||
| 9 | _label = Label.new() | ||
| 10 | _label.name = "Label" | ||
| 11 | _label.offset_left = 20 | ||
| 12 | _label.offset_top = 20 | ||
| 13 | _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT | ||
| 14 | _label.vertical_alignment = VERTICAL_ALIGNMENT_TOP | ||
| 15 | _label.theme = preload("res://assets/themes/baseUI.tres") | ||
| 16 | _label.add_theme_font_size_override("font_size", 36) | ||
| 17 | add_child(_label) | ||
| 18 | |||
| 19 | |||
| 20 | func set_connection_lost(arg): | ||
| 21 | _disconnected = arg | ||
| 22 | |||
| 23 | _update_label() | ||
| 24 | |||
| 25 | |||
| 26 | func _update_label(): | ||
| 27 | var text = [] | ||
| 28 | |||
| 29 | if _disconnected: | ||
| 30 | text.append("Disconnected from multiworld.") | ||
| 31 | |||
| 32 | _label.text = "\n".join(text) | ||
| diff --git a/apworld/client/gamedata.gd b/apworld/client/gamedata.gd new file mode 100644 index 0000000..d7e3136 --- /dev/null +++ b/apworld/client/gamedata.gd | |||
| @@ -0,0 +1,302 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | var SCRIPT_proto | ||
| 4 | |||
| 5 | var objects | ||
| 6 | var door_id_by_map_node_path = {} | ||
| 7 | var painting_id_by_map_node_path = {} | ||
| 8 | var panel_id_by_map_node_path = {} | ||
| 9 | var port_id_by_map_node_path = {} | ||
| 10 | var door_id_by_ap_id = {} | ||
| 11 | var map_id_by_name = {} | ||
| 12 | var progressive_id_by_ap_id = {} | ||
| 13 | var letter_id_by_ap_id = {} | ||
| 14 | var symbol_item_ids = [] | ||
| 15 | var anti_trap_ids = {} | ||
| 16 | var location_name_by_id = {} | ||
| 17 | var ending_display_name_by_name = {} | ||
| 18 | var port_id_by_ap_id = {} | ||
| 19 | |||
| 20 | var kSYMBOL_ITEMS | ||
| 21 | |||
| 22 | |||
| 23 | func _init(proto_script): | ||
| 24 | SCRIPT_proto = proto_script | ||
| 25 | |||
| 26 | kSYMBOL_ITEMS = { | ||
| 27 | SCRIPT_proto.PuzzleSymbol.SUN: "Sun Symbol", | ||
| 28 | SCRIPT_proto.PuzzleSymbol.SPARKLES: "Sparkles Symbol", | ||
| 29 | SCRIPT_proto.PuzzleSymbol.ZERO: "Zero Symbol", | ||
| 30 | SCRIPT_proto.PuzzleSymbol.EXAMPLE: "Example Symbol", | ||
| 31 | SCRIPT_proto.PuzzleSymbol.BOXES: "Boxes Symbol", | ||
| 32 | SCRIPT_proto.PuzzleSymbol.PLANET: "Planet Symbol", | ||
| 33 | SCRIPT_proto.PuzzleSymbol.PYRAMID: "Pyramid Symbol", | ||
| 34 | SCRIPT_proto.PuzzleSymbol.CROSS: "Cross Symbol", | ||
| 35 | SCRIPT_proto.PuzzleSymbol.SWEET: "Sweet Symbol", | ||
| 36 | SCRIPT_proto.PuzzleSymbol.GENDER: "Gender Symbol", | ||
| 37 | SCRIPT_proto.PuzzleSymbol.AGE: "Age Symbol", | ||
| 38 | SCRIPT_proto.PuzzleSymbol.SOUND: "Sound Symbol", | ||
| 39 | SCRIPT_proto.PuzzleSymbol.ANAGRAM: "Anagram Symbol", | ||
| 40 | SCRIPT_proto.PuzzleSymbol.JOB: "Job Symbol", | ||
| 41 | SCRIPT_proto.PuzzleSymbol.STARS: "Stars Symbol", | ||
| 42 | SCRIPT_proto.PuzzleSymbol.NULL: "Null Symbol", | ||
| 43 | SCRIPT_proto.PuzzleSymbol.EVAL: "Eval Symbol", | ||
| 44 | SCRIPT_proto.PuzzleSymbol.LINGO: "Lingo Symbol", | ||
| 45 | SCRIPT_proto.PuzzleSymbol.QUESTION: "Question Symbol", | ||
| 46 | } | ||
| 47 | |||
| 48 | |||
| 49 | func load(data_bytes): | ||
| 50 | objects = SCRIPT_proto.AllObjects.new() | ||
| 51 | |||
| 52 | var result_code = objects.from_bytes(data_bytes) | ||
| 53 | if result_code != SCRIPT_proto.PB_ERR.NO_ERRORS: | ||
| 54 | print("Could not load generated data: %d" % result_code) | ||
| 55 | return | ||
| 56 | |||
| 57 | for map in objects.get_maps(): | ||
| 58 | map_id_by_name[map.get_name()] = map.get_id() | ||
| 59 | |||
| 60 | for door in objects.get_doors(): | ||
| 61 | var map = objects.get_maps()[door.get_map_id()] | ||
| 62 | |||
| 63 | if not map.get_name() in door_id_by_map_node_path: | ||
| 64 | door_id_by_map_node_path[map.get_name()] = {} | ||
| 65 | |||
| 66 | var map_data = door_id_by_map_node_path[map.get_name()] | ||
| 67 | for receiver in door.get_receivers(): | ||
| 68 | map_data[receiver] = door.get_id() | ||
| 69 | |||
| 70 | for painting_id in door.get_move_paintings(): | ||
| 71 | var painting = objects.get_paintings()[painting_id] | ||
| 72 | map_data[painting.get_path()] = door.get_id() | ||
| 73 | |||
| 74 | if door.has_ap_id(): | ||
| 75 | door_id_by_ap_id[door.get_ap_id()] = door.get_id() | ||
| 76 | |||
| 77 | if ( | ||
| 78 | door.get_type() == SCRIPT_proto.DoorType.STANDARD | ||
| 79 | or door.get_type() == SCRIPT_proto.DoorType.LOCATION_ONLY | ||
| 80 | or door.get_type() == SCRIPT_proto.DoorType.GRAVESTONE | ||
| 81 | ): | ||
| 82 | location_name_by_id[door.get_ap_id()] = _get_door_location_name(door) | ||
| 83 | |||
| 84 | for painting in objects.get_paintings(): | ||
| 85 | var room = objects.get_rooms()[painting.get_room_id()] | ||
| 86 | var map = objects.get_maps()[room.get_map_id()] | ||
| 87 | |||
| 88 | if not map.get_name() in painting_id_by_map_node_path: | ||
| 89 | painting_id_by_map_node_path[map.get_name()] = {} | ||
| 90 | |||
| 91 | var _map_data = painting_id_by_map_node_path[map.get_name()] | ||
| 92 | |||
| 93 | for port in objects.get_ports(): | ||
| 94 | var room = objects.get_rooms()[port.get_room_id()] | ||
| 95 | var map = objects.get_maps()[room.get_map_id()] | ||
| 96 | |||
| 97 | if not map.get_name() in port_id_by_map_node_path: | ||
| 98 | port_id_by_map_node_path[map.get_name()] = {} | ||
| 99 | |||
| 100 | var map_data = port_id_by_map_node_path[map.get_name()] | ||
| 101 | map_data[port.get_path()] = port.get_id() | ||
| 102 | |||
| 103 | if port.has_ap_id(): | ||
| 104 | port_id_by_ap_id[port.get_ap_id()] = port.get_id() | ||
| 105 | |||
| 106 | for progressive in objects.get_progressives(): | ||
| 107 | progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id() | ||
| 108 | |||
| 109 | for letter in objects.get_letters(): | ||
| 110 | letter_id_by_ap_id[letter.get_ap_id()] = letter.get_id() | ||
| 111 | location_name_by_id[letter.get_ap_id()] = _get_letter_location_name(letter) | ||
| 112 | |||
| 113 | for mastery in objects.get_masteries(): | ||
| 114 | location_name_by_id[mastery.get_ap_id()] = _get_mastery_location_name(mastery) | ||
| 115 | |||
| 116 | for ending in objects.get_endings(): | ||
| 117 | var location_name = _get_ending_location_name(ending) | ||
| 118 | location_name_by_id[ending.get_ap_id()] = location_name | ||
| 119 | ending_display_name_by_name[ending.get_name()] = location_name | ||
| 120 | |||
| 121 | for keyholder in objects.get_keyholders(): | ||
| 122 | if keyholder.has_key(): | ||
| 123 | location_name_by_id[keyholder.get_ap_id()] = _get_keyholder_location_name(keyholder) | ||
| 124 | |||
| 125 | for panel in objects.get_panels(): | ||
| 126 | var room = objects.get_rooms()[panel.get_room_id()] | ||
| 127 | var map = objects.get_maps()[room.get_map_id()] | ||
| 128 | |||
| 129 | if not map.get_name() in panel_id_by_map_node_path: | ||
| 130 | panel_id_by_map_node_path[map.get_name()] = {} | ||
| 131 | |||
| 132 | var map_data = panel_id_by_map_node_path[map.get_name()] | ||
| 133 | map_data[panel.get_path()] = panel.get_id() | ||
| 134 | |||
| 135 | for symbol_name in kSYMBOL_ITEMS.values(): | ||
| 136 | symbol_item_ids.append(objects.get_special_ids()[symbol_name]) | ||
| 137 | |||
| 138 | for special_name in objects.get_special_ids().keys(): | ||
| 139 | if special_name.begins_with("Anti "): | ||
| 140 | anti_trap_ids[objects.get_special_ids()[special_name]] = ( | ||
| 141 | special_name.substr(5).to_lower() | ||
| 142 | ) | ||
| 143 | |||
| 144 | |||
| 145 | func get_door_for_map_node_path(map_name, node_path): | ||
| 146 | if not door_id_by_map_node_path.has(map_name): | ||
| 147 | return null | ||
| 148 | |||
| 149 | var map_data = door_id_by_map_node_path[map_name] | ||
| 150 | return map_data.get(node_path, null) | ||
| 151 | |||
| 152 | |||
| 153 | func get_panel_for_map_node_path(map_name, node_path): | ||
| 154 | if not panel_id_by_map_node_path.has(map_name): | ||
| 155 | return null | ||
| 156 | |||
| 157 | var map_data = panel_id_by_map_node_path[map_name] | ||
| 158 | return map_data.get(node_path, null) | ||
| 159 | |||
| 160 | |||
| 161 | func get_port_for_map_node_path(map_name, node_path): | ||
| 162 | if not port_id_by_map_node_path.has(map_name): | ||
| 163 | return null | ||
| 164 | |||
| 165 | var map_data = port_id_by_map_node_path[map_name] | ||
| 166 | return map_data.get(node_path, null) | ||
| 167 | |||
| 168 | |||
| 169 | func get_door_ap_id(door_id): | ||
| 170 | var door = objects.get_doors()[door_id] | ||
| 171 | if door.has_ap_id(): | ||
| 172 | return door.get_ap_id() | ||
| 173 | else: | ||
| 174 | return null | ||
| 175 | |||
| 176 | |||
| 177 | func get_door_map_name(door_id): | ||
| 178 | var door = objects.get_doors()[door_id] | ||
| 179 | var map = objects.get_maps()[door.get_map_id()] | ||
| 180 | return map.get_name() | ||
| 181 | |||
| 182 | |||
| 183 | func get_door_receivers(door_id): | ||
| 184 | var door = objects.get_doors()[door_id] | ||
| 185 | return door.get_receivers() | ||
| 186 | |||
| 187 | |||
| 188 | func get_worldport_display_name(port_id): | ||
| 189 | var port = objects.get_ports()[port_id] | ||
| 190 | return "%s - %s" % [_get_room_object_map_name(port), port.get_display_name()] | ||
| 191 | |||
| 192 | |||
| 193 | func _get_map_object_map_name(obj): | ||
| 194 | return objects.get_maps()[obj.get_map_id()].get_display_name() | ||
| 195 | |||
| 196 | |||
| 197 | func _get_room_object_map_name(obj): | ||
| 198 | return _get_map_object_map_name(objects.get_rooms()[obj.get_room_id()]) | ||
| 199 | |||
| 200 | |||
| 201 | func _get_room_object_location_prefix(obj): | ||
| 202 | var room = objects.get_rooms()[obj.get_room_id()] | ||
| 203 | var game_map = objects.get_maps()[room.get_map_id()] | ||
| 204 | |||
| 205 | if room.has_panel_display_name(): | ||
| 206 | return "%s (%s)" % [game_map.get_display_name(), room.get_panel_display_name()] | ||
| 207 | else: | ||
| 208 | return game_map.get_display_name() | ||
| 209 | |||
| 210 | |||
| 211 | func _get_door_location_name(door): | ||
| 212 | var map_part = _get_room_object_location_prefix(door) | ||
| 213 | |||
| 214 | if door.has_location_name(): | ||
| 215 | return "%s - %s" % [map_part, door.get_location_name()] | ||
| 216 | |||
| 217 | var generated_location_name = _get_generated_door_location_name(door) | ||
| 218 | if generated_location_name != null: | ||
| 219 | return generated_location_name | ||
| 220 | |||
| 221 | return "%s - %s" % [map_part, door.get_name()] | ||
| 222 | |||
| 223 | |||
| 224 | func _get_generated_door_location_name(door): | ||
| 225 | if door.get_type() != SCRIPT_proto.DoorType.STANDARD: | ||
| 226 | return null | ||
| 227 | |||
| 228 | if ( | ||
| 229 | door.get_keyholders().size() > 0 | ||
| 230 | or (door.has_white_ending() and door.get_white_ending()) | ||
| 231 | or door.has_complete_at() | ||
| 232 | ): | ||
| 233 | return null | ||
| 234 | |||
| 235 | if door.get_panels().size() > 4: | ||
| 236 | return null | ||
| 237 | |||
| 238 | var map_areas = [] | ||
| 239 | for panel_id in door.get_panels(): | ||
| 240 | var panel = objects.get_panels()[panel_id.get_panel()] | ||
| 241 | var panel_room = objects.get_rooms()[panel.get_room_id()] | ||
| 242 | # It's okay if panel_display_name is not present because then it's coalesced with other unnamed areas. | ||
| 243 | var panel_display_name = "" | ||
| 244 | if panel_room.has_panel_display_name(): | ||
| 245 | panel_display_name = panel_room.get_panel_display_name() | ||
| 246 | if not map_areas.has(panel_display_name): | ||
| 247 | map_areas.append(panel_display_name) | ||
| 248 | |||
| 249 | if map_areas.size() > 1: | ||
| 250 | return null | ||
| 251 | |||
| 252 | var game_map = objects.get_maps()[door.get_map_id()] | ||
| 253 | var map_area = map_areas[0] | ||
| 254 | var map_part | ||
| 255 | if map_area == "": | ||
| 256 | map_part = game_map.get_display_name() | ||
| 257 | else: | ||
| 258 | map_part = "%s (%s)" % [game_map.get_display_name(), map_area] | ||
| 259 | |||
| 260 | var panel_names = [] | ||
| 261 | for panel_id in door.get_panels(): | ||
| 262 | var panel_data = objects.get_panels()[panel_id.get_panel()] | ||
| 263 | var panel_name | ||
| 264 | if panel_data.has_display_name(): | ||
| 265 | panel_name = panel_data.get_display_name() | ||
| 266 | else: | ||
| 267 | panel_name = panel_data.get_name() | ||
| 268 | |||
| 269 | var location_part | ||
| 270 | if panel_id.has_answer(): | ||
| 271 | location_part = "%s/%s" % [panel_name, panel_id.get_answer().to_upper()] | ||
| 272 | else: | ||
| 273 | location_part = panel_name | ||
| 274 | |||
| 275 | panel_names.append(location_part) | ||
| 276 | |||
| 277 | panel_names.sort() | ||
| 278 | |||
| 279 | return map_part + " - " + ", ".join(panel_names) | ||
| 280 | |||
| 281 | |||
| 282 | func _get_letter_location_name(letter): | ||
| 283 | var letter_level = 2 if (letter.has_level2() and letter.get_level2()) else 1 | ||
| 284 | var letter_name = "%s%d" % [letter.get_key().to_upper(), letter_level] | ||
| 285 | return "%s - %s" % [_get_room_object_map_name(letter), letter_name] | ||
| 286 | |||
| 287 | |||
| 288 | func _get_mastery_location_name(mastery): | ||
| 289 | return "%s - Mastery" % _get_room_object_map_name(mastery) | ||
| 290 | |||
| 291 | |||
| 292 | func _get_ending_location_name(ending): | ||
| 293 | return ( | ||
| 294 | "%s - %s Ending" % [_get_room_object_map_name(ending), ending.get_name().to_pascal_case()] | ||
| 295 | ) | ||
| 296 | |||
| 297 | |||
| 298 | func _get_keyholder_location_name(keyholder): | ||
| 299 | return ( | ||
| 300 | "%s - %s Keyholder" | ||
| 301 | % [_get_room_object_location_prefix(keyholder), keyholder.get_key().to_upper()] | ||
| 302 | ) | ||
| diff --git a/client/Archipelago/keyHolder.gd b/apworld/client/keyHolder.gd index 3c037ff..3c037ff 100644 --- a/client/Archipelago/keyHolder.gd +++ b/apworld/client/keyHolder.gd | |||
| diff --git a/client/Archipelago/keyHolderChecker.gd b/apworld/client/keyHolderChecker.gd index a75a9e4..a75a9e4 100644 --- a/client/Archipelago/keyHolderChecker.gd +++ b/apworld/client/keyHolderChecker.gd | |||
| diff --git a/client/Archipelago/keyHolderResetterListener.gd b/apworld/client/keyHolderResetterListener.gd index d5300f3..d5300f3 100644 --- a/client/Archipelago/keyHolderResetterListener.gd +++ b/apworld/client/keyHolderResetterListener.gd | |||
| diff --git a/client/Archipelago/keyboard.gd b/apworld/client/keyboard.gd index e43ec9f..a59c4d0 100644 --- a/client/Archipelago/keyboard.gd +++ b/apworld/client/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 | ||
| @@ -46,6 +48,9 @@ func load_seed(): | |||
| 46 | if localdata.size() > 2: | 48 | if localdata.size() > 2: |
| 47 | keyholder_state = localdata[2] | 49 | keyholder_state = localdata[2] |
| 48 | 50 | ||
| 51 | if not letters_saved.is_empty(): | ||
| 52 | ap.client.updateKeyboard(letters_saved) | ||
| 53 | |||
| 49 | for k in kALL_LETTERS: | 54 | for k in kALL_LETTERS: |
| 50 | var level = 0 | 55 | var level = 0 |
| 51 | 56 | ||
| @@ -79,23 +84,47 @@ func save(): | |||
| 79 | func update_unlocks(): | 84 | func update_unlocks(): |
| 80 | unlocks.resetKeys() | 85 | unlocks.resetKeys() |
| 81 | 86 | ||
| 87 | var has_doubles = false | ||
| 88 | |||
| 82 | for k in kALL_LETTERS: | 89 | for k in kALL_LETTERS: |
| 83 | var level = 0 | 90 | var level = 0 |
| 84 | 91 | ||
| 85 | if not letters_in_keyholders.has(k): | 92 | if not letters_in_keyholders.has(k): |
| 86 | level = letters_saved.get(k, 0) + letters_dynamic.get(k, 0) | 93 | level = letters_saved.get(k, 0) + letters_dynamic.get(k, 0) |
| 87 | 94 | ||
| 88 | if level > 2: | 95 | if level >= 2: |
| 89 | level = 2 | 96 | level = 2 |
| 97 | has_doubles = true | ||
| 98 | |||
| 99 | if letters_blocked.has(k): | ||
| 100 | level = 0 | ||
| 90 | 101 | ||
| 91 | unlocks.unlockKey(k, level) | 102 | unlocks.unlockKey(k, level) |
| 92 | 103 | ||
| 104 | if has_doubles and unlocks.data["double_letters"] != "unlocked": | ||
| 105 | var ap = global.get_node("Archipelago") | ||
| 106 | if ap.cyan_door_behavior == ap.kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER: | ||
| 107 | unlocks.setData("double_letters", "unlocked") | ||
| 108 | |||
| 93 | 109 | ||
| 94 | func collect_local_letter(key, level): | 110 | func collect_local_letter(key, level): |
| 95 | if level < 0 or level > 2 or level < letters_saved.get(key, 0): | 111 | var ap = global.get_node("Archipelago") |
| 112 | var true_level = 0 | ||
| 113 | |||
| 114 | if ap.get_letter_behavior(key, false) == ap.kLETTER_BEHAVIOR_VANILLA: | ||
| 115 | true_level += 1 | ||
| 116 | if level == 2 and ap.get_letter_behavior(key, true) == ap.kLETTER_BEHAVIOR_VANILLA: | ||
| 117 | true_level += 1 | ||
| 118 | |||
| 119 | if true_level < letters_saved.get(key, 0): | ||
| 96 | return | 120 | return |
| 97 | 121 | ||
| 98 | letters_saved[key] = level | 122 | letters_saved[key] = true_level |
| 123 | |||
| 124 | ap.client.updateKeyboard({key: true_level}) | ||
| 125 | |||
| 126 | if letters_blocked.has(key): | ||
| 127 | letters_blocked.erase(key) | ||
| 99 | 128 | ||
| 100 | update_unlocks() | 129 | update_unlocks() |
| 101 | save() | 130 | save() |
| @@ -107,6 +136,9 @@ func collect_remote_letter(key, level): | |||
| 107 | 136 | ||
| 108 | letters_dynamic[key] = level | 137 | letters_dynamic[key] = level |
| 109 | 138 | ||
| 139 | if letters_blocked.has(key): | ||
| 140 | letters_blocked.erase(key) | ||
| 141 | |||
| 110 | update_unlocks() | 142 | update_unlocks() |
| 111 | save() | 143 | save() |
| 112 | 144 | ||
| @@ -140,6 +172,13 @@ func remove_from_keyholder(key, map, kh_path): | |||
| 140 | save() | 172 | save() |
| 141 | 173 | ||
| 142 | 174 | ||
| 175 | func block_letter(key): | ||
| 176 | if not letters_blocked.has(key): | ||
| 177 | letters_blocked.append(key) | ||
| 178 | |||
| 179 | update_unlocks() | ||
| 180 | |||
| 181 | |||
| 143 | func load_keyholders(map): | 182 | func load_keyholders(map): |
| 144 | if keyholder_state.has(map): | 183 | if keyholder_state.has(map): |
| 145 | var khs = keyholder_state[map] | 184 | var khs = keyholder_state[map] |
| @@ -152,9 +191,11 @@ func load_keyholders(map): | |||
| 152 | 191 | ||
| 153 | 192 | ||
| 154 | func reset_keyholders(): | 193 | func reset_keyholders(): |
| 155 | if letters_in_keyholders.is_empty(): | 194 | if letters_in_keyholders.is_empty() and letters_blocked.is_empty(): |
| 156 | return false | 195 | return false |
| 157 | 196 | ||
| 197 | var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty() | ||
| 198 | |||
| 158 | if keyholder_state.has(global.map): | 199 | if keyholder_state.has(global.map): |
| 159 | for path in keyholder_state[global.map]: | 200 | for path in keyholder_state[global.map]: |
| 160 | get_tree().get_root().get_node("scene").get_node(path).setFromAp( | 201 | get_tree().get_root().get_node("scene").get_node(path).setFromAp( |
| @@ -163,8 +204,28 @@ func reset_keyholders(): | |||
| 163 | 204 | ||
| 164 | keyholder_state.clear() | 205 | keyholder_state.clear() |
| 165 | letters_in_keyholders.clear() | 206 | letters_in_keyholders.clear() |
| 207 | letters_blocked.clear() | ||
| 166 | 208 | ||
| 167 | update_unlocks() | 209 | update_unlocks() |
| 168 | save() | 210 | save() |
| 169 | 211 | ||
| 170 | return true | 212 | return cleared_anything |
| 213 | |||
| 214 | |||
| 215 | func remote_keyboard_updated(updates): | ||
| 216 | var reverse = {} | ||
| 217 | var should_update = false | ||
| 218 | |||
| 219 | for k in updates: | ||
| 220 | if not letters_saved.has(k) or updates[k] > letters_saved[k]: | ||
| 221 | letters_saved[k] = updates[k] | ||
| 222 | should_update = true | ||
| 223 | elif updates[k] < letters_saved[k]: | ||
| 224 | reverse[k] = letters_saved[k] | ||
| 225 | |||
| 226 | if should_update: | ||
| 227 | update_unlocks() | ||
| 228 | |||
| 229 | if not reverse.is_empty(): | ||
| 230 | var ap = global.get_node("Archipelago") | ||
| 231 | ap.client.updateKeyboard(reverse) | ||
| diff --git a/client/Archipelago/locationListener.gd b/apworld/client/locationListener.gd index 71792ed..71792ed 100644 --- a/client/Archipelago/locationListener.gd +++ b/apworld/client/locationListener.gd | |||
| diff --git a/apworld/client/main.gd b/apworld/client/main.gd new file mode 100644 index 0000000..c90d6e7 --- /dev/null +++ b/apworld/client/main.gd | |||
| @@ -0,0 +1,308 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | |||
| 4 | func _ready(): | ||
| 5 | var runtime = global.get_node("Runtime") | ||
| 6 | |||
| 7 | # Some helpful logging. | ||
| 8 | if Steam.isSubscribed(): | ||
| 9 | global._print("Provisioning successful! Build ID: %d" % Steam.getAppBuildId()) | ||
| 10 | else: | ||
| 11 | global._print("Provisioning failed.") | ||
| 12 | |||
| 13 | # Undo the load screen removing our cursor | ||
| 14 | get_tree().get_root().set_disable_input(false) | ||
| 15 | Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) | ||
| 16 | |||
| 17 | # Increase the WebSocket input buffer size so that we can download large | ||
| 18 | # data packages. | ||
| 19 | ProjectSettings.set_setting("network/limits/websocket_client/max_in_buffer_kb", 8192) | ||
| 20 | |||
| 21 | switcher.layer = 4 | ||
| 22 | |||
| 23 | # Create the global AP manager, if it doesn't already exist. | ||
| 24 | if not global.has_node("Archipelago"): | ||
| 25 | var ap_script = runtime.load_script("manager.gd") | ||
| 26 | var ap_instance = ap_script.new() | ||
| 27 | ap_instance.name = "Archipelago" | ||
| 28 | |||
| 29 | ap_instance.SCRIPT_client = runtime.load_script("client.gd") | ||
| 30 | ap_instance.SCRIPT_keyboard = runtime.load_script("keyboard.gd") | ||
| 31 | ap_instance.SCRIPT_locationListener = runtime.load_script("locationListener.gd") | ||
| 32 | ap_instance.SCRIPT_minimap = runtime.load_script("minimap.gd") | ||
| 33 | ap_instance.SCRIPT_victoryListener = runtime.load_script("victoryListener.gd") | ||
| 34 | ap_instance.SCRIPT_websocketserver = runtime.load_script("vendor/WebSocketServer.gd") | ||
| 35 | |||
| 36 | global.add_child(ap_instance) | ||
| 37 | |||
| 38 | # Let's also inject any scripts we need to inject now. | ||
| 39 | installScriptExtension(runtime.load_script("allowNumbers.gd")) | ||
| 40 | installScriptExtension(runtime.load_script("animationListener.gd")) | ||
| 41 | installScriptExtension(runtime.load_script("collectable.gd")) | ||
| 42 | installScriptExtension(runtime.load_script("door.gd")) | ||
| 43 | installScriptExtension(runtime.load_script("keyHolder.gd")) | ||
| 44 | installScriptExtension(runtime.load_script("keyHolderChecker.gd")) | ||
| 45 | installScriptExtension(runtime.load_script("keyHolderResetterListener.gd")) | ||
| 46 | installScriptExtension(runtime.load_script("painting.gd")) | ||
| 47 | installScriptExtension(runtime.load_script("paintingAuto.gd")) | ||
| 48 | installScriptExtension(runtime.load_script("panel.gd")) | ||
| 49 | installScriptExtension(runtime.load_script("pauseMenu.gd")) | ||
| 50 | installScriptExtension(runtime.load_script("player.gd")) | ||
| 51 | installScriptExtension(runtime.load_script("saver.gd")) | ||
| 52 | installScriptExtension(runtime.load_script("teleport.gd")) | ||
| 53 | installScriptExtension(runtime.load_script("teleportListener.gd")) | ||
| 54 | installScriptExtension(runtime.load_script("unlockReaderListener.gd")) | ||
| 55 | installScriptExtension(runtime.load_script("visibilityListener.gd")) | ||
| 56 | installScriptExtension(runtime.load_script("worldport.gd")) | ||
| 57 | installScriptExtension(runtime.load_script("worldportListener.gd")) | ||
| 58 | |||
| 59 | var proto_script = runtime.load_script("../generated/proto.gd") | ||
| 60 | var gamedata_script = runtime.load_script("gamedata.gd") | ||
| 61 | var gamedata_instance = gamedata_script.new(proto_script) | ||
| 62 | gamedata_instance.load(runtime.read_path("../generated/data.binpb")) | ||
| 63 | gamedata_instance.name = "Gamedata" | ||
| 64 | global.add_child(gamedata_instance) | ||
| 65 | |||
| 66 | var messages_script = runtime.load_script("messages.gd") | ||
| 67 | var messages_instance = messages_script.new() | ||
| 68 | messages_instance.name = "Messages" | ||
| 69 | messages_instance.SCRIPT_rainbowText = runtime.load_script("rainbowText.gd") | ||
| 70 | global.add_child(messages_instance) | ||
| 71 | |||
| 72 | var effects_script = runtime.load_script("effects.gd") | ||
| 73 | var effects_instance = effects_script.new() | ||
| 74 | effects_instance.name = "Effects" | ||
| 75 | global.add_child(effects_instance) | ||
| 76 | |||
| 77 | var textclient_script = runtime.load_script("textclient.gd") | ||
| 78 | var textclient_instance = textclient_script.new() | ||
| 79 | textclient_instance.name = "Textclient" | ||
| 80 | global.add_child(textclient_instance) | ||
| 81 | |||
| 82 | var compass_overlay_script = runtime.load_script("compass_overlay.gd") | ||
| 83 | var compass_overlay_instance = compass_overlay_script.new() | ||
| 84 | compass_overlay_instance.name = "Compass" | ||
| 85 | compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd") | ||
| 86 | global.add_child(compass_overlay_instance) | ||
| 87 | |||
| 88 | unlocks.data["advanced_mastery"] = "" | ||
| 89 | unlocks.data["charismatic_mastery"] = "" | ||
| 90 | unlocks.data["crystalline_mastery"] = "" | ||
| 91 | unlocks.data["fuzzy_mastery"] = "" | ||
| 92 | unlocks.data["icarus_mastery"] = "" | ||
| 93 | unlocks.data["stellar_mastery"] = "" | ||
| 94 | |||
| 95 | var ap = global.get_node("Archipelago") | ||
| 96 | var gamedata = global.get_node("Gamedata") | ||
| 97 | ap.ap_connected.connect(connectionSuccessful) | ||
| 98 | ap.could_not_connect.connect(connectionUnsuccessful) | ||
| 99 | ap.connect_status.connect(connectionStatus) | ||
| 100 | |||
| 101 | # Populate textboxes with AP settings. | ||
| 102 | get_node("../Panel/server_box").text = ap.ap_server | ||
| 103 | get_node("../Panel/player_box").text = ap.ap_user | ||
| 104 | get_node("../Panel/password_box").text = ap.ap_pass | ||
| 105 | |||
| 106 | var history_box = get_node("../Panel/connection_history") | ||
| 107 | if ap.connection_history.is_empty(): | ||
| 108 | history_box.disabled = true | ||
| 109 | else: | ||
| 110 | history_box.disabled = false | ||
| 111 | |||
| 112 | var i = 0 | ||
| 113 | for details in ap.connection_history: | ||
| 114 | history_box.get_popup().add_item("%s (%s)" % [details[1], details[0]], i) | ||
| 115 | i += 1 | ||
| 116 | |||
| 117 | history_box.get_popup().id_pressed.connect(historySelected) | ||
| 118 | |||
| 119 | # Show client version. | ||
| 120 | var version = gamedata.objects.get_version() | ||
| 121 | get_node("../Panel/title").text = ( | ||
| 122 | "ARCHIPELAGO (%d.%d.%d)" % [version.get_major(), version.get_minor(), version.get_patch()] | ||
| 123 | ) | ||
| 124 | |||
| 125 | # Increase font size in text boxes. | ||
| 126 | get_node("../Panel/server_box").add_theme_font_size_override("font_size", 36) | ||
| 127 | get_node("../Panel/player_box").add_theme_font_size_override("font_size", 36) | ||
| 128 | get_node("../Panel/password_box").add_theme_font_size_override("font_size", 36) | ||
| 129 | |||
| 130 | # Set up version mismatch dialog. | ||
| 131 | get_node("../Panel/VersionMismatch").confirmed.connect(startGame) | ||
| 132 | get_node("../Panel/VersionMismatch").get_cancel_button().pressed.connect( | ||
| 133 | versionMismatchDeclined | ||
| 134 | ) | ||
| 135 | |||
| 136 | # Set up buttons. | ||
| 137 | get_node("../Panel/connect_button").pressed.connect(_connect_pressed) | ||
| 138 | get_node("../Panel/quit_button").pressed.connect(_back_pressed) | ||
| 139 | |||
| 140 | |||
| 141 | func _connect_pressed(): | ||
| 142 | get_node("../Panel/connect_button").disabled = true | ||
| 143 | |||
| 144 | var ap = global.get_node("Archipelago") | ||
| 145 | ap.ap_server = get_node("../Panel/server_box").text | ||
| 146 | ap.ap_user = get_node("../Panel/player_box").text | ||
| 147 | ap.ap_pass = get_node("../Panel/password_box").text | ||
| 148 | ap.saveSettings() | ||
| 149 | |||
| 150 | ap.connectToServer() | ||
| 151 | |||
| 152 | |||
| 153 | func _back_pressed(): | ||
| 154 | var ap = global.get_node("Archipelago") | ||
| 155 | ap.disconnect_from_ap() | ||
| 156 | ap.client.sendQuit() | ||
| 157 | |||
| 158 | get_tree().quit() | ||
| 159 | |||
| 160 | |||
| 161 | # Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd | ||
| 162 | func installScriptExtension(childScript: Resource): | ||
| 163 | # Force Godot to compile the script now. | ||
| 164 | # We need to do this here to ensure that the inheritance chain is | ||
| 165 | # properly set up, and multiple mods can chain-extend the same | ||
| 166 | # class multiple times. | ||
| 167 | # This is also needed to make Godot instantiate the extended class | ||
| 168 | # when creating singletons. | ||
| 169 | # The actual instance is thrown away. | ||
| 170 | childScript.new() | ||
| 171 | |||
| 172 | var parentScript = childScript.get_base_script() | ||
| 173 | var parentScriptPath = parentScript.resource_path | ||
| 174 | global._print("ModLoader: Installing script extension over %s" % parentScriptPath) | ||
| 175 | childScript.take_over_path(parentScriptPath) | ||
| 176 | |||
| 177 | |||
| 178 | func connectionStatus(message): | ||
| 179 | var popup = get_node("../Panel/AcceptDialog") | ||
| 180 | popup.title = "Connecting to Archipelago" | ||
| 181 | popup.dialog_text = message | ||
| 182 | popup.exclusive = true | ||
| 183 | popup.get_ok_button().visible = false | ||
| 184 | popup.popup_centered() | ||
| 185 | |||
| 186 | |||
| 187 | func connectionSuccessful(): | ||
| 188 | var ap = global.get_node("Archipelago") | ||
| 189 | var gamedata = global.get_node("Gamedata") | ||
| 190 | |||
| 191 | # Check for major version mismatch. | ||
| 192 | if ap.apworld_version[0] != gamedata.objects.get_version().get_major(): | ||
| 193 | get_node("../Panel/AcceptDialog").exclusive = false | ||
| 194 | |||
| 195 | var popup = get_node("../Panel/VersionMismatch") | ||
| 196 | popup.title = "Version Mismatch!" | ||
| 197 | popup.dialog_text = ( | ||
| 198 | "This slot was generated using v%d.%d.%d of the Lingo 2 apworld,\nwhich has a different major version than this client (v%d.%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." | ||
| 199 | % [ | ||
| 200 | ap.apworld_version[0], | ||
| 201 | ap.apworld_version[1], | ||
| 202 | ap.apworld_version[2], | ||
| 203 | gamedata.objects.get_version().get_major(), | ||
| 204 | gamedata.objects.get_version().get_minor(), | ||
| 205 | gamedata.objects.get_version().get_patch() | ||
| 206 | ] | ||
| 207 | ) | ||
| 208 | popup.exclusive = true | ||
| 209 | popup.popup_centered() | ||
| 210 | |||
| 211 | return | ||
| 212 | |||
| 213 | startGame() | ||
| 214 | |||
| 215 | |||
| 216 | func startGame(): | ||
| 217 | var ap = global.get_node("Archipelago") | ||
| 218 | |||
| 219 | # Save connection details | ||
| 220 | var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass] | ||
| 221 | if ap.connection_history.has(connection_details): | ||
| 222 | ap.connection_history.erase(connection_details) | ||
| 223 | ap.connection_history.push_front(connection_details) | ||
| 224 | if ap.connection_history.size() > 10: | ||
| 225 | ap.connection_history.resize(10) | ||
| 226 | ap.saveSettings() | ||
| 227 | |||
| 228 | # Switch to the_entry | ||
| 229 | Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) | ||
| 230 | global.user = ap.getSaveFileName() | ||
| 231 | global.universe = "lingo" | ||
| 232 | global.map = "the_entry" | ||
| 233 | |||
| 234 | unlocks.resetCollectables() | ||
| 235 | unlocks.resetData() | ||
| 236 | unlocks.loadCollectables() | ||
| 237 | unlocks.loadData() | ||
| 238 | |||
| 239 | ap.setup_keys() | ||
| 240 | |||
| 241 | unlocks.unlockKey("capslock", 1) | ||
| 242 | |||
| 243 | if ap.shuffle_worldports: | ||
| 244 | settings.worldport_fades = "default" | ||
| 245 | else: | ||
| 246 | settings.worldport_fades = "never" | ||
| 247 | |||
| 248 | clearResourceCache("res://objects/meshes/gridDoor.tscn") | ||
| 249 | clearResourceCache("res://objects/nodes/allowNumbers.tscn") | ||
| 250 | clearResourceCache("res://objects/nodes/collectable.tscn") | ||
| 251 | clearResourceCache("res://objects/nodes/door.tscn") | ||
| 252 | clearResourceCache("res://objects/nodes/keyHolder.tscn") | ||
| 253 | clearResourceCache("res://objects/nodes/listeners/animationListener.tscn") | ||
| 254 | clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn") | ||
| 255 | clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn") | ||
| 256 | clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 257 | clearResourceCache("res://objects/nodes/listeners/unlockReaderListener.tscn") | ||
| 258 | clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn") | ||
| 259 | clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn") | ||
| 260 | clearResourceCache("res://objects/nodes/panel.tscn") | ||
| 261 | clearResourceCache("res://objects/nodes/player.tscn") | ||
| 262 | clearResourceCache("res://objects/nodes/saver.tscn") | ||
| 263 | clearResourceCache("res://objects/nodes/teleport.tscn") | ||
| 264 | clearResourceCache("res://objects/nodes/worldport.tscn") | ||
| 265 | clearResourceCache("res://objects/scenes/menus/pause_menu.tscn") | ||
| 266 | |||
| 267 | var paintings_dir = DirAccess.open("res://objects/meshes/paintings") | ||
| 268 | if paintings_dir: | ||
| 269 | paintings_dir.list_dir_begin() | ||
| 270 | var file_name = paintings_dir.get_next() | ||
| 271 | while file_name != "": | ||
| 272 | if not paintings_dir.current_is_dir() and file_name.ends_with(".tscn"): | ||
| 273 | clearResourceCache("res://objects/meshes/paintings/" + file_name) | ||
| 274 | file_name = paintings_dir.get_next() | ||
| 275 | |||
| 276 | switcher.switch_map.call_deferred("res://objects/scenes/the_entry.tscn") | ||
| 277 | |||
| 278 | |||
| 279 | func connectionUnsuccessful(error_message): | ||
| 280 | get_node("../Panel/connect_button").disabled = false | ||
| 281 | |||
| 282 | var popup = get_node("../Panel/AcceptDialog") | ||
| 283 | popup.title = "Could not connect to Archipelago" | ||
| 284 | popup.dialog_text = error_message | ||
| 285 | popup.exclusive = true | ||
| 286 | popup.get_ok_button().visible = true | ||
| 287 | popup.popup_centered() | ||
| 288 | |||
| 289 | |||
| 290 | func versionMismatchDeclined(): | ||
| 291 | get_node("../Panel/AcceptDialog").hide() | ||
| 292 | get_node("../Panel/connect_button").disabled = false | ||
| 293 | |||
| 294 | var ap = global.get_node("Archipelago") | ||
| 295 | ap.disconnect_from_ap() | ||
| 296 | |||
| 297 | |||
| 298 | func historySelected(index): | ||
| 299 | var ap = global.get_node("Archipelago") | ||
| 300 | var details = ap.connection_history[index] | ||
| 301 | |||
| 302 | get_node("../Panel/server_box").text = details[0] | ||
| 303 | get_node("../Panel/player_box").text = details[1] | ||
| 304 | get_node("../Panel/password_box").text = details[2] | ||
| 305 | |||
| 306 | |||
| 307 | func clearResourceCache(path): | ||
| 308 | ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_REPLACE) | ||
| diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd new file mode 100644 index 0000000..8c981f9 --- /dev/null +++ b/apworld/client/manager.gd | |||
| @@ -0,0 +1,717 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | var SCRIPT_client | ||
| 4 | var SCRIPT_keyboard | ||
| 5 | var SCRIPT_locationListener | ||
| 6 | var SCRIPT_minimap | ||
| 7 | var SCRIPT_victoryListener | ||
| 8 | var SCRIPT_websocketserver | ||
| 9 | |||
| 10 | var ap_server = "" | ||
| 11 | var ap_user = "" | ||
| 12 | var ap_pass = "" | ||
| 13 | var connection_history = [] | ||
| 14 | var show_compass = false | ||
| 15 | var show_locations = false | ||
| 16 | var show_minimap = false | ||
| 17 | |||
| 18 | var client | ||
| 19 | var keyboard | ||
| 20 | |||
| 21 | var _localdata_file = "" | ||
| 22 | var _last_new_item = -1 | ||
| 23 | var _batch_locations = false | ||
| 24 | var _held_locations = [] | ||
| 25 | var _held_location_scouts = [] | ||
| 26 | var _location_scouts = {} | ||
| 27 | var _item_locks = {} | ||
| 28 | var _inverse_item_locks = {} | ||
| 29 | var _held_letters = {} | ||
| 30 | var _letters_setup = false | ||
| 31 | var _already_connected = false | ||
| 32 | var _ignored_locations = [] | ||
| 33 | var _map_scripts = {} | ||
| 34 | |||
| 35 | const kSHUFFLE_LETTERS_VANILLA = 0 | ||
| 36 | const kSHUFFLE_LETTERS_UNLOCKED = 1 | ||
| 37 | const kSHUFFLE_LETTERS_PROGRESSIVE = 2 | ||
| 38 | const kSHUFFLE_LETTERS_VANILLA_CYAN = 3 | ||
| 39 | const kSHUFFLE_LETTERS_ITEM_CYAN = 4 | ||
| 40 | |||
| 41 | const kLETTER_BEHAVIOR_VANILLA = 0 | ||
| 42 | const kLETTER_BEHAVIOR_ITEM = 1 | ||
| 43 | const kLETTER_BEHAVIOR_UNLOCKED = 2 | ||
| 44 | |||
| 45 | const kCYAN_DOOR_BEHAVIOR_H2 = 0 | ||
| 46 | const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1 | ||
| 47 | const kCYAN_DOOR_BEHAVIOR_ITEM = 2 | ||
| 48 | |||
| 49 | const kEndingNameByVictoryValue = { | ||
| 50 | 0: "GRAY", | ||
| 51 | 1: "PURPLE", | ||
| 52 | 2: "MINT", | ||
| 53 | 3: "BLACK", | ||
| 54 | 4: "BLUE", | ||
| 55 | 5: "CYAN", | ||
| 56 | 6: "RED", | ||
| 57 | 7: "PLUM", | ||
| 58 | 8: "ORANGE", | ||
| 59 | 9: "GOLD", | ||
| 60 | 10: "YELLOW", | ||
| 61 | 11: "GREEN", | ||
| 62 | 12: "WHITE", | ||
| 63 | } | ||
| 64 | |||
| 65 | var apworld_version = [0, 0, 0] | ||
| 66 | var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 | ||
| 67 | var daedalus_roof_access = false | ||
| 68 | var enable_gift_maps = [] | ||
| 69 | var enable_icarus = false | ||
| 70 | var endings_requirement = 0 | ||
| 71 | var keyholder_sanity = false | ||
| 72 | var masteries_requirement = 0 | ||
| 73 | var port_pairings = {} | ||
| 74 | var shuffle_control_center_colors = false | ||
| 75 | var shuffle_doors = false | ||
| 76 | var shuffle_gallery_paintings = false | ||
| 77 | var shuffle_letters = kSHUFFLE_LETTERS_VANILLA | ||
| 78 | var shuffle_symbols = false | ||
| 79 | var shuffle_worldports = false | ||
| 80 | var strict_cyan_ending = false | ||
| 81 | var strict_purple_ending = false | ||
| 82 | var victory_condition = -1 | ||
| 83 | |||
| 84 | var color_by_material_path = {} | ||
| 85 | |||
| 86 | signal could_not_connect | ||
| 87 | signal connect_status | ||
| 88 | signal ap_connected | ||
| 89 | |||
| 90 | |||
| 91 | func _init(): | ||
| 92 | # Read AP settings from file, if there are any | ||
| 93 | if FileAccess.file_exists("user://ap_settings"): | ||
| 94 | var file = FileAccess.open("user://ap_settings", FileAccess.READ) | ||
| 95 | var data = file.get_var(true) | ||
| 96 | file.close() | ||
| 97 | |||
| 98 | if typeof(data) != TYPE_ARRAY: | ||
| 99 | global._print("AP settings file is corrupted") | ||
| 100 | data = [] | ||
| 101 | |||
| 102 | if data.size() > 0: | ||
| 103 | ap_server = data[0] | ||
| 104 | |||
| 105 | if data.size() > 1: | ||
| 106 | ap_user = data[1] | ||
| 107 | |||
| 108 | if data.size() > 2: | ||
| 109 | ap_pass = data[2] | ||
| 110 | |||
| 111 | if data.size() > 3: | ||
| 112 | connection_history = data[3] | ||
| 113 | |||
| 114 | if data.size() > 4: | ||
| 115 | show_compass = data[4] | ||
| 116 | |||
| 117 | if data.size() > 5: | ||
| 118 | show_locations = data[5] | ||
| 119 | |||
| 120 | if data.size() > 6: | ||
| 121 | show_minimap = data[6] | ||
| 122 | |||
| 123 | # We need to create a mapping from material paths to the original colors of | ||
| 124 | # those materials. We force reload the materials, overwriting any custom | ||
| 125 | # textures, and create the mapping. We then reload the textures in case the | ||
| 126 | # player had a custom one enabled. | ||
| 127 | var directory = DirAccess.open("res://assets/materials") | ||
| 128 | for material_name in directory.get_files(): | ||
| 129 | var material = ResourceLoader.load( | ||
| 130 | "res://assets/materials/" + material_name, "", ResourceLoader.CACHE_MODE_REPLACE | ||
| 131 | ) | ||
| 132 | |||
| 133 | color_by_material_path[material.resource_path] = Color(material.albedo_color) | ||
| 134 | |||
| 135 | settings.load_user_textures() | ||
| 136 | |||
| 137 | |||
| 138 | func _ready(): | ||
| 139 | client = SCRIPT_client.new() | ||
| 140 | client.SCRIPT_websocketserver = SCRIPT_websocketserver | ||
| 141 | |||
| 142 | client.item_received.connect(_process_item) | ||
| 143 | client.location_scout_received.connect(_process_location_scout) | ||
| 144 | client.text_message_received.connect(_process_text_message) | ||
| 145 | client.item_sent_notification.connect(_process_item_sent_notification) | ||
| 146 | client.hint_received.connect(_process_hint_received) | ||
| 147 | client.accessible_locations_updated.connect(_on_accessible_locations_updated) | ||
| 148 | client.checked_locations_updated.connect(_on_checked_locations_updated) | ||
| 149 | client.ignored_locations_updated.connect(_on_ignored_locations_updated) | ||
| 150 | client.hinted_locations_updated.connect(_on_hinted_locations_updated) | ||
| 151 | client.checked_worldports_updated.connect(_on_checked_worldports_updated) | ||
| 152 | client.door_latched.connect(_on_door_latched) | ||
| 153 | |||
| 154 | client.could_not_connect.connect(_client_could_not_connect) | ||
| 155 | client.connect_status.connect(_client_connect_status) | ||
| 156 | client.client_connected.connect(_client_connected) | ||
| 157 | |||
| 158 | add_child(client) | ||
| 159 | |||
| 160 | keyboard = SCRIPT_keyboard.new() | ||
| 161 | add_child(keyboard) | ||
| 162 | client.keyboard_update_received.connect(keyboard.remote_keyboard_updated) | ||
| 163 | |||
| 164 | |||
| 165 | func saveSettings(): | ||
| 166 | # Save the AP settings to disk. | ||
| 167 | var path = "user://ap_settings" | ||
| 168 | var file = FileAccess.open(path, FileAccess.WRITE) | ||
| 169 | |||
| 170 | var data = [ | ||
| 171 | ap_server, | ||
| 172 | ap_user, | ||
| 173 | ap_pass, | ||
| 174 | connection_history, | ||
| 175 | show_compass, | ||
| 176 | show_locations, | ||
| 177 | show_minimap, | ||
| 178 | ] | ||
| 179 | file.store_var(data, true) | ||
| 180 | file.close() | ||
| 181 | |||
| 182 | |||
| 183 | func saveLocaldata(): | ||
| 184 | # Save the MW/slot specific settings to disk. | ||
| 185 | var dir = DirAccess.open("user://") | ||
| 186 | var folder = "archipelago_data" | ||
| 187 | if not dir.dir_exists(folder): | ||
| 188 | dir.make_dir(folder) | ||
| 189 | |||
| 190 | var file = FileAccess.open(_localdata_file, FileAccess.WRITE) | ||
| 191 | |||
| 192 | var data = [ | ||
| 193 | _last_new_item, | ||
| 194 | ] | ||
| 195 | file.store_var(data, true) | ||
| 196 | file.close() | ||
| 197 | |||
| 198 | |||
| 199 | func connectToServer(): | ||
| 200 | _last_new_item = -1 | ||
| 201 | _batch_locations = false | ||
| 202 | _held_locations = [] | ||
| 203 | _held_location_scouts = [] | ||
| 204 | _location_scouts = {} | ||
| 205 | _letters_setup = false | ||
| 206 | _held_letters = {} | ||
| 207 | _already_connected = false | ||
| 208 | |||
| 209 | client.connectToServer(ap_server, ap_user, ap_pass) | ||
| 210 | |||
| 211 | |||
| 212 | func getSaveFileName(): | ||
| 213 | return "zzAP_%s_%d" % [client._seed, client._slot] | ||
| 214 | |||
| 215 | |||
| 216 | func disconnect_from_ap(): | ||
| 217 | _already_connected = false | ||
| 218 | |||
| 219 | var effects = global.get_node("Effects") | ||
| 220 | effects.set_connection_lost(false) | ||
| 221 | |||
| 222 | client.disconnect_from_ap() | ||
| 223 | |||
| 224 | |||
| 225 | func get_item_id_for_door(door_id): | ||
| 226 | return _item_locks.get(door_id, null) | ||
| 227 | |||
| 228 | |||
| 229 | func _process_item(item, amount): | ||
| 230 | var gamedata = global.get_node("Gamedata") | ||
| 231 | |||
| 232 | var item_id = int(item["id"]) | ||
| 233 | var prog_id = null | ||
| 234 | if _inverse_item_locks.has(item_id): | ||
| 235 | for lock in _inverse_item_locks.get(item_id): | ||
| 236 | if lock[1] != amount: | ||
| 237 | continue | ||
| 238 | |||
| 239 | if gamedata.progressive_id_by_ap_id.has(item_id): | ||
| 240 | prog_id = lock[0] | ||
| 241 | |||
| 242 | if gamedata.get_door_map_name(lock[0]) != global.map: | ||
| 243 | continue | ||
| 244 | |||
| 245 | # TODO: fix doors opening from door groups | ||
| 246 | var receivers = gamedata.get_door_receivers(lock[0]) | ||
| 247 | var scene = get_tree().get_root().get_node_or_null("scene") | ||
| 248 | if scene != null: | ||
| 249 | for receiver in receivers: | ||
| 250 | var rnode = scene.get_node_or_null(receiver) | ||
| 251 | if rnode != null: | ||
| 252 | rnode.handleTriggered() | ||
| 253 | |||
| 254 | var letter_id = gamedata.letter_id_by_ap_id.get(item_id, null) | ||
| 255 | if letter_id != null: | ||
| 256 | var letter = gamedata.objects.get_letters()[letter_id] | ||
| 257 | if not letter.has_level2() or not letter.get_level2(): | ||
| 258 | _process_key_item(letter.get_key(), amount) | ||
| 259 | |||
| 260 | if gamedata.symbol_item_ids.has(item_id): | ||
| 261 | var player = get_tree().get_root().get_node_or_null("scene/player") | ||
| 262 | if player != null: | ||
| 263 | player.evaluate_solvability.emit() | ||
| 264 | |||
| 265 | if item_id == gamedata.objects.get_special_ids()["A Job Well Done"]: | ||
| 266 | update_job_well_done_sign() | ||
| 267 | |||
| 268 | if item_id == gamedata.objects.get_special_ids()["Numbers"] and global.map == "the_fuzzy": | ||
| 269 | global.allow_numbers = true | ||
| 270 | |||
| 271 | # Show a message about the item if it's new. | ||
| 272 | if int(item["index"]) > _last_new_item: | ||
| 273 | _last_new_item = int(item["index"]) | ||
| 274 | saveLocaldata() | ||
| 275 | |||
| 276 | var full_item_name = item["text"] | ||
| 277 | if prog_id != null: | ||
| 278 | var door = gamedata.objects.get_doors()[prog_id] | ||
| 279 | full_item_name = "%s (%s)" % [full_item_name, door.get_name()] | ||
| 280 | |||
| 281 | var message | ||
| 282 | if "sender" in item: | ||
| 283 | message = ( | ||
| 284 | "Received %s from %s" | ||
| 285 | % [wrapInItemColorTags(full_item_name, item["flags"]), item["sender"]] | ||
| 286 | ) | ||
| 287 | else: | ||
| 288 | message = "Found %s" % wrapInItemColorTags(full_item_name, item["flags"]) | ||
| 289 | |||
| 290 | if gamedata.anti_trap_ids.has(item): | ||
| 291 | keyboard.block_letter(gamedata.anti_trap_ids[item]) | ||
| 292 | |||
| 293 | global._print(message) | ||
| 294 | |||
| 295 | global.get_node("Messages").showMessage(message) | ||
| 296 | |||
| 297 | |||
| 298 | func _process_item_sent_notification(message): | ||
| 299 | var sentMsg = ( | ||
| 300 | "Sent %s to %s" | ||
| 301 | % [ | ||
| 302 | wrapInItemColorTags(message["item_name"], message["item_flags"]), | ||
| 303 | message["receiver_name"] | ||
| 304 | ] | ||
| 305 | ) | ||
| 306 | #if _hinted_locations.has(message["item"]["location"]): | ||
| 307 | # sentMsg += " ([color=#fafad2]Hinted![/color])" | ||
| 308 | global.get_node("Messages").showMessage(sentMsg) | ||
| 309 | |||
| 310 | |||
| 311 | func _process_hint_received(message): | ||
| 312 | var is_for = "" | ||
| 313 | if message["self"] == 0: | ||
| 314 | is_for = " for %s" % message["receiver_name"] | ||
| 315 | |||
| 316 | global.get_node("Messages").showMessage( | ||
| 317 | ( | ||
| 318 | "Hint: %s%s is on %s" | ||
| 319 | % [ | ||
| 320 | wrapInItemColorTags(message["item_name"], message["item_flags"]), | ||
| 321 | is_for, | ||
| 322 | message["location_name"] | ||
| 323 | ] | ||
| 324 | ) | ||
| 325 | ) | ||
| 326 | |||
| 327 | |||
| 328 | func _process_text_message(message): | ||
| 329 | var parts = [] | ||
| 330 | for message_part in message: | ||
| 331 | if message_part["type"] == "text": | ||
| 332 | parts.append(message_part["text"]) | ||
| 333 | elif message_part["type"] == "player": | ||
| 334 | if message_part["self"] == 1: | ||
| 335 | parts.append("[color=#ee00ee]%s[/color]" % message_part["text"]) | ||
| 336 | else: | ||
| 337 | parts.append("[color=#fafad2]%s[/color]" % message_part["text"]) | ||
| 338 | elif message_part["type"] == "item": | ||
| 339 | parts.append(wrapInItemColorTags(message_part["text"], int(message_part["flags"]))) | ||
| 340 | elif message_part["type"] == "location": | ||
| 341 | parts.append("[color=#00ff7f]%s[/color]" % message_part["text"]) | ||
| 342 | |||
| 343 | var textclient_node = global.get_node("Textclient") | ||
| 344 | if textclient_node != null: | ||
| 345 | textclient_node.parse_printjson("".join(parts)) | ||
| 346 | |||
| 347 | |||
| 348 | func _process_location_scout(location_id, item_name, player_name, flags, for_self): | ||
| 349 | _location_scouts[location_id] = { | ||
| 350 | "item": item_name, "player": player_name, "flags": flags, "for_self": for_self | ||
| 351 | } | ||
| 352 | |||
| 353 | if for_self and flags & 4 != 0: | ||
| 354 | # This is a trap for us, so let's not display it. | ||
| 355 | return | ||
| 356 | |||
| 357 | var gamedata = global.get_node("Gamedata") | ||
| 358 | var map_id = gamedata.map_id_by_name.get(global.map) | ||
| 359 | |||
| 360 | var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null) | ||
| 361 | if letter_id != null: | ||
| 362 | var letter = gamedata.objects.get_letters()[letter_id] | ||
| 363 | var room = gamedata.objects.get_rooms()[letter.get_room_id()] | ||
| 364 | if room.get_map_id() == map_id: | ||
| 365 | var collectable = get_tree().get_root().get_node("scene").get_node_or_null( | ||
| 366 | letter.get_path() | ||
| 367 | ) | ||
| 368 | if collectable != null: | ||
| 369 | collectable.setScoutedText(item_name) | ||
| 370 | |||
| 371 | |||
| 372 | func _on_accessible_locations_updated(): | ||
| 373 | var textclient_node = global.get_node("Textclient") | ||
| 374 | if textclient_node != null: | ||
| 375 | textclient_node.update_locations() | ||
| 376 | |||
| 377 | |||
| 378 | func _on_checked_locations_updated(): | ||
| 379 | var textclient_node = global.get_node("Textclient") | ||
| 380 | if textclient_node != null: | ||
| 381 | textclient_node.update_locations(false) | ||
| 382 | |||
| 383 | |||
| 384 | func _on_checked_worldports_updated(): | ||
| 385 | var textclient_node = global.get_node("Textclient") | ||
| 386 | if textclient_node != null: | ||
| 387 | textclient_node.update_locations() | ||
| 388 | textclient_node.update_worldports() | ||
| 389 | |||
| 390 | |||
| 391 | func _on_ignored_locations_updated(locations): | ||
| 392 | _ignored_locations = locations | ||
| 393 | |||
| 394 | var textclient_node = global.get_node("Textclient") | ||
| 395 | if textclient_node != null: | ||
| 396 | textclient_node.update_locations() | ||
| 397 | |||
| 398 | |||
| 399 | func _on_hinted_locations_updated(): | ||
| 400 | var textclient_node = global.get_node("Textclient") | ||
| 401 | if textclient_node != null: | ||
| 402 | textclient_node.update_locations() | ||
| 403 | |||
| 404 | |||
| 405 | func _on_door_latched(door_id): | ||
| 406 | var gamedata = global.get_node("Gamedata") | ||
| 407 | if gamedata.get_door_map_name(door_id) != global.map: | ||
| 408 | return | ||
| 409 | |||
| 410 | var receivers = gamedata.get_door_receivers(door_id) | ||
| 411 | var scene = get_tree().get_root().get_node_or_null("scene") | ||
| 412 | if scene != null: | ||
| 413 | for receiver in receivers: | ||
| 414 | var rnode = scene.get_node_or_null(receiver) | ||
| 415 | if rnode != null: | ||
| 416 | rnode.handleTriggered() | ||
| 417 | |||
| 418 | |||
| 419 | func _client_could_not_connect(message): | ||
| 420 | could_not_connect.emit(message) | ||
| 421 | |||
| 422 | if global.loaded: | ||
| 423 | var effects = global.get_node("Effects") | ||
| 424 | effects.set_connection_lost(true) | ||
| 425 | |||
| 426 | var messages = global.get_node("Messages") | ||
| 427 | messages.showMessage("Connection to multiworld lost.") | ||
| 428 | |||
| 429 | |||
| 430 | func _client_connect_status(message): | ||
| 431 | connect_status.emit(message) | ||
| 432 | |||
| 433 | |||
| 434 | func _client_connected(slot_data): | ||
| 435 | var effects = global.get_node("Effects") | ||
| 436 | effects.set_connection_lost(false) | ||
| 437 | |||
| 438 | if _already_connected: | ||
| 439 | var messages = global.get_node("Messages") | ||
| 440 | messages.showMessage("Reconnected to multiworld!") | ||
| 441 | return | ||
| 442 | |||
| 443 | _already_connected = true | ||
| 444 | |||
| 445 | var gamedata = global.get_node("Gamedata") | ||
| 446 | |||
| 447 | _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot] | ||
| 448 | _last_new_item = -1 | ||
| 449 | |||
| 450 | if FileAccess.file_exists(_localdata_file): | ||
| 451 | var ap_file = FileAccess.open(_localdata_file, FileAccess.READ) | ||
| 452 | var localdata = [] | ||
| 453 | if ap_file != null: | ||
| 454 | localdata = ap_file.get_var(true) | ||
| 455 | ap_file.close() | ||
| 456 | |||
| 457 | if typeof(localdata) != TYPE_ARRAY: | ||
| 458 | print("AP localdata file is corrupted") | ||
| 459 | localdata = [] | ||
| 460 | |||
| 461 | if localdata.size() > 0: | ||
| 462 | _last_new_item = localdata[0] | ||
| 463 | |||
| 464 | # Read slot data. | ||
| 465 | cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0)) | ||
| 466 | daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) | ||
| 467 | enable_gift_maps = slot_data.get("enable_gift_maps", []) | ||
| 468 | enable_icarus = bool(slot_data.get("enable_icarus", false)) | ||
| 469 | endings_requirement = int(slot_data.get("endings_requirement", 0)) | ||
| 470 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) | ||
| 471 | masteries_requirement = int(slot_data.get("masteries_requirement", 0)) | ||
| 472 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) | ||
| 473 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) | ||
| 474 | shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false)) | ||
| 475 | shuffle_letters = int(slot_data.get("shuffle_letters", 0)) | ||
| 476 | shuffle_symbols = bool(slot_data.get("shuffle_symbols", false)) | ||
| 477 | shuffle_worldports = bool(slot_data.get("shuffle_worldports", false)) | ||
| 478 | strict_cyan_ending = bool(slot_data.get("strict_cyan_ending", false)) | ||
| 479 | strict_purple_ending = bool(slot_data.get("strict_purple_ending", false)) | ||
| 480 | victory_condition = int(slot_data.get("victory_condition", 0)) | ||
| 481 | |||
| 482 | if slot_data.has("version"): | ||
| 483 | var version_msg = slot_data["version"] | ||
| 484 | apworld_version = [int(version_msg[0]), int(version_msg[1]), 0] | ||
| 485 | if version_msg.size() > 2: | ||
| 486 | apworld_version[2] = int(version_msg[2]) | ||
| 487 | |||
| 488 | port_pairings.clear() | ||
| 489 | if slot_data.has("port_pairings"): | ||
| 490 | var raw_pp = slot_data.get("port_pairings") | ||
| 491 | |||
| 492 | for p1 in raw_pp.keys(): | ||
| 493 | port_pairings[gamedata.port_id_by_ap_id[int(p1)]] = gamedata.port_id_by_ap_id[int( | ||
| 494 | raw_pp[p1] | ||
| 495 | )] | ||
| 496 | |||
| 497 | # Set up item locks. | ||
| 498 | _item_locks = {} | ||
| 499 | |||
| 500 | if shuffle_doors: | ||
| 501 | for door in gamedata.objects.get_doors(): | ||
| 502 | if ( | ||
| 503 | door.get_type() == gamedata.SCRIPT_proto.DoorType.STANDARD | ||
| 504 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY | ||
| 505 | ): | ||
| 506 | _item_locks[door.get_id()] = [door.get_ap_id(), 1] | ||
| 507 | |||
| 508 | for progressive in gamedata.objects.get_progressives(): | ||
| 509 | for i in range(0, progressive.get_doors().size()): | ||
| 510 | var door = gamedata.objects.get_doors()[progressive.get_doors()[i]] | ||
| 511 | _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1] | ||
| 512 | |||
| 513 | for door_group in gamedata.objects.get_door_groups(): | ||
| 514 | if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR: | ||
| 515 | if shuffle_worldports: | ||
| 516 | continue | ||
| 517 | elif door_group.get_type() != gamedata.SCRIPT_proto.DoorGroupType.SHUFFLE_GROUP: | ||
| 518 | continue | ||
| 519 | |||
| 520 | for door in door_group.get_doors(): | ||
| 521 | _item_locks[door] = [door_group.get_ap_id(), 1] | ||
| 522 | |||
| 523 | if shuffle_control_center_colors: | ||
| 524 | for door in gamedata.objects.get_doors(): | ||
| 525 | if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR: | ||
| 526 | _item_locks[door.get_id()] = [door.get_ap_id(), 1] | ||
| 527 | |||
| 528 | for door_group in gamedata.objects.get_door_groups(): | ||
| 529 | if ( | ||
| 530 | door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR | ||
| 531 | and not shuffle_worldports | ||
| 532 | ): | ||
| 533 | for door in door_group.get_doors(): | ||
| 534 | _item_locks[door] = [door_group.get_ap_id(), 1] | ||
| 535 | |||
| 536 | if shuffle_gallery_paintings: | ||
| 537 | for door in gamedata.objects.get_doors(): | ||
| 538 | if door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING: | ||
| 539 | _item_locks[door.get_id()] = [door.get_ap_id(), 1] | ||
| 540 | |||
| 541 | if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM: | ||
| 542 | for door_group in gamedata.objects.get_door_groups(): | ||
| 543 | if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS: | ||
| 544 | for door in door_group.get_doors(): | ||
| 545 | if not _item_locks.has(door): | ||
| 546 | _item_locks[door] = [door_group.get_ap_id(), 1] | ||
| 547 | |||
| 548 | # Create a reverse item locks map for processing items. | ||
| 549 | _inverse_item_locks = {} | ||
| 550 | |||
| 551 | for door_id in _item_locks.keys(): | ||
| 552 | var lock = _item_locks.get(door_id) | ||
| 553 | |||
| 554 | if not _inverse_item_locks.has(lock[0]): | ||
| 555 | _inverse_item_locks[lock[0]] = [] | ||
| 556 | |||
| 557 | _inverse_item_locks[lock[0]].append([door_id, lock[1]]) | ||
| 558 | |||
| 559 | if shuffle_worldports: | ||
| 560 | var textclient = global.get_node("Textclient") | ||
| 561 | textclient.setup_worldports() | ||
| 562 | |||
| 563 | ap_connected.emit() | ||
| 564 | |||
| 565 | |||
| 566 | func start_batching_locations(): | ||
| 567 | _batch_locations = true | ||
| 568 | |||
| 569 | |||
| 570 | func send_location(loc_id): | ||
| 571 | if client._checked_locations.has(loc_id): | ||
| 572 | return | ||
| 573 | |||
| 574 | if _batch_locations: | ||
| 575 | _held_locations.append(loc_id) | ||
| 576 | else: | ||
| 577 | client.sendLocation(loc_id) | ||
| 578 | |||
| 579 | |||
| 580 | func scout_location(loc_id): | ||
| 581 | if _location_scouts.has(loc_id): | ||
| 582 | return _location_scouts.get(loc_id) | ||
| 583 | |||
| 584 | if _batch_locations: | ||
| 585 | _held_location_scouts.append(loc_id) | ||
| 586 | else: | ||
| 587 | client.scoutLocation(loc_id) | ||
| 588 | |||
| 589 | return null | ||
| 590 | |||
| 591 | |||
| 592 | func stop_batching_locations(): | ||
| 593 | _batch_locations = false | ||
| 594 | |||
| 595 | if not _held_locations.is_empty(): | ||
| 596 | client.sendLocations(_held_locations) | ||
| 597 | _held_locations.clear() | ||
| 598 | |||
| 599 | if not _held_location_scouts.is_empty(): | ||
| 600 | client.scoutLocations(_held_location_scouts) | ||
| 601 | _held_location_scouts.clear() | ||
| 602 | |||
| 603 | |||
| 604 | func colorForItemType(flags): | ||
| 605 | var int_flags = int(flags) | ||
| 606 | if int_flags & 1: # progression | ||
| 607 | if int_flags & 2: # proguseful | ||
| 608 | return "#f0d200" | ||
| 609 | else: | ||
| 610 | return "#bc51e0" | ||
| 611 | elif int_flags & 2: # useful | ||
| 612 | return "#2b67ff" | ||
| 613 | elif int_flags & 4: # trap | ||
| 614 | return "#d63a22" | ||
| 615 | else: # filler | ||
| 616 | return "#14de9e" | ||
| 617 | |||
| 618 | |||
| 619 | func wrapInItemColorTags(text, flags): | ||
| 620 | var int_flags = int(flags) | ||
| 621 | if int_flags & 1 and int_flags & 2: # proguseful | ||
| 622 | return "[rainbow]%s[/rainbow]" % text | ||
| 623 | else: | ||
| 624 | return "[color=%s]%s[/color]" % [colorForItemType(flags), text] | ||
| 625 | |||
| 626 | |||
| 627 | func get_letter_behavior(key, level2): | ||
| 628 | if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED: | ||
| 629 | return kLETTER_BEHAVIOR_UNLOCKED | ||
| 630 | |||
| 631 | if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters): | ||
| 632 | if level2: | ||
| 633 | if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN: | ||
| 634 | return kLETTER_BEHAVIOR_VANILLA | ||
| 635 | else: | ||
| 636 | return kLETTER_BEHAVIOR_ITEM | ||
| 637 | else: | ||
| 638 | return kLETTER_BEHAVIOR_UNLOCKED | ||
| 639 | |||
| 640 | if not level2 and ["h", "i", "n", "t"].has(key): | ||
| 641 | # This differs from the equivalent function in the apworld. Logically it is | ||
| 642 | # the same as UNLOCKED since they are in the starting room, but VANILLA | ||
| 643 | # means the player still has to actually pick up the letters. | ||
| 644 | return kLETTER_BEHAVIOR_VANILLA | ||
| 645 | |||
| 646 | if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE: | ||
| 647 | return kLETTER_BEHAVIOR_ITEM | ||
| 648 | |||
| 649 | return kLETTER_BEHAVIOR_VANILLA | ||
| 650 | |||
| 651 | |||
| 652 | func setup_keys(): | ||
| 653 | keyboard.load_seed() | ||
| 654 | |||
| 655 | _letters_setup = true | ||
| 656 | |||
| 657 | for k in _held_letters.keys(): | ||
| 658 | _process_key_item(k, _held_letters[k]) | ||
| 659 | |||
| 660 | _held_letters.clear() | ||
| 661 | |||
| 662 | |||
| 663 | func _process_key_item(key, level): | ||
| 664 | if not _letters_setup: | ||
| 665 | _held_letters[key] = max(_held_letters.get(key, 0), level) | ||
| 666 | return | ||
| 667 | |||
| 668 | if shuffle_letters == kSHUFFLE_LETTERS_ITEM_CYAN: | ||
| 669 | level += 1 | ||
| 670 | |||
| 671 | keyboard.collect_remote_letter(key, level) | ||
| 672 | |||
| 673 | |||
| 674 | func update_job_well_done_sign(): | ||
| 675 | if global.map != "daedalus": | ||
| 676 | return | ||
| 677 | |||
| 678 | var gamedata = global.get_node("Gamedata") | ||
| 679 | var job_item = gamedata.objects.get_special_ids()["A Job Well Done"] | ||
| 680 | var jobs_done = client.getItemAmount(job_item) | ||
| 681 | |||
| 682 | var sign2 = get_tree().get_root().get_node_or_null("scene/Meshes/Miscellaneous/sign2") | ||
| 683 | var sign3 = get_tree().get_root().get_node_or_null("scene/Meshes/Miscellaneous/sign3") | ||
| 684 | |||
| 685 | if sign2 != null and sign3 != null: | ||
| 686 | if jobs_done == 0: | ||
| 687 | sign2.text = "what are you doing" | ||
| 688 | sign3.text = "?" | ||
| 689 | elif jobs_done == 1: | ||
| 690 | sign2.text = "a job well done" | ||
| 691 | sign3.text = "is its own reward" | ||
| 692 | else: | ||
| 693 | sign2.text = "%d jobs well done" % jobs_done | ||
| 694 | sign3.text = "are their own reward" | ||
| 695 | |||
| 696 | sign2.get_node("MeshInstance3D").mesh.text = sign2.text | ||
| 697 | sign3.get_node("MeshInstance3D").mesh.text = sign3.text | ||
| 698 | |||
| 699 | |||
| 700 | func toggle_ignored_location(loc_id): | ||
| 701 | if loc_id in _ignored_locations: | ||
| 702 | client.removeIgnoredLocation(loc_id) | ||
| 703 | else: | ||
| 704 | client.addIgnoredLocation(loc_id) | ||
| 705 | |||
| 706 | |||
| 707 | func get_map_script(map_name): | ||
| 708 | if !_map_scripts.has(map_name): | ||
| 709 | var runtime = global.get_node("Runtime") | ||
| 710 | var script_path = "maps/%s.gd" % map_name | ||
| 711 | if runtime.path_exists(script_path): | ||
| 712 | var script = runtime.load_script(script_path) | ||
| 713 | _map_scripts[map_name] = script.new() | ||
| 714 | else: | ||
| 715 | _map_scripts[map_name] = null | ||
| 716 | |||
| 717 | return _map_scripts[map_name] | ||
| diff --git a/apworld/client/maps/control_center.gd b/apworld/client/maps/control_center.gd new file mode 100644 index 0000000..de9ae4b --- /dev/null +++ b/apworld/client/maps/control_center.gd | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | var ap = global.get_node("Archipelago") | ||
| 3 | |||
| 4 | # Remove the door blocking the trophy case. | ||
| 5 | root.get_node("/root/scene/Components/Doors/entry_18").queue_free() | ||
| 6 | |||
| 7 | # Set up mastery listeners for extra maps. | ||
| 8 | _set_up_mastery_listener(root, "advanced") | ||
| 9 | _set_up_mastery_listener(root, "charismatic") | ||
| 10 | _set_up_mastery_listener(root, "crystalline") | ||
| 11 | _set_up_mastery_listener(root, "fuzzy") | ||
| 12 | _set_up_mastery_listener(root, "icarus") | ||
| 13 | _set_up_mastery_listener(root, "stellar") | ||
| 14 | |||
| 15 | if ap.endings_requirement != 12 or ap.masteries_requirement != 0: | ||
| 16 | # Set up listeners for the potential White Ending requirements. | ||
| 17 | var merging_prefab = preload("res://objects/nodes/listeners/mergingListener.tscn") | ||
| 18 | |||
| 19 | var old_door = root.get_node("/root/scene/Components/Doors/entry_19") | ||
| 20 | var new_door = old_door.duplicate() | ||
| 21 | new_door.name = "entry_19_new" | ||
| 22 | new_door.senders.clear() | ||
| 23 | new_door.senderGroup.clear() | ||
| 24 | new_door.excludeSenders.clear() | ||
| 25 | |||
| 26 | if ap.endings_requirement == 12: | ||
| 27 | new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners")) | ||
| 28 | elif ap.endings_requirement > 0: | ||
| 29 | if ap.masteries_requirement == 0: | ||
| 30 | new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners")) | ||
| 31 | new_door.complete_at = ap.endings_requirement | ||
| 32 | else: | ||
| 33 | var endings_merge = merging_prefab.instantiate() | ||
| 34 | endings_merge.name = "EndingsMerge" | ||
| 35 | endings_merge.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners")) | ||
| 36 | endings_merge.complete_at = ap.endings_requirement | ||
| 37 | root.get_node("/root/scene/Components").add_child.call_deferred(endings_merge) | ||
| 38 | new_door.senders.append(NodePath("/root/scene/Components/EndingsMerge")) | ||
| 39 | |||
| 40 | var max_masteries = 13 + ap.enable_gift_maps.size() | ||
| 41 | if ap.enable_icarus: | ||
| 42 | max_masteries += 1 | ||
| 43 | |||
| 44 | if ap.masteries_requirement == max_masteries: | ||
| 45 | new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/MasteryListeners")) | ||
| 46 | new_door.excludeSenders.append( | ||
| 47 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite") | ||
| 48 | ) | ||
| 49 | elif ap.masteries_requirement > 0: | ||
| 50 | if ap.endings_requirement == 0: | ||
| 51 | new_door.senderGroup.append( | ||
| 52 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners") | ||
| 53 | ) | ||
| 54 | new_door.excludeSenders.append( | ||
| 55 | NodePath( | ||
| 56 | "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite" | ||
| 57 | ) | ||
| 58 | ) | ||
| 59 | new_door.complete_at = ap.masteries_requirement | ||
| 60 | else: | ||
| 61 | var masteries_merge = merging_prefab.instantiate() | ||
| 62 | masteries_merge.name = "MasteriesMerge" | ||
| 63 | masteries_merge.senderGroup.append( | ||
| 64 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners") | ||
| 65 | ) | ||
| 66 | masteries_merge.excludeSenders.append( | ||
| 67 | NodePath( | ||
| 68 | "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite" | ||
| 69 | ) | ||
| 70 | ) | ||
| 71 | masteries_merge.complete_at = ap.masteries_requirement | ||
| 72 | root.get_node("/root/scene/Components").add_child.call_deferred(masteries_merge) | ||
| 73 | new_door.senders.append(NodePath("/root/scene/Components/MasteriesMerge")) | ||
| 74 | |||
| 75 | old_door.queue_free() | ||
| 76 | root.get_node("/root/scene/Components/Doors").add_child.call_deferred(new_door) | ||
| 77 | |||
| 78 | |||
| 79 | func _set_up_mastery_listener(root, name): | ||
| 80 | var prefab = preload("res://objects/nodes/listeners/unlockReaderListener.tscn") | ||
| 81 | var url = prefab.instantiate() | ||
| 82 | url.name = "unlockReaderListenerMastery_%s" % name | ||
| 83 | url.key = "%s_mastery" % name | ||
| 84 | url.value = "unlocked" | ||
| 85 | root.get_node("/root/scene/Meshes/Trophies/MasteryListeners").add_child.call_deferred(url) | ||
| diff --git a/apworld/client/maps/daedalus.gd b/apworld/client/maps/daedalus.gd new file mode 100644 index 0000000..5fcf7a5 --- /dev/null +++ b/apworld/client/maps/daedalus.gd | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | var ap = global.get_node("Archipelago") | ||
| 3 | |||
| 4 | # Teleport the direction panels when the stairs are there. | ||
| 5 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 6 | |||
| 7 | var dir1 = root.get_node("/root/scene/Panels/Castle Entrance/castle_direction_1") | ||
| 8 | var dir1_tpl = tpl_prefab.instantiate() | ||
| 9 | dir1_tpl.target_path = dir1 | ||
| 10 | dir1_tpl.teleport_point = Vector3(59.5, 8, -6.5) | ||
| 11 | dir1_tpl.teleport_rotate = Vector3(-45, 0, 0) | ||
| 12 | dir1_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_south")) | ||
| 13 | dir1_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_north")) | ||
| 14 | dir1_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_west")) | ||
| 15 | dir1.add_child.call_deferred(dir1_tpl) | ||
| 16 | |||
| 17 | var dir2 = root.get_node("/root/scene/Panels/Castle Entrance/castle_direction_2") | ||
| 18 | var dir2_tpl = tpl_prefab.instantiate() | ||
| 19 | dir2_tpl.target_path = dir2 | ||
| 20 | dir2_tpl.teleport_point = Vector3(59.5, 8, 6.5) | ||
| 21 | dir2_tpl.teleport_rotate = Vector3(-45, -180, 0) | ||
| 22 | dir2_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_south")) | ||
| 23 | dir2_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_north")) | ||
| 24 | dir2_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_west")) | ||
| 25 | dir2.add_child.call_deferred(dir2_tpl) | ||
| 26 | |||
| 27 | var dir3 = root.get_node("/root/scene/Panels/Castle Entrance/castle_direction_3") | ||
| 28 | var dir3_tpl = tpl_prefab.instantiate() | ||
| 29 | dir3_tpl.target_path = dir3 | ||
| 30 | dir3_tpl.teleport_point = Vector3(54, 8, 0) | ||
| 31 | dir3_tpl.teleport_rotate = Vector3(-45, 90, 0) | ||
| 32 | dir3_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_south")) | ||
| 33 | dir3_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_north")) | ||
| 34 | dir3_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_west")) | ||
| 35 | dir3.add_child.call_deferred(dir3_tpl) | ||
| 36 | |||
| 37 | # Block off roof access in Daedalus. | ||
| 38 | if not ap.daedalus_roof_access: | ||
| 39 | _set_up_invis_wall(root, 75.5, 11, -24.5, 1, 10, 49) | ||
| 40 | _set_up_invis_wall(root, 51.5, 11, -17, 16, 10, 1) | ||
| 41 | _set_up_invis_wall(root, 46, 10, -9.5, 1, 10, 10) | ||
| 42 | _set_up_invis_wall(root, 67.5, 11, 17, 16, 10, 1) | ||
| 43 | _set_up_invis_wall(root, 50.5, 11, 14, 10, 10, 1) | ||
| 44 | _set_up_invis_wall(root, 39, 10, 18.5, 1, 10, 22) | ||
| 45 | _set_up_invis_wall(root, 20, 15, 18.5, 1, 10, 16) | ||
| 46 | _set_up_invis_wall(root, 11.5, 15, 3, 32, 10, 1) | ||
| 47 | _set_up_invis_wall(root, 11.5, 16, -20, 14, 20, 1) | ||
| 48 | _set_up_invis_wall(root, 14, 16, -26.5, 1, 20, 4) | ||
| 49 | _set_up_invis_wall(root, 28.5, 20.5, -26.5, 1, 15, 25) | ||
| 50 | _set_up_invis_wall(root, 40.5, 20.5, -11, 30, 15, 1) | ||
| 51 | _set_up_invis_wall(root, 50.5, 15, 5.5, 7, 10, 1) | ||
| 52 | _set_up_invis_wall(root, 83.5, 33.5, 5.5, 1, 7, 11) | ||
| 53 | _set_up_invis_wall(root, 83.5, 33.5, -5.5, 1, 7, 11) | ||
| 54 | |||
| 55 | var warp_exit_prefab = preload("res://objects/nodes/exit.tscn") | ||
| 56 | var warp_exit = warp_exit_prefab.instantiate() | ||
| 57 | warp_exit.name = "roof_access_blocker_warp_exit" | ||
| 58 | warp_exit.position = Vector3(58, 10, 0) | ||
| 59 | warp_exit.rotation_degrees.y = 90 | ||
| 60 | root.get_node("/root/scene").add_child.call_deferred(warp_exit) | ||
| 61 | |||
| 62 | var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn") | ||
| 63 | var warp_enter = warp_enter_prefab.instantiate() | ||
| 64 | warp_enter.target = warp_exit | ||
| 65 | warp_enter.position = Vector3(76.5, 30, 1) | ||
| 66 | warp_enter.scale = Vector3(4, 1.5, 1) | ||
| 67 | warp_enter.rotation_degrees.y = 90 | ||
| 68 | root.get_node("/root/scene").add_child.call_deferred(warp_enter) | ||
| 69 | |||
| 70 | |||
| 71 | func _set_up_invis_wall(root, x, y, z, sx, sy, sz): | ||
| 72 | var prefab = preload("res://objects/nodes/block.tscn") | ||
| 73 | var newwall = prefab.instantiate() | ||
| 74 | newwall.position.x = x | ||
| 75 | newwall.position.y = y | ||
| 76 | newwall.position.z = z | ||
| 77 | newwall.scale.x = sz | ||
| 78 | newwall.scale.y = sy | ||
| 79 | newwall.scale.z = sx | ||
| 80 | newwall.set_surface_override_material(0, preload("res://assets/materials/blackMatte.material")) | ||
| 81 | newwall.visibility_range_end = 3 | ||
| 82 | newwall.visibility_range_end_margin = 1 | ||
| 83 | newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF | ||
| 84 | newwall.skeleton = ".." | ||
| 85 | root.get_node("/root/scene").add_child.call_deferred(newwall) | ||
| diff --git a/apworld/client/maps/icarus.gd b/apworld/client/maps/icarus.gd new file mode 100644 index 0000000..ad00741 --- /dev/null +++ b/apworld/client/maps/icarus.gd | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | var ap = global.get_node("Archipelago") | ||
| 3 | |||
| 4 | # Add the mastery to Icarus. | ||
| 5 | if ap.enable_icarus: | ||
| 6 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
| 7 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
| 8 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 9 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 10 | |||
| 11 | var mastery = collectable_prefab.instantiate() | ||
| 12 | mastery.name = "collectable" | ||
| 13 | mastery.position = Vector3(0, -2000, 0) | ||
| 14 | mastery.unlock_type = "smiley" | ||
| 15 | mastery.material_override = load("res://assets/materials/gold.material") | ||
| 16 | root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
| 17 | |||
| 18 | var tpl = tpl_prefab.instantiate() | ||
| 19 | tpl.teleport_point = Vector3(56.25, 0, -5.5) | ||
| 20 | tpl.teleport_rotate = Vector3(0, 0, 0) | ||
| 21 | tpl.target_path = mastery | ||
| 22 | tpl.name = "Teleport" | ||
| 23 | tpl.senderGroup.append(NodePath("/root/scene/Panels")) | ||
| 24 | tpl.nested = true | ||
| 25 | mastery.add_child.call_deferred(tpl) | ||
| 26 | |||
| 27 | var usl = usl_prefab.instantiate() | ||
| 28 | usl.name = "unlockSetterListenerMastery" | ||
| 29 | usl.key = "icarus_mastery" | ||
| 30 | usl.value = "unlocked" | ||
| 31 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 32 | root.get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 33 | |||
| 34 | var saver = saver_prefab.instantiate() | ||
| 35 | saver.name = "saver_collectables" | ||
| 36 | saver.type = "collectables" | ||
| 37 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
| 38 | root.get_node("/root/scene").add_child.call_deferred(saver) | ||
| diff --git a/apworld/client/maps/the_advanced.gd b/apworld/client/maps/the_advanced.gd new file mode 100644 index 0000000..b41549c --- /dev/null +++ b/apworld/client/maps/the_advanced.gd | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | # Add the mastery to The Advanced. | ||
| 3 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
| 4 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
| 5 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 6 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 7 | |||
| 8 | var mastery = collectable_prefab.instantiate() | ||
| 9 | mastery.name = "collectable" | ||
| 10 | mastery.position = Vector3(0, -200, -5) | ||
| 11 | mastery.unlock_type = "smiley" | ||
| 12 | mastery.material_override = load("res://assets/materials/gold.material") | ||
| 13 | root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
| 14 | |||
| 15 | var tpl = tpl_prefab.instantiate() | ||
| 16 | tpl.teleport_point = Vector3(0, 2, -5) | ||
| 17 | tpl.teleport_rotate = Vector3(0, 0, 0) | ||
| 18 | tpl.target_path = mastery | ||
| 19 | tpl.name = "Teleport" | ||
| 20 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_29")) | ||
| 21 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_30")) | ||
| 22 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_31")) | ||
| 23 | mastery.add_child.call_deferred(tpl) | ||
| 24 | |||
| 25 | var usl = usl_prefab.instantiate() | ||
| 26 | usl.name = "unlockSetterListenerMastery" | ||
| 27 | usl.key = "advanced_mastery" | ||
| 28 | usl.value = "unlocked" | ||
| 29 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 30 | root.get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 31 | |||
| 32 | var saver = saver_prefab.instantiate() | ||
| 33 | saver.name = "saver_collectables" | ||
| 34 | saver.type = "collectables" | ||
| 35 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
| 36 | root.get_node("/root/scene").add_child.call_deferred(saver) | ||
| diff --git a/apworld/client/maps/the_charismatic.gd b/apworld/client/maps/the_charismatic.gd new file mode 100644 index 0000000..734001d --- /dev/null +++ b/apworld/client/maps/the_charismatic.gd | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | # Add the mastery to The Charismatic. | ||
| 3 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
| 4 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
| 5 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 6 | |||
| 7 | var mastery = collectable_prefab.instantiate() | ||
| 8 | mastery.name = "collectable" | ||
| 9 | mastery.position = Vector3(-17, 2, -29) | ||
| 10 | mastery.rotation_degrees = Vector3(0, 45, 0) | ||
| 11 | mastery.unlock_type = "smiley" | ||
| 12 | mastery.material_override = load("res://assets/materials/gold.material") | ||
| 13 | root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
| 14 | |||
| 15 | var usl = usl_prefab.instantiate() | ||
| 16 | usl.name = "unlockSetterListenerMastery" | ||
| 17 | usl.key = "charismatic_mastery" | ||
| 18 | usl.value = "unlocked" | ||
| 19 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 20 | root.get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 21 | |||
| 22 | var saver = saver_prefab.instantiate() | ||
| 23 | saver.name = "saver_collectables" | ||
| 24 | saver.type = "collectables" | ||
| 25 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
| 26 | root.get_node("/root/scene").add_child.call_deferred(saver) | ||
| diff --git a/apworld/client/maps/the_crystalline.gd b/apworld/client/maps/the_crystalline.gd new file mode 100644 index 0000000..7d43e78 --- /dev/null +++ b/apworld/client/maps/the_crystalline.gd | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | # Add the mastery to The Crystalline. | ||
| 3 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
| 4 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
| 5 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 6 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 7 | |||
| 8 | var mastery = collectable_prefab.instantiate() | ||
| 9 | mastery.name = "collectable" | ||
| 10 | mastery.position = Vector3(0, 13, 37) | ||
| 11 | mastery.unlock_type = "smiley" | ||
| 12 | mastery.material_override = load("res://assets/materials/gold.material") | ||
| 13 | root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
| 14 | |||
| 15 | var tpl = tpl_prefab.instantiate() | ||
| 16 | tpl.teleport_point = Vector3(0, 11.5, -20) | ||
| 17 | tpl.teleport_rotate = Vector3(0, 0, 180) | ||
| 18 | tpl.target_path = mastery | ||
| 19 | tpl.name = "Teleport" | ||
| 20 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_3")) | ||
| 21 | mastery.add_child.call_deferred(tpl) | ||
| 22 | |||
| 23 | var usl = usl_prefab.instantiate() | ||
| 24 | usl.name = "unlockSetterListenerMastery" | ||
| 25 | usl.key = "crystalline_mastery" | ||
| 26 | usl.value = "unlocked" | ||
| 27 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 28 | root.get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 29 | |||
| 30 | var saver = saver_prefab.instantiate() | ||
| 31 | saver.name = "saver_collectables" | ||
| 32 | saver.type = "collectables" | ||
| 33 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
| 34 | root.get_node("/root/scene").add_child.call_deferred(saver) | ||
| diff --git a/apworld/client/maps/the_entry.gd b/apworld/client/maps/the_entry.gd new file mode 100644 index 0000000..3608bb3 --- /dev/null +++ b/apworld/client/maps/the_entry.gd | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | var ap = global.get_node("Archipelago") | ||
| 3 | |||
| 4 | # Remove door behind X1. | ||
| 5 | var door_node = root.get_node("/root/scene/Components/Doors/exit_1") | ||
| 6 | door_node.handleTriggered() | ||
| 7 | |||
| 8 | # Display win condition. | ||
| 9 | var sign_prefab = preload("res://objects/nodes/sign.tscn") | ||
| 10 | var sign1 = sign_prefab.instantiate() | ||
| 11 | sign1.position = Vector3(-7, 5, -15.01) | ||
| 12 | sign1.text = "victory" | ||
| 13 | root.get_node("/root/scene").add_child.call_deferred(sign1) | ||
| 14 | |||
| 15 | var sign2 = sign_prefab.instantiate() | ||
| 16 | sign2.position = Vector3(-7, 4, -15.01) | ||
| 17 | sign2.text = "%s ending" % ap.kEndingNameByVictoryValue.get(ap.victory_condition, "?") | ||
| 18 | |||
| 19 | var sign2_color = ap.kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower() | ||
| 20 | if sign2_color == "white": | ||
| 21 | sign2_color = "silver" | ||
| 22 | |||
| 23 | sign2.material = load("res://assets/materials/%s.material" % sign2_color) | ||
| 24 | root.get_node("/root/scene").add_child.call_deferred(sign2) | ||
| 25 | |||
| 26 | # Add the gift map entry panel if needed. | ||
| 27 | if not ap.enable_gift_maps.is_empty(): | ||
| 28 | var panel_prefab = preload("res://objects/nodes/panel.tscn") | ||
| 29 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 30 | var wpl_prefab = preload("res://objects/nodes/listeners/worldportListener.tscn") | ||
| 31 | |||
| 32 | var giftmap_parent = Node.new() | ||
| 33 | giftmap_parent.name = "GiftMapEntrance" | ||
| 34 | root.get_node("/root/scene/Components").add_child.call_deferred(giftmap_parent) | ||
| 35 | |||
| 36 | var symbolless_player = "" | ||
| 37 | for i in range(ap.client.ap_user.length()): | ||
| 38 | if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains( | ||
| 39 | ap.client.ap_user[i] | ||
| 40 | ): | ||
| 41 | symbolless_player = symbolless_player + ap.client.ap_user[i].to_lower() | ||
| 42 | |||
| 43 | var giftmap_panel = panel_prefab.instantiate() | ||
| 44 | giftmap_panel.name = "Panel" | ||
| 45 | giftmap_panel.position = Vector3(33.5, -190, 5.5) | ||
| 46 | giftmap_panel.rotation_degrees = Vector3(-45, 0, 0) | ||
| 47 | giftmap_panel.clue = "player" | ||
| 48 | giftmap_panel.answer = symbolless_player | ||
| 49 | |||
| 50 | if ap.enable_gift_maps.has("The Advanced"): | ||
| 51 | var icely_panel = panel_prefab.instantiate() | ||
| 52 | icely_panel.name = "IcelyPanel" | ||
| 53 | icely_panel.answer = "icely" | ||
| 54 | icely_panel.position = Vector3(33.5, -200, 5.5) | ||
| 55 | giftmap_panel.proxies.append(NodePath("../IcelyPanel")) | ||
| 56 | giftmap_parent.add_child.call_deferred(icely_panel) | ||
| 57 | |||
| 58 | var icely_wpl = wpl_prefab.instantiate() | ||
| 59 | icely_wpl.name = "IcelyWpl" | ||
| 60 | icely_wpl.exit = "the_advanced" | ||
| 61 | icely_wpl.senders.append(NodePath("../IcelyPanel")) | ||
| 62 | giftmap_parent.add_child.call_deferred(icely_wpl) | ||
| 63 | |||
| 64 | if ap.enable_gift_maps.has("The Charismatic"): | ||
| 65 | var souvey_panel = panel_prefab.instantiate() | ||
| 66 | souvey_panel.name = "SouveyPanel" | ||
| 67 | souvey_panel.answer = "souvey" | ||
| 68 | souvey_panel.position = Vector3(33.5, -210, 5.5) | ||
| 69 | giftmap_panel.proxies.append(NodePath("../SouveyPanel")) | ||
| 70 | giftmap_parent.add_child.call_deferred(souvey_panel) | ||
| 71 | |||
| 72 | var souvey_wpl = wpl_prefab.instantiate() | ||
| 73 | souvey_wpl.name = "SouveyWpl" | ||
| 74 | souvey_wpl.exit = "the_charismatic" | ||
| 75 | souvey_wpl.senders.append(NodePath("../SouveyPanel")) | ||
| 76 | giftmap_parent.add_child.call_deferred(souvey_wpl) | ||
| 77 | |||
| 78 | if ap.enable_gift_maps.has("The Crystalline"): | ||
| 79 | var q_panel = panel_prefab.instantiate() | ||
| 80 | q_panel.name = "QPanel" | ||
| 81 | q_panel.answer = "q" | ||
| 82 | q_panel.position = Vector3(33.5, -220, 5.5) | ||
| 83 | giftmap_panel.proxies.append(NodePath("../QPanel")) | ||
| 84 | giftmap_parent.add_child.call_deferred(q_panel) | ||
| 85 | |||
| 86 | var q_wpl = wpl_prefab.instantiate() | ||
| 87 | q_wpl.name = "QWpl" | ||
| 88 | q_wpl.exit = "the_crystalline" | ||
| 89 | q_wpl.senders.append(NodePath("../QPanel")) | ||
| 90 | giftmap_parent.add_child.call_deferred(q_wpl) | ||
| 91 | |||
| 92 | if ap.enable_gift_maps.has("The Fuzzy"): | ||
| 93 | var gongus_panel = panel_prefab.instantiate() | ||
| 94 | gongus_panel.name = "GongusPanel" | ||
| 95 | gongus_panel.answer = "gongus" | ||
| 96 | gongus_panel.position = Vector3(33.5, -260, 5.5) | ||
| 97 | giftmap_panel.proxies.append(NodePath("../GongusPanel")) | ||
| 98 | giftmap_parent.add_child.call_deferred(gongus_panel) | ||
| 99 | |||
| 100 | var kiwi_panel = panel_prefab.instantiate() | ||
| 101 | kiwi_panel.name = "KiwiPanel" | ||
| 102 | kiwi_panel.answer = "kiwi" | ||
| 103 | kiwi_panel.position = Vector3(33.5, -270, 5.5) | ||
| 104 | giftmap_panel.proxies.append(NodePath("../KiwiPanel")) | ||
| 105 | giftmap_parent.add_child.call_deferred(kiwi_panel) | ||
| 106 | |||
| 107 | var fuzzy_wpl = wpl_prefab.instantiate() | ||
| 108 | fuzzy_wpl.name = "FuzzyWpl" | ||
| 109 | fuzzy_wpl.exit = "the_fuzzy" | ||
| 110 | fuzzy_wpl.senders.append(NodePath("../GongusPanel")) | ||
| 111 | fuzzy_wpl.senders.append(NodePath("../KiwiPanel")) | ||
| 112 | fuzzy_wpl.complete_at = 1 | ||
| 113 | giftmap_parent.add_child.call_deferred(fuzzy_wpl) | ||
| 114 | |||
| 115 | if ap.enable_gift_maps.has("The Stellar"): | ||
| 116 | var hatkirby_panel = panel_prefab.instantiate() | ||
| 117 | hatkirby_panel.name = "HatkirbyPanel" | ||
| 118 | hatkirby_panel.answer = "hatkirby" | ||
| 119 | hatkirby_panel.position = Vector3(33.5, -230, 5.5) | ||
| 120 | giftmap_panel.proxies.append(NodePath("../HatkirbyPanel")) | ||
| 121 | giftmap_parent.add_child.call_deferred(hatkirby_panel) | ||
| 122 | |||
| 123 | var kirby_panel = panel_prefab.instantiate() | ||
| 124 | kirby_panel.name = "KirbyPanel" | ||
| 125 | kirby_panel.answer = "kirby" | ||
| 126 | kirby_panel.position = Vector3(33.5, -240, 5.5) | ||
| 127 | giftmap_panel.proxies.append(NodePath("../KirbyPanel")) | ||
| 128 | giftmap_parent.add_child.call_deferred(kirby_panel) | ||
| 129 | |||
| 130 | var star_panel = panel_prefab.instantiate() | ||
| 131 | star_panel.name = "StarPanel" | ||
| 132 | star_panel.answer = "star" | ||
| 133 | star_panel.position = Vector3(33.5, -250, 5.5) | ||
| 134 | giftmap_panel.proxies.append(NodePath("../StarPanel")) | ||
| 135 | giftmap_parent.add_child.call_deferred(star_panel) | ||
| 136 | |||
| 137 | var stellar_wpl = wpl_prefab.instantiate() | ||
| 138 | stellar_wpl.name = "StellarWpl" | ||
| 139 | stellar_wpl.exit = "the_stellar" | ||
| 140 | stellar_wpl.senders.append(NodePath("../HatkirbyPanel")) | ||
| 141 | stellar_wpl.senders.append(NodePath("../KirbyPanel")) | ||
| 142 | stellar_wpl.senders.append(NodePath("../StarPanel")) | ||
| 143 | stellar_wpl.complete_at = 1 | ||
| 144 | giftmap_parent.add_child.call_deferred(stellar_wpl) | ||
| 145 | |||
| 146 | giftmap_parent.add_child.call_deferred(giftmap_panel) | ||
| 147 | |||
| 148 | var giftmap_tpl = tpl_prefab.instantiate() | ||
| 149 | giftmap_tpl.name = "PanelTeleporter" | ||
| 150 | giftmap_tpl.teleport_point = Vector3(33.5, 1, 5.5) | ||
| 151 | giftmap_tpl.teleport_rotate = Vector3(-45, 0, 0) | ||
| 152 | giftmap_tpl.target_path = giftmap_panel | ||
| 153 | giftmap_tpl.senders.append( | ||
| 154 | NodePath("/root/scene/Components/Listeners/unlockReaderListenerDoubles") | ||
| 155 | ) | ||
| 156 | giftmap_parent.add_child.call_deferred(giftmap_tpl) | ||
| diff --git a/apworld/client/maps/the_fuzzy.gd b/apworld/client/maps/the_fuzzy.gd new file mode 100644 index 0000000..269dcee --- /dev/null +++ b/apworld/client/maps/the_fuzzy.gd | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | # Add the mastery to The Fuzzy. | ||
| 3 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
| 4 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
| 5 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 6 | |||
| 7 | var mastery = collectable_prefab.instantiate() | ||
| 8 | mastery.name = "collectable" | ||
| 9 | mastery.position = Vector3(0, 2, -20) | ||
| 10 | mastery.unlock_type = "smiley" | ||
| 11 | mastery.material_override = load("res://assets/materials/gold.material") | ||
| 12 | root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
| 13 | |||
| 14 | var usl = usl_prefab.instantiate() | ||
| 15 | usl.name = "unlockSetterListenerMastery" | ||
| 16 | usl.key = "fuzzy_mastery" | ||
| 17 | usl.value = "unlocked" | ||
| 18 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 19 | root.get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 20 | |||
| 21 | var saver = saver_prefab.instantiate() | ||
| 22 | saver.name = "saver_collectables" | ||
| 23 | saver.type = "collectables" | ||
| 24 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
| 25 | root.get_node("/root/scene").add_child.call_deferred(saver) | ||
| diff --git a/apworld/client/maps/the_parthenon.gd b/apworld/client/maps/the_parthenon.gd new file mode 100644 index 0000000..96510da --- /dev/null +++ b/apworld/client/maps/the_parthenon.gd | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | var ap = global.get_node("Archipelago") | ||
| 3 | |||
| 4 | # Add the strict cyan ending validation. | ||
| 5 | if ap.strict_cyan_ending: | ||
| 6 | var panel_prefab = preload("res://objects/nodes/panel.tscn") | ||
| 7 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 8 | var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn") | ||
| 9 | |||
| 10 | var previous_panel = null | ||
| 11 | var next_y = -100 | ||
| 12 | var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"] | ||
| 13 | for word in words: | ||
| 14 | var panel = panel_prefab.instantiate() | ||
| 15 | panel.position = Vector3(0, next_y, 0) | ||
| 16 | next_y -= 10 | ||
| 17 | panel.clue = word | ||
| 18 | panel.symbol = "." | ||
| 19 | panel.answer = "%s%s" % [word, word] | ||
| 20 | panel.name = "EndCheck_%s" % word | ||
| 21 | |||
| 22 | var tpl = tpl_prefab.instantiate() | ||
| 23 | tpl.teleport_point = Vector3(0, 1, -11) | ||
| 24 | tpl.teleport_rotate = Vector3(-45, 0, 0) | ||
| 25 | tpl.target_path = panel | ||
| 26 | tpl.name = "Teleport" | ||
| 27 | |||
| 28 | if previous_panel == null: | ||
| 29 | tpl.senderGroup.append(NodePath("/root/scene/Panels/Rulers")) | ||
| 30 | else: | ||
| 31 | tpl.senders.append(NodePath("../../%s" % previous_panel.name)) | ||
| 32 | |||
| 33 | var reversing = reverse_prefab.instantiate() | ||
| 34 | reversing.senders.append(NodePath("..")) | ||
| 35 | reversing.name = "Reversing" | ||
| 36 | tpl.senders.append(NodePath("../Reversing")) | ||
| 37 | |||
| 38 | panel.add_child.call_deferred(tpl) | ||
| 39 | panel.add_child.call_deferred(reversing) | ||
| 40 | root.get_node("/root/scene/Panels").add_child.call_deferred(panel) | ||
| 41 | |||
| 42 | previous_panel = panel | ||
| 43 | |||
| 44 | # Duplicate the door that usually waits on the rulers. We can't set the | ||
| 45 | # senders here for some reason so we actually set them in the door ready | ||
| 46 | # function. | ||
| 47 | var entry1 = root.get_node("/root/scene/Components/Doors/entry_1") | ||
| 48 | var entry12 = entry1.duplicate() | ||
| 49 | entry12.name = "spe_entry_1" | ||
| 50 | entry1.get_parent().add_child.call_deferred(entry12) | ||
| 51 | entry1.queue_free() | ||
| diff --git a/apworld/client/maps/the_plaza.gd b/apworld/client/maps/the_plaza.gd new file mode 100644 index 0000000..13e002d --- /dev/null +++ b/apworld/client/maps/the_plaza.gd | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | # Move the Plaza RTE trigger outside of the turtle. | ||
| 3 | var rte_trigger = root.get_node("/root/scene/Components/Warps/triggerArea") | ||
| 4 | rte_trigger.position.z = 0 | ||
| diff --git a/apworld/client/maps/the_stellar.gd b/apworld/client/maps/the_stellar.gd new file mode 100644 index 0000000..d633535 --- /dev/null +++ b/apworld/client/maps/the_stellar.gd | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | # Add the mastery to The Stellar. | ||
| 3 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
| 4 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
| 5 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 6 | |||
| 7 | var collectables = Node.new() | ||
| 8 | collectables.name = "Collectables" | ||
| 9 | |||
| 10 | var mastery = collectable_prefab.instantiate() | ||
| 11 | mastery.name = "collectable" | ||
| 12 | mastery.position = Vector3(2, 2, -31) | ||
| 13 | mastery.rotation_degrees = Vector3(0, 90, 0) | ||
| 14 | mastery.unlock_type = "smiley" | ||
| 15 | mastery.material_override = load("res://assets/materials/gold.material") | ||
| 16 | collectables.add_child.call_deferred(mastery) | ||
| 17 | root.get_node("/root/scene/Components").add_child.call_deferred(collectables) | ||
| 18 | |||
| 19 | var usl = usl_prefab.instantiate() | ||
| 20 | usl.name = "unlockSetterListenerMastery" | ||
| 21 | usl.key = "stellar_mastery" | ||
| 22 | usl.value = "unlocked" | ||
| 23 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 24 | root.get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 25 | |||
| 26 | var saver = saver_prefab.instantiate() | ||
| 27 | saver.name = "saver_collectables" | ||
| 28 | saver.type = "collectables" | ||
| 29 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
| 30 | root.get_node("/root/scene").add_child.call_deferred(saver) | ||
| diff --git a/apworld/client/maps/the_sun_temple.gd b/apworld/client/maps/the_sun_temple.gd new file mode 100644 index 0000000..9804bf8 --- /dev/null +++ b/apworld/client/maps/the_sun_temple.gd | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | var ap = global.get_node("Archipelago") | ||
| 3 | |||
| 4 | # Add the strict purple ending validation. | ||
| 5 | if ap.strict_purple_ending: | ||
| 6 | var panel_prefab = preload("res://objects/nodes/panel.tscn") | ||
| 7 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 8 | var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn") | ||
| 9 | |||
| 10 | var previous_panel = null | ||
| 11 | var next_y = -100 | ||
| 12 | var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"] | ||
| 13 | for word in words: | ||
| 14 | var panel = panel_prefab.instantiate() | ||
| 15 | panel.position = Vector3(0, next_y, 0) | ||
| 16 | next_y -= 10 | ||
| 17 | panel.clue = word | ||
| 18 | panel.symbol = "" | ||
| 19 | panel.answer = word | ||
| 20 | panel.name = "EndCheck_%s" % word | ||
| 21 | |||
| 22 | var tpl = tpl_prefab.instantiate() | ||
| 23 | tpl.teleport_point = Vector3(0, 1, 0) | ||
| 24 | tpl.teleport_rotate = Vector3(-45, 180, 0) | ||
| 25 | tpl.target_path = panel | ||
| 26 | tpl.name = "Teleport" | ||
| 27 | |||
| 28 | if previous_panel == null: | ||
| 29 | tpl.senders.append(NodePath("/root/scene/Panels/End/panel_24")) | ||
| 30 | else: | ||
| 31 | tpl.senders.append(NodePath("../../%s" % previous_panel.name)) | ||
| 32 | |||
| 33 | var reversing = reverse_prefab.instantiate() | ||
| 34 | reversing.senders.append(NodePath("..")) | ||
| 35 | reversing.name = "Reversing" | ||
| 36 | tpl.senders.append(NodePath("../Reversing")) | ||
| 37 | |||
| 38 | panel.add_child.call_deferred(tpl) | ||
| 39 | panel.add_child.call_deferred(reversing) | ||
| 40 | root.get_node("/root/scene/Panels").add_child.call_deferred(panel) | ||
| 41 | |||
| 42 | previous_panel = panel | ||
| 43 | |||
| 44 | # Duplicate the doors that usually wait on EQUINOX. We can't set the senders | ||
| 45 | # here for some reason so we actually set them in the door ready function. | ||
| 46 | var endplat = root.get_node("/root/scene/Components/Doors/EndPlatform") | ||
| 47 | var endplat2 = endplat.duplicate() | ||
| 48 | endplat2.name = "spe_EndPlatform" | ||
| 49 | endplat.get_parent().add_child.call_deferred(endplat2) | ||
| 50 | endplat.queue_free() | ||
| 51 | |||
| 52 | var entry2 = root.get_node("/root/scene/Components/Doors/entry_2") | ||
| 53 | var entry22 = entry2.duplicate() | ||
| 54 | entry22.name = "spe_entry_2" | ||
| 55 | entry2.get_parent().add_child.call_deferred(entry22) | ||
| 56 | entry2.queue_free() | ||
| diff --git a/apworld/client/maps/the_unkempt.gd b/apworld/client/maps/the_unkempt.gd new file mode 100644 index 0000000..c907650 --- /dev/null +++ b/apworld/client/maps/the_unkempt.gd | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | # Prevent the COLOR panel from disappearing. | ||
| 3 | var color_tpl = root.get_node("/root/scene/Panels/Assorted/panel_1/teleportListener") | ||
| 4 | color_tpl.target_path = color_tpl | ||
| diff --git a/apworld/client/maps/the_unyielding.gd b/apworld/client/maps/the_unyielding.gd new file mode 100644 index 0000000..a2f8eee --- /dev/null +++ b/apworld/client/maps/the_unyielding.gd | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | func on_map_load(root): | ||
| 2 | # Shrink the painting trigger in The Unyielding. | ||
| 3 | var trigger_area = root.get_node("/root/scene/Components/PaintingUnlocker/triggerArea") | ||
| 4 | trigger_area.position = Vector3(0, 0, -6) | ||
| 5 | trigger_area.scale = Vector3(6, 1, 6) | ||
| diff --git a/client/Archipelago/messages.gd b/apworld/client/messages.gd index 52f38b9..ab4f071 100644 --- a/client/Archipelago/messages.gd +++ b/apworld/client/messages.gd | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | extends CanvasLayer | 1 | extends CanvasLayer |
| 2 | 2 | ||
| 3 | var SCRIPT_rainbowText | ||
| 4 | |||
| 3 | var _message_queue = [] | 5 | var _message_queue = [] |
| 4 | var _font | 6 | var _font |
| 5 | var _container | 7 | var _container |
| @@ -23,6 +25,7 @@ func _ready(): | |||
| 23 | 25 | ||
| 24 | func _add_message(text): | 26 | func _add_message(text): |
| 25 | var new_label = RichTextLabel.new() | 27 | var new_label = RichTextLabel.new() |
| 28 | new_label.install_effect(SCRIPT_rainbowText.new()) | ||
| 26 | new_label.push_font(_font) | 29 | new_label.push_font(_font) |
| 27 | new_label.push_font_size(36) | 30 | new_label.push_font_size(36) |
| 28 | new_label.push_outline_color(Color(0, 0, 0, 1)) | 31 | new_label.push_outline_color(Color(0, 0, 0, 1)) |
| @@ -48,10 +51,11 @@ func showMessage(text): | |||
| 48 | while !_ordered_labels.is_empty(): | 51 | while !_ordered_labels.is_empty(): |
| 49 | await get_tree().create_timer(timeout).timeout | 52 | await get_tree().create_timer(timeout).timeout |
| 50 | 53 | ||
| 51 | var to_remove = _ordered_labels.pop_front() | 54 | if !_ordered_labels.is_empty(): |
| 52 | var to_tween = get_tree().create_tween().bind_node(to_remove) | 55 | var to_remove = _ordered_labels.pop_front() |
| 53 | to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5) | 56 | var to_tween = get_tree().create_tween().bind_node(to_remove) |
| 54 | to_tween.tween_callback(to_remove.queue_free) | 57 | to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5) |
| 58 | to_tween.tween_callback(to_remove.queue_free) | ||
| 55 | 59 | ||
| 56 | if !_message_queue.is_empty(): | 60 | if !_message_queue.is_empty(): |
| 57 | var next_msg = _message_queue.pop_front() | 61 | var next_msg = _message_queue.pop_front() |
| @@ -59,3 +63,12 @@ func showMessage(text): | |||
| 59 | 63 | ||
| 60 | if timeout > 4: | 64 | if timeout > 4: |
| 61 | timeout -= 3 | 65 | timeout -= 3 |
| 66 | |||
| 67 | |||
| 68 | func clear(): | ||
| 69 | _message_queue.clear() | ||
| 70 | |||
| 71 | for message_label in _ordered_labels: | ||
| 72 | message_label.queue_free() | ||
| 73 | |||
| 74 | _ordered_labels.clear() | ||
| diff --git a/apworld/client/minimap.gd b/apworld/client/minimap.gd new file mode 100644 index 0000000..bf70114 --- /dev/null +++ b/apworld/client/minimap.gd | |||
| @@ -0,0 +1,178 @@ | |||
| 1 | extends CanvasLayer | ||
| 2 | |||
| 3 | var player | ||
| 4 | var drawer | ||
| 5 | var sprite | ||
| 6 | var label | ||
| 7 | |||
| 8 | var cell_left | ||
| 9 | var cell_top | ||
| 10 | var cell_right | ||
| 11 | var cell_bottom | ||
| 12 | var cell_width | ||
| 13 | var cell_height | ||
| 14 | var center_x_min | ||
| 15 | var center_x_max | ||
| 16 | var center_y_min | ||
| 17 | var center_y_max | ||
| 18 | |||
| 19 | |||
| 20 | func _ready(): | ||
| 21 | player = get_tree().get_root().get_node("scene/player") | ||
| 22 | |||
| 23 | var svc = PanelContainer.new() | ||
| 24 | svc.anchor_left = 1.0 | ||
| 25 | svc.anchor_top = 1.0 | ||
| 26 | svc.anchor_right = 1.0 | ||
| 27 | svc.anchor_bottom = 1.0 | ||
| 28 | svc.offset_left = -320.0 | ||
| 29 | svc.offset_top = -320.0 | ||
| 30 | svc.offset_right = -64.0 | ||
| 31 | svc.offset_bottom = -64.0 | ||
| 32 | svc.clip_contents = true | ||
| 33 | add_child(svc) | ||
| 34 | |||
| 35 | var background_color = Color.WHITE | ||
| 36 | |||
| 37 | var world_env = get_tree().get_root().get_node("scene/WorldEnvironment") | ||
| 38 | if world_env != null and world_env.environment != null: | ||
| 39 | if world_env.environment.background_mode == Environment.BG_COLOR: | ||
| 40 | background_color = world_env.environment.background_color | ||
| 41 | elif ( | ||
| 42 | world_env.environment.background_mode == Environment.BG_SKY | ||
| 43 | and world_env.environment.sky != null | ||
| 44 | and world_env.environment.sky.sky_material != null | ||
| 45 | ): | ||
| 46 | var sky = world_env.environment.sky.sky_material | ||
| 47 | if sky is PhysicalSkyMaterial: | ||
| 48 | background_color = sky.ground_color | ||
| 49 | elif sky is ProceduralSkyMaterial: | ||
| 50 | background_color = sky.sky_top_color | ||
| 51 | |||
| 52 | var stylebox = StyleBoxFlat.new() | ||
| 53 | stylebox.bg_color = Color(background_color, 0.6) | ||
| 54 | svc.add_theme_stylebox_override("panel", stylebox) | ||
| 55 | |||
| 56 | drawer = Node2D.new() | ||
| 57 | svc.add_child(drawer) | ||
| 58 | |||
| 59 | var gridmap = get_tree().get_root().get_node("scene/GridMap") | ||
| 60 | if gridmap == null: | ||
| 61 | visible = false | ||
| 62 | return | ||
| 63 | |||
| 64 | cell_left = 0 | ||
| 65 | cell_top = 0 | ||
| 66 | cell_right = 0 | ||
| 67 | cell_bottom = 0 | ||
| 68 | |||
| 69 | for pos in gridmap.get_used_cells(): | ||
| 70 | if pos.x < cell_left: | ||
| 71 | cell_left = pos.x | ||
| 72 | if pos.x > cell_right: | ||
| 73 | cell_right = pos.x | ||
| 74 | if pos.z < cell_top: | ||
| 75 | cell_top = pos.z | ||
| 76 | if pos.z > cell_bottom: | ||
| 77 | cell_bottom = pos.z | ||
| 78 | |||
| 79 | cell_width = cell_right - cell_left + 1 | ||
| 80 | cell_height = cell_bottom - cell_top + 1 | ||
| 81 | |||
| 82 | var rendered = _renderMap(gridmap) | ||
| 83 | |||
| 84 | var image_texture = ImageTexture.create_from_image(rendered) | ||
| 85 | sprite = Sprite2D.new() | ||
| 86 | sprite.texture = image_texture | ||
| 87 | sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST | ||
| 88 | sprite.scale = Vector2(2, 2) | ||
| 89 | sprite.centered = false | ||
| 90 | drawer.add_child(sprite) | ||
| 91 | |||
| 92 | label = Label.new() | ||
| 93 | label.theme = preload("res://assets/themes/baseUI.tres") | ||
| 94 | label.add_theme_font_size_override("font_size", 32) | ||
| 95 | label.text = "@" | ||
| 96 | drawer.add_child(label) | ||
| 97 | |||
| 98 | #var local_tl = gridmap.map_to_local(Vector3i(cell_left, 0, cell_top)) | ||
| 99 | #var global_tl = gridmap.to_global(local_tl) | ||
| 100 | #var local_br = gridmap.map_to_local(Vector3i(cell_right, 0, cell_bottom)) | ||
| 101 | #var global_br = gridmap.to_global(local_br) | ||
| 102 | |||
| 103 | center_x_min = 0 | ||
| 104 | center_x_max = cell_width - 128 | ||
| 105 | center_y_min = 0 | ||
| 106 | center_y_max = cell_height - 128 | ||
| 107 | |||
| 108 | if center_x_max < center_x_min: | ||
| 109 | center_x_min = (center_x_min + center_x_max) / 2 | ||
| 110 | center_x_max = center_x_min | ||
| 111 | |||
| 112 | if center_y_max < center_y_min: | ||
| 113 | center_y_min = (center_y_min + center_y_max) / 2 | ||
| 114 | center_y_max = center_y_min | ||
| 115 | |||
| 116 | |||
| 117 | func _process(_delta): | ||
| 118 | if visible == false: | ||
| 119 | return | ||
| 120 | |||
| 121 | drawer.position.x = clamp(player.position.x - cell_left - 64, center_x_min, center_x_max) * -2 | ||
| 122 | drawer.position.y = clamp(player.position.z - cell_top - 64, center_y_min, center_y_max) * -2 | ||
| 123 | |||
| 124 | label.position.x = (player.position.x - cell_left) * 2 - 16 | ||
| 125 | label.position.y = (player.position.z - cell_top) * 2 - 16 | ||
| 126 | |||
| 127 | |||
| 128 | func _renderMap(gridmap): | ||
| 129 | var ap = global.get_node("Archipelago") | ||
| 130 | var heights = {} | ||
| 131 | |||
| 132 | var rendered = Image.create_empty(cell_width, cell_height, false, Image.FORMAT_RGBA8) | ||
| 133 | rendered.fill(Color.TRANSPARENT) | ||
| 134 | |||
| 135 | var meshes_node = get_tree().get_root().get_node("scene/Meshes") | ||
| 136 | if meshes_node != null: | ||
| 137 | _renderMeshNode(ap, gridmap, meshes_node, rendered) | ||
| 138 | |||
| 139 | for pos in gridmap.get_used_cells(): | ||
| 140 | var in_plane = Vector2i(pos.x, pos.z) | ||
| 141 | |||
| 142 | if in_plane in heights and heights[in_plane] > pos.y: | ||
| 143 | continue | ||
| 144 | |||
| 145 | heights[in_plane] = pos.y | ||
| 146 | |||
| 147 | var cell_item = gridmap.get_cell_item(pos) | ||
| 148 | var mesh = gridmap.mesh_library.get_item_mesh(cell_item) | ||
| 149 | var material = mesh.surface_get_material(0) | ||
| 150 | var color = ap.color_by_material_path.get(material.resource_path, Color.TRANSPARENT) | ||
| 151 | |||
| 152 | rendered.set_pixel(pos.x - cell_left, pos.z - cell_top, color) | ||
| 153 | |||
| 154 | return rendered | ||
| 155 | |||
| 156 | |||
| 157 | func _renderMeshNode(ap, gridmap, mesh, rendered): | ||
| 158 | if mesh is MeshInstance3D: | ||
| 159 | var local_tl = gridmap.map_to_local(Vector3i(cell_left, 0, cell_top)) | ||
| 160 | var global_tl = gridmap.to_global(local_tl) | ||
| 161 | var mesh_material = mesh.get_surface_override_material(0) | ||
| 162 | if mesh_material != null: | ||
| 163 | var mesh_color = ap.color_by_material_path.get( | ||
| 164 | mesh_material.resource_path, Color.TRANSPARENT | ||
| 165 | ) | ||
| 166 | |||
| 167 | for y in range( | ||
| 168 | max(mesh.position.z - mesh.scale.z / 2 - global_tl.z, 0), | ||
| 169 | min(mesh.position.z + mesh.scale.z / 2 - global_tl.z, cell_height) | ||
| 170 | ): | ||
| 171 | for x in range( | ||
| 172 | max(mesh.position.x - mesh.scale.x / 2 - global_tl.x, 0), | ||
| 173 | min(mesh.position.x + mesh.scale.x / 2 - global_tl.x, cell_width) | ||
| 174 | ): | ||
| 175 | rendered.set_pixel(x, y, mesh_color) | ||
| 176 | |||
| 177 | for child in mesh.get_children(): | ||
| 178 | _renderMeshNode(ap, gridmap, child, rendered) | ||
| diff --git a/client/Archipelago/painting.gd b/apworld/client/painting.gd index 276d4eb..276d4eb 100644 --- a/client/Archipelago/painting.gd +++ b/apworld/client/painting.gd | |||
| diff --git a/apworld/client/paintingAuto.gd b/apworld/client/paintingAuto.gd new file mode 100644 index 0000000..553c2c9 --- /dev/null +++ b/apworld/client/paintingAuto.gd | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | extends "res://scripts/nodes/paintingAuto.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 | if item_id != null and activate_on_sender_complete: | ||
| 34 | enabled = false | ||
| 35 | if not hide_particles: | ||
| 36 | get_node("Hinge/paintingColliders/TeleportParticles").emitting = false | ||
| 37 | |||
| 38 | |||
| 39 | func _readier(): | ||
| 40 | var ap = global.get_node("Archipelago") | ||
| 41 | |||
| 42 | if ap.client.getItemAmount(item_id) >= item_amount: | ||
| 43 | handleTriggered() | ||
| diff --git a/apworld/client/panel.gd b/apworld/client/panel.gd new file mode 100644 index 0000000..2cef28e --- /dev/null +++ b/apworld/client/panel.gd | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | extends "res://scripts/nodes/panel.gd" | ||
| 2 | |||
| 3 | var panel_logic = null | ||
| 4 | var symbol_solvable = true | ||
| 5 | |||
| 6 | var black = load("res://assets/materials/black.material") | ||
| 7 | |||
| 8 | |||
| 9 | func _ready(): | ||
| 10 | super._ready() | ||
| 11 | |||
| 12 | var node_path = String( | ||
| 13 | get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names() | ||
| 14 | ) | ||
| 15 | |||
| 16 | var gamedata = global.get_node("Gamedata") | ||
| 17 | var panel_id = gamedata.get_panel_for_map_node_path(global.map, node_path) | ||
| 18 | if panel_id != null: | ||
| 19 | var ap = global.get_node("Archipelago") | ||
| 20 | if ap.shuffle_symbols: | ||
| 21 | if global.map == "the_entry" and node_path == "Panels/Entry/front_1": | ||
| 22 | clue = "i" | ||
| 23 | symbol = "" | ||
| 24 | |||
| 25 | setField("clue", clue) | ||
| 26 | setField("symbol", symbol) | ||
| 27 | |||
| 28 | panel_logic = gamedata.objects.get_panels()[panel_id] | ||
| 29 | checkSymbolSolvable() | ||
| 30 | |||
| 31 | if not symbol_solvable: | ||
| 32 | get_tree().get_root().get_node("scene/player").evaluate_solvability.connect( | ||
| 33 | evaluateSolvability | ||
| 34 | ) | ||
| 35 | |||
| 36 | |||
| 37 | func checkSymbolSolvable(): | ||
| 38 | var old_solvable = symbol_solvable | ||
| 39 | symbol_solvable = true | ||
| 40 | |||
| 41 | if panel_logic == null: | ||
| 42 | # There's no logic for this panel. | ||
| 43 | return | ||
| 44 | |||
| 45 | var ap = global.get_node("Archipelago") | ||
| 46 | if not ap.shuffle_symbols: | ||
| 47 | # Symbols aren't item-locked. | ||
| 48 | return | ||
| 49 | |||
| 50 | var gamedata = global.get_node("Gamedata") | ||
| 51 | for symbol in panel_logic.get_symbols(): | ||
| 52 | var item_name = gamedata.kSYMBOL_ITEMS.get(symbol) | ||
| 53 | var item_id = gamedata.objects.get_special_ids()[item_name] | ||
| 54 | if ap.client.getItemAmount(item_id) < 1: | ||
| 55 | symbol_solvable = false | ||
| 56 | break | ||
| 57 | |||
| 58 | if symbol_solvable != old_solvable: | ||
| 59 | if symbol_solvable: | ||
| 60 | setField("clue", clue) | ||
| 61 | setField("symbol", symbol) | ||
| 62 | setField("answer", answer) | ||
| 63 | else: | ||
| 64 | quad_mesh.surface_set_material(0, black) | ||
| 65 | get_node("Hinge/clue").text = "missing" | ||
| 66 | get_node("Hinge/answer").text = "symbols" | ||
| 67 | |||
| 68 | |||
| 69 | func checkSolvable(key): | ||
| 70 | checkSymbolSolvable() | ||
| 71 | if not symbol_solvable: | ||
| 72 | return false | ||
| 73 | |||
| 74 | return super.checkSolvable(key) | ||
| 75 | |||
| 76 | |||
| 77 | func evaluateSolvability(): | ||
| 78 | checkSolvable("") | ||
| 79 | |||
| 80 | |||
| 81 | func passedInput(key, skip_focus_check = false): | ||
| 82 | if not symbol_solvable: | ||
| 83 | return | ||
| 84 | |||
| 85 | super.passedInput(key, skip_focus_check) | ||
| 86 | |||
| 87 | |||
| 88 | func focus(): | ||
| 89 | if not symbol_solvable: | ||
| 90 | has_focus = false | ||
| 91 | return | ||
| 92 | |||
| 93 | super.focus() | ||
| 94 | |||
| 95 | |||
| 96 | func unfocus(): | ||
| 97 | if not symbol_solvable: | ||
| 98 | has_focus = false | ||
| 99 | return | ||
| 100 | |||
| 101 | super.unfocus() | ||
| diff --git a/apworld/client/pauseMenu.gd b/apworld/client/pauseMenu.gd new file mode 100644 index 0000000..72b45e8 --- /dev/null +++ b/apworld/client/pauseMenu.gd | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | extends "res://scripts/ui/pauseMenu.gd" | ||
| 2 | |||
| 3 | var compass_button | ||
| 4 | var locations_button | ||
| 5 | var minimap_button | ||
| 6 | |||
| 7 | |||
| 8 | func _ready(): | ||
| 9 | var ap_panel = Panel.new() | ||
| 10 | ap_panel.name = "Archipelago" | ||
| 11 | get_node("menu/settings/settingsInner/TabContainer").add_child(ap_panel) | ||
| 12 | |||
| 13 | var ap = global.get_node("Archipelago") | ||
| 14 | |||
| 15 | compass_button = CheckBox.new() | ||
| 16 | compass_button.text = "show compass" | ||
| 17 | compass_button.button_pressed = ap.show_compass | ||
| 18 | compass_button.position = Vector2(65, 100) | ||
| 19 | compass_button.theme = preload("res://assets/themes/baseUI.tres") | ||
| 20 | compass_button.add_theme_font_size_override("font_size", 60) | ||
| 21 | compass_button.pressed.connect(_toggle_compass) | ||
| 22 | ap_panel.add_child(compass_button) | ||
| 23 | |||
| 24 | locations_button = CheckBox.new() | ||
| 25 | locations_button.text = "show locations overlay" | ||
| 26 | locations_button.button_pressed = ap.show_locations | ||
| 27 | locations_button.position = Vector2(65, 200) | ||
| 28 | locations_button.theme = preload("res://assets/themes/baseUI.tres") | ||
| 29 | locations_button.add_theme_font_size_override("font_size", 60) | ||
| 30 | locations_button.pressed.connect(_toggle_locations) | ||
| 31 | ap_panel.add_child(locations_button) | ||
| 32 | |||
| 33 | minimap_button = CheckBox.new() | ||
| 34 | minimap_button.text = "show minimap" | ||
| 35 | minimap_button.button_pressed = ap.show_minimap | ||
| 36 | minimap_button.position = Vector2(65, 300) | ||
| 37 | minimap_button.theme = preload("res://assets/themes/baseUI.tres") | ||
| 38 | minimap_button.add_theme_font_size_override("font_size", 60) | ||
| 39 | minimap_button.pressed.connect(_toggle_minimap) | ||
| 40 | ap_panel.add_child(minimap_button) | ||
| 41 | |||
| 42 | super._ready() | ||
| 43 | |||
| 44 | |||
| 45 | func _pause_game(): | ||
| 46 | global.get_node("Textclient").dismiss() | ||
| 47 | super._pause_game() | ||
| 48 | |||
| 49 | |||
| 50 | func _main_menu(): | ||
| 51 | global.loaded = false | ||
| 52 | global.get_node("Archipelago").disconnect_from_ap() | ||
| 53 | global.get_node("Messages").clear() | ||
| 54 | global.get_node("Compass").visible = false | ||
| 55 | global.get_node("Textclient").reset() | ||
| 56 | |||
| 57 | autosplitter.reset() | ||
| 58 | _unpause_game() | ||
| 59 | Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) | ||
| 60 | musicPlayer.stop() | ||
| 61 | |||
| 62 | var runtime = global.get_node("Runtime") | ||
| 63 | runtime.load_script_as_scene.call_deferred("settings_screen.gd", "settings_screen") | ||
| 64 | |||
| 65 | |||
| 66 | func _toggle_compass(): | ||
| 67 | var ap = global.get_node("Archipelago") | ||
| 68 | ap.show_compass = compass_button.button_pressed | ||
| 69 | ap.saveSettings() | ||
| 70 | |||
| 71 | var compass = global.get_node("Compass") | ||
| 72 | compass.visible = compass_button.button_pressed | ||
| 73 | |||
| 74 | |||
| 75 | func _toggle_locations(): | ||
| 76 | var ap = global.get_node("Archipelago") | ||
| 77 | ap.show_locations = locations_button.button_pressed | ||
| 78 | ap.saveSettings() | ||
| 79 | |||
| 80 | var textclient = global.get_node("Textclient") | ||
| 81 | textclient.update_locations_visibility() | ||
| 82 | |||
| 83 | |||
| 84 | func _toggle_minimap(): | ||
| 85 | var ap = global.get_node("Archipelago") | ||
| 86 | ap.show_minimap = minimap_button.button_pressed | ||
| 87 | ap.saveSettings() | ||
| 88 | |||
| 89 | var minimap = get_tree().get_root().get_node("scene/Minimap") | ||
| 90 | if minimap != null: | ||
| 91 | minimap.visible = ap.show_minimap | ||
| diff --git a/client/Archipelago/player.gd b/apworld/client/player.gd index dd6aa2b..5fac9fd 100644 --- a/client/Archipelago/player.gd +++ b/apworld/client/player.gd | |||
| @@ -1,30 +1,31 @@ | |||
| 1 | extends "res://scripts/nodes/player.gd" | 1 | extends "res://scripts/nodes/player.gd" |
| 2 | 2 | ||
| 3 | const kEndingNameByVictoryValue = { | 3 | signal evaluate_solvability |
| 4 | 0: "GRAY", | 4 | |
| 5 | 1: "PURPLE", | 5 | var compass |
| 6 | 2: "MINT", | ||
| 7 | 3: "BLACK", | ||
| 8 | 4: "BLUE", | ||
| 9 | 5: "CYAN", | ||
| 10 | 6: "RED", | ||
| 11 | 7: "PLUM", | ||
| 12 | 8: "ORANGE", | ||
| 13 | 9: "GOLD", | ||
| 14 | 10: "YELLOW", | ||
| 15 | 11: "GREEN", | ||
| 16 | 12: "WHITE", | ||
| 17 | } | ||
| 18 | 6 | ||
| 19 | 7 | ||
| 20 | func _ready(): | 8 | func _ready(): |
| 21 | var khl_script = load("res://scripts/nodes/keyHolderListener.gd") | 9 | var khl_script = load("res://scripts/nodes/keyHolderListener.gd") |
| 22 | 10 | ||
| 11 | var pause_menu = get_node("pause_menu") | ||
| 12 | pause_menu.layer = 3 | ||
| 13 | |||
| 23 | var ap = global.get_node("Archipelago") | 14 | var ap = global.get_node("Archipelago") |
| 24 | var gamedata = global.get_node("Gamedata") | 15 | var gamedata = global.get_node("Gamedata") |
| 25 | 16 | ||
| 17 | compass = global.get_node("Compass") | ||
| 18 | compass.visible = ap.show_compass | ||
| 19 | |||
| 26 | ap.start_batching_locations() | 20 | ap.start_batching_locations() |
| 27 | 21 | ||
| 22 | # Run map-specific initialization. | ||
| 23 | var map_script = ap.get_map_script(global.map) | ||
| 24 | if map_script != null: | ||
| 25 | map_script.on_map_load(get_tree().get_root()) | ||
| 26 | |||
| 27 | ap.update_job_well_done_sign() | ||
| 28 | |||
| 28 | # Set up door locations. | 29 | # Set up door locations. |
| 29 | var map_id = gamedata.map_id_by_name.get(global.map) | 30 | var map_id = gamedata.map_id_by_name.get(global.map) |
| 30 | for door in gamedata.objects.get_doors(): | 31 | for door in gamedata.objects.get_doors(): |
| @@ -34,7 +35,14 @@ func _ready(): | |||
| 34 | if not door.has_ap_id(): | 35 | if not door.has_ap_id(): |
| 35 | continue | 36 | continue |
| 36 | 37 | ||
| 37 | if door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY: | 38 | if ( |
| 39 | not (door.has_legacy_location() and door.get_legacy_location()) | ||
| 40 | and ( | ||
| 41 | door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY | ||
| 42 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING | ||
| 43 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR | ||
| 44 | ) | ||
| 45 | ): | ||
| 38 | continue | 46 | continue |
| 39 | 47 | ||
| 40 | var locationListener = ap.SCRIPT_locationListener.new() | 48 | var locationListener = ap.SCRIPT_locationListener.new() |
| @@ -66,6 +74,12 @@ func _ready(): | |||
| 66 | 74 | ||
| 67 | locationListener.senders.append(NodePath("../" + khl.name)) | 75 | locationListener.senders.append(NodePath("../" + khl.name)) |
| 68 | 76 | ||
| 77 | for sender in door.get_senders(): | ||
| 78 | locationListener.senders.append(NodePath("/root/scene/" + sender)) | ||
| 79 | |||
| 80 | if door.has_complete_at(): | ||
| 81 | locationListener.complete_at = door.get_complete_at() | ||
| 82 | |||
| 69 | get_parent().add_child.call_deferred(locationListener) | 83 | get_parent().add_child.call_deferred(locationListener) |
| 70 | 84 | ||
| 71 | # Set up letter locations. | 85 | # Set up letter locations. |
| @@ -86,17 +100,12 @@ func _ready(): | |||
| 86 | != ap.kLETTER_BEHAVIOR_VANILLA | 100 | != ap.kLETTER_BEHAVIOR_VANILLA |
| 87 | ): | 101 | ): |
| 88 | var scout = ap.scout_location(letter.get_ap_id()) | 102 | var scout = ap.scout_location(letter.get_ap_id()) |
| 89 | if scout != null: | 103 | if scout != null and not (scout["for_self"] and scout["flags"] & 4 != 0): |
| 90 | var item_name = "Unknown" | 104 | var collectable = get_tree().get_root().get_node("scene").get_node_or_null( |
| 91 | var item_player_game = ap.client._game_by_player[float(scout["player"])] | 105 | letter.get_path() |
| 92 | if ap.client._item_id_to_name[item_player_game].has(scout["item"]): | 106 | ) |
| 93 | item_name = ap.client._item_id_to_name[item_player_game][scout["item"]] | 107 | if collectable != null: |
| 94 | 108 | collectable.setScoutedText.call_deferred(scout["item"]) | |
| 95 | var collectable = get_tree().get_root().get_node("scene").get_node_or_null( | ||
| 96 | letter.get_path() | ||
| 97 | ) | ||
| 98 | if collectable != null: | ||
| 99 | collectable.setScoutedText.call_deferred(item_name) | ||
| 100 | 109 | ||
| 101 | # Set up mastery locations. | 110 | # Set up mastery locations. |
| 102 | for mastery in gamedata.objects.get_masteries(): | 111 | for mastery in gamedata.objects.get_masteries(): |
| @@ -124,7 +133,7 @@ func _ready(): | |||
| 124 | 133 | ||
| 125 | get_parent().add_child.call_deferred(locationListener) | 134 | get_parent().add_child.call_deferred(locationListener) |
| 126 | 135 | ||
| 127 | if kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name(): | 136 | if ap.kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name(): |
| 128 | var victoryListener = ap.SCRIPT_victoryListener.new() | 137 | var victoryListener = ap.SCRIPT_victoryListener.new() |
| 129 | victoryListener.name = "victoryListener" | 138 | victoryListener.name = "victoryListener" |
| 130 | victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path())) | 139 | victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path())) |
| @@ -155,38 +164,10 @@ func _ready(): | |||
| 155 | 164 | ||
| 156 | get_parent().add_child.call_deferred(locationListener) | 165 | get_parent().add_child.call_deferred(locationListener) |
| 157 | 166 | ||
| 158 | # Block off roof access in Daedalus. | 167 | var minimap = ap.SCRIPT_minimap.new() |
| 159 | if global.map == "daedalus" and not ap.daedalus_roof_access: | 168 | minimap.name = "Minimap" |
| 160 | _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49) | 169 | minimap.visible = ap.show_minimap |
| 161 | _set_up_invis_wall(51.5, 11, -17, 16, 10, 1) | 170 | get_parent().add_child.call_deferred(minimap) |
| 162 | _set_up_invis_wall(46, 10, -9.5, 1, 10, 10) | ||
| 163 | _set_up_invis_wall(67.5, 11, 17, 16, 10, 1) | ||
| 164 | _set_up_invis_wall(50.5, 11, 14, 10, 10, 1) | ||
| 165 | _set_up_invis_wall(39, 10, 18.5, 1, 10, 22) | ||
| 166 | _set_up_invis_wall(20, 15, 18.5, 1, 10, 16) | ||
| 167 | _set_up_invis_wall(11.5, 15, 3, 32, 10, 1) | ||
| 168 | _set_up_invis_wall(11.5, 16, -20, 14, 20, 1) | ||
| 169 | _set_up_invis_wall(14, 16, -26.5, 1, 20, 4) | ||
| 170 | _set_up_invis_wall(28.5, 20.5, -26.5, 1, 15, 25) | ||
| 171 | _set_up_invis_wall(40.5, 20.5, -11, 30, 15, 1) | ||
| 172 | _set_up_invis_wall(50.5, 15, 5.5, 7, 10, 1) | ||
| 173 | _set_up_invis_wall(83.5, 33.5, 5.5, 1, 7, 11) | ||
| 174 | _set_up_invis_wall(83.5, 33.5, -5.5, 1, 7, 11) | ||
| 175 | |||
| 176 | var warp_exit_prefab = preload("res://objects/nodes/exit.tscn") | ||
| 177 | var warp_exit = warp_exit_prefab.instantiate() | ||
| 178 | warp_exit.name = "roof_access_blocker_warp_exit" | ||
| 179 | warp_exit.position = Vector3(58, 10, 0) | ||
| 180 | warp_exit.rotation_degrees.y = 90 | ||
| 181 | get_parent().add_child.call_deferred(warp_exit) | ||
| 182 | |||
| 183 | var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn") | ||
| 184 | var warp_enter = warp_enter_prefab.instantiate() | ||
| 185 | warp_enter.target = warp_exit | ||
| 186 | warp_enter.position = Vector3(76.5, 30, 1) | ||
| 187 | warp_enter.scale = Vector3(4, 1.5, 1) | ||
| 188 | warp_enter.rotation_degrees.y = 90 | ||
| 189 | get_parent().add_child.call_deferred(warp_enter) | ||
| 190 | 171 | ||
| 191 | super._ready() | 172 | super._ready() |
| 192 | 173 | ||
| @@ -196,18 +177,5 @@ func _ready(): | |||
| 196 | ap.stop_batching_locations() | 177 | ap.stop_batching_locations() |
| 197 | 178 | ||
| 198 | 179 | ||
| 199 | func _set_up_invis_wall(x, y, z, sx, sy, sz): | 180 | func _process(_dt): |
| 200 | var prefab = preload("res://objects/nodes/block.tscn") | 181 | compass.update_rotation(global_rotation.y) |
| 201 | var newwall = prefab.instantiate() | ||
| 202 | newwall.position.x = x | ||
| 203 | newwall.position.y = y | ||
| 204 | newwall.position.z = z | ||
| 205 | newwall.scale.x = sz | ||
| 206 | newwall.scale.y = sy | ||
| 207 | newwall.scale.z = sx | ||
| 208 | newwall.set_surface_override_material(0, preload("res://assets/materials/blackMatte.material")) | ||
| 209 | newwall.visibility_range_end = 3 | ||
| 210 | newwall.visibility_range_end_margin = 1 | ||
| 211 | newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF | ||
| 212 | newwall.skeleton = ".." | ||
| 213 | get_parent().add_child.call_deferred(newwall) | ||
| diff --git a/apworld/client/rainbowText.gd b/apworld/client/rainbowText.gd new file mode 100644 index 0000000..9a4c1d0 --- /dev/null +++ b/apworld/client/rainbowText.gd | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | extends RichTextEffect | ||
| 2 | |||
| 3 | var bbcode = "rainbow" | ||
| 4 | |||
| 5 | |||
| 6 | func _process_custom_fx(char_fx: CharFXTransform): | ||
| 7 | char_fx.color = Color.from_hsv( | ||
| 8 | char_fx.elapsed_time - floor(char_fx.elapsed_time), 1.0, 1.0, 1.0 | ||
| 9 | ) | ||
| 10 | return true | ||
| diff --git a/apworld/client/run_from_apworld.tscn b/apworld/client/run_from_apworld.tscn new file mode 100644 index 0000000..11373e0 --- /dev/null +++ b/apworld/client/run_from_apworld.tscn | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | [gd_scene load_steps=11 format=2] | ||
| 2 | |||
| 3 | [sub_resource id=2 type="GDScript"] | ||
| 4 | script/source = "extends Node2D | ||
| 5 | |||
| 6 | |||
| 7 | func _ready(): | ||
| 8 | var args = OS.get_cmdline_user_args() | ||
| 9 | var apworld_path = args[0] | ||
| 10 | |||
| 11 | var zip_reader = ZIPReader.new() | ||
| 12 | zip_reader.open(apworld_path) | ||
| 13 | |||
| 14 | var runtime_script = GDScript.new() | ||
| 15 | runtime_script.source_code = zip_reader.read_file(\"lingo2/client/apworld_runtime.gd\").get_string_from_utf8() | ||
| 16 | runtime_script.reload() | ||
| 17 | |||
| 18 | zip_reader.close() | ||
| 19 | |||
| 20 | var runtime = runtime_script.new(apworld_path) | ||
| 21 | runtime.name = \"Runtime\" | ||
| 22 | |||
| 23 | global.add_child(runtime) | ||
| 24 | |||
| 25 | runtime.load_script_as_scene.call_deferred(\"settings_screen.gd\", \"settings_screen\") | ||
| 26 | |||
| 27 | " | ||
| 28 | |||
| 29 | [node name="loader" type="Node2D"] | ||
| 30 | script = SubResource( 2 ) | ||
| diff --git a/apworld/client/run_from_source.tscn b/apworld/client/run_from_source.tscn new file mode 100644 index 0000000..59a914d --- /dev/null +++ b/apworld/client/run_from_source.tscn | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | [gd_scene load_steps=11 format=2] | ||
| 2 | |||
| 3 | [sub_resource id=2 type="GDScript"] | ||
| 4 | script/source = "extends Node2D | ||
| 5 | |||
| 6 | |||
| 7 | func _ready(): | ||
| 8 | var args = OS.get_cmdline_user_args() | ||
| 9 | var source_path = args[0] | ||
| 10 | |||
| 11 | var runtime_script = ResourceLoader.load(\"%s/source_runtime.gd\" % source_path) | ||
| 12 | var runtime = runtime_script.new(source_path) | ||
| 13 | runtime.name = \"Runtime\" | ||
| 14 | |||
| 15 | global.add_child(runtime) | ||
| 16 | |||
| 17 | runtime.load_script_as_scene.call_deferred(\"settings_screen.gd\", \"settings_screen\") | ||
| 18 | |||
| 19 | " | ||
| 20 | |||
| 21 | [node name="loader" type="Node2D"] | ||
| 22 | script = SubResource( 2 ) | ||
| diff --git a/apworld/client/saver.gd b/apworld/client/saver.gd new file mode 100644 index 0000000..44bc179 --- /dev/null +++ b/apworld/client/saver.gd | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | extends "res://scripts/nodes/saver.gd" | ||
| 2 | |||
| 3 | |||
| 4 | func levelLoaded(): | ||
| 5 | if type == "keyholders": | ||
| 6 | var ap = global.get_node("Archipelago") | ||
| 7 | ap.keyboard.load_keyholders.call_deferred(global.map) | ||
| 8 | else: | ||
| 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/apworld/client/settings_screen.gd b/apworld/client/settings_screen.gd new file mode 100644 index 0000000..89e8b68 --- /dev/null +++ b/apworld/client/settings_screen.gd | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | |||
| 4 | func _ready(): | ||
| 5 | var theme = preload("res://assets/themes/baseUI.tres") | ||
| 6 | |||
| 7 | var simple_style_box = StyleBoxFlat.new() | ||
| 8 | simple_style_box.bg_color = Color(0, 0, 0, 0) | ||
| 9 | |||
| 10 | var panel = Panel.new() | ||
| 11 | panel.name = "Panel" | ||
| 12 | panel.offset_right = 1920.0 | ||
| 13 | panel.offset_bottom = 1080.0 | ||
| 14 | add_child(panel) | ||
| 15 | |||
| 16 | var title = Label.new() | ||
| 17 | title.name = "title" | ||
| 18 | title.offset_left = 0.0 | ||
| 19 | title.offset_top = 75.0 | ||
| 20 | title.offset_right = 1920.0 | ||
| 21 | title.offset_bottom = 225.0 | ||
| 22 | title.text = "ARCHIPELAGO" | ||
| 23 | title.vertical_alignment = VERTICAL_ALIGNMENT_CENTER | ||
| 24 | title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER | ||
| 25 | title.theme = theme | ||
| 26 | panel.add_child(title) | ||
| 27 | |||
| 28 | var connect_button = Button.new() | ||
| 29 | connect_button.name = "connect_button" | ||
| 30 | connect_button.offset_left = 255.0 | ||
| 31 | connect_button.offset_top = 875.0 | ||
| 32 | connect_button.offset_right = 891.0 | ||
| 33 | connect_button.offset_bottom = 1025.0 | ||
| 34 | connect_button.add_theme_color_override("font_color_hover", Color(1, 0.501961, 0, 1)) | ||
| 35 | connect_button.text = "CONNECT" | ||
| 36 | connect_button.theme = theme | ||
| 37 | panel.add_child(connect_button) | ||
| 38 | |||
| 39 | var quit_button = Button.new() | ||
| 40 | quit_button.name = "quit_button" | ||
| 41 | quit_button.offset_left = 1102.0 | ||
| 42 | quit_button.offset_top = 875.0 | ||
| 43 | quit_button.offset_right = 1738.0 | ||
| 44 | quit_button.offset_bottom = 1025.0 | ||
| 45 | quit_button.add_theme_color_override("font_color_hover", Color(1, 0, 0, 1)) | ||
| 46 | quit_button.text = "QUIT" | ||
| 47 | quit_button.theme = theme | ||
| 48 | panel.add_child(quit_button) | ||
| 49 | |||
| 50 | var credit2 = Label.new() | ||
| 51 | credit2.name = "credit2" | ||
| 52 | credit2.offset_left = -105.0 | ||
| 53 | credit2.offset_top = 346.0 | ||
| 54 | credit2.offset_right = 485.0 | ||
| 55 | credit2.offset_bottom = 410.0 | ||
| 56 | credit2.add_theme_stylebox_override("normal", simple_style_box) | ||
| 57 | credit2.text = "SERVER" | ||
| 58 | credit2.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT | ||
| 59 | credit2.theme = theme | ||
| 60 | panel.add_child(credit2) | ||
| 61 | |||
| 62 | var credit3 = Label.new() | ||
| 63 | credit3.name = "credit3" | ||
| 64 | credit3.offset_left = -105.0 | ||
| 65 | credit3.offset_top = 519.0 | ||
| 66 | credit3.offset_right = 485.0 | ||
| 67 | credit3.offset_bottom = 583.0 | ||
| 68 | credit3.add_theme_stylebox_override("normal", simple_style_box) | ||
| 69 | credit3.text = "PLAYER" | ||
| 70 | credit3.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT | ||
| 71 | credit3.theme = theme | ||
| 72 | panel.add_child(credit3) | ||
| 73 | |||
| 74 | var credit4 = Label.new() | ||
| 75 | credit4.name = "credit4" | ||
| 76 | credit4.offset_left = -105.0 | ||
| 77 | credit4.offset_top = 704.0 | ||
| 78 | credit4.offset_right = 485.0 | ||
| 79 | credit4.offset_bottom = 768.0 | ||
| 80 | credit4.add_theme_stylebox_override("normal", simple_style_box) | ||
| 81 | credit4.text = "PASSWORD" | ||
| 82 | credit4.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT | ||
| 83 | credit4.theme = theme | ||
| 84 | panel.add_child(credit4) | ||
| 85 | |||
| 86 | var credit5 = Label.new() | ||
| 87 | credit5.name = "credit5" | ||
| 88 | credit5.offset_left = 1239.0 | ||
| 89 | credit5.offset_top = 422.0 | ||
| 90 | credit5.offset_right = 1829.0 | ||
| 91 | credit5.offset_bottom = 486.0 | ||
| 92 | credit5.add_theme_stylebox_override("normal", simple_style_box) | ||
| 93 | credit5.text = "OPTIONS" | ||
| 94 | credit5.theme = theme | ||
| 95 | panel.add_child(credit5) | ||
| 96 | |||
| 97 | var server_box = LineEdit.new() | ||
| 98 | server_box.name = "server_box" | ||
| 99 | server_box.offset_left = 502.0 | ||
| 100 | server_box.offset_top = 295.0 | ||
| 101 | server_box.offset_right = 1144.0 | ||
| 102 | server_box.offset_bottom = 445.0 | ||
| 103 | server_box.alignment = HORIZONTAL_ALIGNMENT_CENTER | ||
| 104 | server_box.caret_blink = true | ||
| 105 | panel.add_child(server_box) | ||
| 106 | |||
| 107 | var player_box = LineEdit.new() | ||
| 108 | player_box.name = "player_box" | ||
| 109 | player_box.offset_left = 502.0 | ||
| 110 | player_box.offset_top = 477.0 | ||
| 111 | player_box.offset_right = 1144.0 | ||
| 112 | player_box.offset_bottom = 627.0 | ||
| 113 | player_box.alignment = HORIZONTAL_ALIGNMENT_CENTER | ||
| 114 | player_box.caret_blink = true | ||
| 115 | panel.add_child(player_box) | ||
| 116 | |||
| 117 | var password_box = LineEdit.new() | ||
| 118 | password_box.name = "password_box" | ||
| 119 | password_box.offset_left = 502.0 | ||
| 120 | password_box.offset_top = 659.0 | ||
| 121 | password_box.offset_right = 1144.0 | ||
| 122 | password_box.offset_bottom = 809.0 | ||
| 123 | password_box.alignment = HORIZONTAL_ALIGNMENT_CENTER | ||
| 124 | password_box.caret_blink = true | ||
| 125 | panel.add_child(password_box) | ||
| 126 | |||
| 127 | var accept_dialog = AcceptDialog.new() | ||
| 128 | accept_dialog.name = "AcceptDialog" | ||
| 129 | panel.add_child(accept_dialog) | ||
| 130 | |||
| 131 | var version_mismatch = ConfirmationDialog.new() | ||
| 132 | version_mismatch.name = "VersionMismatch" | ||
| 133 | panel.add_child(version_mismatch) | ||
| 134 | |||
| 135 | var connection_history = MenuButton.new() | ||
| 136 | connection_history.name = "connection_history" | ||
| 137 | connection_history.offset_left = 1239.0 | ||
| 138 | connection_history.offset_top = 276.0 | ||
| 139 | connection_history.offset_right = 1829.0 | ||
| 140 | connection_history.offset_bottom = 372.0 | ||
| 141 | connection_history.text = "connection history" | ||
| 142 | connection_history.flat = false | ||
| 143 | panel.add_child(connection_history) | ||
| 144 | |||
| 145 | var runtime = global.get_node("Runtime") | ||
| 146 | var main_script = runtime.load_script("main.gd") | ||
| 147 | var main_node = main_script.new() | ||
| 148 | main_node.name = "Main" | ||
| 149 | add_child(main_node) | ||
| diff --git a/apworld/client/source_runtime.gd b/apworld/client/source_runtime.gd new file mode 100644 index 0000000..146587a --- /dev/null +++ b/apworld/client/source_runtime.gd | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | var source_path | ||
| 4 | |||
| 5 | |||
| 6 | func _init(path): | ||
| 7 | source_path = path | ||
| 8 | |||
| 9 | |||
| 10 | func path_exists(path): | ||
| 11 | return FileAccess.file_exists("%s/%s" % [source_path, path]) | ||
| 12 | |||
| 13 | |||
| 14 | func load_script(path): | ||
| 15 | return ResourceLoader.load("%s/%s" % [source_path, path]) | ||
| 16 | |||
| 17 | |||
| 18 | func read_path(path): | ||
| 19 | return FileAccess.get_file_as_bytes("%s/%s" % [source_path, path]) | ||
| 20 | |||
| 21 | |||
| 22 | func load_script_as_scene(path, scene_name): | ||
| 23 | var script = load_script(path) | ||
| 24 | var instance = script.new() | ||
| 25 | instance.name = scene_name | ||
| 26 | |||
| 27 | get_tree().unload_current_scene() | ||
| 28 | _load_scene.call_deferred(instance) | ||
| 29 | |||
| 30 | |||
| 31 | func _load_scene(instance): | ||
| 32 | get_tree().get_root().add_child(instance) | ||
| 33 | get_tree().current_scene = instance | ||
| diff --git a/client/Archipelago/door.gd b/apworld/client/teleport.gd index fead818..428d50b 100644 --- a/client/Archipelago/door.gd +++ b/apworld/client/teleport.gd | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | extends "res://scripts/nodes/door.gd" | 1 | extends "res://scripts/nodes/teleport.gd" |
| 2 | 2 | ||
| 3 | var item_id | 3 | var item_id |
| 4 | var item_amount | 4 | var item_amount |
| diff --git a/client/Archipelago/teleportListener.gd b/apworld/client/teleportListener.gd index 4a7deec..6f363af 100644 --- a/client/Archipelago/teleportListener.gd +++ b/apworld/client/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/apworld/client/textclient.gd b/apworld/client/textclient.gd new file mode 100644 index 0000000..ce28a3a --- /dev/null +++ b/apworld/client/textclient.gd | |||
| @@ -0,0 +1,508 @@ | |||
| 1 | extends CanvasLayer | ||
| 2 | |||
| 3 | var tabs | ||
| 4 | var panel | ||
| 5 | var label | ||
| 6 | var entry | ||
| 7 | var is_open = false | ||
| 8 | |||
| 9 | var locations_overlay | ||
| 10 | var location_texture | ||
| 11 | var worldport_texture | ||
| 12 | var goal_texture | ||
| 13 | |||
| 14 | var tracker_tree | ||
| 15 | var tracker_loc_tree_item_by_id = {} | ||
| 16 | var tracker_port_tree_item_by_id = {} | ||
| 17 | var tracker_goal_tree_item = null | ||
| 18 | var tracker_object_by_index = {} | ||
| 19 | var tracker_object_by_ignored_index = {} | ||
| 20 | var tracker_ignored_group = null | ||
| 21 | |||
| 22 | var worldports_tab | ||
| 23 | var worldports_tree | ||
| 24 | var port_tree_item_by_map = {} | ||
| 25 | var port_tree_item_by_map_port = {} | ||
| 26 | |||
| 27 | const kLocation = 0 | ||
| 28 | const kWorldport = 1 | ||
| 29 | const kGoal = 2 | ||
| 30 | |||
| 31 | |||
| 32 | func _ready(): | ||
| 33 | process_mode = ProcessMode.PROCESS_MODE_ALWAYS | ||
| 34 | layer = 2 | ||
| 35 | |||
| 36 | locations_overlay = RichTextLabel.new() | ||
| 37 | locations_overlay.name = "LocationsOverlay" | ||
| 38 | locations_overlay.offset_top = 220 | ||
| 39 | locations_overlay.offset_bottom = 720 | ||
| 40 | locations_overlay.offset_left = 20 | ||
| 41 | locations_overlay.anchor_right = 1.0 | ||
| 42 | locations_overlay.offset_right = -10 | ||
| 43 | locations_overlay.scroll_active = false | ||
| 44 | locations_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE | ||
| 45 | locations_overlay.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST | ||
| 46 | add_child(locations_overlay) | ||
| 47 | update_locations_visibility() | ||
| 48 | |||
| 49 | tabs = TabContainer.new() | ||
| 50 | tabs.name = "Tabs" | ||
| 51 | tabs.offset_left = 100 | ||
| 52 | tabs.offset_right = 1820 | ||
| 53 | tabs.offset_top = 100 | ||
| 54 | tabs.offset_bottom = 980 | ||
| 55 | tabs.visible = false | ||
| 56 | tabs.theme = preload("res://assets/themes/baseUI.tres") | ||
| 57 | tabs.add_theme_font_size_override("font_size", 36) | ||
| 58 | add_child(tabs) | ||
| 59 | |||
| 60 | panel = MarginContainer.new() | ||
| 61 | panel.name = "Text Client" | ||
| 62 | panel.add_theme_constant_override("margin_top", 60) | ||
| 63 | panel.add_theme_constant_override("margin_left", 60) | ||
| 64 | panel.add_theme_constant_override("margin_right", 60) | ||
| 65 | panel.add_theme_constant_override("margin_bottom", 60) | ||
| 66 | tabs.add_child(panel) | ||
| 67 | |||
| 68 | label = RichTextLabel.new() | ||
| 69 | label.set_name("Label") | ||
| 70 | label.scroll_following = true | ||
| 71 | label.selection_enabled = true | ||
| 72 | label.size_flags_horizontal = Control.SIZE_EXPAND_FILL | ||
| 73 | label.size_flags_vertical = Control.SIZE_EXPAND_FILL | ||
| 74 | label.push_font(preload("res://assets/fonts/Lingo2.ttf")) | ||
| 75 | label.push_font_size(30) | ||
| 76 | |||
| 77 | var entry_style = StyleBoxFlat.new() | ||
| 78 | entry_style.bg_color = Color(0.9, 0.9, 0.9, 1) | ||
| 79 | |||
| 80 | entry = LineEdit.new() | ||
| 81 | entry.set_name("Entry") | ||
| 82 | entry.add_theme_font_override("font", preload("res://assets/fonts/Lingo2.ttf")) | ||
| 83 | entry.add_theme_font_size_override("font_size", 36) | ||
| 84 | entry.add_theme_color_override("font_color", Color(0, 0, 0, 1)) | ||
| 85 | entry.add_theme_color_override("cursor_color", Color(0, 0, 0, 1)) | ||
| 86 | entry.add_theme_stylebox_override("focus", entry_style) | ||
| 87 | entry.text_submitted.connect(text_entered) | ||
| 88 | |||
| 89 | var tc_arranger = VBoxContainer.new() | ||
| 90 | tc_arranger.add_child(label) | ||
| 91 | tc_arranger.add_child(entry) | ||
| 92 | tc_arranger.add_theme_constant_override("separation", 40) | ||
| 93 | panel.add_child(tc_arranger) | ||
| 94 | |||
| 95 | var tracker_margins = MarginContainer.new() | ||
| 96 | tracker_margins.name = "Locations" | ||
| 97 | tracker_margins.add_theme_constant_override("margin_top", 60) | ||
| 98 | tracker_margins.add_theme_constant_override("margin_left", 60) | ||
| 99 | tracker_margins.add_theme_constant_override("margin_right", 60) | ||
| 100 | tracker_margins.add_theme_constant_override("margin_bottom", 60) | ||
| 101 | tabs.add_child(tracker_margins) | ||
| 102 | |||
| 103 | tracker_tree = Tree.new() | ||
| 104 | tracker_tree.columns = 4 | ||
| 105 | tracker_tree.hide_root = true | ||
| 106 | tracker_tree.add_theme_font_size_override("font_size", 24) | ||
| 107 | tracker_tree.add_theme_color_override("font_color", Color(0.8, 0.8, 0.8, 1)) | ||
| 108 | tracker_tree.add_theme_constant_override("v_separation", 1) | ||
| 109 | tracker_tree.item_edited.connect(_on_tracker_button_clicked) | ||
| 110 | tracker_tree.set_column_expand(0, false) | ||
| 111 | tracker_tree.set_column_expand(1, true) | ||
| 112 | tracker_tree.set_column_expand(2, false) | ||
| 113 | tracker_tree.set_column_expand(3, false) | ||
| 114 | tracker_tree.set_column_custom_minimum_width(2, 200) | ||
| 115 | tracker_tree.set_column_custom_minimum_width(3, 200) | ||
| 116 | tracker_margins.add_child(tracker_tree) | ||
| 117 | |||
| 118 | worldports_tab = MarginContainer.new() | ||
| 119 | worldports_tab.name = "Worldports" | ||
| 120 | worldports_tab.add_theme_constant_override("margin_top", 60) | ||
| 121 | worldports_tab.add_theme_constant_override("margin_left", 60) | ||
| 122 | worldports_tab.add_theme_constant_override("margin_right", 60) | ||
| 123 | worldports_tab.add_theme_constant_override("margin_bottom", 60) | ||
| 124 | tabs.add_child(worldports_tab) | ||
| 125 | tabs.set_tab_hidden(2, true) | ||
| 126 | |||
| 127 | worldports_tree = Tree.new() | ||
| 128 | worldports_tree.columns = 2 | ||
| 129 | worldports_tree.hide_root = true | ||
| 130 | worldports_tree.theme = preload("res://assets/themes/baseUI.tres") | ||
| 131 | worldports_tree.add_theme_font_size_override("font_size", 24) | ||
| 132 | worldports_tab.add_child(worldports_tree) | ||
| 133 | |||
| 134 | var runtime = global.get_node("Runtime") | ||
| 135 | var location_image = Image.new() | ||
| 136 | location_image.load_png_from_buffer(runtime.read_path("assets/location.png")) | ||
| 137 | location_texture = ImageTexture.create_from_image(location_image) | ||
| 138 | |||
| 139 | var worldport_image = Image.new() | ||
| 140 | worldport_image.load_png_from_buffer(runtime.read_path("assets/worldport.png")) | ||
| 141 | worldport_texture = ImageTexture.create_from_image(worldport_image) | ||
| 142 | |||
| 143 | var goal_image = Image.new() | ||
| 144 | goal_image.load_png_from_buffer(runtime.read_path("assets/goal.png")) | ||
| 145 | goal_texture = ImageTexture.create_from_image(goal_image) | ||
| 146 | |||
| 147 | |||
| 148 | func _input(event): | ||
| 149 | if global.loaded and event is InputEventKey and event.pressed: | ||
| 150 | if event.keycode == KEY_TAB and !Input.is_key_pressed(KEY_SHIFT): | ||
| 151 | if !get_tree().paused: | ||
| 152 | is_open = true | ||
| 153 | get_tree().paused = true | ||
| 154 | Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) | ||
| 155 | tabs.visible = true | ||
| 156 | entry.grab_focus() | ||
| 157 | get_viewport().set_input_as_handled() | ||
| 158 | else: | ||
| 159 | dismiss() | ||
| 160 | elif event.keycode == KEY_ESCAPE: | ||
| 161 | if is_open: | ||
| 162 | dismiss() | ||
| 163 | get_viewport().set_input_as_handled() | ||
| 164 | |||
| 165 | |||
| 166 | func dismiss(): | ||
| 167 | if is_open: | ||
| 168 | get_tree().paused = false | ||
| 169 | Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) | ||
| 170 | tabs.visible = false | ||
| 171 | is_open = false | ||
| 172 | |||
| 173 | |||
| 174 | func parse_printjson(text): | ||
| 175 | label.append_text("[p]" + text + "[/p]") | ||
| 176 | |||
| 177 | |||
| 178 | func text_entered(text): | ||
| 179 | var ap = global.get_node("Archipelago") | ||
| 180 | var cmd = text.trim_suffix("\n") | ||
| 181 | entry.text = "" | ||
| 182 | if OS.is_debug_build(): | ||
| 183 | if cmd.begins_with("/tp_map "): | ||
| 184 | var new_map = cmd.substr(8) | ||
| 185 | global.map = new_map | ||
| 186 | global.sets_entry_point = false | ||
| 187 | switcher.switch_map("res://objects/scenes/%s.tscn" % new_map) | ||
| 188 | return | ||
| 189 | |||
| 190 | ap.client.say(cmd) | ||
| 191 | |||
| 192 | |||
| 193 | func update_locations(reset_locations = true): | ||
| 194 | var ap = global.get_node("Archipelago") | ||
| 195 | var gamedata = global.get_node("Gamedata") | ||
| 196 | |||
| 197 | locations_overlay.clear() | ||
| 198 | locations_overlay.push_font(preload("res://assets/fonts/Lingo2.ttf")) | ||
| 199 | locations_overlay.push_font_size(24) | ||
| 200 | locations_overlay.push_color(Color(0.9, 0.9, 0.9, 1)) | ||
| 201 | locations_overlay.push_outline_color(Color(0, 0, 0, 1)) | ||
| 202 | locations_overlay.push_outline_size(2) | ||
| 203 | |||
| 204 | var locations = [] | ||
| 205 | for location_id in ap.client._accessible_locations: | ||
| 206 | if not ap.client._checked_locations.has(location_id): | ||
| 207 | var location_name = gamedata.location_name_by_id.get(location_id, "(Unknown)") | ||
| 208 | ( | ||
| 209 | locations | ||
| 210 | . append( | ||
| 211 | { | ||
| 212 | "name": location_name, | ||
| 213 | "type": kLocation, | ||
| 214 | "id": location_id, | ||
| 215 | "ignored": ap._ignored_locations.has(location_id), | ||
| 216 | "hint": ap.client._hinted_locations.has(location_id), | ||
| 217 | } | ||
| 218 | ) | ||
| 219 | ) | ||
| 220 | |||
| 221 | for port_id in ap.client._accessible_worldports: | ||
| 222 | if not ap.client._checked_worldports.has(port_id): | ||
| 223 | var port_name = gamedata.get_worldport_display_name(port_id) | ||
| 224 | ( | ||
| 225 | locations | ||
| 226 | . append( | ||
| 227 | { | ||
| 228 | "name": port_name, | ||
| 229 | "type": kWorldport, | ||
| 230 | "id": port_id, | ||
| 231 | "ignored": false, | ||
| 232 | "hint": false, | ||
| 233 | } | ||
| 234 | ) | ||
| 235 | ) | ||
| 236 | |||
| 237 | locations.sort_custom(_cmp_tracker_objects) | ||
| 238 | |||
| 239 | if ap.client._goal_accessible: | ||
| 240 | var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[ | ||
| 241 | ap.victory_condition | ||
| 242 | ]] | ||
| 243 | ( | ||
| 244 | locations | ||
| 245 | . push_front( | ||
| 246 | { | ||
| 247 | "name": location_name, | ||
| 248 | "type": kGoal, | ||
| 249 | "ignored": false, | ||
| 250 | "hint": false, | ||
| 251 | } | ||
| 252 | ) | ||
| 253 | ) | ||
| 254 | |||
| 255 | var count = 0 | ||
| 256 | for location in locations: | ||
| 257 | if count < 18 and not location["ignored"]: | ||
| 258 | locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT) | ||
| 259 | if location["hint"]: | ||
| 260 | locations_overlay.push_color(Color("#fafad2")) | ||
| 261 | locations_overlay.append_text(location["name"]) | ||
| 262 | locations_overlay.append_text(" ") | ||
| 263 | if location["type"] == kLocation: | ||
| 264 | locations_overlay.add_image(location_texture) | ||
| 265 | elif location["type"] == kWorldport: | ||
| 266 | locations_overlay.add_image(worldport_texture) | ||
| 267 | elif location["type"] == kGoal: | ||
| 268 | locations_overlay.add_image(goal_texture) | ||
| 269 | if location["hint"]: | ||
| 270 | locations_overlay.pop() | ||
| 271 | locations_overlay.pop() | ||
| 272 | count += 1 | ||
| 273 | |||
| 274 | if count > 18: | ||
| 275 | locations_overlay.append_text("[p align=right][lb]...[rb][/p]") | ||
| 276 | |||
| 277 | if reset_locations: | ||
| 278 | reset_tracker_tab() | ||
| 279 | |||
| 280 | var root_ti = tracker_tree.create_item(null) | ||
| 281 | |||
| 282 | for location in locations: | ||
| 283 | var loc_row | ||
| 284 | |||
| 285 | if location["ignored"]: | ||
| 286 | if tracker_ignored_group == null: | ||
| 287 | tracker_ignored_group = root_ti.create_child() | ||
| 288 | tracker_ignored_group.set_text(1, "Ignored Locations") | ||
| 289 | tracker_ignored_group.set_selectable(0, false) | ||
| 290 | tracker_ignored_group.set_selectable(1, false) | ||
| 291 | tracker_ignored_group.set_selectable(2, false) | ||
| 292 | tracker_ignored_group.set_selectable(3, false) | ||
| 293 | |||
| 294 | loc_row = tracker_ignored_group.create_child() | ||
| 295 | else: | ||
| 296 | loc_row = root_ti.create_child() | ||
| 297 | |||
| 298 | loc_row.set_cell_mode(0, TreeItem.CELL_MODE_ICON) | ||
| 299 | loc_row.set_selectable(0, false) | ||
| 300 | loc_row.set_text(1, location["name"]) | ||
| 301 | loc_row.set_selectable(1, false) | ||
| 302 | if location["hint"]: | ||
| 303 | loc_row.set_custom_color(1, Color("#fafad2")) | ||
| 304 | loc_row.set_cell_mode(2, TreeItem.CELL_MODE_CUSTOM) | ||
| 305 | loc_row.set_text(2, "Show Path") | ||
| 306 | loc_row.set_custom_as_button(2, true) | ||
| 307 | loc_row.set_editable(2, true) | ||
| 308 | loc_row.set_selectable(2, false) | ||
| 309 | loc_row.set_text_alignment(2, HORIZONTAL_ALIGNMENT_CENTER) | ||
| 310 | loc_row.set_selectable(3, false) | ||
| 311 | if location["type"] == kLocation: | ||
| 312 | loc_row.set_cell_mode(3, TreeItem.CELL_MODE_CUSTOM) | ||
| 313 | if location["ignored"]: | ||
| 314 | loc_row.set_text(3, "Unignore") | ||
| 315 | else: | ||
| 316 | loc_row.set_text(3, "Ignore") | ||
| 317 | loc_row.set_custom_as_button(3, true) | ||
| 318 | loc_row.set_editable(3, true) | ||
| 319 | loc_row.set_text_alignment(3, HORIZONTAL_ALIGNMENT_CENTER) | ||
| 320 | |||
| 321 | if location["type"] == kLocation: | ||
| 322 | loc_row.set_icon(0, location_texture) | ||
| 323 | tracker_loc_tree_item_by_id[location["id"]] = loc_row | ||
| 324 | elif location["type"] == kWorldport: | ||
| 325 | loc_row.set_icon(0, worldport_texture) | ||
| 326 | tracker_port_tree_item_by_id[location["id"]] = loc_row | ||
| 327 | elif location["type"] == kGoal: | ||
| 328 | loc_row.set_icon(0, goal_texture) | ||
| 329 | tracker_goal_tree_item = loc_row | ||
| 330 | |||
| 331 | if location["ignored"]: | ||
| 332 | tracker_object_by_ignored_index[loc_row.get_index()] = location | ||
| 333 | else: | ||
| 334 | tracker_object_by_index[loc_row.get_index()] = location | ||
| 335 | else: | ||
| 336 | for loc_row in tracker_tree.get_root().get_children(): | ||
| 337 | loc_row.visible = false | ||
| 338 | |||
| 339 | for location_id in tracker_loc_tree_item_by_id.keys(): | ||
| 340 | if ( | ||
| 341 | ap.client._accessible_locations.has(location_id) | ||
| 342 | and not ap.client._checked_locations.has(location_id) | ||
| 343 | ): | ||
| 344 | tracker_loc_tree_item_by_id[location_id].visible = true | ||
| 345 | |||
| 346 | for port_id in tracker_port_tree_item_by_id.keys(): | ||
| 347 | if ( | ||
| 348 | ap.client._accessible_worldports.has(port_id) | ||
| 349 | and not ap.client._checked_worldports.has(port_id) | ||
| 350 | ): | ||
| 351 | tracker_port_tree_item_by_id[port_id].visible = true | ||
| 352 | |||
| 353 | if tracker_goal_tree_item != null and ap.client._goal_accessible: | ||
| 354 | tracker_goal_tree_item.visible = true | ||
| 355 | |||
| 356 | if tracker_ignored_group != null: | ||
| 357 | tracker_ignored_group.visible = true | ||
| 358 | |||
| 359 | |||
| 360 | func _cmp_tracker_objects(a, b) -> bool: | ||
| 361 | if a["ignored"] != b["ignored"]: | ||
| 362 | return !a["ignored"] | ||
| 363 | elif a["hint"] != b["hint"]: | ||
| 364 | return a["hint"] | ||
| 365 | else: | ||
| 366 | return a["name"] < b["name"] | ||
| 367 | |||
| 368 | |||
| 369 | func update_locations_visibility(): | ||
| 370 | var ap = global.get_node("Archipelago") | ||
| 371 | locations_overlay.visible = ap.show_locations | ||
| 372 | |||
| 373 | |||
| 374 | func _on_tracker_button_clicked(): | ||
| 375 | var ap = global.get_node("Archipelago") | ||
| 376 | |||
| 377 | var edited_item = tracker_tree.get_edited() | ||
| 378 | var edited_index = edited_item.get_index() | ||
| 379 | |||
| 380 | if edited_item.get_parent() == tracker_tree.get_root(): | ||
| 381 | if tracker_object_by_index.has(edited_index): | ||
| 382 | var tracker_object = tracker_object_by_index[edited_index] | ||
| 383 | if tracker_tree.get_edited_column() == 2: | ||
| 384 | var type_str = "" | ||
| 385 | if tracker_object["type"] == kLocation: | ||
| 386 | type_str = "location" | ||
| 387 | elif tracker_object["type"] == kWorldport: | ||
| 388 | type_str = "worldport" | ||
| 389 | elif tracker_object["type"] == kGoal: | ||
| 390 | type_str = "goal" | ||
| 391 | ap.client.getLogicalPath(type_str, tracker_object.get("id", null)) | ||
| 392 | elif tracker_tree.get_edited_column() == 3: | ||
| 393 | ap.toggle_ignored_location(tracker_object["id"]) | ||
| 394 | elif edited_item.get_parent() == tracker_ignored_group: | ||
| 395 | # This is the ignored locations group. | ||
| 396 | if ( | ||
| 397 | tracker_object_by_ignored_index.has(edited_index) | ||
| 398 | and tracker_tree.get_edited_column() == 3 | ||
| 399 | ): | ||
| 400 | var tracker_object = tracker_object_by_ignored_index[edited_index] | ||
| 401 | ap.toggle_ignored_location(tracker_object["id"]) | ||
| 402 | |||
| 403 | |||
| 404 | func display_logical_path(object_type, object_id, paths): | ||
| 405 | var ap = global.get_node("Archipelago") | ||
| 406 | var gamedata = global.get_node("Gamedata") | ||
| 407 | |||
| 408 | var location_name = "(Unknown)" | ||
| 409 | if object_type == "location" and object_id != null: | ||
| 410 | location_name = gamedata.location_name_by_id.get(object_id, "(Unknown)") | ||
| 411 | elif object_type == "worldport" and object_id != null: | ||
| 412 | location_name = gamedata.get_worldport_display_name(object_id) | ||
| 413 | elif object_type == "goal": | ||
| 414 | location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[ | ||
| 415 | ap.victory_condition | ||
| 416 | ]] | ||
| 417 | |||
| 418 | label.append_text("[p]Path to %s:[/p]" % location_name) | ||
| 419 | label.append_text("[ol]" + "\n".join(paths) + "[/ol]") | ||
| 420 | |||
| 421 | panel.visible = true | ||
| 422 | |||
| 423 | |||
| 424 | func setup_worldports(): | ||
| 425 | tabs.set_tab_hidden(2, false) | ||
| 426 | |||
| 427 | var root_ti = worldports_tree.create_item(null) | ||
| 428 | |||
| 429 | var ports_by_map_id = {} | ||
| 430 | var display_names_by_map_id = {} | ||
| 431 | var display_names_by_port_id = {} | ||
| 432 | |||
| 433 | var ap = global.get_node("Archipelago") | ||
| 434 | var gamedata = global.get_node("Gamedata") | ||
| 435 | for fpid in ap.port_pairings: | ||
| 436 | var port = gamedata.objects.get_ports()[fpid] | ||
| 437 | var room = gamedata.objects.get_rooms()[port.get_room_id()] | ||
| 438 | |||
| 439 | if not ports_by_map_id.has(room.get_map_id()): | ||
| 440 | ports_by_map_id[room.get_map_id()] = [] | ||
| 441 | |||
| 442 | var map = gamedata.objects.get_maps()[room.get_map_id()] | ||
| 443 | display_names_by_map_id[map.get_id()] = map.get_display_name() | ||
| 444 | |||
| 445 | ports_by_map_id[room.get_map_id()].append(fpid) | ||
| 446 | display_names_by_port_id[fpid] = port.get_display_name() | ||
| 447 | |||
| 448 | var sorted_map_ids = ports_by_map_id.keys().duplicate() | ||
| 449 | sorted_map_ids.sort_custom( | ||
| 450 | func(a, b): return display_names_by_map_id[a] < display_names_by_map_id[b] | ||
| 451 | ) | ||
| 452 | |||
| 453 | for map_id in sorted_map_ids: | ||
| 454 | var map_ti = root_ti.create_child() | ||
| 455 | map_ti.set_text(0, display_names_by_map_id[map_id]) | ||
| 456 | map_ti.visible = false | ||
| 457 | map_ti.collapsed = true | ||
| 458 | port_tree_item_by_map[map_id] = map_ti | ||
| 459 | port_tree_item_by_map_port[map_id] = {} | ||
| 460 | |||
| 461 | var port_ids = ports_by_map_id[map_id] | ||
| 462 | port_ids.sort_custom( | ||
| 463 | func(a, b): return display_names_by_port_id[a] < display_names_by_port_id[b] | ||
| 464 | ) | ||
| 465 | |||
| 466 | for port_id in port_ids: | ||
| 467 | var port_ti = map_ti.create_child() | ||
| 468 | port_ti.set_text(0, display_names_by_port_id[port_id]) | ||
| 469 | port_ti.set_text(1, gamedata.get_worldport_display_name(ap.port_pairings[port_id])) | ||
| 470 | port_ti.visible = false | ||
| 471 | port_tree_item_by_map_port[map_id][port_id] = port_ti | ||
| 472 | |||
| 473 | update_worldports() | ||
| 474 | |||
| 475 | |||
| 476 | func update_worldports(): | ||
| 477 | var ap = global.get_node("Archipelago") | ||
| 478 | |||
| 479 | for map_id in port_tree_item_by_map_port.keys(): | ||
| 480 | var map_visible = false | ||
| 481 | |||
| 482 | for port_id in port_tree_item_by_map_port[map_id].keys(): | ||
| 483 | var ti = port_tree_item_by_map_port[map_id][port_id] | ||
| 484 | ti.visible = ap.client._checked_worldports.has(port_id) | ||
| 485 | |||
| 486 | if ti.visible: | ||
| 487 | map_visible = true | ||
| 488 | |||
| 489 | port_tree_item_by_map[map_id].visible = map_visible | ||
| 490 | |||
| 491 | |||
| 492 | func reset(): | ||
| 493 | locations_overlay.clear() | ||
| 494 | tabs.set_tab_hidden(2, true) | ||
| 495 | port_tree_item_by_map.clear() | ||
| 496 | port_tree_item_by_map_port.clear() | ||
| 497 | worldports_tree.clear() | ||
| 498 | reset_tracker_tab() | ||
| 499 | |||
| 500 | |||
| 501 | func reset_tracker_tab(): | ||
| 502 | tracker_loc_tree_item_by_id.clear() | ||
| 503 | tracker_port_tree_item_by_id.clear() | ||
| 504 | tracker_goal_tree_item = null | ||
| 505 | tracker_object_by_index.clear() | ||
| 506 | tracker_object_by_ignored_index.clear() | ||
| 507 | tracker_ignored_group = null | ||
| 508 | tracker_tree.clear() | ||
| diff --git a/apworld/client/unlockReaderListener.gd b/apworld/client/unlockReaderListener.gd new file mode 100644 index 0000000..a5754b9 --- /dev/null +++ b/apworld/client/unlockReaderListener.gd | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | extends "res://scripts/nodes/listeners/unlockReaderListener.gd" | ||
| 2 | |||
| 3 | var item_id = null | ||
| 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 | super._ready() | ||
| 30 | |||
| 31 | |||
| 32 | func _readier(): | ||
| 33 | if item_id != null: | ||
| 34 | var ap = global.get_node("Archipelago") | ||
| 35 | |||
| 36 | if ap.client.getItemAmount(item_id) >= item_amount: | ||
| 37 | handleTriggered() | ||
| 38 | else: | ||
| 39 | super._readier() | ||
| 40 | |||
| 41 | |||
| 42 | func handleTriggered(): | ||
| 43 | if item_id != null: | ||
| 44 | emit_signal("trigger") | ||
| 45 | else: | ||
| 46 | super.handleTriggered() | ||
| diff --git a/apworld/client/vendor/LICENSE b/apworld/client/vendor/LICENSE new file mode 100644 index 0000000..12763b1 --- /dev/null +++ b/apworld/client/vendor/LICENSE | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | WebSocketServer.gd: | ||
| 2 | |||
| 3 | Copyright (c) 2014-present Godot Engine contributors. Copyright (c) 2007-2014 | ||
| 4 | Juan Linietsky, Ariel Manzur. | ||
| 5 | |||
| 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 7 | this software and associated documentation files (the "Software"), to deal in | ||
| 8 | the Software without restriction, including without limitation the rights to | ||
| 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
| 10 | the Software, and to permit persons to whom the Software is furnished to do so, | ||
| 11 | subject to the following conditions: | ||
| 12 | |||
| 13 | The above copyright notice and this permission notice shall be included in all | ||
| 14 | copies or substantial portions of the Software. | ||
| 15 | |||
| 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
| 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
| 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
| 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| diff --git a/apworld/client/vendor/WebSocketServer.gd b/apworld/client/vendor/WebSocketServer.gd new file mode 100644 index 0000000..2cee494 --- /dev/null +++ b/apworld/client/vendor/WebSocketServer.gd | |||
| @@ -0,0 +1,173 @@ | |||
| 1 | class_name WebSocketServer | ||
| 2 | extends Node | ||
| 3 | |||
| 4 | signal message_received(peer_id: int, message: String) | ||
| 5 | signal client_connected(peer_id: int) | ||
| 6 | signal client_disconnected(peer_id: int) | ||
| 7 | |||
| 8 | @export var handshake_headers := PackedStringArray() | ||
| 9 | @export var supported_protocols := PackedStringArray() | ||
| 10 | @export var handshake_timout := 3000 | ||
| 11 | @export var use_tls := false | ||
| 12 | @export var tls_cert: X509Certificate | ||
| 13 | @export var tls_key: CryptoKey | ||
| 14 | @export var refuse_new_connections := false: | ||
| 15 | set(refuse): | ||
| 16 | if refuse: | ||
| 17 | pending_peers.clear() | ||
| 18 | |||
| 19 | |||
| 20 | class PendingPeer: | ||
| 21 | var connect_time: int | ||
| 22 | var tcp: StreamPeerTCP | ||
| 23 | var connection: StreamPeer | ||
| 24 | var ws: WebSocketPeer | ||
| 25 | |||
| 26 | func _init(p_tcp: StreamPeerTCP) -> void: | ||
| 27 | tcp = p_tcp | ||
| 28 | connection = p_tcp | ||
| 29 | connect_time = Time.get_ticks_msec() | ||
| 30 | |||
| 31 | |||
| 32 | var tcp_server := TCPServer.new() | ||
| 33 | var pending_peers: Array[PendingPeer] = [] | ||
| 34 | var peers: Dictionary | ||
| 35 | |||
| 36 | |||
| 37 | func listen(port: int) -> int: | ||
| 38 | assert(not tcp_server.is_listening()) | ||
| 39 | return tcp_server.listen(port) | ||
| 40 | |||
| 41 | |||
| 42 | func stop() -> void: | ||
| 43 | tcp_server.stop() | ||
| 44 | pending_peers.clear() | ||
| 45 | peers.clear() | ||
| 46 | |||
| 47 | |||
| 48 | func send(peer_id: int, message: String) -> int: | ||
| 49 | var type := typeof(message) | ||
| 50 | if peer_id <= 0: | ||
| 51 | # Send to multiple peers, (zero = broadcast, negative = exclude one). | ||
| 52 | for id: int in peers: | ||
| 53 | if id == -peer_id: | ||
| 54 | continue | ||
| 55 | if type == TYPE_STRING: | ||
| 56 | peers[id].send_text(message) | ||
| 57 | else: | ||
| 58 | peers[id].put_packet(message) | ||
| 59 | return OK | ||
| 60 | |||
| 61 | assert(peers.has(peer_id)) | ||
| 62 | var socket: WebSocketPeer = peers[peer_id] | ||
| 63 | if type == TYPE_STRING: | ||
| 64 | return socket.send_text(message) | ||
| 65 | return socket.send(var_to_bytes(message)) | ||
| 66 | |||
| 67 | |||
| 68 | func get_message(peer_id: int) -> Variant: | ||
| 69 | assert(peers.has(peer_id)) | ||
| 70 | var socket: WebSocketPeer = peers[peer_id] | ||
| 71 | if socket.get_available_packet_count() < 1: | ||
| 72 | return null | ||
| 73 | var pkt: PackedByteArray = socket.get_packet() | ||
| 74 | if socket.was_string_packet(): | ||
| 75 | return pkt.get_string_from_utf8() | ||
| 76 | return bytes_to_var(pkt) | ||
| 77 | |||
| 78 | |||
| 79 | func has_message(peer_id: int) -> bool: | ||
| 80 | assert(peers.has(peer_id)) | ||
| 81 | return peers[peer_id].get_available_packet_count() > 0 | ||
| 82 | |||
| 83 | |||
| 84 | func _create_peer() -> WebSocketPeer: | ||
| 85 | var ws := WebSocketPeer.new() | ||
| 86 | ws.supported_protocols = supported_protocols | ||
| 87 | ws.handshake_headers = handshake_headers | ||
| 88 | return ws | ||
| 89 | |||
| 90 | |||
| 91 | func poll() -> void: | ||
| 92 | if not tcp_server.is_listening(): | ||
| 93 | return | ||
| 94 | |||
| 95 | while not refuse_new_connections and tcp_server.is_connection_available(): | ||
| 96 | var conn: StreamPeerTCP = tcp_server.take_connection() | ||
| 97 | assert(conn != null) | ||
| 98 | pending_peers.append(PendingPeer.new(conn)) | ||
| 99 | |||
| 100 | var to_remove := [] | ||
| 101 | |||
| 102 | for p in pending_peers: | ||
| 103 | if not _connect_pending(p): | ||
| 104 | if p.connect_time + handshake_timout < Time.get_ticks_msec(): | ||
| 105 | # Timeout. | ||
| 106 | to_remove.append(p) | ||
| 107 | continue # Still pending. | ||
| 108 | |||
| 109 | to_remove.append(p) | ||
| 110 | |||
| 111 | for r: RefCounted in to_remove: | ||
| 112 | pending_peers.erase(r) | ||
| 113 | |||
| 114 | to_remove.clear() | ||
| 115 | |||
| 116 | for id: int in peers: | ||
| 117 | var p: WebSocketPeer = peers[id] | ||
| 118 | p.poll() | ||
| 119 | |||
| 120 | if p.get_ready_state() != WebSocketPeer.STATE_OPEN: | ||
| 121 | client_disconnected.emit(id) | ||
| 122 | to_remove.append(id) | ||
| 123 | continue | ||
| 124 | |||
| 125 | while p.get_available_packet_count(): | ||
| 126 | message_received.emit(id, get_message(id)) | ||
| 127 | |||
| 128 | for r: int in to_remove: | ||
| 129 | peers.erase(r) | ||
| 130 | to_remove.clear() | ||
| 131 | |||
| 132 | |||
| 133 | func _connect_pending(p: PendingPeer) -> bool: | ||
| 134 | if p.ws != null: | ||
| 135 | # Poll websocket client if doing handshake. | ||
| 136 | p.ws.poll() | ||
| 137 | var state := p.ws.get_ready_state() | ||
| 138 | if state == WebSocketPeer.STATE_OPEN: | ||
| 139 | var id := randi_range(2, 1 << 30) | ||
| 140 | peers[id] = p.ws | ||
| 141 | client_connected.emit(id) | ||
| 142 | return true # Success. | ||
| 143 | elif state != WebSocketPeer.STATE_CONNECTING: | ||
| 144 | return true # Failure. | ||
| 145 | return false # Still connecting. | ||
| 146 | elif p.tcp.get_status() != StreamPeerTCP.STATUS_CONNECTED: | ||
| 147 | return true # TCP disconnected. | ||
| 148 | elif not use_tls: | ||
| 149 | # TCP is ready, create WS peer. | ||
| 150 | p.ws = _create_peer() | ||
| 151 | p.ws.accept_stream(p.tcp) | ||
| 152 | return false # WebSocketPeer connection is pending. | ||
| 153 | |||
| 154 | else: | ||
| 155 | if p.connection == p.tcp: | ||
| 156 | assert(tls_key != null and tls_cert != null) | ||
| 157 | var tls := StreamPeerTLS.new() | ||
| 158 | tls.accept_stream(p.tcp, TLSOptions.server(tls_key, tls_cert)) | ||
| 159 | p.connection = tls | ||
| 160 | p.connection.poll() | ||
| 161 | var status: StreamPeerTLS.Status = p.connection.get_status() | ||
| 162 | if status == StreamPeerTLS.STATUS_CONNECTED: | ||
| 163 | p.ws = _create_peer() | ||
| 164 | p.ws.accept_stream(p.connection) | ||
| 165 | return false # WebSocketPeer connection is pending. | ||
| 166 | if status != StreamPeerTLS.STATUS_HANDSHAKING: | ||
| 167 | return true # Failure. | ||
| 168 | |||
| 169 | return false | ||
| 170 | |||
| 171 | |||
| 172 | func _process(_delta: float) -> void: | ||
| 173 | poll() | ||
| diff --git a/client/Archipelago/victoryListener.gd b/apworld/client/victoryListener.gd index e9089d7..e9089d7 100644 --- a/client/Archipelago/victoryListener.gd +++ b/apworld/client/victoryListener.gd | |||
| diff --git a/apworld/client/visibilityListener.gd b/apworld/client/visibilityListener.gd new file mode 100644 index 0000000..5ea17a0 --- /dev/null +++ b/apworld/client/visibilityListener.gd | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | extends "res://scripts/nodes/listeners/visibilityListener.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/apworld/client/worldport.gd b/apworld/client/worldport.gd new file mode 100644 index 0000000..ed9891e --- /dev/null +++ b/apworld/client/worldport.gd | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | extends "res://scripts/nodes/worldport.gd" | ||
| 2 | |||
| 3 | var absolute_rotation = false | ||
| 4 | var target_rotation = 0 | ||
| 5 | |||
| 6 | var port_id = null | ||
| 7 | |||
| 8 | |||
| 9 | func _ready(): | ||
| 10 | var node_path = String( | ||
| 11 | get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names() | ||
| 12 | ) | ||
| 13 | |||
| 14 | var ap = global.get_node("Archipelago") | ||
| 15 | |||
| 16 | if ap.shuffle_worldports: | ||
| 17 | var gamedata = global.get_node("Gamedata") | ||
| 18 | port_id = gamedata.get_port_for_map_node_path(global.map, node_path) | ||
| 19 | if port_id != null: | ||
| 20 | if port_id in ap.port_pairings: | ||
| 21 | var target_port = gamedata.objects.get_ports()[ap.port_pairings[port_id]] | ||
| 22 | var target_room = gamedata.objects.get_rooms()[target_port.get_room_id()] | ||
| 23 | var target_map = gamedata.objects.get_maps()[target_room.get_map_id()] | ||
| 24 | |||
| 25 | exit = target_map.get_name() | ||
| 26 | entry_point.x = target_port.get_destination().get_x() | ||
| 27 | entry_point.y = target_port.get_destination().get_y() | ||
| 28 | entry_point.z = target_port.get_destination().get_z() | ||
| 29 | absolute_rotation = true | ||
| 30 | target_rotation = target_port.get_rotation() | ||
| 31 | sets_entry_point = true | ||
| 32 | invisible = false | ||
| 33 | fades = true | ||
| 34 | else: | ||
| 35 | port_id = null | ||
| 36 | |||
| 37 | if global.map == "icarus" and exit == "daedalus": | ||
| 38 | if not ap.daedalus_roof_access: | ||
| 39 | entry_point = Vector3(58, 10, 0) | ||
| 40 | |||
| 41 | super._ready() | ||
| 42 | |||
| 43 | |||
| 44 | func bodyEntered(body): | ||
| 45 | if body.is_in_group("player"): | ||
| 46 | if port_id != null: | ||
| 47 | var ap = global.get_node("Archipelago") | ||
| 48 | ap.client.checkWorldport(port_id) | ||
| 49 | |||
| 50 | if absolute_rotation: | ||
| 51 | entry_rotate.y = target_rotation - body.rotation_degrees.y | ||
| 52 | |||
| 53 | super.bodyEntered(body) | ||
| 54 | |||
| 55 | |||
| 56 | func changeScene(): | ||
| 57 | var player = get_tree().get_root().get_node("scene/player") | ||
| 58 | if player != null: | ||
| 59 | player.playable = false | ||
| 60 | |||
| 61 | super.changeScene() | ||
| diff --git a/apworld/client/worldportListener.gd b/apworld/client/worldportListener.gd new file mode 100644 index 0000000..4cff8e9 --- /dev/null +++ b/apworld/client/worldportListener.gd | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | extends "res://scripts/nodes/listeners/worldportListener.gd" | ||
| 2 | |||
| 3 | |||
| 4 | func handleTriggered(): | ||
| 5 | if exit.begins_with("menus/credits"): | ||
| 6 | return | ||
| 7 | |||
| 8 | super.handleTriggered() | ||
| diff --git a/apworld/context.py b/apworld/context.py new file mode 100644 index 0000000..86392f9 --- /dev/null +++ b/apworld/context.py | |||
| @@ -0,0 +1,800 @@ | |||
| 1 | import asyncio | ||
| 2 | import os | ||
| 3 | import pkgutil | ||
| 4 | import subprocess | ||
| 5 | import sys | ||
| 6 | from typing import Any | ||
| 7 | |||
| 8 | import websockets | ||
| 9 | |||
| 10 | import Utils | ||
| 11 | import settings | ||
| 12 | from BaseClasses import ItemClassification | ||
| 13 | from CommonClient import CommonContext, server_loop, gui_enabled, logger, get_base_parser, handle_url_arg | ||
| 14 | from NetUtils import Endpoint, decode, encode, ClientStatus | ||
| 15 | from Utils import async_start | ||
| 16 | from . import Lingo2World | ||
| 17 | from .tracker import Tracker | ||
| 18 | |||
| 19 | ALL_LETTERS = "abcdefghijklmnopqrstuvwxyz" | ||
| 20 | MESSAGE_MAX_SIZE = 16*1024*1024 | ||
| 21 | PORT = 43182 | ||
| 22 | |||
| 23 | KEY_STORAGE_MAPPING = { | ||
| 24 | "a": (1, 0), "b": (1, 1), "c": (1, 2), "d": (1, 3), "e": (1, 4), "f": (1, 5), "g": (1, 6), "h": (1, 7), "i": (1, 8), | ||
| 25 | "j": (1, 9), "k": (1, 10), "l": (1, 11), "m": (1, 12), "n": (2, 0), "o": (2, 1), "p": (2, 2), "q": (2, 3), | ||
| 26 | "r": (2, 4), "s": (2, 5), "t": (2, 6), "u": (2, 7), "v": (2, 8), "w": (2, 9), "x": (2, 10), "y": (2, 11), | ||
| 27 | "z": (2, 12), | ||
| 28 | } | ||
| 29 | |||
| 30 | REVERSE_KEY_STORAGE_MAPPING = {t: k for k, t in KEY_STORAGE_MAPPING.items()} | ||
| 31 | |||
| 32 | |||
| 33 | # There is a distinction between an object's ID and its AP ID. The latter is stable between releases, whereas the former | ||
| 34 | # can change and is also namespaced based on the object type. We should only store AP IDs in multiworld state (such as | ||
| 35 | # slot data and data storage) to increase compatability between releases. The data we currently store is: | ||
| 36 | # - Port pairings for worldport shuffle (slot data) | ||
| 37 | # - Checked worldports for worldport shuffle (data storage) | ||
| 38 | # - Latched doors (data storage) | ||
| 39 | # The client generally deals in the actual object IDs rather than the stable IDs, although it does have to convert the | ||
| 40 | # port pairing IDs when reading them from slot data. The context (this file here) does the work of converting back and | ||
| 41 | # forth between the values. AP IDs are converted to IDs after reading them from data storage, and IDs are converted to | ||
| 42 | # AP IDs before sending them to data storage. | ||
| 43 | class Lingo2Manager: | ||
| 44 | game_ctx: "Lingo2GameContext" | ||
| 45 | client_ctx: "Lingo2ClientContext" | ||
| 46 | tracker: Tracker | ||
| 47 | |||
| 48 | keyboard: dict[str, int] | ||
| 49 | worldports: set[int] | ||
| 50 | goaled: bool | ||
| 51 | latches: set[int] | ||
| 52 | hinted_locations: set[int] | ||
| 53 | |||
| 54 | def __init__(self, game_ctx: "Lingo2GameContext", client_ctx: "Lingo2ClientContext"): | ||
| 55 | self.game_ctx = game_ctx | ||
| 56 | self.game_ctx.manager = self | ||
| 57 | self.client_ctx = client_ctx | ||
| 58 | self.client_ctx.manager = self | ||
| 59 | self.tracker = Tracker(self) | ||
| 60 | self.keyboard = {} | ||
| 61 | |||
| 62 | self.reset() | ||
| 63 | |||
| 64 | def reset(self): | ||
| 65 | for k in ALL_LETTERS: | ||
| 66 | self.keyboard[k] = 0 | ||
| 67 | |||
| 68 | self.worldports = set() | ||
| 69 | self.goaled = False | ||
| 70 | self.latches = set() | ||
| 71 | self.hinted_locations = set() | ||
| 72 | |||
| 73 | def update_keyboard(self, new_keyboard: dict[str, int]) -> dict[str, int]: | ||
| 74 | ret: dict[str, int] = {} | ||
| 75 | |||
| 76 | for k, v in new_keyboard.items(): | ||
| 77 | if v > self.keyboard.get(k, 0): | ||
| 78 | self.keyboard[k] = v | ||
| 79 | ret[k] = v | ||
| 80 | |||
| 81 | if len(ret) > 0: | ||
| 82 | self.tracker.refresh_state() | ||
| 83 | self.game_ctx.send_accessible_locations() | ||
| 84 | |||
| 85 | return ret | ||
| 86 | |||
| 87 | # Input should be real IDs, not AP IDs | ||
| 88 | def update_worldports(self, new_worldports: set[int]) -> set[int]: | ||
| 89 | ret = new_worldports.difference(self.worldports) | ||
| 90 | self.worldports.update(new_worldports) | ||
| 91 | |||
| 92 | if len(ret) > 0: | ||
| 93 | self.tracker.refresh_state() | ||
| 94 | self.game_ctx.send_accessible_locations() | ||
| 95 | |||
| 96 | return ret | ||
| 97 | |||
| 98 | def update_latches(self, new_latches: set[int]) -> set[int]: | ||
| 99 | ret = new_latches.difference(self.latches) | ||
| 100 | self.latches.update(new_latches) | ||
| 101 | |||
| 102 | return ret | ||
| 103 | |||
| 104 | def update_hinted_locations(self, new_locs: set[int]) -> set[int]: | ||
| 105 | ret = new_locs.difference(self.hinted_locations) | ||
| 106 | self.hinted_locations.update(new_locs) | ||
| 107 | |||
| 108 | return ret | ||
| 109 | |||
| 110 | |||
| 111 | class Lingo2GameContext: | ||
| 112 | server: Endpoint | None | ||
| 113 | manager: Lingo2Manager | ||
| 114 | |||
| 115 | def __init__(self): | ||
| 116 | self.server = None | ||
| 117 | |||
| 118 | def send_connected(self): | ||
| 119 | if self.server is None: | ||
| 120 | return | ||
| 121 | |||
| 122 | msg = { | ||
| 123 | "cmd": "Connected", | ||
| 124 | "user": self.manager.client_ctx.username, | ||
| 125 | "seed_name": self.manager.client_ctx.seed_name, | ||
| 126 | "version": self.manager.client_ctx.server_version, | ||
| 127 | "generator_version": self.manager.client_ctx.generator_version, | ||
| 128 | "team": self.manager.client_ctx.team, | ||
| 129 | "slot": self.manager.client_ctx.slot, | ||
| 130 | "checked_locations": self.manager.client_ctx.checked_locations, | ||
| 131 | "slot_data": self.manager.client_ctx.slot_data, | ||
| 132 | } | ||
| 133 | |||
| 134 | async_start(self.send_msgs([msg]), name="game Connected") | ||
| 135 | |||
| 136 | def send_connection_refused(self, text): | ||
| 137 | if self.server is None: | ||
| 138 | return | ||
| 139 | |||
| 140 | msg = { | ||
| 141 | "cmd": "ConnectionRefused", | ||
| 142 | "text": text, | ||
| 143 | } | ||
| 144 | |||
| 145 | async_start(self.send_msgs([msg]), name="game ConnectionRefused") | ||
| 146 | |||
| 147 | def send_item_sent_notification(self, item_name, receiver_name, item_flags): | ||
| 148 | if self.server is None: | ||
| 149 | return | ||
| 150 | |||
| 151 | msg = { | ||
| 152 | "cmd": "ItemSentNotif", | ||
| 153 | "item_name": item_name, | ||
| 154 | "receiver_name": receiver_name, | ||
| 155 | "item_flags": item_flags, | ||
| 156 | } | ||
| 157 | |||
| 158 | async_start(self.send_msgs([msg]), name="item sent notif") | ||
| 159 | |||
| 160 | def send_hint_received(self, item_name, location_name, receiver_name, item_flags, for_self): | ||
| 161 | if self.server is None: | ||
| 162 | return | ||
| 163 | |||
| 164 | msg = { | ||
| 165 | "cmd": "HintReceived", | ||
| 166 | "item_name": item_name, | ||
| 167 | "location_name": location_name, | ||
| 168 | "receiver_name": receiver_name, | ||
| 169 | "item_flags": item_flags, | ||
| 170 | "self": int(for_self), | ||
| 171 | } | ||
| 172 | |||
| 173 | async_start(self.send_msgs([msg]), name="hint received notif") | ||
| 174 | |||
| 175 | def send_item_received(self, items): | ||
| 176 | if self.server is None: | ||
| 177 | return | ||
| 178 | |||
| 179 | msg = { | ||
| 180 | "cmd": "ItemReceived", | ||
| 181 | "items": items, | ||
| 182 | } | ||
| 183 | |||
| 184 | async_start(self.send_msgs([msg]), name="item received") | ||
| 185 | |||
| 186 | def send_location_info(self, locations): | ||
| 187 | if self.server is None: | ||
| 188 | return | ||
| 189 | |||
| 190 | msg = { | ||
| 191 | "cmd": "LocationInfo", | ||
| 192 | "locations": locations, | ||
| 193 | } | ||
| 194 | |||
| 195 | async_start(self.send_msgs([msg]), name="location info") | ||
| 196 | |||
| 197 | def send_text_message(self, parts): | ||
| 198 | if self.server is None: | ||
| 199 | return | ||
| 200 | |||
| 201 | msg = { | ||
| 202 | "cmd": "TextMessage", | ||
| 203 | "data": parts, | ||
| 204 | } | ||
| 205 | |||
| 206 | async_start(self.send_msgs([msg]), name="notif") | ||
| 207 | |||
| 208 | def send_accessible_locations(self): | ||
| 209 | if self.server is None: | ||
| 210 | return | ||
| 211 | |||
| 212 | msg = { | ||
| 213 | "cmd": "AccessibleLocations", | ||
| 214 | "locations": list(self.manager.tracker.accessible_locations), | ||
| 215 | } | ||
| 216 | |||
| 217 | if len(self.manager.tracker.accessible_worldports) > 0: | ||
| 218 | msg["worldports"] = list(self.manager.tracker.accessible_worldports) | ||
| 219 | |||
| 220 | if self.manager.tracker.goal_accessible and not self.manager.goaled: | ||
| 221 | msg["goal"] = True | ||
| 222 | |||
| 223 | async_start(self.send_msgs([msg]), name="accessible locations") | ||
| 224 | |||
| 225 | def send_update_locations(self, locations): | ||
| 226 | if self.server is None: | ||
| 227 | return | ||
| 228 | |||
| 229 | msg = { | ||
| 230 | "cmd": "UpdateLocations", | ||
| 231 | "locations": locations, | ||
| 232 | } | ||
| 233 | |||
| 234 | async_start(self.send_msgs([msg]), name="update locations") | ||
| 235 | |||
| 236 | def send_update_keyboard(self, updates): | ||
| 237 | if self.server is None: | ||
| 238 | return | ||
| 239 | |||
| 240 | msg = { | ||
| 241 | "cmd": "UpdateKeyboard", | ||
| 242 | "updates": updates, | ||
| 243 | } | ||
| 244 | |||
| 245 | async_start(self.send_msgs([msg]), name="update keyboard") | ||
| 246 | |||
| 247 | # Input should be real IDs, not AP IDs | ||
| 248 | def send_update_worldports(self, worldports): | ||
| 249 | if self.server is None: | ||
| 250 | return | ||
| 251 | |||
| 252 | msg = { | ||
| 253 | "cmd": "UpdateWorldports", | ||
| 254 | "worldports": worldports, | ||
| 255 | } | ||
| 256 | |||
| 257 | async_start(self.send_msgs([msg]), name="update worldports") | ||
| 258 | |||
| 259 | def send_path_reply(self, object_type: str, object_id: int | None, path: list[str]): | ||
| 260 | if self.server is None: | ||
| 261 | return | ||
| 262 | |||
| 263 | msg = { | ||
| 264 | "cmd": "PathReply", | ||
| 265 | "type": object_type, | ||
| 266 | "path": path, | ||
| 267 | } | ||
| 268 | |||
| 269 | if object_id is not None: | ||
| 270 | msg["id"] = object_id | ||
| 271 | |||
| 272 | async_start(self.send_msgs([msg]), name="path reply") | ||
| 273 | |||
| 274 | def send_update_latches(self, latches): | ||
| 275 | if self.server is None: | ||
| 276 | return | ||
| 277 | |||
| 278 | msg = { | ||
| 279 | "cmd": "UpdateLatches", | ||
| 280 | "latches": latches, | ||
| 281 | } | ||
| 282 | |||
| 283 | async_start(self.send_msgs([msg]), name="update latches") | ||
| 284 | |||
| 285 | def send_ignored_locations(self, ignored_locations): | ||
| 286 | if self.server is None: | ||
| 287 | return | ||
| 288 | |||
| 289 | msg = { | ||
| 290 | "cmd": "SetIgnoredLocations", | ||
| 291 | "locations": ignored_locations, | ||
| 292 | } | ||
| 293 | |||
| 294 | async_start(self.send_msgs([msg]), name="set ignored locations") | ||
| 295 | |||
| 296 | def send_update_hinted_locations(self, hinted_locations): | ||
| 297 | if self.server is None: | ||
| 298 | return | ||
| 299 | |||
| 300 | msg = { | ||
| 301 | "cmd": "UpdateHintedLocations", | ||
| 302 | "locations": hinted_locations, | ||
| 303 | } | ||
| 304 | |||
| 305 | async_start(self.send_msgs([msg]), name="update hinted locations") | ||
| 306 | |||
| 307 | async def send_msgs(self, msgs: list[Any]) -> None: | ||
| 308 | """ `msgs` JSON serializable """ | ||
| 309 | if not self.server or not self.server.socket.open or self.server.socket.closed: | ||
| 310 | return | ||
| 311 | await self.server.socket.send(encode(msgs)) | ||
| 312 | |||
| 313 | |||
| 314 | class Lingo2ClientContext(CommonContext): | ||
| 315 | manager: Lingo2Manager | ||
| 316 | |||
| 317 | game = "Lingo 2" | ||
| 318 | items_handling = 0b111 | ||
| 319 | |||
| 320 | slot_data: dict[str, Any] | None | ||
| 321 | hints_data_storage_key: str | ||
| 322 | victory_data_storage_key: str | ||
| 323 | |||
| 324 | def __init__(self, server_address: str | None = None, password: str | None = None): | ||
| 325 | super().__init__(server_address, password) | ||
| 326 | |||
| 327 | def make_gui(self): | ||
| 328 | ui = super().make_gui() | ||
| 329 | ui.base_title = "Archipelago Lingo 2 Client" | ||
| 330 | return ui | ||
| 331 | |||
| 332 | async def server_auth(self, password_requested: bool = False): | ||
| 333 | if password_requested and not self.password: | ||
| 334 | self.manager.game_ctx.send_connection_refused("Invalid password.") | ||
| 335 | else: | ||
| 336 | self.auth = self.username | ||
| 337 | await self.send_connect() | ||
| 338 | |||
| 339 | def handle_connection_loss(self, msg: str): | ||
| 340 | super().handle_connection_loss(msg) | ||
| 341 | |||
| 342 | exc_info = sys.exc_info() | ||
| 343 | self.manager.game_ctx.send_connection_refused(str(exc_info[1])) | ||
| 344 | |||
| 345 | def on_package(self, cmd: str, args: dict): | ||
| 346 | if cmd == "RoomInfo": | ||
| 347 | self.seed_name = args.get("seed_name", None) | ||
| 348 | elif cmd == "Connected": | ||
| 349 | self.slot_data = args.get("slot_data", None) | ||
| 350 | |||
| 351 | self.manager.reset() | ||
| 352 | |||
| 353 | self.manager.game_ctx.send_connected() | ||
| 354 | |||
| 355 | self.manager.tracker.setup_slot(self.slot_data) | ||
| 356 | self.manager.tracker.set_checked_locations(self.checked_locations) | ||
| 357 | self.manager.game_ctx.send_accessible_locations() | ||
| 358 | |||
| 359 | self.hints_data_storage_key = f"_read_hints_{self.team}_{self.slot}" | ||
| 360 | self.victory_data_storage_key = f"_read_client_status_{self.team}_{self.slot}" | ||
| 361 | |||
| 362 | self.set_notify(self.get_datastorage_key("keyboard1"), self.get_datastorage_key("keyboard2"), | ||
| 363 | self.victory_data_storage_key, self.get_datastorage_key("latches"), | ||
| 364 | self.get_datastorage_key("ignored_locations")) | ||
| 365 | msg_batch = [{ | ||
| 366 | "cmd": "Set", | ||
| 367 | "key": self.get_datastorage_key("keyboard1"), | ||
| 368 | "default": 0, | ||
| 369 | "want_reply": True, | ||
| 370 | "operations": [{"operation": "default", "value": 0}] | ||
| 371 | }, { | ||
| 372 | "cmd": "Set", | ||
| 373 | "key": self.get_datastorage_key("keyboard2"), | ||
| 374 | "default": 0, | ||
| 375 | "want_reply": True, | ||
| 376 | "operations": [{"operation": "default", "value": 0}] | ||
| 377 | }, { | ||
| 378 | "cmd": "Set", | ||
| 379 | "key": self.get_datastorage_key("latches"), | ||
| 380 | "default": [], | ||
| 381 | "want_reply": True, | ||
| 382 | "operations": [{"operation": "default", "value": []}] | ||
| 383 | }, { | ||
| 384 | "cmd": "Set", | ||
| 385 | "key": self.get_datastorage_key("ignored_locations"), | ||
| 386 | "default": [], | ||
| 387 | "want_reply": True, | ||
| 388 | "operations": [{"operation": "default", "value": []}] | ||
| 389 | }] | ||
| 390 | |||
| 391 | if self.slot_data.get("shuffle_worldports", False): | ||
| 392 | self.set_notify(self.get_datastorage_key("worldports")) | ||
| 393 | msg_batch.append({ | ||
| 394 | "cmd": "Set", | ||
| 395 | "key": self.get_datastorage_key("worldports"), | ||
| 396 | "default": [], | ||
| 397 | "want_reply": True, | ||
| 398 | "operations": [{"operation": "default", "value": []}] | ||
| 399 | }) | ||
| 400 | |||
| 401 | async_start(self.send_msgs(msg_batch), name="default keys") | ||
| 402 | elif cmd == "RoomUpdate": | ||
| 403 | if "checked_locations" in args: | ||
| 404 | self.manager.tracker.set_checked_locations(self.checked_locations) | ||
| 405 | self.manager.game_ctx.send_update_locations(args["checked_locations"]) | ||
| 406 | elif cmd == "ReceivedItems": | ||
| 407 | self.manager.tracker.set_collected_items(self.items_received) | ||
| 408 | |||
| 409 | cur_index = 0 | ||
| 410 | items = [] | ||
| 411 | |||
| 412 | for item in args["items"]: | ||
| 413 | index = cur_index + args["index"] | ||
| 414 | cur_index += 1 | ||
| 415 | |||
| 416 | item_msg = { | ||
| 417 | "id": item.item, | ||
| 418 | "index": index, | ||
| 419 | "flags": item.flags, | ||
| 420 | "text": self.item_names.lookup_in_slot(item.item, self.slot), | ||
| 421 | } | ||
| 422 | |||
| 423 | if item.player != self.slot: | ||
| 424 | item_msg["sender"] = self.player_names.get(item.player) | ||
| 425 | |||
| 426 | items.append(item_msg) | ||
| 427 | |||
| 428 | self.manager.game_ctx.send_item_received(items) | ||
| 429 | |||
| 430 | if any(ItemClassification.progression in ItemClassification(item.flags) for item in args["items"]): | ||
| 431 | self.manager.game_ctx.send_accessible_locations() | ||
| 432 | elif cmd == "PrintJSON": | ||
| 433 | if "receiving" in args and "item" in args and args["item"].player == self.slot: | ||
| 434 | item_name = self.item_names.lookup_in_slot(args["item"].item, args["receiving"]) | ||
| 435 | location_name = self.location_names.lookup_in_slot(args["item"].location, args["item"].player) | ||
| 436 | receiver_name = self.player_names.get(args["receiving"]) | ||
| 437 | |||
| 438 | if args["type"] == "Hint" and not args.get("found", False): | ||
| 439 | self.manager.game_ctx.send_hint_received(item_name, location_name, receiver_name, args["item"].flags, | ||
| 440 | int(args["receiving"]) == self.slot) | ||
| 441 | elif args["receiving"] != self.slot: | ||
| 442 | self.manager.game_ctx.send_item_sent_notification(item_name, receiver_name, args["item"].flags) | ||
| 443 | |||
| 444 | parts = [] | ||
| 445 | for message_part in args["data"]: | ||
| 446 | if "type" not in message_part and "text" in message_part: | ||
| 447 | parts.append({"type": "text", "text": message_part["text"]}) | ||
| 448 | elif message_part["type"] == "player_id": | ||
| 449 | parts.append({ | ||
| 450 | "type": "player", | ||
| 451 | "text": self.player_names.get(int(message_part["text"])), | ||
| 452 | "self": int(int(message_part["text"]) == self.slot), | ||
| 453 | }) | ||
| 454 | elif message_part["type"] == "item_id": | ||
| 455 | parts.append({ | ||
| 456 | "type": "item", | ||
| 457 | "text": self.item_names.lookup_in_slot(int(message_part["text"]), message_part["player"]), | ||
| 458 | "flags": message_part["flags"], | ||
| 459 | }) | ||
| 460 | elif message_part["type"] == "location_id": | ||
| 461 | parts.append({ | ||
| 462 | "type": "location", | ||
| 463 | "text": self.location_names.lookup_in_slot(int(message_part["text"]), | ||
| 464 | message_part["player"]) | ||
| 465 | }) | ||
| 466 | elif "text" in message_part: | ||
| 467 | parts.append({"type": "text", "text": message_part["text"]}) | ||
| 468 | |||
| 469 | self.manager.game_ctx.send_text_message(parts) | ||
| 470 | elif cmd == "LocationInfo": | ||
| 471 | locations = [] | ||
| 472 | |||
| 473 | for location in args["locations"]: | ||
| 474 | locations.append({ | ||
| 475 | "id": location.location, | ||
| 476 | "item": self.item_names.lookup_in_slot(location.item, location.player), | ||
| 477 | "player": self.player_names.get(location.player), | ||
| 478 | "flags": location.flags, | ||
| 479 | "self": int(location.player) == self.slot, | ||
| 480 | }) | ||
| 481 | |||
| 482 | self.manager.game_ctx.send_location_info(locations) | ||
| 483 | elif cmd == "Retrieved": | ||
| 484 | for k, v in args["keys"].items(): | ||
| 485 | if k == self.victory_data_storage_key: | ||
| 486 | self.handle_status_update(v) | ||
| 487 | elif k == self.hints_data_storage_key: | ||
| 488 | self.update_hints() | ||
| 489 | elif cmd == "SetReply": | ||
| 490 | if args["key"] == self.get_datastorage_key("keyboard1"): | ||
| 491 | self.handle_keyboard_update(1, args) | ||
| 492 | elif args["key"] == self.get_datastorage_key("keyboard2"): | ||
| 493 | self.handle_keyboard_update(2, args) | ||
| 494 | elif args["key"] == self.get_datastorage_key("worldports"): | ||
| 495 | port_ids = set(Lingo2World.static_logic.port_id_by_ap_id[ap_id] for ap_id in args["value"]) | ||
| 496 | updates = self.manager.update_worldports(port_ids) | ||
| 497 | if len(updates) > 0: | ||
| 498 | self.manager.game_ctx.send_update_worldports(updates) | ||
| 499 | elif args["key"] == self.victory_data_storage_key: | ||
| 500 | self.handle_status_update(args["value"]) | ||
| 501 | elif args["key"] == self.get_datastorage_key("latches"): | ||
| 502 | door_ids = set(Lingo2World.static_logic.door_id_by_ap_id[ap_id] for ap_id in args["value"]) | ||
| 503 | updates = self.manager.update_latches(door_ids) | ||
| 504 | if len(updates) > 0: | ||
| 505 | self.manager.game_ctx.send_update_latches(updates) | ||
| 506 | elif args["key"] == self.get_datastorage_key("ignored_locations"): | ||
| 507 | self.manager.game_ctx.send_ignored_locations(args["value"]) | ||
| 508 | elif args["key"] == self.hints_data_storage_key: | ||
| 509 | self.update_hints() | ||
| 510 | |||
| 511 | def get_datastorage_key(self, name: str): | ||
| 512 | return f"Lingo2_{self.slot}_{name}" | ||
| 513 | |||
| 514 | async def update_keyboard(self, updates: dict[str, int]): | ||
| 515 | kb1 = 0 | ||
| 516 | kb2 = 0 | ||
| 517 | |||
| 518 | for k, v in updates.items(): | ||
| 519 | if v == 0: | ||
| 520 | continue | ||
| 521 | |||
| 522 | effect = 0 | ||
| 523 | if v >= 1: | ||
| 524 | effect |= 1 | ||
| 525 | if v == 2: | ||
| 526 | effect |= 2 | ||
| 527 | |||
| 528 | pos = KEY_STORAGE_MAPPING[k] | ||
| 529 | if pos[0] == 1: | ||
| 530 | kb1 |= (effect << pos[1] * 2) | ||
| 531 | else: | ||
| 532 | kb2 |= (effect << pos[1] * 2) | ||
| 533 | |||
| 534 | msgs = [] | ||
| 535 | |||
| 536 | if kb1 != 0: | ||
| 537 | msgs.append({ | ||
| 538 | "cmd": "Set", | ||
| 539 | "key": self.get_datastorage_key("keyboard1"), | ||
| 540 | "want_reply": True, | ||
| 541 | "operations": [{ | ||
| 542 | "operation": "or", | ||
| 543 | "value": kb1 | ||
| 544 | }] | ||
| 545 | }) | ||
| 546 | |||
| 547 | if kb2 != 0: | ||
| 548 | msgs.append({ | ||
| 549 | "cmd": "Set", | ||
| 550 | "key": self.get_datastorage_key("keyboard2"), | ||
| 551 | "want_reply": True, | ||
| 552 | "operations": [{ | ||
| 553 | "operation": "or", | ||
| 554 | "value": kb2 | ||
| 555 | }] | ||
| 556 | }) | ||
| 557 | |||
| 558 | if len(msgs) > 0: | ||
| 559 | await self.send_msgs(msgs) | ||
| 560 | |||
| 561 | def handle_keyboard_update(self, field: int, args: dict[str, Any]): | ||
| 562 | keys = {} | ||
| 563 | value = args["value"] | ||
| 564 | |||
| 565 | for i in range(0, 13): | ||
| 566 | if (value & (1 << (i * 2))) != 0: | ||
| 567 | keys[REVERSE_KEY_STORAGE_MAPPING[(field, i)]] = 1 | ||
| 568 | if (value & (1 << (i * 2 + 1))) != 0: | ||
| 569 | keys[REVERSE_KEY_STORAGE_MAPPING[(field, i)]] = 2 | ||
| 570 | |||
| 571 | updates = self.manager.update_keyboard(keys) | ||
| 572 | if len(updates) > 0: | ||
| 573 | self.manager.game_ctx.send_update_keyboard(updates) | ||
| 574 | |||
| 575 | # Input should be real IDs, not AP IDs | ||
| 576 | async def update_worldports(self, updates: set[int]): | ||
| 577 | port_ap_ids = [Lingo2World.static_logic.objects.ports[port_id].ap_id for port_id in updates] | ||
| 578 | await self.send_msgs([{ | ||
| 579 | "cmd": "Set", | ||
| 580 | "key": self.get_datastorage_key("worldports"), | ||
| 581 | "want_reply": True, | ||
| 582 | "operations": [{ | ||
| 583 | "operation": "update", | ||
| 584 | "value": port_ap_ids | ||
| 585 | }] | ||
| 586 | }]) | ||
| 587 | |||
| 588 | def handle_status_update(self, value: int): | ||
| 589 | self.manager.goaled = (value == ClientStatus.CLIENT_GOAL) | ||
| 590 | self.manager.tracker.refresh_state() | ||
| 591 | self.manager.game_ctx.send_accessible_locations() | ||
| 592 | |||
| 593 | async def update_latches(self, updates: set[int]): | ||
| 594 | door_ap_ids = [Lingo2World.static_logic.objects.doors[door_id].ap_id for door_id in updates] | ||
| 595 | await self.send_msgs([{ | ||
| 596 | "cmd": "Set", | ||
| 597 | "key": self.get_datastorage_key("latches"), | ||
| 598 | "want_reply": True, | ||
| 599 | "operations": [{ | ||
| 600 | "operation": "update", | ||
| 601 | "value": door_ap_ids | ||
| 602 | }] | ||
| 603 | }]) | ||
| 604 | |||
| 605 | async def add_ignored_location(self, loc_id: int): | ||
| 606 | await self.send_msgs([{ | ||
| 607 | "cmd": "Set", | ||
| 608 | "key": self.get_datastorage_key("ignored_locations"), | ||
| 609 | "want_reply": True, | ||
| 610 | "operations": [{ | ||
| 611 | "operation": "update", | ||
| 612 | "value": [loc_id] | ||
| 613 | }] | ||
| 614 | }]) | ||
| 615 | |||
| 616 | async def remove_ignored_location(self, loc_id: int): | ||
| 617 | await self.send_msgs([{ | ||
| 618 | "cmd": "Set", | ||
| 619 | "key": self.get_datastorage_key("ignored_locations"), | ||
| 620 | "want_reply": True, | ||
| 621 | "operations": [{ | ||
| 622 | "operation": "remove", | ||
| 623 | "value": loc_id | ||
| 624 | }] | ||
| 625 | }]) | ||
| 626 | |||
| 627 | def update_hints(self): | ||
| 628 | hints = self.stored_data.get(self.hints_data_storage_key, []) | ||
| 629 | |||
| 630 | hinted_locations = set(hint["location"] for hint in hints if hint["finding_player"] == self.slot) | ||
| 631 | updates = self.manager.update_hinted_locations(hinted_locations) | ||
| 632 | if len(updates) > 0: | ||
| 633 | self.manager.game_ctx.send_update_hinted_locations(updates) | ||
| 634 | |||
| 635 | |||
| 636 | async def pipe_loop(manager: Lingo2Manager): | ||
| 637 | while not manager.client_ctx.exit_event.is_set(): | ||
| 638 | try: | ||
| 639 | socket = await websockets.connect("ws://localhost", port=PORT, ping_timeout=None, ping_interval=None, | ||
| 640 | max_size=MESSAGE_MAX_SIZE) | ||
| 641 | manager.game_ctx.server = Endpoint(socket) | ||
| 642 | logger.info("Connected to Lingo 2!") | ||
| 643 | if manager.client_ctx.auth is not None: | ||
| 644 | manager.game_ctx.send_connected() | ||
| 645 | manager.game_ctx.send_accessible_locations() | ||
| 646 | async for data in manager.game_ctx.server.socket: | ||
| 647 | for msg in decode(data): | ||
| 648 | await process_game_cmd(manager, msg) | ||
| 649 | except ConnectionRefusedError: | ||
| 650 | logger.info("Could not connect to Lingo 2.") | ||
| 651 | finally: | ||
| 652 | manager.game_ctx.server = None | ||
| 653 | |||
| 654 | |||
| 655 | async def process_game_cmd(manager: Lingo2Manager, args: dict): | ||
| 656 | cmd = args["cmd"] | ||
| 657 | |||
| 658 | if cmd == "Connect": | ||
| 659 | manager.client_ctx.seed_name = None | ||
| 660 | |||
| 661 | server = args.get("server") | ||
| 662 | player = args.get("player") | ||
| 663 | password = args.get("password") | ||
| 664 | |||
| 665 | if password != "": | ||
| 666 | server_address = f"{player}:{password}@{server}" | ||
| 667 | else: | ||
| 668 | server_address = f"{player}:None@{server}" | ||
| 669 | |||
| 670 | async_start(manager.client_ctx.connect(server_address), name="client connect") | ||
| 671 | elif cmd == "Disconnect": | ||
| 672 | manager.client_ctx.seed_name = None | ||
| 673 | |||
| 674 | async_start(manager.client_ctx.disconnect(), name="client disconnect") | ||
| 675 | elif cmd in ["Sync", "LocationChecks", "Say", "StatusUpdate", "LocationScouts"]: | ||
| 676 | async_start(manager.client_ctx.send_msgs([args]), name="client forward") | ||
| 677 | elif cmd == "UpdateKeyboard": | ||
| 678 | updates = manager.update_keyboard(args["keyboard"]) | ||
| 679 | if len(updates) > 0: | ||
| 680 | async_start(manager.client_ctx.update_keyboard(updates), name="client update keyboard") | ||
| 681 | elif cmd == "CheckWorldport": | ||
| 682 | port_id = args["port_id"] | ||
| 683 | port_ap_id = Lingo2World.static_logic.objects.ports[port_id].ap_id | ||
| 684 | worldports = {port_id} | ||
| 685 | |||
| 686 | # Also check the reverse port if it's a two-way connection. | ||
| 687 | port_pairings = manager.client_ctx.slot_data["port_pairings"] | ||
| 688 | if str(port_ap_id) in port_pairings and\ | ||
| 689 | port_pairings.get(str(port_pairings[str(port_ap_id)]), None) == port_ap_id: | ||
| 690 | worldports.add(Lingo2World.static_logic.port_id_by_ap_id[port_pairings[str(port_ap_id)]]) | ||
| 691 | |||
| 692 | updates = manager.update_worldports(worldports) | ||
| 693 | if len(updates) > 0: | ||
| 694 | async_start(manager.client_ctx.update_worldports(updates), name="client update worldports") | ||
| 695 | manager.game_ctx.send_update_worldports(updates) | ||
| 696 | elif cmd == "GetPath": | ||
| 697 | path = None | ||
| 698 | |||
| 699 | if args["type"] == "location": | ||
| 700 | path = manager.tracker.get_path_to_location(args["id"]) | ||
| 701 | elif args["type"] == "worldport": | ||
| 702 | path = manager.tracker.get_path_to_port(args["id"]) | ||
| 703 | elif args["type"] == "goal": | ||
| 704 | path = manager.tracker.get_path_to_goal() | ||
| 705 | |||
| 706 | manager.game_ctx.send_path_reply(args["type"], args.get("id", None), path) | ||
| 707 | elif cmd == "LatchDoor": | ||
| 708 | updates = manager.update_latches({args["door"]}) | ||
| 709 | if len(updates) > 0: | ||
| 710 | async_start(manager.client_ctx.update_latches(updates), name="client update latches") | ||
| 711 | elif cmd == "IgnoreLocation": | ||
| 712 | async_start(manager.client_ctx.add_ignored_location(args["id"]), name="client ignore loc") | ||
| 713 | elif cmd == "UnignoreLocation": | ||
| 714 | async_start(manager.client_ctx.remove_ignored_location(args["id"]), name="client unignore loc") | ||
| 715 | elif cmd == "Quit": | ||
| 716 | manager.client_ctx.exit_event.set() | ||
| 717 | |||
| 718 | |||
| 719 | async def run_game(): | ||
| 720 | exe_file = settings.get_settings().lingo2_options.exe_file | ||
| 721 | |||
| 722 | # This ensures we can use Steam features without having to open the game | ||
| 723 | # through steam. | ||
| 724 | steam_appid_path = os.path.join(os.path.dirname(exe_file), "steam_appid.txt") | ||
| 725 | with open(steam_appid_path, "w") as said_handle: | ||
| 726 | said_handle.write("2523310") | ||
| 727 | |||
| 728 | if Lingo2World.zip_path is not None: | ||
| 729 | # This is a packaged apworld. | ||
| 730 | init_scene = pkgutil.get_data(__name__, "client/run_from_apworld.tscn") | ||
| 731 | init_path = Utils.local_path("data", "lingo2_init.tscn") | ||
| 732 | |||
| 733 | with open(init_path, "wb") as file_handle: | ||
| 734 | file_handle.write(init_scene) | ||
| 735 | |||
| 736 | subprocess.Popen( | ||
| 737 | [ | ||
| 738 | exe_file, | ||
| 739 | "--scene", | ||
| 740 | init_path, | ||
| 741 | "--", | ||
| 742 | str(Lingo2World.zip_path.absolute()), | ||
| 743 | ], | ||
| 744 | cwd=os.path.dirname(exe_file), | ||
| 745 | ) | ||
| 746 | else: | ||
| 747 | # The world is unzipped and being run in source. | ||
| 748 | subprocess.Popen( | ||
| 749 | [ | ||
| 750 | exe_file, | ||
| 751 | "--scene", | ||
| 752 | Utils.local_path("worlds", "lingo2", "client", "run_from_source.tscn"), | ||
| 753 | "--", | ||
| 754 | Utils.local_path("worlds", "lingo2", "client"), | ||
| 755 | ], | ||
| 756 | cwd=os.path.dirname(exe_file), | ||
| 757 | ) | ||
| 758 | |||
| 759 | |||
| 760 | def client_main(*launch_args: str) -> None: | ||
| 761 | async def main(args): | ||
| 762 | if settings.get_settings().lingo2_options.start_game: | ||
| 763 | async_start(run_game()) | ||
| 764 | |||
| 765 | client_ctx = Lingo2ClientContext(args.connect, args.password) | ||
| 766 | client_ctx.auth = args.name | ||
| 767 | |||
| 768 | game_ctx = Lingo2GameContext() | ||
| 769 | manager = Lingo2Manager(game_ctx, client_ctx) | ||
| 770 | |||
| 771 | client_ctx.server_task = asyncio.create_task(server_loop(client_ctx), name="ServerLoop") | ||
| 772 | |||
| 773 | if gui_enabled: | ||
| 774 | client_ctx.run_gui() | ||
| 775 | client_ctx.run_cli() | ||
| 776 | |||
| 777 | pipe_task = asyncio.create_task(pipe_loop(manager), name="GameWatcher") | ||
| 778 | |||
| 779 | try: | ||
| 780 | await pipe_task | ||
| 781 | except Exception as e: | ||
| 782 | logger.exception(e) | ||
| 783 | |||
| 784 | await client_ctx.exit_event.wait() | ||
| 785 | client_ctx.ui.stop() | ||
| 786 | await client_ctx.shutdown() | ||
| 787 | |||
| 788 | Utils.init_logging("Lingo2Client", exception_logger="Client") | ||
| 789 | import colorama | ||
| 790 | |||
| 791 | parser = get_base_parser(description="Lingo 2 Archipelago Client") | ||
| 792 | parser.add_argument('--name', default=None, help="Slot Name to connect as.") | ||
| 793 | parser.add_argument("url", nargs="?", help="Archipelago connection url") | ||
| 794 | args = parser.parse_args(launch_args) | ||
| 795 | |||
| 796 | args = handle_url_arg(args, parser=parser) | ||
| 797 | |||
| 798 | colorama.just_fix_windows_console() | ||
| 799 | asyncio.run(main(args)) | ||
| 800 | colorama.deinit() | ||
| diff --git a/apworld/docs/en_Lingo_2.md b/apworld/docs/en_Lingo_2.md new file mode 100644 index 0000000..977795a --- /dev/null +++ b/apworld/docs/en_Lingo_2.md | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | # Lingo 2 | ||
| 2 | |||
| 3 | See [the project README](https://code.fourisland.com/lingo2-archipelago/about/) | ||
| 4 | for installation instructions and frequently asked questions. \ No newline at end of file | ||
| diff --git a/apworld/items.py b/apworld/items.py index 971a709..28158c3 100644 --- a/apworld/items.py +++ b/apworld/items.py | |||
| @@ -1,5 +1,31 @@ | |||
| 1 | from .generated import data_pb2 as data_pb2 | ||
| 1 | from BaseClasses import Item | 2 | from BaseClasses import Item |
| 2 | 3 | ||
| 3 | 4 | ||
| 4 | class Lingo2Item(Item): | 5 | class Lingo2Item(Item): |
| 5 | game: str = "Lingo 2" | 6 | game: str = "Lingo 2" |
| 7 | |||
| 8 | |||
| 9 | SYMBOL_ITEMS: dict[data_pb2.PuzzleSymbol, str] = { | ||
| 10 | data_pb2.PuzzleSymbol.SUN: "Sun Symbol", | ||
| 11 | data_pb2.PuzzleSymbol.SPARKLES: "Sparkles Symbol", | ||
| 12 | data_pb2.PuzzleSymbol.ZERO: "Zero Symbol", | ||
| 13 | data_pb2.PuzzleSymbol.EXAMPLE: "Example Symbol", | ||
| 14 | data_pb2.PuzzleSymbol.BOXES: "Boxes Symbol", | ||
| 15 | data_pb2.PuzzleSymbol.PLANET: "Planet Symbol", | ||
| 16 | data_pb2.PuzzleSymbol.PYRAMID: "Pyramid Symbol", | ||
| 17 | data_pb2.PuzzleSymbol.CROSS: "Cross Symbol", | ||
| 18 | data_pb2.PuzzleSymbol.SWEET: "Sweet Symbol", | ||
| 19 | data_pb2.PuzzleSymbol.GENDER: "Gender Symbol", | ||
| 20 | data_pb2.PuzzleSymbol.AGE: "Age Symbol", | ||
| 21 | data_pb2.PuzzleSymbol.SOUND: "Sound Symbol", | ||
| 22 | data_pb2.PuzzleSymbol.ANAGRAM: "Anagram Symbol", | ||
| 23 | data_pb2.PuzzleSymbol.JOB: "Job Symbol", | ||
| 24 | data_pb2.PuzzleSymbol.STARS: "Stars Symbol", | ||
| 25 | data_pb2.PuzzleSymbol.NULL: "Null Symbol", | ||
| 26 | data_pb2.PuzzleSymbol.EVAL: "Eval Symbol", | ||
| 27 | data_pb2.PuzzleSymbol.LINGO: "Lingo Symbol", | ||
| 28 | data_pb2.PuzzleSymbol.QUESTION: "Question Symbol", | ||
| 29 | } | ||
| 30 | |||
| 31 | ANTI_COLLECTABLE_TRAPS: list[str] = [f"Anti {letter}" for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"] | ||
| diff --git a/apworld/locations.py b/apworld/locations.py index 108decb..3d619dc 100644 --- a/apworld/locations.py +++ b/apworld/locations.py | |||
| @@ -3,3 +3,6 @@ from BaseClasses import Location | |||
| 3 | 3 | ||
| 4 | class Lingo2Location(Location): | 4 | class Lingo2Location(Location): |
| 5 | game: str = "Lingo 2" | 5 | game: str = "Lingo 2" |
| 6 | |||
| 7 | port_id: int | ||
| 8 | goal: bool | ||
| diff --git a/apworld/logo.png b/apworld/logo.png new file mode 100644 index 0000000..b9d00ba --- /dev/null +++ b/apworld/logo.png | |||
| Binary files differ | |||
| diff --git a/apworld/options.py b/apworld/options.py index 2197b0f..f687434 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, OptionSet |
| 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 |
| @@ -39,6 +44,23 @@ class ShuffleLetters(Choice): | |||
| 39 | option_item_cyan = 4 | 44 | option_item_cyan = 4 |
| 40 | 45 | ||
| 41 | 46 | ||
| 47 | class ShuffleSymbols(Toggle): | ||
| 48 | """ | ||
| 49 | If enabled, 19 items will be added to the pool, representing the different symbols that can appear on a panel. | ||
| 50 | Players will be prevented from solving puzzles with symbols on them until all of the required symbols are unlocked. | ||
| 51 | """ | ||
| 52 | display_name = "Shuffle Symbols" | ||
| 53 | |||
| 54 | |||
| 55 | class ShuffleWorldports(Toggle): | ||
| 56 | """ | ||
| 57 | Randomizes the connections between maps. This affects worldports only, which are the loading zones you walk into in | ||
| 58 | order to change maps. This does not affect paintings, panels that teleport you, or certain other special connections | ||
| 59 | like the one between The Shop and Control Center. | ||
| 60 | """ | ||
| 61 | display_name = "Shuffle Worldports" | ||
| 62 | |||
| 63 | |||
| 42 | class KeyholderSanity(Toggle): | 64 | class KeyholderSanity(Toggle): |
| 43 | """ | 65 | """ |
| 44 | If enabled, 26 locations will be created for placing each key into its respective Green Ending keyholder. | 66 | If enabled, 26 locations will be created for placing each key into its respective Green Ending keyholder. |
| @@ -69,6 +91,36 @@ class CyanDoorBehavior(Choice): | |||
| 69 | option_item = 2 | 91 | option_item = 2 |
| 70 | 92 | ||
| 71 | 93 | ||
| 94 | class EnableIcarus(Toggle): | ||
| 95 | """ | ||
| 96 | Controls whether Icarus is randomized. If disabled, which is the default, no locations or items will be created for | ||
| 97 | it, and its worldport will not be shuffled when worldport shuffle is on. | ||
| 98 | """ | ||
| 99 | display_name = "Enable Icarus" | ||
| 100 | |||
| 101 | |||
| 102 | class EnableGiftMaps(OptionSet): | ||
| 103 | """ | ||
| 104 | Controls whether the beta tester gift maps are randomized. By default, these are not accessible at all from within | ||
| 105 | the randomizer. This option allows you to enter the maps, and creates items and locations for them. If worldport | ||
| 106 | shuffle is on, their worldports will be included in the randomization. | ||
| 107 | |||
| 108 | The gift maps are accessed via a panel in The Entry's Starting Room, which only appears if at least one gift map is | ||
| 109 | enabled. It is also treated like a cyan door, and will not appear until the condition specified in the Cyan Door | ||
| 110 | Behavior option is satisfied. Solving this panel with the name of one of the beta testers will teleport you to their | ||
| 111 | corresponding gift map. | ||
| 112 | |||
| 113 | In the base game, nothing happens once you complete a gift map. Masteries have been added to the gift maps in the | ||
| 114 | randomizer so that the player can be rewarded for completing them. | ||
| 115 | |||
| 116 | Note that the gift maps were originally only intended to be played by specific people, and as a result may be | ||
| 117 | frustrating or require knowledge of inside jokes. The Crystalline is particularly difficult as it requires | ||
| 118 | completing a parkour course. | ||
| 119 | """ | ||
| 120 | display_name = "Enable Gift Maps" | ||
| 121 | valid_keys = ["The Advanced", "The Charismatic", "The Crystalline", "The Fuzzy", "The Stellar"] | ||
| 122 | |||
| 123 | |||
| 72 | class DaedalusRoofAccess(Toggle): | 124 | class DaedalusRoofAccess(Toggle): |
| 73 | """ | 125 | """ |
| 74 | If enabled, the player will be logically expected to be able to go from the castle entrance to any part of Daedalus | 126 | If enabled, the player will be logically expected to be able to go from the castle entrance to any part of Daedalus |
| @@ -79,8 +131,40 @@ class DaedalusRoofAccess(Toggle): | |||
| 79 | display_name = "Allow Daedalus Roof Access" | 131 | display_name = "Allow Daedalus Roof Access" |
| 80 | 132 | ||
| 81 | 133 | ||
| 134 | class StrictPurpleEnding(DefaultOnToggle): | ||
| 135 | """ | ||
| 136 | If enabled, the player will be required to have all purple (level 1) letters in order to get Purple Ending. | ||
| 137 | Otherwise, some of the letters may be skippable depending on the other options. | ||
| 138 | """ | ||
| 139 | display_name = "Strict Purple Ending" | ||
| 140 | |||
| 141 | |||
| 142 | class StrictCyanEnding(DefaultOnToggle): | ||
| 143 | """ | ||
| 144 | If enabled, the player will be required to have all cyan (level 2) letters in order to get Cyan Ending. Otherwise, | ||
| 145 | at least J2, Q2, and V2 are skippable. Others may also be skippable depending on the options chosen. | ||
| 146 | """ | ||
| 147 | display_name = "Strict Cyan Ending" | ||
| 148 | |||
| 149 | |||
| 82 | class VictoryCondition(Choice): | 150 | class VictoryCondition(Choice): |
| 83 | """Victory condition.""" | 151 | """ |
| 152 | This option determines what your goal is. | ||
| 153 | |||
| 154 | - **Gray Ending** (The Colorful) | ||
| 155 | - **Purple Ending** (The Sun Temple). This ordinarily requires all level 1 (purple) letters. | ||
| 156 | - **Mint Ending** (typing EXIT into the keyholders in Control Center) | ||
| 157 | - **Black Ending** (The Graveyard) | ||
| 158 | - **Blue Ending** (The Words) | ||
| 159 | - **Cyan Ending** (The Parthenon). This ordinarily requires almost all level 2 (cyan) letters. | ||
| 160 | - **Red Ending** (The Tower) | ||
| 161 | - **Plum Ending** (The Wondrous / The Door) | ||
| 162 | - **Orange Ending** (the castle in Daedalus) | ||
| 163 | - **Gold Ending** (The Gold). This involves going through the color rooms in Daedalus. | ||
| 164 | - **Yellow Ending** (The Gallery). This requires unlocking all gallery paintings. | ||
| 165 | - **Green Ending** (The Ancient). This requires filling all keyholders with specific letters. | ||
| 166 | - **White Ending** (Control Center). This combines every other ending. | ||
| 167 | """ | ||
| 84 | display_name = "Victory Condition" | 168 | display_name = "Victory Condition" |
| 85 | option_gray_ending = 0 | 169 | option_gray_ending = 0 |
| 86 | option_purple_ending = 1 | 170 | option_purple_ending = 1 |
| @@ -97,12 +181,50 @@ class VictoryCondition(Choice): | |||
| 97 | option_white_ending = 12 | 181 | option_white_ending = 12 |
| 98 | 182 | ||
| 99 | 183 | ||
| 184 | class EndingsRequirement(Range): | ||
| 185 | """The number of endings required to unlock White Ending.""" | ||
| 186 | display_name = "Endings Requirement" | ||
| 187 | range_start = 0 | ||
| 188 | range_end = 12 | ||
| 189 | default = 12 | ||
| 190 | |||
| 191 | |||
| 192 | class MasteriesRequirement(Range): | ||
| 193 | """The number of masteries required to unlock White Ending. | ||
| 194 | |||
| 195 | There are only 13 masteries in the base game, but some of the other slot options may add more masteries to the | ||
| 196 | world. If the chosen number of masteries is higher than the total in your world, it will be automatically lowered to | ||
| 197 | the maximum.""" | ||
| 198 | display_name = "Masteries Requirement" | ||
| 199 | range_start = 0 | ||
| 200 | range_end = 19 | ||
| 201 | default = 0 | ||
| 202 | |||
| 203 | |||
| 204 | class TrapPercentage(Range): | ||
| 205 | """Replaces junk items with traps, at the specified rate.""" | ||
| 206 | display_name = "Trap Percentage" | ||
| 207 | range_start = 0 | ||
| 208 | range_end = 100 | ||
| 209 | default = 0 | ||
| 210 | |||
| 211 | |||
| 100 | @dataclass | 212 | @dataclass |
| 101 | class Lingo2Options(PerGameCommonOptions): | 213 | class Lingo2Options(PerGameCommonOptions): |
| 102 | shuffle_doors: ShuffleDoors | 214 | shuffle_doors: ShuffleDoors |
| 103 | shuffle_control_center_colors: ShuffleControlCenterColors | 215 | shuffle_control_center_colors: ShuffleControlCenterColors |
| 216 | shuffle_gallery_paintings: ShuffleGalleryPaintings | ||
| 104 | shuffle_letters: ShuffleLetters | 217 | shuffle_letters: ShuffleLetters |
| 218 | shuffle_symbols: ShuffleSymbols | ||
| 219 | shuffle_worldports: ShuffleWorldports | ||
| 105 | keyholder_sanity: KeyholderSanity | 220 | keyholder_sanity: KeyholderSanity |
| 106 | cyan_door_behavior: CyanDoorBehavior | 221 | cyan_door_behavior: CyanDoorBehavior |
| 222 | enable_icarus: EnableIcarus | ||
| 223 | enable_gift_maps: EnableGiftMaps | ||
| 107 | daedalus_roof_access: DaedalusRoofAccess | 224 | daedalus_roof_access: DaedalusRoofAccess |
| 225 | strict_purple_ending: StrictPurpleEnding | ||
| 226 | strict_cyan_ending: StrictCyanEnding | ||
| 108 | victory_condition: VictoryCondition | 227 | victory_condition: VictoryCondition |
| 228 | endings_requirement: EndingsRequirement | ||
| 229 | masteries_requirement: MasteriesRequirement | ||
| 230 | trap_percentage: TrapPercentage | ||
| diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 317d13b..3ee8f38 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py | |||
| @@ -1,9 +1,10 @@ | |||
| 1 | from enum import IntEnum, auto | 1 | from enum import IntEnum, auto |
| 2 | 2 | ||
| 3 | from .generated import data_pb2 as data_pb2 | 3 | from .generated import data_pb2 as data_pb2 |
| 4 | from .items import SYMBOL_ITEMS | ||
| 4 | from typing import TYPE_CHECKING, NamedTuple | 5 | from typing import TYPE_CHECKING, NamedTuple |
| 5 | 6 | ||
| 6 | from .options import VictoryCondition, ShuffleLetters, CyanDoorBehavior | 7 | from .options import ShuffleLetters, CyanDoorBehavior |
| 7 | 8 | ||
| 8 | if TYPE_CHECKING: | 9 | if TYPE_CHECKING: |
| 9 | from . import Lingo2World | 10 | from . import Lingo2World |
| @@ -23,21 +24,38 @@ class AccessRequirements: | |||
| 23 | items: set[str] | 24 | items: set[str] |
| 24 | progressives: dict[str, int] | 25 | progressives: dict[str, int] |
| 25 | rooms: set[str] | 26 | rooms: set[str] |
| 26 | symbols: set[str] | ||
| 27 | letters: dict[str, int] | 27 | letters: dict[str, int] |
| 28 | cyans: bool | 28 | cyans: bool |
| 29 | 29 | ||
| 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() |
| 36 | self.rooms = set() | 41 | self.rooms = set() |
| 37 | self.symbols = set() | ||
| 38 | self.letters = dict() | 42 | self.letters = dict() |
| 39 | self.cyans = False | 43 | self.cyans = False |
| 40 | 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 | ||
| 41 | 59 | ||
| 42 | def merge(self, other: "AccessRequirements"): | 60 | def merge(self, other: "AccessRequirements"): |
| 43 | for item in other.items: | 61 | for item in other.items: |
| @@ -49,16 +67,105 @@ class AccessRequirements: | |||
| 49 | for room in other.rooms: | 67 | for room in other.rooms: |
| 50 | self.rooms.add(room) | 68 | self.rooms.add(room) |
| 51 | 69 | ||
| 52 | for symbol in other.symbols: | ||
| 53 | self.symbols.add(symbol) | ||
| 54 | |||
| 55 | for letter, level in other.letters.items(): | 70 | for letter, level in other.letters.items(): |
| 56 | self.letters[letter] = max(self.letters.get(letter, 0), level) | 71 | self.letters[letter] = max(self.letters.get(letter, 0), level) |
| 57 | 72 | ||
| 58 | self.cyans = self.cyans or other.cyans | 73 | self.cyans = self.cyans or other.cyans |
| 59 | 74 | ||
| 60 | for disjunction in other.or_logic: | 75 | for disjunction in other.or_logic: |
| 61 | self.or_logic.append(disjunction) | 76 | self.or_logic.append([sub_req.copy() for sub_req in disjunction]) |
| 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 = [sub_req.copy() for sub_req in 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 = [sub_req.copy() for sub_req in other.possibilities] | ||
| 96 | self.or_logic.append([right_req]) | ||
| 97 | else: | ||
| 98 | self.complete_at = other.complete_at | ||
| 99 | self.possibilities = [sub_req.copy() for sub_req in other.possibilities] | ||
| 100 | |||
| 101 | def is_empty(self) -> bool: | ||
| 102 | return (len(self.items) == 0 and len(self.progressives) == 0 and len(self.rooms) == 0 and len(self.letters) == 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 = [] |
| @@ -68,15 +175,17 @@ class AccessRequirements: | |||
| 68 | parts.append(f"progressives={self.progressives}") | 175 | parts.append(f"progressives={self.progressives}") |
| 69 | if len(self.rooms) > 0: | 176 | if len(self.rooms) > 0: |
| 70 | parts.append(f"rooms={self.rooms}") | 177 | parts.append(f"rooms={self.rooms}") |
| 71 | if len(self.symbols) > 0: | ||
| 72 | parts.append(f"symbols={self.symbols}") | ||
| 73 | if len(self.letters) > 0: | 178 | if len(self.letters) > 0: |
| 74 | parts.append(f"letters={self.letters}") | 179 | parts.append(f"letters={self.letters}") |
| 75 | if self.cyans: | 180 | if self.cyans: |
| 76 | parts.append(f"cyans=True") | 181 | parts.append(f"cyans=True") |
| 77 | if len(self.or_logic) > 0: | 182 | if len(self.or_logic) > 0: |
| 78 | parts.append(f"or_logic={self.or_logic}") | 183 | parts.append(f"or_logic={self.or_logic}") |
| 79 | 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) + ")" | ||
| 80 | 189 | ||
| 81 | 190 | ||
| 82 | class PlayerLocation(NamedTuple): | 191 | class PlayerLocation(NamedTuple): |
| @@ -93,6 +202,8 @@ class LetterBehavior(IntEnum): | |||
| 93 | class Lingo2PlayerLogic: | 202 | class Lingo2PlayerLogic: |
| 94 | world: "Lingo2World" | 203 | world: "Lingo2World" |
| 95 | 204 | ||
| 205 | shuffled_maps: set[int] | ||
| 206 | |||
| 96 | locations_by_room: dict[int, list[PlayerLocation]] | 207 | locations_by_room: dict[int, list[PlayerLocation]] |
| 97 | event_loc_item_by_room: dict[int, dict[str, str]] | 208 | event_loc_item_by_room: dict[int, dict[str, str]] |
| 98 | 209 | ||
| @@ -105,6 +216,7 @@ class Lingo2PlayerLogic: | |||
| 105 | real_items: list[str] | 216 | real_items: list[str] |
| 106 | 217 | ||
| 107 | double_letter_amount: dict[str, int] | 218 | double_letter_amount: dict[str, int] |
| 219 | goal_room_id: int | ||
| 108 | 220 | ||
| 109 | def __init__(self, world: "Lingo2World"): | 221 | def __init__(self, world: "Lingo2World"): |
| 110 | self.world = world | 222 | self.world = world |
| @@ -117,30 +229,76 @@ class Lingo2PlayerLogic: | |||
| 117 | self.real_items = list() | 229 | self.real_items = list() |
| 118 | self.double_letter_amount = dict() | 230 | self.double_letter_amount = dict() |
| 119 | 231 | ||
| 232 | def should_shuffle_map(game_map) -> bool: | ||
| 233 | if game_map.type == data_pb2.MapType.NORMAL_MAP: | ||
| 234 | return True | ||
| 235 | elif game_map.type == data_pb2.MapType.ICARUS: | ||
| 236 | return bool(world.options.enable_icarus) | ||
| 237 | elif game_map.type == data_pb2.MapType.GIFT_MAP: | ||
| 238 | if game_map.name == "the_advanced": | ||
| 239 | return "The Advanced" in world.options.enable_gift_maps.value | ||
| 240 | elif game_map.name == "the_charismatic": | ||
| 241 | return "The Charismatic" in world.options.enable_gift_maps.value | ||
| 242 | elif game_map.name == "the_crystalline": | ||
| 243 | return "The Crystalline" in world.options.enable_gift_maps.value | ||
| 244 | elif game_map.name == "the_fuzzy": | ||
| 245 | return "The Fuzzy" in world.options.enable_gift_maps.value | ||
| 246 | elif game_map.name == "the_stellar": | ||
| 247 | return "The Stellar" in world.options.enable_gift_maps.value | ||
| 248 | |||
| 249 | return False | ||
| 250 | |||
| 251 | self.shuffled_maps = set(game_map.id for game_map in world.static_logic.objects.maps | ||
| 252 | if should_shuffle_map(game_map)) | ||
| 253 | |||
| 254 | maximum_masteries = 13 + len(world.options.enable_gift_maps.value) | ||
| 255 | if world.options.enable_icarus: | ||
| 256 | maximum_masteries += 1 | ||
| 257 | |||
| 258 | if world.options.masteries_requirement > maximum_masteries: | ||
| 259 | world.options.masteries_requirement.value = maximum_masteries | ||
| 260 | |||
| 261 | if "The Fuzzy" in world.options.enable_gift_maps.value: | ||
| 262 | self.real_items.append("Numbers") | ||
| 263 | |||
| 120 | if self.world.options.shuffle_doors: | 264 | if self.world.options.shuffle_doors: |
| 121 | for progressive in world.static_logic.objects.progressives: | 265 | for progressive in world.static_logic.objects.progressives: |
| 122 | for i in range(0, len(progressive.doors)): | 266 | for i in range(0, len(progressive.doors)): |
| 267 | door = world.static_logic.objects.doors[progressive.doors[i]] | ||
| 268 | if door.map_id not in self.shuffled_maps: | ||
| 269 | continue | ||
| 270 | |||
| 123 | self.item_by_door[progressive.doors[i]] = (progressive.name, i + 1) | 271 | self.item_by_door[progressive.doors[i]] = (progressive.name, i + 1) |
| 124 | self.real_items.append(progressive.name) | 272 | self.real_items.append(progressive.name) |
| 125 | 273 | ||
| 126 | for door_group in world.static_logic.objects.door_groups: | 274 | for door_group in world.static_logic.objects.door_groups: |
| 127 | if door_group.type == data_pb2.DoorGroupType.CONNECTOR: | 275 | if door_group.type == data_pb2.DoorGroupType.CONNECTOR: |
| 128 | if not self.world.options.shuffle_doors: | 276 | if not self.world.options.shuffle_doors or self.world.options.shuffle_worldports: |
| 129 | continue | 277 | continue |
| 130 | elif door_group.type == data_pb2.DoorGroupType.COLOR_CONNECTOR: | 278 | elif door_group.type == data_pb2.DoorGroupType.COLOR_CONNECTOR: |
| 131 | if not self.world.options.shuffle_control_center_colors: | 279 | if not self.world.options.shuffle_control_center_colors or self.world.options.shuffle_worldports: |
| 280 | continue | ||
| 281 | elif door_group.type == data_pb2.DoorGroupType.SHUFFLE_GROUP: | ||
| 282 | if not self.world.options.shuffle_doors: | ||
| 132 | continue | 283 | continue |
| 133 | else: | 284 | else: |
| 134 | continue | 285 | continue |
| 135 | 286 | ||
| 136 | for door in door_group.doors: | 287 | shuffleable_doors = [door_id for door_id in door_group.doors |
| 137 | self.item_by_door[door] = (door_group.name, 1) | 288 | if world.static_logic.objects.doors[door_id].map_id in self.shuffled_maps] |
| 138 | 289 | ||
| 139 | self.real_items.append(door_group.name) | 290 | if len(shuffleable_doors) > 0: |
| 291 | for door in shuffleable_doors: | ||
| 292 | self.item_by_door[door] = (door_group.name, 1) | ||
| 293 | |||
| 294 | self.real_items.append(door_group.name) | ||
| 140 | 295 | ||
| 141 | # We iterate through the doors in two parts because it is essential that we determine which doors are shuffled | 296 | # We iterate through the doors in two parts because it is essential that we determine which doors are shuffled |
| 142 | # before we calculate any access requirements. | 297 | # before we calculate any access requirements. |
| 143 | for door in world.static_logic.objects.doors: | 298 | for door in world.static_logic.objects.doors: |
| 299 | if door.map_id not in self.shuffled_maps: | ||
| 300 | continue | ||
| 301 | |||
| 144 | if door.type in [data_pb2.DoorType.EVENT, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: | 302 | if door.type in [data_pb2.DoorType.EVENT, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: |
| 145 | continue | 303 | continue |
| 146 | 304 | ||
| @@ -155,6 +313,9 @@ class Lingo2PlayerLogic: | |||
| 155 | not self.world.options.shuffle_control_center_colors): | 313 | not self.world.options.shuffle_control_center_colors): |
| 156 | continue | 314 | continue |
| 157 | 315 | ||
| 316 | if door.type == data_pb2.DoorType.GALLERY_PAINTING and not self.world.options.shuffle_gallery_paintings: | ||
| 317 | continue | ||
| 318 | |||
| 158 | door_item_name = self.world.static_logic.get_door_item_name(door) | 319 | door_item_name = self.world.static_logic.get_door_item_name(door) |
| 159 | self.item_by_door[door.id] = (door_item_name, 1) | 320 | self.item_by_door[door.id] = (door_item_name, 1) |
| 160 | self.real_items.append(door_item_name) | 321 | self.real_items.append(door_item_name) |
| @@ -166,29 +327,40 @@ class Lingo2PlayerLogic: | |||
| 166 | if door_group.type != data_pb2.DoorGroupType.CYAN_DOORS: | 327 | if door_group.type != data_pb2.DoorGroupType.CYAN_DOORS: |
| 167 | continue | 328 | continue |
| 168 | 329 | ||
| 169 | for door in door_group.doors: | 330 | shuffleable_doors = [door_id for door_id in door_group.doors |
| 170 | if not door in self.item_by_door: | 331 | if world.static_logic.objects.doors[door_id].map_id in self.shuffled_maps |
| 332 | and door_id not in self.item_by_door] | ||
| 333 | |||
| 334 | if len(shuffleable_doors) > 0: | ||
| 335 | for door in shuffleable_doors: | ||
| 171 | self.item_by_door[door] = (door_group.name, 1) | 336 | self.item_by_door[door] = (door_group.name, 1) |
| 172 | 337 | ||
| 173 | self.real_items.append(door_group.name) | 338 | self.real_items.append(door_group.name) |
| 174 | 339 | ||
| 175 | for door in world.static_logic.objects.doors: | 340 | for door in world.static_logic.objects.doors: |
| 341 | if door.map_id not in self.shuffled_maps: | ||
| 342 | continue | ||
| 343 | |||
| 176 | if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: | 344 | if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: |
| 177 | self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id, | 345 | self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id, |
| 178 | self.get_door_reqs(door.id))) | 346 | self.get_door_reqs(door.id))) |
| 179 | 347 | ||
| 180 | for letter in world.static_logic.objects.letters: | 348 | for letter in world.static_logic.objects.letters: |
| 349 | if world.static_logic.get_room_object_map_id(letter) not in self.shuffled_maps: | ||
| 350 | continue | ||
| 351 | |||
| 181 | self.locations_by_room.setdefault(letter.room_id, []).append(PlayerLocation(letter.ap_id, | 352 | self.locations_by_room.setdefault(letter.room_id, []).append(PlayerLocation(letter.ap_id, |
| 182 | AccessRequirements())) | 353 | AccessRequirements())) |
| 183 | behavior = self.get_letter_behavior(letter.key, letter.level2) | 354 | behavior = self.get_letter_behavior(letter.key, letter.level2) |
| 184 | if behavior == LetterBehavior.VANILLA: | 355 | if behavior == LetterBehavior.VANILLA: |
| 185 | letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}" | 356 | if not world.for_tracker: |
| 186 | event_name = f"{letter_name} (Collected)" | 357 | letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}" |
| 187 | self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() | 358 | event_name = f"{letter_name} (Collected)" |
| 188 | |||
| 189 | if letter.level2: | ||
| 190 | event_name = f"{letter_name} (Double Collected)" | ||
| 191 | self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() | 359 | self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() |
| 360 | |||
| 361 | if letter.level2: | ||
| 362 | event_name = f"{letter_name} (Double Collected)" | ||
| 363 | self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() | ||
| 192 | elif behavior == LetterBehavior.ITEM: | 364 | elif behavior == LetterBehavior.ITEM: |
| 193 | self.real_items.append(letter.key.upper()) | 365 | self.real_items.append(letter.key.upper()) |
| 194 | 366 | ||
| @@ -196,30 +368,42 @@ class Lingo2PlayerLogic: | |||
| 196 | self.double_letter_amount[letter.key.upper()] = self.double_letter_amount.get(letter.key.upper(), 0) + 1 | 368 | self.double_letter_amount[letter.key.upper()] = self.double_letter_amount.get(letter.key.upper(), 0) + 1 |
| 197 | 369 | ||
| 198 | for mastery in world.static_logic.objects.masteries: | 370 | for mastery in world.static_logic.objects.masteries: |
| 371 | if world.static_logic.get_room_object_map_id(mastery) not in self.shuffled_maps: | ||
| 372 | continue | ||
| 373 | |||
| 199 | self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, | 374 | self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, |
| 200 | AccessRequirements())) | 375 | AccessRequirements())) |
| 201 | 376 | ||
| 377 | if world.options.masteries_requirement > 0: | ||
| 378 | event_name = f"{world.static_logic.get_room_object_map_name(mastery)} - Mastery (Collected)" | ||
| 379 | self.event_loc_item_by_room.setdefault(mastery.room_id, {})[event_name] = "Mastery" | ||
| 380 | |||
| 202 | for ending in world.static_logic.objects.endings: | 381 | for ending in world.static_logic.objects.endings: |
| 203 | # Don't ever create a location for White Ending. Don't even make an event for it if it's not the victory | 382 | if world.static_logic.get_room_object_map_id(ending) not in self.shuffled_maps: |
| 204 | # condition, since it is necessarily going to be in the postgame. | 383 | continue |
| 205 | if ending.name == "WHITE": | 384 | |
| 206 | if self.world.options.victory_condition != VictoryCondition.option_white_ending: | 385 | # Don't create a location for your selected ending. Also don't create a location for White Ending if it's |
| 207 | continue | 386 | # necessarily in the postgame, i.e. it requires all 12 other endings. |
| 208 | else: | 387 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() != ending.name\ |
| 388 | and (ending.name != "WHITE" or world.options.endings_requirement < 12): | ||
| 209 | self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id, | 389 | self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id, |
| 210 | AccessRequirements())) | 390 | AccessRequirements())) |
| 211 | 391 | ||
| 212 | event_name = f"{ending.name.capitalize()} Ending (Achieved)" | ||
| 213 | item_name = event_name | ||
| 214 | |||
| 215 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: | 392 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: |
| 216 | item_name = "Victory" | 393 | event_name = f"{ending.name.capitalize()} Ending (Goal)" |
| 394 | self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = "Victory" | ||
| 395 | self.goal_room_id = ending.room_id | ||
| 217 | 396 | ||
| 218 | self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = item_name | 397 | if ending.name != "WHITE": |
| 398 | event_name = f"{ending.name.capitalize()} Ending (Achieved)" | ||
| 399 | self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = "Ending" | ||
| 219 | 400 | ||
| 220 | if self.world.options.keyholder_sanity: | 401 | if self.world.options.keyholder_sanity: |
| 221 | for keyholder in world.static_logic.objects.keyholders: | 402 | for keyholder in world.static_logic.objects.keyholders: |
| 222 | if keyholder.HasField("key"): | 403 | if keyholder.HasField("key"): |
| 404 | if world.static_logic.get_room_object_map_id(keyholder) not in self.shuffled_maps: | ||
| 405 | continue | ||
| 406 | |||
| 223 | reqs = AccessRequirements() | 407 | reqs = AccessRequirements() |
| 224 | 408 | ||
| 225 | if self.get_letter_behavior(keyholder.key, False) != LetterBehavior.UNLOCKED: | 409 | if self.get_letter_behavior(keyholder.key, False) != LetterBehavior.UNLOCKED: |
| @@ -228,6 +412,10 @@ class Lingo2PlayerLogic: | |||
| 228 | self.locations_by_room.setdefault(keyholder.room_id, []).append(PlayerLocation(keyholder.ap_id, | 412 | self.locations_by_room.setdefault(keyholder.room_id, []).append(PlayerLocation(keyholder.ap_id, |
| 229 | reqs)) | 413 | reqs)) |
| 230 | 414 | ||
| 415 | if self.world.options.shuffle_symbols: | ||
| 416 | for symbol_name in SYMBOL_ITEMS.values(): | ||
| 417 | self.real_items.append(symbol_name) | ||
| 418 | |||
| 231 | def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements: | 419 | def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements: |
| 232 | if answer is None: | 420 | if answer is None: |
| 233 | if panel_id not in self.panel_reqs: | 421 | if panel_id not in self.panel_reqs: |
| @@ -250,25 +438,35 @@ class Lingo2PlayerLogic: | |||
| 250 | self.add_solution_reqs(reqs, answer) | 438 | self.add_solution_reqs(reqs, answer) |
| 251 | elif len(panel.proxies) > 0: | 439 | elif len(panel.proxies) > 0: |
| 252 | possibilities = [] | 440 | possibilities = [] |
| 441 | already_filled = False | ||
| 253 | 442 | ||
| 254 | for proxy in panel.proxies: | 443 | for proxy in panel.proxies: |
| 255 | proxy_reqs = AccessRequirements() | 444 | proxy_reqs = AccessRequirements() |
| 256 | self.add_solution_reqs(proxy_reqs, proxy.answer) | 445 | self.add_solution_reqs(proxy_reqs, proxy.answer) |
| 257 | 446 | ||
| 258 | possibilities.append(proxy_reqs) | 447 | if not proxy_reqs.is_empty(): |
| 448 | possibilities.append(proxy_reqs) | ||
| 449 | else: | ||
| 450 | already_filled = True | ||
| 451 | break | ||
| 259 | 452 | ||
| 260 | if not any(proxy.answer == panel.answer for proxy in panel.proxies): | 453 | if not already_filled and not any(proxy.answer == panel.answer for proxy in panel.proxies): |
| 261 | proxy_reqs = AccessRequirements() | 454 | proxy_reqs = AccessRequirements() |
| 262 | self.add_solution_reqs(proxy_reqs, panel.answer) | 455 | self.add_solution_reqs(proxy_reqs, panel.answer) |
| 263 | 456 | ||
| 264 | possibilities.append(proxy_reqs) | 457 | if not proxy_reqs.is_empty(): |
| 458 | possibilities.append(proxy_reqs) | ||
| 459 | else: | ||
| 460 | already_filled = True | ||
| 265 | 461 | ||
| 266 | reqs.or_logic.append(possibilities) | 462 | if not already_filled: |
| 463 | reqs.or_logic.append(possibilities) | ||
| 267 | else: | 464 | else: |
| 268 | self.add_solution_reqs(reqs, panel.answer) | 465 | self.add_solution_reqs(reqs, panel.answer) |
| 269 | 466 | ||
| 270 | for symbol in panel.symbols: | 467 | if self.world.options.shuffle_symbols: |
| 271 | reqs.symbols.add(symbol) | 468 | for symbol in panel.symbols: |
| 469 | reqs.items.add(SYMBOL_ITEMS.get(symbol)) | ||
| 272 | 470 | ||
| 273 | if panel.HasField("required_door"): | 471 | if panel.HasField("required_door"): |
| 274 | door_reqs = self.get_door_open_reqs(panel.required_door) | 472 | door_reqs = self.get_door_open_reqs(panel.required_door) |
| @@ -291,21 +489,28 @@ class Lingo2PlayerLogic: | |||
| 291 | door = self.world.static_logic.objects.doors[door_id] | 489 | door = self.world.static_logic.objects.doors[door_id] |
| 292 | reqs = AccessRequirements() | 490 | reqs = AccessRequirements() |
| 293 | 491 | ||
| 294 | # TODO: lavender_cubes, endings | ||
| 295 | if not door.HasField("complete_at") or door.complete_at == 0: | 492 | if not door.HasField("complete_at") or door.complete_at == 0: |
| 296 | for proxy in door.panels: | 493 | for proxy in door.panels: |
| 297 | panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) | 494 | panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) |
| 298 | reqs.merge(panel_reqs) | 495 | reqs.merge(panel_reqs) |
| 299 | elif door.complete_at == 1: | 496 | elif door.complete_at == 1: |
| 300 | reqs.or_logic.append([self.get_panel_reqs(proxy.panel, | 497 | disjunction = [] |
| 301 | proxy.answer if proxy.HasField("answer") else None) | 498 | for proxy in door.panels: |
| 302 | for proxy in door.panels]) | 499 | proxy_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) |
| 500 | if proxy_reqs.is_empty(): | ||
| 501 | disjunction.clear() | ||
| 502 | break | ||
| 503 | else: | ||
| 504 | disjunction.append(proxy_reqs) | ||
| 505 | if len(disjunction) > 0: | ||
| 506 | reqs.or_logic.append(disjunction) | ||
| 303 | else: | 507 | else: |
| 304 | # TODO: Handle complete_at > 1 | 508 | reqs.complete_at = door.complete_at |
| 305 | pass | 509 | for proxy in door.panels: |
| 510 | panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) | ||
| 511 | reqs.possibilities.append(panel_reqs) | ||
| 306 | 512 | ||
| 307 | if door.HasField("control_center_color"): | 513 | if door.HasField("control_center_color"): |
| 308 | # TODO: Logic for ensuring two CC states aren't needed at once. | ||
| 309 | reqs.rooms.add("Control Center - Main Area") | 514 | reqs.rooms.add("Control Center - Main Area") |
| 310 | self.add_solution_reqs(reqs, door.control_center_color) | 515 | self.add_solution_reqs(reqs, door.control_center_color) |
| 311 | 516 | ||
| @@ -313,7 +518,8 @@ class Lingo2PlayerLogic: | |||
| 313 | if self.world.options.cyan_door_behavior == CyanDoorBehavior.option_collect_h2: | 518 | if self.world.options.cyan_door_behavior == CyanDoorBehavior.option_collect_h2: |
| 314 | reqs.rooms.add("The Repetitive - Main Room") | 519 | reqs.rooms.add("The Repetitive - Main Room") |
| 315 | elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_any_double_letter: | 520 | elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_any_double_letter: |
| 316 | reqs.cyans = True | 521 | if self.world.options.shuffle_letters != ShuffleLetters.option_unlocked: |
| 522 | reqs.cyans = True | ||
| 317 | elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_item: | 523 | elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_item: |
| 318 | # There shouldn't be any locations that are cyan doors. | 524 | # There shouldn't be any locations that are cyan doors. |
| 319 | pass | 525 | pass |
| @@ -330,14 +536,19 @@ class Lingo2PlayerLogic: | |||
| 330 | for room in door.rooms: | 536 | for room in door.rooms: |
| 331 | reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) | 537 | reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) |
| 332 | 538 | ||
| 333 | for ending_id in door.endings: | 539 | if door.white_ending: |
| 334 | ending = self.world.static_logic.objects.endings[ending_id] | 540 | if self.world.options.endings_requirement > 0: |
| 335 | reqs.items.add(f"{ending.name.capitalize()} Ending (Achieved)") | 541 | reqs.progressives["Ending"] = self.world.options.endings_requirement.value |
| 542 | |||
| 543 | if self.world.options.masteries_requirement > 0: | ||
| 544 | reqs.progressives["Mastery"] = self.world.options.masteries_requirement.value | ||
| 336 | 545 | ||
| 337 | for sub_door_id in door.doors: | 546 | for sub_door_id in door.doors: |
| 338 | sub_reqs = self.get_door_open_reqs(sub_door_id) | 547 | sub_reqs = self.get_door_open_reqs(sub_door_id) |
| 339 | reqs.merge(sub_reqs) | 548 | reqs.merge(sub_reqs) |
| 340 | 549 | ||
| 550 | reqs.simplify() | ||
| 551 | |||
| 341 | return reqs | 552 | return reqs |
| 342 | 553 | ||
| 343 | # This gets the requirements to open a door within the world. When a door is shuffled, this means having the item | 554 | # This gets the requirements to open a door within the world. When a door is shuffled, this means having the item |
| @@ -392,3 +603,6 @@ class Lingo2PlayerLogic: | |||
| 392 | 603 | ||
| 393 | if needed > 0: | 604 | if needed > 0: |
| 394 | reqs.letters[l] = max(reqs.letters.get(l, 0), needed) | 605 | reqs.letters[l] = max(reqs.letters.get(l, 0), needed) |
| 606 | |||
| 607 | if any(l.isnumeric() for l in solution): | ||
| 608 | reqs.items.add("Numbers") | ||
| diff --git a/apworld/regions.py b/apworld/regions.py index e30493c..1118603 100644 --- a/apworld/regions.py +++ b/apworld/regions.py | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | from typing import TYPE_CHECKING | 1 | from typing import TYPE_CHECKING |
| 2 | 2 | ||
| 3 | import BaseClasses | ||
| 3 | from BaseClasses import Region, ItemClassification, Entrance | 4 | from BaseClasses import Region, ItemClassification, Entrance |
| 5 | from entrance_rando import randomize_entrances | ||
| 4 | from .items import Lingo2Item | 6 | from .items import Lingo2Item |
| 5 | from .locations import Lingo2Location | 7 | from .locations import Lingo2Location |
| 6 | from .player_logic import AccessRequirements | 8 | from .player_logic import AccessRequirements |
| @@ -11,21 +13,42 @@ if TYPE_CHECKING: | |||
| 11 | 13 | ||
| 12 | 14 | ||
| 13 | def create_region(room, world: "Lingo2World") -> Region: | 15 | def create_region(room, world: "Lingo2World") -> Region: |
| 14 | new_region = Region(world.static_logic.get_room_region_name(room.id), world.player, world.multiworld) | 16 | return Region(world.static_logic.get_room_region_name(room.id), world.player, world.multiworld) |
| 15 | 17 | ||
| 18 | |||
| 19 | 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, {}): | 20 | for location in world.player_logic.locations_by_room.get(room.id, {}): |
| 21 | reqs = location.reqs.copy() | ||
| 22 | reqs.remove_room(new_region.name) | ||
| 23 | |||
| 17 | new_location = Lingo2Location(world.player, world.static_logic.location_id_to_name[location.code], | 24 | new_location = Lingo2Location(world.player, world.static_logic.location_id_to_name[location.code], |
| 18 | location.code, new_region) | 25 | location.code, new_region) |
| 19 | new_location.access_rule = make_location_lambda(location.reqs, world) | 26 | new_location.access_rule = make_location_lambda(reqs, world, regions) |
| 20 | new_region.locations.append(new_location) | 27 | new_region.locations.append(new_location) |
| 21 | 28 | ||
| 22 | for event_name, item_name in world.player_logic.event_loc_item_by_room.get(room.id, {}).items(): | 29 | for event_name, item_name in world.player_logic.event_loc_item_by_room.get(room.id, {}).items(): |
| 23 | new_location = Lingo2Location(world.player, event_name, None, new_region) | 30 | new_location = Lingo2Location(world.player, event_name, None, new_region) |
| 31 | if world.for_tracker and item_name == "Victory": | ||
| 32 | new_location.goal = True | ||
| 33 | |||
| 24 | event_item = Lingo2Item(item_name, ItemClassification.progression, None, world.player) | 34 | event_item = Lingo2Item(item_name, ItemClassification.progression, None, world.player) |
| 25 | new_location.place_locked_item(event_item) | 35 | new_location.place_locked_item(event_item) |
| 26 | new_region.locations.append(new_location) | 36 | new_region.locations.append(new_location) |
| 27 | 37 | ||
| 28 | return new_region | 38 | if world.for_tracker and world.options.shuffle_worldports: |
| 39 | for port_id in room.ports: | ||
| 40 | port = world.static_logic.objects.ports[port_id] | ||
| 41 | if port.no_shuffle: | ||
| 42 | continue | ||
| 43 | |||
| 44 | new_location = Lingo2Location(world.player, f"Worldport {port.id} Entered", None, new_region) | ||
| 45 | new_location.port_id = port.id | ||
| 46 | |||
| 47 | if port.HasField("required_door"): | ||
| 48 | new_location.access_rule = \ | ||
| 49 | make_location_lambda(world.player_logic.get_door_open_reqs(port.required_door), world, regions) | ||
| 50 | |||
| 51 | new_region.locations.append(new_location) | ||
| 29 | 52 | ||
| 30 | 53 | ||
| 31 | def create_regions(world: "Lingo2World"): | 54 | def create_regions(world: "Lingo2World"): |
| @@ -33,19 +56,37 @@ def create_regions(world: "Lingo2World"): | |||
| 33 | "Menu": Region("Menu", world.player, world.multiworld) | 56 | "Menu": Region("Menu", world.player, world.multiworld) |
| 34 | } | 57 | } |
| 35 | 58 | ||
| 59 | region_and_room = [] | ||
| 60 | |||
| 61 | # Create the regions in two stages. First, make the actual region objects and memoize them. Then, add all of the | ||
| 62 | # locations. This allows us to reference the actual region objects in the access rules for the locations, which is | ||
| 63 | # faster than having to look them up during access checking. | ||
| 36 | for room in world.static_logic.objects.rooms: | 64 | for room in world.static_logic.objects.rooms: |
| 65 | if room.map_id not in world.player_logic.shuffled_maps: | ||
| 66 | continue | ||
| 67 | |||
| 37 | region = create_region(room, world) | 68 | region = create_region(room, world) |
| 38 | regions[region.name] = region | 69 | regions[region.name] = region |
| 70 | region_and_room.append((region, room)) | ||
| 71 | |||
| 72 | for (region, room) in region_and_room: | ||
| 73 | create_locations(room, region, world, regions) | ||
| 39 | 74 | ||
| 40 | regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game") | 75 | regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game") |
| 41 | 76 | ||
| 42 | # TODO: The requirements of the opposite trigger also matter. | ||
| 43 | for connection in world.static_logic.objects.connections: | 77 | for connection in world.static_logic.objects.connections: |
| 44 | if connection.roof_access and not world.options.daedalus_roof_access: | 78 | if connection.roof_access and not world.options.daedalus_roof_access: |
| 45 | continue | 79 | continue |
| 46 | 80 | ||
| 81 | if connection.vanilla_only and world.options.shuffle_doors: | ||
| 82 | continue | ||
| 83 | |||
| 47 | from_region = world.static_logic.get_room_region_name(connection.from_room) | 84 | 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) | 85 | to_region = world.static_logic.get_room_region_name(connection.to_room) |
| 86 | |||
| 87 | if from_region not in regions or to_region not in regions: | ||
| 88 | continue | ||
| 89 | |||
| 49 | connection_name = f"{from_region} -> {to_region}" | 90 | connection_name = f"{from_region} -> {to_region}" |
| 50 | 91 | ||
| 51 | reqs = AccessRequirements() | 92 | reqs = AccessRequirements() |
| @@ -59,7 +100,10 @@ def create_regions(world: "Lingo2World"): | |||
| 59 | 100 | ||
| 60 | if connection.HasField("port"): | 101 | if connection.HasField("port"): |
| 61 | port = world.static_logic.objects.ports[connection.port] | 102 | port = world.static_logic.objects.ports[connection.port] |
| 62 | connection_name = f"{connection_name} (via port {port.name})" | 103 | connection_name = f"{connection_name} (via {port.display_name})" |
| 104 | |||
| 105 | if world.options.shuffle_worldports and not port.no_shuffle: | ||
| 106 | continue | ||
| 63 | 107 | ||
| 64 | if port.HasField("required_door"): | 108 | if port.HasField("required_door"): |
| 65 | reqs.merge(world.player_logic.get_door_open_reqs(port.required_door)) | 109 | reqs.merge(world.player_logic.get_door_open_reqs(port.required_door)) |
| @@ -82,14 +126,131 @@ def create_regions(world: "Lingo2World"): | |||
| 82 | else: | 126 | else: |
| 83 | connection_name = f"{connection_name} (via panel {panel.name})" | 127 | connection_name = f"{connection_name} (via panel {panel.name})" |
| 84 | 128 | ||
| 85 | if from_region in regions and to_region in regions: | 129 | 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]) | 130 | world.player_logic.add_solution_reqs(reqs, "abcdefghijklmnopqrstuvwxyz") |
| 87 | connection.access_rule = make_location_lambda(reqs, world) | 131 | |
| 132 | if connection.HasField("cyan_ending") and connection.cyan_ending and world.options.strict_cyan_ending: | ||
| 133 | world.player_logic.add_solution_reqs(reqs, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz") | ||
| 134 | |||
| 135 | reqs.simplify() | ||
| 136 | reqs.remove_room(from_region) | ||
| 137 | |||
| 138 | if to_region in reqs.rooms: | ||
| 139 | # This connection can't ever increase access because you're required to have access to the other side in | ||
| 140 | # order for it to be usable. We will just not create the connection at all, in order to help GER figure out | ||
| 141 | # what regions are dead ends. | ||
| 142 | continue | ||
| 143 | |||
| 144 | connection = Entrance(world.player, connection_name, regions[from_region]) | ||
| 145 | connection.access_rule = make_location_lambda(reqs, world, regions) | ||
| 88 | 146 | ||
| 89 | regions[from_region].exits.append(connection) | 147 | regions[from_region].exits.append(connection) |
| 90 | connection.connect(regions[to_region]) | 148 | connection.connect(regions[to_region]) |
| 91 | 149 | ||
| 92 | for region in reqs.rooms: | 150 | for region in reqs.get_referenced_rooms(): |
| 93 | world.multiworld.register_indirect_condition(regions[region], connection) | 151 | world.multiworld.register_indirect_condition(regions[region], connection) |
| 94 | 152 | ||
| 95 | world.multiworld.regions += regions.values() | 153 | world.multiworld.regions += regions.values() |
| 154 | |||
| 155 | |||
| 156 | def shuffle_entrances(world: "Lingo2World"): | ||
| 157 | er_entrances: list[Entrance] = [] | ||
| 158 | er_exits: list[Entrance] = [] | ||
| 159 | |||
| 160 | port_id_by_name: dict[str, int] = {} | ||
| 161 | |||
| 162 | shuffleable_ports = [port for port in world.static_logic.objects.ports | ||
| 163 | if not port.no_shuffle | ||
| 164 | and world.static_logic.get_room_object_map_id(port) in world.player_logic.shuffled_maps] | ||
| 165 | |||
| 166 | if len(shuffleable_ports) % 2 == 1: | ||
| 167 | # We have an odd number of shuffleable ports! Pick a port from a room that has more than one, and make it a | ||
| 168 | # redundant warp to another port. | ||
| 169 | redundant_rooms = set(room.id for room in world.static_logic.objects.rooms if len(room.ports) > 1) | ||
| 170 | redundant_ports = [port for port in shuffleable_ports if port.room_id in redundant_rooms] | ||
| 171 | chosen_port = world.random.choice(redundant_ports) | ||
| 172 | |||
| 173 | shuffleable_ports.remove(chosen_port) | ||
| 174 | |||
| 175 | chosen_destination = world.random.choice(shuffleable_ports) | ||
| 176 | |||
| 177 | world.port_pairings[chosen_port.id] = chosen_destination.id | ||
| 178 | |||
| 179 | from_region_name = world.static_logic.get_room_region_name(chosen_port.room_id) | ||
| 180 | to_region_name = world.static_logic.get_room_region_name(chosen_destination.room_id) | ||
| 181 | |||
| 182 | from_region = world.multiworld.get_region(from_region_name, world.player) | ||
| 183 | to_region = world.multiworld.get_region(to_region_name, world.player) | ||
| 184 | |||
| 185 | connection = Entrance(world.player, f"{from_region_name} - {chosen_port.display_name}", from_region) | ||
| 186 | from_region.exits.append(connection) | ||
| 187 | connection.connect(to_region) | ||
| 188 | |||
| 189 | if chosen_port.HasField("required_door"): | ||
| 190 | door_reqs = world.player_logic.get_door_open_reqs(chosen_port.required_door) | ||
| 191 | connection.access_rule = make_location_lambda(door_reqs, world, None) | ||
| 192 | |||
| 193 | for region in door_reqs.get_referenced_rooms(): | ||
| 194 | world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player), | ||
| 195 | connection) | ||
| 196 | |||
| 197 | for port in shuffleable_ports: | ||
| 198 | port_region_name = world.static_logic.get_room_region_name(port.room_id) | ||
| 199 | port_region = world.multiworld.get_region(port_region_name, world.player) | ||
| 200 | |||
| 201 | connection_name = f"{port_region_name} - {port.display_name}" | ||
| 202 | port_id_by_name[connection_name] = port.id | ||
| 203 | |||
| 204 | entrance = port_region.create_er_target(connection_name) | ||
| 205 | entrance.randomization_type = BaseClasses.EntranceType.TWO_WAY | ||
| 206 | |||
| 207 | er_exit = port_region.create_exit(connection_name) | ||
| 208 | er_exit.randomization_type = BaseClasses.EntranceType.TWO_WAY | ||
| 209 | |||
| 210 | if port.HasField("required_door"): | ||
| 211 | door_reqs = world.player_logic.get_door_open_reqs(port.required_door) | ||
| 212 | er_exit.access_rule = make_location_lambda(door_reqs, world, None) | ||
| 213 | |||
| 214 | for region in door_reqs.get_referenced_rooms(): | ||
| 215 | world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player), | ||
| 216 | er_exit) | ||
| 217 | |||
| 218 | er_entrances.append(entrance) | ||
| 219 | er_exits.append(er_exit) | ||
| 220 | |||
| 221 | result = randomize_entrances(world, True, {0:[0]}, False, er_entrances, | ||
| 222 | er_exits) | ||
| 223 | |||
| 224 | for (f, to) in result.pairings: | ||
| 225 | world.port_pairings[port_id_by_name[f]] = port_id_by_name[to] | ||
| 226 | |||
| 227 | |||
| 228 | def connect_ports_from_ut(port_pairings: dict[int, int], world: "Lingo2World"): | ||
| 229 | for fpid, tpid in port_pairings.items(): | ||
| 230 | from_port = world.static_logic.objects.ports[fpid] | ||
| 231 | to_port = world.static_logic.objects.ports[tpid] | ||
| 232 | |||
| 233 | from_region_name = world.static_logic.get_room_region_name(from_port.room_id) | ||
| 234 | to_region_name = world.static_logic.get_room_region_name(to_port.room_id) | ||
| 235 | |||
| 236 | from_region = world.multiworld.get_region(from_region_name, world.player) | ||
| 237 | to_region = world.multiworld.get_region(to_region_name, world.player) | ||
| 238 | |||
| 239 | connection = Entrance(world.player, f"{from_region_name} - {from_port.display_name}", from_region) | ||
| 240 | |||
| 241 | reqs = AccessRequirements() | ||
| 242 | if from_port.HasField("required_door"): | ||
| 243 | reqs = world.player_logic.get_door_open_reqs(from_port.required_door).copy() | ||
| 244 | |||
| 245 | if world.for_tracker: | ||
| 246 | reqs.items.add(f"Worldport {fpid} Entered") | ||
| 247 | |||
| 248 | if not reqs.is_empty(): | ||
| 249 | connection.access_rule = make_location_lambda(reqs, world, None) | ||
| 250 | |||
| 251 | for region in reqs.get_referenced_rooms(): | ||
| 252 | world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player), | ||
| 253 | connection) | ||
| 254 | |||
| 255 | from_region.exits.append(connection) | ||
| 256 | connection.connect(to_region) | ||
| diff --git a/apworld/requirements.txt b/apworld/requirements.txt index b701d11..dbc395b 100644 --- a/apworld/requirements.txt +++ b/apworld/requirements.txt | |||
| @@ -1 +1 @@ | |||
| protobuf>=5.29.3 \ No newline at end of file | protobuf==3.20.3 | ||
| diff --git a/apworld/rules.py b/apworld/rules.py index 56486fa..f859e75 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,7 +19,8 @@ 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 | ||
| 21 | # TODO: symbols | 22 | if not all(state.can_reach(region) for region in regions): |
| 23 | return False | ||
| 22 | 24 | ||
| 23 | for letter_key, letter_level in reqs.letters.items(): | 25 | for letter_key, letter_level in reqs.letters.items(): |
| 24 | if not state.has(letter_key, world.player, letter_level): | 26 | if not state.has(letter_key, world.player, letter_level): |
| @@ -30,11 +32,35 @@ def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirem | |||
| 30 | return False | 32 | return False |
| 31 | 33 | ||
| 32 | if len(reqs.or_logic) > 0: | 34 | if len(reqs.or_logic) > 0: |
| 33 | 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) |
| 34 | for subjunction in reqs.or_logic): | 36 | for subjunction in reqs.or_logic): |
| 35 | return False | 37 | return False |
| 36 | 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 | |||
| 37 | return True | 54 | return True |
| 38 | 55 | ||
| 39 | def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World") -> Callable[[CollectionState], bool]: | 56 | def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World", |
| 40 | return lambda state: lingo2_can_satisfy_requirements(state, reqs, world) | 57 | regions: dict[str, Region] | None) -> 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 | if regions is not None: | ||
| 61 | required_regions = [regions[room_name] for room_name in reqs.rooms] | ||
| 62 | else: | ||
| 63 | required_regions = [world.multiworld.get_region(room_name, world.player) for room_name in reqs.rooms] | ||
| 64 | new_reqs = reqs.copy() | ||
| 65 | new_reqs.rooms.clear() | ||
| 66 | 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 3f6cdea..8a84111 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | from .generated import data_pb2 as data_pb2 | 1 | from .generated import data_pb2 as data_pb2 |
| 2 | from .items import SYMBOL_ITEMS, ANTI_COLLECTABLE_TRAPS | ||
| 2 | import pkgutil | 3 | import pkgutil |
| 3 | 4 | ||
| 5 | |||
| 4 | class Lingo2StaticLogic: | 6 | class Lingo2StaticLogic: |
| 5 | item_id_to_name: dict[int, str] | 7 | item_id_to_name: dict[int, str] |
| 6 | location_id_to_name: dict[int, str] | 8 | location_id_to_name: dict[int, str] |
| @@ -8,9 +10,20 @@ class Lingo2StaticLogic: | |||
| 8 | item_name_to_id: dict[str, int] | 10 | item_name_to_id: dict[str, int] |
| 9 | location_name_to_id: dict[str, int] | 11 | location_name_to_id: dict[str, int] |
| 10 | 12 | ||
| 13 | item_name_groups: dict[str, list[str]] | ||
| 14 | location_name_groups: dict[str, list[str]] | ||
| 15 | |||
| 16 | letter_weights: dict[str, int] | ||
| 17 | |||
| 18 | door_id_by_ap_id: dict[int, int] | ||
| 19 | port_id_by_ap_id: dict[int, int] | ||
| 20 | |||
| 11 | def __init__(self): | 21 | def __init__(self): |
| 12 | self.item_id_to_name = {} | 22 | self.item_id_to_name = {} |
| 13 | self.location_id_to_name = {} | 23 | self.location_id_to_name = {} |
| 24 | self.item_name_groups = {} | ||
| 25 | self.location_name_groups = {} | ||
| 26 | self.letter_weights = {} | ||
| 14 | 27 | ||
| 15 | file = pkgutil.get_data(__name__, "generated/data.binpb") | 28 | file = pkgutil.get_data(__name__, "generated/data.binpb") |
| 16 | self.objects = data_pb2.AllObjects() | 29 | self.objects = data_pb2.AllObjects() |
| @@ -29,17 +42,21 @@ class Lingo2StaticLogic: | |||
| 29 | letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}" | 42 | letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}" |
| 30 | location_name = f"{self.get_room_object_map_name(letter)} - {letter_name}" | 43 | location_name = f"{self.get_room_object_map_name(letter)} - {letter_name}" |
| 31 | self.location_id_to_name[letter.ap_id] = location_name | 44 | self.location_id_to_name[letter.ap_id] = location_name |
| 45 | self.location_name_groups.setdefault("Letters", []).append(location_name) | ||
| 32 | 46 | ||
| 33 | if not letter.level2: | 47 | if not letter.level2: |
| 34 | self.item_id_to_name[letter.ap_id] = letter.key.upper() | 48 | self.item_id_to_name[letter.ap_id] = letter.key.upper() |
| 49 | self.item_name_groups.setdefault("Letters", []).append(letter.key.upper()) | ||
| 35 | 50 | ||
| 36 | for mastery in self.objects.masteries: | 51 | for mastery in self.objects.masteries: |
| 37 | location_name = f"{self.get_room_object_map_name(mastery)} - Mastery" | 52 | location_name = f"{self.get_room_object_map_name(mastery)} - Mastery" |
| 38 | self.location_id_to_name[mastery.ap_id] = location_name | 53 | self.location_id_to_name[mastery.ap_id] = location_name |
| 54 | self.location_name_groups.setdefault("Masteries", []).append(location_name) | ||
| 39 | 55 | ||
| 40 | for ending in self.objects.endings: | 56 | for ending in self.objects.endings: |
| 41 | location_name = f"{self.get_room_object_map_name(ending)} - {ending.name.title()} Ending" | 57 | location_name = f"{self.get_room_object_map_name(ending)} - {ending.name.title()} Ending" |
| 42 | self.location_id_to_name[ending.ap_id] = location_name | 58 | self.location_id_to_name[ending.ap_id] = location_name |
| 59 | self.location_name_groups.setdefault("Endings", []).append(location_name) | ||
| 43 | 60 | ||
| 44 | for progressive in self.objects.progressives: | 61 | for progressive in self.objects.progressives: |
| 45 | self.item_id_to_name[progressive.ap_id] = progressive.name | 62 | self.item_id_to_name[progressive.ap_id] = progressive.name |
| @@ -51,12 +68,27 @@ class Lingo2StaticLogic: | |||
| 51 | if keyholder.HasField("key"): | 68 | if keyholder.HasField("key"): |
| 52 | location_name = f"{self.get_room_object_location_prefix(keyholder)} - {keyholder.key.upper()} Keyholder" | 69 | location_name = f"{self.get_room_object_location_prefix(keyholder)} - {keyholder.key.upper()} Keyholder" |
| 53 | self.location_id_to_name[keyholder.ap_id] = location_name | 70 | self.location_id_to_name[keyholder.ap_id] = location_name |
| 71 | self.location_name_groups.setdefault("Keyholders", []).append(location_name) | ||
| 54 | 72 | ||
| 55 | self.item_id_to_name[self.objects.special_ids["A Job Well Done"]] = "A Job Well Done" | 73 | self.item_id_to_name[self.objects.special_ids["A Job Well Done"]] = "A Job Well Done" |
| 74 | self.item_id_to_name[self.objects.special_ids["Numbers"]] = "Numbers" | ||
| 75 | |||
| 76 | for symbol_name in SYMBOL_ITEMS.values(): | ||
| 77 | self.item_id_to_name[self.objects.special_ids[symbol_name]] = symbol_name | ||
| 78 | |||
| 79 | for trap_name in ANTI_COLLECTABLE_TRAPS: | ||
| 80 | self.item_id_to_name[self.objects.special_ids[trap_name]] = trap_name | ||
| 56 | 81 | ||
| 57 | self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()} | 82 | self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()} |
| 58 | self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()} | 83 | self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()} |
| 59 | 84 | ||
| 85 | for panel in self.objects.panels: | ||
| 86 | for letter in panel.answer.upper(): | ||
| 87 | self.letter_weights[letter] = self.letter_weights.get(letter, 0) + 1 | ||
| 88 | |||
| 89 | self.door_id_by_ap_id = {door.ap_id: door.id for door in self.objects.doors if door.HasField("ap_id")} | ||
| 90 | self.port_id_by_ap_id = {port.ap_id: port.id for port in self.objects.ports if port.HasField("ap_id")} | ||
| 91 | |||
| 60 | def get_door_item_name(self, door: data_pb2.Door) -> str: | 92 | def get_door_item_name(self, door: data_pb2.Door) -> str: |
| 61 | return f"{self.get_map_object_map_name(door)} - {door.name}" | 93 | return f"{self.get_map_object_map_name(door)} - {door.name}" |
| 62 | 94 | ||
| @@ -80,7 +112,7 @@ class Lingo2StaticLogic: | |||
| 80 | if door.type != data_pb2.DoorType.STANDARD: | 112 | if door.type != data_pb2.DoorType.STANDARD: |
| 81 | return None | 113 | return None |
| 82 | 114 | ||
| 83 | if len(door.keyholders) > 0 or len(door.endings) > 0: | 115 | if len(door.keyholders) > 0 or door.white_ending or door.HasField("complete_at"): |
| 84 | return None | 116 | return None |
| 85 | 117 | ||
| 86 | if len(door.panels) > 4: | 118 | if len(door.panels) > 4: |
| @@ -116,7 +148,7 @@ class Lingo2StaticLogic: | |||
| 116 | for panel_id in door.panels] | 148 | for panel_id in door.panels] |
| 117 | panel_names.sort() | 149 | panel_names.sort() |
| 118 | 150 | ||
| 119 | return f"{map_part} - {", ".join(panel_names)}" | 151 | return map_part + " - " + ", ".join(panel_names) |
| 120 | 152 | ||
| 121 | def get_door_location_name_by_id(self, door_id: int) -> str: | 153 | def get_door_location_name_by_id(self, door_id: int) -> str: |
| 122 | door = self.objects.doors[door_id] | 154 | door = self.objects.doors[door_id] |
| @@ -140,3 +172,10 @@ class Lingo2StaticLogic: | |||
| 140 | return f"{game_map.display_name} ({room.panel_display_name})" | 172 | return f"{game_map.display_name} ({room.panel_display_name})" |
| 141 | else: | 173 | else: |
| 142 | return game_map.display_name | 174 | return game_map.display_name |
| 175 | |||
| 176 | def get_room_object_map_id(self, obj) -> int: | ||
| 177 | return self.objects.rooms[obj.room_id].map_id | ||
| 178 | |||
| 179 | def get_data_version(self) -> list[int]: | ||
| 180 | version = self.objects.version | ||
| 181 | return [version.major, version.minor, version.patch] | ||
| diff --git a/apworld/tracker.py b/apworld/tracker.py new file mode 100644 index 0000000..d473af4 --- /dev/null +++ b/apworld/tracker.py | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | from typing import TYPE_CHECKING, Iterator | ||
| 2 | |||
| 3 | from BaseClasses import MultiWorld, CollectionState, ItemClassification, Region, Entrance | ||
| 4 | from NetUtils import NetworkItem | ||
| 5 | from . import Lingo2World, Lingo2Item | ||
| 6 | from .regions import connect_ports_from_ut | ||
| 7 | from .options import Lingo2Options, ShuffleLetters | ||
| 8 | |||
| 9 | if TYPE_CHECKING: | ||
| 10 | from .context import Lingo2Manager | ||
| 11 | |||
| 12 | PLAYER_NUM = 1 | ||
| 13 | |||
| 14 | |||
| 15 | class Tracker: | ||
| 16 | manager: "Lingo2Manager" | ||
| 17 | |||
| 18 | multiworld: MultiWorld | ||
| 19 | world: Lingo2World | ||
| 20 | |||
| 21 | collected_items: dict[int, int] | ||
| 22 | checked_locations: set[int] | ||
| 23 | accessible_locations: set[int] | ||
| 24 | accessible_worldports: set[int] | ||
| 25 | goal_accessible: bool | ||
| 26 | |||
| 27 | state: CollectionState | ||
| 28 | |||
| 29 | def __init__(self, manager: "Lingo2Manager"): | ||
| 30 | self.manager = manager | ||
| 31 | self.collected_items = {} | ||
| 32 | self.checked_locations = set() | ||
| 33 | self.accessible_locations = set() | ||
| 34 | self.accessible_worldports = set() | ||
| 35 | self.goal_accessible = False | ||
| 36 | |||
| 37 | def setup_slot(self, slot_data): | ||
| 38 | Lingo2World.for_tracker = True | ||
| 39 | |||
| 40 | self.multiworld = MultiWorld(players=PLAYER_NUM) | ||
| 41 | self.world = Lingo2World(self.multiworld, PLAYER_NUM) | ||
| 42 | self.multiworld.worlds[1] = self.world | ||
| 43 | self.world.options = Lingo2Options(**{k: t(slot_data.get(k, t.default)) | ||
| 44 | for k, t in Lingo2Options.type_hints.items()}) | ||
| 45 | |||
| 46 | self.world.generate_early() | ||
| 47 | self.world.create_regions() | ||
| 48 | |||
| 49 | if self.world.options.shuffle_worldports: | ||
| 50 | port_pairings = { | ||
| 51 | self.world.static_logic.port_id_by_ap_id[int(fp)]: self.world.static_logic.port_id_by_ap_id[int(tp)] | ||
| 52 | for fp, tp in slot_data["port_pairings"].items() | ||
| 53 | } | ||
| 54 | connect_ports_from_ut(port_pairings, self.world) | ||
| 55 | |||
| 56 | self.refresh_state() | ||
| 57 | |||
| 58 | def set_checked_locations(self, checked_locations: set[int]): | ||
| 59 | self.checked_locations = checked_locations.copy() | ||
| 60 | |||
| 61 | def set_collected_items(self, network_items: list[NetworkItem]): | ||
| 62 | self.collected_items = {} | ||
| 63 | |||
| 64 | for item in network_items: | ||
| 65 | self.collected_items[item.item] = self.collected_items.get(item.item, 0) + 1 | ||
| 66 | |||
| 67 | self.refresh_state() | ||
| 68 | |||
| 69 | def refresh_state(self): | ||
| 70 | self.state = CollectionState(self.multiworld) | ||
| 71 | |||
| 72 | for item_id, item_amount in self.collected_items.items(): | ||
| 73 | for i in range(item_amount): | ||
| 74 | self.state.collect(Lingo2Item(Lingo2World.static_logic.item_id_to_name.get(item_id), | ||
| 75 | ItemClassification.progression, item_id, PLAYER_NUM), prevent_sweep=True) | ||
| 76 | |||
| 77 | for k, v in self.manager.keyboard.items(): | ||
| 78 | # Unless all level 1 letters are pre-unlocked, H1 I1 N1 and T1 act differently between the generator and | ||
| 79 | # game. The generator considers them to be unlocked, which means they are not included in logic | ||
| 80 | # requirements, and only one item/event is needed to unlock their level 2 forms. The game considers them to | ||
| 81 | # be vanilla, which means you still have to pick them up in the Starting Room in order for them to appear on | ||
| 82 | # your keyboard. This also means that whether or not you have the level 1 forms should be synced to the | ||
| 83 | # multiworld. The tracker specifically should collect one fewer item for these letters in this scenario. | ||
| 84 | tv = v | ||
| 85 | if k in "hint" and self.world.options.shuffle_letters in [ShuffleLetters.option_vanilla, | ||
| 86 | ShuffleLetters.option_progressive]: | ||
| 87 | tv = max(0, v - 1) | ||
| 88 | |||
| 89 | if tv > 0: | ||
| 90 | for i in range(tv): | ||
| 91 | self.state.collect(Lingo2Item(k.upper(), ItemClassification.progression, None, PLAYER_NUM), | ||
| 92 | prevent_sweep=True) | ||
| 93 | |||
| 94 | for port_id in self.manager.worldports: | ||
| 95 | self.state.collect(Lingo2Item(f"Worldport {port_id} Entered", ItemClassification.progression, None, | ||
| 96 | PLAYER_NUM), prevent_sweep=True) | ||
| 97 | |||
| 98 | self.state.sweep_for_advancements() | ||
| 99 | |||
| 100 | self.accessible_locations = set() | ||
| 101 | self.accessible_worldports = set() | ||
| 102 | self.goal_accessible = False | ||
| 103 | |||
| 104 | for region in self.state.reachable_regions[PLAYER_NUM]: | ||
| 105 | for location in region.locations: | ||
| 106 | if location.access_rule(self.state): | ||
| 107 | if location.address is not None: | ||
| 108 | if location.address not in self.checked_locations: | ||
| 109 | self.accessible_locations.add(location.address) | ||
| 110 | elif hasattr(location, "port_id"): | ||
| 111 | if location.port_id not in self.manager.worldports: | ||
| 112 | self.accessible_worldports.add(location.port_id) | ||
| 113 | elif hasattr(location, "goal") and location.goal: | ||
| 114 | if not self.manager.goaled: | ||
| 115 | self.goal_accessible = True | ||
| 116 | |||
| 117 | def get_path_to_location(self, location_id: int) -> list[str] | None: | ||
| 118 | location_name = self.world.location_id_to_name.get(location_id) | ||
| 119 | location = self.multiworld.get_location(location_name, PLAYER_NUM) | ||
| 120 | return self.get_logical_path(location.parent_region) | ||
| 121 | |||
| 122 | def get_path_to_port(self, port_id: int) -> list[str] | None: | ||
| 123 | port = self.world.static_logic.objects.ports[port_id] | ||
| 124 | region_name = self.world.static_logic.get_room_region_name(port.room_id) | ||
| 125 | region = self.multiworld.get_region(region_name, PLAYER_NUM) | ||
| 126 | return self.get_logical_path(region) | ||
| 127 | |||
| 128 | def get_path_to_goal(self): | ||
| 129 | room_id = self.world.player_logic.goal_room_id | ||
| 130 | region_name = self.world.static_logic.get_room_region_name(room_id) | ||
| 131 | region = self.multiworld.get_region(region_name, PLAYER_NUM) | ||
| 132 | return self.get_logical_path(region) | ||
| 133 | |||
| 134 | def get_logical_path(self, region: Region) -> list[str] | None: | ||
| 135 | if region not in self.state.path: | ||
| 136 | return None | ||
| 137 | |||
| 138 | def flist_to_iter(path_value) -> Iterator[str]: | ||
| 139 | while path_value: | ||
| 140 | region_or_entrance, path_value = path_value | ||
| 141 | yield region_or_entrance | ||
| 142 | |||
| 143 | reversed_path = self.state.path.get(region) | ||
| 144 | flat_path = reversed(list(map(str, flist_to_iter(reversed_path)))) | ||
| 145 | |||
| 146 | return list(flat_path)[1::2] | ||
| diff --git a/client/Archipelago/client.gd b/client/Archipelago/client.gd deleted file mode 100644 index 2e080fd..0000000 --- a/client/Archipelago/client.gd +++ /dev/null | |||
| @@ -1,415 +0,0 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | const ap_version = {"major": 0, "minor": 6, "build": 3, "class": "Version"} | ||
| 4 | |||
| 5 | var SCRIPT_uuid | ||
| 6 | |||
| 7 | var _ws = WebSocketPeer.new() | ||
| 8 | var _should_process = false | ||
| 9 | var _initiated_disconnect = false | ||
| 10 | var _try_wss = false | ||
| 11 | var _has_connected = false | ||
| 12 | |||
| 13 | var _datapackages = {} | ||
| 14 | var _pending_packages = [] | ||
| 15 | var _item_id_to_name = {} # All games | ||
| 16 | var _location_id_to_name = {} # All games | ||
| 17 | var _item_name_to_id = {} # Lingo 2 only | ||
| 18 | var _location_name_to_id = {} # Lingo 2 only | ||
| 19 | |||
| 20 | var _remote_version = {"major": 0, "minor": 0, "build": 0} | ||
| 21 | var _gen_version = {"major": 0, "minor": 0, "build": 0} | ||
| 22 | |||
| 23 | var ap_server = "" | ||
| 24 | var ap_user = "" | ||
| 25 | var ap_pass = "" | ||
| 26 | |||
| 27 | var _authenticated = false | ||
| 28 | var _seed = "" | ||
| 29 | var _team = 0 | ||
| 30 | var _slot = 0 | ||
| 31 | var _players = [] | ||
| 32 | var _player_name_by_slot = {} | ||
| 33 | var _game_by_player = {} | ||
| 34 | var _checked_locations = [] | ||
| 35 | var _received_indexes = [] | ||
| 36 | var _received_items = {} | ||
| 37 | var _slot_data = {} | ||
| 38 | |||
| 39 | signal could_not_connect | ||
| 40 | signal connect_status | ||
| 41 | signal client_connected(slot_data) | ||
| 42 | signal item_received(item_id, index, player, flags, amount) | ||
| 43 | signal message_received(message) | ||
| 44 | signal location_scout_received(item_id, location_id, player, flags) | ||
| 45 | |||
| 46 | |||
| 47 | func _init(): | ||
| 48 | set_process_mode(Node.PROCESS_MODE_ALWAYS) | ||
| 49 | |||
| 50 | global._print("Instantiated APClient") | ||
| 51 | |||
| 52 | # Read AP datapackages from file, if there are any | ||
| 53 | if FileAccess.file_exists("user://ap_datapackages"): | ||
| 54 | var file = FileAccess.open("user://ap_datapackages", FileAccess.READ) | ||
| 55 | var data = file.get_var(true) | ||
| 56 | file.close() | ||
| 57 | |||
| 58 | if typeof(data) != TYPE_DICTIONARY: | ||
| 59 | global._print("AP datapackages file is corrupted") | ||
| 60 | data = {} | ||
| 61 | |||
| 62 | _datapackages = data | ||
| 63 | |||
| 64 | processDatapackages() | ||
| 65 | |||
| 66 | |||
| 67 | func _ready(): | ||
| 68 | pass | ||
| 69 | #_ws.connect("connection_closed", _closed) | ||
| 70 | #_ws.connect("connection_failed", _closed) | ||
| 71 | #_ws.connect("server_disconnected", _closed) | ||
| 72 | #_ws.connect("connection_error", _errored) | ||
| 73 | #_ws.connect("connection_established", _connected) | ||
| 74 | |||
| 75 | |||
| 76 | func _reset_state(): | ||
| 77 | _should_process = false | ||
| 78 | _authenticated = false | ||
| 79 | _try_wss = false | ||
| 80 | _has_connected = false | ||
| 81 | _received_items = {} | ||
| 82 | _received_indexes = [] | ||
| 83 | |||
| 84 | |||
| 85 | func _errored(): | ||
| 86 | if _try_wss: | ||
| 87 | global._print("Could not connect to AP with ws://, now trying wss://") | ||
| 88 | connectToServer(ap_server, ap_user, ap_pass) | ||
| 89 | else: | ||
| 90 | global._print("AP connection failed") | ||
| 91 | _reset_state() | ||
| 92 | |||
| 93 | emit_signal( | ||
| 94 | "could_not_connect", | ||
| 95 | "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information." | ||
| 96 | ) | ||
| 97 | |||
| 98 | |||
| 99 | func _closed(_was_clean = true): | ||
| 100 | global._print("Connection closed") | ||
| 101 | _reset_state() | ||
| 102 | |||
| 103 | if not _initiated_disconnect: | ||
| 104 | emit_signal("could_not_connect", "Disconnected from Archipelago") | ||
| 105 | |||
| 106 | _initiated_disconnect = false | ||
| 107 | |||
| 108 | |||
| 109 | func _connected(_proto = ""): | ||
| 110 | global._print("Connected!") | ||
| 111 | _try_wss = false | ||
| 112 | |||
| 113 | |||
| 114 | func disconnect_from_ap(): | ||
| 115 | _initiated_disconnect = true | ||
| 116 | _ws.close() | ||
| 117 | |||
| 118 | |||
| 119 | func _process(_delta): | ||
| 120 | if _should_process: | ||
| 121 | _ws.poll() | ||
| 122 | |||
| 123 | var state = _ws.get_ready_state() | ||
| 124 | if state == WebSocketPeer.STATE_OPEN: | ||
| 125 | if not _has_connected: | ||
| 126 | _has_connected = true | ||
| 127 | |||
| 128 | _connected() | ||
| 129 | |||
| 130 | while _ws.get_available_packet_count(): | ||
| 131 | var packet = _ws.get_packet() | ||
| 132 | global._print("Got data from server: " + packet.get_string_from_utf8()) | ||
| 133 | var json = JSON.new() | ||
| 134 | var jserror = json.parse(packet.get_string_from_utf8()) | ||
| 135 | if jserror != OK: | ||
| 136 | global._print("Error parsing packet from AP: " + jserror.error_string) | ||
| 137 | return | ||
| 138 | |||
| 139 | for message in json.data: | ||
| 140 | var cmd = message["cmd"] | ||
| 141 | global._print("Received command: " + cmd) | ||
| 142 | |||
| 143 | if cmd == "RoomInfo": | ||
| 144 | _seed = message["seed_name"] | ||
| 145 | _remote_version = message["version"] | ||
| 146 | _gen_version = message["generator_version"] | ||
| 147 | |||
| 148 | var needed_games = [] | ||
| 149 | for game in message["datapackage_checksums"].keys(): | ||
| 150 | if ( | ||
| 151 | !_datapackages.has(game) | ||
| 152 | or ( | ||
| 153 | _datapackages[game]["checksum"] | ||
| 154 | != message["datapackage_checksums"][game] | ||
| 155 | ) | ||
| 156 | ): | ||
| 157 | needed_games.append(game) | ||
| 158 | |||
| 159 | if !needed_games.is_empty(): | ||
| 160 | _pending_packages = needed_games | ||
| 161 | var cur_needed = _pending_packages.pop_front() | ||
| 162 | requestDatapackages([cur_needed]) | ||
| 163 | else: | ||
| 164 | connectToRoom() | ||
| 165 | |||
| 166 | elif cmd == "DataPackage": | ||
| 167 | for game in message["data"]["games"].keys(): | ||
| 168 | _datapackages[game] = message["data"]["games"][game] | ||
| 169 | saveDatapackages() | ||
| 170 | |||
| 171 | if !_pending_packages.is_empty(): | ||
| 172 | var cur_needed = _pending_packages.pop_front() | ||
| 173 | requestDatapackages([cur_needed]) | ||
| 174 | else: | ||
| 175 | processDatapackages() | ||
| 176 | connectToRoom() | ||
| 177 | |||
| 178 | elif cmd == "Connected": | ||
| 179 | _authenticated = true | ||
| 180 | _team = message["team"] | ||
| 181 | _slot = message["slot"] | ||
| 182 | _players = message["players"] | ||
| 183 | _checked_locations = message["checked_locations"] | ||
| 184 | _slot_data = message["slot_data"] | ||
| 185 | |||
| 186 | for player in _players: | ||
| 187 | _player_name_by_slot[player["slot"]] = player["alias"] | ||
| 188 | _game_by_player[player["slot"]] = message["slot_info"][str( | ||
| 189 | player["slot"] | ||
| 190 | )]["game"] | ||
| 191 | |||
| 192 | emit_signal("client_connected", _slot_data) | ||
| 193 | |||
| 194 | elif cmd == "ConnectionRefused": | ||
| 195 | var error_message = "" | ||
| 196 | for error in message["errors"]: | ||
| 197 | var submsg = "" | ||
| 198 | if error == "InvalidSlot": | ||
| 199 | submsg = "Invalid player name." | ||
| 200 | elif error == "InvalidGame": | ||
| 201 | submsg = "The specified player is not playing Lingo." | ||
| 202 | elif error == "IncompatibleVersion": | ||
| 203 | submsg = ( | ||
| 204 | "The Archipelago server is not the correct version for this client. Expected v%d.%d.%d. Found v%d.%d.%d." | ||
| 205 | % [ | ||
| 206 | ap_version["major"], | ||
| 207 | ap_version["minor"], | ||
| 208 | ap_version["build"], | ||
| 209 | _remote_version["major"], | ||
| 210 | _remote_version["minor"], | ||
| 211 | _remote_version["build"] | ||
| 212 | ] | ||
| 213 | ) | ||
| 214 | elif error == "InvalidPassword": | ||
| 215 | submsg = "Incorrect password." | ||
| 216 | elif error == "InvalidItemsHandling": | ||
| 217 | submsg = "Invalid item handling flag. This is a bug with the client." | ||
| 218 | |||
| 219 | if submsg != "": | ||
| 220 | if error_message != "": | ||
| 221 | error_message += " " | ||
| 222 | error_message += submsg | ||
| 223 | |||
| 224 | if error_message == "": | ||
| 225 | error_message = "Unknown error." | ||
| 226 | |||
| 227 | _initiated_disconnect = true | ||
| 228 | _ws.disconnect_from_host() | ||
| 229 | |||
| 230 | emit_signal("could_not_connect", error_message) | ||
| 231 | global._print("Connection to AP refused") | ||
| 232 | global._print(message) | ||
| 233 | |||
| 234 | elif cmd == "ReceivedItems": | ||
| 235 | var i = 0 | ||
| 236 | for item in message["items"]: | ||
| 237 | var index = int(message["index"] + i) | ||
| 238 | i += 1 | ||
| 239 | |||
| 240 | if _received_indexes.has(index): | ||
| 241 | # Do not re-process items. | ||
| 242 | continue | ||
| 243 | |||
| 244 | _received_indexes.append(index) | ||
| 245 | |||
| 246 | var item_id = int(item["item"]) | ||
| 247 | _received_items[item_id] = _received_items.get(item_id, 0) + 1 | ||
| 248 | |||
| 249 | emit_signal( | ||
| 250 | "item_received", | ||
| 251 | item_id, | ||
| 252 | index, | ||
| 253 | int(item["player"]), | ||
| 254 | int(item["flags"]), | ||
| 255 | _received_items[item_id] | ||
| 256 | ) | ||
| 257 | |||
| 258 | elif cmd == "PrintJSON": | ||
| 259 | emit_signal("message_received", message) | ||
| 260 | |||
| 261 | elif cmd == "LocationInfo": | ||
| 262 | for loc in message["locations"]: | ||
| 263 | emit_signal( | ||
| 264 | "location_scout_received", | ||
| 265 | int(loc["item"]), | ||
| 266 | int(loc["location"]), | ||
| 267 | int(loc["player"]), | ||
| 268 | int(loc["flags"]) | ||
| 269 | ) | ||
| 270 | |||
| 271 | elif state == WebSocketPeer.STATE_CLOSED: | ||
| 272 | if _has_connected: | ||
| 273 | _closed() | ||
| 274 | else: | ||
| 275 | _errored() | ||
| 276 | |||
| 277 | |||
| 278 | func saveDatapackages(): | ||
| 279 | # Save the AP datapackages to disk. | ||
| 280 | var file = FileAccess.open("user://ap_datapackages", FileAccess.WRITE) | ||
| 281 | file.store_var(_datapackages, true) | ||
| 282 | file.close() | ||
| 283 | |||
| 284 | |||
| 285 | func connectToServer(server, un, pw): | ||
| 286 | ap_server = server | ||
| 287 | ap_user = un | ||
| 288 | ap_pass = pw | ||
| 289 | |||
| 290 | _initiated_disconnect = false | ||
| 291 | |||
| 292 | var url = "" | ||
| 293 | if ap_server.begins_with("ws://") or ap_server.begins_with("wss://"): | ||
| 294 | url = ap_server | ||
| 295 | _try_wss = false | ||
| 296 | elif _try_wss: | ||
| 297 | url = "wss://" + ap_server | ||
| 298 | _try_wss = false | ||
| 299 | else: | ||
| 300 | url = "ws://" + ap_server | ||
| 301 | _try_wss = true | ||
| 302 | |||
| 303 | var err = _ws.connect_to_url(url) | ||
| 304 | if err != OK: | ||
| 305 | emit_signal( | ||
| 306 | "could_not_connect", | ||
| 307 | ( | ||
| 308 | "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information. Error code: %d." | ||
| 309 | % err | ||
| 310 | ) | ||
| 311 | ) | ||
| 312 | global._print("Could not connect to AP: " + err) | ||
| 313 | return | ||
| 314 | _should_process = true | ||
| 315 | |||
| 316 | emit_signal("connect_status", "Connecting...") | ||
| 317 | |||
| 318 | |||
| 319 | func sendMessage(msg): | ||
| 320 | var payload = JSON.stringify(msg) | ||
| 321 | _ws.send_text(payload) | ||
| 322 | |||
| 323 | |||
| 324 | func requestDatapackages(games): | ||
| 325 | emit_signal("connect_status", "Downloading %s data package..." % games[0]) | ||
| 326 | |||
| 327 | sendMessage([{"cmd": "GetDataPackage", "games": games}]) | ||
| 328 | |||
| 329 | |||
| 330 | func processDatapackages(): | ||
| 331 | _item_id_to_name = {} | ||
| 332 | _location_id_to_name = {} | ||
| 333 | for game in _datapackages.keys(): | ||
| 334 | var package = _datapackages[game] | ||
| 335 | |||
| 336 | _item_id_to_name[game] = {} | ||
| 337 | for item_name in package["item_name_to_id"].keys(): | ||
| 338 | _item_id_to_name[game][int(package["item_name_to_id"][item_name])] = item_name | ||
| 339 | |||
| 340 | _location_id_to_name[game] = {} | ||
| 341 | for location_name in package["location_name_to_id"].keys(): | ||
| 342 | _location_id_to_name[game][int(package["location_name_to_id"][location_name])] = location_name | ||
| 343 | |||
| 344 | if _datapackages.has("Lingo 2"): | ||
| 345 | _item_name_to_id = _datapackages["Lingo 2"]["item_name_to_id"] | ||
| 346 | _location_name_to_id = _datapackages["Lingo 2"]["location_name_to_id"] | ||
| 347 | |||
| 348 | |||
| 349 | func connectToRoom(): | ||
| 350 | emit_signal("connect_status", "Authenticating...") | ||
| 351 | |||
| 352 | sendMessage( | ||
| 353 | [ | ||
| 354 | { | ||
| 355 | "cmd": "Connect", | ||
| 356 | "password": ap_pass, | ||
| 357 | "game": "Lingo 2", | ||
| 358 | "name": ap_user, | ||
| 359 | "uuid": SCRIPT_uuid.v4(), | ||
| 360 | "version": ap_version, | ||
| 361 | "items_handling": 0b111, # always receive our items | ||
| 362 | "tags": [], | ||
| 363 | "slot_data": true | ||
| 364 | } | ||
| 365 | ] | ||
| 366 | ) | ||
| 367 | |||
| 368 | |||
| 369 | func sendConnectUpdate(tags): | ||
| 370 | sendMessage([{"cmd": "ConnectUpdate", "tags": tags}]) | ||
| 371 | |||
| 372 | |||
| 373 | func requestSync(): | ||
| 374 | sendMessage([{"cmd": "Sync"}]) | ||
| 375 | |||
| 376 | |||
| 377 | func sendLocation(loc_id): | ||
| 378 | sendMessage([{"cmd": "LocationChecks", "locations": [loc_id]}]) | ||
| 379 | |||
| 380 | |||
| 381 | func sendLocations(loc_ids): | ||
| 382 | sendMessage([{"cmd": "LocationChecks", "locations": loc_ids}]) | ||
| 383 | |||
| 384 | |||
| 385 | func setValue(key, value, operation = "replace"): | ||
| 386 | sendMessage( | ||
| 387 | [ | ||
| 388 | { | ||
| 389 | "cmd": "Set", | ||
| 390 | "key": "Lingo2_%d_%s" % [_slot, key], | ||
| 391 | "want_reply": false, | ||
| 392 | "operations": [{"operation": operation, "value": value}] | ||
| 393 | } | ||
| 394 | ] | ||
| 395 | ) | ||
| 396 | |||
| 397 | |||
| 398 | func say(textdata): | ||
| 399 | sendMessage([{"cmd": "Say", "text": textdata}]) | ||
| 400 | |||
| 401 | |||
| 402 | func completedGoal(): | ||
| 403 | sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL | ||
| 404 | |||
| 405 | |||
| 406 | func scoutLocations(loc_ids): | ||
| 407 | sendMessage([{"cmd": "LocationScouts", "locations": loc_ids}]) | ||
| 408 | |||
| 409 | |||
| 410 | func hasItem(item_id): | ||
| 411 | return _received_items.has(item_id) | ||
| 412 | |||
| 413 | |||
| 414 | func getItemAmount(item_id): | ||
| 415 | return _received_items.get(item_id, 0) | ||
| diff --git a/client/Archipelago/gamedata.gd b/client/Archipelago/gamedata.gd deleted file mode 100644 index f7a5d90..0000000 --- a/client/Archipelago/gamedata.gd +++ /dev/null | |||
| @@ -1,86 +0,0 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | var SCRIPT_proto | ||
| 4 | |||
| 5 | var objects | ||
| 6 | var door_id_by_map_node_path = {} | ||
| 7 | var painting_id_by_map_node_path = {} | ||
| 8 | var door_id_by_ap_id = {} | ||
| 9 | var map_id_by_name = {} | ||
| 10 | var progressive_id_by_ap_id = {} | ||
| 11 | var letter_id_by_ap_id = {} | ||
| 12 | |||
| 13 | |||
| 14 | func _init(proto_script): | ||
| 15 | SCRIPT_proto = proto_script | ||
| 16 | |||
| 17 | |||
| 18 | func load(data_bytes): | ||
| 19 | objects = SCRIPT_proto.AllObjects.new() | ||
| 20 | |||
| 21 | var result_code = objects.from_bytes(data_bytes) | ||
| 22 | if result_code != SCRIPT_proto.PB_ERR.NO_ERRORS: | ||
| 23 | print("Could not load generated data: %d" % result_code) | ||
| 24 | return | ||
| 25 | |||
| 26 | for map in objects.get_maps(): | ||
| 27 | map_id_by_name[map.get_name()] = map.get_id() | ||
| 28 | |||
| 29 | for door in objects.get_doors(): | ||
| 30 | var map = objects.get_maps()[door.get_map_id()] | ||
| 31 | |||
| 32 | if not map.get_name() in door_id_by_map_node_path: | ||
| 33 | door_id_by_map_node_path[map.get_name()] = {} | ||
| 34 | |||
| 35 | var map_data = door_id_by_map_node_path[map.get_name()] | ||
| 36 | for receiver in door.get_receivers(): | ||
| 37 | map_data[receiver] = door.get_id() | ||
| 38 | |||
| 39 | for painting_id in door.get_move_paintings(): | ||
| 40 | var painting = objects.get_paintings()[painting_id] | ||
| 41 | map_data[painting.get_path()] = door.get_id() | ||
| 42 | |||
| 43 | if door.has_ap_id(): | ||
| 44 | door_id_by_ap_id[door.get_ap_id()] = door.get_id() | ||
| 45 | |||
| 46 | for painting in objects.get_paintings(): | ||
| 47 | var room = objects.get_rooms()[painting.get_room_id()] | ||
| 48 | var map = objects.get_maps()[room.get_map_id()] | ||
| 49 | |||
| 50 | if not map.get_name() in painting_id_by_map_node_path: | ||
| 51 | painting_id_by_map_node_path[map.get_name()] = {} | ||
| 52 | |||
| 53 | var _map_data = painting_id_by_map_node_path[map.get_name()] | ||
| 54 | |||
| 55 | for progressive in objects.get_progressives(): | ||
| 56 | progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id() | ||
| 57 | |||
| 58 | for letter in objects.get_letters(): | ||
| 59 | letter_id_by_ap_id[letter.get_ap_id()] = letter.get_id() | ||
| 60 | |||
| 61 | |||
| 62 | func get_door_for_map_node_path(map_name, node_path): | ||
| 63 | if not door_id_by_map_node_path.has(map_name): | ||
| 64 | return null | ||
| 65 | |||
| 66 | var map_data = door_id_by_map_node_path[map_name] | ||
| 67 | return map_data.get(node_path, null) | ||
| 68 | |||
| 69 | |||
| 70 | func get_door_ap_id(door_id): | ||
| 71 | var door = objects.get_doors()[door_id] | ||
| 72 | if door.has_ap_id(): | ||
| 73 | return door.get_ap_id() | ||
| 74 | else: | ||
| 75 | return null | ||
| 76 | |||
| 77 | |||
| 78 | func get_door_receivers(door_id): | ||
| 79 | var door = objects.get_doors()[door_id] | ||
| 80 | return door.get_receivers() | ||
| 81 | |||
| 82 | |||
| 83 | func get_door_map_name(door_id): | ||
| 84 | var door = objects.get_doors()[door_id] | ||
| 85 | var map = objects.get_maps()[door.get_map_id()] | ||
| 86 | return map.get_name() | ||
| diff --git a/client/Archipelago/manager.gd b/client/Archipelago/manager.gd deleted file mode 100644 index 72abf34..0000000 --- a/client/Archipelago/manager.gd +++ /dev/null | |||
| @@ -1,473 +0,0 @@ | |||
| 1 | extends Node | ||
| 2 | |||
| 3 | const my_version = "0.1.0" | ||
| 4 | |||
| 5 | var SCRIPT_client | ||
| 6 | var SCRIPT_keyboard | ||
| 7 | var SCRIPT_locationListener | ||
| 8 | var SCRIPT_uuid | ||
| 9 | var SCRIPT_victoryListener | ||
| 10 | |||
| 11 | var ap_server = "" | ||
| 12 | var ap_user = "" | ||
| 13 | var ap_pass = "" | ||
| 14 | var connection_history = [] | ||
| 15 | |||
| 16 | var client | ||
| 17 | var keyboard | ||
| 18 | |||
| 19 | var _localdata_file = "" | ||
| 20 | var _last_new_item = -1 | ||
| 21 | var _batch_locations = false | ||
| 22 | var _held_locations = [] | ||
| 23 | var _held_location_scouts = [] | ||
| 24 | var _location_scouts = {} | ||
| 25 | var _item_locks = {} | ||
| 26 | var _held_letters = {} | ||
| 27 | var _letters_setup = false | ||
| 28 | |||
| 29 | const kSHUFFLE_LETTERS_VANILLA = 0 | ||
| 30 | const kSHUFFLE_LETTERS_UNLOCKED = 1 | ||
| 31 | const kSHUFFLE_LETTERS_PROGRESSIVE = 2 | ||
| 32 | const kSHUFFLE_LETTERS_VANILLA_CYAN = 3 | ||
| 33 | const kSHUFFLE_LETTERS_ITEM_CYAN = 4 | ||
| 34 | |||
| 35 | const kLETTER_BEHAVIOR_VANILLA = 0 | ||
| 36 | const kLETTER_BEHAVIOR_ITEM = 1 | ||
| 37 | const kLETTER_BEHAVIOR_UNLOCKED = 2 | ||
| 38 | |||
| 39 | var daedalus_roof_access = false | ||
| 40 | var keyholder_sanity = false | ||
| 41 | var shuffle_control_center_colors = false | ||
| 42 | var shuffle_doors = false | ||
| 43 | var shuffle_letters = kSHUFFLE_LETTERS_VANILLA | ||
| 44 | var victory_condition = -1 | ||
| 45 | |||
| 46 | signal could_not_connect | ||
| 47 | signal connect_status | ||
| 48 | signal ap_connected | ||
| 49 | |||
| 50 | |||
| 51 | func _init(): | ||
| 52 | # Read AP settings from file, if there are any | ||
| 53 | if FileAccess.file_exists("user://ap_settings"): | ||
| 54 | var file = FileAccess.open("user://ap_settings", FileAccess.READ) | ||
| 55 | var data = file.get_var(true) | ||
| 56 | file.close() | ||
| 57 | |||
| 58 | if typeof(data) != TYPE_ARRAY: | ||
| 59 | global._print("AP settings file is corrupted") | ||
| 60 | data = [] | ||
| 61 | |||
| 62 | if data.size() > 0: | ||
| 63 | ap_server = data[0] | ||
| 64 | |||
| 65 | if data.size() > 1: | ||
| 66 | ap_user = data[1] | ||
| 67 | |||
| 68 | if data.size() > 2: | ||
| 69 | ap_pass = data[2] | ||
| 70 | |||
| 71 | if data.size() > 3: | ||
| 72 | connection_history = data[3] | ||
| 73 | |||
| 74 | |||
| 75 | func _ready(): | ||
| 76 | client = SCRIPT_client.new() | ||
| 77 | client.SCRIPT_uuid = SCRIPT_uuid | ||
| 78 | |||
| 79 | client.connect("item_received", _process_item) | ||
| 80 | client.connect("message_received", _process_message) | ||
| 81 | client.connect("location_scout_received", _process_location_scout) | ||
| 82 | client.connect("could_not_connect", _client_could_not_connect) | ||
| 83 | client.connect("connect_status", _client_connect_status) | ||
| 84 | client.connect("client_connected", _client_connected) | ||
| 85 | |||
| 86 | add_child(client) | ||
| 87 | |||
| 88 | keyboard = SCRIPT_keyboard.new() | ||
| 89 | add_child(keyboard) | ||
| 90 | |||
| 91 | |||
| 92 | func saveSettings(): | ||
| 93 | # Save the AP settings to disk. | ||
| 94 | var path = "user://ap_settings" | ||
| 95 | var file = FileAccess.open(path, FileAccess.WRITE) | ||
| 96 | |||
| 97 | var data = [ | ||
| 98 | ap_server, | ||
| 99 | ap_user, | ||
| 100 | ap_pass, | ||
| 101 | connection_history, | ||
| 102 | ] | ||
| 103 | file.store_var(data, true) | ||
| 104 | file.close() | ||
| 105 | |||
| 106 | |||
| 107 | func saveLocaldata(): | ||
| 108 | # Save the MW/slot specific settings to disk. | ||
| 109 | var dir = DirAccess.open("user://") | ||
| 110 | var folder = "archipelago_data" | ||
| 111 | if not dir.dir_exists(folder): | ||
| 112 | dir.make_dir(folder) | ||
| 113 | |||
| 114 | var file = FileAccess.open(_localdata_file, FileAccess.WRITE) | ||
| 115 | |||
| 116 | var data = [ | ||
| 117 | _last_new_item, | ||
| 118 | ] | ||
| 119 | file.store_var(data, true) | ||
| 120 | file.close() | ||
| 121 | |||
| 122 | |||
| 123 | func connectToServer(): | ||
| 124 | _last_new_item = -1 | ||
| 125 | _letters_setup = false | ||
| 126 | _held_letters = {} | ||
| 127 | |||
| 128 | client.connectToServer(ap_server, ap_user, ap_pass) | ||
| 129 | |||
| 130 | |||
| 131 | func getSaveFileName(): | ||
| 132 | return "zzAP_%s_%d" % [client._seed, client._slot] | ||
| 133 | |||
| 134 | |||
| 135 | func disconnect_from_ap(): | ||
| 136 | client.disconnect_from_ap() | ||
| 137 | |||
| 138 | |||
| 139 | func get_item_id_for_door(door_id): | ||
| 140 | return _item_locks.get(door_id, null) | ||
| 141 | |||
| 142 | |||
| 143 | func _process_item(item, index, from, flags, amount): | ||
| 144 | var item_name = "Unknown" | ||
| 145 | if client._item_id_to_name["Lingo 2"].has(item): | ||
| 146 | item_name = client._item_id_to_name["Lingo 2"][item] | ||
| 147 | |||
| 148 | var gamedata = global.get_node("Gamedata") | ||
| 149 | var door_id = gamedata.door_id_by_ap_id.get(item, null) | ||
| 150 | var prog_id = null | ||
| 151 | |||
| 152 | if door_id == null: | ||
| 153 | prog_id = gamedata.progressive_id_by_ap_id.get(item, null) | ||
| 154 | if prog_id != null: | ||
| 155 | var progressive = gamedata.objects.get_progressives()[prog_id] | ||
| 156 | if progressive.get_doors().size() >= amount: | ||
| 157 | door_id = progressive.get_doors()[amount - 1] | ||
| 158 | |||
| 159 | if door_id != null and gamedata.get_door_map_name(door_id) == global.map: | ||
| 160 | var receivers = gamedata.get_door_receivers(door_id) | ||
| 161 | var scene = get_tree().get_root().get_node_or_null("scene") | ||
| 162 | if scene != null: | ||
| 163 | for receiver in receivers: | ||
| 164 | var rnode = scene.get_node_or_null(receiver) | ||
| 165 | if rnode != null: | ||
| 166 | rnode.handleTriggered() | ||
| 167 | |||
| 168 | var letter_id = gamedata.letter_id_by_ap_id.get(item, null) | ||
| 169 | if letter_id != null: | ||
| 170 | var letter = gamedata.objects.get_letters()[letter_id] | ||
| 171 | if not letter.has_level2() or not letter.get_level2(): | ||
| 172 | _process_key_item(letter.get_key(), amount) | ||
| 173 | |||
| 174 | # Show a message about the item if it's new. | ||
| 175 | if index != null and index > _last_new_item: | ||
| 176 | _last_new_item = index | ||
| 177 | saveLocaldata() | ||
| 178 | |||
| 179 | var player_name = "Unknown" | ||
| 180 | if client._player_name_by_slot.has(float(from)): | ||
| 181 | player_name = client._player_name_by_slot[float(from)] | ||
| 182 | |||
| 183 | var item_color = colorForItemType(flags) | ||
| 184 | |||
| 185 | var full_item_name = item_name | ||
| 186 | if prog_id != null and door_id != null: | ||
| 187 | var door = gamedata.objects.get_doors()[door_id] | ||
| 188 | full_item_name = "%s (%s)" % [item_name, door.get_name()] | ||
| 189 | |||
| 190 | var message | ||
| 191 | if from == client._slot: | ||
| 192 | message = "Found [color=%s]%s[/color]" % [item_color, full_item_name] | ||
| 193 | else: | ||
| 194 | message = ( | ||
| 195 | "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name] | ||
| 196 | ) | ||
| 197 | |||
| 198 | global._print(message) | ||
| 199 | |||
| 200 | global.get_node("Messages").showMessage(message) | ||
| 201 | |||
| 202 | |||
| 203 | func _process_message(message): | ||
| 204 | parse_printjson_for_textclient(message) | ||
| 205 | |||
| 206 | if ( | ||
| 207 | !message.has("receiving") | ||
| 208 | or !message.has("item") | ||
| 209 | or message["item"]["player"] != client._slot | ||
| 210 | ): | ||
| 211 | return | ||
| 212 | |||
| 213 | var item_name = "Unknown" | ||
| 214 | var item_player_game = client._game_by_player[message["receiving"]] | ||
| 215 | if client._item_id_to_name[item_player_game].has(int(message["item"]["item"])): | ||
| 216 | item_name = client._item_id_to_name[item_player_game][int(message["item"]["item"])] | ||
| 217 | |||
| 218 | var location_name = "Unknown" | ||
| 219 | var location_player_game = client._game_by_player[message["item"]["player"]] | ||
| 220 | if client._location_id_to_name[location_player_game].has(int(message["item"]["location"])): | ||
| 221 | location_name = (client._location_id_to_name[location_player_game][int( | ||
| 222 | message["item"]["location"] | ||
| 223 | )]) | ||
| 224 | |||
| 225 | var player_name = "Unknown" | ||
| 226 | if client._player_name_by_slot.has(message["receiving"]): | ||
| 227 | player_name = client._player_name_by_slot[message["receiving"]] | ||
| 228 | |||
| 229 | var item_color = colorForItemType(message["item"]["flags"]) | ||
| 230 | |||
| 231 | if message["type"] == "Hint": | ||
| 232 | var is_for = "" | ||
| 233 | if message["receiving"] != client._slot: | ||
| 234 | is_for = " for %s" % player_name | ||
| 235 | if !message.has("found") || !message["found"]: | ||
| 236 | global.get_node("Messages").showMessage( | ||
| 237 | ( | ||
| 238 | "Hint: [color=%s]%s[/color]%s is on %s" | ||
| 239 | % [item_color, item_name, is_for, location_name] | ||
| 240 | ) | ||
| 241 | ) | ||
| 242 | else: | ||
| 243 | if message["receiving"] != client._slot: | ||
| 244 | var sentMsg = "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name] | ||
| 245 | #if _hinted_locations.has(message["item"]["location"]): | ||
| 246 | # sentMsg += " ([color=#fafad2]Hinted![/color])" | ||
| 247 | global.get_node("Messages").showMessage(sentMsg) | ||
| 248 | |||
| 249 | |||
| 250 | func parse_printjson_for_textclient(message): | ||
| 251 | var parts = [] | ||
| 252 | for message_part in message["data"]: | ||
| 253 | if !message_part.has("type") and message_part.has("text"): | ||
| 254 | parts.append(message_part["text"]) | ||
| 255 | elif message_part["type"] == "player_id": | ||
| 256 | if int(message_part["text"]) == client._slot: | ||
| 257 | parts.append( | ||
| 258 | "[color=#ee00ee]%s[/color]" % client._player_name_by_slot[client._slot] | ||
| 259 | ) | ||
| 260 | else: | ||
| 261 | var from = float(message_part["text"]) | ||
| 262 | parts.append("[color=#fafad2]%s[/color]" % client._player_name_by_slot[from]) | ||
| 263 | elif message_part["type"] == "item_id": | ||
| 264 | var item_name = "Unknown" | ||
| 265 | var item_player_game = client._game_by_player[message_part["player"]] | ||
| 266 | if client._item_id_to_name[item_player_game].has(int(message_part["text"])): | ||
| 267 | item_name = client._item_id_to_name[item_player_game][int(message_part["text"])] | ||
| 268 | |||
| 269 | parts.append( | ||
| 270 | "[color=%s]%s[/color]" % [colorForItemType(message_part["flags"]), item_name] | ||
| 271 | ) | ||
| 272 | elif message_part["type"] == "location_id": | ||
| 273 | var location_name = "Unknown" | ||
| 274 | var location_player_game = client._game_by_player[message_part["player"]] | ||
| 275 | if client._location_id_to_name[location_player_game].has(int(message_part["text"])): | ||
| 276 | location_name = client._location_id_to_name[location_player_game][int( | ||
| 277 | message_part["text"] | ||
| 278 | )] | ||
| 279 | |||
| 280 | parts.append("[color=#00ff7f]%s[/color]" % location_name) | ||
| 281 | elif message_part.has("text"): | ||
| 282 | parts.append(message_part["text"]) | ||
| 283 | |||
| 284 | var textclient_node = global.get_node("Textclient") | ||
| 285 | if textclient_node != null: | ||
| 286 | textclient_node.parse_printjson("".join(parts)) | ||
| 287 | |||
| 288 | |||
| 289 | func _process_location_scout(item_id, location_id, player, flags): | ||
| 290 | _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags} | ||
| 291 | |||
| 292 | var gamedata = global.get_node("Gamedata") | ||
| 293 | var map_id = gamedata.map_id_by_name.get(global.map) | ||
| 294 | |||
| 295 | var item_name = "Unknown" | ||
| 296 | var item_player_game = client._game_by_player[float(player)] | ||
| 297 | if client._item_id_to_name[item_player_game].has(item_id): | ||
| 298 | item_name = client._item_id_to_name[item_player_game][item_id] | ||
| 299 | |||
| 300 | var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null) | ||
| 301 | if letter_id != null: | ||
| 302 | var letter = gamedata.objects.get_letters()[letter_id] | ||
| 303 | var room = gamedata.objects.get_rooms()[letter.get_room_id()] | ||
| 304 | if room.get_map_id() == map_id: | ||
| 305 | var collectable = get_tree().get_root().get_node("scene").get_node_or_null( | ||
| 306 | letter.get_path() | ||
| 307 | ) | ||
| 308 | if collectable != null: | ||
| 309 | collectable.setScoutedText(item_name) | ||
| 310 | |||
| 311 | |||
| 312 | func _client_could_not_connect(): | ||
| 313 | emit_signal("could_not_connect") | ||
| 314 | |||
| 315 | |||
| 316 | func _client_connect_status(message): | ||
| 317 | emit_signal("connect_status", message) | ||
| 318 | |||
| 319 | |||
| 320 | func _client_connected(slot_data): | ||
| 321 | var gamedata = global.get_node("Gamedata") | ||
| 322 | |||
| 323 | _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot] | ||
| 324 | _last_new_item = -1 | ||
| 325 | |||
| 326 | if FileAccess.file_exists(_localdata_file): | ||
| 327 | var ap_file = FileAccess.open(_localdata_file, FileAccess.READ) | ||
| 328 | var localdata = [] | ||
| 329 | if ap_file != null: | ||
| 330 | localdata = ap_file.get_var(true) | ||
| 331 | ap_file.close() | ||
| 332 | |||
| 333 | if typeof(localdata) != TYPE_ARRAY: | ||
| 334 | print("AP localdata file is corrupted") | ||
| 335 | localdata = [] | ||
| 336 | |||
| 337 | if localdata.size() > 0: | ||
| 338 | _last_new_item = localdata[0] | ||
| 339 | |||
| 340 | # Read slot data. | ||
| 341 | daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) | ||
| 342 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) | ||
| 343 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) | ||
| 344 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) | ||
| 345 | shuffle_letters = int(slot_data.get("shuffle_letters", 0)) | ||
| 346 | victory_condition = int(slot_data.get("victory_condition", 0)) | ||
| 347 | |||
| 348 | # Set up item locks. | ||
| 349 | _item_locks = {} | ||
| 350 | |||
| 351 | if shuffle_doors: | ||
| 352 | for door in gamedata.objects.get_doors(): | ||
| 353 | if ( | ||
| 354 | door.get_type() == gamedata.SCRIPT_proto.DoorType.STANDARD | ||
| 355 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY | ||
| 356 | ): | ||
| 357 | _item_locks[door.get_id()] = [door.get_ap_id(), 1] | ||
| 358 | |||
| 359 | for progressive in gamedata.objects.get_progressives(): | ||
| 360 | for i in range(0, progressive.get_doors().size()): | ||
| 361 | var door = gamedata.objects.get_doors()[progressive.get_doors()[i]] | ||
| 362 | _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1] | ||
| 363 | |||
| 364 | for door_group in gamedata.objects.get_door_groups(): | ||
| 365 | if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR: | ||
| 366 | for door in door_group.get_doors(): | ||
| 367 | _item_locks[door] = [door_group.get_ap_id(), 1] | ||
| 368 | |||
| 369 | if shuffle_control_center_colors: | ||
| 370 | for door in gamedata.objects.get_doors(): | ||
| 371 | if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR: | ||
| 372 | _item_locks[door.get_id()] = [door.get_ap_id(), 1] | ||
| 373 | |||
| 374 | for door_group in gamedata.objects.get_door_groups(): | ||
| 375 | if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR: | ||
| 376 | for door in door_group.get_doors(): | ||
| 377 | _item_locks[door] = [door_group.get_ap_id(), 1] | ||
| 378 | |||
| 379 | emit_signal("ap_connected") | ||
| 380 | |||
| 381 | |||
| 382 | func start_batching_locations(): | ||
| 383 | _batch_locations = true | ||
| 384 | |||
| 385 | |||
| 386 | func send_location(loc_id): | ||
| 387 | if _batch_locations: | ||
| 388 | _held_locations.append(loc_id) | ||
| 389 | else: | ||
| 390 | client.sendLocation(loc_id) | ||
| 391 | |||
| 392 | |||
| 393 | func scout_location(loc_id): | ||
| 394 | if _location_scouts.has(loc_id): | ||
| 395 | return _location_scouts.get(loc_id) | ||
| 396 | |||
| 397 | if _batch_locations: | ||
| 398 | _held_location_scouts.append(loc_id) | ||
| 399 | else: | ||
| 400 | client.scoutLocation(loc_id) | ||
| 401 | |||
| 402 | return null | ||
| 403 | |||
| 404 | |||
| 405 | func stop_batching_locations(): | ||
| 406 | _batch_locations = false | ||
| 407 | |||
| 408 | if not _held_locations.is_empty(): | ||
| 409 | client.sendLocations(_held_locations) | ||
| 410 | _held_locations.clear() | ||
| 411 | |||
| 412 | if not _held_location_scouts.is_empty(): | ||
| 413 | client.scoutLocations(_held_location_scouts) | ||
| 414 | _held_location_scouts.clear() | ||
| 415 | |||
| 416 | |||
| 417 | func colorForItemType(flags): | ||
| 418 | var int_flags = int(flags) | ||
| 419 | if int_flags & 1: # progression | ||
| 420 | if int_flags & 2: # proguseful | ||
| 421 | return "#f0d200" | ||
| 422 | else: | ||
| 423 | return "#bc51e0" | ||
| 424 | elif int_flags & 2: # useful | ||
| 425 | return "#2b67ff" | ||
| 426 | elif int_flags & 4: # trap | ||
| 427 | return "#d63a22" | ||
| 428 | else: # filler | ||
| 429 | return "#14de9e" | ||
| 430 | |||
| 431 | |||
| 432 | func get_letter_behavior(key, level2): | ||
| 433 | if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED: | ||
| 434 | return kLETTER_BEHAVIOR_UNLOCKED | ||
| 435 | |||
| 436 | if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters): | ||
| 437 | if level2: | ||
| 438 | if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN: | ||
| 439 | return kLETTER_BEHAVIOR_VANILLA | ||
| 440 | else: | ||
| 441 | return kLETTER_BEHAVIOR_ITEM | ||
| 442 | else: | ||
| 443 | return kLETTER_BEHAVIOR_UNLOCKED | ||
| 444 | |||
| 445 | if not level2 and ["h", "i", "n", "t"].has(key): | ||
| 446 | # This differs from the equivalent function in the apworld. Logically it is | ||
| 447 | # the same as UNLOCKED since they are in the starting room, but VANILLA | ||
| 448 | # means the player still has to actually pick up the letters. | ||
| 449 | return kLETTER_BEHAVIOR_VANILLA | ||
| 450 | |||
| 451 | if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE: | ||
| 452 | return kLETTER_BEHAVIOR_ITEM | ||
| 453 | |||
| 454 | return kLETTER_BEHAVIOR_VANILLA | ||
| 455 | |||
| 456 | |||
| 457 | func setup_keys(): | ||
| 458 | keyboard.load_seed() | ||
| 459 | |||
| 460 | _letters_setup = true | ||
| 461 | |||
| 462 | for k in _held_letters.keys(): | ||
| 463 | _process_key_item(k, _held_letters[k]) | ||
| 464 | |||
| 465 | _held_letters.clear() | ||
| 466 | |||
| 467 | |||
| 468 | func _process_key_item(key, level): | ||
| 469 | if not _letters_setup: | ||
| 470 | _held_letters[key] = max(_held_letters.get(key, 0), level) | ||
| 471 | return | ||
| 472 | |||
| 473 | keyboard.collect_remote_letter(key, level) | ||
| diff --git a/client/Archipelago/pauseMenu.gd b/client/Archipelago/pauseMenu.gd deleted file mode 100644 index 6c013a5..0000000 --- a/client/Archipelago/pauseMenu.gd +++ /dev/null | |||
| @@ -1,6 +0,0 @@ | |||
| 1 | extends "res://scripts/ui/pauseMenu.gd" | ||
| 2 | |||
| 3 | |||
| 4 | func _pause_game(): | ||
| 5 | global.get_node("Textclient").dismiss() | ||
| 6 | super._pause_game() | ||
| diff --git a/client/Archipelago/saver.gd b/client/Archipelago/saver.gd deleted file mode 100644 index 0fba9e7..0000000 --- a/client/Archipelago/saver.gd +++ /dev/null | |||
| @@ -1,9 +0,0 @@ | |||
| 1 | extends "res://scripts/nodes/saver.gd" | ||
| 2 | |||
| 3 | |||
| 4 | func levelLoaded(): | ||
| 5 | if type == "keyholders": | ||
| 6 | var ap = global.get_node("Archipelago") | ||
| 7 | ap.keyboard.load_keyholders.call_deferred(global.map) | ||
| 8 | else: | ||
| 9 | reload.call_deferred() | ||
| diff --git a/client/Archipelago/settings_buttons.gd b/client/Archipelago/settings_buttons.gd deleted file mode 100644 index 9e61cb0..0000000 --- a/client/Archipelago/settings_buttons.gd +++ /dev/null | |||
| @@ -1,24 +0,0 @@ | |||
| 1 | extends Button | ||
| 2 | |||
| 3 | |||
| 4 | func _ready(): | ||
| 5 | pass | ||
| 6 | |||
| 7 | |||
| 8 | func _connect_pressed(): | ||
| 9 | self.disabled = true | ||
| 10 | |||
| 11 | var ap = global.get_node("Archipelago") | ||
| 12 | ap.ap_server = self.get_parent().get_node("server_box").text | ||
| 13 | ap.ap_user = self.get_parent().get_node("player_box").text | ||
| 14 | ap.ap_pass = self.get_parent().get_node("password_box").text | ||
| 15 | ap.saveSettings() | ||
| 16 | |||
| 17 | ap.connectToServer() | ||
| 18 | |||
| 19 | |||
| 20 | func _back_pressed(): | ||
| 21 | var ap = global.get_node("Archipelago") | ||
| 22 | ap.disconnect_from_ap() | ||
| 23 | |||
| 24 | get_tree().change_scene_to_file("res://objects/scenes/menus/main_menu.tscn") | ||
| diff --git a/client/Archipelago/settings_screen.gd b/client/Archipelago/settings_screen.gd deleted file mode 100644 index aaaf72a..0000000 --- a/client/Archipelago/settings_screen.gd +++ /dev/null | |||
| @@ -1,199 +0,0 @@ | |||
| 1 | extends Node2D | ||
| 2 | |||
| 3 | |||
| 4 | func _ready(): | ||
| 5 | # Some helpful logging. | ||
| 6 | if Steam.isSubscribed(): | ||
| 7 | global._print("Provisioning successful! Build ID: %d" % Steam.getAppBuildId()) | ||
| 8 | else: | ||
| 9 | global._print("Provisioning failed.") | ||
| 10 | |||
| 11 | # Undo the load screen removing our cursor | ||
| 12 | get_tree().get_root().set_disable_input(false) | ||
| 13 | Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) | ||
| 14 | |||
| 15 | # Increase the WebSocket input buffer size so that we can download large | ||
| 16 | # data packages. | ||
| 17 | ProjectSettings.set_setting("network/limits/websocket_client/max_in_buffer_kb", 8192) | ||
| 18 | |||
| 19 | # Create the global AP manager, if it doesn't already exist. | ||
| 20 | if not global.has_node("Archipelago"): | ||
| 21 | var ap_script = ResourceLoader.load("user://maps/Archipelago/manager.gd") | ||
| 22 | var ap_instance = ap_script.new() | ||
| 23 | ap_instance.name = "Archipelago" | ||
| 24 | |||
| 25 | ap_instance.SCRIPT_client = load("user://maps/Archipelago/client.gd") | ||
| 26 | ap_instance.SCRIPT_keyboard = load("user://maps/Archipelago/keyboard.gd") | ||
| 27 | ap_instance.SCRIPT_locationListener = load("user://maps/Archipelago/locationListener.gd") | ||
| 28 | ap_instance.SCRIPT_uuid = load("user://maps/Archipelago/vendor/uuid.gd") | ||
| 29 | ap_instance.SCRIPT_victoryListener = load("user://maps/Archipelago/victoryListener.gd") | ||
| 30 | |||
| 31 | global.add_child(ap_instance) | ||
| 32 | |||
| 33 | # Let's also inject any scripts we need to inject now. | ||
| 34 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/animationListener.gd")) | ||
| 35 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/collectable.gd")) | ||
| 36 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/door.gd")) | ||
| 37 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/keyHolder.gd")) | ||
| 38 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/keyHolderChecker.gd")) | ||
| 39 | installScriptExtension( | ||
| 40 | ResourceLoader.load("user://maps/Archipelago/keyHolderResetterListener.gd") | ||
| 41 | ) | ||
| 42 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/painting.gd")) | ||
| 43 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pauseMenu.gd")) | ||
| 44 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd")) | ||
| 45 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/saver.gd")) | ||
| 46 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleportListener.gd")) | ||
| 47 | installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldportListener.gd")) | ||
| 48 | |||
| 49 | var proto_script = load("user://maps/Archipelago/generated/proto.gd") | ||
| 50 | var gamedata_script = load("user://maps/Archipelago/gamedata.gd") | ||
| 51 | var gamedata_instance = gamedata_script.new(proto_script) | ||
| 52 | gamedata_instance.load( | ||
| 53 | FileAccess.get_file_as_bytes("user://maps/Archipelago/generated/data.binpb") | ||
| 54 | ) | ||
| 55 | gamedata_instance.name = "Gamedata" | ||
| 56 | global.add_child(gamedata_instance) | ||
| 57 | |||
| 58 | var messages_script = load("user://maps/Archipelago/messages.gd") | ||
| 59 | var messages_instance = messages_script.new() | ||
| 60 | messages_instance.name = "Messages" | ||
| 61 | global.add_child(messages_instance) | ||
| 62 | |||
| 63 | var textclient_script = load("user://maps/Archipelago/textclient.gd") | ||
| 64 | var textclient_instance = textclient_script.new() | ||
| 65 | textclient_instance.name = "Textclient" | ||
| 66 | global.add_child(textclient_instance) | ||
| 67 | |||
| 68 | var ap = global.get_node("Archipelago") | ||
| 69 | ap.connect("ap_connected", connectionSuccessful) | ||
| 70 | ap.connect("could_not_connect", connectionUnsuccessful) | ||
| 71 | ap.connect("connect_status", connectionStatus) | ||
| 72 | |||
| 73 | # Populate textboxes with AP settings. | ||
| 74 | $Panel/server_box.text = ap.ap_server | ||
| 75 | $Panel/player_box.text = ap.ap_user | ||
| 76 | $Panel/password_box.text = ap.ap_pass | ||
| 77 | |||
| 78 | var history_box = $Panel/connection_history | ||
| 79 | if ap.connection_history.is_empty(): | ||
| 80 | history_box.disabled = true | ||
| 81 | else: | ||
| 82 | history_box.disabled = false | ||
| 83 | |||
| 84 | var i = 0 | ||
| 85 | for details in ap.connection_history: | ||
| 86 | history_box.get_popup().add_item("%s (%s)" % [details[1], details[0]], i) | ||
| 87 | i += 1 | ||
| 88 | |||
| 89 | history_box.get_popup().connect("id_pressed", historySelected) | ||
| 90 | |||
| 91 | # Show client version. | ||
| 92 | $Panel/title.text = "ARCHIPELAGO (%s)" % ap.my_version | ||
| 93 | |||
| 94 | # Increase font size in text boxes. | ||
| 95 | $Panel/server_box.add_theme_font_size_override("font_size", 36) | ||
| 96 | $Panel/player_box.add_theme_font_size_override("font_size", 36) | ||
| 97 | $Panel/password_box.add_theme_font_size_override("font_size", 36) | ||
| 98 | |||
| 99 | |||
| 100 | # Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd | ||
| 101 | func installScriptExtension(childScript: Resource): | ||
| 102 | # Force Godot to compile the script now. | ||
| 103 | # We need to do this here to ensure that the inheritance chain is | ||
| 104 | # properly set up, and multiple mods can chain-extend the same | ||
| 105 | # class multiple times. | ||
| 106 | # This is also needed to make Godot instantiate the extended class | ||
| 107 | # when creating singletons. | ||
| 108 | # The actual instance is thrown away. | ||
| 109 | childScript.new() | ||
| 110 | |||
| 111 | var parentScript = childScript.get_base_script() | ||
| 112 | var parentScriptPath = parentScript.resource_path | ||
| 113 | global._print("ModLoader: Installing script extension over %s" % parentScriptPath) | ||
| 114 | childScript.take_over_path(parentScriptPath) | ||
| 115 | |||
| 116 | |||
| 117 | func connectionStatus(message): | ||
| 118 | var popup = self.get_node("Panel/AcceptDialog") | ||
| 119 | popup.title = "Connecting to Archipelago" | ||
| 120 | popup.dialog_text = message | ||
| 121 | popup.exclusive = true | ||
| 122 | popup.get_ok_button().visible = false | ||
| 123 | popup.popup_centered() | ||
| 124 | |||
| 125 | |||
| 126 | func connectionSuccessful(): | ||
| 127 | var ap = global.get_node("Archipelago") | ||
| 128 | |||
| 129 | # Save connection details | ||
| 130 | var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass] | ||
| 131 | if ap.connection_history.has(connection_details): | ||
| 132 | ap.connection_history.erase(connection_details) | ||
| 133 | ap.connection_history.push_front(connection_details) | ||
| 134 | if ap.connection_history.size() > 10: | ||
| 135 | ap.connection_history.resize(10) | ||
| 136 | ap.saveSettings() | ||
| 137 | |||
| 138 | # Switch to the_entry | ||
| 139 | Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) | ||
| 140 | global.user = ap.getSaveFileName() | ||
| 141 | global.universe = "lingo" | ||
| 142 | global.map = "the_entry" | ||
| 143 | |||
| 144 | unlocks.resetCollectables() | ||
| 145 | unlocks.resetData() | ||
| 146 | |||
| 147 | ap.setup_keys() | ||
| 148 | |||
| 149 | unlocks.loadCollectables() | ||
| 150 | unlocks.loadData() | ||
| 151 | unlocks.unlockKey("capslock", 1) | ||
| 152 | |||
| 153 | clearResourceCache("res://objects/meshes/gridDoor.tscn") | ||
| 154 | clearResourceCache("res://objects/nodes/collectable.tscn") | ||
| 155 | clearResourceCache("res://objects/nodes/door.tscn") | ||
| 156 | clearResourceCache("res://objects/nodes/keyHolder.tscn") | ||
| 157 | clearResourceCache("res://objects/nodes/listeners/animationListener.tscn") | ||
| 158 | clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn") | ||
| 159 | clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn") | ||
| 160 | clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn") | ||
| 161 | clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn") | ||
| 162 | clearResourceCache("res://objects/nodes/player.tscn") | ||
| 163 | clearResourceCache("res://objects/nodes/saver.tscn") | ||
| 164 | clearResourceCache("res://objects/scenes/menus/pause_menu.tscn") | ||
| 165 | |||
| 166 | var paintings_dir = DirAccess.open("res://objects/meshes/paintings") | ||
| 167 | if paintings_dir: | ||
| 168 | paintings_dir.list_dir_begin() | ||
| 169 | var file_name = paintings_dir.get_next() | ||
| 170 | while file_name != "": | ||
| 171 | if not paintings_dir.current_is_dir() and file_name.ends_with(".tscn"): | ||
| 172 | clearResourceCache("res://objects/meshes/paintings/" + file_name) | ||
| 173 | file_name = paintings_dir.get_next() | ||
| 174 | |||
| 175 | switcher.switch_map.call_deferred("res://objects/scenes/the_entry.tscn") | ||
| 176 | |||
| 177 | |||
| 178 | func connectionUnsuccessful(error_message): | ||
| 179 | $Panel/connect_button.disabled = false | ||
| 180 | |||
| 181 | var popup = $Panel/AcceptDialog | ||
| 182 | popup.title = "Could not connect to Archipelago" | ||
| 183 | popup.dialog_text = error_message | ||
| 184 | popup.exclusive = true | ||
| 185 | popup.get_ok_button().visible = true | ||
| 186 | popup.popup_centered() | ||
| 187 | |||
| 188 | |||
| 189 | func historySelected(index): | ||
| 190 | var ap = global.get_node("Archipelago") | ||
| 191 | var details = ap.connection_history[index] | ||
| 192 | |||
| 193 | $Panel/server_box.text = details[0] | ||
| 194 | $Panel/player_box.text = details[1] | ||
| 195 | $Panel/password_box.text = details[2] | ||
| 196 | |||
| 197 | |||
| 198 | func clearResourceCache(path): | ||
| 199 | ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_REPLACE) | ||
| diff --git a/client/Archipelago/textclient.gd b/client/Archipelago/textclient.gd deleted file mode 100644 index 4b03151..0000000 --- a/client/Archipelago/textclient.gd +++ /dev/null | |||
| @@ -1,86 +0,0 @@ | |||
| 1 | extends CanvasLayer | ||
| 2 | |||
| 3 | var panel | ||
| 4 | var label | ||
| 5 | var entry | ||
| 6 | var is_open = false | ||
| 7 | |||
| 8 | |||
| 9 | func _ready(): | ||
| 10 | process_mode = ProcessMode.PROCESS_MODE_ALWAYS | ||
| 11 | |||
| 12 | panel = Panel.new() | ||
| 13 | panel.set_name("Panel") | ||
| 14 | panel.offset_left = 100 | ||
| 15 | panel.offset_right = 1820 | ||
| 16 | panel.offset_top = 100 | ||
| 17 | panel.offset_bottom = 980 | ||
| 18 | panel.visible = false | ||
| 19 | add_child(panel) | ||
| 20 | |||
| 21 | label = RichTextLabel.new() | ||
| 22 | label.set_name("Label") | ||
| 23 | label.offset_left = 80 | ||
| 24 | label.offset_right = 1640 | ||
| 25 | label.offset_top = 80 | ||
| 26 | label.offset_bottom = 720 | ||
| 27 | label.scroll_following = true | ||
| 28 | label.selection_enabled = true | ||
| 29 | panel.add_child(label) | ||
| 30 | |||
| 31 | label.push_font(load("res://assets/fonts/Lingo2.ttf")) | ||
| 32 | label.push_font_size(36) | ||
| 33 | |||
| 34 | var entry_style = StyleBoxFlat.new() | ||
| 35 | entry_style.bg_color = Color(0.9, 0.9, 0.9, 1) | ||
| 36 | |||
| 37 | entry = LineEdit.new() | ||
| 38 | entry.set_name("Entry") | ||
| 39 | entry.offset_left = 80 | ||
| 40 | entry.offset_right = 1640 | ||
| 41 | entry.offset_top = 760 | ||
| 42 | entry.offset_bottom = 840 | ||
| 43 | entry.add_theme_font_override("font", load("res://assets/fonts/Lingo2.ttf")) | ||
| 44 | entry.add_theme_font_size_override("font_size", 36) | ||
| 45 | entry.add_theme_color_override("font_color", Color(0, 0, 0, 1)) | ||
| 46 | entry.add_theme_color_override("cursor_color", Color(0, 0, 0, 1)) | ||
| 47 | entry.add_theme_stylebox_override("focus", entry_style) | ||
| 48 | panel.add_child(entry) | ||
| 49 | entry.connect("text_submitted", text_entered) | ||
| 50 | |||
| 51 | |||
| 52 | func _input(event): | ||
| 53 | if event is InputEventKey and event.pressed: | ||
| 54 | if event.keycode == KEY_TAB and !Input.is_key_pressed(KEY_SHIFT): | ||
| 55 | if !get_tree().paused: | ||
| 56 | is_open = true | ||
| 57 | get_tree().paused = true | ||
| 58 | Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) | ||
| 59 | panel.visible = true | ||
| 60 | entry.grab_focus() | ||
| 61 | get_viewport().set_input_as_handled() | ||
| 62 | else: | ||
| 63 | dismiss() | ||
| 64 | elif event.keycode == KEY_ESCAPE: | ||
| 65 | if is_open: | ||
| 66 | dismiss() | ||
| 67 | get_viewport().set_input_as_handled() | ||
| 68 | |||
| 69 | |||
| 70 | func dismiss(): | ||
| 71 | if is_open: | ||
| 72 | get_tree().paused = false | ||
| 73 | Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) | ||
| 74 | panel.visible = false | ||
| 75 | is_open = false | ||
| 76 | |||
| 77 | |||
| 78 | func parse_printjson(text): | ||
| 79 | label.append_text("[p]" + text + "[/p]") | ||
| 80 | |||
| 81 | |||
| 82 | func text_entered(text): | ||
| 83 | var ap = global.get_node("Archipelago") | ||
| 84 | var cmd = text.trim_suffix("\n") | ||
| 85 | ap.client.say(cmd) | ||
| 86 | entry.text = "" | ||
| diff --git a/client/Archipelago/vendor/LICENSE b/client/Archipelago/vendor/LICENSE deleted file mode 100644 index 115ba15..0000000 --- a/client/Archipelago/vendor/LICENSE +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | MIT License | ||
| 2 | |||
| 3 | Copyright (c) 2023 Xavier Sellier | ||
| 4 | |||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 6 | of this software and associated documentation files (the "Software"), to deal | ||
| 7 | in the Software without restriction, including without limitation the rights | ||
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 9 | copies of the Software, and to permit persons to whom the Software is | ||
| 10 | furnished to do so, subject to the following conditions: | ||
| 11 | |||
| 12 | The above copyright notice and this permission notice shall be included in all | ||
| 13 | copies or substantial portions of the Software. | ||
| 14 | |||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 21 | SOFTWARE. \ No newline at end of file | ||
| diff --git a/client/Archipelago/vendor/uuid.gd b/client/Archipelago/vendor/uuid.gd deleted file mode 100644 index b63fa04..0000000 --- a/client/Archipelago/vendor/uuid.gd +++ /dev/null | |||
| @@ -1,195 +0,0 @@ | |||
| 1 | # Note: The code might not be as pretty it could be, since it's written | ||
| 2 | # in a way that maximizes performance. Methods are inlined and loops are avoided. | ||
| 3 | extends Node | ||
| 4 | |||
| 5 | const BYTE_MASK: int = 0b11111111 | ||
| 6 | |||
| 7 | |||
| 8 | static func uuidbin(): | ||
| 9 | randomize() | ||
| 10 | # 16 random bytes with the bytes on index 6 and 8 modified | ||
| 11 | return [ | ||
| 12 | randi() & BYTE_MASK, | ||
| 13 | randi() & BYTE_MASK, | ||
| 14 | randi() & BYTE_MASK, | ||
| 15 | randi() & BYTE_MASK, | ||
| 16 | randi() & BYTE_MASK, | ||
| 17 | randi() & BYTE_MASK, | ||
| 18 | ((randi() & BYTE_MASK) & 0x0f) | 0x40, | ||
| 19 | randi() & BYTE_MASK, | ||
| 20 | ((randi() & BYTE_MASK) & 0x3f) | 0x80, | ||
| 21 | randi() & BYTE_MASK, | ||
| 22 | randi() & BYTE_MASK, | ||
| 23 | randi() & BYTE_MASK, | ||
| 24 | randi() & BYTE_MASK, | ||
| 25 | randi() & BYTE_MASK, | ||
| 26 | randi() & BYTE_MASK, | ||
| 27 | randi() & BYTE_MASK, | ||
| 28 | ] | ||
| 29 | |||
| 30 | |||
| 31 | static func uuidbinrng(rng: RandomNumberGenerator): | ||
| 32 | rng.randomize() | ||
| 33 | return [ | ||
| 34 | rng.randi() & BYTE_MASK, | ||
| 35 | rng.randi() & BYTE_MASK, | ||
| 36 | rng.randi() & BYTE_MASK, | ||
| 37 | rng.randi() & BYTE_MASK, | ||
| 38 | rng.randi() & BYTE_MASK, | ||
| 39 | rng.randi() & BYTE_MASK, | ||
| 40 | ((rng.randi() & BYTE_MASK) & 0x0f) | 0x40, | ||
| 41 | rng.randi() & BYTE_MASK, | ||
| 42 | ((rng.randi() & BYTE_MASK) & 0x3f) | 0x80, | ||
| 43 | rng.randi() & BYTE_MASK, | ||
| 44 | rng.randi() & BYTE_MASK, | ||
| 45 | rng.randi() & BYTE_MASK, | ||
| 46 | rng.randi() & BYTE_MASK, | ||
| 47 | rng.randi() & BYTE_MASK, | ||
| 48 | rng.randi() & BYTE_MASK, | ||
| 49 | rng.randi() & BYTE_MASK, | ||
| 50 | ] | ||
| 51 | |||
| 52 | |||
| 53 | static func v4(): | ||
| 54 | # 16 random bytes with the bytes on index 6 and 8 modified | ||
| 55 | var b = uuidbin() | ||
| 56 | |||
| 57 | return ( | ||
| 58 | "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" | ||
| 59 | % [ | ||
| 60 | # low | ||
| 61 | b[0], | ||
| 62 | b[1], | ||
| 63 | b[2], | ||
| 64 | b[3], | ||
| 65 | # mid | ||
| 66 | b[4], | ||
| 67 | b[5], | ||
| 68 | # hi | ||
| 69 | b[6], | ||
| 70 | b[7], | ||
| 71 | # clock | ||
| 72 | b[8], | ||
| 73 | b[9], | ||
| 74 | # clock | ||
| 75 | b[10], | ||
| 76 | b[11], | ||
| 77 | b[12], | ||
| 78 | b[13], | ||
| 79 | b[14], | ||
| 80 | b[15] | ||
| 81 | ] | ||
| 82 | ) | ||
| 83 | |||
| 84 | |||
| 85 | static func v4_rng(rng: RandomNumberGenerator): | ||
| 86 | # 16 random bytes with the bytes on index 6 and 8 modified | ||
| 87 | var b = uuidbinrng(rng) | ||
| 88 | |||
| 89 | return ( | ||
| 90 | "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" | ||
| 91 | % [ | ||
| 92 | # low | ||
| 93 | b[0], | ||
| 94 | b[1], | ||
| 95 | b[2], | ||
| 96 | b[3], | ||
| 97 | # mid | ||
| 98 | b[4], | ||
| 99 | b[5], | ||
| 100 | # hi | ||
| 101 | b[6], | ||
| 102 | b[7], | ||
| 103 | # clock | ||
| 104 | b[8], | ||
| 105 | b[9], | ||
| 106 | # clock | ||
| 107 | b[10], | ||
| 108 | b[11], | ||
| 109 | b[12], | ||
| 110 | b[13], | ||
| 111 | b[14], | ||
| 112 | b[15] | ||
| 113 | ] | ||
| 114 | ) | ||
| 115 | |||
| 116 | |||
| 117 | var _uuid: Array | ||
| 118 | |||
| 119 | |||
| 120 | func _init(rng := RandomNumberGenerator.new()) -> void: | ||
| 121 | _uuid = uuidbinrng(rng) | ||
| 122 | |||
| 123 | |||
| 124 | func as_array() -> Array: | ||
| 125 | return _uuid.duplicate() | ||
| 126 | |||
| 127 | |||
| 128 | func as_dict(big_endian := true) -> Dictionary: | ||
| 129 | if big_endian: | ||
| 130 | return { | ||
| 131 | "low": (_uuid[0] << 24) + (_uuid[1] << 16) + (_uuid[2] << 8) + _uuid[3], | ||
| 132 | "mid": (_uuid[4] << 8) + _uuid[5], | ||
| 133 | "hi": (_uuid[6] << 8) + _uuid[7], | ||
| 134 | "clock": (_uuid[8] << 8) + _uuid[9], | ||
| 135 | "node": | ||
| 136 | ( | ||
| 137 | (_uuid[10] << 40) | ||
| 138 | + (_uuid[11] << 32) | ||
| 139 | + (_uuid[12] << 24) | ||
| 140 | + (_uuid[13] << 16) | ||
| 141 | + (_uuid[14] << 8) | ||
| 142 | + _uuid[15] | ||
| 143 | ) | ||
| 144 | } | ||
| 145 | else: | ||
| 146 | return { | ||
| 147 | "low": _uuid[0] + (_uuid[1] << 8) + (_uuid[2] << 16) + (_uuid[3] << 24), | ||
| 148 | "mid": _uuid[4] + (_uuid[5] << 8), | ||
| 149 | "hi": _uuid[6] + (_uuid[7] << 8), | ||
| 150 | "clock": _uuid[8] + (_uuid[9] << 8), | ||
| 151 | "node": | ||
| 152 | ( | ||
| 153 | _uuid[10] | ||
| 154 | + (_uuid[11] << 8) | ||
| 155 | + (_uuid[12] << 16) | ||
| 156 | + (_uuid[13] << 24) | ||
| 157 | + (_uuid[14] << 32) | ||
| 158 | + (_uuid[15] << 40) | ||
| 159 | ) | ||
| 160 | } | ||
| 161 | |||
| 162 | |||
| 163 | func as_string() -> String: | ||
| 164 | return ( | ||
| 165 | "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" | ||
| 166 | % [ | ||
| 167 | # low | ||
| 168 | _uuid[0], | ||
| 169 | _uuid[1], | ||
| 170 | _uuid[2], | ||
| 171 | _uuid[3], | ||
| 172 | # mid | ||
| 173 | _uuid[4], | ||
| 174 | _uuid[5], | ||
| 175 | # hi | ||
| 176 | _uuid[6], | ||
| 177 | _uuid[7], | ||
| 178 | # clock | ||
| 179 | _uuid[8], | ||
| 180 | _uuid[9], | ||
| 181 | # node | ||
| 182 | _uuid[10], | ||
| 183 | _uuid[11], | ||
| 184 | _uuid[12], | ||
| 185 | _uuid[13], | ||
| 186 | _uuid[14], | ||
| 187 | _uuid[15] | ||
| 188 | ] | ||
| 189 | ) | ||
| 190 | |||
| 191 | |||
| 192 | func is_equal(other) -> bool: | ||
| 193 | # Godot Engine compares Array recursively | ||
| 194 | # There's no need for custom comparison here. | ||
| 195 | return _uuid == other._uuid | ||
| diff --git a/client/Archipelago/worldportListener.gd b/client/Archipelago/worldportListener.gd deleted file mode 100644 index c31c825..0000000 --- a/client/Archipelago/worldportListener.gd +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | extends "res://scripts/nodes/listeners/worldportListener.gd" | ||
| 2 | |||
| 3 | |||
| 4 | func changeScene(): | ||
| 5 | if exit == "menus/credits": | ||
| 6 | return | ||
| 7 | |||
| 8 | super.changeScene() | ||
| diff --git a/client/archipelago.tscn b/client/archipelago.tscn index 40dd46f..1c156a3 100644 --- a/client/archipelago.tscn +++ b/client/archipelago.tscn | |||
| @@ -1,162 +1,153 @@ | |||
| 1 | [gd_scene load_steps=11 format=2] | 1 | [gd_scene load_steps=3 format=3 uid="uid://b5mj3cq2bcesd"] |
| 2 | 2 | ||
| 3 | [ext_resource path="user://maps/Archipelago/settings_buttons.gd" type="Script" id=4] | 3 | [ext_resource type="Theme" uid="uid://7w454egydi41" path="res://assets/themes/baseUI.tres" id="1_mw3f1"] |
| 4 | [ext_resource path="user://maps/Archipelago/settings_screen.gd" type="Script" id=5] | 4 | |
| 5 | [ext_resource path="res://images/unchecked.png" type="Texture" id=7] | 5 | [sub_resource id=2 type="GDScript"] |
| 6 | [ext_resource path="res://images/checked.png" type="Texture" id=8] | 6 | script/source = "extends Node2D |
| 7 | [ext_resource type="Theme" uid="uid://7w454egydi41" path="res://assets/themes/baseUI.tres" id="2_g4bvn"] | 7 | |
| 8 | 8 | const CACHE_PATH = \"user://apworld_path.txt\" | |
| 9 | [sub_resource type="StyleBoxFlat" id=1] | 9 | |
| 10 | bg_color = Color( 0, 0, 0, 0 ) | 10 | |
| 11 | 11 | func _ready(): | |
| 12 | [sub_resource type="StyleBoxFlat" id=2] | 12 | if FileAccess.file_exists(CACHE_PATH): |
| 13 | bg_color = Color( 1, 1, 1, 1 ) | 13 | var file = FileAccess.open(CACHE_PATH, FileAccess.READ) |
| 14 | border_width_left = 1 | 14 | $Panel/HBoxContainer/LineEdit.text = file.get_as_text() |
| 15 | border_width_top = 1 | 15 | file.close() |
| 16 | border_width_right = 1 | 16 | |
| 17 | border_width_bottom = 1 | 17 | |
| 18 | border_color = Color( 1, 1, 0, 1 ) | 18 | func _browse_pressed(): |
| 19 | border_blend = true | 19 | $FileDialog.popup_centered() |
| 20 | corner_radius_top_left = 3 | 20 | |
| 21 | corner_radius_top_right = 3 | 21 | |
| 22 | corner_radius_bottom_right = 3 | 22 | func _file_selected(path): |
| 23 | corner_radius_bottom_left = 3 | 23 | $Panel/HBoxContainer/LineEdit.text = path |
| 24 | expand_offset_left = 5.0 | 24 | |
| 25 | expand_offset_right = 5.0 | 25 | |
| 26 | expand_offset_top = 5.0 | 26 | func _start_pressed(): |
| 27 | expand_offset_bottom = 5.0 | 27 | var apworld_path = $Panel/HBoxContainer/LineEdit.text |
| 28 | 28 | ||
| 29 | [node name="settings_screen" type="Node2D"] | 29 | if not FileAccess.file_exists(apworld_path): |
| 30 | script = ExtResource( 5 ) | 30 | $AcceptDialog.popup_centered() |
| 31 | return | ||
| 32 | |||
| 33 | var zip_reader = ZIPReader.new() | ||
| 34 | zip_reader.open(apworld_path) | ||
| 35 | |||
| 36 | var inner_path = \"lingo2/client/apworld_runtime.gd\" | ||
| 37 | if not zip_reader.file_exists(inner_path): | ||
| 38 | zip_reader.close() | ||
| 39 | $AcceptDialog.popup_centered() | ||
| 40 | return | ||
| 41 | |||
| 42 | var cache_file = FileAccess.open(CACHE_PATH, FileAccess.WRITE) | ||
| 43 | cache_file.store_string(apworld_path) | ||
| 44 | cache_file.close() | ||
| 45 | |||
| 46 | var runtime_script = GDScript.new() | ||
| 47 | runtime_script.source_code = zip_reader.read_file(inner_path).get_string_from_utf8() | ||
| 48 | runtime_script.reload() | ||
| 49 | |||
| 50 | zip_reader.close() | ||
| 51 | |||
| 52 | var runtime = runtime_script.new(apworld_path) | ||
| 53 | runtime.name = \"Runtime\" | ||
| 54 | |||
| 55 | global.add_child(runtime) | ||
| 56 | |||
| 57 | runtime.load_script_as_scene.call_deferred(\"settings_screen.gd\", \"settings_screen\") | ||
| 58 | |||
| 59 | |||
| 60 | func _quit_pressed(): | ||
| 61 | get_tree().change_scene_to_file(\"res://objects/scenes/menus/main_menu.tscn\") | ||
| 62 | |||
| 63 | " | ||
| 64 | |||
| 65 | [node name="Node2D" type="Node2D"] | ||
| 66 | script = SubResource( 2 ) | ||
| 31 | 67 | ||
| 32 | [node name="Panel" type="Panel" parent="."] | 68 | [node name="Panel" type="Panel" parent="."] |
| 69 | anchors_preset = -1 | ||
| 33 | offset_right = 1920.0 | 70 | offset_right = 1920.0 |
| 34 | offset_bottom = 1080.0 | 71 | offset_bottom = 1080.0 |
| 35 | 72 | ||
| 36 | [node name="title" parent="Panel" type="Label"] | 73 | [node name="Label" type="Label" parent="Panel"] |
| 37 | offset_left = 0.0 | 74 | layout_mode = 1 |
| 75 | anchors_preset = -1 | ||
| 38 | offset_top = 75.0 | 76 | offset_top = 75.0 |
| 39 | offset_right = 1920.0 | 77 | offset_right = 1920.0 |
| 40 | offset_bottom = 225.0 | 78 | offset_bottom = 225.0 |
| 41 | text = "ARCHIPELAGO" | 79 | theme = ExtResource("1_mw3f1") |
| 42 | valign = 1 | 80 | text = "archipelago" |
| 43 | theme = ExtResource("2_g4bvn") | 81 | horizontal_alignment = 1 |
| 44 | 82 | vertical_alignment = 1 | |
| 45 | [node name="credit" parent="Panel" type="Label"] | 83 | |
| 46 | visible = false | 84 | [node name="Label2" type="Label" parent="Panel"] |
| 47 | offset_left = 1278.0 | 85 | layout_mode = 1 |
| 48 | offset_top = 974.0 | 86 | anchors_preset = -1 |
| 49 | offset_right = 1868.0 | 87 | anchor_right = 1.0 |
| 50 | offset_bottom = 1034.0 | 88 | offset_left = 80.0 |
| 51 | text = "Brenton Wildes" | 89 | offset_top = 300.0 |
| 52 | theme = ExtResource("2_g4bvn") | 90 | offset_right = -80.0 |
| 53 | 91 | offset_bottom = 388.0 | |
| 54 | [node name="connect_button" parent="Panel" type="Button"] | 92 | theme = ExtResource("1_mw3f1") |
| 93 | theme_override_font_sizes/font_size = 56 | ||
| 94 | text = "Put the path to your lingo2.apworld in the below field and click start. Then, open the archipelago launcher and click \"Lingo 2 Client\"." | ||
| 95 | horizontal_alignment = 1 | ||
| 96 | autowrap_mode = 3 | ||
| 97 | |||
| 98 | [node name="HBoxContainer" type="HBoxContainer" parent="Panel"] | ||
| 99 | layout_mode = 1 | ||
| 100 | anchors_preset = -1 | ||
| 101 | anchor_right = 1.0 | ||
| 102 | offset_left = 80.0 | ||
| 103 | offset_top = 595.0 | ||
| 104 | offset_right = -80.0 | ||
| 105 | offset_bottom = 755.0 | ||
| 106 | theme_override_constants/separation = 32 | ||
| 107 | |||
| 108 | [node name="LineEdit" type="LineEdit" parent="Panel/HBoxContainer"] | ||
| 109 | layout_mode = 2 | ||
| 110 | size_flags_horizontal = 3 | ||
| 111 | |||
| 112 | [node name="Button" type="Button" parent="Panel/HBoxContainer"] | ||
| 113 | layout_mode = 2 | ||
| 114 | theme = ExtResource("1_mw3f1") | ||
| 115 | text = "browse" | ||
| 116 | |||
| 117 | [node name="StartButton" type="Button" parent="Panel"] | ||
| 118 | layout_mode = 1 | ||
| 119 | anchors_preset = -1 | ||
| 55 | offset_left = 255.0 | 120 | offset_left = 255.0 |
| 56 | offset_top = 875.0 | 121 | offset_top = 875.0 |
| 57 | offset_right = 891.0 | 122 | offset_right = 891.0 |
| 58 | offset_bottom = 1025.0 | 123 | offset_bottom = 1025.0 |
| 59 | custom_colors/font_color_hover = Color( 1, 0.501961, 0, 1 ) | 124 | theme = ExtResource("1_mw3f1") |
| 60 | text = "CONNECT" | 125 | text = "start" |
| 61 | theme = ExtResource("2_g4bvn") | ||
| 62 | script = ExtResource( 4 ) | ||
| 63 | 126 | ||
| 64 | [node name="quit_button" parent="Panel" type="Button"] | 127 | [node name="QuitButton" type="Button" parent="Panel"] |
| 128 | layout_mode = 1 | ||
| 129 | anchors_preset = -1 | ||
| 65 | offset_left = 1102.0 | 130 | offset_left = 1102.0 |
| 66 | offset_top = 875.0 | 131 | offset_top = 875.0 |
| 67 | offset_right = 1738.0 | 132 | offset_right = 1738.0 |
| 68 | offset_bottom = 1025.0 | 133 | offset_bottom = 1025.0 |
| 69 | custom_colors/font_color_hover = Color( 1, 0, 0, 1 ) | 134 | theme = ExtResource("1_mw3f1") |
| 70 | text = "BACK" | 135 | text = "back" |
| 71 | theme = ExtResource("2_g4bvn") | 136 | |
| 72 | script = ExtResource( 4 ) | 137 | [node name="FileDialog" type="FileDialog" parent="."] |
| 73 | 138 | title = "Open a File" | |
| 74 | [node name="credit2" parent="Panel" type="Label"] | 139 | size = Vector2i(512, 512) |
| 75 | offset_left = -105.0 | 140 | ok_button_text = "Open" |
| 76 | offset_top = 346.0 | 141 | file_mode = 0 |
| 77 | offset_right = 485.0 | 142 | access = 2 |
| 78 | offset_bottom = 410.0 | 143 | filters = PackedStringArray("*.apworld;Archipelago Worlds") |
| 79 | custom_styles/normal = SubResource( 1 ) | 144 | show_hidden_files = true |
| 80 | text = "SERVER" | 145 | use_native_dialog = true |
| 81 | align = 2 | 146 | |
| 82 | theme = ExtResource("2_g4bvn") | 147 | [node name="AcceptDialog" type="AcceptDialog" parent="."] |
| 83 | 148 | dialog_text = "Could not open Lingo 2 apworld. Please check that the path is correct." | |
| 84 | [node name="credit5" parent="Panel" type="Label"] | 149 | |
| 85 | offset_left = 1239.0 | 150 | [connection signal="pressed" from="Panel/HBoxContainer/Button" to="." method="_browse_pressed"] |
| 86 | offset_top = 422.0 | 151 | [connection signal="pressed" from="Panel/StartButton" to="." method="_start_pressed"] |
| 87 | offset_right = 1829.0 | 152 | [connection signal="pressed" from="Panel/QuitButton" to="." method="_quit_pressed"] |
| 88 | offset_bottom = 486.0 | 153 | [connection signal="file_selected" from="FileDialog" to="." method="_file_selected"] |
| 89 | custom_styles/normal = SubResource( 1 ) | ||
| 90 | text = "OPTIONS" | ||
| 91 | theme = ExtResource("2_g4bvn") | ||
| 92 | |||
| 93 | [node name="credit3" parent="Panel" type="Label"] | ||
| 94 | offset_left = -105.0 | ||
| 95 | offset_top = 519.0 | ||
| 96 | offset_right = 485.0 | ||
| 97 | offset_bottom = 583.0 | ||
| 98 | custom_styles/normal = SubResource( 1 ) | ||
| 99 | text = "PLAYER" | ||
| 100 | align = 2 | ||
| 101 | theme = ExtResource("2_g4bvn") | ||
| 102 | |||
| 103 | [node name="credit4" parent="Panel" type="Label"] | ||
| 104 | offset_left = -105.0 | ||
| 105 | offset_top = 704.0 | ||
| 106 | offset_right = 485.0 | ||
| 107 | offset_bottom = 768.0 | ||
| 108 | custom_styles/normal = SubResource( 1 ) | ||
| 109 | text = "PASSWORD" | ||
| 110 | align = 2 | ||
| 111 | theme = ExtResource("2_g4bvn") | ||
| 112 | |||
| 113 | [node name="server_box" type="LineEdit" parent="Panel"] | ||
| 114 | offset_left = 502.0 | ||
| 115 | offset_top = 295.0 | ||
| 116 | offset_right = 1144.0 | ||
| 117 | offset_bottom = 445.0 | ||
| 118 | custom_colors/selection_color = Color( 0.482353, 0, 0, 1 ) | ||
| 119 | custom_colors/cursor_color = Color( 0, 0, 0, 1 ) | ||
| 120 | custom_colors/font_color = Color( 0, 0, 0, 1 ) | ||
| 121 | custom_styles/focus = SubResource( 2 ) | ||
| 122 | align = 1 | ||
| 123 | caret_blink = true | ||
| 124 | |||
| 125 | [node name="player_box" type="LineEdit" parent="Panel"] | ||
| 126 | offset_left = 502.0 | ||
| 127 | offset_top = 477.0 | ||
| 128 | offset_right = 1144.0 | ||
| 129 | offset_bottom = 627.0 | ||
| 130 | custom_colors/selection_color = Color( 0.482353, 0, 0, 1 ) | ||
| 131 | custom_colors/cursor_color = Color( 0, 0, 0, 1 ) | ||
| 132 | custom_colors/font_color = Color( 0, 0, 0, 1 ) | ||
| 133 | custom_styles/focus = SubResource( 2 ) | ||
| 134 | align = 1 | ||
| 135 | caret_blink = true | ||
| 136 | |||
| 137 | [node name="password_box" type="LineEdit" parent="Panel"] | ||
| 138 | offset_left = 502.0 | ||
| 139 | offset_top = 659.0 | ||
| 140 | offset_right = 1144.0 | ||
| 141 | offset_bottom = 809.0 | ||
| 142 | custom_colors/selection_color = Color( 0.482353, 0, 0, 1 ) | ||
| 143 | custom_colors/cursor_color = Color( 0, 0, 0, 1 ) | ||
| 144 | custom_colors/font_color = Color( 0, 0, 0, 1 ) | ||
| 145 | custom_styles/focus = SubResource( 2 ) | ||
| 146 | align = 1 | ||
| 147 | caret_blink = true | ||
| 148 | |||
| 149 | [node name="AcceptDialog" type="AcceptDialog" parent="Panel"] | ||
| 150 | offset_right = 83.0 | ||
| 151 | offset_bottom = 58.0 | ||
| 152 | |||
| 153 | [node name="connection_history" type="MenuButton" parent="Panel"] | ||
| 154 | offset_left = 1239.0 | ||
| 155 | offset_top = 276.0 | ||
| 156 | offset_right = 1829.0 | ||
| 157 | offset_bottom = 372.0 | ||
| 158 | text = "connection history" | ||
| 159 | flat = false | ||
| 160 | |||
| 161 | [connection signal="pressed" from="Panel/connect_button" to="Panel/connect_button" method="_connect_pressed"] | ||
| 162 | [connection signal="pressed" from="Panel/quit_button" to="Panel/quit_button" method="_back_pressed"] | ||
| diff --git a/data/MISSING PANELS.txt b/data/MISSING PANELS.txt new file mode 100644 index 0000000..478b8be --- /dev/null +++ b/data/MISSING PANELS.txt | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | Used in vanilla doors: | ||
| 2 | |||
| 3 | The Between - RIGHT | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | Used in a pseudo-connection: | ||
| 9 | |||
| 10 | The Sturdy - COLORS | ||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | Unsolved panels in letter rooms: | ||
| 16 | |||
| 17 | Daedalus (F2 Room) - DEADLY | ||
| 18 | Daedalus (F2 Room) - DIMS | ||
| 19 | Daedalus (F2 Room) - GRAVE | ||
| 20 | Daedalus (F2 Room) - LETHAL | ||
| 21 | Daedalus (O2 Room) - ACCEPT | ||
| 22 | Daedalus (O2 Room) - FOLLOW | ||
| 23 | Daedalus (O2 Room) - PLEDGE | ||
| 24 | Daedalus (O2 Room) - WARNING | ||
| 25 | Daedalus (U2 Room) - EFFECTIVE | ||
| 26 | Daedalus (U2 Room) - HELPFUL | ||
| 27 | Daedalus (U2 Room) - INFERNAL | ||
| 28 | Daedalus (U2 Room) - PRACTICAL | ||
| 29 | Daedalus (U2 Room) - PRODUCTIVE | ||
| 30 | Daedalus (U2 Room) - WONDERLAND | ||
| 31 | The Digital - EYE | ||
| 32 | The Digital - HIGH | ||
| diff --git a/data/README.md b/data/README.md new file mode 100644 index 0000000..bf0a51b --- /dev/null +++ b/data/README.md | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # Lingo 2 Randomizer Data | ||
| 2 | |||
| 3 | This folder contains the logic for the Lingo 2 randomizer in a human-readable | ||
| 4 | format. This data is compiled into a single file and used in the various parts | ||
| 5 | of the randomizer project (client, apworld, etc). | ||
| 6 | |||
| 7 | The data is structured using [Protocol Buffers](https://protobuf.dev/). The | ||
| 8 | schema for the human-readable format is | ||
| 9 | [located in the repository](https://code.fourisland.com/lingo2-archipelago/tree/proto/human.proto). | ||
| 10 | |||
| 11 | ## Compiling | ||
| 12 | |||
| 13 | Hi. | ||
| diff --git a/data/connections.txtpb b/data/connections.txtpb index a79778f..8d75dab 100644 --- a/data/connections.txtpb +++ b/data/connections.txtpb | |||
| @@ -1,5 +1,3 @@ | |||
| 1 | # TODO | ||
| 2 | # daedalus/roof -> icarus | ||
| 3 | connections { | 1 | connections { |
| 4 | from { | 2 | from { |
| 5 | port { | 3 | port { |
| @@ -20,7 +18,7 @@ connections { | |||
| 20 | from { | 18 | from { |
| 21 | port { | 19 | port { |
| 22 | map: "the_entry" | 20 | map: "the_entry" |
| 23 | room: "Flipped Second Room" | 21 | room: "Four Rooms Entrance" |
| 24 | name: "FOUR" | 22 | name: "FOUR" |
| 25 | } | 23 | } |
| 26 | } | 24 | } |
| @@ -141,7 +139,7 @@ connections { | |||
| 141 | to { | 139 | to { |
| 142 | port { | 140 | port { |
| 143 | map: "the_darkroom" | 141 | map: "the_darkroom" |
| 144 | room: "First Room" | 142 | room: "Cyan Hallway" |
| 145 | name: "COLORFUL" | 143 | name: "COLORFUL" |
| 146 | } | 144 | } |
| 147 | } | 145 | } |
| @@ -157,7 +155,7 @@ connections { | |||
| 157 | to { | 155 | to { |
| 158 | port { | 156 | port { |
| 159 | map: "the_darkroom" | 157 | map: "the_darkroom" |
| 160 | room: "Second Room" | 158 | room: "Congruent Entrance" |
| 161 | name: "CONGRUENT" | 159 | name: "CONGRUENT" |
| 162 | } | 160 | } |
| 163 | } | 161 | } |
| @@ -233,7 +231,7 @@ connections { | |||
| 233 | from { | 231 | from { |
| 234 | port { | 232 | port { |
| 235 | map: "the_darkroom" | 233 | map: "the_darkroom" |
| 236 | room: "First Room" | 234 | room: "Double Sided Entrance" |
| 237 | name: "DOUBLESIDED" | 235 | name: "DOUBLESIDED" |
| 238 | } | 236 | } |
| 239 | } | 237 | } |
| @@ -308,6 +306,23 @@ connections { | |||
| 308 | name: "GALLERY" | 306 | name: "GALLERY" |
| 309 | } | 307 | } |
| 310 | } | 308 | } |
| 309 | oneway: true | ||
| 310 | } | ||
| 311 | connections { | ||
| 312 | from { | ||
| 313 | port { | ||
| 314 | map: "the_butterfly" | ||
| 315 | room: "Main Area" | ||
| 316 | name: "GALLERY" | ||
| 317 | } | ||
| 318 | } | ||
| 319 | to { | ||
| 320 | room { | ||
| 321 | map: "the_gallery" | ||
| 322 | name: "Main Area" | ||
| 323 | } | ||
| 324 | } | ||
| 325 | oneway: true | ||
| 311 | } | 326 | } |
| 312 | connections { | 327 | connections { |
| 313 | from { | 328 | from { |
| @@ -618,7 +633,7 @@ connections { | |||
| 618 | from { | 633 | from { |
| 619 | port { | 634 | port { |
| 620 | map: "the_entry" | 635 | map: "the_entry" |
| 621 | room: "Link Area" | 636 | room: "Liberated Entrance" |
| 622 | name: "BLUE" | 637 | name: "BLUE" |
| 623 | } | 638 | } |
| 624 | } | 639 | } |
| @@ -666,7 +681,7 @@ connections { | |||
| 666 | from { | 681 | from { |
| 667 | port { | 682 | port { |
| 668 | map: "the_entry" | 683 | map: "the_entry" |
| 669 | room: "Link Area" | 684 | room: "Literate Entrance" |
| 670 | name: "BROWN" | 685 | name: "BROWN" |
| 671 | } | 686 | } |
| 672 | } | 687 | } |
| @@ -714,7 +729,7 @@ connections { | |||
| 714 | from { | 729 | from { |
| 715 | port { | 730 | port { |
| 716 | map: "the_orb" | 731 | map: "the_orb" |
| 717 | room: "B Room" | 732 | room: "Middle Room" |
| 718 | name: "MID" | 733 | name: "MID" |
| 719 | } | 734 | } |
| 720 | } | 735 | } |
| @@ -841,6 +856,8 @@ connections { | |||
| 841 | } | 856 | } |
| 842 | oneway: true | 857 | oneway: true |
| 843 | } | 858 | } |
| 859 | # Two one-way connections because the CLUE panel only needs to be solved to | ||
| 860 | # go from The Great to The Partial. | ||
| 844 | connections { | 861 | connections { |
| 845 | from { | 862 | from { |
| 846 | port { | 863 | port { |
| @@ -856,6 +873,25 @@ connections { | |||
| 856 | name: "GREAT" | 873 | name: "GREAT" |
| 857 | } | 874 | } |
| 858 | } | 875 | } |
| 876 | oneway: true | ||
| 877 | } | ||
| 878 | connections { | ||
| 879 | from { | ||
| 880 | port { | ||
| 881 | map: "the_partial" | ||
| 882 | room: "Obverse Side" | ||
| 883 | name: "GREAT" | ||
| 884 | } | ||
| 885 | } | ||
| 886 | to { | ||
| 887 | port { | ||
| 888 | map: "the_great" | ||
| 889 | room: "West Side" | ||
| 890 | name: "PARTIAL" | ||
| 891 | } | ||
| 892 | } | ||
| 893 | oneway: true | ||
| 894 | bypass_target_door: true | ||
| 859 | } | 895 | } |
| 860 | connections { | 896 | connections { |
| 861 | from { | 897 | from { |
| @@ -925,7 +961,7 @@ connections { | |||
| 925 | from { | 961 | from { |
| 926 | port { | 962 | port { |
| 927 | map: "the_entry" | 963 | map: "the_entry" |
| 928 | room: "Lime Room" | 964 | room: "Revitalized Entrance" |
| 929 | name: "REVITALIZED" | 965 | name: "REVITALIZED" |
| 930 | } | 966 | } |
| 931 | } | 967 | } |
| @@ -1435,7 +1471,6 @@ connections { | |||
| 1435 | name: "GREAT" | 1471 | name: "GREAT" |
| 1436 | } | 1472 | } |
| 1437 | } | 1473 | } |
| 1438 | door { map: "the_great" name: "Daedalus Entrance" } | ||
| 1439 | oneway: true | 1474 | oneway: true |
| 1440 | } | 1475 | } |
| 1441 | connections { | 1476 | connections { |
| @@ -1454,6 +1489,7 @@ connections { | |||
| 1454 | } | 1489 | } |
| 1455 | } | 1490 | } |
| 1456 | oneway: true | 1491 | oneway: true |
| 1492 | bypass_target_door: true | ||
| 1457 | } | 1493 | } |
| 1458 | connections { | 1494 | connections { |
| 1459 | from { | 1495 | from { |
| @@ -1526,6 +1562,23 @@ connections { | |||
| 1526 | painting { | 1562 | painting { |
| 1527 | map: "the_sturdy" | 1563 | map: "the_sturdy" |
| 1528 | room: "S2 Area" | 1564 | room: "S2 Area" |
| 1565 | name: "RAINBOW2" | ||
| 1566 | } | ||
| 1567 | } | ||
| 1568 | to { | ||
| 1569 | painting { | ||
| 1570 | map: "daedalus" | ||
| 1571 | room: "Rainbow Start" | ||
| 1572 | name: "RAINBOW" | ||
| 1573 | } | ||
| 1574 | } | ||
| 1575 | oneway: true | ||
| 1576 | } | ||
| 1577 | connections { | ||
| 1578 | from { | ||
| 1579 | painting { | ||
| 1580 | map: "the_sturdy" | ||
| 1581 | room: "Hidden Rainbow" | ||
| 1529 | name: "RAINBOW" | 1582 | name: "RAINBOW" |
| 1530 | } | 1583 | } |
| 1531 | } | 1584 | } |
| @@ -1749,12 +1802,13 @@ connections { | |||
| 1749 | } | 1802 | } |
| 1750 | } | 1803 | } |
| 1751 | oneway: true | 1804 | oneway: true |
| 1805 | bypass_target_door: true | ||
| 1752 | } | 1806 | } |
| 1753 | connections { | 1807 | connections { |
| 1754 | from { | 1808 | from { |
| 1755 | port { | 1809 | port { |
| 1756 | map: "the_bearer" | 1810 | map: "the_bearer" |
| 1757 | room: "Back Area" | 1811 | room: "Tree Entrance" |
| 1758 | name: "TREE" | 1812 | name: "TREE" |
| 1759 | } | 1813 | } |
| 1760 | } | 1814 | } |
| @@ -1831,7 +1885,6 @@ connections { | |||
| 1831 | } | 1885 | } |
| 1832 | } | 1886 | } |
| 1833 | connections { | 1887 | connections { |
| 1834 | # Two one-way connections because the door only blocks one direction. | ||
| 1835 | from { | 1888 | from { |
| 1836 | port { | 1889 | port { |
| 1837 | map: "the_great" | 1890 | map: "the_great" |
| @@ -1848,6 +1901,7 @@ connections { | |||
| 1848 | } | 1901 | } |
| 1849 | } | 1902 | } |
| 1850 | connections { | 1903 | connections { |
| 1904 | # Two one-way connections because the door only blocks one direction. | ||
| 1851 | from { | 1905 | from { |
| 1852 | port { | 1906 | port { |
| 1853 | map: "the_unkempt" | 1907 | map: "the_unkempt" |
| @@ -1880,6 +1934,7 @@ connections { | |||
| 1880 | } | 1934 | } |
| 1881 | } | 1935 | } |
| 1882 | oneway: true | 1936 | oneway: true |
| 1937 | bypass_target_door: true | ||
| 1883 | } | 1938 | } |
| 1884 | connections { | 1939 | connections { |
| 1885 | from { | 1940 | from { |
| @@ -2405,3 +2460,281 @@ connections { | |||
| 2405 | } | 2460 | } |
| 2406 | } | 2461 | } |
| 2407 | } | 2462 | } |
| 2463 | connections { | ||
| 2464 | from { | ||
| 2465 | painting { | ||
| 2466 | map: "the_entry" | ||
| 2467 | room: "Eye Room" | ||
| 2468 | name: "GALLERY" | ||
| 2469 | } | ||
| 2470 | } | ||
| 2471 | to { | ||
| 2472 | room { | ||
| 2473 | map: "the_gallery" | ||
| 2474 | name: "Main Area" | ||
| 2475 | } | ||
| 2476 | } | ||
| 2477 | oneway: true | ||
| 2478 | } | ||
| 2479 | connections { | ||
| 2480 | from { | ||
| 2481 | room { | ||
| 2482 | map: "the_sun_temple" | ||
| 2483 | name: "Temple" | ||
| 2484 | } | ||
| 2485 | } | ||
| 2486 | to { | ||
| 2487 | room { | ||
| 2488 | map: "the_graveyard" | ||
| 2489 | name: "Outside" | ||
| 2490 | } | ||
| 2491 | } | ||
| 2492 | oneway: true | ||
| 2493 | } | ||
| 2494 | connections { | ||
| 2495 | from { | ||
| 2496 | room { | ||
| 2497 | map: "daedalus" | ||
| 2498 | name: "Roof" | ||
| 2499 | } | ||
| 2500 | } | ||
| 2501 | to { | ||
| 2502 | port { | ||
| 2503 | map: "icarus" | ||
| 2504 | room: "Welcome Spine (Obverse)" | ||
| 2505 | name: "WORLDPORT" | ||
| 2506 | } | ||
| 2507 | } | ||
| 2508 | } | ||
| 2509 | connections { | ||
| 2510 | from { | ||
| 2511 | panel { | ||
| 2512 | map: "the_entry" | ||
| 2513 | room: "Starting Room" | ||
| 2514 | name: "Gift Maps" | ||
| 2515 | answer: "icely" | ||
| 2516 | } | ||
| 2517 | } | ||
| 2518 | to { | ||
| 2519 | room { | ||
| 2520 | map: "the_advanced" | ||
| 2521 | name: "Main Area" | ||
| 2522 | } | ||
| 2523 | } | ||
| 2524 | oneway: true | ||
| 2525 | } | ||
| 2526 | connections { | ||
| 2527 | from { | ||
| 2528 | port { | ||
| 2529 | map: "the_advanced" | ||
| 2530 | room: "Main Area" | ||
| 2531 | name: "WORLDPORT" | ||
| 2532 | } | ||
| 2533 | } | ||
| 2534 | to { | ||
| 2535 | room { | ||
| 2536 | map: "the_entry" | ||
| 2537 | name: "Starting Room" | ||
| 2538 | } | ||
| 2539 | } | ||
| 2540 | oneway: true | ||
| 2541 | } | ||
| 2542 | connections { | ||
| 2543 | from { | ||
| 2544 | panel { | ||
| 2545 | map: "the_entry" | ||
| 2546 | room: "Starting Room" | ||
| 2547 | name: "Gift Maps" | ||
| 2548 | answer: "souvey" | ||
| 2549 | } | ||
| 2550 | } | ||
| 2551 | to { | ||
| 2552 | room { | ||
| 2553 | map: "the_charismatic" | ||
| 2554 | name: "Main Area" | ||
| 2555 | } | ||
| 2556 | } | ||
| 2557 | oneway: true | ||
| 2558 | } | ||
| 2559 | connections { | ||
| 2560 | from { | ||
| 2561 | port { | ||
| 2562 | map: "the_charismatic" | ||
| 2563 | room: "Main Area" | ||
| 2564 | name: "WORLDPORT" | ||
| 2565 | } | ||
| 2566 | } | ||
| 2567 | to { | ||
| 2568 | room { | ||
| 2569 | map: "the_entry" | ||
| 2570 | name: "Starting Room" | ||
| 2571 | } | ||
| 2572 | } | ||
| 2573 | oneway: true | ||
| 2574 | } | ||
| 2575 | connections { | ||
| 2576 | from { | ||
| 2577 | panel { | ||
| 2578 | map: "the_entry" | ||
| 2579 | room: "Starting Room" | ||
| 2580 | name: "Gift Maps" | ||
| 2581 | answer: "q" | ||
| 2582 | } | ||
| 2583 | } | ||
| 2584 | to { | ||
| 2585 | room { | ||
| 2586 | map: "the_crystalline" | ||
| 2587 | name: "Main Area" | ||
| 2588 | } | ||
| 2589 | } | ||
| 2590 | oneway: true | ||
| 2591 | } | ||
| 2592 | connections { | ||
| 2593 | from { | ||
| 2594 | port { | ||
| 2595 | map: "the_crystalline" | ||
| 2596 | room: "Main Area" | ||
| 2597 | name: "WORLDPORT" | ||
| 2598 | } | ||
| 2599 | } | ||
| 2600 | to { | ||
| 2601 | room { | ||
| 2602 | map: "the_entry" | ||
| 2603 | name: "Starting Room" | ||
| 2604 | } | ||
| 2605 | } | ||
| 2606 | oneway: true | ||
| 2607 | } | ||
| 2608 | connections { | ||
| 2609 | # Possibly the most cursed connection in the entire game. | ||
| 2610 | from { | ||
| 2611 | room { | ||
| 2612 | map: "the_crystalline" | ||
| 2613 | name: "Flip Area" | ||
| 2614 | } | ||
| 2615 | } | ||
| 2616 | to { | ||
| 2617 | room { | ||
| 2618 | map: "icarus" | ||
| 2619 | name: "Welcome Spine (Obverse)" | ||
| 2620 | } | ||
| 2621 | } | ||
| 2622 | oneway: true | ||
| 2623 | } | ||
| 2624 | connections { | ||
| 2625 | from { | ||
| 2626 | panel { | ||
| 2627 | map: "the_entry" | ||
| 2628 | room: "Starting Room" | ||
| 2629 | name: "Gift Maps" | ||
| 2630 | answer: "hatkirby" | ||
| 2631 | } | ||
| 2632 | } | ||
| 2633 | to { | ||
| 2634 | room { | ||
| 2635 | map: "the_stellar" | ||
| 2636 | name: "Starting Room" | ||
| 2637 | } | ||
| 2638 | } | ||
| 2639 | oneway: true | ||
| 2640 | } | ||
| 2641 | connections { | ||
| 2642 | from { | ||
| 2643 | panel { | ||
| 2644 | map: "the_entry" | ||
| 2645 | room: "Starting Room" | ||
| 2646 | name: "Gift Maps" | ||
| 2647 | answer: "kirby" | ||
| 2648 | } | ||
| 2649 | } | ||
| 2650 | to { | ||
| 2651 | room { | ||
| 2652 | map: "the_stellar" | ||
| 2653 | name: "Starting Room" | ||
| 2654 | } | ||
| 2655 | } | ||
| 2656 | oneway: true | ||
| 2657 | } | ||
| 2658 | connections { | ||
| 2659 | from { | ||
| 2660 | panel { | ||
| 2661 | map: "the_entry" | ||
| 2662 | room: "Starting Room" | ||
| 2663 | name: "Gift Maps" | ||
| 2664 | answer: "star" | ||
| 2665 | } | ||
| 2666 | } | ||
| 2667 | to { | ||
| 2668 | room { | ||
| 2669 | map: "the_stellar" | ||
| 2670 | name: "Starting Room" | ||
| 2671 | } | ||
| 2672 | } | ||
| 2673 | oneway: true | ||
| 2674 | } | ||
| 2675 | connections { | ||
| 2676 | from { | ||
| 2677 | port { | ||
| 2678 | map: "the_stellar" | ||
| 2679 | room: "Starting Room" | ||
| 2680 | name: "WORLDPORT" | ||
| 2681 | } | ||
| 2682 | } | ||
| 2683 | to { | ||
| 2684 | room { | ||
| 2685 | map: "the_entry" | ||
| 2686 | name: "Starting Room" | ||
| 2687 | } | ||
| 2688 | } | ||
| 2689 | oneway: true | ||
| 2690 | } | ||
| 2691 | connections { | ||
| 2692 | from { | ||
| 2693 | panel { | ||
| 2694 | map: "the_entry" | ||
| 2695 | room: "Starting Room" | ||
| 2696 | name: "Gift Maps" | ||
| 2697 | answer: "gongus" | ||
| 2698 | } | ||
| 2699 | } | ||
| 2700 | to { | ||
| 2701 | room { | ||
| 2702 | map: "the_fuzzy" | ||
| 2703 | name: "Main Area" | ||
| 2704 | } | ||
| 2705 | } | ||
| 2706 | oneway: true | ||
| 2707 | } | ||
| 2708 | connections { | ||
| 2709 | from { | ||
| 2710 | panel { | ||
| 2711 | map: "the_entry" | ||
| 2712 | room: "Starting Room" | ||
| 2713 | name: "Gift Maps" | ||
| 2714 | answer: "kiwi" | ||
| 2715 | } | ||
| 2716 | } | ||
| 2717 | to { | ||
| 2718 | room { | ||
| 2719 | map: "the_fuzzy" | ||
| 2720 | name: "Main Area" | ||
| 2721 | } | ||
| 2722 | } | ||
| 2723 | oneway: true | ||
| 2724 | } | ||
| 2725 | connections { | ||
| 2726 | from { | ||
| 2727 | port { | ||
| 2728 | map: "the_fuzzy" | ||
| 2729 | room: "Main Area" | ||
| 2730 | name: "WORLDPORT" | ||
| 2731 | } | ||
| 2732 | } | ||
| 2733 | to { | ||
| 2734 | room { | ||
| 2735 | map: "the_entry" | ||
| 2736 | name: "Starting Room" | ||
| 2737 | } | ||
| 2738 | } | ||
| 2739 | oneway: true | ||
| 2740 | } | ||
| diff --git a/data/door_groups.txtpb b/data/door_groups.txtpb index bc8cdf6..fab75f5 100644 --- a/data/door_groups.txtpb +++ b/data/door_groups.txtpb | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | door_groups { | 1 | door_groups { |
| 2 | name: "The Entry - Repetitive Entrance" | 2 | name: "The Entry - Repetitive Entrance" |
| 3 | type: CONNECTOR | 3 | # This *should* be a CONNECTOR, but currently we're not shuffling these |
| 4 | # entrances because we want to guarantee that there's a way to The Repetitive | ||
| 5 | # without missing keys in vanilla doors. Hopefully can be changed eventually. | ||
| 6 | type: SHUFFLE_GROUP | ||
| 4 | doors { | 7 | doors { |
| 5 | map: "the_entry" | 8 | map: "the_entry" |
| 6 | name: "Starting Room West Wall North Door" | 9 | name: "Starting Room West Wall North Door" |
| @@ -107,6 +110,10 @@ door_groups { | |||
| 107 | } | 110 | } |
| 108 | doors { | 111 | doors { |
| 109 | map: "the_entry" | 112 | map: "the_entry" |
| 113 | name: "Gift Maps Entrance" | ||
| 114 | } | ||
| 115 | doors { | ||
| 116 | map: "the_entry" | ||
| 110 | name: "Near D Room Painting" | 117 | name: "Near D Room Painting" |
| 111 | } | 118 | } |
| 112 | doors { | 119 | doors { |
| @@ -118,6 +125,14 @@ door_groups { | |||
| 118 | name: "Tower Entrance" | 125 | name: "Tower Entrance" |
| 119 | } | 126 | } |
| 120 | doors { | 127 | doors { |
| 128 | map: "the_great" | ||
| 129 | name: "Cyan Doors" | ||
| 130 | } | ||
| 131 | doors { | ||
| 132 | map: "the_owl" | ||
| 133 | name: "Double Letters" | ||
| 134 | } | ||
| 135 | doors { | ||
| 121 | map: "the_parthenon" | 136 | map: "the_parthenon" |
| 122 | name: "Double Letters" | 137 | name: "Double Letters" |
| 123 | } | 138 | } |
| @@ -134,3 +149,23 @@ door_groups { | |||
| 134 | name: "Cyan Doors" | 149 | name: "Cyan Doors" |
| 135 | } | 150 | } |
| 136 | } | 151 | } |
| 152 | door_groups { | ||
| 153 | name: "Lavender Cubes" | ||
| 154 | type: SHUFFLE_GROUP | ||
| 155 | doors { | ||
| 156 | map: "daedalus" | ||
| 157 | name: "C Keyholder Blocker" | ||
| 158 | } | ||
| 159 | doors { | ||
| 160 | map: "the_congruent" | ||
| 161 | name: "T Keyholder Blocker" | ||
| 162 | } | ||
| 163 | doors { | ||
| 164 | map: "the_great" | ||
| 165 | name: "Lavender Cube" | ||
| 166 | } | ||
| 167 | doors { | ||
| 168 | map: "the_parthenon" | ||
| 169 | name: "Lavender Cubes" | ||
| 170 | } | ||
| 171 | } | ||
| diff --git a/data/ids.yaml b/data/ids.yaml index 4e2cd66..0042899 100644 --- a/data/ids.yaml +++ b/data/ids.yaml | |||
| @@ -4,15 +4,21 @@ maps: | |||
| 4 | Between Entrance: | 4 | Between Entrance: |
| 5 | panels: | 5 | panels: |
| 6 | RIGHT: 2721 | 6 | RIGHT: 2721 |
| 7 | ports: | ||
| 8 | BETWEEN: 3106 | ||
| 7 | Desert Room: | 9 | Desert Room: |
| 8 | panels: | 10 | panels: |
| 9 | LESS: 2722 | 11 | LESS: 2722 |
| 10 | Entry: | 12 | Entry: |
| 11 | panels: | 13 | panels: |
| 12 | HELLO: 2724 | 14 | HELLO: 2724 |
| 15 | ports: | ||
| 16 | GREAT: 3108 | ||
| 13 | Entry Entrance: | 17 | Entry Entrance: |
| 14 | panels: | 18 | panels: |
| 15 | ENTRY: 2723 | 19 | ENTRY: 2723 |
| 20 | ports: | ||
| 21 | ENTRY: 3107 | ||
| 16 | Hide Room: | 22 | Hide Room: |
| 17 | panels: | 23 | panels: |
| 18 | HIDE: 2725 | 24 | HIDE: 2725 |
| @@ -25,13 +31,23 @@ maps: | |||
| 25 | 2: 2761 | 31 | 2: 2761 |
| 26 | 3: 2762 | 32 | 3: 2762 |
| 27 | 4: 2763 | 33 | 4: 2763 |
| 34 | ports: | ||
| 35 | LEFT: 3110 | ||
| 36 | RIGHT: 3109 | ||
| 28 | Partial Entrance: | 37 | Partial Entrance: |
| 29 | panels: | 38 | panels: |
| 30 | PARTIAL: 2729 | 39 | PARTIAL: 2729 |
| 40 | ports: | ||
| 41 | PARTIAL: 3111 | ||
| 31 | Perceptive Entrance: | 42 | Perceptive Entrance: |
| 32 | panels: | 43 | panels: |
| 33 | COLORS: 2731 | 44 | COLORS: 2731 |
| 34 | PART: 2730 | 45 | PART: 2730 |
| 46 | ports: | ||
| 47 | PERCEPTIVE: 3112 | ||
| 48 | Repetitive Entrance: | ||
| 49 | ports: | ||
| 50 | REPETITIVE: 3113 | ||
| 35 | Shop Entrance: | 51 | Shop Entrance: |
| 36 | panels: | 52 | panels: |
| 37 | HOPS: 2732 | 53 | HOPS: 2732 |
| @@ -41,9 +57,13 @@ maps: | |||
| 41 | Tenacious Entrance: | 57 | Tenacious Entrance: |
| 42 | panels: | 58 | panels: |
| 43 | HERO: 2734 | 59 | HERO: 2734 |
| 60 | ports: | ||
| 61 | TENACIOUS: 3114 | ||
| 44 | Unkempt Entrance: | 62 | Unkempt Entrance: |
| 45 | panels: | 63 | panels: |
| 46 | RETURN: 2735 | 64 | RETURN: 2735 |
| 65 | ports: | ||
| 66 | UNKEMPT: 3115 | ||
| 47 | Unyielding Entrance: | 67 | Unyielding Entrance: |
| 48 | panels: | 68 | panels: |
| 49 | FORTH: 2736 | 69 | FORTH: 2736 |
| @@ -53,7 +73,12 @@ maps: | |||
| 53 | Between Door: 2716 | 73 | Between Door: 2716 |
| 54 | Desert Door: 2717 | 74 | Desert Door: 2717 |
| 55 | Front Door: 2709 | 75 | Front Door: 2709 |
| 76 | Hidden Door: 2840 | ||
| 77 | Letters Panel: 3285 | ||
| 78 | Near Perceptive Panel: 3284 | ||
| 56 | Partial Door: 2713 | 79 | Partial Door: 2713 |
| 80 | Perceptive From Inside: 2842 | ||
| 81 | Perceptive From Outside: 2841 | ||
| 57 | Repetitive Entrance: 2714 | 82 | Repetitive Entrance: 2714 |
| 58 | Shop Door: 2718 | 83 | Shop Door: 2718 |
| 59 | Stormy Entrance: 2710 | 84 | Stormy Entrance: 2710 |
| @@ -245,6 +270,8 @@ maps: | |||
| 245 | TICKETBORNE: 1737 | 270 | TICKETBORNE: 1737 |
| 246 | TWOGOTHIM: 1735 | 271 | TWOGOTHIM: 1735 |
| 247 | UNDERPANTS: 1732 | 272 | UNDERPANTS: 1732 |
| 273 | ports: | ||
| 274 | ENTRY: 3116 | ||
| 248 | Computer Room: | 275 | Computer Room: |
| 249 | panels: | 276 | panels: |
| 250 | KEYBOARD (1): 1746 | 277 | KEYBOARD (1): 1746 |
| @@ -295,6 +322,8 @@ maps: | |||
| 295 | Entry Shortcut: | 322 | Entry Shortcut: |
| 296 | panels: | 323 | panels: |
| 297 | WELCOME: 1776 | 324 | WELCOME: 1776 |
| 325 | ports: | ||
| 326 | ENTRY: 3117 | ||
| 298 | Eye Painting: | 327 | Eye Painting: |
| 299 | panels: | 328 | panels: |
| 300 | REVILED: 1777 | 329 | REVILED: 1777 |
| @@ -484,6 +513,9 @@ maps: | |||
| 484 | Maze Paintings Area: | 513 | Maze Paintings Area: |
| 485 | panels: | 514 | panels: |
| 486 | Paintings: 1929 | 515 | Paintings: 1929 |
| 516 | Moat: | ||
| 517 | ports: | ||
| 518 | HIVE: 3118 | ||
| 487 | North Castle Area: | 519 | North Castle Area: |
| 488 | panels: | 520 | panels: |
| 489 | A: 1930 | 521 | A: 1930 |
| @@ -548,6 +580,8 @@ maps: | |||
| 548 | PETAL: 1976 | 580 | PETAL: 1976 |
| 549 | PLUM (1): 1971 | 581 | PLUM (1): 1971 |
| 550 | PLUM (2): 1972 | 582 | PLUM (2): 1972 |
| 583 | ports: | ||
| 584 | REVITALIZED: 3119 | ||
| 551 | Outside Hotel: | 585 | Outside Hotel: |
| 552 | panels: | 586 | panels: |
| 553 | COLORFUL: 1977 | 587 | COLORFUL: 1977 |
| @@ -627,6 +661,9 @@ maps: | |||
| 627 | PHARAOH: 2021 | 661 | PHARAOH: 2021 |
| 628 | SHEET: 2020 | 662 | SHEET: 2020 |
| 629 | STRAW: 2024 | 663 | STRAW: 2024 |
| 664 | Purple Hallway From Great: | ||
| 665 | ports: | ||
| 666 | GREAT: 3120 | ||
| 630 | Purple NW Vestibule: | 667 | Purple NW Vestibule: |
| 631 | panels: | 668 | panels: |
| 632 | LOSE: 2029 | 669 | LOSE: 2029 |
| @@ -693,9 +730,13 @@ maps: | |||
| 693 | Quiet Entrance: | 730 | Quiet Entrance: |
| 694 | panels: | 731 | panels: |
| 695 | HIDDEN: 2064 | 732 | HIDDEN: 2064 |
| 733 | ports: | ||
| 734 | QUIET: 3121 | ||
| 696 | Rain Side: | 735 | Rain Side: |
| 697 | panels: | 736 | panels: |
| 698 | "?": 2065 | 737 | "?": 2065 |
| 738 | ports: | ||
| 739 | BEARER: 3122 | ||
| 699 | Rainbow Blue: | 740 | Rainbow Blue: |
| 700 | panels: | 741 | panels: |
| 701 | THEME: 2066 | 742 | THEME: 2066 |
| @@ -748,7 +789,7 @@ maps: | |||
| 748 | Back (2): 2090 | 789 | Back (2): 2090 |
| 749 | Colors: 2097 | 790 | Colors: 2097 |
| 750 | FIR: 2095 | 791 | FIR: 2095 |
| 751 | Left: 2088 | 792 | Near Obscured Puzzles: 2088 |
| 752 | OAK: 2093 | 793 | OAK: 2093 |
| 753 | PINE: 2094 | 794 | PINE: 2094 |
| 754 | WALK BACK: 2091 | 795 | WALK BACK: 2091 |
| @@ -798,6 +839,8 @@ maps: | |||
| 798 | Starting Room: | 839 | Starting Room: |
| 799 | panels: | 840 | panels: |
| 800 | ENTRANCE: 2127 | 841 | ENTRANCE: 2127 |
| 842 | ports: | ||
| 843 | GREAT: 3123 | ||
| 801 | Sweet Foyer: | 844 | Sweet Foyer: |
| 802 | panels: | 845 | panels: |
| 803 | EQUAL: 2129 | 846 | EQUAL: 2129 |
| @@ -806,6 +849,9 @@ maps: | |||
| 806 | RENT (2): 2132 | 849 | RENT (2): 2132 |
| 807 | RENT (3): 2133 | 850 | RENT (3): 2133 |
| 808 | RENT (4): 2131 | 851 | RENT (4): 2131 |
| 852 | ports: | ||
| 853 | SWEET1: 3124 | ||
| 854 | SWEET2: 3125 | ||
| 809 | Tree Entrance: | 855 | Tree Entrance: |
| 810 | panels: | 856 | panels: |
| 811 | DIFFERENCE: 2135 | 857 | DIFFERENCE: 2135 |
| @@ -814,6 +860,8 @@ maps: | |||
| 814 | RAT: 2134 | 860 | RAT: 2134 |
| 815 | SUNDER: 2139 | 861 | SUNDER: 2139 |
| 816 | WHERE: 2138 | 862 | WHERE: 2138 |
| 863 | ports: | ||
| 864 | TREE: 3126 | ||
| 817 | U2 Room: | 865 | U2 Room: |
| 818 | panels: | 866 | panels: |
| 819 | CHAOS: 2147 | 867 | CHAOS: 2147 |
| @@ -832,6 +880,9 @@ maps: | |||
| 832 | TROUBLE: 2148 | 880 | TROUBLE: 2148 |
| 833 | WICKED: 2142 | 881 | WICKED: 2142 |
| 834 | WONDERLAND: 2156 | 882 | WONDERLAND: 2156 |
| 883 | Unkempt Entrance: | ||
| 884 | ports: | ||
| 885 | UNKEMPT: 3127 | ||
| 835 | Welcome Back Area: | 886 | Welcome Back Area: |
| 836 | panels: | 887 | panels: |
| 837 | FAREWELL LITTLE LAMB: 2157 | 888 | FAREWELL LITTLE LAMB: 2157 |
| @@ -881,6 +932,9 @@ maps: | |||
| 881 | CUT: 2194 | 932 | CUT: 2194 |
| 882 | MISSING: 2192 | 933 | MISSING: 2192 |
| 883 | STONES: 2195 | 934 | STONES: 2195 |
| 935 | White Hallway From Entry: | ||
| 936 | ports: | ||
| 937 | ENTRY: 3128 | ||
| 884 | Wisdom Panel: | 938 | Wisdom Panel: |
| 885 | panels: | 939 | panels: |
| 886 | INTELLIGENCE: 2198 | 940 | INTELLIGENCE: 2198 |
| @@ -891,6 +945,8 @@ maps: | |||
| 891 | ARTS: 2202 | 945 | ARTS: 2202 |
| 892 | SONG: 2203 | 946 | SONG: 2203 |
| 893 | UNDER: 2200 | 947 | UNDER: 2200 |
| 948 | ports: | ||
| 949 | WONDROUS: 3129 | ||
| 894 | Yellow Color Backside: | 950 | Yellow Color Backside: |
| 895 | panels: | 951 | panels: |
| 896 | BRASS: 2206 | 952 | BRASS: 2206 |
| @@ -902,6 +958,8 @@ maps: | |||
| 902 | Paintings: 2210 | 958 | Paintings: 2210 |
| 903 | SPIN: 2209 | 959 | SPIN: 2209 |
| 904 | SUN: 2208 | 960 | SUN: 2208 |
| 961 | ports: | ||
| 962 | FOURROOMS: 3130 | ||
| 905 | Yellow Room: | 963 | Yellow Room: |
| 906 | panels: | 964 | panels: |
| 907 | COLOR: 2217 | 965 | COLOR: 2217 |
| @@ -971,6 +1029,7 @@ maps: | |||
| 971 | doors: | 1029 | doors: |
| 972 | Amber East Doors: 1511 | 1030 | Amber East Doors: 1511 |
| 973 | Amber North Door: 1510 | 1031 | Amber North Door: 1510 |
| 1032 | Amber Room Panels: 3289 | ||
| 974 | Amber South Door: 1509 | 1033 | Amber South Door: 1509 |
| 975 | Bee Room Back Door: 1523 | 1034 | Bee Room Back Door: 1523 |
| 976 | Bee Room Entrance: 1521 | 1035 | Bee Room Entrance: 1521 |
| @@ -987,7 +1046,6 @@ maps: | |||
| 987 | Blue Rainbow Room: 1538 | 1046 | Blue Rainbow Room: 1538 |
| 988 | Blue Room: 1477 | 1047 | Blue Room: 1477 |
| 989 | Blue Room Entrance: 1476 | 1048 | Blue Room Entrance: 1476 |
| 990 | Blue Smiley Entrance: 1478 | ||
| 991 | Blue Smiley Exit To Red: 1547 | 1049 | Blue Smiley Exit To Red: 1547 |
| 992 | Book Room Entrance: 1588 | 1050 | Book Room Entrance: 1588 |
| 993 | Book Room Exit: 1592 | 1051 | Book Room Exit: 1592 |
| @@ -1014,14 +1072,17 @@ maps: | |||
| 1014 | Dark Light Room Entrance: 1569 | 1072 | Dark Light Room Entrance: 1569 |
| 1015 | Dark Light Room Exit: 1570 | 1073 | Dark Light Room Exit: 1570 |
| 1016 | Dark Light Room Exit Panel: 1571 | 1074 | Dark Light Room Exit Panel: 1571 |
| 1075 | Direction Panels: 3297 | ||
| 1017 | Entry Shortcut Secret Exit: 1437 | 1076 | Entry Shortcut Secret Exit: 1437 |
| 1077 | Equality Panels: 3292 | ||
| 1018 | Eye Painting: 2751 | 1078 | Eye Painting: 2751 |
| 1019 | Eye Painting Exit: 1446 | 1079 | Eye Painting Exit: 1446 |
| 1020 | F Keyholder Door: 1551 | 1080 | F Keyholder Door: 1551 |
| 1021 | F2 Room Back Left Door: 1491 | 1081 | F2 Room Northwest Door: 1491 |
| 1022 | F2 Room Back Middle Door: 1492 | 1082 | F2 Room Southeast Door: 1487 |
| 1023 | F2 Room Back Right Door: 1490 | 1083 | F2 Room Southwest Door: 1490 |
| 1024 | F2 Room Entrance: 1487 | 1084 | F2 Room West Door: 1492 |
| 1085 | Farewell Little Lamb Panels: 3302 | ||
| 1025 | Flip Painting Blocker: 1552 | 1086 | Flip Painting Blocker: 1552 |
| 1026 | Globe Room East Door: 1589 | 1087 | Globe Room East Door: 1589 |
| 1027 | Globe Room South Door: 1591 | 1088 | Globe Room South Door: 1591 |
| @@ -1044,6 +1105,7 @@ maps: | |||
| 1044 | House Entrance: 1495 | 1105 | House Entrance: 1495 |
| 1045 | House Side Door: 1566 | 1106 | House Side Door: 1566 |
| 1046 | Intense Room Entrance: 1522 | 1107 | Intense Room Entrance: 1522 |
| 1108 | Lime Hexes: 2810 | ||
| 1047 | Magenta Hexes: 2272 | 1109 | Magenta Hexes: 2272 |
| 1048 | Magic Room Entrance: 1500 | 1110 | Magic Room Entrance: 1500 |
| 1049 | Magic Room Panels: 1499 | 1111 | Magic Room Panels: 1499 |
| @@ -1051,12 +1113,16 @@ maps: | |||
| 1051 | Maze North Door: 1502 | 1113 | Maze North Door: 1502 |
| 1052 | Maze South Door: 1503 | 1114 | Maze South Door: 1503 |
| 1053 | Near Flip Painting Door: 1474 | 1115 | Near Flip Painting Door: 1474 |
| 1116 | Near H Keyholder Panel: 3299 | ||
| 1054 | Near Pyramid Gate: 1447 | 1117 | Near Pyramid Gate: 1447 |
| 1055 | Near Sweet Blue Door: 1573 | 1118 | Near Sweet Blue Door: 1573 |
| 1056 | Near Sweet Brown Door: 1561 | 1119 | Near Sweet Brown Door: 1561 |
| 1057 | Near Yellow Room Door: 1565 | 1120 | Near Yellow Room Door: 1565 |
| 1058 | North Castle Panel: 2742 | 1121 | North Castle Panel: 2742 |
| 1059 | O2 Room Back Door: 1485 | 1122 | Nursery Panels: 3298 |
| 1123 | O2 Room Northeast Door: 1485 | ||
| 1124 | O2 Room Southeast Door: 1478 | ||
| 1125 | Orange Panels: 3293 | ||
| 1060 | Orange Rainbow Panel: 2267 | 1126 | Orange Rainbow Panel: 2267 |
| 1061 | Orange Rainbow Room: 1535 | 1127 | Orange Rainbow Room: 1535 |
| 1062 | Orange Room: 1507 | 1128 | Orange Room: 1507 |
| @@ -1071,6 +1137,7 @@ maps: | |||
| 1071 | Pink Hallway: 1555 | 1137 | Pink Hallway: 1555 |
| 1072 | Planet Room Divider: 1513 | 1138 | Planet Room Divider: 1513 |
| 1073 | Planet Room Secret Door: 1578 | 1139 | Planet Room Secret Door: 1578 |
| 1140 | Plum Panels: 3300 | ||
| 1074 | Plum Room Entrance: 1576 | 1141 | Plum Room Entrance: 1576 |
| 1075 | Plum Room Exit: 1577 | 1142 | Plum Room Exit: 1577 |
| 1076 | Pumpkin Door: 1583 | 1143 | Pumpkin Door: 1583 |
| @@ -1095,6 +1162,7 @@ maps: | |||
| 1095 | Purple West Area West Door: 1466 | 1162 | Purple West Area West Door: 1466 |
| 1096 | Pyramid Entrance: 1505 | 1163 | Pyramid Entrance: 1505 |
| 1097 | Rain Side Panel: 1546 | 1164 | Rain Side Panel: 1546 |
| 1165 | Rainbow Color Backside Panels: 3286 | ||
| 1098 | Rainbow Rooms Entrance: 1533 | 1166 | Rainbow Rooms Entrance: 1533 |
| 1099 | Red Rainbow Panel: 2266 | 1167 | Red Rainbow Panel: 2266 |
| 1100 | Red Rainbow Room: 1534 | 1168 | Red Rainbow Room: 1534 |
| @@ -1102,6 +1170,7 @@ maps: | |||
| 1102 | Red Room Entrance: 1562 | 1170 | Red Room Entrance: 1562 |
| 1103 | Red Smiley: 1554 | 1171 | Red Smiley: 1554 |
| 1104 | Red Smiley Entrance: 1553 | 1172 | Red Smiley Entrance: 1553 |
| 1173 | Rent Panels: 3291 | ||
| 1105 | Roof Access: 1528 | 1174 | Roof Access: 1528 |
| 1106 | Salt Room Entrance: 1532 | 1175 | Salt Room Entrance: 1532 |
| 1107 | Seasoning Doors: 1544 | 1176 | Seasoning Doors: 1544 |
| @@ -1110,8 +1179,9 @@ maps: | |||
| 1110 | South Castle Area Entrance: 1575 | 1179 | South Castle Area Entrance: 1575 |
| 1111 | South Castle Panel: 2744 | 1180 | South Castle Panel: 2744 |
| 1112 | Southwest Area Intersection: 1475 | 1181 | Southwest Area Intersection: 1475 |
| 1113 | Splintering Exit Left Door: 1449 | 1182 | Splintering Area Panels: 3287 |
| 1114 | Splintering Exit Right Door: 1450 | 1183 | Splintering Exit North Door: 1449 |
| 1184 | Splintering Exit South Door: 1450 | ||
| 1115 | Starting Room East Wall Center Door: 1439 | 1185 | Starting Room East Wall Center Door: 1439 |
| 1116 | Starting Room East Wall North Door: 1440 | 1186 | Starting Room East Wall North Door: 1440 |
| 1117 | Starting Room North Wall Center Door: 1432 | 1187 | Starting Room North Wall Center Door: 1432 |
| @@ -1123,21 +1193,28 @@ maps: | |||
| 1123 | Starting Room West Wall North Door: 1438 | 1193 | Starting Room West Wall North Door: 1438 |
| 1124 | Starting Room West Wall South Door: 1433 | 1194 | Starting Room West Wall South Door: 1433 |
| 1125 | Sticks And Stones Door: 1593 | 1195 | Sticks And Stones Door: 1593 |
| 1196 | Teal Panel: 3296 | ||
| 1126 | Temple of the Eyes Entrance: 1444 | 1197 | Temple of the Eyes Entrance: 1444 |
| 1127 | U2 Room Back Door: 1497 | 1198 | Theo Panels: 2811 |
| 1128 | U2 Room Back Right Door: 1496 | 1199 | Tree Panels: 3295 |
| 1129 | U2 Room Entrance: 1498 | 1200 | U2 Room East Door: 1498 |
| 1130 | U2 Room Shortcut: 1493 | 1201 | U2 Room Southeast Door: 1493 |
| 1202 | U2 Room Southwest Door: 1496 | ||
| 1203 | U2 Room West Door: 1497 | ||
| 1131 | Welcome Back Door: 1435 | 1204 | Welcome Back Door: 1435 |
| 1132 | Welcome Back Secret Door: 1434 | 1205 | Welcome Back Secret Door: 1434 |
| 1133 | West Castle Panel: 2743 | 1206 | West Castle Panel: 2743 |
| 1207 | West Spire Panel: 3294 | ||
| 1208 | West Sticks And Stones Panel: 3288 | ||
| 1134 | White Hallway From Entry: 1488 | 1209 | White Hallway From Entry: 1488 |
| 1135 | Wonderland Left Door: 1520 | 1210 | Wonderland North Door: 1520 |
| 1136 | Wonderland Right Door: 1504 | 1211 | Wonderland South Door: 1504 |
| 1137 | Yellow Rainbow Panel: 2268 | 1212 | Yellow Rainbow Panel: 2268 |
| 1138 | Yellow Rainbow Room: 1536 | 1213 | Yellow Rainbow Room: 1536 |
| 1214 | Yellow Roof Puzzles: 3290 | ||
| 1139 | Yellow Room: 1568 | 1215 | Yellow Room: 1568 |
| 1140 | Yellow Room Entrance: 1567 | 1216 | Yellow Room Entrance: 1567 |
| 1217 | Yellow Smiley Annex Panels: 3301 | ||
| 1141 | Yellow Smiley Door: 1548 | 1218 | Yellow Smiley Door: 1548 |
| 1142 | Z2 Room Back Exit: 1451 | 1219 | Z2 Room Back Exit: 1451 |
| 1143 | Z2 Room Northeast Door: 1454 | 1220 | Z2 Room Northeast Door: 1454 |
| @@ -1151,6 +1228,84 @@ maps: | |||
| 1151 | Zoo Prize Door: 1599 | 1228 | Zoo Prize Door: 1599 |
| 1152 | Zoo South Entrance: 1596 | 1229 | Zoo South Entrance: 1596 |
| 1153 | Zoo West Entrance: 1594 | 1230 | Zoo West Entrance: 1594 |
| 1231 | demo: | ||
| 1232 | rooms: | ||
| 1233 | Backside Area: | ||
| 1234 | panels: | ||
| 1235 | BACKSIDE: 3049 | ||
| 1236 | DOORWAYS: 3050 | ||
| 1237 | ENDS (2): 3052 | ||
| 1238 | SEE: 3051 | ||
| 1239 | Castle: | ||
| 1240 | panels: | ||
| 1241 | G: 3054 | ||
| 1242 | SERIES: 3053 | ||
| 1243 | Center Building: | ||
| 1244 | panels: | ||
| 1245 | FUZZIES: 3056 | ||
| 1246 | WORLD: 3055 | ||
| 1247 | Flower Hallway: | ||
| 1248 | panels: | ||
| 1249 | LACES: 3057 | ||
| 1250 | Main Area: | ||
| 1251 | panels: | ||
| 1252 | A: 3089 | ||
| 1253 | AGES: 3063 | ||
| 1254 | ANY: 3071 | ||
| 1255 | ART: 3059 | ||
| 1256 | Blank: 3095 | ||
| 1257 | C: 3088 | ||
| 1258 | CASTS: 3086 | ||
| 1259 | CLOCKWISE: 3067 | ||
| 1260 | COLORFUL: 3061 | ||
| 1261 | COUNTER: 3070 | ||
| 1262 | DAZES: 3084 | ||
| 1263 | DEN: 3064 | ||
| 1264 | DISCOVER: 3096 | ||
| 1265 | E (1): 3091 | ||
| 1266 | E (2): 3093 | ||
| 1267 | END: 3079 | ||
| 1268 | FAMILY: 3097 | ||
| 1269 | GAZES: 3085 | ||
| 1270 | HAZES: 3083 | ||
| 1271 | HI: 3058 | ||
| 1272 | HID: 3065 | ||
| 1273 | MESS: 3066 | ||
| 1274 | MIND: 3078 | ||
| 1275 | N: 3092 | ||
| 1276 | PACES: 3069 | ||
| 1277 | POSSIBLE: 3068 | ||
| 1278 | R: 3094 | ||
| 1279 | RAD: 3080 | ||
| 1280 | RODS: 3072 | ||
| 1281 | S: 3087 | ||
| 1282 | SECRETIVE: 3075 | ||
| 1283 | STALK: 3082 | ||
| 1284 | TALK: 3074 | ||
| 1285 | TEES: 3060 | ||
| 1286 | TOADS: 3076 | ||
| 1287 | TON: 3077 | ||
| 1288 | TOO: 3081 | ||
| 1289 | TWO: 3073 | ||
| 1290 | V: 3090 | ||
| 1291 | WORD: 3062 | ||
| 1292 | Mastery: | ||
| 1293 | masteries: | ||
| 1294 | MASTERY: 3098 | ||
| 1295 | Tower: | ||
| 1296 | panels: | ||
| 1297 | ENDS (1): 3099 | ||
| 1298 | doors: | ||
| 1299 | Castle: 3046 | ||
| 1300 | Center Building: 3039 | ||
| 1301 | Center Building Panels: 3041 | ||
| 1302 | Flower Hallway: 3040 | ||
| 1303 | Gold Door: 3048 | ||
| 1304 | Orange Door: 3042 | ||
| 1305 | Purple Door: 3043 | ||
| 1306 | Red Door: 3045 | ||
| 1307 | Scavenger Hunt: 3047 | ||
| 1308 | Yellow Door: 3044 | ||
| 1154 | four_rooms: | 1309 | four_rooms: |
| 1155 | rooms: | 1310 | rooms: |
| 1156 | Examples Room: | 1311 | Examples Room: |
| @@ -1163,6 +1318,8 @@ maps: | |||
| 1163 | SONNET: 12 | 1318 | SONNET: 12 |
| 1164 | SUPERLATIVE: 11 | 1319 | SUPERLATIVE: 11 |
| 1165 | URN: 13 | 1320 | URN: 13 |
| 1321 | ports: | ||
| 1322 | DAEDALUS: 3131 | ||
| 1166 | Hallway: | 1323 | Hallway: |
| 1167 | panels: | 1324 | panels: |
| 1168 | HUNCHBACK: 16 | 1325 | HUNCHBACK: 16 |
| @@ -1177,6 +1334,8 @@ maps: | |||
| 1177 | SWAY: 24 | 1334 | SWAY: 24 |
| 1178 | TERROR: 20 | 1335 | TERROR: 20 |
| 1179 | TURN: 22 | 1336 | TURN: 22 |
| 1337 | ports: | ||
| 1338 | IMPRESSIVE: 3132 | ||
| 1180 | Keyholder Room: | 1339 | Keyholder Room: |
| 1181 | keyholders: | 1340 | keyholders: |
| 1182 | A: 2773 | 1341 | A: 2773 |
| @@ -1190,6 +1349,8 @@ maps: | |||
| 1190 | SERIOUS: 31 | 1349 | SERIOUS: 31 |
| 1191 | SURPASS: 29 | 1350 | SURPASS: 29 |
| 1192 | VERSE: 30 | 1351 | VERSE: 30 |
| 1352 | ports: | ||
| 1353 | ENTRY: 3133 | ||
| 1193 | Time Room: | 1354 | Time Room: |
| 1194 | panels: | 1355 | panels: |
| 1195 | BROODING: 33 | 1356 | BROODING: 33 |
| @@ -1200,6 +1361,8 @@ maps: | |||
| 1200 | RHYTHM: 40 | 1361 | RHYTHM: 40 |
| 1201 | SUSPENSE: 36 | 1362 | SUSPENSE: 36 |
| 1202 | WRITING: 38 | 1363 | WRITING: 38 |
| 1364 | ports: | ||
| 1365 | OWL: 3134 | ||
| 1203 | doors: | 1366 | doors: |
| 1204 | A2 Door: 4 | 1367 | A2 Door: 4 |
| 1205 | Examples Door: 1 | 1368 | Examples Door: 1 |
| @@ -1207,6 +1370,179 @@ maps: | |||
| 1207 | Keyholder Door: 5 | 1370 | Keyholder Door: 5 |
| 1208 | Synonyms Door: 2 | 1371 | Synonyms Door: 2 |
| 1209 | Time Door: 3 | 1372 | Time Door: 3 |
| 1373 | icarus: | ||
| 1374 | rooms: | ||
| 1375 | Above Trans Rights: | ||
| 1376 | panels: | ||
| 1377 | ANT: 2877 | ||
| 1378 | Big U: | ||
| 1379 | panels: | ||
| 1380 | COLONY: 2879 | ||
| 1381 | DECK: 2878 | ||
| 1382 | MANOR: 2880 | ||
| 1383 | Fatherland: | ||
| 1384 | panels: | ||
| 1385 | FATHERLAND: 2881 | ||
| 1386 | Highest Point: | ||
| 1387 | panels: | ||
| 1388 | DIAGNOSIS: 2882 | ||
| 1389 | QUEEN: 2883 | ||
| 1390 | Mastery: | ||
| 1391 | masteries: | ||
| 1392 | MASTERY: 2994 | ||
| 1393 | Maze: | ||
| 1394 | panels: | ||
| 1395 | ANALYSIS: 2887 | ||
| 1396 | BOOKS: 2890 | ||
| 1397 | KING (1): 2886 | ||
| 1398 | MANSLAUGHTER: 2888 | ||
| 1399 | MEDIUMS: 2889 | ||
| 1400 | Maze Back: | ||
| 1401 | panels: | ||
| 1402 | THESE: 2884 | ||
| 1403 | Maze King Panel: | ||
| 1404 | panels: | ||
| 1405 | KING (2): 2885 | ||
| 1406 | Mini Icarus 2: | ||
| 1407 | panels: | ||
| 1408 | ANIMALS: 2893 | ||
| 1409 | ARROWS: 2894 | ||
| 1410 | BATTERY: 2891 | ||
| 1411 | SQUAD: 2895 | ||
| 1412 | TROUPE: 2892 | ||
| 1413 | Pillar Ramp: | ||
| 1414 | panels: | ||
| 1415 | ASTEROID: 2896 | ||
| 1416 | BUNCH: 2897 | ||
| 1417 | DRONE: 2900 | ||
| 1418 | PATRICIDE: 2899 | ||
| 1419 | PEA (1): 2901 | ||
| 1420 | PRINCES: 2898 | ||
| 1421 | Spiral Ramp: | ||
| 1422 | panels: | ||
| 1423 | FIREMAN: 2902 | ||
| 1424 | The Orb: | ||
| 1425 | panels: | ||
| 1426 | ADDERS: 2903 | ||
| 1427 | AXIS: 2913 | ||
| 1428 | BASIS (2): 2912 | ||
| 1429 | CLUTCH (1): 2911 | ||
| 1430 | CLUTCH (2): 2918 | ||
| 1431 | DEADLINE: 2908 | ||
| 1432 | DISCUS: 2916 | ||
| 1433 | FISH: 2907 | ||
| 1434 | HISS: 2915 | ||
| 1435 | NEEDLE: 2905 | ||
| 1436 | PEA (2): 2909 | ||
| 1437 | PUPPY: 2904 | ||
| 1438 | SON: 2917 | ||
| 1439 | STRAIGHT: 2914 | ||
| 1440 | THESIS: 2910 | ||
| 1441 | US: 2906 | ||
| 1442 | Through Woman (Obverse): | ||
| 1443 | panels: | ||
| 1444 | COW: 2920 | ||
| 1445 | HUMAN (2): 2919 | ||
| 1446 | Through Woman (Reverse): | ||
| 1447 | panels: | ||
| 1448 | BASIS (1): 2922 | ||
| 1449 | PRINCE: 2921 | ||
| 1450 | Trans Rights: | ||
| 1451 | panels: | ||
| 1452 | SERVANT (1): 2926 | ||
| 1453 | SERVANT (2): 2927 | ||
| 1454 | Trans Rights Panels: | ||
| 1455 | panels: | ||
| 1456 | AGENDER: 2923 | ||
| 1457 | HUMAN (3): 2924 | ||
| 1458 | HUMAN (4): 2925 | ||
| 1459 | Welcome Spine (Obverse): | ||
| 1460 | panels: | ||
| 1461 | FISHWIFE: 2928 | ||
| 1462 | HUMAN (1): 2929 | ||
| 1463 | ports: | ||
| 1464 | WORLDPORT: 3135 | ||
| 1465 | Welcome Spine (Reverse): | ||
| 1466 | panels: | ||
| 1467 | FATHER: 2930 | ||
| 1468 | SISTER: 2932 | ||
| 1469 | TERMITE: 2931 | ||
| 1470 | doors: | ||
| 1471 | Agender Door: 2846 | ||
| 1472 | Animals Door: 2866 | ||
| 1473 | Ant Door: 2855 | ||
| 1474 | Arrows Door: 2865 | ||
| 1475 | Asteroid Bunch Door: 2852 | ||
| 1476 | Battery Door: 2863 | ||
| 1477 | Cow Door: 2853 | ||
| 1478 | Fatherland Door: 2860 | ||
| 1479 | Man Door: 2856 | ||
| 1480 | Mediums Door: 2850 | ||
| 1481 | Murder Panels: 2871 | ||
| 1482 | Near Fireman Wings Painting: 2876 | ||
| 1483 | Orb Panels: 2875 | ||
| 1484 | Patricide Door: 2873 | ||
| 1485 | Pea Door: 2848 | ||
| 1486 | Quick Travel 1: 2851 | ||
| 1487 | Quick Travel 10: 2862 | ||
| 1488 | Quick Travel 2: 2857 | ||
| 1489 | Quick Travel 3: 2847 | ||
| 1490 | Quick Travel 4: 2858 | ||
| 1491 | Quick Travel 5: 2854 | ||
| 1492 | Quick Travel 6: 2870 | ||
| 1493 | Quick Travel 7: 2849 | ||
| 1494 | Quick Travel 8: 2861 | ||
| 1495 | Quick Travel 9: 2864 | ||
| 1496 | Reversed Arrows Door: 2868 | ||
| 1497 | Sun Painting To Drone: 2872 | ||
| 1498 | Termite Door: 2869 | ||
| 1499 | These Door: 2874 | ||
| 1500 | Troupe Door: 2867 | ||
| 1501 | Woman Door: 2859 | ||
| 1502 | the_advanced: | ||
| 1503 | rooms: | ||
| 1504 | CBA: | ||
| 1505 | panels: | ||
| 1506 | CBA (1): 2938 | ||
| 1507 | CBA (2): 2939 | ||
| 1508 | CBA (3): 2940 | ||
| 1509 | Main Area: | ||
| 1510 | panels: | ||
| 1511 | BIRD: 2955 | ||
| 1512 | Blank (1): 2964 | ||
| 1513 | Blank (2): 2965 | ||
| 1514 | Blank (3): 2966 | ||
| 1515 | Blank (4): 2967 | ||
| 1516 | Blank (5): 2968 | ||
| 1517 | DAIRY (1): 2946 | ||
| 1518 | DAIRY (2): 2947 | ||
| 1519 | DAIRY SAUCE: 2948 | ||
| 1520 | DECK (1): 2961 | ||
| 1521 | DECK (2): 2962 | ||
| 1522 | DECK (3): 2963 | ||
| 1523 | FRUIT (1): 2952 | ||
| 1524 | FRUIT (2): 2953 | ||
| 1525 | FRUIT FRUIT: 2954 | ||
| 1526 | GULLIBLE (1): 2949 | ||
| 1527 | GULLIBLE (2): 2950 | ||
| 1528 | GULLIBLE (3): 2951 | ||
| 1529 | I: 2942 | ||
| 1530 | LIVES: 2945 | ||
| 1531 | OBSERVE: 2941 | ||
| 1532 | ORDER (1): 2958 | ||
| 1533 | ORDER (2): 2959 | ||
| 1534 | ORDER (3): 2960 | ||
| 1535 | ORGANIZATION: 2957 | ||
| 1536 | REST: 2943 | ||
| 1537 | THE: 2944 | ||
| 1538 | UNBOTTLING: 2956 | ||
| 1539 | ports: | ||
| 1540 | WORLDPORT: 3136 | ||
| 1541 | Mastery: | ||
| 1542 | masteries: | ||
| 1543 | MASTERY: 2969 | ||
| 1544 | doors: | ||
| 1545 | Side Room Puzzles: 2934 | ||
| 1210 | the_ancient: | 1546 | the_ancient: |
| 1211 | rooms: | 1547 | rooms: |
| 1212 | Inside: | 1548 | Inside: |
| @@ -1223,6 +1559,8 @@ maps: | |||
| 1223 | Back Area: | 1559 | Back Area: |
| 1224 | panels: | 1560 | panels: |
| 1225 | COLOR: 51 | 1561 | COLOR: 51 |
| 1562 | ports: | ||
| 1563 | DAEDALUS: 3137 | ||
| 1226 | Blue Animal (View): | 1564 | Blue Animal (View): |
| 1227 | panels: | 1565 | panels: |
| 1228 | HALF: 52 | 1566 | HALF: 52 |
| @@ -1249,6 +1587,8 @@ maps: | |||
| 1249 | SQUISH: 60 | 1587 | SQUISH: 60 |
| 1250 | TOAD: 64 | 1588 | TOAD: 64 |
| 1251 | VIEW: 58 | 1589 | VIEW: 58 |
| 1590 | ports: | ||
| 1591 | UNYIELDING: 3138 | ||
| 1252 | Green Planet (View): | 1592 | Green Planet (View): |
| 1253 | panels: | 1593 | panels: |
| 1254 | SOIL: 66 | 1594 | SOIL: 66 |
| @@ -1285,6 +1625,9 @@ maps: | |||
| 1285 | Red Vegetable: | 1625 | Red Vegetable: |
| 1286 | panels: | 1626 | panels: |
| 1287 | CARD: 78 | 1627 | CARD: 78 |
| 1628 | Tree Entrance: | ||
| 1629 | ports: | ||
| 1630 | TREE: 3139 | ||
| 1288 | Yellow Planet: | 1631 | Yellow Planet: |
| 1289 | panels: | 1632 | panels: |
| 1290 | ZEUS: 79 | 1633 | ZEUS: 79 |
| @@ -1293,7 +1636,9 @@ maps: | |||
| 1293 | CAKE: 80 | 1636 | CAKE: 80 |
| 1294 | doors: | 1637 | doors: |
| 1295 | Butterfly Entrance: 50 | 1638 | Butterfly Entrance: 50 |
| 1639 | Butterfly Room Panels: 3304 | ||
| 1296 | Control Center Brown Door: 49 | 1640 | Control Center Brown Door: 49 |
| 1641 | Control Center Color Panel: 3303 | ||
| 1297 | Exit Door: 47 | 1642 | Exit Door: 47 |
| 1298 | Overlook Door: 46 | 1643 | Overlook Door: 46 |
| 1299 | the_between: | 1644 | the_between: |
| @@ -1301,6 +1646,9 @@ maps: | |||
| 1301 | Control Center Side: | 1646 | Control Center Side: |
| 1302 | panels: | 1647 | panels: |
| 1303 | RIGHT: 93 | 1648 | RIGHT: 93 |
| 1649 | ports: | ||
| 1650 | CC: 3140 | ||
| 1651 | LIVELY: 3141 | ||
| 1304 | Main Area: | 1652 | Main Area: |
| 1305 | panels: | 1653 | panels: |
| 1306 | CAUGHT: 107 | 1654 | CAUGHT: 107 |
| @@ -1331,6 +1679,11 @@ maps: | |||
| 1331 | SUN KOI: 102 | 1679 | SUN KOI: 102 |
| 1332 | THINK: 119 | 1680 | THINK: 119 |
| 1333 | YOU: 115 | 1681 | YOU: 115 |
| 1682 | ports: | ||
| 1683 | GREAT: 3142 | ||
| 1684 | Plaza Entrance: | ||
| 1685 | ports: | ||
| 1686 | PLAZA: 3143 | ||
| 1334 | doors: | 1687 | doors: |
| 1335 | B2 Door: 91 | 1688 | B2 Door: 91 |
| 1336 | Blue Puzzles: 88 | 1689 | Blue Puzzles: 88 |
| @@ -1365,9 +1718,48 @@ maps: | |||
| 1365 | SPECIES: 122 | 1718 | SPECIES: 122 |
| 1366 | STRUCTURE: 129 | 1719 | STRUCTURE: 129 |
| 1367 | TEXT: 136 | 1720 | TEXT: 136 |
| 1721 | ports: | ||
| 1722 | GALLERY: 3144 | ||
| 1368 | Mastery: | 1723 | Mastery: |
| 1369 | masteries: | 1724 | masteries: |
| 1370 | MASTERY: 140 | 1725 | MASTERY: 140 |
| 1726 | the_charismatic: | ||
| 1727 | rooms: | ||
| 1728 | Latitude Middle: | ||
| 1729 | panels: | ||
| 1730 | FUNNY: 2972 | ||
| 1731 | Latitude North: | ||
| 1732 | panels: | ||
| 1733 | DEPENDABLE: 2973 | ||
| 1734 | Latitude South: | ||
| 1735 | panels: | ||
| 1736 | CHARISMA: 2974 | ||
| 1737 | Longitude East: | ||
| 1738 | panels: | ||
| 1739 | FUN: 2975 | ||
| 1740 | Longitude Middle: | ||
| 1741 | panels: | ||
| 1742 | INTELLIGENT: 2976 | ||
| 1743 | Longitude West: | ||
| 1744 | panels: | ||
| 1745 | CREATIVE: 2977 | ||
| 1746 | Main Area: | ||
| 1747 | panels: | ||
| 1748 | AQUA: 2981 | ||
| 1749 | ARC: 2978 | ||
| 1750 | Blank: 2987 | ||
| 1751 | HERE: 2984 | ||
| 1752 | IT: 2985 | ||
| 1753 | KING: 2979 | ||
| 1754 | NAIL: 2983 | ||
| 1755 | PINS: 2986 | ||
| 1756 | TILE: 2982 | ||
| 1757 | TIP: 2980 | ||
| 1758 | ports: | ||
| 1759 | WORLDPORT: 3145 | ||
| 1760 | Mastery: | ||
| 1761 | masteries: | ||
| 1762 | MASTERY: 2988 | ||
| 1371 | the_colorful: | 1763 | the_colorful: |
| 1372 | rooms: | 1764 | rooms: |
| 1373 | Black Room: | 1765 | Black Room: |
| @@ -1384,6 +1776,9 @@ maps: | |||
| 1384 | CHAOS: 159 | 1776 | CHAOS: 159 |
| 1385 | KOI: 157 | 1777 | KOI: 157 |
| 1386 | WISH: 158 | 1778 | WISH: 158 |
| 1779 | ports: | ||
| 1780 | DARKROOM: 3147 | ||
| 1781 | STURDY: 3146 | ||
| 1387 | Cyan Room: | 1782 | Cyan Room: |
| 1388 | panels: | 1783 | panels: |
| 1389 | BROTHER: 160 | 1784 | BROTHER: 160 |
| @@ -1408,6 +1803,8 @@ maps: | |||
| 1408 | White Room: | 1803 | White Room: |
| 1409 | panels: | 1804 | panels: |
| 1410 | BRIGHT: 170 | 1805 | BRIGHT: 170 |
| 1806 | ports: | ||
| 1807 | GREAT: 3148 | ||
| 1411 | Window Room: | 1808 | Window Room: |
| 1412 | panels: | 1809 | panels: |
| 1413 | FADING: 171 | 1810 | FADING: 171 |
| @@ -1418,6 +1815,7 @@ maps: | |||
| 1418 | Black Door: 142 | 1815 | Black Door: 142 |
| 1419 | Blue Door: 144 | 1816 | Blue Door: 144 |
| 1420 | Brown Door: 151 | 1817 | Brown Door: 151 |
| 1818 | Chaos Panel: 3305 | ||
| 1421 | Cyan Door: 149 | 1819 | Cyan Door: 149 |
| 1422 | Gray Door: 153 | 1820 | Gray Door: 153 |
| 1423 | Green Door: 145 | 1821 | Green Door: 145 |
| @@ -1466,6 +1864,8 @@ maps: | |||
| 1466 | LIGHT: 209 | 1864 | LIGHT: 209 |
| 1467 | LOVES: 210 | 1865 | LOVES: 210 |
| 1468 | RANGER: 211 | 1866 | RANGER: 211 |
| 1867 | ports: | ||
| 1868 | DARKROOM: 3149 | ||
| 1469 | Obverse Yellow Room: | 1869 | Obverse Yellow Room: |
| 1470 | panels: | 1870 | panels: |
| 1471 | CIVIL: 216 | 1871 | CIVIL: 216 |
| @@ -1480,13 +1880,39 @@ maps: | |||
| 1480 | Flipped Yellow Door: 175 | 1880 | Flipped Yellow Door: 175 |
| 1481 | G Keyholder Blocker: 181 | 1881 | G Keyholder Blocker: 181 |
| 1482 | G2 Door: 182 | 1882 | G2 Door: 182 |
| 1883 | Main Area Puzzles: 3306 | ||
| 1483 | Near C Keyholder Puzzles: 180 | 1884 | Near C Keyholder Puzzles: 180 |
| 1484 | Obverse Magenta Door: 173 | 1885 | Obverse Magenta Door: 173 |
| 1485 | Obverse Yellow Door: 178 | 1886 | Obverse Yellow Door: 178 |
| 1486 | Obverse Yellow Puzzles: 179 | 1887 | Obverse Yellow Puzzles: 179 |
| 1487 | T Keyholder Blocker: 183 | 1888 | the_crystalline: |
| 1889 | rooms: | ||
| 1890 | Flip Area: | ||
| 1891 | panels: | ||
| 1892 | SUCCEED: 2989 | ||
| 1893 | Main Area: | ||
| 1894 | panels: | ||
| 1895 | DROP: 2991 | ||
| 1896 | LEAP: 2990 | ||
| 1897 | SPIN: 2992 | ||
| 1898 | ports: | ||
| 1899 | WORLDPORT: 3150 | ||
| 1900 | Mastery: | ||
| 1901 | masteries: | ||
| 1902 | MASTERY: 2993 | ||
| 1903 | doors: | ||
| 1904 | Checkpoint Panels: 3307 | ||
| 1488 | the_darkroom: | 1905 | the_darkroom: |
| 1489 | rooms: | 1906 | rooms: |
| 1907 | Congruent Entrance: | ||
| 1908 | ports: | ||
| 1909 | CONGRUENT: 3151 | ||
| 1910 | Cyan Hallway: | ||
| 1911 | ports: | ||
| 1912 | COLORFUL: 3152 | ||
| 1913 | Double Sided Entrance: | ||
| 1914 | ports: | ||
| 1915 | DOUBLESIDED: 3153 | ||
| 1490 | First Room: | 1916 | First Room: |
| 1491 | panels: | 1917 | panels: |
| 1492 | BISON: 225 | 1918 | BISON: 225 |
| @@ -1494,6 +1920,11 @@ maps: | |||
| 1494 | KOI: 228 | 1920 | KOI: 228 |
| 1495 | SHEEP: 227 | 1921 | SHEEP: 227 |
| 1496 | TUNA: 229 | 1922 | TUNA: 229 |
| 1923 | ports: | ||
| 1924 | ENTRY: 3155 | ||
| 1925 | First Room Exit: | ||
| 1926 | ports: | ||
| 1927 | NEXT: 3154 | ||
| 1497 | Second Room: | 1928 | Second Room: |
| 1498 | panels: | 1929 | panels: |
| 1499 | BISON: 231 | 1930 | BISON: 231 |
| @@ -1501,6 +1932,11 @@ maps: | |||
| 1501 | KOI: 234 | 1932 | KOI: 234 |
| 1502 | SHEEP: 233 | 1933 | SHEEP: 233 |
| 1503 | TUNA: 235 | 1934 | TUNA: 235 |
| 1935 | ports: | ||
| 1936 | ENTRY: 3157 | ||
| 1937 | Second Room Exit: | ||
| 1938 | ports: | ||
| 1939 | NEXT: 3156 | ||
| 1504 | Third Room: | 1940 | Third Room: |
| 1505 | panels: | 1941 | panels: |
| 1506 | COINS: 238 | 1942 | COINS: 238 |
| @@ -1512,6 +1948,8 @@ maps: | |||
| 1512 | LOCKS: 242 | 1948 | LOCKS: 242 |
| 1513 | TOUCHES: 243 | 1949 | TOUCHES: 243 |
| 1514 | TURNS: 237 | 1950 | TURNS: 237 |
| 1951 | ports: | ||
| 1952 | ENTRY: 3158 | ||
| 1515 | doors: | 1953 | doors: |
| 1516 | Colorful Entrance: 222 | 1954 | Colorful Entrance: 222 |
| 1517 | Congruent Entrance: 223 | 1955 | Congruent Entrance: 223 |
| @@ -1531,17 +1969,30 @@ maps: | |||
| 1531 | INN: 254 | 1969 | INN: 254 |
| 1532 | OUT: 257 | 1970 | OUT: 257 |
| 1533 | YOU: 255 | 1971 | YOU: 255 |
| 1972 | Gallery Maze: | ||
| 1973 | ports: | ||
| 1974 | GALLERY: 3159 | ||
| 1534 | Main Area: | 1975 | Main Area: |
| 1535 | panels: | 1976 | panels: |
| 1536 | COLOR: 261 | 1977 | COLOR: 261 |
| 1537 | HIT: 258 | 1978 | HIT: 258 |
| 1538 | PAINTING: 260 | 1979 | PAINTING: 260 |
| 1539 | TIN: 259 | 1980 | TIN: 259 |
| 1981 | ports: | ||
| 1982 | ENTRY1: 3160 | ||
| 1983 | ENTRY2: 3161 | ||
| 1984 | ENTRY3: 3162 | ||
| 1540 | Tree Area: | 1985 | Tree Area: |
| 1541 | panels: | 1986 | panels: |
| 1542 | TREE: 262 | 1987 | TREE: 262 |
| 1988 | ports: | ||
| 1989 | TREE: 3163 | ||
| 1990 | Unyielding Entrance: | ||
| 1991 | ports: | ||
| 1992 | UNYIELDING: 3164 | ||
| 1543 | doors: | 1993 | doors: |
| 1544 | Control Center Blue Door: 246 | 1994 | Control Center Blue Door: 246 |
| 1995 | Control Center Blue Panel: 3308 | ||
| 1545 | Gallery Entrance: 245 | 1996 | Gallery Entrance: 245 |
| 1546 | Tree Entrance: 247 | 1997 | Tree Entrance: 247 |
| 1547 | the_door: | 1998 | the_door: |
| @@ -1605,6 +2056,12 @@ maps: | |||
| 1605 | panels: | 2056 | panels: |
| 1606 | ATTIC: 285 | 2057 | ATTIC: 285 |
| 1607 | FULL: 286 | 2058 | FULL: 286 |
| 2059 | ports: | ||
| 2060 | DARKROOM: 3165 | ||
| 2061 | doors: | ||
| 2062 | 10 Panels: 3310 | ||
| 2063 | 15 Panels: 3311 | ||
| 2064 | 5 Panels: 3309 | ||
| 1608 | the_entry: | 2065 | the_entry: |
| 1609 | rooms: | 2066 | rooms: |
| 1610 | Blue Alcove: | 2067 | Blue Alcove: |
| @@ -1614,6 +2071,9 @@ maps: | |||
| 1614 | Colored Doors Area: | 2071 | Colored Doors Area: |
| 1615 | panels: | 2072 | panels: |
| 1616 | OPEN: 327 | 2073 | OPEN: 327 |
| 2074 | Composite Room Entrance: | ||
| 2075 | ports: | ||
| 2076 | COMPOSITE: 3166 | ||
| 1617 | Ctrl Tutorial: | 2077 | Ctrl Tutorial: |
| 1618 | panels: | 2078 | panels: |
| 1619 | RIGHT: 328 | 2079 | RIGHT: 328 |
| @@ -1628,16 +2088,23 @@ maps: | |||
| 1628 | RED: 332 | 2088 | RED: 332 |
| 1629 | SPRAY: 336 | 2089 | SPRAY: 336 |
| 1630 | SUN: 333 | 2090 | SUN: 333 |
| 2091 | Daedalus Entrance: | ||
| 2092 | ports: | ||
| 2093 | DAEDALUS: 3167 | ||
| 2094 | Digital Entrance: | ||
| 2095 | ports: | ||
| 2096 | DIGITAL: 3168 | ||
| 2097 | Entry Exit: | ||
| 2098 | ports: | ||
| 2099 | GREAT: 3169 | ||
| 1631 | Eye Room: | 2100 | Eye Room: |
| 1632 | panels: | 2101 | panels: |
| 1633 | I: 339 | 2102 | I: 339 |
| 2103 | ports: | ||
| 2104 | LIONIZED: 3170 | ||
| 1634 | Flipped Link Area: | 2105 | Flipped Link Area: |
| 1635 | panels: | 2106 | panels: |
| 1636 | WANDER: 340 | 2107 | WANDER: 340 |
| 1637 | Flipped Pyramid Area: | ||
| 1638 | panels: | ||
| 1639 | TURN (1): 341 | ||
| 1640 | TURN (2): 342 | ||
| 1641 | Flipped Right Eye: | 2108 | Flipped Right Eye: |
| 1642 | panels: | 2109 | panels: |
| 1643 | HERE: 344 | 2110 | HERE: 344 |
| @@ -1646,9 +2113,14 @@ maps: | |||
| 1646 | panels: | 2113 | panels: |
| 1647 | CLUE: 345 | 2114 | CLUE: 345 |
| 1648 | SLENDER: 346 | 2115 | SLENDER: 346 |
| 2116 | Four Rooms Entrance: | ||
| 2117 | ports: | ||
| 2118 | FOUR: 3171 | ||
| 1649 | Gallery Return: | 2119 | Gallery Return: |
| 1650 | panels: | 2120 | panels: |
| 1651 | RETURN: 347 | 2121 | RETURN: 347 |
| 2122 | ports: | ||
| 2123 | GALLERY: 3172 | ||
| 1652 | Least Blue Last: | 2124 | Least Blue Last: |
| 1653 | panels: | 2125 | panels: |
| 1654 | AIL: 356 | 2126 | AIL: 356 |
| @@ -1661,6 +2133,14 @@ maps: | |||
| 1661 | STEALER: 352 | 2133 | STEALER: 352 |
| 1662 | TRUST: 354 | 2134 | TRUST: 354 |
| 1663 | WANT: 351 | 2135 | WANT: 351 |
| 2136 | ports: | ||
| 2137 | DARKROOM: 3173 | ||
| 2138 | Liberated Entrance: | ||
| 2139 | ports: | ||
| 2140 | BLUE: 3174 | ||
| 2141 | Liberated Entrance Panel: | ||
| 2142 | panels: | ||
| 2143 | TURN (1): 341 | ||
| 1664 | Lime Room: | 2144 | Lime Room: |
| 1665 | panels: | 2145 | panels: |
| 1666 | COLOR: 361 | 2146 | COLOR: 361 |
| @@ -1669,12 +2149,22 @@ maps: | |||
| 1669 | Link Area: | 2149 | Link Area: |
| 1670 | panels: | 2150 | panels: |
| 1671 | WANDER: 362 | 2151 | WANDER: 362 |
| 2152 | Literate Entrance: | ||
| 2153 | ports: | ||
| 2154 | BROWN: 3175 | ||
| 2155 | Literate Entrance Panel: | ||
| 2156 | panels: | ||
| 2157 | TURN (2): 342 | ||
| 1672 | Parthenon Return: | 2158 | Parthenon Return: |
| 1673 | panels: | 2159 | panels: |
| 1674 | RETURN: 363 | 2160 | RETURN: 363 |
| 2161 | ports: | ||
| 2162 | PARTHENON: 3176 | ||
| 1675 | Rabbit Hole: | 2163 | Rabbit Hole: |
| 1676 | panels: | 2164 | panels: |
| 1677 | PUZZLE: 364 | 2165 | Blank: 364 |
| 2166 | ports: | ||
| 2167 | HOLE: 3177 | ||
| 1678 | Rabbit Hole Lock: | 2168 | Rabbit Hole Lock: |
| 1679 | panels: | 2169 | panels: |
| 1680 | HOLE: 2749 | 2170 | HOLE: 2749 |
| @@ -1692,6 +2182,12 @@ maps: | |||
| 1692 | RAIN WOMAN: 373 | 2182 | RAIN WOMAN: 373 |
| 1693 | WANDER: 370 | 2183 | WANDER: 370 |
| 1694 | WOMAN: 372 | 2184 | WOMAN: 372 |
| 2185 | Repetitive Entrance: | ||
| 2186 | ports: | ||
| 2187 | REPETITIVE: 3178 | ||
| 2188 | Revitalized Entrance: | ||
| 2189 | ports: | ||
| 2190 | REVITALIZED: 3179 | ||
| 1695 | Right Eye: | 2191 | Right Eye: |
| 1696 | panels: | 2192 | panels: |
| 1697 | EYE: 374 | 2193 | EYE: 374 |
| @@ -1700,9 +2196,12 @@ maps: | |||
| 1700 | Shop Entrance: | 2196 | Shop Entrance: |
| 1701 | panels: | 2197 | panels: |
| 1702 | TURN: 377 | 2198 | TURN: 377 |
| 2199 | ports: | ||
| 2200 | SHOP: 3180 | ||
| 1703 | Starting Room: | 2201 | Starting Room: |
| 1704 | panels: | 2202 | panels: |
| 1705 | EYE: 380 | 2203 | EYE: 380 |
| 2204 | Gift Maps: 2970 | ||
| 1706 | HI: 378 | 2205 | HI: 378 |
| 1707 | HINT: 381 | 2206 | HINT: 381 |
| 1708 | THAN: 383 | 2207 | THAN: 383 |
| @@ -1711,33 +2210,45 @@ maps: | |||
| 1711 | Trick Room: | 2210 | Trick Room: |
| 1712 | panels: | 2211 | panels: |
| 1713 | INK: 388 | 2212 | INK: 388 |
| 2213 | White Hallway To Daedalus: | ||
| 2214 | ports: | ||
| 2215 | DAEDALUS: 3181 | ||
| 1714 | Wrath Room: | 2216 | Wrath Room: |
| 1715 | panels: | 2217 | panels: |
| 1716 | CORN: 393 | 2218 | CORN: 393 |
| 1717 | DICE: 392 | 2219 | DICE: 392 |
| 1718 | WREATH: 391 | 2220 | WREATH: 391 |
| 2221 | X Area: | ||
| 2222 | ports: | ||
| 2223 | CC: 3182 | ||
| 1719 | doors: | 2224 | doors: |
| 2225 | Big Eyes: 3316 | ||
| 1720 | Blue Alcove Entrance: 297 | 2226 | Blue Alcove Entrance: 297 |
| 1721 | Blue Alcove Exit: 293 | 2227 | Blue Alcove Exit: 293 |
| 1722 | Colored Doors Area Entrance: 318 | 2228 | Colored Doors Area Entrance: 318 |
| 1723 | Composite Room Entrance: 309 | 2229 | Composite Room Entrance: 309 |
| 1724 | Control Center White Door: 307 | 2230 | Control Center White Door: 307 |
| 2231 | Control Center White Panel: 3318 | ||
| 1725 | Corners Painting: 292 | 2232 | Corners Painting: 292 |
| 1726 | D Room Entrance: 319 | 2233 | D Room Entrance: 319 |
| 1727 | Daedalus Entrance: 311 | 2234 | Daedalus Entrance: 311 |
| 1728 | Flip Area Entrance: 310 | 2235 | Flip Area Entrance: 310 |
| 2236 | Flipped Right Eye Panels: 3315 | ||
| 1729 | Flipped Second Room Left Door: 300 | 2237 | Flipped Second Room Left Door: 300 |
| 1730 | Flipped Second Room Right Door: 299 | 2238 | Flipped Second Room Right Door: 299 |
| 1731 | Gallery Entrance: 321 | 2239 | Gallery Entrance: 321 |
| 1732 | L Room Entrance: 322 | 2240 | L Room Entrance: 322 |
| 2241 | Least Blue Last: 3317 | ||
| 1733 | Liberated Entrance: 314 | 2242 | Liberated Entrance: 314 |
| 1734 | Lime Room Entrance: 305 | 2243 | Lime Room Entrance: 305 |
| 1735 | Link Area Entrance: 288 | 2244 | Link Area Entrance: 288 |
| 1736 | Literate Entrance: 316 | 2245 | Literate Entrance: 316 |
| 1737 | Near D Room Painting: 320 | 2246 | Near D Room Painting: 320 |
| 1738 | Noon Door: 295 | 2247 | Noon Door: 295 |
| 2248 | Noon Door Panels: 3312 | ||
| 1739 | Orange Door Hider: 304 | 2249 | Orange Door Hider: 304 |
| 1740 | Parthenon Entrance: 317 | 2250 | Parthenon Entrance: 317 |
| 2251 | Rabbit Hole Blank Puzzle: 3319 | ||
| 1741 | Rabbithole Door: 294 | 2252 | Rabbithole Door: 294 |
| 1742 | Red Alcove Exit: 291 | 2253 | Red Alcove Exit: 291 |
| 1743 | Red Blue Area Left Door: 302 | 2254 | Red Blue Area Left Door: 302 |
| @@ -1746,6 +2257,7 @@ maps: | |||
| 1746 | Revitalized Entrance: 306 | 2257 | Revitalized Entrance: 306 |
| 1747 | Right Eye Entrance: 301 | 2258 | Right Eye Entrance: 301 |
| 1748 | Scarf Door: 296 | 2259 | Scarf Door: 296 |
| 2260 | Scarf Door Panels: 3313 | ||
| 1749 | Second Room Left Door: 298 | 2261 | Second Room Left Door: 298 |
| 1750 | Second Room Right Door: 290 | 2262 | Second Room Right Door: 290 |
| 1751 | Shop Entrance: 313 | 2263 | Shop Entrance: 313 |
| @@ -1753,6 +2265,8 @@ maps: | |||
| 1753 | Third Eye Painting: 324 | 2265 | Third Eye Painting: 324 |
| 1754 | Trick Door: 287 | 2266 | Trick Door: 287 |
| 1755 | Trick To Shop Door: 289 | 2267 | Trick To Shop Door: 289 |
| 2268 | Wander Panels: 3314 | ||
| 2269 | Wrath Room Puzzles: 3320 | ||
| 1756 | X Area Entrance: 308 | 2270 | X Area Entrance: 308 |
| 1757 | the_extravagant: | 2271 | the_extravagant: |
| 1758 | rooms: | 2272 | rooms: |
| @@ -1798,6 +2312,33 @@ maps: | |||
| 1798 | panels: | 2312 | panels: |
| 1799 | CACTUS: 410 | 2313 | CACTUS: 410 |
| 1800 | TAIL: 411 | 2314 | TAIL: 411 |
| 2315 | the_fuzzy: | ||
| 2316 | rooms: | ||
| 2317 | Main Area: | ||
| 2318 | panels: | ||
| 2319 | ACHIEVES: 3033 | ||
| 2320 | BEFORE: 3028 | ||
| 2321 | BOTH: 3036 | ||
| 2322 | Blank: 3022 | ||
| 2323 | CAGED: 3027 | ||
| 2324 | COMBINED: 3032 | ||
| 2325 | DICE: 3026 | ||
| 2326 | FIRST: 3035 | ||
| 2327 | FORGED: 3030 | ||
| 2328 | LOTTO: 3024 | ||
| 2329 | OTHERS: 3031 | ||
| 2330 | TOED: 3029 | ||
| 2331 | TUTU: 3023 | ||
| 2332 | UNVEILED: 3034 | ||
| 2333 | WHERETO: 3025 | ||
| 2334 | ports: | ||
| 2335 | WORLDPORT: 3183 | ||
| 2336 | Mastery: | ||
| 2337 | masteries: | ||
| 2338 | MASTERY: 3037 | ||
| 2339 | doors: | ||
| 2340 | Black Panels: 3021 | ||
| 2341 | Green Panels: 3321 | ||
| 1801 | the_gallery: | 2342 | the_gallery: |
| 1802 | rooms: | 2343 | rooms: |
| 1803 | Back Room: | 2344 | Back Room: |
| @@ -1810,6 +2351,8 @@ maps: | |||
| 1810 | Main Area: | 2351 | Main Area: |
| 1811 | keyholders: | 2352 | keyholders: |
| 1812 | P: 2765 | 2353 | P: 2765 |
| 2354 | ports: | ||
| 2355 | ENTRY: 3184 | ||
| 1813 | doors: | 2356 | doors: |
| 1814 | Ancient Painting: 428 | 2357 | Ancient Painting: 428 |
| 1815 | Between Painting: 414 | 2358 | Between Painting: 414 |
| @@ -1835,6 +2378,8 @@ maps: | |||
| 1835 | The Whole Thing: | 2378 | The Whole Thing: |
| 1836 | panels: | 2379 | panels: |
| 1837 | PANEL: 434 | 2380 | PANEL: 434 |
| 2381 | doors: | ||
| 2382 | The Panel: 3322 | ||
| 1838 | the_graveyard: | 2383 | the_graveyard: |
| 1839 | rooms: | 2384 | rooms: |
| 1840 | Inside: | 2385 | Inside: |
| @@ -1844,34 +2389,34 @@ maps: | |||
| 1844 | panels: | 2389 | panels: |
| 1845 | FOOT: 436 | 2390 | FOOT: 436 |
| 1846 | SEVERE: 437 | 2391 | SEVERE: 437 |
| 2392 | doors: | ||
| 2393 | Remember Panel: 3323 | ||
| 1847 | the_great: | 2394 | the_great: |
| 1848 | rooms: | 2395 | rooms: |
| 1849 | Back Area: | 2396 | Back Area: |
| 1850 | panels: | 2397 | panels: |
| 1851 | Left Landscape Bottom: 482 | ||
| 1852 | Left Landscape Left: 483 | ||
| 1853 | Left Landscape Right: 481 | ||
| 1854 | Left Landscape Top: 480 | ||
| 1855 | PAINTING: 474 | 2398 | PAINTING: 474 |
| 1856 | PLANT: 472 | 2399 | PLANT: 472 |
| 1857 | Right Landscape Bottom: 486 | ||
| 1858 | Right Landscape Left: 487 | ||
| 1859 | Right Landscape Right: 485 | ||
| 1860 | Right Landscape Top: 484 | ||
| 1861 | TOWEL: 475 | 2400 | TOWEL: 475 |
| 1862 | TREE: 473 | 2401 | TREE: 473 |
| 1863 | Top Landscape Bottom: 478 | 2402 | ports: |
| 1864 | Top Landscape Left: 479 | 2403 | THREEDOORS: 3186 |
| 1865 | Top Landscape Right: 477 | 2404 | TOWER: 3187 |
| 1866 | Top Landscape Top: 476 | 2405 | TREE: 3188 |
| 2406 | UNKEMPT: 3185 | ||
| 1867 | Behind Question Area: | 2407 | Behind Question Area: |
| 1868 | panels: | 2408 | panels: |
| 1869 | DEW: 488 | 2409 | DEW: 488 |
| 1870 | NO: 490 | 2410 | NO: 490 |
| 1871 | YEW: 489 | 2411 | YEW: 489 |
| 2412 | Colorful Entrance: | ||
| 2413 | ports: | ||
| 2414 | COLORFUL: 3189 | ||
| 1872 | Daedalus Entrance: | 2415 | Daedalus Entrance: |
| 1873 | panels: | 2416 | panels: |
| 1874 | MISSING: 491 | 2417 | MISSING: 491 |
| 2418 | ports: | ||
| 2419 | DAEDALUS: 3190 | ||
| 1875 | East Landscape: | 2420 | East Landscape: |
| 1876 | panels: | 2421 | panels: |
| 1877 | COLOR: 492 | 2422 | COLOR: 492 |
| @@ -1879,6 +2424,8 @@ maps: | |||
| 1879 | Hive Entrance: | 2424 | Hive Entrance: |
| 1880 | panels: | 2425 | panels: |
| 1881 | LOST: 495 | 2426 | LOST: 495 |
| 2427 | ports: | ||
| 2428 | HIVE: 3191 | ||
| 1882 | Jail Part 1: | 2429 | Jail Part 1: |
| 1883 | panels: | 2430 | panels: |
| 1884 | DECATHLON: 506 | 2431 | DECATHLON: 506 |
| @@ -1903,6 +2450,9 @@ maps: | |||
| 1903 | NECROTIZE (2): 511 | 2450 | NECROTIZE (2): 511 |
| 1904 | PILGRIM: 513 | 2451 | PILGRIM: 513 |
| 1905 | TORMENTS: 512 | 2452 | TORMENTS: 512 |
| 2453 | Jubilant Entrance: | ||
| 2454 | ports: | ||
| 2455 | JUBILANT: 3192 | ||
| 1906 | Magnet Room: | 2456 | Magnet Room: |
| 1907 | panels: | 2457 | panels: |
| 1908 | AIRPLANE: 516 | 2458 | AIRPLANE: 516 |
| @@ -1939,6 +2489,12 @@ maps: | |||
| 1939 | SMILE: 539 | 2489 | SMILE: 539 |
| 1940 | WHY: 540 | 2490 | WHY: 540 |
| 1941 | YOU: 537 | 2491 | YOU: 537 |
| 2492 | ports: | ||
| 2493 | DIGITAL: 3197 | ||
| 2494 | ENTRY: 3193 | ||
| 2495 | KEEN: 3194 | ||
| 2496 | LINEAR: 3196 | ||
| 2497 | ORB: 3195 | ||
| 1942 | Maze Center: | 2498 | Maze Center: |
| 1943 | panels: | 2499 | panels: |
| 1944 | CHASE: 549 | 2500 | CHASE: 549 |
| @@ -1996,6 +2552,8 @@ maps: | |||
| 1996 | WEATHER: 568 | 2552 | WEATHER: 568 |
| 1997 | keyholders: | 2553 | keyholders: |
| 1998 | X: 2770 | 2554 | X: 2770 |
| 2555 | ports: | ||
| 2556 | INVISIBLE: 3198 | ||
| 1999 | Outside Jail: | 2557 | Outside Jail: |
| 2000 | panels: | 2558 | panels: |
| 2001 | GUT: 575 | 2559 | GUT: 575 |
| @@ -2009,6 +2567,9 @@ maps: | |||
| 2009 | FOUR: 581 | 2567 | FOUR: 581 |
| 2010 | HAVE: 580 | 2568 | HAVE: 580 |
| 2011 | TEN: 583 | 2569 | TEN: 583 |
| 2570 | Purple Room: | ||
| 2571 | ports: | ||
| 2572 | DAEDALUS: 3199 | ||
| 2012 | Question Room How: | 2573 | Question Room How: |
| 2013 | panels: | 2574 | panels: |
| 2014 | QUESTION: 584 | 2575 | QUESTION: 584 |
| @@ -2021,6 +2582,26 @@ maps: | |||
| 2021 | Question Room Who: | 2582 | Question Room Who: |
| 2022 | panels: | 2583 | panels: |
| 2023 | QUESTION: 587 | 2584 | QUESTION: 587 |
| 2585 | Salmon Room: | ||
| 2586 | ports: | ||
| 2587 | BETWEEN: 3200 | ||
| 2588 | Talented Entrance: | ||
| 2589 | ports: | ||
| 2590 | TALENTED: 3201 | ||
| 2591 | The Landscapes: | ||
| 2592 | panels: | ||
| 2593 | Left Landscape Bottom: 482 | ||
| 2594 | Left Landscape Left: 483 | ||
| 2595 | Left Landscape Right: 481 | ||
| 2596 | Left Landscape Top: 480 | ||
| 2597 | Right Landscape Bottom: 486 | ||
| 2598 | Right Landscape Left: 487 | ||
| 2599 | Right Landscape Right: 485 | ||
| 2600 | Right Landscape Top: 484 | ||
| 2601 | Top Landscape Bottom: 478 | ||
| 2602 | Top Landscape Left: 479 | ||
| 2603 | Top Landscape Right: 477 | ||
| 2604 | Top Landscape Top: 476 | ||
| 2024 | Under Question Room: | 2605 | Under Question Room: |
| 2025 | panels: | 2606 | panels: |
| 2026 | QUESTION: 588 | 2607 | QUESTION: 588 |
| @@ -2033,6 +2614,10 @@ maps: | |||
| 2033 | RIGHT: 591 | 2614 | RIGHT: 591 |
| 2034 | SAVORY: 592 | 2615 | SAVORY: 592 |
| 2035 | TEACH: 590 | 2616 | TEACH: 590 |
| 2617 | ports: | ||
| 2618 | CC: 3203 | ||
| 2619 | IMPRESSIVE: 3202 | ||
| 2620 | PARTIAL: 3204 | ||
| 2036 | Whole Room: | 2621 | Whole Room: |
| 2037 | panels: | 2622 | panels: |
| 2038 | BATHROOM: 604 | 2623 | BATHROOM: 604 |
| @@ -2068,13 +2653,19 @@ maps: | |||
| 2068 | SHIFT: 624 | 2653 | SHIFT: 624 |
| 2069 | doors: | 2654 | doors: |
| 2070 | Back Area Entrance: 439 | 2655 | Back Area Entrance: 439 |
| 2656 | Behind Orb Panel: 3336 | ||
| 2657 | Behind Question Room Panels: 3332 | ||
| 2071 | Between Entrance: 440 | 2658 | Between Entrance: 440 |
| 2072 | Big Y: 462 | 2659 | Big Y: 462 |
| 2660 | Broken Shed Panels: 3333 | ||
| 2073 | Building Building Gravestone: 468 | 2661 | Building Building Gravestone: 468 |
| 2074 | Colorful Entrance: 455 | 2662 | Colorful Entrance: 455 |
| 2075 | Control Center Gray Door: 446 | 2663 | Control Center Gray Door: 446 |
| 2664 | Control Center Gray Panel: 3326 | ||
| 2076 | Control Center Purple Door: 445 | 2665 | Control Center Purple Door: 445 |
| 2666 | Control Center Purple Panel: 3327 | ||
| 2077 | Control Center Red Door: 447 | 2667 | Control Center Red Door: 447 |
| 2668 | Control Center Red Panel: 3328 | ||
| 2078 | Courtyard Entrance: 442 | 2669 | Courtyard Entrance: 442 |
| 2079 | Courtyard Side Door: 461 | 2670 | Courtyard Side Door: 461 |
| 2080 | Daedalus Entrance: 448 | 2671 | Daedalus Entrance: 448 |
| @@ -2084,10 +2675,12 @@ maps: | |||
| 2084 | Into The Mouth Gravestone: 457 | 2675 | Into The Mouth Gravestone: 457 |
| 2085 | Invisible Entrance: 465 | 2676 | Invisible Entrance: 465 |
| 2086 | Jail Entrance: 451 | 2677 | Jail Entrance: 451 |
| 2087 | Lavender Cube: 469 | ||
| 2088 | Magnet Room Entrance: 449 | 2678 | Magnet Room Entrance: 449 |
| 2679 | Mistreat Panel: 3329 | ||
| 2680 | Nature Panels: 3334 | ||
| 2089 | Nature Room Door: 466 | 2681 | Nature Room Door: 466 |
| 2090 | Nature Room Panels: 467 | 2682 | Nature Room Panels: 467 |
| 2683 | Near Linear Panels: 3324 | ||
| 2091 | Near UC Painting Door: 441 | 2684 | Near UC Painting Door: 441 |
| 2092 | North Landscape Entrance: 456 | 2685 | North Landscape Entrance: 456 |
| 2093 | Pillar Room Entrance: 450 | 2686 | Pillar Room Entrance: 450 |
| @@ -2096,11 +2689,21 @@ maps: | |||
| 2096 | Savory Painting: 452 | 2689 | Savory Painting: 452 |
| 2097 | Spiral Painting: 471 | 2690 | Spiral Painting: 471 |
| 2098 | Talented Entrance: 463 | 2691 | Talented Entrance: 463 |
| 2692 | Teal Panel: 3335 | ||
| 2099 | The Landscapes Gravestone: 458 | 2693 | The Landscapes Gravestone: 458 |
| 2100 | The Maze Gravestone: 460 | 2694 | The Maze Gravestone: 460 |
| 2101 | Tower Entrance: 459 | 2695 | Tower Entrance: 459 |
| 2696 | Tower Panels: 3330 | ||
| 2697 | Tree Panels: 3331 | ||
| 2102 | West/East Divider: 443 | 2698 | West/East Divider: 443 |
| 2699 | Why Is It Not Red: 3325 | ||
| 2103 | Zero Room Panels: 470 | 2700 | Zero Room Panels: 470 |
| 2701 | the_hinterlands: | ||
| 2702 | rooms: | ||
| 2703 | Main Area: | ||
| 2704 | ports: | ||
| 2705 | LEFT: 3206 | ||
| 2706 | RIGHT: 3205 | ||
| 2104 | the_hive: | 2707 | the_hive: |
| 2105 | rooms: | 2708 | rooms: |
| 2106 | Main Area: | 2709 | Main Area: |
| @@ -2145,6 +2748,11 @@ maps: | |||
| 2145 | YELL: 636 | 2748 | YELL: 636 |
| 2146 | keyholders: | 2749 | keyholders: |
| 2147 | B: 2769 | 2750 | B: 2769 |
| 2751 | ports: | ||
| 2752 | DAED1: 3207 | ||
| 2753 | DAED2: 3208 | ||
| 2754 | DAED3: 3209 | ||
| 2755 | GREAT: 3210 | ||
| 2148 | Mastery Room: | 2756 | Mastery Room: |
| 2149 | masteries: | 2757 | masteries: |
| 2150 | MASTERY: 666 | 2758 | MASTERY: 666 |
| @@ -2163,28 +2771,40 @@ maps: | |||
| 2163 | LEFT: 676 | 2771 | LEFT: 676 |
| 2164 | RETURN: 674 | 2772 | RETURN: 674 |
| 2165 | TO: 675 | 2773 | TO: 675 |
| 2774 | ports: | ||
| 2775 | PLAZA: 3211 | ||
| 2166 | Lobby: | 2776 | Lobby: |
| 2167 | panels: | 2777 | panels: |
| 2168 | RIGHT: 677 | 2778 | RIGHT: 677 |
| 2779 | ports: | ||
| 2780 | GREAT: 3212 | ||
| 2169 | Side Area: | 2781 | Side Area: |
| 2170 | panels: | 2782 | panels: |
| 2171 | COLOR: 680 | 2783 | COLOR: 680 |
| 2784 | ports: | ||
| 2785 | FOURROOMS: 3213 | ||
| 2172 | WM Room: | 2786 | WM Room: |
| 2173 | panels: | 2787 | panels: |
| 2174 | LEFT: 682 | 2788 | LEFT: 682 |
| 2175 | RIGHT: 683 | 2789 | RIGHT: 683 |
| 2176 | doors: | 2790 | doors: |
| 2177 | Control Center Green Door: 673 | 2791 | Control Center Green Door: 673 |
| 2792 | Control Center Green Panel: 3338 | ||
| 2178 | Front Door: 671 | 2793 | Front Door: 671 |
| 2794 | Green Eye Panels: 3337 | ||
| 2179 | Side Door: 672 | 2795 | Side Door: 672 |
| 2180 | the_invisible: | 2796 | the_invisible: |
| 2181 | rooms: | 2797 | rooms: |
| 2182 | Entrance: | 2798 | Entrance: |
| 2183 | panels: | 2799 | panels: |
| 2184 | VISIBLE: 685 | 2800 | VISIBLE: 685 |
| 2801 | ports: | ||
| 2802 | ENTRY: 3214 | ||
| 2185 | Maze: | 2803 | Maze: |
| 2186 | masteries: | 2804 | masteries: |
| 2187 | MASTERY: 686 | 2805 | MASTERY: 686 |
| 2806 | ports: | ||
| 2807 | ENTRY: 3215 | ||
| 2188 | doors: | 2808 | doors: |
| 2189 | Maze Entrance: 684 | 2809 | Maze Entrance: 684 |
| 2190 | the_jubilant: | 2810 | the_jubilant: |
| @@ -2203,6 +2823,8 @@ maps: | |||
| 2203 | SPRINT: 695 | 2823 | SPRINT: 695 |
| 2204 | TREE: 698 | 2824 | TREE: 698 |
| 2205 | UNFAIR: 694 | 2825 | UNFAIR: 694 |
| 2826 | ports: | ||
| 2827 | GREAT: 3216 | ||
| 2206 | Side Area: | 2828 | Side Area: |
| 2207 | panels: | 2829 | panels: |
| 2208 | CALL: 704 | 2830 | CALL: 704 |
| @@ -2215,6 +2837,7 @@ maps: | |||
| 2215 | J: 2772 | 2837 | J: 2772 |
| 2216 | doors: | 2838 | doors: |
| 2217 | Side Door: 687 | 2839 | Side Door: 687 |
| 2840 | Side Room Puzzles: 3339 | ||
| 2218 | the_keen: | 2841 | the_keen: |
| 2219 | rooms: | 2842 | rooms: |
| 2220 | Main Area: | 2843 | Main Area: |
| @@ -2228,6 +2851,8 @@ maps: | |||
| 2228 | TIN (3): 711 | 2851 | TIN (3): 711 |
| 2229 | TIN (4): 712 | 2852 | TIN (4): 712 |
| 2230 | TIN (5): 713 | 2853 | TIN (5): 713 |
| 2854 | ports: | ||
| 2855 | GREAT: 3217 | ||
| 2231 | doors: | 2856 | doors: |
| 2232 | All Panels: 707 | 2857 | All Panels: 707 |
| 2233 | the_liberated: | 2858 | the_liberated: |
| @@ -2242,6 +2867,8 @@ maps: | |||
| 2242 | PERSON: 720 | 2867 | PERSON: 720 |
| 2243 | SAND: 723 | 2868 | SAND: 723 |
| 2244 | WOLF: 725 | 2869 | WOLF: 725 |
| 2870 | ports: | ||
| 2871 | ENTRY: 3218 | ||
| 2245 | doors: | 2872 | doors: |
| 2246 | Door: 718 | 2873 | Door: 718 |
| 2247 | the_linear: | 2874 | the_linear: |
| @@ -2256,6 +2883,8 @@ maps: | |||
| 2256 | NOR: 735 | 2883 | NOR: 735 |
| 2257 | ROT: 731 | 2884 | ROT: 731 |
| 2258 | TON: 730 | 2885 | TON: 730 |
| 2886 | ports: | ||
| 2887 | GREAT: 3219 | ||
| 2259 | doors: | 2888 | doors: |
| 2260 | Behind The Keen Gravestone: 727 | 2889 | Behind The Keen Gravestone: 727 |
| 2261 | the_lionized: | 2890 | the_lionized: |
| @@ -2270,6 +2899,8 @@ maps: | |||
| 2270 | LION: 741 | 2899 | LION: 741 |
| 2271 | PIG: 743 | 2900 | PIG: 743 |
| 2272 | ROCK: 740 | 2901 | ROCK: 740 |
| 2902 | ports: | ||
| 2903 | ENTRY: 3220 | ||
| 2273 | the_literate: | 2904 | the_literate: |
| 2274 | rooms: | 2905 | rooms: |
| 2275 | Puzzle Room: | 2906 | Puzzle Room: |
| @@ -2282,6 +2913,8 @@ maps: | |||
| 2282 | SAND: 750 | 2913 | SAND: 750 |
| 2283 | STICK: 752 | 2914 | STICK: 752 |
| 2284 | WATER: 746 | 2915 | WATER: 746 |
| 2916 | ports: | ||
| 2917 | ENTRY: 3221 | ||
| 2285 | doors: | 2918 | doors: |
| 2286 | Door: 745 | 2919 | Door: 745 |
| 2287 | the_lively: | 2920 | the_lively: |
| @@ -2296,6 +2929,8 @@ maps: | |||
| 2296 | ROOSTER: 762 | 2929 | ROOSTER: 762 |
| 2297 | SON: 759 | 2930 | SON: 759 |
| 2298 | SOPRANO: 757 | 2931 | SOPRANO: 757 |
| 2932 | ports: | ||
| 2933 | BETWEEN: 3222 | ||
| 2299 | the_nuanced: | 2934 | the_nuanced: |
| 2300 | rooms: | 2935 | rooms: |
| 2301 | Back Room: | 2936 | Back Room: |
| @@ -2326,12 +2961,18 @@ maps: | |||
| 2326 | TORE: 787 | 2961 | TORE: 787 |
| 2327 | keyholders: | 2962 | keyholders: |
| 2328 | S: 2767 | 2963 | S: 2767 |
| 2964 | ports: | ||
| 2965 | UNYIELDING: 3223 | ||
| 2329 | doors: | 2966 | doors: |
| 2330 | Left Room Puzzles: 763 | 2967 | Blue Side Puzzles: 763 |
| 2968 | Green Side Puzzles: 764 | ||
| 2331 | Main Room Door: 2750 | 2969 | Main Room Door: 2750 |
| 2332 | Right Room Puzzles: 764 | 2970 | Stores Panel: 3340 |
| 2333 | the_orb: | 2971 | the_orb: |
| 2334 | rooms: | 2972 | rooms: |
| 2973 | B Room: | ||
| 2974 | ports: | ||
| 2975 | FINAL: 3224 | ||
| 2335 | Main Area: | 2976 | Main Area: |
| 2336 | panels: | 2977 | panels: |
| 2337 | ACT: 799 | 2978 | ACT: 799 |
| @@ -2346,6 +2987,11 @@ maps: | |||
| 2346 | STRIKE: 790 | 2987 | STRIKE: 790 |
| 2347 | THICK: 797 | 2988 | THICK: 797 |
| 2348 | THIN: 793 | 2989 | THIN: 793 |
| 2990 | ports: | ||
| 2991 | GREAT: 3225 | ||
| 2992 | Middle Room: | ||
| 2993 | ports: | ||
| 2994 | MID: 3226 | ||
| 2349 | the_owl: | 2995 | the_owl: |
| 2350 | rooms: | 2996 | rooms: |
| 2351 | Blue Room: | 2997 | Blue Room: |
| @@ -2373,6 +3019,11 @@ maps: | |||
| 2373 | SKETCH: 832 | 3019 | SKETCH: 832 |
| 2374 | WHITE: 824 | 3020 | WHITE: 824 |
| 2375 | WING: 826 | 3021 | WING: 826 |
| 3022 | ports: | ||
| 3023 | FOURROOMS: 3227 | ||
| 3024 | Magenta Hallway: | ||
| 3025 | ports: | ||
| 3026 | STURDY: 3228 | ||
| 2376 | R1C4 Left: | 3027 | R1C4 Left: |
| 2377 | panels: | 3028 | panels: |
| 2378 | STENCIL: 840 | 3029 | STENCIL: 840 |
| @@ -2385,6 +3036,8 @@ maps: | |||
| 2385 | R2C2 Bottom: | 3036 | R2C2 Bottom: |
| 2386 | panels: | 3037 | panels: |
| 2387 | FOUL: 844 | 3038 | FOUL: 844 |
| 3039 | ports: | ||
| 3040 | GALLERY: 3229 | ||
| 2388 | R2C2 Top: | 3041 | R2C2 Top: |
| 2389 | panels: | 3042 | panels: |
| 2390 | CRUSH: 845 | 3043 | CRUSH: 845 |
| @@ -2402,12 +3055,22 @@ maps: | |||
| 2402 | Blue Owl: 818 | 3055 | Blue Owl: 818 |
| 2403 | Brush Door: 804 | 3056 | Brush Door: 804 |
| 2404 | Control Center Magenta Door: 812 | 3057 | Control Center Magenta Door: 812 |
| 3058 | Control Center Magenta Panel: 3343 | ||
| 2405 | First Door: 808 | 3059 | First Door: 808 |
| 2406 | First Room Shortcut: 807 | 3060 | First Room Shortcut: 807 |
| 2407 | Gray Bottom Door: 811 | 3061 | Gray Bottom Door: 811 |
| 2408 | Gray Owl: 814 | 3062 | Gray Owl: 814 |
| 2409 | Gray Top Door: 810 | 3063 | Gray Top Door: 810 |
| 3064 | Near Z1 Panel: 3350 | ||
| 2410 | Orange Owl: 815 | 3065 | Orange Owl: 815 |
| 3066 | R1C1 Panels: 3341 | ||
| 3067 | R1C2 Panels: 3342 | ||
| 3068 | R1C3 Panels: 3344 | ||
| 3069 | R1C4 Panels: 3345 | ||
| 3070 | R2C1 Panels: 3346 | ||
| 3071 | R2C2 Panels: 3347 | ||
| 3072 | R2C3 Panels: 3348 | ||
| 3073 | R2C4 Panels: 3349 | ||
| 2411 | Sky Bottom Doors: 806 | 3074 | Sky Bottom Doors: 806 |
| 2412 | Sky Owl: 813 | 3075 | Sky Owl: 813 |
| 2413 | Sky Top Doors: 805 | 3076 | Sky Top Doors: 805 |
| @@ -2427,16 +3090,23 @@ maps: | |||
| 2427 | CLEOPATRA: 859 | 3090 | CLEOPATRA: 859 |
| 2428 | NAPOLEON: 860 | 3091 | NAPOLEON: 860 |
| 2429 | XERXES: 857 | 3092 | XERXES: 857 |
| 3093 | ports: | ||
| 3094 | ENTRY: 3231 | ||
| 3095 | GALLERY: 3230 | ||
| 3096 | REVITALIZED: 3232 | ||
| 2430 | U Keyholder: | 3097 | U Keyholder: |
| 2431 | keyholders: | 3098 | keyholders: |
| 2432 | U: 2777 | 3099 | U: 2777 |
| 2433 | doors: | 3100 | doors: |
| 2434 | K2 Door: 852 | 3101 | K2 Door: 852 |
| 3102 | Lavender Area Puzzles: 3351 | ||
| 2435 | the_partial: | 3103 | the_partial: |
| 2436 | rooms: | 3104 | rooms: |
| 2437 | Control Center Entrance: | 3105 | Control Center Entrance: |
| 2438 | panels: | 3106 | panels: |
| 2439 | RETURN: 867 | 3107 | RETURN: 867 |
| 3108 | ports: | ||
| 3109 | CC: 3233 | ||
| 2440 | Obverse Side: | 3110 | Obverse Side: |
| 2441 | panels: | 3111 | panels: |
| 2442 | CUT: 881 | 3112 | CUT: 881 |
| @@ -2455,6 +3125,8 @@ maps: | |||
| 2455 | UP: 870 | 3125 | UP: 870 |
| 2456 | keyholders: | 3126 | keyholders: |
| 2457 | L: 2771 | 3127 | L: 2771 |
| 3128 | ports: | ||
| 3129 | GREAT: 3234 | ||
| 2458 | Reverse Side: | 3130 | Reverse Side: |
| 2459 | panels: | 3131 | panels: |
| 2460 | BRO: 884 | 3132 | BRO: 884 |
| @@ -2464,8 +3136,14 @@ maps: | |||
| 2464 | doors: | 3136 | doors: |
| 2465 | Control Center Entrance: 865 | 3137 | Control Center Entrance: 865 |
| 2466 | F Door: 866 | 3138 | F Door: 866 |
| 3139 | L Entered: 2843 | ||
| 2467 | Main Room Puzzles: 863 | 3140 | Main Room Puzzles: 863 |
| 2468 | P Door: 864 | 3141 | P Door: 864 |
| 3142 | the_perceptive: | ||
| 3143 | rooms: | ||
| 3144 | Main Area: | ||
| 3145 | ports: | ||
| 3146 | CC: 3235 | ||
| 2469 | the_plaza: | 3147 | the_plaza: |
| 2470 | rooms: | 3148 | rooms: |
| 2471 | Bottom Left Room: | 3149 | Bottom Left Room: |
| @@ -2499,18 +3177,28 @@ maps: | |||
| 2499 | ASTOUNDING: 919 | 3177 | ASTOUNDING: 919 |
| 2500 | COURTYARD: 918 | 3178 | COURTYARD: 918 |
| 2501 | INFLEXIBLE: 920 | 3179 | INFLEXIBLE: 920 |
| 3180 | ports: | ||
| 3181 | BETWEEN: 3238 | ||
| 3182 | IMPRESSIVE: 3237 | ||
| 3183 | UNYIELDING: 3236 | ||
| 2502 | Mastery: | 3184 | Mastery: |
| 2503 | masteries: | 3185 | masteries: |
| 2504 | MASTERY: 923 | 3186 | MASTERY: 923 |
| 2505 | Repetitive Entrance: | 3187 | Repetitive Entrance: |
| 2506 | panels: | 3188 | panels: |
| 2507 | TEDIOUS: 924 | 3189 | TEDIOUS: 924 |
| 3190 | ports: | ||
| 3191 | REPETITIVE: 3239 | ||
| 2508 | Sirenic Entrance: | 3192 | Sirenic Entrance: |
| 2509 | panels: | 3193 | panels: |
| 2510 | SIREN: 925 | 3194 | SIREN: 925 |
| 3195 | ports: | ||
| 3196 | SIRENIC: 3240 | ||
| 2511 | Symbolic Entrance: | 3197 | Symbolic Entrance: |
| 2512 | panels: | 3198 | panels: |
| 2513 | FIGURATIVE: 926 | 3199 | FIGURATIVE: 926 |
| 3200 | ports: | ||
| 3201 | SYMBOLIC: 3241 | ||
| 2514 | Top Left Room: | 3202 | Top Left Room: |
| 2515 | panels: | 3203 | panels: |
| 2516 | BACKPACK: 939 | 3204 | BACKPACK: 939 |
| @@ -2559,17 +3247,21 @@ maps: | |||
| 2559 | TYPIST BEAR RIGHT WING: 968 | 3247 | TYPIST BEAR RIGHT WING: 968 |
| 2560 | WING: 950 | 3248 | WING: 950 |
| 2561 | doors: | 3249 | doors: |
| 2562 | Bottom Left Door: 894 | 3250 | Near Broken Portal Panel: 3355 |
| 2563 | Bottom Left Puzzles: 898 | 3251 | Near Repetitive Panel: 3354 |
| 2564 | Bottom Right Door: 895 | 3252 | Near Sirenic Panel: 3352 |
| 2565 | Bottom Right Puzzles: 899 | 3253 | Near Symbolic Panel: 3353 |
| 3254 | Northeast Door: 893 | ||
| 3255 | Northeast Puzzles: 897 | ||
| 3256 | Northwest Door: 892 | ||
| 3257 | Northwest Puzzles: 896 | ||
| 2566 | Repetitive Entrance: 888 | 3258 | Repetitive Entrance: 888 |
| 2567 | Sirenic Entrance: 890 | 3259 | Sirenic Entrance: 890 |
| 3260 | Southeast Door: 895 | ||
| 3261 | Southeast Puzzles: 899 | ||
| 3262 | Southwest Door: 894 | ||
| 3263 | Southwest Puzzles: 898 | ||
| 2568 | Symbolic Entrance: 889 | 3264 | Symbolic Entrance: 889 |
| 2569 | Top Left Door: 892 | ||
| 2570 | Top Left Puzzles: 896 | ||
| 2571 | Top Right Door: 893 | ||
| 2572 | Top Right Puzzles: 897 | ||
| 2573 | Turtle Entrance: 891 | 3265 | Turtle Entrance: 891 |
| 2574 | the_quiet: | 3266 | the_quiet: |
| 2575 | rooms: | 3267 | rooms: |
| @@ -2590,6 +3282,8 @@ maps: | |||
| 2590 | RODENT: 972 | 3282 | RODENT: 972 |
| 2591 | RULE: 974 | 3283 | RULE: 974 |
| 2592 | SOLID: 971 | 3284 | SOLID: 971 |
| 3285 | ports: | ||
| 3286 | DAEDALUS: 3242 | ||
| 2593 | doors: | 3287 | doors: |
| 2594 | Side Door: 970 | 3288 | Side Door: 970 |
| 2595 | the_relentless: | 3289 | the_relentless: |
| @@ -2646,13 +3340,17 @@ maps: | |||
| 2646 | HIDE (2): 1021 | 3340 | HIDE (2): 1021 |
| 2647 | MORE: 1022 | 3341 | MORE: 1022 |
| 2648 | doors: | 3342 | doors: |
| 3343 | Left Only Puzzles: 3020 | ||
| 2649 | Left/Turn Door: 984 | 3344 | Left/Turn Door: 984 |
| 3345 | Shop Only Puzzles: 3019 | ||
| 3346 | Turn Only Puzzles: 3018 | ||
| 2650 | Turn/Shop Door: 985 | 3347 | Turn/Shop Door: 985 |
| 2651 | the_repetitive: | 3348 | the_repetitive: |
| 2652 | rooms: | 3349 | rooms: |
| 2653 | Anti Room: | 3350 | Anti Room: |
| 2654 | panels: | 3351 | panels: |
| 2655 | EYE: 1041 | 3352 | EYE (1): 1041 |
| 3353 | EYE (2): 2813 | ||
| 2656 | HA (1): 1035 | 3354 | HA (1): 1035 |
| 2657 | HA (2): 1036 | 3355 | HA (2): 1036 |
| 2658 | HA (3): 1037 | 3356 | HA (3): 1037 |
| @@ -2690,6 +3388,9 @@ maps: | |||
| 2690 | TO (2): 1056 | 3388 | TO (2): 1056 |
| 2691 | TUTU (1): 1054 | 3389 | TUTU (1): 1054 |
| 2692 | TUTU (2): 1068 | 3390 | TUTU (2): 1068 |
| 3391 | Entry Connector: | ||
| 3392 | ports: | ||
| 3393 | ENTRY: 3243 | ||
| 2693 | Lime Room: | 3394 | Lime Room: |
| 2694 | panels: | 3395 | panels: |
| 2695 | BIRD: 1074 | 3396 | BIRD: 1074 |
| @@ -2742,9 +3443,14 @@ maps: | |||
| 2742 | MISHMASH: 1114 | 3443 | MISHMASH: 1114 |
| 2743 | QUESTION: 1105 | 3444 | QUESTION: 1105 |
| 2744 | RICHES: 1112 | 3445 | RICHES: 1112 |
| 3446 | ports: | ||
| 3447 | CC: 3244 | ||
| 2745 | Mastery Room: | 3448 | Mastery Room: |
| 2746 | masteries: | 3449 | masteries: |
| 2747 | MASTERY: 1116 | 3450 | MASTERY: 1116 |
| 3451 | Plaza Connector: | ||
| 3452 | ports: | ||
| 3453 | PLAZA: 3245 | ||
| 2748 | Yellow Room: | 3454 | Yellow Room: |
| 2749 | panels: | 3455 | panels: |
| 2750 | 3D: 1123 | 3456 | 3D: 1123 |
| @@ -2759,12 +3465,16 @@ maps: | |||
| 2759 | W: 1117 | 3465 | W: 1117 |
| 2760 | ZEROING: 1118 | 3466 | ZEROING: 1118 |
| 2761 | doors: | 3467 | doors: |
| 3468 | Anti-Collectable: 2812 | ||
| 2762 | Anti-Collectable Room: 1025 | 3469 | Anti-Collectable Room: 1025 |
| 3470 | Anti-Collectable Room Panels: 3358 | ||
| 2763 | Black Hallway: 2780 | 3471 | Black Hallway: 2780 |
| 2764 | Cyan Door: 1028 | 3472 | Cyan Door: 1028 |
| 2765 | Cyan Puzzles: 1032 | 3473 | Cyan Puzzles: 1032 |
| 2766 | Dot Area Entrance: 1026 | 3474 | Dot Area Entrance: 1026 |
| 2767 | Entry Entrance: 1023 | 3475 | Entry Entrance: 1023 |
| 3476 | H2 Room Puzzles: 3357 | ||
| 3477 | Hots Panels: 3356 | ||
| 2768 | Lime Door: 1027 | 3478 | Lime Door: 1027 |
| 2769 | Lime Puzzles: 1031 | 3479 | Lime Puzzles: 1031 |
| 2770 | Magenta Door: 1029 | 3480 | Magenta Door: 1029 |
| @@ -2776,6 +3486,8 @@ maps: | |||
| 2776 | Bye Room: | 3486 | Bye Room: |
| 2777 | panels: | 3487 | panels: |
| 2778 | BYE: 1129 | 3488 | BYE: 1129 |
| 3489 | ports: | ||
| 3490 | PARTHENON: 3246 | ||
| 2779 | Hidden Room: | 3491 | Hidden Room: |
| 2780 | panels: | 3492 | panels: |
| 2781 | HIDDEN: 1130 | 3493 | HIDDEN: 1130 |
| @@ -2824,9 +3536,12 @@ maps: | |||
| 2824 | TADPOLES: 1159 | 3536 | TADPOLES: 1159 |
| 2825 | keyholders: | 3537 | keyholders: |
| 2826 | N: 2779 | 3538 | N: 2779 |
| 3539 | ports: | ||
| 3540 | ENTRY: 3247 | ||
| 2827 | doors: | 3541 | doors: |
| 2828 | Books Puzzles: 1136 | 3542 | Books Puzzles: 1136 |
| 2829 | Games Puzzles: 1137 | 3543 | Games Puzzles: 1137 |
| 3544 | N Entered: 2971 | ||
| 2830 | the_sirenic: | 3545 | the_sirenic: |
| 2831 | rooms: | 3546 | rooms: |
| 2832 | Mastery: | 3547 | Mastery: |
| @@ -2853,8 +3568,62 @@ maps: | |||
| 2853 | panels: | 3568 | panels: |
| 2854 | Flipped: 1178 | 3569 | Flipped: 1178 |
| 2855 | Obverse: 1179 | 3570 | Obverse: 1179 |
| 3571 | ports: | ||
| 3572 | PLAZA: 3248 | ||
| 2856 | doors: | 3573 | doors: |
| 2857 | Entrance: 1161 | 3574 | Entrance: 1161 |
| 3575 | the_stellar: | ||
| 3576 | rooms: | ||
| 3577 | Blue Panel: | ||
| 3578 | panels: | ||
| 3579 | BLUE: 2996 | ||
| 3580 | Connected Area: | ||
| 3581 | panels: | ||
| 3582 | BEHIND: 3003 | ||
| 3583 | Blank: 3004 | ||
| 3584 | GREETINGS: 3002 | ||
| 3585 | HERE: 2997 | ||
| 3586 | HI: 3000 | ||
| 3587 | QUESTION (1): 2999 | ||
| 3588 | QUESTION (2): 3016 | ||
| 3589 | START: 3005 | ||
| 3590 | WHERE: 3001 | ||
| 3591 | Green Area: | ||
| 3592 | panels: | ||
| 3593 | STRAYS: 3006 | ||
| 3594 | Green Panel: | ||
| 3595 | panels: | ||
| 3596 | GREEN: 3007 | ||
| 3597 | Hi Room: | ||
| 3598 | panels: | ||
| 3599 | HI: 3008 | ||
| 3600 | Mastery: | ||
| 3601 | masteries: | ||
| 3602 | MASTERY: 3009 | ||
| 3603 | Old Crossroads: | ||
| 3604 | panels: | ||
| 3605 | DOORWAY: 3010 | ||
| 3606 | Orange Panel: | ||
| 3607 | panels: | ||
| 3608 | ORANGE: 3011 | ||
| 3609 | Purple Panel: | ||
| 3610 | panels: | ||
| 3611 | PURPLE: 3012 | ||
| 3612 | Red Panel: | ||
| 3613 | panels: | ||
| 3614 | RED: 3013 | ||
| 3615 | Starting Room: | ||
| 3616 | panels: | ||
| 3617 | STARLIKE: 3014 | ||
| 3618 | ports: | ||
| 3619 | WORLDPORT: 3249 | ||
| 3620 | Yellow Panel: | ||
| 3621 | panels: | ||
| 3622 | YELLOW: 3015 | ||
| 3623 | doors: | ||
| 3624 | Entrance: 2995 | ||
| 3625 | Question Panels: 3017 | ||
| 3626 | Welcome Back Panels: 3359 | ||
| 2858 | the_stormy: | 3627 | the_stormy: |
| 2859 | rooms: | 3628 | rooms: |
| 2860 | Center: | 3629 | Center: |
| @@ -2863,6 +3632,8 @@ maps: | |||
| 2863 | REACTOR: 1180 | 3632 | REACTOR: 1180 |
| 2864 | VOLCANO: 1181 | 3633 | VOLCANO: 1181 |
| 2865 | WIND: 1183 | 3634 | WIND: 1183 |
| 3635 | ports: | ||
| 3636 | ENTRY: 3250 | ||
| 2866 | Nuclear Side: | 3637 | Nuclear Side: |
| 2867 | panels: | 3638 | panels: |
| 2868 | GERM: 1184 | 3639 | GERM: 1184 |
| @@ -2895,6 +3666,9 @@ maps: | |||
| 2895 | MOVE (6): 1198 | 3666 | MOVE (6): 1198 |
| 2896 | MOVE (7): 1199 | 3667 | MOVE (7): 1199 |
| 2897 | MOVE (8): 1200 | 3668 | MOVE (8): 1200 |
| 3669 | ports: | ||
| 3670 | COLORFUL: 3252 | ||
| 3671 | OWL: 3251 | ||
| 2898 | S2 Area: | 3672 | S2 Area: |
| 2899 | panels: | 3673 | panels: |
| 2900 | COLORS: 1201 | 3674 | COLORS: 1201 |
| @@ -2903,6 +3677,8 @@ maps: | |||
| 2903 | Entrance: | 3677 | Entrance: |
| 2904 | panels: | 3678 | panels: |
| 2905 | SUN: 1212 | 3679 | SUN: 1212 |
| 3680 | ports: | ||
| 3681 | UNKEMPT: 3253 | ||
| 2906 | Mastery: | 3682 | Mastery: |
| 2907 | masteries: | 3683 | masteries: |
| 2908 | MASTERY: 1213 | 3684 | MASTERY: 1213 |
| @@ -2949,6 +3725,9 @@ maps: | |||
| 2949 | SQUISH: 1241 | 3725 | SQUISH: 1241 |
| 2950 | VEGETABLE: 1232 | 3726 | VEGETABLE: 1232 |
| 2951 | WATER: 1226 | 3727 | WATER: 1226 |
| 3728 | ports: | ||
| 3729 | EXIT1: 3254 | ||
| 3730 | EXIT2: 3255 | ||
| 2952 | the_symbolic: | 3731 | the_symbolic: |
| 2953 | rooms: | 3732 | rooms: |
| 2954 | Black Room: | 3733 | Black Room: |
| @@ -3130,6 +3909,8 @@ maps: | |||
| 3130 | White Room: | 3909 | White Room: |
| 3131 | panels: | 3910 | panels: |
| 3132 | WRITE: 2425 | 3911 | WRITE: 2425 |
| 3912 | ports: | ||
| 3913 | PLAZA: 3256 | ||
| 3133 | Yellow Room: | 3914 | Yellow Room: |
| 3134 | panels: | 3915 | panels: |
| 3135 | WHOLE: 2426 | 3916 | WHOLE: 2426 |
| @@ -3173,9 +3954,12 @@ maps: | |||
| 3173 | WIFE (Brown): 2447 | 3954 | WIFE (Brown): 2447 |
| 3174 | keyholders: | 3955 | keyholders: |
| 3175 | Y: 2764 | 3956 | Y: 2764 |
| 3957 | ports: | ||
| 3958 | GREAT: 3257 | ||
| 3176 | doors: | 3959 | doors: |
| 3177 | Black Side Panels: 2427 | 3960 | Black Side Panels: 2427 |
| 3178 | Brown Side Panels: 2428 | 3961 | Brown Side Panels: 2428 |
| 3962 | Keyholder Hint Panel: 3360 | ||
| 3179 | Main Room Door: 2429 | 3963 | Main Room Door: 2429 |
| 3180 | the_tenacious: | 3964 | the_tenacious: |
| 3181 | rooms: | 3965 | rooms: |
| @@ -3185,6 +3969,8 @@ maps: | |||
| 3185 | Control Center Entrance: | 3969 | Control Center Entrance: |
| 3186 | panels: | 3970 | panels: |
| 3187 | ZERO: 2455 | 3971 | ZERO: 2455 |
| 3972 | ports: | ||
| 3973 | CC: 3258 | ||
| 3188 | Main Area: | 3974 | Main Area: |
| 3189 | keyholders: | 3975 | keyholders: |
| 3190 | K: 2768 | 3976 | K: 2768 |
| @@ -3201,6 +3987,7 @@ maps: | |||
| 3201 | panels: | 3987 | panels: |
| 3202 | WISDOM: 2459 | 3988 | WISDOM: 2459 |
| 3203 | doors: | 3989 | doors: |
| 3990 | K Entered: 2844 | ||
| 3204 | Paintings Door: 2453 | 3991 | Paintings Door: 2453 |
| 3205 | the_three_doors: | 3992 | the_three_doors: |
| 3206 | rooms: | 3993 | rooms: |
| @@ -3210,26 +3997,39 @@ maps: | |||
| 3210 | DOOR: 2461 | 3997 | DOOR: 2461 |
| 3211 | END: 2464 | 3998 | END: 2464 |
| 3212 | WAYS: 2462 | 3999 | WAYS: 2462 |
| 4000 | ports: | ||
| 4001 | BEGIN: 3259 | ||
| 4002 | BEGIN2: 3260 | ||
| 3213 | First Second Room: | 4003 | First Second Room: |
| 3214 | panels: | 4004 | panels: |
| 3215 | FIRS: 2465 | 4005 | FIRS: 2465 |
| 3216 | INITIAL: 2466 | 4006 | INITIAL: 2466 |
| 3217 | MINUTE (1): 2467 | 4007 | MINUTE (1): 2467 |
| 3218 | MINUTE (2): 2468 | 4008 | MINUTE (2): 2468 |
| 4009 | ports: | ||
| 4010 | GREAT: 3261 | ||
| 4011 | TTD: 3262 | ||
| 3219 | Loose Strings Room: | 4012 | Loose Strings Room: |
| 3220 | panels: | 4013 | panels: |
| 3221 | LOOSE: 2469 | 4014 | LOOSE: 2469 |
| 3222 | STRINGS: 2470 | 4015 | STRINGS: 2470 |
| 4016 | ports: | ||
| 4017 | BEGIN: 3263 | ||
| 3223 | One Luck Room: | 4018 | One Luck Room: |
| 3224 | panels: | 4019 | panels: |
| 3225 | CHANCE: 2472 | 4020 | CHANCE: 2472 |
| 3226 | LONE: 2471 | 4021 | LONE: 2471 |
| 4022 | ports: | ||
| 4023 | BEGIN: 3264 | ||
| 3227 | Silver Portal Room: | 4024 | Silver Portal Room: |
| 3228 | panels: | 4025 | panels: |
| 3229 | GOLD: 2473 | 4026 | GOLD: 2473 |
| 3230 | Left: 2475 | 4027 | Left: 2475 |
| 3231 | PORT: 2474 | 4028 | PORT: 2474 |
| 3232 | Right: 2476 | 4029 | Right: 2476 |
| 4030 | ports: | ||
| 4031 | BEGIN: 3265 | ||
| 4032 | NEXT: 3266 | ||
| 3233 | doors: | 4033 | doors: |
| 3234 | The Three Doors Gravestone: 2460 | 4034 | The Three Doors Gravestone: 2460 |
| 3235 | the_tower: | 4035 | the_tower: |
| @@ -3248,6 +4048,8 @@ maps: | |||
| 3248 | PROD: 2485 | 4048 | PROD: 2485 |
| 3249 | RIDE: 2484 | 4049 | RIDE: 2484 |
| 3250 | WARM: 2486 | 4050 | WARM: 2486 |
| 4051 | ports: | ||
| 4052 | GREAT: 3267 | ||
| 3251 | Tower: | 4053 | Tower: |
| 3252 | panels: | 4054 | panels: |
| 3253 | ANNOY (1): 2508 | 4055 | ANNOY (1): 2508 |
| @@ -3311,6 +4113,9 @@ maps: | |||
| 3311 | Third Floor Puzzles: 2480 | 4113 | Third Floor Puzzles: 2480 |
| 3312 | the_tree: | 4114 | the_tree: |
| 3313 | rooms: | 4115 | rooms: |
| 4116 | Bearer Entrance: | ||
| 4117 | ports: | ||
| 4118 | BEARER: 3268 | ||
| 3314 | Main Area: | 4119 | Main Area: |
| 3315 | panels: | 4120 | panels: |
| 3316 | COLOR: 2550 | 4121 | COLOR: 2550 |
| @@ -3343,6 +4148,11 @@ maps: | |||
| 3343 | WADE: 2562 | 4148 | WADE: 2562 |
| 3344 | WALK (1): 2555 | 4149 | WALK (1): 2555 |
| 3345 | WALK (2): 2556 | 4150 | WALK (2): 2556 |
| 4151 | ports: | ||
| 4152 | DAEDALUS: 3272 | ||
| 4153 | DIGITAL: 3270 | ||
| 4154 | GREAT: 3271 | ||
| 4155 | UNKEMPT: 3269 | ||
| 3346 | doors: | 4156 | doors: |
| 3347 | Control Center Brown Door: 2548 | 4157 | Control Center Brown Door: 2548 |
| 3348 | The Tree Gravestone: 2549 | 4158 | The Tree Gravestone: 2549 |
| @@ -3351,6 +4161,11 @@ maps: | |||
| 3351 | Control Center Entrance: | 4161 | Control Center Entrance: |
| 3352 | panels: | 4162 | panels: |
| 3353 | RETURN: 2587 | 4163 | RETURN: 2587 |
| 4164 | ports: | ||
| 4165 | CC: 3273 | ||
| 4166 | Daedalus Entrance: | ||
| 4167 | ports: | ||
| 4168 | DAEDALUS: 3274 | ||
| 3354 | Exit Room 2: | 4169 | Exit Room 2: |
| 3355 | panels: | 4170 | panels: |
| 3356 | DOOR: 2590 | 4171 | DOOR: 2590 |
| @@ -3394,6 +4209,10 @@ maps: | |||
| 3394 | ZOO: 2615 | 4209 | ZOO: 2615 |
| 3395 | keyholders: | 4210 | keyholders: |
| 3396 | I: 2775 | 4211 | I: 2775 |
| 4212 | ports: | ||
| 4213 | GREAT: 3275 | ||
| 4214 | SUNTEMPLE: 3277 | ||
| 4215 | TREE: 3276 | ||
| 3397 | Middle Room: | 4216 | Middle Room: |
| 3398 | panels: | 4217 | panels: |
| 3399 | FELLOW: 2624 | 4218 | FELLOW: 2624 |
| @@ -3450,13 +4269,19 @@ maps: | |||
| 3450 | doors: | 4269 | doors: |
| 3451 | Cog Rhino Hug Rug: 2586 | 4270 | Cog Rhino Hug Rug: 2586 |
| 3452 | Control Center Orange Door: 2582 | 4271 | Control Center Orange Door: 2582 |
| 4272 | Control Center Orange Panel: 3362 | ||
| 4273 | East Door: 2580 | ||
| 3453 | Honor Our Hint: 2585 | 4274 | Honor Our Hint: 2585 |
| 4275 | I Entered: 2845 | ||
| 3454 | Let Untrue Tie: 2583 | 4276 | Let Untrue Tie: 2583 |
| 3455 | Right Door: 2580 | 4277 | Near Teal Door Panels: 3361 |
| 3456 | Routine Out Chute: 2584 | 4278 | Routine Out Chute: 2584 |
| 3457 | W2 Room Door: 2581 | 4279 | W2 Room Door: 2581 |
| 3458 | the_unyielding: | 4280 | the_unyielding: |
| 3459 | rooms: | 4281 | rooms: |
| 4282 | Bearer Entrance: | ||
| 4283 | ports: | ||
| 4284 | BEARER: 3278 | ||
| 3460 | Behind Northeast: | 4285 | Behind Northeast: |
| 3461 | panels: | 4286 | panels: |
| 3462 | FIND: 1260 | 4287 | FIND: 1260 |
| @@ -3544,6 +4369,8 @@ maps: | |||
| 3544 | Digital Entrance: | 4369 | Digital Entrance: |
| 3545 | panels: | 4370 | panels: |
| 3546 | ORANGE: 1326 | 4371 | ORANGE: 1326 |
| 4372 | ports: | ||
| 4373 | DIGITAL: 3279 | ||
| 3547 | Directions Room: | 4374 | Directions Room: |
| 3548 | panels: | 4375 | panels: |
| 3549 | ABS: 1327 | 4376 | ABS: 1327 |
| @@ -3625,6 +4452,9 @@ maps: | |||
| 3625 | RAT: 1380 | 4452 | RAT: 1380 |
| 3626 | SEE: 1377 | 4453 | SEE: 1377 |
| 3627 | TIC: 1379 | 4454 | TIC: 1379 |
| 4455 | Nuanced Entrance: | ||
| 4456 | ports: | ||
| 4457 | NUANCED: 3280 | ||
| 3628 | Orange Alcove: | 4458 | Orange Alcove: |
| 3629 | panels: | 4459 | panels: |
| 3630 | ON: 1386 | 4460 | ON: 1386 |
| @@ -3632,6 +4462,8 @@ maps: | |||
| 3632 | panels: | 4462 | panels: |
| 3633 | GEE: 1387 | 4463 | GEE: 1387 |
| 3634 | SEA: 1388 | 4464 | SEA: 1388 |
| 4465 | ports: | ||
| 4466 | PLAZA: 3281 | ||
| 3635 | Red Eyes: | 4467 | Red Eyes: |
| 3636 | panels: | 4468 | panels: |
| 3637 | RED EYES: 1389 | 4469 | RED EYES: 1389 |
| @@ -3692,7 +4524,9 @@ maps: | |||
| 3692 | HEALTH: 1428 | 4524 | HEALTH: 1428 |
| 3693 | doors: | 4525 | doors: |
| 3694 | Bearer Entrance: 1259 | 4526 | Bearer Entrance: 1259 |
| 4527 | Blue D Room Puzzles: 3363 | ||
| 3695 | Brown Alcove: 1255 | 4528 | Brown Alcove: 1255 |
| 4529 | Color Hallway Panels: 3364 | ||
| 3696 | Digital Entrance: 1257 | 4530 | Digital Entrance: 1257 |
| 3697 | East Room 1: 2740 | 4531 | East Room 1: 2740 |
| 3698 | East Room 1 Entrance: 1251 | 4532 | East Room 1 Entrance: 1251 |
| @@ -3742,6 +4576,8 @@ maps: | |||
| 3742 | Entry: | 4576 | Entry: |
| 3743 | panels: | 4577 | panels: |
| 3744 | WONDER: 2690 | 4578 | WONDER: 2690 |
| 4579 | ports: | ||
| 4580 | DAEDALUS: 3282 | ||
| 3745 | Huge: | 4581 | Huge: |
| 3746 | panels: | 4582 | panels: |
| 3747 | BARK: 2695 | 4583 | BARK: 2695 |
| @@ -3769,6 +4605,8 @@ maps: | |||
| 3769 | METAL: 2706 | 4605 | METAL: 2706 |
| 3770 | SPICE: 2708 | 4606 | SPICE: 2708 |
| 3771 | TREE: 2705 | 4607 | TREE: 2705 |
| 4608 | ports: | ||
| 4609 | ENTRY: 3283 | ||
| 3772 | letters: | 4610 | letters: |
| 3773 | a1: 596 | 4611 | a1: 596 |
| 3774 | a2: 6 | 4612 | a2: 6 |
| @@ -3838,7 +4676,54 @@ endings: | |||
| 3838 | YELLOW: 1206 | 4676 | YELLOW: 1206 |
| 3839 | special: | 4677 | special: |
| 3840 | A Job Well Done: 1160 | 4678 | A Job Well Done: 1160 |
| 4679 | Age Symbol: 2791 | ||
| 4680 | Anagram Symbol: 2792 | ||
| 4681 | Anti A: 2814 | ||
| 4682 | Anti B: 2815 | ||
| 4683 | Anti C: 2816 | ||
| 4684 | Anti D: 2817 | ||
| 4685 | Anti E: 2818 | ||
| 4686 | Anti F: 2819 | ||
| 4687 | Anti G: 2820 | ||
| 4688 | Anti H: 2821 | ||
| 4689 | Anti I: 2822 | ||
| 4690 | Anti J: 2823 | ||
| 4691 | Anti K: 2824 | ||
| 4692 | Anti L: 2825 | ||
| 4693 | Anti M: 2826 | ||
| 4694 | Anti N: 2827 | ||
| 4695 | Anti O: 2828 | ||
| 4696 | Anti P: 2829 | ||
| 4697 | Anti Q: 2830 | ||
| 4698 | Anti R: 2831 | ||
| 4699 | Anti S: 2832 | ||
| 4700 | Anti T: 2833 | ||
| 4701 | Anti U: 2834 | ||
| 4702 | Anti V: 2835 | ||
| 4703 | Anti W: 2836 | ||
| 4704 | Anti X: 2837 | ||
| 4705 | Anti Y: 2838 | ||
| 4706 | Anti Z: 2839 | ||
| 4707 | Boxes Symbol: 2793 | ||
| 4708 | Cross Symbol: 2794 | ||
| 4709 | Eval Symbol: 2795 | ||
| 4710 | Example Symbol: 2796 | ||
| 4711 | Gender Symbol: 2797 | ||
| 4712 | Job Symbol: 2798 | ||
| 4713 | Lingo Symbol: 2799 | ||
| 4714 | Null Symbol: 2800 | ||
| 4715 | Numbers: 3038 | ||
| 4716 | Planet Symbol: 2801 | ||
| 4717 | Pyramid Symbol: 2802 | ||
| 4718 | Question Symbol: 2803 | ||
| 4719 | Sound Symbol: 2804 | ||
| 4720 | Sparkles Symbol: 2805 | ||
| 4721 | Stars Symbol: 2806 | ||
| 4722 | Sun Symbol: 2807 | ||
| 4723 | Sweet Symbol: 2808 | ||
| 4724 | Zero Symbol: 2809 | ||
| 3841 | progressives: | 4725 | progressives: |
| 4726 | Icarus Quick Travel: 2933 | ||
| 3842 | Progressive Gold Ending: 2753 | 4727 | Progressive Gold Ending: 2753 |
| 3843 | door_groups: | 4728 | door_groups: |
| 3844 | Control Center Blue Doors: 2788 | 4729 | Control Center Blue Doors: 2788 |
| @@ -3847,5 +4732,6 @@ door_groups: | |||
| 3847 | Control Center Purple Doors: 2785 | 4732 | Control Center Purple Doors: 2785 |
| 3848 | Control Center White Doors: 2784 | 4733 | Control Center White Doors: 2784 |
| 3849 | Cyan Doors: 2789 | 4734 | Cyan Doors: 2789 |
| 4735 | Lavender Cubes: 2790 | ||
| 3850 | The Entry - Repetitive Entrance: 2782 | 4736 | The Entry - Repetitive Entrance: 2782 |
| 3851 | The Repetitive - Plaza Entrance: 2783 | 4737 | The Repetitive - Plaza Entrance: 2783 |
| diff --git a/data/maps/control_center/doors.txtpb b/data/maps/control_center/doors.txtpb index 08476a7..bec8714 100644 --- a/data/maps/control_center/doors.txtpb +++ b/data/maps/control_center/doors.txtpb | |||
| @@ -15,6 +15,8 @@ doors { | |||
| 15 | doors { | 15 | doors { |
| 16 | name: "Hidden Door" | 16 | name: "Hidden Door" |
| 17 | type: EVENT | 17 | type: EVENT |
| 18 | latch: true | ||
| 19 | receivers: "Components/Doors/entry_12" | ||
| 18 | keyholders { room: "Main Area" name: "1" key: "h" } | 20 | keyholders { room: "Main Area" name: "1" key: "h" } |
| 19 | keyholders { room: "Main Area" name: "2" key: "i" } | 21 | keyholders { room: "Main Area" name: "2" key: "i" } |
| 20 | keyholders { room: "Main Area" name: "3" key: "d" } | 22 | keyholders { room: "Main Area" name: "3" key: "d" } |
| @@ -84,25 +86,12 @@ doors { | |||
| 84 | doors { | 86 | doors { |
| 85 | name: "White Ending Door" | 87 | name: "White Ending Door" |
| 86 | type: EVENT | 88 | type: EVENT |
| 87 | # This is the only time a door depends on endings. However, it's nice to do it | 89 | white_ending: true |
| 88 | # this way instead of just checking for ending room access because this lets | ||
| 89 | # us use events, which makes the playthrough more readable. | ||
| 90 | endings: "MINT" | ||
| 91 | endings: "ORANGE" | ||
| 92 | endings: "GREEN" | ||
| 93 | endings: "GRAY" | ||
| 94 | endings: "PLUM" | ||
| 95 | endings: "YELLOW" | ||
| 96 | endings: "GOLD" | ||
| 97 | endings: "BLACK" | ||
| 98 | endings: "CYAN" | ||
| 99 | endings: "PURPLE" | ||
| 100 | endings: "RED" | ||
| 101 | endings: "BLUE" | ||
| 102 | } | 90 | } |
| 103 | doors { | 91 | doors { |
| 104 | name: "Repetitive Entrance" | 92 | name: "Repetitive Entrance" |
| 105 | type: STANDARD | 93 | type: STANDARD |
| 94 | latch: true | ||
| 106 | receivers: "Components/Doors/entry_7" | 95 | receivers: "Components/Doors/entry_7" |
| 107 | keyholders { room: "Main Area" name: "1" key: "m" } | 96 | keyholders { room: "Main Area" name: "1" key: "m" } |
| 108 | keyholders { room: "Main Area" name: "2" key: "o" } | 97 | keyholders { room: "Main Area" name: "2" key: "o" } |
| @@ -113,20 +102,26 @@ doors { | |||
| 113 | } | 102 | } |
| 114 | doors { | 103 | doors { |
| 115 | name: "Perceptive From Outside" | 104 | name: "Perceptive From Outside" |
| 116 | type: EVENT | 105 | type: STANDARD |
| 106 | latch: true | ||
| 107 | receivers: "Components/Doors/entry_26" | ||
| 117 | keyholders { room: "Main Area" name: "1" key: "p" } | 108 | keyholders { room: "Main Area" name: "1" key: "p" } |
| 118 | keyholders { room: "Main Area" name: "2" key: "a" } | 109 | keyholders { room: "Main Area" name: "2" key: "a" } |
| 119 | keyholders { room: "Main Area" name: "3" key: "r" } | 110 | keyholders { room: "Main Area" name: "3" key: "r" } |
| 120 | keyholders { room: "Main Area" name: "4" key: "t" } | 111 | keyholders { room: "Main Area" name: "4" key: "t" } |
| 112 | location_room: "Main Area" | ||
| 113 | location_name: "Keyword PART" | ||
| 121 | } | 114 | } |
| 122 | doors { | 115 | doors { |
| 123 | name: "Perceptive From Inside" | 116 | name: "Perceptive From Inside" |
| 124 | type: EVENT | 117 | type: LOCATION_ONLY |
| 125 | panels { room: "Perceptive Entrance" name: "PART" } | 118 | panels { room: "Perceptive Entrance" name: "PART" } |
| 119 | location_room: "Perceptive Entrance" | ||
| 126 | } | 120 | } |
| 127 | doors { | 121 | doors { |
| 128 | name: "Ancient Entrance" | 122 | name: "Ancient Entrance" |
| 129 | type: STANDARD | 123 | type: STANDARD |
| 124 | latch: true | ||
| 130 | receivers: "Components/Doors/entry_20" | 125 | receivers: "Components/Doors/entry_20" |
| 131 | keyholders { room: "Main Area" name: "1" key: "z" } | 126 | keyholders { room: "Main Area" name: "1" key: "z" } |
| 132 | keyholders { room: "Main Area" name: "2" key: "e" } | 127 | keyholders { room: "Main Area" name: "2" key: "e" } |
| @@ -170,3 +165,16 @@ doors { | |||
| 170 | panels { room: "Unyielding Entrance" name: "SEEK" } | 165 | panels { room: "Unyielding Entrance" name: "SEEK" } |
| 171 | location_room: "Unyielding Entrance" | 166 | location_room: "Unyielding Entrance" |
| 172 | } | 167 | } |
| 168 | doors { | ||
| 169 | name: "Near Perceptive Panel" | ||
| 170 | type: LOCATION_ONLY | ||
| 171 | panels { room: "Perceptive Entrance" name: "COLORS" } | ||
| 172 | location_room: "Perceptive Entrance" | ||
| 173 | location_name: "COLORS" | ||
| 174 | } | ||
| 175 | doors { | ||
| 176 | name: "Letters Panel" | ||
| 177 | type: LOCATION_ONLY | ||
| 178 | panels { room: "Main Area" name: "Letters" } | ||
| 179 | location_room: "Main Area" | ||
| 180 | } | ||
| diff --git a/data/maps/control_center/rooms/Ancient Entrance.txtpb b/data/maps/control_center/rooms/Ancient Entrance.txtpb index 9fe50c5..dc018ba 100644 --- a/data/maps/control_center/rooms/Ancient Entrance.txtpb +++ b/data/maps/control_center/rooms/Ancient Entrance.txtpb | |||
| @@ -1,5 +1,12 @@ | |||
| 1 | name: "Ancient Entrance" | 1 | name: "Ancient Entrance" |
| 2 | ports { | 2 | ports { |
| 3 | name: "ANCIENT" | 3 | name: "ANCIENT" |
| 4 | display_name: "Ancient Entrance" | ||
| 4 | path: "Components/Warps/worldport8" | 5 | path: "Components/Warps/worldport8" |
| 6 | destination { x: -27 y: 0 z: -34 } | ||
| 7 | rotation: 90 | ||
| 8 | # This is because there's no port on the other side of the connection, so if | ||
| 9 | # this connection was removed and gallery paintings aren't shuffled then | ||
| 10 | # there'd be no way into The Ancient. | ||
| 11 | no_shuffle: true | ||
| 5 | } | 12 | } |
| diff --git a/data/maps/control_center/rooms/Between Entrance.txtpb b/data/maps/control_center/rooms/Between Entrance.txtpb index 2c21bdd..9da5344 100644 --- a/data/maps/control_center/rooms/Between Entrance.txtpb +++ b/data/maps/control_center/rooms/Between Entrance.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "BETWEEN" | 10 | name: "BETWEEN" |
| 11 | display_name: "Between Connector" | ||
| 11 | path: "Components/Warps/worldport5" | 12 | path: "Components/Warps/worldport5" |
| 13 | destination { x: 39 y: 0 z: -17 } | ||
| 14 | rotation: 270 | ||
| 12 | } | 15 | } |
| diff --git a/data/maps/control_center/rooms/Entry Entrance.txtpb b/data/maps/control_center/rooms/Entry Entrance.txtpb index d920523..ad882f5 100644 --- a/data/maps/control_center/rooms/Entry Entrance.txtpb +++ b/data/maps/control_center/rooms/Entry Entrance.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "ENTRY" | 10 | name: "ENTRY" |
| 11 | display_name: "Entry Connector" | ||
| 11 | path: "Components/Warps/worldport2" | 12 | path: "Components/Warps/worldport2" |
| 13 | destination { x: 26 y: 0 z: -16.5 } | ||
| 14 | rotation: 0 | ||
| 12 | } | 15 | } |
| diff --git a/data/maps/control_center/rooms/Entry.txtpb b/data/maps/control_center/rooms/Entry.txtpb index 7ef380c..09c21aa 100644 --- a/data/maps/control_center/rooms/Entry.txtpb +++ b/data/maps/control_center/rooms/Entry.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "GREAT" | 10 | name: "GREAT" |
| 11 | display_name: "Main Entrance" | ||
| 11 | path: "Components/Warps/worldport" | 12 | path: "Components/Warps/worldport" |
| 13 | destination { x: 0 y: 0 z: -1.5 } | ||
| 14 | rotation: 0 | ||
| 12 | } | 15 | } |
| diff --git a/data/maps/control_center/rooms/Main Area.txtpb b/data/maps/control_center/rooms/Main Area.txtpb index bf81e26..2c1e418 100644 --- a/data/maps/control_center/rooms/Main Area.txtpb +++ b/data/maps/control_center/rooms/Main Area.txtpb | |||
| @@ -49,22 +49,33 @@ keyholders { | |||
| 49 | } | 49 | } |
| 50 | ports { | 50 | ports { |
| 51 | name: "RIGHT" | 51 | name: "RIGHT" |
| 52 | display_name: "Hinterlands South Entrance" | ||
| 52 | path: "Components/Warps/worldport6" | 53 | path: "Components/Warps/worldport6" |
| 54 | destination { x: 82 y: 0 z: -10 } | ||
| 55 | rotation: 90 | ||
| 53 | } | 56 | } |
| 54 | ports { | 57 | ports { |
| 55 | name: "LEFT" | 58 | name: "LEFT" |
| 59 | display_name: "Hinterlands North Entrance" | ||
| 56 | path: "Components/Warps/worldport7" | 60 | path: "Components/Warps/worldport7" |
| 57 | # Check that this is correct. | 61 | destination { x: 82 y: 0 z: -48 } |
| 62 | rotation: 90 | ||
| 58 | } | 63 | } |
| 59 | ports { | 64 | ports { |
| 60 | name: "RELENTLESS_LEFT" | 65 | name: "RELENTLESS_LEFT" |
| 66 | display_name: "Relentless LEFT Entrance" | ||
| 61 | path: "Components/Warps/worldport9" | 67 | path: "Components/Warps/worldport9" |
| 68 | no_shuffle: true | ||
| 62 | } | 69 | } |
| 63 | ports { | 70 | ports { |
| 64 | name: "RELENTLESS_SHOP" | 71 | name: "RELENTLESS_SHOP" |
| 72 | display_name: "Relentless SHOP Entrance" | ||
| 65 | path: "Components/Warps/worldport11" | 73 | path: "Components/Warps/worldport11" |
| 74 | no_shuffle: true | ||
| 66 | } | 75 | } |
| 67 | ports { | 76 | ports { |
| 68 | name: "RELENTLESS_TURN" | 77 | name: "RELENTLESS_TURN" |
| 78 | display_name: "Relentless TURN Entrance" | ||
| 69 | path: "Components/Warps/worldport10" | 79 | path: "Components/Warps/worldport10" |
| 80 | no_shuffle: true | ||
| 70 | } | 81 | } |
| diff --git a/data/maps/control_center/rooms/Partial Entrance.txtpb b/data/maps/control_center/rooms/Partial Entrance.txtpb index 77b68fa..de5d91a 100644 --- a/data/maps/control_center/rooms/Partial Entrance.txtpb +++ b/data/maps/control_center/rooms/Partial Entrance.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "PARTIAL" | 10 | name: "PARTIAL" |
| 11 | display_name: "Partial Connector" | ||
| 11 | path: "Components/Warps/worldport4" | 12 | path: "Components/Warps/worldport4" |
| 13 | destination { x: 21 y: 0 z: -41 } | ||
| 14 | rotation: 270 | ||
| 12 | } | 15 | } |
| diff --git a/data/maps/control_center/rooms/Perceptive Entrance.txtpb b/data/maps/control_center/rooms/Perceptive Entrance.txtpb index 6eec265..99b100b 100644 --- a/data/maps/control_center/rooms/Perceptive Entrance.txtpb +++ b/data/maps/control_center/rooms/Perceptive Entrance.txtpb | |||
| @@ -16,5 +16,8 @@ panels { | |||
| 16 | } | 16 | } |
| 17 | ports { | 17 | ports { |
| 18 | name: "PERCEPTIVE" | 18 | name: "PERCEPTIVE" |
| 19 | display_name: "Perceptive Entrance" | ||
| 19 | path: "Components/Warps/worldport12" | 20 | path: "Components/Warps/worldport12" |
| 21 | destination { x: -23 y: 0 z: -11 } | ||
| 22 | rotation: 0 | ||
| 20 | } | 23 | } |
| diff --git a/data/maps/control_center/rooms/Repetitive Entrance.txtpb b/data/maps/control_center/rooms/Repetitive Entrance.txtpb index 08b8fa4..0767e2c 100644 --- a/data/maps/control_center/rooms/Repetitive Entrance.txtpb +++ b/data/maps/control_center/rooms/Repetitive Entrance.txtpb | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | name: "Repetitive Entrance" | 1 | name: "Repetitive Entrance" |
| 2 | ports { | 2 | ports { |
| 3 | name: "REPETITIVE" | 3 | name: "REPETITIVE" |
| 4 | display_name: "Repetitive Entrance" | ||
| 4 | path: "Components/Warps/worldport14" | 5 | path: "Components/Warps/worldport14" |
| 6 | destination { x: -16 y: 0 z: -17.5 } | ||
| 7 | rotation: 0 | ||
| 5 | } | 8 | } |
| diff --git a/data/maps/control_center/rooms/Tenacious Entrance.txtpb b/data/maps/control_center/rooms/Tenacious Entrance.txtpb index 0527d50..093e4fc 100644 --- a/data/maps/control_center/rooms/Tenacious Entrance.txtpb +++ b/data/maps/control_center/rooms/Tenacious Entrance.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "TENACIOUS" | 10 | name: "TENACIOUS" |
| 11 | display_name: "Tenacious Connector" | ||
| 11 | path: "Components/Warps/worldport13" | 12 | path: "Components/Warps/worldport13" |
| 13 | destination { x: 56 y: 0 z: -38 } | ||
| 14 | rotation: 180 | ||
| 12 | } | 15 | } |
| diff --git a/data/maps/control_center/rooms/Unkempt Entrance.txtpb b/data/maps/control_center/rooms/Unkempt Entrance.txtpb index b6fc074..a89cceb 100644 --- a/data/maps/control_center/rooms/Unkempt Entrance.txtpb +++ b/data/maps/control_center/rooms/Unkempt Entrance.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "UNKEMPT" | 10 | name: "UNKEMPT" |
| 11 | display_name: "Unkempt Connector" | ||
| 11 | path: "Components/Warps/worldport3" | 12 | path: "Components/Warps/worldport3" |
| 13 | destination { x: 34 y: 0 z: -38.5 } | ||
| 14 | rotation: 90 | ||
| 12 | } | 15 | } |
| 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 d6c33f4..ed7516f 100644 --- a/data/maps/daedalus/doors.txtpb +++ b/data/maps/daedalus/doors.txtpb | |||
| @@ -188,17 +188,20 @@ doors { | |||
| 188 | } | 188 | } |
| 189 | doors { | 189 | doors { |
| 190 | name: "Welcome Back Secret Door" | 190 | name: "Welcome Back Secret Door" |
| 191 | type: ITEM_ONLY | 191 | type: STANDARD |
| 192 | receivers: "Components/Doors/Entry/entry_13" | 192 | receivers: "Components/Doors/Entry/entry_13" |
| 193 | panels { room: "Welcome Back Area" name: "FAREWELL LITTLE LAMB" } | 193 | panels { room: "Welcome Back Area" name: "FAREWELL LITTLE LAMB" } |
| 194 | panels { room: "West Spire" name: "BYE" } | 194 | panels { room: "West Spire" name: "BYE" } |
| 195 | location_room: "West Spire" | ||
| 196 | location_name: "BYE, FAREWELL LITTLE LAMB" | ||
| 195 | } | 197 | } |
| 196 | doors { | 198 | doors { |
| 197 | name: "Welcome Back Door" | 199 | name: "Welcome Back Door" |
| 198 | type: STANDARD | 200 | type: LOCATION_ONLY |
| 199 | receivers: "Components/Doors/Entry/entry_14" | 201 | #receivers: "Components/Doors/Entry/entry_14" |
| 200 | panels { room: "Welcome Back Area" name: "GREETINGS OLD FRIEND" } | 202 | panels { room: "Welcome Back Area" name: "GREETINGS OLD FRIEND" } |
| 201 | location_room: "Welcome Back Area" | 203 | location_room: "Welcome Back Area" |
| 204 | location_name: "GREETINGS OLD FRIEND" | ||
| 202 | } | 205 | } |
| 203 | # entry_3 is the door to SEAL, which we will ignore. | 206 | # entry_3 is the door to SEAL, which we will ignore. |
| 204 | doors { | 207 | doors { |
| @@ -224,12 +227,21 @@ doors { | |||
| 224 | } | 227 | } |
| 225 | doors { | 228 | doors { |
| 226 | name: "Starting Room East Wall Center Door" | 229 | name: "Starting Room East Wall Center Door" |
| 227 | type: STANDARD | 230 | type: ITEM_ONLY |
| 231 | legacy_location: true | ||
| 228 | receivers: "Components/Doors/Entry/entry_6" | 232 | receivers: "Components/Doors/Entry/entry_6" |
| 229 | panels { room: "Rainbow Color Backside" name: "?" } | 233 | panels { room: "Rainbow Color Backside" name: "?" } |
| 230 | location_room: "Rainbow Color Backside" | 234 | location_room: "Rainbow Color Backside" |
| 231 | } | 235 | } |
| 232 | doors { | 236 | doors { |
| 237 | name: "Rainbow Color Backside Panels" | ||
| 238 | type: LOCATION_ONLY | ||
| 239 | panels { room: "Rainbow Color Backside" name: "?" } | ||
| 240 | panels { room: "Rainbow Color Backside" name: "BACKSIDE" } | ||
| 241 | location_room: "Rainbow Color Backside" | ||
| 242 | location_name: "BACKSIDE, ?" | ||
| 243 | } | ||
| 244 | doors { | ||
| 233 | name: "Starting Room East Wall North Door" | 245 | name: "Starting Room East Wall North Door" |
| 234 | type: ITEM_ONLY | 246 | type: ITEM_ONLY |
| 235 | receivers: "Components/Doors/Entry/entry_7" | 247 | receivers: "Components/Doors/Entry/entry_7" |
| @@ -298,8 +310,9 @@ doors { | |||
| 298 | location_name: "Black Hex" | 310 | location_name: "Black Hex" |
| 299 | } | 311 | } |
| 300 | doors { | 312 | doors { |
| 301 | name: "Splintering Exit Left Door" | 313 | name: "Splintering Exit North Door" |
| 302 | type: STANDARD | 314 | type: ITEM_ONLY |
| 315 | legacy_location: true | ||
| 303 | receivers: "Components/Doors/Entry/gate_4" | 316 | receivers: "Components/Doors/Entry/gate_4" |
| 304 | panels { room: "West Castle Area" name: "EVER" } | 317 | panels { room: "West Castle Area" name: "EVER" } |
| 305 | panels { room: "West Castle Area" name: "AXES" } | 318 | panels { room: "West Castle Area" name: "AXES" } |
| @@ -308,7 +321,7 @@ doors { | |||
| 308 | location_room: "West Castle Area" | 321 | location_room: "West Castle Area" |
| 309 | } | 322 | } |
| 310 | doors { | 323 | doors { |
| 311 | name: "Splintering Exit Right Door" | 324 | name: "Splintering Exit South Door" |
| 312 | type: ITEM_ONLY | 325 | type: ITEM_ONLY |
| 313 | receivers: "Components/Doors/Entry/gate_5" | 326 | receivers: "Components/Doors/Entry/gate_5" |
| 314 | panels { room: "West Castle Area" name: "EVER" } | 327 | panels { room: "West Castle Area" name: "EVER" } |
| @@ -317,6 +330,48 @@ doors { | |||
| 317 | panels { room: "West Castle Area" name: "SLOT" } | 330 | panels { room: "West Castle Area" name: "SLOT" } |
| 318 | } | 331 | } |
| 319 | doors { | 332 | doors { |
| 333 | name: "Splintering Area Panels" | ||
| 334 | type: LOCATION_ONLY | ||
| 335 | panels { room: "West Castle Area" name: "EVER" } | ||
| 336 | panels { room: "West Castle Area" name: "AXES" } | ||
| 337 | panels { room: "West Castle Area" name: "FLIP (1)" } | ||
| 338 | panels { room: "West Castle Area" name: "SLOT" } | ||
| 339 | panels { room: "West Castle Area" name: "WICKEDLY" } | ||
| 340 | panels { room: "West Castle Area" name: "CATHOLIC" } | ||
| 341 | panels { room: "West Castle Area" name: "SISTERLY" } | ||
| 342 | panels { room: "West Castle Area" name: "SQUEALED" } | ||
| 343 | panels { room: "West Castle Area" name: "READ" } | ||
| 344 | panels { room: "West Castle Area" name: "WORD" } | ||
| 345 | panels { room: "West Castle Area" name: "EACH" } | ||
| 346 | panels { room: "West Castle Area" name: "RANK" } | ||
| 347 | panels { room: "West Castle Area" name: "TEAR" } | ||
| 348 | panels { room: "West Castle Area" name: "SHUT" } | ||
| 349 | panels { room: "West Castle Area" name: "FLIP (2)" } | ||
| 350 | panels { room: "West Castle Area" name: "STUN" } | ||
| 351 | panels { room: "West Castle Area" name: "CHAT" } | ||
| 352 | panels { room: "West Castle Area" name: "LOST" } | ||
| 353 | panels { room: "West Castle Area" name: "PODS" } | ||
| 354 | panels { room: "West Castle Area" name: "FAME" } | ||
| 355 | location_room: "West Castle Area" | ||
| 356 | } | ||
| 357 | doors { | ||
| 358 | name: "West Sticks And Stones Panel" | ||
| 359 | type: LOCATION_ONLY | ||
| 360 | panels { room: "West Castle Area" name: "LETTERS" } | ||
| 361 | location_room: "West Castle Area" | ||
| 362 | location_name: "LETTERS" | ||
| 363 | } | ||
| 364 | doors { | ||
| 365 | name: "Amber Room Panels" | ||
| 366 | type: LOCATION_ONLY | ||
| 367 | panels { room: "West Castle Area" name: "HARMONY" } | ||
| 368 | panels { room: "West Castle Area" name: "MELODY" } | ||
| 369 | panels { room: "West Castle Area" name: "RHYTHM" } | ||
| 370 | panels { room: "West Castle Area" name: "TEXTURE" } | ||
| 371 | location_room: "West Castle Area" | ||
| 372 | location_name: "HARMONY, MELODY, RHYTHM, TEXTURE" | ||
| 373 | } | ||
| 374 | doors { | ||
| 320 | name: "Z2 Room Back Exit" | 375 | name: "Z2 Room Back Exit" |
| 321 | type: ITEM_ONLY | 376 | type: ITEM_ONLY |
| 322 | receivers: "Components/Doors/Entry/gate_2" | 377 | receivers: "Components/Doors/Entry/gate_2" |
| @@ -493,7 +548,6 @@ doors { | |||
| 493 | panels { room: "Outside House" name: "WALLS" } | 548 | panels { room: "Outside House" name: "WALLS" } |
| 494 | panels { room: "Outside House" name: "LOCK" } | 549 | panels { room: "Outside House" name: "LOCK" } |
| 495 | location_room: "Outside House" | 550 | location_room: "Outside House" |
| 496 | location_name: "North Purple Vestibules" | ||
| 497 | } | 551 | } |
| 498 | doors { | 552 | doors { |
| 499 | name: "Purple NW Vestibule" | 553 | name: "Purple NW Vestibule" |
| @@ -716,7 +770,7 @@ doors { | |||
| 716 | panels { room: "O2 Room" name: "UNBLOCKED" } | 770 | panels { room: "O2 Room" name: "UNBLOCKED" } |
| 717 | } | 771 | } |
| 718 | doors { | 772 | doors { |
| 719 | name: "Blue Smiley Entrance" | 773 | name: "O2 Room Southeast Door" |
| 720 | type: STANDARD | 774 | type: STANDARD |
| 721 | receivers: "Components/Doors/Halls/oroom_2" | 775 | receivers: "Components/Doors/Halls/oroom_2" |
| 722 | panels { room: "O2 Room" name: "HONEST" } | 776 | panels { room: "O2 Room" name: "HONEST" } |
| @@ -820,9 +874,20 @@ doors { | |||
| 820 | } | 874 | } |
| 821 | doors { | 875 | doors { |
| 822 | name: "Composite Room NW Entrance" | 876 | name: "Composite Room NW Entrance" |
| 823 | type: STANDARD | 877 | type: ITEM_ONLY |
| 878 | legacy_location: true | ||
| 824 | receivers: "Components/Doors/Halls/oroom_10" | 879 | receivers: "Components/Doors/Halls/oroom_10" |
| 825 | panels { room: "Red Color Door" name: "Left" } | 880 | panels { room: "Red Color Door" name: "Near Obscured Puzzles" } |
| 881 | location_room: "Red Color Door" | ||
| 882 | } | ||
| 883 | doors { | ||
| 884 | name: "Yellow Roof Puzzles" | ||
| 885 | type: LOCATION_ONLY | ||
| 886 | panels { room: "Red Color Door" name: "BACKSIDE" } | ||
| 887 | panels { room: "Red Color Door" name: "WALK BACK" } | ||
| 888 | panels { room: "Red Color Door" name: "Back (1)" } | ||
| 889 | panels { room: "Red Color Door" name: "Back (2)" } | ||
| 890 | panels { room: "Red Color Door" name: "Near Obscured Puzzles" } | ||
| 826 | location_room: "Red Color Door" | 891 | location_room: "Red Color Door" |
| 827 | } | 892 | } |
| 828 | doors { | 893 | doors { |
| @@ -857,7 +922,7 @@ doors { | |||
| 857 | location_name: "South Rooms" | 922 | location_name: "South Rooms" |
| 858 | } | 923 | } |
| 859 | doors { | 924 | doors { |
| 860 | name: "O2 Room Back Door" | 925 | name: "O2 Room Northeast Door" |
| 861 | type: STANDARD | 926 | type: STANDARD |
| 862 | receivers: "Components/Doors/Halls/oroom_4" | 927 | receivers: "Components/Doors/Halls/oroom_4" |
| 863 | panels { room: "O2 Room" name: "UNBLOCKED" } | 928 | panels { room: "O2 Room" name: "UNBLOCKED" } |
| @@ -866,6 +931,7 @@ doors { | |||
| 866 | doors { | 931 | doors { |
| 867 | name: "Control Center Orange Door" | 932 | name: "Control Center Orange Door" |
| 868 | type: CONTROL_CENTER_COLOR | 933 | type: CONTROL_CENTER_COLOR |
| 934 | latch: true | ||
| 869 | receivers: "Components/Doors/Halls/oroom_6" | 935 | receivers: "Components/Doors/Halls/oroom_6" |
| 870 | control_center_color: "orange" | 936 | control_center_color: "orange" |
| 871 | } | 937 | } |
| @@ -884,8 +950,9 @@ doors { | |||
| 884 | panels { room: "F2 Room" name: "SHAPE" } | 950 | panels { room: "F2 Room" name: "SHAPE" } |
| 885 | } | 951 | } |
| 886 | doors { | 952 | doors { |
| 887 | name: "F2 Room Entrance" | 953 | name: "F2 Room Southeast Door" |
| 888 | type: STANDARD | 954 | type: ITEM_ONLY |
| 955 | legacy_location: true | ||
| 889 | receivers: "Components/Doors/Halls/froom_2" | 956 | receivers: "Components/Doors/Halls/froom_2" |
| 890 | panels { room: "Sweet Foyer" name: "RENT (1)" } | 957 | panels { room: "Sweet Foyer" name: "RENT (1)" } |
| 891 | location_room: "Sweet Foyer" | 958 | location_room: "Sweet Foyer" |
| @@ -893,31 +960,33 @@ doors { | |||
| 893 | doors { | 960 | doors { |
| 894 | name: "White Hallway From Entry" | 961 | name: "White Hallway From Entry" |
| 895 | type: CONTROL_CENTER_COLOR | 962 | type: CONTROL_CENTER_COLOR |
| 963 | latch: true | ||
| 896 | receivers: "Components/Doors/Halls/froom_6" | 964 | receivers: "Components/Doors/Halls/froom_6" |
| 897 | control_center_color: "white" | 965 | control_center_color: "white" |
| 898 | } | 966 | } |
| 899 | doors { | 967 | doors { |
| 900 | name: "Purple Hallway From Great" | 968 | name: "Purple Hallway From Great" |
| 901 | type: CONTROL_CENTER_COLOR | 969 | type: CONTROL_CENTER_COLOR |
| 970 | latch: true | ||
| 902 | receivers: "Components/Doors/Halls/froom_7" | 971 | receivers: "Components/Doors/Halls/froom_7" |
| 903 | control_center_color: "purple" | 972 | control_center_color: "purple" |
| 904 | } | 973 | } |
| 905 | doors { | 974 | doors { |
| 906 | name: "F2 Room Back Right Door" | 975 | name: "F2 Room Southwest Door" |
| 907 | type: STANDARD | 976 | type: STANDARD |
| 908 | receivers: "Components/Doors/Halls/froom_3" | 977 | receivers: "Components/Doors/Halls/froom_3" |
| 909 | panels { room: "F2 Room" name: "RISKY" } | 978 | panels { room: "F2 Room" name: "RISKY" } |
| 910 | location_room: "F2 Room" | 979 | location_room: "F2 Room" |
| 911 | } | 980 | } |
| 912 | doors { | 981 | doors { |
| 913 | name: "F2 Room Back Left Door" | 982 | name: "F2 Room Northwest Door" |
| 914 | type: STANDARD | 983 | type: STANDARD |
| 915 | receivers: "Components/Doors/Halls/froom_4" | 984 | receivers: "Components/Doors/Halls/froom_4" |
| 916 | panels { room: "F2 Room" name: "SHAPE" } | 985 | panels { room: "F2 Room" name: "SHAPE" } |
| 917 | location_room: "F2 Room" | 986 | location_room: "F2 Room" |
| 918 | } | 987 | } |
| 919 | doors { | 988 | doors { |
| 920 | name: "F2 Room Back Middle Door" | 989 | name: "F2 Room West Door" |
| 921 | type: STANDARD | 990 | type: STANDARD |
| 922 | receivers: "Components/Doors/Halls/froom_5" | 991 | receivers: "Components/Doors/Halls/froom_5" |
| 923 | panels { room: "F2 Room" name: "DIRT" } | 992 | panels { room: "F2 Room" name: "DIRT" } |
| @@ -938,7 +1007,7 @@ doors { | |||
| 938 | panels { room: "U2 Room" name: "HEAVEN" } | 1007 | panels { room: "U2 Room" name: "HEAVEN" } |
| 939 | } | 1008 | } |
| 940 | doors { | 1009 | doors { |
| 941 | name: "U2 Room Shortcut" | 1010 | name: "U2 Room Southeast Door" |
| 942 | type: STANDARD | 1011 | type: STANDARD |
| 943 | receivers: "Components/Doors/Halls/uroom_2" | 1012 | receivers: "Components/Doors/Halls/uroom_2" |
| 944 | panels { room: "U2 Room" name: "WICKED" } | 1013 | panels { room: "U2 Room" name: "WICKED" } |
| @@ -959,21 +1028,21 @@ doors { | |||
| 959 | location_room: "House Entrance" | 1028 | location_room: "House Entrance" |
| 960 | } | 1029 | } |
| 961 | doors { | 1030 | doors { |
| 962 | name: "U2 Room Back Right Door" | 1031 | name: "U2 Room Southwest Door" |
| 963 | type: STANDARD | 1032 | type: STANDARD |
| 964 | receivers: "Components/Doors/Halls/uroom_3" | 1033 | receivers: "Components/Doors/Halls/uroom_3" |
| 965 | panels { room: "U2 Room" name: "HEAVEN" } | 1034 | panels { room: "U2 Room" name: "HEAVEN" } |
| 966 | location_room: "U2 Room" | 1035 | location_room: "U2 Room" |
| 967 | } | 1036 | } |
| 968 | doors { | 1037 | doors { |
| 969 | name: "U2 Room Back Door" | 1038 | name: "U2 Room West Door" |
| 970 | type: ITEM_ONLY | 1039 | type: ITEM_ONLY |
| 971 | receivers: "Components/Doors/Halls/uroom_5" | 1040 | receivers: "Components/Doors/Halls/uroom_5" |
| 972 | panels { room: "Purple Room South" name: "ANY" } | 1041 | panels { room: "Purple Room South" name: "ANY" } |
| 973 | panels { room: "Outside House" name: "A" } | 1042 | panels { room: "Outside House" name: "A" } |
| 974 | } | 1043 | } |
| 975 | doors { | 1044 | doors { |
| 976 | name: "U2 Room Entrance" | 1045 | name: "U2 Room East Door" |
| 977 | type: ITEM_ONLY | 1046 | type: ITEM_ONLY |
| 978 | receivers: "Components/Doors/Halls/uroom_4" | 1047 | receivers: "Components/Doors/Halls/uroom_4" |
| 979 | panels { room: "Outside Magic Room" name: "WIZARD" } | 1048 | panels { room: "Outside Magic Room" name: "WIZARD" } |
| @@ -1017,7 +1086,7 @@ doors { | |||
| 1017 | panels { room: "Outside Magic Room" name: "WIZARD" } | 1086 | panels { room: "Outside Magic Room" name: "WIZARD" } |
| 1018 | } | 1087 | } |
| 1019 | doors { | 1088 | doors { |
| 1020 | name: "Wonderland Right Door" | 1089 | name: "Wonderland South Door" |
| 1021 | type: STANDARD | 1090 | type: STANDARD |
| 1022 | receivers: "Components/Doors/Halls/wonderland_1" | 1091 | receivers: "Components/Doors/Halls/wonderland_1" |
| 1023 | panels { room: "Wonderland" name: "APRIL" } | 1092 | panels { room: "Wonderland" name: "APRIL" } |
| @@ -1208,70 +1277,37 @@ doors { | |||
| 1208 | type: ITEM_ONLY | 1277 | type: ITEM_ONLY |
| 1209 | receivers: "Components/Doors/Halls/connections_1" | 1278 | receivers: "Components/Doors/Halls/connections_1" |
| 1210 | receivers: "Components/Doors/Halls/connections_3" | 1279 | receivers: "Components/Doors/Halls/connections_3" |
| 1280 | # These have the same effect as the above, but including them here prevents | ||
| 1281 | # them from opening in door shuffle when the J2 door opens. | ||
| 1282 | receivers: "Components/Triggers/teleportListenerConnections3" | ||
| 1283 | receivers: "Components/Triggers/teleportListenerConnections4" | ||
| 1284 | # This door can open from either solving all panels, or just the smiley ones, | ||
| 1285 | # and the latter is obviously a subset of the former so let's just check for | ||
| 1286 | # that. | ||
| 1211 | panels { room: "Hotel" name: "PARKA" } | 1287 | panels { room: "Hotel" name: "PARKA" } |
| 1212 | panels { room: "Hotel" name: "MARLIN" } | ||
| 1213 | panels { room: "Hotel" name: "WHO" } | ||
| 1214 | panels { room: "Hotel" name: "CLOAK" } | 1288 | 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" } | 1289 | panels { room: "Hotel" name: "DOROTHY" } |
| 1220 | panels { room: "Hotel" name: "JACKET" } | ||
| 1221 | panels { room: "Hotel" name: "TAIL" } | ||
| 1222 | panels { room: "Hotel" name: "JAWS" } | 1290 | panels { room: "Hotel" name: "JAWS" } |
| 1223 | panels { room: "Hotel" name: "FLOUNDER" } | ||
| 1224 | panels { room: "Hotel" name: "WHEN" } | 1291 | panels { room: "Hotel" name: "WHEN" } |
| 1225 | panels { room: "Hotel" name: "CLAWS" } | 1292 | panels { room: "Hotel" name: "CLAWS" } |
| 1226 | panels { room: "Hotel" name: "BRUCE" } | ||
| 1227 | panels { room: "Hotel" name: "POTATO" } | 1293 | panels { room: "Hotel" name: "POTATO" } |
| 1228 | panels { room: "Hotel" name: "SALAD" } | ||
| 1229 | panels { room: "Hotel" name: "BATHING" } | ||
| 1230 | panels { room: "Hotel" name: "MICRO" } | 1294 | 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" } | 1295 | panels { room: "Hotel" name: "TUNING" } |
| 1236 | panels { room: "Hotel" name: "BOXING" } | ||
| 1237 | panels { room: "Hotel" name: "TELEPHONE" } | ||
| 1238 | panels { room: "Hotel" name: "LAW" } | 1296 | panels { room: "Hotel" name: "LAW" } |
| 1239 | panels { room: "Hotel" name: "POKER" } | ||
| 1240 | panels { room: "Hotel" name: "CARD" } | 1297 | panels { room: "Hotel" name: "CARD" } |
| 1241 | panels { room: "Hotel" name: "ROAD" } | 1298 | panels { room: "Hotel" name: "ROAD" } |
| 1242 | panels { room: "Hotel" name: "CHOCOLATE" } | ||
| 1243 | panels { room: "Hotel" name: "DEPART" } | 1299 | panels { room: "Hotel" name: "DEPART" } |
| 1244 | panels { room: "Hotel" name: "WITHDRAW" } | ||
| 1245 | panels { room: "Hotel" name: "QUIT" } | ||
| 1246 | panels { room: "Hotel" name: "LEAVE" } | 1300 | 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" } | 1301 | panels { room: "Hotel" name: "SERVICE" } |
| 1252 | panels { room: "Hotel" name: "FULL" } | ||
| 1253 | panels { room: "Hotel" name: "EVIL" } | ||
| 1254 | panels { room: "Hotel" name: "HONEY" } | 1302 | panels { room: "Hotel" name: "HONEY" } |
| 1255 | panels { room: "Hotel" name: "CRESCENT" } | ||
| 1256 | panels { room: "Hotel" name: "INVALID" } | 1303 | panels { room: "Hotel" name: "INVALID" } |
| 1257 | panels { room: "Hotel" name: "FESTIVAL" } | 1304 | panels { room: "Hotel" name: "FESTIVAL" } |
| 1258 | panels { room: "Hotel" name: "BEAUTIFUL" } | ||
| 1259 | panels { room: "Hotel" name: "WILTED" } | 1305 | panels { room: "Hotel" name: "WILTED" } |
| 1260 | panels { room: "Hotel" name: "DROOPED" } | ||
| 1261 | panels { room: "Hotel" name: "FADED" } | ||
| 1262 | panels { room: "Hotel" name: "WANED" } | 1306 | 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" } | 1307 | panels { room: "Hotel" name: "RAGE" } |
| 1268 | panels { room: "Hotel" name: "BALL" } | ||
| 1269 | panels { room: "Hotel" name: "FOOL" } | ||
| 1270 | panels { room: "Hotel" name: "VERGE" } | 1308 | panels { room: "Hotel" name: "VERGE" } |
| 1271 | panels { room: "Hotel" name: "ART" } | ||
| 1272 | panels { room: "Hotel" name: "EVER" } | 1309 | panels { room: "Hotel" name: "EVER" } |
| 1273 | panels { room: "Hotel" name: "PAIN" } | 1310 | panels { room: "Hotel" name: "PAIN" } |
| 1274 | panels { room: "Hotel" name: "FOOT" } | ||
| 1275 | } | 1311 | } |
| 1276 | doors { | 1312 | doors { |
| 1277 | name: "J2 Door 1" | 1313 | name: "J2 Door 1" |
| @@ -1307,7 +1343,7 @@ doors { | |||
| 1307 | panels { room: "J2 Vestibule" name: "COLORFUL" } | 1343 | panels { room: "J2 Vestibule" name: "COLORFUL" } |
| 1308 | } | 1344 | } |
| 1309 | doors { | 1345 | doors { |
| 1310 | name: "Wonderland Left Door" | 1346 | name: "Wonderland North Door" |
| 1311 | type: ITEM_ONLY | 1347 | type: ITEM_ONLY |
| 1312 | receivers: "Components/Doors/Halls/wonderland_2" | 1348 | receivers: "Components/Doors/Halls/wonderland_2" |
| 1313 | panels { room: "Wonderland" name: "APRIL" } | 1349 | panels { room: "Wonderland" name: "APRIL" } |
| @@ -1421,6 +1457,9 @@ doors { | |||
| 1421 | receivers: "Meshes/Stairs/staircase31/teleportListener" | 1457 | receivers: "Meshes/Stairs/staircase31/teleportListener" |
| 1422 | receivers: "Meshes/Stairs/staircase32/teleportListener2" | 1458 | receivers: "Meshes/Stairs/staircase32/teleportListener2" |
| 1423 | receivers: "Meshes/Stairs/staircase33/teleportListener3" | 1459 | receivers: "Meshes/Stairs/staircase33/teleportListener3" |
| 1460 | receivers: "Panels/Castle Entrance/castle_direction_1/teleportListener" | ||
| 1461 | receivers: "Panels/Castle Entrance/castle_direction_2/teleportListener" | ||
| 1462 | receivers: "Panels/Castle Entrance/castle_direction_3/teleportListener" | ||
| 1424 | panels { room: "North Castle Area" name: "A SUMMER PLACE" } | 1463 | panels { room: "North Castle Area" name: "A SUMMER PLACE" } |
| 1425 | panels { room: "West Castle Area" name: "SONG FACE" } | 1464 | panels { room: "West Castle Area" name: "SONG FACE" } |
| 1426 | panels { room: "South Castle Area" name: "AN OFFER VILLAGE BEFORE LAIR" } | 1465 | panels { room: "South Castle Area" name: "AN OFFER VILLAGE BEFORE LAIR" } |
| @@ -1613,6 +1652,7 @@ doors { | |||
| 1613 | panels { room: "Salt Room" name: "SEASONING" } | 1652 | panels { room: "Salt Room" name: "SEASONING" } |
| 1614 | panels { room: "Pepper Room" name: "SEASONING" } | 1653 | panels { room: "Pepper Room" name: "SEASONING" } |
| 1615 | location_room: "Pepper Room" | 1654 | location_room: "Pepper Room" |
| 1655 | location_name: "Seasonings" | ||
| 1616 | } | 1656 | } |
| 1617 | doors { | 1657 | doors { |
| 1618 | name: "Bow Side" | 1658 | name: "Bow Side" |
| @@ -1648,6 +1688,7 @@ doors { | |||
| 1648 | # Components/Doors/Smileys/blue_1 | 1688 | # Components/Doors/Smileys/blue_1 |
| 1649 | panels { room: "Blue Smiley" name: "SMILE" } | 1689 | panels { room: "Blue Smiley" name: "SMILE" } |
| 1650 | location_room: "Blue Smiley" | 1690 | location_room: "Blue Smiley" |
| 1691 | location_name: "Blue SMILE" | ||
| 1651 | } | 1692 | } |
| 1652 | doors { | 1693 | doors { |
| 1653 | name: "Blue Smiley Annex" | 1694 | name: "Blue Smiley Annex" |
| @@ -1679,6 +1720,7 @@ doors { | |||
| 1679 | receivers: "Components/Doors/Smileys/yellow_2" | 1720 | receivers: "Components/Doors/Smileys/yellow_2" |
| 1680 | panels { room: "Hedges" name: "SMILE" } | 1721 | panels { room: "Hedges" name: "SMILE" } |
| 1681 | location_room: "Hedges" | 1722 | location_room: "Hedges" |
| 1723 | location_name: "Yellow SMILE" | ||
| 1682 | } | 1724 | } |
| 1683 | doors { | 1725 | doors { |
| 1684 | name: "Green Smiley" | 1726 | name: "Green Smiley" |
| @@ -1687,6 +1729,7 @@ doors { | |||
| 1687 | receivers: "Components/Doors/Smileys/green_2" | 1729 | receivers: "Components/Doors/Smileys/green_2" |
| 1688 | panels { room: "Green Smiley" name: "SMILE" } | 1730 | panels { room: "Green Smiley" name: "SMILE" } |
| 1689 | location_room: "Green Smiley" | 1731 | location_room: "Green Smiley" |
| 1732 | location_name: "Green SMILE" | ||
| 1690 | } | 1733 | } |
| 1691 | doors { | 1734 | doors { |
| 1692 | name: "Orange Smiley Exit" | 1735 | name: "Orange Smiley Exit" |
| @@ -1694,6 +1737,7 @@ doors { | |||
| 1694 | receivers: "Components/Doors/Smileys/orange_1" | 1737 | receivers: "Components/Doors/Smileys/orange_1" |
| 1695 | panels { room: "Outside Orange Room" name: "SMILE" } | 1738 | panels { room: "Outside Orange Room" name: "SMILE" } |
| 1696 | location_room: "Outside Orange Room" | 1739 | location_room: "Outside Orange Room" |
| 1740 | location_name: "Orange SMILE" | ||
| 1697 | } | 1741 | } |
| 1698 | doors { | 1742 | doors { |
| 1699 | name: "F Keyholder Door" | 1743 | name: "F Keyholder Door" |
| @@ -1718,6 +1762,7 @@ doors { | |||
| 1718 | type: LOCATION_ONLY | 1762 | type: LOCATION_ONLY |
| 1719 | panels { room: "Red Smiley" name: "SMILE" } | 1763 | panels { room: "Red Smiley" name: "SMILE" } |
| 1720 | location_room: "Red Smiley" | 1764 | location_room: "Red Smiley" |
| 1765 | location_name: "Red SMILE" | ||
| 1721 | } | 1766 | } |
| 1722 | doors { | 1767 | doors { |
| 1723 | name: "Pink Hallway" | 1768 | name: "Pink Hallway" |
| @@ -1792,12 +1837,37 @@ doors { | |||
| 1792 | } | 1837 | } |
| 1793 | doors { | 1838 | doors { |
| 1794 | name: "Near Sweet Brown Door" | 1839 | name: "Near Sweet Brown Door" |
| 1795 | type: STANDARD | 1840 | type: ITEM_ONLY |
| 1841 | legacy_location: true | ||
| 1796 | receivers: "Components/Doors/Halls 2/halls_2" | 1842 | receivers: "Components/Doors/Halls 2/halls_2" |
| 1797 | panels { room: "Sweet Foyer" name: "RENT (4)" } | 1843 | panels { room: "Sweet Foyer" name: "RENT (4)" } |
| 1798 | location_room: "Sweet Foyer" | 1844 | location_room: "Sweet Foyer" |
| 1799 | } | 1845 | } |
| 1800 | doors { | 1846 | doors { |
| 1847 | name: "Rent Panels" | ||
| 1848 | type: LOCATION_ONLY | ||
| 1849 | panels { room: "Sweet Foyer" name: "RENT (1)" } | ||
| 1850 | panels { room: "Sweet Foyer" name: "RENT (2)" } | ||
| 1851 | panels { room: "Sweet Foyer" name: "RENT (3)" } | ||
| 1852 | panels { room: "Sweet Foyer" name: "RENT (4)" } | ||
| 1853 | location_room: "Sweet Foyer" | ||
| 1854 | } | ||
| 1855 | doors { | ||
| 1856 | name: "Equality Panels" | ||
| 1857 | type: LOCATION_ONLY | ||
| 1858 | panels { room: "Sweet Foyer" name: "EQUAL" } | ||
| 1859 | panels { room: "Sweet Foyer" name: "QUALITY" } | ||
| 1860 | location_room: "Sweet Foyer" | ||
| 1861 | location_name: "EQUAL, QUALITY" | ||
| 1862 | } | ||
| 1863 | doors { | ||
| 1864 | name: "Orange Panels" | ||
| 1865 | type: LOCATION_ONLY | ||
| 1866 | panels { room: "Blue Smiley Annex" name: "ORANGE (1)" } | ||
| 1867 | panels { room: "Blue Smiley Annex" name: "ORANGE (2)" } | ||
| 1868 | location_room: "Blue Smiley Annex" | ||
| 1869 | } | ||
| 1870 | doors { | ||
| 1801 | name: "Red Room Entrance" | 1871 | name: "Red Room Entrance" |
| 1802 | type: STANDARD | 1872 | type: STANDARD |
| 1803 | receivers: "Components/Doors/Halls 2/halls_3" | 1873 | receivers: "Components/Doors/Halls 2/halls_3" |
| @@ -1952,7 +2022,7 @@ doors { | |||
| 1952 | panels { room: "Gray Color Backside" name: "LAST" } | 2022 | panels { room: "Gray Color Backside" name: "LAST" } |
| 1953 | panels { room: "Gray Color Backside" name: "RISE" } | 2023 | panels { room: "Gray Color Backside" name: "RISE" } |
| 1954 | location_room: "Gray Color Backside" | 2024 | location_room: "Gray Color Backside" |
| 1955 | location_name: "Light Green Hex" | 2025 | location_name: "Pale Green Hex" |
| 1956 | } | 2026 | } |
| 1957 | doors { | 2027 | doors { |
| 1958 | name: "South Castle Area Back Door" | 2028 | name: "South Castle Area Back Door" |
| @@ -2112,8 +2182,12 @@ doors { | |||
| 2112 | doors { | 2182 | doors { |
| 2113 | name: "C Keyholder Blocker" | 2183 | name: "C Keyholder Blocker" |
| 2114 | type: EVENT | 2184 | type: EVENT |
| 2115 | # Components/Doors/Unincorporated/temple_foyer_7 | 2185 | receivers: "Components/Doors/Unincorporated/temple_foyer_7" |
| 2116 | switches: "lavender_cubes" | 2186 | panels { |
| 2187 | map: "the_ancient" | ||
| 2188 | room: "Inside" | ||
| 2189 | name: "COLOR" | ||
| 2190 | } | ||
| 2117 | } | 2191 | } |
| 2118 | doors { | 2192 | doors { |
| 2119 | name: "Computer Room Back Door" | 2193 | name: "Computer Room Back Door" |
| @@ -2183,6 +2257,7 @@ doors { | |||
| 2183 | receivers: "Components/Doors/Unincorporated/temple_foyer_6" | 2257 | receivers: "Components/Doors/Unincorporated/temple_foyer_6" |
| 2184 | panels { room: "Globe Room" name: "WORD" } | 2258 | panels { room: "Globe Room" name: "WORD" } |
| 2185 | location_room: "Globe Room" | 2259 | location_room: "Globe Room" |
| 2260 | location_name: "Sticks and Stones" | ||
| 2186 | } | 2261 | } |
| 2187 | doors { | 2262 | doors { |
| 2188 | name: "Castle Numbers Puzzle" | 2263 | name: "Castle Numbers Puzzle" |
| @@ -2302,3 +2377,95 @@ doors { | |||
| 2302 | receivers: "Components/Paintings/Temple of the Eyes/eyeRedStart/teleportListener" | 2377 | receivers: "Components/Paintings/Temple of the Eyes/eyeRedStart/teleportListener" |
| 2303 | double_letters: true | 2378 | double_letters: true |
| 2304 | } | 2379 | } |
| 2380 | doors { | ||
| 2381 | name: "Lime Hexes" | ||
| 2382 | type: LOCATION_ONLY | ||
| 2383 | panels { room: "Tree Entrance" name: "RAT" } | ||
| 2384 | panels { room: "Tree Entrance" name: "DIFFERENCE" } | ||
| 2385 | panels { room: "Tree Entrance" name: "LEANS" } | ||
| 2386 | panels { room: "Tree Entrance" name: "QUESTION" } | ||
| 2387 | panels { room: "Tree Entrance" name: "WHERE" } | ||
| 2388 | panels { room: "Tree Entrance" name: "SUNDER" } | ||
| 2389 | location_room: "Tree Entrance" | ||
| 2390 | } | ||
| 2391 | doors { | ||
| 2392 | name: "Theo Panels" | ||
| 2393 | type: LOCATION_ONLY | ||
| 2394 | panels { room: "House" name: "GOAT" } | ||
| 2395 | panels { room: "House" name: "AMAZE" } | ||
| 2396 | panels { room: "House" name: "SKINNYHIM" } | ||
| 2397 | panels { room: "House" name: "THEO" } | ||
| 2398 | location_room: "House" | ||
| 2399 | location_name: "All Puzzles" | ||
| 2400 | } | ||
| 2401 | doors { | ||
| 2402 | name: "West Spire Panel" | ||
| 2403 | type: LOCATION_ONLY | ||
| 2404 | panels { room: "West Spire" name: "MISSING" } | ||
| 2405 | location_room: "West Spire" | ||
| 2406 | location_name: "MISSING" | ||
| 2407 | } | ||
| 2408 | doors { | ||
| 2409 | name: "Tree Panels" | ||
| 2410 | type: LOCATION_ONLY | ||
| 2411 | panels { room: "Red Color Door" name: "FIR" } | ||
| 2412 | panels { room: "Red Color Door" name: "OAK" } | ||
| 2413 | panels { room: "Red Color Door" name: "PINE" } | ||
| 2414 | panels { room: "Red Color Door" name: "ASH" } | ||
| 2415 | location_room: "Red Color Door" | ||
| 2416 | location_name: "ASH, FIR, OAK, PINE" | ||
| 2417 | } | ||
| 2418 | doors { | ||
| 2419 | name: "Teal Panel" | ||
| 2420 | type: LOCATION_ONLY | ||
| 2421 | panels { room: "Outside Book Room" name: "TEAL" } | ||
| 2422 | location_room: "Outside Book Room" | ||
| 2423 | location_name: "TEAL" | ||
| 2424 | } | ||
| 2425 | doors { | ||
| 2426 | name: "Direction Panels" | ||
| 2427 | type: LOCATION_ONLY | ||
| 2428 | panels { room: "Rainbow Color Doors" name: "DIRECTION (1)" } | ||
| 2429 | panels { room: "Rainbow Color Doors" name: "DIRECTION (2)" } | ||
| 2430 | panels { room: "Rainbow Color Doors" name: "DIRECTION (3)" } | ||
| 2431 | location_room: "Rainbow Color Doors" | ||
| 2432 | } | ||
| 2433 | doors { | ||
| 2434 | name: "Nursery Panels" | ||
| 2435 | type: LOCATION_ONLY | ||
| 2436 | panels { room: "Nursery" name: "Paintings" } | ||
| 2437 | panels { room: "Nursery" name: "?" } | ||
| 2438 | location_room: "Nursery" | ||
| 2439 | location_name: "Paintings, ?" | ||
| 2440 | } | ||
| 2441 | doors { | ||
| 2442 | name: "Near H Keyholder Panel" | ||
| 2443 | type: LOCATION_ONLY | ||
| 2444 | panels { room: "Outside House" name: "SILENCE" } | ||
| 2445 | location_room: "Outside House" | ||
| 2446 | location_name: "SILENCE" | ||
| 2447 | } | ||
| 2448 | doors { | ||
| 2449 | name: "Plum Panels" | ||
| 2450 | type: LOCATION_ONLY | ||
| 2451 | panels { room: "Outside Hedges" name: "PLUM (1)" } | ||
| 2452 | panels { room: "Outside Hedges" name: "PLUM (2)" } | ||
| 2453 | location_room: "Outside Hedges" | ||
| 2454 | } | ||
| 2455 | doors { | ||
| 2456 | name: "Yellow Smiley Annex Panels" | ||
| 2457 | type: LOCATION_ONLY | ||
| 2458 | panels { room: "Yellow Smiley Annex" name: "BELL" } | ||
| 2459 | panels { room: "Yellow Smiley Annex" name: "COW" } | ||
| 2460 | location_room: "Yellow Smiley Annex" | ||
| 2461 | location_name: "BELL, COW" | ||
| 2462 | } | ||
| 2463 | doors { | ||
| 2464 | name: "Farewell Little Lamb Panels" | ||
| 2465 | type: LOCATION_ONLY | ||
| 2466 | panels { room: "Purple Room South" name: "FAREWELL" } | ||
| 2467 | panels { room: "Purple Room South" name: "LITTLE" } | ||
| 2468 | panels { room: "Purple Room South" name: "LAMB" } | ||
| 2469 | location_room: "Purple Room South" | ||
| 2470 | location_name: "FAREWELL, LITTLE, LAMB" | ||
| 2471 | } | ||
| 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/Composite Room S.txtpb b/data/maps/daedalus/rooms/Composite Room S.txtpb index 3773034..0cb69bf 100644 --- a/data/maps/daedalus/rooms/Composite Room S.txtpb +++ b/data/maps/daedalus/rooms/Composite Room S.txtpb | |||
| @@ -194,5 +194,8 @@ panels { | |||
| 194 | } | 194 | } |
| 195 | ports { | 195 | ports { |
| 196 | name: "ENTRY" | 196 | name: "ENTRY" |
| 197 | display_name: "Composite Room Worldport" | ||
| 197 | path: "Components/Warps/Worldports/worldport16" | 198 | path: "Components/Warps/Worldports/worldport16" |
| 199 | destination { x: -84 y: 0 z: 81 } | ||
| 200 | rotation: 270 | ||
| 198 | } | 201 | } |
| diff --git a/data/maps/daedalus/rooms/Entry Shortcut.txtpb b/data/maps/daedalus/rooms/Entry Shortcut.txtpb index 63202ba..3c3abb7 100644 --- a/data/maps/daedalus/rooms/Entry Shortcut.txtpb +++ b/data/maps/daedalus/rooms/Entry Shortcut.txtpb | |||
| @@ -10,5 +10,8 @@ panels { | |||
| 10 | } | 10 | } |
| 11 | ports { | 11 | ports { |
| 12 | name: "ENTRY" | 12 | name: "ENTRY" |
| 13 | display_name: "Starting Room West Wall Middle Worldport" | ||
| 13 | path: "Components/Warps/Worldports/worldport4" | 14 | path: "Components/Warps/Worldports/worldport4" |
| 15 | destination { x: -21 y: 0 z: -4 } | ||
| 16 | rotation: 90 | ||
| 14 | } | 17 | } |
| diff --git a/data/maps/daedalus/rooms/Hedges Tower.txtpb b/data/maps/daedalus/rooms/Hedges Tower.txtpb index 3031c1d..3b88cd7 100644 --- a/data/maps/daedalus/rooms/Hedges Tower.txtpb +++ b/data/maps/daedalus/rooms/Hedges Tower.txtpb | |||
| @@ -2,5 +2,7 @@ name: "Hedges Tower" | |||
| 2 | panel_display_name: "Hedges" | 2 | panel_display_name: "Hedges" |
| 3 | ports { | 3 | ports { |
| 4 | name: "TENACIOUS" | 4 | name: "TENACIOUS" |
| 5 | display_name: "Hedge Maze Tower" | ||
| 5 | path: "Components/Warps/Worldports/worldport13" | 6 | path: "Components/Warps/Worldports/worldport13" |
| 7 | no_shuffle: true | ||
| 6 | } | 8 | } |
| diff --git a/data/maps/daedalus/rooms/Hotel.txtpb b/data/maps/daedalus/rooms/Hotel.txtpb index d2a05db..d590841 100644 --- a/data/maps/daedalus/rooms/Hotel.txtpb +++ b/data/maps/daedalus/rooms/Hotel.txtpb | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | name: "Hotel" | 1 | name: "Hotel" |
| 2 | panel_display_name: "Southwest Area" | 2 | panel_display_name: "Southwest Area" |
| 3 | # TODO: Something has to be changed in-game so that the puzzles don't disappear | ||
| 4 | # and thus cause them to become unsolvable. | ||
| 5 | panels { | 3 | panels { |
| 6 | name: "MARLIN" | 4 | name: "MARLIN" |
| 7 | path: "Panels/Connections/connections_2" | 5 | path: "Panels/Connections/connections_2" |
| diff --git a/data/maps/daedalus/rooms/Moat.txtpb b/data/maps/daedalus/rooms/Moat.txtpb index cbb5d16..7bdb040 100644 --- a/data/maps/daedalus/rooms/Moat.txtpb +++ b/data/maps/daedalus/rooms/Moat.txtpb | |||
| @@ -6,5 +6,8 @@ paintings { | |||
| 6 | } | 6 | } |
| 7 | ports { | 7 | ports { |
| 8 | name: "HIVE" | 8 | name: "HIVE" |
| 9 | display_name: "Moat Worldport" | ||
| 9 | path: "Components/Warps/Worldports/worldport9" | 10 | path: "Components/Warps/Worldports/worldport9" |
| 11 | destination { x: 64 y: 1 z: 24.5 } | ||
| 12 | rotation: 0 | ||
| 10 | } | 13 | } |
| diff --git a/data/maps/daedalus/rooms/Outside Hedges.txtpb b/data/maps/daedalus/rooms/Outside Hedges.txtpb index 9f32e26..fc765d9 100644 --- a/data/maps/daedalus/rooms/Outside Hedges.txtpb +++ b/data/maps/daedalus/rooms/Outside Hedges.txtpb | |||
| @@ -76,5 +76,8 @@ paintings { | |||
| 76 | } | 76 | } |
| 77 | ports { | 77 | ports { |
| 78 | name: "REVITALIZED" | 78 | name: "REVITALIZED" |
| 79 | display_name: "Near Hedges Plum Hallway" | ||
| 79 | path: "Components/Warps/Worldports/worldport7" | 80 | path: "Components/Warps/Worldports/worldport7" |
| 81 | destination { x: 45 y: 0 z: 94 } | ||
| 82 | rotation: 270 | ||
| 80 | } | 83 | } |
| diff --git a/data/maps/daedalus/rooms/Purple Hallway From Great.txtpb b/data/maps/daedalus/rooms/Purple Hallway From Great.txtpb index 5284133..73f8391 100644 --- a/data/maps/daedalus/rooms/Purple Hallway From Great.txtpb +++ b/data/maps/daedalus/rooms/Purple Hallway From Great.txtpb | |||
| @@ -2,5 +2,8 @@ name: "Purple Hallway From Great" | |||
| 2 | panel_display_name: "South Area" | 2 | panel_display_name: "South Area" |
| 3 | ports { | 3 | ports { |
| 4 | name: "GREAT" | 4 | name: "GREAT" |
| 5 | display_name: "Near Sweet Purple Hallway" | ||
| 5 | path: "Components/Warps/Worldports/worldport17" | 6 | path: "Components/Warps/Worldports/worldport17" |
| 7 | destination { x: -24 y: 0 z: 61 } | ||
| 8 | rotation: 270 | ||
| 6 | } | 9 | } |
| diff --git a/data/maps/daedalus/rooms/Quiet Entrance.txtpb b/data/maps/daedalus/rooms/Quiet Entrance.txtpb index 08fbcc7..cd0d0ed 100644 --- a/data/maps/daedalus/rooms/Quiet Entrance.txtpb +++ b/data/maps/daedalus/rooms/Quiet Entrance.txtpb | |||
| @@ -16,5 +16,8 @@ paintings { | |||
| 16 | } | 16 | } |
| 17 | ports { | 17 | ports { |
| 18 | name: "QUIET" | 18 | name: "QUIET" |
| 19 | display_name: "Near Planet Painting Worldport" | ||
| 19 | path: "Components/Warps/Worldports/worldport5" | 20 | path: "Components/Warps/Worldports/worldport5" |
| 21 | destination { x: -32 y: 0 z: -40 } | ||
| 22 | rotation: 90 | ||
| 20 | } | 23 | } |
| diff --git a/data/maps/daedalus/rooms/Rain Side.txtpb b/data/maps/daedalus/rooms/Rain Side.txtpb index 6906aef..2a62525 100644 --- a/data/maps/daedalus/rooms/Rain Side.txtpb +++ b/data/maps/daedalus/rooms/Rain Side.txtpb | |||
| @@ -10,5 +10,8 @@ panels { | |||
| 10 | } | 10 | } |
| 11 | ports { | 11 | ports { |
| 12 | name: "BEARER" | 12 | name: "BEARER" |
| 13 | display_name: "Rain Panel Worldport" | ||
| 13 | path: "Components/Warps/Worldports/worldport11" | 14 | path: "Components/Warps/Worldports/worldport11" |
| 15 | destination { x: 93.5 y: 0 z: 27 } | ||
| 16 | rotation: 90 | ||
| 14 | } | 17 | } |
| 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/Starting Room.txtpb b/data/maps/daedalus/rooms/Starting Room.txtpb index 62fc96a..1b07eb4 100644 --- a/data/maps/daedalus/rooms/Starting Room.txtpb +++ b/data/maps/daedalus/rooms/Starting Room.txtpb | |||
| @@ -9,5 +9,8 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "GREAT" | 11 | name: "GREAT" |
| 12 | display_name: "Starting Room South Wall Middle Worldport" | ||
| 12 | path: "Components/Warps/Worldports/worldport" | 13 | path: "Components/Warps/Worldports/worldport" |
| 14 | destination { x: 0 y: 0 z: 11 } | ||
| 15 | rotation: 0 | ||
| 13 | } | 16 | } |
| diff --git a/data/maps/daedalus/rooms/Sweet Foyer.txtpb b/data/maps/daedalus/rooms/Sweet Foyer.txtpb index 03c8262..d1167eb 100644 --- a/data/maps/daedalus/rooms/Sweet Foyer.txtpb +++ b/data/maps/daedalus/rooms/Sweet Foyer.txtpb | |||
| @@ -51,9 +51,15 @@ paintings { | |||
| 51 | } | 51 | } |
| 52 | ports { | 52 | ports { |
| 53 | name: "SWEET1" | 53 | name: "SWEET1" |
| 54 | display_name: "Sweet East Entrance" | ||
| 54 | path: "Components/Warps/Worldports/worldport14" | 55 | path: "Components/Warps/Worldports/worldport14" |
| 56 | destination { x: -27 y: 0 z: 76.5 } | ||
| 57 | rotation: 270 | ||
| 55 | } | 58 | } |
| 56 | ports { | 59 | ports { |
| 57 | name: "SWEET2" | 60 | name: "SWEET2" |
| 61 | display_name: "Sweet West Entrance" | ||
| 58 | path: "Components/Warps/Worldports/worldport15" | 62 | path: "Components/Warps/Worldports/worldport15" |
| 63 | destination { x: -36 y: 0 z: 76.5 } | ||
| 64 | rotation: 90 | ||
| 59 | } | 65 | } |
| diff --git a/data/maps/daedalus/rooms/Tree Entrance.txtpb b/data/maps/daedalus/rooms/Tree Entrance.txtpb index 2b98178..1453790 100644 --- a/data/maps/daedalus/rooms/Tree Entrance.txtpb +++ b/data/maps/daedalus/rooms/Tree Entrance.txtpb | |||
| @@ -48,5 +48,8 @@ panels { | |||
| 48 | } | 48 | } |
| 49 | ports { | 49 | ports { |
| 50 | name: "TREE" | 50 | name: "TREE" |
| 51 | display_name: "Near Pumpkin Brown Hallway" | ||
| 51 | path: "Components/Warps/Worldports/worldport12" | 52 | path: "Components/Warps/Worldports/worldport12" |
| 53 | destination { x: 41 y: 0 z: 50.5 } | ||
| 54 | rotation: 0 | ||
| 52 | } | 55 | } |
| diff --git a/data/maps/daedalus/rooms/Unkempt Entrance.txtpb b/data/maps/daedalus/rooms/Unkempt Entrance.txtpb index c0cb0df..0a39ee0 100644 --- a/data/maps/daedalus/rooms/Unkempt Entrance.txtpb +++ b/data/maps/daedalus/rooms/Unkempt Entrance.txtpb | |||
| @@ -2,5 +2,8 @@ name: "Unkempt Entrance" | |||
| 2 | panel_display_name: "O2 Room" | 2 | panel_display_name: "O2 Room" |
| 3 | ports { | 3 | ports { |
| 4 | name: "UNKEMPT" | 4 | name: "UNKEMPT" |
| 5 | display_name: "O2 Room Worldport" | ||
| 5 | path: "Components/Warps/Worldports/worldport6" | 6 | path: "Components/Warps/Worldports/worldport6" |
| 7 | destination { x: -61 y: 0 z: 95 } | ||
| 8 | rotation: 270 | ||
| 6 | } | 9 | } |
| diff --git a/data/maps/daedalus/rooms/White Hallway From Entry.txtpb b/data/maps/daedalus/rooms/White Hallway From Entry.txtpb index a172313..d3659d3 100644 --- a/data/maps/daedalus/rooms/White Hallway From Entry.txtpb +++ b/data/maps/daedalus/rooms/White Hallway From Entry.txtpb | |||
| @@ -1,12 +1,9 @@ | |||
| 1 | name: "White Hallway From Entry" | 1 | name: "White Hallway From Entry" |
| 2 | panel_display_name: "Southwest Area" | 2 | panel_display_name: "Southwest Area" |
| 3 | # Not exactly sure what to do with this yet. In unshuffled connections, the door | ||
| 4 | # here should be paired with the door on the other end (which is vanilla) even | ||
| 5 | # if control center color doors are shuffled. But when connections are shuffled | ||
| 6 | # maybe this should be shuffled separately? I might also want to find a way to | ||
| 7 | # register when there's a door immediately outside of a connection so that when | ||
| 8 | # two connections behind doors are shuffled together, their doors can be paired. | ||
| 9 | ports { | 3 | ports { |
| 10 | name: "ENTRY" | 4 | name: "ENTRY" |
| 5 | display_name: "Near Globe White Hallway" | ||
| 11 | path: "Components/Warps/Worldports/worldport10" | 6 | path: "Components/Warps/Worldports/worldport10" |
| 7 | destination { x: -46 y: 0 z: 23 } | ||
| 8 | rotation: 90 | ||
| 12 | } | 9 | } |
| diff --git a/data/maps/daedalus/rooms/Wonderland.txtpb b/data/maps/daedalus/rooms/Wonderland.txtpb index 4b69e99..b4782d2 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" |
| @@ -38,5 +37,8 @@ panels { | |||
| 38 | } | 37 | } |
| 39 | ports { | 38 | ports { |
| 40 | name: "WONDROUS" | 39 | name: "WONDROUS" |
| 40 | display_name: "Wonderland Worldport" | ||
| 41 | path: "Components/Warps/Worldports/worldport3" | 41 | path: "Components/Warps/Worldports/worldport3" |
| 42 | destination { x: -104 y: 0 z: -69 } | ||
| 43 | rotation: 180 | ||
| 42 | } | 44 | } |
| diff --git a/data/maps/daedalus/rooms/Yellow Color Door.txtpb b/data/maps/daedalus/rooms/Yellow Color Door.txtpb index e44658c..61d206b 100644 --- a/data/maps/daedalus/rooms/Yellow Color Door.txtpb +++ b/data/maps/daedalus/rooms/Yellow Color Door.txtpb | |||
| @@ -30,5 +30,8 @@ paintings { | |||
| 30 | } | 30 | } |
| 31 | ports { | 31 | ports { |
| 32 | name: "FOURROOMS" | 32 | name: "FOURROOMS" |
| 33 | display_name: "Near Yellow Worldport" | ||
| 33 | path: "Components/Warps/Worldports/worldport8" | 34 | path: "Components/Warps/Worldports/worldport8" |
| 35 | destination { x: 92.5 y: 0 z: -62 } | ||
| 36 | rotation: 90 | ||
| 34 | } | 37 | } |
| diff --git a/data/maps/demo/connections.txtpb b/data/maps/demo/connections.txtpb new file mode 100644 index 0000000..fd9a918 --- /dev/null +++ b/data/maps/demo/connections.txtpb | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | connections { | ||
| 2 | from_room: "Main Area" | ||
| 3 | to_room: "Center Building" | ||
| 4 | door { name: "Center Building" } | ||
| 5 | } | ||
| 6 | connections { | ||
| 7 | from_room: "Main Area" | ||
| 8 | to_room: "Flower Hallway" | ||
| 9 | door { name: "Flower Hallway" } | ||
| 10 | } | ||
| 11 | connections { | ||
| 12 | from_room: "Main Area" | ||
| 13 | to_room: "Tower" | ||
| 14 | door { name: "Tower Entrance" } | ||
| 15 | } | ||
| 16 | connections { | ||
| 17 | from_room: "Main Area" | ||
| 18 | to_room: "Castle" | ||
| 19 | door { name: "Castle" } | ||
| 20 | } | ||
| 21 | connections { | ||
| 22 | from_room: "Main Area" | ||
| 23 | to_room: "Backside Area" | ||
| 24 | door { name: "Backside Entrance" } | ||
| 25 | } | ||
| 26 | connections { | ||
| 27 | from_room: "Backside Area" | ||
| 28 | to_room: "Mastery" | ||
| 29 | door { name: "Mastery Door" } | ||
| 30 | } | ||
| diff --git a/data/maps/demo/doors.txtpb b/data/maps/demo/doors.txtpb new file mode 100644 index 0000000..37e0bae --- /dev/null +++ b/data/maps/demo/doors.txtpb | |||
| @@ -0,0 +1,161 @@ | |||
| 1 | doors { | ||
| 2 | name: "Center Building" | ||
| 3 | type: STANDARD | ||
| 4 | receivers: "Components/Doors/demo_1" | ||
| 5 | panels { room: "Main Area" name: "HI" } | ||
| 6 | location_room: "Main Area" | ||
| 7 | } | ||
| 8 | doors { | ||
| 9 | name: "Flower Hallway" | ||
| 10 | type: STANDARD | ||
| 11 | receivers: "Components/Doors/demo_32" | ||
| 12 | panels { room: "Main Area" name: "TEES" } | ||
| 13 | location_room: "Main Area" | ||
| 14 | } | ||
| 15 | doors { | ||
| 16 | name: "Center Building Panels" | ||
| 17 | type: LOCATION_ONLY | ||
| 18 | panels { room: "Center Building" name: "WORLD" } | ||
| 19 | panels { room: "Center Building" name: "FUZZIES" } | ||
| 20 | panels { room: "Main Area" name: "COLORFUL" } | ||
| 21 | panels { room: "Main Area" name: "WORD" } | ||
| 22 | location_room: "Center Building" | ||
| 23 | location_name: "COLORFUL, FUZZIES, WORD, WORLD" | ||
| 24 | } | ||
| 25 | doors { | ||
| 26 | name: "Orange Door" | ||
| 27 | type: LOCATION_ONLY | ||
| 28 | panels { room: "Main Area" name: "HID" } | ||
| 29 | panels { room: "Main Area" name: "MESS" } | ||
| 30 | panels { room: "Main Area" name: "DEN" } | ||
| 31 | panels { room: "Main Area" name: "AGES" } | ||
| 32 | location_room: "Main Area" | ||
| 33 | location_name: "AGES, DEN, HID, MESS" | ||
| 34 | } | ||
| 35 | doors { | ||
| 36 | name: "Purple Door" | ||
| 37 | type: LOCATION_ONLY | ||
| 38 | panels { room: "Main Area" name: "COUNTER" } | ||
| 39 | panels { room: "Main Area" name: "POSSIBLE" } | ||
| 40 | panels { room: "Main Area" name: "PACES" } | ||
| 41 | panels { room: "Main Area" name: "CLOCKWISE" } | ||
| 42 | location_room: "Main Area" | ||
| 43 | location_name: "CLOCKWISE, COUNTER, PACES, POSSIBLE" | ||
| 44 | } | ||
| 45 | doors { | ||
| 46 | name: "Yellow Door" | ||
| 47 | type: LOCATION_ONLY | ||
| 48 | panels { room: "Main Area" name: "ANY" } | ||
| 49 | panels { room: "Main Area" name: "RODS" } | ||
| 50 | panels { room: "Main Area" name: "TWO" } | ||
| 51 | panels { room: "Main Area" name: "TALK" } | ||
| 52 | panels { room: "Main Area" name: "SECRETIVE" } | ||
| 53 | panels { room: "Main Area" name: "TOADS" } | ||
| 54 | panels { room: "Main Area" name: "TON" } | ||
| 55 | panels { room: "Main Area" name: "MIND" } | ||
| 56 | panels { room: "Main Area" name: "END" } | ||
| 57 | panels { room: "Main Area" name: "RAD" } | ||
| 58 | panels { room: "Main Area" name: "TOO" } | ||
| 59 | panels { room: "Main Area" name: "STALK" } | ||
| 60 | location_room: "Main Area" | ||
| 61 | location_name: "Yellow Area Puzzles" | ||
| 62 | } | ||
| 63 | doors { | ||
| 64 | name: "Red Door" | ||
| 65 | type: LOCATION_ONLY | ||
| 66 | panels { room: "Castle" name: "SERIES" } | ||
| 67 | location_room: "Castle" | ||
| 68 | location_name: "SERIES" | ||
| 69 | } | ||
| 70 | doors { | ||
| 71 | name: "Castle" | ||
| 72 | type: STANDARD | ||
| 73 | receivers: "Components/Doors/demo_15" | ||
| 74 | panels { room: "Main Area" name: "CASTS" } | ||
| 75 | location_room: "Main Area" | ||
| 76 | } | ||
| 77 | doors { | ||
| 78 | name: "Scavenger Hunt" | ||
| 79 | type: LOCATION_ONLY | ||
| 80 | panels { room: "Main Area" name: "S" } | ||
| 81 | panels { room: "Main Area" name: "C" } | ||
| 82 | panels { room: "Main Area" name: "A" } | ||
| 83 | panels { room: "Main Area" name: "V" } | ||
| 84 | panels { room: "Main Area" name: "E (1)" } | ||
| 85 | panels { room: "Main Area" name: "N" } | ||
| 86 | panels { room: "Castle" name: "G" } | ||
| 87 | panels { room: "Main Area" name: "E (2)" } | ||
| 88 | panels { room: "Main Area" name: "R" } | ||
| 89 | location_room: "Castle" | ||
| 90 | } | ||
| 91 | doors { | ||
| 92 | name: "Gold Door" | ||
| 93 | type: LOCATION_ONLY | ||
| 94 | panels { room: "Main Area" name: "DISCOVER" } | ||
| 95 | panels { room: "Main Area" name: "FAMILY" } | ||
| 96 | panels { room: "Flower Hallway" name: "LACES" } | ||
| 97 | location_room: "Flower Hallway" | ||
| 98 | location_name: "DISCOVER, FAMILY, LACES" | ||
| 99 | } | ||
| 100 | doors { | ||
| 101 | name: "Tower Entrance" | ||
| 102 | type: EVENT | ||
| 103 | panels { room: "Main Area" name: "HI" } | ||
| 104 | panels { room: "Main Area" name: "ART" } | ||
| 105 | panels { room: "Main Area" name: "TEES" } | ||
| 106 | panels { room: "Center Building" name: "WORLD" } | ||
| 107 | panels { room: "Center Building" name: "FUZZIES" } | ||
| 108 | panels { room: "Main Area" name: "COLORFUL" } | ||
| 109 | panels { room: "Main Area" name: "WORD" } | ||
| 110 | panels { room: "Main Area" name: "HID" } | ||
| 111 | panels { room: "Main Area" name: "MESS" } | ||
| 112 | panels { room: "Main Area" name: "DEN" } | ||
| 113 | panels { room: "Main Area" name: "AGES" } | ||
| 114 | panels { room: "Main Area" name: "COUNTER" } | ||
| 115 | panels { room: "Main Area" name: "POSSIBLE" } | ||
| 116 | panels { room: "Main Area" name: "PACES" } | ||
| 117 | panels { room: "Main Area" name: "CLOCKWISE" } | ||
| 118 | panels { room: "Main Area" name: "ANY" } | ||
| 119 | panels { room: "Main Area" name: "RODS" } | ||
| 120 | panels { room: "Main Area" name: "TWO" } | ||
| 121 | panels { room: "Main Area" name: "TALK" } | ||
| 122 | panels { room: "Main Area" name: "SECRETIVE" } | ||
| 123 | panels { room: "Main Area" name: "TOADS" } | ||
| 124 | panels { room: "Main Area" name: "TON" } | ||
| 125 | panels { room: "Main Area" name: "MIND" } | ||
| 126 | panels { room: "Main Area" name: "END" } | ||
| 127 | panels { room: "Main Area" name: "RAD" } | ||
| 128 | panels { room: "Main Area" name: "TOO" } | ||
| 129 | panels { room: "Main Area" name: "STALK" } | ||
| 130 | panels { room: "Castle" name: "SERIES" } | ||
| 131 | panels { room: "Main Area" name: "CASTS" } | ||
| 132 | panels { room: "Main Area" name: "HAZES" } | ||
| 133 | panels { room: "Main Area" name: "DAZES" } | ||
| 134 | panels { room: "Main Area" name: "GAZES" } | ||
| 135 | panels { room: "Main Area" name: "S" } | ||
| 136 | panels { room: "Main Area" name: "C" } | ||
| 137 | panels { room: "Main Area" name: "A" } | ||
| 138 | panels { room: "Main Area" name: "V" } | ||
| 139 | panels { room: "Main Area" name: "E (1)" } | ||
| 140 | panels { room: "Main Area" name: "N" } | ||
| 141 | panels { room: "Castle" name: "G" } | ||
| 142 | panels { room: "Main Area" name: "E (2)" } | ||
| 143 | panels { room: "Main Area" name: "R" } | ||
| 144 | panels { room: "Main Area" name: "Blank" } | ||
| 145 | panels { room: "Main Area" name: "DISCOVER" } | ||
| 146 | panels { room: "Main Area" name: "FAMILY" } | ||
| 147 | panels { room: "Flower Hallway" name: "LACES" } | ||
| 148 | } | ||
| 149 | doors { | ||
| 150 | name: "Backside Entrance" | ||
| 151 | type: EVENT | ||
| 152 | panels { room: "Tower" name: "ENDS (1)" } | ||
| 153 | } | ||
| 154 | doors { | ||
| 155 | name: "Mastery Door" | ||
| 156 | type: EVENT | ||
| 157 | panels { room: "Backside Area" name: "BACKSIDE" } | ||
| 158 | panels { room: "Backside Area" name: "DOORWAYS" } | ||
| 159 | panels { room: "Backside Area" name: "SEE" } | ||
| 160 | panels { room: "Backside Area" name: "ENDS (2)" } | ||
| 161 | } | ||
| diff --git a/data/maps/demo/metadata.txtpb b/data/maps/demo/metadata.txtpb new file mode 100644 index 0000000..4f61386 --- /dev/null +++ b/data/maps/demo/metadata.txtpb | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | display_name: "Demo" | ||
| 2 | type: DEMO | ||
| 3 | # This painting is above a panel and can't be entered. | ||
| 4 | excluded_nodes: "Meshes/owl" | ||
| 5 | # The map's mastery is created at runtime. | ||
| 6 | custom_nodes: "Components/Collectables/collectable" | ||
| diff --git a/data/maps/demo/rooms/Backside Area.txtpb b/data/maps/demo/rooms/Backside Area.txtpb new file mode 100644 index 0000000..ee31973 --- /dev/null +++ b/data/maps/demo/rooms/Backside Area.txtpb | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | name: "Backside Area" | ||
| 2 | panels { | ||
| 3 | name: "BACKSIDE" | ||
| 4 | path: "Panels/Endings/demo_41" | ||
| 5 | clue: "backside" | ||
| 6 | answer: "back" | ||
| 7 | } | ||
| 8 | panels { | ||
| 9 | name: "DOORWAYS" | ||
| 10 | path: "Panels/Endings/demo_42" | ||
| 11 | clue: "doorways" | ||
| 12 | answer: "doors" | ||
| 13 | } | ||
| 14 | panels { | ||
| 15 | name: "SEE" | ||
| 16 | path: "Panels/Endings/demo_43" | ||
| 17 | clue: "see" | ||
| 18 | answer: "secret" | ||
| 19 | } | ||
| 20 | panels { | ||
| 21 | name: "ENDS (2)" | ||
| 22 | path: "Panels/Endings/demo_44" | ||
| 23 | clue: "ends" | ||
| 24 | answer: "endings" | ||
| 25 | } | ||
| diff --git a/data/maps/demo/rooms/Castle.txtpb b/data/maps/demo/rooms/Castle.txtpb new file mode 100644 index 0000000..4e17137 --- /dev/null +++ b/data/maps/demo/rooms/Castle.txtpb | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | name: "Castle" | ||
| 2 | panels { | ||
| 3 | name: "SERIES" | ||
| 4 | path: "Panels/Red/demo_26" | ||
| 5 | clue: "series" | ||
| 6 | answer: "mysteries" | ||
| 7 | } | ||
| 8 | panels { | ||
| 9 | name: "G" | ||
| 10 | path: "Panels/Blue/demo_37" | ||
| 11 | clue: "g" | ||
| 12 | answer: "g" | ||
| 13 | } | ||
| diff --git a/data/maps/demo/rooms/Center Building.txtpb b/data/maps/demo/rooms/Center Building.txtpb new file mode 100644 index 0000000..401fd59 --- /dev/null +++ b/data/maps/demo/rooms/Center Building.txtpb | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | name: "Center Building" | ||
| 2 | panels { | ||
| 3 | name: "WORLD" | ||
| 4 | path: "Panels/Room 1/demo_3" | ||
| 5 | clue: "world" | ||
| 6 | answer: "word" | ||
| 7 | } | ||
| 8 | panels { | ||
| 9 | name: "FUZZIES" | ||
| 10 | path: "Panels/Room 1/demo_4" | ||
| 11 | clue: "fuzzies" | ||
| 12 | answer: "puzzles" | ||
| 13 | } | ||
| diff --git a/data/maps/demo/rooms/Flower Hallway.txtpb b/data/maps/demo/rooms/Flower Hallway.txtpb new file mode 100644 index 0000000..059e4f6 --- /dev/null +++ b/data/maps/demo/rooms/Flower Hallway.txtpb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | name: "Flower Hallway" | ||
| 2 | panels { | ||
| 3 | name: "LACES" | ||
| 4 | path: "Panels/Gold/demo_47" | ||
| 5 | clue: "laces" | ||
| 6 | answer: "places" | ||
| 7 | } | ||
| diff --git a/data/maps/demo/rooms/Main Area.txtpb b/data/maps/demo/rooms/Main Area.txtpb new file mode 100644 index 0000000..f920a26 --- /dev/null +++ b/data/maps/demo/rooms/Main Area.txtpb | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | name: "Main Area" | ||
| 2 | panels { | ||
| 3 | name: "HI" | ||
| 4 | path: "Panels/Entry/demo_1" | ||
| 5 | clue: "hi" | ||
| 6 | answer: "hi" | ||
| 7 | } | ||
| 8 | panels { | ||
| 9 | name: "ART" | ||
| 10 | path: "Panels/Entry/demo_49" | ||
| 11 | clue: "\"art\"" | ||
| 12 | answer: "art" | ||
| 13 | } | ||
| 14 | panels { | ||
| 15 | name: "TEES" | ||
| 16 | path: "Panels/Entry/demo_50" | ||
| 17 | clue: "tees" | ||
| 18 | answer: "trees" | ||
| 19 | } | ||
| 20 | panels { | ||
| 21 | name: "COLORFUL" | ||
| 22 | path: "Panels/Room 1/demo_5" | ||
| 23 | clue: "colorful" | ||
| 24 | answer: "colorful" | ||
| 25 | } | ||
| 26 | panels { | ||
| 27 | name: "WORD" | ||
| 28 | path: "Panels/Room 1/demo_6" | ||
| 29 | clue: "word" | ||
| 30 | answer: "world" | ||
| 31 | } | ||
| 32 | panels { | ||
| 33 | name: "AGES" | ||
| 34 | path: "Panels/Orange/demo_7" | ||
| 35 | clue: "ages" | ||
| 36 | answer: "messages" | ||
| 37 | } | ||
| 38 | panels { | ||
| 39 | name: "DEN" | ||
| 40 | path: "Panels/Orange/demo_8" | ||
| 41 | clue: "den" | ||
| 42 | answer: "hidden" | ||
| 43 | } | ||
| 44 | panels { | ||
| 45 | name: "HID" | ||
| 46 | path: "Panels/Orange/demo_9" | ||
| 47 | clue: "hid" | ||
| 48 | answer: "hidden" | ||
| 49 | } | ||
| 50 | panels { | ||
| 51 | name: "MESS" | ||
| 52 | path: "Panels/Orange/demo_10" | ||
| 53 | clue: "mess" | ||
| 54 | answer: "messages" | ||
| 55 | } | ||
| 56 | panels { | ||
| 57 | name: "CLOCKWISE" | ||
| 58 | path: "Panels/Purple/demo_2" | ||
| 59 | clue: "clockwise" | ||
| 60 | answer: "counter" | ||
| 61 | } | ||
| 62 | panels { | ||
| 63 | name: "POSSIBLE" | ||
| 64 | path: "Panels/Purple/demo_12" | ||
| 65 | clue: "possible" | ||
| 66 | answer: "impossible" | ||
| 67 | } | ||
| 68 | panels { | ||
| 69 | name: "PACES" | ||
| 70 | path: "Panels/Purple/demo_13" | ||
| 71 | clue: "paces" | ||
| 72 | answer: "spaces" | ||
| 73 | } | ||
| 74 | panels { | ||
| 75 | name: "COUNTER" | ||
| 76 | path: "Panels/Purple/demo_30" | ||
| 77 | clue: "counter" | ||
| 78 | answer: "clockwise" | ||
| 79 | } | ||
| 80 | panels { | ||
| 81 | name: "ANY" | ||
| 82 | path: "Panels/Yellow/demo_14" | ||
| 83 | clue: "any" | ||
| 84 | answer: "many" | ||
| 85 | } | ||
| 86 | panels { | ||
| 87 | name: "RODS" | ||
| 88 | path: "Panels/Yellow/demo_15" | ||
| 89 | clue: "rods" | ||
| 90 | answer: "roads" | ||
| 91 | } | ||
| 92 | panels { | ||
| 93 | name: "TWO" | ||
| 94 | path: "Panels/Yellow/demo_16" | ||
| 95 | clue: "two" | ||
| 96 | answer: "to" | ||
| 97 | } | ||
| 98 | panels { | ||
| 99 | name: "TALK" | ||
| 100 | path: "Panels/Yellow/demo_17" | ||
| 101 | clue: "talk" | ||
| 102 | answer: "walk" | ||
| 103 | } | ||
| 104 | panels { | ||
| 105 | name: "SECRETIVE" | ||
| 106 | path: "Panels/Yellow/demo_18" | ||
| 107 | clue: "secretive" | ||
| 108 | answer: "secret" | ||
| 109 | } | ||
| 110 | panels { | ||
| 111 | name: "TOADS" | ||
| 112 | path: "Panels/Yellow/demo_19" | ||
| 113 | clue: "toads" | ||
| 114 | answer: "roads" | ||
| 115 | } | ||
| 116 | panels { | ||
| 117 | name: "TON" | ||
| 118 | path: "Panels/Yellow/demo_20" | ||
| 119 | clue: "ton" | ||
| 120 | answer: "to" | ||
| 121 | } | ||
| 122 | panels { | ||
| 123 | name: "MIND" | ||
| 124 | path: "Panels/Yellow/demo_21" | ||
| 125 | clue: "mind" | ||
| 126 | answer: "find" | ||
| 127 | } | ||
| 128 | panels { | ||
| 129 | name: "END" | ||
| 130 | path: "Panels/Yellow/demo_22" | ||
| 131 | clue: "end" | ||
| 132 | answer: "endless" | ||
| 133 | } | ||
| 134 | panels { | ||
| 135 | name: "RAD" | ||
| 136 | path: "Panels/Yellow/demo_23" | ||
| 137 | clue: "rad" | ||
| 138 | answer: "roads" | ||
| 139 | } | ||
| 140 | panels { | ||
| 141 | name: "TOO" | ||
| 142 | path: "Panels/Yellow/demo_24" | ||
| 143 | clue: "too" | ||
| 144 | answer: "to" | ||
| 145 | } | ||
| 146 | panels { | ||
| 147 | name: "STALK" | ||
| 148 | path: "Panels/Yellow/demo_25" | ||
| 149 | clue: "stalk" | ||
| 150 | answer: "walk" | ||
| 151 | } | ||
| 152 | panels { | ||
| 153 | name: "HAZES" | ||
| 154 | path: "Panels/Green/demo_27" | ||
| 155 | clue: "hazes" | ||
| 156 | answer: "mazes" | ||
| 157 | } | ||
| 158 | panels { | ||
| 159 | name: "DAZES" | ||
| 160 | path: "Panels/Green/demo_28" | ||
| 161 | clue: "dazes" | ||
| 162 | answer: "mazes" | ||
| 163 | } | ||
| 164 | panels { | ||
| 165 | name: "GAZES" | ||
| 166 | path: "Panels/Green/demo_29" | ||
| 167 | clue: "gazes" | ||
| 168 | answer: "mazes" | ||
| 169 | } | ||
| 170 | panels { | ||
| 171 | name: "CASTS" | ||
| 172 | path: "Panels/Green/demo_30" | ||
| 173 | clue: "casts" | ||
| 174 | answer: "castles" | ||
| 175 | } | ||
| 176 | panels { | ||
| 177 | name: "S" | ||
| 178 | path: "Panels/Blue/demo_31" | ||
| 179 | clue: "s" | ||
| 180 | answer: "s" | ||
| 181 | } | ||
| 182 | panels { | ||
| 183 | name: "C" | ||
| 184 | path: "Panels/Blue/demo_32" | ||
| 185 | clue: "c" | ||
| 186 | answer: "c" | ||
| 187 | } | ||
| 188 | panels { | ||
| 189 | name: "A" | ||
| 190 | path: "Panels/Blue/demo_33" | ||
| 191 | clue: "a" | ||
| 192 | answer: "a" | ||
| 193 | } | ||
| 194 | panels { | ||
| 195 | name: "V" | ||
| 196 | path: "Panels/Blue/demo_34" | ||
| 197 | clue: "v" | ||
| 198 | answer: "v" | ||
| 199 | } | ||
| 200 | panels { | ||
| 201 | name: "E (1)" | ||
| 202 | path: "Panels/Blue/demo_35" | ||
| 203 | clue: "e" | ||
| 204 | answer: "e" | ||
| 205 | } | ||
| 206 | panels { | ||
| 207 | name: "N" | ||
| 208 | path: "Panels/Blue/demo_36" | ||
| 209 | clue: "n" | ||
| 210 | answer: "n" | ||
| 211 | } | ||
| 212 | panels { | ||
| 213 | name: "E (2)" | ||
| 214 | path: "Panels/Blue/demo_38" | ||
| 215 | clue: "e" | ||
| 216 | answer: "e" | ||
| 217 | } | ||
| 218 | panels { | ||
| 219 | name: "R" | ||
| 220 | path: "Panels/Blue/demo_39" | ||
| 221 | clue: "r" | ||
| 222 | answer: "r" | ||
| 223 | } | ||
| 224 | panels { | ||
| 225 | name: "Blank" | ||
| 226 | path: "Panels/Blue/demo_40" | ||
| 227 | clue: "" | ||
| 228 | answer: "hunts" | ||
| 229 | } | ||
| 230 | panels { | ||
| 231 | name: "DISCOVER" | ||
| 232 | path: "Panels/Gold/demo_45" | ||
| 233 | clue: "discover" | ||
| 234 | answer: "rediscover" | ||
| 235 | } | ||
| 236 | panels { | ||
| 237 | name: "FAMILY" | ||
| 238 | path: "Panels/Gold/demo_46" | ||
| 239 | clue: "family" | ||
| 240 | answer: "familiar" | ||
| 241 | } | ||
| diff --git a/data/maps/demo/rooms/Mastery.txtpb b/data/maps/demo/rooms/Mastery.txtpb new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/data/maps/demo/rooms/Mastery.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mastery" | ||
| 2 | masteries { | ||
| 3 | name: "MASTERY" | ||
| 4 | path: "Components/Collectables/collectable" | ||
| 5 | } | ||
| diff --git a/data/maps/demo/rooms/Tower.txtpb b/data/maps/demo/rooms/Tower.txtpb new file mode 100644 index 0000000..2e73d79 --- /dev/null +++ b/data/maps/demo/rooms/Tower.txtpb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | name: "Tower" | ||
| 2 | panels { | ||
| 3 | name: "ENDS (1)" | ||
| 4 | path: "Panels/Endings/demo_48" | ||
| 5 | clue: "ends" | ||
| 6 | answer: "endings" | ||
| 7 | } | ||
| diff --git a/data/maps/four_rooms/rooms/Examples Room.txtpb b/data/maps/four_rooms/rooms/Examples Room.txtpb index dc82971..4146120 100644 --- a/data/maps/four_rooms/rooms/Examples Room.txtpb +++ b/data/maps/four_rooms/rooms/Examples Room.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "DAEDALUS" | 59 | name: "DAEDALUS" |
| 60 | display_name: "Examples Room Worldport" | ||
| 60 | path: "Components/Warps/worldport2" | 61 | path: "Components/Warps/worldport2" |
| 61 | orientation: "north" | 62 | destination { x: 0 y: 0 z: -40.5 } |
| 63 | rotation: 180 | ||
| 62 | } | 64 | } |
| diff --git a/data/maps/four_rooms/rooms/Intensify Room.txtpb b/data/maps/four_rooms/rooms/Intensify Room.txtpb index 2cbb214..8c6924a 100644 --- a/data/maps/four_rooms/rooms/Intensify Room.txtpb +++ b/data/maps/four_rooms/rooms/Intensify Room.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "IMPRESSIVE" | 59 | name: "IMPRESSIVE" |
| 60 | display_name: "Intensify Room Worldport" | ||
| 60 | path: "Components/Warps/worldport" | 61 | path: "Components/Warps/worldport" |
| 61 | orientation: "south" | 62 | destination { x: 0 y: 0 z: 6.5 } |
| 63 | rotation: 0 | ||
| 62 | } | 64 | } |
| diff --git a/data/maps/four_rooms/rooms/Synonyms Room.txtpb b/data/maps/four_rooms/rooms/Synonyms Room.txtpb index 4dd5b5d..bcbf64e 100644 --- a/data/maps/four_rooms/rooms/Synonyms Room.txtpb +++ b/data/maps/four_rooms/rooms/Synonyms Room.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "ENTRY" | 59 | name: "ENTRY" |
| 60 | display_name: "Synonyms Room Worldport" | ||
| 60 | path: "Components/Warps/worldport3" | 61 | path: "Components/Warps/worldport3" |
| 61 | orientation: "south" | 62 | destination { x: 20 y: 0 z: 6.5 } |
| 63 | rotation: 0 | ||
| 62 | } | 64 | } |
| diff --git a/data/maps/four_rooms/rooms/Time Room.txtpb b/data/maps/four_rooms/rooms/Time Room.txtpb index d684685..74f951d 100644 --- a/data/maps/four_rooms/rooms/Time Room.txtpb +++ b/data/maps/four_rooms/rooms/Time Room.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "OWL" | 59 | name: "OWL" |
| 60 | display_name: "Time Room Worldport" | ||
| 60 | path: "Components/Warps/worldport4" | 61 | path: "Components/Warps/worldport4" |
| 61 | orientation: "north" | 62 | destination { x: 20 y: 0 z: -40.5 } |
| 63 | rotation: 180 | ||
| 62 | } | 64 | } |
| diff --git a/data/maps/icarus/connections.txtpb b/data/maps/icarus/connections.txtpb new file mode 100644 index 0000000..a963424 --- /dev/null +++ b/data/maps/icarus/connections.txtpb | |||
| @@ -0,0 +1,766 @@ | |||
| 1 | # It is supposed to be in logic to jump from Maze to Maze King Painting, but I | ||
| 2 | # find this difficult to do, and unshuffled paintings never requires it, so I am | ||
| 3 | # making it not in logic. | ||
| 4 | connections { | ||
| 5 | from_room: "Welcome Spine (Obverse)" | ||
| 6 | to_room: "Trans Rights" | ||
| 7 | door { name: "Agender Door" } | ||
| 8 | } | ||
| 9 | connections { | ||
| 10 | from_room: "Through Woman (Obverse)" | ||
| 11 | to_room: "Welcome Spine (Reverse)" | ||
| 12 | door { name: "Agender Door" } | ||
| 13 | oneway: true | ||
| 14 | } | ||
| 15 | connections { | ||
| 16 | from_room: "Above Trans Rights" | ||
| 17 | to_room: "Through Woman (Reverse)" | ||
| 18 | door { name: "Agender Door" } | ||
| 19 | oneway: true | ||
| 20 | } | ||
| 21 | connections { | ||
| 22 | from { | ||
| 23 | painting { | ||
| 24 | room: "Big U" | ||
| 25 | name: "GO3" | ||
| 26 | } | ||
| 27 | } | ||
| 28 | to { | ||
| 29 | painting { | ||
| 30 | room: "Trans Rights" | ||
| 31 | name: "STOP2" | ||
| 32 | } | ||
| 33 | } | ||
| 34 | oneway: true | ||
| 35 | } | ||
| 36 | connections { | ||
| 37 | from_room: "Spiral Ramp" | ||
| 38 | to_room: "Highest Point" | ||
| 39 | door { name: "Pea Door" } | ||
| 40 | } | ||
| 41 | connections { | ||
| 42 | from { | ||
| 43 | painting { | ||
| 44 | room: "Fatherland Quicktravel" | ||
| 45 | name: "GO7" | ||
| 46 | } | ||
| 47 | } | ||
| 48 | to { | ||
| 49 | painting { | ||
| 50 | room: "Pillar Ramp" | ||
| 51 | name: "STOP6" | ||
| 52 | } | ||
| 53 | } | ||
| 54 | oneway: true | ||
| 55 | } | ||
| 56 | connections { | ||
| 57 | from_room: "Maze" | ||
| 58 | to_room: "Mediums Quicktravel" | ||
| 59 | door { name: "Mediums Door" } | ||
| 60 | } | ||
| 61 | connections { | ||
| 62 | from { | ||
| 63 | painting { | ||
| 64 | room: "Big U" | ||
| 65 | name: "GO1" | ||
| 66 | } | ||
| 67 | } | ||
| 68 | to { | ||
| 69 | painting { | ||
| 70 | room: "Pillar Ramp" | ||
| 71 | name: "STOP10" | ||
| 72 | } | ||
| 73 | } | ||
| 74 | oneway: true | ||
| 75 | } | ||
| 76 | connections { | ||
| 77 | from_room: "Pillar Ramp" | ||
| 78 | to_room: "Banana Belt Door" | ||
| 79 | door { name: "Asteroid Bunch Door" } | ||
| 80 | } | ||
| 81 | connections { | ||
| 82 | from_room: "Through Woman (Obverse)" | ||
| 83 | to_room: "Cow Quicktravel" | ||
| 84 | door { name: "Cow Door" } | ||
| 85 | } | ||
| 86 | connections { | ||
| 87 | from { | ||
| 88 | painting { | ||
| 89 | room: "Welcome Spine Quicktravel" | ||
| 90 | name: "GO5" | ||
| 91 | } | ||
| 92 | } | ||
| 93 | to { | ||
| 94 | painting { | ||
| 95 | room: "Cow Quicktravel" | ||
| 96 | name: "STOP4" | ||
| 97 | } | ||
| 98 | } | ||
| 99 | oneway: true | ||
| 100 | } | ||
| 101 | connections { | ||
| 102 | from_room: "Through Woman (Obverse)" | ||
| 103 | to_room: "Behind Welcome Spine" | ||
| 104 | door { name: "Ant Door" } | ||
| 105 | } | ||
| 106 | connections { | ||
| 107 | from_room: "Above Trans Rights" | ||
| 108 | to_room: "Behind Welcome Spine" | ||
| 109 | door { name: "Ant Door" } | ||
| 110 | oneway: true | ||
| 111 | } | ||
| 112 | connections { | ||
| 113 | from_room: "Through Woman (Obverse)" | ||
| 114 | to_room: "Big U" | ||
| 115 | door { name: "Man Door" } | ||
| 116 | } | ||
| 117 | connections { | ||
| 118 | from { | ||
| 119 | painting { | ||
| 120 | room: "Trans Rights" | ||
| 121 | name: "GO2" | ||
| 122 | } | ||
| 123 | } | ||
| 124 | to { | ||
| 125 | painting { | ||
| 126 | room: "Big U" | ||
| 127 | name: "STOP1" | ||
| 128 | } | ||
| 129 | } | ||
| 130 | oneway: true | ||
| 131 | } | ||
| 132 | connections { | ||
| 133 | from { | ||
| 134 | painting { | ||
| 135 | room: "Cow Quicktravel" | ||
| 136 | name: "GO4" | ||
| 137 | } | ||
| 138 | } | ||
| 139 | to { | ||
| 140 | painting { | ||
| 141 | room: "Big U" | ||
| 142 | name: "STOP3" | ||
| 143 | } | ||
| 144 | } | ||
| 145 | oneway: true | ||
| 146 | } | ||
| 147 | connections { | ||
| 148 | from_room: "Welcome Spine (Obverse)" | ||
| 149 | to_room: "Through Woman (Obverse)" | ||
| 150 | door { name: "Woman Door" } | ||
| 151 | } | ||
| 152 | connections { | ||
| 153 | from_room: "Fatherland" | ||
| 154 | to_room: "Fatherland Quicktravel" | ||
| 155 | door { name: "Fatherland Door" } | ||
| 156 | } | ||
| 157 | connections { | ||
| 158 | from { | ||
| 159 | painting { | ||
| 160 | room: "Mini Icarus Sun Loop" | ||
| 161 | name: "GO8" | ||
| 162 | } | ||
| 163 | } | ||
| 164 | to { | ||
| 165 | painting { | ||
| 166 | room: "Fatherland Quicktravel" | ||
| 167 | name: "STOP7" | ||
| 168 | } | ||
| 169 | } | ||
| 170 | oneway: true | ||
| 171 | } | ||
| 172 | connections { | ||
| 173 | from { | ||
| 174 | painting { | ||
| 175 | room: "Pillar Ramp" | ||
| 176 | name: "GO10" | ||
| 177 | } | ||
| 178 | } | ||
| 179 | to { | ||
| 180 | painting { | ||
| 181 | room: "Mediums Quicktravel" | ||
| 182 | name: "STOP9" | ||
| 183 | } | ||
| 184 | } | ||
| 185 | oneway: true | ||
| 186 | } | ||
| 187 | connections { | ||
| 188 | from_room: "Mini Icarus Wings Painting" | ||
| 189 | to_room: "Mini Icarus 2" | ||
| 190 | door { name: "Battery Door" } | ||
| 191 | oneway: true | ||
| 192 | } | ||
| 193 | connections { | ||
| 194 | from_room: "Mini Icarus 2" | ||
| 195 | to_room: "Mini Icarus Sun Loop" | ||
| 196 | door { name: "Battery Door" } | ||
| 197 | } | ||
| 198 | connections { | ||
| 199 | from { | ||
| 200 | painting { | ||
| 201 | room: "Mediums Quicktravel" | ||
| 202 | name: "GO9" | ||
| 203 | } | ||
| 204 | } | ||
| 205 | to { | ||
| 206 | painting { | ||
| 207 | room: "Mini Icarus Sun Loop" | ||
| 208 | name: "STOP8" | ||
| 209 | } | ||
| 210 | } | ||
| 211 | oneway: true | ||
| 212 | } | ||
| 213 | connections { | ||
| 214 | from_room: "Mini Icarus 2" | ||
| 215 | to_room: "Mini Icarus 3" | ||
| 216 | door { name: "Animals Door" } | ||
| 217 | } | ||
| 218 | connections { | ||
| 219 | from_room: "Mini Icarus 2" | ||
| 220 | to_room: "Mini Icarus Wings Painting" | ||
| 221 | door { name: "Troupe Door" } | ||
| 222 | } | ||
| 223 | connections { | ||
| 224 | from_room: "Mini Icarus 2" | ||
| 225 | to_room: "Mini Icarus Sun Loop" | ||
| 226 | door { name: "Reversed Arrows Door" } | ||
| 227 | } | ||
| 228 | connections { | ||
| 229 | from_room: "Welcome Spine (Reverse)" | ||
| 230 | to_room: "Welcome Spine Quicktravel" | ||
| 231 | door { name: "Termite Door" } | ||
| 232 | } | ||
| 233 | connections { | ||
| 234 | from { | ||
| 235 | painting { | ||
| 236 | room: "Pillar Ramp" | ||
| 237 | name: "GO6" | ||
| 238 | } | ||
| 239 | } | ||
| 240 | to { | ||
| 241 | painting { | ||
| 242 | room: "Welcome Spine Quicktravel" | ||
| 243 | name: "STOP5" | ||
| 244 | } | ||
| 245 | } | ||
| 246 | oneway: true | ||
| 247 | } | ||
| 248 | connections { | ||
| 249 | from { | ||
| 250 | painting { | ||
| 251 | room: "Spiral Ramp" | ||
| 252 | name: "SUN5" | ||
| 253 | } | ||
| 254 | } | ||
| 255 | to { | ||
| 256 | painting { | ||
| 257 | room: "Pillar Ramp" | ||
| 258 | name: "SUN6" | ||
| 259 | } | ||
| 260 | } | ||
| 261 | oneway: true | ||
| 262 | } | ||
| 263 | connections { | ||
| 264 | from_room: "Pillar Ramp" | ||
| 265 | to_room: "Patricide Room" | ||
| 266 | door { name: "Patricide Door" } | ||
| 267 | } | ||
| 268 | connections { | ||
| 269 | from_room: "Maze" | ||
| 270 | to_room: "Maze Back" | ||
| 271 | door { name: "These Door" } | ||
| 272 | } | ||
| 273 | connections { | ||
| 274 | from_room: "Welcome Spine (Reverse)" | ||
| 275 | to_room: "Maze Back" | ||
| 276 | oneway: true | ||
| 277 | } | ||
| 278 | connections { | ||
| 279 | from_room: "Welcome Spine (Obverse)" | ||
| 280 | to_room: "Through Woman (Reverse)" | ||
| 281 | oneway: true | ||
| 282 | } | ||
| 283 | connections { | ||
| 284 | from { | ||
| 285 | painting { | ||
| 286 | room: "Through Woman (Obverse)" | ||
| 287 | name: "SUN2" | ||
| 288 | } | ||
| 289 | } | ||
| 290 | to { | ||
| 291 | painting { | ||
| 292 | room: "Above Trans Rights" | ||
| 293 | name: "SUN" | ||
| 294 | } | ||
| 295 | } | ||
| 296 | oneway: true | ||
| 297 | } | ||
| 298 | connections { | ||
| 299 | from { | ||
| 300 | painting { | ||
| 301 | room: "Maze" | ||
| 302 | name: "WINGS14" | ||
| 303 | } | ||
| 304 | } | ||
| 305 | to { | ||
| 306 | painting { | ||
| 307 | room: "Through Woman (Obverse)" | ||
| 308 | name: "WINGS6" | ||
| 309 | } | ||
| 310 | } | ||
| 311 | oneway: true | ||
| 312 | } | ||
| 313 | connections { | ||
| 314 | from_room: "Through Woman (Obverse)" | ||
| 315 | to_room: "Behind Welcome Spine" | ||
| 316 | door { name: "Woman Door" } | ||
| 317 | oneway: true | ||
| 318 | } | ||
| 319 | connections { | ||
| 320 | from { | ||
| 321 | painting { | ||
| 322 | room: "Cow Quicktravel" | ||
| 323 | name: "SUN4" | ||
| 324 | } | ||
| 325 | } | ||
| 326 | to { | ||
| 327 | painting { | ||
| 328 | room: "Highest Point" | ||
| 329 | name: "SUN3" | ||
| 330 | } | ||
| 331 | } | ||
| 332 | oneway: true | ||
| 333 | } | ||
| 334 | connections { | ||
| 335 | from { | ||
| 336 | painting { | ||
| 337 | room: "Through Woman (Reverse)" | ||
| 338 | name: "WINGS8" | ||
| 339 | } | ||
| 340 | } | ||
| 341 | to { | ||
| 342 | painting { | ||
| 343 | room: "Spiral Ramp" | ||
| 344 | name: "WINGS7" | ||
| 345 | } | ||
| 346 | } | ||
| 347 | oneway: true | ||
| 348 | } | ||
| 349 | connections { | ||
| 350 | from_room: "Through Woman (Reverse)" | ||
| 351 | to_room: "Big U" | ||
| 352 | oneway: true | ||
| 353 | } | ||
| 354 | connections { | ||
| 355 | from { | ||
| 356 | painting { | ||
| 357 | room: "Spiral Ramp" | ||
| 358 | name: "WINGS6" | ||
| 359 | } | ||
| 360 | } | ||
| 361 | to { | ||
| 362 | painting { | ||
| 363 | room: "Through Woman (Obverse)" | ||
| 364 | name: "WINGS3" | ||
| 365 | } | ||
| 366 | } | ||
| 367 | # rare two-way painting! | ||
| 368 | bypass_target_door: true | ||
| 369 | } | ||
| 370 | connections { | ||
| 371 | from_room: "Spiral Ramp" | ||
| 372 | to_room: "Pillar Ramp" | ||
| 373 | oneway: true | ||
| 374 | } | ||
| 375 | connections { | ||
| 376 | from_room: "Pillar Ramp" | ||
| 377 | to_room: "The Orb" | ||
| 378 | } | ||
| 379 | connections { | ||
| 380 | from { | ||
| 381 | painting { | ||
| 382 | room: "Banana Belt Door" | ||
| 383 | name: "SUN14" | ||
| 384 | } | ||
| 385 | } | ||
| 386 | to { | ||
| 387 | painting { | ||
| 388 | room: "Above Trans Rights" | ||
| 389 | name: "SUN" | ||
| 390 | } | ||
| 391 | } | ||
| 392 | oneway: true | ||
| 393 | } | ||
| 394 | connections { | ||
| 395 | from_room: "Trans Rights" | ||
| 396 | to_room: "Mini Icarus Wings Painting" | ||
| 397 | oneway: true | ||
| 398 | } | ||
| 399 | connections { | ||
| 400 | from { | ||
| 401 | painting { | ||
| 402 | room: "Mini Icarus Wings Painting" | ||
| 403 | name: "WINGS4" | ||
| 404 | } | ||
| 405 | } | ||
| 406 | to { | ||
| 407 | painting { | ||
| 408 | room: "Mini Icarus Sun Loop" | ||
| 409 | name: "TROUBLEDESTINATION" | ||
| 410 | } | ||
| 411 | } | ||
| 412 | oneway: true | ||
| 413 | } | ||
| 414 | connections { | ||
| 415 | from { | ||
| 416 | painting { | ||
| 417 | room: "Painting Maze 1" | ||
| 418 | name: "SUN9" | ||
| 419 | } | ||
| 420 | } | ||
| 421 | to { | ||
| 422 | painting { | ||
| 423 | room: "Mini Icarus 2" | ||
| 424 | name: "SUN10" | ||
| 425 | } | ||
| 426 | } | ||
| 427 | oneway: true | ||
| 428 | } | ||
| 429 | connections { | ||
| 430 | from { | ||
| 431 | painting { | ||
| 432 | room: "Mini Icarus 2" | ||
| 433 | name: "SUN11" | ||
| 434 | } | ||
| 435 | } | ||
| 436 | to { | ||
| 437 | painting { | ||
| 438 | room: "Mini Icarus Sun Loop" | ||
| 439 | name: "SUN12" | ||
| 440 | } | ||
| 441 | } | ||
| 442 | oneway: true | ||
| 443 | } | ||
| 444 | connections { | ||
| 445 | from { | ||
| 446 | painting { | ||
| 447 | room: "Mini Icarus Sun Loop" | ||
| 448 | name: "SUN13" | ||
| 449 | } | ||
| 450 | } | ||
| 451 | to { | ||
| 452 | painting { | ||
| 453 | room: "Maze" | ||
| 454 | name: "SUN14" | ||
| 455 | } | ||
| 456 | } | ||
| 457 | oneway: true | ||
| 458 | } | ||
| 459 | connections { | ||
| 460 | from_room: "Mini Icarus 3" | ||
| 461 | to_room: "Mini Icarus Sun Loop" | ||
| 462 | door { name: "Battery Door" } | ||
| 463 | oneway: true | ||
| 464 | } | ||
| 465 | connections { | ||
| 466 | from { | ||
| 467 | painting { | ||
| 468 | room: "Maze" | ||
| 469 | name: "SUN5" | ||
| 470 | } | ||
| 471 | } | ||
| 472 | to { | ||
| 473 | painting { | ||
| 474 | room: "Painting Maze 2" | ||
| 475 | name: "SUN6" | ||
| 476 | } | ||
| 477 | } | ||
| 478 | oneway: true | ||
| 479 | } | ||
| 480 | connections { | ||
| 481 | from { | ||
| 482 | painting { | ||
| 483 | room: "Maze" | ||
| 484 | name: "WINGS16" | ||
| 485 | } | ||
| 486 | } | ||
| 487 | to { | ||
| 488 | painting { | ||
| 489 | room: "Maze Wings Passage" | ||
| 490 | name: "WINGS12" | ||
| 491 | } | ||
| 492 | } | ||
| 493 | oneway: true | ||
| 494 | } | ||
| 495 | connections { | ||
| 496 | from { | ||
| 497 | painting { | ||
| 498 | room: "Maze Wings Passage" | ||
| 499 | name: "WINGS12" | ||
| 500 | } | ||
| 501 | } | ||
| 502 | to { | ||
| 503 | painting { | ||
| 504 | room: "Patricide Room" | ||
| 505 | name: "WINGS10" | ||
| 506 | } | ||
| 507 | } | ||
| 508 | oneway: true | ||
| 509 | } | ||
| 510 | connections { | ||
| 511 | from { | ||
| 512 | painting { | ||
| 513 | room: "Patricide Room" | ||
| 514 | name: "SUN4" | ||
| 515 | } | ||
| 516 | } | ||
| 517 | to { | ||
| 518 | painting { | ||
| 519 | room: "Maze" | ||
| 520 | name: "SUN4" | ||
| 521 | } | ||
| 522 | } | ||
| 523 | oneway: true | ||
| 524 | } | ||
| 525 | connections { | ||
| 526 | from { | ||
| 527 | painting { | ||
| 528 | room: "Maze" | ||
| 529 | name: "WINGS9" | ||
| 530 | } | ||
| 531 | } | ||
| 532 | to { | ||
| 533 | painting { | ||
| 534 | room: "Patricide Room" | ||
| 535 | name: "WINGS10" | ||
| 536 | } | ||
| 537 | } | ||
| 538 | oneway: true | ||
| 539 | } | ||
| 540 | connections { | ||
| 541 | from_room: "Maze" | ||
| 542 | to_room: "Maze King Panel" | ||
| 543 | oneway: true | ||
| 544 | } | ||
| 545 | connections { | ||
| 546 | from_room: "Maze King Painting" | ||
| 547 | to_room: "Maze King Panel" | ||
| 548 | oneway: true | ||
| 549 | } | ||
| 550 | connections { | ||
| 551 | from { | ||
| 552 | painting { | ||
| 553 | room: "Maze King Painting" | ||
| 554 | name: "WINGS13" | ||
| 555 | } | ||
| 556 | } | ||
| 557 | to { | ||
| 558 | painting { | ||
| 559 | room: "Maze Wings Passage" | ||
| 560 | name: "WINGS12" | ||
| 561 | } | ||
| 562 | } | ||
| 563 | oneway: true | ||
| 564 | } | ||
| 565 | connections { | ||
| 566 | from { | ||
| 567 | painting { | ||
| 568 | room: "Fatherland" | ||
| 569 | name: "SUN7" | ||
| 570 | } | ||
| 571 | } | ||
| 572 | to { | ||
| 573 | painting { | ||
| 574 | room: "Painting Maze 1" | ||
| 575 | name: "SUN8" | ||
| 576 | } | ||
| 577 | } | ||
| 578 | oneway: true | ||
| 579 | } | ||
| 580 | connections { | ||
| 581 | from { | ||
| 582 | painting { | ||
| 583 | room: "Painting Maze 2" | ||
| 584 | name: "WINGS11" | ||
| 585 | } | ||
| 586 | } | ||
| 587 | to { | ||
| 588 | painting { | ||
| 589 | room: "Fatherland" | ||
| 590 | name: "WINGS2" | ||
| 591 | } | ||
| 592 | } | ||
| 593 | oneway: true | ||
| 594 | } | ||
| 595 | connections { | ||
| 596 | from { | ||
| 597 | painting { | ||
| 598 | room: "Painting Maze 1" | ||
| 599 | name: "WINGS3" | ||
| 600 | } | ||
| 601 | } | ||
| 602 | to { | ||
| 603 | painting { | ||
| 604 | room: "Maze Wings Passage" | ||
| 605 | name: "WINGS12" | ||
| 606 | } | ||
| 607 | } | ||
| 608 | oneway: true | ||
| 609 | } | ||
| 610 | connections { | ||
| 611 | from_room: "Trans Rights" | ||
| 612 | to_room: "Trans Rights Panels" | ||
| 613 | oneway: true | ||
| 614 | } | ||
| 615 | connections { | ||
| 616 | from_room: "Above Trans Rights" | ||
| 617 | to_room: "Trans Rights Panels" | ||
| 618 | oneway: true | ||
| 619 | } | ||
| 620 | connections { | ||
| 621 | from_room: "Welcome Spine (Obverse)" | ||
| 622 | to_room: "Mastery" | ||
| 623 | door { name: "Mastery" } | ||
| 624 | } | ||
| 625 | # It is possible to fall out of the map from every room, which always sends you | ||
| 626 | # back to the beginning. | ||
| 627 | connections { | ||
| 628 | from_room: "Above Trans Rights" | ||
| 629 | to_room: "Welcome Spine (Obverse)" | ||
| 630 | oneway: true | ||
| 631 | } | ||
| 632 | connections { | ||
| 633 | from_room: "Banana Belt Door" | ||
| 634 | to_room: "Welcome Spine (Obverse)" | ||
| 635 | oneway: true | ||
| 636 | } | ||
| 637 | connections { | ||
| 638 | from_room: "Behind Welcome Spine" | ||
| 639 | to_room: "Welcome Spine (Obverse)" | ||
| 640 | oneway: true | ||
| 641 | } | ||
| 642 | connections { | ||
| 643 | from_room: "Big U" | ||
| 644 | to_room: "Welcome Spine (Obverse)" | ||
| 645 | oneway: true | ||
| 646 | } | ||
| 647 | connections { | ||
| 648 | from_room: "Cow Quicktravel" | ||
| 649 | to_room: "Welcome Spine (Obverse)" | ||
| 650 | oneway: true | ||
| 651 | } | ||
| 652 | connections { | ||
| 653 | from_room: "Fatherland Quicktravel" | ||
| 654 | to_room: "Welcome Spine (Obverse)" | ||
| 655 | oneway: true | ||
| 656 | } | ||
| 657 | connections { | ||
| 658 | from_room: "Fatherland" | ||
| 659 | to_room: "Welcome Spine (Obverse)" | ||
| 660 | oneway: true | ||
| 661 | } | ||
| 662 | connections { | ||
| 663 | from_room: "Highest Point" | ||
| 664 | to_room: "Welcome Spine (Obverse)" | ||
| 665 | oneway: true | ||
| 666 | } | ||
| 667 | connections { | ||
| 668 | from_room: "Maze Back" | ||
| 669 | to_room: "Welcome Spine (Obverse)" | ||
| 670 | oneway: true | ||
| 671 | } | ||
| 672 | connections { | ||
| 673 | from_room: "Maze King Painting" | ||
| 674 | to_room: "Welcome Spine (Obverse)" | ||
| 675 | oneway: true | ||
| 676 | } | ||
| 677 | connections { | ||
| 678 | from_room: "Maze Wings Passage" | ||
| 679 | to_room: "Welcome Spine (Obverse)" | ||
| 680 | oneway: true | ||
| 681 | } | ||
| 682 | connections { | ||
| 683 | from_room: "Maze" | ||
| 684 | to_room: "Welcome Spine (Obverse)" | ||
| 685 | oneway: true | ||
| 686 | } | ||
| 687 | connections { | ||
| 688 | from_room: "Mediums Quicktravel" | ||
| 689 | to_room: "Welcome Spine (Obverse)" | ||
| 690 | oneway: true | ||
| 691 | } | ||
| 692 | connections { | ||
| 693 | from_room: "Mini Icarus 2" | ||
| 694 | to_room: "Welcome Spine (Obverse)" | ||
| 695 | oneway: true | ||
| 696 | } | ||
| 697 | connections { | ||
| 698 | from_room: "Mini Icarus 3" | ||
| 699 | to_room: "Welcome Spine (Obverse)" | ||
| 700 | oneway: true | ||
| 701 | } | ||
| 702 | connections { | ||
| 703 | from_room: "Mini Icarus Sun Loop" | ||
| 704 | to_room: "Welcome Spine (Obverse)" | ||
| 705 | oneway: true | ||
| 706 | } | ||
| 707 | connections { | ||
| 708 | from_room: "Mini Icarus Wings Painting" | ||
| 709 | to_room: "Welcome Spine (Obverse)" | ||
| 710 | oneway: true | ||
| 711 | } | ||
| 712 | connections { | ||
| 713 | from_room: "Painting Maze 1" | ||
| 714 | to_room: "Welcome Spine (Obverse)" | ||
| 715 | oneway: true | ||
| 716 | } | ||
| 717 | connections { | ||
| 718 | from_room: "Painting Maze 2" | ||
| 719 | to_room: "Welcome Spine (Obverse)" | ||
| 720 | oneway: true | ||
| 721 | } | ||
| 722 | connections { | ||
| 723 | from_room: "Patricide Room" | ||
| 724 | to_room: "Welcome Spine (Obverse)" | ||
| 725 | oneway: true | ||
| 726 | } | ||
| 727 | connections { | ||
| 728 | from_room: "Pillar Ramp" | ||
| 729 | to_room: "Welcome Spine (Obverse)" | ||
| 730 | oneway: true | ||
| 731 | } | ||
| 732 | connections { | ||
| 733 | from_room: "Spiral Ramp" | ||
| 734 | to_room: "Welcome Spine (Obverse)" | ||
| 735 | oneway: true | ||
| 736 | } | ||
| 737 | connections { | ||
| 738 | from_room: "The Orb" | ||
| 739 | to_room: "Welcome Spine (Obverse)" | ||
| 740 | oneway: true | ||
| 741 | } | ||
| 742 | connections { | ||
| 743 | from_room: "Through Woman (Obverse)" | ||
| 744 | to_room: "Welcome Spine (Obverse)" | ||
| 745 | oneway: true | ||
| 746 | } | ||
| 747 | connections { | ||
| 748 | from_room: "Through Woman (Reverse)" | ||
| 749 | to_room: "Welcome Spine (Obverse)" | ||
| 750 | oneway: true | ||
| 751 | } | ||
| 752 | connections { | ||
| 753 | from_room: "Trans Rights" | ||
| 754 | to_room: "Welcome Spine (Obverse)" | ||
| 755 | oneway: true | ||
| 756 | } | ||
| 757 | connections { | ||
| 758 | from_room: "Welcome Spine (Reverse)" | ||
| 759 | to_room: "Welcome Spine (Obverse)" | ||
| 760 | oneway: true | ||
| 761 | } | ||
| 762 | connections { | ||
| 763 | from_room: "Welcome Spine Quicktravel" | ||
| 764 | to_room: "Welcome Spine (Obverse)" | ||
| 765 | oneway: true | ||
| 766 | } | ||
| diff --git a/data/maps/icarus/doors.txtpb b/data/maps/icarus/doors.txtpb new file mode 100644 index 0000000..a333dea --- /dev/null +++ b/data/maps/icarus/doors.txtpb | |||
| @@ -0,0 +1,286 @@ | |||
| 1 | doors { | ||
| 2 | name: "Agender Door" | ||
| 3 | type: STANDARD | ||
| 4 | receivers: "Components/Doors/quicktravel3" | ||
| 5 | panels { room: "Trans Rights Panels" name: "AGENDER" } | ||
| 6 | location_room: "Trans Rights Panels" | ||
| 7 | } | ||
| 8 | doors { | ||
| 9 | name: "Quick Travel 3" | ||
| 10 | type: ITEM_ONLY | ||
| 11 | receivers: "Components/Paintings/QuickTravel/go3" | ||
| 12 | panels { room: "Trans Rights Panels" name: "AGENDER" } | ||
| 13 | } | ||
| 14 | doors { | ||
| 15 | name: "Pea Door" | ||
| 16 | type: STANDARD | ||
| 17 | receivers: "Components/Doors/quicktravel5" | ||
| 18 | panels { room: "Pillar Ramp" name: "PEA (1)" } | ||
| 19 | location_room: "Pillar Ramp" | ||
| 20 | } | ||
| 21 | doors { | ||
| 22 | name: "Quick Travel 7" | ||
| 23 | type: ITEM_ONLY | ||
| 24 | receivers: "Components/Paintings/QuickTravel/go7" | ||
| 25 | panels { room: "Pillar Ramp" name: "PEA (1)" } | ||
| 26 | } | ||
| 27 | doors { | ||
| 28 | name: "Mediums Door" | ||
| 29 | type: STANDARD | ||
| 30 | receivers: "Components/Doors/quicktravel12" | ||
| 31 | panels { room: "Maze" name: "MEDIUMS" } | ||
| 32 | location_room: "Maze" | ||
| 33 | } | ||
| 34 | doors { | ||
| 35 | name: "Quick Travel 1" | ||
| 36 | type: ITEM_ONLY | ||
| 37 | receivers: "Components/Paintings/QuickTravel/go1" | ||
| 38 | panels { room: "Maze" name: "MEDIUMS" } | ||
| 39 | } | ||
| 40 | doors { | ||
| 41 | name: "Asteroid Bunch Door" | ||
| 42 | type: STANDARD | ||
| 43 | receivers: "Components/Doors/quicktravel13" | ||
| 44 | panels { room: "Pillar Ramp" name: "ASTEROID" } | ||
| 45 | panels { room: "Pillar Ramp" name: "BUNCH" } | ||
| 46 | location_room: "Pillar Ramp" | ||
| 47 | } | ||
| 48 | doors { | ||
| 49 | name: "Cow Door" | ||
| 50 | type: STANDARD | ||
| 51 | receivers: "Components/Doors/quicktravel2" | ||
| 52 | panels { room: "Through Woman (Obverse)" name: "COW" } | ||
| 53 | location_room: "Through Woman (Obverse)" | ||
| 54 | } | ||
| 55 | doors { | ||
| 56 | name: "Quick Travel 5" | ||
| 57 | type: STANDARD | ||
| 58 | receivers: "Components/Paintings/QuickTravel/go5" | ||
| 59 | panels { room: "Highest Point" name: "QUEEN" } | ||
| 60 | location_room: "Highest Point" | ||
| 61 | } | ||
| 62 | doors { | ||
| 63 | name: "Ant Door" | ||
| 64 | type: STANDARD | ||
| 65 | receivers: "Components/Doors/by sun" | ||
| 66 | panels { room: "Above Trans Rights" name: "ANT" } | ||
| 67 | location_room: "Above Trans Rights" | ||
| 68 | } | ||
| 69 | doors { | ||
| 70 | name: "Man Door" | ||
| 71 | type: STANDARD | ||
| 72 | receivers: "Components/Doors/quicktravel" | ||
| 73 | panels { room: "Through Woman (Obverse)" name: "HUMAN (2)" } | ||
| 74 | location_room: "Through Woman (Obverse)" | ||
| 75 | } | ||
| 76 | doors { | ||
| 77 | name: "Quick Travel 2" | ||
| 78 | type: ITEM_ONLY | ||
| 79 | receivers: "Components/Paintings/QuickTravel/go2" | ||
| 80 | panels { room: "Through Woman (Obverse)" name: "HUMAN (2)" } | ||
| 81 | } | ||
| 82 | doors { | ||
| 83 | name: "Quick Travel 4" | ||
| 84 | type: STANDARD | ||
| 85 | receivers: "Components/Paintings/QuickTravel/go4" | ||
| 86 | panels { room: "Big U" name: "COLONY" } | ||
| 87 | location_room: "Big U" | ||
| 88 | } | ||
| 89 | doors { | ||
| 90 | name: "Woman Door" | ||
| 91 | type: STANDARD | ||
| 92 | receivers: "Components/Doors/entry" | ||
| 93 | panels { room: "Welcome Spine (Obverse)" name: "HUMAN (1)" } | ||
| 94 | location_room: "Welcome Spine (Obverse)" | ||
| 95 | } | ||
| 96 | doors { | ||
| 97 | name: "Fatherland Door" | ||
| 98 | type: STANDARD | ||
| 99 | receivers: "Components/Doors/quicktravel6" | ||
| 100 | panels { room: "Fatherland" name: "FATHERLAND" } | ||
| 101 | location_room: "Fatherland" | ||
| 102 | } | ||
| 103 | doors { | ||
| 104 | name: "Quick Travel 8" | ||
| 105 | type: ITEM_ONLY | ||
| 106 | receivers: "Components/Paintings/QuickTravel/go8" | ||
| 107 | panels { room: "Fatherland" name: "FATHERLAND" } | ||
| 108 | } | ||
| 109 | doors { | ||
| 110 | name: "Quick Travel 10" | ||
| 111 | type: ITEM_ONLY | ||
| 112 | receivers: "Components/Paintings/QuickTravel/go10" | ||
| 113 | panels { room: "Fatherland" name: "FATHERLAND" } | ||
| 114 | } | ||
| 115 | doors { | ||
| 116 | name: "Battery Door" | ||
| 117 | type: STANDARD | ||
| 118 | receivers: "Components/Doors/quicktravel7" | ||
| 119 | panels { room: "Mini Icarus 2" name: "BATTERY" } | ||
| 120 | location_room: "Mini Icarus 2" | ||
| 121 | } | ||
| 122 | doors { | ||
| 123 | name: "Quick Travel 9" | ||
| 124 | type: ITEM_ONLY | ||
| 125 | receivers: "Components/Paintings/QuickTravel/go9" | ||
| 126 | panels { room: "Mini Icarus 2" name: "BATTERY" } | ||
| 127 | } | ||
| 128 | doors { | ||
| 129 | name: "Arrows Door" | ||
| 130 | type: LOCATION_ONLY | ||
| 131 | #receivers: "Components/Doors/quicktravel8" | ||
| 132 | panels { room: "Mini Icarus 2" name: "ARROWS" } | ||
| 133 | location_room: "Mini Icarus 2" | ||
| 134 | location_name: "ARROWS" | ||
| 135 | } | ||
| 136 | doors { | ||
| 137 | name: "Animals Door" | ||
| 138 | type: STANDARD | ||
| 139 | receivers: "Components/Doors/quicktravel8" | ||
| 140 | panels { room: "Mini Icarus 2" name: "ANIMALS" } | ||
| 141 | location_room: "Mini Icarus 2" | ||
| 142 | } | ||
| 143 | doors { | ||
| 144 | name: "Troupe Door" | ||
| 145 | type: STANDARD | ||
| 146 | receivers: "Components/Doors/quicktravel10" | ||
| 147 | panels { room: "Mini Icarus 2" name: "TROUPE" } | ||
| 148 | location_room: "Mini Icarus 2" | ||
| 149 | } | ||
| 150 | doors { | ||
| 151 | name: "Reversed Arrows Door" | ||
| 152 | type: STANDARD | ||
| 153 | receivers: "Components/Doors/quicktravel11" | ||
| 154 | panels { room: "Mini Icarus 2" name: "SQUAD" } | ||
| 155 | location_room: "Mini Icarus 2" | ||
| 156 | } | ||
| 157 | doors { | ||
| 158 | name: "Termite Door" | ||
| 159 | type: STANDARD | ||
| 160 | receivers: "Components/Doors/quicktravel4" | ||
| 161 | panels { room: "Welcome Spine (Reverse)" name: "TERMITE" } | ||
| 162 | location_room: "Welcome Spine (Reverse)" | ||
| 163 | } | ||
| 164 | doors { | ||
| 165 | name: "Quick Travel 6" | ||
| 166 | type: ITEM_ONLY | ||
| 167 | receivers: "Components/Paintings/QuickTravel/go6" | ||
| 168 | panels { room: "Welcome Spine (Reverse)" name: "TERMITE" } | ||
| 169 | } | ||
| 170 | doors { | ||
| 171 | name: "Murder Panels" | ||
| 172 | type: LOCATION_ONLY | ||
| 173 | panels { room: "Maze" name: "MANSLAUGHTER" } | ||
| 174 | panels { room: "Pillar Ramp" name: "PATRICIDE" } | ||
| 175 | location_room: "Maze" | ||
| 176 | } | ||
| 177 | doors { | ||
| 178 | name: "Sun Painting To Drone" | ||
| 179 | type: STANDARD | ||
| 180 | receivers: "Components/Paintings/sun5" | ||
| 181 | panels { room: "Pillar Ramp" name: "DRONE" } | ||
| 182 | location_room: "Pillar Ramp" | ||
| 183 | } | ||
| 184 | doors { | ||
| 185 | name: "Patricide Door" | ||
| 186 | type: STANDARD | ||
| 187 | receivers: "Components/Doors/quicktravel15" | ||
| 188 | panels { room: "Pillar Ramp" name: "PATRICIDE" } | ||
| 189 | location_room: "Pillar Ramp" | ||
| 190 | } | ||
| 191 | doors { | ||
| 192 | name: "These Door" | ||
| 193 | type: STANDARD | ||
| 194 | receivers: "Components/Doors/quicktravel14" | ||
| 195 | panels { room: "Maze Back" name: "THESE" } | ||
| 196 | location_room: "Maze Back" | ||
| 197 | } | ||
| 198 | doors { | ||
| 199 | name: "Orb Panels" | ||
| 200 | type: LOCATION_ONLY | ||
| 201 | panels { room: "The Orb" name: "ADDERS" } | ||
| 202 | panels { room: "The Orb" name: "PUPPY" } | ||
| 203 | panels { room: "The Orb" name: "NEEDLE" } | ||
| 204 | panels { room: "The Orb" name: "US" } | ||
| 205 | panels { room: "The Orb" name: "FISH" } | ||
| 206 | panels { room: "The Orb" name: "DEADLINE" } | ||
| 207 | panels { room: "The Orb" name: "PEA (2)" } | ||
| 208 | panels { room: "The Orb" name: "THESIS" } | ||
| 209 | panels { room: "The Orb" name: "CLUTCH (1)" } | ||
| 210 | panels { room: "The Orb" name: "BASIS (2)" } | ||
| 211 | panels { room: "The Orb" name: "AXIS" } | ||
| 212 | panels { room: "The Orb" name: "STRAIGHT" } | ||
| 213 | panels { room: "The Orb" name: "HISS" } | ||
| 214 | panels { room: "The Orb" name: "DISCUS" } | ||
| 215 | panels { room: "The Orb" name: "SON" } | ||
| 216 | panels { room: "The Orb" name: "CLUTCH (2)" } | ||
| 217 | location_room: "The Orb" | ||
| 218 | } | ||
| 219 | doors { | ||
| 220 | name: "Near Fireman Wings Painting" | ||
| 221 | type: STANDARD | ||
| 222 | receivers: "Components/Paintings/oneways/wings6" | ||
| 223 | receivers: "Components/Paintings/oneways/wings3" | ||
| 224 | panels { room: "Spiral Ramp" name: "FIREMAN" } | ||
| 225 | location_room: "Spiral Ramp" | ||
| 226 | } | ||
| 227 | doors { | ||
| 228 | name: "Mastery" | ||
| 229 | type: EVENT | ||
| 230 | panels { room: "Above Trans Rights" name: "ANT" } | ||
| 231 | panels { room: "Big U" name: "DECK" } | ||
| 232 | panels { room: "Big U" name: "COLONY" } | ||
| 233 | panels { room: "Big U" name: "MANOR" } | ||
| 234 | panels { room: "Fatherland" name: "FATHERLAND" } | ||
| 235 | panels { room: "Highest Point" name: "DIAGNOSIS" } | ||
| 236 | panels { room: "Highest Point" name: "QUEEN" } | ||
| 237 | panels { room: "Maze Back" name: "THESE" } | ||
| 238 | panels { room: "Maze King Panel" name: "KING (2)" } | ||
| 239 | panels { room: "Maze" name: "KING (1)" } | ||
| 240 | panels { room: "Maze" name: "ANALYSIS" } | ||
| 241 | panels { room: "Maze" name: "MANSLAUGHTER" } | ||
| 242 | panels { room: "Maze" name: "MEDIUMS" } | ||
| 243 | panels { room: "Maze" name: "BOOKS" } | ||
| 244 | panels { room: "Mini Icarus 2" name: "BATTERY" } | ||
| 245 | panels { room: "Mini Icarus 2" name: "TROUPE" } | ||
| 246 | panels { room: "Mini Icarus 2" name: "ANIMALS" } | ||
| 247 | panels { room: "Mini Icarus 2" name: "ARROWS" } | ||
| 248 | panels { room: "Mini Icarus 2" name: "SQUAD" } | ||
| 249 | panels { room: "Pillar Ramp" name: "ASTEROID" } | ||
| 250 | panels { room: "Pillar Ramp" name: "BUNCH" } | ||
| 251 | panels { room: "Pillar Ramp" name: "PRINCES" } | ||
| 252 | panels { room: "Pillar Ramp" name: "PATRICIDE" } | ||
| 253 | panels { room: "Pillar Ramp" name: "DRONE" } | ||
| 254 | panels { room: "Pillar Ramp" name: "PEA (1)" } | ||
| 255 | panels { room: "Spiral Ramp" name: "FIREMAN" } | ||
| 256 | panels { room: "The Orb" name: "ADDERS" } | ||
| 257 | panels { room: "The Orb" name: "PUPPY" } | ||
| 258 | panels { room: "The Orb" name: "NEEDLE" } | ||
| 259 | panels { room: "The Orb" name: "US" } | ||
| 260 | panels { room: "The Orb" name: "FISH" } | ||
| 261 | panels { room: "The Orb" name: "DEADLINE" } | ||
| 262 | panels { room: "The Orb" name: "PEA (2)" } | ||
| 263 | panels { room: "The Orb" name: "THESIS" } | ||
| 264 | panels { room: "The Orb" name: "CLUTCH (1)" } | ||
| 265 | panels { room: "The Orb" name: "BASIS (2)" } | ||
| 266 | panels { room: "The Orb" name: "AXIS" } | ||
| 267 | panels { room: "The Orb" name: "STRAIGHT" } | ||
| 268 | panels { room: "The Orb" name: "HISS" } | ||
| 269 | panels { room: "The Orb" name: "DISCUS" } | ||
| 270 | panels { room: "The Orb" name: "SON" } | ||
| 271 | panels { room: "The Orb" name: "CLUTCH (2)" } | ||
| 272 | panels { room: "Through Woman (Obverse)" name: "HUMAN (2)" } | ||
| 273 | panels { room: "Through Woman (Obverse)" name: "COW" } | ||
| 274 | panels { room: "Through Woman (Reverse)" name: "PRINCE" } | ||
| 275 | panels { room: "Through Woman (Reverse)" name: "BASIS (1)" } | ||
| 276 | panels { room: "Trans Rights" name: "SERVANT (1)" } | ||
| 277 | panels { room: "Trans Rights" name: "SERVANT (2)" } | ||
| 278 | panels { room: "Trans Rights Panels" name: "AGENDER" } | ||
| 279 | panels { room: "Trans Rights Panels" name: "HUMAN (3)" } | ||
| 280 | panels { room: "Trans Rights Panels" name: "HUMAN (4)" } | ||
| 281 | panels { room: "Welcome Spine (Obverse)" name: "FISHWIFE" } | ||
| 282 | panels { room: "Welcome Spine (Obverse)" name: "HUMAN (1)" } | ||
| 283 | panels { room: "Welcome Spine (Reverse)" name: "FATHER" } | ||
| 284 | panels { room: "Welcome Spine (Reverse)" name: "TERMITE" } | ||
| 285 | panels { room: "Welcome Spine (Reverse)" name: "SISTER" } | ||
| 286 | } | ||
| diff --git a/data/maps/icarus/metadata.txtpb b/data/maps/icarus/metadata.txtpb new file mode 100644 index 0000000..8512d8e --- /dev/null +++ b/data/maps/icarus/metadata.txtpb | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | display_name: "Icarus" | ||
| 2 | type: ICARUS | ||
| 3 | # The map's mastery is created at runtime. | ||
| 4 | custom_nodes: "Components/Collectables/collectable" | ||
| diff --git a/data/maps/icarus/rooms/Above Trans Rights.txtpb b/data/maps/icarus/rooms/Above Trans Rights.txtpb new file mode 100644 index 0000000..66d8220 --- /dev/null +++ b/data/maps/icarus/rooms/Above Trans Rights.txtpb | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | name: "Above Trans Rights" | ||
| 2 | panels { | ||
| 3 | name: "ANT" | ||
| 4 | path: "Panels/Room_1/ant" | ||
| 5 | clue: "ant" | ||
| 6 | answer: "colony" | ||
| 7 | symbols: PLANET | ||
| 8 | } | ||
| 9 | paintings { | ||
| 10 | name: "SUN" | ||
| 11 | path: "Components/Paintings/sun" | ||
| 12 | } | ||
| diff --git a/data/maps/icarus/rooms/Banana Belt Door.txtpb b/data/maps/icarus/rooms/Banana Belt Door.txtpb new file mode 100644 index 0000000..62127d7 --- /dev/null +++ b/data/maps/icarus/rooms/Banana Belt Door.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Banana Belt Door" | ||
| 2 | paintings { | ||
| 3 | name: "SUN14" | ||
| 4 | path: "Components/Paintings/sun14" | ||
| 5 | } | ||
| diff --git a/data/maps/icarus/rooms/Behind Welcome Spine.txtpb b/data/maps/icarus/rooms/Behind Welcome Spine.txtpb new file mode 100644 index 0000000..41b56b3 --- /dev/null +++ b/data/maps/icarus/rooms/Behind Welcome Spine.txtpb | |||
| @@ -0,0 +1 @@ | |||
| name: "Behind Welcome Spine" | |||
| diff --git a/data/maps/icarus/rooms/Big U.txtpb b/data/maps/icarus/rooms/Big U.txtpb new file mode 100644 index 0000000..ea61640 --- /dev/null +++ b/data/maps/icarus/rooms/Big U.txtpb | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | name: "Big U" | ||
| 2 | panels { | ||
| 3 | name: "DECK" | ||
| 4 | path: "Panels/Room_1/deck" | ||
| 5 | clue: "deck" | ||
| 6 | answer: "card" | ||
| 7 | symbols: PLANET | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "COLONY" | ||
| 11 | path: "Panels/Room_1/ant2" | ||
| 12 | clue: "colony" | ||
| 13 | answer: "ant" | ||
| 14 | symbols: PLANET | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "MANOR" | ||
| 18 | path: "Panels/Room_1/manor" | ||
| 19 | clue: "manor" | ||
| 20 | answer: "mentor" | ||
| 21 | symbols: SPARKLES | ||
| 22 | symbols: PLANET | ||
| 23 | } | ||
| 24 | paintings { | ||
| 25 | name: "STOP3" | ||
| 26 | path: "Components/Paintings/QuickTravel/stop3" | ||
| 27 | } | ||
| 28 | paintings { | ||
| 29 | name: "GO3" | ||
| 30 | path: "Components/Paintings/QuickTravel/go3" | ||
| 31 | required_door { name: "Quick Travel 3" } | ||
| 32 | } | ||
| 33 | paintings { | ||
| 34 | name: "STOP1" | ||
| 35 | path: "Components/Paintings/QuickTravel/stop1" | ||
| 36 | } | ||
| 37 | paintings { | ||
| 38 | name: "GO1" | ||
| 39 | path: "Components/Paintings/QuickTravel/go1" | ||
| 40 | } | ||
| diff --git a/data/maps/icarus/rooms/Cow Quicktravel.txtpb b/data/maps/icarus/rooms/Cow Quicktravel.txtpb new file mode 100644 index 0000000..9bb5c82 --- /dev/null +++ b/data/maps/icarus/rooms/Cow Quicktravel.txtpb | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | name: "Cow Quicktravel" | ||
| 2 | paintings { | ||
| 3 | name: "SUN4" | ||
| 4 | path: "Components/Paintings/sun4" | ||
| 5 | } | ||
| 6 | paintings { | ||
| 7 | name: "GO4" | ||
| 8 | path: "Components/Paintings/QuickTravel/go4" | ||
| 9 | required_door { name: "Quick Travel 4" } | ||
| 10 | } | ||
| 11 | paintings { | ||
| 12 | name: "STOP4" | ||
| 13 | path: "Components/Paintings/QuickTravel/stop4" | ||
| 14 | } | ||
| diff --git a/data/maps/icarus/rooms/Fatherland Quicktravel.txtpb b/data/maps/icarus/rooms/Fatherland Quicktravel.txtpb new file mode 100644 index 0000000..c4c11b5 --- /dev/null +++ b/data/maps/icarus/rooms/Fatherland Quicktravel.txtpb | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | name: "Fatherland Quicktravel" | ||
| 2 | paintings { | ||
| 3 | name: "STOP7" | ||
| 4 | path: "Components/Paintings/QuickTravel/stop7" | ||
| 5 | } | ||
| 6 | paintings { | ||
| 7 | name: "GO7" | ||
| 8 | path: "Components/Paintings/QuickTravel/go7" | ||
| 9 | required_door { name: "Quick Travel 7" } | ||
| 10 | } | ||
| diff --git a/data/maps/icarus/rooms/Fatherland.txtpb b/data/maps/icarus/rooms/Fatherland.txtpb new file mode 100644 index 0000000..c04ca75 --- /dev/null +++ b/data/maps/icarus/rooms/Fatherland.txtpb | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | name: "Fatherland" | ||
| 2 | panels { | ||
| 3 | name: "FATHERLAND" | ||
| 4 | path: "Panels/Room_1/fatherland" | ||
| 5 | clue: "fatherland" | ||
| 6 | answer: "homeland" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| 9 | paintings { | ||
| 10 | name: "SUN7" | ||
| 11 | path: "Components/Paintings/sun7" | ||
| 12 | } | ||
| 13 | paintings { | ||
| 14 | name: "WINGS2" | ||
| 15 | path: "Components/Paintings/wings2" | ||
| 16 | } | ||
| diff --git a/data/maps/icarus/rooms/Highest Point.txtpb b/data/maps/icarus/rooms/Highest Point.txtpb new file mode 100644 index 0000000..c4e740b --- /dev/null +++ b/data/maps/icarus/rooms/Highest Point.txtpb | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | name: "Highest Point" | ||
| 2 | panels { | ||
| 3 | name: "DIAGNOSIS" | ||
| 4 | path: "Panels/Room_1/diagnosis" | ||
| 5 | clue: "diagnosis" | ||
| 6 | answer: "diagnose" | ||
| 7 | symbols: PLANET | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "QUEEN" | ||
| 11 | path: "Panels/Room_1/queen" | ||
| 12 | clue: "queen" | ||
| 13 | answer: "ant" | ||
| 14 | symbols: GENDER | ||
| 15 | } | ||
| 16 | paintings { | ||
| 17 | name: "SUN3" | ||
| 18 | path: "Components/Paintings/sun3" | ||
| 19 | } | ||
| diff --git a/data/maps/icarus/rooms/Mastery.txtpb b/data/maps/icarus/rooms/Mastery.txtpb new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/data/maps/icarus/rooms/Mastery.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mastery" | ||
| 2 | masteries { | ||
| 3 | name: "MASTERY" | ||
| 4 | path: "Components/Collectables/collectable" | ||
| 5 | } | ||
| diff --git a/data/maps/icarus/rooms/Maze Back.txtpb b/data/maps/icarus/rooms/Maze Back.txtpb new file mode 100644 index 0000000..860d183 --- /dev/null +++ b/data/maps/icarus/rooms/Maze Back.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Maze Back" | ||
| 2 | panels { | ||
| 3 | name: "THESE" | ||
| 4 | path: "Panels/30DegreePanels/thesis" | ||
| 5 | clue: "these" | ||
| 6 | answer: "thesis" | ||
| 7 | symbols: PLANET | ||
| 8 | } | ||
| diff --git a/data/maps/icarus/rooms/Maze King Painting.txtpb b/data/maps/icarus/rooms/Maze King Painting.txtpb new file mode 100644 index 0000000..11e2f11 --- /dev/null +++ b/data/maps/icarus/rooms/Maze King Painting.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Maze King Painting" | ||
| 2 | paintings { | ||
| 3 | name: "WINGS13" | ||
| 4 | path: "Components/Paintings/30 degrees/wings13" | ||
| 5 | # There's no other entrance to this region (or any entrance at all in vanilla) | ||
| 6 | # so we can't allow this painting to be an entrance. | ||
| 7 | exit_only: true | ||
| 8 | } | ||
| diff --git a/data/maps/icarus/rooms/Maze King Panel.txtpb b/data/maps/icarus/rooms/Maze King Panel.txtpb new file mode 100644 index 0000000..0654ea2 --- /dev/null +++ b/data/maps/icarus/rooms/Maze King Panel.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Maze King Panel" | ||
| 2 | panels { | ||
| 3 | name: "KING (2)" | ||
| 4 | path: "Panels/Other30degree panels/king2" | ||
| 5 | clue: "king" | ||
| 6 | answer: "royal" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| diff --git a/data/maps/icarus/rooms/Maze Wings Passage.txtpb b/data/maps/icarus/rooms/Maze Wings Passage.txtpb new file mode 100644 index 0000000..2533251 --- /dev/null +++ b/data/maps/icarus/rooms/Maze Wings Passage.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Maze Wings Passage" | ||
| 2 | paintings { | ||
| 3 | name: "WINGS12" | ||
| 4 | path: "Components/Paintings/30 degrees/wings12" | ||
| 5 | } | ||
| 6 | paintings { | ||
| 7 | name: "WINGS15" | ||
| 8 | path: "Components/Paintings/30 degrees/wings15" | ||
| 9 | } | ||
| diff --git a/data/maps/icarus/rooms/Maze.txtpb b/data/maps/icarus/rooms/Maze.txtpb new file mode 100644 index 0000000..ee317d9 --- /dev/null +++ b/data/maps/icarus/rooms/Maze.txtpb | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | name: "Maze" | ||
| 2 | panels { | ||
| 3 | name: "KING (1)" | ||
| 4 | path: "Panels/Other30degree panels/king" | ||
| 5 | clue: "king" | ||
| 6 | answer: "ruler" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "ANALYSIS" | ||
| 11 | path: "Panels/Room_1/princes2" | ||
| 12 | clue: "analysis" | ||
| 13 | answer: "analyse" | ||
| 14 | symbols: PLANET | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "MANSLAUGHTER" | ||
| 18 | path: "Panels/Room_1/manslaughter" | ||
| 19 | clue: "manslaughter" | ||
| 20 | answer: "murder" | ||
| 21 | symbols: GENDER | ||
| 22 | } | ||
| 23 | panels { | ||
| 24 | name: "MEDIUMS" | ||
| 25 | path: "Panels/Room_1/mediums" | ||
| 26 | clue: "mediums" | ||
| 27 | answer: "media" | ||
| 28 | symbols: PLANET | ||
| 29 | } | ||
| 30 | panels { | ||
| 31 | name: "BOOKS" | ||
| 32 | path: "Panels/Other30degree panels/books" | ||
| 33 | clue: "books" | ||
| 34 | answer: "library" | ||
| 35 | symbols: PLANET | ||
| 36 | } | ||
| 37 | paintings { | ||
| 38 | name: "SUN14" | ||
| 39 | path: "Components/Paintings/30 degrees/sun14" | ||
| 40 | } | ||
| 41 | paintings { | ||
| 42 | name: "WINGS14" | ||
| 43 | path: "Components/Paintings/30 degrees/wings14" | ||
| 44 | } | ||
| 45 | paintings { | ||
| 46 | name: "SUN5" | ||
| 47 | path: "Components/Paintings/30 degrees/sun5" | ||
| 48 | } | ||
| 49 | paintings { | ||
| 50 | name: "WINGS9" | ||
| 51 | path: "Components/Paintings/30 degrees/wings9" | ||
| 52 | } | ||
| 53 | paintings { | ||
| 54 | name: "SUN4" | ||
| 55 | path: "Components/Paintings/30 degrees/sun4" | ||
| 56 | } | ||
| 57 | paintings { | ||
| 58 | name: "WINGS16" | ||
| 59 | path: "Components/Paintings/30 degrees/wings16" | ||
| 60 | } | ||
| diff --git a/data/maps/icarus/rooms/Mediums Quicktravel.txtpb b/data/maps/icarus/rooms/Mediums Quicktravel.txtpb new file mode 100644 index 0000000..9452450 --- /dev/null +++ b/data/maps/icarus/rooms/Mediums Quicktravel.txtpb | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | name: "Mediums Quicktravel" | ||
| 2 | paintings { | ||
| 3 | name: "STOP9" | ||
| 4 | path: "Components/Paintings/QuickTravel/stop9" | ||
| 5 | } | ||
| 6 | paintings { | ||
| 7 | name: "GO9" | ||
| 8 | path: "Components/Paintings/QuickTravel/go9" | ||
| 9 | required_door { name: "Quick Travel 9" } | ||
| 10 | } | ||
| diff --git a/data/maps/icarus/rooms/Mini Icarus 2.txtpb b/data/maps/icarus/rooms/Mini Icarus 2.txtpb new file mode 100644 index 0000000..b557500 --- /dev/null +++ b/data/maps/icarus/rooms/Mini Icarus 2.txtpb | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | name: "Mini Icarus 2" | ||
| 2 | panels { | ||
| 3 | name: "BATTERY" | ||
| 4 | path: "Panels/Room_1/battery" | ||
| 5 | clue: "battery" | ||
| 6 | answer: "cell" | ||
| 7 | symbols: PLANET | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "TROUPE" | ||
| 11 | path: "Panels/Room_1/troupe" | ||
| 12 | clue: "troupe" | ||
| 13 | answer: "actor" | ||
| 14 | symbols: PLANET | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "ANIMALS" | ||
| 18 | path: "Panels/Room_1/animals" | ||
| 19 | clue: "animals" | ||
| 20 | answer: "zoo" | ||
| 21 | symbols: PLANET | ||
| 22 | # Intended to be solved upside down. | ||
| 23 | } | ||
| 24 | panels { | ||
| 25 | name: "ARROWS" | ||
| 26 | path: "Panels/Room_1/arrow" | ||
| 27 | clue: "arrows" | ||
| 28 | answer: "quiver" | ||
| 29 | symbols: PLANET | ||
| 30 | } | ||
| 31 | panels { | ||
| 32 | name: "SQUAD" | ||
| 33 | path: "Panels/Room_1/ammo" | ||
| 34 | clue: "squad" | ||
| 35 | answer: "soldier" | ||
| 36 | symbols: PLANET | ||
| 37 | } | ||
| 38 | paintings { | ||
| 39 | name: "SUN10" | ||
| 40 | path: "Components/Paintings/sun10" | ||
| 41 | } | ||
| 42 | paintings { | ||
| 43 | name: "SUN11" | ||
| 44 | path: "Components/Paintings/sun11" | ||
| 45 | } | ||
| diff --git a/data/maps/icarus/rooms/Mini Icarus 3.txtpb b/data/maps/icarus/rooms/Mini Icarus 3.txtpb new file mode 100644 index 0000000..633bf79 --- /dev/null +++ b/data/maps/icarus/rooms/Mini Icarus 3.txtpb | |||
| @@ -0,0 +1 @@ | |||
| name: "Mini Icarus 3" | |||
| diff --git a/data/maps/icarus/rooms/Mini Icarus Sun Loop.txtpb b/data/maps/icarus/rooms/Mini Icarus Sun Loop.txtpb new file mode 100644 index 0000000..450bfdd --- /dev/null +++ b/data/maps/icarus/rooms/Mini Icarus Sun Loop.txtpb | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | name: "Mini Icarus Sun Loop" | ||
| 2 | paintings { | ||
| 3 | name: "GO8" | ||
| 4 | path: "Components/Paintings/QuickTravel/go8" | ||
| 5 | required_door { name: "Quick Travel 8" } | ||
| 6 | } | ||
| 7 | paintings { | ||
| 8 | name: "STOP8" | ||
| 9 | path: "Components/Paintings/QuickTravel/stop8" | ||
| 10 | } | ||
| 11 | paintings { | ||
| 12 | name: "TROUBLEDESTINATION" | ||
| 13 | path: "Components/Paintings/TroubleDestination" | ||
| 14 | } | ||
| 15 | paintings { | ||
| 16 | name: "SUN12" | ||
| 17 | path: "Components/Paintings/sun12" | ||
| 18 | } | ||
| 19 | paintings { | ||
| 20 | name: "SUN13" | ||
| 21 | path: "Components/Paintings/sun13" | ||
| 22 | } | ||
| diff --git a/data/maps/icarus/rooms/Mini Icarus Wings Painting.txtpb b/data/maps/icarus/rooms/Mini Icarus Wings Painting.txtpb new file mode 100644 index 0000000..16a597c --- /dev/null +++ b/data/maps/icarus/rooms/Mini Icarus Wings Painting.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mini Icarus Wings Painting" | ||
| 2 | paintings { | ||
| 3 | name: "WINGS4" | ||
| 4 | path: "Components/Paintings/wings4" | ||
| 5 | } | ||
| diff --git a/data/maps/icarus/rooms/Painting Maze 1.txtpb b/data/maps/icarus/rooms/Painting Maze 1.txtpb new file mode 100644 index 0000000..19a3855 --- /dev/null +++ b/data/maps/icarus/rooms/Painting Maze 1.txtpb | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | name: "Painting Maze 1" | ||
| 2 | paintings { | ||
| 3 | name: "SUN9" | ||
| 4 | path: "Components/Paintings/sun9" | ||
| 5 | } | ||
| 6 | paintings { | ||
| 7 | name: "WINGS3" | ||
| 8 | path: "Components/Paintings/wings3" | ||
| 9 | } | ||
| 10 | paintings { | ||
| 11 | name: "SUN8" | ||
| 12 | path: "Components/Paintings/sun8" | ||
| 13 | } | ||
| diff --git a/data/maps/icarus/rooms/Painting Maze 2.txtpb b/data/maps/icarus/rooms/Painting Maze 2.txtpb new file mode 100644 index 0000000..7dc6a79 --- /dev/null +++ b/data/maps/icarus/rooms/Painting Maze 2.txtpb | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | name: "Painting Maze 2" | ||
| 2 | paintings { | ||
| 3 | name: "WINGS10" | ||
| 4 | path: "Components/Paintings/30 degrees/wings10" | ||
| 5 | } | ||
| 6 | paintings { | ||
| 7 | name: "WINGS11" | ||
| 8 | path: "Components/Paintings/30 degrees/wings11" | ||
| 9 | } | ||
| 10 | paintings { | ||
| 11 | name: "SUN6" | ||
| 12 | path: "Components/Paintings/30 degrees/sun6" | ||
| 13 | } | ||
| diff --git a/data/maps/icarus/rooms/Patricide Room.txtpb b/data/maps/icarus/rooms/Patricide Room.txtpb new file mode 100644 index 0000000..90945de --- /dev/null +++ b/data/maps/icarus/rooms/Patricide Room.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Patricide Room" | ||
| 2 | paintings { | ||
| 3 | name: "WINGS10" | ||
| 4 | path: "Components/Paintings/other 30 degrees/wings10" | ||
| 5 | } | ||
| 6 | paintings { | ||
| 7 | name: "SUN4" | ||
| 8 | path: "Components/Paintings/other 30 degrees/sun4" | ||
| 9 | } | ||
| diff --git a/data/maps/icarus/rooms/Pillar Ramp.txtpb b/data/maps/icarus/rooms/Pillar Ramp.txtpb new file mode 100644 index 0000000..9b056f7 --- /dev/null +++ b/data/maps/icarus/rooms/Pillar Ramp.txtpb | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | name: "Pillar Ramp" | ||
| 2 | panels { | ||
| 3 | name: "ASTEROID" | ||
| 4 | path: "Panels/Room_1/asteroid" | ||
| 5 | clue: "asteroid" | ||
| 6 | answer: "belt" | ||
| 7 | symbols: PLANET | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "BUNCH" | ||
| 11 | path: "Panels/Room_1/bunch" | ||
| 12 | clue: "bunch" | ||
| 13 | answer: "banana" | ||
| 14 | symbols: PLANET | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "PRINCES" | ||
| 18 | path: "Panels/Room_1/princes" | ||
| 19 | clue: "princes" | ||
| 20 | answer: "princess" | ||
| 21 | symbols: PLANET | ||
| 22 | } | ||
| 23 | panels { | ||
| 24 | name: "PATRICIDE" | ||
| 25 | path: "Panels/Room_1/patricide" | ||
| 26 | clue: "patricide" | ||
| 27 | answer: "murder" | ||
| 28 | symbols: GENDER | ||
| 29 | } | ||
| 30 | panels { | ||
| 31 | name: "DRONE" | ||
| 32 | path: "Panels/Room_1/ant3" | ||
| 33 | clue: "drone" | ||
| 34 | answer: "ant" | ||
| 35 | symbols: GENDER | ||
| 36 | } | ||
| 37 | panels { | ||
| 38 | name: "PEA (1)" | ||
| 39 | path: "Panels/Room_1/pea" | ||
| 40 | clue: "pea" | ||
| 41 | answer: "pod" | ||
| 42 | symbols: PLANET | ||
| 43 | } | ||
| 44 | paintings { | ||
| 45 | name: "SUN6" | ||
| 46 | path: "Components/Paintings/sun6" | ||
| 47 | } | ||
| 48 | paintings { | ||
| 49 | name: "GO10" | ||
| 50 | path: "Components/Paintings/QuickTravel/go10" | ||
| 51 | required_door { name: "Quick Travel 10" } | ||
| 52 | } | ||
| 53 | paintings { | ||
| 54 | name: "STOP10" | ||
| 55 | path: "Components/Paintings/QuickTravel/stop10" | ||
| 56 | } | ||
| 57 | paintings { | ||
| 58 | name: "GO6" | ||
| 59 | path: "Components/Paintings/QuickTravel/go6" | ||
| 60 | required_door { name: "Quick Travel 6" } | ||
| 61 | } | ||
| 62 | paintings { | ||
| 63 | name: "STOP6" | ||
| 64 | path: "Components/Paintings/QuickTravel/stop6" | ||
| 65 | } | ||
| diff --git a/data/maps/icarus/rooms/Spiral Ramp.txtpb b/data/maps/icarus/rooms/Spiral Ramp.txtpb new file mode 100644 index 0000000..bd2013a --- /dev/null +++ b/data/maps/icarus/rooms/Spiral Ramp.txtpb | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | name: "Spiral Ramp" | ||
| 2 | panels { | ||
| 3 | name: "FIREMAN" | ||
| 4 | path: "Panels/Room_1/fireman" | ||
| 5 | clue: "fireman" | ||
| 6 | answer: "firefighter" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| 9 | paintings { | ||
| 10 | name: "WINGS6" | ||
| 11 | path: "Components/Paintings/oneways/wings6" | ||
| 12 | required_door { name: "Near Fireman Wings Painting" } | ||
| 13 | } | ||
| 14 | paintings { | ||
| 15 | name: "SUN5" | ||
| 16 | path: "Components/Paintings/sun5" | ||
| 17 | required_door { name: "Sun Painting To Drone" } | ||
| 18 | } | ||
| 19 | paintings { | ||
| 20 | name: "WINGS7" | ||
| 21 | path: "Components/Paintings/oneways/wings7" | ||
| 22 | } | ||
| 23 | paintings { | ||
| 24 | name: "WINGS11" | ||
| 25 | path: "Components/Paintings/other 30 degrees/wings11" | ||
| 26 | exit_only: true | ||
| 27 | # There is a ledge near the painting so we might want to turn off exit_only | ||
| 28 | # at some point. | ||
| 29 | } | ||
| diff --git a/data/maps/icarus/rooms/The Orb.txtpb b/data/maps/icarus/rooms/The Orb.txtpb new file mode 100644 index 0000000..c9284d1 --- /dev/null +++ b/data/maps/icarus/rooms/The Orb.txtpb | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | name: "The Orb" | ||
| 2 | panels { | ||
| 3 | name: "ADDERS" | ||
| 4 | path: "Panels/Room_1/needle2" | ||
| 5 | clue: "adders" | ||
| 6 | answer: "sum" | ||
| 7 | symbols: PLANET | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "PUPPY" | ||
| 11 | path: "Panels/Room_1/puppy" | ||
| 12 | clue: "puppy" | ||
| 13 | answer: "garbage" | ||
| 14 | symbols: SUN | ||
| 15 | symbols: PLANET | ||
| 16 | } | ||
| 17 | panels { | ||
| 18 | name: "NEEDLE" | ||
| 19 | path: "Panels/Room_1/needle" | ||
| 20 | clue: "needle" | ||
| 21 | answer: "needless" | ||
| 22 | symbols: PLANET | ||
| 23 | } | ||
| 24 | panels { | ||
| 25 | name: "US" | ||
| 26 | path: "Panels/Room_1/us" | ||
| 27 | clue: "us" | ||
| 28 | answer: "mess" | ||
| 29 | symbols: PLANET | ||
| 30 | } | ||
| 31 | panels { | ||
| 32 | name: "FISH" | ||
| 33 | path: "Panels/Room_1/fish" | ||
| 34 | clue: "fish" | ||
| 35 | answer: "student" | ||
| 36 | symbols: BOXES | ||
| 37 | symbols: PLANET | ||
| 38 | } | ||
| 39 | panels { | ||
| 40 | name: "DEADLINE" | ||
| 41 | path: "Panels/Room_1/deadline" | ||
| 42 | clue: "deadline" | ||
| 43 | answer: "deadliness" | ||
| 44 | symbols: PLANET | ||
| 45 | } | ||
| 46 | panels { | ||
| 47 | name: "PEA (2)" | ||
| 48 | path: "Panels/Room_1/pea2" | ||
| 49 | clue: "pea" | ||
| 50 | answer: "dolphin" | ||
| 51 | symbols: PLANET | ||
| 52 | } | ||
| 53 | panels { | ||
| 54 | name: "THESIS" | ||
| 55 | path: "Panels/Room_1/thesis" | ||
| 56 | clue: "thesis" | ||
| 57 | answer: "these" | ||
| 58 | symbols: PLANET | ||
| 59 | } | ||
| 60 | panels { | ||
| 61 | name: "CLUTCH (1)" | ||
| 62 | path: "Panels/Room_1/clutch" | ||
| 63 | clue: "clutch" | ||
| 64 | answer: "dude" | ||
| 65 | symbols: PLANET | ||
| 66 | symbols: GENDER | ||
| 67 | } | ||
| 68 | panels { | ||
| 69 | name: "BASIS (2)" | ||
| 70 | path: "Panels/Room_1/basis2" | ||
| 71 | clue: "basis" | ||
| 72 | answer: "base" | ||
| 73 | symbols: PLANET | ||
| 74 | } | ||
| 75 | panels { | ||
| 76 | name: "AXIS" | ||
| 77 | path: "Panels/Room_1/axis" | ||
| 78 | clue: "axis" | ||
| 79 | answer: "Axe" | ||
| 80 | symbols: PLANET | ||
| 81 | } | ||
| 82 | panels { | ||
| 83 | name: "STRAIGHT" | ||
| 84 | path: "Panels/Room_1/straight" | ||
| 85 | clue: "straight" | ||
| 86 | answer: "queer" | ||
| 87 | symbols: GENDER | ||
| 88 | } | ||
| 89 | panels { | ||
| 90 | name: "HISS" | ||
| 91 | path: "Panels/Room_1/hiss" | ||
| 92 | clue: "hiss" | ||
| 93 | answer: "their" | ||
| 94 | symbols: PLANET | ||
| 95 | symbols: GENDER | ||
| 96 | } | ||
| 97 | panels { | ||
| 98 | name: "DISCUS" | ||
| 99 | path: "Panels/Room_1/discus" | ||
| 100 | clue: "discus" | ||
| 101 | answer: "discuss" | ||
| 102 | symbols: PLANET | ||
| 103 | } | ||
| 104 | panels { | ||
| 105 | name: "SON" | ||
| 106 | path: "Panels/Room_1/son" | ||
| 107 | clue: "son" | ||
| 108 | answer: "child" | ||
| 109 | symbols: GENDER | ||
| 110 | } | ||
| 111 | panels { | ||
| 112 | name: "CLUTCH (2)" | ||
| 113 | path: "Panels/Room_1/clutch2" | ||
| 114 | clue: "clutch" | ||
| 115 | answer: "chick" | ||
| 116 | symbols: PLANET | ||
| 117 | } | ||
| diff --git a/data/maps/icarus/rooms/Through Woman (Obverse).txtpb b/data/maps/icarus/rooms/Through Woman (Obverse).txtpb new file mode 100644 index 0000000..c502d3a --- /dev/null +++ b/data/maps/icarus/rooms/Through Woman (Obverse).txtpb | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | name: "Through Woman (Obverse)" | ||
| 2 | panels { | ||
| 3 | name: "HUMAN (2)" | ||
| 4 | path: "Panels/Room_1/human" | ||
| 5 | clue: "human" | ||
| 6 | answer: "man" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "COW" | ||
| 11 | path: "Panels/Room_1/cow" | ||
| 12 | clue: "cow" | ||
| 13 | answer: "elephant" | ||
| 14 | symbols: GENDER | ||
| 15 | } | ||
| 16 | paintings { | ||
| 17 | name: "SUN2" | ||
| 18 | path: "Components/Paintings/sun2" | ||
| 19 | } | ||
| 20 | paintings { | ||
| 21 | name: "WINGS6" | ||
| 22 | path: "Components/Paintings/wings6" | ||
| 23 | } | ||
| 24 | paintings { | ||
| 25 | name: "WINGS3" | ||
| 26 | path: "Components/Paintings/oneways/wings3" | ||
| 27 | required_door { name: "Near Fireman Wings Painting" } | ||
| 28 | } | ||
| diff --git a/data/maps/icarus/rooms/Through Woman (Reverse).txtpb b/data/maps/icarus/rooms/Through Woman (Reverse).txtpb new file mode 100644 index 0000000..661be31 --- /dev/null +++ b/data/maps/icarus/rooms/Through Woman (Reverse).txtpb | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | name: "Through Woman (Reverse)" | ||
| 2 | panels { | ||
| 3 | name: "PRINCE" | ||
| 4 | path: "Panels/Room_1/prince" | ||
| 5 | clue: "prince" | ||
| 6 | answer: "princess" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "BASIS (1)" | ||
| 11 | path: "Panels/Room_1/basis" | ||
| 12 | clue: "basis" | ||
| 13 | answer: "bases" | ||
| 14 | symbols: PLANET | ||
| 15 | } | ||
| 16 | paintings { | ||
| 17 | name: "WINGS8" | ||
| 18 | path: "Components/Paintings/oneways/wings8" | ||
| 19 | } | ||
| diff --git a/data/maps/icarus/rooms/Trans Rights Panels.txtpb b/data/maps/icarus/rooms/Trans Rights Panels.txtpb new file mode 100644 index 0000000..e51d4bc --- /dev/null +++ b/data/maps/icarus/rooms/Trans Rights Panels.txtpb | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | name: "Trans Rights Panels" | ||
| 2 | panels { | ||
| 3 | name: "AGENDER" | ||
| 4 | path: "Panels/Room_1/human4" | ||
| 5 | clue: "agender" | ||
| 6 | answer: "human" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "HUMAN (3)" | ||
| 11 | path: "Panels/Room_1/human5" | ||
| 12 | clue: "human" | ||
| 13 | answer: "female" | ||
| 14 | symbols: GENDER | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "HUMAN (4)" | ||
| 18 | path: "Panels/Room_1/human6" | ||
| 19 | clue: "human" | ||
| 20 | answer: "male" | ||
| 21 | symbols: GENDER | ||
| 22 | } | ||
| diff --git a/data/maps/icarus/rooms/Trans Rights.txtpb b/data/maps/icarus/rooms/Trans Rights.txtpb new file mode 100644 index 0000000..2ca98dd --- /dev/null +++ b/data/maps/icarus/rooms/Trans Rights.txtpb | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | name: "Trans Rights" | ||
| 2 | # slay | ||
| 3 | panels { | ||
| 4 | name: "SERVANT (1)" | ||
| 5 | path: "Panels/Room_1/servant" | ||
| 6 | clue: "servant" | ||
| 7 | answer: "butler" | ||
| 8 | symbols: GENDER | ||
| 9 | } | ||
| 10 | panels { | ||
| 11 | name: "SERVANT (2)" | ||
| 12 | path: "Panels/Room_1/servant2" | ||
| 13 | clue: "servant" | ||
| 14 | answer: "maid" | ||
| 15 | symbols: GENDER | ||
| 16 | } | ||
| 17 | paintings { | ||
| 18 | name: "GO2" | ||
| 19 | path: "Components/Paintings/QuickTravel/go2" | ||
| 20 | required_door { name: "Quick Travel 2" } | ||
| 21 | } | ||
| 22 | paintings { | ||
| 23 | name: "STOP2" | ||
| 24 | path: "Components/Paintings/QuickTravel/stop2" | ||
| 25 | } | ||
| diff --git a/data/maps/icarus/rooms/Welcome Spine (Obverse).txtpb b/data/maps/icarus/rooms/Welcome Spine (Obverse).txtpb new file mode 100644 index 0000000..63477d5 --- /dev/null +++ b/data/maps/icarus/rooms/Welcome Spine (Obverse).txtpb | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | name: "Welcome Spine (Obverse)" | ||
| 2 | panels { | ||
| 3 | name: "FISHWIFE" | ||
| 4 | path: "Panels/Room_1/fishwife" | ||
| 5 | clue: "fishwife" | ||
| 6 | answer: "fishmonger" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "HUMAN (1)" | ||
| 11 | path: "Panels/Room_1/human3" | ||
| 12 | clue: "human" | ||
| 13 | answer: "woman" | ||
| 14 | symbols: GENDER | ||
| 15 | } | ||
| 16 | ports { | ||
| 17 | name: "WORLDPORT" | ||
| 18 | display_name: "Entrance" | ||
| 19 | path: "Components/Signs/worldport" | ||
| 20 | destination { x: 55 y: -3.5 z: 7.75 } | ||
| 21 | rotation: 0 | ||
| 22 | } | ||
| diff --git a/data/maps/icarus/rooms/Welcome Spine (Reverse).txtpb b/data/maps/icarus/rooms/Welcome Spine (Reverse).txtpb new file mode 100644 index 0000000..7605141 --- /dev/null +++ b/data/maps/icarus/rooms/Welcome Spine (Reverse).txtpb | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | name: "Welcome Spine (Reverse)" | ||
| 2 | panels { | ||
| 3 | name: "FATHER" | ||
| 4 | path: "Panels/Room_1/father" | ||
| 5 | clue: "father" | ||
| 6 | answer: "parent" | ||
| 7 | symbols: GENDER | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "TERMITE" | ||
| 11 | path: "Panels/Room_1/bat" | ||
| 12 | clue: "termite" | ||
| 13 | answer: "colony" | ||
| 14 | symbols: PLANET | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "SISTER" | ||
| 18 | path: "Panels/Room_1/sister" | ||
| 19 | clue: "sister" | ||
| 20 | answer: "sibling" | ||
| 21 | symbols: GENDER | ||
| 22 | } | ||
| diff --git a/data/maps/icarus/rooms/Welcome Spine Quicktravel.txtpb b/data/maps/icarus/rooms/Welcome Spine Quicktravel.txtpb new file mode 100644 index 0000000..8890345 --- /dev/null +++ b/data/maps/icarus/rooms/Welcome Spine Quicktravel.txtpb | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | name: "Welcome Spine Quicktravel" | ||
| 2 | paintings { | ||
| 3 | name: "STOP5" | ||
| 4 | path: "Components/Paintings/QuickTravel/stop5" | ||
| 5 | } | ||
| 6 | paintings { | ||
| 7 | name: "GO5" | ||
| 8 | path: "Components/Paintings/QuickTravel/go5" | ||
| 9 | required_door { name: "Quick Travel 5" } | ||
| 10 | } | ||
| diff --git a/data/maps/the_advanced/connections.txtpb b/data/maps/the_advanced/connections.txtpb new file mode 100644 index 0000000..4425f3d --- /dev/null +++ b/data/maps/the_advanced/connections.txtpb | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | connections { | ||
| 2 | from_room: "Main Area" | ||
| 3 | to_room: "CBA" | ||
| 4 | door { name: "CBA Door" } | ||
| 5 | } | ||
| 6 | connections { | ||
| 7 | from_room: "CBA" | ||
| 8 | to_room: "Mastery" | ||
| 9 | door { name: "Mastery" } | ||
| 10 | } | ||
| diff --git a/data/maps/the_advanced/doors.txtpb b/data/maps/the_advanced/doors.txtpb new file mode 100644 index 0000000..fed24a8 --- /dev/null +++ b/data/maps/the_advanced/doors.txtpb | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | doors { | ||
| 2 | name: "Side Room Puzzles" | ||
| 3 | type: LOCATION_ONLY | ||
| 4 | panels { room: "Main Area" name: "Blank (1)" } | ||
| 5 | panels { room: "Main Area" name: "Blank (2)" } | ||
| 6 | panels { room: "Main Area" name: "Blank (3)" } | ||
| 7 | panels { room: "Main Area" name: "Blank (4)" } | ||
| 8 | panels { room: "Main Area" name: "Blank (5)" } | ||
| 9 | location_room: "Main Area" | ||
| 10 | } | ||
| 11 | doors { | ||
| 12 | name: "CBA Door" | ||
| 13 | type: EVENT | ||
| 14 | panels { room: "Main Area" name: "Blank (1)" } | ||
| 15 | panels { room: "Main Area" name: "Blank (2)" } | ||
| 16 | panels { room: "Main Area" name: "Blank (3)" } | ||
| 17 | panels { room: "Main Area" name: "Blank (4)" } | ||
| 18 | panels { room: "Main Area" name: "Blank (5)" } | ||
| 19 | panels { room: "Main Area" name: "BIRD" } | ||
| 20 | panels { room: "Main Area" name: "UNBOTTLING" } | ||
| 21 | panels { room: "Main Area" name: "ORGANIZATION" } | ||
| 22 | panels { room: "Main Area" name: "ORDER (1)" } | ||
| 23 | panels { room: "Main Area" name: "ORDER (2)" } | ||
| 24 | panels { room: "Main Area" name: "ORDER (3)" } | ||
| 25 | panels { room: "Main Area" name: "DECK (1)" } | ||
| 26 | panels { room: "Main Area" name: "DECK (2)" } | ||
| 27 | panels { room: "Main Area" name: "DECK (3)" } | ||
| 28 | panels { room: "Main Area" name: "OBSERVE" } | ||
| 29 | panels { room: "Main Area" name: "I" } | ||
| 30 | panels { room: "Main Area" name: "REST" } | ||
| 31 | panels { room: "Main Area" name: "THE" } | ||
| 32 | panels { room: "Main Area" name: "LIVES" } | ||
| 33 | panels { room: "Main Area" name: "DAIRY (1)" } | ||
| 34 | panels { room: "Main Area" name: "DAIRY (2)" } | ||
| 35 | panels { room: "Main Area" name: "DAIRY SAUCE" } | ||
| 36 | panels { room: "Main Area" name: "GULLIBLE (1)" } | ||
| 37 | panels { room: "Main Area" name: "GULLIBLE (2)" } | ||
| 38 | panels { room: "Main Area" name: "GULLIBLE (3)" } | ||
| 39 | panels { room: "Main Area" name: "FRUIT (1)" } | ||
| 40 | panels { room: "Main Area" name: "FRUIT (2)" } | ||
| 41 | panels { room: "Main Area" name: "FRUIT FRUIT" } | ||
| 42 | complete_at: 23 # ???? | ||
| 43 | } | ||
| 44 | doors { | ||
| 45 | name: "Mastery" | ||
| 46 | type: EVENT | ||
| 47 | panels { room: "CBA" name: "CBA (1)" } | ||
| 48 | panels { room: "CBA" name: "CBA (2)" } | ||
| 49 | panels { room: "CBA" name: "CBA (3)" } | ||
| 50 | } | ||
| diff --git a/data/maps/the_advanced/metadata.txtpb b/data/maps/the_advanced/metadata.txtpb new file mode 100644 index 0000000..cee10b6 --- /dev/null +++ b/data/maps/the_advanced/metadata.txtpb | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | display_name: "The Advanced" | ||
| 2 | type: GIFT_MAP | ||
| 3 | # The map's mastery is created at runtime. | ||
| 4 | custom_nodes: "Components/Collectables/collectable" | ||
| diff --git a/data/maps/the_advanced/rooms/CBA.txtpb b/data/maps/the_advanced/rooms/CBA.txtpb new file mode 100644 index 0000000..eefa0d4 --- /dev/null +++ b/data/maps/the_advanced/rooms/CBA.txtpb | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | name: "CBA" | ||
| 2 | panels { | ||
| 3 | name: "CBA (1)" | ||
| 4 | path: "Panels/Room_1/panel_29" | ||
| 5 | clue: "" | ||
| 6 | answer: "chess" | ||
| 7 | symbols: QUESTION | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "CBA (2)" | ||
| 11 | path: "Panels/Room_1/panel_30" | ||
| 12 | clue: "" | ||
| 13 | answer: "battle" | ||
| 14 | symbols: QUESTION | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "CBA (3)" | ||
| 18 | path: "Panels/Room_1/panel_31" | ||
| 19 | clue: "" | ||
| 20 | answer: "advanced" | ||
| 21 | symbols: QUESTION | ||
| 22 | } | ||
| diff --git a/data/maps/the_advanced/rooms/Main Area.txtpb b/data/maps/the_advanced/rooms/Main Area.txtpb new file mode 100644 index 0000000..42e576e --- /dev/null +++ b/data/maps/the_advanced/rooms/Main Area.txtpb | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | name: "Main Area" | ||
| 2 | panels { | ||
| 3 | name: "OBSERVE" | ||
| 4 | path: "Panels/Room_1/panel_1" | ||
| 5 | clue: "observe" | ||
| 6 | answer: "watch" | ||
| 7 | symbols: SUN | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "I" | ||
| 11 | path: "Panels/Room_1/panel_2" | ||
| 12 | clue: "i" | ||
| 13 | answer: "eye" | ||
| 14 | symbols: ZERO | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "REST" | ||
| 18 | path: "Panels/Room_1/panel_3" | ||
| 19 | clue: "rest" | ||
| 20 | answer: "sleep" | ||
| 21 | symbols: SUN | ||
| 22 | } | ||
| 23 | panels { | ||
| 24 | name: "THE" | ||
| 25 | path: "Panels/Room_1/panel_4" | ||
| 26 | clue: "the" | ||
| 27 | answer: "a" | ||
| 28 | symbols: ZERO | ||
| 29 | } | ||
| 30 | panels { | ||
| 31 | name: "LIVES" | ||
| 32 | path: "Panels/Room_1/panel_5" | ||
| 33 | clue: "lives" | ||
| 34 | answer: "souls" | ||
| 35 | symbols: SUN | ||
| 36 | } | ||
| 37 | panels { | ||
| 38 | name: "DAIRY (1)" | ||
| 39 | path: "Panels/Room_1/panel_6" | ||
| 40 | clue: "dairy" | ||
| 41 | answer: "cheese" | ||
| 42 | symbols: EXAMPLE | ||
| 43 | } | ||
| 44 | panels { | ||
| 45 | name: "DAIRY (2)" | ||
| 46 | path: "Panels/Room_1/panel_7" | ||
| 47 | clue: "dairy" | ||
| 48 | answer: "butter" | ||
| 49 | symbols: EXAMPLE | ||
| 50 | } | ||
| 51 | panels { | ||
| 52 | name: "DAIRY SAUCE" | ||
| 53 | path: "Panels/Room_1/panel_8" | ||
| 54 | clue: "dairy? sauce." | ||
| 55 | answer: "alfredo" | ||
| 56 | symbols: EXAMPLE | ||
| 57 | } | ||
| 58 | panels { | ||
| 59 | name: "GULLIBLE (1)" | ||
| 60 | path: "Panels/Room_1/panel_9" | ||
| 61 | clue: "gullible" | ||
| 62 | answer: "credulous" | ||
| 63 | symbols: SUN | ||
| 64 | } | ||
| 65 | panels { | ||
| 66 | name: "GULLIBLE (2)" | ||
| 67 | path: "Panels/Room_1/panel_10" | ||
| 68 | clue: "gullible" | ||
| 69 | answer: "bird" | ||
| 70 | symbols: SPARKLES | ||
| 71 | symbols: EXAMPLE | ||
| 72 | } | ||
| 73 | panels { | ||
| 74 | name: "GULLIBLE (3)" | ||
| 75 | path: "Panels/Room_1/panel_11" | ||
| 76 | clue: "gullible" | ||
| 77 | answer: "advice" | ||
| 78 | symbols: QUESTION | ||
| 79 | } | ||
| 80 | panels { | ||
| 81 | name: "FRUIT (1)" | ||
| 82 | path: "Panels/Room_1/panel_12" | ||
| 83 | clue: "fruit" | ||
| 84 | answer: "cherry" | ||
| 85 | symbols: EXAMPLE | ||
| 86 | } | ||
| 87 | panels { | ||
| 88 | name: "FRUIT (2)" | ||
| 89 | path: "Panels/Room_1/panel_13" | ||
| 90 | clue: "fruit" | ||
| 91 | answer: "banana" | ||
| 92 | symbols: EXAMPLE | ||
| 93 | } | ||
| 94 | panels { | ||
| 95 | name: "FRUIT FRUIT" | ||
| 96 | path: "Panels/Room_1/panel_14" | ||
| 97 | clue: "fruit? fruit!" | ||
| 98 | answer: "avocado" | ||
| 99 | symbols: EXAMPLE | ||
| 100 | } | ||
| 101 | panels { | ||
| 102 | name: "BIRD" | ||
| 103 | path: "Panels/Room_1/panel_15" | ||
| 104 | clue: "bird" | ||
| 105 | answer: "canary" | ||
| 106 | symbols: EXAMPLE | ||
| 107 | } | ||
| 108 | panels { | ||
| 109 | name: "UNBOTTLING" | ||
| 110 | path: "Panels/Room_1/panel_16" | ||
| 111 | clue: "unbottling" | ||
| 112 | answer: "bottling" | ||
| 113 | symbols: SUN | ||
| 114 | } | ||
| 115 | panels { | ||
| 116 | name: "ORGANIZATION" | ||
| 117 | path: "Panels/Room_1/panel_17" | ||
| 118 | clue: "organization" | ||
| 119 | answer: "association" | ||
| 120 | symbols: SUN | ||
| 121 | } | ||
| 122 | panels { | ||
| 123 | name: "ORDER (1)" | ||
| 124 | path: "Panels/Room_1/panel_18" | ||
| 125 | clue: "order" | ||
| 126 | answer: "chaos" | ||
| 127 | symbols: SUN | ||
| 128 | } | ||
| 129 | panels { | ||
| 130 | name: "ORDER (2)" | ||
| 131 | path: "Panels/Room_1/panel_19" | ||
| 132 | clue: "order" | ||
| 133 | answer: "border" | ||
| 134 | symbols: SPARKLES | ||
| 135 | } | ||
| 136 | panels { | ||
| 137 | name: "ORDER (3)" | ||
| 138 | path: "Panels/Room_1/panel_20" | ||
| 139 | clue: "order" | ||
| 140 | answer: "arrange" | ||
| 141 | symbols: SUN | ||
| 142 | } | ||
| 143 | panels { | ||
| 144 | name: "DECK (1)" | ||
| 145 | path: "Panels/Room_1/panel_21" | ||
| 146 | clue: "deck" | ||
| 147 | answer: "card" | ||
| 148 | symbols: BOXES | ||
| 149 | } | ||
| 150 | panels { | ||
| 151 | name: "DECK (2)" | ||
| 152 | path: "Panels/Room_1/panel_22" | ||
| 153 | clue: "deck" | ||
| 154 | answer: "black" | ||
| 155 | symbols: BOXES | ||
| 156 | } | ||
| 157 | panels { | ||
| 158 | name: "DECK (3)" | ||
| 159 | path: "Panels/Room_1/panel_23" | ||
| 160 | clue: "deck" | ||
| 161 | answer: "ace" | ||
| 162 | symbols: BOXES | ||
| 163 | } | ||
| 164 | panels { | ||
| 165 | name: "Blank (1)" | ||
| 166 | path: "Panels/Room_1/panel_24" | ||
| 167 | clue: "" | ||
| 168 | answer: "identity" | ||
| 169 | } | ||
| 170 | panels { | ||
| 171 | name: "Blank (2)" | ||
| 172 | path: "Panels/Room_1/panel_25" | ||
| 173 | clue: "" | ||
| 174 | answer: "theft" | ||
| 175 | } | ||
| 176 | panels { | ||
| 177 | name: "Blank (3)" | ||
| 178 | path: "Panels/Room_1/panel_26" | ||
| 179 | clue: "" | ||
| 180 | answer: "is" | ||
| 181 | } | ||
| 182 | panels { | ||
| 183 | name: "Blank (4)" | ||
| 184 | path: "Panels/Room_1/panel_27" | ||
| 185 | clue: "" | ||
| 186 | answer: "a" | ||
| 187 | } | ||
| 188 | panels { | ||
| 189 | name: "Blank (5)" | ||
| 190 | path: "Panels/Room_1/panel_28" | ||
| 191 | clue: "" | ||
| 192 | answer: "crime" | ||
| 193 | } | ||
| 194 | ports { | ||
| 195 | name: "WORLDPORT" | ||
| 196 | display_name: "Entrance" | ||
| 197 | path: "Components/Warps/worldport" | ||
| 198 | destination { x: 0 y: 0 z: 9.5 } | ||
| 199 | rotation: 0 | ||
| 200 | } | ||
| diff --git a/data/maps/the_advanced/rooms/Mastery.txtpb b/data/maps/the_advanced/rooms/Mastery.txtpb new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/data/maps/the_advanced/rooms/Mastery.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mastery" | ||
| 2 | masteries { | ||
| 3 | name: "MASTERY" | ||
| 4 | path: "Components/Collectables/collectable" | ||
| 5 | } | ||
| diff --git a/data/maps/the_ancient/doors.txtpb b/data/maps/the_ancient/doors.txtpb index 5dc062e..e550306 100644 --- a/data/maps/the_ancient/doors.txtpb +++ b/data/maps/the_ancient/doors.txtpb | |||
| @@ -38,7 +38,8 @@ doors { | |||
| 38 | } | 38 | } |
| 39 | doors { | 39 | doors { |
| 40 | name: "Lavender Cubes" | 40 | name: "Lavender Cubes" |
| 41 | type: STANDARD | 41 | type: LOCATION_ONLY |
| 42 | panels { room: "Inside" name: "COLOR" } | 42 | panels { room: "Inside" name: "COLOR" } |
| 43 | location_room: "Inside" | 43 | location_room: "Inside" |
| 44 | location_name: "COLOR" | ||
| 44 | } | 45 | } |
| diff --git a/data/maps/the_ancient/rooms/Inside.txtpb b/data/maps/the_ancient/rooms/Inside.txtpb index d6e8575..3723b2d 100644 --- a/data/maps/the_ancient/rooms/Inside.txtpb +++ b/data/maps/the_ancient/rooms/Inside.txtpb | |||
| @@ -5,5 +5,4 @@ panels { | |||
| 5 | clue: "color" | 5 | clue: "color" |
| 6 | answer: "lavender" | 6 | answer: "lavender" |
| 7 | symbols: EXAMPLE | 7 | symbols: EXAMPLE |
| 8 | # TODO: how does this connect to the "lavender_cubes" switch? | ||
| 9 | } | 8 | } |
| 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/doors.txtpb b/data/maps/the_bearer/doors.txtpb index f1f5a57..acbf86a 100644 --- a/data/maps/the_bearer/doors.txtpb +++ b/data/maps/the_bearer/doors.txtpb | |||
| @@ -241,6 +241,7 @@ doors { | |||
| 241 | doors { | 241 | doors { |
| 242 | name: "Control Center Brown Door" | 242 | name: "Control Center Brown Door" |
| 243 | type: CONTROL_CENTER_COLOR | 243 | type: CONTROL_CENTER_COLOR |
| 244 | latch: true | ||
| 244 | receivers: "Components/Doors/brown_1" | 245 | receivers: "Components/Doors/brown_1" |
| 245 | control_center_color: "brown" | 246 | control_center_color: "brown" |
| 246 | } | 247 | } |
| @@ -250,3 +251,18 @@ doors { | |||
| 250 | receivers: "Components/Doors/brown_2" | 251 | receivers: "Components/Doors/brown_2" |
| 251 | double_letters: true | 252 | double_letters: true |
| 252 | } | 253 | } |
| 254 | doors { | ||
| 255 | name: "Control Center Color Panel" | ||
| 256 | type: LOCATION_ONLY | ||
| 257 | panels { room: "Back Area" name: "COLOR" } | ||
| 258 | location_room: "Back Area" | ||
| 259 | location_name: "COLOR" | ||
| 260 | } | ||
| 261 | doors { | ||
| 262 | name: "Butterfly Room Panels" | ||
| 263 | type: LOCATION_ONLY | ||
| 264 | panels { room: "Butterfly Room" name: "DARKNESS" } | ||
| 265 | panels { room: "Butterfly Room" name: "VIBRANT" } | ||
| 266 | location_room: "Butterfly Room" | ||
| 267 | location_name: "DARKNESS, VIBRANT" | ||
| 268 | } | ||
| diff --git a/data/maps/the_bearer/rooms/Back Area.txtpb b/data/maps/the_bearer/rooms/Back Area.txtpb index 27e175c..2be4cb4 100644 --- a/data/maps/the_bearer/rooms/Back Area.txtpb +++ b/data/maps/the_bearer/rooms/Back Area.txtpb | |||
| @@ -7,13 +7,9 @@ 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" |
| 11 | display_name: "Dark Hallway" | ||
| 17 | path: "Components/Warps/worldport2" | 12 | path: "Components/Warps/worldport2" |
| 18 | orientation: "north" | 13 | destination { x: 10 y: 0 z: -84.5 } |
| 14 | rotation: 180 | ||
| 19 | } | 15 | } |
| diff --git a/data/maps/the_bearer/rooms/Entry.txtpb b/data/maps/the_bearer/rooms/Entry.txtpb index 4300c1f..517088d 100644 --- a/data/maps/the_bearer/rooms/Entry.txtpb +++ b/data/maps/the_bearer/rooms/Entry.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "UNYIELDING" | 59 | name: "UNYIELDING" |
| 60 | display_name: "Main Entrance" | ||
| 60 | path: "Components/Warps/worldport" | 61 | path: "Components/Warps/worldport" |
| 61 | orientation: "east" | 62 | destination { x: 3 y: 4 z: 15 } |
| 63 | rotation: 90 | ||
| 62 | } | 64 | } |
| 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..1b50ddd --- /dev/null +++ b/data/maps/the_bearer/rooms/Tree Entrance.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Tree Entrance" | ||
| 2 | ports { | ||
| 3 | name: "TREE" | ||
| 4 | display_name: "Brown Hallway" | ||
| 5 | path: "Components/Warps/worldport3" | ||
| 6 | destination { x: -19 y: 0 z: -83.5 } | ||
| 7 | rotation: 180 | ||
| 8 | } | ||
| diff --git a/data/maps/the_between/rooms/Control Center Side.txtpb b/data/maps/the_between/rooms/Control Center Side.txtpb index a6a126a..b308586 100644 --- a/data/maps/the_between/rooms/Control Center Side.txtpb +++ b/data/maps/the_between/rooms/Control Center Side.txtpb | |||
| @@ -13,11 +13,15 @@ paintings { | |||
| 13 | } | 13 | } |
| 14 | ports { | 14 | ports { |
| 15 | name: "CC" | 15 | name: "CC" |
| 16 | display_name: "Lavender Structure" | ||
| 16 | path: "Components/Warps/worldport3" | 17 | path: "Components/Warps/worldport3" |
| 17 | orientation: "north" | 18 | destination { x: 36 y: 0 z: 1 } |
| 19 | rotation: 180 | ||
| 18 | } | 20 | } |
| 19 | ports { | 21 | ports { |
| 20 | name: "LIVELY" | 22 | name: "LIVELY" |
| 23 | display_name: "Near Painting Worldport" | ||
| 21 | path: "Components/Warps/worldport2" | 24 | path: "Components/Warps/worldport2" |
| 22 | orientation: "south" | 25 | destination { x: 24 y: 0 z: 6.5 } |
| 26 | rotation: 0 | ||
| 23 | } | 27 | } |
| diff --git a/data/maps/the_between/rooms/Main Area.txtpb b/data/maps/the_between/rooms/Main Area.txtpb index a0fc596..898b265 100644 --- a/data/maps/the_between/rooms/Main Area.txtpb +++ b/data/maps/the_between/rooms/Main Area.txtpb | |||
| @@ -197,6 +197,8 @@ panels { | |||
| 197 | } | 197 | } |
| 198 | ports { | 198 | ports { |
| 199 | name: "GREAT" | 199 | name: "GREAT" |
| 200 | display_name: "Salmon Hallway" | ||
| 200 | path: "Components/Warps/worldport" | 201 | path: "Components/Warps/worldport" |
| 201 | orientation: "east" | 202 | destination { x: -1 y: 0 z: 20 } |
| 203 | rotation: 90 | ||
| 202 | } | 204 | } |
| diff --git a/data/maps/the_between/rooms/Plaza Entrance.txtpb b/data/maps/the_between/rooms/Plaza Entrance.txtpb index e4d7b19..894ebae 100644 --- a/data/maps/the_between/rooms/Plaza Entrance.txtpb +++ b/data/maps/the_between/rooms/Plaza Entrance.txtpb | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | name: "Plaza Entrance" | 1 | name: "Plaza Entrance" |
| 2 | ports { | 2 | ports { |
| 3 | name: "PLAZA" | 3 | name: "PLAZA" |
| 4 | display_name: "Trick or Treat Worldport" | ||
| 4 | path: "Components/Warps/worldport4" | 5 | path: "Components/Warps/worldport4" |
| 5 | orientation: "north" | 6 | destination { x: -38 y: 0 z: 1 } |
| 7 | rotation: 180 | ||
| 6 | } | 8 | } |
| diff --git a/data/maps/the_butterfly/doors.txtpb b/data/maps/the_butterfly/doors.txtpb index 987c269..1ebc3a2 100644 --- a/data/maps/the_butterfly/doors.txtpb +++ b/data/maps/the_butterfly/doors.txtpb | |||
| @@ -1,4 +1,3 @@ | |||
| 1 | # TODO: The gallery painting | ||
| 2 | doors { | 1 | doors { |
| 3 | name: "Panels" | 2 | name: "Panels" |
| 4 | type: EVENT | 3 | type: EVENT |
| diff --git a/data/maps/the_butterfly/rooms/Main Area.txtpb b/data/maps/the_butterfly/rooms/Main Area.txtpb index 8b441e4..453e64a 100644 --- a/data/maps/the_butterfly/rooms/Main Area.txtpb +++ b/data/maps/the_butterfly/rooms/Main Area.txtpb | |||
| @@ -127,6 +127,8 @@ panels { | |||
| 127 | } | 127 | } |
| 128 | ports { | 128 | ports { |
| 129 | name: "GALLERY" | 129 | name: "GALLERY" |
| 130 | display_name: "Worldport" | ||
| 130 | path: "Components/Warps/worldport" | 131 | path: "Components/Warps/worldport" |
| 131 | orientation: "southwest" # uhhhh this is new | 132 | destination { x: -19 y: 0 z: 19 } |
| 133 | rotation: 315 | ||
| 132 | } | 134 | } |
| diff --git a/data/maps/the_charismatic/connections.txtpb b/data/maps/the_charismatic/connections.txtpb new file mode 100644 index 0000000..6130302 --- /dev/null +++ b/data/maps/the_charismatic/connections.txtpb | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | connections { | ||
| 2 | from_room: "Main Area" | ||
| 3 | to_room: "Latitude South" | ||
| 4 | door { name: "Latitude South Door" } | ||
| 5 | } | ||
| 6 | connections { | ||
| 7 | from_room: "Main Area" | ||
| 8 | to_room: "Latitude Middle" | ||
| 9 | door { name: "Latitude Middle Door" } | ||
| 10 | } | ||
| 11 | connections { | ||
| 12 | from_room: "Main Area" | ||
| 13 | to_room: "Latitude North" | ||
| 14 | door { name: "Latitude North Door" } | ||
| 15 | } | ||
| 16 | connections { | ||
| 17 | from_room: "Main Area" | ||
| 18 | to_room: "Longitude West" | ||
| 19 | door { name: "Longitude West Door" } | ||
| 20 | } | ||
| 21 | connections { | ||
| 22 | from_room: "Main Area" | ||
| 23 | to_room: "Longitude Middle" | ||
| 24 | door { name: "Longitude Middle Door" } | ||
| 25 | } | ||
| 26 | connections { | ||
| 27 | from_room: "Main Area" | ||
| 28 | to_room: "Longitude East" | ||
| 29 | door { name: "Longitude East Door" } | ||
| 30 | } | ||
| 31 | connections { | ||
| 32 | from_room: "Main Area" | ||
| 33 | to_room: "Mastery" | ||
| 34 | door { name: "Mastery Door" } | ||
| 35 | } | ||
| diff --git a/data/maps/the_charismatic/doors.txtpb b/data/maps/the_charismatic/doors.txtpb new file mode 100644 index 0000000..0c7eb40 --- /dev/null +++ b/data/maps/the_charismatic/doors.txtpb | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | doors { | ||
| 2 | name: "Latitude South Door" | ||
| 3 | type: EVENT | ||
| 4 | panels { room: "Main Area" name: "TIP" } | ||
| 5 | panels { room: "Main Area" name: "KING" } | ||
| 6 | panels { room: "Main Area" name: "ARC" } | ||
| 7 | } | ||
| 8 | doors { | ||
| 9 | name: "Latitude Middle Door" | ||
| 10 | type: EVENT | ||
| 11 | panels { room: "Main Area" name: "NAIL" } | ||
| 12 | panels { room: "Main Area" name: "TILE" } | ||
| 13 | panels { room: "Main Area" name: "AQUA" } | ||
| 14 | } | ||
| 15 | doors { | ||
| 16 | name: "Latitude North Door" | ||
| 17 | type: EVENT | ||
| 18 | panels { room: "Main Area" name: "PINS" } | ||
| 19 | panels { room: "Main Area" name: "IT" } | ||
| 20 | panels { room: "Main Area" name: "HERE" } | ||
| 21 | } | ||
| 22 | doors { | ||
| 23 | name: "Longitude West Door" | ||
| 24 | type: EVENT | ||
| 25 | panels { room: "Main Area" name: "ARC" } | ||
| 26 | panels { room: "Main Area" name: "AQUA" } | ||
| 27 | panels { room: "Main Area" name: "HERE" } | ||
| 28 | } | ||
| 29 | doors { | ||
| 30 | name: "Longitude Middle Door" | ||
| 31 | type: EVENT | ||
| 32 | panels { room: "Main Area" name: "KING" } | ||
| 33 | panels { room: "Main Area" name: "TILE" } | ||
| 34 | panels { room: "Main Area" name: "IT" } | ||
| 35 | } | ||
| 36 | doors { | ||
| 37 | name: "Longitude East Door" | ||
| 38 | type: EVENT | ||
| 39 | panels { room: "Main Area" name: "TIP" } | ||
| 40 | panels { room: "Main Area" name: "NAIL" } | ||
| 41 | panels { room: "Main Area" name: "PINS" } | ||
| 42 | } | ||
| 43 | doors { | ||
| 44 | name: "Mastery Door" | ||
| 45 | type: EVENT | ||
| 46 | panels { room: "Main Area" name: "HERE" } | ||
| 47 | panels { room: "Main Area" name: "TILE" } | ||
| 48 | panels { room: "Main Area" name: "TIP" } | ||
| 49 | panels { room: "Main Area" name: "Blank" } | ||
| 50 | panels { room: "Latitude South" name: "CHARISMA" } | ||
| 51 | panels { room: "Latitude Middle" name: "FUNNY" } | ||
| 52 | panels { room: "Latitude North" name: "DEPENDABLE" } | ||
| 53 | panels { room: "Longitude West" name: "CREATIVE" } | ||
| 54 | panels { room: "Longitude Middle" name: "INTELLIGENT" } | ||
| 55 | panels { room: "Longitude East" name: "FUN" } | ||
| 56 | } | ||
| diff --git a/data/maps/the_charismatic/metadata.txtpb b/data/maps/the_charismatic/metadata.txtpb new file mode 100644 index 0000000..8d26105 --- /dev/null +++ b/data/maps/the_charismatic/metadata.txtpb | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | display_name: "The Charismatic" | ||
| 2 | type: GIFT_MAP | ||
| 3 | # The map's mastery is created at runtime. | ||
| 4 | custom_nodes: "Components/Collectables/collectable" | ||
| diff --git a/data/maps/the_charismatic/rooms/Latitude Middle.txtpb b/data/maps/the_charismatic/rooms/Latitude Middle.txtpb new file mode 100644 index 0000000..7d83dcf --- /dev/null +++ b/data/maps/the_charismatic/rooms/Latitude Middle.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Latitude Middle" | ||
| 2 | panels { | ||
| 3 | name: "FUNNY" | ||
| 4 | path: "Panels/Room 2/panel_h" | ||
| 5 | clue: "funny" | ||
| 6 | answer: "hilarious" | ||
| 7 | symbols: PYRAMID | ||
| 8 | } | ||
| diff --git a/data/maps/the_charismatic/rooms/Latitude North.txtpb b/data/maps/the_charismatic/rooms/Latitude North.txtpb new file mode 100644 index 0000000..50c412b --- /dev/null +++ b/data/maps/the_charismatic/rooms/Latitude North.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Latitude North" | ||
| 2 | panels { | ||
| 3 | name: "DEPENDABLE" | ||
| 4 | path: "Panels/Room 2/panel_r" | ||
| 5 | clue: "dependable" | ||
| 6 | answer: "reliable" | ||
| 7 | symbols: SUN | ||
| 8 | } | ||
| diff --git a/data/maps/the_charismatic/rooms/Latitude South.txtpb b/data/maps/the_charismatic/rooms/Latitude South.txtpb new file mode 100644 index 0000000..472e4a7 --- /dev/null +++ b/data/maps/the_charismatic/rooms/Latitude South.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Latitude South" | ||
| 2 | panels { | ||
| 3 | name: "CHARISMA" | ||
| 4 | path: "Panels/Room 2/panel_c" | ||
| 5 | clue: "charisma" | ||
| 6 | answer: "charismatic" | ||
| 7 | symbols: QUESTION | ||
| 8 | } | ||
| diff --git a/data/maps/the_charismatic/rooms/Longitude East.txtpb b/data/maps/the_charismatic/rooms/Longitude East.txtpb new file mode 100644 index 0000000..75cd6e0 --- /dev/null +++ b/data/maps/the_charismatic/rooms/Longitude East.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Longitude East" | ||
| 2 | panels { | ||
| 3 | name: "FUN" | ||
| 4 | path: "Panels/Room 2/panel_s2" | ||
| 5 | clue: "fun" | ||
| 6 | answer: "silly" | ||
| 7 | symbols: SUN | ||
| 8 | } | ||
| diff --git a/data/maps/the_charismatic/rooms/Longitude Middle.txtpb b/data/maps/the_charismatic/rooms/Longitude Middle.txtpb new file mode 100644 index 0000000..7ee8c11 --- /dev/null +++ b/data/maps/the_charismatic/rooms/Longitude Middle.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Longitude Middle" | ||
| 2 | panels { | ||
| 3 | name: "INTELLIGENT" | ||
| 4 | path: "Panels/Room 2/panel_s" | ||
| 5 | clue: "intelligent" | ||
| 6 | answer: "smart" | ||
| 7 | symbols: SUN | ||
| 8 | } | ||
| diff --git a/data/maps/the_charismatic/rooms/Longitude West.txtpb b/data/maps/the_charismatic/rooms/Longitude West.txtpb new file mode 100644 index 0000000..28fe8c8 --- /dev/null +++ b/data/maps/the_charismatic/rooms/Longitude West.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Longitude West" | ||
| 2 | panels { | ||
| 3 | name: "CREATIVE" | ||
| 4 | path: "Panels/Room 2/panel_i" | ||
| 5 | clue: "creative" | ||
| 6 | answer: "imaginative" | ||
| 7 | symbols: SUN | ||
| 8 | } | ||
| diff --git a/data/maps/the_charismatic/rooms/Main Area.txtpb b/data/maps/the_charismatic/rooms/Main Area.txtpb new file mode 100644 index 0000000..2d84000 --- /dev/null +++ b/data/maps/the_charismatic/rooms/Main Area.txtpb | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | name: "Main Area" | ||
| 2 | panels { | ||
| 3 | name: "ARC" | ||
| 4 | path: "Panels/Room_1/panel_1" | ||
| 5 | clue: "arc" | ||
| 6 | answer: "arctic" | ||
| 7 | symbols: QUESTION | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "KING" | ||
| 11 | path: "Panels/Room_1/panel_2" | ||
| 12 | clue: "king" | ||
| 13 | answer: "tacking" | ||
| 14 | symbols: QUESTION | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "TIP" | ||
| 18 | path: "Panels/Room_1/panel_3" | ||
| 19 | clue: "tip" | ||
| 20 | answer: "tiptoe" | ||
| 21 | symbols: QUESTION | ||
| 22 | } | ||
| 23 | panels { | ||
| 24 | name: "AQUA" | ||
| 25 | path: "Panels/Room_1/panel_4" | ||
| 26 | clue: "aqua" | ||
| 27 | answer: "aquatic" | ||
| 28 | symbols: QUESTION | ||
| 29 | } | ||
| 30 | panels { | ||
| 31 | name: "TILE" | ||
| 32 | path: "Panels/Room_1/panel_5" | ||
| 33 | clue: "tile" | ||
| 34 | answer: "tactile" | ||
| 35 | symbols: QUESTION | ||
| 36 | } | ||
| 37 | panels { | ||
| 38 | name: "NAIL" | ||
| 39 | path: "Panels/Room_1/panel_6" | ||
| 40 | clue: "nail" | ||
| 41 | answer: "toenail" | ||
| 42 | symbols: QUESTION | ||
| 43 | } | ||
| 44 | panels { | ||
| 45 | name: "HERE" | ||
| 46 | path: "Panels/Room_1/panel_7" | ||
| 47 | clue: "here" | ||
| 48 | answer: "heretic" | ||
| 49 | symbols: QUESTION | ||
| 50 | } | ||
| 51 | panels { | ||
| 52 | name: "IT" | ||
| 53 | path: "Panels/Room_1/panel_8" | ||
| 54 | clue: "it" | ||
| 55 | answer: "tacit" | ||
| 56 | symbols: QUESTION | ||
| 57 | } | ||
| 58 | panels { | ||
| 59 | name: "PINS" | ||
| 60 | path: "Panels/Room_1/panel_9" | ||
| 61 | clue: "pins" | ||
| 62 | answer: "pintoes" | ||
| 63 | symbols: QUESTION | ||
| 64 | } | ||
| 65 | panels { | ||
| 66 | name: "Blank" | ||
| 67 | path: "Panels/Room 3/panel_10" | ||
| 68 | clue: "" | ||
| 69 | answer: "tactic" | ||
| 70 | symbols: QUESTION | ||
| 71 | } | ||
| 72 | ports { | ||
| 73 | name: "WORLDPORT" | ||
| 74 | display_name: "Entrance" | ||
| 75 | path: "Components/Warps/worldport" | ||
| 76 | destination { x: 0 y: 0 z: 9.5 } | ||
| 77 | rotation: 0 | ||
| 78 | } | ||
| diff --git a/data/maps/the_charismatic/rooms/Mastery.txtpb b/data/maps/the_charismatic/rooms/Mastery.txtpb new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/data/maps/the_charismatic/rooms/Mastery.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mastery" | ||
| 2 | masteries { | ||
| 3 | name: "MASTERY" | ||
| 4 | path: "Components/Collectables/collectable" | ||
| 5 | } | ||
| diff --git a/data/maps/the_colorful/doors.txtpb b/data/maps/the_colorful/doors.txtpb index 003c9a9..3ce5f71 100644 --- a/data/maps/the_colorful/doors.txtpb +++ b/data/maps/the_colorful/doors.txtpb | |||
| @@ -1,4 +1,3 @@ | |||
| 1 | # TODO: gallery painting | ||
| 2 | doors { | 1 | doors { |
| 3 | name: "White Door" | 2 | name: "White Door" |
| 4 | type: STANDARD | 3 | type: STANDARD |
| @@ -104,3 +103,10 @@ doors { | |||
| 104 | panels { room: "Gray Room" name: "MEND" } | 103 | panels { room: "Gray Room" name: "MEND" } |
| 105 | location_room: "Gray Room" | 104 | location_room: "Gray Room" |
| 106 | } | 105 | } |
| 106 | doors { | ||
| 107 | name: "Chaos Panel" | ||
| 108 | type: LOCATION_ONLY | ||
| 109 | panels { room: "Cyan Hallway" name: "CHAOS" } | ||
| 110 | location_room: "Cyan Hallway" | ||
| 111 | location_name: "CHAOS" | ||
| 112 | } | ||
| diff --git a/data/maps/the_colorful/rooms/Cyan Hallway.txtpb b/data/maps/the_colorful/rooms/Cyan Hallway.txtpb index 97ddb0f..d94a0a6 100644 --- a/data/maps/the_colorful/rooms/Cyan Hallway.txtpb +++ b/data/maps/the_colorful/rooms/Cyan Hallway.txtpb | |||
| @@ -23,11 +23,15 @@ panels { | |||
| 23 | } | 23 | } |
| 24 | ports { | 24 | ports { |
| 25 | name: "STURDY" | 25 | name: "STURDY" |
| 26 | display_name: "North Cyan Worldport" | ||
| 26 | path: "Components/Warps/worldport3" | 27 | path: "Components/Warps/worldport3" |
| 27 | orientation: "west" | 28 | destination { x: -17 y: 0 z: -75 } |
| 29 | rotation: 270 | ||
| 28 | } | 30 | } |
| 29 | ports { | 31 | ports { |
| 30 | name: "DARKROOM" | 32 | name: "DARKROOM" |
| 33 | display_name: "South Cyan Worldport" | ||
| 31 | path: "Components/Warps/worldport2" | 34 | path: "Components/Warps/worldport2" |
| 32 | orientation: "west" | 35 | destination { x: -17 y: 0 z: -51 } |
| 36 | rotation: 270 | ||
| 33 | } | 37 | } |
| diff --git a/data/maps/the_colorful/rooms/White Room.txtpb b/data/maps/the_colorful/rooms/White Room.txtpb index c2cf33f..73557ed 100644 --- a/data/maps/the_colorful/rooms/White Room.txtpb +++ b/data/maps/the_colorful/rooms/White Room.txtpb | |||
| @@ -8,6 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "GREAT" | 10 | name: "GREAT" |
| 11 | display_name: "Main Entrance" | ||
| 11 | path: "Components/Warps/worldport" | 12 | path: "Components/Warps/worldport" |
| 12 | orientation: "west" | 13 | destination { x: -3.5 y: 0 z: 19 } |
| 14 | rotation: 270 | ||
| 13 | } | 15 | } |
| diff --git a/data/maps/the_congruent/doors.txtpb b/data/maps/the_congruent/doors.txtpb index 7c79c00..fab8d95 100644 --- a/data/maps/the_congruent/doors.txtpb +++ b/data/maps/the_congruent/doors.txtpb | |||
| @@ -1,12 +1,22 @@ | |||
| 1 | doors { | 1 | doors { |
| 2 | name: "Obverse Magenta Door" | 2 | name: "Obverse Magenta Door" |
| 3 | type: STANDARD | 3 | type: ITEM_ONLY |
| 4 | legacy_location: true | ||
| 4 | receivers: "Components/Doors/magenta_enterer2" | 5 | receivers: "Components/Doors/magenta_enterer2" |
| 5 | panels { room: "Main Area" name: "LAKE" } | 6 | panels { room: "Main Area" name: "LAKE" } |
| 6 | panels { room: "Main Area" name: "DIE" } | 7 | panels { room: "Main Area" name: "DIE" } |
| 7 | location_room: "Main Area" | 8 | location_room: "Main Area" |
| 8 | } | 9 | } |
| 9 | doors { | 10 | doors { |
| 11 | name: "Main Area Puzzles" | ||
| 12 | type: LOCATION_ONLY | ||
| 13 | panels { room: "Main Area" name: "LAKE" } | ||
| 14 | panels { room: "Main Area" name: "DIE" } | ||
| 15 | panels { room: "Main Area" name: "LIGHT" } | ||
| 16 | location_room: "Main Area" | ||
| 17 | location_name: "DIE, LAKE, LIGHT" | ||
| 18 | } | ||
| 19 | doors { | ||
| 10 | name: "Flipped Magenta Door" | 20 | name: "Flipped Magenta Door" |
| 11 | type: STANDARD | 21 | type: STANDARD |
| 12 | receivers: "Components/Doors/magenta_enterer" | 22 | receivers: "Components/Doors/magenta_enterer" |
| @@ -119,7 +129,11 @@ doors { | |||
| 119 | } | 129 | } |
| 120 | doors { | 130 | doors { |
| 121 | name: "T Keyholder Blocker" | 131 | name: "T Keyholder Blocker" |
| 122 | type: ITEM_ONLY | 132 | type: EVENT |
| 123 | receivers: "Components/Doors/magenta_enterer3" | 133 | receivers: "Components/Doors/magenta_enterer3" |
| 124 | switches: "lavender_cubes" | 134 | panels { |
| 135 | map: "the_ancient" | ||
| 136 | room: "Inside" | ||
| 137 | name: "COLOR" | ||
| 138 | } | ||
| 125 | } | 139 | } |
| diff --git a/data/maps/the_congruent/metadata.txtpb b/data/maps/the_congruent/metadata.txtpb index 16428c4..6260ed4 100644 --- a/data/maps/the_congruent/metadata.txtpb +++ b/data/maps/the_congruent/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Congruent" | 1 | display_name: "The Congruent" |
| 2 | worldport_entrance { | ||
| 3 | room: "Main Area" | ||
| 4 | name: "DARKROOM" | ||
| 5 | } | ||
| diff --git a/data/maps/the_congruent/rooms/C Keyholder.txtpb b/data/maps/the_congruent/rooms/C Keyholder.txtpb index 75ef920..d9a8cf2 100644 --- a/data/maps/the_congruent/rooms/C Keyholder.txtpb +++ b/data/maps/the_congruent/rooms/C Keyholder.txtpb | |||
| @@ -2,5 +2,4 @@ name: "C Keyholder" | |||
| 2 | keyholders { | 2 | keyholders { |
| 3 | name: "C" | 3 | name: "C" |
| 4 | path: "Components/KeyHolders/keyHolder" | 4 | path: "Components/KeyHolders/keyHolder" |
| 5 | # TODO: This will need to be modified so that it doesn't actually take the letter. | ||
| 6 | } | 5 | } |
| diff --git a/data/maps/the_congruent/rooms/G Keyholder.txtpb b/data/maps/the_congruent/rooms/G Keyholder.txtpb index 8184703..cd12419 100644 --- a/data/maps/the_congruent/rooms/G Keyholder.txtpb +++ b/data/maps/the_congruent/rooms/G Keyholder.txtpb | |||
| @@ -2,5 +2,4 @@ name: "G Keyholder" | |||
| 2 | keyholders { | 2 | keyholders { |
| 3 | name: "G" | 3 | name: "G" |
| 4 | path: "Components/KeyHolders/keyHolder2" | 4 | path: "Components/KeyHolders/keyHolder2" |
| 5 | # TODO: This will need to be modified so that it doesn't actually take the letter. | ||
| 6 | } | 5 | } |
| diff --git a/data/maps/the_congruent/rooms/Main Area.txtpb b/data/maps/the_congruent/rooms/Main Area.txtpb index e91f419..2b3f62e 100644 --- a/data/maps/the_congruent/rooms/Main Area.txtpb +++ b/data/maps/the_congruent/rooms/Main Area.txtpb | |||
| @@ -85,8 +85,10 @@ panels { | |||
| 85 | } | 85 | } |
| 86 | ports { | 86 | ports { |
| 87 | name: "DARKROOM" | 87 | name: "DARKROOM" |
| 88 | display_name: "Entrance" | ||
| 88 | path: "Components/Warps/worldport2" | 89 | path: "Components/Warps/worldport2" |
| 89 | orientation: "north" | 90 | destination { x: -19 y: 0 z: 7.5 } |
| 91 | rotation: 180 | ||
| 90 | } | 92 | } |
| 91 | paintings { | 93 | paintings { |
| 92 | name: "P" | 94 | name: "P" |
| diff --git a/data/maps/the_crystalline/connections.txtpb b/data/maps/the_crystalline/connections.txtpb new file mode 100644 index 0000000..131335a --- /dev/null +++ b/data/maps/the_crystalline/connections.txtpb | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | connections { | ||
| 2 | from_room: "Main Area" | ||
| 3 | to_room: "Painting Divot" | ||
| 4 | door { name: "Checkpoint Panels" } | ||
| 5 | oneway: true | ||
| 6 | } | ||
| 7 | connections { | ||
| 8 | from { | ||
| 9 | painting { | ||
| 10 | room: "Painting Divot" | ||
| 11 | name: "SNAKE" | ||
| 12 | } | ||
| 13 | } | ||
| 14 | to { | ||
| 15 | painting { | ||
| 16 | room: "Flip Area" | ||
| 17 | name: "SNAKE2" | ||
| 18 | } | ||
| 19 | } | ||
| 20 | oneway: true | ||
| 21 | } | ||
| 22 | connections { | ||
| 23 | from_room: "Flip Area" | ||
| 24 | to_room: "Mastery" | ||
| 25 | door { name: "Mastery" } | ||
| 26 | } | ||
| diff --git a/data/maps/the_crystalline/doors.txtpb b/data/maps/the_crystalline/doors.txtpb new file mode 100644 index 0000000..5930463 --- /dev/null +++ b/data/maps/the_crystalline/doors.txtpb | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | doors { | ||
| 2 | name: "Mastery" | ||
| 3 | type: EVENT | ||
| 4 | panels { room: "Flip Area" name: "SUCCEED" } | ||
| 5 | } | ||
| 6 | doors { | ||
| 7 | name: "Checkpoint Panels" | ||
| 8 | type: LOCATION_ONLY | ||
| 9 | panels { room: "Main Area" name: "DROP" } | ||
| 10 | panels { room: "Main Area" name: "LEAP" } | ||
| 11 | panels { room: "Main Area" name: "SPIN" } | ||
| 12 | location_room: "Main Area" | ||
| 13 | location_name: "DROP, LEAP, SPIN" | ||
| 14 | } | ||
| diff --git a/data/maps/the_crystalline/metadata.txtpb b/data/maps/the_crystalline/metadata.txtpb new file mode 100644 index 0000000..09b0f1d --- /dev/null +++ b/data/maps/the_crystalline/metadata.txtpb | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | display_name: "The Crystalline" | ||
| 2 | type: GIFT_MAP | ||
| 3 | # The map's mastery is created at runtime. | ||
| 4 | custom_nodes: "Components/Collectables/collectable" | ||
| diff --git a/data/maps/the_crystalline/rooms/Flip Area.txtpb b/data/maps/the_crystalline/rooms/Flip Area.txtpb new file mode 100644 index 0000000..3c6e3fd --- /dev/null +++ b/data/maps/the_crystalline/rooms/Flip Area.txtpb | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | name: "Flip Area" | ||
| 2 | panels { | ||
| 3 | name: "SUCCEED" | ||
| 4 | path: "Panels/Room_1/panel_3" | ||
| 5 | clue: "succeed" | ||
| 6 | answer: "win" | ||
| 7 | symbols: SUN | ||
| 8 | } | ||
| 9 | paintings { | ||
| 10 | name: "SNAKE2" | ||
| 11 | path: "Components/snake2" | ||
| 12 | exit_only: true | ||
| 13 | gravity: Y_PLUS | ||
| 14 | } | ||
| diff --git a/data/maps/the_crystalline/rooms/Main Area.txtpb b/data/maps/the_crystalline/rooms/Main Area.txtpb new file mode 100644 index 0000000..0b8d26c --- /dev/null +++ b/data/maps/the_crystalline/rooms/Main Area.txtpb | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | name: "Main Area" | ||
| 2 | panels { | ||
| 3 | name: "LEAP" | ||
| 4 | path: "Panels/Room_1/panel_1" | ||
| 5 | clue: "leap" | ||
| 6 | answer: "jump" | ||
| 7 | symbols: SUN | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "DROP" | ||
| 11 | path: "Panels/Room_1/panel_2" | ||
| 12 | clue: "drop" | ||
| 13 | answer: "fall" | ||
| 14 | symbols: SUN | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "SPIN" | ||
| 18 | path: "Panels/Room_1/panel_4" | ||
| 19 | clue: "spin" | ||
| 20 | answer: "flip" | ||
| 21 | symbols: SUN | ||
| 22 | } | ||
| 23 | ports { | ||
| 24 | name: "WORLDPORT" | ||
| 25 | display_name: "Entrance" | ||
| 26 | path: "Components/Warps/worldport" | ||
| 27 | destination { x: 0 y: 0 z: 9.5 } | ||
| 28 | rotation: 0 | ||
| 29 | } | ||
| diff --git a/data/maps/the_crystalline/rooms/Mastery.txtpb b/data/maps/the_crystalline/rooms/Mastery.txtpb new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/data/maps/the_crystalline/rooms/Mastery.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mastery" | ||
| 2 | masteries { | ||
| 3 | name: "MASTERY" | ||
| 4 | path: "Components/Collectables/collectable" | ||
| 5 | } | ||
| diff --git a/data/maps/the_crystalline/rooms/Painting Divot.txtpb b/data/maps/the_crystalline/rooms/Painting Divot.txtpb new file mode 100644 index 0000000..ab9a132 --- /dev/null +++ b/data/maps/the_crystalline/rooms/Painting Divot.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Painting Divot" | ||
| 2 | paintings { | ||
| 3 | name: "SNAKE" | ||
| 4 | path: "Components/snake" | ||
| 5 | } | ||
| diff --git a/data/maps/the_darkroom/connections.txtpb b/data/maps/the_darkroom/connections.txtpb index 4093585..43bca70 100644 --- a/data/maps/the_darkroom/connections.txtpb +++ b/data/maps/the_darkroom/connections.txtpb | |||
| @@ -1,7 +1,12 @@ | |||
| 1 | connections { | 1 | connections { |
| 2 | from_room: "First Room" | ||
| 3 | to_room: "First Room Exit" | ||
| 4 | door { name: "Second Room Entrance" } | ||
| 5 | } | ||
| 6 | connections { | ||
| 2 | from { | 7 | from { |
| 3 | port { | 8 | port { |
| 4 | room: "First Room" | 9 | room: "First Room Exit" |
| 5 | name: "NEXT" | 10 | name: "NEXT" |
| 6 | } | 11 | } |
| 7 | } | 12 | } |
| @@ -14,9 +19,14 @@ connections { | |||
| 14 | oneway: true | 19 | oneway: true |
| 15 | } | 20 | } |
| 16 | connections { | 21 | connections { |
| 22 | from_room: "Second Room" | ||
| 23 | to_room: "Second Room Exit" | ||
| 24 | door { name: "Third Room Entrance" } | ||
| 25 | } | ||
| 26 | connections { | ||
| 17 | from { | 27 | from { |
| 18 | port { | 28 | port { |
| 19 | room: "Second Room" | 29 | room: "Second Room Exit" |
| 20 | name: "NEXT" | 30 | name: "NEXT" |
| 21 | } | 31 | } |
| 22 | } | 32 | } |
| @@ -33,3 +43,18 @@ connections { | |||
| 33 | to_room: "S Room" | 43 | to_room: "S Room" |
| 34 | door { name: "S1 Door" } | 44 | door { name: "S1 Door" } |
| 35 | } | 45 | } |
| 46 | connections { | ||
| 47 | from_room: "First Room" | ||
| 48 | to_room: "Cyan Hallway" | ||
| 49 | door { name: "Colorful Entrance" } | ||
| 50 | } | ||
| 51 | connections { | ||
| 52 | from_room: "Second Room" | ||
| 53 | to_room: "Congruent Entrance" | ||
| 54 | door { name: "Congruent Entrance" } | ||
| 55 | } | ||
| 56 | connections { | ||
| 57 | from_room: "First Room" | ||
| 58 | to_room: "Double Sided Entrance" | ||
| 59 | door { name: "Double Sided Entrance" } | ||
| 60 | } | ||
| diff --git a/data/maps/the_darkroom/doors.txtpb b/data/maps/the_darkroom/doors.txtpb index 047c7d0..c4a47a0 100644 --- a/data/maps/the_darkroom/doors.txtpb +++ b/data/maps/the_darkroom/doors.txtpb | |||
| @@ -1,4 +1,3 @@ | |||
| 1 | # TODO: gallery painting | ||
| 2 | doors { | 1 | doors { |
| 3 | name: "Double Letter Panel Blockers" | 2 | name: "Double Letter Panel Blockers" |
| 4 | type: EVENT | 3 | type: EVENT |
| 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..e6600a2 --- /dev/null +++ b/data/maps/the_darkroom/rooms/Congruent Entrance.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Congruent Entrance" | ||
| 2 | panel_display_name: "Second Room" | ||
| 3 | ports { | ||
| 4 | name: "CONGRUENT" | ||
| 5 | display_name: "Second Room Gray Hallway" | ||
| 6 | path: "Components/Warps/worldport7" | ||
| 7 | destination { x: 51.5 y: 0 z: 29 } | ||
| 8 | rotation: 90 | ||
| 9 | } | ||
| 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..bce0e5b --- /dev/null +++ b/data/maps/the_darkroom/rooms/Cyan Hallway.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Cyan Hallway" | ||
| 2 | panel_display_name: "First Room" | ||
| 3 | ports { | ||
| 4 | name: "COLORFUL" | ||
| 5 | display_name: "First Room Cyan Hallway" | ||
| 6 | path: "Components/Warps/worldport8" | ||
| 7 | destination { x: 20 y: 0 z: -12 } | ||
| 8 | rotation: 180 | ||
| 9 | } | ||
| 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..79ca839 --- /dev/null +++ b/data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Double Sided Entrance" | ||
| 2 | panel_display_name: "First Room" | ||
| 3 | ports { | ||
| 4 | name: "DOUBLESIDED" | ||
| 5 | display_name: "First Room White Hallway" | ||
| 6 | path: "Components/Warps/worldport6" | ||
| 7 | destination { x: 15 y: 0 z: 23 } | ||
| 8 | rotation: 90 | ||
| 9 | } | ||
| diff --git a/data/maps/the_darkroom/rooms/First Room Exit.txtpb b/data/maps/the_darkroom/rooms/First Room Exit.txtpb new file mode 100644 index 0000000..4a7ebc2 --- /dev/null +++ b/data/maps/the_darkroom/rooms/First Room Exit.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "First Room Exit" | ||
| 2 | panel_display_name: "First Room" | ||
| 3 | ports { | ||
| 4 | name: "NEXT" | ||
| 5 | display_name: "First Room Exit" | ||
| 6 | path: "Components/Warps/worldport2" | ||
| 7 | destination { x: 0 y: 0 z: -15 } | ||
| 8 | rotation: 180 | ||
| 9 | } | ||
| diff --git a/data/maps/the_darkroom/rooms/First Room.txtpb b/data/maps/the_darkroom/rooms/First Room.txtpb index c93f5b4..1113435 100644 --- a/data/maps/the_darkroom/rooms/First Room.txtpb +++ b/data/maps/the_darkroom/rooms/First Room.txtpb | |||
| @@ -33,24 +33,8 @@ panels { | |||
| 33 | } | 33 | } |
| 34 | ports { | 34 | ports { |
| 35 | name: "ENTRY" | 35 | name: "ENTRY" |
| 36 | display_name: "First Room Entrance" | ||
| 36 | path: "Components/Warps/worldport" | 37 | path: "Components/Warps/worldport" |
| 37 | orientation: "south" | 38 | destination { x: -10 y: 0 z: 10 } |
| 38 | } | 39 | rotation: 0 |
| 39 | ports { | ||
| 40 | name: "NEXT" | ||
| 41 | path: "Components/Warps/worldport2" | ||
| 42 | orientation: "north" | ||
| 43 | required_door { name: "Second Room Entrance" } | ||
| 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 | } | 40 | } |
| diff --git a/data/maps/the_darkroom/rooms/Second Room Exit.txtpb b/data/maps/the_darkroom/rooms/Second Room Exit.txtpb new file mode 100644 index 0000000..d500691 --- /dev/null +++ b/data/maps/the_darkroom/rooms/Second Room Exit.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Second Room Exit" | ||
| 2 | panel_display_name: "Second Room" | ||
| 3 | ports { | ||
| 4 | name: "NEXT" | ||
| 5 | display_name: "Second Room Exit" | ||
| 6 | path: "Components/Warps/worldport4" | ||
| 7 | destination { x: 48 y: 0 z: -15 } | ||
| 8 | rotation: 180 | ||
| 9 | } | ||
| diff --git a/data/maps/the_darkroom/rooms/Second Room.txtpb b/data/maps/the_darkroom/rooms/Second Room.txtpb index baeea12..2219895 100644 --- a/data/maps/the_darkroom/rooms/Second Room.txtpb +++ b/data/maps/the_darkroom/rooms/Second Room.txtpb | |||
| @@ -38,18 +38,8 @@ panels { | |||
| 38 | } | 38 | } |
| 39 | ports { | 39 | ports { |
| 40 | name: "ENTRY" | 40 | name: "ENTRY" |
| 41 | display_name: "Second Room Entrance" | ||
| 41 | path: "Components/Warps/worldport3" | 42 | path: "Components/Warps/worldport3" |
| 42 | orientation: "south" | 43 | destination { x: 38 y: 0 z: 10 } |
| 43 | } | 44 | rotation: 0 |
| 44 | ports { | ||
| 45 | name: "NEXT" | ||
| 46 | path: "Components/Warps/worldport4" | ||
| 47 | orientation: "north" | ||
| 48 | required_door { name: "Third Room Entrance" } | ||
| 49 | } | ||
| 50 | ports { | ||
| 51 | name: "CONGRUENT" | ||
| 52 | path: "Components/Warps/worldport7" | ||
| 53 | orientation: "east" | ||
| 54 | required_door { name: "Congruent Entrance" } | ||
| 55 | } | 45 | } |
| diff --git a/data/maps/the_darkroom/rooms/Third Room.txtpb b/data/maps/the_darkroom/rooms/Third Room.txtpb index fc80fa7..0400476 100644 --- a/data/maps/the_darkroom/rooms/Third Room.txtpb +++ b/data/maps/the_darkroom/rooms/Third Room.txtpb | |||
| @@ -65,6 +65,8 @@ panels { | |||
| 65 | } | 65 | } |
| 66 | ports { | 66 | ports { |
| 67 | name: "ENTRY" | 67 | name: "ENTRY" |
| 68 | display_name: "Third Room Entrance" | ||
| 68 | path: "Components/Warps/worldport5" | 69 | path: "Components/Warps/worldport5" |
| 69 | orientation: "south" | 70 | destination { x: 97 y: 0 z: 10 } |
| 71 | rotation: 0 | ||
| 70 | } | 72 | } |
| diff --git a/data/maps/the_digital/connections.txtpb b/data/maps/the_digital/connections.txtpb index 67cd4dc..a4b02a5 100644 --- a/data/maps/the_digital/connections.txtpb +++ b/data/maps/the_digital/connections.txtpb | |||
| @@ -24,11 +24,6 @@ connections { | |||
| 24 | door { name: "Gallery Entrance" } | 24 | door { name: "Gallery Entrance" } |
| 25 | } | 25 | } |
| 26 | connections { | 26 | connections { |
| 27 | from_room: "Gallery Maze" | ||
| 28 | to_room: "Main Area" | ||
| 29 | oneway: true | ||
| 30 | } | ||
| 31 | connections { | ||
| 32 | from_room: "Tree Area" | 27 | from_room: "Tree Area" |
| 33 | to_room: "Main Area" | 28 | to_room: "Main Area" |
| 34 | door { name: "Tree Entrance" } | 29 | door { name: "Tree Entrance" } |
| diff --git a/data/maps/the_digital/doors.txtpb b/data/maps/the_digital/doors.txtpb index 3a2e381..6c56c86 100644 --- a/data/maps/the_digital/doors.txtpb +++ b/data/maps/the_digital/doors.txtpb | |||
| @@ -42,6 +42,7 @@ doors { | |||
| 42 | doors { | 42 | doors { |
| 43 | name: "Control Center Blue Door" | 43 | name: "Control Center Blue Door" |
| 44 | type: CONTROL_CENTER_COLOR | 44 | type: CONTROL_CENTER_COLOR |
| 45 | latch: true | ||
| 45 | receivers: "Components/Doors/maze2" | 46 | receivers: "Components/Doors/maze2" |
| 46 | control_center_color: "blue" | 47 | control_center_color: "blue" |
| 47 | } | 48 | } |
| @@ -52,3 +53,10 @@ doors { | |||
| 52 | panels { room: "Tree Area" name: "TREE" } | 53 | panels { room: "Tree Area" name: "TREE" } |
| 53 | location_room: "Tree Area" | 54 | location_room: "Tree Area" |
| 54 | } | 55 | } |
| 56 | doors { | ||
| 57 | name: "Control Center Blue Panel" | ||
| 58 | type: LOCATION_ONLY | ||
| 59 | panels { room: "Main Area" name: "COLOR" } | ||
| 60 | location_room: "Main Area" | ||
| 61 | location_name: "COLOR" | ||
| 62 | } | ||
| diff --git a/data/maps/the_digital/rooms/Gallery Maze.txtpb b/data/maps/the_digital/rooms/Gallery Maze.txtpb index bfdfa41..31fa98d 100644 --- a/data/maps/the_digital/rooms/Gallery Maze.txtpb +++ b/data/maps/the_digital/rooms/Gallery Maze.txtpb | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | name: "Gallery Maze" | 1 | name: "Gallery Maze" |
| 2 | ports { | 2 | ports { |
| 3 | name: "GALLERY" | 3 | name: "GALLERY" |
| 4 | display_name: "Gallery Maze Worldport" | ||
| 4 | path: "Components/Warps/worldport4" | 5 | path: "Components/Warps/worldport4" |
| 5 | orientation: "east" | 6 | destination { x: -58 y: 0 z: -76 } |
| 7 | rotation: 90 | ||
| 6 | } | 8 | } |
| diff --git a/data/maps/the_digital/rooms/Main Area.txtpb b/data/maps/the_digital/rooms/Main Area.txtpb index 99bcdcc..26770c2 100644 --- a/data/maps/the_digital/rooms/Main Area.txtpb +++ b/data/maps/the_digital/rooms/Main Area.txtpb | |||
| @@ -27,16 +27,22 @@ panels { | |||
| 27 | } | 27 | } |
| 28 | ports { | 28 | ports { |
| 29 | name: "ENTRY1" | 29 | name: "ENTRY1" |
| 30 | display_name: "Maze NW Worldport" | ||
| 30 | path: "Components/Worldports/worldport3" | 31 | path: "Components/Worldports/worldport3" |
| 31 | orientation: "west" | 32 | destination { x: -33 y: 0 z: 28 } |
| 33 | rotation: 270 | ||
| 32 | } | 34 | } |
| 33 | ports { | 35 | ports { |
| 34 | name: "ENTRY2" | 36 | name: "ENTRY2" |
| 37 | display_name: "Maze SW Worldport" | ||
| 35 | path: "Components/Worldports/worldport" | 38 | path: "Components/Worldports/worldport" |
| 36 | orientation: "south" | 39 | destination { x: -30 y: 0 z: 51 } |
| 40 | rotation: 0 | ||
| 37 | } | 41 | } |
| 38 | ports { | 42 | ports { |
| 39 | name: "ENTRY3" | 43 | name: "ENTRY3" |
| 44 | display_name: "Maze SE Worldport" | ||
| 40 | path: "Components/Worldports/worldport2" | 45 | path: "Components/Worldports/worldport2" |
| 41 | orientation: "south" | 46 | destination { x: 0 y: 0 z: 51 } |
| 47 | rotation: 0 | ||
| 42 | } | 48 | } |
| diff --git a/data/maps/the_digital/rooms/Tree Area.txtpb b/data/maps/the_digital/rooms/Tree Area.txtpb index 56301d5..c2dc6b9 100644 --- a/data/maps/the_digital/rooms/Tree Area.txtpb +++ b/data/maps/the_digital/rooms/Tree Area.txtpb | |||
| @@ -8,7 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "TREE" | 10 | name: "TREE" |
| 11 | display_name: "Brown Hallway" | ||
| 11 | path: "Components/Worldports/worldport4" | 12 | path: "Components/Worldports/worldport4" |
| 12 | orientation: "east" | 13 | destination { x: -16 y: 0 z: -31 } |
| 13 | # This is double sided. | 14 | rotation: 270 |
| 14 | } | 15 | } |
| diff --git a/data/maps/the_digital/rooms/Unyielding Entrance.txtpb b/data/maps/the_digital/rooms/Unyielding Entrance.txtpb index 0370928..f4bc663 100644 --- a/data/maps/the_digital/rooms/Unyielding Entrance.txtpb +++ b/data/maps/the_digital/rooms/Unyielding Entrance.txtpb | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | name: "Unyielding Entrance" | 1 | name: "Unyielding Entrance" |
| 2 | ports { | 2 | ports { |
| 3 | name: "UNYIELDING" | 3 | name: "UNYIELDING" |
| 4 | display_name: "Blue Door Worldport" | ||
| 4 | path: "Components/Warps/worldport5" | 5 | path: "Components/Warps/worldport5" |
| 5 | orientation: "east" | 6 | destination { x: 14 y: 0 z: 5 } |
| 7 | rotation: 90 | ||
| 6 | } | 8 | } |
| diff --git a/data/maps/the_double_sided/doors.txtpb b/data/maps/the_double_sided/doors.txtpb index 02b113a..1ae4324 100644 --- a/data/maps/the_double_sided/doors.txtpb +++ b/data/maps/the_double_sided/doors.txtpb | |||
| @@ -113,3 +113,82 @@ doors { | |||
| 113 | # The panel blocks your way; there's no door. | 113 | # The panel blocks your way; there's no door. |
| 114 | panels { room: "Flipped Black Area" name: "SEAPLANE" } | 114 | panels { room: "Flipped Black Area" name: "SEAPLANE" } |
| 115 | } | 115 | } |
| 116 | # These locations are kind of deranged but hey. Welcome to The Double Sided. | ||
| 117 | doors { | ||
| 118 | name: "5 Panels" | ||
| 119 | type: LOCATION_ONLY | ||
| 120 | panels { room: "Flipped Black Area" name: "SEAPLANE" } | ||
| 121 | panels { room: "Flipped Blue Area" name: "SKY" } | ||
| 122 | panels { room: "Flipped Blue Area" name: "HEAD" } | ||
| 123 | panels { room: "Flipped Green Area" name: "HIGH" } | ||
| 124 | panels { room: "Flipped Orange Area" name: "HEAVEN" } | ||
| 125 | panels { room: "Flipped Purple Area" name: "CEILING" } | ||
| 126 | panels { room: "Flipped Purple Area" name: "LEAVES" } | ||
| 127 | panels { room: "Flipped Red Area" name: "RAISED" } | ||
| 128 | panels { room: "Flipped Yellow Back Area" name: "ANGELS" } | ||
| 129 | panels { room: "Obverse Black Area" name: "MOUNTAIN" } | ||
| 130 | panels { room: "Obverse Black Area" name: "TRAIN" } | ||
| 131 | panels { room: "Obverse Green Area" name: "UPSIDE" } | ||
| 132 | panels { room: "Obverse Orange Back Area" name: "OVER" } | ||
| 133 | panels { room: "Obverse Orange Front Area" name: "UP" } | ||
| 134 | panels { room: "Obverse Orange Isolated Section" name: "TOP" } | ||
| 135 | panels { room: "Obverse Pink Area" name: "CLOUD" } | ||
| 136 | panels { room: "Obverse Purple Area" name: "DRAGON" } | ||
| 137 | panels { room: "Obverse Purple Area" name: "ABOVE" } | ||
| 138 | panels { room: "Start" name: "ATTIC" } | ||
| 139 | panels { room: "Start" name: "FULL" } | ||
| 140 | location_room: "Start" | ||
| 141 | complete_at: 5 | ||
| 142 | } | ||
| 143 | doors { | ||
| 144 | name: "10 Panels" | ||
| 145 | type: LOCATION_ONLY | ||
| 146 | panels { room: "Flipped Black Area" name: "SEAPLANE" } | ||
| 147 | panels { room: "Flipped Blue Area" name: "SKY" } | ||
| 148 | panels { room: "Flipped Blue Area" name: "HEAD" } | ||
| 149 | panels { room: "Flipped Green Area" name: "HIGH" } | ||
| 150 | panels { room: "Flipped Orange Area" name: "HEAVEN" } | ||
| 151 | panels { room: "Flipped Purple Area" name: "CEILING" } | ||
| 152 | panels { room: "Flipped Purple Area" name: "LEAVES" } | ||
| 153 | panels { room: "Flipped Red Area" name: "RAISED" } | ||
| 154 | panels { room: "Flipped Yellow Back Area" name: "ANGELS" } | ||
| 155 | panels { room: "Obverse Black Area" name: "MOUNTAIN" } | ||
| 156 | panels { room: "Obverse Black Area" name: "TRAIN" } | ||
| 157 | panels { room: "Obverse Green Area" name: "UPSIDE" } | ||
| 158 | panels { room: "Obverse Orange Back Area" name: "OVER" } | ||
| 159 | panels { room: "Obverse Orange Front Area" name: "UP" } | ||
| 160 | panels { room: "Obverse Orange Isolated Section" name: "TOP" } | ||
| 161 | panels { room: "Obverse Pink Area" name: "CLOUD" } | ||
| 162 | panels { room: "Obverse Purple Area" name: "DRAGON" } | ||
| 163 | panels { room: "Obverse Purple Area" name: "ABOVE" } | ||
| 164 | panels { room: "Start" name: "ATTIC" } | ||
| 165 | panels { room: "Start" name: "FULL" } | ||
| 166 | location_room: "Start" | ||
| 167 | complete_at: 10 | ||
| 168 | } | ||
| 169 | doors { | ||
| 170 | name: "15 Panels" | ||
| 171 | type: LOCATION_ONLY | ||
| 172 | panels { room: "Flipped Black Area" name: "SEAPLANE" } | ||
| 173 | panels { room: "Flipped Blue Area" name: "SKY" } | ||
| 174 | panels { room: "Flipped Blue Area" name: "HEAD" } | ||
| 175 | panels { room: "Flipped Green Area" name: "HIGH" } | ||
| 176 | panels { room: "Flipped Orange Area" name: "HEAVEN" } | ||
| 177 | panels { room: "Flipped Purple Area" name: "CEILING" } | ||
| 178 | panels { room: "Flipped Purple Area" name: "LEAVES" } | ||
| 179 | panels { room: "Flipped Red Area" name: "RAISED" } | ||
| 180 | panels { room: "Flipped Yellow Back Area" name: "ANGELS" } | ||
| 181 | panels { room: "Obverse Black Area" name: "MOUNTAIN" } | ||
| 182 | panels { room: "Obverse Black Area" name: "TRAIN" } | ||
| 183 | panels { room: "Obverse Green Area" name: "UPSIDE" } | ||
| 184 | panels { room: "Obverse Orange Back Area" name: "OVER" } | ||
| 185 | panels { room: "Obverse Orange Front Area" name: "UP" } | ||
| 186 | panels { room: "Obverse Orange Isolated Section" name: "TOP" } | ||
| 187 | panels { room: "Obverse Pink Area" name: "CLOUD" } | ||
| 188 | panels { room: "Obverse Purple Area" name: "DRAGON" } | ||
| 189 | panels { room: "Obverse Purple Area" name: "ABOVE" } | ||
| 190 | panels { room: "Start" name: "ATTIC" } | ||
| 191 | panels { room: "Start" name: "FULL" } | ||
| 192 | location_room: "Start" | ||
| 193 | complete_at: 15 | ||
| 194 | } | ||
| diff --git a/data/maps/the_double_sided/metadata.txtpb b/data/maps/the_double_sided/metadata.txtpb index c354fd8..6f56c63 100644 --- a/data/maps/the_double_sided/metadata.txtpb +++ b/data/maps/the_double_sided/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Double Sided" | 1 | display_name: "The Double Sided" |
| 2 | worldport_entrance { | ||
| 3 | room: "Start" | ||
| 4 | name: "DARKROOM" | ||
| 5 | } | ||
| diff --git a/data/maps/the_double_sided/rooms/Start.txtpb b/data/maps/the_double_sided/rooms/Start.txtpb index b0bfbcd..54efb93 100644 --- a/data/maps/the_double_sided/rooms/Start.txtpb +++ b/data/maps/the_double_sided/rooms/Start.txtpb | |||
| @@ -15,6 +15,8 @@ panels { | |||
| 15 | } | 15 | } |
| 16 | ports { | 16 | ports { |
| 17 | name: "DARKROOM" | 17 | name: "DARKROOM" |
| 18 | display_name: "Entrance" | ||
| 18 | path: "Components/Warps/worldport" | 19 | path: "Components/Warps/worldport" |
| 19 | orientation: "west" | 20 | destination { x: -3 y: 0 z: 8 } |
| 21 | rotation: 270 | ||
| 20 | } | 22 | } |
| diff --git a/data/maps/the_entry/connections.txtpb b/data/maps/the_entry/connections.txtpb index a2e325a..6260665 100644 --- a/data/maps/the_entry/connections.txtpb +++ b/data/maps/the_entry/connections.txtpb | |||
| @@ -64,6 +64,16 @@ connections { | |||
| 64 | door { name: "Flipped Pyramid Area Entrance" } | 64 | door { name: "Flipped Pyramid Area Entrance" } |
| 65 | } | 65 | } |
| 66 | connections { | 66 | connections { |
| 67 | from_room: "Flipped Pyramid Area" | ||
| 68 | to_room: "Liberated Entrance Panel" | ||
| 69 | oneway: true | ||
| 70 | } | ||
| 71 | connections { | ||
| 72 | from_room: "Flipped Pyramid Area" | ||
| 73 | to_room: "Literate Entrance Panel" | ||
| 74 | oneway: true | ||
| 75 | } | ||
| 76 | connections { | ||
| 67 | from_room: "Right Eye" | 77 | from_room: "Right Eye" |
| 68 | to_room: "Least Blue Last" | 78 | to_room: "Least Blue Last" |
| 69 | door { name: "Red Blue Area Left Door" } | 79 | door { name: "Red Blue Area Left Door" } |
| @@ -97,6 +107,12 @@ connections { | |||
| 97 | from_room: "Red Blue Halls" | 107 | from_room: "Red Blue Halls" |
| 98 | to_room: "Wrath Room" | 108 | to_room: "Wrath Room" |
| 99 | door { name: "Noon Door" } | 109 | door { name: "Noon Door" } |
| 110 | oneway: true | ||
| 111 | } | ||
| 112 | connections { | ||
| 113 | from_room: "Wrath Room" | ||
| 114 | to_room: "Least Blue Last" | ||
| 115 | oneway: true | ||
| 100 | } | 116 | } |
| 101 | connections { | 117 | connections { |
| 102 | from_room: "Red Blue Halls" | 118 | from_room: "Red Blue Halls" |
| @@ -154,6 +170,11 @@ connections { | |||
| 154 | door { name: "Lime Room Entrance" } | 170 | door { name: "Lime Room Entrance" } |
| 155 | } | 171 | } |
| 156 | connections { | 172 | connections { |
| 173 | from_room: "Lime Room" | ||
| 174 | to_room: "Revitalized Entrance" | ||
| 175 | door { name: "Revitalized Entrance" } | ||
| 176 | } | ||
| 177 | connections { | ||
| 157 | from { painting { room: "Link Area" name: "NEAR" } } | 178 | from { painting { room: "Link Area" name: "NEAR" } } |
| 158 | to { painting { room: "Flipped Link Area" name: "NEAR" } } | 179 | to { painting { room: "Flipped Link Area" name: "NEAR" } } |
| 159 | oneway: true | 180 | oneway: true |
| @@ -172,12 +193,12 @@ connections { | |||
| 172 | from_room: "Starting Room" | 193 | from_room: "Starting Room" |
| 173 | to_room: "Digital Entrance" | 194 | to_room: "Digital Entrance" |
| 174 | door { name: "Second Room Left Door" } | 195 | door { name: "Second Room Left Door" } |
| 175 | oneway: true | ||
| 176 | } | 196 | } |
| 177 | connections { | 197 | connections { |
| 178 | from_room: "Digital Entrance" | 198 | from_room: "Digital Entrance" |
| 179 | to_room: "Starting Room" | 199 | to_room: "Starting Room" |
| 180 | oneway: true | 200 | oneway: true |
| 201 | vanilla_only: true | ||
| 181 | } | 202 | } |
| 182 | connections { | 203 | connections { |
| 183 | from_room: "Starting Room" | 204 | from_room: "Starting Room" |
| @@ -199,3 +220,28 @@ connections { | |||
| 199 | to_room: "White Hallway To Daedalus" | 220 | to_room: "White Hallway To Daedalus" |
| 200 | door { name: "Control Center White Door" } | 221 | door { name: "Control Center White Door" } |
| 201 | } | 222 | } |
| 223 | connections { | ||
| 224 | from_room: "Flipped Second Room" | ||
| 225 | to_room: "Four Rooms Entrance" | ||
| 226 | door { name: "Flipped Second Room Right Door" } | ||
| 227 | } | ||
| 228 | connections { | ||
| 229 | from_room: "Link Area" | ||
| 230 | to_room: "Liberated Entrance" | ||
| 231 | door { name: "Liberated Entrance" } | ||
| 232 | } | ||
| 233 | connections { | ||
| 234 | from_room: "Link Area" | ||
| 235 | to_room: "Literate Entrance" | ||
| 236 | door { name: "Literate Entrance" } | ||
| 237 | } | ||
| 238 | connections { | ||
| 239 | from_room: "Liberated Entrance" | ||
| 240 | to_room: "Liberated Entrance Panel" | ||
| 241 | oneway: true | ||
| 242 | } | ||
| 243 | connections { | ||
| 244 | from_room: "Literate Entrance" | ||
| 245 | to_room: "Literate Entrance Panel" | ||
| 246 | oneway: true | ||
| 247 | } | ||
| diff --git a/data/maps/the_entry/doors.txtpb b/data/maps/the_entry/doors.txtpb index 6bef160..3f62338 100644 --- a/data/maps/the_entry/doors.txtpb +++ b/data/maps/the_entry/doors.txtpb | |||
| @@ -69,20 +69,40 @@ doors { | |||
| 69 | # second_right is vanilla because it's like LOST door. | 69 | # second_right is vanilla because it's like LOST door. |
| 70 | doors { | 70 | doors { |
| 71 | name: "Noon Door" | 71 | name: "Noon Door" |
| 72 | type: STANDARD | 72 | type: ITEM_ONLY |
| 73 | legacy_location: true | ||
| 73 | receivers: "Components/Doors/second_right5" | 74 | receivers: "Components/Doors/second_right5" |
| 74 | receivers: "Components/Doors/second_right10" | 75 | receivers: "Components/Doors/second_right10" |
| 75 | panels { room: "Red Blue Halls" name: "CENTER DAY" } | 76 | panels { room: "Red Blue Halls" name: "CENTER DAY" } |
| 76 | location_room: "Red Blue Halls" | 77 | location_room: "Red Blue Halls" |
| 77 | } | 78 | } |
| 78 | doors { | 79 | doors { |
| 80 | name: "Noon Door Panels" | ||
| 81 | type: LOCATION_ONLY | ||
| 82 | panels { room: "Red Blue Halls" name: "CENTER" } | ||
| 83 | panels { room: "Red Blue Halls" name: "DAY" } | ||
| 84 | panels { room: "Red Blue Halls" name: "CENTER DAY" } | ||
| 85 | location_room: "Red Blue Halls" | ||
| 86 | location_name: "CENTER, DAY, CENTER DAY" | ||
| 87 | } | ||
| 88 | doors { | ||
| 79 | name: "Scarf Door" | 89 | name: "Scarf Door" |
| 80 | type: STANDARD | 90 | type: ITEM_ONLY |
| 91 | legacy_location: true | ||
| 81 | receivers: "Components/Doors/second_right6" | 92 | receivers: "Components/Doors/second_right6" |
| 82 | panels { room: "Red Blue Halls" name: "RAIN WOMAN" } | 93 | panels { room: "Red Blue Halls" name: "RAIN WOMAN" } |
| 83 | location_room: "Red Blue Halls" | 94 | location_room: "Red Blue Halls" |
| 84 | } | 95 | } |
| 85 | doors { | 96 | doors { |
| 97 | name: "Scarf Door Panels" | ||
| 98 | type: LOCATION_ONLY | ||
| 99 | panels { room: "Red Blue Halls" name: "RAIN" } | ||
| 100 | panels { room: "Red Blue Halls" name: "WOMAN" } | ||
| 101 | panels { room: "Red Blue Halls" name: "RAIN WOMAN" } | ||
| 102 | location_room: "Red Blue Halls" | ||
| 103 | location_name: "RAIN, WOMAN, RAIN WOMAN" | ||
| 104 | } | ||
| 105 | doors { | ||
| 86 | name: "Blue Alcove Entrance" | 106 | name: "Blue Alcove Entrance" |
| 87 | type: STANDARD | 107 | type: STANDARD |
| 88 | receivers: "Components/Doors/second_right9" | 108 | receivers: "Components/Doors/second_right9" |
| @@ -119,7 +139,8 @@ doors { | |||
| 119 | } | 139 | } |
| 120 | doors { | 140 | doors { |
| 121 | name: "Red Blue Area Left Door" | 141 | name: "Red Blue Area Left Door" |
| 122 | type: STANDARD | 142 | type: ITEM_ONLY |
| 143 | legacy_location: true | ||
| 123 | receivers: "Components/Doors/fourth_right" | 144 | receivers: "Components/Doors/fourth_right" |
| 124 | panels { room: "Right Eye" name: "WANDER" } | 145 | panels { room: "Right Eye" name: "WANDER" } |
| 125 | location_room: "Right Eye" | 146 | location_room: "Right Eye" |
| @@ -131,14 +152,41 @@ doors { | |||
| 131 | panels { room: "Right Eye" name: "WANDER" } | 152 | panels { room: "Right Eye" name: "WANDER" } |
| 132 | location_room: "Right Eye" | 153 | location_room: "Right Eye" |
| 133 | } | 154 | } |
| 155 | doors { | ||
| 156 | name: "Wander Panels" | ||
| 157 | type: LOCATION_ONLY | ||
| 158 | panels { room: "Right Eye" name: "WANDER" } | ||
| 159 | panels { room: "Red Blue Halls" name: "WANDER" } | ||
| 160 | panels { room: "Link Area" name: "WANDER" } | ||
| 161 | panels { room: "Flipped Link Area" name: "WANDER" } | ||
| 162 | location_room: "Flipped Link Area" | ||
| 163 | } | ||
| 164 | doors { | ||
| 165 | name: "Flipped Right Eye Panels" | ||
| 166 | type: LOCATION_ONLY | ||
| 167 | panels { room: "Flipped Right Eye" name: "HERE" } | ||
| 168 | panels { room: "Flipped Right Eye" name: "WHERE" } | ||
| 169 | location_room: "Flipped Right Eye" | ||
| 170 | location_name: "HERE, WHERE" | ||
| 171 | } | ||
| 172 | doors { | ||
| 173 | name: "Big Eyes" | ||
| 174 | type: LOCATION_ONLY | ||
| 175 | panels { room: "Starting Room" name: "EYE" } | ||
| 176 | panels { room: "Right Eye" name: "EYE" } | ||
| 177 | location_room: "Right Eye" | ||
| 178 | location_name: "EYE" | ||
| 179 | } | ||
| 134 | # Components/Doors/back_left_1, _3, _4, _6 are vanilla because they're nothing. | 180 | # Components/Doors/back_left_1, _3, _4, _6 are vanilla because they're nothing. |
| 135 | doors { | 181 | doors { |
| 136 | name: "Orange Door Hider" | 182 | name: "Orange Door Hider" |
| 137 | type: STANDARD | 183 | type: STANDARD |
| 138 | receivers: "Components/Doors/back_left_2" | 184 | receivers: "Components/Doors/back_left_2" |
| 139 | panels { room: "Colored Doors Area" name: "OPEN" answer: "orange" } | 185 | panels { room: "Colored Doors Area" name: "OPEN" answer: "orange" } |
| 140 | # "wall" is supposed to also work. idk man | 186 | panels { room: "Colored Doors Area" name: "OPEN" answer: "wall" } |
| 187 | complete_at: 1 | ||
| 141 | location_room: "Colored Doors Area" | 188 | location_room: "Colored Doors Area" |
| 189 | location_name: "OPEN" | ||
| 142 | } | 190 | } |
| 143 | doors { | 191 | doors { |
| 144 | name: "Lime Room Entrance" | 192 | name: "Lime Room Entrance" |
| @@ -159,6 +207,7 @@ doors { | |||
| 159 | doors { | 207 | doors { |
| 160 | name: "Control Center White Door" | 208 | name: "Control Center White Door" |
| 161 | type: CONTROL_CENTER_COLOR | 209 | type: CONTROL_CENTER_COLOR |
| 210 | latch: true | ||
| 162 | receivers: "Components/Doors/back_left_7" | 211 | receivers: "Components/Doors/back_left_7" |
| 163 | control_center_color: "white" | 212 | control_center_color: "white" |
| 164 | } | 213 | } |
| @@ -209,7 +258,7 @@ doors { | |||
| 209 | name: "Liberated Entrance" | 258 | name: "Liberated Entrance" |
| 210 | type: STANDARD | 259 | type: STANDARD |
| 211 | receivers: "Components/Doors/Entry/entry_proxied_10" | 260 | receivers: "Components/Doors/Entry/entry_proxied_10" |
| 212 | panels { room: "Flipped Pyramid Area" name: "TURN (1)" } | 261 | panels { room: "Liberated Entrance Panel" name: "TURN (1)" } |
| 213 | location_room: "Flipped Pyramid Area" | 262 | location_room: "Flipped Pyramid Area" |
| 214 | } | 263 | } |
| 215 | doors { | 264 | doors { |
| @@ -222,7 +271,7 @@ doors { | |||
| 222 | name: "Literate Entrance" | 271 | name: "Literate Entrance" |
| 223 | type: STANDARD | 272 | type: STANDARD |
| 224 | receivers: "Components/Doors/Entry/entry_proxied_11" | 273 | receivers: "Components/Doors/Entry/entry_proxied_11" |
| 225 | panels { room: "Flipped Pyramid Area" name: "TURN (2)" } | 274 | panels { room: "Literate Entrance Panel" name: "TURN (2)" } |
| 226 | location_room: "Flipped Pyramid Area" | 275 | location_room: "Flipped Pyramid Area" |
| 227 | } | 276 | } |
| 228 | doors { | 277 | doors { |
| @@ -301,7 +350,6 @@ doors { | |||
| 301 | doors { | 350 | doors { |
| 302 | name: "Red Room Painting" | 351 | name: "Red Room Painting" |
| 303 | type: STANDARD | 352 | type: STANDARD |
| 304 | #move_paintings { room: "Right Eye" name: "PSYCHIC" } | ||
| 305 | receivers: "Components/Paintings/psychic/teleportListener" | 353 | receivers: "Components/Paintings/psychic/teleportListener" |
| 306 | panels { room: "Right Eye" name: "FAINT" } | 354 | panels { room: "Right Eye" name: "FAINT" } |
| 307 | location_room: "Right Eye" | 355 | location_room: "Right Eye" |
| @@ -309,8 +357,49 @@ doors { | |||
| 309 | doors { | 357 | doors { |
| 310 | name: "Third Eye Painting" | 358 | name: "Third Eye Painting" |
| 311 | type: LOCATION_ONLY | 359 | type: LOCATION_ONLY |
| 312 | # move_paintings { room: "Eye Room" name: "GALLERY" } | ||
| 313 | # TODO: ummmm | 360 | # TODO: ummmm |
| 314 | panels { room: "Eye Room" name: "I" } | 361 | panels { room: "Eye Room" name: "I" } |
| 315 | location_room: "Eye Room" | 362 | location_room: "Eye Room" |
| 316 | } \ No newline at end of file | 363 | } |
| 364 | doors { | ||
| 365 | name: "Gift Maps Entrance" | ||
| 366 | type: EVENT | ||
| 367 | receivers: "Components/GiftMapEntrance/PanelTeleporter" | ||
| 368 | double_letters: true | ||
| 369 | } | ||
| 370 | doors { | ||
| 371 | name: "Least Blue Last" | ||
| 372 | type: LOCATION_ONLY | ||
| 373 | panels { room: "Least Blue Last" name: "CAPABLE (1)" } | ||
| 374 | panels { room: "Least Blue Last" name: "CAPABLE (2)" } | ||
| 375 | panels { room: "Least Blue Last" name: "LUSTRE" } | ||
| 376 | panels { room: "Least Blue Last" name: "WANT" } | ||
| 377 | panels { room: "Least Blue Last" name: "STEALER" } | ||
| 378 | panels { room: "Least Blue Last" name: "OLD" } | ||
| 379 | panels { room: "Least Blue Last" name: "TRUST" } | ||
| 380 | panels { room: "Least Blue Last" name: "LABEL" } | ||
| 381 | panels { room: "Least Blue Last" name: "AIL" } | ||
| 382 | location_room: "Least Blue Last" | ||
| 383 | } | ||
| 384 | doors { | ||
| 385 | name: "Control Center White Panel" | ||
| 386 | type: LOCATION_ONLY | ||
| 387 | panels { room: "Lime Room" name: "COLOR" } | ||
| 388 | location_room: "Lime Room" | ||
| 389 | location_name: "COLOR" | ||
| 390 | } | ||
| 391 | doors { | ||
| 392 | name: "Rabbit Hole Blank Puzzle" | ||
| 393 | type: LOCATION_ONLY | ||
| 394 | panels { room: "Rabbit Hole" name: "Blank" } | ||
| 395 | location_room: "Rabbit Hole" | ||
| 396 | location_name: "Blank Puzzle" | ||
| 397 | } | ||
| 398 | doors { | ||
| 399 | name: "Wrath Room Puzzles" | ||
| 400 | type: LOCATION_ONLY | ||
| 401 | panels { room: "Wrath Room" name: "DICE" } | ||
| 402 | panels { room: "Wrath Room" name: "WREATH" } | ||
| 403 | location_room: "Wrath Room" | ||
| 404 | location_name: "DICE, WRATH" | ||
| 405 | } | ||
| diff --git a/data/maps/the_entry/metadata.txtpb b/data/maps/the_entry/metadata.txtpb index 0eeb29a..da2194b 100644 --- a/data/maps/the_entry/metadata.txtpb +++ b/data/maps/the_entry/metadata.txtpb | |||
| @@ -11,3 +11,13 @@ excluded_nodes: "Panels/Back Left/backleft_4_proxied_1" | |||
| 11 | excluded_nodes: "Panels/Back Left/backleft_4_proxied_2" | 11 | excluded_nodes: "Panels/Back Left/backleft_4_proxied_2" |
| 12 | # This is a proxy related to the first panel and it doesn't seem useful. | 12 | # This is a proxy related to the first panel and it doesn't seem useful. |
| 13 | excluded_nodes: "Panels/Entry/entry_proxied_fake" | 13 | excluded_nodes: "Panels/Entry/entry_proxied_fake" |
| 14 | # The gift map entrance is created by the mod. | ||
| 15 | custom_nodes: "Components/GiftMapEntrance/GongusPanel" | ||
| 16 | custom_nodes: "Components/GiftMapEntrance/HatkirbyPanel" | ||
| 17 | custom_nodes: "Components/GiftMapEntrance/IcelyPanel" | ||
| 18 | custom_nodes: "Components/GiftMapEntrance/KirbyPanel" | ||
| 19 | custom_nodes: "Components/GiftMapEntrance/KiwiPanel" | ||
| 20 | custom_nodes: "Components/GiftMapEntrance/Panel" | ||
| 21 | custom_nodes: "Components/GiftMapEntrance/QPanel" | ||
| 22 | custom_nodes: "Components/GiftMapEntrance/SouveyPanel" | ||
| 23 | custom_nodes: "Components/GiftMapEntrance/StarPanel" | ||
| diff --git a/data/maps/the_entry/rooms/Composite Room Entrance.txtpb b/data/maps/the_entry/rooms/Composite Room Entrance.txtpb index b9a8098..ca9e7f4 100644 --- a/data/maps/the_entry/rooms/Composite Room Entrance.txtpb +++ b/data/maps/the_entry/rooms/Composite Room Entrance.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Composite Room Entrance" | |||
| 2 | panel_display_name: "Starting Room" | 2 | panel_display_name: "Starting Room" |
| 3 | ports { | 3 | ports { |
| 4 | name: "COMPOSITE" | 4 | name: "COMPOSITE" |
| 5 | display_name: "Starting Room NE Worldport" | ||
| 5 | path: "Components/Warps/worldport12" | 6 | path: "Components/Warps/worldport12" |
| 6 | orientation: "east" | 7 | destination { x: 16 y: 0 z: -20 } |
| 8 | rotation: 90 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_entry/rooms/Daedalus Entrance.txtpb b/data/maps/the_entry/rooms/Daedalus Entrance.txtpb index db9b78a..76dc278 100644 --- a/data/maps/the_entry/rooms/Daedalus Entrance.txtpb +++ b/data/maps/the_entry/rooms/Daedalus Entrance.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Daedalus Entrance" | |||
| 2 | panel_display_name: "Starting Room" | 2 | panel_display_name: "Starting Room" |
| 3 | ports { | 3 | ports { |
| 4 | name: "DAEDALUS" | 4 | name: "DAEDALUS" |
| 5 | display_name: "Starting Room North Wall West Worldport" | ||
| 5 | path: "Components/Warps/worldport6" | 6 | path: "Components/Warps/worldport6" |
| 6 | orientation: "west" | 7 | destination { x: -16 y: 0 z: -20 } |
| 8 | rotation: 270 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_entry/rooms/Digital Entrance.txtpb b/data/maps/the_entry/rooms/Digital Entrance.txtpb index dd8b5f4..b7689bd 100644 --- a/data/maps/the_entry/rooms/Digital Entrance.txtpb +++ b/data/maps/the_entry/rooms/Digital Entrance.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Digital Entrance" | |||
| 2 | panel_display_name: "Starting Room" | 2 | panel_display_name: "Starting Room" |
| 3 | ports { | 3 | ports { |
| 4 | name: "DIGITAL" | 4 | name: "DIGITAL" |
| 5 | display_name: "Second Room Left Worldport" | ||
| 5 | path: "Components/Warps/worldport" | 6 | path: "Components/Warps/worldport" |
| 6 | orientation: "west" | 7 | destination { x: -78 y: 0 z: -24 } |
| 8 | rotation: 270 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_entry/rooms/Entry Exit.txtpb b/data/maps/the_entry/rooms/Entry Exit.txtpb index b5d75aa..e270bf8 100644 --- a/data/maps/the_entry/rooms/Entry Exit.txtpb +++ b/data/maps/the_entry/rooms/Entry Exit.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Entry Exit" | |||
| 2 | panel_display_name: "Starting Room" | 2 | panel_display_name: "Starting Room" |
| 3 | ports { | 3 | ports { |
| 4 | name: "GREAT" | 4 | name: "GREAT" |
| 5 | display_name: "Second Room Right Worldport" | ||
| 5 | path: "Components/Warps/worldport2" | 6 | path: "Components/Warps/worldport2" |
| 6 | orientation: "north" | 7 | destination { x: 18 y: 0 z: -36 } |
| 8 | rotation: 180 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_entry/rooms/Eye Room.txtpb b/data/maps/the_entry/rooms/Eye Room.txtpb index da17163..00f2534 100644 --- a/data/maps/the_entry/rooms/Eye Room.txtpb +++ b/data/maps/the_entry/rooms/Eye Room.txtpb | |||
| @@ -31,6 +31,8 @@ paintings { | |||
| 31 | } | 31 | } |
| 32 | ports { | 32 | ports { |
| 33 | name: "LIONIZED" | 33 | name: "LIONIZED" |
| 34 | display_name: "Eye Room Worldport" | ||
| 34 | path: "Components/Warps/worldport10" | 35 | path: "Components/Warps/worldport10" |
| 35 | orientation: "north" | 36 | destination { x: 18 y: 0 z: -88.5 } |
| 37 | rotation: 180 | ||
| 36 | } | 38 | } |
| diff --git a/data/maps/the_entry/rooms/Flipped Pyramid Area.txtpb b/data/maps/the_entry/rooms/Flipped Pyramid Area.txtpb index c0be783..30e737f 100644 --- a/data/maps/the_entry/rooms/Flipped Pyramid Area.txtpb +++ b/data/maps/the_entry/rooms/Flipped Pyramid Area.txtpb | |||
| @@ -1,16 +1,6 @@ | |||
| 1 | name: "Flipped Pyramid Area" | 1 | name: "Flipped Pyramid Area" |
| 2 | panel_display_name: "Pyramid Area" | 2 | panel_display_name: "Pyramid Area" |
| 3 | panels { | 3 | # The fact that the doors here cover up the panels once they open is a problem |
| 4 | name: "TURN (1)" | 4 | # since you're not guaranteed to have access to the lower area if painting |
| 5 | path: "Panels/Entry/l_opener_3" | 5 | # shuffle is a thing. So we need to edit these doors so that they don't cover up |
| 6 | clue: "turn" | 6 | # the panels. |
| 7 | answer: "flip" | ||
| 8 | symbols: SUN | ||
| 9 | } | ||
| 10 | panels { | ||
| 11 | name: "TURN (2)" | ||
| 12 | path: "Panels/Entry/l_opener_4" | ||
| 13 | clue: "turn" | ||
| 14 | answer: "spin" | ||
| 15 | symbols: SUN | ||
| 16 | } \ No newline at end of file | ||
| 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..d4650f0 --- /dev/null +++ b/data/maps/the_entry/rooms/Four Rooms Entrance.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Four Rooms Entrance" | ||
| 2 | ports { | ||
| 3 | name: "FOUR" | ||
| 4 | display_name: "Flipped Second Room Right Worldport" | ||
| 5 | path: "Components/Warps/worldport9" | ||
| 6 | destination { x: -41 y: 6 z: -17.5 } | ||
| 7 | rotation: 0 | ||
| 8 | # This isn't actually Y_PLUS gravity! A nearby warp sneakily flips you. | ||
| 9 | } | ||
| diff --git a/data/maps/the_entry/rooms/Gallery Return.txtpb b/data/maps/the_entry/rooms/Gallery Return.txtpb index 987a3ca..7235d80 100644 --- a/data/maps/the_entry/rooms/Gallery Return.txtpb +++ b/data/maps/the_entry/rooms/Gallery Return.txtpb | |||
| @@ -9,6 +9,8 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "GALLERY" | 11 | name: "GALLERY" |
| 12 | display_name: "Gallery Return" | ||
| 12 | path: "Components/Warps/worldport4" | 13 | path: "Components/Warps/worldport4" |
| 13 | orientation: "north" | 14 | destination { x: -38 y: 0 z: 8.5 } |
| 15 | rotation: 180 | ||
| 14 | } \ No newline at end of file | 16 | } \ No newline at end of file |
| diff --git a/data/maps/the_entry/rooms/Least Blue Last.txtpb b/data/maps/the_entry/rooms/Least Blue Last.txtpb index adbe545..dde203a 100644 --- a/data/maps/the_entry/rooms/Least Blue Last.txtpb +++ b/data/maps/the_entry/rooms/Least Blue Last.txtpb | |||
| @@ -72,8 +72,10 @@ panels { | |||
| 72 | } | 72 | } |
| 73 | ports { | 73 | ports { |
| 74 | name: "DARKROOM" | 74 | name: "DARKROOM" |
| 75 | display_name: "Near L1 Worldport" | ||
| 75 | path: "Components/Warps/worldport5" | 76 | path: "Components/Warps/worldport5" |
| 76 | orientation: "south" | 77 | destination { x: 43 y: 0 z: -10 } |
| 78 | rotation: 0 | ||
| 77 | } | 79 | } |
| 78 | paintings { | 80 | paintings { |
| 79 | name: "PAINS" | 81 | name: "PAINS" |
| diff --git a/data/maps/the_entry/rooms/Liberated Entrance Panel.txtpb b/data/maps/the_entry/rooms/Liberated Entrance Panel.txtpb new file mode 100644 index 0000000..7c5ef71 --- /dev/null +++ b/data/maps/the_entry/rooms/Liberated Entrance Panel.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Liberated Entrance Panel" | ||
| 2 | panel_display_name: "Pyramid Area" | ||
| 3 | panels { | ||
| 4 | name: "TURN (1)" | ||
| 5 | path: "Panels/Entry/l_opener_3" | ||
| 6 | clue: "turn" | ||
| 7 | answer: "flip" | ||
| 8 | symbols: SUN | ||
| 9 | } | ||
| 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..56cc597 --- /dev/null +++ b/data/maps/the_entry/rooms/Liberated Entrance.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Liberated Entrance" | ||
| 2 | ports { | ||
| 3 | name: "BLUE" | ||
| 4 | display_name: "Pyramid Area Blue Worldport" | ||
| 5 | path: "worldport8" | ||
| 6 | destination { x: 18 y: 0 z: 55 } | ||
| 7 | rotation: 270 | ||
| 8 | } | ||
| diff --git a/data/maps/the_entry/rooms/Lime Room.txtpb b/data/maps/the_entry/rooms/Lime Room.txtpb index e94f775..603fbdc 100644 --- a/data/maps/the_entry/rooms/Lime Room.txtpb +++ b/data/maps/the_entry/rooms/Lime Room.txtpb | |||
| @@ -19,9 +19,3 @@ panels { | |||
| 19 | answer: "white" | 19 | answer: "white" |
| 20 | symbols: EXAMPLE | 20 | symbols: EXAMPLE |
| 21 | } | 21 | } |
| 22 | ports { | ||
| 23 | name: "REVITALIZED" | ||
| 24 | path: "worldport7" | ||
| 25 | orientation: "north" | ||
| 26 | required_door { name: "Revitalized Entrance" } | ||
| 27 | } \ No newline at end of file | ||
| 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 Panel.txtpb b/data/maps/the_entry/rooms/Literate Entrance Panel.txtpb new file mode 100644 index 0000000..676598b --- /dev/null +++ b/data/maps/the_entry/rooms/Literate Entrance Panel.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Literate Entrance Panel" | ||
| 2 | panel_display_name: "Pyramid Area" | ||
| 3 | panels { | ||
| 4 | name: "TURN (2)" | ||
| 5 | path: "Panels/Entry/l_opener_4" | ||
| 6 | clue: "turn" | ||
| 7 | answer: "spin" | ||
| 8 | symbols: SUN | ||
| 9 | } | ||
| 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..b86ac80 --- /dev/null +++ b/data/maps/the_entry/rooms/Literate Entrance.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Literate Entrance" | ||
| 2 | ports { | ||
| 3 | name: "BROWN" | ||
| 4 | display_name: "Pyramid Area Brown Worldport" | ||
| 5 | path: "worldport9" | ||
| 6 | destination { x: 39 y: 0 z: 55 } | ||
| 7 | rotation: 90 | ||
| 8 | } | ||
| diff --git a/data/maps/the_entry/rooms/Parthenon Return.txtpb b/data/maps/the_entry/rooms/Parthenon Return.txtpb index 4776d11..bb12964 100644 --- a/data/maps/the_entry/rooms/Parthenon Return.txtpb +++ b/data/maps/the_entry/rooms/Parthenon Return.txtpb | |||
| @@ -9,6 +9,8 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "PARTHENON" | 11 | name: "PARTHENON" |
| 12 | display_name: "Parthenon Return" | ||
| 12 | path: "Components/Warps/worldport8" | 13 | path: "Components/Warps/worldport8" |
| 13 | orientation: "north" | 14 | destination { x: -5.5 y: 0 z: 18 } |
| 14 | } \ No newline at end of file | 15 | rotation: 180 |
| 16 | } | ||
| diff --git a/data/maps/the_entry/rooms/Rabbit Hole.txtpb b/data/maps/the_entry/rooms/Rabbit Hole.txtpb index 520d513..4799fde 100644 --- a/data/maps/the_entry/rooms/Rabbit Hole.txtpb +++ b/data/maps/the_entry/rooms/Rabbit Hole.txtpb | |||
| @@ -1,13 +1,15 @@ | |||
| 1 | name: "Rabbit Hole" | 1 | name: "Rabbit Hole" |
| 2 | panel_display_name: "Red Blue Area" | 2 | panel_display_name: "Red Blue Area" |
| 3 | panels { | 3 | panels { |
| 4 | name: "PUZZLE" | 4 | name: "Blank" |
| 5 | path: "Panels/Back Right/br_6" | 5 | path: "Panels/Back Right/br_6" |
| 6 | clue: "" | 6 | clue: "" |
| 7 | answer: "down" | 7 | answer: "down" |
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "HOLE" | 10 | name: "HOLE" |
| 11 | display_name: "Rabbit Hole" | ||
| 11 | path: "worldport4" | 12 | path: "worldport4" |
| 12 | orientation: "down" | 13 | destination { x: 74 y: 0 z: -43 } |
| 13 | } \ No newline at end of file | 14 | rotation: 0 |
| 15 | } | ||
| diff --git a/data/maps/the_entry/rooms/Repetitive Entrance.txtpb b/data/maps/the_entry/rooms/Repetitive Entrance.txtpb index 04ddcf3..a83eea4 100644 --- a/data/maps/the_entry/rooms/Repetitive Entrance.txtpb +++ b/data/maps/the_entry/rooms/Repetitive Entrance.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Repetitive Entrance" | |||
| 2 | panel_display_name: "Starting Room" | 2 | panel_display_name: "Starting Room" |
| 3 | ports { | 3 | ports { |
| 4 | name: "REPETITIVE" | 4 | name: "REPETITIVE" |
| 5 | display_name: "Starting Room West Wall North Worldport" | ||
| 5 | path: "Components/Warps/worldport7" | 6 | path: "Components/Warps/worldport7" |
| 6 | orientation: "north" | 7 | destination { x: -20 y: 0 z: -16 } |
| 8 | rotation: 180 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_entry/rooms/Revitalized Entrance.txtpb b/data/maps/the_entry/rooms/Revitalized Entrance.txtpb new file mode 100644 index 0000000..fb5e7e0 --- /dev/null +++ b/data/maps/the_entry/rooms/Revitalized Entrance.txtpb | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | name: "Revitalized Entrance" | ||
| 2 | panel_display_name: "Colored Doors Area" | ||
| 3 | ports { | ||
| 4 | name: "REVITALIZED" | ||
| 5 | display_name: "Plum Hallway" | ||
| 6 | path: "worldport7" | ||
| 7 | destination { x: -58 y: 0 z: 31.5 } | ||
| 8 | rotation: 180 | ||
| 9 | } | ||
| diff --git a/data/maps/the_entry/rooms/Shop Entrance.txtpb b/data/maps/the_entry/rooms/Shop Entrance.txtpb index f793da3..4a99efa 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" |
| @@ -9,6 +9,8 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "SHOP" | 11 | name: "SHOP" |
| 12 | display_name: "Shop Entrance" | ||
| 12 | path: "Components/Warps/worldport13" | 13 | path: "Components/Warps/worldport13" |
| 13 | orientation: "east" | 14 | destination { x: 18 y: 0 z: 49 } |
| 15 | rotation: 90 | ||
| 14 | } \ No newline at end of file | 16 | } \ No newline at end of file |
| diff --git a/data/maps/the_entry/rooms/Starting Room.txtpb b/data/maps/the_entry/rooms/Starting Room.txtpb index bc77e6d..d01d807 100644 --- a/data/maps/the_entry/rooms/Starting Room.txtpb +++ b/data/maps/the_entry/rooms/Starting Room.txtpb | |||
| @@ -24,7 +24,9 @@ panels { | |||
| 24 | path: "Panels/Entry/front_1" | 24 | path: "Panels/Entry/front_1" |
| 25 | clue: "eye" | 25 | clue: "eye" |
| 26 | answer: "i" | 26 | answer: "i" |
| 27 | symbols: ZERO | 27 | #symbols: ZERO |
| 28 | # This panel blocks getting N1 and T1. We will mod it to be I/I with no symbol | ||
| 29 | # when symbol shuffle is on. | ||
| 28 | } | 30 | } |
| 29 | panels { | 31 | panels { |
| 30 | name: "HINT" | 32 | name: "HINT" |
| @@ -44,6 +46,25 @@ panels { | |||
| 44 | clue: "than" | 46 | clue: "than" |
| 45 | answer: "than" | 47 | answer: "than" |
| 46 | } | 48 | } |
| 49 | panels { | ||
| 50 | name: "Gift Maps" | ||
| 51 | # TODO: exclude from panelsanity | ||
| 52 | path: "Components/GiftMapEntrance/Panel" | ||
| 53 | clue: "player" | ||
| 54 | answer: "" | ||
| 55 | # The puzzle solution doesn't matter. We'll change it to the player's name | ||
| 56 | # for fun. | ||
| 57 | symbols: QUESTION | ||
| 58 | proxies { answer: "gongus" path: "Components/GiftMapEntrance/GongusPanel" } | ||
| 59 | proxies { answer: "hatkirby" path: "Components/GiftMapEntrance/HatkirbyPanel" } | ||
| 60 | proxies { answer: "icely" path: "Components/GiftMapEntrance/IcelyPanel" } | ||
| 61 | proxies { answer: "kirby" path: "Components/GiftMapEntrance/KirbyPanel" } | ||
| 62 | proxies { answer: "kiwi" path: "Components/GiftMapEntrance/KiwiPanel" } | ||
| 63 | proxies { answer: "q" path: "Components/GiftMapEntrance/QPanel" } | ||
| 64 | proxies { answer: "souvey" path: "Components/GiftMapEntrance/SouveyPanel" } | ||
| 65 | proxies { answer: "star" path: "Components/GiftMapEntrance/StarPanel" } | ||
| 66 | required_door { name: "Gift Maps Entrance" } | ||
| 67 | } | ||
| 47 | letters { | 68 | letters { |
| 48 | key: "h" | 69 | key: "h" |
| 49 | path: "Components/Collectables/h" | 70 | path: "Components/Collectables/h" |
| diff --git a/data/maps/the_entry/rooms/White Hallway To Daedalus.txtpb b/data/maps/the_entry/rooms/White Hallway To Daedalus.txtpb index ce35e5b..de0cec2 100644 --- a/data/maps/the_entry/rooms/White Hallway To Daedalus.txtpb +++ b/data/maps/the_entry/rooms/White Hallway To Daedalus.txtpb | |||
| @@ -2,6 +2,8 @@ name: "White Hallway To Daedalus" | |||
| 2 | panel_display_name: "Colored Doors Area" | 2 | panel_display_name: "Colored Doors Area" |
| 3 | ports { | 3 | ports { |
| 4 | name: "DAEDALUS" | 4 | name: "DAEDALUS" |
| 5 | display_name: "White Control Center Hallway" | ||
| 5 | path: "Components/Warps/worldport11" | 6 | path: "Components/Warps/worldport11" |
| 6 | orientation: "west" | 7 | destination { x: -45 y: 0 z: 24 } |
| 8 | rotation: 270 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_entry/rooms/X Area.txtpb b/data/maps/the_entry/rooms/X Area.txtpb index 3f61c26..8388b4e 100644 --- a/data/maps/the_entry/rooms/X Area.txtpb +++ b/data/maps/the_entry/rooms/X Area.txtpb | |||
| @@ -6,6 +6,8 @@ letters { | |||
| 6 | } | 6 | } |
| 7 | ports { | 7 | ports { |
| 8 | name: "CC" | 8 | name: "CC" |
| 9 | display_name: "Near X1 Worldport" | ||
| 9 | path: "Components/Warps/worldport3" | 10 | path: "Components/Warps/worldport3" |
| 10 | orientation: "west" | 11 | destination { x: -12.5 y: 0 z: 60 } |
| 12 | rotation: 270 | ||
| 11 | } | 13 | } |
| diff --git a/data/maps/the_extravagant/rooms/Engine Room.txtpb b/data/maps/the_extravagant/rooms/Engine Room.txtpb index 3dcc437..18dfcad 100644 --- a/data/maps/the_extravagant/rooms/Engine Room.txtpb +++ b/data/maps/the_extravagant/rooms/Engine Room.txtpb | |||
| @@ -22,7 +22,9 @@ paintings { | |||
| 22 | } | 22 | } |
| 23 | ports { | 23 | ports { |
| 24 | name: "GALLERY" | 24 | name: "GALLERY" |
| 25 | display_name: "Engine Room Worldport" | ||
| 25 | path: "Components/Warps/worldport2" | 26 | path: "Components/Warps/worldport2" |
| 26 | gravity: Z_PLUS | 27 | gravity: Z_PLUS |
| 27 | # TODO: orientation is not well defined with Z-axis gravity | 28 | # TODO: entrance shuffling for non Y_MINUS gravity |
| 29 | no_shuffle: true | ||
| 28 | } | 30 | } |
| diff --git a/data/maps/the_fuzzy/connections.txtpb b/data/maps/the_fuzzy/connections.txtpb new file mode 100644 index 0000000..ea39f34 --- /dev/null +++ b/data/maps/the_fuzzy/connections.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | connections { | ||
| 2 | from_room: "Main Area" | ||
| 3 | to_room: "Mastery" | ||
| 4 | door { name: "Mastery Door" } | ||
| 5 | } | ||
| diff --git a/data/maps/the_fuzzy/doors.txtpb b/data/maps/the_fuzzy/doors.txtpb new file mode 100644 index 0000000..9c481c9 --- /dev/null +++ b/data/maps/the_fuzzy/doors.txtpb | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | doors { | ||
| 2 | name: "Black Panels" | ||
| 3 | type: LOCATION_ONLY | ||
| 4 | panels { room: "Main Area" name: "WHERETO" } | ||
| 5 | panels { room: "Main Area" name: "COMBINED" } | ||
| 6 | location_room: "Main Area" | ||
| 7 | } | ||
| 8 | doors { | ||
| 9 | name: "Green Panels" | ||
| 10 | type: LOCATION_ONLY | ||
| 11 | panels { room: "Main Area" name: "ACHIEVES" } | ||
| 12 | panels { room: "Main Area" name: "BEFORE" } | ||
| 13 | panels { room: "Main Area" name: "Blank" } | ||
| 14 | panels { room: "Main Area" name: "BOTH" } | ||
| 15 | panels { room: "Main Area" name: "CAGED" } | ||
| 16 | panels { room: "Main Area" name: "DICE" } | ||
| 17 | panels { room: "Main Area" name: "FIRST" } | ||
| 18 | panels { room: "Main Area" name: "FORGED" } | ||
| 19 | panels { room: "Main Area" name: "LOTTO" } | ||
| 20 | panels { room: "Main Area" name: "TOED" } | ||
| 21 | panels { room: "Main Area" name: "TUTU" } | ||
| 22 | panels { room: "Main Area" name: "UNVEILED" } | ||
| 23 | location_room: "Main Area" | ||
| 24 | } | ||
| 25 | doors { | ||
| 26 | name: "Mastery Door" | ||
| 27 | type: EVENT | ||
| 28 | panels { room: "Main Area" name: "OTHERS" } | ||
| 29 | } | ||
| diff --git a/data/maps/the_fuzzy/metadata.txtpb b/data/maps/the_fuzzy/metadata.txtpb new file mode 100644 index 0000000..b4178c7 --- /dev/null +++ b/data/maps/the_fuzzy/metadata.txtpb | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | display_name: "The Fuzzy" | ||
| 2 | type: GIFT_MAP | ||
| 3 | # The map's mastery is created at runtime. | ||
| 4 | custom_nodes: "Components/Collectables/collectable" | ||
| diff --git a/data/maps/the_fuzzy/rooms/Main Area.txtpb b/data/maps/the_fuzzy/rooms/Main Area.txtpb new file mode 100644 index 0000000..9c06df8 --- /dev/null +++ b/data/maps/the_fuzzy/rooms/Main Area.txtpb | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | name: "Main Area" | ||
| 2 | panels { | ||
| 3 | name: "Blank" | ||
| 4 | path: "Panels/Room_1/panel_1" | ||
| 5 | clue: "" | ||
| 6 | answer: "2475" | ||
| 7 | symbols: LINGO | ||
| 8 | symbols: QUESTION | ||
| 9 | } | ||
| 10 | panels { | ||
| 11 | name: "TUTU" | ||
| 12 | path: "Panels/Room_1/panel_2" | ||
| 13 | clue: "tutu" | ||
| 14 | answer: "22" | ||
| 15 | symbols: ZERO | ||
| 16 | symbols: EVAL | ||
| 17 | } | ||
| 18 | panels { | ||
| 19 | name: "LOTTO" | ||
| 20 | path: "Panels/Room_1/panel_3" | ||
| 21 | clue: "lotto" | ||
| 22 | answer: "22222222" | ||
| 23 | symbols: ZERO | ||
| 24 | symbols: EVAL | ||
| 25 | } | ||
| 26 | panels { | ||
| 27 | name: "WHERETO" | ||
| 28 | path: "Panels/Room_1/panel_10" | ||
| 29 | clue: "whereto" | ||
| 30 | answer: "sides" | ||
| 31 | symbols: QUESTION | ||
| 32 | } | ||
| 33 | panels { | ||
| 34 | name: "DICE" | ||
| 35 | path: "Panels/Room_1/panel_11" | ||
| 36 | clue: "dice" | ||
| 37 | answer: "4935" | ||
| 38 | symbols: QUESTION | ||
| 39 | } | ||
| 40 | panels { | ||
| 41 | name: "CAGED" | ||
| 42 | path: "Panels/Room_1/panel_12" | ||
| 43 | clue: "caged" | ||
| 44 | answer: "31754" | ||
| 45 | symbols: QUESTION | ||
| 46 | } | ||
| 47 | panels { | ||
| 48 | name: "BEFORE" | ||
| 49 | path: "Panels/Room_1/panel_13" | ||
| 50 | clue: "before" | ||
| 51 | answer: "100" | ||
| 52 | symbols: ZERO | ||
| 53 | symbols: EVAL | ||
| 54 | } | ||
| 55 | panels { | ||
| 56 | name: "TOED" | ||
| 57 | path: "Panels/Room_1/panel_14" | ||
| 58 | clue: "toed" | ||
| 59 | answer: "108" | ||
| 60 | symbols: ZERO | ||
| 61 | symbols: EVAL | ||
| 62 | } | ||
| 63 | panels { | ||
| 64 | name: "FORGED" | ||
| 65 | path: "Panels/Room_1/panel_15" | ||
| 66 | clue: "forged" | ||
| 67 | answer: "3016" | ||
| 68 | symbols: ZERO | ||
| 69 | symbols: EVAL | ||
| 70 | } | ||
| 71 | panels { | ||
| 72 | name: "OTHERS" | ||
| 73 | path: "Panels/Room_1/panel_4" | ||
| 74 | clue: "others" | ||
| 75 | answer: "34390869" | ||
| 76 | symbols: QUESTION | ||
| 77 | } | ||
| 78 | panels { | ||
| 79 | name: "COMBINED" | ||
| 80 | path: "Panels/Room_1/panel_9" | ||
| 81 | clue: "combined" | ||
| 82 | answer: "added" | ||
| 83 | symbols: SUN | ||
| 84 | } | ||
| 85 | panels { | ||
| 86 | name: "ACHIEVES" | ||
| 87 | path: "Panels/Room_1/panel_5" | ||
| 88 | clue: "achieves" | ||
| 89 | answer: "4214" | ||
| 90 | symbols: QUESTION | ||
| 91 | } | ||
| 92 | panels { | ||
| 93 | name: "UNVEILED" | ||
| 94 | path: "Panels/Room_1/panel_6" | ||
| 95 | clue: "unveiled" | ||
| 96 | answer: "12122021" | ||
| 97 | symbols: QUESTION | ||
| 98 | } | ||
| 99 | panels { | ||
| 100 | name: "FIRST" | ||
| 101 | path: "Panels/Room_1/panel_8" | ||
| 102 | clue: "first" | ||
| 103 | answer: "1" | ||
| 104 | symbols: QUESTION | ||
| 105 | } | ||
| 106 | panels { | ||
| 107 | name: "BOTH" | ||
| 108 | path: "Panels/Room_1/panel_7" | ||
| 109 | clue: "both" | ||
| 110 | answer: "2" | ||
| 111 | symbols: QUESTION | ||
| 112 | } | ||
| 113 | ports { | ||
| 114 | name: "WORLDPORT" | ||
| 115 | display_name: "Entrance" | ||
| 116 | path: "Components/Warps/worldport" | ||
| 117 | destination { x: 0 y: 0 z: 9 } | ||
| 118 | rotation: 0 | ||
| 119 | } | ||
| diff --git a/data/maps/the_fuzzy/rooms/Mastery.txtpb b/data/maps/the_fuzzy/rooms/Mastery.txtpb new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/data/maps/the_fuzzy/rooms/Mastery.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mastery" | ||
| 2 | masteries { | ||
| 3 | name: "MASTERY" | ||
| 4 | path: "Components/Collectables/collectable" | ||
| 5 | } | ||
| diff --git a/data/maps/the_gallery/doors.txtpb b/data/maps/the_gallery/doors.txtpb index a7a5d85..9bbc016 100644 --- a/data/maps/the_gallery/doors.txtpb +++ b/data/maps/the_gallery/doors.txtpb | |||
| @@ -1,9 +1,9 @@ | |||
| 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/Listeners/Hint Room/unlockReaderListenerDarkroom" |
| 7 | panels { map: "the_darkroom" room: "First Room" name: "BISON" } | 7 | panels { map: "the_darkroom" room: "First Room" name: "BISON" } |
| 8 | panels { map: "the_darkroom" room: "First Room" name: "FISH" } | 8 | panels { map: "the_darkroom" room: "First Room" name: "FISH" } |
| 9 | panels { map: "the_darkroom" room: "First Room" name: "SHEEP" } | 9 | panels { map: "the_darkroom" room: "First Room" name: "SHEEP" } |
| @@ -27,16 +27,16 @@ 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/Listeners/Hint Room/unlockReaderListenerButterfly" |
| 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/Listeners/Hint Room/unlockReaderListenerBetween" |
| 40 | panels { map: "the_between" room: "Main Area" name: "SUN" } | 40 | panels { map: "the_between" room: "Main Area" name: "SUN" } |
| 41 | panels { map: "the_between" room: "Main Area" name: "KOI" } | 41 | panels { map: "the_between" room: "Main Area" name: "KOI" } |
| 42 | panels { map: "the_between" room: "Main Area" name: "SUN KOI" } | 42 | panels { map: "the_between" room: "Main Area" name: "SUN KOI" } |
| @@ -70,16 +70,16 @@ 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/Listeners/Hint Room/unlockReaderListenerEyes" |
| 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/Listeners/Hint Room/unlockReaderListenerTriangle" |
| 83 | panels { map: "the_wise" room: "Entry" name: "INK" } | 83 | panels { map: "the_wise" room: "Entry" name: "INK" } |
| 84 | panels { map: "the_wise" room: "Puzzles" name: "STORY" } | 84 | panels { map: "the_wise" room: "Puzzles" name: "STORY" } |
| 85 | panels { map: "the_wise" room: "Puzzles" name: "VENTURE" } | 85 | panels { map: "the_wise" room: "Puzzles" name: "VENTURE" } |
| @@ -105,9 +105,9 @@ 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/Listeners/Hint Room/unlockReaderListenerTree" |
| 111 | panels { map: "the_tree" room: "Main Area" name: "COLOR" } | 111 | panels { map: "the_tree" room: "Main Area" name: "COLOR" } |
| 112 | panels { map: "the_tree" room: "Main Area" name: "DAMAGE (1)" } | 112 | panels { map: "the_tree" room: "Main Area" name: "DAMAGE (1)" } |
| 113 | panels { map: "the_tree" room: "Main Area" name: "DAMAGE (2)" } | 113 | panels { map: "the_tree" room: "Main Area" name: "DAMAGE (2)" } |
| @@ -142,37 +142,37 @@ 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/Listeners/Hint Room/unlockReaderListenerUnyielding" |
| 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/Listeners/Endings/unlockReaderListenerGraveyard" |
| 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/Listeners/Endings/unlockReaderListenerDesert" |
| 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/Listeners/Endings/unlockReaderListenerTower" |
| 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/Listeners/Endings/unlockReaderListenerWonderland" |
| 176 | panels { map: "the_wondrous" room: "Entry" name: "WONDER" } | 176 | panels { map: "the_wondrous" room: "Entry" name: "WONDER" } |
| 177 | panels { map: "the_wondrous" room: "Regular" name: "SHRINK" } | 177 | panels { map: "the_wondrous" room: "Regular" name: "SHRINK" } |
| 178 | panels { map: "the_wondrous" room: "Huge" name: "SHRINK" } | 178 | panels { map: "the_wondrous" room: "Huge" name: "SHRINK" } |
| @@ -187,44 +187,44 @@ 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/Listeners/Endings/unlockReaderListenerRainbow" |
| 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/Listeners/Endings/unlockReaderListenerWords" |
| 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/Listeners/Endings/unlockReaderListenerColorful" |
| 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/Listeners/Endings/unlockReaderListenerCastle" |
| 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/Listeners/Endings/unlockReaderListenerTemple" |
| 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/Listeners/Endings/unlockReaderListenerQuartz" |
| 228 | rooms { map: "the_ancient" name: "Outside" } | 228 | rooms { map: "the_ancient" name: "Outside" } |
| 229 | } | 229 | } |
| 230 | doors { | 230 | doors { |
| @@ -255,7 +255,8 @@ doors { | |||
| 255 | doors { name: "Castle Painting" } | 255 | doors { name: "Castle Painting" } |
| 256 | doors { name: "Sun Temple Painting" } | 256 | doors { name: "Sun Temple Painting" } |
| 257 | doors { name: "Ancient Painting" } | 257 | doors { name: "Ancient Painting" } |
| 258 | doors { name: "Gallery Extension" } | 258 | panels { room: "Daedalus Extension" name: "WHERE" } |
| 259 | double_letters: true | ||
| 259 | } | 260 | } |
| 260 | doors { | 261 | doors { |
| 261 | name: "Ending Door" | 262 | name: "Ending Door" |
| diff --git a/data/maps/the_gallery/rooms/Main Area.txtpb b/data/maps/the_gallery/rooms/Main Area.txtpb index bc1606d..e88dc48 100644 --- a/data/maps/the_gallery/rooms/Main Area.txtpb +++ b/data/maps/the_gallery/rooms/Main Area.txtpb | |||
| @@ -162,6 +162,8 @@ paintings { | |||
| 162 | } | 162 | } |
| 163 | ports { | 163 | ports { |
| 164 | name: "ENTRY" | 164 | name: "ENTRY" |
| 165 | display_name: "Entrance" | ||
| 165 | path: "Components/Warps/worldport" | 166 | path: "Components/Warps/worldport" |
| 166 | orientation: "west" | 167 | destination { x: -3.5 y: 0 z: 16 } |
| 168 | rotation: 270 | ||
| 167 | } | 169 | } |
| diff --git a/data/maps/the_gold/doors.txtpb b/data/maps/the_gold/doors.txtpb new file mode 100644 index 0000000..d3329cb --- /dev/null +++ b/data/maps/the_gold/doors.txtpb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | doors { | ||
| 2 | name: "The Panel" | ||
| 3 | type: LOCATION_ONLY | ||
| 4 | panels { room: "The Whole Thing" name: "PANEL" } | ||
| 5 | location_room: "The Whole Thing" | ||
| 6 | location_name: "Panel" | ||
| 7 | } | ||
| diff --git a/data/maps/the_graveyard/doors.txtpb b/data/maps/the_graveyard/doors.txtpb index a10d8f6..20e7fcf 100644 --- a/data/maps/the_graveyard/doors.txtpb +++ b/data/maps/the_graveyard/doors.txtpb | |||
| @@ -23,3 +23,10 @@ doors { | |||
| 23 | receivers: "Components/Paintings/omrt/teleportListener" | 23 | receivers: "Components/Paintings/omrt/teleportListener" |
| 24 | double_letters: true | 24 | double_letters: true |
| 25 | } | 25 | } |
| 26 | doors { | ||
| 27 | name: "Remember Panel" | ||
| 28 | type: LOCATION_ONLY | ||
| 29 | panels { room: "Inside" name: "REMEMBER" } | ||
| 30 | location_room: "Inside" | ||
| 31 | location_name: "REMEMBER" | ||
| 32 | } | ||
| diff --git a/data/maps/the_great/connections.txtpb b/data/maps/the_great/connections.txtpb index f1a7e25..171e809 100644 --- a/data/maps/the_great/connections.txtpb +++ b/data/maps/the_great/connections.txtpb | |||
| @@ -256,3 +256,7 @@ connections { | |||
| 256 | to_room: "Zero Room" | 256 | to_room: "Zero Room" |
| 257 | door { name: "Lavender Cube" } | 257 | door { name: "Lavender Cube" } |
| 258 | } | 258 | } |
| 259 | connections { | ||
| 260 | from_room: "Back Area" | ||
| 261 | to_room: "The Landscapes" | ||
| 262 | } | ||
| diff --git a/data/maps/the_great/doors.txtpb b/data/maps/the_great/doors.txtpb index 992b4fa..98d9859 100644 --- a/data/maps/the_great/doors.txtpb +++ b/data/maps/the_great/doors.txtpb | |||
| @@ -29,6 +29,15 @@ doors { | |||
| 29 | location_room: "Main Area" | 29 | location_room: "Main Area" |
| 30 | } | 30 | } |
| 31 | doors { | 31 | doors { |
| 32 | name: "Near Linear Panels" | ||
| 33 | type: LOCATION_ONLY | ||
| 34 | panels { room: "Main Area" name: "DEW" } | ||
| 35 | panels { room: "Main Area" name: "EWE" } | ||
| 36 | panels { room: "Main Area" name: "NO" } | ||
| 37 | location_room: "Main Area" | ||
| 38 | location_name: "DEW, EWE, NO" | ||
| 39 | } | ||
| 40 | doors { | ||
| 32 | name: "Courtyard Entrance" | 41 | name: "Courtyard Entrance" |
| 33 | type: STANDARD | 42 | type: STANDARD |
| 34 | receivers: "Components/Doors/entry_1" | 43 | receivers: "Components/Doors/entry_1" |
| @@ -54,19 +63,22 @@ doors { | |||
| 54 | doors { | 63 | doors { |
| 55 | name: "Control Center Purple Door" | 64 | name: "Control Center Purple Door" |
| 56 | type: CONTROL_CENTER_COLOR | 65 | type: CONTROL_CENTER_COLOR |
| 66 | latch: true | ||
| 57 | receivers: "Components/Doors/entry_23" | 67 | receivers: "Components/Doors/entry_23" |
| 58 | control_center_color: "purple" | 68 | control_center_color: "purple" |
| 59 | } | 69 | } |
| 60 | doors { | 70 | doors { |
| 61 | name: "Control Center Gray Door" | 71 | name: "Control Center Gray Door" |
| 62 | type: CONTROL_CENTER_COLOR | 72 | type: CONTROL_CENTER_COLOR |
| 63 | receivers: "Components/Doors/Gates/Gate" | 73 | latch: true |
| 74 | receivers: "Components/Doors/Gates/Gate/animationListener" | ||
| 64 | control_center_color: "gray" | 75 | control_center_color: "gray" |
| 65 | } | 76 | } |
| 66 | doors { | 77 | doors { |
| 67 | name: "Control Center Red Door" | 78 | name: "Control Center Red Door" |
| 68 | type: CONTROL_CENTER_COLOR | 79 | type: CONTROL_CENTER_COLOR |
| 69 | receivers: "Components/Doors/Gates/Gate" | 80 | latch: true |
| 81 | receivers: "Components/Doors/entry_18" | ||
| 70 | control_center_color: "red" | 82 | control_center_color: "red" |
| 71 | } | 83 | } |
| 72 | doors { | 84 | doors { |
| @@ -124,6 +136,7 @@ doors { | |||
| 124 | panels { room: "Magnet Room" name: "SAW" } | 136 | panels { room: "Magnet Room" name: "SAW" } |
| 125 | panels { room: "Magnet Room" name: "BLENDER" } | 137 | panels { room: "Magnet Room" name: "BLENDER" } |
| 126 | location_room: "Magnet Room" | 138 | location_room: "Magnet Room" |
| 139 | location_name: "Gravestone" | ||
| 127 | } | 140 | } |
| 128 | doors { | 141 | doors { |
| 129 | name: "Hive Entrance" | 142 | name: "Hive Entrance" |
| @@ -205,23 +218,25 @@ doors { | |||
| 205 | panels { room: "Jail Part 2" name: "GRIMACE" } | 218 | panels { room: "Jail Part 2" name: "GRIMACE" } |
| 206 | panels { room: "Jail Part 2" name: "COMMENCE" } | 219 | panels { room: "Jail Part 2" name: "COMMENCE" } |
| 207 | location_room: "Jail Part 2" | 220 | location_room: "Jail Part 2" |
| 221 | location_name: "Gravestone" | ||
| 208 | } | 222 | } |
| 209 | doors { | 223 | doors { |
| 210 | name: "The Landscapes Gravestone" | 224 | name: "The Landscapes Gravestone" |
| 211 | type: GRAVESTONE | 225 | type: GRAVESTONE |
| 212 | panels { room: "Back Area" name: "Top Landscape Top" } | 226 | panels { room: "The Landscapes" name: "Top Landscape Top" } |
| 213 | panels { room: "Back Area" name: "Top Landscape Right" } | 227 | panels { room: "The Landscapes" name: "Top Landscape Right" } |
| 214 | panels { room: "Back Area" name: "Top Landscape Bottom" } | 228 | panels { room: "The Landscapes" name: "Top Landscape Bottom" } |
| 215 | panels { room: "Back Area" name: "Top Landscape Left" } | 229 | panels { room: "The Landscapes" name: "Top Landscape Left" } |
| 216 | panels { room: "Back Area" name: "Left Landscape Top" } | 230 | panels { room: "The Landscapes" name: "Left Landscape Top" } |
| 217 | panels { room: "Back Area" name: "Left Landscape Right" } | 231 | panels { room: "The Landscapes" name: "Left Landscape Right" } |
| 218 | panels { room: "Back Area" name: "Left Landscape Bottom" } | 232 | panels { room: "The Landscapes" name: "Left Landscape Bottom" } |
| 219 | panels { room: "Back Area" name: "Left Landscape Left" } | 233 | panels { room: "The Landscapes" name: "Left Landscape Left" } |
| 220 | panels { room: "Back Area" name: "Right Landscape Top" } | 234 | panels { room: "The Landscapes" name: "Right Landscape Top" } |
| 221 | panels { room: "Back Area" name: "Right Landscape Right" } | 235 | panels { room: "The Landscapes" name: "Right Landscape Right" } |
| 222 | panels { room: "Back Area" name: "Right Landscape Bottom" } | 236 | panels { room: "The Landscapes" name: "Right Landscape Bottom" } |
| 223 | panels { room: "Back Area" name: "Right Landscape Left" } | 237 | panels { room: "The Landscapes" name: "Right Landscape Left" } |
| 224 | location_room: "Back Area" | 238 | location_room: "The Landscapes" |
| 239 | location_name: "Gravestone" | ||
| 225 | } | 240 | } |
| 226 | doors { | 241 | doors { |
| 227 | name: "Tower Entrance" | 242 | name: "Tower Entrance" |
| @@ -318,6 +333,7 @@ doors { | |||
| 318 | panels { room: "Maze Up Area" name: "UP" } | 333 | panels { room: "Maze Up Area" name: "UP" } |
| 319 | panels { room: "Maze Wreck Area" name: "WRECK" } | 334 | panels { room: "Maze Wreck Area" name: "WRECK" } |
| 320 | location_room: "Maze Slice Area" | 335 | location_room: "Maze Slice Area" |
| 336 | location_name: "Gravestone" | ||
| 321 | } | 337 | } |
| 322 | doors { | 338 | doors { |
| 323 | name: "Courtyard Side Door" | 339 | name: "Courtyard Side Door" |
| @@ -410,7 +426,8 @@ doors { | |||
| 410 | } | 426 | } |
| 411 | doors { | 427 | doors { |
| 412 | name: "Question Room Back Door" | 428 | name: "Question Room Back Door" |
| 413 | type: STANDARD | 429 | type: ITEM_ONLY |
| 430 | legacy_location: true | ||
| 414 | receivers: "Components/Doors/question_11" | 431 | receivers: "Components/Doors/question_11" |
| 415 | panels { room: "Behind Question Area" name: "YEW" answer: "ewe" } | 432 | panels { room: "Behind Question Area" name: "YEW" answer: "ewe" } |
| 416 | location_room: "Behind Question Area" | 433 | location_room: "Behind Question Area" |
| @@ -419,10 +436,10 @@ doors { | |||
| 419 | name: "Invisible Entrance" | 436 | name: "Invisible Entrance" |
| 420 | type: STANDARD | 437 | type: STANDARD |
| 421 | receivers: "Components/Doors/entry_36" | 438 | receivers: "Components/Doors/entry_36" |
| 422 | panels { room: "Back Area" name: "Right Landscape Top" answer: "tell" } | 439 | panels { room: "The Landscapes" name: "Right Landscape Top" answer: "tell" } |
| 423 | panels { room: "Back Area" name: "Right Landscape Left" answer: "eyes" } | 440 | panels { room: "The Landscapes" name: "Right Landscape Left" answer: "eyes" } |
| 424 | location_room: "Back Area" | 441 | location_room: "The Landscapes" |
| 425 | location_name: "Landscapes Room Alternate Answers" | 442 | location_name: "Alternate Answers" |
| 426 | } | 443 | } |
| 427 | doors { | 444 | doors { |
| 428 | name: "Nature Room Door" | 445 | name: "Nature Room Door" |
| @@ -468,12 +485,17 @@ doors { | |||
| 468 | panels { room: "Whole Room" name: "CHIPS" } | 485 | panels { room: "Whole Room" name: "CHIPS" } |
| 469 | panels { room: "Whole Room" name: "TOWER" } | 486 | panels { room: "Whole Room" name: "TOWER" } |
| 470 | location_room: "Whole Room" | 487 | location_room: "Whole Room" |
| 488 | location_name: "Gravestone" | ||
| 471 | } | 489 | } |
| 472 | doors { | 490 | doors { |
| 473 | name: "Lavender Cube" | 491 | name: "Lavender Cube" |
| 474 | type: ITEM_ONLY | 492 | type: EVENT |
| 475 | receivers: "Components/Doors/entry_28" | 493 | receivers: "Components/Doors/entry_28" |
| 476 | switches: "lavender_cubes" | 494 | panels { |
| 495 | map: "the_ancient" | ||
| 496 | room: "Inside" | ||
| 497 | name: "COLOR" | ||
| 498 | } | ||
| 477 | } | 499 | } |
| 478 | doors { | 500 | doors { |
| 479 | name: "Zero Entrance" | 501 | name: "Zero Entrance" |
| @@ -490,6 +512,7 @@ doors { | |||
| 490 | panels { room: "Zero Room" name: "MANY" } | 512 | panels { room: "Zero Room" name: "MANY" } |
| 491 | panels { room: "Zero Room" name: "REMAINING" } | 513 | panels { room: "Zero Room" name: "REMAINING" } |
| 492 | location_room: "Zero Room" | 514 | location_room: "Zero Room" |
| 515 | location_name: "Panels" | ||
| 493 | } | 516 | } |
| 494 | doors { | 517 | doors { |
| 495 | name: "Spiral Painting" | 518 | name: "Spiral Painting" |
| @@ -498,3 +521,110 @@ doors { | |||
| 498 | panels { room: "Back Area" name: "PAINTING" } | 521 | panels { room: "Back Area" name: "PAINTING" } |
| 499 | location_room: "Back Area" | 522 | location_room: "Back Area" |
| 500 | } | 523 | } |
| 524 | doors { | ||
| 525 | name: "Cyan Doors" | ||
| 526 | type: EVENT | ||
| 527 | receivers: "Panels/General/entry_7/teleportListener" | ||
| 528 | double_letters: true | ||
| 529 | } | ||
| 530 | doors { | ||
| 531 | name: "Partial Entrance" | ||
| 532 | type: EVENT | ||
| 533 | panels { room: "West Side" name: "CLUE" } | ||
| 534 | } | ||
| 535 | doors { | ||
| 536 | name: "Why Is It Not Red" | ||
| 537 | type: LOCATION_ONLY | ||
| 538 | panels { room: "Main Area" name: "WHY" } | ||
| 539 | panels { room: "Main Area" name: "IS" } | ||
| 540 | panels { room: "Main Area" name: "IT" } | ||
| 541 | panels { room: "Main Area" name: "NOT" } | ||
| 542 | panels { room: "Main Area" name: "RED" } | ||
| 543 | location_room: "Main Area" | ||
| 544 | location_name: "WHY, IS, IT, NOT, RED" | ||
| 545 | } | ||
| 546 | doors { | ||
| 547 | name: "Control Center Gray Panel" | ||
| 548 | type: LOCATION_ONLY | ||
| 549 | panels { room: "Main Area" name: "COLOR" } | ||
| 550 | location_room: "Main Area" | ||
| 551 | location_name: "COLOR" | ||
| 552 | } | ||
| 553 | doors { | ||
| 554 | name: "Control Center Purple Panel" | ||
| 555 | type: LOCATION_ONLY | ||
| 556 | panels { room: "East Landscape" name: "COLOR" } | ||
| 557 | location_room: "East Landscape" | ||
| 558 | location_name: "COLOR" | ||
| 559 | } | ||
| 560 | doors { | ||
| 561 | name: "Control Center Red Panel" | ||
| 562 | type: LOCATION_ONLY | ||
| 563 | panels { room: "West Side" name: "COLOR" } | ||
| 564 | location_room: "West Side" | ||
| 565 | location_name: "COLOR" | ||
| 566 | } | ||
| 567 | doors { | ||
| 568 | name: "Mistreat Panel" | ||
| 569 | type: LOCATION_ONLY | ||
| 570 | panels { room: "East Landscape" name: "MISTREAT" } | ||
| 571 | location_room: "East Landscape" | ||
| 572 | location_name: "MISTREAT" | ||
| 573 | } | ||
| 574 | doors { | ||
| 575 | name: "Tower Panels" | ||
| 576 | type: LOCATION_ONLY | ||
| 577 | panels { room: "Back Area" name: "TOWEL" } | ||
| 578 | panels { room: "Maze Tower" name: "SPIRE" } | ||
| 579 | location_room: "Maze Tower" | ||
| 580 | location_name: "SPIRE, TOWEL" | ||
| 581 | } | ||
| 582 | doors { | ||
| 583 | name: "Tree Panels" | ||
| 584 | type: LOCATION_ONLY | ||
| 585 | panels { room: "Back Area" name: "PLANT" } | ||
| 586 | panels { room: "Back Area" name: "TREE" } | ||
| 587 | location_room: "Back Area" | ||
| 588 | location_name: "PLANT, TREE" | ||
| 589 | } | ||
| 590 | doors { | ||
| 591 | name: "Behind Question Room Panels" | ||
| 592 | type: LOCATION_ONLY | ||
| 593 | panels { room: "Behind Question Area" name: "DEW" } | ||
| 594 | panels { room: "Behind Question Area" name: "YEW" answer: "ewe" } | ||
| 595 | panels { room: "Behind Question Area" name: "NO" } | ||
| 596 | location_room: "Behind Question Area" | ||
| 597 | location_name: "DEW, YEW/EWE, NO" | ||
| 598 | } | ||
| 599 | doors { | ||
| 600 | name: "Broken Shed Panels" | ||
| 601 | type: LOCATION_ONLY | ||
| 602 | panels { room: "North Landscape" name: "LAUGH" } | ||
| 603 | panels { room: "North Landscape" name: "FINISHED" } | ||
| 604 | panels { room: "North Landscape" name: "LAUGH FINISHED" } | ||
| 605 | location_room: "North Landscape" | ||
| 606 | location_name: "LAUGH, FINISHED, LAUGH FINISHED" | ||
| 607 | } | ||
| 608 | doors { | ||
| 609 | name: "Nature Panels" | ||
| 610 | type: LOCATION_ONLY | ||
| 611 | panels { room: "North Landscape" name: "WEATHER" } | ||
| 612 | panels { room: "North Landscape" name: "ANIMALS" } | ||
| 613 | panels { room: "North Landscape" name: "PLANTS" } | ||
| 614 | location_room: "North Landscape" | ||
| 615 | location_name: "ANIMALS, PLANTS, WEATHER" | ||
| 616 | } | ||
| 617 | doors { | ||
| 618 | name: "Teal Panel" | ||
| 619 | type: LOCATION_ONLY | ||
| 620 | panels { room: "Maze Wreck Area" name: "MAROON" } | ||
| 621 | location_room: "Maze Wreck Area" | ||
| 622 | location_name: "MAROON" | ||
| 623 | } | ||
| 624 | doors { | ||
| 625 | name: "Behind Orb Panel" | ||
| 626 | type: LOCATION_ONLY | ||
| 627 | panels { room: "Main Area" name: "BROWN RED ORANGE" } | ||
| 628 | location_room: "Main Area" | ||
| 629 | location_name: "Brown Red Orange" | ||
| 630 | } | ||
| diff --git a/data/maps/the_great/rooms/Back Area.txtpb b/data/maps/the_great/rooms/Back Area.txtpb index c1b8ab3..33da394 100644 --- a/data/maps/the_great/rooms/Back Area.txtpb +++ b/data/maps/the_great/rooms/Back Area.txtpb | |||
| @@ -28,92 +28,6 @@ panels { | |||
| 28 | answer: "tower" | 28 | answer: "tower" |
| 29 | symbols: SPARKLES | 29 | symbols: SPARKLES |
| 30 | } | 30 | } |
| 31 | panels { | ||
| 32 | name: "Top Landscape Top" | ||
| 33 | path: "Panels/Kiwi Room/kiwi_1" | ||
| 34 | clue: "" | ||
| 35 | answer: "one" | ||
| 36 | symbols: QUESTION | ||
| 37 | } | ||
| 38 | panels { | ||
| 39 | name: "Top Landscape Right" | ||
| 40 | path: "Panels/Kiwi Room/kiwi_2" | ||
| 41 | clue: "" | ||
| 42 | answer: "road" | ||
| 43 | symbols: QUESTION | ||
| 44 | } | ||
| 45 | panels { | ||
| 46 | name: "Top Landscape Bottom" | ||
| 47 | path: "Panels/Kiwi Room/kiwi_3" | ||
| 48 | clue: "" | ||
| 49 | answer: "many" | ||
| 50 | symbols: QUESTION | ||
| 51 | } | ||
| 52 | panels { | ||
| 53 | name: "Top Landscape Left" | ||
| 54 | path: "Panels/Kiwi Room/kiwi_4" | ||
| 55 | clue: "" | ||
| 56 | answer: "turns" | ||
| 57 | symbols: QUESTION | ||
| 58 | } | ||
| 59 | panels { | ||
| 60 | name: "Left Landscape Top" | ||
| 61 | path: "Panels/Kiwi Room/kiwi_5" | ||
| 62 | clue: "" | ||
| 63 | answer: "find" | ||
| 64 | symbols: QUESTION | ||
| 65 | } | ||
| 66 | panels { | ||
| 67 | name: "Left Landscape Right" | ||
| 68 | path: "Panels/Kiwi Room/kiwi_6" | ||
| 69 | clue: "" | ||
| 70 | answer: "keys" | ||
| 71 | symbols: QUESTION | ||
| 72 | } | ||
| 73 | panels { | ||
| 74 | name: "Left Landscape Bottom" | ||
| 75 | path: "Panels/Kiwi Room/kiwi_7" | ||
| 76 | clue: "" | ||
| 77 | answer: "write" | ||
| 78 | symbols: QUESTION | ||
| 79 | } | ||
| 80 | panels { | ||
| 81 | name: "Left Landscape Left" | ||
| 82 | path: "Panels/Kiwi Room/kiwi_8" | ||
| 83 | clue: "" | ||
| 84 | answer: "words" | ||
| 85 | symbols: QUESTION | ||
| 86 | } | ||
| 87 | panels { | ||
| 88 | name: "Right Landscape Top" | ||
| 89 | path: "Panels/Kiwi Room/kiwi_9" | ||
| 90 | clue: "" | ||
| 91 | answer: "hear" | ||
| 92 | symbols: QUESTION | ||
| 93 | proxies { answer: "tell" path: "Panels/Kiwi Proxies/kiwi_9_proxy_1" } | ||
| 94 | } | ||
| 95 | panels { | ||
| 96 | name: "Right Landscape Right" | ||
| 97 | path: "Panels/Kiwi Room/kiwi_10" | ||
| 98 | clue: "" | ||
| 99 | answer: "lies" | ||
| 100 | symbols: QUESTION | ||
| 101 | } | ||
| 102 | panels { | ||
| 103 | name: "Right Landscape Bottom" | ||
| 104 | path: "Panels/Kiwi Room/kiwi_11" | ||
| 105 | clue: "" | ||
| 106 | answer: "the" | ||
| 107 | symbols: QUESTION | ||
| 108 | } | ||
| 109 | panels { | ||
| 110 | name: "Right Landscape Left" | ||
| 111 | path: "Panels/Kiwi Room/kiwi_12" | ||
| 112 | clue: "" | ||
| 113 | answer: "ears" | ||
| 114 | symbols: QUESTION | ||
| 115 | proxies { answer: "eyes" path: "Panels/Kiwi Proxies/kiwi_12_proxy_1" } | ||
| 116 | } | ||
| 117 | paintings { | 31 | paintings { |
| 118 | name: "SPIRAL" | 32 | name: "SPIRAL" |
| 119 | path: "Components/Paintings/spiral" | 33 | path: "Components/Paintings/spiral" |
| @@ -124,23 +38,31 @@ paintings { | |||
| 124 | } | 38 | } |
| 125 | ports { | 39 | ports { |
| 126 | name: "UNKEMPT" | 40 | name: "UNKEMPT" |
| 41 | display_name: "Unkempt Entrance" | ||
| 127 | path: "Meshes/Blocks/Warps/worldport5" | 42 | path: "Meshes/Blocks/Warps/worldport5" |
| 128 | orientation: "north" | 43 | destination { x: 72 y: 0 z: 10.5 } |
| 44 | rotation: 180 | ||
| 129 | } | 45 | } |
| 130 | ports { | 46 | ports { |
| 131 | name: "THREEDOORS" | 47 | name: "THREEDOORS" |
| 48 | display_name: "Three Doors Entrance" | ||
| 132 | path: "Meshes/Blocks/Warps/worldport16" | 49 | path: "Meshes/Blocks/Warps/worldport16" |
| 133 | orientation: "south" | 50 | destination { x: 77 y: 0 z: 33.5 } |
| 51 | rotation: 0 | ||
| 134 | } | 52 | } |
| 135 | ports { | 53 | ports { |
| 136 | name: "TOWER" | 54 | name: "TOWER" |
| 55 | display_name: "Tower Entrance" | ||
| 137 | path: "Meshes/Blocks/Warps/worldport10" | 56 | path: "Meshes/Blocks/Warps/worldport10" |
| 138 | orientation: "south" | 57 | destination { x: 0 y: 0 z: 52 } |
| 58 | rotation: 0 | ||
| 139 | required_door { name: "Tower Entrance" } | 59 | required_door { name: "Tower Entrance" } |
| 140 | # The reverse warp bypasses the door, so there needs to be two oneway connections. | 60 | # The reverse warp bypasses the door, so there needs to be two oneway connections. |
| 141 | } | 61 | } |
| 142 | ports { | 62 | ports { |
| 143 | name: "TREE" | 63 | name: "TREE" |
| 64 | display_name: "Tree Entrance" | ||
| 144 | path: "Meshes/Blocks/Warps/worldport17" | 65 | path: "Meshes/Blocks/Warps/worldport17" |
| 145 | orientation: "north" | 66 | destination { x: 26 y: 0 z: 58 } |
| 67 | rotation: 180 | ||
| 146 | } | 68 | } |
| diff --git a/data/maps/the_great/rooms/Colorful Entrance.txtpb b/data/maps/the_great/rooms/Colorful Entrance.txtpb index cb7eb25..5464698 100644 --- a/data/maps/the_great/rooms/Colorful Entrance.txtpb +++ b/data/maps/the_great/rooms/Colorful Entrance.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Colorful Entrance" | |||
| 2 | panel_display_name: "Pillar Room" | 2 | panel_display_name: "Pillar Room" |
| 3 | ports { | 3 | ports { |
| 4 | name: "COLORFUL" | 4 | name: "COLORFUL" |
| 5 | display_name: "Pillar Room Worldport" | ||
| 5 | path: "Meshes/Blocks/Warps/worldport13" | 6 | path: "Meshes/Blocks/Warps/worldport13" |
| 6 | orientation: "west" | 7 | destination { x: -37.5 y: 0 z: 74 } |
| 8 | rotation: 270 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_great/rooms/Daedalus Entrance.txtpb b/data/maps/the_great/rooms/Daedalus Entrance.txtpb index 003a8a3..abfab99 100644 --- a/data/maps/the_great/rooms/Daedalus Entrance.txtpb +++ b/data/maps/the_great/rooms/Daedalus Entrance.txtpb | |||
| @@ -9,8 +9,10 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "DAEDALUS" | 11 | name: "DAEDALUS" |
| 12 | display_name: "Daedalus Entrance" | ||
| 12 | path: "Meshes/Blocks/Warps/worldport8" | 13 | path: "Meshes/Blocks/Warps/worldport8" |
| 13 | orientation: "south" | 14 | destination { x: 98 y: 2 z: 27.5 } |
| 15 | rotation: 0 | ||
| 14 | required_door { name: "Daedalus Entrance" } | 16 | required_door { name: "Daedalus Entrance" } |
| 15 | # The reverse warp bypasses the door, so there needs to be two oneway connections. | 17 | # The reverse warp bypasses the door, so there needs to be two oneway connections. |
| 16 | } | 18 | } |
| diff --git a/data/maps/the_great/rooms/Hive Entrance.txtpb b/data/maps/the_great/rooms/Hive Entrance.txtpb index cd6ba68..56acc22 100644 --- a/data/maps/the_great/rooms/Hive Entrance.txtpb +++ b/data/maps/the_great/rooms/Hive Entrance.txtpb | |||
| @@ -9,6 +9,8 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "HIVE" | 11 | name: "HIVE" |
| 12 | display_name: "Hive Entrance" | ||
| 12 | path: "Meshes/Blocks/Warps/worldport19" | 13 | path: "Meshes/Blocks/Warps/worldport19" |
| 13 | orientation: "east" | 14 | destination { x: -91.5 y: 0 z: 37 } |
| 15 | rotation: 90 | ||
| 14 | } | 16 | } |
| diff --git a/data/maps/the_great/rooms/Jubilant Entrance.txtpb b/data/maps/the_great/rooms/Jubilant Entrance.txtpb index 0700a6b..b254cc0 100644 --- a/data/maps/the_great/rooms/Jubilant Entrance.txtpb +++ b/data/maps/the_great/rooms/Jubilant Entrance.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Jubilant Entrance" | |||
| 2 | panel_display_name: "West Side" | 2 | panel_display_name: "West Side" |
| 3 | ports { | 3 | ports { |
| 4 | name: "JUBILANT" | 4 | name: "JUBILANT" |
| 5 | display_name: "Jubilant Entrance" | ||
| 5 | path: "Meshes/Blocks/Warps/worldport12" | 6 | path: "Meshes/Blocks/Warps/worldport12" |
| 6 | orientation: "east" | 7 | destination { x: -62 y: 0 z: -19 } |
| 8 | rotation: 90 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_great/rooms/Main Area.txtpb b/data/maps/the_great/rooms/Main Area.txtpb index 18bcf9b..a5ed9f3 100644 --- a/data/maps/the_great/rooms/Main Area.txtpb +++ b/data/maps/the_great/rooms/Main Area.txtpb | |||
| @@ -111,6 +111,7 @@ panels { | |||
| 111 | answer: "high" | 111 | answer: "high" |
| 112 | symbols: SUN | 112 | symbols: SUN |
| 113 | symbols: ZERO | 113 | symbols: ZERO |
| 114 | required_door { name: "Cyan Doors" } | ||
| 114 | } | 115 | } |
| 115 | panels { | 116 | panels { |
| 116 | name: "CURT" | 117 | name: "CURT" |
| @@ -121,27 +122,37 @@ panels { | |||
| 121 | } | 122 | } |
| 122 | ports { | 123 | ports { |
| 123 | name: "ENTRY" | 124 | name: "ENTRY" |
| 125 | display_name: "Entry Building" | ||
| 124 | path: "Meshes/Blocks/Warps/worldport" | 126 | path: "Meshes/Blocks/Warps/worldport" |
| 125 | orientation: "south" | 127 | destination { x: 33 y: 0 z: 15 } |
| 128 | rotation: 0 | ||
| 126 | } | 129 | } |
| 127 | ports { | 130 | ports { |
| 128 | name: "KEEN" | 131 | name: "KEEN" |
| 132 | display_name: "Keen Building Front" | ||
| 129 | path: "Meshes/Blocks/Warps/worldport6" | 133 | path: "Meshes/Blocks/Warps/worldport6" |
| 130 | orientation: "north" | 134 | destination { x: 33 y: 0 z: -21 } |
| 135 | rotation: 180 | ||
| 131 | } | 136 | } |
| 132 | ports { | 137 | ports { |
| 133 | name: "ORB" | 138 | name: "ORB" |
| 139 | display_name: "Orb Building" | ||
| 134 | path: "Meshes/Blocks/Warps/worldport3" | 140 | path: "Meshes/Blocks/Warps/worldport3" |
| 135 | orientation: "north" | 141 | destination { x: 62 y: 0 z: -13 } |
| 142 | rotation: 180 | ||
| 136 | } | 143 | } |
| 137 | ports { | 144 | ports { |
| 138 | name: "LINEAR" | 145 | name: "LINEAR" |
| 146 | display_name: "Keen Building Back" | ||
| 139 | path: "Meshes/Blocks/Warps/worldport15" | 147 | path: "Meshes/Blocks/Warps/worldport15" |
| 140 | orientation: "south" | 148 | destination { x: 33 y: 0 z: -42.5 } |
| 149 | rotation: 0 | ||
| 141 | } | 150 | } |
| 142 | ports { | 151 | ports { |
| 143 | name: "DIGITAL" | 152 | name: "DIGITAL" |
| 153 | display_name: "Digital Hole" | ||
| 144 | path: "Meshes/Blocks/Warps/worldport4" | 154 | path: "Meshes/Blocks/Warps/worldport4" |
| 145 | orientation: "down" | 155 | destination { x: -6.5 y: 0 z: 7.5 } |
| 156 | rotation: 0 | ||
| 146 | required_door { name: "Digital Entrance" } | 157 | required_door { name: "Digital Entrance" } |
| 147 | } | 158 | } |
| diff --git a/data/maps/the_great/rooms/Maze Tower.txtpb b/data/maps/the_great/rooms/Maze Tower.txtpb index 44c30d7..3b1e5fc 100644 --- a/data/maps/the_great/rooms/Maze Tower.txtpb +++ b/data/maps/the_great/rooms/Maze Tower.txtpb | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | name: "Maze Tower" | 1 | name: "Maze Tower" |
| 2 | panel_display_name: "Courtyard" | ||
| 3 | panels { | 2 | panels { |
| 4 | name: "FEEL" | 3 | name: "FEEL" |
| 5 | path: "Panels/Maze/maze_12" | 4 | path: "Panels/Maze/maze_12" |
| diff --git a/data/maps/the_great/rooms/North Landscape.txtpb b/data/maps/the_great/rooms/North Landscape.txtpb index fb11c42..f738ed3 100644 --- a/data/maps/the_great/rooms/North Landscape.txtpb +++ b/data/maps/the_great/rooms/North Landscape.txtpb | |||
| @@ -56,8 +56,9 @@ keyholders { | |||
| 56 | } | 56 | } |
| 57 | ports { | 57 | ports { |
| 58 | name: "INVISIBLE" | 58 | name: "INVISIBLE" |
| 59 | display_name: "Eye Worldport" | ||
| 59 | path: "Meshes/Blocks/Warps/worldport20" | 60 | path: "Meshes/Blocks/Warps/worldport20" |
| 60 | orientation: "north" | 61 | destination { x: 33 y: 0 z: -66.5 } |
| 61 | # Note that this can be easily entered from the other side. | 62 | rotation: 0 |
| 62 | required_door { name: "Invisible Entrance" } | 63 | required_door { name: "Invisible Entrance" } |
| 63 | } | 64 | } |
| diff --git a/data/maps/the_great/rooms/Purple Room.txtpb b/data/maps/the_great/rooms/Purple Room.txtpb index e505ca0..12e79e7 100644 --- a/data/maps/the_great/rooms/Purple Room.txtpb +++ b/data/maps/the_great/rooms/Purple Room.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Purple Room" | |||
| 2 | panel_display_name: "Main Area" | 2 | panel_display_name: "Main Area" |
| 3 | ports { | 3 | ports { |
| 4 | name: "DAEDALUS" | 4 | name: "DAEDALUS" |
| 5 | display_name: "Purple Hallway" | ||
| 5 | path: "Meshes/Blocks/Warps/worldport18" | 6 | path: "Meshes/Blocks/Warps/worldport18" |
| 6 | orientation: "north" | 7 | destination { x: 158 y: 0 z: 14 } |
| 8 | rotation: 180 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_great/rooms/Salmon Room.txtpb b/data/maps/the_great/rooms/Salmon Room.txtpb index ecdef75..8458829 100644 --- a/data/maps/the_great/rooms/Salmon Room.txtpb +++ b/data/maps/the_great/rooms/Salmon Room.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Salmon Room" | |||
| 2 | panel_display_name: "Main Area" | 2 | panel_display_name: "Main Area" |
| 3 | ports { | 3 | ports { |
| 4 | name: "BETWEEN" | 4 | name: "BETWEEN" |
| 5 | display_name: "Salmon Hallway" | ||
| 5 | path: "Meshes/Blocks/Warps/worldport11" | 6 | path: "Meshes/Blocks/Warps/worldport11" |
| 6 | orientation: "east" | 7 | destination { x: 83 y: 0 z: -21 } |
| 8 | rotation: 90 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_great/rooms/Talented Entrance.txtpb b/data/maps/the_great/rooms/Talented Entrance.txtpb index 7329853..53c27dc 100644 --- a/data/maps/the_great/rooms/Talented Entrance.txtpb +++ b/data/maps/the_great/rooms/Talented Entrance.txtpb | |||
| @@ -2,6 +2,8 @@ name: "Talented Entrance" | |||
| 2 | panel_display_name: "Question Room" | 2 | panel_display_name: "Question Room" |
| 3 | ports { | 3 | ports { |
| 4 | name: "TALENTED" | 4 | name: "TALENTED" |
| 5 | display_name: "Under Question Room Worldport" | ||
| 5 | path: "Meshes/Blocks/Warps/worldport14" | 6 | path: "Meshes/Blocks/Warps/worldport14" |
| 6 | orientation: "south" | 7 | destination { x: 109 y: -6 z: -26.5 } |
| 8 | rotation: 0 | ||
| 7 | } | 9 | } |
| diff --git a/data/maps/the_great/rooms/The Landscapes.txtpb b/data/maps/the_great/rooms/The Landscapes.txtpb new file mode 100644 index 0000000..2912843 --- /dev/null +++ b/data/maps/the_great/rooms/The Landscapes.txtpb | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | name: "The Landscapes" | ||
| 2 | panel_display_name: "The Landscapes" | ||
| 3 | panels { | ||
| 4 | name: "Top Landscape Top" | ||
| 5 | path: "Panels/Kiwi Room/kiwi_1" | ||
| 6 | clue: "" | ||
| 7 | answer: "one" | ||
| 8 | symbols: QUESTION | ||
| 9 | } | ||
| 10 | panels { | ||
| 11 | name: "Top Landscape Right" | ||
| 12 | path: "Panels/Kiwi Room/kiwi_2" | ||
| 13 | clue: "" | ||
| 14 | answer: "road" | ||
| 15 | symbols: QUESTION | ||
| 16 | } | ||
| 17 | panels { | ||
| 18 | name: "Top Landscape Bottom" | ||
| 19 | path: "Panels/Kiwi Room/kiwi_3" | ||
| 20 | clue: "" | ||
| 21 | answer: "many" | ||
| 22 | symbols: QUESTION | ||
| 23 | } | ||
| 24 | panels { | ||
| 25 | name: "Top Landscape Left" | ||
| 26 | path: "Panels/Kiwi Room/kiwi_4" | ||
| 27 | clue: "" | ||
| 28 | answer: "turns" | ||
| 29 | symbols: QUESTION | ||
| 30 | } | ||
| 31 | panels { | ||
| 32 | name: "Left Landscape Top" | ||
| 33 | path: "Panels/Kiwi Room/kiwi_5" | ||
| 34 | clue: "" | ||
| 35 | answer: "find" | ||
| 36 | symbols: QUESTION | ||
| 37 | } | ||
| 38 | panels { | ||
| 39 | name: "Left Landscape Right" | ||
| 40 | path: "Panels/Kiwi Room/kiwi_6" | ||
| 41 | clue: "" | ||
| 42 | answer: "keys" | ||
| 43 | symbols: QUESTION | ||
| 44 | } | ||
| 45 | panels { | ||
| 46 | name: "Left Landscape Bottom" | ||
| 47 | path: "Panels/Kiwi Room/kiwi_7" | ||
| 48 | clue: "" | ||
| 49 | answer: "write" | ||
| 50 | symbols: QUESTION | ||
| 51 | } | ||
| 52 | panels { | ||
| 53 | name: "Left Landscape Left" | ||
| 54 | path: "Panels/Kiwi Room/kiwi_8" | ||
| 55 | clue: "" | ||
| 56 | answer: "words" | ||
| 57 | symbols: QUESTION | ||
| 58 | } | ||
| 59 | panels { | ||
| 60 | name: "Right Landscape Top" | ||
| 61 | path: "Panels/Kiwi Room/kiwi_9" | ||
| 62 | clue: "" | ||
| 63 | answer: "hear" | ||
| 64 | symbols: QUESTION | ||
| 65 | proxies { answer: "tell" path: "Panels/Kiwi Proxies/kiwi_9_proxy_1" } | ||
| 66 | } | ||
| 67 | panels { | ||
| 68 | name: "Right Landscape Right" | ||
| 69 | path: "Panels/Kiwi Room/kiwi_10" | ||
| 70 | clue: "" | ||
| 71 | answer: "lies" | ||
| 72 | symbols: QUESTION | ||
| 73 | } | ||
| 74 | panels { | ||
| 75 | name: "Right Landscape Bottom" | ||
| 76 | path: "Panels/Kiwi Room/kiwi_11" | ||
| 77 | clue: "" | ||
| 78 | answer: "the" | ||
| 79 | symbols: QUESTION | ||
| 80 | } | ||
| 81 | panels { | ||
| 82 | name: "Right Landscape Left" | ||
| 83 | path: "Panels/Kiwi Room/kiwi_12" | ||
| 84 | clue: "" | ||
| 85 | answer: "ears" | ||
| 86 | symbols: QUESTION | ||
| 87 | proxies { answer: "eyes" path: "Panels/Kiwi Proxies/kiwi_12_proxy_1" } | ||
| 88 | } | ||
| diff --git a/data/maps/the_great/rooms/West Side.txtpb b/data/maps/the_great/rooms/West Side.txtpb index daf1718..9f098ee 100644 --- a/data/maps/the_great/rooms/West Side.txtpb +++ b/data/maps/the_great/rooms/West Side.txtpb | |||
| @@ -63,17 +63,24 @@ paintings { | |||
| 63 | } | 63 | } |
| 64 | ports { | 64 | ports { |
| 65 | name: "IMPRESSIVE" | 65 | name: "IMPRESSIVE" |
| 66 | display_name: "Impressive Entrance" | ||
| 66 | path: "Meshes/Blocks/Warps/worldport2" | 67 | path: "Meshes/Blocks/Warps/worldport2" |
| 67 | orientation: "south" | 68 | destination { x: -34 y: 0 z: 22.5 } |
| 69 | rotation: 0 | ||
| 68 | } | 70 | } |
| 69 | ports { | 71 | ports { |
| 70 | name: "CC" | 72 | name: "CC" |
| 73 | display_name: "Control Center" | ||
| 71 | path: "Meshes/Blocks/Warps/worldport9" | 74 | path: "Meshes/Blocks/Warps/worldport9" |
| 72 | orientation: "north" | 75 | destination { x: -59 y: 0 z: -50.5 } |
| 76 | rotation: 180 | ||
| 73 | } | 77 | } |
| 74 | ports { | 78 | ports { |
| 75 | name: "PARTIAL" | 79 | name: "PARTIAL" |
| 80 | display_name: "Red Hallway" | ||
| 76 | path: "Meshes/Blocks/Warps/worldport7" | 81 | path: "Meshes/Blocks/Warps/worldport7" |
| 77 | orientation: "east" | 82 | destination { x: -62 y: 0 z: 11 } |
| 83 | rotation: 90 | ||
| 78 | # ER with this is weird; make sure to place on the surface | 84 | # ER with this is weird; make sure to place on the surface |
| 85 | required_door { name: "Partial Entrance" } | ||
| 79 | } | 86 | } |
| diff --git a/data/maps/the_great/rooms/Whole Room.txtpb b/data/maps/the_great/rooms/Whole Room.txtpb index daa174c..989241a 100644 --- a/data/maps/the_great/rooms/Whole Room.txtpb +++ b/data/maps/the_great/rooms/Whole Room.txtpb | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | name: "Whole Room" | 1 | name: "Whole Room" |
| 2 | panel_display_name: "North Landscape" | 2 | panel_display_name: "Building Building" |
| 3 | panels { | 3 | panels { |
| 4 | name: "VAULT" | 4 | name: "VAULT" |
| 5 | path: "Panels/Whole Room/whole_1" | 5 | path: "Panels/Whole Room/whole_1" |
| diff --git a/data/maps/the_hinterlands/rooms/Main Area.txtpb b/data/maps/the_hinterlands/rooms/Main Area.txtpb index 8daac05..5cd626b 100644 --- a/data/maps/the_hinterlands/rooms/Main Area.txtpb +++ b/data/maps/the_hinterlands/rooms/Main Area.txtpb | |||
| @@ -2,11 +2,15 @@ name: "Main Area" | |||
| 2 | # I'm probably not going to include any of the panels in here. | 2 | # I'm probably not going to include any of the panels in here. |
| 3 | ports { | 3 | ports { |
| 4 | name: "RIGHT" | 4 | name: "RIGHT" |
| 5 | display_name: "South Worldport" | ||
| 5 | path: "Components/Warps/worldport" | 6 | path: "Components/Warps/worldport" |
| 6 | orientation: "east" | 7 | destination { x: 30 y: 0 z: 19 } |
| 8 | rotation: 270 | ||
| 7 | } | 9 | } |
| 8 | ports { | 10 | ports { |
| 9 | name: "LEFT" | 11 | name: "LEFT" |
| 12 | display_name: "North Worldport" | ||
| 10 | path: "Components/Warps/worldport2" | 13 | path: "Components/Warps/worldport2" |
| 11 | orientation: "east" | 14 | destination { x: 30 y: 0 z: -76 } |
| 15 | rotation: 270 | ||
| 12 | } | 16 | } |
| diff --git a/data/maps/the_hive/rooms/Main Area.txtpb b/data/maps/the_hive/rooms/Main Area.txtpb index 013390a..aaf8e2a 100644 --- a/data/maps/the_hive/rooms/Main Area.txtpb +++ b/data/maps/the_hive/rooms/Main Area.txtpb | |||
| @@ -272,21 +272,29 @@ keyholders { | |||
| 272 | } | 272 | } |
| 273 | ports { | 273 | ports { |
| 274 | name: "DAED1" | 274 | name: "DAED1" |
| 275 | display_name: "Blue Area Worldport" | ||
| 275 | path: "Components/Warps/worldport" | 276 | path: "Components/Warps/worldport" |
| 276 | orientation: "west" | 277 | destination { x: -1.5 y: 0 z: 24 } |
| 278 | rotation: 270 | ||
| 277 | } | 279 | } |
| 278 | ports { | 280 | ports { |
| 279 | name: "DAED2" | 281 | name: "DAED2" |
| 282 | display_name: "Pink Area South Worldport" | ||
| 280 | path: "Components/Warps/worldport2" | 283 | path: "Components/Warps/worldport2" |
| 281 | orientation: "west" | 284 | destination { x: -26.5 y: 0 z: -22 } |
| 285 | rotation: 270 | ||
| 282 | } | 286 | } |
| 283 | ports { | 287 | ports { |
| 284 | name: "DAED3" | 288 | name: "DAED3" |
| 289 | display_name: "Lime Area Worldport" | ||
| 285 | path: "Components/Warps/worldport3" | 290 | path: "Components/Warps/worldport3" |
| 286 | orientation: "east" | 291 | destination { x: -44.5 y: 0 z: -13 } |
| 292 | rotation: 90 | ||
| 287 | } | 293 | } |
| 288 | ports { | 294 | ports { |
| 289 | name: "GREAT" | 295 | name: "GREAT" |
| 296 | display_name: "Pink Area North Worldport" | ||
| 290 | path: "Components/Warps/worldport4" | 297 | path: "Components/Warps/worldport4" |
| 291 | orientation: "west" | 298 | destination { x: -29.5 y: 0 z: -62 } |
| 299 | rotation: 270 | ||
| 292 | } | 300 | } |
| diff --git a/data/maps/the_impressive/doors.txtpb b/data/maps/the_impressive/doors.txtpb index e27d531..9ab6845 100644 --- a/data/maps/the_impressive/doors.txtpb +++ b/data/maps/the_impressive/doors.txtpb | |||
| @@ -30,8 +30,25 @@ doors { | |||
| 30 | panels { room: "Green Eye" name: "LEFT" } | 30 | panels { room: "Green Eye" name: "LEFT" } |
| 31 | } | 31 | } |
| 32 | doors { | 32 | doors { |
| 33 | name: "Green Eye Panels" | ||
| 34 | type: LOCATION_ONLY | ||
| 35 | panels { room: "Green Eye" name: "RETURN" } | ||
| 36 | panels { room: "Green Eye" name: "TO" } | ||
| 37 | panels { room: "Green Eye" name: "LEFT" } | ||
| 38 | location_room: "Green Eye" | ||
| 39 | location_name: "RETURN, TO, LEFT" | ||
| 40 | } | ||
| 41 | doors { | ||
| 33 | name: "Control Center Green Door" | 42 | name: "Control Center Green Door" |
| 34 | type: CONTROL_CENTER_COLOR | 43 | type: CONTROL_CENTER_COLOR |
| 44 | latch: true | ||
| 35 | receivers: "Components/Doors/entry_2" | 45 | receivers: "Components/Doors/entry_2" |
| 36 | control_center_color: "green" | 46 | control_center_color: "green" |
| 37 | } | 47 | } |
| 48 | doors { | ||
| 49 | name: "Control Center Green Panel" | ||
| 50 | type: LOCATION_ONLY | ||
| 51 | panels { room: "Side Area" name: "COLOR" } | ||
| 52 | location_room: "Side Area" | ||
| 53 | location_name: "COLOR" | ||
| 54 | } | ||
| diff --git a/data/maps/the_impressive/rooms/Green Eye.txtpb b/data/maps/the_impressive/rooms/Green Eye.txtpb index b4a115b..aa31b07 100644 --- a/data/maps/the_impressive/rooms/Green Eye.txtpb +++ b/data/maps/the_impressive/rooms/Green Eye.txtpb | |||
| @@ -22,6 +22,8 @@ panels { | |||
| 22 | } | 22 | } |
| 23 | ports { | 23 | ports { |
| 24 | name: "PLAZA" | 24 | name: "PLAZA" |
| 25 | display_name: "Green Hallway" | ||
| 25 | path: "Components/Warps/worldport3" | 26 | path: "Components/Warps/worldport3" |
| 26 | orientation: "east" | 27 | destination { x: -33 y: 0 z: 1 } |
| 28 | rotation: 90 | ||
| 27 | } | 29 | } |
| diff --git a/data/maps/the_impressive/rooms/Lobby.txtpb b/data/maps/the_impressive/rooms/Lobby.txtpb index 3c565fe..577a051 100644 --- a/data/maps/the_impressive/rooms/Lobby.txtpb +++ b/data/maps/the_impressive/rooms/Lobby.txtpb | |||
| @@ -9,6 +9,8 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "GREAT" | 11 | name: "GREAT" |
| 12 | display_name: "Main Entrance" | ||
| 12 | path: "Components/Warps/worldport" | 13 | path: "Components/Warps/worldport" |
| 13 | orientation: "south" | 14 | destination { x: 0 y: 0 z: 11.5 } |
| 15 | rotation: 0 | ||
| 14 | } | 16 | } |
| diff --git a/data/maps/the_impressive/rooms/Side Area.txtpb b/data/maps/the_impressive/rooms/Side Area.txtpb index 1dfb12b..d1b28a3 100644 --- a/data/maps/the_impressive/rooms/Side Area.txtpb +++ b/data/maps/the_impressive/rooms/Side Area.txtpb | |||
| @@ -9,6 +9,8 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "FOURROOMS" | 11 | name: "FOURROOMS" |
| 12 | display_name: "Four Rooms Entrance" | ||
| 12 | path: "Components/Warps/worldport2" | 13 | path: "Components/Warps/worldport2" |
| 13 | orientation: "south" | 14 | destination { x: -27 y: 0 z: 25.5 } |
| 15 | rotation: 0 | ||
| 14 | } | 16 | } |
| diff --git a/data/maps/the_invisible/rooms/Entrance.txtpb b/data/maps/the_invisible/rooms/Entrance.txtpb index bfc7223..c74b968 100644 --- a/data/maps/the_invisible/rooms/Entrance.txtpb +++ b/data/maps/the_invisible/rooms/Entrance.txtpb | |||
| @@ -8,6 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "ENTRY" | 10 | name: "ENTRY" |
| 11 | display_name: "Entrance" | ||
| 11 | path: "Components/Warps/worldport2" | 12 | path: "Components/Warps/worldport2" |
| 12 | orientation: "north" | 13 | destination { x: 0 y: 0 z: -57 } |
| 14 | rotation: 180 | ||
| 13 | } | 15 | } |
| diff --git a/data/maps/the_invisible/rooms/Maze.txtpb b/data/maps/the_invisible/rooms/Maze.txtpb index 895817a..46f3fbc 100644 --- a/data/maps/the_invisible/rooms/Maze.txtpb +++ b/data/maps/the_invisible/rooms/Maze.txtpb | |||
| @@ -5,6 +5,9 @@ masteries { | |||
| 5 | } | 5 | } |
| 6 | ports { | 6 | ports { |
| 7 | name: "ENTRY" | 7 | name: "ENTRY" |
| 8 | display_name: "Exit" | ||
| 8 | path: "Components/Warps/worldport" | 9 | path: "Components/Warps/worldport" |
| 9 | orientation: "south" | 10 | # Should this be shuffleable? It skips the maze lol. |
| 11 | destination { x: 0 y: 0 z: 9.5 } | ||
| 12 | rotation: 0 | ||
| 10 | } | 13 | } |
| diff --git a/data/maps/the_jubilant/doors.txtpb b/data/maps/the_jubilant/doors.txtpb index 02db1ff..90bfd0f 100644 --- a/data/maps/the_jubilant/doors.txtpb +++ b/data/maps/the_jubilant/doors.txtpb | |||
| @@ -31,3 +31,14 @@ doors { | |||
| 31 | panels { room: "Main Area" name: "QUEEN" answer: "jack" } | 31 | panels { room: "Main Area" name: "QUEEN" answer: "jack" } |
| 32 | location_room: "Main Area" | 32 | location_room: "Main Area" |
| 33 | } | 33 | } |
| 34 | doors { | ||
| 35 | name: "Side Room Puzzles" | ||
| 36 | type: LOCATION_ONLY | ||
| 37 | panels { room: "Side Area" name: "CALLBACK" } | ||
| 38 | panels { room: "Side Area" name: "CALL" } | ||
| 39 | panels { room: "Side Area" name: "PUSHBACK" } | ||
| 40 | panels { room: "Side Area" name: "PUSH" } | ||
| 41 | panels { room: "Side Area" name: "FLASHBACK" } | ||
| 42 | panels { room: "Side Area" name: "FLASH" } | ||
| 43 | location_room: "Side Area" | ||
| 44 | } | ||
| diff --git a/data/maps/the_jubilant/metadata.txtpb b/data/maps/the_jubilant/metadata.txtpb index 7de7b85..4af1874 100644 --- a/data/maps/the_jubilant/metadata.txtpb +++ b/data/maps/the_jubilant/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Jubilant" | 1 | display_name: "The Jubilant" |
| 2 | worldport_entrance { | ||
| 3 | room: "Main Area" | ||
| 4 | name: "GREAT" | ||
| 5 | } | ||
| diff --git a/data/maps/the_jubilant/rooms/Main Area.txtpb b/data/maps/the_jubilant/rooms/Main Area.txtpb index 3b91f6d..b38fafd 100644 --- a/data/maps/the_jubilant/rooms/Main Area.txtpb +++ b/data/maps/the_jubilant/rooms/Main Area.txtpb | |||
| @@ -97,6 +97,8 @@ panels { | |||
| 97 | } | 97 | } |
| 98 | ports { | 98 | ports { |
| 99 | name: "GREAT" | 99 | name: "GREAT" |
| 100 | display_name: "Entrance" | ||
| 100 | path: "Components/Warps/worldport" | 101 | path: "Components/Warps/worldport" |
| 101 | orientation: "west" | 102 | destination { x: -3 y: 0 z: 9 } |
| 103 | rotation: 270 | ||
| 102 | } | 104 | } |
| diff --git a/data/maps/the_keen/metadata.txtpb b/data/maps/the_keen/metadata.txtpb index 669f608..909f420 100644 --- a/data/maps/the_keen/metadata.txtpb +++ b/data/maps/the_keen/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Keen" | 1 | display_name: "The Keen" |
| 2 | worldport_entrance { | ||
| 3 | room: "Main Area" | ||
| 4 | name: "GREAT" | ||
| 5 | } | ||
| diff --git a/data/maps/the_keen/rooms/Main Area.txtpb b/data/maps/the_keen/rooms/Main Area.txtpb index eacd131..32d399a 100644 --- a/data/maps/the_keen/rooms/Main Area.txtpb +++ b/data/maps/the_keen/rooms/Main Area.txtpb | |||
| @@ -69,6 +69,8 @@ panels { | |||
| 69 | } | 69 | } |
| 70 | ports { | 70 | ports { |
| 71 | name: "GREAT" | 71 | name: "GREAT" |
| 72 | display_name: "Entrance" | ||
| 72 | path: "Components/Warps/worldport" | 73 | path: "Components/Warps/worldport" |
| 73 | orientation: "south" | 74 | destination { x: 0 y: 0 z: 7.5 } |
| 75 | rotation: 0 | ||
| 74 | } | 76 | } |
| diff --git a/data/maps/the_liberated/metadata.txtpb b/data/maps/the_liberated/metadata.txtpb index a92d7e5..b9b4321 100644 --- a/data/maps/the_liberated/metadata.txtpb +++ b/data/maps/the_liberated/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Liberated" | 1 | display_name: "The Liberated" |
| 2 | worldport_entrance { | ||
| 3 | room: "Puzzle Room" | ||
| 4 | name: "ENTRY" | ||
| 5 | } | ||
| diff --git a/data/maps/the_liberated/rooms/Puzzle Room.txtpb b/data/maps/the_liberated/rooms/Puzzle Room.txtpb index 0223b44..2103bfa 100644 --- a/data/maps/the_liberated/rooms/Puzzle Room.txtpb +++ b/data/maps/the_liberated/rooms/Puzzle Room.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "ENTRY" | 59 | name: "ENTRY" |
| 60 | display_name: "Entrance" | ||
| 60 | path: "Components/Warps/worldport" | 61 | path: "Components/Warps/worldport" |
| 61 | orientation: "south" | 62 | destination { x: 0 y: 0 z: 7.5 } |
| 63 | rotation: 0 | ||
| 62 | } | 64 | } |
| diff --git a/data/maps/the_linear/metadata.txtpb b/data/maps/the_linear/metadata.txtpb index 989463d..838bb2b 100644 --- a/data/maps/the_linear/metadata.txtpb +++ b/data/maps/the_linear/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Linear" | 1 | display_name: "The Linear" |
| 2 | worldport_entrance { | ||
| 3 | room: "Room" | ||
| 4 | name: "GREAT" | ||
| 5 | } | ||
| diff --git a/data/maps/the_linear/rooms/Room.txtpb b/data/maps/the_linear/rooms/Room.txtpb index c47ce0c..ac03fd9 100644 --- a/data/maps/the_linear/rooms/Room.txtpb +++ b/data/maps/the_linear/rooms/Room.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "GREAT" | 59 | name: "GREAT" |
| 60 | display_name: "Entrance" | ||
| 60 | path: "Components/Warps/worldport" | 61 | path: "Components/Warps/worldport" |
| 61 | orientation: "south" | 62 | destination { x: 0 y: 0 z: 7.5 } |
| 63 | rotation: 0 | ||
| 62 | } | 64 | } |
| diff --git a/data/maps/the_lionized/metadata.txtpb b/data/maps/the_lionized/metadata.txtpb index 38fd5c2..8d6168d 100644 --- a/data/maps/the_lionized/metadata.txtpb +++ b/data/maps/the_lionized/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Lionized" | 1 | display_name: "The Lionized" |
| 2 | worldport_entrance { | ||
| 3 | room: "Puzzle Room" | ||
| 4 | name: "ENTRY" | ||
| 5 | } | ||
| diff --git a/data/maps/the_lionized/rooms/Puzzle Room.txtpb b/data/maps/the_lionized/rooms/Puzzle Room.txtpb index 22b72ac..3a5e267 100644 --- a/data/maps/the_lionized/rooms/Puzzle Room.txtpb +++ b/data/maps/the_lionized/rooms/Puzzle Room.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "ENTRY" | 59 | name: "ENTRY" |
| 60 | display_name: "Entrance" | ||
| 60 | path: "Components/Warps/worldport" | 61 | path: "Components/Warps/worldport" |
| 61 | orientation: "south" | 62 | destination { x: 0 y: 0 z: 6.5 } |
| 63 | rotation: 0 | ||
| 62 | } | 64 | } |
| diff --git a/data/maps/the_literate/metadata.txtpb b/data/maps/the_literate/metadata.txtpb index 76d1df6..0e04306 100644 --- a/data/maps/the_literate/metadata.txtpb +++ b/data/maps/the_literate/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Literate" | 1 | display_name: "The Literate" |
| 2 | worldport_entrance { | ||
| 3 | room: "Puzzle Room" | ||
| 4 | name: "ENTRY" | ||
| 5 | } | ||
| diff --git a/data/maps/the_literate/rooms/Puzzle Room.txtpb b/data/maps/the_literate/rooms/Puzzle Room.txtpb index 3927702..c65d408 100644 --- a/data/maps/the_literate/rooms/Puzzle Room.txtpb +++ b/data/maps/the_literate/rooms/Puzzle Room.txtpb | |||
| @@ -57,6 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "ENTRY" | 59 | name: "ENTRY" |
| 60 | display_name: "Entrance" | ||
| 60 | path: "Components/Warps/worldport" | 61 | path: "Components/Warps/worldport" |
| 61 | orientation: "south" | 62 | destination { x: 0 y: 0 z: 7.5 } |
| 63 | rotation: 0 | ||
| 62 | } | 64 | } |
| diff --git a/data/maps/the_lively/metadata.txtpb b/data/maps/the_lively/metadata.txtpb index cbf11c2..acd1177 100644 --- a/data/maps/the_lively/metadata.txtpb +++ b/data/maps/the_lively/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Lively" | 1 | display_name: "The Lively" |
| 2 | worldport_entrance { | ||
| 3 | room: "Puzzle Room" | ||
| 4 | name: "BETWEEN" | ||
| 5 | } | ||
| diff --git a/data/maps/the_lively/rooms/Puzzle Room.txtpb b/data/maps/the_lively/rooms/Puzzle Room.txtpb index b33a122..4918476 100644 --- a/data/maps/the_lively/rooms/Puzzle Room.txtpb +++ b/data/maps/the_lively/rooms/Puzzle Room.txtpb | |||
| @@ -57,5 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "BETWEEN" | 59 | name: "BETWEEN" |
| 60 | display_name: "Entrance" | ||
| 60 | path: "Components/Warps/worldport" | 61 | path: "Components/Warps/worldport" |
| 62 | destination { x: 0 y: 0 z: 6.5 } | ||
| 63 | rotation: 0 | ||
| 61 | } | 64 | } |
| diff --git a/data/maps/the_nuanced/doors.txtpb b/data/maps/the_nuanced/doors.txtpb index 9b58001..300524b 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" } |
| @@ -52,3 +52,10 @@ doors { | |||
| 52 | panels { room: "Back Room" name: "LIMB" } | 52 | panels { room: "Back Room" name: "LIMB" } |
| 53 | panels { room: "Back Room" name: "SPARE" } | 53 | panels { room: "Back Room" name: "SPARE" } |
| 54 | } | 54 | } |
| 55 | doors { | ||
| 56 | name: "Stores Panel" | ||
| 57 | type: LOCATION_ONLY | ||
| 58 | panels { room: "Main Room" name: "TORE" } | ||
| 59 | location_room: "Main Room" | ||
| 60 | location_name: "TORE" | ||
| 61 | } | ||
| diff --git a/data/maps/the_nuanced/metadata.txtpb b/data/maps/the_nuanced/metadata.txtpb index 9c39826..4ac9b13 100644 --- a/data/maps/the_nuanced/metadata.txtpb +++ b/data/maps/the_nuanced/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Nuanced" | 1 | display_name: "The Nuanced" |
| 2 | worldport_entrance { | ||
| 3 | room: "Main Room" | ||
| 4 | name: "UNYIELDING" | ||
| 5 | } | ||
| diff --git a/data/maps/the_nuanced/rooms/Main Room.txtpb b/data/maps/the_nuanced/rooms/Main Room.txtpb index da89bd8..ce4310e 100644 --- a/data/maps/the_nuanced/rooms/Main Room.txtpb +++ b/data/maps/the_nuanced/rooms/Main Room.txtpb | |||
| @@ -106,8 +106,10 @@ panels { | |||
| 106 | } | 106 | } |
| 107 | ports { | 107 | ports { |
| 108 | name: "UNYIELDING" | 108 | name: "UNYIELDING" |
| 109 | display_name: "Entrance" | ||
| 109 | path: "Components/Warps/worldport" | 110 | path: "Components/Warps/worldport" |
| 110 | orientation: "west" | 111 | destination { x: -3.5 y: 0 z: 21 } |
| 112 | rotation: 270 | ||
| 111 | } | 113 | } |
| 112 | keyholders { | 114 | keyholders { |
| 113 | name: "S" | 115 | name: "S" |
| diff --git a/data/maps/the_orb/connections.txtpb b/data/maps/the_orb/connections.txtpb index 62a7643..b902711 100644 --- a/data/maps/the_orb/connections.txtpb +++ b/data/maps/the_orb/connections.txtpb | |||
| @@ -10,10 +10,20 @@ connections { | |||
| 10 | } | 10 | } |
| 11 | connections { | 11 | connections { |
| 12 | from_room: "Main Area" | 12 | from_room: "Main Area" |
| 13 | to_room: "B Room" | 13 | to_room: "Middle Room" |
| 14 | door { name: "B Puzzles" } | 14 | door { name: "B Puzzles" } |
| 15 | } | 15 | } |
| 16 | connections { | 16 | connections { |
| 17 | from_room: "Middle Room" | ||
| 18 | to_room: "B Room" | ||
| 19 | oneway: true | ||
| 20 | } | ||
| 21 | connections { | ||
| 22 | from_room: "Middle Room" | ||
| 23 | to_room: "Main Area" | ||
| 24 | oneway: true | ||
| 25 | } | ||
| 26 | connections { | ||
| 17 | from_room: "B Room" | 27 | from_room: "B Room" |
| 18 | to_room: "Main Area" | 28 | to_room: "Main Area" |
| 19 | oneway: true | 29 | oneway: true |
| diff --git a/data/maps/the_orb/rooms/B Room.txtpb b/data/maps/the_orb/rooms/B Room.txtpb index 0324647..633232f 100644 --- a/data/maps/the_orb/rooms/B Room.txtpb +++ b/data/maps/the_orb/rooms/B Room.txtpb | |||
| @@ -9,19 +9,10 @@ paintings { | |||
| 9 | # TODO: This is too high up to enter. It's also a hint painting. | 9 | # TODO: This is too high up to enter. It's also a hint painting. |
| 10 | exit_only: true | 10 | exit_only: true |
| 11 | } | 11 | } |
| 12 | # TODO: Should these two be independent for shuffling purposes, or always tied | ||
| 13 | # to the Main Area's port? | ||
| 14 | ports { | ||
| 15 | name: "MID" | ||
| 16 | path: "Components/Warps/worldport4" | ||
| 17 | orientation: "south" | ||
| 18 | # This port is in the room immediately after solving the B puzzles, which | ||
| 19 | # means it seems like it would be inaccessible if you enter the map from the | ||
| 20 | # painting or from the final port, but entering the O or R areas brings you | ||
| 21 | # back to the beginning. | ||
| 22 | } | ||
| 23 | ports { | 12 | ports { |
| 24 | name: "FINAL" | 13 | name: "FINAL" |
| 14 | display_name: "Final Worldport" | ||
| 25 | path: "Components/Warps/worldport5" | 15 | path: "Components/Warps/worldport5" |
| 26 | orientation: "south" | 16 | destination { x: -69 y: 0 z: 87 } |
| 17 | rotation: 90 | ||
| 27 | } | 18 | } |
| diff --git a/data/maps/the_orb/rooms/Main Area.txtpb b/data/maps/the_orb/rooms/Main Area.txtpb index 4fcac29..976c489 100644 --- a/data/maps/the_orb/rooms/Main Area.txtpb +++ b/data/maps/the_orb/rooms/Main Area.txtpb | |||
| @@ -85,6 +85,8 @@ panels { | |||
| 85 | } | 85 | } |
| 86 | ports { | 86 | ports { |
| 87 | name: "GREAT" | 87 | name: "GREAT" |
| 88 | display_name: "Main Entrance" | ||
| 88 | path: "Components/Warps/worldport" | 89 | path: "Components/Warps/worldport" |
| 89 | orientation: "south" | 90 | destination { x: 38 y: 0 z: 39 } |
| 91 | rotation: 90 | ||
| 90 | } | 92 | } |
| diff --git a/data/maps/the_orb/rooms/Middle Room.txtpb b/data/maps/the_orb/rooms/Middle Room.txtpb new file mode 100644 index 0000000..ed1a00c --- /dev/null +++ b/data/maps/the_orb/rooms/Middle Room.txtpb | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | name: "Middle Room" | ||
| 2 | # This is the room after solving the B puzzles but before getting to B1 itself. | ||
| 3 | # It has to be a separate region because if you are shuffling worldports and you | ||
| 4 | # warp to the B1 room port, you can't access this port if you're not able to | ||
| 5 | # solve the B puzzles. | ||
| 6 | ports { | ||
| 7 | name: "MID" | ||
| 8 | display_name: "Middle Worldport" | ||
| 9 | path: "Components/Warps/worldport4" | ||
| 10 | destination { x: -69 y: 0 z: 43 } | ||
| 11 | rotation: 90 | ||
| 12 | } | ||
| diff --git a/data/maps/the_owl/connections.txtpb b/data/maps/the_owl/connections.txtpb index 2bd2380..cb4bee3 100644 --- a/data/maps/the_owl/connections.txtpb +++ b/data/maps/the_owl/connections.txtpb | |||
| @@ -10,10 +10,20 @@ connections { | |||
| 10 | } | 10 | } |
| 11 | connections { | 11 | connections { |
| 12 | from_room: "R2C2 Bottom" | 12 | from_room: "R2C2 Bottom" |
| 13 | to_room: "R2C2 Top" | ||
| 14 | door { name: "Sky Owl" } | ||
| 15 | } | ||
| 16 | connections { | ||
| 17 | from_room: "R2C2 Bottom" | ||
| 13 | to_room: "Connected Area" | 18 | to_room: "Connected Area" |
| 14 | door { name: "Gray Owl" } | 19 | door { name: "Gray Owl" } |
| 15 | } | 20 | } |
| 16 | connections { | 21 | connections { |
| 22 | from_room: "R2C2 Bottom" | ||
| 23 | to_room: "Connected Area" | ||
| 24 | door { name: "Sky Owl" } | ||
| 25 | } | ||
| 26 | connections { | ||
| 17 | from_room: "R2C3 Bottom" | 27 | from_room: "R2C3 Bottom" |
| 18 | to_room: "Connected Area" | 28 | to_room: "Connected Area" |
| 19 | oneway: true | 29 | oneway: true |
| @@ -45,6 +55,11 @@ connections { | |||
| 45 | } | 55 | } |
| 46 | connections { | 56 | connections { |
| 47 | from_room: "Connected Area" | 57 | from_room: "Connected Area" |
| 58 | to_room: "R2C3 Bottom" | ||
| 59 | door { name: "Sky Owl" } | ||
| 60 | } | ||
| 61 | connections { | ||
| 62 | from_room: "Connected Area" | ||
| 48 | to_room: "Magenta Hallway" | 63 | to_room: "Magenta Hallway" |
| 49 | door { name: "Control Center Magenta Door" } | 64 | door { name: "Control Center Magenta Door" } |
| 50 | } | 65 | } |
| @@ -70,6 +85,11 @@ connections { | |||
| 70 | } | 85 | } |
| 71 | connections { | 86 | connections { |
| 72 | from_room: "Connected Area" | 87 | from_room: "Connected Area" |
| 88 | to_room: "R1C4 Left" | ||
| 89 | door { name: "Sky Owl" } | ||
| 90 | } | ||
| 91 | connections { | ||
| 92 | from_room: "Connected Area" | ||
| 73 | to_room: "R2C1 Left" | 93 | to_room: "R2C1 Left" |
| 74 | door { name: "Sky Top Doors" } | 94 | door { name: "Sky Top Doors" } |
| 75 | } | 95 | } |
| @@ -84,6 +104,11 @@ connections { | |||
| 84 | door { name: "Gray Owl" } | 104 | door { name: "Gray Owl" } |
| 85 | } | 105 | } |
| 86 | connections { | 106 | connections { |
| 107 | from_room: "Connected Area" | ||
| 108 | to_room: "R2C1 Left" | ||
| 109 | door { name: "Sky Owl" } | ||
| 110 | } | ||
| 111 | connections { | ||
| 87 | from { | 112 | from { |
| 88 | painting { | 113 | painting { |
| 89 | room: "Connected Area" | 114 | room: "Connected Area" |
| diff --git a/data/maps/the_owl/doors.txtpb b/data/maps/the_owl/doors.txtpb index 3d2d055..2d1c851 100644 --- a/data/maps/the_owl/doors.txtpb +++ b/data/maps/the_owl/doors.txtpb | |||
| @@ -1,13 +1,15 @@ | |||
| 1 | doors { | 1 | doors { |
| 2 | name: "Brush Door" | 2 | name: "Brush Door" |
| 3 | type: STANDARD | 3 | type: ITEM_ONLY |
| 4 | legacy_location: true | ||
| 4 | receivers: "Components/Doors/entry_1" | 5 | receivers: "Components/Doors/entry_1" |
| 5 | panels { room: "R2C2 Top" name: "CRUSH" } | 6 | panels { room: "R2C2 Top" name: "CRUSH" } |
| 6 | location_room: "R2C2 Top" | 7 | location_room: "R2C2 Top" |
| 7 | } | 8 | } |
| 8 | doors { | 9 | doors { |
| 9 | name: "Sky Top Doors" | 10 | name: "Sky Top Doors" |
| 10 | type: STANDARD | 11 | type: ITEM_ONLY |
| 12 | legacy_location: true | ||
| 11 | receivers: "Components/Doors/entry_2" | 13 | receivers: "Components/Doors/entry_2" |
| 12 | receivers: "Components/Doors/entry_4" | 14 | receivers: "Components/Doors/entry_4" |
| 13 | panels { room: "R2C1 Left" name: "VERB" } | 15 | panels { room: "R2C1 Left" name: "VERB" } |
| @@ -15,7 +17,8 @@ doors { | |||
| 15 | } | 17 | } |
| 16 | doors { | 18 | doors { |
| 17 | name: "Sky Bottom Doors" | 19 | name: "Sky Bottom Doors" |
| 18 | type: STANDARD | 20 | type: ITEM_ONLY |
| 21 | legacy_location: true | ||
| 19 | receivers: "Components/Doors/entry_3" | 22 | receivers: "Components/Doors/entry_3" |
| 20 | receivers: "Components/Doors/entry_5" | 23 | receivers: "Components/Doors/entry_5" |
| 21 | panels { room: "R2C1 Left" name: "FOIL" } | 24 | panels { room: "R2C1 Left" name: "FOIL" } |
| @@ -23,21 +26,24 @@ doors { | |||
| 23 | } | 26 | } |
| 24 | doors { | 27 | doors { |
| 25 | name: "First Room Shortcut" | 28 | name: "First Room Shortcut" |
| 26 | type: STANDARD | 29 | type: ITEM_ONLY |
| 30 | legacy_location: true | ||
| 27 | receivers: "Components/Doors/entry_6" | 31 | receivers: "Components/Doors/entry_6" |
| 28 | panels { room: "Connected Area" name: "FIZZLE" } | 32 | panels { room: "Connected Area" name: "FIZZLE" } |
| 29 | location_room: "Connected Area" | 33 | location_room: "Connected Area" |
| 30 | } | 34 | } |
| 31 | doors { | 35 | doors { |
| 32 | name: "First Door" | 36 | name: "First Door" |
| 33 | type: STANDARD | 37 | type: ITEM_ONLY |
| 38 | legacy_location: true | ||
| 34 | receivers: "Components/Doors/entry_7" | 39 | receivers: "Components/Doors/entry_7" |
| 35 | panels { room: "R2C2 Bottom" name: "FOUL" } | 40 | panels { room: "R2C2 Bottom" name: "FOUL" } |
| 36 | location_room: "R2C2 Bottom" | 41 | location_room: "R2C2 Bottom" |
| 37 | } | 42 | } |
| 38 | doors { | 43 | doors { |
| 39 | name: "Blue Door" | 44 | name: "Blue Door" |
| 40 | type: STANDARD | 45 | type: ITEM_ONLY |
| 46 | legacy_location: true | ||
| 41 | receivers: "Components/Doors/entry_8" | 47 | receivers: "Components/Doors/entry_8" |
| 42 | panels { room: "Connected Area" name: "PAST" } | 48 | panels { room: "Connected Area" name: "PAST" } |
| 43 | panels { room: "Connected Area" name: "LAY" } | 49 | panels { room: "Connected Area" name: "LAY" } |
| @@ -59,12 +65,14 @@ doors { | |||
| 59 | doors { | 65 | doors { |
| 60 | name: "Control Center Magenta Door" | 66 | name: "Control Center Magenta Door" |
| 61 | type: CONTROL_CENTER_COLOR | 67 | type: CONTROL_CENTER_COLOR |
| 68 | latch: true | ||
| 62 | receivers: "Components/Doors/entry_18" | 69 | receivers: "Components/Doors/entry_18" |
| 63 | control_center_color: "magenta" | 70 | control_center_color: "magenta" |
| 64 | } | 71 | } |
| 65 | doors { | 72 | doors { |
| 66 | name: "Sky Owl" | 73 | name: "Sky Owl" |
| 67 | type: STANDARD | 74 | type: ITEM_ONLY |
| 75 | legacy_location: true | ||
| 68 | receivers: "Components/Owl/Room 1/LB" | 76 | receivers: "Components/Owl/Room 1/LB" |
| 69 | receivers: "Components/Owl/Room 1/LBG" | 77 | receivers: "Components/Owl/Room 1/LBG" |
| 70 | receivers: "Components/Owl/Room 2/LB" | 78 | receivers: "Components/Owl/Room 2/LB" |
| @@ -92,7 +100,8 @@ doors { | |||
| 92 | } | 100 | } |
| 93 | doors { | 101 | doors { |
| 94 | name: "Gray Owl" | 102 | name: "Gray Owl" |
| 95 | type: STANDARD | 103 | type: ITEM_ONLY |
| 104 | legacy_location: true | ||
| 96 | receivers: "Components/Owl/Room 1/G" | 105 | receivers: "Components/Owl/Room 1/G" |
| 97 | receivers: "Components/Owl/Room 1/GG" | 106 | receivers: "Components/Owl/Room 1/GG" |
| 98 | receivers: "Components/Owl/Room 2/G" | 107 | receivers: "Components/Owl/Room 2/G" |
| @@ -120,7 +129,8 @@ doors { | |||
| 120 | } | 129 | } |
| 121 | doors { | 130 | doors { |
| 122 | name: "Orange Owl" | 131 | name: "Orange Owl" |
| 123 | type: STANDARD | 132 | type: ITEM_ONLY |
| 133 | legacy_location: true | ||
| 124 | receivers: "Components/Owl/Room 1/O" | 134 | receivers: "Components/Owl/Room 1/O" |
| 125 | receivers: "Components/Owl/Room 1/OG" | 135 | receivers: "Components/Owl/Room 1/OG" |
| 126 | receivers: "Components/Owl/Room 2/O" | 136 | receivers: "Components/Owl/Room 2/O" |
| @@ -148,7 +158,8 @@ doors { | |||
| 148 | } | 158 | } |
| 149 | doors { | 159 | doors { |
| 150 | name: "White Owl" | 160 | name: "White Owl" |
| 151 | type: STANDARD | 161 | type: ITEM_ONLY |
| 162 | legacy_location: true | ||
| 152 | receivers: "Components/Owl/Room 1/W" | 163 | receivers: "Components/Owl/Room 1/W" |
| 153 | receivers: "Components/Owl/Room 1/WG" | 164 | receivers: "Components/Owl/Room 1/WG" |
| 154 | receivers: "Components/Owl/Room 2/W" | 165 | receivers: "Components/Owl/Room 2/W" |
| @@ -176,7 +187,8 @@ doors { | |||
| 176 | } | 187 | } |
| 177 | doors { | 188 | doors { |
| 178 | name: "Black Owl" | 189 | name: "Black Owl" |
| 179 | type: STANDARD | 190 | type: ITEM_ONLY |
| 191 | legacy_location: true | ||
| 180 | receivers: "Components/Owl/Room 1/BK" | 192 | receivers: "Components/Owl/Room 1/BK" |
| 181 | receivers: "Components/Owl/Room 1/BKG" | 193 | receivers: "Components/Owl/Room 1/BKG" |
| 182 | receivers: "Components/Owl/Room 2/BK" | 194 | receivers: "Components/Owl/Room 2/BK" |
| @@ -204,7 +216,8 @@ doors { | |||
| 204 | } | 216 | } |
| 205 | doors { | 217 | doors { |
| 206 | name: "Blue Owl" | 218 | name: "Blue Owl" |
| 207 | type: STANDARD | 219 | type: ITEM_ONLY |
| 220 | legacy_location: true | ||
| 208 | receivers: "Components/Owl/Room 1/BL" | 221 | receivers: "Components/Owl/Room 1/BL" |
| 209 | receivers: "Components/Owl/Room 1/BLG" | 222 | receivers: "Components/Owl/Room 1/BLG" |
| 210 | receivers: "Components/Owl/Room 2/BL" | 223 | receivers: "Components/Owl/Room 2/BL" |
| @@ -233,20 +246,112 @@ doors { | |||
| 233 | doors { | 246 | doors { |
| 234 | name: "Gray Panel" | 247 | name: "Gray Panel" |
| 235 | type: EVENT | 248 | type: EVENT |
| 236 | # TODO: Is it okay to have an event with an in-game effect? | 249 | #receivers: "Panels/Colors/owl_2/animationListener2" |
| 237 | receivers: "Panels/Colors/owl_2/animationListener2" | 250 | panels { room: "Connected Area" name: "RANGE" } |
| 238 | doors { name: "Orange Owl" } | 251 | panels { room: "Connected Area" name: "WHITE" } |
| 239 | doors { name: "Black Owl" } | 252 | panels { room: "Blue Room" name: "SKY" } |
| 240 | doors { name: "Blue Owl" } | ||
| 241 | } | 253 | } |
| 242 | doors { | 254 | doors { |
| 243 | name: "Owl Painting" | 255 | name: "Owl Painting" |
| 244 | type: EVENT | 256 | type: EVENT |
| 245 | #move_paintings { room: "Connected Area" name: "OWL" } | 257 | #move_paintings { room: "Connected Area" name: "OWL" } |
| 246 | #receivers: "Components/Paintings/owl/teleportListener" | 258 | #receivers: "Components/Paintings/owl/teleportListener" |
| 247 | doors { name: "Orange Owl" } | 259 | panels { room: "R2C1 Left" name: "DUSKY" } |
| 248 | doors { name: "Black Owl" } | 260 | panels { room: "R2C2 Top" name: "RAY" } |
| 249 | doors { name: "Blue Owl" } | 261 | panels { room: "Connected Area" name: "RANGE" } |
| 250 | doors { name: "White Owl" } | 262 | panels { room: "R2C3 Bottom" name: "BLACK" } |
| 251 | doors { name: "Sky Owl" } | 263 | panels { room: "Connected Area" name: "WHITE" } |
| 264 | panels { room: "Blue Room" name: "SKY" } | ||
| 265 | } | ||
| 266 | doors { | ||
| 267 | name: "R1C1 Panels" | ||
| 268 | type: LOCATION_ONLY | ||
| 269 | panels { room: "Connected Area" name: "ETCH" } | ||
| 270 | panels { room: "Connected Area" name: "SHOE" } | ||
| 271 | panels { room: "Connected Area" name: "MARKER" } | ||
| 272 | location_room: "Connected Area" | ||
| 273 | location_name: "ETCH, MARKER, SHOE" | ||
| 274 | } | ||
| 275 | doors { | ||
| 276 | name: "R1C2 Panels" | ||
| 277 | type: LOCATION_ONLY | ||
| 278 | panels { room: "Connected Area" name: "FAINT" } | ||
| 279 | panels { room: "Connected Area" name: "PURE" } | ||
| 280 | panels { room: "Connected Area" name: "MODE" } | ||
| 281 | location_room: "Connected Area" | ||
| 282 | location_name: "FAINT, MODE, PURE" | ||
| 283 | } | ||
| 284 | doors { | ||
| 285 | name: "Control Center Magenta Panel" | ||
| 286 | type: LOCATION_ONLY | ||
| 287 | panels { room: "Connected Area" name: "COLOR" } | ||
| 288 | location_room: "Connected Area" | ||
| 289 | location_name: "COLOR" | ||
| 290 | } | ||
| 291 | doors { | ||
| 292 | name: "R1C3 Panels" | ||
| 293 | type: LOCATION_ONLY | ||
| 294 | panels { room: "Connected Area" name: "PENCIL" } | ||
| 295 | panels { room: "Connected Area" name: "WING" } | ||
| 296 | location_room: "Connected Area" | ||
| 297 | location_name: "PENCIL, WING" | ||
| 298 | } | ||
| 299 | doors { | ||
| 300 | name: "R1C4 Panels" | ||
| 301 | type: LOCATION_ONLY | ||
| 302 | panels { room: "Connected Area" name: "SKETCH" } | ||
| 303 | panels { room: "Connected Area" name: "PHOTO" } | ||
| 304 | panels { room: "R1C4 Left" name: "WALK" } | ||
| 305 | panels { room: "R1C4 Left" name: "STENCIL" } | ||
| 306 | location_room: "R1C4 Left" | ||
| 307 | location_name: "PHOTO, SKETCH, STENCIL, WALK" | ||
| 308 | } | ||
| 309 | doors { | ||
| 310 | name: "R2C1 Panels" | ||
| 311 | type: LOCATION_ONLY | ||
| 312 | panels { room: "Connected Area" name: "LAY" } | ||
| 313 | panels { room: "Connected Area" name: "PAST" } | ||
| 314 | panels { room: "R2C1 Left" name: "VERB" } | ||
| 315 | panels { room: "R2C1 Left" name: "FOIL" } | ||
| 316 | location_room: "R2C1 Left" | ||
| 317 | location_name: "FOIL, LAY, PAST, VERB" | ||
| 318 | } | ||
| 319 | doors { | ||
| 320 | name: "R2C2 Panels" | ||
| 321 | type: LOCATION_ONLY | ||
| 322 | panels { room: "R2C2 Bottom" name: "FOUL" } | ||
| 323 | panels { room: "R2C2 Top" name: "CRUSH" } | ||
| 324 | panels { room: "Connected Area" name: "FIZZLE" } | ||
| 325 | location_room: "R2C2 Top" | ||
| 326 | location_name: "CRUSH, FOUL, FIZZLE" | ||
| 327 | } | ||
| 328 | doors { | ||
| 329 | name: "R2C3 Panels" | ||
| 330 | type: LOCATION_ONLY | ||
| 331 | panels { room: "Connected Area" name: "PRIMARY" } | ||
| 332 | panels { room: "R2C3 Bottom" name: "FIGMENT" } | ||
| 333 | location_room: "R2C3 Bottom" | ||
| 334 | location_name: "FIGMENT, PRIMARY" | ||
| 335 | } | ||
| 336 | doors { | ||
| 337 | name: "R2C4 Panels" | ||
| 338 | type: LOCATION_ONLY | ||
| 339 | panels { room: "Connected Area" name: "SHOW" } | ||
| 340 | panels { room: "Connected Area" name: "HAD" } | ||
| 341 | panels { room: "Connected Area" name: "HEAVY" } | ||
| 342 | location_room: "Connected Area" | ||
| 343 | location_name: "HAD, HEAVY, SHOW" | ||
| 344 | } | ||
| 345 | doors { | ||
| 346 | name: "Near Z1 Panel" | ||
| 347 | type: LOCATION_ONLY | ||
| 348 | panels { room: "Z Room" name: "MAZE" } | ||
| 349 | location_room: "Z Room" | ||
| 350 | location_name: "MAZE" | ||
| 351 | } | ||
| 352 | doors { | ||
| 353 | name: "Double Letters" | ||
| 354 | type: EVENT | ||
| 355 | receivers: "Panels/Warps/magenta/visibilityListener" | ||
| 356 | double_letters: true | ||
| 252 | } | 357 | } |
| diff --git a/data/maps/the_owl/rooms/Connected Area.txtpb b/data/maps/the_owl/rooms/Connected Area.txtpb index cf5ea1f..b604cba 100644 --- a/data/maps/the_owl/rooms/Connected Area.txtpb +++ b/data/maps/the_owl/rooms/Connected Area.txtpb | |||
| @@ -26,6 +26,7 @@ panels { | |||
| 26 | clue: "color" | 26 | clue: "color" |
| 27 | answer: "magenta" | 27 | answer: "magenta" |
| 28 | symbols: EXAMPLE | 28 | symbols: EXAMPLE |
| 29 | required_door { name: "Double Letters" } | ||
| 29 | } | 30 | } |
| 30 | panels { | 31 | panels { |
| 31 | name: "WHITE" | 32 | name: "WHITE" |
| @@ -149,7 +150,9 @@ paintings { | |||
| 149 | } | 150 | } |
| 150 | ports { | 151 | ports { |
| 151 | name: "FOURROOMS" | 152 | name: "FOURROOMS" |
| 153 | display_name: "Four Rooms Entrance" | ||
| 152 | path: "Components/Warps/worldport2" | 154 | path: "Components/Warps/worldport2" |
| 153 | orientation: "east" | 155 | destination { x: 71.5 y: 0 z: -9 } |
| 156 | rotation: 90 | ||
| 154 | # Note that this is behind teal walls. | 157 | # Note that this is behind teal walls. |
| 155 | } | 158 | } |
| diff --git a/data/maps/the_owl/rooms/Magenta Hallway.txtpb b/data/maps/the_owl/rooms/Magenta Hallway.txtpb index ccbdc1c..14d6f0d 100644 --- a/data/maps/the_owl/rooms/Magenta Hallway.txtpb +++ b/data/maps/the_owl/rooms/Magenta Hallway.txtpb | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | name: "Magenta Hallway" | 1 | name: "Magenta Hallway" |
| 2 | ports { | 2 | ports { |
| 3 | name: "STURDY" | 3 | name: "STURDY" |
| 4 | display_name: "Magenta Hallway" | ||
| 4 | path: "Components/Warps/worldport3" | 5 | path: "Components/Warps/worldport3" |
| 5 | orientation: "west" | 6 | destination { x: 17 y: 0 z: -46 } |
| 7 | rotation: 270 | ||
| 6 | } | 8 | } |
| diff --git a/data/maps/the_owl/rooms/R2C2 Bottom.txtpb b/data/maps/the_owl/rooms/R2C2 Bottom.txtpb index 604a1cc..2cfd340 100644 --- a/data/maps/the_owl/rooms/R2C2 Bottom.txtpb +++ b/data/maps/the_owl/rooms/R2C2 Bottom.txtpb | |||
| @@ -8,8 +8,10 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "GALLERY" | 10 | name: "GALLERY" |
| 11 | display_name: "Gallery Worldport" | ||
| 11 | path: "Components/Warps/worldport" | 12 | path: "Components/Warps/worldport" |
| 12 | orientation: "south" | 13 | destination { x: 0 y: 0 z: 9 } |
| 14 | rotation: 0 | ||
| 13 | # TODO: Note that this port is accessible from the other side in the Z1 | 15 | # TODO: Note that this port is accessible from the other side in the Z1 |
| 14 | # room. Hmm. | 16 | # room. Hmm. |
| 15 | } | 17 | } |
| 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_parthenon/doors.txtpb b/data/maps/the_parthenon/doors.txtpb index 5187aea..05d2e63 100644 --- a/data/maps/the_parthenon/doors.txtpb +++ b/data/maps/the_parthenon/doors.txtpb | |||
| @@ -13,7 +13,12 @@ doors { | |||
| 13 | doors { | 13 | doors { |
| 14 | name: "Lavender Cubes" | 14 | name: "Lavender Cubes" |
| 15 | type: EVENT | 15 | type: EVENT |
| 16 | switches: "lavender_cubes" | 16 | receivers: "Components/Doors/entry_3" |
| 17 | panels { | ||
| 18 | map: "the_ancient" | ||
| 19 | room: "Inside" | ||
| 20 | name: "COLOR" | ||
| 21 | } | ||
| 17 | } | 22 | } |
| 18 | doors { | 23 | doors { |
| 19 | name: "K2 Door" | 24 | name: "K2 Door" |
| @@ -38,3 +43,12 @@ doors { | |||
| 38 | panels { room: "Main Area" name: "ALEXANDER" answer: "alexander" } | 43 | panels { room: "Main Area" name: "ALEXANDER" answer: "alexander" } |
| 39 | panels { room: "Main Area" name: "CAESAR" answer: "caesar" } | 44 | panels { room: "Main Area" name: "CAESAR" answer: "caesar" } |
| 40 | } | 45 | } |
| 46 | doors { | ||
| 47 | name: "Lavender Area Puzzles" | ||
| 48 | type: LOCATION_ONLY | ||
| 49 | panels { room: "Lavender Area" name: "ME" } | ||
| 50 | panels { room: "Lavender Area" name: "SHEEP" } | ||
| 51 | panels { room: "Lavender Area" name: "WOOD" } | ||
| 52 | location_room: "Lavender Area" | ||
| 53 | location_name: "ME, SHEEP, WOOD" | ||
| 54 | } | ||
| diff --git a/data/maps/the_parthenon/rooms/Main Area.txtpb b/data/maps/the_parthenon/rooms/Main Area.txtpb index 85188d1..2d989f8 100644 --- a/data/maps/the_parthenon/rooms/Main Area.txtpb +++ b/data/maps/the_parthenon/rooms/Main Area.txtpb | |||
| @@ -55,16 +55,22 @@ panels { | |||
| 55 | } | 55 | } |
| 56 | ports { | 56 | ports { |
| 57 | name: "GALLERY" | 57 | name: "GALLERY" |
| 58 | display_name: "Columns Worldport" | ||
| 58 | path: "Components/Warps/worldport" | 59 | path: "Components/Warps/worldport" |
| 59 | orientation: "south" | 60 | destination { x: 0 y: 0 z: 0 } |
| 61 | rotation: 0 | ||
| 60 | } | 62 | } |
| 61 | ports { | 63 | ports { |
| 62 | name: "ENTRY" | 64 | name: "ENTRY" |
| 65 | display_name: "Building Worldport" | ||
| 63 | path: "Components/Warps/worldport2" | 66 | path: "Components/Warps/worldport2" |
| 64 | orientation: "south" | 67 | destination { x: 0 y: 0 z: -21 } |
| 68 | rotation: 0 | ||
| 65 | } | 69 | } |
| 66 | ports { | 70 | ports { |
| 67 | name: "REVITALIZED" | 71 | name: "REVITALIZED" |
| 72 | display_name: "Plum Hallway" | ||
| 68 | path: "Components/Warps/worldport3" | 73 | path: "Components/Warps/worldport3" |
| 69 | orientation: "north" | 74 | destination { x: -24 y: 0 z: -39 } |
| 75 | rotation: 180 | ||
| 70 | } | 76 | } |
| diff --git a/data/maps/the_partial/doors.txtpb b/data/maps/the_partial/doors.txtpb index c51062a..e37d077 100644 --- a/data/maps/the_partial/doors.txtpb +++ b/data/maps/the_partial/doors.txtpb | |||
| @@ -43,14 +43,14 @@ doors { | |||
| 43 | doors { | 43 | doors { |
| 44 | name: "L Entered" | 44 | name: "L Entered" |
| 45 | type: EVENT | 45 | type: EVENT |
| 46 | # It does this in vanilla, but I'm specifying it so that the Control Center | 46 | latch: true |
| 47 | # Entrance door doesn't override it. | ||
| 48 | receivers: "Components/Doors/controlDoor" | 47 | receivers: "Components/Doors/controlDoor" |
| 49 | keyholders { room: "Obverse Side" name: "L" key: "l" } | 48 | keyholders { room: "Obverse Side" name: "L" key: "l" } |
| 50 | } | 49 | } |
| 51 | doors { | 50 | doors { |
| 52 | name: "Control Center Entrance" | 51 | name: "Control Center Entrance" |
| 53 | type: LOCATION_ONLY | 52 | type: EVENT |
| 53 | legacy_location: true | ||
| 54 | #receivers: "Components/Doors/controlDoor" | 54 | #receivers: "Components/Doors/controlDoor" |
| 55 | panels { room: "Control Center Entrance" name: "RETURN" } | 55 | panels { room: "Control Center Entrance" name: "RETURN" } |
| 56 | location_room: "Control Center Entrance" | 56 | location_room: "Control Center Entrance" |
| diff --git a/data/maps/the_partial/rooms/Control Center Entrance.txtpb b/data/maps/the_partial/rooms/Control Center Entrance.txtpb index e685822..faccd50 100644 --- a/data/maps/the_partial/rooms/Control Center Entrance.txtpb +++ b/data/maps/the_partial/rooms/Control Center Entrance.txtpb | |||
| @@ -8,6 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "CC" | 10 | name: "CC" |
| 11 | display_name: "Control Center Connector" | ||
| 11 | path: "Components/Warps/worldport2" | 12 | path: "Components/Warps/worldport2" |
| 12 | orientation: "north" | 13 | destination { x: -19 y: 0 z: 8 } |
| 14 | rotation: 180 | ||
| 13 | } | 15 | } |
| diff --git a/data/maps/the_partial/rooms/Obverse Side.txtpb b/data/maps/the_partial/rooms/Obverse Side.txtpb index c0ce04b..462888c 100644 --- a/data/maps/the_partial/rooms/Obverse Side.txtpb +++ b/data/maps/the_partial/rooms/Obverse Side.txtpb | |||
| @@ -99,8 +99,10 @@ panels { | |||
| 99 | } | 99 | } |
| 100 | ports { | 100 | ports { |
| 101 | name: "GREAT" | 101 | name: "GREAT" |
| 102 | display_name: "Main Entrance" | ||
| 102 | path: "Components/Warps/worldport" | 103 | path: "Components/Warps/worldport" |
| 103 | orientation: "west" | 104 | destination { x: -3 y: 0 z: 20 } |
| 105 | rotation: 270 | ||
| 104 | } | 106 | } |
| 105 | keyholders { | 107 | keyholders { |
| 106 | # This is one of the ones that's misnamed within the game. | 108 | # This is one of the ones that's misnamed within the game. |
| diff --git a/data/maps/the_perceptive/metadata.txtpb b/data/maps/the_perceptive/metadata.txtpb index e0c64fb..6942cab 100644 --- a/data/maps/the_perceptive/metadata.txtpb +++ b/data/maps/the_perceptive/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Perceptive" | 1 | display_name: "The Perceptive" |
| 2 | worldport_entrance { | ||
| 3 | room: "Main Area" | ||
| 4 | name: "CC" | ||
| 5 | } | ||
| diff --git a/data/maps/the_perceptive/rooms/Main Area.txtpb b/data/maps/the_perceptive/rooms/Main Area.txtpb index 449bd4d..ebf511d 100644 --- a/data/maps/the_perceptive/rooms/Main Area.txtpb +++ b/data/maps/the_perceptive/rooms/Main Area.txtpb | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | name: "Main Area" | 1 | name: "Main Area" |
| 2 | ports { | 2 | ports { |
| 3 | name: "CC" | 3 | name: "CC" |
| 4 | display_name: "Entrance" | ||
| 4 | path: "Components/Warps/worldport" | 5 | path: "Components/Warps/worldport" |
| 5 | orientation: "east" | 6 | destination { x: 3 y: 0 z: 13 } |
| 7 | rotation: 90 | ||
| 6 | } | 8 | } |
| 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..fef8954 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" } |
| @@ -210,3 +210,31 @@ doors { | |||
| 210 | panels { room: "Bottom Right Room" name: "HONEY" } | 210 | panels { room: "Bottom Right Room" name: "HONEY" } |
| 211 | panels { room: "Bottom Right Room" name: "INJECT" } | 211 | panels { room: "Bottom Right Room" name: "INJECT" } |
| 212 | } | 212 | } |
| 213 | doors { | ||
| 214 | name: "Near Sirenic Panel" | ||
| 215 | type: LOCATION_ONLY | ||
| 216 | panels { room: "Sirenic Entrance" name: "SIREN" } | ||
| 217 | location_room: "Sirenic Entrance" | ||
| 218 | location_name: "SIREN" | ||
| 219 | } | ||
| 220 | doors { | ||
| 221 | name: "Near Symbolic Panel" | ||
| 222 | type: LOCATION_ONLY | ||
| 223 | panels { room: "Symbolic Entrance" name: "FIGURATIVE" } | ||
| 224 | location_room: "Symbolic Entrance" | ||
| 225 | location_name: "FIGURATIVE" | ||
| 226 | } | ||
| 227 | doors { | ||
| 228 | name: "Near Repetitive Panel" | ||
| 229 | type: LOCATION_ONLY | ||
| 230 | panels { room: "Repetitive Entrance" name: "TEDIOUS" } | ||
| 231 | location_room: "Repetitive Entrance" | ||
| 232 | location_name: "TEDIOUS" | ||
| 233 | } | ||
| 234 | doors { | ||
| 235 | name: "Near Broken Portal Panel" | ||
| 236 | type: LOCATION_ONLY | ||
| 237 | panels { room: "Main Area" name: "AFFABLE" } | ||
| 238 | location_room: "Main Area" | ||
| 239 | location_name: "AFFABLE" | ||
| 240 | } | ||
| diff --git a/data/maps/the_plaza/rooms/Main Area.txtpb b/data/maps/the_plaza/rooms/Main Area.txtpb index 521b974..c2fca13 100644 --- a/data/maps/the_plaza/rooms/Main Area.txtpb +++ b/data/maps/the_plaza/rooms/Main Area.txtpb | |||
| @@ -36,16 +36,22 @@ panels { | |||
| 36 | } | 36 | } |
| 37 | ports { | 37 | ports { |
| 38 | name: "UNYIELDING" | 38 | name: "UNYIELDING" |
| 39 | display_name: "Unyielding Hallway" | ||
| 39 | path: "Components/Warps/worldport" | 40 | path: "Components/Warps/worldport" |
| 40 | orientation: "west" | 41 | destination { x: 1 y: 0 z: 10 } |
| 42 | rotation: 270 | ||
| 41 | } | 43 | } |
| 42 | ports { | 44 | ports { |
| 43 | name: "IMPRESSIVE" | 45 | name: "IMPRESSIVE" |
| 46 | display_name: "Impressive Hallway" | ||
| 44 | path: "Components/Warps/worldport2" | 47 | path: "Components/Warps/worldport2" |
| 45 | orientation: "west" | 48 | destination { x: 11 y: 0 z: 10 } |
| 49 | rotation: 270 | ||
| 46 | } | 50 | } |
| 47 | ports { | 51 | ports { |
| 48 | name: "BETWEEN" | 52 | name: "BETWEEN" |
| 53 | display_name: "Between Hallway" | ||
| 49 | path: "Components/Warps/worldport3" | 54 | path: "Components/Warps/worldport3" |
| 50 | orientation: "west" | 55 | destination { x: -9 y: 0 z: 10 } |
| 56 | rotation: 270 | ||
| 51 | } | 57 | } |
| diff --git a/data/maps/the_plaza/rooms/Repetitive Entrance.txtpb b/data/maps/the_plaza/rooms/Repetitive Entrance.txtpb index 3857d5f..59faaa8 100644 --- a/data/maps/the_plaza/rooms/Repetitive Entrance.txtpb +++ b/data/maps/the_plaza/rooms/Repetitive Entrance.txtpb | |||
| @@ -8,6 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "REPETITIVE" | 10 | name: "REPETITIVE" |
| 11 | display_name: "Repetitive Entrance" | ||
| 11 | path: "Components/Warps/worldport5" | 12 | path: "Components/Warps/worldport5" |
| 12 | orientation: "north" | 13 | destination { x: -19 y: 0 z: 16 } |
| 14 | rotation: 180 | ||
| 13 | } | 15 | } |
| diff --git a/data/maps/the_plaza/rooms/Sirenic Entrance.txtpb b/data/maps/the_plaza/rooms/Sirenic Entrance.txtpb index 3c60ca8..524de2b 100644 --- a/data/maps/the_plaza/rooms/Sirenic Entrance.txtpb +++ b/data/maps/the_plaza/rooms/Sirenic Entrance.txtpb | |||
| @@ -8,6 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "SIRENIC" | 10 | name: "SIRENIC" |
| 11 | display_name: "Sirenic Entrance" | ||
| 11 | path: "Components/Warps/worldport6" | 12 | path: "Components/Warps/worldport6" |
| 12 | orientation: "west" | 13 | destination { x: -51 y: 0 z: -43 } |
| 14 | rotation: 270 | ||
| 13 | } | 15 | } |
| diff --git a/data/maps/the_plaza/rooms/Symbolic Entrance.txtpb b/data/maps/the_plaza/rooms/Symbolic Entrance.txtpb index ce5982c..e2719b8 100644 --- a/data/maps/the_plaza/rooms/Symbolic Entrance.txtpb +++ b/data/maps/the_plaza/rooms/Symbolic Entrance.txtpb | |||
| @@ -8,6 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "SYMBOLIC" | 10 | name: "SYMBOLIC" |
| 11 | display_name: "Symbolic Entrance" | ||
| 11 | path: "Components/Warps/worldport4" | 12 | path: "Components/Warps/worldport4" |
| 12 | orientation: "south" | 13 | destination { x: 28 y: 0 z: 4 } |
| 14 | rotation: 0 | ||
| 13 | } | 15 | } |
| diff --git a/data/maps/the_quiet/metadata.txtpb b/data/maps/the_quiet/metadata.txtpb index 1fa2c46..d7fd0eb 100644 --- a/data/maps/the_quiet/metadata.txtpb +++ b/data/maps/the_quiet/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Quiet" | 1 | display_name: "The Quiet" |
| 2 | worldport_entrance { | ||
| 3 | room: "Main Area" | ||
| 4 | name: "DAEDALUS" | ||
| 5 | } | ||
| diff --git a/data/maps/the_quiet/rooms/Main Area.txtpb b/data/maps/the_quiet/rooms/Main Area.txtpb index 180e0bc..72c0a1e 100644 --- a/data/maps/the_quiet/rooms/Main Area.txtpb +++ b/data/maps/the_quiet/rooms/Main Area.txtpb | |||
| @@ -97,6 +97,8 @@ panels { | |||
| 97 | } | 97 | } |
| 98 | ports { | 98 | ports { |
| 99 | name: "DAEDALUS" | 99 | name: "DAEDALUS" |
| 100 | display_name: "Entrance" | ||
| 100 | path: "Components/Warps/worldport" | 101 | path: "Components/Warps/worldport" |
| 101 | orientation: "east" | 102 | destination { x: 3 y: 0 z: 8 } |
| 103 | rotation: 90 | ||
| 102 | } | 104 | } |
| diff --git a/data/maps/the_relentless/doors.txtpb b/data/maps/the_relentless/doors.txtpb index 11f6369..e755d0b 100644 --- a/data/maps/the_relentless/doors.txtpb +++ b/data/maps/the_relentless/doors.txtpb | |||
| @@ -1,6 +1,33 @@ | |||
| 1 | doors { | 1 | doors { |
| 2 | name: "Left/Turn Door" | 2 | name: "Turn Only Puzzles" |
| 3 | type: LOCATION_ONLY | 3 | type: LOCATION_ONLY |
| 4 | panels { room: "Turn Room" name: "HIDE (1)" } | ||
| 5 | panels { room: "Turn Room" name: "HIDE (2)" } | ||
| 6 | panels { room: "Turn Room" name: "MORE" } | ||
| 7 | location_room: "Turn Room" | ||
| 8 | } | ||
| 9 | doors { | ||
| 10 | name: "Shop Only Puzzles" | ||
| 11 | type: LOCATION_ONLY | ||
| 12 | panels { room: "Shop Room" name: "LEFT (1)" } | ||
| 13 | panels { room: "Shop Room" name: "LEFT (2)" } | ||
| 14 | panels { room: "Shop Room" name: "EXIT (1)" } | ||
| 15 | panels { room: "Shop Room" name: "EXIT (2)" } | ||
| 16 | panels { room: "Shop Room" name: "EXIT (3)" } | ||
| 17 | location_room: "Shop Room" | ||
| 18 | } | ||
| 19 | doors { | ||
| 20 | name: "Left Only Puzzles" | ||
| 21 | type: LOCATION_ONLY | ||
| 22 | panels { room: "Left Room" name: "HIDE" } | ||
| 23 | panels { room: "Left Room" name: "LEFT" } | ||
| 24 | panels { room: "Left Room" name: "MORE" } | ||
| 25 | location_room: "Left Room" | ||
| 26 | } | ||
| 27 | doors { | ||
| 28 | name: "Left/Turn Door" | ||
| 29 | type: EVENT | ||
| 30 | legacy_location: true | ||
| 4 | panels { room: "Left Room" name: "HIDE" } | 31 | panels { room: "Left Room" name: "HIDE" } |
| 5 | panels { room: "Left Room" name: "LEFT" } | 32 | panels { room: "Left Room" name: "LEFT" } |
| 6 | panels { room: "Left Room" name: "MORE" } | 33 | panels { room: "Left Room" name: "MORE" } |
| @@ -11,7 +38,8 @@ doors { | |||
| 11 | } | 38 | } |
| 12 | doors { | 39 | doors { |
| 13 | name: "Turn/Shop Door" | 40 | name: "Turn/Shop Door" |
| 14 | type: LOCATION_ONLY | 41 | type: EVENT |
| 42 | legacy_location: true | ||
| 15 | panels { room: "Turn Room" name: "HIDE (1)" } | 43 | panels { room: "Turn Room" name: "HIDE (1)" } |
| 16 | panels { room: "Turn Room" name: "HIDE (2)" } | 44 | panels { room: "Turn Room" name: "HIDE (2)" } |
| 17 | panels { room: "Turn Room" name: "MORE" } | 45 | panels { room: "Turn Room" name: "MORE" } |
| diff --git a/data/maps/the_repetitive/connections.txtpb b/data/maps/the_repetitive/connections.txtpb index 0afe72d..f4c06f2 100644 --- a/data/maps/the_repetitive/connections.txtpb +++ b/data/maps/the_repetitive/connections.txtpb | |||
| @@ -7,12 +7,12 @@ connections { | |||
| 7 | from_room: "Main Room" | 7 | from_room: "Main Room" |
| 8 | to_room: "Plaza Connector" | 8 | to_room: "Plaza Connector" |
| 9 | door { name: "Black Hallway" } | 9 | door { name: "Black Hallway" } |
| 10 | oneway: true | ||
| 11 | } | 10 | } |
| 12 | connections { | 11 | connections { |
| 13 | from_room: "Plaza Connector" | 12 | from_room: "Plaza Connector" |
| 14 | to_room: "Main Room" | 13 | to_room: "Main Room" |
| 15 | oneway: true | 14 | oneway: true |
| 15 | vanilla_only: true | ||
| 16 | } | 16 | } |
| 17 | connections { | 17 | connections { |
| 18 | from_room: "Main Room" | 18 | from_room: "Main Room" |
| diff --git a/data/maps/the_repetitive/doors.txtpb b/data/maps/the_repetitive/doors.txtpb index 8171dc4..95d189f 100644 --- a/data/maps/the_repetitive/doors.txtpb +++ b/data/maps/the_repetitive/doors.txtpb | |||
| @@ -20,12 +20,21 @@ doors { | |||
| 20 | } | 20 | } |
| 21 | doors { | 21 | doors { |
| 22 | name: "Dot Area Entrance" | 22 | name: "Dot Area Entrance" |
| 23 | type: STANDARD | 23 | type: ITEM_ONLY |
| 24 | legacy_location: true | ||
| 24 | receivers: "Components/Doors/Door8" | 25 | receivers: "Components/Doors/Door8" |
| 25 | panels { room: "Main Room" name: "HOTS (2)" } | 26 | panels { room: "Main Room" name: "HOTS (2)" } |
| 26 | location_room: "Main Room" | 27 | location_room: "Main Room" |
| 27 | } | 28 | } |
| 28 | doors { | 29 | doors { |
| 30 | name: "Hots Panels" | ||
| 31 | type: LOCATION_ONLY | ||
| 32 | panels { room: "Main Room" name: "HOTS (1)" } | ||
| 33 | panels { room: "Main Room" name: "HOTS (2)" } | ||
| 34 | location_room: "Main Room" | ||
| 35 | location_name: "HOTS (1), HOTS (2)" | ||
| 36 | } | ||
| 37 | doors { | ||
| 29 | name: "Lime Door" | 38 | name: "Lime Door" |
| 30 | type: STANDARD | 39 | type: STANDARD |
| 31 | receivers: "Components/Doors/Door9" | 40 | receivers: "Components/Doors/Door9" |
| @@ -194,3 +203,41 @@ doors { | |||
| 194 | panels { room: "Yellow Room" name: "ASSESSES" } | 203 | panels { room: "Yellow Room" name: "ASSESSES" } |
| 195 | panels { room: "Yellow Room" name: "TINTING" } | 204 | panels { room: "Yellow Room" name: "TINTING" } |
| 196 | } | 205 | } |
| 206 | doors { | ||
| 207 | name: "Anti-Collectable" | ||
| 208 | type: LOCATION_ONLY | ||
| 209 | senders: "Components/Collectables/anticollectable" | ||
| 210 | location_room: "Anti Room" | ||
| 211 | } | ||
| 212 | doors { | ||
| 213 | name: "H2 Room Puzzles" | ||
| 214 | type: LOCATION_ONLY | ||
| 215 | panels { room: "Main Room" name: "HEIGHT (1)" } | ||
| 216 | panels { room: "Main Room" name: "HEIGHT (2)" } | ||
| 217 | panels { room: "Main Room" name: "HEIGHT (3)" } | ||
| 218 | panels { room: "Main Room" name: "HEIGHT (4)" } | ||
| 219 | panels { room: "Main Room" name: "HEIGHT (5)" } | ||
| 220 | panels { room: "Main Room" name: "HEIGHT (6)" } | ||
| 221 | panels { room: "Main Room" name: "QUESTION" } | ||
| 222 | panels { room: "Main Room" name: "INTUITION" } | ||
| 223 | panels { room: "Main Room" name: "?" } | ||
| 224 | panels { room: "Main Room" name: "HAND" } | ||
| 225 | panels { room: "Main Room" name: "? HAND" } | ||
| 226 | panels { room: "Main Room" name: "RICHES" } | ||
| 227 | panels { room: "Main Room" name: "? RICHES" } | ||
| 228 | panels { room: "Main Room" name: "MISHMASH" } | ||
| 229 | location_room: "Main Room" | ||
| 230 | } | ||
| 231 | doors { | ||
| 232 | name: "Anti-Collectable Room Panels" | ||
| 233 | type: LOCATION_ONLY | ||
| 234 | panels { room: "Anti Room" name: "EYE (1)" } | ||
| 235 | panels { room: "Anti Room" name: "EYE (2)" } | ||
| 236 | panels { room: "Anti Room" name: "HA (1)" } | ||
| 237 | panels { room: "Anti Room" name: "HA (2)" } | ||
| 238 | panels { room: "Anti Room" name: "HA (3)" } | ||
| 239 | panels { room: "Anti Room" name: "HA (4)" } | ||
| 240 | panels { room: "Anti Room" name: "HA (5)" } | ||
| 241 | panels { room: "Anti Room" name: "TWO" } | ||
| 242 | location_room: "Anti Room" | ||
| 243 | } | ||
| 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_repetitive/rooms/Entry Connector.txtpb b/data/maps/the_repetitive/rooms/Entry Connector.txtpb index b6795c2..1508145 100644 --- a/data/maps/the_repetitive/rooms/Entry Connector.txtpb +++ b/data/maps/the_repetitive/rooms/Entry Connector.txtpb | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | name: "Entry Connector" | 1 | name: "Entry Connector" |
| 2 | ports { | 2 | ports { |
| 3 | name: "ENTRY" | 3 | name: "ENTRY" |
| 4 | display_name: "Northwest Worldport" | ||
| 4 | path: "Components/Warps/worldport2" | 5 | path: "Components/Warps/worldport2" |
| 5 | orientation: "south" | 6 | destination { x: -11 y: 0 z: 13 } |
| 7 | rotation: 90 | ||
| 6 | } | 8 | } |
| diff --git a/data/maps/the_repetitive/rooms/Main Room.txtpb b/data/maps/the_repetitive/rooms/Main Room.txtpb index 8a2feb0..623204c 100644 --- a/data/maps/the_repetitive/rooms/Main Room.txtpb +++ b/data/maps/the_repetitive/rooms/Main Room.txtpb | |||
| @@ -138,6 +138,8 @@ paintings { | |||
| 138 | } | 138 | } |
| 139 | ports { | 139 | ports { |
| 140 | name: "CC" | 140 | name: "CC" |
| 141 | display_name: "Southwest Worldport" | ||
| 141 | path: "Components/Warps/worldport3" | 142 | path: "Components/Warps/worldport3" |
| 142 | orientation: "east" | 143 | destination { x: -5.5 y: 0 z: 56 } |
| 144 | rotation: 90 | ||
| 143 | } | 145 | } |
| diff --git a/data/maps/the_repetitive/rooms/Plaza Connector.txtpb b/data/maps/the_repetitive/rooms/Plaza Connector.txtpb index 1ed66b4..b26fdb0 100644 --- a/data/maps/the_repetitive/rooms/Plaza Connector.txtpb +++ b/data/maps/the_repetitive/rooms/Plaza Connector.txtpb | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | name: "Plaza Connector" | 1 | name: "Plaza Connector" |
| 2 | ports { | 2 | ports { |
| 3 | name: "PLAZA" | 3 | name: "PLAZA" |
| 4 | display_name: "Northeast Worldport" | ||
| 4 | path: "Components/Warps/worldport" | 5 | path: "Components/Warps/worldport" |
| 5 | orientation: "north" | 6 | destination { x: 15 y: 0 z: 13 } |
| 7 | rotation: 0 | ||
| 6 | } | 8 | } |
| diff --git a/data/maps/the_revitalized/rooms/Bye Room.txtpb b/data/maps/the_revitalized/rooms/Bye Room.txtpb index 6cefe70..52d8c42 100644 --- a/data/maps/the_revitalized/rooms/Bye Room.txtpb +++ b/data/maps/the_revitalized/rooms/Bye Room.txtpb | |||
| @@ -8,6 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "PARTHENON" | 10 | name: "PARTHENON" |
| 11 | display_name: "Entrance" | ||
| 11 | path: "Components/Warps/worldport" | 12 | path: "Components/Warps/worldport" |
| 12 | orientation: "south" | 13 | destination { x: 30 y: 0 z: 16 } |
| 14 | rotation: 0 | ||
| 13 | } | 15 | } |
| diff --git a/data/maps/the_shop/doors.txtpb b/data/maps/the_shop/doors.txtpb index 5362614..2ce7c71 100644 --- a/data/maps/the_shop/doors.txtpb +++ b/data/maps/the_shop/doors.txtpb | |||
| @@ -33,5 +33,8 @@ doors { | |||
| 33 | doors { | 33 | doors { |
| 34 | name: "N Entered" | 34 | name: "N Entered" |
| 35 | type: EVENT | 35 | type: EVENT |
| 36 | latch: true | ||
| 37 | receivers: "Components/Doors/entry_1" | ||
| 38 | receivers: "Components/Doors/entry_2" | ||
| 36 | keyholders { room: "Main Area" name: "N" key: "n" } | 39 | keyholders { room: "Main Area" name: "N" key: "n" } |
| 37 | } | 40 | } |
| diff --git a/data/maps/the_shop/rooms/Main Area.txtpb b/data/maps/the_shop/rooms/Main Area.txtpb index db93fe1..df1cb14 100644 --- a/data/maps/the_shop/rooms/Main Area.txtpb +++ b/data/maps/the_shop/rooms/Main Area.txtpb | |||
| @@ -155,7 +155,10 @@ panels { | |||
| 155 | } | 155 | } |
| 156 | ports { | 156 | ports { |
| 157 | name: "ENTRY" | 157 | name: "ENTRY" |
| 158 | display_name: "Entrance" | ||
| 158 | path: "Components/Warps/worldport" | 159 | path: "Components/Warps/worldport" |
| 160 | destination { x: 4 y: 0 z: 12 } | ||
| 161 | rotation: 90 | ||
| 159 | } | 162 | } |
| 160 | keyholders { | 163 | keyholders { |
| 161 | name: "N" | 164 | name: "N" |
| diff --git a/data/maps/the_sirenic/metadata.txtpb b/data/maps/the_sirenic/metadata.txtpb index 19e26a3..80b1783 100644 --- a/data/maps/the_sirenic/metadata.txtpb +++ b/data/maps/the_sirenic/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Sirenic" | 1 | display_name: "The Sirenic" |
| 2 | worldport_entrance { | ||
| 3 | room: "Start" | ||
| 4 | name: "PLAZA" | ||
| 5 | } | ||
| diff --git a/data/maps/the_sirenic/rooms/Start.txtpb b/data/maps/the_sirenic/rooms/Start.txtpb index 9014e6d..532d951 100644 --- a/data/maps/the_sirenic/rooms/Start.txtpb +++ b/data/maps/the_sirenic/rooms/Start.txtpb | |||
| @@ -15,6 +15,8 @@ panels { | |||
| 15 | } | 15 | } |
| 16 | ports { | 16 | ports { |
| 17 | name: "PLAZA" | 17 | name: "PLAZA" |
| 18 | display_name: "Entrance" | ||
| 18 | path: "Components/Warps/worldport" | 19 | path: "Components/Warps/worldport" |
| 19 | orientation: "south" | 20 | destination { x: 0 y: 0 z: 26 } |
| 21 | rotation: 0 | ||
| 20 | } | 22 | } |
| diff --git a/data/maps/the_stellar/connections.txtpb b/data/maps/the_stellar/connections.txtpb new file mode 100644 index 0000000..3bfea31 --- /dev/null +++ b/data/maps/the_stellar/connections.txtpb | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | connections { | ||
| 2 | from_room: "Starting Room" | ||
| 3 | to_room: "Connected Area" | ||
| 4 | door { name: "Entrance" } | ||
| 5 | } | ||
| 6 | connections { | ||
| 7 | from_room: "Connected Area" | ||
| 8 | to_room: "Mastery" | ||
| 9 | door { name: "Mastery Door" } | ||
| 10 | } | ||
| 11 | connections { | ||
| 12 | from_room: "Connected Area" | ||
| 13 | to_room: "Hi Room" | ||
| 14 | door { name: "Hi Room Front Door" } | ||
| 15 | } | ||
| 16 | connections { | ||
| 17 | from_room: "Connected Area" | ||
| 18 | to_room: "Hi Room" | ||
| 19 | door { name: "Hi Room Back Door" } | ||
| 20 | } | ||
| 21 | connections { | ||
| 22 | from_room: "Green Area" | ||
| 23 | to_room: "Connected Area" | ||
| 24 | door { name: "Green Area Door" } | ||
| 25 | } | ||
| 26 | connections { | ||
| 27 | from_room: "Connected Area" | ||
| 28 | to_room: "Old Crossroads" | ||
| 29 | door { name: "Crossroads Shortcut" } | ||
| 30 | } | ||
| 31 | connections { | ||
| 32 | from_room: "Connected Area" | ||
| 33 | to_room: "Old Crossroads" | ||
| 34 | oneway: true | ||
| 35 | } | ||
| 36 | connections { | ||
| 37 | from_room: "Old Crossroads" | ||
| 38 | to_room: "Green Area" | ||
| 39 | oneway: true | ||
| 40 | } | ||
| 41 | connections { | ||
| 42 | from_room: "Connected Area" | ||
| 43 | to_room: "Red Panel" | ||
| 44 | door { name: "Red Panel" } | ||
| 45 | } | ||
| 46 | connections { | ||
| 47 | from_room: "Connected Area" | ||
| 48 | to_room: "Orange Panel" | ||
| 49 | door { name: "Orange Panel" } | ||
| 50 | } | ||
| 51 | connections { | ||
| 52 | from_room: "Connected Area" | ||
| 53 | to_room: "Yellow Panel" | ||
| 54 | door { name: "Yellow Panel" } | ||
| 55 | } | ||
| 56 | connections { | ||
| 57 | from_room: "Green Area" | ||
| 58 | to_room: "Green Panel" | ||
| 59 | door { name: "Green Panel" } | ||
| 60 | } | ||
| 61 | connections { | ||
| 62 | from_room: "Connected Area" | ||
| 63 | to_room: "Blue Panel" | ||
| 64 | door { name: "Blue Panel" } | ||
| 65 | } | ||
| 66 | connections { | ||
| 67 | from_room: "Connected Area" | ||
| 68 | to_room: "Purple Panel" | ||
| 69 | door { name: "Purple Panel" } | ||
| 70 | } | ||
| diff --git a/data/maps/the_stellar/doors.txtpb b/data/maps/the_stellar/doors.txtpb new file mode 100644 index 0000000..1359189 --- /dev/null +++ b/data/maps/the_stellar/doors.txtpb | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | # Shortcuts from Connected Area -> Starting Room: | ||
| 2 | # - Components/Doors/entry_3 | ||
| 3 | # - Components/Doors/entry_11 | ||
| 4 | # - Components/Doors/entry_12 | ||
| 5 | # Unopenable door: | ||
| 6 | # - Components/Doors/entry_4 | ||
| 7 | # - Components/Doors/entry_6 | ||
| 8 | # - Components/Doors/entry_7 | ||
| 9 | # This opens and closes automatically: | ||
| 10 | # - Components/Doors/entry_5 | ||
| 11 | doors { | ||
| 12 | name: "Entrance" | ||
| 13 | type: STANDARD | ||
| 14 | receivers: "Components/Doors/entry_1" | ||
| 15 | receivers: "Components/Doors/entry_2" | ||
| 16 | panels { room: "Starting Room" name: "STARLIKE" } | ||
| 17 | location_room: "Starting Room" | ||
| 18 | } | ||
| 19 | doors { | ||
| 20 | name: "Mastery Door" | ||
| 21 | type: EVENT | ||
| 22 | receivers: "Components/Doors/entry_18" | ||
| 23 | panels { room: "Purple Panel" name: "PURPLE" } | ||
| 24 | } | ||
| 25 | doors { | ||
| 26 | name: "Hi Room Front Door" | ||
| 27 | type: EVENT | ||
| 28 | receivers: "Components/Doors/entry_21" | ||
| 29 | panels { room: "Connected Area" name: "HI" } | ||
| 30 | } | ||
| 31 | doors { | ||
| 32 | name: "Hi Room Back Door" | ||
| 33 | type: EVENT | ||
| 34 | receivers: "Components/Doors/entry_20" | ||
| 35 | panels { room: "Hi Room" name: "HI" } | ||
| 36 | } | ||
| 37 | doors { | ||
| 38 | name: "Green Area Door" | ||
| 39 | type: EVENT | ||
| 40 | receivers: "Components/Doors/entry_15" | ||
| 41 | panels { room: "Green Area" name: "STRAYS" } | ||
| 42 | } | ||
| 43 | doors { | ||
| 44 | name: "Crossroads Shortcut" | ||
| 45 | type: EVENT | ||
| 46 | receivers: "Components/Doors/entry_14" | ||
| 47 | panels { room: "Old Crossroads" name: "DOORWAY" } | ||
| 48 | } | ||
| 49 | doors { | ||
| 50 | name: "Red Panel" | ||
| 51 | type: EVENT | ||
| 52 | receivers: "Components/Doors/entry_8" | ||
| 53 | panels { room: "Connected Area" name: "START" } | ||
| 54 | } | ||
| 55 | doors { | ||
| 56 | name: "Orange Panel" | ||
| 57 | type: EVENT | ||
| 58 | receivers: "Components/Doors/entry_19" | ||
| 59 | panels { room: "Red Panel" name: "RED" } | ||
| 60 | } | ||
| 61 | doors { | ||
| 62 | name: "Yellow Panel" | ||
| 63 | type: EVENT | ||
| 64 | receivers: "Components/Doors/entry_10" | ||
| 65 | receivers: "Components/Doors/entry_17" | ||
| 66 | panels { room: "Connected Area" name: "START" } | ||
| 67 | panels { room: "Orange Panel" name: "ORANGE" } | ||
| 68 | } | ||
| 69 | doors { | ||
| 70 | name: "Green Panel" | ||
| 71 | type: EVENT | ||
| 72 | receivers: "Components/Doors/entry_16" | ||
| 73 | panels { room: "Yellow Panel" name: "YELLOW" } | ||
| 74 | } | ||
| 75 | doors { | ||
| 76 | name: "Blue Panel" | ||
| 77 | type: EVENT | ||
| 78 | receivers: "Components/Doors/entry_9" | ||
| 79 | panels { room: "Green Panel" name: "GREEN" } | ||
| 80 | } | ||
| 81 | doors { | ||
| 82 | name: "Purple Panel" | ||
| 83 | type: EVENT | ||
| 84 | receivers: "Components/Doors/entry_13" | ||
| 85 | panels { room: "Blue Panel" name: "BLUE" } | ||
| 86 | } | ||
| 87 | doors { | ||
| 88 | name: "Question Panels" | ||
| 89 | type: LOCATION_ONLY | ||
| 90 | panels { room: "Connected Area" name: "HERE" } | ||
| 91 | panels { room: "Connected Area" name: "WHERE" } | ||
| 92 | panels { room: "Connected Area" name: "QUESTION (1)" } | ||
| 93 | panels { room: "Connected Area" name: "QUESTION (2)" } | ||
| 94 | location_room: "Connected Area" | ||
| 95 | } | ||
| 96 | doors { | ||
| 97 | name: "Welcome Back Panels" | ||
| 98 | type: LOCATION_ONLY | ||
| 99 | panels { room: "Connected Area" name: "GREETINGS" } | ||
| 100 | panels { room: "Connected Area" name: "BEHIND" } | ||
| 101 | panels { room: "Connected Area" name: "Blank" } | ||
| 102 | location_room: "Connected Area" | ||
| 103 | location_name: "BEHIND, GREETINGS, Blank" | ||
| 104 | } | ||
| diff --git a/data/maps/the_stellar/metadata.txtpb b/data/maps/the_stellar/metadata.txtpb new file mode 100644 index 0000000..aaf6631 --- /dev/null +++ b/data/maps/the_stellar/metadata.txtpb | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | display_name: "The Stellar" | ||
| 2 | type: GIFT_MAP | ||
| 3 | # This panel does not appear to be accessible without sniping. | ||
| 4 | excluded_nodes: "Panels/Room_1/panel_2" | ||
| 5 | # The map's mastery is created at runtime. | ||
| 6 | custom_nodes: "Components/Collectables/collectable" | ||
| diff --git a/data/maps/the_stellar/rooms/Blue Panel.txtpb b/data/maps/the_stellar/rooms/Blue Panel.txtpb new file mode 100644 index 0000000..cba885f --- /dev/null +++ b/data/maps/the_stellar/rooms/Blue Panel.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Blue Panel" | ||
| 2 | panels { | ||
| 3 | name: "BLUE" | ||
| 4 | path: "Panels/Colors/blue" | ||
| 5 | clue: "blue" | ||
| 6 | answer: "purple" | ||
| 7 | symbols: BOXES | ||
| 8 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Connected Area.txtpb b/data/maps/the_stellar/rooms/Connected Area.txtpb new file mode 100644 index 0000000..90d9693 --- /dev/null +++ b/data/maps/the_stellar/rooms/Connected Area.txtpb | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | name: "Connected Area" | ||
| 2 | panels { | ||
| 3 | name: "HERE" | ||
| 4 | path: "Panels/Room_1/panel_3" | ||
| 5 | clue: "here" | ||
| 6 | answer: "where" | ||
| 7 | symbols: SPARKLES | ||
| 8 | } | ||
| 9 | panels { | ||
| 10 | name: "QUESTION (1)" | ||
| 11 | path: "Panels/Room_1/panel_4" | ||
| 12 | clue: "question" | ||
| 13 | answer: "what" | ||
| 14 | symbols: EXAMPLE | ||
| 15 | } | ||
| 16 | panels { | ||
| 17 | name: "QUESTION (2)" | ||
| 18 | path: "Panels/Room_1/panel_5" | ||
| 19 | clue: "question" | ||
| 20 | answer: "how" | ||
| 21 | symbols: EXAMPLE | ||
| 22 | } | ||
| 23 | panels { | ||
| 24 | name: "HI" | ||
| 25 | path: "Panels/Room_1/panel_12" | ||
| 26 | clue: "hi" | ||
| 27 | answer: "hi" | ||
| 28 | symbols: QUESTION | ||
| 29 | } | ||
| 30 | panels { | ||
| 31 | name: "WHERE" | ||
| 32 | path: "Panels/Room_1/panel_6" | ||
| 33 | clue: "where" | ||
| 34 | answer: "there" | ||
| 35 | symbols: SPARKLES | ||
| 36 | } | ||
| 37 | panels { | ||
| 38 | name: "GREETINGS" | ||
| 39 | path: "Panels/Room_1/panel_7" | ||
| 40 | clue: "greetings" | ||
| 41 | answer: "welcome" | ||
| 42 | symbols: SUN | ||
| 43 | } | ||
| 44 | panels { | ||
| 45 | name: "BEHIND" | ||
| 46 | path: "Panels/Room_1/panel_8" | ||
| 47 | clue: "behind" | ||
| 48 | answer: "back" | ||
| 49 | symbols: SUN | ||
| 50 | } | ||
| 51 | panels { | ||
| 52 | name: "Blank" | ||
| 53 | path: "Panels/Room_1/panel_9" | ||
| 54 | clue: "" | ||
| 55 | answer: "behind" | ||
| 56 | } | ||
| 57 | panels { | ||
| 58 | name: "START" | ||
| 59 | path: "Panels/Colors/start" | ||
| 60 | clue: "start" | ||
| 61 | answer: "red" | ||
| 62 | symbols: QUESTION | ||
| 63 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Green Area.txtpb b/data/maps/the_stellar/rooms/Green Area.txtpb new file mode 100644 index 0000000..366b5c4 --- /dev/null +++ b/data/maps/the_stellar/rooms/Green Area.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Green Area" | ||
| 2 | panels { | ||
| 3 | name: "STRAYS" | ||
| 4 | path: "Panels/Room_1/panel_11" | ||
| 5 | clue: "strays" | ||
| 6 | answer: "maze" | ||
| 7 | symbols: ZERO | ||
| 8 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Green Panel.txtpb b/data/maps/the_stellar/rooms/Green Panel.txtpb new file mode 100644 index 0000000..5b2f561 --- /dev/null +++ b/data/maps/the_stellar/rooms/Green Panel.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Green Panel" | ||
| 2 | panels { | ||
| 3 | name: "GREEN" | ||
| 4 | path: "Panels/Colors/green" | ||
| 5 | clue: "green" | ||
| 6 | answer: "blue" | ||
| 7 | symbols: BOXES | ||
| 8 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Hi Room.txtpb b/data/maps/the_stellar/rooms/Hi Room.txtpb new file mode 100644 index 0000000..4da7462 --- /dev/null +++ b/data/maps/the_stellar/rooms/Hi Room.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Hi Room" | ||
| 2 | panels { | ||
| 3 | name: "HI" | ||
| 4 | path: "Panels/Room_1/panel_13" | ||
| 5 | clue: "hi" | ||
| 6 | answer: "hi" | ||
| 7 | symbols: QUESTION | ||
| 8 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Mastery.txtpb b/data/maps/the_stellar/rooms/Mastery.txtpb new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/data/maps/the_stellar/rooms/Mastery.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mastery" | ||
| 2 | masteries { | ||
| 3 | name: "MASTERY" | ||
| 4 | path: "Components/Collectables/collectable" | ||
| 5 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Old Crossroads.txtpb b/data/maps/the_stellar/rooms/Old Crossroads.txtpb new file mode 100644 index 0000000..47f1550 --- /dev/null +++ b/data/maps/the_stellar/rooms/Old Crossroads.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Old Crossroads" | ||
| 2 | panels { | ||
| 3 | name: "DOORWAY" | ||
| 4 | path: "Panels/Room_1/panel_10" | ||
| 5 | clue: "doorway" | ||
| 6 | answer: "hallway" | ||
| 7 | symbols: BOXES | ||
| 8 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Orange Panel.txtpb b/data/maps/the_stellar/rooms/Orange Panel.txtpb new file mode 100644 index 0000000..84bfa92 --- /dev/null +++ b/data/maps/the_stellar/rooms/Orange Panel.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Orange Panel" | ||
| 2 | panels { | ||
| 3 | name: "ORANGE" | ||
| 4 | path: "Panels/Colors/orange" | ||
| 5 | clue: "orange" | ||
| 6 | answer: "yellow" | ||
| 7 | symbols: BOXES | ||
| 8 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Purple Panel.txtpb b/data/maps/the_stellar/rooms/Purple Panel.txtpb new file mode 100644 index 0000000..3607679 --- /dev/null +++ b/data/maps/the_stellar/rooms/Purple Panel.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Purple Panel" | ||
| 2 | panels { | ||
| 3 | name: "PURPLE" | ||
| 4 | path: "Panels/Colors/purple" | ||
| 5 | clue: "purple" | ||
| 6 | answer: "end" | ||
| 7 | symbols: QUESTION | ||
| 8 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Red Panel.txtpb b/data/maps/the_stellar/rooms/Red Panel.txtpb new file mode 100644 index 0000000..9d70f03 --- /dev/null +++ b/data/maps/the_stellar/rooms/Red Panel.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Red Panel" | ||
| 2 | panels { | ||
| 3 | name: "RED" | ||
| 4 | path: "Panels/Colors/red" | ||
| 5 | clue: "red" | ||
| 6 | answer: "orange" | ||
| 7 | symbols: BOXES | ||
| 8 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Starting Room.txtpb b/data/maps/the_stellar/rooms/Starting Room.txtpb new file mode 100644 index 0000000..5937509 --- /dev/null +++ b/data/maps/the_stellar/rooms/Starting Room.txtpb | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | name: "Starting Room" | ||
| 2 | panels { | ||
| 3 | name: "STARLIKE" | ||
| 4 | path: "Panels/Room_1/panel_1" | ||
| 5 | clue: "starlike" | ||
| 6 | answer: "stellar" | ||
| 7 | symbols: SUN | ||
| 8 | } | ||
| 9 | ports { | ||
| 10 | name: "WORLDPORT" | ||
| 11 | display_name: "Entrance" | ||
| 12 | path: "Components/Warps/worldport" | ||
| 13 | destination { x: 0 y: 0 z: 0 } | ||
| 14 | rotation: 0 | ||
| 15 | } | ||
| diff --git a/data/maps/the_stellar/rooms/Yellow Panel.txtpb b/data/maps/the_stellar/rooms/Yellow Panel.txtpb new file mode 100644 index 0000000..9d2b0c2 --- /dev/null +++ b/data/maps/the_stellar/rooms/Yellow Panel.txtpb | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | name: "Yellow Panel" | ||
| 2 | panels { | ||
| 3 | name: "YELLOW" | ||
| 4 | path: "Panels/Colors/yellow" | ||
| 5 | clue: "yellow" | ||
| 6 | answer: "green" | ||
| 7 | symbols: BOXES | ||
| 8 | } | ||
| diff --git a/data/maps/the_stormy/rooms/Center.txtpb b/data/maps/the_stormy/rooms/Center.txtpb index f0e3e39..6a929a7 100644 --- a/data/maps/the_stormy/rooms/Center.txtpb +++ b/data/maps/the_stormy/rooms/Center.txtpb | |||
| @@ -31,6 +31,8 @@ panels { | |||
| 31 | } | 31 | } |
| 32 | ports { | 32 | ports { |
| 33 | name: "ENTRY" | 33 | name: "ENTRY" |
| 34 | display_name: "Worldport" | ||
| 34 | path: "Components/Warps/worldport" | 35 | path: "Components/Warps/worldport" |
| 35 | orientation: "west" | 36 | destination { x: -8.5 y: 0 z: 6 } |
| 37 | rotation: 270 | ||
| 36 | } | 38 | } |
| diff --git a/data/maps/the_sturdy/connections.txtpb b/data/maps/the_sturdy/connections.txtpb index efa67c2..341d99e 100644 --- a/data/maps/the_sturdy/connections.txtpb +++ b/data/maps/the_sturdy/connections.txtpb | |||
| @@ -3,3 +3,8 @@ connections { | |||
| 3 | to_room: "S2 Area" | 3 | to_room: "S2 Area" |
| 4 | door { name: "Color Puzzle" } | 4 | door { name: "Color Puzzle" } |
| 5 | } | 5 | } |
| 6 | connections { | ||
| 7 | from_room: "Main Area" | ||
| 8 | to_room: "Hidden Rainbow" | ||
| 9 | door { name: "Hidden Rainbow" } | ||
| 10 | } | ||
| diff --git a/data/maps/the_sturdy/doors.txtpb b/data/maps/the_sturdy/doors.txtpb index 9d37064..819f568 100644 --- a/data/maps/the_sturdy/doors.txtpb +++ b/data/maps/the_sturdy/doors.txtpb | |||
| @@ -10,3 +10,9 @@ doors { | |||
| 10 | panels { room: "Main Area" name: "MOVE (7)" answer: "back" } | 10 | panels { room: "Main Area" name: "MOVE (7)" answer: "back" } |
| 11 | panels { room: "Main Area" name: "MOVE (8)" answer: "down" } | 11 | panels { room: "Main Area" name: "MOVE (8)" answer: "down" } |
| 12 | } | 12 | } |
| 13 | doors { | ||
| 14 | name: "Hidden Rainbow" | ||
| 15 | type: EVENT | ||
| 16 | panels { room: "Main Area" name: "MOVE (2)" answer: "move" } | ||
| 17 | panels { room: "Main Area" name: "MOVE (4)" answer: "move" } | ||
| 18 | } | ||
| diff --git a/data/maps/the_sturdy/metadata.txtpb b/data/maps/the_sturdy/metadata.txtpb index 9f42137..bdc5a94 100644 --- a/data/maps/the_sturdy/metadata.txtpb +++ b/data/maps/the_sturdy/metadata.txtpb | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | display_name: "The Sturdy" | 1 | display_name: "The Sturdy" |
| 2 | # Let's ignore the second half of the rainbow for now. | 2 | # Let's ignore the second half of the rainbows for now. |
| 3 | #excluded_nodes: "Components/Doors/Rainbow2/Hinge/rainbowMirrored" | 3 | #excluded_nodes: "Components/Doors/Rainbow2/Hinge/rainbowMirrored" |
| 4 | # I don't know why there's a second copy of the rainbow. | ||
| 5 | #excluded_nodes: "Components/Doors/Rainbow/Hinge/rainbow" | ||
| 6 | #excluded_nodes: "Components/Doors/Rainbow/Hinge/rainbowMirrored" | 4 | #excluded_nodes: "Components/Doors/Rainbow/Hinge/rainbowMirrored" |
| 5 | # The validator doesn't know that these node exist because they are part of a | ||
| 6 | # sub-scene. | ||
| 7 | custom_nodes: "Components/Doors/Rainbow/Hinge/rainbow" | ||
| 8 | custom_nodes: "Components/Doors/Rainbow2/Hinge/rainbow" | ||
| diff --git a/data/maps/the_sturdy/rooms/Hidden Rainbow.txtpb b/data/maps/the_sturdy/rooms/Hidden Rainbow.txtpb new file mode 100644 index 0000000..215def8 --- /dev/null +++ b/data/maps/the_sturdy/rooms/Hidden Rainbow.txtpb | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | name: "Hidden Rainbow" | ||
| 2 | paintings { | ||
| 3 | name: "RAINBOW" | ||
| 4 | path: "Components/Doors/Rainbow/Hinge/rainbow" | ||
| 5 | enter_only: true | ||
| 6 | } | ||
| diff --git a/data/maps/the_sturdy/rooms/Main Area.txtpb b/data/maps/the_sturdy/rooms/Main Area.txtpb index c437ceb..8c81a1e 100644 --- a/data/maps/the_sturdy/rooms/Main Area.txtpb +++ b/data/maps/the_sturdy/rooms/Main Area.txtpb | |||
| @@ -105,9 +105,15 @@ panels { | |||
| 105 | } | 105 | } |
| 106 | ports { | 106 | ports { |
| 107 | name: "OWL" | 107 | name: "OWL" |
| 108 | display_name: "Magenta Hallway" | ||
| 108 | path: "Components/Warps/worldport" | 109 | path: "Components/Warps/worldport" |
| 110 | destination { x: 17 y: 0 z: 41 } | ||
| 111 | rotation: 90 | ||
| 109 | } | 112 | } |
| 110 | ports { | 113 | ports { |
| 111 | name: "COLORFUL" | 114 | name: "COLORFUL" |
| 115 | display_name: "Cyan Hallway" | ||
| 112 | path: "Components/Warps/worldport2" | 116 | path: "Components/Warps/worldport2" |
| 117 | destination { x: 17 y: 0 z: -33 } | ||
| 118 | rotation: 90 | ||
| 113 | } | 119 | } |
| diff --git a/data/maps/the_sturdy/rooms/S2 Area.txtpb b/data/maps/the_sturdy/rooms/S2 Area.txtpb index 38fad5e..745f78f 100644 --- a/data/maps/the_sturdy/rooms/S2 Area.txtpb +++ b/data/maps/the_sturdy/rooms/S2 Area.txtpb | |||
| @@ -12,9 +12,7 @@ letters { | |||
| 12 | path: "Components/Collectables/collectable" | 12 | path: "Components/Collectables/collectable" |
| 13 | } | 13 | } |
| 14 | paintings { | 14 | paintings { |
| 15 | name: "RAINBOW" | 15 | name: "RAINBOW2" |
| 16 | # The validator is wrong about this node not existing, because it's in a | ||
| 17 | # sub-scene. | ||
| 18 | path: "Components/Doors/Rainbow2/Hinge/rainbow" | 16 | path: "Components/Doors/Rainbow2/Hinge/rainbow" |
| 19 | enter_only: true | 17 | enter_only: true |
| 20 | } | 18 | } |
| 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_sun_temple/metadata.txtpb b/data/maps/the_sun_temple/metadata.txtpb index 97f9290..25ed636 100644 --- a/data/maps/the_sun_temple/metadata.txtpb +++ b/data/maps/the_sun_temple/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Sun Temple" | 1 | display_name: "The Sun Temple" |
| 2 | worldport_entrance { | ||
| 3 | room: "Entrance" | ||
| 4 | name: "UNKEMPT" | ||
| 5 | } | ||
| diff --git a/data/maps/the_sun_temple/rooms/Entrance.txtpb b/data/maps/the_sun_temple/rooms/Entrance.txtpb index f9da822..07d6e38 100644 --- a/data/maps/the_sun_temple/rooms/Entrance.txtpb +++ b/data/maps/the_sun_temple/rooms/Entrance.txtpb | |||
| @@ -9,5 +9,8 @@ panels { | |||
| 9 | } | 9 | } |
| 10 | ports { | 10 | ports { |
| 11 | name: "UNKEMPT" | 11 | name: "UNKEMPT" |
| 12 | display_name: "Entrance" | ||
| 12 | path: "Components/Warps/worldport" | 13 | path: "Components/Warps/worldport" |
| 14 | destination { x: 0 y: 0 z: 13 } | ||
| 15 | rotation: 0 | ||
| 13 | } | 16 | } |
| diff --git a/data/maps/the_sweet/rooms/Main Area.txtpb b/data/maps/the_sweet/rooms/Main Area.txtpb index d4e6fda..a8976f7 100644 --- a/data/maps/the_sweet/rooms/Main Area.txtpb +++ b/data/maps/the_sweet/rooms/Main Area.txtpb | |||
| @@ -200,9 +200,15 @@ panels { | |||
| 200 | } | 200 | } |
| 201 | ports { | 201 | ports { |
| 202 | name: "EXIT1" | 202 | name: "EXIT1" |
| 203 | display_name: "South Worldport" | ||
| 203 | path: "Components/Warps/worldport" | 204 | path: "Components/Warps/worldport" |
| 205 | destination { x: 0 y: 0 z: -11.5 } | ||
| 206 | rotation: 180 | ||
| 204 | } | 207 | } |
| 205 | ports { | 208 | ports { |
| 206 | name: "EXIT2" | 209 | name: "EXIT2" |
| 210 | display_name: "North Worldport" | ||
| 207 | path: "Components/Warps/worldport2" | 211 | path: "Components/Warps/worldport2" |
| 212 | destination { x: 0 y: 0 z: -17.5 } | ||
| 213 | rotation: 0 | ||
| 208 | } | 214 | } |
| diff --git a/data/maps/the_symbolic/metadata.txtpb b/data/maps/the_symbolic/metadata.txtpb index 311dead..2b37985 100644 --- a/data/maps/the_symbolic/metadata.txtpb +++ b/data/maps/the_symbolic/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Symbolic" | 1 | display_name: "The Symbolic" |
| 2 | worldport_entrance { | ||
| 3 | room: "White Room" | ||
| 4 | name: "PLAZA" | ||
| 5 | } | ||
| diff --git a/data/maps/the_symbolic/rooms/White Room.txtpb b/data/maps/the_symbolic/rooms/White Room.txtpb index 808588e..d3509cb 100644 --- a/data/maps/the_symbolic/rooms/White Room.txtpb +++ b/data/maps/the_symbolic/rooms/White Room.txtpb | |||
| @@ -7,5 +7,8 @@ panels { | |||
| 7 | } | 7 | } |
| 8 | ports { | 8 | ports { |
| 9 | name: "PLAZA" | 9 | name: "PLAZA" |
| 10 | display_name: "Entrance" | ||
| 10 | path: "Components/Warps/worldport" | 11 | path: "Components/Warps/worldport" |
| 12 | destination { x: 0 y: 0 z: 2.5 } | ||
| 13 | rotation: 0 | ||
| 11 | } | 14 | } |
| diff --git a/data/maps/the_talented/doors.txtpb b/data/maps/the_talented/doors.txtpb index d4d3148..766e003 100644 --- a/data/maps/the_talented/doors.txtpb +++ b/data/maps/the_talented/doors.txtpb | |||
| @@ -52,3 +52,10 @@ doors { | |||
| 52 | panels { room: "Back Room" name: "RELEVANT" } | 52 | panels { room: "Back Room" name: "RELEVANT" } |
| 53 | panels { room: "Back Room" name: "LONE" } | 53 | panels { room: "Back Room" name: "LONE" } |
| 54 | } | 54 | } |
| 55 | doors { | ||
| 56 | name: "Keyholder Hint Panel" | ||
| 57 | type: LOCATION_ONLY | ||
| 58 | panels { room: "Main Area" name: "EARL" } | ||
| 59 | location_room: "Main Area" | ||
| 60 | location_name: "EARL" | ||
| 61 | } | ||
| diff --git a/data/maps/the_talented/metadata.txtpb b/data/maps/the_talented/metadata.txtpb index 16aa221..943bc69 100644 --- a/data/maps/the_talented/metadata.txtpb +++ b/data/maps/the_talented/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Talented" | 1 | display_name: "The Talented" |
| 2 | worldport_entrance { | ||
| 3 | room: "Main Area" | ||
| 4 | name: "GREAT" | ||
| 5 | } | ||
| diff --git a/data/maps/the_talented/rooms/Main Area.txtpb b/data/maps/the_talented/rooms/Main Area.txtpb index f99be48..a0dac7b 100644 --- a/data/maps/the_talented/rooms/Main Area.txtpb +++ b/data/maps/the_talented/rooms/Main Area.txtpb | |||
| @@ -111,5 +111,8 @@ keyholders { | |||
| 111 | } | 111 | } |
| 112 | ports { | 112 | ports { |
| 113 | name: "GREAT" | 113 | name: "GREAT" |
| 114 | display_name: "Entrance" | ||
| 114 | path: "Components/Warps/worldport" | 115 | path: "Components/Warps/worldport" |
| 116 | destination { x: -3.5 y: 0 z: 21 } | ||
| 117 | rotation: 270 | ||
| 115 | } | 118 | } |
| diff --git a/data/maps/the_tenacious/doors.txtpb b/data/maps/the_tenacious/doors.txtpb index 8fe8bd5..4c454c1 100644 --- a/data/maps/the_tenacious/doors.txtpb +++ b/data/maps/the_tenacious/doors.txtpb | |||
| @@ -6,6 +6,8 @@ doors { | |||
| 6 | doors { | 6 | doors { |
| 7 | name: "K Entered" | 7 | name: "K Entered" |
| 8 | type: EVENT | 8 | type: EVENT |
| 9 | latch: true | ||
| 10 | receivers: "Components/Doors/entry_6" | ||
| 9 | keyholders { room: "Main Area" name: "K" key: "k" } | 11 | keyholders { room: "Main Area" name: "K" key: "k" } |
| 10 | } | 12 | } |
| 11 | doors { | 13 | doors { |
| diff --git a/data/maps/the_tenacious/rooms/Control Center Entrance.txtpb b/data/maps/the_tenacious/rooms/Control Center Entrance.txtpb index 45a0d12..05a3af3 100644 --- a/data/maps/the_tenacious/rooms/Control Center Entrance.txtpb +++ b/data/maps/the_tenacious/rooms/Control Center Entrance.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "CC" | 10 | name: "CC" |
| 11 | display_name: "Control Center Connector" | ||
| 11 | path: "Components/Warps/worldport" | 12 | path: "Components/Warps/worldport" |
| 13 | destination { x: 0 y: 0 z: 10 } | ||
| 14 | rotation: 0 | ||
| 12 | } | 15 | } |
| diff --git a/data/maps/the_three_doors/rooms/Dead End Room.txtpb b/data/maps/the_three_doors/rooms/Dead End Room.txtpb index c752368..8bfe193 100644 --- a/data/maps/the_three_doors/rooms/Dead End Room.txtpb +++ b/data/maps/the_three_doors/rooms/Dead End Room.txtpb | |||
| @@ -29,9 +29,15 @@ panels { | |||
| 29 | } | 29 | } |
| 30 | ports { | 30 | ports { |
| 31 | name: "BEGIN" | 31 | name: "BEGIN" |
| 32 | display_name: "Door Ways Worldport" | ||
| 32 | path: "Components/Warps/worldport6" | 33 | path: "Components/Warps/worldport6" |
| 34 | destination { x: -38 y: 0 z: 41.5 } | ||
| 35 | rotation: 0 | ||
| 33 | } | 36 | } |
| 34 | ports { | 37 | ports { |
| 35 | name: "BEGIN2" | 38 | name: "BEGIN2" |
| 39 | display_name: "Dead End Worldport" | ||
| 36 | path: "Components/Warps/worldport5" | 40 | path: "Components/Warps/worldport5" |
| 41 | destination { x: -38 y: 0 z: 27.5 } | ||
| 42 | rotation: 180 | ||
| 37 | } | 43 | } |
| diff --git a/data/maps/the_three_doors/rooms/First Second Room.txtpb b/data/maps/the_three_doors/rooms/First Second Room.txtpb index 1bee8c7..bdf5b49 100644 --- a/data/maps/the_three_doors/rooms/First Second Room.txtpb +++ b/data/maps/the_three_doors/rooms/First Second Room.txtpb | |||
| @@ -29,9 +29,15 @@ panels { | |||
| 29 | } | 29 | } |
| 30 | ports { | 30 | ports { |
| 31 | name: "GREAT" | 31 | name: "GREAT" |
| 32 | display_name: "First Worldport" | ||
| 32 | path: "Components/Warps/worldport" | 33 | path: "Components/Warps/worldport" |
| 34 | destination { x: -16 y: 0 z: 0.5 } | ||
| 35 | rotation: 180 | ||
| 33 | } | 36 | } |
| 34 | ports { | 37 | ports { |
| 35 | name: "TTD" | 38 | name: "TTD" |
| 39 | display_name: "Second Worldport" | ||
| 36 | path: "Components/Warps/worldport2" | 40 | path: "Components/Warps/worldport2" |
| 41 | destination { x: -16 y: 0 z: 14.5 } | ||
| 42 | rotation: 0 | ||
| 37 | } | 43 | } |
| diff --git a/data/maps/the_three_doors/rooms/Loose Strings Room.txtpb b/data/maps/the_three_doors/rooms/Loose Strings Room.txtpb index 9d4430f..21e3c64 100644 --- a/data/maps/the_three_doors/rooms/Loose Strings Room.txtpb +++ b/data/maps/the_three_doors/rooms/Loose Strings Room.txtpb | |||
| @@ -15,5 +15,8 @@ panels { | |||
| 15 | } | 15 | } |
| 16 | ports { | 16 | ports { |
| 17 | name: "BEGIN" | 17 | name: "BEGIN" |
| 18 | display_name: "Loose Strings Worldport" | ||
| 18 | path: "Components/Warps/worldport7" | 19 | path: "Components/Warps/worldport7" |
| 20 | destination { x: -16 y: 0 z: 41.5 } | ||
| 21 | rotation: 0 | ||
| 19 | } | 22 | } |
| diff --git a/data/maps/the_three_doors/rooms/One Luck Room.txtpb b/data/maps/the_three_doors/rooms/One Luck Room.txtpb index 816b4e3..f5053be 100644 --- a/data/maps/the_three_doors/rooms/One Luck Room.txtpb +++ b/data/maps/the_three_doors/rooms/One Luck Room.txtpb | |||
| @@ -15,5 +15,8 @@ panels { | |||
| 15 | } | 15 | } |
| 16 | ports { | 16 | ports { |
| 17 | name: "BEGIN" | 17 | name: "BEGIN" |
| 18 | display_name: "Lone Chance Worldport" | ||
| 18 | path: "Components/Warps/worldport8" | 19 | path: "Components/Warps/worldport8" |
| 20 | destination { x: -16 y: 0 z: 27.5 } | ||
| 21 | rotation: 180 | ||
| 19 | } | 22 | } |
| diff --git a/data/maps/the_three_doors/rooms/Silver Portal Room.txtpb b/data/maps/the_three_doors/rooms/Silver Portal Room.txtpb index aeab9da..1c00045 100644 --- a/data/maps/the_three_doors/rooms/Silver Portal Room.txtpb +++ b/data/maps/the_three_doors/rooms/Silver Portal Room.txtpb | |||
| @@ -27,9 +27,15 @@ panels { | |||
| 27 | } | 27 | } |
| 28 | ports { | 28 | ports { |
| 29 | name: "BEGIN" | 29 | name: "BEGIN" |
| 30 | display_name: "Third Fourth Worldport" | ||
| 30 | path: "Components/Warps/worldport3" | 31 | path: "Components/Warps/worldport3" |
| 32 | destination { x: -38 y: 0 z: 14.5 } | ||
| 33 | rotation: 0 | ||
| 31 | } | 34 | } |
| 32 | ports { | 35 | ports { |
| 33 | name: "NEXT" | 36 | name: "NEXT" |
| 37 | display_name: "Silver Portal Worldport" | ||
| 34 | path: "Components/Warps/worldport4" | 38 | path: "Components/Warps/worldport4" |
| 39 | destination { x: -38 y: 0 z: 0.5 } | ||
| 40 | rotation: 180 | ||
| 35 | } | 41 | } |
| diff --git a/data/maps/the_tower/metadata.txtpb b/data/maps/the_tower/metadata.txtpb index dc185e0..3876206 100644 --- a/data/maps/the_tower/metadata.txtpb +++ b/data/maps/the_tower/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Tower" | 1 | display_name: "The Tower" |
| 2 | worldport_entrance { | ||
| 3 | room: "First Floor" | ||
| 4 | name: "GREAT" | ||
| 5 | } | ||
| diff --git a/data/maps/the_tower/rooms/First Floor.txtpb b/data/maps/the_tower/rooms/First Floor.txtpb index 33398a3..7a811bf 100644 --- a/data/maps/the_tower/rooms/First Floor.txtpb +++ b/data/maps/the_tower/rooms/First Floor.txtpb | |||
| @@ -97,5 +97,8 @@ panels { | |||
| 97 | } | 97 | } |
| 98 | ports { | 98 | ports { |
| 99 | name: "GREAT" | 99 | name: "GREAT" |
| 100 | display_name: "Entrance" | ||
| 100 | path: "Components/Warps/worldport" | 101 | path: "Components/Warps/worldport" |
| 102 | destination { x: -0 y: 0 z: 13 } | ||
| 103 | rotation: 0 | ||
| 101 | } | 104 | } |
| diff --git a/data/maps/the_tree/doors.txtpb b/data/maps/the_tree/doors.txtpb index 6cb4086..1932aa7 100644 --- a/data/maps/the_tree/doors.txtpb +++ b/data/maps/the_tree/doors.txtpb | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | doors { | 1 | doors { |
| 2 | name: "Control Center Brown Door" | 2 | name: "Control Center Brown Door" |
| 3 | type: CONTROL_CENTER_COLOR | 3 | type: CONTROL_CENTER_COLOR |
| 4 | latch: true | ||
| 4 | receivers: "Components/Doors/entry_1" | 5 | receivers: "Components/Doors/entry_1" |
| 5 | control_center_color: "brown" | 6 | control_center_color: "brown" |
| 6 | } | 7 | } |
| diff --git a/data/maps/the_tree/rooms/Bearer Entrance.txtpb b/data/maps/the_tree/rooms/Bearer Entrance.txtpb index 797e5d0..263a8e8 100644 --- a/data/maps/the_tree/rooms/Bearer Entrance.txtpb +++ b/data/maps/the_tree/rooms/Bearer Entrance.txtpb | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | name: "Bearer Entrance" | 1 | name: "Bearer Entrance" |
| 2 | ports { | 2 | ports { |
| 3 | name: "BEARER" | 3 | name: "BEARER" |
| 4 | display_name: "Brown Control Center Hallway" | ||
| 4 | path: "Components/Warps/worldport" | 5 | path: "Components/Warps/worldport" |
| 6 | destination { x: -15.5 y: 0 z: 20 } | ||
| 7 | rotation: 270 | ||
| 5 | } | 8 | } |
| diff --git a/data/maps/the_tree/rooms/Main Area.txtpb b/data/maps/the_tree/rooms/Main Area.txtpb index b232d1e..bd22c2b 100644 --- a/data/maps/the_tree/rooms/Main Area.txtpb +++ b/data/maps/the_tree/rooms/Main Area.txtpb | |||
| @@ -211,19 +211,33 @@ panels { | |||
| 211 | } | 211 | } |
| 212 | ports { | 212 | ports { |
| 213 | name: "UNKEMPT" | 213 | name: "UNKEMPT" |
| 214 | display_name: "SW Worldport" | ||
| 214 | path: "Components/Warps/worldport4" | 215 | path: "Components/Warps/worldport4" |
| 216 | destination { x: -21 y: 0 z: 7 } | ||
| 217 | rotation: 0 | ||
| 218 | # enterable from either side | ||
| 215 | } | 219 | } |
| 216 | ports { | 220 | ports { |
| 217 | name: "DIGITAL" | 221 | name: "DIGITAL" |
| 222 | display_name: "NW Worldport" | ||
| 218 | path: "Components/Warps/worldport5" | 223 | path: "Components/Warps/worldport5" |
| 224 | destination { x: -21 y: 0 z: -7 } | ||
| 225 | rotation: 180 | ||
| 226 | # enterable from either side | ||
| 219 | } | 227 | } |
| 220 | ports { | 228 | ports { |
| 221 | name: "GREAT" | 229 | name: "GREAT" |
| 230 | display_name: "E Worldport" | ||
| 222 | path: "Components/Warps/worldport2" | 231 | path: "Components/Warps/worldport2" |
| 232 | destination { x: 21 y: 0 z: -4 } | ||
| 233 | rotation: 180 | ||
| 223 | } | 234 | } |
| 224 | ports { | 235 | ports { |
| 225 | name: "DAEDALUS" | 236 | name: "DAEDALUS" |
| 237 | display_name: "NE Worldport" | ||
| 226 | path: "Components/Warps/worldport3" | 238 | path: "Components/Warps/worldport3" |
| 239 | destination { x: 15.5 y: 0 z: -19 } | ||
| 240 | rotation: 90 | ||
| 227 | } | 241 | } |
| 228 | paintings { | 242 | paintings { |
| 229 | name: "SEA" | 243 | name: "SEA" |
| 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..f758369 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" } |
| @@ -48,6 +48,8 @@ doors { | |||
| 48 | doors { | 48 | doors { |
| 49 | name: "I Entered" | 49 | name: "I Entered" |
| 50 | type: EVENT | 50 | type: EVENT |
| 51 | latch: true | ||
| 52 | receivers: "Components/Doors/entry_4" | ||
| 51 | keyholders { room: "Main Area" name: "I" key: "i" } | 53 | keyholders { room: "Main Area" name: "I" key: "i" } |
| 52 | } | 54 | } |
| 53 | doors { | 55 | doors { |
| @@ -66,9 +68,9 @@ doors { | |||
| 66 | doors { | 68 | doors { |
| 67 | name: "Control Center Orange Door" | 69 | name: "Control Center Orange Door" |
| 68 | type: CONTROL_CENTER_COLOR | 70 | type: CONTROL_CENTER_COLOR |
| 71 | latch: true | ||
| 69 | receivers: "Components/Doors/entry_6" | 72 | receivers: "Components/Doors/entry_6" |
| 70 | receivers: "Components/Doors/entry_13" | 73 | receivers: "Components/Doors/entry_13" |
| 71 | receivers: "Panels/Assorted/panel_1/teleportListener" | ||
| 72 | control_center_color: "orange" | 74 | control_center_color: "orange" |
| 73 | double_letters: true | 75 | double_letters: true |
| 74 | } | 76 | } |
| @@ -182,3 +184,20 @@ doors { | |||
| 182 | panels { room: "Right Area" name: "TOUGH" } | 184 | panels { room: "Right Area" name: "TOUGH" } |
| 183 | location_room: "Right Area" | 185 | location_room: "Right Area" |
| 184 | } | 186 | } |
| 187 | doors { | ||
| 188 | name: "Near Teal Door Panels" | ||
| 189 | type: LOCATION_ONLY | ||
| 190 | panels { room: "Main Area" name: "I" } | ||
| 191 | panels { room: "Main Area" name: "SPY" } | ||
| 192 | panels { room: "Main Area" name: "HEFT" } | ||
| 193 | panels { room: "Main Area" name: "THEFT" } | ||
| 194 | location_room: "Main Area" | ||
| 195 | location_name: "HEFT, I, SPY, THEFT" | ||
| 196 | } | ||
| 197 | doors { | ||
| 198 | name: "Control Center Orange Panel" | ||
| 199 | type: LOCATION_ONLY | ||
| 200 | panels { room: "Right Area" name: "COLOR" } | ||
| 201 | location_room: "Right Area" | ||
| 202 | location_name: "COLOR" | ||
| 203 | } | ||
| diff --git a/data/maps/the_unkempt/rooms/Control Center Entrance.txtpb b/data/maps/the_unkempt/rooms/Control Center Entrance.txtpb index e8fa13a..7971cf7 100644 --- a/data/maps/the_unkempt/rooms/Control Center Entrance.txtpb +++ b/data/maps/the_unkempt/rooms/Control Center Entrance.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "CC" | 10 | name: "CC" |
| 11 | display_name: "Control Center Connector" | ||
| 11 | path: "Components/Warps/worldport2" | 12 | path: "Components/Warps/worldport2" |
| 13 | destination { x: -4.5 y: 0 z: 7 } | ||
| 14 | rotation: 0 | ||
| 12 | } | 15 | } |
| diff --git a/data/maps/the_unkempt/rooms/Daedalus Entrance.txtpb b/data/maps/the_unkempt/rooms/Daedalus Entrance.txtpb index 851c863..f20d2cf 100644 --- a/data/maps/the_unkempt/rooms/Daedalus Entrance.txtpb +++ b/data/maps/the_unkempt/rooms/Daedalus Entrance.txtpb | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | name: "Daedalus Entrance" | 1 | name: "Daedalus Entrance" |
| 2 | ports { | 2 | ports { |
| 3 | name: "DAEDALUS" | 3 | name: "DAEDALUS" |
| 4 | display_name: "Orange Hallway" | ||
| 4 | path: "Components/Warps/worldport4" | 5 | path: "Components/Warps/worldport4" |
| 6 | destination { x: 33 y: 0 z: -10 } | ||
| 7 | rotation: 90 | ||
| 5 | } | 8 | } |
| diff --git a/data/maps/the_unkempt/rooms/Main Area.txtpb b/data/maps/the_unkempt/rooms/Main Area.txtpb index b5d29c4..f98220d 100644 --- a/data/maps/the_unkempt/rooms/Main Area.txtpb +++ b/data/maps/the_unkempt/rooms/Main Area.txtpb | |||
| @@ -216,14 +216,23 @@ keyholders { | |||
| 216 | } | 216 | } |
| 217 | ports { | 217 | ports { |
| 218 | name: "GREAT" | 218 | name: "GREAT" |
| 219 | display_name: "Main Entrance" | ||
| 219 | path: "Components/Warps/worldport" | 220 | path: "Components/Warps/worldport" |
| 221 | destination { x: -3 y: 0 z: 11 } | ||
| 222 | rotation: 270 | ||
| 220 | } | 223 | } |
| 221 | ports { | 224 | ports { |
| 222 | name: "TREE" | 225 | name: "TREE" |
| 226 | display_name: "Brown Hallway" | ||
| 223 | path: "Components/Warps/worldport5" | 227 | path: "Components/Warps/worldport5" |
| 228 | destination { x: -34 y: 0 z: 7 } | ||
| 229 | rotation: 270 | ||
| 224 | } | 230 | } |
| 225 | ports { | 231 | ports { |
| 226 | name: "SUNTEMPLE" | 232 | name: "SUNTEMPLE" |
| 233 | display_name: "Sun Temple Entrance" | ||
| 227 | path: "Components/Warps/worldport3" | 234 | path: "Components/Warps/worldport3" |
| 235 | destination { x: -42 y: 0 z: -2 } | ||
| 236 | rotation: 270 | ||
| 228 | required_door { name: "Sun Temple Entrance" } | 237 | required_door { name: "Sun Temple Entrance" } |
| 229 | } | 238 | } |
| diff --git a/data/maps/the_unkempt/rooms/Right Area.txtpb b/data/maps/the_unkempt/rooms/Right Area.txtpb index 03d7cea..313c276 100644 --- a/data/maps/the_unkempt/rooms/Right Area.txtpb +++ b/data/maps/the_unkempt/rooms/Right Area.txtpb | |||
| @@ -159,5 +159,4 @@ panels { | |||
| 159 | clue: "color" | 159 | clue: "color" |
| 160 | answer: "orange" | 160 | answer: "orange" |
| 161 | symbols: EXAMPLE | 161 | symbols: EXAMPLE |
| 162 | required_door { name: "Control Center Orange Door" } | ||
| 163 | } | 162 | } |
| diff --git a/data/maps/the_unyielding/doors.txtpb b/data/maps/the_unyielding/doors.txtpb index a3c3999..265442c 100644 --- a/data/maps/the_unyielding/doors.txtpb +++ b/data/maps/the_unyielding/doors.txtpb | |||
| @@ -504,3 +504,42 @@ doors { | |||
| 504 | receivers: "Panels/Miscellaneous/entry_3/teleportListener" | 504 | receivers: "Panels/Miscellaneous/entry_3/teleportListener" |
| 505 | double_letters: true | 505 | double_letters: true |
| 506 | } | 506 | } |
| 507 | doors { | ||
| 508 | name: "Blue D Room Puzzles" | ||
| 509 | type: LOCATION_ONLY | ||
| 510 | panels { room: "Central Connected Area" name: "FOX" } | ||
| 511 | panels { room: "Central Connected Area" name: "LOCKS" } | ||
| 512 | panels { room: "Central Connected Area" name: "BOX" } | ||
| 513 | panels { room: "Central Connected Area" name: "SQUAWKS" } | ||
| 514 | panels { room: "Central Connected Area" name: "HAWKS" } | ||
| 515 | panels { room: "Central Connected Area" name: "TALKS" } | ||
| 516 | location_room: "Central Connected Area" | ||
| 517 | } | ||
| 518 | doors { | ||
| 519 | name: "Color Hallway Panels" | ||
| 520 | type: LOCATION_ONLY | ||
| 521 | panels { room: "Brown Alcove" name: "BROW" } | ||
| 522 | panels { room: "Central Connected Area" name: "RANGE" } | ||
| 523 | panels { room: "Central Connected Area" name: "WHIT" } | ||
| 524 | panels { room: "Central Connected Area" name: "ALMOND" } | ||
| 525 | panels { room: "Central Connected Area" name: "DAY" } | ||
| 526 | panels { room: "Central Connected Area" name: "REAM" } | ||
| 527 | panels { room: "Central Connected Area" name: "SON (2)" } | ||
| 528 | panels { room: "Central Connected Area" name: "RAY" } | ||
| 529 | panels { room: "Central Connected Area" name: "BURROWING" } | ||
| 530 | panels { room: "Orange Alcove" name: "ON" } | ||
| 531 | panels { room: "Plaza Entrance" name: "GEE" } | ||
| 532 | panels { room: "Plaza Entrance" name: "SEA" } | ||
| 533 | panels { room: "Gray Alcove" name: "GRAVELY" } | ||
| 534 | panels { room: "Cyan Alcove" name: "CAN" } | ||
| 535 | panels { room: "Star Rooms" name: "CYANIDE" } | ||
| 536 | panels { room: "Star Rooms" name: "BACK" } | ||
| 537 | panels { room: "Black Alcove" name: "LACK" } | ||
| 538 | panels { room: "White Corners" name: "ARCH" } | ||
| 539 | panels { room: "White Corners" name: "ZERO" } | ||
| 540 | panels { room: "White Corners" name: "DAM" } | ||
| 541 | panels { room: "White Corners" name: "WHEN" } | ||
| 542 | panels { room: "Hero Room" name: "HER" } | ||
| 543 | panels { room: "Daisy Alcove" name: "CYANIDES" } | ||
| 544 | location_room: "Central Connected Area" | ||
| 545 | } | ||
| diff --git a/data/maps/the_unyielding/rooms/Bearer Entrance.txtpb b/data/maps/the_unyielding/rooms/Bearer Entrance.txtpb index 4c1440f..6ce69da 100644 --- a/data/maps/the_unyielding/rooms/Bearer Entrance.txtpb +++ b/data/maps/the_unyielding/rooms/Bearer Entrance.txtpb | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | name: "Bearer Entrance" | 1 | name: "Bearer Entrance" |
| 2 | ports { | 2 | ports { |
| 3 | name: "BEARER" | 3 | name: "BEARER" |
| 4 | display_name: "East of Yellow Worldport" | ||
| 4 | path: "Components/Warps/worldport4" | 5 | path: "Components/Warps/worldport4" |
| 6 | destination { x: 23 y: 0 z: -29 } | ||
| 7 | rotation: 90 | ||
| 5 | } | 8 | } |
| diff --git a/data/maps/the_unyielding/rooms/Digital Entrance.txtpb b/data/maps/the_unyielding/rooms/Digital Entrance.txtpb index 74665a2..853c5f0 100644 --- a/data/maps/the_unyielding/rooms/Digital Entrance.txtpb +++ b/data/maps/the_unyielding/rooms/Digital Entrance.txtpb | |||
| @@ -8,5 +8,8 @@ panels { | |||
| 8 | } | 8 | } |
| 9 | ports { | 9 | ports { |
| 10 | name: "DIGITAL" | 10 | name: "DIGITAL" |
| 11 | display_name: "South of Yellow Worldport" | ||
| 11 | path: "Components/Warps/worldport" | 12 | path: "Components/Warps/worldport" |
| 13 | destination { x: 0 y: 0 z: 0 } | ||
| 14 | rotation: 0 | ||
| 12 | } | 15 | } |
| diff --git a/data/maps/the_unyielding/rooms/Nuanced Entrance.txtpb b/data/maps/the_unyielding/rooms/Nuanced Entrance.txtpb index f011b32..454dc3f 100644 --- a/data/maps/the_unyielding/rooms/Nuanced Entrance.txtpb +++ b/data/maps/the_unyielding/rooms/Nuanced Entrance.txtpb | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | name: "Nuanced Entrance" | 1 | name: "Nuanced Entrance" |
| 2 | ports { | 2 | ports { |
| 3 | name: "NUANCED" | 3 | name: "NUANCED" |
| 4 | display_name: "West of Yellow Worldport" | ||
| 4 | path: "Components/Warps/worldport3" | 5 | path: "Components/Warps/worldport3" |
| 6 | destination { x: -23 y: 0 z: -29 } | ||
| 7 | rotation: 270 | ||
| 5 | } | 8 | } |
| diff --git a/data/maps/the_unyielding/rooms/Plaza Entrance.txtpb b/data/maps/the_unyielding/rooms/Plaza Entrance.txtpb index 0bc60a7..f866d87 100644 --- a/data/maps/the_unyielding/rooms/Plaza Entrance.txtpb +++ b/data/maps/the_unyielding/rooms/Plaza Entrance.txtpb | |||
| @@ -15,5 +15,8 @@ panels { | |||
| 15 | } | 15 | } |
| 16 | ports { | 16 | ports { |
| 17 | name: "PLAZA" | 17 | name: "PLAZA" |
| 18 | display_name: "Dark Hallway" | ||
| 18 | path: "Components/Warps/worldport5" | 19 | path: "Components/Warps/worldport5" |
| 20 | destination { x: 35 y: 0 z: 44 } | ||
| 21 | rotation: 270 | ||
| 19 | } | 22 | } |
| diff --git a/data/maps/the_wondrous/metadata.txtpb b/data/maps/the_wondrous/metadata.txtpb index 0b96cf2..1c8e04c 100644 --- a/data/maps/the_wondrous/metadata.txtpb +++ b/data/maps/the_wondrous/metadata.txtpb | |||
| @@ -1 +1,5 @@ | |||
| 1 | display_name: "The Wondrous" | 1 | display_name: "The Wondrous" |
| 2 | worldport_entrance { | ||
| 3 | room: "Entry" | ||
| 4 | name: "DAEDALUS" | ||
| 5 | } | ||
| diff --git a/data/maps/the_wondrous/rooms/Entry.txtpb b/data/maps/the_wondrous/rooms/Entry.txtpb index e15f75c..543d193 100644 --- a/data/maps/the_wondrous/rooms/Entry.txtpb +++ b/data/maps/the_wondrous/rooms/Entry.txtpb | |||
| @@ -7,5 +7,8 @@ panels { | |||
| 7 | } | 7 | } |
| 8 | ports { | 8 | ports { |
| 9 | name: "DAEDALUS" | 9 | name: "DAEDALUS" |
| 10 | display_name: "Entrance" | ||
| 10 | path: "Components/Warps/worldport" | 11 | path: "Components/Warps/worldport" |
| 12 | destination { x: 18 y: 0 z: 41 } | ||
| 13 | rotation: 180 | ||
| 11 | } | 14 | } |
| diff --git a/data/maps/the_words/rooms/Main Area.txtpb b/data/maps/the_words/rooms/Main Area.txtpb index 503408c..ae57252 100644 --- a/data/maps/the_words/rooms/Main Area.txtpb +++ b/data/maps/the_words/rooms/Main Area.txtpb | |||
| @@ -57,5 +57,8 @@ panels { | |||
| 57 | } | 57 | } |
| 58 | ports { | 58 | ports { |
| 59 | name: "ENTRY" | 59 | name: "ENTRY" |
| 60 | display_name: "Worldport" | ||
| 60 | path: "Components/Warps/worldport" | 61 | path: "Components/Warps/worldport" |
| 62 | destination { x: 0 y: 0 z: 9.5 } | ||
| 63 | rotation: 0 | ||
| 61 | } | 64 | } |
| diff --git a/data/metadata.txtpb b/data/metadata.txtpb new file mode 100644 index 0000000..24ba573 --- /dev/null +++ b/data/metadata.txtpb | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | version { | ||
| 2 | major: 8 | ||
| 3 | minor: 0 | ||
| 4 | patch: 1 | ||
| 5 | } | ||
| 6 | # Filler item. | ||
| 7 | special_names: "A Job Well Done" | ||
| 8 | # Symbol items. | ||
| 9 | special_names: "Age Symbol" | ||
| 10 | special_names: "Anagram Symbol" | ||
| 11 | special_names: "Boxes Symbol" | ||
| 12 | special_names: "Cross Symbol" | ||
| 13 | special_names: "Eval Symbol" | ||
| 14 | special_names: "Example Symbol" | ||
| 15 | special_names: "Gender Symbol" | ||
| 16 | special_names: "Job Symbol" | ||
| 17 | special_names: "Lingo Symbol" | ||
| 18 | special_names: "Null Symbol" | ||
| 19 | special_names: "Planet Symbol" | ||
| 20 | special_names: "Pyramid Symbol" | ||
| 21 | special_names: "Question Symbol" | ||
| 22 | special_names: "Sound Symbol" | ||
| 23 | special_names: "Sparkles Symbol" | ||
| 24 | special_names: "Stars Symbol" | ||
| 25 | special_names: "Sun Symbol" | ||
| 26 | special_names: "Sweet Symbol" | ||
| 27 | special_names: "Zero Symbol" | ||
| 28 | # Anti collectable traps | ||
| 29 | special_names: "Anti A" | ||
| 30 | special_names: "Anti B" | ||
| 31 | special_names: "Anti C" | ||
| 32 | special_names: "Anti D" | ||
| 33 | special_names: "Anti E" | ||
| 34 | special_names: "Anti F" | ||
| 35 | special_names: "Anti G" | ||
| 36 | special_names: "Anti H" | ||
| 37 | special_names: "Anti I" | ||
| 38 | special_names: "Anti J" | ||
| 39 | special_names: "Anti K" | ||
| 40 | special_names: "Anti L" | ||
| 41 | special_names: "Anti M" | ||
| 42 | special_names: "Anti N" | ||
| 43 | special_names: "Anti O" | ||
| 44 | special_names: "Anti P" | ||
| 45 | special_names: "Anti Q" | ||
| 46 | special_names: "Anti R" | ||
| 47 | special_names: "Anti S" | ||
| 48 | special_names: "Anti T" | ||
| 49 | special_names: "Anti U" | ||
| 50 | special_names: "Anti V" | ||
| 51 | special_names: "Anti W" | ||
| 52 | special_names: "Anti X" | ||
| 53 | special_names: "Anti Y" | ||
| 54 | special_names: "Anti Z" | ||
| 55 | # Numbers for The Fuzzy | ||
| 56 | special_names: "Numbers" | ||
| diff --git a/data/progressives.txtpb b/data/progressives.txtpb index 0eba2bf..51a0742 100644 --- a/data/progressives.txtpb +++ b/data/progressives.txtpb | |||
| @@ -9,3 +9,16 @@ progressives { | |||
| 9 | doors { map: "daedalus" name: "Cyan Rainbow Room" } | 9 | doors { map: "daedalus" name: "Cyan Rainbow Room" } |
| 10 | doors { map: "daedalus" name: "Brown Rainbow Room" } | 10 | doors { map: "daedalus" name: "Brown Rainbow Room" } |
| 11 | } | 11 | } |
| 12 | progressives { | ||
| 13 | name: "Icarus Quick Travel" | ||
| 14 | doors { map: "icarus" name: "Quick Travel 2" } | ||
| 15 | doors { map: "icarus" name: "Quick Travel 3" } | ||
| 16 | doors { map: "icarus" name: "Quick Travel 4" } | ||
| 17 | doors { map: "icarus" name: "Quick Travel 5" } | ||
| 18 | doors { map: "icarus" name: "Quick Travel 6" } | ||
| 19 | doors { map: "icarus" name: "Quick Travel 7" } | ||
| 20 | doors { map: "icarus" name: "Quick Travel 8" } | ||
| 21 | doors { map: "icarus" name: "Quick Travel 9" } | ||
| 22 | doors { map: "icarus" name: "Quick Travel 10" } | ||
| 23 | doors { map: "icarus" name: "Quick Travel 1" } | ||
| 24 | } | ||
| diff --git a/proto/data.proto b/proto/data.proto index 24b98fe..e9cc7d7 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 { |
| @@ -44,6 +47,16 @@ enum DoorGroupType { | |||
| 44 | 47 | ||
| 45 | // Groups with this type become an item if cyan door behavior is set to item. | 48 | // Groups with this type become an item if cyan door behavior is set to item. |
| 46 | CYAN_DOORS = 3; | 49 | CYAN_DOORS = 3; |
| 50 | |||
| 51 | // Groups with this type always become an item if door shuffle is on. | ||
| 52 | SHUFFLE_GROUP = 4; | ||
| 53 | } | ||
| 54 | |||
| 55 | enum MapType { | ||
| 56 | NORMAL_MAP = 0; | ||
| 57 | ICARUS = 1; | ||
| 58 | GIFT_MAP = 2; | ||
| 59 | DEMO = 3; | ||
| 47 | } | 60 | } |
| 48 | 61 | ||
| 49 | enum AxisDirection { | 62 | enum AxisDirection { |
| @@ -81,6 +94,18 @@ enum PuzzleSymbol { | |||
| 81 | QUESTION = 19; | 94 | QUESTION = 19; |
| 82 | } | 95 | } |
| 83 | 96 | ||
| 97 | message Vec3d { | ||
| 98 | optional double x = 1; | ||
| 99 | optional double y = 2; | ||
| 100 | optional double z = 3; | ||
| 101 | } | ||
| 102 | |||
| 103 | message VersionNumber { | ||
| 104 | optional uint64 major = 1; | ||
| 105 | optional uint64 minor = 2; | ||
| 106 | optional uint64 patch = 3; | ||
| 107 | } | ||
| 108 | |||
| 84 | message ProxyIdentifier { | 109 | message ProxyIdentifier { |
| 85 | optional uint64 panel = 1; | 110 | optional uint64 panel = 1; |
| 86 | optional string answer = 2; | 111 | optional string answer = 2; |
| @@ -103,6 +128,9 @@ message Connection { | |||
| 103 | } | 128 | } |
| 104 | 129 | ||
| 105 | optional bool roof_access = 7; | 130 | optional bool roof_access = 7; |
| 131 | optional bool purple_ending = 8; | ||
| 132 | optional bool cyan_ending = 9; | ||
| 133 | optional bool vanilla_only = 10; | ||
| 106 | } | 134 | } |
| 107 | 135 | ||
| 108 | message Door { | 136 | message Door { |
| @@ -119,14 +147,16 @@ message Door { | |||
| 119 | optional uint64 complete_at = 12; | 147 | optional uint64 complete_at = 12; |
| 120 | 148 | ||
| 121 | optional string control_center_color = 6; | 149 | optional string control_center_color = 6; |
| 122 | repeated string switches = 7; | ||
| 123 | repeated KeyholderAnswer keyholders = 13; | 150 | repeated KeyholderAnswer keyholders = 13; |
| 124 | repeated uint64 rooms = 14; | 151 | repeated uint64 rooms = 14; |
| 125 | repeated uint64 doors = 15; | 152 | repeated uint64 doors = 15; |
| 126 | repeated uint64 endings = 16; | 153 | optional bool white_ending = 16; |
| 127 | optional bool double_letters = 18; | 154 | optional bool double_letters = 18; |
| 155 | repeated string senders = 19; | ||
| 128 | 156 | ||
| 129 | optional DoorType type = 8; | 157 | optional DoorType type = 8; |
| 158 | optional bool latch = 20; | ||
| 159 | optional bool legacy_location = 21; | ||
| 130 | 160 | ||
| 131 | optional string location_name = 17; | 161 | optional string location_name = 17; |
| 132 | } | 162 | } |
| @@ -169,12 +199,16 @@ message PaintingData { | |||
| 169 | 199 | ||
| 170 | message Port { | 200 | message Port { |
| 171 | optional uint64 id = 1; | 201 | optional uint64 id = 1; |
| 202 | optional uint64 ap_id = 11; | ||
| 172 | optional uint64 room_id = 2; | 203 | optional uint64 room_id = 2; |
| 173 | optional string name = 3; | 204 | optional string name = 3; |
| 174 | 205 | ||
| 206 | optional string display_name = 10; | ||
| 175 | optional string path = 4; | 207 | optional string path = 4; |
| 176 | optional string orientation = 5; | 208 | optional Vec3d destination = 5; |
| 209 | optional double rotation = 8; | ||
| 177 | optional AxisDirection gravity = 7; | 210 | optional AxisDirection gravity = 7; |
| 211 | optional bool no_shuffle = 9; | ||
| 178 | 212 | ||
| 179 | optional uint64 required_door = 6; | 213 | optional uint64 required_door = 6; |
| 180 | } | 214 | } |
| @@ -240,6 +274,8 @@ message Map { | |||
| 240 | optional uint64 id = 1; | 274 | optional uint64 id = 1; |
| 241 | optional string name = 2; | 275 | optional string name = 2; |
| 242 | optional string display_name = 3; | 276 | optional string display_name = 3; |
| 277 | optional uint64 worldport_entrance = 4; | ||
| 278 | optional MapType type = 5; | ||
| 243 | } | 279 | } |
| 244 | 280 | ||
| 245 | message Progressive { | 281 | message Progressive { |
| @@ -258,6 +294,8 @@ message DoorGroup { | |||
| 258 | } | 294 | } |
| 259 | 295 | ||
| 260 | message AllObjects { | 296 | message AllObjects { |
| 297 | optional VersionNumber version = 15; | ||
| 298 | |||
| 261 | repeated Map maps = 7; | 299 | repeated Map maps = 7; |
| 262 | repeated Room rooms = 1; | 300 | repeated Room rooms = 1; |
| 263 | repeated Door doors = 2; | 301 | repeated Door doors = 2; |
| diff --git a/proto/human.proto b/proto/human.proto index 89fd076..c586599 100644 --- a/proto/human.proto +++ b/proto/human.proto | |||
| @@ -66,6 +66,21 @@ 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; | ||
| 81 | |||
| 82 | // This means that the connection only exists when doors are not shuffled. | ||
| 83 | optional bool vanilla_only = 11; | ||
| 69 | } | 84 | } |
| 70 | 85 | ||
| 71 | message HumanConnections { | 86 | message HumanConnections { |
| @@ -86,16 +101,29 @@ message HumanDoor { | |||
| 86 | optional uint64 complete_at = 9; | 101 | optional uint64 complete_at = 9; |
| 87 | 102 | ||
| 88 | optional string control_center_color = 6; | 103 | optional string control_center_color = 6; |
| 89 | repeated string switches = 7; | ||
| 90 | repeated KeyholderIdentifier keyholders = 10; | 104 | repeated KeyholderIdentifier keyholders = 10; |
| 91 | repeated RoomIdentifier rooms = 11; | 105 | repeated RoomIdentifier rooms = 11; |
| 92 | repeated DoorIdentifier doors = 12; | 106 | repeated DoorIdentifier doors = 12; |
| 93 | repeated string endings = 13; | 107 | optional bool white_ending = 13; |
| 94 | optional bool double_letters = 15; | 108 | optional bool double_letters = 15; |
| 95 | 109 | ||
| 110 | // Sender nodes to be added to the list of requirements for triggering the | ||
| 111 | // location. Only for senders that have no logic requirements. | ||
| 112 | repeated string senders = 16; | ||
| 113 | |||
| 96 | optional DoorType type = 4; | 114 | optional DoorType type = 4; |
| 97 | optional string location_room = 5; | 115 | optional string location_room = 5; |
| 98 | optional string location_name = 14; | 116 | optional string location_name = 14; |
| 117 | |||
| 118 | // Non-item doors that are latched will stay open once opened, even if the | ||
| 119 | // opening trigger is deactivated. This applies to EVENT/LOCATION_ONLY doors, | ||
| 120 | // as well as item-locked doors when not shuffling doors. | ||
| 121 | optional bool latch = 17; | ||
| 122 | |||
| 123 | // If true, the client will treat this door like a location, even though no | ||
| 124 | // location is created for it in the generator. This helps provide backwards | ||
| 125 | // compatability with older worlds. | ||
| 126 | optional bool legacy_location = 18; | ||
| 99 | } | 127 | } |
| 100 | 128 | ||
| 101 | message HumanDoors { | 129 | message HumanDoors { |
| @@ -135,9 +163,16 @@ message HumanPainting { | |||
| 135 | 163 | ||
| 136 | message HumanPort { | 164 | message HumanPort { |
| 137 | optional string name = 1; | 165 | optional string name = 1; |
| 166 | optional string display_name = 8; | ||
| 138 | optional string path = 2; | 167 | optional string path = 2; |
| 139 | 168 | ||
| 140 | optional string orientation = 3; | 169 | optional bool no_shuffle = 7; |
| 170 | |||
| 171 | // These specify how the player should be placed when a randomized entrance | ||
| 172 | // sends them to this port. "rotation" is in degrees and is counter-clockwise | ||
| 173 | // from the positive X axis. | ||
| 174 | optional Vec3d destination = 3; | ||
| 175 | optional double rotation = 6; | ||
| 141 | optional AxisDirection gravity = 5 [default = Y_MINUS]; | 176 | optional AxisDirection gravity = 5 [default = Y_MINUS]; |
| 142 | 177 | ||
| 143 | optional DoorIdentifier required_door = 4; | 178 | optional DoorIdentifier required_door = 4; |
| @@ -191,7 +226,18 @@ message HumanRoom { | |||
| 191 | 226 | ||
| 192 | message HumanMap { | 227 | message HumanMap { |
| 193 | optional string display_name = 1; | 228 | optional string display_name = 1; |
| 229 | optional MapType type = 4; | ||
| 230 | |||
| 231 | optional PortIdentifier worldport_entrance = 3; | ||
| 232 | |||
| 233 | // These two fields are used by the validator and nothing else. excluded_nodes | ||
| 234 | // are objects in the tscn that are intentionally not mentioned in the txtpb. | ||
| 235 | // custom_nodes are the reverse of that; objects that are mentioned in the | ||
| 236 | // txtpb but not present in the tscn. These are generally created dynamically | ||
| 237 | // by the game mod, but may also be used for places where the validator is | ||
| 238 | // just wrong about the contents of the map file. | ||
| 194 | repeated string excluded_nodes = 2; | 239 | repeated string excluded_nodes = 2; |
| 240 | repeated string custom_nodes = 5; | ||
| 195 | } | 241 | } |
| 196 | 242 | ||
| 197 | message HumanProgressive { | 243 | message HumanProgressive { |
| @@ -213,11 +259,17 @@ message HumanDoorGroups { | |||
| 213 | repeated HumanDoorGroup door_groups = 1; | 259 | repeated HumanDoorGroup door_groups = 1; |
| 214 | } | 260 | } |
| 215 | 261 | ||
| 262 | message HumanGlobalMetadata { | ||
| 263 | repeated string special_names = 1; | ||
| 264 | optional VersionNumber version = 2; | ||
| 265 | } | ||
| 266 | |||
| 216 | message IdMappings { | 267 | message IdMappings { |
| 217 | message RoomIds { | 268 | message RoomIds { |
| 218 | map<string, uint64> panels = 1; | 269 | map<string, uint64> panels = 1; |
| 219 | map<string, uint64> masteries = 2; | 270 | map<string, uint64> masteries = 2; |
| 220 | map<string, uint64> keyholders = 3; | 271 | map<string, uint64> keyholders = 3; |
| 272 | map<string, uint64> ports = 4; | ||
| 221 | } | 273 | } |
| 222 | 274 | ||
| 223 | message MapIds { | 275 | message MapIds { |
| diff --git a/tools/assign_ids/main.cpp b/tools/assign_ids/main.cpp index d212767..4cf7c3f 100644 --- a/tools/assign_ids/main.cpp +++ b/tools/assign_ids/main.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #include <google/protobuf/text_format.h> | 2 | #include <google/protobuf/text_format.h> |
| 3 | 3 | ||
| 4 | #include <cstdint> | 4 | #include <cstdint> |
| 5 | #include <filesystem> | ||
| 5 | #include <fstream> | 6 | #include <fstream> |
| 6 | #include <iostream> | 7 | #include <iostream> |
| 7 | #include <map> | 8 | #include <map> |
| @@ -43,6 +44,7 @@ class AssignIds { | |||
| 43 | ProcessSpecialIds(); | 44 | ProcessSpecialIds(); |
| 44 | ProcessProgressivesFile(datadir_path / "progressives.txtpb"); | 45 | ProcessProgressivesFile(datadir_path / "progressives.txtpb"); |
| 45 | ProcessDoorGroupsFile(datadir_path / "door_groups.txtpb"); | 46 | ProcessDoorGroupsFile(datadir_path / "door_groups.txtpb"); |
| 47 | ProcessGlobalMetadataFile(datadir_path / "metadata.txtpb"); | ||
| 46 | 48 | ||
| 47 | WriteIds(ids_path); | 49 | WriteIds(ids_path); |
| 48 | 50 | ||
| @@ -63,6 +65,7 @@ class AssignIds { | |||
| 63 | UpdateNextId(room.panels()); | 65 | UpdateNextId(room.panels()); |
| 64 | UpdateNextId(room.masteries()); | 66 | UpdateNextId(room.masteries()); |
| 65 | UpdateNextId(room.keyholders()); | 67 | UpdateNextId(room.keyholders()); |
| 68 | UpdateNextId(room.ports()); | ||
| 66 | } | 69 | } |
| 67 | } | 70 | } |
| 68 | 71 | ||
| @@ -109,7 +112,8 @@ class AssignIds { | |||
| 109 | 112 | ||
| 110 | void ProcessDoor(const HumanDoor& h_door, | 113 | void ProcessDoor(const HumanDoor& h_door, |
| 111 | const std::string& current_map_name) { | 114 | const std::string& current_map_name) { |
| 112 | if (h_door.type() == DoorType::EVENT) { | 115 | if (h_door.type() == DoorType::EVENT && !h_door.latch() && |
| 116 | !h_door.legacy_location()) { | ||
| 113 | return; | 117 | return; |
| 114 | } | 118 | } |
| 115 | 119 | ||
| @@ -243,6 +247,37 @@ class AssignIds { | |||
| 243 | .at(h_keyholder.name()); | 247 | .at(h_keyholder.name()); |
| 244 | } | 248 | } |
| 245 | } | 249 | } |
| 250 | |||
| 251 | for (const HumanPort& h_port : h_room.ports()) { | ||
| 252 | if (h_port.no_shuffle()) { | ||
| 253 | continue; | ||
| 254 | } | ||
| 255 | |||
| 256 | auto& maps = *output_.mutable_maps(); | ||
| 257 | auto& rooms = *maps[current_map_name].mutable_rooms(); | ||
| 258 | auto& ports = *rooms[h_room.name()].mutable_ports(); | ||
| 259 | |||
| 260 | if (!id_mappings_.maps().contains(current_map_name) || | ||
| 261 | !id_mappings_.maps() | ||
| 262 | .at(current_map_name) | ||
| 263 | .rooms() | ||
| 264 | .contains(h_room.name()) || | ||
| 265 | !id_mappings_.maps() | ||
| 266 | .at(current_map_name) | ||
| 267 | .rooms() | ||
| 268 | .at(h_room.name()) | ||
| 269 | .ports() | ||
| 270 | .contains(h_port.name())) { | ||
| 271 | ports[h_port.name()] = next_id_++; | ||
| 272 | } else { | ||
| 273 | ports[h_port.name()] = id_mappings_.maps() | ||
| 274 | .at(current_map_name) | ||
| 275 | .rooms() | ||
| 276 | .at(h_room.name()) | ||
| 277 | .ports() | ||
| 278 | .at(h_port.name()); | ||
| 279 | } | ||
| 280 | } | ||
| 246 | } | 281 | } |
| 247 | 282 | ||
| 248 | void ProcessSpecialIds() { | 283 | void ProcessSpecialIds() { |
| @@ -287,6 +322,23 @@ class AssignIds { | |||
| 287 | } | 322 | } |
| 288 | } | 323 | } |
| 289 | 324 | ||
| 325 | void ProcessGlobalMetadataFile(std::filesystem::path path) { | ||
| 326 | if (!std::filesystem::exists(path)) { | ||
| 327 | return; | ||
| 328 | } | ||
| 329 | |||
| 330 | auto h_metadata = ReadMessageFromFile<HumanGlobalMetadata>(path.string()); | ||
| 331 | auto& specials = *output_.mutable_special(); | ||
| 332 | |||
| 333 | for (const std::string& h_special : h_metadata.special_names()) { | ||
| 334 | if (!id_mappings_.special().contains(h_special)) { | ||
| 335 | specials[h_special] = next_id_++; | ||
| 336 | } else { | ||
| 337 | specials[h_special] = id_mappings_.special().at(h_special); | ||
| 338 | } | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 290 | private: | 342 | private: |
| 291 | void UpdateNextId(const google::protobuf::Map<std::string, uint64_t>& ids) { | 343 | void UpdateNextId(const google::protobuf::Map<std::string, uint64_t>& ids) { |
| 292 | for (const auto& [_, id] : ids) { | 344 | for (const auto& [_, id] : ids) { |
| diff --git a/tools/datapacker/main.cpp b/tools/datapacker/main.cpp index c72462d..8109bf5 100644 --- a/tools/datapacker/main.cpp +++ b/tools/datapacker/main.cpp | |||
| @@ -45,6 +45,7 @@ class DataPacker { | |||
| 45 | ProcessMaps(datadir_path); | 45 | ProcessMaps(datadir_path); |
| 46 | ProcessProgressivesFile(datadir_path / "progressives.txtpb"); | 46 | ProcessProgressivesFile(datadir_path / "progressives.txtpb"); |
| 47 | ProcessDoorGroupsFile(datadir_path / "door_groups.txtpb"); | 47 | ProcessDoorGroupsFile(datadir_path / "door_groups.txtpb"); |
| 48 | ProcessGlobalMetadataFile(datadir_path / "metadata.txtpb"); | ||
| 48 | ProcessIdsFile(datadir_path / "ids.yaml"); | 49 | ProcessIdsFile(datadir_path / "ids.yaml"); |
| 49 | 50 | ||
| 50 | { | 51 | { |
| @@ -87,9 +88,17 @@ class DataPacker { | |||
| 87 | uint64_t map_id = container_.FindOrAddMap(map_name); | 88 | uint64_t map_id = container_.FindOrAddMap(map_name); |
| 88 | Map& map = *container_.all_objects().mutable_maps(map_id); | 89 | Map& map = *container_.all_objects().mutable_maps(map_id); |
| 89 | 90 | ||
| 91 | map.set_type(metadata.type()); | ||
| 92 | |||
| 90 | if (metadata.has_display_name()) { | 93 | if (metadata.has_display_name()) { |
| 91 | map.set_display_name(metadata.display_name()); | 94 | map.set_display_name(metadata.display_name()); |
| 92 | } | 95 | } |
| 96 | |||
| 97 | if (metadata.has_worldport_entrance()) { | ||
| 98 | map.set_worldport_entrance(container_.FindOrAddPort( | ||
| 99 | map_name, metadata.worldport_entrance().room(), | ||
| 100 | metadata.worldport_entrance().name(), std::nullopt, std::nullopt)); | ||
| 101 | } | ||
| 93 | } | 102 | } |
| 94 | 103 | ||
| 95 | void ProcessRooms(std::filesystem::path path, | 104 | void ProcessRooms(std::filesystem::path path, |
| @@ -238,7 +247,14 @@ class DataPacker { | |||
| 238 | Port& port = *container_.all_objects().mutable_ports(port_id); | 247 | Port& port = *container_.all_objects().mutable_ports(port_id); |
| 239 | 248 | ||
| 240 | port.set_path(h_port.path()); | 249 | port.set_path(h_port.path()); |
| 241 | port.set_orientation(h_port.orientation()); | 250 | port.set_display_name(h_port.display_name()); |
| 251 | |||
| 252 | if (h_port.no_shuffle()) { | ||
| 253 | port.set_no_shuffle(h_port.no_shuffle()); | ||
| 254 | } else { | ||
| 255 | *port.mutable_destination() = h_port.destination(); | ||
| 256 | port.set_rotation(h_port.rotation()); | ||
| 257 | } | ||
| 242 | 258 | ||
| 243 | // Setting this explicitly because the Godot protobuf doesn't support | 259 | // Setting this explicitly because the Godot protobuf doesn't support |
| 244 | // custom defaults. | 260 | // custom defaults. |
| @@ -345,8 +361,8 @@ class DataPacker { | |||
| 345 | h_door.receivers().begin(), h_door.receivers().end(), | 361 | h_door.receivers().begin(), h_door.receivers().end(), |
| 346 | google::protobuf::RepeatedFieldBackInserter(door.mutable_receivers())); | 362 | google::protobuf::RepeatedFieldBackInserter(door.mutable_receivers())); |
| 347 | std::copy( | 363 | std::copy( |
| 348 | h_door.switches().begin(), h_door.switches().end(), | 364 | h_door.senders().begin(), h_door.senders().end(), |
| 349 | google::protobuf::RepeatedFieldBackInserter(door.mutable_switches())); | 365 | google::protobuf::RepeatedFieldBackInserter(door.mutable_senders())); |
| 350 | 366 | ||
| 351 | for (const PaintingIdentifier& pi : h_door.move_paintings()) { | 367 | for (const PaintingIdentifier& pi : h_door.move_paintings()) { |
| 352 | std::optional<std::string> map_name = | 368 | std::optional<std::string> map_name = |
| @@ -395,8 +411,8 @@ class DataPacker { | |||
| 395 | container_.FindOrAddDoor(map_name, di.name(), current_map_name)); | 411 | container_.FindOrAddDoor(map_name, di.name(), current_map_name)); |
| 396 | } | 412 | } |
| 397 | 413 | ||
| 398 | for (const std::string& ending_name : h_door.endings()) { | 414 | if (h_door.has_white_ending()) { |
| 399 | door.add_endings(container_.FindOrAddEnding(ending_name)); | 415 | door.set_white_ending(h_door.white_ending()); |
| 400 | } | 416 | } |
| 401 | 417 | ||
| 402 | if (h_door.has_control_center_color()) { | 418 | if (h_door.has_control_center_color()) { |
| @@ -416,6 +432,14 @@ class DataPacker { | |||
| 416 | if (h_door.has_double_letters()) { | 432 | if (h_door.has_double_letters()) { |
| 417 | door.set_double_letters(h_door.double_letters()); | 433 | door.set_double_letters(h_door.double_letters()); |
| 418 | } | 434 | } |
| 435 | |||
| 436 | if (h_door.has_latch()) { | ||
| 437 | door.set_latch(h_door.latch()); | ||
| 438 | } | ||
| 439 | |||
| 440 | if (h_door.has_legacy_location()) { | ||
| 441 | door.set_legacy_location(h_door.legacy_location()); | ||
| 442 | } | ||
| 419 | } | 443 | } |
| 420 | 444 | ||
| 421 | void ProcessConnectionsFile(std::filesystem::path path, | 445 | void ProcessConnectionsFile(std::filesystem::path path, |
| @@ -472,6 +496,21 @@ class DataPacker { | |||
| 472 | r_connection.set_roof_access(human_connection.roof_access()); | 496 | r_connection.set_roof_access(human_connection.roof_access()); |
| 473 | } | 497 | } |
| 474 | 498 | ||
| 499 | if (human_connection.has_purple_ending()) { | ||
| 500 | f_connection.set_purple_ending(human_connection.purple_ending()); | ||
| 501 | r_connection.set_purple_ending(human_connection.purple_ending()); | ||
| 502 | } | ||
| 503 | |||
| 504 | if (human_connection.has_cyan_ending()) { | ||
| 505 | f_connection.set_cyan_ending(human_connection.cyan_ending()); | ||
| 506 | r_connection.set_cyan_ending(human_connection.cyan_ending()); | ||
| 507 | } | ||
| 508 | |||
| 509 | if (human_connection.has_vanilla_only()) { | ||
| 510 | f_connection.set_vanilla_only(human_connection.vanilla_only()); | ||
| 511 | r_connection.set_vanilla_only(human_connection.vanilla_only()); | ||
| 512 | } | ||
| 513 | |||
| 475 | container_.AddConnection(f_connection); | 514 | container_.AddConnection(f_connection); |
| 476 | if (!human_connection.oneway()) { | 515 | if (!human_connection.oneway()) { |
| 477 | container_.AddConnection(r_connection); | 516 | container_.AddConnection(r_connection); |
| @@ -603,6 +642,15 @@ class DataPacker { | |||
| 603 | } | 642 | } |
| 604 | } | 643 | } |
| 605 | 644 | ||
| 645 | void ProcessGlobalMetadataFile(std::filesystem::path path) { | ||
| 646 | if (!std::filesystem::exists(path)) { | ||
| 647 | return; | ||
| 648 | } | ||
| 649 | |||
| 650 | auto h_metadata = ReadMessageFromFile<HumanGlobalMetadata>(path.string()); | ||
| 651 | *container_.all_objects().mutable_version() = h_metadata.version(); | ||
| 652 | } | ||
| 653 | |||
| 606 | void ProcessIdsFile(std::filesystem::path path) { | 654 | void ProcessIdsFile(std::filesystem::path path) { |
| 607 | auto ids = ReadIdsFromYaml(path.string()); | 655 | auto ids = ReadIdsFromYaml(path.string()); |
| 608 | 656 | ||
| @@ -635,6 +683,12 @@ class DataPacker { | |||
| 635 | .mutable_keyholders(keyholder_id) | 683 | .mutable_keyholders(keyholder_id) |
| 636 | ->set_ap_id(ap_id); | 684 | ->set_ap_id(ap_id); |
| 637 | } | 685 | } |
| 686 | |||
| 687 | for (const auto& [port_name, ap_id] : room.ports()) { | ||
| 688 | uint64_t port_id = container_.FindOrAddPort( | ||
| 689 | map_name, room_name, port_name, std::nullopt, std::nullopt); | ||
| 690 | container_.all_objects().mutable_ports(port_id)->set_ap_id(ap_id); | ||
| 691 | } | ||
| 638 | } | 692 | } |
| 639 | } | 693 | } |
| 640 | 694 | ||
| diff --git a/tools/util/godot_scene.cpp b/tools/util/godot_scene.cpp index 1e77c9e..f788d21 100644 --- a/tools/util/godot_scene.cpp +++ b/tools/util/godot_scene.cpp | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | #include "godot_scene.h" | 1 | #include "godot_scene.h" |
| 2 | 2 | ||
| 3 | #include <absl/strings/str_split.h> | ||
| 4 | #include <absl/strings/string_view.h> | ||
| 5 | |||
| 6 | #include <fstream> | 3 | #include <fstream> |
| 7 | #include <sstream> | 4 | #include <sstream> |
| 5 | #include <string_view> | ||
| 8 | #include <variant> | 6 | #include <variant> |
| 9 | 7 | ||
| 10 | namespace com::fourisland::lingo2_archipelago { | 8 | namespace com::fourisland::lingo2_archipelago { |
| @@ -23,7 +21,7 @@ struct Heading { | |||
| 23 | GodotInstanceType instance_type; | 21 | GodotInstanceType instance_type; |
| 24 | }; | 22 | }; |
| 25 | 23 | ||
| 26 | Heading ParseTscnHeading(absl::string_view line) { | 24 | Heading ParseTscnHeading(std::string_view line) { |
| 27 | std::string original_line(line); | 25 | std::string original_line(line); |
| 28 | Heading heading; | 26 | Heading heading; |
| 29 | 27 | ||
| diff --git a/tools/util/ids_yaml_format.cpp b/tools/util/ids_yaml_format.cpp index 71bfd63..5b9113b 100644 --- a/tools/util/ids_yaml_format.cpp +++ b/tools/util/ids_yaml_format.cpp | |||
| @@ -64,6 +64,13 @@ IdMappings ReadIdsFromYaml(const std::string& filename) { | |||
| 64 | keyholder_it.second.as<uint64_t>(); | 64 | keyholder_it.second.as<uint64_t>(); |
| 65 | } | 65 | } |
| 66 | } | 66 | } |
| 67 | |||
| 68 | if (room_it.second["ports"]) { | ||
| 69 | for (const auto& port_it : room_it.second["ports"]) { | ||
| 70 | (*room_ids.mutable_ports())[port_it.first.as<std::string>()] = | ||
| 71 | port_it.second.as<uint64_t>(); | ||
| 72 | } | ||
| 73 | } | ||
| 67 | } | 74 | } |
| 68 | } | 75 | } |
| 69 | 76 | ||
| @@ -146,6 +153,12 @@ void WriteIdsAsYaml(const IdMappings& ids, const std::string& filename) { | |||
| 146 | keyholder_id; | 153 | keyholder_id; |
| 147 | }); | 154 | }); |
| 148 | 155 | ||
| 156 | OperateOnSortedMap( | ||
| 157 | room_ids.ports(), | ||
| 158 | [&room_node](const std::string& port_name, uint64_t port_id) { | ||
| 159 | room_node["ports"][port_name] = port_id; | ||
| 160 | }); | ||
| 161 | |||
| 149 | map_node["rooms"][room_name] = std::move(room_node); | 162 | map_node["rooms"][room_name] = std::move(room_node); |
| 150 | }); | 163 | }); |
| 151 | 164 | ||
| diff --git a/tools/validator/human_processor.cpp b/tools/validator/human_processor.cpp index 561225e..ffa9765 100644 --- a/tools/validator/human_processor.cpp +++ b/tools/validator/human_processor.cpp | |||
| @@ -77,6 +77,21 @@ class HumanProcessor { | |||
| 77 | for (const std::string& path : metadata.excluded_nodes()) { | 77 | for (const std::string& path : metadata.excluded_nodes()) { |
| 78 | map_info.game_nodes[path].uses++; | 78 | map_info.game_nodes[path].uses++; |
| 79 | } | 79 | } |
| 80 | |||
| 81 | for (const std::string& path : metadata.custom_nodes()) { | ||
| 82 | map_info.game_nodes[path].defined = true; | ||
| 83 | } | ||
| 84 | |||
| 85 | if (metadata.has_worldport_entrance()) { | ||
| 86 | auto port_identifier = GetCompletePortIdentifier( | ||
| 87 | metadata.worldport_entrance(), current_map_name, std::nullopt); | ||
| 88 | if (port_identifier) { | ||
| 89 | PortInfo& port_info = info_.ports[*port_identifier]; | ||
| 90 | port_info.map_worldport_entrances.push_back(current_map_name); | ||
| 91 | } else { | ||
| 92 | map_info.malformed_worldport_entrance = metadata.worldport_entrance(); | ||
| 93 | } | ||
| 94 | } | ||
| 80 | } | 95 | } |
| 81 | 96 | ||
| 82 | void ProcessRooms(std::filesystem::path path, | 97 | void ProcessRooms(std::filesystem::path path, |
| @@ -358,11 +373,6 @@ class HumanProcessor { | |||
| 358 | DoorInfo& other_door_info = info_.doors[complete_door_identifier]; | 373 | DoorInfo& other_door_info = info_.doors[complete_door_identifier]; |
| 359 | other_door_info.doors_referenced_by.push_back(door_identifier); | 374 | other_door_info.doors_referenced_by.push_back(door_identifier); |
| 360 | } | 375 | } |
| 361 | |||
| 362 | for (const std::string& ei : h_door.endings()) { | ||
| 363 | EndingInfo& ending_info = info_.endings[ei]; | ||
| 364 | ending_info.doors_referenced_by.push_back(door_identifier); | ||
| 365 | } | ||
| 366 | } | 376 | } |
| 367 | 377 | ||
| 368 | void ProcessConnectionsFile(std::filesystem::path path, | 378 | void ProcessConnectionsFile(std::filesystem::path path, |
| @@ -394,7 +404,9 @@ class HumanProcessor { | |||
| 394 | } | 404 | } |
| 395 | } else if (human_connection.has_from()) { | 405 | } else if (human_connection.has_from()) { |
| 396 | ProcessSingleConnection(human_connection, human_connection.from(), | 406 | ProcessSingleConnection(human_connection, human_connection.from(), |
| 397 | current_map_name); | 407 | current_map_name, |
| 408 | /*is_target=*/!human_connection.oneway() && | ||
| 409 | !human_connection.bypass_target_door()); | ||
| 398 | } | 410 | } |
| 399 | 411 | ||
| 400 | if (human_connection.has_to_room()) { | 412 | if (human_connection.has_to_room()) { |
| @@ -410,8 +422,9 @@ class HumanProcessor { | |||
| 410 | std::cout << "A global connection used to_room." << std::endl; | 422 | std::cout << "A global connection used to_room." << std::endl; |
| 411 | } | 423 | } |
| 412 | } else if (human_connection.has_to()) { | 424 | } else if (human_connection.has_to()) { |
| 413 | ProcessSingleConnection(human_connection, human_connection.to(), | 425 | ProcessSingleConnection( |
| 414 | current_map_name); | 426 | human_connection, human_connection.to(), current_map_name, |
| 427 | /*is_target=*/!human_connection.bypass_target_door()); | ||
| 415 | } | 428 | } |
| 416 | 429 | ||
| 417 | if (human_connection.has_door()) { | 430 | if (human_connection.has_door()) { |
| @@ -432,7 +445,7 @@ class HumanProcessor { | |||
| 432 | void ProcessSingleConnection( | 445 | void ProcessSingleConnection( |
| 433 | const HumanConnection& human_connection, | 446 | const HumanConnection& human_connection, |
| 434 | const HumanConnection::Endpoint& endpoint, | 447 | const HumanConnection::Endpoint& endpoint, |
| 435 | const std::optional<std::string>& current_map_name) { | 448 | const std::optional<std::string>& current_map_name, bool is_target) { |
| 436 | if (endpoint.has_room()) { | 449 | if (endpoint.has_room()) { |
| 437 | auto room_identifier = | 450 | auto room_identifier = |
| 438 | GetCompleteRoomIdentifier(endpoint.room(), current_map_name); | 451 | GetCompleteRoomIdentifier(endpoint.room(), current_map_name); |
| @@ -451,6 +464,11 @@ class HumanProcessor { | |||
| 451 | if (painting_identifier) { | 464 | if (painting_identifier) { |
| 452 | PaintingInfo& painting_info = info_.paintings[*painting_identifier]; | 465 | PaintingInfo& painting_info = info_.paintings[*painting_identifier]; |
| 453 | painting_info.connections_referenced_by.push_back(human_connection); | 466 | painting_info.connections_referenced_by.push_back(human_connection); |
| 467 | |||
| 468 | if (is_target) { | ||
| 469 | painting_info.target_connections_referenced_by.push_back( | ||
| 470 | human_connection); | ||
| 471 | } | ||
| 454 | } else { | 472 | } else { |
| 455 | // Not sure where else to store this right now. | 473 | // Not sure where else to store this right now. |
| 456 | std::cout | 474 | std::cout |
| @@ -463,6 +481,11 @@ class HumanProcessor { | |||
| 463 | if (port_identifier) { | 481 | if (port_identifier) { |
| 464 | PortInfo& port_info = info_.ports[*port_identifier]; | 482 | PortInfo& port_info = info_.ports[*port_identifier]; |
| 465 | port_info.connections_referenced_by.push_back(human_connection); | 483 | port_info.connections_referenced_by.push_back(human_connection); |
| 484 | |||
| 485 | if (is_target) { | ||
| 486 | port_info.target_connections_referenced_by.push_back( | ||
| 487 | human_connection); | ||
| 488 | } | ||
| 466 | } else { | 489 | } else { |
| 467 | // Not sure where else to store this right now. | 490 | // Not sure where else to store this right now. |
| 468 | std::cout | 491 | std::cout |
| @@ -480,6 +503,11 @@ class HumanProcessor { | |||
| 480 | panel_info.proxies[endpoint.panel().answer()] | 503 | panel_info.proxies[endpoint.panel().answer()] |
| 481 | .connections_referenced_by.push_back(human_connection); | 504 | .connections_referenced_by.push_back(human_connection); |
| 482 | } | 505 | } |
| 506 | |||
| 507 | if (is_target) { | ||
| 508 | panel_info.target_connections_referenced_by.push_back( | ||
| 509 | human_connection); | ||
| 510 | } | ||
| 483 | } | 511 | } |
| 484 | } | 512 | } |
| 485 | } | 513 | } |
| @@ -542,12 +570,14 @@ class HumanProcessor { | |||
| 542 | auto ids = ReadIdsFromYaml(path.string()); | 570 | auto ids = ReadIdsFromYaml(path.string()); |
| 543 | 571 | ||
| 544 | DoorIdentifier di; | 572 | DoorIdentifier di; |
| 545 | PanelIdentifier pi; | 573 | PanelIdentifier pai; |
| 574 | PortIdentifier poi; | ||
| 546 | KeyholderIdentifier ki; | 575 | KeyholderIdentifier ki; |
| 547 | 576 | ||
| 548 | for (const auto& [map_name, map] : ids.maps()) { | 577 | for (const auto& [map_name, map] : ids.maps()) { |
| 549 | di.set_map(map_name); | 578 | di.set_map(map_name); |
| 550 | pi.set_map(map_name); | 579 | pai.set_map(map_name); |
| 580 | poi.set_map(map_name); | ||
| 551 | ki.set_map(map_name); | 581 | ki.set_map(map_name); |
| 552 | 582 | ||
| 553 | for (const auto& [door_name, ap_id] : map.doors()) { | 583 | for (const auto& [door_name, ap_id] : map.doors()) { |
| @@ -558,13 +588,14 @@ class HumanProcessor { | |||
| 558 | } | 588 | } |
| 559 | 589 | ||
| 560 | for (const auto& [room_name, room] : map.rooms()) { | 590 | for (const auto& [room_name, room] : map.rooms()) { |
| 561 | pi.set_room(room_name); | 591 | pai.set_room(room_name); |
| 592 | poi.set_room(room_name); | ||
| 562 | ki.set_room(room_name); | 593 | ki.set_room(room_name); |
| 563 | 594 | ||
| 564 | for (const auto& [panel_name, ap_id] : room.panels()) { | 595 | for (const auto& [panel_name, ap_id] : room.panels()) { |
| 565 | pi.set_name(panel_name); | 596 | pai.set_name(panel_name); |
| 566 | 597 | ||
| 567 | PanelInfo& panel_info = info_.panels[pi]; | 598 | PanelInfo& panel_info = info_.panels[pai]; |
| 568 | panel_info.has_id = true; | 599 | panel_info.has_id = true; |
| 569 | } | 600 | } |
| 570 | 601 | ||
| @@ -578,6 +609,13 @@ class HumanProcessor { | |||
| 578 | KeyholderInfo& keyholder_info = info_.keyholders[ki]; | 609 | KeyholderInfo& keyholder_info = info_.keyholders[ki]; |
| 579 | keyholder_info.has_id = true; | 610 | keyholder_info.has_id = true; |
| 580 | } | 611 | } |
| 612 | |||
| 613 | for (const auto& [port_name, ap_id] : room.ports()) { | ||
| 614 | poi.set_name(port_name); | ||
| 615 | |||
| 616 | PortInfo& port_info = info_.ports[poi]; | ||
| 617 | port_info.has_id = true; | ||
| 618 | } | ||
| 581 | } | 619 | } |
| 582 | } | 620 | } |
| 583 | 621 | ||
| diff --git a/tools/validator/structs.h b/tools/validator/structs.h index 17ed33a..62974a8 100644 --- a/tools/validator/structs.h +++ b/tools/validator/structs.h | |||
| @@ -27,6 +27,8 @@ struct GameNodeInfo { | |||
| 27 | 27 | ||
| 28 | struct MapInfo { | 28 | struct MapInfo { |
| 29 | std::map<std::string, GameNodeInfo> game_nodes; | 29 | std::map<std::string, GameNodeInfo> game_nodes; |
| 30 | |||
| 31 | std::optional<PortIdentifier> malformed_worldport_entrance; | ||
| 30 | }; | 32 | }; |
| 31 | 33 | ||
| 32 | struct RoomInfo { | 34 | struct RoomInfo { |
| @@ -54,14 +56,18 @@ struct DoorInfo { | |||
| 54 | 56 | ||
| 55 | struct PortInfo { | 57 | struct PortInfo { |
| 56 | std::vector<HumanPort> definitions; | 58 | std::vector<HumanPort> definitions; |
| 59 | bool has_id = false; | ||
| 57 | 60 | ||
| 58 | std::vector<HumanConnection> connections_referenced_by; | 61 | std::vector<HumanConnection> connections_referenced_by; |
| 62 | std::vector<HumanConnection> target_connections_referenced_by; | ||
| 63 | std::vector<std::string> map_worldport_entrances; | ||
| 59 | }; | 64 | }; |
| 60 | 65 | ||
| 61 | struct PaintingInfo { | 66 | struct PaintingInfo { |
| 62 | std::vector<HumanPainting> definitions; | 67 | std::vector<HumanPainting> definitions; |
| 63 | 68 | ||
| 64 | std::vector<HumanConnection> connections_referenced_by; | 69 | std::vector<HumanConnection> connections_referenced_by; |
| 70 | std::vector<HumanConnection> target_connections_referenced_by; | ||
| 65 | std::vector<DoorIdentifier> doors_referenced_by; | 71 | std::vector<DoorIdentifier> doors_referenced_by; |
| 66 | }; | 72 | }; |
| 67 | 73 | ||
| @@ -79,6 +85,7 @@ struct PanelInfo { | |||
| 79 | std::string map_area_name; | 85 | std::string map_area_name; |
| 80 | 86 | ||
| 81 | std::vector<HumanConnection> connections_referenced_by; | 87 | std::vector<HumanConnection> connections_referenced_by; |
| 88 | std::vector<HumanConnection> target_connections_referenced_by; | ||
| 82 | std::vector<DoorIdentifier> doors_referenced_by; | 89 | std::vector<DoorIdentifier> doors_referenced_by; |
| 83 | 90 | ||
| 84 | std::map<std::string, ProxyInfo> proxies; | 91 | std::map<std::string, ProxyInfo> proxies; |
| @@ -101,8 +108,6 @@ struct LetterInfo { | |||
| 101 | struct EndingInfo { | 108 | struct EndingInfo { |
| 102 | std::vector<RoomIdentifier> defined_in; | 109 | std::vector<RoomIdentifier> defined_in; |
| 103 | bool has_id = false; | 110 | bool has_id = false; |
| 104 | |||
| 105 | std::vector<DoorIdentifier> doors_referenced_by; | ||
| 106 | }; | 111 | }; |
| 107 | 112 | ||
| 108 | struct PanelNameInfo { | 113 | struct PanelNameInfo { |
| diff --git a/tools/validator/validator.cpp b/tools/validator/validator.cpp index 4149caa..fe36be7 100644 --- a/tools/validator/validator.cpp +++ b/tools/validator/validator.cpp | |||
| @@ -69,6 +69,11 @@ class Validator { | |||
| 69 | << " is not defined in the game file." << std::endl; | 69 | << " is not defined in the game file." << std::endl; |
| 70 | } | 70 | } |
| 71 | } | 71 | } |
| 72 | |||
| 73 | if (map_info.malformed_worldport_entrance) { | ||
| 74 | std::cout << "The worldport entrance for map " << map_name | ||
| 75 | << " is malformed." << std::endl; | ||
| 76 | } | ||
| 72 | } | 77 | } |
| 73 | 78 | ||
| 74 | void ValidateRoom(const RoomIdentifier& room_identifier, | 79 | void ValidateRoom(const RoomIdentifier& room_identifier, |
| @@ -106,7 +111,8 @@ class Validator { | |||
| 106 | return false; | 111 | return false; |
| 107 | } | 112 | } |
| 108 | 113 | ||
| 109 | if (h_door.keyholders_size() > 0 || h_door.endings_size() > 0) { | 114 | if (h_door.keyholders_size() > 0 || h_door.white_ending() || |
| 115 | h_door.complete_at() > 0) { | ||
| 110 | return true; | 116 | return true; |
| 111 | } | 117 | } |
| 112 | 118 | ||
| @@ -219,16 +225,23 @@ class Validator { | |||
| 219 | << " needs an explicit location name." << std::endl; | 225 | << " needs an explicit location name." << std::endl; |
| 220 | } | 226 | } |
| 221 | 227 | ||
| 222 | if (h_door.double_letters() && | 228 | if (h_door.type() == DoorType::STANDARD || |
| 223 | (h_door.type() == DoorType::STANDARD || | 229 | h_door.type() == DoorType::LOCATION_ONLY || |
| 224 | h_door.type() == DoorType::LOCATION_ONLY || | 230 | h_door.type() == DoorType::GRAVESTONE || h_door.legacy_location()) { |
| 225 | h_door.type() == DoorType::GRAVESTONE)) { | 231 | if (h_door.double_letters()) { |
| 226 | std::cout << "Door " << door_identifier.ShortDebugString() | 232 | std::cout << "Door " << door_identifier.ShortDebugString() |
| 227 | << " is a location that depends on double_letters." | 233 | << " is a location that depends on double_letters." |
| 228 | << std::endl; | 234 | << std::endl; |
| 235 | } | ||
| 236 | |||
| 237 | if (!h_door.has_location_room()) { | ||
| 238 | std::cout << "Door " << door_identifier.ShortDebugString() | ||
| 239 | << " is missing a location_room." << std::endl; | ||
| 240 | } | ||
| 229 | } | 241 | } |
| 230 | 242 | ||
| 231 | bool needs_id = (h_door.type() != DoorType::EVENT); | 243 | bool needs_id = (h_door.type() != DoorType::EVENT || h_door.latch() || |
| 244 | h_door.legacy_location()); | ||
| 232 | if (door_info.has_id != needs_id) { | 245 | if (door_info.has_id != needs_id) { |
| 233 | if (needs_id) { | 246 | if (needs_id) { |
| 234 | std::cout << "Door " << door_identifier.ShortDebugString() | 247 | std::cout << "Door " << door_identifier.ShortDebugString() |
| @@ -252,10 +265,57 @@ class Validator { | |||
| 252 | std::cout << " CONNECTION " << connection.ShortDebugString() | 265 | std::cout << " CONNECTION " << connection.ShortDebugString() |
| 253 | << std::endl; | 266 | << std::endl; |
| 254 | } | 267 | } |
| 268 | |||
| 269 | for (const std::string& map_name : port_info.map_worldport_entrances) { | ||
| 270 | std::cout << " MAP (worldport_entrance) " << map_name << std::endl; | ||
| 271 | } | ||
| 272 | |||
| 273 | if (port_info.has_id) { | ||
| 274 | std::cout << " An AP ID is present." << std::endl; | ||
| 275 | } | ||
| 255 | } else if (port_info.definitions.size() > 1) { | 276 | } else if (port_info.definitions.size() > 1) { |
| 256 | std::cout << "Port " << port_identifier.ShortDebugString() | 277 | std::cout << "Port " << port_identifier.ShortDebugString() |
| 257 | << " was defined multiple times." << std::endl; | 278 | << " was defined multiple times." << std::endl; |
| 258 | } | 279 | } |
| 280 | |||
| 281 | if (!port_info.target_connections_referenced_by.empty()) { | ||
| 282 | for (const HumanPort& port : port_info.definitions) { | ||
| 283 | if (port.has_required_door()) { | ||
| 284 | std::cout << "Port " << port_identifier.ShortDebugString() | ||
| 285 | << " has a required door but is the target of a connection:" | ||
| 286 | << std::endl; | ||
| 287 | |||
| 288 | for (const HumanConnection& connection : | ||
| 289 | port_info.target_connections_referenced_by) { | ||
| 290 | std::cout << " CONNECTION " << connection.ShortDebugString() | ||
| 291 | << std::endl; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | for (const HumanPort& port : port_info.definitions) { | ||
| 298 | if (!port.no_shuffle()) { | ||
| 299 | if (!port.has_destination()) { | ||
| 300 | std::cout << "Port " << port_identifier.ShortDebugString() | ||
| 301 | << " is shuffleable and missing a destination." | ||
| 302 | << std::endl; | ||
| 303 | } | ||
| 304 | if (!port.has_rotation()) { | ||
| 305 | std::cout << "Port " << port_identifier.ShortDebugString() | ||
| 306 | << " is shuffleable and missing a rotation." << std::endl; | ||
| 307 | } | ||
| 308 | if (!port_info.has_id) { | ||
| 309 | std::cout << "Port " << port_identifier.ShortDebugString() | ||
| 310 | << " is missing an AP ID." << std::endl; | ||
| 311 | } | ||
| 312 | } else { | ||
| 313 | if (port_info.has_id) { | ||
| 314 | std::cout << "Port " << port_identifier.ShortDebugString() | ||
| 315 | << " should not have an AP ID." << std::endl; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 259 | } | 319 | } |
| 260 | 320 | ||
| 261 | void ValidatePainting(const PaintingIdentifier& painting_identifier, | 321 | void ValidatePainting(const PaintingIdentifier& painting_identifier, |
| @@ -279,6 +339,22 @@ class Validator { | |||
| 279 | std::cout << "Painting " << painting_identifier.ShortDebugString() | 339 | std::cout << "Painting " << painting_identifier.ShortDebugString() |
| 280 | << " was defined multiple times." << std::endl; | 340 | << " was defined multiple times." << std::endl; |
| 281 | } | 341 | } |
| 342 | |||
| 343 | if (!painting_info.target_connections_referenced_by.empty()) { | ||
| 344 | for (const HumanPainting& painting : painting_info.definitions) { | ||
| 345 | if (painting.has_required_door()) { | ||
| 346 | std::cout << "Painting " << painting_identifier.ShortDebugString() | ||
| 347 | << " has a required door but is the target of a connection:" | ||
| 348 | << std::endl; | ||
| 349 | |||
| 350 | for (const HumanConnection& connection : | ||
| 351 | painting_info.target_connections_referenced_by) { | ||
| 352 | std::cout << " CONNECTION " << connection.ShortDebugString() | ||
| 353 | << std::endl; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
| 357 | } | ||
| 282 | } | 358 | } |
| 283 | 359 | ||
| 284 | void ValidatePanel(const PanelIdentifier& panel_identifier, | 360 | void ValidatePanel(const PanelIdentifier& panel_identifier, |
| @@ -340,6 +416,22 @@ class Validator { | |||
| 340 | std::cout << "Panel " << panel_identifier.ShortDebugString() | 416 | std::cout << "Panel " << panel_identifier.ShortDebugString() |
| 341 | << " is missing an AP ID." << std::endl; | 417 | << " is missing an AP ID." << std::endl; |
| 342 | } | 418 | } |
| 419 | |||
| 420 | if (!panel_info.target_connections_referenced_by.empty()) { | ||
| 421 | for (const HumanPanel& panel : panel_info.definitions) { | ||
| 422 | if (panel.has_required_door()) { | ||
| 423 | std::cout << "Panel " << panel_identifier.ShortDebugString() | ||
| 424 | << " has a required door but is the target of a connection:" | ||
| 425 | << std::endl; | ||
| 426 | |||
| 427 | for (const HumanConnection& connection : | ||
| 428 | panel_info.target_connections_referenced_by) { | ||
| 429 | std::cout << " CONNECTION " << connection.ShortDebugString() | ||
| 430 | << std::endl; | ||
| 431 | } | ||
| 432 | } | ||
| 433 | } | ||
| 434 | } | ||
| 343 | } | 435 | } |
| 344 | 436 | ||
| 345 | void ValidateKeyholder(const KeyholderIdentifier& keyholder_identifier, | 437 | void ValidateKeyholder(const KeyholderIdentifier& keyholder_identifier, |
| @@ -410,12 +502,6 @@ class Validator { | |||
| 410 | std::cout << "Ending " << ending_name | 502 | std::cout << "Ending " << ending_name |
| 411 | << " has no definition, but was referenced:" << std::endl; | 503 | << " has no definition, but was referenced:" << std::endl; |
| 412 | 504 | ||
| 413 | for (const DoorIdentifier& door_identifier : | ||
| 414 | ending_info.doors_referenced_by) { | ||
| 415 | std::cout << " DOOR " << door_identifier.ShortDebugString() | ||
| 416 | << std::endl; | ||
| 417 | } | ||
| 418 | |||
| 419 | if (ending_info.has_id) { | 505 | if (ending_info.has_id) { |
| 420 | std::cout << " An AP ID is present." << std::endl; | 506 | std::cout << " An AP ID is present." << std::endl; |
| 421 | } | 507 | } |
| diff --git a/tools/validator/validator.h b/tools/validator/validator.h index b710429..33bc7c9 100644 --- a/tools/validator/validator.h +++ b/tools/validator/validator.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | #ifndef TOOLS_VALIDATOR_VALIDATOR_H_ | 1 | #ifndef TOOLS_VALIDATOR_VALIDATOR_H |
| 2 | #define TOOLS_VALIDATOR_VALIDATOR_H | 2 | #define TOOLS_VALIDATOR_VALIDATOR_H |
| 3 | 3 | ||
| 4 | namespace com::fourisland::lingo2_archipelago { | 4 | namespace com::fourisland::lingo2_archipelago { |
| diff --git a/vcpkg.json b/vcpkg.json index 5a1975d..ba6833f 100644 --- a/vcpkg.json +++ b/vcpkg.json | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | "overrides": [ | 7 | "overrides": [ |
| 8 | { | 8 | { |
| 9 | "name": "protobuf", | 9 | "name": "protobuf", |
| 10 | "version": "5.29.3" | 10 | "version": "3.21.12" |
| 11 | } | 11 | } |
| 12 | ] | 12 | ] |
| 13 | } \ No newline at end of file | 13 | } \ No newline at end of file |
| diff --git a/vendor/godobuf/LICENSE b/vendor/godobuf/LICENSE new file mode 100644 index 0000000..5d473d8 --- /dev/null +++ b/vendor/godobuf/LICENSE | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | BSD 3-Clause License | ||
| 2 | |||
| 3 | Copyright (c) 2018, oniksan | ||
| 4 | All rights reserved. | ||
| 5 | |||
| 6 | Redistribution and use in source and binary forms, with or without | ||
| 7 | modification, are permitted provided that the following conditions are met: | ||
| 8 | |||
| 9 | * Redistributions of source code must retain the above copyright notice, this | ||
| 10 | list of conditions and the following disclaimer. | ||
| 11 | |||
| 12 | * Redistributions in binary form must reproduce the above copyright notice, | ||
| 13 | this list of conditions and the following disclaimer in the documentation | ||
| 14 | and/or other materials provided with the distribution. | ||
| 15 | |||
| 16 | * Neither the name of the copyright holder nor the names of its | ||
| 17 | contributors may be used to endorse or promote products derived from | ||
| 18 | this software without specific prior written permission. | ||
| 19 | |||
| 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
| 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
| 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
| 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| diff --git a/vendor/godobuf/README b/vendor/godobuf/README new file mode 100644 index 0000000..ce716bb --- /dev/null +++ b/vendor/godobuf/README | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | This is a fork of https://github.com/oniksan/godobuf with some minor changes so | ||
| 2 | that it is able to compile the Lingo 2 randomizer proto files. The plugin parts | ||
| 3 | of the project have also been removed since we only need the command line | ||
| 4 | script. | ||
| diff --git a/vendor/godobuf/addons/protobuf/parser.gd b/vendor/godobuf/addons/protobuf/parser.gd new file mode 100644 index 0000000..dfc0bdd --- /dev/null +++ b/vendor/godobuf/addons/protobuf/parser.gd | |||
| @@ -0,0 +1,2254 @@ | |||
| 1 | # | ||
| 2 | # BSD 3-Clause License | ||
| 3 | # | ||
| 4 | # Copyright (c) 2018 - 2023, Oleg Malyavkin | ||
| 5 | # All rights reserved. | ||
| 6 | # | ||
| 7 | # Redistribution and use in source and binary forms, with or without | ||
| 8 | # modification, are permitted provided that the following conditions are met: | ||
| 9 | # | ||
| 10 | # * Redistributions of source code must retain the above copyright notice, this | ||
| 11 | # list of conditions and the following disclaimer. | ||
| 12 | # | ||
| 13 | # * Redistributions in binary form must reproduce the above copyright notice, | ||
| 14 | # this list of conditions and the following disclaimer in the documentation | ||
| 15 | # and/or other materials provided with the distribution. | ||
| 16 | # | ||
| 17 | # * Neither the name of the copyright holder nor the names of its | ||
| 18 | # contributors may be used to endorse or promote products derived from | ||
| 19 | # this software without specific prior written permission. | ||
| 20 | # | ||
| 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 22 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 23 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
| 24 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
| 25 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 26 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 27 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
| 29 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | |||
| 32 | extends Node | ||
| 33 | |||
| 34 | const PROTO_VERSION_CONST : String = "const PROTO_VERSION = " | ||
| 35 | const PROTO_VERSION_DEFAULT : String = PROTO_VERSION_CONST + "0" | ||
| 36 | |||
| 37 | class Document: | ||
| 38 | |||
| 39 | func _init(doc_name : String, doc_text : String): | ||
| 40 | name = doc_name | ||
| 41 | text = doc_text | ||
| 42 | |||
| 43 | var name : String | ||
| 44 | var text : String | ||
| 45 | |||
| 46 | class TokenPosition: | ||
| 47 | func _init(b : int, e : int): | ||
| 48 | begin = b | ||
| 49 | end = e | ||
| 50 | var begin : int = 0 | ||
| 51 | var end : int = 0 | ||
| 52 | |||
| 53 | class Helper: | ||
| 54 | |||
| 55 | class StringPosition: | ||
| 56 | func _init(s : int, c : int, l : int): | ||
| 57 | str_num = s | ||
| 58 | column = c | ||
| 59 | length = l | ||
| 60 | var str_num : int | ||
| 61 | var column : int | ||
| 62 | var length : int | ||
| 63 | |||
| 64 | static func str_pos(text : String, position : TokenPosition) -> StringPosition: | ||
| 65 | var cur_str : int = 1 | ||
| 66 | var cur_col : int = 1 | ||
| 67 | var res_str : int = 0 | ||
| 68 | var res_col : int = 0 | ||
| 69 | var res_length : int = 0 | ||
| 70 | for i in range(text.length()): | ||
| 71 | if text[i] == "\n": | ||
| 72 | cur_str += 1 | ||
| 73 | cur_col = 0 | ||
| 74 | if position.begin == i: | ||
| 75 | res_str = cur_str | ||
| 76 | res_col = cur_col | ||
| 77 | res_length = position.end - position.begin + 1 | ||
| 78 | break | ||
| 79 | cur_col += 1 | ||
| 80 | return StringPosition.new(res_str, res_col, res_length) | ||
| 81 | |||
| 82 | static func text_pos(tokens : Array, index : int) -> TokenPosition: | ||
| 83 | var res_begin : int = 0 | ||
| 84 | var res_end : int = 0 | ||
| 85 | if index < tokens.size() && index >= 0: | ||
| 86 | res_begin = tokens[index].position.begin | ||
| 87 | res_end = tokens[index].position.end | ||
| 88 | return TokenPosition.new(res_begin, res_end) | ||
| 89 | |||
| 90 | static func error_string(file_name, col, row, error_text): | ||
| 91 | return file_name + ":" + str(col) + ":" + str(row) + ": error: " + error_text | ||
| 92 | |||
| 93 | class AnalyzeResult: | ||
| 94 | var classes : Array = [] | ||
| 95 | var fields : Array = [] | ||
| 96 | var groups : Array = [] | ||
| 97 | var version : int = 0 | ||
| 98 | var state : bool = false | ||
| 99 | var tokens : Array = [] | ||
| 100 | var syntax : Analysis.TranslationResult | ||
| 101 | var imports : Array = [] | ||
| 102 | var doc : Document | ||
| 103 | |||
| 104 | func soft_copy() -> AnalyzeResult: | ||
| 105 | var res : AnalyzeResult = AnalyzeResult.new() | ||
| 106 | res.classes = classes | ||
| 107 | res.fields = fields | ||
| 108 | res.groups = groups | ||
| 109 | res.version = version | ||
| 110 | res.state = state | ||
| 111 | res.tokens = tokens | ||
| 112 | res.syntax = syntax | ||
| 113 | res.imports = imports | ||
| 114 | res.doc = doc | ||
| 115 | return res | ||
| 116 | |||
| 117 | class Analysis: | ||
| 118 | |||
| 119 | func _init(path : String, doc : Document): | ||
| 120 | path_dir = path | ||
| 121 | document = doc | ||
| 122 | |||
| 123 | var document : Document | ||
| 124 | var path_dir : String | ||
| 125 | |||
| 126 | const LEX = { | ||
| 127 | LETTER = "[A-Za-z]", | ||
| 128 | DIGIT_DEC = "[0-9]", | ||
| 129 | DIGIT_OCT = "[0-7]", | ||
| 130 | DIGIT_HEX = "[0-9]|[A-F]|[a-f]", | ||
| 131 | BRACKET_ROUND_LEFT = "\\(", | ||
| 132 | BRACKET_ROUND_RIGHT = "\\)", | ||
| 133 | BRACKET_CURLY_LEFT = "\\{", | ||
| 134 | BRACKET_CURLY_RIGHT = "\\}", | ||
| 135 | BRACKET_SQUARE_LEFT = "\\[", | ||
| 136 | BRACKET_SQUARE_RIGHT = "\\]", | ||
| 137 | BRACKET_ANGLE_LEFT = "\\<", | ||
| 138 | BRACKET_ANGLE_RIGHT = "\\>", | ||
| 139 | SEMICOLON = ";", | ||
| 140 | COMMA = ",", | ||
| 141 | EQUAL = "=", | ||
| 142 | SIGN = "\\+|\\-", | ||
| 143 | SPACE = "\\s", | ||
| 144 | QUOTE_SINGLE = "'", | ||
| 145 | QUOTE_DOUBLE = "\"", | ||
| 146 | } | ||
| 147 | |||
| 148 | const TOKEN_IDENT : String = "(" + LEX.LETTER + "+" + "(" + LEX.LETTER + "|" + LEX.DIGIT_DEC + "|" + "_)*)" | ||
| 149 | const TOKEN_FULL_IDENT : String = TOKEN_IDENT + "{0,1}(\\." + TOKEN_IDENT + ")+" | ||
| 150 | const TOKEN_BRACKET_ROUND_LEFT : String = "(" + LEX.BRACKET_ROUND_LEFT + ")" | ||
| 151 | const TOKEN_BRACKET_ROUND_RIGHT : String = "(" + LEX.BRACKET_ROUND_RIGHT + ")" | ||
| 152 | const TOKEN_BRACKET_CURLY_LEFT : String = "(" + LEX.BRACKET_CURLY_LEFT + ")" | ||
| 153 | const TOKEN_BRACKET_CURLY_RIGHT : String = "(" + LEX.BRACKET_CURLY_RIGHT + ")" | ||
| 154 | const TOKEN_BRACKET_SQUARE_LEFT : String = "(" + LEX.BRACKET_SQUARE_LEFT + ")" | ||
| 155 | const TOKEN_BRACKET_SQUARE_RIGHT : String = "(" + LEX.BRACKET_SQUARE_RIGHT + ")" | ||
| 156 | const TOKEN_BRACKET_ANGLE_LEFT : String = "(" + LEX.BRACKET_ANGLE_LEFT + ")" | ||
| 157 | const TOKEN_BRACKET_ANGLE_RIGHT : String = "(" + LEX.BRACKET_ANGLE_RIGHT + ")" | ||
| 158 | const TOKEN_SEMICOLON : String = "(" + LEX.SEMICOLON + ")" | ||
| 159 | const TOKEN_EUQAL : String = "(" + LEX.EQUAL + ")" | ||
| 160 | const TOKEN_SIGN : String = "(" + LEX.SIGN + ")" | ||
| 161 | const TOKEN_LITERAL_DEC : String = "(([1-9])" + LEX.DIGIT_DEC +"*)" | ||
| 162 | const TOKEN_LITERAL_OCT : String = "(0" + LEX.DIGIT_OCT +"*)" | ||
| 163 | const TOKEN_LITERAL_HEX : String = "(0(x|X)(" + LEX.DIGIT_HEX +")+)" | ||
| 164 | const TOKEN_LITERAL_INT : String = "((\\+|\\-){0,1}" + TOKEN_LITERAL_DEC + "|" + TOKEN_LITERAL_OCT + "|" + TOKEN_LITERAL_HEX + ")" | ||
| 165 | const TOKEN_LITERAL_FLOAT_DEC : String = "(" + LEX.DIGIT_DEC + "+)" | ||
| 166 | const TOKEN_LITERAL_FLOAT_EXP : String = "((e|E)(\\+|\\-)?" + TOKEN_LITERAL_FLOAT_DEC + "+)" | ||
| 167 | const TOKEN_LITERAL_FLOAT : String = "((\\+|\\-){0,1}(" + TOKEN_LITERAL_FLOAT_DEC + "\\." + TOKEN_LITERAL_FLOAT_DEC + "?" + TOKEN_LITERAL_FLOAT_EXP + "?)|(" + TOKEN_LITERAL_FLOAT_DEC + TOKEN_LITERAL_FLOAT_EXP + ")|(\\." + TOKEN_LITERAL_FLOAT_DEC + TOKEN_LITERAL_FLOAT_EXP + "?))" | ||
| 168 | const TOKEN_SPACE : String = "(" + LEX.SPACE + ")+" | ||
| 169 | const TOKEN_COMMA : String = "(" + LEX.COMMA + ")" | ||
| 170 | const TOKEN_CHAR_ESC : String = "[\\\\(a|b|f|n|r|t|v|\\\\|'|\")]" | ||
| 171 | const TOKEN_OCT_ESC : String = "[\\\\" + LEX.DIGIT_OCT + "{3}]" | ||
| 172 | const TOKEN_HEX_ESC : String = "[\\\\(x|X)" + LEX.DIGIT_HEX + "{2}]" | ||
| 173 | const TOKEN_CHAR_EXCLUDE : String = "[^\\0\\n\\\\]" | ||
| 174 | const TOKEN_CHAR_VALUE : String = "(" + TOKEN_HEX_ESC + "|" + TOKEN_OCT_ESC + "|" + TOKEN_CHAR_ESC + "|" + TOKEN_CHAR_EXCLUDE + ")" | ||
| 175 | const TOKEN_STRING_SINGLE : String = "('" + TOKEN_CHAR_VALUE + "*?')" | ||
| 176 | const TOKEN_STRING_DOUBLE : String = "(\"" + TOKEN_CHAR_VALUE + "*?\")" | ||
| 177 | const TOKEN_COMMENT_SINGLE : String = "((//[^\\n\\r]*[^\\s])|//)" | ||
| 178 | const TOKEN_COMMENT_MULTI : String = "/\\*(.|[\\n\\r])*?\\*/" | ||
| 179 | |||
| 180 | const TOKEN_SECOND_MESSAGE : String = "^message$" | ||
| 181 | const TOKEN_SECOND_SIMPLE_DATA_TYPE : String = "^(double|float|int32|int64|uint32|uint64|sint32|sint64|fixed32|fixed64|sfixed32|sfixed64|bool|string|bytes)$" | ||
| 182 | const TOKEN_SECOND_ENUM : String = "^enum$" | ||
| 183 | const TOKEN_SECOND_MAP : String = "^map$" | ||
| 184 | const TOKEN_SECOND_ONEOF : String = "^oneof$" | ||
| 185 | const TOKEN_SECOND_LITERAL_BOOL : String = "^(true|false)$" | ||
| 186 | const TOKEN_SECOND_SYNTAX : String = "^syntax$" | ||
| 187 | const TOKEN_SECOND_IMPORT : String = "^import$" | ||
| 188 | const TOKEN_SECOND_PACKAGE : String = "^package$" | ||
| 189 | const TOKEN_SECOND_OPTION : String = "^option$" | ||
| 190 | const TOKEN_SECOND_SERVICE : String = "^service$" | ||
| 191 | const TOKEN_SECOND_RESERVED : String = "^reserved$" | ||
| 192 | const TOKEN_SECOND_IMPORT_QUALIFICATION : String = "^(weak|public)$" | ||
| 193 | const TOKEN_SECOND_FIELD_QUALIFICATION : String = "^(repeated|required|optional)$" | ||
| 194 | const TOKEN_SECOND_ENUM_OPTION : String = "^allow_alias$" | ||
| 195 | const TOKEN_SECOND_QUALIFICATION : String = "^(custom_option|extensions)$" | ||
| 196 | const TOKEN_SECOND_FIELD_OPTION : String = "^packed$" | ||
| 197 | |||
| 198 | class TokenEntrance: | ||
| 199 | func _init(i : int, b : int, e : int, t : String): | ||
| 200 | position = TokenPosition.new(b, e) | ||
| 201 | text = t | ||
| 202 | id = i | ||
| 203 | var position : TokenPosition | ||
| 204 | var text : String | ||
| 205 | var id : int | ||
| 206 | |||
| 207 | enum RANGE_STATE { | ||
| 208 | INCLUDE = 0, | ||
| 209 | EXCLUDE_LEFT = 1, | ||
| 210 | EXCLUDE_RIGHT = 2, | ||
| 211 | OVERLAY = 3, | ||
| 212 | EQUAL = 4, | ||
| 213 | ENTERS = 5 | ||
| 214 | } | ||
| 215 | |||
| 216 | class TokenRange: | ||
| 217 | func _init(b : int, e : int, s): | ||
| 218 | position = TokenPosition.new(b, e) | ||
| 219 | state = s | ||
| 220 | var position : TokenPosition | ||
| 221 | var state | ||
| 222 | |||
| 223 | class Token: | ||
| 224 | var _regex : RegEx | ||
| 225 | var _entrance : TokenEntrance = null | ||
| 226 | var _entrances : Array = [] | ||
| 227 | var _entrance_index : int = 0 | ||
| 228 | var _id : int | ||
| 229 | var _ignore : bool | ||
| 230 | var _clarification : String | ||
| 231 | |||
| 232 | func _init(id : int, clarification : String, regex_str : String, ignore = false): | ||
| 233 | _id = id | ||
| 234 | _regex = RegEx.new() | ||
| 235 | _regex.compile(regex_str) | ||
| 236 | _clarification = clarification | ||
| 237 | _ignore = ignore | ||
| 238 | |||
| 239 | func find(text : String, start : int) -> TokenEntrance: | ||
| 240 | _entrance = null | ||
| 241 | if !_regex.is_valid(): | ||
| 242 | return null | ||
| 243 | var match_result : RegExMatch = _regex.search(text, start) | ||
| 244 | if match_result != null: | ||
| 245 | var capture | ||
| 246 | capture = match_result.get_string(0) | ||
| 247 | if capture.is_empty(): | ||
| 248 | return null | ||
| 249 | _entrance = TokenEntrance.new(_id, match_result.get_start(0), capture.length() - 1 + match_result.get_start(0), capture) | ||
| 250 | return _entrance | ||
| 251 | |||
| 252 | func find_all(text : String) -> Array: | ||
| 253 | var pos : int = 0 | ||
| 254 | clear() | ||
| 255 | while find(text, pos) != null: | ||
| 256 | _entrances.append(_entrance) | ||
| 257 | pos = _entrance.position.end + 1 | ||
| 258 | return _entrances | ||
| 259 | |||
| 260 | func add_entrance(entrance) -> void: | ||
| 261 | _entrances.append(entrance) | ||
| 262 | |||
| 263 | func clear() -> void: | ||
| 264 | _entrance = null | ||
| 265 | _entrances = [] | ||
| 266 | _entrance_index = 0 | ||
| 267 | |||
| 268 | func get_entrances() -> Array: | ||
| 269 | return _entrances | ||
| 270 | |||
| 271 | func remove_entrance(index) -> void: | ||
| 272 | if index < _entrances.size(): | ||
| 273 | _entrances.remove_at(index) | ||
| 274 | |||
| 275 | func get_index() -> int: | ||
| 276 | return _entrance_index | ||
| 277 | |||
| 278 | func set_index(index : int) -> void: | ||
| 279 | if index < _entrances.size(): | ||
| 280 | _entrance_index = index | ||
| 281 | else: | ||
| 282 | _entrance_index = 0 | ||
| 283 | |||
| 284 | func is_ignore() -> bool: | ||
| 285 | return _ignore | ||
| 286 | |||
| 287 | func get_clarification() -> String: | ||
| 288 | return _clarification | ||
| 289 | |||
| 290 | class TokenResult: | ||
| 291 | var tokens : Array = [] | ||
| 292 | var errors : Array = [] | ||
| 293 | |||
| 294 | enum TOKEN_ID { | ||
| 295 | UNDEFINED = -1, | ||
| 296 | IDENT = 0, | ||
| 297 | FULL_IDENT = 1, | ||
| 298 | BRACKET_ROUND_LEFT = 2, | ||
| 299 | BRACKET_ROUND_RIGHT = 3, | ||
| 300 | BRACKET_CURLY_LEFT = 4, | ||
| 301 | BRACKET_CURLY_RIGHT = 5, | ||
| 302 | BRACKET_SQUARE_LEFT = 6, | ||
| 303 | BRACKET_SQUARE_RIGHT = 7, | ||
| 304 | BRACKET_ANGLE_LEFT = 8, | ||
| 305 | BRACKET_ANGLE_RIGHT = 9, | ||
| 306 | SEMICOLON = 10, | ||
| 307 | EUQAL = 11, | ||
| 308 | SIGN = 12, | ||
| 309 | INT = 13, | ||
| 310 | FLOAT = 14, | ||
| 311 | SPACE = 15, | ||
| 312 | COMMA = 16, | ||
| 313 | STRING_SINGLE = 17, | ||
| 314 | STRING_DOUBLE = 18, | ||
| 315 | COMMENT_SINGLE = 19, | ||
| 316 | COMMENT_MULTI = 20, | ||
| 317 | |||
| 318 | MESSAGE = 21, | ||
| 319 | SIMPLE_DATA_TYPE = 22, | ||
| 320 | ENUM = 23, | ||
| 321 | MAP = 24, | ||
| 322 | ONEOF = 25, | ||
| 323 | LITERAL_BOOL = 26, | ||
| 324 | SYNTAX = 27, | ||
| 325 | IMPORT = 28, | ||
| 326 | PACKAGE = 29, | ||
| 327 | OPTION = 30, | ||
| 328 | SERVICE = 31, | ||
| 329 | RESERVED = 32, | ||
| 330 | IMPORT_QUALIFICATION = 33, | ||
| 331 | FIELD_QUALIFICATION = 34, | ||
| 332 | ENUM_OPTION = 35, | ||
| 333 | QUALIFICATION = 36, | ||
| 334 | FIELD_OPTION = 37, | ||
| 335 | |||
| 336 | STRING = 38 | ||
| 337 | } | ||
| 338 | |||
| 339 | var TOKEN = { | ||
| 340 | TOKEN_ID.IDENT: Token.new(TOKEN_ID.IDENT, "Identifier", TOKEN_IDENT), | ||
| 341 | TOKEN_ID.FULL_IDENT: Token.new(TOKEN_ID.FULL_IDENT, "Full identifier", TOKEN_FULL_IDENT), | ||
| 342 | TOKEN_ID.BRACKET_ROUND_LEFT: Token.new(TOKEN_ID.BRACKET_ROUND_LEFT, "(", TOKEN_BRACKET_ROUND_LEFT), | ||
| 343 | TOKEN_ID.BRACKET_ROUND_RIGHT: Token.new(TOKEN_ID.BRACKET_ROUND_RIGHT, ")", TOKEN_BRACKET_ROUND_RIGHT), | ||
| 344 | TOKEN_ID.BRACKET_CURLY_LEFT: Token.new(TOKEN_ID.BRACKET_CURLY_LEFT, "{", TOKEN_BRACKET_CURLY_LEFT), | ||
| 345 | TOKEN_ID.BRACKET_CURLY_RIGHT: Token.new(TOKEN_ID.BRACKET_CURLY_RIGHT, "}", TOKEN_BRACKET_CURLY_RIGHT), | ||
| 346 | TOKEN_ID.BRACKET_SQUARE_LEFT: Token.new(TOKEN_ID.BRACKET_SQUARE_LEFT, "[", TOKEN_BRACKET_SQUARE_LEFT), | ||
| 347 | TOKEN_ID.BRACKET_SQUARE_RIGHT: Token.new(TOKEN_ID.BRACKET_SQUARE_RIGHT, "]", TOKEN_BRACKET_SQUARE_RIGHT), | ||
| 348 | TOKEN_ID.BRACKET_ANGLE_LEFT: Token.new(TOKEN_ID.BRACKET_ANGLE_LEFT, "<", TOKEN_BRACKET_ANGLE_LEFT), | ||
| 349 | TOKEN_ID.BRACKET_ANGLE_RIGHT: Token.new(TOKEN_ID.BRACKET_ANGLE_RIGHT, ">", TOKEN_BRACKET_ANGLE_RIGHT), | ||
| 350 | TOKEN_ID.SEMICOLON: Token.new(TOKEN_ID.SEMICOLON, ";", TOKEN_SEMICOLON), | ||
| 351 | TOKEN_ID.EUQAL: Token.new(TOKEN_ID.EUQAL, "=", TOKEN_EUQAL), | ||
| 352 | TOKEN_ID.INT: Token.new(TOKEN_ID.INT, "Integer", TOKEN_LITERAL_INT), | ||
| 353 | TOKEN_ID.FLOAT: Token.new(TOKEN_ID.FLOAT, "Float", TOKEN_LITERAL_FLOAT), | ||
| 354 | TOKEN_ID.SPACE: Token.new(TOKEN_ID.SPACE, "Space", TOKEN_SPACE), | ||
| 355 | TOKEN_ID.COMMA: Token.new(TOKEN_ID.COMMA, ",", TOKEN_COMMA), | ||
| 356 | TOKEN_ID.STRING_SINGLE: Token.new(TOKEN_ID.STRING_SINGLE, "'String'", TOKEN_STRING_SINGLE), | ||
| 357 | TOKEN_ID.STRING_DOUBLE: Token.new(TOKEN_ID.STRING_DOUBLE, "\"String\"", TOKEN_STRING_DOUBLE), | ||
| 358 | TOKEN_ID.COMMENT_SINGLE: Token.new(TOKEN_ID.COMMENT_SINGLE, "//Comment", TOKEN_COMMENT_SINGLE), | ||
| 359 | TOKEN_ID.COMMENT_MULTI: Token.new(TOKEN_ID.COMMENT_MULTI, "/*Comment*/", TOKEN_COMMENT_MULTI), | ||
| 360 | |||
| 361 | TOKEN_ID.MESSAGE: Token.new(TOKEN_ID.MESSAGE, "Message", TOKEN_SECOND_MESSAGE, true), | ||
| 362 | TOKEN_ID.SIMPLE_DATA_TYPE: Token.new(TOKEN_ID.SIMPLE_DATA_TYPE, "Data type", TOKEN_SECOND_SIMPLE_DATA_TYPE, true), | ||
| 363 | TOKEN_ID.ENUM: Token.new(TOKEN_ID.ENUM, "Enum", TOKEN_SECOND_ENUM, true), | ||
| 364 | TOKEN_ID.MAP: Token.new(TOKEN_ID.MAP, "Map", TOKEN_SECOND_MAP, true), | ||
| 365 | TOKEN_ID.ONEOF: Token.new(TOKEN_ID.ONEOF, "OneOf", TOKEN_SECOND_ONEOF, true), | ||
| 366 | TOKEN_ID.LITERAL_BOOL: Token.new(TOKEN_ID.LITERAL_BOOL, "Bool literal", TOKEN_SECOND_LITERAL_BOOL, true), | ||
| 367 | TOKEN_ID.SYNTAX: Token.new(TOKEN_ID.SYNTAX, "Syntax", TOKEN_SECOND_SYNTAX, true), | ||
| 368 | TOKEN_ID.IMPORT: Token.new(TOKEN_ID.IMPORT, "Import", TOKEN_SECOND_IMPORT, true), | ||
| 369 | TOKEN_ID.PACKAGE: Token.new(TOKEN_ID.PACKAGE, "Package", TOKEN_SECOND_PACKAGE, true), | ||
| 370 | TOKEN_ID.OPTION: Token.new(TOKEN_ID.OPTION, "Option", TOKEN_SECOND_OPTION, true), | ||
| 371 | TOKEN_ID.SERVICE: Token.new(TOKEN_ID.SERVICE, "Service", TOKEN_SECOND_SERVICE, true), | ||
| 372 | TOKEN_ID.RESERVED: Token.new(TOKEN_ID.RESERVED, "Reserved", TOKEN_SECOND_RESERVED, true), | ||
| 373 | TOKEN_ID.IMPORT_QUALIFICATION: Token.new(TOKEN_ID.IMPORT_QUALIFICATION, "Import qualification", TOKEN_SECOND_IMPORT_QUALIFICATION, true), | ||
| 374 | TOKEN_ID.FIELD_QUALIFICATION: Token.new(TOKEN_ID.FIELD_QUALIFICATION, "Field qualification", TOKEN_SECOND_FIELD_QUALIFICATION, true), | ||
| 375 | TOKEN_ID.ENUM_OPTION: Token.new(TOKEN_ID.ENUM_OPTION, "Enum option", TOKEN_SECOND_ENUM_OPTION, true), | ||
| 376 | TOKEN_ID.QUALIFICATION: Token.new(TOKEN_ID.QUALIFICATION, "Qualification", TOKEN_SECOND_QUALIFICATION, true), | ||
| 377 | TOKEN_ID.FIELD_OPTION: Token.new(TOKEN_ID.FIELD_OPTION, "Field option", TOKEN_SECOND_FIELD_OPTION, true), | ||
| 378 | |||
| 379 | TOKEN_ID.STRING: Token.new(TOKEN_ID.STRING, "String", "", true) | ||
| 380 | } | ||
| 381 | |||
| 382 | static func check_range(main : TokenEntrance, current : TokenEntrance) -> TokenRange: | ||
| 383 | if main.position.begin > current.position.begin: | ||
| 384 | if main.position.end > current.position.end: | ||
| 385 | if main.position.begin >= current.position.end: | ||
| 386 | return TokenRange.new(current.position.begin, current.position.end, RANGE_STATE.EXCLUDE_LEFT) | ||
| 387 | else: | ||
| 388 | return TokenRange.new(main.position.begin, current.position.end, RANGE_STATE.OVERLAY) | ||
| 389 | else: | ||
| 390 | return TokenRange.new(current.position.begin, current.position.end, RANGE_STATE.ENTERS) | ||
| 391 | elif main.position.begin < current.position.begin: | ||
| 392 | if main.position.end >= current.position.end: | ||
| 393 | return TokenRange.new(main.position.begin, main.position.end, RANGE_STATE.INCLUDE) | ||
| 394 | else: | ||
| 395 | if main.position.end < current.position.begin: | ||
| 396 | return TokenRange.new(main.position.begin, main.position.end, RANGE_STATE.EXCLUDE_RIGHT) | ||
| 397 | else: | ||
| 398 | return TokenRange.new(main.position.begin, current.position.end, RANGE_STATE.OVERLAY) | ||
| 399 | else: | ||
| 400 | if main.position.end == current.position.end: | ||
| 401 | return TokenRange.new(main.position.begin, main.position.end, RANGE_STATE.EQUAL) | ||
| 402 | elif main.position.end > current.position.end: | ||
| 403 | return TokenRange.new(main.position.begin, main.position.end, RANGE_STATE.INCLUDE) | ||
| 404 | else: | ||
| 405 | return TokenRange.new(current.position.begin, current.position.end, RANGE_STATE.ENTERS) | ||
| 406 | |||
| 407 | func tokenizer() -> TokenResult: | ||
| 408 | for k in TOKEN: | ||
| 409 | if !TOKEN[k].is_ignore(): | ||
| 410 | TOKEN[k].find_all(document.text) | ||
| 411 | var second_tokens : Array = [] | ||
| 412 | second_tokens.append(TOKEN[TOKEN_ID.MESSAGE]) | ||
| 413 | second_tokens.append(TOKEN[TOKEN_ID.SIMPLE_DATA_TYPE]) | ||
| 414 | second_tokens.append(TOKEN[TOKEN_ID.ENUM]) | ||
| 415 | second_tokens.append(TOKEN[TOKEN_ID.MAP]) | ||
| 416 | second_tokens.append(TOKEN[TOKEN_ID.ONEOF]) | ||
| 417 | second_tokens.append(TOKEN[TOKEN_ID.LITERAL_BOOL]) | ||
| 418 | second_tokens.append(TOKEN[TOKEN_ID.SYNTAX]) | ||
| 419 | second_tokens.append(TOKEN[TOKEN_ID.IMPORT]) | ||
| 420 | second_tokens.append(TOKEN[TOKEN_ID.PACKAGE]) | ||
| 421 | second_tokens.append(TOKEN[TOKEN_ID.OPTION]) | ||
| 422 | second_tokens.append(TOKEN[TOKEN_ID.SERVICE]) | ||
| 423 | second_tokens.append(TOKEN[TOKEN_ID.RESERVED]) | ||
| 424 | second_tokens.append(TOKEN[TOKEN_ID.IMPORT_QUALIFICATION]) | ||
| 425 | second_tokens.append(TOKEN[TOKEN_ID.FIELD_QUALIFICATION]) | ||
| 426 | second_tokens.append(TOKEN[TOKEN_ID.ENUM_OPTION]) | ||
| 427 | second_tokens.append(TOKEN[TOKEN_ID.QUALIFICATION]) | ||
| 428 | second_tokens.append(TOKEN[TOKEN_ID.FIELD_OPTION]) | ||
| 429 | |||
| 430 | var ident_token : Token = TOKEN[TOKEN_ID.IDENT] | ||
| 431 | for sec_token in second_tokens: | ||
| 432 | var remove_indexes : Array = [] | ||
| 433 | for i in range(ident_token.get_entrances().size()): | ||
| 434 | var entrance : TokenEntrance = sec_token.find(ident_token.get_entrances()[i].text, 0) | ||
| 435 | if entrance != null: | ||
| 436 | entrance.position.begin = ident_token.get_entrances()[i].position.begin | ||
| 437 | entrance.position.end = ident_token.get_entrances()[i].position.end | ||
| 438 | sec_token.add_entrance(entrance) | ||
| 439 | remove_indexes.append(i) | ||
| 440 | for i in range(remove_indexes.size()): | ||
| 441 | ident_token.remove_entrance(remove_indexes[i] - i) | ||
| 442 | for v in TOKEN[TOKEN_ID.STRING_DOUBLE].get_entrances(): | ||
| 443 | v.id = TOKEN_ID.STRING | ||
| 444 | TOKEN[TOKEN_ID.STRING].add_entrance(v) | ||
| 445 | TOKEN[TOKEN_ID.STRING_DOUBLE].clear() | ||
| 446 | for v in TOKEN[TOKEN_ID.STRING_SINGLE].get_entrances(): | ||
| 447 | v.id = TOKEN_ID.STRING | ||
| 448 | TOKEN[TOKEN_ID.STRING].add_entrance(v) | ||
| 449 | TOKEN[TOKEN_ID.STRING_SINGLE].clear() | ||
| 450 | var main_token : TokenEntrance | ||
| 451 | var cur_token : TokenEntrance | ||
| 452 | var main_index : int = -1 | ||
| 453 | var token_index_flag : bool = false | ||
| 454 | var result : TokenResult = TokenResult.new() | ||
| 455 | var check : TokenRange | ||
| 456 | var end : bool = false | ||
| 457 | var all : bool = false | ||
| 458 | var repeat : bool = false | ||
| 459 | while true: | ||
| 460 | all = true | ||
| 461 | for k in TOKEN: | ||
| 462 | if main_index == k: | ||
| 463 | continue | ||
| 464 | repeat = false | ||
| 465 | while TOKEN[k].get_entrances().size() > 0: | ||
| 466 | all = false | ||
| 467 | if !token_index_flag: | ||
| 468 | main_index = k | ||
| 469 | main_token = TOKEN[main_index].get_entrances()[0] | ||
| 470 | token_index_flag = true | ||
| 471 | break | ||
| 472 | else: | ||
| 473 | cur_token = TOKEN[k].get_entrances()[0] | ||
| 474 | check = check_range(main_token, cur_token) | ||
| 475 | if check.state == RANGE_STATE.INCLUDE: | ||
| 476 | TOKEN[k].remove_entrance(0) | ||
| 477 | end = true | ||
| 478 | elif check.state == RANGE_STATE.EXCLUDE_LEFT: | ||
| 479 | main_token = cur_token | ||
| 480 | main_index = k | ||
| 481 | end = false | ||
| 482 | repeat = true | ||
| 483 | break | ||
| 484 | elif check.state == RANGE_STATE.EXCLUDE_RIGHT: | ||
| 485 | end = true | ||
| 486 | break | ||
| 487 | elif check.state == RANGE_STATE.OVERLAY || check.state == RANGE_STATE.EQUAL: | ||
| 488 | result.errors.append(check) | ||
| 489 | TOKEN[main_index].remove_entrance(0) | ||
| 490 | TOKEN[k].remove_entrance(0) | ||
| 491 | token_index_flag = false | ||
| 492 | end = false | ||
| 493 | repeat = true | ||
| 494 | break | ||
| 495 | elif check.state == RANGE_STATE.ENTERS: | ||
| 496 | TOKEN[main_index].remove_entrance(0) | ||
| 497 | main_token = cur_token | ||
| 498 | main_index = k | ||
| 499 | end = false | ||
| 500 | repeat = true | ||
| 501 | break | ||
| 502 | if repeat: | ||
| 503 | break | ||
| 504 | if end: | ||
| 505 | if TOKEN[main_index].get_entrances().size() > 0: | ||
| 506 | result.tokens.append(main_token) | ||
| 507 | TOKEN[main_index].remove_entrance(0) | ||
| 508 | token_index_flag = false | ||
| 509 | if all: | ||
| 510 | break | ||
| 511 | return result | ||
| 512 | |||
| 513 | static func check_tokens_integrity(tokens : Array, end : int) -> Array: | ||
| 514 | var cur_index : int = 0 | ||
| 515 | var result : Array = [] | ||
| 516 | for v in tokens: | ||
| 517 | if v.position.begin > cur_index: | ||
| 518 | result.append(TokenPosition.new(cur_index, v.position.begin)) | ||
| 519 | cur_index = v.position.end + 1 | ||
| 520 | if cur_index < end: | ||
| 521 | result.append(TokenPosition.new(cur_index, end)) | ||
| 522 | return result | ||
| 523 | |||
| 524 | static func comment_space_processing(tokens : Array) -> void: | ||
| 525 | var remove_indexes : Array = [] | ||
| 526 | for i in range(tokens.size()): | ||
| 527 | if tokens[i].id == TOKEN_ID.COMMENT_SINGLE || tokens[i].id == TOKEN_ID.COMMENT_MULTI: | ||
| 528 | tokens[i].id = TOKEN_ID.SPACE | ||
| 529 | var space_index : int = -1 | ||
| 530 | for i in range(tokens.size()): | ||
| 531 | if tokens[i].id == TOKEN_ID.SPACE: | ||
| 532 | if space_index >= 0: | ||
| 533 | tokens[space_index].position.end = tokens[i].position.end | ||
| 534 | tokens[space_index].text = tokens[space_index].text + tokens[i].text | ||
| 535 | remove_indexes.append(i) | ||
| 536 | else: | ||
| 537 | space_index = i | ||
| 538 | else: | ||
| 539 | space_index = -1 | ||
| 540 | for i in range(remove_indexes.size()): | ||
| 541 | tokens.remove_at(remove_indexes[i] - i) | ||
| 542 | |||
| 543 | #Analysis rule | ||
| 544 | enum AR { | ||
| 545 | MAYBE = 1, | ||
| 546 | MUST_ONE = 2, | ||
| 547 | ANY = 3, | ||
| 548 | OR = 4, | ||
| 549 | MAYBE_BEGIN = 5, | ||
| 550 | MAYBE_END = 6, | ||
| 551 | ANY_BEGIN = 7, | ||
| 552 | ANY_END = 8 | ||
| 553 | } | ||
| 554 | |||
| 555 | #Space rule (space after token) | ||
| 556 | enum SP { | ||
| 557 | MAYBE = 1, | ||
| 558 | MUST = 2, | ||
| 559 | NO = 3 | ||
| 560 | } | ||
| 561 | |||
| 562 | #Analysis Syntax Description | ||
| 563 | class ASD: | ||
| 564 | func _init(t, s : int = SP.MAYBE, r : int = AR.MUST_ONE, i : bool = false): | ||
| 565 | token = t | ||
| 566 | space = s | ||
| 567 | rule = r | ||
| 568 | importance = i | ||
| 569 | var token | ||
| 570 | var space : int | ||
| 571 | var rule : int | ||
| 572 | var importance : bool | ||
| 573 | |||
| 574 | var TEMPLATE_SYNTAX : Array = [ | ||
| 575 | Callable(self, "desc_syntax"), | ||
| 576 | ASD.new(TOKEN_ID.SYNTAX), | ||
| 577 | ASD.new(TOKEN_ID.EUQAL), | ||
| 578 | ASD.new(TOKEN_ID.STRING, SP.MAYBE, AR.MUST_ONE, true), | ||
| 579 | ASD.new(TOKEN_ID.SEMICOLON) | ||
| 580 | ] | ||
| 581 | |||
| 582 | var TEMPLATE_IMPORT : Array = [ | ||
| 583 | Callable(self, "desc_import"), | ||
| 584 | ASD.new(TOKEN_ID.IMPORT, SP.MUST), | ||
| 585 | ASD.new(TOKEN_ID.IMPORT_QUALIFICATION, SP.MUST, AR.MAYBE, true), | ||
| 586 | ASD.new(TOKEN_ID.STRING, SP.MAYBE, AR.MUST_ONE, true), | ||
| 587 | ASD.new(TOKEN_ID.SEMICOLON) | ||
| 588 | ] | ||
| 589 | |||
| 590 | var TEMPLATE_PACKAGE : Array = [ | ||
| 591 | Callable(self, "desc_package"), | ||
| 592 | ASD.new(TOKEN_ID.PACKAGE, SP.MUST), | ||
| 593 | ASD.new([TOKEN_ID.IDENT, TOKEN_ID.FULL_IDENT], SP.MAYBE, AR.OR, true), | ||
| 594 | ASD.new(TOKEN_ID.SEMICOLON) | ||
| 595 | ] | ||
| 596 | |||
| 597 | var TEMPLATE_OPTION : Array = [ | ||
| 598 | Callable(self, "desc_option"), | ||
| 599 | ASD.new(TOKEN_ID.OPTION, SP.MUST), | ||
| 600 | ASD.new([TOKEN_ID.IDENT, TOKEN_ID.FULL_IDENT], SP.MAYBE, AR.OR, true), | ||
| 601 | ASD.new(TOKEN_ID.EUQAL), | ||
| 602 | ASD.new([TOKEN_ID.STRING, TOKEN_ID.INT, TOKEN_ID.FLOAT, TOKEN_ID.LITERAL_BOOL], SP.MAYBE, AR.OR, true), | ||
| 603 | ASD.new(TOKEN_ID.SEMICOLON) | ||
| 604 | ] | ||
| 605 | |||
| 606 | var TEMPLATE_FIELD : Array = [ | ||
| 607 | Callable(self, "desc_field"), | ||
| 608 | ASD.new(TOKEN_ID.FIELD_QUALIFICATION, SP.MUST, AR.MAYBE, true), | ||
| 609 | ASD.new([TOKEN_ID.SIMPLE_DATA_TYPE, TOKEN_ID.IDENT, TOKEN_ID.FULL_IDENT], SP.MAYBE, AR.OR, true), | ||
| 610 | ASD.new(TOKEN_ID.IDENT, SP.MAYBE, AR.MUST_ONE, true), | ||
| 611 | ASD.new(TOKEN_ID.EUQAL), | ||
| 612 | ASD.new(TOKEN_ID.INT, SP.MAYBE, AR.MUST_ONE, true), | ||
| 613 | ASD.new(TOKEN_ID.BRACKET_SQUARE_LEFT, SP.MAYBE, AR.MAYBE_BEGIN), | ||
| 614 | ASD.new(TOKEN_ID.FIELD_OPTION, SP.MAYBE, AR.MUST_ONE, true), | ||
| 615 | ASD.new(TOKEN_ID.EUQAL), | ||
| 616 | ASD.new(TOKEN_ID.LITERAL_BOOL, SP.MAYBE, AR.MUST_ONE, true), | ||
| 617 | ASD.new(TOKEN_ID.BRACKET_SQUARE_RIGHT, SP.MAYBE, AR.MAYBE_END), | ||
| 618 | ASD.new(TOKEN_ID.SEMICOLON) | ||
| 619 | ] | ||
| 620 | |||
| 621 | var TEMPLATE_FIELD_ONEOF : Array = TEMPLATE_FIELD | ||
| 622 | |||
| 623 | var TEMPLATE_MAP_FIELD : Array = [ | ||
| 624 | Callable(self, "desc_map_field"), | ||
| 625 | ASD.new(TOKEN_ID.MAP), | ||
| 626 | ASD.new(TOKEN_ID.BRACKET_ANGLE_LEFT), | ||
| 627 | ASD.new(TOKEN_ID.SIMPLE_DATA_TYPE, SP.MAYBE, AR.MUST_ONE, true), | ||
| 628 | ASD.new(TOKEN_ID.COMMA), | ||
| 629 | ASD.new([TOKEN_ID.SIMPLE_DATA_TYPE, TOKEN_ID.IDENT, TOKEN_ID.FULL_IDENT], SP.MAYBE, AR.OR, true), | ||
| 630 | ASD.new(TOKEN_ID.BRACKET_ANGLE_RIGHT, SP.MUST), | ||
| 631 | ASD.new(TOKEN_ID.IDENT, SP.MAYBE, AR.MUST_ONE, true), | ||
| 632 | ASD.new(TOKEN_ID.EUQAL), | ||
| 633 | ASD.new(TOKEN_ID.INT, SP.MAYBE, AR.MUST_ONE, true), | ||
| 634 | ASD.new(TOKEN_ID.BRACKET_SQUARE_LEFT, SP.MAYBE, AR.MAYBE_BEGIN), | ||
| 635 | ASD.new(TOKEN_ID.FIELD_OPTION, SP.MAYBE, AR.MUST_ONE, true), | ||
| 636 | ASD.new(TOKEN_ID.EUQAL), | ||
| 637 | ASD.new(TOKEN_ID.LITERAL_BOOL, SP.MAYBE, AR.MUST_ONE, true), | ||
| 638 | ASD.new(TOKEN_ID.BRACKET_SQUARE_RIGHT, SP.MAYBE, AR.MAYBE_END), | ||
| 639 | ASD.new(TOKEN_ID.SEMICOLON) | ||
| 640 | ] | ||
| 641 | |||
| 642 | var TEMPLATE_MAP_FIELD_ONEOF : Array = TEMPLATE_MAP_FIELD | ||
| 643 | |||
| 644 | var TEMPLATE_ENUM : Array = [ | ||
| 645 | Callable(self, "desc_enum"), | ||
| 646 | ASD.new(TOKEN_ID.ENUM, SP.MUST), | ||
| 647 | ASD.new(TOKEN_ID.IDENT, SP.MAYBE, AR.MUST_ONE, true), | ||
| 648 | ASD.new(TOKEN_ID.BRACKET_CURLY_LEFT), | ||
| 649 | ASD.new(TOKEN_ID.OPTION, SP.MUST, AR.MAYBE_BEGIN), | ||
| 650 | ASD.new(TOKEN_ID.ENUM_OPTION, SP.MAYBE, AR.MUST_ONE, true), | ||
| 651 | ASD.new(TOKEN_ID.EUQAL), | ||
| 652 | ASD.new(TOKEN_ID.LITERAL_BOOL, SP.MAYBE, AR.MUST_ONE, true), | ||
| 653 | ASD.new(TOKEN_ID.SEMICOLON, SP.MAYBE, AR.MAYBE_END), | ||
| 654 | ASD.new(TOKEN_ID.IDENT, SP.MAYBE, AR.ANY_BEGIN, true), | ||
| 655 | ASD.new(TOKEN_ID.EUQAL), | ||
| 656 | ASD.new(TOKEN_ID.INT, SP.MAYBE, AR.MUST_ONE, true), | ||
| 657 | ASD.new(TOKEN_ID.SEMICOLON, SP.MAYBE, AR.ANY_END), | ||
| 658 | ASD.new(TOKEN_ID.BRACKET_CURLY_RIGHT) | ||
| 659 | ] | ||
| 660 | |||
| 661 | var TEMPLATE_MESSAGE_HEAD : Array = [ | ||
| 662 | Callable(self, "desc_message_head"), | ||
| 663 | ASD.new(TOKEN_ID.MESSAGE, SP.MUST), | ||
| 664 | ASD.new(TOKEN_ID.IDENT, SP.MAYBE, AR.MUST_ONE, true), | ||
| 665 | ASD.new(TOKEN_ID.BRACKET_CURLY_LEFT) | ||
| 666 | ] | ||
| 667 | |||
| 668 | var TEMPLATE_MESSAGE_TAIL : Array = [ | ||
| 669 | Callable(self, "desc_message_tail"), | ||
| 670 | ASD.new(TOKEN_ID.BRACKET_CURLY_RIGHT) | ||
| 671 | ] | ||
| 672 | |||
| 673 | var TEMPLATE_ONEOF_HEAD : Array = [ | ||
| 674 | Callable(self, "desc_oneof_head"), | ||
| 675 | ASD.new(TOKEN_ID.ONEOF, SP.MUST), | ||
| 676 | ASD.new(TOKEN_ID.IDENT, SP.MAYBE, AR.MUST_ONE, true), | ||
| 677 | ASD.new(TOKEN_ID.BRACKET_CURLY_LEFT), | ||
| 678 | ] | ||
| 679 | |||
| 680 | var TEMPLATE_ONEOF_TAIL : Array = [ | ||
| 681 | Callable(self, "desc_oneof_tail"), | ||
| 682 | ASD.new(TOKEN_ID.BRACKET_CURLY_RIGHT) | ||
| 683 | ] | ||
| 684 | |||
| 685 | var TEMPLATE_BEGIN : Array = [ | ||
| 686 | null, | ||
| 687 | ASD.new(TOKEN_ID.SPACE, SP.NO, AR.MAYBE) | ||
| 688 | ] | ||
| 689 | |||
| 690 | var TEMPLATE_END : Array = [ | ||
| 691 | null | ||
| 692 | ] | ||
| 693 | |||
| 694 | func get_token_id(tokens : Array, index : int) -> int: | ||
| 695 | if index < tokens.size(): | ||
| 696 | return tokens[index].id | ||
| 697 | return TOKEN_ID.UNDEFINED | ||
| 698 | |||
| 699 | enum COMPARE_STATE { | ||
| 700 | DONE = 0, | ||
| 701 | MISMATCH = 1, | ||
| 702 | INCOMPLETE = 2, | ||
| 703 | ERROR_VALUE = 3 | ||
| 704 | } | ||
| 705 | |||
| 706 | class TokenCompare: | ||
| 707 | func _init(s : int, i : int, d : String = ""): | ||
| 708 | state = s | ||
| 709 | index = i | ||
| 710 | description = d | ||
| 711 | var state : int | ||
| 712 | var index : int | ||
| 713 | var description : String | ||
| 714 | |||
| 715 | func check_space(tokens : Array, index : int, space) -> int: | ||
| 716 | if get_token_id(tokens, index) == TOKEN_ID.SPACE: | ||
| 717 | if space == SP.MAYBE: | ||
| 718 | return 1 | ||
| 719 | elif space == SP.MUST: | ||
| 720 | return 1 | ||
| 721 | elif space == SP.NO: | ||
| 722 | return -1 | ||
| 723 | else: | ||
| 724 | if space == SP.MUST: | ||
| 725 | return -2 | ||
| 726 | return 0 | ||
| 727 | |||
| 728 | class IndexedToken: | ||
| 729 | func _init(t : TokenEntrance, i : int): | ||
| 730 | token = t | ||
| 731 | index = i | ||
| 732 | var token : TokenEntrance | ||
| 733 | var index : int | ||
| 734 | |||
| 735 | func token_importance_checkadd(template : ASD, token : TokenEntrance, index : int, importance : Array) -> void: | ||
| 736 | if template.importance: | ||
| 737 | importance.append(IndexedToken.new(token, index)) | ||
| 738 | |||
| 739 | class CompareSettings: | ||
| 740 | func _init(ci : int, n : int, pi : int, pn : String = ""): | ||
| 741 | construction_index = ci | ||
| 742 | nesting = n | ||
| 743 | parent_index = pi | ||
| 744 | parent_name = pn | ||
| 745 | |||
| 746 | var construction_index : int | ||
| 747 | var nesting : int | ||
| 748 | var parent_index : int | ||
| 749 | var parent_name : String | ||
| 750 | |||
| 751 | func description_compare(template : Array, tokens : Array, index : int, settings : CompareSettings) -> TokenCompare: | ||
| 752 | var j : int = index | ||
| 753 | var space : int | ||
| 754 | var rule : int | ||
| 755 | var rule_flag : bool | ||
| 756 | var cont : bool | ||
| 757 | var check : int | ||
| 758 | var maybe_group_skip : bool = false | ||
| 759 | var any_group_index : int = -1 | ||
| 760 | var any_end_group_index : int = -1 | ||
| 761 | var i : int = 0 | ||
| 762 | var importance : Array = [] | ||
| 763 | while true: | ||
| 764 | i += 1 | ||
| 765 | if i >= template.size(): | ||
| 766 | break | ||
| 767 | rule_flag = false | ||
| 768 | cont = false | ||
| 769 | rule = template[i].rule | ||
| 770 | space = template[i].space | ||
| 771 | if rule == AR.MAYBE_END && maybe_group_skip: | ||
| 772 | maybe_group_skip = false | ||
| 773 | continue | ||
| 774 | if maybe_group_skip: | ||
| 775 | continue | ||
| 776 | if rule == AR.MAYBE: | ||
| 777 | if template[i].token == get_token_id(tokens, j): | ||
| 778 | token_importance_checkadd(template[i], tokens[j], j, importance) | ||
| 779 | rule_flag = true | ||
| 780 | else: | ||
| 781 | continue | ||
| 782 | elif rule == AR.MUST_ONE || rule == AR.MAYBE_END || rule == AR.ANY_END: | ||
| 783 | if template[i].token == get_token_id(tokens, j): | ||
| 784 | token_importance_checkadd(template[i], tokens[j], j, importance) | ||
| 785 | rule_flag = true | ||
| 786 | elif rule == AR.ANY: | ||
| 787 | var find_any : bool = false | ||
| 788 | while true: | ||
| 789 | if template[i].token == get_token_id(tokens, j): | ||
| 790 | token_importance_checkadd(template[i], tokens[j], j, importance) | ||
| 791 | find_any = true | ||
| 792 | j += 1 | ||
| 793 | check = check_space(tokens, j, space) | ||
| 794 | if check < 0: | ||
| 795 | return TokenCompare.new(COMPARE_STATE.INCOMPLETE, j) | ||
| 796 | else: | ||
| 797 | j += check | ||
| 798 | else: | ||
| 799 | if find_any: | ||
| 800 | cont = true | ||
| 801 | break | ||
| 802 | elif rule == AR.OR: | ||
| 803 | var or_tokens = template[i].token | ||
| 804 | for v in or_tokens: | ||
| 805 | if v == get_token_id(tokens, j): | ||
| 806 | token_importance_checkadd(template[i], tokens[j], j, importance) | ||
| 807 | j += 1 | ||
| 808 | check = check_space(tokens, j, space) | ||
| 809 | if check < 0: | ||
| 810 | return TokenCompare.new(COMPARE_STATE.INCOMPLETE, j) | ||
| 811 | else: | ||
| 812 | j += check | ||
| 813 | cont = true | ||
| 814 | break | ||
| 815 | elif rule == AR.MAYBE_BEGIN: | ||
| 816 | if template[i].token == get_token_id(tokens, j): | ||
| 817 | token_importance_checkadd(template[i], tokens[j], j, importance) | ||
| 818 | rule_flag = true | ||
| 819 | else: | ||
| 820 | maybe_group_skip = true | ||
| 821 | continue | ||
| 822 | elif rule == AR.ANY_BEGIN: | ||
| 823 | if template[i].token == get_token_id(tokens, j): | ||
| 824 | token_importance_checkadd(template[i], tokens[j], j, importance) | ||
| 825 | rule_flag = true | ||
| 826 | any_group_index = i | ||
| 827 | else: | ||
| 828 | if any_end_group_index > 0: | ||
| 829 | any_group_index = -1 | ||
| 830 | i = any_end_group_index | ||
| 831 | any_end_group_index = -1 | ||
| 832 | continue | ||
| 833 | if cont: | ||
| 834 | continue | ||
| 835 | if rule_flag: | ||
| 836 | j += 1 | ||
| 837 | check = check_space(tokens, j, space) | ||
| 838 | if check < 0: | ||
| 839 | return TokenCompare.new(COMPARE_STATE.INCOMPLETE, j) | ||
| 840 | else: | ||
| 841 | j += check | ||
| 842 | else: | ||
| 843 | if j > index: | ||
| 844 | return TokenCompare.new(COMPARE_STATE.INCOMPLETE, j) | ||
| 845 | else: | ||
| 846 | return TokenCompare.new(COMPARE_STATE.MISMATCH, j) | ||
| 847 | if any_group_index >= 0 && rule == AR.ANY_END: | ||
| 848 | any_end_group_index = i | ||
| 849 | i = any_group_index - 1 | ||
| 850 | if template[0] != null: | ||
| 851 | var result : DescriptionResult = template[0].call(importance, settings) | ||
| 852 | if !result.success: | ||
| 853 | return TokenCompare.new(COMPARE_STATE.ERROR_VALUE, result.error, result.description) | ||
| 854 | return TokenCompare.new(COMPARE_STATE.DONE, j) | ||
| 855 | |||
| 856 | var DESCRIPTION : Array = [ | ||
| 857 | TEMPLATE_BEGIN, #0 | ||
| 858 | TEMPLATE_SYNTAX, #1 | ||
| 859 | TEMPLATE_IMPORT, #2 | ||
| 860 | TEMPLATE_PACKAGE, #3 | ||
| 861 | TEMPLATE_OPTION, #4 | ||
| 862 | TEMPLATE_FIELD, #5 | ||
| 863 | TEMPLATE_FIELD_ONEOF, #6 | ||
| 864 | TEMPLATE_MAP_FIELD, #7 | ||
| 865 | TEMPLATE_MAP_FIELD_ONEOF, #8 | ||
| 866 | TEMPLATE_ENUM, #9 | ||
| 867 | TEMPLATE_MESSAGE_HEAD, #10 | ||
| 868 | TEMPLATE_MESSAGE_TAIL, #11 | ||
| 869 | TEMPLATE_ONEOF_HEAD, #12 | ||
| 870 | TEMPLATE_ONEOF_TAIL, #13 | ||
| 871 | TEMPLATE_END #14 | ||
| 872 | ] | ||
| 873 | |||
| 874 | enum JUMP { | ||
| 875 | NOTHING = 0, #nothing | ||
| 876 | SIMPLE = 1, #simple jump | ||
| 877 | NESTED_INCREMENT = 2, #nested increment | ||
| 878 | NESTED_DECREMENT = 3, #nested decrement | ||
| 879 | MUST_NESTED_SIMPLE = 4, #check: must be nested > 0 | ||
| 880 | MUST_NESTED_INCREMENT = 5, #check: must be nested > 0, then nested increment | ||
| 881 | MUST_NESTED_DECREMENT = 6, #nested decrement, then check: must be nested > 0 | ||
| 882 | } | ||
| 883 | |||
| 884 | var TRANSLATION_TABLE : Array = [ | ||
| 885 | # BEGIN SYNTAX IMPORT PACKAGE OPTION FIELD FIELD_O MAP_F MAP_F_O ENUM MES_H MES_T ONEOF_H ONEOF_T END | ||
| 886 | [ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], #BEGIN | ||
| 887 | [ 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1], #SYNTAX | ||
| 888 | [ 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1], #IMPORT | ||
| 889 | [ 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1], #PACKAGE | ||
| 890 | [ 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1], #OPTION | ||
| 891 | [ 0, 0, 0, 0, 0, 4, 0, 4, 0, 1, 2, 3, 5, 0, 0], #FIELD | ||
| 892 | [ 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 6, 0], #FIELD_ONEOF | ||
| 893 | [ 0, 0, 0, 0, 0, 4, 0, 4, 0, 1, 2, 3, 5, 0, 0], #MAP_F | ||
| 894 | [ 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 6, 0], #MAP_F_ONEOF | ||
| 895 | [ 0, 0, 0, 0, 0, 4, 0, 4, 0, 1, 2, 3, 5, 0, 1], #ENUM | ||
| 896 | [ 0, 0, 0, 0, 0, 4, 0, 4, 0, 1, 2, 3, 5, 0, 0], #MES_H | ||
| 897 | [ 0, 0, 0, 0, 0, 4, 0, 4, 0, 1, 2, 3, 5, 0, 1], #MES_T | ||
| 898 | [ 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 0, 0], #ONEOF_H | ||
| 899 | [ 0, 0, 0, 0, 0, 4, 0, 4, 0, 1, 2, 3, 5, 0, 1], #ONEOF_T | ||
| 900 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] #END | ||
| 901 | ] | ||
| 902 | |||
| 903 | class Construction: | ||
| 904 | func _init(b : int, e : int, d : int): | ||
| 905 | begin_token_index = b | ||
| 906 | end_token_index = e | ||
| 907 | description = d | ||
| 908 | var begin_token_index : int | ||
| 909 | var end_token_index : int | ||
| 910 | var description : int | ||
| 911 | |||
| 912 | class TranslationResult: | ||
| 913 | var constructions : Array = [] | ||
| 914 | var done : bool = false | ||
| 915 | var error_description_id : int = -1 | ||
| 916 | var error_description_text : String = "" | ||
| 917 | var parse_token_index : int = 0 | ||
| 918 | var error_token_index : int = 0 | ||
| 919 | |||
| 920 | func analyze_tokens(tokens : Array) -> TranslationResult: | ||
| 921 | var i : int = 0 | ||
| 922 | var result : TranslationResult = TranslationResult.new() | ||
| 923 | var comp : TokenCompare | ||
| 924 | var cur_template_id : int = 0 | ||
| 925 | var error : bool = false | ||
| 926 | var template_index : int | ||
| 927 | var comp_set : CompareSettings = CompareSettings.new(result.constructions.size(), 0, -1) | ||
| 928 | comp = description_compare(DESCRIPTION[cur_template_id], tokens, i, comp_set) | ||
| 929 | if comp.state == COMPARE_STATE.DONE: | ||
| 930 | i = comp.index | ||
| 931 | while true: | ||
| 932 | var end : bool = true | ||
| 933 | var find : bool = false | ||
| 934 | for j in range(TRANSLATION_TABLE[cur_template_id].size()): | ||
| 935 | template_index = j | ||
| 936 | if j == DESCRIPTION.size() - 1 && i < tokens.size(): | ||
| 937 | end = false | ||
| 938 | if result.error_description_id < 0: | ||
| 939 | error = true | ||
| 940 | break | ||
| 941 | if TRANSLATION_TABLE[cur_template_id][j] > 0: | ||
| 942 | end = false | ||
| 943 | comp_set.construction_index = result.constructions.size() | ||
| 944 | comp = description_compare(DESCRIPTION[j], tokens, i, comp_set) | ||
| 945 | if comp.state == COMPARE_STATE.DONE: | ||
| 946 | if TRANSLATION_TABLE[cur_template_id][j] == JUMP.NESTED_INCREMENT: | ||
| 947 | comp_set.nesting += 1 | ||
| 948 | elif TRANSLATION_TABLE[cur_template_id][j] == JUMP.NESTED_DECREMENT: | ||
| 949 | comp_set.nesting -= 1 | ||
| 950 | if comp_set.nesting < 0: | ||
| 951 | error = true | ||
| 952 | break | ||
| 953 | elif TRANSLATION_TABLE[cur_template_id][j] == JUMP.MUST_NESTED_SIMPLE: | ||
| 954 | if comp_set.nesting <= 0: | ||
| 955 | error = true | ||
| 956 | break | ||
| 957 | elif TRANSLATION_TABLE[cur_template_id][j] == JUMP.MUST_NESTED_INCREMENT: | ||
| 958 | if comp_set.nesting <= 0: | ||
| 959 | error = true | ||
| 960 | break | ||
| 961 | comp_set.nesting += 1 | ||
| 962 | elif TRANSLATION_TABLE[cur_template_id][j] == JUMP.MUST_NESTED_DECREMENT: | ||
| 963 | comp_set.nesting -= 1 | ||
| 964 | if comp_set.nesting <= 0: | ||
| 965 | error = true | ||
| 966 | break | ||
| 967 | result.constructions.append(Construction.new(i, comp.index, j)) | ||
| 968 | find = true | ||
| 969 | i = comp.index | ||
| 970 | cur_template_id = j | ||
| 971 | if i == tokens.size(): | ||
| 972 | if TRANSLATION_TABLE[cur_template_id][DESCRIPTION.size() - 1] == JUMP.SIMPLE: | ||
| 973 | if comp_set.nesting == 0: | ||
| 974 | end = true | ||
| 975 | else: | ||
| 976 | error = true | ||
| 977 | else: | ||
| 978 | error = true | ||
| 979 | elif i > tokens.size(): | ||
| 980 | error = true | ||
| 981 | break | ||
| 982 | elif comp.state == COMPARE_STATE.INCOMPLETE: | ||
| 983 | error = true | ||
| 984 | break | ||
| 985 | elif comp.state == COMPARE_STATE.ERROR_VALUE: | ||
| 986 | error = true | ||
| 987 | break | ||
| 988 | if error: | ||
| 989 | result.error_description_text = comp.description | ||
| 990 | result.error_description_id = template_index | ||
| 991 | result.parse_token_index = i | ||
| 992 | if comp.index >= tokens.size(): | ||
| 993 | result.error_token_index = tokens.size() - 1 | ||
| 994 | else: | ||
| 995 | result.error_token_index = comp.index | ||
| 996 | if end: | ||
| 997 | result.done = true | ||
| 998 | result.error_description_id = -1 | ||
| 999 | break | ||
| 1000 | if !find: | ||
| 1001 | break | ||
| 1002 | return result | ||
| 1003 | |||
| 1004 | enum CLASS_TYPE { | ||
| 1005 | ENUM = 0, | ||
| 1006 | MESSAGE = 1, | ||
| 1007 | MAP = 2 | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | enum FIELD_TYPE { | ||
| 1011 | UNDEFINED = -1, | ||
| 1012 | INT32 = 0, | ||
| 1013 | SINT32 = 1, | ||
| 1014 | UINT32 = 2, | ||
| 1015 | INT64 = 3, | ||
| 1016 | SINT64 = 4, | ||
| 1017 | UINT64 = 5, | ||
| 1018 | BOOL = 6, | ||
| 1019 | ENUM = 7, | ||
| 1020 | FIXED32 = 8, | ||
| 1021 | SFIXED32 = 9, | ||
| 1022 | FLOAT = 10, | ||
| 1023 | FIXED64 = 11, | ||
| 1024 | SFIXED64 = 12, | ||
| 1025 | DOUBLE = 13, | ||
| 1026 | STRING = 14, | ||
| 1027 | BYTES = 15, | ||
| 1028 | MESSAGE = 16, | ||
| 1029 | MAP = 17 | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | enum FIELD_QUALIFICATOR { | ||
| 1033 | OPTIONAL = 0, | ||
| 1034 | REQUIRED = 1, | ||
| 1035 | REPEATED = 2, | ||
| 1036 | RESERVED = 3 | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | enum FIELD_OPTION { | ||
| 1040 | PACKED = 0, | ||
| 1041 | NOT_PACKED = 1 | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | class ASTClass: | ||
| 1045 | func _init(n : String, t : int, p : int, pn : String, o : String, ci : int): | ||
| 1046 | name = n | ||
| 1047 | type = t | ||
| 1048 | parent_index = p | ||
| 1049 | parent_name = pn | ||
| 1050 | option = o | ||
| 1051 | construction_index = ci | ||
| 1052 | values = [] | ||
| 1053 | |||
| 1054 | var name : String | ||
| 1055 | var type : int | ||
| 1056 | var parent_index : int | ||
| 1057 | var parent_name : String | ||
| 1058 | var option : String | ||
| 1059 | var construction_index | ||
| 1060 | var values : Array | ||
| 1061 | |||
| 1062 | func copy() -> ASTClass: | ||
| 1063 | var res : ASTClass = ASTClass.new(name, type, parent_index, parent_name, option, construction_index) | ||
| 1064 | for v in values: | ||
| 1065 | res.values.append(v.copy()) | ||
| 1066 | return res | ||
| 1067 | |||
| 1068 | class ASTEnumValue: | ||
| 1069 | func _init(n : String, v : String): | ||
| 1070 | name = n | ||
| 1071 | value = v | ||
| 1072 | |||
| 1073 | var name : String | ||
| 1074 | var value : String | ||
| 1075 | |||
| 1076 | func copy() -> ASTEnumValue: | ||
| 1077 | return ASTEnumValue.new(name, value) | ||
| 1078 | |||
| 1079 | class ASTField: | ||
| 1080 | func _init(t, n : String, tn : String, p : int, q : int, o : int, ci : int, mf : bool): | ||
| 1081 | tag = t | ||
| 1082 | name = n | ||
| 1083 | type_name = tn | ||
| 1084 | parent_class_id = p | ||
| 1085 | qualificator = q | ||
| 1086 | option = o | ||
| 1087 | construction_index = ci | ||
| 1088 | is_map_field = mf | ||
| 1089 | |||
| 1090 | var tag | ||
| 1091 | var name : String | ||
| 1092 | var type_name : String | ||
| 1093 | var parent_class_id : int | ||
| 1094 | var qualificator : int | ||
| 1095 | var option : int | ||
| 1096 | var construction_index : int | ||
| 1097 | var is_map_field : bool | ||
| 1098 | var field_type : int = FIELD_TYPE.UNDEFINED | ||
| 1099 | var type_class_id : int = -1 | ||
| 1100 | |||
| 1101 | func copy() -> ASTField: | ||
| 1102 | var res : ASTField = ASTField.new(tag, name, type_name, parent_class_id, qualificator, option, construction_index, is_map_field) | ||
| 1103 | res.field_type = field_type | ||
| 1104 | res.type_class_id = type_class_id | ||
| 1105 | return res | ||
| 1106 | |||
| 1107 | enum AST_GROUP_RULE { | ||
| 1108 | ONEOF = 0, | ||
| 1109 | ALL = 1 | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | class ASTFieldGroup: | ||
| 1113 | func _init(n : String, pi : int, r : int): | ||
| 1114 | name = n | ||
| 1115 | parent_class_id = pi | ||
| 1116 | rule = r | ||
| 1117 | opened = true | ||
| 1118 | |||
| 1119 | var name : String | ||
| 1120 | var parent_class_id : int | ||
| 1121 | var rule : int | ||
| 1122 | var field_indexes : Array = [] | ||
| 1123 | var opened : bool | ||
| 1124 | |||
| 1125 | func copy() -> ASTFieldGroup: | ||
| 1126 | var res : ASTFieldGroup = ASTFieldGroup.new(name, parent_class_id, rule) | ||
| 1127 | res.opened = opened | ||
| 1128 | for fi in field_indexes: | ||
| 1129 | res.field_indexes.append(fi) | ||
| 1130 | return res | ||
| 1131 | |||
| 1132 | class ASTImport: | ||
| 1133 | func _init(a_path : String, a_public : bool, sha : String): | ||
| 1134 | path = a_path | ||
| 1135 | public = a_public | ||
| 1136 | sha256 = sha | ||
| 1137 | |||
| 1138 | var path : String | ||
| 1139 | var public : bool | ||
| 1140 | var sha256 : String | ||
| 1141 | |||
| 1142 | var class_table : Array = [] | ||
| 1143 | var field_table : Array = [] | ||
| 1144 | var group_table : Array = [] | ||
| 1145 | var import_table : Array = [] | ||
| 1146 | var proto_version : int = 0 | ||
| 1147 | |||
| 1148 | class DescriptionResult: | ||
| 1149 | func _init(s : bool = true, e = null, d : String = ""): | ||
| 1150 | success = s | ||
| 1151 | error = e | ||
| 1152 | description = d | ||
| 1153 | var success : bool | ||
| 1154 | var error | ||
| 1155 | var description : String | ||
| 1156 | |||
| 1157 | static func get_text_from_token(string_token : TokenEntrance) -> String: | ||
| 1158 | return string_token.text.substr(1, string_token.text.length() - 2) | ||
| 1159 | |||
| 1160 | func desc_syntax(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1161 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1162 | var s : String = get_text_from_token(indexed_tokens[0].token) | ||
| 1163 | if s == "proto2": | ||
| 1164 | proto_version = 2 | ||
| 1165 | elif s == "proto3": | ||
| 1166 | proto_version = 3 | ||
| 1167 | else: | ||
| 1168 | result.success = false | ||
| 1169 | result.error = indexed_tokens[0].index | ||
| 1170 | result.description = "Unspecified version of the protocol. Use \"proto2\" or \"proto3\" syntax string." | ||
| 1171 | return result | ||
| 1172 | |||
| 1173 | func desc_import(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1174 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1175 | var offset : int = 0 | ||
| 1176 | var public : bool = false | ||
| 1177 | if indexed_tokens[offset].token.id == TOKEN_ID.IMPORT_QUALIFICATION: | ||
| 1178 | if indexed_tokens[offset].token.text == "public": | ||
| 1179 | public = true | ||
| 1180 | offset += 1 | ||
| 1181 | var f_name : String = path_dir + get_text_from_token(indexed_tokens[offset].token) | ||
| 1182 | var sha : String = FileAccess.get_sha256(f_name) | ||
| 1183 | if FileAccess.file_exists(f_name): | ||
| 1184 | for i in import_table: | ||
| 1185 | if i.path == f_name: | ||
| 1186 | result.success = false | ||
| 1187 | result.error = indexed_tokens[offset].index | ||
| 1188 | result.description = "File '" + f_name + "' already imported." | ||
| 1189 | return result | ||
| 1190 | if i.sha256 == sha: | ||
| 1191 | result.success = false | ||
| 1192 | result.error = indexed_tokens[offset].index | ||
| 1193 | result.description = "File '" + f_name + "' with matching SHA256 already imported." | ||
| 1194 | return result | ||
| 1195 | import_table.append(ASTImport.new(f_name, public, sha)) | ||
| 1196 | else: | ||
| 1197 | result.success = false | ||
| 1198 | result.error = indexed_tokens[offset].index | ||
| 1199 | result.description = "Import file '" + f_name + "' not found." | ||
| 1200 | return result | ||
| 1201 | |||
| 1202 | func desc_package(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1203 | printerr("UNRELEASED desc_package: ", indexed_tokens.size(), ", nesting: ", settings.nesting) | ||
| 1204 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1205 | return result | ||
| 1206 | |||
| 1207 | func desc_option(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1208 | printerr("UNRELEASED desc_option: ", indexed_tokens.size(), ", nesting: ", settings.nesting) | ||
| 1209 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1210 | return result | ||
| 1211 | |||
| 1212 | func desc_field(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1213 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1214 | var qualifcator : int = FIELD_QUALIFICATOR.OPTIONAL | ||
| 1215 | var option : int | ||
| 1216 | var offset : int = 0 | ||
| 1217 | |||
| 1218 | if proto_version == 3: | ||
| 1219 | option = FIELD_OPTION.PACKED | ||
| 1220 | if indexed_tokens[offset].token.id == TOKEN_ID.FIELD_QUALIFICATION: | ||
| 1221 | if indexed_tokens[offset].token.text == "repeated": | ||
| 1222 | qualifcator = FIELD_QUALIFICATOR.REPEATED | ||
| 1223 | elif indexed_tokens[offset].token.text == "required" || indexed_tokens[offset].token.text == "optional": | ||
| 1224 | result.success = false | ||
| 1225 | result.error = indexed_tokens[offset].index | ||
| 1226 | result.description = "Using the 'required' or 'optional' qualificator is unacceptable in Protobuf v3." | ||
| 1227 | return result | ||
| 1228 | offset += 1 | ||
| 1229 | if proto_version == 2: | ||
| 1230 | option = FIELD_OPTION.NOT_PACKED | ||
| 1231 | if !(group_table.size() > 0 && group_table[group_table.size() - 1].opened): | ||
| 1232 | if indexed_tokens[offset].token.id == TOKEN_ID.FIELD_QUALIFICATION: | ||
| 1233 | if indexed_tokens[offset].token.text == "repeated": | ||
| 1234 | qualifcator = FIELD_QUALIFICATOR.REPEATED | ||
| 1235 | elif indexed_tokens[offset].token.text == "required": | ||
| 1236 | qualifcator = FIELD_QUALIFICATOR.REQUIRED | ||
| 1237 | elif indexed_tokens[offset].token.text == "optional": | ||
| 1238 | qualifcator = FIELD_QUALIFICATOR.OPTIONAL | ||
| 1239 | offset += 1 | ||
| 1240 | else: | ||
| 1241 | if class_table[settings.parent_index].type == CLASS_TYPE.MESSAGE: | ||
| 1242 | result.success = false | ||
| 1243 | result.error = indexed_tokens[offset].index | ||
| 1244 | result.description = "Using the 'required', 'optional' or 'repeated' qualificator necessarily in Protobuf v2." | ||
| 1245 | return result | ||
| 1246 | var type_name : String = indexed_tokens[offset].token.text; offset += 1 | ||
| 1247 | var field_name : String = indexed_tokens[offset].token.text; offset += 1 | ||
| 1248 | var tag : String = indexed_tokens[offset].token.text; offset += 1 | ||
| 1249 | |||
| 1250 | if indexed_tokens.size() == offset + 2: | ||
| 1251 | if indexed_tokens[offset].token.text == "packed": | ||
| 1252 | offset += 1 | ||
| 1253 | if indexed_tokens[offset].token.text == "true": | ||
| 1254 | option = FIELD_OPTION.PACKED | ||
| 1255 | else: | ||
| 1256 | option = FIELD_OPTION.NOT_PACKED | ||
| 1257 | else: | ||
| 1258 | result.success = false | ||
| 1259 | result.error = indexed_tokens[offset].index | ||
| 1260 | result.description = "Undefined field option." | ||
| 1261 | return result | ||
| 1262 | |||
| 1263 | if group_table.size() > 0: | ||
| 1264 | if group_table[group_table.size() - 1].opened: | ||
| 1265 | if indexed_tokens[0].token.id == TOKEN_ID.FIELD_QUALIFICATION: | ||
| 1266 | result.success = false | ||
| 1267 | result.error = indexed_tokens[0].index | ||
| 1268 | result.description = "Using the 'required', 'optional' or 'repeated' qualificator is unacceptable in 'OneOf' field." | ||
| 1269 | return result | ||
| 1270 | group_table[group_table.size() - 1].field_indexes.append(field_table.size()) | ||
| 1271 | field_table.append(ASTField.new(tag, field_name, type_name, settings.parent_index, qualifcator, option, settings.construction_index, false)) | ||
| 1272 | return result | ||
| 1273 | |||
| 1274 | func desc_map_field(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1275 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1276 | var qualifcator : int = FIELD_QUALIFICATOR.REPEATED | ||
| 1277 | var option : int | ||
| 1278 | var offset : int = 0 | ||
| 1279 | |||
| 1280 | if proto_version == 3: | ||
| 1281 | option = FIELD_OPTION.PACKED | ||
| 1282 | if proto_version == 2: | ||
| 1283 | option = FIELD_OPTION.NOT_PACKED | ||
| 1284 | |||
| 1285 | var key_type_name : String = indexed_tokens[offset].token.text; offset += 1 | ||
| 1286 | if key_type_name == "float" || key_type_name == "double" || key_type_name == "bytes": | ||
| 1287 | result.success = false | ||
| 1288 | result.error = indexed_tokens[offset - 1].index | ||
| 1289 | result.description = "Map 'key_type' can't be floating point types and bytes." | ||
| 1290 | var type_name : String = indexed_tokens[offset].token.text; offset += 1 | ||
| 1291 | var field_name : String = indexed_tokens[offset].token.text; offset += 1 | ||
| 1292 | var tag : String = indexed_tokens[offset].token.text; offset += 1 | ||
| 1293 | |||
| 1294 | if indexed_tokens.size() == offset + 2: | ||
| 1295 | if indexed_tokens[offset].token.text == "packed": | ||
| 1296 | offset += 1 | ||
| 1297 | if indexed_tokens[offset] == "true": | ||
| 1298 | option = FIELD_OPTION.PACKED | ||
| 1299 | else: | ||
| 1300 | option = FIELD_OPTION.NOT_PACKED | ||
| 1301 | else: | ||
| 1302 | result.success = false | ||
| 1303 | result.error = indexed_tokens[offset].index | ||
| 1304 | result.description = "Undefined field option." | ||
| 1305 | |||
| 1306 | if group_table.size() > 0: | ||
| 1307 | if group_table[group_table.size() - 1].opened: | ||
| 1308 | group_table[group_table.size() - 1].field_indexes.append(field_table.size()) | ||
| 1309 | |||
| 1310 | class_table.append(ASTClass.new("map_type_" + field_name, CLASS_TYPE.MAP, settings.parent_index, settings.parent_name, "", settings.construction_index)) | ||
| 1311 | field_table.append(ASTField.new(tag, field_name, "map_type_" + field_name, settings.parent_index, qualifcator, option, settings.construction_index, false)) | ||
| 1312 | |||
| 1313 | field_table.append(ASTField.new(1, "key", key_type_name, class_table.size() - 1, FIELD_QUALIFICATOR.OPTIONAL, option, settings.construction_index, true)) | ||
| 1314 | field_table.append(ASTField.new(2, "value", type_name, class_table.size() - 1, FIELD_QUALIFICATOR.OPTIONAL, option, settings.construction_index, true)) | ||
| 1315 | |||
| 1316 | return result | ||
| 1317 | |||
| 1318 | func desc_enum(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1319 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1320 | var option : String = "" | ||
| 1321 | var offset : int = 0 | ||
| 1322 | var type_name : String = indexed_tokens[offset].token.text; offset += 1 | ||
| 1323 | if indexed_tokens[offset].token.id == TOKEN_ID.ENUM_OPTION: | ||
| 1324 | if indexed_tokens[offset].token.text == "allow_alias" && indexed_tokens[offset + 1].token.text == "true": | ||
| 1325 | option = "allow_alias" | ||
| 1326 | offset += 2 | ||
| 1327 | var value : ASTEnumValue | ||
| 1328 | var enum_class : ASTClass = ASTClass.new(type_name, CLASS_TYPE.ENUM, settings.parent_index, settings.parent_name, option, settings.construction_index) | ||
| 1329 | var first_value : bool = true | ||
| 1330 | while offset < indexed_tokens.size(): | ||
| 1331 | if first_value: | ||
| 1332 | if indexed_tokens[offset + 1].token.text != "0": | ||
| 1333 | result.success = false | ||
| 1334 | result.error = indexed_tokens[offset + 1].index | ||
| 1335 | result.description = "For Enums, the default value is the first defined enum value, which must be 0." | ||
| 1336 | break | ||
| 1337 | first_value = false | ||
| 1338 | #if indexed_tokens[offset + 1].token.text[0] == "+" || indexed_tokens[offset + 1].token.text[0] == "-": | ||
| 1339 | # result.success = false | ||
| 1340 | # result.error = indexed_tokens[offset + 1].index | ||
| 1341 | # result.description = "For Enums, signed values are not allowed." | ||
| 1342 | # break | ||
| 1343 | value = ASTEnumValue.new(indexed_tokens[offset].token.text, indexed_tokens[offset + 1].token.text) | ||
| 1344 | enum_class.values.append(value) | ||
| 1345 | offset += 2 | ||
| 1346 | |||
| 1347 | class_table.append(enum_class) | ||
| 1348 | return result | ||
| 1349 | |||
| 1350 | func desc_message_head(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1351 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1352 | class_table.append(ASTClass.new(indexed_tokens[0].token.text, CLASS_TYPE.MESSAGE, settings.parent_index, settings.parent_name, "", settings.construction_index)) | ||
| 1353 | settings.parent_index = class_table.size() - 1 | ||
| 1354 | settings.parent_name = settings.parent_name + "." + indexed_tokens[0].token.text | ||
| 1355 | return result | ||
| 1356 | |||
| 1357 | func desc_message_tail(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1358 | settings.parent_index = class_table[settings.parent_index].parent_index | ||
| 1359 | settings.parent_name = class_table[settings.parent_index + 1].parent_name | ||
| 1360 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1361 | return result | ||
| 1362 | |||
| 1363 | func desc_oneof_head(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1364 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1365 | for g in group_table: | ||
| 1366 | if g.parent_class_id == settings.parent_index && g.name == indexed_tokens[0].token.text: | ||
| 1367 | result.success = false | ||
| 1368 | result.error = indexed_tokens[0].index | ||
| 1369 | result.description = "OneOf name must be unique." | ||
| 1370 | return result | ||
| 1371 | group_table.append(ASTFieldGroup.new(indexed_tokens[0].token.text, settings.parent_index, AST_GROUP_RULE.ONEOF)) | ||
| 1372 | return result | ||
| 1373 | |||
| 1374 | func desc_oneof_tail(indexed_tokens : Array, settings : CompareSettings) -> DescriptionResult: | ||
| 1375 | group_table[group_table.size() - 1].opened = false | ||
| 1376 | var result : DescriptionResult = DescriptionResult.new() | ||
| 1377 | return result | ||
| 1378 | |||
| 1379 | func analyze() -> AnalyzeResult: | ||
| 1380 | var analyze_result : AnalyzeResult = AnalyzeResult.new() | ||
| 1381 | analyze_result.doc = document | ||
| 1382 | analyze_result.classes = class_table | ||
| 1383 | analyze_result.fields = field_table | ||
| 1384 | analyze_result.groups = group_table | ||
| 1385 | analyze_result.state = false | ||
| 1386 | var result : TokenResult = tokenizer() | ||
| 1387 | if result.errors.size() > 0: | ||
| 1388 | for v in result.errors: | ||
| 1389 | var spos : Helper.StringPosition = Helper.str_pos(document.text, v.position) | ||
| 1390 | var err_text : String = "Unexpected token intersection " + "'" + document.text.substr(v.position.begin, spos.length) + "'" | ||
| 1391 | printerr(Helper.error_string(document.name, spos.str_num, spos.column, err_text)) | ||
| 1392 | else: | ||
| 1393 | var integrity = check_tokens_integrity(result.tokens, document.text.length() - 1) | ||
| 1394 | if integrity.size() > 0: | ||
| 1395 | for v in integrity: | ||
| 1396 | var spos: Helper.StringPosition = Helper.str_pos(document.text, TokenPosition.new(v.begin, v.end)) | ||
| 1397 | var err_text : String = "Unexpected token " + "'" + document.text.substr(v.begin, spos.length) + "'" | ||
| 1398 | printerr(Helper.error_string(document.name, spos.str_num, spos.column, err_text)) | ||
| 1399 | else: | ||
| 1400 | analyze_result.tokens = result.tokens | ||
| 1401 | comment_space_processing(result.tokens) | ||
| 1402 | var syntax : TranslationResult = analyze_tokens(result.tokens) | ||
| 1403 | if !syntax.done: | ||
| 1404 | var pos_main : TokenPosition = Helper.text_pos(result.tokens, syntax.parse_token_index) | ||
| 1405 | var pos_inner : TokenPosition = Helper.text_pos(result.tokens, syntax.error_token_index) | ||
| 1406 | var spos_main : Helper.StringPosition = Helper.str_pos(document.text, pos_main) | ||
| 1407 | var spos_inner : Helper.StringPosition = Helper.str_pos(document.text, pos_inner) | ||
| 1408 | var err_text : String = "Syntax error in construction '" + result.tokens[syntax.parse_token_index].text + "'. " | ||
| 1409 | err_text += "Unacceptable use '" + result.tokens[syntax.error_token_index].text + "' at:" + str(spos_inner.str_num) + ":" + str(spos_inner.column) | ||
| 1410 | err_text += "\n" + syntax.error_description_text | ||
| 1411 | printerr(Helper.error_string(document.name, spos_main.str_num, spos_main.column, err_text)) | ||
| 1412 | else: | ||
| 1413 | analyze_result.version = proto_version | ||
| 1414 | analyze_result.imports = import_table | ||
| 1415 | analyze_result.syntax = syntax | ||
| 1416 | analyze_result.state = true | ||
| 1417 | return analyze_result | ||
| 1418 | |||
| 1419 | class Semantic: | ||
| 1420 | |||
| 1421 | var class_table : Array | ||
| 1422 | var field_table : Array | ||
| 1423 | var group_table : Array | ||
| 1424 | var syntax : Analysis.TranslationResult | ||
| 1425 | var tokens : Array | ||
| 1426 | var document : Document | ||
| 1427 | |||
| 1428 | func _init(analyze_result : AnalyzeResult): | ||
| 1429 | class_table = analyze_result.classes | ||
| 1430 | field_table = analyze_result.fields | ||
| 1431 | group_table = analyze_result.groups | ||
| 1432 | syntax = analyze_result.syntax | ||
| 1433 | tokens = analyze_result.tokens | ||
| 1434 | document = analyze_result.doc | ||
| 1435 | |||
| 1436 | |||
| 1437 | enum CHECK_SUBJECT { | ||
| 1438 | CLASS_NAME = 0, | ||
| 1439 | FIELD_NAME = 1, | ||
| 1440 | FIELD_TAG_NUMBER = 2, | ||
| 1441 | FIELD_TYPE = 3 | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | var STRING_FIELD_TYPE = { | ||
| 1445 | "int32": Analysis.FIELD_TYPE.INT32, | ||
| 1446 | "sint32": Analysis.FIELD_TYPE.SINT32, | ||
| 1447 | "uint32": Analysis.FIELD_TYPE.UINT32, | ||
| 1448 | "int64": Analysis.FIELD_TYPE.INT64, | ||
| 1449 | "sint64": Analysis.FIELD_TYPE.SINT64, | ||
| 1450 | "uint64": Analysis.FIELD_TYPE.UINT64, | ||
| 1451 | "bool": Analysis.FIELD_TYPE.BOOL, | ||
| 1452 | "fixed32": Analysis.FIELD_TYPE.FIXED32, | ||
| 1453 | "sfixed32": Analysis.FIELD_TYPE.SFIXED32, | ||
| 1454 | "float": Analysis.FIELD_TYPE.FLOAT, | ||
| 1455 | "fixed64": Analysis.FIELD_TYPE.FIXED64, | ||
| 1456 | "sfixed64": Analysis.FIELD_TYPE.SFIXED64, | ||
| 1457 | "double": Analysis.FIELD_TYPE.DOUBLE, | ||
| 1458 | "string": Analysis.FIELD_TYPE.STRING, | ||
| 1459 | "bytes": Analysis.FIELD_TYPE.BYTES, | ||
| 1460 | "map": Analysis.FIELD_TYPE.MAP | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | class CheckResult: | ||
| 1464 | func _init(mci : int, aci : int, ti : int, s : int): | ||
| 1465 | main_construction_index = mci | ||
| 1466 | associated_construction_index = aci | ||
| 1467 | table_index = ti | ||
| 1468 | subject = s | ||
| 1469 | |||
| 1470 | var main_construction_index: int = -1 | ||
| 1471 | var associated_construction_index: int = -1 | ||
| 1472 | var table_index: int = -1 | ||
| 1473 | var subject : int | ||
| 1474 | |||
| 1475 | func check_class_names() -> Array: | ||
| 1476 | var result : Array = [] | ||
| 1477 | for i in range(class_table.size()): | ||
| 1478 | var the_class_name : String = class_table[i].parent_name + "." + class_table[i].name | ||
| 1479 | for j in range(i + 1, class_table.size(), 1): | ||
| 1480 | var inner_name : String = class_table[j].parent_name + "." + class_table[j].name | ||
| 1481 | if inner_name == the_class_name: | ||
| 1482 | var check : CheckResult = CheckResult.new(class_table[j].construction_index, class_table[i].construction_index, j, CHECK_SUBJECT.CLASS_NAME) | ||
| 1483 | result.append(check) | ||
| 1484 | break | ||
| 1485 | return result | ||
| 1486 | |||
| 1487 | func check_field_names() -> Array: | ||
| 1488 | var result : Array = [] | ||
| 1489 | for i in range(field_table.size()): | ||
| 1490 | var the_class_name : String = class_table[field_table[i].parent_class_id].parent_name + "." + class_table[field_table[i].parent_class_id].name | ||
| 1491 | for j in range(i + 1, field_table.size(), 1): | ||
| 1492 | var inner_name : String = class_table[field_table[j].parent_class_id].parent_name + "." + class_table[field_table[j].parent_class_id].name | ||
| 1493 | if inner_name == the_class_name: | ||
| 1494 | if field_table[i].name == field_table[j].name: | ||
| 1495 | var check : CheckResult = CheckResult.new(field_table[j].construction_index, field_table[i].construction_index, j, CHECK_SUBJECT.FIELD_NAME) | ||
| 1496 | result.append(check) | ||
| 1497 | break | ||
| 1498 | if field_table[i].tag == field_table[j].tag: | ||
| 1499 | var check : CheckResult = CheckResult.new(field_table[j].construction_index, field_table[i].construction_index, j, CHECK_SUBJECT.FIELD_TAG_NUMBER) | ||
| 1500 | result.append(check) | ||
| 1501 | break | ||
| 1502 | return result | ||
| 1503 | |||
| 1504 | func find_full_class_name(the_class_name : String) -> int: | ||
| 1505 | for i in range(class_table.size()): | ||
| 1506 | if the_class_name == class_table[i].parent_name + "." + class_table[i].name: | ||
| 1507 | return i | ||
| 1508 | return -1 | ||
| 1509 | |||
| 1510 | func find_class_name(the_class_name : String) -> int: | ||
| 1511 | for i in range(class_table.size()): | ||
| 1512 | if the_class_name == class_table[i].name: | ||
| 1513 | return i | ||
| 1514 | return -1 | ||
| 1515 | |||
| 1516 | func get_class_childs(class_index : int) -> Array: | ||
| 1517 | var result : Array = [] | ||
| 1518 | for i in range(class_table.size()): | ||
| 1519 | if class_table[i].parent_index == class_index: | ||
| 1520 | result.append(i) | ||
| 1521 | return result | ||
| 1522 | |||
| 1523 | func find_in_childs(the_class_name : String, child_indexes : Array) -> int: | ||
| 1524 | for c in child_indexes: | ||
| 1525 | if the_class_name == class_table[c].name: | ||
| 1526 | return c | ||
| 1527 | return -1 | ||
| 1528 | |||
| 1529 | func determine_field_types() -> Array: | ||
| 1530 | var result : Array = [] | ||
| 1531 | for f in field_table: | ||
| 1532 | if STRING_FIELD_TYPE.has(f.type_name): | ||
| 1533 | f.field_type = STRING_FIELD_TYPE[f.type_name] | ||
| 1534 | else: | ||
| 1535 | if f.type_name[0] == ".": | ||
| 1536 | f.type_class_id = find_full_class_name(f.type_name) | ||
| 1537 | else: | ||
| 1538 | # Reset result from previous assignment, that can be incorrect because of merging of imports | ||
| 1539 | f.type_class_id = -1 | ||
| 1540 | var splited_name : Array = f.type_name.split(".", false) | ||
| 1541 | var cur_class_index : int = f.parent_class_id | ||
| 1542 | var exit : bool = false | ||
| 1543 | while(true): | ||
| 1544 | var find : bool = false | ||
| 1545 | if cur_class_index == -1: | ||
| 1546 | break | ||
| 1547 | for n in splited_name: | ||
| 1548 | var childs_and_parent : Array = get_class_childs(cur_class_index) | ||
| 1549 | var res_index : int = find_in_childs(n, childs_and_parent) | ||
| 1550 | if res_index >= 0: | ||
| 1551 | find = true | ||
| 1552 | cur_class_index = res_index | ||
| 1553 | else: | ||
| 1554 | if find: | ||
| 1555 | exit = true | ||
| 1556 | else: | ||
| 1557 | cur_class_index = class_table[cur_class_index].parent_index | ||
| 1558 | break | ||
| 1559 | if exit: | ||
| 1560 | break | ||
| 1561 | if find: | ||
| 1562 | f.type_class_id = cur_class_index | ||
| 1563 | break | ||
| 1564 | if f.type_class_id == -1: | ||
| 1565 | f.type_class_id = find_full_class_name("." + f.type_name) | ||
| 1566 | for i in range(field_table.size()): | ||
| 1567 | if field_table[i].field_type == Analysis.FIELD_TYPE.UNDEFINED: | ||
| 1568 | if field_table[i].type_class_id == -1: | ||
| 1569 | result.append(CheckResult.new(field_table[i].construction_index, field_table[i].construction_index, i, CHECK_SUBJECT.FIELD_TYPE)) | ||
| 1570 | else: | ||
| 1571 | if class_table[field_table[i].type_class_id].type == Analysis.CLASS_TYPE.ENUM: | ||
| 1572 | field_table[i].field_type = Analysis.FIELD_TYPE.ENUM | ||
| 1573 | elif class_table[field_table[i].type_class_id].type == Analysis.CLASS_TYPE.MESSAGE: | ||
| 1574 | field_table[i].field_type = Analysis.FIELD_TYPE.MESSAGE | ||
| 1575 | elif class_table[field_table[i].type_class_id].type == Analysis.CLASS_TYPE.MAP: | ||
| 1576 | field_table[i].field_type = Analysis.FIELD_TYPE.MAP | ||
| 1577 | else: | ||
| 1578 | result.append(CheckResult.new(field_table[i].construction_index, field_table[i].construction_index, i, CHECK_SUBJECT.FIELD_TYPE)) | ||
| 1579 | return result | ||
| 1580 | |||
| 1581 | func check_constructions() -> Array: | ||
| 1582 | var cl : Array = check_class_names() | ||
| 1583 | var fl : Array = check_field_names() | ||
| 1584 | var ft : Array = determine_field_types() | ||
| 1585 | return cl + fl + ft | ||
| 1586 | |||
| 1587 | func check() -> bool: | ||
| 1588 | var check_result : Array = check_constructions() | ||
| 1589 | if check_result.size() == 0: | ||
| 1590 | return true | ||
| 1591 | else: | ||
| 1592 | for v in check_result: | ||
| 1593 | var main_tok : int = syntax.constructions[v.main_construction_index].begin_token_index | ||
| 1594 | var assoc_tok : int = syntax.constructions[v.associated_construction_index].begin_token_index | ||
| 1595 | var main_err_pos : Helper.StringPosition = Helper.str_pos(document.text, Helper.text_pos(tokens, main_tok)) | ||
| 1596 | var assoc_err_pos : Helper.StringPosition = Helper.str_pos(document.text, Helper.text_pos(tokens, assoc_tok)) | ||
| 1597 | var err_text : String | ||
| 1598 | if v.subject == CHECK_SUBJECT.CLASS_NAME: | ||
| 1599 | var class_type = "Undefined" | ||
| 1600 | if class_table[v.table_index].type == Analysis.CLASS_TYPE.ENUM: | ||
| 1601 | class_type = "Enum" | ||
| 1602 | elif class_table[v.table_index].type == Analysis.CLASS_TYPE.MESSAGE: | ||
| 1603 | class_type = "Message" | ||
| 1604 | elif class_table[v.table_index].type == Analysis.CLASS_TYPE.MAP: | ||
| 1605 | class_type = "Map" | ||
| 1606 | err_text = class_type + " name '" + class_table[v.table_index].name + "' is already defined at:" + str(assoc_err_pos.str_num) + ":" + str(assoc_err_pos.column) | ||
| 1607 | elif v.subject == CHECK_SUBJECT.FIELD_NAME: | ||
| 1608 | err_text = "Field name '" + field_table[v.table_index].name + "' is already defined at:" + str(assoc_err_pos.str_num) + ":" + str(assoc_err_pos.column) | ||
| 1609 | elif v.subject == CHECK_SUBJECT.FIELD_TAG_NUMBER: | ||
| 1610 | err_text = "Tag number '" + field_table[v.table_index].tag + "' is already defined at:" + str(assoc_err_pos.str_num) + ":" + str(assoc_err_pos.column) | ||
| 1611 | elif v.subject == CHECK_SUBJECT.FIELD_TYPE: | ||
| 1612 | err_text = "Type '" + field_table[v.table_index].type_name + "' of the '" + field_table[v.table_index].name + "' field undefined" | ||
| 1613 | else: | ||
| 1614 | err_text = "Undefined error" | ||
| 1615 | printerr(Helper.error_string(document.name, main_err_pos.str_num, main_err_pos.column, err_text)) | ||
| 1616 | return false | ||
| 1617 | |||
| 1618 | class Translator: | ||
| 1619 | |||
| 1620 | var class_table : Array | ||
| 1621 | var field_table : Array | ||
| 1622 | var group_table : Array | ||
| 1623 | var proto_version : int | ||
| 1624 | |||
| 1625 | func _init(analyzer_result : AnalyzeResult): | ||
| 1626 | class_table = analyzer_result.classes | ||
| 1627 | field_table = analyzer_result.fields | ||
| 1628 | group_table = analyzer_result.groups | ||
| 1629 | proto_version = analyzer_result.version | ||
| 1630 | |||
| 1631 | func tabulate(text : String, nesting : int) -> String: | ||
| 1632 | var tab : String = "" | ||
| 1633 | for i in range(nesting): | ||
| 1634 | tab += "\t" | ||
| 1635 | return tab + text | ||
| 1636 | |||
| 1637 | func default_dict_text() -> String: | ||
| 1638 | if proto_version == 2: | ||
| 1639 | return "DEFAULT_VALUES_2" | ||
| 1640 | elif proto_version == 3: | ||
| 1641 | return "DEFAULT_VALUES_3" | ||
| 1642 | return "TRANSLATION_ERROR" | ||
| 1643 | |||
| 1644 | func generate_field_type(field : Analysis.ASTField) -> String: | ||
| 1645 | var text : String = "PB_DATA_TYPE." | ||
| 1646 | if field.field_type == Analysis.FIELD_TYPE.INT32: | ||
| 1647 | return text + "INT32" | ||
| 1648 | elif field.field_type == Analysis.FIELD_TYPE.SINT32: | ||
| 1649 | return text + "SINT32" | ||
| 1650 | elif field.field_type == Analysis.FIELD_TYPE.UINT32: | ||
| 1651 | return text + "UINT32" | ||
| 1652 | elif field.field_type == Analysis.FIELD_TYPE.INT64: | ||
| 1653 | return text + "INT64" | ||
| 1654 | elif field.field_type == Analysis.FIELD_TYPE.SINT64: | ||
| 1655 | return text + "SINT64" | ||
| 1656 | elif field.field_type == Analysis.FIELD_TYPE.UINT64: | ||
| 1657 | return text + "UINT64" | ||
| 1658 | elif field.field_type == Analysis.FIELD_TYPE.BOOL: | ||
| 1659 | return text + "BOOL" | ||
| 1660 | elif field.field_type == Analysis.FIELD_TYPE.ENUM: | ||
| 1661 | return text + "ENUM" | ||
| 1662 | elif field.field_type == Analysis.FIELD_TYPE.FIXED32: | ||
| 1663 | return text + "FIXED32" | ||
| 1664 | elif field.field_type == Analysis.FIELD_TYPE.SFIXED32: | ||
| 1665 | return text + "SFIXED32" | ||
| 1666 | elif field.field_type == Analysis.FIELD_TYPE.FLOAT: | ||
| 1667 | return text + "FLOAT" | ||
| 1668 | elif field.field_type == Analysis.FIELD_TYPE.FIXED64: | ||
| 1669 | return text + "FIXED64" | ||
| 1670 | elif field.field_type == Analysis.FIELD_TYPE.SFIXED64: | ||
| 1671 | return text + "SFIXED64" | ||
| 1672 | elif field.field_type == Analysis.FIELD_TYPE.DOUBLE: | ||
| 1673 | return text + "DOUBLE" | ||
| 1674 | elif field.field_type == Analysis.FIELD_TYPE.STRING: | ||
| 1675 | return text + "STRING" | ||
| 1676 | elif field.field_type == Analysis.FIELD_TYPE.BYTES: | ||
| 1677 | return text + "BYTES" | ||
| 1678 | elif field.field_type == Analysis.FIELD_TYPE.MESSAGE: | ||
| 1679 | return text + "MESSAGE" | ||
| 1680 | elif field.field_type == Analysis.FIELD_TYPE.MAP: | ||
| 1681 | return text + "MAP" | ||
| 1682 | return text | ||
| 1683 | |||
| 1684 | func generate_field_rule(field : Analysis.ASTField) -> String: | ||
| 1685 | var text : String = "PB_RULE." | ||
| 1686 | if field.qualificator == Analysis.FIELD_QUALIFICATOR.OPTIONAL: | ||
| 1687 | return text + "OPTIONAL" | ||
| 1688 | elif field.qualificator == Analysis.FIELD_QUALIFICATOR.REQUIRED: | ||
| 1689 | return text + "REQUIRED" | ||
| 1690 | elif field.qualificator == Analysis.FIELD_QUALIFICATOR.REPEATED: | ||
| 1691 | return text + "REPEATED" | ||
| 1692 | elif field.qualificator == Analysis.FIELD_QUALIFICATOR.RESERVED: | ||
| 1693 | return text + "RESERVED" | ||
| 1694 | return text | ||
| 1695 | |||
| 1696 | func generate_gdscript_type(field : Analysis.ASTField) -> String: | ||
| 1697 | if field.field_type == Analysis.FIELD_TYPE.MESSAGE: | ||
| 1698 | var type_name : String = class_table[field.type_class_id].parent_name + "." + class_table[field.type_class_id].name | ||
| 1699 | return type_name.substr(1, type_name.length() - 1) | ||
| 1700 | return generate_gdscript_simple_type(field) | ||
| 1701 | |||
| 1702 | func generate_gdscript_simple_type(field : Analysis.ASTField) -> String: | ||
| 1703 | if field.field_type == Analysis.FIELD_TYPE.INT32: | ||
| 1704 | return "int" | ||
| 1705 | elif field.field_type == Analysis.FIELD_TYPE.SINT32: | ||
| 1706 | return "int" | ||
| 1707 | elif field.field_type == Analysis.FIELD_TYPE.UINT32: | ||
| 1708 | return "int" | ||
| 1709 | elif field.field_type == Analysis.FIELD_TYPE.INT64: | ||
| 1710 | return "int" | ||
| 1711 | elif field.field_type == Analysis.FIELD_TYPE.SINT64: | ||
| 1712 | return "int" | ||
| 1713 | elif field.field_type == Analysis.FIELD_TYPE.UINT64: | ||
| 1714 | return "int" | ||
| 1715 | elif field.field_type == Analysis.FIELD_TYPE.BOOL: | ||
| 1716 | return "bool" | ||
| 1717 | elif field.field_type == Analysis.FIELD_TYPE.ENUM: | ||
| 1718 | return "" | ||
| 1719 | elif field.field_type == Analysis.FIELD_TYPE.FIXED32: | ||
| 1720 | return "int" | ||
| 1721 | elif field.field_type == Analysis.FIELD_TYPE.SFIXED32: | ||
| 1722 | return "int" | ||
| 1723 | elif field.field_type == Analysis.FIELD_TYPE.FLOAT: | ||
| 1724 | return "float" | ||
| 1725 | elif field.field_type == Analysis.FIELD_TYPE.FIXED64: | ||
| 1726 | return "int" | ||
| 1727 | elif field.field_type == Analysis.FIELD_TYPE.SFIXED64: | ||
| 1728 | return "int" | ||
| 1729 | elif field.field_type == Analysis.FIELD_TYPE.DOUBLE: | ||
| 1730 | return "float" | ||
| 1731 | elif field.field_type == Analysis.FIELD_TYPE.STRING: | ||
| 1732 | return "String" | ||
| 1733 | elif field.field_type == Analysis.FIELD_TYPE.BYTES: | ||
| 1734 | return "PackedByteArray" | ||
| 1735 | return "" | ||
| 1736 | |||
| 1737 | func generate_field_constructor(field_index : int, nesting : int) -> String: | ||
| 1738 | var text : String = "" | ||
| 1739 | var f : Analysis.ASTField = field_table[field_index] | ||
| 1740 | var field_name : String = "__" + f.name | ||
| 1741 | var pbfield_text : String | ||
| 1742 | var default_var_name := field_name + "_default" | ||
| 1743 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.REPEATED: | ||
| 1744 | var type_name := generate_gdscript_type(f) | ||
| 1745 | if type_name: | ||
| 1746 | text = tabulate("var %s: Array[%s] = []\n" % [default_var_name, type_name], nesting) | ||
| 1747 | else: | ||
| 1748 | text = tabulate("var %s: Array = []\n" % [default_var_name], nesting) | ||
| 1749 | pbfield_text += field_name + " = PBField.new(" | ||
| 1750 | pbfield_text += "\"" + f.name + "\", " | ||
| 1751 | pbfield_text += generate_field_type(f) + ", " | ||
| 1752 | pbfield_text += generate_field_rule(f) + ", " | ||
| 1753 | pbfield_text += str(f.tag) + ", " | ||
| 1754 | if f.option == Analysis.FIELD_OPTION.PACKED: | ||
| 1755 | pbfield_text += "true" | ||
| 1756 | elif f.option == Analysis.FIELD_OPTION.NOT_PACKED: | ||
| 1757 | pbfield_text += "false" | ||
| 1758 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.REPEATED: | ||
| 1759 | pbfield_text += ", " + default_var_name | ||
| 1760 | else: | ||
| 1761 | pbfield_text += ", " + default_dict_text() + "[" + generate_field_type(f) + "]" | ||
| 1762 | pbfield_text += ")\n" | ||
| 1763 | text += tabulate(pbfield_text, nesting) | ||
| 1764 | if f.is_map_field: | ||
| 1765 | text += tabulate(field_name + ".is_map_field = true\n", nesting) | ||
| 1766 | text += tabulate("service = PBServiceField.new()\n", nesting) | ||
| 1767 | text += tabulate("service.field = " + field_name + "\n", nesting) | ||
| 1768 | if f.field_type == Analysis.FIELD_TYPE.MESSAGE: | ||
| 1769 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.REPEATED: | ||
| 1770 | text += tabulate("service.func_ref = Callable(self, \"add_" + f.name + "\")\n", nesting) | ||
| 1771 | else: | ||
| 1772 | text += tabulate("service.func_ref = Callable(self, \"new_" + f.name + "\")\n", nesting) | ||
| 1773 | elif f.field_type == Analysis.FIELD_TYPE.MAP: | ||
| 1774 | text += tabulate("service.func_ref = Callable(self, \"add_empty_" + f.name + "\")\n", nesting) | ||
| 1775 | text += tabulate("data[" + field_name + ".tag] = service\n", nesting) | ||
| 1776 | |||
| 1777 | return text | ||
| 1778 | |||
| 1779 | func generate_group_clear(field_index : int, nesting : int) -> String: | ||
| 1780 | for g in group_table: | ||
| 1781 | var text : String = "" | ||
| 1782 | var find : bool = false | ||
| 1783 | if g.parent_class_id == field_table[field_index].parent_class_id: | ||
| 1784 | for i in g.field_indexes: | ||
| 1785 | if field_index == i: | ||
| 1786 | find = true | ||
| 1787 | text += tabulate("data[" + field_table[i].tag + "].state = PB_SERVICE_STATE.FILLED\n", nesting) | ||
| 1788 | else: | ||
| 1789 | text += tabulate("__" + field_table[i].name + ".value = " + default_dict_text() + "[" + generate_field_type(field_table[i]) + "]\n", nesting) | ||
| 1790 | text += tabulate("data[" + field_table[i].tag + "].state = PB_SERVICE_STATE.UNFILLED\n", nesting) | ||
| 1791 | if find: | ||
| 1792 | return text | ||
| 1793 | return "" | ||
| 1794 | |||
| 1795 | func generate_has_oneof(field_index : int, nesting : int) -> String: | ||
| 1796 | for g in group_table: | ||
| 1797 | var text : String = "" | ||
| 1798 | if g.parent_class_id == field_table[field_index].parent_class_id: | ||
| 1799 | for i in g.field_indexes: | ||
| 1800 | if field_index == i: | ||
| 1801 | text += tabulate("func has_" + field_table[i].name + "() -> bool:\n", nesting) | ||
| 1802 | nesting += 1 | ||
| 1803 | text += tabulate("return data[" + field_table[i].tag + "].state == PB_SERVICE_STATE.FILLED\n", nesting) | ||
| 1804 | return text | ||
| 1805 | return "" | ||
| 1806 | |||
| 1807 | func generate_field(field_index : int, nesting : int) -> String: | ||
| 1808 | var text : String = "" | ||
| 1809 | var f : Analysis.ASTField = field_table[field_index] | ||
| 1810 | var varname : String = "__" + f.name | ||
| 1811 | text += tabulate("var " + varname + ": PBField\n", nesting) | ||
| 1812 | if f.field_type == Analysis.FIELD_TYPE.MESSAGE: | ||
| 1813 | var the_class_name : String = class_table[f.type_class_id].parent_name + "." + class_table[f.type_class_id].name | ||
| 1814 | the_class_name = the_class_name.substr(1, the_class_name.length() - 1) | ||
| 1815 | if f.qualificator != Analysis.FIELD_QUALIFICATOR.OPTIONAL: | ||
| 1816 | text += generate_has_oneof(field_index, nesting) | ||
| 1817 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.REPEATED: | ||
| 1818 | text += tabulate("func get_" + f.name + "() -> Array[" + the_class_name + "]:\n", nesting) | ||
| 1819 | else: | ||
| 1820 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.OPTIONAL: | ||
| 1821 | text += tabulate("func has_" + f.name + "() -> bool:\n", nesting) | ||
| 1822 | nesting += 1 | ||
| 1823 | text += tabulate("if " + varname + ".value != null:\n", nesting) | ||
| 1824 | nesting += 1 | ||
| 1825 | text += tabulate("return true\n", nesting) | ||
| 1826 | nesting -= 1 | ||
| 1827 | text += tabulate("return false\n", nesting) | ||
| 1828 | nesting -= 1 | ||
| 1829 | text += tabulate("func get_" + f.name + "() -> " + the_class_name + ":\n", nesting) | ||
| 1830 | nesting += 1 | ||
| 1831 | text += tabulate("return " + varname + ".value\n", nesting) | ||
| 1832 | nesting -= 1 | ||
| 1833 | text += tabulate("func clear_" + f.name + "() -> void:\n", nesting) | ||
| 1834 | nesting += 1 | ||
| 1835 | text += tabulate("data[" + str(f.tag) + "].state = PB_SERVICE_STATE.UNFILLED\n", nesting) | ||
| 1836 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.REPEATED: | ||
| 1837 | text += tabulate(varname + ".value.clear()\n", nesting) | ||
| 1838 | nesting -= 1 | ||
| 1839 | text += tabulate("func add_" + f.name + "() -> " + the_class_name + ":\n", nesting) | ||
| 1840 | nesting += 1 | ||
| 1841 | text += tabulate("var element = " + the_class_name + ".new()\n", nesting) | ||
| 1842 | text += tabulate(varname + ".value.append(element)\n", nesting) | ||
| 1843 | text += tabulate("return element\n", nesting) | ||
| 1844 | else: | ||
| 1845 | text += tabulate(varname + ".value = " + default_dict_text() + "[" + generate_field_type(f) + "]\n", nesting) | ||
| 1846 | nesting -= 1 | ||
| 1847 | text += tabulate("func new_" + f.name + "() -> " + the_class_name + ":\n", nesting) | ||
| 1848 | nesting += 1 | ||
| 1849 | text += generate_group_clear(field_index, nesting) | ||
| 1850 | text += tabulate(varname + ".value = " + the_class_name + ".new()\n", nesting) | ||
| 1851 | text += tabulate("return " + varname + ".value\n", nesting) | ||
| 1852 | elif f.field_type == Analysis.FIELD_TYPE.MAP: | ||
| 1853 | var the_parent_class_name : String = class_table[f.type_class_id].parent_name | ||
| 1854 | the_parent_class_name = the_parent_class_name.substr(1, the_parent_class_name.length() - 1) | ||
| 1855 | var the_class_name : String = the_parent_class_name + "." + class_table[f.type_class_id].name | ||
| 1856 | |||
| 1857 | text += generate_has_oneof(field_index, nesting) | ||
| 1858 | text += tabulate("func get_raw_" + f.name + "():\n", nesting) | ||
| 1859 | nesting += 1 | ||
| 1860 | text += tabulate("return " + varname + ".value\n", nesting) | ||
| 1861 | nesting -= 1 | ||
| 1862 | text += tabulate("func get_" + f.name + "():\n", nesting) | ||
| 1863 | nesting += 1 | ||
| 1864 | text += tabulate("return PBPacker.construct_map(" + varname + ".value)\n", nesting) | ||
| 1865 | nesting -= 1 | ||
| 1866 | text += tabulate("func clear_" + f.name + "():\n", nesting) | ||
| 1867 | nesting += 1 | ||
| 1868 | text += tabulate("data[" + str(f.tag) + "].state = PB_SERVICE_STATE.UNFILLED\n", nesting) | ||
| 1869 | text += tabulate(varname + ".value = " + default_dict_text() + "[" + generate_field_type(f) + "]\n", nesting) | ||
| 1870 | nesting -= 1 | ||
| 1871 | for i in range(field_table.size()): | ||
| 1872 | if field_table[i].parent_class_id == f.type_class_id && field_table[i].name == "value": | ||
| 1873 | var gd_type : String = generate_gdscript_simple_type(field_table[i]) | ||
| 1874 | var return_type : String = " -> " + the_class_name | ||
| 1875 | var value_return_type : String = "" | ||
| 1876 | if gd_type != "": | ||
| 1877 | value_return_type = return_type | ||
| 1878 | elif field_table[i].field_type == Analysis.FIELD_TYPE.MESSAGE: | ||
| 1879 | value_return_type = " -> " + the_parent_class_name + "." + field_table[i].type_name | ||
| 1880 | text += tabulate("func add_empty_" + f.name + "()" + return_type + ":\n", nesting) | ||
| 1881 | nesting += 1 | ||
| 1882 | text += generate_group_clear(field_index, nesting) | ||
| 1883 | text += tabulate("var element = " + the_class_name + ".new()\n", nesting) | ||
| 1884 | text += tabulate(varname + ".value.append(element)\n", nesting) | ||
| 1885 | text += tabulate("return element\n", nesting) | ||
| 1886 | nesting -= 1 | ||
| 1887 | if field_table[i].field_type == Analysis.FIELD_TYPE.MESSAGE: | ||
| 1888 | text += tabulate("func add_" + f.name + "(a_key)" + value_return_type + ":\n", nesting) | ||
| 1889 | nesting += 1 | ||
| 1890 | text += generate_group_clear(field_index, nesting) | ||
| 1891 | text += tabulate("var idx = -1\n", nesting) | ||
| 1892 | text += tabulate("for i in range(" + varname + ".value.size()):\n", nesting) | ||
| 1893 | nesting += 1 | ||
| 1894 | text += tabulate("if " + varname + ".value[i].get_key() == a_key:\n", nesting) | ||
| 1895 | nesting += 1 | ||
| 1896 | text += tabulate("idx = i\n", nesting) | ||
| 1897 | text += tabulate("break\n", nesting) | ||
| 1898 | nesting -= 2 | ||
| 1899 | text += tabulate("var element = " + the_class_name + ".new()\n", nesting) | ||
| 1900 | text += tabulate("element.set_key(a_key)\n", nesting) | ||
| 1901 | text += tabulate("if idx != -1:\n", nesting) | ||
| 1902 | nesting += 1 | ||
| 1903 | text += tabulate(varname + ".value[idx] = element\n", nesting) | ||
| 1904 | nesting -= 1 | ||
| 1905 | text += tabulate("else:\n", nesting) | ||
| 1906 | nesting += 1 | ||
| 1907 | text += tabulate(varname + ".value.append(element)\n", nesting) | ||
| 1908 | nesting -= 1 | ||
| 1909 | text += tabulate("return element.new_value()\n", nesting) | ||
| 1910 | else: | ||
| 1911 | text += tabulate("func add_" + f.name + "(a_key, a_value) -> void:\n", nesting) | ||
| 1912 | nesting += 1 | ||
| 1913 | text += generate_group_clear(field_index, nesting) | ||
| 1914 | text += tabulate("var idx = -1\n", nesting) | ||
| 1915 | text += tabulate("for i in range(" + varname + ".value.size()):\n", nesting) | ||
| 1916 | nesting += 1 | ||
| 1917 | text += tabulate("if " + varname + ".value[i].get_key() == a_key:\n", nesting) | ||
| 1918 | nesting += 1 | ||
| 1919 | text += tabulate("idx = i\n", nesting) | ||
| 1920 | text += tabulate("break\n", nesting) | ||
| 1921 | nesting -= 2 | ||
| 1922 | text += tabulate("var element = " + the_class_name + ".new()\n", nesting) | ||
| 1923 | text += tabulate("element.set_key(a_key)\n", nesting) | ||
| 1924 | text += tabulate("element.set_value(a_value)\n", nesting) | ||
| 1925 | text += tabulate("if idx != -1:\n", nesting) | ||
| 1926 | nesting += 1 | ||
| 1927 | text += tabulate(varname + ".value[idx] = element\n", nesting) | ||
| 1928 | nesting -= 1 | ||
| 1929 | text += tabulate("else:\n", nesting) | ||
| 1930 | nesting += 1 | ||
| 1931 | text += tabulate(varname + ".value.append(element)\n", nesting) | ||
| 1932 | nesting -= 1 | ||
| 1933 | break | ||
| 1934 | else: | ||
| 1935 | var gd_type : String = generate_gdscript_simple_type(f) | ||
| 1936 | var return_type : String = "" | ||
| 1937 | var argument_type : String = "" | ||
| 1938 | if gd_type != "": | ||
| 1939 | return_type = " -> " + gd_type | ||
| 1940 | argument_type = " : " + gd_type | ||
| 1941 | if f.qualificator != Analysis.FIELD_QUALIFICATOR.OPTIONAL: | ||
| 1942 | text += generate_has_oneof(field_index, nesting) | ||
| 1943 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.REPEATED: | ||
| 1944 | var array_type := "[" + gd_type + "]" if gd_type else "" | ||
| 1945 | text += tabulate("func get_" + f.name + "() -> Array" + array_type + ":\n", nesting) | ||
| 1946 | else: | ||
| 1947 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.OPTIONAL: | ||
| 1948 | text += tabulate("func has_" + f.name + "() -> bool:\n", nesting) | ||
| 1949 | nesting += 1 | ||
| 1950 | text += tabulate("if " + varname + ".value != null:\n", nesting) | ||
| 1951 | nesting += 1 | ||
| 1952 | text += tabulate("return true\n", nesting) | ||
| 1953 | nesting -= 1 | ||
| 1954 | text += tabulate("return false\n", nesting) | ||
| 1955 | nesting -= 1 | ||
| 1956 | text += tabulate("func get_" + f.name + "()" + return_type + ":\n", nesting) | ||
| 1957 | nesting += 1 | ||
| 1958 | text += tabulate("return " + varname + ".value\n", nesting) | ||
| 1959 | nesting -= 1 | ||
| 1960 | text += tabulate("func clear_" + f.name + "() -> void:\n", nesting) | ||
| 1961 | nesting += 1 | ||
| 1962 | text += tabulate("data[" + str(f.tag) + "].state = PB_SERVICE_STATE.UNFILLED\n", nesting) | ||
| 1963 | if f.qualificator == Analysis.FIELD_QUALIFICATOR.REPEATED: | ||
| 1964 | text += tabulate(varname + ".value.clear()\n", nesting) | ||
| 1965 | nesting -= 1 | ||
| 1966 | text += tabulate("func add_" + f.name + "(value" + argument_type + ") -> void:\n", nesting) | ||
| 1967 | nesting += 1 | ||
| 1968 | text += tabulate(varname + ".value.append(value)\n", nesting) | ||
| 1969 | else: | ||
| 1970 | text += tabulate(varname + ".value = " + default_dict_text() + "[" + generate_field_type(f) + "]\n", nesting) | ||
| 1971 | nesting -= 1 | ||
| 1972 | text += tabulate("func set_" + f.name + "(value" + argument_type + ") -> void:\n", nesting) | ||
| 1973 | nesting += 1 | ||
| 1974 | text += generate_group_clear(field_index, nesting) | ||
| 1975 | text += tabulate(varname + ".value = value\n", nesting) | ||
| 1976 | return text | ||
| 1977 | |||
| 1978 | func generate_class(class_index : int, nesting : int) -> String: | ||
| 1979 | var text : String = "" | ||
| 1980 | if class_table[class_index].type == Analysis.CLASS_TYPE.MESSAGE || class_table[class_index].type == Analysis.CLASS_TYPE.MAP: | ||
| 1981 | var cls_pref : String = "" | ||
| 1982 | cls_pref += tabulate("class " + class_table[class_index].name + ":\n", nesting) | ||
| 1983 | nesting += 1 | ||
| 1984 | cls_pref += tabulate("func _init():\n", nesting) | ||
| 1985 | text += cls_pref | ||
| 1986 | nesting += 1 | ||
| 1987 | text += tabulate("var service\n", nesting) | ||
| 1988 | text += tabulate("\n", nesting) | ||
| 1989 | var field_text : String = "" | ||
| 1990 | for i in range(field_table.size()): | ||
| 1991 | if field_table[i].parent_class_id == class_index: | ||
| 1992 | text += generate_field_constructor(i, nesting) | ||
| 1993 | text += tabulate("\n", nesting) | ||
| 1994 | field_text += generate_field(i, nesting - 1) | ||
| 1995 | field_text += tabulate("\n", nesting - 1) | ||
| 1996 | nesting -= 1 | ||
| 1997 | text += tabulate("var data = {}\n", nesting) | ||
| 1998 | text += tabulate("\n", nesting) | ||
| 1999 | text += field_text | ||
| 2000 | for j in range(class_table.size()): | ||
| 2001 | if class_table[j].parent_index == class_index: | ||
| 2002 | var cl_text = generate_class(j, nesting) | ||
| 2003 | text += cl_text | ||
| 2004 | if class_table[j].type == Analysis.CLASS_TYPE.MESSAGE || class_table[j].type == Analysis.CLASS_TYPE.MAP: | ||
| 2005 | text += generate_class_services(nesting + 1) | ||
| 2006 | text += tabulate("\n", nesting + 1) | ||
| 2007 | elif class_table[class_index].type == Analysis.CLASS_TYPE.ENUM: | ||
| 2008 | text += tabulate("enum " + class_table[class_index].name + " {\n", nesting) | ||
| 2009 | nesting += 1 | ||
| 2010 | |||
| 2011 | var expected_prefix = class_table[class_index].name.to_snake_case().to_upper() + "_" | ||
| 2012 | var all_have_prefix = true | ||
| 2013 | for en in range(class_table[class_index].values.size()): | ||
| 2014 | var value_name = class_table[class_index].values[en].name | ||
| 2015 | all_have_prefix = all_have_prefix and value_name.begins_with(expected_prefix) and value_name != expected_prefix | ||
| 2016 | |||
| 2017 | for en in range(class_table[class_index].values.size()): | ||
| 2018 | var value_name = class_table[class_index].values[en].name | ||
| 2019 | if all_have_prefix: | ||
| 2020 | value_name = value_name.substr(expected_prefix.length()) | ||
| 2021 | var enum_val = value_name + " = " + class_table[class_index].values[en].value | ||
| 2022 | if en == class_table[class_index].values.size() - 1: | ||
| 2023 | text += tabulate(enum_val + "\n", nesting) | ||
| 2024 | else: | ||
| 2025 | text += tabulate(enum_val + ",\n", nesting) | ||
| 2026 | nesting -= 1 | ||
| 2027 | text += tabulate("}\n", nesting) | ||
| 2028 | text += tabulate("\n", nesting) | ||
| 2029 | |||
| 2030 | return text | ||
| 2031 | |||
| 2032 | func generate_class_services(nesting : int) -> String: | ||
| 2033 | var text : String = "" | ||
| 2034 | text += tabulate("func _to_string() -> String:\n", nesting) | ||
| 2035 | nesting += 1 | ||
| 2036 | text += tabulate("return PBPacker.message_to_string(data)\n", nesting) | ||
| 2037 | text += tabulate("\n", nesting) | ||
| 2038 | nesting -= 1 | ||
| 2039 | text += tabulate("func to_bytes() -> PackedByteArray:\n", nesting) | ||
| 2040 | nesting += 1 | ||
| 2041 | text += tabulate("return PBPacker.pack_message(data)\n", nesting) | ||
| 2042 | text += tabulate("\n", nesting) | ||
| 2043 | nesting -= 1 | ||
| 2044 | text += tabulate("func from_bytes(bytes : PackedByteArray, offset : int = 0, limit : int = -1) -> int:\n", nesting) | ||
| 2045 | nesting += 1 | ||
| 2046 | text += tabulate("var cur_limit = bytes.size()\n", nesting) | ||
| 2047 | text += tabulate("if limit != -1:\n", nesting) | ||
| 2048 | nesting += 1 | ||
| 2049 | text += tabulate("cur_limit = limit\n", nesting) | ||
| 2050 | nesting -= 1 | ||
| 2051 | text += tabulate("var result = PBPacker.unpack_message(data, bytes, offset, cur_limit)\n", nesting) | ||
| 2052 | text += tabulate("if result == cur_limit:\n", nesting) | ||
| 2053 | nesting += 1 | ||
| 2054 | text += tabulate("if PBPacker.check_required(data):\n", nesting) | ||
| 2055 | nesting += 1 | ||
| 2056 | text += tabulate("if limit == -1:\n", nesting) | ||
| 2057 | nesting += 1 | ||
| 2058 | text += tabulate("return PB_ERR.NO_ERRORS\n", nesting) | ||
| 2059 | nesting -= 2 | ||
| 2060 | text += tabulate("else:\n", nesting) | ||
| 2061 | nesting += 1 | ||
| 2062 | text += tabulate("return PB_ERR.REQUIRED_FIELDS\n", nesting) | ||
| 2063 | nesting -= 2 | ||
| 2064 | text += tabulate("elif limit == -1 && result > 0:\n", nesting) | ||
| 2065 | nesting += 1 | ||
| 2066 | text += tabulate("return PB_ERR.PARSE_INCOMPLETE\n", nesting) | ||
| 2067 | nesting -= 1 | ||
| 2068 | text += tabulate("return result\n", nesting) | ||
| 2069 | return text | ||
| 2070 | |||
| 2071 | func translate(file_name : String, core_file_name : String) -> bool: | ||
| 2072 | |||
| 2073 | var file : FileAccess = FileAccess.open(file_name, FileAccess.WRITE) | ||
| 2074 | if file == null: | ||
| 2075 | printerr("File: '", file_name, "' save error.") | ||
| 2076 | return false | ||
| 2077 | |||
| 2078 | if !FileAccess.file_exists(core_file_name): | ||
| 2079 | printerr("File: '", core_file_name, "' not found.") | ||
| 2080 | return false | ||
| 2081 | |||
| 2082 | var core_file : FileAccess = FileAccess.open(core_file_name, FileAccess.READ) | ||
| 2083 | if core_file == null: | ||
| 2084 | printerr("File: '", core_file_name, "' read error.") | ||
| 2085 | return false | ||
| 2086 | var core_text : String = core_file.get_as_text() | ||
| 2087 | core_file.close() | ||
| 2088 | |||
| 2089 | var text : String = "" | ||
| 2090 | var nesting : int = 0 | ||
| 2091 | core_text = core_text.replace(PROTO_VERSION_DEFAULT, PROTO_VERSION_CONST + str(proto_version)) | ||
| 2092 | text += core_text + "\n\n\n" | ||
| 2093 | text += "############### USER DATA BEGIN ################\n" | ||
| 2094 | var cls_user : String = "" | ||
| 2095 | for i in range(class_table.size()): | ||
| 2096 | if class_table[i].parent_index == -1: | ||
| 2097 | var cls_text = generate_class(i, nesting) | ||
| 2098 | cls_user += cls_text | ||
| 2099 | if class_table[i].type == Analysis.CLASS_TYPE.MESSAGE: | ||
| 2100 | nesting += 1 | ||
| 2101 | cls_user += generate_class_services(nesting) | ||
| 2102 | cls_user += tabulate("\n", nesting) | ||
| 2103 | nesting -= 1 | ||
| 2104 | text += "\n\n" | ||
| 2105 | text += cls_user | ||
| 2106 | text += "################ USER DATA END #################\n" | ||
| 2107 | file.store_string(text) | ||
| 2108 | file.close() | ||
| 2109 | if !FileAccess.file_exists(file_name): | ||
| 2110 | printerr("File: '", file_name, "' save error.") | ||
| 2111 | return false | ||
| 2112 | return true | ||
| 2113 | |||
| 2114 | |||
| 2115 | class ImportFile: | ||
| 2116 | func _init(sha : String, a_path : String, a_parent : int): | ||
| 2117 | sha256 = sha | ||
| 2118 | path = a_path | ||
| 2119 | parent_index = a_parent | ||
| 2120 | |||
| 2121 | var sha256 : String | ||
| 2122 | var path : String | ||
| 2123 | var parent_index : int | ||
| 2124 | |||
| 2125 | func parse_all(analyzes : Dictionary, imports : Array, path : String, full_name : String, parent_index : int) -> bool: | ||
| 2126 | |||
| 2127 | if !FileAccess.file_exists(full_name): | ||
| 2128 | printerr(full_name, ": not found.") | ||
| 2129 | return false | ||
| 2130 | |||
| 2131 | var file : FileAccess = FileAccess.open(full_name, FileAccess.READ) | ||
| 2132 | if file == null: | ||
| 2133 | printerr(full_name, ": read error.") | ||
| 2134 | return false | ||
| 2135 | var doc : Document = Document.new(full_name, file.get_as_text()) | ||
| 2136 | var sha : String = file.get_sha256(full_name) | ||
| 2137 | file.close() | ||
| 2138 | |||
| 2139 | if !analyzes.has(sha): | ||
| 2140 | print(full_name, ": parsing.") | ||
| 2141 | var analysis : Analysis = Analysis.new(path, doc) | ||
| 2142 | var an_result : AnalyzeResult = analysis.analyze() | ||
| 2143 | if an_result.state: | ||
| 2144 | analyzes[sha] = an_result | ||
| 2145 | var parent : int = imports.size() | ||
| 2146 | imports.append(ImportFile.new(sha, doc.name, parent_index)) | ||
| 2147 | for im in an_result.imports: | ||
| 2148 | if !parse_all(analyzes, imports, path, im.path, parent): | ||
| 2149 | return false | ||
| 2150 | else: | ||
| 2151 | printerr(doc.name + ": parsing error.") | ||
| 2152 | return false | ||
| 2153 | else: | ||
| 2154 | print(full_name, ": retrieving data from cache.") | ||
| 2155 | imports.append(ImportFile.new(sha, doc.name, parent_index)) | ||
| 2156 | return true | ||
| 2157 | |||
| 2158 | func union_analyses(a1 : AnalyzeResult, a2 : AnalyzeResult, only_classes : bool = true) -> void: | ||
| 2159 | var class_offset : int = a1.classes.size() | ||
| 2160 | var field_offset = a1.fields.size() | ||
| 2161 | for cl in a2.classes: | ||
| 2162 | var cur_class : Analysis.ASTClass = cl.copy() | ||
| 2163 | if cur_class.parent_index != -1: | ||
| 2164 | cur_class.parent_index += class_offset | ||
| 2165 | a1.classes.append(cur_class) | ||
| 2166 | if only_classes: | ||
| 2167 | return | ||
| 2168 | for fl in a2.fields: | ||
| 2169 | var cur_field : Analysis.ASTField = fl.copy() | ||
| 2170 | cur_field.parent_class_id += class_offset | ||
| 2171 | cur_field.type_class_id = -1 | ||
| 2172 | a1.fields.append(cur_field) | ||
| 2173 | for gr in a2.groups: | ||
| 2174 | var cur_group : Analysis.ASTFieldGroup = gr.copy() | ||
| 2175 | cur_group.parent_class_id += class_offset | ||
| 2176 | var indexes : Array = [] | ||
| 2177 | for i in cur_group.field_indexes: | ||
| 2178 | indexes.append(i + field_offset) | ||
| 2179 | cur_group.field_indexes = indexes | ||
| 2180 | a1.groups.append(cur_group) | ||
| 2181 | |||
| 2182 | func union_imports(analyzes : Dictionary, key : String, result : AnalyzeResult, keys : Array, nesting : int, use_public : bool = true, only_classes : bool = true) -> void: | ||
| 2183 | nesting += 1 | ||
| 2184 | for im in analyzes[key].imports: | ||
| 2185 | var find : bool = false | ||
| 2186 | for k in keys: | ||
| 2187 | if im.sha256 == k: | ||
| 2188 | find = true | ||
| 2189 | break | ||
| 2190 | if find: | ||
| 2191 | continue | ||
| 2192 | if (!use_public) || (use_public && ((im.public && nesting > 1) || nesting < 2)): | ||
| 2193 | keys.append(im.sha256) | ||
| 2194 | union_analyses(result, analyzes[im.sha256], only_classes) | ||
| 2195 | union_imports(analyzes, im.sha256, result, keys, nesting, use_public, only_classes) | ||
| 2196 | |||
| 2197 | func semantic_all(analyzes : Dictionary, imports : Array)-> bool: | ||
| 2198 | for k in analyzes.keys(): | ||
| 2199 | print(analyzes[k].doc.name, ": analysis.") | ||
| 2200 | var keys : Array = [] | ||
| 2201 | var analyze : AnalyzeResult = analyzes[k].soft_copy() | ||
| 2202 | keys.append(k) | ||
| 2203 | analyze.classes = [] | ||
| 2204 | for cl in analyzes[k].classes: | ||
| 2205 | analyze.classes.append(cl.copy()) | ||
| 2206 | union_imports(analyzes, k, analyze, keys, 0) | ||
| 2207 | var semantic : Semantic = Semantic.new(analyze) | ||
| 2208 | if !semantic.check(): | ||
| 2209 | printerr(analyzes[k].doc.name, ": analysis error.") | ||
| 2210 | return false | ||
| 2211 | return true | ||
| 2212 | |||
| 2213 | func translate_all(analyzes : Dictionary, file_name : String, core_file_name : String) -> bool: | ||
| 2214 | var first_key : String = analyzes.keys()[0] | ||
| 2215 | var analyze : AnalyzeResult = analyzes[first_key] | ||
| 2216 | var keys : Array = [] | ||
| 2217 | keys.append(first_key) | ||
| 2218 | union_imports(analyzes, first_key, analyze, keys, 0, false, false) | ||
| 2219 | print("Performing full semantic analysis.") | ||
| 2220 | var semantic : Semantic = Semantic.new(analyze) | ||
| 2221 | if !semantic.check(): | ||
| 2222 | return false | ||
| 2223 | print("Performing translation.") | ||
| 2224 | var translator : Translator = Translator.new(analyze) | ||
| 2225 | if !translator.translate(file_name, core_file_name): | ||
| 2226 | return false | ||
| 2227 | var first : bool = true | ||
| 2228 | return true | ||
| 2229 | |||
| 2230 | func work(path : String, in_file : String, out_file : String, core_file : String) -> bool: | ||
| 2231 | var in_full_name : String = path + in_file | ||
| 2232 | var imports : Array = [] | ||
| 2233 | var analyzes : Dictionary = {} | ||
| 2234 | |||
| 2235 | print("Compiling source: '", in_full_name, "', output: '", out_file, "'.") | ||
| 2236 | print("\n1. Parsing:") | ||
| 2237 | if parse_all(analyzes, imports, path, in_full_name, -1): | ||
| 2238 | print("* Parsing completed successfully. *") | ||
| 2239 | else: | ||
| 2240 | return false | ||
| 2241 | print("\n2. Perfoming semantic analysis:") | ||
| 2242 | if semantic_all(analyzes, imports): | ||
| 2243 | print("* Semantic analysis completed successfully. *") | ||
| 2244 | else: | ||
| 2245 | return false | ||
| 2246 | print("\n3. Output file creating:") | ||
| 2247 | if translate_all(analyzes, out_file, core_file): | ||
| 2248 | print("* Output file was created successfully. *") | ||
| 2249 | else: | ||
| 2250 | return false | ||
| 2251 | return true | ||
| 2252 | |||
| 2253 | func _ready(): | ||
| 2254 | pass | ||
| diff --git a/vendor/godobuf/addons/protobuf/plugin.cfg b/vendor/godobuf/addons/protobuf/plugin.cfg new file mode 100644 index 0000000..6456a11 --- /dev/null +++ b/vendor/godobuf/addons/protobuf/plugin.cfg | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | [plugin] | ||
| 2 | |||
| 3 | name="Godobuf" | ||
| 4 | description="Google Protobuf implementation for Godot/GDScript" | ||
| 5 | author="oniksan" | ||
| 6 | version="0.6.1 for Godot 4.x.y" | ||
| 7 | script="protobuf_ui.gd" | ||
| diff --git a/vendor/godobuf/addons/protobuf/protobuf_cmdln.gd b/vendor/godobuf/addons/protobuf/protobuf_cmdln.gd new file mode 100644 index 0000000..97d7ba4 --- /dev/null +++ b/vendor/godobuf/addons/protobuf/protobuf_cmdln.gd | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | # | ||
| 2 | # BSD 3-Clause License | ||
| 3 | # | ||
| 4 | # Copyright (c) 2018, Oleg Malyavkin | ||
| 5 | # All rights reserved. | ||
| 6 | # | ||
| 7 | # Redistribution and use in source and binary forms, with or without | ||
| 8 | # modification, are permitted provided that the following conditions are met: | ||
| 9 | # | ||
| 10 | # * Redistributions of source code must retain the above copyright notice, this | ||
| 11 | # list of conditions and the following disclaimer. | ||
| 12 | # | ||
| 13 | # * Redistributions in binary form must reproduce the above copyright notice, | ||
| 14 | # this list of conditions and the following disclaimer in the documentation | ||
| 15 | # and/or other materials provided with the distribution. | ||
| 16 | # | ||
| 17 | # * Neither the name of the copyright holder nor the names of its | ||
| 18 | # contributors may be used to endorse or promote products derived from | ||
| 19 | # this software without specific prior written permission. | ||
| 20 | # | ||
| 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 22 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 23 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
| 24 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
| 25 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 26 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 27 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
| 29 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | |||
| 32 | extends SceneTree | ||
| 33 | |||
| 34 | var Parser = preload("res://addons/protobuf/parser.gd") | ||
| 35 | var Util = preload("res://addons/protobuf/protobuf_util.gd") | ||
| 36 | |||
| 37 | func error(msg : String): | ||
| 38 | push_error(msg) | ||
| 39 | quit() | ||
| 40 | |||
| 41 | func _init(): | ||
| 42 | var arguments = {} | ||
| 43 | for argument in OS.get_cmdline_args(): | ||
| 44 | if argument.find("=") > -1: | ||
| 45 | var key_value = argument.split("=") | ||
| 46 | arguments[key_value[0].lstrip("--")] = key_value[1] | ||
| 47 | |||
| 48 | if !arguments.has("input") || !arguments.has("output"): | ||
| 49 | error("Expected 2 Parameters: input and output") | ||
| 50 | |||
| 51 | var input_file_name = arguments["input"] | ||
| 52 | var output_file_name = arguments["output"] | ||
| 53 | |||
| 54 | var file = FileAccess.open(input_file_name, FileAccess.READ) | ||
| 55 | if file == null: | ||
| 56 | error("File: '" + input_file_name + "' not found.") | ||
| 57 | |||
| 58 | var parser = Parser.new() | ||
| 59 | |||
| 60 | if parser.work(Util.extract_dir(input_file_name), Util.extract_filename(input_file_name), \ | ||
| 61 | output_file_name, "res://addons/protobuf/protobuf_core.gd"): | ||
| 62 | print("Compiled '", input_file_name, "' to '", output_file_name, "'.") | ||
| 63 | else: | ||
| 64 | error("Compilation failed.") | ||
| 65 | |||
| 66 | quit() | ||
| diff --git a/vendor/godobuf/addons/protobuf/protobuf_core.gd b/vendor/godobuf/addons/protobuf/protobuf_core.gd new file mode 100644 index 0000000..7098413 --- /dev/null +++ b/vendor/godobuf/addons/protobuf/protobuf_core.gd | |||
| @@ -0,0 +1,668 @@ | |||
| 1 | # | ||
| 2 | # BSD 3-Clause License | ||
| 3 | # | ||
| 4 | # Copyright (c) 2018 - 2023, Oleg Malyavkin | ||
| 5 | # All rights reserved. | ||
| 6 | # | ||
| 7 | # Redistribution and use in source and binary forms, with or without | ||
| 8 | # modification, are permitted provided that the following conditions are met: | ||
| 9 | # | ||
| 10 | # * Redistributions of source code must retain the above copyright notice, this | ||
| 11 | # list of conditions and the following disclaimer. | ||
| 12 | # | ||
| 13 | # * Redistributions in binary form must reproduce the above copyright notice, | ||
| 14 | # this list of conditions and the following disclaimer in the documentation | ||
| 15 | # and/or other materials provided with the distribution. | ||
| 16 | # | ||
| 17 | # * Neither the name of the copyright holder nor the names of its | ||
| 18 | # contributors may be used to endorse or promote products derived from | ||
| 19 | # this software without specific prior written permission. | ||
| 20 | # | ||
| 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 22 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 23 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
| 24 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
| 25 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 26 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 27 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
| 29 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | |||
| 32 | # DEBUG_TAB redefine this " " if you need, example: const DEBUG_TAB = "\t" | ||
| 33 | |||
| 34 | const PROTO_VERSION = 0 | ||
| 35 | |||
| 36 | const DEBUG_TAB : String = " " | ||
| 37 | |||
| 38 | enum PB_ERR { | ||
| 39 | NO_ERRORS = 0, | ||
| 40 | VARINT_NOT_FOUND = -1, | ||
| 41 | REPEATED_COUNT_NOT_FOUND = -2, | ||
| 42 | REPEATED_COUNT_MISMATCH = -3, | ||
| 43 | LENGTHDEL_SIZE_NOT_FOUND = -4, | ||
| 44 | LENGTHDEL_SIZE_MISMATCH = -5, | ||
| 45 | PACKAGE_SIZE_MISMATCH = -6, | ||
| 46 | UNDEFINED_STATE = -7, | ||
| 47 | PARSE_INCOMPLETE = -8, | ||
| 48 | REQUIRED_FIELDS = -9 | ||
| 49 | } | ||
| 50 | |||
| 51 | enum PB_DATA_TYPE { | ||
| 52 | INT32 = 0, | ||
| 53 | SINT32 = 1, | ||
| 54 | UINT32 = 2, | ||
| 55 | INT64 = 3, | ||
| 56 | SINT64 = 4, | ||
| 57 | UINT64 = 5, | ||
| 58 | BOOL = 6, | ||
| 59 | ENUM = 7, | ||
| 60 | FIXED32 = 8, | ||
| 61 | SFIXED32 = 9, | ||
| 62 | FLOAT = 10, | ||
| 63 | FIXED64 = 11, | ||
| 64 | SFIXED64 = 12, | ||
| 65 | DOUBLE = 13, | ||
| 66 | STRING = 14, | ||
| 67 | BYTES = 15, | ||
| 68 | MESSAGE = 16, | ||
| 69 | MAP = 17 | ||
| 70 | } | ||
| 71 | |||
| 72 | const DEFAULT_VALUES_2 = { | ||
| 73 | PB_DATA_TYPE.INT32: null, | ||
| 74 | PB_DATA_TYPE.SINT32: null, | ||
| 75 | PB_DATA_TYPE.UINT32: null, | ||
| 76 | PB_DATA_TYPE.INT64: null, | ||
| 77 | PB_DATA_TYPE.SINT64: null, | ||
| 78 | PB_DATA_TYPE.UINT64: null, | ||
| 79 | PB_DATA_TYPE.BOOL: null, | ||
| 80 | PB_DATA_TYPE.ENUM: null, | ||
| 81 | PB_DATA_TYPE.FIXED32: null, | ||
| 82 | PB_DATA_TYPE.SFIXED32: null, | ||
| 83 | PB_DATA_TYPE.FLOAT: null, | ||
| 84 | PB_DATA_TYPE.FIXED64: null, | ||
| 85 | PB_DATA_TYPE.SFIXED64: null, | ||
| 86 | PB_DATA_TYPE.DOUBLE: null, | ||
| 87 | PB_DATA_TYPE.STRING: null, | ||
| 88 | PB_DATA_TYPE.BYTES: null, | ||
| 89 | PB_DATA_TYPE.MESSAGE: null, | ||
| 90 | PB_DATA_TYPE.MAP: null | ||
| 91 | } | ||
| 92 | |||
| 93 | const DEFAULT_VALUES_3 = { | ||
| 94 | PB_DATA_TYPE.INT32: 0, | ||
| 95 | PB_DATA_TYPE.SINT32: 0, | ||
| 96 | PB_DATA_TYPE.UINT32: 0, | ||
| 97 | PB_DATA_TYPE.INT64: 0, | ||
| 98 | PB_DATA_TYPE.SINT64: 0, | ||
| 99 | PB_DATA_TYPE.UINT64: 0, | ||
| 100 | PB_DATA_TYPE.BOOL: false, | ||
| 101 | PB_DATA_TYPE.ENUM: 0, | ||
| 102 | PB_DATA_TYPE.FIXED32: 0, | ||
| 103 | PB_DATA_TYPE.SFIXED32: 0, | ||
| 104 | PB_DATA_TYPE.FLOAT: 0.0, | ||
| 105 | PB_DATA_TYPE.FIXED64: 0, | ||
| 106 | PB_DATA_TYPE.SFIXED64: 0, | ||
| 107 | PB_DATA_TYPE.DOUBLE: 0.0, | ||
| 108 | PB_DATA_TYPE.STRING: "", | ||
| 109 | PB_DATA_TYPE.BYTES: [], | ||
| 110 | PB_DATA_TYPE.MESSAGE: null, | ||
| 111 | PB_DATA_TYPE.MAP: [] | ||
| 112 | } | ||
| 113 | |||
| 114 | enum PB_TYPE { | ||
| 115 | VARINT = 0, | ||
| 116 | FIX64 = 1, | ||
| 117 | LENGTHDEL = 2, | ||
| 118 | STARTGROUP = 3, | ||
| 119 | ENDGROUP = 4, | ||
| 120 | FIX32 = 5, | ||
| 121 | UNDEFINED = 8 | ||
| 122 | } | ||
| 123 | |||
| 124 | enum PB_RULE { | ||
| 125 | OPTIONAL = 0, | ||
| 126 | REQUIRED = 1, | ||
| 127 | REPEATED = 2, | ||
| 128 | RESERVED = 3 | ||
| 129 | } | ||
| 130 | |||
| 131 | enum PB_SERVICE_STATE { | ||
| 132 | FILLED = 0, | ||
| 133 | UNFILLED = 1 | ||
| 134 | } | ||
| 135 | |||
| 136 | class PBField: | ||
| 137 | func _init(a_name : String, a_type : int, a_rule : int, a_tag : int, packed : bool, a_value = null): | ||
| 138 | name = a_name | ||
| 139 | type = a_type | ||
| 140 | rule = a_rule | ||
| 141 | tag = a_tag | ||
| 142 | option_packed = packed | ||
| 143 | value = a_value | ||
| 144 | |||
| 145 | var name : String | ||
| 146 | var type : int | ||
| 147 | var rule : int | ||
| 148 | var tag : int | ||
| 149 | var option_packed : bool | ||
| 150 | var value | ||
| 151 | var is_map_field : bool = false | ||
| 152 | var option_default : bool = false | ||
| 153 | |||
| 154 | class PBTypeTag: | ||
| 155 | var ok : bool = false | ||
| 156 | var type : int | ||
| 157 | var tag : int | ||
| 158 | var offset : int | ||
| 159 | |||
| 160 | class PBServiceField: | ||
| 161 | var field : PBField | ||
| 162 | var func_ref = null | ||
| 163 | var state : int = PB_SERVICE_STATE.UNFILLED | ||
| 164 | |||
| 165 | class PBPacker: | ||
| 166 | static func convert_signed(n : int) -> int: | ||
| 167 | if n < -2147483648: | ||
| 168 | return (n << 1) ^ (n >> 63) | ||
| 169 | else: | ||
| 170 | return (n << 1) ^ (n >> 31) | ||
| 171 | |||
| 172 | static func deconvert_signed(n : int) -> int: | ||
| 173 | if n & 0x01: | ||
| 174 | return ~(n >> 1) | ||
| 175 | else: | ||
| 176 | return (n >> 1) | ||
| 177 | |||
| 178 | static func pack_varint(value) -> PackedByteArray: | ||
| 179 | var varint : PackedByteArray = PackedByteArray() | ||
| 180 | if typeof(value) == TYPE_BOOL: | ||
| 181 | if value: | ||
| 182 | value = 1 | ||
| 183 | else: | ||
| 184 | value = 0 | ||
| 185 | for _i in range(9): | ||
| 186 | var b = value & 0x7F | ||
| 187 | value >>= 7 | ||
| 188 | if value: | ||
| 189 | varint.append(b | 0x80) | ||
| 190 | else: | ||
| 191 | varint.append(b) | ||
| 192 | break | ||
| 193 | if varint.size() == 9 && (varint[8] & 0x80 != 0): | ||
| 194 | varint.append(0x01) | ||
| 195 | return varint | ||
| 196 | |||
| 197 | static func pack_bytes(value, count : int, data_type : int) -> PackedByteArray: | ||
| 198 | var bytes : PackedByteArray = PackedByteArray() | ||
| 199 | if data_type == PB_DATA_TYPE.FLOAT: | ||
| 200 | var spb : StreamPeerBuffer = StreamPeerBuffer.new() | ||
| 201 | spb.put_float(value) | ||
| 202 | bytes = spb.get_data_array() | ||
| 203 | elif data_type == PB_DATA_TYPE.DOUBLE: | ||
| 204 | var spb : StreamPeerBuffer = StreamPeerBuffer.new() | ||
| 205 | spb.put_double(value) | ||
| 206 | bytes = spb.get_data_array() | ||
| 207 | else: | ||
| 208 | for _i in range(count): | ||
| 209 | bytes.append(value & 0xFF) | ||
| 210 | value >>= 8 | ||
| 211 | return bytes | ||
| 212 | |||
| 213 | static func unpack_bytes(bytes : PackedByteArray, index : int, count : int, data_type : int): | ||
| 214 | if data_type == PB_DATA_TYPE.FLOAT: | ||
| 215 | return bytes.decode_float(index) | ||
| 216 | elif data_type == PB_DATA_TYPE.DOUBLE: | ||
| 217 | return bytes.decode_double(index) | ||
| 218 | else: | ||
| 219 | # Convert to big endian | ||
| 220 | var slice: PackedByteArray = bytes.slice(index, index + count) | ||
| 221 | slice.reverse() | ||
| 222 | return slice | ||
| 223 | |||
| 224 | static func unpack_varint(varint_bytes) -> int: | ||
| 225 | var value : int = 0 | ||
| 226 | var i: int = varint_bytes.size() - 1 | ||
| 227 | while i > -1: | ||
| 228 | value = (value << 7) | (varint_bytes[i] & 0x7F) | ||
| 229 | i -= 1 | ||
| 230 | return value | ||
| 231 | |||
| 232 | static func pack_type_tag(type : int, tag : int) -> PackedByteArray: | ||
| 233 | return pack_varint((tag << 3) | type) | ||
| 234 | |||
| 235 | static func isolate_varint(bytes : PackedByteArray, index : int) -> PackedByteArray: | ||
| 236 | var i: int = index | ||
| 237 | while i <= index + 10: # Protobuf varint max size is 10 bytes | ||
| 238 | if !(bytes[i] & 0x80): | ||
| 239 | return bytes.slice(index, i + 1) | ||
| 240 | i += 1 | ||
| 241 | return [] # Unreachable | ||
| 242 | |||
| 243 | static func unpack_type_tag(bytes : PackedByteArray, index : int) -> PBTypeTag: | ||
| 244 | var varint_bytes : PackedByteArray = isolate_varint(bytes, index) | ||
| 245 | var result : PBTypeTag = PBTypeTag.new() | ||
| 246 | if varint_bytes.size() != 0: | ||
| 247 | result.ok = true | ||
| 248 | result.offset = varint_bytes.size() | ||
| 249 | var unpacked : int = unpack_varint(varint_bytes) | ||
| 250 | result.type = unpacked & 0x07 | ||
| 251 | result.tag = unpacked >> 3 | ||
| 252 | return result | ||
| 253 | |||
| 254 | static func pack_length_delimeted(type : int, tag : int, bytes : PackedByteArray) -> PackedByteArray: | ||
| 255 | var result : PackedByteArray = pack_type_tag(type, tag) | ||
| 256 | result.append_array(pack_varint(bytes.size())) | ||
| 257 | result.append_array(bytes) | ||
| 258 | return result | ||
| 259 | |||
| 260 | static func pb_type_from_data_type(data_type : int) -> int: | ||
| 261 | if data_type == PB_DATA_TYPE.INT32 || data_type == PB_DATA_TYPE.SINT32 || data_type == PB_DATA_TYPE.UINT32 || data_type == PB_DATA_TYPE.INT64 || data_type == PB_DATA_TYPE.SINT64 || data_type == PB_DATA_TYPE.UINT64 || data_type == PB_DATA_TYPE.BOOL || data_type == PB_DATA_TYPE.ENUM: | ||
| 262 | return PB_TYPE.VARINT | ||
| 263 | elif data_type == PB_DATA_TYPE.FIXED32 || data_type == PB_DATA_TYPE.SFIXED32 || data_type == PB_DATA_TYPE.FLOAT: | ||
| 264 | return PB_TYPE.FIX32 | ||
| 265 | elif data_type == PB_DATA_TYPE.FIXED64 || data_type == PB_DATA_TYPE.SFIXED64 || data_type == PB_DATA_TYPE.DOUBLE: | ||
| 266 | return PB_TYPE.FIX64 | ||
| 267 | elif data_type == PB_DATA_TYPE.STRING || data_type == PB_DATA_TYPE.BYTES || data_type == PB_DATA_TYPE.MESSAGE || data_type == PB_DATA_TYPE.MAP: | ||
| 268 | return PB_TYPE.LENGTHDEL | ||
| 269 | else: | ||
| 270 | return PB_TYPE.UNDEFINED | ||
| 271 | |||
| 272 | static func pack_field(field : PBField) -> PackedByteArray: | ||
| 273 | var type : int = pb_type_from_data_type(field.type) | ||
| 274 | var type_copy : int = type | ||
| 275 | if field.rule == PB_RULE.REPEATED && field.option_packed: | ||
| 276 | type = PB_TYPE.LENGTHDEL | ||
| 277 | var head : PackedByteArray = pack_type_tag(type, field.tag) | ||
| 278 | var data : PackedByteArray = PackedByteArray() | ||
| 279 | if type == PB_TYPE.VARINT: | ||
| 280 | var value | ||
| 281 | if field.rule == PB_RULE.REPEATED: | ||
| 282 | for v in field.value: | ||
| 283 | data.append_array(head) | ||
| 284 | if field.type == PB_DATA_TYPE.SINT32 || field.type == PB_DATA_TYPE.SINT64: | ||
| 285 | value = convert_signed(v) | ||
| 286 | else: | ||
| 287 | value = v | ||
| 288 | data.append_array(pack_varint(value)) | ||
| 289 | return data | ||
| 290 | else: | ||
| 291 | if field.type == PB_DATA_TYPE.SINT32 || field.type == PB_DATA_TYPE.SINT64: | ||
| 292 | value = convert_signed(field.value) | ||
| 293 | else: | ||
| 294 | value = field.value | ||
| 295 | data = pack_varint(value) | ||
| 296 | elif type == PB_TYPE.FIX32: | ||
| 297 | if field.rule == PB_RULE.REPEATED: | ||
| 298 | for v in field.value: | ||
| 299 | data.append_array(head) | ||
| 300 | data.append_array(pack_bytes(v, 4, field.type)) | ||
| 301 | return data | ||
| 302 | else: | ||
| 303 | data.append_array(pack_bytes(field.value, 4, field.type)) | ||
| 304 | elif type == PB_TYPE.FIX64: | ||
| 305 | if field.rule == PB_RULE.REPEATED: | ||
| 306 | for v in field.value: | ||
| 307 | data.append_array(head) | ||
| 308 | data.append_array(pack_bytes(v, 8, field.type)) | ||
| 309 | return data | ||
| 310 | else: | ||
| 311 | data.append_array(pack_bytes(field.value, 8, field.type)) | ||
| 312 | elif type == PB_TYPE.LENGTHDEL: | ||
| 313 | if field.rule == PB_RULE.REPEATED: | ||
| 314 | if type_copy == PB_TYPE.VARINT: | ||
| 315 | if field.type == PB_DATA_TYPE.SINT32 || field.type == PB_DATA_TYPE.SINT64: | ||
| 316 | var signed_value : int | ||
| 317 | for v in field.value: | ||
| 318 | signed_value = convert_signed(v) | ||
| 319 | data.append_array(pack_varint(signed_value)) | ||
| 320 | else: | ||
| 321 | for v in field.value: | ||
| 322 | data.append_array(pack_varint(v)) | ||
| 323 | return pack_length_delimeted(type, field.tag, data) | ||
| 324 | elif type_copy == PB_TYPE.FIX32: | ||
| 325 | for v in field.value: | ||
| 326 | data.append_array(pack_bytes(v, 4, field.type)) | ||
| 327 | return pack_length_delimeted(type, field.tag, data) | ||
| 328 | elif type_copy == PB_TYPE.FIX64: | ||
| 329 | for v in field.value: | ||
| 330 | data.append_array(pack_bytes(v, 8, field.type)) | ||
| 331 | return pack_length_delimeted(type, field.tag, data) | ||
| 332 | elif field.type == PB_DATA_TYPE.STRING: | ||
| 333 | for v in field.value: | ||
| 334 | var obj = v.to_utf8_buffer() | ||
| 335 | data.append_array(pack_length_delimeted(type, field.tag, obj)) | ||
| 336 | return data | ||
| 337 | elif field.type == PB_DATA_TYPE.BYTES: | ||
| 338 | for v in field.value: | ||
| 339 | data.append_array(pack_length_delimeted(type, field.tag, v)) | ||
| 340 | return data | ||
| 341 | elif typeof(field.value[0]) == TYPE_OBJECT: | ||
| 342 | for v in field.value: | ||
| 343 | var obj : PackedByteArray = v.to_bytes() | ||
| 344 | data.append_array(pack_length_delimeted(type, field.tag, obj)) | ||
| 345 | return data | ||
| 346 | else: | ||
| 347 | if field.type == PB_DATA_TYPE.STRING: | ||
| 348 | var str_bytes : PackedByteArray = field.value.to_utf8_buffer() | ||
| 349 | if PROTO_VERSION == 2 || (PROTO_VERSION == 3 && str_bytes.size() > 0): | ||
| 350 | data.append_array(str_bytes) | ||
| 351 | return pack_length_delimeted(type, field.tag, data) | ||
| 352 | if field.type == PB_DATA_TYPE.BYTES: | ||
| 353 | if PROTO_VERSION == 2 || (PROTO_VERSION == 3 && field.value.size() > 0): | ||
| 354 | data.append_array(field.value) | ||
| 355 | return pack_length_delimeted(type, field.tag, data) | ||
| 356 | elif typeof(field.value) == TYPE_OBJECT: | ||
| 357 | var obj : PackedByteArray = field.value.to_bytes() | ||
| 358 | if obj.size() > 0: | ||
| 359 | data.append_array(obj) | ||
| 360 | return pack_length_delimeted(type, field.tag, data) | ||
| 361 | else: | ||
| 362 | pass | ||
| 363 | if data.size() > 0: | ||
| 364 | head.append_array(data) | ||
| 365 | return head | ||
| 366 | else: | ||
| 367 | return data | ||
| 368 | |||
| 369 | static func skip_unknown_field(bytes : PackedByteArray, offset : int, type : int) -> int: | ||
| 370 | if type == PB_TYPE.VARINT: | ||
| 371 | return offset + isolate_varint(bytes, offset).size() | ||
| 372 | if type == PB_TYPE.FIX64: | ||
| 373 | return offset + 8 | ||
| 374 | if type == PB_TYPE.LENGTHDEL: | ||
| 375 | var length_bytes : PackedByteArray = isolate_varint(bytes, offset) | ||
| 376 | var length : int = unpack_varint(length_bytes) | ||
| 377 | return offset + length_bytes.size() + length | ||
| 378 | if type == PB_TYPE.FIX32: | ||
| 379 | return offset + 4 | ||
| 380 | return PB_ERR.UNDEFINED_STATE | ||
| 381 | |||
| 382 | static func unpack_field(bytes : PackedByteArray, offset : int, field : PBField, type : int, message_func_ref) -> int: | ||
| 383 | if field.rule == PB_RULE.REPEATED && type != PB_TYPE.LENGTHDEL && field.option_packed: | ||
| 384 | var count = isolate_varint(bytes, offset) | ||
| 385 | if count.size() > 0: | ||
| 386 | offset += count.size() | ||
| 387 | count = unpack_varint(count) | ||
| 388 | if type == PB_TYPE.VARINT: | ||
| 389 | var val | ||
| 390 | var counter = offset + count | ||
| 391 | while offset < counter: | ||
| 392 | val = isolate_varint(bytes, offset) | ||
| 393 | if val.size() > 0: | ||
| 394 | offset += val.size() | ||
| 395 | val = unpack_varint(val) | ||
| 396 | if field.type == PB_DATA_TYPE.SINT32 || field.type == PB_DATA_TYPE.SINT64: | ||
| 397 | val = deconvert_signed(val) | ||
| 398 | elif field.type == PB_DATA_TYPE.BOOL: | ||
| 399 | if val: | ||
| 400 | val = true | ||
| 401 | else: | ||
| 402 | val = false | ||
| 403 | field.value.append(val) | ||
| 404 | else: | ||
| 405 | return PB_ERR.REPEATED_COUNT_MISMATCH | ||
| 406 | return offset | ||
| 407 | elif type == PB_TYPE.FIX32 || type == PB_TYPE.FIX64: | ||
| 408 | var type_size | ||
| 409 | if type == PB_TYPE.FIX32: | ||
| 410 | type_size = 4 | ||
| 411 | else: | ||
| 412 | type_size = 8 | ||
| 413 | var val | ||
| 414 | var counter = offset + count | ||
| 415 | while offset < counter: | ||
| 416 | if (offset + type_size) > bytes.size(): | ||
| 417 | return PB_ERR.REPEATED_COUNT_MISMATCH | ||
| 418 | val = unpack_bytes(bytes, offset, type_size, field.type) | ||
| 419 | offset += type_size | ||
| 420 | field.value.append(val) | ||
| 421 | return offset | ||
| 422 | else: | ||
| 423 | return PB_ERR.REPEATED_COUNT_NOT_FOUND | ||
| 424 | else: | ||
| 425 | if type == PB_TYPE.VARINT: | ||
| 426 | var val = isolate_varint(bytes, offset) | ||
| 427 | if val.size() > 0: | ||
| 428 | offset += val.size() | ||
| 429 | val = unpack_varint(val) | ||
| 430 | if field.type == PB_DATA_TYPE.SINT32 || field.type == PB_DATA_TYPE.SINT64: | ||
| 431 | val = deconvert_signed(val) | ||
| 432 | elif field.type == PB_DATA_TYPE.BOOL: | ||
| 433 | if val: | ||
| 434 | val = true | ||
| 435 | else: | ||
| 436 | val = false | ||
| 437 | if field.rule == PB_RULE.REPEATED: | ||
| 438 | field.value.append(val) | ||
| 439 | else: | ||
| 440 | field.value = val | ||
| 441 | else: | ||
| 442 | return PB_ERR.VARINT_NOT_FOUND | ||
| 443 | return offset | ||
| 444 | elif type == PB_TYPE.FIX32 || type == PB_TYPE.FIX64: | ||
| 445 | var type_size | ||
| 446 | if type == PB_TYPE.FIX32: | ||
| 447 | type_size = 4 | ||
| 448 | else: | ||
| 449 | type_size = 8 | ||
| 450 | var val | ||
| 451 | if (offset + type_size) > bytes.size(): | ||
| 452 | return PB_ERR.REPEATED_COUNT_MISMATCH | ||
| 453 | val = unpack_bytes(bytes, offset, type_size, field.type) | ||
| 454 | offset += type_size | ||
| 455 | if field.rule == PB_RULE.REPEATED: | ||
| 456 | field.value.append(val) | ||
| 457 | else: | ||
| 458 | field.value = val | ||
| 459 | return offset | ||
| 460 | elif type == PB_TYPE.LENGTHDEL: | ||
| 461 | var inner_size = isolate_varint(bytes, offset) | ||
| 462 | if inner_size.size() > 0: | ||
| 463 | offset += inner_size.size() | ||
| 464 | inner_size = unpack_varint(inner_size) | ||
| 465 | if inner_size >= 0: | ||
| 466 | if inner_size + offset > bytes.size(): | ||
| 467 | return PB_ERR.LENGTHDEL_SIZE_MISMATCH | ||
| 468 | if message_func_ref != null: | ||
| 469 | var message = message_func_ref.call() | ||
| 470 | if inner_size > 0: | ||
| 471 | var sub_offset = message.from_bytes(bytes, offset, inner_size + offset) | ||
| 472 | if sub_offset > 0: | ||
| 473 | if sub_offset - offset >= inner_size: | ||
| 474 | offset = sub_offset | ||
| 475 | return offset | ||
| 476 | else: | ||
| 477 | return PB_ERR.LENGTHDEL_SIZE_MISMATCH | ||
| 478 | return sub_offset | ||
| 479 | else: | ||
| 480 | return offset | ||
| 481 | elif field.type == PB_DATA_TYPE.STRING: | ||
| 482 | var str_bytes : PackedByteArray = bytes.slice(offset, inner_size + offset) | ||
| 483 | if field.rule == PB_RULE.REPEATED: | ||
| 484 | field.value.append(str_bytes.get_string_from_utf8()) | ||
| 485 | else: | ||
| 486 | field.value = str_bytes.get_string_from_utf8() | ||
| 487 | return offset + inner_size | ||
| 488 | elif field.type == PB_DATA_TYPE.BYTES: | ||
| 489 | var val_bytes : PackedByteArray = bytes.slice(offset, inner_size + offset) | ||
| 490 | if field.rule == PB_RULE.REPEATED: | ||
| 491 | field.value.append(val_bytes) | ||
| 492 | else: | ||
| 493 | field.value = val_bytes | ||
| 494 | return offset + inner_size | ||
| 495 | else: | ||
| 496 | return PB_ERR.LENGTHDEL_SIZE_NOT_FOUND | ||
| 497 | else: | ||
| 498 | return PB_ERR.LENGTHDEL_SIZE_NOT_FOUND | ||
| 499 | return PB_ERR.UNDEFINED_STATE | ||
| 500 | |||
| 501 | static func unpack_message(data, bytes : PackedByteArray, offset : int, limit : int) -> int: | ||
| 502 | while true: | ||
| 503 | var tt : PBTypeTag = unpack_type_tag(bytes, offset) | ||
| 504 | if tt.ok: | ||
| 505 | offset += tt.offset | ||
| 506 | if data.has(tt.tag): | ||
| 507 | var service : PBServiceField = data[tt.tag] | ||
| 508 | var type : int = pb_type_from_data_type(service.field.type) | ||
| 509 | if type == tt.type || (tt.type == PB_TYPE.LENGTHDEL && service.field.rule == PB_RULE.REPEATED && service.field.option_packed): | ||
| 510 | var res : int = unpack_field(bytes, offset, service.field, type, service.func_ref) | ||
| 511 | if res > 0: | ||
| 512 | service.state = PB_SERVICE_STATE.FILLED | ||
| 513 | offset = res | ||
| 514 | if offset == limit: | ||
| 515 | return offset | ||
| 516 | elif offset > limit: | ||
| 517 | return PB_ERR.PACKAGE_SIZE_MISMATCH | ||
| 518 | elif res < 0: | ||
| 519 | return res | ||
| 520 | else: | ||
| 521 | break | ||
| 522 | else: | ||
| 523 | var res : int = skip_unknown_field(bytes, offset, tt.type) | ||
| 524 | if res > 0: | ||
| 525 | offset = res | ||
| 526 | if offset == limit: | ||
| 527 | return offset | ||
| 528 | elif offset > limit: | ||
| 529 | return PB_ERR.PACKAGE_SIZE_MISMATCH | ||
| 530 | elif res < 0: | ||
| 531 | return res | ||
| 532 | else: | ||
| 533 | break | ||
| 534 | else: | ||
| 535 | return offset | ||
| 536 | return PB_ERR.UNDEFINED_STATE | ||
| 537 | |||
| 538 | static func pack_message(data) -> PackedByteArray: | ||
| 539 | var DEFAULT_VALUES | ||
| 540 | if PROTO_VERSION == 2: | ||
| 541 | DEFAULT_VALUES = DEFAULT_VALUES_2 | ||
| 542 | elif PROTO_VERSION == 3: | ||
| 543 | DEFAULT_VALUES = DEFAULT_VALUES_3 | ||
| 544 | var result : PackedByteArray = PackedByteArray() | ||
| 545 | var keys : Array = data.keys() | ||
| 546 | keys.sort() | ||
| 547 | for i in keys: | ||
| 548 | if data[i].field.value != null: | ||
| 549 | if data[i].state == PB_SERVICE_STATE.UNFILLED \ | ||
| 550 | && !data[i].field.is_map_field \ | ||
| 551 | && typeof(data[i].field.value) == typeof(DEFAULT_VALUES[data[i].field.type]) \ | ||
| 552 | && data[i].field.value == DEFAULT_VALUES[data[i].field.type]: | ||
| 553 | continue | ||
| 554 | elif data[i].field.rule == PB_RULE.REPEATED && data[i].field.value.size() == 0: | ||
| 555 | continue | ||
| 556 | result.append_array(pack_field(data[i].field)) | ||
| 557 | elif data[i].field.rule == PB_RULE.REQUIRED: | ||
| 558 | print("Error: required field is not filled: Tag:", data[i].field.tag) | ||
| 559 | return PackedByteArray() | ||
| 560 | return result | ||
| 561 | |||
| 562 | static func check_required(data) -> bool: | ||
| 563 | var keys : Array = data.keys() | ||
| 564 | for i in keys: | ||
| 565 | if data[i].field.rule == PB_RULE.REQUIRED && data[i].state == PB_SERVICE_STATE.UNFILLED: | ||
| 566 | return false | ||
| 567 | return true | ||
| 568 | |||
| 569 | static func construct_map(key_values): | ||
| 570 | var result = {} | ||
| 571 | for kv in key_values: | ||
| 572 | result[kv.get_key()] = kv.get_value() | ||
| 573 | return result | ||
| 574 | |||
| 575 | static func tabulate(text : String, nesting : int) -> String: | ||
| 576 | var tab : String = "" | ||
| 577 | for _i in range(nesting): | ||
| 578 | tab += DEBUG_TAB | ||
| 579 | return tab + text | ||
| 580 | |||
| 581 | static func value_to_string(value, field : PBField, nesting : int) -> String: | ||
| 582 | var result : String = "" | ||
| 583 | var text : String | ||
| 584 | if field.type == PB_DATA_TYPE.MESSAGE: | ||
| 585 | result += "{" | ||
| 586 | nesting += 1 | ||
| 587 | text = message_to_string(value.data, nesting) | ||
| 588 | if text != "": | ||
| 589 | result += "\n" + text | ||
| 590 | nesting -= 1 | ||
| 591 | result += tabulate("}", nesting) | ||
| 592 | else: | ||
| 593 | nesting -= 1 | ||
| 594 | result += "}" | ||
| 595 | elif field.type == PB_DATA_TYPE.BYTES: | ||
| 596 | result += "<" | ||
| 597 | for i in range(value.size()): | ||
| 598 | result += str(value[i]) | ||
| 599 | if i != (value.size() - 1): | ||
| 600 | result += ", " | ||
| 601 | result += ">" | ||
| 602 | elif field.type == PB_DATA_TYPE.STRING: | ||
| 603 | result += "\"" + value + "\"" | ||
| 604 | elif field.type == PB_DATA_TYPE.ENUM: | ||
| 605 | result += "ENUM::" + str(value) | ||
| 606 | else: | ||
| 607 | result += str(value) | ||
| 608 | return result | ||
| 609 | |||
| 610 | static func field_to_string(field : PBField, nesting : int) -> String: | ||
| 611 | var result : String = tabulate(field.name + ": ", nesting) | ||
| 612 | if field.type == PB_DATA_TYPE.MAP: | ||
| 613 | if field.value.size() > 0: | ||
| 614 | result += "(\n" | ||
| 615 | nesting += 1 | ||
| 616 | for i in range(field.value.size()): | ||
| 617 | var local_key_value = field.value[i].data[1].field | ||
| 618 | result += tabulate(value_to_string(local_key_value.value, local_key_value, nesting), nesting) + ": " | ||
| 619 | local_key_value = field.value[i].data[2].field | ||
| 620 | result += value_to_string(local_key_value.value, local_key_value, nesting) | ||
| 621 | if i != (field.value.size() - 1): | ||
| 622 | result += "," | ||
| 623 | result += "\n" | ||
| 624 | nesting -= 1 | ||
| 625 | result += tabulate(")", nesting) | ||
| 626 | else: | ||
| 627 | result += "()" | ||
| 628 | elif field.rule == PB_RULE.REPEATED: | ||
| 629 | if field.value.size() > 0: | ||
| 630 | result += "[\n" | ||
| 631 | nesting += 1 | ||
| 632 | for i in range(field.value.size()): | ||
| 633 | result += tabulate(str(i) + ": ", nesting) | ||
| 634 | result += value_to_string(field.value[i], field, nesting) | ||
| 635 | if i != (field.value.size() - 1): | ||
| 636 | result += "," | ||
| 637 | result += "\n" | ||
| 638 | nesting -= 1 | ||
| 639 | result += tabulate("]", nesting) | ||
| 640 | else: | ||
| 641 | result += "[]" | ||
| 642 | else: | ||
| 643 | result += value_to_string(field.value, field, nesting) | ||
| 644 | result += ";\n" | ||
| 645 | return result | ||
| 646 | |||
| 647 | static func message_to_string(data, nesting : int = 0) -> String: | ||
| 648 | var DEFAULT_VALUES | ||
| 649 | if PROTO_VERSION == 2: | ||
| 650 | DEFAULT_VALUES = DEFAULT_VALUES_2 | ||
| 651 | elif PROTO_VERSION == 3: | ||
| 652 | DEFAULT_VALUES = DEFAULT_VALUES_3 | ||
| 653 | var result : String = "" | ||
| 654 | var keys : Array = data.keys() | ||
| 655 | keys.sort() | ||
| 656 | for i in keys: | ||
| 657 | if data[i].field.value != null: | ||
| 658 | if data[i].state == PB_SERVICE_STATE.UNFILLED \ | ||
| 659 | && !data[i].field.is_map_field \ | ||
| 660 | && typeof(data[i].field.value) == typeof(DEFAULT_VALUES[data[i].field.type]) \ | ||
| 661 | && data[i].field.value == DEFAULT_VALUES[data[i].field.type]: | ||
| 662 | continue | ||
| 663 | elif data[i].field.rule == PB_RULE.REPEATED && data[i].field.value.size() == 0: | ||
| 664 | continue | ||
| 665 | result += field_to_string(data[i].field, nesting) | ||
| 666 | elif data[i].field.rule == PB_RULE.REQUIRED: | ||
| 667 | result += data[i].field.name + ": " + "error" | ||
| 668 | return result | ||
| diff --git a/vendor/godobuf/addons/protobuf/protobuf_util.gd b/vendor/godobuf/addons/protobuf/protobuf_util.gd new file mode 100644 index 0000000..5941cb8 --- /dev/null +++ b/vendor/godobuf/addons/protobuf/protobuf_util.gd | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | # | ||
| 2 | # BSD 3-Clause License | ||
| 3 | # | ||
| 4 | # Copyright (c) 2018, Oleg Malyavkin | ||
| 5 | # All rights reserved. | ||
| 6 | # | ||
| 7 | # Redistribution and use in source and binary forms, with or without | ||
| 8 | # modification, are permitted provided that the following conditions are met: | ||
| 9 | # | ||
| 10 | # * Redistributions of source code must retain the above copyright notice, this | ||
| 11 | # list of conditions and the following disclaimer. | ||
| 12 | # | ||
| 13 | # * Redistributions in binary form must reproduce the above copyright notice, | ||
| 14 | # this list of conditions and the following disclaimer in the documentation | ||
| 15 | # and/or other materials provided with the distribution. | ||
| 16 | # | ||
| 17 | # * Neither the name of the copyright holder nor the names of its | ||
| 18 | # contributors may be used to endorse or promote products derived from | ||
| 19 | # this software without specific prior written permission. | ||
| 20 | # | ||
| 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 22 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 23 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
| 24 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
| 25 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 26 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 27 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
| 29 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | |||
| 32 | static func extract_dir(file_path): | ||
| 33 | var parts = file_path.split("/", false) | ||
| 34 | parts.remove_at(parts.size() - 1) | ||
| 35 | var path | ||
| 36 | if file_path.begins_with("/"): | ||
| 37 | path = "/" | ||
| 38 | else: | ||
| 39 | path = "" | ||
| 40 | for part in parts: | ||
| 41 | path += part + "/" | ||
| 42 | return path | ||
| 43 | |||
| 44 | static func extract_filename(file_path): | ||
| 45 | var parts = file_path.split("/", false) | ||
| 46 | return parts[parts.size() - 1] | ||
| diff --git a/vendor/godobuf/default_env.tres b/vendor/godobuf/default_env.tres new file mode 100644 index 0000000..20207a4 --- /dev/null +++ b/vendor/godobuf/default_env.tres | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | [gd_resource type="Environment" load_steps=2 format=2] | ||
| 2 | |||
| 3 | [sub_resource type="ProceduralSky" id=1] | ||
| 4 | |||
| 5 | [resource] | ||
| 6 | background_mode = 2 | ||
| 7 | background_sky = SubResource( 1 ) | ||
| diff --git a/vendor/godobuf/logo.png b/vendor/godobuf/logo.png new file mode 100644 index 0000000..4ff9029 --- /dev/null +++ b/vendor/godobuf/logo.png | |||
| Binary files differ | |||
| diff --git a/vendor/godobuf/logo.png.import b/vendor/godobuf/logo.png.import new file mode 100644 index 0000000..43df7a6 --- /dev/null +++ b/vendor/godobuf/logo.png.import | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | [remap] | ||
| 2 | |||
| 3 | importer="texture" | ||
| 4 | type="StreamTexture" | ||
| 5 | path="res://.import/logo.png-cca8726399059c8d4f806e28e356b14d.stex" | ||
| 6 | metadata={ | ||
| 7 | "vram_texture": false | ||
| 8 | } | ||
| 9 | |||
| 10 | [deps] | ||
| 11 | |||
| 12 | source_file="res://logo.png" | ||
| 13 | dest_files=[ "res://.import/logo.png-cca8726399059c8d4f806e28e356b14d.stex" ] | ||
| 14 | |||
| 15 | [params] | ||
| 16 | |||
| 17 | compress/mode=0 | ||
| 18 | compress/lossy_quality=0.7 | ||
| 19 | compress/hdr_mode=0 | ||
| 20 | compress/bptc_ldr=0 | ||
| 21 | compress/normal_map=0 | ||
| 22 | flags/repeat=0 | ||
| 23 | flags/filter=true | ||
| 24 | flags/mipmaps=false | ||
| 25 | flags/anisotropic=false | ||
| 26 | flags/srgb=2 | ||
| 27 | process/fix_alpha_border=true | ||
| 28 | process/premult_alpha=false | ||
| 29 | process/HDR_as_SRGB=false | ||
| 30 | process/invert_color=false | ||
| 31 | process/normal_map_invert_y=false | ||
| 32 | stream=false | ||
| 33 | size_limit=0 | ||
| 34 | detect_3d=true | ||
| 35 | svg/scale=1.0 | ||
| diff --git a/vendor/godobuf/project.godot b/vendor/godobuf/project.godot new file mode 100644 index 0000000..8cef0a4 --- /dev/null +++ b/vendor/godobuf/project.godot | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | ; Engine configuration file. | ||
| 2 | ; It's best edited using the editor UI and not directly, | ||
| 3 | ; since the parameters that go here are not all obvious. | ||
| 4 | ; | ||
| 5 | ; Format: | ||
| 6 | ; [section] ; section goes between [] | ||
| 7 | ; param=value ; assign values to parameters | ||
| 8 | |||
| 9 | config_version=4 | ||
| 10 | |||
| 11 | _global_script_classes=[ ] | ||
| 12 | _global_script_class_icons={ | ||
| 13 | } | ||
| 14 | |||
| 15 | [application] | ||
| 16 | |||
| 17 | config/name="Protobuf Plugin" | ||
| 18 | config/icon="res://logo.png" | ||
| 19 | |||
| 20 | [editor_plugins] | ||
| 21 | |||
| 22 | enabled=PoolStringArray( "res://addons/protobuf/plugin.cfg" ) | ||
| 23 | |||
| 24 | [rendering] | ||
| 25 | |||
| 26 | environment/default_environment="res://default_env.tres" | ||
