perf: ⚡ add whatInteractingWith to make a great optimization
tags/0.1.0
| @@ -300,14 +300,6 @@ namespace GameClass.GameObj | |||
| if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving; | |||
| return playerState; | |||
| } | |||
| set | |||
| { | |||
| if (value != PlayerStateType.Moving) | |||
| lock (gameObjLock) | |||
| IsMoving = false; | |||
| lock (gameObjLock) playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||
| } | |||
| } | |||
| public bool NoHp() => (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped | |||
| @@ -323,6 +315,38 @@ namespace GameClass.GameObj | |||
| || playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned | |||
| || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | |||
| private GameObj? whatInteractingWith = null; | |||
| public GameObj? WhatInteractingWith => whatInteractingWith; | |||
| public void SetPlayerState(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; | |||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | |||
| } | |||
| } | |||
| public void SetPlayerStateNaturally() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| whatInteractingWith = null; | |||
| playerState = PlayerStateType.Null; | |||
| } | |||
| } | |||
| private int score = 0; | |||
| public int Score | |||
| { | |||
| @@ -382,21 +406,6 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 角色携带的信息 | |||
| /// </summary> | |||
| private string message = "THUAI6"; | |||
| public string Message | |||
| { | |||
| get => message; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| message = value; | |||
| } | |||
| } | |||
| } | |||
| #region 道具和buff相关属性、方法 | |||
| private Prop[] propInventory = new Prop[GameData.maxNumOfPropInPropInventory] | |||
| @@ -20,20 +20,25 @@ namespace GameClass.GameObj | |||
| private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() }; | |||
| public Prop[] PropInChest => propInChest; | |||
| private int openDegree = 0; | |||
| public int OpenDegree | |||
| private int openStartTime = 0; | |||
| public int OpenStartTime => openStartTime; | |||
| private Character? whoOpen = null; | |||
| public Character? WhoOpen => whoOpen; | |||
| public void Open(int startTime, Character character) | |||
| { | |||
| get => openDegree; | |||
| set | |||
| lock (gameObjLock) | |||
| { | |||
| if (value > 0) | |||
| lock (gameObjLock) | |||
| openDegree = (value > GameData.degreeOfOpenedChest) ? GameData.degreeOfOpenedChest : value; | |||
| else | |||
| lock (gameObjLock) | |||
| openDegree = 0; | |||
| openStartTime = startTime; | |||
| whoOpen = character; | |||
| } | |||
| } | |||
| public void StopOpen() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| openStartTime = 0; | |||
| whoOpen = null; | |||
| } | |||
| } | |||
| public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedChest); | |||
| } | |||
| } | |||
| @@ -38,7 +38,7 @@ namespace Gaming | |||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (!playerToMove.Commandable()) return false; | |||
| playerToMove.PlayerState = PlayerStateType.Moving; | |||
| playerToMove.SetPlayerState(PlayerStateType.Moving); | |||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection); | |||
| return true; | |||
| } | |||
| @@ -47,7 +47,7 @@ namespace Gaming | |||
| { | |||
| if (player.Commandable()) | |||
| { | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| return true; | |||
| } | |||
| return false; | |||
| @@ -63,7 +63,7 @@ namespace Gaming | |||
| return false; | |||
| ++generatorForFix.NumOfFixing; | |||
| player.PlayerState = PlayerStateType.Fixing; | |||
| player.SetPlayerState(PlayerStateType.Fixing); | |||
| new Thread | |||
| ( | |||
| () => | |||
| @@ -74,7 +74,7 @@ namespace Gaming | |||
| { | |||
| if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) | |||
| { | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| gameMap.NumOfRepairedGenerators++; | |||
| } | |||
| }, | |||
| @@ -99,7 +99,7 @@ namespace Gaming | |||
| if (doorwayToOpen == null || doorwayToOpen.IsOpening || !doorwayToOpen.PowerSupply) | |||
| return false; | |||
| player.PlayerState = PlayerStateType.OpeningTheDoorway; | |||
| player.SetPlayerState(PlayerStateType.OpeningTheDoorway); | |||
| doorwayToOpen.IsOpening = true; | |||
| new Thread | |||
| ( | |||
| @@ -120,7 +120,7 @@ namespace Gaming | |||
| if (doorwayToOpen.OpenDegree >= GameData.degreeOfOpenedDoorway) | |||
| { | |||
| if (player.PlayerState == PlayerStateType.OpeningTheDoorway) | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| } | |||
| } | |||
| @@ -163,7 +163,7 @@ namespace Gaming | |||
| playerTreated = gameMap.StudentForInteract(player.Position); | |||
| if (playerTreated == null) return false; | |||
| } | |||
| if (player == playerTreated || (!player.Commandable()) || player.PlayerState == PlayerStateType.Treating || | |||
| if (player == playerTreated || (!player.Commandable()) || playerTreated.PlayerState == PlayerStateType.Treated || | |||
| (!playerTreated.Commandable()) || | |||
| playerTreated.HP == playerTreated.MaxHp || !GameData.ApproachToInteract(playerTreated.Position, player.Position)) | |||
| return false; | |||
| @@ -172,22 +172,22 @@ namespace Gaming | |||
| ( | |||
| () => | |||
| { | |||
| playerTreated.PlayerState = PlayerStateType.Treated; | |||
| player.PlayerState = PlayerStateType.Treating; | |||
| playerTreated.SetPlayerState(PlayerStateType.Treated); | |||
| player.SetPlayerState(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.PlayerState = PlayerStateType.Null; | |||
| playerTreated.SetPlayerState(); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| if (player.PlayerState == PlayerStateType.Treating) player.PlayerState = PlayerStateType.Null; | |||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) playerTreated.PlayerState = PlayerStateType.Null; | |||
| if (player.PlayerState == PlayerStateType.Treating) player.SetPlayerState(); | |||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) playerTreated.SetPlayerState(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| @@ -200,11 +200,10 @@ namespace Gaming | |||
| playerRescued = gameMap.StudentForInteract(player.Position); | |||
| if (playerRescued == null) return false; | |||
| } | |||
| if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || player == playerRescued | |||
| || !GameData.ApproachToInteract(playerRescued.Position, player.Position) || playerRescued.TimeOfRescue > 0) | |||
| if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position)) | |||
| return false; | |||
| player.PlayerState = PlayerStateType.Rescuing; | |||
| playerRescued.PlayerState = PlayerStateType.Rescued; | |||
| player.SetPlayerState(PlayerStateType.Rescuing); | |||
| playerRescued.SetPlayerState(PlayerStateType.Rescued); | |||
| new Thread | |||
| ( | |||
| @@ -226,14 +225,14 @@ namespace Gaming | |||
| { | |||
| if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue) | |||
| { | |||
| playerRescued.PlayerState = PlayerStateType.Null; | |||
| playerRescued.SetPlayerState(); | |||
| playerRescued.HP = playerRescued.MaxHp / 2; | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| } | |||
| else | |||
| playerRescued.PlayerState = PlayerStateType.Addicted; | |||
| playerRescued.SetPlayerState(PlayerStateType.Addicted); | |||
| } | |||
| if (player.PlayerState == PlayerStateType.Rescuing) player.PlayerState = PlayerStateType.Null; | |||
| if (player.PlayerState == PlayerStateType.Rescuing) player.SetPlayerState(); | |||
| playerRescued.TimeOfRescue = 0; | |||
| } | |||
| ) | |||
| @@ -247,30 +246,21 @@ namespace Gaming | |||
| return false; | |||
| Chest? chestToOpen = (Chest?)gameMap.OneForInteract(player.Position, GameObjType.Chest); | |||
| if (chestToOpen == null || chestToOpen.OpenDegree > 0) | |||
| if (chestToOpen == null || chestToOpen.OpenStartTime > 0) | |||
| return false; | |||
| player.PlayerState = PlayerStateType.OpeningTheChest; | |||
| player.SetPlayerState(PlayerStateType.OpeningTheChest, chestToOpen); | |||
| int startTime = gameMap.Timer.nowTime(); | |||
| chestToOpen.Open(startTime, player); | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheChest && gameMap.Timer.IsGaming && (!chestToOpen.IsOpen()), | |||
| loopToDo: () => | |||
| { | |||
| chestToOpen.OpenDegree += GameData.frameDuration * player.SpeedOfOpenChest; | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| Thread.Sleep(GameData.degreeOfOpenedChest / player.SpeedOfOpenChest); | |||
| .Start(); | |||
| if (chestToOpen.IsOpen()) | |||
| if (chestToOpen.OpenStartTime == startTime) | |||
| { | |||
| if (player.PlayerState == PlayerStateType.OpeningTheChest) | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerStateNaturally(); | |||
| for (int i = 0; i < GameData.maxNumOfPropInChest; ++i) | |||
| { | |||
| Prop prop = chestToOpen.PropInChest[i]; | |||
| @@ -279,8 +269,6 @@ namespace Gaming | |||
| gameMap.Add(prop); | |||
| } | |||
| } | |||
| else chestToOpen.OpenDegree = 0; | |||
| } | |||
| ) | |||
| @@ -312,7 +300,7 @@ namespace Gaming | |||
| //Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer); | |||
| // gameMap.Add(addWall); | |||
| player.PlayerState = PlayerStateType.ClimbingThroughWindows; | |||
| player.SetPlayerState(PlayerStateType.ClimbingThroughWindows); | |||
| windowForClimb.WhoIsClimbing = player; | |||
| new Thread | |||
| ( | |||
| @@ -354,7 +342,7 @@ namespace Gaming | |||
| // gameMap.Remove(addWall); | |||
| if (player.PlayerState == PlayerStateType.ClimbingThroughWindows) | |||
| { | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| } | |||
| } | |||
| @@ -394,7 +382,7 @@ namespace Gaming | |||
| } | |||
| if (!flag) return false; | |||
| player.PlayerState = PlayerStateType.LockingOrOpeningTheDoor; | |||
| player.SetPlayerState(PlayerStateType.LockingOrOpeningTheDoor); | |||
| new Thread | |||
| ( | |||
| () => | |||
| @@ -417,7 +405,7 @@ namespace Gaming | |||
| doorToLock.IsOpen = (!doorToLock.IsOpen); | |||
| } | |||
| if (player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor) | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| doorToLock.OpenOrLockDegree = 0; | |||
| } | |||
| @@ -174,7 +174,7 @@ namespace Gaming | |||
| if (bullet.CastTime > 0) | |||
| { | |||
| player.PlayerState = PlayerStateType.TryingToAttack; | |||
| player.SetPlayerState(PlayerStateType.TryingToAttack); | |||
| new Thread | |||
| (() => | |||
| @@ -195,7 +195,7 @@ namespace Gaming | |||
| { | |||
| if (player.PlayerState == PlayerStateType.TryingToAttack) | |||
| { | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| } | |||
| else | |||
| bullet.IsMoving = false; | |||
| @@ -216,7 +216,7 @@ namespace Gaming | |||
| return; | |||
| } | |||
| } | |||
| player.PlayerState = PlayerStateType.Addicted; | |||
| player.SetPlayerState(PlayerStateType.Addicted); | |||
| new Thread | |||
| (() => | |||
| { | |||
| @@ -252,10 +252,10 @@ namespace Gaming | |||
| new Thread | |||
| (() => | |||
| { | |||
| player.PlayerState = PlayerStateType.Stunned; | |||
| player.SetPlayerState(PlayerStateType.Stunned); | |||
| Thread.Sleep(time); | |||
| if (player.PlayerState == PlayerStateType.Stunned) | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| @@ -341,7 +341,7 @@ namespace Gaming | |||
| { | |||
| if (player == null || time <= 0) return false; | |||
| if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false; | |||
| player.PlayerState = PlayerStateType.Swinging; | |||
| player.SetPlayerState(PlayerStateType.Swinging); | |||
| new Thread | |||
| (() => | |||
| @@ -350,7 +350,7 @@ namespace Gaming | |||
| if (player.PlayerState == PlayerStateType.Swinging) | |||
| { | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| } | |||
| } | |||
| ) | |||
| @@ -57,7 +57,7 @@ namespace Gaming | |||
| if (player.PlayerState == PlayerStateType.Stunned) | |||
| { | |||
| player.AddScore(GameData.ScorePropRecoverFromDizziness); | |||
| player.PlayerState = PlayerStateType.Null; | |||
| player.SetPlayerState(); | |||
| } | |||
| break; | |||
| default: | |||
| @@ -28,6 +28,31 @@ namespace Gaming | |||
| { }); | |||
| } | |||
| public static bool ShowTime(Character player) | |||
| { | |||
| if ((!player.Commandable())) return false; | |||
| IActiveSkill skill = player.FindIActiveSkill(ActiveSkillType.ShowTime); | |||
| Debugger.Output(player, ": It's show time!"); | |||
| 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 | |||
| ) | |||
| .Start();*/ | |||
| }, | |||
| () => | |||
| { }); | |||
| } | |||
| public static bool BecomeInvisible(Character player) | |||
| { | |||
| if ((!player.Commandable())) return false; | |||
| @@ -180,7 +205,7 @@ namespace Gaming | |||
| { | |||
| if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) | |||
| { | |||
| character.PlayerState = PlayerStateType.Null; | |||
| character.SetPlayerState(); | |||
| character.HP = GameData.RemainHpWhenAddLife; | |||
| ((Student)character).TimeOfRescue = 0; | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| @@ -10,7 +10,7 @@ namespace Preparation.Interface | |||
| public int Score { get; } | |||
| public void AddScore(int add); | |||
| public double Vampire { get; } | |||
| public PlayerStateType PlayerState { get; set; } | |||
| public PlayerStateType PlayerState { get; } | |||
| public BulletType BulletOfPlayer { get; set; } | |||
| public bool IsGhost(); | |||
| @@ -91,6 +91,37 @@ namespace Preparation.Interface | |||
| public int speedOfOpenChest = (int)(GameData.basicSpeedOfOpenChest * 1.1); | |||
| public int SpeedOfOpenChest => speedOfOpenChest; | |||
| } | |||
| public class Idol : IGhostType | |||
| { | |||
| private const int moveSpeed = GameData.basicGhostMoveSpeed; | |||
| public int MoveSpeed => moveSpeed; | |||
| private const int maxHp = GameData.basicHp; | |||
| public int MaxHp => maxHp; | |||
| public BulletType InitBullet => BulletType.CommonAttackOfGhost; | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public double concealment = GameData.basicConcealment; | |||
| public double Concealment => concealment; | |||
| public int alertnessRadius = GameData.basicGhostAlertnessRadius; | |||
| public int AlertnessRadius => alertnessRadius; | |||
| public int viewRange = GameData.basicGhostViewRange * 11 / 10; | |||
| public int ViewRange => viewRange; | |||
| public int speedOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking; | |||
| public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; | |||
| public int speedOfClimbingThroughWindows = GameData.basicGhostSpeedOfClimbingThroughWindows; | |||
| public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | |||
| public int speedOfOpenChest = GameData.basicSpeedOfOpenChest; | |||
| public int SpeedOfOpenChest => speedOfOpenChest; | |||
| } | |||
| public class ANoisyPerson : IGhostType | |||
| { | |||
| private const int moveSpeed = (int)(GameData.basicGhostMoveSpeed * 1.07); | |||
| @@ -33,7 +33,7 @@ namespace Preparation.Interface | |||
| public class BecomeInvisible : IActiveSkill | |||
| { | |||
| public int SkillCD => GameData.commonSkillCD; | |||
| public int SkillCD => GameData.commonSkillCD * 2; | |||
| public int DurationTime => GameData.commonSkillTime * 6 / 10; | |||
| private readonly object commonSkillLock = new object(); | |||
| @@ -121,6 +121,21 @@ namespace Preparation.Interface | |||
| } | |||
| } | |||
| public class ShowTime : IActiveSkill | |||
| { | |||
| public int SkillCD => GameData.commonSkillCD * 3; | |||
| public int DurationTime => GameData.commonSkillTime; | |||
| private readonly object commonSkillLock = new object(); | |||
| public object ActiveSkillLock => commonSkillLock; | |||
| public bool isBeingUsed = false; | |||
| public bool IsBeingUsed | |||
| { | |||
| get => isBeingUsed; set => isBeingUsed = value; | |||
| } | |||
| } | |||
| public class JumpyBomb : IActiveSkill | |||
| { | |||
| public int SkillCD => GameData.commonSkillCD / 2; | |||
| @@ -137,7 +152,7 @@ namespace Preparation.Interface | |||
| public class UseKnife : IActiveSkill | |||
| { | |||
| public int SkillCD => GameData.commonSkillCD * 2 / 3; | |||
| public int SkillCD => GameData.commonSkillCD; | |||
| public int DurationTime => GameData.commonSkillTime / 10; | |||
| private readonly object commonSkillLock = new object(); | |||
| public object ActiveSkillLock => commonSkillLock; | |||
| @@ -100,6 +100,7 @@ namespace Preparation.Utility | |||
| Rouse = 11, | |||
| Encourage = 12, | |||
| Inspire = 13, | |||
| ShowTime = 14, | |||
| } | |||
| public enum PassiveSkillType | |||
| { | |||
| @@ -3,13 +3,14 @@ using System.Collections.Generic; | |||
| using GameClass.GameObj; | |||
| using System.Numerics; | |||
| using Preparation.Utility; | |||
| using Gaming; | |||
| namespace Server | |||
| { | |||
| public static class CopyInfo | |||
| { | |||
| public static MessageOfObj? Auto(GameObj gameObj) | |||
| public static MessageOfObj? Auto(GameObj gameObj, int time) | |||
| { | |||
| switch (gameObj.Type) | |||
| { | |||
| @@ -29,7 +30,7 @@ namespace Server | |||
| case Preparation.Utility.GameObjType.Generator: | |||
| return Classroom((Generator)gameObj); | |||
| case Preparation.Utility.GameObjType.Chest: | |||
| return Chest((Chest)gameObj); | |||
| return Chest((Chest)gameObj, time); | |||
| case Preparation.Utility.GameObjType.Doorway: | |||
| return Gate((Doorway)gameObj); | |||
| case Preparation.Utility.GameObjType.EmergencyExit: | |||
| @@ -223,13 +224,14 @@ namespace Server | |||
| msg.DoorMessage.IsOpen = door.IsOpen; | |||
| return msg; | |||
| } | |||
| private static MessageOfObj Chest(Chest chest) | |||
| 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; | |||
| msg.ChestMessage.Progress = chest.OpenDegree; | |||
| int progress = (chest.OpenStartTime > 0) ? ((time - chest.OpenStartTime) * chest.WhoOpen.SpeedOfOpenChest) : 0; | |||
| msg.ChestMessage.Progress = (progress > GameData.degreeOfOpenedChest) ? GameData.degreeOfOpenedChest : progress; | |||
| return msg; | |||
| } | |||
| } | |||
| @@ -135,22 +135,23 @@ namespace Server | |||
| currentGameInfo.ObjMessage.Add(currentMapMsg); | |||
| IsSpectatorJoin = false; | |||
| } | |||
| int time = game.GameMap.Timer.nowTime(); | |||
| foreach (GameObj gameObj in gameObjList) | |||
| { | |||
| MessageOfObj? msg = CopyInfo.Auto(gameObj); | |||
| if (msg != null) currentGameInfo.ObjMessage.Add(CopyInfo.Auto(gameObj)); | |||
| MessageOfObj? msg = CopyInfo.Auto(gameObj, time); | |||
| if (msg != null) currentGameInfo.ObjMessage.Add(msg); | |||
| } | |||
| lock (newsLock) | |||
| { | |||
| foreach (var news in currentNews) | |||
| { | |||
| MessageOfObj? msg = CopyInfo.Auto(news); | |||
| if (msg != null) currentGameInfo.ObjMessage.Add(CopyInfo.Auto(news)); | |||
| if (msg != null) currentGameInfo.ObjMessage.Add(msg); | |||
| } | |||
| currentNews.Clear(); | |||
| } | |||
| currentGameInfo.GameState = gameState; | |||
| currentGameInfo.AllMessage = GetMessageOfAll(); | |||
| currentGameInfo.AllMessage = GetMessageOfAll(time); | |||
| mwr?.WriteOne(currentGameInfo); | |||
| break; | |||
| default: | |||
| @@ -211,10 +212,10 @@ namespace Server | |||
| return false; | |||
| } | |||
| private MessageOfAll GetMessageOfAll() | |||
| private MessageOfAll GetMessageOfAll(int time) | |||
| { | |||
| MessageOfAll msg = new MessageOfAll(); | |||
| msg.GameTime = game.GameMap.Timer.nowTime(); | |||
| msg.GameTime = time; | |||
| msg.SubjectFinished = (int)game.GameMap.NumOfRepairedGenerators; | |||
| msg.StudentGraduated = (int)game.GameMap.NumOfEscapedStudent; | |||
| msg.StudentQuited = (int)game.GameMap.NumOfDeceasedStudent; | |||
| @@ -8,10 +8,10 @@ | |||
| - **极坐标以x坐标轴为极轴,角度逆时针为正方向**。 | |||
| - 地图由50 * 50个格子构成,其中每个格子代表1000 * 1000的正方形。每个格子的编号(CellX,CellY)可以计算得到: | |||
| - 略 | |||
| - 地图上的每个格子有自己的区域类型:陆地、墙、草地、教室、校门、隐藏校门、门、窗、箱子 | |||
| - 地图上格子有自己的区域类型:陆地、墙、草地、教室、校门、隐藏校门、门、窗、箱子 | |||
| ### 移动 | |||
| - `std::future<bool> Move(int64_t timeInMilliseconds, double angleInRadian)`:移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动的方向,单位弧度,使用极坐标,**竖直向下方向为x轴,水平向右方向为y轴** | |||
| - `std::future<bool> Move(int64_t timeInMilliseconds, double angleInRadian)`:移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为x轴,水平向右方向为y轴** | |||
| ### 使用技能 | |||
| - `std::future<bool> UseSkill(int32_t skillID)`:使用对应序号的主动技能 | |||
| @@ -20,6 +20,7 @@ | |||
| - 人物半径为800 | |||
| - 人物共有17种不可叠加的状态: | |||
| 1. (可)移动状态 | |||
| - `std::future<bool> EndAllAction()`:可以使处在下者八项状态中的玩家终止交互 | |||
| 2. 学习 | |||
| 3. 被勉励 | |||
| 4. 在勉励 | |||
| @@ -28,7 +29,7 @@ | |||
| 7. 使用技能 | |||
| 8. 开启校门 | |||
| 9. 唤醒他人中 | |||
| - 之后八项为不可行动状态 | |||
| 10. 被唤醒中 | |||
| 11. 沉迷 | |||
| 12. 退学 | |||
| @@ -38,9 +39,6 @@ | |||
| 16. 后摇 | |||
| 17. 翻窗 | |||
| - 其中后8项为不可行动状态 | |||
| - `std::future<bool> EndAllAction()`:可以使处在2-9状态的玩家终止对应交互,对于后8项状态无效 | |||
| ### 攻击 | |||
| - `std::future<bool> Attack(double angleInRadian)`:`angleInRadian`为攻击方向,无论近战远程均产生bullet表示攻击 | |||
| - 攻击类型CommonAttackOfGhost攻击未写完的作业,会造成对应攻击力的损坏 | |||
| @@ -67,17 +65,17 @@ | |||
| - 每张地图都有10间教室,学生需要完成其中的**7间**教室的作业,才可以开启任意校门。 | |||
| - `std::future<bool> StartLearning()`:在教室(旁)做作业 | |||
| - `std::future<bool> StartOpenGate()`:开启校门,所需时间为18秒,开启的进度不清空 | |||
| - 隐藏校门会在**3间**教室的作业完成的情况下在3-5个隐藏校门刷新点之一随机显现,当学生只剩1名时,隐藏校门将会自动打开。 | |||
| - 当**3间**教室的作业完成时,隐藏校门在3-5个刷新点之一随机显现;当只剩1名学生时,隐藏校门自动打开。 | |||
| - `std::future<bool> Graduate()`:从开启的校门或隐藏校门毕业。 | |||
| #### 勉励(Treat) | |||
| - `std::future<bool> StartTreatMate(int64_t mateID)`:勉励对应玩家ID的学生,当达到被勉励程度达到1500000或者最大血量与当前血量的差值时,勉励结束。 | |||
| - `std::future<bool> StartTreatMate(int64_t mateID)`:勉励对应玩家ID的学生,当达到被勉励程度达到1500000或者最大学习毅力与当前学习毅力的差值时,勉励完成,学生毅力增加对应被勉励程度。 | |||
| - 勉励中断时,被勉励程度保留;遭到攻击时被勉励程度清空 | |||
| #### 沉迷与唤醒 | |||
| - 当学生血量归零时,学生原地进入沉迷状态,每毫秒增加1沉迷度 | |||
| - `std::future<bool> StartRescueMate(int64_t mateID)`:唤醒对应玩家ID的沉迷的学生,需要时间1秒,之后血量恢复至1/2。沉迷程度不清空。 | |||
| - 进入沉迷状态时。如果学生原沉迷程度在(0,该玩家最大沉迷度/3)中,沉迷程度直接变为其最大沉迷度/3;原沉迷程度在[其最大沉迷度/3,其最大沉迷度x2/3)中,沉迷程度直接变为其最大沉迷度x2/3;原沉迷程度大于其最大沉迷度x2/3,从游戏中出局; | |||
| - 当学生学习毅力归零时,学生原地进入沉迷状态,每毫秒增加1沉迷度 | |||
| - `std::future<bool> StartRescueMate(int64_t mateID)`:唤醒对应玩家ID的沉迷的学生,需要时间1秒,之后学习毅力恢复至1/2。沉迷程度不清空。 | |||
| - 进入沉迷状态时,如果学生原沉迷程度在(0,该玩家最大沉迷度/3)中,沉迷程度直接变为其最大沉迷度/3;原沉迷程度在[其最大沉迷度/3,其最大沉迷度x2/3)中,沉迷程度直接变为其最大沉迷度x2/3;原沉迷程度大于其最大沉迷度x2/3,从游戏中出局; | |||
| - 当学生沉迷程度达到其最大沉迷程度时,从游戏中出局 | |||
| #### 门 | |||
| @@ -99,12 +97,12 @@ | |||
| 3. 学习的声音: 捣蛋鬼警戒半径内有人学习时收到;bgmVolume=(警戒半径x学习进度百分比)/二者距离 | |||
| ### 队内信息 | |||
| - `std::future<bool> SendMessage(int64_t, std::string)`:给同队的队友发送消息。`toPlayerID`指定发送的对象,`message`指定发送的内容。`message`的长度不能超过 64,否则将发送失败! | |||
| - `std::future<bool> SendMessage(int64_t, std::string)`:给同队的队友发送消息。第一个参数指定发送的对象,第二个参数指定发送的内容。 | |||
| - `bool HaveMessage()`:是否有队友发来的尚未接收的信息。 | |||
| - `std::pair<int64_t, std::string> GetMessage()`:从玩家ID为第一个参数的队友获取信息。 | |||
| ### 信息受限 | |||
| - 主动行为每 50ms 最多总共可以调用 50 次; | |||
| - `bool Wait()`:asynchronous 为 true 的情况下,选手可以调用此函数,阻塞当前线程,直到下一次消息更新时继续运行。 | |||
| ### 线程控制 | |||
| - `bool Wait()`:阻塞当前线程,直到下一次消息更新时继续运行。 | |||
| ### 信息获取 | |||
| `std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const` :返回所有可视学生的信息。 | |||
| @@ -189,11 +187,11 @@ | |||
| - 普通攻击为 搞蛋鬼的一般攻击 | |||
| - 主动技能 | |||
| - 隐身 | |||
| - CD:30s 持续时间:6s | |||
| - CD:60s 持续时间:6s | |||
| - 在持续时间内玩家隐身 | |||
| - 使用瞬间得分 | |||
| - 使用飞刀 | |||
| - CD:20s 持续时间:1s | |||
| - CD:30s 持续时间:1s | |||
| - 在持续时间内,攻击类型变为飞刀 | |||
| - 不直接得分 | |||
| @@ -412,14 +410,14 @@ public: | |||
| ### 交互 | |||
| - 在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令 | |||
| - 实际上救援或勉励不同的人是有效的 | |||
| - 实际上唤醒或勉励不同的人是有效的 | |||
| ### 破译与逃脱 | |||
| - 隐藏校门与校门对于人有碰撞体积 | |||
| - 一个校门同时最多可以由一人开启 | |||
| ### 攻击 | |||
| - 每次学生受到攻击后会损失对应子弹的攻击力的血量 | |||
| - 每次学生受到攻击后会损失对应子弹的攻击力的学习毅力 | |||
| - 前摇期间攻击被打断时,子弹消失。 | |||
| - 此处,前摇指 从播放攻击动作开始 攻击者不能交互 的时间 | |||