Browse Source

fix(capi): ✏️ remove warnings from implicit double to int

tags/v0.1.0
Timothy Liu 2 years ago
parent
commit
10e589e74e
100 changed files with 5242 additions and 2731 deletions
  1. +4
    -0
      .github/workflows/upload_COS.yml
  2. +1
    -1
      CAPI/cmd/RunServer.cmd
  3. +1
    -1
      CAPI/cmd/RunServerForDebug.cmd
  4. +6
    -6
      CAPI/cpp/API/include/API.h
  5. +6
    -1
      CAPI/cpp/API/include/Communication.h
  6. +59
    -32
      CAPI/cpp/API/include/constants.h
  7. +1
    -1
      CAPI/cpp/API/include/logic.h
  8. +9
    -7
      CAPI/cpp/API/include/structures.h
  9. +9
    -6
      CAPI/cpp/API/include/utils.hpp
  10. +4
    -4
      CAPI/cpp/API/src/API.cpp
  11. +113
    -4
      CAPI/cpp/API/src/Communication.cpp
  12. +14
    -14
      CAPI/cpp/API/src/DebugAPI.cpp
  13. +95
    -71
      CAPI/cpp/API/src/logic.cpp
  14. +5
    -1
      CAPI/cpp/API/src/main.cpp
  15. +50
    -0
      CAPI/cpp/README.md
  16. +200
    -332
      CAPI/cpp/proto/Message2Clients.pb.cc
  17. +92
    -236
      CAPI/cpp/proto/Message2Clients.pb.h
  18. +38
    -10
      CAPI/cpp/proto/Message2Server.pb.cc
  19. +36
    -0
      CAPI/cpp/proto/Message2Server.pb.h
  20. +40
    -39
      CAPI/cpp/proto/MessageType.pb.cc
  21. +4
    -3
      CAPI/cpp/proto/MessageType.pb.h
  22. +2
    -0
      CAPI/cpp/proto/Services.grpc.pb.h
  23. +18
    -8
      CAPI/python/PyAPI/API.py
  24. +124
    -21
      CAPI/python/PyAPI/Communication.py
  25. +219
    -188
      CAPI/python/PyAPI/DebugAPI.py
  26. +5
    -8
      CAPI/python/PyAPI/Interface.py
  27. +1
    -0
      CAPI/python/PyAPI/State.py
  28. +115
    -90
      CAPI/python/PyAPI/constants.py
  29. +227
    -81
      CAPI/python/PyAPI/logic.py
  30. +47
    -16
      CAPI/python/PyAPI/main.py
  31. +2
    -4
      CAPI/python/PyAPI/structures.py
  32. +123
    -57
      CAPI/python/PyAPI/utils.py
  33. +2
    -2
      CAPI/python/requirements.txt
  34. +1
    -1
      CAPI/python/run.sh
  35. +1
    -1
      CAPI/shell/RunServer.sh
  36. +1
    -1
      CAPI/shell/RunServerForDebug.sh
  37. +3
    -1
      README.md
  38. +1
    -0
      dependency/Dockerfile/Dockerfile_cpp
  39. +4
    -4
      dependency/proto/Message2Clients.proto
  40. +1
    -0
      dependency/proto/Message2Server.proto
  41. +4
    -3
      dependency/proto/MessageType.proto
  42. +0
    -0
      dependency/proto/Proto.sln
  43. +2
    -2
      dependency/proto/Protos.csproj
  44. +205
    -0
      dependency/py/rank.py
  45. +11
    -2
      dependency/shell/compile.sh
  46. +5
    -6
      dependency/shell/run.sh
  47. +48
    -43
      docs/CAPI接口(cpp).md
  48. +45
    -42
      docs/CAPI接口(python).md
  49. +14
    -11
      docs/GameRules.md
  50. +6
    -3
      docs/使用文档.md
  51. +119
    -0
      docs/游戏机制与平衡性调整更新草案.md
  52. +53
    -0
      docs/版本更新说明.md
  53. +51
    -5
      installer/Installer/MainWindow.xaml
  54. +499
    -29
      installer/Installer/Model.cs
  55. +107
    -29
      installer/Installer/ViewModel.cs
  56. +1
    -1
      logic/Client/Client.csproj
  57. +319
    -201
      logic/Client/MainWindow.xaml.cs
  58. +6
    -2
      logic/Client/PlaybackClient.cs
  59. +1
    -1
      logic/Client/Properties/launchSettings.json
  60. +9
    -0
      logic/Client/StatusBarOfHunter.xaml.cs
  61. +9
    -0
      logic/Client/StatusBarOfSurvivor.xaml.cs
  62. +1
    -1
      logic/ClientTest/ClientTest.csproj
  63. +2
    -3
      logic/GameClass/GameObj/Bullet/BombedBullet.cs
  64. +60
    -55
      logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs
  65. +1
    -1
      logic/GameClass/GameObj/Bullet/Bullet.Student.cs
  66. +29
    -16
      logic/GameClass/GameObj/Bullet/Bullet.cs
  67. +13
    -0
      logic/GameClass/GameObj/Character/Character.BuffManager.cs
  68. +0
    -5
      logic/GameClass/GameObj/Character/Character.Ghost.cs
  69. +12
    -22
      logic/GameClass/GameObj/Character/Character.Skill.cs
  70. +1
    -1
      logic/GameClass/GameObj/Character/Character.Student.cs
  71. +439
    -179
      logic/GameClass/GameObj/Character/Character.cs
  72. +1
    -1
      logic/GameClass/GameObj/Character/Team.cs
  73. +15
    -47
      logic/GameClass/GameObj/GameObj.cs
  74. +18
    -0
      logic/GameClass/GameObj/Immovable.cs
  75. +7
    -9
      logic/GameClass/GameObj/Map/Chest.cs
  76. +130
    -15
      logic/GameClass/GameObj/Map/Door.cs
  77. +50
    -17
      logic/GameClass/GameObj/Map/Doorway.cs
  78. +1
    -3
      logic/GameClass/GameObj/Map/EmergencyExit.cs
  79. +1
    -3
      logic/GameClass/GameObj/Map/Generator.cs
  80. +11
    -7
      logic/GameClass/GameObj/Map/Map.cs
  81. +103
    -50
      logic/GameClass/GameObj/Map/MapInfo.cs
  82. +1
    -3
      logic/GameClass/GameObj/Map/Wall.cs
  83. +35
    -6
      logic/GameClass/GameObj/Map/Window.cs
  84. +80
    -47
      logic/GameClass/GameObj/Moveable.cs
  85. +11
    -2
      logic/GameClass/GameObj/ObjOfCharacter.cs
  86. +1
    -3
      logic/GameClass/GameObj/OutOfBoundBlock.cs
  87. +70
    -42
      logic/GameClass/GameObj/Prop/Gadget.cs
  88. +92
    -0
      logic/GameClass/GameObj/Prop/Item.cs
  89. +7
    -12
      logic/GameClass/GameObj/Prop/PickedProp.cs
  90. +120
    -119
      logic/GameEngine/MoveEngine.cs
  91. +294
    -194
      logic/Gaming/ActionManager.cs
  92. +59
    -34
      logic/Gaming/AttackManager.cs
  93. +82
    -102
      logic/Gaming/CharacterManager.cs
  94. +20
    -11
      logic/Gaming/Game.cs
  95. +19
    -22
      logic/Gaming/PropManager.cs
  96. +225
    -55
      logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs
  97. +6
    -2
      logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs
  98. +11
    -4
      logic/Gaming/SkillManager/SkillManager.cs
  99. +7
    -6
      logic/Preparation/Interface/ICharacter.cs
  100. +9
    -0
      logic/Preparation/Interface/IChest.cs

+ 4
- 0
.github/workflows/upload_COS.yml View File

@@ -78,6 +78,10 @@ jobs:
cp -r ./THUAI6/win/CAPI ./THUAI6/linux/ cp -r ./THUAI6/win/CAPI ./THUAI6/linux/
cp -r ./THUAI6/win/CAPI ./THUAI6/osx/ cp -r ./THUAI6/win/CAPI ./THUAI6/osx/


cp -r ./logic/cmd/map ./THUAI6/win/
cp -r ./logic/cmd/map ./THUAI6/linux/
cp -r ./logic/cmd/map ./THUAI6/osx/

- name: Copy shell - name: Copy shell
run: | run: |
cp -r ./CAPI/cmd/* ./THUAI6/win/ cp -r ./CAPI/cmd/* ./THUAI6/win/


+ 1
- 1
CAPI/cmd/RunServer.cmd View File

@@ -1,5 +1,5 @@
@echo off @echo off


.\win64\Server.exe --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video
.\win64\Server.exe --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video --mapResource ".\map\map1_final.txt"


pause pause

+ 1
- 1
CAPI/cmd/RunServerForDebug.cmd View File

@@ -1,5 +1,5 @@
@echo off @echo off


.\win64\Debug\Server.exe --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600
.\win64\Debug\Server.exe --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video --mapResource ".\map\map1_final.txt"


pause pause

+ 6
- 6
CAPI/cpp/API/include/API.h View File

@@ -60,7 +60,7 @@ public:
virtual bool PickProp(THUAI6::PropType prop) = 0; virtual bool PickProp(THUAI6::PropType prop) = 0;
virtual bool UseProp(THUAI6::PropType prop) = 0; virtual bool UseProp(THUAI6::PropType prop) = 0;
virtual bool ThrowProp(THUAI6::PropType prop) = 0; virtual bool ThrowProp(THUAI6::PropType prop) = 0;
virtual bool UseSkill(int32_t skillID) = 0;
virtual bool UseSkill(int32_t skillID, int32_t skillParam) = 0;
virtual bool SendMessage(int64_t toID, std::string message, bool binary) = 0; virtual bool SendMessage(int64_t toID, std::string message, bool binary) = 0;
virtual bool HaveMessage() = 0; virtual bool HaveMessage() = 0;
virtual std::pair<int64_t, std::string> GetMessage() = 0; virtual std::pair<int64_t, std::string> GetMessage() = 0;
@@ -108,7 +108,7 @@ public:
virtual std::future<bool> PickProp(THUAI6::PropType prop) = 0; virtual std::future<bool> PickProp(THUAI6::PropType prop) = 0;
virtual std::future<bool> UseProp(THUAI6::PropType prop) = 0; virtual std::future<bool> UseProp(THUAI6::PropType prop) = 0;
virtual std::future<bool> ThrowProp(THUAI6::PropType prop) = 0; virtual std::future<bool> ThrowProp(THUAI6::PropType prop) = 0;
virtual std::future<bool> UseSkill(int32_t skillID) = 0;
virtual std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) = 0;
virtual std::future<bool> Attack(double angleInRadian) = 0; virtual std::future<bool> Attack(double angleInRadian) = 0;


virtual std::future<bool> OpenDoor() = 0; virtual std::future<bool> OpenDoor() = 0;
@@ -236,7 +236,7 @@ public:
std::future<bool> PickProp(THUAI6::PropType prop) override; std::future<bool> PickProp(THUAI6::PropType prop) override;
std::future<bool> UseProp(THUAI6::PropType prop) override; std::future<bool> UseProp(THUAI6::PropType prop) override;
std::future<bool> ThrowProp(THUAI6::PropType prop) override; std::future<bool> ThrowProp(THUAI6::PropType prop) override;
std::future<bool> UseSkill(int32_t skillID) override;
std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) override;


std::future<bool> Attack(double angleInRadian) override; std::future<bool> Attack(double angleInRadian) override;


@@ -329,7 +329,7 @@ public:
std::future<bool> PickProp(THUAI6::PropType prop) override; std::future<bool> PickProp(THUAI6::PropType prop) override;
std::future<bool> UseProp(THUAI6::PropType prop) override; std::future<bool> UseProp(THUAI6::PropType prop) override;
std::future<bool> ThrowProp(THUAI6::PropType prop) override; std::future<bool> ThrowProp(THUAI6::PropType prop) override;
std::future<bool> UseSkill(int32_t skillID) override;
std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) override;


std::future<bool> OpenDoor() override; std::future<bool> OpenDoor() override;
std::future<bool> CloseDoor() override; std::future<bool> CloseDoor() override;
@@ -410,7 +410,7 @@ public:
std::future<bool> PickProp(THUAI6::PropType prop) override; std::future<bool> PickProp(THUAI6::PropType prop) override;
std::future<bool> UseProp(THUAI6::PropType prop) override; std::future<bool> UseProp(THUAI6::PropType prop) override;
std::future<bool> ThrowProp(THUAI6::PropType prop) override; std::future<bool> ThrowProp(THUAI6::PropType prop) override;
std::future<bool> UseSkill(int32_t skillID) override;
std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) override;


std::future<bool> Attack(double angleInRadian) override; std::future<bool> Attack(double angleInRadian) override;


@@ -488,7 +488,7 @@ public:
std::future<bool> PickProp(THUAI6::PropType prop) override; std::future<bool> PickProp(THUAI6::PropType prop) override;
std::future<bool> UseProp(THUAI6::PropType prop) override; std::future<bool> UseProp(THUAI6::PropType prop) override;
std::future<bool> ThrowProp(THUAI6::PropType prop) override; std::future<bool> ThrowProp(THUAI6::PropType prop) override;
std::future<bool> UseSkill(int32_t skillID) override;
std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) override;


std::future<bool> OpenDoor() override; std::future<bool> OpenDoor() override;
std::future<bool> CloseDoor() override; std::future<bool> CloseDoor() override;


+ 6
- 1
CAPI/cpp/API/include/Communication.h View File

@@ -31,7 +31,7 @@ public:
bool PickProp(THUAI6::PropType prop, int64_t playerID); bool PickProp(THUAI6::PropType prop, int64_t playerID);
bool UseProp(THUAI6::PropType prop, int64_t playerID); bool UseProp(THUAI6::PropType prop, int64_t playerID);
bool ThrowProp(THUAI6::PropType prop, int64_t playerID); bool ThrowProp(THUAI6::PropType prop, int64_t playerID);
bool UseSkill(int32_t skillID, int64_t playerID);
bool UseSkill(int32_t skillID, int32_t skillParam, int64_t playerID);
bool SendMessage(int64_t toID, std::string message, bool binary, int64_t playerID); bool SendMessage(int64_t toID, std::string message, bool binary, int64_t playerID);
bool OpenDoor(int64_t playerID); bool OpenDoor(int64_t playerID);
bool CloseDoor(int64_t playerID); bool CloseDoor(int64_t playerID);
@@ -57,6 +57,11 @@ private:
bool haveNewMessage = false; bool haveNewMessage = false;
protobuf::MessageToClient message2Client; protobuf::MessageToClient message2Client;
std::mutex mtxMessage; std::mutex mtxMessage;
std::mutex mtxLimit;
int counter;
int counterMove;
static constexpr const int limit = 50;
static constexpr const int moveLimit = 10;
std::condition_variable cvMessage; std::condition_variable cvMessage;
}; };




+ 59
- 32
CAPI/cpp/API/include/constants.h View File

@@ -32,10 +32,10 @@ namespace Constants
// 人物属性相关 // 人物属性相关
SCCI int basicEncourageSpeed = 100; SCCI int basicEncourageSpeed = 100;
SCCI int basicFixSpeed = 123; SCCI int basicFixSpeed = 123;
SCCI int basicSpeedOfOpeningOrLocking = 4000;
SCCI int basicSpeedOfOpeningOrLocking = 5000;
SCCI int basicStudentSpeedOfClimbingThroughWindows = 1222; SCCI int basicStudentSpeedOfClimbingThroughWindows = 1222;
SCCI int basicTrickerSpeedOfClimbingThroughWindows = 2540; SCCI int basicTrickerSpeedOfClimbingThroughWindows = 2540;
SCCI int basicSpeedOfOpenChest = 1000;
SCCI int basicSpeedOfOpenChest = 1250;


SCCI int basicHp = 3000000; SCCI int basicHp = 3000000;
SCCI int basicMaxGamingAddiction = 60000; SCCI int basicMaxGamingAddiction = 60000;
@@ -138,19 +138,19 @@ namespace Constants
SCCI int moveSpeed = basicStudentSpeed * 9 / 10; SCCI int moveSpeed = basicStudentSpeed * 9 / 10;
SCCI int maxHp = basicHp * 10; SCCI int maxHp = basicHp * 10;
SCCI int maxAddiction = basicMaxGamingAddiction * 10; SCCI int maxAddiction = basicMaxGamingAddiction * 10;
SCCI int fixSpeed = basicFixSpeed * 0;
SCCI int fixSpeed = (int)(basicFixSpeed * 50 / 123);
SCCI int encourageSpeed = basicEncourageSpeed * 8 / 10; SCCI int encourageSpeed = basicEncourageSpeed * 8 / 10;
SCCI double concealment = 0.5; SCCI double concealment = 0.5;
SCCI int alertnessRadius = basicStudentAlertnessRadius / 2;
SCCI int viewRange = basicStudentViewRange * 9 / 10;
SCCI int alertnessRadius = basicStudentAlertnessRadius * 2 / 3;
SCCI int viewRange = basicStudentViewRange * 8 / 10;
SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking;
SCCI int speedOfClimbingThroughWindows = basicStudentSpeedOfClimbingThroughWindows / 2;
SCCI int speedOfClimbingThroughWindows = (int)(basicStudentSpeedOfClimbingThroughWindows * 1000 / 1222);
SCCI int speedOfOpenChest = basicSpeedOfOpenChest; SCCI int speedOfOpenChest = basicSpeedOfOpenChest;
}; };


struct StraightAStudent struct StraightAStudent
{ {
SCCI int moveSpeed = basicStudentSpeed * 96 / 100;
SCCI int moveSpeed = basicStudentSpeed * 24 / 25;
SCCI int maxHp = basicHp * 11 / 10; SCCI int maxHp = basicHp * 11 / 10;
SCCI int maxAddiction = basicMaxGamingAddiction * 13 / 10; SCCI int maxAddiction = basicMaxGamingAddiction * 13 / 10;
SCCI int fixSpeed = basicFixSpeed * 11 / 10; SCCI int fixSpeed = basicFixSpeed * 11 / 10;
@@ -165,41 +165,41 @@ namespace Constants


struct Robot struct Robot
{ {
SCCI int moveSpeed = basicStudentSpeed;
SCCI int maxHp = basicHp * 2 / 5;
SCCI int moveSpeed = basicStudentSpeed * 9 / 10;
SCCI int maxHp = basicHp * 3 / 10;
SCCI int maxAddiction = basicMaxGamingAddiction * 0; SCCI int maxAddiction = basicMaxGamingAddiction * 0;
SCCI int fixSpeed = basicFixSpeed;
SCCI int fixSpeed = basicFixSpeed * 7 / 10;
SCCI int encourageSpeed = 0; SCCI int encourageSpeed = 0;
SCCI double concealment = 1;
SCCI int alertnessRadius = basicStudentAlertnessRadius * 1;
SCCI int viewRange = basicStudentViewRange;
SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking;
SCCI double concealment = 0.8;
SCCI int alertnessRadius = 0;
SCCI int viewRange = 0;
SCCI int speedOfOpeningOrLocking = 0;
SCCI int speedOfClimbingThroughWindows = 1; SCCI int speedOfClimbingThroughWindows = 1;
SCCI int speedOfOpenChest = basicSpeedOfOpenChest;
SCCI int speedOfOpenChest = basicSpeedOfOpenChest * 4 / 5;
}; };


struct TechOtaku struct TechOtaku
{ {
SCCI int moveSpeed = basicStudentSpeed * 3 / 4;
SCCI int moveSpeed = (int)(basicStudentSpeed * 0.96);
SCCI int maxHp = basicHp * 9 / 10; SCCI int maxHp = basicHp * 9 / 10;
SCCI int maxAddiction = basicMaxGamingAddiction * 11 / 10;
SCCI int fixSpeed = basicFixSpeed * 11 / 10;
SCCI int encourageSpeed = basicEncourageSpeed * 9 / 10;
SCCI double concealment = 1;
SCCI int maxAddiction = basicMaxGamingAddiction;
SCCI int fixSpeed = (int)(basicFixSpeed * 0.9);
SCCI int encourageSpeed = basicEncourageSpeed;
SCCI double concealment = 1.1;
SCCI int alertnessRadius = basicStudentAlertnessRadius; SCCI int alertnessRadius = basicStudentAlertnessRadius;
SCCI int viewRange = basicStudentViewRange * 9 / 10; SCCI int viewRange = basicStudentViewRange * 9 / 10;
SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking;
SCCI int speedOfClimbingThroughWindows = basicStudentSpeedOfClimbingThroughWindows * 3 / 4;
SCCI int speedOfOpenChest = basicSpeedOfOpenChest;
SCCI int speedOfClimbingThroughWindows = (int)(basicStudentSpeedOfClimbingThroughWindows * 0.9);
SCCI int speedOfOpenChest = basicSpeedOfOpenChest * 22 / 25;
}; };


struct Sunshine struct Sunshine
{ {
SCCI int moveSpeed = basicStudentSpeed; SCCI int moveSpeed = basicStudentSpeed;
SCCI int maxHp = basicHp * 32 / 30;
SCCI int maxHp = basicHp * 16 / 15;
SCCI int maxAddiction = basicMaxGamingAddiction * 11 / 10; SCCI int maxAddiction = basicMaxGamingAddiction * 11 / 10;
SCCI int fixSpeed = basicFixSpeed; SCCI int fixSpeed = basicFixSpeed;
SCCI int encourageSpeed = basicEncourageSpeed * 12 / 10;
SCCI int encourageSpeed = basicEncourageSpeed * 6 / 5;
SCCI double concealment = 1; SCCI double concealment = 1;
SCCI int alertnessRadius = basicStudentAlertnessRadius; SCCI int alertnessRadius = basicStudentAlertnessRadius;
SCCI int viewRange = basicStudentViewRange; SCCI int viewRange = basicStudentViewRange;
@@ -244,7 +244,19 @@ namespace Constants


struct Punish struct Punish
{ {
SCCI int skillCD = commonSkillCD * 1;
SCCI int skillCD = commonSkillCD * 3 / 2;
SCCI int durationTime = commonSkillTime * 0;
};

struct SparksNSplash
{
SCCI int skillCD = commonSkillCD * 3 / 2;
SCCI int durationTime = commonSkillTime * 1;
};

struct HaveTea
{
SCCI int skillCD = commonSkillCD * 3;
SCCI int durationTime = commonSkillTime * 0; SCCI int durationTime = commonSkillTime * 0;
}; };


@@ -292,7 +304,7 @@ namespace Constants


struct UseRobot struct UseRobot
{ {
SCCI int skillCD = commonSkillCD / 300;
SCCI int skillCD = commonSkillCD / 15;
SCCI int durationTime = commonSkillTime * 0; SCCI int durationTime = commonSkillTime * 0;
}; };


@@ -304,8 +316,8 @@ namespace Constants


struct SummonGolem struct SummonGolem
{ {
SCCI int skillCD = commonSkillCD * 1;
SCCI int durationTime = commonSkillTime * 0;
SCCI int skillCD = commonSkillCD * 4 / 3;
SCCI int durationTime = commonSkillTime * 6;
}; };


struct CommonAttackOfTricker struct CommonAttackOfTricker
@@ -331,10 +343,10 @@ namespace Constants
SCCI int Speed = basicBulletMoveSpeed * 25 / 10; SCCI int Speed = basicBulletMoveSpeed * 25 / 10;
SCCI bool IsRemoteAttack = true; SCCI bool IsRemoteAttack = true;


SCCI int CastTime = basicCastTime * 4 / 5;
SCCI int CastTime = basicCastTime * 6 / 5;
SCCI int Backswing = 0; SCCI int Backswing = 0;
SCCI int RecoveryFromHit = 0; SCCI int RecoveryFromHit = 0;
SCCI int cd = basicBackswing / 2;
SCCI int cd = basicBackswing * 3 / 4;
SCCI int maxBulletNum = 1; SCCI int maxBulletNum = 1;
}; };


@@ -347,7 +359,7 @@ namespace Constants
SCCI bool IsRemoteAttack = false; SCCI bool IsRemoteAttack = false;


SCCI int CastTime = (int)BulletAttackRange * 1000 / Speed; SCCI int CastTime = (int)BulletAttackRange * 1000 / Speed;
SCCI int Backswing = basicRecoveryFromHit;
SCCI int Backswing = basicBackswing * 3 / 2;
SCCI int RecoveryFromHit = basicRecoveryFromHit; SCCI int RecoveryFromHit = basicRecoveryFromHit;
SCCI int cd = basicCD; SCCI int cd = basicCD;
SCCI int maxBulletNum = 1; SCCI int maxBulletNum = 1;
@@ -356,10 +368,25 @@ namespace Constants
struct JumpyDumpty struct JumpyDumpty
{ {
SCCI double BulletBombRange = basicBulletBombRange / 2; SCCI double BulletBombRange = basicBulletBombRange / 2;
SCCI double BulletAttackRange = basicRemoteAttackRange * 2;
SCCI double BulletAttackRange = basicAttackShortRange * 16 / 22;
SCCI int ap = (int)(basicApOfTricker * 0.6); SCCI int ap = (int)(basicApOfTricker * 0.6);
SCCI int Speed = basicBulletMoveSpeed * 43 / 37; SCCI int Speed = basicBulletMoveSpeed * 43 / 37;
SCCI bool IsRemoteAttack = false; SCCI bool IsRemoteAttack = false;
}; };

struct Strike
{
SCCI double BulletBombRange = 0;
SCCI double BulletAttackRange = basicAttackShortRange;
SCCI int ap = basicApOfTricker * 16 / 15;
SCCI int Speed = basicBulletMoveSpeed * 125 / 148;
SCCI bool IsRemoteAttack = false;

SCCI int CastTime = basicCastTime * 16 / 25;
SCCI int Backswing = basicBackswing;
SCCI int RecoveryFromHit = basicRecoveryFromHit;
SCCI int cd = basicBackswing;
SCCI int maxBulletNum = 1;
};
} // namespace Constants } // namespace Constants
#endif #endif

+ 1
- 1
CAPI/cpp/API/include/logic.h View File

@@ -116,7 +116,7 @@ private:
bool PickProp(THUAI6::PropType prop) override; bool PickProp(THUAI6::PropType prop) override;
bool UseProp(THUAI6::PropType prop) override; bool UseProp(THUAI6::PropType prop) override;
bool ThrowProp(THUAI6::PropType prop) override; bool ThrowProp(THUAI6::PropType prop) override;
bool UseSkill(int32_t skillID) override;
bool UseSkill(int32_t skillID, int32_t skillParam) override;


bool SendMessage(int64_t toID, std::string message, bool binary) override; bool SendMessage(int64_t toID, std::string message, bool binary) override;
bool HaveMessage() override; bool HaveMessage() override;


+ 9
- 7
CAPI/cpp/API/include/structures.h View File

@@ -62,6 +62,7 @@ namespace THUAI6
AddHpOrAp = 6, AddHpOrAp = 6,
ShieldOrSpear = 7, ShieldOrSpear = 7,
RecoveryFromDizziness = 8, RecoveryFromDizziness = 8,
CraftingBench = 9,
}; };


enum class BulletType : unsigned char enum class BulletType : unsigned char
@@ -71,7 +72,7 @@ namespace THUAI6
CommonAttackOfTricker = 2, CommonAttackOfTricker = 2,
BombBomb = 3, BombBomb = 3,
JumpyDumpty = 4, JumpyDumpty = 4,
AtomBomb = 5,
Strike = 5,
}; };


// 玩家类型 // 玩家类型
@@ -197,7 +198,7 @@ namespace THUAI6


PlayerType playerType; // 玩家类型 PlayerType playerType; // 玩家类型
std::vector<PropType> props; std::vector<PropType> props;
PlaceType place; // 所处格子的类型
// PlaceType place; // 所处格子的类型
BulletType bulletType; BulletType bulletType;


PlayerState playerState; PlayerState playerState;
@@ -232,9 +233,9 @@ namespace THUAI6
double facingDirection; // 朝向 double facingDirection; // 朝向
int64_t guid; // 全局唯一ID int64_t guid; // 全局唯一ID
PlayerType team; // 子弹所属队伍 PlayerType team; // 子弹所属队伍
PlaceType place; // 所处格子的类型
double bombRange; // 炸弹爆炸范围
int32_t speed; // 子弹速度
// PlaceType place; // 所处格子的类型
double bombRange; // 炸弹爆炸范围
int32_t speed; // 子弹速度
}; };


struct BombedBullet struct BombedBullet
@@ -253,7 +254,7 @@ namespace THUAI6
int32_t y; int32_t y;
int64_t guid; int64_t guid;
PropType type; PropType type;
PlaceType place;
// PlaceType place;
double facingDirection; // 朝向 double facingDirection; // 朝向
}; };


@@ -363,6 +364,7 @@ namespace THUAI6
{PropType::AddHpOrAp, "AddHpOrAp"}, {PropType::AddHpOrAp, "AddHpOrAp"},
{PropType::ShieldOrSpear, "ShieldOrSpear"}, {PropType::ShieldOrSpear, "ShieldOrSpear"},
{PropType::RecoveryFromDizziness, "RecoveryFromDizziness"}, {PropType::RecoveryFromDizziness, "RecoveryFromDizziness"},
{PropType::CraftingBench, "CraftingBench"},


}; };


@@ -372,7 +374,7 @@ namespace THUAI6
{BulletType::CommonAttackOfTricker, "CommonAttackOfTricker"}, {BulletType::CommonAttackOfTricker, "CommonAttackOfTricker"},
{BulletType::BombBomb, "BombBomb"}, {BulletType::BombBomb, "BombBomb"},
{BulletType::JumpyDumpty, "JumpyDumpty"}, {BulletType::JumpyDumpty, "JumpyDumpty"},
{BulletType::AtomBomb, "AtomBomb"},
{BulletType::Strike, "Strike"},
}; };


inline std::map<StudentBuffType, std::string> studentBuffDict{ inline std::map<StudentBuffType, std::string> studentBuffDict{


+ 9
- 6
CAPI/cpp/API/include/utils.hpp View File

@@ -108,6 +108,7 @@ namespace Proto2THUAI6
{protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE, THUAI6::PropType::AddLifeOrClairaudience}, {protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE, THUAI6::PropType::AddLifeOrClairaudience},
{protobuf::PropType::SHIELD_OR_SPEAR, THUAI6::PropType::ShieldOrSpear}, {protobuf::PropType::SHIELD_OR_SPEAR, THUAI6::PropType::ShieldOrSpear},
{protobuf::PropType::RECOVERY_FROM_DIZZINESS, THUAI6::PropType::RecoveryFromDizziness}, {protobuf::PropType::RECOVERY_FROM_DIZZINESS, THUAI6::PropType::RecoveryFromDizziness},
{protobuf::PropType::CRAFTING_BENCH, THUAI6::PropType::CraftingBench},
}; };


inline std::map<protobuf::PlayerType, THUAI6::PlayerType> playerTypeDict{ inline std::map<protobuf::PlayerType, THUAI6::PlayerType> playerTypeDict{
@@ -186,7 +187,7 @@ namespace Proto2THUAI6
{protobuf::BulletType::COMMON_ATTACK_OF_TRICKER, THUAI6::BulletType::CommonAttackOfTricker}, {protobuf::BulletType::COMMON_ATTACK_OF_TRICKER, THUAI6::BulletType::CommonAttackOfTricker},
{protobuf::BulletType::BOMB_BOMB, THUAI6::BulletType::BombBomb}, {protobuf::BulletType::BOMB_BOMB, THUAI6::BulletType::BombBomb},
{protobuf::BulletType::JUMPY_DUMPTY, THUAI6::BulletType::JumpyDumpty}, {protobuf::BulletType::JUMPY_DUMPTY, THUAI6::BulletType::JumpyDumpty},
{protobuf::BulletType::ATOM_BOMB, THUAI6::BulletType::AtomBomb},
{protobuf::BulletType::STRIKE, THUAI6::BulletType::Strike},
}; };


inline std::map<protobuf::MessageOfObj::MessageOfObjCase, THUAI6::MessageOfObj> messageOfObjDict{ inline std::map<protobuf::MessageOfObj::MessageOfObjCase, THUAI6::MessageOfObj> messageOfObjDict{
@@ -227,7 +228,7 @@ namespace Proto2THUAI6
tricker->timeUntilSkillAvailable.clear(); tricker->timeUntilSkillAvailable.clear();
for (int i = 0; i < trickerMsg.time_until_skill_available().size(); i++) for (int i = 0; i < trickerMsg.time_until_skill_available().size(); i++)
tricker->timeUntilSkillAvailable.push_back(trickerMsg.time_until_skill_available(i)); tricker->timeUntilSkillAvailable.push_back(trickerMsg.time_until_skill_available(i));
tricker->place = placeTypeDict[trickerMsg.place()];
// tricker->place = placeTypeDict[trickerMsg.place()];
tricker->playerState = playerStateDict[trickerMsg.player_state()]; tricker->playerState = playerStateDict[trickerMsg.player_state()];
tricker->props.clear(); tricker->props.clear();
for (int i = 0; i < trickerMsg.prop().size(); i++) for (int i = 0; i < trickerMsg.prop().size(); i++)
@@ -269,7 +270,7 @@ namespace Proto2THUAI6
student->props.clear(); student->props.clear();
for (int i = 0; i < studentMsg.prop().size(); i++) for (int i = 0; i < studentMsg.prop().size(); i++)
student->props.push_back(propTypeDict[studentMsg.prop(i)]); student->props.push_back(propTypeDict[studentMsg.prop(i)]);
student->place = placeTypeDict[studentMsg.place()];
// student->place = placeTypeDict[studentMsg.place()];
student->playerState = playerStateDict[studentMsg.player_state()]; student->playerState = playerStateDict[studentMsg.player_state()];
student->determination = studentMsg.determination(); student->determination = studentMsg.determination();
student->addiction = studentMsg.addiction(); student->addiction = studentMsg.addiction();
@@ -286,7 +287,7 @@ namespace Proto2THUAI6
prop->x = propMsg.x(); prop->x = propMsg.x();
prop->y = propMsg.y(); prop->y = propMsg.y();
prop->type = propTypeDict[propMsg.type()]; prop->type = propTypeDict[propMsg.type()];
prop->place = placeTypeDict[propMsg.place()];
// prop->place = placeTypeDict[propMsg.place()];
prop->guid = propMsg.guid(); prop->guid = propMsg.guid();
prop->facingDirection = propMsg.facing_direction(); prop->facingDirection = propMsg.facing_direction();
return prop; return prop;
@@ -313,7 +314,7 @@ namespace Proto2THUAI6
bullet->facingDirection = bulletMsg.facing_direction(); bullet->facingDirection = bulletMsg.facing_direction();
bullet->guid = bulletMsg.guid(); bullet->guid = bulletMsg.guid();
bullet->team = playerTypeDict[bulletMsg.team()]; bullet->team = playerTypeDict[bulletMsg.team()];
bullet->place = placeTypeDict[bulletMsg.place()];
// bullet->place = placeTypeDict[bulletMsg.place()];
bullet->bombRange = bulletMsg.bomb_range(); bullet->bombRange = bulletMsg.bomb_range();
bullet->speed = bulletMsg.speed(); bullet->speed = bulletMsg.speed();
return bullet; return bullet;
@@ -373,6 +374,7 @@ namespace THUAI62Proto
{THUAI6::PropType::AddLifeOrClairaudience, protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE}, {THUAI6::PropType::AddLifeOrClairaudience, protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE},
{THUAI6::PropType::AddSpeed, protobuf::PropType::ADD_SPEED}, {THUAI6::PropType::AddSpeed, protobuf::PropType::ADD_SPEED},
{THUAI6::PropType::ShieldOrSpear, protobuf::PropType::SHIELD_OR_SPEAR}, {THUAI6::PropType::ShieldOrSpear, protobuf::PropType::SHIELD_OR_SPEAR},
{THUAI6::PropType::CraftingBench, protobuf::PropType::CRAFTING_BENCH},
}; };


inline std::map<THUAI6::PlayerType, protobuf::PlayerType> playerTypeDict{ inline std::map<THUAI6::PlayerType, protobuf::PlayerType> playerTypeDict{
@@ -486,11 +488,12 @@ namespace THUAI62Proto
return attackMsg; return attackMsg;
} }


inline protobuf::SkillMsg THUAI62ProtobufSkill(int32_t skillID, int64_t id)
inline protobuf::SkillMsg THUAI62ProtobufSkill(int32_t skillID, int32_t skillParam, int64_t id)
{ {
protobuf::SkillMsg skillMsg; protobuf::SkillMsg skillMsg;
skillMsg.set_skill_id(skillID); skillMsg.set_skill_id(skillID);
skillMsg.set_player_id(id); skillMsg.set_player_id(id);
skillMsg.set_skill_param(skillParam);
return skillMsg; return skillMsg;
} }
} // namespace THUAI62Proto } // namespace THUAI62Proto


+ 4
- 4
CAPI/cpp/API/src/API.cpp View File

@@ -106,16 +106,16 @@ std::future<bool> TrickerAPI::ThrowProp(THUAI6::PropType prop)
{ return logic.ThrowProp(prop); }); { return logic.ThrowProp(prop); });
} }


std::future<bool> StudentAPI::UseSkill(int32_t skillID)
std::future<bool> StudentAPI::UseSkill(int32_t skillID, int32_t skillParam)
{ {
return std::async(std::launch::async, [=]() return std::async(std::launch::async, [=]()
{ return logic.UseSkill(skillID); });
{ return logic.UseSkill(skillID, skillParam); });
} }


std::future<bool> TrickerAPI::UseSkill(int32_t skillID)
std::future<bool> TrickerAPI::UseSkill(int32_t skillID, int32_t skillParam)
{ {
return std::async(std::launch::async, [=]() return std::async(std::launch::async, [=]()
{ return logic.UseSkill(skillID); });
{ return logic.UseSkill(skillID, skillParam); });
} }


std::future<bool> StudentAPI::OpenDoor() std::future<bool> StudentAPI::OpenDoor()


+ 113
- 4
CAPI/cpp/API/src/Communication.cpp View File

@@ -20,6 +20,13 @@ Communication::Communication(std::string sIP, std::string sPort)


bool Communication::Move(int64_t time, double angle, int64_t playerID) bool Communication::Move(int64_t time, double angle, int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit || counterMove >= moveLimit)
return false;
counter++;
counterMove++;
}
protobuf::MoveRes moveResult; protobuf::MoveRes moveResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufMove(time, angle, playerID); auto request = THUAI62Proto::THUAI62ProtobufMove(time, angle, playerID);
@@ -32,6 +39,12 @@ bool Communication::Move(int64_t time, double angle, int64_t playerID)


bool Communication::PickProp(THUAI6::PropType prop, int64_t playerID) bool Communication::PickProp(THUAI6::PropType prop, int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes pickPropResult; protobuf::BoolRes pickPropResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID); auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID);
@@ -44,6 +57,12 @@ bool Communication::PickProp(THUAI6::PropType prop, int64_t playerID)


bool Communication::UseProp(THUAI6::PropType prop, int64_t playerID) bool Communication::UseProp(THUAI6::PropType prop, int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes usePropResult; protobuf::BoolRes usePropResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID); auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID);
@@ -56,6 +75,12 @@ bool Communication::UseProp(THUAI6::PropType prop, int64_t playerID)


bool Communication::ThrowProp(THUAI6::PropType prop, int64_t playerID) bool Communication::ThrowProp(THUAI6::PropType prop, int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes throwPropResult; protobuf::BoolRes throwPropResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID); auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID);
@@ -66,11 +91,17 @@ bool Communication::ThrowProp(THUAI6::PropType prop, int64_t playerID)
return false; return false;
} }


bool Communication::UseSkill(int32_t skillID, int64_t playerID)
bool Communication::UseSkill(int32_t skillID, int32_t skillParam, int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes useSkillResult; protobuf::BoolRes useSkillResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufSkill(skillID, playerID);
auto request = THUAI62Proto::THUAI62ProtobufSkill(skillID, skillParam, playerID);
auto status = THUAI6Stub->UseSkill(&context, request, &useSkillResult); auto status = THUAI6Stub->UseSkill(&context, request, &useSkillResult);
if (status.ok()) if (status.ok())
return useSkillResult.act_success(); return useSkillResult.act_success();
@@ -80,6 +111,12 @@ bool Communication::UseSkill(int32_t skillID, int64_t playerID)


bool Communication::SendMessage(int64_t toID, std::string message, bool binary, int64_t playerID) bool Communication::SendMessage(int64_t toID, std::string message, bool binary, int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes sendMessageResult; protobuf::BoolRes sendMessageResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufSend(std::move(message), toID, binary, playerID); auto request = THUAI62Proto::THUAI62ProtobufSend(std::move(message), toID, binary, playerID);
@@ -92,6 +129,12 @@ bool Communication::SendMessage(int64_t toID, std::string message, bool binary,


bool Communication::OpenDoor(int64_t playerID) bool Communication::OpenDoor(int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes openDoorResult; protobuf::BoolRes openDoorResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
@@ -104,6 +147,12 @@ bool Communication::OpenDoor(int64_t playerID)


bool Communication::CloseDoor(int64_t playerID) bool Communication::CloseDoor(int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes closeDoorResult; protobuf::BoolRes closeDoorResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
@@ -116,6 +165,12 @@ bool Communication::CloseDoor(int64_t playerID)


bool Communication::SkipWindow(int64_t playerID) bool Communication::SkipWindow(int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes skipWindowResult; protobuf::BoolRes skipWindowResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
@@ -128,6 +183,12 @@ bool Communication::SkipWindow(int64_t playerID)


bool Communication::StartOpenGate(int64_t playerID) bool Communication::StartOpenGate(int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes startOpenGateResult; protobuf::BoolRes startOpenGateResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
@@ -140,6 +201,12 @@ bool Communication::StartOpenGate(int64_t playerID)


bool Communication::StartOpenChest(int64_t playerID) bool Communication::StartOpenChest(int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes startOpenChestResult; protobuf::BoolRes startOpenChestResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
@@ -152,6 +219,13 @@ bool Communication::StartOpenChest(int64_t playerID)


bool Communication::EndAllAction(int64_t playerID) bool Communication::EndAllAction(int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit || counterMove >= moveLimit)
return false;
counter++;
counterMove++;
}
protobuf::BoolRes endAllActionResult; protobuf::BoolRes endAllActionResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
@@ -164,6 +238,12 @@ bool Communication::EndAllAction(int64_t playerID)


bool Communication::Graduate(int64_t playerID) bool Communication::Graduate(int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes graduateResult; protobuf::BoolRes graduateResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
@@ -176,6 +256,12 @@ bool Communication::Graduate(int64_t playerID)


bool Communication::StartLearning(int64_t playerID) bool Communication::StartLearning(int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes startLearningResult; protobuf::BoolRes startLearningResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
@@ -188,6 +274,12 @@ bool Communication::StartLearning(int64_t playerID)


bool Communication::StartRouseMate(int64_t playerID, int64_t mateID) bool Communication::StartRouseMate(int64_t playerID, int64_t mateID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes saveStudentResult; protobuf::BoolRes saveStudentResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufTreatAndRescue(playerID, mateID); auto request = THUAI62Proto::THUAI62ProtobufTreatAndRescue(playerID, mateID);
@@ -200,6 +292,12 @@ bool Communication::StartRouseMate(int64_t playerID, int64_t mateID)


bool Communication::StartEncourageMate(int64_t playerID, int64_t mateID) bool Communication::StartEncourageMate(int64_t playerID, int64_t mateID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes healStudentResult; protobuf::BoolRes healStudentResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufTreatAndRescue(playerID, mateID); auto request = THUAI62Proto::THUAI62ProtobufTreatAndRescue(playerID, mateID);
@@ -212,6 +310,12 @@ bool Communication::StartEncourageMate(int64_t playerID, int64_t mateID)


bool Communication::Attack(double angle, int64_t playerID) bool Communication::Attack(double angle, int64_t playerID)
{ {
{
std::lock_guard<std::mutex> lock(mtxLimit);
if (counter >= limit)
return false;
counter++;
}
protobuf::BoolRes attackResult; protobuf::BoolRes attackResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufAttack(angle, playerID); auto request = THUAI62Proto::THUAI62ProtobufAttack(angle, playerID);
@@ -229,9 +333,7 @@ bool Communication::TryConnection(int64_t playerID)
auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto request = THUAI62Proto::THUAI62ProtobufID(playerID);
auto status = THUAI6Stub->TryConnection(&context, request, &reply); auto status = THUAI6Stub->TryConnection(&context, request, &reply);
if (status.ok()) if (status.ok())
{
return true; return true;
}
else else
return false; return false;
} }
@@ -254,6 +356,8 @@ void Communication::AddPlayer(int64_t playerID, THUAI6::PlayerType playerType, T
auto MessageReader = THUAI6Stub->AddPlayer(&context, playerMsg); auto MessageReader = THUAI6Stub->AddPlayer(&context, playerMsg);


protobuf::MessageToClient buffer2Client; protobuf::MessageToClient buffer2Client;
counter = 0;
counterMove = 0;


while (MessageReader->Read(&buffer2Client)) while (MessageReader->Read(&buffer2Client))
{ {
@@ -261,6 +365,11 @@ void Communication::AddPlayer(int64_t playerID, THUAI6::PlayerType playerType, T
std::lock_guard<std::mutex> lock(mtxMessage); std::lock_guard<std::mutex> lock(mtxMessage);
message2Client = std::move(buffer2Client); message2Client = std::move(buffer2Client);
haveNewMessage = true; haveNewMessage = true;
{
std::lock_guard<std::mutex> lock(mtxLimit);
counter = 0;
counterMove = 0;
}
} }
cvMessage.notify_one(); cvMessage.notify_one();
} }


+ 14
- 14
CAPI/cpp/API/src/DebugAPI.cpp View File

@@ -212,21 +212,21 @@ std::future<bool> TrickerDebugAPI::ThrowProp(THUAI6::PropType prop)
return result; }); return result; });
} }


std::future<bool> StudentDebugAPI::UseSkill(int32_t skillID)
std::future<bool> StudentDebugAPI::UseSkill(int32_t skillID, int32_t skillParam)
{ {
logger->info("UseSkill: skillID={}, called at {}ms", skillID, Time::TimeSinceStart(startPoint));
logger->info("UseSkill: skillID={}, skillParam={}, called at {}ms", skillID, skillParam, Time::TimeSinceStart(startPoint));
return std::async(std::launch::async, [=]() return std::async(std::launch::async, [=]()
{ auto result = logic.UseSkill(skillID);
{ auto result = logic.UseSkill(skillID, skillParam);
if (!result) if (!result)
logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint)); logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint));
return result; }); return result; });
} }


std::future<bool> TrickerDebugAPI::UseSkill(int32_t skillID)
std::future<bool> TrickerDebugAPI::UseSkill(int32_t skillID, int32_t skillParam)
{ {
logger->info("UseSkill: skillID={}, called at {}ms", skillID, Time::TimeSinceStart(startPoint));
logger->info("UseSkill: skillID={}, skillParam={}, called at {}ms", skillID, skillParam, Time::TimeSinceStart(startPoint));
return std::async(std::launch::async, [=]() return std::async(std::launch::async, [=]()
{ auto result = logic.UseSkill(skillID);
{ auto result = logic.UseSkill(skillID, skillParam);
if (!result) if (!result)
logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint)); logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint));
return result; }); return result; });
@@ -684,7 +684,7 @@ void StudentDebugAPI::PrintStudent() const
{ {
logger->info("******Student Info******"); logger->info("******Student Info******");
logger->info("playerID={}, GUID={}, x={}, y={}", student->playerID, student->guid, student->x, student->y); logger->info("playerID={}, GUID={}, x={}, y={}", student->playerID, student->guid, student->x, student->y);
logger->info("speed={}, view range={}, place={}, radius={}", student->speed, student->viewRange, THUAI6::placeTypeDict[student->place], student->radius);
logger->info("speed={}, view range={}, radius={}", student->speed, student->viewRange, student->radius);
std::string skillTime = ""; std::string skillTime = "";
for (const auto& time : student->timeUntilSkillAvailable) for (const auto& time : student->timeUntilSkillAvailable)
skillTime += std::to_string(time) + ", "; skillTime += std::to_string(time) + ", ";
@@ -709,7 +709,7 @@ void TrickerDebugAPI::PrintStudent() const
{ {
logger->info("******Student Info******"); logger->info("******Student Info******");
logger->info("playerID={}, GUID={}, x={}, y={}", student->playerID, student->guid, student->x, student->y); logger->info("playerID={}, GUID={}, x={}, y={}", student->playerID, student->guid, student->x, student->y);
logger->info("speed={}, view range={}, place={}, radius={}", student->speed, student->viewRange, THUAI6::placeTypeDict[student->place], student->radius);
logger->info("speed={}, view range={}, radius={}", student->speed, student->viewRange, student->radius);
std::string skillTime = ""; std::string skillTime = "";
for (const auto& time : student->timeUntilSkillAvailable) for (const auto& time : student->timeUntilSkillAvailable)
skillTime += std::to_string(time) + ", "; skillTime += std::to_string(time) + ", ";
@@ -734,7 +734,7 @@ void StudentDebugAPI::PrintTricker() const
{ {
logger->info("******Tricker Info******"); logger->info("******Tricker Info******");
logger->info("playerID={}, GUID={}, x={}, y={}", tricker->playerID, tricker->guid, tricker->x, tricker->y); logger->info("playerID={}, GUID={}, x={}, y={}", tricker->playerID, tricker->guid, tricker->x, tricker->y);
logger->info("speed={}, view range={}, place={}, radius={}", tricker->speed, tricker->viewRange, THUAI6::placeTypeDict[tricker->place], tricker->radius);
logger->info("speed={}, view range={}, radius={}", tricker->speed, tricker->viewRange, tricker->radius);
std::string skillTime = ""; std::string skillTime = "";
for (const auto& time : tricker->timeUntilSkillAvailable) for (const auto& time : tricker->timeUntilSkillAvailable)
skillTime += std::to_string(time) + ", "; skillTime += std::to_string(time) + ", ";
@@ -758,7 +758,7 @@ void TrickerDebugAPI::PrintTricker() const
{ {
logger->info("******Tricker Info******"); logger->info("******Tricker Info******");
logger->info("playerID={}, GUID={}, x={}, y={}", tricker->playerID, tricker->guid, tricker->x, tricker->y); logger->info("playerID={}, GUID={}, x={}, y={}", tricker->playerID, tricker->guid, tricker->x, tricker->y);
logger->info("speed={}, view range={}, place={}, radius={}", tricker->speed, tricker->viewRange, THUAI6::placeTypeDict[tricker->place], tricker->radius);
logger->info("speed={}, view range={}, radius={}", tricker->speed, tricker->viewRange, tricker->radius);
std::string skillTime = ""; std::string skillTime = "";
for (const auto& time : tricker->timeUntilSkillAvailable) for (const auto& time : tricker->timeUntilSkillAvailable)
skillTime += std::to_string(time) + ", "; skillTime += std::to_string(time) + ", ";
@@ -781,7 +781,7 @@ void StudentDebugAPI::PrintProp() const
for (auto prop : logic.GetProps()) for (auto prop : logic.GetProps())
{ {
logger->info("******Prop Info******"); logger->info("******Prop Info******");
logger->info("GUID={}, x={}, y={}, place={}, facing direction={}", prop->guid, prop->x, prop->y, THUAI6::placeTypeDict[prop->place], prop->facingDirection);
logger->info("GUID={}, x={}, y={}, facing direction={}", prop->guid, prop->x, prop->y, prop->facingDirection);
logger->info("*********************\n"); logger->info("*********************\n");
} }
} }
@@ -791,7 +791,7 @@ void TrickerDebugAPI::PrintProp() const
for (auto prop : logic.GetProps()) for (auto prop : logic.GetProps())
{ {
logger->info("******Prop Info******"); logger->info("******Prop Info******");
logger->info("GUID={}, x={}, y={}, place={}, facing direction={}", prop->guid, prop->x, prop->y, THUAI6::placeTypeDict[prop->place], prop->facingDirection);
logger->info("GUID={}, x={}, y={}, facing direction={}", prop->guid, prop->x, prop->y, prop->facingDirection);
logger->info("*********************\n"); logger->info("*********************\n");
} }
} }
@@ -801,7 +801,7 @@ void StudentDebugAPI::PrintSelfInfo() const
auto student = logic.StudentGetSelfInfo(); auto student = logic.StudentGetSelfInfo();
logger->info("******Self Info******"); logger->info("******Self Info******");
logger->info("playerID={}, GUID={}, x={}, y={}", student->playerID, student->guid, student->x, student->y); logger->info("playerID={}, GUID={}, x={}, y={}", student->playerID, student->guid, student->x, student->y);
logger->info("speed={}, view range={}, place={}, radius={}", student->speed, student->viewRange, THUAI6::placeTypeDict[student->place], student->radius);
logger->info("speed={}, view range={}, radius={}", student->speed, student->viewRange, student->radius);
std::string skillTime = ""; std::string skillTime = "";
for (const auto& time : student->timeUntilSkillAvailable) for (const auto& time : student->timeUntilSkillAvailable)
skillTime += std::to_string(time) + ", "; skillTime += std::to_string(time) + ", ";
@@ -824,7 +824,7 @@ void TrickerDebugAPI::PrintSelfInfo() const
auto tricker = logic.TrickerGetSelfInfo(); auto tricker = logic.TrickerGetSelfInfo();
logger->info("******Self Info******"); logger->info("******Self Info******");
logger->info("playerID={}, GUID={}, x={}, y={}", tricker->playerID, tricker->guid, tricker->x, tricker->y); logger->info("playerID={}, GUID={}, x={}, y={}", tricker->playerID, tricker->guid, tricker->x, tricker->y);
logger->info("speed={}, view range={}, place={}, radius={}", tricker->speed, tricker->viewRange, THUAI6::placeTypeDict[tricker->place], tricker->radius);
logger->info("speed={}, view range={}, radius={}", tricker->speed, tricker->viewRange, tricker->radius);
std::string skillTime = ""; std::string skillTime = "";
for (const auto& time : tricker->timeUntilSkillAvailable) for (const auto& time : tricker->timeUntilSkillAvailable)
skillTime += std::to_string(time) + ", "; skillTime += std::to_string(time) + ", ";


+ 95
- 71
CAPI/cpp/API/src/logic.cpp View File

@@ -208,10 +208,10 @@ bool Logic::ThrowProp(THUAI6::PropType prop)
return pComm->ThrowProp(prop, playerID); return pComm->ThrowProp(prop, playerID);
} }


bool Logic::UseSkill(int32_t skill)
bool Logic::UseSkill(int32_t skill, int32_t skillParam)
{ {
logger->debug("Called UseSkill"); logger->debug("Called UseSkill");
return pComm->UseSkill(skill, playerID);
return pComm->UseSkill(skill, skillParam, playerID);
} }


bool Logic::SendMessage(int64_t toID, std::string message, bool binary) bool Logic::SendMessage(int64_t toID, std::string message, bool binary)
@@ -315,68 +315,81 @@ void Logic::ProcessMessage()
{ {
auto messageThread = [this]() auto messageThread = [this]()
{ {
logger->info("Message thread start!");
pComm->AddPlayer(playerID, playerType, studentType, trickerType);
while (gameState != THUAI6::GameState::GameEnd)
try
{ {
auto clientMsg = pComm->GetMessage2Client(); // 在获得新消息之前阻塞
logger->debug("Get message from server!");
gameState = Proto2THUAI6::gameStateDict[clientMsg.game_state()];
switch (gameState)
logger->info("Message thread start!");
pComm->AddPlayer(playerID, playerType, studentType, trickerType);
while (gameState != THUAI6::GameState::GameEnd)
{ {
case THUAI6::GameState::GameStart:
logger->info("Game Start!");
auto clientMsg = pComm->GetMessage2Client(); // 在获得新消息之前阻塞
logger->debug("Get message from server!");
gameState = Proto2THUAI6::gameStateDict[clientMsg.game_state()];
switch (gameState)
{
case THUAI6::GameState::GameStart:
logger->info("Game Start!");


// 读取地图
for (const auto& item : clientMsg.obj_message())
if (Proto2THUAI6::messageOfObjDict[item.message_of_obj_case()] == THUAI6::MessageOfObj::MapMessage)
{
auto map = std::vector<std::vector<THUAI6::PlaceType>>();
auto mapResult = item.map_message();
for (int i = 0; i < item.map_message().row_size(); i++)
// 读取地图
for (const auto& item : clientMsg.obj_message())
if (Proto2THUAI6::messageOfObjDict[item.message_of_obj_case()] == THUAI6::MessageOfObj::MapMessage)
{ {
std::vector<THUAI6::PlaceType> row;
for (int j = 0; j < mapResult.row(i).col_size(); j++)
auto map = std::vector<std::vector<THUAI6::PlaceType>>();
auto mapResult = item.map_message();
for (int i = 0; i < item.map_message().row_size(); i++)
{ {
if (Proto2THUAI6::placeTypeDict.count(mapResult.row(i).col(j)) == 0)
logger->error("Unknown place type!");
row.push_back(Proto2THUAI6::placeTypeDict[mapResult.row(i).col(j)]);
std::vector<THUAI6::PlaceType> row;
for (int j = 0; j < mapResult.row(i).col_size(); j++)
{
if (Proto2THUAI6::placeTypeDict.count(mapResult.row(i).col(j)) == 0)
logger->error("Unknown place type!");
row.push_back(Proto2THUAI6::placeTypeDict[mapResult.row(i).col(j)]);
}
map.push_back(std::move(row));
} }
map.push_back(std::move(row));
bufferState->gameMap = std::move(map);
currentState->gameMap = bufferState->gameMap;
logger->info("Map loaded!");
break;
} }
bufferState->gameMap = std::move(map);
currentState->gameMap = bufferState->gameMap;
logger->info("Map loaded!");
break;
if (currentState->gameMap.empty())
{
logger->error("Map not loaded!");
throw std::runtime_error("Map not loaded!");
} }
if (currentState->gameMap.empty())
{
logger->error("Map not loaded!");
throw std::runtime_error("Map not loaded!");
}
LoadBuffer(clientMsg);
LoadBuffer(clientMsg);


AILoop = true;
UnBlockAI();
AILoop = true;
UnBlockAI();


break;
case THUAI6::GameState::GameRunning:
break;
case THUAI6::GameState::GameRunning:


LoadBuffer(clientMsg);
break;
default:
logger->debug("Unknown GameState!");
break;
LoadBuffer(clientMsg);
break;
default:
logger->debug("Unknown GameState!");
break;
}
} }
{
std::lock_guard<std::mutex> lock(mtxBuffer);
bufferUpdated = true;
counterBuffer = -1;
}
cvBuffer.notify_one();
logger->info("Game End!");
AILoop = false;
} }
catch (const std::exception& e)
{ {
std::lock_guard<std::mutex> lock(mtxBuffer);
bufferUpdated = true;
counterBuffer = -1;
std::cerr << "C++ Exception: " << e.what() << std::endl;
AILoop = false;
}
catch (...)
{
std::cerr << "Unknown Exception!" << std::endl;
AILoop = false;
} }
cvBuffer.notify_one();
logger->info("Game End!");
AILoop = false;
}; };
std::thread(messageThread).detach(); std::thread(messageThread).detach();
} }
@@ -570,13 +583,13 @@ void Logic::LoadBufferCase(const protobuf::MessageOfObj& item)
{ {
if (Proto2THUAI6::newsTypeDict[news.news_case()] == THUAI6::NewsType::TextMessage) if (Proto2THUAI6::newsTypeDict[news.news_case()] == THUAI6::NewsType::TextMessage)
{ {
messageQueue.emplace(std::make_pair(news.to_id(), news.text_message()));
messageQueue.emplace(std::make_pair(news.from_id(), news.text_message()));
logger->debug("Add News!"); logger->debug("Add News!");
} }
else if (Proto2THUAI6::newsTypeDict[news.news_case()] == THUAI6::NewsType::BinaryMessage) else if (Proto2THUAI6::newsTypeDict[news.news_case()] == THUAI6::NewsType::BinaryMessage)
{ {
messageQueue.emplace(std::make_pair(news.to_id(), news.binary_message()));
logger->debug("Add News!");
messageQueue.emplace(std::make_pair(news.from_id(), news.binary_message()));
logger->debug("Add Binary News!");
} }
else else
logger->error("Unknown NewsType!"); logger->error("Unknown NewsType!");
@@ -699,7 +712,7 @@ bool Logic::HaveView(int gridX, int gridY, int selfX, int selfY, int viewRange)
void Logic::Main(CreateAIFunc createAI, std::string IP, std::string port, bool file, bool print, bool warnOnly) void Logic::Main(CreateAIFunc createAI, std::string IP, std::string port, bool file, bool print, bool warnOnly)
{ {
// 建立日志组件 // 建立日志组件
auto fileLogger = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/logic-log.txt", true);
auto fileLogger = std::make_shared<spdlog::sinks::basic_file_sink_mt>(fmt::format("logs/logic-{}-log.txt", playerID), true);
auto printLogger = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); auto printLogger = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
std::string pattern = "[logic] [%H:%M:%S.%e] [%l] %v"; std::string pattern = "[logic] [%H:%M:%S.%e] [%l] %v";
fileLogger->set_pattern(pattern); fileLogger->set_pattern(pattern);
@@ -747,30 +760,41 @@ void Logic::Main(CreateAIFunc createAI, std::string IP, std::string port, bool f
// 构造AI线程 // 构造AI线程
auto AIThread = [&]() auto AIThread = [&]()
{ {
try
{ {
std::unique_lock<std::mutex> lock(mtxAI);
cvAI.wait(lock, [this]()
{ return AIStart; });
}
auto ai = createAI(playerID);

while (AILoop)
{
if (asynchronous)
{ {
Wait();
timer->StartTimer();
timer->Play(*ai);
timer->EndTimer();
std::unique_lock<std::mutex> lock(mtxAI);
cvAI.wait(lock, [this]()
{ return AIStart; });
} }
else
auto ai = createAI(playerID);

while (AILoop)
{ {
Update();
timer->StartTimer();
timer->Play(*ai);
timer->EndTimer();
if (asynchronous)
{
Wait();
timer->StartTimer();
timer->Play(*ai);
timer->EndTimer();
}
else
{
Update();
timer->StartTimer();
timer->Play(*ai);
timer->EndTimer();
}
} }
} }
catch (const std::exception& e)
{
std::cerr << "C++ Exception: " << e.what() << std::endl;
}
catch (...)
{
std::cerr << "Unknown Exception!" << std::endl;
}
}; };


// 连接服务器 // 连接服务器


+ 5
- 1
CAPI/cpp/API/src/main.cpp View File

@@ -115,7 +115,11 @@ int THUAI6Main(int argc, char** argv, CreateAIFunc AIBuilder)
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
std::cerr << e.what() << '\n';
std::cerr << "C++ Exception: " << e.what() << '\n';
}
catch (...)
{
std::cerr << "Unknown Exception\n";
} }
return 0; return 0;
} }


+ 50
- 0
CAPI/cpp/README.md View File

@@ -52,6 +52,56 @@ C++ 通信组件与选手接口
- 避免忙等待,注意线程安全,做好线程同步 - 避免忙等待,注意线程安全,做好线程同步
- 善于使用 [Google](https://www.google.com/) 并使用[**英文**](https://en.wikipedia.org/wiki/American_English)搜索,善于查阅 [Microsoft Learn](https://learn.microsoft.com/)、[cppreference](https://en.cppreference.com/)、[StackOverflow](https://stackoverflow.com/) 以及第三方库官方文档等;不应轻信 [CSDN](https://www.csdn.net/) 等劣质博客社区以及[博客园](https://www.cnblogs.com/)、[简书](https://www.jianshu.com/)等质量参差不齐的博客社区,对其内容需全方位多角度仔细求证方可相信 - 善于使用 [Google](https://www.google.com/) 并使用[**英文**](https://en.wikipedia.org/wiki/American_English)搜索,善于查阅 [Microsoft Learn](https://learn.microsoft.com/)、[cppreference](https://en.cppreference.com/)、[StackOverflow](https://stackoverflow.com/) 以及第三方库官方文档等;不应轻信 [CSDN](https://www.csdn.net/) 等劣质博客社区以及[博客园](https://www.cnblogs.com/)、[简书](https://www.jianshu.com/)等质量参差不齐的博客社区,对其内容需全方位多角度仔细求证方可相信


## 开发纪实

### C++ SDK Windows 环境配置

#### 配置方法

起初 C++ 开发时使用 Linux 开发,但在选手包发布 Windows SDK 时遇到诸多问题。Windows 平台的 C++ 接口使用 Visual Studio 进行环境配置。期间遇到诸多问题,配置环境方法如下:

1. 更新 Visual Studio:由于届时要求选手与开发组使用**完全相同**版本的 Visual Studio,否则可能会出现找不到一些标准库内置的符号的问题,因此在选手包发布前夕,将开发组的 Visual Studio 升级到最新版后执行步骤 2
2. 编译 gRPC:分别编译 Debug 和 Release 的 gRPC 依赖库各一份。方法有二:
- 方法一:根据 [gRPC Windows 生成教程](https://github.com/grpc/grpc/blob/0b2609a61d50f2dbba4604978ef9243543a2675a/BUILDING.md#windows-using-visual-studio-2019-or-later) 在本地分别编译一份 `Debug` 和 `Release`(即在 CMake config 参数中分别指定 Debug 和 Release 各生成一份 Visual Studio 工程)的 x64 平台的依赖库。注意使用静态编译,根据[此原则](https://github.com/grpc/grpc/blob/0b2609a61d50f2dbba4604978ef9243543a2675a/BUILDING.md#windows-a-note-on-building-shared-libs-dlls)**不生成** gRPC 和 Protocol Buffers 本身的 DLL,即只有 `.lib` 无 `.dll`(但是可能会有一些其依赖的第三方库的 `.dll`),将 Debug 和 Release 生成的 `.lib` 和 `.dll`(如果有的话)均分别保存
- 方法二:根据 [gRPC vcpkg 安装教程](https://github.com/grpc/grpc/tree/master/src/cpp#install-using-vcpkg-package) 安装 **`x64-windows-static`** 的 gRPC,即执行 `vcpkg install grpc:x64-windows-static`,以生成静态库(注意 vcpkg 需要调用本地的 Visual Studio 进行现场编译和生成,所以所耗时间可能较长,请耐心等待;并且此步骤由于需要从 GitHub 上现场拉源代码,因此可能需要科学上网)。然后进入 vcpkg 的安装目录,将里面安装的 gRPC 的所有 Debug 和 Release 的 `.lib` 和 `.dll`(如果有的话)分别保存一份
3. 提取 gRPC 头文件:参考 [gRPC 所有所需头文件](https://github.com/eesast/THUAI6/tree/217d87aeedf6735e3257acb1e2c70574df45b823/CAPI/cpp/grpc)将头文件放入本仓库的相应目录内,以供使用(记得放该版本对应的 LICENSE x)。
4. 配置 `.vcxproj` 和 `.vcxproj.filters`:`.vcxproj` 用于配置项目的编译与链接,`.vcxproj.filters` 用于配置 Visual Studio 的筛选器的结构(即解决方案资源管理器的结构)。参考 THUAI6 的 [`API.vcxproj`](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj) 和 [`API.vcxproj.filters`](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj.filters)。基本上可以直接无脑复制过来,不用做什么修改。如果非要在 Visual Studio 里手动配置,需要配置以下几项(以下的链接均以 x64 Release 为例,Debug 的相应配置在相应链接的上面一些的位置):
- 添加头文件和源文件:[将**自己编写**的 `.h` 和 `.cpp` 文件加入到项目中](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L169-L197)(包括 protoc 生成的那些 `.pb.h` 和 `.pb.cc`,但这些最好参照 `API.vcxproj.filters` 的组织结构[各单独开一个子筛选器](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj.filters#L16-L21)放进去以使得 Visual Studio 打开时更美观)
- 在项目属性中的 Debug 和 Release 的“C/C++”的“语言”中[均设置 C++ 语言标准为 `/std:c++17`](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L154)、[C 语言标准为 `/std:c17`](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L155)
- 在项目属性中的 Debug 和 Release 的[附加编译选项(即在“C/C++”的“命令行”)里均增加 `/source-charset:utf-8` 选项](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L157),以保证编译时编译器使用 UTF-8 编码进行源文件读取(注意这个与 Visual Studio 的高级保存选项不同,高级保存选项决定了文件的保存时的保存编码,而非编译器编译时读取使用的编码)
- 设置头文件的搜索路径:在项目属性的“C/C++”的首页的“附加包含目录”中设置 Debug 和 Release 下的[搜索头文件的根路径](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L156),注意 Windows 下的路径分隔符最好使用 `\` 而非 `/`,负责可能会出现一些玄学问题
- 设置生成方式为静态生成:在项目属性的“C/C++”的“代码生成”的“运行库”中,[Debug 下设置其为“多线程调试(`/MTd`)”](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L137),[Release 下设置其为“多线程(`/MT`)”](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L158)
- 将之前提取的 Debug 和 Release 的 `.lib` 分别放在项目中的单独的文件夹里(THUAI6 使用的是 `CAPI\cpp\lib\debug` 和 `CAPI\cpp\lib\release`),并[使用 `.gitignore` 忽略掉](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/.gitignore#L502)
- 在项目属性的“链接器”的首页的“附加库目录”中分别配置 Debug 和 Release 的 [`.lib` 文件的相应路径](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L166)
- 在项目属性中的“链接器”的“输入”的“附加依赖库”中分别配置 Debug 和 Release [所需要链接的库的文件名](https://github.com/eesast/THUAI6/blob/c8e1fbe299c67a6e101fa02e85bcc971acd0f48b/CAPI/cpp/API/API.vcxproj#L165)。注意 Debug 和 Release 链接的库可能并不完全相同,建议在 cmd 中使用 `dir /b` 将其自动列举并复制。还需要注意需要手动指定链接一些 Windows 自带的 `lib`,例如 `Ws2_32.lib`、`Crypt32.lib`、`Iphlpapi.lib` 等。如果生成过程中不通过,表示找不到一些函数,则在 Google 中搜索该函数,如果发现是 Windows 系统的 API 函数则会搜到[微软官方文档](https://learn.microsoft.com) 的对应链接的页面,则在页面最下方会表明它所在的 `.lib`(例如 [`CreateProcessA` 的页面](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa#requirements)),加进去即可
- 然后进行生成解决方案。如果感觉编译的速度过慢,可以在项目属性的 `C/C++` 的“所有选项”中搜索并行编译,并开启之(`/Qpar`)。不过由于 THUAI6 的疏忽,忘记开启了并行编译
- 然后开始运行。如果提示缺少一些 DLL,可以把之前保存的 `.dll` 文件(如果有的话)放在与 `.exe` 相同的目录下。该目录为**与 `.sln` 相同目录的**(不是与 `.vcxproj` 相同目录的)`x64\Debug` 和 `x64\Release`
- 如果 x64 的 Debug 和 x64 的 Release 均生成成功,那么找一台没配过的电脑再试一次
- 随便写点 AI 代码,重新生成解决方案,确认成功后发布选手包
- 有兴趣也可以支持一下 x86 的 Windows(x
- 有兴趣还可以支持一下 Cygwin(x
### Q&A

#### 为什么不用 CMake

1. 由于 CMake 生成的 Visual Studio 工程是绝对路径,因此不能将 CMake 生成的 Visual Studio 工程直接给选手,需要选手使用 CMake,增加了选手尤其是大一小白选手的负担
2. 对 Debug 和 Release 分别编译时,需要 CMake 分别生成 Debug 和 Release 的 Visual Studio 工程,而难以使用 Visual Studio 和 MSBuild 自带的 Debug 和 Release 的切换方式,这就需要生成两份 Visual Studio 工程,对选手的测试来说也是一种麻烦

#### 为什么不让选手用 vcpkg

1. vcpkg 是将代码拉下来本地编译。拉代码过程需要科学上网,这对一些大一小白选手来说是个不小的负担
2. vcpkg 编译过程极其漫长,会极大消磨选手的耐心

#### 为什么不用 CLion 配合 MinGW、MinGW-w64 等

1. 贵校程设课大多数使用 Visual Studio。对大多数选手来说,这是最易上手的
2. 开箱即用,方便
3. Visual Studio 已使用多年,坑全部踩过了一遍,突然变更又要重新踩坑
4. MinGW 已停止维护,正在维护的是 MinGW-w64,两者名称很像,选手尤其是使用百度等劣质搜索引擎或查阅 CSDN 等劣质博客安装时极易混淆
5. MinGW 需要附加更多的其自己的依赖库如 glibc 等,此 glibc 依然依赖 msvcrt,多一层依赖库便增加了一些版本不兼容问题的风险
6. 其与 MSVC 的 Name Mangling 的规则、ABI 等并不一致或不完全一致,增加各种不兼容问题的风险

## 开发人员 ## 开发人员


- ......(自己加) - ......(自己加)

+ 200
- 332
CAPI/cpp/proto/Message2Clients.pb.cc
File diff suppressed because it is too large
View File


+ 92
- 236
CAPI/cpp/proto/Message2Clients.pb.h View File

@@ -318,21 +318,20 @@ namespace protobuf
kSpeedFieldNumber = 3, kSpeedFieldNumber = 3,
kDeterminationFieldNumber = 4, kDeterminationFieldNumber = 4,
kAddictionFieldNumber = 5, kAddictionFieldNumber = 5,
kPlaceFieldNumber = 7,
kGuidFieldNumber = 10,
kPlayerStateFieldNumber = 9, kPlayerStateFieldNumber = 9,
kGuidFieldNumber = 10,
kBulletTypeFieldNumber = 12, kBulletTypeFieldNumber = 12,
kLearningSpeedFieldNumber = 13, kLearningSpeedFieldNumber = 13,
kTreatSpeedFieldNumber = 14,
kPlayerIdFieldNumber = 15, kPlayerIdFieldNumber = 15,
kTreatSpeedFieldNumber = 14,
kViewRangeFieldNumber = 16, kViewRangeFieldNumber = 16,
kRadiusFieldNumber = 17, kRadiusFieldNumber = 17,
kDangerAlertFieldNumber = 19,
kScoreFieldNumber = 20, kScoreFieldNumber = 20,
kDangerAlertFieldNumber = 19,
kTreatProgressFieldNumber = 21, kTreatProgressFieldNumber = 21,
kRescueProgressFieldNumber = 22, kRescueProgressFieldNumber = 22,
kStudentTypeFieldNumber = 23,
kFacingDirectionFieldNumber = 24, kFacingDirectionFieldNumber = 24,
kStudentTypeFieldNumber = 23,
}; };
// repeated double time_until_skill_available = 6; // repeated double time_until_skill_available = 6;
int time_until_skill_available_size() const; int time_until_skill_available_size() const;
@@ -452,14 +451,14 @@ namespace protobuf
void _internal_set_addiction(int32_t value); void _internal_set_addiction(int32_t value);


public: public:
// .protobuf.PlaceType place = 7;
void clear_place();
::protobuf::PlaceType place() const;
void set_place(::protobuf::PlaceType value);
// .protobuf.PlayerState player_state = 9;
void clear_player_state();
::protobuf::PlayerState player_state() const;
void set_player_state(::protobuf::PlayerState value);


private: private:
::protobuf::PlaceType _internal_place() const;
void _internal_set_place(::protobuf::PlaceType value);
::protobuf::PlayerState _internal_player_state() const;
void _internal_set_player_state(::protobuf::PlayerState value);


public: public:
// int64 guid = 10; // int64 guid = 10;
@@ -471,16 +470,6 @@ namespace protobuf
int64_t _internal_guid() const; int64_t _internal_guid() const;
void _internal_set_guid(int64_t value); void _internal_set_guid(int64_t value);


public:
// .protobuf.PlayerState player_state = 9;
void clear_player_state();
::protobuf::PlayerState player_state() const;
void set_player_state(::protobuf::PlayerState value);

private:
::protobuf::PlayerState _internal_player_state() const;
void _internal_set_player_state(::protobuf::PlayerState value);

public: public:
// .protobuf.BulletType bullet_type = 12; // .protobuf.BulletType bullet_type = 12;
void clear_bullet_type(); void clear_bullet_type();
@@ -501,16 +490,6 @@ namespace protobuf
int32_t _internal_learning_speed() const; int32_t _internal_learning_speed() const;
void _internal_set_learning_speed(int32_t value); void _internal_set_learning_speed(int32_t value);


public:
// int32 treat_speed = 14;
void clear_treat_speed();
int32_t treat_speed() const;
void set_treat_speed(int32_t value);

private:
int32_t _internal_treat_speed() const;
void _internal_set_treat_speed(int32_t value);

public: public:
// int64 player_id = 15; // int64 player_id = 15;
void clear_player_id(); void clear_player_id();
@@ -521,6 +500,16 @@ namespace protobuf
int64_t _internal_player_id() const; int64_t _internal_player_id() const;
void _internal_set_player_id(int64_t value); void _internal_set_player_id(int64_t value);


public:
// int32 treat_speed = 14;
void clear_treat_speed();
int32_t treat_speed() const;
void set_treat_speed(int32_t value);

private:
int32_t _internal_treat_speed() const;
void _internal_set_treat_speed(int32_t value);

public: public:
// int32 view_range = 16; // int32 view_range = 16;
void clear_view_range(); void clear_view_range();
@@ -541,16 +530,6 @@ namespace protobuf
int32_t _internal_radius() const; int32_t _internal_radius() const;
void _internal_set_radius(int32_t value); void _internal_set_radius(int32_t value);


public:
// double danger_alert = 19;
void clear_danger_alert();
double danger_alert() const;
void set_danger_alert(double value);

private:
double _internal_danger_alert() const;
void _internal_set_danger_alert(double value);

public: public:
// int32 score = 20; // int32 score = 20;
void clear_score(); void clear_score();
@@ -561,6 +540,16 @@ namespace protobuf
int32_t _internal_score() const; int32_t _internal_score() const;
void _internal_set_score(int32_t value); void _internal_set_score(int32_t value);


public:
// double danger_alert = 19;
void clear_danger_alert();
double danger_alert() const;
void set_danger_alert(double value);

private:
double _internal_danger_alert() const;
void _internal_set_danger_alert(double value);

public: public:
// int32 treat_progress = 21; // int32 treat_progress = 21;
void clear_treat_progress(); void clear_treat_progress();
@@ -581,16 +570,6 @@ namespace protobuf
int32_t _internal_rescue_progress() const; int32_t _internal_rescue_progress() const;
void _internal_set_rescue_progress(int32_t value); void _internal_set_rescue_progress(int32_t value);


public:
// .protobuf.StudentType student_type = 23;
void clear_student_type();
::protobuf::StudentType student_type() const;
void set_student_type(::protobuf::StudentType value);

private:
::protobuf::StudentType _internal_student_type() const;
void _internal_set_student_type(::protobuf::StudentType value);

public: public:
// double facing_direction = 24; // double facing_direction = 24;
void clear_facing_direction(); void clear_facing_direction();
@@ -601,6 +580,16 @@ namespace protobuf
double _internal_facing_direction() const; double _internal_facing_direction() const;
void _internal_set_facing_direction(double value); void _internal_set_facing_direction(double value);


public:
// .protobuf.StudentType student_type = 23;
void clear_student_type();
::protobuf::StudentType student_type() const;
void set_student_type(::protobuf::StudentType value);

private:
::protobuf::StudentType _internal_student_type() const;
void _internal_set_student_type(::protobuf::StudentType value);

public: public:
// @@protoc_insertion_point(class_scope:protobuf.MessageOfStudent) // @@protoc_insertion_point(class_scope:protobuf.MessageOfStudent)


@@ -623,21 +612,20 @@ namespace protobuf
int32_t speed_; int32_t speed_;
int32_t determination_; int32_t determination_;
int32_t addiction_; int32_t addiction_;
int place_;
int64_t guid_;
int player_state_; int player_state_;
int64_t guid_;
int bullet_type_; int bullet_type_;
int32_t learning_speed_; int32_t learning_speed_;
int32_t treat_speed_;
int64_t player_id_; int64_t player_id_;
int32_t treat_speed_;
int32_t view_range_; int32_t view_range_;
int32_t radius_; int32_t radius_;
double danger_alert_;
int32_t score_; int32_t score_;
double danger_alert_;
int32_t treat_progress_; int32_t treat_progress_;
int32_t rescue_progress_; int32_t rescue_progress_;
int student_type_;
double facing_direction_; double facing_direction_;
int student_type_;
mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
}; };
union union
@@ -812,18 +800,17 @@ namespace protobuf
kXFieldNumber = 1, kXFieldNumber = 1,
kYFieldNumber = 2, kYFieldNumber = 2,
kSpeedFieldNumber = 3, kSpeedFieldNumber = 3,
kPlaceFieldNumber = 6,
kGuidFieldNumber = 9,
kTrickerTypeFieldNumber = 8, kTrickerTypeFieldNumber = 8,
kScoreFieldNumber = 10,
kGuidFieldNumber = 9,
kPlayerIdFieldNumber = 11, kPlayerIdFieldNumber = 11,
kScoreFieldNumber = 10,
kViewRangeFieldNumber = 12, kViewRangeFieldNumber = 12,
kRadiusFieldNumber = 13, kRadiusFieldNumber = 13,
kPlayerStateFieldNumber = 14,
kTrickDesireFieldNumber = 15, kTrickDesireFieldNumber = 15,
kClassVolumeFieldNumber = 16, kClassVolumeFieldNumber = 16,
kPlayerStateFieldNumber = 14,
kBulletTypeFieldNumber = 18,
kFacingDirectionFieldNumber = 17, kFacingDirectionFieldNumber = 17,
kBulletTypeFieldNumber = 18,
}; };
// repeated double time_until_skill_available = 5; // repeated double time_until_skill_available = 5;
int time_until_skill_available_size() const; int time_until_skill_available_size() const;
@@ -923,14 +910,14 @@ namespace protobuf
void _internal_set_speed(int32_t value); void _internal_set_speed(int32_t value);


public: public:
// .protobuf.PlaceType place = 6;
void clear_place();
::protobuf::PlaceType place() const;
void set_place(::protobuf::PlaceType value);
// .protobuf.TrickerType tricker_type = 8;
void clear_tricker_type();
::protobuf::TrickerType tricker_type() const;
void set_tricker_type(::protobuf::TrickerType value);


private: private:
::protobuf::PlaceType _internal_place() const;
void _internal_set_place(::protobuf::PlaceType value);
::protobuf::TrickerType _internal_tricker_type() const;
void _internal_set_tricker_type(::protobuf::TrickerType value);


public: public:
// int64 guid = 9; // int64 guid = 9;
@@ -943,14 +930,14 @@ namespace protobuf
void _internal_set_guid(int64_t value); void _internal_set_guid(int64_t value);


public: public:
// .protobuf.TrickerType tricker_type = 8;
void clear_tricker_type();
::protobuf::TrickerType tricker_type() const;
void set_tricker_type(::protobuf::TrickerType value);
// int64 player_id = 11;
void clear_player_id();
int64_t player_id() const;
void set_player_id(int64_t value);


private: private:
::protobuf::TrickerType _internal_tricker_type() const;
void _internal_set_tricker_type(::protobuf::TrickerType value);
int64_t _internal_player_id() const;
void _internal_set_player_id(int64_t value);


public: public:
// int32 score = 10; // int32 score = 10;
@@ -962,16 +949,6 @@ namespace protobuf
int32_t _internal_score() const; int32_t _internal_score() const;
void _internal_set_score(int32_t value); void _internal_set_score(int32_t value);


public:
// int64 player_id = 11;
void clear_player_id();
int64_t player_id() const;
void set_player_id(int64_t value);

private:
int64_t _internal_player_id() const;
void _internal_set_player_id(int64_t value);

public: public:
// int32 view_range = 12; // int32 view_range = 12;
void clear_view_range(); void clear_view_range();
@@ -992,6 +969,16 @@ namespace protobuf
int32_t _internal_radius() const; int32_t _internal_radius() const;
void _internal_set_radius(int32_t value); void _internal_set_radius(int32_t value);


public:
// .protobuf.PlayerState player_state = 14;
void clear_player_state();
::protobuf::PlayerState player_state() const;
void set_player_state(::protobuf::PlayerState value);

private:
::protobuf::PlayerState _internal_player_state() const;
void _internal_set_player_state(::protobuf::PlayerState value);

public: public:
// double trick_desire = 15; // double trick_desire = 15;
void clear_trick_desire(); void clear_trick_desire();
@@ -1013,14 +1000,14 @@ namespace protobuf
void _internal_set_class_volume(double value); void _internal_set_class_volume(double value);


public: public:
// .protobuf.PlayerState player_state = 14;
void clear_player_state();
::protobuf::PlayerState player_state() const;
void set_player_state(::protobuf::PlayerState value);
// double facing_direction = 17;
void clear_facing_direction();
double facing_direction() const;
void set_facing_direction(double value);


private: private:
::protobuf::PlayerState _internal_player_state() const;
void _internal_set_player_state(::protobuf::PlayerState value);
double _internal_facing_direction() const;
void _internal_set_facing_direction(double value);


public: public:
// .protobuf.BulletType bullet_type = 18; // .protobuf.BulletType bullet_type = 18;
@@ -1032,16 +1019,6 @@ namespace protobuf
::protobuf::BulletType _internal_bullet_type() const; ::protobuf::BulletType _internal_bullet_type() const;
void _internal_set_bullet_type(::protobuf::BulletType value); void _internal_set_bullet_type(::protobuf::BulletType value);


public:
// double facing_direction = 17;
void clear_facing_direction();
double facing_direction() const;
void set_facing_direction(double value);

private:
double _internal_facing_direction() const;
void _internal_set_facing_direction(double value);

public: public:
// @@protoc_insertion_point(class_scope:protobuf.MessageOfTricker) // @@protoc_insertion_point(class_scope:protobuf.MessageOfTricker)


@@ -1062,18 +1039,17 @@ namespace protobuf
int32_t x_; int32_t x_;
int32_t y_; int32_t y_;
int32_t speed_; int32_t speed_;
int place_;
int64_t guid_;
int tricker_type_; int tricker_type_;
int32_t score_;
int64_t guid_;
int64_t player_id_; int64_t player_id_;
int32_t score_;
int32_t view_range_; int32_t view_range_;
int32_t radius_; int32_t radius_;
int player_state_;
double trick_desire_; double trick_desire_;
double class_volume_; double class_volume_;
int player_state_;
int bullet_type_;
double facing_direction_; double facing_direction_;
int bullet_type_;
mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
}; };
union union
@@ -1249,7 +1225,6 @@ namespace protobuf
kTeamFieldNumber = 6, kTeamFieldNumber = 6,
kGuidFieldNumber = 5, kGuidFieldNumber = 5,
kBombRangeFieldNumber = 8, kBombRangeFieldNumber = 8,
kPlaceFieldNumber = 7,
kSpeedFieldNumber = 9, kSpeedFieldNumber = 9,
}; };
// .protobuf.BulletType type = 1; // .protobuf.BulletType type = 1;
@@ -1321,16 +1296,6 @@ namespace protobuf
double _internal_bomb_range() const; double _internal_bomb_range() const;
void _internal_set_bomb_range(double value); void _internal_set_bomb_range(double value);


public:
// .protobuf.PlaceType place = 7;
void clear_place();
::protobuf::PlaceType place() const;
void set_place(::protobuf::PlaceType value);

private:
::protobuf::PlaceType _internal_place() const;
void _internal_set_place(::protobuf::PlaceType value);

public: public:
// int32 speed = 9; // int32 speed = 9;
void clear_speed(); void clear_speed();
@@ -1360,7 +1325,6 @@ namespace protobuf
int team_; int team_;
int64_t guid_; int64_t guid_;
double bomb_range_; double bomb_range_;
int place_;
int32_t speed_; int32_t speed_;
mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
}; };
@@ -1785,9 +1749,8 @@ namespace protobuf
kTypeFieldNumber = 1, kTypeFieldNumber = 1,
kXFieldNumber = 2, kXFieldNumber = 2,
kFacingDirectionFieldNumber = 4, kFacingDirectionFieldNumber = 4,
kYFieldNumber = 3,
kPlaceFieldNumber = 6,
kGuidFieldNumber = 5, kGuidFieldNumber = 5,
kYFieldNumber = 3,
}; };
// .protobuf.PropType type = 1; // .protobuf.PropType type = 1;
void clear_type(); void clear_type();
@@ -1818,26 +1781,6 @@ namespace protobuf
double _internal_facing_direction() const; double _internal_facing_direction() const;
void _internal_set_facing_direction(double value); void _internal_set_facing_direction(double value);


public:
// int32 y = 3;
void clear_y();
int32_t y() const;
void set_y(int32_t value);

private:
int32_t _internal_y() const;
void _internal_set_y(int32_t value);

public:
// .protobuf.PlaceType place = 6;
void clear_place();
::protobuf::PlaceType place() const;
void set_place(::protobuf::PlaceType value);

private:
::protobuf::PlaceType _internal_place() const;
void _internal_set_place(::protobuf::PlaceType value);

public: public:
// int64 guid = 5; // int64 guid = 5;
void clear_guid(); void clear_guid();
@@ -1848,6 +1791,16 @@ namespace protobuf
int64_t _internal_guid() const; int64_t _internal_guid() const;
void _internal_set_guid(int64_t value); void _internal_set_guid(int64_t value);


public:
// int32 y = 3;
void clear_y();
int32_t y() const;
void set_y(int32_t value);

private:
int32_t _internal_y() const;
void _internal_set_y(int32_t value);

public: public:
// @@protoc_insertion_point(class_scope:protobuf.MessageOfProp) // @@protoc_insertion_point(class_scope:protobuf.MessageOfProp)


@@ -1863,9 +1816,8 @@ namespace protobuf
int type_; int type_;
int32_t x_; int32_t x_;
double facing_direction_; double facing_direction_;
int32_t y_;
int place_;
int64_t guid_; int64_t guid_;
int32_t y_;
mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
}; };
union union
@@ -5502,30 +5454,6 @@ namespace protobuf
return _internal_mutable_time_until_skill_available(); return _internal_mutable_time_until_skill_available();
} }


// .protobuf.PlaceType place = 7;
inline void MessageOfStudent::clear_place()
{
_impl_.place_ = 0;
}
inline ::protobuf::PlaceType MessageOfStudent::_internal_place() const
{
return static_cast<::protobuf::PlaceType>(_impl_.place_);
}
inline ::protobuf::PlaceType MessageOfStudent::place() const
{
// @@protoc_insertion_point(field_get:protobuf.MessageOfStudent.place)
return _internal_place();
}
inline void MessageOfStudent::_internal_set_place(::protobuf::PlaceType value)
{
_impl_.place_ = value;
}
inline void MessageOfStudent::set_place(::protobuf::PlaceType value)
{
_internal_set_place(value);
// @@protoc_insertion_point(field_set:protobuf.MessageOfStudent.place)
}

// repeated .protobuf.PropType prop = 8; // repeated .protobuf.PropType prop = 8;
inline int MessageOfStudent::_internal_prop_size() const inline int MessageOfStudent::_internal_prop_size() const
{ {
@@ -6105,30 +6033,6 @@ namespace protobuf
return _internal_mutable_time_until_skill_available(); return _internal_mutable_time_until_skill_available();
} }


// .protobuf.PlaceType place = 6;
inline void MessageOfTricker::clear_place()
{
_impl_.place_ = 0;
}
inline ::protobuf::PlaceType MessageOfTricker::_internal_place() const
{
return static_cast<::protobuf::PlaceType>(_impl_.place_);
}
inline ::protobuf::PlaceType MessageOfTricker::place() const
{
// @@protoc_insertion_point(field_get:protobuf.MessageOfTricker.place)
return _internal_place();
}
inline void MessageOfTricker::_internal_set_place(::protobuf::PlaceType value)
{
_impl_.place_ = value;
}
inline void MessageOfTricker::set_place(::protobuf::PlaceType value)
{
_internal_set_place(value);
// @@protoc_insertion_point(field_set:protobuf.MessageOfTricker.place)
}

// repeated .protobuf.PropType prop = 7; // repeated .protobuf.PropType prop = 7;
inline int MessageOfTricker::_internal_prop_size() const inline int MessageOfTricker::_internal_prop_size() const
{ {
@@ -6649,30 +6553,6 @@ namespace protobuf
// @@protoc_insertion_point(field_set:protobuf.MessageOfBullet.team) // @@protoc_insertion_point(field_set:protobuf.MessageOfBullet.team)
} }


// .protobuf.PlaceType place = 7;
inline void MessageOfBullet::clear_place()
{
_impl_.place_ = 0;
}
inline ::protobuf::PlaceType MessageOfBullet::_internal_place() const
{
return static_cast<::protobuf::PlaceType>(_impl_.place_);
}
inline ::protobuf::PlaceType MessageOfBullet::place() const
{
// @@protoc_insertion_point(field_get:protobuf.MessageOfBullet.place)
return _internal_place();
}
inline void MessageOfBullet::_internal_set_place(::protobuf::PlaceType value)
{
_impl_.place_ = value;
}
inline void MessageOfBullet::set_place(::protobuf::PlaceType value)
{
_internal_set_place(value);
// @@protoc_insertion_point(field_set:protobuf.MessageOfBullet.place)
}

// double bomb_range = 8; // double bomb_range = 8;
inline void MessageOfBullet::clear_bomb_range() inline void MessageOfBullet::clear_bomb_range()
{ {
@@ -6993,30 +6873,6 @@ namespace protobuf
// @@protoc_insertion_point(field_set:protobuf.MessageOfProp.guid) // @@protoc_insertion_point(field_set:protobuf.MessageOfProp.guid)
} }


// .protobuf.PlaceType place = 6;
inline void MessageOfProp::clear_place()
{
_impl_.place_ = 0;
}
inline ::protobuf::PlaceType MessageOfProp::_internal_place() const
{
return static_cast<::protobuf::PlaceType>(_impl_.place_);
}
inline ::protobuf::PlaceType MessageOfProp::place() const
{
// @@protoc_insertion_point(field_get:protobuf.MessageOfProp.place)
return _internal_place();
}
inline void MessageOfProp::_internal_set_place(::protobuf::PlaceType value)
{
_impl_.place_ = value;
}
inline void MessageOfProp::set_place(::protobuf::PlaceType value)
{
_internal_set_place(value);
// @@protoc_insertion_point(field_set:protobuf.MessageOfProp.place)
}

// ------------------------------------------------------------------- // -------------------------------------------------------------------


// MessageOfPickedProp // MessageOfPickedProp


+ 38
- 10
CAPI/cpp/proto/Message2Server.pb.cc View File

@@ -180,7 +180,7 @@ namespace protobuf
::_pbi::ConstantInitialized ::_pbi::ConstantInitialized
) : ) :
_impl_{ _impl_{
/*decltype(_impl_.player_id_)*/ int64_t{0}, /*decltype(_impl_.skill_id_)*/ 0, /*decltype(_impl_._cached_size_)*/ {}}
/*decltype(_impl_.player_id_)*/ int64_t{0}, /*decltype(_impl_.skill_id_)*/ 0, /*decltype(_impl_.skill_param_)*/ 0, /*decltype(_impl_._cached_size_)*/ {}}
{ {
} }
struct SkillMsgDefaultTypeInternal struct SkillMsgDefaultTypeInternal
@@ -274,6 +274,7 @@ const uint32_t TableStruct_Message2Server_2eproto::offsets[] PROTOBUF_SECTION_VA
~0u, // no _inlined_string_donated_ ~0u, // no _inlined_string_donated_
PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.player_id_), PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.player_id_),
PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.skill_id_), PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.skill_id_),
PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.skill_param_),
}; };
static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
{0, -1, -1, sizeof(::protobuf::PlayerMsg)}, {0, -1, -1, sizeof(::protobuf::PlayerMsg)},
@@ -313,9 +314,9 @@ const char descriptor_table_protodef_Message2Server_2eproto[] PROTOBUF_SECTION_V
"\t\n\007message\"-\n\tAttackMsg\022\021\n\tplayer_id\030\001 \001" "\t\n\007message\"-\n\tAttackMsg\022\021\n\tplayer_id\030\001 \001"
"(\003\022\r\n\005angle\030\002 \001(\001\"\032\n\005IDMsg\022\021\n\tplayer_id\030" "(\003\022\r\n\005angle\030\002 \001(\001\"\032\n\005IDMsg\022\021\n\tplayer_id\030"
"\001 \001(\003\"<\n\021TreatAndRescueMsg\022\021\n\tplayer_id\030" "\001 \001(\003\"<\n\021TreatAndRescueMsg\022\021\n\tplayer_id\030"
"\001 \001(\003\022\024\n\014to_player_id\030\002 \001(\003\"/\n\010SkillMsg\022"
"\021\n\tplayer_id\030\001 \001(\003\022\020\n\010skill_id\030\002 \001(\005b\006pr"
"oto3";
"\001 \001(\003\022\024\n\014to_player_id\030\002 \001(\003\"D\n\010SkillMsg\022"
"\021\n\tplayer_id\030\001 \001(\003\022\020\n\010skill_id\030\002 \001(\005\022\023\n\013"
"skill_param\030\003 \001(\005b\006proto3";
static const ::_pbi::DescriptorTable* const descriptor_table_Message2Server_2eproto_deps[1] = { static const ::_pbi::DescriptorTable* const descriptor_table_Message2Server_2eproto_deps[1] = {
&::descriptor_table_MessageType_2eproto, &::descriptor_table_MessageType_2eproto,
}; };
@@ -323,7 +324,7 @@ static ::_pbi::once_flag descriptor_table_Message2Server_2eproto_once;
const ::_pbi::DescriptorTable descriptor_table_Message2Server_2eproto = { const ::_pbi::DescriptorTable descriptor_table_Message2Server_2eproto = {
false, false,
false, false,
684,
705,
descriptor_table_protodef_Message2Server_2eproto, descriptor_table_protodef_Message2Server_2eproto,
"Message2Server.proto", "Message2Server.proto",
&descriptor_table_Message2Server_2eproto_once, &descriptor_table_Message2Server_2eproto_once,
@@ -2341,10 +2342,10 @@ namespace protobuf
SkillMsg* const _this = this; SkillMsg* const _this = this;
(void)_this; (void)_this;
new (&_impl_) Impl_{ new (&_impl_) Impl_{
decltype(_impl_.player_id_){}, decltype(_impl_.skill_id_){}, /*decltype(_impl_._cached_size_)*/ {}};
decltype(_impl_.player_id_){}, decltype(_impl_.skill_id_){}, decltype(_impl_.skill_param_){}, /*decltype(_impl_._cached_size_)*/ {}};


_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
::memcpy(&_impl_.player_id_, &from._impl_.player_id_, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_id_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_id_));
::memcpy(&_impl_.player_id_, &from._impl_.player_id_, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_param_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_param_));
// @@protoc_insertion_point(copy_constructor:protobuf.SkillMsg) // @@protoc_insertion_point(copy_constructor:protobuf.SkillMsg)
} }


@@ -2355,7 +2356,7 @@ namespace protobuf
(void)arena; (void)arena;
(void)is_message_owned; (void)is_message_owned;
new (&_impl_) Impl_{ new (&_impl_) Impl_{
decltype(_impl_.player_id_){int64_t{0}}, decltype(_impl_.skill_id_){0}, /*decltype(_impl_._cached_size_)*/ {}};
decltype(_impl_.player_id_){int64_t{0}}, decltype(_impl_.skill_id_){0}, decltype(_impl_.skill_param_){0}, /*decltype(_impl_._cached_size_)*/ {}};
} }


SkillMsg::~SkillMsg() SkillMsg::~SkillMsg()
@@ -2386,7 +2387,7 @@ namespace protobuf
// Prevent compiler warnings about cached_has_bits being unused // Prevent compiler warnings about cached_has_bits being unused
(void)cached_has_bits; (void)cached_has_bits;


::memset(&_impl_.player_id_, 0, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_id_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_id_));
::memset(&_impl_.player_id_, 0, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_param_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_param_));
_internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
} }


@@ -2421,6 +2422,16 @@ namespace protobuf
else else
goto handle_unusual; goto handle_unusual;
continue; continue;
// int32 skill_param = 3;
case 3:
if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24))
{
_impl_.skill_param_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
CHK_(ptr);
}
else
goto handle_unusual;
continue;
default: default:
goto handle_unusual; goto handle_unusual;
} // switch } // switch
@@ -2469,6 +2480,13 @@ namespace protobuf
target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_skill_id(), target); target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_skill_id(), target);
} }


// int32 skill_param = 3;
if (this->_internal_skill_param() != 0)
{
target = stream->EnsureSpace(target);
target = ::_pbi::WireFormatLite::WriteInt32ToArray(3, this->_internal_skill_param(), target);
}

if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields()))
{ {
target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
@@ -2500,6 +2518,12 @@ namespace protobuf
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_skill_id()); total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_skill_id());
} }


// int32 skill_param = 3;
if (this->_internal_skill_param() != 0)
{
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_skill_param());
}

return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
} }


@@ -2528,6 +2552,10 @@ namespace protobuf
{ {
_this->_internal_set_skill_id(from._internal_skill_id()); _this->_internal_set_skill_id(from._internal_skill_id());
} }
if (from._internal_skill_param() != 0)
{
_this->_internal_set_skill_param(from._internal_skill_param());
}
_this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
} }


@@ -2550,7 +2578,7 @@ namespace protobuf
using std::swap; using std::swap;
_internal_metadata_.InternalSwap(&other->_internal_metadata_); _internal_metadata_.InternalSwap(&other->_internal_metadata_);
::PROTOBUF_NAMESPACE_ID::internal::memswap< ::PROTOBUF_NAMESPACE_ID::internal::memswap<
PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.skill_id_) + sizeof(SkillMsg::_impl_.skill_id_) - PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.player_id_)>(
PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.skill_param_) + sizeof(SkillMsg::_impl_.skill_param_) - PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.player_id_)>(
reinterpret_cast<char*>(&_impl_.player_id_), reinterpret_cast<char*>(&_impl_.player_id_),
reinterpret_cast<char*>(&other->_impl_.player_id_) reinterpret_cast<char*>(&other->_impl_.player_id_)
); );


+ 36
- 0
CAPI/cpp/proto/Message2Server.pb.h View File

@@ -1813,6 +1813,7 @@ namespace protobuf
{ {
kPlayerIdFieldNumber = 1, kPlayerIdFieldNumber = 1,
kSkillIdFieldNumber = 2, kSkillIdFieldNumber = 2,
kSkillParamFieldNumber = 3,
}; };
// int64 player_id = 1; // int64 player_id = 1;
void clear_player_id(); void clear_player_id();
@@ -1833,6 +1834,16 @@ namespace protobuf
int32_t _internal_skill_id() const; int32_t _internal_skill_id() const;
void _internal_set_skill_id(int32_t value); void _internal_set_skill_id(int32_t value);


public:
// int32 skill_param = 3;
void clear_skill_param();
int32_t skill_param() const;
void set_skill_param(int32_t value);

private:
int32_t _internal_skill_param() const;
void _internal_set_skill_param(int32_t value);

public: public:
// @@protoc_insertion_point(class_scope:protobuf.SkillMsg) // @@protoc_insertion_point(class_scope:protobuf.SkillMsg)


@@ -1847,6 +1858,7 @@ namespace protobuf
{ {
int64_t player_id_; int64_t player_id_;
int32_t skill_id_; int32_t skill_id_;
int32_t skill_param_;
mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
}; };
union union
@@ -2597,6 +2609,30 @@ namespace protobuf
// @@protoc_insertion_point(field_set:protobuf.SkillMsg.skill_id) // @@protoc_insertion_point(field_set:protobuf.SkillMsg.skill_id)
} }


// int32 skill_param = 3;
inline void SkillMsg::clear_skill_param()
{
_impl_.skill_param_ = 0;
}
inline int32_t SkillMsg::_internal_skill_param() const
{
return _impl_.skill_param_;
}
inline int32_t SkillMsg::skill_param() const
{
// @@protoc_insertion_point(field_get:protobuf.SkillMsg.skill_param)
return _internal_skill_param();
}
inline void SkillMsg::_internal_set_skill_param(int32_t value)
{
_impl_.skill_param_ = value;
}
inline void SkillMsg::set_skill_param(int32_t value)
{
_internal_set_skill_param(value);
// @@protoc_insertion_point(field_set:protobuf.SkillMsg.skill_param)
}

#ifdef __GNUC__ #ifdef __GNUC__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif // __GNUC__ #endif // __GNUC__


+ 40
- 39
CAPI/cpp/proto/MessageType.pb.cc View File

@@ -30,49 +30,49 @@ static constexpr ::_pbi::MigrationSchema* schemas = nullptr;
static constexpr ::_pb::Message* const* file_default_instances = nullptr; static constexpr ::_pb::Message* const* file_default_instances = nullptr;


const char descriptor_table_protodef_MessageType_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = const char descriptor_table_protodef_MessageType_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
"\n\021MessageType.proto\022\010protobuf*\202\001\n\nBullet"
"Type\022\024\n\020NULL_BULLET_TYPE\020\000\022\020\n\014FLYING_KNI"
"FE\020\001\022\034\n\030COMMON_ATTACK_OF_TRICKER\020\002\022\r\n\tBO"
"MB_BOMB\020\003\022\020\n\014JUMPY_DUMPTY\020\004\022\r\n\tATOM_BOMB"
"\020\005*\241\001\n\tPlaceType\022\023\n\017NULL_PLACE_TYPE\020\000\022\010\n"
"\004LAND\020\001\022\010\n\004WALL\020\002\022\t\n\005GRASS\020\003\022\r\n\tCLASSROO"
"M\020\004\022\010\n\004GATE\020\005\022\017\n\013HIDDEN_GATE\020\006\022\n\n\006WINDOW"
"\020\007\022\t\n\005DOOR3\020\010\022\t\n\005DOOR5\020\t\022\t\n\005DOOR6\020\n\022\t\n\005C"
"HEST\020\013*8\n\tShapeType\022\023\n\017NULL_SHAPE_TYPE\020\000"
"\022\n\n\006CIRCLE\020\001\022\n\n\006SQUARE\020\002*\256\001\n\010PropType\022\022\n"
"\016NULL_PROP_TYPE\020\000\022\r\n\tADD_SPEED\020\001\022\035\n\031ADD_"
"LIFE_OR_CLAIRAUDIENCE\020\002\022\020\n\014ADD_HP_OR_AP\020"
"\003\022\023\n\017SHIELD_OR_SPEAR\020\004\022\010\n\004KEY3\020\005\022\010\n\004KEY5"
"\020\006\022\010\n\004KEY6\020\007\022\033\n\027RECOVERY_FROM_DIZZINESS\020"
"\010*n\n\017StudentBuffType\022\023\n\017NULL_SBUFF_TYPE\020"
"\000\022\025\n\021STUDENT_ADD_SPEED\020\001\022\014\n\010ADD_LIFE\020\002\022\n"
"\n\006SHIELD\020\003\022\025\n\021STUDENT_INVISIBLE\020\004*\251\002\n\013Pl"
"ayerState\022\017\n\013NULL_STATUS\020\000\022\010\n\004IDLE\020\001\022\014\n\010"
"LEARNING\020\002\022\014\n\010ADDICTED\020\003\022\010\n\004QUIT\020\004\022\r\n\tGR"
"ADUATED\020\005\022\013\n\007TREATED\020\006\022\013\n\007RESCUED\020\007\022\013\n\007S"
"TUNNED\020\010\022\014\n\010TREATING\020\t\022\014\n\010RESCUING\020\n\022\014\n\010"
"SWINGING\020\013\022\r\n\tATTACKING\020\014\022\013\n\007LOCKING\020\r\022\r"
"\n\tRUMMAGING\020\016\022\014\n\010CLIMBING\020\017\022\023\n\017OPENING_A"
"_CHEST\020\020\022\027\n\023USING_SPECIAL_SKILL\020\021\022\022\n\016OPE"
"NING_A_GATE\020\022*~\n\017TrickerBuffType\022\023\n\017NULL"
"_TBUFF_TYPE\020\000\022\025\n\021TRICKER_ADD_SPEED\020\001\022\t\n\005"
"SPEAR\020\002\022\n\n\006ADD_AP\020\003\022\021\n\rCLAIRAUDIENCE\020\004\022\025"
"\n\021TRICKER_INVISIBLE\020\005*J\n\nPlayerType\022\024\n\020N"
"ULL_PLAYER_TYPE\020\000\022\022\n\016STUDENT_PLAYER\020\001\022\022\n"
"\016TRICKER_PLAYER\020\002*\177\n\013StudentType\022\025\n\021NULL"
"_STUDENT_TYPE\020\000\022\013\n\007ATHLETE\020\001\022\013\n\007TEACHER\020"
"\002\022\026\n\022STRAIGHT_A_STUDENT\020\003\022\t\n\005ROBOT\020\004\022\016\n\n"
"TECH_OTAKU\020\005\022\014\n\010SUNSHINE\020\006*Z\n\013TrickerTyp"
"e\022\025\n\021NULL_TRICKER_TYPE\020\000\022\014\n\010ASSASSIN\020\001\022\010"
"\n\004KLEE\020\002\022\022\n\016A_NOISY_PERSON\020\003\022\010\n\004IDOL\020\004*P"
"\n\tGameState\022\023\n\017NULL_GAME_STATE\020\000\022\016\n\nGAME"
"_START\020\001\022\020\n\014GAME_RUNNING\020\002\022\014\n\010GAME_END\020\003"
"b\006proto3";
"\n\021MessageType.proto\022\010protobuf*\177\n\nBulletT"
"ype\022\024\n\020NULL_BULLET_TYPE\020\000\022\020\n\014FLYING_KNIF"
"E\020\001\022\034\n\030COMMON_ATTACK_OF_TRICKER\020\002\022\r\n\tBOM"
"B_BOMB\020\003\022\020\n\014JUMPY_DUMPTY\020\004\022\n\n\006STRIKE\020\005*\241"
"\001\n\tPlaceType\022\023\n\017NULL_PLACE_TYPE\020\000\022\010\n\004LAN"
"D\020\001\022\010\n\004WALL\020\002\022\t\n\005GRASS\020\003\022\r\n\tCLASSROOM\020\004\022"
"\010\n\004GATE\020\005\022\017\n\013HIDDEN_GATE\020\006\022\n\n\006WINDOW\020\007\022\t"
"\n\005DOOR3\020\010\022\t\n\005DOOR5\020\t\022\t\n\005DOOR6\020\n\022\t\n\005CHEST"
"\020\013*8\n\tShapeType\022\023\n\017NULL_SHAPE_TYPE\020\000\022\n\n\006"
"CIRCLE\020\001\022\n\n\006SQUARE\020\002*\302\001\n\010PropType\022\022\n\016NUL"
"L_PROP_TYPE\020\000\022\r\n\tADD_SPEED\020\001\022\035\n\031ADD_LIFE"
"_OR_CLAIRAUDIENCE\020\002\022\020\n\014ADD_HP_OR_AP\020\003\022\023\n"
"\017SHIELD_OR_SPEAR\020\004\022\010\n\004KEY3\020\005\022\010\n\004KEY5\020\006\022\010"
"\n\004KEY6\020\007\022\033\n\027RECOVERY_FROM_DIZZINESS\020\010\022\022\n"
"\016CRAFTING_BENCH\020\t*n\n\017StudentBuffType\022\023\n\017"
"NULL_SBUFF_TYPE\020\000\022\025\n\021STUDENT_ADD_SPEED\020\001"
"\022\014\n\010ADD_LIFE\020\002\022\n\n\006SHIELD\020\003\022\025\n\021STUDENT_IN"
"VISIBLE\020\004*\251\002\n\013PlayerState\022\017\n\013NULL_STATUS"
"\020\000\022\010\n\004IDLE\020\001\022\014\n\010LEARNING\020\002\022\014\n\010ADDICTED\020\003"
"\022\010\n\004QUIT\020\004\022\r\n\tGRADUATED\020\005\022\013\n\007TREATED\020\006\022\013"
"\n\007RESCUED\020\007\022\013\n\007STUNNED\020\010\022\014\n\010TREATING\020\t\022\014"
"\n\010RESCUING\020\n\022\014\n\010SWINGING\020\013\022\r\n\tATTACKING\020"
"\014\022\013\n\007LOCKING\020\r\022\r\n\tRUMMAGING\020\016\022\014\n\010CLIMBIN"
"G\020\017\022\023\n\017OPENING_A_CHEST\020\020\022\027\n\023USING_SPECIA"
"L_SKILL\020\021\022\022\n\016OPENING_A_GATE\020\022*~\n\017Tricker"
"BuffType\022\023\n\017NULL_TBUFF_TYPE\020\000\022\025\n\021TRICKER"
"_ADD_SPEED\020\001\022\t\n\005SPEAR\020\002\022\n\n\006ADD_AP\020\003\022\021\n\rC"
"LAIRAUDIENCE\020\004\022\025\n\021TRICKER_INVISIBLE\020\005*J\n"
"\nPlayerType\022\024\n\020NULL_PLAYER_TYPE\020\000\022\022\n\016STU"
"DENT_PLAYER\020\001\022\022\n\016TRICKER_PLAYER\020\002*\177\n\013Stu"
"dentType\022\025\n\021NULL_STUDENT_TYPE\020\000\022\013\n\007ATHLE"
"TE\020\001\022\013\n\007TEACHER\020\002\022\026\n\022STRAIGHT_A_STUDENT\020"
"\003\022\t\n\005ROBOT\020\004\022\016\n\nTECH_OTAKU\020\005\022\014\n\010SUNSHINE"
"\020\006*Z\n\013TrickerType\022\025\n\021NULL_TRICKER_TYPE\020\000"
"\022\014\n\010ASSASSIN\020\001\022\010\n\004KLEE\020\002\022\022\n\016A_NOISY_PERS"
"ON\020\003\022\010\n\004IDOL\020\004*P\n\tGameState\022\023\n\017NULL_GAME"
"_STATE\020\000\022\016\n\nGAME_START\020\001\022\020\n\014GAME_RUNNING"
"\020\002\022\014\n\010GAME_END\020\003b\006proto3";
static ::_pbi::once_flag descriptor_table_MessageType_2eproto_once; static ::_pbi::once_flag descriptor_table_MessageType_2eproto_once;
const ::_pbi::DescriptorTable descriptor_table_MessageType_2eproto = { const ::_pbi::DescriptorTable descriptor_table_MessageType_2eproto = {
false, false,
false, false,
1488,
1504,
descriptor_table_protodef_MessageType_2eproto, descriptor_table_protodef_MessageType_2eproto,
"MessageType.proto", "MessageType.proto",
&descriptor_table_MessageType_2eproto_once, &descriptor_table_MessageType_2eproto_once,
@@ -179,6 +179,7 @@ namespace protobuf
case 6: case 6:
case 7: case 7:
case 8: case 8:
case 9:
return true; return true;
default: default:
return false; return false;


+ 4
- 3
CAPI/cpp/proto/MessageType.pb.h View File

@@ -57,13 +57,13 @@ namespace protobuf
COMMON_ATTACK_OF_TRICKER = 2, COMMON_ATTACK_OF_TRICKER = 2,
BOMB_BOMB = 3, BOMB_BOMB = 3,
JUMPY_DUMPTY = 4, JUMPY_DUMPTY = 4,
ATOM_BOMB = 5,
STRIKE = 5,
BulletType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), BulletType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
BulletType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() BulletType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
}; };
bool BulletType_IsValid(int value); bool BulletType_IsValid(int value);
constexpr BulletType BulletType_MIN = NULL_BULLET_TYPE; constexpr BulletType BulletType_MIN = NULL_BULLET_TYPE;
constexpr BulletType BulletType_MAX = ATOM_BOMB;
constexpr BulletType BulletType_MAX = STRIKE;
constexpr int BulletType_ARRAYSIZE = BulletType_MAX + 1; constexpr int BulletType_ARRAYSIZE = BulletType_MAX + 1;


const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* BulletType_descriptor(); const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* BulletType_descriptor();
@@ -163,12 +163,13 @@ namespace protobuf
KEY5 = 6, KEY5 = 6,
KEY6 = 7, KEY6 = 7,
RECOVERY_FROM_DIZZINESS = 8, RECOVERY_FROM_DIZZINESS = 8,
CRAFTING_BENCH = 9,
PropType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), PropType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
PropType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() PropType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
}; };
bool PropType_IsValid(int value); bool PropType_IsValid(int value);
constexpr PropType PropType_MIN = NULL_PROP_TYPE; constexpr PropType PropType_MIN = NULL_PROP_TYPE;
constexpr PropType PropType_MAX = RECOVERY_FROM_DIZZINESS;
constexpr PropType PropType_MAX = CRAFTING_BENCH;
constexpr int PropType_ARRAYSIZE = PropType_MAX + 1; constexpr int PropType_ARRAYSIZE = PropType_MAX + 1;


const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PropType_descriptor(); const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PropType_descriptor();


+ 2
- 0
CAPI/cpp/proto/Services.grpc.pb.h View File

@@ -25,6 +25,8 @@
#include <grpcpp/impl/codegen/stub_options.h> #include <grpcpp/impl/codegen/stub_options.h>
#include <grpcpp/impl/codegen/sync_stream.h> #include <grpcpp/impl/codegen/sync_stream.h>


#undef SendMessage

namespace protobuf namespace protobuf
{ {




+ 18
- 8
CAPI/python/PyAPI/API.py View File

@@ -6,7 +6,6 @@ from typing import List, cast, Tuple, Union




class StudentAPI(IStudentAPI, IGameTimer): class StudentAPI(IStudentAPI, IGameTimer):

def __init__(self, logic: ILogic) -> None: def __init__(self, logic: ILogic) -> None:
self.__logic = logic self.__logic = logic
self.__pool = ThreadPoolExecutor(20) self.__pool = ThreadPoolExecutor(20)
@@ -44,8 +43,8 @@ class StudentAPI(IStudentAPI, IGameTimer):
def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]:
return self.__pool.submit(self.__logic.ThrowProp, propType) return self.__pool.submit(self.__logic.ThrowProp, propType)


def UseSkill(self, skillID: int) -> Future[bool]:
return self.__pool.submit(self.__logic.UseSkill, skillID)
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
return self.__pool.submit(self.__logic.UseSkill, skillID, skillParam)


# 与地图交互相关 # 与地图交互相关
def OpenDoor(self) -> Future[bool]: def OpenDoor(self) -> Future[bool]:
@@ -133,7 +132,13 @@ class StudentAPI(IStudentAPI, IGameTimer):
return self.__logic.GetGameInfo() return self.__logic.GetGameInfo()


def HaveView(self, gridX: int, gridY: int) -> bool: def HaveView(self, gridX: int, gridY: int) -> bool:
return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange)
return self.__logic.HaveView(
gridX,
gridY,
self.GetSelfInfo().x,
self.GetSelfInfo().y,
self.GetSelfInfo().viewRange,
)


# 用于DEBUG的输出函数,仅在DEBUG模式下有效 # 用于DEBUG的输出函数,仅在DEBUG模式下有效


@@ -182,7 +187,6 @@ class StudentAPI(IStudentAPI, IGameTimer):




class TrickerAPI(ITrickerAPI, IGameTimer): class TrickerAPI(ITrickerAPI, IGameTimer):

def __init__(self, logic: ILogic) -> None: def __init__(self, logic: ILogic) -> None:
self.__logic = logic self.__logic = logic
self.__pool = ThreadPoolExecutor(20) self.__pool = ThreadPoolExecutor(20)
@@ -220,8 +224,8 @@ class TrickerAPI(ITrickerAPI, IGameTimer):
def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]:
return self.__pool.submit(self.__logic.ThrowProp, propType) return self.__pool.submit(self.__logic.ThrowProp, propType)


def UseSkill(self, skillID: int) -> Future[bool]:
return self.__pool.submit(self.__logic.UseSkill, skillID)
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
return self.__pool.submit(self.__logic.UseSkill, skillID, skillParam)


# 与地图交互相关 # 与地图交互相关
def OpenDoor(self) -> Future[bool]: def OpenDoor(self) -> Future[bool]:
@@ -309,7 +313,13 @@ class TrickerAPI(ITrickerAPI, IGameTimer):
return self.__logic.GetGameInfo() return self.__logic.GetGameInfo()


def HaveView(self, gridX: int, gridY: int) -> bool: def HaveView(self, gridX: int, gridY: int) -> bool:
return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange)
return self.__logic.HaveView(
gridX,
gridY,
self.GetSelfInfo().x,
self.GetSelfInfo().y,
self.GetSelfInfo().viewRange,
)


# 用于DEBUG的输出函数,仅在DEBUG模式下有效 # 用于DEBUG的输出函数,仅在DEBUG模式下有效




+ 124
- 21
CAPI/python/PyAPI/Communication.py View File

@@ -19,17 +19,31 @@ class BoolErrorHandler(IErrorHandler):


class Communication: class Communication:
def __init__(self, sIP: str, sPort: str): def __init__(self, sIP: str, sPort: str):
aim = sIP + ':' + sPort
aim = sIP + ":" + sPort
channel = grpc.insecure_channel(aim) channel = grpc.insecure_channel(aim)
self.__THUAI6Stub = Services.AvailableServiceStub(channel) self.__THUAI6Stub = Services.AvailableServiceStub(channel)
self.__haveNewMessage = False self.__haveNewMessage = False
self.__cvMessage = threading.Condition() self.__cvMessage = threading.Condition()
self.__message2Client: Message2Clients.MessageToClient self.__message2Client: Message2Clients.MessageToClient
self.__mtxLimit = threading.Lock()
self.__counter = 0
self.__counterMove = 0
self.__limit = 50
self.__moveLimit = 10


def Move(self, time: int, angle: float, playerID: int) -> bool: def Move(self, time: int, angle: float, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if (
self.__counter >= self.__limit
or self.__counterMove >= self.__moveLimit
):
return False
self.__counter += 1
self.__counterMove += 1
moveResult = self.__THUAI6Stub.Move( moveResult = self.__THUAI6Stub.Move(
THUAI62Proto.THUAI62ProtobufMove(time, angle, playerID))
THUAI62Proto.THUAI62ProtobufMove(time, angle, playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -37,8 +51,13 @@ class Communication:


def PickProp(self, propType: THUAI6.PropType, playerID: int) -> bool: def PickProp(self, propType: THUAI6.PropType, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
pickResult = self.__THUAI6Stub.PickProp( pickResult = self.__THUAI6Stub.PickProp(
THUAI62Proto.THUAI62ProtobufProp(propType, playerID))
THUAI62Proto.THUAI62ProtobufProp(propType, playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -46,8 +65,13 @@ class Communication:


def UseProp(self, propType: THUAI6.PropType, playerID: int) -> bool: def UseProp(self, propType: THUAI6.PropType, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
useResult = self.__THUAI6Stub.UseProp( useResult = self.__THUAI6Stub.UseProp(
THUAI62Proto.THUAI62ProtobufProp(propType, playerID))
THUAI62Proto.THUAI62ProtobufProp(propType, playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -55,17 +79,27 @@ class Communication:


def ThrowProp(self, propType: THUAI6.PropType, playerID: int) -> bool: def ThrowProp(self, propType: THUAI6.PropType, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
throwResult = self.__THUAI6Stub.ThrowProp( throwResult = self.__THUAI6Stub.ThrowProp(
THUAI62Proto.THUAI62ProtobufProp(propType, playerID))
THUAI62Proto.THUAI62ProtobufProp(propType, playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
return throwResult.act_success return throwResult.act_success


def UseSkill(self, skillID: int, playerID: int) -> bool:
def UseSkill(self, skillID: int, skillParam: int, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
useResult = self.__THUAI6Stub.UseSkill( useResult = self.__THUAI6Stub.UseSkill(
THUAI62Proto.THUAI62ProtobufSkill(skillID, playerID))
THUAI62Proto.THUAI62ProtobufSkill(skillID, skillParam, playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -73,8 +107,13 @@ class Communication:


def SendMessage(self, toID: int, message: Union[str, bytes], playerID: int) -> bool: def SendMessage(self, toID: int, message: Union[str, bytes], playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
sendResult = self.__THUAI6Stub.SendMessage( sendResult = self.__THUAI6Stub.SendMessage(
THUAI62Proto.THUAI62ProtobufSend(message, toID, playerID))
THUAI62Proto.THUAI62ProtobufSend(message, toID, playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -82,8 +121,13 @@ class Communication:


def Graduate(self, playerID: int) -> bool: def Graduate(self, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
escapeResult = self.__THUAI6Stub.Graduate( escapeResult = self.__THUAI6Stub.Graduate(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -91,8 +135,13 @@ class Communication:


def StartLearning(self, playerID: int) -> bool: def StartLearning(self, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
learnResult = self.__THUAI6Stub.StartLearning( learnResult = self.__THUAI6Stub.StartLearning(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -100,8 +149,13 @@ class Communication:


def StartEncourageMate(self, playerID: int, mateID: int) -> bool: def StartEncourageMate(self, playerID: int, mateID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
helpResult = self.__THUAI6Stub.StartTreatMate( helpResult = self.__THUAI6Stub.StartTreatMate(
THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID))
THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -109,8 +163,13 @@ class Communication:


def StartRouseMate(self, playerID: int, mateID: int) -> bool: def StartRouseMate(self, playerID: int, mateID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
helpResult = self.__THUAI6Stub.StartRescueMate( helpResult = self.__THUAI6Stub.StartRescueMate(
THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID))
THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -118,8 +177,13 @@ class Communication:


def Attack(self, angle: float, playerID: int) -> bool: def Attack(self, angle: float, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
attackResult = self.__THUAI6Stub.Attack( attackResult = self.__THUAI6Stub.Attack(
THUAI62Proto.THUAI62ProtobufAttack(angle, playerID))
THUAI62Proto.THUAI62ProtobufAttack(angle, playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -127,8 +191,13 @@ class Communication:


def OpenDoor(self, playerID: int) -> bool: def OpenDoor(self, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
openResult = self.__THUAI6Stub.OpenDoor( openResult = self.__THUAI6Stub.OpenDoor(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -136,8 +205,13 @@ class Communication:


def CloseDoor(self, playerID: int) -> bool: def CloseDoor(self, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
closeResult = self.__THUAI6Stub.CloseDoor( closeResult = self.__THUAI6Stub.CloseDoor(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -145,8 +219,13 @@ class Communication:


def SkipWindow(self, playerID: int) -> bool: def SkipWindow(self, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
skipResult = self.__THUAI6Stub.SkipWindow( skipResult = self.__THUAI6Stub.SkipWindow(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -154,8 +233,13 @@ class Communication:


def StartOpenGate(self, playerID: int) -> bool: def StartOpenGate(self, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
openResult = self.__THUAI6Stub.StartOpenGate( openResult = self.__THUAI6Stub.StartOpenGate(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -163,8 +247,13 @@ class Communication:


def StartOpenChest(self, playerID: int) -> bool: def StartOpenChest(self, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if self.__counter >= self.__limit:
return False
self.__counter += 1
openResult = self.__THUAI6Stub.StartOpenChest( openResult = self.__THUAI6Stub.StartOpenChest(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -172,8 +261,17 @@ class Communication:


def EndAllAction(self, playerID: int) -> bool: def EndAllAction(self, playerID: int) -> bool:
try: try:
with self.__mtxLimit:
if (
self.__counter >= self.__limit
or self.__counterMove >= self.__moveLimit
):
return False
self.__counter += 1
self.__counterMove += 1
endResult = self.__THUAI6Stub.EndAllAction( endResult = self.__THUAI6Stub.EndAllAction(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -182,7 +280,8 @@ class Communication:
def TryConnection(self, playerID: int) -> bool: def TryConnection(self, playerID: int) -> bool:
try: try:
connectResult = self.__THUAI6Stub.TryConnection( connectResult = self.__THUAI6Stub.TryConnection(
THUAI62Proto.THUAI62ProtobufID(playerID))
THUAI62Proto.THUAI62ProtobufID(playerID)
)
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False
else: else:
@@ -202,12 +301,16 @@ class Communication:
else: else:
studentType = THUAI6.StudentType.NullStudentType studentType = THUAI6.StudentType.NullStudentType
playerMsg = THUAI62Proto.THUAI62ProtobufPlayer( playerMsg = THUAI62Proto.THUAI62ProtobufPlayer(
playerID, playerType, studentType, Setting.trickerType())
playerID, playerType, studentType, Setting.trickerType()
)
for msg in self.__THUAI6Stub.AddPlayer(playerMsg): for msg in self.__THUAI6Stub.AddPlayer(playerMsg):
with self.__cvMessage: with self.__cvMessage:
self.__haveNewMessage = True self.__haveNewMessage = True
self.__message2Client = msg self.__message2Client = msg
self.__cvMessage.notify() self.__cvMessage.notify()
with self.__mtxLimit:
self.__counter = 0
self.__counterMove = 0
except grpc.RpcError as e: except grpc.RpcError as e:
return return




+ 219
- 188
CAPI/python/PyAPI/DebugAPI.py View File

@@ -10,22 +10,34 @@ from PyAPI.Interface import ILogic, IStudentAPI, ITrickerAPI, IGameTimer, IAI




class StudentDebugAPI(IStudentAPI, IGameTimer): class StudentDebugAPI(IStudentAPI, IGameTimer):

def __init__(self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int) -> None:
def __init__(
self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int
) -> None:
self.__logic = logic self.__logic = logic
self.__pool = ThreadPoolExecutor(20) self.__pool = ThreadPoolExecutor(20)
self.__startPoint = datetime.datetime.now() self.__startPoint = datetime.datetime.now()
self.__logger = logging.getLogger("api " + str(playerID)) self.__logger = logging.getLogger("api " + str(playerID))
self.__logger.setLevel(logging.DEBUG) self.__logger.setLevel(logging.DEBUG)
formatter = logging.Formatter( formatter = logging.Formatter(
"[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s", '%H:%M:%S')
"[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s",
"%H:%M:%S",
)
# 确保文件存在 # 确保文件存在
if not os.path.exists(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"):
os.makedirs(os.path.dirname(os.path.dirname(
os.path.realpath(__file__))) + "/logs")

fileHandler = logging.FileHandler(os.path.dirname(
os.path.dirname(os.path.realpath(__file__))) + "/logs/api-" + str(playerID) + "-log.txt", mode="w+", encoding="utf-8")
if not os.path.exists(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"
):
os.makedirs(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"
)

fileHandler = logging.FileHandler(
os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+ "/logs/api-"
+ str(playerID)
+ "-log.txt",
mode="w+",
encoding="utf-8",
)
screenHandler = logging.StreamHandler() screenHandler = logging.StreamHandler()
if file: if file:
fileHandler.setLevel(logging.DEBUG) fileHandler.setLevel(logging.DEBUG)
@@ -43,13 +55,13 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):


def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"Move: timeInMilliseconds = {timeInMilliseconds}, angle = {angle}, called at {self.__GetTime()}ms")
f"Move: timeInMilliseconds = {timeInMilliseconds}, angle = {angle}, called at {self.__GetTime()}ms"
)


def logMove() -> bool: def logMove() -> bool:
result = self.__logic.Move(timeInMilliseconds, angle) result = self.__logic.Move(timeInMilliseconds, angle)
if not result: if not result:
self.__logger.warning(
f"Move: failed at {self.__GetTime()}ms")
self.__logger.warning(f"Move: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logMove) return self.__pool.submit(logMove)
@@ -69,14 +81,12 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):
return self.Move(timeInMilliseconds, 0) return self.Move(timeInMilliseconds, 0)


def Attack(self, angle: float) -> Future[bool]: def Attack(self, angle: float) -> Future[bool]:
self.__logger.info(
f"Attack: angle = {angle}, called at {self.__GetTime()}ms")
self.__logger.info(f"Attack: angle = {angle}, called at {self.__GetTime()}ms")


def logAttack() -> bool: def logAttack() -> bool:
result = self.__logic.Attack(angle) result = self.__logic.Attack(angle)
if not result: if not result:
self.__logger.warning(
f"Attack: failed at {self.__GetTime()}ms")
self.__logger.warning(f"Attack: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logAttack) return self.__pool.submit(logAttack)
@@ -85,131 +95,119 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):


def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: def PickProp(self, propType: THUAI6.PropType) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"PickProp: prop = {propType.name}, called at {self.__GetTime()}ms")
f"PickProp: prop = {propType.name}, called at {self.__GetTime()}ms"
)


def logPick() -> bool: def logPick() -> bool:
result = self.__logic.PickProp(propType) result = self.__logic.PickProp(propType)
if not result: if not result:
self.__logger.warning(
f"PickProp: failed at {self.__GetTime()}ms")
self.__logger.warning(f"PickProp: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logPick) return self.__pool.submit(logPick)


def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: def UseProp(self, propType: THUAI6.PropType) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"UseProp: prop = {propType.name}, called at {self.__GetTime()}ms")
f"UseProp: prop = {propType.name}, called at {self.__GetTime()}ms"
)


def logUse() -> bool: def logUse() -> bool:
result = self.__logic.UseProp(propType) result = self.__logic.UseProp(propType)
if not result: if not result:
self.__logger.warning(
f"UseProp: failed at {self.__GetTime()}ms")
self.__logger.warning(f"UseProp: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logUse) return self.__pool.submit(logUse)


def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms")
f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms"
)


def logThrow() -> bool: def logThrow() -> bool:
result = self.__logic.ThrowProp(propType) result = self.__logic.ThrowProp(propType)
if not result: if not result:
self.__logger.warning(
f"ThrowProp: failed at {self.__GetTime()}ms")
self.__logger.warning(f"ThrowProp: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logThrow) return self.__pool.submit(logThrow)


def UseSkill(self, skillID: int) -> Future[bool]:
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms")
f"UseSkill: skillID = {skillID}, skillParam = {skillParam}, called at {self.__GetTime()}ms"
)


def logUse() -> bool: def logUse() -> bool:
result = self.__logic.UseSkill(skillID)
result = self.__logic.UseSkill(skillID, skillParam)
if not result: if not result:
self.__logger.warning(
f"UseSkill: failed at {self.__GetTime()}ms")
self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logUse) return self.__pool.submit(logUse)


# 与地图交互相关 # 与地图交互相关
def OpenDoor(self) -> Future[bool]: def OpenDoor(self) -> Future[bool]:
self.__logger.info(
f"OpenDoor: called at {self.__GetTime()}ms")
self.__logger.info(f"OpenDoor: called at {self.__GetTime()}ms")


def logOpen() -> bool: def logOpen() -> bool:
result = self.__logic.OpenDoor() result = self.__logic.OpenDoor()
if not result: if not result:
self.__logger.warning(
f"OpenDoor: failed at {self.__GetTime()}ms")
self.__logger.warning(f"OpenDoor: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logOpen) return self.__pool.submit(logOpen)


def CloseDoor(self) -> Future[bool]: def CloseDoor(self) -> Future[bool]:
self.__logger.info(
f"CloseDoor: called at {self.__GetTime()}ms")
self.__logger.info(f"CloseDoor: called at {self.__GetTime()}ms")


def logClose() -> bool: def logClose() -> bool:
result = self.__logic.CloseDoor() result = self.__logic.CloseDoor()
if not result: if not result:
self.__logger.warning(
f"CloseDoor: failed at {self.__GetTime()}ms")
self.__logger.warning(f"CloseDoor: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logClose) return self.__pool.submit(logClose)


def SkipWindow(self) -> Future[bool]: def SkipWindow(self) -> Future[bool]:
self.__logger.info(
f"SkipWindow: called at {self.__GetTime()}ms")
self.__logger.info(f"SkipWindow: called at {self.__GetTime()}ms")


def logSkip() -> bool: def logSkip() -> bool:
result = self.__logic.SkipWindow() result = self.__logic.SkipWindow()
if not result: if not result:
self.__logger.warning(
f"SkipWindow: failed at {self.__GetTime()}ms")
self.__logger.warning(f"SkipWindow: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logSkip) return self.__pool.submit(logSkip)


def StartOpenGate(self) -> Future[bool]: def StartOpenGate(self) -> Future[bool]:
self.__logger.info(
f"StartOpenGate: called at {self.__GetTime()}ms")
self.__logger.info(f"StartOpenGate: called at {self.__GetTime()}ms")


def logStart() -> bool: def logStart() -> bool:
result = self.__logic.StartOpenGate() result = self.__logic.StartOpenGate()
if not result: if not result:
self.__logger.warning(
f"StartOpenGate: failed at {self.__GetTime()}ms")
self.__logger.warning(f"StartOpenGate: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logStart) return self.__pool.submit(logStart)


def StartOpenChest(self) -> Future[bool]: def StartOpenChest(self) -> Future[bool]:
self.__logger.info(
f"StartOpenChest: called at {self.__GetTime()}ms")
self.__logger.info(f"StartOpenChest: called at {self.__GetTime()}ms")


def logStart() -> bool: def logStart() -> bool:
result = self.__logic.StartOpenChest() result = self.__logic.StartOpenChest()
if not result: if not result:
self.__logger.warning(
f"StartOpenChest: failed at {self.__GetTime()}ms")
self.__logger.warning(f"StartOpenChest: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logStart) return self.__pool.submit(logStart)


def EndAllAction(self) -> Future[bool]: def EndAllAction(self) -> Future[bool]:
self.__logger.info(
f"EndAllAction: called at {self.__GetTime()}ms")
self.__logger.info(f"EndAllAction: called at {self.__GetTime()}ms")


def logEnd() -> bool: def logEnd() -> bool:
result = self.__logic.EndAllAction() result = self.__logic.EndAllAction()
if not result: if not result:
self.__logger.warning(
f"EndAllAction: failed at {self.__GetTime()}ms")
self.__logger.warning(f"EndAllAction: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logEnd) return self.__pool.submit(logEnd)
@@ -218,40 +216,35 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):


def SendMessage(self, toID: int, message: Union[str, bytes]) -> Future[bool]: def SendMessage(self, toID: int, message: Union[str, bytes]) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"SendMessage: toID = {toID}, message = {message}, called at {self.__GetTime()}ms")
f"SendMessage: toID = {toID}, message = {message}, called at {self.__GetTime()}ms"
)


def logSend() -> bool: def logSend() -> bool:
result = self.__logic.SendMessage(toID, message) result = self.__logic.SendMessage(toID, message)
if not result: if not result:
self.__logger.warning(
f"SendMessage: failed at {self.__GetTime()}ms")
self.__logger.warning(f"SendMessage: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logSend) return self.__pool.submit(logSend)


def HaveMessage(self) -> bool: def HaveMessage(self) -> bool:
self.__logger.info(
f"HaveMessage: called at {self.__GetTime()}ms")
self.__logger.info(f"HaveMessage: called at {self.__GetTime()}ms")
result = self.__logic.HaveMessage() result = self.__logic.HaveMessage()
if not result: if not result:
self.__logger.warning(
f"HaveMessage: failed at {self.__GetTime()}ms")
self.__logger.warning(f"HaveMessage: failed at {self.__GetTime()}ms")
return result return result


def GetMessage(self) -> Tuple[int, Union[str, bytes]]: def GetMessage(self) -> Tuple[int, Union[str, bytes]]:
self.__logger.info(
f"GetMessage: called at {self.__GetTime()}ms")
self.__logger.info(f"GetMessage: called at {self.__GetTime()}ms")
result = self.__logic.GetMessage() result = self.__logic.GetMessage()
if result[0] == -1: if result[0] == -1:
self.__logger.warning(
f"GetMessage: failed at {self.__GetTime()}ms")
self.__logger.warning(f"GetMessage: failed at {self.__GetTime()}ms")
return result return result


# 等待下一帧 # 等待下一帧


def Wait(self) -> bool: def Wait(self) -> bool:
self.__logger.info(
f"Wait: called at {self.__GetTime()}ms")
self.__logger.info(f"Wait: called at {self.__GetTime()}ms")
if self.__logic.GetCounter() == -1: if self.__logic.GetCounter() == -1:
return False return False
else: else:
@@ -305,7 +298,13 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):
return self.__logic.GetGameInfo() return self.__logic.GetGameInfo()


def HaveView(self, gridX: int, gridY: int) -> bool: def HaveView(self, gridX: int, gridY: int) -> bool:
return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange)
return self.__logic.HaveView(
gridX,
gridY,
self.GetSelfInfo().x,
self.GetSelfInfo().y,
self.GetSelfInfo().viewRange,
)


# 用于DEBUG的输出函数,仅在DEBUG模式下有效 # 用于DEBUG的输出函数,仅在DEBUG模式下有效


@@ -316,20 +315,26 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):
for student in self.__logic.GetStudents(): for student in self.__logic.GetStudents():
self.__logger.info("******Student Info******") self.__logger.info("******Student Info******")
self.__logger.info( self.__logger.info(
f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}")
f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}"
)
self.__logger.info( self.__logger.info(
f"speed={student.speed}, view range={student.viewRange}, place={student.place.name}, radius={student.radius}")
f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}"
)
self.__logger.info( self.__logger.info(
f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}")
f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}"
)
studentProp = "" studentProp = ""
for prop in student.prop: for prop in student.prop:
studentProp += prop.name + ", " studentProp += prop.name + ", "
self.__logger.info( self.__logger.info(
f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}")
f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}"
)
self.__logger.info( self.__logger.info(
f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}")
f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}"
)
self.__logger.info( self.__logger.info(
f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}")
f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}"
)
studentBuff = "" studentBuff = ""
for buff in student.buff: for buff in student.buff:
studentBuff += buff.name + ", " studentBuff += buff.name + ", "
@@ -340,18 +345,23 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):
for tricker in self.__logic.GetTrickers(): for tricker in self.__logic.GetTrickers():
self.__logger.info("******Tricker Info******") self.__logger.info("******Tricker Info******")
self.__logger.info( self.__logger.info(
f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}")
f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}"
)
self.__logger.info( self.__logger.info(
f"speed={tricker.speed}, view range={tricker.viewRange}, place={tricker.place.name}, radius={tricker.radius}")
f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}"
)
self.__logger.info( self.__logger.info(
f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}")
f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}"
)
trickerProp = "" trickerProp = ""
for prop in tricker.prop: for prop in tricker.prop:
trickerProp += prop.name + ", " trickerProp += prop.name + ", "
self.__logger.info( self.__logger.info(
f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}")
f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}"
)
self.__logger.info( self.__logger.info(
f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}")
f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}"
)
trickerBuff = "" trickerBuff = ""
for buff in tricker.buff: for buff in tricker.buff:
trickerBuff += buff.name + ", " trickerBuff += buff.name + ", "
@@ -362,27 +372,34 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):
for prop in self.__logic.GetProps(): for prop in self.__logic.GetProps():
self.__logger.info("******Prop Info******") self.__logger.info("******Prop Info******")
self.__logger.info( self.__logger.info(
f"GUID={prop.guid}, x={prop.x}, y={prop.y}, place={prop.place.name}, facing direction={prop.facingDirection}")
f"GUID={prop.guid}, x={prop.x}, y={prop.y}, facing direction={prop.facingDirection}"
)
self.__logger.info("*********************") self.__logger.info("*********************")


def PrintSelfInfo(self) -> None: def PrintSelfInfo(self) -> None:
student = cast(THUAI6.Student, self.__logic.GetSelfInfo()) student = cast(THUAI6.Student, self.__logic.GetSelfInfo())
self.__logger.info("******Student Info******") self.__logger.info("******Student Info******")
self.__logger.info( self.__logger.info(
f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}")
f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}"
)
self.__logger.info( self.__logger.info(
f"speed={student.speed}, view range={student.viewRange}, place={student.place.name}, radius={student.radius}")
f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}"
)
self.__logger.info( self.__logger.info(
f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}")
f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}"
)
studentProp = "" studentProp = ""
for prop in student.prop: for prop in student.prop:
studentProp += prop.name + ", " studentProp += prop.name + ", "
self.__logger.info( self.__logger.info(
f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}")
f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}"
)
self.__logger.info( self.__logger.info(
f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}")
f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}"
)
self.__logger.info( self.__logger.info(
f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}")
f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}"
)
studentBuff = "" studentBuff = ""
for buff in student.buff: for buff in student.buff:
studentBuff += buff.name + ", " studentBuff += buff.name + ", "
@@ -392,53 +409,47 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):
# 人类阵营的特殊函数 # 人类阵营的特殊函数


def Graduate(self) -> Future[bool]: def Graduate(self) -> Future[bool]:
self.__logger.info(
f"Graduate: called at {self.__GetTime()}ms")
self.__logger.info(f"Graduate: called at {self.__GetTime()}ms")


def logGraduate() -> bool: def logGraduate() -> bool:
result = self.__logic.Graduate() result = self.__logic.Graduate()
if not result: if not result:
self.__logger.warning(
f"Graduate: failed at {self.__GetTime()}ms")
self.__logger.warning(f"Graduate: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logGraduate) return self.__pool.submit(logGraduate)


def StartLearning(self) -> Future[bool]: def StartLearning(self) -> Future[bool]:
self.__logger.info(
f"StartLearning: called at {self.__GetTime()}ms")
self.__logger.info(f"StartLearning: called at {self.__GetTime()}ms")


def logStart() -> bool: def logStart() -> bool:
result = self.__logic.StartLearning() result = self.__logic.StartLearning()
if not result: if not result:
self.__logger.warning(
f"StartLearning: failed at {self.__GetTime()}ms")
self.__logger.warning(f"StartLearning: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logStart) return self.__pool.submit(logStart)


def StartEncourageMate(self, mateID: int) -> Future[bool]: def StartEncourageMate(self, mateID: int) -> Future[bool]:
self.__logger.info(
f"StartEncourageMate: called at {self.__GetTime()}ms")
self.__logger.info(f"StartEncourageMate: called at {self.__GetTime()}ms")


def logStartEncourageMate() -> bool: def logStartEncourageMate() -> bool:
result = self.__logic.StartEncourageMate(mateID) result = self.__logic.StartEncourageMate(mateID)
if not result: if not result:
self.__logger.warning( self.__logger.warning(
f"StartEncourageMate: failed at {self.__GetTime()}ms")
f"StartEncourageMate: failed at {self.__GetTime()}ms"
)
return result return result


return self.__pool.submit(logStartEncourageMate) return self.__pool.submit(logStartEncourageMate)


def StartRouseMate(self, mateID: int) -> Future[bool]: def StartRouseMate(self, mateID: int) -> Future[bool]:
self.__logger.info(
f"StartRouseMate: called at {self.__GetTime()}ms")
self.__logger.info(f"StartRouseMate: called at {self.__GetTime()}ms")


def logStartRouseMate() -> bool: def logStartRouseMate() -> bool:
result = self.__logic.StartRouseMate(mateID) result = self.__logic.StartRouseMate(mateID)
if not result: if not result:
self.__logger.warning(
f"StartRouseMate: failed at {self.__GetTime()}ms")
self.__logger.warning(f"StartRouseMate: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logStartRouseMate) return self.__pool.submit(logStartRouseMate)
@@ -449,7 +460,9 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):
# Timer用 # Timer用


def __GetTime(self) -> float: def __GetTime(self) -> float:
return (datetime.datetime.now() - self.__startPoint) / datetime.timedelta(milliseconds=1)
return (datetime.datetime.now() - self.__startPoint) / datetime.timedelta(
milliseconds=1
)


def StartTimer(self) -> None: def StartTimer(self) -> None:
self.__startPoint = datetime.datetime.now() self.__startPoint = datetime.datetime.now()
@@ -464,21 +477,33 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):




class TrickerDebugAPI(ITrickerAPI, IGameTimer): class TrickerDebugAPI(ITrickerAPI, IGameTimer):

def __init__(self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int) -> None:
def __init__(
self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int
) -> None:
self.__logic = logic self.__logic = logic
self.__pool = ThreadPoolExecutor(20) self.__pool = ThreadPoolExecutor(20)
self.__logger = logging.getLogger("api " + str(playerID)) self.__logger = logging.getLogger("api " + str(playerID))
self.__logger.setLevel(logging.DEBUG) self.__logger.setLevel(logging.DEBUG)
formatter = logging.Formatter( formatter = logging.Formatter(
"[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s", '%H:%M:%S')
"[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s",
"%H:%M:%S",
)
# 确保文件存在 # 确保文件存在
if not os.path.exists(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"):
os.makedirs(os.path.dirname(os.path.dirname(
os.path.realpath(__file__))) + "/logs")

fileHandler = logging.FileHandler(os.path.dirname(
os.path.dirname(os.path.realpath(__file__))) + "/logs/api-" + str(playerID) + "-log.txt", mode="w+", encoding="utf-8")
if not os.path.exists(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"
):
os.makedirs(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"
)

fileHandler = logging.FileHandler(
os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+ "/logs/api-"
+ str(playerID)
+ "-log.txt",
mode="w+",
encoding="utf-8",
)
screenHandler = logging.StreamHandler() screenHandler = logging.StreamHandler()
if file: if file:
fileHandler.setLevel(logging.DEBUG) fileHandler.setLevel(logging.DEBUG)
@@ -496,13 +521,13 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):


def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"Move: timeInMilliseconds = {timeInMilliseconds}, angle = {angle}, called at {self.__GetTime()}ms")
f"Move: timeInMilliseconds = {timeInMilliseconds}, angle = {angle}, called at {self.__GetTime()}ms"
)


def logMove() -> bool: def logMove() -> bool:
result = self.__logic.Move(timeInMilliseconds, angle) result = self.__logic.Move(timeInMilliseconds, angle)
if not result: if not result:
self.__logger.warning(
f"Move: failed at {self.__GetTime()}ms")
self.__logger.warning(f"Move: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logMove) return self.__pool.submit(logMove)
@@ -524,14 +549,12 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):
# 道具和技能相关 # 道具和技能相关


def Attack(self, angle: float) -> Future[bool]: def Attack(self, angle: float) -> Future[bool]:
self.__logger.info(
f"Attack: angle = {angle}, called at {self.__GetTime()}ms")
self.__logger.info(f"Attack: angle = {angle}, called at {self.__GetTime()}ms")


def logAttack() -> bool: def logAttack() -> bool:
result = self.__logic.Attack(angle) result = self.__logic.Attack(angle)
if not result: if not result:
self.__logger.warning(
f"Attack: failed at {self.__GetTime()}ms")
self.__logger.warning(f"Attack: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logAttack) return self.__pool.submit(logAttack)
@@ -540,131 +563,119 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):


def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: def PickProp(self, propType: THUAI6.PropType) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"PickProp: prop = {propType.name}, called at {self.__GetTime()}ms")
f"PickProp: prop = {propType.name}, called at {self.__GetTime()}ms"
)


def logPick() -> bool: def logPick() -> bool:
result = self.__logic.PickProp(propType) result = self.__logic.PickProp(propType)
if not result: if not result:
self.__logger.warning(
f"PickProp: failed at {self.__GetTime()}ms")
self.__logger.warning(f"PickProp: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logPick) return self.__pool.submit(logPick)


def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: def UseProp(self, propType: THUAI6.PropType) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"UseProp: prop = {propType.name}, called at {self.__GetTime()}ms")
f"UseProp: prop = {propType.name}, called at {self.__GetTime()}ms"
)


def logUse() -> bool: def logUse() -> bool:
result = self.__logic.UseProp(propType) result = self.__logic.UseProp(propType)
if not result: if not result:
self.__logger.warning(
f"UseProp: failed at {self.__GetTime()}ms")
self.__logger.warning(f"UseProp: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logUse) return self.__pool.submit(logUse)


def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms")
f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms"
)


def logThrow() -> bool: def logThrow() -> bool:
result = self.__logic.ThrowProp(propType) result = self.__logic.ThrowProp(propType)
if not result: if not result:
self.__logger.warning(
f"ThrowProp: failed at {self.__GetTime()}ms")
self.__logger.warning(f"ThrowProp: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logThrow) return self.__pool.submit(logThrow)


def UseSkill(self, skillID: int) -> Future[bool]:
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms")
f"UseSkill: skillID = {skillID}, skillParam = {skillParam}, called at {self.__GetTime()}ms"
)


def logUse() -> bool: def logUse() -> bool:
result = self.__logic.UseSkill(skillID)
result = self.__logic.UseSkill(skillID, skillParam)
if not result: if not result:
self.__logger.warning(
f"UseSkill: failed at {self.__GetTime()}ms")
self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logUse) return self.__pool.submit(logUse)


# 与地图交互相关 # 与地图交互相关
def OpenDoor(self) -> Future[bool]: def OpenDoor(self) -> Future[bool]:
self.__logger.info(
f"OpenDoor: called at {self.__GetTime()}ms")
self.__logger.info(f"OpenDoor: called at {self.__GetTime()}ms")


def logOpen() -> bool: def logOpen() -> bool:
result = self.__logic.OpenDoor() result = self.__logic.OpenDoor()
if not result: if not result:
self.__logger.warning(
f"OpenDoor: failed at {self.__GetTime()}ms")
self.__logger.warning(f"OpenDoor: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logOpen) return self.__pool.submit(logOpen)


def CloseDoor(self) -> Future[bool]: def CloseDoor(self) -> Future[bool]:
self.__logger.info(
f"CloseDoor: called at {self.__GetTime()}ms")
self.__logger.info(f"CloseDoor: called at {self.__GetTime()}ms")


def logClose() -> bool: def logClose() -> bool:
result = self.__logic.CloseDoor() result = self.__logic.CloseDoor()
if not result: if not result:
self.__logger.warning(
f"CloseDoor: failed at {self.__GetTime()}ms")
self.__logger.warning(f"CloseDoor: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logClose) return self.__pool.submit(logClose)


def SkipWindow(self) -> Future[bool]: def SkipWindow(self) -> Future[bool]:
self.__logger.info(
f"SkipWindow: called at {self.__GetTime()}ms")
self.__logger.info(f"SkipWindow: called at {self.__GetTime()}ms")


def logSkip() -> bool: def logSkip() -> bool:
result = self.__logic.SkipWindow() result = self.__logic.SkipWindow()
if not result: if not result:
self.__logger.warning(
f"SkipWindow: failed at {self.__GetTime()}ms")
self.__logger.warning(f"SkipWindow: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logSkip) return self.__pool.submit(logSkip)


def StartOpenGate(self) -> Future[bool]: def StartOpenGate(self) -> Future[bool]:
self.__logger.info(
f"StartOpenGate: called at {self.__GetTime()}ms")
self.__logger.info(f"StartOpenGate: called at {self.__GetTime()}ms")


def logStart() -> bool: def logStart() -> bool:
result = self.__logic.StartOpenGate() result = self.__logic.StartOpenGate()
if not result: if not result:
self.__logger.warning(
f"StartOpenGate: failed at {self.__GetTime()}ms")
self.__logger.warning(f"StartOpenGate: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logStart) return self.__pool.submit(logStart)


def StartOpenChest(self) -> Future[bool]: def StartOpenChest(self) -> Future[bool]:
self.__logger.info(
f"StartOpenChest: called at {self.__GetTime()}ms")
self.__logger.info(f"StartOpenChest: called at {self.__GetTime()}ms")


def logStart() -> bool: def logStart() -> bool:
result = self.__logic.StartOpenChest() result = self.__logic.StartOpenChest()
if not result: if not result:
self.__logger.warning(
f"StartOpenChest: failed at {self.__GetTime()}ms")
self.__logger.warning(f"StartOpenChest: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logStart) return self.__pool.submit(logStart)


def EndAllAction(self) -> Future[bool]: def EndAllAction(self) -> Future[bool]:
self.__logger.info(
f"EndAllAction: called at {self.__GetTime()}ms")
self.__logger.info(f"EndAllAction: called at {self.__GetTime()}ms")


def logEnd() -> bool: def logEnd() -> bool:
result = self.__logic.EndAllAction() result = self.__logic.EndAllAction()
if not result: if not result:
self.__logger.warning(
f"EndAllAction: failed at {self.__GetTime()}ms")
self.__logger.warning(f"EndAllAction: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logEnd) return self.__pool.submit(logEnd)
@@ -673,40 +684,35 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):


def SendMessage(self, toID: int, message: Union[str, bytes]) -> Future[bool]: def SendMessage(self, toID: int, message: Union[str, bytes]) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"SendMessage: toID = {toID}, message = {message}, called at {self.__GetTime()}ms")
f"SendMessage: toID = {toID}, message = {message}, called at {self.__GetTime()}ms"
)


def logSend() -> bool: def logSend() -> bool:
result = self.__logic.SendMessage(toID, message) result = self.__logic.SendMessage(toID, message)
if not result: if not result:
self.__logger.warning(
f"SendMessage: failed at {self.__GetTime()}ms")
self.__logger.warning(f"SendMessage: failed at {self.__GetTime()}ms")
return result return result


return self.__pool.submit(logSend) return self.__pool.submit(logSend)


def HaveMessage(self) -> bool: def HaveMessage(self) -> bool:
self.__logger.info(
f"HaveMessage: called at {self.__GetTime()}ms")
self.__logger.info(f"HaveMessage: called at {self.__GetTime()}ms")
result = self.__logic.HaveMessage() result = self.__logic.HaveMessage()
if not result: if not result:
self.__logger.warning(
f"HaveMessage: failed at {self.__GetTime()}ms")
self.__logger.warning(f"HaveMessage: failed at {self.__GetTime()}ms")
return result return result


def GetMessage(self) -> Tuple[int, Union[str, bytes]]: def GetMessage(self) -> Tuple[int, Union[str, bytes]]:
self.__logger.info(
f"GetMessage: called at {self.__GetTime()}ms")
self.__logger.info(f"GetMessage: called at {self.__GetTime()}ms")
result = self.__logic.GetMessage() result = self.__logic.GetMessage()
if result[0] == -1: if result[0] == -1:
self.__logger.warning(
f"GetMessage: failed at {self.__GetTime()}ms")
self.__logger.warning(f"GetMessage: failed at {self.__GetTime()}ms")
return result return result


# 等待下一帧 # 等待下一帧


def Wait(self) -> bool: def Wait(self) -> bool:
self.__logger.info(
f"Wait: called at {self.__GetTime()}ms")
self.__logger.info(f"Wait: called at {self.__GetTime()}ms")
if self.__logic.GetCounter() == -1: if self.__logic.GetCounter() == -1:
return False return False
else: else:
@@ -760,7 +766,13 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):
return self.__logic.GetGameInfo() return self.__logic.GetGameInfo()


def HaveView(self, gridX: int, gridY: int) -> bool: def HaveView(self, gridX: int, gridY: int) -> bool:
return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange)
return self.__logic.HaveView(
gridX,
gridY,
self.GetSelfInfo().x,
self.GetSelfInfo().y,
self.GetSelfInfo().viewRange,
)


# 用于DEBUG的输出函数,仅在DEBUG模式下有效 # 用于DEBUG的输出函数,仅在DEBUG模式下有效


@@ -771,20 +783,26 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):
for student in self.__logic.GetStudents(): for student in self.__logic.GetStudents():
self.__logger.info("******Student Info******") self.__logger.info("******Student Info******")
self.__logger.info( self.__logger.info(
f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}")
f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}"
)
self.__logger.info( self.__logger.info(
f"speed={student.speed}, view range={student.viewRange}, place={student.place.name}, radius={student.radius}")
f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}"
)
self.__logger.info( self.__logger.info(
f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}")
f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}"
)
studentProp = "" studentProp = ""
for prop in student.prop: for prop in student.prop:
studentProp += prop.name + ", " studentProp += prop.name + ", "
self.__logger.info( self.__logger.info(
f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}")
f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}"
)
self.__logger.info( self.__logger.info(
f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}")
f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}"
)
self.__logger.info( self.__logger.info(
f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}")
f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}"
)
studentBuff = "" studentBuff = ""
for buff in student.buff: for buff in student.buff:
studentBuff += buff.name + ", " studentBuff += buff.name + ", "
@@ -795,18 +813,23 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):
for tricker in self.__logic.GetTrickers(): for tricker in self.__logic.GetTrickers():
self.__logger.info("******Tricker Info******") self.__logger.info("******Tricker Info******")
self.__logger.info( self.__logger.info(
f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}")
f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}"
)
self.__logger.info( self.__logger.info(
f"speed={tricker.speed}, view range={tricker.viewRange}, place={tricker.place.name}, radius={tricker.radius}")
f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}"
)
self.__logger.info( self.__logger.info(
f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}")
f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}"
)
trickerProp = "" trickerProp = ""
for prop in tricker.prop: for prop in tricker.prop:
trickerProp += prop.name + ", " trickerProp += prop.name + ", "
self.__logger.info( self.__logger.info(
f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}")
f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}"
)
self.__logger.info( self.__logger.info(
f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}")
f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}"
)
trickerBuff = "" trickerBuff = ""
for buff in tricker.buff: for buff in tricker.buff:
trickerBuff += buff.name + ", " trickerBuff += buff.name + ", "
@@ -817,25 +840,31 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):
for prop in self.__logic.GetProps(): for prop in self.__logic.GetProps():
self.__logger.info("******Prop Info******") self.__logger.info("******Prop Info******")
self.__logger.info( self.__logger.info(
f"GUID={prop.guid}, x={prop.x}, y={prop.y}, place={prop.place.name}, facing direction={prop.facingDirection}")
f"GUID={prop.guid}, x={prop.x}, y={prop.y}, facing direction={prop.facingDirection}"
)
self.__logger.info("*********************") self.__logger.info("*********************")


def PrintSelfInfo(self) -> None: def PrintSelfInfo(self) -> None:
tricker = cast(THUAI6.Tricker, self.__logic.GetSelfInfo()) tricker = cast(THUAI6.Tricker, self.__logic.GetSelfInfo())
self.__logger.info("******Tricker Info******") self.__logger.info("******Tricker Info******")
self.__logger.info( self.__logger.info(
f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}")
f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}"
)
self.__logger.info( self.__logger.info(
f"speed={tricker.speed}, view range={tricker.viewRange}, place={tricker.place.name}, radius={tricker.radius}")
f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}"
)
self.__logger.info( self.__logger.info(
f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}")
f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}"
)
trickerProp = "" trickerProp = ""
for prop in tricker.prop: for prop in tricker.prop:
trickerProp += prop.name + ", " trickerProp += prop.name + ", "
self.__logger.info( self.__logger.info(
f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}")
f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}"
)
self.__logger.info( self.__logger.info(
f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}")
f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}"
)
trickerBuff = "" trickerBuff = ""
for buff in tricker.buff: for buff in tricker.buff:
trickerBuff += buff.name + ", " trickerBuff += buff.name + ", "
@@ -850,7 +879,9 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):
# Timer用 # Timer用


def __GetTime(self) -> float: def __GetTime(self) -> float:
return (datetime.datetime.now() - self.__startPoint) / datetime.timedelta(milliseconds=1)
return (datetime.datetime.now() - self.__startPoint) / datetime.timedelta(
milliseconds=1
)


def StartTimer(self) -> None: def StartTimer(self) -> None:
self.__startPoint = datetime.datetime.now() self.__startPoint = datetime.datetime.now()


+ 5
- 8
CAPI/python/PyAPI/Interface.py View File

@@ -5,7 +5,6 @@ import PyAPI.structures as THUAI6




class ILogic(metaclass=ABCMeta): class ILogic(metaclass=ABCMeta):

# IAPI统一可用的接口 # IAPI统一可用的接口


@abstractmethod @abstractmethod
@@ -81,7 +80,7 @@ class ILogic(metaclass=ABCMeta):
pass pass


@abstractmethod @abstractmethod
def UseSkill(self, skillID: int) -> bool:
def UseSkill(self, skillID: int, skillParam: int) -> bool:
pass pass


@abstractmethod @abstractmethod
@@ -155,12 +154,13 @@ class ILogic(metaclass=ABCMeta):
pass pass


@abstractmethod @abstractmethod
def HaveView(self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int) -> bool:
def HaveView(
self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int
) -> bool:
pass pass




class IAPI(metaclass=ABCMeta): class IAPI(metaclass=ABCMeta):

# 选手可执行的操作 # 选手可执行的操作
# 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴


@@ -201,7 +201,7 @@ class IAPI(metaclass=ABCMeta):
pass pass


@abstractmethod @abstractmethod
def UseSkill(self, skillID: int) -> Future[bool]:
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
pass pass


@abstractmethod @abstractmethod
@@ -346,7 +346,6 @@ class IAPI(metaclass=ABCMeta):




class IStudentAPI(IAPI, metaclass=ABCMeta): class IStudentAPI(IAPI, metaclass=ABCMeta):

# 人类阵营的特殊函数 # 人类阵营的特殊函数


@abstractmethod @abstractmethod
@@ -371,7 +370,6 @@ class IStudentAPI(IAPI, metaclass=ABCMeta):




class ITrickerAPI(IAPI, metaclass=ABCMeta): class ITrickerAPI(IAPI, metaclass=ABCMeta):

# 屠夫阵营的特殊函数 # 屠夫阵营的特殊函数


@abstractmethod @abstractmethod
@@ -390,7 +388,6 @@ class IAI(metaclass=ABCMeta):




class IGameTimer(metaclass=ABCMeta): class IGameTimer(metaclass=ABCMeta):

# 用于计时的接口 # 用于计时的接口


@abstractmethod @abstractmethod


+ 1
- 0
CAPI/python/PyAPI/State.py View File

@@ -15,6 +15,7 @@ class State:
self.mapInfo = THUAI6.GameMap() self.mapInfo = THUAI6.GameMap()
self.gameInfo = THUAI6.GameInfo() self.gameInfo = THUAI6.GameInfo()
self.guids = [] self.guids = []

teamScore: int teamScore: int
self: Union[THUAI6.Student, THUAI6.Tricker] self: Union[THUAI6.Student, THUAI6.Tricker]




+ 115
- 90
CAPI/python/PyAPI/constants.py View File

@@ -10,27 +10,27 @@ class NoInstance:
class Constants(NoInstance): class Constants(NoInstance):
frameDuration = 50 # 每帧毫秒数 frameDuration = 50 # 每帧毫秒数
numOfGridPerCell = 1000 # 单位坐标数 numOfGridPerCell = 1000 # 单位坐标数
rows = 50 # 地图行数
cols = 50 # 地图列数
rows = 50 # 地图行数
cols = 50 # 地图列数


numOfClassroom = 10 # 教室数量 numOfClassroom = 10 # 教室数量
numOfChest = 8 # 宝箱数量
numOfChest = 8 # 宝箱数量


maxClassroomProgress = 10000000 # 教室最大进度 maxClassroomProgress = 10000000 # 教室最大进度
maxDoorProgress = 10000000 # 开关门最大进度
maxChestProgress = 10000000 # 宝箱最大进度
maxGateProgress = 18000 # 大门最大进度
maxDoorProgress = 10000000 # 开关门最大进度
maxChestProgress = 10000000 # 宝箱最大进度
maxGateProgress = 18000 # 大门最大进度


numOfRequiredClassroomForGate = 7 # 打开大门需要完成的教室数量
numOfRequiredClassroomForGate = 7 # 打开大门需要完成的教室数量
numOfRequiredClassroomForHiddenGate = 3 # 打开隐藏门需要完成的教室数量 numOfRequiredClassroomForHiddenGate = 3 # 打开隐藏门需要完成的教室数量


# 人物属性相关 # 人物属性相关
basicEncourageSpeed = 100 basicEncourageSpeed = 100
basicLearnSpeed = 123 basicLearnSpeed = 123
basicSpeedOfOpeningOrLocking = 4000
basicSpeedOfOpeningOrLocking = 5000
basicStudentSpeedOfClimbingThroughWindows = 1222 basicStudentSpeedOfClimbingThroughWindows = 1222
basicTrickerSpeedOfClimbingThroughWindows = 2540 basicTrickerSpeedOfClimbingThroughWindows = 2540
basicSpeedOfOpenChest = 1000
basicSpeedOfOpenChest = 1250


basicHp = 3000000 basicHp = 3000000
basicMaxGamingAddiction = 60000 basicMaxGamingAddiction = 60000
@@ -52,21 +52,21 @@ class Constants(NoInstance):
# 攻击相关 # 攻击相关


basicApOfTricker = 1500000 basicApOfTricker = 1500000
basicCD = 3000 # 初始子弹冷却
basicCastTime = 500 # 基本前摇时间
basicBackswing = 800 # 基本后摇时间
basicCD = 3000 # 初始子弹冷却
basicCastTime = 500 # 基本前摇时间
basicBackswing = 800 # 基本后摇时间
basicRecoveryFromHit = 3700 # 基本命中攻击恢复时长 basicRecoveryFromHit = 3700 # 基本命中攻击恢复时长
basicStunnedTimeOfStudent = 4300 basicStunnedTimeOfStudent = 4300


basicBulletMoveSpeed = 7400 # 基本子弹移动速度
basicBulletMoveSpeed = 7400 # 基本子弹移动速度
basicRemoteAttackRange = 6000 # 基本远程攻击范围 basicRemoteAttackRange = 6000 # 基本远程攻击范围
basicAttackShortRange = 2200 # 基本近程攻击范围
basicBulletBombRange = 2000 # 基本子弹爆炸范围
basicAttackShortRange = 2200 # 基本近程攻击范围
basicBulletBombRange = 2000 # 基本子弹爆炸范围


# 道具相关 # 道具相关


apPropAdd = basicApOfTricker * 12 / 10
apSpearAdd = basicApOfTricker * 6 / 10
apPropAdd = basicApOfTricker * 12 // 10
apSpearAdd = basicApOfTricker * 6 // 10


# 技能相关 # 技能相关
maxNumOfSkill = 3 maxNumOfSkill = 3
@@ -85,7 +85,7 @@ class Constants(NoInstance):


addedTimeOfSpeedWhenInspire = 1.6 addedTimeOfSpeedWhenInspire = 1.6
timeOfAddingSpeedWhenInspire = 6000 timeOfAddingSpeedWhenInspire = 6000
addHpWhenEncourage = basicHp / 4
addHpWhenEncourage = basicHp // 4


checkIntervalWhenShowTime = 200 checkIntervalWhenShowTime = 200
addAddictionPer100msWhenShowTime = 300 addAddictionPer100msWhenShowTime = 300
@@ -96,10 +96,10 @@ class Assassin:
concealment = 1.5 * Constants.basicConcealment concealment = 1.5 * Constants.basicConcealment
alertnessRadius = (int)(1.3 * Constants.basicTrickerAlertnessRadius) alertnessRadius = (int)(1.3 * Constants.basicTrickerAlertnessRadius)
viewRange = (int)(1.2 * Constants.basicTrickerViewRange) viewRange = (int)(1.2 * Constants.basicTrickerViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows)
1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows
)
speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest)




@@ -108,10 +108,10 @@ class Klee:
concealment = 1.0 * Constants.basicConcealment concealment = 1.0 * Constants.basicConcealment
alertnessRadius = (int)(1.0 * Constants.basicTrickerAlertnessRadius) alertnessRadius = (int)(1.0 * Constants.basicTrickerAlertnessRadius)
viewRange = (int)(1.0 * Constants.basicTrickerViewRange) viewRange = (int)(1.0 * Constants.basicTrickerViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows)
1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows
)
speedOfOpenChest = (int)(1.1 * Constants.basicSpeedOfOpenChest) speedOfOpenChest = (int)(1.1 * Constants.basicSpeedOfOpenChest)




@@ -120,10 +120,10 @@ class ANoisyPerson:
concealment = 0.8 * Constants.basicConcealment concealment = 0.8 * Constants.basicConcealment
alertnessRadius = (int)(0.9 * Constants.basicTrickerAlertnessRadius) alertnessRadius = (int)(0.9 * Constants.basicTrickerAlertnessRadius)
viewRange = (int)(1.0 * Constants.basicTrickerViewRange) viewRange = (int)(1.0 * Constants.basicTrickerViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
1.1 * Constants.basicTrickerSpeedOfClimbingThroughWindows)
1.1 * Constants.basicTrickerSpeedOfClimbingThroughWindows
)
speedOfOpenChest = (int)(1.1 * Constants.basicSpeedOfOpenChest) speedOfOpenChest = (int)(1.1 * Constants.basicSpeedOfOpenChest)




@@ -132,10 +132,10 @@ class Idol:
concealment = 0.75 * Constants.basicConcealment concealment = 0.75 * Constants.basicConcealment
alertnessRadius = (int)(1.0 * Constants.basicTrickerAlertnessRadius) alertnessRadius = (int)(1.0 * Constants.basicTrickerAlertnessRadius)
viewRange = (int)(1.1 * Constants.basicTrickerViewRange) viewRange = (int)(1.1 * Constants.basicTrickerViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows)
1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows
)
speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest)




@@ -148,10 +148,10 @@ class Athlete:
concealment = 0.9 * Constants.basicConcealment concealment = 0.9 * Constants.basicConcealment
alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius)
viewRange = (int)(1.1 * Constants.basicStudentViewRange) viewRange = (int)(1.1 * Constants.basicStudentViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
1.2 * Constants.basicStudentSpeedOfClimbingThroughWindows)
1.2 * Constants.basicStudentSpeedOfClimbingThroughWindows
)
speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest)




@@ -159,15 +159,15 @@ class Teacher:
moveSpeed = (int)(0.9 * Constants.basicStudentSpeed) moveSpeed = (int)(0.9 * Constants.basicStudentSpeed)
maxHp = (int)(10.0 * Constants.basicHp) maxHp = (int)(10.0 * Constants.basicHp)
maxAddiction = (int)(10.0 * Constants.basicMaxGamingAddiction) maxAddiction = (int)(10.0 * Constants.basicMaxGamingAddiction)
LearnSpeed = (int)(0.0 * Constants.basicLearnSpeed)
LearnSpeed = (int)( Constants.basicLearnSpeed* 50//123)
EncourageSpeed = (int)(0.8 * Constants.basicEncourageSpeed) EncourageSpeed = (int)(0.8 * Constants.basicEncourageSpeed)
concealment = 0.5 * Constants.basicConcealment concealment = 0.5 * Constants.basicConcealment
alertnessRadius = (int)(0.5 * Constants.basicStudentAlertnessRadius)
viewRange = (int)(0.9 * Constants.basicStudentViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
alertnessRadius = (int)(Constants.basicStudentAlertnessRadius * 2 // 3)
viewRange = (int)(0.8 * Constants.basicStudentViewRange)
speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
0.5 * Constants.basicStudentSpeedOfClimbingThroughWindows)
Constants.basicStudentSpeedOfClimbingThroughWindows* 1000 // 1222
)
speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest)




@@ -180,43 +180,41 @@ class StraightAStudent:
concealment = 0.9 * Constants.basicConcealment concealment = 0.9 * Constants.basicConcealment
alertnessRadius = (int)(0.9 * Constants.basicStudentAlertnessRadius) alertnessRadius = (int)(0.9 * Constants.basicStudentAlertnessRadius)
viewRange = (int)(0.9 * Constants.basicStudentViewRange) viewRange = (int)(0.9 * Constants.basicStudentViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
0.83333 * Constants.basicStudentSpeedOfClimbingThroughWindows)
0.83333 * Constants.basicStudentSpeedOfClimbingThroughWindows
)
speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest)




class Robot: class Robot:
moveSpeed = (int)(1.0 * Constants.basicStudentSpeed)
maxHp = (int)(0.4 * Constants.basicHp)
moveSpeed = (int)(0.9 * Constants.basicStudentSpeed)
maxHp = (int)(0.3 * Constants.basicHp)
maxAddiction = (int)(0.0 * Constants.basicMaxGamingAddiction) maxAddiction = (int)(0.0 * Constants.basicMaxGamingAddiction)
LearnSpeed = (int)(1.0 * Constants.basicLearnSpeed)
LearnSpeed = (int)(0.7 * Constants.basicLearnSpeed)
EncourageSpeed = 0 EncourageSpeed = 0
concealment = 1.0 * Constants.basicConcealment
alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius)
viewRange = (int)(1.0 * Constants.basicStudentViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)(
0.0016 * Constants.basicStudentSpeedOfClimbingThroughWindows)
speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest)
concealment = 0.8 * Constants.basicConcealment
alertnessRadius = (int)(0.0 * Constants.basicStudentAlertnessRadius)
viewRange = (int)(0.0 * Constants.basicStudentViewRange)
speedOfOpeningOrLocking = (int)(0.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = 1
speedOfOpenChest = (int)(0.8 * Constants.basicSpeedOfOpenChest)




class TechOtaku: class TechOtaku:
moveSpeed = (int)(0.75 * Constants.basicStudentSpeed)
moveSpeed = (int)(0.96 * Constants.basicStudentSpeed)
maxHp = (int)(0.9 * Constants.basicHp) maxHp = (int)(0.9 * Constants.basicHp)
maxAddiction = (int)(1.1 * Constants.basicMaxGamingAddiction)
LearnSpeed = (int)(1.1 * Constants.basicLearnSpeed)
EncourageSpeed = (int)(0.9 * Constants.basicEncourageSpeed)
concealment = 1.0 * Constants.basicConcealment
maxAddiction = (int)(1.0 * Constants.basicMaxGamingAddiction)
LearnSpeed = (int)(0.9 * Constants.basicLearnSpeed)
EncourageSpeed = (int)(1.0 * Constants.basicEncourageSpeed)
concealment = 1.1 * Constants.basicConcealment
alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius)
viewRange = (int)(0.9 * Constants.basicStudentViewRange) viewRange = (int)(0.9 * Constants.basicStudentViewRange)
speedOfOpeningOrLocking = (int)(
1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
0.75 * Constants.basicStudentSpeedOfClimbingThroughWindows)
speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest)
0.9 * Constants.basicStudentSpeedOfClimbingThroughWindows
)
speedOfOpenChest = (int)(0.88 * Constants.basicSpeedOfOpenChest)




class Sunshine: class Sunshine:
@@ -228,10 +226,10 @@ class Sunshine:
concealment = 1.0 * Constants.basicConcealment concealment = 1.0 * Constants.basicConcealment
alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius)
viewRange = (int)(1.0 * Constants.basicStudentViewRange) viewRange = (int)(1.0 * Constants.basicStudentViewRange)
speedOfOpeningOrLocking = (int)(
0.7 * Constants.basicSpeedOfOpeningOrLocking)
speedOfOpeningOrLocking = (int)(0.7 * Constants.basicSpeedOfOpeningOrLocking)
speedOfClimbingThroughWindows = (int)( speedOfClimbingThroughWindows = (int)(
1.0 * Constants.basicStudentSpeedOfClimbingThroughWindows)
1.0 * Constants.basicStudentSpeedOfClimbingThroughWindows
)
speedOfOpenChest = (int)(0.9 * Constants.basicSpeedOfOpenChest) speedOfOpenChest = (int)(0.9 * Constants.basicSpeedOfOpenChest)




@@ -241,12 +239,22 @@ class CanBeginToCharge:




class BecomeInvisible: class BecomeInvisible:
skillCD = (int)(4 * Constants.commonSkillCD / 3)
skillCD = (int)(4 * Constants.commonSkillCD // 3)
durationTime = (int)(Constants.commonSkillTime) durationTime = (int)(Constants.commonSkillTime)




class Punish: class Punish:
skillCD = (int)(1.0 * Constants.commonSkillCD)
skillCD = (int)(1.5 * Constants.commonSkillCD)
durationTime = (int)(0.0 * Constants.commonSkillTime)


class SparksNSplash:
skillCD = (int)(1.5 * Constants.commonSkillCD)
durationTime = (int)(1.0 * Constants.commonSkillTime)


class HaveTea:
skillCD = (int)(3 * Constants.commonSkillCD)
durationTime = (int)(0.0 * Constants.commonSkillTime) durationTime = (int)(0.0 * Constants.commonSkillTime)




@@ -271,7 +279,7 @@ class Howl:




class ShowTime: class ShowTime:
skillCD = (int)(8 * Constants.commonSkillCD / 3)
skillCD = (int)(8 * Constants.commonSkillCD // 3)
durationTime = (int)(1.0 * Constants.commonSkillTime) durationTime = (int)(1.0 * Constants.commonSkillTime)




@@ -286,7 +294,7 @@ class UseKnife:




class UseRobot: class UseRobot:
skillCD = (int)(0.0017 * Constants.commonSkillCD)
skillCD = (int)(2 * Constants.commonSkillCD // 30)
durationTime = (int)(0.0 * Constants.commonSkillTime) durationTime = (int)(0.0 * Constants.commonSkillTime)




@@ -296,8 +304,9 @@ class WriteAnswers:




class SummonGolem: class SummonGolem:
skillCD = (int)(1.0 * Constants.commonSkillCD)
durationTime = (int)(0.0 * Constants.commonSkillTime)
skillCD = (int)(Constants.commonSkillCD * 4 // 3)
durationTime = (int)(6.0 * Constants.commonSkillTime)



class CommonAttackOfTricker: class CommonAttackOfTricker:
BulletBombRange = 0 BulletBombRange = 0
@@ -305,39 +314,55 @@ class CommonAttackOfTricker:
ap = Constants.basicApOfTricker ap = Constants.basicApOfTricker
Speed = Constants.basicBulletMoveSpeed Speed = Constants.basicBulletMoveSpeed
IsRemoteAttack = False IsRemoteAttack = False
CastTime = BulletAttackRange * 1000 / Speed
CastTime = BulletAttackRange * 1000 // Speed
Backswing = Constants.basicBackswing Backswing = Constants.basicBackswing
RecoveryFromHit = Constants.basicRecoveryFromHit RecoveryFromHit = Constants.basicRecoveryFromHit
cd = Constants.basicBackswing cd = Constants.basicBackswing
maxBulletNum = 1 maxBulletNum = 1


class FlyingKnife: class FlyingKnife:
BulletBombRange = 0 BulletBombRange = 0
BulletAttackRange = Constants.basicRemoteAttackRange * 13 BulletAttackRange = Constants.basicRemoteAttackRange * 13
ap = Constants.basicApOfTricker* 4 / 5
Speed = Constants.basicBulletMoveSpeed* 25 / 10
ap = Constants.basicApOfTricker * 4 // 5
Speed = Constants.basicBulletMoveSpeed * 25 // 10
IsRemoteAttack = True IsRemoteAttack = True
CastTime = Constants.basicCastTime * 4 / 5
Backswing =0
RecoveryFromHit =0
cd = Constants.basicBackswing / 2
CastTime = Constants.basicCastTime * 6 // 5
Backswing = 0
RecoveryFromHit = 0
cd = Constants.basicBackswing * 3 // 4
maxBulletNum = 1 maxBulletNum = 1


class BombBomb: class BombBomb:
BulletBombRange = Constants.basicBulletBombRange BulletBombRange = Constants.basicBulletBombRange
BulletAttackRange = Constants.basicAttackShortRange BulletAttackRange = Constants.basicAttackShortRange
ap = Constants.basicApOfTricker * 6 / 5
Speed = Constants.basicBulletMoveSpeed* 30 / 37
ap = Constants.basicApOfTricker * 6 // 5
Speed = Constants.basicBulletMoveSpeed * 30 // 37
IsRemoteAttack = False IsRemoteAttack = False
CastTime = BulletAttackRange * 1000 / Speed
Backswing =Constants.basicRecoveryFromHit
RecoveryFromHit =Constants.basicRecoveryFromHit
CastTime = BulletAttackRange * 1000 // Speed
Backswing = Constants.basicBackswing * 3 // 2
RecoveryFromHit = Constants.basicRecoveryFromHit
cd = Constants.basicCD cd = Constants.basicCD
maxBulletNum = 1 maxBulletNum = 1


class JumpyDumpty: class JumpyDumpty:
BulletBombRange = Constants.basicBulletBombRange / 2
BulletAttackRange = Constants.basicRemoteAttackRange * 2
ap = (int)(Constants.basicApOfTricker* 0.6)
Speed = Constants.basicBulletMoveSpeed* 43 / 37
BulletBombRange = Constants.basicBulletBombRange // 2
BulletAttackRange = Constants.basicAttackShortRange * 16 / 22
ap = (int)(Constants.basicApOfTricker * 0.6)
Speed = Constants.basicBulletMoveSpeed * 43 // 37
IsRemoteAttack = False


class Strike:
BulletBombRange = 0
BulletAttackRange = Constants.basicAttackShortRange
ap = Constants.basicApOfTricker * 16 // 5
Speed = Constants.basicBulletMoveSpeed * 125 // 148
IsRemoteAttack = False IsRemoteAttack = False
CastTime = Constants.basicCastTime * 16 // 25
Backswing = Constants.basicBackswing
RecoveryFromHit = Constants.basicRecoveryFromHit
cd = Constants.basicBackswing
maxBulletNum = 1

+ 227
- 81
CAPI/python/PyAPI/logic.py View File

@@ -20,7 +20,6 @@ from PyAPI.Interface import ILogic, IGameTimer


class Logic(ILogic): class Logic(ILogic):
def __init__(self, playerID: int, playerType: THUAI6.PlayerType) -> None: def __init__(self, playerID: int, playerType: THUAI6.PlayerType) -> None:

# ID # ID
self.__playerID: int = playerID self.__playerID: int = playerID


@@ -103,7 +102,12 @@ class Logic(ILogic):


def GetPlaceType(self, x: int, y: int) -> THUAI6.PlaceType: def GetPlaceType(self, x: int, y: int) -> THUAI6.PlaceType:
with self.__mtxState: with self.__mtxState:
if x < 0 or x >= len(self.__currentState.gameMap) or y < 0 or y >= len(self.__currentState.gameMap[0]):
if (
x < 0
or x >= len(self.__currentState.gameMap)
or y < 0
or y >= len(self.__currentState.gameMap[0])
):
self.__logger.warning("Invalid position") self.__logger.warning("Invalid position")
return THUAI6.PlaceType.NullPlaceType return THUAI6.PlaceType.NullPlaceType
self.__logger.debug("Called GetPlaceType") self.__logger.debug("Called GetPlaceType")
@@ -149,7 +153,9 @@ class Logic(ILogic):
with self.__mtxState: with self.__mtxState:
self.__logger.debug("Called GetHiddenGateState") self.__logger.debug("Called GetHiddenGateState")
if (x, y) in self.__currentState.mapInfo.hiddenGateState: if (x, y) in self.__currentState.mapInfo.hiddenGateState:
return copy.deepcopy(self.__currentState.mapInfo.hiddenGateState[(x, y)])
return copy.deepcopy(
self.__currentState.mapInfo.hiddenGateState[(x, y)]
)
else: else:
self.__logger.warning("HiddenGate not found") self.__logger.warning("HiddenGate not found")
return THUAI6.HiddenGateState.Null return THUAI6.HiddenGateState.Null
@@ -184,9 +190,9 @@ class Logic(ILogic):
self.__logger.debug("Called ThrowProp") self.__logger.debug("Called ThrowProp")
return self.__comm.ThrowProp(propType, self.__playerID) return self.__comm.ThrowProp(propType, self.__playerID)


def UseSkill(self, skillID: int) -> bool:
def UseSkill(self, skillID: int, skillParam: int) -> bool:
self.__logger.debug("Called UseSkill") self.__logger.debug("Called UseSkill")
return self.__comm.UseSkill(skillID, self.__playerID)
return self.__comm.UseSkill(skillID, skillParam, self.__playerID)


def SendMessage(self, toID: int, message: Union[str, bytes]) -> bool: def SendMessage(self, toID: int, message: Union[str, bytes]) -> bool:
self.__logger.debug("Called SendMessage") self.__logger.debug("Called SendMessage")
@@ -262,9 +268,13 @@ class Logic(ILogic):
self.__logger.debug("Called EndAllAction") self.__logger.debug("Called EndAllAction")
return self.__comm.EndAllAction(self.__playerID) return self.__comm.EndAllAction(self.__playerID)


def HaveView(self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int) -> bool:
def HaveView(
self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int
) -> bool:
with self.__mtxState: with self.__mtxState:
return AssistFunction.HaveView(viewRange, selfX, selfY, gridX, gridY, self.__currentState.gameMap)
return AssistFunction.HaveView(
viewRange, selfX, selfY, gridX, gridY, self.__currentState.gameMap
)


# Logic内部逻辑 # Logic内部逻辑
def __TryConnection(self) -> bool: def __TryConnection(self) -> bool:
@@ -281,8 +291,7 @@ class Logic(ILogic):
# 读取消息,无消息时此处阻塞 # 读取消息,无消息时此处阻塞
clientMsg = self.__comm.GetMessage2Client() clientMsg = self.__comm.GetMessage2Client()
self.__logger.debug("Get message from server!") self.__logger.debug("Get message from server!")
self.__gameState = Proto2THUAI6.gameStateDict[
clientMsg.game_state]
self.__gameState = Proto2THUAI6.gameStateDict[clientMsg.game_state]


if self.__gameState == THUAI6.GameState.GameStart: if self.__gameState == THUAI6.GameState.GameStart:
# 读取玩家的GUID # 读取玩家的GUID
@@ -294,8 +303,7 @@ class Logic(ILogic):
for row in obj.map_message.row: for row in obj.map_message.row:
col: List[THUAI6.PlaceType] = [] col: List[THUAI6.PlaceType] = []
for place in row.col: for place in row.col:
col.append(
Proto2THUAI6.placeTypeDict[place])
col.append(Proto2THUAI6.placeTypeDict[place])
gameMap.append(col) gameMap.append(col)
self.__currentState.gameMap = gameMap self.__currentState.gameMap = gameMap
self.__bufferState.gameMap = gameMap self.__bufferState.gameMap = gameMap
@@ -330,127 +338,238 @@ class Logic(ILogic):
if item.WhichOneof("message_of_obj") == "student_message": if item.WhichOneof("message_of_obj") == "student_message":
if item.student_message.player_id == self.__playerID: if item.student_message.player_id == self.__playerID:
self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Student( self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Student(
item.student_message)
self.__bufferState.students.append(
self.__bufferState.self)
item.student_message
)
self.__bufferState.students.append(self.__bufferState.self)
else: else:
self.__bufferState.students.append( self.__bufferState.students.append(
Proto2THUAI6.Protobuf2THUAI6Student(item.student_message))
Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)
)
self.__logger.debug("Add Student!") self.__logger.debug("Add Student!")
else: else:
for item in message.obj_message: for item in message.obj_message:
if item.WhichOneof("message_of_obj") == "tricker_message": if item.WhichOneof("message_of_obj") == "tricker_message":
if item.tricker_message.player_id == self.__playerID: if item.tricker_message.player_id == self.__playerID:
self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Tricker( self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Tricker(
item.tricker_message)
self.__bufferState.trickers.append(
self.__bufferState.self)
item.tricker_message
)
self.__bufferState.trickers.append(self.__bufferState.self)
else: else:
self.__bufferState.trickers.append( self.__bufferState.trickers.append(
Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message))
Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message)
)
self.__logger.debug("Add Tricker!") self.__logger.debug("Add Tricker!")


def __LoadBufferCase(self, item: Message2Clients.MessageOfObj) -> None: def __LoadBufferCase(self, item: Message2Clients.MessageOfObj) -> None:
if self.__playerType == THUAI6.PlayerType.StudentPlayer and item.WhichOneof("message_of_obj") == "tricker_message":
if (
self.__playerType == THUAI6.PlayerType.StudentPlayer
and item.WhichOneof("message_of_obj") == "tricker_message"
):
if MessageType.TRICKER_INVISIBLE in item.tricker_message.buff: if MessageType.TRICKER_INVISIBLE in item.tricker_message.buff:
return return
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.tricker_message.x, item.tricker_message.y, self.__bufferState.gameMap):
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.tricker_message.x,
item.tricker_message.y,
self.__bufferState.gameMap,
):
self.__bufferState.trickers.append( self.__bufferState.trickers.append(
Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message))
Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message)
)
self.__logger.debug("Add Tricker!") self.__logger.debug("Add Tricker!")
elif self.__playerType == THUAI6.PlayerType.TrickerPlayer and item.WhichOneof("message_of_obj") == "student_message":
elif (
self.__playerType == THUAI6.PlayerType.TrickerPlayer
and item.WhichOneof("message_of_obj") == "student_message"
):
if THUAI6.TrickerBuffType.Clairaudience in self.__bufferState.self.buff: if THUAI6.TrickerBuffType.Clairaudience in self.__bufferState.self.buff:
self.__bufferState.students.append( self.__bufferState.students.append(
Proto2THUAI6.Protobuf2THUAI6Student(item.student_message))
Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)
)
self.__logger.debug("Add Student!") self.__logger.debug("Add Student!")
return return
if MessageType.STUDENT_INVISIBLE in item.student_message.buff: if MessageType.STUDENT_INVISIBLE in item.student_message.buff:
return return
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.student_message.x, item.student_message.y, self.__bufferState.gameMap):
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.student_message.x,
item.student_message.y,
self.__bufferState.gameMap,
):
self.__bufferState.students.append( self.__bufferState.students.append(
Proto2THUAI6.Protobuf2THUAI6Student(item.student_message))
Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)
)
self.__logger.debug("Add Student!") self.__logger.debug("Add Student!")
elif item.WhichOneof("message_of_obj") == "prop_message": elif item.WhichOneof("message_of_obj") == "prop_message":
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.prop_message.x, item.prop_message.y, self.__bufferState.gameMap):
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.prop_message.x,
item.prop_message.y,
self.__bufferState.gameMap,
):
self.__bufferState.props.append( self.__bufferState.props.append(
Proto2THUAI6.Protobuf2THUAI6Prop(item.prop_message))
Proto2THUAI6.Protobuf2THUAI6Prop(item.prop_message)
)
self.__logger.debug("Add Prop!") self.__logger.debug("Add Prop!")
elif item.WhichOneof("message_of_obj") == "bullet_message": elif item.WhichOneof("message_of_obj") == "bullet_message":
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.bullet_message.x, item.bullet_message.y, self.__bufferState.gameMap):
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.bullet_message.x,
item.bullet_message.y,
self.__bufferState.gameMap,
):
self.__bufferState.bullets.append( self.__bufferState.bullets.append(
Proto2THUAI6.Protobuf2THUAI6Bullet(item.bullet_message))
Proto2THUAI6.Protobuf2THUAI6Bullet(item.bullet_message)
)
self.__logger.debug("Add Bullet!") self.__logger.debug("Add Bullet!")
elif item.WhichOneof("message_of_obj") == "classroom_message": elif item.WhichOneof("message_of_obj") == "classroom_message":
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.classroom_message.x, item.classroom_message.y, self.__bufferState.gameMap):
pos = (AssistFunction.GridToCell(
item.classroom_message.x), AssistFunction.GridToCell(item.classroom_message.y))
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.classroom_message.x,
item.classroom_message.y,
self.__bufferState.gameMap,
):
pos = (
AssistFunction.GridToCell(item.classroom_message.x),
AssistFunction.GridToCell(item.classroom_message.y),
)
if pos not in self.__bufferState.mapInfo.classroomState: if pos not in self.__bufferState.mapInfo.classroomState:
self.__bufferState.mapInfo.classroomState[pos] = item.classroom_message.progress
self.__bufferState.mapInfo.classroomState[
pos
] = item.classroom_message.progress
self.__logger.debug("Add Classroom!") self.__logger.debug("Add Classroom!")
else: else:
self.__bufferState.mapInfo.classroomState[pos] = item.classroom_message.progress
self.__bufferState.mapInfo.classroomState[
pos
] = item.classroom_message.progress
self.__logger.debug("Update Classroom!") self.__logger.debug("Update Classroom!")
elif item.WhichOneof("message_of_obj") == "chest_message": elif item.WhichOneof("message_of_obj") == "chest_message":
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.chest_message.x, item.chest_message.y, self.__bufferState.gameMap):
pos = (AssistFunction.GridToCell(
item.chest_message.x), AssistFunction.GridToCell(item.chest_message.y))
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.chest_message.x,
item.chest_message.y,
self.__bufferState.gameMap,
):
pos = (
AssistFunction.GridToCell(item.chest_message.x),
AssistFunction.GridToCell(item.chest_message.y),
)
if pos not in self.__bufferState.mapInfo.chestState: if pos not in self.__bufferState.mapInfo.chestState:
self.__bufferState.mapInfo.chestState[pos] = item.chest_message.progress
self.__logger.debug(
f"Add Chest at {pos[0]}, {pos[1]}")
self.__bufferState.mapInfo.chestState[
pos
] = item.chest_message.progress
self.__logger.debug(f"Add Chest at {pos[0]}, {pos[1]}")
else: else:
self.__bufferState.mapInfo.chestState[pos] = item.chest_message.progress
self.__logger.debug(
f"Update Chest at {pos[0]}, {pos[1]}")
self.__bufferState.mapInfo.chestState[
pos
] = item.chest_message.progress
self.__logger.debug(f"Update Chest at {pos[0]}, {pos[1]}")
elif item.WhichOneof("message_of_obj") == "door_message": elif item.WhichOneof("message_of_obj") == "door_message":
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.door_message.x, item.door_message.y, self.__bufferState.gameMap):
pos = (AssistFunction.GridToCell(
item.door_message.x), AssistFunction.GridToCell(item.door_message.y))
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.door_message.x,
item.door_message.y,
self.__bufferState.gameMap,
):
pos = (
AssistFunction.GridToCell(item.door_message.x),
AssistFunction.GridToCell(item.door_message.y),
)
if pos not in self.__bufferState.mapInfo.doorState: if pos not in self.__bufferState.mapInfo.doorState:
self.__bufferState.mapInfo.doorState[pos] = item.door_message.is_open
self.__bufferState.mapInfo.doorProgress[pos] = item.door_message.progress
self.__bufferState.mapInfo.doorState[
pos
] = item.door_message.is_open
self.__bufferState.mapInfo.doorProgress[
pos
] = item.door_message.progress
self.__logger.debug("Add Door!") self.__logger.debug("Add Door!")
else: else:
self.__bufferState.mapInfo.doorState[pos] = item.door_message.is_open
self.__bufferState.mapInfo.doorProgress[pos] = item.door_message.progress
self.__bufferState.mapInfo.doorState[
pos
] = item.door_message.is_open
self.__bufferState.mapInfo.doorProgress[
pos
] = item.door_message.progress
self.__logger.debug("Update Door!") self.__logger.debug("Update Door!")
elif item.WhichOneof("message_of_obj") == "hidden_gate_message": elif item.WhichOneof("message_of_obj") == "hidden_gate_message":
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.hidden_gate_message.x, item.hidden_gate_message.y, self.__bufferState.gameMap):
pos = (AssistFunction.GridToCell(
item.hidden_gate_message.x), AssistFunction.GridToCell(item.hidden_gate_message.y))
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.hidden_gate_message.x,
item.hidden_gate_message.y,
self.__bufferState.gameMap,
):
pos = (
AssistFunction.GridToCell(item.hidden_gate_message.x),
AssistFunction.GridToCell(item.hidden_gate_message.y),
)
if pos not in self.__bufferState.mapInfo.hiddenGateState: if pos not in self.__bufferState.mapInfo.hiddenGateState:
self.__bufferState.mapInfo.hiddenGateState[pos] = Proto2THUAI6.Bool2HiddenGateState(
item.hidden_gate_message.opened)
self.__bufferState.mapInfo.hiddenGateState[
pos
] = Proto2THUAI6.Bool2HiddenGateState(
item.hidden_gate_message.opened
)
self.__logger.debug("Add HiddenGate!") self.__logger.debug("Add HiddenGate!")
else: else:
self.__bufferState.mapInfo.hiddenGateState[pos] = Proto2THUAI6.Bool2HiddenGateState(
item.hidden_gate_message.opened)
self.__bufferState.mapInfo.hiddenGateState[
pos
] = Proto2THUAI6.Bool2HiddenGateState(
item.hidden_gate_message.opened
)
self.__logger.debug("Update HiddenGate!") self.__logger.debug("Update HiddenGate!")
elif item.WhichOneof("message_of_obj") == "gate_message": elif item.WhichOneof("message_of_obj") == "gate_message":
if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.gate_message.x, item.gate_message.y, self.__bufferState.gameMap):
pos = (AssistFunction.GridToCell(
item.gate_message.x), AssistFunction.GridToCell(item.gate_message.y))
if AssistFunction.HaveView(
self.__bufferState.self.viewRange,
self.__bufferState.self.x,
self.__bufferState.self.y,
item.gate_message.x,
item.gate_message.y,
self.__bufferState.gameMap,
):
pos = (
AssistFunction.GridToCell(item.gate_message.x),
AssistFunction.GridToCell(item.gate_message.y),
)
if pos not in self.__bufferState.mapInfo.gateState: if pos not in self.__bufferState.mapInfo.gateState:
self.__bufferState.mapInfo.gateState[pos] = item.gate_message.progress
self.__bufferState.mapInfo.gateState[
pos
] = item.gate_message.progress
self.__logger.debug("Add Gate!") self.__logger.debug("Add Gate!")
else: else:
self.__bufferState.mapInfo.gateState[pos] = item.gate_message.progress
self.__bufferState.mapInfo.gateState[
pos
] = item.gate_message.progress
self.__logger.debug("Update Gate!") self.__logger.debug("Update Gate!")
elif item.WhichOneof("message_of_obj") == "news_message": elif item.WhichOneof("message_of_obj") == "news_message":
if item.news_message.to_id == self.__playerID: if item.news_message.to_id == self.__playerID:
if item.news_message.WhichOneof("news") == "text_message": if item.news_message.WhichOneof("news") == "text_message":
self.__messageQueue.put( self.__messageQueue.put(
(item.news_message.from_id, item.news_message.text_message))
(item.news_message.from_id, item.news_message.text_message)
)
self.__logger.debug("Add News!") self.__logger.debug("Add News!")
elif item.news_message.WhichOneof("news") == "binary_message": elif item.news_message.WhichOneof("news") == "binary_message":
self.__messageQueue.put( self.__messageQueue.put(
(item.news_message.from_id, item.news_message.binary_message))
(item.news_message.from_id, item.news_message.binary_message)
)
self.__logger.debug("Add News!") self.__logger.debug("Add News!")
else: else:
self.__logger.error("Unknown News!") self.__logger.error("Unknown News!")
else: else:
self.__logger.debug(
"Unknown Message!")
self.__logger.debug("Unknown Message!")


def __LoadBuffer(self, message: Message2Clients.MessageToClient) -> None: def __LoadBuffer(self, message: Message2Clients.MessageToClient) -> None:
with self.__cvBuffer: with self.__cvBuffer:
@@ -470,14 +589,18 @@ class Logic(ILogic):
self.__bufferState.guids.append(obj.tricker_message.guid) self.__bufferState.guids.append(obj.tricker_message.guid)


self.__bufferState.gameInfo = Proto2THUAI6.Protobuf2THUAI6GameInfo( self.__bufferState.gameInfo = Proto2THUAI6.Protobuf2THUAI6GameInfo(
message.all_message)
message.all_message
)


self.__LoadBufferSelf(message) self.__LoadBufferSelf(message)
for item in message.obj_message: for item in message.obj_message:
self.__LoadBufferCase(item) self.__LoadBufferCase(item)
if Setting.asynchronous(): if Setting.asynchronous():
with self.__mtxState: with self.__mtxState:
self.__currentState, self.__bufferState = self.__bufferState, self.__currentState
self.__currentState, self.__bufferState = (
self.__bufferState,
self.__currentState,
)
self.__counterState = self.__counterBuffer self.__counterState = self.__counterBuffer
self.__logger.info("Update state!") self.__logger.info("Update state!")
self.__freshed = True self.__freshed = True
@@ -496,7 +619,10 @@ class Logic(ILogic):
with self.__cvBuffer: with self.__cvBuffer:
self.__cvBuffer.wait_for(lambda: self.__bufferUpdated) self.__cvBuffer.wait_for(lambda: self.__bufferUpdated)
with self.__mtxState: with self.__mtxState:
self.__bufferState, self.__currentState = self.__currentState, self.__bufferState
self.__bufferState, self.__currentState = (
self.__currentState,
self.__bufferState,
)
self.__counterState = self.__counterBuffer self.__counterState = self.__counterBuffer
self.__bufferUpdated = False self.__bufferUpdated = False
self.__logger.info("Update state!") self.__logger.info("Update state!")
@@ -506,12 +632,21 @@ class Logic(ILogic):
with self.__cvBuffer: with self.__cvBuffer:
self.__cvBuffer.wait_for(lambda: self.__freshed) self.__cvBuffer.wait_for(lambda: self.__freshed)


def Main(self, createAI: Callable, IP: str, port: str, file: bool, screen: bool, warnOnly: bool) -> None:

def Main(
self,
createAI: Callable,
IP: str,
port: str,
file: bool,
screen: bool,
warnOnly: bool,
) -> None:
# 建立日志组件 # 建立日志组件
self.__logger.setLevel(logging.DEBUG) self.__logger.setLevel(logging.DEBUG)
formatter = logging.Formatter( formatter = logging.Formatter(
"[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s", '%H:%M:%S')
"[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s",
"%H:%M:%S",
)
# 确保文件存在 # 确保文件存在
# if not os.path.exists(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"): # if not os.path.exists(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"):
# os.makedirs(os.path.dirname(os.path.dirname( # os.makedirs(os.path.dirname(os.path.dirname(
@@ -519,13 +654,21 @@ class Logic(ILogic):


if platform.system().lower() == "windows": if platform.system().lower() == "windows":
os.system( os.system(
f"mkdir \"{os.path.dirname(os.path.dirname(os.path.realpath(__file__)))}\\logs\"")
f'mkdir "{os.path.dirname(os.path.dirname(os.path.realpath(__file__)))}\\logs"'
)
else: else:
os.system( os.system(
f"mkdir -p \"{os.path.dirname(os.path.dirname(os.path.realpath(__file__)))}/logs\"")

fileHandler = logging.FileHandler(os.path.dirname(
os.path.dirname(os.path.realpath(__file__))) + "/logs/logic" + str(self.__playerID) + "-log.txt", "w+", encoding="utf-8")
f'mkdir -p "{os.path.dirname(os.path.dirname(os.path.realpath(__file__)))}/logs"'
)

fileHandler = logging.FileHandler(
os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+ "/logs/logic"
+ str(self.__playerID)
+ "-log.txt",
"w+",
encoding="utf-8",
)
screenHandler = logging.StreamHandler() screenHandler = logging.StreamHandler()
if file: if file:
fileHandler.setLevel(logging.DEBUG) fileHandler.setLevel(logging.DEBUG)
@@ -555,13 +698,15 @@ class Logic(ILogic):
self.__timer = StudentAPI(self) self.__timer = StudentAPI(self)
else: else:
self.__timer = StudentDebugAPI( self.__timer = StudentDebugAPI(
self, file, screen, warnOnly, self.__playerID)
self, file, screen, warnOnly, self.__playerID
)
elif self.__playerType == THUAI6.PlayerType.TrickerPlayer: elif self.__playerType == THUAI6.PlayerType.TrickerPlayer:
if not file and not screen: if not file and not screen:
self.__timer = TrickerAPI(self) self.__timer = TrickerAPI(self)
else: else:
self.__timer = TrickerDebugAPI( self.__timer = TrickerDebugAPI(
self, file, screen, warnOnly, self.__playerID)
self, file, screen, warnOnly, self.__playerID
)


# 构建AI线程 # 构建AI线程
def AIThread(): def AIThread():
@@ -583,7 +728,8 @@ class Logic(ILogic):


if self.__TryConnection(): if self.__TryConnection():
self.__logger.info( self.__logger.info(
"Connect to the server successfully, AI thread will be started.")
"Connect to the server successfully, AI thread will be started."
)
self.__threadAI = threading.Thread(target=AIThread) self.__threadAI = threading.Thread(target=AIThread)
self.__threadAI.start() self.__threadAI.start()
self.__ProcessMessage() self.__ProcessMessage()


+ 47
- 16
CAPI/python/PyAPI/main.py View File

@@ -1,8 +1,8 @@
import os import os
import sys import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
sys.path.append(os.path.dirname(os.path.dirname(
os.path.realpath(__file__))) + '/proto')
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/proto")


from PyAPI.Interface import IAI from PyAPI.Interface import IAI
from PyAPI.AI import AI from PyAPI.AI import AI
@@ -42,19 +42,50 @@ def THUAI6Main(argv: List[str], AIBuilder: Callable) -> None:
screen: bool = True screen: bool = True
warnOnly: bool = False warnOnly: bool = False
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="THUAI6 Python Interface Commandline Parameter Introduction")
parser.add_argument("-I", type=str, required=True,
help="Server`s IP 127.0.0.1 in default", dest="sIP", default="127.0.0.1")
parser.add_argument("-P", type=str, required=True,
help="Server`s Port 8888 in default", dest="sPort", default="8888")
parser.add_argument("-p", type=int, required=True,
help="Player`s ID", dest="pID", choices=[0, 1, 2, 3, 4])
parser.add_argument("-d", action='store_true',
help="Set this flag to save the debug log to ./logs folder", dest="file")
parser.add_argument("-o", action='store_true',
help="Set this flag to print the debug log to the screen", dest="screen")
parser.add_argument("-w", action='store_true',
help="Set this flag to only print warning on the screen", dest="warnOnly")
description="THUAI6 Python Interface Commandline Parameter Introduction"
)
parser.add_argument(
"-I",
type=str,
required=True,
help="Server`s IP 127.0.0.1 in default",
dest="sIP",
default="127.0.0.1",
)
parser.add_argument(
"-P",
type=str,
required=True,
help="Server`s Port 8888 in default",
dest="sPort",
default="8888",
)
parser.add_argument(
"-p",
type=int,
required=True,
help="Player`s ID",
dest="pID",
choices=[0, 1, 2, 3, 4],
)
parser.add_argument(
"-d",
action="store_true",
help="Set this flag to save the debug log to ./logs folder",
dest="file",
)
parser.add_argument(
"-o",
action="store_true",
help="Set this flag to print the debug log to the screen",
dest="screen",
)
parser.add_argument(
"-w",
action="store_true",
help="Set this flag to only print warning on the screen",
dest="warnOnly",
)
args = parser.parse_args() args = parser.parse_args()
pID = args.pID pID = args.pID
sIP = args.sIP sIP = args.sIP
@@ -79,5 +110,5 @@ def CreateAI(pID: int) -> IAI:
return AI(pID) return AI(pID)




if __name__ == '__main__':
if __name__ == "__main__":
THUAI6Main(sys.argv, CreateAI) THUAI6Main(sys.argv, CreateAI)

+ 2
- 4
CAPI/python/PyAPI/structures.py View File

@@ -52,6 +52,7 @@ class PropType(Enum):
AddHpOrAp = 6 AddHpOrAp = 6
ShieldOrSpear = 7 ShieldOrSpear = 7
RecoveryFromDizziness = 8 RecoveryFromDizziness = 8
CraftingBench = 9




class BulletType(Enum): class BulletType(Enum):
@@ -60,7 +61,7 @@ class BulletType(Enum):
CommonAttackOfTricker = 2 CommonAttackOfTricker = 2
BombBomb = 3 BombBomb = 3
JumpyDumpty = 4 JumpyDumpty = 4
AtomBomb = 5
Strike = 5




class StudentType(Enum): class StudentType(Enum):
@@ -156,7 +157,6 @@ class Player:
self.timeUntilSkillAvailable: List[float] = [] self.timeUntilSkillAvailable: List[float] = []
self.playerType: PlayerType = PlayerType.NullPlayerType self.playerType: PlayerType = PlayerType.NullPlayerType
self.prop: List[PropType] = [] self.prop: List[PropType] = []
self.place: PlaceType = PlaceType.NullPlaceType
self.bulletType: BulletType = BulletType.NullBulletType self.bulletType: BulletType = BulletType.NullBulletType
self.playerState: PlayerState = PlayerState.NullState self.playerState: PlayerState = PlayerState.NullState


@@ -190,7 +190,6 @@ class Prop:
self.y: int = 0 self.y: int = 0
self.guid: int = 0 self.guid: int = 0
self.type: PropType = PropType.NullPropType self.type: PropType = PropType.NullPropType
self.place: PlaceType = PlaceType.NullPlaceType
self.facingDirection: float = 0.0 self.facingDirection: float = 0.0




@@ -202,7 +201,6 @@ class Bullet:
self.facingDirection: float = 0.0 self.facingDirection: float = 0.0
self.guid: int = 0 self.guid: int = 0
self.team: PlayerType = PlayerType.NullPlayerType self.team: PlayerType = PlayerType.NullPlayerType
self.place: PlaceType = PlaceType.NullPlaceType
self.bombRange: float = 0.0 self.bombRange: float = 0.0
self.speed: int = 0 self.speed: int = 0




+ 123
- 57
CAPI/python/PyAPI/utils.py View File

@@ -15,7 +15,6 @@ class NoInstance:




class AssistFunction(NoInstance): class AssistFunction(NoInstance):

@staticmethod @staticmethod
def CellToGrid(cell: int) -> int: def CellToGrid(cell: int) -> int:
return cell * numOfGridPerCell + numOfGridPerCell // 2 return cell * numOfGridPerCell + numOfGridPerCell // 2
@@ -25,14 +24,19 @@ class AssistFunction(NoInstance):
return grid // numOfGridPerCell return grid // numOfGridPerCell


@staticmethod @staticmethod
def HaveView(viewRange: int, x: int, y: int, newX: int, newY: int, map: List[List[THUAI6.PlaceType]]) -> bool:
def HaveView(
viewRange: int,
x: int,
y: int,
newX: int,
newY: int,
map: List[List[THUAI6.PlaceType]],
) -> bool:
deltaX: int = newX - x deltaX: int = newX - x
deltaY: int = newY - y deltaY: int = newY - y
distance: float = deltaX**2 + deltaY**2 distance: float = deltaX**2 + deltaY**2
myPlace = map[AssistFunction.GridToCell(
x)][AssistFunction.GridToCell(y)]
newPlace = map[AssistFunction.GridToCell(
newX)][AssistFunction.GridToCell(newY)]
myPlace = map[AssistFunction.GridToCell(x)][AssistFunction.GridToCell(y)]
newPlace = map[AssistFunction.GridToCell(newX)][AssistFunction.GridToCell(newY)]
if myPlace != THUAI6.PlaceType.Grass and newPlace == THUAI6.PlaceType.Grass: if myPlace != THUAI6.PlaceType.Grass and newPlace == THUAI6.PlaceType.Grass:
return False return False
if distance <= viewRange * viewRange: if distance <= viewRange * viewRange:
@@ -47,7 +51,12 @@ class AssistFunction(NoInstance):
for i in range(divide): for i in range(divide):
selfX += dx selfX += dx
selfY += dy selfY += dy
if map[AssistFunction.GridToCell(int(selfX))][AssistFunction.GridToCell(int(selfY))] != THUAI6.PlaceType.Grass:
if (
map[AssistFunction.GridToCell(int(selfX))][
AssistFunction.GridToCell(int(selfY))
]
!= THUAI6.PlaceType.Grass
):
return False return False
else: else:
return True return True
@@ -55,7 +64,12 @@ class AssistFunction(NoInstance):
for i in range(divide): for i in range(divide):
selfX += dx selfX += dx
selfY += dy selfY += dy
if map[AssistFunction.GridToCell(int(selfX))][AssistFunction.GridToCell(int(selfY))] == THUAI6.PlaceType.Wall:
if (
map[AssistFunction.GridToCell(int(selfX))][
AssistFunction.GridToCell(int(selfY))
]
== THUAI6.PlaceType.Wall
):
return False return False
else: else:
return True return True
@@ -76,12 +90,14 @@ class Proto2THUAI6(NoInstance):
MessageType.DOOR3: THUAI6.PlaceType.Door3, MessageType.DOOR3: THUAI6.PlaceType.Door3,
MessageType.DOOR5: THUAI6.PlaceType.Door5, MessageType.DOOR5: THUAI6.PlaceType.Door5,
MessageType.DOOR6: THUAI6.PlaceType.Door6, MessageType.DOOR6: THUAI6.PlaceType.Door6,
MessageType.CHEST: THUAI6.PlaceType.Chest, }
MessageType.CHEST: THUAI6.PlaceType.Chest,
}


shapeTypeDict: Final[dict] = { shapeTypeDict: Final[dict] = {
MessageType.NULL_SHAPE_TYPE: THUAI6.ShapeType.NullShapeType, MessageType.NULL_SHAPE_TYPE: THUAI6.ShapeType.NullShapeType,
MessageType.SQUARE: THUAI6.ShapeType.Square, MessageType.SQUARE: THUAI6.ShapeType.Square,
MessageType.CIRCLE: THUAI6.ShapeType.Circle}
MessageType.CIRCLE: THUAI6.ShapeType.Circle,
}


propTypeDict: Final[dict] = { propTypeDict: Final[dict] = {
MessageType.NULL_PROP_TYPE: THUAI6.PropType.NullPropType, MessageType.NULL_PROP_TYPE: THUAI6.PropType.NullPropType,
@@ -92,12 +108,15 @@ class Proto2THUAI6(NoInstance):
MessageType.ADD_HP_OR_AP: THUAI6.PropType.AddHpOrAp, MessageType.ADD_HP_OR_AP: THUAI6.PropType.AddHpOrAp,
MessageType.ADD_LIFE_OR_CLAIRAUDIENCE: THUAI6.PropType.AddLifeOrClairaudience, MessageType.ADD_LIFE_OR_CLAIRAUDIENCE: THUAI6.PropType.AddLifeOrClairaudience,
MessageType.SHIELD_OR_SPEAR: THUAI6.PropType.ShieldOrSpear, MessageType.SHIELD_OR_SPEAR: THUAI6.PropType.ShieldOrSpear,
MessageType.RECOVERY_FROM_DIZZINESS: THUAI6.PropType.RecoveryFromDizziness, }
MessageType.RECOVERY_FROM_DIZZINESS: THUAI6.PropType.RecoveryFromDizziness,
MessageType.CRAFTING_BENCH: THUAI6.PropType.CraftingBench,
}


playerTypeDict: Final[dict] = { playerTypeDict: Final[dict] = {
MessageType.NULL_PLAYER_TYPE: THUAI6.PlayerType.NullPlayerType, MessageType.NULL_PLAYER_TYPE: THUAI6.PlayerType.NullPlayerType,
MessageType.STUDENT_PLAYER: THUAI6.PlayerType.StudentPlayer, MessageType.STUDENT_PLAYER: THUAI6.PlayerType.StudentPlayer,
MessageType.TRICKER_PLAYER: THUAI6.PlayerType.TrickerPlayer, }
MessageType.TRICKER_PLAYER: THUAI6.PlayerType.TrickerPlayer,
}


studentTypeDict: Final[dict] = { studentTypeDict: Final[dict] = {
MessageType.NULL_STUDENT_TYPE: THUAI6.StudentType.NullStudentType, MessageType.NULL_STUDENT_TYPE: THUAI6.StudentType.NullStudentType,
@@ -106,21 +125,24 @@ class Proto2THUAI6(NoInstance):
MessageType.STRAIGHT_A_STUDENT: THUAI6.StudentType.StraightAStudent, MessageType.STRAIGHT_A_STUDENT: THUAI6.StudentType.StraightAStudent,
MessageType.ROBOT: THUAI6.StudentType.Robot, MessageType.ROBOT: THUAI6.StudentType.Robot,
MessageType.TECH_OTAKU: THUAI6.StudentType.TechOtaku, MessageType.TECH_OTAKU: THUAI6.StudentType.TechOtaku,
MessageType.SUNSHINE: THUAI6.StudentType.Sunshine, }
MessageType.SUNSHINE: THUAI6.StudentType.Sunshine,
}


trickerTypeDict: Final[dict] = { trickerTypeDict: Final[dict] = {
MessageType.NULL_TRICKER_TYPE: THUAI6.TrickerType.NullTrickerType, MessageType.NULL_TRICKER_TYPE: THUAI6.TrickerType.NullTrickerType,
MessageType.ASSASSIN: THUAI6.TrickerType.Assassin, MessageType.ASSASSIN: THUAI6.TrickerType.Assassin,
MessageType.KLEE: THUAI6.TrickerType.Klee, MessageType.KLEE: THUAI6.TrickerType.Klee,
MessageType.A_NOISY_PERSON: THUAI6.TrickerType.ANoisyPerson, MessageType.A_NOISY_PERSON: THUAI6.TrickerType.ANoisyPerson,
MessageType.IDOL: THUAI6.TrickerType.Idol, }
MessageType.IDOL: THUAI6.TrickerType.Idol,
}


studentBuffTypeDict: Final[dict] = { studentBuffTypeDict: Final[dict] = {
MessageType.NULL_SBUFF_TYPE: THUAI6.StudentBuffType.NullStudentBuffType, MessageType.NULL_SBUFF_TYPE: THUAI6.StudentBuffType.NullStudentBuffType,
MessageType.STUDENT_ADD_SPEED: THUAI6.StudentBuffType.AddSpeed, MessageType.STUDENT_ADD_SPEED: THUAI6.StudentBuffType.AddSpeed,
MessageType.ADD_LIFE: THUAI6.StudentBuffType.AddLife, MessageType.ADD_LIFE: THUAI6.StudentBuffType.AddLife,
MessageType.SHIELD: THUAI6.StudentBuffType.Shield, MessageType.SHIELD: THUAI6.StudentBuffType.Shield,
MessageType.STUDENT_INVISIBLE: THUAI6.StudentBuffType.Invisible, }
MessageType.STUDENT_INVISIBLE: THUAI6.StudentBuffType.Invisible,
}


trickerBuffTypeDict: Final[dict] = { trickerBuffTypeDict: Final[dict] = {
MessageType.NULL_TBUFF_TYPE: THUAI6.TrickerBuffType.NullTrickerBuffType, MessageType.NULL_TBUFF_TYPE: THUAI6.TrickerBuffType.NullTrickerBuffType,
@@ -128,7 +150,8 @@ class Proto2THUAI6(NoInstance):
MessageType.SPEAR: THUAI6.TrickerBuffType.Spear, MessageType.SPEAR: THUAI6.TrickerBuffType.Spear,
MessageType.ADD_AP: THUAI6.TrickerBuffType.AddAp, MessageType.ADD_AP: THUAI6.TrickerBuffType.AddAp,
MessageType.CLAIRAUDIENCE: THUAI6.TrickerBuffType.Clairaudience, MessageType.CLAIRAUDIENCE: THUAI6.TrickerBuffType.Clairaudience,
MessageType.TRICKER_INVISIBLE: THUAI6.TrickerBuffType.Invisible, }
MessageType.TRICKER_INVISIBLE: THUAI6.TrickerBuffType.Invisible,
}


playerStateDict: Final[dict] = { playerStateDict: Final[dict] = {
MessageType.NULL_STATUS: THUAI6.PlayerState.NullState, MessageType.NULL_STATUS: THUAI6.PlayerState.NullState,
@@ -149,13 +172,15 @@ class Proto2THUAI6(NoInstance):
MessageType.CLIMBING: THUAI6.PlayerState.Climbing, MessageType.CLIMBING: THUAI6.PlayerState.Climbing,
MessageType.OPENING_A_CHEST: THUAI6.PlayerState.OpeningAChest, MessageType.OPENING_A_CHEST: THUAI6.PlayerState.OpeningAChest,
MessageType.USING_SPECIAL_SKILL: THUAI6.PlayerState.UsingSpecialSkill, MessageType.USING_SPECIAL_SKILL: THUAI6.PlayerState.UsingSpecialSkill,
MessageType.OPENING_A_GATE: THUAI6.PlayerState.OpeningAGate, }
MessageType.OPENING_A_GATE: THUAI6.PlayerState.OpeningAGate,
}


gameStateDict: Final[dict] = { gameStateDict: Final[dict] = {
MessageType.NULL_GAME_STATE: THUAI6.GameState.NullGameState, MessageType.NULL_GAME_STATE: THUAI6.GameState.NullGameState,
MessageType.GAME_START: THUAI6.GameState.GameStart, MessageType.GAME_START: THUAI6.GameState.GameStart,
MessageType.GAME_RUNNING: THUAI6.GameState.GameRunning, MessageType.GAME_RUNNING: THUAI6.GameState.GameRunning,
MessageType.GAME_END: THUAI6.GameState.GameEnd}
MessageType.GAME_END: THUAI6.GameState.GameEnd,
}


bulletTypeDict: Final[dict] = { bulletTypeDict: Final[dict] = {
MessageType.NULL_BULLET_TYPE: THUAI6.BulletType.NullBulletType, MessageType.NULL_BULLET_TYPE: THUAI6.BulletType.NullBulletType,
@@ -163,11 +188,14 @@ class Proto2THUAI6(NoInstance):
MessageType.BOMB_BOMB: THUAI6.BulletType.BombBomb, MessageType.BOMB_BOMB: THUAI6.BulletType.BombBomb,
MessageType.COMMON_ATTACK_OF_TRICKER: THUAI6.BulletType.CommonAttackOfTricker, MessageType.COMMON_ATTACK_OF_TRICKER: THUAI6.BulletType.CommonAttackOfTricker,
MessageType.JUMPY_DUMPTY: THUAI6.BulletType.JumpyDumpty, MessageType.JUMPY_DUMPTY: THUAI6.BulletType.JumpyDumpty,
MessageType.ATOM_BOMB: THUAI6.BulletType.AtomBomb, }
MessageType.STRIKE: THUAI6.BulletType.Strike,
}


# 用于将Proto的对象转为THUAI6的对象 # 用于将Proto的对象转为THUAI6的对象
@ staticmethod
def Protobuf2THUAI6Tricker(trickerMsg: Message2Clients.MessageOfTricker) -> THUAI6.Tricker:
@staticmethod
def Protobuf2THUAI6Tricker(
trickerMsg: Message2Clients.MessageOfTricker,
) -> THUAI6.Tricker:
tricker = THUAI6.Tricker() tricker = THUAI6.Tricker()
tricker.x = trickerMsg.x tricker.x = trickerMsg.x
tricker.y = trickerMsg.y tricker.y = trickerMsg.y
@@ -179,7 +207,6 @@ class Proto2THUAI6(NoInstance):
tricker.bulletType = Proto2THUAI6.bulletTypeDict[trickerMsg.bullet_type] tricker.bulletType = Proto2THUAI6.bulletTypeDict[trickerMsg.bullet_type]
for time in trickerMsg.time_until_skill_available: for time in trickerMsg.time_until_skill_available:
tricker.timeUntilSkillAvailable.append(time) tricker.timeUntilSkillAvailable.append(time)
tricker.place = Proto2THUAI6.placeTypeDict[trickerMsg.place]
tricker.playerState = Proto2THUAI6.playerStateDict[trickerMsg.player_state] tricker.playerState = Proto2THUAI6.playerStateDict[trickerMsg.player_state]
for item in trickerMsg.prop: for item in trickerMsg.prop:
tricker.prop.append(Proto2THUAI6.propTypeDict[item]) tricker.prop.append(Proto2THUAI6.propTypeDict[item])
@@ -193,8 +220,10 @@ class Proto2THUAI6(NoInstance):
tricker.playerType = THUAI6.PlayerType.TrickerPlayer tricker.playerType = THUAI6.PlayerType.TrickerPlayer
return tricker return tricker


@ staticmethod
def Protobuf2THUAI6Student(studentMsg: Message2Clients.MessageOfStudent) -> THUAI6.Student:
@staticmethod
def Protobuf2THUAI6Student(
studentMsg: Message2Clients.MessageOfStudent,
) -> THUAI6.Student:
student = THUAI6.Student() student = THUAI6.Student()
student.x = studentMsg.x student.x = studentMsg.x
student.y = studentMsg.y student.y = studentMsg.y
@@ -211,7 +240,6 @@ class Proto2THUAI6(NoInstance):
student.dangerAlert = studentMsg.danger_alert student.dangerAlert = studentMsg.danger_alert
for time in studentMsg.time_until_skill_available: for time in studentMsg.time_until_skill_available:
student.timeUntilSkillAvailable.append(time) student.timeUntilSkillAvailable.append(time)
student.place = Proto2THUAI6.placeTypeDict[studentMsg.place]
for item in studentMsg.prop: for item in studentMsg.prop:
student.prop.append(Proto2THUAI6.propTypeDict[item]) student.prop.append(Proto2THUAI6.propTypeDict[item])
student.studentType = Proto2THUAI6.studentTypeDict[studentMsg.student_type] student.studentType = Proto2THUAI6.studentTypeDict[studentMsg.student_type]
@@ -225,7 +253,7 @@ class Proto2THUAI6(NoInstance):
student.playerType = THUAI6.PlayerType.StudentPlayer student.playerType = THUAI6.PlayerType.StudentPlayer
return student return student


@ staticmethod
@staticmethod
def Protobuf2THUAI6Prop(propMsg: Message2Clients.MessageOfProp) -> THUAI6.Prop: def Protobuf2THUAI6Prop(propMsg: Message2Clients.MessageOfProp) -> THUAI6.Prop:
prop = THUAI6.Prop() prop = THUAI6.Prop()
prop.x = propMsg.x prop.x = propMsg.x
@@ -235,7 +263,7 @@ class Proto2THUAI6(NoInstance):
prop.facingDirection = propMsg.facing_direction prop.facingDirection = propMsg.facing_direction
return prop return prop


@ staticmethod
@staticmethod
def Protobuf2THUAI6GameInfo(allMsg: Message2Clients.MessageOfAll): def Protobuf2THUAI6GameInfo(allMsg: Message2Clients.MessageOfAll):
gameInfo = THUAI6.GameInfo() gameInfo = THUAI6.GameInfo()
gameInfo.gameTime = allMsg.game_time gameInfo.gameTime = allMsg.game_time
@@ -246,8 +274,10 @@ class Proto2THUAI6(NoInstance):
gameInfo.trickerScore = allMsg.tricker_score gameInfo.trickerScore = allMsg.tricker_score
return gameInfo return gameInfo


@ staticmethod
def Protobuf2THUAI6Bullet(bulletMsg: Message2Clients.MessageOfBullet) -> THUAI6.Bullet:
@staticmethod
def Protobuf2THUAI6Bullet(
bulletMsg: Message2Clients.MessageOfBullet,
) -> THUAI6.Bullet:
bullet = THUAI6.Bullet() bullet = THUAI6.Bullet()
bullet.x = bulletMsg.x bullet.x = bulletMsg.x
bullet.y = bulletMsg.y bullet.y = bulletMsg.y
@@ -255,12 +285,13 @@ class Proto2THUAI6(NoInstance):
bullet.facingDirection = bulletMsg.facing_direction bullet.facingDirection = bulletMsg.facing_direction
bullet.guid = bulletMsg.guid bullet.guid = bulletMsg.guid
bullet.team = Proto2THUAI6.playerTypeDict[bulletMsg.team] bullet.team = Proto2THUAI6.playerTypeDict[bulletMsg.team]
bullet.place = Proto2THUAI6.placeTypeDict[bulletMsg.place]
bullet.bombRange = bulletMsg.bomb_range bullet.bombRange = bulletMsg.bomb_range
return bullet return bullet


@ staticmethod
def Protobuf2THUAI6BombedBullet(bulletMsg: Message2Clients.MessageOfBombedBullet) -> THUAI6.BombedBullet:
@staticmethod
def Protobuf2THUAI6BombedBullet(
bulletMsg: Message2Clients.MessageOfBombedBullet,
) -> THUAI6.BombedBullet:
bullet = THUAI6.BombedBullet() bullet = THUAI6.BombedBullet()
bullet.x = bulletMsg.x bullet.x = bulletMsg.x
bullet.y = bulletMsg.y bullet.y = bulletMsg.y
@@ -270,7 +301,7 @@ class Proto2THUAI6(NoInstance):
bullet.bombRange = bulletMsg.bomb_range bullet.bombRange = bulletMsg.bomb_range
return bullet return bullet


@ staticmethod
@staticmethod
def Bool2HiddenGateState(gateMsg: bool) -> THUAI6.HiddenGateState: def Bool2HiddenGateState(gateMsg: bool) -> THUAI6.HiddenGateState:
if gateMsg: if gateMsg:
return THUAI6.HiddenGateState.Opened return THUAI6.HiddenGateState.Opened
@@ -291,12 +322,14 @@ class THUAI62Proto(NoInstance):
THUAI6.PlaceType.Door5: MessageType.DOOR5, THUAI6.PlaceType.Door5: MessageType.DOOR5,
THUAI6.PlaceType.Door6: MessageType.DOOR6, THUAI6.PlaceType.Door6: MessageType.DOOR6,
THUAI6.PlaceType.Chest: MessageType.CHEST, THUAI6.PlaceType.Chest: MessageType.CHEST,
THUAI6.PlaceType.Window: MessageType.WINDOW, }
THUAI6.PlaceType.Window: MessageType.WINDOW,
}


playerTypeDict: Final[dict] = { playerTypeDict: Final[dict] = {
THUAI6.PlayerType.NullPlayerType: MessageType.NULL_PLAYER_TYPE, THUAI6.PlayerType.NullPlayerType: MessageType.NULL_PLAYER_TYPE,
THUAI6.PlayerType.StudentPlayer: MessageType.STUDENT_PLAYER, THUAI6.PlayerType.StudentPlayer: MessageType.STUDENT_PLAYER,
THUAI6.PlayerType.TrickerPlayer: MessageType.TRICKER_PLAYER}
THUAI6.PlayerType.TrickerPlayer: MessageType.TRICKER_PLAYER,
}


studentTypeDict: Final[dict] = { studentTypeDict: Final[dict] = {
THUAI6.StudentType.NullStudentType: MessageType.NULL_STUDENT_TYPE, THUAI6.StudentType.NullStudentType: MessageType.NULL_STUDENT_TYPE,
@@ -305,14 +338,16 @@ class THUAI62Proto(NoInstance):
THUAI6.StudentType.StraightAStudent: MessageType.STRAIGHT_A_STUDENT, THUAI6.StudentType.StraightAStudent: MessageType.STRAIGHT_A_STUDENT,
THUAI6.StudentType.Robot: MessageType.ROBOT, THUAI6.StudentType.Robot: MessageType.ROBOT,
THUAI6.StudentType.TechOtaku: MessageType.TECH_OTAKU, THUAI6.StudentType.TechOtaku: MessageType.TECH_OTAKU,
THUAI6.StudentType.Sunshine: MessageType.SUNSHINE, }
THUAI6.StudentType.Sunshine: MessageType.SUNSHINE,
}


trickerTypeDict: Final[dict] = { trickerTypeDict: Final[dict] = {
THUAI6.TrickerType.NullTrickerType: MessageType.NULL_TRICKER_TYPE, THUAI6.TrickerType.NullTrickerType: MessageType.NULL_TRICKER_TYPE,
THUAI6.TrickerType.Assassin: MessageType.ASSASSIN, THUAI6.TrickerType.Assassin: MessageType.ASSASSIN,
THUAI6.TrickerType.Klee: MessageType.KLEE, THUAI6.TrickerType.Klee: MessageType.KLEE,
THUAI6.TrickerType.ANoisyPerson: MessageType.A_NOISY_PERSON, THUAI6.TrickerType.ANoisyPerson: MessageType.A_NOISY_PERSON,
THUAI6.TrickerType.Idol: MessageType.IDOL, }
THUAI6.TrickerType.Idol: MessageType.IDOL,
}


propTypeDict: Final[dict] = { propTypeDict: Final[dict] = {
THUAI6.PropType.NullPropType: MessageType.NULL_PROP_TYPE, THUAI6.PropType.NullPropType: MessageType.NULL_PROP_TYPE,
@@ -322,44 +357,75 @@ class THUAI62Proto(NoInstance):
THUAI6.PropType.AddHpOrAp: MessageType.ADD_HP_OR_AP, THUAI6.PropType.AddHpOrAp: MessageType.ADD_HP_OR_AP,
THUAI6.PropType.AddLifeOrClairaudience: MessageType.ADD_LIFE_OR_CLAIRAUDIENCE, THUAI6.PropType.AddLifeOrClairaudience: MessageType.ADD_LIFE_OR_CLAIRAUDIENCE,
THUAI6.PropType.AddSpeed: MessageType.ADD_SPEED, THUAI6.PropType.AddSpeed: MessageType.ADD_SPEED,
THUAI6.PropType.ShieldOrSpear: MessageType.SHIELD_OR_SPEAR, }
THUAI6.PropType.ShieldOrSpear: MessageType.SHIELD_OR_SPEAR,
THUAI6.PropType.CraftingBench: MessageType.CRAFTING_BENCH,
}


# 用于将THUAI6的对象转为Proto的对象 # 用于将THUAI6的对象转为Proto的对象


@ staticmethod
def THUAI62ProtobufPlayer(playerID: int, playerType: THUAI6.PlayerType, studentType: THUAI6.StudentType, trickerType: THUAI6.TrickerType) -> Message2Server.PlayerMsg:
@staticmethod
def THUAI62ProtobufPlayer(
playerID: int,
playerType: THUAI6.PlayerType,
studentType: THUAI6.StudentType,
trickerType: THUAI6.TrickerType,
) -> Message2Server.PlayerMsg:
if playerType == THUAI6.PlayerType.StudentPlayer: if playerType == THUAI6.PlayerType.StudentPlayer:
return Message2Server.PlayerMsg(player_id=playerID, player_type=MessageType.STUDENT_PLAYER, student_type=THUAI62Proto.studentTypeDict[studentType])
return Message2Server.PlayerMsg(
player_id=playerID,
player_type=MessageType.STUDENT_PLAYER,
student_type=THUAI62Proto.studentTypeDict[studentType],
)
else: else:
return Message2Server.PlayerMsg(player_id=playerID, player_type=MessageType.TRICKER_PLAYER, tricker_type=THUAI62Proto.trickerTypeDict[trickerType])
return Message2Server.PlayerMsg(
player_id=playerID,
player_type=MessageType.TRICKER_PLAYER,
tricker_type=THUAI62Proto.trickerTypeDict[trickerType],
)


@ staticmethod
@staticmethod
def THUAI62ProtobufID(playerID: int) -> Message2Server.IDMsg: def THUAI62ProtobufID(playerID: int) -> Message2Server.IDMsg:
return Message2Server.IDMsg(player_id=playerID) return Message2Server.IDMsg(player_id=playerID)


@ staticmethod
@staticmethod
def THUAI62ProtobufMove(time: int, angle: float, id: int) -> Message2Server.MoveMsg: def THUAI62ProtobufMove(time: int, angle: float, id: int) -> Message2Server.MoveMsg:
return Message2Server.MoveMsg(player_id=id, angle=angle, time_in_milliseconds=time)
return Message2Server.MoveMsg(
player_id=id, angle=angle, time_in_milliseconds=time
)


@ staticmethod
def THUAI62ProtobufTreatAndRescue(playerID: int, mateID: int) -> Message2Server.TreatAndRescueMsg:
@staticmethod
def THUAI62ProtobufTreatAndRescue(
playerID: int, mateID: int
) -> Message2Server.TreatAndRescueMsg:
return Message2Server.TreatAndRescueMsg(player_id=playerID, to_player_id=mateID) return Message2Server.TreatAndRescueMsg(player_id=playerID, to_player_id=mateID)


@ staticmethod
@staticmethod
def THUAI62ProtobufProp(prop: THUAI6.PropType, id: int) -> Message2Server.PropMsg: def THUAI62ProtobufProp(prop: THUAI6.PropType, id: int) -> Message2Server.PropMsg:
return Message2Server.PropMsg(player_id=id, prop_type=THUAI62Proto.propTypeDict[prop])
return Message2Server.PropMsg(
player_id=id, prop_type=THUAI62Proto.propTypeDict[prop]
)


@ staticmethod
def THUAI62ProtobufSend(msg: Union[str, bytes], toID: int, id: int) -> Message2Server.SendMsg:
@staticmethod
def THUAI62ProtobufSend(
msg: Union[str, bytes], toID: int, id: int
) -> Message2Server.SendMsg:
if isinstance(msg, str): if isinstance(msg, str):
return Message2Server.SendMsg(player_id=id, to_player_id=toID, text_message=msg)
return Message2Server.SendMsg(
player_id=id, to_player_id=toID, text_message=msg
)
elif isinstance(msg, bytes): elif isinstance(msg, bytes):
return Message2Server.SendMsg(player_id=id, to_player_id=toID, binary_message=msg)
return Message2Server.SendMsg(
player_id=id, to_player_id=toID, binary_message=msg
)


@ staticmethod
@staticmethod
def THUAI62ProtobufAttack(angle: float, id: int) -> Message2Server.AttackMsg: def THUAI62ProtobufAttack(angle: float, id: int) -> Message2Server.AttackMsg:
return Message2Server.AttackMsg(player_id=id, angle=angle) return Message2Server.AttackMsg(player_id=id, angle=angle)


@ staticmethod
def THUAI62ProtobufSkill(skillID: int, id: int) -> Message2Server.SkillMsg:
return Message2Server.SkillMsg(player_id=id, skill_id=skillID)
@staticmethod
def THUAI62ProtobufSkill(
skillID: int, skillParam: int, id: int
) -> Message2Server.SkillMsg:
return Message2Server.SkillMsg(
player_id=id, skill_id=skillID, skill_param=skillParam
)

+ 2
- 2
CAPI/python/requirements.txt View File

@@ -1,3 +1,3 @@
grpcio==1.52.0
grpcio-tools==1.52.0
grpcio==1.54.2
grpcio-tools==1.54.2
numpy numpy

+ 1
- 1
CAPI/python/run.sh View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash


python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 0 -d -o& python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 0 -d -o&
# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 1 -o&
python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 1 -d -o&
# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 2& # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 2&
# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 3& # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 3&
# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 4& # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 4&

+ 1
- 1
CAPI/shell/RunServer.sh View File

@@ -1,3 +1,3 @@
#!/usr/bin/env bash #!/usr/bin/env bash


./linux64/Server --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600
./linux64/Server --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video --mapResource ".\map\map1_final.txt"

+ 1
- 1
CAPI/shell/RunServerForDebug.sh View File

@@ -1,3 +1,3 @@
#!/usr/bin/env bash #!/usr/bin/env bash


./linux64/Debug/Server --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600
./linux64/Debug/Server --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video --mapResource ".\map\map1_final.txt"

+ 3
- 1
README.md View File

@@ -70,6 +70,8 @@ THUAI6 开发组成员与其他贡献者应当遵循以下流程:
3. 在新的分支上进行修改与开发 3. 在新的分支上进行修改与开发
4. 向 `eesast/THUAI6` 的 `dev` 分支提出 pull request 4. 向 `eesast/THUAI6` 的 `dev` 分支提出 pull request
5. 等待其他开发组成员 review 与 merge 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 时的注意事项 ### 使用 Git 与 Github 时的注意事项


@@ -152,7 +154,7 @@ THUAI6 开发组成员与其他贡献者应当遵循以下流程:


## 文档风格 ## 文档风格


仓库的文档使用 Markdown 语法,具体语法可以参照 [Markdown 语法文档](https://docs.github.com/zh/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#ignoring-markdown-formatting)。
仓库的文档使用 Markdown 语法,具体语法可以参照 [Markdown 语法文档](https://docs.github.com/zh/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)。


中文文档的书写须严格遵循:[中午技术文档规范](https://github.com/ruanyf/document-style-guide)、[中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/),以写出更美观的中文文档。例如:[中文文字与西文文字间空格](https://github.com/ruanyf/document-style-guide/blob/master/docs/text.md#%E5%AD%97%E9%97%B4%E8%B7%9D)、[全角标点符号的正确使用](https://github.com/ruanyf/document-style-guide/blob/master/docs/marks.md),等等。 中文文档的书写须严格遵循:[中午技术文档规范](https://github.com/ruanyf/document-style-guide)、[中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/),以写出更美观的中文文档。例如:[中文文字与西文文字间空格](https://github.com/ruanyf/document-style-guide/blob/master/docs/text.md#%E5%AD%97%E9%97%B4%E8%B7%9D)、[全角标点符号的正确使用](https://github.com/ruanyf/document-style-guide/blob/master/docs/marks.md),等等。




+ 1
- 0
dependency/Dockerfile/Dockerfile_cpp View File

@@ -4,6 +4,7 @@ MAINTAINER eesast
WORKDIR /usr/local WORKDIR /usr/local


COPY ./CAPI/cpp /usr/local/PlayerCode/CAPI/cpp COPY ./CAPI/cpp /usr/local/PlayerCode/CAPI/cpp
COPY ./CAPI/python /usr/local/PlayerCode/CAPI/python
COPY ./dependency/proto /usr/local/PlayerCode/dependency/proto COPY ./dependency/proto /usr/local/PlayerCode/dependency/proto
COPY ./dependency/shell /usr/local/PlayerCode/dependency/shell COPY ./dependency/shell /usr/local/PlayerCode/dependency/shell
WORKDIR /usr/local/PlayerCode/dependency/proto WORKDIR /usr/local/PlayerCode/dependency/proto


+ 4
- 4
dependency/proto/Message2Clients.proto View File

@@ -12,7 +12,7 @@ message MessageOfStudent
int32 determination = 4; // 剩余的学习毅力,相当于血量 int32 determination = 4; // 剩余的学习毅力,相当于血量
int32 addiction = 5; // 沉迷程度,相当于淘汰进度 int32 addiction = 5; // 沉迷程度,相当于淘汰进度
repeated double time_until_skill_available = 6; repeated double time_until_skill_available = 6;
PlaceType place = 7;
// PlaceType place = 7;
repeated PropType prop = 8; repeated PropType prop = 8;
PlayerState player_state = 9; PlayerState player_state = 9;
int64 guid = 10; int64 guid = 10;
@@ -37,7 +37,7 @@ message MessageOfTricker
int32 y = 2; int32 y = 2;
int32 speed = 3; int32 speed = 3;
repeated double time_until_skill_available = 5; repeated double time_until_skill_available = 5;
PlaceType place = 6;
// PlaceType place = 6;
repeated PropType prop = 7; repeated PropType prop = 7;
TrickerType tricker_type = 8; TrickerType tricker_type = 8;
int64 guid = 9; int64 guid = 9;
@@ -61,7 +61,7 @@ message MessageOfBullet
double facing_direction = 4; double facing_direction = 4;
int64 guid = 5; int64 guid = 5;
PlayerType team = 6; PlayerType team = 6;
PlaceType place = 7;
// PlaceType place = 7;
double bomb_range = 8; double bomb_range = 8;
int32 speed = 9; int32 speed = 9;
} }
@@ -83,7 +83,7 @@ message MessageOfProp // 可拾取道具的信息
int32 y = 3; int32 y = 3;
double facing_direction = 4; double facing_direction = 4;
int64 guid = 5; int64 guid = 5;
PlaceType place = 6;
// PlaceType place = 6;
} }






+ 1
- 0
dependency/proto/Message2Server.proto View File

@@ -61,6 +61,7 @@ message SkillMsg
{ {
int64 player_id = 1; int64 player_id = 1;
int32 skill_id = 2; int32 skill_id = 2;
int32 skill_param = 3;
} }


// 基本继承于THUAI5,为了使发送的信息尽可能不被浪费,暂定不发这类大包。 // 基本继承于THUAI5,为了使发送的信息尽可能不被浪费,暂定不发这类大包。


+ 4
- 3
dependency/proto/MessageType.proto View File

@@ -9,7 +9,7 @@ enum BulletType
COMMON_ATTACK_OF_TRICKER = 2; COMMON_ATTACK_OF_TRICKER = 2;
BOMB_BOMB = 3; BOMB_BOMB = 3;
JUMPY_DUMPTY = 4; JUMPY_DUMPTY = 4;
ATOM_BOMB = 5;
STRIKE = 5;
} }


enum PlaceType // 地图中的所有物件类型 enum PlaceType // 地图中的所有物件类型
@@ -49,6 +49,7 @@ enum PropType // 地图中的可拾取道具类型
KEY5 = 6; KEY5 = 6;
KEY6 = 7; KEY6 = 7;
RECOVERY_FROM_DIZZINESS = 8; RECOVERY_FROM_DIZZINESS = 8;
CRAFTING_BENCH = 9;
} }


enum StudentBuffType // 人类可用的增益效果类型 enum StudentBuffType // 人类可用的增益效果类型
@@ -78,7 +79,7 @@ enum PlayerState
LOCKING = 13; LOCKING = 13;
RUMMAGING = 14; RUMMAGING = 14;
CLIMBING = 15; // 翻窗 CLIMBING = 15; // 翻窗
OPENING_A_CHEST =16;
OPENING_A_CHEST = 16;
USING_SPECIAL_SKILL = 17; USING_SPECIAL_SKILL = 17;
OPENING_A_GATE =18; OPENING_A_GATE =18;
} }
@@ -109,7 +110,7 @@ enum StudentType
TEACHER = 2; TEACHER = 2;
STRAIGHT_A_STUDENT = 3; STRAIGHT_A_STUDENT = 3;
ROBOT = 4; ROBOT = 4;
TECH_OTAKU =5;
TECH_OTAKU = 5;
SUNSHINE = 6; SUNSHINE = 6;
} }




+ 0
- 0
dependency/proto/Proto.sln View File


+ 2
- 2
dependency/proto/Protos.csproj View File

@@ -14,8 +14,8 @@
</ItemGroup>--> </ItemGroup>-->


<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.3" />
<PackageReference Include="Google.Protobuf" Version="3.22.4" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.4" />
<PackageReference Include="Grpc" Version="2.46.6" /> <PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Core" Version="2.46.6" /> <PackageReference Include="Grpc.Core" Version="2.46.6" />
<PackageReference Include="Grpc.Tools" Version="2.54.0"> <PackageReference Include="Grpc.Tools" Version="2.54.0">


+ 205
- 0
dependency/py/rank.py View File

@@ -0,0 +1,205 @@
import os
import json
import re

winGamesMap = {}
totalGamesMap = {}
studentTotalGame = {}
trickerTotalGame = {}
winRateMap = {}
studentWinMap = {}
trickerWinMap = {}
studentWinRateMap = {}
trickerWinRateMap = {}

winGamesMap1 = {}
totalGamesMap1 = {}
studentTotalGame1 = {}
trickerTotalGame1 = {}
winRateMap1 = {}
studentWinMap1 = {}
trickerWinMap1 = {}
studentWinRateMap1 = {}
trickerWinRateMap1 = {}

studentWinNum = 0
trickerWinNum = 0
totalGameNum = 0

studentWinNum1 = 0
trickerWinNum1 = 0
totalGameNum1 = 0

teamIDtoName = {
"951c89eb-aa9c-45d4-af10-ad9785a047d6": "无名万物之始", "2e504ec6-50b1-4985-b2fd-995927ea9717": "LQ说什么都队", "fb472ad6-65e0-494b-a7be-f6f25ecda018": "是啊我诶",
"3376909b-5ddb-41ab-994c-3c5b5ba60158": "叛逃者联盟",
"9b0f2257-734c-42e2-8061-c267e3d0d231": "ChatGPA",
"2de908c9-1b99-4811-ae00-68140d1c4527": "昊天上帝和他的三个神父",
"94866510-af51-439c-a41a-b4cb42596e25": "少年毕不了业",
"4613ef48-4988-4508-a258-66857a3250b8": "PKT48TeamTS",
"8e14b2a3-fc37-4fb6-b8ac-a722a10707d7": "京ICP备2022019669号-1 2022 EESAST",
"e8db213c-a636-483f-a6ed-84310b3093a4": "拉拉队",
"04abd472-ed7a-4840-8680-87d20db52721": "努力少女戏尔危",
"2bc1b761-ace3-4403-af83-e46ca328bcd0": "测试",
"4c1d6333-e25c-4b0f-bc06-9851db446bd7": "摆烂吧,少女",
"7f819704-99c0-41d8-bc61-26eec0bd73bb": "一会吃萤火虫",
"de915bbf-0751-4a9d-ab30-a470807406b2": "小小做题家",
"28baa2bf-5130-4a1e-ab9c-36e8faf87f84": "数析少女队",
"5c868c42-3b07-4280-a825-a6f80e0d5a2c": "沙壁北京",
"ed03d1ac-810a-4547-b54a-d56cd5213364": "我会出手队",
"5d59e45f-cb0e-4294-90f4-282adb1d476d": "闪电骑士团",
"637e20c1-a904-4f6f-b706-2cea7c4daf5e": "Mukava Poikaa",
"7185eb49-0cb0-43c0-a469-a39e636d66d4": "劝退吧,少女",
"d2a7ba71-4a86-4278-a362-8a8953a368f8": "纵火犯在何方",
"4e266301-7749-4699-b2de-511d458cf537": "土木清华没有水",
"454f37bd-2f54-4463-94d9-2df9aedb4f21": "电电做不队",
"a2573713-28e4-4af7-8c53-ab21f385f789": "王牌飞行队",
"194c3ddf-6846-47ec-a262-ca24f5639531": "快乐Debug",
"97cf5969-e8ff-410e-b85c-0c8359923289": "卷动量守恒",
"4646739a-9ff5-4854-a3b2-27d5b85ea504": "龙井队",
"c431d105-a2b3-4659-b713-6bc97132ec7f": "疯狂Thurs队",
"9ee48de1-a76a-40eb-b267-59c985bbe6cb": "蒸馍",
"5ef8ffbb-0776-4a74-a84f-3d80d5b4c2ae": "你说什么都队",
"65f94306-69c7-42a2-8c68-44cb45749aae": "closeAI",
"ab0406ae-6a0e-4c1e-9d36-eb14115de076": "N/A",
"82cbff06-9ed1-429b-afc3-7e050318bf93": "代码一行都不队",
"6b52346c-4528-424b-ac75-22fa573ebaad": "pqfobj",
"93e7f3f1-d47f-4588-b433-72877089f0bd": "你说得队",
"2f6f9ce3-f2d3-4799-b291-38dc04d048a0": "少女终末旅行",
"07c0ad6c-f612-4375-9b79-52bb89c79d76": "大括号换行委员会",
"bdf5b1c5-4dbc-4589-a6bc-8c5932c04de7": "孤客若风",
"f0d75eee-34a6-4484-8e23-720d26db747d": "/",
"acea715f-d5b0-4113-b6c3-9f6d7822f2e9": "难崩"
}

dirs = os.listdir(".")
for dir in dirs:
if dir.startswith("Team"):
dirdir = dir.replace("Team_", "")
dirdir = dirdir.split("--vs--")
try:
with open(f"{dir}/result.json", 'r') as f:
result = json.load(f)
for i in (0, 1):
if dirdir[i] not in winGamesMap:
winGamesMap[dirdir[i]] = 0
if dirdir[i] not in totalGamesMap:
totalGamesMap[dirdir[i]] = 0
if dirdir[i] not in studentWinMap:
studentWinMap[dirdir[i]] = 0
if dirdir[i] not in trickerWinMap:
trickerWinMap[dirdir[i]] = 0
if dirdir[i] not in studentTotalGame:
studentTotalGame[dirdir[i]] = 0
if dirdir[i] not in trickerTotalGame:
trickerTotalGame[dirdir[i]] = 0
if dirdir[i] not in winGamesMap1:
winGamesMap1[dirdir[i]] = 0
if dirdir[i] not in totalGamesMap1:
totalGamesMap1[dirdir[i]] = 0
if dirdir[i] not in studentWinMap1:
studentWinMap1[dirdir[i]] = 0
if dirdir[i] not in trickerWinMap1:
trickerWinMap1[dirdir[i]] = 0
if dirdir[i] not in studentTotalGame1:
studentTotalGame1[dirdir[i]] = 0
if dirdir[i] not in trickerTotalGame1:
trickerTotalGame1[dirdir[i]] = 0

totalGamesMap[dirdir[0]] += 1
totalGamesMap[dirdir[1]] += 1
studentTotalGame[dirdir[0]] += 1
trickerTotalGame[dirdir[1]] += 1

totalGameNum += 1

if result["Student"] < result["Tricker"]:
winGamesMap[dirdir[1]] += 1
trickerWinMap[dirdir[1]] += 1
trickerWinNum += 1

elif result["Student"] > result["Tricker"]:
winGamesMap[dirdir[0]] += 1
studentWinMap[dirdir[0]] += 1
studentWinNum += 1

else:
winGamesMap[dirdir[0]] += 0.5
winGamesMap[dirdir[1]] += 0.5

if result["Student"] != 0 and result["Tricker"] != 0:

totalGameNum1 += 1
totalGamesMap1[dirdir[0]] += 1
totalGamesMap1[dirdir[1]] += 1
studentTotalGame1[dirdir[0]] += 1
trickerTotalGame1[dirdir[1]] += 1

if result["Student"] < result["Tricker"]:
winGamesMap1[dirdir[1]] += 1
trickerWinMap1[dirdir[1]] += 1
trickerWinNum1 += 1

elif result["Student"] > result["Tricker"]:
winGamesMap1[dirdir[0]] += 1
studentWinMap1[dirdir[0]] += 1
studentWinNum1 += 1

else:
winGamesMap1[dirdir[0]] += 0.5
winGamesMap1[dirdir[1]] += 0.5

except:
pass

for i in totalGamesMap:
winRateMap[i] = winGamesMap[i] / totalGamesMap[i]
if studentTotalGame[i] == 0:
studentWinRateMap[i] = 0
else:
studentWinRateMap[i] = studentWinMap[i] / studentTotalGame[i]
if trickerTotalGame[i] == 0:
trickerWinRateMap[i] = 0
else:
trickerWinRateMap[i] = trickerWinMap[i] / trickerTotalGame[i]


sortedMap = sorted(winRateMap.items(), key=lambda kv: (
kv[1], kv[0]), reverse=True)

print("************************ALL GAMES(with 0 player)************************")

for i in sortedMap:
width = 33 - len(re.findall('([\u4e00-\u9fa5])', teamIDtoName[i[0]]))
print(
f"Team {teamIDtoName[i[0]]:{width}}({i[0]}) wins {winGamesMap[i[0]]:4}/{totalGamesMap[i[0]]:<2} games({i[1]:.3f}), student wins {studentWinMap[i[0]]:2}/{studentTotalGame[i[0]]:<2}({studentWinRateMap[i[0]]:.3f}), tricker wins {trickerWinMap[i[0]]:2}/{trickerTotalGame[i[0]]:<2}({trickerWinRateMap[i[0]]:.3f})")

print(
f"Total games: {totalGameNum}, student wins {studentWinNum}, tricker wins {trickerWinNum}")

for i in totalGamesMap1:
if totalGamesMap1[i] == 0:
winRateMap1[i] = 0
else:
winRateMap1[i] = winGamesMap1[i] / totalGamesMap1[i]
if studentTotalGame1[i] == 0:
studentWinRateMap1[i] = 0
else:
studentWinRateMap1[i] = studentWinMap1[i] / studentTotalGame1[i]
if trickerTotalGame1[i] == 0:
trickerWinRateMap1[i] = 0
else:
trickerWinRateMap1[i] = trickerWinMap1[i] / trickerTotalGame1[i]


sortedMap1 = sorted(winRateMap1.items(), key=lambda kv: (
kv[1], kv[0]), reverse=True)

print("************************NON-0 GAMES(no 0 player)************************")

for i in sortedMap1:
width = 33 - len(re.findall('([\u4e00-\u9fa5])', teamIDtoName[i[0]]))
print(f"Team {teamIDtoName[i[0]]:{width}}({i[0]}) wins {winGamesMap1[i[0]]:4}/{totalGamesMap1[i[0]]:<2} games({i[1]:.3f}), student wins {studentWinMap1[i[0]]:2}/{studentTotalGame1[i[0]]:<2}({studentWinRateMap1[i[0]]:.3f}), tricker wins {trickerWinMap1[i[0]]:2}/{trickerTotalGame1[i[0]]:<2}({trickerWinRateMap1[i[0]]:.3f})")

print(
f"Total games: {totalGameNum1}, student wins {studentWinNum1}, tricker wins {trickerWinNum1}")

+ 11
- 2
dependency/shell/compile.sh View File

@@ -13,8 +13,17 @@ do
flag=0 flag=0
fi fi
mv ./compile_log$i.txt $bind/compile_log$i.txt mv ./compile_log$i.txt $bind/compile_log$i.txt
elif [ ! -f "${bind}/player${i}.py" ]; then
flag=0
elif [ -f "${bind}/player${i}.py" ]; then
pushd ../python
cp -f $bind/player$i.py ./PyAPI/AI.py
python3 -m compileall ./PyAPI >compile_log$i.txt 2>&1
if [ ! -f ./PyAPI/__pycache__/AI.cpython-39.pyc ]; then
flag=0
else
rm -rf ./PyAPI/__pycache__/AI.cpython-39.pyc
fi
mv ./compile_log$i.txt $bind/compile_log$i.txt
popd
fi fi
let "i++" let "i++"
done done


+ 5
- 6
dependency/shell/run.sh View File

@@ -51,8 +51,12 @@ if [ -f $playback_dir/start.lock ]; then
while [ $? -eq 0 ] while [ $? -eq 0 ]
do do
sleep 1 sleep 1
ps -p $server_pid
ps -p $server_pid > /dev/null 2>&1
done done
# result=$(cat /usr/local/playback/result.json)
# score0=$(echo "$result" | grep -oP '(?<="Student":)\d+')
# score1=$(echo "$result" | grep -oP '(?<="Tricker":)\d+')
# curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"result":[{"team_id":0, "score":'${score0}'}, {"team_id":1, "score":'${score1}'}], "mode":'${MODE}'}'> $playback_dir/send.log 2>&1
touch $playback_dir/finish.lock touch $playback_dir/finish.lock
echo "Finish" echo "Finish"
else else
@@ -62,8 +66,3 @@ else
mv -f temp.lock $playback_dir/video.thuaipb mv -f temp.lock $playback_dir/video.thuaipb
kill -9 $server_pid kill -9 $server_pid
fi fi

result=$(cat /usr/local/playback/result.json)
score0=$(echo "$result" | grep -oP '(?<="Student":)\d+')
score1=$(echo "$result" | grep -oP '(?<="Tricker":)\d+')
curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"result":[{"team_id":0, "score":'${score0}'}, {"team_id":1, "score":'${score1}'}], "mode":'${MODE}'}'

+ 48
- 43
docs/CAPI接口(cpp).md View File

@@ -4,82 +4,86 @@
## 接口解释 ## 接口解释


### 主动指令 ### 主动指令
- 每帧最多发送50条主动指令
- EndAllAction 及 Move 指令调用数总和一帧内不超过 10 次

#### 移动 #### 移动
- `std::future<bool> Move(int64_t timeInMilliseconds, double angleInRadian)`:移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为x轴,水平向右方向为y轴**。因为移动过程中你会受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。
- `std::future<bool> MoveRight(uint32_t timeInMilliseconds)`即向右移动,`MoveLeft`、`MoveDown`、`MoveUp`同理
- `std::future<bool> Move(int64_t timeInMilliseconds, double angleInRadian)`移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为 x 轴,水平向右方向为 y 轴**。因为移动过程中你会受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。
- `std::future<bool> MoveRight(uint32_t timeInMilliseconds)`即向右移动,`MoveLeft`、`MoveDown`、`MoveUp` 同理


#### 使用技能 #### 使用技能
- `std::future<bool> UseSkill(int32_t skillID)`:使用对应序号的主动技能
- `std::future<bool> UseSkill(int32_t skillID)`使用对应序号的主动技能


#### 人物 #### 人物
- `std::future<bool> EndAllAction()`:可以使不处在不可行动状态中的玩家终止当前行动
- 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令
- `std::future<bool> EndAllAction()`可以使不处在不可行动状态中的玩家终止当前行动
- 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 EndAllAction 指令终止进行的指令
- 实际上唤醒或勉励不同的人是有效的 - 实际上唤醒或勉励不同的人是有效的
- EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次


#### 攻击 #### 攻击
- `std::future<bool> Attack(double angleInRadian)`:`angleInRadian`为攻击方向
- `std::future<bool> Attack(double angleInRadian)`:`angleInRadian` 为攻击方向


#### 学习与毕业 #### 学习与毕业
- `std::future<bool> StartLearning()`:在教室里开始做作业
- `std::future<bool> StartOpenGate()`:开始开启校门
- `std::future<bool> Graduate()`:从开启的校门或隐藏校门毕业。
- `std::future<bool> StartLearning()`在教室里开始做作业
- `std::future<bool> StartOpenGate()`开始开启校门
- `std::future<bool> Graduate()`从开启的校门或隐藏校门毕业。


#### 勉励与唤醒 #### 勉励与唤醒
- `std::future<bool> StartEncourageMate(int64_t mateID)`:勉励对应玩家ID的学生。
- `std::future<bool> StartRouseMate(int64_t mateID)`:唤醒对应玩家ID的沉迷的学生。
- `std::future<bool> StartEncourageMate(int64_t mateID)`:勉励对应玩家 ID 的学生。
- `std::future<bool> StartRouseMate(int64_t mateID)`:唤醒对应玩家 ID 的沉迷的学生。


#### 地图互动 #### 地图互动
- `std::future<bool> OpenDoor()`:开门
- `std::future<bool> CloseDoor()`:关门
- `std::future<bool> SkipWindow()`:翻窗
- `std::future<bool> StartOpenChest()`:开箱
- `std::future<bool> OpenDoor()`开门
- `std::future<bool> CloseDoor()`关门
- `std::future<bool> SkipWindow()`翻窗
- `std::future<bool> StartOpenChest()`开箱


#### 道具 #### 道具
- `bool PickProp(THUAI6::PropType prop)`捡起与自己处于同一个格子(cell)的道具。
- `bool UseProp(THUAI6::PropType prop)`使用对应类型的道具
- `bool ThrowProp(THUAI6::PropType prop)`将对应类型的道具扔在原地
- `bool PickProp(THUAI6::PropType prop)`捡起与自己处于同一个格子(cell)的道具。
- `bool UseProp(THUAI6::PropType prop)`使用对应类型的道具
- `bool ThrowProp(THUAI6::PropType prop)`将对应类型的道具扔在原地


### 信息获取 ### 信息获取


#### 队内信息 #### 队内信息
- `std::future<bool> SendMessage(int64_t, std::string)`:给同队的队友发送消息,队友在下一帧收到。第一个参数指定发送的对象,第二个参数指定发送的内容,不得超过256字节。
- `bool HaveMessage()`:是否有队友发来的尚未接收的信息。
- `std::pair<int64_t, std::string> GetMessage()`:按照消息发送顺序获取来自队友的信息,第一个参数为发送该消息的PlayerID。
- `std::future<bool> SendTextMessage(int64_t, std::string)`:给同队的队友发送文本消息,队友在下一帧收到。第一个参数指定发送的对象,第二个参数指定发送的内容,形式为 Unicode 字符串,不得超过 256 字节。
- `std::future<bool> SendBinaryMessage(int64_t, std::string)`:给同队的队友发送二进制消息,队友在下一帧收到。第一个参数指定发送的对象,第二个参数指定发送的内容,形式为二进制字符串,不得超过 256 字节。
- `bool HaveMessage()`:是否有队友发来的尚未接收的信息。
- `std::pair<int64_t, std::string> GetMessage()`:按照消息发送顺序获取来自队友的信息,第一个参数为发送该消息的 PlayerID。
> 需要注意,收到的二进制消息和文本消息都会被这个函数一并返回,选手需要自行约定消息类型。


#### 查询可视范围内的信息 #### 查询可视范围内的信息
- `std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const` :对于学生,返回所有学生的信息;对于捣蛋鬼,返回可视学生的信息。
- `std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const` :返回所有可视捣蛋鬼的信息。
- `std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const` :返回所有可视道具的信息。
- `std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const` :返回所有可视子弹(攻击)的信息。
- `std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const`:对于学生,返回所有学生的信息;对于捣蛋鬼,返回可视学生的信息。
- `std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const`:返回所有可视捣蛋鬼的信息。
- `std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const`:返回所有可视道具的信息。
- `std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const`:返回所有可视子弹(攻击)的信息。
- `bool HaveView(int gridX, int gridY) const`:判断坐标是否可见 - `bool HaveView(int gridX, int gridY) const`:判断坐标是否可见


#### 查询特定位置物体的信息 #### 查询特定位置物体的信息


下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。 下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。


- `THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY)` :返回某一位置场地种类信息。场地种类详见 structure.h
- `THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY)`:返回某一位置场地种类信息。场地种类详见 structure.h。
- 以下指令,若查询物品当前在视野内,则返回最新进度/状态;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度/状态;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。 - 以下指令,若查询物品当前在视野内,则返回最新进度/状态;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度/状态;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。
- `int32_t GetChestProgress(int32_t cellX, int32_t cellY) const`:查询特定位置箱子开启进度
- `int32_t GetGateProgress(int32_t cellX, int32_t cellY) const`:查询特定位置校门开启进度
- `int32_t GetClassroomProgress(int32_t cellX, int32_t cellY) const`:查询特定位置教室作业完成进度
- `int32_t GetDoorProgress(int32_t cellX, int32_t cellY) const`:查询特定位置门开启状态
- `bool IsDoorOpen(int32_t cellX, int32_t cellY) const`:查询特定位置门是否开启,没有门/不在视野内也返回false
- `THUAI6::HiddenGateState GetHiddenGateState(int32_t cellX, int32_t cellY) const`::查询特定位置隐藏校门状态,没有隐藏校门/不在视野内返回THUAI6::HiddenGateState::Null
- `int32_t GetChestProgress(int32_t cellX, int32_t cellY) const`查询特定位置箱子开启进度
- `int32_t GetGateProgress(int32_t cellX, int32_t cellY) const`查询特定位置校门开启进度
- `int32_t GetClassroomProgress(int32_t cellX, int32_t cellY) const`查询特定位置教室作业完成进度
- `int32_t GetDoorProgress(int32_t cellX, int32_t cellY) const`查询特定位置门开启状态
- `bool IsDoorOpen(int32_t cellX, int32_t cellY) const`查询特定位置门是否开启,没有门/不在视野内也返回 false
- `THUAI6::HiddenGateState GetHiddenGateState(int32_t cellX, int32_t cellY) const`:查询特定位置隐藏校门状态,没有隐藏校门/不在视野内返回 THUAI6::HiddenGateState::Null


#### 其他 #### 其他
- `std::shared_ptr<const THUAI6::GameInfo> GetGameInfo() const`:查询当前游戏状态
- `std::vector<int64_t> GetPlayerGUIDs() const`:获取所有玩家的GUID
- `int GetFrameCount() const`:获取目前所进行的帧数
- `std::shared_ptr<const THUAI6::Tricker> GetSelfInfo() const`或`std::shared_ptr<const THUAI6::Student> GetSelfInfo() const`:获取自己的信息
- `std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const`:返回整张地图的地形信息。可以写成类似`api.GetFullMap()[x][y]`,其中x为地图自上到下第几行,y为自左向右第几列,注意从0开始
- `std::shared_ptr<const THUAI6::GameInfo> GetGameInfo() const`查询当前游戏状态
- `std::vector<int64_t> GetPlayerGUIDs() const`:获取所有玩家的 GUID
- `int GetFrameCount() const`获取目前所进行的帧数
- `std::shared_ptr<const THUAI6::Tricker> GetSelfInfo() const` `std::shared_ptr<const THUAI6::Student> GetSelfInfo() const`:获取自己的信息
- `std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const`:返回整张地图的地形信息。可以写成类似 `api.GetFullMap()[x][y]`,其中 x 为地图自上到下第几行,y 为自左向右第几列,注意从 0 开始


### 辅助函数 ### 辅助函数
`static inline int CellToGrid(int cell) noexcept`:将地图格数 cell 转换为绝对坐标 grid。
`static inline int CellToGrid(int cell) noexcept`将地图格数 cell 转换为绝对坐标 grid。


`static inline int GridToCell(int grid) noexcept`:将绝对坐标 grid 转换为地图格数 cell。
`static inline int GridToCell(int grid) noexcept`将绝对坐标 grid 转换为地图格数 cell。


下面为用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用
下面为用于 DEBUG 的输出函数,选手仅在开启 Debug 模式的情况下可以使用
~~~c++ ~~~c++
void Print(std::string str) const; void Print(std::string str) const;
void PrintStudent() const; void PrintStudent() const;
@@ -93,7 +97,7 @@
struct Player struct Player
{ {
std::vector<PropType> props;//大小固定为3,空的位置为NullPropType std::vector<PropType> props;//大小固定为3,空的位置为NullPropType
}
};
~~~ ~~~


## 接口一览 ## 接口一览
@@ -121,7 +125,8 @@
virtual std::future<bool> EndAllAction() = 0; virtual std::future<bool> EndAllAction() = 0;


// 发送信息、接受信息,注意收消息时无消息则返回nullopt // 发送信息、接受信息,注意收消息时无消息则返回nullopt
virtual std::future<bool> SendMessage(int64_t, std::string) = 0;
virtual std::future<bool> SendTextMessage(int64_t, std::string) = 0;
virtual std::future<bool> SendBinaryMessage(int64_t, std::string) = 0;
[[nodiscard]] virtual bool HaveMessage() = 0; [[nodiscard]] virtual bool HaveMessage() = 0;
[[nodiscard]] virtual std::pair<int64_t, std::string> GetMessage() = 0; [[nodiscard]] virtual std::pair<int64_t, std::string> GetMessage() = 0;




+ 45
- 42
docs/CAPI接口(python).md View File

@@ -5,96 +5,99 @@
## 接口解释 ## 接口解释


### 主动指令 ### 主动指令
- 每帧最多发送 50 条主动指令
- EndAllAction 及 Move 指令调用数总和一帧内不超过 10 次


#### 移动 #### 移动


- `def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]`:移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为 x 轴,水平向右方向为 y 轴**因为移动过程中你会受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。
- 5ms以内的移动指令会被禁止,你不应当使用过小的移动指令
- `def MoveRight(self, timeInMilliseconds: int) -> Future[bool]`即向右移动,`MoveLeft`、`MoveDown`、`MoveUp`同理
- `def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]`移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为 x 轴,水平向右方向为 y 轴**因为移动过程中你会受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。
- 5ms 以内的移动指令会被禁止,你不应当使用过小的移动指令
- `def MoveRight(self, timeInMilliseconds: int) -> Future[bool]` 即向右移动, `MoveLeft`、`MoveDown`、`MoveUp` 同理


#### 使用技能 #### 使用技能


- `def UseSkill(self, skillID: int) -> Future[bool]`:使用对应序号的主动技能
- `def UseSkill(self, skillID: int) -> Future[bool]`使用对应序号的主动技能


#### 人物 #### 人物


- `def EndAllAction(self) -> Future[bool]`:可以使不处在不可行动状态中的玩家终止当前行动
- 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令
- `def EndAllAction(self) -> Future[bool]`可以使不处在不可行动状态中的玩家终止当前行动
- 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 EndAllAction 指令终止进行的指令
- 实际上唤醒或勉励不同的人是有效的 - 实际上唤醒或勉励不同的人是有效的
- EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次
- EndAllAction 及 Move 指令调用数总和一帧内不超过 10 次


#### 攻击 #### 攻击


- `def Attack(self, angle: float) -> Future[bool]`:`angleInRadian`为攻击方向
- `def Attack(self, angle: float) -> Future[bool]`:`angleInRadian` 为攻击方向


#### 学习与毕业 #### 学习与毕业


- `def StartLearning(self) -> Future[bool]`:在教室里开始做作业
- `def StartOpenGate(self) -> Future[bool]`:开始开启校门
- `def Graduate(self) -> Future[bool]`:从开启的校门或隐藏校门毕业。
- `def StartLearning(self) -> Future[bool]`在教室里开始做作业
- `def StartOpenGate(self) -> Future[bool]`开始开启校门
- `def Graduate(self) -> Future[bool]`从开启的校门或隐藏校门毕业。


#### 勉励与唤醒 #### 勉励与唤醒


- `def StartEncourageMate(self, mateID: int) -> Future[bool]`:勉励对应玩家 ID 的学生。
- `def StartEncourageMate(self, mateID: int) -> Future[bool]`勉励对应玩家 ID 的学生。
- `def StartRouseMate(self, mateID: int) -> Future[bool]`:唤醒对应玩家 ID 的沉迷的学生。 - `def StartRouseMate(self, mateID: int) -> Future[bool]`:唤醒对应玩家 ID 的沉迷的学生。


#### 地图互动 #### 地图互动


- `def OpenDoor(self) -> Future[bool]`:开门
- `def CloseDoor(self) -> Future[bool]`:关门
- `def SkipWindow(self) -> Future[bool]`:翻窗
- `def StartOpenChest(self) -> Future[bool]`:开箱
- `def OpenDoor(self) -> Future[bool]`开门
- `def CloseDoor(self) -> Future[bool]`关门
- `def SkipWindow(self) -> Future[bool]`翻窗
- `def StartOpenChest(self) -> Future[bool]`开箱


#### 道具 #### 道具


- `def PickProp(self, propType: THUAI6.PropType) -> Future[bool]`捡起与自己处于同一个格子(cell)的道具。
- `def UseProp(self, propType: THUAI6.PropType) -> Future[bool]`使用对应类型的道具
- `def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]`将对应类型的道具扔在原地
- `def PickProp(self, propType: THUAI6.PropType) -> Future[bool]`捡起与自己处于同一个格子(cell)的道具。
- `def UseProp(self, propType: THUAI6.PropType) -> Future[bool]`使用对应类型的道具
- `def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]`将对应类型的道具扔在原地


### 信息获取 ### 信息获取


#### 队内信息 #### 队内信息


- `def GetMessage(self) -> Tuple[int, str]`:给同队的队友发送消息,队友在下一帧收到。第一个参数指定发送的对象,第二个参数指定发送的内容,不得超过256字节。
- `def HaveMessage(self) -> bool`:是否有队友发来的尚未接收的信息。
- `def GetMessage(self) -> Tuple[int, str]`:按照消息发送顺序获取来自队友的信息,第一个参数为发送该消息的PlayerID。
- `def SendMessage(self, toID: int, message: Union[str, bytes]) -> Future[bool]`:给同队的队友发送消息,队友在下一帧收到。第一个参数指定发送的对象,第二个参数指定发送的内容,可以是Unicode字符串,也可以是二进制字符串;不得超过 256 字节。
- `def HaveMessage(self) -> bool`:是否有队友发来的尚未接收的信息。
- `def GetMessage(self) -> Tuple[int, Union[str, bytes]]`:按照消息发送顺序获取来自队友的信息,第一个参数为发送该消息的 PlayerID。
> 需要注意,文本消息和二进制消息都会在这里接受,选手可以简单地使用 `isinstance()` 来判断消息类型。


#### 查询可视范围内的信息 #### 查询可视范围内的信息


- `def GetStudents(self) -> List[THUAI6.Student]` :对于学生,返回所有学生的信息;对于捣蛋鬼,返回可视学生的信息。
- `def GetTrickers(self) -> List[THUAI6.Tricker]` :返回所有可视捣蛋鬼的信息。
- `def GetProps(self) -> List[THUAI6.Prop]` :返回所有可视道具的信息。
- `def GetBullets(self) -> List[THUAI6.Bullet]` :返回所有可视子弹(攻击)的信息。
- `def GetStudents(self) -> List[THUAI6.Student]`:对于学生,返回所有学生的信息;对于捣蛋鬼,返回可视学生的信息。
- `def GetTrickers(self) -> List[THUAI6.Tricker]`:返回所有可视捣蛋鬼的信息。
- `def GetProps(self) -> List[THUAI6.Prop]`:返回所有可视道具的信息。
- `def GetBullets(self) -> List[THUAI6.Bullet]`:返回所有可视子弹(攻击)的信息。
- `def HaveView(self, gridX: int, gridY: int) -> bool`:判断坐标是否可见 - `def HaveView(self, gridX: int, gridY: int) -> bool`:判断坐标是否可见


#### 查询特定位置物体的信息 #### 查询特定位置物体的信息


下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。 下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。


- `def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType` :返回某一位置场地种类信息。场地种类详见 structure.h
- `def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType`:返回某一位置场地种类信息。场地种类详见 structure.h。
- 以下指令,若查询物品当前在视野内,则返回最新进度/状态;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度/状态;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。 - 以下指令,若查询物品当前在视野内,则返回最新进度/状态;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度/状态;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。
- `def GetChestProgress(self, cellX: int, cellY: int) -> int`:查询特定位置箱子开启进度
- `def GetGateProgress(self, cellX: int, cellY: int) -> int`:查询特定位置校门开启进度
- `def GetClassroomProgress(self, cellX: int, cellY: int) -> int`:查询特定位置教室作业完成进度
- `def GetDoorProgress(self, cellX: int, cellY: int) -> int`:查询特定位置门开启状态
- `def IsDoorOpen(self, cellX: int, cellY: int) -> bool`:查询特定位置门是否开启,没有门/不在视野内也返回false
- `def GetHiddenGateState(self, cellX: int, cellY: int) -> THUAI6.HiddenGateState`::查询特定位置隐藏校门状态,没有隐藏校门/不在视野内返回THUAI6::HiddenGateState::Null
- `def GetChestProgress(self, cellX: int, cellY: int) -> int`查询特定位置箱子开启进度
- `def GetGateProgress(self, cellX: int, cellY: int) -> int`查询特定位置校门开启进度
- `def GetClassroomProgress(self, cellX: int, cellY: int) -> int`查询特定位置教室作业完成进度
- `def GetDoorProgress(self, cellX: int, cellY: int) -> int`查询特定位置门开启状态
- `def IsDoorOpen(self, cellX: int, cellY: int) -> bool`查询特定位置门是否开启,没有门/不在视野内也返回 false
- `def GetHiddenGateState(self, cellX: int, cellY: int) -> THUAI6.HiddenGateState`:查询特定位置隐藏校门状态,没有隐藏校门/不在视野内返回 THUAI6::HiddenGateState::Null


#### 其他 #### 其他


- `def GetGameInfo(self) -> THUAI6.GameInfo`:查询当前游戏状态
- `def GetPlayerGUIDs(self) -> List[int]`:获取所有玩家的GUID
- `def GetFrameCount(self) -> int`:获取目前所进行的帧数
- `def GetGameInfo(self) -> THUAI6.GameInfo`查询当前游戏状态
- `def GetPlayerGUIDs(self) -> List[int]`:获取所有玩家的 GUID
- `def GetFrameCount(self) -> int`获取目前所进行的帧数
- `def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]`:获取自己的信息 - `def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]`:获取自己的信息
- `def GetFullMap(self) -> List[List[THUAI6.PlaceType]]`:返回整张地图的地形信息。可以写成类似`self.GetFullMap()[x][y]`,其中x为地图自上到下第几行,y为自左向右第几列,注意从0开始
- `def GetFullMap(self) -> List[List[THUAI6.PlaceType]]`:返回整张地图的地形信息。可以写成类似 `self.GetFullMap()[x][y]`,其中 x 为地图自上到下第几行,y 为自左向右第几列,注意从 0 开始


### 辅助函数 ### 辅助函数


`def CellToGrid(cell: int) -> int`:将地图格数 cell 转换为绝对坐标 grid。
`def CellToGrid(cell: int) -> int`将地图格数 cell 转换为绝对坐标 grid。


`def GridToCell(grid: int) -> int`:将绝对坐标 grid 转换为地图格数 cell。
`def GridToCell(grid: int) -> int`将绝对坐标 grid 转换为地图格数 cell。


下面为用于DEBUG的输出函数,选手仅在开启 Debug 模式的情况下可以使用
下面为用于 DEBUG 的输出函数,选手仅在开启 Debug 模式的情况下可以使用


~~~python ~~~python
def Print(self, cont: str) -> None: def Print(self, cont: str) -> None:
@@ -190,7 +193,7 @@ class IAPI(metaclass=ABCMeta):
# 消息相关,接收消息时无消息则返回(-1, '') # 消息相关,接收消息时无消息则返回(-1, '')


@abstractmethod @abstractmethod
def SendMessage(self, toID: int, message: str) -> Future[bool]:
def SendMessage(self, toID: int, message: Union[str, bytes]) -> Future[bool]:
pass pass


@abstractmethod @abstractmethod
@@ -198,7 +201,7 @@ class IAPI(metaclass=ABCMeta):
pass pass


@abstractmethod @abstractmethod
def GetMessage(self) -> Tuple[int, str]:
def GetMessage(self) -> Tuple[int, Union[str, bytes]]:
pass pass


# 等待下一帧 # 等待下一帧


+ 14
- 11
docs/GameRules.md View File

@@ -1,5 +1,5 @@
# 规则 # 规则
V5.5
V5.6.2
- [规则](#规则) - [规则](#规则)
- [简则](#简则) - [简则](#简则)
- [地图](#地图) - [地图](#地图)
@@ -89,13 +89,14 @@ $$
8. 翻窗 Climbing 8. 翻窗 Climbing


### 攻击 ### 攻击
- 攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)*2
- 攻击类型CommonAttackOfTricker攻击未写完的作业,会造成对应攻击力的损坏 - 攻击类型CommonAttackOfTricker攻击未写完的作业,会造成对应攻击力的损坏
- 捣蛋鬼攻击交互状态或前后摇的学生,将使学生眩晕4.3s - 捣蛋鬼攻击交互状态或前后摇的学生,将使学生眩晕4.3s


| 攻击(子弹)类型 |搞蛋鬼的一般攻击CommonAttackOfTricker| 飞刀FlyingKnife | 蹦蹦炸弹BombBomb | 小炸弹JumpyDumpty | | 攻击(子弹)类型 |搞蛋鬼的一般攻击CommonAttackOfTricker| 飞刀FlyingKnife | 蹦蹦炸弹BombBomb | 小炸弹JumpyDumpty |
| :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- |
| 子弹爆炸范围 | 0 | 0 | 2000 | 1000 |
| 子弹攻击距离 | 2200 | 78000 | 2200 | 4400 |
| 爆炸范围 | 0 | 0 | 2000 | 1000 |
| 攻击距离 | 2200 | 78000 | 2200 | 4400 |
| 攻击力 | 1500000 | 1200000 | 1800000 | 900000 | | 攻击力 | 1500000 | 1200000 | 1800000 | 900000 |
| 移动速度/s | 7400 | 18500 | 6000 | 8600 | | 移动速度/s | 7400 | 18500 | 6000 | 8600 |
| 前摇(ms) | 297 | 400 | 366 | - | | 前摇(ms) | 297 | 400 | 366 | - |
@@ -192,7 +193,7 @@ $$
| :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- |
| 移动速度/s | 3960 | 3600 | 3852 | 3600 | | 移动速度/s | 3960 | 3600 | 3852 | 3600 |
| 隐蔽度 | 1.5 | 1 | 0.8 | 0.75| | 隐蔽度 | 1.5 | 1 | 0.8 | 0.75|
| 警戒范围 | 22,100 | 17000 | 15300 | 17000
| 警戒范围 | 22100 | 17000 | 15300 | 17000|
| 视野范围 | 15600 | 13000 | 13000 | 14300| | 视野范围 | 15600 | 13000 | 13000 | 14300|
| 开锁门速度/ms | 4000 | 4000 | 4000 |4000 | | 开锁门速度/ms | 4000 | 4000 | 4000 |4000 |
| 翻窗速度/s | 2540 | 2540 | 2794 | 2540| | 翻窗速度/s | 2540 | 2540 | 2794 | 2540|
@@ -217,7 +218,7 @@ $$
- CD:15s 持续时间:3s - CD:15s 持续时间:3s
- 在持续时间内,攻击类型变为蹦蹦炸弹 - 在持续时间内,攻击类型变为蹦蹦炸弹
- 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上90°,270° 发出2个小炸弹 - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上90°,270° 发出2个小炸弹
- 2个小炸弹运动停止前会因为碰撞爆炸,停止运动后学生碰撞会造成眩晕3.07s
- 小炸弹运动停止前会因为碰撞爆炸,停止运动后学生碰撞会造成眩晕3.07s
- 不直接得分,通过眩晕等获得对应得分 - 不直接得分,通过眩晕等获得对应得分


#### 喧哗者ANoisyPerson #### 喧哗者ANoisyPerson
@@ -246,14 +247,14 @@ $$
| :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- |
| 移动速度/s | 2700 | 3150 | 2880 | 3000 | | 移动速度/s | 2700 | 3150 | 2880 | 3000 |
| 最大毅力值 | 30000000 | 3000000 | 3300000 | 3200000 | | 最大毅力值 | 30000000 | 3000000 | 3300000 | 3200000 |
| 最大沉迷度 | 600000 | 54,000 | 78,000 | 66,000 |
| 学习一科速度 | 0 | 73 | 135 | 123 |
| 最大沉迷度 | 600000 | 54000 | 78000 | 66000 |
| 学习速度/ms | 0 | 73 | 135 | 123 |
| 勉励速度/ms | 80 | 90 | 100 | 120 | | 勉励速度/ms | 80 | 90 | 100 | 120 |
| 隐蔽度 | 0.5 | 0.9 | 0.9 | 0.8 | | 隐蔽度 | 0.5 | 0.9 | 0.9 | 0.8 |
| 警戒范围 | 7500 | 15000 | 13,500 | 15000 |
| 视野范围 | 9,000 | 11000 | 9,000 | 10000 |
| 警戒范围 | 7500 | 15000 | 13500 | 15000 |
| 视野范围 | 9000 | 11000 | 9000 | 10000 |
| 开锁门速度/ms | 4000 | 4000 | 4000 | 2800 | | 开锁门速度/ms | 4000 | 4000 | 4000 | 2800 |
| 翻窗速度/ms | 1270 | 3048 | 2116 | 2540 |
| 翻窗速度/ms | 611 | 1466 | 1018 | 1222 |
| 翻箱速度/ms | 1000 | 1000 | 1000 | 900 | | 翻箱速度/ms | 1000 | 1000 | 1000 | 900 |


#### 运动员 #### 运动员
@@ -308,13 +309,14 @@ $$
- 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。 - 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。


### 人物 ### 人物
- 眩晕状态中的玩家不能再次被眩晕
- 眩晕状态中的玩家不能再次被眩晕(除非是ShowTime)


### 初始状态 ### 初始状态
- 初赛玩家出生点固定且一定为空地 - 初赛玩家出生点固定且一定为空地


### 道具 ### 道具
- 使用钥匙相当于销毁 - 使用钥匙相当于销毁
- 可接受指令状态下能捡起或扔道具,在场上即可使用道具


### 交互 ### 交互
- 被唤醒或被勉励不属于交互状态,翻窗属于交互状态 - 被唤醒或被勉励不属于交互状态,翻窗属于交互状态
@@ -341,6 +343,7 @@ $$
- 开锁门进度中断后清空 - 开锁门进度中断后清空


### 窗 ### 窗
- 由于窗户被占用导致翻窗失败会使先前行动停止
- 翻越窗户是一种交互行为,翻窗一共有两个过程 - 翻越窗户是一种交互行为,翻窗一共有两个过程
- 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置
- 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心


+ 6
- 3
docs/使用文档.md View File

@@ -49,7 +49,7 @@
Server脚本中参数格式一般如下: Server脚本中参数格式一般如下:


```shell ```shell
--ip 0.0.0.0 --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video
--ip 0.0.0.0 --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video --mapResource ".\map\map1_final.txt"
``` ```


`--ip`是服务器ipv4地址 `--ip`是服务器ipv4地址
@@ -75,6 +75,9 @@ Server脚本中参数格式一般如下:


`--fileName`为回放文件的保存名称 `--fileName`为回放文件的保存名称


`--mapResource`为地图文件存放的相对(该脚本)路径
- 不写则为默认地图

### 启动Client(RunPython或RunCpp) ### 启动Client(RunPython或RunCpp)


`RunCpp.cmd`或`RunPython.cmd`的脚本参数格式如下: `RunCpp.cmd`或`RunPython.cmd`的脚本参数格式如下:
@@ -144,8 +147,8 @@ start python .\CAPI\python\PyAPI\main.py -I 127.0.0.1 -P 8888 -p 0 -d -o -w
| 1 | Athlete | | 1 | Athlete |
| 2 | Teacher | | 2 | Teacher |
| 3 | StraightAStudent | | 3 | StraightAStudent |
| 4 | Robot(目前未实现所有功能) |
| 5 | TechOtaku(目前未实现所有功能) |
| 4 | Robot |
| 5 | TechOtaku |
| 6 | Sunshine | | 6 | Sunshine |


* 捣蛋鬼 * 捣蛋鬼


+ 119
- 0
docs/游戏机制与平衡性调整更新草案.md View File

@@ -0,0 +1,119 @@
# 游戏机制与平衡性调整更新草案
v1.6

## 说明
- 该草案尚未完全确定,请大家不要过分依靠该文档进行修改自己的代码
- 有任何问题都可以在选手群中提出建议和讨论

## 游戏接口
- 删除structures.h中Player的属性place

## 游戏规则

## 游戏机制

## 攻击
- 飞刀FlyingKnife
- 前摇与CD变为600ms
- 搞蛋鬼的一般攻击CommonAttackOfTricker
- 改为“不能攻击未写完的作业”
- 蹦蹦炸弹BombBomb
- 未攻击至目标时的后摇改为1200ms
- 增强为“可以攻击未写完的作业”
- 增强为“可以攻击使门被打开(可以重新被锁上)”
- 小炸弹JumpyDumpty
- 攻击距离改为1600
- 小炸弹不受道具增益影响
- 修改为“小炸弹与自己无碰撞体积”
- strike(新增)
- 可以攻击未写完的作业

修改后:
| 攻击(子弹)类型 |搞蛋鬼的一般攻击CommonAttackOfTricker|飞刀FlyingKnife | 蹦蹦炸弹BombBomb | 小炸弹JumpyDumpty | strike |
| :--------------------- | :---------------------| :--------------------- | :--------------------- | :--------------------- | :--------------------- |
| 子弹爆炸范围 | 0 | 0 | 2000 | 1000 | 0 |
| 子弹攻击距离 | 2200 | 78000 | 2200 | 1600 | 2000 |
| 攻击力 | 1500000 | 1200000 | 1800000 | 900000 | 1600000 |
| 移动速度/s | 7400 | 18500 | 6000 | 8600 | 6250 |
| 前摇(ms) | 297 | 600 | 366 | - | 320 |
|未攻击至目标时的后摇(ms)| 800 | 0 | 1200 | - | 800 |
|攻击至目标时的后摇(ms) | 3700 | 0 | 3700 | - | 3700 |
| CD(ms) | 800 | 600 | 3000 | - | 800 |
| 最大子弹容量 | 1 | 1 | 1 | - | 1 |


## 职业
- 所有角色开锁门速度加速至1.25倍
- 所有角色翻箱速度加速至1.25倍

### 学生
- 先前学生翻窗数据有误

- Teacher
- 学习速度由0改为50
- 警戒范围由7500改为10000
- 翻窗速度改为1000
- 特质:
- 扣血则得分100×受到伤害/基本伤害(1500000)
- 技能惩罚(Punish)改为
- CD:45s
- “使用瞬间,在**视野距离/3范围内(不是可视范围)的**翻窗、开锁门、攻击前后摇、**使用技能期间**的捣蛋鬼会被眩晕(3070+**500***已受伤害/基本伤害(1500000))ms”
- 技能喝茶(HaveTea)(新增)
- CD:90s
- 使用瞬间,向额外参数/1000.0的角度瞬移3000(可以穿墙),如果会碰撞则失败。
- Robot(新增)
- 无技能
- 特性
- 不可被眩晕
- 不可毕业
- 不可救人
- 无牵制得分
- 不可使用道具(可以捡起和扔道具)
- TechOtaku(新增)
- 一名TechOtaku最多可以在场上同时最多拥有3个Robot,无法共享视野
1. SummonGolem
- CD:40s,持续时间:6s
- 在持续时间中,学生进入人物状态进入UsingSpecialSkill(不能移动),进入其他状态会导致制作机器人失败。
- 在持续时间中,学生面前生成道具CraftingBench;学生进入其他状态或该道具被碰撞后,CraftingBench消失且制作机器人失败。
- 持续时间结束后,道具CraftingBench所在位置生成一个Robot,CraftingBench消失
- TechOtaku的Robot的PlayerId = TechOtaku的PlayerId + n×5(一局游戏理论人数),其中1<=n<=3(自己的n为0)
- 新造的Robot的PlayerId的n总是尽量小
2. UseRobot
- CD:2s,持续时间:0s
- 输入额外参数PlayerID,切换到要使用的角色。
- 切换到其他角色时,自己进入UsingSkill状态。

修改后:

| 学生职业 | 教师Teacher | 健身狂Athlete |学霸StraightAStudent | 开心果Sunshine | 机器人Robot | 技术宅TechOtaku |
| :------------ | :------------------ | :------------------ | :------------------ | :------------------ | :------------------ | :------------------ |
| 移动速度/s | 2700 | 3150 | 2880 | 3000 | 2700 | 2880 |
| 最大毅力值 | 30000000 | 3000000 | 3300000 | 3200000 | 900000 | 2700000 |
| 最大沉迷度 | 600000 | 54000 | 78000 | 60000 | 0 | 60000 |
| 学习速度/ms | 50 | 73 | 135 | 123 | 85 | 110 |
| 勉励速度/ms | 80 | 90 | 100 | 120 | 0 | 100 |
| 隐蔽度 | 0.5 | 0.9 | 0.9 | 0.8 | 0.8 | 1.1 |
| 警戒范围 | 10000 | 15000 | 13500 | 15000 | 0 | 15000 |
| 视野范围 | 9000 | 11000 | 9000 | 10000 | 0 | 9000 |
| 开锁门速度/ms | 5000 | 5000 | 5000 | 3500 | 0 | 5000 |
| 翻窗速度/ms | 1000 | 1466 | 1018 | 1222 | 1 | 1100 |
| 翻箱速度/ms | 1250 | 1250 | 1250 | 1125 | 1000 | 1100 |

### 捣蛋鬼
- Assassin
- 技能隐身(BecomeInvisible)添加约束条件“使用隐身技能后,人物状态进入UsingSpecialSkill,进入其他状态会使得隐身状态解除"
- 喧哗者ANoisyPerson
- 普通攻击改为strike
- Klee
- 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙)
- 主动技能SparksNSplash(新增):
- CD:45s, 持续时间:10s
- 技能使用瞬间,对于输入的额外参数PlayerID代表的角色,距离最近的本已停止运动的小炸弹开始追踪该角色(每100ms向该角色直线移动)(该角色无血量则失败)
- 主动技能 蹦蹦炸弹 JumpyBomb
- 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上0°,45°,90°,135°,180°,225°,270°,315° 发出8个小炸弹
- Idol
主动技能ShowTime改为
"持续时间内
- 使警戒范围外的学生眩晕并每**500ms**发送向自己移动**500ms**的指令(速度为学生本应速度*二者距离/警戒范围)
- 对于视野范围(不是可视区域)内的学生每**500ms**加**1500**的沉迷度
- 捣蛋鬼变为0.8倍速"

+ 53
- 0
docs/版本更新说明.md View File

@@ -0,0 +1,53 @@
# 版本更新说明

# 说明
- 只说明对于选手较为重要的修改

# 5月6日3点更新
- docs:添加了 游戏机制与平衡性调整更新草案.pdf
- docs:添加了 版本更新说明.pdf
- docs&hotfix: 修正了GameRules.pdf中学生翻窗速度的错误
- remove: 删除了Place属性

# 5月6日12点更新
- hotfix: 修复了突然的bug(物件锁的相关问题)
- rule:增加了每帧最多50条主动指令的限制

# 5月8日更新
- feat:增加了可选地图功能
- **脚本RunServer(ForDebug).cmd/sh现在支持可选地图功能,但想选择地图,选手需要自行参照使用文档修改命令行或在云盘下载脚本**

# 5月9日19:30更新
- docs:更新了 游戏机制与平衡性调整更新草案.pdf
- change:更改了地图的文件路径

# 5月10日更新
- fix:修复JumpyDumpty的初始位置错误的问题
- fix:修正和重新说明攻击距离
- **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2**
- hotfix:修复小炸弹初始化类型错误的问题
- remove:去除了“实际上唤醒或勉励不同的人是有效的”
- **重复发出同一类型的交互指令和移动指令是无效的**
- feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`**

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

# 5月15日更新
- feat:Robot可用
- hotfix: 修复了移动相关的bug

# 5月19日13:00更新
- feat:TechOtaku可用
- feat:Klee、Idol已调整
- fix:修复了InSpire会给Tricker加速的问题
- fix:修复了开锁门的bug

# 5月19日15:00更新
- docs:更新了 游戏机制与平衡性调整更新草案.pdf
- hotfix:修复了移动状态设置错误

# 5月20日1:30更新
- docs:更新了 游戏机制与平衡性调整更新草案.pdf
- feat:游戏机制与平衡性调整已完成

+ 51
- 5
installer/Installer/MainWindow.xaml View File

@@ -52,7 +52,7 @@
<Button Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Name="UninstBtn" Content="卸载选手包" Command="{Binding ClickUninstCommand}" Visibility="{Binding MenuVis}" /> <Button Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Name="UninstBtn" Content="卸载选手包" Command="{Binding ClickUninstCommand}" Visibility="{Binding MenuVis}" />
<Button Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="2" Name="MenuBackBtn" Content="回到登陆界面" Command="{Binding ClickBackCommand}" Visibility="{Binding MenuVis}" /> <Button Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="2" Name="MenuBackBtn" Content="回到登陆界面" Command="{Binding ClickBackCommand}" Visibility="{Binding MenuVis}" />


<TextBlock Grid.Row="3" Grid.Column="3" Text="正在处理……" Grid.ColumnSpan="2" Visibility="{Binding ProgressVis}"/>
<TextBlock Grid.Row="3" Grid.Column="3" Text="{Binding ProcessingIntro}" Grid.ColumnSpan="2" Visibility="{Binding ProgressVis}"/>
<ProgressBar Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="7" Minimum="0" Maximum="100" Name="Progress" Visibility="{Binding ProgressVis}" IsIndeterminate="True"/> <ProgressBar Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="7" Minimum="0" Maximum="100" Name="Progress" Visibility="{Binding ProgressVis}" IsIndeterminate="True"/>


<TextBlock Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="4" Text="操作完成!你可以继续操作或退出" Visibility="{Binding CompleteVis}"/> <TextBlock Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="4" Text="操作完成!你可以继续操作或退出" Visibility="{Binding CompleteVis}"/>
@@ -81,10 +81,21 @@
<TextBlock Grid.Row="5" Grid.Column="1" Foreground="Red" Text=" 用户名或密码错误!" Visibility="{Binding LoginFailVis}"/> <TextBlock Grid.Row="5" Grid.Column="1" Foreground="Red" Text=" 用户名或密码错误!" Visibility="{Binding LoginFailVis}"/>
</Grid> </Grid>
</StackPanel> </StackPanel>
<Button Grid.Row="7" Grid.Column="1" Name="Login" Content="登录" Command="{Binding ClickLoginCommand}" Visibility="{Binding LoginVis}"/>
<Button Grid.Row="7" Grid.Column="2" Name="Launch" FontSize="11" Content="{Binding LaunchBtnCont}" Command="{Binding ClickLaunchCommand}" Visibility="{Binding LoginVis}"/>
<Button Grid.Row="7" Grid.Column="3" Name="ShiftLanguage" FontSize="11" Content="更改语言" Command="{Binding ClickShiftLanguageCommand}" Visibility="{Binding LaunchVis}"/>
<StackPanel Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Name="Login" Content="登录" Command="{Binding ClickLoginCommand}" Visibility="{Binding LoginVis}"/>
<Button Grid.Row="0" Grid.Column="1" Name="Launch" Content="{Binding LaunchBtnCont}" Command="{Binding ClickLaunchCommand}" Visibility="{Binding LoginVis}" IsEnabled="{Binding UpdatePlanned}"/>
<Button Grid.Row="0" Grid.Column="2" Name="ShiftLanguage" FontSize="11" Content="配置启动器" Command="{Binding ClickShiftLanguageCommand}" Visibility="{Binding LaunchVis}" IsEnabled="False"/>
</Grid>
</StackPanel>
<Button Grid.Row="7" Grid.Column="4" Grid.ColumnSpan="2" Name="Edit" Content="修改文件" Command="{Binding ClickEditCommand}" Visibility="{Binding LoginVis}"/> <Button Grid.Row="7" Grid.Column="4" Grid.ColumnSpan="2" Name="Edit" Content="修改文件" Command="{Binding ClickEditCommand}" Visibility="{Binding LoginVis}"/>




@@ -114,5 +125,40 @@
<Button Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" Content="申请对战" IsEnabled="False" Command="{Binding ClickRequestCommand}" Visibility="{Binding WebVis}" /> <Button Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" Content="申请对战" IsEnabled="False" Command="{Binding ClickRequestCommand}" Visibility="{Binding WebVis}" />
<TextBox Grid.Row="4" Grid.Column="3" Grid.ColumnSpan="2" Text="暂不支持" IsEnabled="False" Visibility="{Binding WebVis}" /> <TextBox Grid.Row="4" Grid.Column="3" Grid.ColumnSpan="2" Text="暂不支持" IsEnabled="False" Visibility="{Binding WebVis}" />
<!--objects below are enabled-->
<StackPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="7">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Name ="ConfigServer" Content="服务端" Command="{Binding ClickCfgServCommand}" IsEnabled="{Binding ServerCfgNotSelected}" Visibility="{Binding CfgVis}"/>
<Button Grid.Column="1" Name ="ConfigAI" Content="客户端" Command="{Binding ClickCfgCliCommand}" IsEnabled="{Binding CliCfhNotSelected}" Visibility="{Binding CfgVis}"/>
<Button Grid.Column="2" Name ="ConfigCliend" Content="图形界面" Command="{Binding ClickCfgGUICommand}" IsEnabled="{Binding GUICfgNotSelected}" Visibility="{Binding CfgVis}"/>
</Grid>
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="1" Grid.RowSpan="5" Grid.ColumnSpan="7">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
</StackPanel>
</Grid> </Grid>
</Window> </Window>

+ 499
- 29
installer/Installer/Model.cs View File

@@ -20,6 +20,7 @@ using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Windows; using System.Windows;
using System.Windows.Shapes; using System.Windows.Shapes;
using System.Collections.Concurrent;
//using System.Windows.Forms; //using System.Windows.Forms;
using System.Threading; using System.Threading;


@@ -198,7 +199,113 @@ namespace starter.viewmodel.settings
{ {
if (Tencent_cos_download.CheckAlreadyDownload()) if (Tencent_cos_download.CheckAlreadyDownload())
{ {
Process.Start(System.IO.Path.Combine(Data.FilePath, startName));
//Process.Start(System.IO.Path.Combine(Data.FilePath, startName));
switch (RunProgram.RunInfo.mode)
{
case RunProgram.RunMode.ServerOnly:
RunProgram.StartServer(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
break;
case RunProgram.RunMode.ServerForDebugOnly:
RunProgram.StartServerForDebug(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
break;
case RunProgram.RunMode.GUIAttendGameOnly:
RunProgram.RunGUIClient(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.characterID,
false, RunProgram.RunInfo.occupation, RunProgram.RunInfo.type);
break;
case RunProgram.RunMode.GUIVisit:
RunProgram.RunGUIClient(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, 0, true, 1, 1);
break;
case RunProgram.RunMode.GUIAndAICpp:
RunProgram.StartServerForDebug(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
Task.Delay(100);
RunProgram.RunCpp(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.saveDebugLog, RunProgram.RunInfo.showDebugLog,
RunProgram.RunInfo.warningOnly, RunProgram.RunInfo.playerId, RunProgram.RunInfo.filePath);
RunProgram.RunInfo.playerId = null;
RunProgram.RunInfo.filePath = null;
RunProgram.RunGUIClient(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.characterID,
false, RunProgram.RunInfo.occupation, RunProgram.RunInfo.type);
break;
case RunProgram.RunMode.GUIAndAIPython:
RunProgram.StartServerForDebug(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
Task.Delay(100);
RunProgram.RunPython(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.saveDebugLog, RunProgram.RunInfo.showDebugLog,
RunProgram.RunInfo.warningOnly, RunProgram.RunInfo.playerId, RunProgram.RunInfo.filePath);
RunProgram.RunInfo.playerId = null;
RunProgram.RunInfo.filePath = null;
RunProgram.RunGUIClient(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.characterID,
false, RunProgram.RunInfo.occupation, RunProgram.RunInfo.type);
break;
case RunProgram.RunMode.ServerAndCpp:
RunProgram.StartServer(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
Task.Delay(100);
RunProgram.RunCpp(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.saveDebugLog, RunProgram.RunInfo.showDebugLog,
RunProgram.RunInfo.warningOnly, RunProgram.RunInfo.playerId, RunProgram.RunInfo.filePath);
RunProgram.RunInfo.playerId = null;
RunProgram.RunInfo.filePath = null;
break;
case RunProgram.RunMode.ServerAndPython:
RunProgram.StartServer(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
Task.Delay(100);
RunProgram.RunPython(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.saveDebugLog, RunProgram.RunInfo.showDebugLog,
RunProgram.RunInfo.warningOnly, RunProgram.RunInfo.playerId, RunProgram.RunInfo.filePath);
RunProgram.RunInfo.playerId = null;
RunProgram.RunInfo.filePath = null;
break;
case RunProgram.RunMode.ServerAndCppVisit:
RunProgram.StartServer(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
Task.Delay(100);
RunProgram.RunCpp(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.saveDebugLog, RunProgram.RunInfo.showDebugLog,
RunProgram.RunInfo.warningOnly, RunProgram.RunInfo.playerId, RunProgram.RunInfo.filePath);
RunProgram.RunInfo.playerId = null;
RunProgram.RunInfo.filePath = null;
RunProgram.RunGUIClient(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.characterID, true, 0, 1);
break;
case RunProgram.RunMode.ServerAndPythonVisit:
RunProgram.StartServer(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
Task.Delay(100);
RunProgram.RunPython(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.saveDebugLog, RunProgram.RunInfo.showDebugLog,
RunProgram.RunInfo.warningOnly, RunProgram.RunInfo.playerId, RunProgram.RunInfo.filePath);
RunProgram.RunInfo.playerId = null;
RunProgram.RunInfo.filePath = null;
RunProgram.RunGUIClient(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.characterID, true, 0, 1);
break;
case RunProgram.RunMode.ServerDebugAndCppVisit:
RunProgram.StartServerForDebug(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
Task.Delay(100);
RunProgram.RunCpp(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.saveDebugLog, RunProgram.RunInfo.showDebugLog,
RunProgram.RunInfo.warningOnly, RunProgram.RunInfo.playerId, RunProgram.RunInfo.filePath);
RunProgram.RunInfo.playerId = null;
RunProgram.RunInfo.filePath = null;
RunProgram.RunGUIClient(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.characterID, true, 0, 1);
break;
case RunProgram.RunMode.ServerDebugAndPythonVisit:
RunProgram.StartServerForDebug(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.gameTimeSec, RunProgram.RunInfo.playbackFileName);
Task.Delay(100);
RunProgram.RunPython(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.studentCount,
RunProgram.RunInfo.trickerCount, RunProgram.RunInfo.saveDebugLog, RunProgram.RunInfo.showDebugLog,
RunProgram.RunInfo.warningOnly, RunProgram.RunInfo.playerId, RunProgram.RunInfo.filePath);
RunProgram.RunInfo.playerId = null;
RunProgram.RunInfo.filePath = null;
RunProgram.RunGUIClient(RunProgram.RunInfo.IP, RunProgram.RunInfo.port, RunProgram.RunInfo.characterID, true, 0, 1);
break;
}
return true; return true;
} }
else else
@@ -298,7 +405,7 @@ namespace starter.viewmodel.settings
/// <summary> /// <summary>
/// 关于介绍的屏幕显示信息 /// 关于介绍的屏幕显示信息
/// </summary> /// </summary>
public enum Status { newUser, menu, move, working, disconnected, error, successful, login, web };
public enum Status { newUser, menu, move, working, initializing, disconnected, error, successful, login, web, launch };
public Status status public Status status
{ {
get; set; get; set;
@@ -361,8 +468,9 @@ namespace Downloader


class Program class Program
{ {
static List<string> newFileName = new List<string>(); // 新文件名
static List<string> updateFileName = new List<string>(); // 更新文件名
static ConcurrentQueue<string> newFileName = new ConcurrentQueue<string>();
//static List<string> newFileName = new List<string>(); // 新文件名
static ConcurrentQueue<string> updateFileName = new ConcurrentQueue<string>(); // 更新文件名
static List<string> updateFailed = new List<string>(); //更新失败的文件名 static List<string> updateFailed = new List<string>(); //更新失败的文件名
static public List<string> UpdateFailed static public List<string> UpdateFailed
{ {
@@ -505,7 +613,6 @@ namespace Downloader
string secretId = "***"; //"云 API 密钥 SecretId"; string secretId = "***"; //"云 API 密钥 SecretId";
string secretKey = "***"; //"云 API 密钥 SecretKey"; string secretKey = "***"; //"云 API 密钥 SecretKey";



long durationSecond = 1000; // 每次请求签名有效时长,单位为秒 long durationSecond = 1000; // 每次请求签名有效时长,单位为秒
QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider( QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider(
secretId, secretKey, durationSecond secretId, secretKey, durationSecond
@@ -654,7 +761,7 @@ namespace Downloader
{ {
MD5 = GetFileMd5Hash(System.IO.Path.Combine(Data.FilePath, pair.Key.TrimStart(new char[] { '.', '/' }))); MD5 = GetFileMd5Hash(System.IO.Path.Combine(Data.FilePath, pair.Key.TrimStart(new char[] { '.', '/' })));
if (MD5.Length == 0) // 文档不存在 if (MD5.Length == 0) // 文档不存在
newFileName.Add(pair.Key);
newFileName.Enqueue(pair.Key);
else if (MD5.Equals("conflict")) else if (MD5.Equals("conflict"))
{ {
if (pair.Key.Equals("THUAI6/win/CAPI/cpp/.vs/CAPI/v17/Browse.VC.db")) if (pair.Key.Equals("THUAI6/win/CAPI/cpp/.vs/CAPI/v17/Browse.VC.db"))
@@ -670,7 +777,7 @@ namespace Downloader
MessageBox.Show($"检查{pair.Key}更新时遇到问题,请反馈", "读取出错", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"检查{pair.Key}更新时遇到问题,请反馈", "读取出错", MessageBoxButton.OK, MessageBoxImage.Error);
} }
else if (!MD5.Equals(pair.Value) && !IsUserFile(System.IO.Path.GetFileName(pair.Key))) // MD5不匹配 else if (!MD5.Equals(pair.Value) && !IsUserFile(System.IO.Path.GetFileName(pair.Key))) // MD5不匹配
updateFileName.Add(pair.Key);
updateFileName.Enqueue(pair.Key);
} }
} }


@@ -736,38 +843,110 @@ namespace Downloader
{ {
try try
{ {
foreach (string filename in newFileName)
int cnt = newFileName.Count;
if (cnt <= 20)
{ {
//Console.WriteLine(newFile + 1 + "/" + totalnew + ":开始下载" + filename);
Downloader.download(System.IO.Path.Combine(@Data.FilePath, filename), filename.TrimStart(new char[] { '.', '/' }));
//Console.WriteLine(filename + "下载完毕!" + Environment.NewLine);
newFile++;
while (newFileName.TryDequeue(out var filename))
{
Downloader.download(System.IO.Path.Combine(@Data.FilePath, filename), filename.TrimStart(new char[] { '.', '/' }));
//Console.WriteLine(filename + "下载完毕!" + Environment.NewLine);
Interlocked.Increment(ref newFile);
}
} }
foreach (string filename in updateFileName)
else
{ {
//Console.WriteLine(updateFile + 1 + "/" + totalupdate + ":开始下载" + filename);
try
const int nthread = 8;
var thrds = new List<Thread>();
for (int i = 0; i < nthread; i++)
{ {
File.Delete(System.IO.Path.Combine(@Data.FilePath, filename));
Downloader.download(System.IO.Path.Combine(@Data.FilePath, filename), filename.TrimStart(new char[] { '.', '/' }));
var thrd = new Thread(() =>
{
while (newFileName.TryDequeue(out var filename))
{
Downloader.download(System.IO.Path.Combine(@Data.FilePath, filename), filename.TrimStart(new char[] { '.', '/' }));
//Console.WriteLine(filename + "下载完毕!" + Environment.NewLine);
Interlocked.Increment(ref newFile);
}
});
thrd.Start();
thrds.Add(thrd);
} }
catch (System.IO.IOException)
foreach (var thrd in thrds)
{ {
updateFailed = updateFailed.Append(filename).ToList();
thrd.Join();
} }
catch
}
// 读取 Interlocked.CompareExchange(ref newFile, 0, 0);

int upcnt = updateFileName.Count;
if (upcnt <= 20)
{
while (updateFileName.TryDequeue(out var filename))
{ {
if (filename.Substring(filename.Length - 4, 4).Equals(".pdf"))
try
{
File.Delete(System.IO.Path.Combine(@Data.FilePath, filename));
Downloader.download(System.IO.Path.Combine(@Data.FilePath, filename), filename.TrimStart(new char[] { '.', '/' }));
}
catch (System.IO.IOException)
{
updateFailed = updateFailed.Append(filename).ToList();
}
catch
{ {
MessageBox.Show($"由于曾经发生过的访问冲突,下载器无法更新{filename}\n"
+ $"请手动删除{filename},然后再试一次。");
if (filename.Substring(filename.Length - 4, 4).Equals(".pdf"))
{
MessageBox.Show($"由于曾经发生过的访问冲突,下载器无法更新{filename}\n"
+ $"请手动删除{filename},然后再试一次。");
}
else
MessageBox.Show($"更新{filename}时遇到未知问题,请反馈");
updateFailed = updateFailed.Append(filename).ToList();
} }
else
MessageBox.Show($"更新{filename}时遇到未知问题,请反馈");
updateFailed = updateFailed.Append(filename).ToList();
Interlocked.Increment(ref newFile);
}
}
else
{
const int nthread = 8;
var thrds = new List<Thread>();

for (int i = 0; i < nthread; i++)
{
var thrd = new Thread(() =>
{
while (updateFileName.TryDequeue(out var filename))
{
try
{
File.Delete(System.IO.Path.Combine(@Data.FilePath, filename));
Downloader.download(System.IO.Path.Combine(@Data.FilePath, filename), filename.TrimStart(new char[] { '.', '/' }));
}
catch (System.IO.IOException)
{
updateFailed = updateFailed.Append(filename).ToList();
}
catch
{
if (filename.Substring(filename.Length - 4, 4).Equals(".pdf"))
{
MessageBox.Show($"由于曾经发生过的访问冲突,下载器无法更新{filename}\n"
+ $"请手动删除{filename},然后再试一次。");
}
else
MessageBox.Show($"更新{filename}时遇到未知问题,请反馈");
updateFailed = updateFailed.Append(filename).ToList();
}
Interlocked.Increment(ref newFile);
}
});
thrd.Start();
thrds.Add(thrd);
}
foreach (var thrd in thrds)
{
thrd.Join();
} }
//Console.WriteLine(filename + "下载完毕!" + Environment.NewLine);
updateFile++;
} }
if (updateFailed.Count == 0) if (updateFailed.Count == 0)
UpdatePlanned = false; UpdatePlanned = false;
@@ -869,7 +1048,7 @@ namespace Downloader


newFileName.Clear(); newFileName.Clear();
updateFileName.Clear(); updateFileName.Clear();
newFileName.Add("THUAI6.tar.gz");
newFileName.Enqueue("THUAI6.tar.gz");
Download(); Download();
Stream? inStream = null; Stream? inStream = null;
Stream? gzipStream = null; Stream? gzipStream = null;
@@ -1373,6 +1552,297 @@ namespace Downloader
sw.Close(); sw.Close();
} }
} }

public class RunProgram
{
public enum RunMode
{
ServerOnly, //只启动Server
ServerForDebugOnly, //只启动ServerForDebug
GUIAttendGameOnly, //只用GUIClient参与游戏
GUIVisit, //只用GUIClient观战
GUIAndAICpp, //用GUI参与游戏并且让cpp的AI同时参与
GUIAndAIPython, //用GUI参与游戏并且让python的AI同时参与
ServerAndCpp, //只运行Server和cpp,
ServerAndPython, //只运行Server和python
ServerAndCppVisit, //运行Server和Cpp并用GUI观战
ServerAndPythonVisit, //运行Server和python并用GUI观战
ServerDebugAndCpp, //运行ServerForDebug...
ServerDebugAndPython,
ServerDebugAndCppVisit,
ServerDebugAndPythonVisit,
}

public class RunInfo //UI需要在调用Launch函数前保证其中数据已经更新并有效
{
static public RunMode mode;
static public string? IP;
static public int port;
static public int studentCount;
static public int trickerCount;
static public int gameTimeSec;
static public string? playbackFileName;
static public int characterID;
static public int type;
static public bool saveDebugLog;
static public bool showDebugLog;
static public bool warningOnly;
static public bool visiting;
static public int occupation;
static public List<int>? playerId = new List<int>(); //两者长度必须与studentCount + trickerCount一致
static public List<string>? filePath = new List<string>();
}

/// <summary>
/// 运行cmd命令
/// 会显示命令窗口
/// </summary>
/// <param name="cmdExe">指定应用程序的完整路径</param>
/// <param name="cmdStr">执行命令行参数</param>
static bool RunCmd(string cmdExe, string cmdStr)
{
bool result = false;
try
{
using (Process myPro = new Process())
{
//指定启动进程是调用的应用程序和命令行参数
ProcessStartInfo psi = new ProcessStartInfo(cmdExe, cmdStr);
myPro.StartInfo = psi;
myPro.Start();
//myPro.WaitForExitAsync();
result = true;
}
}
catch
{

}
return result;
}

/// <summary>
/// 启动Server
/// 会显示命令窗口
/// </summary>
/// <param name="IP">指定Server运行的IPV4地址</param>
/// <param name="port">指定Server运行的端口</param>
/// <param name="studentCount">指定学生人数</param>
/// <param name="trickerCount">指定捣蛋鬼人数</param>
/// <param name="gameTimeSec">指定游戏最大时长</param>
/// <param name="playbackFileName">指定回放文件名称</param>
public static int StartServer(string? IP, int port, int studentCount, int trickerCount, int gameTimeSec, string? playbackFileName)
{
if (System.Diagnostics.Process.GetProcessesByName("Server").ToList().Count > 0)
{
System.Diagnostics.Process.GetProcessesByName("Server")[0].Kill();
}
string cmdExe = $"{Data.FilePath}\\THUAI6\\win\\win64\\Server.exe";
string cmdPara = $"--ip {IP} --port {port} --studentCount {studentCount} --trickerCount {trickerCount}" +
$" --gameTimeInSecond {gameTimeSec} --fileName {playbackFileName}";
RunCmd(cmdExe, cmdPara);
return 0;
}

/// <summary>
/// 启动Debug用的Server
/// 会显示命令窗口
/// </summary>
/// <param name="IP">指定Server运行的IPV4地址</param>
/// <param name="port">指定Server运行的端口</param>
/// <param name="studentCount">指定学生人数</param>
/// <param name="trickerCount">指定捣蛋鬼人数</param>
/// <param name="gameTimeSec">指定游戏最大时长</param>
/// <param name="playbackFileName">指定回放文件名称</param>
public static int StartServerForDebug(string? IP, int port, int studentCount, int trickerCount, int gameTimeSec, string? playbackFileName)
{
if (System.Diagnostics.Process.GetProcessesByName("Server").ToList().Count > 0)
{
System.Diagnostics.Process.GetProcessesByName("Server")[0].Kill();
}
string cmdExe = $"{Data.FilePath}\\THUAI6\\win\\win64\\Debug\\Server.exe";
string cmdPara = $"--ip {IP} --port {port} --studentCount {studentCount} --trickerCount {trickerCount} " +
$"--gameTimeInSecond {gameTimeSec} --fileName {playbackFileName}";
RunCmd(cmdExe, cmdPara);
return 0;
}

/// <summary>
/// 启动cpp,在这之前要先启动Server,可能还需要一些延时
/// 会显示命令窗口
/// </summary>
/// <param name="IP">指定Server运行的IPV4地址</param>
/// <param name="port">指定Server运行的端口</param>
/// <param name="studentCount">指定学生人数</param>
/// <param name="trickerCount">指定捣蛋鬼人数</param>
/// <param name="saveDebugLog">是否保存Debug日志文件</param>
/// <param name="showDebugLog">是否将日志输出到屏幕上</param>
/// <param name="warningOnly">在showDebugLog == true时,是否只输出警告或者报错(不影响日志保存)</param>
/// <param name="playerIp">默认为空,如果不为空,playerIp将会按照顺序代替默认从0~3的IP</param>
public static int RunCpp(string? IP, int port, int studentCount, int trickerCount, bool saveDebugLog,
bool showDebugLog, bool warningOnly, List<int>? playerIp = null, List<string>? filePath = null)
{
string cmdExe;
string cmdBase = $"-I {IP} -P {port} ";
for (int i = 0; i < studentCount; i++)
{
string cmdPara;
if (playerIp == null)
{
cmdPara = cmdBase + $" -p {i}";
}
else
{
cmdPara = cmdBase + $" -p {playerIp[i]}";
}
if (saveDebugLog)
{
cmdPara = cmdPara + " -d";
}
if (showDebugLog)
{
cmdPara = cmdPara + " -o";
}
if (warningOnly)
{
cmdPara = cmdPara + " -w";
}
if (filePath == null)
{
cmdExe = $"{Data.FilePath}\\THUAI6\\win\\CAPI\\cpp\\x64\\Debug\\API.exe";
}
else
{
cmdExe = filePath[i];
}
RunCmd(cmdExe, cmdPara);
}

if (trickerCount != 0)
{
string cmdPara;
cmdPara = cmdBase + " -p 4";
if (saveDebugLog)
{
cmdPara = cmdPara + " -d";
}
if (showDebugLog)
{
cmdPara = cmdPara + " -o";
}
if (warningOnly)
{
cmdPara = cmdPara + " -w";
}
if (filePath == null)
{
cmdExe = $"{Data.FilePath}\\THUAI6\\win\\CAPI\\cpp\\x64\\Debug\\API.exe";
}
else
{
cmdExe = filePath[studentCount];
}
RunCmd(cmdExe, cmdPara);
}
return 0;
}

/// <summary>
/// 启动客户端
/// 会显示命令窗口
/// </summary>
/// <param name="IP">指定Server运行的IPV4地址</param>
/// <param name="port">指定Server运行的端口</param>
/// <param name="characterID">进入游戏时的角色ID 0~4,0~3为学生,4为捣蛋鬼</param>
/// <param name="visiting">是否观战,当值为true时,characterID无效</param>
/// <param name="occupation">指定参加游戏时的角色职业,详情见说明文档</param>
/// <param name="type">角色类别,type = 1 表示学生,type = 2 表示捣蛋鬼</param>
public static int RunGUIClient(string? IP, int port, int characterID, bool visiting, int occupation = 1, int type = 1)
{
string cmdExe = $"{Data.FilePath}\\THUAI6\\win\\win64\\Client.exe";
string cmdBase = $"--port {port} --ip {IP} --type {type} --occupation {occupation}";
if (visiting)
{
cmdBase = cmdBase + " --characterID 200000";
}
else
{
cmdBase = cmdBase + $" --characterID {characterID}";
}
cmdBase = cmdBase + " --cl";
RunCmd(cmdExe, cmdBase);
return 0;
}

//启动python,参数同RunCpp
public static int RunPython(string? IP, int port, int studentCount, int trickerCount, bool saveDebugLog,
bool showDebugLog, bool warningOnly, List<int>? playerIp = null, List<string>? filePath = null)
{
string cmdExe = $"python";
string cmdBase;
for (int i = 0; i < studentCount; i++)
{
string cmdPara;
if (filePath == null)
{
cmdBase = $" {Data.FilePath}\\THUAI6\\win\\CAPI\\python\\PyAPI\\main.py -I {IP} -P {port} ";
}
else
{
cmdBase = " " + filePath[i] + $" -I {IP} -P {port} ";
}
if (playerIp == null)
{
cmdPara = cmdBase + $" -p {i}";
}
else
{
cmdPara = cmdBase + $" -p {playerIp[i]}";
}
if (saveDebugLog)
{
cmdPara = cmdPara + " -d";
}
if (showDebugLog)
{
cmdPara = cmdPara + " -o";
}
if (warningOnly)
{
cmdPara = cmdPara + " -w";
}
RunCmd(cmdExe, cmdPara);
}

if (trickerCount != 0)
{
string cmdPara;
if (filePath == null)
{
cmdBase = $" {Data.FilePath}\\THUAI6\\win\\CAPI\\python\\PyAPI\\main.py -I {IP} -P {port} ";
}
else
{
cmdBase = " " + filePath[studentCount] + $" -I {IP} -P {port} ";
}
cmdPara = cmdBase + " -p 4";
if (saveDebugLog)
{
cmdPara = cmdPara + " -d";
}
if (showDebugLog)
{
cmdPara = cmdPara + " -o";
}
if (warningOnly)
{
cmdPara = cmdPara + " -w";
}
RunCmd(cmdExe, cmdPara);
}
return 0;
}
}
} }
} }




+ 107
- 29
installer/Installer/ViewModel.cs View File

@@ -22,6 +22,7 @@ namespace starter.viewmodel.settings
//定义BackgroundWorker //定义BackgroundWorker
BackgroundWorker asyncDownloader; BackgroundWorker asyncDownloader;
BackgroundWorker asyncUpdater; BackgroundWorker asyncUpdater;
BackgroundWorker asyncInitializer;
/// <summary> /// <summary>
/// Model object /// Model object
/// </summary> /// </summary>
@@ -53,44 +54,91 @@ namespace starter.viewmodel.settings
} }
} }


//实例化BackgroundWorker
asyncDownloader = new BackgroundWorker();
asyncUpdater = new BackgroundWorker();
//指示BackgroundWorker是否可以报告进度更新
//当该属性值为True是,将可以成功调用ReportProgress方法,否则将引发InvalidOperationException异常。
asyncDownloader.WorkerReportsProgress = true;
asyncUpdater.WorkerReportsProgress = true;
//挂载方法:
asyncDownloader.DoWork += AsyncDownloader_DoWork;
asyncUpdater.DoWork += AsyncUpdater_DoWork;
//完成通知器:
asyncDownloader.RunWorkerCompleted += AsyncDownloader_RunWorkerCompleted;
asyncUpdater.RunWorkerCompleted += AsyncUpdater_RunWorkerCompleted;

UpdateInfoVis = Visibility.Collapsed; UpdateInfoVis = Visibility.Collapsed;


if (Downloader.Program.Tencent_cos_download.CheckAlreadyDownload())
asyncInitializer = new BackgroundWorker();
asyncInitializer.WorkerReportsProgress = true;
asyncInitializer.DoWork += AsyncInitializer_DoWork;
asyncInitializer.RunWorkerCompleted += AsyncInitializer_RunWorkerCompleted;

asyncInitializer.RunWorkerAsync();
Status = SettingsModel.Status.initializing;

}

private void AsyncInitializer_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
{
if (e.Result == null)
{
Status = SettingsModel.Status.error;
}
else if ((int)e.Result == 1)
{ {
obj.checkUpdate();
Status = SettingsModel.Status.login; Status = SettingsModel.Status.login;
this.RaisePropertyChanged("WindowWidth");
this.RaisePropertyChanged("LaunchVis"); this.RaisePropertyChanged("LaunchVis");
if (obj.RecallUser())
RememberMe = true;
else
RememberMe = false;
this.RaisePropertyChanged("RememberMe"); this.RaisePropertyChanged("RememberMe");
this.RaisePropertyChanged("SwitchOSBtnCont"); this.RaisePropertyChanged("SwitchOSBtnCont");
//在启动时立刻检查更新,确保选手启动最新版选手包
//若有更新,将启动键改为更新键;
//相应地,使用login界面启动;
//结构:上方为登录框架,下方有“修改选手包”按钮
this.RaisePropertyChanged("UpdateBtnCont");
this.RaisePropertyChanged("LaunchBtnCont");
this.RaisePropertyChanged("Updateinfo");
this.RaisePropertyChanged("UpdatePlanned");
} }
else else
{ {
Route = Environment.GetEnvironmentVariable("USERPROFILE") + "\\THUAI6"; Route = Environment.GetEnvironmentVariable("USERPROFILE") + "\\THUAI6";
Status = SettingsModel.Status.newUser; Status = SettingsModel.Status.newUser;
this.RaisePropertyChanged("WindowWidth");
}
}

private void AsyncInitializer_DoWork(object? sender, DoWorkEventArgs e)
{
if (asyncInitializer.CancellationPending)
{
Status = SettingsModel.Status.error;
e.Cancel = true;
return;
}
else
{
//实例化BackgroundWorker
asyncDownloader = new BackgroundWorker();
asyncUpdater = new BackgroundWorker();
//指示BackgroundWorker是否可以报告进度更新
//当该属性值为True是,将可以成功调用ReportProgress方法,否则将引发InvalidOperationException异常。
asyncDownloader.WorkerReportsProgress = true;
asyncUpdater.WorkerReportsProgress = true;
//挂载方法:
asyncDownloader.DoWork += AsyncDownloader_DoWork;
asyncUpdater.DoWork += AsyncUpdater_DoWork;
//完成通知器:
asyncDownloader.RunWorkerCompleted += AsyncDownloader_RunWorkerCompleted;
asyncUpdater.RunWorkerCompleted += AsyncUpdater_RunWorkerCompleted;

if (Downloader.Program.Tencent_cos_download.CheckAlreadyDownload())
{
obj.checkUpdate();

if (obj.RecallUser())
RememberMe = true;
else
RememberMe = false;

//在启动时立刻检查更新,确保选手启动最新版选手包
//若有更新,将启动键改为更新键;
//相应地,使用login界面启动;
//结构:上方为登录框架,下方有“修改选手包”按钮

//下面几行是用来运行测试的代码
//Program.RunProgram.StartServerForDebug("0.0.0.0",8888,4,1,600,"video");
//Program.RunProgram.RunCpp("127.0.0.1",8888,4,1,false,true,false);
//Program.RunProgram.RunGUIClient("127.0.0.1", 8888, 0, true);

e.Result = 1;
}
else
{
e.Result = 2;
}
} }
} }


@@ -148,6 +196,7 @@ namespace starter.viewmodel.settings
this.RaisePropertyChanged("UpdateInfo"); this.RaisePropertyChanged("UpdateInfo");
this.RaisePropertyChanged("LaunchBtnCont"); this.RaisePropertyChanged("LaunchBtnCont");
} }
this.RaisePropertyChanged("UpdatePlanned");
} }
} }


@@ -252,6 +301,7 @@ namespace starter.viewmodel.settings
this.RaisePropertyChanged("LaunchVis"); this.RaisePropertyChanged("LaunchVis");
this.RaisePropertyChanged("NewUserVis"); this.RaisePropertyChanged("NewUserVis");
this.RaisePropertyChanged("ConfirmBtnCont"); this.RaisePropertyChanged("ConfirmBtnCont");
this.RaisePropertyChanged("ProcessingIntro");
} }
} }
public string Intro public string Intro
@@ -293,6 +343,21 @@ namespace starter.viewmodel.settings
} }
} }
} }
public string ProcessingIntro
{
get
{
switch (Status)
{
case SettingsModel.Status.working:
return "正在下载";
case SettingsModel.Status.initializing:
return "正在检查更新";
default:
return "";
}
}
}
public string AbortOrSelLanguage public string AbortOrSelLanguage
{ {
get get
@@ -448,7 +513,7 @@ namespace starter.viewmodel.settings
{ {
get get
{ {
return Status == SettingsModel.Status.working ? Visibility.Visible : Visibility.Collapsed;
return (Status == SettingsModel.Status.working || Status == SettingsModel.Status.initializing) ? Visibility.Visible : Visibility.Collapsed;
} }
} }
public Visibility CompleteVis public Visibility CompleteVis
@@ -503,6 +568,13 @@ namespace starter.viewmodel.settings
return obj.status == SettingsModel.Status.login && (!obj.UpdatePlanned) ? Visibility.Visible : Visibility.Collapsed; return obj.status == SettingsModel.Status.login && (!obj.UpdatePlanned) ? Visibility.Visible : Visibility.Collapsed;
} }
} }
public Visibility CfgVis
{
get
{
return obj.status == SettingsModel.Status.launch ? Visibility.Visible : Visibility.Collapsed;
}
}


public string UpdateBtnCont public string UpdateBtnCont
{ {
@@ -529,9 +601,9 @@ namespace starter.viewmodel.settings
if (obj.UpdatePlanned) if (obj.UpdatePlanned)
ans = "更新"; ans = "更新";
else if (obj.launchLanguage == SettingsModel.LaunchLanguage.cpp) else if (obj.launchLanguage == SettingsModel.LaunchLanguage.cpp)
ans = "启动c++包";
ans = "启动选手包";
else else
ans = "启动python包";
ans = "启动选手包";
return ans; return ans;
} }
} }
@@ -565,6 +637,11 @@ namespace starter.viewmodel.settings
} }
} }


public bool UpdatePlanned
{
get { return obj.UpdatePlanned; }
}

public string RouteSelectWindow(string type) public string RouteSelectWindow(string type)
{ {
if (type == "Folder") if (type == "Folder")
@@ -714,6 +791,7 @@ namespace starter.viewmodel.settings
this.RaisePropertyChanged("UpdateBtnCont"); this.RaisePropertyChanged("UpdateBtnCont");
this.RaisePropertyChanged("UpdateInfo"); this.RaisePropertyChanged("UpdateInfo");
this.RaisePropertyChanged("LaunchVis"); this.RaisePropertyChanged("LaunchVis");
this.RaisePropertyChanged("UpdatePlanned");
} }
})); }));
} }


+ 1
- 1
logic/Client/Client.csproj View File

@@ -15,7 +15,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="FrameRateTask" Version="1.2.0" /> <PackageReference Include="FrameRateTask" Version="1.2.0" />
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
<PackageReference Include="Google.Protobuf" Version="3.22.4" />
<PackageReference Include="Grpc" Version="2.46.6" /> <PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Core" Version="2.46.6" /> <PackageReference Include="Grpc.Core" Version="2.46.6" />
</ItemGroup> </ItemGroup>


+ 319
- 201
logic/Client/MainWindow.xaml.cs View File

@@ -58,6 +58,7 @@ namespace Client
listOfDoor = new List<MessageOfDoor>(); listOfDoor = new List<MessageOfDoor>();
listOfGate = new List<MessageOfGate>(); listOfGate = new List<MessageOfGate>();
listOfHiddenGate = new List<MessageOfHiddenGate>(); listOfHiddenGate = new List<MessageOfHiddenGate>();
countList = new List<int>();
WindowStartupLocation = WindowStartupLocation.CenterScreen; WindowStartupLocation = WindowStartupLocation.CenterScreen;
unit = Math.Sqrt(UpperLayerOfMap.ActualHeight * UpperLayerOfMap.ActualWidth) / 50; unit = Math.Sqrt(UpperLayerOfMap.ActualHeight * UpperLayerOfMap.ActualWidth) / 50;
unitFontsize = unit / 13; unitFontsize = unit / 13;
@@ -112,7 +113,7 @@ namespace Client
return; return;
} }
_ = Parser.Default.ParseArguments<ArgumentOptions>(args).WithParsed(o => _ = Parser.Default.ParseArguments<ArgumentOptions>(args).WithParsed(o =>
{ options = o; });
{ options = o; });
if (options != null && Convert.ToInt64(options.PlayerID) > 2023) if (options != null && Convert.ToInt64(options.PlayerID) > 2023)
{ {
isSpectatorMode = true; isSpectatorMode = true;
@@ -159,7 +160,7 @@ namespace Client
{ {
var pbClient = new PlaybackClient(fileName, pbSpeed); var pbClient = new PlaybackClient(fileName, pbSpeed);
int[,]? map; 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; isClientStocked = false;
PorC.Content = "⏸"; PorC.Content = "⏸";
@@ -311,6 +312,15 @@ namespace Client


private void DrawMap() 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 i = 0; i < defaultMap.GetLength(0); i++)
{ {
for (int j = 0; j < defaultMap.GetLength(1); j++) for (int j = 0; j < defaultMap.GetLength(1); j++)
@@ -336,10 +346,38 @@ namespace Client
case 8: case 8:
mapPatches[i, j].Fill = Brushes.LightPink; mapPatches[i, j].Fill = Brushes.LightPink;
mapPatches[i, j].Stroke = 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: case 9:
mapPatches[i, j].Fill = Brushes.LightSkyBlue; mapPatches[i, j].Fill = Brushes.LightSkyBlue;
mapPatches[i, j].Stroke = 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 break;//gate
case 10: case 10:
break;//emergency break;//emergency
@@ -352,10 +390,38 @@ namespace Client
case 14: case 14:
mapPatches[i, j].Fill = Brushes.Khaki; mapPatches[i, j].Fill = Brushes.Khaki;
mapPatches[i, j].Stroke = 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 break;//door
case 15: case 15:
mapPatches[i, j].Fill = Brushes.Orange; mapPatches[i, j].Fill = Brushes.Orange;
mapPatches[i, j].Stroke = 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 break;//chest
default: default:
break; break;
@@ -416,6 +482,8 @@ namespace Client
listOfHiddenGate.Clear(); listOfHiddenGate.Clear();
listOfGate.Clear(); listOfGate.Clear();
MessageToClient content = responseStream.ResponseStream.Current; MessageToClient content = responseStream.ResponseStream.Current;
MessageOfMap mapMessage = new MessageOfMap();
bool mapMessageExist = false;
switch (content.GameState) switch (content.GameState)
{ {
case GameState.GameStart: case GameState.GameStart:
@@ -459,11 +527,17 @@ namespace Client
listOfGate.Add(obj.GateMessage); listOfGate.Add(obj.GateMessage);
break; break;
case MessageOfObj.MessageOfObjOneofCase.MapMessage: case MessageOfObj.MessageOfObjOneofCase.MapMessage:
GetMap(obj.MapMessage);
mapMessage = obj.MapMessage;
break; break;
} }
} }
listOfAll.Add(content.AllMessage); listOfAll.Add(content.AllMessage);
countList.Clear();
countList.Add(listOfClassroom.Count);
countList.Add(listOfDoor.Count);
countList.Add(listOfChest.Count);
countList.Add(listOfGate.Count);
GetMap(mapMessage);
break; break;
case GameState.GameRunning: case GameState.GameRunning:
foreach (var obj in content.ObjMessage) foreach (var obj in content.ObjMessage)
@@ -509,11 +583,22 @@ namespace Client
listOfHiddenGate.Add(obj.HiddenGateMessage); listOfHiddenGate.Add(obj.HiddenGateMessage);
break; break;
case MessageOfObj.MessageOfObjOneofCase.MapMessage: case MessageOfObj.MessageOfObjOneofCase.MapMessage:
GetMap(obj.MapMessage);
mapMessage = obj.MapMessage;
mapMessageExist = true;//只有中间加入游戏的旁观者着一种可能,使得在这里收到地图
break; break;
} }
} }
listOfAll.Add(content.AllMessage); listOfAll.Add(content.AllMessage);
if (mapMessageExist)
{
countList.Clear();
countList.Add(listOfClassroom.Count);
countList.Add(listOfDoor.Count);
countList.Add(listOfChest.Count);
countList.Add(listOfGate.Count);
GetMap(mapMessage);
mapMessageExist = false;
}
break; break;
case GameState.GameEnd: case GameState.GameEnd:
MessageBox.Show("Game Over!"); MessageBox.Show("Game Over!");
@@ -522,9 +607,17 @@ namespace Client
switch (obj.MessageOfObjCase) switch (obj.MessageOfObjCase)
{ {
case MessageOfObj.MessageOfObjOneofCase.StudentMessage: case MessageOfObj.MessageOfObjOneofCase.StudentMessage:
if (humanOrButcher && obj.StudentMessage.PlayerId == playerID)
{
human = obj.StudentMessage;
}
listOfHuman.Add(obj.StudentMessage); listOfHuman.Add(obj.StudentMessage);
break; break;
case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: case MessageOfObj.MessageOfObjOneofCase.TrickerMessage:
if (!humanOrButcher && obj.TrickerMessage.PlayerId == playerID)
{
butcher = obj.TrickerMessage;
}
listOfButcher.Add(obj.TrickerMessage); listOfButcher.Add(obj.TrickerMessage);
break; break;
case MessageOfObj.MessageOfObjOneofCase.PropMessage: case MessageOfObj.MessageOfObjOneofCase.PropMessage:
@@ -574,94 +667,94 @@ namespace Client
{ {
if (msg.PlayerState == PlayerState.Quit || msg.PlayerState == PlayerState.Graduated) if (msg.PlayerState == PlayerState.Quit || msg.PlayerState == PlayerState.Graduated)
return false; return false;
if (isSpectatorMode || isPlaybackMode)
return true;
if (humanOrButcher && human != null)
{
if (msg.Place == human.Place)
return true;
if (msg.PlayerId == playerID + Preparation.Utility.GameData.numOfPeople)//robot and its owner
return true;
}
else if (!humanOrButcher && butcher != null)
{
if (msg.Place == butcher.Place)
return true;
}
if (msg.Place == Protobuf.PlaceType.Grass)
return false;
//if (isSpectatorMode || isPlaybackMode)
// return true;
//if (humanOrButcher && human != null)
//{
// if (msg.Place == human.Place)
// return true;
// if (msg.PlayerId == playerID + Preparation.Utility.GameData.numOfPeople)//robot and its owner
// return true;
//}
//else if (!humanOrButcher && butcher != null)
//{
// if (msg.Place == butcher.Place)
// return true;
//}
//if (msg.Place == Protobuf.PlaceType.Grass)
// return false;
return true; return true;
} }


private bool CanSee(MessageOfTricker msg) private bool CanSee(MessageOfTricker msg)
{ {
if (isSpectatorMode || isPlaybackMode)
return true;
if (!humanOrButcher && butcher != null)
{
if (butcher.Guid == msg.Guid) // 自己能看见自己
return true;
}
if (humanOrButcher && human != null)
{
if (msg.TrickerType == Protobuf.TrickerType.Assassin)
{
foreach (var buff in msg.Buff)
{
if (buff == Protobuf.TrickerBuffType.TrickerInvisible)
return false;
}
}
if (msg.Place == human.Place)
return true;
}
if (msg.Place == Protobuf.PlaceType.Grass)
return false;
//if (isSpectatorMode || isPlaybackMode)
// return true;
//if (!humanOrButcher && butcher != null)
//{
// if (butcher.Guid == msg.Guid) // 自己能看见自己
// return true;
//}
//if (humanOrButcher && human != null)
//{
// if (msg.TrickerType == Protobuf.TrickerType.Assassin)
// {
// foreach (var buff in msg.Buff)
// {
// if (buff == Protobuf.TrickerBuffType.TrickerInvisible)
// return false;
// }
// }
// if (msg.Place == human.Place)
// return true;
//}
//if (msg.Place == Protobuf.PlaceType.Grass)
// return false;
return true; return true;
} }


private bool CanSee(MessageOfProp msg) private bool CanSee(MessageOfProp msg)
{ {
if (isSpectatorMode || isPlaybackMode)
return true;
if (humanOrButcher && human != null)
{
if (msg.Place == human.Place)
return true;
}
else if (!humanOrButcher && butcher != null)
{
if (msg.Place == butcher.Place)
return true;
}
if (msg.Place == Protobuf.PlaceType.Grass)
return false;
//if (isSpectatorMode || isPlaybackMode)
// return true;
//if (humanOrButcher && human != null)
//{
// if (msg.Place == human.Place)
// return true;
//}
//else if (!humanOrButcher && butcher != null)
//{
// if (msg.Place == butcher.Place)
// return true;
//}
//if (msg.Place == Protobuf.PlaceType.Grass)
// return false;
return true; return true;
} }


private bool CanSee(MessageOfBullet msg) private bool CanSee(MessageOfBullet msg)
{ {
if (isSpectatorMode || isPlaybackMode)
return true;
if (humanOrButcher && human != null)
{
if (msg.Place == human.Place)
return true;
}
else if (!humanOrButcher && butcher != null)
{
if (msg.Place == butcher.Place)
return true;
}
if (msg.Place == Protobuf.PlaceType.Grass)
return false;
//if (isSpectatorMode || isPlaybackMode)
// return true;
//if (humanOrButcher && human != null)
//{
// if (msg.Place == human.Place)
// return true;
//}
//else if (!humanOrButcher && butcher != null)
//{
// if (msg.Place == butcher.Place)
// return true;
//}
//if (msg.Place == Protobuf.PlaceType.Grass)
// return false;
return true; return true;
} }


private bool CanSee(MessageOfBombedBullet msg) private bool CanSee(MessageOfBombedBullet msg)
{ {
if (isSpectatorMode || isPlaybackMode)
return true;
//if (isSpectatorMode || isPlaybackMode)
// return true;
//if (humanOrButcher && human != null) //if (humanOrButcher && human != null)
//{ //{
// if (msg.Place == human.Place) // if (msg.Place == human.Place)
@@ -677,65 +770,109 @@ namespace Client
return true; 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未更新 private void Refresh(object? sender, EventArgs e) //log未更新
{ {
lock (drawPicLock) // 加锁是必要的,画图操作和接收信息操作不能同时进行
try
{ {
// Bonus();
if (WindowState == WindowState.Maximized)
MaxButton.Content = "❐";
else
MaxButton.Content = "🗖";
foreach (var obj in listOfHuman)
lock (drawPicLock) // 加锁是必要的,画图操作和接收信息操作不能同时进行
{ {
if (!isDataFixed[obj.PlayerId] && obj.PlayerId < GameData.numOfStudent && obj.StudentType != StudentType.Robot)
// Bonus();
if (WindowState == WindowState.Maximized)
MaxButton.Content = "❐";
else
MaxButton.Content = "🗖";
foreach (var obj in listOfHuman)
{ {
IStudentType occupation = (IStudentType)OccupationFactory.FindIOccupation(Transformation.ToStudentType(obj.StudentType));
totalLife[obj.PlayerId] = occupation.MaxHp;
totalDeath[obj.PlayerId] = occupation.MaxGamingAddiction;
int i = 0;
foreach (var skill in occupation.ListOfIActiveSkill)
if (obj.PlayerId < GameData.numOfStudent && !isDataFixed[obj.PlayerId])
{ {
var iActiveSkill = SkillFactory.FindIActiveSkill(skill);
coolTime[i, obj.PlayerId] = iActiveSkill.SkillCD;
++i;
IStudentType occupation = (IStudentType)OccupationFactory.FindIOccupation(Transformation.ToStudentType(obj.StudentType));
totalLife[obj.PlayerId] = occupation.MaxHp;
totalDeath[obj.PlayerId] = occupation.MaxGamingAddiction;
int i = 0;
foreach (var skill in occupation.ListOfIActiveSkill)
{
var iActiveSkill = SkillFactory.FindActiveSkill(skill);
coolTime[i, obj.PlayerId] = iActiveSkill.SkillCD;
++i;
}
isDataFixed[obj.PlayerId] = true;
} }
isDataFixed[obj.PlayerId] = true;
} }
}
foreach (var obj in listOfButcher)
{
if (!isDataFixed[obj.PlayerId])
foreach (var obj in listOfButcher)
{ {
IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType));
int j = 0;
foreach (var skill in occupation1.ListOfIActiveSkill)
if (!isDataFixed[obj.PlayerId])
{ {
var iActiveSkill = SkillFactory.FindIActiveSkill(skill);
coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD;
++j;
IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType));
int j = 0;
foreach (var skill in occupation1.ListOfIActiveSkill)
{
var iActiveSkill = SkillFactory.FindActiveSkill(skill);
coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD;
++j;
}
isDataFixed[obj.PlayerId] = true;
} }
isDataFixed[obj.PlayerId] = true;
} }
}


for (int i = 0; i < GameData.numOfStudent; i++)
{
StatusBarsOfSurvivor[i].NewData(totalLife, totalDeath, coolTime);
}
for (int i = 0; i < GameData.numOfStudent; i++)
{
StatusBarsOfSurvivor[i].NewData(totalLife, totalDeath, coolTime);
}


StatusBarsOfHunter.NewData(totalLife, totalDeath, coolTime);
StatusBarsOfHunter.NewData(totalLife, totalDeath, coolTime);


for (int i = 0; i < GameData.numOfStudent; i++)
{
StatusBarsOfSurvivor[i].SetFontSize(12 * unitFontsize);
}
for (int i = 0; i < GameData.numOfStudent; i++)
{
StatusBarsOfSurvivor[i].SetFontSize(12 * unitFontsize);
}


StatusBarsOfHunter.SetFontSize(12 * unitFontsize);
StatusBarsOfCircumstance.SetFontSize(12 * unitFontsize);
if (!isClientStocked)
{
try
StatusBarsOfHunter.SetFontSize(12 * unitFontsize);
StatusBarsOfCircumstance.SetFontSize(12 * unitFontsize);
if (!isClientStocked)
{ {
UpperLayerOfMap.Children.Clear(); UpperLayerOfMap.Children.Clear();
foreach (var data in listOfAll) foreach (var data in listOfAll)
@@ -748,7 +885,7 @@ namespace Client
} }
foreach (var data in listOfHuman) foreach (var data in listOfHuman)
{ {
if (data.StudentType != StudentType.Robot)
if (data.PlayerId < GameData.numOfStudent)
StatusBarsOfSurvivor[data.PlayerId].SetValue(data, data.PlayerId); StatusBarsOfSurvivor[data.PlayerId].SetValue(data, data.PlayerId);
if (CanSee(data)) if (CanSee(data))
{ {
@@ -763,22 +900,21 @@ namespace Client
}; };
if (data.StudentType == StudentType.Robot) if (data.StudentType == StudentType.Robot)
icon.Fill = Brushes.Gray; icon.Fill = Brushes.Gray;
TextBox num = new()
TextBlock num = new()
{ {
FontSize = 7 * unitFontsize, FontSize = 7 * unitFontsize,
Width = 2 * radiusTimes * unitWidth, Width = 2 * radiusTimes * unitWidth,
Height = 2 * radiusTimes * unitHeight, Height = 2 * radiusTimes * unitHeight,
Text = Convert.ToString(data.PlayerId), Text = Convert.ToString(data.PlayerId),
TextAlignment = TextAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Left, HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top, VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * radiusTimes, data.X * unitHeight / 1000.0 - unitHeight * radiusTimes, 0, 0), Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * radiusTimes, data.X * unitHeight / 1000.0 - unitHeight * radiusTimes, 0, 0),
Background = Brushes.Transparent, Background = Brushes.Transparent,
BorderBrush = Brushes.Transparent,
IsReadOnly = true,
Foreground = Brushes.White, 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(icon);
UpperLayerOfMap.Children.Add(num); UpperLayerOfMap.Children.Add(num);
} }
@@ -830,6 +966,9 @@ namespace Client
case Protobuf.PropType.RecoveryFromDizziness: case Protobuf.PropType.RecoveryFromDizziness:
DrawProp(data, "🕶"); DrawProp(data, "🕶");
break; break;
case Protobuf.PropType.CraftingBench:
DrawProp(data, "🎰");
break;
default: default:
DrawProp(data, ""); DrawProp(data, "");
break; break;
@@ -857,6 +996,7 @@ namespace Client
case Protobuf.BulletType.CommonAttackOfTricker: case Protobuf.BulletType.CommonAttackOfTricker:
case Protobuf.BulletType.BombBomb: case Protobuf.BulletType.BombBomb:
case Protobuf.BulletType.JumpyDumpty: case Protobuf.BulletType.JumpyDumpty:
case Protobuf.BulletType.Strike:
icon.Fill = Brushes.Red; icon.Fill = Brushes.Red;
break; break;
default: default:
@@ -901,12 +1041,6 @@ namespace Client
UpperLayerOfMap.Children.Add(icon); UpperLayerOfMap.Children.Add(icon);
break; 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: default:
break; break;
} }
@@ -915,93 +1049,65 @@ namespace Client
foreach (var data in listOfClassroom) foreach (var data in listOfClassroom)
{ {
int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfFixedGenerator); 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) if (deg == 100)
{ {
icon.Text = "A+";
classroomArray[idx].Text = "A+";
} }
UpperLayerOfMap.Children.Add(icon);
UpperLayerOfMap.Children.Add(classroomArray[idx]);
} }
foreach (var data in listOfChest) foreach (var data in listOfChest)
{ {
int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfOpenedChest); 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) if (deg == 100)
{ {
icon.Text = "Ø";
chestArray[idx].Text = "Ø";
} }
UpperLayerOfMap.Children.Add(icon);
UpperLayerOfMap.Children.Add(chestArray[idx]);
} }
foreach (var data in listOfGate) foreach (var data in listOfGate)
{ {
int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfOpenedDoorway); 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) if (deg == 100)
{ {
gateOpened = true; gateOpened = true;
icon.Text = "🔓";
gateArray[idx].Text = "🔓";
} }
UpperLayerOfMap.Children.Add(icon);
UpperLayerOfMap.Children.Add(gateArray[idx]);
} }
foreach (var data in listOfDoor) 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) if (data.IsOpen)
{ {
icon.Text = Convert.ToString("🔓");
doorArray[idx].Text = Convert.ToString("🔓");
} }
else else
{ {
icon.Text = Convert.ToString("🔒");
doorArray[idx].Text = Convert.ToString("🔒");
} }
UpperLayerOfMap.Children.Add(icon);
UpperLayerOfMap.Children.Add(doorArray[idx]);
} }
foreach (var data in listOfHiddenGate) foreach (var data in listOfHiddenGate)
{ {
@@ -1014,32 +1120,31 @@ namespace Client
if (data.Opened) if (data.Opened)
{ {
isEmergencyOpened = true; isEmergencyOpened = true;
TextBox icon = new()
hiddenGateArray = new TextBlock()
{ {
FontSize = 9 * unitFontsize, FontSize = 9 * unitFontsize,
Width = unitWidth, Width = unitWidth,
Height = unitHeight, Height = unitHeight,
Text = Convert.ToString("🔓"), Text = Convert.ToString("🔓"),
TextAlignment = TextAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Left, HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top, VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0), Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0),
Background = Brushes.Transparent, Background = Brushes.Transparent,
BorderBrush = Brushes.Transparent,
IsReadOnly = true
}; };
UpperLayerOfMap.Children.Add(icon);
UpperLayerOfMap.Children.Add(hiddenGateArray);
} }
} }
} }
catch (Exception exc)
{
ErrorDisplayer error = new("Error: " + exc.ToString());
error.Show();
isClientStocked = true;
PorC.Content = "▶";
}
counter++;
} }
counter++;
}
catch (Exception exc)
{
ErrorDisplayer error = new("Error: " + exc.ToString());
error.Show();
isClientStocked = true;
PorC.Content = "▶";
} }
} }


@@ -1204,6 +1309,7 @@ namespace Client
{ {
PlayerId = playerID, PlayerId = playerID,
SkillId = 0, SkillId = 0,
SkillParam = 0,
}; };
client.UseSkill(msgB); client.UseSkill(msgB);
break; break;
@@ -1416,6 +1522,18 @@ namespace Client
private List<MessageOfDoor> listOfDoor; private List<MessageOfDoor> listOfDoor;
private List<MessageOfGate> listOfGate; private List<MessageOfGate> listOfGate;
private List<MessageOfHiddenGate> listOfHiddenGate; 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 object drawPicLock = new object();
private MessageOfStudent? human = null; private MessageOfStudent? human = null;
private MessageOfTricker? butcher = null; private MessageOfTricker? butcher = null;
@@ -1489,4 +1607,4 @@ namespace Client
private int[] totalLife = new int[4] { 100, 100, 100, 100 }, totalDeath = new int[4] { 100, 100, 100, 100 }; 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 } }; 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, 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<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) if (Reader == null)
return null; return null;
@@ -158,6 +158,10 @@ namespace Client
break; break;
} }
} }
countList.Add(listOfClassroom.Count);
countList.Add(listOfDoor.Count);
countList.Add(listOfChest.Count);
countList.Add(listOfGate.Count);
listOfAll.Add(content.AllMessage); listOfAll.Add(content.AllMessage);
break; break;
case GameState.GameRunning: case GameState.GameRunning:
@@ -270,4 +274,4 @@ namespace Client
return map; return map;
} }
} }
}
}

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

@@ -2,7 +2,7 @@
"profiles": { "profiles": {
"Client": { "Client": {
"commandName": "Project", "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: case Protobuf.PropType.RecoveryFromDizziness:
prop0.Text = "🕶"; prop0.Text = "🕶";
break; break;
case Protobuf.PropType.CraftingBench:
prop0.Text = "🎰";
break;
default: default:
prop0.Text = ""; prop0.Text = "";
break; break;
@@ -202,6 +205,9 @@ namespace Client
case Protobuf.PropType.RecoveryFromDizziness: case Protobuf.PropType.RecoveryFromDizziness:
prop1.Text = "🕶"; prop1.Text = "🕶";
break; break;
case Protobuf.PropType.CraftingBench:
prop1.Text = "🎰";
break;
default: default:
prop1.Text = ""; prop1.Text = "";
break; break;
@@ -235,6 +241,9 @@ namespace Client
case Protobuf.PropType.RecoveryFromDizziness: case Protobuf.PropType.RecoveryFromDizziness:
prop2.Text = "🕶"; prop2.Text = "🕶";
break; break;
case Protobuf.PropType.CraftingBench:
prop2.Text = "🎰";
break;
default: default:
prop2.Text = ""; prop2.Text = "";
break; break;


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

@@ -174,6 +174,9 @@ namespace Client
case Protobuf.PropType.RecoveryFromDizziness: case Protobuf.PropType.RecoveryFromDizziness:
prop0.Text = "🕶"; prop0.Text = "🕶";
break; break;
case Protobuf.PropType.CraftingBench:
prop0.Text = "🎰";
break;
default: default:
prop0.Text = ""; prop0.Text = "";
break; break;
@@ -207,6 +210,9 @@ namespace Client
case Protobuf.PropType.RecoveryFromDizziness: case Protobuf.PropType.RecoveryFromDizziness:
prop1.Text = "🕶"; prop1.Text = "🕶";
break; break;
case Protobuf.PropType.CraftingBench:
prop1.Text = "🎰";
break;
default: default:
prop1.Text = ""; prop1.Text = "";
break; break;
@@ -240,6 +246,9 @@ namespace Client
case Protobuf.PropType.RecoveryFromDizziness: case Protobuf.PropType.RecoveryFromDizziness:
prop2.Text = "🕶"; prop2.Text = "🕶";
break; break;
case Protobuf.PropType.CraftingBench:
prop2.Text = "🎰";
break;
default: default:
prop2.Text = ""; prop2.Text = "";
break; break;


+ 1
- 1
logic/ClientTest/ClientTest.csproj View File

@@ -8,7 +8,7 @@
</PropertyGroup> </PropertyGroup>


<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.22.3" />
<PackageReference Include="Google.Protobuf" Version="3.22.4" />
<PackageReference Include="Grpc" Version="2.46.6" /> <PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Core" Version="2.46.6" /> <PackageReference Include="Grpc.Core" Version="2.46.6" />
</ItemGroup> </ItemGroup>


+ 2
- 3
logic/GameClass/GameObj/Bullet/BombedBullet.cs View File

@@ -3,7 +3,7 @@
namespace GameClass.GameObj namespace GameClass.GameObj
{ {
// 为方便界面组做子弹爆炸特效,现引入“爆炸中的子弹”,在每帧发送给界面组 // 为方便界面组做子弹爆炸特效,现引入“爆炸中的子弹”,在每帧发送给界面组
public sealed class BombedBullet : GameObj
public sealed class BombedBullet : Immovable
{ {
public override ShapeType Shape => ShapeType.Circle; public override ShapeType Shape => ShapeType.Circle;
public override bool IsRigid => false; public override bool IsRigid => false;
@@ -13,10 +13,9 @@ namespace GameClass.GameObj
public BombedBullet(Bullet bullet) : public BombedBullet(Bullet bullet) :
base(bullet.Position, bullet.Radius, GameObjType.BombedBullet) base(bullet.Position, bullet.Radius, GameObjType.BombedBullet)
{ {
this.place = bullet.Place;
this.bulletHasBombed = bullet; this.bulletHasBombed = bullet;
this.MappingID = bullet.ID; this.MappingID = bullet.ID;
this.FacingDirection = bullet.FacingDirection;
this.facingDirection = bullet.FacingDirection;
} }
} }
} }

+ 60
- 55
logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs View File

@@ -5,26 +5,17 @@ namespace GameClass.GameObj
{ {
internal sealed class CommonAttackOfGhost : Bullet internal sealed class CommonAttackOfGhost : Bullet
{ {
public CommonAttackOfGhost(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
public CommonAttackOfGhost(Character player, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, pos)
{ {
ap = GameData.basicApOfGhost;
} }
public override double BulletBombRange => 0; public override double BulletBombRange => 0;
public override double BulletAttackRange => GameData.basicAttackShortRange;
public int ap = GameData.basicApOfGhost;
public override int AP
{
get => ap;
set
{
lock (gameObjLock)
ap = value;
}
}
public override double AttackDistance => GameData.basicAttackShortRange;
public override int Speed => GameData.basicBulletMoveSpeed; public override int Speed => GameData.basicBulletMoveSpeed;
public override bool IsRemoteAttack => false; public override bool IsRemoteAttack => false;


public override int CastTime => (int)BulletAttackRange * 1000 / Speed;
public override int CastTime => (int)AttackDistance * 1000 / Speed;
public override int Backswing => GameData.basicBackswing; public override int Backswing => GameData.basicBackswing;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit; public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicBackswing; public const int cd = GameData.basicBackswing;
@@ -41,7 +32,6 @@ namespace GameClass.GameObj
switch (gameObjType) switch (gameObjType)
{ {
case GameObjType.Character: case GameObjType.Character:
case GameObjType.Generator:
return true; return true;
default: default:
return false; return false;
@@ -50,31 +40,62 @@ namespace GameClass.GameObj
public override BulletType TypeOfBullet => BulletType.CommonAttackOfGhost; public override BulletType TypeOfBullet => BulletType.CommonAttackOfGhost;
} }


internal sealed class FlyingKnife : Bullet
internal sealed class Strike : Bullet
{ {
public FlyingKnife(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
public Strike(Character player, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, pos)
{ {
ap = GameData.basicApOfGhost * 16 / 15;
} }
public override double BulletBombRange => 0; public override double BulletBombRange => 0;
public override double BulletAttackRange => GameData.basicRemoteAttackRange * 13;
public int ap = GameData.basicApOfGhost * 4 / 5;
public override int AP
public override double AttackDistance => GameData.basicAttackShortRange * 20 / 22;
public override int Speed => GameData.basicBulletMoveSpeed * 625 / 740;
public override bool IsRemoteAttack => false;

public override int CastTime => (int)AttackDistance * 1000 / Speed;
public override int Backswing => GameData.basicBackswing;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicBackswing;
public override int CD => cd;
public const int maxBulletNum = 1;
public override int MaxBulletNum => maxBulletNum;

public override bool CanAttack(GameObj target)
{ {
get => ap;
set
return false;
}
public override bool CanBeBombed(GameObjType gameObjType)
{
switch (gameObjType)
{ {
lock (gameObjLock)
ap = value;
case GameObjType.Character:
case GameObjType.Generator:
return true;
default:
return false;
} }
} }
public override BulletType TypeOfBullet => BulletType.Strike;
}

internal sealed class FlyingKnife : Bullet
{
public FlyingKnife(Character player, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, pos)
{
ap = GameData.basicApOfGhost * 4 / 5;
}
public override double BulletBombRange => 0;
public override double AttackDistance => GameData.basicRemoteAttackRange * 13;

public override int Speed => GameData.basicBulletMoveSpeed * 25 / 10; public override int Speed => GameData.basicBulletMoveSpeed * 25 / 10;
public override bool IsRemoteAttack => true; 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 Backswing => 0;
public override int RecoveryFromHit => 0; public override int RecoveryFromHit => 0;
public const int cd = GameData.basicBackswing / 2;
public const int cd = castTime;
public override int CD => cd; public override int CD => cd;
public const int maxBulletNum = 1; public const int maxBulletNum = 1;
public override int MaxBulletNum => maxBulletNum; public override int MaxBulletNum => maxBulletNum;
@@ -100,27 +121,18 @@ namespace GameClass.GameObj


internal sealed class BombBomb : Bullet internal sealed class BombBomb : Bullet
{ {
public BombBomb(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
public BombBomb(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos)
{ {
ap = (int)(GameData.basicApOfGhost * 6.0 / 5);
} }
public override double BulletBombRange => GameData.basicBulletBombRange; public override double BulletBombRange => GameData.basicBulletBombRange;
public override double BulletAttackRange => GameData.basicAttackShortRange;
public int ap = (int)(GameData.basicApOfGhost * 6.0 / 5);
public override int AP
{
get => ap;
set
{
lock (gameObjLock)
ap = value;
}
}
public override double AttackDistance => GameData.basicAttackShortRange;

public override int Speed => (int)(GameData.basicBulletMoveSpeed * 30 / 37); public override int Speed => (int)(GameData.basicBulletMoveSpeed * 30 / 37);
public override bool IsRemoteAttack => false; public override bool IsRemoteAttack => false;


public override int CastTime => (int)(BulletAttackRange * 1000 / Speed);
public override int Backswing => GameData.basicRecoveryFromHit;
public override int CastTime => (int)(AttackDistance * 1000 / Speed);
public override int Backswing => GameData.basicBackswing * 3 / 2;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit; public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicCD; public const int cd = GameData.basicCD;
public override int CD => cd; public override int CD => cd;
@@ -136,6 +148,8 @@ namespace GameClass.GameObj
switch (gameObjType) switch (gameObjType)
{ {
case GameObjType.Character: case GameObjType.Character:
case GameObjType.Generator:
case GameObjType.Door:
return true; return true;
default: default:
return false; return false;
@@ -146,22 +160,13 @@ namespace GameClass.GameObj
} }
internal sealed class JumpyDumpty : Bullet internal sealed class JumpyDumpty : Bullet
{ {
public JumpyDumpty(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
public JumpyDumpty(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos)
{ {
ap = (int)(GameData.basicApOfGhost * 0.6);
} }
public override double BulletBombRange => GameData.basicBulletBombRange / 2; public override double BulletBombRange => GameData.basicBulletBombRange / 2;
public override double BulletAttackRange => GameData.basicAttackShortRange * 2;
public int ap = (int)(GameData.basicApOfGhost * 0.6);
public override int AP
{
get => ap;
set
{
lock (gameObjLock)
ap = value;
}
}
public override double AttackDistance => GameData.basicAttackShortRange * 16 / 22;

public override int Speed => (int)(GameData.basicBulletMoveSpeed * 43 / 37); public override int Speed => (int)(GameData.basicBulletMoveSpeed * 43 / 37);
public override bool IsRemoteAttack => false; public override bool IsRemoteAttack => false;




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

@@ -9,7 +9,7 @@ namespace GameClass.GameObj
{ {
} }
public override double BulletBombRange => 0; public override double BulletBombRange => 0;
public override double BulletAttackRange => 0;
public override double AttackDistance => 0;
public override int AP => 7220; public override int AP => 7220;
public override int Speed => 0; public override int Speed => 0;
public override bool IsRemoteAttack => false; public override bool IsRemoteAttack => false;


+ 29
- 16
logic/GameClass/GameObj/Bullet/Bullet.cs View File

@@ -1,6 +1,6 @@
using Preparation.Interface; using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System;
using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
@@ -10,8 +10,17 @@ namespace GameClass.GameObj
/// //攻击力 /// //攻击力
/// </summary> /// </summary>
public abstract double BulletBombRange { get; } public abstract double BulletBombRange { get; }
public abstract double BulletAttackRange { get; }
public abstract int AP { get; set; }
public abstract double AttackDistance { get; }
protected int ap;
public int AP
{
get => Interlocked.CompareExchange(ref ap, 0, 0);
}
public void AddAP(int addAp)
{
Interlocked.Add(ref ap, addAp);
}

public abstract int Speed { get; } public abstract int Speed { get; }
public abstract bool IsRemoteAttack { get; } public abstract bool IsRemoteAttack { get; }
public abstract int CastTime { get; } public abstract int CastTime { get; }
@@ -27,7 +36,6 @@ namespace GameClass.GameObj
public bool HasSpear => hasSpear; public bool HasSpear => hasSpear;


/// <summary> /// <summary>
/// 与THUAI4不同的一个攻击判定方案,通过这个函数判断爆炸时能否伤害到target
/// </summary> /// </summary>
/// <param name="target">被尝试攻击者</param> /// <param name="target">被尝试攻击者</param>
/// <returns>是否可以攻击到</returns> /// <returns>是否可以攻击到</returns>
@@ -36,17 +44,16 @@ namespace GameClass.GameObj


public override bool IgnoreCollideExecutor(IGameObj targetObj) public override bool IgnoreCollideExecutor(IGameObj targetObj)
{ {
if (targetObj == Parent && CanMove) return true;
if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet)
if (targetObj == Parent) return true;
if (targetObj.Type == GameObjType.Gadget || targetObj.Type == GameObjType.Bullet)
return true; return true;
return false; return false;
} }
public Bullet(Character player, int radius, PlaceType placeType, XY Position) :
public Bullet(Character player, int radius, XY Position) :
base(Position, radius, GameObjType.Bullet) base(Position, radius, GameObjType.Bullet)
{ {
this.place = placeType;
this.CanMove = true;
this.moveSpeed = this.Speed;
this.ReSetCanMove(true);
this.MoveSpeed = this.Speed;
this.hasSpear = player.TryUseSpear(); this.hasSpear = player.TryUseSpear();
this.Parent = player; this.Parent = player;
} }
@@ -57,18 +64,20 @@ namespace GameClass.GameObj


public static class BulletFactory public static class BulletFactory
{ {
public static Bullet? GetBullet(Character character, PlaceType place, XY pos)
public static Bullet? GetBullet(Character character, XY pos, BulletType bulletType)
{ {
switch (character.BulletOfPlayer)
switch (bulletType)
{ {
case BulletType.FlyingKnife: case BulletType.FlyingKnife:
return new FlyingKnife(character, place, pos);
return new FlyingKnife(character, pos);
case BulletType.CommonAttackOfGhost: case BulletType.CommonAttackOfGhost:
return new CommonAttackOfGhost(character, place, pos);
return new CommonAttackOfGhost(character, pos);
case BulletType.JumpyDumpty: case BulletType.JumpyDumpty:
return new JumpyDumpty(character, place, pos);
return new JumpyDumpty(character, pos);
case BulletType.BombBomb: case BulletType.BombBomb:
return new BombBomb(character, place, pos);
return new BombBomb(character, pos);
case BulletType.Strike:
return new Strike(character, pos);
default: default:
return null; return null;
} }
@@ -94,6 +103,8 @@ namespace GameClass.GameObj
return BombBomb.cd; return BombBomb.cd;
case BulletType.JumpyDumpty: case BulletType.JumpyDumpty:
return JumpyDumpty.cd; return JumpyDumpty.cd;
case BulletType.Strike:
return Strike.cd;
default: default:
return GameData.basicCD; return GameData.basicCD;
} }
@@ -110,6 +121,8 @@ namespace GameClass.GameObj
return BombBomb.maxBulletNum; return BombBomb.maxBulletNum;
case BulletType.JumpyDumpty: case BulletType.JumpyDumpty:
return JumpyDumpty.maxBulletNum; return JumpyDumpty.maxBulletNum;
case BulletType.Strike:
return Strike.maxBulletNum;
default: default:
return 0; return 0;
} }


+ 13
- 0
logic/GameClass/GameObj/Character/Character.BuffManager.cs View File

@@ -197,6 +197,19 @@ namespace GameClass.GameObj
return false; return false;
} }


public bool TryDeleteInvisible()
{
if (HasInvisible)
{
lock (buffListLock[(int)BuffType.Invisible])
{
buffList[(int)BuffType.Invisible].RemoveFirst();
}
return true;
}
return false;
}

public void AddClairaudience(int shieldTime) => AddBuff(new BuffValue(), shieldTime, BuffType.Clairaudience, () => public void AddClairaudience(int shieldTime) => AddBuff(new BuffValue(), shieldTime, BuffType.Clairaudience, () =>
{ }); { });
public bool HasClairaudience public bool HasClairaudience


+ 0
- 5
logic/GameClass/GameObj/Character/Character.Ghost.cs View File

@@ -1,10 +1,5 @@
using Preparation.Interface; using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {


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

@@ -1,8 +1,6 @@
using Preparation.Utility; using Preparation.Utility;
using Preparation.Interface; using Preparation.Interface;
using System.Collections.Generic; using System.Collections.Generic;
using System;
using System.Numerics;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
@@ -13,37 +11,32 @@ namespace GameClass.GameObj
private readonly IOccupation occupation; private readonly IOccupation occupation;
public IOccupation Occupation => occupation; public IOccupation Occupation => occupation;


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


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

public IActiveSkill FindIActiveSkill(ActiveSkillType activeSkillType)
public ActiveSkill FindActiveSkill(ActiveSkillType activeSkillType)
{ {
if (Occupation.ListOfIActiveSkill.Contains(activeSkillType)) if (Occupation.ListOfIActiveSkill.Contains(activeSkillType))
{ {
return IActiveSkillDictionary[activeSkillType];
return ActiveSkillDictionary[activeSkillType];
} }
return new NullSkill(); return new NullSkill();
} }


public bool SetTimeUntilActiveSkillAvailable(ActiveSkillType activeSkillType, int timeUntilActiveSkillAvailable) public bool SetTimeUntilActiveSkillAvailable(ActiveSkillType activeSkillType, int timeUntilActiveSkillAvailable)
{ {
if (TimeUntilActiveSkillAvailable.ContainsKey(activeSkillType))
if (Occupation.ListOfIActiveSkill.Contains(activeSkillType))
{ {
lock (gameObjLock)
this.timeUntilActiveSkillAvailable[activeSkillType] = (timeUntilActiveSkillAvailable > 0) ? timeUntilActiveSkillAvailable : 0;
ActiveSkillDictionary[activeSkillType].TimeUntilActiveSkillAvailable = (timeUntilActiveSkillAvailable > 0) ? timeUntilActiveSkillAvailable : 0;
return true; return true;
} }
return false; return false;
} }
public bool AddTimeUntilActiveSkillAvailable(ActiveSkillType activeSkillType, int addTimeUntilActiveSkillAvailable) public bool AddTimeUntilActiveSkillAvailable(ActiveSkillType activeSkillType, int addTimeUntilActiveSkillAvailable)
{ {
if (TimeUntilActiveSkillAvailable.ContainsKey(activeSkillType))
if (Occupation.ListOfIActiveSkill.Contains(activeSkillType))
{ {
lock (gameObjLock)
this.timeUntilActiveSkillAvailable[activeSkillType] = (timeUntilActiveSkillAvailable[activeSkillType] + addTimeUntilActiveSkillAvailable > 0) ? timeUntilActiveSkillAvailable[activeSkillType] + addTimeUntilActiveSkillAvailable : 0;
ActiveSkillDictionary[activeSkillType].TimeUntilActiveSkillAvailable += addTimeUntilActiveSkillAvailable;
return true; return true;
} }
return false; return false;
@@ -57,13 +50,12 @@ namespace GameClass.GameObj
protected Character(XY initPos, int initRadius, CharacterType characterType) : protected Character(XY initPos, int initRadius, CharacterType characterType) :
base(initPos, initRadius, GameObjType.Character) base(initPos, initRadius, GameObjType.Character)
{ {
this.place = PlaceType.Null;
this.CanMove = true;
this.ReSetCanMove(true);
this.score = 0; this.score = 0;
this.buffManager = new BuffManager(); this.buffManager = new BuffManager();
this.occupation = OccupationFactory.FindIOccupation(characterType); this.occupation = OccupationFactory.FindIOccupation(characterType);
this.MaxHp = this.hp = Occupation.MaxHp; 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.BulletOfPlayer = this.OriBulletOfPlayer = Occupation.InitBullet;
this.concealment = Occupation.Concealment; this.concealment = Occupation.Concealment;
this.alertnessRadius = Occupation.AlertnessRadius; this.alertnessRadius = Occupation.AlertnessRadius;
@@ -75,12 +67,10 @@ namespace GameClass.GameObj


foreach (var activeSkill in this.Occupation.ListOfIActiveSkill) foreach (var activeSkill in this.Occupation.ListOfIActiveSkill)
{ {
this.IActiveSkillDictionary.Add(activeSkill, SkillFactory.FindIActiveSkill(activeSkill));
this.TimeUntilActiveSkillAvailable.Add(activeSkill, 0);
this.ActiveSkillDictionary.Add(activeSkill, SkillFactory.FindActiveSkill(activeSkill));
} }


// UsePassiveSkill(); //创建player时开始被动技能,这一过程也可以放到gamestart时进行
// 这可以放在AddPlayer中做
// UsePassiveSkill(); //这一过程放到gamestart时进行


Debugger.Output(this, "constructed!"); Debugger.Output(this, "constructed!");
} }


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

@@ -141,7 +141,7 @@ namespace GameClass.GameObj
} }
} }
} }
public override void AddScore(int add)
public override void AddScore(long add)
{ {
if (parent == null) if (parent == null)
base.AddScore(add); base.AddScore(add);


+ 439
- 179
logic/GameClass/GameObj/Character/Character.cs View File

@@ -2,100 +2,116 @@
using Preparation.Utility; using Preparation.Utility;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了
{ {
#region 装弹、攻击相关的基本属性及方法


protected readonly object beAttackedLock = new();
private readonly ReaderWriterLockSlim hpReaderWriterLock = new();
public ReaderWriterLockSlim HPReadWriterLock => hpReaderWriterLock;
private readonly object vampireLock = new();
public object VampireLock => vampire;
private readonly object inventoryLock = new();
public object InventoryLock => inventoryLock;


#region 装弹、攻击相关的基本属性及方法
/// <summary> /// <summary>
/// 装弹冷却 /// 装弹冷却
/// </summary> /// </summary>
protected int cd; protected int cd;
public int CD public int CD
{ {
get => cd;
private set
get
{ {
lock (gameObjLock)
lock (actionLock)
{ {
cd = value;
Debugger.Output(this, string.Format("'s CD has been set to: {0}.", value));
return cd;
} }
} }
} }
public int OrgCD { get; protected set; } public int OrgCD { get; protected set; }


protected int maxBulletNum;
public int MaxBulletNum => maxBulletNum; // 人物最大子弹数
protected int bulletNum;
public int BulletNum => bulletNum; // 目前持有的子弹数

public readonly BulletType OriBulletOfPlayer; public readonly BulletType OriBulletOfPlayer;
private BulletType bulletOfPlayer; private BulletType bulletOfPlayer;
public BulletType BulletOfPlayer public BulletType BulletOfPlayer
{ {
get => bulletOfPlayer;
get
{
lock (actionLock)
{
return bulletOfPlayer;
}
}
set set
{ {
lock (gameObjLock)
lock (actionLock)
{ {
bulletOfPlayer = value; bulletOfPlayer = value;
OrgCD = (BulletFactory.BulletCD(value));
CD = 0;
cd = OrgCD = (BulletFactory.BulletCD(value));
Debugger.Output(this, string.Format("'s CD has been set to: {0}.", cd));
maxBulletNum = bulletNum = (BulletFactory.BulletNum(value)); maxBulletNum = bulletNum = (BulletFactory.BulletNum(value));
} }
} }
} }


/// <summary>
/// 进行一次攻击
/// </summary>
/// <returns>攻击操作发出的子弹</returns>
public Bullet? Attack(XY pos, PlaceType place)
protected int maxBulletNum;
public int MaxBulletNum
{ {
if (TrySubBulletNum())
return BulletFactory.GetBullet(this, place, pos);
else
return null;
get
{
lock (actionLock)
{
return maxBulletNum;
}
}
} }
private int bulletNum;
private int updateTimeOfBulletNum = 0;


/// <summary>
/// 尝试将子弹数量减1
/// </summary>
/// <returns>减操作是否成功</returns>
private bool TrySubBulletNum()
public int UpdateBulletNum(int time)
{ {
lock (gameObjLock)
lock (actionLock)
{ {
if (bulletNum > 0)
if (bulletNum < maxBulletNum)
{ {
--bulletNum;
return true;
int add = Math.Min(maxBulletNum - bulletNum, (time - updateTimeOfBulletNum) / cd);
updateTimeOfBulletNum += add * cd;
return (bulletNum += add);
} }
return false;
return maxBulletNum;
} }
} }

/// <summary> /// <summary>
/// 尝试将子弹数量加1
/// 进行一次攻击
/// </summary> /// </summary>
/// <returns>加操作是否成功</returns>
public bool TryAddBulletNum()
/// <returns>攻击操作发出的子弹</returns>
public Bullet? Attack(double angle, int time)
{ {
lock (gameObjLock)
lock (actionLock)
{ {
if (bulletNum < maxBulletNum)
if (bulletOfPlayer == BulletType.Null)
return null;
if (UpdateBulletNum(time) > 0)
{ {
++bulletNum;
return true;
if (bulletNum == maxBulletNum) updateTimeOfBulletNum = time;
--bulletNum;

XY res = Position + new XY // 子弹紧贴人物生成。
(
(int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)),
(int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle))
);
Bullet? bullet = BulletFactory.GetBullet(this, res, this.bulletOfPlayer);
if (bullet == null) return null;
if (TryAddAp()) bullet.AddAP(GameData.ApPropAdd);
facingDirection = new(angle, bullet.AttackDistance);
return bullet;
} }
return false;
else
return null;
} }
} }


@@ -224,21 +240,66 @@ namespace GameClass.GameObj
} }
#endregion #endregion
#region 血量相关的基本属性及方法 #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;
if (hp > maxHp) hp = maxHp;
}
finally
{
HPReadWriterLock.ExitWriteLock();
}
}
} // 最大血量
protected int hp; protected int hp;
public int HP public int HP
{ {
get => hp;
set
get
{ {
if (value > 0)
HPReadWriterLock.EnterReadLock();
try
{ {
lock (gameObjLock)
hp = value <= MaxHp ? value : MaxHp;
return hp;
} }
else
lock (gameObjLock)
finally
{
HPReadWriterLock.ExitReadLock();
}
}
set
{
HPReadWriterLock.EnterWriteLock();
try
{
if (value > 0)
{
hp = value <= maxHp ? value : maxHp;
}
else
hp = 0; hp = 0;
}
finally
{
HPReadWriterLock.ExitWriteLock();
}
} }
} }


@@ -246,50 +307,51 @@ namespace GameClass.GameObj
/// 尝试减血 /// 尝试减血
/// </summary> /// </summary>
/// <param name="sub">减血量</param> /// <param name="sub">减血量</param>
/// <returns>减操作是否成功</returns>
public int TrySubHp(int sub) 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之间 private double vampire = 0; // 回血率:0-1之间
public double Vampire 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;
lock (vampireLock)
return vampire;
} }
}
private double oriVampire = 0;
public double OriVampire
{
get => oriVampire;
set set
{ {
if (value > 1)
lock (gameObjLock)
lock (vampireLock)
{
if (value > 1)
vampire = 1; vampire = 1;
else if (value < 0)
lock (gameObjLock)
else if (value < 0)
vampire = 0; vampire = 0;
else
lock (gameObjLock)
else
vampire = value; vampire = value;
}
} }
} }
public double OriVampire { get; protected set; }
#endregion #endregion
#region 状态相关的基本属性与方法 #region 状态相关的基本属性与方法
private PlayerStateType playerState = PlayerStateType.Null; private PlayerStateType playerState = PlayerStateType.Null;
@@ -297,140 +359,295 @@ namespace GameClass.GameObj
{ {
get get
{ {
if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving;
return playerState;
lock (actionLock)
{
if (playerState == PlayerStateType.Moving)
return (IsMoving) ? PlayerStateType.Moving : PlayerStateType.Null;
return playerState;
}
} }
} }


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

public bool NoHp()
{
lock (actionLock)
return (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued);
}
public bool Commandable()
{
lock (actionLock)
{
return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued
&& playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack
&& playerState != PlayerStateType.ClimbingThroughWindows
&& playerState != PlayerStateType.Stunned && playerState != PlayerStateType.Charmed);
}
}
public bool CanPinDown()
{
lock (actionLock)
{
return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued
&& playerState != PlayerStateType.Stunned && playerState != PlayerStateType.Charmed);
}
}
public bool InteractingWithMapWithoutMoving()
{
lock (actionLock)
{
return (playerState == PlayerStateType.LockingTheDoor || playerState == PlayerStateType.OpeningTheDoor
|| playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest);
}
}
public bool NullOrMoving()
{
lock (actionLock)
{
return (playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
}
}
public bool CanBeAwed()
{
lock (actionLock)
return !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped
|| playerState == PlayerStateType.Addicted
|| playerState == PlayerStateType.Rescued || playerState == PlayerStateType.Treated
|| playerState == PlayerStateType.Stunned || playerState == PlayerStateType.Charmed
|| playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
}
private GameObj? whatInteractingWith = null; private GameObj? whatInteractingWith = null;
public GameObj? WhatInteractingWith => whatInteractingWith; public GameObj? WhatInteractingWith => whatInteractingWith;


private long threadNum = 0;
public long ThreadNum => threadNum;
private long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
{
//只能被SetPlayerState引用
whatInteractingWith = gameObj;
playerState = value;
//Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString());
return ++stateNum;
}


public void ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
private long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
{ {
lock (gameObjLock)
{
++threadNum;
whatInteractingWith = gameObj;
if (value != PlayerStateType.Moving)
IsMoving = false;
playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value;
//Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString());
}
//只能被SetPlayerState引用
whatInteractingWith = gameObj;
playerState = value;
//Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString());
return stateNum;
} }


public void ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)

public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null)
{ {
lock (gameObjLock)
GameObj? gameObj = (GameObj?)obj;
lock (actionLock)
{ {
whatInteractingWith = gameObj;
if (value != PlayerStateType.Moving)
IsMoving = false;
playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value;
//Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString());
PlayerStateType nowPlayerState = PlayerState;
if (nowPlayerState == value && value != PlayerStateType.UsingSkill) 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.Addicted || value == PlayerStateType.Swinging
|| value == PlayerStateType.Deceased || value == PlayerStateType.Stunned
|| value == PlayerStateType.Charmed || value == PlayerStateType.Null)
return ChangePlayerState(value, gameObj);
else return -1;
case PlayerStateType.Stunned:
case PlayerStateType.Charmed:
if (value == PlayerStateType.Addicted || value == PlayerStateType.Deceased
|| value == PlayerStateType.Null)
return ChangePlayerState(value, gameObj);
else return -1;
case PlayerStateType.Swinging:
if (value == PlayerStateType.Addicted
|| value == PlayerStateType.Deceased || value == PlayerStateType.Stunned
|| value == PlayerStateType.Charmed || value == PlayerStateType.Null)
{
try
{
return ChangePlayerState(value, gameObj);
}
finally
{
ThreadNum.Release();
}
}
else return -1;
case PlayerStateType.ClimbingThroughWindows:
if (value == PlayerStateType.Addicted
|| value == PlayerStateType.Deceased || value == PlayerStateType.Stunned
|| value == PlayerStateType.Charmed || value == PlayerStateType.Null)
{
Window window = (Window)WhatInteractingWith!;
try
{
window.FinishClimbing();
return ChangePlayerState(value, gameObj);
}
finally
{
if (window.Stage.x == 0)
ThreadNum.Release();
else ReSetPos(window.Stage);
}
}
else return -1;

case PlayerStateType.OpeningTheChest:
((Chest)whatInteractingWith!).StopOpen();
return ChangePlayerState(value, gameObj);
case PlayerStateType.OpeningTheDoorway:
Doorway doorway = (Doorway)whatInteractingWith!;
doorway.StopOpenning();
return ChangePlayerState(value, gameObj);

case PlayerStateType.OpeningTheDoor:
Door door = (Door)whatInteractingWith!;
try
{
door.StopOpen();
ReleaseTool(door.DoorNum switch
{
3 => PropType.Key3,
5 => PropType.Key5,
_ => PropType.Key6,
}
);
return ChangePlayerState(value, gameObj);
}
finally
{
ThreadNum.Release();
}
case PlayerStateType.UsingSkill:
{
switch (CharacterType)
{
case CharacterType.TechOtaku:
{
if (typeof(CraftingBench).IsInstanceOfType(whatInteractingWith))
{
try
{
((CraftingBench)whatInteractingWith!).StopSkill();
return ChangePlayerState(value, gameObj);
}
finally
{
ThreadNum.Release();
}
}
else
{
if (value != PlayerStateType.UsingSkill)
((UseRobot)FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID = (int)playerID;
return ChangePlayerState(value, gameObj);
}
}
case CharacterType.Assassin:
if (value == PlayerStateType.Moving) return StateNum;
else
{
TryDeleteInvisible();
return ChangePlayerState(value, gameObj);
}
default:
return ChangePlayerState(value, gameObj);
}
}
default:
return ChangePlayerState(value, gameObj);
}
} }
} }


public void SetPlayerStateNaturally()
public long SetPlayerStateNaturally()
{ {
lock (gameObjLock)
lock (actionLock)
{ {
++threadNum;
whatInteractingWith = null; whatInteractingWith = null;
IsMoving = false;
playerState = PlayerStateType.Null; playerState = PlayerStateType.Null;
return ++stateNum;
} }
} }


public void RemoveFromGame(PlayerStateType playerStateType) public void RemoveFromGame(PlayerStateType playerStateType)
{ {
lock (gameObjLock)
lock (actionLock)
{ {
playerState = playerStateType;
CanMove = false;
IsResetting = true;
Position = GameData.PosWhoDie;
place = PlaceType.Grass;
TryToRemove();
ReSetCanMove(false);
SetPlayerState(playerStateType);
position = GameData.PosWhoDie;
} }
} }
#endregion #endregion


private int score = 0;
public int Score
private long score = 0;
public long Score
{ {
get => score;
get => Interlocked.Read(ref score);
} }


/// <summary> /// <summary>
/// 加分 /// 加分
/// </summary> /// </summary>
/// <param name="add">增加量</param> /// <param name="add">增加量</param>
public virtual void AddScore(int add)
public virtual void AddScore(long add)
{ {
lock (gameObjLock)
{
score += add;
//Debugger.Output(this, " 's score has been added to: " + score.ToString());
}
Interlocked.Add(ref score, add);
//Debugger.Output(this, " 's score has been added to: " + score.ToString());
} }


/// <summary> /// <summary>
/// 角色所属队伍ID /// 角色所属队伍ID
/// </summary> /// </summary>
private int teamID = int.MaxValue;
public int TeamID
private long teamID = long.MaxValue;
public long TeamID
{ {
get => teamID;
set
{
lock (gameObjLock)
{
teamID = value;
Debugger.Output(this, " joins in the team: " + value.ToString());
}
}
get => Interlocked.Read(ref teamID);
set => Interlocked.Exchange(ref teamID, value);
} }
private int playerID = int.MaxValue;
public int PlayerID
private long playerID = long.MaxValue;
public long PlayerID
{ {
get => playerID;
set
{
lock (gameObjLock)
{
playerID = value;
}
}
get => Interlocked.Read(ref playerID);
set => Interlocked.Exchange(ref playerID, value);
} }


#region 道具和buff相关属性、方法 #region 道具和buff相关属性、方法
private Prop[] propInventory = new Prop[GameData.maxNumOfPropInPropInventory]
private Gadget[] propInventory = new Gadget[GameData.maxNumOfPropInPropInventory]
{new NullProp(), new NullProp(),new NullProp() }; {new NullProp(), new NullProp(),new NullProp() };
public Prop[] PropInventory
public Gadget[] PropInventory
{ {
get => propInventory;
get
{
lock (inventoryLock)
return propInventory;
}
set set
{ {
lock (gameObjLock)
{
lock (inventoryLock)
propInventory = value; propInventory = value;
Debugger.Output(this, " prop becomes " + (PropInventory == null ? "null" : PropInventory.ToString()));
}
} }
} }


@@ -438,45 +655,81 @@ namespace GameClass.GameObj
/// 使用物品栏中的道具 /// 使用物品栏中的道具
/// </summary> /// </summary>
/// <returns>被使用的道具</returns> /// <returns>被使用的道具</returns>
public Prop UseProp(int indexing)
public Gadget UseProp(int indexing)
{ {
if (indexing < 0 || indexing >= GameData.maxNumOfPropInPropInventory) if (indexing < 0 || indexing >= GameData.maxNumOfPropInPropInventory)
return new NullProp(); return new NullProp();
lock (gameObjLock)
lock (inventoryLock)
{ {
Prop prop = propInventory[indexing];
Gadget prop = propInventory[indexing];
if (!prop.IsUsable()) return new NullProp();
PropInventory[indexing] = new NullProp(); PropInventory[indexing] = new NullProp();
return prop; return prop;
} }
} }


public Prop UseProp(PropType propType)
public Gadget UseProp(PropType propType)
{ {
lock (gameObjLock)
if (propType == PropType.Null)
{ {
if (propType == PropType.Null)
lock (inventoryLock)
{ {
for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing) for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
{ {
if (PropInventory[indexing].GetPropType() != PropType.Null)
if (PropInventory[indexing].IsUsable())
{ {
Prop prop = PropInventory[indexing];
Gadget prop = PropInventory[indexing];
PropInventory[indexing] = new NullProp(); PropInventory[indexing] = new NullProp();
return prop; return prop;
} }
} }
} }
else
}
else
{
lock (inventoryLock)
{
for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing) for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
{ {
if (PropInventory[indexing].GetPropType() == propType)
if (PropInventory[indexing].GetPropType() == propType && PropInventory[indexing].IsUsable())
{ {
Prop prop = PropInventory[indexing];
Gadget prop = PropInventory[indexing];
PropInventory[indexing] = new NullProp(); PropInventory[indexing] = new NullProp();
return prop; return prop;
} }
} }
return new NullProp();
}
}
return new NullProp();
}

public bool UseTool(PropType propType)
{
lock (inventoryLock)
{
for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
{
if (PropInventory[indexing].GetPropType() == propType && PropInventory[indexing].IsUsable())
{
return ((Tool)PropInventory[indexing]).IsUsed = true;
}
}
}
return false;
}

public void ReleaseTool(PropType propType)
{
lock (inventoryLock)
{
for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
{
if (PropInventory[indexing].GetPropType() == propType && ((Tool)PropInventory[indexing]).IsUsed)
{
((Tool)PropInventory[indexing]).IsUsed = false;
break;
}
}
} }
} }


@@ -486,9 +739,10 @@ namespace GameClass.GameObj
public int IndexingOfAddProp() public int IndexingOfAddProp()
{ {
int indexing = 0; int indexing = 0;
for (; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
if (PropInventory[indexing].GetPropType() == PropType.Null)
break;
lock (inventoryLock)
for (; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
if (propInventory[indexing].GetPropType() == PropType.Null)
break;
return indexing; return indexing;
} }


@@ -551,6 +805,7 @@ namespace GameClass.GameObj
return false; return false;
} }
} }

public void TryActivatingLIFE() public void TryActivatingLIFE()
{ {
if (buffManager.TryActivatingLIFE()) if (buffManager.TryActivatingLIFE())
@@ -575,6 +830,11 @@ namespace GameClass.GameObj
return buffManager.TryUseSpear(); return buffManager.TryUseSpear();
} }


public bool TryDeleteInvisible()
{
return buffManager.TryDeleteInvisible();
}

public bool TryUseShield() public bool TryUseShield()
{ {
if (buffManager.TryUseShield()) if (buffManager.TryUseShield())
@@ -608,9 +868,9 @@ namespace GameClass.GameObj
public override ShapeType Shape => ShapeType.Circle; public override ShapeType Shape => ShapeType.Circle;
public override bool IgnoreCollideExecutor(IGameObj targetObj) public override bool IgnoreCollideExecutor(IGameObj targetObj)
{ {
if (IsResetting)
if (IsRemoved)
return true; return true;
if (targetObj.Type == GameObjType.Prop)
if (targetObj.Type == GameObjType.Gadget)
{ {
return true; return true;
} }


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

@@ -17,7 +17,7 @@ namespace GameClass.GameObj
{ {
int score = 0; int score = 0;
foreach (var player in playerList) foreach (var player in playerList)
score += player.Score;
score += (int)player.Score;
return score; return score;
} }
} }


+ 15
- 47
logic/GameClass/GameObj/GameObj.cs View File

@@ -9,75 +9,43 @@ namespace GameClass.GameObj
/// </summary> /// </summary>
public abstract class GameObj : IGameObj public abstract class GameObj : IGameObj
{ {
private readonly ReaderWriterLockSlim gameObjReaderWriterLock = new();
public ReaderWriterLockSlim GameObjReaderWriterLock => gameObjReaderWriterLock;
protected readonly object gameObjLock = new(); protected readonly object gameObjLock = new();
public object GameLock => gameObjLock; public object GameLock => gameObjLock;


protected readonly XY birthPos; protected readonly XY birthPos;


private GameObjType type;
private readonly GameObjType type;
public GameObjType Type => type; public GameObjType Type => type;


private static long currentMaxID = 0; // 目前游戏对象的最大ID private static long currentMaxID = 0; // 目前游戏对象的最大ID
public const long invalidID = long.MaxValue; // 无效的ID public const long invalidID = long.MaxValue; // 无效的ID
public const long noneID = long.MinValue;
public long ID { get; } public long ID { get; }


private XY position;
public XY Position
{
get => position;
protected
set
{
lock (gameObjLock)
{
position = value;
}
}
}
protected XY position;
public abstract XY Position { get; }


protected PlaceType place;
public PlaceType Place { get => place; }
protected XY facingDirection = new(1, 0);
public abstract XY FacingDirection { get; }


private XY facingDirection = new(1, 0);
public XY FacingDirection
{
get => facingDirection;
set
{
lock (gameObjLock)
facingDirection = value;
}
}
public abstract bool CanMove { get; }


public abstract bool IsRigid { get; } public abstract bool IsRigid { get; }


public abstract ShapeType Shape { get; } public abstract ShapeType Shape { get; }


private bool canMove;
public bool CanMove
protected int isRemoved = 0;
public bool IsRemoved
{ {
get => canMove;
set
get
{ {
lock (gameObjLock)
{
canMove = value;
}
return (Interlocked.CompareExchange(ref isRemoved, 0, 0) == 1);
} }
} }

private bool isResetting;
public bool IsResetting
public virtual void TryToRemove()
{ {
get => isResetting;
set
{
lock (gameObjLock)
{
isResetting = value;
}
}
Interlocked.Exchange(ref isRemoved, 1);
} }


public int Radius { get; } public int Radius { get; }
@@ -85,7 +53,7 @@ namespace GameClass.GameObj
public virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false; public virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false;
public GameObj(XY initPos, int initRadius, GameObjType initType) public GameObj(XY initPos, int initRadius, GameObjType initType)
{ {
this.Position = this.birthPos = initPos;
this.position = this.birthPos = initPos;
this.Radius = initRadius; this.Radius = initRadius;
this.type = initType; this.type = initType;
ID = Interlocked.Increment(ref currentMaxID); ID = Interlocked.Increment(ref currentMaxID);


+ 18
- 0
logic/GameClass/GameObj/Immovable.cs View File

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

namespace GameClass.GameObj
{
public abstract class Immovable : GameObj
{

public override XY Position => position;

public override XY FacingDirection => facingDirection;

public override bool CanMove => false;

public Immovable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType)
{
}
}
}

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

@@ -1,24 +1,22 @@
using Preparation.Utility;
using System.Collections.Generic;
using Preparation.Interface;
using Preparation.Utility;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
/// <summary> /// <summary>
/// 箱子 /// 箱子
/// </summary> /// </summary>
public class Chest : GameObj
public class Chest : Immovable, IChest
{ {
public Chest(XY initPos) : public Chest(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest)
{ {
this.place = PlaceType.Chest;
this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;
public override ShapeType Shape => ShapeType.Square; public override ShapeType Shape => ShapeType.Square;


private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() };
public Prop[] PropInChest => propInChest;
private readonly Gadget[] propInChest = new Gadget[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() };
public Gadget[] PropInChest => propInChest;


private int openStartTime = 0; private int openStartTime = 0;
public int OpenStartTime => openStartTime; public int OpenStartTime => openStartTime;
@@ -26,7 +24,7 @@ namespace GameClass.GameObj
public Character? WhoOpen => whoOpen; public Character? WhoOpen => whoOpen;
public void Open(int startTime, Character character) public void Open(int startTime, Character character)
{ {
lock (gameObjLock)
lock (GameObjReaderWriterLock)
{ {
openStartTime = startTime; openStartTime = startTime;
whoOpen = character; whoOpen = character;
@@ -34,7 +32,7 @@ namespace GameClass.GameObj
} }
public void StopOpen() public void StopOpen()
{ {
lock (gameObjLock)
lock (GameObjReaderWriterLock)
{ {
openStartTime = 0; openStartTime = 0;
whoOpen = null; whoOpen = null;


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

@@ -1,45 +1,160 @@
using Preparation.Interface; using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System;
using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
/// <summary> /// <summary>
/// 门 /// 门
/// </summary> /// </summary>
public class Door : GameObj
public class Door : Immovable
{ {
public Door(XY initPos, PlaceType placeType) : public Door(XY initPos, PlaceType placeType) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Door) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Door)
{ {
this.place = placeType;
this.CanMove = false;
switch (placeType)
{
case PlaceType.Door3:
doorNum = 3;
break;
case PlaceType.Door5:
doorNum = 5;
break;
case PlaceType.Door6:
default:
doorNum = 6;
break;
}
} }

private readonly int doorNum;
public int DoorNum => doorNum;

public override bool IsRigid => !isOpen; public override bool IsRigid => !isOpen;
public override ShapeType Shape => ShapeType.Square; public override ShapeType Shape => ShapeType.Square;


private Character? whoLockOrOpen = null;
public Character? WhoLockOrOpen
{
get
{
lock (gameObjLock)
return whoLockOrOpen;
}
}

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


private int openOrLockDegree = 0;
public int OpenOrLockDegree
private int lockDegree = 0;
public int LockDegree
{ {
get => openOrLockDegree;
get
{
lock (gameObjLock)
return lockDegree;
}
set set
{ {
if (value > 0)
lock (gameObjLock)
openOrLockDegree = (value > GameData.degreeOfLockingOrOpeningTheDoor) ? GameData.degreeOfLockingOrOpeningTheDoor : value;
else
lock (gameObjLock)
openOrLockDegree = 0;
value = (value > GameData.degreeOfLockingOrOpeningTheDoor) ? GameData.degreeOfLockingOrOpeningTheDoor : value;
lock (gameObjLock)
lockDegree = value;
}
}

private long openStartTime = 0;
public long OpenStartTime
{
get
{
lock (gameObjLock)
return openStartTime;
}
}

public bool TryOpen(Character character)
{
lock (gameObjLock)
{
if (isOpen) return false;
if (whoLockOrOpen != null) return false;
openStartTime = Environment.TickCount64;
whoLockOrOpen = character;
return true;
}
}
public void StopOpen()
{
lock (gameObjLock)
{
if (whoLockOrOpen != null)
{
if ((Environment.TickCount64 - openStartTime) >= GameData.degreeOfLockingOrOpeningTheDoor / whoLockOrOpen.SpeedOfOpeningOrLocking)
isOpen = true;
whoLockOrOpen = null;
}
}
}
public void FinishOpen()
{
lock (gameObjLock)
{
isOpen = true;
whoLockOrOpen = null;
}
}

public bool TryLock(Character character)
{
lock (gameObjLock)
{
if (!isOpen) return false;
if (whoLockOrOpen != null) return false;
lockDegree = 0;
whoLockOrOpen = character;
return true;
}
}
public void StopLock()
{
lock (gameObjLock)
{
if (lockDegree >= GameData.degreeOfLockingOrOpeningTheDoor)
isOpen = false;
whoLockOrOpen = null;
}
}
public void FinishLock()
{
lock (gameObjLock)
{
isOpen = false;
whoLockOrOpen = null;
}
}

public void ForceToOpen()
{
Character? character;
lock (gameObjLock)
{
character = whoLockOrOpen;
whoLockOrOpen = null;
isOpen = true;
}
if (character != null)
{
lock (character.ActionLock)
if (character.PlayerState == PlayerStateType.OpeningTheDoor)
character.SetPlayerState();
} }
} }
} }


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

@@ -1,18 +1,18 @@
using Preparation.Interface;
using Google.Protobuf.WellKnownTypes;
using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
/// <summary> /// <summary>
/// 出口 /// 出口
/// </summary> /// </summary>
public class Doorway : GameObj
public class Doorway : Immovable, IDoorway
{ {
public Doorway(XY initPos) : public Doorway(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Doorway) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Doorway)
{ {
this.place = PlaceType.Doorway;
this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;
public override ShapeType Shape => ShapeType.Square; public override ShapeType Shape => ShapeType.Square;
@@ -27,7 +27,11 @@ namespace GameClass.GameObj
private bool powerSupply = false; private bool powerSupply = false;
public bool PowerSupply public bool PowerSupply
{ {
get => powerSupply;
get
{
lock (gameObjLock)
return powerSupply;
}
set set
{ {
lock (gameObjLock) lock (gameObjLock)
@@ -38,29 +42,58 @@ namespace GameClass.GameObj
private int openStartTime = 0; private int openStartTime = 0;
public int OpenStartTime public int OpenStartTime
{ {
get => openStartTime;
set
get
{ {
lock (gameObjLock) 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; private int openDegree = 0;
public int OpenDegree public int OpenDegree
{ {
get => openDegree;
set
get
{ {
if (value > 0)
lock (gameObjLock)
openDegree = (value < GameData.degreeOfOpenedDoorway) ? value : GameData.degreeOfOpenedDoorway;
else
lock (gameObjLock)
openDegree = 0;
lock (gameObjLock)
return openDegree;
} }
} }


public bool IsOpen() => (openDegree == GameData.degreeOfOpenedDoorway);
public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedDoorway);
} }
} }

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

@@ -6,13 +6,11 @@ namespace GameClass.GameObj
/// <summary> /// <summary>
/// 紧急出口 /// 紧急出口
/// </summary> /// </summary>
public class EmergencyExit : GameObj
public class EmergencyExit : Immovable
{ {
public EmergencyExit(XY initPos) : public EmergencyExit(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.EmergencyExit) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.EmergencyExit)
{ {
this.place = PlaceType.EmergencyExit;
this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;
public override ShapeType Shape => ShapeType.Square; public override ShapeType Shape => ShapeType.Square;


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

@@ -5,13 +5,11 @@ namespace GameClass.GameObj
/// <summary> /// <summary>
/// 发电机 /// 发电机
/// </summary> /// </summary>
public class Generator : GameObj
public class Generator : Immovable
{ {
public Generator(XY initPos) : public Generator(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Generator) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Generator)
{ {
this.place = PlaceType.Generator;
this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;
public override ShapeType Shape => ShapeType.Square; public override ShapeType Shape => ShapeType.Square;


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

@@ -8,7 +8,6 @@ namespace GameClass.GameObj
{ {
public partial class Map : IMap public partial class Map : IMap
{ {

private readonly Dictionary<uint, XY> birthPointList; // 出生点列表 private readonly Dictionary<uint, XY> birthPointList; // 出生点列表
public Dictionary<uint, XY> BirthPointList => birthPointList; public Dictionary<uint, XY> BirthPointList => birthPointList;


@@ -194,13 +193,12 @@ namespace GameClass.GameObj
{ {
if (playerID == person.ID) if (playerID == person.ID)
{ {
if (person.CharacterType == CharacterType.TechOtaku && person.FindIActiveSkill(ActiveSkillType.UseRobot).IsBeingUsed)
if (person.CharacterType == CharacterType.TechOtaku)
{ {
Debugger.Output(person, person.PlayerID.ToString()); Debugger.Output(person, person.PlayerID.ToString());
foreach (Character character in gameObjDict[GameObjType.Character]) foreach (Character character in gameObjDict[GameObjType.Character])
{ {
Debugger.Output(character, character.PlayerID.ToString());
if (person.PlayerID + GameData.numOfPeople == character.PlayerID)
if (((UseRobot)person.FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID == character.PlayerID)
{ {
player = character; player = character;
break; break;
@@ -233,7 +231,10 @@ namespace GameClass.GameObj
} }
} }
if (ToDel != null) if (ToDel != null)
{
GameObjDict[gameObj.Type].Remove(ToDel); GameObjDict[gameObj.Type].Remove(ToDel);
ToDel.TryToRemove();
}
} }
finally finally
{ {
@@ -244,16 +245,19 @@ namespace GameClass.GameObj
public bool RemoveJustFromMap(GameObj gameObj) public bool RemoveJustFromMap(GameObj gameObj)
{ {
GameObjLockDict[gameObj.Type].EnterWriteLock(); GameObjLockDict[gameObj.Type].EnterWriteLock();
bool flag;
try try
{ {
flag = GameObjDict[gameObj.Type].Remove(gameObj);
if (GameObjDict[gameObj.Type].Remove(gameObj))
{
gameObj.TryToRemove();
return true;
}
return false;
} }
finally finally
{ {
GameObjLockDict[gameObj.Type].ExitWriteLock(); GameObjLockDict[gameObj.Type].ExitWriteLock();
} }
return flag;
} }
public void Add(GameObj gameObj) public void Add(GameObj gameObj)
{ {


+ 103
- 50
logic/GameClass/GameObj/Map/MapInfo.cs View File

@@ -13,56 +13,109 @@ namespace GameClass.GameObj
/// </summary> /// </summary>
public static uint[,] defaultMap = new uint[,] public static uint[,] defaultMap = new uint[,]
{ {
{ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },//6墙,1-5出生点
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },//7草
{ 6, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 6 },//8机
{ 6, 0, 0, 0, 0, 6, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 0, 0, 0, 6 },//9大门
{ 6, 0, 0, 0, 0, 6, 6, 6, 6, 7, 0, 0, 0, 0, 0, 15, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 15, 0, 0, 0, 6 },//10紧急出口
{ 6, 6, 0, 0, 0, 0, 9, 6, 6, 7, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 6, 6, 6, 6, 11, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },//11窗
{ 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 7, 7, 6, 6, 7, 7, 6, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 13, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 6 },//12-14门
{ 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 7, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 6 },//15箱
{ 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 0, 6 },
{ 6, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 7, 7, 6, 0, 6 },
{ 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 7, 6, 0, 6 },
{ 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 12, 6, 6, 6, 6, 6, 6, 11, 6, 6, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 6, 0, 6 },
{ 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 0, 6 },
{ 6, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 7, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 6 },
{ 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 11, 6, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 6 },
{ 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 6, 7, 0, 0, 6 },
{ 6, 7, 7, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 6 },
{ 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 5, 0, 7, 7, 6, 0, 0, 0, 0, 0, 0, 7, 6, 6, 6, 6, 15, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 6, 7, 7, 0, 0, 0, 0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 6 },
{ 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 6, 6, 0, 10, 0, 6 },
{ 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 6, 6, 6, 6, 7, 0, 0, 0, 6 },
{ 6, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 6, 7, 0, 2, 0, 0, 6 },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 11, 6, 6, 6, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 11, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 11, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 6, 12, 6, 6, 6, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 7, 7, 0, 0, 0, 0, 6 },
{ 6, 0, 6, 7, 0, 0, 0, 8, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 0, 6 },
{ 6, 0, 6, 6, 6, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,6, 7, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 7, 7, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 6, 7, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 6, 6, 6, 6, 6, 7, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6 },
{ 6, 6, 0, 0, 7, 7, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 6 },
{ 6, 6, 15, 0, 0, 0, 7, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 11, 6, 0, 0, 0, 0, 0, 6 },
{ 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6,6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 15, 0, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0,8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 0, 0, 0, 6, 6, 6, 11, 6, 0, 0, 6, 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 6, 0, 6, 7, 7, 6, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 14, 6, 6, 6, 0, 0, 0, 0, 0, 7, 0, 0, 6, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7, 6, 0, 6, 6, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7, 6, 6, 6, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 6, 6, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 11, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 6, 6, 6, 6, 6, 7, 0, 0, 0, 10, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7, 6, 6, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 7, 7, 6, 6, 0, 0, 0, 6 },
{ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }
{ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },//6墙,1-5出生点
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },//7草
{ 6, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 6 },//8机
{ 6, 0, 0, 0, 0, 6, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 0, 0, 0, 6 },//9大门
{ 6, 0, 0, 0, 0, 6, 6, 6, 6, 7, 0, 0, 0, 0, 0, 15, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 15, 0, 0, 0, 6 },//10紧急出口
{ 6, 6, 0, 0, 0, 0, 9, 6, 6, 7, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 6, 6, 6, 6, 11, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },//11窗
{ 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 7, 7, 6, 6, 7, 7, 6, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 13, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 6 },//12-14门
{ 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 7, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 6 },//15箱
{ 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 0, 6 },
{ 6, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 7, 7, 6, 0, 6 },
{ 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 7, 6, 0, 6 },
{ 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 12, 6, 6, 6, 6, 6, 6, 11, 6, 6, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 6, 0, 6 },
{ 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 0, 6 },
{ 6, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 7, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 6 },
{ 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 11, 6, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 6 },
{ 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 6, 7, 0, 0, 6 },
{ 6, 7, 7, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 6 },
{ 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 5, 0, 7, 7, 6, 0, 0, 0, 0, 0, 0, 7, 6, 6, 6, 6, 15, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 6, 7, 7, 0, 0, 0, 0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 6 },
{ 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 6, 6, 0, 10, 0, 6 },
{ 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 6, 6, 6, 6, 7, 0, 0, 0, 6 },
{ 6, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 6, 7, 0, 2, 0, 0, 6 },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 11, 6, 6, 6, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 11, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 11, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 6, 12, 6, 6, 6, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 7, 7, 0, 0, 0, 0, 6 },
{ 6, 0, 6, 7, 0, 0, 0, 8, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 0, 6 },
{ 6, 0, 6, 6, 6, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,6, 7, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 7, 7, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 6, 7, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 6, 6, 6, 6, 6, 7, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6 },
{ 6, 6, 0, 0, 7, 7, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 6 },
{ 6, 6, 15, 0, 0, 0, 7, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 11, 6, 0, 0, 0, 0, 0, 6 },
{ 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6,6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 15, 0, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0,8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 0, 0, 0, 6, 6, 6, 11, 6, 0, 0, 6, 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 6, 0, 6, 7, 7, 6, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 14, 6, 6, 6, 0, 0, 0, 0, 0, 7, 0, 0, 6, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7, 6, 0, 6, 6, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7, 6, 6, 6, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 6, 6, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 11, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 6, 6, 6, 6, 6, 7, 0, 0, 0, 10, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7, 6, 6, 0, 0, 0, 6 },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 7, 7, 6, 6, 0, 0, 0, 6 },
{ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },

}; };
} }

} }
/*
{ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 11, 6, 6, 6, 6, 6, 6, 6, 6, 11, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 9, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 7, 6, 6, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 6, 7, 7, 0, 6, 6, 6, 7, 7, 7, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 7, 7, 7, 6, 6, 6, 0, 7, 7, 6, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 10, 6, 6, 0, 0, 0, 0, 6, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 6, 0, 0, 0, 0, 6, 6, 10, 0, 6, },
{ 6, 0, 6, 6, 7, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 7, 6, 6, 0, 6, },
{ 6, 0, 0, 7, 7, 0, 0, 0, 0, 0, 6, 6, 0, 0, 6, 6, 6, 6, 12, 6, 6, 6, 6, 0, 0, 0, 0, 6, 6, 6, 6, 12, 6, 6, 6, 6, 0, 0, 6, 6, 0, 0, 0, 0, 0, 7, 7, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 6, 6, 6, 6, 15, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 15, 6, 6, 6, 6, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 6, 6, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 6, 6, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 6, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 6, 0, 0, 0, 6, },
{ 6, 0, 0, 6, 6, 0, 0, 0, 0, 6, 0, 0, 1, 0, 0, 0, 7, 7, 6, 6, 6, 14, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 0, 0, 0, 2, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 7, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 7, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 0, 0, 0, 6, 0, 0, 8, 0, 7, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 7, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 7, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 7, 0, 0, 0, 6, 15, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 15, 6, 0, 0, 0, 7, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 7, 7, 6, 6, 8, 0, 0, 0, 7, 7, 7, 0, 6, 6, 0, 7, 7, 7, 0, 0, 0, 8, 6, 6, 7, 7, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 7, 6, 6, 0, 0, 0, 0, 7, 7, 0, 0, 0, 6, 6, 0, 0, 0, 7, 7, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 6, 6, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 6, 6, 0, 0, 0, 13, 0, 0, 0, 0, 6, 0, 0, 6, },
{ 6, 0, 6, 6, 0, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 6, },
{ 6, 0, 11, 0, 0, 0, 6, 0, 6, 0, 0, 0, 6, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 6, 0, 0, 0, 6, 0, 6, 0, 0, 0, 11, 0, 6, },
{ 6, 0, 6, 0, 0, 0, 6, 0, 6, 0, 0, 0, 11, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 11, 0, 0, 0, 6, 0, 6, 0, 0, 0, 6, 0, 6, },
{ 6, 0, 6, 0, 6, 6, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 6, 0, 0, 6, 6, 0, 6, 6, 6, 0, 6, 0, 6, },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 6, },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 0, 6, 0, 0, 6, 6, 6, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 6, 6, 6, 0, 0, 6, 0, 6, 6, 0, 0, 0, 0, 0, 0, 6, 0, 6, },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 6, 0, 6, 0, 0, 6, 6, 6, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 6, 6, 6, 0, 0, 6, 0, 6, 6, 0, 0, 0, 0, 0, 0, 6, 0, 6, },
{ 6, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 6, },
{ 6, 0, 6, 0, 6, 6, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 6, 0, 0, 6, 6, 0, 6, 6, 6, 0, 6, 0, 6, },
{ 6, 0, 6, 0, 0, 0, 6, 0, 6, 0, 0, 0, 11, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 11, 0, 0, 0, 6, 0, 6, 0, 0, 0, 6, 0, 6, },
{ 6, 0, 11, 0, 0, 0, 6, 0, 6, 0, 0, 0, 6, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 6, 0, 0, 0, 6, 0, 6, 0, 0, 0, 11, 0, 6, },
{ 6, 0, 6, 6, 0, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 6, },
{ 6, 0, 0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 6, 6, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 6, 6, 0, 0, 0, 13, 0, 0, 0, 0, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 7, 6, 6, 0, 0, 0, 0, 7, 7, 0, 0, 0, 6, 6, 0, 0, 0, 7, 7, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 7, 7, 6, 6, 8, 0, 0, 0, 7, 7, 7, 0, 6, 6, 0, 7, 7, 7, 0, 0, 0, 8, 6, 6, 7, 7, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 7, 0, 0, 0, 6, 15, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 15, 6, 0, 0, 0, 7, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 7, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 7, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 7, 0, 8, 0, 0, 6, 0, 0, 0, 0, 0, 0, 7, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7, 6, 0, 0, 6, },
{ 6, 0, 0, 6, 6, 0, 0, 0, 0, 6, 0, 0, 3, 0, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 14, 6, 6, 6, 7, 7, 0, 0, 0, 4, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 0, 6, },
{ 6, 0, 0, 0, 6, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 6, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 6, 6, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 6, 6, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 6, 6, 6, 6, 15, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 15, 6, 6, 6, 6, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 7, 7, 0, 0, 0, 0, 0, 6, 6, 0, 0, 6, 6, 6, 6, 13, 6, 6, 6, 6, 0, 0, 0, 0, 6, 6, 6, 6, 13, 6, 6, 6, 6, 0, 0, 6, 6, 0, 0, 0, 0, 0, 7, 7, 0, 0, 6, },
{ 6, 0, 6, 6, 7, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 7, 6, 6, 0, 6, },
{ 6, 0, 10, 6, 6, 0, 0, 0, 0, 6, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 6, 0, 0, 0, 0, 6, 6, 10, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 6, 7, 7, 0, 6, 6, 6, 7, 7, 7, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 7, 7, 7, 6, 6, 6, 0, 7, 7, 6, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 6, 6, 7, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 7, 6, 6, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 9, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 11, 6, 6, 6, 6, 6, 6, 6, 6, 11, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, },
{ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, },
{ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, },*/

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

@@ -5,13 +5,11 @@ namespace GameClass.GameObj
/// <summary> /// <summary>
/// 墙体 /// 墙体
/// </summary> /// </summary>
public class Wall : GameObj
public class Wall : Immovable
{ {
public Wall(XY initPos) : public Wall(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Wall) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Wall)
{ {
this.place = PlaceType.Wall;
this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;
public override ShapeType Shape => ShapeType.Square; public override ShapeType Shape => ShapeType.Square;


+ 35
- 6
logic/GameClass/GameObj/Map/Window.cs View File

@@ -6,13 +6,11 @@ namespace GameClass.GameObj
/// <summary> /// <summary>
/// 窗 /// 窗
/// </summary> /// </summary>
public class Window : GameObj
public class Window : Immovable, IWindow
{ {
public Window(XY initPos) : public Window(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Window) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Window)
{ {
this.place = PlaceType.Window;
this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;
public override ShapeType Shape => ShapeType.Square; public override ShapeType Shape => ShapeType.Square;
@@ -27,15 +25,46 @@ namespace GameClass.GameObj
return false; return false;
} }


private XY stage = new(0, 0);
public XY Stage
{
get
{
lock (gameObjLock)
return stage;
}
}

private Character? whoIsClimbing = null; private Character? whoIsClimbing = null;
public Character? WhoIsClimbing public Character? WhoIsClimbing
{ {
get => whoIsClimbing;
set
get
{ {
lock (gameObjLock) lock (gameObjLock)
whoIsClimbing = value;
return whoIsClimbing;
} }
} }

public bool TryToClimb(ICharacter character)
{
lock (gameObjLock)
if (whoIsClimbing == null)
{
stage = new(0, 0);
whoIsClimbing = (Character)character;
return true;
}
else return false;
}
public void FinishClimbing()
{
lock (gameObjLock)
whoIsClimbing = null;
}
public void Enter2Stage(XY xy)
{
lock (gameObjLock)
stage = xy;
}
} }
} }

+ 80
- 47
logic/GameClass/GameObj/Moveable.cs View File

@@ -4,79 +4,117 @@ using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
/// <summary>
/// 一切游戏元素的总基类,与THUAI4不同,继承IMoveable接口(出于一切物体其实都是可运动的指导思想)——LHR
/// </summary>
public abstract class Moveable : GameObj, IMoveable public abstract class Moveable : GameObj, IMoveable
{ {
protected readonly object moveObjLock = new();
public object MoveLock => moveObjLock;
protected readonly object actionLock = new();
public object ActionLock => actionLock;
//player.actionLock>其他.actionLock
private readonly ReaderWriterLockSlim moveReaderWriterLock = new();
public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock;
//规定moveReaderWriterLock<actionLock


private bool isMoving;
public bool IsMoving
public Semaphore ThreadNum { get; } = new(1, 1);

protected long stateNum = 0;
public long StateNum
{ {
get => isMoving;
get
{
lock (actionLock)
return stateNum;
}
set set
{ {
lock (gameObjLock)
{
isMoving = value;
}
lock (actionLock) stateNum = value;
} }
} }


public bool IsAvailable => !IsMoving && CanMove && !IsResetting; // 是否能接收指令
public override XY Position
{
get
{
lock (actionLock)
return position;
}
}


protected int moveSpeed;
/// <summary>
/// 移动速度
/// </summary>
public int MoveSpeed
public override XY FacingDirection
{ {
get => moveSpeed;
set
get
{ {
lock (gameObjLock)
{
moveSpeed = value;
}
lock (actionLock)
return facingDirection;
} }
} }
/// <summary>
/// 原初移动速度
/// </summary>
public int OrgMoveSpeed { get; protected set; }

private int isMoving = 0;
public bool IsMoving
{
get => (Interlocked.CompareExchange(ref isMoving, 0, 0) == 1);
set => Interlocked.Exchange(ref isMoving, value ? 1 : 0);
}


// 移动,改变坐标 // 移动,改变坐标
public long MovingSetPos(XY moveVec, PlaceType place)
public long MovingSetPos(XY moveVec, long stateNo)
{ {

if (moveVec.x != 0 || moveVec.y != 0) if (moveVec.x != 0 || moveVec.y != 0)
lock (gameObjLock)
{
lock (actionLock)
{ {
FacingDirection = moveVec;
this.Position += moveVec;
this.place = place;
if (!CanMove || IsRemoved) return -1;
if (stateNo != stateNum) return -1;
facingDirection = moveVec;
this.position += moveVec;
} }
}
return moveVec * moveVec; return moveVec * moveVec;
} }


public void ReSetPos(XY pos, PlaceType place)
public void ReSetPos(XY position)
{
lock (actionLock)
{
this.position = position;
}
}

private int canMove;
public override bool CanMove
{
get => (Interlocked.CompareExchange(ref canMove, 0, 0) == 1);
}

public void ReSetCanMove(bool value)
{
Interlocked.Exchange(ref canMove, (value ? 1 : 0));
}

public bool IsAvailableForMove // 是否能接收移动指令
{ {
lock (gameObjLock)
get
{ {
this.Position = pos;
this.place = place;
lock (actionLock)
{
return !IsMoving && CanMove && !IsRemoved;
}
} }
} }


protected int moveSpeed;
/// <summary> /// <summary>
/// 设置移动速度
/// 移动速度
/// </summary> /// </summary>
/// <param name="newMoveSpeed">新速度</param>
public void SetMoveSpeed(int newMoveSpeed)
public int MoveSpeed
{ {
MoveSpeed = newMoveSpeed;
get => Interlocked.CompareExchange(ref moveSpeed, 0, 0);
set => Interlocked.Exchange(ref moveSpeed, value);
} }
/// <summary>
/// 原初移动速度
/// </summary>
public int OrgMoveSpeed { get; protected set; }

/* /// <summary> /* /// <summary>
/// 复活时数据重置 /// 复活时数据重置
/// </summary> /// </summary>
@@ -87,16 +125,11 @@ namespace GameClass.GameObj
this.FacingDirection = new XY(1, 0); this.FacingDirection = new XY(1, 0);
isMoving = false; isMoving = false;
CanMove = false; CanMove = false;
IsResetting = true;
IsRemoved = true;
this.Position = birthPos; this.Position = birthPos;
this.Place= place; this.Place= place;
} }
}*/ }*/
/// <summary>
/// 为了使IgnoreCollide多态化并使GameObj能不报错地继承IMoveable
/// 在xfgg点播下设计了这个抽象辅助方法,在具体类中实现
/// </summary>
/// <returns> 依具体类及该方法参数而定,默认为false </returns>
public Moveable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType) public Moveable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType)
{ {
} }


+ 11
- 2
logic/GameClass/GameObj/ObjOfCharacter.cs View File

@@ -1,5 +1,6 @@
using Preparation.Interface; using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
@@ -8,13 +9,21 @@ namespace GameClass.GameObj
/// </summary> /// </summary>
public abstract class ObjOfCharacter : Moveable, IObjOfCharacter public abstract class ObjOfCharacter : Moveable, IObjOfCharacter
{ {
private ReaderWriterLockSlim objOfCharacterReaderWriterLock = new();
public ReaderWriterLockSlim ObjOfCharacterReaderWriterLock => objOfCharacterReaderWriterLock;
private ICharacter? parent = null; // 主人 private ICharacter? parent = null; // 主人
public ICharacter? Parent public ICharacter? Parent
{ {
get => parent;
get
{
lock (objOfCharacterReaderWriterLock)
{
return parent;
}
}
set set
{ {
lock (gameObjLock)
lock (objOfCharacterReaderWriterLock)
{ {
parent = value; parent = value;
} }


+ 1
- 3
logic/GameClass/GameObj/OutOfBoundBlock.cs View File

@@ -6,13 +6,11 @@ namespace GameClass.GameObj
/// <summary> /// <summary>
/// 逻辑墙 /// 逻辑墙
/// </summary> /// </summary>
public class OutOfBoundBlock : GameObj, IOutOfBound
public class OutOfBoundBlock : Immovable, IOutOfBound
{ {
public OutOfBoundBlock(XY initPos) : public OutOfBoundBlock(XY initPos) :
base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock)
{ {
this.place = PlaceType.Wall;
this.CanMove = false;
} }


public override bool IsRigid => true; public override bool IsRigid => true;


logic/GameClass/GameObj/Prop.cs → logic/GameClass/GameObj/Prop/Gadget.cs View File

@@ -1,15 +1,16 @@
using Preparation.Interface; using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
public abstract class Prop : ObjOfCharacter
public abstract class Gadget : ObjOfCharacter
{ {
public override bool IsRigid => true; public override bool IsRigid => true;


public override bool IgnoreCollideExecutor(IGameObj targetObj) public override bool IgnoreCollideExecutor(IGameObj targetObj)
{ {
if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet
if (targetObj.Type == GameObjType.Gadget || targetObj.Type == GameObjType.Bullet
|| targetObj.Type == GameObjType.Character || targetObj.Type == GameObjType.Chest) || targetObj.Type == GameObjType.Character || targetObj.Type == GameObjType.Chest)
return true; return true;
return false; return false;
@@ -17,61 +18,87 @@ namespace GameClass.GameObj


public override ShapeType Shape => ShapeType.Square; public override ShapeType Shape => ShapeType.Square;


public abstract bool IsUsable();
public abstract PropType GetPropType(); public abstract PropType GetPropType();


public Prop(XY initPos, PlaceType place, int radius = GameData.PropRadius) :
base(initPos, radius, GameObjType.Prop)
public Gadget(XY initPos, int radius = GameData.propRadius) :
base(initPos, radius, GameObjType.Gadget)
{ {
this.place = place;
this.CanMove = false;
this.moveSpeed = GameData.PropMoveSpeed;
this.ReSetCanMove(false);
this.MoveSpeed = GameData.propMoveSpeed;
} }
} }


public abstract class Tool : Gadget
{
private bool isUsed = false;
public bool IsUsed
{
get
{
lock (gameObjLock)
return isUsed;
}
set
{
lock (gameObjLock)
{
isUsed = value;
}
}
}
public override bool IsUsable() => !IsUsed;
public Tool(XY initPos) : base(initPos) { }
}
public abstract class Consumables : Gadget
{
public override bool IsUsable() => true;
public Consumables(XY initPos) : base(initPos) { }
}


///// <summary> ///// <summary>
///// 坑人地雷 ///// 坑人地雷
///// </summary> ///// </summary>
// public abstract class DebuffMine : Prop
// public abstract class DebuffMine : Gadget
//{ //{
// public DebuffMine(XYPosition initPos) : base(initPos) { } // public DebuffMine(XYPosition initPos) : base(initPos) { }
// } // }

#region 所有增益道具 #region 所有增益道具
/// <summary> /// <summary>
/// 增加速度 /// 增加速度
/// </summary> /// </summary>
public sealed class AddSpeed : Prop
public sealed class AddSpeed : Consumables
{ {
public AddSpeed(XY initPos, PlaceType placeType) :
base(initPos, placeType)
public AddSpeed(XY initPos) :
base(initPos)
{ {
} }
public override PropType GetPropType() => PropType.AddSpeed; public override PropType GetPropType() => PropType.AddSpeed;
} }

/// <summary> /// <summary>
/// 复活甲 /// 复活甲
/// </summary> /// </summary>
public sealed class AddLifeOrClairaudience : Prop
public sealed class AddLifeOrClairaudience : Consumables
{ {
public AddLifeOrClairaudience(XY initPos, PlaceType placeType) :
base(initPos, placeType)
public AddLifeOrClairaudience(XY initPos) :
base(initPos)
{ {
} }
public override PropType GetPropType() => PropType.AddLifeOrClairaudience; public override PropType GetPropType() => PropType.AddLifeOrClairaudience;
} }
public sealed class AddHpOrAp : Prop
public sealed class AddHpOrAp : Consumables
{ {
public AddHpOrAp(XY initPos, PlaceType placeType) :
base(initPos, placeType)
public AddHpOrAp(XY initPos) :
base(initPos)
{ {
} }
public override PropType GetPropType() => PropType.AddHpOrAp; public override PropType GetPropType() => PropType.AddHpOrAp;
} }
public sealed class RecoveryFromDizziness : Prop
public sealed class RecoveryFromDizziness : Consumables
{ {
public RecoveryFromDizziness(XY initPos, PlaceType placeType) :
base(initPos, placeType)
public RecoveryFromDizziness(XY initPos) :
base(initPos)
{ {
} }
public override PropType GetPropType() => PropType.RecoveryFromDizziness; public override PropType GetPropType() => PropType.RecoveryFromDizziness;
@@ -79,42 +106,43 @@ namespace GameClass.GameObj
/// <summary> /// <summary>
/// 矛盾 /// 矛盾
/// </summary> /// </summary>
public sealed class ShieldOrSpear : Prop
public sealed class ShieldOrSpear : Consumables
{ {
public ShieldOrSpear(XY initPos, PlaceType placeType) : base(initPos, placeType)
public ShieldOrSpear(XY initPos) : base(initPos)
{ {
} }
public override PropType GetPropType() => PropType.ShieldOrSpear; public override PropType GetPropType() => PropType.ShieldOrSpear;
} }
public sealed class Key3 : Prop
#endregion
public sealed class Key3 : Tool
{ {
public Key3(XY initPos, PlaceType placeType) : base(initPos, placeType)
public Key3(XY initPos) : base(initPos)
{ {
} }
public override PropType GetPropType() => PropType.Key3; public override PropType GetPropType() => PropType.Key3;
} }
public sealed class Key5 : Prop
public sealed class Key5 : Tool
{ {
public Key5(XY initPos, PlaceType placeType) : base(initPos, placeType)
public Key5(XY initPos) : base(initPos)
{ {
} }
public override PropType GetPropType() => PropType.Key5; public override PropType GetPropType() => PropType.Key5;
} }
public sealed class Key6 : Prop
public sealed class Key6 : Tool
{ {
public Key6(XY initPos, PlaceType placeType) : base(initPos, placeType)
public Key6(XY initPos) : base(initPos)
{ {
} }
public override PropType GetPropType() => PropType.Key6; public override PropType GetPropType() => PropType.Key6;
} }
public sealed class NullProp : Prop
public sealed class NullProp : Gadget
{ {
public NullProp(PlaceType placeType = PlaceType.Wall) : base(new XY(1, 1), placeType)
public override bool IsUsable() => false;
public NullProp() : base(new XY(1, 1))
{ {
} }
public override PropType GetPropType() => PropType.Null; public override PropType GetPropType() => PropType.Null;
} }
#endregion
// #region 所有坑人地雷 // #region 所有坑人地雷
///// <summary> ///// <summary>
///// 减速 ///// 减速
@@ -143,26 +171,26 @@ namespace GameClass.GameObj
// #endregion // #endregion
public static class PropFactory public static class PropFactory
{ {
public static Prop GetProp(PropType propType, XY pos, PlaceType place)
public static Gadget GetConsumables(PropType propType, XY pos)
{ {
switch (propType) switch (propType)
{ {
case PropType.AddSpeed: case PropType.AddSpeed:
return new AddSpeed(pos, place);
return new AddSpeed(pos);
case PropType.AddLifeOrClairaudience: case PropType.AddLifeOrClairaudience:
return new AddLifeOrClairaudience(pos, place);
return new AddLifeOrClairaudience(pos);
case PropType.ShieldOrSpear: case PropType.ShieldOrSpear:
return new ShieldOrSpear(pos, place);
return new ShieldOrSpear(pos);
case PropType.AddHpOrAp: case PropType.AddHpOrAp:
return new AddHpOrAp(pos, place);
return new AddHpOrAp(pos);
case PropType.RecoveryFromDizziness: case PropType.RecoveryFromDizziness:
return new RecoveryFromDizziness(pos, place);
return new RecoveryFromDizziness(pos);
case PropType.Key3: case PropType.Key3:
return new Key3(pos, place);
return new Key3(pos);
case PropType.Key5: case PropType.Key5:
return new Key5(pos, place);
return new Key5(pos);
case PropType.Key6: case PropType.Key6:
return new Key6(pos, place);
return new Key6(pos);
default: default:
return new NullProp(); return new NullProp();
} }

+ 92
- 0
logic/GameClass/GameObj/Prop/Item.cs View File

@@ -0,0 +1,92 @@
using Preparation.Interface;
using Preparation.Utility;
using System.Threading;

namespace GameClass.GameObj
{
public abstract class Item : ObjOfCharacter
{
public override bool IsRigid => true;

public override bool IgnoreCollideExecutor(IGameObj targetObj) => false;

public override ShapeType Shape => ShapeType.Square;

public abstract PropType GetPropType();

public Item(XY initPos, int radius = GameData.propRadius) :
base(initPos, radius, GameObjType.Item)
{
this.ReSetCanMove(false);
this.MoveSpeed = 0;
}
}


///// <summary>
///// 坑人地雷
///// </summary>
// public abstract class DebuffMine : Gadget
//{
// public DebuffMine(XYPosition initPos) : base(initPos) { }
// }

public sealed class CraftingBench : Item
{
public CraftingBench(XY initPos, Character character, int num) :
base(initPos)
{
Parent = character;
this.num = num;
}
private readonly int num;
private long parentStateNum;
public long ParentStateNum
{
get => Interlocked.Read(ref parentStateNum);
set => Interlocked.Exchange(ref parentStateNum, value);
}
public void StopSkill()
{
((SummonGolem)Parent!.FindActiveSkill(ActiveSkillType.SummonGolem)).DeleteGolem((int)num);
}
public void TryStopSkill()
{
lock (Parent!.ActionLock)
{
if (Parent!.StateNum == parentStateNum)
{
Parent!.SetPlayerState();
}
}
}
public override PropType GetPropType() => PropType.CraftingBench;
}

// #region 所有坑人地雷
///// <summary>
///// 减速
///// </summary>
// public sealed class MinusSpeed : DebuffMine
//{
// public MinusSpeed(XYPosition initPos) : base(initPos) { }
// public override PropType GetPropType() => PropType.minusSpeed;
// }
///// <summary>
///// 减少攻击力
///// </summary>
// public sealed class MinusAP : DebuffMine
//{
// public MinusAP(XYPosition initPos) : base(initPos) { }
// public override PropType GetPropType() => PropType.minusAP;
// }
///// <summary>
///// 增加冷却
///// </summary>
// public sealed class AddCD : DebuffMine
//{
// public AddCD(XYPosition initPos) : base(initPos) { }
// public override PropType GetPropType() => PropType.addCD;
// }
// #endregion
}

logic/GameClass/GameObj/PickedProp.cs → logic/GameClass/GameObj/Prop/PickedProp.cs View File

@@ -1,25 +1,20 @@
using Preparation.Utility; using Preparation.Utility;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GameClass.GameObj namespace GameClass.GameObj
{ {
// 为方便界面组做道具拾起特效,现引入“被捡起的道具”,在每帧发送给界面组 // 为方便界面组做道具拾起特效,现引入“被捡起的道具”,在每帧发送给界面组
public class PickedProp : GameObj
/*
public class Item : Immovable
{ {
public override ShapeType Shape => ShapeType.Circle; public override ShapeType Shape => ShapeType.Circle;
public override bool IsRigid => false; public override bool IsRigid => false;
public long MappingID { get; } public long MappingID { get; }
public Prop PropHasPicked;
public PickedProp(Prop prop) :
base(prop.Position, prop.Radius, GameObjType.PickedProp)
public readonly Gadget propHasPicked;
public Item(Gadget prop) :
base(prop.Position, prop.Radius, GameObjType.Item)
{ {
this.place = prop.Place;
this.PropHasPicked = prop;
this.propHasPicked = prop;
this.MappingID = prop.ID; this.MappingID = prop.ID;
} }
}
}*/
} }

+ 120
- 119
logic/GameEngine/MoveEngine.cs View File

@@ -20,18 +20,6 @@ namespace GameEngine


private readonly ITimer gameTimer; private readonly ITimer gameTimer;
private readonly Action<IMoveable> EndMove; private readonly Action<IMoveable> EndMove;
public readonly uint[,] ProtoGameMap;
public PlaceType GetPlaceType(XY Position)
{
try
{
return (PlaceType)ProtoGameMap[Position.x / GameData.numOfPosGridPerCell, Position.y / GameData.numOfPosGridPerCell];
}
catch
{
return PlaceType.Null;
}
}


public IGameObj? CheckCollision(IMoveable obj, XY Pos) public IGameObj? CheckCollision(IMoveable obj, XY Pos)
{ {
@@ -52,7 +40,6 @@ namespace GameEngine
Action<IMoveable> EndMove Action<IMoveable> EndMove
) )
{ {
this.ProtoGameMap = gameMap.ProtoGameMap;
this.gameTimer = gameMap.Timer; this.gameTimer = gameMap.Timer;
this.EndMove = EndMove; this.EndMove = EndMove;
this.OnCollision = OnCollision; this.OnCollision = OnCollision;
@@ -64,35 +51,69 @@ namespace GameEngine
/// </summary> /// </summary>
/// <param name="obj">移动物体,默认obj.Rigid为true</param> /// <param name="obj">移动物体,默认obj.Rigid为true</param>
/// <param name="moveVec">移动的位移向量</param> /// <param name="moveVec">移动的位移向量</param>
private void MoveMax(IMoveable obj, XY moveVec)
private bool MoveMax(IMoveable obj, XY moveVec, long stateNum)
{ {
/*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/
XY nextPos = obj.Position + moveVec; XY nextPos = obj.Position + moveVec;
double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec);
maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond);
obj.MovingSetPos(new XY(moveVec, maxLen), GetPlaceType(nextPos));
return (obj.MovingSetPos(new XY(moveVec, maxLen), stateNum)) >= 0;
} }


public void MoveObj(IMoveable obj, int moveTime, double direction)
private bool LoopDo(IMoveable obj, double direction, ref double deltaLen, long stateNum)
{ {
if (obj.IsMoving) // 已经移动的物体不能再移动
return;
if (!obj.IsAvailable || !gameTimer.IsGaming)
return;
double moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
XY res = new(direction, moveVecLength);

// 越界情况处理:如果越界,则与越界方块碰撞
bool flag; // 循环标志
do
{
flag = false;
IGameObj? collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res);
if (collisionObj == null)
break;

switch (OnCollision(obj, collisionObj, res))
{
case AfterCollision.ContinueCheck:
flag = true;
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
return false;
case AfterCollision.MoveMax:
if (!MoveMax(obj, res, stateNum)) return false;
moveVecLength = 0;
res = new XY(direction, moveVecLength);
break;
}
} while (flag);

long moveL = obj.MovingSetPos(res, stateNum);
if (moveL == -1) return false;
deltaLen = deltaLen + moveVecLength - Math.Sqrt(moveL);
return true;
}

public void MoveObj(IMoveable obj, int moveTime, double direction, long stateNum)
{
if (!gameTimer.IsGaming) return;
lock (obj.ActionLock)
{
if (!obj.IsAvailableForMove) { EndMove(obj); return; }
obj.IsMoving = true;
}


long threadNum = (obj.Type == GameObjType.Character) ? ((ICharacter)obj).ThreadNum : 0;//对人特殊处理
new Thread new Thread
( (
() => () =>
{ {
lock (obj.MoveLock)
obj.IsMoving = true;

double moveVecLength = 0.0; double moveVecLength = 0.0;
XY res = new(direction, moveVecLength); XY res = new(direction, moveVecLength);
double deltaLen = moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res))); // 转向,并用deltaLen存储行走的误差
double deltaLen = (double)0.0; // 转向,并用deltaLen存储行走的误差
IGameObj? collisionObj = null; IGameObj? collisionObj = null;
bool isDestroyed = false;
bool isEnded = false;


bool flag; // 循环标志 bool flag; // 循环标志
do do
@@ -109,34 +130,82 @@ namespace GameEngine
break; break;
case AfterCollision.Destroyed: case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
isEnded = true;
break; break;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
break; break;
} }
} while (flag); } while (flag);


if (!isDestroyed)
if (isEnded)
{
obj.IsMoving = false;
EndMove(obj);
return;
}
else
{ {
new FrameRateTaskExecutor<int>(
() => gameTimer.IsGaming && obj.CanMove && !obj.IsResetting && obj.IsMoving,
() =>
if (moveTime >= GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond)
{
Thread.Sleep(GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
new FrameRateTaskExecutor<int>(
() => gameTimer.IsGaming,
() =>
{
if (obj.StateNum != stateNum || !obj.CanMove || obj.IsRemoved)
return !(isEnded = true);
return !(isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum));
},
GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond,
() =>
{
return 0;
},
maxTotalDuration: moveTime - GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond
)
{ {
moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
res = new XY(direction, moveVecLength);

//对人特殊处理
if (threadNum > 0 && ((ICharacter)obj).ThreadNum != threadNum) return false;

// 越界情况处理:如果越界,则与越界方块碰撞
bool flag; // 循环标志
do
AllowTimeExceed = true,
MaxTolerantTimeExceedCount = ulong.MaxValue,
TimeExceedAction = b =>
{ {
flag = false;
collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res);
if (collisionObj == null)
break;
if (b)
Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!");


#if DEBUG
else
{
Console.WriteLine("Debug info: Object moving time exceed for once.");
}
#endif
}
}.Start();
if (!isEnded && obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved)
isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum);
}
if (isEnded)
{
obj.IsMoving = false;
EndMove(obj);
return;
}
if (obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved)
{
int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
if (leftTime > 0)
{
Thread.Sleep(leftTime); // 多移动的在这里补回来
}
do
{
flag = false;
moveVecLength = (double)deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
res = new XY(direction, moveVecLength);
if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
{
obj.MovingSetPos(res, stateNum);
}
else
{
switch (OnCollision(obj, collisionObj, res)) switch (OnCollision(obj, collisionObj, res))
{ {
case AfterCollision.ContinueCheck: case AfterCollision.ContinueCheck:
@@ -144,87 +213,19 @@ namespace GameEngine
break; break;
case AfterCollision.Destroyed: case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
return false;
isEnded = true;
break;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum)
MoveMax(obj, res);
MoveMax(obj, res, stateNum);
moveVecLength = 0; moveVecLength = 0;
res = new XY(direction, moveVecLength); res = new XY(direction, moveVecLength);
break; break;
} }
} while (flag);

if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum)
deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res)));

return true;
},
GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond,
() =>
{
int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond);
bool flag;
do
{
flag = false;
if (!isDestroyed)
{
moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
res = new XY(direction, moveVecLength);
if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
{
if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum)
obj.MovingSetPos(res, GetPlaceType(obj.Position + res));
}
else
{
switch (OnCollision(obj, collisionObj, res))
{
case AfterCollision.ContinueCheck:
flag = true;
break;
case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
break;
case AfterCollision.MoveMax:
if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum)
MoveMax(obj, res);
moveVecLength = 0;
res = new XY(direction, moveVecLength);
break;
}
}
}
} while (flag);
if (leftTime > 0 && obj.IsMoving)
{
Thread.Sleep(leftTime); // 多移动的在这里补回来
} }
lock (obj.MoveLock)
obj.IsMoving = false; // 结束移动
EndMove(obj);
return 0;
},
maxTotalDuration: moveTime
)
{
AllowTimeExceed = true,
MaxTolerantTimeExceedCount = ulong.MaxValue,
TimeExceedAction = b =>
{
if (b)
Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!");

#if DEBUG
else
{
Console.WriteLine("Debug info: Object moving time exceed for once.");
}
#endif
}
}.Start();
} while (flag);
}
obj.IsMoving = false; // 结束移动
EndMove(obj);
} }
} }
).Start(); ).Start();


+ 294
- 194
logic/Gaming/ActionManager.cs View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Numerics;
using System.Threading; using System.Threading;
using GameClass.GameObj; using GameClass.GameObj;
using GameEngine; using GameEngine;
@@ -13,57 +14,69 @@ namespace Gaming
private readonly ActionManager actionManager; private readonly ActionManager actionManager;
private class ActionManager private class ActionManager
{ {

// 人物移动
private void SkillWhenColliding(Character player, IGameObj collisionObj)
{
if (collisionObj.Type == GameObjType.Bullet)
{
if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty)
{
if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty))
player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty));
gameMap.Remove((GameObj)collisionObj);
}
}
if (player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost())
{
if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge))
player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge));
characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge);
}
}
public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection)
{ {
if (moveTimeInMilliseconds < 5) return false; if (moveTimeInMilliseconds < 5) return false;
if (!playerToMove.Commandable() || !TryToStop()) return false;
if (playerToMove.IsMoving) return false;
characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving);
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection);
long stateNum = playerToMove.SetPlayerState(PlayerStateType.Moving);
if (stateNum == -1) return false;
new Thread
(
() =>
{
playerToMove.ThreadNum.WaitOne();
if (stateNum != playerToMove.StateNum)
playerToMove.ThreadNum.Release();
else
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, stateNum);
}
)
{ IsBackground = true }.Start();
return true; return true;
} }


public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection)
{ {
if (!playerToMove.Commandable() && playerToMove.PlayerState != PlayerStateType.Stunned) return false;
characterManager.BeStunned(playerToMove, moveTimeInMilliseconds);
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection);
if (playerToMove.CharacterType == CharacterType.Robot) return false;
long stateNum = playerToMove.SetPlayerState(PlayerStateType.Charmed);
if (stateNum == -1) return false;
new Thread
(() =>
{
playerToMove.ThreadNum.WaitOne();
if (stateNum != playerToMove.StateNum)
playerToMove.ThreadNum.Release();
else
{
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, playerToMove.StateNum);
Thread.Sleep(moveTimeInMilliseconds);
lock (playerToMove.ActionLock)
{
if (stateNum == playerToMove.StateNum)
playerToMove.SetPlayerStateNaturally();
}
}
}
)
{ IsBackground = true }.Start();
return true; return true;
} }


public bool Stop(Character player)
public static bool Stop(Character player)
{ {
if (player.Commandable() || !TryToStop())
lock (player.ActionLock)
{ {
characterManager.SetPlayerState(player);
return true;
if (player.Commandable())
{
player.SetPlayerState();
return true;
}
} }
return false; return false;
} }


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


@@ -71,21 +84,21 @@ namespace Gaming
return false; return false;


++generatorForFix.NumOfFixing; ++generatorForFix.NumOfFixing;
characterManager.SetPlayerState(player, PlayerStateType.Fixing);
long threadNum = player.ThreadNum;
player.SetPlayerState(PlayerStateType.Fixing);
long threadNum = player.StateNum;
new Thread new Thread
( (
() => () =>
{ {
Thread.Sleep(GameData.frameDuration); Thread.Sleep(GameData.frameDuration);
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && threadNum == player.ThreadNum,
loopCondition: () => gameMap.Timer.IsGaming && threadNum == player.StateNum,
loopToDo: () => loopToDo: () =>
{ {
if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player))
gameMap.NumOfRepairedGenerators++; gameMap.NumOfRepairedGenerators++;
if (generatorForFix.DegreeOfRepair == GameData.degreeOfFixedGenerator) if (generatorForFix.DegreeOfRepair == GameData.degreeOfFixedGenerator)
characterManager.SetPlayerState(player);
player.SetPlayerState();//Num == player.StateNum
}, },
timeInterval: GameData.frameDuration, timeInterval: GameData.frameDuration,
finallyReturn: () => 0 finallyReturn: () => 0
@@ -102,24 +115,26 @@ namespace Gaming


public bool OpenDoorway(Student player) public bool OpenDoorway(Student player)
{ {
if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorway)
if (!(player.Commandable()))
return false; return false;
Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); 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 new Thread
( (
() => () =>
{ {
//player.ThreadNum.WaitOne();
Thread.Sleep(GameData.degreeOfOpenedDoorway - doorwayToOpen.OpenDegree); 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();
}
} }
} }


@@ -157,6 +172,7 @@ namespace Gaming


public bool Treat(Student player, Student? playerTreated = null) public bool Treat(Student player, Student? playerTreated = null)
{ {
if (player.CharacterType == CharacterType.Robot) return false;
if (playerTreated == null) if (playerTreated == null)
{ {
playerTreated = gameMap.StudentForInteract(player.Position); playerTreated = gameMap.StudentForInteract(player.Position);
@@ -171,24 +187,24 @@ namespace Gaming
( (
() => () =>
{ {
characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated);
characterManager.SetPlayerState(player, PlayerStateType.Treating);
long threadNum = player.ThreadNum;
playerTreated.SetPlayerState(PlayerStateType.Treated);
player.SetPlayerState(PlayerStateType.Treating);
long threadNum = player.StateNum;


new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && threadNum == player.StateNum && gameMap.Timer.IsGaming,
loopToDo: () => loopToDo: () =>
{ {
if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player))
characterManager.SetPlayerState(playerTreated);
playerTreated.SetPlayerState();//
}, },
timeInterval: GameData.frameDuration, timeInterval: GameData.frameDuration,
finallyReturn: () => 0 finallyReturn: () => 0
) )
.Start(); .Start();


if (threadNum == player.ThreadNum) 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(); { IsBackground = true }.Start();
@@ -196,23 +212,26 @@ namespace Gaming
} }
public bool Rescue(Student player, Student? playerRescued = null) public bool Rescue(Student player, Student? playerRescued = null)
{ {
if (player.CharacterType == CharacterType.Robot) return false;

if (playerRescued == null) if (playerRescued == null)
{ {
playerRescued = gameMap.StudentForInteract(player.Position); playerRescued = gameMap.StudentForInteract(player.Position);
if (playerRescued == null) return false; if (playerRescued == null) return false;
} }

if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position)) if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position))
return false; return false;
characterManager.SetPlayerState(player, PlayerStateType.Rescuing);
characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued);
long threadNum = player.ThreadNum;
player.SetPlayerState(PlayerStateType.Rescuing);
playerRescued.SetPlayerState(PlayerStateType.Rescued);
long threadNum = player.StateNum;


new Thread new Thread
( (
() => () =>
{ {
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && threadNum == player.StateNum && gameMap.Timer.IsGaming,
loopToDo: () => loopToDo: () =>
{ {
playerRescued.TimeOfRescue += GameData.frameDuration; playerRescued.TimeOfRescue += GameData.frameDuration;
@@ -227,14 +246,14 @@ namespace Gaming
{ {
if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue) if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue)
{ {
characterManager.SetPlayerState(playerRescued);
playerRescued.SetPlayerState();
playerRescued.HP = playerRescued.MaxHp / 2; playerRescued.HP = playerRescued.MaxHp / 2;
player.AddScore(GameData.StudentScoreRescue); player.AddScore(GameData.StudentScoreRescue);
} }
else else
characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted);
playerRescued.SetPlayerState(PlayerStateType.Addicted);
} }
if (threadNum == player.ThreadNum) characterManager.SetPlayerState(player);
if (threadNum == player.StateNum) player.SetPlayerState();
playerRescued.TimeOfRescue = 0; playerRescued.TimeOfRescue = 0;
} }
) )
@@ -251,7 +270,7 @@ namespace Gaming
if (chestToOpen == null || chestToOpen.OpenStartTime > 0) if (chestToOpen == null || chestToOpen.OpenStartTime > 0)
return false; return false;


characterManager.SetPlayerState(player, PlayerStateType.OpeningTheChest, chestToOpen);
player.SetPlayerState(PlayerStateType.OpeningTheChest, chestToOpen);
int startTime = gameMap.Timer.nowTime(); int startTime = gameMap.Timer.nowTime();
chestToOpen.Open(startTime, player); chestToOpen.Open(startTime, player);
new Thread new Thread
@@ -265,9 +284,9 @@ namespace Gaming
player.SetPlayerStateNaturally(); player.SetPlayerStateNaturally();
for (int i = 0; i < GameData.maxNumOfPropInChest; ++i) for (int i = 0; i < GameData.maxNumOfPropInChest; ++i)
{ {
Prop prop = chestToOpen.PropInChest[i];
Gadget prop = chestToOpen.PropInChest[i];
chestToOpen.PropInChest[i] = new NullProp(); chestToOpen.PropInChest[i] = new NullProp();
prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position));
prop.ReSetPos(player.Position);
gameMap.Add(prop); gameMap.Add(prop);
} }
} }
@@ -280,12 +299,11 @@ namespace Gaming
} }
public bool ClimbingThroughWindow(Character player) public bool ClimbingThroughWindow(Character player)
{ {
if (!player.Commandable())
return false;
Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window); Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window);
if (windowForClimb == null) return false;


if (windowForClimb == null || windowForClimb.WhoIsClimbing != null)
return false;
long stateNum = player.SetPlayerState(PlayerStateType.ClimbingThroughWindows, windowForClimb);
if (stateNum == -1) return false;


XY windowToPlayer = new( XY windowToPlayer = new(
(Math.Abs(player.Position.x - windowForClimb.Position.x) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.x > windowForClimb.Position.x ? 1 : -1)) : 0, (Math.Abs(player.Position.x - windowForClimb.Position.x) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.x > windowForClimb.Position.x ? 1 : -1)) : 0,
@@ -295,124 +313,202 @@ namespace Gaming
if (characterInWindow != null) if (characterInWindow != null)
{ {
if (player.IsGhost() && !characterInWindow.IsGhost()) if (player.IsGhost() && !characterInWindow.IsGhost())
characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position, PlaceType.Null));
characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position));
return false; return false;
}*/
}


//Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer);
// gameMap.Add(addWall);
Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer);
gameMap.Add(addWall);*/


characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows);
long threadNum = player.ThreadNum;
windowForClimb.WhoIsClimbing = player;
new Thread new Thread
(
() =>
{
new FrameRateTaskExecutor<int>(
loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopToDo: () => { },
timeInterval: GameData.frameDuration,
finallyReturn: () => 0,
maxTotalDuration: (int)((windowToPlayer + windowForClimb.Position - player.Position).Length() * 1000 / player.MoveSpeed)
)
.Start();
if (player.PlayerState != PlayerStateType.ClimbingThroughWindows)
{
windowForClimb.WhoIsClimbing = null;
return;
}
(
() =>
{
player.ThreadNum.WaitOne();
if (stateNum != player.StateNum)
{
player.ThreadNum.Release();
}
else
{
if (!windowForClimb.TryToClimb(player))
{
player.ThreadNum.Release();
player.SetPlayerStateNaturally();
}
else
{
Thread.Sleep((int)((windowToPlayer + windowForClimb.Position - player.Position).Length() * 1000 / player.MoveSpeed));

lock (player.ActionLock)
{
if (player.StateNum != stateNum) return;
player.ReSetPos(windowToPlayer + windowForClimb.Position);
windowForClimb.Enter2Stage(windowForClimb.Position - 2 * windowToPlayer);
}

player.MoveSpeed = player.SpeedOfClimbingThroughWindows;
moveEngine.MoveObj(player, (int)(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2), (-1 * windowToPlayer).Angle(), stateNum);

Thread.Sleep((int)(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2));

player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed);

lock (player.ActionLock)
{
if (stateNum == player.StateNum)
{
player.SetPlayerState();
windowForClimb.FinishClimbing();
}
}
}
}
}
)
{ IsBackground = true }.Start();


player.ReSetPos(windowToPlayer + windowForClimb.Position, PlaceType.Window);
player.MoveSpeed = player.SpeedOfClimbingThroughWindows;
return true;
}
public bool LockDoor(Character player)
{
if (player.CharacterType == CharacterType.Robot) return false;
Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door);
if (doorToLock == null) return false;


moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle());
PropType propType = doorToLock.DoorNum switch
{
3 => PropType.Key3,
5 => PropType.Key5,
_ => PropType.Key6,
};


new FrameRateTaskExecutor<int>(
loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopToDo: () =>
{
},
timeInterval: GameData.frameDuration,
finallyReturn: () => 0,
maxTotalDuration: (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed)
)
.Start();
XY PosJumpOff = windowForClimb.Position - 2 * windowToPlayer;
player.ReSetPos(PosJumpOff, gameMap.GetPlaceType(PosJumpOff));
player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed);
windowForClimb.WhoIsClimbing = null;
// gameMap.Remove(addWall);
if (threadNum == player.ThreadNum)
{
characterManager.SetPlayerState(player);
}
}
if (!player.UseTool(propType)) return false;


)
long stateNum = player.SetPlayerState(PlayerStateType.LockingTheDoor, doorToLock);
if (stateNum == -1)
{
player.ReleaseTool(propType);
return false;
}

new Thread
(
() =>
{
player.ThreadNum.WaitOne();
if (stateNum != player.StateNum)
{
player.ReleaseTool(propType);
player.ThreadNum.Release();
}
else
{
if (!doorToLock.TryLock(player))
{
player.ReleaseTool(propType);
lock (player.ActionLock)
{
if (stateNum == player.StateNum) player.SetPlayerState();
}
player.ThreadNum.Release();
}
else
{
Thread.Sleep(GameData.checkInterval);
new FrameRateTaskExecutor<int>(
loopCondition: () => stateNum == player.StateNum && gameMap.Timer.IsGaming && doorToLock.LockDegree < GameData.degreeOfLockingOrOpeningTheDoor,
loopToDo: () =>
{
if ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) != null)
return false;
doorToLock.LockDegree += GameData.checkInterval * player.SpeedOfOpeningOrLocking;
return true;
},
timeInterval: GameData.checkInterval,
finallyReturn: () => 0
)
.Start();
doorToLock.StopLock();
player.ReleaseTool(propType);
lock (player.ActionLock)
{
if (stateNum == player.StateNum) player.SetPlayerStateNaturally();
}
player.ThreadNum.Release();
}
}
}
)
{ IsBackground = true }.Start(); { IsBackground = true }.Start();


return true; return true;
} }
public bool LockOrOpenDoor(Character player)

public bool OpenDoor(Character player)
{ {
if (!(player.Commandable()) || player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor)
return false;
if (player.CharacterType == CharacterType.Robot) return false;
Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door); Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door);
if (doorToLock == null || doorToLock.OpenOrLockDegree > 0 || gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character) != null)
return false;
bool flag = false;
foreach (Prop prop in player.PropInventory)
if (doorToLock == null) return false;

PropType propType = doorToLock.DoorNum switch
{ {
switch (prop.GetPropType())
{
case PropType.Key3:
if (doorToLock.Place == PlaceType.Door3)
flag = true;
break;
case PropType.Key5:
if (doorToLock.Place == PlaceType.Door5)
flag = true;
break;
case PropType.Key6:
if (doorToLock.Place == PlaceType.Door6)
flag = true;
break;
default:
break;
}
if (flag) break;
}
if (!flag) return false;
3 => PropType.Key3,
5 => PropType.Key5,
_ => PropType.Key6,
};


characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor);
long threadNum = player.ThreadNum;
new Thread
(
() =>
{
new FrameRateTaskExecutor<int>(
loopCondition: () => flag && threadNum == player.ThreadNum && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor,
loopToDo: () =>
{
flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null);
doorToLock.OpenOrLockDegree += GameData.frameDuration * player.SpeedOfOpeningOrLocking;
},
timeInterval: GameData.frameDuration,
finallyReturn: () => 0
)
if (!player.UseTool(propType)) return false;


.Start();
if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor)
{
doorToLock.IsOpen = (!doorToLock.IsOpen);
}
if (threadNum == player.ThreadNum)
characterManager.SetPlayerState(player);
doorToLock.OpenOrLockDegree = 0;
}
long stateNum = player.SetPlayerState(PlayerStateType.OpeningTheDoor, doorToLock);
if (stateNum == -1)
{
player.ReleaseTool(propType);
return false;
}


)
new Thread
(
() =>
{
player.ThreadNum.WaitOne();
if (stateNum != player.StateNum)
{
player.ReleaseTool(propType);
player.ThreadNum.Release();
}
else
{
if (!doorToLock.TryOpen(player))
{
player.ReleaseTool(propType);
lock (player.ActionLock)
{
if (stateNum == player.StateNum)
{
player.SetPlayerStateNaturally();
player.ThreadNum.Release();
}
}
}
else
{
Thread.Sleep(GameData.degreeOfLockingOrOpeningTheDoor / player.SpeedOfOpeningOrLocking);

lock (player.ActionLock)
{
if (stateNum == player.StateNum)
{
player.SetPlayerStateNaturally();
doorToLock.StopOpen();
player.ReleaseTool(propType);
player.ThreadNum.Release();
}
}
}
}
}
)
{ IsBackground = true }.Start(); { IsBackground = true }.Start();


return true; return true;
@@ -440,33 +536,6 @@ namespace Gaming
} }
} }
*/ */
private object numLock = new object();
private int lastTime = 0;
private int numStop = 0;
private int NumStop => numStop;
private bool TryToStop()
{
lock (numLock)
{
int time = gameMap.Timer.nowTime();
if (time / GameData.frameDuration > lastTime)
{
lastTime = time / GameData.frameDuration;
numStop = 1;
return true;
}
else
{
if (numStop == GameData.LimitOfStopAndMove)
return false;
else
{
++numStop;
return true;
}
}
}
}


private readonly Map gameMap; private readonly Map gameMap;
private readonly CharacterManager characterManager; private readonly CharacterManager characterManager;
@@ -479,7 +548,37 @@ namespace Gaming
gameMap: gameMap, gameMap: gameMap,
OnCollision: (obj, collisionObj, moveVec) => OnCollision: (obj, collisionObj, moveVec) =>
{ {
SkillWhenColliding((Character)obj, collisionObj);
Character player = (Character)obj;
switch (collisionObj.Type)
{
case GameObjType.Bullet:

if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty)
{
if (CharacterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty) > 0)
player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty));
gameMap.Remove((GameObj)collisionObj);
}
break;
case GameObjType.Character:
if (player.FindActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed == 1 && ((Character)collisionObj).IsGhost())
{
if (CharacterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge) > 0)
player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge));
CharacterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge);
}
break;
case GameObjType.Item:
if (((Item)collisionObj).GetPropType() == PropType.CraftingBench)
{
((CraftingBench)collisionObj).TryStopSkill();
gameMap.Remove((Item)collisionObj);
}
break;
default:
break;
}

//Preparation.Utility.Debugger.Output(obj, " end move with " + collisionObj.ToString()); //Preparation.Utility.Debugger.Output(obj, " end move with " + collisionObj.ToString());
//if (collisionObj is Mine) //if (collisionObj is Mine)
//{ //{
@@ -490,6 +589,7 @@ namespace Gaming
}, },
EndMove: obj => EndMove: obj =>
{ {
obj.ThreadNum.Release();
// Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64); // Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64);
} }
); );


+ 59
- 34
logic/Gaming/AttackManager.cs View File

@@ -15,7 +15,7 @@ namespace Gaming
private class AttackManager private class AttackManager
{ {
readonly Map gameMap; readonly Map gameMap;
readonly MoveEngine moveEngine;
public readonly MoveEngine moveEngine;
readonly CharacterManager characterManager; readonly CharacterManager characterManager;


public AttackManager(Map gameMap, CharacterManager characterManager) public AttackManager(Map gameMap, CharacterManager characterManager)
@@ -33,12 +33,23 @@ namespace Gaming
Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64); Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64);
if (obj.CanMove && ((Bullet)obj).TypeOfBullet != BulletType.JumpyDumpty) if (obj.CanMove && ((Bullet)obj).TypeOfBullet != BulletType.JumpyDumpty)
BulletBomb((Bullet)obj, null); BulletBomb((Bullet)obj, null);
obj.CanMove = false;
obj.ReSetCanMove(false);
} }
); );
this.characterManager = characterManager; this.characterManager = characterManager;
} }


public void ProduceBulletNaturally(BulletType bulletType, Character player, double angle, XY pos)
{
// 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange
if (bulletType == BulletType.Null) return;
Bullet? bullet = BulletFactory.GetBullet(player, pos, bulletType);
if (bullet == null) return;
Debugger.Output(bullet, "Attack in " + pos.ToString());
gameMap.Add(bullet);
moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms
}

private void BombObj(Bullet bullet, GameObj objBeingShot) private void BombObj(Bullet bullet, GameObj objBeingShot)
{ {
#if DEBUG #if DEBUG
@@ -48,7 +59,7 @@ namespace Gaming
{ {
case GameObjType.Character: case GameObjType.Character:


if ((!(((Character)objBeingShot).IsGhost())) && bullet.Parent.IsGhost())
if ((!(((Character)objBeingShot).IsGhost())) && bullet.Parent!.IsGhost())
{ {
characterManager.BeAttacked((Student)objBeingShot, bullet); characterManager.BeAttacked((Student)objBeingShot, bullet);
} }
@@ -57,7 +68,18 @@ namespace Gaming
break; break;
case GameObjType.Generator: case GameObjType.Generator:
if (bullet.CanBeBombed(GameObjType.Generator)) if (bullet.CanBeBombed(GameObjType.Generator))
((Generator)objBeingShot).Repair(-bullet.AP * GameData.factorDamageGenerator, (Character)bullet.Parent);
((Generator)objBeingShot).Repair(-bullet.AP * GameData.factorDamageGenerator, (Character)bullet.Parent!);
break;
case GameObjType.Door:
if (bullet.CanBeBombed(GameObjType.Door))
((Door)objBeingShot).ForceToOpen();
break;
case GameObjType.Item:
if (((Item)objBeingShot).GetPropType() == PropType.CraftingBench)
{
((CraftingBench)objBeingShot).TryStopSkill();
gameMap.Remove(objBeingShot);
}
break; break;
default: default:
break; break;
@@ -66,9 +88,9 @@ namespace Gaming


public bool TryRemoveBullet(Bullet bullet) public bool TryRemoveBullet(Bullet bullet)
{ {
bullet.CanMove = false;
if (gameMap.Remove(bullet)) if (gameMap.Remove(bullet))
{ {
bullet.ReSetCanMove(false);
if (bullet.BulletBombRange > 0) if (bullet.BulletBombRange > 0)
{ {
BombedBullet bombedBullet = new(bullet); BombedBullet bombedBullet = new(bullet);
@@ -87,6 +109,17 @@ namespace Gaming
else return false; else return false;
} }


private void ProduceBombBomb(Bullet bullet, double angle)
{
angle += bullet.FacingDirection.Angle();
XY pos = bullet.Position + new XY
(
(int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)),
(int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle))
);
ProduceBulletNaturally(BulletType.JumpyDumpty, (Character)bullet.Parent!, angle, pos);
}

private void BulletBomb(Bullet bullet, GameObj? objBeingShot) private void BulletBomb(Bullet bullet, GameObj? objBeingShot)
{ {
#if DEBUG #if DEBUG
@@ -101,12 +134,12 @@ namespace Gaming
{ {
if (objBeingShot == null) if (objBeingShot == null)
{ {
characterManager.BackSwing((Character)bullet.Parent, bullet.Backswing);
characterManager.BackSwing((Character)bullet.Parent!, bullet.Backswing);
return; return;
} }


BombObj(bullet, objBeingShot); BombObj(bullet, objBeingShot);
characterManager.BackSwing((Character)bullet.Parent, bullet.RecoveryFromHit);
characterManager.BackSwing((Character)bullet.Parent!, bullet.RecoveryFromHit);
return; return;
} }


@@ -122,10 +155,14 @@ namespace Gaming


if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null) if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null)
{ {
bullet.Parent.BulletOfPlayer = BulletType.JumpyDumpty;
Debugger.Output(bullet.Parent, bullet.Parent.CharacterType.ToString() + " " + bullet.Parent.BulletNum.ToString());
Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI / 2.0);
Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI * 3.0 / 2.0);
ProduceBombBomb(bullet, 0);
ProduceBombBomb(bullet, Math.PI / 4);
ProduceBombBomb(bullet, Math.PI / 2);
ProduceBombBomb(bullet, Math.PI * 3 / 4);
ProduceBombBomb(bullet, Math.PI);
ProduceBombBomb(bullet, Math.PI * 5 / 4);
ProduceBombBomb(bullet, Math.PI * 3 / 2);
ProduceBombBomb(bullet, Math.PI * 7 / 4);
} }


var beAttackedList = new List<IGameObj>(); var beAttackedList = new List<IGameObj>();
@@ -159,45 +196,33 @@ namespace Gaming


if (objBeingShot == null) if (objBeingShot == null)
{ {
characterManager.BackSwing((Character)bullet.Parent, bullet.Backswing);
characterManager.BackSwing((Character)bullet.Parent!, bullet.Backswing);
} }
else else
characterManager.BackSwing((Character)bullet.Parent, bullet.RecoveryFromHit);
characterManager.BackSwing((Character)bullet.Parent!, bullet.RecoveryFromHit);
} }


public bool Attack(Character player, double angle) public bool Attack(Character player, double angle)
{ // 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange { // 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange
if (player.BulletOfPlayer == BulletType.Null)
return false;
Debugger.Output(player, player.CharacterType.ToString() + "Attack in " + player.BulletOfPlayer.ToString());

Debugger.Output(player, player.Position.ToString() + " " + player.Radius.ToString() + " " + BulletFactory.BulletRadius(player.BulletOfPlayer).ToString());
XY res = player.Position + new XY // 子弹紧贴人物生成。
(
(int)(Math.Abs((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Cos(angle))) * ((Math.Cos(angle) > 0) ? 1 : -1),
(int)(Math.Abs((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Sin(angle))) * ((Math.Sin(angle) > 0) ? 1 : -1)
);

Bullet? bullet = player.Attack(res, gameMap.GetPlaceType(res));
Bullet? bullet = player.Attack(angle, gameMap.Timer.nowTime());


if (bullet != null) if (bullet != null)
{ {
player.FacingDirection = new(angle, bullet.BulletAttackRange);
Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); Debugger.Output(bullet, "Attack in " + bullet.Position.ToString());
bullet.AP += player.TryAddAp() ? GameData.ApPropAdd : 0;
bullet.CanMove = true;
gameMap.Add(bullet); gameMap.Add(bullet);
moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(player.BulletOfPlayer)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms

moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms

if (bullet.CastTime > 0) if (bullet.CastTime > 0)
{ {
characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack);
long threadNum = player.ThreadNum;
player.SetPlayerState(PlayerStateType.TryingToAttack);
long threadNum = player.StateNum;


new Thread new Thread
(() => (() =>
{ {
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming,
loopToDo: () => loopToDo: () =>
{ {
}, },
@@ -209,9 +234,9 @@ namespace Gaming


if (gameMap.Timer.IsGaming) if (gameMap.Timer.IsGaming)
{ {
if (threadNum == player.ThreadNum)
if (threadNum == player.StateNum)
{ {
characterManager.SetPlayerState(player);
player.SetPlayerState();
} }
else TryRemoveBullet(bullet); else TryRemoveBullet(bullet);
} }


logic/Gaming/CharacterManager .cs → logic/Gaming/CharacterManager.cs View File

@@ -1,5 +1,4 @@
using System;
using System.Threading;
using System.Threading;
using GameClass.GameObj; using GameClass.GameObj;
using Preparation.Utility; using Preparation.Utility;
using Preparation.Interface; using Preparation.Interface;
@@ -18,42 +17,7 @@ namespace Gaming
this.gameMap = gameMap; this.gameMap = gameMap;
} }


public void SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
{
lock (player.MoveLock)
{
switch (player.PlayerState)
{
case PlayerStateType.OpeningTheChest:
((Chest)player.WhatInteractingWith).StopOpen();
player.ChangePlayerState(value, gameObj);
break;
case PlayerStateType.OpeningTheDoorway:
Doorway doorway = (Doorway)player.WhatInteractingWith;
doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime;
doorway.OpenStartTime = 0;
player.ChangePlayerState(value, gameObj);
break;
case PlayerStateType.Addicted:
if (value == PlayerStateType.Rescued)
player.ChangePlayerStateInOneThread(value, gameObj);
else
player.ChangePlayerState(value, gameObj);
break;
case PlayerStateType.Rescued:
if (value == PlayerStateType.Addicted)
player.ChangePlayerStateInOneThread(value, gameObj);
else
player.ChangePlayerState(value, gameObj);
break;
default:
player.ChangePlayerState(value, gameObj);
break;
}
}
}

public Character? AddPlayer(XY pos, int teamID, int playerID, CharacterType characterType, Character? parent = null)
public Character? AddPlayer(XY pos, long teamID, long playerID, CharacterType characterType, Character? parent = null)
{ {
Character newPlayer; Character newPlayer;


@@ -66,43 +30,39 @@ namespace Gaming


newPlayer.TeamID = teamID; newPlayer.TeamID = teamID;
newPlayer.PlayerID = playerID; newPlayer.PlayerID = playerID;
#region 人物装弹
new Thread
(
() =>
{
while (!gameMap.Timer.IsGaming)
Thread.Sleep(Math.Max(newPlayer.CD, GameData.checkInterval));
long lastTime = Environment.TickCount64;
new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting,
loopToDo: () =>
{
long nowTime = Environment.TickCount64;
if (newPlayer.BulletNum == newPlayer.MaxBulletNum)
lastTime = nowTime;
else if (nowTime - lastTime >= newPlayer.CD)
{
_ = newPlayer.TryAddBulletNum();
lastTime = nowTime;
}
},
timeInterval: GameData.checkInterval,
finallyReturn: () => 0
)
{
AllowTimeExceed = true/*,
MaxTolerantTimeExceedCount = 5,
TimeExceedAction = exceedTooMuch =>
{
if (exceedTooMuch) Console.WriteLine("The computer runs too slow that it cannot check the color below the player in time!");
}*/
}
.Start();
}
)
{ IsBackground = true }.Start();
#endregion
/* #region 人物装弹
new Thread
(
() =>
{
while (!gameMap.Timer.IsGaming)
Thread.Sleep(Math.Max(newPlayer.CD, GameData.checkInterval));
long lastTime = Environment.TickCount64;
new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved,
loopToDo: () =>
{
long nowTime = Environment.TickCount64;
if (newPlayer.BulletNum == newPlayer.MaxBulletNum)
lastTime = nowTime;
else if (nowTime - lastTime >= newPlayer.CD)
{
_ = newPlayer.TryAddBulletNum();
lastTime = nowTime;
}
},
timeInterval: GameData.checkInterval,
finallyReturn: () => 0
)
{
AllowTimeExceed = true,
}
.Start();
}
)
{ IsBackground = true }.Start();
#endregion
*/
#region BGM,牵制得分更新 #region BGM,牵制得分更新
new Thread new Thread
( (
@@ -137,7 +97,7 @@ namespace Gaming
} }
} }
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting,
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved,
loopToDo: () => loopToDo: () =>
{ {
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
@@ -168,7 +128,7 @@ namespace Gaming
newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position)); newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position));
else newPlayer.AddBgm(BgmType.GhostIsComing, 0); else newPlayer.AddBgm(BgmType.GhostIsComing, 0);
} }
if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && !newPlayer.NoHp() && newPlayer.PlayerState != PlayerStateType.Stunned && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange)
if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && newPlayer.CanPinDown() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange)
{ {
TimePinningDown += GameData.checkInterval; TimePinningDown += GameData.checkInterval;
newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded); newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded);
@@ -249,8 +209,8 @@ namespace Gaming
return; return;
} }
} }
SetPlayerState(player, PlayerStateType.Addicted);
long threadNum = player.ThreadNum;
player.SetPlayerState(PlayerStateType.Addicted);
long threadNum = player.StateNum;
new Thread new Thread
(() => (() =>
{ {
@@ -258,7 +218,7 @@ namespace Gaming
Debugger.Output(player, " is addicted "); Debugger.Output(player, " is addicted ");
#endif #endif
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
() => threadNum == player.ThreadNum && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming,
() => threadNum == player.StateNum && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming,
() => () =>
{ {
player.GamingAddiction += (player.PlayerState == PlayerStateType.Addicted) ? GameData.frameDuration : 0; player.GamingAddiction += (player.PlayerState == PlayerStateType.Addicted) ? GameData.frameDuration : 0;
@@ -280,29 +240,29 @@ namespace Gaming
{ IsBackground = true }.Start(); { IsBackground = true }.Start();
} }


public bool BeStunned(Character player, int time)
public static long BeStunned(Character player, int time)
{ {
if (player.PlayerState == PlayerStateType.Stunned || player.NoHp() || player.CharacterType == CharacterType.Robot) return false;
if (player.CharacterType == CharacterType.Robot) return -1;
long threadNum = player.SetPlayerState(PlayerStateType.Stunned);
if (threadNum == -1) return -1;
new Thread new Thread
(() => (() =>
{ {
SetPlayerState(player, PlayerStateType.Stunned);
long threadNum = player.ThreadNum;
Thread.Sleep(time); Thread.Sleep(time);
if (threadNum == player.ThreadNum)
SetPlayerState(player);
if (threadNum == player.StateNum)
player.SetPlayerState();
} }
) )
{ IsBackground = true }.Start(); { IsBackground = true }.Start();
return true;
return threadNum;
} }


public bool TryBeAwed(Student character, Bullet bullet) public bool TryBeAwed(Student character, Bullet bullet)
{ {
if (character.CanBeAwed()) if (character.CanBeAwed())
{ {
if (BeStunned(character, GameData.basicStunnedTimeOfStudent))
bullet.Parent.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.basicStunnedTimeOfStudent));
if (BeStunned(character, GameData.basicStunnedTimeOfStudent) > 0)
bullet.Parent!.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.basicStunnedTimeOfStudent));
return true; return true;
} }
return false; return false;
@@ -320,14 +280,16 @@ namespace Gaming
#if DEBUG #if DEBUG
Debugger.Output(student, "is being shot!"); Debugger.Output(student, "is being shot!");
#endif #endif
if (student.NoHp()) return; // 原来已经死了
if (!bullet.Parent.IsGhost()) return;
if (!bullet.Parent!.IsGhost()) return;


if (student.CharacterType == CharacterType.StraightAStudent) if (student.CharacterType == CharacterType.StraightAStudent)
{ {
((WriteAnswers)student.FindIActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation = 0;
((WriteAnswers)student.FindActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation = 0;
} }
student.SetDegreeOfTreatment0(); student.SetDegreeOfTreatment0();

if (student.NoHp()) return; // 原来已经死了

#if DEBUG #if DEBUG
Debugger.Output(bullet, " 's AP is " + bullet.AP.ToString()); Debugger.Output(bullet, " 's AP is " + bullet.AP.ToString());
#endif #endif
@@ -362,6 +324,11 @@ namespace Gaming
#endif #endif
} }
bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp)); bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp));
if (student.CharacterType == CharacterType.Teacher)
{
student.AddScore(subHp * GameData.factorOfScoreWhenTeacherAttacked / GameData.basicApOfGhost);
}

bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * subHp)); bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * subHp));
} }
if (student.HP <= 0) if (student.HP <= 0)
@@ -375,18 +342,22 @@ namespace Gaming
public bool BackSwing(Character player, int time) public bool BackSwing(Character player, int time)
{ {
if (time <= 0) return false; 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.ThreadNum;
long stateNum = player.SetPlayerState(PlayerStateType.Swinging);
if (stateNum == -1) return false;


new Thread new Thread
(() => (() =>
{ {
player.ThreadNum.WaitOne();
Thread.Sleep(time); Thread.Sleep(time);


if (threadNum == player.ThreadNum)
lock (player.ActionLock)
{ {
SetPlayerState(player);
if (stateNum == player.StateNum)
{
player.ThreadNum.Release();
player.SetPlayerStateNaturally();
}
} }
} }
) )
@@ -404,19 +375,28 @@ namespace Gaming


for (int i = 0; i < GameData.maxNumOfPropInPropInventory; i++) for (int i = 0; i < GameData.maxNumOfPropInPropInventory; i++)
{ {
Prop? prop = player.UseProp(i);
Gadget? prop = player.UseProp(i);
if (prop != null) if (prop != null)
{ {
prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position));
prop.ReSetPos(player.Position);
gameMap.Add(prop); gameMap.Add(prop);
} }
} }
if (player.CharacterType == CharacterType.Robot) if (player.CharacterType == CharacterType.Robot)
{ {
if (((Golem)player).Parent != null && ((Golem)player).Parent.CharacterType == CharacterType.TechOtaku)
var parent = ((Golem)player).Parent;
if (parent != null && parent.CharacterType == CharacterType.TechOtaku)
{ {
((SummonGolem)(((Golem)player).Parent.FindIActiveSkill(ActiveSkillType.SummonGolem))).GolemSummoned = null;
player.FindIActiveSkill(ActiveSkillType.UseRobot).IsBeingUsed = false;
((SummonGolem)(parent.FindActiveSkill(ActiveSkillType.SummonGolem))).DeleteGolem((int)(player.PlayerID - parent.PlayerID) / GameData.numOfPeople - 1);
UseRobot useRobot = (UseRobot)parent.FindActiveSkill(ActiveSkillType.UseRobot);
if (useRobot.TryResetNowPlayerID((int)player.PlayerID))
{
lock (parent.ActionLock)
{
if (parent.PlayerState == PlayerStateType.UsingSkill)
parent.SetPlayerState();
}
}
} }
return; return;
} }

+ 20
- 11
logic/Gaming/Game.cs View File

@@ -2,10 +2,8 @@
using System.Threading; using System.Threading;
using System.Collections.Generic; using System.Collections.Generic;
using Preparation.Utility; using Preparation.Utility;
using Timothy.FrameRateTask;
using Preparation.Interface; using Preparation.Interface;
using GameClass.GameObj; using GameClass.GameObj;
using System.Numerics;


namespace Gaming namespace Gaming
{ {
@@ -14,10 +12,10 @@ namespace Gaming
public struct PlayerInitInfo public struct PlayerInitInfo
{ {
public uint birthPointIndex; public uint birthPointIndex;
public int teamID;
public int playerID;
public long teamID;
public long playerID;
public CharacterType characterType; public CharacterType characterType;
public PlayerInitInfo(uint birthPointIndex, int teamID, int playerID, CharacterType characterType)
public PlayerInitInfo(uint birthPointIndex, long teamID, long playerID, CharacterType characterType)
{ {
this.birthPointIndex = birthPointIndex; this.birthPointIndex = birthPointIndex;
this.teamID = teamID; this.teamID = teamID;
@@ -162,7 +160,7 @@ namespace Gaming
Character? player = gameMap.FindPlayerToAction(playerID); Character? player = gameMap.FindPlayerToAction(playerID);
if (player != null) if (player != null)
{ {
return actionManager.Stop(player);
return ActionManager.Stop(player);
} }
return false; return false;
} }
@@ -199,14 +197,25 @@ namespace Gaming
} }
return false; return false;
} }
public bool LockOrOpenDoor(long playerID)
public bool LockDoor(long playerID)
{ {
if (!gameMap.Timer.IsGaming) if (!gameMap.Timer.IsGaming)
return false; return false;
Character? player = gameMap.FindPlayerToAction(playerID); Character? player = gameMap.FindPlayerToAction(playerID);
if (player != null) if (player != null)
{ {
return actionManager.LockOrOpenDoor(player);
return actionManager.LockDoor(player);
}
return false;
}
public bool OpenDoor(long playerID)
{
if (!gameMap.Timer.IsGaming)
return false;
Character? player = gameMap.FindPlayerToAction(playerID);
if (player != null)
{
return actionManager.OpenDoor(player);
} }
return false; return false;
} }
@@ -253,7 +262,7 @@ namespace Gaming
return false; return false;
} }


public bool UseActiveSkill(long playerID, int skillNum)
public bool UseActiveSkill(long playerID, int skillNum, int parameter)
{ {
if (!gameMap.Timer.IsGaming) if (!gameMap.Timer.IsGaming)
return false; return false;
@@ -261,7 +270,7 @@ namespace Gaming
if (player != null) if (player != null)
{ {
if (player.Occupation.ListOfIActiveSkill.Count <= skillNum) return false; if (player.Occupation.ListOfIActiveSkill.Count <= skillNum) return false;
return skillManager.UseActiveSkill(player, player.Occupation.ListOfIActiveSkill[skillNum]);
return skillManager.UseActiveSkill(player, player.Occupation.ListOfIActiveSkill[skillNum], parameter);
} }
else else
return false; return false;
@@ -316,7 +325,7 @@ namespace Gaming
{ {
foreach (Character player in gameMap.GameObjDict[GameObjType.Character]) foreach (Character player in gameMap.GameObjDict[GameObjType.Character])
{ {
player.CanMove = false;
player.ReSetCanMove(false);
} }
} }
gameMap.GameObjDict[keyValuePair.Key].Clear(); gameMap.GameObjDict[keyValuePair.Key].Clear();


+ 19
- 22
logic/Gaming/PropManager.cs View File

@@ -5,8 +5,6 @@ using Preparation.Utility;
using System; using System;
using Timothy.FrameRateTask; using Timothy.FrameRateTask;
using GameEngine; using GameEngine;
using System.Numerics;
using System.Reflection;


namespace Gaming namespace Gaming
{ {
@@ -21,9 +19,9 @@ namespace Gaming


public void UseProp(Character player, PropType propType) public void UseProp(Character player, PropType propType)
{ {
if (player.IsResetting || player.CharacterType == CharacterType.Robot)
if (player.CharacterType == CharacterType.Robot || player.IsRemoved)
return; return;
Prop prop = player.UseProp(propType);
Gadget prop = player.UseProp(propType);
switch (prop.GetPropType()) switch (prop.GetPropType())
{ {
case PropType.ShieldOrSpear: case PropType.ShieldOrSpear:
@@ -54,7 +52,7 @@ namespace Gaming
else player.AddAp(GameData.PropDuration); else player.AddAp(GameData.PropDuration);
break; break;
case PropType.RecoveryFromDizziness: case PropType.RecoveryFromDizziness:
if (player.PlayerState == PlayerStateType.Stunned)
if (player.PlayerState == PlayerStateType.Stunned || player.PlayerState == PlayerStateType.Charmed)
{ {
player.AddScore(GameData.ScorePropRecoverFromDizziness); player.AddScore(GameData.ScorePropRecoverFromDizziness);
player.SetPlayerStateNaturally(); player.SetPlayerStateNaturally();
@@ -73,23 +71,22 @@ namespace Gaming
/// <returns></returns> /// <returns></returns>
public bool PickProp(Character player, PropType propType = PropType.Null) public bool PickProp(Character player, PropType propType = PropType.Null)
{ {
if (player.IsResetting)
return false;
if (!player.Commandable()) return false;
int indexing = player.IndexingOfAddProp(); int indexing = player.IndexingOfAddProp();
if (indexing == GameData.maxNumOfPropInPropInventory) if (indexing == GameData.maxNumOfPropInPropInventory)
return false; return false;


Prop pickProp = new NullProp();
Gadget pickProp = new NullProp();
if (propType == PropType.Null) // 自动检查有无道具可捡 if (propType == PropType.Null) // 自动检查有无道具可捡
{ {
pickProp = player.PropInventory[indexing] = ((Prop?)gameMap.OneInTheSameCell(player.Position, GameObjType.Prop)) ?? new NullProp();
pickProp = player.PropInventory[indexing] = ((Gadget?)gameMap.OneInTheSameCell(player.Position, GameObjType.Gadget)) ?? new NullProp();
} }
else else
{ {
gameMap.GameObjLockDict[GameObjType.Prop].EnterReadLock();
gameMap.GameObjLockDict[GameObjType.Gadget].EnterReadLock();
try try
{ {
foreach (Prop prop in gameMap.GameObjDict[GameObjType.Prop])
foreach (Gadget prop in gameMap.GameObjDict[GameObjType.Gadget])
{ {
if (prop.GetPropType() == propType) if (prop.GetPropType() == propType)
{ {
@@ -102,14 +99,14 @@ namespace Gaming
} }
finally finally
{ {
gameMap.GameObjLockDict[GameObjType.Prop].ExitReadLock();
gameMap.GameObjLockDict[GameObjType.Gadget].ExitReadLock();
} }
} }


if (pickProp.GetPropType() != PropType.Null) if (pickProp.GetPropType() != PropType.Null)
{ {
gameMap.Remove(pickProp);
gameMap.Add(new PickedProp(pickProp));
gameMap.RemoveJustFromMap(pickProp);
//gameMap.Add(new Item(pickProp));
return true; return true;
} }
else else
@@ -118,19 +115,19 @@ namespace Gaming


public void ThrowProp(Character player, PropType propType) public void ThrowProp(Character player, PropType propType)
{ {
if (!gameMap.Timer.IsGaming || player.IsResetting)
if (!gameMap.Timer.IsGaming || player.IsRemoved)
return; return;
Prop prop = player.UseProp(propType);
Gadget prop = player.UseProp(propType);
if (prop.GetPropType() == PropType.Null) if (prop.GetPropType() == PropType.Null)
return; return;


prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position));
prop.ReSetPos(player.Position);
gameMap.Add(prop); gameMap.Add(prop);
} }


private Prop ProduceOnePropNotKey(Random r, XY Pos)
private static Gadget ProduceOnePropNotKey(Random r, XY Pos)
{ {
return PropFactory.GetProp((PropType)r.Next(GameData.numOfTeachingBuilding + 1, GameData.numOfPropSpecies + 1), Pos, gameMap.GetPlaceType(Pos));
return PropFactory.GetConsumables((PropType)r.Next(GameData.numOfTeachingBuilding + 1, GameData.numOfPropSpecies + 1), Pos);
} }


private Chest GetChest(Random r) private Chest GetChest(Random r)
@@ -153,7 +150,7 @@ namespace Gaming
{ {
++cou; ++cou;
Chest chest = GetChest(r); Chest chest = GetChest(r);
chest.PropInChest[1] = new Key3(chest.Position, PlaceType.Chest);
chest.PropInChest[1] = new Key3(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position); chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
} }
cou = 0; cou = 0;
@@ -161,7 +158,7 @@ namespace Gaming
{ {
++cou; ++cou;
Chest chest = GetChest(r); Chest chest = GetChest(r);
chest.PropInChest[1] = new Key5(chest.Position, PlaceType.Chest);
chest.PropInChest[1] = new Key5(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position); chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
} }
cou = 0; cou = 0;
@@ -169,7 +166,7 @@ namespace Gaming
{ {
++cou; ++cou;
Chest chest = GetChest(r); Chest chest = GetChest(r);
chest.PropInChest[1] = new Key6(chest.Position, PlaceType.Chest);
chest.PropInChest[1] = new Key6(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position); chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
} }




+ 225
- 55
logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs View File

@@ -13,9 +13,8 @@ namespace Gaming
{ {
public static bool CanBeginToCharge(Character player) public static bool CanBeginToCharge(Character player)
{ {

if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
IActiveSkill skill = player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge);
ActiveSkill skill = player.FindActiveSkill(ActiveSkillType.CanBeginToCharge);
Debugger.Output(player, "can begin to charge!"); Debugger.Output(player, "can begin to charge!");




@@ -31,7 +30,7 @@ namespace Gaming
public bool ShowTime(Character player) public bool ShowTime(Character player)
{ {
if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
IActiveSkill skill = player.FindIActiveSkill(ActiveSkillType.ShowTime);
ActiveSkill skill = player.FindActiveSkill(ActiveSkillType.ShowTime);


return ActiveSkillEffect(skill, player, () => return ActiveSkillEffect(skill, player, () =>
{ {
@@ -76,6 +75,7 @@ namespace Gaming
} }
}, },
timeInterval: GameData.checkIntervalWhenShowTime, timeInterval: GameData.checkIntervalWhenShowTime,
maxTotalDuration: skill.DurationTime,
finallyReturn: () => 0 finallyReturn: () => 0
) )


@@ -92,34 +92,70 @@ namespace Gaming


public static bool BecomeInvisible(Character player) public static bool BecomeInvisible(Character player)
{ {
if ((!player.Commandable())) return false;
IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.BecomeInvisible);
ActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.BecomeInvisible);
long stateNum = player.SetPlayerState(PlayerStateType.UsingSkill);
if (stateNum == -1)
{
return false;
}
return ActiveSkillEffect(activeSkill, player, () => return ActiveSkillEffect(activeSkill, player, () =>
{ {
player.AddScore(GameData.ScoreBecomeInvisible); player.AddScore(GameData.ScoreBecomeInvisible);
player.AddInvisible(activeSkill.DurationTime); player.AddInvisible(activeSkill.DurationTime);
Debugger.Output(player, "become invisible!"); Debugger.Output(player, "become invisible!");
}, },
() =>
{ });
() =>
{
lock (player.ActionLock)
{
if (stateNum == player.StateNum)
{
player.SetPlayerStateNaturally();
}
}
}
);
} }


public bool UseRobot(Character player)
public static bool UseRobot(Character player, int robotID)
{ {
IGolem? golem = (IGolem?)(((SummonGolem)player.FindIActiveSkill(ActiveSkillType.SummonGolem)).GolemSummoned);
if ((!player.Commandable()) || ((SummonGolem)player.FindIActiveSkill(ActiveSkillType.SummonGolem)).GolemSummoned == null) return false;
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);
return true;
if ((robotID - player.PlayerID) % GameData.numOfPeople != 0) return false;
if ((robotID - (int)player.PlayerID) / GameData.numOfPeople < 0 || (robotID - (int)player.PlayerID) / GameData.numOfPeople > GameData.maxSummonedGolemNum) return false;
UseRobot activeSkill = (UseRobot)player.FindActiveSkill(ActiveSkillType.UseRobot);
lock (activeSkill.ActiveSkillUseLock)
{
if (robotID == player.PlayerID)
{
lock (player.ActionLock)
{
if (player.PlayerState == PlayerStateType.UsingSkill && player.WhatInteractingWith == null)
player.SetPlayerStateNaturally();
activeSkill.NowPlayerID = robotID;
}
}
else
{
SummonGolem summonGolemSkill = (SummonGolem)player.FindActiveSkill(ActiveSkillType.SummonGolem);
if (summonGolemSkill.GolemStateArray[(robotID - (int)player.PlayerID) / GameData.numOfPeople - 1] == 2)
{
activeSkill.NowPlayerID = robotID;
}
else return false;
long stateNum = player.SetPlayerState(PlayerStateType.UsingSkill);
if (stateNum == -1)
{
activeSkill.NowPlayerID = (int)player.PlayerID;
return false;
}
}
return ActiveSkillEffect(activeSkill, player, () => { }, () => { });
}
} }


public static bool JumpyBomb(Character player) public static bool JumpyBomb(Character player)
{ {
if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.JumpyBomb), player, () =>
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.JumpyBomb), player, () =>
{ {
player.BulletOfPlayer = BulletType.BombBomb; player.BulletOfPlayer = BulletType.BombBomb;
Debugger.Output(player, "uses jumpybomb!"); Debugger.Output(player, "uses jumpybomb!");
@@ -128,10 +164,66 @@ namespace Gaming
{ player.BulletOfPlayer = player.OriBulletOfPlayer; }); { player.BulletOfPlayer = player.OriBulletOfPlayer; });
} }


public bool SparksNSplash(Character player, int AttackID)
{
Character? whoAttacked = gameMap.FindPlayer(AttackID);
if (whoAttacked == null || whoAttacked.NoHp()) return false;
ActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.SparksNSplash);

return ActiveSkillEffect(activeSkill, player, () =>
{
new Thread
(
() =>
{
Bullet? homingMissile = null;
double dis;
new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !whoAttacked.NoHp(),
loopToDo: () =>
{
dis = ((homingMissile == null || homingMissile.IsRemoved) ? double.MaxValue : XY.DistanceFloor3(homingMissile.Position, whoAttacked.Position));
gameMap.GameObjLockDict[GameObjType.Bullet].EnterReadLock();
try
{
foreach (Bullet bullet in gameMap.GameObjDict[GameObjType.Bullet])
{
if (!bullet.CanMove && XY.DistanceFloor3(bullet.Position, whoAttacked.Position) < dis && bullet.TypeOfBullet == BulletType.JumpyDumpty)
{
homingMissile = bullet;
dis = XY.DistanceFloor3(bullet.Position, whoAttacked.Position);
}
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Bullet].ExitReadLock();
}
if (homingMissile != null)
{
homingMissile.ReSetCanMove(true);
attackManager.moveEngine.MoveObj(homingMissile, GameData.checkIntervalWhenSparksNSplash - 1, (whoAttacked.Position - homingMissile.Position).Angle(), ++homingMissile.StateNum);
}
},
timeInterval: GameData.checkIntervalWhenSparksNSplash,
maxTotalDuration: activeSkill.DurationTime,
finallyReturn: () => 0
)

.Start();
}
)
{ IsBackground = true }.Start();
Debugger.Output(player, "uses sparks n splash!");
},
() =>
{ });
}

public bool WriteAnswers(Character player) public bool WriteAnswers(Character player)
{ {
if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.WriteAnswers);
ActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.WriteAnswers);
return ActiveSkillEffect(activeSkill, player, () => return ActiveSkillEffect(activeSkill, player, () =>
{ {
Generator? generator = (Generator?)gameMap.OneForInteract(player.Position, GameObjType.Generator); Generator? generator = (Generator?)gameMap.OneForInteract(player.Position, GameObjType.Generator);
@@ -149,25 +241,66 @@ namespace Gaming


public bool SummonGolem(Character player) public bool SummonGolem(Character player)
{ {
if ((!player.Commandable())) return false;
IActiveSkill activeSkill = player.FindIActiveSkill(ActiveSkillType.SummonGolem);
if (((SummonGolem)activeSkill).GolemSummoned != null) return false;
ActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.SummonGolem);
int num = ((SummonGolem)activeSkill).BuildGolem();
if (num >= GameData.maxSummonedGolemNum) return false;

XY res = player.Position + new XY(player.FacingDirection, player.Radius * 2); XY res = player.Position + new XY(player.FacingDirection, player.Radius * 2);
if (actionManager.moveEngine.CheckCollision(player, res) != null)
return false;
Golem? golem = (Golem?)characterManager.AddPlayer(res, player.TeamID, player.PlayerID + GameData.numOfPeople, CharacterType.Robot, player);
if (golem == null) return false;
((SummonGolem)activeSkill).GolemSummoned = golem;
return ActiveSkillEffect(activeSkill, player, () =>
lock (activeSkill.ActiveSkillUseLock)
{ {
},
() =>
{ });
CraftingBench craftingBench = new(res, player, num);

long stateNum = player.SetPlayerState(PlayerStateType.UsingSkill, craftingBench);
if (stateNum == -1)
{
((SummonGolem)activeSkill).DeleteGolem(num);
return false;
}

player.ThreadNum.WaitOne();
if (stateNum != player.StateNum)
{
((SummonGolem)activeSkill).DeleteGolem(num);
player.ThreadNum.Release();
return false;
}

if (actionManager.moveEngine.CheckCollision(craftingBench, res) != null)
{
((SummonGolem)activeSkill).DeleteGolem(num);
player.ThreadNum.Release();
return false;
}
craftingBench.ParentStateNum = stateNum;
gameMap.Add(craftingBench);

return ActiveSkillEffect(activeSkill, player, () =>
{
},
() =>
{
lock (player.ActionLock)
{
if (stateNum == player.StateNum)
{
gameMap.RemoveJustFromMap(craftingBench);
Golem? golem = (Golem?)characterManager.AddPlayer(res, player.TeamID, (num + 1) * GameData.numOfPeople + player.PlayerID, CharacterType.Robot, player);
if (golem == null)
{
((SummonGolem)activeSkill).AddGolem(num);
}
player.SetPlayerStateNaturally();
player.ThreadNum.Release();
}
}
}
);
}
} }


public static bool UseKnife(Character player) public static bool UseKnife(Character player)
{ {
return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.UseKnife), player, () =>
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.UseKnife), player, () =>
{ {
player.BulletOfPlayer = BulletType.FlyingKnife; player.BulletOfPlayer = BulletType.FlyingKnife;
Debugger.Output(player, "uses flyingknife!"); Debugger.Output(player, "uses flyingknife!");
@@ -179,7 +312,7 @@ namespace Gaming
public bool Howl(Character player) public bool Howl(Character player)
{ {
if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Howl), player, () =>
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Howl), player, () =>
{ {
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try try
@@ -188,7 +321,7 @@ namespace Gaming
{ {
if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange) if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange)
{ {
if (characterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl))
if (CharacterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl) > 0)
player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl)); player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl));
} }
} }
@@ -207,7 +340,7 @@ namespace Gaming
public bool Punish(Character player) public bool Punish(Character player)
{ {
if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Punish), player, () =>
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Punish), player, () =>
{ {
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try try
@@ -216,11 +349,13 @@ namespace Gaming
{ {
if (character.IsGhost() && if (character.IsGhost() &&
(character.PlayerState == PlayerStateType.TryingToAttack || character.PlayerState == PlayerStateType.Swinging (character.PlayerState == PlayerStateType.TryingToAttack || character.PlayerState == PlayerStateType.Swinging
|| character.PlayerState == PlayerStateType.UsingSkill || character.PlayerState == PlayerStateType.LockingOrOpeningTheDoor || character.PlayerState == PlayerStateType.ClimbingThroughWindows)
&& gameMap.CanSee(player, character))
|| character.PlayerState == PlayerStateType.UsingSkill
|| character.PlayerState == PlayerStateType.LockingTheDoor || character.PlayerState == PlayerStateType.OpeningTheDoor
|| character.PlayerState == PlayerStateType.ClimbingThroughWindows)
&& XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange / 3)
{ {
if (characterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)))
player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)));
if (CharacterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP) / GameData.basicApOfGhost) > 0)
player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP) / GameData.basicApOfGhost));
break; break;
} }
} }
@@ -235,10 +370,45 @@ namespace Gaming
{ }); { });
} }


public bool HaveTea(Character player, int angle1000)
{
long stateNum = player.SetPlayerState(PlayerStateType.UsingSkill);
if (stateNum == -1)
{
return false;
}
player.ThreadNum.WaitOne();

XY res = player.Position + new XY(angle1000 / 1000.0, GameData.distanceOfHaveTea);
Debugger.Output(res.ToString());
if (actionManager.moveEngine.CheckCollision(player, res) != null)
{
player.ThreadNum.Release();
return false;
}
Debugger.Output("NO Collision!");
player.ReSetPos(res);
lock (player.ActionLock)
{
if (player.StateNum == stateNum)
{
player.SetPlayerStateNaturally();
}
}
player.ThreadNum.Release();

return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.HaveTea), player, () =>
{
Debugger.Output(player, "have tea!");
},
() =>
{ });
}

public bool Rouse(Character player) public bool Rouse(Character player)
{ {
if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Rouse), player, () =>
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Rouse), player, () =>
{ {
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try try
@@ -247,7 +417,7 @@ namespace Gaming
{ {
if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character))
{ {
characterManager.SetPlayerState(character);
character.SetPlayerState();
character.HP = GameData.RemainHpWhenAddLife; character.HP = GameData.RemainHpWhenAddLife;
((Student)character).TimeOfRescue = 0; ((Student)character).TimeOfRescue = 0;
player.AddScore(GameData.StudentScoreRescue); player.AddScore(GameData.StudentScoreRescue);
@@ -268,7 +438,7 @@ namespace Gaming
public bool Encourage(Character player) public bool Encourage(Character player)
{ {
if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Encourage), player, () =>
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Encourage), player, () =>
{ {
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try try
@@ -297,14 +467,14 @@ namespace Gaming
public bool Inspire(Character player) public bool Inspire(Character player)
{ {
if ((!player.Commandable())) return false; if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindIActiveSkill(ActiveSkillType.Inspire), player, () =>
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Inspire), player, () =>
{ {
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try try
{ {
foreach (Character character in gameMap.GameObjDict[GameObjType.Character]) foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
{ {
if (gameMap.CanSee(player, character))
if (gameMap.CanSee(player, character) && !character.IsGhost())
{ {
player.AddScore(GameData.ScoreInspire); player.AddScore(GameData.ScoreInspire);
character.AddMoveSpeed(GameData.timeOfAddingSpeedWhenInspire, GameData.addedTimeOfSpeedWhenInspire); character.AddMoveSpeed(GameData.timeOfAddingSpeedWhenInspire, GameData.addedTimeOfSpeedWhenInspire);
@@ -321,24 +491,24 @@ namespace Gaming
{ }); { });
} }


public static bool ActiveSkillEffect(IActiveSkill activeSkill, Character player, Action startSkill, Action endSkill)
public static bool ActiveSkillEffect(ActiveSkill activeSkill, Character player, Action startSkill, Action endSkill)
{ {
lock (activeSkill.ActiveSkillLock)
lock (activeSkill.ActiveSkillUseLock)
{ {
ActiveSkillType activeSkillType = SkillFactory.FindActiveSkillType(activeSkill);
if (player.TimeUntilActiveSkillAvailable[activeSkillType] == 0)
if (activeSkill.TimeUntilActiveSkillAvailable == 0)
{ {
player.SetTimeUntilActiveSkillAvailable(activeSkillType, activeSkill.SkillCD);
activeSkill.TimeUntilActiveSkillAvailable = activeSkill.SkillCD;

new Thread new Thread
(() => (() =>
{ {
startSkill(); startSkill();
activeSkill.IsBeingUsed = true;
activeSkill.IsBeingUsed = 1;
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
() => !player.IsResetting,
() => !player.IsRemoved,
() => () =>
{ {
player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration);
activeSkill.TimeUntilActiveSkillAvailable -= (int)GameData.frameDuration;
}, },
timeInterval: GameData.frameDuration, timeInterval: GameData.frameDuration,
() => 0, () => 0,
@@ -351,14 +521,14 @@ namespace Gaming
.Start(); .Start();


endSkill(); endSkill();
activeSkill.IsBeingUsed = false;
activeSkill.IsBeingUsed = 0;
Debugger.Output(player, "return to normal."); Debugger.Output(player, "return to normal.");


new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => player.TimeUntilActiveSkillAvailable[activeSkillType] > 0 && !player.IsResetting,
loopCondition: () => activeSkill.TimeUntilActiveSkillAvailable > 0,
loopToDo: () => loopToDo: () =>
{ {
player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration);
activeSkill.TimeUntilActiveSkillAvailable -= (int)GameData.frameDuration;
}, },
timeInterval: GameData.frameDuration, timeInterval: GameData.frameDuration,
finallyReturn: () => 0 finallyReturn: () => 0
@@ -369,7 +539,7 @@ namespace Gaming
} }
.Start(); .Start();


player.SetTimeUntilActiveSkillAvailable(activeSkillType, 0);
activeSkill.TimeUntilActiveSkillAvailable = 0;
Debugger.Output(player, "ActiveSkill is ready."); Debugger.Output(player, "ActiveSkill is ready.");
} }
) )


+ 6
- 2
logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs View File

@@ -15,14 +15,14 @@ namespace Gaming // 被动技能开局时就释放,持续到游戏结束
public void Meditate(Character player) public void Meditate(Character player)
{ {
const int learningDegree = GameData.basicFixSpeed / 4; const int learningDegree = GameData.basicFixSpeed / 4;
WriteAnswers activeSkill = (WriteAnswers)player.FindIActiveSkill(ActiveSkillType.WriteAnswers);
WriteAnswers activeSkill = (WriteAnswers)player.FindActiveSkill(ActiveSkillType.WriteAnswers);
new Thread new Thread
( (
() => () =>
{ {
new FrameRateTaskExecutor<int> new FrameRateTaskExecutor<int>
( (
() => gameMap.Timer.IsGaming && !player.IsResetting,
() => gameMap.Timer.IsGaming && !player.IsRemoved,
() => () =>
{ {
if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation += learningDegree * GameData.frameDuration; if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation += learningDegree * GameData.frameDuration;
@@ -53,6 +53,10 @@ namespace Gaming // 被动技能开局时就释放,持续到游戏结束
) )
{ IsBackground = true }.Start(); { IsBackground = true }.Start();
} }
public void Lucky(Character player)
{
player.PropInventory[0] = PropFactory.GetConsumables((PropType)((4 * Environment.TickCount) % 5 + 4), new XY(0, 0));
}
} }
} }
} }

+ 11
- 4
logic/Gaming/SkillManager/SkillManager.cs View File

@@ -11,7 +11,7 @@ namespace Gaming
readonly SkillManager skillManager; readonly SkillManager skillManager;
private partial class SkillManager private partial class SkillManager
{ {
public bool UseActiveSkill(Character character, ActiveSkillType activeSkillType)
public bool UseActiveSkill(Character character, ActiveSkillType activeSkillType, int parameter)
{ {
if (character.Occupation.ListOfIActiveSkill.Contains(activeSkillType)) if (character.Occupation.ListOfIActiveSkill.Contains(activeSkillType))
switch (activeSkillType) switch (activeSkillType)
@@ -30,14 +30,18 @@ namespace Gaming
return Encourage(character); return Encourage(character);
case ActiveSkillType.Punish: case ActiveSkillType.Punish:
return Punish(character); return Punish(character);
case ActiveSkillType.HaveTea:
return HaveTea(character, parameter);
case ActiveSkillType.JumpyBomb: case ActiveSkillType.JumpyBomb:
return JumpyBomb(character); return JumpyBomb(character);
case ActiveSkillType.SparksNSplash:
return SparksNSplash(character, parameter);
case ActiveSkillType.WriteAnswers: case ActiveSkillType.WriteAnswers:
return WriteAnswers(character); return WriteAnswers(character);
case ActiveSkillType.SummonGolem: case ActiveSkillType.SummonGolem:
return SummonGolem(character); return SummonGolem(character);
case ActiveSkillType.UseRobot: case ActiveSkillType.UseRobot:
return UseRobot(character);
return UseRobot(character, parameter);
case ActiveSkillType.Rouse: case ActiveSkillType.Rouse:
return Rouse(character); return Rouse(character);
case ActiveSkillType.ShowTime: case ActiveSkillType.ShowTime:
@@ -47,7 +51,7 @@ namespace Gaming
} }
return false; return false;
} }
public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType)
/*public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType)
{ {
if (character.Occupation.ListOfIPassiveSkill.Contains(passiveSkillType)) if (character.Occupation.ListOfIPassiveSkill.Contains(passiveSkillType))
switch (passiveSkillType) switch (passiveSkillType)
@@ -59,7 +63,7 @@ namespace Gaming
return; return;
} }
return; return;
}
}*/
public void UseAllPassiveSkill(Character character) public void UseAllPassiveSkill(Character character)
{ {
foreach (var passiveSkill in character.Occupation.ListOfIPassiveSkill) foreach (var passiveSkill in character.Occupation.ListOfIPassiveSkill)
@@ -68,6 +72,9 @@ namespace Gaming
case PassiveSkillType.Meditate: case PassiveSkillType.Meditate:
Meditate(character); Meditate(character);
break; break;
case PassiveSkillType.Lucky:
Lucky(character);
break;
default: default:
return; return;
} }


+ 7
- 6
logic/Preparation/Interface/ICharacter.cs View File

@@ -3,18 +3,19 @@ using Preparation.Utility;


namespace Preparation.Interface namespace Preparation.Interface
{ {
public interface ICharacter : IGameObj
public interface ICharacter : IMoveable
{ {
public int TeamID { get; }
public long TeamID { get; }
public int HP { get; set; } public int HP { get; set; }
public int Score { get; }
public void AddScore(int add);
public long Score { get; }
public void AddScore(long add);
public double Vampire { get; } public double Vampire { get; }
public PlayerStateType PlayerState { get; } public PlayerStateType PlayerState { get; }
public BulletType BulletOfPlayer { get; set; } public BulletType BulletOfPlayer { get; set; }
public CharacterType CharacterType { get; } public CharacterType CharacterType { get; }
public int BulletNum { get; }
public long ThreadNum { get; }
public ActiveSkill FindActiveSkill(ActiveSkillType activeSkillType);
public int UpdateBulletNum(int time);
public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null);


public bool IsGhost(); public bool IsGhost();
} }


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

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save