Add charge throw mechanics; refactor bullet and brick components

This commit is contained in:
2025-05-12 23:08:54 +02:00
parent b09bee9fb6
commit 59c309409c
34 changed files with 422 additions and 96 deletions

View File

@@ -5,35 +5,50 @@ extends Node
@export var fire_rate: float = 1.0
@export var player_controller: PlayerController
@export var timer: Timer
@export var charge_throw_component: ChargeThrowComponent
var can_throw: bool = true
func _ready() -> void:
setup_timer()
can_throw = true
func _input(event: InputEvent) -> void:
if event.is_action_pressed("attack") and can_throw:
throw_brick()
if event.is_action_pressed("attack") and can_throw and charge_throw_component:
charge_throw_component.start_charging()
if event.is_action_released("attack") and can_throw and charge_throw_component:
var power_multiplier: float = charge_throw_component.stop_charging()
print("Power Multiplier: ", power_multiplier)
throw_brick(power_multiplier)
func setup_timer() -> void:
timer.wait_time = fire_rate
timer.one_shot = false
timer.autostart = false
timer.timeout.connect(on_timer_timeout)
func on_timer_timeout() -> void:
can_throw = true
func throw_brick() -> void:
func throw_brick(power_multiplier: float = 1.0) -> void:
var brick_instance: Node2D = brick_scene.instantiate()
var brick: BulletComponent = brick_instance.get_node("BulletComponent")
brick_instance.position = player_controller.position
brick.direction = player_controller.last_direction
var launch_component := brick_instance.get_node_or_null("LaunchComponent") as LaunchComponent
var chargable_component := brick_instance.get_node_or_null("ChargableComponent") as ChargableComponent
if launch_component:
launch_component.initial_direction = player_controller.last_direction
launch_component.spawn_position = player_controller.global_position
launch_component.spawn_rotation = player_controller.rotation
launch_component.speed *= power_multiplier if chargable_component else 1.0
brick_instance.global_position = player_controller.global_position
get_tree().current_scene.add_child(brick_instance)
can_throw = false
timer.start()

View File

@@ -1,40 +0,0 @@
class_name BulletComponent
extends Node
@export var root: Node2D
@export var direction: Vector2 = Vector2.RIGHT
@export var speed: float = 10.0
@export var area2d: Area2D
@export var visibility_notifier: VisibleOnScreenNotifier2D
@export var life_time: float = 5.0
@export var timer: Timer
func _ready() -> void:
root = get_parent()
visibility_notifier.screen_exited.connect(_on_screen_exited)
area2d.body_entered.connect(on_area2d_body_entered)
area2d.area_entered.connect(on_area2d_area_entered)
timer.wait_time = life_time
timer.timeout.connect(on_timer_timeout)
func _physics_process(delta: float) -> void:
root.position += direction * speed * delta
func _on_screen_exited() -> void:
root.queue_free()
func on_area2d_body_entered(_body: Node2D) -> void:
root.queue_free()
func on_area2d_area_entered(_area: Area2D) -> void:
root.queue_free()
func on_timer_timeout() -> void:
root.queue_free()

View File

@@ -1 +0,0 @@
uid://bh2vrkdbrtpin

View File

@@ -0,0 +1,18 @@
class_name BulletComponent
extends Node
@export var root: Node2D
@export var area2d: Area2D
func _ready() -> void:
area2d.body_entered.connect(on_area2d_body_entered)
area2d.area_entered.connect(on_area2d_area_entered)
func on_area2d_body_entered(_body: Node2D) -> void:
root.queue_free()
func on_area2d_area_entered(_area: Area2D) -> void:
root.queue_free()

View File

@@ -0,0 +1 @@
uid://cdnwrn8v05qhi

View File

@@ -0,0 +1,2 @@
class_name ChargableComponent
extends Node

View File

@@ -0,0 +1 @@
uid://ciowd622nk0i7

View File

@@ -0,0 +1,45 @@
class_name ChargeThrowComponent
extends Node
@export var min_power: float = 0.5
@export var max_power: float = 2.0
@export var max_charge_time: float = 1.5
var charge_start_time: float = 0.0
var is_charging: bool = false
signal charge_started
signal charge_updated(charge_ratio: float)
signal charge_stopped
func _process(_delta: float) -> void:
if not is_charging:
return
var charge_ratio := get_charge_ratio()
charge_updated.emit(charge_ratio)
func start_charging() -> void:
is_charging = true
charge_start_time = Time.get_ticks_msec() / 1000.0
charge_started.emit()
func get_charge_ratio() -> float:
if not is_charging:
return 0.0
var held_time := (Time.get_ticks_msec() / 1000.0) - charge_start_time
var t = clamp(held_time / max_charge_time, 0.0, 1.0)
return lerp(min_power, max_power, t)
func stop_charging() -> float:
is_charging = false
var held_time := (Time.get_ticks_msec() / 1000.0) - charge_start_time
var t = clamp(held_time / max_charge_time, 0.0, 1.0)
charge_start_time = 0.0
charge_stopped.emit()
return lerp(min_power, max_power, t)

View File

@@ -0,0 +1 @@
uid://c6fclevp3peuo

View File

@@ -0,0 +1,30 @@
class_name GravityMotionComponent
extends Node2D
@export var character_body: CharacterBody2D
@export var launch_component: LaunchComponent
@export var gravity: Vector2 = Vector2(0, 980.0)
@export var target_direction: Vector2 = Vector2(1.0, -1.0)
var velocity: Vector2 = Vector2.ZERO
func _ready() -> void:
if not launch_component:
return
var direction := target_direction if launch_component.initial_direction.x > 0 else Vector2(-target_direction.x, target_direction.y)
direction = direction.normalized()
velocity = direction * launch_component.speed
func _physics_process(delta: float) -> void:
if not character_body:
return
velocity += gravity * delta
character_body.velocity = velocity
character_body.move_and_slide()
if velocity.length_squared() > 0.01:
character_body.rotation = velocity.angle()

View File

@@ -0,0 +1 @@
uid://c2gbumw4x4t1v

View File

@@ -0,0 +1,17 @@
class_name LaunchComponent
extends Node2D
@export var root: Node2D
@export var initial_direction: Vector2 = Vector2.RIGHT
@export var speed: float = 16.0
@export var spawn_position: Vector2 = Vector2.ZERO
@export var spawn_rotation: float = 0.0
func _ready() -> void:
root.global_position = spawn_position
root.global_rotation = spawn_rotation
func get_initial_velocity() -> Vector2:
return initial_direction.normalized() * speed

View File

@@ -0,0 +1 @@
uid://873un8agkyja

View File

@@ -0,0 +1,22 @@
class_name LifetimeComponent
extends Node
@export var root: Node
@export var life_time: float = 5.0
@export var timer: Timer
func _ready() -> void:
if not root:
printerr("Root node not set.")
return
if not timer:
printerr("Timer node not set.")
return
timer.timeout.connect(on_timer_timeout)
func on_timer_timeout() -> void:
root.queue_free()

View File

@@ -0,0 +1 @@
uid://bvsgg8lu0a8m6

View File

@@ -0,0 +1,21 @@
class_name OutOfScreenComponent
extends Node
@export var visibility_notifier: VisibleOnScreenNotifier2D
@export var root: Node
func _ready() -> void:
if not visibility_notifier:
printerr("Visibility notifier not set.")
return
visibility_notifier.screen_exited.connect(_on_screen_exited)
func _on_screen_exited() -> void:
if not root:
printerr("Root node not set.")
return
root.queue_free()

View File

@@ -0,0 +1 @@
uid://1tnr46o1ib4u

View File

@@ -27,10 +27,14 @@ func shoot() -> void:
return
var bullet_instance: Node2D = bullet_scene.instantiate()
var bullet_component: BulletComponent = bullet_instance.get_node("BulletComponent")
var launch_component: LaunchComponent = bullet_instance.get_node_or_null("LaunchComponent")
var spawn_position: Vector2 = bullet_spawn_right.global_position if shoot_direction == Vector2.RIGHT else bullet_spawn_left.global_position
if launch_component:
launch_component.initial_direction = shoot_direction
launch_component.spawn_position = spawn_position
launch_component.spawn_rotation = root.rotation
bullet_instance.position = spawn_position
bullet_component.direction = shoot_direction
get_tree().current_scene.add_child(bullet_instance)

View File

@@ -0,0 +1,21 @@
class_name ProjectileComponent
extends Node2D
@export var speed: float = 16.0
@export var dir: float
@export var spawn_position: Vector2
@export var spawn_rotation: float
@export var character_body: CharacterBody2D
func _ready() -> void:
global_position = spawn_position
global_rotation = spawn_rotation
func _physics_process(delta: float) -> void:
if not character_body:
return
character_body.velocity = Vector2(0, -speed).rotated(dir)
character_body.move_and_slide()

View File

@@ -0,0 +1 @@
uid://bk2fvcv0g4x1q

View File

@@ -0,0 +1,9 @@
class_name StraightMotionComponent
extends Node
@export var root: Node2D
@export var launch_component: LaunchComponent
func _physics_process(delta: float) -> void:
root.position += launch_component.get_initial_velocity() * delta

View File

@@ -0,0 +1 @@
uid://cvcnfrr1udco5

View File

@@ -28,7 +28,6 @@ func add_skill(skill_data: SkillData) -> void:
elif skill_instance.has_node(value):
value = skill_instance.get_node(value)
else:
print("NodePath not found: ", value)
continue
skill_instance[key] = value

View File

@@ -0,0 +1,52 @@
extends Node
@export var progress_bar: ProgressBar
@export var charge_throw_component: ChargeThrowComponent
func _ready() -> void:
if not charge_throw_component:
return
if not progress_bar:
return
setup_progress_bar()
progress_bar.hide()
charge_throw_component.charge_started.connect(on_charge_started)
charge_throw_component.charge_updated.connect(on_charge_updated)
charge_throw_component.charge_stopped.connect(on_charge_stopped)
func on_charge_updated(charge_ratio: float) -> void:
if not progress_bar:
return
progress_bar.value = charge_ratio
progress_bar.show()
func on_charge_stopped() -> void:
if not progress_bar:
return
progress_bar.hide()
func on_charge_started() -> void:
if not progress_bar:
return
progress_bar.show()
func setup_progress_bar() -> void:
if not progress_bar:
return
progress_bar.min_value = charge_throw_component.min_power
progress_bar.max_value = charge_throw_component.max_power
progress_bar.value = charge_throw_component.min_power
progress_bar.step = 0.01
progress_bar.show()

View File

@@ -0,0 +1 @@
uid://q32rtephu1t1