using System; using System.Linq; using Cirno.Scripts.Resources; using Cirno.Scripts.Weapons; using Godot; using Godot.Collections; namespace Cirno.Scripts.Components.FSM._3DPlayer; public partial class PlayerWeaponProvider3D : Node { [Export] public IsoPlayerStorageModule StorageModule { get; set; } [Export] public PackedScene WeaponTemplate { get; private set; } [Export] public double WeaponSwitchCooldown { get; private set; } = 0.5d; [Export] public Marker3D WeaponRightOffset { get; private set; } // local offset when facing right [Export] public Marker3D 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; } } public Weapon3D EquippedWeapon { get; set; } private CharacterBody3D _parent; public void Init(CharacterBody3D parent) { _parent = parent; InventoryManager.Instance.WeaponEquip += this.OnInventoryWeaponEquipped; InventoryManager.Instance.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(Weapon3D weapon) { EquippedWeapons.Add(weapon); } // Triggered by event in inventorymanager private void EquipWeapon(Weapon3D 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; InventoryManager.Instance.UpdateEquippedWeapon(weapon.WeaponData.ItemKey); } 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 Weapon3D GetWeaponFromLocal(string itemKey) { return EquippedWeapons.FirstOrDefault(x => x.WeaponData.ItemKey == itemKey); } // Remastered method private Weapon3D SpawnWeapon(string itemKey) { return SpawnWeapon(GetItemFromInventory(itemKey)); } // Remastered method private Weapon3D 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 = WeaponTemplate.Instantiate(); _parent.AddChild(weapon); weapon.GlobalPosition = _parent.GlobalPosition; //this.CreateSibling(WeaponTemplate); weapon.WeaponData = startingItem.WeaponData3D; weapon.Sprite.Texture = startingItem.InventorySprite; weapon.Init(); this.AddWeapon(weapon); return weapon; } public Weapon3D 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 Weapon3D 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 Weapon3D Equip(Weapon3D 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; } }