diff --git a/components/explosive_damage_component.gd b/components/explosive_damage_component.gd index 1d26827..99433ad 100644 --- a/components/explosive_damage_component.gd +++ b/components/explosive_damage_component.gd @@ -27,6 +27,6 @@ func _physics_process(_delta : float) -> void: for area in get_overlapping_areas(): if area is HealthComponent and is_multiplayer_authority(): - area.damage.rpc(damage, damage_dealer.player_id) + (area as HealthComponent).damage.rpc(damage, damage_dealer.player_id, damage_dealer.team_id) set_physics_process(false) diff --git a/components/flag_carry_component.gd b/components/flag_carry_component.gd index 0c9d478..d9f6e49 100644 --- a/components/flag_carry_component.gd +++ b/components/flag_carry_component.gd @@ -38,9 +38,9 @@ func _grab(flag : Flag) -> void: _carried_flag = flag show() -func _release(inherited_velocity : Vector3, throw_speed : float) -> void: +func _release(inherited_velocity : Vector3, throw_speed : float, dropper : Player) -> void: if _is_carrying(): - _carried_flag.drop() + _carried_flag.drop(dropper) _carried_flag.rotation_degrees.x = 0.0 _carried_flag.linear_velocity = inherited_velocity + (global_basis.z * throw_speed) # Throw the flag from some distance in front of the player to avoid regrabbing mid-air @@ -55,8 +55,8 @@ func _sensor_on_body_entered(collider : Flag) -> void: if collider is Flag: _grab(collider) -func drop() -> void: - _release(Vector3.ZERO, 0.0) +func drop(dropper : Player) -> void: + _release(Vector3.ZERO, 0.0, dropper) -func throw(inherited_velocity : Vector3) -> void: - _release(inherited_velocity, max_throw_speed) +func throw(inherited_velocity : Vector3, dropper : Player) -> void: + _release(inherited_velocity, max_throw_speed, dropper) diff --git a/components/health_component.gd b/components/health_component.gd index e6d047e..750b51a 100644 --- a/components/health_component.gd +++ b/components/health_component.gd @@ -19,6 +19,7 @@ class_name HealthComponent extends Area3D set(value): health = value health_changed.emit(value) +@export var _player : Player signal health_zeroed(killer_id : int) signal health_changed(value : float) @@ -26,13 +27,16 @@ signal health_changed(value : float) func _ready() -> void: heal_full() -@rpc("call_local") -func damage(amount : float, damage_dealer_id : int) -> void: +@rpc("call_local", "reliable") +func damage(amount : float, damage_dealer_id : int, damage_dealer_team_id : int) -> void: + if damage_dealer_team_id == _player.team_id: + return + health = clampf(health - amount, 0.0, max_health) if health == 0.0: health_zeroed.emit(damage_dealer_id) -@rpc("call_local") +@rpc("call_local", "reliable") func _heal(amount : float) -> void: health = clampf(health + amount, 0.0, max_health) diff --git a/entities/flag/flag.gd b/entities/flag/flag.gd index 4f86225..b6767f6 100644 --- a/entities/flag/flag.gd +++ b/entities/flag/flag.gd @@ -6,7 +6,7 @@ enum FlagState { FLAG_STATE_ON_STAND, FLAG_STATE_DROPPED, FLAG_STATE_TAKEN } signal grabbed(grabber : Player) signal regrabbed -signal dropped +signal dropped(dropper : Player) var last_carrier : Player = null @onready var _mesh : Node = $Smoothing/Mesh @@ -24,9 +24,9 @@ func grab(grabber : Player) -> void: else: regrabbed.emit() -func drop() -> void: +func drop(dropper : Player) -> void: if flag_state == FlagState.FLAG_STATE_TAKEN: _mesh.show() flag_state = FlagState.FLAG_STATE_DROPPED - dropped.emit() + dropped.emit(dropper) diff --git a/entities/player/player.gd b/entities/player/player.gd index daf49f1..79068bd 100644 --- a/entities/player/player.gd +++ b/entities/player/player.gd @@ -39,6 +39,7 @@ enum PlayerState { PLAYER_ALIVE, PLAYER_DEAD } set(id): player_id = id $PlayerInput.set_multiplayer_authority(id) +@export var team_id : int = 1 @export var nickname : String @onready var input : PlayerInput = $PlayerInput @@ -113,7 +114,7 @@ func _jump() -> void: _jumping = true func _throw_flag() -> void: - flag_carry_component.throw(linear_velocity) + flag_carry_component.throw(linear_velocity, self) func is_on_floor() -> bool: if shape_cast.is_colliding(): @@ -228,12 +229,13 @@ func _is_player_dead() -> bool: return player_state != PlayerState.PLAYER_ALIVE func die(killer_id : int) -> void: - flag_carry_component.drop() + flag_carry_component.drop(self) player_state = PlayerState.PLAYER_DEAD if _is_pawn(): animation_player.play("death") died.emit(self, killer_id) +@rpc("call_local", "reliable") func respawn(location : Vector3) -> void: animation_player.stop() player_state = PlayerState.PLAYER_ALIVE @@ -243,7 +245,7 @@ func respawn(location : Vector3) -> void: func _exit_tree() -> void: player_state = PlayerState.PLAYER_DEAD - flag_carry_component.drop() + flag_carry_component.drop(self) func display_jetpack_particles() -> void: for particle: GPUParticles3D in jetpack_particles: diff --git a/entities/player/player.tscn b/entities/player/player.tscn index 3801be6..f53ba92 100644 --- a/entities/player/player.tscn +++ b/entities/player/player.tscn @@ -77,6 +77,9 @@ properties/4/replication_mode = 2 properties/5/path = NodePath(".:nickname") properties/5/spawn = true properties/5/replication_mode = 2 +properties/6/path = NodePath(".:team_id") +properties/6/spawn = true +properties/6/replication_mode = 2 [sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_5j4ew"] properties/0/path = NodePath(".:direction") @@ -237,7 +240,8 @@ shape = ExtResource("2_vjqny") [node name="HUD" parent="." instance=ExtResource("3_ccety")] -[node name="HealthComponent" parent="." instance=ExtResource("5_t6i6e")] +[node name="HealthComponent" parent="." node_paths=PackedStringArray("_player") instance=ExtResource("5_t6i6e")] +_player = NodePath("..") [node name="CollisionShape3D" type="CollisionShape3D" parent="HealthComponent"] shape = ExtResource("2_vjqny") diff --git a/main.tscn b/main.tscn index 3a640a7..1cdcb8c 100644 --- a/main.tscn +++ b/main.tscn @@ -3,7 +3,7 @@ [ext_resource type="PackedScene" uid="uid://boviiugcnfyrj" path="res://modes/demo.tscn" id="1_50a80"] [ext_resource type="PackedScene" uid="uid://bjctlqvs33nqy" path="res://interfaces/menus/boot/boot.tscn" id="1_acy5o"] [ext_resource type="PackedScene" uid="uid://bvwxfgygm2xb8" path="res://modes/multiplayer.tscn" id="2_g8xeb"] -[ext_resource type="Resource" path="res://maps/maps.tres" id="3_1ipir"] +[ext_resource type="Resource" uid="uid://dut5f1sq0wfeb" path="res://maps/maps.tres" id="3_1ipir"] [sub_resource type="GDScript" id="GDScript_e61dq"] script/source = "class_name Game extends Node3D diff --git a/modes/multiplayer.gd b/modes/multiplayer.gd index e0b8dce..9350cde 100644 --- a/modes/multiplayer.gd +++ b/modes/multiplayer.gd @@ -29,6 +29,9 @@ var _map_manager : Map var _flag : Flag var _flag_carrier_scoring_timer : Timer = Timer.new() +var team_chasers : Team +var team_rabbit : Team + signal connected_to_server signal connection_failed @@ -36,6 +39,10 @@ func _ready() -> void: _flag_carrier_scoring_timer.wait_time = 10.0 _flag_carrier_scoring_timer.timeout.connect(_on_flag_carrier_scoring_timer_timeout) add_child(_flag_carrier_scoring_timer) + team_chasers = Team.new() + team_chasers.team_id = 0 + team_rabbit = Team.new() + team_rabbit.team_id = 1 func start_server(config : Dictionary) -> void: # Check if required parameters are missing @@ -79,12 +86,13 @@ func _on_player_died(player : Player, killer_id : int) -> void: func respawn_player(player : Player) -> void: var spawn_location : Vector3 = _map_manager.get_player_spawn().position - player.respawn(spawn_location) + player.respawn.rpc(spawn_location) func add_player(peer_id : int, nickname : String) -> void: var player : Player = PLAYER.instantiate() player.name = str(peer_id) player.player_id = peer_id + player.team_id = team_chasers.team_id player.nickname = nickname player.global_position = _map_manager.get_player_spawn().position players.add_child(player) @@ -122,10 +130,12 @@ func _join_match(nickname : String) -> void: add_player(multiplayer.get_remote_sender_id(), nickname) func _on_flag_grabbed(grabber : Player) -> void: + grabber.team_id = team_rabbit.team_id scoreboard.add_score_to_player(grabber, 10) _flag_carrier_scoring_timer.start() -func _on_flag_dropped() -> void: +func _on_flag_dropped(dropper : Player) -> void: + dropper.team_id = team_chasers.team_id _flag_carrier_scoring_timer.stop() func _on_flag_carrier_scoring_timer_timeout() -> void: diff --git a/modes/team.gd b/modes/team.gd new file mode 100644 index 0000000..2bcfc84 --- /dev/null +++ b/modes/team.gd @@ -0,0 +1,3 @@ +class_name Team extends Object + +var team_id : int diff --git a/tests/test_health_component.gd b/tests/test_health_component.gd index 2ce83bd..0f89077 100644 --- a/tests/test_health_component.gd +++ b/tests/test_health_component.gd @@ -31,15 +31,15 @@ func test_that_it_has_max_health_when_ready() -> void: func test_that_it_takes_damage() -> void: var damage_amount : float = 10 - _subject.damage(damage_amount, -1) + _subject.damage(damage_amount, -1, 0) assert_eq(_subject.health, TEST_MAX_HEALTH - damage_amount) func test_that_it_emits_health_changed_after_damage() -> void: - _subject.damage(1, -1) + _subject.damage(1, -1, 0) assert_signal_emitted(_subject, 'health_changed') func test_that_it_emits_health_zeroed() -> void: - _subject.damage(TEST_MAX_HEALTH, -1) + _subject.damage(TEST_MAX_HEALTH, -1, 0) assert_signal_emitted_with_parameters(_subject, 'health_zeroed', [-1]) func test_that_it_heals_fully() -> void: