| @@ -70,6 +70,8 @@ THUAI6 开发组成员与其他贡献者应当遵循以下流程: | |||
| 3. 在新的分支上进行修改与开发 | |||
| 4. 向 `eesast/THUAI6` 的 `dev` 分支提出 pull request | |||
| 5. 等待其他开发组成员 review 与 merge | |||
| 6. 待需要发布新版本时,从 `dev` 向 `main` 分支提出 pull request,等待 review 与 merge | |||
| 7. 若非必要,**严禁**直接修改 `main` 分支。若有极特殊情况需要直接修改 `main` 分支,则需要**立即**从 `main` 分支(反向)提出 pull request 到 `dev` 并 merge,以将更改和 Git 提交历史同步到 `dev` 分支,既保证 `dev` 为最新的内容,又防止 `dev` 与 `main` 分支在之后发生冲突 | |||
| ### 使用 Git 与 Github 时的注意事项 | |||
| @@ -30,5 +30,10 @@ | |||
| - **重复发出同一类型的交互指令和移动指令是无效的** | |||
| - feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`** | |||
| # 5月13日更新 | |||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | |||
| - feat:更新了下载器 | |||
| # 最新更新 | |||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | |||
| - feat:Robot可用 | |||
| - hotfix: 修复了移动相关的bug | |||
| @@ -58,6 +58,7 @@ namespace Client | |||
| listOfDoor = new List<MessageOfDoor>(); | |||
| listOfGate = new List<MessageOfGate>(); | |||
| listOfHiddenGate = new List<MessageOfHiddenGate>(); | |||
| countList = new List<int>(); | |||
| WindowStartupLocation = WindowStartupLocation.CenterScreen; | |||
| unit = Math.Sqrt(UpperLayerOfMap.ActualHeight * UpperLayerOfMap.ActualWidth) / 50; | |||
| unitFontsize = unit / 13; | |||
| @@ -112,7 +113,7 @@ namespace Client | |||
| return; | |||
| } | |||
| _ = Parser.Default.ParseArguments<ArgumentOptions>(args).WithParsed(o => | |||
| { options = o; }); | |||
| { options = o; }); | |||
| if (options != null && Convert.ToInt64(options.PlayerID) > 2023) | |||
| { | |||
| isSpectatorMode = true; | |||
| @@ -159,7 +160,7 @@ namespace Client | |||
| { | |||
| var pbClient = new PlaybackClient(fileName, pbSpeed); | |||
| int[,]? map; | |||
| if ((map = pbClient.ReadDataFromFile(listOfProp, listOfHuman, listOfButcher, listOfBullet, listOfBombedBullet, listOfAll, listOfChest, listOfClassroom, listOfDoor, listOfHiddenGate, listOfGate, drawPicLock)) != null) | |||
| if ((map = pbClient.ReadDataFromFile(listOfProp, listOfHuman, listOfButcher, listOfBullet, listOfBombedBullet, listOfAll, listOfChest, listOfClassroom, listOfDoor, listOfHiddenGate, listOfGate, drawPicLock, countList)) != null) | |||
| { | |||
| isClientStocked = false; | |||
| PorC.Content = "⏸"; | |||
| @@ -311,6 +312,15 @@ namespace Client | |||
| private void DrawMap() | |||
| { | |||
| classroomArray = new TextBlock[countList[0]]; | |||
| doorArray = new TextBlock[countList[1]]; | |||
| chestArray = new TextBlock[countList[2]]; | |||
| gateArray = new TextBlock[countList[3]]; | |||
| classroomPositionIndex = new int[countList[0]]; | |||
| doorPositionIndex = new int[countList[1]]; | |||
| chestPositionIndex = new int[countList[2]]; | |||
| gatePositionIndex = new int[countList[3]]; | |||
| int cntOfClassroom = 0, cntOfDoor = 0, cntOfGate = 0, cntOfChest = 0; | |||
| for (int i = 0; i < defaultMap.GetLength(0); i++) | |||
| { | |||
| for (int j = 0; j < defaultMap.GetLength(1); j++) | |||
| @@ -336,10 +346,38 @@ namespace Client | |||
| case 8: | |||
| mapPatches[i, j].Fill = Brushes.LightPink; | |||
| mapPatches[i, j].Stroke = Brushes.LightPink; | |||
| break;//machine | |||
| classroomPositionIndex[cntOfClassroom] = 50 * i + j; | |||
| classroomArray[cntOfClassroom] = new TextBlock() | |||
| { | |||
| FontSize = 8 * unitFontsize,// | |||
| Width = unitWidth,// | |||
| Height = unitHeight,// | |||
| Text = Convert.ToString(-1),// | |||
| TextAlignment = TextAlignment.Center, | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(j * unitWidth / 1000.0 - unitWidth / 2, i * unitHeight / 1000.0 - unitHeight / 2, 0, 0),// | |||
| Background = Brushes.Transparent, | |||
| }; | |||
| ++cntOfClassroom; | |||
| break;//classroom | |||
| case 9: | |||
| mapPatches[i, j].Fill = Brushes.LightSkyBlue; | |||
| mapPatches[i, j].Stroke = Brushes.LightSkyBlue; | |||
| gatePositionIndex[cntOfGate] = 50 * i + j; | |||
| gateArray[cntOfGate] = new TextBlock() | |||
| { | |||
| FontSize = 8 * unitFontsize, | |||
| Width = unitWidth, | |||
| Height = unitHeight, | |||
| Text = Convert.ToString(-1), | |||
| TextAlignment = TextAlignment.Center, | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(j * unitWidth / 1000.0 - unitWidth / 2, i * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| }; | |||
| ++cntOfGate; | |||
| break;//gate | |||
| case 10: | |||
| break;//emergency | |||
| @@ -352,10 +390,38 @@ namespace Client | |||
| case 14: | |||
| mapPatches[i, j].Fill = Brushes.Khaki; | |||
| mapPatches[i, j].Stroke = Brushes.Khaki; | |||
| doorPositionIndex[cntOfDoor] = 50 * i + j; | |||
| doorArray[cntOfDoor] = new TextBlock() | |||
| { | |||
| FontSize = 9 * unitFontsize, | |||
| Width = unitWidth, | |||
| Height = unitHeight, | |||
| Text = Convert.ToString(-1), | |||
| TextAlignment = TextAlignment.Center, | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(j * unitWidth / 1000.0 - unitWidth / 2, i * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| }; | |||
| ++cntOfDoor; | |||
| break;//door | |||
| case 15: | |||
| mapPatches[i, j].Fill = Brushes.Orange; | |||
| mapPatches[i, j].Stroke = Brushes.Orange; | |||
| chestPositionIndex[cntOfChest] = 50 * i + j; | |||
| chestArray[cntOfChest] = new TextBlock() | |||
| { | |||
| FontSize = 8 * unitFontsize, | |||
| Width = unitWidth, | |||
| Height = unitHeight, | |||
| Text = Convert.ToString(-1), | |||
| TextAlignment = TextAlignment.Center, | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(j * unitWidth / 1000.0 - unitWidth / 2, i * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| }; | |||
| ++cntOfChest; | |||
| break;//chest | |||
| default: | |||
| break; | |||
| @@ -419,6 +485,7 @@ namespace Client | |||
| switch (content.GameState) | |||
| { | |||
| case GameState.GameStart: | |||
| MessageOfMap mapMessage = new MessageOfMap(); | |||
| foreach (var obj in content.ObjMessage) | |||
| { | |||
| switch (obj.MessageOfObjCase) | |||
| @@ -459,11 +526,16 @@ namespace Client | |||
| listOfGate.Add(obj.GateMessage); | |||
| break; | |||
| case MessageOfObj.MessageOfObjOneofCase.MapMessage: | |||
| GetMap(obj.MapMessage); | |||
| mapMessage = obj.MapMessage; | |||
| break; | |||
| } | |||
| } | |||
| countList.Add(listOfClassroom.Count); | |||
| countList.Add(listOfDoor.Count); | |||
| countList.Add(listOfChest.Count); | |||
| countList.Add(listOfGate.Count); | |||
| listOfAll.Add(content.AllMessage); | |||
| GetMap(mapMessage); | |||
| break; | |||
| case GameState.GameRunning: | |||
| foreach (var obj in content.ObjMessage) | |||
| @@ -522,9 +594,17 @@ namespace Client | |||
| switch (obj.MessageOfObjCase) | |||
| { | |||
| case MessageOfObj.MessageOfObjOneofCase.StudentMessage: | |||
| if (humanOrButcher && obj.StudentMessage.PlayerId == playerID) | |||
| { | |||
| human = obj.StudentMessage; | |||
| } | |||
| listOfHuman.Add(obj.StudentMessage); | |||
| break; | |||
| case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | |||
| if (!humanOrButcher && obj.TrickerMessage.PlayerId == playerID) | |||
| { | |||
| butcher = obj.TrickerMessage; | |||
| } | |||
| listOfButcher.Add(obj.TrickerMessage); | |||
| break; | |||
| case MessageOfObj.MessageOfObjOneofCase.PropMessage: | |||
| @@ -572,8 +652,8 @@ namespace Client | |||
| private bool CanSee(MessageOfStudent msg) | |||
| { | |||
| //if (msg.PlayerState == PlayerState.Quit || msg.PlayerState == PlayerState.Graduated) | |||
| // return false; | |||
| if (msg.PlayerState == PlayerState.Quit || msg.PlayerState == PlayerState.Graduated) | |||
| return false; | |||
| //if (isSpectatorMode || isPlaybackMode) | |||
| // return true; | |||
| //if (humanOrButcher && human != null) | |||
| @@ -677,6 +757,50 @@ namespace Client | |||
| return true; | |||
| } | |||
| private int FindIndexOfClassroom(MessageOfClassroom msg) | |||
| { | |||
| for (int i = 0; i < classroomPositionIndex.Length; ++i) | |||
| { | |||
| int k = msg.X / 1000 * 50 + msg.Y / 1000; | |||
| if (k == classroomPositionIndex[i]) | |||
| return i; | |||
| } | |||
| return -1; | |||
| } | |||
| private int FindIndexOfGate(MessageOfGate msg) | |||
| { | |||
| for (int i = 0; i < gatePositionIndex.Length; ++i) | |||
| { | |||
| int k = msg.X / 1000 * 50 + msg.Y / 1000; | |||
| if (k == gatePositionIndex[i]) | |||
| return i; | |||
| } | |||
| return -1; | |||
| } | |||
| private int FindIndexOfDoor(MessageOfDoor msg) | |||
| { | |||
| for (int i = 0; i < doorPositionIndex.Length; ++i) | |||
| { | |||
| int k = msg.X / 1000 * 50 + msg.Y / 1000; | |||
| if (k == doorPositionIndex[i]) | |||
| return i; | |||
| } | |||
| return -1; | |||
| } | |||
| private int FindIndexOfChest(MessageOfChest msg) | |||
| { | |||
| for (int i = 0; i < chestPositionIndex.Length; ++i) | |||
| { | |||
| int k = msg.X / 1000 * 50 + msg.Y / 1000; | |||
| if (k == chestPositionIndex[i]) | |||
| return i; | |||
| } | |||
| return -1; | |||
| } | |||
| private void Refresh(object? sender, EventArgs e) //log未更新 | |||
| { | |||
| lock (drawPicLock) // 加锁是必要的,画图操作和接收信息操作不能同时进行 | |||
| @@ -763,22 +887,21 @@ namespace Client | |||
| }; | |||
| if (data.StudentType == StudentType.Robot) | |||
| icon.Fill = Brushes.Gray; | |||
| TextBox num = new() | |||
| TextBlock num = new() | |||
| { | |||
| FontSize = 7 * unitFontsize, | |||
| Width = 2 * radiusTimes * unitWidth, | |||
| Height = 2 * radiusTimes * unitHeight, | |||
| Text = Convert.ToString(data.PlayerId), | |||
| TextAlignment = TextAlignment.Center, | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * radiusTimes, data.X * unitHeight / 1000.0 - unitHeight * radiusTimes, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| BorderBrush = Brushes.Transparent, | |||
| IsReadOnly = true, | |||
| Foreground = Brushes.White, | |||
| }; | |||
| if (data.StudentType == StudentType.Robot) | |||
| num.Text = Convert.ToString(data.PlayerId - Preparation.Utility.GameData.numOfPeople); | |||
| //if (data.StudentType == StudentType.Robot) | |||
| // num.Text = Convert.ToString(data.PlayerId - Preparation.Utility.GameData.numOfPeople); | |||
| UpperLayerOfMap.Children.Add(icon); | |||
| UpperLayerOfMap.Children.Add(num); | |||
| } | |||
| @@ -830,6 +953,9 @@ namespace Client | |||
| case Protobuf.PropType.RecoveryFromDizziness: | |||
| DrawProp(data, "🕶"); | |||
| break; | |||
| case Protobuf.PropType.CraftingBench: | |||
| DrawProp(data, "🎰"); | |||
| break; | |||
| default: | |||
| DrawProp(data, ""); | |||
| break; | |||
| @@ -857,6 +983,7 @@ namespace Client | |||
| case Protobuf.BulletType.CommonAttackOfTricker: | |||
| case Protobuf.BulletType.BombBomb: | |||
| case Protobuf.BulletType.JumpyDumpty: | |||
| case Protobuf.BulletType.Strike: | |||
| icon.Fill = Brushes.Red; | |||
| break; | |||
| default: | |||
| @@ -901,12 +1028,6 @@ namespace Client | |||
| UpperLayerOfMap.Children.Add(icon); | |||
| break; | |||
| } | |||
| //case Protobuf.BulletType.LineBullet: | |||
| // { | |||
| // double bombRange = data.BombRange / 1000; | |||
| // DrawLaser(new Point(data.Y * unitWidth / 1000.0, data.X * unitHeight / 1000.0), -data.FacingDirection + Math.PI / 2, bombRange * unitHeight, 0.5 * unitWidth); | |||
| // break; | |||
| // } | |||
| default: | |||
| break; | |||
| } | |||
| @@ -915,93 +1036,65 @@ namespace Client | |||
| foreach (var data in listOfClassroom) | |||
| { | |||
| int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfFixedGenerator); | |||
| TextBox icon = new() | |||
| { | |||
| FontSize = 8 * unitFontsize, | |||
| Width = unitWidth, | |||
| Height = unitHeight, | |||
| Text = Convert.ToString(deg), | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| BorderBrush = Brushes.Transparent, | |||
| IsReadOnly = true | |||
| }; | |||
| int idx = FindIndexOfClassroom(data); | |||
| classroomArray[idx].FontSize = 8 * unitFontsize; | |||
| classroomArray[idx].Width = unitWidth; | |||
| classroomArray[idx].Height = unitHeight; | |||
| classroomArray[idx].Text = Convert.ToString(deg); | |||
| classroomArray[idx].Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0); | |||
| if (deg == 100) | |||
| { | |||
| icon.Text = "A+"; | |||
| classroomArray[idx].Text = "A+"; | |||
| } | |||
| UpperLayerOfMap.Children.Add(icon); | |||
| UpperLayerOfMap.Children.Add(classroomArray[idx]); | |||
| } | |||
| foreach (var data in listOfChest) | |||
| { | |||
| int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfOpenedChest); | |||
| TextBox icon = new() | |||
| { | |||
| FontSize = 8 * unitFontsize, | |||
| Width = unitWidth, | |||
| Height = unitHeight, | |||
| Text = Convert.ToString(deg), | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| BorderBrush = Brushes.Transparent, | |||
| IsReadOnly = true | |||
| }; | |||
| int idx = FindIndexOfChest(data); | |||
| chestArray[idx].FontSize = 8 * unitFontsize; | |||
| chestArray[idx].Width = unitWidth; | |||
| chestArray[idx].Height = unitHeight; | |||
| chestArray[idx].Text = Convert.ToString(deg); | |||
| chestArray[idx].Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0); | |||
| if (deg == 100) | |||
| { | |||
| icon.Text = "Ø"; | |||
| chestArray[idx].Text = "Ø"; | |||
| } | |||
| UpperLayerOfMap.Children.Add(icon); | |||
| UpperLayerOfMap.Children.Add(chestArray[idx]); | |||
| } | |||
| foreach (var data in listOfGate) | |||
| { | |||
| int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfOpenedDoorway); | |||
| TextBox icon = new() | |||
| { | |||
| FontSize = 8 * unitFontsize, | |||
| Width = unitWidth, | |||
| Height = unitHeight, | |||
| Text = Convert.ToString(deg), | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| BorderBrush = Brushes.Transparent, | |||
| IsReadOnly = true | |||
| }; | |||
| int idx = FindIndexOfGate(data); | |||
| gateArray[idx].FontSize = 8 * unitFontsize; | |||
| gateArray[idx].Width = unitWidth; | |||
| gateArray[idx].Height = unitHeight; | |||
| gateArray[idx].Text = Convert.ToString(deg); | |||
| gateArray[idx].Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0); | |||
| if (deg == 100) | |||
| { | |||
| gateOpened = true; | |||
| icon.Text = "🔓"; | |||
| gateArray[idx].Text = "🔓"; | |||
| } | |||
| UpperLayerOfMap.Children.Add(icon); | |||
| UpperLayerOfMap.Children.Add(gateArray[idx]); | |||
| } | |||
| foreach (var data in listOfDoor) | |||
| { | |||
| TextBox icon = new() | |||
| { | |||
| FontSize = 9 * unitFontsize, | |||
| Width = unitWidth, | |||
| Height = unitHeight, | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| BorderBrush = Brushes.Transparent, | |||
| IsReadOnly = true | |||
| }; | |||
| int idx = FindIndexOfDoor(data); | |||
| doorArray[idx].FontSize = 9 * unitFontsize; | |||
| doorArray[idx].Width = unitWidth; | |||
| doorArray[idx].Height = unitHeight; | |||
| doorArray[idx].Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0); | |||
| if (data.IsOpen) | |||
| { | |||
| icon.Text = Convert.ToString("🔓"); | |||
| doorArray[idx].Text = Convert.ToString("🔓"); | |||
| } | |||
| else | |||
| { | |||
| icon.Text = Convert.ToString("🔒"); | |||
| doorArray[idx].Text = Convert.ToString("🔒"); | |||
| } | |||
| UpperLayerOfMap.Children.Add(icon); | |||
| UpperLayerOfMap.Children.Add(doorArray[idx]); | |||
| } | |||
| foreach (var data in listOfHiddenGate) | |||
| { | |||
| @@ -1014,20 +1107,19 @@ namespace Client | |||
| if (data.Opened) | |||
| { | |||
| isEmergencyOpened = true; | |||
| TextBox icon = new() | |||
| hiddenGateArray = new TextBlock() | |||
| { | |||
| FontSize = 9 * unitFontsize, | |||
| Width = unitWidth, | |||
| Height = unitHeight, | |||
| Text = Convert.ToString("🔓"), | |||
| TextAlignment = TextAlignment.Center, | |||
| HorizontalAlignment = HorizontalAlignment.Left, | |||
| VerticalAlignment = VerticalAlignment.Top, | |||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||
| Background = Brushes.Transparent, | |||
| BorderBrush = Brushes.Transparent, | |||
| IsReadOnly = true | |||
| }; | |||
| UpperLayerOfMap.Children.Add(icon); | |||
| UpperLayerOfMap.Children.Add(hiddenGateArray); | |||
| } | |||
| } | |||
| } | |||
| @@ -1416,6 +1508,18 @@ namespace Client | |||
| private List<MessageOfDoor> listOfDoor; | |||
| private List<MessageOfGate> listOfGate; | |||
| private List<MessageOfHiddenGate> listOfHiddenGate; | |||
| private TextBlock[] classroomArray; | |||
| private int[] classroomPositionIndex; | |||
| private TextBlock[] chestArray; | |||
| private int[] chestPositionIndex; | |||
| private TextBlock[] doorArray; | |||
| private int[] doorPositionIndex; | |||
| private TextBlock[] gateArray; | |||
| private int[] gatePositionIndex; | |||
| private TextBlock hiddenGateArray;//make a map from the position of icons to the index | |||
| private List<int> countList; | |||
| private object drawPicLock = new object(); | |||
| private MessageOfStudent? human = null; | |||
| private MessageOfTricker? butcher = null; | |||
| @@ -1489,4 +1593,4 @@ namespace Client | |||
| private int[] totalLife = new int[4] { 100, 100, 100, 100 }, totalDeath = new int[4] { 100, 100, 100, 100 }; | |||
| private int[,] coolTime = new int[3, 5] { { 100, 100, 100, 100, 100 }, { 100, 100, 100, 100, 100 }, { 100, 100, 100, 100, 100 } }; | |||
| } | |||
| } | |||
| } | |||
| @@ -39,7 +39,7 @@ namespace Client | |||
| public int[,]? ReadDataFromFile(List<MessageOfProp> listOfProp, List<MessageOfStudent> listOfHuman, List<MessageOfTricker> listOfButcher, List<MessageOfBullet> listOfBullet, | |||
| List<MessageOfBombedBullet> listOfBombedBullet, List<MessageOfAll> listOfAll, List<MessageOfChest> listOfChest, List<MessageOfClassroom> listOfClassroom, | |||
| List<MessageOfDoor> listOfDoor, List<MessageOfHiddenGate> listOfHiddenGate, List<MessageOfGate> listOfGate, object dataLock) | |||
| List<MessageOfDoor> listOfDoor, List<MessageOfHiddenGate> listOfHiddenGate, List<MessageOfGate> listOfGate, object? dataLock, List<int> countList) | |||
| { | |||
| if (Reader == null) | |||
| return null; | |||
| @@ -158,6 +158,10 @@ namespace Client | |||
| break; | |||
| } | |||
| } | |||
| countList.Add(listOfClassroom.Count); | |||
| countList.Add(listOfDoor.Count); | |||
| countList.Add(listOfChest.Count); | |||
| countList.Add(listOfGate.Count); | |||
| listOfAll.Add(content.AllMessage); | |||
| break; | |||
| case GameState.GameRunning: | |||
| @@ -270,4 +274,4 @@ namespace Client | |||
| return map; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| "profiles": { | |||
| "Client": { | |||
| "commandName": "Project", | |||
| "commandLineArgs": "--port 8892 --characterID 8880 --type 1 --occupation 1 --ip thuai6.eesast.com --cl" | |||
| "commandLineArgs": "--port 8888 --characterID 1 --type 1 --occupation 3" | |||
| } | |||
| } | |||
| } | |||
| @@ -169,6 +169,9 @@ namespace Client | |||
| case Protobuf.PropType.RecoveryFromDizziness: | |||
| prop0.Text = "🕶"; | |||
| break; | |||
| case Protobuf.PropType.CraftingBench: | |||
| prop0.Text = "🎰"; | |||
| break; | |||
| default: | |||
| prop0.Text = ""; | |||
| break; | |||
| @@ -202,6 +205,9 @@ namespace Client | |||
| case Protobuf.PropType.RecoveryFromDizziness: | |||
| prop1.Text = "🕶"; | |||
| break; | |||
| case Protobuf.PropType.CraftingBench: | |||
| prop1.Text = "🎰"; | |||
| break; | |||
| default: | |||
| prop1.Text = ""; | |||
| break; | |||
| @@ -235,6 +241,9 @@ namespace Client | |||
| case Protobuf.PropType.RecoveryFromDizziness: | |||
| prop2.Text = "🕶"; | |||
| break; | |||
| case Protobuf.PropType.CraftingBench: | |||
| prop2.Text = "🎰"; | |||
| break; | |||
| default: | |||
| prop2.Text = ""; | |||
| break; | |||
| @@ -174,6 +174,9 @@ namespace Client | |||
| case Protobuf.PropType.RecoveryFromDizziness: | |||
| prop0.Text = "🕶"; | |||
| break; | |||
| case Protobuf.PropType.CraftingBench: | |||
| prop0.Text = "🎰"; | |||
| break; | |||
| default: | |||
| prop0.Text = ""; | |||
| break; | |||
| @@ -207,6 +210,9 @@ namespace Client | |||
| case Protobuf.PropType.RecoveryFromDizziness: | |||
| prop1.Text = "🕶"; | |||
| break; | |||
| case Protobuf.PropType.CraftingBench: | |||
| prop1.Text = "🎰"; | |||
| break; | |||
| default: | |||
| prop1.Text = ""; | |||
| break; | |||
| @@ -240,6 +246,9 @@ namespace Client | |||
| case Protobuf.PropType.RecoveryFromDizziness: | |||
| prop2.Text = "🕶"; | |||
| break; | |||
| case Protobuf.PropType.CraftingBench: | |||
| prop2.Text = "🎰"; | |||
| break; | |||
| default: | |||
| prop2.Text = ""; | |||
| break; | |||
| @@ -41,7 +41,6 @@ namespace GameClass.GameObj | |||
| switch (gameObjType) | |||
| { | |||
| case GameObjType.Character: | |||
| case GameObjType.Generator: | |||
| return true; | |||
| default: | |||
| return false; | |||
| @@ -71,10 +70,11 @@ namespace GameClass.GameObj | |||
| public override int Speed => GameData.basicBulletMoveSpeed * 25 / 10; | |||
| public override bool IsRemoteAttack => true; | |||
| public override int CastTime => GameData.basicCastTime * 4 / 5; | |||
| public const int castTime = GameData.basicCastTime * 6 / 5; | |||
| public override int CastTime => castTime; | |||
| public override int Backswing => 0; | |||
| public override int RecoveryFromHit => 0; | |||
| public const int cd = GameData.basicBackswing / 2; | |||
| public const int cd = castTime; | |||
| public override int CD => cd; | |||
| public const int maxBulletNum = 1; | |||
| public override int MaxBulletNum => maxBulletNum; | |||
| @@ -119,7 +119,7 @@ namespace GameClass.GameObj | |||
| public override bool IsRemoteAttack => false; | |||
| public override int CastTime => (int)(AttackDistance * 1000 / Speed); | |||
| public override int Backswing => GameData.basicRecoveryFromHit; | |||
| public override int Backswing => GameData.basicBackswing * 3 / 2; | |||
| public override int RecoveryFromHit => GameData.basicRecoveryFromHit; | |||
| public const int cd = GameData.basicCD; | |||
| public override int CD => cd; | |||
| @@ -135,6 +135,8 @@ namespace GameClass.GameObj | |||
| switch (gameObjType) | |||
| { | |||
| case GameObjType.Character: | |||
| case GameObjType.Generator: | |||
| case GameObjType.Door: | |||
| return true; | |||
| default: | |||
| return false; | |||
| @@ -43,7 +43,7 @@ namespace GameClass.GameObj | |||
| base(Position, radius, GameObjType.Bullet) | |||
| { | |||
| this.canMove = true; | |||
| this.moveSpeed = this.Speed; | |||
| this.MoveSpeed = this.Speed; | |||
| this.hasSpear = player.TryUseSpear(); | |||
| this.Parent = player; | |||
| } | |||
| @@ -60,7 +60,7 @@ namespace GameClass.GameObj | |||
| this.buffManager = new BuffManager(); | |||
| this.occupation = OccupationFactory.FindIOccupation(characterType); | |||
| this.MaxHp = this.hp = Occupation.MaxHp; | |||
| this.OrgMoveSpeed = this.moveSpeed = Occupation.MoveSpeed; | |||
| this.MoveSpeed = this.OrgMoveSpeed = Occupation.MoveSpeed; | |||
| this.BulletOfPlayer = this.OriBulletOfPlayer = Occupation.InitBullet; | |||
| this.concealment = Occupation.Concealment; | |||
| this.alertnessRadius = Occupation.AlertnessRadius; | |||
| @@ -2,11 +2,16 @@ | |||
| using Preparation.Utility; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 | |||
| { | |||
| private readonly ReaderWriterLockSlim hpReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim HPReadWriterLock => hpReaderWriterLock; | |||
| #region 装弹、攻击相关的基本属性及方法 | |||
| /// <summary> | |||
| /// 装弹冷却 | |||
| @@ -231,21 +236,65 @@ namespace GameClass.GameObj | |||
| } | |||
| #endregion | |||
| #region 血量相关的基本属性及方法 | |||
| public int MaxHp { get; protected set; } // 最大血量 | |||
| private int maxHp; | |||
| public int MaxHp | |||
| { | |||
| get | |||
| { | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return maxHp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| protected set | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| maxHp = value; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| } // 最大血量 | |||
| protected int hp; | |||
| public int HP | |||
| { | |||
| get => hp; | |||
| get | |||
| { | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return hp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| set | |||
| { | |||
| if (value > 0) | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| if (value > 0) | |||
| { | |||
| hp = value <= MaxHp ? value : MaxHp; | |||
| } | |||
| else | |||
| lock (gameObjLock) | |||
| } | |||
| else | |||
| hp = 0; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| } | |||
| @@ -256,47 +305,62 @@ namespace GameClass.GameObj | |||
| /// <returns>减操作是否成功</returns> | |||
| public int TrySubHp(int sub) | |||
| { | |||
| int previousHp = hp; | |||
| lock (gameObjLock) | |||
| hp = hp <= sub ? 0 : hp - sub; | |||
| Debugger.Output(this, " hp has subed to: " + hp.ToString()); | |||
| return previousHp - hp; | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| int previousHp = hp; | |||
| if (hp <= sub) | |||
| { | |||
| hp = 0; | |||
| return hp; | |||
| } | |||
| else | |||
| { | |||
| hp -= sub; | |||
| return sub; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| private double vampire = 0; // 回血率:0-1之间 | |||
| public double Vampire | |||
| { | |||
| get => vampire; | |||
| set | |||
| get | |||
| { | |||
| if (value > 1) | |||
| lock (gameObjLock) | |||
| vampire = 1; | |||
| else if (value < 0) | |||
| lock (gameObjLock) | |||
| vampire = 0; | |||
| else | |||
| lock (gameObjLock) | |||
| vampire = value; | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return vampire; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| } | |||
| private double oriVampire = 0; | |||
| public double OriVampire | |||
| { | |||
| get => oriVampire; | |||
| set | |||
| { | |||
| if (value > 1) | |||
| lock (gameObjLock) | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| if (value > 1) | |||
| vampire = 1; | |||
| else if (value < 0) | |||
| lock (gameObjLock) | |||
| else if (value < 0) | |||
| vampire = 0; | |||
| else | |||
| lock (gameObjLock) | |||
| else | |||
| vampire = value; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| } | |||
| private double oriVampire = 0; | |||
| public double OriVampire { get; protected set; } | |||
| #endregion | |||
| #region 状态相关的基本属性与方法 | |||
| private PlayerStateType playerState = PlayerStateType.Null; | |||
| @@ -363,7 +427,7 @@ namespace GameClass.GameObj | |||
| private GameObj? whatInteractingWith = null; | |||
| public GameObj? WhatInteractingWith => whatInteractingWith; | |||
| public long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| private long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| //只能被SetPlayerState引用 | |||
| whatInteractingWith = gameObj; | |||
| @@ -374,7 +438,7 @@ namespace GameClass.GameObj | |||
| return ++stateNum; | |||
| } | |||
| public long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| private long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| //只能被SetPlayerState引用 | |||
| whatInteractingWith = gameObj; | |||
| @@ -385,6 +449,74 @@ namespace GameClass.GameObj | |||
| return stateNum; | |||
| } | |||
| public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| PlayerStateType nowPlayerState = PlayerState; | |||
| if (nowPlayerState == value) return -1; | |||
| switch (nowPlayerState) | |||
| { | |||
| case PlayerStateType.Escaped: | |||
| case PlayerStateType.Deceased: | |||
| return -1; | |||
| case PlayerStateType.Addicted: | |||
| if (value == PlayerStateType.Rescued) | |||
| return ChangePlayerStateInOneThread(value, gameObj); | |||
| else if (value == PlayerStateType.Null) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Rescued: | |||
| if (value == PlayerStateType.Addicted) | |||
| return ChangePlayerStateInOneThread(value, gameObj); | |||
| else if (value == PlayerStateType.Null) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.TryingToAttack: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Stunned: | |||
| case PlayerStateType.Charmed: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows && value != PlayerStateType.Swinging) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Swinging: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||
| { | |||
| ThreadNum.Release(); | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.ClimbingThroughWindows: | |||
| if (value != PlayerStateType.Moving) | |||
| { | |||
| Window window = (Window)WhatInteractingWith!; | |||
| window.FinishClimbing(); | |||
| if (window.Stage.x == 0) | |||
| ThreadNum.Release(); | |||
| else ReSetPos(window.Stage); | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.OpeningTheChest: | |||
| ((Chest)WhatInteractingWith!).StopOpen(); | |||
| return ChangePlayerState(value, gameObj); | |||
| case PlayerStateType.OpeningTheDoorway: | |||
| Doorway doorway = (Doorway)WhatInteractingWith!; | |||
| doorway.StopOpenning(); | |||
| return ChangePlayerState(value, gameObj); | |||
| default: | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| } | |||
| } | |||
| public long SetPlayerStateNaturally() | |||
| { | |||
| lock (actionLock) | |||
| @@ -398,20 +530,20 @@ namespace GameClass.GameObj | |||
| public void RemoveFromGame(PlayerStateType playerStateType) | |||
| { | |||
| MoveReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| lock (actionLock) | |||
| { | |||
| lock (actionLock) | |||
| MoveReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| playerState = playerStateType; | |||
| canMove = false; | |||
| isRemoved = true; | |||
| position = GameData.PosWhoDie; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| MoveReaderWriterLock.ExitWriteLock(); | |||
| finally | |||
| { | |||
| MoveReaderWriterLock.ExitWriteLock(); | |||
| } | |||
| playerState = playerStateType; | |||
| position = GameData.PosWhoDie; | |||
| } | |||
| } | |||
| #endregion | |||
| @@ -1,5 +1,4 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using Preparation.Utility; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -1,11 +1,12 @@ | |||
| using Preparation.Utility; | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 箱子 | |||
| /// </summary> | |||
| public class Chest : Immovable | |||
| public class Chest : Immovable, IChest | |||
| { | |||
| public Chest(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest) | |||
| @@ -1,12 +1,14 @@ | |||
| using Preparation.Interface; | |||
| using Google.Protobuf.WellKnownTypes; | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 出口 | |||
| /// </summary> | |||
| public class Doorway : Immovable | |||
| public class Doorway : Immovable, IDoorway | |||
| { | |||
| public Doorway(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Doorway) | |||
| @@ -25,7 +27,11 @@ namespace GameClass.GameObj | |||
| private bool powerSupply = false; | |||
| public bool PowerSupply | |||
| { | |||
| get => powerSupply; | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| return powerSupply; | |||
| } | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| @@ -36,29 +42,58 @@ namespace GameClass.GameObj | |||
| private int openStartTime = 0; | |||
| public int OpenStartTime | |||
| { | |||
| get => openStartTime; | |||
| set | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| openStartTime = value; | |||
| return openStartTime; | |||
| } | |||
| } | |||
| public bool TryToOpen() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (!powerSupply || openStartTime > 0) return false; | |||
| openStartTime = Environment.TickCount; | |||
| return true; | |||
| } | |||
| } | |||
| public bool StopOpenning() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (openDegree + Environment.TickCount - openStartTime >= GameData.degreeOfOpenedDoorway) | |||
| { | |||
| openDegree = GameData.degreeOfOpenedDoorway; | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| openDegree = openDegree + Environment.TickCount - openStartTime; | |||
| openStartTime = 0; | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| public void FinishOpenning() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| openDegree = GameData.degreeOfOpenedDoorway; | |||
| } | |||
| } | |||
| private int openDegree = 0; | |||
| public int OpenDegree | |||
| { | |||
| get => openDegree; | |||
| set | |||
| get | |||
| { | |||
| if (value > 0) | |||
| lock (gameObjLock) | |||
| openDegree = (value < GameData.degreeOfOpenedDoorway) ? value : GameData.degreeOfOpenedDoorway; | |||
| else | |||
| lock (gameObjLock) | |||
| openDegree = 0; | |||
| lock (gameObjLock) | |||
| return openDegree; | |||
| } | |||
| } | |||
| public bool IsOpen() => (openDegree == GameData.degreeOfOpenedDoorway); | |||
| public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedDoorway); | |||
| } | |||
| } | |||
| @@ -1,14 +1,12 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System.Numerics; | |||
| using System; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 窗 | |||
| /// </summary> | |||
| public class Window : Immovable | |||
| public class Window : Immovable, IWindow | |||
| { | |||
| public Window(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Window) | |||
| @@ -32,12 +30,8 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| GameObjReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| return stage; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitReadLock(); } | |||
| } | |||
| } | |||
| @@ -46,47 +40,31 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| GameObjReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| return whoIsClimbing; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitReadLock(); } | |||
| } | |||
| } | |||
| public bool TryToClimb(Character character) | |||
| public bool TryToClimb(ICharacter character) | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| if (whoIsClimbing == null) | |||
| { | |||
| stage = new(0, 0); | |||
| whoIsClimbing = character; | |||
| whoIsClimbing = (Character)character; | |||
| return true; | |||
| } | |||
| else return false; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| public void FinishClimbing() | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| whoIsClimbing = null; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| public void Enter2Stage(XY xy) | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| stage = xy; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| } | |||
| } | |||
| @@ -11,19 +11,9 @@ namespace GameClass.GameObj | |||
| //player.actionLock>其他.actionLock | |||
| private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; | |||
| //规定moveReaderWriterLock<actionLock | |||
| private Semaphore threadNum = new(1, 1); | |||
| public Semaphore ThreadNum | |||
| { | |||
| get | |||
| { | |||
| return threadNum; | |||
| } | |||
| set | |||
| { | |||
| threadNum = value; | |||
| } | |||
| } | |||
| public Semaphore ThreadNum { get; } = new(1, 1); | |||
| protected long stateNum = 0; | |||
| public long StateNum | |||
| @@ -38,7 +28,6 @@ namespace GameClass.GameObj | |||
| lock (actionLock) stateNum = value; | |||
| } | |||
| } | |||
| //规定moveReaderWriterLock>actionLock | |||
| public override XY Position | |||
| { | |||
| @@ -81,20 +70,20 @@ namespace GameClass.GameObj | |||
| if (moveVec.x != 0 || moveVec.y != 0) | |||
| { | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| lock (actionLock) | |||
| { | |||
| lock (actionLock) | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| if (!canMove || isRemoved) return -1; | |||
| if (stateNo != stateNum) return -1; | |||
| facingDirection = moveVec; | |||
| this.position += moveVec; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| if (stateNo != stateNum) return -1; | |||
| facingDirection = moveVec; | |||
| this.position += moveVec; | |||
| } | |||
| } | |||
| return moveVec * moveVec; | |||
| @@ -129,10 +118,7 @@ namespace GameClass.GameObj | |||
| moveReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| canMove = value; | |||
| } | |||
| canMove = value; | |||
| } | |||
| finally | |||
| { | |||
| @@ -161,52 +147,29 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| lock (actionLock) | |||
| { | |||
| lock (actionLock) | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return !isMoving && canMove && !isRemoved; | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| protected int moveSpeed; | |||
| protected long moveSpeed; | |||
| /// <summary> | |||
| /// 移动速度 | |||
| /// </summary> | |||
| public int MoveSpeed | |||
| public long MoveSpeed | |||
| { | |||
| get | |||
| { | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return moveSpeed; | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| set | |||
| { | |||
| moveReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| moveSpeed = value; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| get => Interlocked.Read(ref moveSpeed); | |||
| set => Interlocked.Exchange(ref moveSpeed, value); | |||
| } | |||
| /// <summary> | |||
| /// 原初移动速度 | |||
| @@ -23,7 +23,7 @@ namespace GameClass.GameObj | |||
| base(initPos, radius, GameObjType.Prop) | |||
| { | |||
| this.canMove = false; | |||
| this.moveSpeed = GameData.PropMoveSpeed; | |||
| this.MoveSpeed = GameData.PropMoveSpeed; | |||
| } | |||
| } | |||
| @@ -35,11 +35,28 @@ namespace GameClass.GameObj | |||
| //{ | |||
| // public DebuffMine(XYPosition initPos) : base(initPos) { } | |||
| // } | |||
| public sealed class CraftingBench : Prop | |||
| { | |||
| public override bool IsRigid => true; | |||
| public override bool IgnoreCollideExecutor(IGameObj targetObj) => false; | |||
| public CraftingBench(XY initPos) : | |||
| base(initPos) | |||
| { | |||
| } | |||
| public override PropType GetPropType() => PropType.CraftingBench; | |||
| } | |||
| public abstract class BuffProp : Prop | |||
| { | |||
| public BuffProp(XY initPos) : base(initPos) { } | |||
| } | |||
| #region 所有增益道具 | |||
| /// <summary> | |||
| /// 增加速度 | |||
| /// </summary> | |||
| public sealed class AddSpeed : Prop | |||
| public sealed class AddSpeed : BuffProp | |||
| { | |||
| public AddSpeed(XY initPos) : | |||
| base(initPos) | |||
| @@ -47,10 +64,11 @@ namespace GameClass.GameObj | |||
| } | |||
| public override PropType GetPropType() => PropType.AddSpeed; | |||
| } | |||
| /// <summary> | |||
| /// 复活甲 | |||
| /// </summary> | |||
| public sealed class AddLifeOrClairaudience : Prop | |||
| public sealed class AddLifeOrClairaudience : BuffProp | |||
| { | |||
| public AddLifeOrClairaudience(XY initPos) : | |||
| base(initPos) | |||
| @@ -58,7 +76,7 @@ namespace GameClass.GameObj | |||
| } | |||
| public override PropType GetPropType() => PropType.AddLifeOrClairaudience; | |||
| } | |||
| public sealed class AddHpOrAp : Prop | |||
| public sealed class AddHpOrAp : BuffProp | |||
| { | |||
| public AddHpOrAp(XY initPos) : | |||
| base(initPos) | |||
| @@ -66,7 +84,7 @@ namespace GameClass.GameObj | |||
| } | |||
| public override PropType GetPropType() => PropType.AddHpOrAp; | |||
| } | |||
| public sealed class RecoveryFromDizziness : Prop | |||
| public sealed class RecoveryFromDizziness : BuffProp | |||
| { | |||
| public RecoveryFromDizziness(XY initPos) : | |||
| base(initPos) | |||
| @@ -77,28 +95,28 @@ namespace GameClass.GameObj | |||
| /// <summary> | |||
| /// 矛盾 | |||
| /// </summary> | |||
| public sealed class ShieldOrSpear : Prop | |||
| public sealed class ShieldOrSpear : BuffProp | |||
| { | |||
| public ShieldOrSpear(XY initPos) : base(initPos) | |||
| { | |||
| } | |||
| public override PropType GetPropType() => PropType.ShieldOrSpear; | |||
| } | |||
| public sealed class Key3 : Prop | |||
| public sealed class Key3 : BuffProp | |||
| { | |||
| public Key3(XY initPos) : base(initPos) | |||
| { | |||
| } | |||
| public override PropType GetPropType() => PropType.Key3; | |||
| } | |||
| public sealed class Key5 : Prop | |||
| public sealed class Key5 : BuffProp | |||
| { | |||
| public Key5(XY initPos) : base(initPos) | |||
| { | |||
| } | |||
| public override PropType GetPropType() => PropType.Key5; | |||
| } | |||
| public sealed class Key6 : Prop | |||
| public sealed class Key6 : BuffProp | |||
| { | |||
| public Key6(XY initPos) : base(initPos) | |||
| { | |||
| @@ -145,6 +163,8 @@ namespace GameClass.GameObj | |||
| { | |||
| switch (propType) | |||
| { | |||
| case PropType.CraftingBench: | |||
| return new CraftingBench(pos); | |||
| case PropType.AddSpeed: | |||
| return new AddSpeed(pos); | |||
| case PropType.AddLifeOrClairaudience: | |||
| @@ -104,6 +104,7 @@ namespace GameEngine | |||
| if (!obj.IsAvailableForMove) { EndMove(obj); return; } | |||
| obj.IsMoving = true; | |||
| } | |||
| new Thread | |||
| ( | |||
| () => | |||
| @@ -151,7 +152,7 @@ namespace GameEngine | |||
| () => gameTimer.IsGaming, | |||
| () => | |||
| { | |||
| if (obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) | |||
| if (obj.StateNum != stateNum || !obj.CanMove || obj.IsRemoved) | |||
| return !(isEnded = true); | |||
| return !(isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum)); | |||
| }, | |||
| @@ -36,7 +36,7 @@ namespace Gaming | |||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (moveTimeInMilliseconds < 5) return false; | |||
| long stateNum = characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving); | |||
| long stateNum = playerToMove.SetPlayerState(PlayerStateType.Moving); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| ( | |||
| @@ -56,7 +56,7 @@ namespace Gaming | |||
| public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (playerToMove.CharacterType == CharacterType.Robot) return false; | |||
| long stateNum = characterManager.SetPlayerState(playerToMove, PlayerStateType.Charmed); | |||
| long stateNum = playerToMove.SetPlayerState(PlayerStateType.Charmed); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| (() => | |||
| @@ -86,7 +86,7 @@ namespace Gaming | |||
| { | |||
| if (player.Commandable()) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| return true; | |||
| } | |||
| } | |||
| @@ -103,7 +103,7 @@ namespace Gaming | |||
| return false; | |||
| ++generatorForFix.NumOfFixing; | |||
| characterManager.SetPlayerState(player, PlayerStateType.Fixing); | |||
| player.SetPlayerState(PlayerStateType.Fixing); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| ( | |||
| @@ -117,7 +117,7 @@ namespace Gaming | |||
| if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) | |||
| gameMap.NumOfRepairedGenerators++; | |||
| if (generatorForFix.DegreeOfRepair == GameData.degreeOfFixedGenerator) | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| @@ -134,24 +134,26 @@ namespace Gaming | |||
| public bool OpenDoorway(Student player) | |||
| { | |||
| if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorway) | |||
| if (!(player.Commandable())) | |||
| return false; | |||
| Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); | |||
| if (doorwayToOpen == null || doorwayToOpen.OpenStartTime > 0 || !doorwayToOpen.PowerSupply) | |||
| return false; | |||
| if (doorwayToOpen == null) return false; | |||
| characterManager.SetPlayerState(player, PlayerStateType.OpeningTheDoorway, doorwayToOpen); | |||
| int startTime = doorwayToOpen.OpenStartTime = gameMap.Timer.nowTime(); | |||
| long stateNum = player.SetPlayerState(PlayerStateType.OpeningTheDoorway, doorwayToOpen); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| //player.ThreadNum.WaitOne(); | |||
| Thread.Sleep(GameData.degreeOfOpenedDoorway - doorwayToOpen.OpenDegree); | |||
| if (doorwayToOpen.OpenStartTime == startTime) | |||
| lock (player.ActionLock) | |||
| { | |||
| doorwayToOpen.OpenDegree = GameData.degreeOfOpenedDoorway; | |||
| player.SetPlayerStateNaturally(); | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| player.SetPlayerStateNaturally(); | |||
| doorwayToOpen.FinishOpenning(); | |||
| } | |||
| } | |||
| } | |||
| @@ -189,6 +191,7 @@ namespace Gaming | |||
| public bool Treat(Student player, Student? playerTreated = null) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot) return false; | |||
| if (playerTreated == null) | |||
| { | |||
| playerTreated = gameMap.StudentForInteract(player.Position); | |||
| @@ -203,8 +206,8 @@ namespace Gaming | |||
| ( | |||
| () => | |||
| { | |||
| characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated); | |||
| characterManager.SetPlayerState(player, PlayerStateType.Treating); | |||
| playerTreated.SetPlayerState(PlayerStateType.Treated); | |||
| player.SetPlayerState(PlayerStateType.Treating); | |||
| long threadNum = player.StateNum; | |||
| new FrameRateTaskExecutor<int>( | |||
| @@ -212,15 +215,15 @@ namespace Gaming | |||
| loopToDo: () => | |||
| { | |||
| if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) | |||
| characterManager.SetPlayerState(playerTreated); | |||
| playerTreated.SetPlayerState(); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| if (threadNum == player.StateNum) characterManager.SetPlayerState(player); | |||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated); | |||
| if (threadNum == player.StateNum) player.SetPlayerState(); | |||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) playerTreated.SetPlayerState(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| @@ -228,15 +231,18 @@ namespace Gaming | |||
| } | |||
| public bool Rescue(Student player, Student? playerRescued = null) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot) return false; | |||
| if (playerRescued == null) | |||
| { | |||
| playerRescued = gameMap.StudentForInteract(player.Position); | |||
| if (playerRescued == null) return false; | |||
| } | |||
| if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position)) | |||
| return false; | |||
| characterManager.SetPlayerState(player, PlayerStateType.Rescuing); | |||
| characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued); | |||
| player.SetPlayerState(PlayerStateType.Rescuing); | |||
| playerRescued.SetPlayerState(PlayerStateType.Rescued); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| @@ -259,14 +265,14 @@ namespace Gaming | |||
| { | |||
| if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue) | |||
| { | |||
| characterManager.SetPlayerState(playerRescued); | |||
| playerRescued.SetPlayerState(); | |||
| playerRescued.HP = playerRescued.MaxHp / 2; | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| } | |||
| else | |||
| characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted); | |||
| playerRescued.SetPlayerState(PlayerStateType.Addicted); | |||
| } | |||
| if (threadNum == player.StateNum) characterManager.SetPlayerState(player); | |||
| if (threadNum == player.StateNum) player.SetPlayerState(); | |||
| playerRescued.TimeOfRescue = 0; | |||
| } | |||
| ) | |||
| @@ -283,7 +289,7 @@ namespace Gaming | |||
| if (chestToOpen == null || chestToOpen.OpenStartTime > 0) | |||
| return false; | |||
| characterManager.SetPlayerState(player, PlayerStateType.OpeningTheChest, chestToOpen); | |||
| player.SetPlayerState(PlayerStateType.OpeningTheChest, chestToOpen); | |||
| int startTime = gameMap.Timer.nowTime(); | |||
| chestToOpen.Open(startTime, player); | |||
| new Thread | |||
| @@ -315,7 +321,7 @@ namespace Gaming | |||
| Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window); | |||
| if (windowForClimb == null) return false; | |||
| long stateNum = characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows, windowForClimb); | |||
| long stateNum = player.SetPlayerState(PlayerStateType.ClimbingThroughWindows, windowForClimb); | |||
| if (stateNum == -1) return false; | |||
| XY windowToPlayer = new( | |||
| @@ -361,9 +367,9 @@ namespace Gaming | |||
| } | |||
| player.MoveSpeed = player.SpeedOfClimbingThroughWindows; | |||
| moveEngine.MoveObj(player, GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2, (-1 * windowToPlayer).Angle(), stateNum); | |||
| moveEngine.MoveObj(player, (int)(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2), (-1 * windowToPlayer).Angle(), stateNum); | |||
| Thread.Sleep(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2); | |||
| Thread.Sleep((int)(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2)); | |||
| player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); | |||
| @@ -371,7 +377,7 @@ namespace Gaming | |||
| { | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| windowForClimb.FinishClimbing(); | |||
| } | |||
| } | |||
| @@ -385,6 +391,7 @@ namespace Gaming | |||
| } | |||
| public bool LockOrOpenDoor(Character player) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot) return false; | |||
| if (!(player.Commandable()) || player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor) | |||
| return false; | |||
| Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door); | |||
| @@ -414,7 +421,7 @@ namespace Gaming | |||
| } | |||
| if (!flag) return false; | |||
| characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor); | |||
| player.SetPlayerState(PlayerStateType.LockingOrOpeningTheDoor); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| ( | |||
| @@ -436,7 +443,7 @@ namespace Gaming | |||
| doorToLock.IsOpen = (!doorToLock.IsOpen); | |||
| } | |||
| if (threadNum == player.StateNum) | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| doorToLock.OpenOrLockDegree = 0; | |||
| } | |||
| @@ -200,7 +200,7 @@ namespace Gaming | |||
| if (bullet.CastTime > 0) | |||
| { | |||
| characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); | |||
| player.SetPlayerState(PlayerStateType.TryingToAttack); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| @@ -221,7 +221,7 @@ namespace Gaming | |||
| { | |||
| if (threadNum == player.StateNum) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| } | |||
| else TryRemoveBullet(bullet); | |||
| } | |||
| @@ -17,65 +17,6 @@ namespace Gaming | |||
| this.gameMap = gameMap; | |||
| } | |||
| public long SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| lock (player.ActionLock) | |||
| { | |||
| PlayerStateType nowPlayerState = player.PlayerState; | |||
| if (nowPlayerState == value) return -1; | |||
| switch (nowPlayerState) | |||
| { | |||
| case PlayerStateType.Escaped: | |||
| case PlayerStateType.Deceased: | |||
| return -1; | |||
| case PlayerStateType.Addicted: | |||
| if (value == PlayerStateType.Rescued) | |||
| return player.ChangePlayerStateInOneThread(value, gameObj); | |||
| else if (value == PlayerStateType.Null) | |||
| return player.ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Rescued: | |||
| if (value == PlayerStateType.Addicted) | |||
| return player.ChangePlayerStateInOneThread(value, gameObj); | |||
| else if (value == PlayerStateType.Null) | |||
| return player.ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.TryingToAttack: | |||
| case PlayerStateType.Stunned: | |||
| case PlayerStateType.Charmed: | |||
| case PlayerStateType.Swinging: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||
| return player.ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.ClimbingThroughWindows: | |||
| if (value != PlayerStateType.Moving) | |||
| { | |||
| Window window = (Window)player.WhatInteractingWith!; | |||
| window.FinishClimbing(); | |||
| if (window.Stage.x == 0) | |||
| player.ThreadNum.Release(); | |||
| else player.ReSetPos(window.Stage); | |||
| return player.ChangePlayerState(value, gameObj); | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.OpeningTheChest: | |||
| ((Chest)player.WhatInteractingWith!).StopOpen(); | |||
| return player.ChangePlayerState(value, gameObj); | |||
| case PlayerStateType.OpeningTheDoorway: | |||
| Doorway doorway = (Doorway)player.WhatInteractingWith!; | |||
| doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime; | |||
| doorway.OpenStartTime = 0; | |||
| return player.ChangePlayerState(value, gameObj); | |||
| default: | |||
| return player.ChangePlayerState(value, gameObj); | |||
| } | |||
| } | |||
| } | |||
| public Character? AddPlayer(XY pos, int teamID, int playerID, CharacterType characterType, Character? parent = null) | |||
| { | |||
| Character newPlayer; | |||
| @@ -268,7 +209,7 @@ namespace Gaming | |||
| return; | |||
| } | |||
| } | |||
| SetPlayerState(player, PlayerStateType.Addicted); | |||
| player.SetPlayerState(PlayerStateType.Addicted); | |||
| long threadNum = player.StateNum; | |||
| new Thread | |||
| (() => | |||
| @@ -302,14 +243,14 @@ namespace Gaming | |||
| public long BeStunned(Character player, int time) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot) return -1; | |||
| long threadNum = SetPlayerState(player, PlayerStateType.Stunned); | |||
| long threadNum = player.SetPlayerState(PlayerStateType.Stunned); | |||
| if (threadNum == -1) return -1; | |||
| new Thread | |||
| (() => | |||
| { | |||
| Thread.Sleep(time); | |||
| if (threadNum == player.StateNum) | |||
| SetPlayerState(player); | |||
| player.SetPlayerState(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| @@ -339,7 +280,6 @@ namespace Gaming | |||
| #if DEBUG | |||
| Debugger.Output(student, "is being shot!"); | |||
| #endif | |||
| if (student.NoHp()) return; // 原来已经死了 | |||
| if (!bullet.Parent!.IsGhost()) return; | |||
| if (student.CharacterType == CharacterType.StraightAStudent) | |||
| @@ -347,6 +287,9 @@ namespace Gaming | |||
| ((WriteAnswers)student.FindIActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation = 0; | |||
| } | |||
| student.SetDegreeOfTreatment0(); | |||
| if (student.NoHp()) return; // 原来已经死了 | |||
| #if DEBUG | |||
| Debugger.Output(bullet, " 's AP is " + bullet.AP.ToString()); | |||
| #endif | |||
| @@ -394,18 +337,22 @@ namespace Gaming | |||
| public bool BackSwing(Character player, int time) | |||
| { | |||
| if (time <= 0) return false; | |||
| if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false; | |||
| SetPlayerState(player, PlayerStateType.Swinging); | |||
| long threadNum = player.StateNum; | |||
| long stateNum = player.SetPlayerState(PlayerStateType.Swinging); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| (() => | |||
| { | |||
| player.ThreadNum.WaitOne(); | |||
| Thread.Sleep(time); | |||
| if (threadNum == player.StateNum) | |||
| lock (player.ActionLock) | |||
| { | |||
| SetPlayerState(player); | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| player.ThreadNum.Release(); | |||
| player.SetPlayerStateNaturally(); | |||
| } | |||
| } | |||
| } | |||
| ) | |||
| @@ -111,8 +111,8 @@ namespace Gaming | |||
| Debugger.Output(player, "use robot!"); | |||
| IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.UseRobot); | |||
| activeSkill.IsBeingUsed = (activeSkill.IsBeingUsed) ? false : true; | |||
| if (activeSkill.IsBeingUsed) characterManager.SetPlayerState(player, PlayerStateType.UsingSkill); | |||
| else characterManager.SetPlayerState(player); | |||
| if (activeSkill.IsBeingUsed) player.SetPlayerState(PlayerStateType.UsingSkill); | |||
| else player.SetPlayerState(); | |||
| return true; | |||
| } | |||
| @@ -247,7 +247,7 @@ namespace Gaming | |||
| { | |||
| if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) | |||
| { | |||
| characterManager.SetPlayerState(character); | |||
| character.SetPlayerState(); | |||
| character.HP = GameData.RemainHpWhenAddLife; | |||
| ((Student)character).TimeOfRescue = 0; | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| @@ -0,0 +1,9 @@ | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface IChest : IGameObj | |||
| { | |||
| public void StopOpen(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface IDoorway : IGameObj | |||
| { | |||
| public int OpenStartTime { get; } | |||
| public int OpenDegree { get; } | |||
| public bool StopOpenning(); | |||
| public bool TryToOpen(); | |||
| } | |||
| } | |||
| @@ -7,12 +7,12 @@ namespace Preparation.Interface | |||
| public interface IMoveable : IGameObj | |||
| { | |||
| object ActionLock { get; } | |||
| public int MoveSpeed { get; } | |||
| public long MoveSpeed { get; } | |||
| public bool IsMoving { get; set; } | |||
| public bool IsRemoved { get; } | |||
| public bool IsAvailableForMove { get; } | |||
| public long StateNum { get; } | |||
| public Semaphore ThreadNum { get; set; } | |||
| public Semaphore ThreadNum { get; } | |||
| public long MovingSetPos(XY moveVec, long stateNum); | |||
| public void ReSetCanMove(bool value); | |||
| public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 | |||
| @@ -275,10 +275,10 @@ namespace Preparation.Interface | |||
| } | |||
| public class Robot : IStudentType | |||
| { | |||
| private const int moveSpeed = (int)(GameData.basicStudentMoveSpeed); | |||
| private const int moveSpeed = (int)(GameData.basicStudentMoveSpeed * 9 / 10); | |||
| public int MoveSpeed => moveSpeed; | |||
| private const int maxHp = (int)(GameData.basicHp / 2.5); | |||
| private const int maxHp = (int)(GameData.basicHp * 3 / 10); | |||
| public int MaxHp => maxHp; | |||
| private const int maxGamingAddiction = 0; | |||
| @@ -286,25 +286,25 @@ namespace Preparation.Interface | |||
| public BulletType InitBullet => BulletType.Null; | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(System.Array.Empty<ActiveSkillType>()); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(System.Array.Empty<PassiveSkillType>()); | |||
| public const int fixSpeed = GameData.basicFixSpeed; | |||
| public const int fixSpeed = GameData.basicFixSpeed * 85 / 123; | |||
| public int FixSpeed => fixSpeed; | |||
| public const int treatSpeed = 0; | |||
| public int TreatSpeed => treatSpeed; | |||
| public const double concealment = GameData.basicConcealment; | |||
| public const double concealment = GameData.basicConcealment * 0.8; | |||
| public double Concealment => concealment; | |||
| public const int alertnessRadius = (int)(GameData.basicStudentAlertnessRadius); | |||
| public const int alertnessRadius = 0; | |||
| public int AlertnessRadius => alertnessRadius; | |||
| public int viewRange = GameData.basicStudentViewRange; | |||
| public int viewRange = 0; | |||
| public int ViewRange => viewRange; | |||
| public int speedOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking; | |||
| public int speedOfOpeningOrLocking = 0; | |||
| public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; | |||
| public int speedOfClimbingThroughWindows = 1; | |||
| @@ -206,9 +206,9 @@ namespace Preparation.Interface | |||
| public class SummonGolem : IActiveSkill | |||
| { | |||
| public int SkillCD => GameData.commonSkillCD; | |||
| public int DurationTime => 0; | |||
| private readonly object commonSkillLock = new object(); | |||
| public int SkillCD => GameData.commonSkillCD * 4 / 3; | |||
| public int DurationTime => 6; | |||
| private readonly object commonSkillLock = new(); | |||
| public object ActiveSkillLock => commonSkillLock; | |||
| private IGolem? golemSummoned = null; | |||
| @@ -0,0 +1,12 @@ | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface IWindow : IGameObj | |||
| { | |||
| public void Enter2Stage(XY xy); | |||
| public bool TryToClimb(ICharacter character); | |||
| public XY Stage { get; } | |||
| public void FinishClimbing(); | |||
| } | |||
| } | |||
| @@ -71,6 +71,7 @@ namespace Preparation.Utility | |||
| AddHpOrAp = 6, | |||
| ShieldOrSpear = 7, | |||
| RecoveryFromDizziness = 8, | |||
| CraftingBench = 9, | |||
| } | |||
| public enum CharacterType // 职业 | |||
| { | |||
| @@ -58,7 +58,7 @@ namespace Server | |||
| { | |||
| X = player.Position.x, | |||
| Y = player.Position.y, | |||
| Speed = player.MoveSpeed, | |||
| Speed = (int)player.MoveSpeed, | |||
| Determination = player.HP, | |||
| Addiction = player.GamingAddiction, | |||
| Guid = player.ID, | |||
| @@ -106,7 +106,7 @@ namespace Server | |||
| { | |||
| X = player.Position.x, | |||
| Y = player.Position.y, | |||
| Speed = player.MoveSpeed, | |||
| Speed = (int)player.MoveSpeed, | |||
| TrickerType = Transformation.ToTrickerType(player.CharacterType), | |||
| Guid = player.ID, | |||
| @@ -1,17 +1,17 @@ | |||
| @echo off | |||
| start cmd /k ..\Server\bin\Debug\net6.0\Server.exe --ip 0.0.0.0 --port 8888 --studentCount 2 --trickerCount 1 --gameTimeInSecond 600 --fileName video --mapResource ".\map\map1_final.txt" | |||
| start cmd /k ..\Server\bin\Debug\net6.0\Server.exe --ip 0.0.0.0 --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video --mapResource ".\map\map2_final.txt" | |||
| ping -n 2 127.0.0.1 > NUL | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 4 --type 2 --occupation 1 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 0 --type 1 --occupation 1 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 0 --type 1 --occupation 0 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 1 --type 1 --occupation 2 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 1 --type 1 --occupation 0 | |||
| ::start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 2 --type 1 --occupation 5 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 2 --type 1 --occupation 0 | |||
| ::start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 3 --type 1 --occupation 5 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 3 --type 1 --occupation 0 | |||
| ::start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --port 8888 --characterID 2030 | |||