chore: rebuild the skill system againtags/0.1.0
| @@ -22,7 +22,11 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| return false; | return false; | ||||
| } | } | ||||
| public override bool CanBeBombed(GameObjType gameObjType) | |||||
| { | |||||
| if (gameObjType == GameObjType.Character) return true; | |||||
| return false; | |||||
| } | |||||
| public override BulletType TypeOfBullet => BulletType.CommonAttackOfGhost; | public override BulletType TypeOfBullet => BulletType.CommonAttackOfGhost; | ||||
| } | } | ||||
| @@ -44,6 +48,11 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| return false; | return false; | ||||
| } | } | ||||
| public override bool CanBeBombed(GameObjType gameObjType) | |||||
| { | |||||
| if (gameObjType == GameObjType.Character) return true; | |||||
| return false; | |||||
| } | |||||
| public override BulletType TypeOfBullet => BulletType.FlyingKnife; | public override BulletType TypeOfBullet => BulletType.FlyingKnife; | ||||
| @@ -68,6 +77,11 @@ namespace GameClass.GameObj | |||||
| // 圆形攻击范围 | // 圆形攻击范围 | ||||
| return XY.Distance(this.Position, target.Position) <= BulletBombRange; | 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; | public override BulletType TypeOfBullet => BulletType.AtomBomb; | ||||
| @@ -92,6 +106,11 @@ namespace GameClass.GameObj | |||||
| // 圆形攻击范围 | // 圆形攻击范围 | ||||
| return XY.Distance(this.Position, target.Position) <= BulletBombRange; | 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; | public override BulletType TypeOfBullet => BulletType.OrdinaryBullet; | ||||
| } | } | ||||
| @@ -116,6 +135,11 @@ namespace GameClass.GameObj | |||||
| // 圆形攻击范围 | // 圆形攻击范围 | ||||
| return XY.Distance(this.Position, target.Position) <= BulletBombRange; | 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; | public override BulletType TypeOfBullet => BulletType.FastBullet; | ||||
| } | } | ||||
| @@ -135,6 +159,11 @@ namespace GameClass.GameObj | |||||
| public override int RecoveryFromHit => GameData.basicRecoveryFromHit; | public override int RecoveryFromHit => GameData.basicRecoveryFromHit; | ||||
| public override bool IsToBomb => true; | 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) | public override bool CanAttack(GameObj target) | ||||
| { | { | ||||
| double FacingAngle = Math.Atan2(this.FacingDirection.y, this.FacingDirection.x); | double FacingAngle = Math.Atan2(this.FacingDirection.y, this.FacingDirection.x); | ||||
| @@ -1,7 +1,32 @@ | |||||
| using Preparation.Interface; | |||||
| using Preparation.Utility; | |||||
| using System; | |||||
| using Preparation.Utility; | |||||
| namespace GameClass.GameObj | 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; | |||||
| }*/ | |||||
| } | } | ||||
| @@ -30,6 +30,7 @@ namespace GameClass.GameObj | |||||
| /// <param name="target">被尝试攻击者</param> | /// <param name="target">被尝试攻击者</param> | ||||
| /// <returns>是否可以攻击到</returns> | /// <returns>是否可以攻击到</returns> | ||||
| public abstract bool CanAttack(GameObj target); | public abstract bool CanAttack(GameObj target); | ||||
| public abstract bool CanBeBombed(GameObjType gameObjType); | |||||
| protected override bool IgnoreCollideExecutor(IGameObj targetObj) | protected override bool IgnoreCollideExecutor(IGameObj targetObj) | ||||
| { | { | ||||
| @@ -12,9 +12,22 @@ namespace GameClass.GameObj | |||||
| private readonly IOccupation occupation; | private readonly IOccupation occupation; | ||||
| public IOccupation Occupation => occupation; | public IOccupation Occupation => occupation; | ||||
| private Dictionary<ActiveSkillType, int> timeUntilActiveSkillAvailable = new(); | private Dictionary<ActiveSkillType, int> timeUntilActiveSkillAvailable = new(); | ||||
| public Dictionary<ActiveSkillType, int> TimeUntilActiveSkillAvailable => timeUntilActiveSkillAvailable; | public Dictionary<ActiveSkillType, int> TimeUntilActiveSkillAvailable => timeUntilActiveSkillAvailable; | ||||
| private Dictionary<ActiveSkillType, IActiveSkill> iActiveSkillDictionary = new(); | |||||
| public Dictionary<ActiveSkillType, IActiveSkill> IActiveSkillDictionary => iActiveSkillDictionary; | |||||
| public IActiveSkill? UseIActiveSkill(ActiveSkillType activeSkillType) | |||||
| { | |||||
| if (Occupation.ListOfIActiveSkill.Contains(activeSkillType)) | |||||
| { | |||||
| return IActiveSkillDictionary[activeSkillType]; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| public bool SetTimeUntilActiveSkillAvailable(ActiveSkillType activeSkillType, int timeUntilActiveSkillAvailable) | public bool SetTimeUntilActiveSkillAvailable(ActiveSkillType activeSkillType, int timeUntilActiveSkillAvailable) | ||||
| { | { | ||||
| if (TimeUntilActiveSkillAvailable.ContainsKey(activeSkillType)) | if (TimeUntilActiveSkillAvailable.ContainsKey(activeSkillType)) | ||||
| @@ -73,10 +86,13 @@ namespace GameClass.GameObj | |||||
| this.concealment = Occupation.Concealment; | this.concealment = Occupation.Concealment; | ||||
| this.alertnessRadius = Occupation.AlertnessRadius; | this.alertnessRadius = Occupation.AlertnessRadius; | ||||
| this.characterType = characterType; | this.characterType = characterType; | ||||
| this.TimeOfOpeningOrLocking = Occupation.TimeOfOpeningOrLocking; | |||||
| this.TimeOfClimbingThroughWindows = Occupation.TimeOfClimbingThroughWindows; | |||||
| foreach (var activeSkill in this.Occupation.ListOfIActiveSkill) | foreach (var activeSkill in this.Occupation.ListOfIActiveSkill) | ||||
| { | { | ||||
| this.TimeUntilActiveSkillAvailable.Add(activeSkill, 0); | this.TimeUntilActiveSkillAvailable.Add(activeSkill, 0); | ||||
| this.IActiveSkillDictionary.Add(activeSkill, SkillFactory.FindIActiveSkill(activeSkill)); | |||||
| } | } | ||||
| // UsePassiveSkill(); //创建player时开始被动技能,这一过程也可以放到gamestart时进行 | // UsePassiveSkill(); //创建player时开始被动技能,这一过程也可以放到gamestart时进行 | ||||
| @@ -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; | // private int deathCount = 0; | ||||
| // public int DeathCount => deathCount; // 玩家的死亡次数 | // 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; | get => propInventory; | ||||
| set | set | ||||
| @@ -148,16 +155,30 @@ namespace GameClass.GameObj | |||||
| /// 使用物品栏中的道具 | /// 使用物品栏中的道具 | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns>被使用的道具</returns> | /// <returns>被使用的道具</returns> | ||||
| public Prop? UseProp() | |||||
| public Prop? UseProp(int indexing) | |||||
| { | { | ||||
| if (indexing < 0 || indexing >= GameData.maxNumOfPropInPropInventory) | |||||
| return null; | |||||
| lock (gameObjLock) | lock (gameObjLock) | ||||
| { | { | ||||
| var oldProp = PropInventory; | |||||
| PropInventory = null; | |||||
| return oldProp; | |||||
| Prop prop = propInventory[indexing]; | |||||
| PropInventory[indexing] = null; | |||||
| return prop; | |||||
| } | } | ||||
| } | } | ||||
| /// <summary> | |||||
| /// 如果indexing==GameData.maxNumOfPropInPropInventory表明道具栏为满 | |||||
| /// </summary> | |||||
| public int IndexingOfAddProp() | |||||
| { | |||||
| int indexing = 0; | |||||
| for (; indexing < GameData.maxNumOfPropInPropInventory; ++indexing) | |||||
| if (PropInventory[indexing] == null) | |||||
| break; | |||||
| return indexing; | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// 是否在隐身 | /// 是否在隐身 | ||||
| /// </summary> | /// </summary> | ||||
| @@ -226,7 +247,18 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| } | } | ||||
| private int timeOfClimbingThroughWindows; | |||||
| public int TimeOfClimbingThroughWindows | |||||
| { | |||||
| get => timeOfClimbingThroughWindows; | |||||
| set | |||||
| { | |||||
| lock (gameObjLock) | |||||
| { | |||||
| timeOfClimbingThroughWindows = value; | |||||
| } | |||||
| } | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// 进行一次攻击 | /// 进行一次攻击 | ||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,34 @@ | |||||
| using Preparation.Utility; | |||||
| using System.Collections.Generic; | |||||
| namespace GameClass.GameObj | |||||
| { | |||||
| /// <summary> | |||||
| /// 箱子 | |||||
| /// </summary> | |||||
| 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -96,6 +96,18 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| return flag; | 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) | public void Add(GameObj gameObj) | ||||
| { | { | ||||
| GameObjLockDict[gameObj.Type].EnterWriteLock(); | GameObjLockDict[gameObj.Type].EnterWriteLock(); | ||||
| @@ -134,55 +146,22 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| case (uint)PlaceType.Wall: | 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; | break; | ||||
| } | } | ||||
| case (uint)PlaceType.Doorway: | 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; | break; | ||||
| } | } | ||||
| case (uint)PlaceType.EmergencyExit: | 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; | break; | ||||
| } | } | ||||
| case (uint)PlaceType.Generator: | 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; | break; | ||||
| } | } | ||||
| case (uint)PlaceType.BirthPoint1: | case (uint)PlaceType.BirthPoint1: | ||||
| @@ -12,7 +12,8 @@ namespace GameClass.GameObj | |||||
| protected override bool IgnoreCollideExecutor(IGameObj targetObj) | 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 true; | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -74,12 +74,12 @@ namespace GameEngine | |||||
| { | { | ||||
| if (obj.IsMoving) // 已经移动的物体不能再移动 | if (obj.IsMoving) // 已经移动的物体不能再移动 | ||||
| return; | return; | ||||
| if (!obj.IsAvailable || !gameTimer.IsGaming) | |||||
| return; | |||||
| new Thread | new Thread | ||||
| ( | ( | ||||
| () => | () => | ||||
| { | { | ||||
| if (!obj.IsAvailable && gameTimer.IsGaming) | |||||
| return; | |||||
| lock (obj.MoveLock) | lock (obj.MoveLock) | ||||
| obj.IsMoving = true; | obj.IsMoving = true; | ||||
| @@ -93,7 +93,7 @@ namespace GameEngine | |||||
| () => | () => | ||||
| { | { | ||||
| moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; | moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; | ||||
| XY res = new XY(direction, moveVecLength); | |||||
| res = new XY(direction, moveVecLength); | |||||
| // 越界情况处理:如果越界,则与越界方块碰撞 | // 越界情况处理:如果越界,则与越界方块碰撞 | ||||
| bool flag; // 循环标志 | bool flag; // 循环标志 | ||||
| @@ -136,7 +136,7 @@ namespace GameEngine | |||||
| if (!isDestroyed) | if (!isDestroyed) | ||||
| { | { | ||||
| moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; | 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) | if ((collisionObj = collisionChecker.CheckCollision(obj, res)) == null) | ||||
| { | { | ||||
| obj.MovingSetPos(res, GetPlaceType(obj.Position + res)); | obj.MovingSetPos(res, GetPlaceType(obj.Position + res)); | ||||
| @@ -25,11 +25,7 @@ namespace Gaming | |||||
| public bool Stop(Character player) | 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; | player.PlayerState = PlayerStateType.Null; | ||||
| return true; | return true; | ||||
| @@ -82,11 +78,11 @@ namespace Gaming | |||||
| .Start(); | .Start(); | ||||
| if (generatorForFix.DegreeOfFRepair == GameData.degreeOfFixedGenerator) | 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; | int numOfFixedGenerator = 0; | ||||
| foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator]) | 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) | 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; | return false; | ||||
| if (playerTreated.HP + playerTreated.DegreeOfTreatment >= playerTreated.MaxHp) | if (playerTreated.HP + playerTreated.DegreeOfTreatment >= playerTreated.MaxHp) | ||||
| @@ -68,6 +68,32 @@ namespace Gaming | |||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| } | } | ||||
| public void BeStunned(Character player, int time) | |||||
| { | |||||
| new Thread | |||||
| (() => | |||||
| { | |||||
| player.PlayerState = PlayerStateType.IsStunned; | |||||
| new FrameRateTaskExecutor<int>( | |||||
| () => 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) | private void Die(Character player) | ||||
| { | { | ||||
| @@ -82,22 +108,14 @@ namespace Gaming | |||||
| // gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); | // 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(); | // 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) | private void BombObj(Bullet bullet, GameObj objBeingShot) | ||||
| { | { | ||||
| switch (objBeingShot.Type) | switch (objBeingShot.Type) | ||||
| { | { | ||||
| case GameObjType.Character: | case GameObjType.Character: | ||||
| if (!((Character)objBeingShot).IsGhost()) | |||||
| if ((!((Character)objBeingShot).IsGhost()) && bullet.Parent.IsGhost()) | |||||
| if (((Character)objBeingShot).BeAttacked(bullet)) | if (((Character)objBeingShot).BeAttacked(bullet)) | ||||
| { | { | ||||
| BeAddictedToGame((Student)objBeingShot); | BeAddictedToGame((Student)objBeingShot); | ||||
| } | } | ||||
| // if (((Character)objBeingShot).IsGhost() && !bullet.Parent.IsGhost() && bullet.TypeOfBullet == BulletType.Ram) | |||||
| // BeStunned((Character)objBeingShot, bullet.AP); | |||||
| break; | |||||
| default: | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -218,9 +236,9 @@ namespace Gaming | |||||
| foreach (var kvp in gameMap.GameObjDict) | 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 | try | ||||
| { | { | ||||
| foreach (var item in gameMap.GameObjDict[kvp.Key]) | foreach (var item in gameMap.GameObjDict[kvp.Key]) | ||||
| @@ -232,7 +250,7 @@ namespace Gaming | |||||
| } | } | ||||
| finally | 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)) | (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<int>( | |||||
| 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<int>( | |||||
| 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) | 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 | #if DEBUG | ||||
| Console.WriteLine($"playerID:{player.ID} successfully attacked!"); | Console.WriteLine($"playerID:{player.ID} successfully attacked!"); | ||||
| #endif | #endif | ||||
| @@ -368,4 +382,4 @@ namespace Gaming | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -197,14 +197,6 @@ namespace Gaming | |||||
| public void EndGame() | public void EndGame() | ||||
| { | { | ||||
| gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); | |||||
| /*try | |||||
| { | |||||
| } | |||||
| finally | |||||
| { | |||||
| }*/ | |||||
| gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); | |||||
| } | } | ||||
| public bool MovePlayer(long playerID, int moveTimeInMilliseconds, double angle) | public bool MovePlayer(long playerID, int moveTimeInMilliseconds, double angle) | ||||
| { | { | ||||
| @@ -301,24 +293,24 @@ namespace Gaming | |||||
| _ = attackManager.Attack(player, angle); | _ = attackManager.Attack(player, angle); | ||||
| } | } | ||||
| } | } | ||||
| public void UseProp(long playerID) | |||||
| public void UseProp(long playerID, int indexing) | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return; | return; | ||||
| Character? player = gameMap.FindPlayer(playerID); | Character? player = gameMap.FindPlayer(playerID); | ||||
| if (player != null) | 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) | if (!gameMap.Timer.IsGaming) | ||||
| return; | return; | ||||
| Character? player = gameMap.FindPlayer(playerID); | Character? player = gameMap.FindPlayer(playerID); | ||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| propManager.ThrowProp(player, timeInmillionSeconds, angle); | |||||
| propManager.ThrowProp(player, indexing); | |||||
| } | } | ||||
| } | } | ||||
| public bool PickProp(long playerID, PropType propType = PropType.Null) | public bool PickProp(long playerID, PropType propType = PropType.Null) | ||||
| @@ -28,11 +28,11 @@ namespace Gaming | |||||
| ProduceProp(); | ProduceProp(); | ||||
| } | } | ||||
| public void UseProp(Character player) | |||||
| public void UseProp(Character player, int indexing) | |||||
| { | { | ||||
| if (player.IsResetting) | if (player.IsResetting) | ||||
| return; | return; | ||||
| Prop? prop = player.UseProp(); | |||||
| Prop? prop = player.UseProp(indexing); | |||||
| switch (prop?.GetPropType()) | switch (prop?.GetPropType()) | ||||
| { | { | ||||
| case PropType.Spear: | case PropType.Spear: | ||||
| @@ -62,6 +62,10 @@ namespace Gaming | |||||
| { | { | ||||
| if (player.IsResetting) | if (player.IsResetting) | ||||
| return false; | return false; | ||||
| int indexing = player.IndexingOfAddProp(); | |||||
| if (indexing == GameData.maxNumOfPropInPropInventory) | |||||
| return false; | |||||
| Prop? pickProp = null; | Prop? pickProp = null; | ||||
| if (propType == PropType.Null) // 自动检查有无道具可捡 | if (propType == PropType.Null) // 自动检查有无道具可捡 | ||||
| { | { | ||||
| @@ -70,9 +74,9 @@ namespace Gaming | |||||
| { | { | ||||
| foreach (Prop prop in gameMap.GameObjDict[GameObjType.Prop]) | 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) | 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) | 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; | return true; | ||||
| } | } | ||||
| else | else | ||||
| return false; | 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; | return; | ||||
| if (player.IsResetting) // 移动中也能扔,但由于“惯性”,可能初始位置会有点变化 | |||||
| return; | |||||
| Prop? prop = player.UseProp(); | |||||
| Prop? prop = player.UseProp(indexing); | |||||
| if (prop == null) | if (prop == null) | ||||
| return; | return; | ||||
| prop.CanMove = true; | |||||
| prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position)); | 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() | private void ProduceProp() | ||||
| { | { | ||||
| int len = availableCellForGenerateProp.Count; | int len = availableCellForGenerateProp.Count; | ||||
| @@ -182,30 +146,23 @@ namespace Gaming | |||||
| int rand = r.Next(0, len); | int rand = r.Next(0, len); | ||||
| XY randPos = availableCellForGenerateProp[rand]; | 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, | GameData.PropProduceTime, | ||||
| @@ -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<int>( | |||||
| () => !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<int>( | |||||
| () => 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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<int>( | |||||
| 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<int>( | |||||
| () => !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<int>( | |||||
| 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -14,7 +14,20 @@ namespace Gaming | |||||
| public bool UseActiveSkill(Character character, ActiveSkillType activeSkillType) | public bool UseActiveSkill(Character character, ActiveSkillType activeSkillType) | ||||
| { | { | ||||
| if (character.Occupation.ListOfIActiveSkill.Contains(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; | return false; | ||||
| } | } | ||||
| public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType) | public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType) | ||||
| @@ -15,6 +15,7 @@ namespace Preparation.Interface | |||||
| public double Concealment { get; } | public double Concealment { get; } | ||||
| public int AlertnessRadius { get; } | public int AlertnessRadius { get; } | ||||
| public int TimeOfOpeningOrLocking { get; } | public int TimeOfOpeningOrLocking { get; } | ||||
| public int TimeOfClimbingThroughWindows { get; } | |||||
| } | } | ||||
| public interface IGhost : IOccupation | public interface IGhost : IOccupation | ||||
| @@ -53,6 +54,10 @@ namespace Preparation.Interface | |||||
| public int timeOfOpeningOrLocking = GameData.basicTimeOfOpeningOrLocking; | public int timeOfOpeningOrLocking = GameData.basicTimeOfOpeningOrLocking; | ||||
| public int TimeOfOpeningOrLocking => timeOfOpeningOrLocking; | public int TimeOfOpeningOrLocking => timeOfOpeningOrLocking; | ||||
| public int timeOfClimbingThroughWindows = GameData.basicTimeOfClimbingThroughWindows; | |||||
| public int TimeOfClimbingThroughWindows => timeOfClimbingThroughWindows; | |||||
| } | } | ||||
| public class Athlete : IStudent | public class Athlete : IStudent | ||||
| { | { | ||||
| @@ -70,7 +75,7 @@ namespace Preparation.Interface | |||||
| public BulletType InitBullet => BulletType.Null; | public BulletType InitBullet => BulletType.Null; | ||||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BeginToCharge }); | |||||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.CanBeginToCharge }); | |||||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | ||||
| public const int fixSpeed = GameData.basicFixSpeed / 10 * 6; | 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 const int alertnessRadius = (int)(GameData.basicAlertnessRadius * 0.9); | ||||
| public int AlertnessRadius => alertnessRadius; | public int AlertnessRadius => alertnessRadius; | ||||
| public int timeOfOpeningOrLocking = GameData.basicTimeOfOpeningOrLocking; | |||||
| public int timeOfOpeningOrLocking = GameData.basicTimeOfOpeningOrLocking * 12 / 10; | |||||
| public int TimeOfOpeningOrLocking => timeOfOpeningOrLocking; | public int TimeOfOpeningOrLocking => timeOfOpeningOrLocking; | ||||
| public int timeOfClimbingThroughWindows = GameData.basicTimeOfClimbingThroughWindows / 87 * 80; | |||||
| public int TimeOfClimbingThroughWindows => timeOfClimbingThroughWindows; | |||||
| } | } | ||||
| } | } | ||||
| @@ -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; } | |||||
| } | |||||
| } | |||||
| @@ -20,8 +20,9 @@ namespace Preparation.Utility | |||||
| IsStunned = 11, | IsStunned = 11, | ||||
| IsTryingToAttack = 12,//指前摇 | IsTryingToAttack = 12,//指前摇 | ||||
| IsLockingTheDoor = 13, | IsLockingTheDoor = 13, | ||||
| IsRummagingInTheChest = 14, | |||||
| IsOpeningTheChest = 14, | |||||
| IsClimbingThroughWindows = 15, | IsClimbingThroughWindows = 15, | ||||
| IsUsingSpecialSkill = 16, | |||||
| } | } | ||||
| public enum GameObjType | public enum GameObjType | ||||
| { | { | ||||
| @@ -40,6 +41,7 @@ namespace Preparation.Utility | |||||
| OutOfBoundBlock = 11, // 范围外 | OutOfBoundBlock = 11, // 范围外 | ||||
| Window = 12, | Window = 12, | ||||
| Door = 13, | Door = 13, | ||||
| Chest = 14, | |||||
| } | } | ||||
| public enum ShapeType | public enum ShapeType | ||||
| { | { | ||||
| @@ -55,7 +57,8 @@ namespace Preparation.Utility | |||||
| FastBullet = 3, // 快速子弹 | FastBullet = 3, // 快速子弹 | ||||
| LineBullet = 4, // 直线子弹 | LineBullet = 4, // 直线子弹 | ||||
| FlyingKnife = 5, //飞刀 | FlyingKnife = 5, //飞刀 | ||||
| CommonAttackOfGhost = 6 | |||||
| CommonAttackOfGhost = 6, | |||||
| // Ram = 7, | |||||
| } | } | ||||
| public enum PropType // 道具类型 | public enum PropType // 道具类型 | ||||
| { | { | ||||
| @@ -84,7 +87,7 @@ namespace Preparation.Utility | |||||
| NuclearWeapon = 3, | NuclearWeapon = 3, | ||||
| SuperFast = 4, | SuperFast = 4, | ||||
| UseKnife = 5, | UseKnife = 5, | ||||
| BeginToCharge = 6 | |||||
| CanBeginToCharge = 6 | |||||
| } | } | ||||
| public enum PassiveSkillType | public enum PassiveSkillType | ||||
| { | { | ||||
| @@ -52,38 +52,28 @@ namespace Preparation.Utility | |||||
| #endregion | #endregion | ||||
| #region 角色相关 | #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 basicTreatSpeed = 100; | ||||
| public const int basicFixSpeed = 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 basicMaxGamingAddiction = 60000;//基本完全沉迷时间 | ||||
| public const int BeginGamingAddiction = 10003; | public const int BeginGamingAddiction = 10003; | ||||
| public const int MidGamingAddiction = 30000; | public const int MidGamingAddiction = 30000; | ||||
| public const int basicTreatmentDegree = 1500000; | public const int basicTreatmentDegree = 1500000; | ||||
| public const int basicRescueDegree = 100000; | 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 basicMoveSpeed = 1260; // 基本移动速度,单位:s-1 | ||||
| public const int basicBulletMoveSpeed = 2700; // 基本子弹移动速度,单位:s-1 | |||||
| public const int characterMaxSpeed = 12000; // 最大速度 | public const int characterMaxSpeed = 12000; // 最大速度 | ||||
| public const int basicBulletMoveSpeed = 2700; // 基本子弹移动速度,单位:s-1 | |||||
| public const double basicConcealment = 1.0; | public const double basicConcealment = 1.0; | ||||
| public const int basicAlertnessRadius = 30700; | public const int basicAlertnessRadius = 30700; | ||||
| public const int basicTimeOfOpeningOrLocking = 3000; | |||||
| public const int maxNumOfPropInPropInventory = 3; | |||||
| public const int addScoreWhenKillOneLevelPlayer = 30; // 击杀一级角色获得的加分 | 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 XY PosWhoDie = new XY(1, 1); | ||||
| @@ -95,6 +85,32 @@ namespace Preparation.Utility | |||||
| _ => false, | _ => 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; // 普通技能标准持续时间 | |||||
| /// <summary> | |||||
| /// BeginToCharge | |||||
| /// </summary> | |||||
| public const int TimeOfGhostFainting = 7220;//=AP of Ram | |||||
| public const int TimeOfStudentFainting = 2090; | |||||
| #endregion | #endregion | ||||
| #region 道具相关 | #region 道具相关 | ||||
| public const int MinPropTypeNum = 1; | public const int MinPropTypeNum = 1; | ||||
| @@ -108,8 +124,8 @@ namespace Preparation.Utility | |||||
| #endregion | #endregion | ||||
| #region 物体相关 | #region 物体相关 | ||||
| public const int degreeOfFixedGenerator = 10300000; | public const int degreeOfFixedGenerator = 10300000; | ||||
| public const int maxNumOfPropInChest = 2; | |||||
| #endregion | #endregion | ||||
| #region 游戏帧相关 | #region 游戏帧相关 | ||||
| public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | ||||
| #endregion | #endregion | ||||
| @@ -17,6 +17,11 @@ namespace Preparation.Utility | |||||
| this.x = (int)(length * Math.Cos(angle)); | this.x = (int)(length * Math.Cos(angle)); | ||||
| this.y = (int)(length * Math.Sin(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() | public override string ToString() | ||||
| { | { | ||||
| return "(" + x.ToString() + "," + y.ToString() + ")"; | return "(" + x.ToString() + "," + y.ToString() + ")"; | ||||
| @@ -9,7 +9,10 @@ | |||||
| ## 游戏简介 | ## 游戏简介 | ||||
| - 1位监管者对抗4位求生者的非对称竞技模式 | - 1位监管者对抗4位求生者的非对称竞技模式 | ||||
| - 略 | |||||
| - [本届THUAI电子系赛道为以4名同学和1名捣蛋鬼的求学与阻挠展开的非对称竞技模式,同学需要完成足够的家庭作业和考试,相互督促以避免沉迷娱乐生活,利用道具地形躲避捣蛋鬼的各种干扰诱惑,完成学业;捣蛋鬼则要极力阻止。] | |||||
| - [我们的设计是一个非对称游戏,类似第五人格,分为学生、捣蛋鬼两个阵营。在游戏中,学生修完若干课程之后通过考试即可顺利毕业,捣蛋鬼试图干扰学生使其沉迷游戏,以致于无法修完规定课程,直至挂科、退学。] | |||||
| [对于选手来说,需要提前制定好学生的学习方案以抵御对方捣蛋鬼的干扰,类似地,也需要制定好捣蛋鬼的行动策略以影响对方学生的学习,也即每队至少要写好两份代码以执行不同阵营的不同策略。] | |||||
| [当一局比赛结束(场上的学生有且仅有两种状态:退学或毕业)时,分别记录双方总得分;之后双方换边进行下半场比赛。最终将每队的学生方、捣蛋鬼方的得分相加,比较总得分判断胜负。] | |||||
| ## 地图 | ## 地图 | ||||
| - 地图为矩形区域,地图上的游戏对象坐标为(x, y),且x和y均为整数。x坐标轴正方向竖直向下, | - 地图为矩形区域,地图上的游戏对象坐标为(x, y),且x和y均为整数。x坐标轴正方向竖直向下, | ||||
| @@ -91,16 +94,24 @@ | |||||
| IsStunned = 11, | IsStunned = 11, | ||||
| IsTryingToAttack = 12,//指前摇 | IsTryingToAttack = 12,//指前摇 | ||||
| IsLockingTheDoor = 13, | IsLockingTheDoor = 13, | ||||
| IsRummagingInTheChest = 14, | |||||
| IsOpeningTheChest = 14, | |||||
| IsClimbingThroughWindows = 15, | 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(字典) | - Bgm(字典) | ||||
| - 得分 | - 得分 | ||||
| - ~~回血率/原始回血率~~ | - ~~回血率/原始回血率~~ | ||||
| - 当前子弹类型 | - 当前子弹类型 | ||||
| - 原始子弹类型 | - 原始子弹类型 | ||||
| - 持有道具 *(最多三个)(列表)* | |||||
| - 持有道具 (最多三个)(数组) | |||||
| - 是否隐身 | - 是否隐身 | ||||
| - 队伍ID | - 队伍ID | ||||
| - 玩家ID | - 玩家ID | ||||
| @@ -111,7 +122,7 @@ | |||||
| - 各个主动技能CD(字典) | - 各个主动技能CD(字典) | ||||
| - 警戒半径 | - 警戒半径 | ||||
| - double 隐蔽度 | - double 隐蔽度 | ||||
| - *翻窗时间* | |||||
| - 翻窗时间 | |||||
| - 开锁门时间 | - 开锁门时间 | ||||
| ### 学生:人物 | ### 学生:人物 | ||||
| @@ -210,7 +221,7 @@ | |||||
| 3. 修理电机 | 3. 修理电机 | ||||
| 4. 开锁门 | 4. 开锁门 | ||||
| 5. 翻窗 | 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) | |||||
| ~~~ | |||||
| - 对应下标出现空位,不会对数组进行重新排序 | |||||
| ### 治疗 | ### 治疗 | ||||
| - 可行动的求生者可以对受伤的其他求生者进行治疗,治疗完成后会回复被治疗程度的血量。 | - 可行动的求生者可以对受伤的其他求生者进行治疗,治疗完成后会回复被治疗程度的血量。 | ||||