Browse Source

Merge branch 'dev' of github.com:DragonAura/THUAI6 into dev

tags/0.1.0
DragonAura 2 years ago
parent
commit
6a0dc336cd
28 changed files with 892 additions and 399 deletions
  1. +17
    -3
      logic/Client/MainWindow.xaml.cs
  2. +6
    -9
      logic/Client/StatusBarOfCircumstance.xaml
  3. +5
    -3
      logic/Client/StatusBarOfCircumstance.xaml.cs
  4. +25
    -12
      logic/Client/StatusBarOfHunter.xaml
  5. +165
    -26
      logic/Client/StatusBarOfHunter.xaml.cs
  6. +17
    -16
      logic/Client/StatusBarOfSurvivor.xaml
  7. +63
    -21
      logic/Client/StatusBarOfSurvivor.xaml.cs
  8. +33
    -13
      logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs
  9. +27
    -11
      logic/GameClass/GameObj/Bullet/Bullet.cs
  10. +6
    -9
      logic/GameClass/GameObj/Character/Character.Skill.cs
  11. +38
    -0
      logic/GameClass/GameObj/Character/Character.Student.cs
  12. +20
    -46
      logic/GameClass/GameObj/Character/Character.cs
  13. +9
    -13
      logic/GameClass/GameObj/Character/Skill.cs
  14. +10
    -5
      logic/GameClass/GameObj/Map/Chest.cs
  15. +7
    -7
      logic/GameClass/GameObj/Map/Door.cs
  16. +23
    -0
      logic/GameClass/GameObj/Map/EmergencyExit.cs
  17. +22
    -1
      logic/GameClass/GameObj/Map/Map.cs
  18. +66
    -59
      logic/Gaming/ActionManager.cs
  19. +45
    -38
      logic/Gaming/AttackManager.cs
  20. +58
    -10
      logic/Gaming/Game.cs
  21. +3
    -17
      logic/Gaming/PropManager.cs
  22. +2
    -0
      logic/Preparation/Interface/ICharacter.cs
  23. +47
    -14
      logic/Preparation/Interface/IOccupation.cs
  24. +0
    -1
      logic/Preparation/Interface/ISkill.cs
  25. +1
    -1
      logic/Preparation/Utility/EnumType.cs
  26. +44
    -7
      logic/Preparation/Utility/GameData.cs
  27. +38
    -11
      logic/Server/CopyInfo.cs
  28. +95
    -46
      logic/规则Logic.md

+ 17
- 3
logic/Client/MainWindow.xaml.cs View File

@@ -300,6 +300,20 @@ namespace Client
}
}
break;//emergency
case 11:
mapPatches[i, j].Fill = Brushes.Gray;
mapPatches[i, j].Stroke = Brushes.Gray;
break;//window
case 12:
case 13:
case 14:
mapPatches[i, j].Fill = Brushes.Khaki;
mapPatches[i, j].Stroke = Brushes.Khaki;
break;//door
case 15:
mapPatches[i, j].Fill = Brushes.Orange;
mapPatches[i, j].Stroke = Brushes.Orange;
break;//chest
default:
break;
}
@@ -767,7 +781,7 @@ namespace Client
UpperLayerOfMap.Children.Add(icon);
break;
}
/* case BulletType.LineBullet:
/*case 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);
@@ -848,11 +862,11 @@ namespace Client
};
if (data.IsOpen)
{
icon.Text = Convert.ToString("1");
icon.Text = Convert.ToString("");
}
else
{
icon.Text = Convert.ToString("0");
icon.Text = Convert.ToString("");
}
UpperLayerOfMap.Children.Add(icon);
}


+ 6
- 9
logic/Client/StatusBarOfCircumstance.xaml View File

@@ -9,20 +9,17 @@
<Grid Name="background" >
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="84*"/>
<RowDefinition Height="23*"/>
<RowDefinition Height="70*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="27*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="62*"/>
</Grid.ColumnDefinitions>
<TextBox Name="time" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="Time⏳:" Grid.Row="0" Grid.ColumnSpan="2" FontSize="12"/>
<TextBox Name="status" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="📱:&#xA;🚪:&#xA;🆘:&#xA;🏃:&#xA;⚰️:" Grid.Row="1" Grid.ColumnSpan="2"/>
<TextBox Name="scoresofstudents" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="Scores of Survivors:" Grid.Row="2" Grid.ColumnSpan="2"/>
<TextBox Name="scoresoftrickers" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="Scores of Hunters:" Grid.Row="3" Grid.ColumnSpan="2"/>
<TextBox Name="prop" IsReadOnly="True" BorderBrush="Gray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,136,0"></TextBox>
<ProgressBar Name="skillprogress" Background="White" Grid.Row="4" Grid.Column="1"/>
<TextBox Name="time" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="Time⏳:" Grid.Row="0" Grid.ColumnSpan="2" FontSize="14"/>
<TextBox Name="status" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="📱:&#xA;🚪:&#xA;🆘:&#xA;🏃:&#xA;⚰️:" Grid.Row="1" Grid.ColumnSpan="2" FontSize="13"/>
<TextBox Name="scoresOfStudents" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="Scores of Students:" Grid.Row="2" Grid.ColumnSpan="2" FontSize="12"/>
<TextBox Name="scoresOfTrickers" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="Scores of Tricker:" Grid.Row="3" Grid.ColumnSpan="2" FontSize="12"/>
</Grid>
</UserControl>

+ 5
- 3
logic/Client/StatusBarOfCircumstance.xaml.cs View File

@@ -31,7 +31,9 @@ namespace Client
}
public void SetFontSize(double fontsize)
{
time.FontSize = scoresofstudents.FontSize = scoresoftrickers.FontSize = status.FontSize = prop.FontSize = fontsize;
status.FontSize = 13 * fontsize / 12;
time.FontSize = 13 * fontsize / 12;
scoresOfStudents.FontSize = scoresOfTrickers.FontSize = fontsize;
}

public void SetValue(MessageOfAll obj)
@@ -63,8 +65,8 @@ namespace Client
}
status.Text += Convert.ToString(obj.StudentGraduated)+ "\n⚰️: ";
status.Text += Convert.ToString(obj.StudentQuited);
scoresofstudents.Text = "Scores of Survivors: " + Convert.ToString(obj.StudentScore);
scoresoftrickers.Text = "Scores of Hunters: " + Convert.ToString(obj.TrickerScore);
scoresOfStudents.Text = "Scores of Students:" + Convert.ToString(obj.StudentScore);
scoresOfTrickers.Text = "Scores of Tricker:" + Convert.ToString(obj.TrickerScore);
}
}
}

+ 25
- 12
logic/Client/StatusBarOfHunter.xaml View File

@@ -8,21 +8,34 @@
d:DesignHeight="174" d:DesignWidth="180">
<Grid Name="background" >
<Grid.RowDefinitions>
<RowDefinition Height="35*"/>
<RowDefinition Height="67*"/>
<RowDefinition Height="22*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="26*"/>
<RowDefinition Height="36*"/>
<RowDefinition Height="23*"/>
<RowDefinition Height="23*"/>
<RowDefinition Height="23*"/>
<RowDefinition Height="23*"/>
<RowDefinition Height="23*"/>
<RowDefinition Height="23*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="62*"/>
<ColumnDefinition Width="21*"/>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="21*"/>
</Grid.ColumnDefinitions>
<TextBox Name="serial" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="👥null👻null&#xD;&#xA;Skill:" Grid.Row="0" Grid.ColumnSpan="2" FontSize="12"/>
<TextBox Name="status" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="🏹:&#xA;🏃:&#xA;🤺:&#xA;🗡:" Grid.Row="1" Grid.ColumnSpan="2" />
<TextBox Name="scores" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="Scores:" Grid.Row="2" Grid.ColumnSpan="2"/>
<TextBox Name="star" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="" Grid.Row="3" Grid.ColumnSpan="2"/>
<TextBox Name="prop" IsReadOnly="True" BorderBrush="Gray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="4" Grid.Column="0" Grid.RowSpan="2"></TextBox>
<ProgressBar Name="skillprogress" Background="White" Grid.Row="4" Grid.Column="1"/>
<TextBox Name="serial" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="👥null👻null&#xD;&#xA;职业:" Grid.Row="0" Grid.ColumnSpan="4" FontSize="12"/>
<TextBox Name="status" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="🏃🏿‍:" Grid.Row="1" Grid.ColumnSpan="2" />
<TextBox Name="state" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" />
<TextBox Name="scores" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="Scores:" Grid.Row="2" Grid.ColumnSpan="4"/>
<TextBox Name="activeSkill0" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="" Grid.Row="3" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress0" Background="White" Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="activeSkill1" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="" Grid.Row="4" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress1" Background="White" Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="activeSkill2" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="" Grid.Row="5" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress2" Background="White" Grid.Row="5" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="prop0" IsReadOnly="True" BorderBrush="LightGray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="1"/>
<TextBox Name="prop1" IsReadOnly="True" BorderBrush="LightGray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="1"/>
<TextBox Name="prop2" IsReadOnly="True" BorderBrush="LightGray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="1"/>
<TextBox Name="prop3" IsReadOnly="True" BorderBrush="LightGray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="7" Grid.Column="3" Grid.ColumnSpan="1"/>
</Grid>
</UserControl>

+ 165
- 26
logic/Client/StatusBarOfHunter.xaml.cs View File

@@ -36,7 +36,7 @@ namespace Client
}
public void SetFontSize(double fontsize)
{
serial.FontSize = scores.FontSize = star.FontSize = status.FontSize = prop.FontSize = fontsize;
serial.FontSize = scores.FontSize = state.FontSize = status.FontSize=activeSkill0.FontSize = activeSkill1.FontSize = activeSkill2.FontSize = prop0.FontSize = prop1.FontSize = prop2.FontSize = prop3.FontSize = fontsize;
}

private void SetStaticValue(MessageOfTricker obj)
@@ -44,58 +44,197 @@ namespace Client
switch (obj.TrickerType) // 参数未设定
{
case TrickerType.Assassin:
coolTime = 10000;
serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\n职业:TrickerType1";
coolTime0 = coolTime1 = coolTime2 = 10000;
serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\n职业:Assassin";
break;
case TrickerType._2:
coolTime = 20000;
coolTime0 = coolTime1 = coolTime2 = 20000;
serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\n职业:TrickerType2";
break;
case TrickerType._3:
coolTime = 30000;
coolTime0 = coolTime1 = coolTime2 = 30000;
serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\n职业:TrickerType3";
break;
case TrickerType._4:
coolTime = 40000;
coolTime0 = coolTime1 = coolTime2 = 40000;
serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\n职业:TrickerType4";
break;
case TrickerType.NullTrickerType:
coolTime = 10000;
coolTime0 = coolTime1 = coolTime2 = -1;
serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\n职业:NullTrickerType";
break;
}
activeSkill0.Text = "Skill0";
activeSkill1.Text = "Skill1";
activeSkill2.Text = "Skill2";
initialized = true;
}
private void SetDynamicValue(MessageOfTricker obj)
{
if (obj.PlayerState==PlayerState.Stunned)
status.Text = "🏃🏿‍:"+Convert.ToString(obj.Speed);
switch (obj.PlayerState)
{
skillprogress.Value = 0;
skillprogress.Background = Brushes.Gray;
case PlayerState.Idle:
state.Text = "Idle";
break;
case PlayerState.Learning:
state.Text = "Learning";
break;
case PlayerState.Addicted:
state.Text = "Addicted";
break;
case PlayerState.Graduated:
state.Text = "Graduated";
break;
case PlayerState.Quit:
state.Text = "Quit";
break;
case PlayerState.Treated:
state.Text = "Treated";
break;
case PlayerState.Rescued:
state.Text = "Rescued";
break;
case PlayerState.Stunned:
state.Text = "Stunned";
break;
case PlayerState.Treating:
state.Text = "Treating";
break;
case PlayerState.Rescuing:
state.Text = "Rescuing";
break;
case PlayerState.Swinging:
state.Text = "Swinging";
break;
case PlayerState.Attacking:
state.Text = "Attacking";
break;
case PlayerState.Locking:
state.Text = "Locking";
break;
case PlayerState.Rummaging:
state.Text ="Rummaging";
break;
case PlayerState.Climbing:
state.Text ="Climbing";
break;
case PlayerState.OpeningAChest:
state.Text = "OpeningAChest";
break;
case PlayerState.UsingSpecialSkill:
state.Text = "UsingSpecialSkill";
break;
case PlayerState.OpeningAGate:
state.Text = "OpeningAGate";
break;
default:
break;
}
scores.Text = "Scores:" + Convert.ToString(obj.Score);
if (obj.TimeUntilSkillAvailable[0] >= 0)
skillprogress0.Value = 100 - obj.TimeUntilSkillAvailable[0] / coolTime0 * 100;
if (obj.TimeUntilSkillAvailable[1] >= 0)
skillprogress1.Value = 100 - obj.TimeUntilSkillAvailable[1] / coolTime1 * 100;
if (obj.TimeUntilSkillAvailable[2] >= 0)
skillprogress2.Value = 100 - obj.TimeUntilSkillAvailable[2] / coolTime2 * 100;
if (obj.PlayerState == PlayerState.Quit)
{
skillprogress0.Value = skillprogress1.Value = skillprogress2.Value = 0;
skillprogress0.Background = skillprogress1.Background = skillprogress2.Background = Brushes.Gray;
}
else
skillprogress.Background = Brushes.White;
// star.Text = "⭐:";不知道要放什么
status.Text = "🏹:" + Convert.ToString(1) + "\n🏃:" + Convert.ToString(obj.Speed) + "\n🤺:" + Convert.ToString(2) + "\n🗡:" + Convert.ToString(0);
scores.Text = "Scores:" + Convert.ToString(0);
foreach(var icon in obj.Prop)
skillprogress0.Background = skillprogress1.Background = skillprogress2.Background = Brushes.White;
int cnt = 0;
foreach (var icon in obj.Prop)
{
switch (icon)
switch (cnt)
{
case PropType.Key3:
prop.Text = "🔧";
case 0:
switch (icon)
{
case PropType.Key3:
prop0.Text = "🔧";
break;
case PropType.Key5:
prop0.Text = "🛡";
break;
case PropType.Key6:
prop0.Text = "♥";
break;
case PropType.Ptype4:
prop0.Text = "⛸";
break;
default:
prop0.Text = " ";
break;
}
cnt++;
break;
case PropType.Key5:
prop.Text = "🛡";
case 1:
switch (icon)
{
case PropType.Key3:
prop1.Text = "🔧";
break;
case PropType.Key5:
prop1.Text = "🛡";
break;
case PropType.Key6:
prop1.Text = "♥";
break;
case PropType.Ptype4:
prop1.Text = "⛸";
break;
default:
prop1.Text = " ";
break;
}
cnt++;
break;
case PropType.Key6:
prop.Text = "♥";
case 2:
switch (icon)
{
case PropType.Key3:
prop2.Text = "🔧";
break;
case PropType.Key5:
prop2.Text = "🛡";
break;
case PropType.Key6:
prop2.Text = "♥";
break;
case PropType.Ptype4:
prop2.Text = "⛸";
break;
default:
prop2.Text = " ";
break;
}
cnt++;
break;
case PropType.Ptype4:
prop.Text = "⛸";
case 3:
switch (icon)
{
case PropType.Key3:
prop3.Text = "🔧";
break;
case PropType.Key5:
prop3.Text = "🛡";
break;
case PropType.Key6:
prop3.Text = "♥";
break;
case PropType.Ptype4:
prop3.Text = "⛸";
break;
default:
prop3.Text = " ";
break;
}
cnt++;
break;
default:
prop.Text = " ";
break;
}
}
@@ -106,7 +245,7 @@ namespace Client
SetStaticValue(obj);
SetDynamicValue(obj);
}
private int coolTime;
private int coolTime0, coolTime1, coolTime2;
private bool initialized;
}
}

+ 17
- 16
logic/Client/StatusBarOfSurvivor.xaml View File

@@ -11,9 +11,10 @@
<RowDefinition Height="36*"/>
<RowDefinition Height="19*"/>
<RowDefinition Height="19*"/>
<RowDefinition Height="27*"/>
<RowDefinition Height="26*"/>
<RowDefinition Height="27*"/>
<RowDefinition Height="19*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="21*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@@ -22,18 +23,18 @@
<ColumnDefinition Width="10*"/>
<ColumnDefinition Width="10*"/>
</Grid.ColumnDefinitions>
<TextBox Name="serial" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="👥null🧓null&#xD;&#xA;职业:" Grid.Row="0" Grid.ColumnSpan="4" FontSize="12"/>
<TextBox Name="status" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="♥:" Grid.Row="1" Grid.ColumnSpan="4"/>
<TextBox Name="scores" IsReadOnly="True" BorderBrush="White" TextWrapping="Wrap" Text="Scores:" Grid.Row="2" Grid.ColumnSpan="4"/>
<TextBox Name="activeSkill0" IsReadOnly="True" BorderBrush="Gray" TextWrapping="Wrap" Text="" Grid.Row="3" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress0" Background="White" Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="activeSkill1" IsReadOnly="True" BorderBrush="Gray" TextWrapping="Wrap" Text="" Grid.Row="4" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress1" Background="White" Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="activeSkill2" IsReadOnly="True" BorderBrush="Gray" TextWrapping="Wrap" Text="" Grid.Row="5" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress2" Background="White" Grid.Row="5" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="prop0" IsReadOnly="True" BorderBrush="Gray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="1"/>
<TextBox Name="prop1" IsReadOnly="True" BorderBrush="Gray" Text="" FontSize="12" Margin="22,0,0,0" TextWrapping="Wrap" Grid.Row="6" Grid.ColumnSpan="2"/>
<TextBox Name="prop2" IsReadOnly="True" BorderBrush="Gray" Text="" FontSize="12" Margin="0,0,22,0" TextWrapping="Wrap" Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="prop3" IsReadOnly="True" BorderBrush="Gray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="6" Grid.Column="3" Grid.ColumnSpan="1"/>
<TextBox Name="serial" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="👥null🧓null&#xD;&#xA;职业:" Grid.Row="0" Grid.ColumnSpan="4" FontSize="12"/>
<TextBox Name="status" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="♥:" Grid.Row="1" Grid.RowSpan="2" Grid.ColumnSpan="4"/>
<TextBox Name="scores" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="Scores:" Grid.Row="3" Grid.ColumnSpan="4"/>
<TextBox Name="activeSkill0" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="" Grid.Row="4" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress0" Background="White" Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="activeSkill1" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="" Grid.Row="5" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress1" Background="White" Grid.Row="5" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="activeSkill2" IsReadOnly="True" BorderBrush="LightGray" TextWrapping="Wrap" Text="" Grid.Row="6" Grid.ColumnSpan="2"/>
<ProgressBar Name="skillprogress2" Background="White" Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2"/>
<TextBox Name="prop0" IsReadOnly="True" BorderBrush="LightGray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="1"/>
<TextBox Name="prop1" IsReadOnly="True" BorderBrush="LightGray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="1"/>
<TextBox Name="prop2" IsReadOnly="True" BorderBrush="LightGray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="1"/>
<TextBox Name="prop3" IsReadOnly="True" BorderBrush="LightGray" Text="" FontSize="12" TextWrapping="Wrap" Grid.Row="7" Grid.Column="3" Grid.ColumnSpan="1"/>
</Grid>
</UserControl>

+ 63
- 21
logic/Client/StatusBarOfSurvivor.xaml.cs View File

@@ -35,60 +35,103 @@ namespace Client

private void SetStaticValue(MessageOfStudent obj)
{
switch (obj.StudentType) // 参数未设定
switch (obj.StudentType) // coolTime参数未设定
{
case StudentType.Athlete:
coolTime = 10000;
serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\n职业:StudentType1";
coolTime0 = coolTime1 = coolTime2 = 10000;
serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\n职业:Athlete";
break;
case StudentType._2:
coolTime = 20000;
coolTime0 = coolTime1 = coolTime2 = 20000;
serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\n职业:StudentType2";
break;
case StudentType._3:
coolTime = 30000;
coolTime0 = coolTime1 = coolTime2 = 30000;
serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\n职业:StudentType3";
break;
case StudentType._4:
coolTime = 40000;
coolTime0 = coolTime1 = coolTime2 = 40000;
serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\n职业:StudentType4";
break;
case StudentType.NullStudentType:
coolTime = 10000;
coolTime0 = coolTime1 = coolTime2 = -1;
serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\n职业:NullStudentType";
break;
}
activeSkill0.Text = "Skill0";
activeSkill1.Text = "Skill1";
activeSkill2.Text = "Skill2";
initialized = true;
}
private void SetDynamicValue(MessageOfStudent obj)
{
int life;
int life= obj.Determination,death=obj.Addiction;
switch(obj.PlayerState)
{
case PlayerState.Idle:
life = obj.Determination;
status.Text = "♥:" + Convert.ToString(life);
status.Text = "♥:" + Convert.ToString(life)+"\nIdle";
break;
case PlayerState.Learning:
status.Text = "♥:" + Convert.ToString(life)+ "\nLearning";
break;
case PlayerState.Addicted:
life = obj.Addiction;
status.Text = "💀:" + Convert.ToString(life);
status.Text = "💀:" + Convert.ToString(death)+ "\nAddicted";
break;
case PlayerState.Graduated:
status.Text = "Graduated";
status.Text = status.Text = "♥" + "\nGraduated";
break;
case PlayerState.Quit:
status.Text = "Quit";
status.Text = "💀"+"\nQuit";
break;
case PlayerState.Treated:
status.Text = "♥:" + Convert.ToString(life) + "\nTreated";
break;
case PlayerState.Rescued:
status.Text = "💀:" + Convert.ToString(death)+ "\nRescued";
break;
case PlayerState.Stunned:
status.Text = "♥:" + Convert.ToString(life) + "\nStunned";
break;
case PlayerState.Treating:
status.Text = "♥:" + Convert.ToString(life) + "\nTreating";
break;
case PlayerState.Rescuing:
status.Text = "♥:" + Convert.ToString(life) + "\nRescuing";
break;
case PlayerState.Swinging:
status.Text = "♥:" + Convert.ToString(life) + "\nSwinging";
break;
case PlayerState.Attacking:
status.Text = "♥:" + Convert.ToString(life) + "\nAttacking";
break;
case PlayerState.Locking:
status.Text = "♥:" + Convert.ToString(life) + "\nLocking";
break;
case PlayerState.Rummaging:
status.Text = "♥:" + Convert.ToString(life) + "\nRummaging";
break;
case PlayerState.Climbing:
status.Text = "♥:" + Convert.ToString(life) + "\nClimbing";
break;
case PlayerState.OpeningAChest:
status.Text = "♥:" + Convert.ToString(life) + "\nOpeningAChest";
break;
case PlayerState.UsingSpecialSkill:
status.Text = "♥:" + Convert.ToString(life) + "\nUsingSpecialSkill";
break;
case PlayerState.OpeningAGate:
status.Text = "♥:" + Convert.ToString(life) + "\nOpeningAGate";
break;
default:
break;
}//不完全
}
scores.Text = "Scores:" + obj.Score;
if (obj.TimeUntilSkillAvailable[0]>=0)
skillprogress0.Value = 100 - obj.TimeUntilSkillAvailable[0] / coolTime * 100;
skillprogress0.Value = 100 - obj.TimeUntilSkillAvailable[0] / coolTime0 * 100;
if(obj.TimeUntilSkillAvailable[1] >= 0)
skillprogress1.Value = 100 - obj.TimeUntilSkillAvailable[1] / coolTime * 100;
skillprogress1.Value = 100 - obj.TimeUntilSkillAvailable[1] / coolTime1 * 100;
if(obj.TimeUntilSkillAvailable[2] >= 0)
skillprogress2.Value = 100 - obj.TimeUntilSkillAvailable[2] / coolTime * 100;
skillprogress2.Value = 100 - obj.TimeUntilSkillAvailable[2] / coolTime2 * 100;
if (obj.PlayerState == PlayerState.Quit)
{
skillprogress0.Value = skillprogress1.Value = skillprogress2.Value = 0;
@@ -187,8 +230,7 @@ namespace Client
break;
default:
break;
}
}
}
}
public void SetValue(MessageOfStudent obj)
@@ -197,7 +239,7 @@ namespace Client
SetStaticValue(obj);
SetDynamicValue(obj);
}
private int coolTime;
private int coolTime0, coolTime1, coolTime2;
private bool initialized;
}
}

+ 33
- 13
logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs View File

@@ -15,9 +15,13 @@ namespace GameClass.GameObj
public override int AP => GameData.basicApOfGhost;
public override int Speed => GameData.basicBulletMoveSpeed;
public override bool IsToBomb => false;

public override int CastTime => GameData.basicCastTime;
public override int Backswing => GameData.basicBackswing;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicBackswing;
public override int CD => cd;

public override bool CanAttack(GameObj target)
{
return false;
@@ -38,12 +42,16 @@ namespace GameClass.GameObj
}
public override double BulletBombRange => 0;
public override double BulletAttackRange => GameData.basicRemoteAttackRange * 13;
public override int AP => GameData.basicApOfGhost / 5 * 4;
public override int AP => GameData.basicApOfGhost * 4 / 5;
public override int Speed => GameData.basicBulletMoveSpeed * 2;
public override bool IsToBomb => false;

public override int CastTime => GameData.basicCastTime;
public override int Backswing => GameData.basicBackswing / 5 * 3;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit / 4 * 3;
public override int Backswing => GameData.basicBackswing * 2 / 5;
public override int RecoveryFromHit => GameData.basicBackswing * 3 / 4;
public const int cd = GameData.basicBackswing * 2 / 5 + 100;
public override int CD => cd;

public override bool CanAttack(GameObj target)
{
return false;
@@ -64,13 +72,17 @@ namespace GameClass.GameObj
base(player, radius, placeType, pos)
{
}
public override double BulletBombRange => GameData.basicBulletBombRange / 3 * 7;
public override double BulletAttackRange => GameData.basicAttackShortRange / 9 * 7;
public override int AP => GameData.basicApOfGhost / 3 * 7;
public override int Speed => GameData.basicBulletMoveSpeed / 3 * 2;
public override double BulletBombRange => GameData.basicBulletBombRange * 7 / 3;
public override double BulletAttackRange => GameData.basicAttackShortRange * 7 / 9;
public override int AP => GameData.basicApOfGhost * 7 / 3;
public override int Speed => GameData.basicBulletMoveSpeed * 2 / 3;

public override int CastTime => GameData.basicCastTime;
public override int Backswing => GameData.basicBackswing;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicCD;
public override int CD => cd;

public override bool IsToBomb => true;
public override bool CanAttack(GameObj target)
{
@@ -93,13 +105,17 @@ namespace GameClass.GameObj
base(player, radius, placeType, pos)
{
}
public override double BulletBombRange => GameData.basicBulletBombRange / 6 * 5;
public override double BulletBombRange => GameData.basicBulletBombRange * 5 / 6;
public override double BulletAttackRange => GameData.basicAttackShortRange / 2;
public override int AP => GameData.basicApOfGhost / 6 * 5;
public override int Speed => GameData.basicBulletMoveSpeed / 6 * 5;
public override int AP => GameData.basicApOfGhost * 5 / 6;
public override int Speed => GameData.basicBulletMoveSpeed * 5 / 6;

public override int CastTime => GameData.basicCastTime;
public override int Backswing => GameData.basicBackswing;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicCD;
public override int CD => cd;

public override bool IsToBomb => true;
public override bool CanAttack(GameObj target)
{
@@ -121,13 +137,15 @@ namespace GameClass.GameObj
base(player, radius, placeType, pos)
{
}
public override double BulletBombRange => GameData.basicBulletBombRange / 4 * 2;
public override double BulletBombRange => GameData.basicBulletBombRange * 2 / 4;
public override double BulletAttackRange => GameData.basicAttackShortRange;
public override int AP => (int)(0.5 * GameData.basicApOfGhost);
public override int Speed => 5 * GameData.basicBulletMoveSpeed / 3;
public override int CastTime => GameData.basicCastTime;
public override int Backswing => GameData.basicBackswing;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicCD;
public override int CD => cd;
public override bool IsToBomb => true;

public override bool CanAttack(GameObj target)
@@ -150,13 +168,15 @@ namespace GameClass.GameObj
base(player, radius, placeType, pos)
{
}
public override double BulletBombRange => GameData.basicBulletBombRange / 3 * 4;
public override double BulletBombRange => GameData.basicBulletBombRange * 4 / 3;
public override double BulletAttackRange => 0.1 * GameData.basicAttackShortRange;
public override int AP => GameData.basicApOfGhost / 3 * 2;
public override int AP => GameData.basicApOfGhost * 2 / 3;
public override int Speed => GameData.basicBulletMoveSpeed / 3;
public override int CastTime => GameData.basicCastTime;
public override int Backswing => GameData.basicBackswing;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicCD;
public override int CD => cd;
public override bool IsToBomb => true;

public override bool CanBeBombed(GameObjType gameObjType)


+ 27
- 11
logic/GameClass/GameObj/Bullet/Bullet.cs View File

@@ -17,6 +17,7 @@ namespace GameClass.GameObj
public abstract int CastTime { get; }
public abstract int Backswing { get; }
public abstract int RecoveryFromHit { get; }
public abstract int CD { get; }

private readonly bool hasSpear;
/// <summary>
@@ -56,25 +57,23 @@ namespace GameClass.GameObj
{
public static Bullet? GetBullet(Character character, PlaceType place, XY pos)
{
Bullet? newBullet = null;
switch (character.BulletOfPlayer)
{
case BulletType.FlyingKnife:
return new FlyingKnife(character, place, pos);
case BulletType.CommonAttackOfGhost:
return new CommonAttackOfGhost(character, place, pos);
case BulletType.AtomBomb:
newBullet = new AtomBomb(character, place, pos);
break;
return new AtomBomb(character, place, pos);
case BulletType.LineBullet:
newBullet = new LineBullet(character, place, pos);
break;
return new LineBullet(character, place, pos);
case BulletType.FastBullet:
newBullet = new FastBullet(character, place, pos);
break;
return new FastBullet(character, place, pos);
case BulletType.OrdinaryBullet:
newBullet = new OrdinaryBullet(character, place, pos);
break;
return new OrdinaryBullet(character, place, pos);
default:
break;
return null;
}
return newBullet;
}
public static int BulletRadius(BulletType bulletType)
{
@@ -84,9 +83,26 @@ namespace GameClass.GameObj
case BulletType.LineBullet:
case BulletType.FastBullet:
case BulletType.OrdinaryBullet:
case BulletType.FlyingKnife:
default:
return GameData.bulletRadius;
}
}
public static int BulletCD(BulletType bulletType)
{
switch (bulletType)
{
case BulletType.CommonAttackOfGhost:
return CommonAttackOfGhost.cd;
case BulletType.FlyingKnife:
return FlyingKnife.cd;
case BulletType.AtomBomb:
case BulletType.LineBullet:
case BulletType.FastBullet:
case BulletType.OrdinaryBullet:
default:
return GameData.basicCD;
}
}
}
}

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

@@ -9,23 +9,22 @@ namespace GameClass.GameObj
{
private readonly CharacterType characterType;
public CharacterType CharacterType => characterType;
private readonly IOccupation? occupation;
private readonly IOccupation occupation;
public IOccupation Occupation => occupation;


private Dictionary<ActiveSkillType, int> timeUntilActiveSkillAvailable = new();
public Dictionary<ActiveSkillType, int> TimeUntilActiveSkillAvailable => timeUntilActiveSkillAvailable;

private Dictionary<ActiveSkillType, IActiveSkill> iActiveSkillDictionary = new();
public Dictionary<ActiveSkillType, IActiveSkill> IActiveSkillDictionary => iActiveSkillDictionary;

public IActiveSkill? UseIActiveSkill(ActiveSkillType activeSkillType)
public IActiveSkill UseIActiveSkill(ActiveSkillType activeSkillType)
{
if (Occupation.ListOfIActiveSkill.Contains(activeSkillType))
{
return IActiveSkillDictionary[activeSkillType];
}
return null;
return new NullSkill();
}

public bool SetTimeUntilActiveSkillAvailable(ActiveSkillType activeSkillType, int timeUntilActiveSkillAvailable)
@@ -67,17 +66,15 @@ namespace GameClass.GameObj
this.occupation = new Assassin();
break;
case CharacterType.Athlete:
this.occupation = new Athlete();
break;
default:
this.occupation = null;
this.occupation = new Athlete();
break;
}
this.MaxHp = Occupation.MaxHp;
this.hp = Occupation.MaxHp;
this.OrgMoveSpeed = Occupation.MoveSpeed;
this.moveSpeed = Occupation.MoveSpeed;
this.cd = Occupation.CD;
this.OrgCD = this.cd = BulletFactory.BulletCD(Occupation.InitBullet);
this.maxBulletNum = Occupation.MaxBulletNum;
this.bulletNum = maxBulletNum;
this.bulletOfPlayer = Occupation.InitBullet;
@@ -88,7 +85,7 @@ namespace GameClass.GameObj
this.characterType = characterType;
this.SpeedOfOpeningOrLocking = Occupation.TimeOfOpeningOrLocking;
this.SpeedOfClimbingThroughWindows = Occupation.SpeedOfClimbingThroughWindows;
this.TimeOfOpenChest = Occupation.TimeOfOpenChest;
this.SpeedOfOpenChest = Occupation.TimeOfOpenChest;

foreach (var activeSkill in this.Occupation.ListOfIActiveSkill)
{


+ 38
- 0
logic/GameClass/GameObj/Character/Character.Student.cs View File

@@ -1,10 +1,48 @@
using Preparation.Utility;
using Preparation.Interface;
using System;

namespace GameClass.GameObj
{
public class Student : Character
{
/// <summary>
/// 遭受攻击
/// </summary>
/// <param name="subHP"></param>
/// <param name="hasSpear"></param>
/// <param name="attacker">伤害来源</param>
/// <returns>人物在受到攻击后死了吗</returns>
public bool BeAttacked(Bullet bullet)
{

lock (beAttackedLock)
{
if (hp <= 0 || NoHp())
return false; // 原来已经死了
if (bullet.Parent.TeamID != this.TeamID)
{
if (HasShield)
{
if (bullet.HasSpear)
bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(TrySubHp(bullet.AP)));
else
return false;
}
else
{
bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * TrySubHp(bullet.AP)));
}
#if DEBUG
Console.WriteLine($"PlayerID:{ID} is being shot! Now his hp is {hp}.");
#endif
if (hp <= 0)
TryActivatingLIFE(); // 如果有复活甲
}
return hp <= 0;
}
}

protected int fixSpeed;
/// <summary>
/// 修理电机速度


+ 20
- 46
logic/GameClass/GameObj/Character/Character.cs View File

@@ -10,9 +10,10 @@ namespace GameClass.GameObj
{
public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了
{
private readonly object beAttackedLock = new();
#region 装弹、攻击相关的基本属性及方法

protected readonly object beAttackedLock = new();

#region 装弹相关的基本属性及方法
/// <summary>
/// 装弹冷却
/// </summary>
@@ -44,7 +45,10 @@ namespace GameClass.GameObj
set
{
lock (gameObjLock)
{
bulletOfPlayer = value;
CD = BulletFactory.BulletCD(value);
}
}
}

@@ -93,43 +97,7 @@ namespace GameClass.GameObj
}
}

/// <summary>
/// 遭受攻击
/// </summary>
/// <param name="subHP"></param>
/// <param name="hasSpear"></param>
/// <param name="attacker">伤害来源</param>
/// <returns>人物在受到攻击后死了吗</returns>
public bool BeAttacked(Bullet bullet)
{

lock (beAttackedLock)
{
if (hp <= 0)
return false; // 原来已经死了
if (bullet.Parent.TeamID != this.TeamID)
{

if (HasShield)
{
if (bullet.HasSpear)
_ = TrySubHp(bullet.AP);
else
return false;
}
else
{
bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * TrySubHp(bullet.AP)));
}
#if DEBUG
Console.WriteLine($"PlayerID:{ID} is being shot! Now his hp is {hp}.");
#endif
if (hp <= 0)
TryActivatingLIFE(); // 如果有复活甲
}
return hp <= 0;
}
}
/*
/// <summary>
/// 攻击被反弹,反弹伤害不会再被反弹
/// </summary>
@@ -152,7 +120,7 @@ namespace GameClass.GameObj
}
return hp <= 0;
}
}
}*/
#endregion
#region 感知相关的基本属性及方法
/// <summary>
@@ -256,15 +224,15 @@ namespace GameClass.GameObj
}
}

private int timeOfOpenChest;
public int TimeOfOpenChest
private int speedOfOpenChest;
public int SpeedOfOpenChest
{
get => timeOfOpenChest;
get => speedOfOpenChest;
set
{
lock (gameObjLock)
{
timeOfOpenChest = value;
speedOfOpenChest = value;
}
}
}
@@ -372,12 +340,18 @@ namespace GameClass.GameObj
}
}

public bool NoHp() => (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued);
public bool Commandable() => (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescuing
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued
&& playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack
&& playerState != PlayerStateType.ClimbingThroughWindows && playerState != PlayerStateType.Stunned);
public bool InteractingWithMapWithoutMoving() => (playerState == PlayerStateType.LockingOrOpeningTheDoor || playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest);
public bool NullOrMoving() => (playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
public bool CanBeAwed() => !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped
|| playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued
|| playerState == PlayerStateType.Treated || playerState != PlayerStateType.Stunned
|| playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);

private int score = 0;
public int Score
@@ -542,7 +516,7 @@ namespace GameClass.GameObj
return false;
}
}
private void TryActivatingLIFE()
protected void TryActivatingLIFE()
{
if (buffManager.TryActivatingLIFE())
{


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

@@ -10,8 +10,6 @@ namespace GameClass.GameObj

private readonly object commonSkillLock = new object();
public object ActiveSkillLock => commonSkillLock;

public bool IsBeingUsed { get; set; }
}
public class CanBeginToCharge : IActiveSkill
{
@@ -20,8 +18,6 @@ namespace GameClass.GameObj

private readonly object commonSkillLock = new object();
public object ActiveSkillLock => commonSkillLock;

public bool IsBeingUsed { get; set; }
}

public class BecomeInvisible : IActiveSkill
@@ -31,8 +27,6 @@ namespace GameClass.GameObj

private readonly object commonSkillLock = new object();
public object ActiveSkillLock => commonSkillLock;

public bool IsBeingUsed { get; set; }
}

public class NuclearWeapon : IActiveSkill // 核武器
@@ -41,8 +35,6 @@ namespace GameClass.GameObj
public int DurationTime => GameData.commonSkillTime / 10;
private readonly object commonSkillLock = new object();
public object ActiveSkillLock => commonSkillLock;

public bool IsBeingUsed { get; set; }
}

public class UseKnife : IActiveSkill
@@ -51,8 +43,6 @@ namespace GameClass.GameObj
public int DurationTime => GameData.commonSkillTime / 10;
private readonly object commonSkillLock = new object();
public object ActiveSkillLock => commonSkillLock;

public bool IsBeingUsed { get; set; }
}

public class SuperFast : IActiveSkill // 3倍速
@@ -61,13 +51,19 @@ namespace GameClass.GameObj
public int DurationTime => GameData.commonSkillTime / 10 * 4;
private readonly object commonSkillLock = new object();
public object ActiveSkillLock => commonSkillLock;
}

public bool IsBeingUsed { get; set; }
public class NullSkill : IActiveSkill
{
public int SkillCD => GameData.commonSkillCD;
public int DurationTime => GameData.commonSkillTime;
private readonly object commonSkillLock = new object();
public object ActiveSkillLock => commonSkillLock;
}

public static class SkillFactory
{
public static IActiveSkill? FindIActiveSkill(ActiveSkillType activeSkillType)
public static IActiveSkill FindIActiveSkill(ActiveSkillType activeSkillType)
{
switch (activeSkillType)
{
@@ -76,7 +72,7 @@ namespace GameClass.GameObj
case ActiveSkillType.UseKnife:
return new UseKnife();
default:
return null;
return new NullSkill();
}
}



+ 10
- 5
logic/GameClass/GameObj/Map/Chest.cs View File

@@ -20,15 +20,20 @@ namespace GameClass.GameObj
private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() };
public Prop[] PropInChest => propInChest;

private bool isOpen = false;
public bool IsOpen
private int openDegree = 0;
public int OpenDegree
{
get => isOpen;
get => openDegree;
set
{
lock (gameObjLock)
isOpen = value;
if (value > 0)
lock (gameObjLock)
openDegree = (value > GameData.degreeOfOpeningChest) ? GameData.degreeOfOpeningChest : value;
else
lock (gameObjLock)
openDegree = 0;
}
}
public bool IsOpen() => (OpenDegree == GameData.degreeOfOpeningChest);
}
}

+ 7
- 7
logic/GameClass/GameObj/Map/Door.cs View File

@@ -14,12 +14,8 @@ namespace GameClass.GameObj
this.place = placeType;
this.CanMove = false;
}
public override bool IsRigid => true;
public override bool IsRigid => !isOpen;
public override ShapeType Shape => ShapeType.Square;
protected override bool IgnoreCollideExecutor(IGameObj targetObj)
{
return isOpen;
}

private bool isOpen = false;
public bool IsOpen
@@ -38,8 +34,12 @@ namespace GameClass.GameObj
get => openOrLockDegree;
set
{
lock (gameObjLock)
openOrLockDegree = (value > 0) ? value : 0;
if (value > 0)
lock (gameObjLock)
openOrLockDegree = (value > GameData.degreeOfLockingOrOpeningTheDoor) ? GameData.degreeOfLockingOrOpeningTheDoor : value;
else
lock (gameObjLock)
openOrLockDegree = 0;
}
}
}


+ 23
- 0
logic/GameClass/GameObj/Map/EmergencyExit.cs View File

@@ -25,5 +25,28 @@ namespace GameClass.GameObj
return true; // 不是鬼不碰撞
return false;
}


private bool canOpen = false;
public bool CanOpen
{
get => canOpen;
set
{
lock (gameObjLock)
canOpen = value;
}
}

private bool isOpen = false;
public bool IsOpen
{
get => isOpen;
set
{
lock (gameObjLock)
isOpen = value;
}
}
}
}

+ 22
- 1
logic/GameClass/GameObj/Map/Map.cs View File

@@ -13,6 +13,28 @@ namespace GameClass.GameObj
private readonly Dictionary<uint, XY> birthPointList; // 出生点列表
public Dictionary<uint, XY> BirthPointList => birthPointList;

private object lockForNum = new();
private uint numOfRepairedGenerators = 0;
public uint NumOfRepairedGenerators
{
get => numOfRepairedGenerators;
set
{
lock (lockForNum)
numOfRepairedGenerators = value;
}
}
private uint numOfSurvivingStudent = GameData.numOfStudent;
public uint NumOfSurvivingStudent
{
get => numOfSurvivingStudent;
set
{
lock (lockForNum)
numOfSurvivingStudent = value;
}
}

private Dictionary<GameObjType, IList<IGameObj>> gameObjDict;
public Dictionary<GameObjType, IList<IGameObj>> GameObjDict => gameObjDict;
private Dictionary<GameObjType, ReaderWriterLockSlim> gameObjLockDict;
@@ -31,7 +53,6 @@ namespace GameClass.GameObj
return PlaceType.Null;
}
}

public PlaceType GetPlaceType(XY pos)
{
try


+ 66
- 59
logic/Gaming/ActionManager.cs View File

@@ -34,7 +34,7 @@ namespace Gaming

public bool Fix(Student player)// 自动检查有无发电机可修
{
if (player.IsGhost() || (!player.Commandable()) || player.PlayerState == PlayerStateType.Fixing)
if ((!player.Commandable()) || player.PlayerState == PlayerStateType.Fixing)
return false;
Generator? generatorForFix = (Generator?)gameMap.OneForInteract(player.Position, GameObjType.Generator);

@@ -46,11 +46,14 @@ namespace Gaming
(
() =>
{
int ScoreAdded = GameData.StudentScoreFix(generatorForFix.DegreeOfRepair);
new FrameRateTaskExecutor<int>(
loopCondition: () => player.PlayerState == PlayerStateType.Fixing && gameMap.Timer.IsGaming && generatorForFix.DegreeOfRepair < GameData.degreeOfFixedGenerator,
loopToDo: () =>
{
generatorForFix.Repair(player.FixSpeed * GameData.frameDuration);
player.AddScore(GameData.StudentScoreFix(generatorForFix.DegreeOfRepair - ScoreAdded));
ScoreAdded = GameData.StudentScoreFix(generatorForFix.DegreeOfRepair);
},
timeInterval: GameData.frameDuration,
finallyReturn: () => 0
@@ -58,34 +61,33 @@ namespace Gaming
.Start();
if (generatorForFix.DegreeOfRepair >= GameData.degreeOfFixedGenerator)
{
gameMap.NumOfRepairedGenerators++;
if (player.PlayerState == PlayerStateType.Fixing) player.PlayerState = PlayerStateType.Null;
Doorway exit = (Doorway)gameMap.GameObjDict[GameObjType.Doorway][1];
if (!exit.PowerSupply)
if (gameMap.NumOfRepairedGenerators == GameData.numOfGeneratorRequiredForEmergencyExit)
{
gameMap.GameObjLockDict[GameObjType.Generator].EnterReadLock();
gameMap.GameObjLockDict[GameObjType.EmergencyExit].EnterWriteLock();
try
{
int numOfFixedGenerator = 0;
foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator])
if (generator.DegreeOfRepair == GameData.degreeOfFixedGenerator)
++numOfFixedGenerator;
if (numOfFixedGenerator >= GameData.numOfGeneratorRequiredForRepair)
{
gameMap.GameObjLockDict[GameObjType.Doorway].EnterWriteLock();
try
{
foreach (Doorway doorway in gameMap.GameObjDict[GameObjType.Doorway])
doorway.PowerSupply = true;
}
finally
{
gameMap.GameObjLockDict[GameObjType.Doorway].ExitWriteLock();
}
}
Random r = new Random(Environment.TickCount);
((EmergencyExit)(gameMap.GameObjDict[GameObjType.EmergencyExit][r.Next(0, gameMap.GameObjDict[GameObjType.EmergencyExit].Count)])).CanOpen = true;
}
finally
{
gameMap.GameObjLockDict[GameObjType.Generator].ExitReadLock();
gameMap.GameObjLockDict[GameObjType.EmergencyExit].ExitWriteLock();
}
}
else
if (gameMap.NumOfRepairedGenerators == GameData.numOfGeneratorRequiredForRepair)
{
gameMap.GameObjLockDict[GameObjType.Doorway].EnterWriteLock();
try
{
foreach (Doorway doorway in gameMap.GameObjDict[GameObjType.Doorway])
doorway.PowerSupply = true;
}
finally
{
gameMap.GameObjLockDict[GameObjType.Doorway].ExitWriteLock();
}
}

@@ -98,22 +100,22 @@ namespace Gaming
return true;
}

public bool OpenDoorWay(Student player)
public bool OpenDoorway(Student player)
{
if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorWay)
if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorway)
return false;
Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway);
if (doorwayToOpen == null || doorwayToOpen.IsOpening || !doorwayToOpen.PowerSupply)
return false;

player.PlayerState = PlayerStateType.OpeningTheDoorWay;
player.PlayerState = PlayerStateType.OpeningTheDoorway;
doorwayToOpen.IsOpening = true;
new Thread
(
() =>
{
new FrameRateTaskExecutor<int>(
loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheDoorWay && gameMap.Timer.IsGaming && doorwayToOpen.OpenDegree < GameData.degreeOfOpenedDoorway,
loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheDoorway && gameMap.Timer.IsGaming && doorwayToOpen.OpenDegree < GameData.degreeOfOpenedDoorway,
loopToDo: () =>
{
doorwayToOpen.OpenDegree += GameData.frameDuration;
@@ -126,7 +128,7 @@ namespace Gaming
doorwayToOpen.IsOpening = false;
if (doorwayToOpen.OpenDegree >= GameData.degreeOfOpenedDoorway)
{
if (player.PlayerState == PlayerStateType.OpeningTheDoorWay)
if (player.PlayerState == PlayerStateType.OpeningTheDoorway)
player.PlayerState = PlayerStateType.Null;
}
}
@@ -141,33 +143,29 @@ namespace Gaming
{
if (!(player.Commandable()))
return false;
Doorway? doorwayForEscape = null;

gameMap.GameObjLockDict[GameObjType.Doorway].EnterReadLock();
try
Doorway? doorwayForEscape = (Doorway?)gameMap.OneInTheSameCell(player.Position, GameObjType.Doorway);
if (doorwayForEscape != null)
{
foreach (Doorway doorway in gameMap.GameObjDict[GameObjType.Doorway])
if (doorwayForEscape.IsOpen())
{
if (GameData.IsInTheSameCell(doorway.Position, player.Position))
{
doorwayForEscape = doorway;
break;
}
player.AddScore(GameData.StudentScoreEscape);
player.Die(PlayerStateType.Escaped);
return true;
}
else
return false;
}
finally
{
gameMap.GameObjLockDict[GameObjType.Doorway].ExitReadLock();
}


if (doorwayForEscape != null && doorwayForEscape.IsOpen())
else
{
player.Die(PlayerStateType.Escaped);
return true;
EmergencyExit? emergencyExit = (EmergencyExit?)gameMap.OneInTheSameCell(player.Position, GameObjType.EmergencyExit);
if (emergencyExit != null && emergencyExit.IsOpen)
{
player.Die(PlayerStateType.Escaped);
return true;
}
else
return false;
}
else
return false;
}

public bool Treat(Student player, Student playerTreated)
@@ -211,12 +209,14 @@ namespace Gaming

if (playerTreated.HP + playerTreated.DegreeOfTreatment >= playerTreated.MaxHp)
{
player.AddScore(GameData.StudentScoreTreat(playerTreated.MaxHp - playerTreated.HP));
playerTreated.HP = playerTreated.MaxHp;
playerTreated.DegreeOfTreatment = 0;
}
else
if (playerTreated.DegreeOfTreatment >= GameData.basicTreatmentDegree)
{
player.AddScore(GameData.StudentScoreTreat(GameData.basicTreatmentDegree));
playerTreated.HP += GameData.basicTreatmentDegree;
playerTreated.DegreeOfTreatment = 0;
}
@@ -248,8 +248,17 @@ namespace Gaming
)
.Start();

if (playerRescued.PlayerState == PlayerStateType.Rescued) playerRescued.PlayerState = PlayerStateType.Null;
if (player.PlayerState == PlayerStateType.Rescuing) player.PlayerState = (player.TimeOfRescue >= GameData.basicTimeOfRescue) ? PlayerStateType.Null : PlayerStateType.Addicted;
if (playerRescued.PlayerState == PlayerStateType.Rescued)
{
if (player.TimeOfRescue >= GameData.basicTimeOfRescue)
{
playerRescued.PlayerState = PlayerStateType.Null;
player.AddScore(GameData.StudentScoreRescue);
}
else
playerRescued.PlayerState = PlayerStateType.Addicted;
}
if (player.PlayerState == PlayerStateType.Rescuing) player.PlayerState = PlayerStateType.Null;
player.TimeOfRescue = 0;
}
)
@@ -263,7 +272,7 @@ namespace Gaming
return false;
Chest? chestToOpen = (Chest?)gameMap.OneForInteract(player.Position, GameObjType.Chest);

if (chestToOpen == null || chestToOpen.IsOpen)
if (chestToOpen == null || chestToOpen.IsOpen() || chestToOpen.OpenDegree > 0)
return false;

player.PlayerState = PlayerStateType.OpeningTheChest;
@@ -271,12 +280,11 @@ namespace Gaming
(
() =>
{
int OpenDegree = 0;
new FrameRateTaskExecutor<int>(
loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheChest && gameMap.Timer.IsGaming && OpenDegree < player.TimeOfOpenChest,
loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheChest && gameMap.Timer.IsGaming && (!chestToOpen.IsOpen()),
loopToDo: () =>
{
OpenDegree += GameData.frameDuration;
chestToOpen.OpenDegree += GameData.frameDuration * player.SpeedOfOpenChest;
},
timeInterval: GameData.frameDuration,
finallyReturn: () => 0
@@ -284,9 +292,8 @@ namespace Gaming

.Start();

if (OpenDegree >= player.TimeOfOpenChest)
if (chestToOpen.IsOpen())
{
chestToOpen.IsOpen = true;
if (player.PlayerState == PlayerStateType.OpeningTheChest)
player.PlayerState = PlayerStateType.Null;
for (int i = 0; i < GameData.maxNumOfPropInChest; ++i)
@@ -297,6 +304,8 @@ namespace Gaming
gameMap.Add(prop);
}
}
else chestToOpen.OpenDegree = 0;

}

)
@@ -323,7 +332,6 @@ namespace Gaming
(
() =>
{

new FrameRateTaskExecutor<int>(
loopCondition: () => player.PlayerState == PlayerStateType.ClimbingThroughWindows && gameMap.Timer.IsGaming,
loopToDo: () => { },
@@ -404,8 +412,7 @@ namespace Gaming
loopCondition: () => flag && player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor,
loopToDo: () =>
{
Character? character = (Character?)gameMap.OneInTheSameCell(doorToLock.Position, GameObjType.Character);
flag = (character != null);
flag = ((gameMap.OneInTheSameCell(doorToLock.Position, GameObjType.Character)) != null);
doorToLock.OpenOrLockDegree += GameData.frameDuration * player.SpeedOfOpeningOrLocking;
},
timeInterval: GameData.frameDuration,


+ 45
- 38
logic/Gaming/AttackManager.cs View File

@@ -38,8 +38,9 @@ namespace Gaming
);
}

private void BeAddictedToGame(Student player)
private void BeAddictedToGame(Student player, Ghost ghost)
{
ghost.AddScore(GameData.TrickerScoreStudentBeAddicted);
new Thread
(() =>
{
@@ -47,16 +48,17 @@ namespace Gaming
player.GamingAddiction = GameData.MidGamingAddiction;
player.PlayerState = PlayerStateType.Addicted;
new FrameRateTaskExecutor<int>(
() => player.PlayerState == PlayerStateType.Addicted && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming,
() => (player.PlayerState == PlayerStateType.Addicted || player.PlayerState == PlayerStateType.Rescued) && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming,
() =>
{
player.GamingAddiction += GameData.frameDuration;
player.GamingAddiction += (player.PlayerState == PlayerStateType.Addicted) ? GameData.frameDuration : 0;
},
timeInterval: GameData.frameDuration,
() =>
{
if (player.GamingAddiction == player.MaxGamingAddiction && gameMap.Timer.IsGaming)
{
ghost.AddScore(GameData.TrickerScoreStudentDie);
Die(player);
}
return 0;
@@ -98,15 +100,6 @@ namespace Gaming
{

player.Die(PlayerStateType.Deceased);
// gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock();
// try
//{
// gameMap.GameObjDict[GameObjType.Character].Remove(playerBeingShot);
// }
// finally
//{
// gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock();
// }

for (int i = 0; i < GameData.maxNumOfPropInPropInventory; i++)
{
@@ -117,34 +110,28 @@ namespace Gaming
gameMap.Add(prop);
}
}
--gameMap.NumOfSurvivingStudent;
if (gameMap.NumOfSurvivingStudent == 1)
{
gameMap.GameObjLockDict[GameObjType.EmergencyExit].EnterReadLock();
try
{
foreach (EmergencyExit emergencyExit in gameMap.GameObjDict[GameObjType.EmergencyExit])
if (emergencyExit.CanOpen)
{
emergencyExit.IsOpen = true;
break;
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.EmergencyExit].ExitReadLock();
}
}

// player.Reset();
// ((Character?)bullet.Parent)?.AddScore(GameData.addScoreWhenKillOneLevelPlayer); // 给击杀者加分

/* new Thread
(() =>
{

Thread.Sleep(GameData.reviveTime);

playerBeingShot.AddShield(GameData.shieldTimeAtBirth); // 复活加个盾

// gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock();
// try
//{
// gameMap.GameObjDict[GameObjType.Character].Add(playerBeingShot);
// }
// finally { gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); }

if (gameMap.Timer.IsGaming)
{
playerBeingShot.CanMove = true;
}
playerBeingShot.Deceased = false;
}
)
{ IsBackground = true }.Start();
*/
}

private void BombObj(Bullet bullet, GameObj objBeingShot)
@@ -154,10 +141,30 @@ namespace Gaming
case GameObjType.Character:

if ((!((Character)objBeingShot).IsGhost()) && bullet.Parent.IsGhost())
if (((Character)objBeingShot).BeAttacked(bullet))
{
Student oneBeAttacked = (Student)objBeingShot;
if (oneBeAttacked.BeAttacked(bullet))
{
BeAddictedToGame((Student)objBeingShot);
BeAddictedToGame(oneBeAttacked, (Ghost)bullet.Parent);
}
if (oneBeAttacked.CanBeAwed())
{
oneBeAttacked.PlayerState = PlayerStateType.Stunned;
bullet.Parent.AddScore(GameData.TrickerScoreStudentBeStunned);
new Thread
(
() =>
{
Thread.Sleep(GameData.basicStunnedTimeOfStudent);
if (oneBeAttacked.PlayerState == PlayerStateType.Stunned)
{
oneBeAttacked.PlayerState = PlayerStateType.Null;
}
}
)
{ IsBackground = true }.Start();
}
}
// if (((Character)objBeingShot).IsGhost() && !bullet.Parent.IsGhost() && bullet.TypeOfBullet == BulletType.Ram)
// BeStunned((Character)objBeingShot, bullet.AP);
break;


+ 58
- 10
logic/Gaming/Game.cs View File

@@ -54,7 +54,7 @@ namespace Gaming
() =>
{
while (!gameMap.Timer.IsGaming)
Thread.Sleep(newPlayer.CD);
Thread.Sleep(Math.Max(newPlayer.CD, GameData.checkInterval));
long lastTime = Environment.TickCount64;
new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting,
@@ -63,7 +63,7 @@ namespace Gaming
long nowTime = Environment.TickCount64;
if (newPlayer.BulletNum == newPlayer.MaxBulletNum)
lastTime = nowTime;
if (nowTime - lastTime >= newPlayer.CD)
else if (nowTime - lastTime >= newPlayer.CD)
{
_ = newPlayer.TryAddBulletNum();
lastTime = nowTime;
@@ -85,13 +85,14 @@ namespace Gaming
)
{ IsBackground = true }.Start();
#endregion
#region BGM更新
#region BGM,牵制得分更新
new Thread
(
() =>
{
while (!gameMap.Timer.IsGaming)
Thread.Sleep((int)GameData.checkInterval);
Thread.Sleep(GameData.checkInterval);
int TimePinningDown = 0, ScoreAdded = 0;
new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting,
loopToDo: () =>
@@ -121,6 +122,13 @@ namespace Gaming
{
if (XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position));
if (XY.Distance(newPlayer.Position, person.Position) <= GameData.basicViewRange)
{
TimePinningDown += GameData.checkInterval;
newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded);
ScoreAdded = GameData.StudentScorePinDown(TimePinningDown);
}
else TimePinningDown = ScoreAdded = 0;
break;
}
}
@@ -252,11 +260,8 @@ namespace Gaming
if (!gameMap.Timer.IsGaming)
return false;
ICharacter? player = gameMap.FindPlayer(playerID);
if (player != null)
{
if (!player.IsGhost())
return actionManager.Fix((Student)player);
}
if (player != null && !player.IsGhost())
return actionManager.Fix((Student)player);
return false;
}
public bool Escape(long playerID)
@@ -282,7 +287,50 @@ namespace Gaming
}
return false;
}

public bool OpenDoorway(long playerID)
{
if (!gameMap.Timer.IsGaming)
return false;
Character? player = gameMap.FindPlayer(playerID);
if (player != null && !player.IsGhost())
{
return actionManager.OpenDoorway((Student)player);
}
return false;
}
public bool OpenChest(long playerID)
{
if (!gameMap.Timer.IsGaming)
return false;
Character? player = gameMap.FindPlayer(playerID);
if (player != null)
{
return actionManager.OpenChest(player);
}
return false;
}
public bool ClimbingThroughWindow(long playerID)
{
if (!gameMap.Timer.IsGaming)
return false;
Character? player = gameMap.FindPlayer(playerID);
if (player != null)
{
return actionManager.ClimbingThroughWindow(player);
}
return false;
}
public bool LockOrOpenDoor(long playerID)
{
if (!gameMap.Timer.IsGaming)
return false;
Character? player = gameMap.FindPlayer(playerID);
if (player != null)
{
return actionManager.LockOrOpenDoor(player);
}
return false;
}
public void Attack(long playerID, double angle)
{
if (!gameMap.Timer.IsGaming)


+ 3
- 17
logic/Gaming/PropManager.cs View File

@@ -62,21 +62,7 @@ namespace Gaming
Prop pickProp = new NullProp();
if (propType == PropType.Null) // 自动检查有无道具可捡
{
gameMap.GameObjLockDict[GameObjType.Prop].EnterReadLock();
try
{
foreach (Prop prop in gameMap.GameObjDict[GameObjType.Prop])
{
if (GameData.IsInTheSameCell(prop.Position, player.Position))
{
player.PropInventory[indexing] = prop;
}
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Prop].ExitReadLock();
}
pickProp = player.PropInventory[indexing] = ((Prop?)gameMap.OneInTheSameCell(player.Position, GameObjType.Prop)) ?? new NullProp();
}
else
{
@@ -89,7 +75,7 @@ namespace Gaming
{
if (GameData.IsInTheSameCell(prop.Position, player.Position) && prop.CanMove == false)
{
player.PropInventory[indexing] = prop;
pickProp = player.PropInventory[indexing] = prop;
}
}
}
@@ -135,7 +121,7 @@ namespace Gaming
case 3:
return new Spear(Pos, gameMap.GetPlaceType(Pos));
default:
return null;
return new NullProp();
}
}



+ 2
- 0
logic/Preparation/Interface/ICharacter.cs View File

@@ -7,6 +7,8 @@ namespace Preparation.Interface
{
public long TeamID { get; }
public int HP { get; set; }
public int Score { get; }
public void AddScore(int add);
public double Vampire { get; }
public PlayerStateType PlayerState { get; set; }
public bool IsGhost();


+ 47
- 14
logic/Preparation/Interface/IOccupation.cs View File

@@ -8,7 +8,6 @@ namespace Preparation.Interface
public int MoveSpeed { get; }
public int MaxHp { get; }
public BulletType InitBullet { get; }
public int CD { get; }
public int MaxBulletNum { get; }
public List<ActiveSkillType> ListOfIActiveSkill { get; }
public List<PassiveSkillType> ListOfIPassiveSkill { get; }
@@ -32,15 +31,12 @@ namespace Preparation.Interface

public class Assassin : IGhost
{
private const int moveSpeed = GameData.basicMoveSpeed / 380 * 473;
private const int moveSpeed = GameData.basicMoveSpeed * 473 / 380;
public int MoveSpeed => moveSpeed;

private const int maxHp = GameData.basicHp;
public int MaxHp => maxHp;

public const int cd = 0;
public int CD => cd;

public const int maxBulletNum = 1;
public int MaxBulletNum => maxBulletNum;

@@ -64,19 +60,56 @@ namespace Preparation.Interface
public int speedOfClimbingThroughWindows = GameData.basicGhostSpeedOfClimbingThroughWindows;
public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows;

public int timeOfOpenChest = GameData.basicTimeOfOpenChest;
public int timeOfOpenChest = GameData.basicSpeedOfOpenChest;
public int TimeOfOpenChest => timeOfOpenChest;
}
public class Athlete : IStudent
public class Teacher : IStudent
{
private const int moveSpeed = GameData.basicMoveSpeed / 38 * 40;
private const int moveSpeed = GameData.basicMoveSpeed * 3 / 4;
public int MoveSpeed => moveSpeed;

private const int maxHp = GameData.basicHp / 30 * 32;
private const int maxHp = GameData.basicHp * 10;
public int MaxHp => maxHp;

public const int cd = 0;
public int CD => cd;
public const int maxBulletNum = 0;
public int MaxBulletNum => maxBulletNum;

public BulletType InitBullet => BulletType.Null;

public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.CanBeginToCharge });
public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { });

public const int fixSpeed = GameData.basicFixSpeed * 6 / 10;
public int FixSpeed => fixSpeed;

public const int treatSpeed = GameData.basicTreatSpeed * 8 / 10;
public int TreatSpeed => treatSpeed;

public const double concealment = GameData.basicConcealment * 0.9;
public double Concealment => concealment;

public const int alertnessRadius = (int)(GameData.basicAlertnessRadius * 0.9);
public int AlertnessRadius => alertnessRadius;

public int viewRange = (int)(GameData.basicViewRange * 1.1);
public int ViewRange => viewRange;

public int timeOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking * 12 / 10;
public int TimeOfOpeningOrLocking => timeOfOpeningOrLocking;

public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows * 12 / 10;
public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows;

public int timeOfOpenChest = GameData.basicSpeedOfOpenChest;
public int TimeOfOpenChest => timeOfOpenChest;
}
public class Athlete : IStudent
{
private const int moveSpeed = GameData.basicMoveSpeed * 40 / 38;
public int MoveSpeed => moveSpeed;

private const int maxHp = GameData.basicHp * 32 / 30;
public int MaxHp => maxHp;

public const int maxBulletNum = 0;
public int MaxBulletNum => maxBulletNum;
@@ -86,10 +119,10 @@ namespace Preparation.Interface
public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.CanBeginToCharge });
public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { });

public const int fixSpeed = GameData.basicFixSpeed / 10 * 6;
public const int fixSpeed = GameData.basicFixSpeed * 6 / 10;
public int FixSpeed => fixSpeed;

public const int treatSpeed = GameData.basicTreatSpeed / 10 * 8;
public const int treatSpeed = GameData.basicTreatSpeed * 8 / 10;
public int TreatSpeed => treatSpeed;

public const double concealment = GameData.basicConcealment * 0.9;
@@ -107,7 +140,7 @@ namespace Preparation.Interface
public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows * 12 / 10;
public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows;

public int timeOfOpenChest = GameData.basicTimeOfOpenChest;
public int timeOfOpenChest = GameData.basicSpeedOfOpenChest;
public int TimeOfOpenChest => timeOfOpenChest;
}
}

+ 0
- 1
logic/Preparation/Interface/ISkill.cs View File

@@ -11,6 +11,5 @@
public int SkillCD { get; }
public int DurationTime { get; } //技能持续时间
public object ActiveSkillLock { get; }
public bool IsBeingUsed { get; set; }
}
}

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

@@ -23,7 +23,7 @@ namespace Preparation.Utility
OpeningTheChest = 14,
ClimbingThroughWindows = 15,
UsingSkill = 16,
OpeningTheDoorWay = 17,
OpeningTheDoorway = 17,
}
public enum GameObjType
{


+ 44
- 7
logic/Preparation/Utility/GameData.cs View File

@@ -1,4 +1,5 @@
using System;
using System.Net.NetworkInformation;

namespace Preparation.Utility
{
@@ -7,6 +8,7 @@ namespace Preparation.Utility
#region 基本常数
public const int numOfStepPerSecond = 20; // 每秒行走的步数
public const int frameDuration = 50; // 每帧时长
public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长

public const long gameDuration = 600000; // 游戏时长600000ms = 10min

@@ -21,7 +23,6 @@ namespace Preparation.Utility

public const int numOfBirthPoint = 5;
// public const int numOfGenerator = 9;
public const int numOfGeneratorRequiredForRepair = 7;
public const int numOfChest = 8;

private const int numOfObjNotMap = 5;
@@ -61,6 +62,7 @@ namespace Preparation.Utility
}
#endregion
#region 角色相关
public const int numOfStudent = 4;
public const int characterRadius = numOfPosGridPerCell / 2 / 5 * 4; // 人物半径

public const int basicTreatSpeed = 100;
@@ -68,7 +70,7 @@ namespace Preparation.Utility
public const int basicSpeedOfOpeningOrLocking = 3280;
public const int basicStudentSpeedOfClimbingThroughWindows = 611;
public const int basicGhostSpeedOfClimbingThroughWindows = 1270;
public const int basicTimeOfOpenChest = 10000;
public const int basicSpeedOfOpenChest = 1000;

public const int basicHp = 3000000; // 初始血量
public const int basicMaxGamingAddiction = 60000;//基本完全沉迷时间
@@ -85,7 +87,6 @@ namespace Preparation.Utility
public const int basicAlertnessRadius = 30700;
public const int basicViewRange = 5 * numOfPosGridPerCell;
public const int maxNumOfPropInPropInventory = 3;
public const int addScoreWhenKillOneLevelPlayer = 30; // 击杀一级角色获得的加分

public static XY PosWhoDie = new XY(1, 1);

@@ -98,6 +99,41 @@ namespace Preparation.Utility
};
}
#endregion
#region 得分相关
public static int TrickerScoreAttackStudent(int damage)
{
return damage * 100 / basicApOfGhost;
}
public const int TrickerScoreStudentBeAddicted = 50;
public const int TrickerScoreStudentBeStunned = 25;
public const int TrickerScoreStudentDie = 1000;

public static int StudentScoreFix(int degreeOfFix)
{
return degreeOfFix;
}
public const int StudentScoreFixed = 25;
public static int StudentScorePinDown(int timeOfPiningDown)
{
return 0;
}
public const int StudentScoreTrickerBeStunned = 25;
public const int StudentScoreRescue = 100;
public static int StudentScoreTreat(int degree)
{
return degree;
}
public const int StudentScoreEscape = 1000;

public static int ScoreUseProp(PropType prop, bool IsGhost)
{
return 0;
}
public static int ScoreUseSkill(ActiveSkillType activeSkillType)
{
return 0;
}
#endregion
#region 攻击与子弹相关
public const int basicApOfGhost = 1500000; // 捣蛋鬼攻击力
public const int MinAP = 0; // 最小攻击力
@@ -105,8 +141,9 @@ namespace Preparation.Utility

public const int basicCD = 3000; // 初始子弹冷却
public const int basicCastTime = 500;//基本前摇时间
public const int basicBackswing = 500;//基本后摇时间
public const int basicBackswing = 818;//基本后摇时间
public const int basicRecoveryFromHit = 4300;//基本命中攻击恢复时长
public const int basicStunnedTimeOfStudent = 4130;

public const int bulletRadius = 200; // 默认子弹半径
public const int basicBulletNum = 3; // 基本初始子弹量
@@ -141,11 +178,11 @@ namespace Preparation.Utility
#region 物体相关
public const int degreeOfFixedGenerator = 10300000;
public const int degreeOfLockingOrOpeningTheDoor = 10000;
public const int degreeOfOpeningChest = 10000;
public const int degreeOfOpenedDoorway = 18000;
public const int maxNumOfPropInChest = 2;
#endregion
#region 游戏帧相关
public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长
public const int numOfGeneratorRequiredForRepair = 7;
public const int numOfGeneratorRequiredForEmergencyExit = 3;
#endregion
}
}

+ 38
- 11
logic/Server/CopyInfo.cs View File

@@ -96,7 +96,7 @@ namespace Server
return PlayerState.Attacking;
case Preparation.Utility.PlayerStateType.UsingSkill:
return PlayerState.UsingSpecialSkill;
case Preparation.Utility.PlayerStateType.OpeningTheDoorWay:
case Preparation.Utility.PlayerStateType.OpeningTheDoorway:
return PlayerState.OpeningAGate;
default:
return PlayerState.NullStatus;
@@ -173,9 +173,16 @@ namespace Server
return PickedProp((PickedProp)gameObj);
case Preparation.Utility.GameObjType.Generator:
return Classroom((Generator)gameObj);
// case Preparation.Utility.GameObjType.Chest:
case Preparation.Utility.GameObjType.Chest:
return Chest((Chest)gameObj);
case Preparation.Utility.GameObjType.Doorway:
return Gate((Doorway)gameObj);
case Preparation.Utility.GameObjType.EmergencyExit:
if (((EmergencyExit)gameObj).CanOpen)
return HiddenGate((EmergencyExit)gameObj);
else return null;
case Preparation.Utility.GameObjType.Door:
return Door((Door)gameObj);
default: return null;
}
}
@@ -341,14 +348,34 @@ namespace Server
msg.GateMessage.Progress = doorway.OpenDegree;
return msg;
}
/* private static MessageOfObj Chest(Chest chest)
{
MessageOfObj msg = new MessageOfObj();
msg.ChestMessage = new();
msg.ChestMessage.X=chest.Position.x;
msg.ChestMessage.Y=chest.Position.y;
// msg.ChestMessage.Progress=generator.DegreeOfRepair;
return msg;
}*/
private static MessageOfObj HiddenGate(EmergencyExit Exit)
{
MessageOfObj msg = new MessageOfObj();
msg.HiddenGateMessage = new();
msg.HiddenGateMessage.X = Exit.Position.x;
msg.HiddenGateMessage.Y = Exit.Position.y;
msg.HiddenGateMessage.Opened = Exit.IsOpen;
return msg;
}

private static MessageOfObj Door(Door door)
{
MessageOfObj msg = new MessageOfObj();
msg.DoorMessage = new();
msg.DoorMessage.X = door.Position.x;
msg.DoorMessage.Y = door.Position.y;
msg.DoorMessage.Progress = door.OpenOrLockDegree;
msg.DoorMessage.IsOpen = door.IsOpen;
return msg;
}
private static MessageOfObj Chest(Chest chest)
{
MessageOfObj msg = new MessageOfObj();
msg.ChestMessage = new();
msg.ChestMessage.X = chest.Position.x;
msg.ChestMessage.Y = chest.Position.y;
msg.ChestMessage.Progress = chest.OpenDegree;
return msg;
}
}
}

+ 95
- 46
logic/规则Logic.md View File

@@ -28,7 +28,7 @@
- 只展示外部需要的属性,部分属性被省略

### BgmType
- *枚举类BgmType*
- 枚举类BgmType
1. 不详的感觉:监管者进入(求生者的警戒半径/监管者的隐蔽度)时,求生者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离)
2. 期待搞事的感觉:求生者进入(监管者的警戒半径/求生者的隐蔽度)时,监管者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/可被发觉的最近的求生者距离)
3. 修理电机的声音: 监管者警戒半径内有电机正在被修理时收到;bgmVolume=(警戒半径*电机修理程度/二者距离)/10300000
@@ -81,22 +81,23 @@
public enum PlayerStateType
{
Null = 0,
IsAddicted = 1,
IsEscaped = 2,
IsSwinging = 3,//指后摇
IsDeceased = 4,
IsMoving = 5,
IsTreating = 6,
IsRescuing = 7,
IsFixing = 8,
IsTreated = 9,
IsRescued = 10,
IsStunned = 11,
IsTryingToAttack = 12,//指前摇
IsLockingTheDoor = 13,
IsOpeningTheChest = 14,
IsClimbingThroughWindows = 15,
IsUsingSpecialSkill = 16,
Addicted = 1,
Escaped = 2,
Swinging = 3,//指后摇
Deceased = 4,
Moving = 5,
Treating = 6,
Rescuing = 7,
Fixing = 8,
Treated = 9,
Rescued = 10,
Stunned = 11,
TryingToAttack = 12,//指前摇
LockingOrOpeningTheDoor = 13,
OpeningTheChest = 14,
ClimbingThroughWindows = 15,
UsingSkill = 16,
OpeningTheDoorway = 17,
}
~~~
- 可执行指令的(不用给选手)
@@ -124,7 +125,7 @@
- double 隐蔽度
- 翻窗时间
- 开锁门速度
- *开箱速度*
- 开箱速度
- 视野范围

### 学生:人物
@@ -174,22 +175,21 @@
- 开启进度

### 紧急出口:物体
- *是否显现*
- *是否打开*
- 是否显现
- 是否打开

### 墙:物体

### 窗:物体
- *正在翻窗的人*
- 正在翻窗的人

### 箱子:物体
- *是否开启*
- *开箱进度*
- 开箱进度

### 门:物体
- *属于那个教学区*
- *是否锁上*
- *开锁门进度*
- 属于那个教学区
- 是否锁上
- 开锁门进度
- 不提供是否可以锁上的属性

### 电机(建议称为homework):物体
@@ -205,14 +205,14 @@
- 除了逃离和翻窗,交互目标与交互者在一个九宫格内则为可交互
- 逃离时学生应当与出口在同一格子内
- 翻窗时玩家应当在窗前后一个格子内
- *在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令*
- 在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令

### 破译与逃脱
- *每张地图都会刷新 9台电机,求生者需要破译其中的7台*,并开启任意一个大门后从任意一个开启的大门- 逃脱,亦或者在只剩1名求生者的情况下从紧急出口逃脱;
- 每张地图都会刷新 9台电机,求生者需要破译其中的7台,并开启任意一个大门后从任意一个开启的大门- 逃脱,亦或者在只剩1名求生者的情况下从紧急出口逃脱;
- 求生者和监管者在靠近电机时,可以看到电机的破译进度条。
- 紧急出口会在电机破译完成3台的情况下在地图的3-5个固定紧急出口刷新点之一随机刷新。
- 当求生者只剩1名时,紧急出口盖将会自动打开,该求生者可从紧急出口逃脱。
- *开启大门所需时间为18秒。*
- 开启大门所需时间为18秒。
- 大门开启的进度不清空
- 一个大门同时最多可以由一人开启

@@ -222,7 +222,7 @@
- 无论搞蛋鬼或学生,攻击时,从播放攻击动作到可以开始产生伤害的期间,称为前摇。(前摇阶段,搞蛋鬼产生通常为不可爆炸(部分搞蛋鬼能可以产生可爆炸)子弹(爆炸范围=0),[子弹大小待商榷],期间监管者攻击被打断时,子弹消失)(无论近战远程均产生子弹)
- 无论搞蛋鬼或学生,攻击后,通常会出现一段无伤害判定的攻击动作后置时间,称为后摇。击中物体时后摇更长

- *假如监管者攻击或一些监管者的特定技能击中正在交互的求生者,将使求生者眩晕*
- 假如监管者攻击或一些监管者的特定技能击中正在交互或处于攻击前后摇或使用部分技能(指PlayerState==UsingSkill)的求生者,将使求生者眩晕
1. 处于前摇或后摇
2. 治疗或解救他人
3. 修理电机
@@ -231,16 +231,16 @@
6. 开启箱子

### 门
- *门分别属于三个教学区:三教,五教,六教*
- *监管者或求生者都需要拿到对应教学区的钥匙才能打开或锁住对应的门*
- *锁门和开门都需要一定时间,进出门为正常移动过程*
- *门只有开、锁两种状态,锁住时门有碰撞体积*
- *当门所在格子内有人时,无法锁门(必须从门外锁门)*
- *锁门时其他人可以进入门所在格子,锁门过程中断*
- *钥匙只会出现在箱子中,每个教学区都有2把钥匙*
- *一扇门只允许同时一个人开锁门*
- *开锁门未完成前,门状态表现为原来的状态*
- *开锁门进度中断后清空*
- 门分别属于三个教学区:三教,五教,六教
- 监管者或求生者都需要拿到对应教学区的钥匙才能打开或锁住对应的门
- 锁门和开门都需要一定时间,进出门为正常移动过程
- 门只有开、锁两种状态,锁住时门有碰撞体积
- 当门所在格子内有人时,无法锁门(必须从门外锁门)
- 锁门时其他人可以进入门所在格子,锁门过程中断
- 钥匙只会出现在箱子中,每个教学区都有2把钥匙
- 一扇门只允许同时一个人开锁门
- 开锁门未完成前,门状态表现为原来的状态
- 开锁门进度中断后清空

### 窗
- 求生者和监管者都可以翻越窗户,但通常情况下监管者翻越窗户的速度**高于**求生者。
@@ -253,11 +253,11 @@
- 窗必须在两个墙之间,另外两侧不能为墙

### 箱子
- *监管者和求生者都能与箱子交互,同一时刻只允许一人进行开启*
- *开启箱子有不同概率获得不同道具。*
- *开启箱子的基础持续时间为10秒。*
- *未开启完成的箱子在下一次需要重新开始开启。*
- *箱子开启后其中道具才可以被观测和拿取*
- 监管者和求生者都能与箱子交互,同一时刻只允许一人进行开启
- 开启箱子有不同概率获得不同道具。
- 开启箱子的基础持续时间为10秒。
- 未开启完成的箱子在下一次需要重新开始开启。
- 箱子开启后其中道具才可以被观测和拿取
- [箱子道具不刷新]
- [箱子不可被关闭]
- [箱子内道具最多两个]
@@ -288,10 +288,59 @@

### 沉迷
- 当求生者血量归零时,求生者自动原地进入沉迷状态,每毫秒增加1沉迷度
- 在被救时沉迷度不增加
- 该求生者可由其他的求生者救下,救下后,*血量恢复至1/2并可以重新行动*。沉迷程度不清空。
- 进入沉迷状态时,如果求生者原本沉迷程度在【10003,30000】中,求生者沉迷程度直接变为30000
- 当求生者沉迷程度达到该玩家最大沉迷程度(学生有的属性)时,从游戏中出局

### 救人
- 一般情况下,救人时间为1秒。
- *不能两人同时救一个人*
- 不能两人同时救一个人

## 得分

### 屠夫

#### 伤害得分
[Tricker对Student造成伤害时,得伤害*100/基本伤害(1500000)分。]

#### *[使用道具/技能得分]*

——需要造成一定效果才能获取分数,仅使用不得分

- 不同道具/技能有不同得分

#### 沉迷
使Student进入沉迷状态时,得50分。

#### 眩晕
使人类进入眩晕状态时,得25分。

#### 淘汰
每淘汰一个Student,得1000分

#### ~~主动/被动解除特殊状态得分(如解除眩晕)~~
~~解除眩晕,得15分~~

### 人类

#### 修机得分
- 人类每修n%的电机,得n分
- 修完一台电机,额外得?分

#### [牵制得分]

#### 使用道具/技能得分
- 不同道具/技能有不同得分

#### 使屠夫进入特殊状态得分(如使之眩晕)

#### 救人

#### 治疗

#### 逃脱

#### ~~解除眩晕~~

#### ~~自愈~~

Loading…
Cancel
Save