RIP GOTM
This commit is contained in:
parent
3d57f3d839
commit
b77326eb80
39 changed files with 211 additions and 1624 deletions
|
@ -185,6 +185,7 @@ rect_scale = Vector2( 0.5, 0.5 )
|
||||||
|
|
||||||
[node name="ActivationRange" type="Area2D" parent="."]
|
[node name="ActivationRange" type="Area2D" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
|
input_pickable = false
|
||||||
script = ExtResource( 6 )
|
script = ExtResource( 6 )
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||||
|
|
|
@ -66,6 +66,7 @@ one_shot = true
|
||||||
|
|
||||||
[node name="ActivationRange" type="Area2D" parent="."]
|
[node name="ActivationRange" type="Area2D" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
|
input_pickable = false
|
||||||
script = ExtResource( 3 )
|
script = ExtResource( 3 )
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||||
|
@ -73,6 +74,7 @@ position = Vector2( 16, 16 )
|
||||||
shape = SubResource( 8 )
|
shape = SubResource( 8 )
|
||||||
|
|
||||||
[node name="CloseRange" type="Area2D" parent="."]
|
[node name="CloseRange" type="Area2D" parent="."]
|
||||||
|
input_pickable = false
|
||||||
script = ExtResource( 3 )
|
script = ExtResource( 3 )
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="CloseRange"]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="CloseRange"]
|
||||||
|
|
|
@ -52,7 +52,7 @@ func refresh_sprite() -> void:
|
||||||
$Light2D.rotation = rot
|
$Light2D.rotation = rot
|
||||||
$ActivationRange.rotation = rot
|
$ActivationRange.rotation = rot
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(_delta: float) -> void:
|
||||||
if Engine.editor_hint:
|
if Engine.editor_hint:
|
||||||
return
|
return
|
||||||
manager.power_usage = max(1e-3, MAX_USAGE * strength)
|
manager.power_usage = max(1e-3, MAX_USAGE * strength)
|
||||||
|
|
|
@ -14,7 +14,6 @@ extents = Vector2( 72, 72 )
|
||||||
|
|
||||||
[node name="Engine" type="StaticBody2D"]
|
[node name="Engine" type="StaticBody2D"]
|
||||||
script = ExtResource( 2 )
|
script = ExtResource( 2 )
|
||||||
max_force = 0.05
|
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||||
position = Vector2( 48, 48 )
|
position = Vector2( 48, 48 )
|
||||||
|
@ -36,6 +35,7 @@ energy = 0.002
|
||||||
|
|
||||||
[node name="ActivationRange" type="Area2D" parent="."]
|
[node name="ActivationRange" type="Area2D" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
|
input_pickable = false
|
||||||
script = ExtResource( 1 )
|
script = ExtResource( 1 )
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||||
|
|
|
@ -38,6 +38,7 @@ __meta__ = {
|
||||||
|
|
||||||
[node name="ActivationRange" type="Area2D" parent="."]
|
[node name="ActivationRange" type="Area2D" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
|
input_pickable = false
|
||||||
script = ExtResource( 4 )
|
script = ExtResource( 4 )
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||||
|
|
|
@ -97,6 +97,7 @@ rect_scale = Vector2( 0.5, 0.5 )
|
||||||
[node name="ActivationRange" type="Area2D" parent="."]
|
[node name="ActivationRange" type="Area2D" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
position = Vector2( 16, 16 )
|
position = Vector2( 16, 16 )
|
||||||
|
input_pickable = false
|
||||||
script = ExtResource( 3 )
|
script = ExtResource( 3 )
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||||
|
|
|
@ -5,5 +5,5 @@ class_name GameObjectScanner
|
||||||
func serialize():
|
func serialize():
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
func deserialize(data):
|
func deserialize(_data):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -100,5 +100,5 @@ func draw_circle_arc_poly(center, radius, angle_from, angle_to, color):
|
||||||
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
|
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
|
||||||
draw_polygon(points_arc, colors)
|
draw_polygon(points_arc, colors)
|
||||||
|
|
||||||
func hear(event):
|
func hear(_event):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -24,7 +24,7 @@ position = Vector2( 0, -3 )
|
||||||
shape = SubResource( 1 )
|
shape = SubResource( 1 )
|
||||||
|
|
||||||
[node name="Earing" type="Area2D" parent="."]
|
[node name="Earing" type="Area2D" parent="."]
|
||||||
monitorable = false
|
input_pickable = false
|
||||||
collision_layer = 32
|
collision_layer = 32
|
||||||
collision_mask = 32
|
collision_mask = 32
|
||||||
|
|
||||||
|
|
|
@ -111,3 +111,26 @@ func _draw():
|
||||||
func _area_moved():
|
func _area_moved():
|
||||||
scout()
|
scout()
|
||||||
update()
|
update()
|
||||||
|
|
||||||
|
func serialize():
|
||||||
|
var subareas = {}
|
||||||
|
for child in get_children():
|
||||||
|
subareas[child.name] = {
|
||||||
|
"transform": child.transform
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"name": area_name,
|
||||||
|
"wall_path": wall_tilemap,
|
||||||
|
"base_path": base_tilemap,
|
||||||
|
"subareas": subareas
|
||||||
|
}
|
||||||
|
|
||||||
|
func deserialize(data):
|
||||||
|
for subarea in data.subareas:
|
||||||
|
var node = Node2D.new()
|
||||||
|
node.name = subarea
|
||||||
|
node.transform = data.subareas[subarea].transform
|
||||||
|
add_child(node)
|
||||||
|
area_name = data.name
|
||||||
|
wall_tilemap = data.wall_path
|
||||||
|
base_tilemap = data.base_path
|
||||||
|
|
|
@ -14,6 +14,7 @@ a = Vector2( -16, 16 )
|
||||||
b = Vector2( 48, 16 )
|
b = Vector2( 48, 16 )
|
||||||
|
|
||||||
[node name="ElProbe" type="Area2D"]
|
[node name="ElProbe" type="Area2D"]
|
||||||
|
input_pickable = false
|
||||||
collision_layer = 4
|
collision_layer = 4
|
||||||
collision_mask = 4
|
collision_mask = 4
|
||||||
script = ExtResource( 1 )
|
script = ExtResource( 1 )
|
||||||
|
|
|
@ -21,7 +21,8 @@ static func to_letter(num: int) -> String:
|
||||||
return "Α"
|
return "Α"
|
||||||
elif num < 0:
|
elif num < 0:
|
||||||
letters = "αβγδεζηθικλμνξοπρστυφχψω"
|
letters = "αβγδεζηθικλμνξοπρστυφχψω"
|
||||||
num = abs(num)
|
if num < 0:
|
||||||
|
num = -num
|
||||||
var out = ""
|
var out = ""
|
||||||
var base = letters.length()
|
var base = letters.length()
|
||||||
while num > 0:
|
while num > 0:
|
||||||
|
|
|
@ -4,7 +4,7 @@ var thread
|
||||||
var mutex
|
var mutex
|
||||||
var sem
|
var sem
|
||||||
|
|
||||||
var gotm_mode = false
|
var threaded = true
|
||||||
var time_max = 100 # Milliseconds.
|
var time_max = 100 # Milliseconds.
|
||||||
|
|
||||||
var queue = []
|
var queue = []
|
||||||
|
@ -13,19 +13,15 @@ var pending = {}
|
||||||
func _lock(_caller):
|
func _lock(_caller):
|
||||||
mutex.lock()
|
mutex.lock()
|
||||||
|
|
||||||
|
|
||||||
func _unlock(_caller):
|
func _unlock(_caller):
|
||||||
mutex.unlock()
|
mutex.unlock()
|
||||||
|
|
||||||
|
|
||||||
func _post(_caller):
|
func _post(_caller):
|
||||||
sem.post()
|
sem.post()
|
||||||
|
|
||||||
|
|
||||||
func _wait(_caller):
|
func _wait(_caller):
|
||||||
sem.wait()
|
sem.wait()
|
||||||
|
|
||||||
|
|
||||||
func queue_resource(path, p_in_front = false):
|
func queue_resource(path, p_in_front = false):
|
||||||
_lock("queue_resource")
|
_lock("queue_resource")
|
||||||
if path in pending:
|
if path in pending:
|
||||||
|
@ -48,7 +44,6 @@ func queue_resource(path, p_in_front = false):
|
||||||
_unlock("queue_resource")
|
_unlock("queue_resource")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
func cancel_resource(path):
|
func cancel_resource(path):
|
||||||
_lock("cancel_resource")
|
_lock("cancel_resource")
|
||||||
if path in pending:
|
if path in pending:
|
||||||
|
@ -57,9 +52,8 @@ func cancel_resource(path):
|
||||||
pending.erase(path)
|
pending.erase(path)
|
||||||
_unlock("cancel_resource")
|
_unlock("cancel_resource")
|
||||||
|
|
||||||
|
|
||||||
func get_progress(path):
|
func get_progress(path):
|
||||||
if gotm_mode:
|
if not threaded:
|
||||||
return 1.0
|
return 1.0
|
||||||
_lock("get_progress")
|
_lock("get_progress")
|
||||||
var ret = -1
|
var ret = -1
|
||||||
|
@ -71,9 +65,8 @@ func get_progress(path):
|
||||||
_unlock("get_progress")
|
_unlock("get_progress")
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
func is_ready(path):
|
func is_ready(path):
|
||||||
if gotm_mode:
|
if not threaded:
|
||||||
return true
|
return true
|
||||||
var ret
|
var ret
|
||||||
_lock("is_ready")
|
_lock("is_ready")
|
||||||
|
@ -84,9 +77,8 @@ func is_ready(path):
|
||||||
_unlock("is_ready")
|
_unlock("is_ready")
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
func _wait_for_resource(res, path):
|
func _wait_for_resource(res, path):
|
||||||
if gotm_mode:
|
if not threaded:
|
||||||
return get_resource(path)
|
return get_resource(path)
|
||||||
_unlock("wait_for_resource")
|
_unlock("wait_for_resource")
|
||||||
while true:
|
while true:
|
||||||
|
@ -97,9 +89,8 @@ func _wait_for_resource(res, path):
|
||||||
return pending[path]
|
return pending[path]
|
||||||
_unlock("wait_for_resource")
|
_unlock("wait_for_resource")
|
||||||
|
|
||||||
|
|
||||||
func get_resource(path):
|
func get_resource(path):
|
||||||
if gotm_mode:
|
if not threaded:
|
||||||
return load(path)
|
return load(path)
|
||||||
_lock("get_resource")
|
_lock("get_resource")
|
||||||
if path in pending:
|
if path in pending:
|
||||||
|
@ -123,7 +114,6 @@ func get_resource(path):
|
||||||
_unlock("return")
|
_unlock("return")
|
||||||
return ResourceLoader.load(path)
|
return ResourceLoader.load(path)
|
||||||
|
|
||||||
|
|
||||||
func thread_process():
|
func thread_process():
|
||||||
_wait("thread_process")
|
_wait("thread_process")
|
||||||
_lock("process")
|
_lock("process")
|
||||||
|
@ -143,15 +133,14 @@ func thread_process():
|
||||||
queue.erase(res)
|
queue.erase(res)
|
||||||
_unlock("process")
|
_unlock("process")
|
||||||
|
|
||||||
|
|
||||||
func thread_func(_u):
|
func thread_func(_u):
|
||||||
while true:
|
while true:
|
||||||
thread_process()
|
thread_process()
|
||||||
|
|
||||||
|
func start(use_threads: bool):
|
||||||
func start(gotm: bool):
|
threaded = use_threads
|
||||||
gotm_mode = gotm
|
if not threaded:
|
||||||
if not gotm:
|
return
|
||||||
mutex = Mutex.new()
|
mutex = Mutex.new()
|
||||||
sem = Semaphore.new()
|
sem = Semaphore.new()
|
||||||
thread = Thread.new()
|
thread = Thread.new()
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
script/source = "extends Node
|
script/source = "extends Node
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
$\"/root/Multiplayer\".gotm_mode = true # Faster startup
|
|
||||||
$\"/root/Multiplayer\".host()
|
$\"/root/Multiplayer\".host()
|
||||||
"
|
"
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ var writing = false
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
randomize()
|
randomize()
|
||||||
ui.connect("command", world, "process_command")
|
|
||||||
if netgame.hosting:
|
if netgame.hosting:
|
||||||
world.load_map(netgame.get_current_map())
|
world.load_map(netgame.get_current_map())
|
||||||
world.map.current_ship_position = Vector2(randf() * 1e4, randf() * 1e4)
|
world.map.current_ship_position = Vector2(randf() * 1e4, randf() * 1e4)
|
||||||
|
@ -58,11 +57,14 @@ master func broadcast_zone(message: String, origin: Vector2, radius: float) -> v
|
||||||
var shape = CircleShape2D.new()
|
var shape = CircleShape2D.new()
|
||||||
shape.radius = radius
|
shape.radius = radius
|
||||||
var query = Physics2DShapeQueryParameters.new()
|
var query = Physics2DShapeQueryParameters.new()
|
||||||
|
query.collide_with_areas = true
|
||||||
|
query.collide_with_bodies = false
|
||||||
query.collision_layer = 32
|
query.collision_layer = 32
|
||||||
query.transform = Transform(world.transform.translated(origin))
|
query.transform.origin = origin
|
||||||
query.set_shape(shape)
|
query.set_shape(shape)
|
||||||
var res = physics.intersect_shape(query, 100)
|
var res = physics.intersect_shape(query, 100)
|
||||||
print(res)
|
for col in res:
|
||||||
|
rpc_id(col.collider.get_network_master(), "add_log", message)
|
||||||
|
|
||||||
master func broadcast(message: String) -> void:
|
master func broadcast(message: String) -> void:
|
||||||
rpc("add_log", message)
|
rpc("add_log", message)
|
||||||
|
|
|
@ -14,7 +14,7 @@ const SYSTEMS_UPDATE_INTERVAL = 10
|
||||||
# Master server data
|
# Master server data
|
||||||
const MASTER_SERVER_ADDR = "fgms.zyg.ovh"
|
const MASTER_SERVER_ADDR = "fgms.zyg.ovh"
|
||||||
const MASTER_SERVER_UDP_PORT = 9434
|
const MASTER_SERVER_UDP_PORT = 9434
|
||||||
const MS_GAME_CODE = "odyssey-0-a1"
|
const MS_GAME_CODE = "odyssey-0-a2"
|
||||||
const GOTM_OVERRIDE = false
|
const GOTM_OVERRIDE = false
|
||||||
|
|
||||||
# Master server entry
|
# Master server entry
|
||||||
|
@ -22,7 +22,6 @@ var ms_active = false
|
||||||
var ms_key = ""
|
var ms_key = ""
|
||||||
var server_name = ""
|
var server_name = ""
|
||||||
|
|
||||||
onready var gotm_mode = Gotm.is_live() or GOTM_OVERRIDE
|
|
||||||
var hosting = false
|
var hosting = false
|
||||||
|
|
||||||
export var player_name = ""
|
export var player_name = ""
|
||||||
|
@ -34,8 +33,6 @@ onready var scene_manager = $"/root/SceneManager"
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
player_name = "tider-" + str(randi() % 1000)
|
player_name = "tider-" + str(randi() % 1000)
|
||||||
if gotm_mode:
|
|
||||||
Gotm.connect("lobby_changed", self, "_lobby_changed")
|
|
||||||
|
|
||||||
func bind_events():
|
func bind_events():
|
||||||
get_tree().connect("network_peer_connected", self, "_player_connected")
|
get_tree().connect("network_peer_connected", self, "_player_connected")
|
||||||
|
@ -76,7 +73,6 @@ func host():
|
||||||
yield(get_tree().create_timer(0.3), "timeout")
|
yield(get_tree().create_timer(0.3), "timeout")
|
||||||
|
|
||||||
# Run port forwarding/nat punchthrough if the platform doesn't do it already
|
# Run port forwarding/nat punchthrough if the platform doesn't do it already
|
||||||
if not gotm_mode:
|
|
||||||
print("Running UPNP magicks")
|
print("Running UPNP magicks")
|
||||||
if discover_upnp() == UPNP.UPNP_RESULT_SUCCESS:
|
if discover_upnp() == UPNP.UPNP_RESULT_SUCCESS:
|
||||||
print("UPNP mapping added")
|
print("UPNP mapping added")
|
||||||
|
@ -88,10 +84,6 @@ func host():
|
||||||
player_info[1] = { "name": player_name }
|
player_info[1] = { "name": player_name }
|
||||||
round_info = { "map": "odyssey" }
|
round_info = { "map": "odyssey" }
|
||||||
|
|
||||||
if gotm_mode:
|
|
||||||
# Add to master server before hosting (in GOTM mode)
|
|
||||||
create_ms_entry()
|
|
||||||
|
|
||||||
bind_events()
|
bind_events()
|
||||||
var peer = NetworkedMultiplayerENet.new()
|
var peer = NetworkedMultiplayerENet.new()
|
||||||
peer.compression_mode = NetworkedMultiplayerENet.COMPRESS_FASTLZ
|
peer.compression_mode = NetworkedMultiplayerENet.COMPRESS_FASTLZ
|
||||||
|
@ -113,7 +105,6 @@ func host():
|
||||||
"res://Scenes/Maps/odyssey.tscn"
|
"res://Scenes/Maps/odyssey.tscn"
|
||||||
])
|
])
|
||||||
|
|
||||||
if not gotm_mode:
|
|
||||||
# Add to master server after hosting
|
# Add to master server after hosting
|
||||||
create_ms_entry()
|
create_ms_entry()
|
||||||
|
|
||||||
|
@ -126,13 +117,9 @@ func join(server):
|
||||||
|
|
||||||
bind_events()
|
bind_events()
|
||||||
var peer = NetworkedMultiplayerENet.new()
|
var peer = NetworkedMultiplayerENet.new()
|
||||||
|
peer.compression_mode = NetworkedMultiplayerENet.COMPRESS_FASTLZ
|
||||||
|
|
||||||
var addr = null
|
var addr = server.address
|
||||||
if gotm_mode:
|
|
||||||
var success = yield(server.join(), "completed")
|
|
||||||
addr = Gotm.lobby.host.address
|
|
||||||
else:
|
|
||||||
addr = server.address
|
|
||||||
|
|
||||||
peer.create_client(addr, SERVER_PORT)
|
peer.create_client(addr, SERVER_PORT)
|
||||||
get_tree().network_peer = peer
|
get_tree().network_peer = peer
|
||||||
|
@ -193,11 +180,6 @@ func _ms_request(endpoint: String, data):
|
||||||
push_error("An error occurred in the HTTP request.")
|
push_error("An error occurred in the HTTP request.")
|
||||||
|
|
||||||
func ms_get_entries():
|
func ms_get_entries():
|
||||||
if gotm_mode:
|
|
||||||
var fetch = GotmLobbyFetch.new()
|
|
||||||
var lobbies = yield(fetch.first(), "completed")
|
|
||||||
emit_signal("ms_updated", "list_games", lobbies)
|
|
||||||
else:
|
|
||||||
var http_request = HTTPRequest.new()
|
var http_request = HTTPRequest.new()
|
||||||
add_child(http_request)
|
add_child(http_request)
|
||||||
http_request.connect("request_completed", self, "_ms_response", ["list_games"])
|
http_request.connect("request_completed", self, "_ms_response", ["list_games"])
|
||||||
|
@ -217,23 +199,12 @@ func get_game_data():
|
||||||
}
|
}
|
||||||
|
|
||||||
func create_ms_entry():
|
func create_ms_entry():
|
||||||
if gotm_mode:
|
|
||||||
Gotm.host_lobby(true)
|
|
||||||
Gotm.lobby.hidden = false
|
|
||||||
else:
|
|
||||||
_ms_request("new", {
|
_ms_request("new", {
|
||||||
"game_id": MS_GAME_CODE,
|
"game_id": MS_GAME_CODE,
|
||||||
"data": get_game_data()
|
"data": get_game_data()
|
||||||
})
|
})
|
||||||
update_ms_entry()
|
|
||||||
|
|
||||||
func update_ms_entry():
|
func update_ms_entry():
|
||||||
if gotm_mode:
|
|
||||||
var data = get_game_data()
|
|
||||||
Gotm.lobby.name = data.name
|
|
||||||
for key in data:
|
|
||||||
Gotm.lobby.set_property(key, data[key])
|
|
||||||
else:
|
|
||||||
if ms_active:
|
if ms_active:
|
||||||
_ms_request("update", {
|
_ms_request("update", {
|
||||||
"key": ms_key,
|
"key": ms_key,
|
||||||
|
@ -276,6 +247,3 @@ func get_current_map():
|
||||||
return GameWorld.Map.RUNTIME
|
return GameWorld.Map.RUNTIME
|
||||||
_:
|
_:
|
||||||
return GameWorld.Map.EMPTY
|
return GameWorld.Map.EMPTY
|
||||||
|
|
||||||
func _lobby_changed():
|
|
||||||
print("Lobby changed ", Gotm.lobby)
|
|
||||||
|
|
|
@ -9,13 +9,16 @@ var loader = preload("res://Scenes/Loader.tscn")
|
||||||
onready var netgame = $"/root/Multiplayer"
|
onready var netgame = $"/root/Multiplayer"
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
queue.start(netgame.gotm_mode)
|
var threads_supported = true
|
||||||
|
if OS.get_name() == "HTML5":
|
||||||
|
threads_supported = false
|
||||||
|
queue.start(threads_supported)
|
||||||
|
|
||||||
func enter_loader() -> void:
|
func enter_loader() -> void:
|
||||||
get_tree().change_scene_to(loader)
|
get_tree().change_scene_to(loader)
|
||||||
|
|
||||||
func load_scene(scene_path: String, dependencies: Array) -> void:
|
func load_scene(scene_path: String, dependencies: Array) -> void:
|
||||||
if not netgame.gotm_mode:
|
if queue.threaded:
|
||||||
target_scene = scene_path
|
target_scene = scene_path
|
||||||
queue.queue_resource(scene_path)
|
queue.queue_resource(scene_path)
|
||||||
for dep in dependencies:
|
for dep in dependencies:
|
||||||
|
@ -24,7 +27,7 @@ func load_scene(scene_path: String, dependencies: Array) -> void:
|
||||||
get_tree().change_scene(scene_path)
|
get_tree().change_scene(scene_path)
|
||||||
|
|
||||||
func _physics_process(_delta: float) -> void:
|
func _physics_process(_delta: float) -> void:
|
||||||
if not netgame.gotm_mode:
|
if queue.threaded:
|
||||||
if target_scene != null:
|
if target_scene != null:
|
||||||
var remaining = queue.pending.size()
|
var remaining = queue.pending.size()
|
||||||
for path in queue.pending:
|
for path in queue.pending:
|
||||||
|
|
|
@ -61,11 +61,8 @@ func _process(delta: float):
|
||||||
if Engine.editor_hint:
|
if Engine.editor_hint:
|
||||||
return
|
return
|
||||||
|
|
||||||
var current_speed_ratio = 0
|
|
||||||
var engines = get_engine_count()
|
var engines = get_engine_count()
|
||||||
var max_speed = get_max_speed()
|
var max_speed = get_max_speed()
|
||||||
if engines > 0:
|
|
||||||
current_speed_ratio = current_ship_speed / max_speed
|
|
||||||
|
|
||||||
# Ease ship speed/direction changes
|
# Ease ship speed/direction changes
|
||||||
if abs(current_ship_direction) > PI*2:
|
if abs(current_ship_direction) > PI*2:
|
||||||
|
@ -163,19 +160,14 @@ func serialize() -> Dictionary:
|
||||||
data.append([cell, tilemap.get_cellv(cell)])
|
data.append([cell, tilemap.get_cellv(cell)])
|
||||||
tilemapdata[tilemap.name] = data
|
tilemapdata[tilemap.name] = data
|
||||||
|
|
||||||
# Get objects
|
|
||||||
var engines = serialize_children($engines)
|
|
||||||
var objects = serialize_children($objects)
|
|
||||||
var lights = serialize_children($lights)
|
|
||||||
var sockets = serialize_children($sockets)
|
|
||||||
var pois = serialize_children($pois)
|
|
||||||
return {
|
return {
|
||||||
"tilemaps": tilemapdata,
|
"tilemaps": tilemapdata,
|
||||||
"engines": engines,
|
"engines": serialize_children($engines),
|
||||||
"objects": objects,
|
"objects": serialize_children($objects),
|
||||||
"lights": lights,
|
"lights": serialize_children($lights),
|
||||||
"sockets": sockets,
|
"sockets": serialize_children($sockets),
|
||||||
"pois": pois,
|
"pois": serialize_children($pois),
|
||||||
|
"areas": serialize_children($areas),
|
||||||
"ship": {
|
"ship": {
|
||||||
"position": current_ship_position,
|
"position": current_ship_position,
|
||||||
"target": current_ship_target,
|
"target": current_ship_target,
|
||||||
|
@ -201,6 +193,8 @@ func deserialize(data: Dictionary) -> void:
|
||||||
deserialize_children($lights, data["lights"])
|
deserialize_children($lights, data["lights"])
|
||||||
deserialize_children($pois, data["pois"])
|
deserialize_children($pois, data["pois"])
|
||||||
deserialize_children($sockets, data["sockets"])
|
deserialize_children($sockets, data["sockets"])
|
||||||
|
deserialize_children($sockets, data["sockets"])
|
||||||
|
deserialize_children($areas, data["areas"], true)
|
||||||
|
|
||||||
# Set ship parameters
|
# Set ship parameters
|
||||||
current_ship_position = data["ship"]["position"]
|
current_ship_position = data["ship"]["position"]
|
||||||
|
@ -223,13 +217,16 @@ func serialize_children(node: Node) -> Dictionary:
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
|
|
||||||
func deserialize_children(node: Node, data: Dictionary) -> void:
|
func deserialize_children(node: Node, data: Dictionary, deserialize_first: bool = false) -> void:
|
||||||
for node_name in data:
|
for node_name in data:
|
||||||
var node_data = data[node_name] as Dictionary
|
var node_data = data[node_name] as Dictionary
|
||||||
var node_scene = load(node_data.filename).instance()
|
var node_scene = load(node_data.filename).instance()
|
||||||
node_scene.name = node_name
|
node_scene.name = node_name
|
||||||
node_scene.transform = node_data.transform
|
node_scene.transform = node_data.transform
|
||||||
|
if deserialize_first:
|
||||||
|
node_scene.deserialize(node_data.data)
|
||||||
node.add_child(node_scene, true)
|
node.add_child(node_scene, true)
|
||||||
|
if not deserialize_first:
|
||||||
node_scene.deserialize(node_data.data)
|
node_scene.deserialize(node_data.data)
|
||||||
|
|
||||||
# Lighting
|
# Lighting
|
||||||
|
|
|
@ -103,3 +103,5 @@ __meta__ = {
|
||||||
|
|
||||||
[node name="pois" type="Node2D" parent="."]
|
[node name="pois" type="Node2D" parent="."]
|
||||||
z_index = 999
|
z_index = 999
|
||||||
|
|
||||||
|
[node name="areas" type="Node2D" parent="."]
|
||||||
|
|
|
@ -21,10 +21,6 @@ func _ready() -> void:
|
||||||
$"/root/Music/BGM".play()
|
$"/root/Music/BGM".play()
|
||||||
netgame.connect("ms_updated", self, "_ms_update")
|
netgame.connect("ms_updated", self, "_ms_update")
|
||||||
request_servers()
|
request_servers()
|
||||||
if netgame.gotm_mode:
|
|
||||||
# Hide manual connect
|
|
||||||
$Popup/MarginContainer/VBoxContainer/Label2.visible = false
|
|
||||||
$Popup/MarginContainer/VBoxContainer/HBoxContainer.visible = false
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
refresh_server_remaining -= delta
|
refresh_server_remaining -= delta
|
||||||
|
@ -49,11 +45,6 @@ func _ms_update(action, result):
|
||||||
if action == "list_games":
|
if action == "list_games":
|
||||||
# Reset server list
|
# Reset server list
|
||||||
server_list.clear()
|
server_list.clear()
|
||||||
if netgame.gotm_mode:
|
|
||||||
servers = result
|
|
||||||
for server in servers:
|
|
||||||
server_list.add_item(server.name + " (" + server.id + ") - " + str(server.peers.size()) + "/" + str(server.get_property("max_players")) + " players")
|
|
||||||
else:
|
|
||||||
servers = result
|
servers = result
|
||||||
for server in servers:
|
for server in servers:
|
||||||
server_list.add_item(server.data.name + " (" + server.address + ") - " + str(server.data.players) + "/" + str(server.data.max_players) + " players")
|
server_list.add_item(server.data.name + " (" + server.address + ") - " + str(server.data.players) + "/" + str(server.data.max_players) + " players")
|
||||||
|
@ -84,7 +75,7 @@ func _server_addr_changed(new_text: String) -> void:
|
||||||
$Popup/MarginContainer/VBoxContainer/HBoxContainer/Button.disabled = new_text.length() < 1
|
$Popup/MarginContainer/VBoxContainer/HBoxContainer/Button.disabled = new_text.length() < 1
|
||||||
|
|
||||||
func _manual_join_pressed():
|
func _manual_join_pressed():
|
||||||
join_server($Popup/MarginContainer/VBoxContainer/HBoxContainer/LineEdit.text)
|
join_server({ "address": $Popup/MarginContainer/VBoxContainer/HBoxContainer/LineEdit.text })
|
||||||
|
|
||||||
func _server_item_clicked(index):
|
func _server_item_clicked(index):
|
||||||
$"/root/Music/BGM".stop()
|
$"/root/Music/BGM".stop()
|
||||||
|
|
|
@ -2,8 +2,6 @@ extends Control
|
||||||
|
|
||||||
class_name GameUI
|
class_name GameUI
|
||||||
|
|
||||||
signal command(command)
|
|
||||||
|
|
||||||
enum ServerMenuItem {
|
enum ServerMenuItem {
|
||||||
ServerInfo
|
ServerInfo
|
||||||
}
|
}
|
||||||
|
@ -16,7 +14,7 @@ enum PopupName {
|
||||||
onready var logs = $Logs
|
onready var logs = $Logs
|
||||||
onready var scene = $"/root/scene"
|
onready var scene = $"/root/scene"
|
||||||
|
|
||||||
const CHAT_RADIUS = 32*6
|
const CHAT_RADIUS = 32*10
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# Add options to menu buttons
|
# Add options to menu buttons
|
||||||
|
|
|
@ -1,11 +1,36 @@
|
||||||
[gd_scene load_steps=6 format=2]
|
[gd_scene load_steps=15 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://Scenes/UI/SpaceMap.tscn" type="PackedScene" id=1]
|
[ext_resource path="res://Scenes/UI/SpaceMap.tscn" type="PackedScene" id=1]
|
||||||
[ext_resource path="res://Scenes/UI.gd" type="Script" id=2]
|
[ext_resource path="res://Scenes/UI.gd" type="Script" id=2]
|
||||||
[ext_resource path="res://Scenes/UI/Panels/Logs.tscn" type="PackedScene" id=3]
|
[ext_resource path="res://Graphics/UI/iosevka-aile-regular.ttf" type="DynamicFontData" id=3]
|
||||||
[ext_resource path="res://Scenes/UI/Panels/ServerInfo.tscn" type="PackedScene" id=4]
|
[ext_resource path="res://Scenes/UI/Panels/ServerInfo.tscn" type="PackedScene" id=4]
|
||||||
|
[ext_resource path="res://Scenes/UI/Widgets/ResizablePanel.tscn" type="PackedScene" id=5]
|
||||||
|
[ext_resource path="res://Scenes/UI/Panels/Logs.gd" type="Script" id=6]
|
||||||
|
[ext_resource path="res://Graphics/UI/iosevka-aile-italic.ttf" type="DynamicFontData" id=7]
|
||||||
|
[ext_resource path="res://Graphics/UI/iosevka-aile-bold.ttf" type="DynamicFontData" id=8]
|
||||||
|
[ext_resource path="res://Graphics/UI/iosevka-aile-bolditalic.ttf" type="DynamicFontData" id=9]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id=1]
|
[sub_resource type="DynamicFont" id=1]
|
||||||
|
size = 14
|
||||||
|
use_mipmaps = true
|
||||||
|
font_data = ExtResource( 9 )
|
||||||
|
|
||||||
|
[sub_resource type="DynamicFont" id=2]
|
||||||
|
size = 14
|
||||||
|
use_mipmaps = true
|
||||||
|
font_data = ExtResource( 7 )
|
||||||
|
|
||||||
|
[sub_resource type="DynamicFont" id=3]
|
||||||
|
size = 14
|
||||||
|
use_mipmaps = true
|
||||||
|
font_data = ExtResource( 8 )
|
||||||
|
|
||||||
|
[sub_resource type="DynamicFont" id=4]
|
||||||
|
size = 14
|
||||||
|
use_mipmaps = true
|
||||||
|
font_data = ExtResource( 3 )
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id=5]
|
||||||
bg_color = Color( 0.133333, 0.12549, 0.203922, 0.705882 )
|
bg_color = Color( 0.133333, 0.12549, 0.203922, 0.705882 )
|
||||||
border_width_left = 4
|
border_width_left = 4
|
||||||
border_width_top = 4
|
border_width_top = 4
|
||||||
|
@ -31,11 +56,53 @@ __meta__ = {
|
||||||
|
|
||||||
[node name="MapPopup" parent="." instance=ExtResource( 1 )]
|
[node name="MapPopup" parent="." instance=ExtResource( 1 )]
|
||||||
|
|
||||||
[node name="Logs" parent="." instance=ExtResource( 3 )]
|
[node name="Logs" type="Control" parent="."]
|
||||||
margin_left = 10.0
|
margin_left = 10.0
|
||||||
margin_top = 10.0
|
margin_top = 10.0
|
||||||
margin_right = -857.0
|
margin_right = 475.0
|
||||||
margin_bottom = -566.0
|
margin_bottom = 273.0
|
||||||
|
script = ExtResource( 6 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="ResizablePanel" parent="Logs" instance=ExtResource( 5 )]
|
||||||
|
title = "Logs"
|
||||||
|
|
||||||
|
[node name="RichTextLabel" type="RichTextLabel" parent="Logs/ResizablePanel"]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
margin_left = 10.0
|
||||||
|
margin_top = 40.0
|
||||||
|
margin_right = -10.0
|
||||||
|
margin_bottom = -40.0
|
||||||
|
focus_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
custom_fonts/bold_italics_font = SubResource( 1 )
|
||||||
|
custom_fonts/italics_font = SubResource( 2 )
|
||||||
|
custom_fonts/bold_font = SubResource( 3 )
|
||||||
|
custom_fonts/normal_font = SubResource( 4 )
|
||||||
|
bbcode_enabled = true
|
||||||
|
selection_enabled = true
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="LineEdit" type="LineEdit" parent="Logs/ResizablePanel"]
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
margin_left = 6.0
|
||||||
|
margin_top = -30.0
|
||||||
|
margin_right = -16.0
|
||||||
|
margin_bottom = -6.0
|
||||||
|
focus_mode = 1
|
||||||
|
caret_blink = true
|
||||||
|
caret_blink_speed = 0.5
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
[node name="Menu" type="PanelContainer" parent="."]
|
[node name="Menu" type="PanelContainer" parent="."]
|
||||||
anchor_left = 1.0
|
anchor_left = 1.0
|
||||||
|
@ -48,7 +115,7 @@ grow_horizontal = 0
|
||||||
grow_vertical = 0
|
grow_vertical = 0
|
||||||
size_flags_horizontal = 8
|
size_flags_horizontal = 8
|
||||||
size_flags_vertical = 8
|
size_flags_vertical = 8
|
||||||
custom_styles/panel = SubResource( 1 )
|
custom_styles/panel = SubResource( 5 )
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
|
@ -90,3 +157,9 @@ margin_left = 31.2773
|
||||||
margin_top = 46.9222
|
margin_top = 46.9222
|
||||||
margin_right = 287.277
|
margin_right = 287.277
|
||||||
margin_bottom = 214.923
|
margin_bottom = 214.923
|
||||||
|
[connection signal="chat_message_sent" from="Logs" to="." method="_send_chat"]
|
||||||
|
[connection signal="focus_entered" from="Logs/ResizablePanel/LineEdit" to="Logs" method="_chat_bar_status" binds= [ true ]]
|
||||||
|
[connection signal="focus_exited" from="Logs/ResizablePanel/LineEdit" to="Logs" method="_chat_bar_status" binds= [ false ]]
|
||||||
|
[connection signal="mouse_entered" from="Logs/ResizablePanel/LineEdit" to="Logs" method="_chat_bar_focus" binds= [ true ]]
|
||||||
|
[connection signal="mouse_exited" from="Logs/ResizablePanel/LineEdit" to="Logs" method="_chat_bar_focus" binds= [ false ]]
|
||||||
|
[connection signal="text_entered" from="Logs/ResizablePanel/LineEdit" to="Logs" method="_text_submitted"]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
|
signal chat_message_sent(text)
|
||||||
|
|
||||||
onready var log_text = $ResizablePanel/RichTextLabel
|
onready var log_text = $ResizablePanel/RichTextLabel
|
||||||
onready var chat_bar = $ResizablePanel/LineEdit
|
onready var chat_bar = $ResizablePanel/LineEdit
|
||||||
|
|
||||||
|
@ -7,6 +9,8 @@ func _input(event: InputEvent) -> void:
|
||||||
if event is InputEventMouseButton:
|
if event is InputEventMouseButton:
|
||||||
if not chat_bar_focus:
|
if not chat_bar_focus:
|
||||||
chat_bar.release_focus()
|
chat_bar.release_focus()
|
||||||
|
if event.is_action_released("ui_chat") and chat_bar_focus == false:
|
||||||
|
chat_bar.grab_focus()
|
||||||
|
|
||||||
func add_line(line: String) -> void:
|
func add_line(line: String) -> void:
|
||||||
log_text.append_bbcode(line)
|
log_text.append_bbcode(line)
|
||||||
|
@ -18,3 +22,8 @@ func _chat_bar_status(editing: bool) -> void:
|
||||||
|
|
||||||
func _chat_bar_focus(entered: bool) -> void:
|
func _chat_bar_focus(entered: bool) -> void:
|
||||||
chat_bar_focus = entered
|
chat_bar_focus = entered
|
||||||
|
|
||||||
|
func _text_submitted(text):
|
||||||
|
emit_signal("chat_message_sent", text)
|
||||||
|
chat_bar.text = ""
|
||||||
|
chat_bar.release_focus()
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
[gd_scene load_steps=11 format=2]
|
|
||||||
|
|
||||||
[ext_resource path="res://Graphics/UI/iosevka-aile-regular.ttf" type="DynamicFontData" id=1]
|
|
||||||
[ext_resource path="res://Graphics/UI/iosevka-aile-italic.ttf" type="DynamicFontData" id=2]
|
|
||||||
[ext_resource path="res://Graphics/UI/iosevka-aile-bold.ttf" type="DynamicFontData" id=3]
|
|
||||||
[ext_resource path="res://Graphics/UI/iosevka-aile-bolditalic.ttf" type="DynamicFontData" id=4]
|
|
||||||
[ext_resource path="res://Scenes/UI/Panels/Logs.gd" type="Script" id=5]
|
|
||||||
[ext_resource path="res://Scenes/UI/Widgets/ResizablePanel.tscn" type="PackedScene" id=6]
|
|
||||||
|
|
||||||
[sub_resource type="DynamicFont" id=1]
|
|
||||||
size = 14
|
|
||||||
use_mipmaps = true
|
|
||||||
font_data = ExtResource( 4 )
|
|
||||||
|
|
||||||
[sub_resource type="DynamicFont" id=2]
|
|
||||||
size = 14
|
|
||||||
use_mipmaps = true
|
|
||||||
font_data = ExtResource( 2 )
|
|
||||||
|
|
||||||
[sub_resource type="DynamicFont" id=3]
|
|
||||||
size = 14
|
|
||||||
use_mipmaps = true
|
|
||||||
font_data = ExtResource( 3 )
|
|
||||||
|
|
||||||
[sub_resource type="DynamicFont" id=4]
|
|
||||||
size = 14
|
|
||||||
use_mipmaps = true
|
|
||||||
font_data = ExtResource( 1 )
|
|
||||||
|
|
||||||
[node name="Logs" type="Control"]
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
script = ExtResource( 5 )
|
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="ResizablePanel" parent="." instance=ExtResource( 6 )]
|
|
||||||
|
|
||||||
[node name="RichTextLabel" type="RichTextLabel" parent="ResizablePanel"]
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
margin_left = 10.0
|
|
||||||
margin_top = 40.0
|
|
||||||
margin_right = -10.0
|
|
||||||
margin_bottom = -40.0
|
|
||||||
focus_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
custom_fonts/bold_italics_font = SubResource( 1 )
|
|
||||||
custom_fonts/italics_font = SubResource( 2 )
|
|
||||||
custom_fonts/bold_font = SubResource( 3 )
|
|
||||||
custom_fonts/normal_font = SubResource( 4 )
|
|
||||||
bbcode_enabled = true
|
|
||||||
selection_enabled = true
|
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="LineEdit" type="LineEdit" parent="ResizablePanel"]
|
|
||||||
anchor_top = 1.0
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
margin_left = 10.0
|
|
||||||
margin_top = -30.0
|
|
||||||
margin_right = -10.0
|
|
||||||
focus_mode = 1
|
|
||||||
caret_blink = true
|
|
||||||
caret_blink_speed = 0.5
|
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
[connection signal="focus_entered" from="ResizablePanel/LineEdit" to="." method="_chat_bar_status" binds= [ true ]]
|
|
||||||
[connection signal="focus_exited" from="ResizablePanel/LineEdit" to="." method="_chat_bar_status" binds= [ false ]]
|
|
||||||
[connection signal="mouse_entered" from="ResizablePanel/LineEdit" to="." method="_chat_bar_focus" binds= [ true ]]
|
|
||||||
[connection signal="mouse_exited" from="ResizablePanel/LineEdit" to="." method="_chat_bar_focus" binds= [ false ]]
|
|
|
@ -1,6 +1,5 @@
|
||||||
extends WindowDialog
|
extends WindowDialog
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(_delta):
|
||||||
if not visible:
|
if not visible:
|
||||||
return
|
return
|
||||||
multiplayer
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ export var bgzoom = 50
|
||||||
const BORDER_WIDTH = 4
|
const BORDER_WIDTH = 4
|
||||||
const BORDER_LENGTH = 30
|
const BORDER_LENGTH = 30
|
||||||
const RADAR_EFFECT_DELAY = 1
|
const RADAR_EFFECT_DELAY = 1
|
||||||
|
const HALF_BORDER = BORDER_LENGTH/2
|
||||||
|
|
||||||
export(Texture) var background
|
export(Texture) var background
|
||||||
export(Font) var font
|
export(Font) var font
|
||||||
|
@ -87,13 +88,13 @@ func draw_target(viewport: Rect2, position: Vector2, color: Color):
|
||||||
var relative_pos = position - viewport.position
|
var relative_pos = position - viewport.position
|
||||||
var clamped = Vector2(clamp(relative_pos.x, 0, viewport.size.x), clamp(relative_pos.y, 0, viewport.size.y))
|
var clamped = Vector2(clamp(relative_pos.x, 0, viewport.size.x), clamp(relative_pos.y, 0, viewport.size.y))
|
||||||
if relative_pos.x < 0:
|
if relative_pos.x < 0:
|
||||||
draw_line(Vector2(0, clamped.y-BORDER_LENGTH/2), Vector2(0, clamped.y+BORDER_LENGTH/2), color, BORDER_WIDTH)
|
draw_line(Vector2(0, clamped.y-HALF_BORDER), Vector2(0, clamped.y+HALF_BORDER), color, BORDER_WIDTH)
|
||||||
elif relative_pos.x > viewport.size.x:
|
elif relative_pos.x > viewport.size.x:
|
||||||
draw_line(Vector2(viewport.size.x, clamped.y-BORDER_LENGTH/2), Vector2(viewport.size.x, clamped.y+BORDER_LENGTH/2), color, BORDER_WIDTH)
|
draw_line(Vector2(viewport.size.x, clamped.y-HALF_BORDER), Vector2(viewport.size.x, clamped.y+HALF_BORDER), color, BORDER_WIDTH)
|
||||||
if relative_pos.y < 0:
|
if relative_pos.y < 0:
|
||||||
draw_line(Vector2(clamped.x-BORDER_LENGTH/2, 0), Vector2(clamped.x+BORDER_LENGTH/2, 0), color, BORDER_WIDTH)
|
draw_line(Vector2(clamped.x-HALF_BORDER, 0), Vector2(clamped.x+HALF_BORDER, 0), color, BORDER_WIDTH)
|
||||||
elif relative_pos.y > viewport.size.y:
|
elif relative_pos.y > viewport.size.y:
|
||||||
draw_line(Vector2(clamped.x-BORDER_LENGTH/2, viewport.size.y), Vector2(clamped.x+BORDER_LENGTH/2, viewport.size.y), color, BORDER_WIDTH)
|
draw_line(Vector2(clamped.x-HALF_BORDER, viewport.size.y), Vector2(clamped.x+HALF_BORDER, viewport.size.y), color, BORDER_WIDTH)
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
if event is InputEventMouseButton:
|
if event is InputEventMouseButton:
|
||||||
|
|
|
@ -43,7 +43,7 @@ application/trademarks=""
|
||||||
|
|
||||||
[preset.1]
|
[preset.1]
|
||||||
|
|
||||||
name="GOTM"
|
name="HTML5"
|
||||||
platform="HTML5"
|
platform="HTML5"
|
||||||
runnable=true
|
runnable=true
|
||||||
custom_features=""
|
custom_features=""
|
||||||
|
|
117
gotm/Gotm.gd
117
gotm/Gotm.gd
|
@ -1,117 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
extends Node
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
|
|
||||||
# Official GDScript API for games on gotm.io
|
|
||||||
# This plugin serves as a polyfill while developing against the API locally.
|
|
||||||
|
|
||||||
# The 'real' API calls are only available when running the game live on gotm.io.
|
|
||||||
# Running the game in the web player (gotm.io/web-player) also counts as live.
|
|
||||||
|
|
||||||
# Add this script as a global autoload. Make sure the global autoload is named
|
|
||||||
# "Gotm". It must be named "Gotm" for it to work.
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# SIGNALS
|
|
||||||
##############################################################
|
|
||||||
# You connected or disconnected from a lobby. Access it at 'Gotm.lobby'
|
|
||||||
signal lobby_changed()
|
|
||||||
|
|
||||||
# Files were drag'n'dropped into the screen.
|
|
||||||
# The 'files' argument is an array of 'GotmFile'.
|
|
||||||
signal files_dropped(files, screen)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# PROPERTIES
|
|
||||||
##############################################################
|
|
||||||
# These are all read only.
|
|
||||||
|
|
||||||
# Player information.
|
|
||||||
var user: GotmUser = GotmUser.new()
|
|
||||||
|
|
||||||
# Current lobby you are in.
|
|
||||||
# Is null when not in a lobby.
|
|
||||||
var lobby: GotmLobby = null
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# METHODS
|
|
||||||
##############################################################
|
|
||||||
|
|
||||||
# The API is live when the game runs on gotm.io.
|
|
||||||
# Running the game in the web player (gotm.io/web-player) also counts as live.
|
|
||||||
func is_live() -> bool:
|
|
||||||
return false
|
|
||||||
|
|
||||||
|
|
||||||
# Create a new lobby and join it.
|
|
||||||
#
|
|
||||||
# If 'show_invitation' is true, show an invitation link in a popup.
|
|
||||||
#
|
|
||||||
# By default, the lobby is hidden and is only accessible directly through
|
|
||||||
# its 'invite_link'.
|
|
||||||
# Set 'lobby.hidden' to false to make it fetchable with 'GotmLobbyFetch'.
|
|
||||||
#
|
|
||||||
# Returns the hosted lobby (also accessible at 'Gotm.lobby').
|
|
||||||
static func host_lobby(show_invitation: bool = true) -> GotmLobby:
|
|
||||||
return _GotmImpl._host_lobby(GotmLobby.new())
|
|
||||||
|
|
||||||
|
|
||||||
# Play an audio snippet with 'message' as a synthesized voice.
|
|
||||||
# 'language' is in BCP 47 format (e.g. "en-US" for american english).
|
|
||||||
# If specified language is not available "en-US" is used.
|
|
||||||
# Return true if playback succeeded.
|
|
||||||
func text_to_speech(message: String, language: String = "en-US") -> bool:
|
|
||||||
return true # pretend it worked
|
|
||||||
|
|
||||||
|
|
||||||
# Asynchronously open up the browser's file picker.
|
|
||||||
#
|
|
||||||
# If 'types' is specified, limit the file picker to files with matching file
|
|
||||||
# types (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers).
|
|
||||||
# If 'only_one' is true, only allow the user to pick one file.
|
|
||||||
#
|
|
||||||
# If a picking-session is already in progress, an empty
|
|
||||||
# array is asynchronously returned.
|
|
||||||
#
|
|
||||||
# Asynchronously return an array of 'GotmFile'.
|
|
||||||
# Use 'yield(pick_files(), "completed")' to retrieve the return value.
|
|
||||||
func pick_files(types: Array = Array(), only_one: bool = false) -> Array:
|
|
||||||
yield(get_tree().create_timer(0.25), "timeout")
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# PRIVATE
|
|
||||||
##############################################################
|
|
||||||
func _ready() -> void:
|
|
||||||
_GotmImpl._initialize(GotmLobby, GotmUser)
|
|
||||||
func _process(delta) -> void:
|
|
||||||
_GotmImpl._process()
|
|
||||||
var _impl: Dictionary = {}
|
|
|
@ -1,65 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class_name GotmDebug
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
# Helper library for testing against the API locally, as if it would be live.
|
|
||||||
#
|
|
||||||
# These functions do not make real API calls. They fake operations and
|
|
||||||
# trigger relevant signals as if they happened live.
|
|
||||||
#
|
|
||||||
# These functions do nothing when the game is running live on gotm.io.
|
|
||||||
# Running the game in the web player (gotm.io/web-player) also counts as live.
|
|
||||||
|
|
||||||
# Host a lobby without joining it.
|
|
||||||
# Note that the lobby is hidden by default and not fetchable with
|
|
||||||
# 'GotmLobbyFetch'. To make it fetchable, set 'hidden' to false.
|
|
||||||
# The lobby is only fetchable and joinable in this game process.
|
|
||||||
# Returns added lobby.
|
|
||||||
static func add_lobby() -> GotmLobby:
|
|
||||||
return _GotmDebugImpl._add_lobby(GotmLobby.new())
|
|
||||||
|
|
||||||
|
|
||||||
# Remove a lobby created with 'add_lobby', as if its host (you) disconnected from it.
|
|
||||||
# Triggers 'lobby_changed' if you are in that lobby.
|
|
||||||
static func remove_lobby(lobby: GotmLobby) -> void:
|
|
||||||
_GotmDebugImpl._remove_lobby(lobby)
|
|
||||||
|
|
||||||
|
|
||||||
# Remove all lobbies.
|
|
||||||
static func clear_lobbies() -> void:
|
|
||||||
_GotmDebugImpl._clear_lobbies()
|
|
||||||
|
|
||||||
|
|
||||||
# Add yourself to the lobby, without joining it.
|
|
||||||
# Triggers 'peer_joined' if you are in that lobby.
|
|
||||||
# Returns joined peer.
|
|
||||||
static func add_lobby_peer(lobby: GotmLobby) -> GotmUser:
|
|
||||||
return _GotmDebugImpl._add_lobby_player(lobby, GotmUser.new())
|
|
||||||
|
|
||||||
|
|
||||||
# Remove a peer created with 'add_lobby_peer' from the lobby, as if the peer (you) disconnected
|
|
||||||
# from the lobby.
|
|
||||||
# Triggers 'peer_left' if you are in that lobby.
|
|
||||||
static func remove_lobby_peer(lobby: GotmLobby, peer: GotmUser) -> void:
|
|
||||||
_GotmDebugImpl._remove_lobby_player(lobby, peer)
|
|
|
@ -1,50 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class_name GotmFile
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
# A simple in-memory file descriptor used by 'Gotm.pick_files' and
|
|
||||||
# 'Gotm.files_dropped'.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# PROPERTIES
|
|
||||||
##############################################################
|
|
||||||
# File name.
|
|
||||||
var name: String
|
|
||||||
|
|
||||||
# File data.
|
|
||||||
var data: PoolByteArray
|
|
||||||
|
|
||||||
# Last time the file was modified in unix time (seconds since epoch).
|
|
||||||
var modified_time: int
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# METHODS
|
|
||||||
##############################################################
|
|
||||||
# Save the file to the browser's download folder.
|
|
||||||
func download() -> void:
|
|
||||||
pass
|
|
|
@ -1,155 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class_name GotmLobby
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
# A lobby is a way of connecting players with eachother as if they
|
|
||||||
# were on the same local network.
|
|
||||||
#
|
|
||||||
# Lobbies can be joined either directly through an 'invite_link', or by
|
|
||||||
# joining lobbies fetched with the 'GotmLobbyFetch' class.
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# SIGNALS
|
|
||||||
##############################################################
|
|
||||||
# Peer joined the lobby.
|
|
||||||
# 'peer_user' is a 'GotmUser' instance.
|
|
||||||
# This is only emitted if you are in this lobby.
|
|
||||||
signal peer_joined(peer_user)
|
|
||||||
|
|
||||||
# Peer left the lobby.
|
|
||||||
# 'peer_user' is a 'GotmUser' instance.
|
|
||||||
# This is only emitted if you are in this lobby.
|
|
||||||
signal peer_left(peer_user)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# READ-ONLY PROPERTIES
|
|
||||||
##############################################################
|
|
||||||
# Globally unique identifier.
|
|
||||||
var id: String
|
|
||||||
|
|
||||||
# Other peers in the lobby with addresses.
|
|
||||||
# Is an array of 'GotmUser'.
|
|
||||||
var peers: Array = []
|
|
||||||
|
|
||||||
# You with address.
|
|
||||||
var me: GotmUser = GotmUser.new()
|
|
||||||
|
|
||||||
# Host user with address.
|
|
||||||
var host: GotmUser = GotmUser.new()
|
|
||||||
|
|
||||||
# Peers can join the lobby directly through this link.
|
|
||||||
var invite_link: String
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# WRITABLE PROPERTIES
|
|
||||||
##############################################################
|
|
||||||
# Note that only the host can write to these properties.
|
|
||||||
|
|
||||||
# Name that is searchable using 'GotmLobbyFetch'
|
|
||||||
# Names longer than 64 characters are truncated.
|
|
||||||
var name: String = ""
|
|
||||||
|
|
||||||
# Prevent the lobby from showing up in fetches?
|
|
||||||
# Peers may still join directly through 'invite_link'
|
|
||||||
var hidden: bool = true
|
|
||||||
|
|
||||||
# Prevent new peers from joining?
|
|
||||||
# Also prevents the lobby from showing up in fetches.
|
|
||||||
var locked: bool = false
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# METHODS
|
|
||||||
##############################################################
|
|
||||||
# Asynchronously join this lobby after leaving current lobby.
|
|
||||||
#
|
|
||||||
# Use 'var success = yield(lobby.join(), "completed")' to wait for the call to complete
|
|
||||||
# and retrieve the return value.
|
|
||||||
#
|
|
||||||
# Sets 'Gotm.lobby' to the joined lobby if successful.
|
|
||||||
#
|
|
||||||
# Asyncronously returns true if successful, else false.
|
|
||||||
func join() -> bool:
|
|
||||||
return yield(_GotmImpl._join_lobby(self), "completed")
|
|
||||||
|
|
||||||
|
|
||||||
# Leave this lobby.
|
|
||||||
func leave() -> void:
|
|
||||||
_GotmImpl._leave_lobby(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Am I the host of this lobby?
|
|
||||||
func is_host() -> bool:
|
|
||||||
return _GotmImpl._is_lobby_host(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Get a custom property.
|
|
||||||
func get_property(name: String):
|
|
||||||
return _GotmImpl._get_lobby_property(self, name)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
################################
|
|
||||||
# Host-only methods
|
|
||||||
################################
|
|
||||||
# Kick peer from this lobby.
|
|
||||||
# Returns true if successful, else false.
|
|
||||||
func kick(peer: GotmUser) -> bool:
|
|
||||||
return _GotmImpl._kick_lobby_peer(self, peer)
|
|
||||||
|
|
||||||
|
|
||||||
# Store up to 10 of your own custom properties in the lobby.
|
|
||||||
# These are visible to other peers when fetching lobbies.
|
|
||||||
# Only properties of types String, int, float or bool are allowed.
|
|
||||||
# Integers are converted to floats.
|
|
||||||
# Strings longer than 64 characters are truncated.
|
|
||||||
# Setting 'value' to null removes the property.
|
|
||||||
func set_property(name: String, value) -> void:
|
|
||||||
_GotmImpl._set_lobby_property(self, name, value)
|
|
||||||
|
|
||||||
|
|
||||||
# Make this lobby filterable by a custom property.
|
|
||||||
# Filtering is done when fetching lobbies with 'GotmLobbyFetch'.
|
|
||||||
# Up to 3 properties can be set as filterable at once.
|
|
||||||
func set_filterable(property_name: String, filterable: bool = true) -> void:
|
|
||||||
_GotmImpl._set_lobby_filterable(self, property_name, filterable)
|
|
||||||
|
|
||||||
|
|
||||||
# Make this lobby sortable by a custom property.
|
|
||||||
# Sorting is done when fetching lobbies with 'GotmLobbyFetch'.
|
|
||||||
# Up to 3 properties can be set as sortable at once.
|
|
||||||
func set_sortable(property_name: String, sortable: bool = true) -> void:
|
|
||||||
_GotmImpl._set_lobby_sortable(self, property_name, sortable)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
################################
|
|
||||||
# PRIVATE
|
|
||||||
################################
|
|
||||||
var _impl: Dictionary = {}
|
|
|
@ -1,122 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class_name GotmLobbyFetch
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
# Used for fetching non-hidden and non-locked lobbies.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# PROPERTIES
|
|
||||||
##############################################################
|
|
||||||
################
|
|
||||||
# Filter options
|
|
||||||
################
|
|
||||||
# If not empty, fetch lobbies whose 'Lobby.name' contains 'name'.
|
|
||||||
var filter_name: String = ""
|
|
||||||
|
|
||||||
# If not empty, fetch lobbies whose filterable custom properties
|
|
||||||
# matches those in 'filter_properties'.
|
|
||||||
#
|
|
||||||
# For example, setting 'filter_properties.difficulty = 2' will
|
|
||||||
# only fetch lobbies that have been set up with both 'lobby.set_property("difficulty", 2)'
|
|
||||||
# and 'lobby.set_filterable("difficulty", true)'.
|
|
||||||
#
|
|
||||||
# If your lobby has multiple filterable props, you must provide every filterable
|
|
||||||
# prop in 'filter_properties'. Setting a prop's value to 'null' will match any
|
|
||||||
# value of that prop.
|
|
||||||
var filter_properties: Dictionary = {}
|
|
||||||
|
|
||||||
|
|
||||||
################
|
|
||||||
# Sort options
|
|
||||||
################
|
|
||||||
# If not empty, sort by a sortable custom property.
|
|
||||||
#
|
|
||||||
# For example, setting 'sort_property = "difficulty"' will
|
|
||||||
# only fetch lobbies that have been set up with both 'lobby.set_property("difficulty", some_value)'
|
|
||||||
# and 'lobby.set_sortable("difficulty", true)'.
|
|
||||||
#
|
|
||||||
# If your lobby has a sortable prop, you must always provide a 'sort_property'.
|
|
||||||
var sort_property: String = ""
|
|
||||||
|
|
||||||
# Sort results in ascending order?
|
|
||||||
var sort_ascending: bool = false
|
|
||||||
|
|
||||||
# If not null, fetch lobbies whose sort property's value is equal to or greater than 'sort_min'.
|
|
||||||
var sort_min = null
|
|
||||||
|
|
||||||
# If not null, fetch lobbies whose sort property's value is equal to or lesser than 'sort_max'.
|
|
||||||
var sort_max = null
|
|
||||||
|
|
||||||
# If true, and 'sort_min' is provided, exclude lobbies whose sort property's value is equal to 'sort_min'.
|
|
||||||
var sort_min_exclusive = false
|
|
||||||
|
|
||||||
# If true, and 'sort_max' is provided, exclude lobbies whose sort property's value is equal to 'sort_max'.
|
|
||||||
var sort_max_exclusive = false
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# METHODS
|
|
||||||
##############################################################
|
|
||||||
# All these methods asynchronously fetch up to 8 non-hidden
|
|
||||||
# and non-locked lobbies.
|
|
||||||
#
|
|
||||||
# Modifying any filtering or sorting option resets the state of this
|
|
||||||
# 'GotmLobbyFetch' instance and causes the next fetch call to
|
|
||||||
# fetch the first lobbies.
|
|
||||||
#
|
|
||||||
# All calls asynchronously return an array of fetched lobbies.
|
|
||||||
# Use 'yield(fetch.next(), "completed")' to retrieve it.
|
|
||||||
|
|
||||||
|
|
||||||
# Fetch the next lobbies, starting after the last lobby fetched
|
|
||||||
# in the previous call.
|
|
||||||
func next(count: int = 8) -> Array:
|
|
||||||
return yield(_GotmImpl._fetch_lobbies(self, count, "next"), "completed")
|
|
||||||
|
|
||||||
|
|
||||||
# Fetch the previous lobbies, ending before the first lobby
|
|
||||||
# that was fetched in the previous call.
|
|
||||||
func previous(count: int = 8) -> Array:
|
|
||||||
return yield(_GotmImpl._fetch_lobbies(self, count, "previous"), "completed")
|
|
||||||
|
|
||||||
|
|
||||||
# Fetch the first lobbies.
|
|
||||||
func first(count: int = 8) -> Array:
|
|
||||||
return yield(_GotmImpl._fetch_lobbies(self, count, "first"), "completed")
|
|
||||||
|
|
||||||
|
|
||||||
# Fetch lobbies at the current position.
|
|
||||||
# Useful for refreshing lobbies without changing the page.
|
|
||||||
func current(count: int = 8) -> Array:
|
|
||||||
return yield(_GotmImpl._fetch_lobbies(self, count, "current"), "completed")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# PRIVATE
|
|
||||||
##############################################################
|
|
||||||
var _impl: Dictionary = {}
|
|
|
@ -1,52 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class_name GotmUser
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
# Holds information about a Gotm user.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# PROPERTIES
|
|
||||||
##############################################################
|
|
||||||
# These are all read-only.
|
|
||||||
|
|
||||||
# Globally unique ID.
|
|
||||||
# Is empty if user is not logged in.
|
|
||||||
var id: String = ""
|
|
||||||
|
|
||||||
# Current nickname.
|
|
||||||
var display_name: String = ""
|
|
||||||
|
|
||||||
# The IP address of the user.
|
|
||||||
# Is empty if you are not in the same lobby.
|
|
||||||
var address: String = ""
|
|
||||||
|
|
||||||
# Is user logged in?
|
|
||||||
var is_logged_in: bool = false
|
|
||||||
|
|
||||||
##############################################################
|
|
||||||
# PRIVATE
|
|
||||||
##############################################################
|
|
||||||
var _impl: Dictionary = {}
|
|
21
gotm/LICENSE
21
gotm/LICENSE
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,30 +0,0 @@
|
||||||
<p align="center">
|
|
||||||
<a href="https://gotm.io"><img src="https://i.imgur.com/mc8HAgS.png" alt="gotm.io"></a>
|
|
||||||
<br/>
|
|
||||||
Access Gotm's API with GDScript
|
|
||||||
<br />
|
|
||||||
<a href="https://gotm.io/about/plugin">Learn more</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# Install
|
|
||||||
|
|
||||||
_Supports versions 3.1.0 and newer._
|
|
||||||
|
|
||||||
## 1. Download
|
|
||||||
|
|
||||||
Download the plugin from the [AssetLib](https://docs.godotengine.org/en/stable/tutorials/assetlib/using_assetlib.html#in-the-editor) in the Godot Editor and follow its instructions.
|
|
||||||
|
|
||||||
You can also download it directly [here](https://github.com/PlayGotm/GDGotm/archive/master.zip). Extract the contents into your project's directory.
|
|
||||||
|
|
||||||
## 2. Setup
|
|
||||||
|
|
||||||
Add Gotm.gd to your autoloads at "Project Settings -> AutoLoad". Make sure the global autoload is named "Gotm". It must be named "Gotm" for it to work.
|
|
||||||
|
|
||||||
# Examples
|
|
||||||
|
|
||||||
[Examples](https://github.com/PlayGotM/game-examples)
|
|
||||||
|
|
||||||
# Notes
|
|
||||||
|
|
||||||
This plugin serves as a polyfill when developing against the API locally.
|
|
||||||
The "real" API calls are only available when running the game live on gotm.io.
|
|
|
@ -1,108 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class_name _GotmDebugImpl
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
static func _login() -> void:
|
|
||||||
var g = _GotmImpl._get_gotm()
|
|
||||||
if g.is_live():
|
|
||||||
return
|
|
||||||
|
|
||||||
_logout()
|
|
||||||
|
|
||||||
g.user_id = _GotmImpl._generate_id()
|
|
||||||
g.emit_signal("user_changed")
|
|
||||||
|
|
||||||
|
|
||||||
static func _logout() -> void:
|
|
||||||
var g = _GotmImpl._get_gotm()
|
|
||||||
if g.is_live():
|
|
||||||
return
|
|
||||||
|
|
||||||
if !g.has_user():
|
|
||||||
return
|
|
||||||
|
|
||||||
g.user_id = ""
|
|
||||||
g.emit_signal("user_changed")
|
|
||||||
|
|
||||||
|
|
||||||
static func _add_lobby(lobby):
|
|
||||||
var g = _GotmImpl._get_gotm()
|
|
||||||
if g.is_live():
|
|
||||||
return lobby
|
|
||||||
|
|
||||||
lobby = _GotmImpl._add_lobby(lobby)
|
|
||||||
lobby._impl.address = "127.0.0.1"
|
|
||||||
lobby._impl.host_id = g.user._impl.id
|
|
||||||
lobby.host._impl.id = g.user._impl.id
|
|
||||||
lobby.peers = []
|
|
||||||
return lobby
|
|
||||||
|
|
||||||
|
|
||||||
static func _remove_lobby(lobby) -> void:
|
|
||||||
var g = _GotmImpl._get_gotm()
|
|
||||||
if g.is_live():
|
|
||||||
return
|
|
||||||
|
|
||||||
_GotmImpl._leave_lobby(lobby)
|
|
||||||
g._impl.lobbies.erase(lobby)
|
|
||||||
|
|
||||||
|
|
||||||
static func _clear_lobbies() -> void:
|
|
||||||
var g = _GotmImpl._get_gotm()
|
|
||||||
if g.is_live():
|
|
||||||
return
|
|
||||||
|
|
||||||
for lobby in g._impl.lobbies.duplicate():
|
|
||||||
_remove_lobby(lobby)
|
|
||||||
|
|
||||||
|
|
||||||
static func _add_lobby_player(lobby, peer):
|
|
||||||
var g = _GotmImpl._get_gotm()
|
|
||||||
if g.is_live():
|
|
||||||
return null
|
|
||||||
|
|
||||||
peer.address = "127.0.0.1"
|
|
||||||
peer._impl.id = peer._impl.id
|
|
||||||
lobby.peers.push_back(peer)
|
|
||||||
if lobby == g.lobby:
|
|
||||||
lobby.emit_signal("peer_joined", peer)
|
|
||||||
return peer
|
|
||||||
|
|
||||||
|
|
||||||
static func _remove_lobby_player(lobby, peer) -> void:
|
|
||||||
var g = _GotmImpl._get_gotm()
|
|
||||||
if g.is_live():
|
|
||||||
return
|
|
||||||
|
|
||||||
for p in lobby.peers.duplicate():
|
|
||||||
if peer._impl.id != p._impl.id:
|
|
||||||
continue
|
|
||||||
if peer._impl.id == lobby.host._impl.id:
|
|
||||||
_remove_lobby(lobby)
|
|
||||||
else:
|
|
||||||
lobby.peers.erase(p)
|
|
||||||
if lobby == g.lobby:
|
|
||||||
lobby.emit_signal("peer_left", p)
|
|
||||||
|
|
||||||
|
|
|
@ -1,586 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class_name _GotmImpl
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
|
|
||||||
# Utility sorter for 'sort_custom'.
|
|
||||||
class LobbySorter:
|
|
||||||
var fetch
|
|
||||||
var g
|
|
||||||
|
|
||||||
func sort(lhs, rhs) -> bool:
|
|
||||||
var a
|
|
||||||
var b
|
|
||||||
if fetch.sort_property.empty():
|
|
||||||
a = lhs._impl.created
|
|
||||||
b = rhs._impl.created
|
|
||||||
else:
|
|
||||||
a = lhs._impl.props[fetch.sort_property]
|
|
||||||
b = rhs._impl.props[fetch.sort_property]
|
|
||||||
|
|
||||||
if fetch.sort_ascending:
|
|
||||||
return _GotmImplUtility.is_less(a, b)
|
|
||||||
else:
|
|
||||||
return _GotmImplUtility.is_greater(a, b)
|
|
||||||
|
|
||||||
|
|
||||||
# Generate 20 characters long random string.
|
|
||||||
static func _generate_id() -> String:
|
|
||||||
var g = _get_gotm()
|
|
||||||
var id: String = ""
|
|
||||||
for i in range(20):
|
|
||||||
id += g._impl.chars[g._impl.rng.randi() % g._impl.chars.length()]
|
|
||||||
return id
|
|
||||||
|
|
||||||
|
|
||||||
# Retrieve scene statically via Engine singleton.
|
|
||||||
static func _get_tree() -> SceneTree:
|
|
||||||
return Engine.get_main_loop() as SceneTree
|
|
||||||
|
|
||||||
|
|
||||||
# Get autoloaded Gotm instance from scene's root.
|
|
||||||
static func _get_gotm() -> Node:
|
|
||||||
return _get_tree().root.get_node("Gotm")
|
|
||||||
|
|
||||||
|
|
||||||
# Simplify string somewhat. We want exact matches, but with some reasonable fuzziness.
|
|
||||||
static func _init_search_string_encoders() -> Array:
|
|
||||||
var encoders: Array = [
|
|
||||||
["[àáâãäå]", "a"],
|
|
||||||
["[èéêë]", "e"],
|
|
||||||
["[ìíîï]", "i"],
|
|
||||||
["[òóôõöő]", "o"],
|
|
||||||
["[ùúûüű]", "u"],
|
|
||||||
["[ýŷÿ]", "y"],
|
|
||||||
["ñ", "n"],
|
|
||||||
["[çc]", "k"],
|
|
||||||
["ß", "s"],
|
|
||||||
["[-/]", " "],
|
|
||||||
["[^a-z0-9 ]", ""],
|
|
||||||
["\\s+", " "],
|
|
||||||
["^\\s+", ""],
|
|
||||||
["\\s+$", ""]
|
|
||||||
]
|
|
||||||
for encoder in encoders:
|
|
||||||
var regex: RegEx = RegEx.new()
|
|
||||||
regex.compile(encoder[0])
|
|
||||||
encoder[0] = regex
|
|
||||||
|
|
||||||
return encoders
|
|
||||||
|
|
||||||
|
|
||||||
# Initialize socket for fetching lobbies on local network.
|
|
||||||
static func _init_socket() -> void:
|
|
||||||
var g = _get_gotm()
|
|
||||||
if not g._impl.sockets:
|
|
||||||
g._impl.sockets = []
|
|
||||||
var is_listening = false
|
|
||||||
for i in range(5):
|
|
||||||
var socket = PacketPeerUDP.new()
|
|
||||||
if socket.has_method("set_broadcast_enabled"):
|
|
||||||
socket.set_broadcast_enabled(true)
|
|
||||||
if not is_listening:
|
|
||||||
is_listening = socket.listen(8075 + i) == OK
|
|
||||||
socket.set_dest_address("255.255.255.255", 8075 + i)
|
|
||||||
g._impl.sockets.push_back(socket)
|
|
||||||
|
|
||||||
if not is_listening:
|
|
||||||
push_error("Failed to listen for lobbies. All ports 8075-8079 are busy.")
|
|
||||||
|
|
||||||
|
|
||||||
# Attach some global state to autoloaded Gotm instance.
|
|
||||||
static func _initialize(GotmLobbyT, GotmUserT) -> void:
|
|
||||||
var g = _get_gotm()
|
|
||||||
|
|
||||||
g._impl = {
|
|
||||||
"lobbies": [],
|
|
||||||
"chars": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-",
|
|
||||||
"rng": RandomNumberGenerator.new(),
|
|
||||||
"search_string_encoders": _init_search_string_encoders(),
|
|
||||||
"sockets": null,
|
|
||||||
"lobby_requests": {},
|
|
||||||
"GotmLobbyT": GotmLobbyT,
|
|
||||||
"GotmUserT": GotmUserT,
|
|
||||||
"is_listening": false
|
|
||||||
}
|
|
||||||
g._impl.rng.randomize()
|
|
||||||
g.user._impl.id = _generate_id()
|
|
||||||
g.user.address = "localhost"
|
|
||||||
|
|
||||||
|
|
||||||
static func _process() -> void:
|
|
||||||
var g = _get_gotm()
|
|
||||||
|
|
||||||
if g.lobby:
|
|
||||||
if OS.get_system_time_msecs() - g.lobby._impl.last_heartbeat > 2500:
|
|
||||||
_init_socket()
|
|
||||||
_put_sockets({
|
|
||||||
"op": "peer_heartbeat",
|
|
||||||
"data": {
|
|
||||||
"lobby_id": g.lobby.id,
|
|
||||||
"id": g.user._impl.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
g.lobby._impl.last_heartbeat = OS.get_system_time_msecs()
|
|
||||||
|
|
||||||
if g._impl.sockets:
|
|
||||||
for socket in g._impl.sockets:
|
|
||||||
while socket.get_available_packet_count() > 0:
|
|
||||||
var v = socket.get_var()
|
|
||||||
if v.op == "get_lobbies":
|
|
||||||
var data = null
|
|
||||||
if g.lobby and g.lobby.is_host():
|
|
||||||
data = {
|
|
||||||
"id": g.lobby.id,
|
|
||||||
"name": g.lobby.name,
|
|
||||||
"peers": [],
|
|
||||||
"invite_link": g.lobby.invite_link,
|
|
||||||
"_impl": g.lobby._impl
|
|
||||||
}
|
|
||||||
|
|
||||||
_put_sockets({"op": "lobby", "data": data, "id": v.id})
|
|
||||||
elif v.op == "leave_lobby":
|
|
||||||
if g.lobby and v.data.lobby_id == g.lobby.id:
|
|
||||||
if g.lobby.is_host():
|
|
||||||
_put_sockets({
|
|
||||||
"op": "peer_left",
|
|
||||||
"data": {
|
|
||||||
"lobby_id": g.lobby.id,
|
|
||||||
"id": v.data.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
elif v.data.id == g.lobby.host._impl.id:
|
|
||||||
_leave_lobby(g.lobby)
|
|
||||||
elif v.op == "join_lobby":
|
|
||||||
var data = null
|
|
||||||
if g.lobby and g.lobby.is_host() and v.data.lobby_id == g.lobby.id:
|
|
||||||
_put_sockets({
|
|
||||||
"op": "peer_joined",
|
|
||||||
"data": {
|
|
||||||
"lobby_id": g.lobby.id,
|
|
||||||
"address": socket.get_packet_ip(),
|
|
||||||
"id": v.data.id
|
|
||||||
},
|
|
||||||
"id": v.id
|
|
||||||
})
|
|
||||||
|
|
||||||
var peers = []
|
|
||||||
for peer in g.lobby.peers:
|
|
||||||
peers.push_back({"address": peer.address, "_impl": peer._impl})
|
|
||||||
data = {
|
|
||||||
"id": g.lobby.id,
|
|
||||||
"name": g.lobby.name,
|
|
||||||
"peers": peers,
|
|
||||||
"invite_link": g.lobby.invite_link,
|
|
||||||
"_impl": g.lobby._impl
|
|
||||||
}
|
|
||||||
_put_sockets({"op": "lobby", "data": data, "id": v.id})
|
|
||||||
elif v.op == "peer_left":
|
|
||||||
if g.lobby and v.data.lobby_id == g.lobby.id:
|
|
||||||
for peer in g.lobby.peers.duplicate():
|
|
||||||
if peer._impl.id == v.data.id:
|
|
||||||
g.lobby.peers.erase(peer)
|
|
||||||
g.lobby.emit_signal("peer_left", peer)
|
|
||||||
elif v.op == "peer_joined":
|
|
||||||
if g.lobby and v.data.lobby_id == g.lobby.id and not g._impl.lobby_requests.has(v.id):
|
|
||||||
var peer = g._impl.GotmUserT.new()
|
|
||||||
peer.address = v.data.address
|
|
||||||
peer._impl.id = v.data.id
|
|
||||||
g.lobby.peers.push_back(peer)
|
|
||||||
g.lobby.emit_signal("peer_joined", peer)
|
|
||||||
if g.lobby.is_host():
|
|
||||||
g.lobby._impl.heartbeats[v.data.id] = OS.get_system_time_msecs()
|
|
||||||
elif v.op == "peer_heartbeat":
|
|
||||||
if g.lobby and v.data.lobby_id == g.lobby.id:
|
|
||||||
g.lobby._impl.heartbeats[v.data.id] = OS.get_system_time_msecs()
|
|
||||||
elif v.op == "lobby":
|
|
||||||
if v.data and g._impl.lobby_requests.has(v.id):
|
|
||||||
var lobby = g._impl.GotmLobbyT.new()
|
|
||||||
var peers = []
|
|
||||||
if not v.data._impl.host_id.empty():
|
|
||||||
v.data.peers.push_back({
|
|
||||||
"address": socket.get_packet_ip(),
|
|
||||||
"_impl": {
|
|
||||||
"id": v.data._impl.host_id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
for peer in v.data.peers:
|
|
||||||
var p = g._impl.GotmUserT.new()
|
|
||||||
p.address = peer.address
|
|
||||||
p._impl = peer._impl
|
|
||||||
peers.push_back(p)
|
|
||||||
|
|
||||||
lobby.hidden = false
|
|
||||||
lobby.locked = false
|
|
||||||
lobby.id = v.data.id
|
|
||||||
lobby.name = v.data.name
|
|
||||||
lobby.peers = peers
|
|
||||||
lobby.invite_link = v.data.invite_link
|
|
||||||
lobby._impl = v.data._impl
|
|
||||||
lobby._impl.address = socket.get_packet_ip()
|
|
||||||
lobby.me.address = "127.0.0.1"
|
|
||||||
lobby.host.address = socket.get_packet_ip()
|
|
||||||
lobby.host._impl.id = v.data._impl.host_id
|
|
||||||
g._impl.lobby_requests[v.id].push_back(lobby)
|
|
||||||
|
|
||||||
if g.lobby:
|
|
||||||
for peer_id in g.lobby._impl.heartbeats.duplicate():
|
|
||||||
if OS.get_system_time_msecs() - g.lobby._impl.heartbeats[peer_id] > 10000:
|
|
||||||
if g.lobby.is_host():
|
|
||||||
_put_sockets({
|
|
||||||
"op": "peer_left",
|
|
||||||
"data": {
|
|
||||||
"lobby_id": g.lobby.id,
|
|
||||||
"id": peer_id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
g.lobby._impl.heartbeats.erase(peer_id)
|
|
||||||
elif peer_id == g.lobby.host._impl.id:
|
|
||||||
_leave_lobby(g.lobby)
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Improve search experience a little by adding fuzziness.
|
|
||||||
static func _encode_search_string(s: String) -> String:
|
|
||||||
s = s.to_lower()
|
|
||||||
var encoders: Array = _get_gotm()._impl.search_string_encoders
|
|
||||||
for encoder in encoders:
|
|
||||||
s = encoder[0].sub(s, encoder[1], true)
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
# Return true if 'lobby' matches filter options in 'fetch'.
|
|
||||||
static func _match_lobby(lobby, fetch) -> bool:
|
|
||||||
if lobby.locked or lobby.hidden:
|
|
||||||
return false
|
|
||||||
|
|
||||||
if not fetch.filter_name.empty():
|
|
||||||
var name: String = _encode_search_string(lobby.name)
|
|
||||||
var query: String = _encode_search_string(fetch.filter_name)
|
|
||||||
if not query.empty() and name.find(query) < 0:
|
|
||||||
return false
|
|
||||||
|
|
||||||
var lobby_props: Dictionary = {}
|
|
||||||
for key in lobby._impl.filterable_props:
|
|
||||||
if not lobby._impl.props.has(key):
|
|
||||||
return false
|
|
||||||
if not fetch.filter_properties.has(key):
|
|
||||||
return false
|
|
||||||
|
|
||||||
var lhs = fetch.filter_properties[key]
|
|
||||||
var rhs = lobby._impl.props[key]
|
|
||||||
if lhs != null and lhs != rhs:
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
# Used to detect changes.
|
|
||||||
static func _stringify_fetch_state(fetch) -> String:
|
|
||||||
var d: Array = [
|
|
||||||
fetch.filter_name,
|
|
||||||
fetch.filter_properties,
|
|
||||||
fetch.sort_property,
|
|
||||||
fetch.sort_property,
|
|
||||||
fetch.sort_ascending,
|
|
||||||
fetch.sort_min,
|
|
||||||
fetch.sort_max,
|
|
||||||
fetch.sort_min_exclusive,
|
|
||||||
fetch.sort_max_exclusive
|
|
||||||
]
|
|
||||||
return JSON.print(d)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Return sorted copy of 'lobbies' using sort options in 'fetch'.
|
|
||||||
static func _sort_lobbies(lobbies: Array, fetch) -> Array:
|
|
||||||
var sorted: Array = []
|
|
||||||
var g = _get_gotm()
|
|
||||||
for lobby in lobbies:
|
|
||||||
if fetch.sort_property.empty():
|
|
||||||
sorted.push_back(lobby)
|
|
||||||
|
|
||||||
var v = lobby._impl.props.get(fetch.sort_property)
|
|
||||||
if v == null:
|
|
||||||
continue
|
|
||||||
if fetch.sort_min != null:
|
|
||||||
if _GotmImplUtility.is_less(v, fetch.sort_min):
|
|
||||||
continue
|
|
||||||
if fetch.sort_min_exclusive and not _GotmImplUtility.is_greater(v, fetch.sort_min):
|
|
||||||
continue
|
|
||||||
if fetch.sort_max != null:
|
|
||||||
if _GotmImplUtility.is_greater(v, fetch.sort_max):
|
|
||||||
continue
|
|
||||||
if fetch.sort_max_exclusive and not _GotmImplUtility.is_less(v, fetch.sort_max):
|
|
||||||
continue
|
|
||||||
|
|
||||||
sorted.push_back(lobby)
|
|
||||||
|
|
||||||
|
|
||||||
var sorter: LobbySorter = LobbySorter.new()
|
|
||||||
sorter.fetch = fetch
|
|
||||||
sorter.g = g
|
|
||||||
sorted.sort_custom(sorter, "sort")
|
|
||||||
|
|
||||||
return sorted
|
|
||||||
|
|
||||||
|
|
||||||
static func _put_sockets(v: Dictionary):
|
|
||||||
_init_socket()
|
|
||||||
for socket in _get_gotm()._impl.sockets:
|
|
||||||
socket.put_var(v)
|
|
||||||
|
|
||||||
|
|
||||||
static func _request_lobbies() -> Array:
|
|
||||||
var g = _get_gotm()
|
|
||||||
var request_id: String = _generate_id()
|
|
||||||
g._impl.lobby_requests[request_id] = []
|
|
||||||
_put_sockets({"op": "get_lobbies", "id": request_id})
|
|
||||||
yield(g.get_tree().create_timer(0.5), "timeout")
|
|
||||||
|
|
||||||
var lobbies = g._impl.lobby_requests[request_id]
|
|
||||||
g._impl.lobby_requests.erase(request_id)
|
|
||||||
return lobbies
|
|
||||||
|
|
||||||
|
|
||||||
static func _request_join(lobby_id: String):
|
|
||||||
var g = _get_gotm()
|
|
||||||
var request_id: String = _generate_id()
|
|
||||||
g._impl.lobby_requests[request_id] = []
|
|
||||||
_put_sockets({
|
|
||||||
"op": "join_lobby",
|
|
||||||
"id": request_id,
|
|
||||||
"data": {
|
|
||||||
"lobby_id": lobby_id,
|
|
||||||
"id": g.user._impl.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
yield(g.get_tree().create_timer(0.5), "timeout")
|
|
||||||
|
|
||||||
var lobbies = g._impl.lobby_requests[request_id]
|
|
||||||
g._impl.lobby_requests.erase(request_id)
|
|
||||||
for lobby in lobbies:
|
|
||||||
if lobby:
|
|
||||||
return lobby
|
|
||||||
return null
|
|
||||||
|
|
||||||
|
|
||||||
static func _fetch_lobbies(fetch, count: int, type: String) -> Array:
|
|
||||||
var g = _get_gotm()
|
|
||||||
|
|
||||||
# Reset fetch state if user has modified any options.
|
|
||||||
var stringified_state: String = _stringify_fetch_state(fetch)
|
|
||||||
if not fetch._impl.has("last_state") or stringified_state != fetch._impl.last_state:
|
|
||||||
fetch._impl.last_state = stringified_state
|
|
||||||
fetch._impl.last_lobby = -1
|
|
||||||
fetch._impl.start_lobby = -1
|
|
||||||
|
|
||||||
|
|
||||||
# Apply filter options
|
|
||||||
var lobbies: Array = []
|
|
||||||
for lobby in yield(_request_lobbies(), "completed") + g._impl.lobbies:
|
|
||||||
if _match_lobby(lobby, fetch):
|
|
||||||
lobbies.push_back(lobby)
|
|
||||||
|
|
||||||
# Apply sort options
|
|
||||||
lobbies = _sort_lobbies(lobbies, fetch)
|
|
||||||
count = min(8, count)
|
|
||||||
var index: int = 0
|
|
||||||
if type == "first":
|
|
||||||
index = 0
|
|
||||||
elif type == "next":
|
|
||||||
index = fetch._impl.last_lobby + 1
|
|
||||||
elif type == "current":
|
|
||||||
index = fetch._impl.start_lobby + 1
|
|
||||||
elif type == "previous":
|
|
||||||
index = max(fetch._impl.start_lobby - count, 0)
|
|
||||||
|
|
||||||
# Get 'count' lobbies.
|
|
||||||
var result: Array = []
|
|
||||||
for i in range(index, min(index + count, lobbies.size())):
|
|
||||||
result.push_back(lobbies[i])
|
|
||||||
|
|
||||||
# Write down last lobby for subsequent 'next' calls.
|
|
||||||
if not result.empty():
|
|
||||||
var start: int = lobbies.find(result.front()) - 1
|
|
||||||
fetch._impl.start_lobby = max(start, -1)
|
|
||||||
fetch._impl.last_lobby = lobbies.find(result.back())
|
|
||||||
elif index > 0:
|
|
||||||
fetch._impl.start_lobby = fetch._impl.last_lobby
|
|
||||||
|
|
||||||
yield(_get_tree().create_timer(0.25), "timeout") # fake delay
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
# Common initialization.
|
|
||||||
static func _add_lobby(lobby):
|
|
||||||
var g = _get_gotm()
|
|
||||||
|
|
||||||
lobby.id = _generate_id()
|
|
||||||
lobby.invite_link = "https://gotm.io/my-studio/my-game/"
|
|
||||||
lobby.invite_link += "?connectToken=" + _generate_id()
|
|
||||||
lobby._impl = {
|
|
||||||
# Not exposed to user, so doesn't have to be a real timestamp.
|
|
||||||
"created": OS.get_system_time_msecs(),
|
|
||||||
"props": {},
|
|
||||||
"sortable_props": [],
|
|
||||||
"filterable_props": [],
|
|
||||||
"heartbeats": {},
|
|
||||||
"last_heartbeat": 0,
|
|
||||||
"host_id": "",
|
|
||||||
"address": ""
|
|
||||||
}
|
|
||||||
lobby.me._impl.id = g.user._impl.id
|
|
||||||
|
|
||||||
g._impl.lobbies.push_back(lobby)
|
|
||||||
return lobby
|
|
||||||
|
|
||||||
|
|
||||||
static func _host_lobby(lobby):
|
|
||||||
var g = _get_gotm()
|
|
||||||
_leave_lobby(g.lobby)
|
|
||||||
|
|
||||||
lobby = _add_lobby(lobby)
|
|
||||||
lobby._impl.address = "127.0.0.1"
|
|
||||||
lobby.host.address = "127.0.0.1"
|
|
||||||
lobby._impl.host_id = g.user._impl.id
|
|
||||||
lobby.host._impl.id = g.user._impl.id
|
|
||||||
lobby.me.address = "127.0.0.1"
|
|
||||||
g.lobby = lobby
|
|
||||||
g.emit_signal("lobby_changed")
|
|
||||||
|
|
||||||
_init_socket()
|
|
||||||
|
|
||||||
return lobby
|
|
||||||
|
|
||||||
|
|
||||||
static func _join_lobby(lobby) -> bool:
|
|
||||||
var g = _get_gotm()
|
|
||||||
_leave_lobby(g.lobby)
|
|
||||||
|
|
||||||
if not g._impl.lobbies.has(lobby):
|
|
||||||
lobby = yield(_request_join(lobby.id), "completed")
|
|
||||||
else:
|
|
||||||
yield(g.get_tree().create_timer(0.25), "timeout")
|
|
||||||
|
|
||||||
if not lobby or lobby.locked:
|
|
||||||
return false
|
|
||||||
|
|
||||||
lobby.host.address = lobby._impl.address
|
|
||||||
lobby.host._impl.id = lobby._impl.host_id
|
|
||||||
lobby.me.address = "127.0.0.1"
|
|
||||||
g.lobby = lobby
|
|
||||||
g.emit_signal("lobby_changed")
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
static func _is_lobby_host(lobby) -> bool:
|
|
||||||
var g = _get_gotm()
|
|
||||||
return lobby.host._impl.id == g.user._impl.id
|
|
||||||
|
|
||||||
|
|
||||||
static func _kick_lobby_peer(lobby, peer) -> bool:
|
|
||||||
var g = _get_gotm()
|
|
||||||
|
|
||||||
if not lobby.is_host():
|
|
||||||
return false
|
|
||||||
|
|
||||||
if g.user._impl.id == peer._impl.id:
|
|
||||||
_leave_lobby(lobby)
|
|
||||||
else:
|
|
||||||
for p in lobby.peers.duplicate():
|
|
||||||
if p._impl.id != peer._impl.id:
|
|
||||||
continue
|
|
||||||
lobby.peers.erase(p)
|
|
||||||
if lobby == g.lobby:
|
|
||||||
lobby.emit_signal("peer_left", p)
|
|
||||||
break
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
static func _leave_lobby(lobby) -> void:
|
|
||||||
if not lobby:
|
|
||||||
return
|
|
||||||
|
|
||||||
var g = _get_gotm()
|
|
||||||
if g.lobby == lobby:
|
|
||||||
if lobby.host.address == lobby.me.address:
|
|
||||||
g._impl.lobbies.erase(lobby)
|
|
||||||
lobby.me.address = ""
|
|
||||||
lobby.host.address = ""
|
|
||||||
_put_sockets({
|
|
||||||
"op": "leave_lobby",
|
|
||||||
"data": {
|
|
||||||
"lobby_id": lobby.id,
|
|
||||||
"id": g.user._impl.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
g.lobby = null
|
|
||||||
g.emit_signal("lobby_changed")
|
|
||||||
|
|
||||||
|
|
||||||
static func _truncate_string(s: String) -> String:
|
|
||||||
return s.substr(0, 64)
|
|
||||||
|
|
||||||
static func _set_lobby_property(lobby, name: String, value) -> void:
|
|
||||||
name = _truncate_string(name)
|
|
||||||
if value == null:
|
|
||||||
lobby._impl.props.erase(name)
|
|
||||||
|
|
||||||
match typeof(value):
|
|
||||||
TYPE_BOOL:
|
|
||||||
pass
|
|
||||||
TYPE_INT:
|
|
||||||
pass
|
|
||||||
TYPE_REAL:
|
|
||||||
pass
|
|
||||||
TYPE_STRING:
|
|
||||||
value = _truncate_string(value)
|
|
||||||
_:
|
|
||||||
push_error("Invalid lobby property type.")
|
|
||||||
return
|
|
||||||
|
|
||||||
lobby._impl.props[name] = value
|
|
||||||
|
|
||||||
|
|
||||||
static func _get_lobby_property(lobby, name: String):
|
|
||||||
return lobby._impl.props[_truncate_string(name)]
|
|
||||||
|
|
||||||
static func _set_lobby_filterable(lobby, property_name: String, filterable: bool) -> void:
|
|
||||||
property_name = _truncate_string(property_name)
|
|
||||||
if not filterable:
|
|
||||||
lobby._impl.filterable_props.erase(property_name)
|
|
||||||
elif not lobby._impl.filterable_props.has(property_name):
|
|
||||||
lobby._impl.filterable_props.push_back(property_name)
|
|
||||||
|
|
||||||
|
|
||||||
static func _set_lobby_sortable(lobby, property_name: String, sortable: bool) -> void:
|
|
||||||
property_name = _truncate_string(property_name)
|
|
||||||
if not sortable:
|
|
||||||
lobby._impl.sortable_props.erase(property_name)
|
|
||||||
elif not lobby._impl.sortable_props.has(property_name):
|
|
||||||
lobby._impl.sortable_props.push_back(property_name)
|
|
|
@ -1,48 +0,0 @@
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020-2020 Macaroni Studios AB
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
class_name _GotmImplUtility
|
|
||||||
#warnings-disable
|
|
||||||
|
|
||||||
|
|
||||||
static func _fuzzy_compare(a, b, compare_less: bool) -> bool:
|
|
||||||
if typeof(a) == typeof(b):
|
|
||||||
return a < b if compare_less else a > b
|
|
||||||
|
|
||||||
# GDScript doesn't handle comparison of different types very well.
|
|
||||||
# Abuse Array's min and max functions instead.
|
|
||||||
var m = [a, b].min() if compare_less else [a, b].max()
|
|
||||||
if m != null or a == null or b == null:
|
|
||||||
return m == a
|
|
||||||
|
|
||||||
# Array method failed. Go with strings instead.
|
|
||||||
a = String(a)
|
|
||||||
b = String(b)
|
|
||||||
return a < b if compare_less else a > b
|
|
||||||
|
|
||||||
|
|
||||||
static func is_less(a, b) -> bool:
|
|
||||||
return _fuzzy_compare(a, b, true)
|
|
||||||
|
|
||||||
|
|
||||||
static func is_greater(a, b) -> bool:
|
|
||||||
return _fuzzy_compare(a, b, false)
|
|
|
@ -74,31 +74,6 @@ _global_script_classes=[ {
|
||||||
"language": "GDScript",
|
"language": "GDScript",
|
||||||
"path": "res://Scenes/World.gd"
|
"path": "res://Scenes/World.gd"
|
||||||
}, {
|
}, {
|
||||||
"base": "Reference",
|
|
||||||
"class": "GotmDebug",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://gotm/GotmDebug.gd"
|
|
||||||
}, {
|
|
||||||
"base": "Reference",
|
|
||||||
"class": "GotmFile",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://gotm/GotmFile.gd"
|
|
||||||
}, {
|
|
||||||
"base": "Reference",
|
|
||||||
"class": "GotmLobby",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://gotm/GotmLobby.gd"
|
|
||||||
}, {
|
|
||||||
"base": "Reference",
|
|
||||||
"class": "GotmLobbyFetch",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://gotm/GotmLobbyFetch.gd"
|
|
||||||
}, {
|
|
||||||
"base": "Reference",
|
|
||||||
"class": "GotmUser",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://gotm/GotmUser.gd"
|
|
||||||
}, {
|
|
||||||
"base": "TileMap",
|
"base": "TileMap",
|
||||||
"class": "MapTiles",
|
"class": "MapTiles",
|
||||||
"language": "GDScript",
|
"language": "GDScript",
|
||||||
|
@ -143,21 +118,6 @@ _global_script_classes=[ {
|
||||||
"class": "UICommand",
|
"class": "UICommand",
|
||||||
"language": "GDScript",
|
"language": "GDScript",
|
||||||
"path": "res://Classes/UICommand.gd"
|
"path": "res://Classes/UICommand.gd"
|
||||||
}, {
|
|
||||||
"base": "Reference",
|
|
||||||
"class": "_GotmDebugImpl",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://gotm/_impl/_GotmDebugImpl.gd"
|
|
||||||
}, {
|
|
||||||
"base": "Reference",
|
|
||||||
"class": "_GotmImpl",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://gotm/_impl/_GotmImpl.gd"
|
|
||||||
}, {
|
|
||||||
"base": "Reference",
|
|
||||||
"class": "_GotmImplUtility",
|
|
||||||
"language": "GDScript",
|
|
||||||
"path": "res://gotm/_impl/_GotmImplUtility.gd"
|
|
||||||
} ]
|
} ]
|
||||||
_global_script_class_icons={
|
_global_script_class_icons={
|
||||||
"ActivationRange": "",
|
"ActivationRange": "",
|
||||||
|
@ -173,11 +133,6 @@ _global_script_class_icons={
|
||||||
"GameObjectScanner": "",
|
"GameObjectScanner": "",
|
||||||
"GameUI": "",
|
"GameUI": "",
|
||||||
"GameWorld": "",
|
"GameWorld": "",
|
||||||
"GotmDebug": "",
|
|
||||||
"GotmFile": "",
|
|
||||||
"GotmLobby": "",
|
|
||||||
"GotmLobbyFetch": "",
|
|
||||||
"GotmUser": "",
|
|
||||||
"MapTiles": "",
|
"MapTiles": "",
|
||||||
"Occluder": "",
|
"Occluder": "",
|
||||||
"POI": "",
|
"POI": "",
|
||||||
|
@ -186,10 +141,7 @@ _global_script_class_icons={
|
||||||
"ProbeArea": "",
|
"ProbeArea": "",
|
||||||
"ProbeElectric": "",
|
"ProbeElectric": "",
|
||||||
"ResourceQueue": "",
|
"ResourceQueue": "",
|
||||||
"UICommand": "",
|
"UICommand": ""
|
||||||
"_GotmDebugImpl": "",
|
|
||||||
"_GotmImpl": "",
|
|
||||||
"_GotmImplUtility": ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[application]
|
[application]
|
||||||
|
@ -205,7 +157,6 @@ config/icon="res://icon.png"
|
||||||
Music="*res://Scenes/Global/Music.tscn"
|
Music="*res://Scenes/Global/Music.tscn"
|
||||||
Multiplayer="*res://Scenes/Global/Multiplayer.gd"
|
Multiplayer="*res://Scenes/Global/Multiplayer.gd"
|
||||||
SceneManager="*res://Scenes/Global/SceneManager.gd"
|
SceneManager="*res://Scenes/Global/SceneManager.gd"
|
||||||
Gotm="*res://gotm/Gotm.gd"
|
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
|
|
||||||
|
@ -264,6 +215,11 @@ sprint={
|
||||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"unicode":0,"echo":false,"script":null)
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"unicode":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
ui_chat={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":84,"unicode":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[layer_names]
|
[layer_names]
|
||||||
|
|
||||||
|
|
Reference in a new issue