diff --git a/objects/brick_player.tscn b/objects/brick_player.tscn index 2e6c18b..3f31fa6 100644 --- a/objects/brick_player.tscn +++ b/objects/brick_player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=24 format=3 uid="uid://bqi5s710xb1ju"] +[gd_scene load_steps=26 format=3 uid="uid://bqi5s710xb1ju"] [ext_resource type="Script" path="res://scripts/player.gd" id="1_8j4h4"] [ext_resource type="Texture2D" uid="uid://b7gp0gqvkv8j4" path="res://sprites/MrBrick_base.png" id="2_bc55y"] @@ -9,17 +9,19 @@ [ext_resource type="Script" path="res://scripts/components/health.gd" id="7_tqjk8"] [ext_resource type="Script" path="res://scripts/components/player_death.gd" id="8_1v23d"] [ext_resource type="Script" path="res://scripts/components/knockback.gd" id="9_rjyu4"] -[ext_resource type="Script" path="res://scripts/components/brick_throw.gd" id="10_u0v3b"] -[ext_resource type="PackedScene" uid="uid://bymro4t7angv5" path="res://objects/brick.tscn" id="11_4d0cj"] [ext_resource type="Script" path="res://scripts/components/stomp_damage_component.gd" id="12_payr4"] [ext_resource type="Script" path="res://scripts/components/flashing_component.gd" id="13_hrtyn"] [ext_resource type="Script" path="res://scripts/components/invulnerability_component.gd" id="14_jopig"] -[ext_resource type="Script" path="res://scripts/components/magnetic_skill.gd" id="15_4df3h"] [ext_resource type="Script" path="res://scripts/components/can_be_launched_component.gd" id="16_kemlv"] +[ext_resource type="Resource" uid="uid://dw5ee2lpeypnb" path="res://resources/skills/brick_throw.tres" id="16_smbir"] [ext_resource type="Script" path="res://scripts/components/trigger_lever_component.gd" id="17_hglfj"] [ext_resource type="AudioStream" uid="uid://duj2q0rqytaxg" path="res://sfx/jump.wav" id="18_pysae"] [ext_resource type="AudioStream" uid="uid://bmfn6p88gy575" path="res://sfx/player_hurt.wav" id="19_7anly"] [ext_resource type="AudioStream" uid="uid://ycgtf6wj7mto" path="res://sfx/heal.wav" id="20_bptj5"] +[ext_resource type="Script" path="res://scripts/skill_manager.gd" id="20_ppfy7"] +[ext_resource type="Script" path="res://scripts/resources/skill_data.gd" id="21_d0oiv"] +[ext_resource type="Resource" uid="uid://d3bjre2etov1n" path="res://resources/skills/magnetic.tres" id="22_vnsgd"] +[ext_resource type="Script" path="res://scripts/components/skill_unlocker_component.gd" id="23_qsv2c"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_hdsg1"] size = Vector2(16, 31) @@ -90,20 +92,11 @@ script = ExtResource("9_rjyu4") character_body = NodePath("..") knockback_force = 1500.0 -[node name="BrickThrowComponent" type="Node" parent="." node_paths=PackedStringArray("player_controller", "timer")] -script = ExtResource("10_u0v3b") -brick_scene = ExtResource("11_4d0cj") -fire_rate = 0.3 -player_controller = NodePath("..") -timer = NodePath("../ThrowTimer") - [node name="ThrowTimer" type="Timer" parent="."] -[node name="StompDamageComponent" type="Node" parent="." node_paths=PackedStringArray("area2d", "root")] -script = ExtResource("12_payr4") -damage = 4.0 -area2d = NodePath("../StompDamageArea") -root = NodePath("..") +[node name="InvulnerabilityComponent" type="Node" parent="." node_paths=PackedStringArray("flashing_component")] +script = ExtResource("14_jopig") +flashing_component = NodePath("../FlashingComponent Base") [node name="FlashingComponent Base" type="Node" parent="." node_paths=PackedStringArray("sprite", "health_component")] script = ExtResource("13_hrtyn") @@ -123,15 +116,19 @@ sprite = NodePath("../Root/Right Eye") flash_duration = 1.0 health_component = NodePath("../HealthComponent") -[node name="InvulnerabilityComponent" type="Node" parent="." node_paths=PackedStringArray("flashing_component")] -script = ExtResource("14_jopig") -flashing_component = NodePath("../FlashingComponent Base") - -[node name="MagneticSkillComponent" type="Node" parent="." node_paths=PackedStringArray("magnetic_area", "root")] -script = ExtResource("15_4df3h") -magnetic_area = NodePath("../MagneticArea") +[node name="StompDamageComponent" type="Node" parent="." node_paths=PackedStringArray("area2d", "root")] +script = ExtResource("12_payr4") +damage = 4.0 +area2d = NodePath("../StompDamageArea") root = NodePath("..") -magnetic_move_duration = 0.9 + +[node name="SkillManager" type="Node" parent="."] +script = ExtResource("20_ppfy7") +available_skills = Array[ExtResource("21_d0oiv")]([ExtResource("22_vnsgd"), ExtResource("16_smbir")]) + +[node name="SkillUnlockerComponent" type="Node" parent="." node_paths=PackedStringArray("skill_manager")] +script = ExtResource("23_qsv2c") +skill_manager = NodePath("../SkillManager") [node name="MagneticArea" type="Area2D" parent="."] collision_layer = 0 diff --git a/objects/enemy.tscn b/objects/enemy.tscn index c6659b5..7420fb2 100644 --- a/objects/enemy.tscn +++ b/objects/enemy.tscn @@ -24,7 +24,7 @@ shader_parameter/enabled = false shader_parameter/tint = Color(1, 1, 1, 1) [sub_resource type="RectangleShape2D" id="RectangleShape2D_ejbqt"] -size = Vector2(25, 31) +size = Vector2(34, 31) [node name="Enemy" type="CharacterBody2D"] collision_layer = 8 @@ -61,7 +61,6 @@ left_ray = NodePath("../Left Ray") right_ray = NodePath("../Right Ray") [node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("side_to_side_movement", "root", "bullet_spawn_right", "bullet_spawn_left")] -process_mode = 3 script = ExtResource("5_m03v0") side_to_side_movement = NodePath("../SideToSideMovement") root = NodePath("..") @@ -80,7 +79,7 @@ collision_layer = 8 collision_mask = 20 [node name="CollisionShape2D" type="CollisionShape2D" parent="Hitbox"] -position = Vector2(-1.5, 0.5) +position = Vector2(-2, 0.5) shape = SubResource("RectangleShape2D_ejbqt") debug_color = Color(0.913521, 0.265052, 0.323172, 0.42) diff --git a/objects/game_manager.tscn b/objects/game_manager.tscn index a00d4a5..53496ab 100644 --- a/objects/game_manager.tscn +++ b/objects/game_manager.tscn @@ -2,5 +2,5 @@ [ext_resource type="Script" path="res://scripts/game_manager.gd" id="1_58t7u"] -[node name="Node" type="Node"] +[node name="GameManager" type="Node"] script = ExtResource("1_58t7u") diff --git a/objects/killzone.tscn b/objects/killzone.tscn new file mode 100644 index 0000000..e260582 --- /dev/null +++ b/objects/killzone.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=3 uid="uid://bqom4cm7r18db"] + +[ext_resource type="Script" path="res://scripts/components/damage_component.gd" id="1_un3fl"] + +[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_cr00c"] + +[node name="Killzone" type="Area2D"] +collision_layer = 0 +collision_mask = 4 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("WorldBoundaryShape2D_cr00c") + +[node name="DamageComponent" type="Node" parent="." node_paths=PackedStringArray("area2d")] +script = ExtResource("1_un3fl") +damage = 999999.0 +area2d = NodePath("..") diff --git a/objects/player_skills/brick_throw_skill.tscn b/objects/player_skills/brick_throw_skill.tscn new file mode 100644 index 0000000..99d851e --- /dev/null +++ b/objects/player_skills/brick_throw_skill.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=3 format=3 uid="uid://coayig4dxelo2"] + +[ext_resource type="Script" path="res://scripts/components/brick_throw.gd" id="1_hniwk"] +[ext_resource type="PackedScene" uid="uid://bymro4t7angv5" path="res://objects/brick.tscn" id="2_4txoq"] + +[node name="BrickThrowComponent" type="Node"] +script = ExtResource("1_hniwk") +brick_scene = ExtResource("2_4txoq") +fire_rate = 0.3 diff --git a/objects/player_skills/magnetic_skill.tscn b/objects/player_skills/magnetic_skill.tscn new file mode 100644 index 0000000..7db1dcb --- /dev/null +++ b/objects/player_skills/magnetic_skill.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cunyndudjh2he"] + +[ext_resource type="Script" path="res://scripts/components/magnetic_skill.gd" id="1_lwbas"] + +[node name="MagneticSkill" type="Node"] +script = ExtResource("1_lwbas") diff --git a/project.godot b/project.godot index 4f29b8f..452829f 100644 --- a/project.godot +++ b/project.godot @@ -34,7 +34,6 @@ GUIDE="*res://addons/guide/guide.gd" window/size/viewport_width=640 window/size/viewport_height=360 -window/size/initial_position=Vector2i(420, 0) window/size/window_width_override=1280 window/size/window_height_override=720 window/stretch/mode="viewport" @@ -83,6 +82,11 @@ attack={ , Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(416, 6),"global_position":Vector2(425, 50),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) ] } +unlock_skills={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} [layer_names] diff --git a/resources/skills/brick_throw.tres b/resources/skills/brick_throw.tres new file mode 100644 index 0000000..7f0ac85 --- /dev/null +++ b/resources/skills/brick_throw.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" script_class="SkillData" load_steps=3 format=3 uid="uid://dw5ee2lpeypnb"] + +[ext_resource type="PackedScene" uid="uid://coayig4dxelo2" path="res://objects/player_skills/brick_throw_skill.tscn" id="1_5gnea"] +[ext_resource type="Script" path="res://scripts/resources/skill_data.gd" id="1_etxe2"] + +[resource] +script = ExtResource("1_etxe2") +name = "Brick power" +description = "Allows you to throw bricks at enemies" +node = ExtResource("1_5gnea") +config = { +"player_controller": NodePath("."), +"timer": NodePath("ThrowTimer") +} +cost = 50 diff --git a/resources/skills/magnetic.tres b/resources/skills/magnetic.tres new file mode 100644 index 0000000..8104ddb --- /dev/null +++ b/resources/skills/magnetic.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" script_class="SkillData" load_steps=3 format=3 uid="uid://d3bjre2etov1n"] + +[ext_resource type="PackedScene" uid="uid://cunyndudjh2he" path="res://objects/player_skills/magnetic_skill.tscn" id="1_er41s"] +[ext_resource type="Script" path="res://scripts/resources/skill_data.gd" id="1_r01oq"] + +[resource] +script = ExtResource("1_r01oq") +name = "Magnetic" +description = "Attract coins" +node = ExtResource("1_er41s") +config = { +"magnetic_area": NodePath("MagneticArea"), +"magnetic_move_duration": 1.25, +"root": NodePath(".") +} +cost = 70 diff --git a/scenes/test.tscn b/scenes/test.tscn index bfd5f24..7fd2cae 100644 --- a/scenes/test.tscn +++ b/scenes/test.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=30 format=4 uid="uid://h60obxmju6mo"] +[gd_scene load_steps=31 format=4 uid="uid://h60obxmju6mo"] [ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_5lb42"] [ext_resource type="TileSet" uid="uid://cl4bn8lofqvky" path="res://tileset/village/tileset_village.tres" id="1_d680t"] @@ -18,6 +18,7 @@ [ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd" id="13_rsy5s"] [ext_resource type="Script" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="14_mjvn7"] [ext_resource type="PackedScene" uid="uid://dtjrpkhssq32a" path="res://objects/bg.tscn" id="14_ws5fk"] +[ext_resource type="PackedScene" uid="uid://bqom4cm7r18db" path="res://objects/killzone.tscn" id="20_8a4vc"] [ext_resource type="PackedScene" uid="uid://d0s2abysa86rq" path="res://objects/child.tscn" id="21_8rhdx"] [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_cagp7"] @@ -478,3 +479,6 @@ position = Vector2(877, -577) [node name="CanvasLayer" type="CanvasLayer" parent="."] [node name="HUD" parent="CanvasLayer" instance=ExtResource("1_gbpkv")] + +[node name="Killzone" parent="." instance=ExtResource("20_8a4vc")] +position = Vector2(0, 990) diff --git a/scripts/components/damage_component.gd b/scripts/components/damage_component.gd index 3a47220..f7ee0b9 100644 --- a/scripts/components/damage_component.gd +++ b/scripts/components/damage_component.gd @@ -4,6 +4,9 @@ extends Node @export var damage: float = 0.25 @export var area2d: Area2D @export var status_effect_data: StatusEffectDataResource +@export var damage_timer: Timer + +var current_target: Node = null signal effect_inflicted(target: Node2D, effect: StatusEffectDataResource) @@ -13,13 +16,34 @@ func _ready() -> void: return area2d.body_entered.connect(on_area2d_body_entered) + area2d.body_exited.connect(on_area2d_body_exited) + area2d.area_entered.connect(on_area2d_area_entered) + + if damage_timer: + damage_timer.timeout.connect(on_damage_timer_timeout) + + +func _process(_delta: float) -> void: + if not current_target: + return + if damage_timer: + return + + process_entity_and_apply_damage(current_target) func deal_damage(target: HealthComponent) -> void: target.decrease_health(damage) -func on_area2d_body_entered(body: Node2D) -> void: +func on_damage_timer_timeout() -> void: + if not current_target: + return + + process_entity_and_apply_damage(current_target) + + +func process_entity_and_apply_damage(body: Node2D) -> void: if body.has_node("HealthComponent"): var health_component: HealthComponent = body.get_node("HealthComponent") var invulnerability_component: InvulnerabilityComponent = body.get_node_or_null("InvulnerabilityComponent") @@ -34,3 +58,27 @@ func on_area2d_body_entered(body: Node2D) -> void: if invulnerability_component: invulnerability_component.activate() + + +func on_area2d_body_entered(body: Node2D) -> void: + current_target = body + + if damage_timer: + damage_timer.start() + + process_entity_and_apply_damage(body) + + +func on_area2d_body_exited(body: Node2D) -> void: + if body == current_target: + current_target = null + if damage_timer: + damage_timer.stop() + + +func on_area2d_area_entered(area: Area2D) -> void: + if area == area2d: + return + var parent := area.get_parent() + if parent.has_node("DamageComponent"): + process_entity_and_apply_damage(parent) diff --git a/scripts/components/skill_unlocker_component.gd b/scripts/components/skill_unlocker_component.gd new file mode 100644 index 0000000..40ac750 --- /dev/null +++ b/scripts/components/skill_unlocker_component.gd @@ -0,0 +1,34 @@ +class_name SkillUnlockerComponent +extends Node + +@export var skill_manager: SkillManager + +@onready var game_manager: GM = $"/root/GameManager" + + +func try_unlock_skill(skill_data: SkillData) -> void: + if not game_manager: + return + + if game_manager.is_skill_unlocked(skill_data.name): + return + + if game_manager.get_coins() < skill_data.cost: + return + + game_manager.remove_coins(skill_data.cost) + game_manager.unlock_skill(skill_data.name) + skill_manager.add_skill(skill_data) + + +func _input(event: InputEvent) -> void: + if event.is_action_pressed("unlock_skills"): + var available_skills: Array[SkillData] = skill_manager.available_skills + var skills: Array[String] = [] + + for skill in available_skills: + skills.append(skill.name) + + game_manager.unlock_skills(skills) + skill_manager.apply_unlocked_skills() + print("Unlocked skills: ", skills) diff --git a/scripts/game_manager.gd b/scripts/game_manager.gd index 0c5f17b..89b978d 100644 --- a/scripts/game_manager.gd +++ b/scripts/game_manager.gd @@ -4,6 +4,7 @@ extends Node var player_state = { "coins": 0, "lives": 3, + "unlocked_skills": [], } @@ -36,4 +37,18 @@ func set_lives(amount: int) -> void: func get_lives() -> int: - return player_state["lives"] \ No newline at end of file + return player_state["lives"] + + +func is_skill_unlocked(skill_name: String) -> bool: + return skill_name in player_state["unlocked_skills"] + + +func unlock_skill(skill_name: String) -> void: + if not is_skill_unlocked(skill_name): + player_state["unlocked_skills"].append(skill_name) + + +func unlock_skills(skill_names: Array[String]) -> void: + for skill_name in skill_names: + unlock_skill(skill_name) \ No newline at end of file diff --git a/scripts/resources/skill_data.gd b/scripts/resources/skill_data.gd new file mode 100644 index 0000000..8cadbec --- /dev/null +++ b/scripts/resources/skill_data.gd @@ -0,0 +1,9 @@ +class_name SkillData +extends Resource + +@export var name: String = "" +@export var description: String = "" +@export var node: PackedScene +@export var config: Dictionary = {} +@export var cost: int = 0 +@export var icon: Texture2D \ No newline at end of file diff --git a/scripts/save_system.gd b/scripts/save_system.gd new file mode 100644 index 0000000..34245f1 --- /dev/null +++ b/scripts/save_system.gd @@ -0,0 +1,32 @@ +class_name SaveSystem +extends Node + +@export var save_path: String = "user://savegame.save" +@export var version: int = 1 + +@onready var gm: GM = $"/root/GameManager" + + +func save_game(): + var save_data := { + "player_state": gm.player_state, + "version": version, + } + var file := FileAccess.open(save_path, FileAccess.WRITE) + file.store_var(save_data) + file.close() + + +func load_game(): + if not FileAccess.file_exists(save_path): + return + var file := FileAccess.open(save_path, FileAccess.READ) + var save_data: Dictionary = file.get_var() + file.close() + + if save_data.has("version") and save_data["version"] != version: + print("Save file version mismatch. Expected: ", version, ", Found: ", save_data["version"]) + return + + gm.player_state = save_data["player_state"] + gm.unlock_skills(gm.player_state["unlocked_skills"]) \ No newline at end of file diff --git a/scripts/skill_manager.gd b/scripts/skill_manager.gd new file mode 100644 index 0000000..1be0ee7 --- /dev/null +++ b/scripts/skill_manager.gd @@ -0,0 +1,57 @@ +class_name SkillManager +extends Node + +@export var available_skills: Array[SkillData] = [] + +@onready var gm: GM = $"/root/GameManager" + +var active_components: Dictionary = {} + + +func _ready() -> void: + apply_unlocked_skills() + + +func add_skill(skill_data: SkillData) -> void: + if active_components.has(skill_data.name): + return + + var skill_instance := skill_data.node.instantiate() + for key in skill_data.config.keys(): + if key in skill_instance: + var value = skill_data.config[key] + var parent := get_parent() + + if value is NodePath: + if parent.has_node(value): + value = parent.get_node(value) + elif skill_instance.has_node(value): + value = skill_instance.get_node(value) + else: + print("NodePath not found: ", value) + continue + + skill_instance[key] = value + + add_child(skill_instance) + active_components[skill_data.name] = skill_instance + + +func remove_skill(skill_name: String) -> void: + if not active_components.has(skill_name): + return + + var skill_instance = active_components[skill_name] + if is_instance_valid(skill_instance): + skill_instance.queue_free() + + active_components.erase(skill_name) + + +func apply_unlocked_skills() -> void: + for skill_data in available_skills: + if gm.is_skill_unlocked(skill_data.name): + print("Applying skill: ", skill_data.name) + add_skill(skill_data) + else: + remove_skill(skill_data.name)