Implements modifier system for weapons

Adds a modifier system allowing dynamic modification of weapon
stats and behavior. This includes:

- Creating ModifierLibrary to manage available modifiers.
- Adds ModifierManager to handle equipping and unequipping modifiers
- Adds a new RangedWeaponComponent to handle firing projectiles and
  managing modifiers.
- Introduces a DebugUI for in-game modifier management.
- Introduces an "Unlimited Power" modifier that changes the projectile scene.
- Modifies stats components to work with the new modifier system.

This system allows for more flexible and customizable weapon
functionality.
This commit is contained in:
Dan Baker 2025-05-08 18:31:19 +01:00
parent 9f66ab0a73
commit 70839387ca
22 changed files with 432 additions and 40 deletions

107
UI/_scripts/debug_ui.gd Normal file
View file

@ -0,0 +1,107 @@
extends Control
class_name DebugUI
var fps_label: Label
var player_weapon_mods_label: Label
var player: Player
var was_debug_key_pressed: bool = false
var is_mouse_over_ui: bool = false
func _ready():
self.visible = false
player = get_tree().get_first_node_in_group("player")
fps_label = %FPSValue
player_weapon_mods_label = %PlayerWeaponModsValue
self.position = Vector2.ZERO
self.size = get_viewport_rect().size
self.scale = Vector2(1.2, 1.2)
# Connect the mouse entered/exited signals to track when mouse is over UI
self.mouse_entered.connect(_on_mouse_entered)
self.mouse_exited.connect(_on_mouse_exited)
# Make the UI control consume input events
self.mouse_filter = Control.MOUSE_FILTER_STOP
func _on_mouse_entered() -> void:
is_mouse_over_ui = true
func _on_mouse_exited() -> void:
is_mouse_over_ui = false
func _input(event: InputEvent) -> void:
# Only process if debug UI is visible
if not self.visible:
return
# Check if it's a mouse button event and if mouse is over UI
if event is InputEventMouseButton:
if is_mouse_over_ui:
# Consume the event so it doesn't propagate to the game
get_viewport().set_input_as_handled()
func _process(_delta):
if Input.is_action_pressed("debug_menu"):
if not was_debug_key_pressed:
populate_modifier_buttons()
was_debug_key_pressed = true
self.visible = !self.visible
else:
was_debug_key_pressed = false
var current_fps = Engine.get_frames_per_second()
fps_label.text = str(current_fps)
var player_stats = player.stats.get_nested_stat("ranged")
var player_stats_label = %PlayerStatsValue
player_stats_label.text = ""
for stat in player_stats:
player_stats_label.text += str(stat) + ": " + str(player_stats[stat]) + "\n"
var player_mods = player.ranged.modifier_manager.modifiers
player_weapon_mods_label.text = ""
for mod in player_mods:
player_weapon_mods_label.text += str(mod.id) + " (" + str(mod.description) + ") \n"
func populate_modifier_buttons() -> void:
# Clear existing buttons
for child in %ModifierButtons.get_children():
if child is Button:
child.queue_free()
# Create buttons for each modifier
for modifier in ModLib.get_all_modifiers():
var button = Button.new()
button.mouse_filter = Control.MOUSE_FILTER_STOP
button.toggle_mode = true
button.text = modifier.id + " [" + modifier.description + "]"
# If the modifier exists in the player's weapon, set the button to "pressed"
if player.ranged.modifier_manager.has_modifier(modifier.id):
button.button_pressed = true
else:
button.button_pressed = false
# Store modifier id in the button's metadata for reference in the callback
button.set_meta("modifier_id", modifier.id)
# Connect the toggled signal to a callback function
button.toggled.connect(_on_modifier_button_toggled.bind(modifier.id))
%ModifierButtons.add_child(button)
func _on_modifier_button_toggled(button_pressed: bool, modifier_id: String) -> void:
if button_pressed:
# Add the modifier if the button is pressed
var modifier = ModLib.get_modifier_by_id(modifier_id)
if modifier:
player.ranged.modifier_manager.add_modifier(modifier)
else:
# Remove the modifier if the button is unpressed
player.ranged.modifier_manager.remove_modifier(modifier_id)

View file

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