Adds in-game debug menu addon

Adds an in-game debug menu that displays performance metrics (FPS, frame times) and hardware/software information.

The menu can be toggled using the F3 key (or a custom input binding). It has different display styles, ranging from a compact FPS display to a detailed view with graphs and system information.
This commit is contained in:
Dan Baker 2025-05-04 17:53:46 +01:00
parent 214e0aa5e0
commit ff62d67f54
37 changed files with 1484 additions and 49 deletions

View file

@ -0,0 +1,110 @@
class_name Projectile extends Area2D
signal on_hit(projectile, target)
signal on_spawned(projectile)
signal on_destroyed(projectile)
@export var speed: float = 500.0
@export var damage: float = 10.0
@export var lifetime: float = 5.0
@export var direction: Vector2 = Vector2.RIGHT
@export var is_friendly: bool = true
# Modifier-related properties
var pierce_count: int = 0
var has_explosive_impact: bool = true
var explosion_projectile_count: int = 2
var explosion_projectile_damage_mult: float = 0.5
var explosion_projectile_speed: float = 300.0
var explosion_spread_angle: float = 360.0 # Full circle by default
# References
var source_weapon: RangedWeapon # Reference to the weapon that fired this
var lifetime_timer: Timer
# Add a variable to track the entity that triggered the explosion
var ignore_target = null
func _ready():
lifetime_timer = Timer.new()
add_child(lifetime_timer)
lifetime_timer.one_shot = true
lifetime_timer.wait_time = lifetime
lifetime_timer.connect("timeout", _on_lifetime_timeout)
lifetime_timer.start()
emit_signal("on_spawned", self)
connect("body_entered", _on_body_entered)
func _physics_process(delta):
position += direction * speed * delta
func _on_body_entered(body):
# Check if this is a body we should ignore
if body == ignore_target:
return
if body.is_in_group("enemies") and is_friendly:
Log.pr("Hit enemy: ", body.name)
# Deal damage to enemy
if body.has_method("take_damage"):
body.take_damage(damage)
# Emit signal for modifiers to react to
emit_signal("on_hit", self, body)
# Handle piercing
if pierce_count > 0:
pierce_count -= 1
else:
# Handle explosive impact
if has_explosive_impact:
# Store the target that triggered the explosion
ignore_target = body
_trigger_explosion()
# Destroy the projectile
destroy()
func _trigger_explosion():
# Create the explosion VFX
# var explosion = preload("res://scenes/explosion_effect.tscn").instantiate()
# explosion.global_position = global_position
# get_tree().root.add_child(explosion)
# Spawn the additional projectiles
if explosion_projectile_count > 0:
_spawn_explosion_projectiles()
func _spawn_explosion_projectiles():
# Calculate even angle distribution
var angle_step = explosion_spread_angle / explosion_projectile_count
var start_angle = - explosion_spread_angle / 2
for i in range(explosion_projectile_count):
# Create a new projectile
var new_proj = duplicate()
new_proj.global_position = global_position
# Calculate new direction based on spread
var random_angle = randf_range(0, 2 * PI)
var new_dir = Vector2.RIGHT.rotated(random_angle)
# Set properties for the new projectile
new_proj.direction = new_dir
new_proj.damage = damage * explosion_projectile_damage_mult
new_proj.speed = explosion_projectile_speed
# Clear explosive properties so we don't get infinite loops
new_proj.has_explosive_impact = true
new_proj.explosion_projectile_count = 1
# Pass the ignore_target to the new projectiles
new_proj.ignore_target = ignore_target
# Add to scene tree
get_tree().root.add_child(new_proj)
func destroy():
emit_signal("on_destroyed", self)
queue_free()
func _on_lifetime_timeout():
destroy()

View file

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

View file

@ -7,10 +7,12 @@ signal projectile_spawned(projectile)
# Base stats - will be modified by modifiers
var base_stats = {
"damage": 10.0,
"fire_rate": 2.0, # Shots per second
"fire_rate": 2.0,
"projectile_speed": 500.0,
"projectile_size": 1.0,
"projectile_lifetime": 5.0,
"projectile_quantity": 1,
"projectile_spread": 33,
"max_pierce": 0
}
@ -24,14 +26,15 @@ func _init() -> void:
Log.pr(stats)
add_child(stats)
func _ready():
# Connect to stats updated signal
stats.connect("stats_updated", _on_stats_updated)
# Setup fire timer
fire_timer = Timer.new()
add_child(fire_timer)
fire_timer.one_shot = true
projectile_scene = preload("res://assets/projectiles/basic_projectile.tscn")
func _ready():
stats.connect("stats_updated", _on_stats_updated)
fire_timer.connect("timeout", _on_fire_timer_timeout)
# Initial update
@ -47,30 +50,55 @@ func fire(direction: Vector2):
fire_timer.start(1.0 / stats.get_stat("fire_rate"))
func _spawn_projectile(spawn_position: Vector2, spawn_direction: Vector2):
var projectile = projectile_scene.instantiate()
projectile.global_position = spawn_position
projectile.direction = spawn_direction
# Get projectile quantity and spread from stats
var quantity = stats.get_stat("projectile_quantity")
var spread_angle = stats.get_stat("projectile_spread")
# Apply stats to projectile
projectile.speed = stats.get_stat("projectile_speed")
projectile.damage = stats.get_stat("damage")
projectile.lifetime = stats.get_stat("projectile_lifetime")
projectile.pierce_count = stats.get_stat("max_pierce")
projectile.source_weapon = self
# Calculate the angle between each projectile
var angle_step = 0.0
if quantity > 1 and spread_angle > 0:
angle_step = spread_angle / (quantity - 1)
# Apply size (scale)
var size = stats.get_stat("projectile_size")
projectile.scale = Vector2(size, size)
# Calculate starting angle (to center the spread)
var start_angle = - spread_angle / 2
# Allow modifiers to directly modify the projectile
for modifier in stats.modifiers:
modifier.modify_projectile(projectile)
get_tree().root.add_child(projectile)
projectile.emit_signal("on_spawned", projectile)
emit_signal("projectile_spawned", projectile)
# Spawn each projectile
for i in range(quantity):
var projectile = projectile_scene.instantiate()
projectile.global_position = spawn_position
# Calculate the direction with spread
var direction = spawn_direction
if quantity > 1:
var current_angle = start_angle + (i * angle_step)
direction = spawn_direction.rotated(deg_to_rad(current_angle))
projectile.direction = direction
# Apply stats to projectile
projectile.speed = stats.get_stat("projectile_speed")
projectile.damage = stats.get_stat("damage")
projectile.lifetime = stats.get_stat("projectile_lifetime")
projectile.source_weapon = self
# Set base size
var size = stats.get_stat("projectile_size")
projectile.scale = Vector2(size, size)
# Allow modifiers to directly modify the projectile
for modifier in stats.modifiers:
modifier.modify_projectile(projectile)
# Add to scene tree
if get_tree() and get_tree().get_root():
get_tree().get_root().add_child(projectile)
# Emit the spawn signal
if projectile.has_signal("on_spawned"):
projectile.emit_signal("on_spawned", projectile)
func add_modifier(modifier: Modifier):
Log.pr("Adding modifier: ", modifier)
stats.add_modifier(modifier)
func remove_modifier(modifier_id: String):