Browse Source

Merge pull request #539 from eesast/dev

fix: 🚑 fix a bug about moving
tags/v0.1.0
shangfengh GitHub 2 years ago
parent
commit
f634fbc67a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 636 additions and 384 deletions
  1. +2
    -0
      README.md
  2. +6
    -1
      docs/版本更新说明.md
  3. +186
    -82
      logic/Client/MainWindow.xaml.cs
  4. +6
    -2
      logic/Client/PlaybackClient.cs
  5. +1
    -1
      logic/Client/Properties/launchSettings.json
  6. +9
    -0
      logic/Client/StatusBarOfHunter.xaml.cs
  7. +9
    -0
      logic/Client/StatusBarOfSurvivor.xaml.cs
  8. +6
    -4
      logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs
  9. +1
    -1
      logic/GameClass/GameObj/Bullet/Bullet.cs
  10. +1
    -1
      logic/GameClass/GameObj/Character/Character.Skill.cs
  11. +177
    -45
      logic/GameClass/GameObj/Character/Character.cs
  12. +1
    -2
      logic/GameClass/GameObj/Immovable.cs
  13. +3
    -2
      logic/GameClass/GameObj/Map/Chest.cs
  14. +50
    -15
      logic/GameClass/GameObj/Map/Doorway.cs
  15. +8
    -30
      logic/GameClass/GameObj/Map/Window.cs
  16. +26
    -63
      logic/GameClass/GameObj/Moveable.cs
  17. +29
    -9
      logic/GameClass/GameObj/Prop.cs
  18. +2
    -1
      logic/GameEngine/MoveEngine.cs
  19. +38
    -31
      logic/Gaming/ActionManager.cs
  20. +2
    -2
      logic/Gaming/AttackManager.cs
  21. +15
    -68
      logic/Gaming/CharacterManager.cs
  22. +3
    -3
      logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs
  23. +9
    -0
      logic/Preparation/Interface/IChest.cs
  24. +12
    -0
      logic/Preparation/Interface/IDoorway.cs
  25. +2
    -2
      logic/Preparation/Interface/IMoveable.cs
  26. +9
    -9
      logic/Preparation/Interface/IOccupation.cs
  27. +3
    -3
      logic/Preparation/Interface/ISkill.cs
  28. +12
    -0
      logic/Preparation/Interface/IWindow.cs
  29. +1
    -0
      logic/Preparation/Utility/EnumType.cs
  30. +2
    -2
      logic/Server/CopyInfo.cs
  31. +5
    -5
      logic/cmd/gameServer.cmd

+ 2
- 0
README.md View File

@@ -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 时的注意事项



+ 6
- 1
docs/版本更新说明.md View File

@@ -30,5 +30,10 @@
- **重复发出同一类型的交互指令和移动指令是无效的**
- feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`**

# 5月13日更新
- docs:更新了 游戏机制与平衡性调整更新草案.pdf
- feat:更新了下载器

# 最新更新
- docs:更新了 游戏机制与平衡性调整更新草案.pdf
- feat:Robot可用
- hotfix: 修复了移动相关的bug

+ 186
- 82
logic/Client/MainWindow.xaml.cs View File

@@ -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 } };
}
}
}

+ 6
- 2
logic/Client/PlaybackClient.cs View File

@@ -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;
}
}
}
}

+ 1
- 1
logic/Client/Properties/launchSettings.json View File

@@ -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"
}
}
}

+ 9
- 0
logic/Client/StatusBarOfHunter.xaml.cs View File

@@ -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;


+ 9
- 0
logic/Client/StatusBarOfSurvivor.xaml.cs View File

@@ -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;


+ 6
- 4
logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs View File

@@ -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;


+ 1
- 1
logic/GameClass/GameObj/Bullet/Bullet.cs View File

@@ -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;
}


+ 1
- 1
logic/GameClass/GameObj/Character/Character.Skill.cs View File

@@ -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;


+ 177
- 45
logic/GameClass/GameObj/Character/Character.cs View File

@@ -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
- 2
logic/GameClass/GameObj/Immovable.cs View File

@@ -1,5 +1,4 @@
using Preparation.Interface;
using Preparation.Utility;
using Preparation.Utility;

namespace GameClass.GameObj
{


+ 3
- 2
logic/GameClass/GameObj/Map/Chest.cs View File

@@ -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)


+ 50
- 15
logic/GameClass/GameObj/Map/Doorway.cs View File

@@ -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);
}
}

+ 8
- 30
logic/GameClass/GameObj/Map/Window.cs View File

@@ -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(); }
}
}
}

+ 26
- 63
logic/GameClass/GameObj/Moveable.cs View File

@@ -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>
/// 原初移动速度


+ 29
- 9
logic/GameClass/GameObj/Prop.cs View File

@@ -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:


+ 2
- 1
logic/GameEngine/MoveEngine.cs View File

@@ -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));
},


+ 38
- 31
logic/Gaming/ActionManager.cs View File

@@ -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;
}



+ 2
- 2
logic/Gaming/AttackManager.cs View File

@@ -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);
}


+ 15
- 68
logic/Gaming/CharacterManager.cs View File

@@ -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();
}
}
}
)


+ 3
- 3
logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs View File

@@ -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);


+ 9
- 0
logic/Preparation/Interface/IChest.cs View File

@@ -0,0 +1,9 @@
using Preparation.Utility;

namespace Preparation.Interface
{
public interface IChest : IGameObj
{
public void StopOpen();
}
}

+ 12
- 0
logic/Preparation/Interface/IDoorway.cs View File

@@ -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();
}
}

+ 2
- 2
logic/Preparation/Interface/IMoveable.cs View File

@@ -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) // 检查下一位置是否会和目标物碰撞


+ 9
- 9
logic/Preparation/Interface/IOccupation.cs View File

@@ -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;


+ 3
- 3
logic/Preparation/Interface/ISkill.cs View File

@@ -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;


+ 12
- 0
logic/Preparation/Interface/IWindow.cs View File

@@ -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();
}
}

+ 1
- 0
logic/Preparation/Utility/EnumType.cs View File

@@ -71,6 +71,7 @@ namespace Preparation.Utility
AddHpOrAp = 6,
ShieldOrSpear = 7,
RecoveryFromDizziness = 8,
CraftingBench = 9,
}
public enum CharacterType // 职业
{


+ 2
- 2
logic/Server/CopyInfo.cs View File

@@ -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,


+ 5
- 5
logic/cmd/gameServer.cmd View File

@@ -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

Loading…
Cancel
Save