From 0942013ec73b25cc7911d8ddc60eda1ab5e0cbe6 Mon Sep 17 00:00:00 2001 From: SanpoNeko Date: Sun, 8 Mar 2026 18:36:04 +0700 Subject: [PATCH 1/7] Implement and fix Fish enemy --- src/Actor/Enemy/Enemy.gd | 4 +- src/Actor/Enemy/Fish.gd | 201 ++++++++++ src/Actor/Enemy/Fish.tscn | 510 ++++++++++++++++++++++++ src/Actor/Enemy/Outdated/Fish/Fish.gd | 80 ---- src/Actor/Enemy/Outdated/Fish/Fish.tscn | 146 ------- 5 files changed, 714 insertions(+), 227 deletions(-) create mode 100644 src/Actor/Enemy/Fish.gd create mode 100644 src/Actor/Enemy/Fish.tscn delete mode 100644 src/Actor/Enemy/Outdated/Fish/Fish.gd delete mode 100644 src/Actor/Enemy/Outdated/Fish/Fish.tscn diff --git a/src/Actor/Enemy/Enemy.gd b/src/Actor/Enemy/Enemy.gd index 516f413a..d588e571 100644 --- a/src/Actor/Enemy/Enemy.gd +++ b/src/Actor/Enemy/Enemy.gd @@ -177,7 +177,9 @@ func set_damagenum(damage): func die(quietly = false): if dead: return dead = true - f.pc().enemies_touching.erase(self) + + if (f.pc()): + f.pc().enemies_touching.erase(self) if !quietly: am.play(die_sound, self) do_death_routine() diff --git a/src/Actor/Enemy/Fish.gd b/src/Actor/Enemy/Fish.gd new file mode 100644 index 00000000..3d85c87b --- /dev/null +++ b/src/Actor/Enemy/Fish.gd @@ -0,0 +1,201 @@ +extends Enemy + +const PATH_LINE = preload("res://src/Utility/PathLine.tscn") + +var move_dir = Vector2.LEFT +@export var swim_dir_x = -1: set = on_swim_dir_x_changed +@export var jump_height: int = 6: set = on_jump_height_changed + +@onready var start_pos = global_position +var jump_pos: Vector2 = global_position + +@export var can_move_x = true +@export var x_min = -3: set = on_x_min_changed +@export var x_max = 3: set = on_x_max_changed + +@onready var ap: AnimationPlayer = get_node("AnimationPlayer") +@onready var rc: RayCast2D = get_node("RayCast2D") + + +func setup(): + change_state("idle") + if debug: print("ready fish") + speed = Vector2(20, 150) + damage_on_contact = 1 + update_path_lines() + +func on_swim_dir_x_changed(new): + swim_dir_x = new + if (get_node_or_null("Sprite2D")): + $Sprite2D.scale.x = new + move_dir.x = new + +func on_jump_height_changed(new): + if (get_node_or_null("RayCast2D")): + $RayCast2D.target_position.y = -new * 16 + jump_height = new + update_path_lines() + +func on_x_min_changed(new): + x_min = new + update_path_lines() + +func on_x_max_changed(new): + x_max = new + update_path_lines() + +func calc_velocity(move_dir, _do_gravity = true, _do_acceleration = true, _do_friction = true) -> Vector2: + match state: + "idle", "swim": + var out = velocity + + out.x = speed.x * move_dir.x + + return out + "attack": + var out = velocity + + out.y += gravity * get_physics_process_delta_time() + if move_dir.y < 0: + out.y = speed.y * move_dir.y + + return out + "fall": + var out = velocity + + out.y += gravity * get_physics_process_delta_time() + + return out + return Vector2.ZERO + + +#region STATES +#region Idle +func enter_idle(_prev_state: String): + if can_move_x: + change_state("swim") + else: + get_parent().get_parent().get_node("AnimationPlayer").play("Idle") + + +func do_idle(): + velocity = calc_velocity(move_dir) + move_and_slide() + + var collision = get_node("RayCast2D").get_collider() + if collision != null: + if (collision is not TileMapLayer and collision.get_collision_layer_value(1)): + if (abs(collision.global_position.x - global_position.x) <= 1): + print("got target") + change_state("attack") + return +#endregion + +#region Swim +func enter_swim(_prev_state: String, _arg: Dictionary = {}): + move_dir.x = swim_dir_x + ap.play("Idle") + +func exit_swim(_next_state: String, _arg: Dictionary = {}): + move_dir = Vector2.ZERO + velocity = Vector2.ZERO + +func do_swim(): + var collision = rc.get_collider() + if collision != null: + if (collision is not TileMapLayer and collision.get_collision_layer_value(1)): + if (abs(collision.global_position.x - global_position.x) <= 1): #Prevent the fish target just the side of player hitbox + if debug: print("got target") + change_state("attack") + return + + if is_on_wall(): + swim_dir_x = -swim_dir_x + else: + if global_position.x < start_pos.x + x_min * 16: + swim_dir_x = 1 + if global_position.x > start_pos.x + x_max * 16: + swim_dir_x = -1 + + velocity = calc_velocity(move_dir) + move_and_slide() +#endregion + +#region Attack +func enter_attack(_prev_state: String): + jump_pos = global_position + ap.play("Target") + + await ap.animation_finished + move_dir = Vector2.UP + +func do_attack(): + if position.y <= jump_pos.y - jump_height * 16 + speed.y * 0.3 or is_on_ceiling(): + change_state("fall") + return + + velocity = calc_velocity(move_dir) + move_and_slide() +#endregion + +#region Fall +func enter_fall(_prev_state: String): + ap.play("Fall") + +func exit_fall(_next_state: String): + velocity = Vector2.ZERO + +func do_fall(): + if !(global_position.y >= jump_pos.y - 5 or is_on_floor()): + velocity = calc_velocity(move_dir) + move_and_slide() + else: + #var tween = get_tree().create_tween() + #tween.tween_property(main, "position", Vector2(main.position.x, main.start_pos.y), 0.8).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT) + #await tween.finished + var collision: KinematicCollision2D = move_and_collide(jump_pos - global_position) + if (collision): + global_position += collision.get_travel() + else: + global_position.y = jump_pos.y + if (can_move_x): + change_state("swim") + else: + change_state("idle") + return + +#endregion +#endregion + +func update_path_lines(): + #if Engine.editor_hint or debug: + if !(debug or Engine.is_editor_hint()): + return + for c in get_children(): + if c.name == "VPath" or c.name == "HPath": c.free() + + var vline = PATH_LINE.instantiate() + vline.name = "VPath" + vline.default_color = Color.LIGHT_GREEN + if Engine.is_editor_hint(): + vline.add_point(Vector2.ZERO) + vline.add_point(Vector2(0, jump_height * -16)) + add_child(vline) + move_child(vline, 0) + elif debug and world: + vline.add_point(position) + vline.add_point(jump_pos) + world.front.add_child(vline) + + var hline = PATH_LINE.instantiate() + hline.name = "HPath" + hline.default_color = Color.RED + if Engine.is_editor_hint(): + hline.add_point(Vector2(x_min * 16, -4)) + hline.add_point(Vector2(x_max * 16, -4)) + add_child(hline) + move_child(hline, 0) + elif debug and world: + hline.add_point(position + Vector2(x_min * 16, -4)) + hline.add_point(position + Vector2(x_max * 16, -4)) + world.front.add_child(hline) diff --git a/src/Actor/Enemy/Fish.tscn b/src/Actor/Enemy/Fish.tscn new file mode 100644 index 00000000..cabcd188 --- /dev/null +++ b/src/Actor/Enemy/Fish.tscn @@ -0,0 +1,510 @@ +[gd_scene format=3 uid="uid://crtxny1yjnkmi"] + +[ext_resource type="Texture2D" uid="uid://bbu1jyosea4de" path="res://assets/Actor/Enemy/Fish.png" id="1"] +[ext_resource type="Script" uid="uid://ddxy1osfjb432" path="res://src/Actor/Enemy/Fish.gd" id="2"] + +[sub_resource type="RectangleShape2D" id="5"] +size = Vector2(10, 5) + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_vcisb"] +size = Vector2(10, 5) + +[sub_resource type="Animation" id="1"] +resource_name = "Fall" +length = 0.1 +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite2D:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = false +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [12] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CollisionShape2D:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(-0.5, -4)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Hurtbox/CollisionShape2D:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(-0.5, -4)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Hitbox/CollisionShape2D:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(-0.5, -4)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CollisionShape2D:rotation") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [1.5707963267948966] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("Hurtbox/CollisionShape2D:rotation") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [1.5707963267948966] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("Hitbox/CollisionShape2D:rotation") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [1.5707963267948966] +} + +[sub_resource type="Animation" id="2"] +resource_name = "Idle" +length = 0.4 +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite2D:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = false +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.3), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0, 3] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CollisionShape2D:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(0, -3.5)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Hurtbox/CollisionShape2D:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(0, -3.5)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Hitbox/CollisionShape2D:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(0, -3.5)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CollisionShape2D:rotation") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [0.0] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("Hurtbox/CollisionShape2D:rotation") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [0.0] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("Hitbox/CollisionShape2D:rotation") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [0.0] +} + +[sub_resource type="Animation" id="Animation_vcisb"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("CollisionShape2D:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(0, -3.5)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Hurtbox/CollisionShape2D:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(0, -3.5)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Hitbox/CollisionShape2D:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(0, -3.5)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("CollisionShape2D:rotation") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [0.0] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("Hurtbox/CollisionShape2D:rotation") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [0.0] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("Hitbox/CollisionShape2D:rotation") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [0.0] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("Sprite2D:frame") +tracks/6/interp = 1 +tracks/6/loop_wrap = false +tracks/6/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [0] +} + +[sub_resource type="Animation" id="3"] +resource_name = "Rise" +length = 0.1 +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite2D:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = false +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [8] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CollisionShape2D:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(-0.5, -4)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Hurtbox/CollisionShape2D:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(-0.5, -4)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Hitbox/CollisionShape2D:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [Vector2(-0.5, -4)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CollisionShape2D:rotation") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [1.5707963267948966] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("Hurtbox/CollisionShape2D:rotation") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [1.5707963267948966] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("Hitbox/CollisionShape2D:rotation") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [1.5707963267948966] +} + +[sub_resource type="Animation" id="4"] +resource_name = "Target" +length = 0.2 +step = 0.05 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite2D:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = false +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.2), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [4, 7] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CollisionShape2D:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.05, 0.1), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [Vector2(0, -3.5), Vector2(0, -4), Vector2(-0.5, -3.5)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Hurtbox/CollisionShape2D:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.05, 0.10000001), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [Vector2(0, -3.5), Vector2(0, -4), Vector2(-0.5, -3.5)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Hitbox/CollisionShape2D:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 0.05, 0.10000001), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [Vector2(0, -3.5), Vector2(0, -4), Vector2(-0.5, -3.5)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CollisionShape2D:rotation") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0, 0.05, 0.1), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [0.0, 0.7853981633974483, 1.5707963267948966] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("Hurtbox/CollisionShape2D:rotation") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0, 0.05, 0.1), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [0.0, 0.7853982, 1.5707963267948966] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("Hitbox/CollisionShape2D:rotation") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0, 0.05, 0.1), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [0.0, 0.7853982, 1.5707963267948966] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_3s1p6"] +_data = { +&"Fall": SubResource("1"), +&"Idle": SubResource("2"), +&"RESET": SubResource("Animation_vcisb"), +&"Rise": SubResource("3"), +&"Target": SubResource("4") +} + +[node name="Fish" type="CharacterBody2D" unique_id=1312856769 groups=["Actors", "Enemies", "Entities"]] +collision_layer = 2 +collision_mask = 8 +floor_max_angle = 0.0 +script = ExtResource("2") +speed = Vector2(20, 150) +editor_hidden = false + +[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1208749473] +position = Vector2(0, -3.5) +shape = SubResource("5") + +[node name="Hurtbox" type="Area2D" parent="." unique_id=434285087] +collision_layer = 131072 +collision_mask = 0 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Hurtbox" unique_id=569733112] +position = Vector2(0, -3.5) +shape = SubResource("RectangleShape2D_vcisb") +debug_color = Color(0, 1, 0, 0.235294) + +[node name="Hitbox" type="Area2D" parent="." unique_id=430236071] +collision_layer = 262144 +collision_mask = 65536 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Hitbox" unique_id=2082591926] +position = Vector2(0, -3.5) +shape = SubResource("RectangleShape2D_vcisb") +debug_color = Color(1, 0, 0, 0.235294) + +[node name="Sprite2D" type="Sprite2D" parent="." unique_id=971659152] +position = Vector2(0, -4) +scale = Vector2(-1, 1) +texture = ExtResource("1") +hframes = 4 +vframes = 4 + +[node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=335076207] +libraries/ = SubResource("AnimationLibrary_3s1p6") + +[node name="RayCast2D" type="RayCast2D" parent="." unique_id=583405653] +target_position = Vector2(0, -96) +collision_mask = 9 +collide_with_areas = true + +[connection signal="area_entered" from="Hitbox" to="." method="_on_hitbox_area_entered"] +[connection signal="area_exited" from="Hitbox" to="." method="_on_hitbox_area_exited"] diff --git a/src/Actor/Enemy/Outdated/Fish/Fish.gd b/src/Actor/Enemy/Outdated/Fish/Fish.gd deleted file mode 100644 index dfa88cf0..00000000 --- a/src/Actor/Enemy/Outdated/Fish/Fish.gd +++ /dev/null @@ -1,80 +0,0 @@ -@tool -extends Enemy - - -const PATH_LINE = preload("res://src/Utility/PathLine.tscn") - - -var move_dir = Vector2.LEFT -@export var swim_dir_x = -1: set = on_swim_dir_x_changed -@export var jump_height: int = 6: set = on_jump_height_changed -@onready var start_pos = position -var jump_pos - -@export var can_move_x = true -@export var x_min = -3: set = on_x_min_changed -@export var x_max = 3: set = on_x_max_changed - - -func _ready(): - if debug: print("ready fish") - speed = Vector2(20,150) - damage_on_contact = 1 - update_path_lines() - - -func _physics_process(_delta): - if not Engine.is_editor_hint(): - gravity = 300.0 if not is_in_water else 150.0 - - -func on_swim_dir_x_changed(new): - swim_dir_x = new - $Sprite2D.scale.x = new - move_dir.x = new - -func on_jump_height_changed(new): - jump_height = new - jump_pos = Vector2(position.x, position.y + new * -16) - update_path_lines() - -func on_x_min_changed(new): - x_min = new - update_path_lines() - -func on_x_max_changed(new): - x_max = new - update_path_lines() - -func update_path_lines(): - #if Engine.editor_hint or debug: - for c in get_children(): - if c.name == "VPath" or c.name == "HPath": c.free() - - var vline = PATH_LINE.instantiate() - vline.name = "VPath" - vline.default_color = Color.LIGHT_GREEN - if Engine.is_editor_hint(): - vline.add_point(Vector2.ZERO) - vline.add_point(Vector2(0, jump_height * -16)) - add_child(vline) - move_child(vline, 0) - elif debug and world: - vline.add_point(position) - vline.add_point(jump_pos) - world.front.add_child(vline) - - $RayCast2D.target_position = Vector2(0, jump_height * -16) - - var hline = PATH_LINE.instantiate() - hline.name = "HPath" - hline.default_color = Color.RED - if Engine.is_editor_hint(): - hline.add_point(Vector2(x_min * 16,0)) - hline.add_point(Vector2(x_max * 16,0)) - add_child(hline) - move_child(hline, 0) - elif debug and world: - hline.add_point(position + Vector2(x_min * 16,0)) - hline.add_point(position + Vector2(x_max * 16,0)) - world.front.add_child(hline) diff --git a/src/Actor/Enemy/Outdated/Fish/Fish.tscn b/src/Actor/Enemy/Outdated/Fish/Fish.tscn deleted file mode 100644 index 56378554..00000000 --- a/src/Actor/Enemy/Outdated/Fish/Fish.tscn +++ /dev/null @@ -1,146 +0,0 @@ -[gd_scene load_steps=15 format=3 uid="uid://crtxny1yjnkmi"] - -[ext_resource type="Texture2D" uid="uid://bbu1jyosea4de" path="res://assets/Actor/Enemy/Fish.png" id="1"] -[ext_resource type="Script" uid="uid://ddxy1osfjb432" path="res://src/Actor/Enemy/Outdated/Fish/Fish.gd" id="2"] -[ext_resource type="Script" uid="uid://f3j1idg16dr0" path="res://src/Actor/Enemy/Outdated/Fish/Idle.gd" id="3"] -[ext_resource type="Script" uid="uid://5si1ouivktcq" path="res://src/Actor/Enemy/Outdated/Fish/Attack.gd" id="4"] -[ext_resource type="Theme" uid="uid://45e84j2sj7w0" path="res://src/UI/Theme/LaputaTheme.tres" id="5"] -[ext_resource type="Script" uid="uid://cdevxkqwx005a" path="res://src/Actor/Enemy/Outdated/Fish/Fall.gd" id="6"] -[ext_resource type="Script" uid="uid://bat4sn1l7tdkt" path="res://src/Actor/Enemy/Outdated/Fish/Swim.gd" id="7"] -[ext_resource type="PackedScene" uid="uid://qmvnhwivhm4j" path="res://src/Utility/StateMachine.tscn" id="8"] - -[sub_resource type="RectangleShape2D" id="5"] -size = Vector2(4, 4) - -[sub_resource type="Animation" id="1"] -resource_name = "Fall" -length = 0.1 -loop_mode = 1 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Sprite2D:frame") -tracks/0/interp = 1 -tracks/0/loop_wrap = false -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [12] -} - -[sub_resource type="Animation" id="2"] -resource_name = "Idle" -length = 0.4 -loop_mode = 1 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Sprite2D:frame") -tracks/0/interp = 1 -tracks/0/loop_wrap = false -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.3), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [0, 3] -} - -[sub_resource type="Animation" id="3"] -resource_name = "Rise" -length = 0.1 -loop_mode = 1 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Sprite2D:frame") -tracks/0/interp = 1 -tracks/0/loop_wrap = false -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [8] -} - -[sub_resource type="Animation" id="4"] -resource_name = "Target" -length = 0.2 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Sprite2D:frame") -tracks/0/interp = 1 -tracks/0/loop_wrap = false -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.2), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [4, 7] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_3s1p6"] -_data = { -&"Fall": SubResource("1"), -&"Idle": SubResource("2"), -&"Rise": SubResource("3"), -&"Target": SubResource("4") -} - -[node name="Fish" type="CharacterBody2D" groups=["Actors", "Enemies", "Entities"]] -collision_layer = 2 -collision_mask = 8 -script = ExtResource("2") -id = "0.5" -speed = Vector2(20, 150) - -[node name="CollisionShape2D" type="CollisionShape2D" parent="."] -shape = SubResource("5") - -[node name="Sprite2D" type="Sprite2D" parent="."] -scale = Vector2(-1, 1) -texture = ExtResource("1") -hframes = 4 -vframes = 4 -frame = 12 - -[node name="AnimationPlayer" type="AnimationPlayer" parent="."] -libraries = { -"": SubResource("AnimationLibrary_3s1p6") -} - -[node name="RayCast2D" type="RayCast2D" parent="."] -target_position = Vector2(0, -96) -collision_mask = 9 -collide_with_areas = true - -[node name="StateMachine" parent="." instance=ExtResource("8")] -start_state = NodePath("Idle") - -[node name="Label" type="Label" parent="StateMachine"] -offset_left = -16.0 -offset_top = -16.0 -offset_right = 16.0 -theme = ExtResource("5") - -[node name="Idle" type="Node" parent="StateMachine"] -script = ExtResource("3") - -[node name="Swim" type="Node" parent="StateMachine"] -script = ExtResource("7") - -[node name="Attack" type="Node" parent="StateMachine"] -script = ExtResource("4") - -[node name="Fall" type="Node" parent="StateMachine"] -script = ExtResource("6") - -[node name="Tween" type="Tween" parent="StateMachine/Fall"] -_import_path = NodePath("") -unique_name_in_owner = false -process_mode = 0 -process_priority = 0 -process_physics_priority = 0 -process_thread_group = 0 -editor_description = "" -script = null From bfde38faa46cc9022f5bf799fd9e60020ca88adf Mon Sep 17 00:00:00 2001 From: SanpoNeko Date: Mon, 9 Mar 2026 09:08:45 +0700 Subject: [PATCH 2/7] + Refix the problem where water splashed effect is emitted when an Enemy is already in water when spawned + Fix a bug where an Actor gravity is not properly initialized on spawned --- src/Actor/Actor.gd | 3 ++- src/Trigger/Water.gd | 2 +- src/Trigger/Water.tscn | 11 ++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Actor/Actor.gd b/src/Actor/Actor.gd index abf36847..69d90e81 100644 --- a/src/Actor/Actor.gd +++ b/src/Actor/Actor.gd @@ -5,7 +5,6 @@ class_name Actor const DAMAGE_NUMBER = preload("res://src/Effect/EnemyDamageNumber.tscn") const FLOOR_NORMAL: = Vector2.UP -var gravity := 300.0 var acceleration = 50 var ground_cof = 0.2 var air_cof = 0.05 @@ -20,6 +19,8 @@ var rng = RandomNumberGenerator.new() @export var speed: = Vector2(150, 350) @export var editor_hidden = true +@onready var gravity := water_gravity if is_in_water else base_gravity + @onready var world = get_tree().get_root().get_node("World") func set_is_in_water(val): diff --git a/src/Trigger/Water.gd b/src/Trigger/Water.gd index d55b21f9..8560779d 100644 --- a/src/Trigger/Water.gd +++ b/src/Trigger/Water.gd @@ -23,7 +23,7 @@ func _on_Water_body_entered(body): target.velocity *= velocity_dropoff do_bubbles = false if !body.get_collision_layer_value(1): - if target.just_spawned: + if $GraceTimer.time_left > 0.0: if (!splash_targets.has(target)): splash_targets.append(target) active_bodies.append(target) diff --git a/src/Trigger/Water.tscn b/src/Trigger/Water.tscn index 64417a70..ee81ae5d 100644 --- a/src/Trigger/Water.tscn +++ b/src/Trigger/Water.tscn @@ -1,19 +1,24 @@ -[gd_scene load_steps=3 format=3 uid="uid://xce0p5v08dn2"] +[gd_scene format=3 uid="uid://xce0p5v08dn2"] [ext_resource type="Script" uid="uid://b7n2ctrbbq4kn" path="res://src/Trigger/Water.gd" id="1"] [sub_resource type="RectangleShape2D" id="1"] size = Vector2(16, 16) -[node name="Water" type="Area2D" groups=["Triggers"]] +[node name="Water" type="Area2D" unique_id=1352829104 groups=["Triggers"]] collision_layer = 1048576 collision_mask = 7315 script = ExtResource("1") color = Color(0, 0, 0.701961, 1) -[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1012661956] position = Vector2(8, 8) shape = SubResource("1") +[node name="GraceTimer" type="Timer" parent="." unique_id=712103786] +wait_time = 0.15 +one_shot = true +autostart = true + [connection signal="body_entered" from="." to="." method="_on_Water_body_entered"] [connection signal="body_exited" from="." to="." method="_on_Water_body_exited"] From b062143c51fe2772ec717d39c2edeb6cca0e4ae0 Mon Sep 17 00:00:00 2001 From: SanpoNeko Date: Mon, 9 Mar 2026 09:37:04 +0700 Subject: [PATCH 3/7] Cleanup debug code --- src/Actor/Enemy/Billy.tscn | 1 - src/Actor/Enemy/Fish.gd | 20 +++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Actor/Enemy/Billy.tscn b/src/Actor/Enemy/Billy.tscn index 36094d96..33487f0b 100644 --- a/src/Actor/Enemy/Billy.tscn +++ b/src/Actor/Enemy/Billy.tscn @@ -139,7 +139,6 @@ collision_layer = 2 collision_mask = 520 floor_max_angle = 1.0471976 script = ExtResource("1") -debug = true editor_hidden = false [node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1482682800] diff --git a/src/Actor/Enemy/Fish.gd b/src/Actor/Enemy/Fish.gd index 3d85c87b..fe6ec1dd 100644 --- a/src/Actor/Enemy/Fish.gd +++ b/src/Actor/Enemy/Fish.gd @@ -86,7 +86,7 @@ func do_idle(): if collision != null: if (collision is not TileMapLayer and collision.get_collision_layer_value(1)): if (abs(collision.global_position.x - global_position.x) <= 1): - print("got target") + if (debug): print("got target") change_state("attack") return #endregion @@ -180,12 +180,11 @@ func update_path_lines(): if Engine.is_editor_hint(): vline.add_point(Vector2.ZERO) vline.add_point(Vector2(0, jump_height * -16)) - add_child(vline) - move_child(vline, 0) elif debug and world: - vline.add_point(position) - vline.add_point(jump_pos) - world.front.add_child(vline) + vline.add_point(global_position) + vline.add_point(global_position + Vector2(0, jump_height * -16)) + add_child(vline) + move_child(vline, 0) var hline = PATH_LINE.instantiate() hline.name = "HPath" @@ -193,9 +192,8 @@ func update_path_lines(): if Engine.is_editor_hint(): hline.add_point(Vector2(x_min * 16, -4)) hline.add_point(Vector2(x_max * 16, -4)) - add_child(hline) - move_child(hline, 0) elif debug and world: - hline.add_point(position + Vector2(x_min * 16, -4)) - hline.add_point(position + Vector2(x_max * 16, -4)) - world.front.add_child(hline) + hline.add_point(global_position + Vector2(x_min * 16, -4)) + hline.add_point(global_position + Vector2(x_max * 16, -4)) + add_child(hline) + move_child(hline, 0) From 986b1208427b03d46a3a8775a158deda05baa917 Mon Sep 17 00:00:00 2001 From: SanpoNeko Date: Tue, 10 Mar 2026 15:16:03 +0700 Subject: [PATCH 4/7] Bonk --- src/Actor/Enemy/Fish.gd | 48 ++++++++++++++++++++++++++++++++------- src/Actor/Enemy/Fish.tscn | 9 +++++--- src/Utility/PathLine.tscn | 3 ++- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/Actor/Enemy/Fish.gd b/src/Actor/Enemy/Fish.gd index fe6ec1dd..1939b056 100644 --- a/src/Actor/Enemy/Fish.gd +++ b/src/Actor/Enemy/Fish.gd @@ -16,6 +16,8 @@ var jump_pos: Vector2 = global_position @onready var ap: AnimationPlayer = get_node("AnimationPlayer") @onready var rc: RayCast2D = get_node("RayCast2D") +@onready var BONK: PackedScene = preload("res://src/Effect/BonkParticle.tscn") +var did_bonk: bool = false func setup(): change_state("idle") @@ -44,6 +46,19 @@ func on_x_max_changed(new): x_max = new update_path_lines() +func bonk(): + if (did_bonk or get_slide_collision_count() == 0): + return + + var slide_collision: KinematicCollision2D = get_last_slide_collision() + var bonk_effect = BONK.instantiate() + #print("Fish bonked ", slide_collision.get_normal()) + bonk_effect.normal = slide_collision.get_normal() + bonk_effect.global_position.x = global_position.x + bonk_effect.global_position.y = global_position.y + 7.0 + w.get_node("Front").add_child(bonk_effect) + did_bonk = true + func calc_velocity(move_dir, _do_gravity = true, _do_acceleration = true, _do_friction = true) -> Vector2: match state: "idle", "swim": @@ -58,12 +73,16 @@ func calc_velocity(move_dir, _do_gravity = true, _do_acceleration = true, _do_fr out.y += gravity * get_physics_process_delta_time() if move_dir.y < 0: out.y = speed.y * move_dir.y + elif is_on_ceiling() and !did_bonk: + out.y = -10 return out "fall": var out = velocity - - out.y += gravity * get_physics_process_delta_time() + if (is_on_ceiling() and !did_bonk): + out.y = -10 + else: + out.y += gravity * get_physics_process_delta_time() return out return Vector2.ZERO @@ -72,6 +91,7 @@ func calc_velocity(move_dir, _do_gravity = true, _do_acceleration = true, _do_fr #region STATES #region Idle func enter_idle(_prev_state: String): + motion_mode = CharacterBody2D.MOTION_MODE_FLOATING if can_move_x: change_state("swim") else: @@ -85,7 +105,7 @@ func do_idle(): var collision = get_node("RayCast2D").get_collider() if collision != null: if (collision is not TileMapLayer and collision.get_collision_layer_value(1)): - if (abs(collision.global_position.x - global_position.x) <= 1): + if (abs(collision.global_position.x - rc.global_position.x) <= 1): if (debug): print("got target") change_state("attack") return @@ -93,6 +113,7 @@ func do_idle(): #region Swim func enter_swim(_prev_state: String, _arg: Dictionary = {}): + motion_mode = CharacterBody2D.MOTION_MODE_FLOATING move_dir.x = swim_dir_x ap.play("Idle") @@ -104,12 +125,12 @@ func do_swim(): var collision = rc.get_collider() if collision != null: if (collision is not TileMapLayer and collision.get_collision_layer_value(1)): - if (abs(collision.global_position.x - global_position.x) <= 1): #Prevent the fish target just the side of player hitbox + if (abs(collision.global_position.x - rc.global_position.x) <= 1): #Prevent the fish target just the side of player hitbox if debug: print("got target") change_state("attack") return - if is_on_wall(): + if is_on_wall() and (move_and_collide(Vector2.RIGHT, true) or move_and_collide(Vector2.LEFT, true)): swim_dir_x = -swim_dir_x else: if global_position.x < start_pos.x + x_min * 16: @@ -123,14 +144,18 @@ func do_swim(): #region Attack func enter_attack(_prev_state: String): + motion_mode = CharacterBody2D.MOTION_MODE_GROUNDED jump_pos = global_position ap.play("Target") + did_bonk = false await ap.animation_finished move_dir = Vector2.UP func do_attack(): if position.y <= jump_pos.y - jump_height * 16 + speed.y * 0.3 or is_on_ceiling(): + if (is_on_ceiling()): + bonk() change_state("fall") return @@ -139,13 +164,20 @@ func do_attack(): #endregion #region Fall -func enter_fall(_prev_state: String): - ap.play("Fall") - func exit_fall(_next_state: String): velocity = Vector2.ZERO func do_fall(): + if (velocity.y > 0): + if (ap.current_animation != "Fall"): + ap.play("Fall") + else: + if (ap.current_animation != "Rise"): + ap.play("Rise") + + if (is_on_ceiling()): + bonk() + if !(global_position.y >= jump_pos.y - 5 or is_on_floor()): velocity = calc_velocity(move_dir) move_and_slide() diff --git a/src/Actor/Enemy/Fish.tscn b/src/Actor/Enemy/Fish.tscn index cabcd188..99f92d98 100644 --- a/src/Actor/Enemy/Fish.tscn +++ b/src/Actor/Enemy/Fish.tscn @@ -1,7 +1,7 @@ -[gd_scene format=3 uid="uid://crtxny1yjnkmi"] +[gd_scene format=3 uid="uid://bcw46w7o0xwu3"] [ext_resource type="Texture2D" uid="uid://bbu1jyosea4de" path="res://assets/Actor/Enemy/Fish.png" id="1"] -[ext_resource type="Script" uid="uid://ddxy1osfjb432" path="res://src/Actor/Enemy/Fish.gd" id="2"] +[ext_resource type="Script" uid="uid://fmkb35te6ic0" path="res://src/Actor/Enemy/Fish.gd" id="2"] [sub_resource type="RectangleShape2D" id="5"] size = Vector2(10, 5) @@ -464,7 +464,10 @@ _data = { [node name="Fish" type="CharacterBody2D" unique_id=1312856769 groups=["Actors", "Enemies", "Entities"]] collision_layer = 2 collision_mask = 8 -floor_max_angle = 0.0 +motion_mode = 1 +slide_on_ceiling = false +wall_min_slide_angle = 3.1415927 +floor_snap_length = 0.0 script = ExtResource("2") speed = Vector2(20, 150) editor_hidden = false diff --git a/src/Utility/PathLine.tscn b/src/Utility/PathLine.tscn index 1a2c90fb..7c7f82a1 100644 --- a/src/Utility/PathLine.tscn +++ b/src/Utility/PathLine.tscn @@ -1,6 +1,7 @@ [gd_scene format=3 uid="uid://ctxje1wg3ojk7"] -[node name="PathLine" type="Line2D"] +[node name="PathLine" type="Line2D" unique_id=1318880159] +top_level = true width = 0.5 default_color = Color(0.976471, 0.811765, 0.247059, 1) antialiased = true From 2ec44643b4f2adabe95cbab518df8a7dc98c3d7f Mon Sep 17 00:00:00 2001 From: SanpoNeko Date: Tue, 10 Mar 2026 15:19:36 +0700 Subject: [PATCH 5/7] Make grace time a bit longer to ensure no more splash bug --- src/Trigger/Water.tscn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Trigger/Water.tscn b/src/Trigger/Water.tscn index ee81ae5d..b80ef5a9 100644 --- a/src/Trigger/Water.tscn +++ b/src/Trigger/Water.tscn @@ -16,7 +16,7 @@ position = Vector2(8, 8) shape = SubResource("1") [node name="GraceTimer" type="Timer" parent="." unique_id=712103786] -wait_time = 0.15 +wait_time = 0.2 one_shot = true autostart = true From cbfbe3caed050335e6b4b51279102378f4926e93 Mon Sep 17 00:00:00 2001 From: SanpoNeko Date: Tue, 10 Mar 2026 15:23:34 +0700 Subject: [PATCH 6/7] Cleanup --- src/Actor/Enemy/Fish.gd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Actor/Enemy/Fish.gd b/src/Actor/Enemy/Fish.gd index 1939b056..6137dcd6 100644 --- a/src/Actor/Enemy/Fish.gd +++ b/src/Actor/Enemy/Fish.gd @@ -59,28 +59,28 @@ func bonk(): w.get_node("Front").add_child(bonk_effect) did_bonk = true -func calc_velocity(move_dir, _do_gravity = true, _do_acceleration = true, _do_friction = true) -> Vector2: +func calc_velocity(dir, _do_gravity = true, _do_acceleration = true, _do_friction = true) -> Vector2: match state: "idle", "swim": var out = velocity - out.x = speed.x * move_dir.x + out.x = speed.x * dir.x return out "attack": var out = velocity out.y += gravity * get_physics_process_delta_time() - if move_dir.y < 0: - out.y = speed.y * move_dir.y + if dir.y < 0: + out.y = speed.y * dir.y elif is_on_ceiling() and !did_bonk: - out.y = -10 + out.y = 0 return out "fall": var out = velocity if (is_on_ceiling() and !did_bonk): - out.y = -10 + out.y = 0 else: out.y += gravity * get_physics_process_delta_time() From 7fbdb7d57040c2c5836685e4a6c31093cd0428eb Mon Sep 17 00:00:00 2001 From: AM-Mikey <76826262+AM-Mikey@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:02:51 -0500 Subject: [PATCH 7/7] updated hitbox, damage, and bubbles on fish --- src/Actor/Actor.gd | 1 + src/Actor/Enemy/Fish.gd | 14 ++++++-- src/Actor/Enemy/Fish.gd.uid | 1 + src/Actor/Enemy/Fish.tscn | 65 +++++++++++++++++++------------------ src/Player/Player.gd | 1 + src/Trigger/Water.gd | 4 +-- 6 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 src/Actor/Enemy/Fish.gd.uid diff --git a/src/Actor/Actor.gd b/src/Actor/Actor.gd index 69d90e81..1bb423f5 100644 --- a/src/Actor/Actor.gd +++ b/src/Actor/Actor.gd @@ -11,6 +11,7 @@ var air_cof = 0.05 var dead = false var is_in_water = false: set = set_is_in_water +var do_bubbles = true var home := Vector2(0, 0) var rng = RandomNumberGenerator.new() diff --git a/src/Actor/Enemy/Fish.gd b/src/Actor/Enemy/Fish.gd index 6137dcd6..bc9f5fb4 100644 --- a/src/Actor/Enemy/Fish.gd +++ b/src/Actor/Enemy/Fish.gd @@ -5,6 +5,8 @@ const PATH_LINE = preload("res://src/Utility/PathLine.tscn") var move_dir = Vector2.LEFT @export var swim_dir_x = -1: set = on_swim_dir_x_changed @export var jump_height: int = 6: set = on_jump_height_changed +var normal_damage = 1 +var attack_damage = 4 @onready var start_pos = global_position var jump_pos: Vector2 = global_position @@ -21,9 +23,11 @@ var did_bonk: bool = false func setup(): change_state("idle") + do_bubbles = false if debug: print("ready fish") speed = Vector2(20, 150) - damage_on_contact = 1 + hp = 3 + damage_on_contact = normal_damage update_path_lines() func on_swim_dir_x_changed(new): @@ -106,7 +110,7 @@ func do_idle(): if collision != null: if (collision is not TileMapLayer and collision.get_collision_layer_value(1)): if (abs(collision.global_position.x - rc.global_position.x) <= 1): - if (debug): print("got target") + if (debug): print("fish got target") change_state("attack") return #endregion @@ -126,7 +130,7 @@ func do_swim(): if collision != null: if (collision is not TileMapLayer and collision.get_collision_layer_value(1)): if (abs(collision.global_position.x - rc.global_position.x) <= 1): #Prevent the fish target just the side of player hitbox - if debug: print("got target") + if debug: print("fish got target") change_state("attack") return @@ -144,6 +148,7 @@ func do_swim(): #region Attack func enter_attack(_prev_state: String): + damage_on_contact = attack_damage motion_mode = CharacterBody2D.MOTION_MODE_GROUNDED jump_pos = global_position ap.play("Target") @@ -163,6 +168,9 @@ func do_attack(): move_and_slide() #endregion +func exit_attack(_prev_state): + damage_on_contact = normal_damage + #region Fall func exit_fall(_next_state: String): velocity = Vector2.ZERO diff --git a/src/Actor/Enemy/Fish.gd.uid b/src/Actor/Enemy/Fish.gd.uid new file mode 100644 index 00000000..a42440a9 --- /dev/null +++ b/src/Actor/Enemy/Fish.gd.uid @@ -0,0 +1 @@ +uid://bugags3jgh55c diff --git a/src/Actor/Enemy/Fish.tscn b/src/Actor/Enemy/Fish.tscn index 99f92d98..194910f7 100644 --- a/src/Actor/Enemy/Fish.tscn +++ b/src/Actor/Enemy/Fish.tscn @@ -1,28 +1,30 @@ [gd_scene format=3 uid="uid://bcw46w7o0xwu3"] [ext_resource type="Texture2D" uid="uid://bbu1jyosea4de" path="res://assets/Actor/Enemy/Fish.png" id="1"] -[ext_resource type="Script" uid="uid://fmkb35te6ic0" path="res://src/Actor/Enemy/Fish.gd" id="2"] +[ext_resource type="Script" uid="uid://bugags3jgh55c" path="res://src/Actor/Enemy/Fish.gd" id="2"] [sub_resource type="RectangleShape2D" id="5"] size = Vector2(10, 5) +[sub_resource type="RectangleShape2D" id="RectangleShape2D_yyuli"] +size = Vector2(12, 10) + [sub_resource type="RectangleShape2D" id="RectangleShape2D_vcisb"] size = Vector2(10, 5) [sub_resource type="Animation" id="1"] resource_name = "Fall" length = 0.1 -loop_mode = 1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true tracks/0/path = NodePath("Sprite2D:frame") tracks/0/interp = 1 -tracks/0/loop_wrap = false +tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), -"update": 0, +"update": 1, "values": [12] } tracks/1/type = "value" @@ -35,7 +37,7 @@ tracks/1/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(-0.5, -4)] +"values": [Vector2(-0.5, -8)] } tracks/2/type = "value" tracks/2/imported = false @@ -47,7 +49,7 @@ tracks/2/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(-0.5, -4)] +"values": [Vector2(-0.5, -8)] } tracks/3/type = "value" tracks/3/imported = false @@ -59,7 +61,7 @@ tracks/3/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(-0.5, -4)] +"values": [Vector2(-0.5, -8)] } tracks/4/type = "value" tracks/4/imported = false @@ -124,7 +126,7 @@ tracks/1/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(0, -3.5)] +"values": [Vector2(0, -7.5)] } tracks/2/type = "value" tracks/2/imported = false @@ -136,7 +138,7 @@ tracks/2/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(0, -3.5)] +"values": [Vector2(0, -7.5)] } tracks/3/type = "value" tracks/3/imported = false @@ -148,7 +150,7 @@ tracks/3/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(0, -3.5)] +"values": [Vector2(0, -7.5)] } tracks/4/type = "value" tracks/4/imported = false @@ -199,7 +201,7 @@ tracks/0/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(0, -3.5)] +"values": [Vector2(0, -7.5)] } tracks/1/type = "value" tracks/1/imported = false @@ -211,7 +213,7 @@ tracks/1/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(0, -3.5)] +"values": [Vector2(0, -7.5)] } tracks/2/type = "value" tracks/2/imported = false @@ -223,7 +225,7 @@ tracks/2/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(0, -3.5)] +"values": [Vector2(0, -7.5)] } tracks/3/type = "value" tracks/3/imported = false @@ -277,17 +279,16 @@ tracks/6/keys = { [sub_resource type="Animation" id="3"] resource_name = "Rise" length = 0.1 -loop_mode = 1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true tracks/0/path = NodePath("Sprite2D:frame") tracks/0/interp = 1 -tracks/0/loop_wrap = false +tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), -"update": 0, +"update": 1, "values": [8] } tracks/1/type = "value" @@ -300,7 +301,7 @@ tracks/1/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(-0.5, -4)] +"values": [Vector2(-0.5, -8)] } tracks/2/type = "value" tracks/2/imported = false @@ -312,7 +313,7 @@ tracks/2/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(-0.5, -4)] +"values": [Vector2(-0.5, -8)] } tracks/3/type = "value" tracks/3/imported = false @@ -324,7 +325,7 @@ tracks/3/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [Vector2(-0.5, -4)] +"values": [Vector2(-0.5, -8)] } tracks/4/type = "value" tracks/4/imported = false @@ -366,7 +367,7 @@ tracks/6/keys = { [sub_resource type="Animation" id="4"] resource_name = "Target" length = 0.2 -step = 0.05 +step = 0.025 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true @@ -374,10 +375,10 @@ tracks/0/path = NodePath("Sprite2D:frame") tracks/0/interp = 1 tracks/0/loop_wrap = false tracks/0/keys = { -"times": PackedFloat32Array(0, 0.2), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [4, 7] +"times": PackedFloat32Array(0, 0.05, 0.1, 0.15), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 1, +"values": [4, 5, 6, 7] } tracks/1/type = "value" tracks/1/imported = false @@ -389,7 +390,7 @@ tracks/1/keys = { "times": PackedFloat32Array(0, 0.05, 0.1), "transitions": PackedFloat32Array(1, 1, 1), "update": 1, -"values": [Vector2(0, -3.5), Vector2(0, -4), Vector2(-0.5, -3.5)] +"values": [Vector2(0, -7.5), Vector2(0, -8), Vector2(-0.5, -7.5)] } tracks/2/type = "value" tracks/2/imported = false @@ -401,7 +402,7 @@ tracks/2/keys = { "times": PackedFloat32Array(0, 0.05, 0.10000001), "transitions": PackedFloat32Array(1, 1, 1), "update": 1, -"values": [Vector2(0, -3.5), Vector2(0, -4), Vector2(-0.5, -3.5)] +"values": [Vector2(0, -7.5), Vector2(0, -8), Vector2(-0.5, -7.5)] } tracks/3/type = "value" tracks/3/imported = false @@ -413,7 +414,7 @@ tracks/3/keys = { "times": PackedFloat32Array(0, 0.05, 0.10000001), "transitions": PackedFloat32Array(1, 1, 1), "update": 1, -"values": [Vector2(0, -3.5), Vector2(0, -4), Vector2(-0.5, -3.5)] +"values": [Vector2(0, -7.5), Vector2(0, -8), Vector2(-0.5, -7.5)] } tracks/4/type = "value" tracks/4/imported = false @@ -473,7 +474,7 @@ speed = Vector2(20, 150) editor_hidden = false [node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1208749473] -position = Vector2(0, -3.5) +position = Vector2(0, -7.5) shape = SubResource("5") [node name="Hurtbox" type="Area2D" parent="." unique_id=434285087] @@ -481,8 +482,8 @@ collision_layer = 131072 collision_mask = 0 [node name="CollisionShape2D" type="CollisionShape2D" parent="Hurtbox" unique_id=569733112] -position = Vector2(0, -3.5) -shape = SubResource("RectangleShape2D_vcisb") +position = Vector2(0, -7.5) +shape = SubResource("RectangleShape2D_yyuli") debug_color = Color(0, 1, 0, 0.235294) [node name="Hitbox" type="Area2D" parent="." unique_id=430236071] @@ -490,12 +491,12 @@ collision_layer = 262144 collision_mask = 65536 [node name="CollisionShape2D" type="CollisionShape2D" parent="Hitbox" unique_id=2082591926] -position = Vector2(0, -3.5) +position = Vector2(0, -7.5) shape = SubResource("RectangleShape2D_vcisb") debug_color = Color(1, 0, 0, 0.235294) [node name="Sprite2D" type="Sprite2D" parent="." unique_id=971659152] -position = Vector2(0, -4) +position = Vector2(0, -8) scale = Vector2(-1, 1) texture = ExtResource("1") hframes = 4 diff --git a/src/Player/Player.gd b/src/Player/Player.gd index 3228c559..c20738d2 100644 --- a/src/Player/Player.gd +++ b/src/Player/Player.gd @@ -40,6 +40,7 @@ var forbid_crouching = false var is_in_water = false var is_in_coyote = false var dead = false +var do_bubbles = true @export var controller_id: int = 0 diff --git a/src/Trigger/Water.gd b/src/Trigger/Water.gd index 8560779d..4a287e19 100644 --- a/src/Trigger/Water.gd +++ b/src/Trigger/Water.gd @@ -30,7 +30,7 @@ func _on_Water_body_entered(body): if not target.is_in_water: target.is_in_water = true - if do_bubbles: + if do_bubbles && target.do_bubbles: var be = BUBBLEEMITTER.instantiate() bubble_emitters[target] = be target.call_deferred("add_child", be) @@ -56,7 +56,7 @@ func _on_Water_body_exited(body): if not get_overlap(body): target.is_in_water = false - if do_bubbles and bubble_emitters.has(target): + if do_bubbles && bubble_emitters.has(target): bubble_emitters[target].queue_free() bubble_emitters.erase(target) await get_tree().physics_frame