diff --git a/project.godot b/project.godot index 632d37c..1901b1c 100644 --- a/project.godot +++ b/project.godot @@ -65,5 +65,6 @@ escape={ [layer_names] -2d_physics/layer_1="Player" -2d_physics/layer_2="Hitbox" +2d_physics/layer_1="Navigation" +2d_physics/layer_2="AttackHitboxes" +2d_physics/layer_3="Projectiles" diff --git a/source/characters/archer/archer.gd b/source/characters/archer/archer.gd index ee5f651..5c6d259 100644 --- a/source/characters/archer/archer.gd +++ b/source/characters/archer/archer.gd @@ -8,10 +8,9 @@ extends PlayerBody2D @export var _bow_pivot: Node2D @export var _arrow_scene: PackedScene -@export var dash_speed: int = 700 @export var max_dash_frames: int = 5 @export var state = states.IDLE -@export var bow_state = states.IDLE +@export var death_animation_finished = false enum states {IDLE, WALKING, BOW_CHARGING, BOW_RELEASED, DEAD} # Stringed Enum's aren't a thing yet :( @@ -24,11 +23,11 @@ const animations = { BOW_RELEASING = "bow_releasing" } -var death_animation_finished = false -var bow_completely_charged = false -var bow_completely_released = false var arrow_created = false var total_dash_frames = 0 +var bow_reset = false +var bow_completely_charged = false +var bow_completely_released = false func setup( @@ -45,25 +44,27 @@ func _ready() -> void: _death_sprites.hide() _animation_player.animation_finished.connect(_on_animation_finished) + player_body_area.player_area_hit.connect(_on_player_area_hit) func _physics_process(_delta) -> void: match state: states.IDLE, states.WALKING: if input_synchronizer.attack: + _reset_bow() state = states.BOW_CHARGING - bow_completely_charged = false - bow_completely_released = false - arrow_created = false _charge_bow() elif is_move_action_pressed(): state = states.WALKING _walk() else: - state = states.IDLE _idle() states.BOW_CHARGING: + if not bow_reset: + _reset_bow() + bow_reset = true + if input_synchronizer.attack: _charge_bow() elif bow_completely_charged: @@ -90,7 +91,14 @@ func _physics_process(_delta) -> void: _die() +func _reset_bow(): + bow_completely_charged = false + bow_completely_released = false + arrow_created = false + + func _idle() -> void: + _show_archer_alive_sprites(true) _show_archer_separated_sprites(false) _animation_player.play(animations.IDLE) @@ -116,11 +124,11 @@ func _release_bow() -> void: ) arrow_created = true - if not bow_completely_released: - _animation_player.play(animations.BOW_RELEASING) + _animation_player.play(animations.BOW_RELEASING) func _aim() -> void: + _show_archer_alive_sprites(true) _show_archer_separated_sprites(true) var direction = get_direction_to_mouse() @@ -137,26 +145,28 @@ func _walk() -> void: func _move(speed: int) -> void: + _show_archer_alive_sprites(true) _show_archer_separated_sprites(false) _animation_player.play(animations.WALK) move_player_body(speed, set_scale_normal) func _die(): - _archer_separated_sprites.hide() - _archer_sprites.hide() - _death_sprites.show() + _show_archer_alive_sprites(false) _animation_player.play(animations.DIE) if death_animation_finished: player_body_dead.emit() - _death_sprites.hide() -func die(): +func _set_player_state_to_dead(): state = states.DEAD +func _on_player_area_hit(): + _set_player_state_to_dead() + + func _on_animation_finished(animation_name: String) -> void: match animation_name: animations.DIE: @@ -165,6 +175,7 @@ func _on_animation_finished(animation_name: String) -> void: bow_completely_charged = true animations.BOW_RELEASING: bow_completely_released = true + bow_reset = false func set_scale_normal(is_normal=true) -> void: @@ -174,10 +185,27 @@ func set_scale_normal(is_normal=true) -> void: _sprites.scale.x = SCALE_REVERSED +func reset_player_body(spawn_position): + state = states.IDLE + position = spawn_position + death_animation_finished = false + + func _show_archer_separated_sprites(is_showing := true): if is_showing: _archer_separated_sprites.show() _archer_sprites.hide() else: _archer_separated_sprites.hide() - _archer_sprites.show() \ No newline at end of file + _archer_sprites.show() + + +func _show_archer_alive_sprites(alive := true): + if alive: + _death_sprites.hide() + _archer_separated_sprites.show() + _archer_sprites.show() + else: + _death_sprites.show() + _archer_separated_sprites.hide() + _archer_sprites.hide() \ No newline at end of file diff --git a/source/characters/archer/archer.tscn b/source/characters/archer/archer.tscn index add05e1..c004e8e 100644 --- a/source/characters/archer/archer.tscn +++ b/source/characters/archer/archer.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=17 format=3 uid="uid://g3us0p5v2s8h"] +[gd_scene load_steps=20 format=3 uid="uid://g3us0p5v2s8h"] [ext_resource type="Script" path="res://source/characters/archer/archer.gd" id="1_dij1d"] [ext_resource type="PackedScene" uid="uid://dgjw21g2hxlcg" path="res://source/characters/archer/arrow.tscn" id="2_jugn6"] @@ -6,6 +6,8 @@ [ext_resource type="Texture2D" uid="uid://bhf7ryc2ms8ps" path="res://assets/Factions/Knights/Troops/Archer/Archer + Bow/Archer_Blue_(NoArms).png" id="3_my82u"] [ext_resource type="Texture2D" uid="uid://vubkc3emgblt" path="res://assets/Factions/Knights/Troops/Dead/Dead.png" id="4_eh6nl"] [ext_resource type="Texture2D" uid="uid://d3dvcng3l04v" path="res://assets/Factions/Knights/Troops/Archer/Blue/Archer_Blue.png" id="6_2wgfu"] +[ext_resource type="Script" path="res://source/characters/player_body/player_area.gd" id="7_3pks7"] +[ext_resource type="Script" path="res://source/characters/player_body/hitboxes.gd" id="7_ltp0r"] [sub_resource type="Animation" id="Animation_bh0aq"] length = 0.001 @@ -34,108 +36,106 @@ tracks/1/keys = { "values": [8] } -[sub_resource type="Animation" id="Animation_548xe"] -resource_name = "die" -length = 1.40001 -loop_mode = 1 +[sub_resource type="Animation" id="Animation_ampfp"] +resource_name = "bow_charged" +length = 0.10001 step = 0.1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("Sprites/DeathSprites:frame") +tracks/0/path = NodePath("Sprites/ArcherSeparatedSprites/BowPivot/BowSprites:frame") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { -"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3), -"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), -"update": 0, -"values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [13] } -[sub_resource type="Animation" id="Animation_136w2"] -resource_name = "idle" -length = 0.60001 -loop_mode = 1 +[sub_resource type="Animation" id="Animation_t2v8g"] +resource_name = "bow_charging" +length = 0.50001 step = 0.1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("Sprites/ArcherSprites:frame") +tracks/0/path = NodePath("Sprites/ArcherSeparatedSprites/BowPivot/BowSprites:frame") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { -"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5), -"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), +"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1), "update": 1, -"values": [0, 1, 2, 3, 4, 5] +"values": [8, 9, 10, 11, 12] } -[sub_resource type="Animation" id="Animation_aiwkm"] -resource_name = "walk" -length = 0.60001 +[sub_resource type="Animation" id="Animation_alsbo"] +resource_name = "bow_releasing" +length = 0.20001 step = 0.1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("Sprites/ArcherSprites:frame") +tracks/0/path = NodePath("Sprites/ArcherSeparatedSprites/BowPivot/BowSprites:frame") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { -"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5), -"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), -"update": 0, -"values": [8, 9, 10, 11, 12, 13] +"times": PackedFloat32Array(0, 0.1), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [14, 15] } -[sub_resource type="Animation" id="Animation_t2v8g"] -resource_name = "bow_charging" -length = 0.60001 +[sub_resource type="Animation" id="Animation_548xe"] +resource_name = "die" +length = 1.40001 step = 0.1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("Sprites/ArcherSeparatedSprites/BowPivot/BowSprites:frame") +tracks/0/path = NodePath("Sprites/DeathSprites:frame") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { -"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5), -"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), -"update": 1, -"values": [8, 9, 10, 11, 12, 13] +"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] } -[sub_resource type="Animation" id="Animation_ampfp"] -resource_name = "bow_charged" -length = 0.10001 +[sub_resource type="Animation" id="Animation_136w2"] +resource_name = "idle" +length = 0.60001 step = 0.1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("Sprites/ArcherSeparatedSprites/BowPivot/BowSprites:frame") +tracks/0/path = NodePath("Sprites/ArcherSprites:frame") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), +"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), "update": 1, -"values": [13] +"values": [0, 1, 2, 3, 4, 5] } -[sub_resource type="Animation" id="Animation_alsbo"] -resource_name = "bow_releasing" -length = 0.20001 +[sub_resource type="Animation" id="Animation_aiwkm"] +resource_name = "walk" +length = 0.60001 step = 0.1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("Sprites/ArcherSeparatedSprites/BowPivot/BowSprites:frame") +tracks/0/path = NodePath("Sprites/ArcherSprites:frame") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { -"times": PackedFloat32Array(0, 0.1), -"transitions": PackedFloat32Array(1, 1), -"update": 1, -"values": [14, 15] +"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), +"update": 0, +"values": [8, 9, 10, 11, 12, 13] } [sub_resource type="AnimationLibrary" id="AnimationLibrary_4rae1"] @@ -149,13 +149,17 @@ _data = { "walk": SubResource("Animation_aiwkm") } +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_6mx4l"] +radius = 24.0 +height = 63.9999 + [sub_resource type="RectangleShape2D" id="RectangleShape2D_ra4dr"] size = Vector2(40, 16) [sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_h8tdm"] properties/0/path = NodePath(".:position") properties/0/spawn = true -properties/0/replication_mode = 2 +properties/0/replication_mode = 1 properties/1/path = NodePath("Sprites:scale") properties/1/spawn = true properties/1/replication_mode = 0 @@ -169,7 +173,7 @@ properties/4/path = NodePath(".:state") properties/4/spawn = true properties/4/replication_mode = 2 -[node name="Archer" type="CharacterBody2D" node_paths=PackedStringArray("_animation_player", "_sprites", "_archer_separated_sprites", "_archer_sprites", "_death_sprites", "_bow_pivot")] +[node name="Archer" type="CharacterBody2D" node_paths=PackedStringArray("_animation_player", "_sprites", "_archer_separated_sprites", "_archer_sprites", "_death_sprites", "_bow_pivot", "hitboxes_container", "player_body_area")] y_sort_enabled = true rotation = 0.00152653 collision_layer = 7 @@ -181,6 +185,8 @@ _archer_sprites = NodePath("Sprites/ArcherSprites") _death_sprites = NodePath("Sprites/DeathSprites") _bow_pivot = NodePath("Sprites/ArcherSeparatedSprites/BowPivot") _arrow_scene = ExtResource("2_jugn6") +hitboxes_container = NodePath("Hitboxes") +player_body_area = NodePath("Hitboxes/BodyHitbox") [node name="AnimationPlayer" type="AnimationPlayer" parent="."] libraries = { @@ -190,6 +196,7 @@ libraries = { [node name="Sprites" type="Node2D" parent="."] [node name="ArcherSprites" type="Sprite2D" parent="Sprites"] +visible = false texture = ExtResource("6_2wgfu") hframes = 8 vframes = 7 @@ -201,7 +208,6 @@ hframes = 7 vframes = 2 [node name="ArcherSeparatedSprites" type="Node2D" parent="Sprites"] -visible = false [node name="ArcherBodySprites" type="Sprite2D" parent="Sprites/ArcherSeparatedSprites"] rotation = -0.00152653 @@ -219,6 +225,20 @@ hframes = 8 vframes = 2 frame = 8 +[node name="Hitboxes" type="Node2D" parent="." node_paths=PackedStringArray("hitboxes")] +scale = Vector2(1, 1) +script = ExtResource("7_ltp0r") +hitboxes = [NodePath("BodyHitbox")] + +[node name="BodyHitbox" type="Area2D" parent="Hitboxes"] +collision_layer = 6 +collision_mask = 0 +script = ExtResource("7_3pks7") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Hitboxes/BodyHitbox"] +position = Vector2(0.0122122, 7.99999) +shape = SubResource("CapsuleShape2D_6mx4l") + [node name="BodyHitbox" type="CollisionShape2D" parent="."] position = Vector2(0, 32) shape = SubResource("RectangleShape2D_ra4dr") diff --git a/source/characters/archer/arrow.gd b/source/characters/archer/arrow.gd index 2369f3c..b9a0966 100644 --- a/source/characters/archer/arrow.gd +++ b/source/characters/archer/arrow.gd @@ -1,9 +1,10 @@ extends Area2D + @export var arrow_speed = 1400 +@export var velocity: Vector2 var direction: Vector2 -var velocity: Vector2 var target_position: Vector2 var spawn_position: Vector2 @@ -12,8 +13,8 @@ func setup(spawn_position_, target_position_): self.target_position = target_position_ self.spawn_position = spawn_position_ self.position = self.spawn_position + self.rotation = get_angle_to(target_position) - look_at(target_position) direction = spawn_position.direction_to(target_position) direction = direction.normalized() velocity = arrow_speed * direction diff --git a/source/characters/archer/arrow.tscn b/source/characters/archer/arrow.tscn index 653c4a1..a1b43f6 100644 --- a/source/characters/archer/arrow.tscn +++ b/source/characters/archer/arrow.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=4 format=3 uid="uid://dgjw21g2hxlcg"] +[gd_scene load_steps=5 format=3 uid="uid://dgjw21g2hxlcg"] [ext_resource type="Script" path="res://source/characters/archer/arrow.gd" id="1_fur70"] [ext_resource type="Texture2D" uid="uid://be17uy52srj8g" path="res://assets/Factions/Knights/Troops/Archer/Arrow/Arrow.png" id="2_iussc"] @@ -7,8 +7,21 @@ radius = 9.0 height = 64.0 +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_dw3fw"] +properties/0/path = NodePath(".:velocity") +properties/0/spawn = true +properties/0/replication_mode = 1 +properties/1/path = NodePath(".:position") +properties/1/spawn = true +properties/1/replication_mode = 1 +properties/2/path = NodePath(".:rotation") +properties/2/spawn = true +properties/2/replication_mode = 1 + [node name="Arrow" type="Area2D"] z_index = 1 +collision_layer = 0 +collision_mask = 4 script = ExtResource("1_fur70") [node name="ArrowSprite" type="Sprite2D" parent="."] @@ -19,3 +32,6 @@ vframes = 2 [node name="CollisionShape2D" type="CollisionShape2D" parent="."] rotation = 1.5708 shape = SubResource("CapsuleShape2D_qqrxr") + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] +replication_config = SubResource("SceneReplicationConfig_dw3fw") diff --git a/source/characters/player_body/player_area.gd b/source/characters/player_body/player_area.gd new file mode 100644 index 0000000..fdc5af3 --- /dev/null +++ b/source/characters/player_body/player_area.gd @@ -0,0 +1,7 @@ +extends Area2D + +signal player_area_hit + + +func hit(): + player_area_hit.emit() \ No newline at end of file diff --git a/source/characters/player_body/player_body_2d.gd b/source/characters/player_body/player_body_2d.gd index c55b9ae..4f2855d 100644 --- a/source/characters/player_body/player_body_2d.gd +++ b/source/characters/player_body/player_body_2d.gd @@ -4,15 +4,17 @@ class_name PlayerBody2D signal player_moved() signal player_body_dead -signal body_entered(body: Node2D, hitbox_name: StringName) -signal body_exited(body: Node2D, hitbox_name: StringName) -signal player_bodies_hit(hitbox_name: StringName) +signal player_area_entered(body: Node2D, hitbox_name: StringName) +signal player_area_exited(body: Node2D, hitbox_name: StringName) +signal player_areas_hit(hitbox_name: StringName) signal create_projectile(projectile: Node2D, spawn_position: Vector2, target_position: Vector2) @export var walk_speed: int = 150 -@export var input_synchronizer: MultiplayerSynchronizer -@export var hitboxes_container: Node2D = null +@export var hitboxes_container: Node2D +@export var player_body_area: Area2D + +var input_synchronizer: MultiplayerSynchronizer const SCALE_NORMAL := 1 const SCALE_REVERSED := -1 @@ -27,8 +29,8 @@ func _ready() -> void: if hitboxes_container: for hitbox in hitboxes_container.hitboxes: - hitbox.body_entered.connect(_on_body_entered.bind(hitbox.name)) - hitbox.body_exited.connect(_on_body_exited.bind(hitbox.name)) + hitbox.area_entered.connect(_on_player_area_entered.bind(hitbox.name)) + hitbox.area_exited.connect(_on_player_area_exited.bind(hitbox.name)) func move_player_body(speed: int, set_scale_normal: Callable) -> void: @@ -62,13 +64,13 @@ func get_direction_to_mouse(): return position.direction_to(mouse_position) -func _on_body_entered(body: Node2D, hitbox_name: StringName) -> void: - body_entered.emit(body, hitbox_name) +func _on_player_area_entered(area: Area2D, hitbox_name: String) -> void: + player_area_entered.emit(area, hitbox_name) -func _on_body_exited(body: Node2D, hitbox_name: StringName) -> void: - body_exited.emit(body, hitbox_name) +func _on_player_area_exited(area: Node2D, hitbox_name: StringName) -> void: + player_area_exited.emit(area, hitbox_name) -func _on_player_bodies_hit(hitbox_name: StringName) -> void: - player_bodies_hit.emit(hitbox_name) \ No newline at end of file +func _on_player_areas_hit(hitbox_name: StringName) -> void: + player_areas_hit.emit(hitbox_name) \ No newline at end of file diff --git a/source/characters/warrior/warrior.gd b/source/characters/warrior/warrior.gd index b83de29..a4f282d 100644 --- a/source/characters/warrior/warrior.gd +++ b/source/characters/warrior/warrior.gd @@ -8,6 +8,7 @@ extends PlayerBody2D @export var dash_speed: int = 700 @export var max_dash_frames: int = 5 @export var state = states.IDLE +@export var death_animation_finished = false enum states {IDLE, WALKING, ATTACKING, DASHING, DEAD} # Stringed Enum's aren't a thing yet :( @@ -20,7 +21,6 @@ const animations = { DIE = "die", } -var death_animation_finished = false var total_dash_frames = 0 @@ -37,8 +37,9 @@ func _ready() -> void: _death_sprites.hide() - _animation_player.animation_attack.connect(_on_player_bodies_hit) + _animation_player.animation_attack.connect(_on_player_areas_hit) _animation_player.animation_finished.connect(_on_animation_finished) + player_body_area.player_area_hit.connect(_on_player_area_hit) func _physics_process(_delta) -> void: @@ -95,10 +96,12 @@ func _physics_process(_delta) -> void: func _idle() -> void: + _show_warrior_alive_sprites(true) _animation_player.play(animations.IDLE) func _attack() -> void: + _show_warrior_alive_sprites(true) var direction = get_direction_to_mouse() if abs(direction.x) > abs(direction.y): @@ -125,24 +128,27 @@ func _walk() -> void: func _move(speed: int) -> void: + _show_warrior_alive_sprites(true) _animation_player.play(animations.WALK) move_player_body(speed, set_scale_normal) func _die(): - _warrior_sprites.hide() - _death_sprites.show() + _show_warrior_alive_sprites(false) _animation_player.play(animations.DIE) if death_animation_finished: player_body_dead.emit() - _death_sprites.hide() -func die(): +func _set_player_state_to_dead(): state = states.DEAD +func _on_player_area_hit(): + _set_player_state_to_dead() + + func _on_animation_finished(animation_name: String) -> void: match animation_name: animations.DIE: @@ -155,4 +161,19 @@ func set_scale_normal(is_normal=true) -> void: hitboxes_container.scale.x = SCALE_NORMAL else: _sprites.scale.x = SCALE_REVERSED - hitboxes_container.scale.x = SCALE_REVERSED \ No newline at end of file + hitboxes_container.scale.x = SCALE_REVERSED + + +func reset_player_body(spawn_position): + state = states.IDLE + position = spawn_position + death_animation_finished = false + + +func _show_warrior_alive_sprites(alive := true): + if alive: + _death_sprites.hide() + _warrior_sprites.show() + else: + _warrior_sprites.hide() + _death_sprites.show() \ No newline at end of file diff --git a/source/characters/warrior/warrior.tscn b/source/characters/warrior/warrior.tscn index 50a0272..9b2abc9 100644 --- a/source/characters/warrior/warrior.tscn +++ b/source/characters/warrior/warrior.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=16 format=3 uid="uid://cwowgatou4rs2"] +[gd_scene load_steps=18 format=3 uid="uid://cwowgatou4rs2"] [ext_resource type="Script" path="res://source/characters/warrior/warrior.gd" id="1_dr5k8"] [ext_resource type="Script" path="res://source/characters/player_body/animation_player.gd" id="2_pf2e4"] [ext_resource type="Texture2D" uid="uid://b471ilnxuytxp" path="res://assets/Factions/Knights/Troops/Warrior/Blue/Warrior_Blue.png" id="2_uhed5"] [ext_resource type="Texture2D" uid="uid://vubkc3emgblt" path="res://assets/Factions/Knights/Troops/Dead/Dead.png" id="4_eh6nl"] [ext_resource type="Script" path="res://source/characters/player_body/hitboxes.gd" id="4_qw0i4"] +[ext_resource type="Script" path="res://source/characters/player_body/player_area.gd" id="6_x51bo"] [sub_resource type="Animation" id="Animation_1e117"] length = 0.001 @@ -218,6 +219,10 @@ _data = { [sub_resource type="RectangleShape2D" id="RectangleShape2D_ra4dr"] size = Vector2(40, 16) +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_gtgik"] +radius = 21.0046 +height = 67.9999 + [sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_h8tdm"] properties/0/path = NodePath(".:position") properties/0/spawn = true @@ -235,16 +240,16 @@ properties/4/path = NodePath(".:state") properties/4/spawn = true properties/4/replication_mode = 2 -[node name="Warrior" type="CharacterBody2D" node_paths=PackedStringArray("_animation_player", "_sprites", "_death_sprites", "_warrior_sprites", "hitboxes_container")] +[node name="Warrior" type="CharacterBody2D" node_paths=PackedStringArray("_animation_player", "_sprites", "_death_sprites", "_warrior_sprites", "hitboxes_container", "player_body_area")] y_sort_enabled = true rotation = 0.00152653 -collision_layer = 7 script = ExtResource("1_dr5k8") _animation_player = NodePath("AnimationPlayer") _sprites = NodePath("Sprites") _death_sprites = NodePath("Sprites/DeathSprites") _warrior_sprites = NodePath("Sprites/WarriorSprites") hitboxes_container = NodePath("Hitboxes") +player_body_area = NodePath("Hitboxes/BodyHitbox") [node name="AnimationPlayer" type="AnimationPlayer" parent="."] libraries = { @@ -266,7 +271,8 @@ texture = ExtResource("4_eh6nl") hframes = 7 vframes = 2 -[node name="BodyHitbox" type="CollisionShape2D" parent="."] +[node name="NavigationHitbox" type="CollisionShape2D" parent="."] +visible = false position = Vector2(0, 32) shape = SubResource("RectangleShape2D_ra4dr") @@ -276,6 +282,17 @@ position = Vector2(0, 32) script = ExtResource("4_qw0i4") hitboxes = [NodePath("DownHitbox1"), NodePath("DownHitbox2"), NodePath("ForwardHitbox1"), NodePath("ForwardHitbox2"), NodePath("UpHitbox1"), NodePath("UpHitbox2")] +[node name="BodyHitbox" type="Area2D" parent="Hitboxes"] +scale = Vector2(1, 1) +collision_layer = 6 +collision_mask = 0 +script = ExtResource("6_x51bo") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Hitboxes/BodyHitbox"] +position = Vector2(0.00915917, -26) +scale = Vector2(1, 1) +shape = SubResource("CapsuleShape2D_gtgik") + [node name="DownHitbox1" type="Area2D" parent="Hitboxes"] position = Vector2(0, -32) collision_layer = 0 @@ -285,6 +302,7 @@ collision_mask = 2 polygon = PackedVector2Array(68.0365, 23.8962, 68.0533, 34.8961, 61.09, 58.9068, 43.119, 77.9343, 26.1267, 82.9602, 5.12212, 79.9923, -6.88245, 77.0106, -32.9068, 61.0503, -50.9358, 42.0778, -55.9496, 33.0854, -16.9694, 20.0259) [node name="DownHitbox2" type="Area2D" parent="Hitboxes"] +visible = false position = Vector2(0, -32) collision_layer = 0 collision_mask = 2 @@ -293,6 +311,7 @@ collision_mask = 2 polygon = PackedVector2Array(5.03663, 23.9923, 90.0182, 11.8626, 88.0243, 15.8656, 80.061, 39.8778, 40.0977, 63.9389, 10.1038, 67.9847, -19.9069, 61.0305, -40.9404, 39.0625, -52.974, 17.0809, -54.9938, 4.08395) [node name="ForwardHitbox1" type="Area2D" parent="Hitboxes"] +visible = false position = Vector2(0, -32) collision_layer = 0 collision_mask = 2 @@ -301,6 +320,7 @@ collision_mask = 2 polygon = PackedVector2Array(15.0443, 28.9771, 77.0808, 52.8824, 87.0243, 15.8672, 86.9846, -10.1328, 69.9251, -49.1068, 47.9007, -65.0732, 24.8916, -71.0381, 18.9542, -30.029, 16, -0.0244245) [node name="ForwardHitbox2" type="Area2D" parent="Hitboxes"] +visible = false position = Vector2(0, -32) collision_layer = 0 collision_mask = 2 @@ -317,6 +337,7 @@ collision_mask = 2 polygon = PackedVector2Array(41.9984, -1.06411, 63.9831, -11.0977, 72.9663, -22.1114, 42.919, -53.0656, 19.9038, -63.0304, -6.10379, -67.9907, -22.1007, -65.9663, -46.0778, -50.9297, -61.0411, -26.9068, -69.0045, -2.89466, 0.0351102, 23) [node name="UpHitbox2" type="Area2D" parent="Hitboxes"] +visible = false position = Vector2(0, -32) collision_layer = 0 collision_mask = 2 diff --git a/source/levels/level_arena/level_arena.gd b/source/levels/level_arena/level_arena.gd index f10b6e5..7433d69 100644 --- a/source/levels/level_arena/level_arena.gd +++ b/source/levels/level_arena/level_arena.gd @@ -9,9 +9,9 @@ func _ready() -> void: MultiplayerLobby.player_connected.connect(_on_player_connected) -func _on_player_bodies_hit_from_player(_attacker: String, player_bodies: Array[PlayerBody2D]) -> void: - for player_body in player_bodies: - player_body.die() +func _on_player_areas_hit_from_player(_attacker: String, player_areas: Array[Area2D]) -> void: + for player_area in player_areas: + player_area.hit() func create_player(peer_id: int): @@ -20,7 +20,7 @@ func create_player(peer_id: int): peer_id, _player_spawn_point.position, ) - player.player_bodies_hit_from_player.connect(_on_player_bodies_hit_from_player) + player.player_areas_hit_from_player.connect(_on_player_areas_hit_from_player) _players.add_child(player, true) diff --git a/source/player/player.gd b/source/player/player.gd index 14e8eb1..bae36b2 100644 --- a/source/player/player.gd +++ b/source/player/player.gd @@ -1,6 +1,6 @@ extends Node2D -signal player_bodies_hit_from_player(attacker: String, player_bodies: Array[Node2D]) +signal player_areas_hit_from_player(attacker: String, player_areas: Array[Area2D]) @export var _warrior_scene: PackedScene @export var _archer_scene: PackedScene @@ -9,6 +9,7 @@ signal player_bodies_hit_from_player(attacker: String, player_bodies: Array[Node @export var _projectiles_container: Node2D @export var _input_synchronizer: MultiplayerSynchronizer @export var player_name: String +@export var player_body_spawn_position: Vector2 @export var player_id: int: set(new_player_id): player_id = new_player_id @@ -20,10 +21,9 @@ enum characters { } var character := characters.ARCHER var player_body: PlayerBody2D = null -var player_body_spawn_position: Vector2 var characters_scenes: Array[PackedScene] -# Dict[StringName, Array[Node2D]] -var hitboxes_with_hittable_player_bodies = {} +# Dict[StringName, Array[Area2D]] +var hitboxes_with_hittable_player_areas = {} func setup( @@ -68,50 +68,67 @@ func _spawn_player_body(): player_body_spawn_position ) - player_body.player_bodies_hit.connect(_on_player_bodies_hit) - player_body.player_body_dead.connect(_on_player_body_dead) - player_body.create_projectile.connect(_on_create_projectile) - player_body.body_entered.connect(_on_body_entered) - player_body.body_exited.connect(_on_body_exited) + # Prevent puppets from having a signal handler. We want the host to run most logic. + if is_multiplayer_authority(): + player_body.player_areas_hit.connect(_on_player_areas_hit) + player_body.player_body_dead.connect(_on_player_body_dead) + player_body.create_projectile.connect(_on_create_projectile) + player_body.player_area_entered.connect(_on_player_area_entered) + player_body.player_area_exited.connect(_on_player_area_exited) add_child(player_body) +func _respawn_player_body(): + player_body.hide() + player_body.reset_player_body(player_body_spawn_position) + player_body.show() + + func _on_player_body_dead(): - _spawn_player_body() + _respawn_player_body() -func _on_body_entered(body: Node2D, hitbox_name: String) -> void: - if body == player_body: +func _on_player_area_entered(area: Area2D, hitbox_name: String) -> void: + if area == player_body.player_body_area: return - if not hitboxes_with_hittable_player_bodies.has(hitbox_name): + if not hitboxes_with_hittable_player_areas.has(hitbox_name): # Dicts are untyped, and type inference doesn't work if we don't # tell GDScript what type this array is meant to contain. # This can break functions expecting parameters of certain types. - var empty_array: Array[PlayerBody2D] = [] - hitboxes_with_hittable_player_bodies[hitbox_name] = empty_array + var empty_array: Array[Area2D] = [] + hitboxes_with_hittable_player_areas[hitbox_name] = empty_array - hitboxes_with_hittable_player_bodies[hitbox_name].append(body) + hitboxes_with_hittable_player_areas[hitbox_name].append(area) -func _on_body_exited(body: Node2D, hitbox_name: String) -> void: - if not hitboxes_with_hittable_player_bodies.has(hitbox_name): - var empty_array: Array[PlayerBody2D] = [] - hitboxes_with_hittable_player_bodies[hitbox_name] = empty_array +func _on_player_area_exited(area: Area2D, hitbox_name: String) -> void: + if not hitboxes_with_hittable_player_areas.has(hitbox_name): + var empty_array: Array[Area2D] = [] + hitboxes_with_hittable_player_areas[hitbox_name] = empty_array - hitboxes_with_hittable_player_bodies[hitbox_name].erase(body) + hitboxes_with_hittable_player_areas[hitbox_name].erase(area) -func _on_player_bodies_hit(hitbox_name: String) -> void: - if hitboxes_with_hittable_player_bodies.has(hitbox_name): - player_bodies_hit_from_player.emit(player_name, hitboxes_with_hittable_player_bodies[hitbox_name]) +func _on_player_areas_hit(hitbox_name: String) -> void: + if hitboxes_with_hittable_player_areas.has(hitbox_name): + player_areas_hit_from_player.emit(player_name, hitboxes_with_hittable_player_areas[hitbox_name]) func _on_create_projectile(projectile: Node2D, spawn_position: Vector2, target_position: Vector2): - _projectiles_container.add_child(projectile) - print("Arrow") + _projectiles_container.add_child(projectile, true) projectile.setup( spawn_position, target_position - ) \ No newline at end of file + ) + projectile.area_entered.connect(_on_projectile_area_entered) + + +func _on_projectile_area_entered(area: Area2D): + if area == player_body.player_body_area: + return + + var areas: Array[Area2D] = [] + areas.append(area) + player_areas_hit_from_player.emit(player_name, areas) \ No newline at end of file diff --git a/source/player/player.tscn b/source/player/player.tscn index b40522b..66bde23 100644 --- a/source/player/player.tscn +++ b/source/player/player.tscn @@ -12,6 +12,9 @@ properties/0/replication_mode = 2 properties/1/path = NodePath(".:y_sort_enabled") properties/1/spawn = true properties/1/replication_mode = 0 +properties/2/path = NodePath(".:player_body_spawn_position") +properties/2/spawn = true +properties/2/replication_mode = 1 [sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_w3k14"] properties/0/path = NodePath("InputSynchronizer:attack") @@ -63,3 +66,8 @@ _mouse_position_finder = NodePath("MousePositionFinder") [node name="Camera2D" type="Camera2D" parent="."] position_smoothing_enabled = true + +[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."] +_spawnable_scenes = PackedStringArray("res://source/characters/archer/arrow.tscn") +spawn_path = NodePath("../ProjectilesContainer") +spawn_limit = 100