From 6fff2dd9a32f7d8c6401f1011106032662d46771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Fri, 22 Aug 2025 20:50:11 +0200 Subject: [PATCH] game: add bleed --- export_presets.cfg | 2 +- scenes/enemies/effects/enemy_effect_base.gd | 7 ++++ .../enemies/effects/enemy_effect_base.gd.uid | 1 + scenes/enemies/effects/enemy_effect_bleed.gd | 29 ++++++++++++++++ .../enemies/effects/enemy_effect_bleed.gd.uid | 1 + scenes/enemies/enemy_base.gd | 17 ++++++++-- scenes/enemies/enemy_base.tscn | 25 ++++++++------ scenes/managers/enemy_manager.gd | 1 - scenes/player.gd | 6 +--- scenes/puddle.gd | 21 ++++++++++++ scenes/puddle.gd.uid | 1 + scenes/puddle.tscn | 34 +++++++++++++++++++ scenes/weapons/weapon_base.gd | 26 ++++++++++++++ scenes/weapons/weapon_sword.gd | 19 ++++++----- scenes/weapons/weapon_sword_projectile.gd | 1 + 15 files changed, 163 insertions(+), 28 deletions(-) create mode 100644 scenes/enemies/effects/enemy_effect_base.gd create mode 100644 scenes/enemies/effects/enemy_effect_base.gd.uid create mode 100644 scenes/enemies/effects/enemy_effect_bleed.gd create mode 100644 scenes/enemies/effects/enemy_effect_bleed.gd.uid create mode 100644 scenes/puddle.gd create mode 100644 scenes/puddle.gd.uid create mode 100644 scenes/puddle.tscn diff --git a/export_presets.cfg b/export_presets.cfg index edb44e9..bed71b6 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -3,7 +3,7 @@ name="Linux" platform="Linux" runnable=true -advanced_options=false +advanced_options=true dedicated_server=false custom_features="" export_filter="all_resources" diff --git a/scenes/enemies/effects/enemy_effect_base.gd b/scenes/enemies/effects/enemy_effect_base.gd new file mode 100644 index 0000000..4f3eeab --- /dev/null +++ b/scenes/enemies/effects/enemy_effect_base.gd @@ -0,0 +1,7 @@ +class_name EnemyEffectBase +extends Resource + +var enemy: EnemyBase + +func apply(enemy: EnemyBase) -> void: + push_error("%s does not implement apply" % self) diff --git a/scenes/enemies/effects/enemy_effect_base.gd.uid b/scenes/enemies/effects/enemy_effect_base.gd.uid new file mode 100644 index 0000000..b39e34a --- /dev/null +++ b/scenes/enemies/effects/enemy_effect_base.gd.uid @@ -0,0 +1 @@ +uid://dg3yshxb4vdiu diff --git a/scenes/enemies/effects/enemy_effect_bleed.gd b/scenes/enemies/effects/enemy_effect_bleed.gd new file mode 100644 index 0000000..76b8fa2 --- /dev/null +++ b/scenes/enemies/effects/enemy_effect_bleed.gd @@ -0,0 +1,29 @@ +class_name EnemyEffectBleed +extends EnemyEffectBase + +var damage: float +var duration: float +var tick_rate: float = 1.0 + +var _timer: Timer +var _remaining_ticks: int = 5 +var _enemy: EnemyBase + +const PUDDLE = preload("res://scenes/puddle.tscn") + +func _init(enemy: EnemyBase, damage: float, duration: float): + _timer = Timer.new() + +func apply(enemy: EnemyBase) -> void: + enemy.effects.append(self) + while _remaining_ticks > 0: + _remaining_ticks -= 1 + var tree = enemy.get_tree() + await enemy.get_tree().create_timer(1.0, false, true, false).timeout + var p = PUDDLE.instantiate() + enemy.get_parent().add_child(p) + p.global_position = enemy.global_position + enemy.effects.erase(self) + +static func _is_bleeding(enemy: EnemyBase) -> bool: + return false diff --git a/scenes/enemies/effects/enemy_effect_bleed.gd.uid b/scenes/enemies/effects/enemy_effect_bleed.gd.uid new file mode 100644 index 0000000..4a5fd01 --- /dev/null +++ b/scenes/enemies/effects/enemy_effect_bleed.gd.uid @@ -0,0 +1 @@ +uid://b2ptwltm211t3 diff --git a/scenes/enemies/enemy_base.gd b/scenes/enemies/enemy_base.gd index 8ea0fe4..3afef25 100644 --- a/scenes/enemies/enemy_base.gd +++ b/scenes/enemies/enemy_base.gd @@ -17,7 +17,9 @@ var modifiers: Array[EnemyMod] = [] @onready var collision_shape_2d: CollisionShape2D = $CollisionShape2D @onready var shape_cast_2d: ShapeCast2D = $ShapeCast2D @onready var sprite_2d: Sprite2D = $Sprite2D -@onready var label: Label = $Label +@onready var label: Label = $HBoxContainer/Label +@onready var effect_container: HBoxContainer = $HBoxContainer/EffectContainer + var player: Player var enemy_name: String @@ -26,9 +28,11 @@ var god_mode: bool = false var is_dead: bool = false var health: float +var effects: Array[EnemyEffectBase] + var _path_update_timer: float = 0.0 var _compute_cache: KeyedCache = KeyedCache.new() - +var _effects_visible = [] func _ready() -> void: enemy_name = _gen_name() @@ -63,6 +67,15 @@ func _find_player(): func _gen_name() -> String: return "Unnamed enemy" +func _process(delta: float) -> void: + for effect in effects: + if effect in _effects_visible: + continue + var effect_sprite = Sprite2D.new() + effect_sprite.texture = preload("res://assets/sprites/small_bleed_icon.png") + effect_container.add_child(effect_sprite) + _effects_visible.append(effect) + func _physics_process(delta: float) -> void: if not target: diff --git a/scenes/enemies/enemy_base.tscn b/scenes/enemies/enemy_base.tscn index fa66c2d..e940b76 100644 --- a/scenes/enemies/enemy_base.tscn +++ b/scenes/enemies/enemy_base.tscn @@ -90,20 +90,25 @@ wait_time = 0.5 shape = SubResource("CircleShape2D_pkqou") max_results = 2 -[node name="Label" type="Label" parent="."] -anchors_preset = 7 +[node name="HBoxContainer" type="HBoxContainer" parent="."] +anchors_preset = 8 anchor_left = 0.5 -anchor_top = 1.0 +anchor_top = 0.5 anchor_right = 0.5 -anchor_bottom = 1.0 -offset_left = -85.5 -offset_top = 5.0 -offset_right = 85.5 -offset_bottom = 28.0 +anchor_bottom = 0.5 +offset_left = -53.0 +offset_right = 53.0 +offset_bottom = 40.0 grow_horizontal = 2 -grow_vertical = 0 +grow_vertical = 2 + +[node name="EffectContainer" type="HBoxContainer" parent="HBoxContainer"] +layout_mode = 2 +size_flags_vertical = 4 + +[node name="Label" type="Label" parent="HBoxContainer"] +layout_mode = 2 theme_override_font_sizes/font_size = 9 -text = "Unnamed the Adjective" horizontal_alignment = 1 [connection signal="animation_finished" from="AnimationPlayer" to="." method="_on_animation_player_animation_finished"] diff --git a/scenes/managers/enemy_manager.gd b/scenes/managers/enemy_manager.gd index 3c38092..3895513 100644 --- a/scenes/managers/enemy_manager.gd +++ b/scenes/managers/enemy_manager.gd @@ -115,4 +115,3 @@ func _on_set_spawn_rate(val: float): timer.stop() timer.wait_time = 1.0 / val timer.start() - print_debug("spawn_rate: %s" % timer.wait_time) diff --git a/scenes/player.gd b/scenes/player.gd index 60a7d3e..44f1d40 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -29,11 +29,7 @@ func _ready() -> void: if not weapon: weapon = WEAPON_SWORD.instantiate() add_child(weapon) - var mod = WeaponModBase.new() - mod.mod_property = "attack_damage" - mod.mod_value = 10.0 - mod.mod_type = WeaponModBase.ModType.ADDITIVE - weapon.add_mod(mod) + func _physics_process(delta: float) -> void: diff --git a/scenes/puddle.gd b/scenes/puddle.gd new file mode 100644 index 0000000..ec96434 --- /dev/null +++ b/scenes/puddle.gd @@ -0,0 +1,21 @@ +class_name Puddle +extends Node2D + +@export var color: Color = Color.CRIMSON +@onready var base: Sprite2D = $Base + +func _ready() -> void: + var player: Player = get_tree().get_first_node_in_group(GlobalConst.GROUP_PLAYER) + var shader = preload("res://assets/shaders/base_color_tint.gdshader") + var shader_material: ShaderMaterial + shader_material = ShaderMaterial.new() + shader_material.set_shader_parameter("base_color", color) + shader_material.shader = shader + base.material = shader_material + match randi() % 4: + 1: + rotation_degrees = 90 + 2: + rotation_degrees = 180 + 3: + rotation_degrees = -90 diff --git a/scenes/puddle.gd.uid b/scenes/puddle.gd.uid new file mode 100644 index 0000000..2e33828 --- /dev/null +++ b/scenes/puddle.gd.uid @@ -0,0 +1 @@ +uid://cds5aqq2mqmpe diff --git a/scenes/puddle.tscn b/scenes/puddle.tscn new file mode 100644 index 0000000..b45ccc4 --- /dev/null +++ b/scenes/puddle.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=8 format=3 uid="uid://dcka1mmlgeoj7"] + +[ext_resource type="Shader" uid="uid://cf48pgfl308o3" path="res://assets/shaders/base_color_tint.gdshader" id="1_qlq6d"] +[ext_resource type="Script" uid="uid://cds5aqq2mqmpe" path="res://scenes/puddle.gd" id="1_s8xbh"] +[ext_resource type="Texture2D" uid="uid://c5t4it4if0s6g" path="res://assets/sprites/puddle_1.png" id="2_s8xbh"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_i64cl"] +shader = ExtResource("1_qlq6d") +shader_parameter/base_color = Color(1, 0.2, 0.2, 1) + +[sub_resource type="AtlasTexture" id="AtlasTexture_h42jt"] +atlas = ExtResource("2_s8xbh") +region = Rect2(0, 0, 16, 13) + +[sub_resource type="AtlasTexture" id="AtlasTexture_16dvh"] +atlas = ExtResource("2_s8xbh") +region = Rect2(32, 0, 16, 13) + +[sub_resource type="AtlasTexture" id="AtlasTexture_5nuq4"] +atlas = ExtResource("2_s8xbh") +region = Rect2(16, 0, 16, 13) + +[node name="Puddle" type="Node2D"] +script = ExtResource("1_s8xbh") + +[node name="Base" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_i64cl") +texture = SubResource("AtlasTexture_h42jt") + +[node name="Highlights" type="Sprite2D" parent="."] +texture = SubResource("AtlasTexture_16dvh") + +[node name="Shading" type="Sprite2D" parent="."] +texture = SubResource("AtlasTexture_5nuq4") diff --git a/scenes/weapons/weapon_base.gd b/scenes/weapons/weapon_base.gd index 36b8695..cb6f7a3 100644 --- a/scenes/weapons/weapon_base.gd +++ b/scenes/weapons/weapon_base.gd @@ -4,6 +4,8 @@ extends Node2D enum WeaponTag { CAN_RETURN, CAN_BLEED, + CAN_CHAIN, + CAN_FORK, } @export var attack_cd: float @@ -12,11 +14,19 @@ enum WeaponTag { @export var attack_duration: float @export var attack_range: float @export var attack_crit_chance: float = 0.05 +@export var return_chance: float = 0.0 +@export var bleed_chance: float = 0.0 +@export var bleed_duration: float = 5.0 +@export var chain_chance: float = 0.0 @export var tags: Array[String] = [] @export var modifiers: Array[WeaponModBase] = [] @onready var active_cd_timer: Timer = $ActiveCDTimer +var _player: Player + +func _ready() -> void: + _player = get_tree().get_first_node_in_group(GlobalConst.GROUP_PLAYER) func _on_attack_cd_timer_timeout() -> void: do_attack() @@ -76,3 +86,19 @@ func has_property(key: String) -> bool: if prop.name == key: return true return false + +func did_crit() -> bool: + var weapon_crit = get_calculated("attack_crit_chance") + var player_crit = _player.player_stats.get_final("crit_chance", _player.modifiers) + + return randf() >= 1 - weapon_crit + player_crit + +func did_bleed() -> bool: + return randf() >= 1 - bleed_chance + +func base_damage() -> Array[Variant]: + var damage = get_calculated("attack_damage") + var is_crit := did_crit() + if is_crit: + damage *= _player.player_stats.get_final("crit_multiplier", _player.modifiers) + return [damage, is_crit] diff --git a/scenes/weapons/weapon_sword.gd b/scenes/weapons/weapon_sword.gd index 5e0fa99..c3dce36 100644 --- a/scenes/weapons/weapon_sword.gd +++ b/scenes/weapons/weapon_sword.gd @@ -8,13 +8,14 @@ const WEAPON_SWORD_PROJECTILE = preload("res://scenes/weapons/weapon_sword_proje signal projectile_hit(projectile: WeaponSwordProjectile, enemy: EnemyBase) -var _player: Player + func _ready() -> void: + bleed_chance = 0.2 targeting_range_shape.shape.radius = attack_range projectile_hit.connect(_on_projectile_hit) - _player = get_tree().get_first_node_in_group(GlobalConst.GROUP_PLAYER) + super._ready() func do_attack() -> void: @@ -44,13 +45,13 @@ func _do_active() -> void: func deal_damage(enemy: EnemyBase, damage_mult: float): - var weapon_crit = get_calculated("attack_crit_chance") - var player_crit = _player.player_stats.get_final("crit_chance", _player.modifiers) - var damage = get_calculated("attack_damage") - var is_crit = randf() >= 1 - weapon_crit + player_crit - if is_crit: - damage *= _player.player_stats.get_final("crit_multiplier", _player.modifiers) - enemy.take_damage(damage * damage_mult, is_crit) + var damage_and_crit = base_damage() + # TODO: Fix crit value + enemy.take_damage(damage_and_crit[0], damage_and_crit[1]) + + if did_bleed(): + var bleed = EnemyEffectBleed.new(enemy, damage_and_crit[0], bleed_duration) + bleed.apply(enemy) func _on_projectile_hit(projectile: WeaponSwordProjectile, enemy: EnemyBase, damage_mult: float): diff --git a/scenes/weapons/weapon_sword_projectile.gd b/scenes/weapons/weapon_sword_projectile.gd index 230f4fc..a55b186 100644 --- a/scenes/weapons/weapon_sword_projectile.gd +++ b/scenes/weapons/weapon_sword_projectile.gd @@ -5,6 +5,7 @@ extends Node2D @export var range: float = 200.0 @export var target: Node2D @export var damage_mult: float = 1.0 +@export var bleed_chance: float = 0.2 @export var on_hit_sig: Signal var _direction: Vector2