about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Archipelago/client.gd54
-rw-r--r--Archipelago/effects.gd66
-rw-r--r--Archipelago/load.gd1
-rw-r--r--Archipelago/multiplayer.gd55
-rw-r--r--Archipelago/textclient.gd16
-rw-r--r--CHANGELOG.md19
-rw-r--r--README.md5
7 files changed, 199 insertions, 17 deletions
diff --git a/Archipelago/client.gd b/Archipelago/client.gd index 5f8be4e..8cddb83 100644 --- a/Archipelago/client.gd +++ b/Archipelago/client.gd
@@ -19,7 +19,7 @@ var enable_multiplayer = false
19var track_player = false 19var track_player = false
20var connection_history = [] 20var connection_history = []
21 21
22const my_version = "panels-beta6" 22const my_version = "4.2.1"
23const ap_version = {"major": 0, "minor": 5, "build": 0, "class": "Version"} 23const ap_version = {"major": 0, "minor": 5, "build": 0, "class": "Version"}
24const color_items = [ 24const color_items = [
25 "White", "Black", "Red", "Blue", "Green", "Brown", "Gray", "Orange", "Purple", "Yellow" 25 "White", "Black", "Red", "Blue", "Green", "Brown", "Gray", "Orange", "Purple", "Yellow"
@@ -114,6 +114,7 @@ var _pilgrimage_allows_roof_access = false
114var _pilgrimage_allows_paintings = false 114var _pilgrimage_allows_paintings = false
115var _sunwarp_shuffle = false 115var _sunwarp_shuffle = false
116var _sunwarp_mapping = [] 116var _sunwarp_mapping = []
117var _speed_boost_mode = false
117var _slot_seed = 0 118var _slot_seed = 0
118 119
119var _map_loaded = false 120var _map_loaded = false
@@ -127,8 +128,11 @@ var _puzzle_skips = 0
127var _cached_slowness = 0 128var _cached_slowness = 0
128var _cached_iceland = 0 129var _cached_iceland = 0
129var _cached_atbash = 0 130var _cached_atbash = 0
131var _cached_speed_boosts = 0
130var _geronimo_skip = false 132var _geronimo_skip = false
131var _checked_paintings = [] 133var _checked_paintings = []
134var _hints_key = ""
135var _hinted_locations = []
132 136
133signal could_not_connect 137signal could_not_connect
134signal connect_status 138signal connect_status
@@ -346,6 +350,10 @@ func _on_data():
346 _sunwarp_shuffle = false 350 _sunwarp_shuffle = false
347 if _slot_data.has("sunwarp_permutation"): 351 if _slot_data.has("sunwarp_permutation"):
348 _sunwarp_mapping = _slot_data["sunwarp_permutation"] 352 _sunwarp_mapping = _slot_data["sunwarp_permutation"]
353 if _slot_data.has("speed_boost_mode"):
354 _speed_boost_mode = _slot_data["speed_boost_mode"]
355 else:
356 _speed_boost_mode = false
349 357
350 if ( 358 if (
351 _location_classification_bit != kCLASSIFICATION_LOCAL_INSANITY 359 _location_classification_bit != kCLASSIFICATION_LOCAL_INSANITY
@@ -364,6 +372,7 @@ func _on_data():
364 _cached_slowness = 0 372 _cached_slowness = 0
365 _cached_iceland = 0 373 _cached_iceland = 0
366 _cached_atbash = 0 374 _cached_atbash = 0
375 _cached_speed_boosts = 0
367 _geronimo_skip = false 376 _geronimo_skip = false
368 377
369 _localdata_file = "user://archipelago_data/%s_%d" % [_seed, _slot] 378 _localdata_file = "user://archipelago_data/%s_%d" % [_seed, _slot]
@@ -395,6 +404,9 @@ func _on_data():
395 if localdata.size() > 5: 404 if localdata.size() > 5:
396 _geronimo_skip = localdata[5] 405 _geronimo_skip = localdata[5]
397 406
407 if localdata.size() > 6:
408 _cached_speed_boosts = localdata[6]
409
398 requestSync() 410 requestSync()
399 411
400 sendMessage( 412 sendMessage(
@@ -409,6 +421,11 @@ func _on_data():
409 ] 421 ]
410 ) 422 )
411 423
424 _hints_key = "_read_hints_%d_%d" % [_team, _slot]
425 sendMessage(
426 [{"cmd": "SetNotify", "keys": [_hints_key]}, {"cmd": "Get", "keys": [_hints_key]}]
427 )
428
412 emit_signal("client_connected") 429 emit_signal("client_connected")
413 430
414 elif cmd == "ConnectionRefused": 431 elif cmd == "ConnectionRefused":
@@ -506,9 +523,12 @@ func _on_data():
506 ) 523 )
507 else: 524 else:
508 if message["receiving"] != _slot: 525 if message["receiving"] != _slot:
509 messages.showMessage( 526 var sentMsg = (
510 "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name] 527 "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name]
511 ) 528 )
529 if _hinted_locations.has(message["item"]["location"]):
530 sentMsg += " ([color=#fafad2]Hinted![/color])"
531 messages.showMessage(sentMsg)
512 532
513 elif cmd == "Bounced": 533 elif cmd == "Bounced":
514 if ( 534 if (
@@ -529,8 +549,15 @@ func _on_data():
529 get_tree().get_root().get_node("Spatial/player/pause_menu")._reload() 549 get_tree().get_root().get_node("Spatial/player/pause_menu")._reload()
530 550
531 elif cmd == "SetReply": 551 elif cmd == "SetReply":
532 if message.has("key") and message["key"] == ("Lingo_%d_Paintings" % _slot): 552 if message.has("key"):
533 _checked_paintings = message["value"] 553 if message["key"] == ("Lingo_%d_Paintings" % _slot):
554 _checked_paintings = message["value"]
555 elif message["key"] == _hints_key:
556 parseHints(message["value"])
557
558 elif cmd == "Retrieved":
559 if message.has("keys") and message["keys"].has(_hints_key):
560 parseHints(message["keys"][_hints_key])
534 561
535 562
536func _process(_delta): 563func _process(_delta):
@@ -584,7 +611,8 @@ func saveLocaldata():
584 effects_node.slowness_remaining, 611 effects_node.slowness_remaining,
585 effects_node.iceland_remaining, 612 effects_node.iceland_remaining,
586 effects_node.atbash_remaining, 613 effects_node.atbash_remaining,
587 _geronimo_skip 614 _geronimo_skip,
615 effects_node.speed_boosts_remaining,
588 ] 616 ]
589 file.store_var(data, true) 617 file.store_var(data, true)
590 file.close() 618 file.close()
@@ -831,12 +859,14 @@ func processItem(item, index, from, flags):
831 ) 859 )
832 860
833 var effects_node = get_tree().get_root().get_node("Spatial/AP_Effects") 861 var effects_node = get_tree().get_root().get_node("Spatial/AP_Effects")
834 if item_name == "Slowness Trap": 862 if item_name == "Slowness Trap" and !_speed_boost_mode:
835 effects_node.trigger_slowness_trap() 863 effects_node.trigger_slowness_trap()
836 if item_name == "Iceland Trap": 864 if item_name == "Iceland Trap":
837 effects_node.trigger_iceland_trap() 865 effects_node.trigger_iceland_trap()
838 if item_name == "Atbash Trap": 866 if item_name == "Atbash Trap":
839 effects_node.trigger_atbash_trap() 867 effects_node.trigger_atbash_trap()
868 if item_name == "Speed Boost" and _speed_boost_mode:
869 effects_node.trigger_speed_boost()
840 if item_name == "Puzzle Skip": 870 if item_name == "Puzzle Skip":
841 _puzzle_skips += 1 871 _puzzle_skips += 1
842 872
@@ -887,6 +917,14 @@ func checkPainting(painting_id):
887 setValue("Paintings", [painting_id], "add") 917 setValue("Paintings", [painting_id], "add")
888 918
889 919
920func parseHints(hints):
921 _hinted_locations.clear()
922
923 for hint in hints:
924 if hint["finding_player"] == int(_slot):
925 _hinted_locations.append(hint["location"])
926
927
890func colorForItemType(flags): 928func colorForItemType(flags):
891 var int_flags = int(flags) 929 var int_flags = int(flags)
892 if int_flags & 1: # progression 930 if int_flags & 1: # progression
@@ -936,6 +974,10 @@ func parse_printjson(message):
936 textclient_node.parse_printjson("".join(parts)) 974 textclient_node.parse_printjson("".join(parts))
937 975
938 976
977func get_player_name():
978 return _player_name_by_slot[_slot]
979
980
939func compareVersion(lhs, rhs): 981func compareVersion(lhs, rhs):
940 if lhs["major"] == rhs["major"]: 982 if lhs["major"] == rhs["major"]:
941 if lhs["minor"] == rhs["minor"]: 983 if lhs["minor"] == rhs["minor"]:
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/load.gd b/Archipelago/load.gd index ce7bba5..4811b47 100644 --- a/Archipelago/load.gd +++ b/Archipelago/load.gd
@@ -674,6 +674,7 @@ func _load():
674 # Create the multiplayer node, if needed. 674 # Create the multiplayer node, if needed.
675 if apclient.enable_multiplayer: 675 if apclient.enable_multiplayer:
676 var multiplayer_node = apclient.SCRIPT_multiplayer.new() 676 var multiplayer_node = apclient.SCRIPT_multiplayer.new()
677 multiplayer_node.name = "Multiplayer"
677 multiplayer_node.ghost_mode = true 678 multiplayer_node.ghost_mode = true
678 add_child(multiplayer_node) 679 add_child(multiplayer_node)
679 680
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/textclient.gd b/Archipelago/textclient.gd index 7bddf38..3abd9e0 100644 --- a/Archipelago/textclient.gd +++ b/Archipelago/textclient.gd
@@ -25,6 +25,7 @@ func _ready():
25 label.margin_top = 80 25 label.margin_top = 80
26 label.margin_bottom = 720 26 label.margin_bottom = 720
27 label.scroll_following = true 27 label.scroll_following = true
28 label.selection_enabled = true
28 panel.add_child(label) 29 panel.add_child(label)
29 30
30 var dynamic_font = DynamicFont.new() 31 var dynamic_font = DynamicFont.new()
@@ -51,7 +52,7 @@ func _ready():
51 52
52func _input(event): 53func _input(event):
53 if event is InputEventKey and event.pressed: 54 if event is InputEventKey and event.pressed:
54 if event.scancode == KEY_TAB: 55 if event.scancode == KEY_TAB and !Input.is_key_pressed(KEY_SHIFT):
55 if !get_tree().paused: 56 if !get_tree().paused:
56 is_open = true 57 is_open = true
57 get_tree().paused = true 58 get_tree().paused = true
@@ -84,5 +85,16 @@ func parse_printjson(text):
84 85
85func text_entered(text): 86func text_entered(text):
86 var apclient = global.get_node("Archipelago") 87 var apclient = global.get_node("Archipelago")
87 apclient.say(text.trim_suffix("\n")) 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)
88 entry.text = "" 100 entry.text = ""
diff --git a/CHANGELOG.md b/CHANGELOG.md index d00999c..6bea522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,5 +1,24 @@
1# lingo-archipelago Releases 1# lingo-archipelago Releases
2 2
3## v4.2.1 - 2024-09-21
4
5- The text client will no longer open if SHIFT is being held, to prevent
6 conflicts with the Steam Overlay.
7
8Download:
9[lingo-archipelago-v4.2.1.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v4.2.1.zip)<br/>
10Source: [v4.2.1](https://code.fourisland.com/lingo-archipelago/tag/?h=v4.2.1)
11
12## v4.2.0 - 2024-09-20
13
14- Added a proximity chat feature. You can use the command "/say" followed by a
15 message in the text client, and it will broadcast the message only to other
16 nearby Lingo players.
17
18Download:
19[lingo-archipelago-v4.2.0.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v4.2.0.zip)<br/>
20Source: [v4.2.0](https://code.fourisland.com/lingo-archipelago/tag/?h=v4.2.0)
21
3## v4.1.0 - 2024-09-02 22## v4.1.0 - 2024-09-02
4 23
5- Added an in-game text client, which can be used to talk to other players and 24- Added an in-game text client, which can be used to talk to other players and
diff --git a/README.md b/README.md index a8491d6..dcdc9cd 100644 --- a/README.md +++ b/README.md
@@ -95,6 +95,11 @@ pick and choose which ones you would like to use.
95There is also a built-in text client you can use to talk to other players and 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. 96issue commands. You can open and close it by pressing tab.
97 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
98## Frequently Asked Questions 103## Frequently Asked Questions
99 104
100### What are location checks in this game? 105### What are location checks in this game?