about summary refs log blame commit diff stats
path: root/Archipelago/load.gd
blob: 673c762665f75f79c59844ef87a47245702f1d49 (plain) (tree)
1
2
3
4
5
6
7
8
9
                               




                            
                           





                         
                          
 



                                                     
                                                   
 







                                                   









                                                                                            



                                                                                 
 




                                                           
                                                     































                                                                                                 









                                                                                              
                                                                    
 
                                                                                    








                                                                 
                                                                             
 
                                             







                                                                               
                                             




                                                                                        
 
                                                                                  
                                                    
                                                      
                                                                    
                                                                                                                     
                                

                                                                 













                                                                                         
 








                                                                               


                                                                                     





                                                                          


                                             



                                                                                   
 

                                                                                        
 



                                                                                     


                                                            


                                                             
                                                                           
                                                                             






                                                                                                   































                                                                                    
 
                                                                 
                                                         
                                                     
 
                                                                      

                                                   
 
                                                                 
                                          









                                                              




                                                                                               



                                                                    
                                                                                                                  


























                                                                                                    

                                                                                                                                 









                                                                                                        

                                                                                   
                                                                             






                                                                                
                                                              
                                                                 





                                                                                            









                                                                                               
                                                                                   
                                                                  

























                                                                
                                                










                                                                                                          

                                           







                                                                                    
                                               













                                                                                              
                                                   


                                                                            
                                                          




                                                                                                 






























                                                                                              
                                  


                                                                                              
 
                                                                       
                                                      






                                                                                     




                                                                                                                                                            



















                                                                                            
 
                                                                                    
                                                
                                     



                                                                                       




                                                                                                 
                                 
                                                               


                                                                                             



                                                                                 



                                                                                    



                                                                                 














                                                                                


















































                                                                                                                                                       
 
                                  
                                                    


                                          




                                                                        

                                                                               

                                            
 







                                                                        

                                                                                   
 
                                            



                                                                               
                                    
 
                                    
 








                                                         






                                                                


                                                                                        
 
                                                          

                                              
                                                    






                                                                   

                                                             
                                         
                       
 






                                                                                                 






                                                                                      






                                                                                                 
                                      
                                    










                            
extends "res://scripts/load.gd"

const EXCLUDED_PAINTINGS = [
	"ascension.tscn",
	"ascension_ne.tscn",
	"ascension_nw.tscn",
	"ascension_se.tscn",
	"ascension_sw.tscn",
	"dan_L1_gate.tscn",
	"frame.tscn",
	"scenery_0.tscn",
	"scenery_1.tscn",
	"scenery_2.tscn",
	"scenery_3.tscn",
	"scenery_4.tscn",
	"scenery_5.tscn",
	"pilgrim.tscn",
	"so_pearl_bk.tscn"
]


func _load():
	global._print("Hooked Load Start")

	var apclient = global.get_node("Archipelago")
	var panels_parent = self.get_node("Panels")

	# Add a wall-blocked raycast to the player.
	var wallcast = RayCast.new()
	wallcast.name = "wallcast"
	wallcast.cast_to = Vector3(0, 0, -15)
	wallcast.set_collision_mask_bit(1, true)
	wallcast.set_collision_mask_bit(3, true)
	wallcast.collide_with_areas = true
	$player/pivot/camera.add_child(wallcast)

	# Override the YOU panel with the AP slot name.
	if self.get_node_or_null("Panels/Color Arrow Room/Panel_you") != null:
		self.get_node("Panels/Color Arrow Room/Panel_you").answer = apclient.ap_user
	for node in get_tree().get_nodes_in_group("text_you"):
		if "text" in node:
			node.text = apclient.ap_user
		elif "value" in node:
			node.value = apclient.ap_user
	for node in get_tree().get_nodes_in_group("answer_you"):
		if "answer" in node:
			node.answer = apclient.ap_user
	
	# Hide the countdown true panels.
	for child in $CountdownPanels.get_children():
		if child.is_class("Spatial"):
			child.get_node(child.replace_with).translation.y -= 100.0

	# Create "The Wanderer".
	set_gridmap_tile(-4.5, 6.5, 56.5, "MeshInstance4")
	set_gridmap_tile(-3.5, 6.5, 56.5, "MeshInstance18")
	set_gridmap_tile(-3.5, 6.5, 57.5, "MeshInstance5")

	var door_scene = load("res://nodes/door.tscn")
	var door_script = apclient.SCRIPT_doorControl
	var wanderer_entrance = door_scene.instance()
	wanderer_entrance.name = "Door_wanderer_entrance"
	wanderer_entrance.translation = Vector3(7.5, 5, 53)
	wanderer_entrance.rotation = Vector3(0, -PI / 2, 0)
	wanderer_entrance.scale = Vector3(1, 1.5, 1)
	wanderer_entrance.set_script(door_script)
	wanderer_entrance.panels.append("../../../Panels/Tower Room/Panel_wanderlust_1234567890")
	get_node("Doors/Tower Room Area Doors").add_child(wanderer_entrance)

	var wanderer_achieve = get_node("Panels/Tower Room/Panel_1234567890_wanderlust")
	wanderer_achieve.get_parent().remove_child(wanderer_achieve)
	get_node("Panels/Countdown Panels").add_child(wanderer_achieve)

	var countdown_scene = load("res://nodes/panel_countdown.tscn")
	var wanderer_cdp = countdown_scene.instance()
	wanderer_cdp.name = "CountdownPanel_wanderer"
	wanderer_cdp.panels = [
		"../../Panels/Tower Room/Panel_wanderlust_1234567890",
		"../../Panels/Orange Room/Panel_lust",
		"../../Panels/Orange Room/Panel_read",
		"../../Panels/Orange Room/Panel_sew",
		"../../Panels/Orange Room/Panel_dead",
		"../../Panels/Orange Room/Panel_learn",
		"../../Panels/Orange Room/Panel_dust",
		"../../Panels/Orange Room/Panel_star",
		"../../Panels/Orange Room/Panel_wander"
	]
	wanderer_cdp.translation = wanderer_achieve.translation
	wanderer_cdp.rotation = wanderer_achieve.rotation
	get_node("CountdownPanels").add_child(wanderer_cdp)

	wanderer_achieve.translation = Vector3(-51, -33, 35)  # way under the map

	# Turn THE COLORFUL into a cdp.
	var real_colorful = panels_parent.get_node("Countdown Panels/Panel_colorful_colorful")
	var cdp_auto_scene = load("res://nodes/panel_countdown_auto.tscn")
	var colorful_cdp = cdp_auto_scene.instance()
	colorful_cdp.name = "CountdownPanel_colorful"
	colorful_cdp.replace_with = "../../Panels/Countdown Panels/Panel_colorful_colorful"
	colorful_cdp.panels = "../../Panels/Doorways Room"
	colorful_cdp.translation = real_colorful.translation
	colorful_cdp.rotation = real_colorful.rotation
	get_node("CountdownPanels").add_child(colorful_cdp)
	real_colorful.translation = Vector3(-51, -35, 35)  # way under the map
	get_node("Doors/Doorway Room Doors/Door_gray2").queue_free()

	# Set up The Master to be variable.
	var old_master_cdp = get_node("CountdownPanels/CountdownPanel_countdown_16")
	var new_master_cdp = cdp_auto_scene.instance()
	new_master_cdp.name = "AP_variable_master"
	new_master_cdp.replace_with = old_master_cdp.replace_with
	new_master_cdp.panels = "../../Panels/Countdown Panels"
	new_master_cdp.maxlength = apclient._mastery_achievements
	new_master_cdp.translation = old_master_cdp.translation
	new_master_cdp.rotation = old_master_cdp.rotation
	get_node("CountdownPanels").add_child(new_master_cdp)
	old_master_cdp.queue_free()

	# Configure AN OTHER WAY.
	var another_cdp = get_node("CountdownPanels/CountdownPanel_level2_0")

	if apclient._level_2_requirement > 1:
		var new_another_cdp = cdp_auto_scene.instance()
		new_another_cdp.name = "AP_variable_another"
		new_another_cdp.replace_with = another_cdp.replace_with
		new_another_cdp.panels = another_cdp.panels
		new_another_cdp.translation = another_cdp.translation
		new_another_cdp.rotation = another_cdp.rotation
		new_another_cdp.maxlength = (apclient._level_2_requirement - 1)
		new_another_cdp.nested = true
		get_node("CountdownPanels").add_child(new_another_cdp)
		another_cdp.replace_with = ""
		another_cdp.queue_free()
	else:
		var another_replacement = another_cdp.get_node(another_cdp.replace_with)
		another_replacement.translation = another_cdp.translation
		another_replacement.rotation = another_cdp.rotation
		another_cdp.queue_free()

	# This is the best time to create the location nodes, since the map is now
	# loaded but the panels haven't been solved from the save file yet.
	var gamedata = apclient.get_node("Gamedata")
	var location_script = apclient.SCRIPT_location
	for location_id in gamedata.panel_ids_by_location_id.keys():
		if not (gamedata.classification_by_location_id[location_id] & apclient._location_classification_bit):
			continue

		var location = location_script.new()
		location.ap_id = location_id
		location.name = "AP_location_%d" % location.ap_id
		self.add_child(location)

		var panels = gamedata.panel_ids_by_location_id[location_id]
		location.total = panels.size()

		for panel in panels:
			var that_panel
			if panel.begins_with("EndPanel"):
				that_panel = self.get_node("Decorations").get_node(panel)
			else:
				that_panel = panels_parent.get_node(panel)

			that_panel.get_node("Viewport/GUI/Panel/TextEdit").connect(
				"answer_correct", location, "handle_correct"
			)

	# HOT CRUSTS should be at eye-level, have a yellow block behind it, and
	# not vanish when solved.
	var hotcrusts = panels_parent.get_node("Shuffle Room/Panel_shortcuts")
	hotcrusts.translation.y = 1.5
	hotcrusts.get_node("Viewport/GUI/Panel/TextEdit").disconnect(
		"answer_correct", hotcrusts, "handle_correct"
	)

	set_gridmap_tile(-20.5, 1.5, -79.5, "MeshInstance9")

	# TRANS RIGHTS should be bottom white, like it used to be.
	var trans_rights = panels_parent.get_node("Shuffle Room/Panel_secret_secret")
	trans_rights.translation.y = 0.5

	# Move LOW/LOW back to where it used to be.
	var panel_low = panels_parent.get_node("Entry Room/Panel_low_low")
	var sign_low = $Decorations/PanelSign/sign21
	panel_low.translation = sign_low.translation
	panel_low.rotation = sign_low.rotation
	sign_low.queue_free()

	# Randomize the panels, if necessary.
	var rng = RandomNumberGenerator.new()
	rng.seed = apclient._slot_seed

	# Remove opaque wall in front of FOURTH.
	set_gridmap_tile(-71.5, 1.5, -64.5, "MeshInstance18")

	# Move The Lab's OPEN out of the wall.
	panels_parent.get_node("Chemistry Room/Panel_open").translation.x = -87.001

	# Rotate ZERO toward the front, and remove the black door.
	panels_parent.get_node("Backside Room/Panel_zero_zero").rotation_degrees.y = 180
	get_node("Doors/Count Up Room Area Doors/Door_zero_hider_3").queue_free()

	# Block the roof access to The Wondrous.
	for x in range(0, 3):
		for z in range(0, 3):
			set_gridmap_tile(-95.5 - x, -3.5, -44.5 - z, "MeshInstance4")

	# Block visibility of RAINY from the roof.
	set_gridmap_tile(-88.5, 4.5, -41.5, "MeshInstance8")
	set_gridmap_tile(-89.5, 4.5, -41.5, "MeshInstance4")

	# Remove black block from THE RED.
	clear_gridmap_tile(68.5, 6.5, 76.5)
	get_node("Decorations/PanelSign/sign19").queue_free()

	if apclient.confusify_world:
		# Remove welcome back / color hallway / sunwarp indicators.
		get_node("Decorations/Signs/Welcome Back Signs").queue_free()
		
		# Remove tower floor indicators.
		get_node("Decorations/Signs/Tower Signs").queue_free()

		# Turn tower doors black.
		var material_asset = load("res://materials/matteBlackBlock.material")
		for door in get_node("Doors/Tower Room Area Doors").get_children():
			door.get_node("Hinge/MeshInstance").set_surface_material(0, material_asset)
		
		# Make hidden doors white and flush with surrounding walls.
		var hidden_parent = get_node("Decorations/HiddenDoors")
		hidden_parent.get_node("hidden_door_1").translation.z = -47
		hidden_parent.get_node("hidden_door_2").translation.x = -8
		hidden_parent.get_node("hidden_door_3").translation.x = 17
		hidden_parent.get_node("hidden_door_4").translation.z = -69
		hidden_parent.get_node("hidden_door_4")._setReference("whiteBlock")
		hidden_parent.get_node("hidden_door_5").translation.x = -82
		hidden_parent.get_node("hidden_door_5")._setReference("whiteBlock")
		hidden_parent.get_node("hidden_door_6").translation.z = -2
		hidden_parent.get_node("hidden_door_7").translation.z = 32
		hidden_parent.get_node("hidden_door_8").translation.x = 28
		hidden_parent.get_node("hidden_door_8")._setReference("whiteBlock")
		hidden_parent.get_node("hidden_door_9").translation.x = -32
		hidden_parent.get_node("hidden_door_9")._setReference("whiteBlock")
		hidden_parent.get_node("hidden_door_10").translation.z = 5
		hidden_parent.get_node("hidden_door_12").translation.z = -30
		hidden_parent.get_node("hidden_door_13").translation.z = -39
		hidden_parent.get_node("hidden_door_14").translation.z = -39
		hidden_parent.get_node("hidden_door_16")._setReference("whiteBlock")
		hidden_parent.get_node("hidden_door_17").translation.x = -86
		hidden_parent.get_node("hidden_door_27").translation.z = -6
		hidden_parent.get_node("hidden_door_27")._setReference("whiteBlock")
		hidden_parent.get_node("hidden_door_31").translation.z = 17
		hidden_parent.get_node("hidden_door_32").translation.z = -93
		hidden_parent.get_node("hidden_door_38").translation.z = -40
		hidden_parent.get_node("hidden_door_46").translation.z = -72
		hidden_parent.get_node("hidden_door_47").translation.z = -55
		hidden_parent.get_node("hidden_door_57").translation.z = -20
		hidden_parent.get_node("hidden_door_57")._setReference("whiteBlock")
		hidden_parent.get_node("hidden_door_58").translation.x = 48
		hidden_parent.get_node("hidden_door_58")._setReference("whiteBlock")

	if apclient._panel_shuffle != apclient.kNO_PANEL_SHUFFLE:
		# Make The Wondrous's FIRE solely midred.
		clear_gridmap_tile(-76.5, 1.5, -73.5)

		# Reduce double/triple length puzzles in Knight/Night.
		clear_gridmap_tile(24.5, 1.5, 11.5)
		clear_gridmap_tile(25.5, 1.5, 11.5)
		clear_gridmap_tile(47.5, 1.5, 11.5)

	if apclient._panel_shuffle == apclient.kREARRANGE_PANELS:
		# Do the actual shuffling.
		var panel_pools = {}
		for panel in gamedata.panels:
			if not panel_pools.has(panel["tag"]):
				panel_pools[panel["tag"]] = {}
			var pool = panel_pools[panel["tag"]]
			var subtag = "default"
			if panel.has("subtag"):
				subtag = panel["subtag"]
			if not pool.has(subtag):
				pool[subtag] = []

			var panel_node
			if panel["id"].begins_with("EndPanel"):
				panel_node = self.get_node("Decorations").get_node(panel["id"])
			else:
				panel_node = panels_parent.get_node(panel["id"])

			pool[subtag].append(
				{
					"id": panel["id"],
					"hint": panel_node.text,
					"answer": panel_node.answer,
					"link": panel["link"] if panel.has("link") else "",
					"copy_to_sign": panel["copy_to_sign"] if panel.has("copy_to_sign") else ""
				}
			)

		for tag in panel_pools.keys():
			if tag == "forbid":
				continue

			var pool = panel_pools[tag]
			for subtag in pool.keys():
				pool[subtag].sort_custom(self, "sort_by_link")

			var count = pool[pool.keys()[0]].size()
			var iota = range(0, count)
			var order = []
			while not iota.empty():
				var i = rng.randi_range(0, iota.size() - 1)
				order.append(iota[i])
				iota.remove(i)

			for subtag in pool.keys():
				for i in range(0, count):
					var source = pool[subtag][i]
					var target = pool[subtag][order[i]]
					var target_panel_node = panels_parent.get_node(target["id"])

					target_panel_node.text = source["hint"]
					target_panel_node.answer = source["answer"]

					for sign_name in target["copy_to_sign"]:
						self.get_node("Decorations/PanelSign").get_node(sign_name).value = source["hint"]

		# Change the answer to the final puzzle in the art gallery based on the
		# puzzles that were shuffled into the constituent places.
		var new_answer = panels_parent.get_node("Painting Room/Panel_eon_one").answer
		new_answer += " "
		new_answer += panels_parent.get_node("Painting Room/Panel_path_road").answer
		new_answer += " "
		new_answer += panels_parent.get_node("Painting Room/Panel_any_many").answer
		new_answer += " "
		new_answer += panels_parent.get_node("Painting Room/Panel_send_use_turns").answer
		panels_parent.get_node("Painting Room/Panel_order_onepathmanyturns").answer = new_answer

	# Handle our other static panels after panel randomization, so that the old
	# values can enter the pool, if necessary.
	set_static_panel("Entry Room/Panel_hi_hi", "hi")
	set_static_panel("Entry Room/Panel_write_write", apclient.my_version)
	set_static_panel("Entry Room/Panel_same_same", str(apclient._slot_seed))
	set_static_panel("Entry Room/Panel_type_type", "victory")

	var victory_condition = "unknown"
	if apclient._victory_condition == apclient.kTHE_END:
		victory_condition = "the end"
	elif apclient._victory_condition == apclient.kTHE_MASTER:
		victory_condition = "the master"
	elif apclient._victory_condition == apclient.kLEVEL_2:
		victory_condition = "level 2"
	elif apclient._victory_condition == apclient.kPILGRIMAGE:
		victory_condition = "pilgrimage"

	set_static_panel("Entry Room/Panel_this_this", victory_condition)
	set_static_panel("Entry Room/Panel_hidden_hidden", "hewwo")
	set_static_panel("Entry Room/Panel_hi_high", "goode", "good")
	set_static_panel("Entry Room/Panel_low_low", "serendipity", "luck")
	set_static_panel("Shuffle Room/Panel_secret_secret", "trans rights", "human rights")

	# Finish up with The Wanderer.
	wanderer_achieve.text = "12345656"
	wanderer_achieve.answer = "the wanderer"
	wanderer_achieve.achieved_text = "the wanderer"

	wanderer_cdp.replace_with = "../../Panels/Countdown Panels/Panel_1234567890_wanderlust"

	get_node("Doors/Tower Room Area Doors/Door_wanderlust_start").panels = [
		"../../../Panels/Countdown Panels/Panel_1234567890_wanderlust"
	]

	if apclient._early_color_hallways:
		var painting_scene = load("res://nodes/paintings/dan_L1_gate.tscn")
		var mypainting_script = apclient.SCRIPT_mypainting

		var exit_painting = painting_scene.instance()
		exit_painting.set_name("color_exit_painting")
		exit_painting.translation.x = -98.75
		exit_painting.translation.y = 1
		exit_painting.translation.z = 3.5
		exit_painting.rotation_degrees.y = -90

		var exit_mps = mypainting_script.new()
		exit_mps.set_name("Script")
		exit_mps.orientation = "west"
		exit_painting.add_child(exit_mps)
		$Decorations/Paintings.add_child(exit_painting)

		var enter_painting = painting_scene.instance()
		enter_painting.set_name("color_enter_painting")
		enter_painting.translation.x = 4.5
		enter_painting.translation.y = 1
		enter_painting.translation.z = 6.75

		var enter_mps = mypainting_script.new()
		enter_mps.set_name("Script")
		enter_mps.orientation = "south"
		enter_mps.target = exit_mps
		enter_painting.add_child(enter_mps)
		$Decorations/Paintings.add_child(enter_painting)

	# Randomize the paintings, if necessary.
	if apclient._painting_shuffle:
		var paintings_dir = Directory.new()
		var all_paintings = []
		if paintings_dir.open("res://nodes/paintings") == OK:
			paintings_dir.list_dir_begin()
			var file_name = paintings_dir.get_next()
			while file_name != "":
				if file_name.ends_with(".tscn") and not EXCLUDED_PAINTINGS.has(file_name):
					all_paintings.append(file_name.trim_suffix(".tscn"))
				file_name = paintings_dir.get_next()
			paintings_dir.list_dir_end()

		var pd = Node.new()
		pd.set_name("AP_Paintings")
		self.add_child(pd)

		var classes = {}
		for painting in apclient._paintings_mapping.values():
			if not classes.has(painting):
				var i = rng.randi_range(0, all_paintings.size() - 1)
				var chosen = all_paintings[i]
				classes[painting] = chosen
				all_paintings.remove(i)

		var randomized = []
		for painting in classes.keys():
			var painting_class = classes[painting]
			instantiate_painting(painting, painting_class)
			randomized.append(painting)

		for source_painting in apclient._paintings_mapping.keys():
			var target_painting = apclient._paintings_mapping[source_painting]
			var painting_class = classes[target_painting]
			var new_source = instantiate_painting(source_painting, painting_class)
			new_source.target = pd.get_node(target_painting).get_node("Script")
			randomized.append(source_painting)

		var remaining_size = classes.size() / 2
		if remaining_size >= all_paintings.size():
			remaining_size = all_paintings.size()
		var remaining = []
		for _i in range(0, remaining_size):
			var j = rng.randi_range(0, all_paintings.size() - 1)
			remaining.append(all_paintings[j])
			all_paintings.remove(j)

		for painting in gamedata.paintings.keys():
			if randomized.has(painting):
				continue

			var chosen_painting = remaining[rng.randi_range(0, remaining.size() - 1)]
			instantiate_painting(painting, chosen_painting)

	# We need to make some changes to the Art Gallery. The player should always
	# have access to the backroom, but they shouldn't have access to ORDER until
	# getting the fifth floor, so will move the backroom door. Also, the
	# paintings in the backroom should only show up as the player gets the
	# progressive art gallery items.
	var backroom_door = get_node("Doors/Tower Room Area Doors/Door_painting_backroom")
	backroom_door.translation.x = 97
	backroom_door.translation.y = 0
	backroom_door.translation.z = 39
	backroom_door.scale.x = 2
	backroom_door.scale.y = 2.5
	backroom_door.scale.z = 1

	for i in range(2, 6):
		var painting_path = "Decorations/Paintings/scenery_painting_%db" % i
		var painting_node = get_node(painting_path)
		var rotate = painting_node.rotate
		var target_path = painting_node.target_path
		painting_node.set_script(load("res://scripts/painting_eye.gd"))
		painting_node.rotate = rotate
		painting_node.target_path = target_path
		painting_node.move_to_x = painting_node.translation.x
		painting_node.move_to_z = painting_node.translation.z
		painting_node.translation.x = 88
		painting_node.translation.z = 39

		var ref_painting_path = "Decorations/Paintings/scenery_painting_%dc" % i
		var ref_painting_node = get_node(ref_painting_path)
		painting_node.key_path = ref_painting_node.key_path
		painting_node._ready() # shows an error about looked_at being double connected

	# If door shuffle is on, we need to add an extra door to The Fearless.
	if apclient._door_shuffle:
		var fearless_door = get_node("Doors/Naps Room Doors/Door_hider_5").duplicate()
		fearless_door.name = "Door_hider_new1"
		fearless_door.translation.y = 5
		get_node("Doors/Naps Room Doors").add_child(fearless_door)

	# Set up notifiers for each achievement panel, for the tracker.
	var notifier_script = apclient.SCRIPT_notifier
	for panel in gamedata.panels:
		if "achievement" in panel:
			var panel_node = panels_parent.get_node(panel["id"])
			var script_instance = notifier_script.new()
			script_instance.name = "Achievement_Notifier"
			script_instance.key = "Achievement|%s" % panel["achievement"]
			panel_node.add_child(script_instance)

		if "hunt" in panel and panel["hunt"] and not (gamedata.classification_by_location_id[panel["loc"]] & apclient._location_classification_bit):
			var panel_node = panels_parent.get_node(panel["id"])
			var script_instance = notifier_script.new()
			script_instance.name = "Hunt_Notifier"
			script_instance.key = "Hunt|%d" % panel["loc"]
			panel_node.add_child(script_instance)
	
	# Make stack/double puzzles into proxies, unless panelsanity is on.
	if apclient._location_classification_bit != apclient.kCLASSIFICATION_LOCAL_INSANITY:
		var proxyscript = load("res://scripts/panelProxy.gd")

		var extradata_proxies = apclient.get_node("Extradata").proxies
		for truepanel in extradata_proxies:
			var proxies = extradata_proxies[truepanel]
			for proxypanel in proxies:
				var proxynode = panels_parent.get_node(proxypanel)
				var oldparent = proxynode.get_parent()
				oldparent.remove_child(proxynode)
				var oldtext = proxynode.text
				var oldanswer = proxynode.answer
				proxynode.set_script(proxyscript)
				proxynode.text = oldtext
				proxynode.answer = oldanswer
				proxynode.proxied_panels = ["../../%s" % truepanel]
				proxynode.exact_proxy = true
				proxynode.request_ready()
				oldparent.add_child(proxynode)

	# Attach a script to every panel so that we can do things like conditionally
	# disable them.
	var panel_script = apclient.SCRIPT_panel
	for panel in gamedata.panels:
		var panel_node
		if panel["id"].begins_with("EndPanel"):
			panel_node = self.get_node("Decorations").get_node(panel["id"])
		else:
			panel_node = panels_parent.get_node(panel["id"])
		var script_instance = panel_script.new()
		script_instance.name = "AP_Panel"
		script_instance.data = panel
		panel_node.add_child(script_instance)
		apclient.connect("evaluate_solvability", script_instance, "evaluate_solvability")

	# Hook up the goal panel.
	if apclient._victory_condition == apclient.kTHE_MASTER:
		var the_master = self.get_node("Panels/Countdown Panels/Panel_master_master")
		the_master.get_node("Viewport/GUI/Panel/TextEdit").connect(
			"answer_correct", apclient, "completedGoal"
		)
	elif apclient._victory_condition == apclient.kLEVEL_2:
		var level_2 = self.get_node("Decorations/EndPanel/Panel_level_2")
		level_2.get_node("Viewport/GUI/Panel/TextEdit").connect(
			"answer_correct", apclient, "completedGoal"
		)
	elif apclient._victory_condition == apclient.kPILGRIMAGE:
		var pilgrim_panel = self.get_node("Panels/Lingo Room/Panel_pilgrim")
		pilgrim_panel.get_node("Viewport/GUI/Panel/TextEdit").connect(
			"answer_correct", apclient, "completedGoal"
		)
	else:
		var the_end = self.get_node("Decorations/EndPanel/Panel_end_end")
		the_end.get_node("Viewport/GUI/Panel/TextEdit").connect(
			"answer_correct", apclient, "completedGoal"
		)
	
	# If pilgrimage does not allow roof access, add a node on the Crossroads
	# Roof Access stairs that disables it.
	if !apclient._pilgrimage_allows_roof_access:
		var terminator = apclient.SCRIPT_pilgrimage_terminator.new()
		terminator.name = "RoofAccessPilgrimageTerminator"
		terminator.translation.x = -36
		terminator.translation.y = 3
		terminator.translation.z = -35

		var terminator_shape = CollisionShape.new()
		terminator_shape.shape = BoxShape.new()
		terminator_shape.shape.extents.x = 0.1

		terminator.add_child(terminator_shape)
		get_node("Decorations").add_child(terminator)
	
	if apclient._sunwarp_shuffle:
		# Sunwarps 1 and 6 are rotated differently from the rest, so we have to fix that.
		get_node("Decorations/Teleporter Windows/localmap").rotation_degrees.y = 0
		get_node("Decorations/Teleporter Windows/localmap2").rotation_degrees.y = 0
		get_node("Decorations/Teleporter Windows/localmap11").rotation_degrees.y = 0
		get_node("Decorations/Teleporter Windows/localmap12").rotation_degrees.y = 0
		get_node("Decorations/Teleporter Windows/localmap13").rotation_degrees.y = -90

		get_node("Warps/Teleporter Warps/Sunwarp_enter_1").translation.x = 19.5
		get_node("Warps/Teleporter Warps/Sunwarp_exit_1").translation.x = -15.5
		get_node("Warps/Teleporter Warps/Sunwarp_enter_6").translation.x = 4.5
		get_node("Warps/Teleporter Warps/Sunwarp_exit_6").translation.x = -37.5
		get_node("Warps/Teleporter Warps/Sunwarp_exit_7").translation.z = 23.5

		# Change the sunwarps in accordance with the mapping.
		var sw_orig_translations = []
		var sw_text_translations = []
		var sw_text_rotations = []
		for i in range(1,7):
			sw_orig_translations.append(get_node("Warps/Teleporter Warps/Sunwarp_enter_%d" % i).translation)
			sw_text_translations.append(get_node("Decorations/Signs/Sunwarp Numbers/enter_%d" % i).translation)
			sw_text_rotations.append(get_node("Decorations/Signs/Sunwarp Numbers/enter_%d" % i).rotation_degrees)
		for i in range(1,7):
			sw_orig_translations.append(get_node("Warps/Teleporter Warps/Sunwarp_exit_%d" % i).translation)
			sw_text_translations.append(get_node("Decorations/Signs/Sunwarp Numbers/exit_%d" % i).translation)
			sw_text_rotations.append(get_node("Decorations/Signs/Sunwarp Numbers/exit_%d" % i).rotation_degrees)

		var sw_enter_indicators = [4, 5, 6, 12, 7, 10]
		for i in range(1,7):
			get_node("Warps/Teleporter Warps/Sunwarp_enter_%d" % i).translation = sw_orig_translations[apclient._sunwarp_mapping[i-1]]
			get_node("Warps/Teleporter Warps/Sunwarp_exit_%d" % i).translation = sw_orig_translations[apclient._sunwarp_mapping[i+5]]

			get_node("Decorations/Signs/Sunwarp Numbers/enter_%d" % i).translation = sw_text_translations[apclient._sunwarp_mapping[i-1]]
			get_node("Decorations/Signs/Sunwarp Numbers/enter_%d" % i).rotation_degrees = sw_text_rotations[apclient._sunwarp_mapping[i-1]]

			get_node("Decorations/Signs/Sunwarp Numbers/exit_%d" % i).translation = sw_text_translations[apclient._sunwarp_mapping[i+5]]
			get_node("Decorations/Signs/Sunwarp Numbers/exit_%d" % i).rotation_degrees = sw_text_rotations[apclient._sunwarp_mapping[i+5]]

			var enter_rot = _dir_to_int(gamedata.sunwarps[apclient._sunwarp_mapping[i-1]]["orientation"]) * 90
			var exit_rot = _dir_to_int(gamedata.sunwarps[apclient._sunwarp_mapping[i+5]]["orientation"]) * 90
			var final_rot = enter_rot - exit_rot
			if final_rot < 0:
				final_rot += 360
			get_node("Warps/Teleporter Warps/Sunwarp_enter_%d" % i).rotate = str(final_rot)

			var sw_enter_indicator_pos = gamedata.sunwarps[apclient._sunwarp_mapping[i-1]]["entrance_indicator_pos"]
			var sw_enter_indicator = get_node("Decorations/Signs/Welcome Back Signs/Sign%d" % sw_enter_indicators[i-1])
			sw_enter_indicator.translation.x = sw_enter_indicator_pos[0]
			sw_enter_indicator.translation.y = sw_enter_indicator_pos[1]
			sw_enter_indicator.translation.z = sw_enter_indicator_pos[2]
			sw_enter_indicator.rotation_degrees.y = (enter_rot * -1) + 180

	# Create the effects node.
	var effects_script = apclient.SCRIPT_effects
	var effects = effects_script.new()
	effects.set_name("AP_Effects")
	self.add_child(effects)

	# Create the multiplayer node, if needed.
	if apclient.enable_multiplayer:
		var multiplayer_node = apclient.SCRIPT_multiplayer.new()
		multiplayer_node.ghost_mode = true
		add_child(multiplayer_node)

	# Hook up the scene to be able to handle connection failures.
	apclient.connect("could_not_connect", self, "archipelago_disconnected")

	# Proceed with the rest of the load.
	global._print("Hooked Load End")
	._load()

	# Activate any cached traps.
	if apclient._cached_slowness > 0:
		effects.trigger_slowness_trap(apclient._cached_slowness)
	if apclient._cached_iceland > 0:
		effects.trigger_iceland_trap(apclient._cached_iceland)
	if apclient._cached_atbash > 0:
		for _i in range(0, apclient._cached_atbash):
			effects.trigger_atbash_trap()

	# Process any items received while the map was loading, and send the checks
	# from the save load.
	apclient.mapFinishedLoading()


func _load_user_textures(var custom = true):
	# We are using this function as a hook to process queued Iceland Traps
	# because it happens after the environment gets set.
	var effects_node = get_tree().get_root().get_node("Spatial/AP_Effects")
	effects_node.activate()

	._load_user_textures(custom)


func sort_by_link(a, b):
	return a["link"] < b["link"]


func set_static_panel(name, question, answer = ""):
	if answer == "":
		answer = question

	var node = self.get_node("Panels").get_node(name)

	node.text = question
	node.answer = answer


func instantiate_painting(name, scene):
	var apclient = global.get_node("Archipelago")

	var scene_path = "res://nodes/paintings/%s.tscn" % scene
	var painting_scene = load(scene_path)
	var new_painting = painting_scene.instance()
	new_painting.set_name(name)

	var old_painting = self.get_node("Decorations/Paintings").get_node_or_null(name)
	if old_painting != null:
		new_painting.translation = old_painting.translation
		new_painting.rotation = old_painting.rotation

	var mypainting_script = apclient.SCRIPT_mypainting
	var mps_inst = mypainting_script.new()
	mps_inst.set_name("Script")

	var gamedata = apclient.get_node("Gamedata")
	if gamedata.paintings.has(name):
		var pconfig = gamedata.paintings[name]
		mps_inst.orientation = pconfig["orientation"]
		if pconfig["move"]:
			mps_inst.move = true
			mps_inst.move_to_x = old_painting.move_to_x
			mps_inst.move_to_z = old_painting.move_to_z
			mps_inst.key = old_painting.key

	self.get_node("AP_Paintings").add_child(new_painting)
	new_painting.add_child(mps_inst)
	if old_painting != null:
		old_painting.queue_free()
	return mps_inst


func set_gridmap_tile(x, y, z, tile):
	var gridmap = self.get_node("GridMap")
	var mesh_library = gridmap.mesh_library
	var mapvec = gridmap.world_to_map(gridmap.to_local(Vector3(x, y, z)))

	gridmap.set_cell_item(mapvec.x, mapvec.y, mapvec.z, mesh_library.find_item_by_name(tile))


func clear_gridmap_tile(x, y, z):
	var gridmap = self.get_node("GridMap")
	var mesh_library = gridmap.mesh_library
	var mapvec = gridmap.world_to_map(gridmap.to_local(Vector3(x, y, z)))

	gridmap.set_cell_item(mapvec.x, mapvec.y, mapvec.z, GridMap.INVALID_CELL_ITEM)


func set_small_gridmap_tile(x, y, z, tile):
	var gridmap = self.get_node("GridMapSmall")
	var mesh_library = gridmap.mesh_library
	var mapvec = gridmap.world_to_map(gridmap.to_local(Vector3(x, y, z)))

	gridmap.set_cell_item(mapvec.x, mapvec.y, mapvec.z, mesh_library.find_item_by_name(tile))


func archipelago_disconnected(reason):
	messages.showMessage(reason)


func _dir_to_int(dir):
	if dir == "north":
		return 0
	elif dir == "west":
		return 1
	elif dir == "south":
		return 2
	elif dir == "east":
		return 3
	return 4