diff --git a/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs b/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs index d4a1895..5af9533 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs @@ -22,7 +22,11 @@ namespace GameClass.GameObj { return false; } - + public override bool CanBeBombed(GameObjType gameObjType) + { + if (gameObjType == GameObjType.Character) return true; + return false; + } public override BulletType TypeOfBullet => BulletType.CommonAttackOfGhost; } @@ -44,6 +48,11 @@ namespace GameClass.GameObj { return false; } + public override bool CanBeBombed(GameObjType gameObjType) + { + if (gameObjType == GameObjType.Character) return true; + return false; + } public override BulletType TypeOfBullet => BulletType.FlyingKnife; @@ -68,6 +77,11 @@ namespace GameClass.GameObj // 圆形攻击范围 return XY.Distance(this.Position, target.Position) <= BulletBombRange; } + public override bool CanBeBombed(GameObjType gameObjType) + { + if (gameObjType == GameObjType.Character) return true; + return false; + } public override BulletType TypeOfBullet => BulletType.AtomBomb; @@ -92,6 +106,11 @@ namespace GameClass.GameObj // 圆形攻击范围 return XY.Distance(this.Position, target.Position) <= BulletBombRange; } + public override bool CanBeBombed(GameObjType gameObjType) + { + if (gameObjType == GameObjType.Character) return true; + return false; + } public override BulletType TypeOfBullet => BulletType.OrdinaryBullet; } @@ -116,6 +135,11 @@ namespace GameClass.GameObj // 圆形攻击范围 return XY.Distance(this.Position, target.Position) <= BulletBombRange; } + public override bool CanBeBombed(GameObjType gameObjType) + { + if (gameObjType == GameObjType.Character) return true; + return false; + } public override BulletType TypeOfBullet => BulletType.FastBullet; } @@ -135,6 +159,11 @@ namespace GameClass.GameObj public override int RecoveryFromHit => GameData.basicRecoveryFromHit; public override bool IsToBomb => true; + public override bool CanBeBombed(GameObjType gameObjType) + { + if (gameObjType == GameObjType.Character) return true; + return false; + } public override bool CanAttack(GameObj target) { double FacingAngle = Math.Atan2(this.FacingDirection.y, this.FacingDirection.x); diff --git a/logic/GameClass/GameObj/Bullet/Bullet.Student.cs b/logic/GameClass/GameObj/Bullet/Bullet.Student.cs index 6140d57..26510a3 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.Student.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.Student.cs @@ -1,7 +1,32 @@ -using Preparation.Interface; -using Preparation.Utility; -using System; +using Preparation.Utility; namespace GameClass.GameObj { + /* internal sealed class Ram : Bullet + { + public Ram(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) : + base(player, radius, placeType, pos) + { + } + public override double BulletBombRange => 0; + public override double BulletAttackRange => 0; + public override int AP => 7220; + public override int Speed => 0; + public override bool IsToBomb => false; + public override int CastTime => 0; + public override int Backswing => 0; + public override int RecoveryFromHit => 0; + public override bool CanAttack(GameObj target) + { + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + // if (gameObjType == GameObjType.Character) return true; + return false; + } + + public override BulletType TypeOfBullet => BulletType.Ram; + + }*/ } diff --git a/logic/GameClass/GameObj/Bullet/Bullet.cs b/logic/GameClass/GameObj/Bullet/Bullet.cs index 2e4611c..0020a2f 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.cs @@ -30,6 +30,7 @@ namespace GameClass.GameObj /// 被尝试攻击者 /// 是否可以攻击到 public abstract bool CanAttack(GameObj target); + public abstract bool CanBeBombed(GameObjType gameObjType); protected override bool IgnoreCollideExecutor(IGameObj targetObj) { diff --git a/logic/GameClass/GameObj/Character/Character.Skill.cs b/logic/GameClass/GameObj/Character/Character.Skill.cs index db44536..1ebcc7c 100644 --- a/logic/GameClass/GameObj/Character/Character.Skill.cs +++ b/logic/GameClass/GameObj/Character/Character.Skill.cs @@ -12,9 +12,22 @@ namespace GameClass.GameObj private readonly IOccupation occupation; public IOccupation Occupation => occupation; + private Dictionary timeUntilActiveSkillAvailable = new(); public Dictionary TimeUntilActiveSkillAvailable => timeUntilActiveSkillAvailable; + private Dictionary iActiveSkillDictionary = new(); + public Dictionary IActiveSkillDictionary => iActiveSkillDictionary; + + public IActiveSkill? UseIActiveSkill(ActiveSkillType activeSkillType) + { + if (Occupation.ListOfIActiveSkill.Contains(activeSkillType)) + { + return IActiveSkillDictionary[activeSkillType]; + } + return null; + } + public bool SetTimeUntilActiveSkillAvailable(ActiveSkillType activeSkillType, int timeUntilActiveSkillAvailable) { if (TimeUntilActiveSkillAvailable.ContainsKey(activeSkillType)) @@ -73,10 +86,13 @@ namespace GameClass.GameObj this.concealment = Occupation.Concealment; this.alertnessRadius = Occupation.AlertnessRadius; this.characterType = characterType; + this.TimeOfOpeningOrLocking = Occupation.TimeOfOpeningOrLocking; + this.TimeOfClimbingThroughWindows = Occupation.TimeOfClimbingThroughWindows; foreach (var activeSkill in this.Occupation.ListOfIActiveSkill) { this.TimeUntilActiveSkillAvailable.Add(activeSkill, 0); + this.IActiveSkillDictionary.Add(activeSkill, SkillFactory.FindIActiveSkill(activeSkill)); } // UsePassiveSkill(); //创建player时开始被动技能,这一过程也可以放到gamestart时进行 diff --git a/logic/GameClass/GameObj/Character/Character.cs b/logic/GameClass/GameObj/Character/Character.cs index 6d7b927..ba33b4c 100644 --- a/logic/GameClass/GameObj/Character/Character.cs +++ b/logic/GameClass/GameObj/Character/Character.cs @@ -72,6 +72,13 @@ namespace GameClass.GameObj } } + public bool Commandable() => (playerState != PlayerStateType.IsDeceased && playerState != PlayerStateType.IsEscaped + && 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 NullOrMoving() => (playerState == PlayerStateType.Null || playerState == PlayerStateType.IsMoving); + // private int deathCount = 0; // public int DeathCount => deathCount; // 玩家的死亡次数 @@ -130,8 +137,8 @@ namespace GameClass.GameObj } } - private Prop? propInventory; - public Prop? PropInventory // 持有的道具 + private Prop[] propInventory = new Prop[GameData.maxNumOfPropInPropInventory]; + public Prop[] PropInventory { get => propInventory; set @@ -148,16 +155,30 @@ namespace GameClass.GameObj /// 使用物品栏中的道具 /// /// 被使用的道具 - public Prop? UseProp() + public Prop? UseProp(int indexing) { + if (indexing < 0 || indexing >= GameData.maxNumOfPropInPropInventory) + return null; lock (gameObjLock) { - var oldProp = PropInventory; - PropInventory = null; - return oldProp; + Prop prop = propInventory[indexing]; + PropInventory[indexing] = null; + return prop; } } + /// + /// 如果indexing==GameData.maxNumOfPropInPropInventory表明道具栏为满 + /// + public int IndexingOfAddProp() + { + int indexing = 0; + for (; indexing < GameData.maxNumOfPropInPropInventory; ++indexing) + if (PropInventory[indexing] == null) + break; + return indexing; + } + /// /// 是否在隐身 /// @@ -226,7 +247,18 @@ namespace GameClass.GameObj } } - + private int timeOfClimbingThroughWindows; + public int TimeOfClimbingThroughWindows + { + get => timeOfClimbingThroughWindows; + set + { + lock (gameObjLock) + { + timeOfClimbingThroughWindows = value; + } + } + } /// /// 进行一次攻击 diff --git a/logic/GameClass/GameObj/Character/Skill.cs b/logic/GameClass/GameObj/Character/Skill.cs new file mode 100644 index 0000000..0a28727 --- /dev/null +++ b/logic/GameClass/GameObj/Character/Skill.cs @@ -0,0 +1,98 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj +{ + public class BecomeVampire : IActiveSkill // 化身吸血鬼 + { + public int SkillCD => GameData.commonSkillCD / 3 * 4; + public int DurationTime => GameData.commonSkillTime; + + private readonly object commonSkillLock = new object(); + public object ActiveSkillLock => commonSkillLock; + + public bool IsBeingUsed { get; set; } + } + public class CanBeginToCharge : IActiveSkill + { + public int SkillCD => GameData.commonSkillCD / 5; + public int DurationTime => GameData.commonSkillTime / 10 * 6; + + private readonly object commonSkillLock = new object(); + public object ActiveSkillLock => commonSkillLock; + + public bool IsBeingUsed { get; set; } + } + + public class BecomeInvisible : IActiveSkill + { + public int SkillCD => GameData.commonSkillCD; + public int DurationTime => GameData.commonSkillTime / 10 * 6; + + private readonly object commonSkillLock = new object(); + public object ActiveSkillLock => commonSkillLock; + + public bool IsBeingUsed { get; set; } + } + + public class NuclearWeapon : IActiveSkill // 核武器 + { + public int SkillCD => GameData.commonSkillCD / 3 * 7; + public int DurationTime => GameData.commonSkillTime / 10; + private readonly object commonSkillLock = new object(); + public object ActiveSkillLock => commonSkillLock; + + public bool IsBeingUsed { get; set; } + } + + public class UseKnife : IActiveSkill + { + public int SkillCD => GameData.commonSkillCD / 3 * 2; + public int DurationTime => GameData.commonSkillTime / 10; + private readonly object commonSkillLock = new object(); + public object ActiveSkillLock => commonSkillLock; + + public bool IsBeingUsed { get; set; } + } + + public class SuperFast : IActiveSkill // 3倍速 + { + public int SkillCD => GameData.commonSkillCD; + public int DurationTime => GameData.commonSkillTime / 10 * 4; + private readonly object commonSkillLock = new object(); + public object ActiveSkillLock => commonSkillLock; + + public bool IsBeingUsed { get; set; } + } + + public static class SkillFactory + { + public static IActiveSkill? FindIActiveSkill(ActiveSkillType activeSkillType) + { + switch (activeSkillType) + { + case ActiveSkillType.BecomeInvisible: + return new BecomeInvisible(); + case ActiveSkillType.UseKnife: + return new UseKnife(); + default: + return null; + } + } + + public static ActiveSkillType FindActiveSkillType(IActiveSkill ActiveSkill) + { + switch (ActiveSkill) + { + case BecomeInvisible: + return ActiveSkillType.BecomeInvisible; + case UseKnife: + return ActiveSkillType.UseKnife; + case CanBeginToCharge: + return ActiveSkillType.CanBeginToCharge; + default: + return ActiveSkillType.Null; + } + } + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Map/Chest.cs b/logic/GameClass/GameObj/Map/Chest.cs new file mode 100644 index 0000000..975e1cf --- /dev/null +++ b/logic/GameClass/GameObj/Map/Chest.cs @@ -0,0 +1,34 @@ +using Preparation.Utility; +using System.Collections.Generic; + +namespace GameClass.GameObj +{ + /// + /// 箱子 + /// + public class Chest : GameObj + { + public Chest(XY initPos) : + base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest) + { + this.place = PlaceType.Chest; + this.CanMove = false; + } + public override bool IsRigid => true; + public override ShapeType Shape => ShapeType.Square; + + private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest]; + public Prop[] PropInChest => propInChest; + + private bool isOpen = false; + public bool IsOpen + { + get => isOpen; + set + { + lock (gameObjLock) + isOpen = value; + } + } + } +} diff --git a/logic/GameClass/GameObj/Map/Map.cs b/logic/GameClass/GameObj/Map/Map.cs index 0a373f6..cecd378 100644 --- a/logic/GameClass/GameObj/Map/Map.cs +++ b/logic/GameClass/GameObj/Map/Map.cs @@ -96,6 +96,18 @@ namespace GameClass.GameObj } return flag; } + public bool RemoveJustFromMap(GameObj gameObj) + { + GameObjLockDict[gameObj.Type].EnterWriteLock(); + try + { + return GameObjDict[gameObj.Type].Remove(gameObj); + } + finally + { + GameObjLockDict[gameObj.Type].ExitWriteLock(); + } + } public void Add(GameObj gameObj) { GameObjLockDict[gameObj.Type].EnterWriteLock(); @@ -134,55 +146,22 @@ namespace GameClass.GameObj { case (uint)PlaceType.Wall: { - GameObjLockDict[GameObjType.Wall].EnterWriteLock(); - try - { - GameObjDict[GameObjType.Wall].Add(new Wall(GameData.GetCellCenterPos(i, j))); - } - finally - { - GameObjLockDict[GameObjType.Wall].ExitWriteLock(); - } + Add(new Wall(GameData.GetCellCenterPos(i, j))); break; } case (uint)PlaceType.Doorway: { - GameObjLockDict[GameObjType.Doorway].EnterWriteLock(); - try - { - GameObjDict[GameObjType.Doorway].Add(new Doorway(GameData.GetCellCenterPos(i, j))); - } - finally - { - GameObjLockDict[GameObjType.Doorway].ExitWriteLock(); - } + Add(new Doorway(GameData.GetCellCenterPos(i, j))); break; } - case (uint)PlaceType.EmergencyExit: { - GameObjLockDict[GameObjType.EmergencyExit].EnterWriteLock(); - try - { - GameObjDict[GameObjType.EmergencyExit].Add(new EmergencyExit(GameData.GetCellCenterPos(i, j))); - } - finally - { - GameObjLockDict[GameObjType.EmergencyExit].ExitWriteLock(); - } + Add(new EmergencyExit(GameData.GetCellCenterPos(i, j))); break; } case (uint)PlaceType.Generator: { - GameObjLockDict[GameObjType.Generator].EnterWriteLock(); - try - { - GameObjDict[GameObjType.Generator].Add(new Generator(GameData.GetCellCenterPos(i, j))); - } - finally - { - GameObjLockDict[GameObjType.Generator].ExitWriteLock(); - } + Add(new Generator(GameData.GetCellCenterPos(i, j))); break; } case (uint)PlaceType.BirthPoint1: diff --git a/logic/GameClass/GameObj/Prop.cs b/logic/GameClass/GameObj/Prop.cs index 5ebcdbd..9e4fea2 100644 --- a/logic/GameClass/GameObj/Prop.cs +++ b/logic/GameClass/GameObj/Prop.cs @@ -12,7 +12,8 @@ namespace GameClass.GameObj protected override bool IgnoreCollideExecutor(IGameObj targetObj) { - if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet || targetObj.Type == GameObjType.Character) + if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet + || targetObj.Type == GameObjType.Character || targetObj.Type == GameObjType.Chest) return true; return false; } diff --git a/logic/GameEngine/MoveEngine.cs b/logic/GameEngine/MoveEngine.cs index 4169731..0f728ff 100644 --- a/logic/GameEngine/MoveEngine.cs +++ b/logic/GameEngine/MoveEngine.cs @@ -74,12 +74,12 @@ namespace GameEngine { if (obj.IsMoving) // 已经移动的物体不能再移动 return; + if (!obj.IsAvailable || !gameTimer.IsGaming) + return; new Thread ( () => { - if (!obj.IsAvailable && gameTimer.IsGaming) - return; lock (obj.MoveLock) obj.IsMoving = true; @@ -93,7 +93,7 @@ namespace GameEngine () => { moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; - XY res = new XY(direction, moveVecLength); + res = new XY(direction, moveVecLength); // 越界情况处理:如果越界,则与越界方块碰撞 bool flag; // 循环标志 @@ -136,7 +136,7 @@ namespace GameEngine if (!isDestroyed) { moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; - XY res = new XY(direction, moveVecLength); + res = new XY(direction, moveVecLength); if ((collisionObj = collisionChecker.CheckCollision(obj, res)) == null) { obj.MovingSetPos(res, GetPlaceType(obj.Position + res)); diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs index 14c0441..c088076 100644 --- a/logic/Gaming/ActionManager.cs +++ b/logic/Gaming/ActionManager.cs @@ -25,11 +25,7 @@ namespace Gaming public bool Stop(Character player) { - if (player.PlayerState == PlayerStateType.IsRescuing || player.PlayerState == PlayerStateType.IsRescued - || player.PlayerState == PlayerStateType.IsFixing || player.PlayerState == PlayerStateType.IsMoving - || player.PlayerState == PlayerStateType.IsTreated || player.PlayerState == PlayerStateType.IsTreating - || player.PlayerState == PlayerStateType.IsRummagingInTheChest || player.PlayerState == PlayerStateType.IsLockingTheDoor - || player.PlayerState == PlayerStateType.IsClimbingThroughWindows) + if (player.Commandable()) { player.PlayerState = PlayerStateType.Null; return true; @@ -82,11 +78,11 @@ namespace Gaming .Start(); if (generatorForFix.DegreeOfFRepair == GameData.degreeOfFixedGenerator) { - gameMap.GameObjLockDict[GameObjType.Generator].EnterReadLock(); - try + Doorway exit = (Doorway)gameMap.GameObjDict[GameObjType.Doorway][1]; + if (!exit.PowerSupply) { - Doorway exit = (Doorway)gameMap.GameObjDict[GameObjType.Doorway][1]; - if (!exit.PowerSupply) + gameMap.GameObjLockDict[GameObjType.Generator].EnterReadLock(); + try { int numOfFixedGenerator = 0; foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator]) @@ -106,12 +102,12 @@ namespace Gaming } } } + finally + { + gameMap.GameObjLockDict[GameObjType.Generator].ExitReadLock(); + } } - finally - { - gameMap.GameObjLockDict[GameObjType.Generator].ExitReadLock(); - } } } @@ -156,7 +152,9 @@ namespace Gaming public bool Treat(Student player, Student playerTreated) { - if (playerTreated.PlayerState == PlayerStateType.Null || player.PlayerState == PlayerStateType.Null || playerTreated.HP == playerTreated.MaxHp || !GameData.ApproachToInteract(playerTreated.Position, player.Position)) + if (!((playerTreated.NullOrMoving() || playerTreated.InteractingWithMapWithoutMoving()) + && (player.NullOrMoving() || player.InteractingWithMapWithoutMoving())) + || playerTreated.HP == playerTreated.MaxHp || !GameData.ApproachToInteract(playerTreated.Position, player.Position)) return false; if (playerTreated.HP + playerTreated.DegreeOfTreatment >= playerTreated.MaxHp) diff --git a/logic/Gaming/AttackManager.cs b/logic/Gaming/AttackManager.cs index bd30d58..55dd75d 100644 --- a/logic/Gaming/AttackManager.cs +++ b/logic/Gaming/AttackManager.cs @@ -68,6 +68,32 @@ namespace Gaming { IsBackground = true }.Start(); } + public void BeStunned(Character player, int time) + { + new Thread + (() => + { + player.PlayerState = PlayerStateType.IsStunned; + new FrameRateTaskExecutor( + () => player.PlayerState == PlayerStateType.IsStunned && gameMap.Timer.IsGaming, + () => + { + }, + timeInterval: GameData.frameDuration, + () => + { + if (player.PlayerState == PlayerStateType.IsStunned) + player.PlayerState = PlayerStateType.Null; + return 0; + }, + maxTotalDuration: time + ) + .Start(); + } + ) + { IsBackground = true }.Start(); + } + private void Die(Character player) { @@ -82,22 +108,14 @@ namespace Gaming // gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); // } - Prop? dropProp = null; - if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地 - { - dropProp = player.PropInventory; - XY res = GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell); - dropProp.ReSetPos(res, gameMap.GetPlaceType(res)); - } - gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); - try + for (int i = 0; i < GameData.maxNumOfPropInPropInventory; i++) { - if (dropProp != null) - gameMap.GameObjDict[GameObjType.Prop].Add(dropProp); - } - finally - { - gameMap.GameObjLockDict[GameObjType.Prop].ExitWriteLock(); + Prop? prop = player.UseProp(i); + if (prop != null) + { + prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position)); + gameMap.Add(prop); + } } // player.Reset(); @@ -129,21 +147,21 @@ namespace Gaming */ } - private bool CanBeBombed(Bullet bullet, GameObjType gameObjType) - { - if (gameObjType == GameObjType.Character) return true; - return false; - } private void BombObj(Bullet bullet, GameObj objBeingShot) { switch (objBeingShot.Type) { case GameObjType.Character: - if (!((Character)objBeingShot).IsGhost()) + + if ((!((Character)objBeingShot).IsGhost()) && bullet.Parent.IsGhost()) if (((Character)objBeingShot).BeAttacked(bullet)) { BeAddictedToGame((Student)objBeingShot); } + // if (((Character)objBeingShot).IsGhost() && !bullet.Parent.IsGhost() && bullet.TypeOfBullet == BulletType.Ram) + // BeStunned((Character)objBeingShot, bullet.AP); + break; + default: break; } } @@ -218,9 +236,9 @@ namespace Gaming foreach (var kvp in gameMap.GameObjDict) { - if (CanBeBombed(bullet, kvp.Key)) + if (bullet.CanBeBombed(kvp.Key)) { - gameMap.GameObjLockDict[kvp.Key].EnterWriteLock(); + gameMap.GameObjLockDict[kvp.Key].EnterReadLock(); try { foreach (var item in gameMap.GameObjDict[kvp.Key]) @@ -232,7 +250,7 @@ namespace Gaming } finally { - gameMap.GameObjLockDict[kvp.Key].ExitWriteLock(); + gameMap.GameObjLockDict[kvp.Key].ExitReadLock(); } } } @@ -304,55 +322,51 @@ namespace Gaming (int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Sin(angle)) ); - Bullet? bullet = player.Attack( - res, gameMap.GetPlaceType(res) - ); - if (bullet.CastTime > 0) + Bullet? bullet = player.Attack(res, gameMap.GetPlaceType(res)); + + if (bullet != null) { - player.PlayerState = PlayerStateType.IsTryingToAttack; + bullet.CanMove = true; + gameMap.Add(bullet); + moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(player.BulletOfPlayer)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms - new Thread - (() => - { - new FrameRateTaskExecutor( - loopCondition: () => player.PlayerState == PlayerStateType.IsTryingToAttack && gameMap.Timer.IsGaming, - loopToDo: () => - { - }, - timeInterval: GameData.frameDuration, - finallyReturn: () => 0, - maxTotalDuration: bullet.CastTime - ) - - .Start(); - - if (gameMap.Timer.IsGaming) + + if (bullet.CastTime > 0) + { + player.PlayerState = PlayerStateType.IsTryingToAttack; + + new Thread + (() => { - if (player.PlayerState == PlayerStateType.IsTryingToAttack) + new FrameRateTaskExecutor( + loopCondition: () => player.PlayerState == PlayerStateType.IsTryingToAttack && gameMap.Timer.IsGaming, + loopToDo: () => + { + }, + timeInterval: GameData.frameDuration, + finallyReturn: () => 0, + maxTotalDuration: bullet.CastTime + ) + + .Start(); + + if (gameMap.Timer.IsGaming) { - player.PlayerState = PlayerStateType.Null; + if (player.PlayerState == PlayerStateType.IsTryingToAttack) + { + player.PlayerState = PlayerStateType.Null; + } + else + bullet.IsMoving = false; + gameMap.Remove(bullet); } - else - bullet.IsMoving = false; - gameMap.Remove(bullet); } - } - ) - { IsBackground = true }.Start(); + ) + { IsBackground = true }.Start(); + } } if (bullet != null) { - bullet.CanMove = true; - gameMap.GameObjLockDict[GameObjType.Bullet].EnterWriteLock(); - try - { - gameMap.GameObjDict[GameObjType.Bullet].Add(bullet); - } - finally - { - gameMap.GameObjLockDict[GameObjType.Bullet].ExitWriteLock(); - } - moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(player.BulletOfPlayer)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms #if DEBUG Console.WriteLine($"playerID:{player.ID} successfully attacked!"); #endif @@ -368,4 +382,4 @@ namespace Gaming } } } -} +} \ No newline at end of file diff --git a/logic/Gaming/Game.cs b/logic/Gaming/Game.cs index 2807231..d11a9c6 100644 --- a/logic/Gaming/Game.cs +++ b/logic/Gaming/Game.cs @@ -197,14 +197,6 @@ namespace Gaming public void EndGame() { - gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); - /*try - { - } - finally - { - }*/ - gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); } public bool MovePlayer(long playerID, int moveTimeInMilliseconds, double angle) { @@ -301,24 +293,24 @@ namespace Gaming _ = attackManager.Attack(player, angle); } } - public void UseProp(long playerID) + public void UseProp(long playerID, int indexing) { if (!gameMap.Timer.IsGaming) return; Character? player = gameMap.FindPlayer(playerID); if (player != null) { - propManager.UseProp(player); + propManager.UseProp(player, indexing); } } - public void ThrowProp(long playerID, int timeInmillionSeconds, double angle) + public void ThrowProp(long playerID, int indexing) { if (!gameMap.Timer.IsGaming) return; Character? player = gameMap.FindPlayer(playerID); if (player != null) { - propManager.ThrowProp(player, timeInmillionSeconds, angle); + propManager.ThrowProp(player, indexing); } } public bool PickProp(long playerID, PropType propType = PropType.Null) diff --git a/logic/Gaming/PropManager.cs b/logic/Gaming/PropManager.cs index 716631a..d84787a 100644 --- a/logic/Gaming/PropManager.cs +++ b/logic/Gaming/PropManager.cs @@ -28,11 +28,11 @@ namespace Gaming ProduceProp(); } - public void UseProp(Character player) + public void UseProp(Character player, int indexing) { if (player.IsResetting) return; - Prop? prop = player.UseProp(); + Prop? prop = player.UseProp(indexing); switch (prop?.GetPropType()) { case PropType.Spear: @@ -62,6 +62,10 @@ namespace Gaming { if (player.IsResetting) return false; + int indexing = player.IndexingOfAddProp(); + if (indexing == GameData.maxNumOfPropInPropInventory) + return false; + Prop? pickProp = null; if (propType == PropType.Null) // 自动检查有无道具可捡 { @@ -70,9 +74,9 @@ namespace Gaming { foreach (Prop prop in gameMap.GameObjDict[GameObjType.Prop]) { - if (GameData.IsInTheSameCell(prop.Position, player.Position) && prop.CanMove == false) + if (GameData.IsInTheSameCell(prop.Position, player.Position)) { - pickProp = prop; + player.PropInventory[indexing] = prop; } } } @@ -92,7 +96,7 @@ namespace Gaming { if (GameData.IsInTheSameCell(prop.Position, player.Position) && prop.CanMove == false) { - pickProp = prop; + player.PropInventory[indexing] = prop; } } } @@ -105,66 +109,26 @@ namespace Gaming if (pickProp != null) { - // pickProp.CanMove = false; - Prop? dropProp = null; - if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地 - { - dropProp = player.PropInventory; - XY res = GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell); - dropProp.ReSetPos(res, gameMap.GetPlaceType(res)); - } - player.PropInventory = pickProp; - gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); - try - { - gameMap.GameObjDict[GameObjType.Prop].Remove((Preparation.Interface.IGameObj)pickProp); - if (dropProp != null) - gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)dropProp); - } - finally - { - gameMap.GameObjLockDict[GameObjType.Prop].ExitWriteLock(); - } - gameMap.GameObjLockDict[GameObjType.PickedProp].EnterWriteLock(); - try - { - gameMap.GameObjDict[GameObjType.PickedProp].Add(new PickedProp(pickProp)); - } - finally - { - gameMap.GameObjLockDict[GameObjType.PickedProp].ExitWriteLock(); - } - + gameMap.Remove(pickProp); + gameMap.Add(new PickedProp(pickProp)); return true; } else return false; } - public void ThrowProp(Character player, int timeInMilliseconds, double angle) + public void ThrowProp(Character player, int indexing) { - if (!gameMap.Timer.IsGaming) + if (!gameMap.Timer.IsGaming || player.IsResetting) return; - if (player.IsResetting) // 移动中也能扔,但由于“惯性”,可能初始位置会有点变化 - return; - Prop? prop = player.UseProp(); + Prop? prop = player.UseProp(indexing); if (prop == null) return; - prop.CanMove = true; prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position)); - gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); - try - { - gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)prop); - } - finally - { - gameMap.GameObjLockDict[GameObjType.Prop].ExitWriteLock(); - } - timeInMilliseconds = timeInMilliseconds < GameData.PropMaxMoveDistance / prop.MoveSpeed * 1000 ? timeInMilliseconds : GameData.PropMaxMoveDistance / prop.MoveSpeed * 1000; - moveEngine.MoveObj(prop, timeInMilliseconds, angle); + gameMap.Add(prop); } + private void ProduceProp() { int len = availableCellForGenerateProp.Count; @@ -182,30 +146,23 @@ namespace Gaming int rand = r.Next(0, len); XY randPos = availableCellForGenerateProp[rand]; - gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); - try - { - switch (r.Next(0, 4)) - { - case 0: - gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddLIFE(randPos, gameMap.GetPlaceType(randPos))); - break; - case 1: - gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddSpeed(randPos, gameMap.GetPlaceType(randPos))); - break; - case 2: - gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Shield(randPos, gameMap.GetPlaceType(randPos))); - break; - case 3: - gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Spear(randPos, gameMap.GetPlaceType(randPos))); - break; - default: - break; - } - } - finally + + switch (r.Next(0, 4)) { - gameMap.GameObjLockDict[GameObjType.Prop].ExitWriteLock(); + case 0: + gameMap.Add(new AddLIFE(randPos, gameMap.GetPlaceType(randPos))); + break; + case 1: + gameMap.Add(new AddSpeed(randPos, gameMap.GetPlaceType(randPos))); + break; + case 2: + gameMap.Add(new Shield(randPos, gameMap.GetPlaceType(randPos))); + break; + case 3: + gameMap.Add(new Spear(randPos, gameMap.GetPlaceType(randPos))); + break; + default: + break; } }, GameData.PropProduceTime, diff --git a/logic/Gaming/SkillManager/ActiveSkill.cs b/logic/Gaming/SkillManager/ActiveSkill.cs deleted file mode 100644 index f16c093..0000000 --- a/logic/Gaming/SkillManager/ActiveSkill.cs +++ /dev/null @@ -1,238 +0,0 @@ -using GameClass.GameObj; -using System.Threading; -using Preparation.Interface; -using Preparation.Utility; -using System; -using Timothy.FrameRateTask; - -namespace Gaming -{ - public partial class Game - { - private partial class SkillManager - { - public interface IActiveSkill - { - public int SkillCD { get; } - public int DurationTime { get; } //技能持续时间 - public object ActiveSkillLock { get; } - public bool SkillEffect(Character player); - } - public class BecomeVampire : IActiveSkill // 化身吸血鬼 - { - public int SkillCD => GameData.commonSkillCD / 3 * 4; - public int DurationTime => GameData.commonSkillTime; - - private readonly object commonSkillLock = new object(); - public object ActiveSkillLock => commonSkillLock; - - public bool SkillEffect(Character player) - { - return ActiveSkillEffect(this, player, () => - { - player.Vampire += 0.5; - Debugger.Output(player, "becomes vampire!"); - }, - () => - { - double tempVam = player.Vampire - 0.5; - player.Vampire = tempVam < player.OriVampire ? player.OriVampire : tempVam; - }); - } - } - private IActiveSkill becomeVampire = new BecomeVampire(); - - public class BeginToCharge : IActiveSkill - { - public int SkillCD => GameData.commonSkillCD / 3 * 4; - public int DurationTime => GameData.commonSkillTime; - - private readonly object commonSkillLock = new object(); - public object ActiveSkillLock => commonSkillLock; - - public bool SkillEffect(Character player) - { - return ActiveSkillEffect(this, player, () => - { - player.Vampire += 0.5; - Debugger.Output(player, "becomes vampire!"); - }, - () => - { - double tempVam = player.Vampire - 0.5; - player.Vampire = tempVam < player.OriVampire ? player.OriVampire : tempVam; - }); - } - } - private IActiveSkill beginToCharge = new BeginToCharge(); - - public class BecomeInvisible : IActiveSkill - { - public int SkillCD => GameData.commonSkillCD; - public int DurationTime => GameData.commonSkillTime / 10 * 6; - - private readonly object commonSkillLock = new object(); - public object ActiveSkillLock => commonSkillLock; - public bool SkillEffect(Character player) - { - return ActiveSkillEffect(this, player, () => - { - player.IsInvisible = true; - Debugger.Output(player, "become invisible!"); - }, - () => - { player.IsInvisible = false; }); - } - } - private IActiveSkill becomeInvisible = new BecomeInvisible(); - - public class NuclearWeapon : IActiveSkill // 核武器 - { - public int SkillCD => GameData.commonSkillCD / 3 * 7; - public int DurationTime => GameData.commonSkillTime / 10; - private readonly object commonSkillLock = new object(); - public object ActiveSkillLock => commonSkillLock; - public bool SkillEffect(Character player) - { - return ActiveSkillEffect(this, player, () => - { - player.BulletOfPlayer = BulletType.AtomBomb; - Debugger.Output(player, "uses atombomb!"); - }, - () => - { player.BulletOfPlayer = player.OriBulletOfPlayer; }); - } - } - private IActiveSkill nuclearWeapon = new NuclearWeapon(); - - public class UseKnife : IActiveSkill - { - public int SkillCD => GameData.commonSkillCD / 3 * 2; - public int DurationTime => GameData.commonSkillTime / 10; - private readonly object commonSkillLock = new object(); - public object ActiveSkillLock => commonSkillLock; - public bool SkillEffect(Character player) - { - return ActiveSkillEffect(this, player, () => - { - player.BulletOfPlayer = BulletType.FlyingKnife; - Debugger.Output(player, "uses flyingknife!"); - }, - () => - { player.BulletOfPlayer = player.OriBulletOfPlayer; }); - } - } - private IActiveSkill useKnife = new UseKnife(); - - public class SuperFast : IActiveSkill // 3倍速 - { - public int SkillCD => GameData.commonSkillCD; - public int DurationTime => GameData.commonSkillTime / 10 * 4; - private readonly object commonSkillLock = new object(); - public object ActiveSkillLock => commonSkillLock; - public bool SkillEffect(Character player) - { - return ActiveSkillEffect(this, player, () => - { - player.AddMoveSpeed(this.DurationTime, 3.0); - Debugger.Output(player, "moves very fast!"); - }, - () => - { }); - } - } - private IActiveSkill superFast = new SuperFast(); - - public IActiveSkill? FindIActiveSkill(ActiveSkillType activeSkillType) - { - switch (activeSkillType) - { - case ActiveSkillType.BecomeInvisible: - return this.becomeInvisible; - default: - return null; - } - } - public static ActiveSkillType FindActiveSkillType(IActiveSkill ActiveSkill) - { - switch (ActiveSkill) - { - case BecomeInvisible: - return ActiveSkillType.BecomeInvisible; - case UseKnife: - return ActiveSkillType.UseKnife; - case BeginToCharge: - return ActiveSkillType.BeginToCharge; - default: - return ActiveSkillType.Null; - } - } - - public static bool ActiveSkillEffect(IActiveSkill activeSkill, Character player, Action startSkill, Action endSkill) - { - lock (activeSkill.ActiveSkillLock) - { - ActiveSkillType activeSkillType = FindActiveSkillType(activeSkill); - if (player.TimeUntilActiveSkillAvailable[activeSkillType] == 0) - { - - player.SetTimeUntilActiveSkillAvailable(activeSkillType, activeSkill.SkillCD); - new Thread - (() => - { - startSkill(); - new FrameRateTaskExecutor( - () => !player.IsResetting, - () => - { - player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration); - }, - timeInterval: GameData.frameDuration, - () => 0, - maxTotalDuration: (long)(activeSkill.DurationTime) - ) - { - AllowTimeExceed = true, - MaxTolerantTimeExceedCount = ulong.MaxValue, - } - .Start(); - - endSkill(); - Debugger.Output(player, "return to normal."); - - new FrameRateTaskExecutor( - () => player.TimeUntilActiveSkillAvailable[activeSkillType] > 0 && !player.IsResetting, - () => - { - player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration); - }, - timeInterval: GameData.frameDuration, - () => 0, - maxTotalDuration: (long)(activeSkill.SkillCD - activeSkill.DurationTime) - ) - { - AllowTimeExceed = true, - MaxTolerantTimeExceedCount = ulong.MaxValue, - } - .Start(); - - player.SetTimeUntilActiveSkillAvailable(activeSkillType, 0); - Debugger.Output(player, "ActiveSkill is ready."); - } - ) - { IsBackground = true }.Start(); - - return true; - } - else - { - Debugger.Output(player, "CommonSkill is cooling down!"); - return false; - } - } - } - - } - - } -} diff --git a/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs b/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs new file mode 100644 index 0000000..73961e1 --- /dev/null +++ b/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs @@ -0,0 +1,193 @@ +using GameClass.GameObj; +using System.Threading; +using Preparation.Interface; +using Preparation.Utility; +using System; +using Timothy.FrameRateTask; + +namespace Gaming +{ + public partial class Game + { + private partial class SkillManager + { + public bool BecomeVampire(Character player) + { + return ActiveSkillEffect(player.UseIActiveSkill(ActiveSkillType.BecomeVampire), player, () => + { + player.Vampire += 0.5; + Debugger.Output(player, "becomes vampire!"); + }, + () => + { + double tempVam = player.Vampire - 0.5; + player.Vampire = tempVam < player.OriVampire ? player.OriVampire : tempVam; + }); + } + + public bool CanBeginToCharge(Character player) + { + + if ((!player.Commandable())) return false; + IActiveSkill skill = player.UseIActiveSkill(ActiveSkillType.CanBeginToCharge); + return ActiveSkillEffect(skill, player, () => + { + player.AddMoveSpeed(skill.DurationTime, 3.0); + //player.BulletOfPlayer = BulletType.Ram; + new Thread + ( + () => + { + new FrameRateTaskExecutor( + loopCondition: () => player.Commandable() && gameMap.Timer.IsGaming, + loopToDo: () => + { + gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); + try + { + foreach (Character character in gameMap.GameObjDict[GameObjType.Character]) + { + if (character.IsGhost() != player.IsGhost() && XY.Distance(player.Position + new XY(player.FacingDirection, player.Radius), character.Position) <= character.Radius) + { + attackManager.BeStunned(character, GameData.TimeOfGhostFainting); + attackManager.BeStunned(player, GameData.TimeOfStudentFainting); + break; + } + } + } + finally + { + gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); + } + }, + timeInterval: GameData.frameDuration, + finallyReturn: () => 0, + maxTotalDuration: skill.DurationTime + ) + + .Start(); + } + + ) + { IsBackground = true }.Start(); + Debugger.Output(player, "can begin to charge!"); + }, + () => + { + double tempVam = player.Vampire - 0.5; + player.Vampire = tempVam < player.OriVampire ? player.OriVampire : tempVam; + }); + } + + + public bool BecomeInvisible(Character player) + { + return ActiveSkillEffect(player.UseIActiveSkill(ActiveSkillType.BecomeInvisible), player, () => + { + player.IsInvisible = true; + Debugger.Output(player, "become invisible!"); + }, + () => + { player.IsInvisible = false; }); + } + + public bool NuclearWeapon(Character player) + { + return ActiveSkillEffect(player.UseIActiveSkill(ActiveSkillType.NuclearWeapon), player, () => + { + player.BulletOfPlayer = BulletType.AtomBomb; + Debugger.Output(player, "uses atombomb!"); + }, + () => + { player.BulletOfPlayer = player.OriBulletOfPlayer; }); + } + + + public bool UseKnife(Character player) + { + return ActiveSkillEffect(player.UseIActiveSkill(ActiveSkillType.UseKnife), player, () => + { + player.BulletOfPlayer = BulletType.FlyingKnife; + Debugger.Output(player, "uses flyingknife!"); + }, + () => + { player.BulletOfPlayer = player.OriBulletOfPlayer; }); + } + + public bool SuperFast(Character player) + { + return ActiveSkillEffect(player.UseIActiveSkill(ActiveSkillType.SuperFast), player, () => + { + player.AddMoveSpeed(player.UseIActiveSkill(ActiveSkillType.SuperFast).DurationTime, 3.0); + Debugger.Output(player, "moves very fast!"); + }, + () => + { }); + } + + + public static bool ActiveSkillEffect(IActiveSkill activeSkill, Character player, Action startSkill, Action endSkill) + { + lock (activeSkill.ActiveSkillLock) + { + ActiveSkillType activeSkillType = SkillFactory.FindActiveSkillType(activeSkill); + if (player.TimeUntilActiveSkillAvailable[activeSkillType] == 0) + { + + player.SetTimeUntilActiveSkillAvailable(activeSkillType, activeSkill.SkillCD); + new Thread + (() => + { + startSkill(); + new FrameRateTaskExecutor( + () => !player.IsResetting, + () => + { + player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration); + }, + timeInterval: GameData.frameDuration, + () => 0, + maxTotalDuration: (long)(activeSkill.DurationTime) + ) + { + AllowTimeExceed = true, + MaxTolerantTimeExceedCount = ulong.MaxValue, + } + .Start(); + + endSkill(); + Debugger.Output(player, "return to normal."); + + new FrameRateTaskExecutor( + loopCondition: () => player.TimeUntilActiveSkillAvailable[activeSkillType] > 0 && !player.IsResetting, + loopToDo: () => + { + player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration); + }, + timeInterval: GameData.frameDuration, + finallyReturn: () => 0 + ) + { + AllowTimeExceed = true, + MaxTolerantTimeExceedCount = ulong.MaxValue, + } + .Start(); + + player.SetTimeUntilActiveSkillAvailable(activeSkillType, 0); + Debugger.Output(player, "ActiveSkill is ready."); + } + ) + { IsBackground = true }.Start(); + + return true; + } + else + { + Debugger.Output(player, "CommonSkill is cooling down!"); + return false; + } + } + } + } + } +} \ No newline at end of file diff --git a/logic/Gaming/SkillManager/PassiveSkill.cs b/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs similarity index 100% rename from logic/Gaming/SkillManager/PassiveSkill.cs rename to logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs diff --git a/logic/Gaming/SkillManager/SkillManager.cs b/logic/Gaming/SkillManager/SkillManager.cs index 0d5363e..31b249c 100644 --- a/logic/Gaming/SkillManager/SkillManager.cs +++ b/logic/Gaming/SkillManager/SkillManager.cs @@ -14,7 +14,20 @@ namespace Gaming public bool UseActiveSkill(Character character, ActiveSkillType activeSkillType) { if (character.Occupation.ListOfIActiveSkill.Contains(activeSkillType)) - return FindIActiveSkill(activeSkillType).SkillEffect(character); + switch (activeSkillType) + { + case ActiveSkillType.BecomeInvisible: + BecomeInvisible(character); + break; + case ActiveSkillType.UseKnife: + UseKnife(character); + break; + case ActiveSkillType.CanBeginToCharge: + CanBeginToCharge(character); + break; + default: + return false; + } return false; } public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType) diff --git a/logic/Preparation/Interface/IOccupation.cs b/logic/Preparation/Interface/IOccupation.cs index 16895a2..e510a98 100644 --- a/logic/Preparation/Interface/IOccupation.cs +++ b/logic/Preparation/Interface/IOccupation.cs @@ -15,6 +15,7 @@ namespace Preparation.Interface public double Concealment { get; } public int AlertnessRadius { get; } public int TimeOfOpeningOrLocking { get; } + public int TimeOfClimbingThroughWindows { get; } } public interface IGhost : IOccupation @@ -53,6 +54,10 @@ namespace Preparation.Interface public int timeOfOpeningOrLocking = GameData.basicTimeOfOpeningOrLocking; public int TimeOfOpeningOrLocking => timeOfOpeningOrLocking; + + public int timeOfClimbingThroughWindows = GameData.basicTimeOfClimbingThroughWindows; + public int TimeOfClimbingThroughWindows => timeOfClimbingThroughWindows; + } public class Athlete : IStudent { @@ -70,7 +75,7 @@ namespace Preparation.Interface public BulletType InitBullet => BulletType.Null; - public List ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BeginToCharge }); + public List ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.CanBeginToCharge }); public List ListOfIPassiveSkill => new(new PassiveSkillType[] { }); public const int fixSpeed = GameData.basicFixSpeed / 10 * 6; @@ -82,7 +87,10 @@ namespace Preparation.Interface public const int alertnessRadius = (int)(GameData.basicAlertnessRadius * 0.9); public int AlertnessRadius => alertnessRadius; - public int timeOfOpeningOrLocking = GameData.basicTimeOfOpeningOrLocking; + public int timeOfOpeningOrLocking = GameData.basicTimeOfOpeningOrLocking * 12 / 10; public int TimeOfOpeningOrLocking => timeOfOpeningOrLocking; + + public int timeOfClimbingThroughWindows = GameData.basicTimeOfClimbingThroughWindows / 87 * 80; + public int TimeOfClimbingThroughWindows => timeOfClimbingThroughWindows; } } diff --git a/logic/Preparation/Interface/ISkill.cs b/logic/Preparation/Interface/ISkill.cs new file mode 100644 index 0000000..c38663b --- /dev/null +++ b/logic/Preparation/Interface/ISkill.cs @@ -0,0 +1,16 @@ +namespace Preparation.Interface +{ + public interface ISkill + { + } + public interface IPassiveSkill : ISkill + { + } + public interface IActiveSkill : ISkill + { + public int SkillCD { get; } + public int DurationTime { get; } //技能持续时间 + public object ActiveSkillLock { get; } + public bool IsBeingUsed { get; set; } + } +} \ No newline at end of file diff --git a/logic/Preparation/Utility/EnumType.cs b/logic/Preparation/Utility/EnumType.cs index c082442..5247c50 100644 --- a/logic/Preparation/Utility/EnumType.cs +++ b/logic/Preparation/Utility/EnumType.cs @@ -20,8 +20,9 @@ namespace Preparation.Utility IsStunned = 11, IsTryingToAttack = 12,//指前摇 IsLockingTheDoor = 13, - IsRummagingInTheChest = 14, + IsOpeningTheChest = 14, IsClimbingThroughWindows = 15, + IsUsingSpecialSkill = 16, } public enum GameObjType { @@ -40,6 +41,7 @@ namespace Preparation.Utility OutOfBoundBlock = 11, // 范围外 Window = 12, Door = 13, + Chest = 14, } public enum ShapeType { @@ -55,7 +57,8 @@ namespace Preparation.Utility FastBullet = 3, // 快速子弹 LineBullet = 4, // 直线子弹 FlyingKnife = 5, //飞刀 - CommonAttackOfGhost = 6 + CommonAttackOfGhost = 6, + // Ram = 7, } public enum PropType // 道具类型 { @@ -84,7 +87,7 @@ namespace Preparation.Utility NuclearWeapon = 3, SuperFast = 4, UseKnife = 5, - BeginToCharge = 6 + CanBeginToCharge = 6 } public enum PassiveSkillType { diff --git a/logic/Preparation/Utility/GameData.cs b/logic/Preparation/Utility/GameData.cs index 3e0cc28..b1e5b2b 100644 --- a/logic/Preparation/Utility/GameData.cs +++ b/logic/Preparation/Utility/GameData.cs @@ -52,38 +52,28 @@ namespace Preparation.Utility #endregion #region 角色相关 - public const int characterRadius = numOfPosGridPerCell / 2; // 人物半径 - public const int basicApOfGhost = 1500000; // 攻击力 + public const int characterRadius = numOfPosGridPerCell / 2 / 5 * 4; // 人物半径 + public const int basicTreatSpeed = 100; public const int basicFixSpeed = 100; + public const int basicTimeOfOpeningOrLocking = 3000; + public const int basicTimeOfClimbingThroughWindows = 870; + + public const int basicHp = 3000000; // 初始血量 public const int basicMaxGamingAddiction = 60000;//基本完全沉迷时间 public const int BeginGamingAddiction = 10003; public const int MidGamingAddiction = 30000; public const int basicTreatmentDegree = 1500000; public const int basicRescueDegree = 100000; - public const int basicHp = 3000000; // 初始血量 - public const int basicCD = 3000; // 初始子弹冷却 - public const int basicCastTime = 500;//基本前摇时间 - public const int basicBackswing = 500;//基本后摇时间 - public const int basicRecoveryFromHit = 4300;//基本命中攻击恢复时长 - public const int basicBulletNum = 3; // 基本初始子弹量 - public const int MinAP = 0; // 最小攻击力 - public const int MaxAP = int.MaxValue; // 最大攻击力 - public const double basicRemoteAttackRange = 9000; // 基本远程攻击范围 - public const double basicAttackShortRange = 2700; // 基本近程攻击范围 - public const double basicBulletBombRange = 3000; // 基本子弹爆炸范围 + public const int basicMoveSpeed = 1260; // 基本移动速度,单位:s-1 - public const int basicBulletMoveSpeed = 2700; // 基本子弹移动速度,单位:s-1 public const int characterMaxSpeed = 12000; // 最大速度 + public const int basicBulletMoveSpeed = 2700; // 基本子弹移动速度,单位:s-1 + public const double basicConcealment = 1.0; public const int basicAlertnessRadius = 30700; - public const int basicTimeOfOpeningOrLocking = 3000; + public const int maxNumOfPropInPropInventory = 3; 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); @@ -95,6 +85,32 @@ namespace Preparation.Utility _ => false, }; } + #endregion + #region 攻击与子弹相关 + public const int basicApOfGhost = 1500000; // 捣蛋鬼攻击力 + public const int MinAP = 0; // 最小攻击力 + public const int MaxAP = int.MaxValue; // 最大攻击力 + + public const int basicCD = 3000; // 初始子弹冷却 + public const int basicCastTime = 500;//基本前摇时间 + public const int basicBackswing = 500;//基本后摇时间 + public const int basicRecoveryFromHit = 4300;//基本命中攻击恢复时长 + + public const int bulletRadius = 200; // 默认子弹半径 + public const int basicBulletNum = 3; // 基本初始子弹量 + public const double basicRemoteAttackRange = 9000; // 基本远程攻击范围 + public const double basicAttackShortRange = 2700; // 基本近程攻击范围 + public const double basicBulletBombRange = 3000; // 基本子弹爆炸范围 + #endregion + #region 技能相关 + public const int commonSkillCD = 30000; // 普通技能标准冷却时间 + public const int commonSkillTime = 10000; // 普通技能标准持续时间 + /// + /// BeginToCharge + /// + public const int TimeOfGhostFainting = 7220;//=AP of Ram + public const int TimeOfStudentFainting = 2090; + #endregion #region 道具相关 public const int MinPropTypeNum = 1; @@ -108,8 +124,8 @@ namespace Preparation.Utility #endregion #region 物体相关 public const int degreeOfFixedGenerator = 10300000; + public const int maxNumOfPropInChest = 2; #endregion - #region 游戏帧相关 public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 #endregion diff --git a/logic/Preparation/Utility/XY.cs b/logic/Preparation/Utility/XY.cs index 8f4a29f..7fc87ae 100644 --- a/logic/Preparation/Utility/XY.cs +++ b/logic/Preparation/Utility/XY.cs @@ -17,6 +17,11 @@ namespace Preparation.Utility this.x = (int)(length * Math.Cos(angle)); this.y = (int)(length * Math.Sin(angle)); } + public XY(XY Direction, double length) + { + this.x = (int)(length * Math.Cos(Direction.Angle())); + this.y = (int)(length * Math.Sin(Direction.Angle())); + } public override string ToString() { return "(" + x.ToString() + "," + y.ToString() + ")"; diff --git a/logic/规则Logic.md b/logic/规则Logic.md index ef62a11..aa881fb 100644 --- a/logic/规则Logic.md +++ b/logic/规则Logic.md @@ -9,7 +9,10 @@ ## 游戏简介 - 1位监管者对抗4位求生者的非对称竞技模式 -- 略 +- [本届THUAI电子系赛道为以4名同学和1名捣蛋鬼的求学与阻挠展开的非对称竞技模式,同学需要完成足够的家庭作业和考试,相互督促以避免沉迷娱乐生活,利用道具地形躲避捣蛋鬼的各种干扰诱惑,完成学业;捣蛋鬼则要极力阻止。] +- [我们的设计是一个非对称游戏,类似第五人格,分为学生、捣蛋鬼两个阵营。在游戏中,学生修完若干课程之后通过考试即可顺利毕业,捣蛋鬼试图干扰学生使其沉迷游戏,以致于无法修完规定课程,直至挂科、退学。] +[对于选手来说,需要提前制定好学生的学习方案以抵御对方捣蛋鬼的干扰,类似地,也需要制定好捣蛋鬼的行动策略以影响对方学生的学习,也即每队至少要写好两份代码以执行不同阵营的不同策略。] +[当一局比赛结束(场上的学生有且仅有两种状态:退学或毕业)时,分别记录双方总得分;之后双方换边进行下半场比赛。最终将每队的学生方、捣蛋鬼方的得分相加,比较总得分判断胜负。] ## 地图 - 地图为矩形区域,地图上的游戏对象坐标为(x, y),且x和y均为整数。x坐标轴正方向竖直向下, @@ -91,16 +94,24 @@ IsStunned = 11, IsTryingToAttack = 12,//指前摇 IsLockingTheDoor = 13, - IsRummagingInTheChest = 14, + IsOpeningTheChest = 14, IsClimbingThroughWindows = 15, + IsUsingSpecialSkill = 16, } ~~~ +- 可执行指令的(不用给选手) + ~~~csharp + public bool Commandable() => (playerState!=PlayerStateType.IsDeceased&&playerState!=PlayerStateType.IsEscaped + &&playerState!=PlayerStateType.IsAddicted &&playerState!=PlayerStateType.IsStunned + &&playerState!=PlayerStateType.IsSwinging&&playerState!=PlayerStateType.IsTryingToAttack + &&playerState!=PlayerStateType.IsClimbingThroughWindows); + ~~~ - Bgm(字典) - 得分 - ~~回血率/原始回血率~~ - 当前子弹类型 - 原始子弹类型 -- 持有道具 *(最多三个)(列表)* +- 持有道具 (最多三个)(数组) - 是否隐身 - 队伍ID - 玩家ID @@ -111,7 +122,7 @@ - 各个主动技能CD(字典) - 警戒半径 - double 隐蔽度 -- *翻窗时间* +- 翻窗时间 - 开锁门时间 ### 学生:人物 @@ -210,7 +221,7 @@ 3. 修理电机 4. 开锁门 5. 翻窗 -6. 翻找箱子 +6. 开启箱子 ### 门 - *门分别属于三个教学区:三教,五教,六教* @@ -227,10 +238,31 @@ - *翻越窗户是一种交互行为,执行时,实质是限定方向的减速运动* ### 箱子 -- *监管者和求生者都能与箱子交互,同一时刻就允许一人进行翻找* +- *监管者和求生者都能与箱子交互,同一时刻只允许一人进行开启* - *开启箱子有不同概率获得不同道具。* -- *搜寻物品的基础持续时间为10秒。* -- *未搜寻完成的箱子在下一次需要重新开始搜寻。* +- *开启箱子的基础持续时间为10秒。* +- *未开启完成的箱子在下一次需要重新开始开启。* +- *箱子开启后其中道具才可以被观测和拿取* +- [箱子道具不刷新] +- [箱子不可被关闭] +- [箱子内道具最多两个] + +### 道具 +- 每次玩家试图捡起道具时,需要确保道具栏有空位 +- indexing指道具栏数组下标从0开始 +- 扔道具 + - Logic内实现 + ~~~csharp + public void ThrowProp(long playerID, int indexing) + ~~~ + - 对应下标出现空位,不会对数组进行重新排序 +- 使用道具 + - Logic内实现 + ~~~csharp + public void UseProp(long playerID,int indexing) + ~~~ + - 对应下标出现空位,不会对数组进行重新排序 + ### 治疗 - 可行动的求生者可以对受伤的其他求生者进行治疗,治疗完成后会回复被治疗程度的血量。