using System; using System.Linq; using Cirno.Scripts.Components.FSM.Player; using Cirno.Scripts.Resources; using Cirno.Scripts.Utils; using Godot; using Godot.Collections; namespace Cirno.Scripts.Components.Actors; public partial class PlayerWeaponProvider : Node2D { [Export] public PlayerStorageModule StorageModule { get; set; } [Export] public PackedScene WeaponTemplate { get; private set; } [Export] public double WeaponSwitchCooldown { get; private set; } = 0.5d; [Export] public Marker2D WeaponRightOffset { get; private set; } // local offset when facing right [Export] public Marker2D WeaponLeftOffset { get; private set; } // local offset when facing left public Array EquippedWeapons { get; set; } = []; private int _currentWeaponIndex = 0; private double _switchCooldown = 0d; private bool _switching = false; private int CurrentWeaponIndex { get => Math.Clamp(_currentWeaponIndex, 0, EquippedWeapons.Count -1); set { if (value > EquippedWeapons.Count - 1) { _currentWeaponIndex = 0; return; } if (value < 0) { _currentWeaponIndex = EquippedWeapons.Count - 1; return; } _currentWeaponIndex = value; } } private InventoryManager _inventoryManager; public Weapon EquippedWeapon { get; set; } private CharacterBody2D _parent; public void Init(CharacterBody2D parent) { _parent = parent; _inventoryManager = this.GetInventoryManager(); _inventoryManager.WeaponEquip += this.OnInventoryWeaponEquipped; _inventoryManager.ItemAdded += OnInventoryWeaponAdded; EquipStartupWeapon(); } public void Update(double delta) { RotateWeapon(); if (!_switching) return; _switchCooldown += delta; if (_switchCooldown >= WeaponSwitchCooldown) { _switching = false; _switchCooldown = 0d; } } private void RotateWeapon() { if (EquippedWeapon is null) return; EquippedWeapon.RotateWeapon(StorageModule.FacingDirection, WeaponLeftOffset.Position, WeaponRightOffset.Position); // EquippedWeapon.SetRotation(angle + Mathf.Pi / 2.0f); // // // // EquippedWeapon.FlipH = facingLeft; // // // 3. Position on correct side (assuming EquippedWeapon is a child of the Player node) // EquippedWeapon.Position = facingLeft ? WeaponLeftOffset : WeaponRightOffset; } private void OnInventoryWeaponEquipped(string itemKey) { Equip(itemKey, true); } private void OnInventoryWeaponAdded(LootItem item, int amount) { if (item.Item is not ItemTypes.Weapon) return; Equip(item, false); } private void EquipStartupWeapon() { if (EquippedWeapon is not null) return; if (!string.IsNullOrWhiteSpace(GlobalState.Session.EquippedWeaponId)) { // equip it Equip(GlobalState.Session.EquippedWeaponId, false); } else { // Try to equip whatever is first var weaponData = InventoryManager.Instance.Items.FirstOrDefault(x => x.Item.Item is ItemTypes.Weapon); if (weaponData is null) return; Equip(weaponData.Item.ItemKey, false); } } // This is a soft equip public void AddWeapon(Weapon weapon) { EquippedWeapons.Add(weapon); } // Triggered by event in inventorymanager private void EquipWeapon(Weapon weapon) { if (EquippedWeapon == weapon) { return; } // Need to start cooldown EquippedWeapon?.Hide(); EquippedWeapon = weapon; CurrentWeaponIndex = EquippedWeapons.IndexOf(weapon); GlobalState.Session.EquippedWeaponId = weapon.WeaponData.ItemKey; EquippedWeapon.Show(); _switching = true; _switchCooldown = 0d; } public void NextWeapon() { CurrentWeaponIndex += 1; Equip(EquippedWeapons[CurrentWeaponIndex], true); } public void PreviousWeapon() { CurrentWeaponIndex -= 1; Equip(EquippedWeapons[CurrentWeaponIndex], true); } public void Shoot(Vector2 direction) { if (EquippedWeapon == null) return; if (_switching) return; EquippedWeapon.ShootDirection = direction; EquippedWeapon.Shoot(); } public void Reload() { if (EquippedWeapon == null) return; if (_switching) return; EquippedWeapon.Reload(); } // Remastered method private LootItem GetItemFromInventory(string itemKey) { return InventoryManager.Instance.Items.FirstOrDefault(x => x.Item.ItemKey == itemKey)?.Item; } private Weapon GetWeaponFromLocal(string itemKey) { return EquippedWeapons.FirstOrDefault(x => x.WeaponData.ItemKey == itemKey); } // Remastered method private Weapon SpawnWeapon(string itemKey) { return SpawnWeapon(GetItemFromInventory(itemKey)); } // Remastered method private Weapon SpawnWeapon(LootItem startingItem) { if (startingItem is null) { GD.Print($"Could not spawn weapon was not in the inventory."); return null; } if (WeaponTemplate == null) { GD.Print("Could not spawn weapon because template is null"); return null; } // Check if it's not spawned already var maybeExistingWeapon = GetWeaponFromLocal(startingItem.ItemKey); if (maybeExistingWeapon is not null) return maybeExistingWeapon; var weapon = this.CreateSibling(WeaponTemplate); weapon.WeaponData = startingItem.WeaponData; weapon.Sprite.Texture = startingItem.InventorySprite; this.AddWeapon(weapon); return weapon; } public Weapon Equip(LootItem item, bool force) { var maybeExistingWeapon = GetWeaponFromLocal(item.ItemKey); if (maybeExistingWeapon is not null) return Equip(maybeExistingWeapon, force); // Spawn if not present var spawnedWeapon = SpawnWeapon(item); return Equip(spawnedWeapon, force); } public Weapon Equip(string itemKey, bool force) { // Check in local inventory first var maybeExistingWeapon = GetWeaponFromLocal(itemKey); if (maybeExistingWeapon is not null) return Equip(maybeExistingWeapon, force); // Spawn if not present var spawnedWeapon = SpawnWeapon(itemKey); if (spawnedWeapon is null) { GD.Print($"Tried to spawn weapon {itemKey} but failed because it was null."); return null; }; return Equip(spawnedWeapon, force); } public Weapon Equip(Weapon weapon, bool force) { // When we get here we already have a spawned weapon and it's in the list if (weapon is null) { return null; } // Always equip it if there's nothing equipped if (this.EquippedWeapon is null) { this.EquipWeapon(weapon); return weapon; } // If it's a soft equip check for priority if (!force && this.EquippedWeapon.WeaponData.Priority < weapon.WeaponData.Priority) { this.EquipWeapon(weapon); return weapon; } EquipWeapon(weapon); return weapon; } }