Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,36 @@
class_name FragileBarrel
extends FillingBarrel

## Emitted when [member current_health] reaches 0.
## Emitted when barrel's health reaches 0.
signal barrel_destroyed(barrel_instance: FragileBarrel)

## Maximum hits the barrel can take before breaking.
@export_range(1, 100) var max_health: int = 4

var current_health: int

@onready var crack_overlay_node: AnimatedSprite2D = %CrackOverlay
@onready var crack_sound: AudioStreamPlayer2D = %CrackSound
@onready var shatter_sound: AudioStreamPlayer2D = %ShatterSound
@onready var health_component: HealthComponent = %HealthComponent


func _ready() -> void:
super._ready()
current_health = max_health
crack_overlay_node.visible = false


# Logic called by Projectile when it hits this object
func hit_by_droplet(droplet_label: String) -> void:
# Ignore if already full or destroyed
if _amount >= needed_amount or current_health <= 0:
if _amount >= needed_amount or health_component.has_depleted_health:
return

if droplet_label == self.label:
increment()
else:
take_damage()


func take_damage() -> void:
current_health -= 1

if current_health <= 0:
break_barrel()
else:
crack_sound.play()
update_cracks()
health_component.damage(1)


func update_cracks() -> void:
var damage_taken: int = max_health - current_health

# IMPROVEMENT: Calculate frame index proportionally based on damage percentage.
var total_frames: int = crack_overlay_node.sprite_frames.get_frame_count("default")
var frame_index: int = int(floor((float(damage_taken) / max_health) * total_frames))
var frame_index: int = int(floor((health_component.damage_taken_ratio) * total_frames))

# Clamp to ensure we don't exceed available frames (0-based index)
frame_index = clamp(frame_index, 0, total_frames - 1)
Expand All @@ -59,7 +42,7 @@ func update_cracks() -> void:
crack_overlay_node.frame = frame_index


func break_barrel() -> void:
func _on_health_component_health_depleted() -> void:
crack_overlay_node.visible = false

shatter_sound.play()
Expand All @@ -69,3 +52,8 @@ func break_barrel() -> void:
await animated_sprite_2d.animation_finished

barrel_destroyed.emit(self)


func _on_health_component_health_changed() -> void:
crack_sound.play()
update_cracks()
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
[ext_resource type="Script" uid="uid://d05hp7ascndlr" path="res://scenes/game_elements/props/filling_barrel/components/fragile_barrel.gd" id="2_wal8y"]
[ext_resource type="AudioStream" uid="uid://bknpb07lvbded" path="res://scenes/game_elements/props/filling_barrel/components/sfx_barrel_crack.tres" id="4_sdj6j"]
[ext_resource type="AudioStream" uid="uid://c3iuv5ax8i78v" path="res://scenes/game_elements/props/filling_barrel/components/sfx_barrel_breaking.tres" id="5_v7k7g"]
[ext_resource type="Script" uid="uid://b0qrfv6upnqtu" path="res://scenes/game_logic/health_component.gd" id="6_v7k7g"]

[node name="FragileBarrel" unique_id=2064522120 instance=ExtResource("1_ab25j")]
script = ExtResource("2_wal8y")
max_health = 4

[node name="CrackOverlay" type="AnimatedSprite2D" parent="." index="1" unique_id=473951260]
unique_name_in_owner = true
Expand All @@ -25,3 +25,11 @@ bus = &"SFX"
unique_name_in_owner = true
stream = ExtResource("5_v7k7g")
bus = &"SFX"

[node name="HealthComponent" type="Node" parent="." index="10" unique_id=707508929]
unique_name_in_owner = true
script = ExtResource("6_v7k7g")
metadata/_custom_type_script = "uid://b0qrfv6upnqtu"

[connection signal="health_changed" from="HealthComponent" to="." method="_on_health_component_health_changed" unbinds=1]
[connection signal="health_depleted" from="HealthComponent" to="." method="_on_health_component_health_depleted"]
46 changes: 46 additions & 0 deletions scenes/game_logic/health_component.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# SPDX-FileCopyrightText: The Threadbare Authors
# SPDX-License-Identifier: MPL-2.0
class_name HealthComponent
extends Node

## Emitted when [member current_health] reaches zero.
signal health_depleted

## Emitted when [member current_health] changes.[br]
## Is not emitted when the health reaches zero, [signal health_depleted] is emitted instead.
signal health_changed(current_health: int)
Comment thread
wjt marked this conversation as resolved.

@export_range(1, 100) var max_health: int = 4

## Represents the current health.[br]
## The value will always be in the following range [code]0 <= current_health <= max_health[/code].
var current_health: int = max_health:
set(value):
if value == current_health:
return

current_health = clamp(value, 0, max_health)
if current_health <= 0:
health_depleted.emit()
else:
health_changed.emit(current_health)

var damage_taken: int:
get:
return max_health - current_health

var damage_taken_ratio: float:
get:
return float(damage_taken) / max_health

var has_depleted_health: bool:
get:
return current_health <= 0


func damage(amount: int) -> void:
current_health -= amount


func heal(amount: int) -> void:
current_health += amount
1 change: 1 addition & 0 deletions scenes/game_logic/health_component.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://b0qrfv6upnqtu