perf: 🔒 add a limit of call the Stop or Move fuction
tags/0.1.0
| @@ -118,7 +118,7 @@ enum TrickerType | |||
| ASSASSIN = 1; | |||
| KLEE = 2; | |||
| A_NOISY_PERSON = 3; | |||
| TRICKERTYPE4 = 4; | |||
| IDOL = 4; | |||
| } | |||
| // 游戏进行状态 | |||
| @@ -241,7 +241,7 @@ namespace Client | |||
| playerMsg.TrickerType = TrickerType.ANoisyPerson; | |||
| break; | |||
| case 4: | |||
| playerMsg.TrickerType = TrickerType._4; | |||
| playerMsg.TrickerType = TrickerType.Idol; | |||
| break; | |||
| case 0: | |||
| default: | |||
| @@ -49,8 +49,8 @@ namespace Client | |||
| case TrickerType.ANoisyPerson: | |||
| serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nANoisyPerson"; | |||
| break; | |||
| case TrickerType._4: | |||
| serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nTrickerType4"; | |||
| case TrickerType.Idol: | |||
| serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nIdol"; | |||
| break; | |||
| case TrickerType.NullTrickerType: | |||
| serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nNullTrickerType"; | |||
| @@ -318,18 +318,10 @@ namespace GameClass.GameObj | |||
| private GameObj? whatInteractingWith = null; | |||
| public GameObj? WhatInteractingWith => whatInteractingWith; | |||
| public void SetPlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| public void ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| switch (playerState) | |||
| { | |||
| case PlayerStateType.OpeningTheChest: | |||
| ((Chest)whatInteractingWith).StopOpen(); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| whatInteractingWith = gameObj; | |||
| if (value != PlayerStateType.Moving) | |||
| IsMoving = false; | |||
| @@ -35,14 +35,14 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| private bool isOpening = false; | |||
| public bool IsOpening | |||
| private int openStartTime = 0; | |||
| public int OpenStartTime | |||
| { | |||
| get => isOpening; | |||
| get => openStartTime; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| isOpening = value; | |||
| openStartTime = value; | |||
| } | |||
| } | |||
| @@ -61,6 +61,6 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedDoorway); | |||
| public bool IsOpen() => (openDegree == GameData.degreeOfOpenedDoorway); | |||
| } | |||
| } | |||
| @@ -23,31 +23,31 @@ namespace Gaming | |||
| { | |||
| if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) | |||
| { | |||
| if (CharacterManager.BeStunned((Character)player, GameData.TimeOfStunnedWhenJumpyDumpty)) | |||
| if (characterManager.BeStunned((Character)player, GameData.TimeOfStunnedWhenJumpyDumpty)) | |||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.TimeOfStunnedWhenJumpyDumpty)); | |||
| gameMap.Remove((GameObj)collisionObj); | |||
| } | |||
| } | |||
| if (player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost()) | |||
| { | |||
| if (CharacterManager.BeStunned((Character)collisionObj, GameData.TimeOfGhostFaintingWhenCharge)) | |||
| if (characterManager.BeStunned((Character)collisionObj, GameData.TimeOfGhostFaintingWhenCharge)) | |||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.TimeOfGhostFaintingWhenCharge)); | |||
| CharacterManager.BeStunned(player, GameData.TimeOfStudentFaintingWhenCharge); | |||
| characterManager.BeStunned(player, GameData.TimeOfStudentFaintingWhenCharge); | |||
| } | |||
| } | |||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (!playerToMove.Commandable()) return false; | |||
| playerToMove.SetPlayerState(PlayerStateType.Moving); | |||
| if (!playerToMove.Commandable() || !TryToStop()) return false; | |||
| characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving); | |||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection); | |||
| return true; | |||
| } | |||
| public static bool Stop(Character player) | |||
| public bool Stop(Character player) | |||
| { | |||
| if (player.Commandable()) | |||
| if (player.Commandable() || !TryToStop()) | |||
| { | |||
| player.SetPlayerState(); | |||
| characterManager.SetPlayerState(player); | |||
| return true; | |||
| } | |||
| return false; | |||
| @@ -63,7 +63,7 @@ namespace Gaming | |||
| return false; | |||
| ++generatorForFix.NumOfFixing; | |||
| player.SetPlayerState(PlayerStateType.Fixing); | |||
| characterManager.SetPlayerState(player, PlayerStateType.Fixing); | |||
| new Thread | |||
| ( | |||
| () => | |||
| @@ -74,7 +74,7 @@ namespace Gaming | |||
| { | |||
| if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) | |||
| { | |||
| player.SetPlayerState(); | |||
| characterManager.SetPlayerState(player); | |||
| gameMap.NumOfRepairedGenerators++; | |||
| } | |||
| }, | |||
| @@ -96,31 +96,21 @@ namespace Gaming | |||
| if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorway) | |||
| return false; | |||
| Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); | |||
| if (doorwayToOpen == null || doorwayToOpen.IsOpening || !doorwayToOpen.PowerSupply) | |||
| if (doorwayToOpen == null || doorwayToOpen.OpenStartTime > 0 || !doorwayToOpen.PowerSupply) | |||
| return false; | |||
| player.SetPlayerState(PlayerStateType.OpeningTheDoorway); | |||
| doorwayToOpen.IsOpening = true; | |||
| characterManager.SetPlayerState(player, PlayerStateType.OpeningTheDoorway, doorwayToOpen); | |||
| int startTime = doorwayToOpen.OpenStartTime = gameMap.Timer.nowTime(); | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheDoorway && gameMap.Timer.IsGaming && doorwayToOpen.OpenDegree < GameData.degreeOfOpenedDoorway, | |||
| loopToDo: () => | |||
| { | |||
| doorwayToOpen.OpenDegree += GameData.frameDuration; | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| Thread.Sleep(GameData.degreeOfOpenedDoorway - doorwayToOpen.OpenDegree); | |||
| .Start(); | |||
| doorwayToOpen.IsOpening = false; | |||
| if (doorwayToOpen.OpenDegree >= GameData.degreeOfOpenedDoorway) | |||
| if (doorwayToOpen.OpenStartTime == startTime) | |||
| { | |||
| if (player.PlayerState == PlayerStateType.OpeningTheDoorway) | |||
| player.SetPlayerState(); | |||
| doorwayToOpen.OpenDegree = GameData.degreeOfOpenedDoorway; | |||
| player.SetPlayerStateNaturally(); | |||
| } | |||
| } | |||
| @@ -172,22 +162,22 @@ namespace Gaming | |||
| ( | |||
| () => | |||
| { | |||
| playerTreated.SetPlayerState(PlayerStateType.Treated); | |||
| player.SetPlayerState(PlayerStateType.Treating); | |||
| characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated); | |||
| characterManager.SetPlayerState(player, PlayerStateType.Treating); | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && player.PlayerState == PlayerStateType.Treating && gameMap.Timer.IsGaming, | |||
| loopToDo: () => | |||
| { | |||
| if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) | |||
| playerTreated.SetPlayerState(); | |||
| characterManager.SetPlayerState(playerTreated); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| if (player.PlayerState == PlayerStateType.Treating) player.SetPlayerState(); | |||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) playerTreated.SetPlayerState(); | |||
| if (player.PlayerState == PlayerStateType.Treating) characterManager.SetPlayerState(player); | |||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| @@ -202,8 +192,8 @@ namespace Gaming | |||
| } | |||
| if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position)) | |||
| return false; | |||
| player.SetPlayerState(PlayerStateType.Rescuing); | |||
| playerRescued.SetPlayerState(PlayerStateType.Rescued); | |||
| characterManager.SetPlayerState(player, PlayerStateType.Rescuing); | |||
| characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued); | |||
| new Thread | |||
| ( | |||
| @@ -225,14 +215,14 @@ namespace Gaming | |||
| { | |||
| if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue) | |||
| { | |||
| playerRescued.SetPlayerState(); | |||
| characterManager.SetPlayerState(playerRescued); | |||
| playerRescued.HP = playerRescued.MaxHp / 2; | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| } | |||
| else | |||
| playerRescued.SetPlayerState(PlayerStateType.Addicted); | |||
| characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted); | |||
| } | |||
| if (player.PlayerState == PlayerStateType.Rescuing) player.SetPlayerState(); | |||
| if (player.PlayerState == PlayerStateType.Rescuing) characterManager.SetPlayerState(player); | |||
| playerRescued.TimeOfRescue = 0; | |||
| } | |||
| ) | |||
| @@ -249,7 +239,7 @@ namespace Gaming | |||
| if (chestToOpen == null || chestToOpen.OpenStartTime > 0) | |||
| return false; | |||
| player.SetPlayerState(PlayerStateType.OpeningTheChest, chestToOpen); | |||
| characterManager.SetPlayerState(player, PlayerStateType.OpeningTheChest, chestToOpen); | |||
| int startTime = gameMap.Timer.nowTime(); | |||
| chestToOpen.Open(startTime, player); | |||
| new Thread | |||
| @@ -300,7 +290,7 @@ namespace Gaming | |||
| //Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer); | |||
| // gameMap.Add(addWall); | |||
| player.SetPlayerState(PlayerStateType.ClimbingThroughWindows); | |||
| characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows); | |||
| windowForClimb.WhoIsClimbing = player; | |||
| new Thread | |||
| ( | |||
| @@ -342,7 +332,7 @@ namespace Gaming | |||
| // gameMap.Remove(addWall); | |||
| if (player.PlayerState == PlayerStateType.ClimbingThroughWindows) | |||
| { | |||
| player.SetPlayerState(); | |||
| characterManager.SetPlayerState(player); | |||
| } | |||
| } | |||
| @@ -382,7 +372,7 @@ namespace Gaming | |||
| } | |||
| if (!flag) return false; | |||
| player.SetPlayerState(PlayerStateType.LockingOrOpeningTheDoor); | |||
| characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor); | |||
| new Thread | |||
| ( | |||
| () => | |||
| @@ -405,7 +395,7 @@ namespace Gaming | |||
| doorToLock.IsOpen = (!doorToLock.IsOpen); | |||
| } | |||
| if (player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor) | |||
| player.SetPlayerState(); | |||
| characterManager.SetPlayerState(player); | |||
| doorToLock.OpenOrLockDegree = 0; | |||
| } | |||
| @@ -437,6 +427,33 @@ namespace Gaming | |||
| } | |||
| } | |||
| */ | |||
| private object numLock = new object(); | |||
| private int lastTime = 0; | |||
| private int numStop = 0; | |||
| private int NumStop => numStop; | |||
| private bool TryToStop() | |||
| { | |||
| lock (numLock) | |||
| { | |||
| int time = gameMap.Timer.nowTime(); | |||
| if (time / GameData.frameDuration > lastTime) | |||
| { | |||
| lastTime = time / GameData.frameDuration; | |||
| numStop = 1; | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| if (numStop == GameData.LimitOfStopAndMove) | |||
| return false; | |||
| else | |||
| { | |||
| ++numStop; | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| private readonly Map gameMap; | |||
| private readonly CharacterManager characterManager; | |||
| @@ -81,14 +81,14 @@ namespace Gaming | |||
| { | |||
| if (objBeingShot == null) | |||
| { | |||
| CharacterManager.BackSwing((Character?)bullet.Parent, bullet.Backswing); | |||
| characterManager.BackSwing((Character?)bullet.Parent, bullet.Backswing); | |||
| return; | |||
| } | |||
| Debugger.Output(bullet, bullet.TypeOfBullet.ToString()); | |||
| BombObj(bullet, objBeingShot); | |||
| CharacterManager.BackSwing((Character?)bullet.Parent, bullet.RecoveryFromHit); | |||
| characterManager.BackSwing((Character?)bullet.Parent, bullet.RecoveryFromHit); | |||
| return; | |||
| } | |||
| @@ -140,10 +140,10 @@ namespace Gaming | |||
| if (objBeingShot == null) | |||
| { | |||
| CharacterManager.BackSwing((Character?)bullet.Parent, bullet.Backswing); | |||
| characterManager.BackSwing((Character?)bullet.Parent, bullet.Backswing); | |||
| } | |||
| else | |||
| CharacterManager.BackSwing((Character?)bullet.Parent, bullet.RecoveryFromHit); | |||
| characterManager.BackSwing((Character?)bullet.Parent, bullet.RecoveryFromHit); | |||
| } | |||
| public bool Attack(Character? player, double angle) | |||
| @@ -174,7 +174,7 @@ namespace Gaming | |||
| if (bullet.CastTime > 0) | |||
| { | |||
| player.SetPlayerState(PlayerStateType.TryingToAttack); | |||
| characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); | |||
| new Thread | |||
| (() => | |||
| @@ -195,7 +195,7 @@ namespace Gaming | |||
| { | |||
| if (player.PlayerState == PlayerStateType.TryingToAttack) | |||
| { | |||
| player.SetPlayerState(); | |||
| characterManager.SetPlayerState(player); | |||
| } | |||
| else | |||
| bullet.IsMoving = false; | |||
| @@ -7,6 +7,7 @@ using GameEngine; | |||
| using Preparation.Interface; | |||
| using Timothy.FrameRateTask; | |||
| using System.Numerics; | |||
| using System.Timers; | |||
| namespace Gaming | |||
| { | |||
| @@ -21,6 +22,23 @@ namespace Gaming | |||
| this.gameMap = gameMap; | |||
| } | |||
| public void SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| switch (player.PlayerState) | |||
| { | |||
| case PlayerStateType.OpeningTheChest: | |||
| ((Chest)player.WhatInteractingWith).StopOpen(); | |||
| break; | |||
| case PlayerStateType.OpeningTheDoorway: | |||
| Doorway doorway = (Doorway)player.WhatInteractingWith; | |||
| doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime; | |||
| doorway.OpenStartTime = 0; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| player.ChangePlayerState(value, gameObj); | |||
| } | |||
| public Character? AddPlayer(XY pos, int teamID, int playerID, CharacterType characterType, Character? parent = null) | |||
| { | |||
| @@ -216,7 +234,7 @@ namespace Gaming | |||
| return; | |||
| } | |||
| } | |||
| player.SetPlayerState(PlayerStateType.Addicted); | |||
| SetPlayerState(player, PlayerStateType.Addicted); | |||
| new Thread | |||
| (() => | |||
| { | |||
| @@ -246,23 +264,23 @@ namespace Gaming | |||
| { IsBackground = true }.Start(); | |||
| } | |||
| public static bool BeStunned(Character player, int time) | |||
| public bool BeStunned(Character player, int time) | |||
| { | |||
| if (player.PlayerState == PlayerStateType.Stunned || player.NoHp() || player.CharacterType == CharacterType.Robot) return false; | |||
| new Thread | |||
| (() => | |||
| { | |||
| player.SetPlayerState(PlayerStateType.Stunned); | |||
| SetPlayerState(player, PlayerStateType.Stunned); | |||
| Thread.Sleep(time); | |||
| if (player.PlayerState == PlayerStateType.Stunned) | |||
| player.SetPlayerState(); | |||
| SetPlayerState(player); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| return true; | |||
| } | |||
| public static bool TryBeAwed(Student character, Bullet bullet) | |||
| public bool TryBeAwed(Student character, Bullet bullet) | |||
| { | |||
| if (character.CanBeAwed()) | |||
| { | |||
| @@ -337,11 +355,11 @@ namespace Gaming | |||
| else TryBeAwed(student, bullet); | |||
| } | |||
| public static bool BackSwing(Character? player, int time) | |||
| public bool BackSwing(Character? player, int time) | |||
| { | |||
| if (player == null || time <= 0) return false; | |||
| if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false; | |||
| player.SetPlayerState(PlayerStateType.Swinging); | |||
| SetPlayerState(player, PlayerStateType.Swinging); | |||
| new Thread | |||
| (() => | |||
| @@ -350,7 +368,7 @@ namespace Gaming | |||
| if (player.PlayerState == PlayerStateType.Swinging) | |||
| { | |||
| player.SetPlayerState(); | |||
| SetPlayerState(player); | |||
| } | |||
| } | |||
| ) | |||
| @@ -167,7 +167,7 @@ namespace Gaming | |||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||
| if (player != null) | |||
| { | |||
| return ActionManager.Stop(player); | |||
| return actionManager.Stop(player); | |||
| } | |||
| return false; | |||
| } | |||
| @@ -233,7 +233,7 @@ namespace Gaming | |||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||
| if (player != null) | |||
| { | |||
| PropManager.UseProp(player, propType); | |||
| propManager.UseProp(player, propType); | |||
| } | |||
| } | |||
| public void ThrowProp(long playerID, PropType propType = PropType.Null) | |||
| @@ -375,7 +375,7 @@ namespace Gaming | |||
| characterManager = new CharacterManager(gameMap); | |||
| attackManager = new AttackManager(gameMap, characterManager); | |||
| actionManager = new ActionManager(gameMap, characterManager); | |||
| propManager = new PropManager(gameMap); | |||
| propManager = new PropManager(gameMap, characterManager); | |||
| skillManager = new SkillManager(gameMap, actionManager, attackManager, propManager, characterManager); | |||
| } | |||
| } | |||
| @@ -16,10 +16,10 @@ namespace Gaming | |||
| private class PropManager | |||
| { | |||
| private readonly Map gameMap; | |||
| private readonly CharacterManager characterManager; | |||
| private readonly List<XY> availableCellForGenerateProp; | |||
| public static void UseProp(Character player, PropType propType) | |||
| public void UseProp(Character player, PropType propType) | |||
| { | |||
| if (player.IsResetting || player.CharacterType == CharacterType.Robot) | |||
| return; | |||
| @@ -57,7 +57,7 @@ namespace Gaming | |||
| if (player.PlayerState == PlayerStateType.Stunned) | |||
| { | |||
| player.AddScore(GameData.ScorePropRecoverFromDizziness); | |||
| player.SetPlayerState(); | |||
| player.SetPlayerStateNaturally(); | |||
| } | |||
| break; | |||
| default: | |||
| @@ -210,8 +210,9 @@ namespace Gaming | |||
| { IsBackground = true }.Start(); | |||
| */ | |||
| } | |||
| public PropManager(Map gameMap) // 道具不能扔过墙 | |||
| public PropManager(Map gameMap, CharacterManager characterManager) // 道具不能扔过墙 | |||
| { | |||
| this.characterManager = characterManager; | |||
| this.gameMap = gameMap; | |||
| /* this.moveEngine = new MoveEngine( | |||
| gameMap: gameMap, | |||
| @@ -4,6 +4,7 @@ using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| using Timothy.FrameRateTask; | |||
| using Google.Protobuf.Compiler; | |||
| namespace Gaming | |||
| { | |||
| @@ -28,29 +29,51 @@ namespace Gaming | |||
| { }); | |||
| } | |||
| public static bool ShowTime(Character player) | |||
| public bool ShowTime(Character player) | |||
| { | |||
| if ((!player.Commandable())) return false; | |||
| IActiveSkill skill = player.FindIActiveSkill(ActiveSkillType.ShowTime); | |||
| Debugger.Output(player, ": It's show time!"); | |||
| characterManager.SetPlayerState(player, PlayerStateType.UsingSkill); | |||
| return ActiveSkillEffect(skill, player, () => | |||
| { | |||
| /* Thread | |||
| * new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheChest && gameMap.Timer.IsGaming && (!chestToOpen.IsOpen()), | |||
| loopToDo: () => | |||
| { | |||
| chestToOpen.OpenStartTime += GameData.frameDuration * player.SpeedOfOpenChest; | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => player.PlayerState == PlayerStateType.UsingSkill && gameMap.Timer.IsGaming, | |||
| loopToDo: () => | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | |||
| try | |||
| { | |||
| foreach (Character person in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| if (!person.IsGhost()) | |||
| actionManager.MovePlayer(person, GameData.frameDuration, (player.Position - person.Position).Angle()); | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||
| } | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start();*/ | |||
| .Start(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| }, | |||
| () => | |||
| { }); | |||
| { | |||
| if (player.PlayerState == PlayerStateType.UsingSkill) | |||
| player.SetPlayerStateNaturally(); | |||
| } | |||
| ); | |||
| } | |||
| public static bool BecomeInvisible(Character player) | |||
| @@ -145,7 +168,7 @@ namespace Gaming | |||
| { | |||
| if (!character.IsGhost() && XY.Distance(character.Position, player.Position) <= player.ViewRange) | |||
| { | |||
| if (CharacterManager.BeStunned(character, GameData.TimeOfStudentFaintingWhenHowl)) | |||
| if (characterManager.BeStunned(character, GameData.TimeOfStudentFaintingWhenHowl)) | |||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.TimeOfStudentFaintingWhenHowl)); | |||
| break; | |||
| } | |||
| @@ -155,7 +178,7 @@ namespace Gaming | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||
| } | |||
| CharacterManager.BackSwing(player, GameData.TimeOfGhostSwingingAfterHowl); | |||
| characterManager.BackSwing(player, GameData.TimeOfGhostSwingingAfterHowl); | |||
| Debugger.Output(player, "howled!"); | |||
| }, | |||
| () => | |||
| @@ -177,7 +200,7 @@ namespace Gaming | |||
| || character.PlayerState == PlayerStateType.UsingSkill) | |||
| && gameMap.CanSee(player, character)) | |||
| { | |||
| if (CharacterManager.BeStunned(character, GameData.TimeOfGhostFaintingWhenPunish)) | |||
| if (characterManager.BeStunned(character, GameData.TimeOfGhostFaintingWhenPunish)) | |||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.TimeOfGhostFaintingWhenPunish)); | |||
| break; | |||
| } | |||
| @@ -205,7 +228,7 @@ namespace Gaming | |||
| { | |||
| if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) | |||
| { | |||
| character.SetPlayerState(); | |||
| characterManager.SetPlayerState(character); | |||
| character.HP = GameData.RemainHpWhenAddLife; | |||
| ((Student)character).TimeOfRescue = 0; | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| @@ -52,6 +52,9 @@ namespace Gaming | |||
| case ActiveSkillType.Rouse: | |||
| Rouse(character); | |||
| break; | |||
| case ActiveSkillType.ShowTime: | |||
| ShowTime(character); | |||
| break; | |||
| default: | |||
| return false; | |||
| } | |||
| @@ -101,10 +101,10 @@ namespace Preparation.Interface | |||
| public BulletType InitBullet => BulletType.CommonAttackOfGhost; | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { }); | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.ShowTime }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public double concealment = GameData.basicConcealment; | |||
| public double concealment = GameData.basicConcealment * 3 / 4; | |||
| public double Concealment => concealment; | |||
| public int alertnessRadius = GameData.basicGhostAlertnessRadius; | |||
| @@ -414,6 +414,8 @@ namespace Preparation.Interface | |||
| return new ANoisyPerson(); | |||
| case CharacterType.TechOtaku: | |||
| return new TechOtaku(); | |||
| case CharacterType.Idol: | |||
| return new Idol(); | |||
| case CharacterType.Athlete: | |||
| default: | |||
| return new Athlete(); | |||
| @@ -126,7 +126,7 @@ namespace Preparation.Interface | |||
| public int SkillCD => GameData.commonSkillCD * 3; | |||
| public int DurationTime => GameData.commonSkillTime; | |||
| private readonly object commonSkillLock = new object(); | |||
| private readonly object commonSkillLock = new(); | |||
| public object ActiveSkillLock => commonSkillLock; | |||
| public bool isBeingUsed = false; | |||
| @@ -275,6 +275,8 @@ namespace Preparation.Interface | |||
| return new Rouse(); | |||
| case ActiveSkillType.Inspire: | |||
| return new Inspire(); | |||
| case ActiveSkillType.ShowTime: | |||
| return new ShowTime(); | |||
| default: | |||
| return new NullSkill(); | |||
| } | |||
| @@ -308,6 +310,8 @@ namespace Preparation.Interface | |||
| return ActiveSkillType.UseRobot; | |||
| case Rouse: | |||
| return ActiveSkillType.Rouse; | |||
| case ShowTime: | |||
| return ActiveSkillType.ShowTime; | |||
| default: | |||
| return ActiveSkillType.Null; | |||
| } | |||
| @@ -83,6 +83,7 @@ namespace Preparation.Utility | |||
| ANoisyPerson = 7, | |||
| Robot = 8, | |||
| Sunshine = 9, | |||
| Idol = 10, | |||
| } | |||
| public enum ActiveSkillType // 主动技能 | |||
| { | |||
| @@ -15,6 +15,8 @@ namespace Preparation.Utility | |||
| public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | |||
| public const long gameDuration = 600000; // 游戏时长600000ms = 10min | |||
| public const int LimitOfStopAndMove = 15; | |||
| public const int MinSpeed = 1; // 最小速度 | |||
| public const int MaxSpeed = int.MaxValue; // 最大速度 | |||
| @@ -127,6 +129,7 @@ namespace Preparation.Utility | |||
| CharacterType.Assassin => true, | |||
| CharacterType.Klee => true, | |||
| CharacterType.ANoisyPerson => true, | |||
| CharacterType.Idol => true, | |||
| _ => false, | |||
| }; | |||
| } | |||
| @@ -233,6 +233,8 @@ namespace Preparation.Utility | |||
| return Protobuf.TrickerType.Klee; | |||
| case Preparation.Utility.CharacterType.ANoisyPerson: | |||
| return Protobuf.TrickerType.ANoisyPerson; | |||
| case CharacterType.Idol: | |||
| return TrickerType.Idol; | |||
| default: | |||
| return Protobuf.TrickerType.NullTrickerType; | |||
| } | |||
| @@ -247,6 +249,8 @@ namespace Preparation.Utility | |||
| return Preparation.Utility.CharacterType.Klee; | |||
| case Protobuf.TrickerType.ANoisyPerson: | |||
| return Preparation.Utility.CharacterType.ANoisyPerson; | |||
| case TrickerType.Idol: | |||
| return CharacterType.Idol; | |||
| default: | |||
| return Preparation.Utility.CharacterType.Null; | |||
| } | |||
| @@ -75,10 +75,7 @@ namespace Preparation.Utility | |||
| return Math.Atan2(y, x); | |||
| } | |||
| public override bool Equals(object obj) | |||
| { | |||
| throw new NotImplementedException(); | |||
| } | |||
| public override bool Equals(object obj) => throw new NotImplementedException(); | |||
| public override int GetHashCode() | |||
| { | |||
| @@ -32,7 +32,7 @@ namespace Server | |||
| case Preparation.Utility.GameObjType.Chest: | |||
| return Chest((Chest)gameObj, time); | |||
| case Preparation.Utility.GameObjType.Doorway: | |||
| return Gate((Doorway)gameObj); | |||
| return Gate((Doorway)gameObj, time); | |||
| case Preparation.Utility.GameObjType.EmergencyExit: | |||
| if (((EmergencyExit)gameObj).CanOpen) | |||
| return HiddenGate((EmergencyExit)gameObj); | |||
| @@ -44,133 +44,153 @@ namespace Server | |||
| } | |||
| public static MessageOfObj? Auto(MessageOfNews news) | |||
| { | |||
| MessageOfObj objMsg = new(); | |||
| objMsg.NewsMessage = news; | |||
| MessageOfObj objMsg = new() | |||
| { | |||
| NewsMessage = news | |||
| }; | |||
| return objMsg; | |||
| } | |||
| private static MessageOfObj? Student(Student player) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| if (player.IsGhost()) return null; | |||
| msg.StudentMessage = new(); | |||
| MessageOfObj msg = new() | |||
| { | |||
| StudentMessage = new() | |||
| { | |||
| X = player.Position.x, | |||
| Y = player.Position.y, | |||
| Speed = player.MoveSpeed, | |||
| Determination = player.HP, | |||
| Addiction = player.GamingAddiction, | |||
| Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)player.Place), | |||
| Guid = player.ID, | |||
| PlayerState = Transformation.ToPlayerState((PlayerStateType)player.PlayerState), | |||
| PlayerId = player.PlayerID, | |||
| ViewRange = player.ViewRange, | |||
| Radius = player.Radius, | |||
| DangerAlert = (player.BgmDictionary.ContainsKey(BgmType.GhostIsComing)) ? player.BgmDictionary[BgmType.GhostIsComing] : 0, | |||
| Score = player.Score, | |||
| TreatProgress = player.DegreeOfTreatment, | |||
| RescueProgress = player.TimeOfRescue, | |||
| msg.StudentMessage.X = player.Position.x; | |||
| msg.StudentMessage.Y = player.Position.y; | |||
| msg.StudentMessage.Speed = player.MoveSpeed; | |||
| msg.StudentMessage.Determination = player.HP; | |||
| msg.StudentMessage.Addiction = player.GamingAddiction; | |||
| BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer), | |||
| LearningSpeed = player.FixSpeed, | |||
| TreatSpeed = player.TreatSpeed, | |||
| FacingDirection = player.FacingDirection.Angle(), | |||
| StudentType = Transformation.ToStudentType(player.CharacterType) | |||
| } | |||
| }; | |||
| foreach (var keyValue in player.TimeUntilActiveSkillAvailable) | |||
| msg.StudentMessage.TimeUntilSkillAvailable.Add(keyValue.Value); | |||
| for (int i = 0; i < GameData.maxNumOfSkill - player.TimeUntilActiveSkillAvailable.Count(); ++i) | |||
| for (int i = 0; i < GameData.maxNumOfSkill - player.TimeUntilActiveSkillAvailable.Count; ++i) | |||
| msg.StudentMessage.TimeUntilSkillAvailable.Add(-1); | |||
| foreach (var value in player.PropInventory) | |||
| msg.StudentMessage.Prop.Add(Transformation.ToPropType(value.GetPropType())); | |||
| msg.StudentMessage.Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)player.Place); | |||
| msg.StudentMessage.Guid = player.ID; | |||
| msg.StudentMessage.PlayerState = Transformation.ToPlayerState((PlayerStateType)player.PlayerState); | |||
| msg.StudentMessage.PlayerId = player.PlayerID; | |||
| msg.StudentMessage.ViewRange = player.ViewRange; | |||
| msg.StudentMessage.Radius = player.Radius; | |||
| msg.StudentMessage.DangerAlert = (player.BgmDictionary.ContainsKey(BgmType.GhostIsComing)) ? player.BgmDictionary[BgmType.GhostIsComing] : 0; | |||
| msg.StudentMessage.Score = player.Score; | |||
| msg.StudentMessage.TreatProgress = player.DegreeOfTreatment; | |||
| msg.StudentMessage.RescueProgress = player.TimeOfRescue; | |||
| foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | |||
| { | |||
| if (kvp.Value) | |||
| msg.StudentMessage.Buff.Add(Transformation.ToStudentBuffType(kvp.Key)); | |||
| } | |||
| msg.StudentMessage.BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer); | |||
| msg.StudentMessage.LearningSpeed = player.FixSpeed; | |||
| msg.StudentMessage.TreatSpeed = player.TreatSpeed; | |||
| msg.StudentMessage.FacingDirection = player.FacingDirection.Angle(); | |||
| msg.StudentMessage.StudentType = Transformation.ToStudentType(player.CharacterType); | |||
| return msg; | |||
| } | |||
| private static MessageOfObj? Tricker(Character player) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| if (!player.IsGhost()) return null; | |||
| msg.TrickerMessage = new(); | |||
| MessageOfObj msg = new() | |||
| { | |||
| TrickerMessage = new() | |||
| { | |||
| X = player.Position.x, | |||
| Y = player.Position.y, | |||
| Speed = player.MoveSpeed, | |||
| Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)player.Place), | |||
| TrickerType = Transformation.ToTrickerType(player.CharacterType), | |||
| Guid = player.ID, | |||
| Score = player.Score, | |||
| PlayerId = player.PlayerID, | |||
| ViewRange = player.ViewRange, | |||
| Radius = player.Radius, | |||
| PlayerState = Transformation.ToPlayerState((PlayerStateType)player.PlayerState), | |||
| TrickDesire = (player.BgmDictionary.ContainsKey(BgmType.StudentIsApproaching)) ? player.BgmDictionary[BgmType.StudentIsApproaching] : 0, | |||
| ClassVolume = (player.BgmDictionary.ContainsKey(BgmType.GeneratorIsBeingFixed)) ? player.BgmDictionary[BgmType.GeneratorIsBeingFixed] : 0, | |||
| FacingDirection = player.FacingDirection.Angle(), | |||
| BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer) | |||
| } | |||
| }; | |||
| msg.TrickerMessage.X = player.Position.x; | |||
| msg.TrickerMessage.Y = player.Position.y; | |||
| msg.TrickerMessage.Speed = player.MoveSpeed; | |||
| foreach (var keyValue in player.TimeUntilActiveSkillAvailable) | |||
| msg.TrickerMessage.TimeUntilSkillAvailable.Add(keyValue.Value); | |||
| for (int i = 0; i < GameData.maxNumOfSkill - player.TimeUntilActiveSkillAvailable.Count(); ++i) | |||
| for (int i = 0; i < GameData.maxNumOfSkill - player.TimeUntilActiveSkillAvailable.Count; ++i) | |||
| msg.TrickerMessage.TimeUntilSkillAvailable.Add(-1); | |||
| msg.TrickerMessage.Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)player.Place); | |||
| foreach (var value in player.PropInventory) | |||
| msg.TrickerMessage.Prop.Add(Transformation.ToPropType(value.GetPropType())); | |||
| msg.TrickerMessage.TrickerType = Transformation.ToTrickerType(player.CharacterType); | |||
| msg.TrickerMessage.Guid = player.ID; | |||
| msg.TrickerMessage.Score = player.Score; | |||
| msg.TrickerMessage.PlayerId = player.PlayerID; | |||
| msg.TrickerMessage.ViewRange = player.ViewRange; | |||
| msg.TrickerMessage.Radius = player.Radius; | |||
| msg.TrickerMessage.PlayerState = Transformation.ToPlayerState((PlayerStateType)player.PlayerState); | |||
| msg.TrickerMessage.TrickDesire = (player.BgmDictionary.ContainsKey(BgmType.StudentIsApproaching)) ? player.BgmDictionary[BgmType.StudentIsApproaching] : 0; | |||
| msg.TrickerMessage.ClassVolume = (player.BgmDictionary.ContainsKey(BgmType.GeneratorIsBeingFixed)) ? player.BgmDictionary[BgmType.GeneratorIsBeingFixed] : 0; | |||
| msg.TrickerMessage.FacingDirection = player.FacingDirection.Angle(); | |||
| msg.TrickerMessage.BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer); | |||
| foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | |||
| { | |||
| if (kvp.Value) | |||
| msg.TrickerMessage.Buff.Add(Transformation.ToTrickerBuffType(kvp.Key)); | |||
| } | |||
| return msg; | |||
| } | |||
| private static MessageOfObj Bullet(Bullet bullet) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.BulletMessage = new(); | |||
| msg.BulletMessage.X = bullet.Position.x; | |||
| msg.BulletMessage.Y = bullet.Position.y; | |||
| msg.BulletMessage.FacingDirection = bullet.FacingDirection.Angle(); | |||
| msg.BulletMessage.Guid = bullet.ID; | |||
| msg.BulletMessage.Team = (bullet.Parent.IsGhost()) ? PlayerType.TrickerPlayer : PlayerType.StudentPlayer; | |||
| msg.BulletMessage.Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)bullet.Place); | |||
| msg.BulletMessage.BombRange = bullet.BulletBombRange; | |||
| msg.BulletMessage.Speed = bullet.Speed; | |||
| MessageOfObj msg = new() | |||
| { | |||
| BulletMessage = new() | |||
| { | |||
| X = bullet.Position.x, | |||
| Y = bullet.Position.y, | |||
| FacingDirection = bullet.FacingDirection.Angle(), | |||
| Guid = bullet.ID, | |||
| Team = (bullet.Parent.IsGhost()) ? PlayerType.TrickerPlayer : PlayerType.StudentPlayer, | |||
| Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)bullet.Place), | |||
| BombRange = bullet.BulletBombRange, | |||
| Speed = bullet.Speed | |||
| } | |||
| }; | |||
| return msg; | |||
| } | |||
| private static MessageOfObj Prop(Prop prop) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.PropMessage = new(); | |||
| msg.PropMessage.Type = Transformation.ToPropType(prop.GetPropType()); | |||
| msg.PropMessage.X = prop.Position.x; | |||
| msg.PropMessage.Y = prop.Position.y; | |||
| msg.PropMessage.FacingDirection = prop.FacingDirection.Angle(); | |||
| msg.PropMessage.Guid = prop.ID; | |||
| msg.PropMessage.Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)prop.Place); | |||
| MessageOfObj msg = new() | |||
| { | |||
| PropMessage = new() | |||
| { | |||
| Type = Transformation.ToPropType(prop.GetPropType()), | |||
| X = prop.Position.x, | |||
| Y = prop.Position.y, | |||
| FacingDirection = prop.FacingDirection.Angle(), | |||
| Guid = prop.ID, | |||
| Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)prop.Place) | |||
| } | |||
| }; | |||
| return msg; | |||
| } | |||
| private static MessageOfObj BombedBullet(BombedBullet bombedBullet) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.BombedBulletMessage = new(); | |||
| msg.BombedBulletMessage.X = bombedBullet.bulletHasBombed.Position.x; | |||
| msg.BombedBulletMessage.Y = bombedBullet.bulletHasBombed.Position.y; | |||
| msg.BombedBulletMessage.FacingDirection = bombedBullet.FacingDirection.Angle(); | |||
| msg.BombedBulletMessage.MappingId = bombedBullet.MappingID; | |||
| msg.BombedBulletMessage.BombRange = bombedBullet.bulletHasBombed.BulletBombRange; | |||
| MessageOfObj msg = new() | |||
| { | |||
| BombedBulletMessage = new() | |||
| { | |||
| X = bombedBullet.bulletHasBombed.Position.x, | |||
| Y = bombedBullet.bulletHasBombed.Position.y, | |||
| FacingDirection = bombedBullet.FacingDirection.Angle(), | |||
| MappingId = bombedBullet.MappingID, | |||
| BombRange = bombedBullet.bulletHasBombed.BulletBombRange | |||
| } | |||
| }; | |||
| return msg; | |||
| } | |||
| @@ -188,48 +208,69 @@ namespace Server | |||
| private static MessageOfObj Classroom(Generator generator) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.ClassroomMessage = new(); | |||
| msg.ClassroomMessage.X = generator.Position.x; | |||
| msg.ClassroomMessage.Y = generator.Position.y; | |||
| msg.ClassroomMessage.Progress = generator.DegreeOfRepair; | |||
| MessageOfObj msg = new() | |||
| { | |||
| ClassroomMessage = new() | |||
| { | |||
| X = generator.Position.x, | |||
| Y = generator.Position.y, | |||
| Progress = generator.DegreeOfRepair | |||
| } | |||
| }; | |||
| return msg; | |||
| } | |||
| private static MessageOfObj Gate(Doorway doorway) | |||
| private static MessageOfObj Gate(Doorway doorway, int time) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.GateMessage = new(); | |||
| msg.GateMessage.X = doorway.Position.x; | |||
| msg.GateMessage.Y = doorway.Position.y; | |||
| msg.GateMessage.Progress = doorway.OpenDegree; | |||
| MessageOfObj msg = new() | |||
| { | |||
| GateMessage = new() | |||
| { | |||
| X = doorway.Position.x, | |||
| Y = doorway.Position.y | |||
| } | |||
| }; | |||
| int progress = ((doorway.OpenStartTime > 0) ? (time - doorway.OpenStartTime) : 0) + doorway.OpenDegree; | |||
| msg.GateMessage.Progress = (progress > GameData.degreeOfOpenedDoorway) ? GameData.degreeOfOpenedDoorway : progress; | |||
| return msg; | |||
| } | |||
| private static MessageOfObj HiddenGate(EmergencyExit Exit) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.HiddenGateMessage = new(); | |||
| msg.HiddenGateMessage.X = Exit.Position.x; | |||
| msg.HiddenGateMessage.Y = Exit.Position.y; | |||
| msg.HiddenGateMessage.Opened = Exit.IsOpen; | |||
| MessageOfObj msg = new() | |||
| { | |||
| HiddenGateMessage = new() | |||
| { | |||
| X = Exit.Position.x, | |||
| Y = Exit.Position.y, | |||
| Opened = Exit.IsOpen | |||
| } | |||
| }; | |||
| return msg; | |||
| } | |||
| private static MessageOfObj Door(Door door) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.DoorMessage = new(); | |||
| msg.DoorMessage.X = door.Position.x; | |||
| msg.DoorMessage.Y = door.Position.y; | |||
| msg.DoorMessage.Progress = door.OpenOrLockDegree; | |||
| msg.DoorMessage.IsOpen = door.IsOpen; | |||
| MessageOfObj msg = new() | |||
| { | |||
| DoorMessage = new() | |||
| { | |||
| X = door.Position.x, | |||
| Y = door.Position.y, | |||
| Progress = door.OpenOrLockDegree, | |||
| IsOpen = door.IsOpen | |||
| } | |||
| }; | |||
| return msg; | |||
| } | |||
| private static MessageOfObj Chest(Chest chest, int time) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.ChestMessage = new(); | |||
| msg.ChestMessage.X = chest.Position.x; | |||
| msg.ChestMessage.Y = chest.Position.y; | |||
| MessageOfObj msg = new() | |||
| { | |||
| ChestMessage = new() | |||
| { | |||
| X = chest.Position.x, | |||
| Y = chest.Position.y | |||
| } | |||
| }; | |||
| int progress = (chest.OpenStartTime > 0) ? ((time - chest.OpenStartTime) * chest.WhoOpen.SpeedOfOpenChest) : 0; | |||
| msg.ChestMessage.Progress = (progress > GameData.degreeOfOpenedChest) ? GameData.degreeOfOpenedChest : progress; | |||
| return msg; | |||
| @@ -252,7 +252,7 @@ | |||
| - 特性 | |||
| - 冥想 | |||
| - 当玩家处于可接受指令状态且不在修机时,会积累学习进度,速度为0.3%/ms | |||
| - 受到攻击(并非伤害)或眩晕或翻窗(或攻击他人)学习进度清零 | |||
| - 受到攻击(并非伤害)或学习或进入不可接受治疗状态(包括翻窗)学习进度清零 | |||
| - 主动技能5 | |||
| - 写答案 | |||
| - CD:30s | |||
| @@ -401,6 +401,7 @@ public: | |||
| ### 人物 | |||
| - 被唤醒或被勉励不属于交互状态,翻窗属于交互状态 | |||
| - EndAllAction()及Move指令调用数总和一帧内不超过10次 | |||
| ### 初始状态 | |||
| - 玩家出生点固定且一定为空地 | |||