about summary refs log tree commit diff stats
path: root/proto/common.proto
blob: e300e94f95e9038ee4546ebc5b32e72d7b1bcc07 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
edition = "2023";

package com.fourisland.lingo2_archipelago;

message Proxy {
  string answer = 1;
  string path = 2;
}

enum DoorType {
  DOOR_TYPE_UNKNOWN = 0;

  // This door is a location unless panelsanity is on, and it is an item as long as door shuffle is on.
  STANDARD = 1;

  // This door is never an item or a location.
  EVENT = 2;

  // This door is never a location, and is an item as long as door shuffle is on.
  ITEM_ONLY = 3;

  // This door is never a location, and is an item as long as control center color shuffle is on.
  CONTROL_CENTER_COLOR = 4;

  // This door is never an item, and is a location as long as panelsanity is not on.
  LOCATION_ONLY = 5;
}
*/ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
extends Node

const kALL_LETTERS = "abcdefghjiklmnopqrstuvwxyz"

var letters_saved = {}
var letters_in_keyholders = []
var letters_blocked = []
var letters_dynamic = {}
var keyholder_state = {}

var filename = ""


func _init():
	reset()


func reset():
	letters_saved.clear()
	letters_in_keyholders.clear()
	letters_blocked.clear()
	letters_dynamic.clear()
	keyholder_state.clear()


func load_seed():
	var ap = global.get_node("Archipelago")

	reset()

	filename = "user://archipelago_keys/%s_%d" % [ap.client._seed, ap.client._slot]

	if FileAccess.file_exists(filename):
		var ap_file = FileAccess.open(filename, FileAccess.READ)
		var localdata = []
		if ap_file != null:
			localdata = ap_file.get_var(true)
			ap_file.close()

		if typeof(localdata) != TYPE_ARRAY:
			print("AP keyboard file is corrupted")
			localdata = []

		if localdata.size() > 0:
			letters_saved = localdata[0]
		if localdata.size() > 1:
			letters_in_keyholders = localdata[1]
		if localdata.size() > 2:
			keyholder_state = localdata[2]

	for k in kALL_LETTERS:
		var level = 0

		if ap.get_letter_behavior(k, false) == ap.kLETTER_BEHAVIOR_UNLOCKED:
			level += 1
		if ap.get_letter_behavior(k, true) == ap.kLETTER_BEHAVIOR_UNLOCKED:
			level += 1

		letters_dynamic[k] = level

	update_unlocks()


func save():
	var dir = DirAccess.open("user://")
	var folder = "archipelago_keys"
	if not dir.dir_exists(folder):
		dir.make_dir(folder)

	var file = FileAccess.open(filename, FileAccess.WRITE)

	var data = [
		letters_saved,
		letters_in_keyholders,
		keyholder_state,
	]
	file.store_var(data, true)
	file.close()


func update_unlocks():
	unlocks.resetKeys()

	var has_doubles = false

	for k in kALL_LETTERS:
		var level = 0

		if not letters_in_keyholders.has(k):
			level = letters_saved.get(k, 0) + letters_dynamic.get(k, 0)

		if level >= 2:
			level = 2
			has_doubles = true

		if letters_blocked.has(k):
			level = 0

		unlocks.unlockKey(k, level)

	if has_doubles and unlocks.data["double_letters"] != "unlocked":
		var ap = global.get_node("Archipelago")
		if ap.cyan_door_behavior == ap.kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER:
			unlocks.setData("double_letters", "unlocked")


func collect_local_letter(key, level):
	if level < 0 or level > 2 or level < letters_saved.get(key, 0):
		return

	letters_saved[key] = level

	if letters_blocked.has(key):
		letters_blocked.erase(key)

	update_unlocks()
	save()


func collect_remote_letter(key, level):
	if level < 0 or level > 2 or level < letters_dynamic.get(key, 0):
		return

	letters_dynamic[key] = level

	if letters_blocked.has(key):
		letters_blocked.erase(key)

	update_unlocks()
	save()


func put_in_keyholder(key, map, kh_path):
	if not keyholder_state.has(map):
		keyholder_state[map] = {}

	keyholder_state[map][kh_path] = key
	letters_in_keyholders.append(key)

	get_tree().get_root().get_node("scene").get_node(kh_path).setFromAp(
		key, min(letters_saved.get(key, 0) + letters_dynamic.get(key, 0), 2)
	)

	update_unlocks()
	save()


func remove_from_keyholder(key, map, kh_path):
	if not keyholder_state.has(map):
		# This... shouldn't happen.
		keyholder_state[map] = {}

	keyholder_state[map].erase(kh_path)
	letters_in_keyholders.erase(key)

	get_tree().get_root().get_node("scene").get_node(kh_path).setFromAp(key, 0)

	update_unlocks()
	save()


func block_letter(key):
	if not letters_blocked.has(key):
		letters_blocked.append(key)

	update_unlocks()


func load_keyholders(map):
	if keyholder_state.has(map):
		var khs = keyholder_state[map]

		for path in khs.keys():
			var key = khs[path]
			get_tree().get_root().get_node("scene").get_node(path).setFromAp(
				key, min(letters_saved.get(key, 0) + letters_dynamic.get(key, 0), 2)
			)


func reset_keyholders():
	if letters_in_keyholders.is_empty() and letters_blocked.is_empty():
		return false

	var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty()

	if keyholder_state.has(global.map):
		for path in keyholder_state[global.map]:
			get_tree().get_root().get_node("scene").get_node(path).setFromAp(
				keyholder_state[global.map][path], 0
			)

	keyholder_state.clear()
	letters_in_keyholders.clear()
	letters_blocked.clear()

	update_unlocks()
	save()

	return cleared_anything