summaryrefslogtreecommitdiff
path: root/Scenes/Entities
diff options
context:
space:
mode:
authorDaniel Weipert <code@drogueronin.de>2023-08-10 11:48:05 +0200
committerDaniel Weipert <code@drogueronin.de>2023-08-10 11:48:05 +0200
commit46556d864b9685c3b09a0038f5de83966fe7ff94 (patch)
treec68082eacd35559e14565d1598dd694972fb8e0e /Scenes/Entities
Initial commit
Diffstat (limited to 'Scenes/Entities')
-rw-r--r--Scenes/Entities/Bombs/Bomb.gd165
-rw-r--r--Scenes/Entities/Bombs/Bomb__Breakables.gd4
-rw-r--r--Scenes/Entities/Bombs/Bomb__Breakables.tscn50
-rw-r--r--Scenes/Entities/Bombs/Bomb__Normal.gd1
-rw-r--r--Scenes/Entities/Bombs/Bomb__Normal.tscn49
-rw-r--r--Scenes/Entities/Bombs/Explosion.gd11
-rw-r--r--Scenes/Entities/Bombs/Explosion.tscn63
-rw-r--r--Scenes/Entities/Enemies/Enemy.gd81
-rw-r--r--Scenes/Entities/Enemies/Enemy.tscn91
-rw-r--r--Scenes/Entities/Objects/Box.gd28
-rw-r--r--Scenes/Entities/Objects/Box.tscn106
-rw-r--r--Scenes/Entities/Objects/Coin.gd16
-rw-r--r--Scenes/Entities/Objects/Coin.tscn21
-rw-r--r--Scenes/Entities/Player.gd202
-rw-r--r--Scenes/Entities/Player.tscn118
15 files changed, 1006 insertions, 0 deletions
diff --git a/Scenes/Entities/Bombs/Bomb.gd b/Scenes/Entities/Bombs/Bomb.gd
new file mode 100644
index 0000000..96a5e62
--- /dev/null
+++ b/Scenes/Entities/Bombs/Bomb.gd
@@ -0,0 +1,165 @@
+extends CharacterBody2D
+
+class_name Bomb
+
+
+signal body_exited
+signal exploded
+
+
+var Explosion = preload("res://Scenes/Entities/Bombs/Explosion.tscn")
+var power = 2
+
+
+func _ready():
+ add_to_group("bombs")
+
+ var collision_area = Utilities.Collision.Area.new(self, $CollisionShape2D)
+ collision_area.connect("collided", Callable(self, "_collide"))
+ collision_area.connect("body_exited", Callable(self, "_body_exited"))
+ add_child(collision_area)
+
+ $AnimatedSprite2D.play()
+
+
+func _process(delta):
+ var collision = move_and_collide(velocity * delta)
+ if collision:
+ velocity = Vector2(0, 0)
+
+
+func explode():
+ $CollisionShape2D.disabled = true
+
+ # explode on the spot
+ var explosion = self.spawn_explosion(self.global_position)
+
+ # explode in all directions
+ var directions = [
+ Vector2.UP,
+ Vector2.RIGHT,
+ Vector2.DOWN,
+ Vector2.LEFT,
+ ]
+
+ for j in range(directions.size()):
+ var direction = directions[j]
+
+ for i in range(self.get_power()):
+ var to = Utilities.from_grid_to_position(
+ Utilities.from_position_to_grid(self.global_position) + (direction * (i + 1))
+ )
+
+ var query = PhysicsPointQueryParameters2D.new()
+ query.set_position(to)
+ query.set_collision_mask(explosion.collision_mask)
+
+ var explosion_intersection = get_world_2d().direct_space_state.intersect_point(query)
+ if explosion_intersection:
+ var collider = explosion_intersection[0].collider
+
+ # call collision function and get collision type
+ var collision_type
+ if collider.is_in_group("player"):
+ collision_type = self._on_collide_group_player()
+ elif collider.is_in_group("enemies"):
+ collision_type = self._on_collide_group_enemies()
+ elif collider.is_in_group("explosions"):
+ collision_type = self._on_collide_group_explosions()
+ elif collider.is_in_group("bombs"):
+ collision_type = self._on_collide_group_bombs()
+ elif collider.is_in_group("breakables"):
+ collision_type = self._on_collide_group_breakables()
+ else:
+ collision_type = self._on_collide_group_else()
+
+ # progress loop based on collision type
+ if collision_type == EXPLOSION_COLLISION_TYPE.STOP:
+ break
+ if collision_type == EXPLOSION_COLLISION_TYPE.SPAWN_AND_STOP:
+ self.spawn_explosion(to)
+ break
+ elif collision_type == EXPLOSION_COLLISION_TYPE.SKIP:
+ continue
+ elif collision_type == EXPLOSION_COLLISION_TYPE.CONTINUE:
+ pass
+ elif collision_type == EXPLOSION_COLLISION_TYPE.HIT_STOP:
+ if collider.has_method("hit_by_explosion"):
+ collider.hit_by_explosion()
+ break
+ elif collision_type == EXPLOSION_COLLISION_TYPE.HIT_CONTINUE:
+ if collider.has_method("hit_by_explosion"):
+ collider.hit_by_explosion()
+ continue
+
+ self.spawn_explosion(to)
+
+ emit_signal("exploded", self)
+
+ queue_free()
+
+
+func get_power():
+ return self.power
+
+
+func spawn_explosion(spawn_position: Vector2):
+ var explosion = Explosion.instantiate()
+ explosion.position = spawn_position
+ get_tree().get_current_scene().add_child(explosion)
+
+ return explosion
+
+
+func hit_by_explosion():
+ call_deferred("explode")
+
+
+func _collide(area: Area2D):
+ if area.is_in_group("explosions"):
+ call_deferred("explode")
+
+
+func _on_Timer_timeout():
+ self.explode()
+
+
+func _body_exited(body):
+ emit_signal("body_exited", body)
+
+
+### Explosion Collision ###
+
+
+enum EXPLOSION_COLLISION_TYPE {
+ STOP,
+ SKIP,
+ CONTINUE,
+ SPAWN_AND_STOP,
+ HIT_STOP,
+ HIT_CONTINUE,
+}
+
+
+func _on_collide_group_player():
+ return EXPLOSION_COLLISION_TYPE.CONTINUE
+
+
+func _on_collide_group_enemies():
+ return EXPLOSION_COLLISION_TYPE.CONTINUE
+
+
+func _on_collide_group_explosions():
+ return EXPLOSION_COLLISION_TYPE.SKIP
+
+
+func _on_collide_group_bombs():
+ return EXPLOSION_COLLISION_TYPE.HIT_STOP
+
+
+func _on_collide_group_breakables():
+ return EXPLOSION_COLLISION_TYPE.HIT_STOP
+
+
+func _on_collide_group_else():
+ return EXPLOSION_COLLISION_TYPE.STOP
diff --git a/Scenes/Entities/Bombs/Bomb__Breakables.gd b/Scenes/Entities/Bombs/Bomb__Breakables.gd
new file mode 100644
index 0000000..177d86e
--- /dev/null
+++ b/Scenes/Entities/Bombs/Bomb__Breakables.gd
@@ -0,0 +1,4 @@
+extends "res://Scenes/Entities/Bombs/Bomb.gd"
+
+func _on_collide_group_breakables():
+ return EXPLOSION_COLLISION_TYPE.HIT_CONTINUE
diff --git a/Scenes/Entities/Bombs/Bomb__Breakables.tscn b/Scenes/Entities/Bombs/Bomb__Breakables.tscn
new file mode 100644
index 0000000..a4bb192
--- /dev/null
+++ b/Scenes/Entities/Bombs/Bomb__Breakables.tscn
@@ -0,0 +1,50 @@
+[gd_scene load_steps=7 format=3 uid="uid://bcdo4xwalfyw2"]
+
+[ext_resource type="Script" path="res://Scenes/Entities/Bombs/Bomb__Breakables.gd" id="1_53dqw"]
+[ext_resource type="Texture2D" uid="uid://tb10jfeilqxk" path="res://Assets/16_bit_animated_bomb/16_bit_bomb3.png" id="2_xuubc"]
+[ext_resource type="Texture2D" uid="uid://ptkpqpfgcoug" path="res://Assets/16_bit_animated_bomb/16bit_bomb1.png" id="3_3gndm"]
+[ext_resource type="Texture2D" uid="uid://dsomaiq14ajhf" path="res://Assets/16_bit_animated_bomb/16_bit_bomb2.png" id="4_u38sl"]
+
+[sub_resource type="CircleShape2D" id="1"]
+radius = 7.9
+
+[sub_resource type="SpriteFrames" id="2"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("2_xuubc")
+}, {
+"duration": 1.0,
+"texture": ExtResource("3_3gndm")
+}, {
+"duration": 1.0,
+"texture": ExtResource("4_u38sl")
+}, {
+"duration": 1.0,
+"texture": ExtResource("2_xuubc")
+}],
+"loop": false,
+"name": &"default",
+"speed": 1.0
+}]
+
+[node name="Bomb__Breakables" type="CharacterBody2D"]
+collision_layer = 4
+collision_mask = 62
+script = ExtResource("1_53dqw")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("1")
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+modulate = Color(1, 0, 0, 1)
+position = Vector2(1, -1)
+scale = Vector2(1.23978, 1.23978)
+sprite_frames = SubResource("2")
+
+[node name="Timer" type="Timer" parent="."]
+wait_time = 4.0
+one_shot = true
+autostart = true
+
+[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"]
diff --git a/Scenes/Entities/Bombs/Bomb__Normal.gd b/Scenes/Entities/Bombs/Bomb__Normal.gd
new file mode 100644
index 0000000..2c89be6
--- /dev/null
+++ b/Scenes/Entities/Bombs/Bomb__Normal.gd
@@ -0,0 +1 @@
+extends "res://Scenes/Entities/Bombs/Bomb.gd"
diff --git a/Scenes/Entities/Bombs/Bomb__Normal.tscn b/Scenes/Entities/Bombs/Bomb__Normal.tscn
new file mode 100644
index 0000000..011c298
--- /dev/null
+++ b/Scenes/Entities/Bombs/Bomb__Normal.tscn
@@ -0,0 +1,49 @@
+[gd_scene load_steps=7 format=3 uid="uid://elsifewesoyx"]
+
+[ext_resource type="Script" path="res://Scenes/Entities/Bombs/Bomb__Normal.gd" id="1_3o4t3"]
+[ext_resource type="Texture2D" uid="uid://tb10jfeilqxk" path="res://Assets/16_bit_animated_bomb/16_bit_bomb3.png" id="2_qio5f"]
+[ext_resource type="Texture2D" uid="uid://ptkpqpfgcoug" path="res://Assets/16_bit_animated_bomb/16bit_bomb1.png" id="3_nom56"]
+[ext_resource type="Texture2D" uid="uid://dsomaiq14ajhf" path="res://Assets/16_bit_animated_bomb/16_bit_bomb2.png" id="4_xndnu"]
+
+[sub_resource type="CircleShape2D" id="1"]
+radius = 7.9
+
+[sub_resource type="SpriteFrames" id="2"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("2_qio5f")
+}, {
+"duration": 1.0,
+"texture": ExtResource("3_nom56")
+}, {
+"duration": 1.0,
+"texture": ExtResource("4_xndnu")
+}, {
+"duration": 1.0,
+"texture": ExtResource("2_qio5f")
+}],
+"loop": false,
+"name": &"default",
+"speed": 1.0
+}]
+
+[node name="Bomb__Normal" type="CharacterBody2D"]
+collision_layer = 4
+collision_mask = 62
+script = ExtResource("1_3o4t3")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("1")
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+position = Vector2(1, -1)
+scale = Vector2(1.23978, 1.23978)
+sprite_frames = SubResource("2")
+
+[node name="Timer" type="Timer" parent="."]
+wait_time = 4.0
+one_shot = true
+autostart = true
+
+[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"]
diff --git a/Scenes/Entities/Bombs/Explosion.gd b/Scenes/Entities/Bombs/Explosion.gd
new file mode 100644
index 0000000..eba8b29
--- /dev/null
+++ b/Scenes/Entities/Bombs/Explosion.gd
@@ -0,0 +1,11 @@
+extends Area2D
+
+
+func _ready():
+ add_to_group("explosions")
+
+ $AnimatedSprite2D.play()
+
+
+func _on_AnimatedSprite_animation_finished():
+ queue_free()
diff --git a/Scenes/Entities/Bombs/Explosion.tscn b/Scenes/Entities/Bombs/Explosion.tscn
new file mode 100644
index 0000000..2521dfb
--- /dev/null
+++ b/Scenes/Entities/Bombs/Explosion.tscn
@@ -0,0 +1,63 @@
+[gd_scene load_steps=8 format=3 uid="uid://c8cg25hagp4lj"]
+
+[ext_resource type="Texture2D" uid="uid://5dk0c1kpvdgs" path="res://Assets/bomb_party_v4.png" id="1"]
+[ext_resource type="Script" path="res://Scenes/Entities/Bombs/Explosion.gd" id="2"]
+
+[sub_resource type="AtlasTexture" id="3"]
+atlas = ExtResource("1")
+region = Rect2(224, 288, 16, 16)
+
+[sub_resource type="AtlasTexture" id="4"]
+atlas = ExtResource("1")
+region = Rect2(224, 272, 16, 16)
+
+[sub_resource type="AtlasTexture" id="5"]
+atlas = ExtResource("1")
+region = Rect2(224, 256, 16, 16)
+
+[sub_resource type="SpriteFrames" id="1"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": SubResource("3")
+}, {
+"duration": 1.0,
+"texture": SubResource("3")
+}, {
+"duration": 1.0,
+"texture": SubResource("4")
+}, {
+"duration": 1.0,
+"texture": SubResource("4")
+}, {
+"duration": 1.0,
+"texture": SubResource("4")
+}, {
+"duration": 1.0,
+"texture": SubResource("5")
+}, {
+"duration": 1.0,
+"texture": SubResource("5")
+}],
+"loop": false,
+"name": &"default",
+"speed": 60.0
+}]
+
+[sub_resource type="CircleShape2D" id="2"]
+radius = 7.5
+
+[node name="Explosion" type="Area2D"]
+collision_layer = 32
+collision_mask = 62
+script = ExtResource("2")
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+position = Vector2(-2.98023e-08, -2.98023e-08)
+scale = Vector2(0.913346, 0.913346)
+sprite_frames = SubResource("1")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("2")
+
+[connection signal="animation_finished" from="AnimatedSprite2D" to="." method="_on_AnimatedSprite_animation_finished"]
diff --git a/Scenes/Entities/Enemies/Enemy.gd b/Scenes/Entities/Enemies/Enemy.gd
new file mode 100644
index 0000000..5b72c04
--- /dev/null
+++ b/Scenes/Entities/Enemies/Enemy.gd
@@ -0,0 +1,81 @@
+extends CharacterBody2D
+
+
+const SPEED = 30
+
+const DIRECTIONS = [Vector2.UP, Vector2.RIGHT, Vector2.DOWN, Vector2.LEFT]
+var CURRENT_DIRECTION = Vector2.UP
+
+@export var health: int = 4
+
+var is_invincible = false
+
+
+func _ready():
+ add_to_group("enemies")
+
+ var collision_area = Utilities.Collision.Area.new(self, $CollisionShape2D)
+ collision_area.connect("collided", Callable(self, "_collide"))
+ add_child(collision_area)
+
+ $Label.text = str(self.health)
+
+
+func _physics_process(delta):
+ if CURRENT_DIRECTION == Vector2.UP:
+ velocity.y -= SPEED
+ $AnimatedSprite2D.play("up")
+ elif CURRENT_DIRECTION == Vector2.DOWN:
+ velocity.y += SPEED
+ $AnimatedSprite2D.play("down")
+ elif CURRENT_DIRECTION == Vector2.LEFT:
+ velocity.x -= SPEED
+ $AnimatedSprite2D.flip_h = true
+ $AnimatedSprite2D.play("left")
+ elif CURRENT_DIRECTION == Vector2.RIGHT:
+ velocity.x += SPEED
+ $AnimatedSprite2D.flip_h = false
+ $AnimatedSprite2D.play("right")
+
+ move_and_collide(velocity * delta)
+ velocity = velocity.lerp(Vector2(0, 0), 1)
+
+
+func take_damage(amount):
+ if self.is_invincible:
+ return
+
+ self.set_invincibility()
+
+ self.health -= amount
+
+ if self.health == 0:
+ queue_free()
+
+ $Label.text = str(self.health)
+
+
+func _collide(area: Area2D):
+ if area.is_in_group("explosions"):
+ self.take_damage(4)
+
+
+func _on_movement_timer_timeout():
+ var directions = self.DIRECTIONS.duplicate()
+
+ directions.remove_at(directions.find(CURRENT_DIRECTION))
+ directions.shuffle()
+
+ CURRENT_DIRECTION = directions[0]
+
+ $MovementTimer.start()
+
+
+func set_invincibility():
+ $InvincibilityTimer.start()
+ $AnimatedSprite2D.set_modulate(Color(10, 1, 1, 1))
+ self.is_invincible = true
+
+func _on_invincibility_timer_timeout():
+ $AnimatedSprite2D.set_modulate(Color(1, 1, 1, 1))
+ self.is_invincible = false
diff --git a/Scenes/Entities/Enemies/Enemy.tscn b/Scenes/Entities/Enemies/Enemy.tscn
new file mode 100644
index 0000000..f78d0e5
--- /dev/null
+++ b/Scenes/Entities/Enemies/Enemy.tscn
@@ -0,0 +1,91 @@
+[gd_scene load_steps=9 format=3 uid="uid://dgyk1sged38ct"]
+
+[ext_resource type="Script" path="res://Scenes/Entities/Enemies/Enemy.gd" id="1_k16kd"]
+[ext_resource type="Texture2D" uid="uid://5dk0c1kpvdgs" path="res://Assets/bomb_party_v4.png" id="2_cjc2s"]
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_gw5gm"]
+size = Vector2(11, 11.5)
+
+[sub_resource type="AtlasTexture" id="AtlasTexture_t7fm8"]
+atlas = ExtResource("2_cjc2s")
+region = Rect2(16, 224, 16, 16)
+
+[sub_resource type="AtlasTexture" id="AtlasTexture_pyhot"]
+atlas = ExtResource("2_cjc2s")
+region = Rect2(64, 224, 16, 16)
+
+[sub_resource type="AtlasTexture" id="AtlasTexture_aj58r"]
+atlas = ExtResource("2_cjc2s")
+region = Rect2(64, 224, 16, 16)
+
+[sub_resource type="AtlasTexture" id="AtlasTexture_spvfr"]
+atlas = ExtResource("2_cjc2s")
+region = Rect2(0, 224, 16, 16)
+
+[sub_resource type="SpriteFrames" id="SpriteFrames_t5cld"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": SubResource("AtlasTexture_t7fm8")
+}],
+"loop": true,
+"name": &"down",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": SubResource("AtlasTexture_pyhot")
+}],
+"loop": true,
+"name": &"left",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": SubResource("AtlasTexture_aj58r")
+}],
+"loop": true,
+"name": &"right",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": SubResource("AtlasTexture_spvfr")
+}],
+"loop": true,
+"name": &"up",
+"speed": 5.0
+}]
+
+[node name="Enemy" type="CharacterBody2D"]
+collision_layer = 16
+collision_mask = 62
+script = ExtResource("1_k16kd")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+position = Vector2(0.5, -0.25)
+shape = SubResource("RectangleShape2D_gw5gm")
+metadata/_edit_lock_ = true
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+sprite_frames = SubResource("SpriteFrames_t5cld")
+animation = &"down"
+
+[node name="Label" type="Label" parent="."]
+offset_left = -5.0
+offset_top = -20.0
+offset_right = 16.0
+offset_bottom = 6.0
+scale = Vector2(0.5, 0.5)
+text = "hp"
+
+[node name="MovementTimer" type="Timer" parent="."]
+wait_time = 3.0
+autostart = true
+
+[node name="InvincibilityTimer" type="Timer" parent="."]
+wait_time = 1.5
+one_shot = true
+
+[connection signal="timeout" from="MovementTimer" to="." method="_on_movement_timer_timeout"]
+[connection signal="timeout" from="InvincibilityTimer" to="." method="_on_invincibility_timer_timeout"]
diff --git a/Scenes/Entities/Objects/Box.gd b/Scenes/Entities/Objects/Box.gd
new file mode 100644
index 0000000..c7b7d4e
--- /dev/null
+++ b/Scenes/Entities/Objects/Box.gd
@@ -0,0 +1,28 @@
+extends StaticBody2D
+
+
+func _ready():
+ var collision_area = Utilities.Collision.Area.new(self, $CollisionShape2D, false)
+ collision_area.connect("collided", Callable(self, "_collide"))
+ add_child(collision_area)
+
+
+func hit_by_explosion():
+ $AnimationPlayer.play("breaking")
+ await $AnimationPlayer.animation_finished
+
+ if randi_range(1, 3) == 1:
+ call_deferred("spawn_coin")
+
+ queue_free()
+
+
+func spawn_coin():
+ var coin = preload("res://Scenes/Entities/Objects/Coin.tscn").instantiate()
+ coin.position = self.position
+ get_tree().get_current_scene().add_child(coin)
+
+
+func _collide(area: Area2D):
+ if area.is_in_group("explosions"):
+ self.hit_by_explosion()
diff --git a/Scenes/Entities/Objects/Box.tscn b/Scenes/Entities/Objects/Box.tscn
new file mode 100644
index 0000000..6acb93f
--- /dev/null
+++ b/Scenes/Entities/Objects/Box.tscn
@@ -0,0 +1,106 @@
+[gd_scene load_steps=7 format=3 uid="uid://bugyo0c505kdw"]
+
+[ext_resource type="Script" path="res://Scenes/Entities/Objects/Box.gd" id="1_owgyi"]
+[ext_resource type="Texture2D" uid="uid://5dk0c1kpvdgs" path="res://Assets/bomb_party_v4.png" id="1_yqw0v"]
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_rwppg"]
+size = Vector2(16, 16)
+
+[sub_resource type="Animation" id="Animation_ihbs5"]
+resource_name = "breaking"
+length = 0.2
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:modulate")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0, 0.1, 0.2),
+"transitions": PackedFloat32Array(1, 1, 1),
+"update": 0,
+"values": [Color(1, 1, 1, 1), Color(1, 1, 0, 1), Color(1, 0, 0, 1)]
+}
+
+[sub_resource type="Animation" id="Animation_5u23n"]
+length = 0.001
+tracks/0/type = "bezier"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:modulate:r")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"handle_modes": PackedInt32Array(0),
+"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0)
+}
+tracks/1/type = "bezier"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath("Sprite2D:modulate:g")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
+tracks/1/keys = {
+"handle_modes": PackedInt32Array(0),
+"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0)
+}
+tracks/2/type = "bezier"
+tracks/2/imported = false
+tracks/2/enabled = true
+tracks/2/path = NodePath("Sprite2D:modulate:b")
+tracks/2/interp = 1
+tracks/2/loop_wrap = true
+tracks/2/keys = {
+"handle_modes": PackedInt32Array(0),
+"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0)
+}
+tracks/3/type = "bezier"
+tracks/3/imported = false
+tracks/3/enabled = true
+tracks/3/path = NodePath("Sprite2D:modulate:a")
+tracks/3/interp = 1
+tracks/3/loop_wrap = true
+tracks/3/keys = {
+"handle_modes": PackedInt32Array(0),
+"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0)
+}
+tracks/4/type = "value"
+tracks/4/imported = false
+tracks/4/enabled = true
+tracks/4/path = NodePath("Sprite2D:modulate")
+tracks/4/interp = 1
+tracks/4/loop_wrap = true
+tracks/4/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Color(1, 1, 0, 1)]
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_cqvgo"]
+_data = {
+"RESET": SubResource("Animation_5u23n"),
+"breaking": SubResource("Animation_ihbs5")
+}
+
+[node name="Box" type="StaticBody2D" groups=["breakables"]]
+collision_layer = 8
+collision_mask = 32
+script = ExtResource("1_owgyi")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("RectangleShape2D_rwppg")
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+texture = ExtResource("1_yqw0v")
+region_enabled = true
+region_rect = Rect2(144.052, 208.08, 16, 16)
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+libraries = {
+"": SubResource("AnimationLibrary_cqvgo")
+}
diff --git a/Scenes/Entities/Objects/Coin.gd b/Scenes/Entities/Objects/Coin.gd
new file mode 100644
index 0000000..77fc6c2
--- /dev/null
+++ b/Scenes/Entities/Objects/Coin.gd
@@ -0,0 +1,16 @@
+extends StaticBody2D
+
+
+func _ready():
+ var collision_area = Utilities.Collision.Area.new(self, $CollisionShape2D, false)
+ collision_area.connect("collided", Callable(self, "_collide"))
+ add_child(collision_area)
+
+
+func hit_by_explosion():
+ queue_free()
+
+
+func _collide(area: Area2D):
+ if area.is_in_group("player"):
+ queue_free()
diff --git a/Scenes/Entities/Objects/Coin.tscn b/Scenes/Entities/Objects/Coin.tscn
new file mode 100644
index 0000000..aacb70b
--- /dev/null
+++ b/Scenes/Entities/Objects/Coin.tscn
@@ -0,0 +1,21 @@
+[gd_scene load_steps=4 format=3 uid="uid://cl7jri45a43t0"]
+
+[ext_resource type="Script" path="res://Scenes/Entities/Objects/Coin.gd" id="1_kjrye"]
+[ext_resource type="Texture2D" uid="uid://5dk0c1kpvdgs" path="res://Assets/bomb_party_v4.png" id="2_0mghm"]
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_rwppg"]
+size = Vector2(16, 16)
+
+[node name="Coin" type="StaticBody2D" groups=["breakables"]]
+collision_layer = 8
+collision_mask = 34
+script = ExtResource("1_kjrye")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("RectangleShape2D_rwppg")
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+self_modulate = Color(1, 0, 1, 1)
+texture = ExtResource("2_0mghm")
+region_enabled = true
+region_rect = Rect2(48.8626, 208.456, 15.1374, 15.5442)
diff --git a/Scenes/Entities/Player.gd b/Scenes/Entities/Player.gd
new file mode 100644
index 0000000..a578bbb
--- /dev/null
+++ b/Scenes/Entities/Player.gd
@@ -0,0 +1,202 @@
+extends CharacterBody2D
+
+class_name Player
+
+
+signal damaged
+
+
+const SPEED = 60
+const KICK_SPEED = 300
+const THROW_DISTANCE = 3
+
+@export var Bomb: PackedScene = preload("res://Scenes/Entities/Bombs/Bomb__Normal.tscn")
+@export var bomb_power: int = 2
+
+@export var maxHealth: int = 12
+@export var health: int = self.maxHealth
+@export var extraHealth: int = 5
+
+var is_invincible = false
+
+var held_bomb: Bomb
+
+var DIRECTION = Vector2.DOWN
+
+var collision_area: Area2D
+
+
+
+func _ready():
+ add_to_group("player")
+ set_up_direction(Vector2.UP)
+ motion_mode = CharacterBody2D.MOTION_MODE_FLOATING
+
+ collision_area = Utilities.Collision.Area.new(self, $CollisionShape2D)
+ collision_area.connect("collided", Callable(self, "_collide"))
+ add_child(collision_area)
+
+
+func _process(delta):
+ if Input.is_action_pressed("ui_left"):
+ velocity.x -= SPEED
+ $AnimatedSprite2D.play("left")
+ self.DIRECTION = Vector2.LEFT
+ if Input.is_action_pressed("ui_right"):
+ velocity.x += SPEED
+ $AnimatedSprite2D.play("right")
+ self.DIRECTION = Vector2.RIGHT
+ if Input.is_action_pressed("ui_up"):
+ velocity.y -= SPEED
+ $AnimatedSprite2D.play("up")
+ self.DIRECTION = Vector2.UP
+ if Input.is_action_pressed("ui_down"):
+ velocity.y += SPEED
+ $AnimatedSprite2D.play("down")
+ self.DIRECTION = Vector2.DOWN
+
+ if velocity.x < 0 && velocity.y < 0:
+ $AnimatedSprite2D.play("tl")
+ elif velocity.x > 0 && velocity.y < 0:
+ $AnimatedSprite2D.play("tr")
+ elif velocity.x < 0 && velocity.y > 0:
+ $AnimatedSprite2D.play("bl")
+ elif velocity.x > 0 && velocity.y > 0:
+ $AnimatedSprite2D.play("br")
+
+ if Input.is_action_just_pressed("ui_accept"):
+ if self.held_bomb:
+ self.throw_bomb()
+ else:
+ var interacted = false
+ var areas = $InteractionArea.get_overlapping_areas()
+ for area in areas:
+ if area.is_in_group("bombs"):
+ var bomb = area.get_parent()
+ self.pick_up_bomb(bomb)
+
+ interacted = true
+ break
+
+ if not interacted:
+ self.plant_bomb()
+
+ self.collide(move_and_collide(velocity * delta))
+ velocity = velocity.lerp(Vector2(0, 0), 1)
+
+
+func plant_bomb():
+ var bomb = Bomb.instantiate()
+ bomb.position = Utilities.get_level_position_grid(self)
+ bomb.power = self.bomb_power
+
+ self.add_collision_exception_with(bomb)
+ bomb.connect("body_exited", func(body):
+ if body.is_in_group("player") and not self.held_bomb:
+ self.remove_collision_exception_with(bomb)
+ )
+
+ get_tree().get_current_scene().add_child(bomb)
+
+
+func pick_up_bomb(bomb: Bomb):
+ get_tree().get_current_scene().remove_child(bomb)
+ bomb.position = Vector2(0, 0)
+ bomb.set_collision_layer_value(Utilities.Collision.Layer.BOMB, false)
+ self.add_collision_exception_with(bomb)
+ self.add_child(bomb)
+
+ self.held_bomb = bomb
+ bomb.connect("exploded", func(bomb):
+ if self.held_bomb == bomb:
+ self.held_bomb = null
+ )
+
+
+func throw_bomb():
+ var bomb = self.held_bomb
+
+ self.remove_child(bomb)
+
+ var target_position = null
+ var target_intersection = true
+ var additional_distance = 0
+ while target_intersection:
+ target_position = Utilities.from_grid_to_position(
+ Utilities.from_position_to_grid(self.position) + ((self.THROW_DISTANCE + additional_distance) * self.DIRECTION)
+ )
+
+ var query = PhysicsPointQueryParameters2D.new()
+ query.set_position(target_position)
+ target_intersection = get_world_2d().direct_space_state.intersect_point(query)
+
+ additional_distance += 1
+
+ bomb.position = target_position
+
+ get_tree().get_current_scene().add_child(bomb)
+
+ bomb.set_collision_layer_value(Utilities.Collision.Layer.BOMB, true)
+ (func(): self.remove_collision_exception_with(bomb)).call_deferred()
+
+ self.held_bomb = null
+
+
+func collide(collision: KinematicCollision2D):
+ if not collision:
+ return
+
+ var collider = collision.get_collider()
+
+ if collider.is_in_group("bombs"):
+ self.kick_bomb(collider)
+
+
+func kick_bomb(bomb):
+ var diff = Utilities.get_level_position(self) - Utilities.get_level_position(bomb)
+ if diff.x > 0:
+ bomb.velocity.x -= KICK_SPEED
+ elif diff.x < 0:
+ bomb.velocity.x += KICK_SPEED
+ elif diff.y > 0:
+ bomb.velocity.y -= KICK_SPEED
+ elif diff.y < 0:
+ bomb.velocity.y += KICK_SPEED
+
+
+func take_damage(amount):
+ if self.held_bomb:
+ self.held_bomb.call_deferred("explode")
+
+ if self.is_invincible:
+ return
+
+ self.set_invincibility()
+
+ if self.extraHealth > 0:
+ if amount > self.extraHealth:
+ self.health -= amount - self.extraHealth
+ self.extraHealth = 0
+ else:
+ self.extraHealth -= amount
+ else:
+ self.health -= amount
+
+ self.emit_signal("damaged", self.health)
+
+
+func set_invincibility():
+ $Invincibility.start()
+ $AnimatedSprite2D.set_modulate(Color(10, 10, 10, 1))
+ self.is_invincible = true
+
+func _on_invincibility_timeout():
+ $AnimatedSprite2D.set_modulate(Color(1, 1, 1, 1))
+ self.is_invincible = false
+
+
+func _collide(area: Area2D):
+ if area.is_in_group("explosions"):
+ self.take_damage(2)
+ elif area.is_in_group("enemies"):
+ self.take_damage(1)
diff --git a/Scenes/Entities/Player.tscn b/Scenes/Entities/Player.tscn
new file mode 100644
index 0000000..f4ef6ab
--- /dev/null
+++ b/Scenes/Entities/Player.tscn
@@ -0,0 +1,118 @@
+[gd_scene load_steps=13 format=3 uid="uid://b1xhgqwrw4pgs"]
+
+[ext_resource type="Texture2D" uid="uid://dtvvpqc7i2dm6" path="res://Assets/tux/signal-2021-05-05-214118_001.png" id="1"]
+[ext_resource type="Script" path="res://Scenes/Entities/Player.gd" id="1_2xulf"]
+[ext_resource type="Texture2D" uid="uid://o7x47dkwao04" path="res://Assets/tux/signal-2021-05-05-214118_002.png" id="2"]
+[ext_resource type="Texture2D" uid="uid://4lhw1rn7w1ye" path="res://Assets/tux/signal-2021-05-05-214118_003.png" id="3"]
+[ext_resource type="Texture2D" uid="uid://d354aghbycxto" path="res://Assets/tux/signal-2021-05-05-214118_004.png" id="4"]
+[ext_resource type="Texture2D" uid="uid://3jnv1c847c3" path="res://Assets/tux/signal-2021-05-06-203546_001.png" id="5"]
+[ext_resource type="Texture2D" uid="uid://c3o4wky2ywpng" path="res://Assets/tux/signal-2021-05-06-203546_004.png" id="6"]
+[ext_resource type="Texture2D" uid="uid://b5v0jg6lwnij2" path="res://Assets/tux/signal-2021-05-06-203546_003.png" id="7"]
+[ext_resource type="Texture2D" uid="uid://cnxxo0qivbv4c" path="res://Assets/tux/signal-2021-05-06-203546_002.png" id="7_bxbay"]
+
+[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_xsiww"]
+radius = 5.0
+height = 14.0
+
+[sub_resource type="SpriteFrames" id="2"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("7")
+}],
+"loop": true,
+"name": &"bl",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("6")
+}],
+"loop": true,
+"name": &"br",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("3")
+}],
+"loop": true,
+"name": &"down",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("4")
+}],
+"loop": true,
+"name": &"left",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("1")
+}],
+"loop": true,
+"name": &"right",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("7_bxbay")
+}],
+"loop": true,
+"name": &"tl",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("5")
+}],
+"loop": true,
+"name": &"tr",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("2")
+}],
+"loop": true,
+"name": &"up",
+"speed": 5.0
+}]
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_c6666"]
+size = Vector2(14, 14)
+
+[node name="Player" type="CharacterBody2D"]
+collision_layer = 2
+collision_mask = 60
+script = ExtResource("1_2xulf")
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("CapsuleShape2D_xsiww")
+
+[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
+position = Vector2(5.96046e-08, 5.96046e-08)
+scale = Vector2(0.405234, 0.405234)
+sprite_frames = SubResource("2")
+animation = &"down"
+metadata/_edit_lock_ = true
+
+[node name="Camera2D" type="Camera2D" parent="."]
+drag_horizontal_enabled = true
+drag_vertical_enabled = true
+
+[node name="InteractionArea" type="Area2D" parent="."]
+collision_layer = 0
+collision_mask = 4
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="InteractionArea"]
+position = Vector2(0, 1)
+shape = SubResource("RectangleShape2D_c6666")
+
+[node name="Invincibility" type="Timer" parent="."]
+wait_time = 1.5
+one_shot = true
+
+[connection signal="timeout" from="Invincibility" to="." method="_on_invincibility_timeout"]