| @@ -76,6 +76,7 @@ enum PlayerState | |||
| CLIMBING = 15; // 翻窗 | |||
| OPENING_A_CHEST =16; | |||
| USING_SPECIAL_SKILL = 17; | |||
| OPENING_A_GATE =18; | |||
| } | |||
| enum TrickerBuffType // 屠夫可用的增益效果类型 | |||
| @@ -23,7 +23,7 @@ namespace GameClass.GameObj | |||
| /// <summary> | |||
| /// 原初修理电机速度 | |||
| /// </summary> | |||
| public int OrgFixSpeed { get; protected set; } = GameData.basicFixSpeed; | |||
| public int OrgFixSpeed { get; protected set; } | |||
| protected int treatSpeed = GameData.basicTreatSpeed; | |||
| public int TreatSpeed | |||
| @@ -37,7 +37,7 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| } | |||
| public int OrgTreatSpeed { get; protected set; } = GameData.basicTreatSpeed; | |||
| public int OrgTreatSpeed { get; protected set; } | |||
| public int MaxGamingAddiction { get; protected set; } | |||
| private int gamingAddiction; | |||
| @@ -98,7 +98,7 @@ namespace GameClass.GameObj | |||
| public Student(XY initPos, int initRadius, CharacterType characterType) : base(initPos, initRadius, characterType) | |||
| { | |||
| this.fixSpeed = ((IStudent)Occupation).FixSpeed; | |||
| this.OrgFixSpeed = this.fixSpeed = ((IStudent)Occupation).FixSpeed; | |||
| } | |||
| } | |||
| } | |||
| @@ -76,7 +76,7 @@ namespace GameClass.GameObj | |||
| && playerState != PlayerStateType.IsAddicted && playerState != PlayerStateType.IsStunned | |||
| && playerState != PlayerStateType.IsSwinging && playerState != PlayerStateType.IsTryingToAttack | |||
| && playerState != PlayerStateType.IsClimbingThroughWindows); | |||
| public bool InteractingWithMapWithoutMoving() => (playerState == PlayerStateType.IsLockingTheDoor || playerState == PlayerStateType.IsFixing || playerState == PlayerStateType.IsOpeningTheChest); | |||
| public bool InteractingWithMapWithoutMoving() => (playerState == PlayerStateType.IsLockingOrOpeningTheDoor || playerState == PlayerStateType.IsFixing || playerState == PlayerStateType.IsOpeningTheChest); | |||
| public bool NullOrMoving() => (playerState == PlayerStateType.Null || playerState == PlayerStateType.IsMoving); | |||
| // private int deathCount = 0; | |||
| @@ -36,6 +36,32 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public bool IsOpen => powerSupply; | |||
| private bool isOpening = false; | |||
| public bool IsOpening | |||
| { | |||
| get => isOpening; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| isOpening = value; | |||
| } | |||
| } | |||
| private int openDegree = 0; | |||
| public int OpenDegree | |||
| { | |||
| get => openDegree; | |||
| set | |||
| { | |||
| if (value > 0) | |||
| lock (gameObjLock) | |||
| openDegree = (value < GameData.degreeOfOpenedDoorway) ? value : GameData.degreeOfOpenedDoorway; | |||
| else | |||
| lock (gameObjLock) | |||
| openDegree = 0; | |||
| } | |||
| } | |||
| public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedDoorway); | |||
| } | |||
| } | |||
| @@ -120,6 +120,27 @@ namespace GameClass.GameObj | |||
| GameObjLockDict[gameObj.Type].ExitWriteLock(); | |||
| } | |||
| } | |||
| public GameObj? OneForInteract(XY Pos, GameObjType gameObjType) | |||
| { | |||
| GameObj? GameObjForInteract = null; | |||
| GameObjLockDict[gameObjType].EnterReadLock(); | |||
| try | |||
| { | |||
| foreach (GameObj gameObj in GameObjDict[gameObjType]) | |||
| { | |||
| if (GameData.ApproachToInteract(gameObj.Position, Pos)) | |||
| { | |||
| GameObjForInteract = gameObj; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| GameObjLockDict[gameObjType].ExitReadLock(); | |||
| } | |||
| return GameObjForInteract; | |||
| } | |||
| public Map(uint[,] mapResource) | |||
| { | |||
| gameObjDict = new Dictionary<GameObjType, IList<IGameObj>>(); | |||
| @@ -15,7 +15,7 @@ namespace Gaming | |||
| // 人物移动 | |||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (playerToMove.PlayerState != PlayerStateType.Null) return false; | |||
| if (!playerToMove.Commandable()) return false; | |||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection); | |||
| return true; | |||
| } | |||
| @@ -32,7 +32,7 @@ namespace Gaming | |||
| public bool Fix(Student player)// 自动检查有无发电机可修 | |||
| { | |||
| if (player.IsGhost() || (player.PlayerState != PlayerStateType.Null && player.PlayerState != PlayerStateType.IsMoving)) | |||
| if (player.IsGhost() || (!player.Commandable()) || player.PlayerState == PlayerStateType.IsFixing) | |||
| return false; | |||
| Generator? generatorForFix = null; | |||
| @@ -108,6 +108,45 @@ namespace Gaming | |||
| } | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| return true; | |||
| } | |||
| public bool OpenDoorWay(Student player) | |||
| { | |||
| if (!(player.Commandable()) || player.PlayerState == PlayerStateType.IsOpeningTheDoorWay) | |||
| return false; | |||
| Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); | |||
| if (doorwayToOpen == null || doorwayToOpen.IsOpening || !doorwayToOpen.PowerSupply) | |||
| return false; | |||
| player.PlayerState = PlayerStateType.IsOpeningTheDoorWay; | |||
| doorwayToOpen.IsOpening = true; | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => doorwayToOpen.IsOpening && player.PlayerState == PlayerStateType.IsOpeningTheDoorWay && gameMap.Timer.IsGaming && doorwayToOpen.OpenDegree < GameData.degreeOfOpenedDoorway && GameData.ApproachToInteract(player.Position, doorwayToOpen.Position), | |||
| loopToDo: () => | |||
| { | |||
| doorwayToOpen.OpenDegree += GameData.frameDuration; | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| doorwayToOpen.IsOpening = false; | |||
| if (doorwayToOpen.OpenDegree >= GameData.degreeOfOpenedDoorway) | |||
| { | |||
| if (player.PlayerState == PlayerStateType.IsOpeningTheDoorWay) | |||
| player.PlayerState = PlayerStateType.Null; | |||
| } | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| @@ -116,7 +155,7 @@ namespace Gaming | |||
| public bool Escape(Student player) | |||
| { | |||
| if (!(player.PlayerState == PlayerStateType.Null || player.PlayerState == PlayerStateType.IsMoving) || player.IsGhost()) | |||
| if (!(player.Commandable())) | |||
| return false; | |||
| Doorway? doorwayForEscape = null; | |||
| @@ -149,9 +188,9 @@ namespace Gaming | |||
| public bool Treat(Student player, Student playerTreated) | |||
| { | |||
| if (!((playerTreated.NullOrMoving() || playerTreated.InteractingWithMapWithoutMoving()) | |||
| && (player.NullOrMoving() || player.InteractingWithMapWithoutMoving())) | |||
| || playerTreated.HP == playerTreated.MaxHp || !GameData.ApproachToInteract(playerTreated.Position, player.Position)) | |||
| if ((!player.Commandable()) || player.PlayerState == PlayerStateType.IsTreating || | |||
| (!playerTreated.Commandable()) || | |||
| playerTreated.HP == playerTreated.MaxHp || !GameData.ApproachToInteract(playerTreated.Position, player.Position)) | |||
| return false; | |||
| if (playerTreated.HP + playerTreated.DegreeOfTreatment >= playerTreated.MaxHp) | |||
| @@ -205,7 +244,7 @@ namespace Gaming | |||
| public bool Rescue(Student player, Student playerRescued) | |||
| { | |||
| if (player.PlayerState != PlayerStateType.Null || playerRescued.PlayerState != PlayerStateType.IsAddicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position)) | |||
| if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.IsAddicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position)) | |||
| return false; | |||
| player.PlayerState = PlayerStateType.IsRescuing; | |||
| playerRescued.PlayerState = PlayerStateType.IsRescued; | |||
| @@ -247,7 +286,7 @@ namespace Gaming | |||
| public bool OpenChest(Character player) | |||
| { | |||
| if (!player.Commandable() || player.PlayerState == PlayerStateType.IsOpeningTheChest) | |||
| if ((!player.Commandable()) || player.PlayerState == PlayerStateType.IsOpeningTheChest) | |||
| return false; | |||
| Chest? chestToOpen = null; | |||
| @@ -313,7 +313,7 @@ namespace Gaming | |||
| return false; | |||
| } | |||
| if (player.PlayerState != PlayerStateType.Null || player.PlayerState != PlayerStateType.IsMoving) | |||
| if (!player.Commandable()) | |||
| return false; | |||
| XY res = new XY // 子弹紧贴人物生成。 | |||
| @@ -26,6 +26,7 @@ namespace Preparation.Interface | |||
| public interface IStudent : IOccupation | |||
| { | |||
| public int FixSpeed { get; } | |||
| public int TreatSpeed { get; } | |||
| } | |||
| public class Assassin : IGhost | |||
| @@ -84,6 +85,9 @@ namespace Preparation.Interface | |||
| public const int fixSpeed = GameData.basicFixSpeed / 10 * 6; | |||
| public int FixSpeed => fixSpeed; | |||
| public const int treatSpeed = GameData.basicTreatSpeed / 10 * 8; | |||
| public int TreatSpeed => treatSpeed; | |||
| public const double concealment = GameData.basicConcealment * 0.9; | |||
| public double Concealment => concealment; | |||
| @@ -19,10 +19,11 @@ namespace Preparation.Utility | |||
| IsRescued = 10, | |||
| IsStunned = 11, | |||
| IsTryingToAttack = 12,//指前摇 | |||
| IsLockingTheDoor = 13, | |||
| IsLockingOrOpeningTheDoor = 13, | |||
| IsOpeningTheChest = 14, | |||
| IsClimbingThroughWindows = 15, | |||
| IsUsingSpecialSkill = 16, | |||
| IsOpeningTheDoorWay = 17, | |||
| } | |||
| public enum GameObjType | |||
| { | |||
| @@ -4,18 +4,20 @@ namespace Preparation.Utility | |||
| { | |||
| public static class GameData | |||
| { | |||
| #region 基本常数与常方法 | |||
| public const int numOfPosGridPerCell = 1000; // 每格的【坐标单位】数 | |||
| #region 基本常数 | |||
| public const int numOfStepPerSecond = 20; // 每秒行走的步数 | |||
| public const int frameDuration = 50; // 每帧时长 | |||
| public const int lengthOfMap = 50000; // 地图长度 | |||
| public const int rows = 50; // 行数 | |||
| public const int cols = 50; // 列数 | |||
| public const long gameDuration = 600000; // 游戏时长600000ms = 10min | |||
| public const int MinSpeed = 1; // 最小速度 | |||
| public const int MaxSpeed = int.MaxValue; // 最大速度 | |||
| #endregion | |||
| #region 地图相关 | |||
| public const int numOfPosGridPerCell = 1000; // 每格的【坐标单位】数 | |||
| public const int lengthOfMap = 50000; // 地图长度 | |||
| public const int rows = 50; // 行数 | |||
| public const int cols = 50; // 列数 | |||
| public const int numOfBirthPoint = 5; | |||
| // public const int numOfGenerator = 9; | |||
| @@ -49,7 +51,6 @@ namespace Preparation.Utility | |||
| { | |||
| return Math.Abs(PosGridToCellX(pos1) - PosGridToCellX(pos2)) <= 1 && Math.Abs(PosGridToCellY(pos1) - PosGridToCellY(pos2)) <= 1; | |||
| } | |||
| #endregion | |||
| #region 角色相关 | |||
| public const int characterRadius = numOfPosGridPerCell / 2 / 5 * 4; // 人物半径 | |||
| @@ -128,6 +129,7 @@ namespace Preparation.Utility | |||
| #endregion | |||
| #region 物体相关 | |||
| public const int degreeOfFixedGenerator = 10300000; | |||
| public const int degreeOfOpenedDoorway = 18000; | |||
| public const int maxNumOfPropInChest = 2; | |||
| #endregion | |||
| #region 游戏帧相关 | |||
| @@ -71,7 +71,7 @@ namespace Server | |||
| return PlayerState.Graduated; | |||
| case Preparation.Utility.PlayerStateType.IsFixing: | |||
| return PlayerState.Learning; | |||
| case Preparation.Utility.PlayerStateType.IsLockingTheDoor: | |||
| case Preparation.Utility.PlayerStateType.IsLockingOrOpeningTheDoor: | |||
| return PlayerState.Locking; | |||
| case Preparation.Utility.PlayerStateType.IsOpeningTheChest: | |||
| return PlayerState.OpeningAChest; | |||
| @@ -91,6 +91,8 @@ namespace Server | |||
| return PlayerState.Attacking; | |||
| case Preparation.Utility.PlayerStateType.IsUsingSpecialSkill: | |||
| return PlayerState.UsingSpecialSkill; | |||
| case Preparation.Utility.PlayerStateType.IsOpeningTheDoorWay: | |||
| return PlayerState.OpeningAGate; | |||
| default: | |||
| return PlayerState.NullStatus; | |||
| } | |||
| @@ -149,22 +151,27 @@ namespace Server | |||
| public static MessageOfObj? Auto(GameObj gameObj) | |||
| { | |||
| if (gameObj.Type == Preparation.Utility.GameObjType.Character) | |||
| switch (gameObj.Type) | |||
| { | |||
| Character character = (Character)gameObj; | |||
| if (character.IsGhost()) | |||
| return Tricker((Ghost)character); | |||
| else return Student((Student)character); | |||
| case Preparation.Utility.GameObjType.Character: | |||
| Character character = (Character)gameObj; | |||
| if (character.IsGhost()) | |||
| return Tricker((Ghost)character); | |||
| else return Student((Student)character); | |||
| case Preparation.Utility.GameObjType.Bullet: | |||
| return Bullet((Bullet)gameObj); | |||
| case Preparation.Utility.GameObjType.Prop: | |||
| return Prop((Prop)gameObj); | |||
| case Preparation.Utility.GameObjType.BombedBullet: | |||
| return BombedBullet((BombedBullet)gameObj); | |||
| case Preparation.Utility.GameObjType.PickedProp: | |||
| return PickedProp((PickedProp)gameObj); | |||
| case Preparation.Utility.GameObjType.Generator: | |||
| return Classroom((Generator)gameObj); | |||
| // case Preparation.Utility.GameObjType.Chest: | |||
| default: return null; | |||
| } | |||
| else if (gameObj.Type == Preparation.Utility.GameObjType.Bullet) | |||
| return Bullet((Bullet)gameObj); | |||
| else if (gameObj.Type == Preparation.Utility.GameObjType.Prop) | |||
| return Prop((Prop)gameObj); | |||
| else if (gameObj.Type == Preparation.Utility.GameObjType.BombedBullet) | |||
| return BombedBullet((BombedBullet)gameObj); | |||
| else if (gameObj.Type == Preparation.Utility.GameObjType.PickedProp) | |||
| return PickedProp((PickedProp)gameObj); | |||
| else return null; //先写着防报错 | |||
| } | |||
| public static MessageOfObj? Auto(MessageOfNews news) | |||
| { | |||
| @@ -304,5 +311,25 @@ namespace Server | |||
| msg.MessageOfPickedProp.FacingDirection = pickedProp.PropHasPicked.FacingDirection;*/ | |||
| return msg; | |||
| } | |||
| 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.DegreeOfFRepair; | |||
| return msg; | |||
| } | |||
| /* private static MessageOfObj Chest(Chest chest) | |||
| { | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.ChestMessage = new(); | |||
| msg.ChestMessage.X=chest.Position.x; | |||
| msg.ChestMessage.Y=chest.Position.y; | |||
| // msg.ChestMessage.Progress=generator.DegreeOfFRepair; | |||
| return msg; | |||
| }*/ | |||
| } | |||
| } | |||
| @@ -171,7 +171,7 @@ | |||
| ### 出口:物体 | |||
| - *电力供应(是否可以被打开)* | |||
| - *是否打开* | |||
| - *开启进度* | |||
| ### 紧急出口:物体 | |||
| - *是否显现* | |||
| @@ -180,15 +180,18 @@ | |||
| ### 墙:物体 | |||
| ### 窗:物体 | |||
| - *正在翻窗的人* | |||
| - *是否正在被翻窗* | |||
| - *窗的朝向* | |||
| ### 箱子:物体 | |||
| - *是否开启* | |||
| - *开箱进度* | |||
| - *是否正在被开启* | |||
| ### 门:物体 | |||
| - *属于那个教学区* | |||
| - *是否锁上* | |||
| - *是否正在被锁* | |||
| - 不提供是否可以锁上的属性 | |||
| ### 电机(建议称为homework):物体 | |||
| @@ -202,13 +205,15 @@ | |||
| ### 交互 | |||
| - *除了逃离,交互目标与交互者在一个九宫格内则为可交互* | |||
| - *在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令* | |||
| ### 破译与逃脱 | |||
| - *每张地图都会刷新 9台电机,求生者需要破译其中的7台*,并开启任意一个大门后从任意一个开启的大门- 逃脱,亦或者在只剩1名求生者的情况下从紧急出口逃脱; | |||
| - 求生者和监管者在靠近电机时,可以看到电机的破译进度条。 | |||
| - 紧急出口会在电机破译完成3台的情况下在地图的3-5个固定紧急出口刷新点之一随机刷新。 | |||
| - 当求生者只剩1名时,紧急出口盖将会自动打开,该求生者可从紧急出口逃脱。 | |||
| - *一般情况下,开启大门所需时间为18秒。* | |||
| - *开启大门所需时间为18秒。* | |||
| - 大门开启的进度不清空 | |||
| ### 攻击 | |||
| - 每次求生者收到攻击后会损失对应子弹的攻击力的血量 | |||
| @@ -229,7 +234,8 @@ | |||
| - *监管者或求生者都需要拿到对应教学区的钥匙才能打开或锁住对应的门* | |||
| - *锁门和开门都需要一定时间,进出门为正常移动过程* | |||
| - *门只有开、锁两种状态,锁住时门有碰撞体积* | |||
| - *当门所在格子内有人时,无法锁门* | |||
| - *当门所在格子内有人时,无法锁门(必须从门外锁门)* | |||
| - *锁门时其他人可以进入门所在格子,锁门过程中断* | |||
| - *钥匙只会出现在箱子中,每个教学区都有2把钥匙* | |||
| ### 窗 | |||
| @@ -269,7 +275,7 @@ | |||
| - 可行动的求生者可以对受伤的其他求生者进行治疗,治疗完成后会回复被治疗程度的血量。 | |||
| - 治疗时每毫秒增加相当于治疗者治疗速度的被治疗程度 | |||
| - 当达到被治疗程度达到1500000或者最大血量与当前血量的差值时,治疗结束。 | |||
| - 治疗他人未完成时重新发出治疗指令是无效的(无论是否要求治疗同一人) | |||
| - 治疗中断时,被治疗程度保留;被治疗者遭到攻击时被治疗程度清空 | |||
| ### 沉迷 | |||