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="."]
|
||||
visible = false
|
||||
input_pickable = false
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||
|
|
|
@ -66,6 +66,7 @@ one_shot = true
|
|||
|
||||
[node name="ActivationRange" type="Area2D" parent="."]
|
||||
visible = false
|
||||
input_pickable = false
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||
|
@ -73,6 +74,7 @@ position = Vector2( 16, 16 )
|
|||
shape = SubResource( 8 )
|
||||
|
||||
[node name="CloseRange" type="Area2D" parent="."]
|
||||
input_pickable = false
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="CloseRange"]
|
||||
|
|
|
@ -52,7 +52,7 @@ func refresh_sprite() -> void:
|
|||
$Light2D.rotation = rot
|
||||
$ActivationRange.rotation = rot
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
func _physics_process(_delta: float) -> void:
|
||||
if Engine.editor_hint:
|
||||
return
|
||||
manager.power_usage = max(1e-3, MAX_USAGE * strength)
|
||||
|
|
|
@ -14,7 +14,6 @@ extents = Vector2( 72, 72 )
|
|||
|
||||
[node name="Engine" type="StaticBody2D"]
|
||||
script = ExtResource( 2 )
|
||||
max_force = 0.05
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2( 48, 48 )
|
||||
|
@ -36,6 +35,7 @@ energy = 0.002
|
|||
|
||||
[node name="ActivationRange" type="Area2D" parent="."]
|
||||
visible = false
|
||||
input_pickable = false
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||
|
|
|
@ -38,6 +38,7 @@ __meta__ = {
|
|||
|
||||
[node name="ActivationRange" type="Area2D" parent="."]
|
||||
visible = false
|
||||
input_pickable = false
|
||||
script = ExtResource( 4 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||
|
|
|
@ -97,6 +97,7 @@ rect_scale = Vector2( 0.5, 0.5 )
|
|||
[node name="ActivationRange" type="Area2D" parent="."]
|
||||
visible = false
|
||||
position = Vector2( 16, 16 )
|
||||
input_pickable = false
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ActivationRange"]
|
||||
|
|
|
@ -5,5 +5,5 @@ class_name GameObjectScanner
|
|||
func serialize():
|
||||
return {}
|
||||
|
||||
func deserialize(data):
|
||||
func deserialize(_data):
|
||||
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)
|
||||
draw_polygon(points_arc, colors)
|
||||
|
||||
func hear(event):
|
||||
func hear(_event):
|
||||
pass
|
||||
|
|
|
@ -24,7 +24,7 @@ position = Vector2( 0, -3 )
|
|||
shape = SubResource( 1 )
|
||||
|
||||
[node name="Earing" type="Area2D" parent="."]
|
||||
monitorable = false
|
||||
input_pickable = false
|
||||
collision_layer = 32
|
||||
collision_mask = 32
|
||||
|
||||
|
|
|
@ -111,3 +111,26 @@ func _draw():
|
|||
func _area_moved():
|
||||
scout()
|
||||
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 )
|
||||
|
||||
[node name="ElProbe" type="Area2D"]
|
||||
input_pickable = false
|
||||
collision_layer = 4
|
||||
collision_mask = 4
|
||||
script = ExtResource( 1 )
|
||||
|
|
|
@ -21,7 +21,8 @@ static func to_letter(num: int) -> String:
|
|||
return "Α"
|
||||
elif num < 0:
|
||||
letters = "αβγδεζηθικλμνξοπρστυφχψω"
|
||||
num = abs(num)
|
||||
if num < 0:
|
||||
num = -num
|
||||
var out = ""
|
||||
var base = letters.length()
|
||||
while num > 0:
|
||||
|
|
|
@ -4,7 +4,7 @@ var thread
|
|||
var mutex
|
||||
var sem
|
||||
|
||||
var gotm_mode = false
|
||||
var threaded = true
|
||||
var time_max = 100 # Milliseconds.
|
||||
|
||||
var queue = []
|
||||
|
@ -13,19 +13,15 @@ var pending = {}
|
|||
func _lock(_caller):
|
||||
mutex.lock()
|
||||
|
||||
|
||||
func _unlock(_caller):
|
||||
mutex.unlock()
|
||||
|
||||
|
||||
func _post(_caller):
|
||||
sem.post()
|
||||
|
||||
|
||||
func _wait(_caller):
|
||||
sem.wait()
|
||||
|
||||
|
||||
func queue_resource(path, p_in_front = false):
|
||||
_lock("queue_resource")
|
||||
if path in pending:
|
||||
|
@ -48,7 +44,6 @@ func queue_resource(path, p_in_front = false):
|
|||
_unlock("queue_resource")
|
||||
return
|
||||
|
||||
|
||||
func cancel_resource(path):
|
||||
_lock("cancel_resource")
|
||||
if path in pending:
|
||||
|
@ -57,9 +52,8 @@ func cancel_resource(path):
|
|||
pending.erase(path)
|
||||
_unlock("cancel_resource")
|
||||
|
||||
|
||||
func get_progress(path):
|
||||
if gotm_mode:
|
||||
if not threaded:
|
||||
return 1.0
|
||||
_lock("get_progress")
|
||||
var ret = -1
|
||||
|
@ -71,9 +65,8 @@ func get_progress(path):
|
|||
_unlock("get_progress")
|
||||
return ret
|
||||
|
||||
|
||||
func is_ready(path):
|
||||
if gotm_mode:
|
||||
if not threaded:
|
||||
return true
|
||||
var ret
|
||||
_lock("is_ready")
|
||||
|
@ -84,9 +77,8 @@ func is_ready(path):
|
|||
_unlock("is_ready")
|
||||
return ret
|
||||
|
||||
|
||||
func _wait_for_resource(res, path):
|
||||
if gotm_mode:
|
||||
if not threaded:
|
||||
return get_resource(path)
|
||||
_unlock("wait_for_resource")
|
||||
while true:
|
||||
|
@ -97,9 +89,8 @@ func _wait_for_resource(res, path):
|
|||
return pending[path]
|
||||
_unlock("wait_for_resource")
|
||||
|
||||
|
||||
func get_resource(path):
|
||||
if gotm_mode:
|
||||
if not threaded:
|
||||
return load(path)
|
||||
_lock("get_resource")
|
||||
if path in pending:
|
||||
|
@ -123,7 +114,6 @@ func get_resource(path):
|
|||
_unlock("return")
|
||||
return ResourceLoader.load(path)
|
||||
|
||||
|
||||
func thread_process():
|
||||
_wait("thread_process")
|
||||
_lock("process")
|
||||
|
@ -143,16 +133,15 @@ func thread_process():
|
|||
queue.erase(res)
|
||||
_unlock("process")
|
||||
|
||||
|
||||
func thread_func(_u):
|
||||
while true:
|
||||
thread_process()
|
||||
|
||||
|
||||
func start(gotm: bool):
|
||||
gotm_mode = gotm
|
||||
if not gotm:
|
||||
mutex = Mutex.new()
|
||||
sem = Semaphore.new()
|
||||
thread = Thread.new()
|
||||
thread.start(self, "thread_func", 0)
|
||||
func start(use_threads: bool):
|
||||
threaded = use_threads
|
||||
if not threaded:
|
||||
return
|
||||
mutex = Mutex.new()
|
||||
sem = Semaphore.new()
|
||||
thread = Thread.new()
|
||||
thread.start(self, "thread_func", 0)
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
script/source = "extends Node
|
||||
|
||||
func _ready():
|
||||
$\"/root/Multiplayer\".gotm_mode = true # Faster startup
|
||||
$\"/root/Multiplayer\".host()
|
||||
"
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ var writing = false
|
|||
|
||||
func _ready() -> void:
|
||||
randomize()
|
||||
ui.connect("command", world, "process_command")
|
||||
if netgame.hosting:
|
||||
world.load_map(netgame.get_current_map())
|
||||
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()
|
||||
shape.radius = radius
|
||||
var query = Physics2DShapeQueryParameters.new()
|
||||
query.collide_with_areas = true
|
||||
query.collide_with_bodies = false
|
||||
query.collision_layer = 32
|
||||
query.transform = Transform(world.transform.translated(origin))
|
||||
query.transform.origin = origin
|
||||
query.set_shape(shape)
|
||||
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:
|
||||
rpc("add_log", message)
|
||||
|
|
|
@ -14,7 +14,7 @@ const SYSTEMS_UPDATE_INTERVAL = 10
|
|||
# Master server data
|
||||
const MASTER_SERVER_ADDR = "fgms.zyg.ovh"
|
||||
const MASTER_SERVER_UDP_PORT = 9434
|
||||
const MS_GAME_CODE = "odyssey-0-a1"
|
||||
const MS_GAME_CODE = "odyssey-0-a2"
|
||||
const GOTM_OVERRIDE = false
|
||||
|
||||
# Master server entry
|
||||
|
@ -22,7 +22,6 @@ var ms_active = false
|
|||
var ms_key = ""
|
||||
var server_name = ""
|
||||
|
||||
onready var gotm_mode = Gotm.is_live() or GOTM_OVERRIDE
|
||||
var hosting = false
|
||||
|
||||
export var player_name = ""
|
||||
|
@ -34,8 +33,6 @@ onready var scene_manager = $"/root/SceneManager"
|
|||
|
||||
func _ready():
|
||||
player_name = "tider-" + str(randi() % 1000)
|
||||
if gotm_mode:
|
||||
Gotm.connect("lobby_changed", self, "_lobby_changed")
|
||||
|
||||
func bind_events():
|
||||
get_tree().connect("network_peer_connected", self, "_player_connected")
|
||||
|
@ -76,22 +73,17 @@ func host():
|
|||
yield(get_tree().create_timer(0.3), "timeout")
|
||||
|
||||
# Run port forwarding/nat punchthrough if the platform doesn't do it already
|
||||
if not gotm_mode:
|
||||
print("Running UPNP magicks")
|
||||
if discover_upnp() == UPNP.UPNP_RESULT_SUCCESS:
|
||||
print("UPNP mapping added")
|
||||
else:
|
||||
push_warning("UPNP magicks fail, punching NAT in the face")
|
||||
yield(punch_nat(), "completed")
|
||||
print("Running UPNP magicks")
|
||||
if discover_upnp() == UPNP.UPNP_RESULT_SUCCESS:
|
||||
print("UPNP mapping added")
|
||||
else:
|
||||
push_warning("UPNP magicks fail, punching NAT in the face")
|
||||
yield(punch_nat(), "completed")
|
||||
|
||||
server_name = player_name + "'s server"
|
||||
player_info[1] = { "name": player_name }
|
||||
round_info = { "map": "odyssey" }
|
||||
|
||||
if gotm_mode:
|
||||
# Add to master server before hosting (in GOTM mode)
|
||||
create_ms_entry()
|
||||
|
||||
bind_events()
|
||||
var peer = NetworkedMultiplayerENet.new()
|
||||
peer.compression_mode = NetworkedMultiplayerENet.COMPRESS_FASTLZ
|
||||
|
@ -113,9 +105,8 @@ func host():
|
|||
"res://Scenes/Maps/odyssey.tscn"
|
||||
])
|
||||
|
||||
if not gotm_mode:
|
||||
# Add to master server after hosting
|
||||
create_ms_entry()
|
||||
# Add to master server after hosting
|
||||
create_ms_entry()
|
||||
|
||||
func join(server):
|
||||
scene_manager.enter_loader()
|
||||
|
@ -126,13 +117,9 @@ func join(server):
|
|||
|
||||
bind_events()
|
||||
var peer = NetworkedMultiplayerENet.new()
|
||||
peer.compression_mode = NetworkedMultiplayerENet.COMPRESS_FASTLZ
|
||||
|
||||
var addr = null
|
||||
if gotm_mode:
|
||||
var success = yield(server.join(), "completed")
|
||||
addr = Gotm.lobby.host.address
|
||||
else:
|
||||
addr = server.address
|
||||
var addr = server.address
|
||||
|
||||
peer.create_client(addr, SERVER_PORT)
|
||||
get_tree().network_peer = peer
|
||||
|
@ -193,20 +180,15 @@ func _ms_request(endpoint: String, data):
|
|||
push_error("An error occurred in the HTTP request.")
|
||||
|
||||
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()
|
||||
add_child(http_request)
|
||||
http_request.connect("request_completed", self, "_ms_response", ["list_games"])
|
||||
var error = http_request.request(
|
||||
"https://" + MASTER_SERVER_ADDR + "/" + MS_GAME_CODE,
|
||||
["Content-Type: application/json"],
|
||||
true, HTTPClient.METHOD_GET)
|
||||
if error != OK:
|
||||
push_error("An error occurred in the HTTP request.")
|
||||
var http_request = HTTPRequest.new()
|
||||
add_child(http_request)
|
||||
http_request.connect("request_completed", self, "_ms_response", ["list_games"])
|
||||
var error = http_request.request(
|
||||
"https://" + MASTER_SERVER_ADDR + "/" + MS_GAME_CODE,
|
||||
["Content-Type: application/json"],
|
||||
true, HTTPClient.METHOD_GET)
|
||||
if error != OK:
|
||||
push_error("An error occurred in the HTTP request.")
|
||||
|
||||
func get_game_data():
|
||||
return {
|
||||
|
@ -217,28 +199,17 @@ func get_game_data():
|
|||
}
|
||||
|
||||
func create_ms_entry():
|
||||
if gotm_mode:
|
||||
Gotm.host_lobby(true)
|
||||
Gotm.lobby.hidden = false
|
||||
else:
|
||||
_ms_request("new", {
|
||||
"game_id": MS_GAME_CODE,
|
||||
"data": get_game_data()
|
||||
})
|
||||
update_ms_entry()
|
||||
_ms_request("new", {
|
||||
"game_id": MS_GAME_CODE,
|
||||
"data": get_game_data()
|
||||
})
|
||||
|
||||
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:
|
||||
_ms_request("update", {
|
||||
"key": ms_key,
|
||||
"data": get_game_data()
|
||||
})
|
||||
if ms_active:
|
||||
_ms_request("update", {
|
||||
"key": ms_key,
|
||||
"data": get_game_data()
|
||||
})
|
||||
|
||||
var time_left = 30
|
||||
func _process(delta):
|
||||
|
@ -276,6 +247,3 @@ func get_current_map():
|
|||
return GameWorld.Map.RUNTIME
|
||||
_:
|
||||
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"
|
||||
|
||||
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:
|
||||
get_tree().change_scene_to(loader)
|
||||
|
||||
func load_scene(scene_path: String, dependencies: Array) -> void:
|
||||
if not netgame.gotm_mode:
|
||||
if queue.threaded:
|
||||
target_scene = scene_path
|
||||
queue.queue_resource(scene_path)
|
||||
for dep in dependencies:
|
||||
|
@ -24,7 +27,7 @@ func load_scene(scene_path: String, dependencies: Array) -> void:
|
|||
get_tree().change_scene(scene_path)
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
if not netgame.gotm_mode:
|
||||
if queue.threaded:
|
||||
if target_scene != null:
|
||||
var remaining = queue.pending.size()
|
||||
for path in queue.pending:
|
||||
|
|
|
@ -61,11 +61,8 @@ func _process(delta: float):
|
|||
if Engine.editor_hint:
|
||||
return
|
||||
|
||||
var current_speed_ratio = 0
|
||||
var engines = get_engine_count()
|
||||
var max_speed = get_max_speed()
|
||||
if engines > 0:
|
||||
current_speed_ratio = current_ship_speed / max_speed
|
||||
|
||||
# Ease ship speed/direction changes
|
||||
if abs(current_ship_direction) > PI*2:
|
||||
|
@ -163,19 +160,14 @@ func serialize() -> Dictionary:
|
|||
data.append([cell, tilemap.get_cellv(cell)])
|
||||
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 {
|
||||
"tilemaps": tilemapdata,
|
||||
"engines": engines,
|
||||
"objects": objects,
|
||||
"lights": lights,
|
||||
"sockets": sockets,
|
||||
"pois": pois,
|
||||
"engines": serialize_children($engines),
|
||||
"objects": serialize_children($objects),
|
||||
"lights": serialize_children($lights),
|
||||
"sockets": serialize_children($sockets),
|
||||
"pois": serialize_children($pois),
|
||||
"areas": serialize_children($areas),
|
||||
"ship": {
|
||||
"position": current_ship_position,
|
||||
"target": current_ship_target,
|
||||
|
@ -201,6 +193,8 @@ func deserialize(data: Dictionary) -> void:
|
|||
deserialize_children($lights, data["lights"])
|
||||
deserialize_children($pois, data["pois"])
|
||||
deserialize_children($sockets, data["sockets"])
|
||||
deserialize_children($sockets, data["sockets"])
|
||||
deserialize_children($areas, data["areas"], true)
|
||||
|
||||
# Set ship parameters
|
||||
current_ship_position = data["ship"]["position"]
|
||||
|
@ -223,14 +217,17 @@ func serialize_children(node: Node) -> Dictionary:
|
|||
}
|
||||
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:
|
||||
var node_data = data[node_name] as Dictionary
|
||||
var node_scene = load(node_data.filename).instance()
|
||||
node_scene.name = node_name
|
||||
node_scene.transform = node_data.transform
|
||||
if deserialize_first:
|
||||
node_scene.deserialize(node_data.data)
|
||||
node.add_child(node_scene, true)
|
||||
node_scene.deserialize(node_data.data)
|
||||
if not deserialize_first:
|
||||
node_scene.deserialize(node_data.data)
|
||||
|
||||
# Lighting
|
||||
|
||||
|
|
|
@ -103,3 +103,5 @@ __meta__ = {
|
|||
|
||||
[node name="pois" type="Node2D" parent="."]
|
||||
z_index = 999
|
||||
|
||||
[node name="areas" type="Node2D" parent="."]
|
||||
|
|
|
@ -21,10 +21,6 @@ func _ready() -> void:
|
|||
$"/root/Music/BGM".play()
|
||||
netgame.connect("ms_updated", self, "_ms_update")
|
||||
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:
|
||||
refresh_server_remaining -= delta
|
||||
|
@ -49,14 +45,9 @@ func _ms_update(action, result):
|
|||
if action == "list_games":
|
||||
# Reset server list
|
||||
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
|
||||
for server in servers:
|
||||
server_list.add_item(server.data.name + " (" + server.address + ") - " + str(server.data.players) + "/" + str(server.data.max_players) + " players")
|
||||
servers = result
|
||||
for server in servers:
|
||||
server_list.add_item(server.data.name + " (" + server.address + ") - " + str(server.data.players) + "/" + str(server.data.max_players) + " players")
|
||||
|
||||
func set_scale(val) -> void:
|
||||
scale = val
|
||||
|
@ -84,7 +75,7 @@ func _server_addr_changed(new_text: String) -> void:
|
|||
$Popup/MarginContainer/VBoxContainer/HBoxContainer/Button.disabled = new_text.length() < 1
|
||||
|
||||
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):
|
||||
$"/root/Music/BGM".stop()
|
||||
|
|
|
@ -2,8 +2,6 @@ extends Control
|
|||
|
||||
class_name GameUI
|
||||
|
||||
signal command(command)
|
||||
|
||||
enum ServerMenuItem {
|
||||
ServerInfo
|
||||
}
|
||||
|
@ -16,7 +14,7 @@ enum PopupName {
|
|||
onready var logs = $Logs
|
||||
onready var scene = $"/root/scene"
|
||||
|
||||
const CHAT_RADIUS = 32*6
|
||||
const CHAT_RADIUS = 32*10
|
||||
|
||||
func _ready() -> void:
|
||||
# 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.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/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 )
|
||||
border_width_left = 4
|
||||
border_width_top = 4
|
||||
|
@ -31,11 +56,53 @@ __meta__ = {
|
|||
|
||||
[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_top = 10.0
|
||||
margin_right = -857.0
|
||||
margin_bottom = -566.0
|
||||
margin_right = 475.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="."]
|
||||
anchor_left = 1.0
|
||||
|
@ -48,7 +115,7 @@ grow_horizontal = 0
|
|||
grow_vertical = 0
|
||||
size_flags_horizontal = 8
|
||||
size_flags_vertical = 8
|
||||
custom_styles/panel = SubResource( 1 )
|
||||
custom_styles/panel = SubResource( 5 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
@ -90,3 +157,9 @@ margin_left = 31.2773
|
|||
margin_top = 46.9222
|
||||
margin_right = 287.277
|
||||
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
|
||||
|
||||
signal chat_message_sent(text)
|
||||
|
||||
onready var log_text = $ResizablePanel/RichTextLabel
|
||||
onready var chat_bar = $ResizablePanel/LineEdit
|
||||
|
||||
|
@ -7,6 +9,8 @@ func _input(event: InputEvent) -> void:
|
|||
if event is InputEventMouseButton:
|
||||
if not chat_bar_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:
|
||||
log_text.append_bbcode(line)
|
||||
|
@ -18,3 +22,8 @@ func _chat_bar_status(editing: bool) -> void:
|
|||
|
||||
func _chat_bar_focus(entered: bool) -> void:
|
||||
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
|
||||
|
||||
func _physics_process(delta):
|
||||
func _physics_process(_delta):
|
||||
if not visible:
|
||||
return
|
||||
multiplayer
|
||||
|
|
|
@ -6,6 +6,7 @@ export var bgzoom = 50
|
|||
const BORDER_WIDTH = 4
|
||||
const BORDER_LENGTH = 30
|
||||
const RADAR_EFFECT_DELAY = 1
|
||||
const HALF_BORDER = BORDER_LENGTH/2
|
||||
|
||||
export(Texture) var background
|
||||
export(Font) var font
|
||||
|
@ -87,13 +88,13 @@ func draw_target(viewport: Rect2, position: Vector2, color: Color):
|
|||
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))
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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):
|
||||
if event is InputEventMouseButton:
|
||||
|
|
|
@ -43,7 +43,7 @@ application/trademarks=""
|
|||
|
||||
[preset.1]
|
||||
|
||||
name="GOTM"
|
||||
name="HTML5"
|
||||
platform="HTML5"
|
||||
runnable=true
|
||||
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",
|
||||
"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",
|
||||
"class": "MapTiles",
|
||||
"language": "GDScript",
|
||||
|
@ -143,21 +118,6 @@ _global_script_classes=[ {
|
|||
"class": "UICommand",
|
||||
"language": "GDScript",
|
||||
"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={
|
||||
"ActivationRange": "",
|
||||
|
@ -173,11 +133,6 @@ _global_script_class_icons={
|
|||
"GameObjectScanner": "",
|
||||
"GameUI": "",
|
||||
"GameWorld": "",
|
||||
"GotmDebug": "",
|
||||
"GotmFile": "",
|
||||
"GotmLobby": "",
|
||||
"GotmLobbyFetch": "",
|
||||
"GotmUser": "",
|
||||
"MapTiles": "",
|
||||
"Occluder": "",
|
||||
"POI": "",
|
||||
|
@ -186,10 +141,7 @@ _global_script_class_icons={
|
|||
"ProbeArea": "",
|
||||
"ProbeElectric": "",
|
||||
"ResourceQueue": "",
|
||||
"UICommand": "",
|
||||
"_GotmDebugImpl": "",
|
||||
"_GotmImpl": "",
|
||||
"_GotmImplUtility": ""
|
||||
"UICommand": ""
|
||||
}
|
||||
|
||||
[application]
|
||||
|
@ -205,7 +157,6 @@ config/icon="res://icon.png"
|
|||
Music="*res://Scenes/Global/Music.tscn"
|
||||
Multiplayer="*res://Scenes/Global/Multiplayer.gd"
|
||||
SceneManager="*res://Scenes/Global/SceneManager.gd"
|
||||
Gotm="*res://gotm/Gotm.gd"
|
||||
|
||||
[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)
|
||||
]
|
||||
}
|
||||
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]
|
||||
|
||||
|
|
Reference in a new issue