diff --git a/docs/GameRules.md b/docs/GameRules.md
index 9f6f16c..42a06f0 100644
--- a/docs/GameRules.md
+++ b/docs/GameRules.md
@@ -342,6 +342,7 @@ $$
- 开锁门进度中断后清空
### 窗
+- 由于窗户被占用导致翻窗失败会使先前行动停止
- 翻越窗户是一种交互行为,翻窗一共有两个过程
- 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置
- 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心
diff --git a/docs/游戏机制与平衡性调整更新草案.md b/docs/游戏机制与平衡性调整更新草案.md
index 2159323..8e4418e 100644
--- a/docs/游戏机制与平衡性调整更新草案.md
+++ b/docs/游戏机制与平衡性调整更新草案.md
@@ -1,5 +1,5 @@
# 游戏机制与平衡性调整更新草案
-v1.5
+v1.6
## 说明
- 该草案尚未完全确定,请大家不要过分依靠该文档进行修改自己的代码
@@ -106,6 +106,12 @@ v1.5
- 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙)
- 主动技能SparksNSplash(新增):
- CD:45s, 持续时间:10s
- - 技能使用瞬间,离输入的额外参数PlayerID代表的角色最近的本来停止运动的小炸弹开始追踪该角色(每50ms向该角色直线移动)
+ - 技能使用瞬间,对于输入的额外参数PlayerID代表的角色,距离最近的本已停止运动的小炸弹开始追踪该角色(每50ms向该角色直线移动)
- 主动技能 蹦蹦炸弹 JumpyBomb
- - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上0°,45°,90°,135°,180°,225°,270°,315° 发出2个小炸弹
\ No newline at end of file
+ - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上0°,45°,90°,135°,180°,225°,270°,315° 发出8个小炸弹
+- Idol
+ 主动技能ShowTime改为
+ "持续时间内
+ - 使警戒范围外的学生眩晕并每**500ms**发送向自己移动**500ms**的指令(速度为学生本应速度*二者距离/警戒范围)
+ - 对于视野范围(不是可视区域)内的学生每**500ms**加**1500**的沉迷度
+ - 捣蛋鬼变为0.8倍速"
\ No newline at end of file
diff --git a/docs/版本更新说明.md b/docs/版本更新说明.md
index 70f91f2..61f3801 100644
--- a/docs/版本更新说明.md
+++ b/docs/版本更新说明.md
@@ -21,11 +21,14 @@
- docs:更新了 游戏机制与平衡性调整更新草案.pdf
- change:更改了地图的文件路径
-# 最新更新
+# 5月10日更新
- fix:修复JumpyDumpty的初始位置错误的问题
- fix:修正和重新说明攻击距离
- **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2**
- hotfix:修复小炸弹初始化类型错误的问题
- remove:去除了“实际上唤醒或勉励不同的人是有效的”
- **重复发出同一类型的交互指令和移动指令是无效的**
-- feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`**
\ No newline at end of file
+- feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`**
+
+# 最新更新
+- docs:更新了 游戏机制与平衡性调整更新草案.pdf
\ No newline at end of file
diff --git a/logic/GameClass/GameObj/Character/Character.cs b/logic/GameClass/GameObj/Character/Character.cs
index 2ecee40..1fe012e 100644
--- a/logic/GameClass/GameObj/Character/Character.cs
+++ b/logic/GameClass/GameObj/Character/Character.cs
@@ -324,7 +324,17 @@ namespace GameClass.GameObj
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);
+ && 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()
@@ -345,8 +355,9 @@ namespace GameClass.GameObj
{
lock (actionLock)
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);
}
private GameObj? whatInteractingWith = null;
@@ -354,28 +365,24 @@ namespace GameClass.GameObj
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)
{
- 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()
diff --git a/logic/GameClass/GameObj/Map/Map.cs b/logic/GameClass/GameObj/Map/Map.cs
index 3a3869f..7e35cff 100644
--- a/logic/GameClass/GameObj/Map/Map.cs
+++ b/logic/GameClass/GameObj/Map/Map.cs
@@ -484,7 +484,7 @@ namespace GameClass.GameObj
}
case (uint)PlaceType.Window:
{
- Add(new Window(GameData.GetCellCenterPos(i, j)));
+ Add(new Window(GameData.GetCellCenterPos(i, j), mapResource[i - 1, j] == (uint)PlaceType.Wall));
break;
}
case (uint)PlaceType.BirthPoint1:
diff --git a/logic/GameClass/GameObj/Map/Window.cs b/logic/GameClass/GameObj/Map/Window.cs
index 864e58f..e9f2d0e 100644
--- a/logic/GameClass/GameObj/Map/Window.cs
+++ b/logic/GameClass/GameObj/Map/Window.cs
@@ -1,5 +1,7 @@
using Preparation.Interface;
using Preparation.Utility;
+using System.Numerics;
+using System;
namespace GameClass.GameObj
{
@@ -8,9 +10,10 @@ namespace GameClass.GameObj
///
public class Window : Immovable
{
- public Window(XY initPos) :
+ public Window(XY initPos, bool xIsWall) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Window)
{
+ this.xIsWall = xIsWall;
}
public override bool IsRigid => true;
public override ShapeType Shape => ShapeType.Square;
@@ -25,15 +28,68 @@ namespace GameClass.GameObj
return false;
}
+ public readonly bool xIsWall;
+
+ private XY stage = new(0, 0);
+ public XY Stage
+ {
+ get
+ {
+ GameObjReaderWriterLock.EnterReadLock();
+ try
+ {
+ return stage;
+ }
+ finally { GameObjReaderWriterLock.ExitReadLock(); }
+ }
+ }
+
private Character? whoIsClimbing = null;
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(); }
}
}
}
diff --git a/logic/GameClass/GameObj/Moveable.cs b/logic/GameClass/GameObj/Moveable.cs
index f31d0ed..2c78489 100644
--- a/logic/GameClass/GameObj/Moveable.cs
+++ b/logic/GameClass/GameObj/Moveable.cs
@@ -8,8 +8,23 @@ namespace GameClass.GameObj
{
protected readonly object actionLock = new();
public object ActionLock => actionLock;
+ //player.actionLock>其他.actionLock
private readonly ReaderWriterLockSlim moveReaderWriterLock = new();
public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock;
+
+ private Semaphore threadNum = new(1, 1);
+ public Semaphore ThreadNum
+ {
+ get
+ {
+ return threadNum;
+ }
+ set
+ {
+ threadNum = value;
+ }
+ }
+
protected long stateNum = 0;
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)
- 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;
}
diff --git a/logic/GameEngine/MoveEngine.cs b/logic/GameEngine/MoveEngine.cs
index fd84018..8370df5 100644
--- a/logic/GameEngine/MoveEngine.cs
+++ b/logic/GameEngine/MoveEngine.cs
@@ -64,21 +64,57 @@ namespace GameEngine
///
/// 移动物体,默认obj.Rigid为true
/// 移动的位移向量
- private void MoveMax(IMoveable obj, XY moveVec)
+ private bool MoveMax(IMoveable obj, XY moveVec, long stateNum)
{
/*由于四周是墙,所以人物永远不可能与越界方块碰撞*/
XY nextPos = obj.Position + moveVec;
double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec);
maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond);
- obj.MovingSetPos(new XY(moveVec, maxLen));
+ return (obj.MovingSetPos(new XY(moveVec, maxLen), stateNum)) >= 0;
}
- public void MoveObj(IMoveable obj, int moveTime, double direction, long threadNum)
+ 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 stateNum)
{
if (!gameTimer.IsGaming) return;
lock (obj.ActionLock)
{
- if (!obj.IsAvailableForMove) return;
+ if (!obj.IsAvailableForMove) { obj.ThreadNum.Release(); return; }
obj.IsMoving = true;
}
new Thread
@@ -87,9 +123,9 @@ namespace GameEngine
{
double moveVecLength = 0.0;
XY res = new(direction, moveVecLength);
- double deltaLen = 0; // 转向,并用deltaLen存储行走的误差
+ double deltaLen = (double)0.0; // 转向,并用deltaLen存储行走的误差
IGameObj? collisionObj = null;
- bool isDestroyed = false;
+ bool isEnded = false;
bool flag; // 循环标志
do
@@ -106,34 +142,76 @@ namespace GameEngine
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
- isDestroyed = true;
+ isEnded = true;
break;
case AfterCollision.MoveMax:
break;
}
} while (flag);
- if (!isDestroyed)
+ if (isEnded)
+ {
+ obj.IsMoving = false;
+ obj.ThreadNum.Release();
+ EndMove(obj);
+ return;
+ }
+ else
{
- new FrameRateTaskExecutor(
- () => gameTimer.IsGaming && obj.CanMove && !obj.IsRemoved && obj.IsMoving,
- () =>
+ if (moveTime >= GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond)
+ {
+ Thread.Sleep(GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
+ new FrameRateTaskExecutor(
+ () => gameTimer.IsGaming && obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved,
+ () =>
+ {
+ 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);
+ 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 (threadNum > 0 && obj.StateNum != threadNum) return false;
+#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);
+ }
- // 越界情况处理:如果越界,则与越界方块碰撞
- bool flag; // 循环标志
- do
+ if (!isEnded && 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
{
- flag = false;
- collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res);
- if (collisionObj == null)
- break;
-
switch (OnCollision(obj, collisionObj, res))
{
case AfterCollision.ContinueCheck:
@@ -141,87 +219,20 @@ namespace GameEngine
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
- isDestroyed = true;
- return false;
+ isEnded = true;
+ break;
case AfterCollision.MoveMax:
- if (threadNum == 0 || obj.StateNum == threadNum)
- MoveMax(obj, res);
+ MoveMax(obj, res, stateNum);
moveVecLength = 0;
res = new XY(direction, moveVecLength);
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; // 结束移动
+ obj.ThreadNum.Release();
+ EndMove(obj);
}
}
).Start();
diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs
index 894cab4..64a00ac 100644
--- a/logic/Gaming/ActionManager.cs
+++ b/logic/Gaming/ActionManager.cs
@@ -36,26 +36,59 @@ namespace Gaming
public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection)
{
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;
}
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;
}
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;
}
@@ -279,12 +312,11 @@ namespace Gaming
}
public bool ClimbingThroughWindow(Character player)
{
- if (!player.Commandable())
- return false;
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(
(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())
characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position));
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 FrameRateTaskExecutor(
- 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(
- 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();
return true;
@@ -400,7 +430,6 @@ namespace Gaming
timeInterval: GameData.frameDuration,
finallyReturn: () => 0
)
-
.Start();
if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor)
{
diff --git a/logic/Gaming/AttackManager.cs b/logic/Gaming/AttackManager.cs
index 70aa202..a94a18d 100644
--- a/logic/Gaming/AttackManager.cs
+++ b/logic/Gaming/AttackManager.cs
@@ -47,7 +47,15 @@ namespace Gaming
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
+ new Thread
+ (
+ () =>
+ {
+ bullet.ThreadNum.WaitOne();
+ moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms
+ }
+ )
+ { IsBackground = true }.Start();
}
private void BombObj(Bullet bullet, GameObj objBeingShot)
@@ -195,7 +203,15 @@ namespace Gaming
{
Debugger.Output(bullet, "Attack in " + bullet.Position.ToString());
gameMap.Add(bullet);
- moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms
+ new Thread
+ (
+ () =>
+ {
+ bullet.ThreadNum.WaitOne();
+ moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms
+ }
+ )
+ { IsBackground = true }.Start();
if (bullet.CastTime > 0)
{
characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack);
diff --git a/logic/Gaming/CharacterManager .cs b/logic/Gaming/CharacterManager .cs
index 4c80b8b..c0f0f91 100644
--- a/logic/Gaming/CharacterManager .cs
+++ b/logic/Gaming/CharacterManager .cs
@@ -28,26 +28,48 @@ namespace Gaming
case PlayerStateType.Escaped:
case PlayerStateType.Deceased:
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);
+
case PlayerStateType.Addicted:
if (value == PlayerStateType.Rescued)
return player.ChangePlayerStateInOneThread(value, gameObj);
- else if (value==PlayerStateType.Null)
+ else if (value == PlayerStateType.Null)
return player.ChangePlayerState(value, gameObj);
else return -1;
case PlayerStateType.Rescued:
if (value == PlayerStateType.Addicted)
return player.ChangePlayerStateInOneThread(value, gameObj);
- else if(value==PlayerStateType.Null)
+ 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);
+ }
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:
return player.ChangePlayerState(value, gameObj);
}
@@ -165,7 +187,7 @@ namespace Gaming
newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position));
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;
newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded);
diff --git a/logic/Gaming/PropManager.cs b/logic/Gaming/PropManager.cs
index 2a971a9..5079a73 100644
--- a/logic/Gaming/PropManager.cs
+++ b/logic/Gaming/PropManager.cs
@@ -54,7 +54,7 @@ namespace Gaming
else player.AddAp(GameData.PropDuration);
break;
case PropType.RecoveryFromDizziness:
- if (player.PlayerState == PlayerStateType.Stunned)
+ if (player.PlayerState == PlayerStateType.Stunned || player.PlayerState == PlayerStateType.Charmed)
{
player.AddScore(GameData.ScorePropRecoverFromDizziness);
player.SetPlayerStateNaturally();
diff --git a/logic/Preparation/Interface/IMoveable.cs b/logic/Preparation/Interface/IMoveable.cs
index cd9cb15..01577c7 100644
--- a/logic/Preparation/Interface/IMoveable.cs
+++ b/logic/Preparation/Interface/IMoveable.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading;
using Preparation.Utility;
namespace Preparation.Interface
@@ -11,7 +12,8 @@ namespace Preparation.Interface
public bool IsRemoved { get; }
public bool IsAvailableForMove { 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 bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞
{
diff --git a/logic/Preparation/Utility/EnumType.cs b/logic/Preparation/Utility/EnumType.cs
index 9f426a3..5bd1032 100644
--- a/logic/Preparation/Utility/EnumType.cs
+++ b/logic/Preparation/Utility/EnumType.cs
@@ -24,6 +24,7 @@ namespace Preparation.Utility
ClimbingThroughWindows = 15,
UsingSkill = 16,
OpeningTheDoorway = 17,
+ Charmed = 18,
}
public enum GameObjType
{
diff --git a/logic/Preparation/Utility/GameData.cs b/logic/Preparation/Utility/GameData.cs
index 72c37b8..63f37d4 100644
--- a/logic/Preparation/Utility/GameData.cs
+++ b/logic/Preparation/Utility/GameData.cs
@@ -6,13 +6,13 @@ namespace Preparation.Utility
public static class GameData
{
#region 基本常数
- public const int numOfStepPerSecond = 20; // 每秒行走的步数
+ public const int numOfStepPerSecond = 100; // 每秒行走的步数
public const int tolerancesLength = 3;
public const int adjustLength = 3;
public const int frameDuration = 50; // 每帧时长
- public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长
+ public const int checkInterval = 10;
public const long gameDuration = 600000; // 游戏时长600000ms = 10min
public const int LimitOfStopAndMove = 15;
diff --git a/logic/Preparation/Utility/Transformation.cs b/logic/Preparation/Utility/Transformation.cs
index 22b3464..0a997be 100644
--- a/logic/Preparation/Utility/Transformation.cs
+++ b/logic/Preparation/Utility/Transformation.cs
@@ -116,6 +116,7 @@ namespace Preparation.Utility
case Preparation.Utility.PlayerStateType.Rescuing:
return PlayerState.Rescuing;
case Preparation.Utility.PlayerStateType.Stunned:
+ case Preparation.Utility.PlayerStateType.Charmed:
return PlayerState.Stunned;
case Preparation.Utility.PlayerStateType.Swinging:
return PlayerState.Swinging;