diff --git a/DungeonShooting_Godot/prefab/bullet/Bullet0001.tscn b/DungeonShooting_Godot/prefab/bullet/Bullet0001.tscn
index 8c2a3c7..4081afc 100644
--- a/DungeonShooting_Godot/prefab/bullet/Bullet0001.tscn
+++ b/DungeonShooting_Godot/prefab/bullet/Bullet0001.tscn
@@ -1,9 +1,10 @@
[gd_scene load_steps=9 format=3 uid="uid://bj4kmvt8jg1cf"]
-[ext_resource type="Script" path="res://src/game/item/bullet/Bullet.cs" id="1_82ma0"]
+[ext_resource type="Script" path="res://src/game/activity/bullet/Bullet.cs" id="1_82ma0"]
[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="2_p12d3"]
[ext_resource type="Texture2D" uid="uid://bu0b11hiuecxy" path="res://resource/sprite/bullet/bullet.png" id="3_hjgpe"]
+
[sub_resource type="ShaderMaterial" id="ShaderMaterial_5a4f2"]
resource_local_to_scene = true
shader = ExtResource("2_p12d3")
diff --git a/DungeonShooting_Godot/prefab/bullet/Bullet0002.tscn b/DungeonShooting_Godot/prefab/bullet/Bullet0002.tscn
index f951e9f..ed25dfe 100644
--- a/DungeonShooting_Godot/prefab/bullet/Bullet0002.tscn
+++ b/DungeonShooting_Godot/prefab/bullet/Bullet0002.tscn
@@ -1,9 +1,10 @@
[gd_scene load_steps=9 format=3 uid="uid://bqkj0rn72ppge"]
-[ext_resource type="Script" path="res://src/game/item/bullet/Bullet.cs" id="1_wphe7"]
+[ext_resource type="Script" path="res://src/game/activity/bullet/Bullet.cs" id="1_wphe7"]
[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="2_l3yjy"]
[ext_resource type="Texture2D" uid="uid://ctsvj4y1t538u" path="res://resource/sprite/bullet/bullet3.png" id="3_nf7ic"]
+
[sub_resource type="ShaderMaterial" id="ShaderMaterial_5a4f2"]
resource_local_to_scene = true
shader = ExtResource("2_l3yjy")
diff --git a/DungeonShooting_Godot/prefab/shell/Shell0001.tscn b/DungeonShooting_Godot/prefab/shell/Shell0001.tscn
index b11de73..6deb5aa 100644
--- a/DungeonShooting_Godot/prefab/shell/Shell0001.tscn
+++ b/DungeonShooting_Godot/prefab/shell/Shell0001.tscn
@@ -1,9 +1,10 @@
[gd_scene load_steps=7 format=3 uid="uid://bj4yr6ru8nhwr"]
-[ext_resource type="Script" path="res://src/game/item/shell/Shell.cs" id="1_ph0ad"]
+[ext_resource type="Script" path="res://src/game/activity/shell/Shell.cs" id="1_ph0ad"]
[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="2_k705i"]
[ext_resource type="Texture2D" uid="uid://dto03bc2qbhnj" path="res://resource/sprite/shell/shellCase.png" id="3_0fth1"]
+
[sub_resource type="ShaderMaterial" id="ShaderMaterial_px12l"]
resource_local_to_scene = true
shader = ExtResource("2_k705i")
diff --git a/DungeonShooting_Godot/prefab/weapon/Weapon0001.tscn b/DungeonShooting_Godot/prefab/weapon/Weapon0001.tscn
index 6fd7f8c..c140971 100644
--- a/DungeonShooting_Godot/prefab/weapon/Weapon0001.tscn
+++ b/DungeonShooting_Godot/prefab/weapon/Weapon0001.tscn
@@ -1,10 +1,11 @@
[gd_scene load_steps=7 format=3 uid="uid://c6etppq4v63xw"]
[ext_resource type="PackedScene" uid="uid://cxltmhhp4rbyk" path="res://prefab/weapon/WeaponTemplate.tscn" id="1_0nysf"]
-[ext_resource type="Script" path="res://src/game/item/weapon/gun/Gun.cs" id="2_bd6qw"]
+[ext_resource type="Script" path="res://src/game/activity/weapon/gun/Gun.cs" id="2_bd6qw"]
[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="3_ksckd"]
[ext_resource type="SpriteFrames" uid="uid://5m0qs7m4er5u" path="res://resource/spriteFrames/Weapon0001.tres" id="4_xo84l"]
+
[sub_resource type="ShaderMaterial" id="ShaderMaterial_5bfqf"]
resource_local_to_scene = true
shader = ExtResource("3_ksckd")
diff --git a/DungeonShooting_Godot/resource/sprite/role/enemy0001/enemy0001.png.import b/DungeonShooting_Godot/resource/sprite/role/enemy0001/enemy0001.png.import
index c36be94..b20c9a6 100644
--- a/DungeonShooting_Godot/resource/sprite/role/enemy0001/enemy0001.png.import
+++ b/DungeonShooting_Godot/resource/sprite/role/enemy0001/enemy0001.png.import
@@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://chd2vtesap5cf"
-path="res://.godot/imported/Enemy0001.png-148a38dfa95953b26d890356e8875de4.ctex"
+path="res://.godot/imported/enemy0001.png-1247a3ddf8a1a163d812cad12c4340fd.ctex"
metadata={
"vram_texture": false
}
[deps]
-source_file="res://resource/sprite/role/enemy0001/Enemy0001.png"
-dest_files=["res://.godot/imported/Enemy0001.png-148a38dfa95953b26d890356e8875de4.ctex"]
+source_file="res://resource/sprite/role/enemy0001/enemy0001.png"
+dest_files=["res://.godot/imported/enemy0001.png-1247a3ddf8a1a163d812cad12c4340fd.ctex"]
[params]
diff --git a/DungeonShooting_Godot/resource/sprite/role/enemy0001/enemy0001_Debris.png.import b/DungeonShooting_Godot/resource/sprite/role/enemy0001/enemy0001_Debris.png.import
index d563acf..56388a2 100644
--- a/DungeonShooting_Godot/resource/sprite/role/enemy0001/enemy0001_Debris.png.import
+++ b/DungeonShooting_Godot/resource/sprite/role/enemy0001/enemy0001_Debris.png.import
@@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://d2f55lu60x64i"
-path="res://.godot/imported/Enemy0001_Debris.png-ac416dc79cd3c1217b27e1ef1fbe0d0b.ctex"
+path="res://.godot/imported/enemy0001_Debris.png-297a2fb6680cb862a9a085cf58f8268c.ctex"
metadata={
"vram_texture": false
}
[deps]
-source_file="res://resource/sprite/role/enemy0001/Enemy0001_Debris.png"
-dest_files=["res://.godot/imported/Enemy0001_Debris.png-ac416dc79cd3c1217b27e1ef1fbe0d0b.ctex"]
+source_file="res://resource/sprite/role/enemy0001/enemy0001_Debris.png"
+dest_files=["res://.godot/imported/enemy0001_Debris.png-297a2fb6680cb862a9a085cf58f8268c.ctex"]
[params]
diff --git a/DungeonShooting_Godot/src/game/GameApplication.cs b/DungeonShooting_Godot/src/game/GameApplication.cs
index 271affb..1fc0257 100644
--- a/DungeonShooting_Godot/src/game/GameApplication.cs
+++ b/DungeonShooting_Godot/src/game/GameApplication.cs
@@ -95,6 +95,8 @@
InitRoomConfig();
//初始化 ActivityObject
ActivityObject.InitActivity();
+ //初始化武器数据
+ Weapon.InitWeaponAttribute();
DungeonConfig = new DungeonConfig();
DungeonConfig.GroupName = "testGroup";
diff --git a/DungeonShooting_Godot/src/game/activity/bullet/Bullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/Bullet.cs
new file mode 100644
index 0000000..bb20329
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/bullet/Bullet.cs
@@ -0,0 +1,96 @@
+using Godot;
+
+///
+/// 子弹类
+///
+[Tool, GlobalClass]
+public partial class Bullet : ActivityObject
+{
+ ///
+ /// 碰撞区域
+ ///
+ [Export, ExportFillNode]
+ public Area2D CollisionArea { get; set; }
+
+ ///
+ /// 发射该子弹的武器
+ ///
+ public Weapon Weapon { get; private set; }
+
+ // 最大飞行距离
+ private float MaxDistance;
+
+ // 子弹飞行速度
+ private float FlySpeed;
+
+ //当前子弹已经飞行的距离
+ private float CurrFlyDistance = 0;
+
+ public void Init(Weapon weapon, float speed, float maxDistance, Vector2 position, float rotation, uint targetLayer)
+ {
+ Weapon = weapon;
+ CollisionArea.CollisionMask = targetLayer;
+ CollisionArea.AreaEntered += OnArea2dEntered;
+
+ //只有玩家使用该武器才能获得正常速度的子弹
+ if (weapon.Master is Player)
+ {
+ FlySpeed = speed;
+ }
+ else
+ {
+ FlySpeed = speed * weapon.Attribute.AiBulletSpeedScale;
+ }
+ MaxDistance = maxDistance;
+ Position = position;
+ Rotation = rotation;
+ ShadowOffset = new Vector2(0, 5);
+
+ BasisVelocity = new Vector2(FlySpeed, 0).Rotated(Rotation);
+ }
+
+ protected override void PhysicsProcessOver(float delta)
+ {
+ //移动
+ var lastSlideCollision = GetLastSlideCollision();
+ //撞到墙
+ if (lastSlideCollision != null)
+ {
+ //创建粒子特效
+ var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_BulletSmoke_tscn);
+ var smoke = packedScene.Instantiate();
+ smoke.GlobalPosition = lastSlideCollision.GetPosition();
+ smoke.GlobalRotation = lastSlideCollision.GetNormal().Angle();
+ smoke.AddToActivityRoot(RoomLayerEnum.YSortLayer);
+
+ Destroy();
+ return;
+ }
+ //距离太大, 自动销毁
+ CurrFlyDistance += FlySpeed * delta;
+ if (CurrFlyDistance >= MaxDistance)
+ {
+ var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_BulletDisappear_tscn);
+ var node = packedScene.Instantiate();
+ node.GlobalPosition = GlobalPosition;
+ node.AddToActivityRoot(RoomLayerEnum.YSortLayer);
+
+ Destroy();
+ }
+ }
+
+ private void OnArea2dEntered(Area2D other)
+ {
+ var role = other.AsActivityObject();
+ if (role != null)
+ {
+ var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_BulletDisappear_tscn);
+ var node = packedScene.Instantiate();
+ node.GlobalPosition = GlobalPosition;
+ node.AddToActivityRoot(RoomLayerEnum.YSortLayer);
+
+ role.CallDeferred(nameof(Role.Hurt), 4, Rotation);
+ Destroy();
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/bullet/BulletAttribute.cs b/DungeonShooting_Godot/src/game/activity/bullet/BulletAttribute.cs
new file mode 100644
index 0000000..aa30af1
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/bullet/BulletAttribute.cs
@@ -0,0 +1,5 @@
+
+public class BulletAttribute
+{
+
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/package/Holster.cs b/DungeonShooting_Godot/src/game/activity/package/Holster.cs
new file mode 100644
index 0000000..a487f64
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/package/Holster.cs
@@ -0,0 +1,369 @@
+using System;
+using System.Collections.Generic;
+using Godot;
+
+///
+/// 角色身上的武器袋, 存储角色携带的武器
+///
+public class Holster
+{
+ ///
+ /// 归属者
+ ///
+ public Role Master { get; }
+
+ ///
+ /// 当前使用的武器对象
+ ///
+ public Weapon ActiveWeapon { get; private set; }
+
+ ///
+ /// 当前使用的武器的索引
+ ///
+ public int ActiveIndex { get; private set; } = 0;
+
+ ///
+ /// 武器袋容量
+ ///
+ public int Capacity { get; private set; } = 0;
+
+ ///
+ /// 武器插槽
+ ///
+ public Weapon[] Weapons { get; private set; }
+
+ public Holster(Role master)
+ {
+ Master = master;
+ //默认容量4
+ SetCapacity(4);
+ }
+
+ ///
+ /// 修改武器袋容量
+ ///
+ public void SetCapacity(int capacity)
+ {
+ if (capacity < 0)
+ {
+ capacity = 0;
+ }
+
+ if (capacity == Capacity)
+ {
+ return;
+ }
+
+ if (Weapons == null)
+ {
+ Weapons = new Weapon[capacity];
+ }
+ else if (Weapons.Length > capacity) //删减格子
+ {
+ var newArray = new Weapon[capacity];
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ if (i < capacity)
+ {
+ newArray[i] = Weapons[i];
+ }
+ else
+ {
+ Master.ThrowWeapon(i);
+ }
+ }
+
+ Weapons = newArray;
+ }
+ else //添加格子
+ {
+ var newArray = new Weapon[capacity];
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ newArray[i] = Weapons[i];
+ }
+ Weapons = newArray;
+ }
+ Capacity = capacity;
+
+ }
+
+ ///
+ /// 返回当前武器袋是否是空的
+ ///
+ public bool IsEmpty()
+ {
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ if (Weapons[i] != null)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// 返回当前武器袋是否还有空位
+ ///
+ public bool HasVacancy()
+ {
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ if (Weapons[i] == null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// 根据索引获取武器
+ ///
+ public Weapon GetWeapon(int index)
+ {
+ if (index < 0 || index >= Weapons.Length)
+ {
+ return null;
+ }
+ return Weapons[index];
+ }
+
+ ///
+ /// 根据武器id查找武器袋中该武器所在的位置, 如果没有, 则返回 -1
+ ///
+ /// 武器id
+ public int FindWeapon(string id)
+ {
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ var item = Weapons[i];
+ if (item != null && item.ItemId == id)
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ ///
+ /// 通过回调函数查询武器在武器袋中的位置, 如果没有, 则返回 -1
+ ///
+ public int FindWeapon(Func handler)
+ {
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ var item = Weapons[i];
+ if (item != null && handler(item, i))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ ///
+ /// 遍历所有武器
+ ///
+ public void ForEach(Action handler)
+ {
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ var item = Weapons[i];
+ if (item != null)
+ {
+ handler(item, i);
+ }
+ }
+ }
+
+ ///
+ /// 从武器袋中移除所有武器, 并返回
+ ///
+ public Weapon[] GetAndClearWeapon()
+ {
+ var weapons = new List();
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ var weapon = Weapons[i];
+ if (weapon != null)
+ {
+ weapon.GetParent().RemoveChild(weapon);
+ weapon.RemoveAt();
+ weapons.Add(weapon);
+ Weapons[i] = null;
+ }
+ }
+
+ return weapons.ToArray();
+ }
+
+ ///
+ /// 返回是否能放入武器
+ ///
+ /// 武器对象
+ public bool CanPickupWeapon(Weapon weapon)
+ {
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ var item = Weapons[i];
+ if (item == null)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// 拾起武器, 存入武器袋中, 返回存放在武器袋的位置, 如果容不下这把武器, 则会返回 -1
+ ///
+ /// 武器对象
+ /// 是否立即切换到该武器, 默认 true
+ public int PickupWeapon(Weapon weapon, bool exchange = true)
+ {
+ //已经被拾起了
+ if (weapon.Master != null)
+ {
+ return -1;
+ }
+ for (var i = 0; i < Weapons.Length; i++)
+ {
+ var item = Weapons[i];
+ if (item == null)
+ {
+ weapon.Pickup();
+ Weapons[i] = weapon;
+ weapon.PickUpWeapon(Master);
+ if (exchange)
+ {
+ ExchangeByIndex(i);
+ }
+
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ ///
+ /// 移除指定位置的武器, 并返回这个武器对象, 如果移除正在使用的这把武器, 则会自动切换到上一把武器
+ ///
+ /// 所在武器袋的位置索引
+ public Weapon RemoveWeapon(int index)
+ {
+ if (index < 0 || index >= Weapons.Length)
+ {
+ return null;
+ }
+ var weapon = Weapons[index];
+ if (weapon == null)
+ {
+ return null;
+ }
+ weapon.GetParent().RemoveChild(weapon);
+ Weapons[index] = null;
+
+ //如果是当前手持的武器, 就需要调用切换武器操作
+ if (index == ActiveIndex)
+ {
+ //没有其他武器了
+ if (ExchangePrev() == index)
+ {
+ ActiveIndex = 0;
+ ActiveWeapon = null;
+ }
+ }
+ weapon.RemoveAt();
+ return weapon;
+ }
+
+ ///
+ /// 切换到上一个武器
+ ///
+ public int ExchangePrev()
+ {
+ var index = ActiveIndex - 1;
+ do
+ {
+ if (index < 0)
+ {
+ index = Weapons.Length - 1;
+ }
+ if (ExchangeByIndex(index))
+ {
+ return index;
+ }
+ } while (index-- != ActiveIndex);
+ return -1;
+ }
+
+ ///
+ /// 切换到下一个武器,
+ ///
+ public int ExchangeNext()
+ {
+ var index = ActiveIndex + 1;
+ do
+ {
+ if (index >= Weapons.Length)
+ {
+ index = 0;
+ }
+ if (ExchangeByIndex(index))
+ {
+ return index;
+ }
+ }
+ while (index++ != ActiveIndex);
+ return -1;
+ }
+
+ ///
+ /// 切换到指定索引的武器
+ ///
+ public bool ExchangeByIndex(int index)
+ {
+ if (index == ActiveIndex && ActiveWeapon != null) return true;
+ if (index < 0 || index > Weapons.Length) return false;
+ var weapon = Weapons[index];
+ if (weapon == null) return false;
+
+ //将上一把武器放到背后
+ if (ActiveWeapon != null)
+ {
+ var tempParent = ActiveWeapon.GetParentOrNull();
+ if (tempParent != null)
+ {
+ tempParent.RemoveChild(ActiveWeapon);
+ Master.BackMountPoint.AddChild(ActiveWeapon);
+ Master.OnPutBackMount(ActiveWeapon, ActiveIndex);
+ ActiveWeapon.Conceal();
+ }
+ }
+
+ //更改父节点
+ var parent = weapon.GetParentOrNull();
+ if (parent == null)
+ {
+ Master.MountPoint.AddChild(weapon);
+ }
+ else if (parent != Master.MountPoint)
+ {
+ parent.RemoveChild(weapon);
+ Master.MountPoint.AddChild(weapon);
+ }
+
+ weapon.Position = Vector2.Zero;
+ weapon.Scale = Vector2.One;
+ weapon.RotationDegrees = 0;
+ weapon.Visible = true;
+ ActiveWeapon = weapon;
+ ActiveIndex = index;
+ ActiveWeapon.Active();
+ return true;
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/activity/shell/Shell.cs b/DungeonShooting_Godot/src/game/activity/shell/Shell.cs
new file mode 100644
index 0000000..310ee81
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/shell/Shell.cs
@@ -0,0 +1,22 @@
+
+using Godot;
+
+///
+/// 弹壳类
+///
+[Tool, GlobalClass]
+public partial class Shell : ActivityObject
+{
+ public override void OnInit()
+ {
+ base.OnInit();
+ ShadowOffset = new Vector2(0, 1);
+ ThrowCollisionSize = new Vector2(5, 5);
+ }
+
+ protected override void OnThrowOver()
+ {
+ EnableBehavior = false;
+ Collision.QueueFree();
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs
new file mode 100644
index 0000000..6122436
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs
@@ -0,0 +1,1183 @@
+using Godot;
+using System;
+using System.Collections.Generic;
+using Config;
+
+///
+/// 武器的基类
+///
+public abstract partial class Weapon : ActivityObject
+{
+ ///
+ /// 开火回调事件
+ ///
+ public event Action FireEvent;
+
+ ///
+ /// 武器属性数据
+ ///
+ public ExcelConfig.Weapon Attribute => _weaponAttribute;
+ private ExcelConfig.Weapon _weaponAttribute;
+ private ExcelConfig.Weapon _playerWeaponAttribute;
+ private ExcelConfig.Weapon _aiWeaponAttribute;
+
+ ///
+ /// 武器攻击的目标阵营
+ ///
+ public CampEnum TargetCamp { get; set; }
+
+ ///
+ /// 该武器的拥有者
+ ///
+ public Role Master { get; private set; }
+
+ ///
+ /// 当前弹夹弹药剩余量
+ ///
+ public int CurrAmmo { get; private set; }
+
+ ///
+ /// 剩余弹药量(备用弹药)
+ ///
+ public int ResidueAmmo { get; private set; }
+
+ ///
+ /// 武器管的开火点
+ ///
+ [Export, ExportFillNode]
+ public Marker2D FirePoint { get; set; }
+
+ ///
+ /// 弹壳抛出的点
+ ///
+ [Export, ExportFillNode]
+ public Marker2D ShellPoint { get; set; }
+
+ ///
+ /// 武器的当前散射半径
+ ///
+ public float CurrScatteringRange { get; private set; } = 0;
+
+ ///
+ /// 是否在换弹中
+ ///
+ ///
+ public bool Reloading { get; private set; } = false;
+
+ ///
+ /// 换弹计时器
+ ///
+ public float ReloadTimer { get; private set; } = 0;
+
+ ///
+ /// 换弹进度 (0 - 1)
+ ///
+ public float ReloadProgress
+ {
+ get
+ {
+ if (Attribute.AloneReload)
+ {
+ var num = 1f / Attribute.AmmoCapacity;
+ return num * (Attribute.AmmoCapacity - CurrAmmo - 1) + num * (ReloadTimer / Attribute.ReloadTime);
+ }
+
+ return ReloadTimer / Attribute.ReloadTime;
+ }
+ }
+
+ ///
+ /// 返回是否在蓄力中,
+ /// 注意, 属性仅在 Attribute.LooseShoot == false 时有正确的返回值, 否则返回 false
+ ///
+ public bool IsCharging => _looseShootFlag;
+
+ ///
+ /// 返回武器是否在武器袋中
+ ///
+ public bool IsInHolster => Master != null;
+
+ ///
+ /// 返回是否真正使用该武器
+ ///
+ public bool IsActive => Master != null && Master.Holster.ActiveWeapon == this;
+
+ ///
+ /// 动画播放器
+ ///
+ [Export, ExportFillNode]
+ public AnimationPlayer AnimationPlayer { get; set; }
+
+ ///
+ /// 是否自动播放 SpriteFrames 的动画
+ ///
+ public bool IsAutoPlaySpriteFrames { get; set; } = true;
+
+ //--------------------------------------------------------------------------------------------
+
+ //是否按下
+ private bool _triggerFlag = false;
+
+ //扳机计时器
+ private float _triggerTimer = 0;
+
+ //开火前延时时间
+ private float _delayedTime = 0;
+
+ //开火间隙时间
+ private float _fireInterval = 0;
+
+ //开火武器口角度
+ private float _fireAngle = 0;
+
+ //攻击冷却计时
+ private float _attackTimer = 0;
+
+ //攻击状态
+ private bool _attackFlag = false;
+
+ //按下的时间
+ private float _downTimer = 0;
+
+ //松开的时间
+ private float _upTimer = 0;
+
+ //连发次数
+ private float _continuousCount = 0;
+
+ //连发状态记录
+ private bool _continuousShootFlag = false;
+
+ //松开扳机是否开火
+ private bool _looseShootFlag = false;
+
+ //蓄力攻击时长
+ private float _chargeTime = 0;
+
+ //是否需要重置武器数据
+ private bool _dirtyFlag = false;
+
+ //当前后坐力导致的偏移长度
+ private float _currBacklashLength = 0;
+
+ // ----------------------------------------------
+ private uint _tempLayer;
+
+ private static bool _init = false;
+ private static Dictionary _weaponAttributeMap =
+ new Dictionary();
+
+ ///
+ /// 初始化武器属性数据
+ ///
+ public static void InitWeaponAttribute()
+ {
+ if (_init)
+ {
+ return;
+ }
+
+ _init = true;
+ foreach (var weaponAttr in ExcelConfig.Weapon_List)
+ {
+ if (string.IsNullOrEmpty(weaponAttr.WeaponId))
+ {
+ if (!_weaponAttributeMap.TryAdd(weaponAttr.WeaponId, weaponAttr))
+ {
+ GD.PrintErr("发现重复注册的武器属性: " + weaponAttr.Id);
+ }
+ }
+ }
+ }
+
+ private static ExcelConfig.Weapon _GetWeaponAttribute(string itemId)
+ {
+ if (_weaponAttributeMap.TryGetValue(itemId, out var attr))
+ {
+ return attr;
+ }
+
+ throw new Exception($"武器'{itemId}'没有在 Weapon 表中配置属性数据!");
+ }
+
+ public override void OnInit()
+ {
+ InitWeapon(_GetWeaponAttribute(ItemId));
+ }
+
+ ///
+ /// 初始化武器属性
+ ///
+ public void InitWeapon(ExcelConfig.Weapon attribute)
+ {
+ _playerWeaponAttribute = attribute;
+ _weaponAttribute = attribute;
+ if (!string.IsNullOrEmpty(attribute.AiUseAttributeId))
+ {
+ _aiWeaponAttribute = ExcelConfig.Weapon_Map[attribute.AiUseAttributeId];
+ }
+ else
+ {
+ _aiWeaponAttribute = attribute;
+ }
+ //未完成
+ //AnimatedSprite.Position = Attribute.ThrowSpritePosition;
+
+ if (Attribute.AmmoCapacity > Attribute.MaxAmmoCapacity)
+ {
+ Attribute.AmmoCapacity = Attribute.MaxAmmoCapacity;
+ GD.PrintErr("弹夹的容量不能超过弹药上限, 武器id: " + ItemId);
+ }
+ //弹药量
+ CurrAmmo = Attribute.AmmoCapacity;
+ //剩余弹药量
+ ResidueAmmo = Mathf.Min(Attribute.StandbyAmmoCapacity + CurrAmmo, Attribute.MaxAmmoCapacity) - CurrAmmo;
+
+ ThrowCollisionSize = attribute.ThrowCollisionSize.AsVector2();
+ }
+
+ ///
+ /// 单次开火时调用的函数
+ ///
+ protected abstract void OnFire();
+
+ ///
+ /// 发射子弹时调用的函数, 每发射一枚子弹调用一次,
+ /// 如果做霰弹武器效果, 一次开火发射5枚子弹, 则该函数调用5次
+ ///
+ /// 开火时枪口旋转角度
+ protected abstract void OnShoot(float fireRotation);
+
+ ///
+ /// 当按下扳机时调用
+ ///
+ protected virtual void OnDownTrigger()
+ {
+ }
+
+ ///
+ /// 当松开扳机时调用
+ ///
+ protected virtual void OnUpTrigger()
+ {
+ }
+
+ ///
+ /// 开始蓄力时调用,
+ /// 注意, 该函数仅在 Attribute.LooseShoot == false 时才能被调用
+ ///
+ protected virtual void OnStartCharge()
+ {
+ }
+
+ ///
+ /// 当开始换弹时调用
+ ///
+ protected virtual void OnReload()
+ {
+ }
+
+ ///
+ /// 当换弹完成时调用
+ ///
+ protected virtual void OnReloadFinish()
+ {
+ }
+
+ ///
+ /// 当武器被拾起时调用
+ ///
+ /// 拾起该武器的角色
+ protected virtual void OnPickUp(Role master)
+ {
+ }
+
+ ///
+ /// 当武器从武器袋中移除时调用
+ ///
+ protected virtual void OnRemove()
+ {
+ }
+
+ ///
+ /// 当武器被激活时调用, 也就是使用当武器时调用
+ ///
+ protected virtual void OnActive()
+ {
+ }
+
+ ///
+ /// 当武器被收起时调用
+ ///
+ protected virtual void OnConceal()
+ {
+ }
+
+ ///
+ /// 射击时调用, 返回消耗弹药数量, 默认为1, 如果返回为 0, 则不消耗弹药
+ ///
+ protected virtual int UseAmmoCount()
+ {
+ return 1;
+ }
+
+ public override void EnterTree()
+ {
+ base.EnterTree();
+ //收集落在地上的武器
+ if (IsInGround())
+ {
+ World.Weapon_UnclaimedWeapons.Add(this);
+ }
+ }
+
+ public override void ExitTree()
+ {
+ base.ExitTree();
+ World.Weapon_UnclaimedWeapons.Remove(this);
+ }
+
+ protected override void Process(float delta)
+ {
+ //GD.Print("AnimatedSprite: " + AnimatedSprite.Position);
+ //这把武器被扔在地上, 或者当前武器没有被使用
+ if (Master == null || Master.Holster.ActiveWeapon != this)
+ {
+ //_triggerTimer
+ _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0;
+ //攻击冷却计时
+ _attackTimer = _attackTimer > 0 ? _attackTimer - delta : 0;
+ //武器的当前散射半径
+ CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta,
+ Attribute.StartScatteringRange);
+ //松开扳机
+ if (_triggerFlag || _downTimer > 0)
+ {
+ UpTrigger();
+ _downTimer = 0;
+ }
+
+ _triggerFlag = false;
+
+ //重置数据
+ if (_dirtyFlag)
+ {
+ _dirtyFlag = false;
+ Reloading = false;
+ ReloadTimer = 0;
+ _attackFlag = false;
+ _continuousCount = 0;
+ _delayedTime = 0;
+ _upTimer = 0;
+ _looseShootFlag = false;
+ _chargeTime = 0;
+ }
+ }
+ else //正在使用中
+ {
+ _dirtyFlag = true;
+ //换弹
+ if (Reloading)
+ {
+ ReloadTimer -= delta;
+ if (ReloadTimer <= 0)
+ {
+ ReloadSuccess();
+ }
+ }
+
+ // 攻击的计时器
+ if (_attackTimer > 0)
+ {
+ _attackTimer -= delta;
+ if (_attackTimer < 0)
+ {
+ _delayedTime += _attackTimer;
+ _attackTimer = 0;
+ //枪口默认角度
+ RotationDegrees = -Attribute.DefaultAngle;
+ }
+ }
+ else if (_delayedTime > 0) //攻击延时
+ {
+ _delayedTime -= delta;
+ if (_attackTimer < 0)
+ {
+ _delayedTime = 0;
+ }
+ }
+
+ //扳机判定
+ if (_triggerFlag)
+ {
+ if (_looseShootFlag) //蓄力时长
+ {
+ _chargeTime += delta;
+ }
+
+ _downTimer += delta;
+ if (_upTimer > 0) //第一帧按下扳机
+ {
+ DownTrigger();
+ _upTimer = 0;
+ }
+ }
+ else
+ {
+ _upTimer += delta;
+ if (_downTimer > 0) //第一帧松开扳机
+ {
+ UpTrigger();
+ _downTimer = 0;
+ }
+ }
+
+ //连发判断
+ if (!_looseShootFlag && _continuousCount > 0 && _delayedTime <= 0 && _attackTimer <= 0)
+ {
+ //连发开火
+ TriggerFire();
+ }
+
+ if (!_attackFlag && _attackTimer <= 0)
+ {
+ if (_upTimer >= Attribute.ScatteringRangeBackTime)
+ {
+ CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta,
+ Attribute.StartScatteringRange);
+ }
+ }
+
+ _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0;
+ _triggerFlag = false;
+ _attackFlag = false;
+
+ //武器身回归
+ //Position = Position.MoveToward(Vector2.Zero, Attribute.BacklashRegressionSpeed * delta).Rotated(Rotation);
+ _currBacklashLength = Mathf.MoveToward(_currBacklashLength, 0, Attribute.BacklashRegressionSpeed * delta);
+ Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
+ if (_attackTimer > 0)
+ {
+ RotationDegrees = Mathf.Lerp(
+ _fireAngle, -Attribute.DefaultAngle,
+ Mathf.Clamp((_fireInterval - _attackTimer) * Attribute.UpliftAngleRestore / _fireInterval, 0, 1)
+ );
+ }
+ }
+ }
+
+ ///
+ /// 返回武器是否在地上
+ ///
+ ///
+ public bool IsInGround()
+ {
+ return Master == null && GetParent() == GameApplication.Instance.World.NormalLayer;
+ }
+
+ ///
+ /// 扳机函数, 调用即视为按下扳机
+ ///
+ public void Trigger()
+ {
+ //这一帧已经按过了, 不需要再按下
+ if (_triggerFlag) return;
+
+ //是否第一帧按下
+ var justDown = _downTimer == 0;
+ //是否能发射
+ var flag = false;
+ if (_continuousCount <= 0) //不能处于连发状态下
+ {
+ if (Attribute.ContinuousShoot) //自动射击
+ {
+ if (_triggerTimer > 0)
+ {
+ if (_continuousShootFlag)
+ {
+ flag = true;
+ }
+ }
+ else
+ {
+ flag = true;
+ if (_delayedTime <= 0 && _attackTimer <= 0)
+ {
+ _continuousShootFlag = true;
+ }
+ }
+ }
+ else //半自动
+ {
+ if (justDown && _triggerTimer <= 0 && _attackTimer <= 0)
+ {
+ flag = true;
+ }
+ }
+ }
+
+ if (flag)
+ {
+ var fireFlag = true; //是否能开火
+ if (Reloading) //换弹中
+ {
+ if (CurrAmmo > 0 && Attribute.AloneReload && Attribute.AloneReloadCanShoot) //立即停止换弹
+ {
+ Reloading = false;
+ ReloadTimer = 0;
+ }
+ else
+ {
+ fireFlag = false;
+ }
+ }
+ else if (CurrAmmo <= 0) //子弹不够
+ {
+ fireFlag = false;
+ if (justDown)
+ {
+ //第一帧按下, 触发换弹
+ Reload();
+ }
+ }
+
+ if (fireFlag)
+ {
+ if (justDown)
+ {
+ //开火前延时
+ _delayedTime = Attribute.DelayedTime;
+ //扳机按下间隔
+ _triggerTimer = Attribute.TriggerInterval;
+ //连发数量
+ if (!Attribute.ContinuousShoot)
+ {
+ _continuousCount =
+ Utils.RandomRangeInt(Attribute.MinContinuousCount, Attribute.MaxContinuousCount);
+ }
+ }
+
+ if (_delayedTime <= 0 && _attackTimer <= 0)
+ {
+ if (Attribute.LooseShoot) //松发开火
+ {
+ _looseShootFlag = true;
+ OnStartCharge();
+ }
+ else
+ {
+ //开火
+ TriggerFire();
+ }
+ }
+
+ _attackFlag = true;
+ }
+
+ }
+
+ _triggerFlag = true;
+ }
+
+ ///
+ /// 返回是否按下扳机
+ ///
+ public bool IsPressTrigger()
+ {
+ return _triggerFlag;
+ }
+
+ ///
+ /// 获取本次扳机按下的时长, 单位: 秒
+ ///
+ public float GetTriggerDownTime()
+ {
+ return _downTimer;
+ }
+
+ ///
+ /// 获取扳机蓄力时长, 计算按下扳机后从可以开火到当前一共经过了多长时间, 可用于计算蓄力攻击
+ /// 注意, 该函数仅在 Attribute.LooseShoot == false 时有正确的返回值, 否则返回 0
+ ///
+ public float GetTriggerChargeTime()
+ {
+ return _chargeTime;
+ }
+
+ ///
+ /// 获取延时射击倒计时, 单位: 秒
+ ///
+ public float GetDelayedAttackTime()
+ {
+ return _delayedTime;
+ }
+
+ ///
+ /// 刚按下扳机
+ ///
+ private void DownTrigger()
+ {
+ OnDownTrigger();
+ }
+
+ ///
+ /// 刚松开扳机
+ ///
+ private void UpTrigger()
+ {
+ _continuousShootFlag = false;
+ if (_delayedTime > 0)
+ {
+ _continuousCount = 0;
+ }
+
+ //松发开火执行
+ if (_looseShootFlag)
+ {
+ _looseShootFlag = false;
+ if (_chargeTime >= Attribute.MinChargeTime) //判断蓄力是否够了
+ {
+ TriggerFire();
+ }
+ else //不能攻击
+ {
+ _continuousCount = 0;
+ }
+ _chargeTime = 0;
+ }
+
+ OnUpTrigger();
+ }
+
+ ///
+ /// 触发开火
+ ///
+ private void TriggerFire()
+ {
+ _continuousCount = _continuousCount > 0 ? _continuousCount - 1 : 0;
+
+ //减子弹数量
+ if (_playerWeaponAttribute != _weaponAttribute) //Ai使用该武器, 有一定概率不消耗弹药
+ {
+ if (Utils.RandomRangeFloat(0, 1) < _weaponAttribute.AiAmmoConsumptionProbability) //触发消耗弹药
+ {
+ CurrAmmo -= UseAmmoCount();
+ }
+ }
+ else
+ {
+ CurrAmmo -= UseAmmoCount();
+ }
+
+ //开火间隙
+ _fireInterval = 60 / Attribute.StartFiringSpeed;
+ //攻击冷却
+ _attackTimer += _fireInterval;
+
+ //播放开火动画
+ if (IsAutoPlaySpriteFrames)
+ {
+ PlaySpriteAnimation(AnimatorNames.Fire);
+ }
+ //触发开火函数
+ OnFire();
+
+ //开火发射的子弹数量
+ var bulletCount = Utils.RandomRangeInt(Attribute.MaxFireBulletCount, Attribute.MinFireBulletCount);
+ //武器口角度
+ var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();
+
+ //先算武器口方向
+ var tempRotation = Utils.RandomRangeFloat(-angle, angle);
+ var tempAngle = Mathf.RadToDeg(tempRotation);
+
+ //开火时枪口角度
+ var fireRotation = Mathf.DegToRad(Master.MountPoint.RealRotationDegrees) + tempRotation;
+ //创建子弹
+ for (int i = 0; i < bulletCount; i++)
+ {
+ //发射子弹
+ OnShoot(fireRotation);
+ }
+
+ //当前的散射半径
+ CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue,
+ Attribute.FinalScatteringRange);
+ //武器的旋转角度
+ tempAngle -= Attribute.UpliftAngle;
+ RotationDegrees = tempAngle;
+ _fireAngle = tempAngle;
+
+ //武器身位置
+ var max = Mathf.Abs(Mathf.Max(Attribute.MaxBacklash, Attribute.MinBacklash));
+ _currBacklashLength = Mathf.Clamp(
+ _currBacklashLength - Utils.RandomRangeFloat(Attribute.MinBacklash, Attribute.MaxBacklash),
+ -max, max
+ );
+ Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
+
+ if (FireEvent != null)
+ {
+ FireEvent(this);
+ }
+ }
+
+ ///
+ /// 获取武器攻击的目标层级
+ ///
+ ///
+ public uint GetAttackLayer()
+ {
+ return Master != null ? Master.AttackLayer : Role.DefaultAttackLayer;
+ }
+
+ ///
+ /// 返回弹药是否到达上限
+ ///
+ public bool IsAmmoFull()
+ {
+ return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
+ }
+
+ ///
+ /// 返回弹夹是否打空
+ ///
+ public bool IsAmmoEmpty()
+ {
+ return CurrAmmo == 0;
+ }
+
+ ///
+ /// 返回是否弹药耗尽
+ ///
+ public bool IsTotalAmmoEmpty()
+ {
+ return CurrAmmo + ResidueAmmo == 0;
+ }
+
+ ///
+ /// 强制修改当前弹夹弹药量
+ ///
+ public void SetCurrAmmo(int count)
+ {
+ CurrAmmo = Mathf.Clamp(count, 0, Attribute.AmmoCapacity);
+ }
+
+ ///
+ /// 强制修改备用弹药量
+ ///
+ public void SetResidueAmmo(int count)
+ {
+ ResidueAmmo = Mathf.Clamp(count, 0, Attribute.MaxAmmoCapacity - CurrAmmo);
+ }
+
+ ///
+ /// 强制修改弹药量, 优先改动备用弹药
+ ///
+ public void SetTotalAmmo(int total)
+ {
+ if (total < 0)
+ {
+ return;
+ }
+ var totalAmmo = CurrAmmo + ResidueAmmo;
+ if (totalAmmo == total)
+ {
+ return;
+ }
+
+ if (total > totalAmmo) //弹药增加
+ {
+ ResidueAmmo = Mathf.Min(total - CurrAmmo, Attribute.MaxAmmoCapacity - CurrAmmo);
+ }
+ else //弹药减少
+ {
+ if (CurrAmmo < total)
+ {
+ ResidueAmmo = total - CurrAmmo;
+ }
+ else
+ {
+ CurrAmmo = total;
+ ResidueAmmo = 0;
+ }
+ }
+ }
+
+ ///
+ /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
+ ///
+ /// 弹药数量
+ private int PickUpAmmo(int count)
+ {
+ var num = ResidueAmmo;
+ ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
+ return count - ResidueAmmo + num;
+ }
+
+ ///
+ /// 触发换弹
+ ///
+ public void Reload()
+ {
+ if (CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && !Reloading)
+ {
+ Reloading = true;
+ ReloadTimer = Attribute.ReloadTime;
+ //播放换弹动画
+ if (IsAutoPlaySpriteFrames)
+ {
+ PlaySpriteAnimation(AnimatorNames.Reload);
+ }
+ OnReload();
+ }
+ }
+
+ ///
+ /// 换弹计时器时间到, 执行换弹操作
+ ///
+ private void ReloadSuccess()
+ {
+ if (Attribute.AloneReload) //单独装填
+ {
+ if (ResidueAmmo >= Attribute.AloneReloadCount) //剩余子弹充足
+ {
+ if (CurrAmmo + Attribute.AloneReloadCount <= Attribute.AmmoCapacity)
+ {
+ ResidueAmmo -= Attribute.AloneReloadCount;
+ CurrAmmo += Attribute.AloneReloadCount;
+ }
+ else //子弹满了
+ {
+ var num = Attribute.AmmoCapacity - CurrAmmo;
+ CurrAmmo = Attribute.AmmoCapacity;
+ ResidueAmmo -= num;
+ }
+ }
+ else if (ResidueAmmo != 0) //剩余子弹不足
+ {
+ if (ResidueAmmo + CurrAmmo <= Attribute.AmmoCapacity)
+ {
+ CurrAmmo += ResidueAmmo;
+ ResidueAmmo = 0;
+ }
+ else //子弹满了
+ {
+ var num = Attribute.AmmoCapacity - CurrAmmo;
+ CurrAmmo = Attribute.AmmoCapacity;
+ ResidueAmmo -= num;
+ }
+ }
+
+ if (ResidueAmmo == 0 || CurrAmmo == Attribute.AmmoCapacity) //换弹结束
+ {
+ Reloading = false;
+ ReloadTimer = 0;
+ OnReloadFinish();
+ }
+ else
+ {
+ ReloadTimer = Attribute.ReloadTime;
+ //播放换弹动画
+ if (IsAutoPlaySpriteFrames)
+ {
+ PlaySpriteAnimation(AnimatorNames.Reload);
+ }
+ OnReload();
+ }
+ }
+ else //换弹结束
+ {
+ if (CurrAmmo + ResidueAmmo >= Attribute.AmmoCapacity)
+ {
+ ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
+ CurrAmmo = Attribute.AmmoCapacity;
+ }
+ else
+ {
+ CurrAmmo += ResidueAmmo;
+ ResidueAmmo = 0;
+ }
+
+ Reloading = false;
+ ReloadTimer = 0;
+ OnReloadFinish();
+ }
+ }
+
+ //播放动画
+ private void PlaySpriteAnimation(string name)
+ {
+ var spriteFrames = AnimatedSprite.SpriteFrames;
+ if (spriteFrames != null && spriteFrames.HasAnimation(name))
+ {
+ AnimatedSprite.Play(name);
+ }
+ }
+
+ public override CheckInteractiveResult CheckInteractive(ActivityObject master)
+ {
+ var result = new CheckInteractiveResult(this);
+
+ if (master is Role roleMaster) //碰到角色
+ {
+ if (Master == null)
+ {
+ var masterWeapon = roleMaster.Holster.ActiveWeapon;
+ //查找是否有同类型武器
+ var index = roleMaster.Holster.FindWeapon(ItemId);
+ if (index != -1) //如果有这个武器
+ {
+ if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
+ {
+ var targetWeapon = roleMaster.Holster.GetWeapon(index);
+ if (!targetWeapon.IsAmmoFull()) //背包里面的武器子弹未满
+ {
+ //可以互动拾起弹药
+ result.CanInteractive = true;
+ result.Message = Attribute.Name;
+ result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_bullet_png;
+ return result;
+ }
+ }
+ }
+ else //没有武器
+ {
+ if (roleMaster.Holster.CanPickupWeapon(this)) //能拾起武器
+ {
+ //可以互动, 拾起武器
+ result.CanInteractive = true;
+ result.Message = Attribute.Name;
+ result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_pickup_png;
+ return result;
+ }
+ else if (masterWeapon != null && masterWeapon.Attribute.WeightType == Attribute.WeightType) //替换武器
+ {
+ //可以互动, 切换武器
+ result.CanInteractive = true;
+ result.Message = Attribute.Name;
+ result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_replace_png;
+ return result;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public override void Interactive(ActivityObject master)
+ {
+ if (master is Role roleMaster) //与role互动
+ {
+ var holster = roleMaster.Holster;
+ //查找是否有同类型武器
+ var index = holster.FindWeapon(ItemId);
+ if (index != -1) //如果有这个武器
+ {
+ if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
+ {
+ return;
+ }
+
+ var weapon = holster.GetWeapon(index);
+ //子弹上限
+ var maxCount = Attribute.MaxAmmoCapacity;
+ //是否捡到子弹
+ var flag = false;
+ if (ResidueAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
+ {
+ var count = weapon.PickUpAmmo(ResidueAmmo);
+ if (count != ResidueAmmo)
+ {
+ ResidueAmmo = count;
+ flag = true;
+ }
+ }
+
+ if (CurrAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
+ {
+ var count = weapon.PickUpAmmo(CurrAmmo);
+ if (count != CurrAmmo)
+ {
+ CurrAmmo = count;
+ flag = true;
+ }
+ }
+
+ //播放互动效果
+ if (flag)
+ {
+ Throw(GlobalPosition, 0, Utils.RandomRangeInt(20, 50), Vector2.Zero, Utils.RandomRangeInt(-180, 180));
+ }
+ }
+ else //没有武器
+ {
+ if (holster.PickupWeapon(this) == -1)
+ {
+ //替换武器
+ roleMaster.ThrowWeapon();
+ roleMaster.PickUpWeapon(this);
+ }
+ }
+ }
+ }
+
+ ///
+ /// 获取当前武器真实的旋转角度(弧度制), 由于武器旋转时加入了旋转吸附, 所以需要通过该函数来来知道当前武器的真实旋转角度
+ ///
+ public float GetRealGlobalRotation()
+ {
+ return Mathf.DegToRad(Master.MountPoint.RealRotationDegrees) + Rotation;
+ }
+
+ ///
+ /// 触发扔掉武器抛出的效果, 并不会管武器是否在武器袋中
+ ///
+ /// 触发扔掉该武器的的角色
+ public void ThrowWeapon(Role master)
+ {
+ ThrowWeapon(master, master.GlobalPosition);
+ }
+
+ ///
+ /// 触发扔掉武器抛出的效果, 并不会管武器是否在武器袋中
+ ///
+ /// 触发扔掉该武器的的角色
+ /// 投抛起始位置
+ public void ThrowWeapon(Role master, Vector2 startPosition)
+ {
+ //阴影偏移
+ ShadowOffset = new Vector2(0, 2);
+
+ if (master.Face == FaceDirection.Left)
+ {
+ Scale *= new Vector2(1, -1);
+ }
+
+ var angle = master.MountPoint.GlobalRotationDegrees;
+ GlobalRotationDegrees = angle;
+
+ //继承role的移动速度
+ InheritVelocity(master);
+
+ var startHeight = -master.MountPoint.Position.Y;
+ var direction = angle + Utils.RandomRangeInt(-20, 20);
+ var velocity = new Vector2(20, 0).Rotated(direction * Mathf.Pi / 180);
+ var yf = Utils.RandomRangeInt(50, 70);
+ var rotate = Utils.RandomRangeInt(-60, 60);
+ Throw(startPosition, startHeight, yf, velocity, rotate);
+ }
+
+ protected override void OnThrowStart()
+ {
+ //禁用碰撞
+ //Collision.Disabled = true;
+ AnimationPlayer.Play(AnimatorNames.Floodlight);
+ }
+
+ protected override void OnThrowOver()
+ {
+ //启用碰撞
+ //Collision.Disabled = false;
+ AnimationPlayer.Play(AnimatorNames.Floodlight);
+ }
+
+ ///
+ /// 触发拾起到 Holster, 这个函数由 Holster 对象调用
+ ///
+ public void PickUpWeapon(Role master)
+ {
+ Master = master;
+ if (master.IsAi)
+ {
+ _weaponAttribute = _aiWeaponAttribute;
+ }
+ else
+ {
+ _weaponAttribute = _playerWeaponAttribute;
+ }
+ //停止动画
+ AnimationPlayer.Stop();
+ //清除泛白效果
+ SetBlendSchedule(0);
+ ZIndex = 0;
+ //禁用碰撞
+ //Collision.Disabled = true;
+ //修改层级
+ _tempLayer = CollisionLayer;
+ CollisionLayer = PhysicsLayer.InHand;
+ //清除 Ai 拾起标记
+ RemoveSign(SignNames.AiFindWeaponSign);
+ OnPickUp(master);
+ }
+
+ ///
+ /// 触发从 Holster 中移除, 这个函数由 Holster 对象调用
+ ///
+ public void RemoveAt()
+ {
+ Master = null;
+ CollisionLayer = _tempLayer;
+ _weaponAttribute = _playerWeaponAttribute;
+ //未完成
+ //AnimatedSprite.Position = Attribute.ThrowSpritePosition;
+ //清除 Ai 拾起标记
+ RemoveSign(SignNames.AiFindWeaponSign);
+ OnRemove();
+ }
+
+ ///
+ /// 触发启用武器
+ ///
+ public void Active()
+ {
+ //调整阴影
+ ShadowOffset = new Vector2(0, Master.GlobalPosition.Y - GlobalPosition.Y);
+ //枪口默认抬起角度
+ RotationDegrees = -Attribute.DefaultAngle;
+ ShowShadowSprite();
+ OnActive();
+ }
+
+ ///
+ /// 触发收起武器
+ ///
+ public void Conceal()
+ {
+ HideShadowSprite();
+ OnConceal();
+ }
+
+ //-------------------------- ----- 子弹相关 -----------------------------
+
+ ///
+ /// 投抛弹壳的默认实现方式, shellId为弹壳id, 不需要前缀
+ ///
+ protected ActivityObject ThrowShell(string shellId)
+ {
+ var shellPosition = Master.MountPoint.Position + ShellPoint.Position;
+ var startPos = ShellPoint.GlobalPosition;
+ var startHeight = -shellPosition.Y;
+ startPos.Y += startHeight;
+ var direction = GlobalRotationDegrees + Utils.RandomRangeInt(-30, 30) + 180;
+ var verticalSpeed = Utils.RandomRangeInt(60, 120);
+ var velocity = new Vector2(Utils.RandomRangeInt(20, 60), 0).Rotated(direction * Mathf.Pi / 180);
+ var rotate = Utils.RandomRangeInt(-720, 720);
+ var shell = Create(ActivityIdPrefix.Shell + shellId);
+ shell.Rotation = Master.MountPoint.RealRotation;
+ shell.InheritVelocity(Master);
+ shell.Throw(startPos, startHeight, verticalSpeed, velocity, rotate);
+ return shell;
+ }
+
+ //-------------------------------- Ai相关 -----------------------------
+
+ ///
+ /// 获取 Ai 对于该武器的评分, 评分越高, 代表 Ai 会越优先选择该武器, 如果为 -1, 则表示 Ai 不会使用该武器
+ ///
+ public float GetAiScore()
+ {
+ return 1;
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/weapon/WeaponWeightType.cs b/DungeonShooting_Godot/src/game/activity/weapon/WeaponWeightType.cs
new file mode 100644
index 0000000..5528683
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/weapon/WeaponWeightType.cs
@@ -0,0 +1,19 @@
+
+///
+/// 根据重量划分的武器类型
+///
+public enum WeaponWeightType
+{
+ ///
+ /// 副武器
+ ///
+ DeputyWeapon = 1,
+ ///
+ /// 主武器
+ ///
+ MainWeapon = 2,
+ ///
+ /// 重型武器
+ ///
+ HeavyWeapon = 3
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/weapon/gun/Gun.cs b/DungeonShooting_Godot/src/game/activity/weapon/gun/Gun.cs
new file mode 100644
index 0000000..4267f78
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/weapon/gun/Gun.cs
@@ -0,0 +1,197 @@
+using Godot;
+
+///
+/// 普通的枪
+///
+[Tool, GlobalClass]
+public partial class Gun : Weapon
+{
+ // //步枪属性数据
+ // private class RifleAttribute : WeaponAttribute
+ // {
+ // public RifleAttribute()
+ // {
+ // Name = "步枪";
+ // Icon = ResourcePath.resource_sprite_gun_gun4_png;
+ // SpriteFrames = ResourcePath.resource_spriteFrames_Weapon0001_tres;
+ // Weight = 40;
+ // ThrowSpritePosition = new Vector2(0.4f, -2.6f);
+ // StartFiringSpeed = 480;
+ // StartScatteringRange = 30;
+ // FinalScatteringRange = 90;
+ // ScatteringRangeAddValue = 2f;
+ // ScatteringRangeBackSpeed = 40;
+ // //连发
+ // ContinuousShoot = true;
+ // AmmoCapacity = 30;
+ // StandbyAmmoCapacity = 30 * 3;
+ // MaxAmmoCapacity = 30 * 3;
+ // //扳机检测间隔
+ // TriggerInterval = 0f;
+ //
+ // //开火前延时
+ // DelayedTime = 0f;
+ // //攻击距离
+ // MinDistance = 300;
+ // MaxDistance = 400;
+ // //发射子弹数量
+ // MinFireBulletCount = 1;
+ // MaxFireBulletCount = 1;
+ // //抬起角度
+ // UpliftAngle = 10;
+ // //开火位置
+ // FirePosition = new Vector2(21, -3f);
+ // //精灵位置
+ // SpritePosition = new Vector2(6, -1);
+ // ShellPosition = new Vector2(7.5f, -4.5f);
+ //
+ // AiUseAttribute = Clone();
+ // AiUseAttribute.AiTargetLockingTime = 0.5f;
+ // AiUseAttribute.TriggerInterval = 3f;
+ // AiUseAttribute.ContinuousShoot = false;
+ // AiUseAttribute.MinContinuousCount = 3;
+ // AiUseAttribute.MaxContinuousCount = 3;
+ // }
+ // }
+ //
+ // //手枪属性数据
+ // private class PistolAttribute : WeaponAttribute
+ // {
+ // public PistolAttribute()
+ // {
+ // Name = "手枪";
+ // Icon = ResourcePath.resource_sprite_gun_gun3_png;
+ // SpriteFrames = ResourcePath.resource_spriteFrames_Weapon0003_tres;
+ // Weight = 20;
+ // ThrowSpritePosition = new Vector2(0.4f, -2.6f);
+ // WeightType = WeaponWeightType.DeputyWeapon;
+ // StartFiringSpeed = 300;
+ // FinalFiringSpeed = 300;
+ // StartScatteringRange = 5;
+ // FinalScatteringRange = 60;
+ // ScatteringRangeAddValue = 8f;
+ // ScatteringRangeBackSpeed = 40;
+ // ScatteringRangeBackTime = 0.5f;
+ // //连发
+ // ContinuousShoot = false;
+ // AmmoCapacity = 12;
+ // StandbyAmmoCapacity = 72;
+ // MaxAmmoCapacity = 72;
+ // //扳机检测间隔
+ // TriggerInterval = 0.1f;
+ // //连发数量
+ // MinContinuousCount = 1;
+ // MaxContinuousCount = 1;
+ // //开火前延时
+ // DelayedTime = 0f;
+ // //攻击距离
+ // MinDistance = 250;
+ // MaxDistance = 300;
+ // //发射子弹数量
+ // MinFireBulletCount = 1;
+ // MaxFireBulletCount = 1;
+ // //抬起角度
+ // UpliftAngle = 20;
+ // //开火位置
+ // FirePosition = new Vector2(13, -2);
+ // //精灵位置
+ // SpritePosition = new Vector2(5, 0);
+ // ShellPosition = new Vector2(5, -3);
+ //
+ // AiUseAttribute = Clone();
+ // AiUseAttribute.AiTargetLockingTime = 1f;
+ // AiUseAttribute.TriggerInterval = 2f;
+ // }
+ // }
+ //
+ // //狙击步枪
+ // private class SniperRifleAttribute : WeaponAttribute
+ // {
+ // public SniperRifleAttribute()
+ // {
+ // Name = "狙击步枪";
+ // Icon = ResourcePath.resource_sprite_gun_gun3_png;
+ // SpriteFrames = ResourcePath.resource_spriteFrames_Weapon0005_tres;
+ // Weight = 20;
+ // ThrowSpritePosition = new Vector2(0.4f, -2.6f);
+ // WeightType = WeaponWeightType.DeputyWeapon;
+ // StartFiringSpeed = 60;
+ // FinalFiringSpeed = 60;
+ // StartScatteringRange = 0;
+ // FinalScatteringRange = 20;
+ // ScatteringRangeAddValue = 10;
+ // ScatteringRangeBackSpeed = 10;
+ // //连发
+ // ContinuousShoot = false;
+ // AmmoCapacity = 10;
+ // StandbyAmmoCapacity = 50;
+ // MaxAmmoCapacity = 50;
+ // //扳机检测间隔
+ // TriggerInterval = 0.1f;
+ // //连发数量
+ // MinContinuousCount = 1;
+ // MaxContinuousCount = 1;
+ // //开火前延时
+ // DelayedTime = 0f;
+ // //攻击距离
+ // MinDistance = 600;
+ // MaxDistance = 800;
+ // //发射子弹数量
+ // MinFireBulletCount = 1;
+ // MaxFireBulletCount = 1;
+ // //抬起角度
+ // UpliftAngle = 15;
+ // UpliftAngleRestore = 3.5f;
+ // //开火位置
+ // FirePosition = new Vector2(28, -3.5f);
+ // //精灵位置
+ // SpritePosition = new Vector2(9, 0);
+ // ShellPosition = new Vector2(7, -3.5f);
+ // MinBacklash = 6;
+ // MinBacklash = 8;
+ // BacklashRegressionSpeed = 20;
+ // ReloadTime = 3.5f;
+ //
+ // AiUseAttribute = Clone();
+ // AiUseAttribute.AiTargetLockingTime = 1f;
+ // AiUseAttribute.TriggerInterval = 2f;
+ // }
+ // }
+
+ protected override void OnFire()
+ {
+ //创建一个弹壳
+ ThrowShell("0001");
+
+ if (Master == Player.Current)
+ {
+ //创建抖动
+ GameCamera.Main.DirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 2f);
+ }
+
+ //创建开火特效
+ var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_ShotFire_tscn);
+ var sprite = packedScene.Instantiate();
+ sprite.GlobalPosition = FirePoint.GlobalPosition;
+ sprite.GlobalRotation = FirePoint.GlobalRotation;
+ sprite.AddToActivityRoot(RoomLayerEnum.YSortLayer);
+
+ //播放射击音效
+ SoundManager.PlaySoundEffectPosition(ResourcePath.resource_sound_sfx_ordinaryBullet2_mp3, GameApplication.Instance.ViewToGlobalPosition(GlobalPosition), -8);
+ }
+
+ protected override void OnShoot(float fireRotation)
+ {
+ //创建子弹
+ var bullet = ActivityObject.Create(Attribute.BulletId);
+ bullet.Init(
+ this,
+ 350,
+ Utils.RandomRangeFloat(Attribute.MinDistance, Attribute.MaxDistance),
+ FirePoint.GlobalPosition,
+ fireRotation,
+ GetAttackLayer()
+ );
+ bullet.PutDown(RoomLayerEnum.YSortLayer);
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/weapon/gun/Shotgun.cs b/DungeonShooting_Godot/src/game/activity/weapon/gun/Shotgun.cs
new file mode 100644
index 0000000..4490496
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/weapon/gun/Shotgun.cs
@@ -0,0 +1,105 @@
+using Godot;
+
+[Tool, GlobalClass]
+public partial class Shotgun : Weapon
+{
+
+ // private class ShotgunAttribute : WeaponAttribute
+ // {
+ // public ShotgunAttribute()
+ // {
+ // Name = "霰弹枪";
+ // Icon = ResourcePath.resource_sprite_gun_gun2_png;
+ // SpriteFrames = ResourcePath.resource_spriteFrames_Weapon0002_tres;
+ // Weight = 40;
+ // ThrowSpritePosition = new Vector2(0.4f, -2.6f);
+ // StartFiringSpeed = 400;
+ // StartScatteringRange = 30;
+ // FinalScatteringRange = 90;
+ // ScatteringRangeAddValue = 50f;
+ // ScatteringRangeBackSpeed = 50;
+ // //连发
+ // ContinuousShoot = false;
+ // AmmoCapacity = 7;
+ // StandbyAmmoCapacity = 42;
+ // MaxAmmoCapacity = 42;
+ // AloneReload = true;
+ // AloneReloadCanShoot = true;
+ // ReloadTime = 0.6f;
+ // //连发数量
+ // MinContinuousCount = 1;
+ // MaxContinuousCount = 1;
+ // //开火前延时
+ // DelayedTime = 0f;
+ // //攻击距离
+ // MinDistance = 200;
+ // MaxDistance = 250;
+ // //发射子弹数量
+ // MinFireBulletCount = 5;
+ // MaxFireBulletCount = 5;
+ // //抬起角度
+ // UpliftAngle = 15;
+ // MaxBacklash = 6;
+ // MinBacklash = 5;
+ // //开火位置
+ // FirePosition = new Vector2(22, -4);
+ // //精灵位置
+ // SpritePosition = new Vector2(9, -2);
+ // ShellPosition = new Vector2(3, -4);
+ //
+ // BulletId = ActivityIdPrefix.Bullet + "0002";
+ //
+ // AiUseAttribute = Clone();
+ // AiUseAttribute.AiTargetLockingTime = 0.2f;
+ // AiUseAttribute.TriggerInterval = 3.5f;
+ // }
+ // }
+
+ ///
+ /// 弹壳预制体
+ ///
+ public PackedScene ShellPack;
+
+ public override void OnInit()
+ {
+ base.OnInit();
+ ShellPack = ResourceManager.Load(ResourcePath.prefab_weapon_shell_ShellCase_tscn);
+ }
+
+ protected override void OnFire()
+ {
+ //创建一个弹壳
+ ThrowShell("0001");
+
+ if (Master == Player.Current)
+ {
+ //创建抖动
+ GameCamera.Main.DirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 2f);
+ }
+
+ //创建开火特效
+ var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_ShotFire_tscn);
+ var sprite = packedScene.Instantiate();
+ sprite.GlobalPosition = FirePoint.GlobalPosition;
+ sprite.GlobalRotation = FirePoint.GlobalRotation;
+ sprite.AddToActivityRoot(RoomLayerEnum.YSortLayer);
+
+ //播放射击音效
+ SoundManager.PlaySoundEffectPosition(ResourcePath.resource_sound_sfx_ordinaryBullet3_mp3, GameApplication.Instance.ViewToGlobalPosition(GlobalPosition), -15);
+ }
+
+ protected override void OnShoot(float fireRotation)
+ {
+ //创建子弹
+ var bullet = ActivityObject.Create(Attribute.BulletId);
+ bullet.Init(
+ this,
+ Utils.RandomRangeInt(280, 380),
+ Utils.RandomRangeFloat(Attribute.MinDistance, Attribute.MaxDistance),
+ FirePoint.GlobalPosition,
+ fireRotation + Utils.RandomRangeFloat(-20 / 180f * Mathf.Pi, 20 / 180f * Mathf.Pi),
+ GetAttackLayer()
+ );
+ bullet.PutDown(RoomLayerEnum.YSortLayer);
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs
new file mode 100644
index 0000000..4912170
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs
@@ -0,0 +1,128 @@
+
+using Godot;
+
+[Tool, GlobalClass]
+public partial class Knife : Weapon
+{
+ // private class KnifeAttribute : WeaponAttribute
+ // {
+ // public KnifeAttribute()
+ // {
+ // Icon = ResourcePath.resource_sprite_gun_knife1_png;
+ // WeaponPrefab = ResourcePath.prefab_weapon_Knife_tscn;
+ // //攻速设置
+ // StartFiringSpeed = 180;
+ // FinalFiringSpeed = StartFiringSpeed;
+ // //关闭连发
+ // ContinuousShoot = false;
+ // //设置成松发开火
+ // LooseShoot = true;
+ // //弹药量, 可以理解为耐久度
+ // AmmoCapacity = 180;
+ // MaxAmmoCapacity = AmmoCapacity;
+ // //握把位置
+ // SpritePosition = new Vector2(10, 0);
+ // MaxDistance = MinDistance = 35;
+ // //后坐力改为向前, 模拟手伸长的效果
+ // MaxBacklash = -8;
+ // MinBacklash = -8;
+ // BacklashRegressionSpeed = 24;
+ // UpliftAngle = -95;
+ //
+ // //AiUseAttribute = Clone();
+ // //AiUseAttribute.TriggerInterval = 3f;
+ // }
+ // }
+
+ private Area2D _hitArea;
+ private int _attackIndex = 0;
+
+ public override void OnInit()
+ {
+ base.OnInit();
+
+ _hitArea = GetNode("HitArea");
+ _hitArea.Monitoring = false;
+ _hitArea.Monitorable = false;
+ _hitArea.BodyEntered += OnBodyEntered;
+ //禁用自动播放动画
+ IsAutoPlaySpriteFrames = false;
+ }
+
+ protected override void Process(float delta)
+ {
+ base.Process(delta);
+ if (IsActive)
+ {
+ //让碰撞节点与武器挂载节点位置保持一致, 而不跟着武器走
+ _hitArea.GlobalPosition = Master.MountPoint.GlobalPosition;
+ }
+ }
+
+ protected override void PhysicsProcess(float delta)
+ {
+ base.PhysicsProcess(delta);
+ //过去两个物理帧后就能关闭碰撞了
+ if (++_attackIndex >= 2)
+ {
+ _hitArea.Monitoring = false;
+ }
+ }
+
+ protected override void OnStartCharge()
+ {
+ //开始蓄力时武器角度上抬120度
+ RotationDegrees = -120;
+ }
+
+ protected override void OnFire()
+ {
+ GD.Print("近战武器攻击! 蓄力时长: " + GetTriggerChargeTime() + ", 扳机按下时长: " + GetTriggerDownTime());
+ //更新碰撞层级
+ _hitArea.CollisionMask = GetAttackLayer();
+ //启用碰撞
+ _hitArea.Monitoring = true;
+ _attackIndex = 0;
+
+ if (IsActive) //被使用
+ {
+ //播放挥刀特效
+ SpecialEffectManager.Play(
+ ResourcePath.resource_spriteFrames_KnifeHit1_tres, "default",
+ Master.MountPoint.GlobalPosition, GlobalRotation + Mathf.Pi * 0.5f, new Vector2((int)Master.Face, 1) * AnimatedSprite.Scale,
+ new Vector2(17, 4), 1
+ );
+ }
+
+
+ if (Master == Player.Current)
+ {
+ //创建抖动
+ //GameCamera.Main.ProcessDirectionalShake(Vector2.Right.Rotated(GlobalRotation - Mathf.Pi * 0.5f) * 1.5f);
+ }
+ }
+
+ protected override void OnShoot(float fireRotation)
+ {
+
+ }
+
+ protected override int UseAmmoCount()
+ {
+ //这里要做判断, 如果没有碰到敌人, 则不消耗弹药 (耐久)
+ return 0;
+ }
+
+ private void OnBodyEntered(Node2D body)
+ {
+ GD.Print("碰到物体: " + body.Name);
+ var activityObject = body.AsActivityObject();
+ if (activityObject != null)
+ {
+ if (activityObject is Role role)
+ {
+ role.CallDeferred(nameof(Role.Hurt), 10, (role.GetCenterPosition() - GlobalPosition).Angle());
+ }
+ }
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/item/bullet/Bullet.cs b/DungeonShooting_Godot/src/game/item/bullet/Bullet.cs
deleted file mode 100644
index bb20329..0000000
--- a/DungeonShooting_Godot/src/game/item/bullet/Bullet.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using Godot;
-
-///
-/// 子弹类
-///
-[Tool, GlobalClass]
-public partial class Bullet : ActivityObject
-{
- ///
- /// 碰撞区域
- ///
- [Export, ExportFillNode]
- public Area2D CollisionArea { get; set; }
-
- ///
- /// 发射该子弹的武器
- ///
- public Weapon Weapon { get; private set; }
-
- // 最大飞行距离
- private float MaxDistance;
-
- // 子弹飞行速度
- private float FlySpeed;
-
- //当前子弹已经飞行的距离
- private float CurrFlyDistance = 0;
-
- public void Init(Weapon weapon, float speed, float maxDistance, Vector2 position, float rotation, uint targetLayer)
- {
- Weapon = weapon;
- CollisionArea.CollisionMask = targetLayer;
- CollisionArea.AreaEntered += OnArea2dEntered;
-
- //只有玩家使用该武器才能获得正常速度的子弹
- if (weapon.Master is Player)
- {
- FlySpeed = speed;
- }
- else
- {
- FlySpeed = speed * weapon.Attribute.AiBulletSpeedScale;
- }
- MaxDistance = maxDistance;
- Position = position;
- Rotation = rotation;
- ShadowOffset = new Vector2(0, 5);
-
- BasisVelocity = new Vector2(FlySpeed, 0).Rotated(Rotation);
- }
-
- protected override void PhysicsProcessOver(float delta)
- {
- //移动
- var lastSlideCollision = GetLastSlideCollision();
- //撞到墙
- if (lastSlideCollision != null)
- {
- //创建粒子特效
- var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_BulletSmoke_tscn);
- var smoke = packedScene.Instantiate();
- smoke.GlobalPosition = lastSlideCollision.GetPosition();
- smoke.GlobalRotation = lastSlideCollision.GetNormal().Angle();
- smoke.AddToActivityRoot(RoomLayerEnum.YSortLayer);
-
- Destroy();
- return;
- }
- //距离太大, 自动销毁
- CurrFlyDistance += FlySpeed * delta;
- if (CurrFlyDistance >= MaxDistance)
- {
- var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_BulletDisappear_tscn);
- var node = packedScene.Instantiate();
- node.GlobalPosition = GlobalPosition;
- node.AddToActivityRoot(RoomLayerEnum.YSortLayer);
-
- Destroy();
- }
- }
-
- private void OnArea2dEntered(Area2D other)
- {
- var role = other.AsActivityObject();
- if (role != null)
- {
- var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_BulletDisappear_tscn);
- var node = packedScene.Instantiate();
- node.GlobalPosition = GlobalPosition;
- node.AddToActivityRoot(RoomLayerEnum.YSortLayer);
-
- role.CallDeferred(nameof(Role.Hurt), 4, Rotation);
- Destroy();
- }
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/bullet/BulletAttribute.cs b/DungeonShooting_Godot/src/game/item/bullet/BulletAttribute.cs
deleted file mode 100644
index aa30af1..0000000
--- a/DungeonShooting_Godot/src/game/item/bullet/BulletAttribute.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-
-public class BulletAttribute
-{
-
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/package/Holster.cs b/DungeonShooting_Godot/src/game/item/package/Holster.cs
deleted file mode 100644
index a487f64..0000000
--- a/DungeonShooting_Godot/src/game/item/package/Holster.cs
+++ /dev/null
@@ -1,369 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Godot;
-
-///
-/// 角色身上的武器袋, 存储角色携带的武器
-///
-public class Holster
-{
- ///
- /// 归属者
- ///
- public Role Master { get; }
-
- ///
- /// 当前使用的武器对象
- ///
- public Weapon ActiveWeapon { get; private set; }
-
- ///
- /// 当前使用的武器的索引
- ///
- public int ActiveIndex { get; private set; } = 0;
-
- ///
- /// 武器袋容量
- ///
- public int Capacity { get; private set; } = 0;
-
- ///
- /// 武器插槽
- ///
- public Weapon[] Weapons { get; private set; }
-
- public Holster(Role master)
- {
- Master = master;
- //默认容量4
- SetCapacity(4);
- }
-
- ///
- /// 修改武器袋容量
- ///
- public void SetCapacity(int capacity)
- {
- if (capacity < 0)
- {
- capacity = 0;
- }
-
- if (capacity == Capacity)
- {
- return;
- }
-
- if (Weapons == null)
- {
- Weapons = new Weapon[capacity];
- }
- else if (Weapons.Length > capacity) //删减格子
- {
- var newArray = new Weapon[capacity];
- for (var i = 0; i < Weapons.Length; i++)
- {
- if (i < capacity)
- {
- newArray[i] = Weapons[i];
- }
- else
- {
- Master.ThrowWeapon(i);
- }
- }
-
- Weapons = newArray;
- }
- else //添加格子
- {
- var newArray = new Weapon[capacity];
- for (var i = 0; i < Weapons.Length; i++)
- {
- newArray[i] = Weapons[i];
- }
- Weapons = newArray;
- }
- Capacity = capacity;
-
- }
-
- ///
- /// 返回当前武器袋是否是空的
- ///
- public bool IsEmpty()
- {
- for (var i = 0; i < Weapons.Length; i++)
- {
- if (Weapons[i] != null)
- {
- return false;
- }
- }
-
- return true;
- }
-
- ///
- /// 返回当前武器袋是否还有空位
- ///
- public bool HasVacancy()
- {
- for (var i = 0; i < Weapons.Length; i++)
- {
- if (Weapons[i] == null)
- {
- return true;
- }
- }
-
- return false;
- }
-
- ///
- /// 根据索引获取武器
- ///
- public Weapon GetWeapon(int index)
- {
- if (index < 0 || index >= Weapons.Length)
- {
- return null;
- }
- return Weapons[index];
- }
-
- ///
- /// 根据武器id查找武器袋中该武器所在的位置, 如果没有, 则返回 -1
- ///
- /// 武器id
- public int FindWeapon(string id)
- {
- for (var i = 0; i < Weapons.Length; i++)
- {
- var item = Weapons[i];
- if (item != null && item.ItemId == id)
- {
- return i;
- }
- }
- return -1;
- }
-
- ///
- /// 通过回调函数查询武器在武器袋中的位置, 如果没有, 则返回 -1
- ///
- public int FindWeapon(Func handler)
- {
- for (var i = 0; i < Weapons.Length; i++)
- {
- var item = Weapons[i];
- if (item != null && handler(item, i))
- {
- return i;
- }
- }
- return -1;
- }
-
- ///
- /// 遍历所有武器
- ///
- public void ForEach(Action handler)
- {
- for (var i = 0; i < Weapons.Length; i++)
- {
- var item = Weapons[i];
- if (item != null)
- {
- handler(item, i);
- }
- }
- }
-
- ///
- /// 从武器袋中移除所有武器, 并返回
- ///
- public Weapon[] GetAndClearWeapon()
- {
- var weapons = new List();
- for (var i = 0; i < Weapons.Length; i++)
- {
- var weapon = Weapons[i];
- if (weapon != null)
- {
- weapon.GetParent().RemoveChild(weapon);
- weapon.RemoveAt();
- weapons.Add(weapon);
- Weapons[i] = null;
- }
- }
-
- return weapons.ToArray();
- }
-
- ///
- /// 返回是否能放入武器
- ///
- /// 武器对象
- public bool CanPickupWeapon(Weapon weapon)
- {
- for (var i = 0; i < Weapons.Length; i++)
- {
- var item = Weapons[i];
- if (item == null)
- {
- return true;
- }
- }
- return false;
- }
-
- ///
- /// 拾起武器, 存入武器袋中, 返回存放在武器袋的位置, 如果容不下这把武器, 则会返回 -1
- ///
- /// 武器对象
- /// 是否立即切换到该武器, 默认 true
- public int PickupWeapon(Weapon weapon, bool exchange = true)
- {
- //已经被拾起了
- if (weapon.Master != null)
- {
- return -1;
- }
- for (var i = 0; i < Weapons.Length; i++)
- {
- var item = Weapons[i];
- if (item == null)
- {
- weapon.Pickup();
- Weapons[i] = weapon;
- weapon.PickUpWeapon(Master);
- if (exchange)
- {
- ExchangeByIndex(i);
- }
-
- return i;
- }
- }
- return -1;
- }
-
- ///
- /// 移除指定位置的武器, 并返回这个武器对象, 如果移除正在使用的这把武器, 则会自动切换到上一把武器
- ///
- /// 所在武器袋的位置索引
- public Weapon RemoveWeapon(int index)
- {
- if (index < 0 || index >= Weapons.Length)
- {
- return null;
- }
- var weapon = Weapons[index];
- if (weapon == null)
- {
- return null;
- }
- weapon.GetParent().RemoveChild(weapon);
- Weapons[index] = null;
-
- //如果是当前手持的武器, 就需要调用切换武器操作
- if (index == ActiveIndex)
- {
- //没有其他武器了
- if (ExchangePrev() == index)
- {
- ActiveIndex = 0;
- ActiveWeapon = null;
- }
- }
- weapon.RemoveAt();
- return weapon;
- }
-
- ///
- /// 切换到上一个武器
- ///
- public int ExchangePrev()
- {
- var index = ActiveIndex - 1;
- do
- {
- if (index < 0)
- {
- index = Weapons.Length - 1;
- }
- if (ExchangeByIndex(index))
- {
- return index;
- }
- } while (index-- != ActiveIndex);
- return -1;
- }
-
- ///
- /// 切换到下一个武器,
- ///
- public int ExchangeNext()
- {
- var index = ActiveIndex + 1;
- do
- {
- if (index >= Weapons.Length)
- {
- index = 0;
- }
- if (ExchangeByIndex(index))
- {
- return index;
- }
- }
- while (index++ != ActiveIndex);
- return -1;
- }
-
- ///
- /// 切换到指定索引的武器
- ///
- public bool ExchangeByIndex(int index)
- {
- if (index == ActiveIndex && ActiveWeapon != null) return true;
- if (index < 0 || index > Weapons.Length) return false;
- var weapon = Weapons[index];
- if (weapon == null) return false;
-
- //将上一把武器放到背后
- if (ActiveWeapon != null)
- {
- var tempParent = ActiveWeapon.GetParentOrNull();
- if (tempParent != null)
- {
- tempParent.RemoveChild(ActiveWeapon);
- Master.BackMountPoint.AddChild(ActiveWeapon);
- Master.OnPutBackMount(ActiveWeapon, ActiveIndex);
- ActiveWeapon.Conceal();
- }
- }
-
- //更改父节点
- var parent = weapon.GetParentOrNull();
- if (parent == null)
- {
- Master.MountPoint.AddChild(weapon);
- }
- else if (parent != Master.MountPoint)
- {
- parent.RemoveChild(weapon);
- Master.MountPoint.AddChild(weapon);
- }
-
- weapon.Position = Vector2.Zero;
- weapon.Scale = Vector2.One;
- weapon.RotationDegrees = 0;
- weapon.Visible = true;
- ActiveWeapon = weapon;
- ActiveIndex = index;
- ActiveWeapon.Active();
- return true;
- }
-}
diff --git a/DungeonShooting_Godot/src/game/item/shell/Shell.cs b/DungeonShooting_Godot/src/game/item/shell/Shell.cs
deleted file mode 100644
index 310ee81..0000000
--- a/DungeonShooting_Godot/src/game/item/shell/Shell.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-
-using Godot;
-
-///
-/// 弹壳类
-///
-[Tool, GlobalClass]
-public partial class Shell : ActivityObject
-{
- public override void OnInit()
- {
- base.OnInit();
- ShadowOffset = new Vector2(0, 1);
- ThrowCollisionSize = new Vector2(5, 5);
- }
-
- protected override void OnThrowOver()
- {
- EnableBehavior = false;
- Collision.QueueFree();
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
deleted file mode 100644
index 959487f..0000000
--- a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
+++ /dev/null
@@ -1,1140 +0,0 @@
-using Godot;
-using System;
-using Config;
-
-///
-/// 武器的基类
-///
-public abstract partial class Weapon : ActivityObject
-{
- ///
- /// 开火回调事件
- ///
- public event Action FireEvent;
-
- ///
- /// 武器属性数据
- ///
- public ExcelConfig.Weapon Attribute => _weaponAttribute;
- private ExcelConfig.Weapon _weaponAttribute;
- private ExcelConfig.Weapon _playerWeaponAttribute;
- private ExcelConfig.Weapon _aiWeaponAttribute;
-
- ///
- /// 武器攻击的目标阵营
- ///
- public CampEnum TargetCamp { get; set; }
-
- ///
- /// 该武器的拥有者
- ///
- public Role Master { get; private set; }
-
- ///
- /// 当前弹夹弹药剩余量
- ///
- public int CurrAmmo { get; private set; }
-
- ///
- /// 剩余弹药量(备用弹药)
- ///
- public int ResidueAmmo { get; private set; }
-
- ///
- /// 武器管的开火点
- ///
- [Export, ExportFillNode]
- public Marker2D FirePoint { get; set; }
-
- ///
- /// 弹壳抛出的点
- ///
- [Export, ExportFillNode]
- public Marker2D ShellPoint { get; set; }
-
- ///
- /// 武器的当前散射半径
- ///
- public float CurrScatteringRange { get; private set; } = 0;
-
- ///
- /// 是否在换弹中
- ///
- ///
- public bool Reloading { get; private set; } = false;
-
- ///
- /// 换弹计时器
- ///
- public float ReloadTimer { get; private set; } = 0;
-
- ///
- /// 换弹进度 (0 - 1)
- ///
- public float ReloadProgress
- {
- get
- {
- if (Attribute.AloneReload)
- {
- var num = 1f / Attribute.AmmoCapacity;
- return num * (Attribute.AmmoCapacity - CurrAmmo - 1) + num * (ReloadTimer / Attribute.ReloadTime);
- }
-
- return ReloadTimer / Attribute.ReloadTime;
- }
- }
-
- ///
- /// 返回是否在蓄力中,
- /// 注意, 属性仅在 Attribute.LooseShoot == false 时有正确的返回值, 否则返回 false
- ///
- public bool IsCharging => _looseShootFlag;
-
- ///
- /// 返回武器是否在武器袋中
- ///
- public bool IsInHolster => Master != null;
-
- ///
- /// 返回是否真正使用该武器
- ///
- public bool IsActive => Master != null && Master.Holster.ActiveWeapon == this;
-
- ///
- /// 动画播放器
- ///
- [Export, ExportFillNode]
- public AnimationPlayer AnimationPlayer { get; set; }
-
- ///
- /// 是否自动播放 SpriteFrames 的动画
- ///
- public bool IsAutoPlaySpriteFrames { get; set; } = true;
-
- //--------------------------------------------------------------------------------------------
-
- //是否按下
- private bool _triggerFlag = false;
-
- //扳机计时器
- private float _triggerTimer = 0;
-
- //开火前延时时间
- private float _delayedTime = 0;
-
- //开火间隙时间
- private float _fireInterval = 0;
-
- //开火武器口角度
- private float _fireAngle = 0;
-
- //攻击冷却计时
- private float _attackTimer = 0;
-
- //攻击状态
- private bool _attackFlag = false;
-
- //按下的时间
- private float _downTimer = 0;
-
- //松开的时间
- private float _upTimer = 0;
-
- //连发次数
- private float _continuousCount = 0;
-
- //连发状态记录
- private bool _continuousShootFlag = false;
-
- //松开扳机是否开火
- private bool _looseShootFlag = false;
-
- //蓄力攻击时长
- private float _chargeTime = 0;
-
- //是否需要重置武器数据
- private bool _dirtyFlag = false;
-
- //当前后坐力导致的偏移长度
- private float _currBacklashLength = 0;
-
- // ----------------------------------------------
- private uint _tempLayer;
-
- ///
- /// 初始化武器属性
- ///
- public void InitWeapon(ExcelConfig.Weapon attribute)
- {
- _playerWeaponAttribute = attribute;
- _weaponAttribute = attribute;
- if (!string.IsNullOrEmpty(attribute.AiUseAttributeId))
- {
- _aiWeaponAttribute = ExcelConfig.Weapon_Map[attribute.AiUseAttributeId];
- }
- else
- {
- _aiWeaponAttribute = attribute;
- }
- //未完成
- //AnimatedSprite.Position = Attribute.ThrowSpritePosition;
-
- if (Attribute.AmmoCapacity > Attribute.MaxAmmoCapacity)
- {
- Attribute.AmmoCapacity = Attribute.MaxAmmoCapacity;
- GD.PrintErr("弹夹的容量不能超过弹药上限, 武器id: " + ItemId);
- }
- //弹药量
- CurrAmmo = Attribute.AmmoCapacity;
- //剩余弹药量
- ResidueAmmo = Mathf.Min(Attribute.StandbyAmmoCapacity + CurrAmmo, Attribute.MaxAmmoCapacity) - CurrAmmo;
-
- ThrowCollisionSize = attribute.ThrowCollisionSize.AsVector2();
- }
-
- ///
- /// 单次开火时调用的函数
- ///
- protected abstract void OnFire();
-
- ///
- /// 发射子弹时调用的函数, 每发射一枚子弹调用一次,
- /// 如果做霰弹武器效果, 一次开火发射5枚子弹, 则该函数调用5次
- ///
- /// 开火时枪口旋转角度
- protected abstract void OnShoot(float fireRotation);
-
- ///
- /// 当按下扳机时调用
- ///
- protected virtual void OnDownTrigger()
- {
- }
-
- ///
- /// 当松开扳机时调用
- ///
- protected virtual void OnUpTrigger()
- {
- }
-
- ///
- /// 开始蓄力时调用,
- /// 注意, 该函数仅在 Attribute.LooseShoot == false 时才能被调用
- ///
- protected virtual void OnStartCharge()
- {
- }
-
- ///
- /// 当开始换弹时调用
- ///
- protected virtual void OnReload()
- {
- }
-
- ///
- /// 当换弹完成时调用
- ///
- protected virtual void OnReloadFinish()
- {
- }
-
- ///
- /// 当武器被拾起时调用
- ///
- /// 拾起该武器的角色
- protected virtual void OnPickUp(Role master)
- {
- }
-
- ///
- /// 当武器从武器袋中移除时调用
- ///
- protected virtual void OnRemove()
- {
- }
-
- ///
- /// 当武器被激活时调用, 也就是使用当武器时调用
- ///
- protected virtual void OnActive()
- {
- }
-
- ///
- /// 当武器被收起时调用
- ///
- protected virtual void OnConceal()
- {
- }
-
- ///
- /// 射击时调用, 返回消耗弹药数量, 默认为1, 如果返回为 0, 则不消耗弹药
- ///
- protected virtual int UseAmmoCount()
- {
- return 1;
- }
-
- public override void EnterTree()
- {
- base.EnterTree();
- //收集落在地上的武器
- if (IsInGround())
- {
- World.Weapon_UnclaimedWeapons.Add(this);
- }
- }
-
- public override void ExitTree()
- {
- base.ExitTree();
- World.Weapon_UnclaimedWeapons.Remove(this);
- }
-
- protected override void Process(float delta)
- {
- //GD.Print("AnimatedSprite: " + AnimatedSprite.Position);
- //这把武器被扔在地上, 或者当前武器没有被使用
- if (Master == null || Master.Holster.ActiveWeapon != this)
- {
- //_triggerTimer
- _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0;
- //攻击冷却计时
- _attackTimer = _attackTimer > 0 ? _attackTimer - delta : 0;
- //武器的当前散射半径
- CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta,
- Attribute.StartScatteringRange);
- //松开扳机
- if (_triggerFlag || _downTimer > 0)
- {
- UpTrigger();
- _downTimer = 0;
- }
-
- _triggerFlag = false;
-
- //重置数据
- if (_dirtyFlag)
- {
- _dirtyFlag = false;
- Reloading = false;
- ReloadTimer = 0;
- _attackFlag = false;
- _continuousCount = 0;
- _delayedTime = 0;
- _upTimer = 0;
- _looseShootFlag = false;
- _chargeTime = 0;
- }
- }
- else //正在使用中
- {
- _dirtyFlag = true;
- //换弹
- if (Reloading)
- {
- ReloadTimer -= delta;
- if (ReloadTimer <= 0)
- {
- ReloadSuccess();
- }
- }
-
- // 攻击的计时器
- if (_attackTimer > 0)
- {
- _attackTimer -= delta;
- if (_attackTimer < 0)
- {
- _delayedTime += _attackTimer;
- _attackTimer = 0;
- //枪口默认角度
- RotationDegrees = -Attribute.DefaultAngle;
- }
- }
- else if (_delayedTime > 0) //攻击延时
- {
- _delayedTime -= delta;
- if (_attackTimer < 0)
- {
- _delayedTime = 0;
- }
- }
-
- //扳机判定
- if (_triggerFlag)
- {
- if (_looseShootFlag) //蓄力时长
- {
- _chargeTime += delta;
- }
-
- _downTimer += delta;
- if (_upTimer > 0) //第一帧按下扳机
- {
- DownTrigger();
- _upTimer = 0;
- }
- }
- else
- {
- _upTimer += delta;
- if (_downTimer > 0) //第一帧松开扳机
- {
- UpTrigger();
- _downTimer = 0;
- }
- }
-
- //连发判断
- if (!_looseShootFlag && _continuousCount > 0 && _delayedTime <= 0 && _attackTimer <= 0)
- {
- //连发开火
- TriggerFire();
- }
-
- if (!_attackFlag && _attackTimer <= 0)
- {
- if (_upTimer >= Attribute.ScatteringRangeBackTime)
- {
- CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta,
- Attribute.StartScatteringRange);
- }
- }
-
- _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0;
- _triggerFlag = false;
- _attackFlag = false;
-
- //武器身回归
- //Position = Position.MoveToward(Vector2.Zero, Attribute.BacklashRegressionSpeed * delta).Rotated(Rotation);
- _currBacklashLength = Mathf.MoveToward(_currBacklashLength, 0, Attribute.BacklashRegressionSpeed * delta);
- Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
- if (_attackTimer > 0)
- {
- RotationDegrees = Mathf.Lerp(
- _fireAngle, -Attribute.DefaultAngle,
- Mathf.Clamp((_fireInterval - _attackTimer) * Attribute.UpliftAngleRestore / _fireInterval, 0, 1)
- );
- }
- }
- }
-
- ///
- /// 返回武器是否在地上
- ///
- ///
- public bool IsInGround()
- {
- return Master == null && GetParent() == GameApplication.Instance.World.NormalLayer;
- }
-
- ///
- /// 扳机函数, 调用即视为按下扳机
- ///
- public void Trigger()
- {
- //这一帧已经按过了, 不需要再按下
- if (_triggerFlag) return;
-
- //是否第一帧按下
- var justDown = _downTimer == 0;
- //是否能发射
- var flag = false;
- if (_continuousCount <= 0) //不能处于连发状态下
- {
- if (Attribute.ContinuousShoot) //自动射击
- {
- if (_triggerTimer > 0)
- {
- if (_continuousShootFlag)
- {
- flag = true;
- }
- }
- else
- {
- flag = true;
- if (_delayedTime <= 0 && _attackTimer <= 0)
- {
- _continuousShootFlag = true;
- }
- }
- }
- else //半自动
- {
- if (justDown && _triggerTimer <= 0 && _attackTimer <= 0)
- {
- flag = true;
- }
- }
- }
-
- if (flag)
- {
- var fireFlag = true; //是否能开火
- if (Reloading) //换弹中
- {
- if (CurrAmmo > 0 && Attribute.AloneReload && Attribute.AloneReloadCanShoot) //立即停止换弹
- {
- Reloading = false;
- ReloadTimer = 0;
- }
- else
- {
- fireFlag = false;
- }
- }
- else if (CurrAmmo <= 0) //子弹不够
- {
- fireFlag = false;
- if (justDown)
- {
- //第一帧按下, 触发换弹
- Reload();
- }
- }
-
- if (fireFlag)
- {
- if (justDown)
- {
- //开火前延时
- _delayedTime = Attribute.DelayedTime;
- //扳机按下间隔
- _triggerTimer = Attribute.TriggerInterval;
- //连发数量
- if (!Attribute.ContinuousShoot)
- {
- _continuousCount =
- Utils.RandomRangeInt(Attribute.MinContinuousCount, Attribute.MaxContinuousCount);
- }
- }
-
- if (_delayedTime <= 0 && _attackTimer <= 0)
- {
- if (Attribute.LooseShoot) //松发开火
- {
- _looseShootFlag = true;
- OnStartCharge();
- }
- else
- {
- //开火
- TriggerFire();
- }
- }
-
- _attackFlag = true;
- }
-
- }
-
- _triggerFlag = true;
- }
-
- ///
- /// 返回是否按下扳机
- ///
- public bool IsPressTrigger()
- {
- return _triggerFlag;
- }
-
- ///
- /// 获取本次扳机按下的时长, 单位: 秒
- ///
- public float GetTriggerDownTime()
- {
- return _downTimer;
- }
-
- ///
- /// 获取扳机蓄力时长, 计算按下扳机后从可以开火到当前一共经过了多长时间, 可用于计算蓄力攻击
- /// 注意, 该函数仅在 Attribute.LooseShoot == false 时有正确的返回值, 否则返回 0
- ///
- public float GetTriggerChargeTime()
- {
- return _chargeTime;
- }
-
- ///
- /// 获取延时射击倒计时, 单位: 秒
- ///
- public float GetDelayedAttackTime()
- {
- return _delayedTime;
- }
-
- ///
- /// 刚按下扳机
- ///
- private void DownTrigger()
- {
- OnDownTrigger();
- }
-
- ///
- /// 刚松开扳机
- ///
- private void UpTrigger()
- {
- _continuousShootFlag = false;
- if (_delayedTime > 0)
- {
- _continuousCount = 0;
- }
-
- //松发开火执行
- if (_looseShootFlag)
- {
- _looseShootFlag = false;
- if (_chargeTime >= Attribute.MinChargeTime) //判断蓄力是否够了
- {
- TriggerFire();
- }
- else //不能攻击
- {
- _continuousCount = 0;
- }
- _chargeTime = 0;
- }
-
- OnUpTrigger();
- }
-
- ///
- /// 触发开火
- ///
- private void TriggerFire()
- {
- _continuousCount = _continuousCount > 0 ? _continuousCount - 1 : 0;
-
- //减子弹数量
- if (_playerWeaponAttribute != _weaponAttribute) //Ai使用该武器, 有一定概率不消耗弹药
- {
- if (Utils.RandomRangeFloat(0, 1) < _weaponAttribute.AiAmmoConsumptionProbability) //触发消耗弹药
- {
- CurrAmmo -= UseAmmoCount();
- }
- }
- else
- {
- CurrAmmo -= UseAmmoCount();
- }
-
- //开火间隙
- _fireInterval = 60 / Attribute.StartFiringSpeed;
- //攻击冷却
- _attackTimer += _fireInterval;
-
- //播放开火动画
- if (IsAutoPlaySpriteFrames)
- {
- PlaySpriteAnimation(AnimatorNames.Fire);
- }
- //触发开火函数
- OnFire();
-
- //开火发射的子弹数量
- var bulletCount = Utils.RandomRangeInt(Attribute.MaxFireBulletCount, Attribute.MinFireBulletCount);
- //武器口角度
- var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();
-
- //先算武器口方向
- var tempRotation = Utils.RandomRangeFloat(-angle, angle);
- var tempAngle = Mathf.RadToDeg(tempRotation);
-
- //开火时枪口角度
- var fireRotation = Mathf.DegToRad(Master.MountPoint.RealRotationDegrees) + tempRotation;
- //创建子弹
- for (int i = 0; i < bulletCount; i++)
- {
- //发射子弹
- OnShoot(fireRotation);
- }
-
- //当前的散射半径
- CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue,
- Attribute.FinalScatteringRange);
- //武器的旋转角度
- tempAngle -= Attribute.UpliftAngle;
- RotationDegrees = tempAngle;
- _fireAngle = tempAngle;
-
- //武器身位置
- var max = Mathf.Abs(Mathf.Max(Attribute.MaxBacklash, Attribute.MinBacklash));
- _currBacklashLength = Mathf.Clamp(
- _currBacklashLength - Utils.RandomRangeFloat(Attribute.MinBacklash, Attribute.MaxBacklash),
- -max, max
- );
- Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
-
- if (FireEvent != null)
- {
- FireEvent(this);
- }
- }
-
- ///
- /// 获取武器攻击的目标层级
- ///
- ///
- public uint GetAttackLayer()
- {
- return Master != null ? Master.AttackLayer : Role.DefaultAttackLayer;
- }
-
- ///
- /// 返回弹药是否到达上限
- ///
- public bool IsAmmoFull()
- {
- return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
- }
-
- ///
- /// 返回弹夹是否打空
- ///
- public bool IsAmmoEmpty()
- {
- return CurrAmmo == 0;
- }
-
- ///
- /// 返回是否弹药耗尽
- ///
- public bool IsTotalAmmoEmpty()
- {
- return CurrAmmo + ResidueAmmo == 0;
- }
-
- ///
- /// 强制修改当前弹夹弹药量
- ///
- public void SetCurrAmmo(int count)
- {
- CurrAmmo = Mathf.Clamp(count, 0, Attribute.AmmoCapacity);
- }
-
- ///
- /// 强制修改备用弹药量
- ///
- public void SetResidueAmmo(int count)
- {
- ResidueAmmo = Mathf.Clamp(count, 0, Attribute.MaxAmmoCapacity - CurrAmmo);
- }
-
- ///
- /// 强制修改弹药量, 优先改动备用弹药
- ///
- public void SetTotalAmmo(int total)
- {
- if (total < 0)
- {
- return;
- }
- var totalAmmo = CurrAmmo + ResidueAmmo;
- if (totalAmmo == total)
- {
- return;
- }
-
- if (total > totalAmmo) //弹药增加
- {
- ResidueAmmo = Mathf.Min(total - CurrAmmo, Attribute.MaxAmmoCapacity - CurrAmmo);
- }
- else //弹药减少
- {
- if (CurrAmmo < total)
- {
- ResidueAmmo = total - CurrAmmo;
- }
- else
- {
- CurrAmmo = total;
- ResidueAmmo = 0;
- }
- }
- }
-
- ///
- /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
- ///
- /// 弹药数量
- private int PickUpAmmo(int count)
- {
- var num = ResidueAmmo;
- ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
- return count - ResidueAmmo + num;
- }
-
- ///
- /// 触发换弹
- ///
- public void Reload()
- {
- if (CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && !Reloading)
- {
- Reloading = true;
- ReloadTimer = Attribute.ReloadTime;
- //播放换弹动画
- if (IsAutoPlaySpriteFrames)
- {
- PlaySpriteAnimation(AnimatorNames.Reload);
- }
- OnReload();
- }
- }
-
- ///
- /// 换弹计时器时间到, 执行换弹操作
- ///
- private void ReloadSuccess()
- {
- if (Attribute.AloneReload) //单独装填
- {
- if (ResidueAmmo >= Attribute.AloneReloadCount) //剩余子弹充足
- {
- if (CurrAmmo + Attribute.AloneReloadCount <= Attribute.AmmoCapacity)
- {
- ResidueAmmo -= Attribute.AloneReloadCount;
- CurrAmmo += Attribute.AloneReloadCount;
- }
- else //子弹满了
- {
- var num = Attribute.AmmoCapacity - CurrAmmo;
- CurrAmmo = Attribute.AmmoCapacity;
- ResidueAmmo -= num;
- }
- }
- else if (ResidueAmmo != 0) //剩余子弹不足
- {
- if (ResidueAmmo + CurrAmmo <= Attribute.AmmoCapacity)
- {
- CurrAmmo += ResidueAmmo;
- ResidueAmmo = 0;
- }
- else //子弹满了
- {
- var num = Attribute.AmmoCapacity - CurrAmmo;
- CurrAmmo = Attribute.AmmoCapacity;
- ResidueAmmo -= num;
- }
- }
-
- if (ResidueAmmo == 0 || CurrAmmo == Attribute.AmmoCapacity) //换弹结束
- {
- Reloading = false;
- ReloadTimer = 0;
- OnReloadFinish();
- }
- else
- {
- ReloadTimer = Attribute.ReloadTime;
- //播放换弹动画
- if (IsAutoPlaySpriteFrames)
- {
- PlaySpriteAnimation(AnimatorNames.Reload);
- }
- OnReload();
- }
- }
- else //换弹结束
- {
- if (CurrAmmo + ResidueAmmo >= Attribute.AmmoCapacity)
- {
- ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
- CurrAmmo = Attribute.AmmoCapacity;
- }
- else
- {
- CurrAmmo += ResidueAmmo;
- ResidueAmmo = 0;
- }
-
- Reloading = false;
- ReloadTimer = 0;
- OnReloadFinish();
- }
- }
-
- //播放动画
- private void PlaySpriteAnimation(string name)
- {
- var spriteFrames = AnimatedSprite.SpriteFrames;
- if (spriteFrames != null && spriteFrames.HasAnimation(name))
- {
- AnimatedSprite.Play(name);
- }
- }
-
- public override CheckInteractiveResult CheckInteractive(ActivityObject master)
- {
- var result = new CheckInteractiveResult(this);
-
- if (master is Role roleMaster) //碰到角色
- {
- if (Master == null)
- {
- var masterWeapon = roleMaster.Holster.ActiveWeapon;
- //查找是否有同类型武器
- var index = roleMaster.Holster.FindWeapon(ItemId);
- if (index != -1) //如果有这个武器
- {
- if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
- {
- var targetWeapon = roleMaster.Holster.GetWeapon(index);
- if (!targetWeapon.IsAmmoFull()) //背包里面的武器子弹未满
- {
- //可以互动拾起弹药
- result.CanInteractive = true;
- result.Message = Attribute.Name;
- result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_bullet_png;
- return result;
- }
- }
- }
- else //没有武器
- {
- if (roleMaster.Holster.CanPickupWeapon(this)) //能拾起武器
- {
- //可以互动, 拾起武器
- result.CanInteractive = true;
- result.Message = Attribute.Name;
- result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_pickup_png;
- return result;
- }
- else if (masterWeapon != null && masterWeapon.Attribute.WeightType == Attribute.WeightType) //替换武器
- {
- //可以互动, 切换武器
- result.CanInteractive = true;
- result.Message = Attribute.Name;
- result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_replace_png;
- return result;
- }
- }
- }
- }
-
- return result;
- }
-
- public override void Interactive(ActivityObject master)
- {
- if (master is Role roleMaster) //与role互动
- {
- var holster = roleMaster.Holster;
- //查找是否有同类型武器
- var index = holster.FindWeapon(ItemId);
- if (index != -1) //如果有这个武器
- {
- if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
- {
- return;
- }
-
- var weapon = holster.GetWeapon(index);
- //子弹上限
- var maxCount = Attribute.MaxAmmoCapacity;
- //是否捡到子弹
- var flag = false;
- if (ResidueAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
- {
- var count = weapon.PickUpAmmo(ResidueAmmo);
- if (count != ResidueAmmo)
- {
- ResidueAmmo = count;
- flag = true;
- }
- }
-
- if (CurrAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
- {
- var count = weapon.PickUpAmmo(CurrAmmo);
- if (count != CurrAmmo)
- {
- CurrAmmo = count;
- flag = true;
- }
- }
-
- //播放互动效果
- if (flag)
- {
- Throw(GlobalPosition, 0, Utils.RandomRangeInt(20, 50), Vector2.Zero, Utils.RandomRangeInt(-180, 180));
- }
- }
- else //没有武器
- {
- if (holster.PickupWeapon(this) == -1)
- {
- //替换武器
- roleMaster.ThrowWeapon();
- roleMaster.PickUpWeapon(this);
- }
- }
- }
- }
-
- ///
- /// 获取当前武器真实的旋转角度(弧度制), 由于武器旋转时加入了旋转吸附, 所以需要通过该函数来来知道当前武器的真实旋转角度
- ///
- public float GetRealGlobalRotation()
- {
- return Mathf.DegToRad(Master.MountPoint.RealRotationDegrees) + Rotation;
- }
-
- ///
- /// 触发扔掉武器抛出的效果, 并不会管武器是否在武器袋中
- ///
- /// 触发扔掉该武器的的角色
- public void ThrowWeapon(Role master)
- {
- ThrowWeapon(master, master.GlobalPosition);
- }
-
- ///
- /// 触发扔掉武器抛出的效果, 并不会管武器是否在武器袋中
- ///
- /// 触发扔掉该武器的的角色
- /// 投抛起始位置
- public void ThrowWeapon(Role master, Vector2 startPosition)
- {
- //阴影偏移
- ShadowOffset = new Vector2(0, 2);
-
- if (master.Face == FaceDirection.Left)
- {
- Scale *= new Vector2(1, -1);
- }
-
- var angle = master.MountPoint.GlobalRotationDegrees;
- GlobalRotationDegrees = angle;
-
- //继承role的移动速度
- InheritVelocity(master);
-
- var startHeight = -master.MountPoint.Position.Y;
- var direction = angle + Utils.RandomRangeInt(-20, 20);
- var velocity = new Vector2(20, 0).Rotated(direction * Mathf.Pi / 180);
- var yf = Utils.RandomRangeInt(50, 70);
- var rotate = Utils.RandomRangeInt(-60, 60);
- Throw(startPosition, startHeight, yf, velocity, rotate);
- }
-
- protected override void OnThrowStart()
- {
- //禁用碰撞
- //Collision.Disabled = true;
- AnimationPlayer.Play(AnimatorNames.Floodlight);
- }
-
- protected override void OnThrowOver()
- {
- //启用碰撞
- //Collision.Disabled = false;
- AnimationPlayer.Play(AnimatorNames.Floodlight);
- }
-
- ///
- /// 触发拾起到 Holster, 这个函数由 Holster 对象调用
- ///
- public void PickUpWeapon(Role master)
- {
- Master = master;
- if (master.IsAi)
- {
- _weaponAttribute = _aiWeaponAttribute;
- }
- else
- {
- _weaponAttribute = _playerWeaponAttribute;
- }
- //停止动画
- AnimationPlayer.Stop();
- //清除泛白效果
- SetBlendSchedule(0);
- ZIndex = 0;
- //禁用碰撞
- //Collision.Disabled = true;
- //修改层级
- _tempLayer = CollisionLayer;
- CollisionLayer = PhysicsLayer.InHand;
- //清除 Ai 拾起标记
- RemoveSign(SignNames.AiFindWeaponSign);
- OnPickUp(master);
- }
-
- ///
- /// 触发从 Holster 中移除, 这个函数由 Holster 对象调用
- ///
- public void RemoveAt()
- {
- Master = null;
- CollisionLayer = _tempLayer;
- _weaponAttribute = _playerWeaponAttribute;
- //未完成
- //AnimatedSprite.Position = Attribute.ThrowSpritePosition;
- //清除 Ai 拾起标记
- RemoveSign(SignNames.AiFindWeaponSign);
- OnRemove();
- }
-
- ///
- /// 触发启用武器
- ///
- public void Active()
- {
- //调整阴影
- ShadowOffset = new Vector2(0, Master.GlobalPosition.Y - GlobalPosition.Y);
- //枪口默认抬起角度
- RotationDegrees = -Attribute.DefaultAngle;
- ShowShadowSprite();
- OnActive();
- }
-
- ///
- /// 触发收起武器
- ///
- public void Conceal()
- {
- HideShadowSprite();
- OnConceal();
- }
-
- //-------------------------- ----- 子弹相关 -----------------------------
-
- ///
- /// 投抛弹壳的默认实现方式, shellId为弹壳id, 不需要前缀
- ///
- protected ActivityObject ThrowShell(string shellId)
- {
- var shellPosition = Master.MountPoint.Position + ShellPoint.Position;
- var startPos = ShellPoint.GlobalPosition;
- var startHeight = -shellPosition.Y;
- startPos.Y += startHeight;
- var direction = GlobalRotationDegrees + Utils.RandomRangeInt(-30, 30) + 180;
- var verticalSpeed = Utils.RandomRangeInt(60, 120);
- var velocity = new Vector2(Utils.RandomRangeInt(20, 60), 0).Rotated(direction * Mathf.Pi / 180);
- var rotate = Utils.RandomRangeInt(-720, 720);
- var shell = Create(ActivityIdPrefix.Shell + shellId);
- shell.Rotation = Master.MountPoint.RealRotation;
- shell.InheritVelocity(Master);
- shell.Throw(startPos, startHeight, verticalSpeed, velocity, rotate);
- return shell;
- }
-
- //-------------------------------- Ai相关 -----------------------------
-
- ///
- /// 获取 Ai 对于该武器的评分, 评分越高, 代表 Ai 会越优先选择该武器, 如果为 -1, 则表示 Ai 不会使用该武器
- ///
- public float GetAiScore()
- {
- return 1;
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/WeaponAttribute.cs b/DungeonShooting_Godot/src/game/item/weapon/WeaponAttribute.cs
deleted file mode 100644
index 5ac32ed..0000000
--- a/DungeonShooting_Godot/src/game/item/weapon/WeaponAttribute.cs
+++ /dev/null
@@ -1,277 +0,0 @@
-// using System;
-// using Godot;
-//
-// ///
-// /// 武器上的属性
-// ///
-// public class WeaponAttribute
-// {
-// ///
-// /// 武器显示的名称
-// ///
-// public string Name = "Gun1";
-// ///
-// /// 武器 Prefab, 必须继承场景 "res://prefab/weapon/Weapon.tscn"
-// ///
-// public string WeaponPrefab = ResourcePath.prefab_weapon_Weapon_tscn;
-// ///
-// /// 武器类型
-// ///
-// public WeaponWeightType WeightType = WeaponWeightType.MainWeapon;
-// ///
-// /// 武器的图标
-// ///
-// public string Icon = ResourcePath.resource_sprite_gun_gun1_png;
-// ///
-// /// 动画序列帧资源名称
-// ///
-// public string SpriteFrames;
-// ///
-// /// 是否连续发射, 如果为false, 则每次发射都需要扣动扳机
-// ///
-// public bool ContinuousShoot = true;
-// ///
-// /// 弹夹容量
-// ///
-// public int AmmoCapacity = 30;
-// ///
-// /// 弹药容量上限
-// ///
-// public int MaxAmmoCapacity = 120;
-// ///
-// /// 起始备用弹药数量
-// ///
-// public int StandbyAmmoCapacity = 90;
-// ///
-// /// 装弹时间, 单位: 秒
-// ///
-// public float ReloadTime = 1.5f;
-// ///
-// /// 每粒子弹是否是单独装填, 如果是, 那么每上一发子弹的时间就是 ReloadTime, 可以做霰弹武器装填效果
-// ///
-// public bool AloneReload = false;
-// ///
-// /// 单独装填时每次装填子弹数量, 必须要将 'AloneReload' 属性设置为 true
-// ///
-// public int AloneReloadCount = 1;
-// ///
-// /// 单独装填的子弹时可以立即射击, 必须要将 'AloneReload' 属性设置为 true
-// ///
-// public bool AloneReloadCanShoot = false;
-// ///
-// /// 是否为松发开火, 也就是松开扳机才开火, 若要启用该属性, 必须将 'ContinuousShoot' 设置为 false
-// ///
-// public bool LooseShoot = false;
-// ///
-// /// 最少需要蓄力多久才能开火, 必须将 'LooseShoot' 设置为 true
-// ///
-// public float MinChargeTime = 0f;
-// ///
-// /// 连续发射最小次数, 仅当 ContinuousShoot 为 false 时生效
-// ///
-// public int MinContinuousCount = 1;
-// ///
-// /// 连续发射最大次数, 仅当 ContinuousShoot 为 false 时生效
-// ///
-// public int MaxContinuousCount = 1;
-// ///
-// /// 按下一次扳机后需要多长时间才能再次感应按下
-// ///
-// public float TriggerInterval = 0;
-// ///
-// /// 初始射速, 初始每分钟能开火次数
-// ///
-// public float StartFiringSpeed = 300;
-// ///
-// /// 最终射速, 最终每分钟能开火次数, 仅当 ContinuousShoot 为 true 时生效
-// ///
-// public float FinalFiringSpeed = 300;
-// ///
-// /// 按下扳机并开火后射速增加速率
-// ///
-// public float FiringSpeedAddSpeed = 2;
-// ///
-// /// 松开扳机后射速消散速率
-// ///
-// public float FiringSpeedBackSpeed = 10;
-// ///
-// /// 单次开火发射子弹最小数量
-// ///
-// public int MinFireBulletCount = 1;
-// ///
-// /// 单次开火发射子弹最大数量
-// ///
-// public int MaxFireBulletCount = 1;
-// ///
-// /// 开火前延时
-// ///
-// public float DelayedTime = 0f;
-// ///
-// /// 初始散射半径
-// ///
-// public float StartScatteringRange = 0;
-// ///
-// /// 最终散射半径
-// ///
-// public float FinalScatteringRange = 20;
-// ///
-// /// 每次发射后散射增加值
-// ///
-// public float ScatteringRangeAddValue = 2;
-// ///
-// /// 松开扳机后散射销退速率
-// ///
-// public float ScatteringRangeBackSpeed = 10;
-// ///
-// /// 松开扳机多久后开始销退散射值
-// ///
-// public float ScatteringRangeBackTime = 0f;
-// ///
-// /// 子弹飞行最大距离
-// ///
-// public float MaxDistance = 600;
-// ///
-// /// 子弹飞行最小距离
-// ///
-// public float MinDistance = 800;
-// ///
-// /// 开火位置
-// ///
-// public Vector2 FirePosition = new Vector2(11, 0);
-// ///
-// /// 精灵位置
-// ///
-// public Vector2 SpritePosition = new Vector2(4, -3);
-// ///
-// /// 弹壳投抛起始位置
-// ///
-// public Vector2 ShellPosition = new Vector2(5, -2.5f);
-// ///
-// /// 重量
-// ///
-// public float Weight = 11;
-// ///
-// /// 最大后坐力 (仅用于开火后武器身抖动)
-// ///
-// public float MaxBacklash = 4;
-// ///
-// /// 最小后坐力 (仅用于开火后武器身抖动)
-// ///
-// public float MinBacklash = 2;
-// ///
-// /// 后坐力偏移回归回归速度
-// ///
-// public float BacklashRegressionSpeed = 35f;
-// ///
-// /// 开火后武器口上抬角度
-// ///
-// public float UpliftAngle = 30;
-// ///
-// /// 武器默认上抬角度
-// ///
-// public float DefaultAngle = 0;
-// ///
-// /// 开火后武器口角度恢复速度倍数
-// ///
-// public float UpliftAngleRestore = 1f;
-// ///
-// /// 默认射出的子弹
-// ///
-// public string BulletId = ActivityIdPrefix.Bullet + "0001";
-// ///
-// /// 武器精灵投抛时的旋转中心坐标
-// ///
-// public Vector2 ThrowSpritePosition = new Vector2(0, 0);
-// ///
-// /// 投抛状态下物体碰撞器大小
-// ///
-// public Vector2 ThrowCollisionSize = new Vector2(20, 15);
-//
-// ///
-// /// 克隆一份新的属性配置
-// ///
-// ///
-// public WeaponAttribute Clone()
-// {
-// var attr = _Clone();
-// if (AiUseAttribute != null)
-// {
-// attr.AiUseAttribute = AiUseAttribute._Clone();
-// }
-// return attr;
-// }
-//
-// private WeaponAttribute _Clone()
-// {
-// var attr = new WeaponAttribute();
-// attr.Name = Name;
-// attr.WeaponPrefab = WeaponPrefab;
-// attr.WeightType = WeightType;
-// attr.Icon = Icon;
-// attr.SpriteFrames = SpriteFrames;
-// attr.ContinuousShoot = ContinuousShoot;
-// attr.AmmoCapacity = AmmoCapacity;
-// attr.MaxAmmoCapacity = MaxAmmoCapacity;
-// attr.StandbyAmmoCapacity = StandbyAmmoCapacity;
-// attr.ReloadTime = ReloadTime;
-// attr.AloneReload = AloneReload;
-// attr.AloneReloadCount = AloneReloadCount;
-// attr.AloneReloadCanShoot = AloneReloadCanShoot;
-// attr.LooseShoot = LooseShoot;
-// attr.MinChargeTime = MinChargeTime;
-// attr.MinContinuousCount = MinContinuousCount;
-// attr.MaxContinuousCount = MaxContinuousCount;
-// attr.TriggerInterval = TriggerInterval;
-// attr.StartFiringSpeed = StartFiringSpeed;
-// attr.FinalFiringSpeed = FinalFiringSpeed;
-// attr.FiringSpeedAddSpeed = FiringSpeedAddSpeed;
-// attr.FiringSpeedBackSpeed = FiringSpeedBackSpeed;
-// attr.MinFireBulletCount = MinFireBulletCount;
-// attr.MaxFireBulletCount = MaxFireBulletCount;
-// attr.DelayedTime = DelayedTime;
-// attr.StartScatteringRange = StartScatteringRange;
-// attr.FinalScatteringRange = FinalScatteringRange;
-// attr.ScatteringRangeAddValue = ScatteringRangeAddValue;
-// attr.ScatteringRangeBackSpeed = ScatteringRangeBackSpeed;
-// attr.ScatteringRangeBackTime = ScatteringRangeBackTime;
-// attr.MaxDistance = MaxDistance;
-// attr.MinDistance = MinDistance;
-// attr.FirePosition = FirePosition;
-// attr.ShellPosition = ShellPosition;
-// attr.SpritePosition = SpritePosition;
-// attr.Weight = Weight;
-// attr.MaxBacklash = MaxBacklash;
-// attr.MinBacklash = MinBacklash;
-// attr.BacklashRegressionSpeed = BacklashRegressionSpeed;
-// attr.UpliftAngle = UpliftAngle;
-// attr.DefaultAngle = DefaultAngle;
-// attr.UpliftAngleRestore = UpliftAngleRestore;
-// attr.AiTargetLockingTime = AiTargetLockingTime;
-// attr.BulletId = BulletId;
-// attr.ThrowSpritePosition = ThrowSpritePosition;
-// attr.ThrowCollisionSize = ThrowCollisionSize;
-// return attr;
-// }
-//
-// //------------------------------ Ai相关 -----------------------------
-//
-// ///
-// /// 用于Ai, 目标锁定时间, 也就是瞄准目标多久才会开火
-// ///
-// public float AiTargetLockingTime = 0;
-//
-// ///
-// /// 用于Ai, Ai使用该武器发射的子弹速度缩放比
-// ///
-// public float AiBulletSpeedScale = 0.7f;
-//
-// ///
-// /// 用于Ai, Ai使用该武器消耗弹药的概率, (0 - 1)
-// ///
-// public float AiAmmoConsumptionProbability = 0f;
-//
-// ///
-// /// Ai 使用该武器时的武器数据, 设置该字段, 可让同一把武器在敌人和玩家手上有不同属性
-// ///
-// public WeaponAttribute AiUseAttribute;
-// }
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/WeaponWeightType.cs b/DungeonShooting_Godot/src/game/item/weapon/WeaponWeightType.cs
deleted file mode 100644
index 5528683..0000000
--- a/DungeonShooting_Godot/src/game/item/weapon/WeaponWeightType.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-///
-/// 根据重量划分的武器类型
-///
-public enum WeaponWeightType
-{
- ///
- /// 副武器
- ///
- DeputyWeapon = 1,
- ///
- /// 主武器
- ///
- MainWeapon = 2,
- ///
- /// 重型武器
- ///
- HeavyWeapon = 3
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs b/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
deleted file mode 100644
index 4267f78..0000000
--- a/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-using Godot;
-
-///
-/// 普通的枪
-///
-[Tool, GlobalClass]
-public partial class Gun : Weapon
-{
- // //步枪属性数据
- // private class RifleAttribute : WeaponAttribute
- // {
- // public RifleAttribute()
- // {
- // Name = "步枪";
- // Icon = ResourcePath.resource_sprite_gun_gun4_png;
- // SpriteFrames = ResourcePath.resource_spriteFrames_Weapon0001_tres;
- // Weight = 40;
- // ThrowSpritePosition = new Vector2(0.4f, -2.6f);
- // StartFiringSpeed = 480;
- // StartScatteringRange = 30;
- // FinalScatteringRange = 90;
- // ScatteringRangeAddValue = 2f;
- // ScatteringRangeBackSpeed = 40;
- // //连发
- // ContinuousShoot = true;
- // AmmoCapacity = 30;
- // StandbyAmmoCapacity = 30 * 3;
- // MaxAmmoCapacity = 30 * 3;
- // //扳机检测间隔
- // TriggerInterval = 0f;
- //
- // //开火前延时
- // DelayedTime = 0f;
- // //攻击距离
- // MinDistance = 300;
- // MaxDistance = 400;
- // //发射子弹数量
- // MinFireBulletCount = 1;
- // MaxFireBulletCount = 1;
- // //抬起角度
- // UpliftAngle = 10;
- // //开火位置
- // FirePosition = new Vector2(21, -3f);
- // //精灵位置
- // SpritePosition = new Vector2(6, -1);
- // ShellPosition = new Vector2(7.5f, -4.5f);
- //
- // AiUseAttribute = Clone();
- // AiUseAttribute.AiTargetLockingTime = 0.5f;
- // AiUseAttribute.TriggerInterval = 3f;
- // AiUseAttribute.ContinuousShoot = false;
- // AiUseAttribute.MinContinuousCount = 3;
- // AiUseAttribute.MaxContinuousCount = 3;
- // }
- // }
- //
- // //手枪属性数据
- // private class PistolAttribute : WeaponAttribute
- // {
- // public PistolAttribute()
- // {
- // Name = "手枪";
- // Icon = ResourcePath.resource_sprite_gun_gun3_png;
- // SpriteFrames = ResourcePath.resource_spriteFrames_Weapon0003_tres;
- // Weight = 20;
- // ThrowSpritePosition = new Vector2(0.4f, -2.6f);
- // WeightType = WeaponWeightType.DeputyWeapon;
- // StartFiringSpeed = 300;
- // FinalFiringSpeed = 300;
- // StartScatteringRange = 5;
- // FinalScatteringRange = 60;
- // ScatteringRangeAddValue = 8f;
- // ScatteringRangeBackSpeed = 40;
- // ScatteringRangeBackTime = 0.5f;
- // //连发
- // ContinuousShoot = false;
- // AmmoCapacity = 12;
- // StandbyAmmoCapacity = 72;
- // MaxAmmoCapacity = 72;
- // //扳机检测间隔
- // TriggerInterval = 0.1f;
- // //连发数量
- // MinContinuousCount = 1;
- // MaxContinuousCount = 1;
- // //开火前延时
- // DelayedTime = 0f;
- // //攻击距离
- // MinDistance = 250;
- // MaxDistance = 300;
- // //发射子弹数量
- // MinFireBulletCount = 1;
- // MaxFireBulletCount = 1;
- // //抬起角度
- // UpliftAngle = 20;
- // //开火位置
- // FirePosition = new Vector2(13, -2);
- // //精灵位置
- // SpritePosition = new Vector2(5, 0);
- // ShellPosition = new Vector2(5, -3);
- //
- // AiUseAttribute = Clone();
- // AiUseAttribute.AiTargetLockingTime = 1f;
- // AiUseAttribute.TriggerInterval = 2f;
- // }
- // }
- //
- // //狙击步枪
- // private class SniperRifleAttribute : WeaponAttribute
- // {
- // public SniperRifleAttribute()
- // {
- // Name = "狙击步枪";
- // Icon = ResourcePath.resource_sprite_gun_gun3_png;
- // SpriteFrames = ResourcePath.resource_spriteFrames_Weapon0005_tres;
- // Weight = 20;
- // ThrowSpritePosition = new Vector2(0.4f, -2.6f);
- // WeightType = WeaponWeightType.DeputyWeapon;
- // StartFiringSpeed = 60;
- // FinalFiringSpeed = 60;
- // StartScatteringRange = 0;
- // FinalScatteringRange = 20;
- // ScatteringRangeAddValue = 10;
- // ScatteringRangeBackSpeed = 10;
- // //连发
- // ContinuousShoot = false;
- // AmmoCapacity = 10;
- // StandbyAmmoCapacity = 50;
- // MaxAmmoCapacity = 50;
- // //扳机检测间隔
- // TriggerInterval = 0.1f;
- // //连发数量
- // MinContinuousCount = 1;
- // MaxContinuousCount = 1;
- // //开火前延时
- // DelayedTime = 0f;
- // //攻击距离
- // MinDistance = 600;
- // MaxDistance = 800;
- // //发射子弹数量
- // MinFireBulletCount = 1;
- // MaxFireBulletCount = 1;
- // //抬起角度
- // UpliftAngle = 15;
- // UpliftAngleRestore = 3.5f;
- // //开火位置
- // FirePosition = new Vector2(28, -3.5f);
- // //精灵位置
- // SpritePosition = new Vector2(9, 0);
- // ShellPosition = new Vector2(7, -3.5f);
- // MinBacklash = 6;
- // MinBacklash = 8;
- // BacklashRegressionSpeed = 20;
- // ReloadTime = 3.5f;
- //
- // AiUseAttribute = Clone();
- // AiUseAttribute.AiTargetLockingTime = 1f;
- // AiUseAttribute.TriggerInterval = 2f;
- // }
- // }
-
- protected override void OnFire()
- {
- //创建一个弹壳
- ThrowShell("0001");
-
- if (Master == Player.Current)
- {
- //创建抖动
- GameCamera.Main.DirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 2f);
- }
-
- //创建开火特效
- var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_ShotFire_tscn);
- var sprite = packedScene.Instantiate();
- sprite.GlobalPosition = FirePoint.GlobalPosition;
- sprite.GlobalRotation = FirePoint.GlobalRotation;
- sprite.AddToActivityRoot(RoomLayerEnum.YSortLayer);
-
- //播放射击音效
- SoundManager.PlaySoundEffectPosition(ResourcePath.resource_sound_sfx_ordinaryBullet2_mp3, GameApplication.Instance.ViewToGlobalPosition(GlobalPosition), -8);
- }
-
- protected override void OnShoot(float fireRotation)
- {
- //创建子弹
- var bullet = ActivityObject.Create(Attribute.BulletId);
- bullet.Init(
- this,
- 350,
- Utils.RandomRangeFloat(Attribute.MinDistance, Attribute.MaxDistance),
- FirePoint.GlobalPosition,
- fireRotation,
- GetAttackLayer()
- );
- bullet.PutDown(RoomLayerEnum.YSortLayer);
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs b/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs
deleted file mode 100644
index 4490496..0000000
--- a/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using Godot;
-
-[Tool, GlobalClass]
-public partial class Shotgun : Weapon
-{
-
- // private class ShotgunAttribute : WeaponAttribute
- // {
- // public ShotgunAttribute()
- // {
- // Name = "霰弹枪";
- // Icon = ResourcePath.resource_sprite_gun_gun2_png;
- // SpriteFrames = ResourcePath.resource_spriteFrames_Weapon0002_tres;
- // Weight = 40;
- // ThrowSpritePosition = new Vector2(0.4f, -2.6f);
- // StartFiringSpeed = 400;
- // StartScatteringRange = 30;
- // FinalScatteringRange = 90;
- // ScatteringRangeAddValue = 50f;
- // ScatteringRangeBackSpeed = 50;
- // //连发
- // ContinuousShoot = false;
- // AmmoCapacity = 7;
- // StandbyAmmoCapacity = 42;
- // MaxAmmoCapacity = 42;
- // AloneReload = true;
- // AloneReloadCanShoot = true;
- // ReloadTime = 0.6f;
- // //连发数量
- // MinContinuousCount = 1;
- // MaxContinuousCount = 1;
- // //开火前延时
- // DelayedTime = 0f;
- // //攻击距离
- // MinDistance = 200;
- // MaxDistance = 250;
- // //发射子弹数量
- // MinFireBulletCount = 5;
- // MaxFireBulletCount = 5;
- // //抬起角度
- // UpliftAngle = 15;
- // MaxBacklash = 6;
- // MinBacklash = 5;
- // //开火位置
- // FirePosition = new Vector2(22, -4);
- // //精灵位置
- // SpritePosition = new Vector2(9, -2);
- // ShellPosition = new Vector2(3, -4);
- //
- // BulletId = ActivityIdPrefix.Bullet + "0002";
- //
- // AiUseAttribute = Clone();
- // AiUseAttribute.AiTargetLockingTime = 0.2f;
- // AiUseAttribute.TriggerInterval = 3.5f;
- // }
- // }
-
- ///
- /// 弹壳预制体
- ///
- public PackedScene ShellPack;
-
- public override void OnInit()
- {
- base.OnInit();
- ShellPack = ResourceManager.Load(ResourcePath.prefab_weapon_shell_ShellCase_tscn);
- }
-
- protected override void OnFire()
- {
- //创建一个弹壳
- ThrowShell("0001");
-
- if (Master == Player.Current)
- {
- //创建抖动
- GameCamera.Main.DirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 2f);
- }
-
- //创建开火特效
- var packedScene = ResourceManager.Load(ResourcePath.prefab_effect_ShotFire_tscn);
- var sprite = packedScene.Instantiate();
- sprite.GlobalPosition = FirePoint.GlobalPosition;
- sprite.GlobalRotation = FirePoint.GlobalRotation;
- sprite.AddToActivityRoot(RoomLayerEnum.YSortLayer);
-
- //播放射击音效
- SoundManager.PlaySoundEffectPosition(ResourcePath.resource_sound_sfx_ordinaryBullet3_mp3, GameApplication.Instance.ViewToGlobalPosition(GlobalPosition), -15);
- }
-
- protected override void OnShoot(float fireRotation)
- {
- //创建子弹
- var bullet = ActivityObject.Create(Attribute.BulletId);
- bullet.Init(
- this,
- Utils.RandomRangeInt(280, 380),
- Utils.RandomRangeFloat(Attribute.MinDistance, Attribute.MaxDistance),
- FirePoint.GlobalPosition,
- fireRotation + Utils.RandomRangeFloat(-20 / 180f * Mathf.Pi, 20 / 180f * Mathf.Pi),
- GetAttackLayer()
- );
- bullet.PutDown(RoomLayerEnum.YSortLayer);
- }
-}
diff --git a/DungeonShooting_Godot/src/game/item/weapon/knife/Knife.cs b/DungeonShooting_Godot/src/game/item/weapon/knife/Knife.cs
deleted file mode 100644
index 4912170..0000000
--- a/DungeonShooting_Godot/src/game/item/weapon/knife/Knife.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-
-using Godot;
-
-[Tool, GlobalClass]
-public partial class Knife : Weapon
-{
- // private class KnifeAttribute : WeaponAttribute
- // {
- // public KnifeAttribute()
- // {
- // Icon = ResourcePath.resource_sprite_gun_knife1_png;
- // WeaponPrefab = ResourcePath.prefab_weapon_Knife_tscn;
- // //攻速设置
- // StartFiringSpeed = 180;
- // FinalFiringSpeed = StartFiringSpeed;
- // //关闭连发
- // ContinuousShoot = false;
- // //设置成松发开火
- // LooseShoot = true;
- // //弹药量, 可以理解为耐久度
- // AmmoCapacity = 180;
- // MaxAmmoCapacity = AmmoCapacity;
- // //握把位置
- // SpritePosition = new Vector2(10, 0);
- // MaxDistance = MinDistance = 35;
- // //后坐力改为向前, 模拟手伸长的效果
- // MaxBacklash = -8;
- // MinBacklash = -8;
- // BacklashRegressionSpeed = 24;
- // UpliftAngle = -95;
- //
- // //AiUseAttribute = Clone();
- // //AiUseAttribute.TriggerInterval = 3f;
- // }
- // }
-
- private Area2D _hitArea;
- private int _attackIndex = 0;
-
- public override void OnInit()
- {
- base.OnInit();
-
- _hitArea = GetNode("HitArea");
- _hitArea.Monitoring = false;
- _hitArea.Monitorable = false;
- _hitArea.BodyEntered += OnBodyEntered;
- //禁用自动播放动画
- IsAutoPlaySpriteFrames = false;
- }
-
- protected override void Process(float delta)
- {
- base.Process(delta);
- if (IsActive)
- {
- //让碰撞节点与武器挂载节点位置保持一致, 而不跟着武器走
- _hitArea.GlobalPosition = Master.MountPoint.GlobalPosition;
- }
- }
-
- protected override void PhysicsProcess(float delta)
- {
- base.PhysicsProcess(delta);
- //过去两个物理帧后就能关闭碰撞了
- if (++_attackIndex >= 2)
- {
- _hitArea.Monitoring = false;
- }
- }
-
- protected override void OnStartCharge()
- {
- //开始蓄力时武器角度上抬120度
- RotationDegrees = -120;
- }
-
- protected override void OnFire()
- {
- GD.Print("近战武器攻击! 蓄力时长: " + GetTriggerChargeTime() + ", 扳机按下时长: " + GetTriggerDownTime());
- //更新碰撞层级
- _hitArea.CollisionMask = GetAttackLayer();
- //启用碰撞
- _hitArea.Monitoring = true;
- _attackIndex = 0;
-
- if (IsActive) //被使用
- {
- //播放挥刀特效
- SpecialEffectManager.Play(
- ResourcePath.resource_spriteFrames_KnifeHit1_tres, "default",
- Master.MountPoint.GlobalPosition, GlobalRotation + Mathf.Pi * 0.5f, new Vector2((int)Master.Face, 1) * AnimatedSprite.Scale,
- new Vector2(17, 4), 1
- );
- }
-
-
- if (Master == Player.Current)
- {
- //创建抖动
- //GameCamera.Main.ProcessDirectionalShake(Vector2.Right.Rotated(GlobalRotation - Mathf.Pi * 0.5f) * 1.5f);
- }
- }
-
- protected override void OnShoot(float fireRotation)
- {
-
- }
-
- protected override int UseAmmoCount()
- {
- //这里要做判断, 如果没有碰到敌人, 则不消耗弹药 (耐久)
- return 0;
- }
-
- private void OnBodyEntered(Node2D body)
- {
- GD.Print("碰到物体: " + body.Name);
- var activityObject = body.AsActivityObject();
- if (activityObject != null)
- {
- if (activityObject is Role role)
- {
- role.CallDeferred(nameof(Role.Hurt), 10, (role.GetCenterPosition() - GlobalPosition).Angle());
- }
- }
- }
-}
diff --git a/DungeonShooting_Godot/src/game/room/DungeonManager.cs b/DungeonShooting_Godot/src/game/room/DungeonManager.cs
index a719a8d..904877c 100644
--- a/DungeonShooting_Godot/src/game/room/DungeonManager.cs
+++ b/DungeonShooting_Godot/src/game/room/DungeonManager.cs
@@ -150,7 +150,6 @@
//玩家手上添加武器
//player.PickUpWeapon(ActivityObject.Create(ActivityObject.Ids.Id_weapon0001));
var weapon = ActivityObject.Create(ActivityObject.Ids.Id_weapon0001);
- weapon.InitWeapon(ExcelConfig.Weapon_List[0]);
weapon.PutDown(player.Position, RoomLayerEnum.NormalLayer);
GameApplication.Instance.Cursor.SetGuiMode(false);