about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md307
-rw-r--r--README.md276
-rw-r--r--apworld/README.md48
-rw-r--r--apworld/__init__.py111
-rw-r--r--apworld/client/allowNumbers.gd10
-rw-r--r--apworld/client/animationListener.gd (renamed from client/Archipelago/animationListener.gd)0
-rw-r--r--apworld/client/apworld_runtime.gd49
-rw-r--r--apworld/client/assets/goal.pngbin0 -> 215 bytes
-rw-r--r--apworld/client/assets/location.pngbin0 -> 311 bytes
-rw-r--r--apworld/client/assets/worldport.pngbin0 -> 219 bytes
-rw-r--r--apworld/client/client.gd318
-rw-r--r--apworld/client/collectable.gd (renamed from client/Archipelago/collectable.gd)0
-rw-r--r--apworld/client/compass.gd66
-rw-r--r--apworld/client/compass_overlay.gd17
-rw-r--r--apworld/client/door.gd75
-rw-r--r--apworld/client/effects.gd32
-rw-r--r--apworld/client/gamedata.gd302
-rw-r--r--apworld/client/keyHolder.gd (renamed from client/Archipelago/keyHolder.gd)0
-rw-r--r--apworld/client/keyHolderChecker.gd (renamed from client/Archipelago/keyHolderChecker.gd)0
-rw-r--r--apworld/client/keyHolderResetterListener.gd (renamed from client/Archipelago/keyHolderResetterListener.gd)0
-rw-r--r--apworld/client/keyboard.gd (renamed from client/Archipelago/keyboard.gd)61
-rw-r--r--apworld/client/locationListener.gd (renamed from client/Archipelago/locationListener.gd)0
-rw-r--r--apworld/client/main.gd308
-rw-r--r--apworld/client/manager.gd717
-rw-r--r--apworld/client/maps/control_center.gd85
-rw-r--r--apworld/client/maps/daedalus.gd85
-rw-r--r--apworld/client/maps/icarus.gd38
-rw-r--r--apworld/client/maps/the_advanced.gd36
-rw-r--r--apworld/client/maps/the_charismatic.gd26
-rw-r--r--apworld/client/maps/the_crystalline.gd34
-rw-r--r--apworld/client/maps/the_entry.gd156
-rw-r--r--apworld/client/maps/the_fuzzy.gd25
-rw-r--r--apworld/client/maps/the_parthenon.gd51
-rw-r--r--apworld/client/maps/the_plaza.gd4
-rw-r--r--apworld/client/maps/the_stellar.gd30
-rw-r--r--apworld/client/maps/the_sun_temple.gd56
-rw-r--r--apworld/client/maps/the_unkempt.gd4
-rw-r--r--apworld/client/maps/the_unyielding.gd5
-rw-r--r--apworld/client/messages.gd (renamed from client/Archipelago/messages.gd)21
-rw-r--r--apworld/client/minimap.gd178
-rw-r--r--apworld/client/painting.gd (renamed from client/Archipelago/painting.gd)0
-rw-r--r--apworld/client/paintingAuto.gd43
-rw-r--r--apworld/client/panel.gd101
-rw-r--r--apworld/client/pauseMenu.gd91
-rw-r--r--apworld/client/player.gd (renamed from client/Archipelago/player.gd)118
-rw-r--r--apworld/client/rainbowText.gd10
-rw-r--r--apworld/client/run_from_apworld.tscn30
-rw-r--r--apworld/client/run_from_source.tscn22
-rw-r--r--apworld/client/saver.gd23
-rw-r--r--apworld/client/settings_screen.gd149
-rw-r--r--apworld/client/source_runtime.gd33
-rw-r--r--apworld/client/teleport.gd (renamed from client/Archipelago/door.gd)2
-rw-r--r--apworld/client/teleportListener.gd (renamed from client/Archipelago/teleportListener.gd)11
-rw-r--r--apworld/client/textclient.gd508
-rw-r--r--apworld/client/unlockReaderListener.gd46
-rw-r--r--apworld/client/vendor/LICENSE21
-rw-r--r--apworld/client/vendor/WebSocketServer.gd173
-rw-r--r--apworld/client/victoryListener.gd (renamed from client/Archipelago/victoryListener.gd)0
-rw-r--r--apworld/client/visibilityListener.gd38
-rw-r--r--apworld/client/worldport.gd61
-rw-r--r--apworld/client/worldportListener.gd8
-rw-r--r--apworld/context.py800
-rw-r--r--apworld/docs/en_Lingo_2.md4
-rw-r--r--apworld/items.py26
-rw-r--r--apworld/locations.py3
-rw-r--r--apworld/logo.pngbin0 -> 9429 bytes
-rw-r--r--apworld/options.py128
-rw-r--r--apworld/player_logic.py315
-rw-r--r--apworld/regions.py185
-rw-r--r--apworld/requirements.txt2
-rw-r--r--apworld/rules.py38
-rw-r--r--apworld/static_logic.py33
-rw-r--r--apworld/tracker.py146
-rw-r--r--client/Archipelago/client.gd415
-rw-r--r--client/Archipelago/gamedata.gd86
-rw-r--r--client/Archipelago/manager.gd507
-rw-r--r--client/Archipelago/pauseMenu.gd12
-rw-r--r--client/Archipelago/saver.gd9
-rw-r--r--client/Archipelago/settings_buttons.gd24
-rw-r--r--client/Archipelago/settings_screen.gd199
-rw-r--r--client/Archipelago/textclient.gd86
-rw-r--r--client/Archipelago/vendor/LICENSE21
-rw-r--r--client/Archipelago/vendor/uuid.gd195
-rw-r--r--client/Archipelago/worldportListener.gd8
-rw-r--r--client/README.md97
-rw-r--r--client/archipelago.tscn281
-rw-r--r--data/MISSING PANELS.txt32
-rw-r--r--data/connections.txtpb359
-rw-r--r--data/door_groups.txtpb13
-rw-r--r--data/ids.yaml975
-rw-r--r--data/maps/control_center/doors.txtpb42
-rw-r--r--data/maps/control_center/rooms/Ancient Entrance.txtpb7
-rw-r--r--data/maps/control_center/rooms/Between Entrance.txtpb3
-rw-r--r--data/maps/control_center/rooms/Entry Entrance.txtpb3
-rw-r--r--data/maps/control_center/rooms/Entry.txtpb3
-rw-r--r--data/maps/control_center/rooms/Main Area.txtpb13
-rw-r--r--data/maps/control_center/rooms/Partial Entrance.txtpb3
-rw-r--r--data/maps/control_center/rooms/Perceptive Entrance.txtpb3
-rw-r--r--data/maps/control_center/rooms/Repetitive Entrance.txtpb3
-rw-r--r--data/maps/control_center/rooms/Tenacious Entrance.txtpb3
-rw-r--r--data/maps/control_center/rooms/Unkempt Entrance.txtpb3
-rw-r--r--data/maps/daedalus/connections.txtpb45
-rw-r--r--data/maps/daedalus/doors.txtpb293
-rw-r--r--data/maps/daedalus/rooms/C Keyholder.txtpb2
-rw-r--r--data/maps/daedalus/rooms/Composite Room S.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Entry Shortcut.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Hedges Tower.txtpb2
-rw-r--r--data/maps/daedalus/rooms/Hotel.txtpb2
-rw-r--r--data/maps/daedalus/rooms/Moat.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Outside Hedges.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Purple Hallway From Great.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Quiet Entrance.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Rain Side.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Red Color Door.txtpb2
-rw-r--r--data/maps/daedalus/rooms/Starting Room.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Sweet Foyer.txtpb6
-rw-r--r--data/maps/daedalus/rooms/Tree Entrance.txtpb3
-rw-r--r--data/maps/daedalus/rooms/Unkempt Entrance.txtpb3
-rw-r--r--data/maps/daedalus/rooms/White Hallway From Entry.txtpb9
-rw-r--r--data/maps/daedalus/rooms/Wonderland.txtpb4
-rw-r--r--data/maps/daedalus/rooms/Yellow Color Door.txtpb3
-rw-r--r--data/maps/demo/connections.txtpb30
-rw-r--r--data/maps/demo/doors.txtpb161
-rw-r--r--data/maps/demo/metadata.txtpb6
-rw-r--r--data/maps/demo/rooms/Backside Area.txtpb25
-rw-r--r--data/maps/demo/rooms/Castle.txtpb13
-rw-r--r--data/maps/demo/rooms/Center Building.txtpb13
-rw-r--r--data/maps/demo/rooms/Flower Hallway.txtpb7
-rw-r--r--data/maps/demo/rooms/Main Area.txtpb241
-rw-r--r--data/maps/demo/rooms/Mastery.txtpb5
-rw-r--r--data/maps/demo/rooms/Tower.txtpb7
-rw-r--r--data/maps/four_rooms/rooms/Examples Room.txtpb4
-rw-r--r--data/maps/four_rooms/rooms/Intensify Room.txtpb4
-rw-r--r--data/maps/four_rooms/rooms/Synonyms Room.txtpb4
-rw-r--r--data/maps/four_rooms/rooms/Time Room.txtpb4
-rw-r--r--data/maps/icarus/connections.txtpb766
-rw-r--r--data/maps/icarus/doors.txtpb286
-rw-r--r--data/maps/icarus/metadata.txtpb4
-rw-r--r--data/maps/icarus/rooms/Above Trans Rights.txtpb12
-rw-r--r--data/maps/icarus/rooms/Banana Belt Door.txtpb5
-rw-r--r--data/maps/icarus/rooms/Behind Welcome Spine.txtpb1
-rw-r--r--data/maps/icarus/rooms/Big U.txtpb40
-rw-r--r--data/maps/icarus/rooms/Cow Quicktravel.txtpb14
-rw-r--r--data/maps/icarus/rooms/Fatherland Quicktravel.txtpb10
-rw-r--r--data/maps/icarus/rooms/Fatherland.txtpb16
-rw-r--r--data/maps/icarus/rooms/Highest Point.txtpb19
-rw-r--r--data/maps/icarus/rooms/Mastery.txtpb5
-rw-r--r--data/maps/icarus/rooms/Maze Back.txtpb8
-rw-r--r--data/maps/icarus/rooms/Maze King Painting.txtpb8
-rw-r--r--data/maps/icarus/rooms/Maze King Panel.txtpb8
-rw-r--r--data/maps/icarus/rooms/Maze Wings Passage.txtpb9
-rw-r--r--data/maps/icarus/rooms/Maze.txtpb60
-rw-r--r--data/maps/icarus/rooms/Mediums Quicktravel.txtpb10
-rw-r--r--data/maps/icarus/rooms/Mini Icarus 2.txtpb45
-rw-r--r--data/maps/icarus/rooms/Mini Icarus 3.txtpb1
-rw-r--r--data/maps/icarus/rooms/Mini Icarus Sun Loop.txtpb22
-rw-r--r--data/maps/icarus/rooms/Mini Icarus Wings Painting.txtpb5
-rw-r--r--data/maps/icarus/rooms/Painting Maze 1.txtpb13
-rw-r--r--data/maps/icarus/rooms/Painting Maze 2.txtpb13
-rw-r--r--data/maps/icarus/rooms/Patricide Room.txtpb9
-rw-r--r--data/maps/icarus/rooms/Pillar Ramp.txtpb65
-rw-r--r--data/maps/icarus/rooms/Spiral Ramp.txtpb29
-rw-r--r--data/maps/icarus/rooms/The Orb.txtpb117
-rw-r--r--data/maps/icarus/rooms/Through Woman (Obverse).txtpb28
-rw-r--r--data/maps/icarus/rooms/Through Woman (Reverse).txtpb19
-rw-r--r--data/maps/icarus/rooms/Trans Rights Panels.txtpb22
-rw-r--r--data/maps/icarus/rooms/Trans Rights.txtpb25
-rw-r--r--data/maps/icarus/rooms/Welcome Spine (Obverse).txtpb22
-rw-r--r--data/maps/icarus/rooms/Welcome Spine (Reverse).txtpb22
-rw-r--r--data/maps/icarus/rooms/Welcome Spine Quicktravel.txtpb10
-rw-r--r--data/maps/the_advanced/connections.txtpb10
-rw-r--r--data/maps/the_advanced/doors.txtpb50
-rw-r--r--data/maps/the_advanced/metadata.txtpb4
-rw-r--r--data/maps/the_advanced/rooms/CBA.txtpb22
-rw-r--r--data/maps/the_advanced/rooms/Main Area.txtpb200
-rw-r--r--data/maps/the_advanced/rooms/Mastery.txtpb5
-rw-r--r--data/maps/the_ancient/rooms/Outside.txtpb1
-rw-r--r--data/maps/the_bearer/connections.txtpb5
-rw-r--r--data/maps/the_bearer/doors.txtpb16
-rw-r--r--data/maps/the_bearer/rooms/Back Area.txtpb10
-rw-r--r--data/maps/the_bearer/rooms/Entry.txtpb4
-rw-r--r--data/maps/the_bearer/rooms/Tree Entrance.txtpb8
-rw-r--r--data/maps/the_between/rooms/Control Center Side.txtpb8
-rw-r--r--data/maps/the_between/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_between/rooms/Plaza Entrance.txtpb4
-rw-r--r--data/maps/the_butterfly/doors.txtpb1
-rw-r--r--data/maps/the_butterfly/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_charismatic/connections.txtpb35
-rw-r--r--data/maps/the_charismatic/doors.txtpb56
-rw-r--r--data/maps/the_charismatic/metadata.txtpb4
-rw-r--r--data/maps/the_charismatic/rooms/Latitude Middle.txtpb8
-rw-r--r--data/maps/the_charismatic/rooms/Latitude North.txtpb8
-rw-r--r--data/maps/the_charismatic/rooms/Latitude South.txtpb8
-rw-r--r--data/maps/the_charismatic/rooms/Longitude East.txtpb8
-rw-r--r--data/maps/the_charismatic/rooms/Longitude Middle.txtpb8
-rw-r--r--data/maps/the_charismatic/rooms/Longitude West.txtpb8
-rw-r--r--data/maps/the_charismatic/rooms/Main Area.txtpb78
-rw-r--r--data/maps/the_charismatic/rooms/Mastery.txtpb5
-rw-r--r--data/maps/the_colorful/doors.txtpb8
-rw-r--r--data/maps/the_colorful/rooms/Cyan Hallway.txtpb8
-rw-r--r--data/maps/the_colorful/rooms/White Room.txtpb4
-rw-r--r--data/maps/the_congruent/doors.txtpb12
-rw-r--r--data/maps/the_congruent/metadata.txtpb4
-rw-r--r--data/maps/the_congruent/rooms/C Keyholder.txtpb1
-rw-r--r--data/maps/the_congruent/rooms/G Keyholder.txtpb1
-rw-r--r--data/maps/the_congruent/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_crystalline/connections.txtpb26
-rw-r--r--data/maps/the_crystalline/doors.txtpb14
-rw-r--r--data/maps/the_crystalline/metadata.txtpb4
-rw-r--r--data/maps/the_crystalline/rooms/Flip Area.txtpb14
-rw-r--r--data/maps/the_crystalline/rooms/Main Area.txtpb29
-rw-r--r--data/maps/the_crystalline/rooms/Mastery.txtpb5
-rw-r--r--data/maps/the_crystalline/rooms/Painting Divot.txtpb5
-rw-r--r--data/maps/the_darkroom/connections.txtpb29
-rw-r--r--data/maps/the_darkroom/doors.txtpb1
-rw-r--r--data/maps/the_darkroom/rooms/Congruent Entrance.txtpb9
-rw-r--r--data/maps/the_darkroom/rooms/Cyan Hallway.txtpb9
-rw-r--r--data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb9
-rw-r--r--data/maps/the_darkroom/rooms/First Room Exit.txtpb9
-rw-r--r--data/maps/the_darkroom/rooms/First Room.txtpb22
-rw-r--r--data/maps/the_darkroom/rooms/Second Room Exit.txtpb9
-rw-r--r--data/maps/the_darkroom/rooms/Second Room.txtpb16
-rw-r--r--data/maps/the_darkroom/rooms/Third Room.txtpb4
-rw-r--r--data/maps/the_digital/connections.txtpb5
-rw-r--r--data/maps/the_digital/doors.txtpb8
-rw-r--r--data/maps/the_digital/rooms/Gallery Maze.txtpb4
-rw-r--r--data/maps/the_digital/rooms/Main Area.txtpb12
-rw-r--r--data/maps/the_digital/rooms/Tree Area.txtpb5
-rw-r--r--data/maps/the_digital/rooms/Unyielding Entrance.txtpb4
-rw-r--r--data/maps/the_double_sided/doors.txtpb79
-rw-r--r--data/maps/the_double_sided/metadata.txtpb4
-rw-r--r--data/maps/the_double_sided/rooms/Start.txtpb4
-rw-r--r--data/maps/the_entry/connections.txtpb48
-rw-r--r--data/maps/the_entry/doors.txtpb107
-rw-r--r--data/maps/the_entry/metadata.txtpb10
-rw-r--r--data/maps/the_entry/rooms/Composite Room Entrance.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Daedalus Entrance.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Digital Entrance.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Entry Exit.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Eye Room.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Flipped Pyramid Area.txtpb18
-rw-r--r--data/maps/the_entry/rooms/Flipped Second Room.txtpb7
-rw-r--r--data/maps/the_entry/rooms/Four Rooms Entrance.txtpb9
-rw-r--r--data/maps/the_entry/rooms/Gallery Return.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Least Blue Last.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Liberated Entrance Panel.txtpb9
-rw-r--r--data/maps/the_entry/rooms/Liberated Entrance.txtpb8
-rw-r--r--data/maps/the_entry/rooms/Lime Room.txtpb6
-rw-r--r--data/maps/the_entry/rooms/Link Area.txtpb12
-rw-r--r--data/maps/the_entry/rooms/Literate Entrance Panel.txtpb9
-rw-r--r--data/maps/the_entry/rooms/Literate Entrance.txtpb8
-rw-r--r--data/maps/the_entry/rooms/Parthenon Return.txtpb6
-rw-r--r--data/maps/the_entry/rooms/Rabbit Hole.txtpb8
-rw-r--r--data/maps/the_entry/rooms/Repetitive Entrance.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Revitalized Entrance.txtpb9
-rw-r--r--data/maps/the_entry/rooms/Shop Entrance.txtpb6
-rw-r--r--data/maps/the_entry/rooms/Starting Room.txtpb23
-rw-r--r--data/maps/the_entry/rooms/White Hallway To Daedalus.txtpb4
-rw-r--r--data/maps/the_entry/rooms/X Area.txtpb4
-rw-r--r--data/maps/the_extravagant/rooms/Engine Room.txtpb4
-rw-r--r--data/maps/the_fuzzy/connections.txtpb5
-rw-r--r--data/maps/the_fuzzy/doors.txtpb29
-rw-r--r--data/maps/the_fuzzy/metadata.txtpb4
-rw-r--r--data/maps/the_fuzzy/rooms/Main Area.txtpb119
-rw-r--r--data/maps/the_fuzzy/rooms/Mastery.txtpb5
-rw-r--r--data/maps/the_gallery/doors.txtpb71
-rw-r--r--data/maps/the_gallery/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_gold/doors.txtpb7
-rw-r--r--data/maps/the_graveyard/doors.txtpb7
-rw-r--r--data/maps/the_great/connections.txtpb4
-rw-r--r--data/maps/the_great/doors.txtpb160
-rw-r--r--data/maps/the_great/rooms/Back Area.txtpb102
-rw-r--r--data/maps/the_great/rooms/Colorful Entrance.txtpb4
-rw-r--r--data/maps/the_great/rooms/Daedalus Entrance.txtpb4
-rw-r--r--data/maps/the_great/rooms/Hive Entrance.txtpb4
-rw-r--r--data/maps/the_great/rooms/Jubilant Entrance.txtpb4
-rw-r--r--data/maps/the_great/rooms/Main Area.txtpb20
-rw-r--r--data/maps/the_great/rooms/Maze Tower.txtpb1
-rw-r--r--data/maps/the_great/rooms/North Landscape.txtpb5
-rw-r--r--data/maps/the_great/rooms/Purple Room.txtpb4
-rw-r--r--data/maps/the_great/rooms/Salmon Room.txtpb4
-rw-r--r--data/maps/the_great/rooms/Talented Entrance.txtpb4
-rw-r--r--data/maps/the_great/rooms/The Landscapes.txtpb88
-rw-r--r--data/maps/the_great/rooms/West Side.txtpb13
-rw-r--r--data/maps/the_great/rooms/Whole Room.txtpb2
-rw-r--r--data/maps/the_hinterlands/rooms/Main Area.txtpb8
-rw-r--r--data/maps/the_hive/rooms/Main Area.txtpb16
-rw-r--r--data/maps/the_impressive/doors.txtpb17
-rw-r--r--data/maps/the_impressive/rooms/Green Eye.txtpb4
-rw-r--r--data/maps/the_impressive/rooms/Lobby.txtpb4
-rw-r--r--data/maps/the_impressive/rooms/Side Area.txtpb4
-rw-r--r--data/maps/the_invisible/rooms/Entrance.txtpb4
-rw-r--r--data/maps/the_invisible/rooms/Maze.txtpb5
-rw-r--r--data/maps/the_jubilant/doors.txtpb11
-rw-r--r--data/maps/the_jubilant/metadata.txtpb4
-rw-r--r--data/maps/the_jubilant/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_keen/metadata.txtpb4
-rw-r--r--data/maps/the_keen/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_liberated/metadata.txtpb4
-rw-r--r--data/maps/the_liberated/rooms/Puzzle Room.txtpb4
-rw-r--r--data/maps/the_linear/metadata.txtpb4
-rw-r--r--data/maps/the_linear/rooms/Room.txtpb4
-rw-r--r--data/maps/the_lionized/metadata.txtpb4
-rw-r--r--data/maps/the_lionized/rooms/Puzzle Room.txtpb4
-rw-r--r--data/maps/the_literate/metadata.txtpb4
-rw-r--r--data/maps/the_literate/rooms/Puzzle Room.txtpb4
-rw-r--r--data/maps/the_lively/metadata.txtpb4
-rw-r--r--data/maps/the_lively/rooms/Puzzle Room.txtpb3
-rw-r--r--data/maps/the_nuanced/doors.txtpb11
-rw-r--r--data/maps/the_nuanced/metadata.txtpb4
-rw-r--r--data/maps/the_nuanced/rooms/Main Room.txtpb4
-rw-r--r--data/maps/the_orb/connections.txtpb12
-rw-r--r--data/maps/the_orb/rooms/B Room.txtpb15
-rw-r--r--data/maps/the_orb/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_orb/rooms/Middle Room.txtpb12
-rw-r--r--data/maps/the_owl/doors.txtpb131
-rw-r--r--data/maps/the_owl/rooms/Connected Area.txtpb5
-rw-r--r--data/maps/the_owl/rooms/Magenta Hallway.txtpb4
-rw-r--r--data/maps/the_owl/rooms/R2C2 Bottom.txtpb4
-rw-r--r--data/maps/the_parthenon/connections.txtpb1
-rw-r--r--data/maps/the_parthenon/doors.txtpb9
-rw-r--r--data/maps/the_parthenon/rooms/Main Area.txtpb12
-rw-r--r--data/maps/the_partial/doors.txtpb6
-rw-r--r--data/maps/the_partial/rooms/Control Center Entrance.txtpb4
-rw-r--r--data/maps/the_partial/rooms/Obverse Side.txtpb4
-rw-r--r--data/maps/the_perceptive/metadata.txtpb4
-rw-r--r--data/maps/the_perceptive/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_plaza/connections.txtpb8
-rw-r--r--data/maps/the_plaza/doors.txtpb44
-rw-r--r--data/maps/the_plaza/rooms/Main Area.txtpb12
-rw-r--r--data/maps/the_plaza/rooms/Repetitive Entrance.txtpb4
-rw-r--r--data/maps/the_plaza/rooms/Sirenic Entrance.txtpb4
-rw-r--r--data/maps/the_plaza/rooms/Symbolic Entrance.txtpb4
-rw-r--r--data/maps/the_quiet/metadata.txtpb4
-rw-r--r--data/maps/the_quiet/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_relentless/doors.txtpb32
-rw-r--r--data/maps/the_repetitive/connections.txtpb2
-rw-r--r--data/maps/the_repetitive/doors.txtpb49
-rw-r--r--data/maps/the_repetitive/metadata.txtpb4
-rw-r--r--data/maps/the_repetitive/rooms/Anti Room.txtpb11
-rw-r--r--data/maps/the_repetitive/rooms/Entry Connector.txtpb4
-rw-r--r--data/maps/the_repetitive/rooms/Main Room.txtpb4
-rw-r--r--data/maps/the_repetitive/rooms/Plaza Connector.txtpb4
-rw-r--r--data/maps/the_revitalized/rooms/Bye Room.txtpb4
-rw-r--r--data/maps/the_shop/doors.txtpb3
-rw-r--r--data/maps/the_shop/rooms/Main Area.txtpb3
-rw-r--r--data/maps/the_sirenic/metadata.txtpb4
-rw-r--r--data/maps/the_sirenic/rooms/Start.txtpb4
-rw-r--r--data/maps/the_stellar/connections.txtpb70
-rw-r--r--data/maps/the_stellar/doors.txtpb104
-rw-r--r--data/maps/the_stellar/metadata.txtpb6
-rw-r--r--data/maps/the_stellar/rooms/Blue Panel.txtpb8
-rw-r--r--data/maps/the_stellar/rooms/Connected Area.txtpb63
-rw-r--r--data/maps/the_stellar/rooms/Green Area.txtpb8
-rw-r--r--data/maps/the_stellar/rooms/Green Panel.txtpb8
-rw-r--r--data/maps/the_stellar/rooms/Hi Room.txtpb8
-rw-r--r--data/maps/the_stellar/rooms/Mastery.txtpb5
-rw-r--r--data/maps/the_stellar/rooms/Old Crossroads.txtpb8
-rw-r--r--data/maps/the_stellar/rooms/Orange Panel.txtpb8
-rw-r--r--data/maps/the_stellar/rooms/Purple Panel.txtpb8
-rw-r--r--data/maps/the_stellar/rooms/Red Panel.txtpb8
-rw-r--r--data/maps/the_stellar/rooms/Starting Room.txtpb15
-rw-r--r--data/maps/the_stellar/rooms/Yellow Panel.txtpb8
-rw-r--r--data/maps/the_stormy/rooms/Center.txtpb4
-rw-r--r--data/maps/the_sturdy/connections.txtpb5
-rw-r--r--data/maps/the_sturdy/doors.txtpb6
-rw-r--r--data/maps/the_sturdy/metadata.txtpb8
-rw-r--r--data/maps/the_sturdy/rooms/Hidden Rainbow.txtpb6
-rw-r--r--data/maps/the_sturdy/rooms/Main Area.txtpb6
-rw-r--r--data/maps/the_sturdy/rooms/S2 Area.txtpb4
-rw-r--r--data/maps/the_sun_temple/connections.txtpb1
-rw-r--r--data/maps/the_sun_temple/metadata.txtpb4
-rw-r--r--data/maps/the_sun_temple/rooms/Entrance.txtpb3
-rw-r--r--data/maps/the_sweet/rooms/Main Area.txtpb6
-rw-r--r--data/maps/the_symbolic/metadata.txtpb4
-rw-r--r--data/maps/the_symbolic/rooms/White Room.txtpb3
-rw-r--r--data/maps/the_talented/doors.txtpb7
-rw-r--r--data/maps/the_talented/metadata.txtpb4
-rw-r--r--data/maps/the_talented/rooms/Main Area.txtpb3
-rw-r--r--data/maps/the_tenacious/doors.txtpb2
-rw-r--r--data/maps/the_tenacious/rooms/Control Center Entrance.txtpb3
-rw-r--r--data/maps/the_three_doors/rooms/Dead End Room.txtpb6
-rw-r--r--data/maps/the_three_doors/rooms/First Second Room.txtpb6
-rw-r--r--data/maps/the_three_doors/rooms/Loose Strings Room.txtpb3
-rw-r--r--data/maps/the_three_doors/rooms/One Luck Room.txtpb3
-rw-r--r--data/maps/the_three_doors/rooms/Silver Portal Room.txtpb6
-rw-r--r--data/maps/the_tower/metadata.txtpb4
-rw-r--r--data/maps/the_tower/rooms/First Floor.txtpb3
-rw-r--r--data/maps/the_tree/doors.txtpb1
-rw-r--r--data/maps/the_tree/rooms/Bearer Entrance.txtpb3
-rw-r--r--data/maps/the_tree/rooms/Main Area.txtpb14
-rw-r--r--data/maps/the_unkempt/connections.txtpb2
-rw-r--r--data/maps/the_unkempt/doors.txtpb23
-rw-r--r--data/maps/the_unkempt/rooms/Control Center Entrance.txtpb3
-rw-r--r--data/maps/the_unkempt/rooms/Daedalus Entrance.txtpb3
-rw-r--r--data/maps/the_unkempt/rooms/Main Area.txtpb9
-rw-r--r--data/maps/the_unkempt/rooms/Right Area.txtpb1
-rw-r--r--data/maps/the_unyielding/doors.txtpb39
-rw-r--r--data/maps/the_unyielding/rooms/Bearer Entrance.txtpb3
-rw-r--r--data/maps/the_unyielding/rooms/Digital Entrance.txtpb3
-rw-r--r--data/maps/the_unyielding/rooms/Nuanced Entrance.txtpb3
-rw-r--r--data/maps/the_unyielding/rooms/Plaza Entrance.txtpb3
-rw-r--r--data/maps/the_wondrous/metadata.txtpb4
-rw-r--r--data/maps/the_wondrous/rooms/Entry.txtpb3
-rw-r--r--data/maps/the_words/rooms/Main Area.txtpb3
-rw-r--r--data/metadata.txtpb56
-rw-r--r--data/progressives.txtpb13
-rw-r--r--proto/data.proto40
-rw-r--r--proto/human.proto57
-rw-r--r--tools/assign_ids/main.cpp53
-rw-r--r--tools/datapacker/main.cpp63
-rw-r--r--tools/util/ids_yaml_format.cpp13
-rw-r--r--tools/validator/human_processor.cpp66
-rw-r--r--tools/validator/structs.h9
-rw-r--r--tools/validator/validator.cpp116
-rw-r--r--tools/validator/validator.h2
416 files changed, 13761 insertions, 2640 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
11Compatability 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
22Download:
23[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.1/lingo2.apworld)<br/>
24Template YAML:
25[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.1/Lingo%202.yaml)<br/>
26Source: [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
52Download:
53[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.0/lingo2.apworld)<br/>
54Template YAML:
55[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.0/Lingo%202.yaml)<br/>
56Source: [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
75Download:
76[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.2.0/lingo2.apworld)<br/>
77Template YAML:
78[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.2.0/Lingo%202.yaml)<br/>
79Source: [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
97Download:
98[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.1.0/lingo2.apworld)<br/>
99Template YAML:
100[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.1.0/Lingo%202.yaml)<br/>
101Source: [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
108Download:
109[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.2/lingo2.apworld)<br/>
110Template YAML:
111[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.2/Lingo%202.yaml)<br/>
112Source: [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
124Download:
125[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.1/lingo2.apworld)<br/>
126Template YAML:
127[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.1/Lingo%202.yaml)<br/>
128Source: [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
161Download:
162[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.0/lingo2.apworld)<br/>
163Template YAML:
164[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.0/Lingo%202.yaml)<br/>
165Source: [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
175Download:
176[lingo2-archipelago-client-v6.7.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v6.7.zip)<br/>
177Source:
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
191Download:
192[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v6.6/lingo2.apworld)<br/>
193Template YAML:
194[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v6.6/Lingo%202.yaml)<br/>
195Source:
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
205Download:
206[lingo2-archipelago-client-v5.6.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.6.zip)<br/>
207Source:
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
214Download:
215[lingo2-archipelago-client-v5.5.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.5.zip)<br/>
216Source:
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
226Download:
227[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/lingo2.apworld)<br/>
228Template YAML:
229[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/Lingo%202.yaml)<br/>
230Source:
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
237Download:
238[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/lingo2.apworld)<br/>
239Template YAML:
240[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/Lingo%202.yaml)<br/>
241Source:
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
256Download:
257[lingo2-archipelago-client-v4.4.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v4.4.zip)<br/>
258Source:
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
270Download:
271[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/lingo2.apworld)<br/>
272Template YAML:
273[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/Lingo%202.yaml)<br/>
274Source:
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
282Download:
283[lingo2-archipelago-client-v3.3.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.3.zip)<br/>
284Source:
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
292Download:
293[lingo2-archipelago-client-v3.2.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.2.zip)<br/>
294Source:
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
302Download:
303[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/lingo2.apworld)<br/>
304Template YAML:
305[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/Lingo%202.yaml)<br/>
306Source:
307[v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v3.2)
diff --git a/README.md b/README.md index 60dc1b3..24e9fa2 100644 --- a/README.md +++ b/README.md
@@ -9,8 +9,276 @@ This is a project that modifies the game
9[Lingo 2](https://www.lingothegame.com/lingo2.html) so that it can be played as 9[Lingo 2](https://www.lingothegame.com/lingo2.html) so that it can be played as
10part of an Archipelago multiworld game. 10part of an Archipelago multiworld game.
11 11
12There are multiple parts of this project: 12## Installation
13 13
14- [Client](https://code.fourisland.com/lingo2-archipelago/about/client/README.md) 141. Download the Lingo 2 Apworld from
15- [Apworld](https://code.fourisland.com/lingo2-archipelago/about/apworld/README.md) 15 [the releases page](https://code.fourisland.com/lingo2-archipelago/about/CHANGELOG.md).
16- [Data](https://code.fourisland.com/lingo2-archipelago/about/data/README.md) 162. If you do not already have it, download and install the
17 [Archipelago software](https://github.com/ArchipelagoMW/Archipelago/releases/).
183. 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
231. Open the Archipelago Launcher.
242. Select "Lingo 2 Client".
253. 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".
294. 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.
325. Press Connect.
336. Enjoy!
34
35To continue an earlier game, you can perform the exact same steps as above.
36
37## Joining a Multiworld game (Non-Windows)
38
39Lingo 2 only officially supports Windows, but has been known to work on Linux
40using Proton. Archipelago can be played on a non-Windows system, but the process
41is a little more complex.
42
431. 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.
462. Open Lingo 2, and select Archipelago from the level selection list.
473. 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.
494. Click Start and wait for the connection settings screen to load.
505. Open the Archipelago Launcher.
516. Select "Lingo 2 Client".
527. You should see "Connected to Lingo 2!" You can then return to Lingo 2 and
53 fill out your connection details.
548. Press Connect.
559. Enjoy!
56
57## Frequently Asked Questions
58
59### Why aren't the starting room letters shuffled?
60
61The letter requirements for solving puzzles are very restrictive, especially in
62the early game. It is possible for the generator to find some subset of letters
63and doors to place in the starting room such that you are not trapped, but this
64places a lot of strain on generation and leads to significantly more generation
65failures.
66
67As a result, the starting room letters (H1, I1, N1, and T1) are always present
68in the starting room, even when remote letter shuffle is enabled. These letters
69will _also_ count as clearing a check, so you will send out another item at the
70same time as collecting the letter.
71
72### What areas are randomized?
73
74Almost all maps that you can access from the base game are randomized. The only
75exception is The Hinterlands, which will probably be repurposed into a hint
76area. Some advanced/hidden maps are also disabled by default (as discussed
77below).
78
79### Is my progress saved locally?
80
81Lingo 2 autosaves your progress every time you solve a puzzle, get a
82collectable, or interact with a keyholder. The randomizer generates a savefile
83name based on your Multiworld seed and slot number, so you should be able to
84seamlessly switch between multiworlds and even slots within a multiworld.
85
86The exception to this is different rooms created from the same multiworld seed.
87The client is unable to tell rooms in a seed apart (this is a limitation of the
88Archipelago API), so the client will use the same save file for the same slot in
89different rooms on the same seed. You can work around this by manually moving or
90removing the save folder from the users directory in Lingo 2's game files.
91
92If you play the base game again, you will see one or more save files with a long
93name that begins with "zzAP\_". These are the saves for your multiworlds. They
94can be safely deleted after you have completed the associated multiworld. It is
95not recommended to load these save files outside of the randomizer.
96
97A connection to Archipelago is required to resume playing a multiworld. This is
98because 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
103other side of opaque walls. The player is never expected to or required to do
104this in normal gameplay. This randomizer does not change how wall snipes work,
105but it will likewise never require the use of them.
106
107### How do cyan doors work?
108
109In the base game, there are a number of cyan-colored doors that ordinarily open
110once you collect H2 in The Repetitive. There are also a handful of panels that
111only appear upon getting H2 as well, which the apworld treats the same as the
112cyan doors.
113
114There is an option that lets you choose how these doors and panels behave. By
115default, they act the same as in the base game: they only open or appear after
116collecting H2. Note that this means the actual H2 collectable in The Repetitive.
117Receiving H2 via remote letter shuffle does not count for this requirement.
118However, you can also make cyan doors activate upon collecting or receiving your
119first double letter, regardless of what it is or if it's remote. Finally, you
120can lock cyan doors behind an item called "Cyan Doors".
121
122It is important to note, however, that the Cyan Door Behavior option only
123applies to cyan doors that are not already affected by another type of
124shuffling. When door shuffle is on, the following cyan doors are activated by
125individual 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
136Additionally, when control center color shuffle is enabled, the orange door in
137The Unkempt (which ordinarily doubles as a cyan door) opens upon receiving the
138Control Center Orange Doors item, instead of following the Cyan Door Behavior
139option.
140
141### Help! I lost C/G in The Congruent!
142
143If you place C or G into the relevant keyholders in The Congruent, the keyholder
144disappears. You can retrieve your letter immediately by pressing C or G again
145before 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
147another 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
151Red usually indicates that a panel cannot be solved because of missing letters.
152However, that only applies to the puzzle's main answer. If a puzzle has
153alternate answers, you may be expected to use one of those instead of the main
154one. As long as you have all of the necessary letters, an alternate answer can
155be typed into a red panel even though it does not show you typing. When you
156finish 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
160This is an infamous panel, both in the base game and in the randomizer. There
161are _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
166The Sturdy contains a rainbow painting that leads to the Gold Ending area in
167Daedalus. There are three ways to spawn this painting, which have different
168logical 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
183While Icarus is easily accessible during normal play, it is not randomized by
184default. The main reason for this is that Icarus employs significantly more use
185of gravity changing mechanics than the rest of the game and as a result tends to
186cause motion sickness in a lot of players. It is also an infamously confusing
187area to navigate.
188
189Because of this, the player may enter and exit Icarus from the usual place in
190Daedalus, but it will not contain any locations, and no items will be added to
191the pool for it. The worldport will not be included in the randomization if
192worldport shuffle is on. Icarus can also still be entered from The Crystalline,
193but doing so (in order to then access Daedalus) will not be logically required.
194
195However, Icarus can be randomized via the "Enable Icarus" option. Doing so
196creates locations and items for the map, and includes the worldport in worldport
197shuffle. The aforementioned connection from The Crystalline also becomes
198logical, if The Crystalline is enabled.
199
200It is not trivial to telegraph exactly what is logical within Icarus. It is very
201easy to break logic because the gravity changers allow you to fall in almost any
202direction you want to. In general, falling is only in logic if it is "guided",
203i.e. falling through a hole or an open door to another platform, or using a
204gravity inverter. You may also sometimes be required to solve panels that are
205physically near you and easily visible, but not on your plane of gravity. The
206tracker can help you determine what is considered logical, if you want to stay
207within the randomizer's logic.
208
209### How do the gift maps work?
210
211The beta tester gift maps are hidden levels intended for specific people. By
212default, these are not accessible at all from within the randomizer. The "Enable
213Gift Maps" option allows you to enter the maps, and creates items and locations
214for them. If worldport shuffle is on, their worldports will be included in the
215randomization.
216
217The gift maps are accessed via a panel in The Entry's Starting Room, which only
218appears if at least one gift map is enabled. It is also treated like a cyan
219door, and will not appear until the condition specified in the Cyan Door
220Behavior option is satisfied. Solving this panel with the name of one of the
221beta testers will teleport you to their corresponding gift map. This README
222purposefully does not list the names you need to enter the maps via the panel.
223
224In the base game, nothing happens once you complete a gift map. Masteries have
225been added to the gift maps in the randomizer so that the player can be rewarded
226for completing them.
227
228Note that the gift maps were originally only intended to be played by specific
229people, and as a result may be frustrating or require knowledge of inside jokes.
230The Crystalline is particularly difficult as it requires completing a parkour
231course. It is highly recommended that you complete these maps vanilla or solo
232before bringing them to a multiworld. It is also perfectly acceptable to never
233enable them.
234
235## Running from source
236
237The randomizer is mostly written in Python and GDScript, which do not need to be
238compiled. However, there are three files that need to be generated before the
239apworld can be used.
240
241The first file is `data.binpb`, the datafile containing the randomizer logic.
242You can read about how to generate it on
243[its own README page](https://code.fourisland.com/lingo2-archipelago/about/data/README.md).
244Once you have it, put it in a subfolder of `apworld` called `generated`.
245
246The second generated file is `data_pb2.py`. This file allows Archipelago to read
247the datafile. We use `protoc`, the Protocol Buffer compiler, to generate it. As
248of 0.6.3, Archipelago has protobuf 3.20.3 packaged with it, which means we need
249to compile our proto file with a similar version.
250
251If you followed the steps to generate `data.binpb` and compiled the `datapacker`
252tool yourself, you will already have protobuf version 3.21.12 installed through
253vcpkg. You can then run a command similar to this in order to generate the
254python 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
261The exact path to `protoc.exe` is going to depend on where vcpkg installed its
262packages. The above location is where Visual Studio will probably put it.
263
264The third generated file is `proto.gd`. This is the GDScript version of the
265previous file. We use a Godot script to generate it, which means
266[the Godot Editor](https://godotengine.org/download/) is required. From the root
267of the repository:
268
269```shell
270cd vendor\godobuf
271godot --headless -s addons\protobuf\protobuf_cmdln.gd --input=..\..\proto\data.proto ^
272 --output=..\..\apworld\generated\proto.gd
273```
274
275If you are not on Windows, replace the forward slashes with backslashes as
276appropriate (and the caret with a forward slash). You will also probably need to
277replace "godot" at the start of the second line with a path to a Godot Editor
278executable.
279
280After generating those three files, the apworld should be functional. You can
281copy it into an Archipelago source tree (rename the folder `apworld` to `lingo2`
282if you do so) if you want to edit/debug the code. Otherwise, you can zip up the
283folder and rename it to `lingo2.apworld` in order to package it for
284distribution.
diff --git a/apworld/README.md b/apworld/README.md deleted file mode 100644 index 13374b2..0000000 --- a/apworld/README.md +++ /dev/null
@@ -1,48 +0,0 @@
1# Lingo 2 Apworld
2
3The Lingo 2 Apworld allows you to generate Archipelago Multiworlds containing
4Lingo 2.
5
6## Installation
7
81. Download the Lingo 2 Apworld from
9 [the releases page](https://code.fourisland.com/lingo2-archipelago/about/apworld/CHANGELOG.md).
102. If you do not already have it, download and install the
11 [Archipelago software](https://github.com/ArchipelagoMW/Archipelago/releases/).
123. Double click on `lingo2.apworld` to install it, or copy it manually to the
13 `custom_worlds` folder of your Archipelago installation.
14
15## Running from source
16
17The apworld is mostly written in Python, which does not need to be compiled.
18However, there are two files that need to be generated before the apworld can be
19used.
20
21The first file is `data.binpb`, the datafile containing the randomizer logic.
22You can read about how to generate it on
23[its own README page](https://code.fourisland.com/lingo2-archipelago/about/data/README.md).
24Once you have it, put it in a subfolder of `apworld` called `generated`.
25
26The second generated file is `data_pb2.py`. This file allows Archipelago to read
27the datafile. We use `protoc`, the Protocol Buffer compiler, to generate it. As
28of 0.6.3, Archipelago has protobuf 3.20.3 packaged with it, which means we need
29to compile our proto file with a similar version.
30
31If you followed the steps to generate `data.binpb` and compiled the `datapacker`
32tool yourself, you will already have protobuf version 3.21.12 installed through
33vcpkg. You can then run a command similar to this in order to generate the
34python file.
35
36```shell
37.\out\build\x64-Debug\vcpkg_installed\x64-windows\tools\protobuf\protoc.exe -Iproto\ ^
38 --python_out=apworld\generated\ .\proto\data.proto
39```
40
41The exact path to `protoc.exe` is going to depend on where vcpkg installed its
42packages. The above location is where Visual Studio will probably put it.
43
44After generating those two files, the apworld should be functional. You can copy
45it into an Archipelago source tree (rename the folder `apworld` to `lingo2` if
46you do so) if you want to edit/debug the code. Otherwise, you can zip up the
47folder and rename it to `lingo2.apworld` in order to package it for
48distribution.
diff --git a/apworld/__init__.py b/apworld/__init__.py index d05bd38..3d2f075 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py
@@ -1,18 +1,40 @@
1""" 1"""
2Archipelago init file for Lingo 2 2Archipelago init file for Lingo 2
3""" 3"""
4from BaseClasses import ItemClassification, Item 4from typing import ClassVar
5
6from BaseClasses import ItemClassification, Item, Tutorial
7from Options import OptionError
8from settings import Group, UserFilePath
5from worlds.AutoWorld import WebWorld, World 9from worlds.AutoWorld import WebWorld, World
6from .items import Lingo2Item 10from .items import Lingo2Item, ANTI_COLLECTABLE_TRAPS
7from .options import Lingo2Options 11from .options import Lingo2Options
8from .player_logic import Lingo2PlayerLogic 12from .player_logic import Lingo2PlayerLogic
9from .regions import create_regions 13from .regions import create_regions, shuffle_entrances, connect_ports_from_ut
10from .static_logic import Lingo2StaticLogic 14from .static_logic import Lingo2StaticLogic
15from worlds.LauncherComponents import Component, Type, components, launch as launch_component, icon_paths
11 16
12 17
13class Lingo2WebWorld(WebWorld): 18class 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
31class 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
18class Lingo2World(World): 40class 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
@@ -35,17 +60,35 @@ class Lingo2World(World):
35 item_name_groups = static_logic.item_name_groups 60 item_name_groups = static_logic.item_name_groups
36 location_name_groups = static_logic.location_name_groups 61 location_name_groups = static_logic.location_name_groups
37 62
63 for_tracker: ClassVar[bool] = False
64
38 player_logic: Lingo2PlayerLogic 65 player_logic: Lingo2PlayerLogic
39 66
67 port_pairings: dict[int, int]
68
40 def generate_early(self): 69 def generate_early(self):
41 self.player_logic = Lingo2PlayerLogic(self) 70 self.player_logic = Lingo2PlayerLogic(self)
71 self.port_pairings = {}
42 72
43 def create_regions(self): 73 def create_regions(self):
44 create_regions(self) 74 create_regions(self)
45 75
46 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
47 90
48 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")
49 92
50 def create_items(self): 93 def create_items(self):
51 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]
@@ -53,13 +96,33 @@ class Lingo2World(World):
53 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())
54 97
55 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
56 for i in range(0, item_difference): 113 for i in range(0, item_difference):
57 pool.append(self.create_item(self.get_filler_item_name())) 114 pool.append(self.create_item(self.get_filler_item_name()))
58 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
59 self.multiworld.itempool += pool 121 self.multiworld.itempool += pool
60 122
61 def create_item(self, name: str) -> Item: 123 def create_item(self, name: str) -> Item:
62 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
63 ItemClassification.progression, 126 ItemClassification.progression,
64 self.item_name_to_id.get(name), self.player) 127 self.item_name_to_id.get(name), self.player)
65 128
@@ -70,18 +133,54 @@ class Lingo2World(World):
70 slot_options = [ 133 slot_options = [
71 "cyan_door_behavior", 134 "cyan_door_behavior",
72 "daedalus_roof_access", 135 "daedalus_roof_access",
136 "enable_gift_maps",
137 "enable_icarus",
138 "endings_requirement",
73 "keyholder_sanity", 139 "keyholder_sanity",
140 "masteries_requirement",
74 "shuffle_control_center_colors", 141 "shuffle_control_center_colors",
75 "shuffle_doors", 142 "shuffle_doors",
143 "shuffle_gallery_paintings",
76 "shuffle_letters", 144 "shuffle_letters",
145 "shuffle_symbols",
146 "shuffle_worldports",
147 "strict_cyan_ending",
148 "strict_purple_ending",
77 "victory_condition", 149 "victory_condition",
78 ] 150 ]
79 151
80 slot_data = { 152 slot_data: dict[str, object] = {
81 **self.options.as_dict(*slot_options), 153 **self.options.as_dict(*slot_options),
154 "version": self.static_logic.get_data_version(),
82 } 155 }
83 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
84 return slot_data 164 return slot_data
85 165
86 def get_filler_item_name(self) -> str: 166 def get_filler_item_name(self) -> str:
87 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
178def launch_client(*args):
179 from .context import client_main
180 launch_component(client_main, name="Lingo2Client", args=args)
181
182
183icon_paths["lingo2_ico"] = f"ap:{__name__}/logo.png"
184component = 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")
186components.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 @@
1extends "res://scripts/nodes/allowNumbers.gd"
2
3
4func _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 @@
1extends Node
2
3var apworld_reader
4
5
6func _init(path):
7 apworld_reader = ZIPReader.new()
8 apworld_reader.open(path)
9
10
11func _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
18func path_exists(path):
19 var true_path = _get_true_path(path)
20 return apworld_reader.file_exists(true_path)
21
22
23func 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
33func read_path(path):
34 var true_path = _get_true_path(path)
35 return apworld_reader.read_file(true_path)
36
37
38func 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
47func _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 @@
1extends Node
2
3const ap_version = {"major": 0, "minor": 6, "build": 3, "class": "Version"}
4
5var SCRIPT_websocketserver
6
7var _server
8var _should_process = false
9
10var _remote_version = {"major": 0, "minor": 0, "build": 0}
11var _gen_version = {"major": 0, "minor": 0, "build": 0}
12
13var ap_server = ""
14var ap_user = ""
15var ap_pass = ""
16
17var _seed = ""
18var _team = 0
19var _slot = 0
20var _checked_locations = []
21var _checked_worldports = []
22var _received_indexes = []
23var _received_items = {}
24var _slot_data = {}
25var _accessible_locations = []
26var _accessible_worldports = []
27var _goal_accessible = false
28var _latched_doors = []
29var _hinted_locations = []
30
31signal could_not_connect
32signal connect_status
33signal client_connected(slot_data)
34signal item_received(item, amount)
35signal location_scout_received(location_id, item_name, player_name, flags, for_self)
36signal text_message_received(message)
37signal item_sent_notification(message)
38signal hint_received(message)
39signal door_latched(id)
40signal accessible_locations_updated
41signal checked_locations_updated
42signal ignored_locations_updated(locations)
43signal checked_worldports_updated
44signal keyboard_update_received
45signal hinted_locations_updated
46
47
48func _init():
49 set_process_mode(Node.PROCESS_MODE_ALWAYS)
50
51 global._print("Instantiated APClient")
52
53
54func _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
63func _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
73func disconnect_from_ap():
74 sendMessage([{"cmd": "Disconnect"}])
75
76
77func _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
83func _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
94func _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
221func 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
233func sendMessage(msg):
234 var payload = JSON.stringify(msg)
235 _server.send(0, payload)
236
237
238func 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
253func requestSync():
254 sendMessage([{"cmd": "Sync"}])
255
256
257func sendLocation(loc_id):
258 sendMessage([{"cmd": "LocationChecks", "locations": [loc_id]}])
259
260
261func sendLocations(loc_ids):
262 sendMessage([{"cmd": "LocationChecks", "locations": loc_ids}])
263
264
265func say(textdata):
266 sendMessage([{"cmd": "Say", "text": textdata}])
267
268
269func completedGoal():
270 sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL
271
272
273func scoutLocations(loc_ids):
274 sendMessage([{"cmd": "LocationScouts", "locations": loc_ids}])
275
276
277func updateKeyboard(updates):
278 sendMessage([{"cmd": "UpdateKeyboard", "keyboard": updates}])
279
280
281func checkWorldport(port_id):
282 if not _checked_worldports.has(port_id):
283 sendMessage([{"cmd": "CheckWorldport", "port_id": port_id}])
284
285
286func latchDoor(id):
287 if not _latched_doors.has(id):
288 _latched_doors.append(id)
289
290 sendMessage([{"cmd": "LatchDoor", "door": id}])
291
292
293func 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
301func addIgnoredLocation(loc_id):
302 sendMessage([{"cmd": "IgnoreLocation", "id": loc_id}])
303
304
305func removeIgnoredLocation(loc_id):
306 sendMessage([{"cmd": "UnignoreLocation", "id": loc_id}])
307
308
309func sendQuit():
310 sendMessage([{"cmd": "Quit"}])
311
312
313func hasItem(item_id):
314 return _received_items.has(item_id)
315
316
317func 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 @@
1extends Node2D
2
3const RADIUS = 48
4
5var _font
6
7
8func _ready():
9 _font = load("res://assets/fonts/Lingo2.ttf")
10
11
12func _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 @@
1extends CanvasLayer
2
3var SCRIPT_compass
4
5var compass
6
7
8func _ready():
9 compass = SCRIPT_compass.new()
10 compass.position = Vector2(1840, 80)
11 add_child(compass)
12
13 visible = false
14
15
16func 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 @@
1extends "res://scripts/nodes/door.gd"
2
3var door_id
4var item_id
5var item_amount
6var latched = false
7
8
9func _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
50func _readier():
51 var ap = global.get_node("Archipelago")
52
53 if ap.client.getItemAmount(item_id) >= item_amount:
54 handleTriggered()
55
56
57func _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
65func 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
73func 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 @@
1extends CanvasLayer
2
3var _label
4
5var _disconnected = false
6
7
8func _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
20func set_connection_lost(arg):
21 _disconnected = arg
22
23 _update_label()
24
25
26func _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 @@
1extends Node
2
3var SCRIPT_proto
4
5var objects
6var door_id_by_map_node_path = {}
7var painting_id_by_map_node_path = {}
8var panel_id_by_map_node_path = {}
9var port_id_by_map_node_path = {}
10var door_id_by_ap_id = {}
11var map_id_by_name = {}
12var progressive_id_by_ap_id = {}
13var letter_id_by_ap_id = {}
14var symbol_item_ids = []
15var anti_trap_ids = {}
16var location_name_by_id = {}
17var ending_display_name_by_name = {}
18var port_id_by_ap_id = {}
19
20var kSYMBOL_ITEMS
21
22
23func _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
49func 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
145func 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
153func 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
161func 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
169func 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
177func 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
183func get_door_receivers(door_id):
184 var door = objects.get_doors()[door_id]
185 return door.get_receivers()
186
187
188func 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
193func _get_map_object_map_name(obj):
194 return objects.get_maps()[obj.get_map_id()].get_display_name()
195
196
197func _get_room_object_map_name(obj):
198 return _get_map_object_map_name(objects.get_rooms()[obj.get_room_id()])
199
200
201func _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
211func _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
224func _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
282func _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
288func _get_mastery_location_name(mastery):
289 return "%s - Mastery" % _get_room_object_map_name(mastery)
290
291
292func _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
298func _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 600a047..a59c4d0 100644 --- a/client/Archipelago/keyboard.gd +++ b/apworld/client/keyboard.gd
@@ -4,6 +4,7 @@ const kALL_LETTERS = "abcdefghjiklmnopqrstuvwxyz"
4 4
5var letters_saved = {} 5var letters_saved = {}
6var letters_in_keyholders = [] 6var letters_in_keyholders = []
7var letters_blocked = []
7var letters_dynamic = {} 8var letters_dynamic = {}
8var keyholder_state = {} 9var keyholder_state = {}
9 10
@@ -17,6 +18,7 @@ func _init():
17func reset(): 18func 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
@@ -91,6 +96,9 @@ func update_unlocks():
91 level = 2 96 level = 2
92 has_doubles = true 97 has_doubles = true
93 98
99 if letters_blocked.has(k):
100 level = 0
101
94 unlocks.unlockKey(k, level) 102 unlocks.unlockKey(k, level)
95 103
96 if has_doubles and unlocks.data["double_letters"] != "unlocked": 104 if has_doubles and unlocks.data["double_letters"] != "unlocked":
@@ -100,10 +108,23 @@ func update_unlocks():
100 108
101 109
102func collect_local_letter(key, level): 110func collect_local_letter(key, level):
103 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):
104 return 120 return
105 121
106 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)
107 128
108 update_unlocks() 129 update_unlocks()
109 save() 130 save()
@@ -115,6 +136,9 @@ func collect_remote_letter(key, level):
115 136
116 letters_dynamic[key] = level 137 letters_dynamic[key] = level
117 138
139 if letters_blocked.has(key):
140 letters_blocked.erase(key)
141
118 update_unlocks() 142 update_unlocks()
119 save() 143 save()
120 144
@@ -148,6 +172,13 @@ func remove_from_keyholder(key, map, kh_path):
148 save() 172 save()
149 173
150 174
175func block_letter(key):
176 if not letters_blocked.has(key):
177 letters_blocked.append(key)
178
179 update_unlocks()
180
181
151func load_keyholders(map): 182func load_keyholders(map):
152 if keyholder_state.has(map): 183 if keyholder_state.has(map):
153 var khs = keyholder_state[map] 184 var khs = keyholder_state[map]
@@ -160,9 +191,11 @@ func load_keyholders(map):
160 191
161 192
162func reset_keyholders(): 193func reset_keyholders():
163 if letters_in_keyholders.is_empty(): 194 if letters_in_keyholders.is_empty() and letters_blocked.is_empty():
164 return false 195 return false
165 196
197 var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty()
198
166 if keyholder_state.has(global.map): 199 if keyholder_state.has(global.map):
167 for path in keyholder_state[global.map]: 200 for path in keyholder_state[global.map]:
168 get_tree().get_root().get_node("scene").get_node(path).setFromAp( 201 get_tree().get_root().get_node("scene").get_node(path).setFromAp(
@@ -171,8 +204,28 @@ func reset_keyholders():
171 204
172 keyholder_state.clear() 205 keyholder_state.clear()
173 letters_in_keyholders.clear() 206 letters_in_keyholders.clear()
207 letters_blocked.clear()
174 208
175 update_unlocks() 209 update_unlocks()
176 save() 210 save()
177 211
178 return true 212 return cleared_anything
213
214
215func 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 @@
1extends Node
2
3
4func _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
141func _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
153func _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
162func 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
178func 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
187func 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
216func 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
279func 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
290func 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
298func 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
307func 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 @@
1extends Node
2
3var SCRIPT_client
4var SCRIPT_keyboard
5var SCRIPT_locationListener
6var SCRIPT_minimap
7var SCRIPT_victoryListener
8var SCRIPT_websocketserver
9
10var ap_server = ""
11var ap_user = ""
12var ap_pass = ""
13var connection_history = []
14var show_compass = false
15var show_locations = false
16var show_minimap = false
17
18var client
19var keyboard
20
21var _localdata_file = ""
22var _last_new_item = -1
23var _batch_locations = false
24var _held_locations = []
25var _held_location_scouts = []
26var _location_scouts = {}
27var _item_locks = {}
28var _inverse_item_locks = {}
29var _held_letters = {}
30var _letters_setup = false
31var _already_connected = false
32var _ignored_locations = []
33var _map_scripts = {}
34
35const kSHUFFLE_LETTERS_VANILLA = 0
36const kSHUFFLE_LETTERS_UNLOCKED = 1
37const kSHUFFLE_LETTERS_PROGRESSIVE = 2
38const kSHUFFLE_LETTERS_VANILLA_CYAN = 3
39const kSHUFFLE_LETTERS_ITEM_CYAN = 4
40
41const kLETTER_BEHAVIOR_VANILLA = 0
42const kLETTER_BEHAVIOR_ITEM = 1
43const kLETTER_BEHAVIOR_UNLOCKED = 2
44
45const kCYAN_DOOR_BEHAVIOR_H2 = 0
46const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1
47const kCYAN_DOOR_BEHAVIOR_ITEM = 2
48
49const 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
65var apworld_version = [0, 0, 0]
66var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
67var daedalus_roof_access = false
68var enable_gift_maps = []
69var enable_icarus = false
70var endings_requirement = 0
71var keyholder_sanity = false
72var masteries_requirement = 0
73var port_pairings = {}
74var shuffle_control_center_colors = false
75var shuffle_doors = false
76var shuffle_gallery_paintings = false
77var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
78var shuffle_symbols = false
79var shuffle_worldports = false
80var strict_cyan_ending = false
81var strict_purple_ending = false
82var victory_condition = -1
83
84var color_by_material_path = {}
85
86signal could_not_connect
87signal connect_status
88signal ap_connected
89
90
91func _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
138func _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
165func 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
183func 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
199func 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
212func getSaveFileName():
213 return "zzAP_%s_%d" % [client._seed, client._slot]
214
215
216func 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
225func get_item_id_for_door(door_id):
226 return _item_locks.get(door_id, null)
227
228
229func _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
298func _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
311func _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
328func _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
348func _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
372func _on_accessible_locations_updated():
373 var textclient_node = global.get_node("Textclient")
374 if textclient_node != null:
375 textclient_node.update_locations()
376
377
378func _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
384func _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
391func _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
399func _on_hinted_locations_updated():
400 var textclient_node = global.get_node("Textclient")
401 if textclient_node != null:
402 textclient_node.update_locations()
403
404
405func _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
419func _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
430func _client_connect_status(message):
431 connect_status.emit(message)
432
433
434func _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
566func start_batching_locations():
567 _batch_locations = true
568
569
570func 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
580func 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
592func 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
604func 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
619func 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
627func 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
652func 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
663func _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
674func 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
700func 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
707func 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 @@
1func 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
79func _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 @@
1func 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
71func _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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1func 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 @@
1extends CanvasLayer 1extends CanvasLayer
2 2
3var SCRIPT_rainbowText
4
3var _message_queue = [] 5var _message_queue = []
4var _font 6var _font
5var _container 7var _container
@@ -23,6 +25,7 @@ func _ready():
23 25
24func _add_message(text): 26func _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
68func 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 @@
1extends CanvasLayer
2
3var player
4var drawer
5var sprite
6var label
7
8var cell_left
9var cell_top
10var cell_right
11var cell_bottom
12var cell_width
13var cell_height
14var center_x_min
15var center_x_max
16var center_y_min
17var center_y_max
18
19
20func _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
117func _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
128func _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
157func _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 @@
1extends "res://scripts/nodes/paintingAuto.gd"
2
3var item_id
4var item_amount
5
6
7func _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
39func _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 @@
1extends "res://scripts/nodes/panel.gd"
2
3var panel_logic = null
4var symbol_solvable = true
5
6var black = load("res://assets/materials/black.material")
7
8
9func _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
37func 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
69func checkSolvable(key):
70 checkSymbolSolvable()
71 if not symbol_solvable:
72 return false
73
74 return super.checkSolvable(key)
75
76
77func evaluateSolvability():
78 checkSolvable("")
79
80
81func passedInput(key, skip_focus_check = false):
82 if not symbol_solvable:
83 return
84
85 super.passedInput(key, skip_focus_check)
86
87
88func focus():
89 if not symbol_solvable:
90 has_focus = false
91 return
92
93 super.focus()
94
95
96func 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 @@
1extends "res://scripts/ui/pauseMenu.gd"
2
3var compass_button
4var locations_button
5var minimap_button
6
7
8func _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
45func _pause_game():
46 global.get_node("Textclient").dismiss()
47 super._pause_game()
48
49
50func _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
66func _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
75func _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
84func _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 @@
1extends "res://scripts/nodes/player.gd" 1extends "res://scripts/nodes/player.gd"
2 2
3const kEndingNameByVictoryValue = { 3signal evaluate_solvability
4 0: "GRAY", 4
5 1: "PURPLE", 5var 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
20func _ready(): 8func _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
199func _set_up_invis_wall(x, y, z, sx, sy, sz): 180func _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 @@
1extends RichTextEffect
2
3var bbcode = "rainbow"
4
5
6func _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"]
4script/source = "extends Node2D
5
6
7func _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"]
30script = 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"]
4script/source = "extends Node2D
5
6
7func _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"]
22script = 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 @@
1extends "res://scripts/nodes/saver.gd"
2
3
4func 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
12func 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 @@
1extends Node
2
3
4func _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 @@
1extends Node
2
3var source_path
4
5
6func _init(path):
7 source_path = path
8
9
10func path_exists(path):
11 return FileAccess.file_exists("%s/%s" % [source_path, path])
12
13
14func load_script(path):
15 return ResourceLoader.load("%s/%s" % [source_path, path])
16
17
18func read_path(path):
19 return FileAccess.get_file_as_bytes("%s/%s" % [source_path, path])
20
21
22func 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
31func _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 @@
1extends "res://scripts/nodes/door.gd" 1extends "res://scripts/nodes/teleport.gd"
2 2
3var item_id 3var item_id
4var item_amount 4var 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 @@
1extends CanvasLayer
2
3var tabs
4var panel
5var label
6var entry
7var is_open = false
8
9var locations_overlay
10var location_texture
11var worldport_texture
12var goal_texture
13
14var tracker_tree
15var tracker_loc_tree_item_by_id = {}
16var tracker_port_tree_item_by_id = {}
17var tracker_goal_tree_item = null
18var tracker_object_by_index = {}
19var tracker_object_by_ignored_index = {}
20var tracker_ignored_group = null
21
22var worldports_tab
23var worldports_tree
24var port_tree_item_by_map = {}
25var port_tree_item_by_map_port = {}
26
27const kLocation = 0
28const kWorldport = 1
29const kGoal = 2
30
31
32func _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
148func _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
166func 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
174func parse_printjson(text):
175 label.append_text("[p]" + text + "[/p]")
176
177
178func 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
193func 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
360func _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
369func update_locations_visibility():
370 var ap = global.get_node("Archipelago")
371 locations_overlay.visible = ap.show_locations
372
373
374func _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
404func 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
424func 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
476func 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
492func 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
501func 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 @@
1extends "res://scripts/nodes/listeners/unlockReaderListener.gd"
2
3var item_id = null
4var item_amount
5
6
7func _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
32func _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
42func 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 @@
1WebSocketServer.gd:
2
3Copyright (c) 2014-present Godot Engine contributors. Copyright (c) 2007-2014
4Juan Linietsky, Ariel Manzur.
5
6Permission is hereby granted, free of charge, to any person obtaining a copy of
7this software and associated documentation files (the "Software"), to deal in
8the Software without restriction, including without limitation the rights to
9use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10the Software, and to permit persons to whom the Software is furnished to do so,
11subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21CONNECTION 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 @@
1class_name WebSocketServer
2extends Node
3
4signal message_received(peer_id: int, message: String)
5signal client_connected(peer_id: int)
6signal 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
20class 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
32var tcp_server := TCPServer.new()
33var pending_peers: Array[PendingPeer] = []
34var peers: Dictionary
35
36
37func listen(port: int) -> int:
38 assert(not tcp_server.is_listening())
39 return tcp_server.listen(port)
40
41
42func stop() -> void:
43 tcp_server.stop()
44 pending_peers.clear()
45 peers.clear()
46
47
48func 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
68func 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
79func has_message(peer_id: int) -> bool:
80 assert(peers.has(peer_id))
81 return peers[peer_id].get_available_packet_count() > 0
82
83
84func _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
91func 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
133func _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
172func _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 @@
1extends "res://scripts/nodes/listeners/visibilityListener.gd"
2
3var item_id
4var item_amount
5
6
7func _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
34func _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 @@
1extends "res://scripts/nodes/worldport.gd"
2
3var absolute_rotation = false
4var target_rotation = 0
5
6var port_id = null
7
8
9func _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
44func 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
56func 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 @@
1extends "res://scripts/nodes/listeners/worldportListener.gd"
2
3
4func 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 @@
1import asyncio
2import os
3import pkgutil
4import subprocess
5import sys
6from typing import Any
7
8import websockets
9
10import Utils
11import settings
12from BaseClasses import ItemClassification
13from CommonClient import CommonContext, server_loop, gui_enabled, logger, get_base_parser, handle_url_arg
14from NetUtils import Endpoint, decode, encode, ClientStatus
15from Utils import async_start
16from . import Lingo2World
17from .tracker import Tracker
18
19ALL_LETTERS = "abcdefghijklmnopqrstuvwxyz"
20MESSAGE_MAX_SIZE = 16*1024*1024
21PORT = 43182
22
23KEY_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
30REVERSE_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.
43class 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
111class 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
314class 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
636async 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
655async 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
719async 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
760def 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
3See [the project README](https://code.fourisland.com/lingo2-archipelago/about/)
4for 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 @@
1from .generated import data_pb2 as data_pb2
1from BaseClasses import Item 2from BaseClasses import Item
2 3
3 4
4class Lingo2Item(Item): 5class Lingo2Item(Item):
5 game: str = "Lingo 2" 6 game: str = "Lingo 2"
7
8
9SYMBOL_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
31ANTI_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
4class Lingo2Location(Location): 4class 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 @@
1from dataclasses import dataclass 1from dataclasses import dataclass
2 2
3from Options import PerGameCommonOptions, Toggle, Choice 3from Options import PerGameCommonOptions, Toggle, Choice, DefaultOnToggle, Range, OptionSet
4 4
5 5
6class ShuffleDoors(Toggle): 6class 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
19class 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
19class ShuffleLetters(Choice): 24class 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
47class 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
55class 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
42class KeyholderSanity(Toggle): 64class 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
94class 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
102class 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
72class DaedalusRoofAccess(Toggle): 124class 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
134class 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
142class 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
82class VictoryCondition(Choice): 150class 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
184class 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
192class 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
204class 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
101class Lingo2Options(PerGameCommonOptions): 213class 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 c94b809..3ee8f38 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py
@@ -1,9 +1,10 @@
1from enum import IntEnum, auto 1from enum import IntEnum, auto
2 2
3from .generated import data_pb2 as data_pb2 3from .generated import data_pb2 as data_pb2
4from .items import SYMBOL_ITEMS
4from typing import TYPE_CHECKING, NamedTuple 5from typing import TYPE_CHECKING, NamedTuple
5 6
6from .options import VictoryCondition, ShuffleLetters, CyanDoorBehavior 7from .options import ShuffleLetters, CyanDoorBehavior
7 8
8if TYPE_CHECKING: 9if 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
82class PlayerLocation(NamedTuple): 191class PlayerLocation(NamedTuple):
@@ -93,6 +202,8 @@ class LetterBehavior(IntEnum):
93class Lingo2PlayerLogic: 202class 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,18 +229,54 @@ 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:
132 continue 280 continue
133 elif door_group.type == data_pb2.DoorGroupType.SHUFFLE_GROUP: 281 elif door_group.type == data_pb2.DoorGroupType.SHUFFLE_GROUP:
134 if not self.world.options.shuffle_doors: 282 if not self.world.options.shuffle_doors:
@@ -136,14 +284,21 @@ class Lingo2PlayerLogic:
136 else: 284 else:
137 continue 285 continue
138 286
139 for door in door_group.doors: 287 shuffleable_doors = [door_id for door_id in door_group.doors
140 self.item_by_door[door] = (door_group.name, 1) 288 if world.static_logic.objects.doors[door_id].map_id in self.shuffled_maps]
289
290 if len(shuffleable_doors) > 0:
291 for door in shuffleable_doors:
292 self.item_by_door[door] = (door_group.name, 1)
141 293
142 self.real_items.append(door_group.name) 294 self.real_items.append(door_group.name)
143 295
144 # 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
145 # before we calculate any access requirements. 297 # before we calculate any access requirements.
146 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
147 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]:
148 continue 303 continue
149 304
@@ -158,6 +313,9 @@ class Lingo2PlayerLogic:
158 not self.world.options.shuffle_control_center_colors): 313 not self.world.options.shuffle_control_center_colors):
159 continue 314 continue
160 315
316 if door.type == data_pb2.DoorType.GALLERY_PAINTING and not self.world.options.shuffle_gallery_paintings:
317 continue
318
161 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)
162 self.item_by_door[door.id] = (door_item_name, 1) 320 self.item_by_door[door.id] = (door_item_name, 1)
163 self.real_items.append(door_item_name) 321 self.real_items.append(door_item_name)
@@ -169,29 +327,40 @@ class Lingo2PlayerLogic:
169 if door_group.type != data_pb2.DoorGroupType.CYAN_DOORS: 327 if door_group.type != data_pb2.DoorGroupType.CYAN_DOORS:
170 continue 328 continue
171 329
172 for door in door_group.doors: 330 shuffleable_doors = [door_id for door_id in door_group.doors
173 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:
174 self.item_by_door[door] = (door_group.name, 1) 336 self.item_by_door[door] = (door_group.name, 1)
175 337
176 self.real_items.append(door_group.name) 338 self.real_items.append(door_group.name)
177 339
178 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
179 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]:
180 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,
181 self.get_door_reqs(door.id))) 346 self.get_door_reqs(door.id)))
182 347
183 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
184 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,
185 AccessRequirements())) 353 AccessRequirements()))
186 behavior = self.get_letter_behavior(letter.key, letter.level2) 354 behavior = self.get_letter_behavior(letter.key, letter.level2)
187 if behavior == LetterBehavior.VANILLA: 355 if behavior == LetterBehavior.VANILLA:
188 letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}" 356 if not world.for_tracker:
189 event_name = f"{letter_name} (Collected)" 357 letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}"
190 self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() 358 event_name = f"{letter_name} (Collected)"
191
192 if letter.level2:
193 event_name = f"{letter_name} (Double Collected)"
194 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()
195 elif behavior == LetterBehavior.ITEM: 364 elif behavior == LetterBehavior.ITEM:
196 self.real_items.append(letter.key.upper()) 365 self.real_items.append(letter.key.upper())
197 366
@@ -199,30 +368,42 @@ class Lingo2PlayerLogic:
199 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
200 369
201 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
202 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,
203 AccessRequirements())) 375 AccessRequirements()))
204 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
205 for ending in world.static_logic.objects.endings: 381 for ending in world.static_logic.objects.endings:
206 # 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:
207 # condition, since it is necessarily going to be in the postgame. 383 continue
208 if ending.name == "WHITE": 384
209 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
210 continue 386 # necessarily in the postgame, i.e. it requires all 12 other endings.
211 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):
212 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,
213 AccessRequirements())) 390 AccessRequirements()))
214 391
215 event_name = f"{ending.name.capitalize()} Ending (Achieved)"
216 item_name = event_name
217
218 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:
219 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
220 396
221 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"
222 400
223 if self.world.options.keyholder_sanity: 401 if self.world.options.keyholder_sanity:
224 for keyholder in world.static_logic.objects.keyholders: 402 for keyholder in world.static_logic.objects.keyholders:
225 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
226 reqs = AccessRequirements() 407 reqs = AccessRequirements()
227 408
228 if self.get_letter_behavior(keyholder.key, False) != LetterBehavior.UNLOCKED: 409 if self.get_letter_behavior(keyholder.key, False) != LetterBehavior.UNLOCKED:
@@ -231,6 +412,10 @@ class Lingo2PlayerLogic:
231 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,
232 reqs)) 413 reqs))
233 414
415 if self.world.options.shuffle_symbols:
416 for symbol_name in SYMBOL_ITEMS.values():
417 self.real_items.append(symbol_name)
418
234 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:
235 if answer is None: 420 if answer is None:
236 if panel_id not in self.panel_reqs: 421 if panel_id not in self.panel_reqs:
@@ -253,25 +438,35 @@ class Lingo2PlayerLogic:
253 self.add_solution_reqs(reqs, answer) 438 self.add_solution_reqs(reqs, answer)
254 elif len(panel.proxies) > 0: 439 elif len(panel.proxies) > 0:
255 possibilities = [] 440 possibilities = []
441 already_filled = False
256 442
257 for proxy in panel.proxies: 443 for proxy in panel.proxies:
258 proxy_reqs = AccessRequirements() 444 proxy_reqs = AccessRequirements()
259 self.add_solution_reqs(proxy_reqs, proxy.answer) 445 self.add_solution_reqs(proxy_reqs, proxy.answer)
260 446
261 possibilities.append(proxy_reqs) 447 if not proxy_reqs.is_empty():
448 possibilities.append(proxy_reqs)
449 else:
450 already_filled = True
451 break
262 452
263 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):
264 proxy_reqs = AccessRequirements() 454 proxy_reqs = AccessRequirements()
265 self.add_solution_reqs(proxy_reqs, panel.answer) 455 self.add_solution_reqs(proxy_reqs, panel.answer)
266 456
267 possibilities.append(proxy_reqs) 457 if not proxy_reqs.is_empty():
458 possibilities.append(proxy_reqs)
459 else:
460 already_filled = True
268 461
269 reqs.or_logic.append(possibilities) 462 if not already_filled:
463 reqs.or_logic.append(possibilities)
270 else: 464 else:
271 self.add_solution_reqs(reqs, panel.answer) 465 self.add_solution_reqs(reqs, panel.answer)
272 466
273 for symbol in panel.symbols: 467 if self.world.options.shuffle_symbols:
274 reqs.symbols.add(symbol) 468 for symbol in panel.symbols:
469 reqs.items.add(SYMBOL_ITEMS.get(symbol))
275 470
276 if panel.HasField("required_door"): 471 if panel.HasField("required_door"):
277 door_reqs = self.get_door_open_reqs(panel.required_door) 472 door_reqs = self.get_door_open_reqs(panel.required_door)
@@ -294,21 +489,28 @@ class Lingo2PlayerLogic:
294 door = self.world.static_logic.objects.doors[door_id] 489 door = self.world.static_logic.objects.doors[door_id]
295 reqs = AccessRequirements() 490 reqs = AccessRequirements()
296 491
297 # TODO: lavender_cubes, endings
298 if not door.HasField("complete_at") or door.complete_at == 0: 492 if not door.HasField("complete_at") or door.complete_at == 0:
299 for proxy in door.panels: 493 for proxy in door.panels:
300 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)
301 reqs.merge(panel_reqs) 495 reqs.merge(panel_reqs)
302 elif door.complete_at == 1: 496 elif door.complete_at == 1:
303 reqs.or_logic.append([self.get_panel_reqs(proxy.panel, 497 disjunction = []
304 proxy.answer if proxy.HasField("answer") else None) 498 for proxy in door.panels:
305 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)
306 else: 507 else:
307 # TODO: Handle complete_at > 1 508 reqs.complete_at = door.complete_at
308 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)
309 512
310 if door.HasField("control_center_color"): 513 if door.HasField("control_center_color"):
311 # TODO: Logic for ensuring two CC states aren't needed at once.
312 reqs.rooms.add("Control Center - Main Area") 514 reqs.rooms.add("Control Center - Main Area")
313 self.add_solution_reqs(reqs, door.control_center_color) 515 self.add_solution_reqs(reqs, door.control_center_color)
314 516
@@ -316,7 +518,8 @@ class Lingo2PlayerLogic:
316 if self.world.options.cyan_door_behavior == CyanDoorBehavior.option_collect_h2: 518 if self.world.options.cyan_door_behavior == CyanDoorBehavior.option_collect_h2:
317 reqs.rooms.add("The Repetitive - Main Room") 519 reqs.rooms.add("The Repetitive - Main Room")
318 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:
319 reqs.cyans = True 521 if self.world.options.shuffle_letters != ShuffleLetters.option_unlocked:
522 reqs.cyans = True
320 elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_item: 523 elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_item:
321 # There shouldn't be any locations that are cyan doors. 524 # There shouldn't be any locations that are cyan doors.
322 pass 525 pass
@@ -333,14 +536,19 @@ class Lingo2PlayerLogic:
333 for room in door.rooms: 536 for room in door.rooms:
334 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))
335 538
336 for ending_id in door.endings: 539 if door.white_ending:
337 ending = self.world.static_logic.objects.endings[ending_id] 540 if self.world.options.endings_requirement > 0:
338 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
339 545
340 for sub_door_id in door.doors: 546 for sub_door_id in door.doors:
341 sub_reqs = self.get_door_open_reqs(sub_door_id) 547 sub_reqs = self.get_door_open_reqs(sub_door_id)
342 reqs.merge(sub_reqs) 548 reqs.merge(sub_reqs)
343 549
550 reqs.simplify()
551
344 return reqs 552 return reqs
345 553
346 # 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
@@ -395,3 +603,6 @@ class Lingo2PlayerLogic:
395 603
396 if needed > 0: 604 if needed > 0:
397 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 @@
1from typing import TYPE_CHECKING 1from typing import TYPE_CHECKING
2 2
3import BaseClasses
3from BaseClasses import Region, ItemClassification, Entrance 4from BaseClasses import Region, ItemClassification, Entrance
5from entrance_rando import randomize_entrances
4from .items import Lingo2Item 6from .items import Lingo2Item
5from .locations import Lingo2Location 7from .locations import Lingo2Location
6from .player_logic import AccessRequirements 8from .player_logic import AccessRequirements
@@ -11,21 +13,42 @@ if TYPE_CHECKING:
11 13
12 14
13def create_region(room, world: "Lingo2World") -> Region: 15def 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
19def 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
31def create_regions(world: "Lingo2World"): 54def 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
156def 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
228def 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 49ca0a7..dbc395b 100644 --- a/apworld/requirements.txt +++ b/apworld/requirements.txt
@@ -1 +1 @@
protobuf==3.20.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 @@
1from collections.abc import Callable 1from collections.abc import Callable
2from typing import TYPE_CHECKING 2from typing import TYPE_CHECKING
3 3
4from BaseClasses import CollectionState 4from BaseClasses import CollectionState, Region
5from .player_logic import AccessRequirements 5from .player_logic import AccessRequirements
6 6
7if TYPE_CHECKING: 7if TYPE_CHECKING:
8 from . import Lingo2World 8 from . import Lingo2World
9 9
10 10
11def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirements, world: "Lingo2World") -> bool: 11def 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
39def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World") -> Callable[[CollectionState], bool]: 56def 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 0cc7e55..8a84111 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py
@@ -1,6 +1,8 @@
1from .generated import data_pb2 as data_pb2 1from .generated import data_pb2 as data_pb2
2from .items import SYMBOL_ITEMS, ANTI_COLLECTABLE_TRAPS
2import pkgutil 3import pkgutil
3 4
5
4class Lingo2StaticLogic: 6class 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]
@@ -11,11 +13,17 @@ class Lingo2StaticLogic:
11 item_name_groups: dict[str, list[str]] 13 item_name_groups: dict[str, list[str]]
12 location_name_groups: dict[str, list[str]] 14 location_name_groups: dict[str, list[str]]
13 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
14 def __init__(self): 21 def __init__(self):
15 self.item_id_to_name = {} 22 self.item_id_to_name = {}
16 self.location_id_to_name = {} 23 self.location_id_to_name = {}
17 self.item_name_groups = {} 24 self.item_name_groups = {}
18 self.location_name_groups = {} 25 self.location_name_groups = {}
26 self.letter_weights = {}
19 27
20 file = pkgutil.get_data(__name__, "generated/data.binpb") 28 file = pkgutil.get_data(__name__, "generated/data.binpb")
21 self.objects = data_pb2.AllObjects() 29 self.objects = data_pb2.AllObjects()
@@ -63,10 +71,24 @@ class Lingo2StaticLogic:
63 self.location_name_groups.setdefault("Keyholders", []).append(location_name) 71 self.location_name_groups.setdefault("Keyholders", []).append(location_name)
64 72
65 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
66 81
67 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()}
68 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()}
69 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
70 def get_door_item_name(self, door: data_pb2.Door) -> str: 92 def get_door_item_name(self, door: data_pb2.Door) -> str:
71 return f"{self.get_map_object_map_name(door)} - {door.name}" 93 return f"{self.get_map_object_map_name(door)} - {door.name}"
72 94
@@ -90,7 +112,7 @@ class Lingo2StaticLogic:
90 if door.type != data_pb2.DoorType.STANDARD: 112 if door.type != data_pb2.DoorType.STANDARD:
91 return None 113 return None
92 114
93 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"):
94 return None 116 return None
95 117
96 if len(door.panels) > 4: 118 if len(door.panels) > 4:
@@ -126,7 +148,7 @@ class Lingo2StaticLogic:
126 for panel_id in door.panels] 148 for panel_id in door.panels]
127 panel_names.sort() 149 panel_names.sort()
128 150
129 return f"{map_part} - {", ".join(panel_names)}" 151 return map_part + " - " + ", ".join(panel_names)
130 152
131 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:
132 door = self.objects.doors[door_id] 154 door = self.objects.doors[door_id]
@@ -150,3 +172,10 @@ class Lingo2StaticLogic:
150 return f"{game_map.display_name} ({room.panel_display_name})" 172 return f"{game_map.display_name} ({room.panel_display_name})"
151 else: 173 else:
152 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 @@
1from typing import TYPE_CHECKING, Iterator
2
3from BaseClasses import MultiWorld, CollectionState, ItemClassification, Region, Entrance
4from NetUtils import NetworkItem
5from . import Lingo2World, Lingo2Item
6from .regions import connect_ports_from_ut
7from .options import Lingo2Options, ShuffleLetters
8
9if TYPE_CHECKING:
10 from .context import Lingo2Manager
11
12PLAYER_NUM = 1
13
14
15class 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 @@
1extends Node
2
3const ap_version = {"major": 0, "minor": 6, "build": 3, "class": "Version"}
4
5var SCRIPT_uuid
6
7var _ws = WebSocketPeer.new()
8var _should_process = false
9var _initiated_disconnect = false
10var _try_wss = false
11var _has_connected = false
12
13var _datapackages = {}
14var _pending_packages = []
15var _item_id_to_name = {} # All games
16var _location_id_to_name = {} # All games
17var _item_name_to_id = {} # Lingo 2 only
18var _location_name_to_id = {} # Lingo 2 only
19
20var _remote_version = {"major": 0, "minor": 0, "build": 0}
21var _gen_version = {"major": 0, "minor": 0, "build": 0}
22
23var ap_server = ""
24var ap_user = ""
25var ap_pass = ""
26
27var _authenticated = false
28var _seed = ""
29var _team = 0
30var _slot = 0
31var _players = []
32var _player_name_by_slot = {}
33var _game_by_player = {}
34var _checked_locations = []
35var _received_indexes = []
36var _received_items = {}
37var _slot_data = {}
38
39signal could_not_connect
40signal connect_status
41signal client_connected(slot_data)
42signal item_received(item_id, index, player, flags, amount)
43signal message_received(message)
44signal location_scout_received(item_id, location_id, player, flags)
45
46
47func _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
67func _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
76func _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
85func _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
99func _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
109func _connected(_proto = ""):
110 global._print("Connected!")
111 _try_wss = false
112
113
114func disconnect_from_ap():
115 _initiated_disconnect = true
116 _ws.close()
117
118
119func _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
278func 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
285func 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
319func sendMessage(msg):
320 var payload = JSON.stringify(msg)
321 _ws.send_text(payload)
322
323
324func requestDatapackages(games):
325 emit_signal("connect_status", "Downloading %s data package..." % games[0])
326
327 sendMessage([{"cmd": "GetDataPackage", "games": games}])
328
329
330func 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
349func 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
369func sendConnectUpdate(tags):
370 sendMessage([{"cmd": "ConnectUpdate", "tags": tags}])
371
372
373func requestSync():
374 sendMessage([{"cmd": "Sync"}])
375
376
377func sendLocation(loc_id):
378 sendMessage([{"cmd": "LocationChecks", "locations": [loc_id]}])
379
380
381func sendLocations(loc_ids):
382 sendMessage([{"cmd": "LocationChecks", "locations": loc_ids}])
383
384
385func 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
398func say(textdata):
399 sendMessage([{"cmd": "Say", "text": textdata}])
400
401
402func completedGoal():
403 sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL
404
405
406func scoutLocations(loc_ids):
407 sendMessage([{"cmd": "LocationScouts", "locations": loc_ids}])
408
409
410func hasItem(item_id):
411 return _received_items.has(item_id)
412
413
414func 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 @@
1extends Node
2
3var SCRIPT_proto
4
5var objects
6var door_id_by_map_node_path = {}
7var painting_id_by_map_node_path = {}
8var door_id_by_ap_id = {}
9var map_id_by_name = {}
10var progressive_id_by_ap_id = {}
11var letter_id_by_ap_id = {}
12
13
14func _init(proto_script):
15 SCRIPT_proto = proto_script
16
17
18func 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
62func 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
70func 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
78func get_door_receivers(door_id):
79 var door = objects.get_doors()[door_id]
80 return door.get_receivers()
81
82
83func 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 cd0654f..0000000 --- a/client/Archipelago/manager.gd +++ /dev/null
@@ -1,507 +0,0 @@
1extends Node
2
3const my_version = "0.1.0"
4
5var SCRIPT_client
6var SCRIPT_keyboard
7var SCRIPT_locationListener
8var SCRIPT_uuid
9var SCRIPT_victoryListener
10
11var ap_server = ""
12var ap_user = ""
13var ap_pass = ""
14var connection_history = []
15
16var client
17var keyboard
18
19var _localdata_file = ""
20var _last_new_item = -1
21var _batch_locations = false
22var _held_locations = []
23var _held_location_scouts = []
24var _location_scouts = {}
25var _item_locks = {}
26var _inverse_item_locks = {}
27var _held_letters = {}
28var _letters_setup = false
29
30const kSHUFFLE_LETTERS_VANILLA = 0
31const kSHUFFLE_LETTERS_UNLOCKED = 1
32const kSHUFFLE_LETTERS_PROGRESSIVE = 2
33const kSHUFFLE_LETTERS_VANILLA_CYAN = 3
34const kSHUFFLE_LETTERS_ITEM_CYAN = 4
35
36const kLETTER_BEHAVIOR_VANILLA = 0
37const kLETTER_BEHAVIOR_ITEM = 1
38const kLETTER_BEHAVIOR_UNLOCKED = 2
39
40const kCYAN_DOOR_BEHAVIOR_H2 = 0
41const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1
42const kCYAN_DOOR_BEHAVIOR_ITEM = 2
43
44var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
45var daedalus_roof_access = false
46var keyholder_sanity = false
47var shuffle_control_center_colors = false
48var shuffle_doors = false
49var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
50var victory_condition = -1
51
52signal could_not_connect
53signal connect_status
54signal ap_connected
55
56
57func _init():
58 # Read AP settings from file, if there are any
59 if FileAccess.file_exists("user://ap_settings"):
60 var file = FileAccess.open("user://ap_settings", FileAccess.READ)
61 var data = file.get_var(true)
62 file.close()
63
64 if typeof(data) != TYPE_ARRAY:
65 global._print("AP settings file is corrupted")
66 data = []
67
68 if data.size() > 0:
69 ap_server = data[0]
70
71 if data.size() > 1:
72 ap_user = data[1]
73
74 if data.size() > 2:
75 ap_pass = data[2]
76
77 if data.size() > 3:
78 connection_history = data[3]
79
80
81func _ready():
82 client = SCRIPT_client.new()
83 client.SCRIPT_uuid = SCRIPT_uuid
84
85 client.connect("item_received", _process_item)
86 client.connect("message_received", _process_message)
87 client.connect("location_scout_received", _process_location_scout)
88 client.connect("could_not_connect", _client_could_not_connect)
89 client.connect("connect_status", _client_connect_status)
90 client.connect("client_connected", _client_connected)
91
92 add_child(client)
93
94 keyboard = SCRIPT_keyboard.new()
95 add_child(keyboard)
96
97
98func saveSettings():
99 # Save the AP settings to disk.
100 var path = "user://ap_settings"
101 var file = FileAccess.open(path, FileAccess.WRITE)
102
103 var data = [
104 ap_server,
105 ap_user,
106 ap_pass,
107 connection_history,
108 ]
109 file.store_var(data, true)
110 file.close()
111
112
113func saveLocaldata():
114 # Save the MW/slot specific settings to disk.
115 var dir = DirAccess.open("user://")
116 var folder = "archipelago_data"
117 if not dir.dir_exists(folder):
118 dir.make_dir(folder)
119
120 var file = FileAccess.open(_localdata_file, FileAccess.WRITE)
121
122 var data = [
123 _last_new_item,
124 ]
125 file.store_var(data, true)
126 file.close()
127
128
129func connectToServer():
130 _last_new_item = -1
131 _batch_locations = false
132 _held_locations = []
133 _held_location_scouts = []
134 _location_scouts = {}
135 _letters_setup = false
136 _held_letters = {}
137
138 client.connectToServer(ap_server, ap_user, ap_pass)
139
140
141func getSaveFileName():
142 return "zzAP_%s_%d" % [client._seed, client._slot]
143
144
145func disconnect_from_ap():
146 client.disconnect_from_ap()
147
148
149func get_item_id_for_door(door_id):
150 return _item_locks.get(door_id, null)
151
152
153func _process_item(item, index, from, flags, amount):
154 var item_name = "Unknown"
155 if client._item_id_to_name["Lingo 2"].has(item):
156 item_name = client._item_id_to_name["Lingo 2"][item]
157
158 var gamedata = global.get_node("Gamedata")
159
160 var prog_id = null
161 if _inverse_item_locks.has(item):
162 for lock in _inverse_item_locks.get(item):
163 if lock[1] != amount:
164 continue
165
166 if gamedata.progressive_id_by_ap_id.has(item):
167 prog_id = lock[0]
168
169 if gamedata.get_door_map_name(lock[0]) != global.map:
170 continue
171
172 var receivers = gamedata.get_door_receivers(lock[0])
173 var scene = get_tree().get_root().get_node_or_null("scene")
174 if scene != null:
175 for receiver in receivers:
176 var rnode = scene.get_node_or_null(receiver)
177 if rnode != null:
178 rnode.handleTriggered()
179
180 var letter_id = gamedata.letter_id_by_ap_id.get(item, null)
181 if letter_id != null:
182 var letter = gamedata.objects.get_letters()[letter_id]
183 if not letter.has_level2() or not letter.get_level2():
184 _process_key_item(letter.get_key(), amount)
185
186 # Show a message about the item if it's new.
187 if index != null and index > _last_new_item:
188 _last_new_item = index
189 saveLocaldata()
190
191 var player_name = "Unknown"
192 if client._player_name_by_slot.has(float(from)):
193 player_name = client._player_name_by_slot[float(from)]
194
195 var item_color = colorForItemType(flags)
196
197 var full_item_name = item_name
198 if prog_id != null:
199 var door = gamedata.objects.get_doors()[prog_id]
200 full_item_name = "%s (%s)" % [item_name, door.get_name()]
201
202 var message
203 if from == client._slot:
204 message = "Found [color=%s]%s[/color]" % [item_color, full_item_name]
205 else:
206 message = (
207 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name]
208 )
209
210 global._print(message)
211
212 global.get_node("Messages").showMessage(message)
213
214
215func _process_message(message):
216 parse_printjson_for_textclient(message)
217
218 if (
219 !message.has("receiving")
220 or !message.has("item")
221 or message["item"]["player"] != client._slot
222 ):
223 return
224
225 var item_name = "Unknown"
226 var item_player_game = client._game_by_player[message["receiving"]]
227 if client._item_id_to_name[item_player_game].has(int(message["item"]["item"])):
228 item_name = client._item_id_to_name[item_player_game][int(message["item"]["item"])]
229
230 var location_name = "Unknown"
231 var location_player_game = client._game_by_player[message["item"]["player"]]
232 if client._location_id_to_name[location_player_game].has(int(message["item"]["location"])):
233 location_name = (client._location_id_to_name[location_player_game][int(
234 message["item"]["location"]
235 )])
236
237 var player_name = "Unknown"
238 if client._player_name_by_slot.has(message["receiving"]):
239 player_name = client._player_name_by_slot[message["receiving"]]
240
241 var item_color = colorForItemType(message["item"]["flags"])
242
243 if message["type"] == "Hint":
244 var is_for = ""
245 if message["receiving"] != client._slot:
246 is_for = " for %s" % player_name
247 if !message.has("found") || !message["found"]:
248 global.get_node("Messages").showMessage(
249 (
250 "Hint: [color=%s]%s[/color]%s is on %s"
251 % [item_color, item_name, is_for, location_name]
252 )
253 )
254 else:
255 if message["receiving"] != client._slot:
256 var sentMsg = "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name]
257 #if _hinted_locations.has(message["item"]["location"]):
258 # sentMsg += " ([color=#fafad2]Hinted![/color])"
259 global.get_node("Messages").showMessage(sentMsg)
260
261
262func parse_printjson_for_textclient(message):
263 var parts = []
264 for message_part in message["data"]:
265 if !message_part.has("type") and message_part.has("text"):
266 parts.append(message_part["text"])
267 elif message_part["type"] == "player_id":
268 if int(message_part["text"]) == client._slot:
269 parts.append(
270 "[color=#ee00ee]%s[/color]" % client._player_name_by_slot[client._slot]
271 )
272 else:
273 var from = float(message_part["text"])
274 parts.append("[color=#fafad2]%s[/color]" % client._player_name_by_slot[from])
275 elif message_part["type"] == "item_id":
276 var item_name = "Unknown"
277 var item_player_game = client._game_by_player[message_part["player"]]
278 if client._item_id_to_name[item_player_game].has(int(message_part["text"])):
279 item_name = client._item_id_to_name[item_player_game][int(message_part["text"])]
280
281 parts.append(
282 "[color=%s]%s[/color]" % [colorForItemType(message_part["flags"]), item_name]
283 )
284 elif message_part["type"] == "location_id":
285 var location_name = "Unknown"
286 var location_player_game = client._game_by_player[message_part["player"]]
287 if client._location_id_to_name[location_player_game].has(int(message_part["text"])):
288 location_name = client._location_id_to_name[location_player_game][int(
289 message_part["text"]
290 )]
291
292 parts.append("[color=#00ff7f]%s[/color]" % location_name)
293 elif message_part.has("text"):
294 parts.append(message_part["text"])
295
296 var textclient_node = global.get_node("Textclient")
297 if textclient_node != null:
298 textclient_node.parse_printjson("".join(parts))
299
300
301func _process_location_scout(item_id, location_id, player, flags):
302 _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags}
303
304 var gamedata = global.get_node("Gamedata")
305 var map_id = gamedata.map_id_by_name.get(global.map)
306
307 var item_name = "Unknown"
308 var item_player_game = client._game_by_player[float(player)]
309 if client._item_id_to_name[item_player_game].has(item_id):
310 item_name = client._item_id_to_name[item_player_game][item_id]
311
312 var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null)
313 if letter_id != null:
314 var letter = gamedata.objects.get_letters()[letter_id]
315 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
316 if room.get_map_id() == map_id:
317 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
318 letter.get_path()
319 )
320 if collectable != null:
321 collectable.setScoutedText(item_name)
322
323
324func _client_could_not_connect():
325 emit_signal("could_not_connect")
326
327
328func _client_connect_status(message):
329 emit_signal("connect_status", message)
330
331
332func _client_connected(slot_data):
333 var gamedata = global.get_node("Gamedata")
334
335 _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot]
336 _last_new_item = -1
337
338 if FileAccess.file_exists(_localdata_file):
339 var ap_file = FileAccess.open(_localdata_file, FileAccess.READ)
340 var localdata = []
341 if ap_file != null:
342 localdata = ap_file.get_var(true)
343 ap_file.close()
344
345 if typeof(localdata) != TYPE_ARRAY:
346 print("AP localdata file is corrupted")
347 localdata = []
348
349 if localdata.size() > 0:
350 _last_new_item = localdata[0]
351
352 # Read slot data.
353 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0))
354 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false))
355 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
356 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
357 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
358 shuffle_letters = int(slot_data.get("shuffle_letters", 0))
359 victory_condition = int(slot_data.get("victory_condition", 0))
360
361 # Set up item locks.
362 _item_locks = {}
363
364 if shuffle_doors:
365 for door in gamedata.objects.get_doors():
366 if (
367 door.get_type() == gamedata.SCRIPT_proto.DoorType.STANDARD
368 or door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
369 ):
370 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
371
372 for progressive in gamedata.objects.get_progressives():
373 for i in range(0, progressive.get_doors().size()):
374 var door = gamedata.objects.get_doors()[progressive.get_doors()[i]]
375 _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1]
376
377 for door_group in gamedata.objects.get_door_groups():
378 if (
379 door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR
380 or door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.SHUFFLE_GROUP
381 ):
382 for door in door_group.get_doors():
383 _item_locks[door] = [door_group.get_ap_id(), 1]
384
385 if shuffle_control_center_colors:
386 for door in gamedata.objects.get_doors():
387 if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR:
388 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
389
390 for door_group in gamedata.objects.get_door_groups():
391 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR:
392 for door in door_group.get_doors():
393 _item_locks[door] = [door_group.get_ap_id(), 1]
394
395 if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM:
396 for door_group in gamedata.objects.get_door_groups():
397 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS:
398 for door in door_group.get_doors():
399 if not _item_locks.has(door):
400 _item_locks[door] = [door_group.get_ap_id(), 1]
401
402 # Create a reverse item locks map for processing items.
403 _inverse_item_locks = {}
404
405 for door_id in _item_locks.keys():
406 var lock = _item_locks.get(door_id)
407
408 if not _inverse_item_locks.has(lock[0]):
409 _inverse_item_locks[lock[0]] = []
410
411 _inverse_item_locks[lock[0]].append([door_id, lock[1]])
412
413 emit_signal("ap_connected")
414
415
416func start_batching_locations():
417 _batch_locations = true
418
419
420func send_location(loc_id):
421 if _batch_locations:
422 _held_locations.append(loc_id)
423 else:
424 client.sendLocation(loc_id)
425
426
427func scout_location(loc_id):
428 if _location_scouts.has(loc_id):
429 return _location_scouts.get(loc_id)
430
431 if _batch_locations:
432 _held_location_scouts.append(loc_id)
433 else:
434 client.scoutLocation(loc_id)
435
436 return null
437
438
439func stop_batching_locations():
440 _batch_locations = false
441
442 if not _held_locations.is_empty():
443 client.sendLocations(_held_locations)
444 _held_locations.clear()
445
446 if not _held_location_scouts.is_empty():
447 client.scoutLocations(_held_location_scouts)
448 _held_location_scouts.clear()
449
450
451func colorForItemType(flags):
452 var int_flags = int(flags)
453 if int_flags & 1: # progression
454 if int_flags & 2: # proguseful
455 return "#f0d200"
456 else:
457 return "#bc51e0"
458 elif int_flags & 2: # useful
459 return "#2b67ff"
460 elif int_flags & 4: # trap
461 return "#d63a22"
462 else: # filler
463 return "#14de9e"
464
465
466func get_letter_behavior(key, level2):
467 if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED:
468 return kLETTER_BEHAVIOR_UNLOCKED
469
470 if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters):
471 if level2:
472 if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN:
473 return kLETTER_BEHAVIOR_VANILLA
474 else:
475 return kLETTER_BEHAVIOR_ITEM
476 else:
477 return kLETTER_BEHAVIOR_UNLOCKED
478
479 if not level2 and ["h", "i", "n", "t"].has(key):
480 # This differs from the equivalent function in the apworld. Logically it is
481 # the same as UNLOCKED since they are in the starting room, but VANILLA
482 # means the player still has to actually pick up the letters.
483 return kLETTER_BEHAVIOR_VANILLA
484
485 if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE:
486 return kLETTER_BEHAVIOR_ITEM
487
488 return kLETTER_BEHAVIOR_VANILLA
489
490
491func setup_keys():
492 keyboard.load_seed()
493
494 _letters_setup = true
495
496 for k in _held_letters.keys():
497 _process_key_item(k, _held_letters[k])
498
499 _held_letters.clear()
500
501
502func _process_key_item(key, level):
503 if not _letters_setup:
504 _held_letters[key] = max(_held_letters.get(key, 0), level)
505 return
506
507 keyboard.collect_remote_letter(key, level)
diff --git a/client/Archipelago/pauseMenu.gd b/client/Archipelago/pauseMenu.gd deleted file mode 100644 index 5da114a..0000000 --- a/client/Archipelago/pauseMenu.gd +++ /dev/null
@@ -1,12 +0,0 @@
1extends "res://scripts/ui/pauseMenu.gd"
2
3
4func _pause_game():
5 global.get_node("Textclient").dismiss()
6 super._pause_game()
7
8
9func _main_menu():
10 global.loaded = false
11 global.get_node("Archipelago").disconnect_from_ap()
12 super._main_menu()
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 @@
1extends "res://scripts/nodes/saver.gd"
2
3
4func 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 @@
1extends Button
2
3
4func _ready():
5 pass
6
7
8func _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
20func _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 @@
1extends Node2D
2
3
4func _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
101func 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
117func 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
126func 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
178func 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
189func 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
198func 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 85cc6d2..0000000 --- a/client/Archipelago/textclient.gd +++ /dev/null
@@ -1,86 +0,0 @@
1extends CanvasLayer
2
3var panel
4var label
5var entry
6var is_open = false
7
8
9func _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
52func _input(event):
53 if global.loaded and 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
70func 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
78func parse_printjson(text):
79 label.append_text("[p]" + text + "[/p]")
80
81
82func 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 @@
1MIT License
2
3Copyright (c) 2023 Xavier Sellier
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE. \ 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.
3extends Node
4
5const BYTE_MASK: int = 0b11111111
6
7
8static 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
31static 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
53static 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
85static 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
117var _uuid: Array
118
119
120func _init(rng := RandomNumberGenerator.new()) -> void:
121 _uuid = uuidbinrng(rng)
122
123
124func as_array() -> Array:
125 return _uuid.duplicate()
126
127
128func 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
163func 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
192func 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 @@
1extends "res://scripts/nodes/listeners/worldportListener.gd"
2
3
4func changeScene():
5 if exit == "menus/credits":
6 return
7
8 super.changeScene()
diff --git a/client/README.md b/client/README.md deleted file mode 100644 index 1e94bdb..0000000 --- a/client/README.md +++ /dev/null
@@ -1,97 +0,0 @@
1# Lingo 2 Archipelago Client
2
3The Lingo 2 Archipelago Client is a mod for Lingo 2 that allows you to connect
4to an Archipelago Multiworld and randomize your game.
5
6## Installation
7
81. Download the Lingo 2 Archipelago Randomizer from
9 [the releases page](https://code.fourisland.com/lingo2-archipelago/about/client/CHANGELOG.md).
102. Open up Lingo 2, go to settings, and click View Game Data. This should open
11 up a folder in Windows Explorer.
123. Unzip the randomizer into the "maps" folder. Ensure that archipelago.tscn and
13 the Archipelago folder are both within the maps folder.
14
15**NOTE**: It is important that the major version number of your client matches
16the major version number of the apworld you generated with.
17
18## Joining a Multiworld game
19
201. Launch Lingo 2.
212. Click on Level Selection, and choose Archipelago from the list.
223. The selected player is generally ignored by the mod, and you don't even need
23 to ensure you use the same player between connections. However, if your
24 player name has a gift map associated with it, Lingo 2 will prioritize the
25 gift map over loading the mod, so in that case you should choose another
26 player.
274. Press Play.
285. Enter the Archipelago address, slot name, and password into the fields.
296. Press Connect.
307. Enjoy!
31
32To continue an earlier game, you can perform the exact same steps as above. You
33will probably have to re-select Archipelago from the Level Selection screen, as
34the game does not remember which level you were playing.
35
36**Note**: Running the randomizer modifies the game's memory. If you want to play
37the base game after playing the randomizer, you need to restart Lingo 2 first.
38
39## Running from source
40
41The mod is mostly written in GDScript, which is parsed and executed by Lingo 2
42itself, and thus does not need to be compiled. However, there are two files that
43need to be generated before the client can be run.
44
45The first file is `data.binpb`, the datafile containing the randomizer logic.
46You can read about how to generate it on
47[its own README page](https://code.fourisland.com/lingo2-archipelago/about/data/README.md).
48Once you have it, put it in a subfolder of `client` called `generated`.
49
50The second generated file is `proto.gd`. This file allows Lingo 2 to read the
51datafile. We use a Godot script to generate it, which means
52[the Godot Editor](https://godotengine.org/download/) is required. From the root
53of the repository:
54
55```shell
56cd vendor\godobuf
57godot --headless -s addons\protobuf\protobuf_cmdln.gd --input=..\..\proto\data.proto ^
58 --output=..\..\client\Archipelago\generated\proto.gd
59```
60
61If you are not on Windows, replace the forward slashes with backslashes as
62appropriate (and the caret with a forward slash). You will also probably need to
63replace "godot" at the start of the second line with a path to a Godot Editor
64executable.
65
66After generating those two files, the contents of the `client` folder (minus
67this README) can be pasted into the Lingo 2 maps directory as described above.
68
69## Frequently Asked Questions
70
71### Is my progress saved locally?
72
73Lingo 2 autosaves your progress every time you solve a puzzle, get a
74collectable, or interact with a keyholder. The randomizer generates a savefile
75name based on your Multiworld seed and slot number, so you should be able to
76seamlessly switch between multiworlds and even slots within a multiworld.
77
78The exception to this is different rooms created from the same multiworld seed.
79The client is unable to tell rooms in a seed apart (this is a limitation of the
80Archipelago API), so the client will use the same save file for the same slot in
81different rooms on the same seed. You can work around this by manually moving or
82removing the save file from the level1 save file directory.
83
84If you play the base game again, you will see one or more save files with a long
85name that begins with "zzAP\_". These are the saves for your multiworlds. They
86can be safely deleted after you have completed the associated multiworld. It is
87not recommended to load these save files outside of the randomizer.
88
89A connection to Archipelago is required to resume playing a multiworld. This is
90because the set of items you have received is not stored locally.
91
92### What about wall snipes?
93
94"Wall sniping" refers to the fact that you are able to solve puzzles on the
95other side of opaque walls. The player is never expected to or required to do
96this in normal gameplay. This randomizer does not change how wall snipes work,
97but it will likewise never require the use of them.
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] 6script/source = "extends Node2D
7[ext_resource type="Theme" uid="uid://7w454egydi41" path="res://assets/themes/baseUI.tres" id="2_g4bvn"] 7
8 8const CACHE_PATH = \"user://apworld_path.txt\"
9[sub_resource type="StyleBoxFlat" id=1] 9
10bg_color = Color( 0, 0, 0, 0 ) 10
11 11func _ready():
12[sub_resource type="StyleBoxFlat" id=2] 12 if FileAccess.file_exists(CACHE_PATH):
13bg_color = Color( 1, 1, 1, 1 ) 13 var file = FileAccess.open(CACHE_PATH, FileAccess.READ)
14border_width_left = 1 14 $Panel/HBoxContainer/LineEdit.text = file.get_as_text()
15border_width_top = 1 15 file.close()
16border_width_right = 1 16
17border_width_bottom = 1 17
18border_color = Color( 1, 1, 0, 1 ) 18func _browse_pressed():
19border_blend = true 19 $FileDialog.popup_centered()
20corner_radius_top_left = 3 20
21corner_radius_top_right = 3 21
22corner_radius_bottom_right = 3 22func _file_selected(path):
23corner_radius_bottom_left = 3 23 $Panel/HBoxContainer/LineEdit.text = path
24expand_offset_left = 5.0 24
25expand_offset_right = 5.0 25
26expand_offset_top = 5.0 26func _start_pressed():
27expand_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):
30script = 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
60func _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"]
66script = SubResource( 2 )
31 67
32[node name="Panel" type="Panel" parent="."] 68[node name="Panel" type="Panel" parent="."]
69anchors_preset = -1
33offset_right = 1920.0 70offset_right = 1920.0
34offset_bottom = 1080.0 71offset_bottom = 1080.0
35 72
36[node name="title" parent="Panel" type="Label"] 73[node name="Label" type="Label" parent="Panel"]
37offset_left = 0.0 74layout_mode = 1
75anchors_preset = -1
38offset_top = 75.0 76offset_top = 75.0
39offset_right = 1920.0 77offset_right = 1920.0
40offset_bottom = 225.0 78offset_bottom = 225.0
41text = "ARCHIPELAGO" 79theme = ExtResource("1_mw3f1")
42valign = 1 80text = "archipelago"
43theme = ExtResource("2_g4bvn") 81horizontal_alignment = 1
44 82vertical_alignment = 1
45[node name="credit" parent="Panel" type="Label"] 83
46visible = false 84[node name="Label2" type="Label" parent="Panel"]
47offset_left = 1278.0 85layout_mode = 1
48offset_top = 974.0 86anchors_preset = -1
49offset_right = 1868.0 87anchor_right = 1.0
50offset_bottom = 1034.0 88offset_left = 80.0
51text = "Brenton Wildes" 89offset_top = 300.0
52theme = ExtResource("2_g4bvn") 90offset_right = -80.0
53 91offset_bottom = 388.0
54[node name="connect_button" parent="Panel" type="Button"] 92theme = ExtResource("1_mw3f1")
93theme_override_font_sizes/font_size = 56
94text = "Put the path to your lingo2.apworld in the below field and click start. Then, open the archipelago launcher and click \"Lingo 2 Client\"."
95horizontal_alignment = 1
96autowrap_mode = 3
97
98[node name="HBoxContainer" type="HBoxContainer" parent="Panel"]
99layout_mode = 1
100anchors_preset = -1
101anchor_right = 1.0
102offset_left = 80.0
103offset_top = 595.0
104offset_right = -80.0
105offset_bottom = 755.0
106theme_override_constants/separation = 32
107
108[node name="LineEdit" type="LineEdit" parent="Panel/HBoxContainer"]
109layout_mode = 2
110size_flags_horizontal = 3
111
112[node name="Button" type="Button" parent="Panel/HBoxContainer"]
113layout_mode = 2
114theme = ExtResource("1_mw3f1")
115text = "browse"
116
117[node name="StartButton" type="Button" parent="Panel"]
118layout_mode = 1
119anchors_preset = -1
55offset_left = 255.0 120offset_left = 255.0
56offset_top = 875.0 121offset_top = 875.0
57offset_right = 891.0 122offset_right = 891.0
58offset_bottom = 1025.0 123offset_bottom = 1025.0
59custom_colors/font_color_hover = Color( 1, 0.501961, 0, 1 ) 124theme = ExtResource("1_mw3f1")
60text = "CONNECT" 125text = "start"
61theme = ExtResource("2_g4bvn")
62script = ExtResource( 4 )
63 126
64[node name="quit_button" parent="Panel" type="Button"] 127[node name="QuitButton" type="Button" parent="Panel"]
128layout_mode = 1
129anchors_preset = -1
65offset_left = 1102.0 130offset_left = 1102.0
66offset_top = 875.0 131offset_top = 875.0
67offset_right = 1738.0 132offset_right = 1738.0
68offset_bottom = 1025.0 133offset_bottom = 1025.0
69custom_colors/font_color_hover = Color( 1, 0, 0, 1 ) 134theme = ExtResource("1_mw3f1")
70text = "BACK" 135text = "back"
71theme = ExtResource("2_g4bvn") 136
72script = ExtResource( 4 ) 137[node name="FileDialog" type="FileDialog" parent="."]
73 138title = "Open a File"
74[node name="credit2" parent="Panel" type="Label"] 139size = Vector2i(512, 512)
75offset_left = -105.0 140ok_button_text = "Open"
76offset_top = 346.0 141file_mode = 0
77offset_right = 485.0 142access = 2
78offset_bottom = 410.0 143filters = PackedStringArray("*.apworld;Archipelago Worlds")
79custom_styles/normal = SubResource( 1 ) 144show_hidden_files = true
80text = "SERVER" 145use_native_dialog = true
81align = 2 146
82theme = ExtResource("2_g4bvn") 147[node name="AcceptDialog" type="AcceptDialog" parent="."]
83 148dialog_text = "Could not open Lingo 2 apworld. Please check that the path is correct."
84[node name="credit5" parent="Panel" type="Label"] 149
85offset_left = 1239.0 150[connection signal="pressed" from="Panel/HBoxContainer/Button" to="." method="_browse_pressed"]
86offset_top = 422.0 151[connection signal="pressed" from="Panel/StartButton" to="." method="_start_pressed"]
87offset_right = 1829.0 152[connection signal="pressed" from="Panel/QuitButton" to="." method="_quit_pressed"]
88offset_bottom = 486.0 153[connection signal="file_selected" from="FileDialog" to="." method="_file_selected"]
89custom_styles/normal = SubResource( 1 )
90text = "OPTIONS"
91theme = ExtResource("2_g4bvn")
92
93[node name="credit3" parent="Panel" type="Label"]
94offset_left = -105.0
95offset_top = 519.0
96offset_right = 485.0
97offset_bottom = 583.0
98custom_styles/normal = SubResource( 1 )
99text = "PLAYER"
100align = 2
101theme = ExtResource("2_g4bvn")
102
103[node name="credit4" parent="Panel" type="Label"]
104offset_left = -105.0
105offset_top = 704.0
106offset_right = 485.0
107offset_bottom = 768.0
108custom_styles/normal = SubResource( 1 )
109text = "PASSWORD"
110align = 2
111theme = ExtResource("2_g4bvn")
112
113[node name="server_box" type="LineEdit" parent="Panel"]
114offset_left = 502.0
115offset_top = 295.0
116offset_right = 1144.0
117offset_bottom = 445.0
118custom_colors/selection_color = Color( 0.482353, 0, 0, 1 )
119custom_colors/cursor_color = Color( 0, 0, 0, 1 )
120custom_colors/font_color = Color( 0, 0, 0, 1 )
121custom_styles/focus = SubResource( 2 )
122align = 1
123caret_blink = true
124
125[node name="player_box" type="LineEdit" parent="Panel"]
126offset_left = 502.0
127offset_top = 477.0
128offset_right = 1144.0
129offset_bottom = 627.0
130custom_colors/selection_color = Color( 0.482353, 0, 0, 1 )
131custom_colors/cursor_color = Color( 0, 0, 0, 1 )
132custom_colors/font_color = Color( 0, 0, 0, 1 )
133custom_styles/focus = SubResource( 2 )
134align = 1
135caret_blink = true
136
137[node name="password_box" type="LineEdit" parent="Panel"]
138offset_left = 502.0
139offset_top = 659.0
140offset_right = 1144.0
141offset_bottom = 809.0
142custom_colors/selection_color = Color( 0.482353, 0, 0, 1 )
143custom_colors/cursor_color = Color( 0, 0, 0, 1 )
144custom_colors/font_color = Color( 0, 0, 0, 1 )
145custom_styles/focus = SubResource( 2 )
146align = 1
147caret_blink = true
148
149[node name="AcceptDialog" type="AcceptDialog" parent="Panel"]
150offset_right = 83.0
151offset_bottom = 58.0
152
153[node name="connection_history" type="MenuButton" parent="Panel"]
154offset_left = 1239.0
155offset_top = 276.0
156offset_right = 1829.0
157offset_bottom = 372.0
158text = "connection history"
159flat = 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 @@
1Used in vanilla doors:
2
3The Between - RIGHT
4
5
6
7
8Used in a pseudo-connection:
9
10The Sturdy - COLORS
11
12
13
14
15Unsolved panels in letter rooms:
16
17Daedalus (F2 Room) - DEADLY
18Daedalus (F2 Room) - DIMS
19Daedalus (F2 Room) - GRAVE
20Daedalus (F2 Room) - LETHAL
21Daedalus (O2 Room) - ACCEPT
22Daedalus (O2 Room) - FOLLOW
23Daedalus (O2 Room) - PLEDGE
24Daedalus (O2 Room) - WARNING
25Daedalus (U2 Room) - EFFECTIVE
26Daedalus (U2 Room) - HELPFUL
27Daedalus (U2 Room) - INFERNAL
28Daedalus (U2 Room) - PRACTICAL
29Daedalus (U2 Room) - PRODUCTIVE
30Daedalus (U2 Room) - WONDERLAND
31The Digital - EYE
32The Digital - HIGH
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
3connections { 1connections {
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}
311connections {
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}
312connections { 327connections {
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.
844connections { 861connections {
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}
878connections {
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}
860connections { 896connections {
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}
1441connections { 1476connections {
@@ -1454,6 +1489,7 @@ connections {
1454 } 1489 }
1455 } 1490 }
1456 oneway: true 1491 oneway: true
1492 bypass_target_door: true
1457} 1493}
1458connections { 1494connections {
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}
1577connections {
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}
1753connections { 1807connections {
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}
1833connections { 1887connections {
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}
1850connections { 1903connections {
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}
1884connections { 1939connections {
1885 from { 1940 from {
@@ -2405,3 +2460,281 @@ connections {
2405 } 2460 }
2406 } 2461 }
2407} 2462}
2463connections {
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}
2479connections {
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}
2494connections {
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}
2509connections {
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}
2526connections {
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}
2542connections {
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}
2559connections {
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}
2575connections {
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}
2592connections {
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}
2608connections {
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}
2624connections {
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}
2641connections {
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}
2658connections {
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}
2675connections {
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}
2691connections {
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}
2708connections {
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}
2725connections {
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 1a75c45..fab75f5 100644 --- a/data/door_groups.txtpb +++ b/data/door_groups.txtpb
@@ -1,6 +1,9 @@
1door_groups { 1door_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 {
@@ -122,6 +129,10 @@ door_groups {
122 name: "Cyan Doors" 129 name: "Cyan Doors"
123 } 130 }
124 doors { 131 doors {
132 map: "the_owl"
133 name: "Double Letters"
134 }
135 doors {
125 map: "the_parthenon" 136 map: "the_parthenon"
126 name: "Double Letters" 137 name: "Double Letters"
127 } 138 }
diff --git a/data/ids.yaml b/data/ids.yaml index e2ec985..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,12 +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
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
1487 the_darkroom: 1905 the_darkroom:
1488 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
1489 First Room: 1916 First Room:
1490 panels: 1917 panels:
1491 BISON: 225 1918 BISON: 225
@@ -1493,6 +1920,11 @@ maps:
1493 KOI: 228 1920 KOI: 228
1494 SHEEP: 227 1921 SHEEP: 227
1495 TUNA: 229 1922 TUNA: 229
1923 ports:
1924 ENTRY: 3155
1925 First Room Exit:
1926 ports:
1927 NEXT: 3154
1496 Second Room: 1928 Second Room:
1497 panels: 1929 panels:
1498 BISON: 231 1930 BISON: 231
@@ -1500,6 +1932,11 @@ maps:
1500 KOI: 234 1932 KOI: 234
1501 SHEEP: 233 1933 SHEEP: 233
1502 TUNA: 235 1934 TUNA: 235
1935 ports:
1936 ENTRY: 3157
1937 Second Room Exit:
1938 ports:
1939 NEXT: 3156
1503 Third Room: 1940 Third Room:
1504 panels: 1941 panels:
1505 COINS: 238 1942 COINS: 238
@@ -1511,6 +1948,8 @@ maps:
1511 LOCKS: 242 1948 LOCKS: 242
1512 TOUCHES: 243 1949 TOUCHES: 243
1513 TURNS: 237 1950 TURNS: 237
1951 ports:
1952 ENTRY: 3158
1514 doors: 1953 doors:
1515 Colorful Entrance: 222 1954 Colorful Entrance: 222
1516 Congruent Entrance: 223 1955 Congruent Entrance: 223
@@ -1530,17 +1969,30 @@ maps:
1530 INN: 254 1969 INN: 254
1531 OUT: 257 1970 OUT: 257
1532 YOU: 255 1971 YOU: 255
1972 Gallery Maze:
1973 ports:
1974 GALLERY: 3159
1533 Main Area: 1975 Main Area:
1534 panels: 1976 panels:
1535 COLOR: 261 1977 COLOR: 261
1536 HIT: 258 1978 HIT: 258
1537 PAINTING: 260 1979 PAINTING: 260
1538 TIN: 259 1980 TIN: 259
1981 ports:
1982 ENTRY1: 3160
1983 ENTRY2: 3161
1984 ENTRY3: 3162
1539 Tree Area: 1985 Tree Area:
1540 panels: 1986 panels:
1541 TREE: 262 1987 TREE: 262
1988 ports:
1989 TREE: 3163
1990 Unyielding Entrance:
1991 ports:
1992 UNYIELDING: 3164
1542 doors: 1993 doors:
1543 Control Center Blue Door: 246 1994 Control Center Blue Door: 246
1995 Control Center Blue Panel: 3308
1544 Gallery Entrance: 245 1996 Gallery Entrance: 245
1545 Tree Entrance: 247 1997 Tree Entrance: 247
1546 the_door: 1998 the_door:
@@ -1604,6 +2056,12 @@ maps:
1604 panels: 2056 panels:
1605 ATTIC: 285 2057 ATTIC: 285
1606 FULL: 286 2058 FULL: 286
2059 ports:
2060 DARKROOM: 3165
2061 doors:
2062 10 Panels: 3310
2063 15 Panels: 3311
2064 5 Panels: 3309
1607 the_entry: 2065 the_entry:
1608 rooms: 2066 rooms:
1609 Blue Alcove: 2067 Blue Alcove:
@@ -1613,6 +2071,9 @@ maps:
1613 Colored Doors Area: 2071 Colored Doors Area:
1614 panels: 2072 panels:
1615 OPEN: 327 2073 OPEN: 327
2074 Composite Room Entrance:
2075 ports:
2076 COMPOSITE: 3166
1616 Ctrl Tutorial: 2077 Ctrl Tutorial:
1617 panels: 2078 panels:
1618 RIGHT: 328 2079 RIGHT: 328
@@ -1627,16 +2088,23 @@ maps:
1627 RED: 332 2088 RED: 332
1628 SPRAY: 336 2089 SPRAY: 336
1629 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
1630 Eye Room: 2100 Eye Room:
1631 panels: 2101 panels:
1632 I: 339 2102 I: 339
2103 ports:
2104 LIONIZED: 3170
1633 Flipped Link Area: 2105 Flipped Link Area:
1634 panels: 2106 panels:
1635 WANDER: 340 2107 WANDER: 340
1636 Flipped Pyramid Area:
1637 panels:
1638 TURN (1): 341
1639 TURN (2): 342
1640 Flipped Right Eye: 2108 Flipped Right Eye:
1641 panels: 2109 panels:
1642 HERE: 344 2110 HERE: 344
@@ -1645,9 +2113,14 @@ maps:
1645 panels: 2113 panels:
1646 CLUE: 345 2114 CLUE: 345
1647 SLENDER: 346 2115 SLENDER: 346
2116 Four Rooms Entrance:
2117 ports:
2118 FOUR: 3171
1648 Gallery Return: 2119 Gallery Return:
1649 panels: 2120 panels:
1650 RETURN: 347 2121 RETURN: 347
2122 ports:
2123 GALLERY: 3172
1651 Least Blue Last: 2124 Least Blue Last:
1652 panels: 2125 panels:
1653 AIL: 356 2126 AIL: 356
@@ -1660,6 +2133,14 @@ maps:
1660 STEALER: 352 2133 STEALER: 352
1661 TRUST: 354 2134 TRUST: 354
1662 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
1663 Lime Room: 2144 Lime Room:
1664 panels: 2145 panels:
1665 COLOR: 361 2146 COLOR: 361
@@ -1668,12 +2149,22 @@ maps:
1668 Link Area: 2149 Link Area:
1669 panels: 2150 panels:
1670 WANDER: 362 2151 WANDER: 362
2152 Literate Entrance:
2153 ports:
2154 BROWN: 3175
2155 Literate Entrance Panel:
2156 panels:
2157 TURN (2): 342
1671 Parthenon Return: 2158 Parthenon Return:
1672 panels: 2159 panels:
1673 RETURN: 363 2160 RETURN: 363
2161 ports:
2162 PARTHENON: 3176
1674 Rabbit Hole: 2163 Rabbit Hole:
1675 panels: 2164 panels:
1676 PUZZLE: 364 2165 Blank: 364
2166 ports:
2167 HOLE: 3177
1677 Rabbit Hole Lock: 2168 Rabbit Hole Lock:
1678 panels: 2169 panels:
1679 HOLE: 2749 2170 HOLE: 2749
@@ -1691,6 +2182,12 @@ maps:
1691 RAIN WOMAN: 373 2182 RAIN WOMAN: 373
1692 WANDER: 370 2183 WANDER: 370
1693 WOMAN: 372 2184 WOMAN: 372
2185 Repetitive Entrance:
2186 ports:
2187 REPETITIVE: 3178
2188 Revitalized Entrance:
2189 ports:
2190 REVITALIZED: 3179
1694 Right Eye: 2191 Right Eye:
1695 panels: 2192 panels:
1696 EYE: 374 2193 EYE: 374
@@ -1699,9 +2196,12 @@ maps:
1699 Shop Entrance: 2196 Shop Entrance:
1700 panels: 2197 panels:
1701 TURN: 377 2198 TURN: 377
2199 ports:
2200 SHOP: 3180
1702 Starting Room: 2201 Starting Room:
1703 panels: 2202 panels:
1704 EYE: 380 2203 EYE: 380
2204 Gift Maps: 2970
1705 HI: 378 2205 HI: 378
1706 HINT: 381 2206 HINT: 381
1707 THAN: 383 2207 THAN: 383
@@ -1710,33 +2210,45 @@ maps:
1710 Trick Room: 2210 Trick Room:
1711 panels: 2211 panels:
1712 INK: 388 2212 INK: 388
2213 White Hallway To Daedalus:
2214 ports:
2215 DAEDALUS: 3181
1713 Wrath Room: 2216 Wrath Room:
1714 panels: 2217 panels:
1715 CORN: 393 2218 CORN: 393
1716 DICE: 392 2219 DICE: 392
1717 WREATH: 391 2220 WREATH: 391
2221 X Area:
2222 ports:
2223 CC: 3182
1718 doors: 2224 doors:
2225 Big Eyes: 3316
1719 Blue Alcove Entrance: 297 2226 Blue Alcove Entrance: 297
1720 Blue Alcove Exit: 293 2227 Blue Alcove Exit: 293
1721 Colored Doors Area Entrance: 318 2228 Colored Doors Area Entrance: 318
1722 Composite Room Entrance: 309 2229 Composite Room Entrance: 309
1723 Control Center White Door: 307 2230 Control Center White Door: 307
2231 Control Center White Panel: 3318
1724 Corners Painting: 292 2232 Corners Painting: 292
1725 D Room Entrance: 319 2233 D Room Entrance: 319
1726 Daedalus Entrance: 311 2234 Daedalus Entrance: 311
1727 Flip Area Entrance: 310 2235 Flip Area Entrance: 310
2236 Flipped Right Eye Panels: 3315
1728 Flipped Second Room Left Door: 300 2237 Flipped Second Room Left Door: 300
1729 Flipped Second Room Right Door: 299 2238 Flipped Second Room Right Door: 299
1730 Gallery Entrance: 321 2239 Gallery Entrance: 321
1731 L Room Entrance: 322 2240 L Room Entrance: 322
2241 Least Blue Last: 3317
1732 Liberated Entrance: 314 2242 Liberated Entrance: 314
1733 Lime Room Entrance: 305 2243 Lime Room Entrance: 305
1734 Link Area Entrance: 288 2244 Link Area Entrance: 288
1735 Literate Entrance: 316 2245 Literate Entrance: 316
1736 Near D Room Painting: 320 2246 Near D Room Painting: 320
1737 Noon Door: 295 2247 Noon Door: 295
2248 Noon Door Panels: 3312
1738 Orange Door Hider: 304 2249 Orange Door Hider: 304
1739 Parthenon Entrance: 317 2250 Parthenon Entrance: 317
2251 Rabbit Hole Blank Puzzle: 3319
1740 Rabbithole Door: 294 2252 Rabbithole Door: 294
1741 Red Alcove Exit: 291 2253 Red Alcove Exit: 291
1742 Red Blue Area Left Door: 302 2254 Red Blue Area Left Door: 302
@@ -1745,6 +2257,7 @@ maps:
1745 Revitalized Entrance: 306 2257 Revitalized Entrance: 306
1746 Right Eye Entrance: 301 2258 Right Eye Entrance: 301
1747 Scarf Door: 296 2259 Scarf Door: 296
2260 Scarf Door Panels: 3313
1748 Second Room Left Door: 298 2261 Second Room Left Door: 298
1749 Second Room Right Door: 290 2262 Second Room Right Door: 290
1750 Shop Entrance: 313 2263 Shop Entrance: 313
@@ -1752,6 +2265,8 @@ maps:
1752 Third Eye Painting: 324 2265 Third Eye Painting: 324
1753 Trick Door: 287 2266 Trick Door: 287
1754 Trick To Shop Door: 289 2267 Trick To Shop Door: 289
2268 Wander Panels: 3314
2269 Wrath Room Puzzles: 3320
1755 X Area Entrance: 308 2270 X Area Entrance: 308
1756 the_extravagant: 2271 the_extravagant:
1757 rooms: 2272 rooms:
@@ -1797,6 +2312,33 @@ maps:
1797 panels: 2312 panels:
1798 CACTUS: 410 2313 CACTUS: 410
1799 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
1800 the_gallery: 2342 the_gallery:
1801 rooms: 2343 rooms:
1802 Back Room: 2344 Back Room:
@@ -1809,6 +2351,8 @@ maps:
1809 Main Area: 2351 Main Area:
1810 keyholders: 2352 keyholders:
1811 P: 2765 2353 P: 2765
2354 ports:
2355 ENTRY: 3184
1812 doors: 2356 doors:
1813 Ancient Painting: 428 2357 Ancient Painting: 428
1814 Between Painting: 414 2358 Between Painting: 414
@@ -1834,6 +2378,8 @@ maps:
1834 The Whole Thing: 2378 The Whole Thing:
1835 panels: 2379 panels:
1836 PANEL: 434 2380 PANEL: 434
2381 doors:
2382 The Panel: 3322
1837 the_graveyard: 2383 the_graveyard:
1838 rooms: 2384 rooms:
1839 Inside: 2385 Inside:
@@ -1843,34 +2389,34 @@ maps:
1843 panels: 2389 panels:
1844 FOOT: 436 2390 FOOT: 436
1845 SEVERE: 437 2391 SEVERE: 437
2392 doors:
2393 Remember Panel: 3323
1846 the_great: 2394 the_great:
1847 rooms: 2395 rooms:
1848 Back Area: 2396 Back Area:
1849 panels: 2397 panels:
1850 Left Landscape Bottom: 482
1851 Left Landscape Left: 483
1852 Left Landscape Right: 481
1853 Left Landscape Top: 480
1854 PAINTING: 474 2398 PAINTING: 474
1855 PLANT: 472 2399 PLANT: 472
1856 Right Landscape Bottom: 486
1857 Right Landscape Left: 487
1858 Right Landscape Right: 485
1859 Right Landscape Top: 484
1860 TOWEL: 475 2400 TOWEL: 475
1861 TREE: 473 2401 TREE: 473
1862 Top Landscape Bottom: 478 2402 ports:
1863 Top Landscape Left: 479 2403 THREEDOORS: 3186
1864 Top Landscape Right: 477 2404 TOWER: 3187
1865 Top Landscape Top: 476 2405 TREE: 3188
2406 UNKEMPT: 3185
1866 Behind Question Area: 2407 Behind Question Area:
1867 panels: 2408 panels:
1868 DEW: 488 2409 DEW: 488
1869 NO: 490 2410 NO: 490
1870 YEW: 489 2411 YEW: 489
2412 Colorful Entrance:
2413 ports:
2414 COLORFUL: 3189
1871 Daedalus Entrance: 2415 Daedalus Entrance:
1872 panels: 2416 panels:
1873 MISSING: 491 2417 MISSING: 491
2418 ports:
2419 DAEDALUS: 3190
1874 East Landscape: 2420 East Landscape:
1875 panels: 2421 panels:
1876 COLOR: 492 2422 COLOR: 492
@@ -1878,6 +2424,8 @@ maps:
1878 Hive Entrance: 2424 Hive Entrance:
1879 panels: 2425 panels:
1880 LOST: 495 2426 LOST: 495
2427 ports:
2428 HIVE: 3191
1881 Jail Part 1: 2429 Jail Part 1:
1882 panels: 2430 panels:
1883 DECATHLON: 506 2431 DECATHLON: 506
@@ -1902,6 +2450,9 @@ maps:
1902 NECROTIZE (2): 511 2450 NECROTIZE (2): 511
1903 PILGRIM: 513 2451 PILGRIM: 513
1904 TORMENTS: 512 2452 TORMENTS: 512
2453 Jubilant Entrance:
2454 ports:
2455 JUBILANT: 3192
1905 Magnet Room: 2456 Magnet Room:
1906 panels: 2457 panels:
1907 AIRPLANE: 516 2458 AIRPLANE: 516
@@ -1938,6 +2489,12 @@ maps:
1938 SMILE: 539 2489 SMILE: 539
1939 WHY: 540 2490 WHY: 540
1940 YOU: 537 2491 YOU: 537
2492 ports:
2493 DIGITAL: 3197
2494 ENTRY: 3193
2495 KEEN: 3194
2496 LINEAR: 3196
2497 ORB: 3195
1941 Maze Center: 2498 Maze Center:
1942 panels: 2499 panels:
1943 CHASE: 549 2500 CHASE: 549
@@ -1995,6 +2552,8 @@ maps:
1995 WEATHER: 568 2552 WEATHER: 568
1996 keyholders: 2553 keyholders:
1997 X: 2770 2554 X: 2770
2555 ports:
2556 INVISIBLE: 3198
1998 Outside Jail: 2557 Outside Jail:
1999 panels: 2558 panels:
2000 GUT: 575 2559 GUT: 575
@@ -2008,6 +2567,9 @@ maps:
2008 FOUR: 581 2567 FOUR: 581
2009 HAVE: 580 2568 HAVE: 580
2010 TEN: 583 2569 TEN: 583
2570 Purple Room:
2571 ports:
2572 DAEDALUS: 3199
2011 Question Room How: 2573 Question Room How:
2012 panels: 2574 panels:
2013 QUESTION: 584 2575 QUESTION: 584
@@ -2020,6 +2582,26 @@ maps:
2020 Question Room Who: 2582 Question Room Who:
2021 panels: 2583 panels:
2022 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
2023 Under Question Room: 2605 Under Question Room:
2024 panels: 2606 panels:
2025 QUESTION: 588 2607 QUESTION: 588
@@ -2032,6 +2614,10 @@ maps:
2032 RIGHT: 591 2614 RIGHT: 591
2033 SAVORY: 592 2615 SAVORY: 592
2034 TEACH: 590 2616 TEACH: 590
2617 ports:
2618 CC: 3203
2619 IMPRESSIVE: 3202
2620 PARTIAL: 3204
2035 Whole Room: 2621 Whole Room:
2036 panels: 2622 panels:
2037 BATHROOM: 604 2623 BATHROOM: 604
@@ -2067,13 +2653,19 @@ maps:
2067 SHIFT: 624 2653 SHIFT: 624
2068 doors: 2654 doors:
2069 Back Area Entrance: 439 2655 Back Area Entrance: 439
2656 Behind Orb Panel: 3336
2657 Behind Question Room Panels: 3332
2070 Between Entrance: 440 2658 Between Entrance: 440
2071 Big Y: 462 2659 Big Y: 462
2660 Broken Shed Panels: 3333
2072 Building Building Gravestone: 468 2661 Building Building Gravestone: 468
2073 Colorful Entrance: 455 2662 Colorful Entrance: 455
2074 Control Center Gray Door: 446 2663 Control Center Gray Door: 446
2664 Control Center Gray Panel: 3326
2075 Control Center Purple Door: 445 2665 Control Center Purple Door: 445
2666 Control Center Purple Panel: 3327
2076 Control Center Red Door: 447 2667 Control Center Red Door: 447
2668 Control Center Red Panel: 3328
2077 Courtyard Entrance: 442 2669 Courtyard Entrance: 442
2078 Courtyard Side Door: 461 2670 Courtyard Side Door: 461
2079 Daedalus Entrance: 448 2671 Daedalus Entrance: 448
@@ -2084,8 +2676,11 @@ maps:
2084 Invisible Entrance: 465 2676 Invisible Entrance: 465
2085 Jail Entrance: 451 2677 Jail Entrance: 451
2086 Magnet Room Entrance: 449 2678 Magnet Room Entrance: 449
2679 Mistreat Panel: 3329
2680 Nature Panels: 3334
2087 Nature Room Door: 466 2681 Nature Room Door: 466
2088 Nature Room Panels: 467 2682 Nature Room Panels: 467
2683 Near Linear Panels: 3324
2089 Near UC Painting Door: 441 2684 Near UC Painting Door: 441
2090 North Landscape Entrance: 456 2685 North Landscape Entrance: 456
2091 Pillar Room Entrance: 450 2686 Pillar Room Entrance: 450
@@ -2094,11 +2689,21 @@ maps:
2094 Savory Painting: 452 2689 Savory Painting: 452
2095 Spiral Painting: 471 2690 Spiral Painting: 471
2096 Talented Entrance: 463 2691 Talented Entrance: 463
2692 Teal Panel: 3335
2097 The Landscapes Gravestone: 458 2693 The Landscapes Gravestone: 458
2098 The Maze Gravestone: 460 2694 The Maze Gravestone: 460
2099 Tower Entrance: 459 2695 Tower Entrance: 459
2696 Tower Panels: 3330
2697 Tree Panels: 3331
2100 West/East Divider: 443 2698 West/East Divider: 443
2699 Why Is It Not Red: 3325
2101 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
2102 the_hive: 2707 the_hive:
2103 rooms: 2708 rooms:
2104 Main Area: 2709 Main Area:
@@ -2143,6 +2748,11 @@ maps:
2143 YELL: 636 2748 YELL: 636
2144 keyholders: 2749 keyholders:
2145 B: 2769 2750 B: 2769
2751 ports:
2752 DAED1: 3207
2753 DAED2: 3208
2754 DAED3: 3209
2755 GREAT: 3210
2146 Mastery Room: 2756 Mastery Room:
2147 masteries: 2757 masteries:
2148 MASTERY: 666 2758 MASTERY: 666
@@ -2161,28 +2771,40 @@ maps:
2161 LEFT: 676 2771 LEFT: 676
2162 RETURN: 674 2772 RETURN: 674
2163 TO: 675 2773 TO: 675
2774 ports:
2775 PLAZA: 3211
2164 Lobby: 2776 Lobby:
2165 panels: 2777 panels:
2166 RIGHT: 677 2778 RIGHT: 677
2779 ports:
2780 GREAT: 3212
2167 Side Area: 2781 Side Area:
2168 panels: 2782 panels:
2169 COLOR: 680 2783 COLOR: 680
2784 ports:
2785 FOURROOMS: 3213
2170 WM Room: 2786 WM Room:
2171 panels: 2787 panels:
2172 LEFT: 682 2788 LEFT: 682
2173 RIGHT: 683 2789 RIGHT: 683
2174 doors: 2790 doors:
2175 Control Center Green Door: 673 2791 Control Center Green Door: 673
2792 Control Center Green Panel: 3338
2176 Front Door: 671 2793 Front Door: 671
2794 Green Eye Panels: 3337
2177 Side Door: 672 2795 Side Door: 672
2178 the_invisible: 2796 the_invisible:
2179 rooms: 2797 rooms:
2180 Entrance: 2798 Entrance:
2181 panels: 2799 panels:
2182 VISIBLE: 685 2800 VISIBLE: 685
2801 ports:
2802 ENTRY: 3214
2183 Maze: 2803 Maze:
2184 masteries: 2804 masteries:
2185 MASTERY: 686 2805 MASTERY: 686
2806 ports:
2807 ENTRY: 3215
2186 doors: 2808 doors:
2187 Maze Entrance: 684 2809 Maze Entrance: 684
2188 the_jubilant: 2810 the_jubilant:
@@ -2201,6 +2823,8 @@ maps:
2201 SPRINT: 695 2823 SPRINT: 695
2202 TREE: 698 2824 TREE: 698
2203 UNFAIR: 694 2825 UNFAIR: 694
2826 ports:
2827 GREAT: 3216
2204 Side Area: 2828 Side Area:
2205 panels: 2829 panels:
2206 CALL: 704 2830 CALL: 704
@@ -2213,6 +2837,7 @@ maps:
2213 J: 2772 2837 J: 2772
2214 doors: 2838 doors:
2215 Side Door: 687 2839 Side Door: 687
2840 Side Room Puzzles: 3339
2216 the_keen: 2841 the_keen:
2217 rooms: 2842 rooms:
2218 Main Area: 2843 Main Area:
@@ -2226,6 +2851,8 @@ maps:
2226 TIN (3): 711 2851 TIN (3): 711
2227 TIN (4): 712 2852 TIN (4): 712
2228 TIN (5): 713 2853 TIN (5): 713
2854 ports:
2855 GREAT: 3217
2229 doors: 2856 doors:
2230 All Panels: 707 2857 All Panels: 707
2231 the_liberated: 2858 the_liberated:
@@ -2240,6 +2867,8 @@ maps:
2240 PERSON: 720 2867 PERSON: 720
2241 SAND: 723 2868 SAND: 723
2242 WOLF: 725 2869 WOLF: 725
2870 ports:
2871 ENTRY: 3218
2243 doors: 2872 doors:
2244 Door: 718 2873 Door: 718
2245 the_linear: 2874 the_linear:
@@ -2254,6 +2883,8 @@ maps:
2254 NOR: 735 2883 NOR: 735
2255 ROT: 731 2884 ROT: 731
2256 TON: 730 2885 TON: 730
2886 ports:
2887 GREAT: 3219
2257 doors: 2888 doors:
2258 Behind The Keen Gravestone: 727 2889 Behind The Keen Gravestone: 727
2259 the_lionized: 2890 the_lionized:
@@ -2268,6 +2899,8 @@ maps:
2268 LION: 741 2899 LION: 741
2269 PIG: 743 2900 PIG: 743
2270 ROCK: 740 2901 ROCK: 740
2902 ports:
2903 ENTRY: 3220
2271 the_literate: 2904 the_literate:
2272 rooms: 2905 rooms:
2273 Puzzle Room: 2906 Puzzle Room:
@@ -2280,6 +2913,8 @@ maps:
2280 SAND: 750 2913 SAND: 750
2281 STICK: 752 2914 STICK: 752
2282 WATER: 746 2915 WATER: 746
2916 ports:
2917 ENTRY: 3221
2283 doors: 2918 doors:
2284 Door: 745 2919 Door: 745
2285 the_lively: 2920 the_lively:
@@ -2294,6 +2929,8 @@ maps:
2294 ROOSTER: 762 2929 ROOSTER: 762
2295 SON: 759 2930 SON: 759
2296 SOPRANO: 757 2931 SOPRANO: 757
2932 ports:
2933 BETWEEN: 3222
2297 the_nuanced: 2934 the_nuanced:
2298 rooms: 2935 rooms:
2299 Back Room: 2936 Back Room:
@@ -2324,12 +2961,18 @@ maps:
2324 TORE: 787 2961 TORE: 787
2325 keyholders: 2962 keyholders:
2326 S: 2767 2963 S: 2767
2964 ports:
2965 UNYIELDING: 3223
2327 doors: 2966 doors:
2328 Left Room Puzzles: 763 2967 Blue Side Puzzles: 763
2968 Green Side Puzzles: 764
2329 Main Room Door: 2750 2969 Main Room Door: 2750
2330 Right Room Puzzles: 764 2970 Stores Panel: 3340
2331 the_orb: 2971 the_orb:
2332 rooms: 2972 rooms:
2973 B Room:
2974 ports:
2975 FINAL: 3224
2333 Main Area: 2976 Main Area:
2334 panels: 2977 panels:
2335 ACT: 799 2978 ACT: 799
@@ -2344,6 +2987,11 @@ maps:
2344 STRIKE: 790 2987 STRIKE: 790
2345 THICK: 797 2988 THICK: 797
2346 THIN: 793 2989 THIN: 793
2990 ports:
2991 GREAT: 3225
2992 Middle Room:
2993 ports:
2994 MID: 3226
2347 the_owl: 2995 the_owl:
2348 rooms: 2996 rooms:
2349 Blue Room: 2997 Blue Room:
@@ -2371,6 +3019,11 @@ maps:
2371 SKETCH: 832 3019 SKETCH: 832
2372 WHITE: 824 3020 WHITE: 824
2373 WING: 826 3021 WING: 826
3022 ports:
3023 FOURROOMS: 3227
3024 Magenta Hallway:
3025 ports:
3026 STURDY: 3228
2374 R1C4 Left: 3027 R1C4 Left:
2375 panels: 3028 panels:
2376 STENCIL: 840 3029 STENCIL: 840
@@ -2383,6 +3036,8 @@ maps:
2383 R2C2 Bottom: 3036 R2C2 Bottom:
2384 panels: 3037 panels:
2385 FOUL: 844 3038 FOUL: 844
3039 ports:
3040 GALLERY: 3229
2386 R2C2 Top: 3041 R2C2 Top:
2387 panels: 3042 panels:
2388 CRUSH: 845 3043 CRUSH: 845
@@ -2400,12 +3055,22 @@ maps:
2400 Blue Owl: 818 3055 Blue Owl: 818
2401 Brush Door: 804 3056 Brush Door: 804
2402 Control Center Magenta Door: 812 3057 Control Center Magenta Door: 812
3058 Control Center Magenta Panel: 3343
2403 First Door: 808 3059 First Door: 808
2404 First Room Shortcut: 807 3060 First Room Shortcut: 807
2405 Gray Bottom Door: 811 3061 Gray Bottom Door: 811
2406 Gray Owl: 814 3062 Gray Owl: 814
2407 Gray Top Door: 810 3063 Gray Top Door: 810
3064 Near Z1 Panel: 3350
2408 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
2409 Sky Bottom Doors: 806 3074 Sky Bottom Doors: 806
2410 Sky Owl: 813 3075 Sky Owl: 813
2411 Sky Top Doors: 805 3076 Sky Top Doors: 805
@@ -2425,16 +3090,23 @@ maps:
2425 CLEOPATRA: 859 3090 CLEOPATRA: 859
2426 NAPOLEON: 860 3091 NAPOLEON: 860
2427 XERXES: 857 3092 XERXES: 857
3093 ports:
3094 ENTRY: 3231
3095 GALLERY: 3230
3096 REVITALIZED: 3232
2428 U Keyholder: 3097 U Keyholder:
2429 keyholders: 3098 keyholders:
2430 U: 2777 3099 U: 2777
2431 doors: 3100 doors:
2432 K2 Door: 852 3101 K2 Door: 852
3102 Lavender Area Puzzles: 3351
2433 the_partial: 3103 the_partial:
2434 rooms: 3104 rooms:
2435 Control Center Entrance: 3105 Control Center Entrance:
2436 panels: 3106 panels:
2437 RETURN: 867 3107 RETURN: 867
3108 ports:
3109 CC: 3233
2438 Obverse Side: 3110 Obverse Side:
2439 panels: 3111 panels:
2440 CUT: 881 3112 CUT: 881
@@ -2453,6 +3125,8 @@ maps:
2453 UP: 870 3125 UP: 870
2454 keyholders: 3126 keyholders:
2455 L: 2771 3127 L: 2771
3128 ports:
3129 GREAT: 3234
2456 Reverse Side: 3130 Reverse Side:
2457 panels: 3131 panels:
2458 BRO: 884 3132 BRO: 884
@@ -2462,8 +3136,14 @@ maps:
2462 doors: 3136 doors:
2463 Control Center Entrance: 865 3137 Control Center Entrance: 865
2464 F Door: 866 3138 F Door: 866
3139 L Entered: 2843
2465 Main Room Puzzles: 863 3140 Main Room Puzzles: 863
2466 P Door: 864 3141 P Door: 864
3142 the_perceptive:
3143 rooms:
3144 Main Area:
3145 ports:
3146 CC: 3235
2467 the_plaza: 3147 the_plaza:
2468 rooms: 3148 rooms:
2469 Bottom Left Room: 3149 Bottom Left Room:
@@ -2497,18 +3177,28 @@ maps:
2497 ASTOUNDING: 919 3177 ASTOUNDING: 919
2498 COURTYARD: 918 3178 COURTYARD: 918
2499 INFLEXIBLE: 920 3179 INFLEXIBLE: 920
3180 ports:
3181 BETWEEN: 3238
3182 IMPRESSIVE: 3237
3183 UNYIELDING: 3236
2500 Mastery: 3184 Mastery:
2501 masteries: 3185 masteries:
2502 MASTERY: 923 3186 MASTERY: 923
2503 Repetitive Entrance: 3187 Repetitive Entrance:
2504 panels: 3188 panels:
2505 TEDIOUS: 924 3189 TEDIOUS: 924
3190 ports:
3191 REPETITIVE: 3239
2506 Sirenic Entrance: 3192 Sirenic Entrance:
2507 panels: 3193 panels:
2508 SIREN: 925 3194 SIREN: 925
3195 ports:
3196 SIRENIC: 3240
2509 Symbolic Entrance: 3197 Symbolic Entrance:
2510 panels: 3198 panels:
2511 FIGURATIVE: 926 3199 FIGURATIVE: 926
3200 ports:
3201 SYMBOLIC: 3241
2512 Top Left Room: 3202 Top Left Room:
2513 panels: 3203 panels:
2514 BACKPACK: 939 3204 BACKPACK: 939
@@ -2557,17 +3247,21 @@ maps:
2557 TYPIST BEAR RIGHT WING: 968 3247 TYPIST BEAR RIGHT WING: 968
2558 WING: 950 3248 WING: 950
2559 doors: 3249 doors:
2560 Bottom Left Door: 894 3250 Near Broken Portal Panel: 3355
2561 Bottom Left Puzzles: 898 3251 Near Repetitive Panel: 3354
2562 Bottom Right Door: 895 3252 Near Sirenic Panel: 3352
2563 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
2564 Repetitive Entrance: 888 3258 Repetitive Entrance: 888
2565 Sirenic Entrance: 890 3259 Sirenic Entrance: 890
3260 Southeast Door: 895
3261 Southeast Puzzles: 899
3262 Southwest Door: 894
3263 Southwest Puzzles: 898
2566 Symbolic Entrance: 889 3264 Symbolic Entrance: 889
2567 Top Left Door: 892
2568 Top Left Puzzles: 896
2569 Top Right Door: 893
2570 Top Right Puzzles: 897
2571 Turtle Entrance: 891 3265 Turtle Entrance: 891
2572 the_quiet: 3266 the_quiet:
2573 rooms: 3267 rooms:
@@ -2588,6 +3282,8 @@ maps:
2588 RODENT: 972 3282 RODENT: 972
2589 RULE: 974 3283 RULE: 974
2590 SOLID: 971 3284 SOLID: 971
3285 ports:
3286 DAEDALUS: 3242
2591 doors: 3287 doors:
2592 Side Door: 970 3288 Side Door: 970
2593 the_relentless: 3289 the_relentless:
@@ -2644,13 +3340,17 @@ maps:
2644 HIDE (2): 1021 3340 HIDE (2): 1021
2645 MORE: 1022 3341 MORE: 1022
2646 doors: 3342 doors:
3343 Left Only Puzzles: 3020
2647 Left/Turn Door: 984 3344 Left/Turn Door: 984
3345 Shop Only Puzzles: 3019
3346 Turn Only Puzzles: 3018
2648 Turn/Shop Door: 985 3347 Turn/Shop Door: 985
2649 the_repetitive: 3348 the_repetitive:
2650 rooms: 3349 rooms:
2651 Anti Room: 3350 Anti Room:
2652 panels: 3351 panels:
2653 EYE: 1041 3352 EYE (1): 1041
3353 EYE (2): 2813
2654 HA (1): 1035 3354 HA (1): 1035
2655 HA (2): 1036 3355 HA (2): 1036
2656 HA (3): 1037 3356 HA (3): 1037
@@ -2688,6 +3388,9 @@ maps:
2688 TO (2): 1056 3388 TO (2): 1056
2689 TUTU (1): 1054 3389 TUTU (1): 1054
2690 TUTU (2): 1068 3390 TUTU (2): 1068
3391 Entry Connector:
3392 ports:
3393 ENTRY: 3243
2691 Lime Room: 3394 Lime Room:
2692 panels: 3395 panels:
2693 BIRD: 1074 3396 BIRD: 1074
@@ -2740,9 +3443,14 @@ maps:
2740 MISHMASH: 1114 3443 MISHMASH: 1114
2741 QUESTION: 1105 3444 QUESTION: 1105
2742 RICHES: 1112 3445 RICHES: 1112
3446 ports:
3447 CC: 3244
2743 Mastery Room: 3448 Mastery Room:
2744 masteries: 3449 masteries:
2745 MASTERY: 1116 3450 MASTERY: 1116
3451 Plaza Connector:
3452 ports:
3453 PLAZA: 3245
2746 Yellow Room: 3454 Yellow Room:
2747 panels: 3455 panels:
2748 3D: 1123 3456 3D: 1123
@@ -2757,12 +3465,16 @@ maps:
2757 W: 1117 3465 W: 1117
2758 ZEROING: 1118 3466 ZEROING: 1118
2759 doors: 3467 doors:
3468 Anti-Collectable: 2812
2760 Anti-Collectable Room: 1025 3469 Anti-Collectable Room: 1025
3470 Anti-Collectable Room Panels: 3358
2761 Black Hallway: 2780 3471 Black Hallway: 2780
2762 Cyan Door: 1028 3472 Cyan Door: 1028
2763 Cyan Puzzles: 1032 3473 Cyan Puzzles: 1032
2764 Dot Area Entrance: 1026 3474 Dot Area Entrance: 1026
2765 Entry Entrance: 1023 3475 Entry Entrance: 1023
3476 H2 Room Puzzles: 3357
3477 Hots Panels: 3356
2766 Lime Door: 1027 3478 Lime Door: 1027
2767 Lime Puzzles: 1031 3479 Lime Puzzles: 1031
2768 Magenta Door: 1029 3480 Magenta Door: 1029
@@ -2774,6 +3486,8 @@ maps:
2774 Bye Room: 3486 Bye Room:
2775 panels: 3487 panels:
2776 BYE: 1129 3488 BYE: 1129
3489 ports:
3490 PARTHENON: 3246
2777 Hidden Room: 3491 Hidden Room:
2778 panels: 3492 panels:
2779 HIDDEN: 1130 3493 HIDDEN: 1130
@@ -2822,9 +3536,12 @@ maps:
2822 TADPOLES: 1159 3536 TADPOLES: 1159
2823 keyholders: 3537 keyholders:
2824 N: 2779 3538 N: 2779
3539 ports:
3540 ENTRY: 3247
2825 doors: 3541 doors:
2826 Books Puzzles: 1136 3542 Books Puzzles: 1136
2827 Games Puzzles: 1137 3543 Games Puzzles: 1137
3544 N Entered: 2971
2828 the_sirenic: 3545 the_sirenic:
2829 rooms: 3546 rooms:
2830 Mastery: 3547 Mastery:
@@ -2851,8 +3568,62 @@ maps:
2851 panels: 3568 panels:
2852 Flipped: 1178 3569 Flipped: 1178
2853 Obverse: 1179 3570 Obverse: 1179
3571 ports:
3572 PLAZA: 3248
2854 doors: 3573 doors:
2855 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
2856 the_stormy: 3627 the_stormy:
2857 rooms: 3628 rooms:
2858 Center: 3629 Center:
@@ -2861,6 +3632,8 @@ maps:
2861 REACTOR: 1180 3632 REACTOR: 1180
2862 VOLCANO: 1181 3633 VOLCANO: 1181
2863 WIND: 1183 3634 WIND: 1183
3635 ports:
3636 ENTRY: 3250
2864 Nuclear Side: 3637 Nuclear Side:
2865 panels: 3638 panels:
2866 GERM: 1184 3639 GERM: 1184
@@ -2893,6 +3666,9 @@ maps:
2893 MOVE (6): 1198 3666 MOVE (6): 1198
2894 MOVE (7): 1199 3667 MOVE (7): 1199
2895 MOVE (8): 1200 3668 MOVE (8): 1200
3669 ports:
3670 COLORFUL: 3252
3671 OWL: 3251
2896 S2 Area: 3672 S2 Area:
2897 panels: 3673 panels:
2898 COLORS: 1201 3674 COLORS: 1201
@@ -2901,6 +3677,8 @@ maps:
2901 Entrance: 3677 Entrance:
2902 panels: 3678 panels:
2903 SUN: 1212 3679 SUN: 1212
3680 ports:
3681 UNKEMPT: 3253
2904 Mastery: 3682 Mastery:
2905 masteries: 3683 masteries:
2906 MASTERY: 1213 3684 MASTERY: 1213
@@ -2947,6 +3725,9 @@ maps:
2947 SQUISH: 1241 3725 SQUISH: 1241
2948 VEGETABLE: 1232 3726 VEGETABLE: 1232
2949 WATER: 1226 3727 WATER: 1226
3728 ports:
3729 EXIT1: 3254
3730 EXIT2: 3255
2950 the_symbolic: 3731 the_symbolic:
2951 rooms: 3732 rooms:
2952 Black Room: 3733 Black Room:
@@ -3128,6 +3909,8 @@ maps:
3128 White Room: 3909 White Room:
3129 panels: 3910 panels:
3130 WRITE: 2425 3911 WRITE: 2425
3912 ports:
3913 PLAZA: 3256
3131 Yellow Room: 3914 Yellow Room:
3132 panels: 3915 panels:
3133 WHOLE: 2426 3916 WHOLE: 2426
@@ -3171,9 +3954,12 @@ maps:
3171 WIFE (Brown): 2447 3954 WIFE (Brown): 2447
3172 keyholders: 3955 keyholders:
3173 Y: 2764 3956 Y: 2764
3957 ports:
3958 GREAT: 3257
3174 doors: 3959 doors:
3175 Black Side Panels: 2427 3960 Black Side Panels: 2427
3176 Brown Side Panels: 2428 3961 Brown Side Panels: 2428
3962 Keyholder Hint Panel: 3360
3177 Main Room Door: 2429 3963 Main Room Door: 2429
3178 the_tenacious: 3964 the_tenacious:
3179 rooms: 3965 rooms:
@@ -3183,6 +3969,8 @@ maps:
3183 Control Center Entrance: 3969 Control Center Entrance:
3184 panels: 3970 panels:
3185 ZERO: 2455 3971 ZERO: 2455
3972 ports:
3973 CC: 3258
3186 Main Area: 3974 Main Area:
3187 keyholders: 3975 keyholders:
3188 K: 2768 3976 K: 2768
@@ -3199,6 +3987,7 @@ maps:
3199 panels: 3987 panels:
3200 WISDOM: 2459 3988 WISDOM: 2459
3201 doors: 3989 doors:
3990 K Entered: 2844
3202 Paintings Door: 2453 3991 Paintings Door: 2453
3203 the_three_doors: 3992 the_three_doors:
3204 rooms: 3993 rooms:
@@ -3208,26 +3997,39 @@ maps:
3208 DOOR: 2461 3997 DOOR: 2461
3209 END: 2464 3998 END: 2464
3210 WAYS: 2462 3999 WAYS: 2462
4000 ports:
4001 BEGIN: 3259
4002 BEGIN2: 3260
3211 First Second Room: 4003 First Second Room:
3212 panels: 4004 panels:
3213 FIRS: 2465 4005 FIRS: 2465
3214 INITIAL: 2466 4006 INITIAL: 2466
3215 MINUTE (1): 2467 4007 MINUTE (1): 2467
3216 MINUTE (2): 2468 4008 MINUTE (2): 2468
4009 ports:
4010 GREAT: 3261
4011 TTD: 3262
3217 Loose Strings Room: 4012 Loose Strings Room:
3218 panels: 4013 panels:
3219 LOOSE: 2469 4014 LOOSE: 2469
3220 STRINGS: 2470 4015 STRINGS: 2470
4016 ports:
4017 BEGIN: 3263
3221 One Luck Room: 4018 One Luck Room:
3222 panels: 4019 panels:
3223 CHANCE: 2472 4020 CHANCE: 2472
3224 LONE: 2471 4021 LONE: 2471
4022 ports:
4023 BEGIN: 3264
3225 Silver Portal Room: 4024 Silver Portal Room:
3226 panels: 4025 panels:
3227 GOLD: 2473 4026 GOLD: 2473
3228 Left: 2475 4027 Left: 2475
3229 PORT: 2474 4028 PORT: 2474
3230 Right: 2476 4029 Right: 2476
4030 ports:
4031 BEGIN: 3265
4032 NEXT: 3266
3231 doors: 4033 doors:
3232 The Three Doors Gravestone: 2460 4034 The Three Doors Gravestone: 2460
3233 the_tower: 4035 the_tower:
@@ -3246,6 +4048,8 @@ maps:
3246 PROD: 2485 4048 PROD: 2485
3247 RIDE: 2484 4049 RIDE: 2484
3248 WARM: 2486 4050 WARM: 2486
4051 ports:
4052 GREAT: 3267
3249 Tower: 4053 Tower:
3250 panels: 4054 panels:
3251 ANNOY (1): 2508 4055 ANNOY (1): 2508
@@ -3309,6 +4113,9 @@ maps:
3309 Third Floor Puzzles: 2480 4113 Third Floor Puzzles: 2480
3310 the_tree: 4114 the_tree:
3311 rooms: 4115 rooms:
4116 Bearer Entrance:
4117 ports:
4118 BEARER: 3268
3312 Main Area: 4119 Main Area:
3313 panels: 4120 panels:
3314 COLOR: 2550 4121 COLOR: 2550
@@ -3341,6 +4148,11 @@ maps:
3341 WADE: 2562 4148 WADE: 2562
3342 WALK (1): 2555 4149 WALK (1): 2555
3343 WALK (2): 2556 4150 WALK (2): 2556
4151 ports:
4152 DAEDALUS: 3272
4153 DIGITAL: 3270
4154 GREAT: 3271
4155 UNKEMPT: 3269
3344 doors: 4156 doors:
3345 Control Center Brown Door: 2548 4157 Control Center Brown Door: 2548
3346 The Tree Gravestone: 2549 4158 The Tree Gravestone: 2549
@@ -3349,6 +4161,11 @@ maps:
3349 Control Center Entrance: 4161 Control Center Entrance:
3350 panels: 4162 panels:
3351 RETURN: 2587 4163 RETURN: 2587
4164 ports:
4165 CC: 3273
4166 Daedalus Entrance:
4167 ports:
4168 DAEDALUS: 3274
3352 Exit Room 2: 4169 Exit Room 2:
3353 panels: 4170 panels:
3354 DOOR: 2590 4171 DOOR: 2590
@@ -3392,6 +4209,10 @@ maps:
3392 ZOO: 2615 4209 ZOO: 2615
3393 keyholders: 4210 keyholders:
3394 I: 2775 4211 I: 2775
4212 ports:
4213 GREAT: 3275
4214 SUNTEMPLE: 3277
4215 TREE: 3276
3395 Middle Room: 4216 Middle Room:
3396 panels: 4217 panels:
3397 FELLOW: 2624 4218 FELLOW: 2624
@@ -3448,13 +4269,19 @@ maps:
3448 doors: 4269 doors:
3449 Cog Rhino Hug Rug: 2586 4270 Cog Rhino Hug Rug: 2586
3450 Control Center Orange Door: 2582 4271 Control Center Orange Door: 2582
4272 Control Center Orange Panel: 3362
4273 East Door: 2580
3451 Honor Our Hint: 2585 4274 Honor Our Hint: 2585
4275 I Entered: 2845
3452 Let Untrue Tie: 2583 4276 Let Untrue Tie: 2583
3453 Right Door: 2580 4277 Near Teal Door Panels: 3361
3454 Routine Out Chute: 2584 4278 Routine Out Chute: 2584
3455 W2 Room Door: 2581 4279 W2 Room Door: 2581
3456 the_unyielding: 4280 the_unyielding:
3457 rooms: 4281 rooms:
4282 Bearer Entrance:
4283 ports:
4284 BEARER: 3278
3458 Behind Northeast: 4285 Behind Northeast:
3459 panels: 4286 panels:
3460 FIND: 1260 4287 FIND: 1260
@@ -3542,6 +4369,8 @@ maps:
3542 Digital Entrance: 4369 Digital Entrance:
3543 panels: 4370 panels:
3544 ORANGE: 1326 4371 ORANGE: 1326
4372 ports:
4373 DIGITAL: 3279
3545 Directions Room: 4374 Directions Room:
3546 panels: 4375 panels:
3547 ABS: 1327 4376 ABS: 1327
@@ -3623,6 +4452,9 @@ maps:
3623 RAT: 1380 4452 RAT: 1380
3624 SEE: 1377 4453 SEE: 1377
3625 TIC: 1379 4454 TIC: 1379
4455 Nuanced Entrance:
4456 ports:
4457 NUANCED: 3280
3626 Orange Alcove: 4458 Orange Alcove:
3627 panels: 4459 panels:
3628 ON: 1386 4460 ON: 1386
@@ -3630,6 +4462,8 @@ maps:
3630 panels: 4462 panels:
3631 GEE: 1387 4463 GEE: 1387
3632 SEA: 1388 4464 SEA: 1388
4465 ports:
4466 PLAZA: 3281
3633 Red Eyes: 4467 Red Eyes:
3634 panels: 4468 panels:
3635 RED EYES: 1389 4469 RED EYES: 1389
@@ -3690,7 +4524,9 @@ maps:
3690 HEALTH: 1428 4524 HEALTH: 1428
3691 doors: 4525 doors:
3692 Bearer Entrance: 1259 4526 Bearer Entrance: 1259
4527 Blue D Room Puzzles: 3363
3693 Brown Alcove: 1255 4528 Brown Alcove: 1255
4529 Color Hallway Panels: 3364
3694 Digital Entrance: 1257 4530 Digital Entrance: 1257
3695 East Room 1: 2740 4531 East Room 1: 2740
3696 East Room 1 Entrance: 1251 4532 East Room 1 Entrance: 1251
@@ -3740,6 +4576,8 @@ maps:
3740 Entry: 4576 Entry:
3741 panels: 4577 panels:
3742 WONDER: 2690 4578 WONDER: 2690
4579 ports:
4580 DAEDALUS: 3282
3743 Huge: 4581 Huge:
3744 panels: 4582 panels:
3745 BARK: 2695 4583 BARK: 2695
@@ -3767,6 +4605,8 @@ maps:
3767 METAL: 2706 4605 METAL: 2706
3768 SPICE: 2708 4606 SPICE: 2708
3769 TREE: 2705 4607 TREE: 2705
4608 ports:
4609 ENTRY: 3283
3770letters: 4610letters:
3771 a1: 596 4611 a1: 596
3772 a2: 6 4612 a2: 6
@@ -3836,7 +4676,54 @@ endings:
3836 YELLOW: 1206 4676 YELLOW: 1206
3837special: 4677special:
3838 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
3839progressives: 4725progressives:
4726 Icarus Quick Travel: 2933
3840 Progressive Gold Ending: 2753 4727 Progressive Gold Ending: 2753
3841door_groups: 4728door_groups:
3842 Control Center Blue Doors: 2788 4729 Control Center Blue Doors: 2788
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 {
15doors { 15doors {
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 {
84doors { 86doors {
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}
103doors { 91doors {
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}
114doors { 103doors {
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}
122doors { 115doors {
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}
127doors { 121doors {
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}
168doors {
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}
175doors {
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 @@
1name: "Ancient Entrance" 1name: "Ancient Entrance"
2ports { 2ports {
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}
9ports { 9ports {
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}
9ports { 9ports {
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}
9ports { 9ports {
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}
50ports { 50ports {
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}
54ports { 57ports {
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}
59ports { 64ports {
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}
63ports { 70ports {
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}
67ports { 76ports {
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}
9ports { 9ports {
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}
17ports { 17ports {
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 @@
1name: "Repetitive Entrance" 1name: "Repetitive Entrance"
2ports { 2ports {
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}
9ports { 9ports {
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}
9ports { 9ports {
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}
102connections { 102connections {
103 from_room: "Outside House"
104 to_room: "Blue Hallway Tall Side"
105 door { name: "House Side Door" }
106}
107connections {
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 {
222connections { 227connections {
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}
227connections { 232connections {
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}
232connections { 237connections {
233 from_room: "Z2 Room" 238 from_room: "Z2 Room"
@@ -378,7 +383,7 @@ connections {
378connections { 383connections {
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}
383connections { 388connections {
384 from_room: "O2 Room" 389 from_room: "O2 Room"
@@ -408,7 +413,7 @@ connections {
408connections { 413connections {
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}
413connections { 418connections {
414 from_room: "O2 Room" 419 from_room: "O2 Room"
@@ -423,7 +428,7 @@ connections {
423connections { 428connections {
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}
428connections { 433connections {
429 from_room: "Globe Room" 434 from_room: "Globe Room"
@@ -438,17 +443,17 @@ connections {
438connections { 443connections {
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}
443connections { 448connections {
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}
448connections { 453connections {
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 {
461connections { 466connections {
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}
466connections { 471connections {
467 from_room: "Maze Paintings Area" 472 from_room: "Maze Paintings Area"
@@ -476,17 +481,17 @@ connections {
476connections { 481connections {
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}
481connections { 486connections {
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}
486connections { 491connections {
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}
491connections { 496connections {
492 from_room: "Outside Magic Room" 497 from_room: "Outside Magic Room"
@@ -511,7 +516,7 @@ connections {
511connections { 516connections {
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}
516connections { 521connections {
517 from_room: "Outside Pyramid" 522 from_room: "Outside Pyramid"
@@ -601,7 +606,7 @@ connections {
601connections { 606connections {
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}
606connections { 611connections {
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}
1865connections {
1866 from_room: "Roof"
1867 to_room: "F Keyholder"
1868 oneway: true
1869 roof_access: true
1870}
1871connections {
1872 from_room: "Roof"
1873 to_room: "Yellow Color Backside"
1874 oneway: true
1875 roof_access: true
1876}
diff --git a/data/maps/daedalus/doors.txtpb b/data/maps/daedalus/doors.txtpb index ccbf3f0..ed7516f 100644 --- a/data/maps/daedalus/doors.txtpb +++ b/data/maps/daedalus/doors.txtpb
@@ -188,17 +188,20 @@ doors {
188} 188}
189doors { 189doors {
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}
196doors { 198doors {
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.
204doors { 207doors {
@@ -224,12 +227,21 @@ doors {
224} 227}
225doors { 228doors {
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}
232doors { 236doors {
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}
244doors {
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}
300doors { 312doors {
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}
310doors { 323doors {
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}
319doors { 332doors {
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}
357doors {
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}
364doors {
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}
374doors {
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}
498doors { 552doors {
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}
718doors { 772doors {
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}
821doors { 875doors {
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}
883doors {
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}
828doors { 893doors {
@@ -857,7 +922,7 @@ doors {
857 location_name: "South Rooms" 922 location_name: "South Rooms"
858} 923}
859doors { 924doors {
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 {
866doors { 931doors {
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}
886doors { 952doors {
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 {
893doors { 960doors {
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}
899doors { 967doors {
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}
905doors { 974doors {
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}
912doors { 981doors {
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}
919doors { 988doors {
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}
940doors { 1009doors {
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}
961doors { 1030doors {
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}
968doors { 1037doors {
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}
975doors { 1044doors {
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}
1019doors { 1088doors {
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}
1276doors { 1312doors {
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}
1309doors { 1345doors {
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}
1617doors { 1657doors {
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}
1652doors { 1693doors {
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}
1683doors { 1725doors {
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}
1691doors { 1734doors {
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}
1698doors { 1742doors {
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}
1722doors { 1767doors {
1723 name: "Pink Hallway" 1768 name: "Pink Hallway"
@@ -1792,12 +1837,37 @@ doors {
1792} 1837}
1793doors { 1838doors {
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}
1800doors { 1846doors {
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}
1855doors {
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}
1863doors {
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}
1870doors {
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}
1957doors { 2027doors {
1958 name: "South Castle Area Back Door" 2028 name: "South Castle Area Back Door"
@@ -2187,6 +2257,7 @@ doors {
2187 receivers: "Components/Doors/Unincorporated/temple_foyer_6" 2257 receivers: "Components/Doors/Unincorporated/temple_foyer_6"
2188 panels { room: "Globe Room" name: "WORD" } 2258 panels { room: "Globe Room" name: "WORD" }
2189 location_room: "Globe Room" 2259 location_room: "Globe Room"
2260 location_name: "Sticks and Stones"
2190} 2261}
2191doors { 2262doors {
2192 name: "Castle Numbers Puzzle" 2263 name: "Castle Numbers Puzzle"
@@ -2306,3 +2377,95 @@ doors {
2306 receivers: "Components/Paintings/Temple of the Eyes/eyeRedStart/teleportListener" 2377 receivers: "Components/Paintings/Temple of the Eyes/eyeRedStart/teleportListener"
2307 double_letters: true 2378 double_letters: true
2308} 2379}
2380doors {
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}
2391doors {
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}
2401doors {
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}
2408doors {
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}
2418doors {
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}
2425doors {
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}
2433doors {
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}
2441doors {
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}
2448doors {
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}
2455doors {
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}
2463doors {
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 @@
1name: "C Keyholder" 1name: "C Keyholder"
2panel_display_name: "North Area" 2panel_display_name: "East Area"
3keyholders { 3keyholders {
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}
195ports { 195ports {
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}
11ports { 11ports {
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"
2panel_display_name: "Hedges" 2panel_display_name: "Hedges"
3ports { 3ports {
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 @@
1name: "Hotel" 1name: "Hotel"
2panel_display_name: "Southwest Area" 2panel_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.
5panels { 3panels {
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}
7ports { 7ports {
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}
77ports { 77ports {
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"
2panel_display_name: "South Area" 2panel_display_name: "South Area"
3ports { 3ports {
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}
17ports { 17ports {
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}
11ports { 11ports {
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 @@
1name: "Red Color Door" 1name: "Red Color Door"
2panel_display_name: "Southwest Area" 2panel_display_name: "Southwest Area"
3panels { 3panels {
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}
10ports { 10ports {
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}
52ports { 52ports {
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}
56ports { 59ports {
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}
49ports { 49ports {
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"
2panel_display_name: "O2 Room" 2panel_display_name: "O2 Room"
3ports { 3ports {
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 @@
1name: "White Hallway From Entry" 1name: "White Hallway From Entry"
2panel_display_name: "Southwest Area" 2panel_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.
9ports { 3ports {
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 @@
1name: "Wonderland" 1name: "Wonderland"
2panel_display_name: "Northwest Area" 2panel_display_name: "Northwest Area"
3# TODO: There's a warp from The Entry into here.
4panels { 3panels {
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}
39ports { 38ports {
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}
31ports { 31ports {
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 @@
1connections {
2 from_room: "Main Area"
3 to_room: "Center Building"
4 door { name: "Center Building" }
5}
6connections {
7 from_room: "Main Area"
8 to_room: "Flower Hallway"
9 door { name: "Flower Hallway" }
10}
11connections {
12 from_room: "Main Area"
13 to_room: "Tower"
14 door { name: "Tower Entrance" }
15}
16connections {
17 from_room: "Main Area"
18 to_room: "Castle"
19 door { name: "Castle" }
20}
21connections {
22 from_room: "Main Area"
23 to_room: "Backside Area"
24 door { name: "Backside Entrance" }
25}
26connections {
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 @@
1doors {
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}
8doors {
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}
15doors {
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}
25doors {
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}
35doors {
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}
45doors {
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}
63doors {
64 name: "Red Door"
65 type: LOCATION_ONLY
66 panels { room: "Castle" name: "SERIES" }
67 location_room: "Castle"
68 location_name: "SERIES"
69}
70doors {
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}
77doors {
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}
91doors {
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}
100doors {
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}
149doors {
150 name: "Backside Entrance"
151 type: EVENT
152 panels { room: "Tower" name: "ENDS (1)" }
153}
154doors {
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 @@
1display_name: "Demo"
2type: DEMO
3# This painting is above a panel and can't be entered.
4excluded_nodes: "Meshes/owl"
5# The map's mastery is created at runtime.
6custom_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 @@
1name: "Backside Area"
2panels {
3 name: "BACKSIDE"
4 path: "Panels/Endings/demo_41"
5 clue: "backside"
6 answer: "back"
7}
8panels {
9 name: "DOORWAYS"
10 path: "Panels/Endings/demo_42"
11 clue: "doorways"
12 answer: "doors"
13}
14panels {
15 name: "SEE"
16 path: "Panels/Endings/demo_43"
17 clue: "see"
18 answer: "secret"
19}
20panels {
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 @@
1name: "Castle"
2panels {
3 name: "SERIES"
4 path: "Panels/Red/demo_26"
5 clue: "series"
6 answer: "mysteries"
7}
8panels {
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 @@
1name: "Center Building"
2panels {
3 name: "WORLD"
4 path: "Panels/Room 1/demo_3"
5 clue: "world"
6 answer: "word"
7}
8panels {
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 @@
1name: "Flower Hallway"
2panels {
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 @@
1name: "Main Area"
2panels {
3 name: "HI"
4 path: "Panels/Entry/demo_1"
5 clue: "hi"
6 answer: "hi"
7}
8panels {
9 name: "ART"
10 path: "Panels/Entry/demo_49"
11 clue: "\"art\""
12 answer: "art"
13}
14panels {
15 name: "TEES"
16 path: "Panels/Entry/demo_50"
17 clue: "tees"
18 answer: "trees"
19}
20panels {
21 name: "COLORFUL"
22 path: "Panels/Room 1/demo_5"
23 clue: "colorful"
24 answer: "colorful"
25}
26panels {
27 name: "WORD"
28 path: "Panels/Room 1/demo_6"
29 clue: "word"
30 answer: "world"
31}
32panels {
33 name: "AGES"
34 path: "Panels/Orange/demo_7"
35 clue: "ages"
36 answer: "messages"
37}
38panels {
39 name: "DEN"
40 path: "Panels/Orange/demo_8"
41 clue: "den"
42 answer: "hidden"
43}
44panels {
45 name: "HID"
46 path: "Panels/Orange/demo_9"
47 clue: "hid"
48 answer: "hidden"
49}
50panels {
51 name: "MESS"
52 path: "Panels/Orange/demo_10"
53 clue: "mess"
54 answer: "messages"
55}
56panels {
57 name: "CLOCKWISE"
58 path: "Panels/Purple/demo_2"
59 clue: "clockwise"
60 answer: "counter"
61}
62panels {
63 name: "POSSIBLE"
64 path: "Panels/Purple/demo_12"
65 clue: "possible"
66 answer: "impossible"
67}
68panels {
69 name: "PACES"
70 path: "Panels/Purple/demo_13"
71 clue: "paces"
72 answer: "spaces"
73}
74panels {
75 name: "COUNTER"
76 path: "Panels/Purple/demo_30"
77 clue: "counter"
78 answer: "clockwise"
79}
80panels {
81 name: "ANY"
82 path: "Panels/Yellow/demo_14"
83 clue: "any"
84 answer: "many"
85}
86panels {
87 name: "RODS"
88 path: "Panels/Yellow/demo_15"
89 clue: "rods"
90 answer: "roads"
91}
92panels {
93 name: "TWO"
94 path: "Panels/Yellow/demo_16"
95 clue: "two"
96 answer: "to"
97}
98panels {
99 name: "TALK"
100 path: "Panels/Yellow/demo_17"
101 clue: "talk"
102 answer: "walk"
103}
104panels {
105 name: "SECRETIVE"
106 path: "Panels/Yellow/demo_18"
107 clue: "secretive"
108 answer: "secret"
109}
110panels {
111 name: "TOADS"
112 path: "Panels/Yellow/demo_19"
113 clue: "toads"
114 answer: "roads"
115}
116panels {
117 name: "TON"
118 path: "Panels/Yellow/demo_20"
119 clue: "ton"
120 answer: "to"
121}
122panels {
123 name: "MIND"
124 path: "Panels/Yellow/demo_21"
125 clue: "mind"
126 answer: "find"
127}
128panels {
129 name: "END"
130 path: "Panels/Yellow/demo_22"
131 clue: "end"
132 answer: "endless"
133}
134panels {
135 name: "RAD"
136 path: "Panels/Yellow/demo_23"
137 clue: "rad"
138 answer: "roads"
139}
140panels {
141 name: "TOO"
142 path: "Panels/Yellow/demo_24"
143 clue: "too"
144 answer: "to"
145}
146panels {
147 name: "STALK"
148 path: "Panels/Yellow/demo_25"
149 clue: "stalk"
150 answer: "walk"
151}
152panels {
153 name: "HAZES"
154 path: "Panels/Green/demo_27"
155 clue: "hazes"
156 answer: "mazes"
157}
158panels {
159 name: "DAZES"
160 path: "Panels/Green/demo_28"
161 clue: "dazes"
162 answer: "mazes"
163}
164panels {
165 name: "GAZES"
166 path: "Panels/Green/demo_29"
167 clue: "gazes"
168 answer: "mazes"
169}
170panels {
171 name: "CASTS"
172 path: "Panels/Green/demo_30"
173 clue: "casts"
174 answer: "castles"
175}
176panels {
177 name: "S"
178 path: "Panels/Blue/demo_31"
179 clue: "s"
180 answer: "s"
181}
182panels {
183 name: "C"
184 path: "Panels/Blue/demo_32"
185 clue: "c"
186 answer: "c"
187}
188panels {
189 name: "A"
190 path: "Panels/Blue/demo_33"
191 clue: "a"
192 answer: "a"
193}
194panels {
195 name: "V"
196 path: "Panels/Blue/demo_34"
197 clue: "v"
198 answer: "v"
199}
200panels {
201 name: "E (1)"
202 path: "Panels/Blue/demo_35"
203 clue: "e"
204 answer: "e"
205}
206panels {
207 name: "N"
208 path: "Panels/Blue/demo_36"
209 clue: "n"
210 answer: "n"
211}
212panels {
213 name: "E (2)"
214 path: "Panels/Blue/demo_38"
215 clue: "e"
216 answer: "e"
217}
218panels {
219 name: "R"
220 path: "Panels/Blue/demo_39"
221 clue: "r"
222 answer: "r"
223}
224panels {
225 name: "Blank"
226 path: "Panels/Blue/demo_40"
227 clue: ""
228 answer: "hunts"
229}
230panels {
231 name: "DISCOVER"
232 path: "Panels/Gold/demo_45"
233 clue: "discover"
234 answer: "rediscover"
235}
236panels {
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 @@
1name: "Mastery"
2masteries {
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 @@
1name: "Tower"
2panels {
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}
58ports { 58ports {
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}
58ports { 58ports {
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}
58ports { 58ports {
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}
58ports { 58ports {
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.
4connections {
5 from_room: "Welcome Spine (Obverse)"
6 to_room: "Trans Rights"
7 door { name: "Agender Door" }
8}
9connections {
10 from_room: "Through Woman (Obverse)"
11 to_room: "Welcome Spine (Reverse)"
12 door { name: "Agender Door" }
13 oneway: true
14}
15connections {
16 from_room: "Above Trans Rights"
17 to_room: "Through Woman (Reverse)"
18 door { name: "Agender Door" }
19 oneway: true
20}
21connections {
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}
36connections {
37 from_room: "Spiral Ramp"
38 to_room: "Highest Point"
39 door { name: "Pea Door" }
40}
41connections {
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}
56connections {
57 from_room: "Maze"
58 to_room: "Mediums Quicktravel"
59 door { name: "Mediums Door" }
60}
61connections {
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}
76connections {
77 from_room: "Pillar Ramp"
78 to_room: "Banana Belt Door"
79 door { name: "Asteroid Bunch Door" }
80}
81connections {
82 from_room: "Through Woman (Obverse)"
83 to_room: "Cow Quicktravel"
84 door { name: "Cow Door" }
85}
86connections {
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}
101connections {
102 from_room: "Through Woman (Obverse)"
103 to_room: "Behind Welcome Spine"
104 door { name: "Ant Door" }
105}
106connections {
107 from_room: "Above Trans Rights"
108 to_room: "Behind Welcome Spine"
109 door { name: "Ant Door" }
110 oneway: true
111}
112connections {
113 from_room: "Through Woman (Obverse)"
114 to_room: "Big U"
115 door { name: "Man Door" }
116}
117connections {
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}
132connections {
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}
147connections {
148 from_room: "Welcome Spine (Obverse)"
149 to_room: "Through Woman (Obverse)"
150 door { name: "Woman Door" }
151}
152connections {
153 from_room: "Fatherland"
154 to_room: "Fatherland Quicktravel"
155 door { name: "Fatherland Door" }
156}
157connections {
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}
172connections {
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}
187connections {
188 from_room: "Mini Icarus Wings Painting"
189 to_room: "Mini Icarus 2"
190 door { name: "Battery Door" }
191 oneway: true
192}
193connections {
194 from_room: "Mini Icarus 2"
195 to_room: "Mini Icarus Sun Loop"
196 door { name: "Battery Door" }
197}
198connections {
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}
213connections {
214 from_room: "Mini Icarus 2"
215 to_room: "Mini Icarus 3"
216 door { name: "Animals Door" }
217}
218connections {
219 from_room: "Mini Icarus 2"
220 to_room: "Mini Icarus Wings Painting"
221 door { name: "Troupe Door" }
222}
223connections {
224 from_room: "Mini Icarus 2"
225 to_room: "Mini Icarus Sun Loop"
226 door { name: "Reversed Arrows Door" }
227}
228connections {
229 from_room: "Welcome Spine (Reverse)"
230 to_room: "Welcome Spine Quicktravel"
231 door { name: "Termite Door" }
232}
233connections {
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}
248connections {
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}
263connections {
264 from_room: "Pillar Ramp"
265 to_room: "Patricide Room"
266 door { name: "Patricide Door" }
267}
268connections {
269 from_room: "Maze"
270 to_room: "Maze Back"
271 door { name: "These Door" }
272}
273connections {
274 from_room: "Welcome Spine (Reverse)"
275 to_room: "Maze Back"
276 oneway: true
277}
278connections {
279 from_room: "Welcome Spine (Obverse)"
280 to_room: "Through Woman (Reverse)"
281 oneway: true
282}
283connections {
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}
298connections {
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}
313connections {
314 from_room: "Through Woman (Obverse)"
315 to_room: "Behind Welcome Spine"
316 door { name: "Woman Door" }
317 oneway: true
318}
319connections {
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}
334connections {
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}
349connections {
350 from_room: "Through Woman (Reverse)"
351 to_room: "Big U"
352 oneway: true
353}
354connections {
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}
370connections {
371 from_room: "Spiral Ramp"
372 to_room: "Pillar Ramp"
373 oneway: true
374}
375connections {
376 from_room: "Pillar Ramp"
377 to_room: "The Orb"
378}
379connections {
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}
394connections {
395 from_room: "Trans Rights"
396 to_room: "Mini Icarus Wings Painting"
397 oneway: true
398}
399connections {
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}
414connections {
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}
429connections {
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}
444connections {
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}
459connections {
460 from_room: "Mini Icarus 3"
461 to_room: "Mini Icarus Sun Loop"
462 door { name: "Battery Door" }
463 oneway: true
464}
465connections {
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}
480connections {
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}
495connections {
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}
510connections {
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}
525connections {
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}
540connections {
541 from_room: "Maze"
542 to_room: "Maze King Panel"
543 oneway: true
544}
545connections {
546 from_room: "Maze King Painting"
547 to_room: "Maze King Panel"
548 oneway: true
549}
550connections {
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}
565connections {
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}
580connections {
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}
595connections {
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}
610connections {
611 from_room: "Trans Rights"
612 to_room: "Trans Rights Panels"
613 oneway: true
614}
615connections {
616 from_room: "Above Trans Rights"
617 to_room: "Trans Rights Panels"
618 oneway: true
619}
620connections {
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.
627connections {
628 from_room: "Above Trans Rights"
629 to_room: "Welcome Spine (Obverse)"
630 oneway: true
631}
632connections {
633 from_room: "Banana Belt Door"
634 to_room: "Welcome Spine (Obverse)"
635 oneway: true
636}
637connections {
638 from_room: "Behind Welcome Spine"
639 to_room: "Welcome Spine (Obverse)"
640 oneway: true
641}
642connections {
643 from_room: "Big U"
644 to_room: "Welcome Spine (Obverse)"
645 oneway: true
646}
647connections {
648 from_room: "Cow Quicktravel"
649 to_room: "Welcome Spine (Obverse)"
650 oneway: true
651}
652connections {
653 from_room: "Fatherland Quicktravel"
654 to_room: "Welcome Spine (Obverse)"
655 oneway: true
656}
657connections {
658 from_room: "Fatherland"
659 to_room: "Welcome Spine (Obverse)"
660 oneway: true
661}
662connections {
663 from_room: "Highest Point"
664 to_room: "Welcome Spine (Obverse)"
665 oneway: true
666}
667connections {
668 from_room: "Maze Back"
669 to_room: "Welcome Spine (Obverse)"
670 oneway: true
671}
672connections {
673 from_room: "Maze King Painting"
674 to_room: "Welcome Spine (Obverse)"
675 oneway: true
676}
677connections {
678 from_room: "Maze Wings Passage"
679 to_room: "Welcome Spine (Obverse)"
680 oneway: true
681}
682connections {
683 from_room: "Maze"
684 to_room: "Welcome Spine (Obverse)"
685 oneway: true
686}
687connections {
688 from_room: "Mediums Quicktravel"
689 to_room: "Welcome Spine (Obverse)"
690 oneway: true
691}
692connections {
693 from_room: "Mini Icarus 2"
694 to_room: "Welcome Spine (Obverse)"
695 oneway: true
696}
697connections {
698 from_room: "Mini Icarus 3"
699 to_room: "Welcome Spine (Obverse)"
700 oneway: true
701}
702connections {
703 from_room: "Mini Icarus Sun Loop"
704 to_room: "Welcome Spine (Obverse)"
705 oneway: true
706}
707connections {
708 from_room: "Mini Icarus Wings Painting"
709 to_room: "Welcome Spine (Obverse)"
710 oneway: true
711}
712connections {
713 from_room: "Painting Maze 1"
714 to_room: "Welcome Spine (Obverse)"
715 oneway: true
716}
717connections {
718 from_room: "Painting Maze 2"
719 to_room: "Welcome Spine (Obverse)"
720 oneway: true
721}
722connections {
723 from_room: "Patricide Room"
724 to_room: "Welcome Spine (Obverse)"
725 oneway: true
726}
727connections {
728 from_room: "Pillar Ramp"
729 to_room: "Welcome Spine (Obverse)"
730 oneway: true
731}
732connections {
733 from_room: "Spiral Ramp"
734 to_room: "Welcome Spine (Obverse)"
735 oneway: true
736}
737connections {
738 from_room: "The Orb"
739 to_room: "Welcome Spine (Obverse)"
740 oneway: true
741}
742connections {
743 from_room: "Through Woman (Obverse)"
744 to_room: "Welcome Spine (Obverse)"
745 oneway: true
746}
747connections {
748 from_room: "Through Woman (Reverse)"
749 to_room: "Welcome Spine (Obverse)"
750 oneway: true
751}
752connections {
753 from_room: "Trans Rights"
754 to_room: "Welcome Spine (Obverse)"
755 oneway: true
756}
757connections {
758 from_room: "Welcome Spine (Reverse)"
759 to_room: "Welcome Spine (Obverse)"
760 oneway: true
761}
762connections {
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 @@
1doors {
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}
8doors {
9 name: "Quick Travel 3"
10 type: ITEM_ONLY
11 receivers: "Components/Paintings/QuickTravel/go3"
12 panels { room: "Trans Rights Panels" name: "AGENDER" }
13}
14doors {
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}
21doors {
22 name: "Quick Travel 7"
23 type: ITEM_ONLY
24 receivers: "Components/Paintings/QuickTravel/go7"
25 panels { room: "Pillar Ramp" name: "PEA (1)" }
26}
27doors {
28 name: "Mediums Door"
29 type: STANDARD
30 receivers: "Components/Doors/quicktravel12"
31 panels { room: "Maze" name: "MEDIUMS" }
32 location_room: "Maze"
33}
34doors {
35 name: "Quick Travel 1"
36 type: ITEM_ONLY
37 receivers: "Components/Paintings/QuickTravel/go1"
38 panels { room: "Maze" name: "MEDIUMS" }
39}
40doors {
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}
48doors {
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}
55doors {
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}
62doors {
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}
69doors {
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}
76doors {
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}
82doors {
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}
89doors {
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}
96doors {
97 name: "Fatherland Door"
98 type: STANDARD
99 receivers: "Components/Doors/quicktravel6"
100 panels { room: "Fatherland" name: "FATHERLAND" }
101 location_room: "Fatherland"
102}
103doors {
104 name: "Quick Travel 8"
105 type: ITEM_ONLY
106 receivers: "Components/Paintings/QuickTravel/go8"
107 panels { room: "Fatherland" name: "FATHERLAND" }
108}
109doors {
110 name: "Quick Travel 10"
111 type: ITEM_ONLY
112 receivers: "Components/Paintings/QuickTravel/go10"
113 panels { room: "Fatherland" name: "FATHERLAND" }
114}
115doors {
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}
122doors {
123 name: "Quick Travel 9"
124 type: ITEM_ONLY
125 receivers: "Components/Paintings/QuickTravel/go9"
126 panels { room: "Mini Icarus 2" name: "BATTERY" }
127}
128doors {
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}
136doors {
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}
143doors {
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}
150doors {
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}
157doors {
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}
164doors {
165 name: "Quick Travel 6"
166 type: ITEM_ONLY
167 receivers: "Components/Paintings/QuickTravel/go6"
168 panels { room: "Welcome Spine (Reverse)" name: "TERMITE" }
169}
170doors {
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}
177doors {
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}
184doors {
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}
191doors {
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}
198doors {
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}
219doors {
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}
227doors {
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 @@
1display_name: "Icarus"
2type: ICARUS
3# The map's mastery is created at runtime.
4custom_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 @@
1name: "Above Trans Rights"
2panels {
3 name: "ANT"
4 path: "Panels/Room_1/ant"
5 clue: "ant"
6 answer: "colony"
7 symbols: PLANET
8}
9paintings {
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 @@
1name: "Banana Belt Door"
2paintings {
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 @@
1name: "Big U"
2panels {
3 name: "DECK"
4 path: "Panels/Room_1/deck"
5 clue: "deck"
6 answer: "card"
7 symbols: PLANET
8}
9panels {
10 name: "COLONY"
11 path: "Panels/Room_1/ant2"
12 clue: "colony"
13 answer: "ant"
14 symbols: PLANET
15}
16panels {
17 name: "MANOR"
18 path: "Panels/Room_1/manor"
19 clue: "manor"
20 answer: "mentor"
21 symbols: SPARKLES
22 symbols: PLANET
23}
24paintings {
25 name: "STOP3"
26 path: "Components/Paintings/QuickTravel/stop3"
27}
28paintings {
29 name: "GO3"
30 path: "Components/Paintings/QuickTravel/go3"
31 required_door { name: "Quick Travel 3" }
32}
33paintings {
34 name: "STOP1"
35 path: "Components/Paintings/QuickTravel/stop1"
36}
37paintings {
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 @@
1name: "Cow Quicktravel"
2paintings {
3 name: "SUN4"
4 path: "Components/Paintings/sun4"
5}
6paintings {
7 name: "GO4"
8 path: "Components/Paintings/QuickTravel/go4"
9 required_door { name: "Quick Travel 4" }
10}
11paintings {
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 @@
1name: "Fatherland Quicktravel"
2paintings {
3 name: "STOP7"
4 path: "Components/Paintings/QuickTravel/stop7"
5}
6paintings {
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 @@
1name: "Fatherland"
2panels {
3 name: "FATHERLAND"
4 path: "Panels/Room_1/fatherland"
5 clue: "fatherland"
6 answer: "homeland"
7 symbols: GENDER
8}
9paintings {
10 name: "SUN7"
11 path: "Components/Paintings/sun7"
12}
13paintings {
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 @@
1name: "Highest Point"
2panels {
3 name: "DIAGNOSIS"
4 path: "Panels/Room_1/diagnosis"
5 clue: "diagnosis"
6 answer: "diagnose"
7 symbols: PLANET
8}
9panels {
10 name: "QUEEN"
11 path: "Panels/Room_1/queen"
12 clue: "queen"
13 answer: "ant"
14 symbols: GENDER
15}
16paintings {
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 @@
1name: "Mastery"
2masteries {
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 @@
1name: "Maze Back"
2panels {
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 @@
1name: "Maze King Painting"
2paintings {
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 @@
1name: "Maze King Panel"
2panels {
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 @@
1name: "Maze Wings Passage"
2paintings {
3 name: "WINGS12"
4 path: "Components/Paintings/30 degrees/wings12"
5}
6paintings {
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 @@
1name: "Maze"
2panels {
3 name: "KING (1)"
4 path: "Panels/Other30degree panels/king"
5 clue: "king"
6 answer: "ruler"
7 symbols: GENDER
8}
9panels {
10 name: "ANALYSIS"
11 path: "Panels/Room_1/princes2"
12 clue: "analysis"
13 answer: "analyse"
14 symbols: PLANET
15}
16panels {
17 name: "MANSLAUGHTER"
18 path: "Panels/Room_1/manslaughter"
19 clue: "manslaughter"
20 answer: "murder"
21 symbols: GENDER
22}
23panels {
24 name: "MEDIUMS"
25 path: "Panels/Room_1/mediums"
26 clue: "mediums"
27 answer: "media"
28 symbols: PLANET
29}
30panels {
31 name: "BOOKS"
32 path: "Panels/Other30degree panels/books"
33 clue: "books"
34 answer: "library"
35 symbols: PLANET
36}
37paintings {
38 name: "SUN14"
39 path: "Components/Paintings/30 degrees/sun14"
40}
41paintings {
42 name: "WINGS14"
43 path: "Components/Paintings/30 degrees/wings14"
44}
45paintings {
46 name: "SUN5"
47 path: "Components/Paintings/30 degrees/sun5"
48}
49paintings {
50 name: "WINGS9"
51 path: "Components/Paintings/30 degrees/wings9"
52}
53paintings {
54 name: "SUN4"
55 path: "Components/Paintings/30 degrees/sun4"
56}
57paintings {
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 @@
1name: "Mediums Quicktravel"
2paintings {
3 name: "STOP9"
4 path: "Components/Paintings/QuickTravel/stop9"
5}
6paintings {
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 @@
1name: "Mini Icarus 2"
2panels {
3 name: "BATTERY"
4 path: "Panels/Room_1/battery"
5 clue: "battery"
6 answer: "cell"
7 symbols: PLANET
8}
9panels {
10 name: "TROUPE"
11 path: "Panels/Room_1/troupe"
12 clue: "troupe"
13 answer: "actor"
14 symbols: PLANET
15}
16panels {
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}
24panels {
25 name: "ARROWS"
26 path: "Panels/Room_1/arrow"
27 clue: "arrows"
28 answer: "quiver"
29 symbols: PLANET
30}
31panels {
32 name: "SQUAD"
33 path: "Panels/Room_1/ammo"
34 clue: "squad"
35 answer: "soldier"
36 symbols: PLANET
37}
38paintings {
39 name: "SUN10"
40 path: "Components/Paintings/sun10"
41}
42paintings {
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 @@
1name: "Mini Icarus Sun Loop"
2paintings {
3 name: "GO8"
4 path: "Components/Paintings/QuickTravel/go8"
5 required_door { name: "Quick Travel 8" }
6}
7paintings {
8 name: "STOP8"
9 path: "Components/Paintings/QuickTravel/stop8"
10}
11paintings {
12 name: "TROUBLEDESTINATION"
13 path: "Components/Paintings/TroubleDestination"
14}
15paintings {
16 name: "SUN12"
17 path: "Components/Paintings/sun12"
18}
19paintings {
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 @@
1name: "Mini Icarus Wings Painting"
2paintings {
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 @@
1name: "Painting Maze 1"
2paintings {
3 name: "SUN9"
4 path: "Components/Paintings/sun9"
5}
6paintings {
7 name: "WINGS3"
8 path: "Components/Paintings/wings3"
9}
10paintings {
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 @@
1name: "Painting Maze 2"
2paintings {
3 name: "WINGS10"
4 path: "Components/Paintings/30 degrees/wings10"
5}
6paintings {
7 name: "WINGS11"
8 path: "Components/Paintings/30 degrees/wings11"
9}
10paintings {
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 @@
1name: "Patricide Room"
2paintings {
3 name: "WINGS10"
4 path: "Components/Paintings/other 30 degrees/wings10"
5}
6paintings {
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 @@
1name: "Pillar Ramp"
2panels {
3 name: "ASTEROID"
4 path: "Panels/Room_1/asteroid"
5 clue: "asteroid"
6 answer: "belt"
7 symbols: PLANET
8}
9panels {
10 name: "BUNCH"
11 path: "Panels/Room_1/bunch"
12 clue: "bunch"
13 answer: "banana"
14 symbols: PLANET
15}
16panels {
17 name: "PRINCES"
18 path: "Panels/Room_1/princes"
19 clue: "princes"
20 answer: "princess"
21 symbols: PLANET
22}
23panels {
24 name: "PATRICIDE"
25 path: "Panels/Room_1/patricide"
26 clue: "patricide"
27 answer: "murder"
28 symbols: GENDER
29}
30panels {
31 name: "DRONE"
32 path: "Panels/Room_1/ant3"
33 clue: "drone"
34 answer: "ant"
35 symbols: GENDER
36}
37panels {
38 name: "PEA (1)"
39 path: "Panels/Room_1/pea"
40 clue: "pea"
41 answer: "pod"
42 symbols: PLANET
43}
44paintings {
45 name: "SUN6"
46 path: "Components/Paintings/sun6"
47}
48paintings {
49 name: "GO10"
50 path: "Components/Paintings/QuickTravel/go10"
51 required_door { name: "Quick Travel 10" }
52}
53paintings {
54 name: "STOP10"
55 path: "Components/Paintings/QuickTravel/stop10"
56}
57paintings {
58 name: "GO6"
59 path: "Components/Paintings/QuickTravel/go6"
60 required_door { name: "Quick Travel 6" }
61}
62paintings {
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 @@
1name: "Spiral Ramp"
2panels {
3 name: "FIREMAN"
4 path: "Panels/Room_1/fireman"
5 clue: "fireman"
6 answer: "firefighter"
7 symbols: GENDER
8}
9paintings {
10 name: "WINGS6"
11 path: "Components/Paintings/oneways/wings6"
12 required_door { name: "Near Fireman Wings Painting" }
13}
14paintings {
15 name: "SUN5"
16 path: "Components/Paintings/sun5"
17 required_door { name: "Sun Painting To Drone" }
18}
19paintings {
20 name: "WINGS7"
21 path: "Components/Paintings/oneways/wings7"
22}
23paintings {
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 @@
1name: "The Orb"
2panels {
3 name: "ADDERS"
4 path: "Panels/Room_1/needle2"
5 clue: "adders"
6 answer: "sum"
7 symbols: PLANET
8}
9panels {
10 name: "PUPPY"
11 path: "Panels/Room_1/puppy"
12 clue: "puppy"
13 answer: "garbage"
14 symbols: SUN
15 symbols: PLANET
16}
17panels {
18 name: "NEEDLE"
19 path: "Panels/Room_1/needle"
20 clue: "needle"
21 answer: "needless"
22 symbols: PLANET
23}
24panels {
25 name: "US"
26 path: "Panels/Room_1/us"
27 clue: "us"
28 answer: "mess"
29 symbols: PLANET
30}
31panels {
32 name: "FISH"
33 path: "Panels/Room_1/fish"
34 clue: "fish"
35 answer: "student"
36 symbols: BOXES
37 symbols: PLANET
38}
39panels {
40 name: "DEADLINE"
41 path: "Panels/Room_1/deadline"
42 clue: "deadline"
43 answer: "deadliness"
44 symbols: PLANET
45}
46panels {
47 name: "PEA (2)"
48 path: "Panels/Room_1/pea2"
49 clue: "pea"
50 answer: "dolphin"
51 symbols: PLANET
52}
53panels {
54 name: "THESIS"
55 path: "Panels/Room_1/thesis"
56 clue: "thesis"
57 answer: "these"
58 symbols: PLANET
59}
60panels {
61 name: "CLUTCH (1)"
62 path: "Panels/Room_1/clutch"
63 clue: "clutch"
64 answer: "dude"
65 symbols: PLANET
66 symbols: GENDER
67}
68panels {
69 name: "BASIS (2)"
70 path: "Panels/Room_1/basis2"
71 clue: "basis"
72 answer: "base"
73 symbols: PLANET
74}
75panels {
76 name: "AXIS"
77 path: "Panels/Room_1/axis"
78 clue: "axis"
79 answer: "Axe"
80 symbols: PLANET
81}
82panels {
83 name: "STRAIGHT"
84 path: "Panels/Room_1/straight"
85 clue: "straight"
86 answer: "queer"
87 symbols: GENDER
88}
89panels {
90 name: "HISS"
91 path: "Panels/Room_1/hiss"
92 clue: "hiss"
93 answer: "their"
94 symbols: PLANET
95 symbols: GENDER
96}
97panels {
98 name: "DISCUS"
99 path: "Panels/Room_1/discus"
100 clue: "discus"
101 answer: "discuss"
102 symbols: PLANET
103}
104panels {
105 name: "SON"
106 path: "Panels/Room_1/son"
107 clue: "son"
108 answer: "child"
109 symbols: GENDER
110}
111panels {
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 @@
1name: "Through Woman (Obverse)"
2panels {
3 name: "HUMAN (2)"
4 path: "Panels/Room_1/human"
5 clue: "human"
6 answer: "man"
7 symbols: GENDER
8}
9panels {
10 name: "COW"
11 path: "Panels/Room_1/cow"
12 clue: "cow"
13 answer: "elephant"
14 symbols: GENDER
15}
16paintings {
17 name: "SUN2"
18 path: "Components/Paintings/sun2"
19}
20paintings {
21 name: "WINGS6"
22 path: "Components/Paintings/wings6"
23}
24paintings {
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 @@
1name: "Through Woman (Reverse)"
2panels {
3 name: "PRINCE"
4 path: "Panels/Room_1/prince"
5 clue: "prince"
6 answer: "princess"
7 symbols: GENDER
8}
9panels {
10 name: "BASIS (1)"
11 path: "Panels/Room_1/basis"
12 clue: "basis"
13 answer: "bases"
14 symbols: PLANET
15}
16paintings {
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 @@
1name: "Trans Rights Panels"
2panels {
3 name: "AGENDER"
4 path: "Panels/Room_1/human4"
5 clue: "agender"
6 answer: "human"
7 symbols: GENDER
8}
9panels {
10 name: "HUMAN (3)"
11 path: "Panels/Room_1/human5"
12 clue: "human"
13 answer: "female"
14 symbols: GENDER
15}
16panels {
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 @@
1name: "Trans Rights"
2# slay
3panels {
4 name: "SERVANT (1)"
5 path: "Panels/Room_1/servant"
6 clue: "servant"
7 answer: "butler"
8 symbols: GENDER
9}
10panels {
11 name: "SERVANT (2)"
12 path: "Panels/Room_1/servant2"
13 clue: "servant"
14 answer: "maid"
15 symbols: GENDER
16}
17paintings {
18 name: "GO2"
19 path: "Components/Paintings/QuickTravel/go2"
20 required_door { name: "Quick Travel 2" }
21}
22paintings {
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 @@
1name: "Welcome Spine (Obverse)"
2panels {
3 name: "FISHWIFE"
4 path: "Panels/Room_1/fishwife"
5 clue: "fishwife"
6 answer: "fishmonger"
7 symbols: GENDER
8}
9panels {
10 name: "HUMAN (1)"
11 path: "Panels/Room_1/human3"
12 clue: "human"
13 answer: "woman"
14 symbols: GENDER
15}
16ports {
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 @@
1name: "Welcome Spine (Reverse)"
2panels {
3 name: "FATHER"
4 path: "Panels/Room_1/father"
5 clue: "father"
6 answer: "parent"
7 symbols: GENDER
8}
9panels {
10 name: "TERMITE"
11 path: "Panels/Room_1/bat"
12 clue: "termite"
13 answer: "colony"
14 symbols: PLANET
15}
16panels {
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 @@
1name: "Welcome Spine Quicktravel"
2paintings {
3 name: "STOP5"
4 path: "Components/Paintings/QuickTravel/stop5"
5}
6paintings {
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 @@
1connections {
2 from_room: "Main Area"
3 to_room: "CBA"
4 door { name: "CBA Door" }
5}
6connections {
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 @@
1doors {
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}
11doors {
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}
44doors {
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 @@
1display_name: "The Advanced"
2type: GIFT_MAP
3# The map's mastery is created at runtime.
4custom_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 @@
1name: "CBA"
2panels {
3 name: "CBA (1)"
4 path: "Panels/Room_1/panel_29"
5 clue: ""
6 answer: "chess"
7 symbols: QUESTION
8}
9panels {
10 name: "CBA (2)"
11 path: "Panels/Room_1/panel_30"
12 clue: ""
13 answer: "battle"
14 symbols: QUESTION
15}
16panels {
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 @@
1name: "Main Area"
2panels {
3 name: "OBSERVE"
4 path: "Panels/Room_1/panel_1"
5 clue: "observe"
6 answer: "watch"
7 symbols: SUN
8}
9panels {
10 name: "I"
11 path: "Panels/Room_1/panel_2"
12 clue: "i"
13 answer: "eye"
14 symbols: ZERO
15}
16panels {
17 name: "REST"
18 path: "Panels/Room_1/panel_3"
19 clue: "rest"
20 answer: "sleep"
21 symbols: SUN
22}
23panels {
24 name: "THE"
25 path: "Panels/Room_1/panel_4"
26 clue: "the"
27 answer: "a"
28 symbols: ZERO
29}
30panels {
31 name: "LIVES"
32 path: "Panels/Room_1/panel_5"
33 clue: "lives"
34 answer: "souls"
35 symbols: SUN
36}
37panels {
38 name: "DAIRY (1)"
39 path: "Panels/Room_1/panel_6"
40 clue: "dairy"
41 answer: "cheese"
42 symbols: EXAMPLE
43}
44panels {
45 name: "DAIRY (2)"
46 path: "Panels/Room_1/panel_7"
47 clue: "dairy"
48 answer: "butter"
49 symbols: EXAMPLE
50}
51panels {
52 name: "DAIRY SAUCE"
53 path: "Panels/Room_1/panel_8"
54 clue: "dairy? sauce."
55 answer: "alfredo"
56 symbols: EXAMPLE
57}
58panels {
59 name: "GULLIBLE (1)"
60 path: "Panels/Room_1/panel_9"
61 clue: "gullible"
62 answer: "credulous"
63 symbols: SUN
64}
65panels {
66 name: "GULLIBLE (2)"
67 path: "Panels/Room_1/panel_10"
68 clue: "gullible"
69 answer: "bird"
70 symbols: SPARKLES
71 symbols: EXAMPLE
72}
73panels {
74 name: "GULLIBLE (3)"
75 path: "Panels/Room_1/panel_11"
76 clue: "gullible"
77 answer: "advice"
78 symbols: QUESTION
79}
80panels {
81 name: "FRUIT (1)"
82 path: "Panels/Room_1/panel_12"
83 clue: "fruit"
84 answer: "cherry"
85 symbols: EXAMPLE
86}
87panels {
88 name: "FRUIT (2)"
89 path: "Panels/Room_1/panel_13"
90 clue: "fruit"
91 answer: "banana"
92 symbols: EXAMPLE
93}
94panels {
95 name: "FRUIT FRUIT"
96 path: "Panels/Room_1/panel_14"
97 clue: "fruit? fruit!"
98 answer: "avocado"
99 symbols: EXAMPLE
100}
101panels {
102 name: "BIRD"
103 path: "Panels/Room_1/panel_15"
104 clue: "bird"
105 answer: "canary"
106 symbols: EXAMPLE
107}
108panels {
109 name: "UNBOTTLING"
110 path: "Panels/Room_1/panel_16"
111 clue: "unbottling"
112 answer: "bottling"
113 symbols: SUN
114}
115panels {
116 name: "ORGANIZATION"
117 path: "Panels/Room_1/panel_17"
118 clue: "organization"
119 answer: "association"
120 symbols: SUN
121}
122panels {
123 name: "ORDER (1)"
124 path: "Panels/Room_1/panel_18"
125 clue: "order"
126 answer: "chaos"
127 symbols: SUN
128}
129panels {
130 name: "ORDER (2)"
131 path: "Panels/Room_1/panel_19"
132 clue: "order"
133 answer: "border"
134 symbols: SPARKLES
135}
136panels {
137 name: "ORDER (3)"
138 path: "Panels/Room_1/panel_20"
139 clue: "order"
140 answer: "arrange"
141 symbols: SUN
142}
143panels {
144 name: "DECK (1)"
145 path: "Panels/Room_1/panel_21"
146 clue: "deck"
147 answer: "card"
148 symbols: BOXES
149}
150panels {
151 name: "DECK (2)"
152 path: "Panels/Room_1/panel_22"
153 clue: "deck"
154 answer: "black"
155 symbols: BOXES
156}
157panels {
158 name: "DECK (3)"
159 path: "Panels/Room_1/panel_23"
160 clue: "deck"
161 answer: "ace"
162 symbols: BOXES
163}
164panels {
165 name: "Blank (1)"
166 path: "Panels/Room_1/panel_24"
167 clue: ""
168 answer: "identity"
169}
170panels {
171 name: "Blank (2)"
172 path: "Panels/Room_1/panel_25"
173 clue: ""
174 answer: "theft"
175}
176panels {
177 name: "Blank (3)"
178 path: "Panels/Room_1/panel_26"
179 clue: ""
180 answer: "is"
181}
182panels {
183 name: "Blank (4)"
184 path: "Panels/Room_1/panel_27"
185 clue: ""
186 answer: "a"
187}
188panels {
189 name: "Blank (5)"
190 path: "Panels/Room_1/panel_28"
191 clue: ""
192 answer: "crime"
193}
194ports {
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 @@
1name: "Mastery"
2masteries {
3 name: "MASTERY"
4 path: "Components/Collectables/collectable"
5}
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}
266connections {
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 {
241doors { 241doors {
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}
254doors {
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}
261doors {
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}
9ports { 9ports {
10 name: "TREE"
11 path: "Components/Warps/worldport3"
12 orientation: "north"
13 required_door { name: "Control Center Brown Door" }
14}
15ports {
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}
58ports { 58ports {
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 @@
1name: "Tree Entrance"
2ports {
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}
14ports { 14ports {
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}
19ports { 21ports {
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}
198ports { 198ports {
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 @@
1name: "Plaza Entrance" 1name: "Plaza Entrance"
2ports { 2ports {
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
2doors { 1doors {
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}
128ports { 128ports {
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 @@
1connections {
2 from_room: "Main Area"
3 to_room: "Latitude South"
4 door { name: "Latitude South Door" }
5}
6connections {
7 from_room: "Main Area"
8 to_room: "Latitude Middle"
9 door { name: "Latitude Middle Door" }
10}
11connections {
12 from_room: "Main Area"
13 to_room: "Latitude North"
14 door { name: "Latitude North Door" }
15}
16connections {
17 from_room: "Main Area"
18 to_room: "Longitude West"
19 door { name: "Longitude West Door" }
20}
21connections {
22 from_room: "Main Area"
23 to_room: "Longitude Middle"
24 door { name: "Longitude Middle Door" }
25}
26connections {
27 from_room: "Main Area"
28 to_room: "Longitude East"
29 door { name: "Longitude East Door" }
30}
31connections {
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 @@
1doors {
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}
8doors {
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}
15doors {
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}
22doors {
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}
29doors {
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}
36doors {
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}
43doors {
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 @@
1display_name: "The Charismatic"
2type: GIFT_MAP
3# The map's mastery is created at runtime.
4custom_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 @@
1name: "Latitude Middle"
2panels {
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 @@
1name: "Latitude North"
2panels {
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 @@
1name: "Latitude South"
2panels {
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 @@
1name: "Longitude East"
2panels {
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 @@
1name: "Longitude Middle"
2panels {
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 @@
1name: "Longitude West"
2panels {
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 @@
1name: "Main Area"
2panels {
3 name: "ARC"
4 path: "Panels/Room_1/panel_1"
5 clue: "arc"
6 answer: "arctic"
7 symbols: QUESTION
8}
9panels {
10 name: "KING"
11 path: "Panels/Room_1/panel_2"
12 clue: "king"
13 answer: "tacking"
14 symbols: QUESTION
15}
16panels {
17 name: "TIP"
18 path: "Panels/Room_1/panel_3"
19 clue: "tip"
20 answer: "tiptoe"
21 symbols: QUESTION
22}
23panels {
24 name: "AQUA"
25 path: "Panels/Room_1/panel_4"
26 clue: "aqua"
27 answer: "aquatic"
28 symbols: QUESTION
29}
30panels {
31 name: "TILE"
32 path: "Panels/Room_1/panel_5"
33 clue: "tile"
34 answer: "tactile"
35 symbols: QUESTION
36}
37panels {
38 name: "NAIL"
39 path: "Panels/Room_1/panel_6"
40 clue: "nail"
41 answer: "toenail"
42 symbols: QUESTION
43}
44panels {
45 name: "HERE"
46 path: "Panels/Room_1/panel_7"
47 clue: "here"
48 answer: "heretic"
49 symbols: QUESTION
50}
51panels {
52 name: "IT"
53 path: "Panels/Room_1/panel_8"
54 clue: "it"
55 answer: "tacit"
56 symbols: QUESTION
57}
58panels {
59 name: "PINS"
60 path: "Panels/Room_1/panel_9"
61 clue: "pins"
62 answer: "pintoes"
63 symbols: QUESTION
64}
65panels {
66 name: "Blank"
67 path: "Panels/Room 3/panel_10"
68 clue: ""
69 answer: "tactic"
70 symbols: QUESTION
71}
72ports {
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 @@
1name: "Mastery"
2masteries {
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
2doors { 1doors {
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}
106doors {
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}
24ports { 24ports {
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}
29ports { 31ports {
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}
9ports { 9ports {
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 a714eba..fab8d95 100644 --- a/data/maps/the_congruent/doors.txtpb +++ b/data/maps/the_congruent/doors.txtpb
@@ -1,12 +1,22 @@
1doors { 1doors {
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}
9doors { 10doors {
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}
19doors {
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"
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 @@
1display_name: "The Congruent" 1display_name: "The Congruent"
2worldport_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"
2keyholders { 2keyholders {
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"
2keyholders { 2keyholders {
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}
86ports { 86ports {
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}
91paintings { 93paintings {
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 @@
1connections {
2 from_room: "Main Area"
3 to_room: "Painting Divot"
4 door { name: "Checkpoint Panels" }
5 oneway: true
6}
7connections {
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}
22connections {
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 @@
1doors {
2 name: "Mastery"
3 type: EVENT
4 panels { room: "Flip Area" name: "SUCCEED" }
5}
6doors {
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 @@
1display_name: "The Crystalline"
2type: GIFT_MAP
3# The map's mastery is created at runtime.
4custom_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 @@
1name: "Flip Area"
2panels {
3 name: "SUCCEED"
4 path: "Panels/Room_1/panel_3"
5 clue: "succeed"
6 answer: "win"
7 symbols: SUN
8}
9paintings {
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 @@
1name: "Main Area"
2panels {
3 name: "LEAP"
4 path: "Panels/Room_1/panel_1"
5 clue: "leap"
6 answer: "jump"
7 symbols: SUN
8}
9panels {
10 name: "DROP"
11 path: "Panels/Room_1/panel_2"
12 clue: "drop"
13 answer: "fall"
14 symbols: SUN
15}
16panels {
17 name: "SPIN"
18 path: "Panels/Room_1/panel_4"
19 clue: "spin"
20 answer: "flip"
21 symbols: SUN
22}
23ports {
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 @@
1name: "Mastery"
2masteries {
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 @@
1name: "Painting Divot"
2paintings {
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 @@
1connections { 1connections {
2 from_room: "First Room"
3 to_room: "First Room Exit"
4 door { name: "Second Room Entrance" }
5}
6connections {
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}
16connections { 21connections {
22 from_room: "Second Room"
23 to_room: "Second Room Exit"
24 door { name: "Third Room Entrance" }
25}
26connections {
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}
46connections {
47 from_room: "First Room"
48 to_room: "Cyan Hallway"
49 door { name: "Colorful Entrance" }
50}
51connections {
52 from_room: "Second Room"
53 to_room: "Congruent Entrance"
54 door { name: "Congruent Entrance" }
55}
56connections {
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
2doors { 1doors {
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 @@
1name: "Congruent Entrance"
2panel_display_name: "Second Room"
3ports {
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 @@
1name: "Cyan Hallway"
2panel_display_name: "First Room"
3ports {
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 @@
1name: "Double Sided Entrance"
2panel_display_name: "First Room"
3ports {
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 @@
1name: "First Room Exit"
2panel_display_name: "First Room"
3ports {
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}
34ports { 34ports {
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
39ports {
40 name: "NEXT"
41 path: "Components/Warps/worldport2"
42 orientation: "north"
43 required_door { name: "Second Room Entrance" }
44}
45ports {
46 name: "COLORFUL"
47 path: "Components/Warps/worldport8"
48 orientation: "north"
49 required_door { name: "Colorful Entrance" }
50}
51ports {
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 @@
1name: "Second Room Exit"
2panel_display_name: "Second Room"
3ports {
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}
39ports { 39ports {
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
44ports {
45 name: "NEXT"
46 path: "Components/Warps/worldport4"
47 orientation: "north"
48 required_door { name: "Third Room Entrance" }
49}
50ports {
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}
66ports { 66ports {
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}
26connections { 26connections {
27 from_room: "Gallery Maze"
28 to_room: "Main Area"
29 oneway: true
30}
31connections {
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 {
42doors { 42doors {
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}
56doors {
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 @@
1name: "Gallery Maze" 1name: "Gallery Maze"
2ports { 2ports {
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}
28ports { 28ports {
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}
33ports { 35ports {
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}
38ports { 42ports {
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}
9ports { 9ports {
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 @@
1name: "Unyielding Entrance" 1name: "Unyielding Entrance"
2ports { 2ports {
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.
117doors {
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}
143doors {
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}
169doors {
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 @@
1display_name: "The Double Sided" 1display_name: "The Double Sided"
2worldport_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}
16ports { 16ports {
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}
66connections { 66connections {
67 from_room: "Flipped Pyramid Area"
68 to_room: "Liberated Entrance Panel"
69 oneway: true
70}
71connections {
72 from_room: "Flipped Pyramid Area"
73 to_room: "Literate Entrance Panel"
74 oneway: true
75}
76connections {
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}
112connections {
113 from_room: "Wrath Room"
114 to_room: "Least Blue Last"
115 oneway: true
100} 116}
101connections { 117connections {
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}
156connections { 172connections {
173 from_room: "Lime Room"
174 to_room: "Revitalized Entrance"
175 door { name: "Revitalized Entrance" }
176}
177connections {
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}
177connections { 197connections {
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}
182connections { 203connections {
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}
223connections {
224 from_room: "Flipped Second Room"
225 to_room: "Four Rooms Entrance"
226 door { name: "Flipped Second Room Right Door" }
227}
228connections {
229 from_room: "Link Area"
230 to_room: "Liberated Entrance"
231 door { name: "Liberated Entrance" }
232}
233connections {
234 from_room: "Link Area"
235 to_room: "Literate Entrance"
236 door { name: "Literate Entrance" }
237}
238connections {
239 from_room: "Liberated Entrance"
240 to_room: "Liberated Entrance Panel"
241 oneway: true
242}
243connections {
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.
70doors { 70doors {
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}
78doors { 79doors {
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}
88doors {
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}
85doors { 96doors {
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}
105doors {
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}
120doors { 140doors {
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}
155doors {
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}
164doors {
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}
172doors {
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.
135doors { 181doors {
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}
143doors { 191doors {
144 name: "Lime Room Entrance" 192 name: "Lime Room Entrance"
@@ -159,6 +207,7 @@ doors {
159doors { 207doors {
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}
215doors { 264doors {
@@ -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}
228doors { 277doors {
@@ -301,7 +350,6 @@ doors {
301doors { 350doors {
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 {
309doors { 357doors {
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}
364doors {
365 name: "Gift Maps Entrance"
366 type: EVENT
367 receivers: "Components/GiftMapEntrance/PanelTeleporter"
368 double_letters: true
369}
370doors {
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}
384doors {
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}
391doors {
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}
398doors {
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"
11excluded_nodes: "Panels/Back Left/backleft_4_proxied_2" 11excluded_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.
13excluded_nodes: "Panels/Entry/entry_proxied_fake" 13excluded_nodes: "Panels/Entry/entry_proxied_fake"
14# The gift map entrance is created by the mod.
15custom_nodes: "Components/GiftMapEntrance/GongusPanel"
16custom_nodes: "Components/GiftMapEntrance/HatkirbyPanel"
17custom_nodes: "Components/GiftMapEntrance/IcelyPanel"
18custom_nodes: "Components/GiftMapEntrance/KirbyPanel"
19custom_nodes: "Components/GiftMapEntrance/KiwiPanel"
20custom_nodes: "Components/GiftMapEntrance/Panel"
21custom_nodes: "Components/GiftMapEntrance/QPanel"
22custom_nodes: "Components/GiftMapEntrance/SouveyPanel"
23custom_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"
2panel_display_name: "Starting Room" 2panel_display_name: "Starting Room"
3ports { 3ports {
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"
2panel_display_name: "Starting Room" 2panel_display_name: "Starting Room"
3ports { 3ports {
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"
2panel_display_name: "Starting Room" 2panel_display_name: "Starting Room"
3ports { 3ports {
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"
2panel_display_name: "Starting Room" 2panel_display_name: "Starting Room"
3ports { 3ports {
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}
32ports { 32ports {
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 @@
1name: "Flipped Pyramid Area" 1name: "Flipped Pyramid Area"
2panel_display_name: "Pyramid Area" 2panel_display_name: "Pyramid Area"
3panels { 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}
10panels {
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}
24ports {
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 @@
1name: "Four Rooms Entrance"
2ports {
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}
10ports { 10ports {
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}
73ports { 73ports {
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}
78paintings { 80paintings {
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 @@
1name: "Liberated Entrance Panel"
2panel_display_name: "Pyramid Area"
3panels {
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 @@
1name: "Liberated Entrance"
2ports {
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}
22ports {
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}
29ports {
30 name: "BLUE"
31 path: "worldport8"
32 orientation: "west"
33 required_door { name: "Liberated Entrance" }
34}
35ports {
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 @@
1name: "Literate Entrance Panel"
2panel_display_name: "Pyramid Area"
3panels {
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 @@
1name: "Literate Entrance"
2ports {
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}
10ports { 10ports {
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 @@
1name: "Rabbit Hole" 1name: "Rabbit Hole"
2panel_display_name: "Red Blue Area" 2panel_display_name: "Red Blue Area"
3panels { 3panels {
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}
9ports { 9ports {
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"
2panel_display_name: "Starting Room" 2panel_display_name: "Starting Room"
3ports { 3ports {
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 @@
1name: "Revitalized Entrance"
2panel_display_name: "Colored Doors Area"
3ports {
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 @@
1name: "Shop Entrance" 1name: "Shop Entrance"
2panel_display_name: "Starting Room" 2panel_display_name: "Shop Entrance"
3panels { 3panels {
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}
10ports { 10ports {
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}
29panels { 31panels {
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}
49panels {
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}
47letters { 68letters {
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"
2panel_display_name: "Colored Doors Area" 2panel_display_name: "Colored Doors Area"
3ports { 3ports {
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}
7ports { 7ports {
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}
23ports { 23ports {
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 @@
1connections {
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 @@
1doors {
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}
8doors {
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}
25doors {
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 @@
1display_name: "The Fuzzy"
2type: GIFT_MAP
3# The map's mastery is created at runtime.
4custom_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 @@
1name: "Main Area"
2panels {
3 name: "Blank"
4 path: "Panels/Room_1/panel_1"
5 clue: ""
6 answer: "2475"
7 symbols: LINGO
8 symbols: QUESTION
9}
10panels {
11 name: "TUTU"
12 path: "Panels/Room_1/panel_2"
13 clue: "tutu"
14 answer: "22"
15 symbols: ZERO
16 symbols: EVAL
17}
18panels {
19 name: "LOTTO"
20 path: "Panels/Room_1/panel_3"
21 clue: "lotto"
22 answer: "22222222"
23 symbols: ZERO
24 symbols: EVAL
25}
26panels {
27 name: "WHERETO"
28 path: "Panels/Room_1/panel_10"
29 clue: "whereto"
30 answer: "sides"
31 symbols: QUESTION
32}
33panels {
34 name: "DICE"
35 path: "Panels/Room_1/panel_11"
36 clue: "dice"
37 answer: "4935"
38 symbols: QUESTION
39}
40panels {
41 name: "CAGED"
42 path: "Panels/Room_1/panel_12"
43 clue: "caged"
44 answer: "31754"
45 symbols: QUESTION
46}
47panels {
48 name: "BEFORE"
49 path: "Panels/Room_1/panel_13"
50 clue: "before"
51 answer: "100"
52 symbols: ZERO
53 symbols: EVAL
54}
55panels {
56 name: "TOED"
57 path: "Panels/Room_1/panel_14"
58 clue: "toed"
59 answer: "108"
60 symbols: ZERO
61 symbols: EVAL
62}
63panels {
64 name: "FORGED"
65 path: "Panels/Room_1/panel_15"
66 clue: "forged"
67 answer: "3016"
68 symbols: ZERO
69 symbols: EVAL
70}
71panels {
72 name: "OTHERS"
73 path: "Panels/Room_1/panel_4"
74 clue: "others"
75 answer: "34390869"
76 symbols: QUESTION
77}
78panels {
79 name: "COMBINED"
80 path: "Panels/Room_1/panel_9"
81 clue: "combined"
82 answer: "added"
83 symbols: SUN
84}
85panels {
86 name: "ACHIEVES"
87 path: "Panels/Room_1/panel_5"
88 clue: "achieves"
89 answer: "4214"
90 symbols: QUESTION
91}
92panels {
93 name: "UNVEILED"
94 path: "Panels/Room_1/panel_6"
95 clue: "unveiled"
96 answer: "12122021"
97 symbols: QUESTION
98}
99panels {
100 name: "FIRST"
101 path: "Panels/Room_1/panel_8"
102 clue: "first"
103 answer: "1"
104 symbols: QUESTION
105}
106panels {
107 name: "BOTH"
108 path: "Panels/Room_1/panel_7"
109 clue: "both"
110 answer: "2"
111 symbols: QUESTION
112}
113ports {
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 @@
1name: "Mastery"
2masteries {
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.
2doors { 2doors {
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}
28doors { 28doors {
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}
35doors { 35doors {
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}
71doors { 71doors {
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}
78doors { 78doors {
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}
106doors { 106doors {
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}
143doors { 143doors {
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}
150doors { 150doors {
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}
157doors { 157doors {
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}
164doors { 164doors {
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}
171doors { 171doors {
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}
188doors { 188doors {
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}
195doors { 195doors {
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}
202doors { 202doors {
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}
209doors { 209doors {
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}
216doors { 216doors {
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}
223doors { 223doors {
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}
230doors { 230doors {
@@ -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}
260doors { 261doors {
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}
163ports { 163ports {
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 @@
1doors {
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}
26doors {
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}
259connections {
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 f0f2fde..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}
31doors { 31doors {
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}
40doors {
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 {
54doors { 63doors {
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}
60doors { 70doors {
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}
66doors { 77doors {
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}
72doors { 84doors {
@@ -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}
128doors { 141doors {
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}
209doors { 223doors {
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}
226doors { 241doors {
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}
322doors { 338doors {
323 name: "Courtyard Side Door" 339 name: "Courtyard Side Door"
@@ -410,7 +426,8 @@ doors {
410} 426}
411doors { 427doors {
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}
427doors { 444doors {
428 name: "Nature Room Door" 445 name: "Nature Room Door"
@@ -468,6 +485,7 @@ 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}
472doors { 490doors {
473 name: "Lavender Cube" 491 name: "Lavender Cube"
@@ -494,6 +512,7 @@ doors {
494 panels { room: "Zero Room" name: "MANY" } 512 panels { room: "Zero Room" name: "MANY" }
495 panels { room: "Zero Room" name: "REMAINING" } 513 panels { room: "Zero Room" name: "REMAINING" }
496 location_room: "Zero Room" 514 location_room: "Zero Room"
515 location_name: "Panels"
497} 516}
498doors { 517doors {
499 name: "Spiral Painting" 518 name: "Spiral Painting"
@@ -508,3 +527,104 @@ doors {
508 receivers: "Panels/General/entry_7/teleportListener" 527 receivers: "Panels/General/entry_7/teleportListener"
509 double_letters: true 528 double_letters: true
510} 529}
530doors {
531 name: "Partial Entrance"
532 type: EVENT
533 panels { room: "West Side" name: "CLUE" }
534}
535doors {
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}
546doors {
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}
553doors {
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}
560doors {
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}
567doors {
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}
574doors {
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}
582doors {
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}
590doors {
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}
599doors {
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}
608doors {
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}
617doors {
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}
624doors {
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}
31panels {
32 name: "Top Landscape Top"
33 path: "Panels/Kiwi Room/kiwi_1"
34 clue: ""
35 answer: "one"
36 symbols: QUESTION
37}
38panels {
39 name: "Top Landscape Right"
40 path: "Panels/Kiwi Room/kiwi_2"
41 clue: ""
42 answer: "road"
43 symbols: QUESTION
44}
45panels {
46 name: "Top Landscape Bottom"
47 path: "Panels/Kiwi Room/kiwi_3"
48 clue: ""
49 answer: "many"
50 symbols: QUESTION
51}
52panels {
53 name: "Top Landscape Left"
54 path: "Panels/Kiwi Room/kiwi_4"
55 clue: ""
56 answer: "turns"
57 symbols: QUESTION
58}
59panels {
60 name: "Left Landscape Top"
61 path: "Panels/Kiwi Room/kiwi_5"
62 clue: ""
63 answer: "find"
64 symbols: QUESTION
65}
66panels {
67 name: "Left Landscape Right"
68 path: "Panels/Kiwi Room/kiwi_6"
69 clue: ""
70 answer: "keys"
71 symbols: QUESTION
72}
73panels {
74 name: "Left Landscape Bottom"
75 path: "Panels/Kiwi Room/kiwi_7"
76 clue: ""
77 answer: "write"
78 symbols: QUESTION
79}
80panels {
81 name: "Left Landscape Left"
82 path: "Panels/Kiwi Room/kiwi_8"
83 clue: ""
84 answer: "words"
85 symbols: QUESTION
86}
87panels {
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}
95panels {
96 name: "Right Landscape Right"
97 path: "Panels/Kiwi Room/kiwi_10"
98 clue: ""
99 answer: "lies"
100 symbols: QUESTION
101}
102panels {
103 name: "Right Landscape Bottom"
104 path: "Panels/Kiwi Room/kiwi_11"
105 clue: ""
106 answer: "the"
107 symbols: QUESTION
108}
109panels {
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}
117paintings { 31paintings {
118 name: "SPIRAL" 32 name: "SPIRAL"
119 path: "Components/Paintings/spiral" 33 path: "Components/Paintings/spiral"
@@ -124,23 +38,31 @@ paintings {
124} 38}
125ports { 39ports {
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}
130ports { 46ports {
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}
135ports { 53ports {
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}
142ports { 62ports {
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"
2panel_display_name: "Pillar Room" 2panel_display_name: "Pillar Room"
3ports { 3ports {
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}
10ports { 10ports {
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}
10ports { 10ports {
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"
2panel_display_name: "West Side" 2panel_display_name: "West Side"
3ports { 3ports {
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 82ec48c..a5ed9f3 100644 --- a/data/maps/the_great/rooms/Main Area.txtpb +++ b/data/maps/the_great/rooms/Main Area.txtpb
@@ -122,27 +122,37 @@ panels {
122} 122}
123ports { 123ports {
124 name: "ENTRY" 124 name: "ENTRY"
125 display_name: "Entry Building"
125 path: "Meshes/Blocks/Warps/worldport" 126 path: "Meshes/Blocks/Warps/worldport"
126 orientation: "south" 127 destination { x: 33 y: 0 z: 15 }
128 rotation: 0
127} 129}
128ports { 130ports {
129 name: "KEEN" 131 name: "KEEN"
132 display_name: "Keen Building Front"
130 path: "Meshes/Blocks/Warps/worldport6" 133 path: "Meshes/Blocks/Warps/worldport6"
131 orientation: "north" 134 destination { x: 33 y: 0 z: -21 }
135 rotation: 180
132} 136}
133ports { 137ports {
134 name: "ORB" 138 name: "ORB"
139 display_name: "Orb Building"
135 path: "Meshes/Blocks/Warps/worldport3" 140 path: "Meshes/Blocks/Warps/worldport3"
136 orientation: "north" 141 destination { x: 62 y: 0 z: -13 }
142 rotation: 180
137} 143}
138ports { 144ports {
139 name: "LINEAR" 145 name: "LINEAR"
146 display_name: "Keen Building Back"
140 path: "Meshes/Blocks/Warps/worldport15" 147 path: "Meshes/Blocks/Warps/worldport15"
141 orientation: "south" 148 destination { x: 33 y: 0 z: -42.5 }
149 rotation: 0
142} 150}
143ports { 151ports {
144 name: "DIGITAL" 152 name: "DIGITAL"
153 display_name: "Digital Hole"
145 path: "Meshes/Blocks/Warps/worldport4" 154 path: "Meshes/Blocks/Warps/worldport4"
146 orientation: "down" 155 destination { x: -6.5 y: 0 z: 7.5 }
156 rotation: 0
147 required_door { name: "Digital Entrance" } 157 required_door { name: "Digital Entrance" }
148} 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 @@
1name: "Maze Tower" 1name: "Maze Tower"
2panel_display_name: "Courtyard"
3panels { 2panels {
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}
57ports { 57ports {
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"
2panel_display_name: "Main Area" 2panel_display_name: "Main Area"
3ports { 3ports {
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"
2panel_display_name: "Main Area" 2panel_display_name: "Main Area"
3ports { 3ports {
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"
2panel_display_name: "Question Room" 2panel_display_name: "Question Room"
3ports { 3ports {
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 @@
1name: "The Landscapes"
2panel_display_name: "The Landscapes"
3panels {
4 name: "Top Landscape Top"
5 path: "Panels/Kiwi Room/kiwi_1"
6 clue: ""
7 answer: "one"
8 symbols: QUESTION
9}
10panels {
11 name: "Top Landscape Right"
12 path: "Panels/Kiwi Room/kiwi_2"
13 clue: ""
14 answer: "road"
15 symbols: QUESTION
16}
17panels {
18 name: "Top Landscape Bottom"
19 path: "Panels/Kiwi Room/kiwi_3"
20 clue: ""
21 answer: "many"
22 symbols: QUESTION
23}
24panels {
25 name: "Top Landscape Left"
26 path: "Panels/Kiwi Room/kiwi_4"
27 clue: ""
28 answer: "turns"
29 symbols: QUESTION
30}
31panels {
32 name: "Left Landscape Top"
33 path: "Panels/Kiwi Room/kiwi_5"
34 clue: ""
35 answer: "find"
36 symbols: QUESTION
37}
38panels {
39 name: "Left Landscape Right"
40 path: "Panels/Kiwi Room/kiwi_6"
41 clue: ""
42 answer: "keys"
43 symbols: QUESTION
44}
45panels {
46 name: "Left Landscape Bottom"
47 path: "Panels/Kiwi Room/kiwi_7"
48 clue: ""
49 answer: "write"
50 symbols: QUESTION
51}
52panels {
53 name: "Left Landscape Left"
54 path: "Panels/Kiwi Room/kiwi_8"
55 clue: ""
56 answer: "words"
57 symbols: QUESTION
58}
59panels {
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}
67panels {
68 name: "Right Landscape Right"
69 path: "Panels/Kiwi Room/kiwi_10"
70 clue: ""
71 answer: "lies"
72 symbols: QUESTION
73}
74panels {
75 name: "Right Landscape Bottom"
76 path: "Panels/Kiwi Room/kiwi_11"
77 clue: ""
78 answer: "the"
79 symbols: QUESTION
80}
81panels {
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}
64ports { 64ports {
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}
69ports { 71ports {
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}
74ports { 78ports {
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 @@
1name: "Whole Room" 1name: "Whole Room"
2panel_display_name: "North Landscape" 2panel_display_name: "Building Building"
3panels { 3panels {
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.
3ports { 3ports {
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}
8ports { 10ports {
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}
273ports { 273ports {
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}
278ports { 280ports {
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}
283ports { 287ports {
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}
288ports { 294ports {
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}
32doors { 32doors {
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}
41doors {
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}
48doors {
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}
23ports { 23ports {
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}
10ports { 10ports {
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}
10ports { 10ports {
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}
9ports { 9ports {
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}
6ports { 6ports {
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}
34doors {
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 @@
1display_name: "The Jubilant" 1display_name: "The Jubilant"
2worldport_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}
98ports { 98ports {
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 @@
1display_name: "The Keen" 1display_name: "The Keen"
2worldport_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}
70ports { 70ports {
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 @@
1display_name: "The Liberated" 1display_name: "The Liberated"
2worldport_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}
58ports { 58ports {
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 @@
1display_name: "The Linear" 1display_name: "The Linear"
2worldport_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}
58ports { 58ports {
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 @@
1display_name: "The Lionized" 1display_name: "The Lionized"
2worldport_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}
58ports { 58ports {
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 @@
1display_name: "The Literate" 1display_name: "The Literate"
2worldport_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}
58ports { 58ports {
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 @@
1display_name: "The Lively" 1display_name: "The Lively"
2worldport_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}
58ports { 58ports {
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 @@
1doors { 1doors {
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}
13doors { 13doors {
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}
55doors {
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 @@
1display_name: "The Nuanced" 1display_name: "The Nuanced"
2worldport_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}
107ports { 107ports {
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}
112keyholders { 114keyholders {
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}
11connections { 11connections {
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}
16connections { 16connections {
17 from_room: "Middle Room"
18 to_room: "B Room"
19 oneway: true
20}
21connections {
22 from_room: "Middle Room"
23 to_room: "Main Area"
24 oneway: true
25}
26connections {
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?
14ports {
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}
23ports { 12ports {
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}
86ports { 86ports {
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 @@
1name: "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.
6ports {
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/doors.txtpb b/data/maps/the_owl/doors.txtpb index 5ec34c6..2d1c851 100644 --- a/data/maps/the_owl/doors.txtpb +++ b/data/maps/the_owl/doors.txtpb
@@ -1,13 +1,15 @@
1doors { 1doors {
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}
8doors { 9doors {
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}
16doors { 18doors {
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}
24doors { 27doors {
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}
31doors { 35doors {
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}
38doors { 43doors {
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 {
59doors { 65doors {
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}
65doors { 72doors {
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}
93doors { 101doors {
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}
121doors { 130doors {
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}
149doors { 159doors {
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}
177doors { 188doors {
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}
205doors { 217doors {
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"
@@ -235,7 +248,7 @@ doors {
235 type: EVENT 248 type: EVENT
236 #receivers: "Panels/Colors/owl_2/animationListener2" 249 #receivers: "Panels/Colors/owl_2/animationListener2"
237 panels { room: "Connected Area" name: "RANGE" } 250 panels { room: "Connected Area" name: "RANGE" }
238 panels { room: "R2C3 Bottom" name: "BLACK" } 251 panels { room: "Connected Area" name: "WHITE" }
239 panels { room: "Blue Room" name: "SKY" } 252 panels { room: "Blue Room" name: "SKY" }
240} 253}
241doors { 254doors {
@@ -250,3 +263,95 @@ doors {
250 panels { room: "Connected Area" name: "WHITE" } 263 panels { room: "Connected Area" name: "WHITE" }
251 panels { room: "Blue Room" name: "SKY" } 264 panels { room: "Blue Room" name: "SKY" }
252} 265}
266doors {
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}
275doors {
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}
284doors {
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}
291doors {
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}
299doors {
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}
309doors {
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}
319doors {
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}
328doors {
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}
336doors {
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}
345doors {
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}
352doors {
353 name: "Double Letters"
354 type: EVENT
355 receivers: "Panels/Warps/magenta/visibilityListener"
356 double_letters: true
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}
30panels { 31panels {
31 name: "WHITE" 32 name: "WHITE"
@@ -149,7 +150,9 @@ paintings {
149} 150}
150ports { 151ports {
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 @@
1name: "Magenta Hallway" 1name: "Magenta Hallway"
2ports { 2ports {
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}
9ports { 9ports {
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}
11connections { 12connections {
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 1161917..05d2e63 100644 --- a/data/maps/the_parthenon/doors.txtpb +++ b/data/maps/the_parthenon/doors.txtpb
@@ -43,3 +43,12 @@ doors {
43 panels { room: "Main Area" name: "ALEXANDER" answer: "alexander" } 43 panels { room: "Main Area" name: "ALEXANDER" answer: "alexander" }
44 panels { room: "Main Area" name: "CAESAR" answer: "caesar" } 44 panels { room: "Main Area" name: "CAESAR" answer: "caesar" }
45} 45}
46doors {
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}
56ports { 56ports {
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}
61ports { 63ports {
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}
66ports { 70ports {
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 {
43doors { 43doors {
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}
51doors { 50doors {
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}
9ports { 9ports {
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}
100ports { 100ports {
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}
105keyholders { 107keyholders {
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 @@
1display_name: "The Perceptive" 1display_name: "The Perceptive"
2worldport_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 @@
1name: "Main Area" 1name: "Main Area"
2ports { 2ports {
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 {
21connections { 21connections {
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}
26connections { 26connections {
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}
31connections { 31connections {
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}
36connections { 36connections {
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}
41connections { 41connections {
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}
33doors { 33doors {
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}
46doors { 46doors {
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}
57doors { 57doors {
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}
68doors { 68doors {
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}
79doors { 79doors {
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}
106doors { 106doors {
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}
132doors { 132doors {
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}
143doors { 143doors {
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}
213doors {
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}
220doors {
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}
227doors {
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}
234doors {
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}
37ports { 37ports {
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}
42ports { 44ports {
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}
47ports { 51ports {
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}
9ports { 9ports {
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}
9ports { 9ports {
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}
9ports { 9ports {
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 @@
1display_name: "The Quiet" 1display_name: "The Quiet"
2worldport_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}
98ports { 98ports {
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 @@
1doors { 1doors {
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}
9doors {
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}
19doors {
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}
27doors {
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}
12doors { 39doors {
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}
12connections { 11connections {
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}
17connections { 17connections {
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}
21doors { 21doors {
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}
28doors { 29doors {
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}
37doors {
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}
206doors {
207 name: "Anti-Collectable"
208 type: LOCATION_ONLY
209 senders: "Components/Collectables/anticollectable"
210 location_room: "Anti Room"
211}
212doors {
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}
231doors {
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 @@
1display_name: "The Repetitive" 1display_name: "The Repetitive"
2# The anti-collectable doesn't fit into our system right now so let's ignore it.
3excluded_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.
5excluded_nodes: "Meshes/eyeRed3" 3excluded_nodes: "Meshes/eyeRed3"
6excluded_nodes: "Meshes/eyeRed4" 4excluded_nodes: "Meshes/eyeRed4"
7# I do not know what this is.
8excluded_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.
10excluded_nodes: "Panels/Eval/panel_26_proxyied_fake" 6excluded_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 @@
1name: "Anti Room" 1name: "Anti Room"
2# Ignore the collectible. The mod should remove it and the back wall too.
3panels { 2panels {
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}
40panels { 39panels {
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}
46panels {
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 @@
1name: "Entry Connector" 1name: "Entry Connector"
2ports { 2ports {
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}
139ports { 139ports {
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 @@
1name: "Plaza Connector" 1name: "Plaza Connector"
2ports { 2ports {
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}
9ports { 9ports {
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 {
33doors { 33doors {
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}
156ports { 156ports {
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}
160keyholders { 163keyholders {
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 @@
1display_name: "The Sirenic" 1display_name: "The Sirenic"
2worldport_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}
16ports { 16ports {
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 @@
1connections {
2 from_room: "Starting Room"
3 to_room: "Connected Area"
4 door { name: "Entrance" }
5}
6connections {
7 from_room: "Connected Area"
8 to_room: "Mastery"
9 door { name: "Mastery Door" }
10}
11connections {
12 from_room: "Connected Area"
13 to_room: "Hi Room"
14 door { name: "Hi Room Front Door" }
15}
16connections {
17 from_room: "Connected Area"
18 to_room: "Hi Room"
19 door { name: "Hi Room Back Door" }
20}
21connections {
22 from_room: "Green Area"
23 to_room: "Connected Area"
24 door { name: "Green Area Door" }
25}
26connections {
27 from_room: "Connected Area"
28 to_room: "Old Crossroads"
29 door { name: "Crossroads Shortcut" }
30}
31connections {
32 from_room: "Connected Area"
33 to_room: "Old Crossroads"
34 oneway: true
35}
36connections {
37 from_room: "Old Crossroads"
38 to_room: "Green Area"
39 oneway: true
40}
41connections {
42 from_room: "Connected Area"
43 to_room: "Red Panel"
44 door { name: "Red Panel" }
45}
46connections {
47 from_room: "Connected Area"
48 to_room: "Orange Panel"
49 door { name: "Orange Panel" }
50}
51connections {
52 from_room: "Connected Area"
53 to_room: "Yellow Panel"
54 door { name: "Yellow Panel" }
55}
56connections {
57 from_room: "Green Area"
58 to_room: "Green Panel"
59 door { name: "Green Panel" }
60}
61connections {
62 from_room: "Connected Area"
63 to_room: "Blue Panel"
64 door { name: "Blue Panel" }
65}
66connections {
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
11doors {
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}
19doors {
20 name: "Mastery Door"
21 type: EVENT
22 receivers: "Components/Doors/entry_18"
23 panels { room: "Purple Panel" name: "PURPLE" }
24}
25doors {
26 name: "Hi Room Front Door"
27 type: EVENT
28 receivers: "Components/Doors/entry_21"
29 panels { room: "Connected Area" name: "HI" }
30}
31doors {
32 name: "Hi Room Back Door"
33 type: EVENT
34 receivers: "Components/Doors/entry_20"
35 panels { room: "Hi Room" name: "HI" }
36}
37doors {
38 name: "Green Area Door"
39 type: EVENT
40 receivers: "Components/Doors/entry_15"
41 panels { room: "Green Area" name: "STRAYS" }
42}
43doors {
44 name: "Crossroads Shortcut"
45 type: EVENT
46 receivers: "Components/Doors/entry_14"
47 panels { room: "Old Crossroads" name: "DOORWAY" }
48}
49doors {
50 name: "Red Panel"
51 type: EVENT
52 receivers: "Components/Doors/entry_8"
53 panels { room: "Connected Area" name: "START" }
54}
55doors {
56 name: "Orange Panel"
57 type: EVENT
58 receivers: "Components/Doors/entry_19"
59 panels { room: "Red Panel" name: "RED" }
60}
61doors {
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}
69doors {
70 name: "Green Panel"
71 type: EVENT
72 receivers: "Components/Doors/entry_16"
73 panels { room: "Yellow Panel" name: "YELLOW" }
74}
75doors {
76 name: "Blue Panel"
77 type: EVENT
78 receivers: "Components/Doors/entry_9"
79 panels { room: "Green Panel" name: "GREEN" }
80}
81doors {
82 name: "Purple Panel"
83 type: EVENT
84 receivers: "Components/Doors/entry_13"
85 panels { room: "Blue Panel" name: "BLUE" }
86}
87doors {
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}
96doors {
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 @@
1display_name: "The Stellar"
2type: GIFT_MAP
3# This panel does not appear to be accessible without sniping.
4excluded_nodes: "Panels/Room_1/panel_2"
5# The map's mastery is created at runtime.
6custom_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 @@
1name: "Blue Panel"
2panels {
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 @@
1name: "Connected Area"
2panels {
3 name: "HERE"
4 path: "Panels/Room_1/panel_3"
5 clue: "here"
6 answer: "where"
7 symbols: SPARKLES
8}
9panels {
10 name: "QUESTION (1)"
11 path: "Panels/Room_1/panel_4"
12 clue: "question"
13 answer: "what"
14 symbols: EXAMPLE
15}
16panels {
17 name: "QUESTION (2)"
18 path: "Panels/Room_1/panel_5"
19 clue: "question"
20 answer: "how"
21 symbols: EXAMPLE
22}
23panels {
24 name: "HI"
25 path: "Panels/Room_1/panel_12"
26 clue: "hi"
27 answer: "hi"
28 symbols: QUESTION
29}
30panels {
31 name: "WHERE"
32 path: "Panels/Room_1/panel_6"
33 clue: "where"
34 answer: "there"
35 symbols: SPARKLES
36}
37panels {
38 name: "GREETINGS"
39 path: "Panels/Room_1/panel_7"
40 clue: "greetings"
41 answer: "welcome"
42 symbols: SUN
43}
44panels {
45 name: "BEHIND"
46 path: "Panels/Room_1/panel_8"
47 clue: "behind"
48 answer: "back"
49 symbols: SUN
50}
51panels {
52 name: "Blank"
53 path: "Panels/Room_1/panel_9"
54 clue: ""
55 answer: "behind"
56}
57panels {
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 @@
1name: "Green Area"
2panels {
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 @@
1name: "Green Panel"
2panels {
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 @@
1name: "Hi Room"
2panels {
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 @@
1name: "Mastery"
2masteries {
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 @@
1name: "Old Crossroads"
2panels {
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 @@
1name: "Orange Panel"
2panels {
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 @@
1name: "Purple Panel"
2panels {
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 @@
1name: "Red Panel"
2panels {
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 @@
1name: "Starting Room"
2panels {
3 name: "STARLIKE"
4 path: "Panels/Room_1/panel_1"
5 clue: "starlike"
6 answer: "stellar"
7 symbols: SUN
8}
9ports {
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 @@
1name: "Yellow Panel"
2panels {
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}
32ports { 32ports {
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}
6connections {
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}
13doors {
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 @@
1display_name: "The Sturdy" 1display_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.
7custom_nodes: "Components/Doors/Rainbow/Hinge/rainbow"
8custom_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 @@
1name: "Hidden Rainbow"
2paintings {
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}
106ports { 106ports {
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}
110ports { 113ports {
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}
14paintings { 14paintings {
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}
11connections { 12connections {
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 @@
1display_name: "The Sun Temple" 1display_name: "The Sun Temple"
2worldport_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}
10ports { 10ports {
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}
201ports { 201ports {
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}
205ports { 208ports {
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 @@
1display_name: "The Symbolic" 1display_name: "The Symbolic"
2worldport_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}
8ports { 8ports {
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}
55doors {
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 @@
1display_name: "The Talented" 1display_name: "The Talented"
2worldport_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}
112ports { 112ports {
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 {
6doors { 6doors {
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}
11doors { 13doors {
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}
9ports { 9ports {
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}
30ports { 30ports {
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}
34ports { 37ports {
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}
30ports { 30ports {
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}
34ports { 37ports {
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}
16ports { 16ports {
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}
16ports { 16ports {
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}
28ports { 28ports {
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}
32ports { 35ports {
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 @@
1display_name: "The Tower" 1display_name: "The Tower"
2worldport_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}
98ports { 98ports {
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 @@
1doors { 1doors {
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 @@
1name: "Bearer Entrance" 1name: "Bearer Entrance"
2ports { 2ports {
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}
212ports { 212ports {
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}
216ports { 220ports {
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}
220ports { 228ports {
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}
224ports { 235ports {
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}
228paintings { 242paintings {
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 @@
1connections { 1connections {
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}
6connections { 6connections {
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 @@
1doors { 1doors {
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 {
48doors { 48doors {
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}
53doors { 55doors {
@@ -66,9 +68,9 @@ doors {
66doors { 68doors {
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}
187doors {
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}
197doors {
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}
9ports { 9ports {
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 @@
1name: "Daedalus Entrance" 1name: "Daedalus Entrance"
2ports { 2ports {
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}
217ports { 217ports {
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}
221ports { 224ports {
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}
225ports { 231ports {
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}
507doors {
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}
518doors {
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 @@
1name: "Bearer Entrance" 1name: "Bearer Entrance"
2ports { 2ports {
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}
9ports { 9ports {
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 @@
1name: "Nuanced Entrance" 1name: "Nuanced Entrance"
2ports { 2ports {
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}
16ports { 16ports {
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 @@
1display_name: "The Wondrous" 1display_name: "The Wondrous"
2worldport_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}
8ports { 8ports {
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}
58ports { 58ports {
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 @@
1version {
2 major: 8
3 minor: 0
4 patch: 1
5}
6# Filler item.
7special_names: "A Job Well Done"
8# Symbol items.
9special_names: "Age Symbol"
10special_names: "Anagram Symbol"
11special_names: "Boxes Symbol"
12special_names: "Cross Symbol"
13special_names: "Eval Symbol"
14special_names: "Example Symbol"
15special_names: "Gender Symbol"
16special_names: "Job Symbol"
17special_names: "Lingo Symbol"
18special_names: "Null Symbol"
19special_names: "Planet Symbol"
20special_names: "Pyramid Symbol"
21special_names: "Question Symbol"
22special_names: "Sound Symbol"
23special_names: "Sparkles Symbol"
24special_names: "Stars Symbol"
25special_names: "Sun Symbol"
26special_names: "Sweet Symbol"
27special_names: "Zero Symbol"
28# Anti collectable traps
29special_names: "Anti A"
30special_names: "Anti B"
31special_names: "Anti C"
32special_names: "Anti D"
33special_names: "Anti E"
34special_names: "Anti F"
35special_names: "Anti G"
36special_names: "Anti H"
37special_names: "Anti I"
38special_names: "Anti J"
39special_names: "Anti K"
40special_names: "Anti L"
41special_names: "Anti M"
42special_names: "Anti N"
43special_names: "Anti O"
44special_names: "Anti P"
45special_names: "Anti Q"
46special_names: "Anti R"
47special_names: "Anti S"
48special_names: "Anti T"
49special_names: "Anti U"
50special_names: "Anti V"
51special_names: "Anti W"
52special_names: "Anti X"
53special_names: "Anti Y"
54special_names: "Anti Z"
55# Numbers for The Fuzzy
56special_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}
12progressives {
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 014cbeb..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
32enum DoorGroupType { 35enum DoorGroupType {
@@ -49,6 +52,13 @@ enum DoorGroupType {
49 SHUFFLE_GROUP = 4; 52 SHUFFLE_GROUP = 4;
50} 53}
51 54
55enum MapType {
56 NORMAL_MAP = 0;
57 ICARUS = 1;
58 GIFT_MAP = 2;
59 DEMO = 3;
60}
61
52enum AxisDirection { 62enum AxisDirection {
53 AXIS_DIRECTION_UNKNOWN = 0; 63 AXIS_DIRECTION_UNKNOWN = 0;
54 64
@@ -84,6 +94,18 @@ enum PuzzleSymbol {
84 QUESTION = 19; 94 QUESTION = 19;
85} 95}
86 96
97message Vec3d {
98 optional double x = 1;
99 optional double y = 2;
100 optional double z = 3;
101}
102
103message VersionNumber {
104 optional uint64 major = 1;
105 optional uint64 minor = 2;
106 optional uint64 patch = 3;
107}
108
87message ProxyIdentifier { 109message ProxyIdentifier {
88 optional uint64 panel = 1; 110 optional uint64 panel = 1;
89 optional string answer = 2; 111 optional string answer = 2;
@@ -106,6 +128,9 @@ message Connection {
106 } 128 }
107 129
108 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;
109} 134}
110 135
111message Door { 136message Door {
@@ -125,10 +150,13 @@ message Door {
125 repeated KeyholderAnswer keyholders = 13; 150 repeated KeyholderAnswer keyholders = 13;
126 repeated uint64 rooms = 14; 151 repeated uint64 rooms = 14;
127 repeated uint64 doors = 15; 152 repeated uint64 doors = 15;
128 repeated uint64 endings = 16; 153 optional bool white_ending = 16;
129 optional bool double_letters = 18; 154 optional bool double_letters = 18;
155 repeated string senders = 19;
130 156
131 optional DoorType type = 8; 157 optional DoorType type = 8;
158 optional bool latch = 20;
159 optional bool legacy_location = 21;
132 160
133 optional string location_name = 17; 161 optional string location_name = 17;
134} 162}
@@ -171,12 +199,16 @@ message PaintingData {
171 199
172message Port { 200message Port {
173 optional uint64 id = 1; 201 optional uint64 id = 1;
202 optional uint64 ap_id = 11;
174 optional uint64 room_id = 2; 203 optional uint64 room_id = 2;
175 optional string name = 3; 204 optional string name = 3;
176 205
206 optional string display_name = 10;
177 optional string path = 4; 207 optional string path = 4;
178 optional string orientation = 5; 208 optional Vec3d destination = 5;
209 optional double rotation = 8;
179 optional AxisDirection gravity = 7; 210 optional AxisDirection gravity = 7;
211 optional bool no_shuffle = 9;
180 212
181 optional uint64 required_door = 6; 213 optional uint64 required_door = 6;
182} 214}
@@ -242,6 +274,8 @@ message Map {
242 optional uint64 id = 1; 274 optional uint64 id = 1;
243 optional string name = 2; 275 optional string name = 2;
244 optional string display_name = 3; 276 optional string display_name = 3;
277 optional uint64 worldport_entrance = 4;
278 optional MapType type = 5;
245} 279}
246 280
247message Progressive { 281message Progressive {
@@ -260,6 +294,8 @@ message DoorGroup {
260} 294}
261 295
262message AllObjects { 296message AllObjects {
297 optional VersionNumber version = 15;
298
263 repeated Map maps = 7; 299 repeated Map maps = 7;
264 repeated Room rooms = 1; 300 repeated Room rooms = 1;
265 repeated Door doors = 2; 301 repeated Door doors = 2;
diff --git a/proto/human.proto b/proto/human.proto index d48f687..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
71message HumanConnections { 86message HumanConnections {
@@ -89,12 +104,26 @@ message HumanDoor {
89 repeated KeyholderIdentifier keyholders = 10; 104 repeated KeyholderIdentifier keyholders = 10;
90 repeated RoomIdentifier rooms = 11; 105 repeated RoomIdentifier rooms = 11;
91 repeated DoorIdentifier doors = 12; 106 repeated DoorIdentifier doors = 12;
92 repeated string endings = 13; 107 optional bool white_ending = 13;
93 optional bool double_letters = 15; 108 optional bool double_letters = 15;
94 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
95 optional DoorType type = 4; 114 optional DoorType type = 4;
96 optional string location_room = 5; 115 optional string location_room = 5;
97 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;
98} 127}
99 128
100message HumanDoors { 129message HumanDoors {
@@ -134,9 +163,16 @@ message HumanPainting {
134 163
135message HumanPort { 164message HumanPort {
136 optional string name = 1; 165 optional string name = 1;
166 optional string display_name = 8;
137 optional string path = 2; 167 optional string path = 2;
138 168
139 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;
140 optional AxisDirection gravity = 5 [default = Y_MINUS]; 176 optional AxisDirection gravity = 5 [default = Y_MINUS];
141 177
142 optional DoorIdentifier required_door = 4; 178 optional DoorIdentifier required_door = 4;
@@ -190,7 +226,18 @@ message HumanRoom {
190 226
191message HumanMap { 227message HumanMap {
192 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.
193 repeated string excluded_nodes = 2; 239 repeated string excluded_nodes = 2;
240 repeated string custom_nodes = 5;
194} 241}
195 242
196message HumanProgressive { 243message HumanProgressive {
@@ -212,11 +259,17 @@ message HumanDoorGroups {
212 repeated HumanDoorGroup door_groups = 1; 259 repeated HumanDoorGroup door_groups = 1;
213} 260}
214 261
262message HumanGlobalMetadata {
263 repeated string special_names = 1;
264 optional VersionNumber version = 2;
265}
266
215message IdMappings { 267message IdMappings {
216 message RoomIds { 268 message RoomIds {
217 map<string, uint64> panels = 1; 269 map<string, uint64> panels = 1;
218 map<string, uint64> masteries = 2; 270 map<string, uint64> masteries = 2;
219 map<string, uint64> keyholders = 3; 271 map<string, uint64> keyholders = 3;
272 map<string, uint64> ports = 4;
220 } 273 }
221 274
222 message MapIds { 275 message MapIds {
diff --git a/tools/assign_ids/main.cpp b/tools/assign_ids/main.cpp index ee55338..4cf7c3f 100644 --- a/tools/assign_ids/main.cpp +++ b/tools/assign_ids/main.cpp
@@ -44,6 +44,7 @@ class AssignIds {
44 ProcessSpecialIds(); 44 ProcessSpecialIds();
45 ProcessProgressivesFile(datadir_path / "progressives.txtpb"); 45 ProcessProgressivesFile(datadir_path / "progressives.txtpb");
46 ProcessDoorGroupsFile(datadir_path / "door_groups.txtpb"); 46 ProcessDoorGroupsFile(datadir_path / "door_groups.txtpb");
47 ProcessGlobalMetadataFile(datadir_path / "metadata.txtpb");
47 48
48 WriteIds(ids_path); 49 WriteIds(ids_path);
49 50
@@ -64,6 +65,7 @@ class AssignIds {
64 UpdateNextId(room.panels()); 65 UpdateNextId(room.panels());
65 UpdateNextId(room.masteries()); 66 UpdateNextId(room.masteries());
66 UpdateNextId(room.keyholders()); 67 UpdateNextId(room.keyholders());
68 UpdateNextId(room.ports());
67 } 69 }
68 } 70 }
69 71
@@ -110,7 +112,8 @@ class AssignIds {
110 112
111 void ProcessDoor(const HumanDoor& h_door, 113 void ProcessDoor(const HumanDoor& h_door,
112 const std::string& current_map_name) { 114 const std::string& current_map_name) {
113 if (h_door.type() == DoorType::EVENT) { 115 if (h_door.type() == DoorType::EVENT && !h_door.latch() &&
116 !h_door.legacy_location()) {
114 return; 117 return;
115 } 118 }
116 119
@@ -244,6 +247,37 @@ class AssignIds {
244 .at(h_keyholder.name()); 247 .at(h_keyholder.name());
245 } 248 }
246 } 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 }
247 } 281 }
248 282
249 void ProcessSpecialIds() { 283 void ProcessSpecialIds() {
@@ -288,6 +322,23 @@ class AssignIds {
288 } 322 }
289 } 323 }
290 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
291 private: 342 private:
292 void UpdateNextId(const google::protobuf::Map<std::string, uint64_t>& ids) { 343 void UpdateNextId(const google::protobuf::Map<std::string, uint64_t>& ids) {
293 for (const auto& [_, id] : ids) { 344 for (const auto& [_, id] : ids) {
diff --git a/tools/datapacker/main.cpp b/tools/datapacker/main.cpp index c640de6..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.
@@ -344,6 +360,9 @@ class DataPacker {
344 std::copy( 360 std::copy(
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()));
363 std::copy(
364 h_door.senders().begin(), h_door.senders().end(),
365 google::protobuf::RepeatedFieldBackInserter(door.mutable_senders()));
347 366
348 for (const PaintingIdentifier& pi : h_door.move_paintings()) { 367 for (const PaintingIdentifier& pi : h_door.move_paintings()) {
349 std::optional<std::string> map_name = 368 std::optional<std::string> map_name =
@@ -392,8 +411,8 @@ class DataPacker {
392 container_.FindOrAddDoor(map_name, di.name(), current_map_name)); 411 container_.FindOrAddDoor(map_name, di.name(), current_map_name));
393 } 412 }
394 413
395 for (const std::string& ending_name : h_door.endings()) { 414 if (h_door.has_white_ending()) {
396 door.add_endings(container_.FindOrAddEnding(ending_name)); 415 door.set_white_ending(h_door.white_ending());
397 } 416 }
398 417
399 if (h_door.has_control_center_color()) { 418 if (h_door.has_control_center_color()) {
@@ -413,6 +432,14 @@ class DataPacker {
413 if (h_door.has_double_letters()) { 432 if (h_door.has_double_letters()) {
414 door.set_double_letters(h_door.double_letters()); 433 door.set_double_letters(h_door.double_letters());
415 } 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 }
416 } 443 }
417 444
418 void ProcessConnectionsFile(std::filesystem::path path, 445 void ProcessConnectionsFile(std::filesystem::path path,
@@ -469,6 +496,21 @@ class DataPacker {
469 r_connection.set_roof_access(human_connection.roof_access()); 496 r_connection.set_roof_access(human_connection.roof_access());
470 } 497 }
471 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
472 container_.AddConnection(f_connection); 514 container_.AddConnection(f_connection);
473 if (!human_connection.oneway()) { 515 if (!human_connection.oneway()) {
474 container_.AddConnection(r_connection); 516 container_.AddConnection(r_connection);
@@ -600,6 +642,15 @@ class DataPacker {
600 } 642 }
601 } 643 }
602 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
603 void ProcessIdsFile(std::filesystem::path path) { 654 void ProcessIdsFile(std::filesystem::path path) {
604 auto ids = ReadIdsFromYaml(path.string()); 655 auto ids = ReadIdsFromYaml(path.string());
605 656
@@ -632,6 +683,12 @@ class DataPacker {
632 .mutable_keyholders(keyholder_id) 683 .mutable_keyholders(keyholder_id)
633 ->set_ap_id(ap_id); 684 ->set_ap_id(ap_id);
634 } 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 }
635 } 692 }
636 } 693 }
637 694
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
28struct MapInfo { 28struct 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
32struct RoomInfo { 34struct RoomInfo {
@@ -54,14 +56,18 @@ struct DoorInfo {
54 56
55struct PortInfo { 57struct 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
61struct PaintingInfo { 66struct 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 {
101struct EndingInfo { 108struct 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
108struct PanelNameInfo { 113struct 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
4namespace com::fourisland::lingo2_archipelago { 4namespace com::fourisland::lingo2_archipelago {