diff --git a/docs/CAPI接口(cpp).md b/docs/CAPI接口(cpp).md index a9311cf..723f727 100644 --- a/docs/CAPI接口(cpp).md +++ b/docs/CAPI接口(cpp).md @@ -17,7 +17,7 @@ #### 人物 - `std::future EndAllAction()`:可以使不处在不可行动状态中的玩家终止当前行动 - 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令 - - 实际上唤醒或勉励不同的人是有效的 +- EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次 #### 攻击 - `std::future Attack(double angleInRadian)`:`angleInRadian`为攻击方向 diff --git a/docs/CAPI接口(python).md b/docs/CAPI接口(python).md index 104c83f..dda0414 100644 --- a/docs/CAPI接口(python).md +++ b/docs/CAPI接口(python).md @@ -22,7 +22,6 @@ - `def EndAllAction(self) -> Future[bool]`:可以使不处在不可行动状态中的玩家终止当前行动 - 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令 - - 实际上唤醒或勉励不同的人是有效的 - EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次 #### 攻击 diff --git a/docs/GameRules.md b/docs/GameRules.md index a868e63..9f6f16c 100644 --- a/docs/GameRules.md +++ b/docs/GameRules.md @@ -89,13 +89,14 @@ $$ 8. 翻窗 Climbing ### 攻击 +- 攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)*2 - 攻击类型CommonAttackOfTricker攻击未写完的作业,会造成对应攻击力的损坏 - 捣蛋鬼攻击交互状态或前后摇的学生,将使学生眩晕4.3s | 攻击(子弹)类型 |搞蛋鬼的一般攻击CommonAttackOfTricker| 飞刀FlyingKnife | 蹦蹦炸弹BombBomb | 小炸弹JumpyDumpty | | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | -| 子弹爆炸范围 | 0 | 0 | 2000 | 1000 | -| 子弹攻击距离 | 2200 | 78000 | 2200 | 4400 | +| 爆炸范围 | 0 | 0 | 2000 | 1000 | +| 攻击距离 | 2200 | 78000 | 2200 | 4400 | | 攻击力 | 1500000 | 1200000 | 1800000 | 900000 | | 移动速度/s | 7400 | 18500 | 6000 | 8600 | | 前摇(ms) | 297 | 400 | 366 | - | diff --git a/docs/版本更新说明.md b/docs/版本更新说明.md index 6869194..70f91f2 100644 --- a/docs/版本更新说明.md +++ b/docs/版本更新说明.md @@ -11,11 +11,21 @@ # 5月6日12点更新 - hotfix: 修复了突然的bug(物件锁的相关问题) -- rule:增加了每帧最多50条主动指令的限制 +- rule:增加了每帧最多50条主动指令的限制 # 5月8日更新 -- feat:增加了可选地图功能 +- feat:增加了可选地图功能 - **脚本RunServer(ForDebug).cmd/sh现在支持可选地图功能,但想选择地图,选手需要自行参照使用文档修改命令行或在云盘下载脚本** +# 5月9日19:30更新 +- docs:更新了 游戏机制与平衡性调整更新草案.pdf +- change:更改了地图的文件路径 + # 最新更新 -- docs:更新了 游戏机制与平衡性调整更新草案.pdf \ No newline at end of file +- fix:修复JumpyDumpty的初始位置错误的问题 +- fix:修正和重新说明攻击距离 + - **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2** +- hotfix:修复小炸弹初始化类型错误的问题 +- remove:去除了“实际上唤醒或勉励不同的人是有效的” + - **重复发出同一类型的交互指令和移动指令是无效的** +- feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`** \ No newline at end of file diff --git a/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs b/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs index 7601669..52e29dc 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs @@ -10,7 +10,7 @@ namespace GameClass.GameObj { } public override double BulletBombRange => 0; - public override double BulletAttackRange => GameData.basicAttackShortRange; + public override double AttackDistance => GameData.basicAttackShortRange; public int ap = GameData.basicApOfGhost; public override int AP { @@ -24,7 +24,7 @@ namespace GameClass.GameObj public override int Speed => GameData.basicBulletMoveSpeed; public override bool IsRemoteAttack => false; - public override int CastTime => (int)BulletAttackRange * 1000 / Speed; + public override int CastTime => (int)AttackDistance * 1000 / Speed; public override int Backswing => GameData.basicBackswing; public override int RecoveryFromHit => GameData.basicRecoveryFromHit; public const int cd = GameData.basicBackswing; @@ -57,7 +57,7 @@ namespace GameClass.GameObj { } public override double BulletBombRange => 0; - public override double BulletAttackRange => GameData.basicRemoteAttackRange * 13; + public override double AttackDistance => GameData.basicRemoteAttackRange * 13; public int ap = GameData.basicApOfGhost * 4 / 5; public override int AP { @@ -104,7 +104,7 @@ namespace GameClass.GameObj { } public override double BulletBombRange => GameData.basicBulletBombRange; - public override double BulletAttackRange => GameData.basicAttackShortRange; + public override double AttackDistance => GameData.basicAttackShortRange; public int ap = (int)(GameData.basicApOfGhost * 6.0 / 5); public override int AP { @@ -118,7 +118,7 @@ namespace GameClass.GameObj public override int Speed => (int)(GameData.basicBulletMoveSpeed * 30 / 37); public override bool IsRemoteAttack => false; - public override int CastTime => (int)(BulletAttackRange * 1000 / Speed); + public override int CastTime => (int)(AttackDistance * 1000 / Speed); public override int Backswing => GameData.basicRecoveryFromHit; public override int RecoveryFromHit => GameData.basicRecoveryFromHit; public const int cd = GameData.basicCD; @@ -149,7 +149,7 @@ namespace GameClass.GameObj { } public override double BulletBombRange => GameData.basicBulletBombRange / 2; - public override double BulletAttackRange => GameData.basicAttackShortRange * 2; + public override double AttackDistance => GameData.basicAttackShortRange * 2; public int ap = (int)(GameData.basicApOfGhost * 0.6); public override int AP { diff --git a/logic/GameClass/GameObj/Bullet/Bullet.Student.cs b/logic/GameClass/GameObj/Bullet/Bullet.Student.cs index 5f42b49..abb0c94 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.Student.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.Student.cs @@ -9,7 +9,7 @@ namespace GameClass.GameObj { } public override double BulletBombRange => 0; - public override double BulletAttackRange => 0; + public override double AttackDistance => 0; public override int AP => 7220; public override int Speed => 0; public override bool IsRemoteAttack => false; diff --git a/logic/GameClass/GameObj/Bullet/Bullet.cs b/logic/GameClass/GameObj/Bullet/Bullet.cs index ff512bc..262bc92 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.cs @@ -10,7 +10,7 @@ namespace GameClass.GameObj /// //攻击力 /// public abstract double BulletBombRange { get; } - public abstract double BulletAttackRange { get; } + public abstract double AttackDistance { get; } public abstract int AP { get; set; } public abstract int Speed { get; } public abstract bool IsRemoteAttack { get; } @@ -56,9 +56,9 @@ namespace GameClass.GameObj public static class BulletFactory { - public static Bullet? GetBullet(Character character, XY pos) + public static Bullet? GetBullet(Character character, XY pos, BulletType bulletType) { - switch (character.BulletOfPlayer) + switch (bulletType) { case BulletType.FlyingKnife: return new FlyingKnife(character, pos); diff --git a/logic/GameClass/GameObj/Character/Character.Skill.cs b/logic/GameClass/GameObj/Character/Character.Skill.cs index 0ae23db..b9b152b 100644 --- a/logic/GameClass/GameObj/Character/Character.Skill.cs +++ b/logic/GameClass/GameObj/Character/Character.Skill.cs @@ -1,8 +1,6 @@ using Preparation.Utility; using Preparation.Interface; using System.Collections.Generic; -using System; -using System.Numerics; namespace GameClass.GameObj { diff --git a/logic/GameClass/GameObj/Character/Character.cs b/logic/GameClass/GameObj/Character/Character.cs index 15c2e78..2ecee40 100644 --- a/logic/GameClass/GameObj/Character/Character.cs +++ b/logic/GameClass/GameObj/Character/Character.cs @@ -8,9 +8,6 @@ namespace GameClass.GameObj public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 { #region 装弹、攻击相关的基本属性及方法 - private readonly object attackLock = new(); - public object AttackLock => attackLock; - /// /// 装弹冷却 /// @@ -19,7 +16,7 @@ namespace GameClass.GameObj { get { - lock (attackLock) + lock (actionLock) { return cd; } @@ -33,14 +30,14 @@ namespace GameClass.GameObj { get { - lock (attackLock) + lock (actionLock) { return bulletOfPlayer; } } set { - lock (attackLock) + lock (actionLock) { bulletOfPlayer = value; cd = OrgCD = (BulletFactory.BulletCD(value)); @@ -55,7 +52,7 @@ namespace GameClass.GameObj { get { - lock (attackLock) + lock (actionLock) { return maxBulletNum; } @@ -66,7 +63,7 @@ namespace GameClass.GameObj public int UpdateBulletNum(int time) { - lock (attackLock) + lock (actionLock) { if (bulletNum < maxBulletNum) { @@ -84,7 +81,7 @@ namespace GameClass.GameObj /// 攻击操作发出的子弹 public Bullet? Attack(double angle, int time) { - lock (attackLock) + lock (actionLock) { if (bulletOfPlayer == BulletType.Null) return null; @@ -92,14 +89,16 @@ namespace GameClass.GameObj { if (bulletNum == maxBulletNum) updateTimeOfBulletNum = time; --bulletNum; + XY res = Position + new XY // 子弹紧贴人物生成。 ( - (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Cos(angle))) * ((Math.Cos(angle) > 0) ? 1 : -1), - (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Sin(angle))) * ((Math.Sin(angle) > 0) ? 1 : -1) + (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), + (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) ); - Bullet? bullet = BulletFactory.GetBullet(this, res); + Bullet? bullet = BulletFactory.GetBullet(this, res, this.bulletOfPlayer); if (bullet == null) return null; - facingDirection = new(angle, bullet.BulletAttackRange); + bullet.AP += TryAddAp() ? GameData.ApPropAdd : 0; + facingDirection = new(angle, bullet.AttackDistance); return bullet; } else @@ -305,63 +304,88 @@ namespace GameClass.GameObj { get { - if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving; - return playerState; + lock (actionLock) + { + if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving; + return playerState; + } } } - public bool NoHp() => (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped - || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued); - public bool Commandable() => (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped - && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued - && playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack - && playerState != PlayerStateType.ClimbingThroughWindows && playerState != PlayerStateType.Stunned); - public bool InteractingWithMapWithoutMoving() => (playerState == PlayerStateType.LockingOrOpeningTheDoor || playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest); - public bool NullOrMoving() => (playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); - public bool CanBeAwed() => !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped - || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued - || playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned - || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); - + public bool NoHp() + { + lock (actionLock) + return (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued); + } + public bool Commandable() + { + lock (actionLock) + { + return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped + && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued + && playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack + && playerState != PlayerStateType.ClimbingThroughWindows && playerState != PlayerStateType.Stunned); + } + } + public bool InteractingWithMapWithoutMoving() + { + lock (actionLock) + { + return (playerState == PlayerStateType.LockingOrOpeningTheDoor || playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest); + } + } + public bool NullOrMoving() + { + lock (actionLock) + { + return (playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); + } + } + public bool CanBeAwed() + { + lock (actionLock) + return !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped + || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued + || playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned + || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); + } private GameObj? whatInteractingWith = null; public GameObj? WhatInteractingWith => whatInteractingWith; - private long threadNum = 0; - public long ThreadNum => threadNum; - - public void ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) + public long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) { - lock (moveObjLock) + lock (actionLock) { - ++threadNum; whatInteractingWith = gameObj; if (value != PlayerStateType.Moving) IsMoving = false; playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); + return ++stateNum; } } - public void ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) + public long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) { - lock (moveObjLock) + lock (actionLock) { whatInteractingWith = gameObj; if (value != PlayerStateType.Moving) IsMoving = false; playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); + return stateNum; } } - public void SetPlayerStateNaturally() + public long SetPlayerStateNaturally() { - lock (moveObjLock) + lock (actionLock) { - ++threadNum; whatInteractingWith = null; IsMoving = false; playerState = PlayerStateType.Null; + return ++stateNum; } } @@ -370,11 +394,11 @@ namespace GameClass.GameObj MoveReaderWriterLock.EnterWriteLock(); try { - lock (moveObjLock) + lock (actionLock) { playerState = playerStateType; canMove = false; - isResetting = true; + isRemoved = true; position = GameData.PosWhoDie; } } @@ -623,7 +647,7 @@ namespace GameClass.GameObj public override ShapeType Shape => ShapeType.Circle; public override bool IgnoreCollideExecutor(IGameObj targetObj) { - if (IsResetting) + if (IsRemoved) return true; if (targetObj.Type == GameObjType.Prop) { diff --git a/logic/GameClass/GameObj/Moveable.cs b/logic/GameClass/GameObj/Moveable.cs index 10351dc..f31d0ed 100644 --- a/logic/GameClass/GameObj/Moveable.cs +++ b/logic/GameClass/GameObj/Moveable.cs @@ -6,17 +6,30 @@ namespace GameClass.GameObj { public abstract class Moveable : GameObj, IMoveable { - protected readonly object moveObjLock = new(); - public object MoveLock => moveObjLock; + protected readonly object actionLock = new(); + public object ActionLock => actionLock; private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; - //规定moveReaderWriterLock>moveObjLock + protected long stateNum = 0; + public long StateNum + { + get + { + lock (actionLock) + return stateNum; + } + set + { + lock (actionLock) stateNum = value; + } + } + //规定moveReaderWriterLock>actionLock public override XY Position { get { - lock (moveObjLock) + lock (actionLock) return position; } } @@ -25,7 +38,7 @@ namespace GameClass.GameObj { get { - lock (moveObjLock) + lock (actionLock) return facingDirection; } } @@ -35,12 +48,12 @@ namespace GameClass.GameObj { get { - lock (moveObjLock) + lock (actionLock) return isMoving; } set { - lock (moveObjLock) + lock (actionLock) { isMoving = value; } @@ -51,7 +64,7 @@ namespace GameClass.GameObj public long MovingSetPos(XY moveVec) { if (moveVec.x != 0 || moveVec.y != 0) - lock (moveObjLock) + lock (actionLock) { facingDirection = moveVec; this.position += moveVec; @@ -61,7 +74,7 @@ namespace GameClass.GameObj public void ReSetPos(XY position) { - lock (moveObjLock) + lock (actionLock) { this.position = position; } @@ -88,7 +101,7 @@ namespace GameClass.GameObj moveReaderWriterLock.EnterWriteLock(); try { - lock (moveObjLock) + lock (actionLock) { canMove = value; } @@ -99,15 +112,15 @@ namespace GameClass.GameObj } } - protected bool isResetting; - public bool IsResetting + protected bool isRemoved; + public bool IsRemoved { get { moveReaderWriterLock.EnterReadLock(); try { - return isResetting; + return isRemoved; } finally { @@ -116,7 +129,22 @@ namespace GameClass.GameObj } } - public bool IsAvailable => !IsMoving && CanMove && !IsResetting; // 是否能接收指令 + public bool IsAvailableForMove // 是否能接收移动指令 + { + get + { + moveReaderWriterLock.EnterReadLock(); + try + { + lock (actionLock) + return !isMoving && canMove && !isRemoved; + } + finally + { + moveReaderWriterLock.ExitReadLock(); + } + } + } protected int moveSpeed; /// @@ -141,7 +169,7 @@ namespace GameClass.GameObj moveReaderWriterLock.EnterWriteLock(); try { - lock (moveObjLock) + lock (actionLock) { moveSpeed = value; } @@ -167,7 +195,7 @@ namespace GameClass.GameObj this.FacingDirection = new XY(1, 0); isMoving = false; CanMove = false; - IsResetting = true; + IsRemoved = true; this.Position = birthPos; this.Place= place; } diff --git a/logic/GameEngine/MoveEngine.cs b/logic/GameEngine/MoveEngine.cs index e3c758e..fd84018 100644 --- a/logic/GameEngine/MoveEngine.cs +++ b/logic/GameEngine/MoveEngine.cs @@ -73,24 +73,21 @@ namespace GameEngine obj.MovingSetPos(new XY(moveVec, maxLen)); } - public void MoveObj(IMoveable obj, int moveTime, double direction) + public void MoveObj(IMoveable obj, int moveTime, double direction, long threadNum) { - if (obj.IsMoving) // 已经移动的物体不能再移动 - return; - if (!obj.IsAvailable || !gameTimer.IsGaming) - return; - - long threadNum = (obj.Type == GameObjType.Character) ? ((ICharacter)obj).ThreadNum : 0;//对人特殊处理 + if (!gameTimer.IsGaming) return; + lock (obj.ActionLock) + { + if (!obj.IsAvailableForMove) return; + obj.IsMoving = true; + } new Thread ( () => { - lock (obj.MoveLock) - obj.IsMoving = true; - double moveVecLength = 0.0; XY res = new(direction, moveVecLength); - double deltaLen = moveVecLength - Math.Sqrt(obj.MovingSetPos(res)); // 转向,并用deltaLen存储行走的误差 + double deltaLen = 0; // 转向,并用deltaLen存储行走的误差 IGameObj? collisionObj = null; bool isDestroyed = false; @@ -119,14 +116,14 @@ namespace GameEngine if (!isDestroyed) { new FrameRateTaskExecutor( - () => gameTimer.IsGaming && obj.CanMove && !obj.IsResetting && obj.IsMoving, + () => gameTimer.IsGaming && obj.CanMove && !obj.IsRemoved && obj.IsMoving, () => { moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; res = new XY(direction, moveVecLength); //对人特殊处理 - if (threadNum > 0 && ((ICharacter)obj).ThreadNum != threadNum) return false; + if (threadNum > 0 && obj.StateNum != threadNum) return false; // 越界情况处理:如果越界,则与越界方块碰撞 bool flag; // 循环标志 @@ -147,7 +144,7 @@ namespace GameEngine isDestroyed = true; return false; case AfterCollision.MoveMax: - if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum) + if (threadNum == 0 || obj.StateNum == threadNum) MoveMax(obj, res); moveVecLength = 0; res = new XY(direction, moveVecLength); @@ -155,7 +152,7 @@ namespace GameEngine } } while (flag); - if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum) + if (threadNum == 0 || obj.StateNum == threadNum) deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res)); return true; @@ -174,7 +171,7 @@ namespace GameEngine res = new XY(direction, moveVecLength); if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) { - if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum) + if (threadNum == 0 || obj.StateNum == threadNum) obj.MovingSetPos(res); } else @@ -189,7 +186,7 @@ namespace GameEngine isDestroyed = true; break; case AfterCollision.MoveMax: - if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum) + if (threadNum == 0 || obj.StateNum == threadNum) MoveMax(obj, res); moveVecLength = 0; res = new XY(direction, moveVecLength); @@ -202,7 +199,7 @@ namespace GameEngine { Thread.Sleep(leftTime); // 多移动的在这里补回来 } - lock (obj.MoveLock) + lock (obj.ActionLock) obj.IsMoving = false; // 结束移动 EndMove(obj); return 0; diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs index 5f0f1db..894cab4 100644 --- a/logic/Gaming/ActionManager.cs +++ b/logic/Gaming/ActionManager.cs @@ -21,14 +21,14 @@ namespace Gaming { if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) { - if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty)) + if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty) > 0) player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty)); gameMap.Remove((GameObj)collisionObj); } } if (player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost()) { - if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge)) + if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge) > 0) player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge)); characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge); } @@ -37,9 +37,8 @@ namespace Gaming { if (moveTimeInMilliseconds < 5) return false; if (!playerToMove.Commandable()) return false; - if (playerToMove.IsMoving) return false; - characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving); - moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection); + moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, + characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving)); return true; } @@ -47,7 +46,7 @@ namespace Gaming { if (!playerToMove.Commandable() && playerToMove.PlayerState != PlayerStateType.Stunned) return false; characterManager.BeStunned(playerToMove, moveTimeInMilliseconds); - moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection); + moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, playerToMove.StateNum); return true; } @@ -72,14 +71,14 @@ namespace Gaming ++generatorForFix.NumOfFixing; characterManager.SetPlayerState(player, PlayerStateType.Fixing); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread ( () => { Thread.Sleep(GameData.frameDuration); new FrameRateTaskExecutor( - loopCondition: () => gameMap.Timer.IsGaming && threadNum == player.ThreadNum, + loopCondition: () => gameMap.Timer.IsGaming && threadNum == player.StateNum, loopToDo: () => { if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) @@ -173,10 +172,10 @@ namespace Gaming { characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated); characterManager.SetPlayerState(player, PlayerStateType.Treating); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new FrameRateTaskExecutor( - loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) @@ -187,7 +186,7 @@ namespace Gaming ) .Start(); - if (threadNum == player.ThreadNum) characterManager.SetPlayerState(player); + if (threadNum == player.StateNum) characterManager.SetPlayerState(player); else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated); } ) @@ -205,14 +204,14 @@ namespace Gaming return false; characterManager.SetPlayerState(player, PlayerStateType.Rescuing); characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread ( () => { new FrameRateTaskExecutor( - loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { playerRescued.TimeOfRescue += GameData.frameDuration; @@ -234,7 +233,7 @@ namespace Gaming else characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted); } - if (threadNum == player.ThreadNum) characterManager.SetPlayerState(player); + if (threadNum == player.StateNum) characterManager.SetPlayerState(player); playerRescued.TimeOfRescue = 0; } ) @@ -303,14 +302,14 @@ namespace Gaming // gameMap.Add(addWall); characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; windowForClimb.WhoIsClimbing = player; new Thread ( () => { new FrameRateTaskExecutor( - loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { }, timeInterval: GameData.frameDuration, finallyReturn: () => 0, @@ -326,10 +325,10 @@ namespace Gaming player.ReSetPos(windowToPlayer + windowForClimb.Position); player.MoveSpeed = player.SpeedOfClimbingThroughWindows; - moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle()); + moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle(), threadNum); new FrameRateTaskExecutor( - loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { }, @@ -343,7 +342,7 @@ namespace Gaming player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); windowForClimb.WhoIsClimbing = null; // gameMap.Remove(addWall); - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) { characterManager.SetPlayerState(player); } @@ -386,13 +385,13 @@ namespace Gaming if (!flag) return false; characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread ( () => { new FrameRateTaskExecutor( - loopCondition: () => flag && threadNum == player.ThreadNum && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor, + loopCondition: () => flag && threadNum == player.StateNum && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor, loopToDo: () => { flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null); @@ -407,7 +406,7 @@ namespace Gaming { doorToLock.IsOpen = (!doorToLock.IsOpen); } - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) characterManager.SetPlayerState(player); doorToLock.OpenOrLockDegree = 0; } diff --git a/logic/Gaming/AttackManager.cs b/logic/Gaming/AttackManager.cs index e7f3eca..70aa202 100644 --- a/logic/Gaming/AttackManager.cs +++ b/logic/Gaming/AttackManager.cs @@ -39,6 +39,17 @@ namespace Gaming this.characterManager = characterManager; } + public void ProduceBulletNaturally(BulletType bulletType, Character player, double angle, XY pos) + { + // 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange + if (bulletType == BulletType.Null) return; + Bullet? bullet = BulletFactory.GetBullet(player, pos, bulletType); + if (bullet == null) return; + Debugger.Output(bullet, "Attack in " + pos.ToString()); + gameMap.Add(bullet); + moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms + } + private void BombObj(Bullet bullet, GameObj objBeingShot) { #if DEBUG @@ -122,9 +133,21 @@ namespace Gaming if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null) { - bullet.Parent!.BulletOfPlayer = BulletType.JumpyDumpty; - Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI / 2.0); - Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI * 3.0 / 2.0); + double angle = bullet.FacingDirection.Angle() + Math.PI / 2.0; + XY pos = bullet.Position + new XY // 子弹紧贴人物生成。 + ( + (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), + (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) + ); + ProduceBulletNaturally(BulletType.JumpyDumpty, (Character)bullet.Parent!, angle, pos); + + angle = bullet.FacingDirection.Angle() + Math.PI * 3.0 / 2.0; + pos = bullet.Position + new XY // 子弹紧贴人物生成。 + ( + (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), + (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) + ); + ProduceBulletNaturally(BulletType.JumpyDumpty, (Character)bullet.Parent!, angle, pos); } var beAttackedList = new List(); @@ -171,19 +194,18 @@ namespace Gaming if (bullet != null) { Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); - bullet.AP += player.TryAddAp() ? GameData.ApPropAdd : 0; gameMap.Add(bullet); - moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(player.BulletOfPlayer)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms + moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms if (bullet.CastTime > 0) { characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread (() => { new FrameRateTaskExecutor( - loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { }, @@ -195,7 +217,7 @@ namespace Gaming if (gameMap.Timer.IsGaming) { - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) { characterManager.SetPlayerState(player); } diff --git a/logic/Gaming/CharacterManager .cs b/logic/Gaming/CharacterManager .cs index bb49b8c..07ca53c 100644 --- a/logic/Gaming/CharacterManager .cs +++ b/logic/Gaming/CharacterManager .cs @@ -1,5 +1,4 @@ -using System; -using System.Threading; +using System.Threading; using GameClass.GameObj; using Preparation.Utility; using Preparation.Interface; @@ -18,37 +17,37 @@ namespace Gaming this.gameMap = gameMap; } - public void SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) + public long SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) { - lock (player.MoveLock) + lock (player.ActionLock) { - switch (player.PlayerState) + PlayerStateType nowPlayerState = player.PlayerState; + if (nowPlayerState == value) return -1; + switch (nowPlayerState) { case PlayerStateType.OpeningTheChest: + if (player.NoHp()) return -1; ((Chest)player.WhatInteractingWith!).StopOpen(); - player.ChangePlayerState(value, gameObj); - break; + return player.ChangePlayerState(value, gameObj); case PlayerStateType.OpeningTheDoorway: + if (player.NoHp()) return -1; Doorway doorway = (Doorway)player.WhatInteractingWith!; doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime; doorway.OpenStartTime = 0; - player.ChangePlayerState(value, gameObj); - break; + return player.ChangePlayerState(value, gameObj); case PlayerStateType.Addicted: if (value == PlayerStateType.Rescued) - player.ChangePlayerStateInOneThread(value, gameObj); + return player.ChangePlayerStateInOneThread(value, gameObj); else - player.ChangePlayerState(value, gameObj); - break; + return player.ChangePlayerState(value, gameObj); case PlayerStateType.Rescued: if (value == PlayerStateType.Addicted) - player.ChangePlayerStateInOneThread(value, gameObj); + return player.ChangePlayerStateInOneThread(value, gameObj); else - player.ChangePlayerState(value, gameObj); - break; + return player.ChangePlayerState(value, gameObj); default: - player.ChangePlayerState(value, gameObj); - break; + if (player.NoHp()) return -1; + return player.ChangePlayerState(value, gameObj); } } } @@ -75,7 +74,7 @@ namespace Gaming Thread.Sleep(Math.Max(newPlayer.CD, GameData.checkInterval)); long lastTime = Environment.TickCount64; new FrameRateTaskExecutor( - loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting, + loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved, loopToDo: () => { long nowTime = Environment.TickCount64; @@ -133,7 +132,7 @@ namespace Gaming } } new FrameRateTaskExecutor( - loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting, + loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved, loopToDo: () => { gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); @@ -246,7 +245,7 @@ namespace Gaming } } SetPlayerState(player, PlayerStateType.Addicted); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread (() => { @@ -254,7 +253,7 @@ namespace Gaming Debugger.Output(player, " is addicted "); #endif new FrameRateTaskExecutor( - () => threadNum == player.ThreadNum && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming, + () => threadNum == player.StateNum && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming, () => { player.GamingAddiction += (player.PlayerState == PlayerStateType.Addicted) ? GameData.frameDuration : 0; @@ -276,28 +275,28 @@ namespace Gaming { IsBackground = true }.Start(); } - public bool BeStunned(Character player, int time) + public long BeStunned(Character player, int time) { - if (player.PlayerState == PlayerStateType.Stunned || player.NoHp() || player.CharacterType == CharacterType.Robot) return false; + if (player.CharacterType == CharacterType.Robot) return -1; + long threadNum = SetPlayerState(player, PlayerStateType.Stunned); + if (threadNum == -1) return -1; new Thread (() => { - SetPlayerState(player, PlayerStateType.Stunned); - long threadNum = player.ThreadNum; Thread.Sleep(time); - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) SetPlayerState(player); } ) { IsBackground = true }.Start(); - return true; + return threadNum; } public bool TryBeAwed(Student character, Bullet bullet) { if (character.CanBeAwed()) { - if (BeStunned(character, GameData.basicStunnedTimeOfStudent)) + if (BeStunned(character, GameData.basicStunnedTimeOfStudent) > 0) bullet.Parent!.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.basicStunnedTimeOfStudent)); return true; } @@ -373,14 +372,14 @@ namespace Gaming if (time <= 0) return false; if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false; SetPlayerState(player, PlayerStateType.Swinging); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread (() => { Thread.Sleep(time); - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) { SetPlayerState(player); } diff --git a/logic/Gaming/PropManager.cs b/logic/Gaming/PropManager.cs index 9bf9714..2a971a9 100644 --- a/logic/Gaming/PropManager.cs +++ b/logic/Gaming/PropManager.cs @@ -21,7 +21,7 @@ namespace Gaming public void UseProp(Character player, PropType propType) { - if (player.IsResetting || player.CharacterType == CharacterType.Robot) + if (player.IsRemoved || player.CharacterType == CharacterType.Robot) return; Prop prop = player.UseProp(propType); switch (prop.GetPropType()) @@ -73,7 +73,7 @@ namespace Gaming /// public bool PickProp(Character player, PropType propType = PropType.Null) { - if (player.IsResetting) + if (player.IsRemoved) return false; int indexing = player.IndexingOfAddProp(); if (indexing == GameData.maxNumOfPropInPropInventory) @@ -118,7 +118,7 @@ namespace Gaming public void ThrowProp(Character player, PropType propType) { - if (!gameMap.Timer.IsGaming || player.IsResetting) + if (!gameMap.Timer.IsGaming || player.IsRemoved) return; Prop prop = player.UseProp(propType); if (prop.GetPropType() == PropType.Null) diff --git a/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs b/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs index a6d0a86..b04aa8e 100644 --- a/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs +++ b/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs @@ -188,7 +188,7 @@ namespace Gaming { if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange) { - if (characterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl)) + if (characterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl) > 0) player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl)); } } @@ -219,7 +219,7 @@ namespace Gaming || character.PlayerState == PlayerStateType.UsingSkill || character.PlayerState == PlayerStateType.LockingOrOpeningTheDoor || character.PlayerState == PlayerStateType.ClimbingThroughWindows) && gameMap.CanSee(player, character)) { - if (characterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP))) + if (characterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)) > 0) player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP))); break; } @@ -335,7 +335,7 @@ namespace Gaming startSkill(); activeSkill.IsBeingUsed = true; new FrameRateTaskExecutor( - () => !player.IsResetting, + () => !player.IsRemoved, () => { player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration); @@ -355,7 +355,7 @@ namespace Gaming Debugger.Output(player, "return to normal."); new FrameRateTaskExecutor( - loopCondition: () => player.TimeUntilActiveSkillAvailable[activeSkillType] > 0 && !player.IsResetting, + loopCondition: () => player.TimeUntilActiveSkillAvailable[activeSkillType] > 0 && !player.IsRemoved, loopToDo: () => { player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration); diff --git a/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs b/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs index b39a792..399624e 100644 --- a/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs +++ b/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs @@ -22,7 +22,7 @@ namespace Gaming // 被动技能开局时就释放,持续到游戏结束 { new FrameRateTaskExecutor ( - () => gameMap.Timer.IsGaming && !player.IsResetting, + () => gameMap.Timer.IsGaming && !player.IsRemoved, () => { if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation += learningDegree * GameData.frameDuration; diff --git a/logic/Preparation/Interface/ICharacter.cs b/logic/Preparation/Interface/ICharacter.cs index 3af523d..a112f5e 100644 --- a/logic/Preparation/Interface/ICharacter.cs +++ b/logic/Preparation/Interface/ICharacter.cs @@ -14,7 +14,6 @@ namespace Preparation.Interface public BulletType BulletOfPlayer { get; set; } public CharacterType CharacterType { get; } public int UpdateBulletNum(int time); - public long ThreadNum { get; } public bool IsGhost(); } diff --git a/logic/Preparation/Interface/IMoveable.cs b/logic/Preparation/Interface/IMoveable.cs index a6c74e4..cd9cb15 100644 --- a/logic/Preparation/Interface/IMoveable.cs +++ b/logic/Preparation/Interface/IMoveable.cs @@ -5,11 +5,12 @@ namespace Preparation.Interface { public interface IMoveable : IGameObj { - object MoveLock { get; } + object ActionLock { get; } public int MoveSpeed { get; } public bool IsMoving { get; set; } - public bool IsResetting { get; } // reviving - public bool IsAvailable { get; } + public bool IsRemoved { get; } + public bool IsAvailableForMove { get; } + public long StateNum { get; } public long MovingSetPos(XY moveVec); public void ReSetCanMove(bool value); public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞