fix: 🔒 refactor the moveEngine and edit 游戏机制与平衡性调整更新草案.md
tags/v0.1.0
| @@ -309,7 +309,7 @@ $$ | |||||
| - 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。 | - 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。 | ||||
| ### 人物 | ### 人物 | ||||
| - 眩晕状态中的玩家不能再次被眩晕 | |||||
| - 眩晕状态中的玩家不能再次被眩晕(除非是ShowTime) | |||||
| ### 初始状态 | ### 初始状态 | ||||
| - 初赛玩家出生点固定且一定为空地 | - 初赛玩家出生点固定且一定为空地 | ||||
| @@ -342,6 +342,7 @@ $$ | |||||
| - 开锁门进度中断后清空 | - 开锁门进度中断后清空 | ||||
| ### 窗 | ### 窗 | ||||
| - 由于窗户被占用导致翻窗失败会使先前行动停止 | |||||
| - 翻越窗户是一种交互行为,翻窗一共有两个过程 | - 翻越窗户是一种交互行为,翻窗一共有两个过程 | ||||
| - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 | - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 | ||||
| - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 | - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 | ||||
| @@ -1,5 +1,5 @@ | |||||
| # 游戏机制与平衡性调整更新草案 | # 游戏机制与平衡性调整更新草案 | ||||
| v1.5 | |||||
| v1.6 | |||||
| ## 说明 | ## 说明 | ||||
| - 该草案尚未完全确定,请大家不要过分依靠该文档进行修改自己的代码 | - 该草案尚未完全确定,请大家不要过分依靠该文档进行修改自己的代码 | ||||
| @@ -21,9 +21,9 @@ v1.5 | |||||
| - 未攻击至目标时的后摇改为1200ms | - 未攻击至目标时的后摇改为1200ms | ||||
| - 增强为“可以攻击未写完的作业” | - 增强为“可以攻击未写完的作业” | ||||
| - 增强为“可以攻击使门被打开(可以重新被锁上)” | - 增强为“可以攻击使门被打开(可以重新被锁上)” | ||||
| - 改为“当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上90°,180° ,270° 发出3个小炸弹” | |||||
| - 小炸弹JumpyDumpty | - 小炸弹JumpyDumpty | ||||
| - 小炸弹不受道具增益影响 | - 小炸弹不受道具增益影响 | ||||
| - 修改为“小炸弹与自己无碰撞体积” | |||||
| - strike(新增) | - strike(新增) | ||||
| - 可以攻击未写完的作业 | - 可以攻击未写完的作业 | ||||
| @@ -105,3 +105,14 @@ v1.5 | |||||
| - 普通攻击改为strike | - 普通攻击改为strike | ||||
| - Klee | - Klee | ||||
| - 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙) | - 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙) | ||||
| - 主动技能SparksNSplash(新增): | |||||
| - CD:45s, 持续时间:10s | |||||
| - 技能使用瞬间,对于输入的额外参数PlayerID代表的角色,距离最近的本已停止运动的小炸弹开始追踪该角色(每50ms向该角色直线移动) | |||||
| - 主动技能 蹦蹦炸弹 JumpyBomb | |||||
| - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上0°,45°,90°,135°,180°,225°,270°,315° 发出8个小炸弹 | |||||
| - Idol | |||||
| 主动技能ShowTime改为 | |||||
| "持续时间内 | |||||
| - 使警戒范围外的学生眩晕并每**500ms**发送向自己移动**500ms**的指令(速度为学生本应速度*二者距离/警戒范围) | |||||
| - 对于视野范围(不是可视区域)内的学生每**500ms**加**1500**的沉迷度 | |||||
| - 捣蛋鬼变为0.8倍速" | |||||
| @@ -21,11 +21,14 @@ | |||||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | - docs:更新了 游戏机制与平衡性调整更新草案.pdf | ||||
| - change:更改了地图的文件路径 | - change:更改了地图的文件路径 | ||||
| # 最新更新 | |||||
| # 5月10日更新 | |||||
| - fix:修复JumpyDumpty的初始位置错误的问题 | - fix:修复JumpyDumpty的初始位置错误的问题 | ||||
| - fix:修正和重新说明攻击距离 | - fix:修正和重新说明攻击距离 | ||||
| - **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2** | - **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2** | ||||
| - hotfix:修复小炸弹初始化类型错误的问题 | - hotfix:修复小炸弹初始化类型错误的问题 | ||||
| - remove:去除了“实际上唤醒或勉励不同的人是有效的” | - remove:去除了“实际上唤醒或勉励不同的人是有效的” | ||||
| - **重复发出同一类型的交互指令和移动指令是无效的** | - **重复发出同一类型的交互指令和移动指令是无效的** | ||||
| - feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`** | |||||
| - feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`** | |||||
| # 最新更新 | |||||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | |||||
| @@ -1,6 +1,5 @@ | |||||
| using Preparation.Interface; | using Preparation.Interface; | ||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| using System; | |||||
| namespace GameClass.GameObj | namespace GameClass.GameObj | ||||
| { | { | ||||
| @@ -27,7 +26,6 @@ namespace GameClass.GameObj | |||||
| public bool HasSpear => hasSpear; | public bool HasSpear => hasSpear; | ||||
| /// <summary> | /// <summary> | ||||
| /// 与THUAI4不同的一个攻击判定方案,通过这个函数判断爆炸时能否伤害到target | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="target">被尝试攻击者</param> | /// <param name="target">被尝试攻击者</param> | ||||
| /// <returns>是否可以攻击到</returns> | /// <returns>是否可以攻击到</returns> | ||||
| @@ -36,7 +34,7 @@ namespace GameClass.GameObj | |||||
| public override bool IgnoreCollideExecutor(IGameObj targetObj) | public override bool IgnoreCollideExecutor(IGameObj targetObj) | ||||
| { | { | ||||
| if (targetObj == Parent && CanMove) return true; | |||||
| if (targetObj == Parent) return true; | |||||
| if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet) | if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet) | ||||
| return true; | return true; | ||||
| return false; | return false; | ||||
| @@ -1,10 +1,5 @@ | |||||
| using Preparation.Interface; | using Preparation.Interface; | ||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Numerics; | |||||
| using System.Runtime.InteropServices; | |||||
| using System.Threading; | |||||
| namespace GameClass.GameObj | namespace GameClass.GameObj | ||||
| { | { | ||||
| @@ -324,7 +324,17 @@ namespace GameClass.GameObj | |||||
| return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped | return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped | ||||
| && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued | && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued | ||||
| && playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack | && playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack | ||||
| && playerState != PlayerStateType.ClimbingThroughWindows && playerState != PlayerStateType.Stunned); | |||||
| && playerState != PlayerStateType.ClimbingThroughWindows | |||||
| && playerState != PlayerStateType.Stunned && playerState != PlayerStateType.Charmed); | |||||
| } | |||||
| } | |||||
| public bool CanPinDown() | |||||
| { | |||||
| lock (actionLock) | |||||
| { | |||||
| return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped | |||||
| && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued | |||||
| && playerState != PlayerStateType.Stunned && playerState != PlayerStateType.Charmed); | |||||
| } | } | ||||
| } | } | ||||
| public bool InteractingWithMapWithoutMoving() | public bool InteractingWithMapWithoutMoving() | ||||
| @@ -345,8 +355,9 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| lock (actionLock) | lock (actionLock) | ||||
| return !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped | return !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped | ||||
| || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued | |||||
| || playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned | |||||
| || playerState == PlayerStateType.Addicted | |||||
| || playerState == PlayerStateType.Rescued || playerState == PlayerStateType.Treated | |||||
| || playerState == PlayerStateType.Stunned || playerState == PlayerStateType.Charmed | |||||
| || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | ||||
| } | } | ||||
| private GameObj? whatInteractingWith = null; | private GameObj? whatInteractingWith = null; | ||||
| @@ -354,28 +365,24 @@ namespace GameClass.GameObj | |||||
| public long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | public long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | ||||
| { | { | ||||
| 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; | |||||
| } | |||||
| //只能被SetPlayerState引用 | |||||
| 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 long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | public long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | ||||
| { | { | ||||
| 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; | |||||
| } | |||||
| //只能被SetPlayerState引用 | |||||
| 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 long SetPlayerStateNaturally() | public long SetPlayerStateNaturally() | ||||
| @@ -1,5 +1,7 @@ | |||||
| using Preparation.Interface; | using Preparation.Interface; | ||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| using System.Numerics; | |||||
| using System; | |||||
| namespace GameClass.GameObj | namespace GameClass.GameObj | ||||
| { | { | ||||
| @@ -25,15 +27,66 @@ namespace GameClass.GameObj | |||||
| return false; | return false; | ||||
| } | } | ||||
| private XY stage = new(0, 0); | |||||
| public XY Stage | |||||
| { | |||||
| get | |||||
| { | |||||
| GameObjReaderWriterLock.EnterReadLock(); | |||||
| try | |||||
| { | |||||
| return stage; | |||||
| } | |||||
| finally { GameObjReaderWriterLock.ExitReadLock(); } | |||||
| } | |||||
| } | |||||
| private Character? whoIsClimbing = null; | private Character? whoIsClimbing = null; | ||||
| public Character? WhoIsClimbing | public Character? WhoIsClimbing | ||||
| { | { | ||||
| get => whoIsClimbing; | |||||
| set | |||||
| get | |||||
| { | |||||
| GameObjReaderWriterLock.EnterReadLock(); | |||||
| try | |||||
| { | |||||
| return whoIsClimbing; | |||||
| } | |||||
| finally { GameObjReaderWriterLock.ExitReadLock(); } | |||||
| } | |||||
| } | |||||
| public bool TryToClimb(Character character) | |||||
| { | |||||
| GameObjReaderWriterLock.EnterWriteLock(); | |||||
| try | |||||
| { | |||||
| if (whoIsClimbing == null) | |||||
| { | |||||
| stage = new(0, 0); | |||||
| whoIsClimbing = character; | |||||
| return true; | |||||
| } | |||||
| else return false; | |||||
| } | |||||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||||
| } | |||||
| public void FinishClimbing() | |||||
| { | |||||
| GameObjReaderWriterLock.EnterWriteLock(); | |||||
| try | |||||
| { | |||||
| whoIsClimbing = null; | |||||
| } | |||||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||||
| } | |||||
| public void Enter2Stage(XY xy) | |||||
| { | |||||
| GameObjReaderWriterLock.EnterWriteLock(); | |||||
| try | |||||
| { | { | ||||
| lock (gameObjLock) | |||||
| whoIsClimbing = value; | |||||
| stage = xy; | |||||
| } | } | ||||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -8,8 +8,23 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| protected readonly object actionLock = new(); | protected readonly object actionLock = new(); | ||||
| public object ActionLock => actionLock; | public object ActionLock => actionLock; | ||||
| //player.actionLock>其他.actionLock | |||||
| private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); | private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); | ||||
| public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; | public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; | ||||
| private Semaphore threadNum = new(1, 1); | |||||
| public Semaphore ThreadNum | |||||
| { | |||||
| get | |||||
| { | |||||
| return threadNum; | |||||
| } | |||||
| set | |||||
| { | |||||
| threadNum = value; | |||||
| } | |||||
| } | |||||
| protected long stateNum = 0; | protected long stateNum = 0; | ||||
| public long StateNum | public long StateNum | ||||
| { | { | ||||
| @@ -61,14 +76,27 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| // 移动,改变坐标 | // 移动,改变坐标 | ||||
| public long MovingSetPos(XY moveVec) | |||||
| public long MovingSetPos(XY moveVec, long stateNo) | |||||
| { | { | ||||
| if (moveVec.x != 0 || moveVec.y != 0) | if (moveVec.x != 0 || moveVec.y != 0) | ||||
| lock (actionLock) | |||||
| { | |||||
| moveReaderWriterLock.EnterReadLock(); | |||||
| try | |||||
| { | { | ||||
| facingDirection = moveVec; | |||||
| this.position += moveVec; | |||||
| lock (actionLock) | |||||
| { | |||||
| if (!canMove || isRemoved) return -1; | |||||
| if (stateNo != stateNum) return -1; | |||||
| facingDirection = moveVec; | |||||
| this.position += moveVec; | |||||
| } | |||||
| } | } | ||||
| finally | |||||
| { | |||||
| moveReaderWriterLock.ExitReadLock(); | |||||
| } | |||||
| } | |||||
| return moveVec * moveVec; | return moveVec * moveVec; | ||||
| } | } | ||||
| @@ -20,18 +20,6 @@ namespace GameEngine | |||||
| private readonly ITimer gameTimer; | private readonly ITimer gameTimer; | ||||
| private readonly Action<IMoveable> EndMove; | private readonly Action<IMoveable> EndMove; | ||||
| public readonly uint[,] ProtoGameMap; | |||||
| public PlaceType GetPlaceType(XY Position) | |||||
| { | |||||
| try | |||||
| { | |||||
| return (PlaceType)ProtoGameMap[Position.x / GameData.numOfPosGridPerCell, Position.y / GameData.numOfPosGridPerCell]; | |||||
| } | |||||
| catch | |||||
| { | |||||
| return PlaceType.Null; | |||||
| } | |||||
| } | |||||
| public IGameObj? CheckCollision(IMoveable obj, XY Pos) | public IGameObj? CheckCollision(IMoveable obj, XY Pos) | ||||
| { | { | ||||
| @@ -52,7 +40,6 @@ namespace GameEngine | |||||
| Action<IMoveable> EndMove | Action<IMoveable> EndMove | ||||
| ) | ) | ||||
| { | { | ||||
| this.ProtoGameMap = gameMap.ProtoGameMap; | |||||
| this.gameTimer = gameMap.Timer; | this.gameTimer = gameMap.Timer; | ||||
| this.EndMove = EndMove; | this.EndMove = EndMove; | ||||
| this.OnCollision = OnCollision; | this.OnCollision = OnCollision; | ||||
| @@ -64,21 +51,57 @@ namespace GameEngine | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="obj">移动物体,默认obj.Rigid为true</param> | /// <param name="obj">移动物体,默认obj.Rigid为true</param> | ||||
| /// <param name="moveVec">移动的位移向量</param> | /// <param name="moveVec">移动的位移向量</param> | ||||
| private void MoveMax(IMoveable obj, XY moveVec) | |||||
| private bool MoveMax(IMoveable obj, XY moveVec, long stateNum) | |||||
| { | { | ||||
| /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ | /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ | ||||
| XY nextPos = obj.Position + moveVec; | XY nextPos = obj.Position + moveVec; | ||||
| double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); | double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); | ||||
| maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); | maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); | ||||
| obj.MovingSetPos(new XY(moveVec, maxLen)); | |||||
| return (obj.MovingSetPos(new XY(moveVec, maxLen), stateNum)) >= 0; | |||||
| } | |||||
| private bool LoopDo(IMoveable obj, double direction, ref double deltaLen, long stateNum) | |||||
| { | |||||
| double moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; | |||||
| XY res = new(direction, moveVecLength); | |||||
| // 越界情况处理:如果越界,则与越界方块碰撞 | |||||
| bool flag; // 循环标志 | |||||
| do | |||||
| { | |||||
| flag = false; | |||||
| IGameObj? collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res); | |||||
| if (collisionObj == null) | |||||
| break; | |||||
| switch (OnCollision(obj, collisionObj, res)) | |||||
| { | |||||
| case AfterCollision.ContinueCheck: | |||||
| flag = true; | |||||
| break; | |||||
| case AfterCollision.Destroyed: | |||||
| Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | |||||
| return false; | |||||
| case AfterCollision.MoveMax: | |||||
| if (!MoveMax(obj, res, stateNum)) return false; | |||||
| moveVecLength = 0; | |||||
| res = new XY(direction, moveVecLength); | |||||
| break; | |||||
| } | |||||
| } while (flag); | |||||
| long moveL = obj.MovingSetPos(res, stateNum); | |||||
| if (moveL == -1) return false; | |||||
| deltaLen = deltaLen + moveVecLength - Math.Sqrt(moveL); | |||||
| return true; | |||||
| } | } | ||||
| public void MoveObj(IMoveable obj, int moveTime, double direction, long threadNum) | |||||
| public void MoveObj(IMoveable obj, int moveTime, double direction, long stateNum) | |||||
| { | { | ||||
| if (!gameTimer.IsGaming) return; | if (!gameTimer.IsGaming) return; | ||||
| lock (obj.ActionLock) | lock (obj.ActionLock) | ||||
| { | { | ||||
| if (!obj.IsAvailableForMove) return; | |||||
| if (!obj.IsAvailableForMove) { EndMove(obj); return; } | |||||
| obj.IsMoving = true; | obj.IsMoving = true; | ||||
| } | } | ||||
| new Thread | new Thread | ||||
| @@ -87,9 +110,9 @@ namespace GameEngine | |||||
| { | { | ||||
| double moveVecLength = 0.0; | double moveVecLength = 0.0; | ||||
| XY res = new(direction, moveVecLength); | XY res = new(direction, moveVecLength); | ||||
| double deltaLen = 0; // 转向,并用deltaLen存储行走的误差 | |||||
| double deltaLen = (double)0.0; // 转向,并用deltaLen存储行走的误差 | |||||
| IGameObj? collisionObj = null; | IGameObj? collisionObj = null; | ||||
| bool isDestroyed = false; | |||||
| bool isEnded = false; | |||||
| bool flag; // 循环标志 | bool flag; // 循环标志 | ||||
| do | do | ||||
| @@ -106,34 +129,82 @@ namespace GameEngine | |||||
| break; | break; | ||||
| case AfterCollision.Destroyed: | case AfterCollision.Destroyed: | ||||
| Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | ||||
| isDestroyed = true; | |||||
| isEnded = true; | |||||
| break; | break; | ||||
| case AfterCollision.MoveMax: | case AfterCollision.MoveMax: | ||||
| break; | break; | ||||
| } | } | ||||
| } while (flag); | } while (flag); | ||||
| if (!isDestroyed) | |||||
| if (isEnded) | |||||
| { | { | ||||
| new FrameRateTaskExecutor<int>( | |||||
| () => gameTimer.IsGaming && obj.CanMove && !obj.IsRemoved && obj.IsMoving, | |||||
| () => | |||||
| obj.IsMoving = false; | |||||
| EndMove(obj); | |||||
| return; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (moveTime >= GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond) | |||||
| { | |||||
| Thread.Sleep(GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond); | |||||
| new FrameRateTaskExecutor<int>( | |||||
| () => gameTimer.IsGaming, | |||||
| () => | |||||
| { | |||||
| if (obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) | |||||
| return !(isEnded = true); | |||||
| return !(isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum)); | |||||
| }, | |||||
| GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond, | |||||
| () => | |||||
| { | |||||
| return 0; | |||||
| }, | |||||
| maxTotalDuration: moveTime - GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond | |||||
| ) | |||||
| { | { | ||||
| moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; | |||||
| res = new XY(direction, moveVecLength); | |||||
| //对人特殊处理 | |||||
| if (threadNum > 0 && obj.StateNum != threadNum) return false; | |||||
| // 越界情况处理:如果越界,则与越界方块碰撞 | |||||
| bool flag; // 循环标志 | |||||
| do | |||||
| AllowTimeExceed = true, | |||||
| MaxTolerantTimeExceedCount = ulong.MaxValue, | |||||
| TimeExceedAction = b => | |||||
| { | { | ||||
| flag = false; | |||||
| collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res); | |||||
| if (collisionObj == null) | |||||
| break; | |||||
| if (b) | |||||
| Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!"); | |||||
| #if DEBUG | |||||
| else | |||||
| { | |||||
| Console.WriteLine("Debug info: Object moving time exceed for once."); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| }.Start(); | |||||
| if (!isEnded && obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) | |||||
| isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum); | |||||
| } | |||||
| if (isEnded) | |||||
| { | |||||
| obj.IsMoving = false; | |||||
| EndMove(obj); | |||||
| return; | |||||
| } | |||||
| if (obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) | |||||
| { | |||||
| int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond); | |||||
| if (leftTime > 0) | |||||
| { | |||||
| Thread.Sleep(leftTime); // 多移动的在这里补回来 | |||||
| } | |||||
| do | |||||
| { | |||||
| flag = false; | |||||
| moveVecLength = (double)deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; | |||||
| res = new XY(direction, moveVecLength); | |||||
| if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) | |||||
| { | |||||
| obj.MovingSetPos(res, stateNum); | |||||
| } | |||||
| else | |||||
| { | |||||
| switch (OnCollision(obj, collisionObj, res)) | switch (OnCollision(obj, collisionObj, res)) | ||||
| { | { | ||||
| case AfterCollision.ContinueCheck: | case AfterCollision.ContinueCheck: | ||||
| @@ -141,87 +212,19 @@ namespace GameEngine | |||||
| break; | break; | ||||
| case AfterCollision.Destroyed: | case AfterCollision.Destroyed: | ||||
| Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | ||||
| isDestroyed = true; | |||||
| return false; | |||||
| isEnded = true; | |||||
| break; | |||||
| case AfterCollision.MoveMax: | case AfterCollision.MoveMax: | ||||
| if (threadNum == 0 || obj.StateNum == threadNum) | |||||
| MoveMax(obj, res); | |||||
| MoveMax(obj, res, stateNum); | |||||
| moveVecLength = 0; | moveVecLength = 0; | ||||
| res = new XY(direction, moveVecLength); | res = new XY(direction, moveVecLength); | ||||
| break; | break; | ||||
| } | } | ||||
| } while (flag); | |||||
| if (threadNum == 0 || obj.StateNum == threadNum) | |||||
| deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res)); | |||||
| return true; | |||||
| }, | |||||
| GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond, | |||||
| () => | |||||
| { | |||||
| int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond); | |||||
| bool flag; | |||||
| do | |||||
| { | |||||
| flag = false; | |||||
| if (!isDestroyed) | |||||
| { | |||||
| moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; | |||||
| res = new XY(direction, moveVecLength); | |||||
| if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) | |||||
| { | |||||
| if (threadNum == 0 || obj.StateNum == threadNum) | |||||
| obj.MovingSetPos(res); | |||||
| } | |||||
| else | |||||
| { | |||||
| switch (OnCollision(obj, collisionObj, res)) | |||||
| { | |||||
| case AfterCollision.ContinueCheck: | |||||
| flag = true; | |||||
| break; | |||||
| case AfterCollision.Destroyed: | |||||
| Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | |||||
| isDestroyed = true; | |||||
| break; | |||||
| case AfterCollision.MoveMax: | |||||
| if (threadNum == 0 || obj.StateNum == threadNum) | |||||
| MoveMax(obj, res); | |||||
| moveVecLength = 0; | |||||
| res = new XY(direction, moveVecLength); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } while (flag); | |||||
| if (leftTime > 0 && obj.IsMoving) | |||||
| { | |||||
| Thread.Sleep(leftTime); // 多移动的在这里补回来 | |||||
| } | |||||
| lock (obj.ActionLock) | |||||
| obj.IsMoving = false; // 结束移动 | |||||
| EndMove(obj); | |||||
| return 0; | |||||
| }, | |||||
| maxTotalDuration: moveTime | |||||
| ) | |||||
| { | |||||
| AllowTimeExceed = true, | |||||
| MaxTolerantTimeExceedCount = ulong.MaxValue, | |||||
| TimeExceedAction = b => | |||||
| { | |||||
| if (b) | |||||
| Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!"); | |||||
| #if DEBUG | |||||
| else | |||||
| { | |||||
| Console.WriteLine("Debug info: Object moving time exceed for once."); | |||||
| } | } | ||||
| #endif | |||||
| } | |||||
| }.Start(); | |||||
| } while (flag); | |||||
| } | |||||
| obj.IsMoving = false; // 结束移动 | |||||
| EndMove(obj); | |||||
| } | } | ||||
| } | } | ||||
| ).Start(); | ).Start(); | ||||
| @@ -36,26 +36,59 @@ namespace Gaming | |||||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | ||||
| { | { | ||||
| if (moveTimeInMilliseconds < 5) return false; | if (moveTimeInMilliseconds < 5) return false; | ||||
| if (!playerToMove.Commandable()) return false; | |||||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, | |||||
| characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving)); | |||||
| long stateNum = characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving); | |||||
| if (stateNum == -1) return false; | |||||
| new Thread | |||||
| ( | |||||
| () => | |||||
| { | |||||
| playerToMove.ThreadNum.WaitOne(); | |||||
| if (stateNum != playerToMove.StateNum) | |||||
| playerToMove.ThreadNum.Release(); | |||||
| else | |||||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, stateNum); | |||||
| } | |||||
| ) | |||||
| { IsBackground = true }.Start(); | |||||
| return true; | return true; | ||||
| } | } | ||||
| public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | ||||
| { | { | ||||
| if (!playerToMove.Commandable() && playerToMove.PlayerState != PlayerStateType.Stunned) return false; | |||||
| characterManager.BeStunned(playerToMove, moveTimeInMilliseconds); | |||||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, playerToMove.StateNum); | |||||
| if (playerToMove.CharacterType == CharacterType.Robot) return false; | |||||
| long stateNum = characterManager.SetPlayerState(playerToMove, PlayerStateType.Charmed); | |||||
| if (stateNum == -1) return false; | |||||
| new Thread | |||||
| (() => | |||||
| { | |||||
| playerToMove.ThreadNum.WaitOne(); | |||||
| if (stateNum != playerToMove.StateNum) | |||||
| playerToMove.ThreadNum.Release(); | |||||
| else | |||||
| { | |||||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, playerToMove.StateNum); | |||||
| Thread.Sleep(moveTimeInMilliseconds); | |||||
| lock (playerToMove.ActionLock) | |||||
| { | |||||
| if (stateNum == playerToMove.StateNum) | |||||
| playerToMove.SetPlayerStateNaturally(); | |||||
| } | |||||
| } | |||||
| } | |||||
| ) | |||||
| { IsBackground = true }.Start(); | |||||
| return true; | return true; | ||||
| } | } | ||||
| public bool Stop(Character player) | public bool Stop(Character player) | ||||
| { | { | ||||
| if (player.Commandable()) | |||||
| lock (player.ActionLock) | |||||
| { | { | ||||
| characterManager.SetPlayerState(player); | |||||
| return true; | |||||
| if (player.Commandable()) | |||||
| { | |||||
| characterManager.SetPlayerState(player); | |||||
| return true; | |||||
| } | |||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -279,12 +312,11 @@ namespace Gaming | |||||
| } | } | ||||
| public bool ClimbingThroughWindow(Character player) | public bool ClimbingThroughWindow(Character player) | ||||
| { | { | ||||
| if (!player.Commandable()) | |||||
| return false; | |||||
| Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window); | Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window); | ||||
| if (windowForClimb == null) return false; | |||||
| if (windowForClimb == null || windowForClimb.WhoIsClimbing != null) | |||||
| return false; | |||||
| long stateNum = characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows, windowForClimb); | |||||
| if (stateNum == -1) return false; | |||||
| XY windowToPlayer = new( | XY windowToPlayer = new( | ||||
| (Math.Abs(player.Position.x - windowForClimb.Position.x) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.x > windowForClimb.Position.x ? 1 : -1)) : 0, | (Math.Abs(player.Position.x - windowForClimb.Position.x) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.x > windowForClimb.Position.x ? 1 : -1)) : 0, | ||||
| @@ -296,59 +328,57 @@ namespace Gaming | |||||
| if (player.IsGhost() && !characterInWindow.IsGhost()) | if (player.IsGhost() && !characterInWindow.IsGhost()) | ||||
| characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position)); | characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position)); | ||||
| return false; | return false; | ||||
| }*/ | |||||
| } | |||||
| //Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer); | |||||
| // gameMap.Add(addWall); | |||||
| Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer); | |||||
| gameMap.Add(addWall);*/ | |||||
| characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows); | |||||
| long threadNum = player.StateNum; | |||||
| windowForClimb.WhoIsClimbing = player; | |||||
| new Thread | new Thread | ||||
| ( | |||||
| () => | |||||
| { | |||||
| new FrameRateTaskExecutor<int>( | |||||
| loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, | |||||
| loopToDo: () => { }, | |||||
| timeInterval: GameData.frameDuration, | |||||
| finallyReturn: () => 0, | |||||
| maxTotalDuration: (int)((windowToPlayer + windowForClimb.Position - player.Position).Length() * 1000 / player.MoveSpeed) | |||||
| ) | |||||
| .Start(); | |||||
| if (player.PlayerState != PlayerStateType.ClimbingThroughWindows) | |||||
| { | |||||
| windowForClimb.WhoIsClimbing = null; | |||||
| return; | |||||
| } | |||||
| player.ReSetPos(windowToPlayer + windowForClimb.Position); | |||||
| player.MoveSpeed = player.SpeedOfClimbingThroughWindows; | |||||
| moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle(), threadNum); | |||||
| new FrameRateTaskExecutor<int>( | |||||
| loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, | |||||
| loopToDo: () => | |||||
| ( | |||||
| () => | |||||
| { | { | ||||
| }, | |||||
| timeInterval: GameData.frameDuration, | |||||
| finallyReturn: () => 0, | |||||
| maxTotalDuration: (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed) | |||||
| ) | |||||
| .Start(); | |||||
| XY PosJumpOff = windowForClimb.Position - 2 * windowToPlayer; | |||||
| player.ReSetPos(PosJumpOff); | |||||
| player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); | |||||
| windowForClimb.WhoIsClimbing = null; | |||||
| // gameMap.Remove(addWall); | |||||
| if (threadNum == player.StateNum) | |||||
| { | |||||
| characterManager.SetPlayerState(player); | |||||
| } | |||||
| } | |||||
| ) | |||||
| player.ThreadNum.WaitOne(); | |||||
| if (stateNum != player.StateNum) | |||||
| { | |||||
| player.ThreadNum.Release(); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (!windowForClimb.TryToClimb(player)) | |||||
| { | |||||
| player.ThreadNum.Release(); | |||||
| player.SetPlayerStateNaturally(); | |||||
| } | |||||
| else | |||||
| { | |||||
| Thread.Sleep((int)((windowToPlayer + windowForClimb.Position - player.Position).Length() * 1000 / player.MoveSpeed)); | |||||
| lock (player.ActionLock) | |||||
| { | |||||
| if (player.StateNum != stateNum) return; | |||||
| player.ReSetPos(windowToPlayer + windowForClimb.Position); | |||||
| windowForClimb.Enter2Stage(windowForClimb.Position - 2 * windowToPlayer); | |||||
| } | |||||
| player.MoveSpeed = player.SpeedOfClimbingThroughWindows; | |||||
| moveEngine.MoveObj(player, GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2, (-1 * windowToPlayer).Angle(), stateNum); | |||||
| Thread.Sleep(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2); | |||||
| player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); | |||||
| lock (player.ActionLock) | |||||
| { | |||||
| if (stateNum == player.StateNum) | |||||
| { | |||||
| characterManager.SetPlayerState(player); | |||||
| windowForClimb.FinishClimbing(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| ) | |||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| return true; | return true; | ||||
| @@ -400,7 +430,6 @@ namespace Gaming | |||||
| timeInterval: GameData.frameDuration, | timeInterval: GameData.frameDuration, | ||||
| finallyReturn: () => 0 | finallyReturn: () => 0 | ||||
| ) | ) | ||||
| .Start(); | .Start(); | ||||
| if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | ||||
| { | { | ||||
| @@ -462,6 +491,7 @@ namespace Gaming | |||||
| }, | }, | ||||
| EndMove: obj => | EndMove: obj => | ||||
| { | { | ||||
| obj.ThreadNum.Release(); | |||||
| // Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64); | // Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64); | ||||
| } | } | ||||
| ); | ); | ||||
| @@ -195,7 +195,9 @@ namespace Gaming | |||||
| { | { | ||||
| Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); | Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); | ||||
| gameMap.Add(bullet); | gameMap.Add(bullet); | ||||
| moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms | moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms | ||||
| if (bullet.CastTime > 0) | if (bullet.CastTime > 0) | ||||
| { | { | ||||
| characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); | characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); | ||||
| @@ -25,28 +25,52 @@ namespace Gaming | |||||
| if (nowPlayerState == value) return -1; | if (nowPlayerState == value) return -1; | ||||
| switch (nowPlayerState) | switch (nowPlayerState) | ||||
| { | { | ||||
| case PlayerStateType.OpeningTheChest: | |||||
| if (player.NoHp()) return -1; | |||||
| ((Chest)player.WhatInteractingWith!).StopOpen(); | |||||
| 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; | |||||
| return player.ChangePlayerState(value, gameObj); | |||||
| case PlayerStateType.Escaped: | |||||
| case PlayerStateType.Deceased: | |||||
| return -1; | |||||
| case PlayerStateType.Addicted: | case PlayerStateType.Addicted: | ||||
| if (value == PlayerStateType.Rescued) | if (value == PlayerStateType.Rescued) | ||||
| return player.ChangePlayerStateInOneThread(value, gameObj); | return player.ChangePlayerStateInOneThread(value, gameObj); | ||||
| else | |||||
| else if (value == PlayerStateType.Null) | |||||
| return player.ChangePlayerState(value, gameObj); | return player.ChangePlayerState(value, gameObj); | ||||
| else return -1; | |||||
| case PlayerStateType.Rescued: | case PlayerStateType.Rescued: | ||||
| if (value == PlayerStateType.Addicted) | if (value == PlayerStateType.Addicted) | ||||
| return player.ChangePlayerStateInOneThread(value, gameObj); | return player.ChangePlayerStateInOneThread(value, gameObj); | ||||
| else | |||||
| else if (value == PlayerStateType.Null) | |||||
| return player.ChangePlayerState(value, gameObj); | |||||
| else return -1; | |||||
| case PlayerStateType.TryingToAttack: | |||||
| case PlayerStateType.Stunned: | |||||
| case PlayerStateType.Charmed: | |||||
| case PlayerStateType.Swinging: | |||||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||||
| return player.ChangePlayerState(value, gameObj); | |||||
| else return -1; | |||||
| case PlayerStateType.ClimbingThroughWindows: | |||||
| if (value != PlayerStateType.Moving) | |||||
| { | |||||
| Window window = (Window)player.WhatInteractingWith!; | |||||
| window.FinishClimbing(); | |||||
| if (window.Stage.x == 0) | |||||
| player.ThreadNum.Release(); | |||||
| else player.ReSetPos(window.Stage); | |||||
| return player.ChangePlayerState(value, gameObj); | return player.ChangePlayerState(value, gameObj); | ||||
| } | |||||
| else return -1; | |||||
| case PlayerStateType.OpeningTheChest: | |||||
| ((Chest)player.WhatInteractingWith!).StopOpen(); | |||||
| return player.ChangePlayerState(value, gameObj); | |||||
| case PlayerStateType.OpeningTheDoorway: | |||||
| Doorway doorway = (Doorway)player.WhatInteractingWith!; | |||||
| doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime; | |||||
| doorway.OpenStartTime = 0; | |||||
| return player.ChangePlayerState(value, gameObj); | |||||
| default: | default: | ||||
| if (player.NoHp()) return -1; | |||||
| return player.ChangePlayerState(value, gameObj); | return player.ChangePlayerState(value, gameObj); | ||||
| } | } | ||||
| } | } | ||||
| @@ -163,7 +187,7 @@ namespace Gaming | |||||
| newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position)); | newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position)); | ||||
| else newPlayer.AddBgm(BgmType.GhostIsComing, 0); | else newPlayer.AddBgm(BgmType.GhostIsComing, 0); | ||||
| } | } | ||||
| if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && !newPlayer.NoHp() && newPlayer.PlayerState != PlayerStateType.Stunned && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange) | |||||
| if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && newPlayer.CanPinDown() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange) | |||||
| { | { | ||||
| TimePinningDown += GameData.checkInterval; | TimePinningDown += GameData.checkInterval; | ||||
| newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded); | newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded); | ||||
| @@ -54,7 +54,7 @@ namespace Gaming | |||||
| else player.AddAp(GameData.PropDuration); | else player.AddAp(GameData.PropDuration); | ||||
| break; | break; | ||||
| case PropType.RecoveryFromDizziness: | case PropType.RecoveryFromDizziness: | ||||
| if (player.PlayerState == PlayerStateType.Stunned) | |||||
| if (player.PlayerState == PlayerStateType.Stunned || player.PlayerState == PlayerStateType.Charmed) | |||||
| { | { | ||||
| player.AddScore(GameData.ScorePropRecoverFromDizziness); | player.AddScore(GameData.ScorePropRecoverFromDizziness); | ||||
| player.SetPlayerStateNaturally(); | player.SetPlayerStateNaturally(); | ||||
| @@ -1,4 +1,5 @@ | |||||
| using System; | using System; | ||||
| using System.Threading; | |||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| namespace Preparation.Interface | namespace Preparation.Interface | ||||
| @@ -11,7 +12,8 @@ namespace Preparation.Interface | |||||
| public bool IsRemoved { get; } | public bool IsRemoved { get; } | ||||
| public bool IsAvailableForMove { get; } | public bool IsAvailableForMove { get; } | ||||
| public long StateNum { get; } | public long StateNum { get; } | ||||
| public long MovingSetPos(XY moveVec); | |||||
| public Semaphore ThreadNum { get; set; } | |||||
| public long MovingSetPos(XY moveVec, long stateNum); | |||||
| public void ReSetCanMove(bool value); | public void ReSetCanMove(bool value); | ||||
| public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 | public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 | ||||
| { | { | ||||
| @@ -24,6 +24,7 @@ namespace Preparation.Utility | |||||
| ClimbingThroughWindows = 15, | ClimbingThroughWindows = 15, | ||||
| UsingSkill = 16, | UsingSkill = 16, | ||||
| OpeningTheDoorway = 17, | OpeningTheDoorway = 17, | ||||
| Charmed = 18, | |||||
| } | } | ||||
| public enum GameObjType | public enum GameObjType | ||||
| { | { | ||||
| @@ -6,13 +6,13 @@ namespace Preparation.Utility | |||||
| public static class GameData | public static class GameData | ||||
| { | { | ||||
| #region 基本常数 | #region 基本常数 | ||||
| public const int numOfStepPerSecond = 20; // 每秒行走的步数 | |||||
| public const int numOfStepPerSecond = 100; // 每秒行走的步数 | |||||
| public const int tolerancesLength = 3; | public const int tolerancesLength = 3; | ||||
| public const int adjustLength = 3; | public const int adjustLength = 3; | ||||
| public const int frameDuration = 50; // 每帧时长 | public const int frameDuration = 50; // 每帧时长 | ||||
| public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | |||||
| public const int checkInterval = 10; | |||||
| public const long gameDuration = 600000; // 游戏时长600000ms = 10min | public const long gameDuration = 600000; // 游戏时长600000ms = 10min | ||||
| public const int LimitOfStopAndMove = 15; | public const int LimitOfStopAndMove = 15; | ||||
| @@ -116,6 +116,7 @@ namespace Preparation.Utility | |||||
| case Preparation.Utility.PlayerStateType.Rescuing: | case Preparation.Utility.PlayerStateType.Rescuing: | ||||
| return PlayerState.Rescuing; | return PlayerState.Rescuing; | ||||
| case Preparation.Utility.PlayerStateType.Stunned: | case Preparation.Utility.PlayerStateType.Stunned: | ||||
| case Preparation.Utility.PlayerStateType.Charmed: | |||||
| return PlayerState.Stunned; | return PlayerState.Stunned; | ||||
| case Preparation.Utility.PlayerStateType.Swinging: | case Preparation.Utility.PlayerStateType.Swinging: | ||||
| return PlayerState.Swinging; | return PlayerState.Swinging; | ||||