summaryrefslogtreecommitdiff
path: root/Scenes/Entities/Enemies
diff options
context:
space:
mode:
authorDaniel Weipert <git@mail.dweipert.de>2024-05-06 16:30:46 +0200
committerDaniel Weipert <git@mail.dweipert.de>2024-05-06 16:30:46 +0200
commit276b7664bd4a475a3ca93a682c16b49c504c58f7 (patch)
tree6b8f8cf0e1a82496c1f539e55e26bc078300b23a /Scenes/Entities/Enemies
parent8c0e8b5bf73caaf2fd6f04fb97b9c13f2970c431 (diff)
update
Diffstat (limited to 'Scenes/Entities/Enemies')
-rw-r--r--Scenes/Entities/Enemies/Balloon.tscn2
-rw-r--r--Scenes/Entities/Enemies/Components/Movement.gd82
-rw-r--r--Scenes/Entities/Enemies/Projectile.gd28
-rw-r--r--Scenes/Entities/Enemies/Projectile.tscn44
-rw-r--r--Scenes/Entities/Enemies/Snake.gd144
-rw-r--r--Scenes/Entities/Enemies/Snake.tscn404
-rw-r--r--Scenes/Entities/Enemies/Statue.gd27
-rw-r--r--Scenes/Entities/Enemies/Statue.tscn30
8 files changed, 732 insertions, 29 deletions
diff --git a/Scenes/Entities/Enemies/Balloon.tscn b/Scenes/Entities/Enemies/Balloon.tscn
index 9e1a6bc..c1b0701 100644
--- a/Scenes/Entities/Enemies/Balloon.tscn
+++ b/Scenes/Entities/Enemies/Balloon.tscn
@@ -144,7 +144,7 @@ shape = SubResource("CapsuleShape2D_abul4")
[node name="Movement" parent="." instance=ExtResource("15_fwetl")]
entityPath = NodePath("..")
spritePath = NodePath("../AnimatedSprite2D")
-SPEED = 10
+SPEED = 10.0
timer_time = 10.0
[node name="Collision" parent="." instance=ExtResource("16_yau34")]
diff --git a/Scenes/Entities/Enemies/Components/Movement.gd b/Scenes/Entities/Enemies/Components/Movement.gd
index 5470a7d..3174508 100644
--- a/Scenes/Entities/Enemies/Components/Movement.gd
+++ b/Scenes/Entities/Enemies/Components/Movement.gd
@@ -10,13 +10,21 @@ signal direction_changed
@export_node_path("AnimatedSprite2D") var spritePath: NodePath
@onready var sprite: AnimatedSprite2D = get_node(spritePath)
-@export var SPEED: int
+@export var SPEED: float
const DIRECTIONS = [Vector2.UP, Vector2.RIGHT, Vector2.DOWN, Vector2.LEFT]
var CURRENT_DIRECTION = Vector2.UP
var LAST_DIRECTION = Vector2.UP
var was_colliding = false
+var animations: Dictionary = {
+ "default": "default",
+ "up": "up",
+ "down": "down",
+ "left": "left",
+ "right": "right",
+}
+
@export var timer_time: float = 3.0
@onready var MovementTimer: Timer = Timer.new()
@@ -33,28 +41,33 @@ func _ready():
func physics_process(delta):
if CURRENT_DIRECTION == Vector2.UP:
- entity.velocity.y -= SPEED
- if sprite.sprite_frames.has_animation("up"):
- sprite.play("up")
+ entity.velocity.y -= SPEED/2
+ if sprite.sprite_frames.has_animation(animations["up"]):
+ sprite.play(animations["up"])
elif CURRENT_DIRECTION == Vector2.DOWN:
- entity.velocity.y += SPEED
- if sprite.sprite_frames.has_animation("down"):
- sprite.play("down")
+ entity.velocity.y += SPEED/2
+ if sprite.sprite_frames.has_animation(animations["down"]):
+ sprite.play(animations["down"])
elif CURRENT_DIRECTION == Vector2.LEFT:
- entity.velocity.x -= SPEED
- if sprite.sprite_frames.has_animation("left"):
- sprite.play("left")
+ entity.velocity.x -= SPEED/2
+ if sprite.sprite_frames.has_animation(animations["left"]):
+ sprite.play(animations["left"])
elif CURRENT_DIRECTION == Vector2.RIGHT:
- entity.velocity.x += SPEED
- if sprite.sprite_frames.has_animation("right"):
- sprite.play("right")
+ entity.velocity.x += SPEED/2
+ if sprite.sprite_frames.has_animation(animations["right"]):
+ sprite.play(animations["right"])
- if sprite.sprite_frames.has_animation("default"):
- sprite.play("default")
+ if sprite.sprite_frames.has_animation(animations["default"]):
+ sprite.play(animations["default"])
var collision = entity.move_and_collide(entity.velocity * delta)
entity.velocity = entity.velocity.lerp(Vector2(0, 0), 1) # speed too low => no collision
+ # todo movement along grid (maybe move position to current tile center)
+ var target_grid_position = Utilities.from_grid_to_position(Utilities.get_level_position(entity) + CURRENT_DIRECTION)
+ entity.position = entity.position.move_toward(target_grid_position, delta*SPEED/2)
+ # todo or set velocity to tiles per SPEED per second somehow, instead of Timer
+
if collision:
was_colliding = true
if MovementTimer.time_left > 0:
@@ -65,11 +78,23 @@ func physics_process(delta):
return collision
+func set_animations(new_animations: Dictionary):
+ animations.merge(new_animations, true)
+
+
func _on_movement_timer_timeout():
var randomize_direction = false
- if follows and randi_range(0, 100) <= follow_chance:
+ if false and follows and randi_range(0, 100) <= follow_chance:
var player_direction = entity.position.direction_to(Global.player.position)
+ var query = PhysicsRayQueryParameters2D.new()
+ query.set_from(entity.position)
+ query.set_to(Global.player.position)
+ query.set_collision_mask(entity.collision_mask)
+ var intersection = entity.get_world_2d().direct_space_state.intersect_ray(query)
+ if intersection:
+ randomize_direction = true
+
if abs(player_direction.x) >= abs(player_direction.y):
if player_direction.x >= 0:
CURRENT_DIRECTION = Vector2.RIGHT
@@ -89,25 +114,26 @@ func _on_movement_timer_timeout():
if randomize_direction:
var directions = DIRECTIONS.duplicate()
- directions.remove_at(directions.find(CURRENT_DIRECTION))
+ directions.erase(CURRENT_DIRECTION)
directions.shuffle()
-
- CURRENT_DIRECTION = directions[0]
+
+ if not directions.is_empty():
+ CURRENT_DIRECTION = directions[0]
emit_signal("direction_changed", CURRENT_DIRECTION)
LAST_DIRECTION = CURRENT_DIRECTION
- if not sprite.sprite_frames.has_animation("default"):
+ if not sprite.sprite_frames.has_animation(animations["default"]):
var frame = sprite.frame
var progress = sprite.frame_progress
- if CURRENT_DIRECTION == Vector2.UP and sprite.sprite_frames.has_animation("up"):
- sprite.play("up")
- elif CURRENT_DIRECTION == Vector2.DOWN and sprite.sprite_frames.has_animation("down"):
- sprite.play("down")
- elif CURRENT_DIRECTION == Vector2.LEFT and sprite.sprite_frames.has_animation("left"):
- sprite.play("left")
- elif CURRENT_DIRECTION == Vector2.RIGHT and sprite.sprite_frames.has_animation("right"):
- sprite.play("right")
+ if CURRENT_DIRECTION == Vector2.UP and sprite.sprite_frames.has_animation(animations["up"]):
+ sprite.play(animations["up"])
+ elif CURRENT_DIRECTION == Vector2.DOWN and sprite.sprite_frames.has_animation(animations["down"]):
+ sprite.play(animations["down"])
+ elif CURRENT_DIRECTION == Vector2.LEFT and sprite.sprite_frames.has_animation(animations["left"]):
+ sprite.play(animations["left"])
+ elif CURRENT_DIRECTION == Vector2.RIGHT and sprite.sprite_frames.has_animation(animations["right"]):
+ sprite.play(animations["right"])
sprite.set_frame_and_progress(frame, progress)
MovementTimer.start()
diff --git a/Scenes/Entities/Enemies/Projectile.gd b/Scenes/Entities/Enemies/Projectile.gd
new file mode 100644
index 0000000..af283b4
--- /dev/null
+++ b/Scenes/Entities/Enemies/Projectile.gd
@@ -0,0 +1,28 @@
+extends CharacterBody2D
+
+
+signal hit
+
+@export var target: Vector2
+var direction: Vector2
+
+@export var speed: int = 50
+
+
+func _ready():
+ velocity = position.direction_to(target) * speed
+
+
+func _physics_process(delta):
+ $AnimatedSprite2D.play("default")
+ move_and_slide()
+
+
+func _on_area_2d_body_entered(body):
+ if body is Player:
+ hit.emit()
+ queue_free()
+
+
+func _on_visible_on_screen_notifier_2d_screen_exited():
+ queue_free()
diff --git a/Scenes/Entities/Enemies/Projectile.tscn b/Scenes/Entities/Enemies/Projectile.tscn
new file mode 100644
index 0000000..e417172
--- /dev/null
+++ b/Scenes/Entities/Enemies/Projectile.tscn
@@ -0,0 +1,44 @@
+[gd_scene load_steps=6 format=3 uid="uid://b3tyux5hmqjdv"]
+
+[ext_resource type="Texture2D" uid="uid://1j0mcugpmtip" path="res://Assets/Enemies/Projectile_01.png" id="1_1awp7"]
+[ext_resource type="Script" path="res://Scenes/Entities/Enemies/Projectile.gd" id="1_e3x2k"]
+[ext_resource type="Texture2D" uid="uid://dlopu0vp85dv0" path="res://Assets/Enemies/Projectile_02.png" id="2_qk14s"]
+
+[sub_resource type="SpriteFrames" id="SpriteFrames_xmf75"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("1_1awp7")
+}, {
+"duration": 1.0,
+"texture": ExtResource("2_qk14s")
+}],
+"loop": false,
+"name": &"default",
+"speed": 8.0
+}]
+
+[sub_resource type="CircleShape2D" id="CircleShape2D_gcjn2"]
+radius = 3.0
+
+[node name="Projectile" type="CharacterBody2D"]
+collision_layer = 0
+collision_mask = 0
+script = ExtResource("1_e3x2k")
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+texture_filter = 1
+sprite_frames = SubResource("SpriteFrames_xmf75")
+
+[node name="Area2D" type="Area2D" parent="."]
+collision_layer = 0
+collision_mask = 2
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
+shape = SubResource("CircleShape2D_gcjn2")
+
+[node name="VisibleOnScreenNotifier2D" type="VisibleOnScreenNotifier2D" parent="."]
+scale = Vector2(0.8, 0.8)
+
+[connection signal="body_entered" from="Area2D" to="." method="_on_area_2d_body_entered"]
+[connection signal="screen_exited" from="VisibleOnScreenNotifier2D" to="." method="_on_visible_on_screen_notifier_2d_screen_exited"]
diff --git a/Scenes/Entities/Enemies/Snake.gd b/Scenes/Entities/Enemies/Snake.gd
new file mode 100644
index 0000000..a87ecf0
--- /dev/null
+++ b/Scenes/Entities/Enemies/Snake.gd
@@ -0,0 +1,144 @@
+extends CharacterBody2D
+
+
+@export var speed: float = 10
+
+static var detected_bombs = []
+var did_detect_bomb = false
+var detected_bomb: Bomb
+var detected_bomb_direction = Vector2.UP
+var is_full = false
+
+var is_animating_state = false
+var animations_default = {
+ "up": "up",
+ "down": "down",
+ "left": "left",
+ "right": "right",
+}
+var animations_full = {
+ "up": "up_full",
+ "down": "down_full",
+ "left": "left_full",
+ "right": "right_full",
+}
+var current_delta
+
+@onready var component_collision: ComponentCollision = $Collision
+@onready var component_movement: ComponentMovement = $Movement
+
+
+func _ready():
+ add_to_group("enemies")
+
+ component_collision.init()
+ component_movement.SPEED = speed
+
+
+func _physics_process(delta):
+ current_delta = delta
+
+ if not did_detect_bomb and not is_full:
+ component_movement.SPEED = speed
+
+ for ray: RayCast2D in $Rays.get_children():
+ if (
+ ray.is_colliding() and
+ ray.get_collider() and
+ ray.get_collider().is_in_group("bombs") and
+ not detected_bombs.has(ray.get_collider())
+ ):
+ did_detect_bomb = true
+ detected_bomb = ray.get_collider()
+ detected_bombs.push_front(detected_bomb)
+ detected_bomb.tree_exited.connect(func():
+ did_detect_bomb = false
+ detected_bombs.erase(detected_bomb)
+ detected_bomb = null
+ )
+
+ if ray.name == "Down":
+ detected_bomb_direction = Vector2.DOWN
+ elif ray.name == "Up":
+ detected_bomb_direction = Vector2.UP
+ elif ray.name == "Left":
+ detected_bomb_direction = Vector2.LEFT
+ elif ray.name == "Right":
+ detected_bomb_direction = Vector2.RIGHT
+
+ component_movement.SPEED = speed*4
+
+ if $DetectionArea.get_overlapping_bodies().has(detected_bomb):
+ _on_detection_area_body_entered(detected_bomb)
+ elif did_detect_bomb:
+ component_movement.CURRENT_DIRECTION = detected_bomb_direction
+
+ if not is_animating_state:
+ component_movement.physics_process(delta)
+
+
+func set_animating_state(state: bool):
+ is_animating_state = state
+ if is_animating_state:
+ component_movement.MovementTimer.stop()
+ else:
+ component_movement.MovementTimer.start()
+
+
+func _on_detection_area_body_entered(_body):
+ if did_detect_bomb and detected_bomb:
+ set_animating_state(true)
+
+ add_collision_exception_with(detected_bomb)
+ detected_bomb.stop()
+ self.z_index = detected_bomb.z_index + 1
+
+ if detected_bomb_direction == Vector2.UP:
+ $AnimatedSprite2D.play("up_bite")
+ smooth_over("up_bite")
+ elif detected_bomb_direction == Vector2.DOWN:
+ $AnimatedSprite2D.play("down_bite")
+ smooth_over("down_bite")
+ elif detected_bomb_direction == Vector2.LEFT:
+ $AnimatedSprite2D.play("left_bite")
+ smooth_over("left_bite")
+ elif detected_bomb_direction == Vector2.RIGHT:
+ $AnimatedSprite2D.play("right_bite")
+ smooth_over("right_bite")
+ await $AnimatedSprite2D.animation_finished
+
+ if detected_bomb:
+ detected_bomb.queue_free()
+ component_movement.set_animations(animations_full)
+ is_full = true
+ component_movement.SPEED = speed/2
+
+ $Timer.start()
+ set_animating_state(false)
+
+func smooth_over (animation):
+ create_tween().tween_property(
+ self,
+ "position",
+ detected_bomb.position,
+ $AnimatedSprite2D.sprite_frames.get_animation_speed(animation) / $AnimatedSprite2D.sprite_frames.get_frame_count(animation)
+ )
+
+
+func _on_timer_timeout():
+ set_animating_state(true)
+
+ if component_movement.CURRENT_DIRECTION == Vector2.UP:
+ $AnimatedSprite2D.play("up_shrink")
+ elif component_movement.CURRENT_DIRECTION == Vector2.DOWN:
+ $AnimatedSprite2D.play("down_shrink")
+ elif component_movement.CURRENT_DIRECTION == Vector2.LEFT:
+ $AnimatedSprite2D.play("left_shrink")
+ elif component_movement.CURRENT_DIRECTION == Vector2.RIGHT:
+ $AnimatedSprite2D.play("right_shrink")
+ await $AnimatedSprite2D.animation_finished
+
+ component_movement.set_animations(animations_default)
+ is_full = false
+ component_movement.SPEED = speed
+ set_animating_state(false)
diff --git a/Scenes/Entities/Enemies/Snake.tscn b/Scenes/Entities/Enemies/Snake.tscn
new file mode 100644
index 0000000..660b3d7
--- /dev/null
+++ b/Scenes/Entities/Enemies/Snake.tscn
@@ -0,0 +1,404 @@
+[gd_scene load_steps=55 format=3 uid="uid://bpg6ye1abah3g"]
+
+[ext_resource type="Script" path="res://Scenes/Entities/Enemies/Snake.gd" id="1_h5f33"]
+[ext_resource type="Texture2D" uid="uid://cypaci6jxhxsj" path="res://Assets/Enemies/Snake_down_01.png" id="2_en6kb"]
+[ext_resource type="Texture2D" uid="uid://bn5ueyfn3asnc" path="res://Assets/Enemies/Snake_death.png" id="2_hsp6b"]
+[ext_resource type="Texture2D" uid="uid://b5rvgttltyven" path="res://Assets/Enemies/Snake_down_02.png" id="3_gjbw3"]
+[ext_resource type="Texture2D" uid="uid://cpl4n4jmgiwrv" path="res://Assets/Enemies/Enemy_death_01_01.png" id="3_wuqrr"]
+[ext_resource type="Texture2D" uid="uid://cive10sqmjo2m" path="res://Assets/Enemies/Snake_down_03.png" id="4_c5itv"]
+[ext_resource type="Texture2D" uid="uid://d2gk5amafhctu" path="res://Assets/Enemies/Enemy_death_01_02.png" id="4_eidva"]
+[ext_resource type="Texture2D" uid="uid://dqwxq3g3aph4i" path="res://Assets/Enemies/Enemy_death_01_03.png" id="5_sfv8a"]
+[ext_resource type="Texture2D" uid="uid://csuf0rfj4hk3j" path="res://Assets/Enemies/Snake_left_01.png" id="5_sg6fb"]
+[ext_resource type="Texture2D" uid="uid://uoj5222vlq8q" path="res://Assets/Enemies/Enemy_death_01_04.png" id="6_jbbsf"]
+[ext_resource type="Texture2D" uid="uid://dqfoqshawtnwm" path="res://Assets/Enemies/Snake_left_02.png" id="6_sc476"]
+[ext_resource type="Texture2D" uid="uid://cwyhl4ykqy0n7" path="res://Assets/Enemies/Snake_left_03.png" id="7_40rym"]
+[ext_resource type="Texture2D" uid="uid://cxx2vv1ohiw07" path="res://Assets/Enemies/Enemy_death_01_05.png" id="7_guf5s"]
+[ext_resource type="Texture2D" uid="uid://cswrpv58ren4f" path="res://Assets/Enemies/Snake_right_01.png" id="8_7lycw"]
+[ext_resource type="Texture2D" uid="uid://b2rl0d5ivdvn4" path="res://Assets/Enemies/Snake_right_02.png" id="9_jalnm"]
+[ext_resource type="Texture2D" uid="uid://n8uednsjk6b1" path="res://Assets/Enemies/Snake_right_03.png" id="10_ecduy"]
+[ext_resource type="Texture2D" uid="uid://bi2u8uf22x66v" path="res://Assets/Enemies/Snake_up_01.png" id="11_rtkup"]
+[ext_resource type="Texture2D" uid="uid://33h3hla14uqm" path="res://Assets/Enemies/Snake_bite_down_01.png" id="11_y0yuq"]
+[ext_resource type="Texture2D" uid="uid://xxi1cq4pvdl" path="res://Assets/Enemies/Snake_bite_down_02.png" id="12_4h16w"]
+[ext_resource type="Texture2D" uid="uid://b8ld0ln4iquhc" path="res://Assets/Enemies/Snake_up_02.png" id="12_eyhtl"]
+[ext_resource type="Texture2D" uid="uid://dpc8aoe5as3vb" path="res://Assets/Enemies/Snake_bite_down_03.png" id="13_pfxrj"]
+[ext_resource type="Texture2D" uid="uid://osy613yub321" path="res://Assets/Enemies/Snake_up_03.png" id="13_y6ve5"]
+[ext_resource type="Texture2D" uid="uid://dssta2jqwe8o" path="res://Assets/Enemies/Snake_bite_down_04.png" id="14_5apeq"]
+[ext_resource type="PackedScene" uid="uid://cq7yj2av01tqd" path="res://Scenes/Entities/Enemies/Components/Movement.tscn" id="14_ab70e"]
+[ext_resource type="PackedScene" uid="uid://ce3vv2pod6auc" path="res://Scenes/Entities/Enemies/Components/Collision.tscn" id="15_mm88w"]
+[ext_resource type="Texture2D" uid="uid://ljutjpsw6jv7" path="res://Assets/Enemies/Snake_full_down_01.png" id="15_x3mge"]
+[ext_resource type="PackedScene" uid="uid://b3i372vgdbxk" path="res://Scenes/Entities/Enemies/Components/Health.tscn" id="16_0dow6"]
+[ext_resource type="Texture2D" uid="uid://l5du3vt4nusk" path="res://Assets/Enemies/Snake_full_down_02.png" id="16_vjet0"]
+[ext_resource type="Texture2D" uid="uid://ogb404prqs4w" path="res://Assets/Enemies/Snake_full_down_03.png" id="17_1qbr6"]
+[ext_resource type="Texture2D" uid="uid://ds34ow5ju743x" path="res://Assets/Enemies/Snake_shrink_down.png" id="18_ybkp8"]
+[ext_resource type="Texture2D" uid="uid://byrg1wwnx2j76" path="res://Assets/Enemies/Snake_bite_left_01.png" id="22_mtrrd"]
+[ext_resource type="Texture2D" uid="uid://dpp2ug4a647uj" path="res://Assets/Enemies/Snake_bite_left_02.png" id="23_1576u"]
+[ext_resource type="Texture2D" uid="uid://dem758o6o7wkc" path="res://Assets/Enemies/Snake_bite_left_03.png" id="24_stadt"]
+[ext_resource type="Texture2D" uid="uid://oxilj52s31pr" path="res://Assets/Enemies/Snake_full_left_01.png" id="25_2ntrh"]
+[ext_resource type="Texture2D" uid="uid://c6htr1keocnh8" path="res://Assets/Enemies/Snake_full_left_02.png" id="26_vp11f"]
+[ext_resource type="Texture2D" uid="uid://b8xoqf1mn0msw" path="res://Assets/Enemies/Snake_full_left_03.png" id="27_6np6x"]
+[ext_resource type="Texture2D" uid="uid://dign0a7vhai7v" path="res://Assets/Enemies/Snake_shrink_left.png" id="28_7fu21"]
+[ext_resource type="Texture2D" uid="uid://v74mnwccruy5" path="res://Assets/Enemies/Snake_bite_right_01.png" id="32_gsu2j"]
+[ext_resource type="Texture2D" uid="uid://bgpygm3dhfnke" path="res://Assets/Enemies/Snake_bite_right_02.png" id="33_2qv3t"]
+[ext_resource type="Texture2D" uid="uid://brj0tvwn6yqpn" path="res://Assets/Enemies/Snake_bite_right_03.png" id="34_gf2fa"]
+[ext_resource type="Texture2D" uid="uid://dwp484rrsq6rd" path="res://Assets/Enemies/Snake_full_right_01.png" id="35_6rebf"]
+[ext_resource type="Texture2D" uid="uid://cep7jpug03ju7" path="res://Assets/Enemies/Snake_full_right_02.png" id="36_f8mis"]
+[ext_resource type="Texture2D" uid="uid://dv0rhkw7012nf" path="res://Assets/Enemies/Snake_full_right_03.png" id="37_rsbv2"]
+[ext_resource type="Texture2D" uid="uid://bta6ffkouymtr" path="res://Assets/Enemies/Snake_shrink_right.png" id="38_mxcmv"]
+[ext_resource type="Texture2D" uid="uid://p1wwaipiidpk" path="res://Assets/Enemies/Snake_bite_up_01.png" id="42_gg2ke"]
+[ext_resource type="Texture2D" uid="uid://lyocxvmujulv" path="res://Assets/Enemies/Snake_bite_up_02.png" id="43_x72jq"]
+[ext_resource type="Texture2D" uid="uid://dlu76im4x6ih5" path="res://Assets/Enemies/Snake_bite_up_03.png" id="44_xrna7"]
+[ext_resource type="Texture2D" uid="uid://bvd14glih8rp7" path="res://Assets/Enemies/Snake_full_up_01.png" id="45_f3csq"]
+[ext_resource type="Texture2D" uid="uid://2hf7tus6n73i" path="res://Assets/Enemies/Snake_full_up_02.png" id="46_n7us6"]
+[ext_resource type="Texture2D" uid="uid://05siba3xdcnn" path="res://Assets/Enemies/Snake_full_up_03.png" id="47_kwyk6"]
+[ext_resource type="Texture2D" uid="uid://co4dwdeo3su3g" path="res://Assets/Enemies/Snake_shrink_up.png" id="48_w8b5g"]
+
+[sub_resource type="SpriteFrames" id="SpriteFrames_jpiqi"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("2_hsp6b")
+}],
+"loop": false,
+"name": &"death",
+"speed": 1.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("3_wuqrr")
+}, {
+"duration": 1.0,
+"texture": ExtResource("4_eidva")
+}, {
+"duration": 1.0,
+"texture": ExtResource("5_sfv8a")
+}, {
+"duration": 1.0,
+"texture": ExtResource("6_jbbsf")
+}, {
+"duration": 1.0,
+"texture": ExtResource("7_guf5s")
+}],
+"loop": false,
+"name": &"death_post",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("2_en6kb")
+}, {
+"duration": 1.0,
+"texture": ExtResource("3_gjbw3")
+}, {
+"duration": 1.0,
+"texture": ExtResource("4_c5itv")
+}, {
+"duration": 1.0,
+"texture": ExtResource("3_gjbw3")
+}],
+"loop": true,
+"name": &"down",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("11_y0yuq")
+}, {
+"duration": 1.0,
+"texture": ExtResource("12_4h16w")
+}, {
+"duration": 1.0,
+"texture": ExtResource("13_pfxrj")
+}, {
+"duration": 1.0,
+"texture": ExtResource("14_5apeq")
+}],
+"loop": false,
+"name": &"down_bite",
+"speed": 3.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("15_x3mge")
+}, {
+"duration": 1.0,
+"texture": ExtResource("16_vjet0")
+}, {
+"duration": 1.0,
+"texture": ExtResource("17_1qbr6")
+}, {
+"duration": 1.0,
+"texture": ExtResource("16_vjet0")
+}],
+"loop": true,
+"name": &"down_full",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("17_1qbr6")
+}, {
+"duration": 1.0,
+"texture": ExtResource("18_ybkp8")
+}, {
+"duration": 1.0,
+"texture": ExtResource("4_c5itv")
+}],
+"loop": false,
+"name": &"down_shrink",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("5_sg6fb")
+}, {
+"duration": 1.0,
+"texture": ExtResource("6_sc476")
+}, {
+"duration": 1.0,
+"texture": ExtResource("7_40rym")
+}, {
+"duration": 1.0,
+"texture": ExtResource("6_sc476")
+}],
+"loop": true,
+"name": &"left",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("22_mtrrd")
+}, {
+"duration": 1.0,
+"texture": ExtResource("23_1576u")
+}, {
+"duration": 1.0,
+"texture": ExtResource("24_stadt")
+}],
+"loop": false,
+"name": &"left_bite",
+"speed": 3.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("25_2ntrh")
+}, {
+"duration": 1.0,
+"texture": ExtResource("26_vp11f")
+}, {
+"duration": 1.0,
+"texture": ExtResource("27_6np6x")
+}, {
+"duration": 1.0,
+"texture": ExtResource("26_vp11f")
+}],
+"loop": true,
+"name": &"left_full",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("26_vp11f")
+}, {
+"duration": 1.0,
+"texture": ExtResource("28_7fu21")
+}, {
+"duration": 1.0,
+"texture": ExtResource("6_sc476")
+}],
+"loop": false,
+"name": &"left_shrink",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("8_7lycw")
+}, {
+"duration": 1.0,
+"texture": ExtResource("9_jalnm")
+}, {
+"duration": 1.0,
+"texture": ExtResource("10_ecduy")
+}, {
+"duration": 1.0,
+"texture": ExtResource("9_jalnm")
+}],
+"loop": true,
+"name": &"right",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("32_gsu2j")
+}, {
+"duration": 1.0,
+"texture": ExtResource("33_2qv3t")
+}, {
+"duration": 1.0,
+"texture": ExtResource("34_gf2fa")
+}],
+"loop": false,
+"name": &"right_bite",
+"speed": 3.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("35_6rebf")
+}, {
+"duration": 1.0,
+"texture": ExtResource("36_f8mis")
+}, {
+"duration": 1.0,
+"texture": ExtResource("37_rsbv2")
+}, {
+"duration": 1.0,
+"texture": ExtResource("36_f8mis")
+}],
+"loop": true,
+"name": &"right_full",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("36_f8mis")
+}, {
+"duration": 1.0,
+"texture": ExtResource("38_mxcmv")
+}, {
+"duration": 1.0,
+"texture": ExtResource("9_jalnm")
+}],
+"loop": false,
+"name": &"right_shrink",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("11_rtkup")
+}, {
+"duration": 1.0,
+"texture": ExtResource("12_eyhtl")
+}, {
+"duration": 1.0,
+"texture": ExtResource("13_y6ve5")
+}, {
+"duration": 1.0,
+"texture": ExtResource("12_eyhtl")
+}],
+"loop": true,
+"name": &"up",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("42_gg2ke")
+}, {
+"duration": 1.0,
+"texture": ExtResource("43_x72jq")
+}, {
+"duration": 1.0,
+"texture": ExtResource("44_xrna7")
+}],
+"loop": false,
+"name": &"up_bite",
+"speed": 3.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("45_f3csq")
+}, {
+"duration": 1.0,
+"texture": ExtResource("46_n7us6")
+}, {
+"duration": 1.0,
+"texture": ExtResource("47_kwyk6")
+}, {
+"duration": 1.0,
+"texture": ExtResource("46_n7us6")
+}],
+"loop": true,
+"name": &"up_full",
+"speed": 6.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("45_f3csq")
+}, {
+"duration": 1.0,
+"texture": ExtResource("48_w8b5g")
+}, {
+"duration": 1.0,
+"texture": ExtResource("12_eyhtl")
+}],
+"loop": false,
+"name": &"up_shrink",
+"speed": 6.0
+}]
+
+[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_1dh0q"]
+radius = 6.0
+height = 14.0
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_7ji0i"]
+
+[node name="Snake" type="CharacterBody2D"]
+collision_layer = 16
+collision_mask = 188
+script = ExtResource("1_h5f33")
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+sprite_frames = SubResource("SpriteFrames_jpiqi")
+animation = &"down"
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("CapsuleShape2D_1dh0q")
+
+[node name="Rays" type="Node2D" parent="."]
+
+[node name="Down" type="RayCast2D" parent="Rays"]
+target_position = Vector2(0, 56)
+collision_mask = 12
+
+[node name="Up" type="RayCast2D" parent="Rays"]
+target_position = Vector2(0, -56)
+collision_mask = 12
+
+[node name="Left" type="RayCast2D" parent="Rays"]
+target_position = Vector2(-56, 0)
+collision_mask = 12
+
+[node name="Right" type="RayCast2D" parent="Rays"]
+target_position = Vector2(56, 0)
+collision_mask = 12
+
+[node name="DetectionArea" type="Area2D" parent="."]
+collision_layer = 0
+collision_mask = 4
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="DetectionArea"]
+shape = SubResource("RectangleShape2D_7ji0i")
+
+[node name="Movement" parent="." instance=ExtResource("14_ab70e")]
+entityPath = NodePath("..")
+spritePath = NodePath("../AnimatedSprite2D")
+timer_time = 2.0
+follows = true
+follow_chance = 80
+
+[node name="Collision" parent="." instance=ExtResource("15_mm88w")]
+entityPath = NodePath("..")
+collision_shape_path = NodePath("../CollisionShape2D")
+component_health_path = NodePath("../Health")
+
+[node name="Health" parent="." instance=ExtResource("16_0dow6")]
+entityPath = NodePath("..")
+spritePath = NodePath("../AnimatedSprite2D")
+component_movement_path = NodePath("../Movement")
+health = 1
+drop_chance = 50
+
+[node name="Timer" type="Timer" parent="."]
+wait_time = 5.0
+one_shot = true
+
+[connection signal="body_entered" from="DetectionArea" to="." method="_on_detection_area_body_entered"]
+[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]
diff --git a/Scenes/Entities/Enemies/Statue.gd b/Scenes/Entities/Enemies/Statue.gd
new file mode 100644
index 0000000..7f1231b
--- /dev/null
+++ b/Scenes/Entities/Enemies/Statue.gd
@@ -0,0 +1,27 @@
+extends StaticBody2D
+
+
+var projectile
+
+@onready var ProjectileScene = preload("res://Scenes/Entities/Enemies/Projectile.tscn")
+
+
+func _on_timer_timeout():
+ if projectile:
+ await projectile.tree_exited
+
+ projectile = ProjectileScene.instantiate()
+
+ projectile.tree_exited.connect(func():
+ projectile = null
+ )
+ projectile.hit.connect(func():
+ Global.player.take_damage(1)
+ )
+
+ projectile.global_position = $ProjectileStart.global_position
+ projectile.target = Global.player.position
+ projectile.scale = Vector2(0.75, 0.75)
+ projectile.speed = 60
+
+ get_tree().current_scene.add_child(projectile)
diff --git a/Scenes/Entities/Enemies/Statue.tscn b/Scenes/Entities/Enemies/Statue.tscn
new file mode 100644
index 0000000..50d8bf2
--- /dev/null
+++ b/Scenes/Entities/Enemies/Statue.tscn
@@ -0,0 +1,30 @@
+[gd_scene load_steps=4 format=3 uid="uid://c2l6n3sjgpy5"]
+
+[ext_resource type="Script" path="res://Scenes/Entities/Enemies/Statue.gd" id="1_n74bh"]
+[ext_resource type="Texture2D" uid="uid://td1476flhtb5" path="res://Assets/Enemies/Statue_2.png" id="2_ra1ju"]
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_oavvo"]
+size = Vector2(16, 32)
+
+[node name="Statue" type="StaticBody2D"]
+collision_layer = 8
+collision_mask = 0
+script = ExtResource("1_n74bh")
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+texture_filter = 1
+position = Vector2(0, -8)
+texture = ExtResource("2_ra1ju")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+position = Vector2(0, -8)
+shape = SubResource("RectangleShape2D_oavvo")
+
+[node name="Timer" type="Timer" parent="."]
+wait_time = 3.0
+autostart = true
+
+[node name="ProjectileStart" type="Node2D" parent="."]
+position = Vector2(0, -8)
+
+[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]