| @@ -53,11 +53,7 @@ namespace GameClass.GameObj | |||
| public bool IsGhost() | |||
| { | |||
| return this.CharacterType switch | |||
| { | |||
| CharacterType.Assassin => true, | |||
| _ => false, | |||
| }; | |||
| return GameData.IsGhost(CharacterType); | |||
| } | |||
| protected Character(XY initPos, int initRadius, CharacterType characterType) : | |||
| @@ -88,6 +84,8 @@ namespace GameClass.GameObj | |||
| this.bulletNum = maxBulletNum; | |||
| this.bulletOfPlayer = Occupation.InitBullet; | |||
| this.OriBulletOfPlayer = Occupation.InitBullet; | |||
| this.concealment = Occupation.Concealment; | |||
| this.alertnessRadius = Occupation.AlertnessRadius; | |||
| this.characterType = characterType; | |||
| foreach (var activeSkill in this.Occupation.ListOfIActiveSkill) | |||
| @@ -175,6 +175,45 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| private Dictionary<BgmType, double> bgmDictionary = new(); | |||
| public Dictionary<BgmType, double> BgmDictionary | |||
| { | |||
| get => bgmDictionary; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| bgmDictionary = value; | |||
| } | |||
| } | |||
| } | |||
| private int alertnessRadius; | |||
| public int AlertnessRadius | |||
| { | |||
| get => alertnessRadius; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| alertnessRadius = value; | |||
| } | |||
| } | |||
| } | |||
| private double concealment; | |||
| public double Concealment | |||
| { | |||
| get => concealment; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| concealment = value; | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 进行一次远程攻击 | |||
| /// </summary> | |||
| @@ -1,4 +1,5 @@ | |||
| using System; | |||
| using System.Numerics; | |||
| using System.Runtime.InteropServices; | |||
| using System.Threading; | |||
| using GameClass.GameObj; | |||
| @@ -38,7 +39,7 @@ namespace Gaming | |||
| public bool Fix(Student player)// 自动检查有无发电机可修 | |||
| { | |||
| if (player.PlayerState != PlayerStateType.Null || player.IsGhost()) | |||
| if (player.IsGhost() || (player.PlayerState != PlayerStateType.Null && player.PlayerState != PlayerStateType.IsMoving)) | |||
| return false; | |||
| Generator? generatorForFix = null; | |||
| @@ -72,7 +73,7 @@ namespace Gaming | |||
| loopCondition: () => player.PlayerState == PlayerStateType.IsFixing && gameMap.Timer.IsGaming && generatorForFix.DegreeOfFRepair < GameData.degreeOfFixedGenerator && GameData.ApproachToInteract(player.Position, generatorForFix.Position), | |||
| loopToDo: () => | |||
| { | |||
| return !generatorForFix.Repair(player.FixSpeed * GameData.frameDuration); | |||
| generatorForFix.Repair(player.FixSpeed * GameData.frameDuration); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| @@ -38,7 +38,7 @@ namespace Gaming | |||
| ); | |||
| } | |||
| public void BeAddictedToGame(Student player) | |||
| private void BeAddictedToGame(Student player) | |||
| { | |||
| new Thread | |||
| (() => | |||
| @@ -68,11 +68,10 @@ namespace Gaming | |||
| { IsBackground = true }.Start(); | |||
| } | |||
| public void Die(Character player) | |||
| private void Die(Character player) | |||
| { | |||
| player.CanMove = false; | |||
| player.IsResetting = true; | |||
| player.Die(PlayerStateType.IsDeceased); | |||
| // gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); | |||
| // try | |||
| //{ | |||
| @@ -5,6 +5,7 @@ using Preparation.Utility; | |||
| using Timothy.FrameRateTask; | |||
| using Preparation.Interface; | |||
| using GameClass.GameObj; | |||
| using System.Numerics; | |||
| namespace Gaming | |||
| { | |||
| @@ -41,21 +42,14 @@ namespace Gaming | |||
| // Console.WriteLine($"x,y: {pos.x},{pos.y}"); | |||
| Character newPlayer = (GameData.IsGhost(playerInitInfo.characterType)) ? new Ghost(pos, GameData.characterRadius, playerInitInfo.characterType) : new Student(pos, GameData.characterRadius, playerInitInfo.characterType); | |||
| gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); | |||
| try | |||
| { | |||
| gameMap.GameObjDict[GameObjType.Character].Add(newPlayer); | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); | |||
| } | |||
| gameMap.Add(newPlayer); | |||
| // Console.WriteLine($"GameObjDict[GameObjType.Character] length:{gameMap.GameObjDict[GameObjType.Character].Count}"); | |||
| teamList[(int)playerInitInfo.teamID].AddPlayer(newPlayer); | |||
| newPlayer.TeamID = playerInitInfo.teamID; | |||
| newPlayer.PlayerID = playerInitInfo.playerID; | |||
| new Thread //人物装弹 | |||
| #region 人物装弹 | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| @@ -63,19 +57,16 @@ namespace Gaming | |||
| Thread.Sleep(newPlayer.CD); | |||
| long lastTime = Environment.TickCount64; | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => gameMap.Timer.IsGaming, | |||
| loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting, | |||
| loopToDo: () => | |||
| { | |||
| if (!newPlayer.IsResetting) | |||
| long nowTime = Environment.TickCount64; | |||
| if (newPlayer.BulletNum == newPlayer.MaxBulletNum) | |||
| lastTime = nowTime; | |||
| if (nowTime - lastTime >= newPlayer.CD) | |||
| { | |||
| long nowTime = Environment.TickCount64; | |||
| if (newPlayer.BulletNum == newPlayer.MaxBulletNum) | |||
| lastTime = nowTime; | |||
| if (nowTime - lastTime >= newPlayer.CD) | |||
| { | |||
| _ = newPlayer.TryAddBulletNum(); | |||
| lastTime = nowTime; | |||
| } | |||
| _ = newPlayer.TryAddBulletNum(); | |||
| lastTime = nowTime; | |||
| } | |||
| }, | |||
| timeInterval: GameData.checkInterval, | |||
| @@ -93,6 +84,91 @@ namespace Gaming | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| #endregion | |||
| #region BGM更新 | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| while (!gameMap.Timer.IsGaming) | |||
| Thread.Sleep((int)GameData.checkInterval); | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting, | |||
| loopToDo: () => | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | |||
| try | |||
| { | |||
| if (newPlayer.IsGhost()) | |||
| { | |||
| double bgmVolume = 0; | |||
| foreach (Character person in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| if (!person.IsGhost() && XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment)) | |||
| { | |||
| if ((double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position) > bgmVolume) | |||
| bgmVolume = newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position); | |||
| } | |||
| } | |||
| if (bgmVolume > 0) | |||
| newPlayer.BgmDictionary.Add(BgmType.StudentIsApproaching, bgmVolume); | |||
| } | |||
| else | |||
| { | |||
| foreach (Character person in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| if (person.IsGhost()) | |||
| { | |||
| if (XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment)) | |||
| newPlayer.BgmDictionary.Add(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position)); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||
| } | |||
| gameMap.GameObjLockDict[GameObjType.Generator].EnterReadLock(); | |||
| try | |||
| { | |||
| double bgmVolume = 0; | |||
| foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator]) | |||
| { | |||
| if (XY.Distance(newPlayer.Position, generator.Position) <= newPlayer.AlertnessRadius) | |||
| { | |||
| if ((double)newPlayer.AlertnessRadius * generator.DegreeOfFRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position) > bgmVolume) | |||
| bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfFRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position); | |||
| } | |||
| } | |||
| if (bgmVolume > 0) | |||
| newPlayer.BgmDictionary.Add(BgmType.StudentIsApproaching, bgmVolume); | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||
| } | |||
| }, | |||
| timeInterval: GameData.checkInterval, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| { | |||
| AllowTimeExceed = true/*, | |||
| MaxTolerantTimeExceedCount = 5, | |||
| TimeExceedAction = exceedTooMuch => | |||
| { | |||
| if (exceedTooMuch) Console.WriteLine("The computer runs too slow that it cannot check the color below the player in time!"); | |||
| }*/ | |||
| } | |||
| .Start(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| #endregion | |||
| return newPlayer.ID; | |||
| } | |||
| @@ -100,20 +176,6 @@ namespace Gaming | |||
| { | |||
| if (gameMap.Timer.IsGaming) | |||
| return false; | |||
| gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | |||
| try | |||
| { | |||
| foreach (Character player in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| player.CanMove = true; | |||
| player.AddShield(GameData.shieldTimeAtBirth); | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||
| } | |||
| propManager.StartProducing(); | |||
| @@ -12,6 +12,8 @@ namespace Preparation.Interface | |||
| public int MaxBulletNum { get; } | |||
| public List<ActiveSkillType> ListOfIActiveSkill { get; } | |||
| public List<PassiveSkillType> ListOfIPassiveSkill { get; } | |||
| public double Concealment { get; } | |||
| public int AlertnessRadius { get; } | |||
| } | |||
| public interface IGhost : IOccupation | |||
| @@ -41,6 +43,12 @@ namespace Preparation.Interface | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BecomeInvisible, ActiveSkillType.UseKnife }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public double concealment = GameData.basicConcealment * 1.5; | |||
| public double Concealment => concealment; | |||
| public int alertnessRadius = (int)(GameData.basicAlertnessRadius * 1.3); | |||
| public int AlertnessRadius => alertnessRadius; | |||
| } | |||
| public class Athlete : IStudent | |||
| { | |||
| @@ -61,6 +69,13 @@ namespace Preparation.Interface | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BeginToCharge }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public int FixSpeed => GameData.basicFixSpeed / 10 * 6; | |||
| public const int fixSpeed = GameData.basicFixSpeed / 10 * 6; | |||
| public int FixSpeed => fixSpeed; | |||
| public const double concealment = GameData.basicConcealment * 0.9; | |||
| public double Concealment => concealment; | |||
| public const int alertnessRadius = (int)(GameData.basicAlertnessRadius * 0.9); | |||
| public int AlertnessRadius => alertnessRadius; | |||
| } | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| using System; | |||
| using System.Reflection.Metadata.Ecma335; | |||
| using System.Threading; | |||
| namespace Preparation.Utility | |||
| { | |||
| @@ -75,12 +76,15 @@ namespace Preparation.Utility | |||
| public const int basicMoveSpeed = 3800; // 基本移动速度,单位:s-1 | |||
| public const int basicBulletMoveSpeed = 5400; // 基本子弹移动速度,单位:s-1 | |||
| public const int characterMaxSpeed = 12000; // 最大速度 | |||
| public const double basicConcealment = 1.0; | |||
| public const int basicAlertnessRadius = 30700; | |||
| public const int addScoreWhenKillOneLevelPlayer = 30; // 击杀一级角色获得的加分 | |||
| public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | |||
| public const int commonSkillTime = 10000; // 普通技能标准持续时间 | |||
| public const int bulletRadius = 200; // 默认子弹半径 | |||
| public const int reviveTime = 30000; // 复活时间 | |||
| public const int shieldTimeAtBirth = 3000; // 复活时的护盾时间 | |||
| public static XY PosWhoDie = new XY(1, 1); | |||
| public static bool IsGhost(CharacterType characterType) | |||
| @@ -101,9 +105,12 @@ namespace Preparation.Utility | |||
| public const long GemProduceTime = 10000; | |||
| public const long PropProduceTime = 10000; | |||
| public const int PropDuration = 10000; | |||
| #endregion | |||
| #region 物体相关 | |||
| public const int degreeOfFixedGenerator = 10300000; | |||
| #endregion | |||
| #region 游戏帧相关 | |||
| public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | |||
| #endregion | |||
| @@ -1,7 +1,7 @@ | |||
| # 规则Logic | |||
| ## 说明 | |||
| - 版本V1.003 | |||
| - 版本V2.0 | |||
| - 该规则直接服务于Sever,并非选手版本 | |||
| - *斜体表示Logic底层尚未(完全)实现* | |||
| - []表示待决定 | |||
| @@ -23,13 +23,11 @@ | |||
| - 底层实现中的属性,不代表界面全部都需要展示,也可能需要额外展示信息 | |||
| - 只展示外部需要的属性,部分属性被省略 | |||
| ### Bgm | |||
| - *double bgmVolume* | |||
| - *BgmType bgmType* | |||
| 对于枚举类BgmType | |||
| 1. *不详的感觉:监管者进入(求生者的警戒半径/监管者的隐蔽度)时,求生者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离)* | |||
| 2. *期待搞事的感觉:求生者进入(监管者的警戒半径/求生者的隐蔽度)时,监管者收到;监管者距离求生者越近,Bgm音量2.越大。bgmVolume=(警戒半径/二者距离)* | |||
| 3. *修理电机的声音: 警戒半径内有电机正在被修理时,全员收到;bgmVolume=(警戒半径/二者距离)*电机修理程度/10300000* | |||
| ### BgmType | |||
| - 枚举类BgmType | |||
| 1. 不详的感觉:监管者进入(求生者的警戒半径/监管者的隐蔽度)时,求生者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离) | |||
| 2. 期待搞事的感觉:求生者进入(监管者的警戒半径/求生者的隐蔽度)时,监管者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/可被发觉的最近的求生者距离) | |||
| 3. 修理电机的声音: 警戒半径内有电机正在被修理时收到;bgmVolume=(警戒半径*电机修理程度/二者距离)/10300000 | |||
| ~~~csharp | |||
| public enum BgmType | |||
| { | |||
| @@ -95,20 +93,20 @@ | |||
| IsClimbingThroughWindows = 15, | |||
| } | |||
| ~~~ | |||
| - *Bgm(数组)* | |||
| - *Bgm(字典)* | |||
| - 得分 | |||
| - ~~回血率/原始回血率~~ | |||
| - 当前子弹类型 | |||
| - 原始子弹类型 | |||
| - 持有道具*(最多三个)(数组)* | |||
| - 持有道具 *(最多三个)(列表)* | |||
| - 是否隐身 | |||
| - 队伍ID | |||
| - 玩家ID | |||
| - 当前Buff | |||
| - 职业类型 | |||
| - 拥有的被动技能(数组) | |||
| - 拥有的主动技能(数组) | |||
| - 各个主动技能CD(数组) | |||
| - 拥有的被动技能(列表) | |||
| - 拥有的主动技能(列表) | |||
| - 各个主动技能CD(字典) | |||
| - *警戒半径* | |||
| - *double 隐蔽度* | |||
| - *翻墙速度* | |||