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] if not letters_saved.is_empty(): ap.client.updateKeyboard(letters_saved) 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): var ap = global.get_node("Archipelago") var true_level = 0 if ap.get_letter_behavior(key, false) == ap.kLETTER_BEHAVIOR_VANILLA: true_level += 1 if level == 2 and ap.get_letter_behavior(key, true) == ap.kLETTER_BEHAVIOR_VANILLA: true_level += 1 if true_level < letters_saved.get(key, 0): return letters_saved[key] = true_level ap.client.updateKeyboard({key: true_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 func remote_keyboard_updated(updates): var reverse = {} var should_update = false for k in updates: if not letters_saved.has(k) or updates[k] > letters_saved[k]: letters_saved[k] = updates[k] should_update = true elif updates[k] < letters_saved[k]: reverse[k] = letters_saved[k] if should_update: update_unlocks() if not reverse.is_empty(): var ap = global.get_node("Archipelago") ap.client.updateKeyboard(reverse) mment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .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 */
display_name: "The Orb"
# These are inaccessible, and were probably just copy pasted from the other
# rooms.
excluded_nodes: "Components/Warps/worldport2"
excluded_nodes: "Components/Warps/worldport3"