diff --git a/client/addons/com.heroiclabs.nakama/Nakama.gd.uid b/client/addons/com.heroiclabs.nakama/Nakama.gd.uid new file mode 100644 index 0000000..8c40e6f --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/Nakama.gd.uid @@ -0,0 +1 @@ +uid://b3os1t7djg0c7 diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid new file mode 100644 index 0000000..37f64e3 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid @@ -0,0 +1 @@ +uid://hebdk7gx1u8c diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid new file mode 100644 index 0000000..44cf509 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid @@ -0,0 +1 @@ +uid://2l8y628n8ukc diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid new file mode 100644 index 0000000..a32884c --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid @@ -0,0 +1 @@ +uid://d4lu87a2trueb diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid new file mode 100644 index 0000000..e167f8f --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid @@ -0,0 +1 @@ +uid://ckcep8xcypxhx diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid new file mode 100644 index 0000000..ea52579 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid @@ -0,0 +1 @@ +uid://bqgpnrx3ym11e diff --git a/client/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid b/client/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid new file mode 100644 index 0000000..1efd6e4 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid @@ -0,0 +1 @@ +uid://csqmo268gf217 diff --git a/client/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid b/client/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid new file mode 100644 index 0000000..2a3c8e1 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid @@ -0,0 +1 @@ +uid://do7ks66xlkvqg diff --git a/client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid b/client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid new file mode 100644 index 0000000..140a5ff --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid @@ -0,0 +1 @@ +uid://chnrd7u5qamlq diff --git a/client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid b/client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid new file mode 100644 index 0000000..2434a2c --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid @@ -0,0 +1 @@ +uid://iy2uo326og1p diff --git a/client/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid b/client/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid new file mode 100644 index 0000000..4094832 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid @@ -0,0 +1 @@ +uid://c8tfobve6b0k4 diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid new file mode 100644 index 0000000..2beb26e --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid @@ -0,0 +1 @@ +uid://dqyr6q4tquve3 diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid new file mode 100644 index 0000000..81b892e --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid @@ -0,0 +1 @@ +uid://dp68cesoggrb4 diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid new file mode 100644 index 0000000..0752a03 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid @@ -0,0 +1 @@ +uid://duqqgdiqwur2g diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid new file mode 100644 index 0000000..ec9c9c7 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid @@ -0,0 +1 @@ +uid://c35k2c0pg7csr diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid new file mode 100644 index 0000000..0bcdba0 --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid @@ -0,0 +1 @@ +uid://dldoidphr80mb diff --git a/client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid b/client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid new file mode 100644 index 0000000..a67d64e --- /dev/null +++ b/client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid @@ -0,0 +1 @@ +uid://bnxd5hq82ncyw diff --git a/client/danmaku!/Board.tscn b/client/danmaku!/Board.tscn index dd95b89..a34a461 100644 --- a/client/danmaku!/Board.tscn +++ b/client/danmaku!/Board.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=4 format=3 uid="uid://dsiowq0rnacln"] +[gd_scene load_steps=5 format=3 uid="uid://dsiowq0rnacln"] [ext_resource type="PackedScene" uid="uid://cd3tqt7hr5pqs" path="res://danmaku!/Player.tscn" id="1_22cjd"] -[ext_resource type="Script" path="res://danmaku!/scaling.gd" id="1_o1mqp"] -[ext_resource type="Script" path="res://danmaku!/network_manager.gd" id="2_b2dol"] +[ext_resource type="Script" uid="uid://ggkxv1cb1bjk" path="res://danmaku!/scaling.gd" id="1_o1mqp"] +[ext_resource type="Script" uid="uid://cd67rrch5h4t7" path="res://danmaku!/network_manager.gd" id="2_b2dol"] +[ext_resource type="Script" uid="uid://ddc5iqgtyv2ns" path="res://danmaku!/BulletManager.gd" id="4_ubrrh"] [node name="Board" type="Control"] custom_minimum_size = Vector2(607.5, 1080) @@ -32,6 +33,11 @@ scale = Vector2(6.75, 6.75) [node name="Player" parent="World" instance=ExtResource("1_22cjd")] unique_name_in_owner = true -[node name="NetworkManager" type="Node2D" parent="World" node_paths=PackedStringArray("player")] +[node name="BulletManager" type="Node2D" parent="World"] +unique_name_in_owner = true +script = ExtResource("4_ubrrh") + +[node name="NetworkManager" type="Node" parent="World" node_paths=PackedStringArray("player")] +unique_name_in_owner = true script = ExtResource("2_b2dol") player = NodePath("../Player") diff --git a/client/danmaku!/BulletManager.gd b/client/danmaku!/BulletManager.gd new file mode 100644 index 0000000..c57619f --- /dev/null +++ b/client/danmaku!/BulletManager.gd @@ -0,0 +1,22 @@ +extends Node2D + +var bullet_intraframe_mov_delta := 0.0 +var predicted_tick := 0 +var bullets = [] + +func _ready() -> void: + child_exiting_tree.connect(_on_child_exiting_tree) + +func _process(delta: float) -> void: + for bullet in bullets: + var prev_pos = bullet.get_current_pos(predicted_tick) + var next_pos = bullet.get_current_pos(predicted_tick + 1) + var interpolated_pos = prev_pos.lerp(next_pos, bullet_intraframe_mov_delta) + + bullet.position = interpolated_pos + + if bullet.beyond_kill_boundary(predicted_tick): + bullet.queue_free() + +func _on_child_exiting_tree(node: DanmakuBullet): + bullets.erase(node) diff --git a/client/danmaku!/BulletManager.gd.uid b/client/danmaku!/BulletManager.gd.uid new file mode 100644 index 0000000..59a0227 --- /dev/null +++ b/client/danmaku!/BulletManager.gd.uid @@ -0,0 +1 @@ +uid://ddc5iqgtyv2ns diff --git a/client/danmaku!/ScalableSprite2D.gd.uid b/client/danmaku!/ScalableSprite2D.gd.uid new file mode 100644 index 0000000..50196f8 --- /dev/null +++ b/client/danmaku!/ScalableSprite2D.gd.uid @@ -0,0 +1 @@ +uid://v6jris184o8u diff --git a/client/danmaku!/globals.gd.uid b/client/danmaku!/globals.gd.uid new file mode 100644 index 0000000..9fa7127 --- /dev/null +++ b/client/danmaku!/globals.gd.uid @@ -0,0 +1 @@ +uid://ca3lryv0wsoat diff --git a/client/danmaku!/network_manager.gd b/client/danmaku!/network_manager.gd index cc21624..8221654 100644 --- a/client/danmaku!/network_manager.gd +++ b/client/danmaku!/network_manager.gd @@ -1,4 +1,4 @@ -extends Node2D +extends Node var nakama_client: NakamaClient var nakama_session: NakamaSession var nakama_socket: NakamaSocket @@ -7,12 +7,9 @@ var nakama_socket: NakamaSocket var predicted_tick = 0 var delta_counter = 0 -var bullet_lerp_factor := 0.0 -var bullets = [] var current_match_id = "" func _ready() -> void: - child_exiting_tree.connect(_on_child_exiting_tree) print("Attempting auth.") await attempt_auth() print("Attempting to create debug match.") @@ -23,24 +20,15 @@ func _process(delta: float) -> void: if current_match_id == "": return - predict_tick_and_broadcast(delta) - - for bullet in bullets: - var prev_pos = bullet.get_current_pos(predicted_tick) - var next_pos = bullet.get_current_pos(predicted_tick + 1) - var interpolated_pos = prev_pos.lerp(next_pos, bullet_lerp_factor) - - bullet.position = interpolated_pos - - if bullet.beyond_kill_boundary(predicted_tick): - bullet.queue_free() + %BulletManager.bullet_intraframe_mov_delta = predict_tick_and_broadcast(delta) + %BulletManager.predicted_tick = predicted_tick func _on_match_state(p_state : NakamaRTAPI.MatchData): match p_state.op_code: 2: var data = JSON.parse_string(p_state.data) - # Set player position given server bounds-checking + # Set player position if server demands a forced position if data["forcePlayerPos"]: player.set_position_data( Vector2( @@ -49,10 +37,12 @@ func _on_match_state(p_state : NakamaRTAPI.MatchData): ), float(data["playerPos"]["radius_multiplier"]) ) - + + # Handle player death if there is an ongoing death timer if int(data["deathTimer"]) > 0: - print("server says u died") %Player.kill() + elif int(data["deathTimer"]) == 0: + %Player.resurrect() # Spawn new bullets for b in data["newBullets"]: @@ -72,8 +62,8 @@ func _on_match_state(p_state : NakamaRTAPI.MatchData): var scale_ratio = ((b["radius_multiplier"] * 2) * Globals.SERVER_SIZE.x) / bullet.texture.get_width() bullet.scale = Vector2(scale_ratio, scale_ratio) - add_child(bullet) - bullets.append(bullet) + %BulletManager.add_child(bullet) + %BulletManager.bullets.append(bullet) predicted_tick = int(b["tick"]) delta_counter = 0 @@ -103,7 +93,7 @@ func create_and_join_debug_match() -> void: else: current_match_id = response.payload -func predict_tick_and_broadcast(delta): +func predict_tick_and_broadcast(delta) -> float: delta_counter += delta # New tick (60 tick rate), broadcast player inputs @@ -111,10 +101,7 @@ func predict_tick_and_broadcast(delta): if delta_counter >= tick_time: predicted_tick += 1 delta_counter -= tick_time - var pos = get_node("../Player").position + var pos = %Player.position var json_string = JSON.stringify({"x": pos.x, "y": pos.y}) nakama_socket.send_match_state_async(current_match_id, 0, json_string) - bullet_lerp_factor = delta_counter / tick_time - -func _on_child_exiting_tree(node: DanmakuBullet): - bullets.erase(node) + return delta_counter / tick_time diff --git a/client/danmaku!/network_manager.gd.uid b/client/danmaku!/network_manager.gd.uid new file mode 100644 index 0000000..8d9b13d --- /dev/null +++ b/client/danmaku!/network_manager.gd.uid @@ -0,0 +1 @@ +uid://cd67rrch5h4t7 diff --git a/client/danmaku!/player.gd b/client/danmaku!/player.gd index 2e13b85..fde37fb 100644 --- a/client/danmaku!/player.gd +++ b/client/danmaku!/player.gd @@ -8,9 +8,7 @@ var collision: DanmakuCircle = DanmakuCircle.new() # This is temporary, it should be defined per-sprite when I get to the skin system const PLAYER_BODY_WIDTH_MULTIPLIER = 0.18 -# Temp -var flash_timer = 0.0 -var flashing = false +var alive: bool = true func _ready() -> void: $BodySprite.scale_sprite(PLAYER_BODY_WIDTH_MULTIPLIER) @@ -23,15 +21,12 @@ func get_input(): velocity = Input.get_vector("Left", "Right", "Up", "Down") * speed -func _physics_process(delta: float): - # Temp - if flashing: - flash_timer -= delta - $BodySprite.modulate = Color(1, 1, 1, 1) - flashing = false - +func _physics_process(delta: float): get_input() + if !alive: + return + # Bounds checking var attempted_position := position + (velocity * delta) attempted_position = attempted_position.clamp(Vector2(0, 0), Globals.SERVER_SIZE) @@ -47,7 +42,16 @@ func set_position_data(pos: Vector2, hurtcircle_scale_multiplier): $HurtcircleSprite.scale_sprite(hurtcircle_scale_multiplier*2) func kill(): - # Temp - $BodySprite.modulate = Color(1, 0, 0, 1) - flash_timer = 0.5 - flashing = true + if alive == false: + return + + alive = false + $AudioStreamPlayer.play() + self.hide() + +func resurrect(): + if alive == true: + return + + alive = true + self.show() diff --git a/client/danmaku!/player.gd.uid b/client/danmaku!/player.gd.uid new file mode 100644 index 0000000..aa633ce --- /dev/null +++ b/client/danmaku!/player.gd.uid @@ -0,0 +1 @@ +uid://bhwiun72wpk6e diff --git a/client/danmaku!/player.tscn b/client/danmaku!/player.tscn index e49a7e2..2017661 100644 --- a/client/danmaku!/player.tscn +++ b/client/danmaku!/player.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://cd3tqt7hr5pqs"] +[gd_scene load_steps=6 format=3 uid="uid://cd3tqt7hr5pqs"] -[ext_resource type="Script" path="res://danmaku!/player.gd" id="1_r7xhp"] +[ext_resource type="Script" uid="uid://bhwiun72wpk6e" path="res://danmaku!/player.gd" id="1_r7xhp"] [ext_resource type="Texture2D" uid="uid://bs3fntlmlqpt2" path="res://icon.svg" id="2_04s0l"] [ext_resource type="Texture2D" uid="uid://c3deywcu4du2b" path="res://test-collision.png" id="3_gf44i"] -[ext_resource type="Script" path="res://danmaku!/ScalableSprite2D.gd" id="3_u0x7w"] +[ext_resource type="Script" uid="uid://v6jris184o8u" path="res://danmaku!/ScalableSprite2D.gd" id="3_u0x7w"] +[ext_resource type="AudioStream" uid="uid://c5n7x6q67tp78" path="res://test-death-noise.mp3" id="5_poktv"] [node name="Player" type="Node2D"] script = ExtResource("1_r7xhp") @@ -15,3 +16,7 @@ script = ExtResource("3_u0x7w") [node name="HurtcircleSprite" type="Sprite2D" parent="."] texture = ExtResource("3_gf44i") script = ExtResource("3_u0x7w") + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +stream = ExtResource("5_poktv") +volume_db = -20.0 diff --git a/client/danmaku!/scaling.gd.uid b/client/danmaku!/scaling.gd.uid new file mode 100644 index 0000000..2c74b5a --- /dev/null +++ b/client/danmaku!/scaling.gd.uid @@ -0,0 +1 @@ +uid://ggkxv1cb1bjk diff --git a/client/danmaku-shared.gdextension.uid b/client/danmaku-shared.gdextension.uid new file mode 100644 index 0000000..2ca77c8 --- /dev/null +++ b/client/danmaku-shared.gdextension.uid @@ -0,0 +1 @@ +uid://b4v43qwewcg37 diff --git a/client/project.godot b/client/project.godot index 06843f6..07dd999 100644 --- a/client/project.godot +++ b/client/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="danmaku!" run/main_scene="res://danmaku!/Game.tscn" -config/features=PackedStringArray("4.3", "Forward Plus") +config/features=PackedStringArray("4.4", "Forward Plus") config/icon="res://icon.svg" [autoload] diff --git a/client/test-death-noise.mp3 b/client/test-death-noise.mp3 new file mode 100644 index 0000000..86525a0 Binary files /dev/null and b/client/test-death-noise.mp3 differ diff --git a/server/main.go b/server/main.go index cd2526b..8c61d88 100644 --- a/server/main.go +++ b/server/main.go @@ -35,7 +35,7 @@ const ( STAGE_WIDTH float64 = 90.0 STAGE_HEIGHT float64 = 160.0 BULLET_KILL_BUFFER_WIDTH float64 = 16.0 - PLAYER_COL_RADIUS_MULTIPLIER float64 = 0.04 + PLAYER_COL_RADIUS_MULTIPLIER float64 = 0.01 PLAYER_DEATH_TIMER_MAX int = 180 ) @@ -48,6 +48,7 @@ type PlayerStageState struct { updatePlayerPos bool health int graze int + deathTimer int } type PlayerUpdate struct { @@ -118,6 +119,7 @@ func (m *BattleRoyaleMatch) MatchJoin(ctx context.Context, logger runtime.Logger bullets: []*C.Bullet{}, updatePlayerPos: true, health: 3, + deathTimer: -1, }, } } @@ -232,21 +234,30 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger return false }) - // Check if the player collided with a bullet and kill them if so - deathTimer := 0 - if slices.ContainsFunc(v.stageState.bullets, func(b *C.Bullet) bool { - return bool(C.bullet_collides_with(b, C.int64_t(tick), v.stageState.col)) - }) { - deathTimer = PLAYER_DEATH_TIMER_MAX + // If the player is dead. Decrement the death timer + if v.stageState.deathTimer >= 0 { + v.stageState.deathTimer -= 1 + } + + if v.stageState.deathTimer == 0 { // If the player's death timer has run out, reset them. 0 is a special deathTimer tick that indicates reset to the clients. + v.stageState.col.x = C.double(STAGE_WIDTH * 0.5) + v.stageState.col.y = C.double(STAGE_HEIGHT - STAGE_HEIGHT*0.1) + v.stageState.updatePlayerPos = true + } else { // If the player is alive, check if the player collided with a bullet and kill them if so + if slices.ContainsFunc(v.stageState.bullets, func(b *C.Bullet) bool { + return bool(C.bullet_collides_with(b, C.int64_t(tick), v.stageState.col)) + }) { + v.stageState.deathTimer = PLAYER_DEATH_TIMER_MAX + } } var newBulletsToBroadcast = []map[string]interface{}{} - // Test bullet spawning - if tick%10 == 0 { + // Test bullet spawning, only when player is alive + if tick%10 == 0 && v.stageState.deathTimer == -1 { velx := (rand.Float64() * STAGE_WIDTH) / float64(lobbyState.tickRate) vely := (rand.Float64() * STAGE_WIDTH) / float64(lobbyState.tickRate) - radius_multiplier := 0.03 + rand.Float64()*(0.1-0.03) + radius_multiplier := 0.01 + rand.Float64()*(0.1-0.01) vel_x_sign := 2*rand.Intn(2) - 1 vel_y_sign := 2*rand.Intn(2) - 1 @@ -287,7 +298,7 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger }, NewBullets: newBulletsToBroadcast, ForcePlayerPos: v.stageState.updatePlayerPos, - DeathTimer: deathTimer, + DeathTimer: v.stageState.deathTimer, } v.stageState.updatePlayerPos = false