using System.Collections.Generic; using ColdMint.scripts.debug; using ColdMint.scripts.inventory; using ColdMint.scripts.map.events; using ColdMint.scripts.projectile; using ColdMint.scripts.utils; using Godot; namespace ColdMint.scripts.weapon; /// /// Projectile weapons /// 抛射体武器 /// /// ///These weapons can fire projectiles to attack the enemy.For example: guns and scepters.Generate a bullet to attack the enemy. ///这类武器可发射抛射体,攻击敌人。例如:枪支和法杖。生成一个子弹攻击敌人。 /// public partial class ProjectileWeapon : WeaponTemplate { /// /// The formation position of the projectile /// 抛射体的生成位置 /// private Marker2D? _marker2D; /// /// Number of slots for ranged weapons /// 远程武器的槽位数量 /// [Export] private int _numberSlots; /// /// SpellList /// 法术列表 /// /// ///To make weapons out of the box, you need to configure pre-made spells here. ///为了使武器开箱即用,您需要在这里配置预制的法术。 /// [Export] private string[]? _spellList; /// /// How many projectiles are generated per fire /// 每次开火生成多少个抛射体 /// public int NumberOfProjectiles { get; set; } = 1; private readonly List _spells = new(); /// /// Saves the position of a spell with projectile generation /// 保存具有抛射体生成能力的法术位置 /// private readonly List _spellProjectileIndexes = new(); /// /// Whether to fire spells in sequence /// 是否按顺序发射法术 /// [Export] private bool _fireSequentially; /// /// The index used the last time a spell was cast /// 上次发射法术时采用的索引 /// private int _lastUsedProjectileMagicIndex = -1; /// /// Get next index /// 获取下次索引 /// /// private int GetNextProjectileMagicIndex() { if (_fireSequentially) { _lastUsedProjectileMagicIndex++; if (_lastUsedProjectileMagicIndex >= _spellProjectileIndexes.Count) { _lastUsedProjectileMagicIndex = 0; } return _lastUsedProjectileMagicIndex; } else { return RandomUtils.Instance.Next(0, _spellProjectileIndexes.Count); } } /// /// Gets the loading range of the spell /// 获取法术的加载范围 /// /// ///Return array meaning: 0, starting position 1, ending position 2, with projectile generated spell position, length 3. ///返回数组的含义为:0,起始位置1,结束位置2,带有抛射体生成的法术位置,长度为3。 /// private int[] GetSpellScope() { var index = GetNextProjectileMagicIndex(); var projectileSpellPosition = _spellProjectileIndexes[index]; var endIndex = projectileSpellPosition; var startIndex = 0; if (index > 0) { //And the previous index can set the starting position.(The starting position is increased by 1 to avoid using spells with projectile generation as starting points.) //还有前面的索引可设定起始位置。(这里起始位置加1是为了避免 具有抛射体生成能力的法术 作为起点。) startIndex = _spellProjectileIndexes[index - 1] + 1; } if (index == _spellProjectileIndexes.Count - 1) { endIndex = _spells.Count - 1; } return [startIndex, endIndex, projectileSpellPosition]; } public override int ItemType { get => Config.ItemType.ProjectileWeapon; } public override void LoadResource() { base.LoadResource(); _marker2D = GetNode("Marker2D"); if (SelfItemContainer == null) { SelfItemContainer = new UniversalItemContainer(_numberSlots); SelfItemContainer.AllowAddingItemByType(Config.ItemType.Spell); //Reload pre-made spells. //装填预制的法术。 if (_spellList is not { Length: > 0 }) return; foreach (var spellId in _spellList) { if (string.IsNullOrEmpty(spellId)) { continue; } var item = ItemTypeManager.CreateItem(spellId, this); if (item is not ISpell spell) { continue; } //The spell is stored in memory and has not yet been loaded into the node tree. So we call the InvokeLoadResource method to initialize the resource. //法术保存在内存中,尚未加载到节点树。所以我们调用InvokeLoadResource方法来初始化资源。 spell.LoadResource(); if (SelfItemContainer.CanAddItem(item)) { SelfItemContainer.AddItem(item); } } UpdateSpellCache(); } } /// /// Update spell cache /// 更新法术缓存 /// /// ///This will parse available spells from inside the item container. ///这将从物品容器内解析可用的法术。 /// public void UpdateSpellCache() { if (SelfItemContainer == null) { return; } _spells.Clear(); _spellProjectileIndexes.Clear(); var totalCapacity = SelfItemContainer.GetTotalCapacity(); for (var i = 0; i < totalCapacity; i++) { var item = SelfItemContainer.GetItem(i); if (item == null) { continue; } if (item is not ISpell spell) { continue; } _spells.Add(spell); var packedScene = spell.GetProjectile(); if (packedScene != null) { //Has the ability to generate projectiles. //拥有抛射体生成能力。 _spellProjectileIndexes.Add(_spells.Count - 1); } } } private void OnItemDataChangeEvent(ItemDataChangeEvent itemDataChangeEvent) { UpdateSpellCache(); } public override void _EnterTree() { base._EnterTree(); if (SelfItemContainer != null) { SelfItemContainer.ItemDataChangeEvent += OnItemDataChangeEvent; } } public override void _ExitTree() { base._ExitTree(); if (SelfItemContainer != null) { SelfItemContainer.ItemDataChangeEvent -= OnItemDataChangeEvent; } } protected override bool DoFire(Node2D? owner, Vector2 enemyGlobalPosition) { if (owner == null) { LogCat.LogError("owner_is_null"); return false; } if (_marker2D == null) { LogCat.LogError("marker2d_is_null"); return false; } if (GameSceneDepend.ProjectileContainer == null) { LogCat.LogError("projectile_container_is_null"); return false; } if (_spellProjectileIndexes.Count == 0) { LogCat.LogError("projectile_generate_magic_is_null"); return false; } var spellScope = GetSpellScope(); LogCat.LogWithFormat("projectile_weapon_range", LogCat.LogLabel.Default, true, string.Join(",", spellScope), _fireSequentially, _lastUsedProjectileMagicIndex); //The final spell is a projectile generator. //最后的法术是拥有抛射体生成能力的。 var spellProjectile = _spells[spellScope[2]]; var packedScene = spellProjectile.GetProjectile(); if (packedScene == null) { LogCat.LogError("projectile_scene_is_null"); return false; } ModifyWeapon(spellScope); for (var i = 0; i < NumberOfProjectiles; i++) { var projectile = NodeUtils.InstantiatePackedScene(packedScene); if (projectile == null) { LogCat.LogError("projectile_is_null"); RestoreWeapon(spellScope); return false; } var velocity = _marker2D.GlobalPosition.DirectionTo(enemyGlobalPosition) * projectile.Speed; for (var s = spellScope[0]; s <= spellScope[1]; s++) { var spell = _spells[s]; spell.ModifyProjectile(i, projectile, ref velocity); } NodeUtils.CallDeferredAddChild(GameSceneDepend.ProjectileContainer, projectile); projectile.Owner = owner; projectile.TargetNode = GameSceneDepend.TemporaryTargetNode; projectile.Velocity = velocity; projectile.Position = _marker2D.GlobalPosition; } RestoreWeapon(spellScope); return true; } /// /// Modify weapon attributes /// 修改武器属性 /// /// private void ModifyWeapon(int[] spellScope) { for (var i = spellScope[0]; i <= spellScope[1]; i++) { var spell = _spells[i]; spell.ModifyWeapon(this); } } /// /// Restores modifications to weapons /// 恢复对武器的修改 /// /// private void RestoreWeapon(int[] spellScope) { for (var i = spellScope[0]; i <= spellScope[1]; i++) { var spell = _spells[i]; spell.RestoreWeapon(this); } } }