about summary refs log tree commit diff stats
path: root/tools
Commit message (Collapse)AuthorAgeFilesLines
* Annotated daedalus only mapsStar Rauchenberger2026-01-151-0/+9
|
* (Almost) all panels are locations or connections nowStar Rauchenberger2025-11-013-4/+9
|
* Assign stable IDs for shuffleable worldportsStar Rauchenberger2025-10-276-5/+80
|
* Added backwards compatibility with removed locationsStar Rauchenberger2025-10-261-1/+2
|
* Add white_ending flag to doorStar Rauchenberger2025-10-244-16/+3
|
* Allow validator to ignore custom nodesStar Rauchenberger2025-10-231-0/+4
|
* Fixed typos in the_advanced locationsStar Rauchenberger2025-10-231-7/+13
|
* Fix datapacking for worldport_entranceStar Rauchenberger2025-10-221-3/+2
|
* Added icarusStar Rauchenberger2025-10-221-0/+2
|
* Mark some doors as "latched"Star Rauchenberger2025-10-203-2/+6
|
* Annotate "worldport entrances"Star Rauchenberger2025-10-194-2/+31
|
* Fix vanilla-only oneway door in The RepetitiveStar Rauchenberger2025-09-301-0/+5
|
* Change version schemeStar Rauchenberger2025-09-291-1/+1
|
* Added display names to portsStar Rauchenberger2025-09-281-0/+1
|
* [Tools] Typo in header guardStar Rauchenberger2025-09-221-1/+1
|
* [Data] Annotate shuffleable portsStar Rauchenberger2025-09-212-1/+22
|
* Added strict purple/cyan ending optionsStar Rauchenberger2025-09-191-0/+10
|
* Add anti-collectable locationStar Rauchenberger2025-09-131-0/+3
|
* [Data] Allow WALL solution to the_entry!OPENStar Rauchenberger2025-09-111-1/+2
|
* [Data] Fixed connection target required door logic bugsStar Rauchenberger2025-09-113-4/+73
|
* [Data] Add version numberStar Rauchenberger2025-09-101-0/+10
|
* Added symbol shuffleStar Rauchenberger2025-09-091-0/+18
| | | | | Also fixed unlocked letters + any double letter cyan doors, and tweaked some logic related to important panels with symbols on them.
* Downgrade protobufStar Rauchenberger2025-09-082-4/+3
| | | | This allows the generated Python file to be compatible with the frozen Archipelago install.
* Added lavender cubes logicStar Rauchenberger2025-09-081-3/+0
|
* Added door groupsStar Rauchenberger2025-09-078-0/+153
|
* [Data] Strip unnecessary AP IDsStar Rauchenberger2025-09-044-20/+208
| | | | This was causing issues in the client, specifically for The Ancient.
* Added option for Daedalus roof access logicStar Rauchenberger2025-09-031-0/+5
|
* Added keyholder sanityStar Rauchenberger2025-09-023-0/+50
|
* Added progressive doorsStar Rauchenberger2025-09-018-35/+146
|
* Handled cyan doorsStar Rauchenberger2025-08-312-0/+13
|
* Changed how door location names are formattedStar Rauchenberger2025-08-305-215/+328
| | | | | | | | | | | | | | | | | | STANDARD type doors with at most four panels in the same map area and no other trigger objects will have their location names generated from the names of the panels used to open the door, similar to Lingo 1. Other door types will use the door's name. In either case, the name can be overridden using the new location_name field. Rooms can also set a panel_display_name field, which will be used in location names for doors, and is used to group panels into areas. Panels themselves can set display names, which differentiates their locations from other panels in the same area. Many maps were updated for this, but note that the_symbolic and the_unyielding have validator failures because of duplicate panel names. This won't matter until panelsanity is implemented.
* [Data] Made proxies with the same answer as the panel explicitStar Rauchenberger2025-08-301-1/+0
|
* Renamed Painting and Keyholder protosStar Rauchenberger2025-08-272-4/+4
|
* Prevent assigning ap_id==0Star Rauchenberger2025-08-271-1/+1
|
* Added control_centerStar Rauchenberger2025-08-274-1/+21
|
* Added daedalusStar Rauchenberger2025-08-241-0/+5
|
* Maps have display names nowStar Rauchenberger2025-08-201-0/+17
| | | | Also added endings to the apworld.
* Added "endings" object typeStar Rauchenberger2025-08-208-1/+105
|
* Store IDs in a yaml fileStar Rauchenberger2025-08-195-12/+169
| | | | This is much more efficient than the txtpb format, and we only need an interface for it in C++ since the IDs will be packed into the binary proto representation.
* Added the_repetitiveStar Rauchenberger2025-08-183-114/+40
|
* Validate that nodes in game files are usedStar Rauchenberger2025-08-1810-4/+459
| | | | You can now also list out nodes that you are explicitly not mapping out. The current state of the repo does produce some warnings when the validator is run and they're either endings, paintings that I'm not sure what to do with yet, and weird proxy stuff I'm not sure how to handle yet.
* Validate that node paths aren't used multiple timesStar Rauchenberger2025-08-173-1/+40
|
* Minor changes for compiling on WindowsStar Rauchenberger2025-08-163-3/+3
|
* Started writing a data validatorStar Rauchenberger2025-08-1612-0/+1040
| | | | | | | Currently, it can check whether identifiers point to non-existent objects, or whether multiple objects share the same identifier. It can also determine whether an identifier is underspecified (e.g. a door doesn't specify a room, or a global connection doesn't specify a map).
* Assigned IDs for the_galleryStar Rauchenberger2025-08-131-0/+7
|
* Converted to proto2Star Rauchenberger2025-08-126-19/+18
| | | | | | | | | | | | | | | | | This will let us use an older version of protobuf in Python, and allows us to use the Godot protobuf implementation at all. Scalar fields with custom defaults in data.proto were changed to not have a default, because Godot doesn't handle it properly. The equivalent fields in human.proto still have the defaults, and datapacker copies the default value in if necessary. The Panel message in data.proto was also renamed to PanelData because otherwise it conflicts with the native Godot class named Panel. The double field in Letter was renamed to level2, because Godot couldn't handle it well. Finally, common.proto was removed and its contents were moved into data.proto, which allows us to generate code for Python without needing to edit it. NOTE: I had to slightly modify the Godot protobuf code generator. I'll need to upload that somewhere.
* Add gravity to paintings/portsStar Rauchenberger2025-08-111-2/+6
|
* Assigned IDs for the_congruentStar Rauchenberger2025-08-103-0/+91
| | | | | Keyholders are packed now. Doors can rely on keyholders and rooms. Paintings can be exit only.
* Assigned IDs for the_colorfulStar Rauchenberger2025-08-102-0/+21
| | | | | | Also fixed bug where the ID assigner didn't read letter and mastery IDs and would thus reuse them. I reassigned all IDs because of this (since we don't need to worry about ID stability yet).
* Added support for masteriesStar Rauchenberger2025-08-099-7/+196
| | | | | Also assigned IDs for the_butterfly, as well as already configured letters.
href='#n203'>203 204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

                                       















                                   

                           

              

                                                                         


                                                  

                                     
                                







                                                            



                                                                                             






                                                                                
                                                                                             








                                                                                              
 












                                                                                                              


                                                                                          


                                                                             

                                                                      
                                  











                                                                                             




                                                                                                             



                                                                                                        










                                                                                                                   
                                   











                                                                                              
                                  











                                                                                             






                                                                                                    























                                                                                             
                                            
                                                                    






























                                                                                        
                                     
                                        


                                                                                                     

















                                                                                                         
                      
 




                                      















                                                                                                       
extends "res://scripts/nodes/player.gd"

const kEndingNameByVictoryValue = {
	0: "GRAY",
	1: "PURPLE",
	2: "MINT",
	3: "BLACK",
	4: "BLUE",
	5: "CYAN",
	6: "RED",
	7: "PLUM",
	8: "ORANGE",
	9: "GOLD",
	10: "YELLOW",
	11: "GREEN",
	12: "WHITE",
}

signal evaluate_solvability


func _ready():
	var khl_script = load("res://scripts/nodes/keyHolderListener.gd")

	var ap = global.get_node("Archipelago")
	var gamedata = global.get_node("Gamedata")

	ap.start_batching_locations()

	# Set up door locations.
	var map_id = gamedata.map_id_by_name.get(global.map)
	for door in gamedata.objects.get_doors():
		if door.get_map_id() != map_id:
			continue

		if not door.has_ap_id():
			continue

		if (
			door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
			or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING
		):
			continue

		var locationListener = ap.SCRIPT_locationListener.new()
		locationListener.location_id = door.get_ap_id()
		locationListener.name = "locationListener_%d" % door.get_ap_id()

		for panel_ref in door.get_panels():
			var panel_data = gamedata.objects.get_panels()[panel_ref.get_panel()]
			var panel_path = panel_data.get_path()

			if panel_ref.has_answer():
				for proxy in panel_data.get_proxies():
					if proxy.get_answer() == panel_ref.get_answer():
						panel_path = proxy.get_path()
						break

			locationListener.senders.append(NodePath("/root/scene/" + panel_path))

		for keyholder_ref in door.get_keyholders():
			var keyholder_data = gamedata.objects.get_keyholders()[keyholder_ref.get_keyholder()]

			var khl = khl_script.new()
			khl.name = (
				"location_%d_keyholder_%d" % [door.get_ap_id(), keyholder_ref.get_keyholder()]
			)
			khl.answer = keyholder_ref.get_key()
			khl.senders.append(NodePath("/root/scene/" + keyholder_data.get_path()))
			get_parent().add_child.call_deferred(khl)

			locationListener.senders.append(NodePath("../" + khl.name))

		for sender in door.get_senders():
			locationListener.senders.append(NodePath("/root/scene/" + sender))

		if door.has_complete_at():
			locationListener.complete_at = door.get_complete_at()

		get_parent().add_child.call_deferred(locationListener)

	# Set up letter locations.
	for letter in gamedata.objects.get_letters():
		var room = gamedata.objects.get_rooms()[letter.get_room_id()]
		if room.get_map_id() != map_id:
			continue

		var locationListener = ap.SCRIPT_locationListener.new()
		locationListener.location_id = letter.get_ap_id()
		locationListener.name = "locationListener_%d" % letter.get_ap_id()
		locationListener.senders.append(NodePath("/root/scene/" + letter.get_path()))

		get_parent().add_child.call_deferred(locationListener)

		if (
			ap.get_letter_behavior(letter.get_key(), letter.has_level2() and letter.get_level2())
			!= ap.kLETTER_BEHAVIOR_VANILLA
		):
			var scout = ap.scout_location(letter.get_ap_id())
			if (
				scout != null
				and not (scout["player"] == ap.client._slot and scout["flags"] & 4 != 0)
			):
				var item_name = "Unknown"
				var item_player_game = ap.client._game_by_player[float(scout["player"])]
				if ap.client._item_id_to_name[item_player_game].has(scout["item"]):
					item_name = ap.client._item_id_to_name[item_player_game][scout["item"]]

					var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
						letter.get_path()
					)
					if collectable != null:
						collectable.setScoutedText.call_deferred(item_name)

	# Set up mastery locations.
	for mastery in gamedata.objects.get_masteries():
		var room = gamedata.objects.get_rooms()[mastery.get_room_id()]
		if room.get_map_id() != map_id:
			continue

		var locationListener = ap.SCRIPT_locationListener.new()
		locationListener.location_id = mastery.get_ap_id()
		locationListener.name = "locationListener_%d" % mastery.get_ap_id()
		locationListener.senders.append(NodePath("/root/scene/" + mastery.get_path()))

		get_parent().add_child.call_deferred(locationListener)

	# Set up ending locations.
	for ending in gamedata.objects.get_endings():
		var room = gamedata.objects.get_rooms()[ending.get_room_id()]
		if room.get_map_id() != map_id:
			continue

		var locationListener = ap.SCRIPT_locationListener.new()
		locationListener.location_id = ending.get_ap_id()
		locationListener.name = "locationListener_%d" % ending.get_ap_id()
		locationListener.senders.append(NodePath("/root/scene/" + ending.get_path()))

		get_parent().add_child.call_deferred(locationListener)

		if kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name():
			var victoryListener = ap.SCRIPT_victoryListener.new()
			victoryListener.name = "victoryListener"
			victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path()))

			get_parent().add_child.call_deferred(victoryListener)

	# Set up keyholder locations, in keyholder sanity.
	if ap.keyholder_sanity:
		for keyholder in gamedata.objects.get_keyholders():
			if not keyholder.has_key():
				continue

			var room = gamedata.objects.get_rooms()[keyholder.get_room_id()]
			if room.get_map_id() != map_id:
				continue

			var locationListener = ap.SCRIPT_locationListener.new()
			locationListener.location_id = keyholder.get_ap_id()
			locationListener.name = "locationListener_%d" % keyholder.get_ap_id()

			var khl = khl_script.new()
			khl.name = "location_%d_keyholder" % keyholder.get_ap_id()
			khl.answer = keyholder.get_key()
			khl.senders.append(NodePath("/root/scene/" + keyholder.get_path()))
			get_parent().add_child.call_deferred(khl)

			locationListener.senders.append(NodePath("../" + khl.name))

			get_parent().add_child.call_deferred(locationListener)

	# Block off roof access in Daedalus.
	if global.map == "daedalus" and not ap.daedalus_roof_access:
		_set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49)
		_set_up_invis_wall(51.5, 11, -17, 16, 10, 1)
		_set_up_invis_wall(46, 10, -9.5, 1, 10, 10)
		_set_up_invis_wall(67.5, 11, 17, 16, 10, 1)
		_set_up_invis_wall(50.5, 11, 14, 10, 10, 1)
		_set_up_invis_wall(39, 10, 18.5, 1, 10, 22)
		_set_up_invis_wall(20, 15, 18.5, 1, 10, 16)
		_set_up_invis_wall(11.5, 15, 3, 32, 10, 1)
		_set_up_invis_wall(11.5, 16, -20, 14, 20, 1)
		_set_up_invis_wall(14, 16, -26.5, 1, 20, 4)
		_set_up_invis_wall(28.5, 20.5, -26.5, 1, 15, 25)
		_set_up_invis_wall(40.5, 20.5, -11, 30, 15, 1)
		_set_up_invis_wall(50.5, 15, 5.5, 7, 10, 1)
		_set_up_invis_wall(83.5, 33.5, 5.5, 1, 7, 11)
		_set_up_invis_wall(83.5, 33.5, -5.5, 1, 7, 11)

		var warp_exit_prefab = preload("res://objects/nodes/exit.tscn")
		var warp_exit = warp_exit_prefab.instantiate()
		warp_exit.name = "roof_access_blocker_warp_exit"
		warp_exit.position = Vector3(58, 10, 0)
		warp_exit.rotation_degrees.y = 90
		get_parent().add_child.call_deferred(warp_exit)

		var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn")
		var warp_enter = warp_enter_prefab.instantiate()
		warp_enter.target = warp_exit
		warp_enter.position = Vector3(76.5, 30, 1)
		warp_enter.scale = Vector3(4, 1.5, 1)
		warp_enter.rotation_degrees.y = 90
		get_parent().add_child.call_deferred(warp_enter)

	if global.map == "the_entry":
		# Remove door behind X1.
		var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1")
		door_node.handleTriggered()

		# Display win condition.
		var sign_prefab = preload("res://objects/nodes/sign.tscn")
		var sign1 = sign_prefab.instantiate()
		sign1.position = Vector3(-7, 5, -15.01)
		sign1.text = "victory"
		get_parent().add_child.call_deferred(sign1)

		var sign2 = sign_prefab.instantiate()
		sign2.position = Vector3(-7, 4, -15.01)
		sign2.text = "%s ending" % kEndingNameByVictoryValue.get(ap.victory_condition, "?")

		var sign2_color = kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower()
		if sign2_color == "white":
			sign2_color = "silver"

		sign2.material = load("res://assets/materials/%s.material" % sign2_color)
		get_parent().add_child.call_deferred(sign2)

	super._ready()

	await get_tree().process_frame
	await get_tree().process_frame

	ap.stop_batching_locations()


func _set_up_invis_wall(x, y, z, sx, sy, sz):
	var prefab = preload("res://objects/nodes/block.tscn")
	var newwall = prefab.instantiate()
	newwall.position.x = x
	newwall.position.y = y
	newwall.position.z = z
	newwall.scale.x = sz
	newwall.scale.y = sy
	newwall.scale.z = sx
	newwall.set_surface_override_material(0, preload("res://assets/materials/blackMatte.material"))
	newwall.visibility_range_end = 3
	newwall.visibility_range_end_margin = 1
	newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF
	newwall.skeleton = ".."
	get_parent().add_child.call_deferred(newwall)