| @@ -76,7 +76,7 @@ namespace GameClass.GameObj | |||
| && playerState != PlayerStateType.IsAddicted && playerState != PlayerStateType.IsStunned | |||
| && playerState != PlayerStateType.IsSwinging && playerState != PlayerStateType.IsTryingToAttack | |||
| && playerState != PlayerStateType.IsClimbingThroughWindows); | |||
| public bool InteractingWithMapWithoutMoving() => (playerState == PlayerStateType.IsLockingTheDoor || playerState == PlayerStateType.IsFixing || playerState == PlayerStateType.IsRummagingInTheChest); | |||
| public bool InteractingWithMapWithoutMoving() => (playerState == PlayerStateType.IsLockingTheDoor || playerState == PlayerStateType.IsFixing || playerState == PlayerStateType.IsOpeningTheChest); | |||
| public bool NullOrMoving() => (playerState == PlayerStateType.Null || playerState == PlayerStateType.IsMoving); | |||
| // private int deathCount = 0; | |||
| @@ -137,8 +137,8 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| private Prop? propInventory; | |||
| public Prop? PropInventory // 持有的道具 | |||
| private Prop[] propInventory = new Prop[GameData.maxNumOfPropInPropInventory]; | |||
| public Prop[] PropInventory | |||
| { | |||
| get => propInventory; | |||
| set | |||
| @@ -155,16 +155,30 @@ namespace GameClass.GameObj | |||
| /// 使用物品栏中的道具 | |||
| /// </summary> | |||
| /// <returns>被使用的道具</returns> | |||
| public Prop? UseProp() | |||
| public Prop? UseProp(int indexing) | |||
| { | |||
| if (indexing < 0 || indexing >= GameData.maxNumOfPropInPropInventory) | |||
| return null; | |||
| lock (gameObjLock) | |||
| { | |||
| var oldProp = PropInventory; | |||
| PropInventory = null; | |||
| return oldProp; | |||
| Prop prop = propInventory[indexing]; | |||
| PropInventory[indexing] = null; | |||
| return prop; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 如果indexing==GameData.maxNumOfPropInPropInventory表明道具栏为满 | |||
| /// </summary> | |||
| public int IndexingOfAddProp() | |||
| { | |||
| int indexing = 0; | |||
| for (; indexing < GameData.maxNumOfPropInPropInventory; ++indexing) | |||
| if (PropInventory[indexing] == null) | |||
| break; | |||
| return indexing; | |||
| } | |||
| /// <summary> | |||
| /// 是否在隐身 | |||
| /// </summary> | |||
| @@ -0,0 +1,34 @@ | |||
| using Preparation.Utility; | |||
| using System.Collections.Generic; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 箱子 | |||
| /// </summary> | |||
| public class Chest : GameObj | |||
| { | |||
| public Chest(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest) | |||
| { | |||
| this.place = PlaceType.Chest; | |||
| this.CanMove = false; | |||
| } | |||
| public override bool IsRigid => true; | |||
| public override ShapeType Shape => ShapeType.Square; | |||
| private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest]; | |||
| public Prop[] PropInChest => propInChest; | |||
| private bool isOpen = false; | |||
| public bool IsOpen | |||
| { | |||
| get => isOpen; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| isOpen = value; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -12,7 +12,8 @@ namespace GameClass.GameObj | |||
| protected override bool IgnoreCollideExecutor(IGameObj targetObj) | |||
| { | |||
| if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet || targetObj.Type == GameObjType.Character) | |||
| if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet | |||
| || targetObj.Type == GameObjType.Character || targetObj.Type == GameObjType.Chest) | |||
| return true; | |||
| return false; | |||
| } | |||
| @@ -108,17 +108,16 @@ namespace Gaming | |||
| // gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); | |||
| // } | |||
| Prop? dropProp = null; | |||
| if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地 | |||
| for (int i = 0; i < GameData.maxNumOfPropInPropInventory; i++) | |||
| { | |||
| dropProp = player.PropInventory; | |||
| XY res = GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell); | |||
| dropProp.ReSetPos(res, gameMap.GetPlaceType(res)); | |||
| Prop? prop = player.UseProp(i); | |||
| if (prop != null) | |||
| { | |||
| prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position)); | |||
| gameMap.Add(prop); | |||
| } | |||
| } | |||
| if (dropProp != null) | |||
| gameMap.Add(dropProp); | |||
| // player.Reset(); | |||
| // ((Character?)bullet.Parent)?.AddScore(GameData.addScoreWhenKillOneLevelPlayer); // 给击杀者加分 | |||
| @@ -293,24 +293,24 @@ namespace Gaming | |||
| _ = attackManager.Attack(player, angle); | |||
| } | |||
| } | |||
| public void UseProp(long playerID) | |||
| public void UseProp(long playerID, int indexing) | |||
| { | |||
| if (!gameMap.Timer.IsGaming) | |||
| return; | |||
| Character? player = gameMap.FindPlayer(playerID); | |||
| if (player != null) | |||
| { | |||
| propManager.UseProp(player); | |||
| propManager.UseProp(player, indexing); | |||
| } | |||
| } | |||
| public void ThrowProp(long playerID, int timeInmillionSeconds, double angle) | |||
| public void ThrowProp(long playerID, int indexing) | |||
| { | |||
| if (!gameMap.Timer.IsGaming) | |||
| return; | |||
| Character? player = gameMap.FindPlayer(playerID); | |||
| if (player != null) | |||
| { | |||
| propManager.ThrowProp(player, timeInmillionSeconds, angle); | |||
| propManager.ThrowProp(player, indexing); | |||
| } | |||
| } | |||
| public bool PickProp(long playerID, PropType propType = PropType.Null) | |||
| @@ -28,11 +28,11 @@ namespace Gaming | |||
| ProduceProp(); | |||
| } | |||
| public void UseProp(Character player) | |||
| public void UseProp(Character player, int indexing) | |||
| { | |||
| if (player.IsResetting) | |||
| return; | |||
| Prop? prop = player.UseProp(); | |||
| Prop? prop = player.UseProp(indexing); | |||
| switch (prop?.GetPropType()) | |||
| { | |||
| case PropType.Spear: | |||
| @@ -62,6 +62,10 @@ namespace Gaming | |||
| { | |||
| if (player.IsResetting) | |||
| return false; | |||
| int indexing = player.IndexingOfAddProp(); | |||
| if (indexing == GameData.maxNumOfPropInPropInventory) | |||
| return false; | |||
| Prop? pickProp = null; | |||
| if (propType == PropType.Null) // 自动检查有无道具可捡 | |||
| { | |||
| @@ -70,9 +74,9 @@ namespace Gaming | |||
| { | |||
| foreach (Prop prop in gameMap.GameObjDict[GameObjType.Prop]) | |||
| { | |||
| if (GameData.IsInTheSameCell(prop.Position, player.Position) && prop.CanMove == false) | |||
| if (GameData.IsInTheSameCell(prop.Position, player.Position)) | |||
| { | |||
| pickProp = prop; | |||
| player.PropInventory[indexing] = prop; | |||
| } | |||
| } | |||
| } | |||
| @@ -92,7 +96,7 @@ namespace Gaming | |||
| { | |||
| if (GameData.IsInTheSameCell(prop.Position, player.Position) && prop.CanMove == false) | |||
| { | |||
| pickProp = prop; | |||
| player.PropInventory[indexing] = prop; | |||
| } | |||
| } | |||
| } | |||
| @@ -105,42 +109,26 @@ namespace Gaming | |||
| if (pickProp != null) | |||
| { | |||
| // pickProp.CanMove = false; | |||
| Prop? dropProp = null; | |||
| if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地 | |||
| { | |||
| dropProp = player.PropInventory; | |||
| XY res = GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell); | |||
| dropProp.ReSetPos(res, gameMap.GetPlaceType(res)); | |||
| } | |||
| player.PropInventory = pickProp; | |||
| gameMap.Remove(pickProp); | |||
| if (dropProp != null) gameMap.Add(dropProp); | |||
| gameMap.Add(new PickedProp(pickProp)); | |||
| return true; | |||
| } | |||
| else | |||
| return false; | |||
| } | |||
| public void ThrowProp(Character player, int timeInMilliseconds, double angle) | |||
| public void ThrowProp(Character player, int indexing) | |||
| { | |||
| if (!gameMap.Timer.IsGaming) | |||
| return; | |||
| if (player.IsResetting) // 移动中也能扔,但由于“惯性”,可能初始位置会有点变化 | |||
| if (!gameMap.Timer.IsGaming || player.IsResetting) | |||
| return; | |||
| Prop? prop = player.UseProp(); | |||
| Prop? prop = player.UseProp(indexing); | |||
| if (prop == null) | |||
| return; | |||
| prop.CanMove = true; | |||
| prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position)); | |||
| gameMap.Add(prop); | |||
| timeInMilliseconds = timeInMilliseconds < GameData.PropMaxMoveDistance / prop.MoveSpeed * 1000 ? timeInMilliseconds : GameData.PropMaxMoveDistance / prop.MoveSpeed * 1000; | |||
| moveEngine.MoveObj(prop, timeInMilliseconds, angle); | |||
| } | |||
| private void ProduceProp() | |||
| { | |||
| int len = availableCellForGenerateProp.Count; | |||
| @@ -20,7 +20,7 @@ namespace Preparation.Utility | |||
| IsStunned = 11, | |||
| IsTryingToAttack = 12,//指前摇 | |||
| IsLockingTheDoor = 13, | |||
| IsRummagingInTheChest = 14, | |||
| IsOpeningTheChest = 14, | |||
| IsClimbingThroughWindows = 15, | |||
| IsUsingSpecialSkill = 16, | |||
| } | |||
| @@ -52,39 +52,28 @@ namespace Preparation.Utility | |||
| #endregion | |||
| #region 角色相关 | |||
| public const int characterRadius = numOfPosGridPerCell / 2; // 人物半径 | |||
| public const int basicApOfGhost = 1500000; // 攻击力 | |||
| public const int characterRadius = numOfPosGridPerCell / 2 / 5 * 4; // 人物半径 | |||
| public const int basicTreatSpeed = 100; | |||
| public const int basicFixSpeed = 100; | |||
| public const int basicTimeOfOpeningOrLocking = 3000; | |||
| public const int basicTimeOfClimbingThroughWindows = 870; | |||
| public const int basicHp = 3000000; // 初始血量 | |||
| public const int basicMaxGamingAddiction = 60000;//基本完全沉迷时间 | |||
| public const int BeginGamingAddiction = 10003; | |||
| public const int MidGamingAddiction = 30000; | |||
| public const int basicTreatmentDegree = 1500000; | |||
| public const int basicRescueDegree = 100000; | |||
| public const int basicHp = 3000000; // 初始血量 | |||
| public const int basicCD = 3000; // 初始子弹冷却 | |||
| public const int basicCastTime = 500;//基本前摇时间 | |||
| public const int basicBackswing = 500;//基本后摇时间 | |||
| public const int basicRecoveryFromHit = 4300;//基本命中攻击恢复时长 | |||
| public const int basicBulletNum = 3; // 基本初始子弹量 | |||
| public const int MinAP = 0; // 最小攻击力 | |||
| public const int MaxAP = int.MaxValue; // 最大攻击力 | |||
| public const double basicRemoteAttackRange = 9000; // 基本远程攻击范围 | |||
| public const double basicAttackShortRange = 2700; // 基本近程攻击范围 | |||
| public const double basicBulletBombRange = 3000; // 基本子弹爆炸范围 | |||
| public const int basicMoveSpeed = 1260; // 基本移动速度,单位:s-1 | |||
| public const int basicBulletMoveSpeed = 2700; // 基本子弹移动速度,单位:s-1 | |||
| public const int characterMaxSpeed = 12000; // 最大速度 | |||
| public const int basicBulletMoveSpeed = 2700; // 基本子弹移动速度,单位:s-1 | |||
| public const double basicConcealment = 1.0; | |||
| public const int basicAlertnessRadius = 30700; | |||
| public const int basicTimeOfOpeningOrLocking = 3000; | |||
| public const int basicTimeOfClimbingThroughWindows = 870; | |||
| public const int maxNumOfPropInPropInventory = 3; | |||
| public const int addScoreWhenKillOneLevelPlayer = 30; // 击杀一级角色获得的加分 | |||
| public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | |||
| public const int commonSkillTime = 10000; // 普通技能标准持续时间 | |||
| public const int bulletRadius = 200; // 默认子弹半径 | |||
| public const int reviveTime = 30000; // 复活时间 | |||
| public const int shieldTimeAtBirth = 3000; // 复活时的护盾时间 | |||
| public static XY PosWhoDie = new XY(1, 1); | |||
| @@ -97,7 +86,25 @@ namespace Preparation.Utility | |||
| }; | |||
| } | |||
| #endregion | |||
| #region 攻击与子弹相关 | |||
| public const int basicApOfGhost = 1500000; // 捣蛋鬼攻击力 | |||
| public const int MinAP = 0; // 最小攻击力 | |||
| public const int MaxAP = int.MaxValue; // 最大攻击力 | |||
| public const int basicCD = 3000; // 初始子弹冷却 | |||
| public const int basicCastTime = 500;//基本前摇时间 | |||
| public const int basicBackswing = 500;//基本后摇时间 | |||
| public const int basicRecoveryFromHit = 4300;//基本命中攻击恢复时长 | |||
| public const int bulletRadius = 200; // 默认子弹半径 | |||
| public const int basicBulletNum = 3; // 基本初始子弹量 | |||
| public const double basicRemoteAttackRange = 9000; // 基本远程攻击范围 | |||
| public const double basicAttackShortRange = 2700; // 基本近程攻击范围 | |||
| public const double basicBulletBombRange = 3000; // 基本子弹爆炸范围 | |||
| #endregion | |||
| #region 技能相关 | |||
| public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | |||
| public const int commonSkillTime = 10000; // 普通技能标准持续时间 | |||
| /// <summary> | |||
| /// BeginToCharge | |||
| /// </summary> | |||
| @@ -117,6 +124,7 @@ namespace Preparation.Utility | |||
| #endregion | |||
| #region 物体相关 | |||
| public const int degreeOfFixedGenerator = 10300000; | |||
| public const int maxNumOfPropInChest = 2; | |||
| #endregion | |||
| #region 游戏帧相关 | |||
| public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | |||
| @@ -94,7 +94,7 @@ | |||
| IsStunned = 11, | |||
| IsTryingToAttack = 12,//指前摇 | |||
| IsLockingTheDoor = 13, | |||
| IsRummagingInTheChest = 14, | |||
| IsOpeningTheChest = 14, | |||
| IsClimbingThroughWindows = 15, | |||
| IsUsingSpecialSkill = 16, | |||
| } | |||
| @@ -111,7 +111,7 @@ | |||
| - ~~回血率/原始回血率~~ | |||
| - 当前子弹类型 | |||
| - 原始子弹类型 | |||
| - 持有道具 *(最多三个)(列表)* | |||
| - 持有道具 (最多三个)(数组) | |||
| - 是否隐身 | |||
| - 队伍ID | |||
| - 玩家ID | |||
| @@ -221,7 +221,7 @@ | |||
| 3. 修理电机 | |||
| 4. 开锁门 | |||
| 5. 翻窗 | |||
| 6. 翻找箱子 | |||
| 6. 开启箱子 | |||
| ### 门 | |||
| - *门分别属于三个教学区:三教,五教,六教* | |||
| @@ -238,10 +238,31 @@ | |||
| - *翻越窗户是一种交互行为,执行时,实质是限定方向的减速运动* | |||
| ### 箱子 | |||
| - *监管者和求生者都能与箱子交互,同一时刻就允许一人进行翻找* | |||
| - *监管者和求生者都能与箱子交互,同一时刻只允许一人进行开启* | |||
| - *开启箱子有不同概率获得不同道具。* | |||
| - *搜寻物品的基础持续时间为10秒。* | |||
| - *未搜寻完成的箱子在下一次需要重新开始搜寻。* | |||
| - *开启箱子的基础持续时间为10秒。* | |||
| - *未开启完成的箱子在下一次需要重新开始开启。* | |||
| - *箱子开启后其中道具才可以被观测和拿取* | |||
| - [箱子道具不刷新] | |||
| - [箱子不可被关闭] | |||
| - [箱子内道具最多两个] | |||
| ### 道具 | |||
| - 每次玩家试图捡起道具时,需要确保道具栏有空位 | |||
| - indexing指道具栏数组下标从0开始 | |||
| - 扔道具 | |||
| - Logic内实现 | |||
| ~~~csharp | |||
| public void ThrowProp(long playerID, int indexing) | |||
| ~~~ | |||
| - 对应下标出现空位,不会对数组进行重新排序 | |||
| - 使用道具 | |||
| - Logic内实现 | |||
| ~~~csharp | |||
| public void UseProp(long playerID,int indexing) | |||
| ~~~ | |||
| - 对应下标出现空位,不会对数组进行重新排序 | |||
| ### 治疗 | |||
| - 可行动的求生者可以对受伤的其他求生者进行治疗,治疗完成后会回复被治疗程度的血量。 | |||