| @@ -61,6 +61,7 @@ namespace GameClass.GameObj | |||||
| this.CanMove = true; | this.CanMove = true; | ||||
| this.score = 0; | this.score = 0; | ||||
| this.buffManager = new BuffManager(); | this.buffManager = new BuffManager(); | ||||
| Debugger.Output(this, characterType.ToString()); | |||||
| switch (characterType) | switch (characterType) | ||||
| { | { | ||||
| case CharacterType.Assassin: | case CharacterType.Assassin: | ||||
| @@ -373,8 +373,8 @@ namespace GameClass.GameObj | |||||
| /// <summary> | /// <summary> | ||||
| /// 角色所属队伍ID | /// 角色所属队伍ID | ||||
| /// </summary> | /// </summary> | ||||
| private long teamID = long.MaxValue; | |||||
| public long TeamID | |||||
| private int teamID = int.MaxValue; | |||||
| public int TeamID | |||||
| { | { | ||||
| get => teamID; | get => teamID; | ||||
| set | set | ||||
| @@ -386,8 +386,8 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| private long playerID = long.MaxValue; | |||||
| public long PlayerID | |||||
| private int playerID = int.MaxValue; | |||||
| public int PlayerID | |||||
| { | { | ||||
| get => playerID; | get => playerID; | ||||
| set | set | ||||
| @@ -91,6 +91,20 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| } | } | ||||
| public class UseRobot : IActiveSkill | |||||
| { | |||||
| public int SkillCD => 0; | |||||
| public int DurationTime => 0; | |||||
| private readonly object commonSkillLock = new object(); | |||||
| public object ActiveSkillLock => commonSkillLock; | |||||
| public bool isBeingUsed = false; | |||||
| public bool IsBeingUsed | |||||
| { | |||||
| get => isBeingUsed; set => isBeingUsed = value; | |||||
| } | |||||
| } | |||||
| public class WriteAnswers : IActiveSkill | public class WriteAnswers : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.commonSkillCD; | public int SkillCD => GameData.commonSkillCD; | ||||
| @@ -110,6 +124,32 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| public bool isBeingUsed = false; | |||||
| public bool IsBeingUsed | |||||
| { | |||||
| get => isBeingUsed; set => isBeingUsed = value; | |||||
| } | |||||
| } | |||||
| public class SummonGolem : IActiveSkill | |||||
| { | |||||
| public int SkillCD => GameData.commonSkillCD; | |||||
| public int DurationTime => 0; | |||||
| private readonly object commonSkillLock = new object(); | |||||
| public object ActiveSkillLock => commonSkillLock; | |||||
| private Golem? golemSummoned = null; | |||||
| public Golem? GolemSummoned | |||||
| { | |||||
| get => golemSummoned; | |||||
| set | |||||
| { | |||||
| lock (commonSkillLock) | |||||
| { | |||||
| golemSummoned = value; | |||||
| } | |||||
| } | |||||
| } | |||||
| public bool isBeingUsed = false; | public bool isBeingUsed = false; | ||||
| public bool IsBeingUsed | public bool IsBeingUsed | ||||
| @@ -152,6 +192,8 @@ namespace GameClass.GameObj | |||||
| return new JumpyBomb(); | return new JumpyBomb(); | ||||
| case ActiveSkillType.WriteAnswers: | case ActiveSkillType.WriteAnswers: | ||||
| return new WriteAnswers(); | return new WriteAnswers(); | ||||
| case ActiveSkillType.SummonGolem: | |||||
| return new SummonGolem(); | |||||
| default: | default: | ||||
| return new NullSkill(); | return new NullSkill(); | ||||
| } | } | ||||
| @@ -175,6 +217,8 @@ namespace GameClass.GameObj | |||||
| return ActiveSkillType.JumpyBomb; | return ActiveSkillType.JumpyBomb; | ||||
| case WriteAnswers: | case WriteAnswers: | ||||
| return ActiveSkillType.WriteAnswers; | return ActiveSkillType.WriteAnswers; | ||||
| case SummonGolem: | |||||
| return ActiveSkillType.SummonGolem; | |||||
| default: | default: | ||||
| return ActiveSkillType.Null; | return ActiveSkillType.Null; | ||||
| } | } | ||||
| @@ -119,6 +119,38 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| return player; | return player; | ||||
| } | } | ||||
| public Character? FindPlayerToAction(long playerID) | |||||
| { | |||||
| Character? player = null; | |||||
| gameObjLockDict[GameObjType.Character].EnterReadLock(); | |||||
| try | |||||
| { | |||||
| foreach (Character person in gameObjDict[GameObjType.Character]) | |||||
| { | |||||
| if (playerID == person.ID) | |||||
| { | |||||
| if (person.CharacterType == CharacterType.TechOtaku && person.FindIActiveSkill(ActiveSkillType.UseRobot).IsBeingUsed) | |||||
| { | |||||
| foreach (Character character in gameObjDict[GameObjType.Character]) | |||||
| { | |||||
| if (playerID + GameData.numOfPeople == character.ID) | |||||
| { | |||||
| player = character; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| else player = person; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| finally | |||||
| { | |||||
| gameObjLockDict[GameObjType.Character].ExitReadLock(); | |||||
| } | |||||
| return player; | |||||
| } | |||||
| public bool Remove(GameObj gameObj) | public bool Remove(GameObj gameObj) | ||||
| { | { | ||||
| bool flag = false; | bool flag = false; | ||||
| @@ -7,46 +7,33 @@ namespace GameEngine | |||||
| { | { | ||||
| internal class CollisionChecker | internal class CollisionChecker | ||||
| { | { | ||||
| /// <summary> | |||||
| /// 碰撞检测,如果这样行走是否会与之碰撞,返回与之碰撞的物体 | |||||
| /// </summary> | |||||
| /// <param name="obj">移动的物体</param> | |||||
| /// <param name="moveVec">移动的位移向量</param> | |||||
| /// <returns>和它碰撞的物体</returns> | |||||
| public IGameObj? CheckCollision(IMoveable obj, XY moveVec) | |||||
| public IGameObj? CheckCollision(IMoveable obj, XY Pos) | |||||
| { | { | ||||
| XY nextPos = obj.Position + moveVec; | |||||
| if (!obj.IsRigid) | |||||
| { | |||||
| if (gameMap.IsOutOfBound(obj)) | |||||
| return gameMap.GetOutOfBound(nextPos); | |||||
| return null; | |||||
| } | |||||
| // 在列表中检查碰撞 | // 在列表中检查碰撞 | ||||
| Func<IEnumerable<IGameObj>, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList = | Func<IEnumerable<IGameObj>, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList = | ||||
| (IEnumerable<IGameObj> lst, ReaderWriterLockSlim listLock) => | (IEnumerable<IGameObj> lst, ReaderWriterLockSlim listLock) => | ||||
| { | |||||
| IGameObj? collisionObj = null; | |||||
| listLock.EnterReadLock(); | |||||
| try | |||||
| { | { | ||||
| foreach (var listObj in lst) | |||||
| IGameObj? collisionObj = null; | |||||
| listLock.EnterReadLock(); | |||||
| try | |||||
| { | { | ||||
| if (obj.WillCollideWith(listObj, nextPos)) | |||||
| foreach (var listObj in lst) | |||||
| { | { | ||||
| collisionObj = listObj; | |||||
| break; | |||||
| if (obj.WillCollideWith(listObj, Pos)) | |||||
| { | |||||
| collisionObj = listObj; | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| finally | |||||
| { | |||||
| listLock.ExitReadLock(); | |||||
| } | |||||
| return collisionObj; | |||||
| }; | |||||
| finally | |||||
| { | |||||
| listLock.ExitReadLock(); | |||||
| } | |||||
| return collisionObj; | |||||
| }; | |||||
| IGameObj? collisionObj = null; | |||||
| IGameObj? collisionObj; | |||||
| foreach (var list in lists) | foreach (var list in lists) | ||||
| { | { | ||||
| if ((collisionObj = CheckCollisionInList(list.Item1, list.Item2)) != null) | if ((collisionObj = CheckCollisionInList(list.Item1, list.Item2)) != null) | ||||
| @@ -58,6 +45,23 @@ namespace GameEngine | |||||
| return null; | return null; | ||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// 碰撞检测,如果这样行走是否会与之碰撞,返回与之碰撞的物体 | |||||
| /// </summary> | |||||
| /// <param name="obj">移动的物体</param> | |||||
| /// <param name="moveVec">移动的位移向量</param> | |||||
| /// <returns>和它碰撞的物体</returns> | |||||
| public IGameObj? CheckCollisionWhenMoving(IMoveable obj, XY moveVec) | |||||
| { | |||||
| XY nextPos = obj.Position + moveVec; | |||||
| if (!obj.IsRigid) | |||||
| { | |||||
| if (gameMap.IsOutOfBound(obj)) | |||||
| return gameMap.GetOutOfBound(nextPos); | |||||
| return null; | |||||
| } | |||||
| return CheckCollision(obj, nextPos); | |||||
| } | |||||
| /// <summary> | |||||
| /// /// 可移动物体(圆)向矩形物体移动时,可移动且不会碰撞的最大距离。直接用double计算,防止误差 | /// /// 可移动物体(圆)向矩形物体移动时,可移动且不会碰撞的最大距离。直接用double计算,防止误差 | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="obj"></param> | /// <param name="obj"></param> | ||||
| @@ -33,6 +33,12 @@ namespace GameEngine | |||||
| return PlaceType.Null; | return PlaceType.Null; | ||||
| } | } | ||||
| } | } | ||||
| public IGameObj? CheckCollision(IMoveable obj, XY Pos) | |||||
| { | |||||
| return collisionChecker.CheckCollision(obj, Pos); | |||||
| } | |||||
| private readonly CollisionChecker collisionChecker; | private readonly CollisionChecker collisionChecker; | ||||
| private readonly Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision; | private readonly Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision; | ||||
| /// <summary> | /// <summary> | ||||
| @@ -65,7 +71,7 @@ namespace GameEngine | |||||
| XY nextPos = obj.Position + moveVec; | XY nextPos = obj.Position + moveVec; | ||||
| double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); | double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); | ||||
| maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); | maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); | ||||
| obj.MovingSetPos(new XY(moveVec.Angle(), maxLen), GetPlaceType(nextPos)); | |||||
| obj.MovingSetPos(new XY(moveVec, maxLen), GetPlaceType(nextPos)); | |||||
| } | } | ||||
| public void MoveObj(IMoveable obj, int moveTime, double direction) | public void MoveObj(IMoveable obj, int moveTime, double direction) | ||||
| @@ -98,7 +104,7 @@ namespace GameEngine | |||||
| do | do | ||||
| { | { | ||||
| flag = false; | flag = false; | ||||
| collisionObj = collisionChecker.CheckCollision(obj, res); | |||||
| collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res); | |||||
| if (collisionObj == null) | if (collisionObj == null) | ||||
| break; | break; | ||||
| @@ -135,7 +141,7 @@ namespace GameEngine | |||||
| { | { | ||||
| moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; | moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; | ||||
| res = new XY(direction, moveVecLength); | res = new XY(direction, moveVecLength); | ||||
| if ((collisionObj = collisionChecker.CheckCollision(obj, res)) == null) | |||||
| if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) | |||||
| { | { | ||||
| obj.MovingSetPos(res, GetPlaceType(obj.Position + res)); | obj.MovingSetPos(res, GetPlaceType(obj.Position + res)); | ||||
| } | } | ||||
| @@ -375,7 +375,7 @@ namespace Gaming | |||||
| loopToDo: () => { }, | loopToDo: () => { }, | ||||
| timeInterval: GameData.frameDuration, | timeInterval: GameData.frameDuration, | ||||
| finallyReturn: () => 0, | finallyReturn: () => 0, | ||||
| maxTotalDuration: (int)((windowToPlayer + windowForClimb.Position - player.Position).Length() / player.MoveSpeed) | |||||
| maxTotalDuration: (int)((windowToPlayer + windowForClimb.Position - player.Position).Length() * 1000 / player.MoveSpeed) | |||||
| ) | ) | ||||
| .Start(); | .Start(); | ||||
| if (player.PlayerState != PlayerStateType.ClimbingThroughWindows) | if (player.PlayerState != PlayerStateType.ClimbingThroughWindows) | ||||
| @@ -386,13 +386,13 @@ namespace Gaming | |||||
| player.ReSetPos(windowToPlayer + windowForClimb.Position, PlaceType.Window); | player.ReSetPos(windowToPlayer + windowForClimb.Position, PlaceType.Window); | ||||
| player.MoveSpeed = player.SpeedOfClimbingThroughWindows; | player.MoveSpeed = player.SpeedOfClimbingThroughWindows; | ||||
| MovePlayer(player, (int)(windowToPlayer.Length() * 3.0 / player.MoveSpeed), (-1 * windowToPlayer).Angle()); | |||||
| MovePlayer(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle()); | |||||
| new FrameRateTaskExecutor<int>( | new FrameRateTaskExecutor<int>( | ||||
| loopCondition: () => player.PlayerState == PlayerStateType.ClimbingThroughWindows && player.IsMoving && gameMap.Timer.IsGaming, | loopCondition: () => player.PlayerState == PlayerStateType.ClimbingThroughWindows && player.IsMoving && gameMap.Timer.IsGaming, | ||||
| loopToDo: () => { }, | loopToDo: () => { }, | ||||
| timeInterval: GameData.frameDuration, | timeInterval: GameData.frameDuration, | ||||
| finallyReturn: () => 0, | finallyReturn: () => 0, | ||||
| maxTotalDuration: (int)(windowToPlayer.Length() * 3.0 / player.MoveSpeed) | |||||
| maxTotalDuration: (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed) | |||||
| ) | ) | ||||
| .Start(); | .Start(); | ||||
| XY PosJumpOff = windowForClimb.Position - 2 * windowToPlayer; | XY PosJumpOff = windowForClimb.Position - 2 * windowToPlayer; | ||||
| @@ -497,7 +497,7 @@ namespace Gaming | |||||
| */ | */ | ||||
| private readonly Map gameMap; | private readonly Map gameMap; | ||||
| private readonly MoveEngine moveEngine; | |||||
| public readonly MoveEngine moveEngine; | |||||
| public ActionManager(Map gameMap) | public ActionManager(Map gameMap) | ||||
| { | { | ||||
| this.gameMap = gameMap; | this.gameMap = gameMap; | ||||
| @@ -287,7 +287,15 @@ namespace Gaming | |||||
| gameMap.Add(prop); | gameMap.Add(prop); | ||||
| } | } | ||||
| } | } | ||||
| if (player.CharacterType == CharacterType.Robot) return; | |||||
| if (player.CharacterType == CharacterType.Robot) | |||||
| { | |||||
| if (((Golem)player).Parent != null && ((Golem)player).Parent.CharacterType == CharacterType.TechOtaku) | |||||
| { | |||||
| ((SummonGolem)player.FindIActiveSkill(ActiveSkillType.SummonGolem)).GolemSummoned = null; | |||||
| player.FindIActiveSkill(ActiveSkillType.UseRobot).IsBeingUsed = false; | |||||
| } | |||||
| return; | |||||
| } | |||||
| ++gameMap.NumOfDeceasedStudent; | ++gameMap.NumOfDeceasedStudent; | ||||
| if (GameData.numOfStudent - gameMap.NumOfDeceasedStudent - gameMap.NumOfEscapedStudent == 1) | if (GameData.numOfStudent - gameMap.NumOfDeceasedStudent - gameMap.NumOfEscapedStudent == 1) | ||||
| { | { | ||||
| @@ -77,7 +77,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| bool res = actionManager.MovePlayer(player, moveTimeInMilliseconds, angle); | bool res = actionManager.MovePlayer(player, moveTimeInMilliseconds, angle); | ||||
| @@ -100,7 +100,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| ICharacter? player = gameMap.FindPlayer(playerID); | |||||
| ICharacter? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (playerTreatedID == -1) | if (playerTreatedID == -1) | ||||
| { | { | ||||
| if (player != null && !player.IsGhost()) | if (player != null && !player.IsGhost()) | ||||
| @@ -121,7 +121,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| ICharacter? player = gameMap.FindPlayer(playerID); | |||||
| ICharacter? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (playerRescuedID == -1) | if (playerRescuedID == -1) | ||||
| { | { | ||||
| if (player != null && !player.IsGhost()) | if (player != null && !player.IsGhost()) | ||||
| @@ -143,7 +143,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| ICharacter? player = gameMap.FindPlayer(playerID); | |||||
| ICharacter? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null && !player.IsGhost()) | if (player != null && !player.IsGhost()) | ||||
| return actionManager.Fix((Student)player); | return actionManager.Fix((Student)player); | ||||
| return false; | return false; | ||||
| @@ -152,7 +152,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| ICharacter? player = gameMap.FindPlayer(playerID); | |||||
| ICharacter? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| if (!player.IsGhost()) | if (!player.IsGhost()) | ||||
| @@ -164,7 +164,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| return ActionManager.Stop(player); | return ActionManager.Stop(player); | ||||
| @@ -175,7 +175,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null && !player.IsGhost()) | if (player != null && !player.IsGhost()) | ||||
| { | { | ||||
| return actionManager.OpenDoorway((Student)player); | return actionManager.OpenDoorway((Student)player); | ||||
| @@ -186,7 +186,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| return actionManager.OpenChest(player); | return actionManager.OpenChest(player); | ||||
| @@ -197,7 +197,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| return actionManager.ClimbingThroughWindow(player); | return actionManager.ClimbingThroughWindow(player); | ||||
| @@ -208,7 +208,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| return actionManager.LockOrOpenDoor(player); | return actionManager.LockOrOpenDoor(player); | ||||
| @@ -219,7 +219,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return; | return; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| _ = attackManager.Attack(player, angle); | _ = attackManager.Attack(player, angle); | ||||
| @@ -229,7 +229,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return; | return; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| PropManager.UseProp(player, propType); | PropManager.UseProp(player, propType); | ||||
| @@ -239,7 +239,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return; | return; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| propManager.ThrowProp(player, propType); | propManager.ThrowProp(player, propType); | ||||
| @@ -249,7 +249,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return false; | return false; | ||||
| Character? player = gameMap.FindPlayer(playerID); | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| return propManager.PickProp(player, propType); | return propManager.PickProp(player, propType); | ||||
| @@ -186,7 +186,7 @@ namespace Gaming | |||||
| { | { | ||||
| gameMap.GameObjLockDict[GameObjType.Chest].ExitWriteLock(); | gameMap.GameObjLockDict[GameObjType.Chest].ExitWriteLock(); | ||||
| } | } | ||||
| /* | |||||
| new Thread | new Thread | ||||
| ( | ( | ||||
| () => | () => | ||||
| @@ -208,6 +208,7 @@ namespace Gaming | |||||
| } | } | ||||
| ) | ) | ||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| */ | |||||
| } | } | ||||
| public PropManager(Map gameMap) // 道具不能扔过墙 | public PropManager(Map gameMap) // 道具不能扔过墙 | ||||
| { | { | ||||
| @@ -11,20 +11,6 @@ namespace Gaming | |||||
| { | { | ||||
| private partial class SkillManager | private partial class SkillManager | ||||
| { | { | ||||
| public bool BecomeVampire(Character player) | |||||
| { | |||||
| return ActiveSkillEffect(player.FindIActiveSkill(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) | public bool CanBeginToCharge(Character player) | ||||
| { | { | ||||
| @@ -42,9 +28,9 @@ namespace Gaming | |||||
| { }); | { }); | ||||
| } | } | ||||
| public static bool BecomeInvisible(Character player) | public static bool BecomeInvisible(Character player) | ||||
| { | { | ||||
| if ((!player.Commandable())) return false; | |||||
| IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.BecomeInvisible); | IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.BecomeInvisible); | ||||
| return ActiveSkillEffect(activeSkill, player, () => | return ActiveSkillEffect(activeSkill, player, () => | ||||
| { | { | ||||
| @@ -55,6 +41,14 @@ namespace Gaming | |||||
| { }); | { }); | ||||
| } | } | ||||
| public static bool UseRobot(Character player) | |||||
| { | |||||
| if ((!player.Commandable()) || ((SummonGolem)player.FindIActiveSkill(ActiveSkillType.SummonGolem)).GolemSummoned == null) return false; | |||||
| IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.UseRobot); | |||||
| activeSkill.IsBeingUsed = (activeSkill.IsBeingUsed) ? false : true; | |||||
| return true; | |||||
| } | |||||
| public bool JumpyBomb(Character player) | public bool JumpyBomb(Character player) | ||||
| { | { | ||||
| return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.JumpyBomb), player, () => | return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.JumpyBomb), player, () => | ||||
| @@ -68,6 +62,7 @@ namespace Gaming | |||||
| public bool WriteAnswers(Character player) | public bool WriteAnswers(Character player) | ||||
| { | { | ||||
| if ((!player.Commandable())) return false; | |||||
| IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.WriteAnswers); | IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.WriteAnswers); | ||||
| return ActiveSkillEffect(activeSkill, player, () => | return ActiveSkillEffect(activeSkill, player, () => | ||||
| { | { | ||||
| @@ -83,6 +78,22 @@ namespace Gaming | |||||
| { }); | { }); | ||||
| } | } | ||||
| public bool SummonGolem(Character player) | |||||
| { | |||||
| if ((!player.Commandable())) return false; | |||||
| IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.SummonGolem); | |||||
| if (((SummonGolem)activeSkill).GolemSummoned != null) return false; | |||||
| XY res = player.Position + new XY(player.FacingDirection, player.Radius * 2); | |||||
| if (actionManager.moveEngine.CheckCollision(player, res) != null) | |||||
| return false; | |||||
| return ActiveSkillEffect(activeSkill, player, () => | |||||
| { | |||||
| characterManager.AddPlayer(res, player.TeamID, player.PlayerID + GameData.numOfPeople, CharacterType.Robot, player); | |||||
| }, | |||||
| () => | |||||
| { }); | |||||
| } | |||||
| public static bool UseKnife(Character player) | public static bool UseKnife(Character player) | ||||
| { | { | ||||
| @@ -97,6 +108,7 @@ namespace Gaming | |||||
| public bool Howl(Character player) | public bool Howl(Character player) | ||||
| { | { | ||||
| if ((!player.Commandable())) return false; | |||||
| return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Howl), player, () => | return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Howl), player, () => | ||||
| { | { | ||||
| gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | ||||
| @@ -125,6 +137,7 @@ namespace Gaming | |||||
| public bool Punish(Character player) | public bool Punish(Character player) | ||||
| { | { | ||||
| if ((!player.Commandable())) return false; | |||||
| return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Punish), player, () => | return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Punish), player, () => | ||||
| { | { | ||||
| gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | ||||
| @@ -153,18 +166,6 @@ namespace Gaming | |||||
| { }); | { }); | ||||
| } | } | ||||
| public bool SuperFast(Character player) | |||||
| { | |||||
| return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.SuperFast), player, () => | |||||
| { | |||||
| player.AddMoveSpeed(player.FindIActiveSkill(ActiveSkillType.SuperFast).DurationTime, 3.0); | |||||
| Debugger.Output(player, "moves very fast!"); | |||||
| }, | |||||
| () => | |||||
| { }); | |||||
| } | |||||
| public static bool ActiveSkillEffect(IActiveSkill activeSkill, Character player, Action startSkill, Action endSkill) | public static bool ActiveSkillEffect(IActiveSkill activeSkill, Character player, Action startSkill, Action endSkill) | ||||
| { | { | ||||
| lock (activeSkill.ActiveSkillLock) | lock (activeSkill.ActiveSkillLock) | ||||
| @@ -37,6 +37,9 @@ namespace Gaming | |||||
| case ActiveSkillType.WriteAnswers: | case ActiveSkillType.WriteAnswers: | ||||
| WriteAnswers(character); | WriteAnswers(character); | ||||
| break; | break; | ||||
| case ActiveSkillType.SummonGolem: | |||||
| SummonGolem(character); | |||||
| break; | |||||
| default: | default: | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -5,7 +5,7 @@ namespace Preparation.Interface | |||||
| { | { | ||||
| public interface ICharacter : IGameObj | public interface ICharacter : IGameObj | ||||
| { | { | ||||
| public long TeamID { get; } | |||||
| public int TeamID { get; } | |||||
| public int HP { get; set; } | public int HP { get; set; } | ||||
| public int Score { get; } | public int Score { get; } | ||||
| public void AddScore(int add); | public void AddScore(int add); | ||||
| @@ -295,7 +295,7 @@ namespace Preparation.Interface | |||||
| public BulletType InitBullet => BulletType.Null; | public BulletType InitBullet => BulletType.Null; | ||||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { }); | |||||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.SummonGolem, ActiveSkillType.UseRobot }); | |||||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | ||||
| public const int fixSpeed = (int)(GameData.basicFixSpeed * 1.1); | public const int fixSpeed = (int)(GameData.basicFixSpeed * 1.1); | ||||
| @@ -89,12 +89,13 @@ namespace Preparation.Utility | |||||
| BecomeInvisible = 1, | BecomeInvisible = 1, | ||||
| BecomeVampire = 2, | BecomeVampire = 2, | ||||
| JumpyBomb = 3, | JumpyBomb = 3, | ||||
| SuperFast = 4, | |||||
| SummonGolem = 4, | |||||
| UseKnife = 5, | UseKnife = 5, | ||||
| CanBeginToCharge = 6, | CanBeginToCharge = 6, | ||||
| Punish = 7, | Punish = 7, | ||||
| WriteAnswers = 8, | WriteAnswers = 8, | ||||
| Howl = 9, | Howl = 9, | ||||
| UseRobot = 10, | |||||
| } | } | ||||
| public enum PassiveSkillType | public enum PassiveSkillType | ||||
| { | { | ||||
| @@ -16,6 +16,9 @@ namespace Preparation.Utility | |||||
| public const int MinSpeed = 1; // 最小速度 | public const int MinSpeed = 1; // 最小速度 | ||||
| public const int MaxSpeed = int.MaxValue; // 最大速度 | public const int MaxSpeed = int.MaxValue; // 最大速度 | ||||
| public const int numOfStudent = 4; | |||||
| public const int numOfPeople = 5; | |||||
| #endregion | #endregion | ||||
| #region 地图相关 | #region 地图相关 | ||||
| public const int numOfPosGridPerCell = 1000; // 每格的【坐标单位】数 | public const int numOfPosGridPerCell = 1000; // 每格的【坐标单位】数 | ||||
| @@ -24,7 +27,7 @@ namespace Preparation.Utility | |||||
| public const int cols = 50; // 列数 | public const int cols = 50; // 列数 | ||||
| public const int numOfBirthPoint = 5; | public const int numOfBirthPoint = 5; | ||||
| public const int numOfGenerator = 9; | |||||
| public const int numOfGenerator = 10; | |||||
| public const int numOfChest = 8; | public const int numOfChest = 8; | ||||
| public static bool IsMap(GameObjType gameObjType) | public static bool IsMap(GameObjType gameObjType) | ||||
| @@ -77,7 +80,6 @@ namespace Preparation.Utility | |||||
| } | } | ||||
| #endregion | #endregion | ||||
| #region 角色相关 | #region 角色相关 | ||||
| public const int numOfStudent = 4; | |||||
| public const int characterRadius = numOfPosGridPerCell * 4 / 10; // 人物半径 | public const int characterRadius = numOfPosGridPerCell * 4 / 10; // 人物半径 | ||||
| public const int basicTreatSpeed = 100; | public const int basicTreatSpeed = 100; | ||||
| @@ -12,6 +12,6 @@ start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --ch | |||||
| ping -n 2 127.0.0.1 > NUL | ping -n 2 127.0.0.1 > NUL | ||||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 1 --type 1 --occupation 3 | |||||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 1 --type 1 --occupation 5 | |||||
| ping -n 2 127.0.0.1 > NUL | ping -n 2 127.0.0.1 > NUL | ||||
| @@ -208,7 +208,7 @@ | |||||
| - 在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令 | - 在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令 | ||||
| ### 破译与逃脱 | ### 破译与逃脱 | ||||
| - 每张地图都会刷新 9台电机,求生者需要破译其中的7台,并开启任意一个大门后从任意一个开启的大门- 逃脱,亦或者在只剩1名求生者的情况下从紧急出口逃脱; | |||||
| - 每张地图都有10台电机,求生者需要破译其中的7台,并开启任意一个大门后从任意一个开启的大门- 逃脱,亦或者在只剩1名求生者的情况下从紧急出口逃脱; | |||||
| - 求生者和监管者在靠近电机时,可以看到电机的破译进度条。 | - 求生者和监管者在靠近电机时,可以看到电机的破译进度条。 | ||||
| - 紧急出口会在电机破译完成3台的情况下在地图的3-5个固定紧急出口刷新点之一随机刷新。 | - 紧急出口会在电机破译完成3台的情况下在地图的3-5个固定紧急出口刷新点之一随机刷新。 | ||||
| - 当求生者只剩1名时,紧急出口盖将会自动打开,该求生者可从紧急出口逃脱。 | - 当求生者只剩1名时,紧急出口盖将会自动打开,该求生者可从紧急出口逃脱。 | ||||