| @@ -316,6 +316,7 @@ $$ | |||
| ### 道具 | |||
| - 使用钥匙相当于销毁 | |||
| - 可接受指令状态下能捡起或扔道具,在场上即可使用道具 | |||
| ### 交互 | |||
| - 被唤醒或被勉励不属于交互状态,翻窗属于交互状态 | |||
| @@ -341,12 +341,14 @@ namespace GameClass.GameObj | |||
| set | |||
| { | |||
| lock (vampireLock) | |||
| { | |||
| if (value > 1) | |||
| vampire = 1; | |||
| else if (value < 0) | |||
| vampire = 0; | |||
| else | |||
| vampire = value; | |||
| } | |||
| } | |||
| } | |||
| public double OriVampire { get; protected set; } | |||
| @@ -394,7 +396,8 @@ namespace GameClass.GameObj | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| return (playerState == PlayerStateType.LockingOrOpeningTheDoor || playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest); | |||
| return (playerState == PlayerStateType.LockingTheDoor || playerState == PlayerStateType.OpeningTheDoor | |||
| || playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest); | |||
| } | |||
| } | |||
| public bool NullOrMoving() | |||
| @@ -465,41 +468,75 @@ namespace GameClass.GameObj | |||
| else return -1; | |||
| case PlayerStateType.TryingToAttack: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows | |||
| && value != PlayerStateType.LockingTheDoor && value != PlayerStateType.OpeningTheDoor) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Stunned: | |||
| case PlayerStateType.Charmed: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows && value != PlayerStateType.Swinging) | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows | |||
| && value != PlayerStateType.LockingTheDoor && value != PlayerStateType.OpeningTheDoor | |||
| && value != PlayerStateType.Swinging) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Swinging: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows | |||
| && value != PlayerStateType.LockingTheDoor && value != PlayerStateType.OpeningTheDoor) | |||
| { | |||
| ThreadNum.Release(); | |||
| return ChangePlayerState(value, gameObj); | |||
| try | |||
| { | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| finally | |||
| { | |||
| ThreadNum.Release(); | |||
| } | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.ClimbingThroughWindows: | |||
| if (value != PlayerStateType.Moving) | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.LockingTheDoor && value != PlayerStateType.OpeningTheDoor) | |||
| { | |||
| Window window = (Window)WhatInteractingWith!; | |||
| window.FinishClimbing(); | |||
| if (window.Stage.x == 0) | |||
| ThreadNum.Release(); | |||
| else ReSetPos(window.Stage); | |||
| return ChangePlayerState(value, gameObj); | |||
| try | |||
| { | |||
| window.FinishClimbing(); | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| finally | |||
| { | |||
| if (window.Stage.x == 0) | |||
| ThreadNum.Release(); | |||
| else ReSetPos(window.Stage); | |||
| } | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.OpeningTheChest: | |||
| ((Chest)WhatInteractingWith!).StopOpen(); | |||
| ((Chest)whatInteractingWith!).StopOpen(); | |||
| return ChangePlayerState(value, gameObj); | |||
| case PlayerStateType.OpeningTheDoorway: | |||
| Doorway doorway = (Doorway)WhatInteractingWith!; | |||
| Doorway doorway = (Doorway)whatInteractingWith!; | |||
| doorway.StopOpenning(); | |||
| return ChangePlayerState(value, gameObj); | |||
| case PlayerStateType.OpeningTheDoor: | |||
| Door door = (Door)whatInteractingWith!; | |||
| try | |||
| { | |||
| door.StopOpen(); | |||
| ReleaseTool(door.DoorNum switch | |||
| { | |||
| 3 => PropType.Key3, | |||
| 5 => PropType.Key5, | |||
| _ => PropType.Key6, | |||
| } | |||
| ); | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| finally | |||
| { | |||
| ThreadNum.Release(); | |||
| } | |||
| default: | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| @@ -638,6 +675,36 @@ namespace GameClass.GameObj | |||
| return new NullProp(); | |||
| } | |||
| public bool UseTool(PropType propType) | |||
| { | |||
| lock (inventoryLock) | |||
| { | |||
| for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing) | |||
| { | |||
| if (PropInventory[indexing].GetPropType() == propType && PropInventory[indexing].IsUsable()) | |||
| { | |||
| return ((Tool)PropInventory[indexing]).IsUsed = true; | |||
| } | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| public void ReleaseTool(PropType propType) | |||
| { | |||
| lock (inventoryLock) | |||
| { | |||
| for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing) | |||
| { | |||
| if (PropInventory[indexing].GetPropType() == propType && ((Tool)PropInventory[indexing]).IsUsed) | |||
| { | |||
| ((Tool)PropInventory[indexing]).IsUsed = false; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 如果indexing==GameData.maxNumOfPropInPropInventory表明道具栏为满 | |||
| /// </summary> | |||
| @@ -1,5 +1,7 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -32,30 +34,123 @@ namespace GameClass.GameObj | |||
| public override bool IsRigid => !isOpen; | |||
| public override ShapeType Shape => ShapeType.Square; | |||
| private Character? whoLockOrOpen = null; | |||
| public Character? WhoLockOrOpen | |||
| { | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| return whoLockOrOpen; | |||
| } | |||
| } | |||
| private bool isOpen = true; | |||
| public bool IsOpen | |||
| { | |||
| get => isOpen; | |||
| set | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| isOpen = value; | |||
| return isOpen; | |||
| } | |||
| } | |||
| private int openOrLockDegree = 0; | |||
| public int OpenOrLockDegree | |||
| private int lockDegree = 0; | |||
| public int LockDegree | |||
| { | |||
| get => openOrLockDegree; | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| return lockDegree; | |||
| } | |||
| set | |||
| { | |||
| if (value > 0) | |||
| lock (gameObjLock) | |||
| openOrLockDegree = (value > GameData.degreeOfLockingOrOpeningTheDoor) ? GameData.degreeOfLockingOrOpeningTheDoor : value; | |||
| else | |||
| lock (gameObjLock) | |||
| openOrLockDegree = 0; | |||
| value = (value > GameData.degreeOfLockingOrOpeningTheDoor) ? GameData.degreeOfLockingOrOpeningTheDoor : value; | |||
| lock (gameObjLock) | |||
| lockDegree = value; | |||
| } | |||
| } | |||
| private long openStartTime = 0; | |||
| public long OpenStartTime | |||
| { | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| return openStartTime; | |||
| } | |||
| } | |||
| public bool TryOpen(Character character) | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (isOpen) return false; | |||
| if (whoLockOrOpen != null) return false; | |||
| openStartTime = Environment.TickCount64; | |||
| whoLockOrOpen = character; | |||
| return true; | |||
| } | |||
| } | |||
| public void StopOpen() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (whoLockOrOpen != null) | |||
| { | |||
| if (Environment.TickCount64 - openStartTime >= GameData.degreeOfLockingOrOpeningTheDoor) | |||
| isOpen = true; | |||
| whoLockOrOpen = null; | |||
| } | |||
| } | |||
| } | |||
| public void FinishOpen() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| isOpen = true; | |||
| whoLockOrOpen = null; | |||
| } | |||
| } | |||
| public bool TryLock(Character character) | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (!isOpen) return false; | |||
| if (whoLockOrOpen != null) return false; | |||
| lockDegree = 0; | |||
| whoLockOrOpen = character; | |||
| return true; | |||
| } | |||
| } | |||
| public void StopLock() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (lockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | |||
| isOpen = true; | |||
| whoLockOrOpen = null; | |||
| } | |||
| } | |||
| public void FinishLock() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| isOpen = false; | |||
| whoLockOrOpen = null; | |||
| } | |||
| } | |||
| public void ForceToOpen() | |||
| { | |||
| Character? character; | |||
| lock (gameObjLock) | |||
| { | |||
| character = whoLockOrOpen; | |||
| whoLockOrOpen = null; | |||
| isOpen = true; | |||
| } | |||
| if (character != null) character.SetPlayerState(); | |||
| } | |||
| } | |||
| } | |||
| @@ -389,67 +389,133 @@ namespace Gaming | |||
| return true; | |||
| } | |||
| public bool LockOrOpenDoor(Character player) | |||
| public bool LockDoor(Character player) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot) return false; | |||
| Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door); | |||
| if (doorToLock == null) return false; | |||
| bool flag = false; | |||
| foreach (Gadget prop in player.PropInventory) | |||
| PropType propType = doorToLock.DoorNum switch | |||
| { | |||
| 3 => PropType.Key3, | |||
| 5 => PropType.Key5, | |||
| _ => PropType.Key6, | |||
| }; | |||
| if (!player.UseTool(propType)) return false; | |||
| long stateNum = player.SetPlayerState(PlayerStateType.LockingTheDoor, doorToLock); | |||
| if (stateNum == -1) | |||
| { | |||
| switch (prop.GetPropType()) | |||
| player.ReleaseTool(propType); | |||
| return false; | |||
| } | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| case PropType.Key3: | |||
| if (doorToLock.DoorNum == 3) | |||
| flag = true; | |||
| break; | |||
| case PropType.Key5: | |||
| if (doorToLock.DoorNum == 5) | |||
| flag = true; | |||
| break; | |||
| case PropType.Key6: | |||
| if (doorToLock.DoorNum == 6) | |||
| flag = true; | |||
| break; | |||
| default: | |||
| break; | |||
| player.ThreadNum.WaitOne(); | |||
| if (stateNum != player.StateNum) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| else | |||
| { | |||
| if (!doorToLock.TryLock(player)) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.SetPlayerState(); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| else | |||
| { | |||
| Thread.Sleep(GameData.checkInterval); | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => stateNum == player.StateNum && gameMap.Timer.IsGaming && doorToLock.LockDegree < GameData.degreeOfLockingOrOpeningTheDoor, | |||
| loopToDo: () => | |||
| { | |||
| if ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) != null) | |||
| return false; | |||
| doorToLock.LockDegree += GameData.checkInterval * player.SpeedOfOpeningOrLocking; | |||
| return true; | |||
| }, | |||
| timeInterval: GameData.checkInterval, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| doorToLock.StopLock(); | |||
| if (stateNum == player.StateNum) player.SetPlayerState(); | |||
| player.ReleaseTool(propType); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| } | |||
| } | |||
| if (flag) break; | |||
| } | |||
| if (!flag) return false; | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| if (doorToLock.OpenOrLockDegree > 0 || gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character) != null) | |||
| return false; | |||
| if (!(player.Commandable()) || player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor) | |||
| return true; | |||
| } | |||
| public bool OpenDoor(Character player) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot) return false; | |||
| Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door); | |||
| if (doorToLock == null) return false; | |||
| PropType propType = doorToLock.DoorNum switch | |||
| { | |||
| 3 => PropType.Key3, | |||
| 5 => PropType.Key5, | |||
| _ => PropType.Key6, | |||
| }; | |||
| if (!player.UseTool(propType)) return false; | |||
| long stateNum = player.SetPlayerState(PlayerStateType.OpeningTheDoor, doorToLock); | |||
| if (stateNum == -1) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| return false; | |||
| } | |||
| player.SetPlayerState(PlayerStateType.LockingOrOpeningTheDoor); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => flag && threadNum == player.StateNum && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor, | |||
| loopToDo: () => | |||
| { | |||
| flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null); | |||
| doorToLock.OpenOrLockDegree += GameData.frameDuration * player.SpeedOfOpeningOrLocking; | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | |||
| { | |||
| doorToLock.IsOpen = (!doorToLock.IsOpen); | |||
| } | |||
| if (threadNum == player.StateNum) | |||
| player.SetPlayerState(); | |||
| doorToLock.OpenOrLockDegree = 0; | |||
| } | |||
| ( | |||
| () => | |||
| { | |||
| player.ThreadNum.WaitOne(); | |||
| if (stateNum != player.StateNum) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| else | |||
| { | |||
| if (!doorToLock.TryOpen(player)) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.SetPlayerState(); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| else | |||
| { | |||
| Thread.Sleep(GameData.degreeOfLockingOrOpeningTheDoor / player.SpeedOfOpeningOrLocking); | |||
| ) | |||
| lock (player.ActionLock) | |||
| { | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| player.SetPlayerState(); | |||
| doorToLock.StopOpen(); | |||
| player.ReleaseTool(propType); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| return true; | |||
| @@ -197,14 +197,25 @@ namespace Gaming | |||
| } | |||
| return false; | |||
| } | |||
| public bool LockOrOpenDoor(long playerID) | |||
| public bool LockDoor(long playerID) | |||
| { | |||
| if (!gameMap.Timer.IsGaming) | |||
| return false; | |||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||
| if (player != null) | |||
| { | |||
| return actionManager.LockOrOpenDoor(player); | |||
| return actionManager.LockDoor(player); | |||
| } | |||
| return false; | |||
| } | |||
| public bool OpenDoor(long playerID) | |||
| { | |||
| if (!gameMap.Timer.IsGaming) | |||
| return false; | |||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||
| if (player != null) | |||
| { | |||
| return actionManager.OpenDoor(player); | |||
| } | |||
| return false; | |||
| } | |||
| @@ -21,7 +21,7 @@ namespace Gaming | |||
| public void UseProp(Character player, PropType propType) | |||
| { | |||
| if (player.IsRemoved || player.CharacterType == CharacterType.Robot) | |||
| if (player.CharacterType == CharacterType.Robot || player.IsRemoved) | |||
| return; | |||
| Gadget prop = player.UseProp(propType); | |||
| switch (prop.GetPropType()) | |||
| @@ -216,7 +216,9 @@ namespace Gaming | |||
| { | |||
| if (character.IsGhost() && | |||
| (character.PlayerState == PlayerStateType.TryingToAttack || character.PlayerState == PlayerStateType.Swinging | |||
| || character.PlayerState == PlayerStateType.UsingSkill || character.PlayerState == PlayerStateType.LockingOrOpeningTheDoor || character.PlayerState == PlayerStateType.ClimbingThroughWindows) | |||
| || character.PlayerState == PlayerStateType.UsingSkill | |||
| || character.PlayerState == PlayerStateType.LockingTheDoor || character.PlayerState == PlayerStateType.OpeningTheDoor | |||
| || character.PlayerState == PlayerStateType.ClimbingThroughWindows) | |||
| && gameMap.CanSee(player, character)) | |||
| { | |||
| if (characterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)) > 0) | |||
| @@ -19,12 +19,13 @@ namespace Preparation.Utility | |||
| Rescued = 10, | |||
| Stunned = 11, | |||
| TryingToAttack = 12,//指前摇 | |||
| LockingOrOpeningTheDoor = 13, | |||
| LockingTheDoor = 13, | |||
| OpeningTheChest = 14, | |||
| ClimbingThroughWindows = 15, | |||
| UsingSkill = 16, | |||
| OpeningTheDoorway = 17, | |||
| Charmed = 18, | |||
| OpeningTheDoor = 19, | |||
| } | |||
| public enum GameObjType | |||
| { | |||
| @@ -107,7 +107,8 @@ namespace Preparation.Utility | |||
| return PlayerState.Graduated; | |||
| case Preparation.Utility.PlayerStateType.Fixing: | |||
| return PlayerState.Learning; | |||
| case Preparation.Utility.PlayerStateType.LockingOrOpeningTheDoor: | |||
| case Preparation.Utility.PlayerStateType.LockingTheDoor: | |||
| case PlayerStateType.OpeningTheDoor: | |||
| return PlayerState.Locking; | |||
| case Preparation.Utility.PlayerStateType.OpeningTheChest: | |||
| return PlayerState.OpeningAChest; | |||
| @@ -256,7 +256,7 @@ namespace Server | |||
| { | |||
| X = door.Position.x, | |||
| Y = door.Position.y, | |||
| Progress = door.OpenOrLockDegree, | |||
| Progress = door.LockDegree, | |||
| IsOpen = door.IsOpen | |||
| } | |||
| }; | |||
| @@ -471,7 +471,7 @@ namespace Server | |||
| return Task.FromResult(boolRes); | |||
| } | |||
| var gameID = communicationToGameID[request.PlayerId]; | |||
| boolRes.ActSuccess = game.LockOrOpenDoor(gameID); | |||
| boolRes.ActSuccess = game.OpenDoor(gameID); | |||
| return Task.FromResult(boolRes); | |||
| } | |||
| @@ -487,7 +487,7 @@ namespace Server | |||
| return Task.FromResult(boolRes); | |||
| } | |||
| var gameID = communicationToGameID[request.PlayerId]; | |||
| boolRes.ActSuccess = game.LockOrOpenDoor(gameID); | |||
| boolRes.ActSuccess = game.LockDoor(gameID); | |||
| return Task.FromResult(boolRes); | |||
| } | |||