about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Archipelago/client.gd71
-rw-r--r--Archipelago/load.gd41
-rw-r--r--Archipelago/mypainting.gd113
-rw-r--r--Archipelago/player.gd2
-rw-r--r--Archipelago/settings_menu.gd25
-rw-r--r--Archipelago/settings_screen.gd1
-rw-r--r--CHANGELOG.md47
7 files changed, 203 insertions, 97 deletions
diff --git a/Archipelago/client.gd b/Archipelago/client.gd index c690986..8a15d03 100644 --- a/Archipelago/client.gd +++ b/Archipelago/client.gd
@@ -18,7 +18,7 @@ var enable_multiplayer = false
18var track_player = false 18var track_player = false
19var connection_history = [] 19var connection_history = []
20 20
21const my_version = "3.0.1" 21const my_version = "3.2.1"
22const ap_version = {"major": 0, "minor": 4, "build": 6, "class": "Version"} 22const ap_version = {"major": 0, "minor": 4, "build": 6, "class": "Version"}
23const color_items = [ 23const color_items = [
24 "White", "Black", "Red", "Blue", "Green", "Brown", "Gray", "Orange", "Purple", "Yellow" 24 "White", "Black", "Red", "Blue", "Green", "Brown", "Gray", "Orange", "Purple", "Yellow"
@@ -81,6 +81,7 @@ var _team = 0
81var _slot = 0 81var _slot = 0
82var _players = [] 82var _players = []
83var _player_name_by_slot = {} 83var _player_name_by_slot = {}
84var _game_by_player = {}
84var _checked_locations = [] 85var _checked_locations = []
85var _slot_data = {} 86var _slot_data = {}
86var _paintings_mapping = {} 87var _paintings_mapping = {}
@@ -116,6 +117,7 @@ var _cached_slowness = 0
116var _cached_iceland = 0 117var _cached_iceland = 0
117var _cached_atbash = 0 118var _cached_atbash = 0
118var _geronimo_skip = false 119var _geronimo_skip = false
120var _checked_paintings = []
119 121
120signal could_not_connect 122signal could_not_connect
121signal connect_status 123signal connect_status
@@ -133,6 +135,10 @@ func _init():
133 var data = file.get_var(true) 135 var data = file.get_var(true)
134 file.close() 136 file.close()
135 137
138 if typeof(data) != TYPE_ARRAY:
139 global._print("AP settings file is corrupted")
140 data = []
141
136 if data.size() > 0: 142 if data.size() > 0:
137 ap_server = data[0] 143 ap_server = data[0]
138 if data.size() > 1: 144 if data.size() > 1:
@@ -257,6 +263,7 @@ func _on_data():
257 263
258 for player in _players: 264 for player in _players:
259 _player_name_by_slot[player["slot"]] = player["alias"] 265 _player_name_by_slot[player["slot"]] = player["alias"]
266 _game_by_player[player["slot"]] = message["slot_info"][str(player["slot"])]["game"]
260 267
261 _death_link = _slot_data.has("death_link") and _slot_data["death_link"] 268 _death_link = _slot_data.has("death_link") and _slot_data["death_link"]
262 if _death_link: 269 if _death_link:
@@ -340,6 +347,10 @@ func _on_data():
340 var localdata = ap_file.get_var(true) 347 var localdata = ap_file.get_var(true)
341 ap_file.close() 348 ap_file.close()
342 349
350 if typeof(localdata) != TYPE_ARRAY:
351 global._print("AP localdata file is corrupted")
352 localdata = []
353
343 if localdata.size() > 0: 354 if localdata.size() > 0:
344 _last_new_item = localdata[0] 355 _last_new_item = localdata[0]
345 else: 356 else:
@@ -370,6 +381,18 @@ func _on_data():
370 381
371 requestSync() 382 requestSync()
372 383
384 sendMessage(
385 [
386 {
387 "cmd": "Set",
388 "key": "Lingo_%d_Paintings" % [_slot],
389 "default": [],
390 "want_reply": true,
391 "operations": [{"operation": "default", "value": []}]
392 }
393 ]
394 )
395
373 emit_signal("client_connected") 396 emit_signal("client_connected")
374 397
375 elif cmd == "ConnectionRefused": 398 elif cmd == "ConnectionRefused":
@@ -437,12 +460,14 @@ func _on_data():
437 continue 460 continue
438 461
439 var item_name = "Unknown" 462 var item_name = "Unknown"
440 if _item_id_to_name.has(message["item"]["item"]): 463 var item_player_game = _game_by_player[message["receiving"]]
441 item_name = _item_id_to_name[message["item"]["item"]] 464 if _item_id_to_name[item_player_game].has(message["item"]["item"]):
465 item_name = _item_id_to_name[item_player_game][message["item"]["item"]]
442 466
443 var location_name = "Unknown" 467 var location_name = "Unknown"
444 if _location_id_to_name.has(message["item"]["location"]): 468 var location_player_game = _game_by_player[message["item"]["player"]]
445 location_name = _location_id_to_name[message["item"]["location"]] 469 if _location_id_to_name[location_player_game].has(message["item"]["location"]):
470 location_name = _location_id_to_name[location_player_game][message["item"]["location"]]
446 471
447 var player_name = "Unknown" 472 var player_name = "Unknown"
448 if _player_name_by_slot.has(message["receiving"]): 473 if _player_name_by_slot.has(message["receiving"]):
@@ -485,6 +510,10 @@ func _on_data():
485 # Return the player home. 510 # Return the player home.
486 get_tree().get_root().get_node("Spatial/player/pause_menu")._reload() 511 get_tree().get_root().get_node("Spatial/player/pause_menu")._reload()
487 512
513 elif cmd == "SetReply":
514 if message.has("key") and message["key"] == ("Lingo_%d_Paintings" % _slot):
515 _checked_paintings = message["value"]
516
488 517
489func _process(_delta): 518func _process(_delta):
490 if _should_process: 519 if _should_process:
@@ -592,12 +621,16 @@ func requestDatapackages(games):
592func processDatapackages(): 621func processDatapackages():
593 _item_id_to_name = {} 622 _item_id_to_name = {}
594 _location_id_to_name = {} 623 _location_id_to_name = {}
595 for package in _datapackages.values(): 624 for game in _datapackages.keys():
625 var package = _datapackages[game]
626
627 _item_id_to_name[game] = {}
596 for name in package["item_name_to_id"].keys(): 628 for name in package["item_name_to_id"].keys():
597 _item_id_to_name[package["item_name_to_id"][name]] = name 629 _item_id_to_name[game][package["item_name_to_id"][name]] = name
598 630
631 _location_id_to_name[game] = {}
599 for name in package["location_name_to_id"].keys(): 632 for name in package["location_name_to_id"].keys():
600 _location_id_to_name[package["location_name_to_id"][name]] = name 633 _location_id_to_name[game][package["location_name_to_id"][name]] = name
601 634
602 if _datapackages.has("Lingo"): 635 if _datapackages.has("Lingo"):
603 _item_name_to_id = _datapackages["Lingo"]["item_name_to_id"] 636 _item_name_to_id = _datapackages["Lingo"]["item_name_to_id"]
@@ -639,13 +672,14 @@ func sendLocation(loc_id):
639 _held_locations.append(loc_id) 672 _held_locations.append(loc_id)
640 673
641 674
642func setValue(key, value): 675func setValue(key, value, operation = "replace"):
643 sendMessage( 676 sendMessage(
644 [ 677 [
645 { 678 {
646 "cmd": "Set", 679 "cmd": "Set",
647 "key": "Lingo_%d_%s" % [_slot, key], 680 "key": "Lingo_%d_%s" % [_slot, key],
648 "operations": [{"operation": "replace", "value": value}] 681 "want_reply": false,
682 "operations": [{"operation": operation, "value": value}]
649 } 683 }
650 ] 684 ]
651 ) 685 )
@@ -686,8 +720,8 @@ func processItem(item, index, from, flags):
686 720
687 var gamedata = $Gamedata 721 var gamedata = $Gamedata
688 var item_name = "Unknown" 722 var item_name = "Unknown"
689 if _item_id_to_name.has(item): 723 if _item_id_to_name["Lingo"].has(item):
690 item_name = _item_id_to_name[item] 724 item_name = _item_id_to_name["Lingo"][item]
691 725
692 if gamedata.door_ids_by_item_id.has(int(item)): 726 if gamedata.door_ids_by_item_id.has(int(item)):
693 var doorsNode = get_tree().get_root().get_node("Spatial/Doors") 727 var doorsNode = get_tree().get_root().get_node("Spatial/Doors")
@@ -725,8 +759,8 @@ func processItem(item, index, from, flags):
725 processItem(subitem_id, null, null, null) 759 processItem(subitem_id, null, null, null)
726 _progressive_progress[int(item)] += 1 760 _progressive_progress[int(item)] += 1
727 761
728 if _color_shuffle and color_items.has(_item_id_to_name[item]): 762 if _color_shuffle and color_items.has(_item_id_to_name["Lingo"][item]):
729 var lcol = _item_id_to_name[item].to_lower() 763 var lcol = _item_id_to_name["Lingo"][item].to_lower()
730 if not _has_colors.has(lcol): 764 if not _has_colors.has(lcol):
731 _has_colors.append(lcol) 765 _has_colors.append(lcol)
732 emit_signal("evaluate_solvability") 766 emit_signal("evaluate_solvability")
@@ -801,6 +835,15 @@ func geronimo():
801 saveLocaldata() 835 saveLocaldata()
802 836
803 837
838func checkPainting(painting_id):
839 if _checked_paintings.has(painting_id):
840 return
841
842 _checked_paintings.append(painting_id)
843
844 setValue("Paintings", [painting_id], "add")
845
846
804func colorForItemType(flags): 847func colorForItemType(flags):
805 var int_flags = int(flags) 848 var int_flags = int(flags)
806 if int_flags & 1: # progression 849 if int_flags & 1: # progression
diff --git a/Archipelago/load.gd b/Archipelago/load.gd index 0ed978a..931dfde 100644 --- a/Archipelago/load.gd +++ b/Archipelago/load.gd
@@ -6,7 +6,7 @@ const EXCLUDED_PAINTINGS = [
6 "ascension_nw.tscn", 6 "ascension_nw.tscn",
7 "ascension_se.tscn", 7 "ascension_se.tscn",
8 "ascension_sw.tscn", 8 "ascension_sw.tscn",
9 "dan_L1_gate.tscn", 9 "color_hallways.tscn",
10 "frame.tscn", 10 "frame.tscn",
11 "scenery_0.tscn", 11 "scenery_0.tscn",
12 "scenery_1.tscn", 12 "scenery_1.tscn",
@@ -263,6 +263,9 @@ func _load():
263 hidden_parent.get_node("hidden_door_58").translation.x = 48 263 hidden_parent.get_node("hidden_door_58").translation.x = 48
264 hidden_parent.get_node("hidden_door_58")._setReference("whiteBlock") 264 hidden_parent.get_node("hidden_door_58")._setReference("whiteBlock")
265 265
266 # Remove Fearless entrance indicator.
267 get_node("Decorations/Signs/Miscellaneous/Sign14").queue_free()
268
266 if apclient._panel_shuffle != apclient.kNO_PANEL_SHUFFLE: 269 if apclient._panel_shuffle != apclient.kNO_PANEL_SHUFFLE:
267 # Make The Wondrous's FIRE solely midred. 270 # Make The Wondrous's FIRE solely midred.
268 clear_gridmap_tile(-76.5, 1.5, -73.5) 271 clear_gridmap_tile(-76.5, 1.5, -73.5)
@@ -375,33 +378,23 @@ func _load():
375 ] 378 ]
376 379
377 if apclient._early_color_hallways: 380 if apclient._early_color_hallways:
378 var painting_scene = load("res://nodes/paintings/dan_L1_gate.tscn") 381 var exit_painting = get_node("Decorations/Paintings/crown_painting_exit").duplicate()
379 var mypainting_script = apclient.SCRIPT_mypainting 382 exit_painting.name = "color_hallways_exit"
380 383 exit_painting.translation.x = 48
381 var exit_painting = painting_scene.instance() 384 exit_painting.translation.y = 0.25
382 exit_painting.set_name("color_exit_painting") 385 exit_painting.translation.z = -18
383 exit_painting.translation.x = -98.75
384 exit_painting.translation.y = 1
385 exit_painting.translation.z = 3.5
386 exit_painting.rotation_degrees.y = -90
387
388 var exit_mps = mypainting_script.new()
389 exit_mps.set_name("Script")
390 exit_mps.orientation = "west"
391 exit_painting.add_child(exit_mps)
392 $Decorations/Paintings.add_child(exit_painting) 386 $Decorations/Paintings.add_child(exit_painting)
393 387
388 var painting_scene = load("res://nodes/paintings/color_hallways.tscn")
394 var enter_painting = painting_scene.instance() 389 var enter_painting = painting_scene.instance()
395 enter_painting.set_name("color_enter_painting") 390 enter_painting.set_name("color_hallways")
396 enter_painting.translation.x = 4.5 391 enter_painting.translation.x = 4.5
397 enter_painting.translation.y = 1 392 enter_painting.translation.y = 0.25
398 enter_painting.translation.z = 6.75 393 enter_painting.translation.z = 7
399 394 enter_painting.rotation_degrees.y = 180
400 var enter_mps = mypainting_script.new() 395 enter_painting.set_script(load("res://scripts/painting.gd"))
401 enter_mps.set_name("Script") 396 enter_painting.rotate = "90"
402 enter_mps.orientation = "south" 397 enter_painting.target_path = "../color_hallways_exit"
403 enter_mps.target = exit_mps
404 enter_painting.add_child(enter_mps)
405 $Decorations/Paintings.add_child(enter_painting) 398 $Decorations/Paintings.add_child(enter_painting)
406 399
407 # Randomize the paintings, if necessary. 400 # Randomize the paintings, if necessary.
diff --git a/Archipelago/mypainting.gd b/Archipelago/mypainting.gd index 999b122..7aee434 100644 --- a/Archipelago/mypainting.gd +++ b/Archipelago/mypainting.gd
@@ -30,66 +30,65 @@ func movePainting():
30 30
31 31
32func _looked_at(body, painting): 32func _looked_at(body, painting):
33 if ( 33 if body.is_in_group("player") and (painting.get_name() == self.get_parent().get_name()):
34 target != null 34 var apclient = global.get_node("Archipelago")
35 and body.is_in_group("player") 35 apclient.checkPainting(painting.get_name())
36 and (painting.get_name() == self.get_parent().get_name())
37 ):
38 var target_dir = _dir_to_int(target.orientation)
39 var source_dir = _dir_to_int(orientation)
40 var rotate = target_dir - source_dir
41 if rotate < 0:
42 rotate += 4
43 rotate *= 90
44 36
45 var target_painting = target.get_parent() 37 if target != null:
38 var target_dir = _dir_to_int(target.orientation)
39 var source_dir = _dir_to_int(orientation)
40 var rotate = target_dir - source_dir
41 if rotate < 0:
42 rotate += 4
43 rotate *= 90
46 44
47 # this is ACW 45 var target_painting = target.get_parent()
48 if rotate == 0:
49 body.translation.x = (
50 target_painting.translation.x + (body.translation.x - painting.translation.x)
51 )
52 body.translation.y = (
53 target_painting.translation.y + (body.translation.y - painting.translation.y)
54 )
55 body.translation.z = (
56 target_painting.translation.z + (body.translation.z - painting.translation.z)
57 )
58 elif rotate == 180:
59 body.translation.x = (
60 target_painting.translation.x - (body.translation.x - painting.translation.x)
61 )
62 body.translation.y = (
63 target_painting.translation.y + (body.translation.y - painting.translation.y)
64 )
65 body.translation.z = (
66 target_painting.translation.z - (body.translation.z - painting.translation.z)
67 )
68 body.rotate_y(PI)
69 body.velocity = body.velocity.rotated(Vector3(0, 1, 0), PI)
70 elif rotate == 90:
71 var diff_x = body.translation.x - painting.translation.x
72 var diff_y = body.translation.y - painting.translation.y
73 var diff_z = body.translation.z - painting.translation.z
74 body.translation.x = target_painting.translation.x + diff_z
75 body.translation.y = target_painting.translation.y + diff_y
76 body.translation.z = target_painting.translation.z - diff_x
77 body.rotate_y(PI / 2)
78 body.velocity = body.velocity.rotated(Vector3(0, 1, 0), PI / 2)
79 elif rotate == 270:
80 var diff_x = body.translation.x - painting.translation.x
81 var diff_y = body.translation.y - painting.translation.y
82 var diff_z = body.translation.z - painting.translation.z
83 body.translation.x = target_painting.translation.x - diff_z
84 body.translation.y = target_painting.translation.y + diff_y
85 body.translation.z = target_painting.translation.z + diff_x
86 body.rotate_y(3 * PI / 2)
87 body.velocity = body.velocity.rotated(Vector3(0, 1, 0), 3 * PI / 2)
88 46
89 var apclient = global.get_node("Archipelago") 47 # this is ACW
90 if !apclient._pilgrimage_allows_paintings: 48 if rotate == 0:
91 global.sunwarp = 1 49 body.translation.x = (
92 body.get_node("pivot/camera/sunwarp_background").visible = false 50 target_painting.translation.x + (body.translation.x - painting.translation.x)
51 )
52 body.translation.y = (
53 target_painting.translation.y + (body.translation.y - painting.translation.y)
54 )
55 body.translation.z = (
56 target_painting.translation.z + (body.translation.z - painting.translation.z)
57 )
58 elif rotate == 180:
59 body.translation.x = (
60 target_painting.translation.x - (body.translation.x - painting.translation.x)
61 )
62 body.translation.y = (
63 target_painting.translation.y + (body.translation.y - painting.translation.y)
64 )
65 body.translation.z = (
66 target_painting.translation.z - (body.translation.z - painting.translation.z)
67 )
68 body.rotate_y(PI)
69 body.velocity = body.velocity.rotated(Vector3(0, 1, 0), PI)
70 elif rotate == 90:
71 var diff_x = body.translation.x - painting.translation.x
72 var diff_y = body.translation.y - painting.translation.y
73 var diff_z = body.translation.z - painting.translation.z
74 body.translation.x = target_painting.translation.x + diff_z
75 body.translation.y = target_painting.translation.y + diff_y
76 body.translation.z = target_painting.translation.z - diff_x
77 body.rotate_y(PI / 2)
78 body.velocity = body.velocity.rotated(Vector3(0, 1, 0), PI / 2)
79 elif rotate == 270:
80 var diff_x = body.translation.x - painting.translation.x
81 var diff_y = body.translation.y - painting.translation.y
82 var diff_z = body.translation.z - painting.translation.z
83 body.translation.x = target_painting.translation.x - diff_z
84 body.translation.y = target_painting.translation.y + diff_y
85 body.translation.z = target_painting.translation.z + diff_x
86 body.rotate_y(3 * PI / 2)
87 body.velocity = body.velocity.rotated(Vector3(0, 1, 0), 3 * PI / 2)
88
89 if !apclient._pilgrimage_allows_paintings:
90 global.sunwarp = 1
91 body.get_node("pivot/camera/sunwarp_background").visible = false
93 92
94 93
95func _dir_to_int(dir): 94func _dir_to_int(dir):
diff --git a/Archipelago/player.gd b/Archipelago/player.gd index 49d907d..52d743a 100644 --- a/Archipelago/player.gd +++ b/Archipelago/player.gd
@@ -44,8 +44,6 @@ func _solvingEnd():
44 44
45 45
46func _unhandled_input(event): 46func _unhandled_input(event):
47 ._unhandled_input(event)
48
49 if event is InputEventKey: 47 if event is InputEventKey:
50 if event.pressed and event.scancode == KEY_P: 48 if event.pressed and event.scancode == KEY_P:
51 var effects_node = get_tree().get_root().get_node("Spatial/AP_Effects") 49 var effects_node = get_tree().get_root().get_node("Spatial/AP_Effects")
diff --git a/Archipelago/settings_menu.gd b/Archipelago/settings_menu.gd new file mode 100644 index 0000000..0efce40 --- /dev/null +++ b/Archipelago/settings_menu.gd
@@ -0,0 +1,25 @@
1extends "res://scripts/settings_menu.gd"
2
3
4func _ready():
5 var level_tab = get_node("Panel/Tabs/Level")
6 level_tab.get_node("ScrollContainer").queue_free()
7 level_tab.get_node("upload_button").queue_free()
8
9 var new_label = Label.new()
10 new_label.text = "You must restart Lingo before playing a non-Archipelago game."
11 new_label.align = Label.ALIGN_CENTER
12 new_label.valign = Label.VALIGN_CENTER
13 new_label.autowrap = true
14 new_label.margin_left = 25
15 new_label.margin_top = 25
16 new_label.margin_right = 1250
17 new_label.margin_bottom = 492
18
19 var field_font = DynamicFont.new()
20 field_font.font_data = load("res://fonts/Lingo2.ttf")
21 field_font.size = 48
22
23 new_label.add_font_override("font", field_font)
24
25 level_tab.add_child(new_label)
diff --git a/Archipelago/settings_screen.gd b/Archipelago/settings_screen.gd index 79fdcc3..43c9468 100644 --- a/Archipelago/settings_screen.gd +++ b/Archipelago/settings_screen.gd
@@ -55,6 +55,7 @@ func _ready():
55 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/panelInput.gd")) 55 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/panelInput.gd"))
56 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pause_menu.gd")) 56 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pause_menu.gd"))
57 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd")) 57 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd"))
58 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/settings_menu.gd"))
58 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleport.gd")) 59 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleport.gd"))
59 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldTransporter.gd")) 60 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldTransporter.gd"))
60 61
diff --git a/CHANGELOG.md b/CHANGELOG.md index de47cb0..dea4b62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,5 +1,52 @@
1# lingo-archipelago Releases 1# lingo-archipelago Releases
2 2
3## v3.2.1 - 2024-06-21
4
5- The player can no longer select another level after Archipelago has been
6 loaded without restarting the game.
7
8Download:
9[lingo-archipelago-v3.2.1.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v3.2.1.zip)<br/>
10Source: [v3.2.1](https://code.fourisland.com/lingo-archipelago/tag/?h=v3.2.1)
11
12## v3.2.0 - 2024-06-10
13
14- Replaced the early color hallways painting with a new one contributed by
15 [KF2015](https://www.youtube.com/channel/UCOf5qCfZLZ1UbDXpOf1eBig).
16
17Download:
18[lingo-archipelago-v3.2.0.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v3.2.0.zip)<br/>
19Source: [v3.2.0](https://code.fourisland.com/lingo-archipelago/tag/?h=v3.2.0)
20
21## v3.1.0 - 2024-06-06
22
23- The client now sends information on what paintings you've checked if you're
24 playing painting shuffle, which can be used to populate the subway map in
25 v0.10.0 and later of the tracker.
26
27Download:
28[lingo-archipelago-v3.1.0.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v3.1.0.zip)<br/>
29Source: [v3.1.0](https://code.fourisland.com/lingo-archipelago/tag/?h=v3.1.0)
30
31## v3.0.3 - 2024-06-02
32
33- Fixed issue with mouse sensitivity being doubled.
34
35Download:
36[lingo-archipelago-v3.0.3.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v3.0.3.zip)<br/>
37Source: [v3.0.3](https://code.fourisland.com/lingo-archipelago/tag/?h=v3.0.3)
38
39## v3.0.2 - 2024-05-30
40
41- Fixed a crash that could happen if the local data got corrupted.
42- Added compatibility for
43 [non universally unique datapackage IDs](https://github.com/ArchipelagoMW/Archipelago/issues/3394).
44- Removed the arrows pointing at The Fearless when confusify world is enabled.
45
46Download:
47[lingo-archipelago-v3.0.2.zip](https://files.fourisland.com/releases/lingo-archipelago/lingo-archipelago-v3.0.2.zip)<br/>
48Source: [v3.0.2](https://code.fourisland.com/lingo-archipelago/tag/?h=v3.0.2)
49
3## v3.0.1 - 2024-04-22 50## v3.0.1 - 2024-04-22
4 51
5- Fixed issue where Progressive Hallway Room did not work properly on worlds 52- Fixed issue where Progressive Hallway Room did not work properly on worlds