about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md495
-rw-r--r--README.md220
-rw-r--r--apworld/CHANGELOG.md54
-rw-r--r--apworld/README.md48
-rw-r--r--apworld/__init__.py105
-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.gd (renamed from client/Archipelago/compass.gd)0
-rw-r--r--apworld/client/compass_overlay.gd (renamed from client/Archipelago/compass_overlay.gd)0
-rw-r--r--apworld/client/door.gd75
-rw-r--r--apworld/client/effects.gd32
-rw-r--r--apworld/client/gamedata.gd310
-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)2
-rw-r--r--apworld/client/keyboard.gd (renamed from client/Archipelago/keyboard.gd)39
-rw-r--r--apworld/client/locationListener.gd (renamed from client/Archipelago/locationListener.gd)0
-rw-r--r--apworld/client/main.gd314
-rw-r--r--apworld/client/manager.gd773
-rw-r--r--apworld/client/maps/control_center.gd139
-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_gallery.gd7
-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)3
-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.gd (renamed from client/Archipelago/door.gd)15
-rw-r--r--apworld/client/panel.gd (renamed from client/Archipelago/panel.gd)4
-rw-r--r--apworld/client/pauseMenu.gd91
-rw-r--r--apworld/client/player.gd219
-rw-r--r--apworld/client/rainbowText.gd10
-rw-r--r--apworld/client/rteMenu.gd67
-rw-r--r--apworld/client/run_from_apworld.tscn30
-rw-r--r--apworld/client/run_from_source.tscn22
-rw-r--r--apworld/client/saver.gd (renamed from client/Archipelago/saver.gd)0
-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/teleport.gd)0
-rw-r--r--apworld/client/teleportListener.gd (renamed from client/Archipelago/teleportListener.gd)0
-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.gd (renamed from client/Archipelago/visibilityListener.gd)0
-rw-r--r--apworld/client/worldport.gd61
-rw-r--r--apworld/client/worldportListener.gd (renamed from client/Archipelago/worldportListener.gd)2
-rw-r--r--apworld/context.py800
-rw-r--r--apworld/items.py5
-rw-r--r--apworld/locations.py109
-rw-r--r--apworld/logo.pngbin0 -> 9429 bytes
-rw-r--r--apworld/options.py135
-rw-r--r--apworld/player_logic.py410
-rw-r--r--apworld/regions.py189
-rw-r--r--apworld/requirements.txt2
-rw-r--r--apworld/rules.py238
-rw-r--r--apworld/static_logic.py35
-rw-r--r--apworld/tracker.py151
-rw-r--r--apworld/version.py1
-rw-r--r--client/Archipelago/client.gd417
-rw-r--r--client/Archipelago/gamedata.gd140
-rw-r--r--client/Archipelago/manager.gd544
-rw-r--r--client/Archipelago/pauseMenu.gd44
-rw-r--r--client/Archipelago/player.gd362
-rw-r--r--client/Archipelago/settings_buttons.gd24
-rw-r--r--client/Archipelago/settings_screen.gd252
-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/worldport.gd10
-rw-r--r--client/CHANGELOG.md59
-rw-r--r--client/README.md90
-rw-r--r--client/archipelago.tscn284
-rw-r--r--data/MISSING PANELS.txt32
-rw-r--r--data/connections.txtpb285
-rw-r--r--data/door_groups.txtpb32
-rw-r--r--data/ids.yaml932
-rw-r--r--data/maps/control_center/connections.txtpb1
-rw-r--r--data/maps/control_center/doors.txtpb42
-rw-r--r--data/maps/control_center/metadata.txtpb1
-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/doors.txtpb188
-rw-r--r--data/maps/daedalus/metadata.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/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.txtpb3
-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.txtpb8
-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/metadata.txtpb3
-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.txtpb7
-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.txtpb7
-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/metadata.txtpb3
-rw-r--r--data/maps/the_bearer/doors.txtpb16
-rw-r--r--data/maps/the_bearer/metadata.txtpb3
-rw-r--r--data/maps/the_bearer/rooms/Back Area.txtpb4
-rw-r--r--data/maps/the_bearer/rooms/Entry.txtpb4
-rw-r--r--data/maps/the_bearer/rooms/Tree Entrance.txtpb4
-rw-r--r--data/maps/the_between/metadata.txtpb3
-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/metadata.txtpb3
-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.txtpb7
-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/metadata.txtpb3
-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.txtpb7
-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.txtpb7
-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.txtpb14
-rw-r--r--data/maps/the_darkroom/doors.txtpb1
-rw-r--r--data/maps/the_darkroom/metadata.txtpb3
-rw-r--r--data/maps/the_darkroom/rooms/Congruent Entrance.txtpb4
-rw-r--r--data/maps/the_darkroom/rooms/Cyan Hallway.txtpb4
-rw-r--r--data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb4
-rw-r--r--data/maps/the_darkroom/rooms/First Room Exit.txtpb9
-rw-r--r--data/maps/the_darkroom/rooms/First Room.txtpb10
-rw-r--r--data/maps/the_darkroom/rooms/Second Room Exit.txtpb9
-rw-r--r--data/maps/the_darkroom/rooms/Second Room.txtpb10
-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/metadata.txtpb3
-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_door/metadata.txtpb1
-rw-r--r--data/maps/the_double_sided/doors.txtpb79
-rw-r--r--data/maps/the_double_sided/metadata.txtpb7
-rw-r--r--data/maps/the_double_sided/rooms/Start.txtpb4
-rw-r--r--data/maps/the_entry/connections.txtpb33
-rw-r--r--data/maps/the_entry/doors.txtpb103
-rw-r--r--data/maps/the_entry/metadata.txtpb12
-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/Four Rooms Entrance.txtpb6
-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.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Lime Room.txtpb6
-rw-r--r--data/maps/the_entry/rooms/Literate Entrance Panel.txtpb9
-rw-r--r--data/maps/the_entry/rooms/Literate Entrance.txtpb4
-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.txtpb4
-rw-r--r--data/maps/the_entry/rooms/Starting Room.txtpb19
-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/metadata.txtpb3
-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.txtpb7
-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.txtpb43
-rw-r--r--data/maps/the_gallery/metadata.txtpb2
-rw-r--r--data/maps/the_gallery/rooms/Daedalus Extension.txtpb1
-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_gold/metadata.txtpb2
-rw-r--r--data/maps/the_graveyard/doors.txtpb7
-rw-r--r--data/maps/the_graveyard/metadata.txtpb3
-rw-r--r--data/maps/the_great/connections.txtpb4
-rw-r--r--data/maps/the_great/doors.txtpb153
-rw-r--r--data/maps/the_great/metadata.txtpb3
-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.txtpb12
-rw-r--r--data/maps/the_great/rooms/Whole Room.txtpb2
-rw-r--r--data/maps/the_hinterlands/metadata.txtpb2
-rw-r--r--data/maps/the_hinterlands/rooms/Main Area.txtpb8
-rw-r--r--data/maps/the_hive/metadata.txtpb3
-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/metadata.txtpb3
-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/metadata.txtpb3
-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.txtpb7
-rw-r--r--data/maps/the_jubilant/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_keen/metadata.txtpb7
-rw-r--r--data/maps/the_keen/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_liberated/metadata.txtpb7
-rw-r--r--data/maps/the_liberated/rooms/Puzzle Room.txtpb4
-rw-r--r--data/maps/the_linear/metadata.txtpb7
-rw-r--r--data/maps/the_linear/rooms/Room.txtpb4
-rw-r--r--data/maps/the_lionized/metadata.txtpb7
-rw-r--r--data/maps/the_lionized/rooms/Puzzle Room.txtpb4
-rw-r--r--data/maps/the_literate/metadata.txtpb7
-rw-r--r--data/maps/the_literate/rooms/Puzzle Room.txtpb4
-rw-r--r--data/maps/the_lively/metadata.txtpb7
-rw-r--r--data/maps/the_lively/rooms/Puzzle Room.txtpb3
-rw-r--r--data/maps/the_nuanced/doors.txtpb7
-rw-r--r--data/maps/the_nuanced/metadata.txtpb7
-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/metadata.txtpb3
-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.txtpb129
-rw-r--r--data/maps/the_owl/metadata.txtpb3
-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/doors.txtpb9
-rw-r--r--data/maps/the_parthenon/metadata.txtpb3
-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/metadata.txtpb3
-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.txtpb7
-rw-r--r--data/maps/the_perceptive/rooms/Main Area.txtpb4
-rw-r--r--data/maps/the_plaza/doors.txtpb28
-rw-r--r--data/maps/the_plaza/metadata.txtpb1
-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.txtpb7
-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_relentless/metadata.txtpb1
-rw-r--r--data/maps/the_repetitive/connections.txtpb2
-rw-r--r--data/maps/the_repetitive/doors.txtpb43
-rw-r--r--data/maps/the_repetitive/metadata.txtpb3
-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/metadata.txtpb3
-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/metadata.txtpb3
-rw-r--r--data/maps/the_shop/rooms/Main Area.txtpb3
-rw-r--r--data/maps/the_sirenic/metadata.txtpb7
-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.txtpb9
-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/metadata.txtpb3
-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.txtpb11
-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/metadata.txtpb7
-rw-r--r--data/maps/the_sun_temple/rooms/Entrance.txtpb3
-rw-r--r--data/maps/the_sweet/metadata.txtpb3
-rw-r--r--data/maps/the_sweet/rooms/Main Area.txtpb6
-rw-r--r--data/maps/the_symbolic/doors.txtpb38
-rw-r--r--data/maps/the_symbolic/metadata.txtpb7
-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.txtpb7
-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/metadata.txtpb4
-rw-r--r--data/maps/the_tenacious/rooms/Control Center Entrance.txtpb3
-rw-r--r--data/maps/the_three_doors/metadata.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.txtpb7
-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/metadata.txtpb3
-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/doors.txtpb21
-rw-r--r--data/maps/the_unkempt/metadata.txtpb3
-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/metadata.txtpb3
-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_wise/metadata.txtpb3
-rw-r--r--data/maps/the_wondrous/metadata.txtpb7
-rw-r--r--data/maps/the_wondrous/rooms/Entry.txtpb3
-rw-r--r--data/maps/the_words/metadata.txtpb3
-rw-r--r--data/maps/the_words/rooms/Main Area.txtpb3
-rw-r--r--data/metadata.txtpb8
-rw-r--r--data/progressives.txtpb13
-rw-r--r--proto/data.proto54
-rw-r--r--proto/human.proto57
-rw-r--r--tools/assign_ids/main.cpp70
-rw-r--r--tools/datapacker/main.cpp83
-rw-r--r--tools/util/ids_yaml_format.cpp21
-rw-r--r--tools/validator/human_processor.cpp56
-rw-r--r--tools/validator/main.cpp4
-rw-r--r--tools/validator/structs.h10
-rw-r--r--tools/validator/validator.cpp88
-rw-r--r--tools/validator/validator.h2
449 files changed, 14026 insertions, 3246 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d6ca532 --- /dev/null +++ b/CHANGELOG.md
@@ -0,0 +1,495 @@
1# lingo2-archipelago Releases
2
3## v9.1.0 - 2026-02-08
4
5- Added "Custom Mint Ending" option. This creates a panel in the Control Center
6 after putting EXIT into the keyholders, the answer to which is the value of
7 the option. The player must solve the panel to spawn Mint Ending.
8- Added "Shuffle Music" option. This randomizes the music track that plays on
9 each map.
10- Added an item group for symbols.
11- Fix the message on the Control Center wall overcounting the number of endings
12 by 1.
13
14Compatibility notes:
15
16- This client should be completely compatible with worlds generated on v9.0.0.
17
18Download:
19[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v9.1.0/lingo2.apworld)<br/>
20Template YAML:
21[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v9.1.0/Lingo%202.yaml)<br/>
22Source: [v9.1.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v9.1.0)
23
24## v9.0.0 - 2026-02-07
25
26- Added "Shuffle Fast Travel" option. This allows you to randomize the
27 destinations of the fast travel buttons on the pause menu to almost any map
28 (except for The Entry, which is always the center button).
29- Added "Fast Travel Access" option. By default, fast travel is unlocked by
30 entering the destination area manually. You can instead set to have all fast
31 travel destinations unlocked from the start, or have them locked behind items
32 (apart from The Entry).
33- Added "Restrict Letter Placements" option. When enabled, letter items will
34 only be placed in your local world, in letter locations. This only has an
35 effect when Shuffle Letters is set to Item Cyan or Progressive. This is
36 experimental, and may slow down generation.
37- The values of the Endings Requirement and Masteries Requirement options are
38 now shown in the Control Center, as well as how many endings and masteries you
39 currently have.
40- Previously, vanilla doors logic expected you to have to solve every panel in
41 the Daedalus Computer Room in order to use the back exit door. This has been
42 fixed to only require solving one panel.
43- Previously, shuffled doors + shuffled worldports logic expected you to be able
44 to enter Control Center from the Perceptive Entrance without the door item by
45 solving the PART panel (as you would in vanilla doors). This has been fixed so
46 that the door item is required in both directions.
47
48Compatibility notes:
49
50- The change to the door in the Control Center required creating a new item with
51 a new ID, which is not present in worlds generated on older versions of the
52 apworld. Therefore, if you have an older world using door shuffle, you may not
53 be able to access the checks in the Perceptive Entrance or the worldport (if
54 worldports are shuffled).
55
56Download:
57[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v9.0.0/lingo2.apworld)<br/>
58Template YAML:
59[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v9.0.0/Lingo%202.yaml)<br/>
60Source: [v9.0.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v9.0.0)
61
62## v8.1.1 - 2026-02-05
63
64- Fixed issue in Daedalus Only mode where the Lavender Cubes and Rainbow Rooms
65 Entrance items would not work on vanilla doors.
66- Prevented the door to the main gallery area from opening in Daedalus Only mode
67 with vanilla doors.
68
69Compatibility notes:
70
71- This client should be completely compatible with worlds generated on v8.0.2 -
72 v8.1.0.
73- See the v8.0.2 release notes for additional compatibility notes regarding
74 earlier versions.
75
76Download:
77[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.1.1/lingo2.apworld)<br/>
78Template YAML:
79[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.1.1/Lingo%202.yaml)<br/>
80Source: [v8.1.1](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.1.1)
81
82## v8.1.0 - 2026-02-04
83
84- Added "Daedalus Only" mode. This is an experimental option that allows for a
85 more contained Lingo 2 experience. The only maps available will be Daedalus,
86 The Gold, The Tenacious, and the Daedalus extension in The Gallery. There are
87 a number of restrictions that come with using this option, which are detailed
88 in the option description.
89
90Compatibility notes:
91
92- This client should be completely compatible with worlds generated on v8.0.2 -
93 v8.0.5.
94- See the v8.0.2 release notes for additional compatibility notes regarding
95 earlier versions.
96
97Download:
98[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.1.0/lingo2.apworld)<br/>
99Template YAML:
100[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.1.0/Lingo%202.yaml)<br/>
101Source: [v8.1.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.1.0)
102
103## v8.0.5 - 2026-01-12
104
105- Fixed bug that prevented Yellow Ending door from opening when Cyan Door
106 Behavior is set to Item.
107
108Compatibility notes:
109
110- This client should be completely compatible with worlds generated on v8.0.2 -
111 v8.0.4.
112- See the v8.0.2 release notes for additional compatibility notes regarding
113 earlier versions.
114
115Download:
116[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.5/lingo2.apworld)<br/>
117Template YAML:
118[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.5/Lingo%202.yaml)<br/>
119Source: [v8.0.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.0.5)
120
121## v8.0.4 - 2026-01-05
122
123- Fixed worldport shuffle, which was broken in v8.0.3.
124- Previously, if you set your mastery requirement to the maximum available to
125 you, the game would expect you to complete all masteries, including ones not
126 available to you. This has been fixed.
127- Fixed an incorrect connection in Icarus. This mistake had no actual impact on
128 logic, but caused the Show Path tracker feature to show the wrong name of an
129 area.
130
131Compatibility notes:
132
133- This client should be completely compatible with worlds generated on v8.0.2
134 and v8.0.3.
135- See the v8.0.2 release notes for additional compatibility notes regarding
136 earlier versions.
137
138Download:
139[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.4/lingo2.apworld)<br/>
140Template YAML:
141[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.4/Lingo%202.yaml)<br/>
142Source: [v8.0.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.0.4)
143
144## v8.0.3 - 2025-12-18
145
146- Fixed the "Set changed during iteration" error some users were experiencing.
147- Fixed issue where generation would try to create anti-number traps when The
148 Fuzzy was enabled.
149- Using the key return now acts like interacting with a keyholder, and should
150 force-update your keyboard in case it gets out of sync.
151
152Compatibility notes:
153
154- This client should be completely compatible with worlds generated on v8.0.2.
155- See the v8.0.2 release notes for additional compatibility notes regarding
156 earlier versions.
157
158Download:
159[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.3/lingo2.apworld)<br/>
160Template YAML:
161[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.3/Lingo%202.yaml)<br/>
162Source: [v8.0.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.0.3)
163
164## v8.0.2 - 2025-11-11
165
166- Fixed a logic issue in The Symbolic where Poetry Room and Whirred Room were
167 not part of the requirements to access the mastery.
168- Fixed the issue where the Poetry Room location would be sent early.
169- The Daedalus Hedges Tower door is now latched (it will stay open after being
170 opened once).
171
172Compatibility notes:
173
174- This client should overall be compatible with worlds generated on v8.0.0 and
175 v8.0.1. The tracker will now correctly show when The Symbolic - Mastery is
176 possible to get in-game. However, this does not match the generator's logic in
177 earlier versions of the apworld, so you may end up in a position where you are
178 blocked because the mastery is impossible to get. Playing on another version
179 of the client will not fix this problem, nor is there an easy way to get the
180 mastery out-of-logic, and you may need to use server commands to send the
181 location early.
182- See the v8.0.1 release notes for additional compatibility notes regarding
183 earlier versions.
184
185Download:
186[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.2/lingo2.apworld)<br/>
187Template YAML:
188[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.2/Lingo%202.yaml)<br/>
189Source: [v8.0.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.0.2)
190
191## v8.0.1 - 2025-11-05
192
193- Fixed issue where The Unkempt - COLOR would disappear when it became logical
194 to solve it. It is now always present, and does not logically require cyan
195 doors or the orange control center door.
196- Fixed issue where you would be expected to solve The Owl - COLOR while it is
197 invisible. It is now considered a cyan door.
198
199Compatibility notes:
200
201- This client should overall be compatible with worlds generated on v8.0.0. The
202 tracker will show the new logic for The Unkempt - COLOR, which reflects when
203 it is solvable in game, but may place it in an earlier sphere than it was in
204 the original generation. Conversely, The Owl - COLOR is now more restricted in
205 the new logic, so you may end up in a situation where you are required to
206 solve it but the tracker does not realize it is in logic. However, the panel
207 is simply invisible when you don't have cyan doors, meaning it is easy to
208 solve it early if necessary.
209
210Download:
211[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.1/lingo2.apworld)<br/>
212Template YAML:
213[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.1/Lingo%202.yaml)<br/>
214Source: [v8.0.1](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.0.1)
215
216## v8.0.0 - 2025-11-02
217
218- ~50 new locations were added, such that almost every panel in the game is now
219 part of either a location or a connection. Some existing locations were also
220 modified or removed to make this cleaner. The only panels that are not part of
221 any location or connection are the ones in rooms that you are explicitly not
222 supposed to solve (e.g. the letter rooms in Daedalus where you are only
223 supposed to solve the panels indicated by the ceiling).
224- Multiworld state is now saved in a way that should increase compatibility when
225 playing on a client that is a different version than the apworld that
226 generated the world, going forward. Complete compatibility is not guaranteed,
227 but this fixes some of the glaring issues. Also note that this client is _not_
228 compatible with worlds generated on v7.x.x.
229- Hinted locations are now prioritized in the tracker, and are displayed in a
230 different color.
231- Locations can now be ignored in the tracker, which removes them from the
232 overlay and puts them at the bottom of the tab in the text client.
233- Fixed the Yellow Ending door not opening properly when gallery paintings are
234 shuffled.
235- The trigger for the gallery painting in The Unyielding is now smaller, so that
236 it matches the logical requirement.
237- The DIRECTION panels near the Castle in Daedalus are now moved when the roof
238 access stairs are present, so that you don't lose access to them.
239
240Download:
241[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.0/lingo2.apworld)<br/>
242Template YAML:
243[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v8.0.0/Lingo%202.yaml)<br/>
244Source: [v8.0.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v8.0.0)
245
246## v7.2.0 - 2025-10-25
247
248- Doors that rely on keyholders or the control center color panel are now
249 "latched". This means they will not close once they've been opened. Because of
250 this, the worldports near these doors are now eligible for randomization in
251 worldport shuffle.
252- Icarus is now optionally randomizable.
253- The requirements for accessing White Ending are now customizable. You can
254 choose to require a number of endings as well as a number of masteries.
255- The "Return To" trigger in The Plaza is now outside of the turtle.
256- Fixed a logic error regarding a couple of specific doors in vanilla doors
257 mode.
258- Fixed a bug where unlocks would not persist if you were playing with all
259 letters pre-unlocked and cyan doors on "any double letter".
260- Fixed a bug where the client would fail to connect properly when launched from
261 a URL if the player name had spaces in it.
262
263Download:
264[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.2.0/lingo2.apworld)<br/>
265Template YAML:
266[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.2.0/Lingo%202.yaml)<br/>
267Source: [v7.2.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.2.0)
268
269## v7.1.0 - 2025-10-07
270
271- Added a "Get Path" button to the locations tracker. This shows you the path
272 you're currently expected to be able to take in order to reach that
273 location/worldport/goal.
274- Worldport names in the spoiler log have been changed to be more descriptive.
275- Jumping into The Graveyard from The Sun Temple is now in logic.
276- Solving the FLIP panels above the Liberated and Literate entrances by looking
277 up is now in logic.
278- Renamed some locations so that they're shorter.
279- Fixed bug where White Ending would kick the player out of Archipelago.
280- Fixed bug where the minimap would be completely white when a texture pack is
281 enabled.
282- Generation failures while shuffling worldports should be significantly less
283 common.
284
285Download:
286[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.1.0/lingo2.apworld)<br/>
287Template YAML:
288[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.1.0/Lingo%202.yaml)<br/>
289Source: [v7.1.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.1.0)
290
291## v7.0.2 - 2025-10-03
292
293- Fixed issue connecting to password-protected slots.
294- Added instructions for using the client on Linux.
295
296Download:
297[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.2/lingo2.apworld)<br/>
298Template YAML:
299[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.2/Lingo%202.yaml)<br/>
300Source: [v7.0.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.0.2)
301
302## v7.0.1 - 2025-10-01
303
304- Fixed logic error regarding the Plaza Entrance in The Repetitive. Going from
305 The Plaza to The Repetitive does not require the door to be open in vanilla
306 doors, but both directions require the door item when doors are shuffled.
307- Fixed Worldports tracker tab getting messed up after disconnecting and
308 reconnecting to multiworld.
309- Improved error messages when failing to connect. The game now also shows you
310 when your connection has dropped.
311
312Download:
313[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.1/lingo2.apworld)<br/>
314Template YAML:
315[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.1/Lingo%202.yaml)<br/>
316Source: [v7.0.1](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.0.1)
317
318## v7.0.0 - 2025-09-30
319
320- Major update! First and foremost: the client and apworld are no longer
321 separate! There is only an apworld now, which you install into your
322 Archipelago custom worlds folder as per normal. In order to play a randomized
323 world, you open the Archipelago Launcher and click Lingo 2 Client. The first
324 time you do this, it will ask you for the location of your Lingo2.exe, which
325 you can find by right clicking on the game in Steam and clicking Browse Local
326 Files.
327- **Built-in tracker**: The in-game text client has a new tab called "Locations"
328 which lists the currently accessible locations similar to how Universal
329 Tracker does it. There is also an optional overlay you can enable in the
330 settings, which shows you some of your accessible locations on screen while
331 you're playing. If you're playing with vanilla letters, the tracker won't show
332 you locations that are solvable with letters until you collect the actual
333 letters, in order to help direct you better. More features will be coming to
334 the tracker in the future!
335- **Worldport shuffle**: The first part of entrance randomization is here!
336 Enabling this shuffles the destinations of the worldports, which are the
337 loading zones you walk into in order to change maps. Some restrictions apply,
338 which are noted in the option description. The tracker will show a list of
339 worldports you haven't entered yet, and will also not show you what lies
340 beyond a worldport until you've entered it. There is also a tab in the
341 textclient showing you the mapping between worldports you've already entered.
342- **Minimap**: There is an option in the settings to show a minimap in the
343 corner of the screen. This shows an overhead view of the map you're on, and
344 where you are in it. More features will be coming to the minimap in the
345 future!
346- Fixed the gate outside the Daedalus entrance in The Great not opening when
347 control center colors are shuffled.
348
349Download:
350[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.0/lingo2.apworld)<br/>
351Template YAML:
352[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v7.0.0/Lingo%202.yaml)<br/>
353Source: [v7.0.0](https://code.fourisland.com/lingo2-archipelago/tag/?h=v7.0.0)
354
355## Legacy Client v6.7 - 2025-09-19
356
357- Added a compass overlay. This makes it clearer which direction corresponds to
358 which compass direction, which is useful since many location/item names
359 reference compass directions. It can be enabled in the settings screen on the
360 pause menu.
361- Compatability update for the changes in v6.6 of the apworld.
362
363Download:
364[lingo2-archipelago-client-v6.7.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v6.7.zip)<br/>
365Source:
366[v6.7](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v6.7)
367
368## Legacy Apworld v6.6 - 2025-09-19
369
370- Added options that make the requirements for Purple Ending and Cyan Ending
371 stricter. With the strict options on, players are required to have all purple
372 (level 1) letters in order to get Purple Ending, and all cyan (level 2)
373 letters to get Cyan Ending. These options are on by default.
374- Renamed several items and locations, mostly regarding changing relative
375 directions (left, right, etc) to compass directions. The colored SMILE panels
376 in Daedalus now have clearer names too.
377- Fixed some minor logic errors.
378
379Download:
380[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v6.6/lingo2.apworld)<br/>
381Template YAML:
382[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v6.6/Lingo%202.yaml)<br/>
383Source:
384[v6.6](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v6.6)
385
386## Legacy Client v5.6 - 2025-09-17
387
388- Letter locations will no longer reappear after being collected.
389- This also prevents a potential scenario in which it is impossible to access
390 the location "The Congruent - Obverse Yellow Puzzles" when door shuffle is
391 disabled.
392
393Download:
394[lingo2-archipelago-client-v5.6.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.6.zip)<br/>
395Source:
396[v5.6](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v5.6)
397
398## Legacy Client v5.5 - 2025-09-16
399
400- Compatability update for v5.5 of the apworld.
401
402Download:
403[lingo2-archipelago-client-v5.5.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.5.zip)<br/>
404Source:
405[v5.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v5.5)
406
407## Legacy Apworld v5.5 - 2025-09-16
408
409- Fixed a panel in The Ancient that was missing a symbol.
410- Fixed an issue where you could be expected to get S1 in The Darkroom without
411 having U.
412- Renamed a few locations.
413
414Download:
415[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/lingo2.apworld)<br/>
416Template YAML:
417[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/Lingo%202.yaml)<br/>
418Source:
419[v5.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v5.5)
420
421## Legacy Apworld v4.4 - 2025-09-14
422
423- Fixed panel set location names.
424
425Download:
426[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/lingo2.apworld)<br/>
427Template YAML:
428[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/Lingo%202.yaml)<br/>
429Source:
430[v4.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v4.4)
431
432## Legacy Client v4.4 - 2025-09-13
433
434- Added support for anti-collectable trap items.
435- Fixed entrance to The Jubilant not opening properly when using control center
436 color shuffle.
437- Fixed the location "The Entry (Colored Doors Area) - OPEN" not sending.
438- Fixed level 2 letters not activating properly when letter shuffle is set to
439 Item Cyan.
440- Messages are now cleared out when returning to the main menu.
441- The player is prevented from accidentally breaking roof access logic when
442 returning to Daedalus from Icarus.
443
444Download:
445[lingo2-archipelago-client-v4.4.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v4.4.zip)<br/>
446Source:
447[v4.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v4.4)
448
449## Legacy Apworld v4.3 - 2025-09-13
450
451- Added a location for the anti-collectable in The Repetitive.
452- Added trap items. These remove letters from your keyboard until you use the
453 Key Return in The Entry, similar to the anti-collectable in The Repetitive.
454 This can be controlled using the `trap_percentage` option, which defaults to
455 zero.
456- Fixed crash on load when using Python 3.11.
457
458Download:
459[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/lingo2.apworld)<br/>
460Template YAML:
461[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/Lingo%202.yaml)<br/>
462Source:
463[v4.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v4.3)
464
465## Legacy Client v3.3 - 2025-09-12
466
467- Fixed issue downloading large datapackages (such as TUNIC's).
468- Connection failures now show error messages.
469
470Download:
471[lingo2-archipelago-client-v3.3.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.3.zip)<br/>
472Source:
473[v3.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v3.3)
474
475## Legacy Client v3.2 - 2025-09-12
476
477- Initial release for testing. Features include door shuffle, letter shuffle,
478 and symbol shuffle.
479
480Download:
481[lingo2-archipelago-client-v3.2.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.2.zip)<br/>
482Source:
483[v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v3.2)
484
485## Legacy Apworld v3.2 - 2025-09-12
486
487- Initial release for testing. Features include door shuffle, letter shuffle,
488 and symbol shuffle.
489
490Download:
491[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/lingo2.apworld)<br/>
492Template YAML:
493[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/Lingo%202.yaml)<br/>
494Source:
495[v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v3.2)
diff --git a/README.md b/README.md index 1da3c2b..24e9fa2 100644 --- a/README.md +++ b/README.md
@@ -9,17 +9,50 @@ 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
12## How To Play 12## Installation
13 13
14Here are the components needed in order to play: 141. Download the Lingo 2 Apworld from
15 [the releases page](https://code.fourisland.com/lingo2-archipelago/about/CHANGELOG.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.
15 20
16- **Apworld**: This is used by Archipelago to generate randomized Lingo 2 21## Joining a Multiworld game (Windows)
17 worlds. 22
18 - [Installation & FAQ](https://code.fourisland.com/lingo2-archipelago/about/apworld/README.md) 231. Open the Archipelago Launcher.
19 - [Downloads](https://code.fourisland.com/lingo2-archipelago/about/apworld/CHANGELOG.md) 242. Select "Lingo 2 Client".
20- **Client**: This is how Lingo 2 connects to an Archipelago multiworld. 253. The first time you do this, Archipelago will prompt you for the location of
21 - [Installation & FAQ](https://code.fourisland.com/lingo2-archipelago/about/client/README.md) 26 the Lingo 2 executable file ("Lingo2.exe"). You can find this by
22 - [Downloads](https://code.fourisland.com/lingo2-archipelago/about/client/CHANGELOG.md) 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!
23 56
24## Frequently Asked Questions 57## Frequently Asked Questions
25 58
@@ -38,13 +71,31 @@ same time as collecting the letter.
38 71
39### What areas are randomized? 72### What areas are randomized?
40 73
41Almost all maps that you can access from the base game are randomized. The 74Almost all maps that you can access from the base game are randomized. The only
42exceptions are: 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.
43 96
44- Icarus (this will be randomized at some point, although it will be optional) 97A connection to Archipelago is required to resume playing a multiworld. This is
45- Demo 98because the set of items you have received is not stored locally.
46- The Hinterlands (this will probably be repurposed)
47- The beta tester gift maps
48 99
49### What about wall snipes? 100### What about wall snipes?
50 101
@@ -95,10 +146,139 @@ before leaving solve mode, as the keyholder will still be considered to be
95"focused", even though it has moved. If you have already moved, though, there is 146"focused", even though it has moved. If you have already moved, though, there is
96another way to get your letters back: just use the Key Return in The Entry. 147another way to get your letters back: just use the Key Return in The Entry.
97 148
98## Project Details 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```
99 274
100There are multiple parts of this project: 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.
101 279
102- [Client](https://code.fourisland.com/lingo2-archipelago/about/client/README.md) 280After generating those three files, the apworld should be functional. You can
103- [Apworld](https://code.fourisland.com/lingo2-archipelago/about/apworld/README.md) 281copy it into an Archipelago source tree (rename the folder `apworld` to `lingo2`
104- [Data](https://code.fourisland.com/lingo2-archipelago/about/data/README.md) 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/CHANGELOG.md b/apworld/CHANGELOG.md deleted file mode 100644 index af45992..0000000 --- a/apworld/CHANGELOG.md +++ /dev/null
@@ -1,54 +0,0 @@
1# lingo2-archipelago Apworld Releases
2
3## v5.5 - 2025-09-16
4
5- Fixed a panel in The Ancient that was missing a symbol.
6- Fixed an issue where you could be expected to get S1 in The Darkroom without
7 having U.
8- Renamed a few locations.
9
10Download:
11[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/lingo2.apworld)<br/>
12Template YAML:
13[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v5.5/Lingo%202.yaml)<br/>
14Source:
15[v5.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v5.5)
16
17## v4.4 - 2025-09-14
18
19- Fixed panel set location names.
20
21Download:
22[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/lingo2.apworld)<br/>
23Template YAML:
24[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.4/Lingo%202.yaml)<br/>
25Source:
26[v4.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v4.4)
27
28## v4.3 - 2025-09-13
29
30- Added a location for the anti-collectable in The Repetitive.
31- Added trap items. These remove letters from your keyboard until you use the
32 Key Return in The Entry, similar to the anti-collectable in The Repetitive.
33 This can be controlled using the `trap_percentage` option, which defaults to
34 zero.
35- Fixed crash on load when using Python 3.11.
36
37Download:
38[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/lingo2.apworld)<br/>
39Template YAML:
40[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v4.3/Lingo%202.yaml)<br/>
41Source:
42[v4.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v4.3)
43
44## v3.2 - 2025-09-12
45
46- Initial release for testing. Features include door shuffle, letter shuffle,
47 and symbol shuffle.
48
49Download:
50[lingo2.apworld](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/lingo2.apworld)<br/>
51Template YAML:
52[Lingo 2.yaml](https://files.fourisland.com/releases/lingo2-archipelago/apworld/v3.2/Lingo%202.yaml)<br/>
53Source:
54[v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=apworld-v3.2)
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 f1de503..6b5338e 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py
@@ -1,14 +1,18 @@
1""" 1"""
2Archipelago init file for Lingo 2 2Archipelago init file for Lingo 2
3""" 3"""
4from typing import ClassVar
5
4from BaseClasses import ItemClassification, Item, Tutorial 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, ANTI_COLLECTABLE_TRAPS 10from .items import Lingo2Item, ANTI_COLLECTABLE_TRAPS, ALL_LETTERS_UPPER
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
11from .version import APWORLD_VERSION 15from worlds.LauncherComponents import Component, Type, components, launch as launch_component, icon_paths
12 16
13 17
14class Lingo2WebWorld(WebWorld): 18class Lingo2WebWorld(WebWorld):
@@ -24,6 +28,15 @@ class Lingo2WebWorld(WebWorld):
24 )] 28 )]
25 29
26 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
38
39
27class Lingo2World(World): 40class Lingo2World(World):
28 """ 41 """
29 Lingo 2 is a first person indie puzzle game where you solve word puzzles in a labyrinthe world. Compared to its 42 Lingo 2 is a first person indie puzzle game where you solve word puzzles in a labyrinthe world. Compared to its
@@ -33,6 +46,9 @@ class Lingo2World(World):
33 game = "Lingo 2" 46 game = "Lingo 2"
34 web = Lingo2WebWorld() 47 web = Lingo2WebWorld()
35 48
49 settings: ClassVar[Lingo2Settings]
50 settings_key = "lingo2_options"
51
36 topology_present = True 52 topology_present = True
37 53
38 options_dataclass = Lingo2Options 54 options_dataclass = Lingo2Options
@@ -44,17 +60,42 @@ class Lingo2World(World):
44 item_name_groups = static_logic.item_name_groups 60 item_name_groups = static_logic.item_name_groups
45 location_name_groups = static_logic.location_name_groups 61 location_name_groups = static_logic.location_name_groups
46 62
63 for_tracker: ClassVar[bool] = False
64
47 player_logic: Lingo2PlayerLogic 65 player_logic: Lingo2PlayerLogic
48 66
67 port_pairings: dict[int, int]
68
49 def generate_early(self): 69 def generate_early(self):
50 self.player_logic = Lingo2PlayerLogic(self) 70 self.player_logic = Lingo2PlayerLogic(self)
71 self.port_pairings = {}
72
73 if self.options.restrict_letter_placements:
74 self.options.local_items.value |= set(ALL_LETTERS_UPPER)
51 75
52 def create_regions(self): 76 def create_regions(self):
77 if hasattr(self.multiworld, "re_gen_passthrough") and "Lingo 2" in self.multiworld.re_gen_passthrough:
78 self.player_logic.rte_mapping = [self.world.static_logic.map_id_by_name[map_name]
79 for map_name in self.multiworld.re_gen_passthrough["Lingo 2"]["rte"]]
80
53 create_regions(self) 81 create_regions(self)
54 82
55 from Utils import visualize_regions 83 def connect_entrances(self):
84 if self.options.shuffle_worldports:
85 if hasattr(self.multiworld, "re_gen_passthrough") and "Lingo 2" in self.multiworld.re_gen_passthrough:
86 slot_value = self.multiworld.re_gen_passthrough["Lingo 2"]["port_pairings"]
87 self.port_pairings = {
88 self.static_logic.port_id_by_ap_id[int(fp)]: self.static_logic.port_id_by_ap_id[int(tp)]
89 for fp, tp in slot_value.items()
90 }
91
92 connect_ports_from_ut(self.port_pairings, self)
93 else:
94 shuffle_entrances(self)
95
96 #from Utils import visualize_regions
56 97
57 visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml") 98 #visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml")
58 99
59 def create_items(self): 100 def create_items(self):
60 pool = [self.create_item(name) for name in self.player_logic.real_items] 101 pool = [self.create_item(name) for name in self.player_logic.real_items]
@@ -79,38 +120,86 @@ class Lingo2World(World):
79 for i in range(0, item_difference): 120 for i in range(0, item_difference):
80 pool.append(self.create_item(self.get_filler_item_name())) 121 pool.append(self.create_item(self.get_filler_item_name()))
81 122
123 if not any(ItemClassification.progression in item.classification for item in pool):
124 raise OptionError(f"Lingo 2 player {self.player} has no progression items. Please enable at least one "
125 f"option that would add progression gating to your world, such as Shuffle Doors or "
126 f"Shuffle Letters.")
127
82 self.multiworld.itempool += pool 128 self.multiworld.itempool += pool
83 129
130 for name in self.player_logic.starting_items:
131 self.push_precollected(self.create_item(name))
132
84 def create_item(self, name: str) -> Item: 133 def create_item(self, name: str) -> Item:
85 return Lingo2Item(name, ItemClassification.filler if name == self.get_filler_item_name() else 134 item = Lingo2Item(name, ItemClassification.filler if name == self.get_filler_item_name() else
86 ItemClassification.trap if name in ANTI_COLLECTABLE_TRAPS else 135 ItemClassification.trap if name in ANTI_COLLECTABLE_TRAPS else
87 ItemClassification.progression, 136 ItemClassification.progression,
88 self.item_name_to_id.get(name), self.player) 137 self.item_name_to_id.get(name), self.player)
89 138
139 item.is_letter = (name in ALL_LETTERS_UPPER)
140 return item
141
90 def set_rules(self): 142 def set_rules(self):
91 self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player) 143 self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
92 144
93 def fill_slot_data(self): 145 def fill_slot_data(self):
94 slot_options = [ 146 slot_options = [
95 "cyan_door_behavior", 147 "cyan_door_behavior",
148 "daedalus_only",
96 "daedalus_roof_access", 149 "daedalus_roof_access",
150 "enable_gift_maps",
151 "enable_icarus",
152 "endings_requirement",
153 "fast_travel_access",
97 "keyholder_sanity", 154 "keyholder_sanity",
155 "masteries_requirement",
98 "shuffle_control_center_colors", 156 "shuffle_control_center_colors",
99 "shuffle_doors", 157 "shuffle_doors",
100 "shuffle_gallery_paintings", 158 "shuffle_gallery_paintings",
101 "shuffle_letters", 159 "shuffle_letters",
160 "shuffle_music",
102 "shuffle_symbols", 161 "shuffle_symbols",
162 "shuffle_worldports",
103 "strict_cyan_ending", 163 "strict_cyan_ending",
104 "strict_purple_ending", 164 "strict_purple_ending",
105 "victory_condition", 165 "victory_condition",
106 ] 166 ]
107 167
108 slot_data = { 168 slot_data: dict[str, object] = {
109 **self.options.as_dict(*slot_options), 169 **self.options.as_dict(*slot_options),
110 "version": [self.static_logic.get_data_version(), APWORLD_VERSION], 170 "custom_mint_ending": self.player_logic.custom_mint_ending or "",
171 "rte": [self.static_logic.objects.maps[map_id].name for map_id in self.player_logic.rte_mapping],
172 "seed": self.random.randint(0, 1000000),
173 "version": self.static_logic.get_data_version(),
111 } 174 }
112 175
176 if self.options.shuffle_worldports:
177 def get_port_ap_id(port_id):
178 return self.static_logic.objects.ports[port_id].ap_id
179
180 slot_data["port_pairings"] = {get_port_ap_id(from_id): get_port_ap_id(to_id)
181 for from_id, to_id in self.port_pairings.items()}
182
113 return slot_data 183 return slot_data
114 184
115 def get_filler_item_name(self) -> str: 185 def get_filler_item_name(self) -> str:
116 return "A Job Well Done" 186 return "A Job Well Done"
187
188 # for the universal tracker, doesn't get called in standard gen
189 # docs: https://github.com/FarisTheAncient/Archipelago/blob/tracker/worlds/tracker/docs/re-gen-passthrough.md
190 @staticmethod
191 def interpret_slot_data(slot_data: dict[str, object]) -> dict[str, object]:
192 # returning slot_data so it regens, giving it back in multiworld.re_gen_passthrough
193 # we are using re_gen_passthrough over modifying the world here due to complexities with ER
194 return slot_data
195
196
197def launch_client(*args):
198 from .context import client_main
199 launch_component(client_main, name="Lingo2Client", args=args)
200
201
202icon_paths["lingo2_ico"] = f"ap:{__name__}/logo.png"
203component = Component("Lingo 2 Client", component_type=Type.CLIENT, func=launch_client,
204 description="Open Lingo 2.", supports_uri=True, game_name="Lingo 2", icon="lingo2_ico")
205components.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/client/Archipelago/compass.gd b/apworld/client/compass.gd index c90475a..c90475a 100644 --- a/client/Archipelago/compass.gd +++ b/apworld/client/compass.gd
diff --git a/client/Archipelago/compass_overlay.gd b/apworld/client/compass_overlay.gd index 56e81ff..56e81ff 100644 --- a/client/Archipelago/compass_overlay.gd +++ b/apworld/client/compass_overlay.gd
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..373f981 --- /dev/null +++ b/apworld/client/gamedata.gd
@@ -0,0 +1,310 @@
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 = {}
19var map_id_by_rte_ap_id = {}
20
21var kSYMBOL_ITEMS
22
23
24func _init(proto_script):
25 SCRIPT_proto = proto_script
26
27 kSYMBOL_ITEMS = {
28 SCRIPT_proto.PuzzleSymbol.SUN: "Sun Symbol",
29 SCRIPT_proto.PuzzleSymbol.SPARKLES: "Sparkles Symbol",
30 SCRIPT_proto.PuzzleSymbol.ZERO: "Zero Symbol",
31 SCRIPT_proto.PuzzleSymbol.EXAMPLE: "Example Symbol",
32 SCRIPT_proto.PuzzleSymbol.BOXES: "Boxes Symbol",
33 SCRIPT_proto.PuzzleSymbol.PLANET: "Planet Symbol",
34 SCRIPT_proto.PuzzleSymbol.PYRAMID: "Pyramid Symbol",
35 SCRIPT_proto.PuzzleSymbol.CROSS: "Cross Symbol",
36 SCRIPT_proto.PuzzleSymbol.SWEET: "Sweet Symbol",
37 SCRIPT_proto.PuzzleSymbol.GENDER: "Gender Symbol",
38 SCRIPT_proto.PuzzleSymbol.AGE: "Age Symbol",
39 SCRIPT_proto.PuzzleSymbol.SOUND: "Sound Symbol",
40 SCRIPT_proto.PuzzleSymbol.ANAGRAM: "Anagram Symbol",
41 SCRIPT_proto.PuzzleSymbol.JOB: "Job Symbol",
42 SCRIPT_proto.PuzzleSymbol.STARS: "Stars Symbol",
43 SCRIPT_proto.PuzzleSymbol.NULL: "Null Symbol",
44 SCRIPT_proto.PuzzleSymbol.EVAL: "Eval Symbol",
45 SCRIPT_proto.PuzzleSymbol.LINGO: "Lingo Symbol",
46 SCRIPT_proto.PuzzleSymbol.QUESTION: "Question Symbol",
47 }
48
49
50func load(data_bytes):
51 objects = SCRIPT_proto.AllObjects.new()
52
53 var result_code = objects.from_bytes(data_bytes)
54 if result_code != SCRIPT_proto.PB_ERR.NO_ERRORS:
55 print("Could not load generated data: %d" % result_code)
56 return
57
58 for map in objects.get_maps():
59 map_id_by_name[map.get_name()] = map.get_id()
60
61 if map.has_rte_ap_id():
62 map_id_by_rte_ap_id[map.get_rte_ap_id()] = map.get_id()
63
64 for door in objects.get_doors():
65 var map = objects.get_maps()[door.get_map_id()]
66
67 if not map.get_name() in door_id_by_map_node_path:
68 door_id_by_map_node_path[map.get_name()] = {}
69
70 var map_data = door_id_by_map_node_path[map.get_name()]
71 for receiver in door.get_receivers():
72 map_data[receiver] = door.get_id()
73
74 for painting_id in door.get_move_paintings():
75 var painting = objects.get_paintings()[painting_id]
76 map_data[painting.get_path()] = door.get_id()
77
78 if door.has_ap_id():
79 door_id_by_ap_id[door.get_ap_id()] = door.get_id()
80
81 if (
82 door.get_type() == SCRIPT_proto.DoorType.STANDARD
83 or door.get_type() == SCRIPT_proto.DoorType.LOCATION_ONLY
84 or door.get_type() == SCRIPT_proto.DoorType.GRAVESTONE
85 ):
86 location_name_by_id[door.get_ap_id()] = _get_door_location_name(door)
87
88 for painting in objects.get_paintings():
89 var room = objects.get_rooms()[painting.get_room_id()]
90 var map = objects.get_maps()[room.get_map_id()]
91
92 if not map.get_name() in painting_id_by_map_node_path:
93 painting_id_by_map_node_path[map.get_name()] = {}
94
95 var _map_data = painting_id_by_map_node_path[map.get_name()]
96
97 for port in objects.get_ports():
98 var room = objects.get_rooms()[port.get_room_id()]
99 var map = objects.get_maps()[room.get_map_id()]
100
101 if not map.get_name() in port_id_by_map_node_path:
102 port_id_by_map_node_path[map.get_name()] = {}
103
104 var map_data = port_id_by_map_node_path[map.get_name()]
105 map_data[port.get_path()] = port.get_id()
106
107 if port.has_ap_id():
108 port_id_by_ap_id[port.get_ap_id()] = port.get_id()
109
110 for progressive in objects.get_progressives():
111 progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id()
112
113 for letter in objects.get_letters():
114 letter_id_by_ap_id[letter.get_ap_id()] = letter.get_id()
115 location_name_by_id[letter.get_ap_id()] = _get_letter_location_name(letter)
116
117 for mastery in objects.get_masteries():
118 location_name_by_id[mastery.get_ap_id()] = _get_mastery_location_name(mastery)
119
120 for ending in objects.get_endings():
121 var location_name = _get_ending_location_name(ending)
122 location_name_by_id[ending.get_ap_id()] = location_name
123 ending_display_name_by_name[ending.get_name()] = location_name
124
125 for keyholder in objects.get_keyholders():
126 if keyholder.has_key():
127 location_name_by_id[keyholder.get_ap_id()] = _get_keyholder_location_name(keyholder)
128
129 for panel in objects.get_panels():
130 var room = objects.get_rooms()[panel.get_room_id()]
131 var map = objects.get_maps()[room.get_map_id()]
132
133 if not map.get_name() in panel_id_by_map_node_path:
134 panel_id_by_map_node_path[map.get_name()] = {}
135
136 var map_data = panel_id_by_map_node_path[map.get_name()]
137 map_data[panel.get_path()] = panel.get_id()
138
139 for symbol_name in kSYMBOL_ITEMS.values():
140 symbol_item_ids.append(objects.get_special_ids()[symbol_name])
141
142 for special_name in objects.get_special_ids().keys():
143 if special_name.begins_with("Anti "):
144 anti_trap_ids[objects.get_special_ids()[special_name]] = (
145 special_name.substr(5).to_lower()
146 )
147
148
149func get_door_for_map_node_path(map_name, node_path):
150 if not door_id_by_map_node_path.has(map_name):
151 return null
152
153 var map_data = door_id_by_map_node_path[map_name]
154 return map_data.get(node_path, null)
155
156
157func get_panel_for_map_node_path(map_name, node_path):
158 if not panel_id_by_map_node_path.has(map_name):
159 return null
160
161 var map_data = panel_id_by_map_node_path[map_name]
162 return map_data.get(node_path, null)
163
164
165func get_port_for_map_node_path(map_name, node_path):
166 if not port_id_by_map_node_path.has(map_name):
167 return null
168
169 var map_data = port_id_by_map_node_path[map_name]
170 return map_data.get(node_path, null)
171
172
173func get_door_ap_id(door_id):
174 var door = objects.get_doors()[door_id]
175 if door.has_ap_id():
176 return door.get_ap_id()
177 else:
178 return null
179
180
181func get_door_map_name(door_id):
182 var door = objects.get_doors()[door_id]
183 var map = objects.get_maps()[door.get_map_id()]
184 return map.get_name()
185
186
187func get_door_receivers(door_id):
188 var door = objects.get_doors()[door_id]
189 return door.get_receivers()
190
191
192func get_worldport_display_name(port_id):
193 var port = objects.get_ports()[port_id]
194 return "%s - %s" % [_get_room_object_map_name(port), port.get_display_name()]
195
196
197func _get_map_object_map_name(obj):
198 return objects.get_maps()[obj.get_map_id()].get_display_name()
199
200
201func _get_room_object_map_name(obj):
202 return _get_map_object_map_name(objects.get_rooms()[obj.get_room_id()])
203
204
205func _get_room_object_location_prefix(obj):
206 var room = objects.get_rooms()[obj.get_room_id()]
207 var game_map = objects.get_maps()[room.get_map_id()]
208
209 if room.has_panel_display_name():
210 return "%s (%s)" % [game_map.get_display_name(), room.get_panel_display_name()]
211 else:
212 return game_map.get_display_name()
213
214
215func _get_door_location_name(door):
216 var map_part = _get_room_object_location_prefix(door)
217
218 if door.has_location_name():
219 return "%s - %s" % [map_part, door.get_location_name()]
220
221 var generated_location_name = _get_generated_door_location_name(door)
222 if generated_location_name != null:
223 return generated_location_name
224
225 return "%s - %s" % [map_part, door.get_name()]
226
227
228func _get_generated_door_location_name(door):
229 if door.get_type() != SCRIPT_proto.DoorType.STANDARD:
230 return null
231
232 if (
233 door.get_keyholders().size() > 0
234 or (door.has_white_ending() and door.get_white_ending())
235 or door.has_complete_at()
236 ):
237 return null
238
239 if door.get_panels().size() > 4:
240 return null
241
242 var map_areas = []
243 for panel_id in door.get_panels():
244 var panel = objects.get_panels()[panel_id.get_panel()]
245 var panel_room = objects.get_rooms()[panel.get_room_id()]
246 # It's okay if panel_display_name is not present because then it's coalesced with other unnamed areas.
247 var panel_display_name = ""
248 if panel_room.has_panel_display_name():
249 panel_display_name = panel_room.get_panel_display_name()
250 if not map_areas.has(panel_display_name):
251 map_areas.append(panel_display_name)
252
253 if map_areas.size() > 1:
254 return null
255
256 var game_map = objects.get_maps()[door.get_map_id()]
257 var map_area = map_areas[0]
258 var map_part
259 if map_area == "":
260 map_part = game_map.get_display_name()
261 else:
262 map_part = "%s (%s)" % [game_map.get_display_name(), map_area]
263
264 var panel_names = []
265 for panel_id in door.get_panels():
266 var panel_data = objects.get_panels()[panel_id.get_panel()]
267 var panel_name
268 if panel_data.has_display_name():
269 panel_name = panel_data.get_display_name()
270 else:
271 panel_name = panel_data.get_name()
272
273 var location_part
274 if panel_id.has_answer():
275 location_part = "%s/%s" % [panel_name, panel_id.get_answer().to_upper()]
276 else:
277 location_part = panel_name
278
279 panel_names.append(location_part)
280
281 panel_names.sort()
282
283 return map_part + " - " + ", ".join(panel_names)
284
285
286func _get_letter_location_name(letter):
287 var letter_level = 2 if (letter.has_level2() and letter.get_level2()) else 1
288 var letter_name = "%s%d" % [letter.get_key().to_upper(), letter_level]
289 return "%s - %s" % [_get_room_object_map_name(letter), letter_name]
290
291
292func _get_mastery_location_name(mastery):
293 return "%s - Mastery" % _get_room_object_map_name(mastery)
294
295
296func _get_ending_location_name(ending):
297 return (
298 "%s - %s Ending" % [_get_room_object_map_name(ending), ending.get_name().to_pascal_case()]
299 )
300
301
302func _get_keyholder_location_name(keyholder):
303 return (
304 "%s - %s Keyholder"
305 % [_get_room_object_location_prefix(keyholder), keyholder.get_key().to_upper()]
306 )
307
308
309func vec3d_to_vector3(input) -> Vector3:
310 return Vector3(input.get_x(), input.get_y(), input.get_z())
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..9ab45f9 100644 --- a/client/Archipelago/keyHolderResetterListener.gd +++ b/apworld/client/keyHolderResetterListener.gd
@@ -6,3 +6,5 @@ func reset():
6 var was_removed = ap.keyboard.reset_keyholders() 6 var was_removed = ap.keyboard.reset_keyholders()
7 if was_removed: 7 if was_removed:
8 sfxPlayer.sfx_play("pickup") 8 sfxPlayer.sfx_play("pickup")
9
10 ap.client.requestSync()
diff --git a/client/Archipelago/keyboard.gd b/apworld/client/keyboard.gd index 450566d..9026c06 100644 --- a/client/Archipelago/keyboard.gd +++ b/apworld/client/keyboard.gd
@@ -48,6 +48,9 @@ func load_seed():
48 if localdata.size() > 2: 48 if localdata.size() > 2:
49 keyholder_state = localdata[2] 49 keyholder_state = localdata[2]
50 50
51 if not letters_saved.is_empty():
52 ap.client.updateKeyboard(letters_saved)
53
51 for k in kALL_LETTERS: 54 for k in kALL_LETTERS:
52 var level = 0 55 var level = 0
53 56
@@ -105,10 +108,20 @@ func update_unlocks():
105 108
106 109
107func collect_local_letter(key, level): 110func collect_local_letter(key, level):
108 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):
109 return 120 return
110 121
111 letters_saved[key] = level 122 letters_saved[key] = true_level
123
124 ap.client.updateKeyboard({key: true_level})
112 125
113 if letters_blocked.has(key): 126 if letters_blocked.has(key):
114 letters_blocked.erase(key) 127 letters_blocked.erase(key)
@@ -178,9 +191,6 @@ func load_keyholders(map):
178 191
179 192
180func reset_keyholders(): 193func reset_keyholders():
181 if letters_in_keyholders.is_empty() and letters_blocked.is_empty():
182 return false
183
184 var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty() 194 var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty()
185 195
186 if keyholder_state.has(global.map): 196 if keyholder_state.has(global.map):
@@ -197,3 +207,22 @@ func reset_keyholders():
197 save() 207 save()
198 208
199 return cleared_anything 209 return cleared_anything
210
211
212func remote_keyboard_updated(updates):
213 var reverse = {}
214 var should_update = false
215
216 for k in updates:
217 if not letters_saved.has(k) or updates[k] > letters_saved[k]:
218 letters_saved[k] = updates[k]
219 should_update = true
220 elif updates[k] < letters_saved[k]:
221 reverse[k] = letters_saved[k]
222
223 if should_update:
224 update_unlocks()
225
226 if not reverse.is_empty():
227 var ap = global.get_node("Archipelago")
228 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..8cac24c --- /dev/null +++ b/apworld/client/main.gd
@@ -0,0 +1,314 @@
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("rteMenu.gd"))
52 installScriptExtension(runtime.load_script("saver.gd"))
53 installScriptExtension(runtime.load_script("teleport.gd"))
54 installScriptExtension(runtime.load_script("teleportListener.gd"))
55 installScriptExtension(runtime.load_script("unlockReaderListener.gd"))
56 installScriptExtension(runtime.load_script("visibilityListener.gd"))
57 installScriptExtension(runtime.load_script("worldport.gd"))
58 installScriptExtension(runtime.load_script("worldportListener.gd"))
59
60 var proto_script = runtime.load_script("../generated/proto.gd")
61 var gamedata_script = runtime.load_script("gamedata.gd")
62 var gamedata_instance = gamedata_script.new(proto_script)
63 gamedata_instance.load(runtime.read_path("../generated/data.binpb"))
64 gamedata_instance.name = "Gamedata"
65 global.add_child(gamedata_instance)
66
67 var messages_script = runtime.load_script("messages.gd")
68 var messages_instance = messages_script.new()
69 messages_instance.name = "Messages"
70 messages_instance.SCRIPT_rainbowText = runtime.load_script("rainbowText.gd")
71 global.add_child(messages_instance)
72
73 var effects_script = runtime.load_script("effects.gd")
74 var effects_instance = effects_script.new()
75 effects_instance.name = "Effects"
76 global.add_child(effects_instance)
77
78 var textclient_script = runtime.load_script("textclient.gd")
79 var textclient_instance = textclient_script.new()
80 textclient_instance.name = "Textclient"
81 global.add_child(textclient_instance)
82
83 var compass_overlay_script = runtime.load_script("compass_overlay.gd")
84 var compass_overlay_instance = compass_overlay_script.new()
85 compass_overlay_instance.name = "Compass"
86 compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd")
87 global.add_child(compass_overlay_instance)
88
89 unlocks.data["advanced_mastery"] = ""
90 unlocks.data["charismatic_mastery"] = ""
91 unlocks.data["crystalline_mastery"] = ""
92 unlocks.data["fuzzy_mastery"] = ""
93 unlocks.data["icarus_mastery"] = ""
94 unlocks.data["stellar_mastery"] = ""
95
96 var ap = global.get_node("Archipelago")
97 var gamedata = global.get_node("Gamedata")
98 ap.ap_connected.connect(connectionSuccessful)
99 ap.could_not_connect.connect(connectionUnsuccessful)
100 ap.connect_status.connect(connectionStatus)
101
102 # Populate textboxes with AP settings.
103 get_node("../Panel/server_box").text = ap.ap_server
104 get_node("../Panel/player_box").text = ap.ap_user
105 get_node("../Panel/password_box").text = ap.ap_pass
106
107 var history_box = get_node("../Panel/connection_history")
108 if ap.connection_history.is_empty():
109 history_box.disabled = true
110 else:
111 history_box.disabled = false
112
113 var i = 0
114 for details in ap.connection_history:
115 history_box.get_popup().add_item("%s (%s)" % [details[1], details[0]], i)
116 i += 1
117
118 history_box.get_popup().id_pressed.connect(historySelected)
119
120 # Show client version.
121 var version = gamedata.objects.get_version()
122 get_node("../Panel/title").text = (
123 "ARCHIPELAGO (%d.%d.%d)" % [version.get_major(), version.get_minor(), version.get_patch()]
124 )
125
126 # Increase font size in text boxes.
127 get_node("../Panel/server_box").add_theme_font_size_override("font_size", 36)
128 get_node("../Panel/player_box").add_theme_font_size_override("font_size", 36)
129 get_node("../Panel/password_box").add_theme_font_size_override("font_size", 36)
130
131 # Set up version mismatch dialog.
132 get_node("../Panel/VersionMismatch").confirmed.connect(startGame)
133 get_node("../Panel/VersionMismatch").get_cancel_button().pressed.connect(
134 versionMismatchDeclined
135 )
136
137 # Set up buttons.
138 get_node("../Panel/connect_button").pressed.connect(_connect_pressed)
139 get_node("../Panel/quit_button").pressed.connect(_back_pressed)
140
141
142func _connect_pressed():
143 get_node("../Panel/connect_button").disabled = true
144
145 var ap = global.get_node("Archipelago")
146 ap.ap_server = get_node("../Panel/server_box").text
147 ap.ap_user = get_node("../Panel/player_box").text
148 ap.ap_pass = get_node("../Panel/password_box").text
149 ap.saveSettings()
150
151 ap.connectToServer()
152
153
154func _back_pressed():
155 var ap = global.get_node("Archipelago")
156 ap.disconnect_from_ap()
157 ap.client.sendQuit()
158
159 get_tree().quit()
160
161
162# Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd
163func installScriptExtension(childScript: Resource):
164 # Force Godot to compile the script now.
165 # We need to do this here to ensure that the inheritance chain is
166 # properly set up, and multiple mods can chain-extend the same
167 # class multiple times.
168 # This is also needed to make Godot instantiate the extended class
169 # when creating singletons.
170 # The actual instance is thrown away.
171 childScript.new()
172
173 var parentScript = childScript.get_base_script()
174 var parentScriptPath = parentScript.resource_path
175 global._print("ModLoader: Installing script extension over %s" % parentScriptPath)
176 childScript.take_over_path(parentScriptPath)
177
178
179func connectionStatus(message):
180 var popup = get_node("../Panel/AcceptDialog")
181 popup.title = "Connecting to Archipelago"
182 popup.dialog_text = message
183 popup.exclusive = true
184 popup.get_ok_button().visible = false
185 popup.popup_centered()
186
187
188func connectionSuccessful():
189 var ap = global.get_node("Archipelago")
190 var gamedata = global.get_node("Gamedata")
191
192 # Check for major version mismatch.
193 if ap.apworld_version[0] != gamedata.objects.get_version().get_major():
194 get_node("../Panel/AcceptDialog").exclusive = false
195
196 var popup = get_node("../Panel/VersionMismatch")
197 popup.title = "Version Mismatch!"
198 popup.dialog_text = (
199 "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."
200 % [
201 ap.apworld_version[0],
202 ap.apworld_version[1],
203 ap.apworld_version[2],
204 gamedata.objects.get_version().get_major(),
205 gamedata.objects.get_version().get_minor(),
206 gamedata.objects.get_version().get_patch()
207 ]
208 )
209 popup.exclusive = true
210 popup.popup_centered()
211
212 return
213
214 startGame()
215
216
217func startGame():
218 var ap = global.get_node("Archipelago")
219
220 # Save connection details
221 var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass]
222 if ap.connection_history.has(connection_details):
223 ap.connection_history.erase(connection_details)
224 ap.connection_history.push_front(connection_details)
225 if ap.connection_history.size() > 10:
226 ap.connection_history.resize(10)
227 ap.saveSettings()
228
229 # Switch to the_entry
230 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
231 global.user = ap.getSaveFileName()
232 global.universe = "lingo"
233
234 if ap.daedalus_only:
235 global.map = "daedalus"
236 else:
237 global.map = "the_entry"
238
239 unlocks.resetCollectables()
240 unlocks.resetData()
241 unlocks.loadCollectables()
242 unlocks.loadData()
243
244 ap.setup_keys()
245
246 unlocks.unlockKey("capslock", 1)
247
248 if ap.shuffle_worldports:
249 settings.worldport_fades = "default"
250 else:
251 settings.worldport_fades = "never"
252
253 clearResourceCache("res://objects/meshes/gridDoor.tscn")
254 clearResourceCache("res://objects/nodes/allowNumbers.tscn")
255 clearResourceCache("res://objects/nodes/collectable.tscn")
256 clearResourceCache("res://objects/nodes/door.tscn")
257 clearResourceCache("res://objects/nodes/keyHolder.tscn")
258 clearResourceCache("res://objects/nodes/listeners/animationListener.tscn")
259 clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn")
260 clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn")
261 clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn")
262 clearResourceCache("res://objects/nodes/listeners/unlockReaderListener.tscn")
263 clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn")
264 clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn")
265 clearResourceCache("res://objects/nodes/panel.tscn")
266 clearResourceCache("res://objects/nodes/player.tscn")
267 clearResourceCache("res://objects/nodes/saver.tscn")
268 clearResourceCache("res://objects/nodes/teleport.tscn")
269 clearResourceCache("res://objects/nodes/worldport.tscn")
270 clearResourceCache("res://objects/scenes/menus/pause_menu.tscn")
271 clearResourceCache("res://objects/scenes/menus/rte_inner.tscn")
272
273 var paintings_dir = DirAccess.open("res://objects/meshes/paintings")
274 if paintings_dir:
275 paintings_dir.list_dir_begin()
276 var file_name = paintings_dir.get_next()
277 while file_name != "":
278 if not paintings_dir.current_is_dir() and file_name.ends_with(".tscn"):
279 clearResourceCache("res://objects/meshes/paintings/" + file_name)
280 file_name = paintings_dir.get_next()
281
282 switcher.switch_map.call_deferred("res://objects/scenes/%s.tscn" % global.map)
283
284
285func connectionUnsuccessful(error_message):
286 get_node("../Panel/connect_button").disabled = false
287
288 var popup = get_node("../Panel/AcceptDialog")
289 popup.title = "Could not connect to Archipelago"
290 popup.dialog_text = error_message
291 popup.exclusive = true
292 popup.get_ok_button().visible = true
293 popup.popup_centered()
294
295
296func versionMismatchDeclined():
297 get_node("../Panel/AcceptDialog").hide()
298 get_node("../Panel/connect_button").disabled = false
299
300 var ap = global.get_node("Archipelago")
301 ap.disconnect_from_ap()
302
303
304func historySelected(index):
305 var ap = global.get_node("Archipelago")
306 var details = ap.connection_history[index]
307
308 get_node("../Panel/server_box").text = details[0]
309 get_node("../Panel/player_box").text = details[1]
310 get_node("../Panel/password_box").text = details[2]
311
312
313func clearResourceCache(path):
314 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..f10a0b7 --- /dev/null +++ b/apworld/client/manager.gd
@@ -0,0 +1,773 @@
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 kFAST_TRAVEL_ACCESS_VANILLA = 0
50const kFAST_TRAVEL_ACCESS_UNLOCKED = 1
51const kFAST_TRAVEL_ACCESS_ITEMS = 2
52
53const kEndingNameByVictoryValue = {
54 0: "GRAY",
55 1: "PURPLE",
56 2: "MINT",
57 3: "BLACK",
58 4: "BLUE",
59 5: "CYAN",
60 6: "RED",
61 7: "PLUM",
62 8: "ORANGE",
63 9: "GOLD",
64 10: "YELLOW",
65 11: "GREEN",
66 12: "WHITE",
67}
68
69var apworld_version = [0, 0, 0]
70var custom_mint_ending = ""
71var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
72var daedalus_only = false
73var daedalus_roof_access = false
74var enable_gift_maps = []
75var enable_icarus = false
76var endings_requirement = 0
77var fast_travel_access = 0
78var keyholder_sanity = false
79var masteries_requirement = 0
80var music_mapping = {}
81var port_pairings = {}
82var rte_mapping = []
83var shuffle_control_center_colors = false
84var shuffle_doors = false
85var shuffle_gallery_paintings = false
86var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
87var shuffle_symbols = false
88var shuffle_worldports = false
89var slot_rng = null
90var strict_cyan_ending = false
91var strict_purple_ending = false
92var victory_condition = -1
93
94var color_by_material_path = {}
95
96signal could_not_connect
97signal connect_status
98signal ap_connected
99
100
101func _init():
102 # Read AP settings from file, if there are any
103 if FileAccess.file_exists("user://ap_settings"):
104 var file = FileAccess.open("user://ap_settings", FileAccess.READ)
105 var data = file.get_var(true)
106 file.close()
107
108 if typeof(data) != TYPE_ARRAY:
109 global._print("AP settings file is corrupted")
110 data = []
111
112 if data.size() > 0:
113 ap_server = data[0]
114
115 if data.size() > 1:
116 ap_user = data[1]
117
118 if data.size() > 2:
119 ap_pass = data[2]
120
121 if data.size() > 3:
122 connection_history = data[3]
123
124 if data.size() > 4:
125 show_compass = data[4]
126
127 if data.size() > 5:
128 show_locations = data[5]
129
130 if data.size() > 6:
131 show_minimap = data[6]
132
133 # We need to create a mapping from material paths to the original colors of
134 # those materials. We force reload the materials, overwriting any custom
135 # textures, and create the mapping. We then reload the textures in case the
136 # player had a custom one enabled.
137 var directory = DirAccess.open("res://assets/materials")
138 for material_name in directory.get_files():
139 var material = ResourceLoader.load(
140 "res://assets/materials/" + material_name, "", ResourceLoader.CACHE_MODE_REPLACE
141 )
142
143 color_by_material_path[material.resource_path] = Color(material.albedo_color)
144
145 settings.load_user_textures()
146
147
148func _ready():
149 client = SCRIPT_client.new()
150 client.SCRIPT_websocketserver = SCRIPT_websocketserver
151
152 client.item_received.connect(_process_item)
153 client.location_scout_received.connect(_process_location_scout)
154 client.text_message_received.connect(_process_text_message)
155 client.item_sent_notification.connect(_process_item_sent_notification)
156 client.hint_received.connect(_process_hint_received)
157 client.accessible_locations_updated.connect(_on_accessible_locations_updated)
158 client.checked_locations_updated.connect(_on_checked_locations_updated)
159 client.ignored_locations_updated.connect(_on_ignored_locations_updated)
160 client.hinted_locations_updated.connect(_on_hinted_locations_updated)
161 client.checked_worldports_updated.connect(_on_checked_worldports_updated)
162 client.door_latched.connect(_on_door_latched)
163
164 client.could_not_connect.connect(_client_could_not_connect)
165 client.connect_status.connect(_client_connect_status)
166 client.client_connected.connect(_client_connected)
167
168 add_child(client)
169
170 keyboard = SCRIPT_keyboard.new()
171 add_child(keyboard)
172 client.keyboard_update_received.connect(keyboard.remote_keyboard_updated)
173
174
175func saveSettings():
176 # Save the AP settings to disk.
177 var path = "user://ap_settings"
178 var file = FileAccess.open(path, FileAccess.WRITE)
179
180 var data = [
181 ap_server,
182 ap_user,
183 ap_pass,
184 connection_history,
185 show_compass,
186 show_locations,
187 show_minimap,
188 ]
189 file.store_var(data, true)
190 file.close()
191
192
193func saveLocaldata():
194 # Save the MW/slot specific settings to disk.
195 var dir = DirAccess.open("user://")
196 var folder = "archipelago_data"
197 if not dir.dir_exists(folder):
198 dir.make_dir(folder)
199
200 var file = FileAccess.open(_localdata_file, FileAccess.WRITE)
201
202 var data = [
203 _last_new_item,
204 ]
205 file.store_var(data, true)
206 file.close()
207
208
209func connectToServer():
210 _last_new_item = -1
211 _batch_locations = false
212 _held_locations = []
213 _held_location_scouts = []
214 _location_scouts = {}
215 _letters_setup = false
216 _held_letters = {}
217 _already_connected = false
218
219 client.connectToServer(ap_server, ap_user, ap_pass)
220
221
222func getSaveFileName():
223 return "zzAP_%s_%d" % [client._seed, client._slot]
224
225
226func disconnect_from_ap():
227 _already_connected = false
228
229 var effects = global.get_node("Effects")
230 effects.set_connection_lost(false)
231
232 client.disconnect_from_ap()
233
234
235func get_item_id_for_door(door_id):
236 return _item_locks.get(door_id, null)
237
238
239func _process_item(item, amount):
240 var gamedata = global.get_node("Gamedata")
241
242 var item_id = int(item["id"])
243 var prog_id = null
244 if _inverse_item_locks.has(item_id):
245 for lock in _inverse_item_locks.get(item_id):
246 if lock[1] != amount:
247 continue
248
249 if gamedata.progressive_id_by_ap_id.has(item_id):
250 prog_id = lock[0]
251
252 if gamedata.get_door_map_name(lock[0]) != global.map:
253 continue
254
255 # TODO: fix doors opening from door groups
256 var receivers = gamedata.get_door_receivers(lock[0])
257 var scene = get_tree().get_root().get_node_or_null("scene")
258 if scene != null:
259 for receiver in receivers:
260 var rnode = scene.get_node_or_null(receiver)
261 if rnode != null:
262 rnode.handleTriggered()
263
264 var letter_id = gamedata.letter_id_by_ap_id.get(item_id, null)
265 if letter_id != null:
266 var letter = gamedata.objects.get_letters()[letter_id]
267 if not letter.has_level2() or not letter.get_level2():
268 _process_key_item(letter.get_key(), amount)
269
270 if gamedata.symbol_item_ids.has(item_id):
271 var player = get_tree().get_root().get_node_or_null("scene/player")
272 if player != null:
273 player.evaluate_solvability.emit()
274
275 if item_id == gamedata.objects.get_special_ids()["A Job Well Done"]:
276 update_job_well_done_sign()
277
278 if item_id == gamedata.objects.get_special_ids()["Numbers"] and global.map == "the_fuzzy":
279 global.allow_numbers = true
280
281 if gamedata.map_id_by_rte_ap_id.has(item_id):
282 var rteInner = get_tree().get_root().get_node_or_null(
283 "scene/player/pause_menu/menu/return/rteInner"
284 )
285 if rteInner != null:
286 rteInner.refreshButtons()
287
288 # Show a message about the item if it's new.
289 if int(item["index"]) > _last_new_item:
290 _last_new_item = int(item["index"])
291 saveLocaldata()
292
293 var full_item_name = item["text"]
294 if prog_id != null:
295 var door = gamedata.objects.get_doors()[prog_id]
296 full_item_name = "%s (%s)" % [full_item_name, door.get_name()]
297
298 var message
299 if "sender" in item:
300 message = (
301 "Received %s from %s"
302 % [wrapInItemColorTags(full_item_name, item["flags"]), item["sender"]]
303 )
304 else:
305 message = "Found %s" % wrapInItemColorTags(full_item_name, item["flags"])
306
307 if gamedata.anti_trap_ids.has(item):
308 keyboard.block_letter(gamedata.anti_trap_ids[item])
309
310 global._print(message)
311
312 global.get_node("Messages").showMessage(message)
313
314
315func _process_item_sent_notification(message):
316 var sentMsg = (
317 "Sent %s to %s"
318 % [
319 wrapInItemColorTags(message["item_name"], message["item_flags"]),
320 message["receiver_name"]
321 ]
322 )
323 #if _hinted_locations.has(message["item"]["location"]):
324 # sentMsg += " ([color=#fafad2]Hinted![/color])"
325 global.get_node("Messages").showMessage(sentMsg)
326
327
328func _process_hint_received(message):
329 var is_for = ""
330 if message["self"] == 0:
331 is_for = " for %s" % message["receiver_name"]
332
333 global.get_node("Messages").showMessage(
334 (
335 "Hint: %s%s is on %s"
336 % [
337 wrapInItemColorTags(message["item_name"], message["item_flags"]),
338 is_for,
339 message["location_name"]
340 ]
341 )
342 )
343
344
345func _process_text_message(message):
346 var parts = []
347 for message_part in message:
348 if message_part["type"] == "text":
349 parts.append(message_part["text"])
350 elif message_part["type"] == "player":
351 if message_part["self"] == 1:
352 parts.append("[color=#ee00ee]%s[/color]" % message_part["text"])
353 else:
354 parts.append("[color=#fafad2]%s[/color]" % message_part["text"])
355 elif message_part["type"] == "item":
356 parts.append(wrapInItemColorTags(message_part["text"], int(message_part["flags"])))
357 elif message_part["type"] == "location":
358 parts.append("[color=#00ff7f]%s[/color]" % message_part["text"])
359
360 var textclient_node = global.get_node("Textclient")
361 if textclient_node != null:
362 textclient_node.parse_printjson("".join(parts))
363
364
365func _process_location_scout(location_id, item_name, player_name, flags, for_self):
366 _location_scouts[location_id] = {
367 "item": item_name, "player": player_name, "flags": flags, "for_self": for_self
368 }
369
370 if for_self and flags & 4 != 0:
371 # This is a trap for us, so let's not display it.
372 return
373
374 var gamedata = global.get_node("Gamedata")
375 var map_id = gamedata.map_id_by_name.get(global.map)
376
377 var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null)
378 if letter_id != null:
379 var letter = gamedata.objects.get_letters()[letter_id]
380 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
381 if room.get_map_id() == map_id:
382 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
383 letter.get_path()
384 )
385 if collectable != null:
386 collectable.setScoutedText(item_name)
387
388
389func _on_accessible_locations_updated():
390 var textclient_node = global.get_node("Textclient")
391 if textclient_node != null:
392 textclient_node.update_locations()
393
394
395func _on_checked_locations_updated():
396 var textclient_node = global.get_node("Textclient")
397 if textclient_node != null:
398 textclient_node.update_locations(false)
399
400
401func _on_checked_worldports_updated():
402 var textclient_node = global.get_node("Textclient")
403 if textclient_node != null:
404 textclient_node.update_locations()
405 textclient_node.update_worldports()
406
407
408func _on_ignored_locations_updated(locations):
409 _ignored_locations = locations
410
411 var textclient_node = global.get_node("Textclient")
412 if textclient_node != null:
413 textclient_node.update_locations()
414
415
416func _on_hinted_locations_updated():
417 var textclient_node = global.get_node("Textclient")
418 if textclient_node != null:
419 textclient_node.update_locations()
420
421
422func _on_door_latched(door_id):
423 var gamedata = global.get_node("Gamedata")
424 if gamedata.get_door_map_name(door_id) != global.map:
425 return
426
427 var receivers = gamedata.get_door_receivers(door_id)
428 var scene = get_tree().get_root().get_node_or_null("scene")
429 if scene != null:
430 for receiver in receivers:
431 var rnode = scene.get_node_or_null(receiver)
432 if rnode != null:
433 rnode.handleTriggered()
434
435
436func _client_could_not_connect(message):
437 could_not_connect.emit(message)
438
439 if global.loaded:
440 var effects = global.get_node("Effects")
441 effects.set_connection_lost(true)
442
443 var messages = global.get_node("Messages")
444 messages.showMessage("Connection to multiworld lost.")
445
446
447func _client_connect_status(message):
448 connect_status.emit(message)
449
450
451func _client_connected(slot_data):
452 var effects = global.get_node("Effects")
453 effects.set_connection_lost(false)
454
455 if _already_connected:
456 var messages = global.get_node("Messages")
457 messages.showMessage("Reconnected to multiworld!")
458 return
459
460 _already_connected = true
461
462 var gamedata = global.get_node("Gamedata")
463
464 _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot]
465 _last_new_item = -1
466
467 if FileAccess.file_exists(_localdata_file):
468 var ap_file = FileAccess.open(_localdata_file, FileAccess.READ)
469 var localdata = []
470 if ap_file != null:
471 localdata = ap_file.get_var(true)
472 ap_file.close()
473
474 if typeof(localdata) != TYPE_ARRAY:
475 print("AP localdata file is corrupted")
476 localdata = []
477
478 if localdata.size() > 0:
479 _last_new_item = localdata[0]
480
481 # Read slot data.
482 custom_mint_ending = slot_data.get("custom_mint_ending", "")
483 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0))
484 daedalus_only = bool(slot_data.get("daedalus_only", false))
485 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false))
486 enable_gift_maps = slot_data.get("enable_gift_maps", [])
487 enable_icarus = bool(slot_data.get("enable_icarus", false))
488 endings_requirement = int(slot_data.get("endings_requirement", 0))
489 fast_travel_access = int(slot_data.get("fast_travel_access", 0))
490 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
491 masteries_requirement = int(slot_data.get("masteries_requirement", 0))
492 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
493 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
494 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false))
495 shuffle_letters = int(slot_data.get("shuffle_letters", 0))
496 shuffle_symbols = bool(slot_data.get("shuffle_symbols", false))
497 shuffle_worldports = bool(slot_data.get("shuffle_worldports", false))
498 strict_cyan_ending = bool(slot_data.get("strict_cyan_ending", false))
499 strict_purple_ending = bool(slot_data.get("strict_purple_ending", false))
500 victory_condition = int(slot_data.get("victory_condition", 0))
501
502 if slot_data.has("version"):
503 var version_msg = slot_data["version"]
504 apworld_version = [int(version_msg[0]), int(version_msg[1]), 0]
505 if version_msg.size() > 2:
506 apworld_version[2] = int(version_msg[2])
507
508 port_pairings.clear()
509 if slot_data.has("port_pairings"):
510 var raw_pp = slot_data.get("port_pairings")
511
512 for p1 in raw_pp.keys():
513 port_pairings[gamedata.port_id_by_ap_id[int(p1)]] = gamedata.port_id_by_ap_id[int(
514 raw_pp[p1]
515 )]
516
517 rte_mapping.clear()
518 if slot_data.has("rte"):
519 rte_mapping = slot_data.get("rte")
520
521 slot_rng = RandomNumberGenerator.new()
522 slot_rng.seed = int(slot_data.get("seed", 0))
523
524 music_mapping.clear()
525 if bool(slot_data.get("shuffle_music", false)):
526 for map_name in global.reserved_scenes:
527 var track_index = slot_rng.randi_range(0, musicPlayer.all_tracks.size() - 1)
528 music_mapping[map_name] = musicPlayer.all_tracks.keys()[track_index]
529
530 # Set up item locks.
531 _item_locks = {}
532
533 if shuffle_doors or daedalus_only:
534 for door in gamedata.objects.get_doors():
535 if (
536 door.get_type() != gamedata.SCRIPT_proto.DoorType.STANDARD
537 and door.get_type() != gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
538 ):
539 continue
540
541 if (
542 not shuffle_doors
543 and not (
544 daedalus_only
545 and door.has_daedalus_only_always_item()
546 and door.get_daedalus_only_always_item()
547 )
548 ):
549 continue
550
551 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
552
553 if shuffle_doors:
554 for progressive in gamedata.objects.get_progressives():
555 for i in range(0, progressive.get_doors().size()):
556 var door = gamedata.objects.get_doors()[progressive.get_doors()[i]]
557 _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1]
558
559 for door_group in gamedata.objects.get_door_groups():
560 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR:
561 if shuffle_worldports:
562 continue
563 elif door_group.get_type() != gamedata.SCRIPT_proto.DoorGroupType.SHUFFLE_GROUP:
564 continue
565
566 if (
567 not shuffle_doors
568 and not (
569 daedalus_only
570 and door_group.has_daedalus_only_always_item()
571 and door_group.get_daedalus_only_always_item()
572 )
573 ):
574 continue
575
576 for door in door_group.get_doors():
577 _item_locks[door] = [door_group.get_ap_id(), 1]
578
579 if shuffle_control_center_colors:
580 for door in gamedata.objects.get_doors():
581 if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR:
582 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
583
584 for door_group in gamedata.objects.get_door_groups():
585 if (
586 door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR
587 and not shuffle_worldports
588 ):
589 for door in door_group.get_doors():
590 _item_locks[door] = [door_group.get_ap_id(), 1]
591
592 if shuffle_gallery_paintings:
593 for door in gamedata.objects.get_doors():
594 if door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING:
595 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
596
597 if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM:
598 for door_group in gamedata.objects.get_door_groups():
599 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS:
600 for door in door_group.get_doors():
601 if not _item_locks.has(door):
602 _item_locks[door] = [door_group.get_ap_id(), 1]
603
604 # Create a reverse item locks map for processing items.
605 _inverse_item_locks = {}
606
607 for door_id in _item_locks.keys():
608 var lock = _item_locks.get(door_id)
609
610 if not _inverse_item_locks.has(lock[0]):
611 _inverse_item_locks[lock[0]] = []
612
613 _inverse_item_locks[lock[0]].append([door_id, lock[1]])
614
615 if shuffle_worldports:
616 var textclient = global.get_node("Textclient")
617 textclient.setup_worldports()
618
619 ap_connected.emit()
620
621
622func start_batching_locations():
623 _batch_locations = true
624
625
626func send_location(loc_id):
627 if client._checked_locations.has(loc_id):
628 return
629
630 if _batch_locations:
631 _held_locations.append(loc_id)
632 else:
633 client.sendLocation(loc_id)
634
635
636func scout_location(loc_id):
637 if _location_scouts.has(loc_id):
638 return _location_scouts.get(loc_id)
639
640 if _batch_locations:
641 _held_location_scouts.append(loc_id)
642 else:
643 client.scoutLocation(loc_id)
644
645 return null
646
647
648func stop_batching_locations():
649 _batch_locations = false
650
651 if not _held_locations.is_empty():
652 client.sendLocations(_held_locations)
653 _held_locations.clear()
654
655 if not _held_location_scouts.is_empty():
656 client.scoutLocations(_held_location_scouts)
657 _held_location_scouts.clear()
658
659
660func colorForItemType(flags):
661 var int_flags = int(flags)
662 if int_flags & 1: # progression
663 if int_flags & 2: # proguseful
664 return "#f0d200"
665 else:
666 return "#bc51e0"
667 elif int_flags & 2: # useful
668 return "#2b67ff"
669 elif int_flags & 4: # trap
670 return "#d63a22"
671 else: # filler
672 return "#14de9e"
673
674
675func wrapInItemColorTags(text, flags):
676 var int_flags = int(flags)
677 if int_flags & 1 and int_flags & 2: # proguseful
678 return "[rainbow]%s[/rainbow]" % text
679 else:
680 return "[color=%s]%s[/color]" % [colorForItemType(flags), text]
681
682
683func get_letter_behavior(key, level2):
684 if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED:
685 return kLETTER_BEHAVIOR_UNLOCKED
686
687 if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters):
688 if level2:
689 if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN:
690 return kLETTER_BEHAVIOR_VANILLA
691 else:
692 return kLETTER_BEHAVIOR_ITEM
693 else:
694 return kLETTER_BEHAVIOR_UNLOCKED
695
696 if not level2 and ["h", "i", "n", "t"].has(key):
697 # This differs from the equivalent function in the apworld. Logically it is
698 # the same as UNLOCKED since they are in the starting room, but VANILLA
699 # means the player still has to actually pick up the letters.
700 return kLETTER_BEHAVIOR_VANILLA
701
702 if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE:
703 return kLETTER_BEHAVIOR_ITEM
704
705 return kLETTER_BEHAVIOR_VANILLA
706
707
708func setup_keys():
709 keyboard.load_seed()
710
711 _letters_setup = true
712
713 for k in _held_letters.keys():
714 _process_key_item(k, _held_letters[k])
715
716 _held_letters.clear()
717
718
719func _process_key_item(key, level):
720 if not _letters_setup:
721 _held_letters[key] = max(_held_letters.get(key, 0), level)
722 return
723
724 if shuffle_letters == kSHUFFLE_LETTERS_ITEM_CYAN:
725 level += 1
726
727 keyboard.collect_remote_letter(key, level)
728
729
730func update_job_well_done_sign():
731 if global.map != "daedalus":
732 return
733
734 var gamedata = global.get_node("Gamedata")
735 var job_item = gamedata.objects.get_special_ids()["A Job Well Done"]
736 var jobs_done = client.getItemAmount(job_item)
737
738 var sign2 = get_tree().get_root().get_node_or_null("scene/Meshes/Miscellaneous/sign2")
739 var sign3 = get_tree().get_root().get_node_or_null("scene/Meshes/Miscellaneous/sign3")
740
741 if sign2 != null and sign3 != null:
742 if jobs_done == 0:
743 sign2.text = "what are you doing"
744 sign3.text = "?"
745 elif jobs_done == 1:
746 sign2.text = "a job well done"
747 sign3.text = "is its own reward"
748 else:
749 sign2.text = "%d jobs well done" % jobs_done
750 sign3.text = "are their own reward"
751
752 sign2.get_node("MeshInstance3D").mesh.text = sign2.text
753 sign3.get_node("MeshInstance3D").mesh.text = sign3.text
754
755
756func toggle_ignored_location(loc_id):
757 if loc_id in _ignored_locations:
758 client.removeIgnoredLocation(loc_id)
759 else:
760 client.addIgnoredLocation(loc_id)
761
762
763func get_map_script(map_name):
764 if !_map_scripts.has(map_name):
765 var runtime = global.get_node("Runtime")
766 var script_path = "maps/%s.gd" % map_name
767 if runtime.path_exists(script_path):
768 var script = runtime.load_script(script_path)
769 _map_scripts[map_name] = script.new()
770 else:
771 _map_scripts[map_name] = null
772
773 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..8e919ab --- /dev/null +++ b/apworld/client/maps/control_center.gd
@@ -0,0 +1,139 @@
1const kALL_MASTERIES = 19
2
3
4func on_map_load(root):
5 var ap = global.get_node("Archipelago")
6
7 # Remove the door blocking the trophy case.
8 root.get_node("/root/scene/Components/Doors/entry_18").queue_free()
9
10 # Set up mastery listeners for extra maps.
11 _set_up_mastery_listener(root, "advanced")
12 _set_up_mastery_listener(root, "charismatic")
13 _set_up_mastery_listener(root, "crystalline")
14 _set_up_mastery_listener(root, "fuzzy")
15 _set_up_mastery_listener(root, "icarus")
16 _set_up_mastery_listener(root, "stellar")
17
18 if ap.endings_requirement != 12 or ap.masteries_requirement != 0:
19 # Set up listeners for the potential White Ending requirements.
20 var merging_prefab = preload("res://objects/nodes/listeners/mergingListener.tscn")
21
22 var old_door = root.get_node("/root/scene/Components/Doors/entry_19")
23 var new_door = old_door.duplicate()
24 new_door.name = "entry_19_new"
25 new_door.senders.clear()
26 new_door.senderGroup.clear()
27 new_door.excludeSenders.clear()
28
29 if ap.endings_requirement == 12:
30 new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners"))
31 elif ap.endings_requirement > 0:
32 if ap.masteries_requirement == 0:
33 new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners"))
34 new_door.complete_at = ap.endings_requirement
35 else:
36 var endings_merge = merging_prefab.instantiate()
37 endings_merge.name = "EndingsMerge"
38 endings_merge.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners"))
39 endings_merge.complete_at = ap.endings_requirement
40 root.get_node("/root/scene/Components").add_child.call_deferred(endings_merge)
41 new_door.senders.append(NodePath("/root/scene/Components/EndingsMerge"))
42
43 if ap.masteries_requirement == kALL_MASTERIES:
44 new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/MasteryListeners"))
45 new_door.excludeSenders.append(
46 NodePath("/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite")
47 )
48 elif ap.masteries_requirement > 0:
49 if ap.endings_requirement == 0:
50 new_door.senderGroup.append(
51 NodePath("/root/scene/Meshes/Trophies/MasteryListeners")
52 )
53 new_door.excludeSenders.append(
54 NodePath(
55 "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite"
56 )
57 )
58 new_door.complete_at = ap.masteries_requirement
59 else:
60 var masteries_merge = merging_prefab.instantiate()
61 masteries_merge.name = "MasteriesMerge"
62 masteries_merge.senderGroup.append(
63 NodePath("/root/scene/Meshes/Trophies/MasteryListeners")
64 )
65 masteries_merge.excludeSenders.append(
66 NodePath(
67 "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite"
68 )
69 )
70 masteries_merge.complete_at = ap.masteries_requirement
71 root.get_node("/root/scene/Components").add_child.call_deferred(masteries_merge)
72 new_door.senders.append(NodePath("/root/scene/Components/MasteriesMerge"))
73
74 old_door.queue_free()
75 root.get_node("/root/scene/Components/Doors").add_child.call_deferred(new_door)
76
77 # Display White Ending requirements.
78 var ending_count = 0
79 var mastery_count = 0
80 for key in unlocks.data:
81 if unlocks.data[key] == "unlocked":
82 if key.ends_with("_ending") and key != "free_ending":
83 ending_count += 1
84 elif key.ends_with("_mastery"):
85 mastery_count += 1
86
87 var sign_prefab = preload("res://objects/nodes/sign.tscn")
88 var sign1 = sign_prefab.instantiate()
89 sign1.position = Vector3(87.5, 5, -42.01)
90 sign1.text = "Endings: %d/%d" % [ending_count, ap.endings_requirement]
91 root.get_node("/root/scene").add_child.call_deferred(sign1)
92
93 var sign2 = sign_prefab.instantiate()
94 sign2.position = Vector3(87.5, 5, -15.99)
95 sign2.rotation_degrees.y = 180
96 sign2.text = "Masteries: %d/%d" % [mastery_count, ap.masteries_requirement]
97 root.get_node("/root/scene").add_child.call_deferred(sign2)
98
99 # Handle custom Mint Ending.
100 if ap.custom_mint_ending != "":
101 var panel_prefab = preload("res://objects/nodes/panel.tscn")
102 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
103
104 var mint_ending = root.get_node("/root/scene/Components/Endings/mint_ending")
105
106 var mint_panel = panel_prefab.instantiate()
107 mint_panel.name = "mint_panel"
108 mint_panel.clue = ap.custom_mint_ending
109 mint_panel.symbol = ""
110 mint_panel.answer = ap.custom_mint_ending
111 mint_panel.position = Vector3(-63, 3, -29)
112 mint_panel.rotation_degrees = Vector3(-45, 90, 0)
113 root.get_node("/root/scene").add_child.call_deferred(mint_panel)
114
115 var mint_tpl = tpl_prefab.instantiate()
116 mint_tpl.name = "mint_tpl"
117 mint_tpl.teleport_point = mint_ending.position
118 mint_tpl.teleport_rotate = mint_ending.rotation_degrees
119 mint_tpl.target_path = mint_ending
120 mint_tpl.senders.append(NodePath("/root/scene/mint_panel"))
121 root.get_node("/root/scene").add_child.call_deferred(mint_tpl)
122
123 var mint_tpl2 = tpl_prefab.instantiate()
124 mint_tpl2.name = "mint_tpl2"
125 mint_tpl2.teleport_point = Vector3(0, -1000, 0)
126 mint_tpl2.target_path = mint_panel
127 mint_tpl2.senders.append(NodePath("/root/scene/mint_panel"))
128 root.get_node("/root/scene").add_child.call_deferred(mint_tpl2)
129
130 mint_ending.position.y = -1000
131
132
133func _set_up_mastery_listener(root, name):
134 var prefab = preload("res://objects/nodes/listeners/unlockReaderListener.tscn")
135 var url = prefab.instantiate()
136 url.name = "unlockReaderListenerMastery_%s" % name
137 url.key = "%s_mastery" % name
138 url.value = "unlocked"
139 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_gallery.gd b/apworld/client/maps/the_gallery.gd new file mode 100644 index 0000000..6731c16 --- /dev/null +++ b/apworld/client/maps/the_gallery.gd
@@ -0,0 +1,7 @@
1func on_map_load(root):
2 var ap = global.get_node("Archipelago")
3
4 if ap.daedalus_only:
5 # Prevent QUESTION door from opening on Daedalus Only mode.
6 var door = root.get_node("/root/scene/Components/Doors/entry_2")
7 door.animate_distance_y = 0
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 82fdbc4..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))
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/client/Archipelago/door.gd b/apworld/client/paintingAuto.gd index 49f5728..553c2c9 100644 --- a/client/Archipelago/door.gd +++ b/apworld/client/paintingAuto.gd
@@ -1,4 +1,4 @@
1extends "res://scripts/nodes/door.gd" 1extends "res://scripts/nodes/paintingAuto.gd"
2 2
3var item_id 3var item_id
4var item_amount 4var item_amount
@@ -28,16 +28,13 @@ func _ready():
28 28
29 call_deferred("_readier") 29 call_deferred("_readier")
30 30
31 if global.map == "the_sun_temple":
32 if name == "spe_EndPlatform" or name == "spe_entry_2":
33 senders = [NodePath("/root/scene/Panels/EndCheck_dog")]
34
35 if global.map == "the_parthenon":
36 if name == "spe_entry_1":
37 senders = [NodePath("/root/scene/Panels/EndCheck_dog")]
38
39 super._ready() 31 super._ready()
40 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
41 38
42func _readier(): 39func _readier():
43 var ap = global.get_node("Archipelago") 40 var ap = global.get_node("Archipelago")
diff --git a/client/Archipelago/panel.gd b/apworld/client/panel.gd index fdaaf0e..2cef28e 100644 --- a/client/Archipelago/panel.gd +++ b/apworld/client/panel.gd
@@ -29,8 +29,8 @@ func _ready():
29 checkSymbolSolvable() 29 checkSymbolSolvable()
30 30
31 if not symbol_solvable: 31 if not symbol_solvable:
32 get_tree().get_root().get_node("scene/player").connect( 32 get_tree().get_root().get_node("scene/player").evaluate_solvability.connect(
33 "evaluate_solvability", evaluateSolvability 33 evaluateSolvability
34 ) 34 )
35 35
36 36
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/apworld/client/player.gd b/apworld/client/player.gd new file mode 100644 index 0000000..dabc15d --- /dev/null +++ b/apworld/client/player.gd
@@ -0,0 +1,219 @@
1extends "res://scripts/nodes/player.gd"
2
3signal evaluate_solvability
4
5var compass
6
7
8func _ready():
9 var khl_script = load("res://scripts/nodes/keyHolderListener.gd")
10
11 var pause_menu = get_node("pause_menu")
12 pause_menu.layer = 3
13
14 var ap = global.get_node("Archipelago")
15 var gamedata = global.get_node("Gamedata")
16 var map_id = gamedata.map_id_by_name.get(global.map)
17 var map_data = gamedata.objects.get_maps()[map_id]
18
19 compass = global.get_node("Compass")
20 compass.visible = ap.show_compass
21
22 ap.start_batching_locations()
23
24 # Run map-specific initialization.
25 var map_script = ap.get_map_script(global.map)
26 if map_script != null:
27 map_script.on_map_load(get_tree().get_root())
28
29 ap.update_job_well_done_sign()
30
31 # Set up the RTE trigger, if there is one.
32 if map_data.has_rte_trigger_pos():
33 var oneShotListener_prefab = preload("res://objects/nodes/listeners/oneShotListener.tscn")
34 var triggerArea_prefab = preload("res://objects/nodes/triggerArea.tscn")
35 var unlockSetterListener_prefab = preload(
36 "res://objects/nodes/listeners/unlockSetterListener.tscn"
37 )
38
39 var triggerArea = triggerArea_prefab.instantiate()
40 triggerArea.name = "rte_triggerArea"
41 triggerArea.position = gamedata.vec3d_to_vector3(map_data.get_rte_trigger_pos())
42 triggerArea.scale = gamedata.vec3d_to_vector3(map_data.get_rte_trigger_scale())
43 get_parent().add_child.call_deferred(triggerArea)
44
45 var osl = oneShotListener_prefab.instantiate()
46 osl.name = "rte_osl"
47 osl.senders.append(NodePath("/root/scene/rte_triggerArea"))
48 get_parent().add_child.call_deferred(osl)
49
50 var usl = unlockSetterListener_prefab.instantiate()
51 usl.name = "rte_usl"
52 usl.key = "rte_%s" % global.map
53 usl.value = "unlocked"
54 usl.senders.append(NodePath("/root/scene/rte_osl"))
55 get_parent().add_child.call_deferred(usl)
56
57 # Set up door locations.
58 for door in gamedata.objects.get_doors():
59 if door.get_map_id() != map_id:
60 continue
61
62 if not door.has_ap_id():
63 continue
64
65 if (
66 not (door.has_legacy_location() and door.get_legacy_location())
67 and (
68 door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
69 or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING
70 or door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR
71 )
72 ):
73 continue
74
75 var locationListener = ap.SCRIPT_locationListener.new()
76 locationListener.location_id = door.get_ap_id()
77 locationListener.name = "locationListener_%d" % door.get_ap_id()
78
79 for panel_ref in door.get_panels():
80 var panel_data = gamedata.objects.get_panels()[panel_ref.get_panel()]
81 var panel_path = panel_data.get_path()
82
83 if panel_ref.has_answer():
84 for proxy in panel_data.get_proxies():
85 if proxy.get_answer() == panel_ref.get_answer():
86 panel_path = proxy.get_path()
87 break
88
89 locationListener.senders.append(NodePath("/root/scene/" + panel_path))
90
91 for keyholder_ref in door.get_keyholders():
92 var keyholder_data = gamedata.objects.get_keyholders()[keyholder_ref.get_keyholder()]
93
94 var khl = khl_script.new()
95 khl.name = (
96 "location_%d_keyholder_%d" % [door.get_ap_id(), keyholder_ref.get_keyholder()]
97 )
98 khl.answer = keyholder_ref.get_key()
99 khl.senders.append(NodePath("/root/scene/" + keyholder_data.get_path()))
100 get_parent().add_child.call_deferred(khl)
101
102 locationListener.senders.append(NodePath("../" + khl.name))
103
104 for sender in door.get_senders():
105 locationListener.senders.append(NodePath("/root/scene/" + sender))
106
107 if door.has_complete_at():
108 locationListener.complete_at = door.get_complete_at()
109
110 get_parent().add_child.call_deferred(locationListener)
111
112 # Set up letter locations.
113 for letter in gamedata.objects.get_letters():
114 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
115 if room.get_map_id() != map_id:
116 continue
117
118 var locationListener = ap.SCRIPT_locationListener.new()
119 locationListener.location_id = letter.get_ap_id()
120 locationListener.name = "locationListener_%d" % letter.get_ap_id()
121 locationListener.senders.append(NodePath("/root/scene/" + letter.get_path()))
122
123 get_parent().add_child.call_deferred(locationListener)
124
125 if (
126 ap.get_letter_behavior(letter.get_key(), letter.has_level2() and letter.get_level2())
127 != ap.kLETTER_BEHAVIOR_VANILLA
128 ):
129 var scout = ap.scout_location(letter.get_ap_id())
130 if scout != null and not (scout["for_self"] and scout["flags"] & 4 != 0):
131 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
132 letter.get_path()
133 )
134 if collectable != null:
135 collectable.setScoutedText.call_deferred(scout["item"])
136
137 # Set up mastery locations.
138 for mastery in gamedata.objects.get_masteries():
139 var room = gamedata.objects.get_rooms()[mastery.get_room_id()]
140 if room.get_map_id() != map_id:
141 continue
142
143 var locationListener = ap.SCRIPT_locationListener.new()
144 locationListener.location_id = mastery.get_ap_id()
145 locationListener.name = "locationListener_%d" % mastery.get_ap_id()
146 locationListener.senders.append(NodePath("/root/scene/" + mastery.get_path()))
147
148 get_parent().add_child.call_deferred(locationListener)
149
150 # Set up ending locations.
151 for ending in gamedata.objects.get_endings():
152 var room = gamedata.objects.get_rooms()[ending.get_room_id()]
153 if room.get_map_id() != map_id:
154 continue
155
156 var locationListener = ap.SCRIPT_locationListener.new()
157 locationListener.location_id = ending.get_ap_id()
158 locationListener.name = "locationListener_%d" % ending.get_ap_id()
159 locationListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
160
161 get_parent().add_child.call_deferred(locationListener)
162
163 if ap.kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name():
164 var victoryListener = ap.SCRIPT_victoryListener.new()
165 victoryListener.name = "victoryListener"
166 victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
167
168 get_parent().add_child.call_deferred(victoryListener)
169
170 # Set up keyholder locations, in keyholder sanity.
171 if ap.keyholder_sanity:
172 for keyholder in gamedata.objects.get_keyholders():
173 if not keyholder.has_key():
174 continue
175
176 var room = gamedata.objects.get_rooms()[keyholder.get_room_id()]
177 if room.get_map_id() != map_id:
178 continue
179
180 var locationListener = ap.SCRIPT_locationListener.new()
181 locationListener.location_id = keyholder.get_ap_id()
182 locationListener.name = "locationListener_%d" % keyholder.get_ap_id()
183
184 var khl = khl_script.new()
185 khl.name = "location_%d_keyholder" % keyholder.get_ap_id()
186 khl.answer = keyholder.get_key()
187 khl.senders.append(NodePath("/root/scene/" + keyholder.get_path()))
188 get_parent().add_child.call_deferred(khl)
189
190 locationListener.senders.append(NodePath("../" + khl.name))
191
192 get_parent().add_child.call_deferred(locationListener)
193
194 var minimap = ap.SCRIPT_minimap.new()
195 minimap.name = "Minimap"
196 minimap.visible = ap.show_minimap
197 get_parent().add_child.call_deferred(minimap)
198
199 if ap.music_mapping.has(global.map):
200 var song_setter = get_node_or_null("/root/scene/songSetter")
201 if song_setter:
202 song_setter.song_name = ap.music_mapping[global.map]
203 else:
204 var song_setter_prefab = preload("res://objects/nodes/songSetter.tscn")
205 song_setter = song_setter_prefab.instantiate()
206 song_setter.name = "songSetter"
207 song_setter.song_name = ap.music_mapping[global.map]
208 get_parent().add_child.call_deferred(song_setter)
209
210 super._ready()
211
212 await get_tree().process_frame
213 await get_tree().process_frame
214
215 ap.stop_batching_locations()
216
217
218func _process(_dt):
219 compass.update_rotation(global_rotation.y)
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/rteMenu.gd b/apworld/client/rteMenu.gd new file mode 100644 index 0000000..519f09f --- /dev/null +++ b/apworld/client/rteMenu.gd
@@ -0,0 +1,67 @@
1extends "res://scripts/ui/rteMenu.gd"
2
3var buttons = []
4
5
6func _readier():
7 var ap = global.get_node("Archipelago")
8 if ap.daedalus_only:
9 get_node("rte_the_entry").hide()
10 get_node("rte_daedalus").show()
11
12 switcher.preload_map("res://objects/scenes/daedalus.tscn")
13 elif !ap.rte_mapping.is_empty():
14 buttons = [$rte_the_plaza, $rte_the_gallery, $rte_daedalus, $rte_control_center]
15 for i in range(4):
16 buttons[i].name = "button_%d" % i
17 for i in range(4):
18 _setupButton(buttons[i], ap.rte_mapping[i])
19
20 refreshButtons()
21 else:
22 super()._readier()
23
24
25func _setupButton(button, map_name):
26 switcher.preload_map("res://objects/scenes/%s.tscn" % map_name)
27
28 button.hide()
29 button.text = map_name.replace("_", " ")
30 button.name = "rte_%s" % map_name
31 button.autowrap_mode = TextServer.AUTOWRAP_WORD
32
33 var ap = global.get_node("Archipelago")
34 if (
35 ap.fast_travel_access == ap.kFAST_TRAVEL_ACCESS_VANILLA
36 and !unlocks.data.has("rte_%s" % map_name)
37 ):
38 unlocks.data["rte_%s" % map_name] = ""
39
40
41func refreshButtons():
42 var ap = global.get_node("Archipelago")
43 if ap.rte_mapping.is_empty():
44 return
45
46 for i in range(4):
47 if _shouldShowButton(ap.rte_mapping[i]):
48 buttons[i].show()
49 else:
50 buttons[i].hide()
51
52
53func _shouldShowButton(map_name):
54 var ap = global.get_node("Archipelago")
55
56 if ap.fast_travel_access == ap.kFAST_TRAVEL_ACCESS_VANILLA:
57 return unlocks.data["rte_%s" % map_name] == "unlocked"
58 elif ap.fast_travel_access == ap.kFAST_TRAVEL_ACCESS_UNLOCKED:
59 return true
60 elif ap.fast_travel_access == ap.kFAST_TRAVEL_ACCESS_ITEMS:
61 var gamedata = global.get_node("Gamedata")
62 var map_id = gamedata.map_id_by_name[map_name]
63 var rte_ap_id = gamedata.objects.get_maps()[map_id].get_rte_ap_id()
64
65 return ap.client.hasItem(rte_ap_id)
66
67 return false
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/client/Archipelago/saver.gd b/apworld/client/saver.gd index 44bc179..44bc179 100644 --- a/client/Archipelago/saver.gd +++ b/apworld/client/saver.gd
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/teleport.gd b/apworld/client/teleport.gd index 428d50b..428d50b 100644 --- a/client/Archipelago/teleport.gd +++ b/apworld/client/teleport.gd
diff --git a/client/Archipelago/teleportListener.gd b/apworld/client/teleportListener.gd index 6f363af..6f363af 100644 --- a/client/Archipelago/teleportListener.gd +++ b/apworld/client/teleportListener.gd
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/client/Archipelago/visibilityListener.gd b/apworld/client/visibilityListener.gd index 5ea17a0..5ea17a0 100644 --- a/client/Archipelago/visibilityListener.gd +++ b/apworld/client/visibilityListener.gd
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/client/Archipelago/worldportListener.gd b/apworld/client/worldportListener.gd index 5c2faff..4cff8e9 100644 --- a/client/Archipelago/worldportListener.gd +++ b/apworld/client/worldportListener.gd
@@ -2,7 +2,7 @@ extends "res://scripts/nodes/listeners/worldportListener.gd"
2 2
3 3
4func handleTriggered(): 4func handleTriggered():
5 if exit == "menus/credits": 5 if exit.begins_with("menus/credits"):
6 return 6 return
7 7
8 super.handleTriggered() 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/items.py b/apworld/items.py index 28158c3..143ccb1 100644 --- a/apworld/items.py +++ b/apworld/items.py
@@ -5,6 +5,8 @@ from BaseClasses import Item
5class Lingo2Item(Item): 5class Lingo2Item(Item):
6 game: str = "Lingo 2" 6 game: str = "Lingo 2"
7 7
8 is_letter: bool
9
8 10
9SYMBOL_ITEMS: dict[data_pb2.PuzzleSymbol, str] = { 11SYMBOL_ITEMS: dict[data_pb2.PuzzleSymbol, str] = {
10 data_pb2.PuzzleSymbol.SUN: "Sun Symbol", 12 data_pb2.PuzzleSymbol.SUN: "Sun Symbol",
@@ -28,4 +30,5 @@ SYMBOL_ITEMS: dict[data_pb2.PuzzleSymbol, str] = {
28 data_pb2.PuzzleSymbol.QUESTION: "Question Symbol", 30 data_pb2.PuzzleSymbol.QUESTION: "Question Symbol",
29} 31}
30 32
31ANTI_COLLECTABLE_TRAPS: list[str] = [f"Anti {letter}" for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"] 33ALL_LETTERS_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
34ANTI_COLLECTABLE_TRAPS: list[str] = [f"Anti {letter}" for letter in ALL_LETTERS_UPPER]
diff --git a/apworld/locations.py b/apworld/locations.py index 108decb..c92215e 100644 --- a/apworld/locations.py +++ b/apworld/locations.py
@@ -1,5 +1,112 @@
1from BaseClasses import Location 1from enum import Enum
2from typing import TYPE_CHECKING
3
4from BaseClasses import Location, Item, Region, CollectionState, Entrance
5from .items import Lingo2Item
6from .rules import AccessRequirements
7
8if TYPE_CHECKING:
9 from . import Lingo2World
10
11
12class LetterPlacementType(Enum):
13 ANY = 0
14 DISALLOW = 1
15 FORCE = 2
16
17
18def get_required_regions(reqs: AccessRequirements, world: "Lingo2World",
19 regions: dict[str, Region] | None) -> list[Region]:
20 # Replace required rooms with regions for the top level requirement, which saves looking up the regions during rule
21 # checking.
22 if regions is not None:
23 return [regions[room_name] for room_name in reqs.rooms]
24 else:
25 return [world.multiworld.get_region(room_name, world.player) for room_name in reqs.rooms]
2 26
3 27
4class Lingo2Location(Location): 28class Lingo2Location(Location):
5 game: str = "Lingo 2" 29 game: str = "Lingo 2"
30
31 reqs: AccessRequirements | None
32 world: "Lingo2World"
33 required_regions: list[Region]
34
35 port_id: int
36 goal: bool
37 letter_placement_type: LetterPlacementType
38
39 @classmethod
40 def non_event_location(cls, world: "Lingo2World", code: int, region: Region):
41 result = cls(world.player, world.static_logic.location_id_to_name[code], code, region)
42 result.reqs = None
43 result.world = world
44 result.required_regions = []
45
46 return result
47
48 @classmethod
49 def event_location(cls, world: "Lingo2World", name: str, region: Region):
50 result = cls(world.player, name, None, region)
51 result.reqs = None
52 result.world = world
53 result.required_regions = []
54
55 return result
56
57 def set_access_rule(self, reqs: AccessRequirements, regions: dict[str, Region] | None):
58 self.reqs = reqs
59 self.required_regions = get_required_regions(reqs, self.world, regions)
60 self.access_rule = self._l2_access_rule
61
62 def _l2_access_rule(self, state: CollectionState) -> bool:
63 if self.reqs is not None and not self.reqs.check_access(state, self.world):
64 return False
65
66 if not all(state.can_reach(region) for region in self.required_regions):
67 return False
68
69 return True
70
71 def set_up_letter_rule(self, lpt: LetterPlacementType):
72 self.letter_placement_type = lpt
73 self.item_rule = self._l2_item_rule
74
75 def _l2_item_rule(self, item: Item) -> bool:
76 if not isinstance(item, Lingo2Item):
77 return True
78
79 if self.letter_placement_type == LetterPlacementType.FORCE:
80 return item.is_letter
81 elif self.letter_placement_type == LetterPlacementType.DISALLOW:
82 return not item.is_letter
83
84 return True
85
86
87class Lingo2Entrance(Entrance):
88 reqs: AccessRequirements | None
89 world: "Lingo2World"
90 required_regions: list[Region]
91
92 def __init__(self, world: "Lingo2World", description: str, region: Region):
93 super().__init__(world.player, description, region)
94
95 self.reqs = None
96 self.world = world
97 self.required_regions = []
98
99 def set_access_rule(self, reqs: AccessRequirements, regions: dict[str, Region] | None):
100 self.reqs = reqs
101 self.required_regions = get_required_regions(reqs, self.world, regions)
102 self.access_rule = self._l2_access_rule
103
104 def _l2_access_rule(self, state: CollectionState) -> bool:
105 if self.reqs is not None and not self.reqs.check_access(state, self.world):
106 return False
107
108 if not all(state.can_reach(region) for region in self.required_regions):
109 return False
110
111 return True
112
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 3646eea..c1eab33 100644 --- a/apworld/options.py +++ b/apworld/options.py
@@ -1,6 +1,6 @@
1from dataclasses import dataclass 1from dataclasses import dataclass
2 2
3from Options import PerGameCommonOptions, Toggle, Choice, DefaultOnToggle, Range 3from Options import PerGameCommonOptions, Toggle, Choice, DefaultOnToggle, Range, OptionSet, FreeText
4 4
5 5
6class ShuffleDoors(DefaultOnToggle): 6class ShuffleDoors(DefaultOnToggle):
@@ -44,6 +44,17 @@ class ShuffleLetters(Choice):
44 option_item_cyan = 4 44 option_item_cyan = 4
45 45
46 46
47class RestrictLetterPlacements(Toggle):
48 """
49 If enabled, letter items will be shuffled among letter locations in your local world. Shuffle Letters must be set to
50 Progressive or Item Cyan for this to be useful.
51
52 WARNING: This option may slow down generation. Additionally, it is only reliable with Shuffle Letters set to Item
53 Cyan. When set to Progressive, Shuffle Doors and Shuffle Symbols must be turned off.
54 """
55 display_name = "Restrict Letter Placements"
56
57
47class ShuffleSymbols(Toggle): 58class ShuffleSymbols(Toggle):
48 """ 59 """
49 If enabled, 19 items will be added to the pool, representing the different symbols that can appear on a panel. 60 If enabled, 19 items will be added to the pool, representing the different symbols that can appear on a panel.
@@ -52,6 +63,15 @@ class ShuffleSymbols(Toggle):
52 display_name = "Shuffle Symbols" 63 display_name = "Shuffle Symbols"
53 64
54 65
66class ShuffleWorldports(Toggle):
67 """
68 Randomizes the connections between maps. This affects worldports only, which are the loading zones you walk into in
69 order to change maps. This does not affect paintings, panels that teleport you, or certain other special connections
70 like the one between The Shop and Control Center.
71 """
72 display_name = "Shuffle Worldports"
73
74
55class KeyholderSanity(Toggle): 75class KeyholderSanity(Toggle):
56 """ 76 """
57 If enabled, 26 locations will be created for placing each key into its respective Green Ending keyholder. 77 If enabled, 26 locations will be created for placing each key into its respective Green Ending keyholder.
@@ -82,6 +102,72 @@ class CyanDoorBehavior(Choice):
82 option_item = 2 102 option_item = 2
83 103
84 104
105class ShuffleFastTravel(Toggle):
106 """If enabled, the list of maps you can fast travel to is randomized, except for The Entry, which is always
107 accessible."""
108 display_name = "Shuffle Fast Travel"
109
110
111class FastTravelAccess(Choice):
112 """
113 Controls how the fast travel buttons on the pause menu work.
114
115 - **Vanilla**: You can only fast travel to maps once you have been to them and stepped foot in the general area that
116 the warp would place you. This option means that fast travel has no impact on logic.
117 - **Unlocked**: All five fast travel maps will be available from the start.
118 - **Items**: Only The Entry is available from the start. The other fast travel buttons are locked behind items.
119 """
120 display_name = "Fast Travel Access"
121 option_vanilla = 0
122 option_unlocked = 1
123 option_items = 2
124
125
126class EnableIcarus(Toggle):
127 """
128 Controls whether Icarus is randomized. If disabled, which is the default, no locations or items will be created for
129 it, and its worldport will not be shuffled when worldport shuffle is on.
130 """
131 display_name = "Enable Icarus"
132
133
134class EnableGiftMaps(OptionSet):
135 """
136 Controls whether the beta tester gift maps are randomized. By default, these are not accessible at all from within
137 the randomizer. This option allows you to enter the maps, and creates items and locations for them. If worldport
138 shuffle is on, their worldports will be included in the randomization.
139
140 The gift maps are accessed via a panel in The Entry's Starting Room, which only appears if at least one gift map is
141 enabled. It is also treated like a cyan door, and will not appear until the condition specified in the Cyan Door
142 Behavior option is satisfied. Solving this panel with the name of one of the beta testers will teleport you to their
143 corresponding gift map.
144
145 In the base game, nothing happens once you complete a gift map. Masteries have been added to the gift maps in the
146 randomizer so that the player can be rewarded for completing them.
147
148 Note that the gift maps were originally only intended to be played by specific people, and as a result may be
149 frustrating or require knowledge of inside jokes. The Crystalline is particularly difficult as it requires
150 completing a parkour course.
151 """
152 display_name = "Enable Gift Maps"
153 valid_keys = ["The Advanced", "The Charismatic", "The Crystalline", "The Fuzzy", "The Stellar"]
154
155
156class DaedalusOnly(Toggle):
157 """
158 If enabled, all maps besides Daedalus, The Gold, and The Tenacious will be disabled. This overrides any other
159 map-based option, such as Enable Icarus. The player will start in Daedalus. The following options are restricted
160 with this setting:
161
162 - The ending must be set to Orange or Gold.
163 - Worldport shuffle must be enabled.
164 - Letter shuffle must Unlocked.
165 - Cyan Door Behavior cannot be set to Collect H2.
166 - Control Center Color shuffle must be enabled.
167 """
168 display_name = "Daedalus Only"
169
170
85class DaedalusRoofAccess(Toggle): 171class DaedalusRoofAccess(Toggle):
86 """ 172 """
87 If enabled, the player will be logically expected to be able to go from the castle entrance to any part of Daedalus 173 If enabled, the player will be logically expected to be able to go from the castle entrance to any part of Daedalus
@@ -92,6 +178,15 @@ class DaedalusRoofAccess(Toggle):
92 display_name = "Allow Daedalus Roof Access" 178 display_name = "Allow Daedalus Roof Access"
93 179
94 180
181class CustomMintEnding(FreeText):
182 """
183 If not blank, this will add a new panel that must be solved before collecting Mint Ending (EXIT in the Control
184 Center). The panel will only require typing the text provided for this option, which means the choice of letters
185 here has an impact on logic.
186 """
187 display_name = "Custom Mint Ending"
188
189
95class StrictPurpleEnding(DefaultOnToggle): 190class StrictPurpleEnding(DefaultOnToggle):
96 """ 191 """
97 If enabled, the player will be required to have all purple (level 1) letters in order to get Purple Ending. 192 If enabled, the player will be required to have all purple (level 1) letters in order to get Purple Ending.
@@ -142,6 +237,26 @@ class VictoryCondition(Choice):
142 option_white_ending = 12 237 option_white_ending = 12
143 238
144 239
240class EndingsRequirement(Range):
241 """The number of endings required to unlock White Ending."""
242 display_name = "Endings Requirement"
243 range_start = 0
244 range_end = 12
245 default = 12
246
247
248class MasteriesRequirement(Range):
249 """The number of masteries required to unlock White Ending.
250
251 There are only 13 masteries in the base game, but some of the other slot options may add more masteries to the
252 world. If the chosen number of masteries is higher than the total in your world, it will be automatically lowered to
253 the maximum."""
254 display_name = "Masteries Requirement"
255 range_start = 0
256 range_end = 19
257 default = 0
258
259
145class TrapPercentage(Range): 260class TrapPercentage(Range):
146 """Replaces junk items with traps, at the specified rate.""" 261 """Replaces junk items with traps, at the specified rate."""
147 display_name = "Trap Percentage" 262 display_name = "Trap Percentage"
@@ -150,17 +265,35 @@ class TrapPercentage(Range):
150 default = 0 265 default = 0
151 266
152 267
268class ShuffleMusic(Toggle):
269 """
270 If enabled, every map will be assigned a random music track.
271 """
272 display_name = "Shuffle Music"
273
274
153@dataclass 275@dataclass
154class Lingo2Options(PerGameCommonOptions): 276class Lingo2Options(PerGameCommonOptions):
155 shuffle_doors: ShuffleDoors 277 shuffle_doors: ShuffleDoors
156 shuffle_control_center_colors: ShuffleControlCenterColors 278 shuffle_control_center_colors: ShuffleControlCenterColors
157 shuffle_gallery_paintings: ShuffleGalleryPaintings 279 shuffle_gallery_paintings: ShuffleGalleryPaintings
158 shuffle_letters: ShuffleLetters 280 shuffle_letters: ShuffleLetters
281 restrict_letter_placements: RestrictLetterPlacements
159 shuffle_symbols: ShuffleSymbols 282 shuffle_symbols: ShuffleSymbols
283 shuffle_worldports: ShuffleWorldports
160 keyholder_sanity: KeyholderSanity 284 keyholder_sanity: KeyholderSanity
161 cyan_door_behavior: CyanDoorBehavior 285 cyan_door_behavior: CyanDoorBehavior
286 shuffle_fast_travel: ShuffleFastTravel
287 fast_travel_access: FastTravelAccess
288 enable_icarus: EnableIcarus
289 enable_gift_maps: EnableGiftMaps
290 daedalus_only: DaedalusOnly
162 daedalus_roof_access: DaedalusRoofAccess 291 daedalus_roof_access: DaedalusRoofAccess
292 custom_mint_ending: CustomMintEnding
163 strict_purple_ending: StrictPurpleEnding 293 strict_purple_ending: StrictPurpleEnding
164 strict_cyan_ending: StrictCyanEnding 294 strict_cyan_ending: StrictCyanEnding
165 victory_condition: VictoryCondition 295 victory_condition: VictoryCondition
296 endings_requirement: EndingsRequirement
297 masteries_requirement: MasteriesRequirement
166 trap_percentage: TrapPercentage 298 trap_percentage: TrapPercentage
299 shuffle_music: ShuffleMusic
diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 4aa481d..2c3e08b 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py
@@ -1,10 +1,12 @@
1from enum import IntEnum, auto 1from enum import IntEnum, auto
2 2
3from Options import OptionError
3from .generated import data_pb2 as data_pb2 4from .generated import data_pb2 as data_pb2
4from .items import SYMBOL_ITEMS 5from .items import SYMBOL_ITEMS
5from typing import TYPE_CHECKING, NamedTuple 6from typing import TYPE_CHECKING, NamedTuple
6 7
7from .options import VictoryCondition, ShuffleLetters, CyanDoorBehavior 8from .options import ShuffleLetters, CyanDoorBehavior, VictoryCondition, FastTravelAccess
9from .rules import AccessRequirements
8 10
9if TYPE_CHECKING: 11if TYPE_CHECKING:
10 from . import Lingo2World 12 from . import Lingo2World
@@ -20,177 +22,10 @@ def calculate_letter_histogram(solution: str) -> dict[str, int]:
20 return histogram 22 return histogram
21 23
22 24
23class AccessRequirements:
24 items: set[str]
25 progressives: dict[str, int]
26 rooms: set[str]
27 letters: dict[str, int]
28 cyans: bool
29
30 # This is an AND of ORs.
31 or_logic: list[list["AccessRequirements"]]
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
38 def __init__(self):
39 self.items = set()
40 self.progressives = dict()
41 self.rooms = set()
42 self.letters = dict()
43 self.cyans = False
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
59
60 def merge(self, other: "AccessRequirements"):
61 for item in other.items:
62 self.items.add(item)
63
64 for item, amount in other.progressives.items():
65 self.progressives[item] = max(amount, self.progressives.get(item, 0))
66
67 for room in other.rooms:
68 self.rooms.add(room)
69
70 for letter, level in other.letters.items():
71 self.letters[letter] = max(self.letters.get(letter, 0), level)
72
73 self.cyans = self.cyans or other.cyans
74
75 for disjunction in other.or_logic:
76 self.or_logic.append(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 = self.possibilities
88 self.or_logic.append([left_req])
89
90 self.complete_at = None
91 self.possibilities = list()
92
93 right_req = AccessRequirements()
94 right_req.complete_at = other.complete_at
95 right_req.possibilities = other.possibilities
96 self.or_logic.append([right_req])
97 else:
98 self.complete_at = other.complete_at
99 self.possibilities = other.possibilities
100
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)
169
170 def __repr__(self):
171 parts = []
172 if len(self.items) > 0:
173 parts.append(f"items={self.items}")
174 if len(self.progressives) > 0:
175 parts.append(f"progressives={self.progressives}")
176 if len(self.rooms) > 0:
177 parts.append(f"rooms={self.rooms}")
178 if len(self.letters) > 0:
179 parts.append(f"letters={self.letters}")
180 if self.cyans:
181 parts.append(f"cyans=True")
182 if len(self.or_logic) > 0:
183 parts.append(f"or_logic={self.or_logic}")
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) + ")"
189
190
191class PlayerLocation(NamedTuple): 25class PlayerLocation(NamedTuple):
192 code: int | None 26 code: int | None
193 reqs: AccessRequirements 27 reqs: AccessRequirements
28 is_letter: bool = False
194 29
195 30
196class LetterBehavior(IntEnum): 31class LetterBehavior(IntEnum):
@@ -202,6 +37,10 @@ class LetterBehavior(IntEnum):
202class Lingo2PlayerLogic: 37class Lingo2PlayerLogic:
203 world: "Lingo2World" 38 world: "Lingo2World"
204 39
40 shuffled_maps: set[int]
41 shuffled_rooms: set[int]
42 shuffled_doors: set[int]
43
205 locations_by_room: dict[int, list[PlayerLocation]] 44 locations_by_room: dict[int, list[PlayerLocation]]
206 event_loc_item_by_room: dict[int, dict[str, str]] 45 event_loc_item_by_room: dict[int, dict[str, str]]
207 46
@@ -212,11 +51,17 @@ class Lingo2PlayerLogic:
212 door_reqs: dict[int, AccessRequirements] 51 door_reqs: dict[int, AccessRequirements]
213 52
214 real_items: list[str] 53 real_items: list[str]
54 starting_items: list[str]
215 55
216 double_letter_amount: dict[str, int] 56 double_letter_amount: dict[str, int]
57 goal_room_id: int
58 rte_mapping: list[int]
59 custom_mint_ending: str | None
217 60
218 def __init__(self, world: "Lingo2World"): 61 def __init__(self, world: "Lingo2World"):
219 self.world = world 62 self.world = world
63 self.shuffled_rooms = set()
64 self.shuffled_doors = set()
220 self.locations_by_room = {} 65 self.locations_by_room = {}
221 self.event_loc_item_by_room = {} 66 self.event_loc_item_by_room = {}
222 self.item_by_door = {} 67 self.item_by_door = {}
@@ -224,35 +69,141 @@ class Lingo2PlayerLogic:
224 self.proxy_reqs = dict() 69 self.proxy_reqs = dict()
225 self.door_reqs = dict() 70 self.door_reqs = dict()
226 self.real_items = list() 71 self.real_items = list()
72 self.starting_items = list()
227 self.double_letter_amount = dict() 73 self.double_letter_amount = dict()
74 self.custom_mint_ending = None
75
76 def should_shuffle_map(game_map) -> bool | set[int]:
77 if world.options.daedalus_only:
78 return game_map.daedalus_only_mode == data_pb2.DaedalusOnlyMode.DAED_ONLY_ALLOW
79
80 if game_map.type == data_pb2.MapType.NORMAL_MAP:
81 return True
82 elif game_map.type == data_pb2.MapType.ICARUS:
83 return bool(world.options.enable_icarus)
84 elif game_map.type == data_pb2.MapType.GIFT_MAP:
85 if game_map.name == "the_advanced":
86 return "The Advanced" in world.options.enable_gift_maps.value
87 elif game_map.name == "the_charismatic":
88 return "The Charismatic" in world.options.enable_gift_maps.value
89 elif game_map.name == "the_crystalline":
90 return "The Crystalline" in world.options.enable_gift_maps.value
91 elif game_map.name == "the_fuzzy":
92 return "The Fuzzy" in world.options.enable_gift_maps.value
93 elif game_map.name == "the_stellar":
94 return "The Stellar" in world.options.enable_gift_maps.value
95
96 return False
97
98 self.shuffled_maps = set(game_map.id for game_map in world.static_logic.objects.maps
99 if should_shuffle_map(game_map))
100
101 if world.options.daedalus_only:
102 if world.options.victory_condition not in [VictoryCondition.option_orange_ending,
103 VictoryCondition.option_gold_ending]:
104 raise OptionError(f"When Daedalus Only mode is enabled, the victory condition must be Orange Ending or "
105 f"Gold Ending (Player {world.player}).")
106
107 if not world.options.shuffle_worldports:
108 raise OptionError(f"When Daedalus Only mode is enabled, worldport shuffle must also be enabled (Player "
109 f"{world.player}).")
110
111 if world.options.shuffle_letters != ShuffleLetters.option_unlocked:
112 raise OptionError(f"When Daedalus Only mode is enabled, letter shuffle must be set to Unlocked (Player "
113 f"{world.player}).")
114
115 if world.options.cyan_door_behavior == CyanDoorBehavior.option_collect_h2:
116 raise OptionError(f"When Daedalus Only mode is enabled, Cyan Door Behavior cannot be set to Collect H2 "
117 f"(Player {world.player}).")
118
119 if not world.options.shuffle_control_center_colors:
120 raise OptionError(f"When Daedalus Only mode is enabled, control center color shuffle must be enabled "
121 f"(Player {world.player}).")
122
123 if world.options.shuffle_symbols and not world.options.keyholder_sanity:
124 raise OptionError(f"When Daedalus Only mode is enabled and symbols are shuffled, keyholdersanity must "
125 f"also be enabled (Player {world.player}).")
126
127 for game_map in world.static_logic.objects.maps:
128 if game_map.daedalus_only_mode == data_pb2.DaedalusOnlyMode.DAED_ONLY_PARTIAL:
129 self.shuffled_rooms.update(set(room.id for room in world.static_logic.objects.rooms
130 if room.map_id == game_map.id and room.daedalus_only_allow))
131 self.shuffled_doors.update(set(door.id for door in world.static_logic.objects.doors
132 if door.map_id == game_map.id and door.daedalus_only_allow))
133
134 if (world.options.restrict_letter_placements
135 and world.options.shuffle_letters == ShuffleLetters.option_progressive
136 and (world.options.shuffle_doors or world.options.shuffle_symbols)):
137 raise OptionError(f"When Restrict Letter Placements is enabled and Shuffle Letters is set to Progressive, "
138 f"both Shuffle Doors and Shuffle Symbols must be disabled (Player {world.player}).")
139
140 if world.options.custom_mint_ending.value != "":
141 self.custom_mint_ending = ''.join(filter(str.isalpha, world.options.custom_mint_ending.value)).lower()
142
143 if len(self.custom_mint_ending) > 52:
144 raise OptionError(f"Custom Mint Ending should not be greater than 52 letters (Player {world.player}).")
145
146 maximum_masteries = 13 + len(world.options.enable_gift_maps.value)
147 if world.options.enable_icarus:
148 maximum_masteries += 1
149
150 if world.options.masteries_requirement > maximum_masteries:
151 world.options.masteries_requirement.value = maximum_masteries
152
153 if "The Fuzzy" in world.options.enable_gift_maps.value:
154 self.real_items.append("Numbers")
155
156 if world.options.shuffle_fast_travel:
157 travelable_maps = [map_id for map_id in self.shuffled_maps
158 if world.static_logic.objects.maps[map_id].HasField("rte_room")]
159 self.rte_mapping = world.random.sample(travelable_maps, 4)
160 else:
161 canonical_rtes = ["the_plaza", "the_gallery", "daedalus", "control_center"]
162 self.rte_mapping = [world.static_logic.map_id_by_name[map_name] for map_name in canonical_rtes
163 if world.static_logic.map_id_by_name[map_name] in self.shuffled_maps]
164
165 if world.options.fast_travel_access == FastTravelAccess.option_items:
166 for rte_map in self.rte_mapping:
167 self.real_items.append(world.static_logic.get_map_rte_item_name(rte_map))
228 168
229 if self.world.options.shuffle_doors: 169 if self.world.options.shuffle_doors:
230 for progressive in world.static_logic.objects.progressives: 170 for progressive in world.static_logic.objects.progressives:
231 for i in range(0, len(progressive.doors)): 171 for i in range(0, len(progressive.doors)):
172 door = world.static_logic.objects.doors[progressive.doors[i]]
173 if not self.should_shuffle_door(door.id):
174 continue
175
232 self.item_by_door[progressive.doors[i]] = (progressive.name, i + 1) 176 self.item_by_door[progressive.doors[i]] = (progressive.name, i + 1)
233 self.real_items.append(progressive.name) 177 self.real_items.append(progressive.name)
234 178
235 for door_group in world.static_logic.objects.door_groups: 179 for door_group in world.static_logic.objects.door_groups:
236 if door_group.type == data_pb2.DoorGroupType.CONNECTOR: 180 if door_group.type == data_pb2.DoorGroupType.CONNECTOR:
237 if not self.world.options.shuffle_doors: 181 if not self.world.options.shuffle_doors or self.world.options.shuffle_worldports:
238 continue 182 continue
239 elif door_group.type == data_pb2.DoorGroupType.COLOR_CONNECTOR: 183 elif door_group.type == data_pb2.DoorGroupType.COLOR_CONNECTOR:
240 if not self.world.options.shuffle_control_center_colors: 184 if not self.world.options.shuffle_control_center_colors or self.world.options.shuffle_worldports:
241 continue 185 continue
242 elif door_group.type == data_pb2.DoorGroupType.SHUFFLE_GROUP: 186 elif door_group.type == data_pb2.DoorGroupType.SHUFFLE_GROUP:
243 if not self.world.options.shuffle_doors: 187 if (not self.world.options.shuffle_doors and
188 not (door_group.daedalus_only_always_item and self.world.options.daedalus_only)):
244 continue 189 continue
245 else: 190 else:
246 continue 191 continue
247 192
248 for door in door_group.doors: 193 shuffleable_doors = [door_id for door_id in door_group.doors if self.should_shuffle_door(door_id)]
249 self.item_by_door[door] = (door_group.name, 1)
250 194
251 self.real_items.append(door_group.name) 195 if len(shuffleable_doors) > 0:
196 for door in shuffleable_doors:
197 self.item_by_door[door] = (door_group.name, 1)
198
199 self.real_items.append(door_group.name)
252 200
253 # We iterate through the doors in two parts because it is essential that we determine which doors are shuffled 201 # We iterate through the doors in two parts because it is essential that we determine which doors are shuffled
254 # before we calculate any access requirements. 202 # before we calculate any access requirements.
255 for door in world.static_logic.objects.doors: 203 for door in world.static_logic.objects.doors:
204 if not self.should_shuffle_door(door.id):
205 continue
206
256 if door.type in [data_pb2.DoorType.EVENT, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: 207 if door.type in [data_pb2.DoorType.EVENT, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]:
257 continue 208 continue
258 209
@@ -260,7 +211,8 @@ class Lingo2PlayerLogic:
260 continue 211 continue
261 212
262 if (door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.ITEM_ONLY] and 213 if (door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.ITEM_ONLY] and
263 not self.world.options.shuffle_doors): 214 not self.world.options.shuffle_doors and
215 not (door.daedalus_only_always_item and self.world.options.daedalus_only)):
264 continue 216 continue
265 217
266 if (door.type == data_pb2.DoorType.CONTROL_CENTER_COLOR and 218 if (door.type == data_pb2.DoorType.CONTROL_CENTER_COLOR and
@@ -281,29 +233,41 @@ class Lingo2PlayerLogic:
281 if door_group.type != data_pb2.DoorGroupType.CYAN_DOORS: 233 if door_group.type != data_pb2.DoorGroupType.CYAN_DOORS:
282 continue 234 continue
283 235
284 for door in door_group.doors: 236 shuffleable_doors = [door_id for door_id in door_group.doors
285 if not door in self.item_by_door: 237 if self.should_shuffle_door(door_id) and door_id not in self.item_by_door]
238
239 if len(shuffleable_doors) > 0:
240 for door in shuffleable_doors:
286 self.item_by_door[door] = (door_group.name, 1) 241 self.item_by_door[door] = (door_group.name, 1)
287 242
288 self.real_items.append(door_group.name) 243 self.real_items.append(door_group.name)
289 244
290 for door in world.static_logic.objects.doors: 245 for door in world.static_logic.objects.doors:
246 if not self.should_shuffle_door(door.id):
247 continue
248
291 if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: 249 if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]:
292 self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id, 250 self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id,
293 self.get_door_reqs(door.id))) 251 self.get_door_reqs(door.id)))
294 252
295 for letter in world.static_logic.objects.letters: 253 for letter in world.static_logic.objects.letters:
296 self.locations_by_room.setdefault(letter.room_id, []).append(PlayerLocation(letter.ap_id, 254 if not self.should_shuffle_room(letter.room_id):
297 AccessRequirements())) 255 continue
256
298 behavior = self.get_letter_behavior(letter.key, letter.level2) 257 behavior = self.get_letter_behavior(letter.key, letter.level2)
299 if behavior == LetterBehavior.VANILLA:
300 letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}"
301 event_name = f"{letter_name} (Collected)"
302 self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper()
303 258
304 if letter.level2: 259 self.locations_by_room.setdefault(letter.room_id, []).append(
305 event_name = f"{letter_name} (Double Collected)" 260 PlayerLocation(letter.ap_id, AccessRequirements(), behavior == LetterBehavior.ITEM))
261
262 if behavior == LetterBehavior.VANILLA:
263 if not world.for_tracker:
264 letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}"
265 event_name = f"{letter_name} (Collected)"
306 self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() 266 self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper()
267
268 if letter.level2:
269 event_name = f"{letter_name} (Double Collected)"
270 self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper()
307 elif behavior == LetterBehavior.ITEM: 271 elif behavior == LetterBehavior.ITEM:
308 self.real_items.append(letter.key.upper()) 272 self.real_items.append(letter.key.upper())
309 273
@@ -311,30 +275,42 @@ class Lingo2PlayerLogic:
311 self.double_letter_amount[letter.key.upper()] = self.double_letter_amount.get(letter.key.upper(), 0) + 1 275 self.double_letter_amount[letter.key.upper()] = self.double_letter_amount.get(letter.key.upper(), 0) + 1
312 276
313 for mastery in world.static_logic.objects.masteries: 277 for mastery in world.static_logic.objects.masteries:
278 if not self.should_shuffle_room(mastery.room_id):
279 continue
280
314 self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, 281 self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id,
315 AccessRequirements())) 282 AccessRequirements()))
316 283
284 if world.options.masteries_requirement > 0:
285 event_name = f"{world.static_logic.get_room_object_map_name(mastery)} - Mastery (Collected)"
286 self.event_loc_item_by_room.setdefault(mastery.room_id, {})[event_name] = "Mastery"
287
317 for ending in world.static_logic.objects.endings: 288 for ending in world.static_logic.objects.endings:
318 # Don't ever create a location for White Ending. Don't even make an event for it if it's not the victory 289 if not self.should_shuffle_room(ending.room_id):
319 # condition, since it is necessarily going to be in the postgame. 290 continue
320 if ending.name == "WHITE": 291
321 if self.world.options.victory_condition != VictoryCondition.option_white_ending: 292 # Don't create a location for your selected ending. Also don't create a location for White Ending if it's
322 continue 293 # necessarily in the postgame, i.e. it requires all 12 other endings.
323 else: 294 if world.options.victory_condition.current_key.removesuffix("_ending").upper() != ending.name\
295 and (ending.name != "WHITE" or world.options.endings_requirement < 12):
324 self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id, 296 self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id,
325 AccessRequirements())) 297 AccessRequirements()))
326 298
327 event_name = f"{ending.name.capitalize()} Ending (Achieved)"
328 item_name = event_name
329
330 if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: 299 if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name:
331 item_name = "Victory" 300 event_name = f"{ending.name.capitalize()} Ending (Goal)"
301 self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = "Victory"
302 self.goal_room_id = ending.room_id
332 303
333 self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = item_name 304 if ending.name != "WHITE":
305 event_name = f"{ending.name.capitalize()} Ending (Achieved)"
306 self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = "Ending"
334 307
335 if self.world.options.keyholder_sanity: 308 if self.world.options.keyholder_sanity:
336 for keyholder in world.static_logic.objects.keyholders: 309 for keyholder in world.static_logic.objects.keyholders:
337 if keyholder.HasField("key"): 310 if keyholder.HasField("key"):
311 if not self.should_shuffle_room(keyholder.room_id):
312 continue
313
338 reqs = AccessRequirements() 314 reqs = AccessRequirements()
339 315
340 if self.get_letter_behavior(keyholder.key, False) != LetterBehavior.UNLOCKED: 316 if self.get_letter_behavior(keyholder.key, False) != LetterBehavior.UNLOCKED:
@@ -345,7 +321,10 @@ class Lingo2PlayerLogic:
345 321
346 if self.world.options.shuffle_symbols: 322 if self.world.options.shuffle_symbols:
347 for symbol_name in SYMBOL_ITEMS.values(): 323 for symbol_name in SYMBOL_ITEMS.values():
348 self.real_items.append(symbol_name) 324 if self.world.options.daedalus_only and symbol_name == "Sun Symbol":
325 self.starting_items.append(symbol_name)
326 else:
327 self.real_items.append(symbol_name)
349 328
350 def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements: 329 def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements:
351 if answer is None: 330 if answer is None:
@@ -442,7 +421,6 @@ class Lingo2PlayerLogic:
442 reqs.possibilities.append(panel_reqs) 421 reqs.possibilities.append(panel_reqs)
443 422
444 if door.HasField("control_center_color"): 423 if door.HasField("control_center_color"):
445 # TODO: Logic for ensuring two CC states aren't needed at once.
446 reqs.rooms.add("Control Center - Main Area") 424 reqs.rooms.add("Control Center - Main Area")
447 self.add_solution_reqs(reqs, door.control_center_color) 425 self.add_solution_reqs(reqs, door.control_center_color)
448 426
@@ -468,13 +446,12 @@ class Lingo2PlayerLogic:
468 for room in door.rooms: 446 for room in door.rooms:
469 reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) 447 reqs.rooms.add(self.world.static_logic.get_room_region_name(room))
470 448
471 for ending_id in door.endings: 449 if door.white_ending:
472 ending = self.world.static_logic.objects.endings[ending_id] 450 if self.world.options.endings_requirement > 0:
451 reqs.progressives["Ending"] = self.world.options.endings_requirement.value
473 452
474 if self.world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: 453 if self.world.options.masteries_requirement > 0:
475 reqs.items.add("Victory") 454 reqs.progressives["Mastery"] = self.world.options.masteries_requirement.value
476 else:
477 reqs.items.add(f"{ending.name.capitalize()} Ending (Achieved)")
478 455
479 for sub_door_id in door.doors: 456 for sub_door_id in door.doors:
480 sub_reqs = self.get_door_open_reqs(sub_door_id) 457 sub_reqs = self.get_door_open_reqs(sub_door_id)
@@ -536,3 +513,24 @@ class Lingo2PlayerLogic:
536 513
537 if needed > 0: 514 if needed > 0:
538 reqs.letters[l] = max(reqs.letters.get(l, 0), needed) 515 reqs.letters[l] = max(reqs.letters.get(l, 0), needed)
516
517 if any(l.isnumeric() for l in solution):
518 reqs.items.add("Numbers")
519
520 def should_shuffle_room(self, room_id: int) -> bool:
521 if room_id in self.shuffled_rooms:
522 return True
523
524 room = self.world.static_logic.objects.rooms[room_id]
525 game_map = self.world.static_logic.objects.maps[room.map_id]
526
527 return game_map.id in self.shuffled_maps
528
529 def should_shuffle_door(self, door_id: int) -> bool:
530 if door_id in self.shuffled_doors:
531 return True
532
533 door = self.world.static_logic.objects.doors[door_id]
534 game_map = self.world.static_logic.objects.maps[door.map_id]
535
536 return game_map.id in self.shuffled_maps
diff --git a/apworld/regions.py b/apworld/regions.py index 993eec8..313fd02 100644 --- a/apworld/regions.py +++ b/apworld/regions.py
@@ -1,10 +1,12 @@
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, LetterPlacementType, Lingo2Entrance
8from .options import FastTravelAccess
6from .player_logic import AccessRequirements 9from .player_logic import AccessRequirements
7from .rules import make_location_lambda
8 10
9if TYPE_CHECKING: 11if TYPE_CHECKING:
10 from . import Lingo2World 12 from . import Lingo2World
@@ -19,17 +21,39 @@ def create_locations(room, new_region: Region, world: "Lingo2World", regions: di
19 reqs = location.reqs.copy() 21 reqs = location.reqs.copy()
20 reqs.remove_room(new_region.name) 22 reqs.remove_room(new_region.name)
21 23
22 new_location = Lingo2Location(world.player, world.static_logic.location_id_to_name[location.code], 24 new_location = Lingo2Location.non_event_location(world, location.code, new_region)
23 location.code, new_region) 25 new_location.set_access_rule(reqs, regions)
24 new_location.access_rule = make_location_lambda(reqs, world, regions) 26 if world.options.restrict_letter_placements:
27 if location.is_letter:
28 new_location.set_up_letter_rule(LetterPlacementType.FORCE)
29 else:
30 new_location.set_up_letter_rule(LetterPlacementType.DISALLOW)
25 new_region.locations.append(new_location) 31 new_region.locations.append(new_location)
26 32
27 for event_name, item_name in world.player_logic.event_loc_item_by_room.get(room.id, {}).items(): 33 for event_name, item_name in world.player_logic.event_loc_item_by_room.get(room.id, {}).items():
28 new_location = Lingo2Location(world.player, event_name, None, new_region) 34 new_location = Lingo2Location.event_location(world, event_name, new_region)
35 if world.for_tracker and item_name == "Victory":
36 new_location.goal = True
37
29 event_item = Lingo2Item(item_name, ItemClassification.progression, None, world.player) 38 event_item = Lingo2Item(item_name, ItemClassification.progression, None, world.player)
30 new_location.place_locked_item(event_item) 39 new_location.place_locked_item(event_item)
31 new_region.locations.append(new_location) 40 new_region.locations.append(new_location)
32 41
42 if world.for_tracker and world.options.shuffle_worldports:
43 for port_id in room.ports:
44 port = world.static_logic.objects.ports[port_id]
45 if port.no_shuffle:
46 continue
47
48 new_location = Lingo2Location.event_location(world, f"Worldport {port.id} Entered", new_region)
49 new_location.port_id = port.id
50
51 if port.HasField("required_door"):
52 new_location.set_access_rule(world.player_logic.get_door_open_reqs(port.required_door), regions)
53
54 new_region.locations.append(new_location)
55
56
33def create_regions(world: "Lingo2World"): 57def create_regions(world: "Lingo2World"):
34 regions = { 58 regions = {
35 "Menu": Region("Menu", world.player, world.multiworld) 59 "Menu": Region("Menu", world.player, world.multiworld)
@@ -41,6 +65,9 @@ def create_regions(world: "Lingo2World"):
41 # locations. This allows us to reference the actual region objects in the access rules for the locations, which is 65 # locations. This allows us to reference the actual region objects in the access rules for the locations, which is
42 # faster than having to look them up during access checking. 66 # faster than having to look them up during access checking.
43 for room in world.static_logic.objects.rooms: 67 for room in world.static_logic.objects.rooms:
68 if not world.player_logic.should_shuffle_room(room.id):
69 continue
70
44 region = create_region(room, world) 71 region = create_region(room, world)
45 regions[region.name] = region 72 regions[region.name] = region
46 region_and_room.append((region, room)) 73 region_and_room.append((region, room))
@@ -48,13 +75,18 @@ def create_regions(world: "Lingo2World"):
48 for (region, room) in region_and_room: 75 for (region, room) in region_and_room:
49 create_locations(room, region, world, regions) 76 create_locations(room, region, world, regions)
50 77
51 regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game") 78 if world.options.daedalus_only:
79 regions["Menu"].connect(regions["Daedalus - Starting Room"], "Start Game")
80 else:
81 regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game")
52 82
53 # TODO: The requirements of the opposite trigger also matter.
54 for connection in world.static_logic.objects.connections: 83 for connection in world.static_logic.objects.connections:
55 if connection.roof_access and not world.options.daedalus_roof_access: 84 if connection.roof_access and not world.options.daedalus_roof_access:
56 continue 85 continue
57 86
87 if connection.vanilla_only and world.options.shuffle_doors:
88 continue
89
58 from_region = world.static_logic.get_room_region_name(connection.from_room) 90 from_region = world.static_logic.get_room_region_name(connection.from_room)
59 to_region = world.static_logic.get_room_region_name(connection.to_room) 91 to_region = world.static_logic.get_room_region_name(connection.to_room)
60 92
@@ -74,7 +106,10 @@ def create_regions(world: "Lingo2World"):
74 106
75 if connection.HasField("port"): 107 if connection.HasField("port"):
76 port = world.static_logic.objects.ports[connection.port] 108 port = world.static_logic.objects.ports[connection.port]
77 connection_name = f"{connection_name} (via port {port.name})" 109 connection_name = f"{connection_name} (via {port.display_name})"
110
111 if world.options.shuffle_worldports and not port.no_shuffle:
112 continue
78 113
79 if port.HasField("required_door"): 114 if port.HasField("required_door"):
80 reqs.merge(world.player_logic.get_door_open_reqs(port.required_door)) 115 reqs.merge(world.player_logic.get_door_open_reqs(port.required_door))
@@ -103,11 +138,21 @@ def create_regions(world: "Lingo2World"):
103 if connection.HasField("cyan_ending") and connection.cyan_ending and world.options.strict_cyan_ending: 138 if connection.HasField("cyan_ending") and connection.cyan_ending and world.options.strict_cyan_ending:
104 world.player_logic.add_solution_reqs(reqs, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz") 139 world.player_logic.add_solution_reqs(reqs, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz")
105 140
141 if (connection.HasField("mint_ending") and connection.mint_ending
142 and world.player_logic.custom_mint_ending is not None):
143 world.player_logic.add_solution_reqs(reqs, world.player_logic.custom_mint_ending)
144
106 reqs.simplify() 145 reqs.simplify()
107 reqs.remove_room(from_region) 146 reqs.remove_room(from_region)
108 147
109 connection = Entrance(world.player, connection_name, regions[from_region]) 148 if to_region in reqs.rooms:
110 connection.access_rule = make_location_lambda(reqs, world, regions) 149 # This connection can't ever increase access because you're required to have access to the other side in
150 # order for it to be usable. We will just not create the connection at all, in order to help GER figure out
151 # what regions are dead ends.
152 continue
153
154 connection = Lingo2Entrance(world, connection_name, regions[from_region])
155 connection.set_access_rule(reqs, regions)
111 156
112 regions[from_region].exits.append(connection) 157 regions[from_region].exits.append(connection)
113 connection.connect(regions[to_region]) 158 connection.connect(regions[to_region])
@@ -115,4 +160,126 @@ def create_regions(world: "Lingo2World"):
115 for region in reqs.get_referenced_rooms(): 160 for region in reqs.get_referenced_rooms():
116 world.multiworld.register_indirect_condition(regions[region], connection) 161 world.multiworld.register_indirect_condition(regions[region], connection)
117 162
163 if world.options.fast_travel_access != FastTravelAccess.option_vanilla:
164 for rte_map_id in world.player_logic.rte_mapping:
165 rte_map = world.static_logic.objects.maps[rte_map_id]
166 to_region = world.static_logic.get_room_region_name(rte_map.rte_room)
167
168 if to_region not in regions:
169 continue
170
171 connection_name = f"Return to {to_region}"
172 connection = Lingo2Entrance(world, connection_name, regions["Menu"])
173 regions["Menu"].exits.append(connection)
174 connection.connect(regions[to_region])
175
176 if world.options.fast_travel_access == FastTravelAccess.option_items:
177 reqs = AccessRequirements()
178 reqs.items.add(world.static_logic.get_map_rte_item_name(rte_map_id))
179
180 connection.set_access_rule(reqs, regions)
181
118 world.multiworld.regions += regions.values() 182 world.multiworld.regions += regions.values()
183
184
185def shuffle_entrances(world: "Lingo2World"):
186 er_entrances: list[Entrance] = []
187 er_exits: list[Entrance] = []
188
189 port_id_by_name: dict[str, int] = {}
190
191 shuffleable_ports = [port for port in world.static_logic.objects.ports
192 if not port.no_shuffle and world.player_logic.should_shuffle_room(port.room_id)]
193
194 if len(shuffleable_ports) % 2 == 1:
195 # We have an odd number of shuffleable ports! Pick a port from a room that has more than one, and make it a
196 # redundant warp to another port.
197 redundant_rooms = set(room.id for room in world.static_logic.objects.rooms if len(room.ports) > 1)
198 redundant_ports = [port for port in shuffleable_ports if port.room_id in redundant_rooms]
199 chosen_port = world.random.choice(redundant_ports)
200
201 shuffleable_ports.remove(chosen_port)
202
203 chosen_destination = world.random.choice(shuffleable_ports)
204
205 world.port_pairings[chosen_port.id] = chosen_destination.id
206
207 from_region_name = world.static_logic.get_room_region_name(chosen_port.room_id)
208 to_region_name = world.static_logic.get_room_region_name(chosen_destination.room_id)
209
210 from_region = world.multiworld.get_region(from_region_name, world.player)
211 to_region = world.multiworld.get_region(to_region_name, world.player)
212
213 connection = Lingo2Entrance(world, f"{from_region_name} - {chosen_port.display_name}", from_region)
214 from_region.exits.append(connection)
215 connection.connect(to_region)
216
217 if chosen_port.HasField("required_door"):
218 door_reqs = world.player_logic.get_door_open_reqs(chosen_port.required_door)
219 connection.set_access_rule(door_reqs, None)
220
221 for region in door_reqs.get_referenced_rooms():
222 world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player),
223 connection)
224
225 for port in shuffleable_ports:
226 port_region_name = world.static_logic.get_room_region_name(port.room_id)
227 port_region = world.multiworld.get_region(port_region_name, world.player)
228
229 connection_name = f"{port_region_name} - {port.display_name}"
230 port_id_by_name[connection_name] = port.id
231
232 entrance = port_region.create_er_target(connection_name)
233 entrance.randomization_type = BaseClasses.EntranceType.TWO_WAY
234
235 er_exit = Lingo2Entrance(world, connection_name, port_region)
236 port_region.exits.append(er_exit)
237 er_exit.randomization_type = BaseClasses.EntranceType.TWO_WAY
238
239 if port.HasField("required_door"):
240 door_reqs = world.player_logic.get_door_open_reqs(port.required_door)
241 er_exit.set_access_rule(door_reqs, None)
242
243 for region in door_reqs.get_referenced_rooms():
244 world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player),
245 er_exit)
246
247 er_entrances.append(entrance)
248 er_exits.append(er_exit)
249
250 result = randomize_entrances(world, True, {0:[0]}, False, er_entrances,
251 er_exits)
252
253 for (f, to) in result.pairings:
254 world.port_pairings[port_id_by_name[f]] = port_id_by_name[to]
255
256
257def connect_ports_from_ut(port_pairings: dict[int, int], world: "Lingo2World"):
258 for fpid, tpid in port_pairings.items():
259 from_port = world.static_logic.objects.ports[fpid]
260 to_port = world.static_logic.objects.ports[tpid]
261
262 from_region_name = world.static_logic.get_room_region_name(from_port.room_id)
263 to_region_name = world.static_logic.get_room_region_name(to_port.room_id)
264
265 from_region = world.multiworld.get_region(from_region_name, world.player)
266 to_region = world.multiworld.get_region(to_region_name, world.player)
267
268 connection = Lingo2Entrance(world, f"{from_region_name} - {from_port.display_name}", from_region)
269
270 reqs = AccessRequirements()
271 if from_port.HasField("required_door"):
272 reqs = world.player_logic.get_door_open_reqs(from_port.required_door).copy()
273
274 if world.for_tracker:
275 reqs.items.add(f"Worldport {fpid} Entered")
276
277 if not reqs.is_empty():
278 connection.set_access_rule(reqs, None)
279
280 for region in reqs.get_referenced_rooms():
281 world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player),
282 connection)
283
284 from_region.exits.append(connection)
285 connection.connect(to_region)
diff --git a/apworld/requirements.txt b/apworld/requirements.txt index dbc395b..f466c11 100644 --- a/apworld/requirements.txt +++ b/apworld/requirements.txt
@@ -1 +1 @@
protobuf==3.20.3 protobuf==6.31.1
diff --git a/apworld/rules.py b/apworld/rules.py index c077858..70a76c0 100644 --- a/apworld/rules.py +++ b/apworld/rules.py
@@ -1,63 +1,215 @@
1from collections.abc import Callable
2from typing import TYPE_CHECKING 1from typing import TYPE_CHECKING
3 2
4from BaseClasses import CollectionState, Region 3from BaseClasses import CollectionState
5from .player_logic import AccessRequirements
6 4
7if TYPE_CHECKING: 5if TYPE_CHECKING:
8 from . import Lingo2World 6 from . import Lingo2World
9 7
10 8
11def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirements, regions: list[Region], 9class AccessRequirements:
12 world: "Lingo2World") -> bool: 10 items: set[str]
13 if not all(state.has(item, world.player) for item in reqs.items): 11 progressives: dict[str, int]
14 return False 12 rooms: set[str]
13 letters: dict[str, int]
14 cyans: bool
15 15
16 if not all(state.has(item, world.player, amount) for item, amount in reqs.progressives.items()): 16 # This is an AND of ORs.
17 return False 17 or_logic: list[list["AccessRequirements"]]
18 18
19 if not all(state.can_reach_region(region_name, world.player) for region_name in reqs.rooms): 19 # When complete_at is set, at least that many of the requirements in possibilities must be accessible. This should
20 return False 20 # only be used for doors with complete_at > 1, as or_logic is more efficient for complete_at == 1.
21 complete_at: int | None
22 possibilities: list["AccessRequirements"]
21 23
22 if not all(state.can_reach(region) for region in regions): 24 def __init__(self):
23 return False 25 self.items = set()
26 self.progressives = dict()
27 self.rooms = set()
28 self.letters = dict()
29 self.cyans = False
30 self.or_logic = list()
31 self.complete_at = None
32 self.possibilities = list()
24 33
25 for letter_key, letter_level in reqs.letters.items(): 34 def copy(self) -> "AccessRequirements":
26 if not state.has(letter_key, world.player, letter_level): 35 reqs = AccessRequirements()
36 reqs.items = self.items.copy()
37 reqs.progressives = self.progressives.copy()
38 reqs.rooms = self.rooms.copy()
39 reqs.letters = self.letters.copy()
40 reqs.cyans = self.cyans
41 reqs.or_logic = [[other_req.copy() for other_req in disjunction] for disjunction in self.or_logic]
42 reqs.complete_at = self.complete_at
43 reqs.possibilities = self.possibilities.copy()
44 return reqs
45
46 def merge(self, other: "AccessRequirements"):
47 for item in other.items:
48 self.items.add(item)
49
50 for item, amount in other.progressives.items():
51 self.progressives[item] = max(amount, self.progressives.get(item, 0))
52
53 for room in other.rooms:
54 self.rooms.add(room)
55
56 for letter, level in other.letters.items():
57 self.letters[letter] = max(self.letters.get(letter, 0), level)
58
59 self.cyans = self.cyans or other.cyans
60
61 for disjunction in other.or_logic:
62 self.or_logic.append([sub_req.copy() for sub_req in disjunction])
63
64 if other.complete_at is not None:
65 # Merging multiple requirements that use complete_at sucks, and is part of why we want to minimize use of
66 # it. If both requirements use complete_at, we will cheat by using the or_logic field, which supports
67 # conjunctions of requirements.
68 if self.complete_at is not None:
69 print("Merging requirements with complete_at > 1. This is messy and should be avoided!")
70
71 left_req = AccessRequirements()
72 left_req.complete_at = self.complete_at
73 left_req.possibilities = [sub_req.copy() for sub_req in self.possibilities]
74 self.or_logic.append([left_req])
75
76 self.complete_at = None
77 self.possibilities = list()
78
79 right_req = AccessRequirements()
80 right_req.complete_at = other.complete_at
81 right_req.possibilities = [sub_req.copy() for sub_req in other.possibilities]
82 self.or_logic.append([right_req])
83 else:
84 self.complete_at = other.complete_at
85 self.possibilities = [sub_req.copy() for sub_req in other.possibilities]
86
87 def is_empty(self) -> bool:
88 return (len(self.items) == 0 and len(self.progressives) == 0 and len(self.rooms) == 0 and len(self.letters) == 0
89 and not self.cyans and len(self.or_logic) == 0 and self.complete_at is None)
90
91 def __eq__(self, other: "AccessRequirements"):
92 return (self.items == other.items and self.progressives == other.progressives and self.rooms == other.rooms and
93 self.letters == other.letters and self.cyans == other.cyans and self.or_logic == other.or_logic and
94 self.complete_at == other.complete_at and self.possibilities == other.possibilities)
95
96 def simplify(self):
97 resimplify = False
98
99 if len(self.or_logic) > 0:
100 old_or_logic = self.or_logic
101
102 def remove_redundant(sub_reqs: "AccessRequirements"):
103 new_reqs = sub_reqs.copy()
104 new_reqs.letters = {l: v for l, v in new_reqs.letters.items() if self.letters.get(l, 0) < v}
105 if new_reqs != sub_reqs:
106 return new_reqs
107 else:
108 return sub_reqs
109
110 self.or_logic = []
111 for disjunction in old_or_logic:
112 new_disjunction = []
113 for ssr in disjunction:
114 new_ssr = remove_redundant(ssr)
115 if not new_ssr.is_empty():
116 new_disjunction.append(new_ssr)
117 else:
118 new_disjunction.clear()
119 break
120 if len(new_disjunction) == 1:
121 self.merge(new_disjunction[0])
122 resimplify = True
123 elif len(new_disjunction) > 1:
124 if all(cjr == new_disjunction[0] for cjr in new_disjunction):
125 self.merge(new_disjunction[0])
126 resimplify = True
127 else:
128 self.or_logic.append(new_disjunction)
129
130 if resimplify:
131 self.simplify()
132
133 def get_referenced_rooms(self):
134 result = set(self.rooms)
135
136 for disjunction in self.or_logic:
137 for sub_req in disjunction:
138 result = result.union(sub_req.get_referenced_rooms())
139
140 for sub_req in self.possibilities:
141 result = result.union(sub_req.get_referenced_rooms())
142
143 return result
144
145 def remove_room(self, room: str):
146 if room in self.rooms:
147 self.rooms.remove(room)
148
149 for disjunction in self.or_logic:
150 for sub_req in disjunction:
151 sub_req.remove_room(room)
152
153 for sub_req in self.possibilities:
154 sub_req.remove_room(room)
155
156 def __repr__(self):
157 parts = []
158 if len(self.items) > 0:
159 parts.append(f"items={self.items}")
160 if len(self.progressives) > 0:
161 parts.append(f"progressives={self.progressives}")
162 if len(self.rooms) > 0:
163 parts.append(f"rooms={self.rooms}")
164 if len(self.letters) > 0:
165 parts.append(f"letters={self.letters}")
166 if self.cyans:
167 parts.append(f"cyans=True")
168 if len(self.or_logic) > 0:
169 parts.append(f"or_logic={self.or_logic}")
170 if self.complete_at is not None:
171 parts.append(f"complete_at={self.complete_at}")
172 if len(self.possibilities) > 0:
173 parts.append(f"possibilities={self.possibilities}")
174 return "AccessRequirements(" + ", ".join(parts) + ")"
175
176 def check_access(self, state: CollectionState, world: "Lingo2World") -> bool:
177 if not all(state.has(item, world.player) for item in self.items):
27 return False 178 return False
28 179
29 if reqs.cyans: 180 if not all(state.has(item, world.player, amount) for item, amount in self.progressives.items()):
30 if not any(state.has(letter, world.player, amount)
31 for letter, amount in world.player_logic.double_letter_amount.items()):
32 return False 181 return False
33 182
34 if len(reqs.or_logic) > 0: 183 if not all(state.can_reach_region(region_name, world.player) for region_name in self.rooms):
35 if not all(any(lingo2_can_satisfy_requirements(state, sub_reqs, [], world) for sub_reqs in subjunction)
36 for subjunction in reqs.or_logic):
37 return False 184 return False
38 185
39 if reqs.complete_at is not None: 186 for letter_key, letter_level in self.letters.items():
40 completed = 0 187 if not state.has(letter_key, world.player, letter_level):
41 checked = 0 188 return False
42 for possibility in reqs.possibilities: 189
43 checked += 1 190 if self.cyans:
44 if lingo2_can_satisfy_requirements(state, possibility, [], world): 191 if not any(state.has(letter, world.player, amount)
45 completed += 1 192 for letter, amount in world.player_logic.double_letter_amount.items()):
46 if completed >= reqs.complete_at: 193 return False
47 break 194
48 elif len(reqs.possibilities) - checked + completed < reqs.complete_at: 195 if len(self.or_logic) > 0:
49 # There aren't enough remaining possibilities for the check to pass. 196 if not all(any(sub_reqs.check_access(state, world) for sub_reqs in subjunction)
197 for subjunction in self.or_logic):
50 return False 198 return False
51 if completed < reqs.complete_at:
52 return False
53 199
54 return True 200 if self.complete_at is not None:
201 completed = 0
202 checked = 0
203 for possibility in self.possibilities:
204 checked += 1
205 if possibility.check_access(state, world):
206 completed += 1
207 if completed >= self.complete_at:
208 break
209 elif len(self.possibilities) - checked + completed < self.complete_at:
210 # There aren't enough remaining possibilities for the check to pass.
211 return False
212 if completed < self.complete_at:
213 return False
55 214
56def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World", 215 return True
57 regions: dict[str, Region]) -> Callable[[CollectionState], bool]:
58 # Replace required rooms with regions for the top level requirement, which saves looking up the regions during rule
59 # checking.
60 required_regions = [regions[room_name] for room_name in reqs.rooms]
61 new_reqs = reqs.copy()
62 new_reqs.rooms.clear()
63 return lambda state: lingo2_can_satisfy_requirements(state, new_reqs, required_regions, world)
diff --git a/apworld/static_logic.py b/apworld/static_logic.py index e4d7d49..48ad78e 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py
@@ -2,6 +2,7 @@ from .generated import data_pb2 as data_pb2
2from .items import SYMBOL_ITEMS, ANTI_COLLECTABLE_TRAPS 2from .items import SYMBOL_ITEMS, ANTI_COLLECTABLE_TRAPS
3import pkgutil 3import pkgutil
4 4
5
5class Lingo2StaticLogic: 6class Lingo2StaticLogic:
6 item_id_to_name: dict[int, str] 7 item_id_to_name: dict[int, str]
7 location_id_to_name: dict[int, str] 8 location_id_to_name: dict[int, str]
@@ -14,6 +15,11 @@ class Lingo2StaticLogic:
14 15
15 letter_weights: dict[str, int] 16 letter_weights: dict[str, int]
16 17
18 door_id_by_ap_id: dict[int, int]
19 port_id_by_ap_id: dict[int, int]
20
21 map_id_by_name: dict[str, int]
22
17 def __init__(self): 23 def __init__(self):
18 self.item_id_to_name = {} 24 self.item_id_to_name = {}
19 self.location_id_to_name = {} 25 self.location_id_to_name = {}
@@ -67,19 +73,32 @@ class Lingo2StaticLogic:
67 self.location_name_groups.setdefault("Keyholders", []).append(location_name) 73 self.location_name_groups.setdefault("Keyholders", []).append(location_name)
68 74
69 self.item_id_to_name[self.objects.special_ids["A Job Well Done"]] = "A Job Well Done" 75 self.item_id_to_name[self.objects.special_ids["A Job Well Done"]] = "A Job Well Done"
76 self.item_id_to_name[self.objects.special_ids["Numbers"]] = "Numbers"
70 77
78 self.item_name_groups["Symbols"] = []
71 for symbol_name in SYMBOL_ITEMS.values(): 79 for symbol_name in SYMBOL_ITEMS.values():
72 self.item_id_to_name[self.objects.special_ids[symbol_name]] = symbol_name 80 self.item_id_to_name[self.objects.special_ids[symbol_name]] = symbol_name
81 self.item_name_groups["Symbols"].append(symbol_name)
73 82
74 for trap_name in ANTI_COLLECTABLE_TRAPS: 83 for trap_name in ANTI_COLLECTABLE_TRAPS:
75 self.item_id_to_name[self.objects.special_ids[trap_name]] = trap_name 84 self.item_id_to_name[self.objects.special_ids[trap_name]] = trap_name
76 85
86 for game_map in self.objects.maps:
87 if game_map.HasField("rte_room"):
88 self.item_id_to_name[game_map.rte_ap_id] = self.get_map_rte_item_name(game_map.id)
89
77 self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()} 90 self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()}
78 self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()} 91 self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()}
79 92
80 for panel in self.objects.panels: 93 for panel in self.objects.panels:
81 for letter in panel.answer.upper(): 94 for letter in panel.answer.upper():
82 self.letter_weights[letter] = self.letter_weights.get(letter, 0) + 1 95 if letter.isalpha():
96 self.letter_weights[letter] = self.letter_weights.get(letter, 0) + 1
97
98 self.door_id_by_ap_id = {door.ap_id: door.id for door in self.objects.doors if door.HasField("ap_id")}
99 self.port_id_by_ap_id = {port.ap_id: port.id for port in self.objects.ports if port.HasField("ap_id")}
100
101 self.map_id_by_name = {game_map.name: game_map.id for game_map in self.objects.maps}
83 102
84 def get_door_item_name(self, door: data_pb2.Door) -> str: 103 def get_door_item_name(self, door: data_pb2.Door) -> str:
85 return f"{self.get_map_object_map_name(door)} - {door.name}" 104 return f"{self.get_map_object_map_name(door)} - {door.name}"
@@ -104,7 +123,7 @@ class Lingo2StaticLogic:
104 if door.type != data_pb2.DoorType.STANDARD: 123 if door.type != data_pb2.DoorType.STANDARD:
105 return None 124 return None
106 125
107 if len(door.keyholders) > 0 or len(door.endings) > 0 or door.HasField("complete_at"): 126 if len(door.keyholders) > 0 or door.white_ending or door.HasField("complete_at"):
108 return None 127 return None
109 128
110 if len(door.panels) > 4: 129 if len(door.panels) > 4:
@@ -165,5 +184,13 @@ class Lingo2StaticLogic:
165 else: 184 else:
166 return game_map.display_name 185 return game_map.display_name
167 186
168 def get_data_version(self) -> int: 187 def get_room_object_map_id(self, obj) -> int:
169 return self.objects.version 188 return self.objects.rooms[obj.room_id].map_id
189
190 def get_map_rte_item_name(self, map_id: int) -> str:
191 game_map = self.objects.maps[map_id]
192 return f"Return to {game_map.display_name}"
193
194 def get_data_version(self) -> list[int]:
195 version = self.objects.version
196 return [version.major, version.minor, version.patch]
diff --git a/apworld/tracker.py b/apworld/tracker.py new file mode 100644 index 0000000..3e1cafb --- /dev/null +++ b/apworld/tracker.py
@@ -0,0 +1,151 @@
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
48 self.world.player_logic.rte_mapping = [self.world.static_logic.map_id_by_name[map_name]
49 for map_name in slot_data.get("rte", [])]
50
51 self.world.create_regions()
52
53 if self.world.options.shuffle_worldports:
54 port_pairings = {
55 self.world.static_logic.port_id_by_ap_id[int(fp)]: self.world.static_logic.port_id_by_ap_id[int(tp)]
56 for fp, tp in slot_data["port_pairings"].items()
57 }
58 connect_ports_from_ut(port_pairings, self.world)
59
60 self.refresh_state()
61
62 def set_checked_locations(self, checked_locations: set[int]):
63 self.checked_locations = checked_locations.copy()
64
65 def set_collected_items(self, network_items: list[NetworkItem]):
66 self.collected_items = {}
67
68 for item in network_items:
69 self.collected_items[item.item] = self.collected_items.get(item.item, 0) + 1
70
71 self.refresh_state()
72
73 def refresh_state(self):
74 self.state = CollectionState(self.multiworld)
75
76 for item_id, item_amount in self.collected_items.items():
77 for i in range(item_amount):
78 self.state.collect(Lingo2Item(Lingo2World.static_logic.item_id_to_name.get(item_id),
79 ItemClassification.progression, item_id, PLAYER_NUM), prevent_sweep=True)
80
81 for k, v in self.manager.keyboard.items():
82 # Unless all level 1 letters are pre-unlocked, H1 I1 N1 and T1 act differently between the generator and
83 # game. The generator considers them to be unlocked, which means they are not included in logic
84 # requirements, and only one item/event is needed to unlock their level 2 forms. The game considers them to
85 # be vanilla, which means you still have to pick them up in the Starting Room in order for them to appear on
86 # your keyboard. This also means that whether or not you have the level 1 forms should be synced to the
87 # multiworld. The tracker specifically should collect one fewer item for these letters in this scenario.
88 tv = v
89 if k in "hint" and self.world.options.shuffle_letters in [ShuffleLetters.option_vanilla,
90 ShuffleLetters.option_progressive]:
91 tv = max(0, v - 1)
92
93 if tv > 0:
94 for i in range(tv):
95 self.state.collect(Lingo2Item(k.upper(), ItemClassification.progression, None, PLAYER_NUM),
96 prevent_sweep=True)
97
98 for port_id in self.manager.worldports:
99 self.state.collect(Lingo2Item(f"Worldport {port_id} Entered", ItemClassification.progression, None,
100 PLAYER_NUM), prevent_sweep=True)
101
102 self.state.sweep_for_advancements()
103 self.state.update_reachable_regions(PLAYER_NUM)
104
105 self.accessible_locations = set()
106 self.accessible_worldports = set()
107 self.goal_accessible = False
108
109 for region in self.state.reachable_regions[PLAYER_NUM]:
110 for location in region.locations:
111 if location.access_rule(self.state):
112 if location.address is not None:
113 if location.address not in self.checked_locations:
114 self.accessible_locations.add(location.address)
115 elif hasattr(location, "port_id"):
116 if location.port_id not in self.manager.worldports:
117 self.accessible_worldports.add(location.port_id)
118 elif hasattr(location, "goal") and location.goal:
119 if not self.manager.goaled:
120 self.goal_accessible = True
121
122 def get_path_to_location(self, location_id: int) -> list[str] | None:
123 location_name = self.world.location_id_to_name.get(location_id)
124 location = self.multiworld.get_location(location_name, PLAYER_NUM)
125 return self.get_logical_path(location.parent_region)
126
127 def get_path_to_port(self, port_id: int) -> list[str] | None:
128 port = self.world.static_logic.objects.ports[port_id]
129 region_name = self.world.static_logic.get_room_region_name(port.room_id)
130 region = self.multiworld.get_region(region_name, PLAYER_NUM)
131 return self.get_logical_path(region)
132
133 def get_path_to_goal(self):
134 room_id = self.world.player_logic.goal_room_id
135 region_name = self.world.static_logic.get_room_region_name(room_id)
136 region = self.multiworld.get_region(region_name, PLAYER_NUM)
137 return self.get_logical_path(region)
138
139 def get_logical_path(self, region: Region) -> list[str] | None:
140 if region not in self.state.path:
141 return None
142
143 def flist_to_iter(path_value) -> Iterator[str]:
144 while path_value:
145 region_or_entrance, path_value = path_value
146 yield region_or_entrance
147
148 reversed_path = self.state.path.get(region)
149 flat_path = reversed(list(map(str, flist_to_iter(reversed_path))))
150
151 return list(flat_path)[1::2]
diff --git a/apworld/version.py b/apworld/version.py deleted file mode 100644 index a0becae..0000000 --- a/apworld/version.py +++ /dev/null
@@ -1 +0,0 @@
1APWORLD_VERSION = 5
diff --git a/client/Archipelago/client.gd b/client/Archipelago/client.gd deleted file mode 100644 index 843647d..0000000 --- a/client/Archipelago/client.gd +++ /dev/null
@@ -1,417 +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 _ws.inbound_buffer_size = 8388608
51
52 global._print("Instantiated APClient")
53
54 # Read AP datapackages from file, if there are any
55 if FileAccess.file_exists("user://ap_datapackages"):
56 var file = FileAccess.open("user://ap_datapackages", FileAccess.READ)
57 var data = file.get_var(true)
58 file.close()
59
60 if typeof(data) != TYPE_DICTIONARY:
61 global._print("AP datapackages file is corrupted")
62 data = {}
63
64 _datapackages = data
65
66 processDatapackages()
67
68
69func _ready():
70 pass
71 #_ws.connect("connection_closed", _closed)
72 #_ws.connect("connection_failed", _closed)
73 #_ws.connect("server_disconnected", _closed)
74 #_ws.connect("connection_error", _errored)
75 #_ws.connect("connection_established", _connected)
76
77
78func _reset_state():
79 _should_process = false
80 _authenticated = false
81 _try_wss = false
82 _has_connected = false
83 _received_items = {}
84 _received_indexes = []
85
86
87func _errored():
88 if _try_wss:
89 global._print("Could not connect to AP with ws://, now trying wss://")
90 connectToServer(ap_server, ap_user, ap_pass)
91 else:
92 global._print("AP connection failed")
93 _reset_state()
94
95 emit_signal(
96 "could_not_connect",
97 "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information."
98 )
99
100
101func _closed(_was_clean = true):
102 global._print("Connection closed")
103 _reset_state()
104
105 if not _initiated_disconnect:
106 emit_signal("could_not_connect", "Disconnected from Archipelago")
107
108 _initiated_disconnect = false
109
110
111func _connected(_proto = ""):
112 global._print("Connected!")
113 _try_wss = false
114
115
116func disconnect_from_ap():
117 _initiated_disconnect = true
118 _ws.close()
119
120
121func _process(_delta):
122 if _should_process:
123 _ws.poll()
124
125 var state = _ws.get_ready_state()
126 if state == WebSocketPeer.STATE_OPEN:
127 if not _has_connected:
128 _has_connected = true
129
130 _connected()
131
132 while _ws.get_available_packet_count():
133 var packet = _ws.get_packet()
134 global._print("Got data from server: " + packet.get_string_from_utf8())
135 var json = JSON.new()
136 var jserror = json.parse(packet.get_string_from_utf8())
137 if jserror != OK:
138 global._print("Error parsing packet from AP: " + jserror.error_string)
139 return
140
141 for message in json.data:
142 var cmd = message["cmd"]
143 global._print("Received command: " + cmd)
144
145 if cmd == "RoomInfo":
146 _seed = message["seed_name"]
147 _remote_version = message["version"]
148 _gen_version = message["generator_version"]
149
150 var needed_games = []
151 for game in message["datapackage_checksums"].keys():
152 if (
153 !_datapackages.has(game)
154 or (
155 _datapackages[game]["checksum"]
156 != message["datapackage_checksums"][game]
157 )
158 ):
159 needed_games.append(game)
160
161 if !needed_games.is_empty():
162 _pending_packages = needed_games
163 var cur_needed = _pending_packages.pop_front()
164 requestDatapackages([cur_needed])
165 else:
166 connectToRoom()
167
168 elif cmd == "DataPackage":
169 for game in message["data"]["games"].keys():
170 _datapackages[game] = message["data"]["games"][game]
171 saveDatapackages()
172
173 if !_pending_packages.is_empty():
174 var cur_needed = _pending_packages.pop_front()
175 requestDatapackages([cur_needed])
176 else:
177 processDatapackages()
178 connectToRoom()
179
180 elif cmd == "Connected":
181 _authenticated = true
182 _team = message["team"]
183 _slot = message["slot"]
184 _players = message["players"]
185 _checked_locations = message["checked_locations"]
186 _slot_data = message["slot_data"]
187
188 for player in _players:
189 _player_name_by_slot[player["slot"]] = player["alias"]
190 _game_by_player[player["slot"]] = message["slot_info"][str(
191 player["slot"]
192 )]["game"]
193
194 emit_signal("client_connected", _slot_data)
195
196 elif cmd == "ConnectionRefused":
197 var error_message = ""
198 for error in message["errors"]:
199 var submsg = ""
200 if error == "InvalidSlot":
201 submsg = "Invalid player name."
202 elif error == "InvalidGame":
203 submsg = "The specified player is not playing Lingo."
204 elif error == "IncompatibleVersion":
205 submsg = (
206 "The Archipelago server is not the correct version for this client. Expected v%d.%d.%d. Found v%d.%d.%d."
207 % [
208 ap_version["major"],
209 ap_version["minor"],
210 ap_version["build"],
211 _remote_version["major"],
212 _remote_version["minor"],
213 _remote_version["build"]
214 ]
215 )
216 elif error == "InvalidPassword":
217 submsg = "Incorrect password."
218 elif error == "InvalidItemsHandling":
219 submsg = "Invalid item handling flag. This is a bug with the client."
220
221 if submsg != "":
222 if error_message != "":
223 error_message += " "
224 error_message += submsg
225
226 if error_message == "":
227 error_message = "Unknown error."
228
229 _initiated_disconnect = true
230 _ws.close()
231
232 emit_signal("could_not_connect", error_message)
233 global._print("Connection to AP refused")
234 global._print(message)
235
236 elif cmd == "ReceivedItems":
237 var i = 0
238 for item in message["items"]:
239 var index = int(message["index"] + i)
240 i += 1
241
242 if _received_indexes.has(index):
243 # Do not re-process items.
244 continue
245
246 _received_indexes.append(index)
247
248 var item_id = int(item["item"])
249 _received_items[item_id] = _received_items.get(item_id, 0) + 1
250
251 emit_signal(
252 "item_received",
253 item_id,
254 index,
255 int(item["player"]),
256 int(item["flags"]),
257 _received_items[item_id]
258 )
259
260 elif cmd == "PrintJSON":
261 emit_signal("message_received", message)
262
263 elif cmd == "LocationInfo":
264 for loc in message["locations"]:
265 emit_signal(
266 "location_scout_received",
267 int(loc["item"]),
268 int(loc["location"]),
269 int(loc["player"]),
270 int(loc["flags"])
271 )
272
273 elif state == WebSocketPeer.STATE_CLOSED:
274 if _has_connected:
275 _closed()
276 else:
277 _errored()
278
279
280func saveDatapackages():
281 # Save the AP datapackages to disk.
282 var file = FileAccess.open("user://ap_datapackages", FileAccess.WRITE)
283 file.store_var(_datapackages, true)
284 file.close()
285
286
287func connectToServer(server, un, pw):
288 ap_server = server
289 ap_user = un
290 ap_pass = pw
291
292 _initiated_disconnect = false
293
294 var url = ""
295 if ap_server.begins_with("ws://") or ap_server.begins_with("wss://"):
296 url = ap_server
297 _try_wss = false
298 elif _try_wss:
299 url = "wss://" + ap_server
300 _try_wss = false
301 else:
302 url = "ws://" + ap_server
303 _try_wss = true
304
305 var err = _ws.connect_to_url(url)
306 if err != OK:
307 emit_signal(
308 "could_not_connect",
309 (
310 "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information. Error code: %d."
311 % err
312 )
313 )
314 global._print("Could not connect to AP: %d" % err)
315 return
316 _should_process = true
317
318 emit_signal("connect_status", "Connecting...")
319
320
321func sendMessage(msg):
322 var payload = JSON.stringify(msg)
323 _ws.send_text(payload)
324
325
326func requestDatapackages(games):
327 emit_signal("connect_status", "Downloading %s data package..." % games[0])
328
329 sendMessage([{"cmd": "GetDataPackage", "games": games}])
330
331
332func processDatapackages():
333 _item_id_to_name = {}
334 _location_id_to_name = {}
335 for game in _datapackages.keys():
336 var package = _datapackages[game]
337
338 _item_id_to_name[game] = {}
339 for item_name in package["item_name_to_id"].keys():
340 _item_id_to_name[game][int(package["item_name_to_id"][item_name])] = item_name
341
342 _location_id_to_name[game] = {}
343 for location_name in package["location_name_to_id"].keys():
344 _location_id_to_name[game][int(package["location_name_to_id"][location_name])] = location_name
345
346 if _datapackages.has("Lingo 2"):
347 _item_name_to_id = _datapackages["Lingo 2"]["item_name_to_id"]
348 _location_name_to_id = _datapackages["Lingo 2"]["location_name_to_id"]
349
350
351func connectToRoom():
352 emit_signal("connect_status", "Authenticating...")
353
354 sendMessage(
355 [
356 {
357 "cmd": "Connect",
358 "password": ap_pass,
359 "game": "Lingo 2",
360 "name": ap_user,
361 "uuid": SCRIPT_uuid.v4(),
362 "version": ap_version,
363 "items_handling": 0b111, # always receive our items
364 "tags": [],
365 "slot_data": true
366 }
367 ]
368 )
369
370
371func sendConnectUpdate(tags):
372 sendMessage([{"cmd": "ConnectUpdate", "tags": tags}])
373
374
375func requestSync():
376 sendMessage([{"cmd": "Sync"}])
377
378
379func sendLocation(loc_id):
380 sendMessage([{"cmd": "LocationChecks", "locations": [loc_id]}])
381
382
383func sendLocations(loc_ids):
384 sendMessage([{"cmd": "LocationChecks", "locations": loc_ids}])
385
386
387func setValue(key, value, operation = "replace"):
388 sendMessage(
389 [
390 {
391 "cmd": "Set",
392 "key": "Lingo2_%d_%s" % [_slot, key],
393 "want_reply": false,
394 "operations": [{"operation": operation, "value": value}]
395 }
396 ]
397 )
398
399
400func say(textdata):
401 sendMessage([{"cmd": "Say", "text": textdata}])
402
403
404func completedGoal():
405 sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL
406
407
408func scoutLocations(loc_ids):
409 sendMessage([{"cmd": "LocationScouts", "locations": loc_ids}])
410
411
412func hasItem(item_id):
413 return _received_items.has(item_id)
414
415
416func getItemAmount(item_id):
417 return _received_items.get(item_id, 0)
diff --git a/client/Archipelago/gamedata.gd b/client/Archipelago/gamedata.gd deleted file mode 100644 index 41d966a..0000000 --- a/client/Archipelago/gamedata.gd +++ /dev/null
@@ -1,140 +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 panel_id_by_map_node_path = {}
9var door_id_by_ap_id = {}
10var map_id_by_name = {}
11var progressive_id_by_ap_id = {}
12var letter_id_by_ap_id = {}
13var symbol_item_ids = []
14var anti_trap_ids = {}
15
16var kSYMBOL_ITEMS
17
18
19func _init(proto_script):
20 SCRIPT_proto = proto_script
21
22 kSYMBOL_ITEMS = {
23 SCRIPT_proto.PuzzleSymbol.SUN: "Sun Symbol",
24 SCRIPT_proto.PuzzleSymbol.SPARKLES: "Sparkles Symbol",
25 SCRIPT_proto.PuzzleSymbol.ZERO: "Zero Symbol",
26 SCRIPT_proto.PuzzleSymbol.EXAMPLE: "Example Symbol",
27 SCRIPT_proto.PuzzleSymbol.BOXES: "Boxes Symbol",
28 SCRIPT_proto.PuzzleSymbol.PLANET: "Planet Symbol",
29 SCRIPT_proto.PuzzleSymbol.PYRAMID: "Pyramid Symbol",
30 SCRIPT_proto.PuzzleSymbol.CROSS: "Cross Symbol",
31 SCRIPT_proto.PuzzleSymbol.SWEET: "Sweet Symbol",
32 SCRIPT_proto.PuzzleSymbol.GENDER: "Gender Symbol",
33 SCRIPT_proto.PuzzleSymbol.AGE: "Age Symbol",
34 SCRIPT_proto.PuzzleSymbol.SOUND: "Sound Symbol",
35 SCRIPT_proto.PuzzleSymbol.ANAGRAM: "Anagram Symbol",
36 SCRIPT_proto.PuzzleSymbol.JOB: "Job Symbol",
37 SCRIPT_proto.PuzzleSymbol.STARS: "Stars Symbol",
38 SCRIPT_proto.PuzzleSymbol.NULL: "Null Symbol",
39 SCRIPT_proto.PuzzleSymbol.EVAL: "Eval Symbol",
40 SCRIPT_proto.PuzzleSymbol.LINGO: "Lingo Symbol",
41 SCRIPT_proto.PuzzleSymbol.QUESTION: "Question Symbol",
42 }
43
44
45func load(data_bytes):
46 objects = SCRIPT_proto.AllObjects.new()
47
48 var result_code = objects.from_bytes(data_bytes)
49 if result_code != SCRIPT_proto.PB_ERR.NO_ERRORS:
50 print("Could not load generated data: %d" % result_code)
51 return
52
53 for map in objects.get_maps():
54 map_id_by_name[map.get_name()] = map.get_id()
55
56 for door in objects.get_doors():
57 var map = objects.get_maps()[door.get_map_id()]
58
59 if not map.get_name() in door_id_by_map_node_path:
60 door_id_by_map_node_path[map.get_name()] = {}
61
62 var map_data = door_id_by_map_node_path[map.get_name()]
63 for receiver in door.get_receivers():
64 map_data[receiver] = door.get_id()
65
66 for painting_id in door.get_move_paintings():
67 var painting = objects.get_paintings()[painting_id]
68 map_data[painting.get_path()] = door.get_id()
69
70 if door.has_ap_id():
71 door_id_by_ap_id[door.get_ap_id()] = door.get_id()
72
73 for painting in objects.get_paintings():
74 var room = objects.get_rooms()[painting.get_room_id()]
75 var map = objects.get_maps()[room.get_map_id()]
76
77 if not map.get_name() in painting_id_by_map_node_path:
78 painting_id_by_map_node_path[map.get_name()] = {}
79
80 var _map_data = painting_id_by_map_node_path[map.get_name()]
81
82 for progressive in objects.get_progressives():
83 progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id()
84
85 for letter in objects.get_letters():
86 letter_id_by_ap_id[letter.get_ap_id()] = letter.get_id()
87
88 for panel in objects.get_panels():
89 var room = objects.get_rooms()[panel.get_room_id()]
90 var map = objects.get_maps()[room.get_map_id()]
91
92 if not map.get_name() in panel_id_by_map_node_path:
93 panel_id_by_map_node_path[map.get_name()] = {}
94
95 var map_data = panel_id_by_map_node_path[map.get_name()]
96 map_data[panel.get_path()] = panel.get_id()
97
98 for symbol_name in kSYMBOL_ITEMS.values():
99 symbol_item_ids.append(objects.get_special_ids()[symbol_name])
100
101 for special_name in objects.get_special_ids().keys():
102 if special_name.begins_with("Anti "):
103 anti_trap_ids[objects.get_special_ids()[special_name]] = (
104 special_name.substr(5).to_lower()
105 )
106
107
108func get_door_for_map_node_path(map_name, node_path):
109 if not door_id_by_map_node_path.has(map_name):
110 return null
111
112 var map_data = door_id_by_map_node_path[map_name]
113 return map_data.get(node_path, null)
114
115
116func get_panel_for_map_node_path(map_name, node_path):
117 if not panel_id_by_map_node_path.has(map_name):
118 return null
119
120 var map_data = panel_id_by_map_node_path[map_name]
121 return map_data.get(node_path, null)
122
123
124func get_door_ap_id(door_id):
125 var door = objects.get_doors()[door_id]
126 if door.has_ap_id():
127 return door.get_ap_id()
128 else:
129 return null
130
131
132func get_door_receivers(door_id):
133 var door = objects.get_doors()[door_id]
134 return door.get_receivers()
135
136
137func get_door_map_name(door_id):
138 var door = objects.get_doors()[door_id]
139 var map = objects.get_maps()[door.get_map_id()]
140 return map.get_name()
diff --git a/client/Archipelago/manager.gd b/client/Archipelago/manager.gd deleted file mode 100644 index a585167..0000000 --- a/client/Archipelago/manager.gd +++ /dev/null
@@ -1,544 +0,0 @@
1extends Node
2
3const MOD_VERSION = 6
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 = []
15var show_compass = false
16
17var client
18var keyboard
19
20var _localdata_file = ""
21var _last_new_item = -1
22var _batch_locations = false
23var _held_locations = []
24var _held_location_scouts = []
25var _location_scouts = {}
26var _item_locks = {}
27var _inverse_item_locks = {}
28var _held_letters = {}
29var _letters_setup = false
30
31const kSHUFFLE_LETTERS_VANILLA = 0
32const kSHUFFLE_LETTERS_UNLOCKED = 1
33const kSHUFFLE_LETTERS_PROGRESSIVE = 2
34const kSHUFFLE_LETTERS_VANILLA_CYAN = 3
35const kSHUFFLE_LETTERS_ITEM_CYAN = 4
36
37const kLETTER_BEHAVIOR_VANILLA = 0
38const kLETTER_BEHAVIOR_ITEM = 1
39const kLETTER_BEHAVIOR_UNLOCKED = 2
40
41const kCYAN_DOOR_BEHAVIOR_H2 = 0
42const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1
43const kCYAN_DOOR_BEHAVIOR_ITEM = 2
44
45var apworld_version = [0, 0]
46var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
47var daedalus_roof_access = false
48var keyholder_sanity = false
49var shuffle_control_center_colors = false
50var shuffle_doors = false
51var shuffle_gallery_paintings = false
52var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
53var shuffle_symbols = false
54var strict_cyan_ending = false
55var strict_purple_ending = false
56var victory_condition = -1
57
58signal could_not_connect
59signal connect_status
60signal ap_connected
61
62
63func _init():
64 # Read AP settings from file, if there are any
65 if FileAccess.file_exists("user://ap_settings"):
66 var file = FileAccess.open("user://ap_settings", FileAccess.READ)
67 var data = file.get_var(true)
68 file.close()
69
70 if typeof(data) != TYPE_ARRAY:
71 global._print("AP settings file is corrupted")
72 data = []
73
74 if data.size() > 0:
75 ap_server = data[0]
76
77 if data.size() > 1:
78 ap_user = data[1]
79
80 if data.size() > 2:
81 ap_pass = data[2]
82
83 if data.size() > 3:
84 connection_history = data[3]
85
86 if data.size() > 4:
87 show_compass = data[4]
88
89
90func _ready():
91 client = SCRIPT_client.new()
92 client.SCRIPT_uuid = SCRIPT_uuid
93
94 client.connect("item_received", _process_item)
95 client.connect("message_received", _process_message)
96 client.connect("location_scout_received", _process_location_scout)
97 client.connect("could_not_connect", _client_could_not_connect)
98 client.connect("connect_status", _client_connect_status)
99 client.connect("client_connected", _client_connected)
100
101 add_child(client)
102
103 keyboard = SCRIPT_keyboard.new()
104 add_child(keyboard)
105
106
107func saveSettings():
108 # Save the AP settings to disk.
109 var path = "user://ap_settings"
110 var file = FileAccess.open(path, FileAccess.WRITE)
111
112 var data = [
113 ap_server,
114 ap_user,
115 ap_pass,
116 connection_history,
117 show_compass,
118 ]
119 file.store_var(data, true)
120 file.close()
121
122
123func saveLocaldata():
124 # Save the MW/slot specific settings to disk.
125 var dir = DirAccess.open("user://")
126 var folder = "archipelago_data"
127 if not dir.dir_exists(folder):
128 dir.make_dir(folder)
129
130 var file = FileAccess.open(_localdata_file, FileAccess.WRITE)
131
132 var data = [
133 _last_new_item,
134 ]
135 file.store_var(data, true)
136 file.close()
137
138
139func connectToServer():
140 _last_new_item = -1
141 _batch_locations = false
142 _held_locations = []
143 _held_location_scouts = []
144 _location_scouts = {}
145 _letters_setup = false
146 _held_letters = {}
147
148 client.connectToServer(ap_server, ap_user, ap_pass)
149
150
151func getSaveFileName():
152 return "zzAP_%s_%d" % [client._seed, client._slot]
153
154
155func disconnect_from_ap():
156 client.disconnect_from_ap()
157
158
159func get_item_id_for_door(door_id):
160 return _item_locks.get(door_id, null)
161
162
163func _process_item(item, index, from, flags, amount):
164 var item_name = "Unknown"
165 if client._item_id_to_name["Lingo 2"].has(item):
166 item_name = client._item_id_to_name["Lingo 2"][item]
167
168 var gamedata = global.get_node("Gamedata")
169
170 var prog_id = null
171 if _inverse_item_locks.has(item):
172 for lock in _inverse_item_locks.get(item):
173 if lock[1] != amount:
174 continue
175
176 if gamedata.progressive_id_by_ap_id.has(item):
177 prog_id = lock[0]
178
179 if gamedata.get_door_map_name(lock[0]) != global.map:
180 continue
181
182 var receivers = gamedata.get_door_receivers(lock[0])
183 var scene = get_tree().get_root().get_node_or_null("scene")
184 if scene != null:
185 for receiver in receivers:
186 var rnode = scene.get_node_or_null(receiver)
187 if rnode != null:
188 rnode.handleTriggered()
189
190 var letter_id = gamedata.letter_id_by_ap_id.get(item, null)
191 if letter_id != null:
192 var letter = gamedata.objects.get_letters()[letter_id]
193 if not letter.has_level2() or not letter.get_level2():
194 _process_key_item(letter.get_key(), amount)
195
196 if gamedata.symbol_item_ids.has(item):
197 var player = get_tree().get_root().get_node_or_null("scene/player")
198 if player != null:
199 player.emit_signal("evaluate_solvability")
200
201 # Show a message about the item if it's new.
202 if index != null and index > _last_new_item:
203 _last_new_item = index
204 saveLocaldata()
205
206 var player_name = "Unknown"
207 if client._player_name_by_slot.has(float(from)):
208 player_name = client._player_name_by_slot[float(from)]
209
210 var item_color = colorForItemType(flags)
211
212 var full_item_name = item_name
213 if prog_id != null:
214 var door = gamedata.objects.get_doors()[prog_id]
215 full_item_name = "%s (%s)" % [item_name, door.get_name()]
216
217 var message
218 if from == client._slot:
219 message = "Found [color=%s]%s[/color]" % [item_color, full_item_name]
220 else:
221 message = (
222 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name]
223 )
224
225 if gamedata.anti_trap_ids.has(item):
226 keyboard.block_letter(gamedata.anti_trap_ids[item])
227
228 global._print(message)
229
230 global.get_node("Messages").showMessage(message)
231
232
233func _process_message(message):
234 parse_printjson_for_textclient(message)
235
236 if (
237 !message.has("receiving")
238 or !message.has("item")
239 or message["item"]["player"] != client._slot
240 ):
241 return
242
243 var item_name = "Unknown"
244 var item_player_game = client._game_by_player[message["receiving"]]
245 if client._item_id_to_name[item_player_game].has(int(message["item"]["item"])):
246 item_name = client._item_id_to_name[item_player_game][int(message["item"]["item"])]
247
248 var location_name = "Unknown"
249 var location_player_game = client._game_by_player[message["item"]["player"]]
250 if client._location_id_to_name[location_player_game].has(int(message["item"]["location"])):
251 location_name = (client._location_id_to_name[location_player_game][int(
252 message["item"]["location"]
253 )])
254
255 var player_name = "Unknown"
256 if client._player_name_by_slot.has(message["receiving"]):
257 player_name = client._player_name_by_slot[message["receiving"]]
258
259 var item_color = colorForItemType(message["item"]["flags"])
260
261 if message["type"] == "Hint":
262 var is_for = ""
263 if message["receiving"] != client._slot:
264 is_for = " for %s" % player_name
265 if !message.has("found") || !message["found"]:
266 global.get_node("Messages").showMessage(
267 (
268 "Hint: [color=%s]%s[/color]%s is on %s"
269 % [item_color, item_name, is_for, location_name]
270 )
271 )
272 else:
273 if message["receiving"] != client._slot:
274 var sentMsg = "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name]
275 #if _hinted_locations.has(message["item"]["location"]):
276 # sentMsg += " ([color=#fafad2]Hinted![/color])"
277 global.get_node("Messages").showMessage(sentMsg)
278
279
280func parse_printjson_for_textclient(message):
281 var parts = []
282 for message_part in message["data"]:
283 if !message_part.has("type") and message_part.has("text"):
284 parts.append(message_part["text"])
285 elif message_part["type"] == "player_id":
286 if int(message_part["text"]) == client._slot:
287 parts.append(
288 "[color=#ee00ee]%s[/color]" % client._player_name_by_slot[client._slot]
289 )
290 else:
291 var from = float(message_part["text"])
292 parts.append("[color=#fafad2]%s[/color]" % client._player_name_by_slot[from])
293 elif message_part["type"] == "item_id":
294 var item_name = "Unknown"
295 var item_player_game = client._game_by_player[message_part["player"]]
296 if client._item_id_to_name[item_player_game].has(int(message_part["text"])):
297 item_name = client._item_id_to_name[item_player_game][int(message_part["text"])]
298
299 parts.append(
300 "[color=%s]%s[/color]" % [colorForItemType(message_part["flags"]), item_name]
301 )
302 elif message_part["type"] == "location_id":
303 var location_name = "Unknown"
304 var location_player_game = client._game_by_player[message_part["player"]]
305 if client._location_id_to_name[location_player_game].has(int(message_part["text"])):
306 location_name = client._location_id_to_name[location_player_game][int(
307 message_part["text"]
308 )]
309
310 parts.append("[color=#00ff7f]%s[/color]" % location_name)
311 elif message_part.has("text"):
312 parts.append(message_part["text"])
313
314 var textclient_node = global.get_node("Textclient")
315 if textclient_node != null:
316 textclient_node.parse_printjson("".join(parts))
317
318
319func _process_location_scout(item_id, location_id, player, flags):
320 _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags}
321
322 if player == client._slot and flags & 4 != 0:
323 # This is a trap for us, so let's not display it.
324 return
325
326 var gamedata = global.get_node("Gamedata")
327 var map_id = gamedata.map_id_by_name.get(global.map)
328
329 var item_name = "Unknown"
330 var item_player_game = client._game_by_player[float(player)]
331 if client._item_id_to_name[item_player_game].has(item_id):
332 item_name = client._item_id_to_name[item_player_game][item_id]
333
334 var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null)
335 if letter_id != null:
336 var letter = gamedata.objects.get_letters()[letter_id]
337 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
338 if room.get_map_id() == map_id:
339 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
340 letter.get_path()
341 )
342 if collectable != null:
343 collectable.setScoutedText(item_name)
344
345
346func _client_could_not_connect(message):
347 emit_signal("could_not_connect", message)
348
349
350func _client_connect_status(message):
351 emit_signal("connect_status", message)
352
353
354func _client_connected(slot_data):
355 var gamedata = global.get_node("Gamedata")
356
357 _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot]
358 _last_new_item = -1
359
360 if FileAccess.file_exists(_localdata_file):
361 var ap_file = FileAccess.open(_localdata_file, FileAccess.READ)
362 var localdata = []
363 if ap_file != null:
364 localdata = ap_file.get_var(true)
365 ap_file.close()
366
367 if typeof(localdata) != TYPE_ARRAY:
368 print("AP localdata file is corrupted")
369 localdata = []
370
371 if localdata.size() > 0:
372 _last_new_item = localdata[0]
373
374 # Read slot data.
375 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0))
376 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false))
377 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
378 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
379 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
380 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false))
381 shuffle_letters = int(slot_data.get("shuffle_letters", 0))
382 shuffle_symbols = bool(slot_data.get("shuffle_symbols", false))
383 strict_cyan_ending = bool(slot_data.get("strict_cyan_ending", false))
384 strict_purple_ending = bool(slot_data.get("strict_purple_ending", false))
385 victory_condition = int(slot_data.get("victory_condition", 0))
386
387 if slot_data.has("version"):
388 apworld_version = [int(slot_data["version"][0]), int(slot_data["version"][1])]
389
390 # Set up item locks.
391 _item_locks = {}
392
393 if shuffle_doors:
394 for door in gamedata.objects.get_doors():
395 if (
396 door.get_type() == gamedata.SCRIPT_proto.DoorType.STANDARD
397 or door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
398 ):
399 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
400
401 for progressive in gamedata.objects.get_progressives():
402 for i in range(0, progressive.get_doors().size()):
403 var door = gamedata.objects.get_doors()[progressive.get_doors()[i]]
404 _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1]
405
406 for door_group in gamedata.objects.get_door_groups():
407 if (
408 door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR
409 or door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.SHUFFLE_GROUP
410 ):
411 for door in door_group.get_doors():
412 _item_locks[door] = [door_group.get_ap_id(), 1]
413
414 if shuffle_control_center_colors:
415 for door in gamedata.objects.get_doors():
416 if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR:
417 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
418
419 for door_group in gamedata.objects.get_door_groups():
420 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR:
421 for door in door_group.get_doors():
422 _item_locks[door] = [door_group.get_ap_id(), 1]
423
424 if shuffle_gallery_paintings:
425 for door in gamedata.objects.get_doors():
426 if door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING:
427 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
428
429 if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM:
430 for door_group in gamedata.objects.get_door_groups():
431 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS:
432 for door in door_group.get_doors():
433 if not _item_locks.has(door):
434 _item_locks[door] = [door_group.get_ap_id(), 1]
435
436 # Create a reverse item locks map for processing items.
437 _inverse_item_locks = {}
438
439 for door_id in _item_locks.keys():
440 var lock = _item_locks.get(door_id)
441
442 if not _inverse_item_locks.has(lock[0]):
443 _inverse_item_locks[lock[0]] = []
444
445 _inverse_item_locks[lock[0]].append([door_id, lock[1]])
446
447 emit_signal("ap_connected")
448
449
450func start_batching_locations():
451 _batch_locations = true
452
453
454func send_location(loc_id):
455 if _batch_locations:
456 _held_locations.append(loc_id)
457 else:
458 client.sendLocation(loc_id)
459
460
461func scout_location(loc_id):
462 if _location_scouts.has(loc_id):
463 return _location_scouts.get(loc_id)
464
465 if _batch_locations:
466 _held_location_scouts.append(loc_id)
467 else:
468 client.scoutLocation(loc_id)
469
470 return null
471
472
473func stop_batching_locations():
474 _batch_locations = false
475
476 if not _held_locations.is_empty():
477 client.sendLocations(_held_locations)
478 _held_locations.clear()
479
480 if not _held_location_scouts.is_empty():
481 client.scoutLocations(_held_location_scouts)
482 _held_location_scouts.clear()
483
484
485func colorForItemType(flags):
486 var int_flags = int(flags)
487 if int_flags & 1: # progression
488 if int_flags & 2: # proguseful
489 return "#f0d200"
490 else:
491 return "#bc51e0"
492 elif int_flags & 2: # useful
493 return "#2b67ff"
494 elif int_flags & 4: # trap
495 return "#d63a22"
496 else: # filler
497 return "#14de9e"
498
499
500func get_letter_behavior(key, level2):
501 if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED:
502 return kLETTER_BEHAVIOR_UNLOCKED
503
504 if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters):
505 if level2:
506 if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN:
507 return kLETTER_BEHAVIOR_VANILLA
508 else:
509 return kLETTER_BEHAVIOR_ITEM
510 else:
511 return kLETTER_BEHAVIOR_UNLOCKED
512
513 if not level2 and ["h", "i", "n", "t"].has(key):
514 # This differs from the equivalent function in the apworld. Logically it is
515 # the same as UNLOCKED since they are in the starting room, but VANILLA
516 # means the player still has to actually pick up the letters.
517 return kLETTER_BEHAVIOR_VANILLA
518
519 if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE:
520 return kLETTER_BEHAVIOR_ITEM
521
522 return kLETTER_BEHAVIOR_VANILLA
523
524
525func setup_keys():
526 keyboard.load_seed()
527
528 _letters_setup = true
529
530 for k in _held_letters.keys():
531 _process_key_item(k, _held_letters[k])
532
533 _held_letters.clear()
534
535
536func _process_key_item(key, level):
537 if not _letters_setup:
538 _held_letters[key] = max(_held_letters.get(key, 0), level)
539 return
540
541 if shuffle_letters == kSHUFFLE_LETTERS_ITEM_CYAN:
542 level += 1
543
544 keyboard.collect_remote_letter(key, level)
diff --git a/client/Archipelago/pauseMenu.gd b/client/Archipelago/pauseMenu.gd deleted file mode 100644 index cd1813c..0000000 --- a/client/Archipelago/pauseMenu.gd +++ /dev/null
@@ -1,44 +0,0 @@
1extends "res://scripts/ui/pauseMenu.gd"
2
3var compass_button
4
5
6func _ready():
7 var ap_panel = Panel.new()
8 ap_panel.name = "Archipelago"
9 get_node("menu/settings/settingsInner/TabContainer").add_child(ap_panel)
10
11 var ap = global.get_node("Archipelago")
12
13 compass_button = CheckBox.new()
14 compass_button.text = "show compass"
15 compass_button.button_pressed = ap.show_compass
16 compass_button.position = Vector2(65, 100)
17 compass_button.theme = preload("res://assets/themes/baseUI.tres")
18 compass_button.add_theme_font_size_override("font_size", 60)
19 compass_button.pressed.connect(_toggle_compass)
20 ap_panel.add_child(compass_button)
21
22 super._ready()
23
24
25func _pause_game():
26 global.get_node("Textclient").dismiss()
27 super._pause_game()
28
29
30func _main_menu():
31 global.loaded = false
32 global.get_node("Archipelago").disconnect_from_ap()
33 global.get_node("Messages").clear()
34 global.get_node("Compass").visible = false
35 super._main_menu()
36
37
38func _toggle_compass():
39 var ap = global.get_node("Archipelago")
40 ap.show_compass = compass_button.button_pressed
41 ap.saveSettings()
42
43 var compass = global.get_node("Compass")
44 compass.visible = compass_button.button_pressed
diff --git a/client/Archipelago/player.gd b/client/Archipelago/player.gd deleted file mode 100644 index 538830f..0000000 --- a/client/Archipelago/player.gd +++ /dev/null
@@ -1,362 +0,0 @@
1extends "res://scripts/nodes/player.gd"
2
3const kEndingNameByVictoryValue = {
4 0: "GRAY",
5 1: "PURPLE",
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
19signal evaluate_solvability
20
21var compass
22
23
24func _ready():
25 var khl_script = load("res://scripts/nodes/keyHolderListener.gd")
26
27 var ap = global.get_node("Archipelago")
28 var gamedata = global.get_node("Gamedata")
29
30 compass = global.get_node("Compass")
31 compass.visible = ap.show_compass
32
33 ap.start_batching_locations()
34
35 # Set up door locations.
36 var map_id = gamedata.map_id_by_name.get(global.map)
37 for door in gamedata.objects.get_doors():
38 if door.get_map_id() != map_id:
39 continue
40
41 if not door.has_ap_id():
42 continue
43
44 if (
45 door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
46 or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING
47 ):
48 continue
49
50 var locationListener = ap.SCRIPT_locationListener.new()
51 locationListener.location_id = door.get_ap_id()
52 locationListener.name = "locationListener_%d" % door.get_ap_id()
53
54 for panel_ref in door.get_panels():
55 var panel_data = gamedata.objects.get_panels()[panel_ref.get_panel()]
56 var panel_path = panel_data.get_path()
57
58 if panel_ref.has_answer():
59 for proxy in panel_data.get_proxies():
60 if proxy.get_answer() == panel_ref.get_answer():
61 panel_path = proxy.get_path()
62 break
63
64 locationListener.senders.append(NodePath("/root/scene/" + panel_path))
65
66 for keyholder_ref in door.get_keyholders():
67 var keyholder_data = gamedata.objects.get_keyholders()[keyholder_ref.get_keyholder()]
68
69 var khl = khl_script.new()
70 khl.name = (
71 "location_%d_keyholder_%d" % [door.get_ap_id(), keyholder_ref.get_keyholder()]
72 )
73 khl.answer = keyholder_ref.get_key()
74 khl.senders.append(NodePath("/root/scene/" + keyholder_data.get_path()))
75 get_parent().add_child.call_deferred(khl)
76
77 locationListener.senders.append(NodePath("../" + khl.name))
78
79 for sender in door.get_senders():
80 locationListener.senders.append(NodePath("/root/scene/" + sender))
81
82 if door.has_complete_at():
83 locationListener.complete_at = door.get_complete_at()
84
85 get_parent().add_child.call_deferred(locationListener)
86
87 # Set up letter locations.
88 for letter in gamedata.objects.get_letters():
89 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
90 if room.get_map_id() != map_id:
91 continue
92
93 var locationListener = ap.SCRIPT_locationListener.new()
94 locationListener.location_id = letter.get_ap_id()
95 locationListener.name = "locationListener_%d" % letter.get_ap_id()
96 locationListener.senders.append(NodePath("/root/scene/" + letter.get_path()))
97
98 get_parent().add_child.call_deferred(locationListener)
99
100 if (
101 ap.get_letter_behavior(letter.get_key(), letter.has_level2() and letter.get_level2())
102 != ap.kLETTER_BEHAVIOR_VANILLA
103 ):
104 var scout = ap.scout_location(letter.get_ap_id())
105 if (
106 scout != null
107 and not (scout["player"] == ap.client._slot and scout["flags"] & 4 != 0)
108 ):
109 var item_name = "Unknown"
110 var item_player_game = ap.client._game_by_player[float(scout["player"])]
111 if ap.client._item_id_to_name[item_player_game].has(scout["item"]):
112 item_name = ap.client._item_id_to_name[item_player_game][scout["item"]]
113
114 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
115 letter.get_path()
116 )
117 if collectable != null:
118 collectable.setScoutedText.call_deferred(item_name)
119
120 # Set up mastery locations.
121 for mastery in gamedata.objects.get_masteries():
122 var room = gamedata.objects.get_rooms()[mastery.get_room_id()]
123 if room.get_map_id() != map_id:
124 continue
125
126 var locationListener = ap.SCRIPT_locationListener.new()
127 locationListener.location_id = mastery.get_ap_id()
128 locationListener.name = "locationListener_%d" % mastery.get_ap_id()
129 locationListener.senders.append(NodePath("/root/scene/" + mastery.get_path()))
130
131 get_parent().add_child.call_deferred(locationListener)
132
133 # Set up ending locations.
134 for ending in gamedata.objects.get_endings():
135 var room = gamedata.objects.get_rooms()[ending.get_room_id()]
136 if room.get_map_id() != map_id:
137 continue
138
139 var locationListener = ap.SCRIPT_locationListener.new()
140 locationListener.location_id = ending.get_ap_id()
141 locationListener.name = "locationListener_%d" % ending.get_ap_id()
142 locationListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
143
144 get_parent().add_child.call_deferred(locationListener)
145
146 if kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name():
147 var victoryListener = ap.SCRIPT_victoryListener.new()
148 victoryListener.name = "victoryListener"
149 victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
150
151 get_parent().add_child.call_deferred(victoryListener)
152
153 # Set up keyholder locations, in keyholder sanity.
154 if ap.keyholder_sanity:
155 for keyholder in gamedata.objects.get_keyholders():
156 if not keyholder.has_key():
157 continue
158
159 var room = gamedata.objects.get_rooms()[keyholder.get_room_id()]
160 if room.get_map_id() != map_id:
161 continue
162
163 var locationListener = ap.SCRIPT_locationListener.new()
164 locationListener.location_id = keyholder.get_ap_id()
165 locationListener.name = "locationListener_%d" % keyholder.get_ap_id()
166
167 var khl = khl_script.new()
168 khl.name = "location_%d_keyholder" % keyholder.get_ap_id()
169 khl.answer = keyholder.get_key()
170 khl.senders.append(NodePath("/root/scene/" + keyholder.get_path()))
171 get_parent().add_child.call_deferred(khl)
172
173 locationListener.senders.append(NodePath("../" + khl.name))
174
175 get_parent().add_child.call_deferred(locationListener)
176
177 # Block off roof access in Daedalus.
178 if global.map == "daedalus" and not ap.daedalus_roof_access:
179 _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49)
180 _set_up_invis_wall(51.5, 11, -17, 16, 10, 1)
181 _set_up_invis_wall(46, 10, -9.5, 1, 10, 10)
182 _set_up_invis_wall(67.5, 11, 17, 16, 10, 1)
183 _set_up_invis_wall(50.5, 11, 14, 10, 10, 1)
184 _set_up_invis_wall(39, 10, 18.5, 1, 10, 22)
185 _set_up_invis_wall(20, 15, 18.5, 1, 10, 16)
186 _set_up_invis_wall(11.5, 15, 3, 32, 10, 1)
187 _set_up_invis_wall(11.5, 16, -20, 14, 20, 1)
188 _set_up_invis_wall(14, 16, -26.5, 1, 20, 4)
189 _set_up_invis_wall(28.5, 20.5, -26.5, 1, 15, 25)
190 _set_up_invis_wall(40.5, 20.5, -11, 30, 15, 1)
191 _set_up_invis_wall(50.5, 15, 5.5, 7, 10, 1)
192 _set_up_invis_wall(83.5, 33.5, 5.5, 1, 7, 11)
193 _set_up_invis_wall(83.5, 33.5, -5.5, 1, 7, 11)
194
195 var warp_exit_prefab = preload("res://objects/nodes/exit.tscn")
196 var warp_exit = warp_exit_prefab.instantiate()
197 warp_exit.name = "roof_access_blocker_warp_exit"
198 warp_exit.position = Vector3(58, 10, 0)
199 warp_exit.rotation_degrees.y = 90
200 get_parent().add_child.call_deferred(warp_exit)
201
202 var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn")
203 var warp_enter = warp_enter_prefab.instantiate()
204 warp_enter.target = warp_exit
205 warp_enter.position = Vector3(76.5, 30, 1)
206 warp_enter.scale = Vector3(4, 1.5, 1)
207 warp_enter.rotation_degrees.y = 90
208 get_parent().add_child.call_deferred(warp_enter)
209
210 if global.map == "the_entry":
211 # Remove door behind X1.
212 var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1")
213 door_node.handleTriggered()
214
215 # Display win condition.
216 var sign_prefab = preload("res://objects/nodes/sign.tscn")
217 var sign1 = sign_prefab.instantiate()
218 sign1.position = Vector3(-7, 5, -15.01)
219 sign1.text = "victory"
220 get_parent().add_child.call_deferred(sign1)
221
222 var sign2 = sign_prefab.instantiate()
223 sign2.position = Vector3(-7, 4, -15.01)
224 sign2.text = "%s ending" % kEndingNameByVictoryValue.get(ap.victory_condition, "?")
225
226 var sign2_color = kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower()
227 if sign2_color == "white":
228 sign2_color = "silver"
229
230 sign2.material = load("res://assets/materials/%s.material" % sign2_color)
231 get_parent().add_child.call_deferred(sign2)
232
233 # Add the strict purple ending validation.
234 if global.map == "the_sun_temple" and ap.strict_purple_ending:
235 var panel_prefab = preload("res://objects/nodes/panel.tscn")
236 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
237 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
238
239 var previous_panel = null
240 var next_y = -100
241 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
242 for word in words:
243 var panel = panel_prefab.instantiate()
244 panel.position = Vector3(0, next_y, 0)
245 next_y -= 10
246 panel.clue = word
247 panel.symbol = ""
248 panel.answer = word
249 panel.name = "EndCheck_%s" % word
250
251 var tpl = tpl_prefab.instantiate()
252 tpl.teleport_point = Vector3(0, 1, 0)
253 tpl.teleport_rotate = Vector3(-45, 180, 0)
254 tpl.target_path = panel
255 tpl.name = "Teleport"
256
257 if previous_panel == null:
258 tpl.senders.append(NodePath("/root/scene/Panels/End/panel_24"))
259 else:
260 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
261
262 var reversing = reverse_prefab.instantiate()
263 reversing.senders.append(NodePath(".."))
264 reversing.name = "Reversing"
265 tpl.senders.append(NodePath("../Reversing"))
266
267 panel.add_child.call_deferred(tpl)
268 panel.add_child.call_deferred(reversing)
269 get_parent().get_node("Panels").add_child.call_deferred(panel)
270
271 previous_panel = panel
272
273 # Duplicate the doors that usually wait on EQUINOX. We can't set the senders
274 # here for some reason so we actually set them in the door ready function.
275 var endplat = get_node("/root/scene/Components/Doors/EndPlatform")
276 var endplat2 = endplat.duplicate()
277 endplat2.name = "spe_EndPlatform"
278 endplat.get_parent().add_child.call_deferred(endplat2)
279 endplat.queue_free()
280
281 var entry2 = get_node("/root/scene/Components/Doors/entry_2")
282 var entry22 = entry2.duplicate()
283 entry22.name = "spe_entry_2"
284 entry2.get_parent().add_child.call_deferred(entry22)
285 entry2.queue_free()
286
287 # Add the strict cyan ending validation.
288 if global.map == "the_parthenon" and ap.strict_cyan_ending:
289 var panel_prefab = preload("res://objects/nodes/panel.tscn")
290 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
291 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
292
293 var previous_panel = null
294 var next_y = -100
295 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
296 for word in words:
297 var panel = panel_prefab.instantiate()
298 panel.position = Vector3(0, next_y, 0)
299 next_y -= 10
300 panel.clue = word
301 panel.symbol = "."
302 panel.answer = "%s%s" % [word, word]
303 panel.name = "EndCheck_%s" % word
304
305 var tpl = tpl_prefab.instantiate()
306 tpl.teleport_point = Vector3(0, 1, -11)
307 tpl.teleport_rotate = Vector3(-45, 0, 0)
308 tpl.target_path = panel
309 tpl.name = "Teleport"
310
311 if previous_panel == null:
312 tpl.senderGroup.append(NodePath("/root/scene/Panels/Rulers"))
313 else:
314 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
315
316 var reversing = reverse_prefab.instantiate()
317 reversing.senders.append(NodePath(".."))
318 reversing.name = "Reversing"
319 tpl.senders.append(NodePath("../Reversing"))
320
321 panel.add_child.call_deferred(tpl)
322 panel.add_child.call_deferred(reversing)
323 get_parent().get_node("Panels").add_child.call_deferred(panel)
324
325 previous_panel = panel
326
327 # Duplicate the door that usually waits on the rulers. We can't set the
328 # senders here for some reason so we actually set them in the door ready
329 # function.
330 var entry1 = get_node("/root/scene/Components/Doors/entry_1")
331 var entry12 = entry1.duplicate()
332 entry12.name = "spe_entry_1"
333 entry1.get_parent().add_child.call_deferred(entry12)
334 entry1.queue_free()
335
336 super._ready()
337
338 await get_tree().process_frame
339 await get_tree().process_frame
340
341 ap.stop_batching_locations()
342
343
344func _set_up_invis_wall(x, y, z, sx, sy, sz):
345 var prefab = preload("res://objects/nodes/block.tscn")
346 var newwall = prefab.instantiate()
347 newwall.position.x = x
348 newwall.position.y = y
349 newwall.position.z = z
350 newwall.scale.x = sz
351 newwall.scale.y = sy
352 newwall.scale.z = sx
353 newwall.set_surface_override_material(0, preload("res://assets/materials/blackMatte.material"))
354 newwall.visibility_range_end = 3
355 newwall.visibility_range_end_margin = 1
356 newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF
357 newwall.skeleton = ".."
358 get_parent().add_child.call_deferred(newwall)
359
360
361func _process(_dt):
362 compass.update_rotation(global_rotation.y)
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 b7bfacf..0000000 --- a/client/Archipelago/settings_screen.gd +++ /dev/null
@@ -1,252 +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/panel.gd"))
44 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pauseMenu.gd"))
45 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd"))
46 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/saver.gd"))
47 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleport.gd"))
48 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleportListener.gd"))
49 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/visibilityListener.gd"))
50 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldport.gd"))
51 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldportListener.gd"))
52
53 var proto_script = load("user://maps/Archipelago/generated/proto.gd")
54 var gamedata_script = load("user://maps/Archipelago/gamedata.gd")
55 var gamedata_instance = gamedata_script.new(proto_script)
56 gamedata_instance.load(
57 FileAccess.get_file_as_bytes("user://maps/Archipelago/generated/data.binpb")
58 )
59 gamedata_instance.name = "Gamedata"
60 global.add_child(gamedata_instance)
61
62 var messages_script = load("user://maps/Archipelago/messages.gd")
63 var messages_instance = messages_script.new()
64 messages_instance.name = "Messages"
65 global.add_child(messages_instance)
66
67 var textclient_script = load("user://maps/Archipelago/textclient.gd")
68 var textclient_instance = textclient_script.new()
69 textclient_instance.name = "Textclient"
70 global.add_child(textclient_instance)
71
72 var compass_overlay_script = load("user://maps/Archipelago/compass_overlay.gd")
73 var compass_overlay_instance = compass_overlay_script.new()
74 compass_overlay_instance.name = "Compass"
75 compass_overlay_instance.SCRIPT_compass = load("user://maps/Archipelago/compass.gd")
76 global.add_child(compass_overlay_instance)
77
78 var ap = global.get_node("Archipelago")
79 var gamedata = global.get_node("Gamedata")
80 ap.connect("ap_connected", connectionSuccessful)
81 ap.connect("could_not_connect", connectionUnsuccessful)
82 ap.connect("connect_status", connectionStatus)
83
84 # Populate textboxes with AP settings.
85 $Panel/server_box.text = ap.ap_server
86 $Panel/player_box.text = ap.ap_user
87 $Panel/password_box.text = ap.ap_pass
88
89 var history_box = $Panel/connection_history
90 if ap.connection_history.is_empty():
91 history_box.disabled = true
92 else:
93 history_box.disabled = false
94
95 var i = 0
96 for details in ap.connection_history:
97 history_box.get_popup().add_item("%s (%s)" % [details[1], details[0]], i)
98 i += 1
99
100 history_box.get_popup().connect("id_pressed", historySelected)
101
102 # Show client version.
103 $Panel/title.text = "ARCHIPELAGO (%d.%d)" % [gamedata.objects.get_version(), ap.MOD_VERSION]
104
105 # Increase font size in text boxes.
106 $Panel/server_box.add_theme_font_size_override("font_size", 36)
107 $Panel/player_box.add_theme_font_size_override("font_size", 36)
108 $Panel/password_box.add_theme_font_size_override("font_size", 36)
109
110 # Set up version mismatch dialog.
111 $Panel/VersionMismatch.connect("confirmed", startGame)
112 $Panel/VersionMismatch.get_cancel_button().pressed.connect(versionMismatchDeclined)
113
114
115# Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd
116func installScriptExtension(childScript: Resource):
117 # Force Godot to compile the script now.
118 # We need to do this here to ensure that the inheritance chain is
119 # properly set up, and multiple mods can chain-extend the same
120 # class multiple times.
121 # This is also needed to make Godot instantiate the extended class
122 # when creating singletons.
123 # The actual instance is thrown away.
124 childScript.new()
125
126 var parentScript = childScript.get_base_script()
127 var parentScriptPath = parentScript.resource_path
128 global._print("ModLoader: Installing script extension over %s" % parentScriptPath)
129 childScript.take_over_path(parentScriptPath)
130
131
132func connectionStatus(message):
133 var popup = self.get_node("Panel/AcceptDialog")
134 popup.title = "Connecting to Archipelago"
135 popup.dialog_text = message
136 popup.exclusive = true
137 popup.get_ok_button().visible = false
138 popup.popup_centered()
139
140
141func connectionSuccessful():
142 var ap = global.get_node("Archipelago")
143 var gamedata = global.get_node("Gamedata")
144
145 # Check for major version mismatch.
146 if ap.apworld_version[0] != gamedata.objects.get_version():
147 $Panel/AcceptDialog.exclusive = false
148
149 var popup = self.get_node("Panel/VersionMismatch")
150 popup.title = "Version Mismatch!"
151 popup.dialog_text = (
152 "This slot was generated using v%d.%d of the Lingo 2 apworld,\nwhich has a different major version than this client (v%d.%d).\nIt is highly recommended to play using the correct version of the client.\nYou may experience bugs or logic issues if you continue."
153 % [
154 ap.apworld_version[0],
155 ap.apworld_version[1],
156 gamedata.objects.get_version(),
157 ap.MOD_VERSION
158 ]
159 )
160 popup.exclusive = true
161 popup.popup_centered()
162
163 return
164
165 startGame()
166
167
168func startGame():
169 var ap = global.get_node("Archipelago")
170
171 # Save connection details
172 var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass]
173 if ap.connection_history.has(connection_details):
174 ap.connection_history.erase(connection_details)
175 ap.connection_history.push_front(connection_details)
176 if ap.connection_history.size() > 10:
177 ap.connection_history.resize(10)
178 ap.saveSettings()
179
180 # Switch to the_entry
181 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
182 global.user = ap.getSaveFileName()
183 global.universe = "lingo"
184 global.map = "the_entry"
185
186 unlocks.resetCollectables()
187 unlocks.resetData()
188
189 ap.setup_keys()
190
191 unlocks.loadCollectables()
192 unlocks.loadData()
193 unlocks.unlockKey("capslock", 1)
194
195 clearResourceCache("res://objects/meshes/gridDoor.tscn")
196 clearResourceCache("res://objects/nodes/collectable.tscn")
197 clearResourceCache("res://objects/nodes/door.tscn")
198 clearResourceCache("res://objects/nodes/keyHolder.tscn")
199 clearResourceCache("res://objects/nodes/listeners/animationListener.tscn")
200 clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn")
201 clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn")
202 clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn")
203 clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn")
204 clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn")
205 clearResourceCache("res://objects/nodes/panel.tscn")
206 clearResourceCache("res://objects/nodes/player.tscn")
207 clearResourceCache("res://objects/nodes/saver.tscn")
208 clearResourceCache("res://objects/nodes/teleport.tscn")
209 clearResourceCache("res://objects/nodes/worldport.tscn")
210 clearResourceCache("res://objects/scenes/menus/pause_menu.tscn")
211
212 var paintings_dir = DirAccess.open("res://objects/meshes/paintings")
213 if paintings_dir:
214 paintings_dir.list_dir_begin()
215 var file_name = paintings_dir.get_next()
216 while file_name != "":
217 if not paintings_dir.current_is_dir() and file_name.ends_with(".tscn"):
218 clearResourceCache("res://objects/meshes/paintings/" + file_name)
219 file_name = paintings_dir.get_next()
220
221 switcher.switch_map.call_deferred("res://objects/scenes/the_entry.tscn")
222
223
224func connectionUnsuccessful(error_message):
225 $Panel/connect_button.disabled = false
226
227 var popup = $Panel/AcceptDialog
228 popup.title = "Could not connect to Archipelago"
229 popup.dialog_text = error_message
230 popup.exclusive = true
231 popup.get_ok_button().visible = true
232 popup.popup_centered()
233
234 $Panel/connect_button.disabled = false
235
236
237func versionMismatchDeclined():
238 $Panel/AcceptDialog.hide()
239 $Panel/connect_button.disabled = false
240
241
242func historySelected(index):
243 var ap = global.get_node("Archipelago")
244 var details = ap.connection_history[index]
245
246 $Panel/server_box.text = details[0]
247 $Panel/player_box.text = details[1]
248 $Panel/password_box.text = details[2]
249
250
251func clearResourceCache(path):
252 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/worldport.gd b/client/Archipelago/worldport.gd deleted file mode 100644 index d0fb6c9..0000000 --- a/client/Archipelago/worldport.gd +++ /dev/null
@@ -1,10 +0,0 @@
1extends "res://scripts/nodes/worldport.gd"
2
3
4func _ready():
5 if global.map == "icarus" and exit == "daedalus":
6 var ap = global.get_node("Archipelago")
7 if not ap.daedalus_roof_access:
8 entry_point = Vector3(58, 10, 0)
9
10 super._ready()
diff --git a/client/CHANGELOG.md b/client/CHANGELOG.md deleted file mode 100644 index a0a538b..0000000 --- a/client/CHANGELOG.md +++ /dev/null
@@ -1,59 +0,0 @@
1# lingo2-archipelago Client Releases
2
3## v5.6 - 2025-09-17
4
5- Letter locations will no longer reappear after being collected.
6- This also prevents a potential scenario in which it is impossible to access
7 the location "The Congruent - Obverse Yellow Puzzles" when door shuffle is
8 disabled.
9
10Download:
11[lingo2-archipelago-client-v5.6.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.6.zip)<br/>
12Source:
13[v5.6](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v5.6)
14
15## v5.5 - 2025-09-16
16
17- Compatability update for v5.5 of the apworld.
18
19Download:
20[lingo2-archipelago-client-v5.5.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v5.5.zip)<br/>
21Source:
22[v5.5](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v5.5)
23
24## v4.4 - 2025-09-13
25
26- Added support for anti-collectable trap items.
27- Fixed entrance to The Jubilant not opening properly when using control center
28 color shuffle.
29- Fixed the location "The Entry (Colored Doors Area) - OPEN" not sending.
30- Fixed level 2 letters not activating properly when letter shuffle is set to
31 Item Cyan.
32- Messages are now cleared out when returning to the main menu.
33- The player is prevented from accidentally breaking roof access logic when
34 returning to Daedalus from Icarus.
35
36Download:
37[lingo2-archipelago-client-v4.4.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v4.4.zip)<br/>
38Source:
39[v4.4](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v4.4)
40
41## v3.3 - 2025-09-12
42
43- Fixed issue downloading large datapackages (such as TUNIC's).
44- Connection failures now show error messages.
45
46Download:
47[lingo2-archipelago-client-v3.3.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.3.zip)<br/>
48Source:
49[v3.3](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v3.3)
50
51## v3.2 - 2025-09-12
52
53- Initial release for testing. Features include door shuffle, letter shuffle,
54 and symbol shuffle.
55
56Download:
57[lingo2-archipelago-client-v3.2.zip](https://files.fourisland.com/releases/lingo2-archipelago/client/lingo2-archipelago-client-v3.2.zip)<br/>
58Source:
59[v3.2](https://code.fourisland.com/lingo2-archipelago/tag/?h=client-v3.2)
diff --git a/client/README.md b/client/README.md deleted file mode 100644 index 99589c5..0000000 --- a/client/README.md +++ /dev/null
@@ -1,90 +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.
diff --git a/client/archipelago.tscn b/client/archipelago.tscn index da83b23..1c156a3 100644 --- a/client/archipelago.tscn +++ b/client/archipelago.tscn
@@ -1,167 +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"
81horizontal_alignment = 1
82vertical_alignment = 1
83
84[node name="Label2" type="Label" parent="Panel"]
85layout_mode = 1
86anchors_preset = -1
87anchor_right = 1.0
88offset_left = 80.0
89offset_top = 300.0
90offset_right = -80.0
91offset_bottom = 388.0
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\"."
43horizontal_alignment = 1 95horizontal_alignment = 1
44theme = ExtResource("2_g4bvn") 96autowrap_mode = 3
45 97
46[node name="credit" parent="Panel" type="Label"] 98[node name="HBoxContainer" type="HBoxContainer" parent="Panel"]
47visible = false 99layout_mode = 1
48offset_left = 1278.0 100anchors_preset = -1
49offset_top = 974.0 101anchor_right = 1.0
50offset_right = 1868.0 102offset_left = 80.0
51offset_bottom = 1034.0 103offset_top = 595.0
52text = "Brenton Wildes" 104offset_right = -80.0
53theme = ExtResource("2_g4bvn") 105offset_bottom = 755.0
54 106theme_override_constants/separation = 32
55[node name="connect_button" parent="Panel" type="Button"] 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
56offset_left = 255.0 120offset_left = 255.0
57offset_top = 875.0 121offset_top = 875.0
58offset_right = 891.0 122offset_right = 891.0
59offset_bottom = 1025.0 123offset_bottom = 1025.0
60custom_colors/font_color_hover = Color( 1, 0.501961, 0, 1 ) 124theme = ExtResource("1_mw3f1")
61text = "CONNECT" 125text = "start"
62theme = ExtResource("2_g4bvn")
63script = ExtResource( 4 )
64 126
65[node name="quit_button" parent="Panel" type="Button"] 127[node name="QuitButton" type="Button" parent="Panel"]
128layout_mode = 1
129anchors_preset = -1
66offset_left = 1102.0 130offset_left = 1102.0
67offset_top = 875.0 131offset_top = 875.0
68offset_right = 1738.0 132offset_right = 1738.0
69offset_bottom = 1025.0 133offset_bottom = 1025.0
70custom_colors/font_color_hover = Color( 1, 0, 0, 1 ) 134theme = ExtResource("1_mw3f1")
71text = "BACK" 135text = "back"
72theme = ExtResource("2_g4bvn") 136
73script = ExtResource( 4 ) 137[node name="FileDialog" type="FileDialog" parent="."]
74 138title = "Open a File"
75[node name="credit2" parent="Panel" type="Label"] 139size = Vector2i(512, 512)
76offset_left = -105.0 140ok_button_text = "Open"
77offset_top = 346.0 141file_mode = 0
78offset_right = 485.0 142access = 2
79offset_bottom = 410.0 143filters = PackedStringArray("*.apworld;Archipelago Worlds")
80custom_styles/normal = SubResource( 1 ) 144show_hidden_files = true
81text = "SERVER" 145use_native_dialog = true
82align = 2 146
83theme = ExtResource("2_g4bvn") 147[node name="AcceptDialog" type="AcceptDialog" parent="."]
84 148dialog_text = "Could not open Lingo 2 apworld. Please check that the path is correct."
85[node name="credit5" parent="Panel" type="Label"] 149
86offset_left = 1239.0 150[connection signal="pressed" from="Panel/HBoxContainer/Button" to="." method="_browse_pressed"]
87offset_top = 422.0 151[connection signal="pressed" from="Panel/StartButton" to="." method="_start_pressed"]
88offset_right = 1829.0 152[connection signal="pressed" from="Panel/QuitButton" to="." method="_quit_pressed"]
89offset_bottom = 486.0 153[connection signal="file_selected" from="FileDialog" to="." method="_file_selected"]
90custom_styles/normal = SubResource( 1 )
91text = "OPTIONS"
92theme = ExtResource("2_g4bvn")
93
94[node name="credit3" parent="Panel" type="Label"]
95offset_left = -105.0
96offset_top = 519.0
97offset_right = 485.0
98offset_bottom = 583.0
99custom_styles/normal = SubResource( 1 )
100text = "PLAYER"
101align = 2
102theme = ExtResource("2_g4bvn")
103
104[node name="credit4" parent="Panel" type="Label"]
105offset_left = -105.0
106offset_top = 704.0
107offset_right = 485.0
108offset_bottom = 768.0
109custom_styles/normal = SubResource( 1 )
110text = "PASSWORD"
111align = 2
112theme = ExtResource("2_g4bvn")
113
114[node name="server_box" type="LineEdit" parent="Panel"]
115offset_left = 502.0
116offset_top = 295.0
117offset_right = 1144.0
118offset_bottom = 445.0
119custom_colors/selection_color = Color( 0.482353, 0, 0, 1 )
120custom_colors/cursor_color = Color( 0, 0, 0, 1 )
121custom_colors/font_color = Color( 0, 0, 0, 1 )
122custom_styles/focus = SubResource( 2 )
123align = 1
124caret_blink = true
125
126[node name="player_box" type="LineEdit" parent="Panel"]
127offset_left = 502.0
128offset_top = 477.0
129offset_right = 1144.0
130offset_bottom = 627.0
131custom_colors/selection_color = Color( 0.482353, 0, 0, 1 )
132custom_colors/cursor_color = Color( 0, 0, 0, 1 )
133custom_colors/font_color = Color( 0, 0, 0, 1 )
134custom_styles/focus = SubResource( 2 )
135align = 1
136caret_blink = true
137
138[node name="password_box" type="LineEdit" parent="Panel"]
139offset_left = 502.0
140offset_top = 659.0
141offset_right = 1144.0
142offset_bottom = 809.0
143custom_colors/selection_color = Color( 0.482353, 0, 0, 1 )
144custom_colors/cursor_color = Color( 0, 0, 0, 1 )
145custom_colors/font_color = Color( 0, 0, 0, 1 )
146custom_styles/focus = SubResource( 2 )
147align = 1
148caret_blink = true
149
150[node name="AcceptDialog" type="AcceptDialog" parent="Panel"]
151offset_right = 83.0
152offset_bottom = 58.0
153
154[node name="VersionMismatch" type="ConfirmationDialog" parent="Panel"]
155offset_right = 83.0
156offset_bottom = 58.0
157
158[node name="connection_history" type="MenuButton" parent="Panel"]
159offset_left = 1239.0
160offset_top = 276.0
161offset_right = 1829.0
162offset_bottom = 372.0
163text = "connection history"
164flat = false
165
166[connection signal="pressed" from="Panel/connect_button" to="Panel/connect_button" method="_connect_pressed"]
167[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 17f71a3..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 {
@@ -731,7 +729,7 @@ connections {
731 from { 729 from {
732 port { 730 port {
733 map: "the_orb" 731 map: "the_orb"
734 room: "B Room" 732 room: "Middle Room"
735 name: "MID" 733 name: "MID"
736 } 734 }
737 } 735 }
@@ -963,7 +961,7 @@ connections {
963 from { 961 from {
964 port { 962 port {
965 map: "the_entry" 963 map: "the_entry"
966 room: "Lime Room" 964 room: "Revitalized Entrance"
967 name: "REVITALIZED" 965 name: "REVITALIZED"
968 } 966 }
969 } 967 }
@@ -1564,6 +1562,23 @@ connections {
1564 painting { 1562 painting {
1565 map: "the_sturdy" 1563 map: "the_sturdy"
1566 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"
1567 name: "RAINBOW" 1582 name: "RAINBOW"
1568 } 1583 }
1569 } 1584 }
@@ -2461,3 +2476,265 @@ connections {
2461 } 2476 }
2462 oneway: true 2477 oneway: true
2463} 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..128cbb8 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,9 +110,17 @@ 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 {
120 map: "the_gallery"
121 name: "Cyan Doors"
122 }
123 doors {
113 map: "the_graveyard" 124 map: "the_graveyard"
114 name: "Double Letters" 125 name: "Double Letters"
115 } 126 }
@@ -122,6 +133,10 @@ door_groups {
122 name: "Cyan Doors" 133 name: "Cyan Doors"
123 } 134 }
124 doors { 135 doors {
136 map: "the_owl"
137 name: "Double Letters"
138 }
139 doors {
125 map: "the_parthenon" 140 map: "the_parthenon"
126 name: "Double Letters" 141 name: "Double Letters"
127 } 142 }
@@ -141,6 +156,7 @@ door_groups {
141door_groups { 156door_groups {
142 name: "Lavender Cubes" 157 name: "Lavender Cubes"
143 type: SHUFFLE_GROUP 158 type: SHUFFLE_GROUP
159 daedalus_only_always_item: true
144 doors { 160 doors {
145 map: "daedalus" 161 map: "daedalus"
146 name: "C Keyholder Blocker" 162 name: "C Keyholder Blocker"
@@ -158,3 +174,17 @@ door_groups {
158 name: "Lavender Cubes" 174 name: "Lavender Cubes"
159 } 175 }
160} 176}
177door_groups {
178 name: "Control Center - Perceptive Entrance"
179 type: SHUFFLE_GROUP
180 # This is a weird situation where there's two different ways to open the door
181 # and we want both to work in vanilla.
182 doors {
183 map: "control_center"
184 name: "Perceptive From Outside"
185 }
186 doors {
187 map: "control_center"
188 name: "Perceptive From Inside"
189 }
190}
diff --git a/data/ids.yaml b/data/ids.yaml index 80ace1a..c55cfa6 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
@@ -61,6 +86,7 @@ maps:
61 Unkempt Door: 2712 86 Unkempt Door: 2712
62 Unyielding Door: 2720 87 Unyielding Door: 2720
63 X1 Door: 2711 88 X1 Door: 2711
89 rte: 3385
64 daedalus: 90 daedalus:
65 rooms: 91 rooms:
66 After Bee Room: 92 After Bee Room:
@@ -245,6 +271,8 @@ maps:
245 TICKETBORNE: 1737 271 TICKETBORNE: 1737
246 TWOGOTHIM: 1735 272 TWOGOTHIM: 1735
247 UNDERPANTS: 1732 273 UNDERPANTS: 1732
274 ports:
275 ENTRY: 3116
248 Computer Room: 276 Computer Room:
249 panels: 277 panels:
250 KEYBOARD (1): 1746 278 KEYBOARD (1): 1746
@@ -295,6 +323,8 @@ maps:
295 Entry Shortcut: 323 Entry Shortcut:
296 panels: 324 panels:
297 WELCOME: 1776 325 WELCOME: 1776
326 ports:
327 ENTRY: 3117
298 Eye Painting: 328 Eye Painting:
299 panels: 329 panels:
300 REVILED: 1777 330 REVILED: 1777
@@ -484,6 +514,9 @@ maps:
484 Maze Paintings Area: 514 Maze Paintings Area:
485 panels: 515 panels:
486 Paintings: 1929 516 Paintings: 1929
517 Moat:
518 ports:
519 HIVE: 3118
487 North Castle Area: 520 North Castle Area:
488 panels: 521 panels:
489 A: 1930 522 A: 1930
@@ -548,6 +581,8 @@ maps:
548 PETAL: 1976 581 PETAL: 1976
549 PLUM (1): 1971 582 PLUM (1): 1971
550 PLUM (2): 1972 583 PLUM (2): 1972
584 ports:
585 REVITALIZED: 3119
551 Outside Hotel: 586 Outside Hotel:
552 panels: 587 panels:
553 COLORFUL: 1977 588 COLORFUL: 1977
@@ -627,6 +662,9 @@ maps:
627 PHARAOH: 2021 662 PHARAOH: 2021
628 SHEET: 2020 663 SHEET: 2020
629 STRAW: 2024 664 STRAW: 2024
665 Purple Hallway From Great:
666 ports:
667 GREAT: 3120
630 Purple NW Vestibule: 668 Purple NW Vestibule:
631 panels: 669 panels:
632 LOSE: 2029 670 LOSE: 2029
@@ -693,9 +731,13 @@ maps:
693 Quiet Entrance: 731 Quiet Entrance:
694 panels: 732 panels:
695 HIDDEN: 2064 733 HIDDEN: 2064
734 ports:
735 QUIET: 3121
696 Rain Side: 736 Rain Side:
697 panels: 737 panels:
698 "?": 2065 738 "?": 2065
739 ports:
740 BEARER: 3122
699 Rainbow Blue: 741 Rainbow Blue:
700 panels: 742 panels:
701 THEME: 2066 743 THEME: 2066
@@ -798,6 +840,8 @@ maps:
798 Starting Room: 840 Starting Room:
799 panels: 841 panels:
800 ENTRANCE: 2127 842 ENTRANCE: 2127
843 ports:
844 GREAT: 3123
801 Sweet Foyer: 845 Sweet Foyer:
802 panels: 846 panels:
803 EQUAL: 2129 847 EQUAL: 2129
@@ -806,6 +850,9 @@ maps:
806 RENT (2): 2132 850 RENT (2): 2132
807 RENT (3): 2133 851 RENT (3): 2133
808 RENT (4): 2131 852 RENT (4): 2131
853 ports:
854 SWEET1: 3124
855 SWEET2: 3125
809 Tree Entrance: 856 Tree Entrance:
810 panels: 857 panels:
811 DIFFERENCE: 2135 858 DIFFERENCE: 2135
@@ -814,6 +861,8 @@ maps:
814 RAT: 2134 861 RAT: 2134
815 SUNDER: 2139 862 SUNDER: 2139
816 WHERE: 2138 863 WHERE: 2138
864 ports:
865 TREE: 3126
817 U2 Room: 866 U2 Room:
818 panels: 867 panels:
819 CHAOS: 2147 868 CHAOS: 2147
@@ -832,6 +881,9 @@ maps:
832 TROUBLE: 2148 881 TROUBLE: 2148
833 WICKED: 2142 882 WICKED: 2142
834 WONDERLAND: 2156 883 WONDERLAND: 2156
884 Unkempt Entrance:
885 ports:
886 UNKEMPT: 3127
835 Welcome Back Area: 887 Welcome Back Area:
836 panels: 888 panels:
837 FAREWELL LITTLE LAMB: 2157 889 FAREWELL LITTLE LAMB: 2157
@@ -881,6 +933,9 @@ maps:
881 CUT: 2194 933 CUT: 2194
882 MISSING: 2192 934 MISSING: 2192
883 STONES: 2195 935 STONES: 2195
936 White Hallway From Entry:
937 ports:
938 ENTRY: 3128
884 Wisdom Panel: 939 Wisdom Panel:
885 panels: 940 panels:
886 INTELLIGENCE: 2198 941 INTELLIGENCE: 2198
@@ -891,6 +946,8 @@ maps:
891 ARTS: 2202 946 ARTS: 2202
892 SONG: 2203 947 SONG: 2203
893 UNDER: 2200 948 UNDER: 2200
949 ports:
950 WONDROUS: 3129
894 Yellow Color Backside: 951 Yellow Color Backside:
895 panels: 952 panels:
896 BRASS: 2206 953 BRASS: 2206
@@ -902,6 +959,8 @@ maps:
902 Paintings: 2210 959 Paintings: 2210
903 SPIN: 2209 960 SPIN: 2209
904 SUN: 2208 961 SUN: 2208
962 ports:
963 FOURROOMS: 3130
905 Yellow Room: 964 Yellow Room:
906 panels: 965 panels:
907 COLOR: 2217 966 COLOR: 2217
@@ -971,6 +1030,7 @@ maps:
971 doors: 1030 doors:
972 Amber East Doors: 1511 1031 Amber East Doors: 1511
973 Amber North Door: 1510 1032 Amber North Door: 1510
1033 Amber Room Panels: 3289
974 Amber South Door: 1509 1034 Amber South Door: 1509
975 Bee Room Back Door: 1523 1035 Bee Room Back Door: 1523
976 Bee Room Entrance: 1521 1036 Bee Room Entrance: 1521
@@ -1013,7 +1073,9 @@ maps:
1013 Dark Light Room Entrance: 1569 1073 Dark Light Room Entrance: 1569
1014 Dark Light Room Exit: 1570 1074 Dark Light Room Exit: 1570
1015 Dark Light Room Exit Panel: 1571 1075 Dark Light Room Exit Panel: 1571
1076 Direction Panels: 3297
1016 Entry Shortcut Secret Exit: 1437 1077 Entry Shortcut Secret Exit: 1437
1078 Equality Panels: 3292
1017 Eye Painting: 2751 1079 Eye Painting: 2751
1018 Eye Painting Exit: 1446 1080 Eye Painting Exit: 1446
1019 F Keyholder Door: 1551 1081 F Keyholder Door: 1551
@@ -1021,6 +1083,7 @@ maps:
1021 F2 Room Southeast Door: 1487 1083 F2 Room Southeast Door: 1487
1022 F2 Room Southwest Door: 1490 1084 F2 Room Southwest Door: 1490
1023 F2 Room West Door: 1492 1085 F2 Room West Door: 1492
1086 Farewell Little Lamb Panels: 3302
1024 Flip Painting Blocker: 1552 1087 Flip Painting Blocker: 1552
1025 Globe Room East Door: 1589 1088 Globe Room East Door: 1589
1026 Globe Room South Door: 1591 1089 Globe Room South Door: 1591
@@ -1051,13 +1114,16 @@ maps:
1051 Maze North Door: 1502 1114 Maze North Door: 1502
1052 Maze South Door: 1503 1115 Maze South Door: 1503
1053 Near Flip Painting Door: 1474 1116 Near Flip Painting Door: 1474
1117 Near H Keyholder Panel: 3299
1054 Near Pyramid Gate: 1447 1118 Near Pyramid Gate: 1447
1055 Near Sweet Blue Door: 1573 1119 Near Sweet Blue Door: 1573
1056 Near Sweet Brown Door: 1561 1120 Near Sweet Brown Door: 1561
1057 Near Yellow Room Door: 1565 1121 Near Yellow Room Door: 1565
1058 North Castle Panel: 2742 1122 North Castle Panel: 2742
1123 Nursery Panels: 3298
1059 O2 Room Northeast Door: 1485 1124 O2 Room Northeast Door: 1485
1060 O2 Room Southeast Door: 1478 1125 O2 Room Southeast Door: 1478
1126 Orange Panels: 3293
1061 Orange Rainbow Panel: 2267 1127 Orange Rainbow Panel: 2267
1062 Orange Rainbow Room: 1535 1128 Orange Rainbow Room: 1535
1063 Orange Room: 1507 1129 Orange Room: 1507
@@ -1072,6 +1138,7 @@ maps:
1072 Pink Hallway: 1555 1138 Pink Hallway: 1555
1073 Planet Room Divider: 1513 1139 Planet Room Divider: 1513
1074 Planet Room Secret Door: 1578 1140 Planet Room Secret Door: 1578
1141 Plum Panels: 3300
1075 Plum Room Entrance: 1576 1142 Plum Room Entrance: 1576
1076 Plum Room Exit: 1577 1143 Plum Room Exit: 1577
1077 Pumpkin Door: 1583 1144 Pumpkin Door: 1583
@@ -1096,6 +1163,7 @@ maps:
1096 Purple West Area West Door: 1466 1163 Purple West Area West Door: 1466
1097 Pyramid Entrance: 1505 1164 Pyramid Entrance: 1505
1098 Rain Side Panel: 1546 1165 Rain Side Panel: 1546
1166 Rainbow Color Backside Panels: 3286
1099 Rainbow Rooms Entrance: 1533 1167 Rainbow Rooms Entrance: 1533
1100 Red Rainbow Panel: 2266 1168 Red Rainbow Panel: 2266
1101 Red Rainbow Room: 1534 1169 Red Rainbow Room: 1534
@@ -1103,6 +1171,7 @@ maps:
1103 Red Room Entrance: 1562 1171 Red Room Entrance: 1562
1104 Red Smiley: 1554 1172 Red Smiley: 1554
1105 Red Smiley Entrance: 1553 1173 Red Smiley Entrance: 1553
1174 Rent Panels: 3291
1106 Roof Access: 1528 1175 Roof Access: 1528
1107 Salt Room Entrance: 1532 1176 Salt Room Entrance: 1532
1108 Seasoning Doors: 1544 1177 Seasoning Doors: 1544
@@ -1111,6 +1180,7 @@ maps:
1111 South Castle Area Entrance: 1575 1180 South Castle Area Entrance: 1575
1112 South Castle Panel: 2744 1181 South Castle Panel: 2744
1113 Southwest Area Intersection: 1475 1182 Southwest Area Intersection: 1475
1183 Splintering Area Panels: 3287
1114 Splintering Exit North Door: 1449 1184 Splintering Exit North Door: 1449
1115 Splintering Exit South Door: 1450 1185 Splintering Exit South Door: 1450
1116 Starting Room East Wall Center Door: 1439 1186 Starting Room East Wall Center Door: 1439
@@ -1124,8 +1194,10 @@ maps:
1124 Starting Room West Wall North Door: 1438 1194 Starting Room West Wall North Door: 1438
1125 Starting Room West Wall South Door: 1433 1195 Starting Room West Wall South Door: 1433
1126 Sticks And Stones Door: 1593 1196 Sticks And Stones Door: 1593
1197 Teal Panel: 3296
1127 Temple of the Eyes Entrance: 1444 1198 Temple of the Eyes Entrance: 1444
1128 Theo Panels: 2811 1199 Theo Panels: 2811
1200 Tree Panels: 3295
1129 U2 Room East Door: 1498 1201 U2 Room East Door: 1498
1130 U2 Room Southeast Door: 1493 1202 U2 Room Southeast Door: 1493
1131 U2 Room Southwest Door: 1496 1203 U2 Room Southwest Door: 1496
@@ -1133,13 +1205,17 @@ maps:
1133 Welcome Back Door: 1435 1205 Welcome Back Door: 1435
1134 Welcome Back Secret Door: 1434 1206 Welcome Back Secret Door: 1434
1135 West Castle Panel: 2743 1207 West Castle Panel: 2743
1208 West Spire Panel: 3294
1209 West Sticks And Stones Panel: 3288
1136 White Hallway From Entry: 1488 1210 White Hallway From Entry: 1488
1137 Wonderland North Door: 1520 1211 Wonderland North Door: 1520
1138 Wonderland South Door: 1504 1212 Wonderland South Door: 1504
1139 Yellow Rainbow Panel: 2268 1213 Yellow Rainbow Panel: 2268
1140 Yellow Rainbow Room: 1536 1214 Yellow Rainbow Room: 1536
1215 Yellow Roof Puzzles: 3290
1141 Yellow Room: 1568 1216 Yellow Room: 1568
1142 Yellow Room Entrance: 1567 1217 Yellow Room Entrance: 1567
1218 Yellow Smiley Annex Panels: 3301
1143 Yellow Smiley Door: 1548 1219 Yellow Smiley Door: 1548
1144 Z2 Room Back Exit: 1451 1220 Z2 Room Back Exit: 1451
1145 Z2 Room Northeast Door: 1454 1221 Z2 Room Northeast Door: 1454
@@ -1153,6 +1229,85 @@ maps:
1153 Zoo Prize Door: 1599 1229 Zoo Prize Door: 1599
1154 Zoo South Entrance: 1596 1230 Zoo South Entrance: 1596
1155 Zoo West Entrance: 1594 1231 Zoo West Entrance: 1594
1232 rte: 3376
1233 demo:
1234 rooms:
1235 Backside Area:
1236 panels:
1237 BACKSIDE: 3049
1238 DOORWAYS: 3050
1239 ENDS (2): 3052
1240 SEE: 3051
1241 Castle:
1242 panels:
1243 G: 3054
1244 SERIES: 3053
1245 Center Building:
1246 panels:
1247 FUZZIES: 3056
1248 WORLD: 3055
1249 Flower Hallway:
1250 panels:
1251 LACES: 3057
1252 Main Area:
1253 panels:
1254 A: 3089
1255 AGES: 3063
1256 ANY: 3071
1257 ART: 3059
1258 Blank: 3095
1259 C: 3088
1260 CASTS: 3086
1261 CLOCKWISE: 3067
1262 COLORFUL: 3061
1263 COUNTER: 3070
1264 DAZES: 3084
1265 DEN: 3064
1266 DISCOVER: 3096
1267 E (1): 3091
1268 E (2): 3093
1269 END: 3079
1270 FAMILY: 3097
1271 GAZES: 3085
1272 HAZES: 3083
1273 HI: 3058
1274 HID: 3065
1275 MESS: 3066
1276 MIND: 3078
1277 N: 3092
1278 PACES: 3069
1279 POSSIBLE: 3068
1280 R: 3094
1281 RAD: 3080
1282 RODS: 3072
1283 S: 3087
1284 SECRETIVE: 3075
1285 STALK: 3082
1286 TALK: 3074
1287 TEES: 3060
1288 TOADS: 3076
1289 TON: 3077
1290 TOO: 3081
1291 TWO: 3073
1292 V: 3090
1293 WORD: 3062
1294 Mastery:
1295 masteries:
1296 MASTERY: 3098
1297 Tower:
1298 panels:
1299 ENDS (1): 3099
1300 doors:
1301 Castle: 3046
1302 Center Building: 3039
1303 Center Building Panels: 3041
1304 Flower Hallway: 3040
1305 Gold Door: 3048
1306 Orange Door: 3042
1307 Purple Door: 3043
1308 Red Door: 3045
1309 Scavenger Hunt: 3047
1310 Yellow Door: 3044
1156 four_rooms: 1311 four_rooms:
1157 rooms: 1312 rooms:
1158 Examples Room: 1313 Examples Room:
@@ -1165,6 +1320,8 @@ maps:
1165 SONNET: 12 1320 SONNET: 12
1166 SUPERLATIVE: 11 1321 SUPERLATIVE: 11
1167 URN: 13 1322 URN: 13
1323 ports:
1324 DAEDALUS: 3131
1168 Hallway: 1325 Hallway:
1169 panels: 1326 panels:
1170 HUNCHBACK: 16 1327 HUNCHBACK: 16
@@ -1179,6 +1336,8 @@ maps:
1179 SWAY: 24 1336 SWAY: 24
1180 TERROR: 20 1337 TERROR: 20
1181 TURN: 22 1338 TURN: 22
1339 ports:
1340 IMPRESSIVE: 3132
1182 Keyholder Room: 1341 Keyholder Room:
1183 keyholders: 1342 keyholders:
1184 A: 2773 1343 A: 2773
@@ -1192,6 +1351,8 @@ maps:
1192 SERIOUS: 31 1351 SERIOUS: 31
1193 SURPASS: 29 1352 SURPASS: 29
1194 VERSE: 30 1353 VERSE: 30
1354 ports:
1355 ENTRY: 3133
1195 Time Room: 1356 Time Room:
1196 panels: 1357 panels:
1197 BROODING: 33 1358 BROODING: 33
@@ -1202,6 +1363,8 @@ maps:
1202 RHYTHM: 40 1363 RHYTHM: 40
1203 SUSPENSE: 36 1364 SUSPENSE: 36
1204 WRITING: 38 1365 WRITING: 38
1366 ports:
1367 OWL: 3134
1205 doors: 1368 doors:
1206 A2 Door: 4 1369 A2 Door: 4
1207 Examples Door: 1 1370 Examples Door: 1
@@ -1209,6 +1372,182 @@ maps:
1209 Keyholder Door: 5 1372 Keyholder Door: 5
1210 Synonyms Door: 2 1373 Synonyms Door: 2
1211 Time Door: 3 1374 Time Door: 3
1375 rte: 3415
1376 icarus:
1377 rooms:
1378 Above Trans Rights:
1379 panels:
1380 ANT: 2877
1381 Big U:
1382 panels:
1383 COLONY: 2879
1384 DECK: 2878
1385 MANOR: 2880
1386 Fatherland:
1387 panels:
1388 FATHERLAND: 2881
1389 Highest Point:
1390 panels:
1391 DIAGNOSIS: 2882
1392 QUEEN: 2883
1393 Mastery:
1394 masteries:
1395 MASTERY: 2994
1396 Maze:
1397 panels:
1398 ANALYSIS: 2887
1399 BOOKS: 2890
1400 KING (1): 2886
1401 MANSLAUGHTER: 2888
1402 MEDIUMS: 2889
1403 Maze Back:
1404 panels:
1405 THESE: 2884
1406 Maze King Panel:
1407 panels:
1408 KING (2): 2885
1409 Mini Icarus 2:
1410 panels:
1411 ANIMALS: 2893
1412 ARROWS: 2894
1413 BATTERY: 2891
1414 SQUAD: 2895
1415 TROUPE: 2892
1416 Pillar Ramp:
1417 panels:
1418 ASTEROID: 2896
1419 BUNCH: 2897
1420 DRONE: 2900
1421 PATRICIDE: 2899
1422 PEA (1): 2901
1423 PRINCES: 2898
1424 Spiral Ramp:
1425 panels:
1426 FIREMAN: 2902
1427 The Orb:
1428 panels:
1429 ADDERS: 2903
1430 AXIS: 2913
1431 BASIS (2): 2912
1432 CLUTCH (1): 2911
1433 CLUTCH (2): 2918
1434 DEADLINE: 2908
1435 DISCUS: 2916
1436 FISH: 2907
1437 HISS: 2915
1438 NEEDLE: 2905
1439 PEA (2): 2909
1440 PUPPY: 2904
1441 SON: 2917
1442 STRAIGHT: 2914
1443 THESIS: 2910
1444 US: 2906
1445 Through Woman (Obverse):
1446 panels:
1447 COW: 2920
1448 HUMAN (2): 2919
1449 Through Woman (Reverse):
1450 panels:
1451 BASIS (1): 2922
1452 PRINCE: 2921
1453 Trans Rights:
1454 panels:
1455 SERVANT (1): 2926
1456 SERVANT (2): 2927
1457 Trans Rights Panels:
1458 panels:
1459 AGENDER: 2923
1460 HUMAN (3): 2924
1461 HUMAN (4): 2925
1462 Welcome Spine (Obverse):
1463 panels:
1464 FISHWIFE: 2928
1465 HUMAN (1): 2929
1466 ports:
1467 WORLDPORT: 3135
1468 Welcome Spine (Reverse):
1469 panels:
1470 FATHER: 2930
1471 SISTER: 2932
1472 TERMITE: 2931
1473 doors:
1474 Agender Door: 2846
1475 Animals Door: 2866
1476 Ant Door: 2855
1477 Arrows Door: 2865
1478 Asteroid Bunch Door: 2852
1479 Battery Door: 2863
1480 Cow Door: 2853
1481 Fatherland Door: 2860
1482 Man Door: 2856
1483 Mediums Door: 2850
1484 Murder Panels: 2871
1485 Near Fireman Wings Painting: 2876
1486 Orb Panels: 2875
1487 Patricide Door: 2873
1488 Pea Door: 2848
1489 Quick Travel 1: 2851
1490 Quick Travel 10: 2862
1491 Quick Travel 2: 2857
1492 Quick Travel 3: 2847
1493 Quick Travel 4: 2858
1494 Quick Travel 5: 2854
1495 Quick Travel 6: 2870
1496 Quick Travel 7: 2849
1497 Quick Travel 8: 2861
1498 Quick Travel 9: 2864
1499 Reversed Arrows Door: 2868
1500 Sun Painting To Drone: 2872
1501 Termite Door: 2869
1502 These Door: 2874
1503 Troupe Door: 2867
1504 Woman Door: 2859
1505 rte: 3416
1506 the_advanced:
1507 rooms:
1508 CBA:
1509 panels:
1510 CBA (1): 2938
1511 CBA (2): 2939
1512 CBA (3): 2940
1513 Main Area:
1514 panels:
1515 BIRD: 2955
1516 Blank (1): 2964
1517 Blank (2): 2965
1518 Blank (3): 2966
1519 Blank (4): 2967
1520 Blank (5): 2968
1521 DAIRY (1): 2946
1522 DAIRY (2): 2947
1523 DAIRY SAUCE: 2948
1524 DECK (1): 2961
1525 DECK (2): 2962
1526 DECK (3): 2963
1527 FRUIT (1): 2952
1528 FRUIT (2): 2953
1529 FRUIT FRUIT: 2954
1530 GULLIBLE (1): 2949
1531 GULLIBLE (2): 2950
1532 GULLIBLE (3): 2951
1533 I: 2942
1534 LIVES: 2945
1535 OBSERVE: 2941
1536 ORDER (1): 2958
1537 ORDER (2): 2959
1538 ORDER (3): 2960
1539 ORGANIZATION: 2957
1540 REST: 2943
1541 THE: 2944
1542 UNBOTTLING: 2956
1543 ports:
1544 WORLDPORT: 3136
1545 Mastery:
1546 masteries:
1547 MASTERY: 2969
1548 doors:
1549 Side Room Puzzles: 2934
1550 rte: 3380
1212 the_ancient: 1551 the_ancient:
1213 rooms: 1552 rooms:
1214 Inside: 1553 Inside:
@@ -1220,11 +1559,14 @@ maps:
1220 doors: 1559 doors:
1221 Front Door: 41 1560 Front Door: 41
1222 Lavender Cubes: 43 1561 Lavender Cubes: 43
1562 rte: 3412
1223 the_bearer: 1563 the_bearer:
1224 rooms: 1564 rooms:
1225 Back Area: 1565 Back Area:
1226 panels: 1566 panels:
1227 COLOR: 51 1567 COLOR: 51
1568 ports:
1569 DAEDALUS: 3137
1228 Blue Animal (View): 1570 Blue Animal (View):
1229 panels: 1571 panels:
1230 HALF: 52 1572 HALF: 52
@@ -1251,6 +1593,8 @@ maps:
1251 SQUISH: 60 1593 SQUISH: 60
1252 TOAD: 64 1594 TOAD: 64
1253 VIEW: 58 1595 VIEW: 58
1596 ports:
1597 UNYIELDING: 3138
1254 Green Planet (View): 1598 Green Planet (View):
1255 panels: 1599 panels:
1256 SOIL: 66 1600 SOIL: 66
@@ -1287,6 +1631,9 @@ maps:
1287 Red Vegetable: 1631 Red Vegetable:
1288 panels: 1632 panels:
1289 CARD: 78 1633 CARD: 78
1634 Tree Entrance:
1635 ports:
1636 TREE: 3139
1290 Yellow Planet: 1637 Yellow Planet:
1291 panels: 1638 panels:
1292 ZEUS: 79 1639 ZEUS: 79
@@ -1295,14 +1642,20 @@ maps:
1295 CAKE: 80 1642 CAKE: 80
1296 doors: 1643 doors:
1297 Butterfly Entrance: 50 1644 Butterfly Entrance: 50
1645 Butterfly Room Panels: 3304
1298 Control Center Brown Door: 49 1646 Control Center Brown Door: 49
1647 Control Center Color Panel: 3303
1299 Exit Door: 47 1648 Exit Door: 47
1300 Overlook Door: 46 1649 Overlook Door: 46
1650 rte: 3373
1301 the_between: 1651 the_between:
1302 rooms: 1652 rooms:
1303 Control Center Side: 1653 Control Center Side:
1304 panels: 1654 panels:
1305 RIGHT: 93 1655 RIGHT: 93
1656 ports:
1657 CC: 3140
1658 LIVELY: 3141
1306 Main Area: 1659 Main Area:
1307 panels: 1660 panels:
1308 CAUGHT: 107 1661 CAUGHT: 107
@@ -1333,6 +1686,11 @@ maps:
1333 SUN KOI: 102 1686 SUN KOI: 102
1334 THINK: 119 1687 THINK: 119
1335 YOU: 115 1688 YOU: 115
1689 ports:
1690 GREAT: 3142
1691 Plaza Entrance:
1692 ports:
1693 PLAZA: 3143
1336 doors: 1694 doors:
1337 B2 Door: 91 1695 B2 Door: 91
1338 Blue Puzzles: 88 1696 Blue Puzzles: 88
@@ -1345,6 +1703,7 @@ maps:
1345 Purple Puzzles: 87 1703 Purple Puzzles: 87
1346 Red Puzzles: 81 1704 Red Puzzles: 81
1347 Yellow Puzzles: 82 1705 Yellow Puzzles: 82
1706 rte: 3408
1348 the_butterfly: 1707 the_butterfly:
1349 rooms: 1708 rooms:
1350 Main Area: 1709 Main Area:
@@ -1367,9 +1726,50 @@ maps:
1367 SPECIES: 122 1726 SPECIES: 122
1368 STRUCTURE: 129 1727 STRUCTURE: 129
1369 TEXT: 136 1728 TEXT: 136
1729 ports:
1730 GALLERY: 3144
1370 Mastery: 1731 Mastery:
1371 masteries: 1732 masteries:
1372 MASTERY: 140 1733 MASTERY: 140
1734 rte: 3395
1735 the_charismatic:
1736 rooms:
1737 Latitude Middle:
1738 panels:
1739 FUNNY: 2972
1740 Latitude North:
1741 panels:
1742 DEPENDABLE: 2973
1743 Latitude South:
1744 panels:
1745 CHARISMA: 2974
1746 Longitude East:
1747 panels:
1748 FUN: 2975
1749 Longitude Middle:
1750 panels:
1751 INTELLIGENT: 2976
1752 Longitude West:
1753 panels:
1754 CREATIVE: 2977
1755 Main Area:
1756 panels:
1757 AQUA: 2981
1758 ARC: 2978
1759 Blank: 2987
1760 HERE: 2984
1761 IT: 2985
1762 KING: 2979
1763 NAIL: 2983
1764 PINS: 2986
1765 TILE: 2982
1766 TIP: 2980
1767 ports:
1768 WORLDPORT: 3145
1769 Mastery:
1770 masteries:
1771 MASTERY: 2988
1772 rte: 3396
1373 the_colorful: 1773 the_colorful:
1374 rooms: 1774 rooms:
1375 Black Room: 1775 Black Room:
@@ -1386,6 +1786,9 @@ maps:
1386 CHAOS: 159 1786 CHAOS: 159
1387 KOI: 157 1787 KOI: 157
1388 WISH: 158 1788 WISH: 158
1789 ports:
1790 DARKROOM: 3147
1791 STURDY: 3146
1389 Cyan Room: 1792 Cyan Room:
1390 panels: 1793 panels:
1391 BROTHER: 160 1794 BROTHER: 160
@@ -1410,6 +1813,8 @@ maps:
1410 White Room: 1813 White Room:
1411 panels: 1814 panels:
1412 BRIGHT: 170 1815 BRIGHT: 170
1816 ports:
1817 GREAT: 3148
1413 Window Room: 1818 Window Room:
1414 panels: 1819 panels:
1415 FADING: 171 1820 FADING: 171
@@ -1420,6 +1825,7 @@ maps:
1420 Black Door: 142 1825 Black Door: 142
1421 Blue Door: 144 1826 Blue Door: 144
1422 Brown Door: 151 1827 Brown Door: 151
1828 Chaos Panel: 3305
1423 Cyan Door: 149 1829 Cyan Door: 149
1424 Gray Door: 153 1830 Gray Door: 153
1425 Green Door: 145 1831 Green Door: 145
@@ -1430,6 +1836,7 @@ maps:
1430 White Door: 141 1836 White Door: 141
1431 Window Door: 152 1837 Window Door: 152
1432 Yellow Door: 146 1838 Yellow Door: 146
1839 rte: 3365
1433 the_congruent: 1840 the_congruent:
1434 rooms: 1841 rooms:
1435 Flipped Magenta Room: 1842 Flipped Magenta Room:
@@ -1468,6 +1875,8 @@ maps:
1468 LIGHT: 209 1875 LIGHT: 209
1469 LOVES: 210 1876 LOVES: 210
1470 RANGER: 211 1877 RANGER: 211
1878 ports:
1879 DARKROOM: 3149
1471 Obverse Yellow Room: 1880 Obverse Yellow Room:
1472 panels: 1881 panels:
1473 CIVIL: 216 1882 CIVIL: 216
@@ -1482,12 +1891,41 @@ maps:
1482 Flipped Yellow Door: 175 1891 Flipped Yellow Door: 175
1483 G Keyholder Blocker: 181 1892 G Keyholder Blocker: 181
1484 G2 Door: 182 1893 G2 Door: 182
1894 Main Area Puzzles: 3306
1485 Near C Keyholder Puzzles: 180 1895 Near C Keyholder Puzzles: 180
1486 Obverse Magenta Door: 173 1896 Obverse Magenta Door: 173
1487 Obverse Yellow Door: 178 1897 Obverse Yellow Door: 178
1488 Obverse Yellow Puzzles: 179 1898 Obverse Yellow Puzzles: 179
1899 rte: 3369
1900 the_crystalline:
1901 rooms:
1902 Flip Area:
1903 panels:
1904 SUCCEED: 2989
1905 Main Area:
1906 panels:
1907 DROP: 2991
1908 LEAP: 2990
1909 SPIN: 2992
1910 ports:
1911 WORLDPORT: 3150
1912 Mastery:
1913 masteries:
1914 MASTERY: 2993
1915 doors:
1916 Checkpoint Panels: 3307
1917 rte: 3367
1489 the_darkroom: 1918 the_darkroom:
1490 rooms: 1919 rooms:
1920 Congruent Entrance:
1921 ports:
1922 CONGRUENT: 3151
1923 Cyan Hallway:
1924 ports:
1925 COLORFUL: 3152
1926 Double Sided Entrance:
1927 ports:
1928 DOUBLESIDED: 3153
1491 First Room: 1929 First Room:
1492 panels: 1930 panels:
1493 BISON: 225 1931 BISON: 225
@@ -1495,6 +1933,11 @@ maps:
1495 KOI: 228 1933 KOI: 228
1496 SHEEP: 227 1934 SHEEP: 227
1497 TUNA: 229 1935 TUNA: 229
1936 ports:
1937 ENTRY: 3155
1938 First Room Exit:
1939 ports:
1940 NEXT: 3154
1498 Second Room: 1941 Second Room:
1499 panels: 1942 panels:
1500 BISON: 231 1943 BISON: 231
@@ -1502,6 +1945,11 @@ maps:
1502 KOI: 234 1945 KOI: 234
1503 SHEEP: 233 1946 SHEEP: 233
1504 TUNA: 235 1947 TUNA: 235
1948 ports:
1949 ENTRY: 3157
1950 Second Room Exit:
1951 ports:
1952 NEXT: 3156
1505 Third Room: 1953 Third Room:
1506 panels: 1954 panels:
1507 COINS: 238 1955 COINS: 238
@@ -1513,12 +1961,15 @@ maps:
1513 LOCKS: 242 1961 LOCKS: 242
1514 TOUCHES: 243 1962 TOUCHES: 243
1515 TURNS: 237 1963 TURNS: 237
1964 ports:
1965 ENTRY: 3158
1516 doors: 1966 doors:
1517 Colorful Entrance: 222 1967 Colorful Entrance: 222
1518 Congruent Entrance: 223 1968 Congruent Entrance: 223
1519 Double Sided Entrance: 224 1969 Double Sided Entrance: 224
1520 Second Room Entrance: 219 1970 Second Room Entrance: 219
1521 Third Room Entrance: 220 1971 Third Room Entrance: 220
1972 rte: 3409
1522 the_digital: 1973 the_digital:
1523 rooms: 1974 rooms:
1524 Chamber: 1975 Chamber:
@@ -1532,19 +1983,33 @@ maps:
1532 INN: 254 1983 INN: 254
1533 OUT: 257 1984 OUT: 257
1534 YOU: 255 1985 YOU: 255
1986 Gallery Maze:
1987 ports:
1988 GALLERY: 3159
1535 Main Area: 1989 Main Area:
1536 panels: 1990 panels:
1537 COLOR: 261 1991 COLOR: 261
1538 HIT: 258 1992 HIT: 258
1539 PAINTING: 260 1993 PAINTING: 260
1540 TIN: 259 1994 TIN: 259
1995 ports:
1996 ENTRY1: 3160
1997 ENTRY2: 3161
1998 ENTRY3: 3162
1541 Tree Area: 1999 Tree Area:
1542 panels: 2000 panels:
1543 TREE: 262 2001 TREE: 262
2002 ports:
2003 TREE: 3163
2004 Unyielding Entrance:
2005 ports:
2006 UNYIELDING: 3164
1544 doors: 2007 doors:
1545 Control Center Blue Door: 246 2008 Control Center Blue Door: 246
2009 Control Center Blue Panel: 3308
1546 Gallery Entrance: 245 2010 Gallery Entrance: 245
1547 Tree Entrance: 247 2011 Tree Entrance: 247
2012 rte: 3403
1548 the_door: 2013 the_door:
1549 rooms: 2014 rooms:
1550 Main Area: 2015 Main Area:
@@ -1606,6 +2071,13 @@ maps:
1606 panels: 2071 panels:
1607 ATTIC: 285 2072 ATTIC: 285
1608 FULL: 286 2073 FULL: 286
2074 ports:
2075 DARKROOM: 3165
2076 doors:
2077 10 Panels: 3310
2078 15 Panels: 3311
2079 5 Panels: 3309
2080 rte: 3384
1609 the_entry: 2081 the_entry:
1610 rooms: 2082 rooms:
1611 Blue Alcove: 2083 Blue Alcove:
@@ -1615,6 +2087,9 @@ maps:
1615 Colored Doors Area: 2087 Colored Doors Area:
1616 panels: 2088 panels:
1617 OPEN: 327 2089 OPEN: 327
2090 Composite Room Entrance:
2091 ports:
2092 COMPOSITE: 3166
1618 Ctrl Tutorial: 2093 Ctrl Tutorial:
1619 panels: 2094 panels:
1620 RIGHT: 328 2095 RIGHT: 328
@@ -1629,16 +2104,23 @@ maps:
1629 RED: 332 2104 RED: 332
1630 SPRAY: 336 2105 SPRAY: 336
1631 SUN: 333 2106 SUN: 333
2107 Daedalus Entrance:
2108 ports:
2109 DAEDALUS: 3167
2110 Digital Entrance:
2111 ports:
2112 DIGITAL: 3168
2113 Entry Exit:
2114 ports:
2115 GREAT: 3169
1632 Eye Room: 2116 Eye Room:
1633 panels: 2117 panels:
1634 I: 339 2118 I: 339
2119 ports:
2120 LIONIZED: 3170
1635 Flipped Link Area: 2121 Flipped Link Area:
1636 panels: 2122 panels:
1637 WANDER: 340 2123 WANDER: 340
1638 Flipped Pyramid Area:
1639 panels:
1640 TURN (1): 341
1641 TURN (2): 342
1642 Flipped Right Eye: 2124 Flipped Right Eye:
1643 panels: 2125 panels:
1644 HERE: 344 2126 HERE: 344
@@ -1647,9 +2129,14 @@ maps:
1647 panels: 2129 panels:
1648 CLUE: 345 2130 CLUE: 345
1649 SLENDER: 346 2131 SLENDER: 346
2132 Four Rooms Entrance:
2133 ports:
2134 FOUR: 3171
1650 Gallery Return: 2135 Gallery Return:
1651 panels: 2136 panels:
1652 RETURN: 347 2137 RETURN: 347
2138 ports:
2139 GALLERY: 3172
1653 Least Blue Last: 2140 Least Blue Last:
1654 panels: 2141 panels:
1655 AIL: 356 2142 AIL: 356
@@ -1662,6 +2149,14 @@ maps:
1662 STEALER: 352 2149 STEALER: 352
1663 TRUST: 354 2150 TRUST: 354
1664 WANT: 351 2151 WANT: 351
2152 ports:
2153 DARKROOM: 3173
2154 Liberated Entrance:
2155 ports:
2156 BLUE: 3174
2157 Liberated Entrance Panel:
2158 panels:
2159 TURN (1): 341
1665 Lime Room: 2160 Lime Room:
1666 panels: 2161 panels:
1667 COLOR: 361 2162 COLOR: 361
@@ -1670,12 +2165,22 @@ maps:
1670 Link Area: 2165 Link Area:
1671 panels: 2166 panels:
1672 WANDER: 362 2167 WANDER: 362
2168 Literate Entrance:
2169 ports:
2170 BROWN: 3175
2171 Literate Entrance Panel:
2172 panels:
2173 TURN (2): 342
1673 Parthenon Return: 2174 Parthenon Return:
1674 panels: 2175 panels:
1675 RETURN: 363 2176 RETURN: 363
2177 ports:
2178 PARTHENON: 3176
1676 Rabbit Hole: 2179 Rabbit Hole:
1677 panels: 2180 panels:
1678 PUZZLE: 364 2181 Blank: 364
2182 ports:
2183 HOLE: 3177
1679 Rabbit Hole Lock: 2184 Rabbit Hole Lock:
1680 panels: 2185 panels:
1681 HOLE: 2749 2186 HOLE: 2749
@@ -1693,6 +2198,12 @@ maps:
1693 RAIN WOMAN: 373 2198 RAIN WOMAN: 373
1694 WANDER: 370 2199 WANDER: 370
1695 WOMAN: 372 2200 WOMAN: 372
2201 Repetitive Entrance:
2202 ports:
2203 REPETITIVE: 3178
2204 Revitalized Entrance:
2205 ports:
2206 REVITALIZED: 3179
1696 Right Eye: 2207 Right Eye:
1697 panels: 2208 panels:
1698 EYE: 374 2209 EYE: 374
@@ -1701,9 +2212,12 @@ maps:
1701 Shop Entrance: 2212 Shop Entrance:
1702 panels: 2213 panels:
1703 TURN: 377 2214 TURN: 377
2215 ports:
2216 SHOP: 3180
1704 Starting Room: 2217 Starting Room:
1705 panels: 2218 panels:
1706 EYE: 380 2219 EYE: 380
2220 Gift Maps: 2970
1707 HI: 378 2221 HI: 378
1708 HINT: 381 2222 HINT: 381
1709 THAN: 383 2223 THAN: 383
@@ -1712,33 +2226,45 @@ maps:
1712 Trick Room: 2226 Trick Room:
1713 panels: 2227 panels:
1714 INK: 388 2228 INK: 388
2229 White Hallway To Daedalus:
2230 ports:
2231 DAEDALUS: 3181
1715 Wrath Room: 2232 Wrath Room:
1716 panels: 2233 panels:
1717 CORN: 393 2234 CORN: 393
1718 DICE: 392 2235 DICE: 392
1719 WREATH: 391 2236 WREATH: 391
2237 X Area:
2238 ports:
2239 CC: 3182
1720 doors: 2240 doors:
2241 Big Eyes: 3316
1721 Blue Alcove Entrance: 297 2242 Blue Alcove Entrance: 297
1722 Blue Alcove Exit: 293 2243 Blue Alcove Exit: 293
1723 Colored Doors Area Entrance: 318 2244 Colored Doors Area Entrance: 318
1724 Composite Room Entrance: 309 2245 Composite Room Entrance: 309
1725 Control Center White Door: 307 2246 Control Center White Door: 307
2247 Control Center White Panel: 3318
1726 Corners Painting: 292 2248 Corners Painting: 292
1727 D Room Entrance: 319 2249 D Room Entrance: 319
1728 Daedalus Entrance: 311 2250 Daedalus Entrance: 311
1729 Flip Area Entrance: 310 2251 Flip Area Entrance: 310
2252 Flipped Right Eye Panels: 3315
1730 Flipped Second Room Left Door: 300 2253 Flipped Second Room Left Door: 300
1731 Flipped Second Room Right Door: 299 2254 Flipped Second Room Right Door: 299
1732 Gallery Entrance: 321 2255 Gallery Entrance: 321
1733 L Room Entrance: 322 2256 L Room Entrance: 322
2257 Least Blue Last: 3317
1734 Liberated Entrance: 314 2258 Liberated Entrance: 314
1735 Lime Room Entrance: 305 2259 Lime Room Entrance: 305
1736 Link Area Entrance: 288 2260 Link Area Entrance: 288
1737 Literate Entrance: 316 2261 Literate Entrance: 316
1738 Near D Room Painting: 320 2262 Near D Room Painting: 320
1739 Noon Door: 295 2263 Noon Door: 295
2264 Noon Door Panels: 3312
1740 Orange Door Hider: 304 2265 Orange Door Hider: 304
1741 Parthenon Entrance: 317 2266 Parthenon Entrance: 317
2267 Rabbit Hole Blank Puzzle: 3319
1742 Rabbithole Door: 294 2268 Rabbithole Door: 294
1743 Red Alcove Exit: 291 2269 Red Alcove Exit: 291
1744 Red Blue Area Left Door: 302 2270 Red Blue Area Left Door: 302
@@ -1747,6 +2273,7 @@ maps:
1747 Revitalized Entrance: 306 2273 Revitalized Entrance: 306
1748 Right Eye Entrance: 301 2274 Right Eye Entrance: 301
1749 Scarf Door: 296 2275 Scarf Door: 296
2276 Scarf Door Panels: 3313
1750 Second Room Left Door: 298 2277 Second Room Left Door: 298
1751 Second Room Right Door: 290 2278 Second Room Right Door: 290
1752 Shop Entrance: 313 2279 Shop Entrance: 313
@@ -1754,6 +2281,8 @@ maps:
1754 Third Eye Painting: 324 2281 Third Eye Painting: 324
1755 Trick Door: 287 2282 Trick Door: 287
1756 Trick To Shop Door: 289 2283 Trick To Shop Door: 289
2284 Wander Panels: 3314
2285 Wrath Room Puzzles: 3320
1757 X Area Entrance: 308 2286 X Area Entrance: 308
1758 the_extravagant: 2287 the_extravagant:
1759 rooms: 2288 rooms:
@@ -1799,6 +2328,35 @@ maps:
1799 panels: 2328 panels:
1800 CACTUS: 410 2329 CACTUS: 410
1801 TAIL: 411 2330 TAIL: 411
2331 rte: 3397
2332 the_fuzzy:
2333 rooms:
2334 Main Area:
2335 panels:
2336 ACHIEVES: 3033
2337 BEFORE: 3028
2338 BOTH: 3036
2339 Blank: 3022
2340 CAGED: 3027
2341 COMBINED: 3032
2342 DICE: 3026
2343 FIRST: 3035
2344 FORGED: 3030
2345 LOTTO: 3024
2346 OTHERS: 3031
2347 TOED: 3029
2348 TUTU: 3023
2349 UNVEILED: 3034
2350 WHERETO: 3025
2351 ports:
2352 WORLDPORT: 3183
2353 Mastery:
2354 masteries:
2355 MASTERY: 3037
2356 doors:
2357 Black Panels: 3021
2358 Green Panels: 3321
2359 rte: 3420
1802 the_gallery: 2360 the_gallery:
1803 rooms: 2361 rooms:
1804 Back Room: 2362 Back Room:
@@ -1811,6 +2369,8 @@ maps:
1811 Main Area: 2369 Main Area:
1812 keyholders: 2370 keyholders:
1813 P: 2765 2371 P: 2765
2372 ports:
2373 ENTRY: 3184
1814 doors: 2374 doors:
1815 Ancient Painting: 428 2375 Ancient Painting: 428
1816 Between Painting: 414 2376 Between Painting: 414
@@ -1831,11 +2391,14 @@ maps:
1831 Wise Painting: 416 2391 Wise Painting: 416
1832 Wondrous Painting: 422 2392 Wondrous Painting: 422
1833 Words Painting: 424 2393 Words Painting: 424
2394 rte: 3394
1834 the_gold: 2395 the_gold:
1835 rooms: 2396 rooms:
1836 The Whole Thing: 2397 The Whole Thing:
1837 panels: 2398 panels:
1838 PANEL: 434 2399 PANEL: 434
2400 doors:
2401 The Panel: 3322
1839 the_graveyard: 2402 the_graveyard:
1840 rooms: 2403 rooms:
1841 Inside: 2404 Inside:
@@ -1845,34 +2408,35 @@ maps:
1845 panels: 2408 panels:
1846 FOOT: 436 2409 FOOT: 436
1847 SEVERE: 437 2410 SEVERE: 437
2411 doors:
2412 Remember Panel: 3323
2413 rte: 3370
1848 the_great: 2414 the_great:
1849 rooms: 2415 rooms:
1850 Back Area: 2416 Back Area:
1851 panels: 2417 panels:
1852 Left Landscape Bottom: 482
1853 Left Landscape Left: 483
1854 Left Landscape Right: 481
1855 Left Landscape Top: 480
1856 PAINTING: 474 2418 PAINTING: 474
1857 PLANT: 472 2419 PLANT: 472
1858 Right Landscape Bottom: 486
1859 Right Landscape Left: 487
1860 Right Landscape Right: 485
1861 Right Landscape Top: 484
1862 TOWEL: 475 2420 TOWEL: 475
1863 TREE: 473 2421 TREE: 473
1864 Top Landscape Bottom: 478 2422 ports:
1865 Top Landscape Left: 479 2423 THREEDOORS: 3186
1866 Top Landscape Right: 477 2424 TOWER: 3187
1867 Top Landscape Top: 476 2425 TREE: 3188
2426 UNKEMPT: 3185
1868 Behind Question Area: 2427 Behind Question Area:
1869 panels: 2428 panels:
1870 DEW: 488 2429 DEW: 488
1871 NO: 490 2430 NO: 490
1872 YEW: 489 2431 YEW: 489
2432 Colorful Entrance:
2433 ports:
2434 COLORFUL: 3189
1873 Daedalus Entrance: 2435 Daedalus Entrance:
1874 panels: 2436 panels:
1875 MISSING: 491 2437 MISSING: 491
2438 ports:
2439 DAEDALUS: 3190
1876 East Landscape: 2440 East Landscape:
1877 panels: 2441 panels:
1878 COLOR: 492 2442 COLOR: 492
@@ -1880,6 +2444,8 @@ maps:
1880 Hive Entrance: 2444 Hive Entrance:
1881 panels: 2445 panels:
1882 LOST: 495 2446 LOST: 495
2447 ports:
2448 HIVE: 3191
1883 Jail Part 1: 2449 Jail Part 1:
1884 panels: 2450 panels:
1885 DECATHLON: 506 2451 DECATHLON: 506
@@ -1904,6 +2470,9 @@ maps:
1904 NECROTIZE (2): 511 2470 NECROTIZE (2): 511
1905 PILGRIM: 513 2471 PILGRIM: 513
1906 TORMENTS: 512 2472 TORMENTS: 512
2473 Jubilant Entrance:
2474 ports:
2475 JUBILANT: 3192
1907 Magnet Room: 2476 Magnet Room:
1908 panels: 2477 panels:
1909 AIRPLANE: 516 2478 AIRPLANE: 516
@@ -1940,6 +2509,12 @@ maps:
1940 SMILE: 539 2509 SMILE: 539
1941 WHY: 540 2510 WHY: 540
1942 YOU: 537 2511 YOU: 537
2512 ports:
2513 DIGITAL: 3197
2514 ENTRY: 3193
2515 KEEN: 3194
2516 LINEAR: 3196
2517 ORB: 3195
1943 Maze Center: 2518 Maze Center:
1944 panels: 2519 panels:
1945 CHASE: 549 2520 CHASE: 549
@@ -1997,6 +2572,8 @@ maps:
1997 WEATHER: 568 2572 WEATHER: 568
1998 keyholders: 2573 keyholders:
1999 X: 2770 2574 X: 2770
2575 ports:
2576 INVISIBLE: 3198
2000 Outside Jail: 2577 Outside Jail:
2001 panels: 2578 panels:
2002 GUT: 575 2579 GUT: 575
@@ -2010,6 +2587,9 @@ maps:
2010 FOUR: 581 2587 FOUR: 581
2011 HAVE: 580 2588 HAVE: 580
2012 TEN: 583 2589 TEN: 583
2590 Purple Room:
2591 ports:
2592 DAEDALUS: 3199
2013 Question Room How: 2593 Question Room How:
2014 panels: 2594 panels:
2015 QUESTION: 584 2595 QUESTION: 584
@@ -2022,6 +2602,26 @@ maps:
2022 Question Room Who: 2602 Question Room Who:
2023 panels: 2603 panels:
2024 QUESTION: 587 2604 QUESTION: 587
2605 Salmon Room:
2606 ports:
2607 BETWEEN: 3200
2608 Talented Entrance:
2609 ports:
2610 TALENTED: 3201
2611 The Landscapes:
2612 panels:
2613 Left Landscape Bottom: 482
2614 Left Landscape Left: 483
2615 Left Landscape Right: 481
2616 Left Landscape Top: 480
2617 Right Landscape Bottom: 486
2618 Right Landscape Left: 487
2619 Right Landscape Right: 485
2620 Right Landscape Top: 484
2621 Top Landscape Bottom: 478
2622 Top Landscape Left: 479
2623 Top Landscape Right: 477
2624 Top Landscape Top: 476
2025 Under Question Room: 2625 Under Question Room:
2026 panels: 2626 panels:
2027 QUESTION: 588 2627 QUESTION: 588
@@ -2034,6 +2634,10 @@ maps:
2034 RIGHT: 591 2634 RIGHT: 591
2035 SAVORY: 592 2635 SAVORY: 592
2036 TEACH: 590 2636 TEACH: 590
2637 ports:
2638 CC: 3203
2639 IMPRESSIVE: 3202
2640 PARTIAL: 3204
2037 Whole Room: 2641 Whole Room:
2038 panels: 2642 panels:
2039 BATHROOM: 604 2643 BATHROOM: 604
@@ -2069,13 +2673,19 @@ maps:
2069 SHIFT: 624 2673 SHIFT: 624
2070 doors: 2674 doors:
2071 Back Area Entrance: 439 2675 Back Area Entrance: 439
2676 Behind Orb Panel: 3336
2677 Behind Question Room Panels: 3332
2072 Between Entrance: 440 2678 Between Entrance: 440
2073 Big Y: 462 2679 Big Y: 462
2680 Broken Shed Panels: 3333
2074 Building Building Gravestone: 468 2681 Building Building Gravestone: 468
2075 Colorful Entrance: 455 2682 Colorful Entrance: 455
2076 Control Center Gray Door: 446 2683 Control Center Gray Door: 446
2684 Control Center Gray Panel: 3326
2077 Control Center Purple Door: 445 2685 Control Center Purple Door: 445
2686 Control Center Purple Panel: 3327
2078 Control Center Red Door: 447 2687 Control Center Red Door: 447
2688 Control Center Red Panel: 3328
2079 Courtyard Entrance: 442 2689 Courtyard Entrance: 442
2080 Courtyard Side Door: 461 2690 Courtyard Side Door: 461
2081 Daedalus Entrance: 448 2691 Daedalus Entrance: 448
@@ -2086,8 +2696,11 @@ maps:
2086 Invisible Entrance: 465 2696 Invisible Entrance: 465
2087 Jail Entrance: 451 2697 Jail Entrance: 451
2088 Magnet Room Entrance: 449 2698 Magnet Room Entrance: 449
2699 Mistreat Panel: 3329
2700 Nature Panels: 3334
2089 Nature Room Door: 466 2701 Nature Room Door: 466
2090 Nature Room Panels: 467 2702 Nature Room Panels: 467
2703 Near Linear Panels: 3324
2091 Near UC Painting Door: 441 2704 Near UC Painting Door: 441
2092 North Landscape Entrance: 456 2705 North Landscape Entrance: 456
2093 Pillar Room Entrance: 450 2706 Pillar Room Entrance: 450
@@ -2096,11 +2709,22 @@ maps:
2096 Savory Painting: 452 2709 Savory Painting: 452
2097 Spiral Painting: 471 2710 Spiral Painting: 471
2098 Talented Entrance: 463 2711 Talented Entrance: 463
2712 Teal Panel: 3335
2099 The Landscapes Gravestone: 458 2713 The Landscapes Gravestone: 458
2100 The Maze Gravestone: 460 2714 The Maze Gravestone: 460
2101 Tower Entrance: 459 2715 Tower Entrance: 459
2716 Tower Panels: 3330
2717 Tree Panels: 3331
2102 West/East Divider: 443 2718 West/East Divider: 443
2719 Why Is It Not Red: 3325
2103 Zero Room Panels: 470 2720 Zero Room Panels: 470
2721 rte: 3405
2722 the_hinterlands:
2723 rooms:
2724 Main Area:
2725 ports:
2726 LEFT: 3206
2727 RIGHT: 3205
2104 the_hive: 2728 the_hive:
2105 rooms: 2729 rooms:
2106 Main Area: 2730 Main Area:
@@ -2145,6 +2769,11 @@ maps:
2145 YELL: 636 2769 YELL: 636
2146 keyholders: 2770 keyholders:
2147 B: 2769 2771 B: 2769
2772 ports:
2773 DAED1: 3207
2774 DAED2: 3208
2775 DAED3: 3209
2776 GREAT: 3210
2148 Mastery Room: 2777 Mastery Room:
2149 masteries: 2778 masteries:
2150 MASTERY: 666 2779 MASTERY: 666
@@ -2156,6 +2785,7 @@ maps:
2156 WILDFLOWER: 670 2785 WILDFLOWER: 670
2157 doors: 2786 doors:
2158 Room 8 Door: 627 2787 Room 8 Door: 627
2788 rte: 3401
2159 the_impressive: 2789 the_impressive:
2160 rooms: 2790 rooms:
2161 Green Eye: 2791 Green Eye:
@@ -2163,30 +2793,44 @@ maps:
2163 LEFT: 676 2793 LEFT: 676
2164 RETURN: 674 2794 RETURN: 674
2165 TO: 675 2795 TO: 675
2796 ports:
2797 PLAZA: 3211
2166 Lobby: 2798 Lobby:
2167 panels: 2799 panels:
2168 RIGHT: 677 2800 RIGHT: 677
2801 ports:
2802 GREAT: 3212
2169 Side Area: 2803 Side Area:
2170 panels: 2804 panels:
2171 COLOR: 680 2805 COLOR: 680
2806 ports:
2807 FOURROOMS: 3213
2172 WM Room: 2808 WM Room:
2173 panels: 2809 panels:
2174 LEFT: 682 2810 LEFT: 682
2175 RIGHT: 683 2811 RIGHT: 683
2176 doors: 2812 doors:
2177 Control Center Green Door: 673 2813 Control Center Green Door: 673
2814 Control Center Green Panel: 3338
2178 Front Door: 671 2815 Front Door: 671
2816 Green Eye Panels: 3337
2179 Side Door: 672 2817 Side Door: 672
2818 rte: 3386
2180 the_invisible: 2819 the_invisible:
2181 rooms: 2820 rooms:
2182 Entrance: 2821 Entrance:
2183 panels: 2822 panels:
2184 VISIBLE: 685 2823 VISIBLE: 685
2824 ports:
2825 ENTRY: 3214
2185 Maze: 2826 Maze:
2186 masteries: 2827 masteries:
2187 MASTERY: 686 2828 MASTERY: 686
2829 ports:
2830 ENTRY: 3215
2188 doors: 2831 doors:
2189 Maze Entrance: 684 2832 Maze Entrance: 684
2833 rte: 3410
2190 the_jubilant: 2834 the_jubilant:
2191 rooms: 2835 rooms:
2192 Main Area: 2836 Main Area:
@@ -2203,6 +2847,8 @@ maps:
2203 SPRINT: 695 2847 SPRINT: 695
2204 TREE: 698 2848 TREE: 698
2205 UNFAIR: 694 2849 UNFAIR: 694
2850 ports:
2851 GREAT: 3216
2206 Side Area: 2852 Side Area:
2207 panels: 2853 panels:
2208 CALL: 704 2854 CALL: 704
@@ -2215,6 +2861,8 @@ maps:
2215 J: 2772 2861 J: 2772
2216 doors: 2862 doors:
2217 Side Door: 687 2863 Side Door: 687
2864 Side Room Puzzles: 3339
2865 rte: 3411
2218 the_keen: 2866 the_keen:
2219 rooms: 2867 rooms:
2220 Main Area: 2868 Main Area:
@@ -2228,8 +2876,11 @@ maps:
2228 TIN (3): 711 2876 TIN (3): 711
2229 TIN (4): 712 2877 TIN (4): 712
2230 TIN (5): 713 2878 TIN (5): 713
2879 ports:
2880 GREAT: 3217
2231 doors: 2881 doors:
2232 All Panels: 707 2882 All Panels: 707
2883 rte: 3402
2233 the_liberated: 2884 the_liberated:
2234 rooms: 2885 rooms:
2235 Puzzle Room: 2886 Puzzle Room:
@@ -2242,8 +2893,11 @@ maps:
2242 PERSON: 720 2893 PERSON: 720
2243 SAND: 723 2894 SAND: 723
2244 WOLF: 725 2895 WOLF: 725
2896 ports:
2897 ENTRY: 3218
2245 doors: 2898 doors:
2246 Door: 718 2899 Door: 718
2900 rte: 3422
2247 the_linear: 2901 the_linear:
2248 rooms: 2902 rooms:
2249 Room: 2903 Room:
@@ -2256,8 +2910,11 @@ maps:
2256 NOR: 735 2910 NOR: 735
2257 ROT: 731 2911 ROT: 731
2258 TON: 730 2912 TON: 730
2913 ports:
2914 GREAT: 3219
2259 doors: 2915 doors:
2260 Behind The Keen Gravestone: 727 2916 Behind The Keen Gravestone: 727
2917 rte: 3393
2261 the_lionized: 2918 the_lionized:
2262 rooms: 2919 rooms:
2263 Puzzle Room: 2920 Puzzle Room:
@@ -2270,6 +2927,9 @@ maps:
2270 LION: 741 2927 LION: 741
2271 PIG: 743 2928 PIG: 743
2272 ROCK: 740 2929 ROCK: 740
2930 ports:
2931 ENTRY: 3220
2932 rte: 3404
2273 the_literate: 2933 the_literate:
2274 rooms: 2934 rooms:
2275 Puzzle Room: 2935 Puzzle Room:
@@ -2282,8 +2942,11 @@ maps:
2282 SAND: 750 2942 SAND: 750
2283 STICK: 752 2943 STICK: 752
2284 WATER: 746 2944 WATER: 746
2945 ports:
2946 ENTRY: 3221
2285 doors: 2947 doors:
2286 Door: 745 2948 Door: 745
2949 rte: 3382
2287 the_lively: 2950 the_lively:
2288 rooms: 2951 rooms:
2289 Puzzle Room: 2952 Puzzle Room:
@@ -2296,6 +2959,9 @@ maps:
2296 ROOSTER: 762 2959 ROOSTER: 762
2297 SON: 759 2960 SON: 759
2298 SOPRANO: 757 2961 SOPRANO: 757
2962 ports:
2963 BETWEEN: 3222
2964 rte: 3383
2299 the_nuanced: 2965 the_nuanced:
2300 rooms: 2966 rooms:
2301 Back Room: 2967 Back Room:
@@ -2326,12 +2992,19 @@ maps:
2326 TORE: 787 2992 TORE: 787
2327 keyholders: 2993 keyholders:
2328 S: 2767 2994 S: 2767
2995 ports:
2996 UNYIELDING: 3223
2329 doors: 2997 doors:
2330 Blue Side Puzzles: 763 2998 Blue Side Puzzles: 763
2331 Green Side Puzzles: 764 2999 Green Side Puzzles: 764
2332 Main Room Door: 2750 3000 Main Room Door: 2750
3001 Stores Panel: 3340
3002 rte: 3398
2333 the_orb: 3003 the_orb:
2334 rooms: 3004 rooms:
3005 B Room:
3006 ports:
3007 FINAL: 3224
2335 Main Area: 3008 Main Area:
2336 panels: 3009 panels:
2337 ACT: 799 3010 ACT: 799
@@ -2346,6 +3019,12 @@ maps:
2346 STRIKE: 790 3019 STRIKE: 790
2347 THICK: 797 3020 THICK: 797
2348 THIN: 793 3021 THIN: 793
3022 ports:
3023 GREAT: 3225
3024 Middle Room:
3025 ports:
3026 MID: 3226
3027 rte: 3381
2349 the_owl: 3028 the_owl:
2350 rooms: 3029 rooms:
2351 Blue Room: 3030 Blue Room:
@@ -2373,6 +3052,11 @@ maps:
2373 SKETCH: 832 3052 SKETCH: 832
2374 WHITE: 824 3053 WHITE: 824
2375 WING: 826 3054 WING: 826
3055 ports:
3056 FOURROOMS: 3227
3057 Magenta Hallway:
3058 ports:
3059 STURDY: 3228
2376 R1C4 Left: 3060 R1C4 Left:
2377 panels: 3061 panels:
2378 STENCIL: 840 3062 STENCIL: 840
@@ -2385,6 +3069,8 @@ maps:
2385 R2C2 Bottom: 3069 R2C2 Bottom:
2386 panels: 3070 panels:
2387 FOUL: 844 3071 FOUL: 844
3072 ports:
3073 GALLERY: 3229
2388 R2C2 Top: 3074 R2C2 Top:
2389 panels: 3075 panels:
2390 CRUSH: 845 3076 CRUSH: 845
@@ -2402,16 +3088,27 @@ maps:
2402 Blue Owl: 818 3088 Blue Owl: 818
2403 Brush Door: 804 3089 Brush Door: 804
2404 Control Center Magenta Door: 812 3090 Control Center Magenta Door: 812
3091 Control Center Magenta Panel: 3343
2405 First Door: 808 3092 First Door: 808
2406 First Room Shortcut: 807 3093 First Room Shortcut: 807
2407 Gray Bottom Door: 811 3094 Gray Bottom Door: 811
2408 Gray Owl: 814 3095 Gray Owl: 814
2409 Gray Top Door: 810 3096 Gray Top Door: 810
3097 Near Z1 Panel: 3350
2410 Orange Owl: 815 3098 Orange Owl: 815
3099 R1C1 Panels: 3341
3100 R1C2 Panels: 3342
3101 R1C3 Panels: 3344
3102 R1C4 Panels: 3345
3103 R2C1 Panels: 3346
3104 R2C2 Panels: 3347
3105 R2C3 Panels: 3348
3106 R2C4 Panels: 3349
2411 Sky Bottom Doors: 806 3107 Sky Bottom Doors: 806
2412 Sky Owl: 813 3108 Sky Owl: 813
2413 Sky Top Doors: 805 3109 Sky Top Doors: 805
2414 White Owl: 816 3110 White Owl: 816
3111 rte: 3414
2415 the_parthenon: 3112 the_parthenon:
2416 rooms: 3113 rooms:
2417 Lavender Area: 3114 Lavender Area:
@@ -2427,16 +3124,24 @@ maps:
2427 CLEOPATRA: 859 3124 CLEOPATRA: 859
2428 NAPOLEON: 860 3125 NAPOLEON: 860
2429 XERXES: 857 3126 XERXES: 857
3127 ports:
3128 ENTRY: 3231
3129 GALLERY: 3230
3130 REVITALIZED: 3232
2430 U Keyholder: 3131 U Keyholder:
2431 keyholders: 3132 keyholders:
2432 U: 2777 3133 U: 2777
2433 doors: 3134 doors:
2434 K2 Door: 852 3135 K2 Door: 852
3136 Lavender Area Puzzles: 3351
3137 rte: 3418
2435 the_partial: 3138 the_partial:
2436 rooms: 3139 rooms:
2437 Control Center Entrance: 3140 Control Center Entrance:
2438 panels: 3141 panels:
2439 RETURN: 867 3142 RETURN: 867
3143 ports:
3144 CC: 3233
2440 Obverse Side: 3145 Obverse Side:
2441 panels: 3146 panels:
2442 CUT: 881 3147 CUT: 881
@@ -2455,6 +3160,8 @@ maps:
2455 UP: 870 3160 UP: 870
2456 keyholders: 3161 keyholders:
2457 L: 2771 3162 L: 2771
3163 ports:
3164 GREAT: 3234
2458 Reverse Side: 3165 Reverse Side:
2459 panels: 3166 panels:
2460 BRO: 884 3167 BRO: 884
@@ -2464,8 +3171,16 @@ maps:
2464 doors: 3171 doors:
2465 Control Center Entrance: 865 3172 Control Center Entrance: 865
2466 F Door: 866 3173 F Door: 866
3174 L Entered: 2843
2467 Main Room Puzzles: 863 3175 Main Room Puzzles: 863
2468 P Door: 864 3176 P Door: 864
3177 rte: 3407
3178 the_perceptive:
3179 rooms:
3180 Main Area:
3181 ports:
3182 CC: 3235
3183 rte: 3374
2469 the_plaza: 3184 the_plaza:
2470 rooms: 3185 rooms:
2471 Bottom Left Room: 3186 Bottom Left Room:
@@ -2499,18 +3214,28 @@ maps:
2499 ASTOUNDING: 919 3214 ASTOUNDING: 919
2500 COURTYARD: 918 3215 COURTYARD: 918
2501 INFLEXIBLE: 920 3216 INFLEXIBLE: 920
3217 ports:
3218 BETWEEN: 3238
3219 IMPRESSIVE: 3237
3220 UNYIELDING: 3236
2502 Mastery: 3221 Mastery:
2503 masteries: 3222 masteries:
2504 MASTERY: 923 3223 MASTERY: 923
2505 Repetitive Entrance: 3224 Repetitive Entrance:
2506 panels: 3225 panels:
2507 TEDIOUS: 924 3226 TEDIOUS: 924
3227 ports:
3228 REPETITIVE: 3239
2508 Sirenic Entrance: 3229 Sirenic Entrance:
2509 panels: 3230 panels:
2510 SIREN: 925 3231 SIREN: 925
3232 ports:
3233 SIRENIC: 3240
2511 Symbolic Entrance: 3234 Symbolic Entrance:
2512 panels: 3235 panels:
2513 FIGURATIVE: 926 3236 FIGURATIVE: 926
3237 ports:
3238 SYMBOLIC: 3241
2514 Top Left Room: 3239 Top Left Room:
2515 panels: 3240 panels:
2516 BACKPACK: 939 3241 BACKPACK: 939
@@ -2559,6 +3284,10 @@ maps:
2559 TYPIST BEAR RIGHT WING: 968 3284 TYPIST BEAR RIGHT WING: 968
2560 WING: 950 3285 WING: 950
2561 doors: 3286 doors:
3287 Near Broken Portal Panel: 3355
3288 Near Repetitive Panel: 3354
3289 Near Sirenic Panel: 3352
3290 Near Symbolic Panel: 3353
2562 Northeast Door: 893 3291 Northeast Door: 893
2563 Northeast Puzzles: 897 3292 Northeast Puzzles: 897
2564 Northwest Door: 892 3293 Northwest Door: 892
@@ -2571,6 +3300,7 @@ maps:
2571 Southwest Puzzles: 898 3300 Southwest Puzzles: 898
2572 Symbolic Entrance: 889 3301 Symbolic Entrance: 889
2573 Turtle Entrance: 891 3302 Turtle Entrance: 891
3303 rte: 3390
2574 the_quiet: 3304 the_quiet:
2575 rooms: 3305 rooms:
2576 Keyholder Room: 3306 Keyholder Room:
@@ -2590,8 +3320,11 @@ maps:
2590 RODENT: 972 3320 RODENT: 972
2591 RULE: 974 3321 RULE: 974
2592 SOLID: 971 3322 SOLID: 971
3323 ports:
3324 DAEDALUS: 3242
2593 doors: 3325 doors:
2594 Side Door: 970 3326 Side Door: 970
3327 rte: 3419
2595 the_relentless: 3328 the_relentless:
2596 rooms: 3329 rooms:
2597 All: 3330 All:
@@ -2646,7 +3379,10 @@ maps:
2646 HIDE (2): 1021 3379 HIDE (2): 1021
2647 MORE: 1022 3380 MORE: 1022
2648 doors: 3381 doors:
3382 Left Only Puzzles: 3020
2649 Left/Turn Door: 984 3383 Left/Turn Door: 984
3384 Shop Only Puzzles: 3019
3385 Turn Only Puzzles: 3018
2650 Turn/Shop Door: 985 3386 Turn/Shop Door: 985
2651 the_repetitive: 3387 the_repetitive:
2652 rooms: 3388 rooms:
@@ -2691,6 +3427,9 @@ maps:
2691 TO (2): 1056 3427 TO (2): 1056
2692 TUTU (1): 1054 3428 TUTU (1): 1054
2693 TUTU (2): 1068 3429 TUTU (2): 1068
3430 Entry Connector:
3431 ports:
3432 ENTRY: 3243
2694 Lime Room: 3433 Lime Room:
2695 panels: 3434 panels:
2696 BIRD: 1074 3435 BIRD: 1074
@@ -2743,9 +3482,14 @@ maps:
2743 MISHMASH: 1114 3482 MISHMASH: 1114
2744 QUESTION: 1105 3483 QUESTION: 1105
2745 RICHES: 1112 3484 RICHES: 1112
3485 ports:
3486 CC: 3244
2746 Mastery Room: 3487 Mastery Room:
2747 masteries: 3488 masteries:
2748 MASTERY: 1116 3489 MASTERY: 1116
3490 Plaza Connector:
3491 ports:
3492 PLAZA: 3245
2749 Yellow Room: 3493 Yellow Room:
2750 panels: 3494 panels:
2751 3D: 1123 3495 3D: 1123
@@ -2762,22 +3506,28 @@ maps:
2762 doors: 3506 doors:
2763 Anti-Collectable: 2812 3507 Anti-Collectable: 2812
2764 Anti-Collectable Room: 1025 3508 Anti-Collectable Room: 1025
3509 Anti-Collectable Room Panels: 3358
2765 Black Hallway: 2780 3510 Black Hallway: 2780
2766 Cyan Door: 1028 3511 Cyan Door: 1028
2767 Cyan Puzzles: 1032 3512 Cyan Puzzles: 1032
2768 Dot Area Entrance: 1026 3513 Dot Area Entrance: 1026
2769 Entry Entrance: 1023 3514 Entry Entrance: 1023
3515 H2 Room Puzzles: 3357
3516 Hots Panels: 3356
2770 Lime Door: 1027 3517 Lime Door: 1027
2771 Lime Puzzles: 1031 3518 Lime Puzzles: 1031
2772 Magenta Door: 1029 3519 Magenta Door: 1029
2773 Magenta Puzzles: 1033 3520 Magenta Puzzles: 1033
2774 Yellow Door: 1030 3521 Yellow Door: 1030
2775 Yellow Puzzles: 1034 3522 Yellow Puzzles: 1034
3523 rte: 3377
2776 the_revitalized: 3524 the_revitalized:
2777 rooms: 3525 rooms:
2778 Bye Room: 3526 Bye Room:
2779 panels: 3527 panels:
2780 BYE: 1129 3528 BYE: 1129
3529 ports:
3530 PARTHENON: 3246
2781 Hidden Room: 3531 Hidden Room:
2782 panels: 3532 panels:
2783 HIDDEN: 1130 3533 HIDDEN: 1130
@@ -2798,6 +3548,7 @@ maps:
2798 WON: 1135 3548 WON: 1135
2799 doors: 3549 doors:
2800 Return Panel: 1128 3550 Return Panel: 1128
3551 rte: 3378
2801 the_shop: 3552 the_shop:
2802 rooms: 3553 rooms:
2803 Main Area: 3554 Main Area:
@@ -2826,9 +3577,13 @@ maps:
2826 TADPOLES: 1159 3577 TADPOLES: 1159
2827 keyholders: 3578 keyholders:
2828 N: 2779 3579 N: 2779
3580 ports:
3581 ENTRY: 3247
2829 doors: 3582 doors:
2830 Books Puzzles: 1136 3583 Books Puzzles: 1136
2831 Games Puzzles: 1137 3584 Games Puzzles: 1137
3585 N Entered: 2971
3586 rte: 3421
2832 the_sirenic: 3587 the_sirenic:
2833 rooms: 3588 rooms:
2834 Mastery: 3589 Mastery:
@@ -2855,8 +3610,64 @@ maps:
2855 panels: 3610 panels:
2856 Flipped: 1178 3611 Flipped: 1178
2857 Obverse: 1179 3612 Obverse: 1179
3613 ports:
3614 PLAZA: 3248
2858 doors: 3615 doors:
2859 Entrance: 1161 3616 Entrance: 1161
3617 rte: 3389
3618 the_stellar:
3619 rooms:
3620 Blue Panel:
3621 panels:
3622 BLUE: 2996
3623 Connected Area:
3624 panels:
3625 BEHIND: 3003
3626 Blank: 3004
3627 GREETINGS: 3002
3628 HERE: 2997
3629 HI: 3000
3630 QUESTION (1): 2999
3631 QUESTION (2): 3016
3632 START: 3005
3633 WHERE: 3001
3634 Green Area:
3635 panels:
3636 STRAYS: 3006
3637 Green Panel:
3638 panels:
3639 GREEN: 3007
3640 Hi Room:
3641 panels:
3642 HI: 3008
3643 Mastery:
3644 masteries:
3645 MASTERY: 3009
3646 Old Crossroads:
3647 panels:
3648 DOORWAY: 3010
3649 Orange Panel:
3650 panels:
3651 ORANGE: 3011
3652 Purple Panel:
3653 panels:
3654 PURPLE: 3012
3655 Red Panel:
3656 panels:
3657 RED: 3013
3658 Starting Room:
3659 panels:
3660 STARLIKE: 3014
3661 ports:
3662 WORLDPORT: 3249
3663 Yellow Panel:
3664 panels:
3665 YELLOW: 3015
3666 doors:
3667 Entrance: 2995
3668 Question Panels: 3017
3669 Welcome Back Panels: 3359
3670 rte: 3387
2860 the_stormy: 3671 the_stormy:
2861 rooms: 3672 rooms:
2862 Center: 3673 Center:
@@ -2865,6 +3676,8 @@ maps:
2865 REACTOR: 1180 3676 REACTOR: 1180
2866 VOLCANO: 1181 3677 VOLCANO: 1181
2867 WIND: 1183 3678 WIND: 1183
3679 ports:
3680 ENTRY: 3250
2868 Nuclear Side: 3681 Nuclear Side:
2869 panels: 3682 panels:
2870 GERM: 1184 3683 GERM: 1184
@@ -2885,6 +3698,7 @@ maps:
2885 panels: 3698 panels:
2886 RAIN: 1191 3699 RAIN: 1191
2887 SNOW: 1190 3700 SNOW: 1190
3701 rte: 3368
2888 the_sturdy: 3702 the_sturdy:
2889 rooms: 3703 rooms:
2890 Main Area: 3704 Main Area:
@@ -2897,14 +3711,20 @@ maps:
2897 MOVE (6): 1198 3711 MOVE (6): 1198
2898 MOVE (7): 1199 3712 MOVE (7): 1199
2899 MOVE (8): 1200 3713 MOVE (8): 1200
3714 ports:
3715 COLORFUL: 3252
3716 OWL: 3251
2900 S2 Area: 3717 S2 Area:
2901 panels: 3718 panels:
2902 COLORS: 1201 3719 COLORS: 1201
3720 rte: 3391
2903 the_sun_temple: 3721 the_sun_temple:
2904 rooms: 3722 rooms:
2905 Entrance: 3723 Entrance:
2906 panels: 3724 panels:
2907 SUN: 1212 3725 SUN: 1212
3726 ports:
3727 UNKEMPT: 3253
2908 Mastery: 3728 Mastery:
2909 masteries: 3729 masteries:
2910 MASTERY: 1213 3730 MASTERY: 1213
@@ -2919,6 +3739,7 @@ maps:
2919 LAWN: 1215 3739 LAWN: 1215
2920 doors: 3740 doors:
2921 Entrance: 1210 3741 Entrance: 1210
3742 rte: 3375
2922 the_sweet: 3743 the_sweet:
2923 rooms: 3744 rooms:
2924 Main Area: 3745 Main Area:
@@ -2951,6 +3772,10 @@ maps:
2951 SQUISH: 1241 3772 SQUISH: 1241
2952 VEGETABLE: 1232 3773 VEGETABLE: 1232
2953 WATER: 1226 3774 WATER: 1226
3775 ports:
3776 EXIT1: 3254
3777 EXIT2: 3255
3778 rte: 3400
2954 the_symbolic: 3779 the_symbolic:
2955 rooms: 3780 rooms:
2956 Black Room: 3781 Black Room:
@@ -3132,6 +3957,8 @@ maps:
3132 White Room: 3957 White Room:
3133 panels: 3958 panels:
3134 WRITE: 2425 3959 WRITE: 2425
3960 ports:
3961 PLAZA: 3256
3135 Yellow Room: 3962 Yellow Room:
3136 panels: 3963 panels:
3137 WHOLE: 2426 3964 WHOLE: 2426
@@ -3145,6 +3972,7 @@ maps:
3145 Tutorial Door: 2754 3972 Tutorial Door: 2754
3146 Tutorial Panels: 2283 3973 Tutorial Panels: 2283
3147 Whirred Room Panels: 2284 3974 Whirred Room Panels: 2284
3975 rte: 3379
3148 the_talented: 3976 the_talented:
3149 rooms: 3977 rooms:
3150 Back Room: 3978 Back Room:
@@ -3175,10 +4003,14 @@ maps:
3175 WIFE (Brown): 2447 4003 WIFE (Brown): 2447
3176 keyholders: 4004 keyholders:
3177 Y: 2764 4005 Y: 2764
4006 ports:
4007 GREAT: 3257
3178 doors: 4008 doors:
3179 Black Side Panels: 2427 4009 Black Side Panels: 2427
3180 Brown Side Panels: 2428 4010 Brown Side Panels: 2428
4011 Keyholder Hint Panel: 3360
3181 Main Room Door: 2429 4012 Main Room Door: 2429
4013 rte: 3388
3182 the_tenacious: 4014 the_tenacious:
3183 rooms: 4015 rooms:
3184 Color Room: 4016 Color Room:
@@ -3187,6 +4019,8 @@ maps:
3187 Control Center Entrance: 4019 Control Center Entrance:
3188 panels: 4020 panels:
3189 ZERO: 2455 4021 ZERO: 2455
4022 ports:
4023 CC: 3258
3190 Main Area: 4024 Main Area:
3191 keyholders: 4025 keyholders:
3192 K: 2768 4026 K: 2768
@@ -3203,7 +4037,9 @@ maps:
3203 panels: 4037 panels:
3204 WISDOM: 2459 4038 WISDOM: 2459
3205 doors: 4039 doors:
4040 K Entered: 2844
3206 Paintings Door: 2453 4041 Paintings Door: 2453
4042 rte: 3399
3207 the_three_doors: 4043 the_three_doors:
3208 rooms: 4044 rooms:
3209 Dead End Room: 4045 Dead End Room:
@@ -3212,28 +4048,42 @@ maps:
3212 DOOR: 2461 4048 DOOR: 2461
3213 END: 2464 4049 END: 2464
3214 WAYS: 2462 4050 WAYS: 2462
4051 ports:
4052 BEGIN: 3259
4053 BEGIN2: 3260
3215 First Second Room: 4054 First Second Room:
3216 panels: 4055 panels:
3217 FIRS: 2465 4056 FIRS: 2465
3218 INITIAL: 2466 4057 INITIAL: 2466
3219 MINUTE (1): 2467 4058 MINUTE (1): 2467
3220 MINUTE (2): 2468 4059 MINUTE (2): 2468
4060 ports:
4061 GREAT: 3261
4062 TTD: 3262
3221 Loose Strings Room: 4063 Loose Strings Room:
3222 panels: 4064 panels:
3223 LOOSE: 2469 4065 LOOSE: 2469
3224 STRINGS: 2470 4066 STRINGS: 2470
4067 ports:
4068 BEGIN: 3263
3225 One Luck Room: 4069 One Luck Room:
3226 panels: 4070 panels:
3227 CHANCE: 2472 4071 CHANCE: 2472
3228 LONE: 2471 4072 LONE: 2471
4073 ports:
4074 BEGIN: 3264
3229 Silver Portal Room: 4075 Silver Portal Room:
3230 panels: 4076 panels:
3231 GOLD: 2473 4077 GOLD: 2473
3232 Left: 2475 4078 Left: 2475
3233 PORT: 2474 4079 PORT: 2474
3234 Right: 2476 4080 Right: 2476
4081 ports:
4082 BEGIN: 3265
4083 NEXT: 3266
3235 doors: 4084 doors:
3236 The Three Doors Gravestone: 2460 4085 The Three Doors Gravestone: 2460
4086 rte: 3392
3237 the_tower: 4087 the_tower:
3238 rooms: 4088 rooms:
3239 First Floor: 4089 First Floor:
@@ -3250,6 +4100,8 @@ maps:
3250 PROD: 2485 4100 PROD: 2485
3251 RIDE: 2484 4101 RIDE: 2484
3252 WARM: 2486 4102 WARM: 2486
4103 ports:
4104 GREAT: 3267
3253 Tower: 4105 Tower:
3254 panels: 4106 panels:
3255 ANNOY (1): 2508 4107 ANNOY (1): 2508
@@ -3311,8 +4163,12 @@ maps:
3311 Fourth Floor Puzzles: 2481 4163 Fourth Floor Puzzles: 2481
3312 Second Floor Puzzles: 2479 4164 Second Floor Puzzles: 2479
3313 Third Floor Puzzles: 2480 4165 Third Floor Puzzles: 2480
4166 rte: 3413
3314 the_tree: 4167 the_tree:
3315 rooms: 4168 rooms:
4169 Bearer Entrance:
4170 ports:
4171 BEARER: 3268
3316 Main Area: 4172 Main Area:
3317 panels: 4173 panels:
3318 COLOR: 2550 4174 COLOR: 2550
@@ -3345,14 +4201,25 @@ maps:
3345 WADE: 2562 4201 WADE: 2562
3346 WALK (1): 2555 4202 WALK (1): 2555
3347 WALK (2): 2556 4203 WALK (2): 2556
4204 ports:
4205 DAEDALUS: 3272
4206 DIGITAL: 3270
4207 GREAT: 3271
4208 UNKEMPT: 3269
3348 doors: 4209 doors:
3349 Control Center Brown Door: 2548 4210 Control Center Brown Door: 2548
3350 The Tree Gravestone: 2549 4211 The Tree Gravestone: 2549
4212 rte: 3371
3351 the_unkempt: 4213 the_unkempt:
3352 rooms: 4214 rooms:
3353 Control Center Entrance: 4215 Control Center Entrance:
3354 panels: 4216 panels:
3355 RETURN: 2587 4217 RETURN: 2587
4218 ports:
4219 CC: 3273
4220 Daedalus Entrance:
4221 ports:
4222 DAEDALUS: 3274
3356 Exit Room 2: 4223 Exit Room 2:
3357 panels: 4224 panels:
3358 DOOR: 2590 4225 DOOR: 2590
@@ -3396,6 +4263,10 @@ maps:
3396 ZOO: 2615 4263 ZOO: 2615
3397 keyholders: 4264 keyholders:
3398 I: 2775 4265 I: 2775
4266 ports:
4267 GREAT: 3275
4268 SUNTEMPLE: 3277
4269 TREE: 3276
3399 Middle Room: 4270 Middle Room:
3400 panels: 4271 panels:
3401 FELLOW: 2624 4272 FELLOW: 2624
@@ -3452,13 +4323,20 @@ maps:
3452 doors: 4323 doors:
3453 Cog Rhino Hug Rug: 2586 4324 Cog Rhino Hug Rug: 2586
3454 Control Center Orange Door: 2582 4325 Control Center Orange Door: 2582
4326 Control Center Orange Panel: 3362
3455 East Door: 2580 4327 East Door: 2580
3456 Honor Our Hint: 2585 4328 Honor Our Hint: 2585
4329 I Entered: 2845
3457 Let Untrue Tie: 2583 4330 Let Untrue Tie: 2583
4331 Near Teal Door Panels: 3361
3458 Routine Out Chute: 2584 4332 Routine Out Chute: 2584
3459 W2 Room Door: 2581 4333 W2 Room Door: 2581
4334 rte: 3417
3460 the_unyielding: 4335 the_unyielding:
3461 rooms: 4336 rooms:
4337 Bearer Entrance:
4338 ports:
4339 BEARER: 3278
3462 Behind Northeast: 4340 Behind Northeast:
3463 panels: 4341 panels:
3464 FIND: 1260 4342 FIND: 1260
@@ -3546,6 +4424,8 @@ maps:
3546 Digital Entrance: 4424 Digital Entrance:
3547 panels: 4425 panels:
3548 ORANGE: 1326 4426 ORANGE: 1326
4427 ports:
4428 DIGITAL: 3279
3549 Directions Room: 4429 Directions Room:
3550 panels: 4430 panels:
3551 ABS: 1327 4431 ABS: 1327
@@ -3627,6 +4507,9 @@ maps:
3627 RAT: 1380 4507 RAT: 1380
3628 SEE: 1377 4508 SEE: 1377
3629 TIC: 1379 4509 TIC: 1379
4510 Nuanced Entrance:
4511 ports:
4512 NUANCED: 3280
3630 Orange Alcove: 4513 Orange Alcove:
3631 panels: 4514 panels:
3632 ON: 1386 4515 ON: 1386
@@ -3634,6 +4517,8 @@ maps:
3634 panels: 4517 panels:
3635 GEE: 1387 4518 GEE: 1387
3636 SEA: 1388 4519 SEA: 1388
4520 ports:
4521 PLAZA: 3281
3637 Red Eyes: 4522 Red Eyes:
3638 panels: 4523 panels:
3639 RED EYES: 1389 4524 RED EYES: 1389
@@ -3694,7 +4579,9 @@ maps:
3694 HEALTH: 1428 4579 HEALTH: 1428
3695 doors: 4580 doors:
3696 Bearer Entrance: 1259 4581 Bearer Entrance: 1259
4582 Blue D Room Puzzles: 3363
3697 Brown Alcove: 1255 4583 Brown Alcove: 1255
4584 Color Hallway Panels: 3364
3698 Digital Entrance: 1257 4585 Digital Entrance: 1257
3699 East Room 1: 2740 4586 East Room 1: 2740
3700 East Room 1 Entrance: 1251 4587 East Room 1 Entrance: 1251
@@ -3707,6 +4594,7 @@ maps:
3707 Southwest Corner Behind: 1254 4594 Southwest Corner Behind: 1254
3708 Southwest Room: 1431 4595 Southwest Room: 1431
3709 Yellow Room Puzzles: 1250 4596 Yellow Room Puzzles: 1250
4597 rte: 3366
3710 the_wise: 4598 the_wise:
3711 rooms: 4599 rooms:
3712 Entry: 4600 Entry:
@@ -3739,11 +4627,14 @@ maps:
3739 WORDS: 2688 4627 WORDS: 2688
3740 doors: 4628 doors:
3741 Front Door: 2666 4629 Front Door: 2666
4630 rte: 3372
3742 the_wondrous: 4631 the_wondrous:
3743 rooms: 4632 rooms:
3744 Entry: 4633 Entry:
3745 panels: 4634 panels:
3746 WONDER: 2690 4635 WONDER: 2690
4636 ports:
4637 DAEDALUS: 3282
3747 Huge: 4638 Huge:
3748 panels: 4639 panels:
3749 BARK: 2695 4640 BARK: 2695
@@ -3759,6 +4650,7 @@ maps:
3759 SHRINK: 2699 4650 SHRINK: 2699
3760 doors: 4651 doors:
3761 Shrink Door: 2689 4652 Shrink Door: 2689
4653 rte: 3406
3762 the_words: 4654 the_words:
3763 rooms: 4655 rooms:
3764 Main Area: 4656 Main Area:
@@ -3771,6 +4663,9 @@ maps:
3771 METAL: 2706 4663 METAL: 2706
3772 SPICE: 2708 4664 SPICE: 2708
3773 TREE: 2705 4665 TREE: 2705
4666 ports:
4667 ENTRY: 3283
4668 rte: 3423
3774letters: 4669letters:
3775 a1: 596 4670 a1: 596
3776 a2: 6 4671 a2: 6
@@ -3876,6 +4771,7 @@ special:
3876 Job Symbol: 2798 4771 Job Symbol: 2798
3877 Lingo Symbol: 2799 4772 Lingo Symbol: 2799
3878 Null Symbol: 2800 4773 Null Symbol: 2800
4774 Numbers: 3038
3879 Planet Symbol: 2801 4775 Planet Symbol: 2801
3880 Pyramid Symbol: 2802 4776 Pyramid Symbol: 2802
3881 Question Symbol: 2803 4777 Question Symbol: 2803
@@ -3886,8 +4782,10 @@ special:
3886 Sweet Symbol: 2808 4782 Sweet Symbol: 2808
3887 Zero Symbol: 2809 4783 Zero Symbol: 2809
3888progressives: 4784progressives:
4785 Icarus Quick Travel: 2933
3889 Progressive Gold Ending: 2753 4786 Progressive Gold Ending: 2753
3890door_groups: 4787door_groups:
4788 Control Center - Perceptive Entrance: 3424
3891 Control Center Blue Doors: 2788 4789 Control Center Blue Doors: 2788
3892 Control Center Brown Doors: 2787 4790 Control Center Brown Doors: 2787
3893 Control Center Orange Doors: 2786 4791 Control Center Orange Doors: 2786
diff --git a/data/maps/control_center/connections.txtpb b/data/maps/control_center/connections.txtpb index 432d39d..5dc2890 100644 --- a/data/maps/control_center/connections.txtpb +++ b/data/maps/control_center/connections.txtpb
@@ -17,6 +17,7 @@ connections {
17 from_room: "Main Area" 17 from_room: "Main Area"
18 to_room: "Mint Ending" 18 to_room: "Mint Ending"
19 door { name: "Mint Ending Door" } 19 door { name: "Mint Ending Door" }
20 mint_ending: true
20} 21}
21connections { 22connections {
22 from_room: "Main Area" 23 from_room: "Main Area"
diff --git a/data/maps/control_center/doors.txtpb b/data/maps/control_center/doors.txtpb index 08476a7..c64274a 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: STANDARD
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/metadata.txtpb b/data/maps/control_center/metadata.txtpb index bf89670..e56f7a9 100644 --- a/data/maps/control_center/metadata.txtpb +++ b/data/maps/control_center/metadata.txtpb
@@ -1 +1,2 @@
1display_name: "Control Center" 1display_name: "Control Center"
2rte_room: "Main Area"
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/doors.txtpb b/data/maps/daedalus/doors.txtpb index 95a3537..4a34070 100644 --- a/data/maps/daedalus/doors.txtpb +++ b/data/maps/daedalus/doors.txtpb
@@ -188,10 +188,12 @@ 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"
@@ -199,6 +201,7 @@ doors {
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"
@@ -299,7 +311,8 @@ doors {
299} 311}
300doors { 312doors {
301 name: "Splintering Exit North 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" }
@@ -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"
@@ -644,6 +699,8 @@ doors {
644doors { 699doors {
645 name: "Hedges Tower" 700 name: "Hedges Tower"
646 type: LOCATION_ONLY 701 type: LOCATION_ONLY
702 latch: true
703 receivers: "Components/Doors/Halls/tower_door"
647 # TODO: Not making this an item right now in order to force the player to 704 # TODO: Not making this an item right now in order to force the player to
648 # solve the puzzles in order to enter The Tenacious. In the future, I'd like 705 # solve the puzzles in order to enter The Tenacious. In the future, I'd like
649 # to make this an item, and make you solve the panels in order to get the 706 # to make this an item, and make you solve the panels in order to get the
@@ -819,12 +876,23 @@ doors {
819} 876}
820doors { 877doors {
821 name: "Composite Room NW Entrance" 878 name: "Composite Room NW Entrance"
822 type: STANDARD 879 type: ITEM_ONLY
880 legacy_location: true
823 receivers: "Components/Doors/Halls/oroom_10" 881 receivers: "Components/Doors/Halls/oroom_10"
824 panels { room: "Red Color Door" name: "Near Obscured Puzzles" } 882 panels { room: "Red Color Door" name: "Near Obscured Puzzles" }
825 location_room: "Red Color Door" 883 location_room: "Red Color Door"
826} 884}
827doors { 885doors {
886 name: "Yellow Roof Puzzles"
887 type: LOCATION_ONLY
888 panels { room: "Red Color Door" name: "BACKSIDE" }
889 panels { room: "Red Color Door" name: "WALK BACK" }
890 panels { room: "Red Color Door" name: "Back (1)" }
891 panels { room: "Red Color Door" name: "Back (2)" }
892 panels { room: "Red Color Door" name: "Near Obscured Puzzles" }
893 location_room: "Red Color Door"
894}
895doors {
828 name: "Composite Room South Door" 896 name: "Composite Room South Door"
829 type: LOCATION_ONLY 897 type: LOCATION_ONLY
830 #receivers: "Components/Doors/Halls/oroom_9" 898 #receivers: "Components/Doors/Halls/oroom_9"
@@ -865,6 +933,7 @@ doors {
865doors { 933doors {
866 name: "Control Center Orange Door" 934 name: "Control Center Orange Door"
867 type: CONTROL_CENTER_COLOR 935 type: CONTROL_CENTER_COLOR
936 latch: true
868 receivers: "Components/Doors/Halls/oroom_6" 937 receivers: "Components/Doors/Halls/oroom_6"
869 control_center_color: "orange" 938 control_center_color: "orange"
870} 939}
@@ -884,7 +953,8 @@ doors {
884} 953}
885doors { 954doors {
886 name: "F2 Room Southeast Door" 955 name: "F2 Room Southeast Door"
887 type: STANDARD 956 type: ITEM_ONLY
957 legacy_location: true
888 receivers: "Components/Doors/Halls/froom_2" 958 receivers: "Components/Doors/Halls/froom_2"
889 panels { room: "Sweet Foyer" name: "RENT (1)" } 959 panels { room: "Sweet Foyer" name: "RENT (1)" }
890 location_room: "Sweet Foyer" 960 location_room: "Sweet Foyer"
@@ -892,12 +962,14 @@ doors {
892doors { 962doors {
893 name: "White Hallway From Entry" 963 name: "White Hallway From Entry"
894 type: CONTROL_CENTER_COLOR 964 type: CONTROL_CENTER_COLOR
965 latch: true
895 receivers: "Components/Doors/Halls/froom_6" 966 receivers: "Components/Doors/Halls/froom_6"
896 control_center_color: "white" 967 control_center_color: "white"
897} 968}
898doors { 969doors {
899 name: "Purple Hallway From Great" 970 name: "Purple Hallway From Great"
900 type: CONTROL_CENTER_COLOR 971 type: CONTROL_CENTER_COLOR
972 latch: true
901 receivers: "Components/Doors/Halls/froom_7" 973 receivers: "Components/Doors/Halls/froom_7"
902 control_center_color: "purple" 974 control_center_color: "purple"
903} 975}
@@ -1387,6 +1459,9 @@ doors {
1387 receivers: "Meshes/Stairs/staircase31/teleportListener" 1459 receivers: "Meshes/Stairs/staircase31/teleportListener"
1388 receivers: "Meshes/Stairs/staircase32/teleportListener2" 1460 receivers: "Meshes/Stairs/staircase32/teleportListener2"
1389 receivers: "Meshes/Stairs/staircase33/teleportListener3" 1461 receivers: "Meshes/Stairs/staircase33/teleportListener3"
1462 receivers: "Panels/Castle Entrance/castle_direction_1/teleportListener"
1463 receivers: "Panels/Castle Entrance/castle_direction_2/teleportListener"
1464 receivers: "Panels/Castle Entrance/castle_direction_3/teleportListener"
1390 panels { room: "North Castle Area" name: "A SUMMER PLACE" } 1465 panels { room: "North Castle Area" name: "A SUMMER PLACE" }
1391 panels { room: "West Castle Area" name: "SONG FACE" } 1466 panels { room: "West Castle Area" name: "SONG FACE" }
1392 panels { room: "South Castle Area" name: "AN OFFER VILLAGE BEFORE LAIR" } 1467 panels { room: "South Castle Area" name: "AN OFFER VILLAGE BEFORE LAIR" }
@@ -1463,6 +1538,7 @@ doors {
1463 type: ITEM_ONLY 1538 type: ITEM_ONLY
1464 receivers: "Components/Doors/Color Reading/door_2" 1539 receivers: "Components/Doors/Color Reading/door_2"
1465 panels { room: "Rainbow Start" name: "PAINTING" } 1540 panels { room: "Rainbow Start" name: "PAINTING" }
1541 daedalus_only_always_item: true
1466} 1542}
1467doors { 1543doors {
1468 name: "Red Rainbow Room" 1544 name: "Red Rainbow Room"
@@ -1579,6 +1655,7 @@ doors {
1579 panels { room: "Salt Room" name: "SEASONING" } 1655 panels { room: "Salt Room" name: "SEASONING" }
1580 panels { room: "Pepper Room" name: "SEASONING" } 1656 panels { room: "Pepper Room" name: "SEASONING" }
1581 location_room: "Pepper Room" 1657 location_room: "Pepper Room"
1658 location_name: "Seasonings"
1582} 1659}
1583doors { 1660doors {
1584 name: "Bow Side" 1661 name: "Bow Side"
@@ -1763,12 +1840,37 @@ doors {
1763} 1840}
1764doors { 1841doors {
1765 name: "Near Sweet Brown Door" 1842 name: "Near Sweet Brown Door"
1766 type: STANDARD 1843 type: ITEM_ONLY
1844 legacy_location: true
1767 receivers: "Components/Doors/Halls 2/halls_2" 1845 receivers: "Components/Doors/Halls 2/halls_2"
1768 panels { room: "Sweet Foyer" name: "RENT (4)" } 1846 panels { room: "Sweet Foyer" name: "RENT (4)" }
1769 location_room: "Sweet Foyer" 1847 location_room: "Sweet Foyer"
1770} 1848}
1771doors { 1849doors {
1850 name: "Rent Panels"
1851 type: LOCATION_ONLY
1852 panels { room: "Sweet Foyer" name: "RENT (1)" }
1853 panels { room: "Sweet Foyer" name: "RENT (2)" }
1854 panels { room: "Sweet Foyer" name: "RENT (3)" }
1855 panels { room: "Sweet Foyer" name: "RENT (4)" }
1856 location_room: "Sweet Foyer"
1857}
1858doors {
1859 name: "Equality Panels"
1860 type: LOCATION_ONLY
1861 panels { room: "Sweet Foyer" name: "EQUAL" }
1862 panels { room: "Sweet Foyer" name: "QUALITY" }
1863 location_room: "Sweet Foyer"
1864 location_name: "EQUAL, QUALITY"
1865}
1866doors {
1867 name: "Orange Panels"
1868 type: LOCATION_ONLY
1869 panels { room: "Blue Smiley Annex" name: "ORANGE (1)" }
1870 panels { room: "Blue Smiley Annex" name: "ORANGE (2)" }
1871 location_room: "Blue Smiley Annex"
1872}
1873doors {
1772 name: "Red Room Entrance" 1874 name: "Red Room Entrance"
1773 type: STANDARD 1875 type: STANDARD
1774 receivers: "Components/Doors/Halls 2/halls_3" 1876 receivers: "Components/Doors/Halls 2/halls_3"
@@ -1923,7 +2025,7 @@ doors {
1923 panels { room: "Gray Color Backside" name: "LAST" } 2025 panels { room: "Gray Color Backside" name: "LAST" }
1924 panels { room: "Gray Color Backside" name: "RISE" } 2026 panels { room: "Gray Color Backside" name: "RISE" }
1925 location_room: "Gray Color Backside" 2027 location_room: "Gray Color Backside"
1926 location_name: "Light Green Hex" 2028 location_name: "Pale Green Hex"
1927} 2029}
1928doors { 2030doors {
1929 name: "South Castle Area Back Door" 2031 name: "South Castle Area Back Door"
@@ -2104,6 +2206,7 @@ doors {
2104 panels { room: "Computer Room" name: "PROCESSOR (2)" } 2206 panels { room: "Computer Room" name: "PROCESSOR (2)" }
2105 panels { room: "Computer Room" name: "MOUSE (2)" } 2207 panels { room: "Computer Room" name: "MOUSE (2)" }
2106 panels { room: "Computer Room" name: "KEYBOARD (2)" } 2208 panels { room: "Computer Room" name: "KEYBOARD (2)" }
2209 complete_at: 1
2107} 2210}
2108doors { 2211doors {
2109 name: "Book Room Entrance" 2212 name: "Book Room Entrance"
@@ -2299,3 +2402,74 @@ doors {
2299 location_room: "House" 2402 location_room: "House"
2300 location_name: "All Puzzles" 2403 location_name: "All Puzzles"
2301} 2404}
2405doors {
2406 name: "West Spire Panel"
2407 type: LOCATION_ONLY
2408 panels { room: "West Spire" name: "MISSING" }
2409 location_room: "West Spire"
2410 location_name: "MISSING"
2411}
2412doors {
2413 name: "Tree Panels"
2414 type: LOCATION_ONLY
2415 panels { room: "Red Color Door" name: "FIR" }
2416 panels { room: "Red Color Door" name: "OAK" }
2417 panels { room: "Red Color Door" name: "PINE" }
2418 panels { room: "Red Color Door" name: "ASH" }
2419 location_room: "Red Color Door"
2420 location_name: "ASH, FIR, OAK, PINE"
2421}
2422doors {
2423 name: "Teal Panel"
2424 type: LOCATION_ONLY
2425 panels { room: "Outside Book Room" name: "TEAL" }
2426 location_room: "Outside Book Room"
2427 location_name: "TEAL"
2428}
2429doors {
2430 name: "Direction Panels"
2431 type: LOCATION_ONLY
2432 panels { room: "Rainbow Color Doors" name: "DIRECTION (1)" }
2433 panels { room: "Rainbow Color Doors" name: "DIRECTION (2)" }
2434 panels { room: "Rainbow Color Doors" name: "DIRECTION (3)" }
2435 location_room: "Rainbow Color Doors"
2436}
2437doors {
2438 name: "Nursery Panels"
2439 type: LOCATION_ONLY
2440 panels { room: "Nursery" name: "Paintings" }
2441 panels { room: "Nursery" name: "?" }
2442 location_room: "Nursery"
2443 location_name: "Paintings, ?"
2444}
2445doors {
2446 name: "Near H Keyholder Panel"
2447 type: LOCATION_ONLY
2448 panels { room: "Outside House" name: "SILENCE" }
2449 location_room: "Outside House"
2450 location_name: "SILENCE"
2451}
2452doors {
2453 name: "Plum Panels"
2454 type: LOCATION_ONLY
2455 panels { room: "Outside Hedges" name: "PLUM (1)" }
2456 panels { room: "Outside Hedges" name: "PLUM (2)" }
2457 location_room: "Outside Hedges"
2458}
2459doors {
2460 name: "Yellow Smiley Annex Panels"
2461 type: LOCATION_ONLY
2462 panels { room: "Yellow Smiley Annex" name: "BELL" }
2463 panels { room: "Yellow Smiley Annex" name: "COW" }
2464 location_room: "Yellow Smiley Annex"
2465 location_name: "BELL, COW"
2466}
2467doors {
2468 name: "Farewell Little Lamb Panels"
2469 type: LOCATION_ONLY
2470 panels { room: "Purple Room South" name: "FAREWELL" }
2471 panels { room: "Purple Room South" name: "LITTLE" }
2472 panels { room: "Purple Room South" name: "LAMB" }
2473 location_room: "Purple Room South"
2474 location_name: "FAREWELL, LITTLE, LAMB"
2475}
diff --git a/data/maps/daedalus/metadata.txtpb b/data/maps/daedalus/metadata.txtpb index e0f5148..2f3da90 100644 --- a/data/maps/daedalus/metadata.txtpb +++ b/data/maps/daedalus/metadata.txtpb
@@ -1,4 +1,6 @@
1display_name: "Daedalus" 1display_name: "Daedalus"
2rte_room: "Starting Room"
3daedalus_only_mode: DAED_ONLY_ALLOW
2# These paintings can't be shuffled because they are behind panels. 4# These paintings can't be shuffled because they are behind panels.
3excluded_nodes: "Components/Paintings/Group3/mouse" 5excluded_nodes: "Components/Paintings/Group3/mouse"
4excluded_nodes: "Components/Paintings/Group3/bee" 6excluded_nodes: "Components/Paintings/Group3/bee"
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/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 ae9b3f1..b4782d2 100644 --- a/data/maps/daedalus/rooms/Wonderland.txtpb +++ b/data/maps/daedalus/rooms/Wonderland.txtpb
@@ -37,5 +37,8 @@ panels {
37} 37}
38ports { 38ports {
39 name: "WONDROUS" 39 name: "WONDROUS"
40 display_name: "Wonderland Worldport"
40 path: "Components/Warps/Worldports/worldport3" 41 path: "Components/Warps/Worldports/worldport3"
42 destination { x: -104 y: 0 z: -69 }
43 rotation: 180
41} 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..acaf461 --- /dev/null +++ b/data/maps/demo/metadata.txtpb
@@ -0,0 +1,8 @@
1display_name: "Demo"
2type: DEMO
3# No RTE because this is a secret map.
4
5# This painting is above a panel and can't be entered.
6excluded_nodes: "Meshes/owl"
7# The map's mastery is created at runtime.
8custom_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/metadata.txtpb b/data/maps/four_rooms/metadata.txtpb index ac4631d..d0d7133 100644 --- a/data/maps/four_rooms/metadata.txtpb +++ b/data/maps/four_rooms/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "Four Rooms" 1display_name: "Four Rooms"
2rte_room: "Synonyms Room"
3rte_trigger_pos { x: 20 y: 0 z: -2.5 }
4rte_trigger_scale { x: 6 y: 1 z: 8 }
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..ee105f7 --- /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: "Pillar 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..3a45887 --- /dev/null +++ b/data/maps/icarus/metadata.txtpb
@@ -0,0 +1,7 @@
1display_name: "Icarus"
2type: ICARUS
3rte_room: "Welcome Spine (Obverse)"
4rte_trigger_pos { x: 60 y: -3 z: 1 }
5rte_trigger_scale { x: 6 y: 1 z: 7 }
6# The map's mastery is created at runtime.
7custom_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..db1cf5f --- /dev/null +++ b/data/maps/the_advanced/metadata.txtpb
@@ -0,0 +1,7 @@
1display_name: "The Advanced"
2type: GIFT_MAP
3rte_room: "Main Area"
4rte_trigger_pos { x: 0 y: 0 z: 4 }
5rte_trigger_scale { x: 6 y: 1 z: 6 }
6# The map's mastery is created at runtime.
7custom_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/metadata.txtpb b/data/maps/the_ancient/metadata.txtpb index cf6bce3..c44473b 100644 --- a/data/maps/the_ancient/metadata.txtpb +++ b/data/maps/the_ancient/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Ancient" 1display_name: "The Ancient"
2rte_room: "Outside"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
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/metadata.txtpb b/data/maps/the_bearer/metadata.txtpb index 584c71e..4dc0ac5 100644 --- a/data/maps/the_bearer/metadata.txtpb +++ b/data/maps/the_bearer/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Bearer" 1display_name: "The Bearer"
2rte_room: "Entry"
3rte_trigger_pos { x: 0 y: 0 z: -4 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
diff --git a/data/maps/the_bearer/rooms/Back Area.txtpb b/data/maps/the_bearer/rooms/Back Area.txtpb index b1860de..2be4cb4 100644 --- a/data/maps/the_bearer/rooms/Back Area.txtpb +++ b/data/maps/the_bearer/rooms/Back Area.txtpb
@@ -8,6 +8,8 @@ panels {
8} 8}
9ports { 9ports {
10 name: "DAEDALUS" 10 name: "DAEDALUS"
11 display_name: "Dark Hallway"
11 path: "Components/Warps/worldport2" 12 path: "Components/Warps/worldport2"
12 orientation: "north" 13 destination { x: 10 y: 0 z: -84.5 }
14 rotation: 180
13} 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 index 97a07da..1b50ddd 100644 --- a/data/maps/the_bearer/rooms/Tree Entrance.txtpb +++ b/data/maps/the_bearer/rooms/Tree Entrance.txtpb
@@ -1,6 +1,8 @@
1name: "Tree Entrance" 1name: "Tree Entrance"
2ports { 2ports {
3 name: "TREE" 3 name: "TREE"
4 display_name: "Brown Hallway"
4 path: "Components/Warps/worldport3" 5 path: "Components/Warps/worldport3"
5 orientation: "north" 6 destination { x: -19 y: 0 z: -83.5 }
7 rotation: 180
6} 8}
diff --git a/data/maps/the_between/metadata.txtpb b/data/maps/the_between/metadata.txtpb index 33f96a1..00e5ce9 100644 --- a/data/maps/the_between/metadata.txtpb +++ b/data/maps/the_between/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Between" 1display_name: "The Between"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 13 y: 1 z: 13 }
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/metadata.txtpb b/data/maps/the_butterfly/metadata.txtpb index 5206dfe..d7a64ce 100644 --- a/data/maps/the_butterfly/metadata.txtpb +++ b/data/maps/the_butterfly/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Butterfly" 1display_name: "The Butterfly"
2rte_room: "Main Area"
3rte_trigger_pos { x: -15.5 y: 0 z: 15.5 }
4rte_trigger_scale { x: 8 y: 1 z: 8 }
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..1555cba --- /dev/null +++ b/data/maps/the_charismatic/metadata.txtpb
@@ -0,0 +1,7 @@
1display_name: "The Charismatic"
2type: GIFT_MAP
3rte_room: "Main Area"
4rte_trigger_pos { x: 0 y: 0 z: 0 }
5rte_trigger_scale { x: 3 y: 1 z: 10 }
6# The map's mastery is created at runtime.
7custom_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/metadata.txtpb b/data/maps/the_colorful/metadata.txtpb index 5e67428..0038cff 100644 --- a/data/maps/the_colorful/metadata.txtpb +++ b/data/maps/the_colorful/metadata.txtpb
@@ -1,3 +1,6 @@
1display_name: "The Colorful" 1display_name: "The Colorful"
2rte_room: "White Room"
3rte_trigger_pos { x: 0 y: 0 z: 10 }
4rte_trigger_scale { x: 4 y: 1 z: 4 }
2# This has something to do with the FISH/FISHES proxy. 5# This has something to do with the FISH/FISHES proxy.
3excluded_nodes: "Components/panel_fake" 6excluded_nodes: "Components/panel_fake"
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..da3919d 100644 --- a/data/maps/the_congruent/metadata.txtpb +++ b/data/maps/the_congruent/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Congruent" 1display_name: "The Congruent"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "Main Area"
7 name: "DARKROOM"
8}
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..4863264 --- /dev/null +++ b/data/maps/the_crystalline/metadata.txtpb
@@ -0,0 +1,7 @@
1display_name: "The Crystalline"
2type: GIFT_MAP
3rte_room: "Main Area"
4rte_trigger_pos { x: 0 y: 0 z: 0 }
5rte_trigger_scale { x: 10 y: 1 z: 10 }
6# The map's mastery is created at runtime.
7custom_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 1b7ad05..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 }
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/metadata.txtpb b/data/maps/the_darkroom/metadata.txtpb index e4a1290..7b5d539 100644 --- a/data/maps/the_darkroom/metadata.txtpb +++ b/data/maps/the_darkroom/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Darkroom" 1display_name: "The Darkroom"
2rte_room: "First Room"
3rte_trigger_pos { x: 0 y: 0 z: 1 }
4rte_trigger_scale { x: 14 y: 1 z: 13 }
diff --git a/data/maps/the_darkroom/rooms/Congruent Entrance.txtpb b/data/maps/the_darkroom/rooms/Congruent Entrance.txtpb index 7ea1286..e6600a2 100644 --- a/data/maps/the_darkroom/rooms/Congruent Entrance.txtpb +++ b/data/maps/the_darkroom/rooms/Congruent Entrance.txtpb
@@ -2,6 +2,8 @@ name: "Congruent Entrance"
2panel_display_name: "Second Room" 2panel_display_name: "Second Room"
3ports { 3ports {
4 name: "CONGRUENT" 4 name: "CONGRUENT"
5 display_name: "Second Room Gray Hallway"
5 path: "Components/Warps/worldport7" 6 path: "Components/Warps/worldport7"
6 orientation: "east" 7 destination { x: 51.5 y: 0 z: 29 }
8 rotation: 90
7} 9}
diff --git a/data/maps/the_darkroom/rooms/Cyan Hallway.txtpb b/data/maps/the_darkroom/rooms/Cyan Hallway.txtpb index 308efb1..bce0e5b 100644 --- a/data/maps/the_darkroom/rooms/Cyan Hallway.txtpb +++ b/data/maps/the_darkroom/rooms/Cyan Hallway.txtpb
@@ -2,6 +2,8 @@ name: "Cyan Hallway"
2panel_display_name: "First Room" 2panel_display_name: "First Room"
3ports { 3ports {
4 name: "COLORFUL" 4 name: "COLORFUL"
5 display_name: "First Room Cyan Hallway"
5 path: "Components/Warps/worldport8" 6 path: "Components/Warps/worldport8"
6 orientation: "north" 7 destination { x: 20 y: 0 z: -12 }
8 rotation: 180
7} 9}
diff --git a/data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb b/data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb index 9d25108..79ca839 100644 --- a/data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb +++ b/data/maps/the_darkroom/rooms/Double Sided Entrance.txtpb
@@ -2,6 +2,8 @@ name: "Double Sided Entrance"
2panel_display_name: "First Room" 2panel_display_name: "First Room"
3ports { 3ports {
4 name: "DOUBLESIDED" 4 name: "DOUBLESIDED"
5 display_name: "First Room White Hallway"
5 path: "Components/Warps/worldport6" 6 path: "Components/Warps/worldport6"
6 orientation: "east" 7 destination { x: 15 y: 0 z: 23 }
8 rotation: 90
7} 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 c635757..1113435 100644 --- a/data/maps/the_darkroom/rooms/First Room.txtpb +++ b/data/maps/the_darkroom/rooms/First Room.txtpb
@@ -33,12 +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} 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 a3964ea..2219895 100644 --- a/data/maps/the_darkroom/rooms/Second Room.txtpb +++ b/data/maps/the_darkroom/rooms/Second Room.txtpb
@@ -38,12 +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} 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/metadata.txtpb b/data/maps/the_digital/metadata.txtpb index e505dcf..001fe55 100644 --- a/data/maps/the_digital/metadata.txtpb +++ b/data/maps/the_digital/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Digital" 1display_name: "The Digital"
2rte_room: "Main Area"
3rte_trigger_pos { x: -9 y: 0 z: -5 }
4rte_trigger_scale { x: 15 y: 1 z: 15 }
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_door/metadata.txtpb b/data/maps/the_door/metadata.txtpb index 85dc340..a253d9f 100644 --- a/data/maps/the_door/metadata.txtpb +++ b/data/maps/the_door/metadata.txtpb
@@ -1 +1,2 @@
1display_name: "The Door" 1display_name: "The Door"
2# No RTE because it would make The Wondrous pointless.
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..5d60122 100644 --- a/data/maps/the_double_sided/metadata.txtpb +++ b/data/maps/the_double_sided/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Double Sided" 1display_name: "The Double Sided"
2rte_room: "Start"
3rte_trigger_pos { x: 0 y: 0 z: -4 }
4rte_trigger_scale { x: 4 y: 1 z: 4 }
5worldport_entrance {
6 room: "Start"
7 name: "DARKROOM"
8}
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 9813f85..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"
@@ -214,3 +235,13 @@ connections {
214 to_room: "Literate Entrance" 235 to_room: "Literate Entrance"
215 door { name: "Literate Entrance" } 236 door { name: "Literate Entrance" }
216} 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 466f5ce..d2e66a7 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,6 +152,31 @@ 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"
@@ -161,6 +207,7 @@ doors {
161doors { 207doors {
162 name: "Control Center White Door" 208 name: "Control Center White Door"
163 type: CONTROL_CENTER_COLOR 209 type: CONTROL_CENTER_COLOR
210 latch: true
164 receivers: "Components/Doors/back_left_7" 211 receivers: "Components/Doors/back_left_7"
165 control_center_color: "white" 212 control_center_color: "white"
166} 213}
@@ -211,7 +258,7 @@ doors {
211 name: "Liberated Entrance" 258 name: "Liberated Entrance"
212 type: STANDARD 259 type: STANDARD
213 receivers: "Components/Doors/Entry/entry_proxied_10" 260 receivers: "Components/Doors/Entry/entry_proxied_10"
214 panels { room: "Flipped Pyramid Area" name: "TURN (1)" } 261 panels { room: "Liberated Entrance Panel" name: "TURN (1)" }
215 location_room: "Flipped Pyramid Area" 262 location_room: "Flipped Pyramid Area"
216} 263}
217doors { 264doors {
@@ -224,7 +271,7 @@ doors {
224 name: "Literate Entrance" 271 name: "Literate Entrance"
225 type: STANDARD 272 type: STANDARD
226 receivers: "Components/Doors/Entry/entry_proxied_11" 273 receivers: "Components/Doors/Entry/entry_proxied_11"
227 panels { room: "Flipped Pyramid Area" name: "TURN (2)" } 274 panels { room: "Literate Entrance Panel" name: "TURN (2)" }
228 location_room: "Flipped Pyramid Area" 275 location_room: "Flipped Pyramid Area"
229} 276}
230doors { 277doors {
@@ -303,7 +350,6 @@ doors {
303doors { 350doors {
304 name: "Red Room Painting" 351 name: "Red Room Painting"
305 type: STANDARD 352 type: STANDARD
306 #move_paintings { room: "Right Eye" name: "PSYCHIC" }
307 receivers: "Components/Paintings/psychic/teleportListener" 353 receivers: "Components/Paintings/psychic/teleportListener"
308 panels { room: "Right Eye" name: "FAINT" } 354 panels { room: "Right Eye" name: "FAINT" }
309 location_room: "Right Eye" 355 location_room: "Right Eye"
@@ -311,8 +357,49 @@ doors {
311doors { 357doors {
312 name: "Third Eye Painting" 358 name: "Third Eye Painting"
313 type: LOCATION_ONLY 359 type: LOCATION_ONLY
314 # move_paintings { room: "Eye Room" name: "GALLERY" }
315 # TODO: ummmm 360 # TODO: ummmm
316 panels { room: "Eye Room" name: "I" } 361 panels { room: "Eye Room" name: "I" }
317 location_room: "Eye Room" 362 location_room: "Eye Room"
318} \ 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, WREATH"
405}
diff --git a/data/maps/the_entry/metadata.txtpb b/data/maps/the_entry/metadata.txtpb index 0eeb29a..d20137c 100644 --- a/data/maps/the_entry/metadata.txtpb +++ b/data/maps/the_entry/metadata.txtpb
@@ -1,4 +1,6 @@
1display_name: "The Entry" 1display_name: "The Entry"
2# No RTE because this map is always an RTE.
3
2# This is a debug warp to The Ancient and as far as I can tell there is no way 4# This is a debug warp to The Ancient and as far as I can tell there is no way
3# to access it. 5# to access it.
4excluded_nodes: "Components/Warps/worldport-test" 6excluded_nodes: "Components/Warps/worldport-test"
@@ -11,3 +13,13 @@ excluded_nodes: "Panels/Back Left/backleft_4_proxied_1"
11excluded_nodes: "Panels/Back Left/backleft_4_proxied_2" 13excluded_nodes: "Panels/Back Left/backleft_4_proxied_2"
12# This is a proxy related to the first panel and it doesn't seem useful. 14# This is a proxy related to the first panel and it doesn't seem useful.
13excluded_nodes: "Panels/Entry/entry_proxied_fake" 15excluded_nodes: "Panels/Entry/entry_proxied_fake"
16# The gift map entrance is created by the mod.
17custom_nodes: "Components/GiftMapEntrance/GongusPanel"
18custom_nodes: "Components/GiftMapEntrance/HatkirbyPanel"
19custom_nodes: "Components/GiftMapEntrance/IcelyPanel"
20custom_nodes: "Components/GiftMapEntrance/KirbyPanel"
21custom_nodes: "Components/GiftMapEntrance/KiwiPanel"
22custom_nodes: "Components/GiftMapEntrance/Panel"
23custom_nodes: "Components/GiftMapEntrance/QPanel"
24custom_nodes: "Components/GiftMapEntrance/SouveyPanel"
25custom_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/Four Rooms Entrance.txtpb b/data/maps/the_entry/rooms/Four Rooms Entrance.txtpb index 689d23e..d4650f0 100644 --- a/data/maps/the_entry/rooms/Four Rooms Entrance.txtpb +++ b/data/maps/the_entry/rooms/Four Rooms Entrance.txtpb
@@ -1,7 +1,9 @@
1name: "Four Rooms Entrance" 1name: "Four Rooms Entrance"
2ports { 2ports {
3 name: "FOUR" 3 name: "FOUR"
4 display_name: "Flipped Second Room Right Worldport"
4 path: "Components/Warps/worldport9" 5 path: "Components/Warps/worldport9"
5 orientation: "south" 6 destination { x: -41 y: 6 z: -17.5 }
6 gravity: Y_PLUS 7 rotation: 0
8 # This isn't actually Y_PLUS gravity! A nearby warp sneakily flips you.
7} 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 index f0176a0..56cc597 100644 --- a/data/maps/the_entry/rooms/Liberated Entrance.txtpb +++ b/data/maps/the_entry/rooms/Liberated Entrance.txtpb
@@ -1,6 +1,8 @@
1name: "Liberated Entrance" 1name: "Liberated Entrance"
2ports { 2ports {
3 name: "BLUE" 3 name: "BLUE"
4 display_name: "Pyramid Area Blue Worldport"
4 path: "worldport8" 5 path: "worldport8"
5 orientation: "west" 6 destination { x: 18 y: 0 z: 55 }
7 rotation: 270
6} 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/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 index 4ec402f..b86ac80 100644 --- a/data/maps/the_entry/rooms/Literate Entrance.txtpb +++ b/data/maps/the_entry/rooms/Literate Entrance.txtpb
@@ -1,6 +1,8 @@
1name: "Literate Entrance" 1name: "Literate Entrance"
2ports { 2ports {
3 name: "BROWN" 3 name: "BROWN"
4 display_name: "Pyramid Area Brown Worldport"
4 path: "worldport9" 5 path: "worldport9"
5 orientation: "east" 6 destination { x: 39 y: 0 z: 55 }
7 rotation: 90
6} 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 67aa6de..4a99efa 100644 --- a/data/maps/the_entry/rooms/Shop Entrance.txtpb +++ b/data/maps/the_entry/rooms/Shop Entrance.txtpb
@@ -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 8e8373b..d7ff7eb 100644 --- a/data/maps/the_entry/rooms/Starting Room.txtpb +++ b/data/maps/the_entry/rooms/Starting Room.txtpb
@@ -46,6 +46,25 @@ panels {
46 clue: "than" 46 clue: "than"
47 answer: "than" 47 answer: "than"
48} 48}
49panels {
50 name: "Gift Maps"
51 exclude_from_panelsanity: true
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}
49letters { 68letters {
50 key: "h" 69 key: "h"
51 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/metadata.txtpb b/data/maps/the_extravagant/metadata.txtpb index 0a35c90..f4604ab 100644 --- a/data/maps/the_extravagant/metadata.txtpb +++ b/data/maps/the_extravagant/metadata.txtpb
@@ -1,3 +1,6 @@
1display_name: "The Extravagant" 1display_name: "The Extravagant"
2rte_room: "Y Minus First Floor"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
2# This appears to be completely inaccessible. 5# This appears to be completely inaccessible.
3excluded_nodes: "Components/Warps/worldport" 6excluded_nodes: "Components/Warps/worldport"
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..75d6ff6 --- /dev/null +++ b/data/maps/the_fuzzy/metadata.txtpb
@@ -0,0 +1,7 @@
1display_name: "The Fuzzy"
2type: GIFT_MAP
3rte_room: "Main Area"
4rte_trigger_pos { x: 0 y: 0 z: 4 }
5rte_trigger_scale { x: 6 y: 1 z: 6 }
6# The map's mastery is created at runtime.
7custom_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 adbc766..e87e807 100644 --- a/data/maps/the_gallery/doors.txtpb +++ b/data/maps/the_gallery/doors.txtpb
@@ -3,7 +3,7 @@ doors {
3 name: "Darkroom Painting" 3 name: "Darkroom Painting"
4 type: GALLERY_PAINTING 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" }
@@ -29,14 +29,14 @@ doors {
29 name: "Butterfly Painting" 29 name: "Butterfly Painting"
30 type: GALLERY_PAINTING 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: GALLERY_PAINTING 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" }
@@ -72,14 +72,14 @@ doors {
72 name: "Entry Painting" 72 name: "Entry Painting"
73 type: GALLERY_PAINTING 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: GALLERY_PAINTING 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" }
@@ -107,7 +107,7 @@ doors {
107 name: "Tree Painting" 107 name: "Tree Painting"
108 type: GALLERY_PAINTING 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)" }
@@ -144,35 +144,41 @@ doors {
144 name: "Unyielding Painting" 144 name: "Unyielding Painting"
145 type: GALLERY_PAINTING 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: "Cyan Doors"
152 type: EVENT
153 receivers: "Components/Listeners/Hint Room/unlockReaderListenerDoubles"
154 double_letters: true
155}
156doors {
151 name: "Graveyard Painting" 157 name: "Graveyard Painting"
152 type: GALLERY_PAINTING 158 type: GALLERY_PAINTING
153 #move_paintings { room: "Main Area" name: "GRAVEYARD" } 159 #move_paintings { room: "Main Area" name: "GRAVEYARD" }
154 receivers: "Components/Paintings/Endings/grave/teleportListener" 160 receivers: "Components/Listeners/Endings/unlockReaderListenerGraveyard"
155 rooms { map: "the_graveyard" name: "Outside" } 161 rooms { map: "the_graveyard" name: "Outside" }
156} 162}
157doors { 163doors {
158 name: "Control Center Painting" 164 name: "Control Center Painting"
159 type: GALLERY_PAINTING 165 type: GALLERY_PAINTING
160 #move_paintings { room: "Main Area" name: "CC" } 166 #move_paintings { room: "Main Area" name: "CC" }
161 receivers: "Components/Paintings/Endings/desert/teleportListener" 167 receivers: "Components/Listeners/Endings/unlockReaderListenerDesert"
162 rooms { map: "the_impressive" name: "M2 Room" } 168 rooms { map: "the_impressive" name: "M2 Room" }
163} 169}
164doors { 170doors {
165 name: "Tower Painting" 171 name: "Tower Painting"
166 type: GALLERY_PAINTING 172 type: GALLERY_PAINTING
167 #move_paintings { room: "Main Area" name: "TOWER" } 173 #move_paintings { room: "Main Area" name: "TOWER" }
168 receivers: "Components/Paintings/Endings/red/teleportListener" 174 receivers: "Components/Listeners/Endings/unlockReaderListenerTower"
169 rooms { map: "the_tower" name: "First Floor" } 175 rooms { map: "the_tower" name: "First Floor" }
170} 176}
171doors { 177doors {
172 name: "Wondrous Painting" 178 name: "Wondrous Painting"
173 type: GALLERY_PAINTING 179 type: GALLERY_PAINTING
174 #move_paintings { room: "Main Area" name: "WONDROUS" } 180 #move_paintings { room: "Main Area" name: "WONDROUS" }
175 receivers: "Components/Paintings/Endings/window/teleportListener" 181 receivers: "Components/Listeners/Endings/unlockReaderListenerWonderland"
176 panels { map: "the_wondrous" room: "Entry" name: "WONDER" } 182 panels { map: "the_wondrous" room: "Entry" name: "WONDER" }
177 panels { map: "the_wondrous" room: "Regular" name: "SHRINK" } 183 panels { map: "the_wondrous" room: "Regular" name: "SHRINK" }
178 panels { map: "the_wondrous" room: "Huge" name: "SHRINK" } 184 panels { map: "the_wondrous" room: "Huge" name: "SHRINK" }
@@ -189,42 +195,42 @@ doors {
189 name: "Rainbow Painting" 195 name: "Rainbow Painting"
190 type: GALLERY_PAINTING 196 type: GALLERY_PAINTING
191 #move_paintings { room: "Main Area" name: "RAINBOW" } 197 #move_paintings { room: "Main Area" name: "RAINBOW" }
192 receivers: "Components/Paintings/Endings/rainbow/teleportListener" 198 receivers: "Components/Listeners/Endings/unlockReaderListenerRainbow"
193 rooms { map: "daedalus" name: "Rainbow Start" } 199 rooms { map: "daedalus" name: "Rainbow Start" }
194} 200}
195doors { 201doors {
196 name: "Words Painting" 202 name: "Words Painting"
197 type: GALLERY_PAINTING 203 type: GALLERY_PAINTING
198 #move_paintings { room: "Main Area" name: "WORDS" } 204 #move_paintings { room: "Main Area" name: "WORDS" }
199 receivers: "Components/Paintings/Endings/words/teleportListener" 205 receivers: "Components/Listeners/Endings/unlockReaderListenerWords"
200 rooms { map: "the_words" name: "Main Area" } 206 rooms { map: "the_words" name: "Main Area" }
201} 207}
202doors { 208doors {
203 name: "Colorful Painting" 209 name: "Colorful Painting"
204 type: GALLERY_PAINTING 210 type: GALLERY_PAINTING
205 #move_paintings { room: "Main Area" name: "COLORFUL" } 211 #move_paintings { room: "Main Area" name: "COLORFUL" }
206 receivers: "Components/Paintings/Endings/colorful/teleportListener" 212 receivers: "Components/Listeners/Endings/unlockReaderListenerColorful"
207 rooms { map: "the_colorful" name: "White Room" } 213 rooms { map: "the_colorful" name: "White Room" }
208} 214}
209doors { 215doors {
210 name: "Castle Painting" 216 name: "Castle Painting"
211 type: GALLERY_PAINTING 217 type: GALLERY_PAINTING
212 #move_paintings { room: "Main Area" name: "CASTLE" } 218 #move_paintings { room: "Main Area" name: "CASTLE" }
213 receivers: "Components/Paintings/Endings/castle/teleportListener" 219 receivers: "Components/Listeners/Endings/unlockReaderListenerCastle"
214 rooms { map: "daedalus" name: "Castle" } 220 rooms { map: "daedalus" name: "Castle" }
215} 221}
216doors { 222doors {
217 name: "Sun Temple Painting" 223 name: "Sun Temple Painting"
218 type: GALLERY_PAINTING 224 type: GALLERY_PAINTING
219 #move_paintings { room: "Main Area" name: "SUNTEMPLE" } 225 #move_paintings { room: "Main Area" name: "SUNTEMPLE" }
220 receivers: "Components/Paintings/Endings/temple/teleportListener" 226 receivers: "Components/Listeners/Endings/unlockReaderListenerTemple"
221 rooms { map: "the_sun_temple" name: "Entrance" } 227 rooms { map: "the_sun_temple" name: "Entrance" }
222} 228}
223doors { 229doors {
224 name: "Ancient Painting" 230 name: "Ancient Painting"
225 type: GALLERY_PAINTING 231 type: GALLERY_PAINTING
226 #move_paintings { room: "Main Area" name: "ANCIENT" } 232 #move_paintings { room: "Main Area" name: "ANCIENT" }
227 receivers: "Components/Paintings/Endings/cubes/teleportListener" 233 receivers: "Components/Listeners/Endings/unlockReaderListenerQuartz"
228 rooms { map: "the_ancient" name: "Outside" } 234 rooms { map: "the_ancient" name: "Outside" }
229} 235}
230doors { 236doors {
@@ -245,6 +251,7 @@ doors {
245 doors { name: "Wise Painting" } 251 doors { name: "Wise Painting" }
246 doors { name: "Tree Painting" } 252 doors { name: "Tree Painting" }
247 doors { name: "Unyielding Painting" } 253 doors { name: "Unyielding Painting" }
254 doors { name: "Cyan Doors" }
248 doors { name: "Graveyard Painting" } 255 doors { name: "Graveyard Painting" }
249 doors { name: "Control Center Painting" } 256 doors { name: "Control Center Painting" }
250 doors { name: "Tower Painting" } 257 doors { name: "Tower Painting" }
@@ -255,7 +262,7 @@ doors {
255 doors { name: "Castle Painting" } 262 doors { name: "Castle Painting" }
256 doors { name: "Sun Temple Painting" } 263 doors { name: "Sun Temple Painting" }
257 doors { name: "Ancient Painting" } 264 doors { name: "Ancient Painting" }
258 doors { name: "Gallery Extension" } 265 panels { room: "Daedalus Extension" name: "WHERE" }
259} 266}
260doors { 267doors {
261 name: "Ending Door" 268 name: "Ending Door"
diff --git a/data/maps/the_gallery/metadata.txtpb b/data/maps/the_gallery/metadata.txtpb index 41ec36e..a1bbe25 100644 --- a/data/maps/the_gallery/metadata.txtpb +++ b/data/maps/the_gallery/metadata.txtpb
@@ -1,4 +1,6 @@
1display_name: "The Gallery" 1display_name: "The Gallery"
2rte_room: "Main Area"
3daedalus_only_mode: DAED_ONLY_PARTIAL
2# These are the eyes in the foyer, and aren't normal paintings. 4# These are the eyes in the foyer, and aren't normal paintings.
3excluded_nodes: "Components/Paintings/Starting/eye" 5excluded_nodes: "Components/Paintings/Starting/eye"
4excluded_nodes: "Components/Paintings/Starting/eye2" 6excluded_nodes: "Components/Paintings/Starting/eye2"
diff --git a/data/maps/the_gallery/rooms/Daedalus Extension.txtpb b/data/maps/the_gallery/rooms/Daedalus Extension.txtpb index 51d2be5..c3a72c2 100644 --- a/data/maps/the_gallery/rooms/Daedalus Extension.txtpb +++ b/data/maps/the_gallery/rooms/Daedalus Extension.txtpb
@@ -1,4 +1,5 @@
1name: "Daedalus Extension" 1name: "Daedalus Extension"
2daedalus_only_allow: true
2panels { 3panels {
3 name: "WHERE" 4 name: "WHERE"
4 path: "Panels/entry_1" 5 path: "Panels/entry_1"
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_gold/metadata.txtpb b/data/maps/the_gold/metadata.txtpb index fef3e34..7c20bae 100644 --- a/data/maps/the_gold/metadata.txtpb +++ b/data/maps/the_gold/metadata.txtpb
@@ -1 +1,3 @@
1display_name: "The Gold" 1display_name: "The Gold"
2daedalus_only_mode: DAED_ONLY_ALLOW
3# No RTE because it would trivialize Gold Ending.
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_graveyard/metadata.txtpb b/data/maps/the_graveyard/metadata.txtpb index 0bac222..d97eb88 100644 --- a/data/maps/the_graveyard/metadata.txtpb +++ b/data/maps/the_graveyard/metadata.txtpb
@@ -1,4 +1,7 @@
1display_name: "The Graveyard" 1display_name: "The Graveyard"
2rte_room: "Outside"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 8 y: 1 z: 8 }
2# These really shouldn't be shuffled because it would make Black Ending trivial. 5# These really shouldn't be shuffled because it would make Black Ending trivial.
3excluded_nodes: "Components/Paintings/grave" 6excluded_nodes: "Components/Paintings/grave"
4excluded_nodes: "Components/Paintings/grave2" 7excluded_nodes: "Components/Paintings/grave2"
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 abbbc11..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,18 +63,21 @@ 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
80 latch: true
69 receivers: "Components/Doors/entry_18" 81 receivers: "Components/Doors/entry_18"
70 control_center_color: "red" 82 control_center_color: "red"
71} 83}
@@ -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"
@@ -513,3 +532,99 @@ doors {
513 type: EVENT 532 type: EVENT
514 panels { room: "West Side" name: "CLUE" } 533 panels { room: "West Side" name: "CLUE" }
515} 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/metadata.txtpb b/data/maps/the_great/metadata.txtpb index b01faf7..bbea189 100644 --- a/data/maps/the_great/metadata.txtpb +++ b/data/maps/the_great/metadata.txtpb
@@ -1,4 +1,7 @@
1display_name: "The Great" 1display_name: "The Great"
2rte_room: "Main Area"
3rte_trigger_pos { x: 24 y: 0 z: 1 }
4rte_trigger_scale { x: 35 y: 1 z: 14 }
2# This can't be shuffled because it is tilted. 5# This can't be shuffled because it is tilted.
3excluded_nodes: "Components/Paintings/u" 6excluded_nodes: "Components/Paintings/u"
4# This can't be shuffled because it is on the ground. 7# This can't be shuffled because it is on the ground.
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 8279e16..9f098ee 100644 --- a/data/maps/the_great/rooms/West Side.txtpb +++ b/data/maps/the_great/rooms/West Side.txtpb
@@ -63,18 +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
79 required_door { name: "Partial Entrance" } 85 required_door { name: "Partial Entrance" }
80} 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/metadata.txtpb b/data/maps/the_hinterlands/metadata.txtpb index dd1e627..8148c2a 100644 --- a/data/maps/the_hinterlands/metadata.txtpb +++ b/data/maps/the_hinterlands/metadata.txtpb
@@ -1,4 +1,6 @@
1display_name: "The Hinterlands" 1display_name: "The Hinterlands"
2# No RTE because the map is not randomized.
3
2# I'm not currently planning on shuffling anything in here. 4# I'm not currently planning on shuffling anything in here.
3excluded_nodes: "Components/Paintings/C" 5excluded_nodes: "Components/Paintings/C"
4excluded_nodes: "Components/Paintings/E" 6excluded_nodes: "Components/Paintings/E"
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/metadata.txtpb b/data/maps/the_hive/metadata.txtpb index 8f3894c..d89d2ff 100644 --- a/data/maps/the_hive/metadata.txtpb +++ b/data/maps/the_hive/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Hive" 1display_name: "The Hive"
2rte_room: "Main Area"
3rte_trigger_pos { x: -26 y: 0 z: -19 }
4rte_trigger_scale { x: 60 y: 10 z: 60 }
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/metadata.txtpb b/data/maps/the_impressive/metadata.txtpb index 63929cb..d05595d 100644 --- a/data/maps/the_impressive/metadata.txtpb +++ b/data/maps/the_impressive/metadata.txtpb
@@ -1,4 +1,7 @@
1display_name: "The Impressive" 1display_name: "The Impressive"
2rte_room: "Lobby"
3rte_trigger_pos { x: 0 y: 0 z: 5 }
4rte_trigger_scale { x: 6 y: 1 z: 10 }
2# These are apparently little eyes on the Green Eye panel pedestals? I don't 5# These are apparently little eyes on the Green Eye panel pedestals? I don't
3# think they're ever visible in gameplay. 6# think they're ever visible in gameplay.
4excluded_nodes: "Meshes/eye" 7excluded_nodes: "Meshes/eye"
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/metadata.txtpb b/data/maps/the_invisible/metadata.txtpb index 967bf19..e995085 100644 --- a/data/maps/the_invisible/metadata.txtpb +++ b/data/maps/the_invisible/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Invisible" 1display_name: "The Invisible"
2rte_room: "Entrance"
3rte_trigger_pos { x: 0 y: 0 z: -57 }
4rte_trigger_scale { x: 12 y: 1 z: 12 }
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..66fb7d2 100644 --- a/data/maps/the_jubilant/metadata.txtpb +++ b/data/maps/the_jubilant/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Jubilant" 1display_name: "The Jubilant"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 2 }
4rte_trigger_scale { x: 8 y: 1 z: 8 }
5worldport_entrance {
6 room: "Main Area"
7 name: "GREAT"
8}
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..93a9eef 100644 --- a/data/maps/the_keen/metadata.txtpb +++ b/data/maps/the_keen/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Keen" 1display_name: "The Keen"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "Main Area"
7 name: "GREAT"
8}
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..bdff786 100644 --- a/data/maps/the_liberated/metadata.txtpb +++ b/data/maps/the_liberated/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Liberated" 1display_name: "The Liberated"
2rte_room: "Puzzle Room"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "Puzzle Room"
7 name: "ENTRY"
8}
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..03930f8 100644 --- a/data/maps/the_linear/metadata.txtpb +++ b/data/maps/the_linear/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Linear" 1display_name: "The Linear"
2rte_room: "Room"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "Room"
7 name: "GREAT"
8}
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..0beb352 100644 --- a/data/maps/the_lionized/metadata.txtpb +++ b/data/maps/the_lionized/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Lionized" 1display_name: "The Lionized"
2rte_room: "Puzzle Room"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "Puzzle Room"
7 name: "ENTRY"
8}
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..ce4db7a 100644 --- a/data/maps/the_literate/metadata.txtpb +++ b/data/maps/the_literate/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Literate" 1display_name: "The Literate"
2rte_room: "Puzzle Room"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "Puzzle Room"
7 name: "ENTRY"
8}
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..101a265 100644 --- a/data/maps/the_lively/metadata.txtpb +++ b/data/maps/the_lively/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Lively" 1display_name: "The Lively"
2rte_room: "Puzzle Room"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "Puzzle Room"
7 name: "BETWEEN"
8}
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 cd29766..300524b 100644 --- a/data/maps/the_nuanced/doors.txtpb +++ b/data/maps/the_nuanced/doors.txtpb
@@ -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..9d2e044 100644 --- a/data/maps/the_nuanced/metadata.txtpb +++ b/data/maps/the_nuanced/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Nuanced" 1display_name: "The Nuanced"
2rte_room: "Main Room"
3rte_trigger_pos { x: 0 y: 0 z: 9 }
4rte_trigger_scale { x: 6 y: 1 z: 15 }
5worldport_entrance {
6 room: "Main Room"
7 name: "UNYIELDING"
8}
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/metadata.txtpb b/data/maps/the_orb/metadata.txtpb index 0b25353..2b5c43f 100644 --- a/data/maps/the_orb/metadata.txtpb +++ b/data/maps/the_orb/metadata.txtpb
@@ -1,4 +1,7 @@
1display_name: "The Orb" 1display_name: "The Orb"
2rte_room: "Main Area"
3rte_trigger_pos { x: 34 y: 0 z: 39 }
4rte_trigger_scale { x: 4 y: 1 z: 8 }
2# These are inaccessible, and were probably just copy pasted from the other 5# These are inaccessible, and were probably just copy pasted from the other
3# rooms. 6# rooms.
4excluded_nodes: "Components/Warps/worldport2" 7excluded_nodes: "Components/Warps/worldport2"
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 9254c2a..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"
@@ -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/metadata.txtpb b/data/maps/the_owl/metadata.txtpb index a2004f8..a9c4a6c 100644 --- a/data/maps/the_owl/metadata.txtpb +++ b/data/maps/the_owl/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Owl" 1display_name: "The Owl"
2rte_room: "R2C2 Bottom"
3rte_trigger_pos { x: 0 y: 0 z: 8 }
4rte_trigger_scale { x: 4 y: 1 z: 3 }
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/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/metadata.txtpb b/data/maps/the_parthenon/metadata.txtpb index 8696c33..f3559ea 100644 --- a/data/maps/the_parthenon/metadata.txtpb +++ b/data/maps/the_parthenon/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Parthenon" 1display_name: "The Parthenon"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: -15 }
4rte_trigger_scale { x: 18 y: 1 z: 30 }
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/metadata.txtpb b/data/maps/the_partial/metadata.txtpb index 48e9f42..dd090f5 100644 --- a/data/maps/the_partial/metadata.txtpb +++ b/data/maps/the_partial/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Partial" 1display_name: "The Partial"
2rte_room: "Obverse Side"
3rte_trigger_pos { x: 0 y: 0 z: 5 }
4rte_trigger_scale { x: 13 y: 1 z: 16 }
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..e67b84a 100644 --- a/data/maps/the_perceptive/metadata.txtpb +++ b/data/maps/the_perceptive/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Perceptive" 1display_name: "The Perceptive"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 30 y: 1 z: 20 }
5worldport_entrance {
6 room: "Main Area"
7 name: "CC"
8}
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/doors.txtpb b/data/maps/the_plaza/doors.txtpb index d95273c..fef8954 100644 --- a/data/maps/the_plaza/doors.txtpb +++ b/data/maps/the_plaza/doors.txtpb
@@ -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/metadata.txtpb b/data/maps/the_plaza/metadata.txtpb index 262fe99..6ff2e83 100644 --- a/data/maps/the_plaza/metadata.txtpb +++ b/data/maps/the_plaza/metadata.txtpb
@@ -1 +1,2 @@
1display_name: "The Plaza" 1display_name: "The Plaza"
2rte_room: "Main Area"
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..5cd177f 100644 --- a/data/maps/the_quiet/metadata.txtpb +++ b/data/maps/the_quiet/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Quiet" 1display_name: "The Quiet"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 2 }
4rte_trigger_scale { x: 8 y: 1 z: 8 }
5worldport_entrance {
6 room: "Main Area"
7 name: "DAEDALUS"
8}
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_relentless/metadata.txtpb b/data/maps/the_relentless/metadata.txtpb index 9515145..313420d 100644 --- a/data/maps/the_relentless/metadata.txtpb +++ b/data/maps/the_relentless/metadata.txtpb
@@ -1 +1,2 @@
1display_name: "The Relentless" 1display_name: "The Relentless"
2# No RTE in order to preserve the gimmick of the map.
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 d964928..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"
@@ -200,3 +209,35 @@ doors {
200 senders: "Components/Collectables/anticollectable" 209 senders: "Components/Collectables/anticollectable"
201 location_room: "Anti Room" 210 location_room: "Anti Room"
202} 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 76a0f50..a54d8b0 100644 --- a/data/maps/the_repetitive/metadata.txtpb +++ b/data/maps/the_repetitive/metadata.txtpb
@@ -1,4 +1,7 @@
1display_name: "The Repetitive" 1display_name: "The Repetitive"
2rte_room: "Main Room"
3rte_trigger_pos { x: 0 y: 0 z: 35.5 }
4rte_trigger_scale { x: 13 y: 1 z: 14 }
2# These paintings are directly above/behind panels and thus can't be entered. 5# These paintings are directly above/behind panels and thus can't be entered.
3excluded_nodes: "Meshes/eyeRed3" 6excluded_nodes: "Meshes/eyeRed3"
4excluded_nodes: "Meshes/eyeRed4" 7excluded_nodes: "Meshes/eyeRed4"
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/metadata.txtpb b/data/maps/the_revitalized/metadata.txtpb index 7c4bf46..7046a17 100644 --- a/data/maps/the_revitalized/metadata.txtpb +++ b/data/maps/the_revitalized/metadata.txtpb
@@ -1,3 +1,6 @@
1display_name: "The Revitalized" 1display_name: "The Revitalized"
2rte_room: "Bye Room"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 14 y: 1 z: 14 }
2# Let's not include the demo (for now). 5# Let's not include the demo (for now).
3excluded_nodes: "Components/panel_demo" 6excluded_nodes: "Components/panel_demo"
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/metadata.txtpb b/data/maps/the_shop/metadata.txtpb index 06f7fed..2777760 100644 --- a/data/maps/the_shop/metadata.txtpb +++ b/data/maps/the_shop/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Shop" 1display_name: "The Shop"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0.5 y: 0 z: -12 }
4rte_trigger_scale { x: 15 y: 1 z: 19 }
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..7f30968 100644 --- a/data/maps/the_sirenic/metadata.txtpb +++ b/data/maps/the_sirenic/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Sirenic" 1display_name: "The Sirenic"
2rte_room: "Start"
3rte_trigger_pos { x: 0 y: 0 z: 23 }
4rte_trigger_scale { x: 6 y: 1 z: 4 }
5worldport_entrance {
6 room: "Start"
7 name: "PLAZA"
8}
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..2aeb43c --- /dev/null +++ b/data/maps/the_stellar/metadata.txtpb
@@ -0,0 +1,9 @@
1display_name: "The Stellar"
2type: GIFT_MAP
3rte_room: "Starting Room"
4rte_trigger_pos { x: 0 y: 0 z: -3 }
5rte_trigger_scale { x: 6 y: 1 z: 6 }
6# This panel does not appear to be accessible without sniping.
7excluded_nodes: "Panels/Room_1/panel_2"
8# The map's mastery is created at runtime.
9custom_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/metadata.txtpb b/data/maps/the_stormy/metadata.txtpb index 42b1814..de85a3b 100644 --- a/data/maps/the_stormy/metadata.txtpb +++ b/data/maps/the_stormy/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Stormy" 1display_name: "The Stormy"
2rte_room: "Center"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 8 y: 1 z: 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..d99f18d 100644 --- a/data/maps/the_sturdy/metadata.txtpb +++ b/data/maps/the_sturdy/metadata.txtpb
@@ -1,6 +1,11 @@
1display_name: "The Sturdy" 1display_name: "The Sturdy"
2# Let's ignore the second half of the rainbow for now. 2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 2 }
4rte_trigger_scale { x: 40 y: 1 z: 40 }
5# Let's ignore the second half of the rainbows for now.
3#excluded_nodes: "Components/Doors/Rainbow2/Hinge/rainbowMirrored" 6#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" 7#excluded_nodes: "Components/Doors/Rainbow/Hinge/rainbowMirrored"
8# The validator doesn't know that these node exist because they are part of a
9# sub-scene.
10custom_nodes: "Components/Doors/Rainbow/Hinge/rainbow"
11custom_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/metadata.txtpb b/data/maps/the_sun_temple/metadata.txtpb index 97f9290..6fa770b 100644 --- a/data/maps/the_sun_temple/metadata.txtpb +++ b/data/maps/the_sun_temple/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Sun Temple" 1display_name: "The Sun Temple"
2rte_room: "Entrance"
3rte_trigger_pos { x: 0 y: 0 z: 2 }
4rte_trigger_scale { x: 10 y: 1 z: 10 }
5worldport_entrance {
6 room: "Entrance"
7 name: "UNKEMPT"
8}
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/metadata.txtpb b/data/maps/the_sweet/metadata.txtpb index 95f2209..e97f36f 100644 --- a/data/maps/the_sweet/metadata.txtpb +++ b/data/maps/the_sweet/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Sweet" 1display_name: "The Sweet"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: -14.5 }
4rte_trigger_scale { x: 10 y: 1 z: 25 }
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/doors.txtpb b/data/maps/the_symbolic/doors.txtpb index 5a443e7..7728e0d 100644 --- a/data/maps/the_symbolic/doors.txtpb +++ b/data/maps/the_symbolic/doors.txtpb
@@ -139,17 +139,13 @@ doors {
139doors { 139doors {
140 name: "Poetry Room Panels" 140 name: "Poetry Room Panels"
141 type: LOCATION_ONLY 141 type: LOCATION_ONLY
142 panels { room: "Poetry Room 1" name: "ABSORBED" }
143 panels { room: "Poetry Room 1" name: "PRIMORDIAL" }
144 panels { room: "Poetry Room 2" name: "NOT" } 142 panels { room: "Poetry Room 2" name: "NOT" }
145 panels { room: "Poetry Room 2" name: "THERE" } 143 panels { room: "Poetry Room 2" name: "THERE" }
146 panels { room: "Poetry Room 2" name: "NOT THERE" } 144 panels { room: "Poetry Room 2" name: "NOT THERE" }
147 panels { room: "Poetry Room 3" name: "NOT" } 145 panels { room: "Poetry Room 3" name: "NOT" }
148 panels { room: "Poetry Room 3" name: "PRETTY" } 146 panels { room: "Poetry Room 3" name: "PRETTY" }
149 panels { room: "Poetry Room 3" name: "NOT PRETTY" }
150 panels { room: "Poetry Room Left" name: "NOT" } 147 panels { room: "Poetry Room Left" name: "NOT" }
151 panels { room: "Poetry Room Left" name: "TRUE" } 148 panels { room: "Poetry Room Left" name: "TRUE" }
152 panels { room: "Poetry Room Left" name: "NOT TRUE" }
153 panels { room: "Poetry Room Left Left" name: "NOT (1)" } 149 panels { room: "Poetry Room Left Left" name: "NOT (1)" }
154 panels { room: "Poetry Room Left Left" name: "NOT (2)" } 150 panels { room: "Poetry Room Left Left" name: "NOT (2)" }
155 panels { room: "Poetry Room Left Left" name: "LEFT" } 151 panels { room: "Poetry Room Left Left" name: "LEFT" }
@@ -160,7 +156,6 @@ doors {
160 panels { room: "Poetry Room Left Right" name: "NOT NOT MISS" } 156 panels { room: "Poetry Room Left Right" name: "NOT NOT MISS" }
161 panels { room: "Poetry Room Right" name: "NOT" } 157 panels { room: "Poetry Room Right" name: "NOT" }
162 panels { room: "Poetry Room Right" name: "BETTER" } 158 panels { room: "Poetry Room Right" name: "BETTER" }
163 panels { room: "Poetry Room Right" name: "NOT BETTER" }
164 panels { room: "Poetry Room Right Left" name: "NOT (1)" } 159 panels { room: "Poetry Room Right Left" name: "NOT (1)" }
165 panels { room: "Poetry Room Right Left" name: "NOT (2)" } 160 panels { room: "Poetry Room Right Left" name: "NOT (2)" }
166 panels { room: "Poetry Room Right Left" name: "TABLET" } 161 panels { room: "Poetry Room Right Left" name: "TABLET" }
@@ -276,6 +271,9 @@ doors {
276doors { 271doors {
277 name: "Main Area Exit" 272 name: "Main Area Exit"
278 type: EVENT 273 type: EVENT
274 # The game logic here requires you to solve every panel on the map, EXCEPT:
275 # 1) The four panels past the door, and
276 # 2) Any panel that has a proxy.
279 panels { room: "Main Area" name: "JUSTICE" } 277 panels { room: "Main Area" name: "JUSTICE" }
280 panels { room: "Main Area" name: "NOTICE (1)" } 278 panels { room: "Main Area" name: "NOTICE (1)" }
281 panels { room: "Main Area" name: "NOTICE (2)" } 279 panels { room: "Main Area" name: "NOTICE (2)" }
@@ -353,6 +351,36 @@ doors {
353 panels { room: "Main Area" name: "LIKE" } 351 panels { room: "Main Area" name: "LIKE" }
354 panels { room: "Main Area" name: "NEEDLESS" } 352 panels { room: "Main Area" name: "NEEDLESS" }
355 panels { room: "Main Area" name: "RESTLESS" } 353 panels { room: "Main Area" name: "RESTLESS" }
354 panels { room: "Poetry Room 2" name: "NOT" }
355 panels { room: "Poetry Room 2" name: "THERE" }
356 panels { room: "Poetry Room 2" name: "NOT THERE" }
357 panels { room: "Poetry Room 3" name: "NOT" }
358 panels { room: "Poetry Room 3" name: "PRETTY" }
359 panels { room: "Poetry Room Left" name: "NOT" }
360 panels { room: "Poetry Room Left" name: "TRUE" }
361 panels { room: "Poetry Room Left Left" name: "NOT (1)" }
362 panels { room: "Poetry Room Left Left" name: "NOT (2)" }
363 panels { room: "Poetry Room Left Left" name: "LEFT" }
364 panels { room: "Poetry Room Left Left" name: "NOT NOT LEFT" }
365 panels { room: "Poetry Room Left Right" name: "NOT (1)" }
366 panels { room: "Poetry Room Left Right" name: "NOT (2)" }
367 panels { room: "Poetry Room Left Right" name: "MISS" }
368 panels { room: "Poetry Room Left Right" name: "NOT NOT MISS" }
369 panels { room: "Poetry Room Right" name: "NOT" }
370 panels { room: "Poetry Room Right" name: "BETTER" }
371 panels { room: "Poetry Room Right Left" name: "NOT (1)" }
372 panels { room: "Poetry Room Right Left" name: "NOT (2)" }
373 panels { room: "Poetry Room Right Left" name: "TABLET" }
374 panels { room: "Poetry Room Right Left" name: "NOT NOT TABLET" }
375 panels { room: "Poetry Room Right Right" name: "NOT (1)" }
376 panels { room: "Poetry Room Right Right" name: "NOT (2)" }
377 panels { room: "Poetry Room Right Right" name: "NOT (3)" }
378 panels { room: "Poetry Room Right Right" name: "NOT NOT NOT" }
379 panels { room: "Whirred Room" name: "TAIPEI" }
380 panels { room: "Whirred Room" name: "NAYSAYER" }
381 panels { room: "Whirred Room" name: "NAY" }
382 panels { room: "Whirred Room" name: "INDEX (1)" }
383 panels { room: "Whirred Room" name: "INDEX (2)" }
356} 384}
357doors { 385doors {
358 name: "Mastery" 386 name: "Mastery"
diff --git a/data/maps/the_symbolic/metadata.txtpb b/data/maps/the_symbolic/metadata.txtpb index 311dead..41b9799 100644 --- a/data/maps/the_symbolic/metadata.txtpb +++ b/data/maps/the_symbolic/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Symbolic" 1display_name: "The Symbolic"
2rte_room: "White Room"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "White Room"
7 name: "PLAZA"
8}
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..59e599c 100644 --- a/data/maps/the_talented/metadata.txtpb +++ b/data/maps/the_talented/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Talented" 1display_name: "The Talented"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 9 }
4rte_trigger_scale { x: 6 y: 1 z: 15 }
5worldport_entrance {
6 room: "Main Area"
7 name: "GREAT"
8}
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/metadata.txtpb b/data/maps/the_tenacious/metadata.txtpb index d98e8f2..55ea23d 100644 --- a/data/maps/the_tenacious/metadata.txtpb +++ b/data/maps/the_tenacious/metadata.txtpb
@@ -1 +1,5 @@
1display_name: "The Tenacious" 1display_name: "The Tenacious"
2rte_room: "Control Center Entrance"
3rte_trigger_pos { x: 0 y: 0 z: 10 }
4rte_trigger_scale { x: 4 y: 1 z: 4 }
5daedalus_only_mode: DAED_ONLY_ALLOW
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/metadata.txtpb b/data/maps/the_three_doors/metadata.txtpb index 0c6052a..d2fa498 100644 --- a/data/maps/the_three_doors/metadata.txtpb +++ b/data/maps/the_three_doors/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Three Doors" 1display_name: "The Three Doors"
2rte_room: "First Second Room"
3rte_trigger_pos { x: -15 y: 0 z: 1 }
4rte_trigger_scale { x: 8 y: 1 z: 4 }
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..11ab3da 100644 --- a/data/maps/the_tower/metadata.txtpb +++ b/data/maps/the_tower/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Tower" 1display_name: "The Tower"
2rte_room: "First Floor"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 8 y: 1 z: 8 }
5worldport_entrance {
6 room: "First Floor"
7 name: "GREAT"
8}
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/metadata.txtpb b/data/maps/the_tree/metadata.txtpb index 6090384..dbe7a6e 100644 --- a/data/maps/the_tree/metadata.txtpb +++ b/data/maps/the_tree/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Tree" 1display_name: "The Tree"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 15 y: 1 z: 15 }
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/doors.txtpb b/data/maps/the_unkempt/doors.txtpb index 29065ec..f758369 100644 --- a/data/maps/the_unkempt/doors.txtpb +++ b/data/maps/the_unkempt/doors.txtpb
@@ -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/metadata.txtpb b/data/maps/the_unkempt/metadata.txtpb index f2862bc..c3fdb8c 100644 --- a/data/maps/the_unkempt/metadata.txtpb +++ b/data/maps/the_unkempt/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Unkempt" 1display_name: "The Unkempt"
2rte_room: "Main Area"
3rte_trigger_pos { x: -12 y: 0 z: -2 }
4rte_trigger_scale { x: 20 y: 1 z: 5 }
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/metadata.txtpb b/data/maps/the_unyielding/metadata.txtpb index d674150..2e67c0d 100644 --- a/data/maps/the_unyielding/metadata.txtpb +++ b/data/maps/the_unyielding/metadata.txtpb
@@ -1 +1,4 @@
1display_name: "The Unyielding" 1display_name: "The Unyielding"
2rte_room: "Digital Entrance"
3rte_trigger_pos { x: 0 y: 0 z: -6 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
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_wise/metadata.txtpb b/data/maps/the_wise/metadata.txtpb index 91af34e..e9b77ad 100644 --- a/data/maps/the_wise/metadata.txtpb +++ b/data/maps/the_wise/metadata.txtpb
@@ -1,3 +1,6 @@
1display_name: "The Wise" 1display_name: "The Wise"
2rte_room: "Entry"
3rte_trigger_pos { x: 0 y: 0 z: 22.5 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
2# This port is out of bounds. 5# This port is out of bounds.
3excluded_nodes: "Components/Warps/worldport" 6excluded_nodes: "Components/Warps/worldport"
diff --git a/data/maps/the_wondrous/metadata.txtpb b/data/maps/the_wondrous/metadata.txtpb index 0b96cf2..ef453aa 100644 --- a/data/maps/the_wondrous/metadata.txtpb +++ b/data/maps/the_wondrous/metadata.txtpb
@@ -1 +1,8 @@
1display_name: "The Wondrous" 1display_name: "The Wondrous"
2rte_room: "Entry"
3rte_trigger_pos { x: 8 y: 0 z: 40 }
4rte_trigger_scale { x: 6 y: 1 z: 6 }
5worldport_entrance {
6 room: "Entry"
7 name: "DAEDALUS"
8}
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/metadata.txtpb b/data/maps/the_words/metadata.txtpb index 2dadcae..0fdee3f 100644 --- a/data/maps/the_words/metadata.txtpb +++ b/data/maps/the_words/metadata.txtpb
@@ -1,4 +1,7 @@
1display_name: "The Words" 1display_name: "The Words"
2rte_room: "Main Area"
3rte_trigger_pos { x: 0 y: 0 z: 0 }
4rte_trigger_scale { x: 8 y: 1 z: 8 }
2# These are old proxies of the main room's panels that are not linked up 5# These are old proxies of the main room's panels that are not linked up
3# anymore. 6# anymore.
4excluded_nodes: "Panels/Proxies/panel_3" 7excluded_nodes: "Panels/Proxies/panel_3"
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 index 0b90348..99d3021 100644 --- a/data/metadata.txtpb +++ b/data/metadata.txtpb
@@ -1,4 +1,8 @@
1version: 5 1version {
2 major: 9
3 minor: 1
4 patch: 0
5}
2# Filler item. 6# Filler item.
3special_names: "A Job Well Done" 7special_names: "A Job Well Done"
4# Symbol items. 8# Symbol items.
@@ -48,3 +52,5 @@ special_names: "Anti W"
48special_names: "Anti X" 52special_names: "Anti X"
49special_names: "Anti Y" 53special_names: "Anti Y"
50special_names: "Anti Z" 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 64e3ddc..619b3d3 100644 --- a/proto/data.proto +++ b/proto/data.proto
@@ -52,6 +52,13 @@ enum DoorGroupType {
52 SHUFFLE_GROUP = 4; 52 SHUFFLE_GROUP = 4;
53} 53}
54 54
55enum MapType {
56 NORMAL_MAP = 0;
57 ICARUS = 1;
58 GIFT_MAP = 2;
59 DEMO = 3;
60}
61
55enum AxisDirection { 62enum AxisDirection {
56 AXIS_DIRECTION_UNKNOWN = 0; 63 AXIS_DIRECTION_UNKNOWN = 0;
57 64
@@ -87,6 +94,24 @@ enum PuzzleSymbol {
87 QUESTION = 19; 94 QUESTION = 19;
88} 95}
89 96
97enum DaedalusOnlyMode {
98 DAED_ONLY_DISALLOW = 0;
99 DAED_ONLY_PARTIAL = 1;
100 DAED_ONLY_ALLOW = 2;
101}
102
103message Vec3d {
104 optional double x = 1;
105 optional double y = 2;
106 optional double z = 3;
107}
108
109message VersionNumber {
110 optional uint64 major = 1;
111 optional uint64 minor = 2;
112 optional uint64 patch = 3;
113}
114
90message ProxyIdentifier { 115message ProxyIdentifier {
91 optional uint64 panel = 1; 116 optional uint64 panel = 1;
92 optional string answer = 2; 117 optional string answer = 2;
@@ -111,6 +136,8 @@ message Connection {
111 optional bool roof_access = 7; 136 optional bool roof_access = 7;
112 optional bool purple_ending = 8; 137 optional bool purple_ending = 8;
113 optional bool cyan_ending = 9; 138 optional bool cyan_ending = 9;
139 optional bool mint_ending = 11;
140 optional bool vanilla_only = 10;
114} 141}
115 142
116message Door { 143message Door {
@@ -130,13 +157,17 @@ message Door {
130 repeated KeyholderAnswer keyholders = 13; 157 repeated KeyholderAnswer keyholders = 13;
131 repeated uint64 rooms = 14; 158 repeated uint64 rooms = 14;
132 repeated uint64 doors = 15; 159 repeated uint64 doors = 15;
133 repeated uint64 endings = 16; 160 optional bool white_ending = 16;
134 optional bool double_letters = 18; 161 optional bool double_letters = 18;
135 repeated string senders = 19; 162 repeated string senders = 19;
136 163
137 optional DoorType type = 8; 164 optional DoorType type = 8;
165 optional bool latch = 20;
166 optional bool legacy_location = 21;
138 167
139 optional string location_name = 17; 168 optional string location_name = 17;
169 optional bool daedalus_only_allow = 22;
170 optional bool daedalus_only_always_item = 23;
140} 171}
141 172
142message PanelData { 173message PanelData {
@@ -155,6 +186,8 @@ message PanelData {
155 optional uint64 required_door = 9; 186 optional uint64 required_door = 9;
156 optional uint64 required_room = 11; 187 optional uint64 required_room = 11;
157 188
189 optional bool exclude_from_panelsanity = 13;
190
158 optional string display_name = 12; 191 optional string display_name = 12;
159} 192}
160 193
@@ -177,12 +210,16 @@ message PaintingData {
177 210
178message Port { 211message Port {
179 optional uint64 id = 1; 212 optional uint64 id = 1;
213 optional uint64 ap_id = 11;
180 optional uint64 room_id = 2; 214 optional uint64 room_id = 2;
181 optional string name = 3; 215 optional string name = 3;
182 216
217 optional string display_name = 10;
183 optional string path = 4; 218 optional string path = 4;
184 optional string orientation = 5; 219 optional Vec3d destination = 5;
220 optional double rotation = 8;
185 optional AxisDirection gravity = 7; 221 optional AxisDirection gravity = 7;
222 optional bool no_shuffle = 9;
186 223
187 optional uint64 required_door = 6; 224 optional uint64 required_door = 6;
188} 225}
@@ -233,6 +270,7 @@ message Room {
233 optional string name = 2; 270 optional string name = 2;
234 optional string display_name = 3; 271 optional string display_name = 3;
235 optional string panel_display_name = 13; 272 optional string panel_display_name = 13;
273 optional bool daedalus_only_allow = 14;
236 274
237 repeated uint64 panels = 4; 275 repeated uint64 panels = 4;
238 repeated uint64 paintings = 5; 276 repeated uint64 paintings = 5;
@@ -248,6 +286,14 @@ message Map {
248 optional uint64 id = 1; 286 optional uint64 id = 1;
249 optional string name = 2; 287 optional string name = 2;
250 optional string display_name = 3; 288 optional string display_name = 3;
289 optional DaedalusOnlyMode daedalus_only_mode = 6;
290 optional uint64 worldport_entrance = 4;
291 optional MapType type = 5;
292
293 optional uint64 rte_room = 7;
294 optional uint64 rte_ap_id = 8;
295 optional Vec3d rte_trigger_pos = 9;
296 optional Vec3d rte_trigger_scale = 10;
251} 297}
252 298
253message Progressive { 299message Progressive {
@@ -263,10 +309,12 @@ message DoorGroup {
263 optional uint64 ap_id = 3; 309 optional uint64 ap_id = 3;
264 optional DoorGroupType type = 4; 310 optional DoorGroupType type = 4;
265 repeated uint64 doors = 5; 311 repeated uint64 doors = 5;
312
313 optional bool daedalus_only_always_item = 6;
266} 314}
267 315
268message AllObjects { 316message AllObjects {
269 optional uint64 version = 15; 317 optional VersionNumber version = 15;
270 318
271 repeated Map maps = 7; 319 repeated Map maps = 7;
272 repeated Room rooms = 1; 320 repeated Room rooms = 1;
diff --git a/proto/human.proto b/proto/human.proto index c247edf..5cd8ce7 100644 --- a/proto/human.proto +++ b/proto/human.proto
@@ -78,6 +78,13 @@ message HumanConnection {
78 // This means that the connection should additionally require all cyan letters 78 // This means that the connection should additionally require all cyan letters
79 // when the Strict Cyan Ending option is on. 79 // when the Strict Cyan Ending option is on.
80 optional bool cyan_ending = 10; 80 optional bool cyan_ending = 10;
81
82 // This means that the connection should additionally require being able to
83 // type a specific text string when Custom Mint Ending is on.
84 optional bool mint_ending = 12;
85
86 // This means that the connection only exists when doors are not shuffled.
87 optional bool vanilla_only = 11;
81} 88}
82 89
83message HumanConnections { 90message HumanConnections {
@@ -101,7 +108,7 @@ message HumanDoor {
101 repeated KeyholderIdentifier keyholders = 10; 108 repeated KeyholderIdentifier keyholders = 10;
102 repeated RoomIdentifier rooms = 11; 109 repeated RoomIdentifier rooms = 11;
103 repeated DoorIdentifier doors = 12; 110 repeated DoorIdentifier doors = 12;
104 repeated string endings = 13; 111 optional bool white_ending = 13;
105 optional bool double_letters = 15; 112 optional bool double_letters = 15;
106 113
107 // Sender nodes to be added to the list of requirements for triggering the 114 // Sender nodes to be added to the list of requirements for triggering the
@@ -111,6 +118,19 @@ message HumanDoor {
111 optional DoorType type = 4; 118 optional DoorType type = 4;
112 optional string location_room = 5; 119 optional string location_room = 5;
113 optional string location_name = 14; 120 optional string location_name = 14;
121
122 // Non-item doors that are latched will stay open once opened, even if the
123 // opening trigger is deactivated. This applies to EVENT/LOCATION_ONLY doors,
124 // as well as item-locked doors when not shuffling doors.
125 optional bool latch = 17;
126
127 // If true, the client will treat this door like a location, even though no
128 // location is created for it in the generator. This helps provide backwards
129 // compatability with older worlds.
130 optional bool legacy_location = 18;
131
132 optional bool daedalus_only_allow = 19;
133 optional bool daedalus_only_always_item = 20;
114} 134}
115 135
116message HumanDoors { 136message HumanDoors {
@@ -130,6 +150,8 @@ message HumanPanel {
130 optional DoorIdentifier required_door = 7; 150 optional DoorIdentifier required_door = 7;
131 optional RoomIdentifier required_room = 8; 151 optional RoomIdentifier required_room = 8;
132 152
153 optional bool exclude_from_panelsanity = 10;
154
133 optional string display_name = 9; 155 optional string display_name = 9;
134} 156}
135 157
@@ -150,9 +172,16 @@ message HumanPainting {
150 172
151message HumanPort { 173message HumanPort {
152 optional string name = 1; 174 optional string name = 1;
175 optional string display_name = 8;
153 optional string path = 2; 176 optional string path = 2;
154 177
155 optional string orientation = 3; 178 optional bool no_shuffle = 7;
179
180 // These specify how the player should be placed when a randomized entrance
181 // sends them to this port. "rotation" is in degrees and is counter-clockwise
182 // from the positive X axis.
183 optional Vec3d destination = 3;
184 optional double rotation = 6;
156 optional AxisDirection gravity = 5 [default = Y_MINUS]; 185 optional AxisDirection gravity = 5 [default = Y_MINUS];
157 186
158 optional DoorIdentifier required_door = 4; 187 optional DoorIdentifier required_door = 4;
@@ -195,6 +224,8 @@ message HumanRoom {
195 // doors generated from panels in the same area. 224 // doors generated from panels in the same area.
196 optional string panel_display_name = 10; 225 optional string panel_display_name = 10;
197 226
227 optional bool daedalus_only_allow = 11;
228
198 repeated HumanPanel panels = 3; 229 repeated HumanPanel panels = 3;
199 repeated HumanPainting paintings = 4; 230 repeated HumanPainting paintings = 4;
200 repeated HumanLetter letters = 5; 231 repeated HumanLetter letters = 5;
@@ -206,7 +237,23 @@ message HumanRoom {
206 237
207message HumanMap { 238message HumanMap {
208 optional string display_name = 1; 239 optional string display_name = 1;
240 optional MapType type = 4;
241
242 optional DaedalusOnlyMode daedalus_only_mode = 6;
243
244 optional PortIdentifier worldport_entrance = 3;
245 optional string rte_room = 7;
246 optional Vec3d rte_trigger_pos = 8;
247 optional Vec3d rte_trigger_scale = 9;
248
249 // These two fields are used by the validator and nothing else. excluded_nodes
250 // are objects in the tscn that are intentionally not mentioned in the txtpb.
251 // custom_nodes are the reverse of that; objects that are mentioned in the
252 // txtpb but not present in the tscn. These are generally created dynamically
253 // by the game mod, but may also be used for places where the validator is
254 // just wrong about the contents of the map file.
209 repeated string excluded_nodes = 2; 255 repeated string excluded_nodes = 2;
256 repeated string custom_nodes = 5;
210} 257}
211 258
212message HumanProgressive { 259message HumanProgressive {
@@ -222,6 +269,8 @@ message HumanDoorGroup {
222 optional string name = 1; 269 optional string name = 1;
223 optional DoorGroupType type = 2; 270 optional DoorGroupType type = 2;
224 repeated DoorIdentifier doors = 3; 271 repeated DoorIdentifier doors = 3;
272
273 optional bool daedalus_only_always_item = 4;
225} 274}
226 275
227message HumanDoorGroups { 276message HumanDoorGroups {
@@ -230,7 +279,7 @@ message HumanDoorGroups {
230 279
231message HumanGlobalMetadata { 280message HumanGlobalMetadata {
232 repeated string special_names = 1; 281 repeated string special_names = 1;
233 optional uint64 version = 2; 282 optional VersionNumber version = 2;
234} 283}
235 284
236message IdMappings { 285message IdMappings {
@@ -238,11 +287,13 @@ message IdMappings {
238 map<string, uint64> panels = 1; 287 map<string, uint64> panels = 1;
239 map<string, uint64> masteries = 2; 288 map<string, uint64> masteries = 2;
240 map<string, uint64> keyholders = 3; 289 map<string, uint64> keyholders = 3;
290 map<string, uint64> ports = 4;
241 } 291 }
242 292
243 message MapIds { 293 message MapIds {
244 map<string, uint64> doors = 1; 294 map<string, uint64> doors = 1;
245 map<string, RoomIds> rooms = 2; 295 map<string, RoomIds> rooms = 2;
296 optional uint64 rte = 3;
246 } 297 }
247 298
248 map<string, MapIds> maps = 1; 299 map<string, MapIds> maps = 1;
diff --git a/tools/assign_ids/main.cpp b/tools/assign_ids/main.cpp index 3e16f78..4a48b86 100644 --- a/tools/assign_ids/main.cpp +++ b/tools/assign_ids/main.cpp
@@ -65,6 +65,11 @@ class AssignIds {
65 UpdateNextId(room.panels()); 65 UpdateNextId(room.panels());
66 UpdateNextId(room.masteries()); 66 UpdateNextId(room.masteries());
67 UpdateNextId(room.keyholders()); 67 UpdateNextId(room.keyholders());
68 UpdateNextId(room.ports());
69 }
70
71 if (map.has_rte()) {
72 UpdateNextId(map.rte());
68 } 73 }
69 } 74 }
70 75
@@ -92,10 +97,31 @@ class AssignIds {
92 void ProcessMap(std::filesystem::path path) { 97 void ProcessMap(std::filesystem::path path) {
93 std::string map_name = path.filename().string(); 98 std::string map_name = path.filename().string();
94 99
100 ProcessMapMetadata(path / "metadata.txtpb", map_name);
95 ProcessDoorsFile(path / "doors.txtpb", map_name); 101 ProcessDoorsFile(path / "doors.txtpb", map_name);
96 ProcessRooms(path / "rooms", map_name); 102 ProcessRooms(path / "rooms", map_name);
97 } 103 }
98 104
105 void ProcessMapMetadata(std::filesystem::path path,
106 const std::string& current_map_name) {
107 if (!std::filesystem::exists(path)) {
108 return;
109 }
110
111 auto metadata = ReadMessageFromFile<HumanMap>(path.string());
112 auto& maps = *output_.mutable_maps();
113
114 if (metadata.has_rte_room()) {
115 if (!id_mappings_.maps().contains(current_map_name) ||
116 !id_mappings_.maps().at(current_map_name).has_rte()) {
117 maps[current_map_name].set_rte(next_id_++);
118 } else {
119 maps[current_map_name].set_rte(
120 id_mappings_.maps().at(current_map_name).rte());
121 }
122 }
123 }
124
99 void ProcessDoorsFile(std::filesystem::path path, 125 void ProcessDoorsFile(std::filesystem::path path,
100 const std::string& current_map_name) { 126 const std::string& current_map_name) {
101 if (!std::filesystem::exists(path)) { 127 if (!std::filesystem::exists(path)) {
@@ -111,7 +137,8 @@ class AssignIds {
111 137
112 void ProcessDoor(const HumanDoor& h_door, 138 void ProcessDoor(const HumanDoor& h_door,
113 const std::string& current_map_name) { 139 const std::string& current_map_name) {
114 if (h_door.type() == DoorType::EVENT) { 140 if (h_door.type() == DoorType::EVENT && !h_door.latch() &&
141 !h_door.legacy_location()) {
115 return; 142 return;
116 } 143 }
117 144
@@ -245,6 +272,37 @@ class AssignIds {
245 .at(h_keyholder.name()); 272 .at(h_keyholder.name());
246 } 273 }
247 } 274 }
275
276 for (const HumanPort& h_port : h_room.ports()) {
277 if (h_port.no_shuffle()) {
278 continue;
279 }
280
281 auto& maps = *output_.mutable_maps();
282 auto& rooms = *maps[current_map_name].mutable_rooms();
283 auto& ports = *rooms[h_room.name()].mutable_ports();
284
285 if (!id_mappings_.maps().contains(current_map_name) ||
286 !id_mappings_.maps()
287 .at(current_map_name)
288 .rooms()
289 .contains(h_room.name()) ||
290 !id_mappings_.maps()
291 .at(current_map_name)
292 .rooms()
293 .at(h_room.name())
294 .ports()
295 .contains(h_port.name())) {
296 ports[h_port.name()] = next_id_++;
297 } else {
298 ports[h_port.name()] = id_mappings_.maps()
299 .at(current_map_name)
300 .rooms()
301 .at(h_room.name())
302 .ports()
303 .at(h_port.name());
304 }
305 }
248 } 306 }
249 307
250 void ProcessSpecialIds() { 308 void ProcessSpecialIds() {
@@ -309,9 +367,13 @@ class AssignIds {
309 private: 367 private:
310 void UpdateNextId(const google::protobuf::Map<std::string, uint64_t>& ids) { 368 void UpdateNextId(const google::protobuf::Map<std::string, uint64_t>& ids) {
311 for (const auto& [_, id] : ids) { 369 for (const auto& [_, id] : ids) {
312 if (id > next_id_) { 370 UpdateNextId(id);
313 next_id_ = id; 371 }
314 } 372 }
373
374 void UpdateNextId(uint64_t id) {
375 if (id > next_id_) {
376 next_id_ = id;
315 } 377 }
316 } 378 }
317 379
diff --git a/tools/datapacker/main.cpp b/tools/datapacker/main.cpp index 596259b..4ecde74 100644 --- a/tools/datapacker/main.cpp +++ b/tools/datapacker/main.cpp
@@ -88,9 +88,28 @@ class DataPacker {
88 uint64_t map_id = container_.FindOrAddMap(map_name); 88 uint64_t map_id = container_.FindOrAddMap(map_name);
89 Map& map = *container_.all_objects().mutable_maps(map_id); 89 Map& map = *container_.all_objects().mutable_maps(map_id);
90 90
91 map.set_type(metadata.type());
92 map.set_daedalus_only_mode(metadata.daedalus_only_mode());
93
91 if (metadata.has_display_name()) { 94 if (metadata.has_display_name()) {
92 map.set_display_name(metadata.display_name()); 95 map.set_display_name(metadata.display_name());
93 } 96 }
97
98 if (metadata.has_worldport_entrance()) {
99 map.set_worldport_entrance(container_.FindOrAddPort(
100 map_name, metadata.worldport_entrance().room(),
101 metadata.worldport_entrance().name(), std::nullopt, std::nullopt));
102 }
103
104 if (metadata.has_rte_room()) {
105 map.set_rte_room(container_.FindOrAddRoom(map_name, metadata.rte_room(),
106 std::nullopt));
107 }
108
109 if (metadata.has_rte_trigger_pos()) {
110 *map.mutable_rte_trigger_pos() = metadata.rte_trigger_pos();
111 *map.mutable_rte_trigger_scale() = metadata.rte_trigger_scale();
112 }
94 } 113 }
95 114
96 void ProcessRooms(std::filesystem::path path, 115 void ProcessRooms(std::filesystem::path path,
@@ -113,6 +132,10 @@ class DataPacker {
113 room.set_panel_display_name(h_room.panel_display_name()); 132 room.set_panel_display_name(h_room.panel_display_name());
114 } 133 }
115 134
135 if (h_room.has_daedalus_only_allow()) {
136 room.set_daedalus_only_allow(h_room.daedalus_only_allow());
137 }
138
116 for (const HumanPanel& h_panel : h_room.panels()) { 139 for (const HumanPanel& h_panel : h_room.panels()) {
117 room.add_panels(ProcessPanel(h_panel, current_map_name, room.name())); 140 room.add_panels(ProcessPanel(h_panel, current_map_name, room.name()));
118 } 141 }
@@ -186,6 +209,10 @@ class DataPacker {
186 panel.set_display_name(h_panel.display_name()); 209 panel.set_display_name(h_panel.display_name());
187 } 210 }
188 211
212 if (h_panel.has_exclude_from_panelsanity()) {
213 panel.set_exclude_from_panelsanity(h_panel.exclude_from_panelsanity());
214 }
215
189 return panel_id; 216 return panel_id;
190 } 217 }
191 218
@@ -239,7 +266,14 @@ class DataPacker {
239 Port& port = *container_.all_objects().mutable_ports(port_id); 266 Port& port = *container_.all_objects().mutable_ports(port_id);
240 267
241 port.set_path(h_port.path()); 268 port.set_path(h_port.path());
242 port.set_orientation(h_port.orientation()); 269 port.set_display_name(h_port.display_name());
270
271 if (h_port.no_shuffle()) {
272 port.set_no_shuffle(h_port.no_shuffle());
273 } else {
274 *port.mutable_destination() = h_port.destination();
275 port.set_rotation(h_port.rotation());
276 }
243 277
244 // Setting this explicitly because the Godot protobuf doesn't support 278 // Setting this explicitly because the Godot protobuf doesn't support
245 // custom defaults. 279 // custom defaults.
@@ -396,8 +430,8 @@ class DataPacker {
396 container_.FindOrAddDoor(map_name, di.name(), current_map_name)); 430 container_.FindOrAddDoor(map_name, di.name(), current_map_name));
397 } 431 }
398 432
399 for (const std::string& ending_name : h_door.endings()) { 433 if (h_door.has_white_ending()) {
400 door.add_endings(container_.FindOrAddEnding(ending_name)); 434 door.set_white_ending(h_door.white_ending());
401 } 435 }
402 436
403 if (h_door.has_control_center_color()) { 437 if (h_door.has_control_center_color()) {
@@ -417,6 +451,22 @@ class DataPacker {
417 if (h_door.has_double_letters()) { 451 if (h_door.has_double_letters()) {
418 door.set_double_letters(h_door.double_letters()); 452 door.set_double_letters(h_door.double_letters());
419 } 453 }
454
455 if (h_door.has_latch()) {
456 door.set_latch(h_door.latch());
457 }
458
459 if (h_door.has_legacy_location()) {
460 door.set_legacy_location(h_door.legacy_location());
461 }
462
463 if (h_door.has_daedalus_only_allow()) {
464 door.set_daedalus_only_allow(h_door.daedalus_only_allow());
465 }
466
467 if (h_door.has_daedalus_only_always_item()) {
468 door.set_daedalus_only_always_item(h_door.daedalus_only_always_item());
469 }
420 } 470 }
421 471
422 void ProcessConnectionsFile(std::filesystem::path path, 472 void ProcessConnectionsFile(std::filesystem::path path,
@@ -483,6 +533,16 @@ class DataPacker {
483 r_connection.set_cyan_ending(human_connection.cyan_ending()); 533 r_connection.set_cyan_ending(human_connection.cyan_ending());
484 } 534 }
485 535
536 if (human_connection.has_mint_ending()) {
537 f_connection.set_mint_ending(human_connection.mint_ending());
538 r_connection.set_mint_ending(human_connection.mint_ending());
539 }
540
541 if (human_connection.has_vanilla_only()) {
542 f_connection.set_vanilla_only(human_connection.vanilla_only());
543 r_connection.set_vanilla_only(human_connection.vanilla_only());
544 }
545
486 container_.AddConnection(f_connection); 546 container_.AddConnection(f_connection);
487 if (!human_connection.oneway()) { 547 if (!human_connection.oneway()) {
488 container_.AddConnection(r_connection); 548 container_.AddConnection(r_connection);
@@ -612,6 +672,10 @@ class DataPacker {
612 container_.FindOrAddDoor(di.map(), di.name(), std::nullopt); 672 container_.FindOrAddDoor(di.map(), di.name(), std::nullopt);
613 group.add_doors(door_id); 673 group.add_doors(door_id);
614 } 674 }
675
676 if (h_group.has_daedalus_only_always_item()) {
677 group.set_daedalus_only_always_item(h_group.daedalus_only_always_item());
678 }
615 } 679 }
616 680
617 void ProcessGlobalMetadataFile(std::filesystem::path path) { 681 void ProcessGlobalMetadataFile(std::filesystem::path path) {
@@ -620,7 +684,7 @@ class DataPacker {
620 } 684 }
621 685
622 auto h_metadata = ReadMessageFromFile<HumanGlobalMetadata>(path.string()); 686 auto h_metadata = ReadMessageFromFile<HumanGlobalMetadata>(path.string());
623 container_.all_objects().set_version(h_metadata.version()); 687 *container_.all_objects().mutable_version() = h_metadata.version();
624 } 688 }
625 689
626 void ProcessIdsFile(std::filesystem::path path) { 690 void ProcessIdsFile(std::filesystem::path path) {
@@ -655,6 +719,17 @@ class DataPacker {
655 .mutable_keyholders(keyholder_id) 719 .mutable_keyholders(keyholder_id)
656 ->set_ap_id(ap_id); 720 ->set_ap_id(ap_id);
657 } 721 }
722
723 for (const auto& [port_name, ap_id] : room.ports()) {
724 uint64_t port_id = container_.FindOrAddPort(
725 map_name, room_name, port_name, std::nullopt, std::nullopt);
726 container_.all_objects().mutable_ports(port_id)->set_ap_id(ap_id);
727 }
728 }
729
730 if (map.has_rte()) {
731 uint64_t map_id = container_.FindOrAddMap(map_name);
732 container_.all_objects().mutable_maps(map_id)->set_rte_ap_id(map.rte());
658 } 733 }
659 } 734 }
660 735
diff --git a/tools/util/ids_yaml_format.cpp b/tools/util/ids_yaml_format.cpp index 71bfd63..c23c66b 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
@@ -73,6 +80,10 @@ IdMappings ReadIdsFromYaml(const std::string& filename) {
73 door_it.second.as<uint64_t>(); 80 door_it.second.as<uint64_t>();
74 } 81 }
75 } 82 }
83
84 if (map_it.second["rte"]) {
85 map_ids.set_rte(map_it.second["rte"].as<uint64_t>());
86 }
76 } 87 }
77 } 88 }
78 89
@@ -146,6 +157,12 @@ void WriteIdsAsYaml(const IdMappings& ids, const std::string& filename) {
146 keyholder_id; 157 keyholder_id;
147 }); 158 });
148 159
160 OperateOnSortedMap(
161 room_ids.ports(),
162 [&room_node](const std::string& port_name, uint64_t port_id) {
163 room_node["ports"][port_name] = port_id;
164 });
165
149 map_node["rooms"][room_name] = std::move(room_node); 166 map_node["rooms"][room_name] = std::move(room_node);
150 }); 167 });
151 168
@@ -155,6 +172,10 @@ void WriteIdsAsYaml(const IdMappings& ids, const std::string& filename) {
155 map_node["doors"][door_name] = door_id; 172 map_node["doors"][door_name] = door_id;
156 }); 173 });
157 174
175 if (map_ids.has_rte()) {
176 map_node["rte"] = map_ids.rte();
177 }
178
158 result["maps"][map_name] = std::move(map_node); 179 result["maps"][map_name] = std::move(map_node);
159 }); 180 });
160 181
diff --git a/tools/validator/human_processor.cpp b/tools/validator/human_processor.cpp index 2c978bf..d6fcfa6 100644 --- a/tools/validator/human_processor.cpp +++ b/tools/validator/human_processor.cpp
@@ -74,9 +74,35 @@ class HumanProcessor {
74 MapInfo& map_info = info_.maps[current_map_name]; 74 MapInfo& map_info = info_.maps[current_map_name];
75 75
76 auto metadata = ReadMessageFromFile<HumanMap>(path.string()); 76 auto metadata = ReadMessageFromFile<HumanMap>(path.string());
77 map_info.definitions.push_back(metadata);
78
77 for (const std::string& path : metadata.excluded_nodes()) { 79 for (const std::string& path : metadata.excluded_nodes()) {
78 map_info.game_nodes[path].uses++; 80 map_info.game_nodes[path].uses++;
79 } 81 }
82
83 for (const std::string& path : metadata.custom_nodes()) {
84 map_info.game_nodes[path].defined = true;
85 }
86
87 if (metadata.has_worldport_entrance()) {
88 auto port_identifier = GetCompletePortIdentifier(
89 metadata.worldport_entrance(), current_map_name, std::nullopt);
90 if (port_identifier) {
91 PortInfo& port_info = info_.ports[*port_identifier];
92 port_info.map_worldport_entrances.push_back(current_map_name);
93 } else {
94 map_info.malformed_worldport_entrance = metadata.worldport_entrance();
95 }
96 }
97
98 if (metadata.has_rte_room()) {
99 RoomIdentifier room_identifier;
100 room_identifier.set_map(current_map_name);
101 room_identifier.set_name(metadata.rte_room());
102
103 RoomInfo& room_info = info_.rooms[room_identifier];
104 room_info.map_rtes_referenced_by.push_back(current_map_name);
105 }
80 } 106 }
81 107
82 void ProcessRooms(std::filesystem::path path, 108 void ProcessRooms(std::filesystem::path path,
@@ -358,11 +384,6 @@ class HumanProcessor {
358 DoorInfo& other_door_info = info_.doors[complete_door_identifier]; 384 DoorInfo& other_door_info = info_.doors[complete_door_identifier];
359 other_door_info.doors_referenced_by.push_back(door_identifier); 385 other_door_info.doors_referenced_by.push_back(door_identifier);
360 } 386 }
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 } 387 }
367 388
368 void ProcessConnectionsFile(std::filesystem::path path, 389 void ProcessConnectionsFile(std::filesystem::path path,
@@ -560,12 +581,14 @@ class HumanProcessor {
560 auto ids = ReadIdsFromYaml(path.string()); 581 auto ids = ReadIdsFromYaml(path.string());
561 582
562 DoorIdentifier di; 583 DoorIdentifier di;
563 PanelIdentifier pi; 584 PanelIdentifier pai;
585 PortIdentifier poi;
564 KeyholderIdentifier ki; 586 KeyholderIdentifier ki;
565 587
566 for (const auto& [map_name, map] : ids.maps()) { 588 for (const auto& [map_name, map] : ids.maps()) {
567 di.set_map(map_name); 589 di.set_map(map_name);
568 pi.set_map(map_name); 590 pai.set_map(map_name);
591 poi.set_map(map_name);
569 ki.set_map(map_name); 592 ki.set_map(map_name);
570 593
571 for (const auto& [door_name, ap_id] : map.doors()) { 594 for (const auto& [door_name, ap_id] : map.doors()) {
@@ -576,13 +599,14 @@ class HumanProcessor {
576 } 599 }
577 600
578 for (const auto& [room_name, room] : map.rooms()) { 601 for (const auto& [room_name, room] : map.rooms()) {
579 pi.set_room(room_name); 602 pai.set_room(room_name);
603 poi.set_room(room_name);
580 ki.set_room(room_name); 604 ki.set_room(room_name);
581 605
582 for (const auto& [panel_name, ap_id] : room.panels()) { 606 for (const auto& [panel_name, ap_id] : room.panels()) {
583 pi.set_name(panel_name); 607 pai.set_name(panel_name);
584 608
585 PanelInfo& panel_info = info_.panels[pi]; 609 PanelInfo& panel_info = info_.panels[pai];
586 panel_info.has_id = true; 610 panel_info.has_id = true;
587 } 611 }
588 612
@@ -596,6 +620,18 @@ class HumanProcessor {
596 KeyholderInfo& keyholder_info = info_.keyholders[ki]; 620 KeyholderInfo& keyholder_info = info_.keyholders[ki];
597 keyholder_info.has_id = true; 621 keyholder_info.has_id = true;
598 } 622 }
623
624 for (const auto& [port_name, ap_id] : room.ports()) {
625 poi.set_name(port_name);
626
627 PortInfo& port_info = info_.ports[poi];
628 port_info.has_id = true;
629 }
630 }
631
632 if (map.has_rte()) {
633 MapInfo& map_info = info_.maps[map_name];
634 map_info.has_rte_id = true;
599 } 635 }
600 } 636 }
601 637
diff --git a/tools/validator/main.cpp b/tools/validator/main.cpp index 1a72e9a..6139e95 100644 --- a/tools/validator/main.cpp +++ b/tools/validator/main.cpp
@@ -21,7 +21,9 @@ void Run(const std::string& mapdir, const std::string& repodir) {
21int main(int argc, char** argv) { 21int main(int argc, char** argv) {
22 if (argc != 3) { 22 if (argc != 3) {
23 std::cout << "Incorrect argument count." << std::endl; 23 std::cout << "Incorrect argument count." << std::endl;
24 std::cout << "Usage: validator [path to map directory] [path to Lingo 2 repository]" << std::endl; 24 std::cout << "Usage: validator [path to map directory] [path to Lingo 2 "
25 "repository]"
26 << std::endl;
25 return 1; 27 return 1;
26 } 28 }
27 29
diff --git a/tools/validator/structs.h b/tools/validator/structs.h index d1d45f2..81a0e8f 100644 --- a/tools/validator/structs.h +++ b/tools/validator/structs.h
@@ -27,6 +27,11 @@ 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::vector<HumanMap> definitions;
32 bool has_rte_id = false;
33
34 std::optional<PortIdentifier> malformed_worldport_entrance;
30}; 35};
31 36
32struct RoomInfo { 37struct RoomInfo {
@@ -35,6 +40,7 @@ struct RoomInfo {
35 std::vector<DoorIdentifier> doors_referenced_by; 40 std::vector<DoorIdentifier> doors_referenced_by;
36 std::vector<PanelIdentifier> panels_referenced_by; 41 std::vector<PanelIdentifier> panels_referenced_by;
37 std::vector<HumanConnection> connections_referenced_by; 42 std::vector<HumanConnection> connections_referenced_by;
43 std::vector<std::string> map_rtes_referenced_by;
38}; 44};
39 45
40struct DoorInfo { 46struct DoorInfo {
@@ -54,9 +60,11 @@ struct DoorInfo {
54 60
55struct PortInfo { 61struct PortInfo {
56 std::vector<HumanPort> definitions; 62 std::vector<HumanPort> definitions;
63 bool has_id = false;
57 64
58 std::vector<HumanConnection> connections_referenced_by; 65 std::vector<HumanConnection> connections_referenced_by;
59 std::vector<HumanConnection> target_connections_referenced_by; 66 std::vector<HumanConnection> target_connections_referenced_by;
67 std::vector<std::string> map_worldport_entrances;
60}; 68};
61 69
62struct PaintingInfo { 70struct PaintingInfo {
@@ -104,8 +112,6 @@ struct LetterInfo {
104struct EndingInfo { 112struct EndingInfo {
105 std::vector<RoomIdentifier> defined_in; 113 std::vector<RoomIdentifier> defined_in;
106 bool has_id = false; 114 bool has_id = false;
107
108 std::vector<DoorIdentifier> doors_referenced_by;
109}; 115};
110 116
111struct PanelNameInfo { 117struct PanelNameInfo {
diff --git a/tools/validator/validator.cpp b/tools/validator/validator.cpp index dd41f5c..e9fbb74 100644 --- a/tools/validator/validator.cpp +++ b/tools/validator/validator.cpp
@@ -1,5 +1,6 @@
1#include "validator.h" 1#include "validator.h"
2 2
3#include <algorithm>
3#include <iostream> 4#include <iostream>
4 5
5#include "proto/human.pb.h" 6#include "proto/human.pb.h"
@@ -69,6 +70,27 @@ class Validator {
69 << " is not defined in the game file." << std::endl; 70 << " is not defined in the game file." << std::endl;
70 } 71 }
71 } 72 }
73
74 if (map_info.malformed_worldport_entrance) {
75 std::cout << "The worldport entrance for map " << map_name
76 << " is malformed." << std::endl;
77 }
78
79 if (map_info.has_rte_id) {
80 if (!std::any_of(
81 map_info.definitions.begin(), map_info.definitions.end(),
82 [](const HumanMap& h_map) { return h_map.has_rte_room(); })) {
83 std::cout << "Map " << map_name << " has an RTE ID but no RTE room."
84 << std::endl;
85 }
86 } else {
87 if (std::any_of(
88 map_info.definitions.begin(), map_info.definitions.end(),
89 [](const HumanMap& h_map) { return h_map.has_rte_room(); })) {
90 std::cout << "Map " << map_name << " has an RTE room but no RTE ID."
91 << std::endl;
92 }
93 }
72 } 94 }
73 95
74 void ValidateRoom(const RoomIdentifier& room_identifier, 96 void ValidateRoom(const RoomIdentifier& room_identifier,
@@ -94,6 +116,10 @@ class Validator {
94 std::cout << " CONNECTION " << connection.ShortDebugString() 116 std::cout << " CONNECTION " << connection.ShortDebugString()
95 << std::endl; 117 << std::endl;
96 } 118 }
119
120 for (const std::string& map_name : room_info.map_rtes_referenced_by) {
121 std::cout << " MAP RTE " << map_name << std::endl;
122 }
97 } else if (room_info.definitions.size() > 1) { 123 } else if (room_info.definitions.size() > 1) {
98 std::cout << "Room " << room_identifier.ShortDebugString() 124 std::cout << "Room " << room_identifier.ShortDebugString()
99 << " was defined multiple times." << std::endl; 125 << " was defined multiple times." << std::endl;
@@ -106,7 +132,7 @@ class Validator {
106 return false; 132 return false;
107 } 133 }
108 134
109 if (h_door.keyholders_size() > 0 || h_door.endings_size() > 0 || 135 if (h_door.keyholders_size() > 0 || h_door.white_ending() ||
110 h_door.complete_at() > 0) { 136 h_door.complete_at() > 0) {
111 return true; 137 return true;
112 } 138 }
@@ -220,16 +246,23 @@ class Validator {
220 << " needs an explicit location name." << std::endl; 246 << " needs an explicit location name." << std::endl;
221 } 247 }
222 248
223 if (h_door.double_letters() && 249 if (h_door.type() == DoorType::STANDARD ||
224 (h_door.type() == DoorType::STANDARD || 250 h_door.type() == DoorType::LOCATION_ONLY ||
225 h_door.type() == DoorType::LOCATION_ONLY || 251 h_door.type() == DoorType::GRAVESTONE || h_door.legacy_location()) {
226 h_door.type() == DoorType::GRAVESTONE)) { 252 if (h_door.double_letters()) {
227 std::cout << "Door " << door_identifier.ShortDebugString() 253 std::cout << "Door " << door_identifier.ShortDebugString()
228 << " is a location that depends on double_letters." 254 << " is a location that depends on double_letters."
229 << std::endl; 255 << std::endl;
256 }
257
258 if (!h_door.has_location_room()) {
259 std::cout << "Door " << door_identifier.ShortDebugString()
260 << " is missing a location_room." << std::endl;
261 }
230 } 262 }
231 263
232 bool needs_id = (h_door.type() != DoorType::EVENT); 264 bool needs_id = (h_door.type() != DoorType::EVENT || h_door.latch() ||
265 h_door.legacy_location());
233 if (door_info.has_id != needs_id) { 266 if (door_info.has_id != needs_id) {
234 if (needs_id) { 267 if (needs_id) {
235 std::cout << "Door " << door_identifier.ShortDebugString() 268 std::cout << "Door " << door_identifier.ShortDebugString()
@@ -253,6 +286,14 @@ class Validator {
253 std::cout << " CONNECTION " << connection.ShortDebugString() 286 std::cout << " CONNECTION " << connection.ShortDebugString()
254 << std::endl; 287 << std::endl;
255 } 288 }
289
290 for (const std::string& map_name : port_info.map_worldport_entrances) {
291 std::cout << " MAP (worldport_entrance) " << map_name << std::endl;
292 }
293
294 if (port_info.has_id) {
295 std::cout << " An AP ID is present." << std::endl;
296 }
256 } else if (port_info.definitions.size() > 1) { 297 } else if (port_info.definitions.size() > 1) {
257 std::cout << "Port " << port_identifier.ShortDebugString() 298 std::cout << "Port " << port_identifier.ShortDebugString()
258 << " was defined multiple times." << std::endl; 299 << " was defined multiple times." << std::endl;
@@ -273,6 +314,29 @@ class Validator {
273 } 314 }
274 } 315 }
275 } 316 }
317
318 for (const HumanPort& port : port_info.definitions) {
319 if (!port.no_shuffle()) {
320 if (!port.has_destination()) {
321 std::cout << "Port " << port_identifier.ShortDebugString()
322 << " is shuffleable and missing a destination."
323 << std::endl;
324 }
325 if (!port.has_rotation()) {
326 std::cout << "Port " << port_identifier.ShortDebugString()
327 << " is shuffleable and missing a rotation." << std::endl;
328 }
329 if (!port_info.has_id) {
330 std::cout << "Port " << port_identifier.ShortDebugString()
331 << " is missing an AP ID." << std::endl;
332 }
333 } else {
334 if (port_info.has_id) {
335 std::cout << "Port " << port_identifier.ShortDebugString()
336 << " should not have an AP ID." << std::endl;
337 }
338 }
339 }
276 } 340 }
277 341
278 void ValidatePainting(const PaintingIdentifier& painting_identifier, 342 void ValidatePainting(const PaintingIdentifier& painting_identifier,
@@ -459,12 +523,6 @@ class Validator {
459 std::cout << "Ending " << ending_name 523 std::cout << "Ending " << ending_name
460 << " has no definition, but was referenced:" << std::endl; 524 << " has no definition, but was referenced:" << std::endl;
461 525
462 for (const DoorIdentifier& door_identifier :
463 ending_info.doors_referenced_by) {
464 std::cout << " DOOR " << door_identifier.ShortDebugString()
465 << std::endl;
466 }
467
468 if (ending_info.has_id) { 526 if (ending_info.has_id) {
469 std::cout << " An AP ID is present." << std::endl; 527 std::cout << " An AP ID is present." << std::endl;
470 } 528 }
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 {