| @@ -88,7 +88,7 @@ namespace GameClass.GameObj | |||
| this.characterType = characterType; | |||
| this.SpeedOfOpeningOrLocking = Occupation.TimeOfOpeningOrLocking; | |||
| this.SpeedOfClimbingThroughWindows = Occupation.SpeedOfClimbingThroughWindows; | |||
| this.TimeOfOpenChest = Occupation.TimeOfOpenChest; | |||
| this.SpeedOfOpenChest = Occupation.TimeOfOpenChest; | |||
| foreach (var activeSkill in this.Occupation.ListOfIActiveSkill) | |||
| { | |||
| @@ -1,10 +1,49 @@ | |||
| using Preparation.Utility; | |||
| using Preparation.Interface; | |||
| using System; | |||
| namespace GameClass.GameObj | |||
| { | |||
| public class Student : Character | |||
| { | |||
| /// <summary> | |||
| /// 遭受攻击 | |||
| /// </summary> | |||
| /// <param name="subHP"></param> | |||
| /// <param name="hasSpear"></param> | |||
| /// <param name="attacker">伤害来源</param> | |||
| /// <returns>人物在受到攻击后死了吗</returns> | |||
| public bool BeAttacked(Bullet bullet) | |||
| { | |||
| lock (beAttackedLock) | |||
| { | |||
| if (hp <= 0 || NoHp()) | |||
| return false; // 原来已经死了 | |||
| if (bullet.Parent.TeamID != this.TeamID) | |||
| { | |||
| if (CanBeAwed()) PlayerState = PlayerStateType.Stunned; | |||
| if (HasShield) | |||
| { | |||
| if (bullet.HasSpear) | |||
| _ = TrySubHp(bullet.AP); | |||
| else | |||
| return false; | |||
| } | |||
| else | |||
| { | |||
| bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * TrySubHp(bullet.AP))); | |||
| } | |||
| #if DEBUG | |||
| Console.WriteLine($"PlayerID:{ID} is being shot! Now his hp is {hp}."); | |||
| #endif | |||
| if (hp <= 0) | |||
| TryActivatingLIFE(); // 如果有复活甲 | |||
| } | |||
| return hp <= 0; | |||
| } | |||
| } | |||
| protected int fixSpeed; | |||
| /// <summary> | |||
| /// 修理电机速度 | |||
| @@ -10,9 +10,10 @@ namespace GameClass.GameObj | |||
| { | |||
| public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 | |||
| { | |||
| private readonly object beAttackedLock = new(); | |||
| #region 装弹、攻击相关的基本属性及方法 | |||
| protected readonly object beAttackedLock = new(); | |||
| #region 装弹相关的基本属性及方法 | |||
| /// <summary> | |||
| /// 装弹冷却 | |||
| /// </summary> | |||
| @@ -93,43 +94,7 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 遭受攻击 | |||
| /// </summary> | |||
| /// <param name="subHP"></param> | |||
| /// <param name="hasSpear"></param> | |||
| /// <param name="attacker">伤害来源</param> | |||
| /// <returns>人物在受到攻击后死了吗</returns> | |||
| public bool BeAttacked(Bullet bullet) | |||
| { | |||
| lock (beAttackedLock) | |||
| { | |||
| if (hp <= 0) | |||
| return false; // 原来已经死了 | |||
| if (bullet.Parent.TeamID != this.TeamID) | |||
| { | |||
| if (HasShield) | |||
| { | |||
| if (bullet.HasSpear) | |||
| _ = TrySubHp(bullet.AP); | |||
| else | |||
| return false; | |||
| } | |||
| else | |||
| { | |||
| bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * TrySubHp(bullet.AP))); | |||
| } | |||
| #if DEBUG | |||
| Console.WriteLine($"PlayerID:{ID} is being shot! Now his hp is {hp}."); | |||
| #endif | |||
| if (hp <= 0) | |||
| TryActivatingLIFE(); // 如果有复活甲 | |||
| } | |||
| return hp <= 0; | |||
| } | |||
| } | |||
| /* | |||
| /// <summary> | |||
| /// 攻击被反弹,反弹伤害不会再被反弹 | |||
| /// </summary> | |||
| @@ -152,7 +117,7 @@ namespace GameClass.GameObj | |||
| } | |||
| return hp <= 0; | |||
| } | |||
| } | |||
| }*/ | |||
| #endregion | |||
| #region 感知相关的基本属性及方法 | |||
| /// <summary> | |||
| @@ -256,15 +221,15 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| private int timeOfOpenChest; | |||
| public int TimeOfOpenChest | |||
| private int speedOfOpenChest; | |||
| public int SpeedOfOpenChest | |||
| { | |||
| get => timeOfOpenChest; | |||
| get => speedOfOpenChest; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| timeOfOpenChest = value; | |||
| speedOfOpenChest = value; | |||
| } | |||
| } | |||
| } | |||
| @@ -372,12 +337,18 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public bool NoHp() => (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped | |||
| && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued); | |||
| public bool Commandable() => (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped | |||
| && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescuing | |||
| && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued | |||
| && playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack | |||
| && playerState != PlayerStateType.ClimbingThroughWindows && playerState != PlayerStateType.Stunned); | |||
| public bool InteractingWithMapWithoutMoving() => (playerState == PlayerStateType.LockingOrOpeningTheDoor || playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest); | |||
| public bool NullOrMoving() => (playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | |||
| public bool CanBeAwed() => !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped | |||
| || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued | |||
| || playerState == PlayerStateType.Treated || playerState != PlayerStateType.Stunned | |||
| || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | |||
| private int score = 0; | |||
| public int Score | |||
| @@ -542,7 +513,7 @@ namespace GameClass.GameObj | |||
| return false; | |||
| } | |||
| } | |||
| private void TryActivatingLIFE() | |||
| protected void TryActivatingLIFE() | |||
| { | |||
| if (buffManager.TryActivatingLIFE()) | |||
| { | |||
| @@ -20,15 +20,20 @@ namespace GameClass.GameObj | |||
| private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() }; | |||
| public Prop[] PropInChest => propInChest; | |||
| private bool isOpen = false; | |||
| public bool IsOpen | |||
| private int openDegree = 0; | |||
| public int OpenDegree | |||
| { | |||
| get => isOpen; | |||
| get => openDegree; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| isOpen = value; | |||
| if (value > 0) | |||
| lock (gameObjLock) | |||
| openDegree = (value > GameData.degreeOfOpeningChest) ? GameData.degreeOfOpeningChest : value; | |||
| else | |||
| lock (gameObjLock) | |||
| openDegree = 0; | |||
| } | |||
| } | |||
| public bool IsOpen() => (OpenDegree == GameData.degreeOfOpeningChest); | |||
| } | |||
| } | |||
| @@ -14,12 +14,8 @@ namespace GameClass.GameObj | |||
| this.place = placeType; | |||
| this.CanMove = false; | |||
| } | |||
| public override bool IsRigid => true; | |||
| public override bool IsRigid => !isOpen; | |||
| public override ShapeType Shape => ShapeType.Square; | |||
| protected override bool IgnoreCollideExecutor(IGameObj targetObj) | |||
| { | |||
| return isOpen; | |||
| } | |||
| private bool isOpen = false; | |||
| public bool IsOpen | |||
| @@ -38,8 +34,12 @@ namespace GameClass.GameObj | |||
| get => openOrLockDegree; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| openOrLockDegree = (value > 0) ? value : 0; | |||
| if (value > 0) | |||
| lock (gameObjLock) | |||
| openOrLockDegree = (value > GameData.degreeOfLockingOrOpeningTheDoor) ? GameData.degreeOfLockingOrOpeningTheDoor : value; | |||
| else | |||
| lock (gameObjLock) | |||
| openOrLockDegree = 0; | |||
| } | |||
| } | |||
| } | |||
| @@ -25,5 +25,28 @@ namespace GameClass.GameObj | |||
| return true; // 不是鬼不碰撞 | |||
| return false; | |||
| } | |||
| private bool canOpen = false; | |||
| public bool CanOpen | |||
| { | |||
| get => canOpen; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| canOpen = value; | |||
| } | |||
| } | |||
| private bool isOpen = false; | |||
| public bool IsOpen | |||
| { | |||
| get => isOpen; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| isOpen = value; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -13,6 +13,28 @@ namespace GameClass.GameObj | |||
| private readonly Dictionary<uint, XY> birthPointList; // 出生点列表 | |||
| public Dictionary<uint, XY> BirthPointList => birthPointList; | |||
| private object lockForNum = new(); | |||
| private uint numOfRepairedGenerators = 0; | |||
| public uint NumOfRepairedGenerators | |||
| { | |||
| get => numOfRepairedGenerators; | |||
| set | |||
| { | |||
| lock (lockForNum) | |||
| numOfRepairedGenerators = value; | |||
| } | |||
| } | |||
| private uint numOfSurvivingStudent = GameData.numOfStudent; | |||
| public uint NumOfSurvivingStudent | |||
| { | |||
| get => numOfSurvivingStudent; | |||
| set | |||
| { | |||
| lock (lockForNum) | |||
| numOfSurvivingStudent = value; | |||
| } | |||
| } | |||
| private Dictionary<GameObjType, IList<IGameObj>> gameObjDict; | |||
| public Dictionary<GameObjType, IList<IGameObj>> GameObjDict => gameObjDict; | |||
| private Dictionary<GameObjType, ReaderWriterLockSlim> gameObjLockDict; | |||
| @@ -31,7 +53,6 @@ namespace GameClass.GameObj | |||
| return PlaceType.Null; | |||
| } | |||
| } | |||
| public PlaceType GetPlaceType(XY pos) | |||
| { | |||
| try | |||
| @@ -58,34 +58,33 @@ namespace Gaming | |||
| .Start(); | |||
| if (generatorForFix.DegreeOfRepair >= GameData.degreeOfFixedGenerator) | |||
| { | |||
| gameMap.NumOfRepairedGenerators++; | |||
| if (player.PlayerState == PlayerStateType.Fixing) player.PlayerState = PlayerStateType.Null; | |||
| Doorway exit = (Doorway)gameMap.GameObjDict[GameObjType.Doorway][1]; | |||
| if (!exit.PowerSupply) | |||
| if (gameMap.NumOfRepairedGenerators == GameData.numOfGeneratorRequiredForEmergencyExit) | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Generator].EnterReadLock(); | |||
| gameMap.GameObjLockDict[GameObjType.EmergencyExit].EnterWriteLock(); | |||
| try | |||
| { | |||
| int numOfFixedGenerator = 0; | |||
| foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator]) | |||
| if (generator.DegreeOfRepair == GameData.degreeOfFixedGenerator) | |||
| ++numOfFixedGenerator; | |||
| if (numOfFixedGenerator >= GameData.numOfGeneratorRequiredForRepair) | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Doorway].EnterWriteLock(); | |||
| try | |||
| { | |||
| foreach (Doorway doorway in gameMap.GameObjDict[GameObjType.Doorway]) | |||
| doorway.PowerSupply = true; | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Doorway].ExitWriteLock(); | |||
| } | |||
| } | |||
| Random r = new Random(Environment.TickCount); | |||
| ((EmergencyExit)(gameMap.GameObjDict[GameObjType.EmergencyExit][r.Next(0, gameMap.GameObjDict[GameObjType.EmergencyExit].Count)])).CanOpen = true; | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Generator].ExitReadLock(); | |||
| gameMap.GameObjLockDict[GameObjType.EmergencyExit].ExitWriteLock(); | |||
| } | |||
| } | |||
| else | |||
| if (gameMap.NumOfRepairedGenerators == GameData.numOfGeneratorRequiredForRepair) | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Doorway].EnterWriteLock(); | |||
| try | |||
| { | |||
| foreach (Doorway doorway in gameMap.GameObjDict[GameObjType.Doorway]) | |||
| doorway.PowerSupply = true; | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Doorway].ExitWriteLock(); | |||
| } | |||
| } | |||
| @@ -141,33 +140,28 @@ namespace Gaming | |||
| { | |||
| if (!(player.Commandable())) | |||
| return false; | |||
| Doorway? doorwayForEscape = null; | |||
| gameMap.GameObjLockDict[GameObjType.Doorway].EnterReadLock(); | |||
| try | |||
| Doorway? doorwayForEscape = (Doorway?)gameMap.OneInTheSameCell(player.Position, GameObjType.Doorway); | |||
| if (doorwayForEscape != null) | |||
| { | |||
| foreach (Doorway doorway in gameMap.GameObjDict[GameObjType.Doorway]) | |||
| if (doorwayForEscape.IsOpen()) | |||
| { | |||
| if (GameData.IsInTheSameCell(doorway.Position, player.Position)) | |||
| { | |||
| doorwayForEscape = doorway; | |||
| break; | |||
| } | |||
| player.Die(PlayerStateType.Escaped); | |||
| return true; | |||
| } | |||
| else | |||
| return false; | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Doorway].ExitReadLock(); | |||
| } | |||
| if (doorwayForEscape != null && doorwayForEscape.IsOpen()) | |||
| else | |||
| { | |||
| player.Die(PlayerStateType.Escaped); | |||
| return true; | |||
| EmergencyExit? emergencyExit = (EmergencyExit?)gameMap.OneInTheSameCell(player.Position, GameObjType.EmergencyExit); | |||
| if (emergencyExit != null && emergencyExit.IsOpen) | |||
| { | |||
| player.Die(PlayerStateType.Escaped); | |||
| return true; | |||
| } | |||
| else | |||
| return false; | |||
| } | |||
| else | |||
| return false; | |||
| } | |||
| public bool Treat(Student player, Student playerTreated) | |||
| @@ -263,7 +257,7 @@ namespace Gaming | |||
| return false; | |||
| Chest? chestToOpen = (Chest?)gameMap.OneForInteract(player.Position, GameObjType.Chest); | |||
| if (chestToOpen == null || chestToOpen.IsOpen) | |||
| if (chestToOpen == null || chestToOpen.IsOpen() || chestToOpen.OpenDegree > 0) | |||
| return false; | |||
| player.PlayerState = PlayerStateType.OpeningTheChest; | |||
| @@ -271,12 +265,11 @@ namespace Gaming | |||
| ( | |||
| () => | |||
| { | |||
| int OpenDegree = 0; | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheChest && gameMap.Timer.IsGaming && OpenDegree < player.TimeOfOpenChest, | |||
| loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheChest && gameMap.Timer.IsGaming && (!chestToOpen.IsOpen()), | |||
| loopToDo: () => | |||
| { | |||
| OpenDegree += GameData.frameDuration; | |||
| chestToOpen.OpenDegree += GameData.frameDuration * player.SpeedOfOpenChest; | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| @@ -284,9 +277,8 @@ namespace Gaming | |||
| .Start(); | |||
| if (OpenDegree >= player.TimeOfOpenChest) | |||
| if (chestToOpen.IsOpen()) | |||
| { | |||
| chestToOpen.IsOpen = true; | |||
| if (player.PlayerState == PlayerStateType.OpeningTheChest) | |||
| player.PlayerState = PlayerStateType.Null; | |||
| for (int i = 0; i < GameData.maxNumOfPropInChest; ++i) | |||
| @@ -297,6 +289,8 @@ namespace Gaming | |||
| gameMap.Add(prop); | |||
| } | |||
| } | |||
| else chestToOpen.OpenDegree = 0; | |||
| } | |||
| ) | |||
| @@ -404,8 +398,7 @@ namespace Gaming | |||
| loopCondition: () => flag && player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor, | |||
| loopToDo: () => | |||
| { | |||
| Character? character = (Character?)gameMap.OneInTheSameCell(doorToLock.Position, GameObjType.Character); | |||
| flag = (character != null); | |||
| flag = ((gameMap.OneInTheSameCell(doorToLock.Position, GameObjType.Character)) != null); | |||
| doorToLock.OpenOrLockDegree += GameData.frameDuration * player.SpeedOfOpeningOrLocking; | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| @@ -98,15 +98,6 @@ namespace Gaming | |||
| { | |||
| player.Die(PlayerStateType.Deceased); | |||
| // gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); | |||
| // try | |||
| //{ | |||
| // gameMap.GameObjDict[GameObjType.Character].Remove(playerBeingShot); | |||
| // } | |||
| // finally | |||
| //{ | |||
| // gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); | |||
| // } | |||
| for (int i = 0; i < GameData.maxNumOfPropInPropInventory; i++) | |||
| { | |||
| @@ -117,34 +108,28 @@ namespace Gaming | |||
| gameMap.Add(prop); | |||
| } | |||
| } | |||
| --gameMap.NumOfSurvivingStudent; | |||
| if (gameMap.NumOfSurvivingStudent == 1) | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.EmergencyExit].EnterReadLock(); | |||
| try | |||
| { | |||
| foreach (EmergencyExit emergencyExit in gameMap.GameObjDict[GameObjType.EmergencyExit]) | |||
| if (emergencyExit.CanOpen) | |||
| { | |||
| emergencyExit.IsOpen = true; | |||
| break; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.EmergencyExit].ExitReadLock(); | |||
| } | |||
| } | |||
| // player.Reset(); | |||
| // ((Character?)bullet.Parent)?.AddScore(GameData.addScoreWhenKillOneLevelPlayer); // 给击杀者加分 | |||
| /* new Thread | |||
| (() => | |||
| { | |||
| Thread.Sleep(GameData.reviveTime); | |||
| playerBeingShot.AddShield(GameData.shieldTimeAtBirth); // 复活加个盾 | |||
| // gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); | |||
| // try | |||
| //{ | |||
| // gameMap.GameObjDict[GameObjType.Character].Add(playerBeingShot); | |||
| // } | |||
| // finally { gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); } | |||
| if (gameMap.Timer.IsGaming) | |||
| { | |||
| playerBeingShot.CanMove = true; | |||
| } | |||
| playerBeingShot.Deceased = false; | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| */ | |||
| } | |||
| private void BombObj(Bullet bullet, GameObj objBeingShot) | |||
| @@ -154,7 +139,7 @@ namespace Gaming | |||
| case GameObjType.Character: | |||
| if ((!((Character)objBeingShot).IsGhost()) && bullet.Parent.IsGhost()) | |||
| if (((Character)objBeingShot).BeAttacked(bullet)) | |||
| if (((Student)objBeingShot).BeAttacked(bullet)) | |||
| { | |||
| BeAddictedToGame((Student)objBeingShot); | |||
| } | |||
| @@ -62,21 +62,7 @@ namespace Gaming | |||
| Prop pickProp = new NullProp(); | |||
| if (propType == PropType.Null) // 自动检查有无道具可捡 | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Prop].EnterReadLock(); | |||
| try | |||
| { | |||
| foreach (Prop prop in gameMap.GameObjDict[GameObjType.Prop]) | |||
| { | |||
| if (GameData.IsInTheSameCell(prop.Position, player.Position)) | |||
| { | |||
| player.PropInventory[indexing] = prop; | |||
| } | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Prop].ExitReadLock(); | |||
| } | |||
| pickProp = player.PropInventory[indexing] = ((Prop?)gameMap.OneInTheSameCell(player.Position, GameObjType.Prop)) ?? new NullProp(); | |||
| } | |||
| else | |||
| { | |||
| @@ -89,7 +75,7 @@ namespace Gaming | |||
| { | |||
| if (GameData.IsInTheSameCell(prop.Position, player.Position) && prop.CanMove == false) | |||
| { | |||
| player.PropInventory[indexing] = prop; | |||
| pickProp = player.PropInventory[indexing] = prop; | |||
| } | |||
| } | |||
| } | |||
| @@ -64,7 +64,7 @@ namespace Preparation.Interface | |||
| public int speedOfClimbingThroughWindows = GameData.basicGhostSpeedOfClimbingThroughWindows; | |||
| public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | |||
| public int timeOfOpenChest = GameData.basicTimeOfOpenChest; | |||
| public int timeOfOpenChest = GameData.basicSpeedOfOpenChest; | |||
| public int TimeOfOpenChest => timeOfOpenChest; | |||
| } | |||
| public class Athlete : IStudent | |||
| @@ -107,7 +107,7 @@ namespace Preparation.Interface | |||
| public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows * 12 / 10; | |||
| public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | |||
| public int timeOfOpenChest = GameData.basicTimeOfOpenChest; | |||
| public int timeOfOpenChest = GameData.basicSpeedOfOpenChest; | |||
| public int TimeOfOpenChest => timeOfOpenChest; | |||
| } | |||
| } | |||
| @@ -21,7 +21,6 @@ namespace Preparation.Utility | |||
| public const int numOfBirthPoint = 5; | |||
| // public const int numOfGenerator = 9; | |||
| public const int numOfGeneratorRequiredForRepair = 7; | |||
| public const int numOfChest = 8; | |||
| private const int numOfObjNotMap = 5; | |||
| @@ -61,6 +60,7 @@ namespace Preparation.Utility | |||
| } | |||
| #endregion | |||
| #region 角色相关 | |||
| public const int numOfStudent = 4; | |||
| public const int characterRadius = numOfPosGridPerCell / 2 / 5 * 4; // 人物半径 | |||
| public const int basicTreatSpeed = 100; | |||
| @@ -68,7 +68,7 @@ namespace Preparation.Utility | |||
| public const int basicSpeedOfOpeningOrLocking = 3280; | |||
| public const int basicStudentSpeedOfClimbingThroughWindows = 611; | |||
| public const int basicGhostSpeedOfClimbingThroughWindows = 1270; | |||
| public const int basicTimeOfOpenChest = 10000; | |||
| public const int basicSpeedOfOpenChest = 1000; | |||
| public const int basicHp = 3000000; // 初始血量 | |||
| public const int basicMaxGamingAddiction = 60000;//基本完全沉迷时间 | |||
| @@ -141,8 +141,11 @@ namespace Preparation.Utility | |||
| #region 物体相关 | |||
| public const int degreeOfFixedGenerator = 10300000; | |||
| public const int degreeOfLockingOrOpeningTheDoor = 10000; | |||
| public const int degreeOfOpeningChest = 10000; | |||
| public const int degreeOfOpenedDoorway = 18000; | |||
| public const int maxNumOfPropInChest = 2; | |||
| public const int numOfGeneratorRequiredForRepair = 7; | |||
| public const int numOfGeneratorRequiredForEmergencyExit = 3; | |||
| #endregion | |||
| #region 游戏帧相关 | |||
| public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | |||
| @@ -28,7 +28,7 @@ | |||
| - 只展示外部需要的属性,部分属性被省略 | |||
| ### BgmType | |||
| - *枚举类BgmType* | |||
| - 枚举类BgmType | |||
| 1. 不详的感觉:监管者进入(求生者的警戒半径/监管者的隐蔽度)时,求生者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离) | |||
| 2. 期待搞事的感觉:求生者进入(监管者的警戒半径/求生者的隐蔽度)时,监管者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/可被发觉的最近的求生者距离) | |||
| 3. 修理电机的声音: 监管者警戒半径内有电机正在被修理时收到;bgmVolume=(警戒半径*电机修理程度/二者距离)/10300000 | |||
| @@ -81,22 +81,23 @@ | |||
| public enum PlayerStateType | |||
| { | |||
| Null = 0, | |||
| IsAddicted = 1, | |||
| IsEscaped = 2, | |||
| IsSwinging = 3,//指后摇 | |||
| IsDeceased = 4, | |||
| IsMoving = 5, | |||
| IsTreating = 6, | |||
| IsRescuing = 7, | |||
| IsFixing = 8, | |||
| IsTreated = 9, | |||
| IsRescued = 10, | |||
| IsStunned = 11, | |||
| IsTryingToAttack = 12,//指前摇 | |||
| IsLockingTheDoor = 13, | |||
| IsOpeningTheChest = 14, | |||
| IsClimbingThroughWindows = 15, | |||
| IsUsingSpecialSkill = 16, | |||
| Addicted = 1, | |||
| Escaped = 2, | |||
| Swinging = 3,//指后摇 | |||
| Deceased = 4, | |||
| Moving = 5, | |||
| Treating = 6, | |||
| Rescuing = 7, | |||
| Fixing = 8, | |||
| Treated = 9, | |||
| Rescued = 10, | |||
| Stunned = 11, | |||
| TryingToAttack = 12,//指前摇 | |||
| LockingOrOpeningTheDoor = 13, | |||
| OpeningTheChest = 14, | |||
| ClimbingThroughWindows = 15, | |||
| UsingSkill = 16, | |||
| OpeningTheDoorWay = 17, | |||
| } | |||
| ~~~ | |||
| - 可执行指令的(不用给选手) | |||
| @@ -124,7 +125,7 @@ | |||
| - double 隐蔽度 | |||
| - 翻窗时间 | |||
| - 开锁门速度 | |||
| - *开箱速度* | |||
| - 开箱速度 | |||
| - 视野范围 | |||
| ### 学生:人物 | |||
| @@ -174,22 +175,21 @@ | |||
| - 开启进度 | |||
| ### 紧急出口:物体 | |||
| - *是否显现* | |||
| - *是否打开* | |||
| - 是否显现 | |||
| - 是否打开 | |||
| ### 墙:物体 | |||
| ### 窗:物体 | |||
| - *正在翻窗的人* | |||
| - 正在翻窗的人 | |||
| ### 箱子:物体 | |||
| - *是否开启* | |||
| - *开箱进度* | |||
| - 开箱进度 | |||
| ### 门:物体 | |||
| - *属于那个教学区* | |||
| - *是否锁上* | |||
| - *开锁门进度* | |||
| - 属于那个教学区 | |||
| - 是否锁上 | |||
| - 开锁门进度 | |||
| - 不提供是否可以锁上的属性 | |||
| ### 电机(建议称为homework):物体 | |||
| @@ -205,14 +205,14 @@ | |||
| - 除了逃离和翻窗,交互目标与交互者在一个九宫格内则为可交互 | |||
| - 逃离时学生应当与出口在同一格子内 | |||
| - 翻窗时玩家应当在窗前后一个格子内 | |||
| - *在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令* | |||
| - 在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令 | |||
| ### 破译与逃脱 | |||
| - *每张地图都会刷新 9台电机,求生者需要破译其中的7台*,并开启任意一个大门后从任意一个开启的大门- 逃脱,亦或者在只剩1名求生者的情况下从紧急出口逃脱; | |||
| - 每张地图都会刷新 9台电机,求生者需要破译其中的7台,并开启任意一个大门后从任意一个开启的大门- 逃脱,亦或者在只剩1名求生者的情况下从紧急出口逃脱; | |||
| - 求生者和监管者在靠近电机时,可以看到电机的破译进度条。 | |||
| - 紧急出口会在电机破译完成3台的情况下在地图的3-5个固定紧急出口刷新点之一随机刷新。 | |||
| - 当求生者只剩1名时,紧急出口盖将会自动打开,该求生者可从紧急出口逃脱。 | |||
| - *开启大门所需时间为18秒。* | |||
| - 开启大门所需时间为18秒。 | |||
| - 大门开启的进度不清空 | |||
| - 一个大门同时最多可以由一人开启 | |||
| @@ -222,7 +222,7 @@ | |||
| - 无论搞蛋鬼或学生,攻击时,从播放攻击动作到可以开始产生伤害的期间,称为前摇。(前摇阶段,搞蛋鬼产生通常为不可爆炸(部分搞蛋鬼能可以产生可爆炸)子弹(爆炸范围=0),[子弹大小待商榷],期间监管者攻击被打断时,子弹消失)(无论近战远程均产生子弹) | |||
| - 无论搞蛋鬼或学生,攻击后,通常会出现一段无伤害判定的攻击动作后置时间,称为后摇。击中物体时后摇更长 | |||
| - *假如监管者攻击或一些监管者的特定技能击中正在交互的求生者,将使求生者眩晕* | |||
| - 假如监管者攻击或一些监管者的特定技能击中正在交互或处于攻击前后摇或使用部分技能(指PlayerState==UsingSkill)的求生者,将使求生者眩晕 | |||
| 1. 处于前摇或后摇 | |||
| 2. 治疗或解救他人 | |||
| 3. 修理电机 | |||
| @@ -231,16 +231,16 @@ | |||
| 6. 开启箱子 | |||
| ### 门 | |||
| - *门分别属于三个教学区:三教,五教,六教* | |||
| - *监管者或求生者都需要拿到对应教学区的钥匙才能打开或锁住对应的门* | |||
| - *锁门和开门都需要一定时间,进出门为正常移动过程* | |||
| - *门只有开、锁两种状态,锁住时门有碰撞体积* | |||
| - *当门所在格子内有人时,无法锁门(必须从门外锁门)* | |||
| - *锁门时其他人可以进入门所在格子,锁门过程中断* | |||
| - *钥匙只会出现在箱子中,每个教学区都有2把钥匙* | |||
| - *一扇门只允许同时一个人开锁门* | |||
| - *开锁门未完成前,门状态表现为原来的状态* | |||
| - *开锁门进度中断后清空* | |||
| - 门分别属于三个教学区:三教,五教,六教 | |||
| - 监管者或求生者都需要拿到对应教学区的钥匙才能打开或锁住对应的门 | |||
| - 锁门和开门都需要一定时间,进出门为正常移动过程 | |||
| - 门只有开、锁两种状态,锁住时门有碰撞体积 | |||
| - 当门所在格子内有人时,无法锁门(必须从门外锁门) | |||
| - 锁门时其他人可以进入门所在格子,锁门过程中断 | |||
| - 钥匙只会出现在箱子中,每个教学区都有2把钥匙 | |||
| - 一扇门只允许同时一个人开锁门 | |||
| - 开锁门未完成前,门状态表现为原来的状态 | |||
| - 开锁门进度中断后清空 | |||
| ### 窗 | |||
| - 求生者和监管者都可以翻越窗户,但通常情况下监管者翻越窗户的速度**高于**求生者。 | |||
| @@ -253,11 +253,11 @@ | |||
| - 窗必须在两个墙之间,另外两侧不能为墙 | |||
| ### 箱子 | |||
| - *监管者和求生者都能与箱子交互,同一时刻只允许一人进行开启* | |||
| - *开启箱子有不同概率获得不同道具。* | |||
| - *开启箱子的基础持续时间为10秒。* | |||
| - *未开启完成的箱子在下一次需要重新开始开启。* | |||
| - *箱子开启后其中道具才可以被观测和拿取* | |||
| - 监管者和求生者都能与箱子交互,同一时刻只允许一人进行开启 | |||
| - 开启箱子有不同概率获得不同道具。 | |||
| - 开启箱子的基础持续时间为10秒。 | |||
| - 未开启完成的箱子在下一次需要重新开始开启。 | |||
| - 箱子开启后其中道具才可以被观测和拿取 | |||
| - [箱子道具不刷新] | |||
| - [箱子不可被关闭] | |||
| - [箱子内道具最多两个] | |||
| @@ -294,4 +294,4 @@ | |||
| ### 救人 | |||
| - 一般情况下,救人时间为1秒。 | |||
| - *不能两人同时救一个人* | |||
| - 不能两人同时救一个人 | |||