| @@ -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,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,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 | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| }; | }; | ||||
| @@ -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 | ||||
| @@ -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; | ||||
| @@ -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{ | ||||
| @@ -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 | ||||
| @@ -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() | ||||
| @@ -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(); | ||||
| } | } | ||||
| @@ -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) + ", "; | ||||
| @@ -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; | |||||
| } | |||||
| }; | }; | ||||
| // 连接服务器 | // 连接服务器 | ||||
| @@ -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; | ||||
| } | } | ||||
| @@ -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 等并不一致或不完全一致,增加各种不兼容问题的风险 | |||||
| ## 开发人员 | ## 开发人员 | ||||
| - ......(自己加) | - ......(自己加) | ||||
| @@ -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 | ||||
| @@ -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_) | ||||
| ); | ); | ||||
| @@ -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__ | ||||
| @@ -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; | ||||
| @@ -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(); | ||||
| @@ -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 | ||||
| { | { | ||||
| @@ -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模式下有效 | ||||
| @@ -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 | ||||
| @@ -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,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 | ||||
| @@ -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] | ||||
| @@ -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 | |||||
| @@ -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() | ||||
| @@ -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) | ||||
| @@ -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 | ||||
| @@ -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 | |||||
| ) | |||||
| @@ -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,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,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,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" | |||||
| @@ -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),等等。 | ||||
| @@ -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 | ||||
| @@ -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; | |||||
| } | } | ||||
| @@ -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,为了使发送的信息尽可能不被浪费,暂定不发这类大包。 | ||||
| @@ -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; | ||||
| } | } | ||||
| @@ -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"> | ||||
| @@ -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}") | |||||
| @@ -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 | ||||
| @@ -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}'}' | |||||
| @@ -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; | ||||
| @@ -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 | ||||
| # 等待下一帧 | # 等待下一帧 | ||||
| @@ -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 @@ $$ | |||||
| - 开锁门进度中断后清空 | - 开锁门进度中断后清空 | ||||
| ### 窗 | ### 窗 | ||||
| - 由于窗户被占用导致翻窗失败会使先前行动停止 | |||||
| - 翻越窗户是一种交互行为,翻窗一共有两个过程 | - 翻越窗户是一种交互行为,翻窗一共有两个过程 | ||||
| - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 | - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 | ||||
| - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 | - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 | ||||
| @@ -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 | | ||||
| * 捣蛋鬼 | * 捣蛋鬼 | ||||
| @@ -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倍速" | |||||
| @@ -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:游戏机制与平衡性调整已完成 | |||||
| @@ -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> | ||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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"); | |||||
| } | } | ||||
| })); | })); | ||||
| } | } | ||||
| @@ -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> | ||||
| @@ -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 } }; | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -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; | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -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" | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| @@ -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> | ||||
| @@ -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; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| } | } | ||||
| @@ -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 | ||||
| @@ -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 | ||||
| { | { | ||||
| @@ -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!"); | ||||
| } | } | ||||
| @@ -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); | ||||
| @@ -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; | ||||
| } | } | ||||
| @@ -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; | ||||
| } | } | ||||
| } | } | ||||
| @@ -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); | ||||
| @@ -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) | |||||
| { | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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; | ||||
| @@ -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(); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -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); | |||||
| } | } | ||||
| } | } | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| @@ -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) | ||||
| { | { | ||||
| @@ -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, },*/ | |||||
| @@ -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; | ||||
| @@ -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; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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) | ||||
| { | { | ||||
| } | } | ||||
| @@ -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; | ||||
| } | } | ||||
| @@ -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; | ||||
| @@ -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(); | ||||
| } | } | ||||
| @@ -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 | |||||
| } | |||||
| @@ -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; | ||||
| } | } | ||||
| } | |||||
| }*/ | |||||
| } | } | ||||
| @@ -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(); | ||||
| @@ -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); | ||||
| } | } | ||||
| ); | ); | ||||
| @@ -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); | ||||
| } | } | ||||
| @@ -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; | ||||
| } | } | ||||
| @@ -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(); | ||||
| @@ -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); | ||||
| } | } | ||||
| @@ -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."); | ||||
| } | } | ||||
| ) | ) | ||||
| @@ -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,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; | ||||
| } | } | ||||
| @@ -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(); | ||||
| } | } | ||||
| @@ -0,0 +1,9 @@ | |||||
| using Preparation.Utility; | |||||
| namespace Preparation.Interface | |||||
| { | |||||
| public interface IChest : IGameObj | |||||
| { | |||||
| public void StopOpen(); | |||||
| } | |||||
| } | |||||