From b091f5863618782bcfef6a2a65b310fa3168b062 Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Wed, 8 Dec 2021 16:35:37 -0600 Subject: [PATCH] Oops --- .../iosevkalyte-regular.ttf.import | 29 + assets/img/panel.png | Bin 0 -> 676 bytes assets/img/panel.png.import | 35 + assets/img/spaced-dungeon-tileset.png | Bin 0 -> 8718 bytes assets/img/spaced-dungeon-tileset.png.import | 35 + assets/theme.tres | 14101 +++++++++++++++- objects/bar.tscn | 10 + objects/lobby.tscn | 12 +- objects/peer.tscn | 31 + objects/player.tscn | 131 + project.godot | 16 + screens/lobby_browser.tscn | 63 +- screens/main_menu.tscn | 26 +- screens/multiplayer_lobby.tscn | 43 +- scripts/global/global.gd | 1 + scripts/global/multiplayer_client.gd | 16 +- scripts/global/signaller_client.gd | 3 + scripts/objects/lobby.gd | 20 +- scripts/objects/peer.gd | 36 + scripts/objects/player.gd | 125 + scripts/screens/game.gd | 19 +- scripts/screens/lobby_browser.gd | 89 +- scripts/screens/multiplayer_lobby.gd | 134 +- server.ts | 12 +- 24 files changed, 14800 insertions(+), 187 deletions(-) create mode 100644 assets/fonts/iosevkalyte/iosevkalyte-regular.ttf.import create mode 100644 assets/img/panel.png create mode 100644 assets/img/panel.png.import create mode 100644 assets/img/spaced-dungeon-tileset.png create mode 100644 assets/img/spaced-dungeon-tileset.png.import create mode 100644 objects/bar.tscn create mode 100644 objects/peer.tscn create mode 100644 objects/player.tscn create mode 100644 scripts/objects/peer.gd create mode 100644 scripts/objects/player.gd diff --git a/assets/fonts/iosevkalyte/iosevkalyte-regular.ttf.import b/assets/fonts/iosevkalyte/iosevkalyte-regular.ttf.import new file mode 100644 index 0000000..0b7e41f --- /dev/null +++ b/assets/fonts/iosevkalyte/iosevkalyte-regular.ttf.import @@ -0,0 +1,29 @@ +[remap] + +importer="font_data_dynamic" +type="FontData" +uid="uid://cklw0gsblqjmg" +path="res://.godot/imported/iosevkalyte-regular.ttf-a2a88ad4f5b4e12dda9997dfa2756a6e.fontdata" + +[deps] + +source_file="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf" +dest_files=["res://.godot/imported/iosevkalyte-regular.ttf-a2a88ad4f5b4e12dda9997dfa2756a6e.fontdata"] + +[params] + +antialiased=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +force_autohinter=false +hinting=1 +oversampling=0.0 +compress=true +preload/char_ranges=PackedStringArray() +preload/glyph_ranges=PackedStringArray() +preload/configurations=PackedStringArray() +support_overrides/language_enabled=PackedStringArray() +support_overrides/language_disabled=PackedStringArray() +support_overrides/script_enabled=PackedStringArray() +support_overrides/script_disabled=PackedStringArray() diff --git a/assets/img/panel.png b/assets/img/panel.png new file mode 100644 index 0000000000000000000000000000000000000000..87e6c0bb9af8f398ba6c02080072fde862e2b4eb GIT binary patch literal 676 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU~JBGb`J1#c2+1T%1_J8No8Qr zm{>c}*5j~)%+dJZrAngg#vK+ZEBwU%ILU0~a$O;i)fy)2qxZK-txe_V(ZB*-iys>! zN(&0FT|F2oz+&%k=gu6F*d<5)tG0?6%E^3xe0T5hyVedX!e``__b}~x81XX2>D05Z zB`35ObvU%N7`EPgwnk^UfW^Ldf{$Lu&PivT|GW50M&)cq#my?lSjpm1iSS-{+uYi`rQ1SYd`Be&8V4QWHx%}h~AFKO%B^s^2qbijAhbO^iyiD zS>zX9(+<71N4-ALBwt6raPBjONf8H>QeJtuil&A0axM zBrKn8WR&fC&i=po{@q)xorU(Avly)6W4NQ<%Zye-Mld|+f7ROcidt8HQ(oGkd3BKu!cy(cjI5nWG<9*m#qJ_*YtbgFNTyA ztFQ1D6mal}YgX$%xEuR+@89ZWH}CV$h$z~ou`JdA7%OZ^-tI08|3PrU-sO{lBAf*t zk;M!QddeWoSh3W;3@FH6;_2(k{*;G9R8XQLU$oV@SoVx3>cY85DV(4<1;sB zKGxI9__n&|{_k$<)~~uJDF@UcYk(2r|&q)z4*}Q$iB} D&@lVm literal 0 HcmV?d00001 diff --git a/assets/img/panel.png.import b/assets/img/panel.png.import new file mode 100644 index 0000000..ce8fb4f --- /dev/null +++ b/assets/img/panel.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/panel.png-13859c06a04f6a7402b47e151aade45d.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/img/panel.png" +dest_files=[ "res://.import/panel.png-13859c06a04f6a7402b47e151aade45d.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/assets/img/spaced-dungeon-tileset.png b/assets/img/spaced-dungeon-tileset.png new file mode 100644 index 0000000000000000000000000000000000000000..9149c2d6234ad3ade0436a7d4c0fd5aaf4c4c700 GIT binary patch literal 8718 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 max_mana: + $bars/mana_bar.hide() + else: + $bars/mana_bar.show() + +func maybe_hide_health_bar(): + if health >= max_health: + $bars/health_bar.hide() + else: + $bars/health_bar.show() + +func _ready(): + # rset_config("acceleration", MultiplayerAPI.RPC_MODE_REMOTESYNC) + # rset_config("velocity", MultiplayerAPI.RPC_MODE_REMOTESYNC) + rset_config("position", MultiplayerAPI.RPC_MODE_SLAVE) + set_display_name(name) + +func set_display_name(i_name): + display_name = i_name + if len(get_tree().get_network_connected_peers()) > 1: + name_displayer.text = display_name + else: + name_displayer.text = "" + +static func int_input_action(action): + return int(Input.is_action_pressed(action)) + +func _physics_process(delta): + self.mana += (10*delta) + self.health += (1*delta) + _process_input() + _process_animation() + rset("velocity", move_and_slide((velocity + acceleration).clamped(max_speed), Vector2.ZERO, false, 4, 0.785398, false).move_toward(Vector2.ZERO, friction * delta)) + for index in get_slide_count(): + var collision = get_slide_collision(index) + if collision.collider is RigidBody2D: + collision.collider.apply_central_impulse(-collision.normal * weight) + + if is_network_master(): + rset_unreliable("position", position) + +func _process_input(): + if is_network_master(): + var iv = Vector2() + # probably want to implement a "whichever one was pressed last" so that + # players move as fast as possible without having to worry about switching + # key frame perfectly + iv.x = int_input_action("ui_right") - int_input_action("ui_left") + iv.y = int_input_action("ui_down") - int_input_action("ui_up") + rset("acceleration", iv.normalized() * speed) + +func _process_animation(): + if velocity == Vector2.ZERO: + sprite.animation = "idle" + else: + sprite.animation = "run" + if velocity.x < 0: + sprite.flip_h = true + elif velocity.x > 0: + sprite.flip_h = false diff --git a/scripts/screens/game.gd b/scripts/screens/game.gd index a9a9b67..b8f2afc 100644 --- a/scripts/screens/game.gd +++ b/scripts/screens/game.gd @@ -1,20 +1,17 @@ extends Node2D +onready var player = preload("res://objects/player.tscn") -# Declare member variables here. Examples: -# var a = 2 -# var b = "text" - - -# Called when the node enters the scene tree for the first time. func _ready(): - pass # Replace with function body. + rpc("add_player", get_tree().get_network_unique_id()) +func _process(delta): + pass -# Called every frame. 'delta' is the elapsed time since the previous frame. -#func _process(delta): -# pass - +remotesync func add_player(peer_id): + var new_player = player.instance() + new_player.set_network_master(peer_id) + add_child(new_player) func _on_Button_pressed(): Global.main_menu() diff --git a/scripts/screens/lobby_browser.gd b/scripts/screens/lobby_browser.gd index f8cae5a..7295e0e 100644 --- a/scripts/screens/lobby_browser.gd +++ b/scripts/screens/lobby_browser.gd @@ -4,16 +4,17 @@ extends Control onready var is_loaded = false -onready var lobbies = $v/body/lobbies -onready var join_button = $v/head/join +onready var lobbies_grid = $v/body/p/lobbies +onready var zero_state = $v/body/p/lobbies/zero_state onready var lobbies_label = $v/subhead/label onready var display_name_edit = $v/subhead/display_name -onready var lobbies_map = {} +onready var lobbies = {} + +onready var lobby = preload("res://objects/lobby.tscn") func _ready(): display_name_edit.text = Global.client.signaller.display_name - join_button.disabled = true Global.client.signaller.connect("lobby_new", self, "_lobby_new") Global.client.signaller.connect("lobby_delete", self, "_lobby_delete") Global.client.signaller.connect("lobby_joined", self, "_lobby_joined") @@ -25,6 +26,23 @@ func _ready(): Global.create_lobby = false Global.client.signaller.create_lobby() + """ + var ls = [] + for n in range(200): + ls.push_back({id = n, name = n, currentPlayers = n, maxPlayers = n, locked = false}) + call_deferred("_lobby_new", ls) + """ + +# TODO: add search +func _input(ev): + if ev is InputEventKey and ev.pressed: + if ev.scancode == KEY_T: + var ls = [] + for n in range(5): + var j = n + lobbies.size() + ls.push_back({id = j, name = j, currentPlayers = j, maxPlayers = j, locked = false}) + call_deferred("_lobby_new", ls) + func _signaller_disconnected(): Global.main_menu() @@ -45,40 +63,55 @@ func _on_join_pressed(): if len(items) > 0: Global.client.signaller.join_lobby(lobbies.get_item_metadata(items[0])["id"]) -func _lobby_new(new_lobbies): +func _lobby_new(new_lobbies: Array): # TODO: handle scrolling so that the user is never too jarred (like chat) for lobby_index in range(len(new_lobbies)): - var lobby = new_lobbies[lobby_index] - var id = lobby["id"] - var lobby_text = "%s (%d/%d players)" % [lobby["name"], lobby["currentPlayers"], lobby["maxPlayers"]] - if lobbies_map.has(id): - print("Updated Lobby ", lobby) - var i = lobbies_map[id] - lobbies.set_item_metadata(i, lobby) - lobbies.set_item_text(i, lobby_text) + var lobby_data = new_lobbies[lobby_index] + var id = lobby_data["id"] + if lobbies.has(id): + _update_lobby(id, lobby_data) else: - print("New Lobby ", lobby) - lobbies.add_item(lobby_text) - lobbies_map[id] = lobbies.get_item_count() - 1 - lobbies.set_item_metadata(lobbies_map[id], lobby) + _add_lobby(id, lobby_data) if Global.join_first_available_lobby: Global.join_first_available_lobby = false Global.client.signaller.join_lobby(id) - lobbies_label.text = "Active Lobbies: %d" % lobbies.get_item_count() -func _lobby_delete(id): - if lobbies_map.has(id): - var i = lobbies_map[id] - # TODO: this will break our current mappings due to re-order - lobbies.remove_item(i) - lobbies_label.text = "Active Lobbies: %d" % lobbies.get_item_count() +func _lobby_delete(id: String): + print("Lobby Deleted: %s" % id) + _delete_lobby(id) -func _on_lobbies_item_activated(_index): - _on_join_pressed() +func _add_lobby(id, lobby_data): + call_deferred("update_lobbies_text") + print("New Lobby ", lobby_data) + # if lobby_data.currentPlayers > 0: + var new_lobby = lobby.instance() + new_lobby.set_with_dict(lobby_data) + lobbies_grid.add_child(new_lobby) + lobbies[id] = new_lobby -func _on_lobbies_item_selected(_index): - join_button.disabled = false +func _update_lobby(id, lobby_data): + call_deferred("update_lobbies_text") + print("Updated Lobby ", lobby_data) + lobbies[id].set_with_dict(lobby_data) + +func _delete_lobby(id): + call_deferred("update_lobbies_text") + if lobbies.has(id): + print("Removing lobby...") + lobbies_grid.remove_child(lobbies[id]) + lobbies[id].queue_free() + lobbies.erase(id) + +func update_lobbies_text(): + var n = lobbies.size() + lobbies_label.text = "Active Lobbies: %d" % n + if n < 1: + if !lobbies_grid.is_a_parent_of(zero_state): + lobbies_grid.add_child(zero_state) + if n > 0: + if lobbies_grid.is_a_parent_of(zero_state): + lobbies_grid.remove_child(zero_state) func _on_display_name_text_changed(new_text): Global.client.signaller.set_display_name(new_text) diff --git a/scripts/screens/multiplayer_lobby.gd b/scripts/screens/multiplayer_lobby.gd index 3de604f..955dbf8 100644 --- a/scripts/screens/multiplayer_lobby.gd +++ b/scripts/screens/multiplayer_lobby.gd @@ -1,100 +1,85 @@ extends MarginContainer -onready var peers = $v/body/peers/peers +onready var peers_grid = $v/body/peers/p/peers onready var lobby_name = $v/head/lobby_info onready var peers_list_label = $v/body/peers/label -onready var cursors = {} onready var chat = $v/body/v/messages onready var chat_edit = $v/body/v/h/chat onready var auto_scroll_chk = $v/body/v/chat_head/auto_scroll - onready var max_players = $v/head/max_players onready var ready_up = $v/head/ready_up onready var lock = $v/head/lock onready var start = $v/head/start -onready var ready_tex: Texture = load("res://assets/img/check.png") -onready var not_ready_tex: Texture = load("res://assets/img/cross.png") -onready var can_start = true - -onready var peers_map = {} +onready var peer = preload("res://objects/peer.tscn") +onready var peers = {} +onready var is_host = false func _ready(): Global.client.signaller.connect("peer_joined", self, "_peer_joined") Global.client.signaller.connect("peer_left", self, "_peer_left") - # Global.client.connect("peer_disconnected", self, "_peer_left") Global.client.signaller.connect("lobby_left", self, "_lobby_left") - lobby_name.text = "%s" % Global.client.signaller.lobby_name + Global.client.signaller.connect("lobby_new", self, "_lobby_update") + Global.client.signaller.connect("websocket_disconnected", self, "_signaller_disconnected") - Global.client.signaller.request_peer_list() + + lobby_name.text = "%s" % Global.client.signaller.lobby_name + + Global.client.signaller.call_deferred("request_peer_list") call_deferred("add_chat", "# Connected to %s" % Global.client.signaller.lobby_name) + # hide/show controls depending on whether or not we're the host var is_host = Global.client.signaller.peer_id == 1 if is_host: ready_up.queue_free() else: + lobby_name.editable = false max_players.editable = false start.queue_free() lock.queue_free() - -func _host_suffix(id): - if id == 1: - return " (Host)" - else: - return "" - -func _signaller_disconnected(): - Global.main_menu() + +func _lobby_update(l): + for u in l: + if !is_host: + max_players.text = str(u.maxPlayers) + lobby_name.text = u.name + print(l) + break func _peer_joined(joined_peers): call_deferred("update_player_count") + if Global.client.signaller.is_host(): call_deferred("update_can_start") for peer_index in range(len(joined_peers)): - var peer = joined_peers[peer_index] - var id = peer["id"] - var peer_text = "%s%s" % [peer["name"], _host_suffix(peer["peerId"])] - if peers_map.has(id): - var i = peers_map[id] - var old_peer = peers.get_item_metadata(i) - peers.set_item_metadata(i, peer) - # announce changes? + var peer_data = joined_peers[peer_index] + var id = peer_data["id"] + if peers.has(id): + _update_peer(id, peer_data) else: - call_deferred("add_chat", "> %s joined the lobby" % peer["name"]) - var ready = false - var tex = not_ready_tex - if peerId == 1: - ready = true - tex = ready_tex - peers.add_item(peer_text, tex) - var i = peers.get_item_count() - 1 - peers.set_item_metadata(i, peer) - peers_map[id] = i - if !ready: can_start = false + _add_peer(id, peer_data) -func _peer_left(ids): +func _add_peer(id, peer_data): + call_deferred("update_player_count") + + var new_peer = peer.instance() + new_peer.set_with_dict(peer_data) + peers_grid.add_child(new_peer) + peers[id] = new_peer + add_chat("> %s joined the lobby" % new_peer.display_name) + +func _update_peer(id, peer_data): + if peer_data.ready != peers[id].ready: add_chat("! %s is %s" % [peers[id].display_name, "now ready" if peer_data.ready else "no longer ready"]) + peers[id].set_with_dict(peer_data) + # TODO: announce changes in chat? + +func _peer_left(ids: Array): call_deferred("update_player_count") - print("Peer(s) Leaving: %s" % ids) for data in ids: - print(data) var id = data["id"] - for i in range(peers.get_item_count()): - var md = peers.get_item_metadata(i) - if md and md.has("id") and md["id"] == id: - peers.remove_item(i) - call_deferred("add_chat", "< %s left the lobby" % md["name"]) - return + if peers.has(id): + call_deferred("add_chat", "< %s left the lobby" % peers[id].display_name) + peers[id].queue_free() + peers.erase(id) -func update_player_count(): - peers_list_label.text = "Players: %d" % peers.get_item_count() - -func _on_Button_pressed(): - send_chat_message() - -func _on_ready_up_toggled(button_pressed: bool): - Global.client.signaller.set_ready(button_pressed) - -func _on_lobby_info_text_changed(new_text: String): - Global.client.signaller.set_lobby_name(new_text) - remotesync func add_chat(message): if auto_scroll_chk.pressed: call_deferred("scroll_chat_to_bottom") else: @@ -110,17 +95,28 @@ func preserve_chat_scroll(n, tl, fl, tc, fc): chat.select(fl, fc, tl, tc) chat.center_viewport_to_cursor() -func _lobby_left(_id): - Global.lobby_browser() - -func _on_leave_button_pressed(): - Global.lobby_browser() - -func _on_TextEdit_text_entered(_new_text): - send_chat_message() - func send_chat_message(): var message = chat_edit.text if message != "": rpc("add_chat", "%s: %s" % [Global.client.signaller.display_name, message]) chat_edit.text = "" + +func update_can_start(): + var can_start = true + for p in peers: + var peer = peers[p] + if !peer.ready: + can_start = false + break + print(can_start) + if can_start: add_chat("! All players ready - game may now be started") + start.disabled = !can_start + +func _lobby_left(_id): Global.lobby_browser() +func _on_leave_button_pressed(): Global.lobby_browser() +func _on_TextEdit_text_entered(_new_text): send_chat_message() +func update_player_count(): peers_list_label.text = "Players: %d" % peers.size() +func _on_Button_pressed(): send_chat_message() +func _on_ready_up_toggled(button_pressed: bool): Global.client.signaller.set_ready(button_pressed) +func _on_lobby_info_text_changed(new_text: String): Global.client.signaller.set_lobby_name(new_text) +func _signaller_disconnected(): Global.main_menu() diff --git a/server.ts b/server.ts index 2258dbc..0e6aff2 100644 --- a/server.ts +++ b/server.ts @@ -131,8 +131,8 @@ class Client { } lobbyNew(lobby: Lobby) { - // if the client is already in a lobby, they don't care about new lobbies - if (this.lobby) return; + // if the client is already in a lobby, only send messages about their lobby + if (this.lobby && this.lobby != lobby) return; const { id, name, maxPlayers, locked } = lobby; this.send( buildMessage("lobby_new", { @@ -151,6 +151,7 @@ class Client { } lobbyCreate(opts?: Partial) { + this.ready = true; if (this.lobby) { this.send( `[info] cannot create lobby (already in lobby ${this.lobby.id})`, @@ -177,6 +178,7 @@ class Client { } lobbyLeave() { + this.ready = false; const leavingLobby = this.lobby; if (!leavingLobby) { this.send(`[info] cannot leave lobby (not in a lobby)`); @@ -199,6 +201,8 @@ class Lobby { maxPlayers: number; locked: boolean; + _shouldNotify: boolean; + constructor(host: Client, name?: string, maxPlayers = 20) { this.id = crypto.randomUUID(); this.hostClientId = host.id; @@ -207,6 +211,8 @@ class Lobby { this.maxPlayers = maxPlayers; this.locked = false; + this._shouldNotify = true; + allLobbies.set(this.id, this); host.peerId = 1; host.lobbyJoin(this); @@ -244,10 +250,12 @@ class Lobby { } notify() { + if (!this._shouldNotify) return; allClients.forEach((client) => client.lobbyNew(this)); } remove() { + this._shouldNotify = false; allLobbies.delete(this.id); allClients.forEach((client) => client.lobbyDelete(this)); this.clients.forEach((client) => {