Updated cycle testing logic and various other behaviours for enhanced
system.
This commit is contained in:
parent
1ac71cc104
commit
7ffb93963f
10 changed files with 237 additions and 31 deletions
|
|
@ -4,41 +4,184 @@ class_name WeaponComponent extends Node
|
|||
@export var available_weapons: Array[WeaponShot] = []
|
||||
@export var weapon_change_flash: BaseEffect = null
|
||||
|
||||
var _current_weapon_index: int = 0
|
||||
var _current_weapon_index = 0
|
||||
var _current_power_level = 0
|
||||
var _cycled_weapon: WeaponShot = null
|
||||
|
||||
@onready var effects_component: EffectComponent = %EffectsComponent
|
||||
@onready var effects_component: EffectComponent = get_node("EffectsComponent")
|
||||
|
||||
func _ready() -> void:
|
||||
|
||||
# Set the default weapon to be stock, i.e. index 1
|
||||
if weapon_data and not available_weapons.is_empty():
|
||||
for i in range(available_weapons.size()):
|
||||
if available_weapons[i] == weapon_data:
|
||||
_current_weapon_index = i
|
||||
# Initialize the power level from the default weapon so
|
||||
# shooting works immediately without needing to cycle.
|
||||
_current_power_level = weapon_data.power_level
|
||||
return
|
||||
# If available_weapons was populated from the scene but no matching
|
||||
# weapon_data was found (e.g. all weapons share the same power level),
|
||||
# cap the power level to the minimum power level across all weapons.
|
||||
# This ensures initial shooting works even when weapon_data is null or
|
||||
# mismatches are found.
|
||||
if not available_weapons.is_empty():
|
||||
var min_level: int = 999
|
||||
for w: WeaponShot in available_weapons:
|
||||
if w != null and w.power_level < min_level:
|
||||
min_level = w.power_level
|
||||
_current_power_level = min_level
|
||||
|
||||
|
||||
func _compute_active_weapons() -> Array[WeaponShot]:
|
||||
# Filter available weapons by current power level.
|
||||
var active: Array[WeaponShot] = []
|
||||
for weapon: WeaponShot in available_weapons:
|
||||
if weapon != null and weapon.power_level <= _current_power_level:
|
||||
active.append(weapon)
|
||||
return active
|
||||
|
||||
|
||||
func get_active_weapons(power_level: int = -1) -> Array[WeaponShot]:
|
||||
# Return all weapons active at the given power level.
|
||||
#
|
||||
# If power_level is negative, uses the internally tracked power level.
|
||||
# var _level: int = power_level if power_level >= 0 else _current_power_level
|
||||
# (unused - power_level parameter is ignored by design)
|
||||
# If the cycled weapon is set, return it exclusively —
|
||||
# cycling overrides all additive logic.
|
||||
if _cycled_weapon != null:
|
||||
return [_cycled_weapon]
|
||||
var base: Array[WeaponShot] = _compute_active_weapons()
|
||||
if not base.is_empty():
|
||||
var additive: Array[WeaponShot] = base.filter(
|
||||
func(w: WeaponShot): return not w.force_replace_all
|
||||
)
|
||||
if not additive.is_empty():
|
||||
return additive
|
||||
# If no non-force-replace weapons are active, return only the current
|
||||
# default weapon (weapon_data). This ensures the starting weapon (stock)
|
||||
# fires as a single shot rather than all force_replace_all weapons firing
|
||||
# simultaneously.
|
||||
if weapon_data != null and base.has(weapon_data):
|
||||
return [weapon_data]
|
||||
return base.filter(
|
||||
func(w: WeaponShot): return w.force_replace_all
|
||||
)
|
||||
|
||||
|
||||
func _get_force_replace_weapons() -> Array[WeaponShot]:
|
||||
# Return weapons that override all others (force_replace_all = true).
|
||||
return _compute_active_weapons().filter(
|
||||
func(w: WeaponShot): return w.force_replace_all
|
||||
)
|
||||
|
||||
|
||||
func collect_weapon(new_weapon: WeaponShot) -> void:
|
||||
# Handle adding a weapon, applying replacement logic as needed.
|
||||
#
|
||||
# Replacement rules (applied in order):
|
||||
# 1. force_replace_all: Discard all weapons, add only this one.
|
||||
# Rule 2: replaces_at_same_level: Remove weapons at matching power level.
|
||||
# 3. Normal: Add additive weapon (no removal).
|
||||
#
|
||||
# Sets the power level to this weapon's if higher than current.
|
||||
# Emits weapon_changed with the new active set.
|
||||
#
|
||||
# Clear any active cycle — collecting a weapon during cycling restores
|
||||
# normal additive behavior.
|
||||
if _cycled_weapon != null:
|
||||
reset_weapon_cycle()
|
||||
# Rule 1: Force replace — discard everything, keep only this weapon
|
||||
if new_weapon.force_replace_all:
|
||||
available_weapons = [new_weapon]
|
||||
_current_power_level = new_weapon.power_level
|
||||
_update_data_from_active_weapons()
|
||||
_emit_weapon_changed()
|
||||
_trigger_change_flash()
|
||||
return
|
||||
|
||||
# Rule 2: Replace if same level — remove weapons at matching power level
|
||||
if new_weapon.replaces_at_same_level:
|
||||
var was_replaced: bool = false
|
||||
for i in range(available_weapons.size()):
|
||||
if available_weapons[i] != null and \
|
||||
available_weapons[i].replaces_at_same_level == false and \
|
||||
available_weapons[i].power_level == new_weapon.power_level:
|
||||
available_weapons.remove_at(i)
|
||||
was_replaced = true
|
||||
break # Only replace the highest (first match at this level)
|
||||
if was_replaced:
|
||||
# Also remove any other weapons with the same power_level that may
|
||||
# have sneaked in via previous power level upgrades
|
||||
var filtered: Array[WeaponShot] = []
|
||||
for w: WeaponShot in available_weapons:
|
||||
if w != null and w.power_level == new_weapon.power_level:
|
||||
continue
|
||||
filtered.append(w)
|
||||
available_weapons = filtered
|
||||
|
||||
# Rule 3: Additive — just add it (no removal)
|
||||
var already_has: bool = false
|
||||
for w: WeaponShot in available_weapons:
|
||||
if w != null and w.shot_name == new_weapon.shot_name:
|
||||
already_has = true
|
||||
break
|
||||
if not already_has:
|
||||
available_weapons.append(new_weapon)
|
||||
|
||||
# Update power level if this weapon is at a higher tier
|
||||
if new_weapon.power_level > _current_power_level:
|
||||
_current_power_level = new_weapon.power_level
|
||||
|
||||
_update_data_from_active_weapons()
|
||||
_emit_weapon_changed()
|
||||
_trigger_change_flash()
|
||||
|
||||
|
||||
func _update_data_from_active_weapons() -> void:
|
||||
# Update weapon_data to point to the highest-priority active weapon.
|
||||
#
|
||||
# This is called after any weapon collection or power level change to
|
||||
# keep the legacy `weapon_data` property in sync for backward compatibility.
|
||||
# Force_replace_all weapons take priority over additive weapons.
|
||||
# Check for force_replace weapons first (they override everything)
|
||||
var force_replaces: Array[WeaponShot] = _get_force_replace_weapons()
|
||||
if not force_replaces.is_empty():
|
||||
weapon_data = force_replaces[-1]
|
||||
return
|
||||
|
||||
# Otherwise, point to the highest-priority additive weapon
|
||||
var active: Array[WeaponShot] = get_active_weapons()
|
||||
if not active.is_empty():
|
||||
weapon_data = active[-1]
|
||||
else:
|
||||
weapon_data = null
|
||||
|
||||
|
||||
func _emit_weapon_changed() -> void:
|
||||
if weapon_data == null:
|
||||
return
|
||||
var ship: Sprite2D = get_parent().get_node("Ship")
|
||||
var sprite_size = ship.get_rect().size
|
||||
var origin_offset: float = weapon_data.origin if weapon_data else 0.0
|
||||
var origin_offset: float = weapon_data.origin
|
||||
EventBus.weapon_changed.emit(weapon_data.shot_name, get_parent(), sprite_size, origin_offset)
|
||||
|
||||
|
||||
func get_bullet_scene() -> PackedScene:
|
||||
return weapon_data.bullet_scene if weapon_data else null
|
||||
|
||||
|
||||
func get_weapon_resource() -> Resource:
|
||||
return weapon_data
|
||||
|
||||
|
||||
func select_weapon_by_name(weapon_name: String) -> bool:
|
||||
for i in range(available_weapons.size()):
|
||||
if available_weapons[i].shot_name == weapon_name:
|
||||
_current_weapon_index = i
|
||||
weapon_data = available_weapons[i]
|
||||
var candidate: WeaponShot = available_weapons[i]
|
||||
if candidate != null and candidate.shot_name == weapon_name:
|
||||
_cycled_weapon = candidate # override additive behavior
|
||||
print("Switched to: ", weapon_name)
|
||||
|
||||
_emit_weapon_changed()
|
||||
_trigger_change_flash()
|
||||
return true
|
||||
return false
|
||||
|
|
@ -49,14 +192,36 @@ func _trigger_change_flash() -> void:
|
|||
effects_component.apply_effect(weapon_change_flash)
|
||||
|
||||
|
||||
|
||||
func cycle_weapon() -> void: # Used for testing weapon cycling
|
||||
func cycle_weapon() -> void: # Used for testing weapon cycling
|
||||
if available_weapons.is_empty():
|
||||
return
|
||||
|
||||
_current_weapon_index = (_current_weapon_index + 1) % available_weapons.size()
|
||||
weapon_data = available_weapons[_current_weapon_index]
|
||||
print("Switched to: ", weapon_data.shot_name)
|
||||
var new_weapon: WeaponShot = available_weapons[_current_weapon_index]
|
||||
if new_weapon == null:
|
||||
return
|
||||
print("Switched to: ", new_weapon.shot_name)
|
||||
|
||||
# Remove the previous weapon from the active set before engaging the next.
|
||||
# Clearing _cycled_weapon forces get_active_weapons() to evaluate the
|
||||
# additive state (which temporarily re-includes the old weapon). Then
|
||||
# re-setting _cycled_weapon to the new weapon re-establishes the cycle
|
||||
# override. This two-step process ensures the old weapon is fully removed
|
||||
# from the active set and its power level doesn't leak into the new cycle.
|
||||
_cycled_weapon = null # 1) Remove previous weapon
|
||||
_cycled_weapon = new_weapon # 2) Engage next weapon
|
||||
_current_power_level = new_weapon.power_level
|
||||
|
||||
# Update weapon_data to reflect the cycled weapon directly,
|
||||
# so _emit_weapon_changed() reports the correct name and origin.
|
||||
weapon_data = new_weapon
|
||||
_emit_weapon_changed()
|
||||
_trigger_change_flash()
|
||||
|
||||
|
||||
func reset_weapon_cycle() -> void:
|
||||
# Restore normal additive behavior after cycling.
|
||||
_cycled_weapon = null
|
||||
_update_data_from_active_weapons()
|
||||
_emit_weapon_changed()
|
||||
_trigger_change_flash()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue