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

85
UI/DebugUI.tscn Normal file
View file

@ -0,0 +1,85 @@
[gd_scene load_steps=2 format=3 uid="uid://b0w0oxbtax5si"]
[ext_resource type="Script" uid="uid://bblhlxj8sqtql" path="res://UI/_scripts/debug_ui.gd" id="1_pwlud"]
[node name="DebugUi" type="Control"]
layout_mode = 3
anchors_preset = 0
script = ExtResource("1_pwlud")
[node name="MarginContainer" type="MarginContainer" parent="."]
offset_left = 13.0
offset_top = 15.0
offset_right = 91.0
offset_bottom = 173.0
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 5
[node name="FPS" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="FPSLabel" type="Label" parent="MarginContainer/VBoxContainer/FPS"]
layout_mode = 2
text = "FPS: "
[node name="FPSValue" type="Label" parent="MarginContainer/VBoxContainer/FPS"]
unique_name_in_owner = true
layout_mode = 2
text = "123"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="PlayerWeapon" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
visible = false
layout_mode = 2
size_flags_vertical = 3
[node name="PlayerWeaponLabel" type="Label" parent="MarginContainer/VBoxContainer/PlayerWeapon"]
layout_mode = 2
text = "Player Weapon:"
[node name="PlayerWeaponValue" type="Label" parent="MarginContainer/VBoxContainer/PlayerWeapon"]
layout_mode = 2
[node name="PlayerWeaponMods" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
visible = false
layout_mode = 2
size_flags_vertical = 2
[node name="PlayerWeaponModsLabel" type="Label" parent="MarginContainer/VBoxContainer/PlayerWeaponMods"]
visible = false
layout_mode = 2
text = "Mods:"
[node name="PlayerWeaponModsValue" type="Label" parent="MarginContainer/VBoxContainer/PlayerWeaponMods"]
unique_name_in_owner = true
layout_mode = 2
text = "123
456
678"
[node name="ModifierButtons" type="VBoxContainer" parent="MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="Button" type="Button" parent="MarginContainer/VBoxContainer/ModifierButtons"]
layout_mode = 2
text = "123"
[node name="PlayerStats" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 2
[node name="PlayerStatsValue" type="Label" parent="MarginContainer/VBoxContainer/PlayerStats"]
unique_name_in_owner = true
layout_mode = 2
text = "123
456
678"

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

View file

@ -0,0 +1,16 @@
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://ddgipwj5yd0vw"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6ioqn"]
bg_color = Color(0.6, 0.6, 0.6, 0.560784)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.888331, 0.274714, 0.539223, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[resource]
Button/styles/pressed = SubResource("StyleBoxFlat_6ioqn")

View file

@ -18,6 +18,12 @@ func _ready() -> void:
func set_stats(stats: StatsComponent) -> void: func set_stats(stats: StatsComponent) -> void:
self.stats_component = stats self.stats_component = stats
func has_modifier(modifier_id: String) -> bool:
for modifier in modifiers:
if modifier.id == modifier_id:
return true
return false
func add_modifier(modifier: Modifier) -> void: func add_modifier(modifier: Modifier) -> void:
modifiers.append(modifier) modifiers.append(modifier)
modifier.on_equip(get_parent()) modifier.on_equip(get_parent())
@ -63,6 +69,29 @@ func recalculate_stats() -> void:
emit_signal("stats_updated") emit_signal("stats_updated")
func check_callable(func_name: String) -> Callable:
# Create a default callable that does nothing and returns null
var default_callable = func(): return null
# Check each modifier in priority order
for modifier in modifiers:
if modifier.has_method(func_name):
# Return the callable from this modifier
return Callable(modifier, func_name)
# Return the default callable if no modifier has the function
return default_callable
# Convenience method to check and call in one step
func check_and_call(func_name: String, args := []):
var callable = check_callable(func_name)
if callable.is_valid():
if args.size() > 0:
return callable.callv(args)
else:
return callable.call()
return null
func _apply_modifier_stats(modifier: Modifier) -> void: func _apply_modifier_stats(modifier: Modifier) -> void:
if modifier.has_method("apply_stats_modification"): if modifier.has_method("apply_stats_modification"):
modifier.apply_stats_modification(stats_component) modifier.apply_stats_modification(stats_component)

View file

@ -5,7 +5,7 @@ class_name FireRateAdditive extends Modifier
func _init(): func _init():
id = "fire_rate_additive" id = "fire_rate_additive"
display_name = "Rapid Fire" display_name = "Rapid Fire"
description = "Increases fire rate by %0.1f shots per second" % fire_rate_bonus description = "Increases fire rate by %0.1f shots per round" % fire_rate_bonus
modifier_type = ModifierType.ADDITIVE modifier_type = ModifierType.ADDITIVE
func apply_stats_modification(stats: StatsComponent) -> void: func apply_stats_modification(stats: StatsComponent) -> void:

View file

@ -3,11 +3,10 @@ class_name FireRateMultiplicative extends Modifier
@export var fire_rate_multiplier: float = 1.2 # 20% faster firing @export var fire_rate_multiplier: float = 1.2 # 20% faster firing
func _init(): func _init():
id = "fire_rate_multiplicative" id = "fire_rate_multiplicative"
display_name = "Frenzy" display_name = "Frenzy"
description = "Increases fire rate by %d%%" % ((fire_rate_multiplier - 1.0) * 100) description = "Increases fire rate by %d%%" % ((fire_rate_multiplier - 1.0) * 100)
modifier_type = ModifierType.MULTIPLICATIVE modifier_type = ModifierType.MULTIPLICATIVE
func apply_stats_modification(final_stats: Dictionary, _base_stats: Dictionary) -> void: func apply_stats_modification(stats: StatsComponent) -> void:
if final_stats.has("fire_rate"): stats.update_stat("ranged.attack_rate", stats.get_stat("ranged.attack_rate") * fire_rate_multiplier)
final_stats.fire_rate *= fire_rate_multiplier

View file

@ -17,15 +17,15 @@ enum ModifierType {
# Called when the modifier is added to a weapon or ability # Called when the modifier is added to a weapon or ability
func on_equip(_owner) -> void: func on_equip(_owner) -> void:
pass pass
# Called when the modifier is removed # Called when the modifier is removed
func on_unequip(_owner) -> void: func on_unequip(_owner) -> void:
pass pass
# Override in child classes for specific modification logic # Override in child classes for specific modification logic
func modify_projectile(_projectile) -> void: func modify_projectile(_projectile) -> void:
pass pass
func modify_ability(_ability) -> void: func modify_ability(_ability) -> void:
pass pass

View file

@ -3,12 +3,11 @@ class_name ProjectileSizeMultiplicative extends Modifier
@export var size_multiplier: float = 1.5 # 50% bigger @export var size_multiplier: float = 1.5 # 50% bigger
func _init(): func _init():
id = "size_multiplicative" id = "size_multiplicative"
display_name = "Giant Projectiles" display_name = "Giant Projectiles"
description = "Multiplies projectile size by %0.1fx" % size_multiplier description = "Multiplies projectile size by %0.1fx" % size_multiplier
modifier_type = ModifierType.MULTIPLICATIVE modifier_type = ModifierType.MULTIPLICATIVE
priority = 10 # Higher priority than the additive version priority = 10 # Higher priority than the additive version
func apply_stats_modification(final_stats: Dictionary, base_stats: Dictionary) -> void: func apply_stats_modification(stats: StatsComponent) -> void:
if final_stats.has("projectile_size"): stats.update_stat("ranged.projectile_size", stats.get_stat("ranged.projectile_size") * size_multiplier)
final_stats.projectile_size *= size_multiplier

View file

@ -0,0 +1,17 @@
class_name UnlimitedPower extends Modifier
@export var fire_rate_bonus: float = 1.0 # +1 shot per second
@export var ranged_damage: float = 99.5
func _init():
id = "unlimited_power"
display_name = "Unlimited Power"
description = "Shoot lightning bolts. Fire rate + %0.1f. Ranged damage + %0.1f" % [fire_rate_bonus, ranged_damage]
modifier_type = ModifierType.ADDITIVE
func apply_stats_modification(stats: StatsComponent) -> void:
stats.update_stat("ranged.attack_rate", stats.get_stat("ranged.attack_rate") + fire_rate_bonus)
stats.update_stat("ranged.damage", stats.get_stat("ranged.damage") + ranged_damage)
func set_projectile_scene(weapon: RangedWeaponComponent) -> void:
weapon.projectile_scene = preload("res://assets/projectiles/projectile_lightning.tscn")

View file

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

View file

@ -3,15 +3,68 @@ extends Node2D
class_name RangedWeaponComponent class_name RangedWeaponComponent
@export var stats: StatsComponent @export var stats: StatsComponent
@export var basic_projectile: PackedScene = preload("res://assets/projectiles/basic_projectile.tscn")
@onready var modifier_manager = $ModifierManager @onready var modifier_manager = $ModifierManager
@onready var projectile_scene: PackedScene = basic_projectile
var can_fire: bool = true
var cooldown: Timer
func _init() -> void:
cooldown = Timer.new()
add_child(cooldown)
cooldown.one_shot = true
func _ready() -> void: func _ready() -> void:
Log.pr("RangedWeaponComponent initialized") Log.pr("RangedWeaponComponent initialized")
modifier_manager.set_stats(stats) modifier_manager.set_stats(stats)
modifier_manager.connect("modifier_added", _on_modifier_added)
modifier_manager.connect("modifier_removed", _on_modifier_removed)
cooldown.connect("timeout", _on_fire_timer_timeout)
func add_modifier(modifier: Modifier) -> void: func add_modifier(modifier: Modifier) -> void:
modifier_manager.add_modifier(modifier) modifier_manager.add_modifier(modifier)
func remove_modifier(modifier_id: String) -> void: func remove_modifier(modifier_id: String) -> void:
modifier_manager.remove_modifier(modifier_id) modifier_manager.remove_modifier(modifier_id)
func fire(direction: Vector2, target_position: Vector2) -> void:
if !can_fire:
return
spawn_projectile(global_position, direction, target_position)
can_fire = false
cooldown.start(1 / stats.get_stat("ranged.attack_rate"))
func set_projectile_scene() -> void:
projectile_scene = basic_projectile
modifier_manager.check_and_call("set_projectile_scene", [self])
func spawn_projectile(spawn_position: Vector2, spawn_direction: Vector2, target_position: Vector2) -> void:
Log.pr("Spawning projectile")
modifier_manager.check_and_call("spawn_projectile", [self])
## TODO: Handle multiple shots per fire
var projectile = projectile_scene.instantiate()
projectile.global_position = spawn_position
projectile.target_position = target_position
projectile.speed = 200 # stats.get_stat("projectile_speed")
projectile.damage = 10 # stats.get_stat("damage")
projectile.lifetime = 2 # stats.get_stat("projectile_lifetime")
projectile.source_weapon = self
var size = stats.get_stat("ranged.projectile_size")
projectile.set_projectile_scale(Vector2(size, size))
if get_tree() and get_tree().get_root():
get_tree().get_root().add_child(projectile)
func _on_modifier_added(_modifier: Modifier) -> void:
set_projectile_scene()
func _on_modifier_removed(_modifier: Modifier) -> void:
set_projectile_scene()
func _on_fire_timer_timeout() -> void:
can_fire = true

View file

@ -31,14 +31,14 @@ var base_stats: Dictionary[String, Variant] = {
} }
} }
var stats: Dictionary var stats: Dictionary = {}
func _init() -> void: func _init() -> void:
if not stats: if not stats:
reset_stats() reset_stats()
func reset_stats() -> void: func reset_stats() -> void:
stats = base_stats.duplicate() stats = base_stats.duplicate(true)
Log.pr("StatsComponent reset to base stats") Log.pr("StatsComponent reset to base stats")
func get_stat(stat_name: String) -> Variant: func get_stat(stat_name: String) -> Variant:
@ -92,4 +92,4 @@ func set_nested_stat(path: String, value) -> bool:
# Set the value at the final key # Set the value at the final key
current[final_key] = value current[final_key] = value
return true return true

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=86 format=3 uid="uid://bo5aw2cad3akl"] [gd_scene load_steps=87 format=3 uid="uid://bo5aw2cad3akl"]
[ext_resource type="Script" uid="uid://bq038uo4cm6nv" path="res://player/scripts/player.gd" id="1_oul6g"] [ext_resource type="Script" uid="uid://bq038uo4cm6nv" path="res://player/scripts/player.gd" id="1_oul6g"]
[ext_resource type="Texture2D" uid="uid://dqgq2c1h6yk3k" path="res://assets/sprites/characters/pink/Pink_Monster_Attack1_4.png" id="2_yllr7"] [ext_resource type="Texture2D" uid="uid://dqgq2c1h6yk3k" path="res://assets/sprites/characters/pink/Pink_Monster_Attack1_4.png" id="2_yllr7"]
@ -17,6 +17,7 @@
[ext_resource type="PackedScene" uid="uid://dud7c465danl4" path="res://combat/weapons/RangedWeaponComponent.tscn" id="15_wodsf"] [ext_resource type="PackedScene" uid="uid://dud7c465danl4" path="res://combat/weapons/RangedWeaponComponent.tscn" id="15_wodsf"]
[ext_resource type="PackedScene" uid="uid://dqful6et42ok8" path="res://combat/weapons/MeleeWeaponComponent.tscn" id="16_32hag"] [ext_resource type="PackedScene" uid="uid://dqful6et42ok8" path="res://combat/weapons/MeleeWeaponComponent.tscn" id="16_32hag"]
[ext_resource type="PackedScene" uid="uid://bjybfg0xrowb5" path="res://entities/StatsComponent.tscn" id="17_tqiix"] [ext_resource type="PackedScene" uid="uid://bjybfg0xrowb5" path="res://entities/StatsComponent.tscn" id="17_tqiix"]
[ext_resource type="PackedScene" uid="uid://b0w0oxbtax5si" path="res://UI/DebugUI.tscn" id="18_e7oew"]
[sub_resource type="CircleShape2D" id="CircleShape2D_rkbax"] [sub_resource type="CircleShape2D" id="CircleShape2D_rkbax"]
@ -545,11 +546,12 @@ animations = [{
"speed": 5.0 "speed": 5.0
}] }]
[node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("ranged", "melee") groups=["friendly"]] [node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("ranged", "melee", "stats") groups=["friendly", "player"]]
collision_mask = 14 collision_mask = 14
script = ExtResource("1_oul6g") script = ExtResource("1_oul6g")
ranged = NodePath("RangedWeaponComponent") ranged = NodePath("RangedWeaponComponent")
melee = NodePath("MeleeWeaponComponent") melee = NodePath("MeleeWeaponComponent")
stats = NodePath("StatsComponent")
[node name="PlayerCollision" type="CollisionShape2D" parent="."] [node name="PlayerCollision" type="CollisionShape2D" parent="."]
position = Vector2(0, 7) position = Vector2(0, 7)
@ -572,3 +574,15 @@ stats = NodePath("../StatsComponent")
[node name="MeleeWeaponComponent" parent="." instance=ExtResource("16_32hag")] [node name="MeleeWeaponComponent" parent="." instance=ExtResource("16_32hag")]
[node name="StatsComponent" parent="." instance=ExtResource("17_tqiix")] [node name="StatsComponent" parent="." instance=ExtResource("17_tqiix")]
[node name="CanvasLayer" type="CanvasLayer" parent="."]
layer = 100
[node name="DebugUi" parent="CanvasLayer" instance=ExtResource("18_e7oew")]
offset_left = -288.0
offset_top = -161.0
offset_right = 870.0
offset_bottom = 492.0
scale = Vector2(0.499389, 0.499439)
size_flags_horizontal = 4
size_flags_vertical = 4

View file

@ -1,9 +1,12 @@
extends CharacterBody2D extends CharacterBody2D
class_name Player
@export var speed = 200 @export var speed = 200
@export var special_ability: Ability @export var special_ability: Ability
@export var ranged: RangedWeaponComponent @export var ranged: RangedWeaponComponent
@export var melee: MeleeWeaponComponent @export var melee: MeleeWeaponComponent
@export var stats: StatsComponent
var movement: PlayerMovement var movement: PlayerMovement
var combat: PlayerCombat var combat: PlayerCombat

View file

@ -21,7 +21,7 @@ var explosion_projectile_speed: float = 300.0
var explosion_spread_angle: float = 360.0 # Full circle by default var explosion_spread_angle: float = 360.0 # Full circle by default
# References # References
var source_weapon: RangedWeapon # Reference to the weapon that fired this var source_weapon: RangedWeaponComponent # Reference to the weapon that fired this
var lifetime_timer: Timer var lifetime_timer: Timer
# Add a variable to track the entity that triggered the explosion # Add a variable to track the entity that triggered the explosion
var ignore_target = [] var ignore_target = []

View file

@ -106,7 +106,6 @@ func add_modifier(modifier: Modifier):
func remove_modifier(modifier_id: String): func remove_modifier(modifier_id: String):
pass pass
#stats.remove_modifier(modifier_id) #stats.remove_modifier(modifier_id)
func _on_stats_updated(): func _on_stats_updated():
# Update any visual components based on new stats # Update any visual components based on new stats
# For example, if weapon appearance changes based on damage/fire rate # For example, if weapon appearance changes based on damage/fire rate

View file

@ -22,6 +22,7 @@ Global="*res://utility/Globals.gd"
SceneSelector="*res://utility/SceneSelector.gd" SceneSelector="*res://utility/SceneSelector.gd"
MapBuilder="*res://utility/MapBuilder.gd" MapBuilder="*res://utility/MapBuilder.gd"
DebugMenu="*res://addons/debug_menu/debug_menu.tscn" DebugMenu="*res://addons/debug_menu/debug_menu.tscn"
ModLib="*res://utility/ModifierLibrary.gd"
[display] [display]
@ -36,11 +37,17 @@ enabled=PackedStringArray("res://addons/debug_menu/plugin.cfg", "res://addons/lo
[file_customization] [file_customization]
folder_colors={ folder_colors={
"res://UI/": "green",
"res://combat/": "red", "res://combat/": "red",
"res://combat/modifiers/": "green", "res://combat/modifiers/": "green",
"res://combat/weapons/": "red" "res://combat/weapons/": "red",
"res://utility/": "purple"
} }
[gui]
theme/custom="uid://ddgipwj5yd0vw"
[input] [input]
move_up={ move_up={
@ -68,6 +75,11 @@ fire={
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(115, 24),"global_position":Vector2(129, 97),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(115, 24),"global_position":Vector2(129, 97),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
] ]
} }
debug_menu={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
[layer_names] [layer_names]

View file

@ -5,13 +5,10 @@ class_name MapBuilderClass
# Function to copy a TileMap layer from one scene to a target TileMap at a specific grid position # Function to copy a TileMap layer from one scene to a target TileMap at a specific grid position
func copy_tilemap_to_target(source_scene, target_tilemap: TileMapLayer, target_layer: String, grid_position: Vector2i): func copy_tilemap_to_target(source_scene, target_tilemap: TileMapLayer, target_layer: String, grid_position: Vector2i):
# First, load and instantiate the source scene if it's a resource path # First, load and instantiate the source scene if it's a resource path
Log.pr("Copying tilemap from source scene to target tilemap at grid position: ", grid_position)
Log.pr("Source scene: ", source_scene)
var source_instance var source_instance
if source_scene is String: if source_scene is String:
source_instance = load(source_scene).instantiate() source_instance = load(source_scene).instantiate()
elif source_scene is PackedScene: elif source_scene is PackedScene:
Log.pr("Source scene is a PackedScene, instantiating it")
source_instance = source_scene.instantiate() source_instance = source_scene.instantiate()
else: else:
source_instance = source_scene source_instance = source_scene
@ -68,8 +65,6 @@ func find_tilemap_by_name(root_node: Node, tilemap_name: String) -> TileMapLayer
func redraw_terrain(positions: Array, layer: TileMapLayer, terrain_set: int, terrain: int) -> void: func redraw_terrain(positions: Array, layer: TileMapLayer, terrain_set: int, terrain: int) -> void:
Log.pr("Filtering and redrawing surrounding tiles", positions)
# Filter positions to only include cells with terrainset 0 and terrain 0 # Filter positions to only include cells with terrainset 0 and terrain 0
var filtered_positions: Array = [] var filtered_positions: Array = []
for cell in positions: for cell in positions:
@ -80,7 +75,6 @@ func redraw_terrain(positions: Array, layer: TileMapLayer, terrain_set: int, ter
# Only redraw if we have filtered cells # Only redraw if we have filtered cells
if filtered_positions.size() > 0: if filtered_positions.size() > 0:
Log.pr("Redrawing filtered tiles:", filtered_positions)
layer.set_cells_terrain_connect(filtered_positions, terrain_set, terrain) layer.set_cells_terrain_connect(filtered_positions, terrain_set, terrain)
else: else:
Log.pr("No tiles to redraw after filtering") Log.pr("No tiles to redraw after filtering")

View file

@ -0,0 +1,62 @@
extends Node
class_name ModifierLibrary
var modifiers: Array[Modifier] = []
func _ready() -> void:
## Check the modifier folder for any scripts that extend the Modifier class
var modifier_folder = "res://combat/modifiers/modifiers/"
var dir = DirAccess.open(modifier_folder)
if dir:
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if file_name.ends_with(".gd"):
var script_path = modifier_folder + file_name
var script = load(script_path)
var temp_instance = script.new() if script else null
var is_modifier = temp_instance is Modifier if temp_instance else false
if is_modifier and file_name != "modifier.gd":
modifiers.append(temp_instance)
file_name = dir.get_next()
dir.list_dir_end()
else:
print("Failed to open directory: ", modifier_folder)
Log.pr("ModifierLibrary initialized with %d modifiers" % modifiers.size())
Log.pr("Modifiers: ", modifiers)
func get_modifier_by_id(modifier_id: String) -> Modifier:
for modifier in modifiers:
if modifier.id == modifier_id:
return modifier.duplicate()
return null
func get_modifiers_by_type(modifier_type: int) -> Array[Modifier]:
var result: Array[Modifier] = []
for modifier in modifiers:
if modifier.modifier_type == modifier_type:
result.append(modifier)
return result
func get_modifiers_by_rarity(rarity: int) -> Array[Modifier]:
var result: Array[Modifier] = []
for modifier in modifiers:
if modifier.rarity == rarity:
result.append(modifier)
return result
func get_modifiers_by_priority(priority: int) -> Array[Modifier]:
var result: Array[Modifier] = []
for modifier in modifiers:
if modifier.priority == priority:
result.append(modifier)
return result
func get_all_modifiers() -> Array[Modifier]:
return modifiers

View file

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

View file

@ -29,7 +29,7 @@ func initialize_cache(folder_path: String, preload_scenes: bool = false):
scene_files.append(folder_path + "/" + file_name) scene_files.append(folder_path + "/" + file_name)
file_name = dir.get_next() file_name = dir.get_next()
Log.pr("Found %d scene files in %s" % [scene_files.size(), folder_path]) #Log.pr("Found %d scene files in %s" % [scene_files.size(), folder_path])
# Store in cache # Store in cache
_scene_paths_cache[folder_path] = scene_files _scene_paths_cache[folder_path] = scene_files
@ -37,7 +37,7 @@ func initialize_cache(folder_path: String, preload_scenes: bool = false):
if preload_scenes and scene_files.size() > 0: if preload_scenes and scene_files.size() > 0:
_loaded_scenes_cache[folder_path] = [] _loaded_scenes_cache[folder_path] = []
for scene_path in scene_files: for scene_path in scene_files:
Log.pr("Preloading scene: " + scene_path) #Log.pr("Preloading scene: " + scene_path)
# Load the scene and store it in the cache # Load the scene and store it in the cache
#var loaded_scene = preload(scene_path) #var loaded_scene = preload(scene_path)
_loaded_scenes_cache[folder_path].append(load(scene_path)) _loaded_scenes_cache[folder_path].append(load(scene_path))
@ -47,7 +47,7 @@ func initialize_cache(folder_path: String, preload_scenes: bool = false):
func get_random_scene(folder_path: String): func get_random_scene(folder_path: String):
# Make sure the cache is initialized # Make sure the cache is initialized
if not _scene_paths_cache.has(folder_path): if not _scene_paths_cache.has(folder_path):
Log.pr("Initializing cache for folder: " + folder_path) #Log.pr("Initializing cache for folder: " + folder_path)
initialize_cache(folder_path, true) initialize_cache(folder_path, true)
var scene_paths = _scene_paths_cache[folder_path] var scene_paths = _scene_paths_cache[folder_path]
@ -60,10 +60,10 @@ func get_random_scene(folder_path: String):
# Return from loaded scenes cache if available # Return from loaded scenes cache if available
if _loaded_scenes_cache.has(folder_path): if _loaded_scenes_cache.has(folder_path):
Log.pr("Returning preloaded scene from cache") #Log.pr("Returning preloaded scene from cache")
return _loaded_scenes_cache[folder_path][random_index] return _loaded_scenes_cache[folder_path][random_index]
# Otherwise load the scene # Otherwise load the scene
var scene = load(scene_paths[random_index]) var scene = load(scene_paths[random_index])
Log.pr(scene) #Log.pr(scene)
return scene return scene