| @@ -30,5 +30,6 @@ | |||
| - **重复发出同一类型的交互指令和移动指令是无效的** | |||
| - feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`** | |||
| # 最新更新 | |||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | |||
| # 5月13日更新 | |||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | |||
| - feat:更新了下载器 | |||
| @@ -2,11 +2,16 @@ | |||
| using Preparation.Utility; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 | |||
| { | |||
| private readonly ReaderWriterLockSlim hpReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim HPReadWriterLock => hpReaderWriterLock; | |||
| #region 装弹、攻击相关的基本属性及方法 | |||
| /// <summary> | |||
| /// 装弹冷却 | |||
| @@ -231,21 +236,65 @@ namespace GameClass.GameObj | |||
| } | |||
| #endregion | |||
| #region 血量相关的基本属性及方法 | |||
| public int MaxHp { get; protected set; } // 最大血量 | |||
| private int maxHp; | |||
| public int MaxHp | |||
| { | |||
| get | |||
| { | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return maxHp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| protected set | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| maxHp = value; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| } // 最大血量 | |||
| protected int hp; | |||
| public int HP | |||
| { | |||
| get => hp; | |||
| get | |||
| { | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return hp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| set | |||
| { | |||
| if (value > 0) | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| if (value > 0) | |||
| { | |||
| hp = value <= MaxHp ? value : MaxHp; | |||
| } | |||
| else | |||
| lock (gameObjLock) | |||
| } | |||
| else | |||
| hp = 0; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| } | |||
| @@ -256,47 +305,62 @@ namespace GameClass.GameObj | |||
| /// <returns>减操作是否成功</returns> | |||
| public int TrySubHp(int sub) | |||
| { | |||
| int previousHp = hp; | |||
| lock (gameObjLock) | |||
| hp = hp <= sub ? 0 : hp - sub; | |||
| Debugger.Output(this, " hp has subed to: " + hp.ToString()); | |||
| return previousHp - hp; | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| int previousHp = hp; | |||
| if (hp <= sub) | |||
| { | |||
| hp = 0; | |||
| return hp; | |||
| } | |||
| else | |||
| { | |||
| hp -= sub; | |||
| return sub; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| private double vampire = 0; // 回血率:0-1之间 | |||
| public double Vampire | |||
| { | |||
| get => vampire; | |||
| set | |||
| get | |||
| { | |||
| if (value > 1) | |||
| lock (gameObjLock) | |||
| vampire = 1; | |||
| else if (value < 0) | |||
| lock (gameObjLock) | |||
| vampire = 0; | |||
| else | |||
| lock (gameObjLock) | |||
| vampire = value; | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return vampire; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| } | |||
| private double oriVampire = 0; | |||
| public double OriVampire | |||
| { | |||
| get => oriVampire; | |||
| set | |||
| { | |||
| if (value > 1) | |||
| lock (gameObjLock) | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| if (value > 1) | |||
| vampire = 1; | |||
| else if (value < 0) | |||
| lock (gameObjLock) | |||
| else if (value < 0) | |||
| vampire = 0; | |||
| else | |||
| lock (gameObjLock) | |||
| else | |||
| vampire = value; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| } | |||
| private double oriVampire = 0; | |||
| public double OriVampire { get; protected set; } | |||
| #endregion | |||
| #region 状态相关的基本属性与方法 | |||
| private PlayerStateType playerState = PlayerStateType.Null; | |||
| @@ -363,7 +427,7 @@ namespace GameClass.GameObj | |||
| private GameObj? whatInteractingWith = null; | |||
| public GameObj? WhatInteractingWith => whatInteractingWith; | |||
| public long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| private long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| //只能被SetPlayerState引用 | |||
| whatInteractingWith = gameObj; | |||
| @@ -374,7 +438,7 @@ namespace GameClass.GameObj | |||
| return ++stateNum; | |||
| } | |||
| public long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| private long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| //只能被SetPlayerState引用 | |||
| whatInteractingWith = gameObj; | |||
| @@ -385,6 +449,74 @@ namespace GameClass.GameObj | |||
| return stateNum; | |||
| } | |||
| public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| PlayerStateType nowPlayerState = PlayerState; | |||
| if (nowPlayerState == value) return -1; | |||
| switch (nowPlayerState) | |||
| { | |||
| case PlayerStateType.Escaped: | |||
| case PlayerStateType.Deceased: | |||
| return -1; | |||
| case PlayerStateType.Addicted: | |||
| if (value == PlayerStateType.Rescued) | |||
| return ChangePlayerStateInOneThread(value, gameObj); | |||
| else if (value == PlayerStateType.Null) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Rescued: | |||
| if (value == PlayerStateType.Addicted) | |||
| return ChangePlayerStateInOneThread(value, gameObj); | |||
| else if (value == PlayerStateType.Null) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.TryingToAttack: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Stunned: | |||
| case PlayerStateType.Charmed: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows && value != PlayerStateType.Swinging) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Swinging: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||
| { | |||
| ThreadNum.Release(); | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.ClimbingThroughWindows: | |||
| if (value != PlayerStateType.Moving) | |||
| { | |||
| Window window = (Window)WhatInteractingWith!; | |||
| window.FinishClimbing(); | |||
| if (window.Stage.x == 0) | |||
| ThreadNum.Release(); | |||
| else ReSetPos(window.Stage); | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.OpeningTheChest: | |||
| ((Chest)WhatInteractingWith!).StopOpen(); | |||
| return ChangePlayerState(value, gameObj); | |||
| case PlayerStateType.OpeningTheDoorway: | |||
| Doorway doorway = (Doorway)WhatInteractingWith!; | |||
| doorway.StopOpenning(); | |||
| return ChangePlayerState(value, gameObj); | |||
| default: | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| } | |||
| } | |||
| public long SetPlayerStateNaturally() | |||
| { | |||
| lock (actionLock) | |||
| @@ -398,20 +530,20 @@ namespace GameClass.GameObj | |||
| public void RemoveFromGame(PlayerStateType playerStateType) | |||
| { | |||
| MoveReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| lock (actionLock) | |||
| { | |||
| lock (actionLock) | |||
| MoveReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| playerState = playerStateType; | |||
| canMove = false; | |||
| isRemoved = true; | |||
| position = GameData.PosWhoDie; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| MoveReaderWriterLock.ExitWriteLock(); | |||
| finally | |||
| { | |||
| MoveReaderWriterLock.ExitWriteLock(); | |||
| } | |||
| playerState = playerStateType; | |||
| position = GameData.PosWhoDie; | |||
| } | |||
| } | |||
| #endregion | |||
| @@ -1,5 +1,4 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using Preparation.Utility; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -1,11 +1,12 @@ | |||
| using Preparation.Utility; | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 箱子 | |||
| /// </summary> | |||
| public class Chest : Immovable | |||
| public class Chest : Immovable, IChest | |||
| { | |||
| public Chest(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest) | |||
| @@ -1,12 +1,14 @@ | |||
| using Preparation.Interface; | |||
| using Google.Protobuf.WellKnownTypes; | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 出口 | |||
| /// </summary> | |||
| public class Doorway : Immovable | |||
| public class Doorway : Immovable, IDoorway | |||
| { | |||
| public Doorway(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Doorway) | |||
| @@ -25,7 +27,11 @@ namespace GameClass.GameObj | |||
| private bool powerSupply = false; | |||
| public bool PowerSupply | |||
| { | |||
| get => powerSupply; | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| return powerSupply; | |||
| } | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| @@ -36,29 +42,58 @@ namespace GameClass.GameObj | |||
| private int openStartTime = 0; | |||
| public int OpenStartTime | |||
| { | |||
| get => openStartTime; | |||
| set | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| openStartTime = value; | |||
| return openStartTime; | |||
| } | |||
| } | |||
| public bool TryToOpen() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (!powerSupply || openStartTime > 0) return false; | |||
| openStartTime = Environment.TickCount; | |||
| return true; | |||
| } | |||
| } | |||
| public bool StopOpenning() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (openDegree + Environment.TickCount - openStartTime >= GameData.degreeOfOpenedDoorway) | |||
| { | |||
| openDegree = GameData.degreeOfOpenedDoorway; | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| openDegree = openDegree + Environment.TickCount - openStartTime; | |||
| openStartTime = 0; | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| public void FinishOpenning() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| openDegree = GameData.degreeOfOpenedDoorway; | |||
| } | |||
| } | |||
| private int openDegree = 0; | |||
| public int OpenDegree | |||
| { | |||
| get => openDegree; | |||
| set | |||
| get | |||
| { | |||
| if (value > 0) | |||
| lock (gameObjLock) | |||
| openDegree = (value < GameData.degreeOfOpenedDoorway) ? value : GameData.degreeOfOpenedDoorway; | |||
| else | |||
| lock (gameObjLock) | |||
| openDegree = 0; | |||
| lock (gameObjLock) | |||
| return openDegree; | |||
| } | |||
| } | |||
| public bool IsOpen() => (openDegree == GameData.degreeOfOpenedDoorway); | |||
| public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedDoorway); | |||
| } | |||
| } | |||
| @@ -1,14 +1,12 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System.Numerics; | |||
| using System; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 窗 | |||
| /// </summary> | |||
| public class Window : Immovable | |||
| public class Window : Immovable, IWindow | |||
| { | |||
| public Window(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Window) | |||
| @@ -32,12 +30,8 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| GameObjReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| return stage; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitReadLock(); } | |||
| } | |||
| } | |||
| @@ -46,47 +40,31 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| GameObjReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| return whoIsClimbing; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitReadLock(); } | |||
| } | |||
| } | |||
| public bool TryToClimb(Character character) | |||
| public bool TryToClimb(ICharacter character) | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| if (whoIsClimbing == null) | |||
| { | |||
| stage = new(0, 0); | |||
| whoIsClimbing = character; | |||
| whoIsClimbing = (Character)character; | |||
| return true; | |||
| } | |||
| else return false; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| public void FinishClimbing() | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| whoIsClimbing = null; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| public void Enter2Stage(XY xy) | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| stage = xy; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| } | |||
| } | |||
| @@ -11,19 +11,9 @@ namespace GameClass.GameObj | |||
| //player.actionLock>其他.actionLock | |||
| private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; | |||
| //规定moveReaderWriterLock<actionLock | |||
| private Semaphore threadNum = new(1, 1); | |||
| public Semaphore ThreadNum | |||
| { | |||
| get | |||
| { | |||
| return threadNum; | |||
| } | |||
| set | |||
| { | |||
| threadNum = value; | |||
| } | |||
| } | |||
| public Semaphore ThreadNum { get; } = new(1, 1); | |||
| protected long stateNum = 0; | |||
| public long StateNum | |||
| @@ -38,7 +28,6 @@ namespace GameClass.GameObj | |||
| lock (actionLock) stateNum = value; | |||
| } | |||
| } | |||
| //规定moveReaderWriterLock>actionLock | |||
| public override XY Position | |||
| { | |||
| @@ -81,20 +70,20 @@ namespace GameClass.GameObj | |||
| if (moveVec.x != 0 || moveVec.y != 0) | |||
| { | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| lock (actionLock) | |||
| { | |||
| lock (actionLock) | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| if (!canMove || isRemoved) return -1; | |||
| if (stateNo != stateNum) return -1; | |||
| facingDirection = moveVec; | |||
| this.position += moveVec; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| if (stateNo != stateNum) return -1; | |||
| facingDirection = moveVec; | |||
| this.position += moveVec; | |||
| } | |||
| } | |||
| return moveVec * moveVec; | |||
| @@ -129,10 +118,7 @@ namespace GameClass.GameObj | |||
| moveReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| canMove = value; | |||
| } | |||
| canMove = value; | |||
| } | |||
| finally | |||
| { | |||
| @@ -161,52 +147,29 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| lock (actionLock) | |||
| { | |||
| lock (actionLock) | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return !isMoving && canMove && !isRemoved; | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| protected int moveSpeed; | |||
| protected long moveSpeed; | |||
| /// <summary> | |||
| /// 移动速度 | |||
| /// </summary> | |||
| public int MoveSpeed | |||
| public long MoveSpeed | |||
| { | |||
| get | |||
| { | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return moveSpeed; | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| set | |||
| { | |||
| moveReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| moveSpeed = value; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| get => Interlocked.Read(ref moveSpeed); | |||
| set => Interlocked.Exchange(ref moveSpeed, value); | |||
| } | |||
| /// <summary> | |||
| /// 原初移动速度 | |||
| @@ -36,7 +36,7 @@ namespace Gaming | |||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (moveTimeInMilliseconds < 5) return false; | |||
| long stateNum = characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving); | |||
| long stateNum = playerToMove.SetPlayerState(PlayerStateType.Moving); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| ( | |||
| @@ -56,7 +56,7 @@ namespace Gaming | |||
| public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (playerToMove.CharacterType == CharacterType.Robot) return false; | |||
| long stateNum = characterManager.SetPlayerState(playerToMove, PlayerStateType.Charmed); | |||
| long stateNum = playerToMove.SetPlayerState(PlayerStateType.Charmed); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| (() => | |||
| @@ -86,7 +86,7 @@ namespace Gaming | |||
| { | |||
| if (player.Commandable()) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| return true; | |||
| } | |||
| } | |||
| @@ -103,7 +103,7 @@ namespace Gaming | |||
| return false; | |||
| ++generatorForFix.NumOfFixing; | |||
| characterManager.SetPlayerState(player, PlayerStateType.Fixing); | |||
| player.SetPlayerState(PlayerStateType.Fixing); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| ( | |||
| @@ -117,7 +117,7 @@ namespace Gaming | |||
| if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) | |||
| gameMap.NumOfRepairedGenerators++; | |||
| if (generatorForFix.DegreeOfRepair == GameData.degreeOfFixedGenerator) | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| @@ -134,24 +134,26 @@ namespace Gaming | |||
| public bool OpenDoorway(Student player) | |||
| { | |||
| if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorway) | |||
| if (!(player.Commandable())) | |||
| return false; | |||
| Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); | |||
| if (doorwayToOpen == null || doorwayToOpen.OpenStartTime > 0 || !doorwayToOpen.PowerSupply) | |||
| return false; | |||
| if (doorwayToOpen == null) return false; | |||
| characterManager.SetPlayerState(player, PlayerStateType.OpeningTheDoorway, doorwayToOpen); | |||
| int startTime = doorwayToOpen.OpenStartTime = gameMap.Timer.nowTime(); | |||
| long stateNum = player.SetPlayerState(PlayerStateType.OpeningTheDoorway, doorwayToOpen); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| //player.ThreadNum.WaitOne(); | |||
| Thread.Sleep(GameData.degreeOfOpenedDoorway - doorwayToOpen.OpenDegree); | |||
| if (doorwayToOpen.OpenStartTime == startTime) | |||
| lock (player.ActionLock) | |||
| { | |||
| doorwayToOpen.OpenDegree = GameData.degreeOfOpenedDoorway; | |||
| player.SetPlayerStateNaturally(); | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| player.SetPlayerStateNaturally(); | |||
| doorwayToOpen.FinishOpenning(); | |||
| } | |||
| } | |||
| } | |||
| @@ -203,8 +205,8 @@ namespace Gaming | |||
| ( | |||
| () => | |||
| { | |||
| characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated); | |||
| characterManager.SetPlayerState(player, PlayerStateType.Treating); | |||
| playerTreated.SetPlayerState(PlayerStateType.Treated); | |||
| player.SetPlayerState(PlayerStateType.Treating); | |||
| long threadNum = player.StateNum; | |||
| new FrameRateTaskExecutor<int>( | |||
| @@ -212,15 +214,15 @@ namespace Gaming | |||
| loopToDo: () => | |||
| { | |||
| if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) | |||
| characterManager.SetPlayerState(playerTreated); | |||
| playerTreated.SetPlayerState(); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| if (threadNum == player.StateNum) characterManager.SetPlayerState(player); | |||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated); | |||
| if (threadNum == player.StateNum) player.SetPlayerState(); | |||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) playerTreated.SetPlayerState(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| @@ -235,8 +237,8 @@ namespace Gaming | |||
| } | |||
| if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position)) | |||
| return false; | |||
| characterManager.SetPlayerState(player, PlayerStateType.Rescuing); | |||
| characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued); | |||
| player.SetPlayerState(PlayerStateType.Rescuing); | |||
| playerRescued.SetPlayerState(PlayerStateType.Rescued); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| @@ -259,14 +261,14 @@ namespace Gaming | |||
| { | |||
| if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue) | |||
| { | |||
| characterManager.SetPlayerState(playerRescued); | |||
| playerRescued.SetPlayerState(); | |||
| playerRescued.HP = playerRescued.MaxHp / 2; | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| } | |||
| else | |||
| characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted); | |||
| playerRescued.SetPlayerState(PlayerStateType.Addicted); | |||
| } | |||
| if (threadNum == player.StateNum) characterManager.SetPlayerState(player); | |||
| if (threadNum == player.StateNum) player.SetPlayerState(); | |||
| playerRescued.TimeOfRescue = 0; | |||
| } | |||
| ) | |||
| @@ -283,7 +285,7 @@ namespace Gaming | |||
| if (chestToOpen == null || chestToOpen.OpenStartTime > 0) | |||
| return false; | |||
| characterManager.SetPlayerState(player, PlayerStateType.OpeningTheChest, chestToOpen); | |||
| player.SetPlayerState(PlayerStateType.OpeningTheChest, chestToOpen); | |||
| int startTime = gameMap.Timer.nowTime(); | |||
| chestToOpen.Open(startTime, player); | |||
| new Thread | |||
| @@ -315,7 +317,7 @@ namespace Gaming | |||
| Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window); | |||
| if (windowForClimb == null) return false; | |||
| long stateNum = characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows, windowForClimb); | |||
| long stateNum = player.SetPlayerState(PlayerStateType.ClimbingThroughWindows, windowForClimb); | |||
| if (stateNum == -1) return false; | |||
| XY windowToPlayer = new( | |||
| @@ -371,7 +373,7 @@ namespace Gaming | |||
| { | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| windowForClimb.FinishClimbing(); | |||
| } | |||
| } | |||
| @@ -414,7 +416,7 @@ namespace Gaming | |||
| } | |||
| if (!flag) return false; | |||
| characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor); | |||
| player.SetPlayerState(PlayerStateType.LockingOrOpeningTheDoor); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| ( | |||
| @@ -436,7 +438,7 @@ namespace Gaming | |||
| doorToLock.IsOpen = (!doorToLock.IsOpen); | |||
| } | |||
| if (threadNum == player.StateNum) | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| doorToLock.OpenOrLockDegree = 0; | |||
| } | |||
| @@ -200,7 +200,7 @@ namespace Gaming | |||
| if (bullet.CastTime > 0) | |||
| { | |||
| characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); | |||
| player.SetPlayerState(PlayerStateType.TryingToAttack); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| @@ -221,7 +221,7 @@ namespace Gaming | |||
| { | |||
| if (threadNum == player.StateNum) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| } | |||
| else TryRemoveBullet(bullet); | |||
| } | |||
| @@ -17,65 +17,6 @@ namespace Gaming | |||
| this.gameMap = gameMap; | |||
| } | |||
| public long SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| lock (player.ActionLock) | |||
| { | |||
| PlayerStateType nowPlayerState = player.PlayerState; | |||
| if (nowPlayerState == value) return -1; | |||
| switch (nowPlayerState) | |||
| { | |||
| case PlayerStateType.Escaped: | |||
| case PlayerStateType.Deceased: | |||
| return -1; | |||
| case PlayerStateType.Addicted: | |||
| if (value == PlayerStateType.Rescued) | |||
| return player.ChangePlayerStateInOneThread(value, gameObj); | |||
| 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) | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| public Character? AddPlayer(XY pos, int teamID, int playerID, CharacterType characterType, Character? parent = null) | |||
| { | |||
| Character newPlayer; | |||
| @@ -268,7 +209,7 @@ namespace Gaming | |||
| return; | |||
| } | |||
| } | |||
| SetPlayerState(player, PlayerStateType.Addicted); | |||
| player.SetPlayerState(PlayerStateType.Addicted); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| (() => | |||
| @@ -302,14 +243,14 @@ namespace Gaming | |||
| public long BeStunned(Character player, int time) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot) return -1; | |||
| long threadNum = SetPlayerState(player, PlayerStateType.Stunned); | |||
| long threadNum = player.SetPlayerState(PlayerStateType.Stunned); | |||
| if (threadNum == -1) return -1; | |||
| new Thread | |||
| (() => | |||
| { | |||
| Thread.Sleep(time); | |||
| if (threadNum == player.StateNum) | |||
| SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| @@ -339,7 +280,6 @@ namespace Gaming | |||
| #if DEBUG | |||
| Debugger.Output(student, "is being shot!"); | |||
| #endif | |||
| if (student.NoHp()) return; // 原来已经死了 | |||
| if (!bullet.Parent!.IsGhost()) return; | |||
| if (student.CharacterType == CharacterType.StraightAStudent) | |||
| @@ -347,6 +287,9 @@ namespace Gaming | |||
| ((WriteAnswers)student.FindIActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation = 0; | |||
| } | |||
| student.SetDegreeOfTreatment0(); | |||
| if (student.NoHp()) return; // 原来已经死了 | |||
| #if DEBUG | |||
| Debugger.Output(bullet, " 's AP is " + bullet.AP.ToString()); | |||
| #endif | |||
| @@ -394,18 +337,22 @@ namespace Gaming | |||
| public bool BackSwing(Character player, int time) | |||
| { | |||
| 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.StateNum; | |||
| long stateNum = player.SetPlayerState(PlayerStateType.Swinging); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| (() => | |||
| { | |||
| player.ThreadNum.WaitOne(); | |||
| Thread.Sleep(time); | |||
| if (threadNum == player.StateNum) | |||
| lock (player.ActionLock) | |||
| { | |||
| SetPlayerState(player); | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| player.ThreadNum.Release(); | |||
| player.SetPlayerStateNaturally(); | |||
| } | |||
| } | |||
| } | |||
| ) | |||
| @@ -111,8 +111,8 @@ namespace Gaming | |||
| Debugger.Output(player, "use robot!"); | |||
| IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.UseRobot); | |||
| activeSkill.IsBeingUsed = (activeSkill.IsBeingUsed) ? false : true; | |||
| if (activeSkill.IsBeingUsed) characterManager.SetPlayerState(player, PlayerStateType.UsingSkill); | |||
| else characterManager.SetPlayerState(player); | |||
| if (activeSkill.IsBeingUsed) player.SetPlayerState(PlayerStateType.UsingSkill); | |||
| else player.SetPlayerState(); | |||
| return true; | |||
| } | |||
| @@ -247,7 +247,7 @@ namespace Gaming | |||
| { | |||
| if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) | |||
| { | |||
| characterManager.SetPlayerState(character); | |||
| character.SetPlayerState(); | |||
| character.HP = GameData.RemainHpWhenAddLife; | |||
| ((Student)character).TimeOfRescue = 0; | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| @@ -0,0 +1,9 @@ | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface IChest : IGameObj | |||
| { | |||
| public void StopOpen(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface IDoorway : IGameObj | |||
| { | |||
| public int OpenStartTime { get; } | |||
| public int OpenDegree { get; } | |||
| public bool StopOpenning(); | |||
| public bool TryToOpen(); | |||
| } | |||
| } | |||
| @@ -12,7 +12,7 @@ namespace Preparation.Interface | |||
| public bool IsRemoved { get; } | |||
| public bool IsAvailableForMove { get; } | |||
| public long StateNum { get; } | |||
| public Semaphore ThreadNum { get; set; } | |||
| public Semaphore ThreadNum { get; } | |||
| public long MovingSetPos(XY moveVec, long stateNum); | |||
| public void ReSetCanMove(bool value); | |||
| public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 | |||
| @@ -0,0 +1,12 @@ | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface IWindow : IGameObj | |||
| { | |||
| public void Enter2Stage(XY xy); | |||
| public bool TryToClimb(ICharacter character); | |||
| public XY Stage { get; } | |||
| public void FinishClimbing(); | |||
| } | |||
| } | |||