about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2024-12-09 12:50:37 -0500
committerStar Rauchenberger <fefferburbia@gmail.com>2024-12-09 12:50:37 -0500
commit880e4b5069c4dfbf1ed532fc117697cde83b5566 (patch)
tree233bcbd82ff1ea8b2237232d430c665168ba009f
parentf834267a75c873d143e1f7f56f73faaa5e15cf2f (diff)
parent45fa76505dd00bdd6665bae476277371fb1252a2 (diff)
downloadlingo-archipelago-880e4b5069c4dfbf1ed532fc117697cde83b5566.tar.gz
lingo-archipelago-880e4b5069c4dfbf1ed532fc117697cde83b5566.tar.bz2
lingo-archipelago-880e4b5069c4dfbf1ed532fc117697cde83b5566.zip
Merge branch 'main' into experimental-panels
-rw-r--r--Archipelago/client.gd171
-rw-r--r--Archipelago/effects.gd66
-rw-r--r--Archipelago/extradata.gd84
-rw-r--r--Archipelago/load.gd23
-rw-r--r--Archipelago/multiplayer.gd55
-rw-r--r--Archipelago/panel.gd11
-rw-r--r--Archipelago/pause_menu.gd5
-rw-r--r--Archipelago/settings_screen.gd1
-rw-r--r--Archipelago/textclient.gd100
-rw-r--r--CHANGELOG.md86
-rw-r--r--README.md8
-rw-r--r--util/generate_gamedata.rb75
12 files changed, 649 insertions, 36 deletions
diff --git a/Archipelago/client.gd b/Archipelago/client.gd index 78058ca..390b14d 100644 --- a/Archipelago/client.gd +++ b/Archipelago/client.gd
@@ -9,6 +9,7 @@ var SCRIPT_mypainting
9var SCRIPT_notifier 9var SCRIPT_notifier
10var SCRIPT_panel 10var SCRIPT_panel
11var SCRIPT_pilgrimage_terminator 11var SCRIPT_pilgrimage_terminator
12var SCRIPT_textclient
12var SCRIPT_uuid 13var SCRIPT_uuid
13 14
14var ap_server = "" 15var ap_server = ""
@@ -19,12 +20,12 @@ var enable_multiplayer = false
19var track_player = false 20var track_player = false
20var connection_history = [] 21var connection_history = []
21 22
22const my_version = "4.0.2" 23const my_version = "5.0.1"
23const ap_version = {"major": 0, "minor": 5, "build": 0, "class": "Version"} 24const ap_version = {"major": 0, "minor": 5, "build": 1, "class": "Version"}
24const color_items = [ 25const color_items = [
25 "White", "Black", "Red", "Blue", "Green", "Brown", "Gray", "Orange", "Purple", "Yellow" 26 "White", "Black", "Red", "Blue", "Green", "Brown", "Gray", "Orange", "Purple", "Yellow"
26] 27]
27const progressive_items = { 28const door_progressive_items = {
28 "Progressive Orange Tower": 29 "Progressive Orange Tower":
29 ["Second Floor", "Third Floor", "Fourth Floor", "Fifth Floor", "Sixth Floor", "Seventh Floor"], 30 ["Second Floor", "Third Floor", "Fourth Floor", "Fifth Floor", "Sixth Floor", "Seventh Floor"],
30 "Progressive Art Gallery": 31 "Progressive Art Gallery":
@@ -36,6 +37,15 @@ const progressive_items = {
36 "Progressive Pilgrimage": 37 "Progressive Pilgrimage":
37 ["1 Sunwarp", "2 Sunwarp", "3 Sunwarp", "4 Sunwarp", "5 Sunwarp", "6 Sunwarp"] 38 ["1 Sunwarp", "2 Sunwarp", "3 Sunwarp", "4 Sunwarp", "5 Sunwarp", "6 Sunwarp"]
38} 39}
40const panel_progressive_items = {
41 "Progressive Hallway Room": ["First Door", "Second Door", "Third Door", "Fourth Door"],
42 "Progressive Colorful":
43 ["White", "Black", "Red", "Yellow", "Blue", "Purple", "Orange", "Green", "Brown", "Gray"],
44 "Progressive Number Hunt":
45 ["Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Zero"],
46 "Progressive Symmetry Room": ["Near Far", "Warts Straw", "Leaf Feel"],
47 "Progressive Suits Area": ["Words Sword", "Lost", "Amen Name"]
48}
39 49
40const kTHE_END = 0 50const kTHE_END = 0
41const kTHE_MASTER = 1 51const kTHE_MASTER = 1
@@ -91,6 +101,7 @@ var _localdata_file = ""
91var _death_link = false 101var _death_link = false
92var _victory_condition = 0 # THE END, THE MASTER, LEVEL 2 102var _victory_condition = 0 # THE END, THE MASTER, LEVEL 2
93var _door_shuffle = false 103var _door_shuffle = false
104var _panel_door_shuffle = false
94var _color_shuffle = false 105var _color_shuffle = false
95var _panel_shuffle = 0 # none, rearrange 106var _panel_shuffle = 0 # none, rearrange
96var _painting_shuffle = false 107var _painting_shuffle = false
@@ -105,6 +116,7 @@ var _pilgrimage_allows_roof_access = false
105var _pilgrimage_allows_paintings = false 116var _pilgrimage_allows_paintings = false
106var _sunwarp_shuffle = false 117var _sunwarp_shuffle = false
107var _sunwarp_mapping = [] 118var _sunwarp_mapping = []
119var _speed_boost_mode = false
108var _slot_seed = 0 120var _slot_seed = 0
109 121
110var _map_loaded = false 122var _map_loaded = false
@@ -118,8 +130,11 @@ var _puzzle_skips = 0
118var _cached_slowness = 0 130var _cached_slowness = 0
119var _cached_iceland = 0 131var _cached_iceland = 0
120var _cached_atbash = 0 132var _cached_atbash = 0
133var _cached_speed_boosts = 0
121var _geronimo_skip = false 134var _geronimo_skip = false
122var _checked_paintings = [] 135var _checked_paintings = []
136var _hints_key = ""
137var _hinted_locations = []
123 138
124signal could_not_connect 139signal could_not_connect
125signal connect_status 140signal connect_status
@@ -277,9 +292,15 @@ func _on_data():
277 _color_shuffle = _slot_data["shuffle_colors"] 292 _color_shuffle = _slot_data["shuffle_colors"]
278 293
279 if _slot_data.has("shuffle_doors"): 294 if _slot_data.has("shuffle_doors"):
280 _door_shuffle = (_slot_data["shuffle_doors"] > 0) 295 if _slot_data.has("group_doors"):
296 _door_shuffle = (_slot_data["shuffle_doors"] == 2)
297 _panel_door_shuffle = (_slot_data["shuffle_doors"] == 1)
298 else:
299 _door_shuffle = (_slot_data["shuffle_doors"] > 0)
300 _panel_door_shuffle = false
281 else: 301 else:
282 _door_shuffle = false 302 _door_shuffle = false
303 _panel_door_shuffle = false
283 304
284 if _slot_data.has("shuffle_paintings"): 305 if _slot_data.has("shuffle_paintings"):
285 _painting_shuffle = _slot_data["shuffle_paintings"] 306 _painting_shuffle = _slot_data["shuffle_paintings"]
@@ -331,6 +352,10 @@ func _on_data():
331 _sunwarp_shuffle = false 352 _sunwarp_shuffle = false
332 if _slot_data.has("sunwarp_permutation"): 353 if _slot_data.has("sunwarp_permutation"):
333 _sunwarp_mapping = _slot_data["sunwarp_permutation"] 354 _sunwarp_mapping = _slot_data["sunwarp_permutation"]
355 if _slot_data.has("speed_boost_mode"):
356 _speed_boost_mode = _slot_data["speed_boost_mode"]
357 else:
358 _speed_boost_mode = false
334 359
335 if ( 360 if (
336 _location_classification_bit != kCLASSIFICATION_LOCAL_INSANITY 361 _location_classification_bit != kCLASSIFICATION_LOCAL_INSANITY
@@ -349,6 +374,7 @@ func _on_data():
349 _cached_slowness = 0 374 _cached_slowness = 0
350 _cached_iceland = 0 375 _cached_iceland = 0
351 _cached_atbash = 0 376 _cached_atbash = 0
377 _cached_speed_boosts = 0
352 _geronimo_skip = false 378 _geronimo_skip = false
353 379
354 _localdata_file = "user://archipelago_data/%s_%d" % [_seed, _slot] 380 _localdata_file = "user://archipelago_data/%s_%d" % [_seed, _slot]
@@ -380,6 +406,9 @@ func _on_data():
380 if localdata.size() > 5: 406 if localdata.size() > 5:
381 _geronimo_skip = localdata[5] 407 _geronimo_skip = localdata[5]
382 408
409 if localdata.size() > 6:
410 _cached_speed_boosts = localdata[6]
411
383 requestSync() 412 requestSync()
384 413
385 sendMessage( 414 sendMessage(
@@ -394,6 +423,11 @@ func _on_data():
394 ] 423 ]
395 ) 424 )
396 425
426 _hints_key = "_read_hints_%d_%d" % [_team, _slot]
427 sendMessage(
428 [{"cmd": "SetNotify", "keys": [_hints_key]}, {"cmd": "Get", "keys": [_hints_key]}]
429 )
430
397 emit_signal("client_connected") 431 emit_signal("client_connected")
398 432
399 elif cmd == "ConnectionRefused": 433 elif cmd == "ConnectionRefused":
@@ -453,6 +487,8 @@ func _on_data():
453 i += 1 487 i += 1
454 488
455 elif cmd == "PrintJSON": 489 elif cmd == "PrintJSON":
490 parse_printjson(message)
491
456 if ( 492 if (
457 !message.has("receiving") 493 !message.has("receiving")
458 or !message.has("item") 494 or !message.has("item")
@@ -489,9 +525,12 @@ func _on_data():
489 ) 525 )
490 else: 526 else:
491 if message["receiving"] != _slot: 527 if message["receiving"] != _slot:
492 messages.showMessage( 528 var sentMsg = (
493 "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name] 529 "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name]
494 ) 530 )
531 if _hinted_locations.has(message["item"]["location"]):
532 sentMsg += " ([color=#fafad2]Hinted![/color])"
533 messages.showMessage(sentMsg)
495 534
496 elif cmd == "Bounced": 535 elif cmd == "Bounced":
497 if ( 536 if (
@@ -512,8 +551,15 @@ func _on_data():
512 get_tree().get_root().get_node("Spatial/player/pause_menu")._reload() 551 get_tree().get_root().get_node("Spatial/player/pause_menu")._reload()
513 552
514 elif cmd == "SetReply": 553 elif cmd == "SetReply":
515 if message.has("key") and message["key"] == ("Lingo_%d_Paintings" % _slot): 554 if message.has("key"):
516 _checked_paintings = message["value"] 555 if message["key"] == ("Lingo_%d_Paintings" % _slot):
556 _checked_paintings = message["value"]
557 elif message["key"] == _hints_key:
558 parseHints(message["value"])
559
560 elif cmd == "Retrieved":
561 if message.has("keys") and message["keys"].has(_hints_key):
562 parseHints(message["keys"][_hints_key])
517 563
518 564
519func _process(_delta): 565func _process(_delta):
@@ -567,7 +613,8 @@ func saveLocaldata():
567 effects_node.slowness_remaining, 613 effects_node.slowness_remaining,
568 effects_node.iceland_remaining, 614 effects_node.iceland_remaining,
569 effects_node.atbash_remaining, 615 effects_node.atbash_remaining,
570 _geronimo_skip 616 _geronimo_skip,
617 effects_node.speed_boosts_remaining,
571 ] 618 ]
572 file.store_var(data, true) 619 file.store_var(data, true)
573 file.close() 620 file.close()
@@ -686,6 +733,10 @@ func setValue(key, value, operation = "replace"):
686 ) 733 )
687 734
688 735
736func say(textdata):
737 sendMessage([{"cmd": "Say", "text": textdata}])
738
739
689func completedGoal(): 740func completedGoal():
690 sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL 741 sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL
691 742
@@ -729,6 +780,18 @@ func processItem(item, index, from, flags):
729 for door_id in gamedata.door_ids_by_item_id[int(item)]: 780 for door_id in gamedata.door_ids_by_item_id[int(item)]:
730 doorsNode.get_node(door_id).openDoor() 781 doorsNode.get_node(door_id).openDoor()
731 782
783 if gamedata.panel_ids_by_item_id.has(int(item)):
784 var panel_ids = gamedata.panel_ids_by_item_id[int(item)]
785 if wasGeneratedOnVersion(0, 5, 1):
786 var extradata = get_node("Extradata")
787 if extradata.panels_mode_051_panel_fixes.has(int(item)):
788 panel_ids = extradata.panels_mode_051_panel_fixes[int(item)]
789
790 var panelsNode = get_tree().get_root().get_node("Spatial/Panels")
791 for panel_id in panel_ids:
792 panelsNode.get_node(panel_id).get_node("AP_Panel").locked = false
793 emit_signal("evaluate_solvability")
794
732 if gamedata.painting_ids_by_item_id.has(int(item)): 795 if gamedata.painting_ids_by_item_id.has(int(item)):
733 var real_parent_node = get_tree().get_root().get_node("Spatial/Decorations/Paintings") 796 var real_parent_node = get_tree().get_root().get_node("Spatial/Decorations/Paintings")
734 var fake_parent_node = get_tree().get_root().get_node_or_null("Spatial/AP_Paintings") 797 var fake_parent_node = get_tree().get_root().get_node_or_null("Spatial/AP_Paintings")
@@ -749,15 +812,34 @@ func processItem(item, index, from, flags):
749 warpsNode.get_node(warp_id).unlock_warp() 812 warpsNode.get_node(warp_id).unlock_warp()
750 813
751 # Handle progressive items. 814 # Handle progressive items.
752 if int(item) in gamedata.items_by_progressive_id.keys(): 815 var is_progressive_door = int(item) in gamedata.door_items_by_progressive_id
816 var is_progressive_panel = int(item) in gamedata.panel_items_by_progressive_id
817 var progitems = null
818 var prognames = null
819
820 if is_progressive_door and is_progressive_panel:
821 if _door_shuffle:
822 progitems = gamedata.door_items_by_progressive_id[int(item)]
823 prognames = door_progressive_items
824 else:
825 progitems = gamedata.panel_items_by_progressive_id[int(item)]
826 prognames = panel_progressive_items
827 elif is_progressive_door:
828 progitems = gamedata.door_items_by_progressive_id[int(item)]
829 prognames = door_progressive_items
830 elif is_progressive_panel:
831 progitems = gamedata.panel_items_by_progressive_id[int(item)]
832 prognames = panel_progressive_items
833
834 if progitems != null:
753 if not int(item) in _progressive_progress: 835 if not int(item) in _progressive_progress:
754 _progressive_progress[int(item)] = 0 836 _progressive_progress[int(item)] = 0
755 837
756 if _progressive_progress[int(item)] < gamedata.items_by_progressive_id[int(item)].size(): 838 if _progressive_progress[int(item)] < progitems.size():
757 var subitems = gamedata.items_by_progressive_id[int(item)] 839 var subitem_id = progitems[_progressive_progress[int(item)]]
758 var subitem_id = subitems[_progressive_progress[int(item)]]
759 global._print("Subitem: %d" % subitem_id) 840 global._print("Subitem: %d" % subitem_id)
760 processItem(subitem_id, null, null, null) 841 processItem(subitem_id, null, null, null)
842 item_name += " (%s)" % prognames[item_name][_progressive_progress[int(item)]]
761 _progressive_progress[int(item)] += 1 843 _progressive_progress[int(item)] += 1
762 844
763 if _color_shuffle and color_items.has(_item_id_to_name["Lingo"][item]): 845 if _color_shuffle and color_items.has(_item_id_to_name["Lingo"][item]):
@@ -771,10 +853,6 @@ func processItem(item, index, from, flags):
771 _last_new_item = index 853 _last_new_item = index
772 saveLocaldata() 854 saveLocaldata()
773 855
774 if item_name in progressive_items:
775 var subitem = progressive_items[item_name][_progressive_progress[int(item)] - 1]
776 item_name += " (%s)" % subitem
777
778 var player_name = "Unknown" 856 var player_name = "Unknown"
779 if _player_name_by_slot.has(from): 857 if _player_name_by_slot.has(from):
780 player_name = _player_name_by_slot[from] 858 player_name = _player_name_by_slot[from]
@@ -789,12 +867,14 @@ func processItem(item, index, from, flags):
789 ) 867 )
790 868
791 var effects_node = get_tree().get_root().get_node("Spatial/AP_Effects") 869 var effects_node = get_tree().get_root().get_node("Spatial/AP_Effects")
792 if item_name == "Slowness Trap": 870 if item_name == "Slowness Trap" and !_speed_boost_mode:
793 effects_node.trigger_slowness_trap() 871 effects_node.trigger_slowness_trap()
794 if item_name == "Iceland Trap": 872 if item_name == "Iceland Trap":
795 effects_node.trigger_iceland_trap() 873 effects_node.trigger_iceland_trap()
796 if item_name == "Atbash Trap": 874 if item_name == "Atbash Trap":
797 effects_node.trigger_atbash_trap() 875 effects_node.trigger_atbash_trap()
876 if item_name == "Speed Boost" and _speed_boost_mode:
877 effects_node.trigger_speed_boost()
798 if item_name == "Puzzle Skip": 878 if item_name == "Puzzle Skip":
799 _puzzle_skips += 1 879 _puzzle_skips += 1
800 880
@@ -845,6 +925,14 @@ func checkPainting(painting_id):
845 setValue("Paintings", [painting_id], "add") 925 setValue("Paintings", [painting_id], "add")
846 926
847 927
928func parseHints(hints):
929 _hinted_locations.clear()
930
931 for hint in hints:
932 if hint["finding_player"] == int(_slot):
933 _hinted_locations.append(hint["location"])
934
935
848func colorForItemType(flags): 936func colorForItemType(flags):
849 var int_flags = int(flags) 937 var int_flags = int(flags)
850 if int_flags & 1: # progression 938 if int_flags & 1: # progression
@@ -857,6 +945,47 @@ func colorForItemType(flags):
857 return "#14de9e" 945 return "#14de9e"
858 946
859 947
948func parse_printjson(message):
949 var parts = []
950 for message_part in message["data"]:
951 if !message_part.has("type") and message_part.has("text"):
952 parts.append(message_part["text"])
953 elif message_part["type"] == "player_id":
954 if int(message_part["text"]) == _slot:
955 parts.append("[color=#ee00ee]%s[/color]" % _player_name_by_slot[_slot])
956 else:
957 var from = float(message_part["text"])
958 parts.append("[color=#fafad2]%s[/color]" % _player_name_by_slot[from])
959 elif message_part["type"] == "item_id":
960 var item_name = "Unknown"
961 var item_player_game = _game_by_player[message_part["player"]]
962 if _item_id_to_name[item_player_game].has(float(message_part["text"])):
963 item_name = _item_id_to_name[item_player_game][float(message_part["text"])]
964
965 parts.append(
966 "[color=%s]%s[/color]" % [colorForItemType(message_part["flags"]), item_name]
967 )
968 elif message_part["type"] == "location_id":
969 var location_name = "Unknown"
970 var location_player_game = _game_by_player[message_part["player"]]
971 if _location_id_to_name[location_player_game].has(float(message_part["text"])):
972 location_name = _location_id_to_name[location_player_game][float(
973 message_part["text"]
974 )]
975
976 parts.append("[color=#00ff7f]%s[/color]" % location_name)
977 elif message_part.has("text"):
978 parts.append(message_part["text"])
979
980 var textclient_node = get_tree().get_root().get_node_or_null("Spatial/AP_TextClient")
981 if textclient_node != null:
982 textclient_node.parse_printjson("".join(parts))
983
984
985func get_player_name():
986 return _player_name_by_slot[_slot]
987
988
860func compareVersion(lhs, rhs): 989func compareVersion(lhs, rhs):
861 if lhs["major"] == rhs["major"]: 990 if lhs["major"] == rhs["major"]:
862 if lhs["minor"] == rhs["minor"]: 991 if lhs["minor"] == rhs["minor"]:
@@ -869,3 +998,11 @@ func compareVersion(lhs, rhs):
869 998
870func wasGeneratedBeforeVersion(major, minor, build): 999func wasGeneratedBeforeVersion(major, minor, build):
871 return compareVersion(_gen_version, {"major": major, "minor": minor, "build": build}) 1000 return compareVersion(_gen_version, {"major": major, "minor": minor, "build": build})
1001
1002
1003func wasGeneratedOnVersion(major, minor, build):
1004 return (
1005 _gen_version["major"] == major
1006 and _gen_version["minor"] == minor
1007 and _gen_version["build"] == build
1008 )
diff --git a/Archipelago/effects.gd b/Archipelago/effects.gd index 1e2e311..341a783 100644 --- a/Archipelago/effects.gd +++ b/Archipelago/effects.gd
@@ -5,6 +5,7 @@ var effect_running = false
5var slowness_remaining = 0 5var slowness_remaining = 0
6var iceland_remaining = 0 6var iceland_remaining = 0
7var atbash_remaining = 0 7var atbash_remaining = 0
8var speed_boosts_remaining = 0
8var queued_iceland = 0 9var queued_iceland = 0
9var skip_available = false 10var skip_available = false
10var puzzle_focused = false 11var puzzle_focused = false
@@ -48,6 +49,12 @@ func _ready():
48 add_child(slowness_timer) 49 add_child(slowness_timer)
49 slowness_timer.connect("timeout", self, "_tick_slowness") 50 slowness_timer.connect("timeout", self, "_tick_slowness")
50 51
52 var speed_boost_timer = Timer.new()
53 speed_boost_timer.name = "SpeedBoostTimer"
54 speed_boost_timer.wait_time = 1.0
55 add_child(speed_boost_timer)
56 speed_boost_timer.connect("timeout", self, "_tick_speed_boost")
57
51 var iceland_timer = Timer.new() 58 var iceland_timer = Timer.new()
52 iceland_timer.name = "IcelandTimer" 59 iceland_timer.name = "IcelandTimer"
53 iceland_timer.wait_time = 1.0 60 iceland_timer.wait_time = 1.0
@@ -63,6 +70,12 @@ func activate():
63 70
64 queued_iceland = 0 71 queued_iceland = 0
65 72
73 var apclient = global.get_node("Archipelago")
74 if apclient._speed_boost_mode:
75 var player = get_tree().get_root().get_node("Spatial/player")
76 player.walk_speed = orig_walk / 2.0
77 player.run_speed = orig_run / 2.0
78
66 79
67func trigger_slowness_trap(length = 30): 80func trigger_slowness_trap(length = 30):
68 if slowness_remaining == 0: 81 if slowness_remaining == 0:
@@ -79,6 +92,21 @@ func trigger_slowness_trap(length = 30):
79 apclient.saveLocaldata() 92 apclient.saveLocaldata()
80 93
81 94
95func trigger_speed_boost(length = 20):
96 if speed_boosts_remaining == 0:
97 var player = get_tree().get_root().get_node("Spatial/player")
98 player.walk_speed = orig_walk
99 player.run_speed = orig_run
100
101 $SpeedBoostTimer.start()
102
103 speed_boosts_remaining += length
104 text_dirty = true
105
106 var apclient = global.get_node("Archipelago")
107 apclient.saveLocaldata()
108
109
82func trigger_iceland_trap(length = 60): 110func trigger_iceland_trap(length = 60):
83 if not activated: 111 if not activated:
84 queued_iceland += length 112 queued_iceland += length
@@ -99,7 +127,7 @@ func trigger_iceland_trap(length = 60):
99 127
100 128
101func trigger_atbash_trap(): 129func trigger_atbash_trap():
102 var newly_atbash = (atbash_remaining == 0) 130 var newly_atbash = atbash_remaining == 0
103 atbash_remaining += 1 131 atbash_remaining += 1
104 132
105 if newly_atbash: 133 if newly_atbash:
@@ -121,7 +149,7 @@ func deactivate_atbash_trap():
121 apclient.evaluateSolvability() 149 apclient.evaluateSolvability()
122 150
123 text_dirty = true 151 text_dirty = true
124 152
125 var apclient = global.get_node("Archipelago") 153 var apclient = global.get_node("Archipelago")
126 apclient.saveLocaldata() 154 apclient.saveLocaldata()
127 155
@@ -192,23 +220,37 @@ func _tick_slowness():
192 player.run_speed = orig_run 220 player.run_speed = orig_run
193 221
194 $SlownessTimer.stop() 222 $SlownessTimer.stop()
195 223
196 if slowness_remaining % 5 == 0: 224 if slowness_remaining % 5 == 0:
197 var apclient = global.get_node("Archipelago") 225 var apclient = global.get_node("Archipelago")
198 apclient.saveLocaldata() 226 apclient.saveLocaldata()
199 227
200 228
229func _tick_speed_boost():
230 speed_boosts_remaining -= 1
231 text_dirty = true
232
233 if speed_boosts_remaining == 0:
234 var player = get_tree().get_root().get_node("Spatial/player")
235 player.walk_speed = orig_walk / 2.0
236 player.run_speed = orig_run / 2.0
237
238 $SpeedBoostTimer.stop()
239
240 if speed_boosts_remaining % 5 == 0:
241 var apclient = global.get_node("Archipelago")
242 apclient.saveLocaldata()
243
244
201func _tick_iceland(): 245func _tick_iceland():
202 iceland_remaining -= 1 246 iceland_remaining -= 1
203 text_dirty = true 247 text_dirty = true
204 248
205 if iceland_remaining == 0: 249 if iceland_remaining == 0:
206 get_tree().get_root().get_node("Spatial/player/pivot/camera").set_environment( 250 get_tree().get_root().get_node("Spatial/player/pivot/camera").set_environment(orig_env)
207 orig_env
208 )
209 251
210 $IcelandTimer.stop() 252 $IcelandTimer.stop()
211 253
212 if iceland_remaining % 5 == 0: 254 if iceland_remaining % 5 == 0:
213 var apclient = global.get_node("Archipelago") 255 var apclient = global.get_node("Archipelago")
214 apclient.saveLocaldata() 256 apclient.saveLocaldata()
@@ -220,9 +262,11 @@ func _process(_delta):
220 if wallcast.is_colliding(): 262 if wallcast.is_colliding():
221 var player = get_tree().get_root().get_node("Spatial/player") 263 var player = get_tree().get_root().get_node("Spatial/player")
222 var puzzlecast = player.get_node("pivot/camera/RayCast_sight") 264 var puzzlecast = player.get_node("pivot/camera/RayCast_sight")
223 var distance = puzzlecast.get_collision_point().distance_to(wallcast.get_collision_point()) 265 var distance = puzzlecast.get_collision_point().distance_to(
266 wallcast.get_collision_point()
267 )
224 should_nbw = (distance < 0.05) 268 should_nbw = (distance < 0.05)
225 269
226 if should_nbw != not_behind_wall: 270 if should_nbw != not_behind_wall:
227 not_behind_wall = should_nbw 271 not_behind_wall = should_nbw
228 text_dirty = true 272 text_dirty = true
@@ -239,6 +283,10 @@ func _process(_delta):
239 if not text.empty(): 283 if not text.empty():
240 text += "\n" 284 text += "\n"
241 text += "Slowness: %d seconds" % slowness_remaining 285 text += "Slowness: %d seconds" % slowness_remaining
286 if speed_boosts_remaining > 0:
287 if not text.empty():
288 text += "\n"
289 text += "Speed Boost: %d seconds" % speed_boosts_remaining
242 if iceland_remaining > 0: 290 if iceland_remaining > 0:
243 if not text.empty(): 291 if not text.empty():
244 text += "\n" 292 text += "\n"
diff --git a/Archipelago/extradata.gd b/Archipelago/extradata.gd index 89c41d2..2e26eb2 100644 --- a/Archipelago/extradata.gd +++ b/Archipelago/extradata.gd
@@ -93,3 +93,87 @@ var proxies = {
93 "Open Areas/Panel_rise_horizon": ["Open Areas/Panel_son_horizon"], 93 "Open Areas/Panel_rise_horizon": ["Open Areas/Panel_son_horizon"],
94 "Open Areas/Panel_son_sunrise": ["Open Areas/Panel_rise_sunrise"] 94 "Open Areas/Panel_son_sunrise": ["Open Areas/Panel_rise_sunrise"]
95} 95}
96
97var panels_mode_051_panel_fixes = {
98 444647:
99 [
100 "Backside Room/Panel_three_three",
101 "Backside Room/Panel_three_three_2",
102 "Backside Room/Panel_three_three_3",
103 "Backside Room/Panel_four_four_3",
104 "Backside Room/Panel_four_four_2",
105 "Backside Room/Panel_four_four_4"
106 ],
107 444648:
108 [
109 "Backside Room/Panel_four_four",
110 "Backside Room/Panel_five_five_5",
111 "Backside Room/Panel_five_five_4"
112 ],
113 444649:
114 [
115 "Backside Room/Panel_five_five",
116 "Backside Room/Panel_five_five_3",
117 "Backside Room/Panel_five_five_2",
118 "Backside Room/Panel_six_six_4"
119 ],
120 444650:
121 [
122 "Backside Room/Panel_six_six",
123 "Backside Room/Panel_six_six_3",
124 "Backside Room/Panel_six_six_2",
125 "Backside Room/Panel_six_six_5",
126 "Backside Room/Panel_six_six_6",
127 "Backside Room/Panel_seven_seven_5",
128 "Backside Room/Panel_seven_seven_6"
129 ],
130 444651:
131 [
132 "Backside Room/Panel_seven_seven",
133 "Backside Room/Panel_seven_seven_2",
134 "Backside Room/Panel_seven_seven_7",
135 "Backside Room/Panel_seven_seven_3",
136 "Backside Room/Panel_seven_seven_4",
137 "Backside Room/Panel_eight_eight_8",
138 "Backside Room/Panel_eight_eight_5",
139 "Backside Room/Panel_eight_eight_3",
140 "Backside Room/Panel_eight_eight_7"
141 ],
142 444652:
143 [
144 "Backside Room/Panel_eight_eight",
145 "Backside Room/Panel_eight_eight_2",
146 "Backside Room/Panel_eight_eight_4",
147 "Backside Room/Panel_eight_eight_6",
148 "Backside Room/Panel_nine_nine_3",
149 "Backside Room/Panel_nine_nine_8",
150 "Backside Room/Panel_nine_nine_4",
151 "Backside Room/Panel_nine_nine_5",
152 "Backside Room/Panel_nine_nine_2"
153 ],
154 444653:
155 [
156 "Backside Room/Panel_nine_nine",
157 "Backside Room/Panel_nine_nine_6",
158 "Backside Room/Panel_nine_nine_8",
159 "Backside Room/Panel_nine_nine_9",
160 "Backside Room/Panel_nine_nine_7"
161 ]
162}
163
164var panels_mode_051_door_fixes = {
165 "Door_four_hider": ["Door_four_hider_3", "Door_four_hider_4", "Door_four_hider_2"],
166 "Door_five_hider": ["Door_five_hider_4", "Door_five_hider_5"],
167 "Door_six_hider": ["Door_six_hider_4"],
168 "Door_seven_hider": ["Door_seven_hider_6", "Door_seven_hider_5"],
169 "Door_eight_hider":
170 ["Door_eight_hider_7", "Door_eight_hider_8", "Door_eight_hider_3", "Door_eight_hider_5"],
171 "Door_nine_hider":
172 [
173 "Door_nine_hider_3",
174 "Door_nine_hider_8",
175 "Door_nine_hider_4",
176 "Door_nine_hider_5",
177 "Door_nine_hider_2"
178 ]
179}
diff --git a/Archipelago/load.gd b/Archipelago/load.gd index be209c3..8559b10 100644 --- a/Archipelago/load.gd +++ b/Archipelago/load.gd
@@ -554,6 +554,20 @@ func _load():
554 proxynode.exact_proxy = true 554 proxynode.exact_proxy = true
555 proxynode.request_ready() 555 proxynode.request_ready()
556 oldparent.add_child(proxynode) 556 oldparent.add_child(proxynode)
557
558 # If the world was generated on 0.5.1, apply the hotfix for the number hunt doors.
559 if apclient._panel_door_shuffle && apclient.wasGeneratedOnVersion(0, 5, 1):
560 var number_hunt_parent = get_node("Doors/Count Up Room Area Doors")
561 var extradata_051_fix = apclient.get_node("Extradata").panels_mode_051_door_fixes
562 for template_door_path in extradata_051_fix:
563 var template_door = number_hunt_parent.get_node(template_door_path)
564 var impacted_doors = extradata_051_fix[template_door_path]
565 for impacted_door_path in impacted_doors:
566 var impacted_door = number_hunt_parent.get_node(impacted_door_path)
567 var copied_door = impacted_door.duplicate()
568 copied_door.panels = template_door.panels
569 number_hunt_parent.add_child(copied_door)
570 impacted_door.queue_free()
557 571
558 # Attach a script to every panel so that we can do things like conditionally 572 # Attach a script to every panel so that we can do things like conditionally
559 # disable them. 573 # disable them.
@@ -567,6 +581,8 @@ func _load():
567 var script_instance = panel_script.new() 581 var script_instance = panel_script.new()
568 script_instance.name = "AP_Panel" 582 script_instance.name = "AP_Panel"
569 script_instance.data = panel 583 script_instance.data = panel
584 if apclient._panel_door_shuffle and gamedata.mentioned_panels.has(panel["id"]):
585 script_instance.locked = true
570 panel_node.add_child(script_instance) 586 panel_node.add_child(script_instance)
571 apclient.connect("evaluate_solvability", script_instance, "evaluate_solvability") 587 apclient.connect("evaluate_solvability", script_instance, "evaluate_solvability")
572 588
@@ -666,9 +682,16 @@ func _load():
666 effects.set_name("AP_Effects") 682 effects.set_name("AP_Effects")
667 self.add_child(effects) 683 self.add_child(effects)
668 684
685 # Create the textclient node.
686 var textclient_script = apclient.SCRIPT_textclient
687 var textclient = textclient_script.new()
688 textclient.set_name("AP_TextClient")
689 self.add_child(textclient)
690
669 # Create the multiplayer node, if needed. 691 # Create the multiplayer node, if needed.
670 if apclient.enable_multiplayer: 692 if apclient.enable_multiplayer:
671 var multiplayer_node = apclient.SCRIPT_multiplayer.new() 693 var multiplayer_node = apclient.SCRIPT_multiplayer.new()
694 multiplayer_node.name = "Multiplayer"
672 multiplayer_node.ghost_mode = true 695 multiplayer_node.ghost_mode = true
673 add_child(multiplayer_node) 696 add_child(multiplayer_node)
674 697
diff --git a/Archipelago/multiplayer.gd b/Archipelago/multiplayer.gd index 0bc2241..c2d9875 100644 --- a/Archipelago/multiplayer.gd +++ b/Archipelago/multiplayer.gd
@@ -1,5 +1,8 @@
1extends "res://scripts/multiplayer.gd" 1extends "res://scripts/multiplayer.gd"
2 2
3var queued_messages = []
4var queued_messages_mutex = Mutex.new()
5
3 6
4func _request_lobby_list(): 7func _request_lobby_list():
5 var apclient = global.get_node("Archipelago") 8 var apclient = global.get_node("Archipelago")
@@ -41,3 +44,55 @@ func _update_lobby_members():
41 if member_id != player_steam_id and member_id in active_lobby_members: 44 if member_id != player_steam_id and member_id in active_lobby_members:
42 var slot_name = Steam.getLobbyMemberData(active_lobby_id, member_id, "slot_name") 45 var slot_name = Steam.getLobbyMemberData(active_lobby_id, member_id, "slot_name")
43 active_lobby_members[member_id].steam_name = slot_name 46 active_lobby_members[member_id].steam_name = slot_name
47
48
49func say(text):
50 queued_messages_mutex.lock()
51 queued_messages.append(text)
52 queued_messages_mutex.unlock()
53
54
55func _physics_process(_delta):
56 if queued_messages_mutex.try_lock() == OK:
57 if queued_messages.empty():
58 queued_messages_mutex.unlock()
59 else:
60 var messages = queued_messages.duplicate()
61 queued_messages = []
62 queued_messages_mutex.unlock()
63
64 var player = get_tree().get_root().get_node("Spatial/player")
65 var space_state = get_tree().get_root().get_world().direct_space_state
66 var nearby_members = []
67 for member_id in active_lobby_members.keys():
68 var other_member = active_lobby_members[member_id]
69 var ray = space_state.intersect_ray(
70 player.global_translation, other_member.global_translation, [player], 0b0101
71 )
72 if !("collider" in ray) or ray["collider"] == other_member:
73 # Visible!
74 nearby_members.append(member_id)
75
76 var apclient = global.get_node("Archipelago")
77 var player_name = apclient.get_player_name()
78 for member_id in nearby_members:
79 for msg in messages:
80 _send_p2p_packet(
81 {"solves": [{"say": msg, "from": player_name}]},
82 member_id,
83 Steam.P2P_SEND_RELIABLE,
84 true
85 )
86
87
88# I'm completely hijacking this callback, since we're in ghost mode and it won't be called normally.
89func _receive_solve(data):
90 if "say" in data:
91 get_tree().get_root().get_node("Spatial/AP_TextClient").parse_printjson(
92 "[LOCAL] [color=#fafad2]%s[/color]: %s" % [data["from"], data["say"]]
93 )
94 messages.showMessage("[color=#fafad2]%s[/color]: %s" % [data["from"], data["say"]])
95
96
97func _send_hi(_discard):
98 pass
diff --git a/Archipelago/panel.gd b/Archipelago/panel.gd index fc5963a..ce632c5 100644 --- a/Archipelago/panel.gd +++ b/Archipelago/panel.gd
@@ -5,6 +5,7 @@ var orig_text = ""
5var atbash_text = "" 5var atbash_text = ""
6var orig_color = Color(0, 0, 0, 0) 6var orig_color = Color(0, 0, 0, 0)
7var solvable = true 7var solvable = true
8var locked = false
8 9
9const kAtbashPre = "abcdefghijklmnopqrstuvwxyz1234567890+-" 10const kAtbashPre = "abcdefghijklmnopqrstuvwxyz1234567890+-"
10const kAtbashPost = "zyxwvutsrqponmlkjihgfedcba0987654321-+" 11const kAtbashPost = "zyxwvutsrqponmlkjihgfedcba0987654321-+"
@@ -39,7 +40,9 @@ func evaluate_solvability():
39 solvable = true 40 solvable = true
40 var missing = [] 41 var missing = []
41 42
42 if apclient._color_shuffle: 43 if locked:
44 solvable = false
45 elif apclient._color_shuffle:
43 for color in data["color"]: 46 for color in data["color"]:
44 if not apclient._has_colors.has(color): 47 if not apclient._has_colors.has(color):
45 missing.append(color) 48 missing.append(color)
@@ -52,6 +55,12 @@ func evaluate_solvability():
52 self.get_parent().get_node("Viewport/GUI/Panel/Label").text = orig_text 55 self.get_parent().get_node("Viewport/GUI/Panel/Label").text = orig_text
53 self.get_parent().get_node("Viewport/GUI/Panel/TextEdit").editable = true 56 self.get_parent().get_node("Viewport/GUI/Panel/TextEdit").editable = true
54 self.get_parent().get_node("Quad").get_surface_material(0).albedo_color = orig_color 57 self.get_parent().get_node("Quad").get_surface_material(0).albedo_color = orig_color
58 elif locked:
59 self.get_parent().get_node("Viewport/GUI/Panel/Label").text = "Locked"
60 self.get_parent().get_node("Viewport/GUI/Panel/TextEdit").editable = false
61 self.get_parent().get_node("Quad").get_surface_material(0).albedo_color = Color(
62 0.2, 0.7, 0.7
63 )
55 else: 64 else:
56 var missing_text = "Missing: " 65 var missing_text = "Missing: "
57 for thing in missing: 66 for thing in missing:
diff --git a/Archipelago/pause_menu.gd b/Archipelago/pause_menu.gd index 40994d9..62ba6c3 100644 --- a/Archipelago/pause_menu.gd +++ b/Archipelago/pause_menu.gd
@@ -6,3 +6,8 @@ func _main_menu():
6 apclient.disconnect_from_ap() 6 apclient.disconnect_from_ap()
7 7
8 ._main_menu() 8 ._main_menu()
9
10
11func _pause_game():
12 get_tree().get_root().get_node("Spatial/AP_TextClient").dismiss()
13 ._pause_game()
diff --git a/Archipelago/settings_screen.gd b/Archipelago/settings_screen.gd index 956d966..ec3b5f1 100644 --- a/Archipelago/settings_screen.gd +++ b/Archipelago/settings_screen.gd
@@ -38,6 +38,7 @@ func _ready():
38 apclient_instance.SCRIPT_panel = load("user://maps/Archipelago/panel.gd") 38 apclient_instance.SCRIPT_panel = load("user://maps/Archipelago/panel.gd")
39 var pilg_term = load("user://maps/Archipelago/pilgrimage_terminator.gd") 39 var pilg_term = load("user://maps/Archipelago/pilgrimage_terminator.gd")
40 apclient_instance.SCRIPT_pilgrimage_terminator = pilg_term 40 apclient_instance.SCRIPT_pilgrimage_terminator = pilg_term
41 apclient_instance.SCRIPT_textclient = load("user://maps/Archipelago/textclient.gd")
41 apclient_instance.SCRIPT_uuid = load("user://maps/Archipelago/vendor/uuid.gd") 42 apclient_instance.SCRIPT_uuid = load("user://maps/Archipelago/vendor/uuid.gd")
42 43
43 var apdata = ResourceLoader.load("user://maps/Archipelago/gamedata.gd") 44 var apdata = ResourceLoader.load("user://maps/Archipelago/gamedata.gd")
diff --git a/Archipelago/textclient.gd b/Archipelago/textclient.gd new file mode 100644 index 0000000..3abd9e0 --- /dev/null +++ b/Archipelago/textclient.gd
@@ -0,0 +1,100 @@
1extends Node
2
3var panel
4var label
5var entry
6var is_open = false
7
8
9func _ready():
10 pause_mode = PAUSE_MODE_PROCESS
11
12 panel = Panel.new()
13 panel.set_name("Panel")
14 panel.margin_left = 100
15 panel.margin_right = 1820
16 panel.margin_top = 100
17 panel.margin_bottom = 980
18 panel.visible = false
19 add_child(panel)
20
21 label = RichTextLabel.new()
22 label.set_name("Label")
23 label.margin_left = 80
24 label.margin_right = 1640
25 label.margin_top = 80
26 label.margin_bottom = 720
27 label.scroll_following = true
28 label.selection_enabled = true
29 panel.add_child(label)
30
31 var dynamic_font = DynamicFont.new()
32 dynamic_font.font_data = load("res://fonts/Lingo.ttf")
33 dynamic_font.size = 36
34 label.push_font(dynamic_font)
35
36 var entry_style = StyleBoxFlat.new()
37 entry_style.bg_color = Color(0.9, 0.9, 0.9, 1)
38
39 entry = LineEdit.new()
40 entry.set_name("Entry")
41 entry.margin_left = 80
42 entry.margin_right = 1640
43 entry.margin_top = 760
44 entry.margin_bottom = 840
45 entry.add_font_override("font", dynamic_font)
46 entry.add_color_override("font_color", Color(0, 0, 0, 1))
47 entry.add_color_override("cursor_color", Color(0, 0, 0, 1))
48 entry.add_stylebox_override("focus", entry_style)
49 panel.add_child(entry)
50 entry.connect("text_entered", self, "text_entered")
51
52
53func _input(event):
54 if event is InputEventKey and event.pressed:
55 if event.scancode == KEY_TAB and !Input.is_key_pressed(KEY_SHIFT):
56 if !get_tree().paused:
57 is_open = true
58 get_tree().paused = true
59 Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
60 panel.visible = true
61 entry.grab_focus()
62 get_tree().set_input_as_handled()
63 else:
64 dismiss()
65 elif event.scancode == KEY_ESCAPE:
66 if is_open:
67 dismiss()
68 get_tree().set_input_as_handled()
69
70
71func dismiss():
72 if is_open:
73 get_tree().paused = false
74 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
75 panel.visible = false
76 is_open = false
77
78
79func parse_printjson(text):
80 if !label.text.empty():
81 label.append_bbcode("\n")
82
83 label.append_bbcode(text)
84
85
86func text_entered(text):
87 var apclient = global.get_node("Archipelago")
88 var cmd = text.trim_suffix("\n")
89 if cmd.begins_with("/say "):
90 if apclient.enable_multiplayer:
91 var msg = cmd.trim_prefix("/say ")
92 parse_printjson(
93 "[LOCAL] [color=#ee00ee]%s[/color]: %s" % [apclient.get_player_name(), msg]
94 )
95 get_tree().get_root().get_node("Spatial/Multiplayer").say(msg)
96 else:
97 parse_printjson("Multiplayer must be enabled to use /say")
98 else:
99 apclient.say(cmd)
100 entry.text = ""
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c5113a..d34321d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,5 +1,91 @@
1# lingo-archipelago Releases 1# lingo-archipelago Releases
2 2
3## v5.0.1 - 2024-12-08
4
5- This update adds a workaround for
6 [a logic error in Archipelago 0.5.1](https://github.com/ArchipelagoMW/Archipelago/pull/4342).
7 If your slot is affected by this bug (if you are playing panels mode door
8 shuffle in a multiworld generated on Archipelago 0.5.1), the following changes
9 will occur:
10 - The broken panels (listed at the above link) will now unlock at one
11 Progressive Number Hunt earlier than would be expected.
12 - The doors covering the broken panels will now open when all of the numbers
13 in the previous set have been solved, instead of when the number in the
14 number hunt hallway is solved.
15- This can lead to some non-intuitive gameplay, because the panels no longer
16 unlock the way they do in the base game. To help with this, v0.11.5 of the
17 tracker now uses the 0.5.1 broken logic, so that you can always tell what is
18 expected of you.
19- There is already a fix for the logic error, and it will likely be included as
20 part of the next major Archipelago release. I apologize for the inconvenience.
21
22Download:
23[lingo-archipelago-v5.0.1.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v5.0.1.zip)<br/>
24Source: [v5.0.1](https://code.fourisland.com/lingo-archipelago/tag/?h=v5.0.1)
25
26## v5.0.0 - 2024-11-26
27
28- [Archipelago 0.5.1](https://github.com/ArchipelagoMW/Archipelago/releases/tag/0.5.1)
29 has been released! this release includes a couple of major new features for
30 Lingo:
31 - There is a new type of door shuffle called "panels mode". In panels mode,
32 the doors open when their vanilla panels are solved, but the panels
33 themselves are locked behind items. This provides a vanilla-like progression
34 route through the game, while maintaining a door shuffle amount of
35 progression gating.
36 - By default, checks that are behind your victory condition are now removed
37 from your world.
38 - There are also a number of minor tweaks and bug fixes, which you can see in
39 the above changelog.
40- This update should retain compatibility with Archipelago 0.5.0 worlds.
41- The messages in the bottom left corner now lets you know if an item you sent
42 out was hinted.
43- The text in the text client is now selectable.
44
45Download:
46[lingo-archipelago-v5.0.0.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v5.0.0.zip)<br/>
47Source: [v5.0.0](https://code.fourisland.com/lingo-archipelago/tag/?h=v5.0.0)
48
49## v4.2.1 - 2024-09-21
50
51- The text client will no longer open if SHIFT is being held, to prevent
52 conflicts with the Steam Overlay.
53
54Download:
55[lingo-archipelago-v4.2.1.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v4.2.1.zip)<br/>
56Source: [v4.2.1](https://code.fourisland.com/lingo-archipelago/tag/?h=v4.2.1)
57
58## v4.2.0 - 2024-09-20
59
60- Added a proximity chat feature. You can use the command "/say" followed by a
61 message in the text client, and it will broadcast the message only to other
62 nearby Lingo players.
63
64Download:
65[lingo-archipelago-v4.2.0.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v4.2.0.zip)<br/>
66Source: [v4.2.0](https://code.fourisland.com/lingo-archipelago/tag/?h=v4.2.0)
67
68## v4.1.0 - 2024-09-02
69
70- Added an in-game text client, which can be used to talk to other players and
71 issue commands, similar to the official Archipelago Text Client app. Press TAB
72 to open or close the text client.
73
74Download:
75[lingo-archipelago-v4.1.0.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v4.1.0.zip)<br/>
76Source: [v4.1.0](https://code.fourisland.com/lingo-archipelago/tag/?h=v4.1.0)
77
78## v4.0.2 - 2024-07-14
79
80- Multiplayer mode now shows a player's alias in addition to their slot name.
81- Messages are now cleared out between connections.
82- Fixed issue where traps and Geronimo state could persist when switching from
83 an active slot to a new slot.
84
85Download:
86[lingo-archipelago-v4.0.2.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v4.0.2.zip)<br/>
87Source: [v4.0.2](https://code.fourisland.com/lingo-archipelago/tag/?h=v4.0.2)
88
3## v4.0.1 - 2024-07-02 89## v4.0.1 - 2024-07-02
4 90
5- Fixed issue where paired panels would become proxied on panelsanity under 91- Fixed issue where paired panels would become proxied on panelsanity under
diff --git a/README.md b/README.md index a242f95..dcdc9cd 100644 --- a/README.md +++ b/README.md
@@ -92,6 +92,14 @@ pick and choose which ones you would like to use.
92 the numbering can change. You can also specify whether access to the sunwarps 92 the numbering can change. You can also specify whether access to the sunwarps
93 should be locked behind receiving certain items. 93 should be locked behind receiving certain items.
94 94
95There is also a built-in text client you can use to talk to other players and
96issue commands. You can open and close it by pressing tab.
97
98The command "/say" can be used in the built-in text client to send messages that
99are only visible to players that you can currently see. Note that this is not
100symmetric: it is possible that you may see someone who cannot see you back, if,
101for instance, there is a door in between you and it is only open on your slot.
102
95## Frequently Asked Questions 103## Frequently Asked Questions
96 104
97### What are location checks in this game? 105### What are location checks in this game?
diff --git a/util/generate_gamedata.rb b/util/generate_gamedata.rb index ce8df43..83099ad 100644 --- a/util/generate_gamedata.rb +++ b/util/generate_gamedata.rb
@@ -12,20 +12,24 @@ CLASSIFICATION_SMALL_SPHERE_ONE = 8
12 12
13panel_to_id = {} 13panel_to_id = {}
14door_groups = {} 14door_groups = {}
15panel_groups = {}
15warp_groups = {} 16warp_groups = {}
16 17
17panel_output = [] 18panel_output = []
18door_ids_by_item_id = {} 19door_ids_by_item_id = {}
19painting_ids_by_item_id = {} 20painting_ids_by_item_id = {}
21panel_ids_by_item_id = {}
20warp_ids_by_item_id = {} 22warp_ids_by_item_id = {}
21panel_ids_by_location_id = {} 23panel_ids_by_location_id = {}
22classification_by_location_id = {} 24classification_by_location_id = {}
23sunwarps = Array.new(12) {Hash.new} 25sunwarps = Array.new(12) {Hash.new}
24mentioned_doors = Set[] 26mentioned_doors = Set[]
25mentioned_paintings = Set[] 27mentioned_paintings = Set[]
28mentioned_panels = Set[]
26mentioned_warps = Set[] 29mentioned_warps = Set[]
27painting_output = {} 30painting_output = {}
28items_by_progressive_id = {} 31door_items_by_progressive_id = {}
32panel_items_by_progressive_id = {}
29 33
30ids_config = YAML.load_file(idspath) 34ids_config = YAML.load_file(idspath)
31 35
@@ -110,15 +114,29 @@ config.each do |room_name, room_data|
110 end 114 end
111 115
112 if room_data.include? "progression" 116 if room_data.include? "progression"
113 room_data["progression"].each do |progressive_item_name, progression| 117 room_data["progression"].each do |progressive_item_name, pdata|
114 progressive_id = ids_config["progression"][progressive_item_name] 118 progressive_id = ids_config["progression"][progressive_item_name]
115 items_by_progressive_id[progressive_id] = []
116 119
117 progression.each do |item| 120 if pdata.include? "doors"
118 item_room_name = (item.kind_of? Hash) ? item["room"] : room_name 121 door_items_by_progressive_id[progressive_id] = []
119 item_item_name = (item.kind_of? Hash) ? item["door"] : item
120 122
121 items_by_progressive_id[progressive_id] << ids_config["doors"][item_room_name][item_item_name]["item"] 123 pdata["doors"].each do |item|
124 item_room_name = (item.kind_of? Hash) ? item["room"] : room_name
125 item_item_name = (item.kind_of? Hash) ? item["door"] : item
126
127 door_items_by_progressive_id[progressive_id] << ids_config["doors"][item_room_name][item_item_name]["item"]
128 end
129 end
130
131 if pdata.include? "panel_doors"
132 panel_items_by_progressive_id[progressive_id] = []
133
134 pdata["panel_doors"].each do |item|
135 item_room_name = (item.kind_of? Hash) ? item["room"] : room_name
136 item_item_name = (item.kind_of? Hash) ? item["panel_door"] : item
137
138 panel_items_by_progressive_id[progressive_id] << ids_config["panel_doors"][item_room_name][item_item_name]
139 end
122 end 140 end
123 end 141 end
124 end 142 end
@@ -206,6 +224,26 @@ config.each do |room_name, room_data|
206 end 224 end
207 end 225 end
208 end 226 end
227
228 if room_data.include? "panel_doors"
229 room_data["panel_doors"].each do |panel_door_name, panel_door|
230 item_id = ids_config["panel_doors"][room_name][panel_door_name]
231
232 panel_ids_by_item_id[item_id] = panel_door["panels"].map do |panel_identifier|
233 other_room_name = (panel_identifier.kind_of? String) ? room_name : panel_identifier["room"]
234 other_panel_name = (panel_identifier.kind_of? String) ? panel_identifier : panel_identifier["panel"]
235
236 config[other_room_name]["panels"][other_panel_name]["id"]
237 end
238
239 mentioned_panels.merge(panel_ids_by_item_id[item_id])
240
241 if panel_door.include? "panel_group"
242 panel_groups[panel_door["panel_group"]] ||= Set[]
243 panel_groups[panel_door["panel_group"]].merge(panel_ids_by_item_id[item_id])
244 end
245 end
246 end
209end 247end
210 248
211door_groups.each do |group_name, door_ids| 249door_groups.each do |group_name, door_ids|
@@ -213,6 +251,11 @@ door_groups.each do |group_name, door_ids|
213 door_ids_by_item_id[item_id] = door_ids.to_a 251 door_ids_by_item_id[item_id] = door_ids.to_a
214end 252end
215 253
254panel_groups.each do |group_name, panel_ids|
255 item_id = ids_config["panel_groups"][group_name]
256 panel_ids_by_item_id[item_id] = panel_ids.to_a
257end
258
216warp_groups.each do |group_name, warp_ids| 259warp_groups.each do |group_name, warp_ids|
217 item_id = ids_config["door_groups"][group_name] 260 item_id = ids_config["door_groups"][group_name]
218 warp_ids_by_item_id[item_id] = warp_ids.to_a 261 warp_ids_by_item_id[item_id] = warp_ids.to_a
@@ -231,6 +274,12 @@ File.open(outputpath, "w") do |f|
231 "\"#{door_id}\"" 274 "\"#{door_id}\""
232 end.join(",") + "]" 275 end.join(",") + "]"
233 end.join(",")) 276 end.join(","))
277 f.write "}\nvar panel_ids_by_item_id = {"
278 f.write(panel_ids_by_item_id.map do |item_id, panel_ids|
279 "#{item_id}:[" + panel_ids.map do |panel_id|
280 "\"#{panel_id}\""
281 end.join(",") + "]"
282 end.join(","))
234 f.write "}\nvar painting_ids_by_item_id = {" 283 f.write "}\nvar painting_ids_by_item_id = {"
235 f.write(painting_ids_by_item_id.map do |item_id, painting_ids| 284 f.write(painting_ids_by_item_id.map do |item_id, painting_ids|
236 "#{item_id}:[" + painting_ids.map do |painting_id| 285 "#{item_id}:[" + painting_ids.map do |painting_id|
@@ -257,6 +306,10 @@ File.open(outputpath, "w") do |f|
257 f.write(mentioned_paintings.map do |painting_id| 306 f.write(mentioned_paintings.map do |painting_id|
258 "\"#{painting_id}\"" 307 "\"#{painting_id}\""
259 end.join(",")) 308 end.join(","))
309 f.write "]\nvar mentioned_panels = ["
310 f.write(mentioned_panels.map do |panel_id|
311 "\"#{panel_id}\""
312 end.join(","))
260 f.write "]\nvar mentioned_warps = [" 313 f.write "]\nvar mentioned_warps = ["
261 f.write(mentioned_warps.map do |warp_id| 314 f.write(mentioned_warps.map do |warp_id|
262 "\"#{warp_id}\"" 315 "\"#{warp_id}\""
@@ -273,8 +326,12 @@ File.open(outputpath, "w") do |f|
273 f.write(sunwarps.map do |sunwarp| 326 f.write(sunwarps.map do |sunwarp|
274 "{\"orientation\":\"#{sunwarp["orientation"]}\",\"entrance_indicator_pos\":#{sunwarp["entrance_indicator_pos"].to_s}}" 327 "{\"orientation\":\"#{sunwarp["orientation"]}\",\"entrance_indicator_pos\":#{sunwarp["entrance_indicator_pos"].to_s}}"
275 end.join(",")) 328 end.join(","))
276 f.write "]\nvar items_by_progressive_id = {" 329 f.write "]\nvar door_items_by_progressive_id = {"
277 f.write(items_by_progressive_id.map do |item_id, progression_ids| 330 f.write(door_items_by_progressive_id.map do |item_id, progression_ids|
331 "#{item_id}:[" + progression_ids.map(&:to_s).join(",") + "]"
332 end.join(","))
333 f.write "}\nvar panel_items_by_progressive_id = {"
334 f.write(panel_items_by_progressive_id.map do |item_id, progression_ids|
278 "#{item_id}:[" + progression_ids.map(&:to_s).join(",") + "]" 335 "#{item_id}:[" + progression_ids.map(&:to_s).join(",") + "]"
279 end.join(",")) 336 end.join(","))
280 f.write "}" 337 f.write "}"