| @@ -0,0 +1,24 @@ | |||
| name: build | |||
| on: [push, pull_request] | |||
| jobs: | |||
| dotnet-build: | |||
| runs-on: windows-latest | |||
| steps: | |||
| - uses: actions/checkout@v3 | |||
| - name: Setup .NET Core | |||
| uses: actions/setup-dotnet@v3 | |||
| with: | |||
| dotnet-version: 6.0.x | |||
| - name: Build Logic | |||
| run: dotnet build "./logic/logic.sln" -c Release | |||
| - name: Build Installer | |||
| run: dotnet build "./installer/installer.sln" -c Release | |||
| - name: Build Launcher | |||
| run: dotnet build "./launcher/launcher.sln" -c Release | |||
| - name: Build Playback | |||
| run: dotnet build "./playback/playback.sln" -c Release | |||
| @@ -61,6 +61,13 @@ public: | |||
| virtual bool StartTreatMate() = 0; | |||
| virtual bool StartRescueMate() = 0; | |||
| virtual bool OpenDoor() = 0; | |||
| virtual bool CloseDoor() = 0; | |||
| virtual bool SkipWindow() = 0; | |||
| virtual bool StartOpenGate() = 0; | |||
| virtual bool StartOpenChest() = 0; | |||
| virtual bool EndAllAction() = 0; | |||
| // ITrickerAPI使用的部分 | |||
| virtual bool Attack(double angle) = 0; | |||
| @@ -84,6 +91,14 @@ public: | |||
| virtual std::future<bool> PickProp(THUAI6::PropType prop) = 0; | |||
| virtual std::future<bool> UseProp(THUAI6::PropType prop) = 0; | |||
| virtual std::future<bool> UseSkill(int32_t skillID) = 0; | |||
| virtual std::future<bool> Attack(double angleInRadian) = 0; | |||
| virtual std::future<bool> OpenDoor() = 0; | |||
| virtual std::future<bool> CloseDoor() = 0; | |||
| virtual std::future<bool> SkipWindow() = 0; | |||
| virtual std::future<bool> StartOpenGate() = 0; | |||
| virtual std::future<bool> StartOpenChest() = 0; | |||
| virtual std::future<bool> EndAllAction() = 0; | |||
| // 发送信息、接受信息,注意收消息时无消息则返回nullopt | |||
| virtual std::future<bool> SendMessage(int64_t, std::string) = 0; | |||
| @@ -149,7 +164,6 @@ class ITrickerAPI : public IAPI | |||
| public: | |||
| /*****捣蛋鬼阵营的特定函数*****/ | |||
| virtual std::future<bool> Attack(double angleInRadian) = 0; | |||
| [[nodiscard]] virtual std::shared_ptr<const THUAI6::Tricker> GetSelfInfo() const = 0; | |||
| }; | |||
| @@ -190,6 +204,15 @@ public: | |||
| std::future<bool> UseProp(THUAI6::PropType prop) override; | |||
| std::future<bool> UseSkill(int32_t skillID) override; | |||
| std::future<bool> Attack(double angleInRadian) override; | |||
| std::future<bool> OpenDoor() override; | |||
| std::future<bool> CloseDoor() override; | |||
| std::future<bool> SkipWindow() override; | |||
| std::future<bool> StartOpenGate() override; | |||
| std::future<bool> StartOpenChest() override; | |||
| std::future<bool> EndAllAction() override; | |||
| std::future<bool> SendMessage(int64_t, std::string) override; | |||
| [[nodiscard]] std::future<bool> HaveMessage() override; | |||
| [[nodiscard]] std::future<std::optional<std::pair<int64_t, std::string>>> GetMessage() override; | |||
| @@ -256,6 +279,13 @@ public: | |||
| std::future<bool> UseProp(THUAI6::PropType prop) override; | |||
| std::future<bool> UseSkill(int32_t skillID) override; | |||
| std::future<bool> OpenDoor() override; | |||
| std::future<bool> CloseDoor() override; | |||
| std::future<bool> SkipWindow() override; | |||
| std::future<bool> StartOpenGate() override; | |||
| std::future<bool> StartOpenChest() override; | |||
| std::future<bool> EndAllAction() override; | |||
| std::future<bool> SendMessage(int64_t, std::string) override; | |||
| [[nodiscard]] std::future<bool> HaveMessage() override; | |||
| [[nodiscard]] std::future<std::optional<std::pair<int64_t, std::string>>> GetMessage() override; | |||
| @@ -312,6 +342,15 @@ public: | |||
| std::future<bool> UseProp(THUAI6::PropType prop) override; | |||
| std::future<bool> UseSkill(int32_t skillID) override; | |||
| std::future<bool> Attack(double angleInRadian) override; | |||
| std::future<bool> OpenDoor() override; | |||
| std::future<bool> CloseDoor() override; | |||
| std::future<bool> SkipWindow() override; | |||
| std::future<bool> StartOpenGate() override; | |||
| std::future<bool> StartOpenChest() override; | |||
| std::future<bool> EndAllAction() override; | |||
| std::future<bool> SendMessage(int64_t, std::string) override; | |||
| [[nodiscard]] std::future<bool> HaveMessage() override; | |||
| [[nodiscard]] std::future<std::optional<std::pair<int64_t, std::string>>> GetMessage() override; | |||
| @@ -365,6 +404,13 @@ public: | |||
| std::future<bool> UseProp(THUAI6::PropType prop) override; | |||
| std::future<bool> UseSkill(int32_t skillID) override; | |||
| std::future<bool> OpenDoor() override; | |||
| std::future<bool> CloseDoor() override; | |||
| std::future<bool> SkipWindow() override; | |||
| std::future<bool> StartOpenGate() override; | |||
| std::future<bool> StartOpenChest() override; | |||
| std::future<bool> EndAllAction() override; | |||
| std::future<bool> SendMessage(int64_t, std::string) override; | |||
| [[nodiscard]] std::future<bool> HaveMessage() override; | |||
| [[nodiscard]] std::future<std::optional<std::pair<int64_t, std::string>>> GetMessage() override; | |||
| @@ -118,6 +118,13 @@ private: | |||
| bool Attack(double angle) override; | |||
| bool OpenDoor() override; | |||
| bool CloseDoor() override; | |||
| bool SkipWindow() override; | |||
| bool StartOpenGate() override; | |||
| bool StartOpenChest() override; | |||
| bool EndAllAction() override; | |||
| bool WaitThread() override; | |||
| int GetCounter() const override; | |||
| @@ -101,6 +101,78 @@ std::future<bool> TrickerAPI::UseSkill(int32_t skillID) | |||
| { return logic.UseSkill(skillID); }); | |||
| } | |||
| std::future<bool> StudentAPI::OpenDoor() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.OpenDoor(); }); | |||
| } | |||
| std::future<bool> TrickerAPI::OpenDoor() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.OpenDoor(); }); | |||
| } | |||
| std::future<bool> StudentAPI::CloseDoor() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.CloseDoor(); }); | |||
| } | |||
| std::future<bool> TrickerAPI::CloseDoor() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.CloseDoor(); }); | |||
| } | |||
| std::future<bool> StudentAPI::SkipWindow() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.SkipWindow(); }); | |||
| } | |||
| std::future<bool> TrickerAPI::SkipWindow() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.SkipWindow(); }); | |||
| } | |||
| std::future<bool> StudentAPI::StartOpenGate() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.StartOpenGate(); }); | |||
| } | |||
| std::future<bool> TrickerAPI::StartOpenGate() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.StartOpenGate(); }); | |||
| } | |||
| std::future<bool> StudentAPI::StartOpenChest() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.StartOpenChest(); }); | |||
| } | |||
| std::future<bool> TrickerAPI::StartOpenChest() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.StartOpenChest(); }); | |||
| } | |||
| std::future<bool> StudentAPI::EndAllAction() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.EndAllAction(); }); | |||
| } | |||
| std::future<bool> TrickerAPI::EndAllAction() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.EndAllAction(); }); | |||
| } | |||
| std::future<bool> StudentAPI::SendMessage(int64_t toID, std::string message) | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| @@ -252,6 +324,12 @@ std::future<bool> TrickerAPI::Attack(double angleInRadian) | |||
| { return logic.Attack(angleInRadian); }); | |||
| } | |||
| std::future<bool> StudentAPI::Attack(double angleInRadian) | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.Attack(angleInRadian); }); | |||
| } | |||
| std::shared_ptr<const THUAI6::Tricker> TrickerAPI::GetSelfInfo() const | |||
| { | |||
| return logic.TrickerGetSelfInfo(); | |||
| @@ -206,6 +206,78 @@ std::future<bool> TrickerDebugAPI::UseSkill(int32_t skillID) | |||
| return result; }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::OpenDoor() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.OpenDoor(); }); | |||
| } | |||
| std::future<bool> TrickerDebugAPI::OpenDoor() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.OpenDoor(); }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::CloseDoor() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.CloseDoor(); }); | |||
| } | |||
| std::future<bool> TrickerDebugAPI::CloseDoor() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.CloseDoor(); }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::SkipWindow() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.SkipWindow(); }); | |||
| } | |||
| std::future<bool> TrickerDebugAPI::SkipWindow() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.SkipWindow(); }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::StartOpenGate() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.StartOpenGate(); }); | |||
| } | |||
| std::future<bool> TrickerDebugAPI::StartOpenGate() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.StartOpenGate(); }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::StartOpenChest() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.StartOpenChest(); }); | |||
| } | |||
| std::future<bool> TrickerDebugAPI::StartOpenChest() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.StartOpenChest(); }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::EndAllAction() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.EndAllAction(); }); | |||
| } | |||
| std::future<bool> TrickerDebugAPI::EndAllAction() | |||
| { | |||
| return std::async(std::launch::async, [&]() | |||
| { return logic.EndAllAction(); }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::SendMessage(int64_t toID, std::string message) | |||
| { | |||
| logger->info("SendMessage: toID = {}, message = {}, called at {}ms", toID, message, Time::TimeSinceStart(startPoint)); | |||
| @@ -403,6 +475,16 @@ std::future<bool> TrickerDebugAPI::Attack(double angleInRadian) | |||
| return result; }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::Attack(double angleInRadian) | |||
| { | |||
| logger->info("Attack: angleInRadian = {}, called at {}ms", angleInRadian, Time::TimeSinceStart(startPoint)); | |||
| return std::async(std::launch::async, [=]() | |||
| { auto result = logic.Attack(angleInRadian); | |||
| if (!result) | |||
| logger->warn("Attack: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||
| return result; }); | |||
| } | |||
| std::shared_ptr<const THUAI6::Tricker> TrickerDebugAPI::GetSelfInfo() const | |||
| { | |||
| return logic.TrickerGetSelfInfo(); | |||
| @@ -147,6 +147,42 @@ bool Logic::Attack(double angle) | |||
| return pComm->Attack(angle, playerID); | |||
| } | |||
| bool Logic::OpenDoor() | |||
| { | |||
| logger->debug("Called OpenDoor"); | |||
| return pComm->OpenDoor(playerID); | |||
| } | |||
| bool Logic::CloseDoor() | |||
| { | |||
| logger->debug("Called CloseDoor"); | |||
| return pComm->CloseDoor(playerID); | |||
| } | |||
| bool Logic::SkipWindow() | |||
| { | |||
| logger->debug("Called SkipWindow"); | |||
| return pComm->SkipWindow(playerID); | |||
| } | |||
| bool Logic::StartOpenGate() | |||
| { | |||
| logger->debug("Called StartOpenGate"); | |||
| return pComm->StartOpenGate(playerID); | |||
| } | |||
| bool Logic::StartOpenChest() | |||
| { | |||
| logger->debug("Called StartOpenChest"); | |||
| return pComm->StartOpenChest(playerID); | |||
| } | |||
| bool Logic::EndAllAction() | |||
| { | |||
| logger->debug("Called EndAllAction"); | |||
| return pComm->EndAllAction(playerID); | |||
| } | |||
| bool Logic::WaitThread() | |||
| { | |||
| Update(); | |||
| @@ -1,10 +1,15 @@ | |||
| # THUAI6 | |||
| 清华大学第六届人工智能挑战赛电子系赛道(原电子系第 24 届队式程序设计大赛 teamstyle24) | |||
| Gitee 镜像地址:[THUAI6: Gitee Mirror](https://gitee.com/eesast/THUAI6) | |||
| GitLink 镜像地址:[THUAI6: GitLink Mirror](https://www.gitlink.org.cn/EESAST/THUAI6) | |||
| 项目主页:[THUAI6 Project Home Page](https://eesast.github.io/THUAI6) | |||
| 关于本届及历届清华大学人工智能挑战赛与队式程序设计大赛的更多内容参见:[THUAI6 Github Wiki](https://github.com/eesast/THUAI6/wiki) | |||
| ## 赛题背景 | |||
| 待定 | |||
| @@ -10,8 +10,8 @@ message MessageOfStudent | |||
| int32 y = 2; | |||
| int32 speed = 3; | |||
| int32 determination = 4; // 剩余的学习毅力,相当于血量 | |||
| int32 fail_num = 5; // 挂科的科目数 | |||
| double time_until_skill_available = 6; | |||
| //int32 fail_num = 5; // 挂科的科目数 | |||
| repeated double time_until_skill_available = 6; | |||
| PlaceType place = 7; | |||
| repeated PropType prop = 8; | |||
| PlayerState player_state = 9; | |||
| @@ -142,7 +142,6 @@ message MessageOfMap | |||
| repeated PlaceType col = 1; | |||
| } | |||
| repeated Row row = 2; | |||
| repeated MessageOfMapObj map_obj_message = 3; | |||
| } | |||
| message MessageOfObj | |||
| @@ -154,6 +153,7 @@ message MessageOfObj | |||
| MessageOfProp prop_message = 3; | |||
| MessageOfBullet bullet_message = 4; | |||
| MessageOfBombedBullet bombed_bullet_message = 5; | |||
| MessageOfMapObj map_obj_message = 6; | |||
| } | |||
| } | |||
| @@ -9,9 +9,11 @@ namespace GameClass.GameObj | |||
| public override bool IsRigid => false; | |||
| public long MappingID { get; } | |||
| public Bullet bulletHasBombed; | |||
| public BombedBullet(Bullet bullet) : | |||
| base(bullet.Position, bullet.Radius, GameObjType.BombedBullet) | |||
| { | |||
| this.place = bullet.Place; | |||
| this.bulletHasBombed = bullet; | |||
| this.MappingID = bullet.ID; | |||
| this.FacingDirection = bullet.FacingDirection; | |||
| @@ -6,8 +6,8 @@ namespace GameClass.GameObj | |||
| { | |||
| internal sealed class CommonAttackOfGhost : Bullet | |||
| { | |||
| public CommonAttackOfGhost(Character player, int radius = GameData.bulletRadius) : | |||
| base(player, radius) | |||
| public CommonAttackOfGhost(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, placeType, pos) | |||
| { | |||
| } | |||
| public override double BulletBombRange => 0; | |||
| @@ -28,8 +28,8 @@ namespace GameClass.GameObj | |||
| } | |||
| internal sealed class FlyingKnife : Bullet | |||
| { | |||
| public FlyingKnife(Character player, int radius = GameData.bulletRadius) : | |||
| base(player, radius) | |||
| public FlyingKnife(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, placeType, pos) | |||
| { | |||
| } | |||
| public override double BulletBombRange => 0; | |||
| @@ -51,8 +51,8 @@ namespace GameClass.GameObj | |||
| internal sealed class AtomBomb : Bullet | |||
| { | |||
| public AtomBomb(Character player, int radius = GameData.bulletRadius) : | |||
| base(player, radius) | |||
| public AtomBomb(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, placeType, pos) | |||
| { | |||
| } | |||
| public override double BulletBombRange => GameData.basicBulletBombRange / 3 * 7; | |||
| @@ -75,8 +75,8 @@ namespace GameClass.GameObj | |||
| internal sealed class OrdinaryBullet : Bullet // 1倍攻击范围,1倍攻击力,一倍速 | |||
| { | |||
| public OrdinaryBullet(Character player, int radius = GameData.bulletRadius) : | |||
| base(player, radius) | |||
| public OrdinaryBullet(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, placeType, pos) | |||
| { | |||
| } | |||
| public override double BulletBombRange => GameData.basicBulletBombRange / 6 * 5; | |||
| @@ -98,8 +98,8 @@ namespace GameClass.GameObj | |||
| internal sealed class FastBullet : Bullet // 1倍攻击范围,0.2倍攻击力,2倍速 | |||
| { | |||
| public FastBullet(Character player, int radius = GameData.bulletRadius) : | |||
| base(player, radius) | |||
| public FastBullet(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, placeType, pos) | |||
| { | |||
| } | |||
| public override double BulletBombRange => GameData.basicBulletBombRange / 4 * 2; | |||
| @@ -122,8 +122,8 @@ namespace GameClass.GameObj | |||
| internal sealed class LineBullet : Bullet // 直线爆炸,宽度1格,长度为2倍爆炸范围 | |||
| { | |||
| public LineBullet(Character player, int radius = GameData.bulletRadius) : | |||
| base(player, radius) | |||
| public LineBullet(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, placeType, pos) | |||
| { | |||
| } | |||
| public override double BulletBombRange => GameData.basicBulletBombRange / 3 * 4; | |||
| @@ -37,9 +37,10 @@ namespace GameClass.GameObj | |||
| return true; | |||
| return false; | |||
| } | |||
| public Bullet(Character player, int radius) : | |||
| base(player.Position, radius, GameObjType.Bullet) | |||
| public Bullet(Character player, int radius, PlaceType placeType, XY Position) : | |||
| base(Position, radius, GameObjType.Bullet) | |||
| { | |||
| this.place = placeType; | |||
| this.CanMove = true; | |||
| this.moveSpeed = this.Speed; | |||
| this.hasSpear = player.HasSpear; | |||
| @@ -52,22 +53,22 @@ namespace GameClass.GameObj | |||
| public static class BulletFactory | |||
| { | |||
| public static Bullet? GetBullet(Character character) | |||
| public static Bullet? GetBullet(Character character, PlaceType place, XY pos) | |||
| { | |||
| Bullet? newBullet = null; | |||
| switch (character.BulletOfPlayer) | |||
| { | |||
| case BulletType.AtomBomb: | |||
| newBullet = new AtomBomb(character); | |||
| newBullet = new AtomBomb(character, place, pos); | |||
| break; | |||
| case BulletType.LineBullet: | |||
| newBullet = new LineBullet(character); | |||
| newBullet = new LineBullet(character, place, pos); | |||
| break; | |||
| case BulletType.FastBullet: | |||
| newBullet = new FastBullet(character); | |||
| newBullet = new FastBullet(character, place, pos); | |||
| break; | |||
| case BulletType.OrdinaryBullet: | |||
| newBullet = new OrdinaryBullet(character); | |||
| newBullet = new OrdinaryBullet(character, place, pos); | |||
| break; | |||
| default: | |||
| break; | |||
| @@ -53,16 +53,13 @@ namespace GameClass.GameObj | |||
| public bool IsGhost() | |||
| { | |||
| return this.CharacterType switch | |||
| { | |||
| CharacterType.Assassin => true, | |||
| _ => false, | |||
| }; | |||
| return GameData.IsGhost(CharacterType); | |||
| } | |||
| protected Character(XY initPos, int initRadius, CharacterType characterType) : | |||
| base(initPos, initRadius, GameObjType.Character) | |||
| { | |||
| this.place = PlaceType.Null; | |||
| this.CanMove = true; | |||
| this.score = 0; | |||
| this.propInventory = null; | |||
| @@ -88,6 +85,8 @@ namespace GameClass.GameObj | |||
| this.bulletNum = maxBulletNum; | |||
| this.bulletOfPlayer = Occupation.InitBullet; | |||
| this.OriBulletOfPlayer = Occupation.InitBullet; | |||
| this.concealment = Occupation.Concealment; | |||
| this.alertnessRadius = Occupation.AlertnessRadius; | |||
| this.characterType = characterType; | |||
| foreach (var activeSkill in this.Occupation.ListOfIActiveSkill) | |||
| @@ -8,7 +8,7 @@ using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| public partial class Character : GameObj, ICharacter // 负责人LHR摆烂终了 | |||
| public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 | |||
| { | |||
| private readonly object beAttackedLock = new(); | |||
| @@ -175,24 +175,56 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| private Dictionary<BgmType, double> bgmDictionary = new(); | |||
| public Dictionary<BgmType, double> BgmDictionary | |||
| { | |||
| get => bgmDictionary; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| bgmDictionary = value; | |||
| } | |||
| } | |||
| } | |||
| private int alertnessRadius; | |||
| public int AlertnessRadius | |||
| { | |||
| get => alertnessRadius; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| alertnessRadius = value; | |||
| } | |||
| } | |||
| } | |||
| private double concealment; | |||
| public double Concealment | |||
| { | |||
| get => concealment; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| concealment = value; | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 进行一次远程攻击 | |||
| /// 进行一次攻击 | |||
| /// </summary> | |||
| /// <param name="posOffset">子弹初始位置偏差值</param> | |||
| /// <returns>攻击操作发出的子弹</returns> | |||
| public Bullet? RemoteAttack(XY posOffset) | |||
| public Bullet? Attack(XY pos, PlaceType place) | |||
| { | |||
| if (TrySubBulletNum()) | |||
| return ProduceOneBullet(this.Position + posOffset); | |||
| return BulletFactory.GetBullet(this, place, pos); | |||
| else | |||
| return null; | |||
| } | |||
| protected Bullet? ProduceOneBullet(XY initPos) | |||
| { | |||
| var newBullet = BulletFactory.GetBullet(this); | |||
| newBullet?.SetPosition(initPos); | |||
| return newBullet; | |||
| } | |||
| /// <summary> | |||
| /// 尝试将子弹数量减1 | |||
| @@ -452,24 +484,24 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| #endregion | |||
| public override void Reset() // 要加锁吗? | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| // _ = AddDeathCount(); | |||
| base.Reset(); | |||
| this.MoveSpeed = OrgMoveSpeed; | |||
| HP = MaxHp; | |||
| PropInventory = null; | |||
| BulletOfPlayer = OriBulletOfPlayer; | |||
| lock (gameObjLock) | |||
| bulletNum = maxBulletNum; | |||
| /* public override void Reset() // 要加锁吗? | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| // _ = AddDeathCount(); | |||
| base.Reset(); | |||
| this.MoveSpeed = OrgMoveSpeed; | |||
| HP = MaxHp; | |||
| PropInventory = null; | |||
| BulletOfPlayer = OriBulletOfPlayer; | |||
| lock (gameObjLock) | |||
| bulletNum = maxBulletNum; | |||
| buffManager.ClearAll(); | |||
| IsInvisible = false; | |||
| this.Vampire = this.OriVampire; | |||
| } | |||
| } | |||
| buffManager.ClearAll(); | |||
| IsInvisible = false; | |||
| this.Vampire = this.OriVampire; | |||
| } | |||
| }*/ | |||
| public void Die(PlayerStateType playerStateType) | |||
| { | |||
| lock (gameObjLock) | |||
| @@ -478,6 +510,7 @@ namespace GameClass.GameObj | |||
| CanMove = false; | |||
| IsResetting = true; | |||
| Position = GameData.PosWhoDie; | |||
| place = PlaceType.Grass; | |||
| } | |||
| } | |||
| @@ -7,13 +7,10 @@ namespace GameClass.GameObj | |||
| /// <summary> | |||
| /// 一切游戏元素的总基类,与THUAI4不同,继承IMoveable接口(出于一切物体其实都是可运动的指导思想)——LHR | |||
| /// </summary> | |||
| public abstract class GameObj : IMoveable | |||
| public abstract class GameObj : IGameObj | |||
| { | |||
| protected readonly object gameObjLock = new(); | |||
| /// <summary> | |||
| /// 可移动物体专用锁 | |||
| /// </summary> | |||
| public object MoveLock => gameObjLock; | |||
| public object GameLock => gameObjLock; | |||
| protected readonly XY birthPos; | |||
| @@ -38,7 +35,9 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| } | |||
| public abstract bool IsRigid { get; } | |||
| protected PlaceType place; | |||
| public PlaceType Place { get => place; } | |||
| private XY facingDirection = new(1, 0); | |||
| public XY FacingDirection | |||
| @@ -50,6 +49,9 @@ namespace GameClass.GameObj | |||
| facingDirection = value; | |||
| } | |||
| } | |||
| public abstract bool IsRigid { get; } | |||
| public abstract ShapeType Shape { get; } | |||
| private bool canMove; | |||
| @@ -65,19 +67,6 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| private bool isMoving; | |||
| public bool IsMoving | |||
| { | |||
| get => isMoving; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| isMoving = value; | |||
| } | |||
| } | |||
| } | |||
| private bool isResetting; | |||
| public bool IsResetting | |||
| { | |||
| @@ -90,77 +79,14 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| } | |||
| public bool IsAvailable => !IsMoving && CanMove && !IsResetting; // 是否能接收指令 | |||
| public int Radius { get; } | |||
| protected int moveSpeed; | |||
| /// <summary> | |||
| /// 移动速度 | |||
| /// </summary> | |||
| public int MoveSpeed | |||
| { | |||
| get => moveSpeed; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| moveSpeed = value; | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 原初移动速度 | |||
| /// </summary> | |||
| public int OrgMoveSpeed { get; protected set; } | |||
| public int Radius { get; } | |||
| // 移动,改变坐标 | |||
| public long Move(XY moveVec) | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| FacingDirection = moveVec; | |||
| this.Position += moveVec; | |||
| } | |||
| return (long)(moveVec * moveVec); | |||
| } | |||
| /// <summary> | |||
| /// 设置位置 | |||
| /// </summary> | |||
| /// <param name="newpos">新位置</param> | |||
| public void SetPosition(XY newpos) | |||
| { | |||
| Position = newpos; | |||
| } | |||
| /// <summary> | |||
| /// 设置移动速度 | |||
| /// </summary> | |||
| /// <param name="newMoveSpeed">新速度</param> | |||
| public void SetMoveSpeed(int newMoveSpeed) | |||
| { | |||
| MoveSpeed = newMoveSpeed; | |||
| } | |||
| /// <summary> | |||
| /// 复活时数据重置 | |||
| /// </summary> | |||
| public virtual void Reset() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| facingDirection = new XY(1, 0); | |||
| isMoving = false; | |||
| canMove = false; | |||
| isResetting = true; | |||
| this.position = birthPos; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 为了使IgnoreCollide多态化并使GameObj能不报错地继承IMoveable | |||
| /// 在xfgg点播下设计了这个抽象辅助方法,在具体类中实现 | |||
| /// </summary> | |||
| /// <returns> 依具体类及该方法参数而定,默认为false </returns> | |||
| protected virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false; | |||
| bool IMoveable.IgnoreCollide(IGameObj targetObj) => IgnoreCollideExecutor(targetObj); | |||
| bool IGameObj.IgnoreCollide(IGameObj targetObj) => IgnoreCollideExecutor(targetObj); | |||
| public GameObj(XY initPos, int initRadius, GameObjType initType) | |||
| { | |||
| this.Position = this.birthPos = initPos; | |||
| @@ -11,6 +11,7 @@ namespace GameClass.GameObj | |||
| public Doorway(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Doorway) | |||
| { | |||
| this.place = PlaceType.Doorway; | |||
| this.CanMove = false; | |||
| } | |||
| public override bool IsRigid => true; | |||
| @@ -11,6 +11,7 @@ namespace GameClass.GameObj | |||
| public EmergencyExit(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.EmergencyExit) | |||
| { | |||
| this.place = PlaceType.EmergencyExit; | |||
| this.CanMove = false; | |||
| } | |||
| public override bool IsRigid => true; | |||
| @@ -10,6 +10,7 @@ namespace GameClass.GameObj | |||
| public Generator(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Generator) | |||
| { | |||
| this.place = PlaceType.Generator; | |||
| this.CanMove = false; | |||
| } | |||
| public override bool IsRigid => true; | |||
| @@ -18,12 +18,13 @@ namespace GameClass.GameObj | |||
| private Dictionary<GameObjType, ReaderWriterLockSlim> gameObjLockDict; | |||
| public Dictionary<GameObjType, ReaderWriterLockSlim> GameObjLockDict => gameObjLockDict; | |||
| public readonly uint[,] ProtoGameMap; | |||
| public PlaceType GetPlaceType(GameObj obj) | |||
| public readonly uint[,] protoGameMap; | |||
| public uint[,] ProtoGameMap => protoGameMap; | |||
| public PlaceType GetPlaceType(IGameObj obj) | |||
| { | |||
| try | |||
| { | |||
| return (PlaceType)ProtoGameMap[obj.Position.x / GameData.numOfPosGridPerCell, obj.Position.y / GameData.numOfPosGridPerCell]; | |||
| return (PlaceType)protoGameMap[obj.Position.x / GameData.numOfPosGridPerCell, obj.Position.y / GameData.numOfPosGridPerCell]; | |||
| } | |||
| catch | |||
| { | |||
| @@ -35,7 +36,7 @@ namespace GameClass.GameObj | |||
| { | |||
| try | |||
| { | |||
| return (PlaceType)ProtoGameMap[pos.x / GameData.numOfPosGridPerCell, pos.y / GameData.numOfPosGridPerCell]; | |||
| return (PlaceType)protoGameMap[pos.x / GameData.numOfPosGridPerCell, pos.y / GameData.numOfPosGridPerCell]; | |||
| } | |||
| catch | |||
| { | |||
| @@ -120,8 +121,8 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| ProtoGameMap = new uint[mapResource.GetLength(0), mapResource.GetLength(1)]; | |||
| Array.Copy(mapResource, ProtoGameMap, mapResource.Length); | |||
| protoGameMap = new uint[mapResource.GetLength(0), mapResource.GetLength(1)]; | |||
| Array.Copy(mapResource, protoGameMap, mapResource.Length); | |||
| birthPointList = new Dictionary<uint, XY>(GameData.numOfBirthPoint); | |||
| @@ -10,6 +10,7 @@ namespace GameClass.GameObj | |||
| public Wall(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Wall) | |||
| { | |||
| this.place = PlaceType.Wall; | |||
| this.CanMove = false; | |||
| } | |||
| public override bool IsRigid => true; | |||
| @@ -0,0 +1,103 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 一切游戏元素的总基类,与THUAI4不同,继承IMoveable接口(出于一切物体其实都是可运动的指导思想)——LHR | |||
| /// </summary> | |||
| public abstract class Moveable : GameObj, IMoveable | |||
| { | |||
| protected readonly object moveObjLock = new(); | |||
| public object MoveLock => moveObjLock; | |||
| private bool isMoving; | |||
| public bool IsMoving | |||
| { | |||
| get => isMoving; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| isMoving = value; | |||
| } | |||
| } | |||
| } | |||
| public bool IsAvailable => !IsMoving && CanMove && !IsResetting; // 是否能接收指令 | |||
| protected int moveSpeed; | |||
| /// <summary> | |||
| /// 移动速度 | |||
| /// </summary> | |||
| public int MoveSpeed | |||
| { | |||
| get => moveSpeed; | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| moveSpeed = value; | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 原初移动速度 | |||
| /// </summary> | |||
| public int OrgMoveSpeed { get; protected set; } | |||
| // 移动,改变坐标 | |||
| public long MovingSetPos(XY moveVec, PlaceType place) | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| FacingDirection = moveVec; | |||
| this.Position += moveVec; | |||
| this.place = place; | |||
| } | |||
| return moveVec * moveVec; | |||
| } | |||
| public void ReSetPos(XY pos, PlaceType place) | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| this.Position = pos; | |||
| this.place = place; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 设置移动速度 | |||
| /// </summary> | |||
| /// <param name="newMoveSpeed">新速度</param> | |||
| public void SetMoveSpeed(int newMoveSpeed) | |||
| { | |||
| MoveSpeed = newMoveSpeed; | |||
| } | |||
| /* /// <summary> | |||
| /// 复活时数据重置 | |||
| /// </summary> | |||
| public virtual void Reset(PlaceType place) | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| this.FacingDirection = new XY(1, 0); | |||
| isMoving = false; | |||
| CanMove = false; | |||
| IsResetting = true; | |||
| this.Position = birthPos; | |||
| this.Place= place; | |||
| } | |||
| }*/ | |||
| /// <summary> | |||
| /// 为了使IgnoreCollide多态化并使GameObj能不报错地继承IMoveable | |||
| /// 在xfgg点播下设计了这个抽象辅助方法,在具体类中实现 | |||
| /// </summary> | |||
| /// <returns> 依具体类及该方法参数而定,默认为false </returns> | |||
| public Moveable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType) | |||
| { | |||
| } | |||
| } | |||
| } | |||
| @@ -6,7 +6,7 @@ namespace GameClass.GameObj | |||
| /// <summary> | |||
| /// 所有物,具有主人(Parent)(特定玩家)属性的对象 | |||
| /// </summary> | |||
| public abstract class ObjOfCharacter : GameObj, IObjOfCharacter | |||
| public abstract class ObjOfCharacter : Moveable, IObjOfCharacter | |||
| { | |||
| private ICharacter? parent = null; // 主人 | |||
| public ICharacter? Parent | |||
| @@ -11,6 +11,7 @@ namespace GameClass.GameObj | |||
| public OutOfBoundBlock(XY initPos) : | |||
| base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) | |||
| { | |||
| this.place = PlaceType.Wall; | |||
| this.CanMove = false; | |||
| } | |||
| @@ -17,6 +17,7 @@ namespace GameClass.GameObj | |||
| public PickedProp(Prop prop) : | |||
| base(prop.Position, prop.Radius, GameObjType.PickedProp) | |||
| { | |||
| this.place = prop.Place; | |||
| this.PropHasPicked = prop; | |||
| this.MappingID = prop.ID; | |||
| } | |||
| @@ -21,16 +21,13 @@ namespace GameClass.GameObj | |||
| public abstract PropType GetPropType(); | |||
| public Prop(XY initPos, int radius = GameData.PropRadius) : | |||
| public Prop(XY initPos, PlaceType place, int radius = GameData.PropRadius) : | |||
| base(initPos, radius, GameObjType.Prop) | |||
| { | |||
| this.place = place; | |||
| this.CanMove = false; | |||
| this.moveSpeed = GameData.PropMoveSpeed; | |||
| } | |||
| public void SetNewPos(XY pos) | |||
| { | |||
| this.Position = pos; | |||
| } | |||
| } | |||
| @@ -48,8 +45,8 @@ namespace GameClass.GameObj | |||
| /// </summary> | |||
| public sealed class AddSpeed : Prop | |||
| { | |||
| public AddSpeed(XY initPos) : | |||
| base(initPos) | |||
| public AddSpeed(XY initPos, PlaceType placeType) : | |||
| base(initPos, placeType) | |||
| { | |||
| } | |||
| public override PropType GetPropType() => PropType.addSpeed; | |||
| @@ -59,8 +56,8 @@ namespace GameClass.GameObj | |||
| /// </summary> | |||
| public sealed class AddLIFE : Prop | |||
| { | |||
| public AddLIFE(XY initPos) : | |||
| base(initPos) | |||
| public AddLIFE(XY initPos, PlaceType placeType) : | |||
| base(initPos, placeType) | |||
| { | |||
| } | |||
| public override PropType GetPropType() => PropType.addLIFE; | |||
| @@ -70,8 +67,7 @@ namespace GameClass.GameObj | |||
| /// </summary> | |||
| public sealed class Shield : Prop | |||
| { | |||
| public Shield(XY initPos) : | |||
| base(initPos) | |||
| public Shield(XY initPos, PlaceType placeType) : base(initPos, placeType) | |||
| { | |||
| } | |||
| public override PropType GetPropType() => PropType.Shield; | |||
| @@ -81,8 +77,7 @@ namespace GameClass.GameObj | |||
| /// </summary> | |||
| public sealed class Spear : Prop | |||
| { | |||
| public Spear(XY initPos) : | |||
| base(initPos) | |||
| public Spear(XY initPos, PlaceType placeType) : base(initPos, placeType) | |||
| { | |||
| } | |||
| public override PropType GetPropType() => PropType.Spear; | |||
| @@ -20,6 +20,18 @@ namespace GameEngine | |||
| private readonly ITimer gameTimer; | |||
| 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; | |||
| } | |||
| } | |||
| private readonly CollisionChecker collisionChecker; | |||
| private readonly Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision; | |||
| /// <summary> | |||
| @@ -34,6 +46,7 @@ namespace GameEngine | |||
| Action<IMoveable> EndMove | |||
| ) | |||
| { | |||
| this.ProtoGameMap = gameMap.ProtoGameMap; | |||
| this.gameTimer = gameMap.Timer; | |||
| this.EndMove = EndMove; | |||
| this.OnCollision = OnCollision; | |||
| @@ -50,9 +63,11 @@ namespace GameEngine | |||
| /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ | |||
| XY nextPos = obj.Position + moveVec; | |||
| double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); | |||
| maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); | |||
| _ = obj.Move(new XY(moveVec.Angle(), maxLen)); | |||
| //double maxLen | |||
| _ = collisionChecker.FindMax(obj, nextPos, moveVec); | |||
| //maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); | |||
| obj.MovingSetPos(moveVec, GetPlaceType(nextPos)); | |||
| } | |||
| public void MoveObj(IMoveable obj, int moveTime, double direction) | |||
| @@ -69,7 +84,8 @@ namespace GameEngine | |||
| obj.IsMoving = true; | |||
| double moveVecLength = 0.0; | |||
| double deltaLen = moveVecLength - Math.Sqrt(obj.Move(new XY(direction, moveVecLength))); // 转向,并用deltaLen存储行走的误差 | |||
| XY res = new XY(direction, moveVecLength); | |||
| double deltaLen = moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res))); // 转向,并用deltaLen存储行走的误差 | |||
| IGameObj? collisionObj = null; | |||
| bool isDestroyed = false; | |||
| new FrameRateTaskExecutor<int>( | |||
| @@ -77,17 +93,18 @@ namespace GameEngine | |||
| () => | |||
| { | |||
| moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; | |||
| XY res = new XY(direction, moveVecLength); | |||
| // 越界情况处理:如果越界,则与越界方块碰撞 | |||
| bool flag; // 循环标志 | |||
| do | |||
| { | |||
| flag = false; | |||
| collisionObj = collisionChecker.CheckCollision(obj, new XY(direction, moveVecLength)); | |||
| collisionObj = collisionChecker.CheckCollision(obj, res); | |||
| if (collisionObj == null) | |||
| break; | |||
| switch (OnCollision(obj, collisionObj, new XY(direction, moveVecLength))) | |||
| switch (OnCollision(obj, collisionObj, res)) | |||
| { | |||
| case AfterCollision.ContinueCheck: | |||
| flag = true; | |||
| @@ -97,13 +114,14 @@ namespace GameEngine | |||
| isDestroyed = true; | |||
| return false; | |||
| case AfterCollision.MoveMax: | |||
| MoveMax(obj, new XY(direction, moveVecLength)); | |||
| MoveMax(obj, res); | |||
| moveVecLength = 0; | |||
| res = new XY(direction, moveVecLength); | |||
| break; | |||
| } | |||
| } while (flag); | |||
| deltaLen += moveVecLength - Math.Sqrt(obj.Move(new XY(direction, moveVecLength))); | |||
| deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res))); | |||
| return true; | |||
| }, | |||
| @@ -118,13 +136,14 @@ namespace GameEngine | |||
| if (!isDestroyed) | |||
| { | |||
| moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; | |||
| if ((collisionObj = collisionChecker.CheckCollision(obj, new XY(direction, moveVecLength))) == null) | |||
| XY res = new XY(direction, moveVecLength); | |||
| if ((collisionObj = collisionChecker.CheckCollision(obj, res)) == null) | |||
| { | |||
| obj.Move(new XY(direction, moveVecLength)); | |||
| obj.MovingSetPos(res, GetPlaceType(obj.Position + res)); | |||
| } | |||
| else | |||
| { | |||
| switch (OnCollision(obj, collisionObj, new XY(direction, moveVecLength))) | |||
| switch (OnCollision(obj, collisionObj, res)) | |||
| { | |||
| case AfterCollision.ContinueCheck: | |||
| flag = true; | |||
| @@ -134,8 +153,9 @@ namespace GameEngine | |||
| isDestroyed = true; | |||
| break; | |||
| case AfterCollision.MoveMax: | |||
| MoveMax(obj, new XY(direction, moveVecLength)); | |||
| MoveMax(obj, res); | |||
| moveVecLength = 0; | |||
| res = new XY(direction, moveVecLength); | |||
| break; | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| using System; | |||
| using System.Numerics; | |||
| using System.Runtime.InteropServices; | |||
| using System.Threading; | |||
| using GameClass.GameObj; | |||
| @@ -38,7 +39,7 @@ namespace Gaming | |||
| public bool Fix(Student player)// 自动检查有无发电机可修 | |||
| { | |||
| if (player.PlayerState != PlayerStateType.Null || player.IsGhost()) | |||
| if (player.IsGhost() || (player.PlayerState != PlayerStateType.Null && player.PlayerState != PlayerStateType.IsMoving)) | |||
| return false; | |||
| Generator? generatorForFix = null; | |||
| @@ -72,7 +73,7 @@ namespace Gaming | |||
| loopCondition: () => player.PlayerState == PlayerStateType.IsFixing && gameMap.Timer.IsGaming && generatorForFix.DegreeOfFRepair < GameData.degreeOfFixedGenerator && GameData.ApproachToInteract(player.Position, generatorForFix.Position), | |||
| loopToDo: () => | |||
| { | |||
| return !generatorForFix.Repair(player.FixSpeed * GameData.frameDuration); | |||
| generatorForFix.Repair(player.FixSpeed * GameData.frameDuration); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| @@ -38,7 +38,7 @@ namespace Gaming | |||
| ); | |||
| } | |||
| public void BeAddictedToGame(Student player) | |||
| private void BeAddictedToGame(Student player) | |||
| { | |||
| new Thread | |||
| (() => | |||
| @@ -68,11 +68,10 @@ namespace Gaming | |||
| { IsBackground = true }.Start(); | |||
| } | |||
| public void Die(Character player) | |||
| private void Die(Character player) | |||
| { | |||
| player.CanMove = false; | |||
| player.IsResetting = true; | |||
| player.Die(PlayerStateType.IsDeceased); | |||
| // gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); | |||
| // try | |||
| //{ | |||
| @@ -87,7 +86,8 @@ namespace Gaming | |||
| if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地 | |||
| { | |||
| dropProp = player.PropInventory; | |||
| dropProp.SetNewPos(GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell)); | |||
| XY res = GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell); | |||
| dropProp.ReSetPos(res, gameMap.GetPlaceType(res)); | |||
| } | |||
| gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); | |||
| try | |||
| @@ -298,12 +298,14 @@ namespace Gaming | |||
| if (player.PlayerState != PlayerStateType.Null || player.PlayerState != PlayerStateType.IsMoving) | |||
| return false; | |||
| Bullet? bullet = player.RemoteAttack( | |||
| new XY // 子弹紧贴人物生成。 | |||
| XY res = new XY // 子弹紧贴人物生成。 | |||
| ( | |||
| (int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Cos(angle)), | |||
| (int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Sin(angle)) | |||
| ) | |||
| ); | |||
| Bullet? bullet = player.Attack( | |||
| res, gameMap.GetPlaceType(res) | |||
| ); | |||
| if (bullet.CastTime > 0) | |||
| { | |||
| @@ -5,6 +5,7 @@ using Preparation.Utility; | |||
| using Timothy.FrameRateTask; | |||
| using Preparation.Interface; | |||
| using GameClass.GameObj; | |||
| using System.Numerics; | |||
| namespace Gaming | |||
| { | |||
| @@ -41,21 +42,14 @@ namespace Gaming | |||
| // Console.WriteLine($"x,y: {pos.x},{pos.y}"); | |||
| Character newPlayer = (GameData.IsGhost(playerInitInfo.characterType)) ? new Ghost(pos, GameData.characterRadius, playerInitInfo.characterType) : new Student(pos, GameData.characterRadius, playerInitInfo.characterType); | |||
| gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); | |||
| try | |||
| { | |||
| gameMap.GameObjDict[GameObjType.Character].Add(newPlayer); | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock(); | |||
| } | |||
| gameMap.Add(newPlayer); | |||
| // Console.WriteLine($"GameObjDict[GameObjType.Character] length:{gameMap.GameObjDict[GameObjType.Character].Count}"); | |||
| teamList[(int)playerInitInfo.teamID].AddPlayer(newPlayer); | |||
| newPlayer.TeamID = playerInitInfo.teamID; | |||
| newPlayer.PlayerID = playerInitInfo.playerID; | |||
| new Thread //人物装弹 | |||
| #region 人物装弹 | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| @@ -63,19 +57,16 @@ namespace Gaming | |||
| Thread.Sleep(newPlayer.CD); | |||
| long lastTime = Environment.TickCount64; | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => gameMap.Timer.IsGaming, | |||
| loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting, | |||
| loopToDo: () => | |||
| { | |||
| if (!newPlayer.IsResetting) | |||
| long nowTime = Environment.TickCount64; | |||
| if (newPlayer.BulletNum == newPlayer.MaxBulletNum) | |||
| lastTime = nowTime; | |||
| if (nowTime - lastTime >= newPlayer.CD) | |||
| { | |||
| long nowTime = Environment.TickCount64; | |||
| if (newPlayer.BulletNum == newPlayer.MaxBulletNum) | |||
| lastTime = nowTime; | |||
| if (nowTime - lastTime >= newPlayer.CD) | |||
| { | |||
| _ = newPlayer.TryAddBulletNum(); | |||
| lastTime = nowTime; | |||
| } | |||
| _ = newPlayer.TryAddBulletNum(); | |||
| lastTime = nowTime; | |||
| } | |||
| }, | |||
| timeInterval: GameData.checkInterval, | |||
| @@ -93,6 +84,91 @@ namespace Gaming | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| #endregion | |||
| #region BGM更新 | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| while (!gameMap.Timer.IsGaming) | |||
| Thread.Sleep((int)GameData.checkInterval); | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting, | |||
| loopToDo: () => | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | |||
| try | |||
| { | |||
| if (newPlayer.IsGhost()) | |||
| { | |||
| double bgmVolume = 0; | |||
| foreach (Character person in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| if (!person.IsGhost() && XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment)) | |||
| { | |||
| if ((double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position) > bgmVolume) | |||
| bgmVolume = newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position); | |||
| } | |||
| } | |||
| if (bgmVolume > 0) | |||
| newPlayer.BgmDictionary.Add(BgmType.StudentIsApproaching, bgmVolume); | |||
| } | |||
| else | |||
| { | |||
| foreach (Character person in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| if (person.IsGhost()) | |||
| { | |||
| if (XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment)) | |||
| newPlayer.BgmDictionary.Add(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position)); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||
| } | |||
| gameMap.GameObjLockDict[GameObjType.Generator].EnterReadLock(); | |||
| try | |||
| { | |||
| double bgmVolume = 0; | |||
| foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator]) | |||
| { | |||
| if (XY.Distance(newPlayer.Position, generator.Position) <= newPlayer.AlertnessRadius) | |||
| { | |||
| if ((double)newPlayer.AlertnessRadius * generator.DegreeOfFRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position) > bgmVolume) | |||
| bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfFRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position); | |||
| } | |||
| } | |||
| if (bgmVolume > 0) | |||
| newPlayer.BgmDictionary.Add(BgmType.StudentIsApproaching, bgmVolume); | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||
| } | |||
| }, | |||
| 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 | |||
| return newPlayer.ID; | |||
| } | |||
| @@ -100,20 +176,6 @@ namespace Gaming | |||
| { | |||
| if (gameMap.Timer.IsGaming) | |||
| return false; | |||
| gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | |||
| try | |||
| { | |||
| foreach (Character player in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| player.CanMove = true; | |||
| player.AddShield(GameData.shieldTimeAtBirth); | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||
| } | |||
| propManager.StartProducing(); | |||
| @@ -110,7 +110,8 @@ namespace Gaming | |||
| if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地 | |||
| { | |||
| dropProp = player.PropInventory; | |||
| dropProp.SetNewPos(GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell)); | |||
| XY res = GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell); | |||
| dropProp.ReSetPos(res, gameMap.GetPlaceType(res)); | |||
| } | |||
| player.PropInventory = pickProp; | |||
| gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); | |||
| @@ -151,7 +152,7 @@ namespace Gaming | |||
| return; | |||
| prop.CanMove = true; | |||
| prop.SetNewPos(player.Position); | |||
| prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position)); | |||
| gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); | |||
| try | |||
| { | |||
| @@ -187,16 +188,16 @@ namespace Gaming | |||
| switch (r.Next(0, 4)) | |||
| { | |||
| case 0: | |||
| gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddLIFE(randPos)); | |||
| gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddLIFE(randPos, gameMap.GetPlaceType(randPos))); | |||
| break; | |||
| case 1: | |||
| gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddSpeed(randPos)); | |||
| gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddSpeed(randPos, gameMap.GetPlaceType(randPos))); | |||
| break; | |||
| case 2: | |||
| gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Shield(randPos)); | |||
| gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Shield(randPos, gameMap.GetPlaceType(randPos))); | |||
| break; | |||
| case 3: | |||
| gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Spear(randPos)); | |||
| gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Spear(randPos, gameMap.GetPlaceType(randPos))); | |||
| break; | |||
| default: | |||
| break; | |||
| @@ -230,11 +231,11 @@ namespace Gaming | |||
| } | |||
| ); | |||
| availableCellForGenerateProp = new List<XY>(); | |||
| for (int i = 0; i < gameMap.ProtoGameMap.GetLength(0); i++) | |||
| for (int i = 0; i < gameMap.protoGameMap.GetLength(0); i++) | |||
| { | |||
| for (int j = 0; j < gameMap.ProtoGameMap.GetLength(1); j++) | |||
| for (int j = 0; j < gameMap.protoGameMap.GetLength(1); j++) | |||
| { | |||
| if (gameMap.ProtoGameMap[i, j] == (int)PlaceType.Null) | |||
| if (gameMap.protoGameMap[i, j] == (int)PlaceType.Null) | |||
| { | |||
| availableCellForGenerateProp.Add(GameData.GetCellCenterPos(i, j)); | |||
| } | |||
| @@ -7,13 +7,13 @@ namespace Preparation.Interface | |||
| public GameObjType Type { get; } | |||
| public long ID { get; } | |||
| public XY Position { get; } // if Square, Pos equals the center | |||
| public PlaceType Place { get; } | |||
| public XY FacingDirection { get; } | |||
| public bool IsRigid { get; } | |||
| public ShapeType Shape { get; } | |||
| public bool CanMove { get; set; } | |||
| public bool IsMoving { get; set; } | |||
| public bool IsResetting { get; set; } // reviving | |||
| public bool IsAvailable { get; } | |||
| public int Radius { get; } // if Square, Radius equals half length of one side | |||
| protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现 | |||
| } | |||
| } | |||
| @@ -12,6 +12,8 @@ namespace Preparation.Interface | |||
| Dictionary<GameObjType, IList<IGameObj>> GameObjDict { get; } | |||
| Dictionary<GameObjType, ReaderWriterLockSlim> GameObjLockDict { get; } | |||
| public uint[,] ProtoGameMap { get; } | |||
| public PlaceType GetPlaceType(IGameObj obj); | |||
| public bool IsOutOfBound(IGameObj obj); | |||
| public IOutOfBound GetOutOfBound(XY pos); // 返回新建的一个OutOfBound对象 | |||
| } | |||
| @@ -7,8 +7,10 @@ namespace Preparation.Interface | |||
| { | |||
| object MoveLock { get; } | |||
| public int MoveSpeed { get; } | |||
| public long Move(XY moveVec); | |||
| protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现 | |||
| public bool IsMoving { get; set; } | |||
| public bool IsAvailable { get; } | |||
| public long MovingSetPos(XY moveVec, PlaceType place); | |||
| public void ReSetPos(XY pos, PlaceType place); | |||
| public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 | |||
| { | |||
| if (targetObj == null) | |||
| @@ -12,6 +12,8 @@ namespace Preparation.Interface | |||
| public int MaxBulletNum { get; } | |||
| public List<ActiveSkillType> ListOfIActiveSkill { get; } | |||
| public List<PassiveSkillType> ListOfIPassiveSkill { get; } | |||
| public double Concealment { get; } | |||
| public int AlertnessRadius { get; } | |||
| } | |||
| public interface IGhost : IOccupation | |||
| @@ -41,6 +43,12 @@ namespace Preparation.Interface | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BecomeInvisible, ActiveSkillType.UseKnife }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public double concealment = GameData.basicConcealment * 1.5; | |||
| public double Concealment => concealment; | |||
| public int alertnessRadius = (int)(GameData.basicAlertnessRadius * 1.3); | |||
| public int AlertnessRadius => alertnessRadius; | |||
| } | |||
| public class Athlete : IStudent | |||
| { | |||
| @@ -61,6 +69,13 @@ namespace Preparation.Interface | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BeginToCharge }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public int FixSpeed => GameData.basicFixSpeed / 10 * 6; | |||
| public const int fixSpeed = GameData.basicFixSpeed / 10 * 6; | |||
| public int FixSpeed => fixSpeed; | |||
| public const double concealment = GameData.basicConcealment * 0.9; | |||
| public double Concealment => concealment; | |||
| public const int alertnessRadius = (int)(GameData.basicAlertnessRadius * 0.9); | |||
| public int AlertnessRadius => alertnessRadius; | |||
| } | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| using System; | |||
| using System.Reflection.Metadata.Ecma335; | |||
| using System.Threading; | |||
| namespace Preparation.Utility | |||
| { | |||
| @@ -75,12 +76,15 @@ namespace Preparation.Utility | |||
| public const int basicMoveSpeed = 3800; // 基本移动速度,单位:s-1 | |||
| public const int basicBulletMoveSpeed = 5400; // 基本子弹移动速度,单位:s-1 | |||
| public const int characterMaxSpeed = 12000; // 最大速度 | |||
| public const double basicConcealment = 1.0; | |||
| public const int basicAlertnessRadius = 30700; | |||
| public const int addScoreWhenKillOneLevelPlayer = 30; // 击杀一级角色获得的加分 | |||
| public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | |||
| public const int commonSkillTime = 10000; // 普通技能标准持续时间 | |||
| public const int bulletRadius = 200; // 默认子弹半径 | |||
| public const int reviveTime = 30000; // 复活时间 | |||
| public const int shieldTimeAtBirth = 3000; // 复活时的护盾时间 | |||
| public static XY PosWhoDie = new XY(1, 1); | |||
| public static bool IsGhost(CharacterType characterType) | |||
| @@ -101,9 +105,12 @@ namespace Preparation.Utility | |||
| public const long GemProduceTime = 10000; | |||
| public const long PropProduceTime = 10000; | |||
| public const int PropDuration = 10000; | |||
| #endregion | |||
| #region 物体相关 | |||
| public const int degreeOfFixedGenerator = 10300000; | |||
| #endregion | |||
| #region 游戏帧相关 | |||
| public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | |||
| #endregion | |||
| @@ -8,13 +8,14 @@ namespace Server | |||
| public static class CopyInfo | |||
| { | |||
| // 下面赋值为0的大概率是还没写完 2023-03-03 | |||
| /*public static MessageOfObj? Auto(GameObj gameObj) | |||
| public static MessageOfObj? Auto(GameObj gameObj) | |||
| { | |||
| if (gameObj.Type == Preparation.Utility.GameObjType.Character) | |||
| { | |||
| Character character = (Character)gameObj; | |||
| if (character.IsGhost()) | |||
| return Tri | |||
| return Tricker((Character)character); | |||
| else return Student((Character)character); | |||
| } | |||
| else if (gameObj.Type == Preparation.Utility.GameObjType.Bullet) | |||
| return Bullet((Bullet)gameObj); | |||
| @@ -25,28 +26,33 @@ namespace Server | |||
| else if (gameObj.Type == Preparation.Utility.GameObjType.PickedProp) | |||
| return PickedProp((PickedProp)gameObj); | |||
| else return null; //先写着防报错 | |||
| }*/ | |||
| } | |||
| private static MessageOfStudent? Student(Character player) | |||
| private static MessageOfObj? Student(Character player) | |||
| { | |||
| MessageOfStudent msg = new MessageOfStudent(); | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| if (player.IsGhost()) return null; | |||
| msg.X = player.Position.x; | |||
| msg.Y = player.Position.y; | |||
| msg.Speed = player.MoveSpeed; | |||
| msg.Determination = player.HP; | |||
| msg.FailNum = 0; | |||
| msg.TimeUntilSkillAvailable = 0; | |||
| msg.StudentType = StudentType.NullStudentType; // 下面写 | |||
| msg.Guid = 0; | |||
| msg.State = StudentState.NullStatus; | |||
| msg.FailTime = 0; | |||
| msg.EmoTime = 0; | |||
| msg.PlayerId = 0; | |||
| msg.ViewRange = 0; | |||
| msg.Radius = 0; | |||
| msg.StudentMessage.X = player.Position.x; | |||
| msg.StudentMessage.Y = player.Position.y; | |||
| msg.StudentMessage.Speed = player.MoveSpeed; | |||
| msg.StudentMessage.Determination = player.HP; | |||
| //msg.StudentMessage.FailNum = 0; | |||
| foreach (var keyValue in player.TimeUntilActiveSkillAvailable) | |||
| msg.StudentMessage.TimeUntilSkillAvailable.Add(keyValue.Value); | |||
| //msg.StudentMessage.StudentType; // 下面写 | |||
| msg.StudentMessage.Guid = player.ID; | |||
| msg.StudentMessage.State = StudentState.NullStatus; | |||
| msg.StudentMessage.FailTime = 0; | |||
| msg.StudentMessage.EmoTime = 0; | |||
| msg.StudentMessage.PlayerId = 0; | |||
| msg.StudentMessage.ViewRange = 0; | |||
| msg.StudentMessage.Radius = 0; | |||
| msg.StudentMessage.Damage = 0; | |||
| msg.StudentMessage.DangerAlert = 0; | |||
| msg.StudentMessage.Score = 0; | |||
| msg.StudentMessage.TreatProgress = 0; | |||
| msg.StudentMessage.RescueProgress = 0; | |||
| foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | |||
| { | |||
| @@ -55,178 +61,76 @@ namespace Server | |||
| switch (kvp.Key) // StudentBuffType具体内容待定 | |||
| { | |||
| case Preparation.Utility.BuffType.Spear: | |||
| msg.Buff.Add(StudentBuffType.NullSbuffType); | |||
| msg.StudentMessage.Buff.Add(StudentBuffType.NullSbuffType); | |||
| break; | |||
| case Preparation.Utility.BuffType.AddLIFE: | |||
| msg.Buff.Add(StudentBuffType.NullSbuffType); | |||
| msg.StudentMessage.Buff.Add(StudentBuffType.NullSbuffType); | |||
| break; | |||
| case Preparation.Utility.BuffType.Shield: | |||
| msg.Buff.Add(StudentBuffType.NullSbuffType); | |||
| msg.StudentMessage.Buff.Add(StudentBuffType.NullSbuffType); | |||
| break; | |||
| case Preparation.Utility.BuffType.AddSpeed: | |||
| msg.Buff.Add(StudentBuffType.NullSbuffType); | |||
| msg.StudentMessage.Buff.Add(StudentBuffType.NullSbuffType); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| switch (player.Place) | |||
| { | |||
| case Preparation.Utility.PlaceType.EmergencyExit: | |||
| msg.Place = PlaceType.HiddenGate; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Doorway: | |||
| msg.Place = PlaceType.Gate; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass: | |||
| msg.Place = PlaceType.Grass; | |||
| break; | |||
| case Preparation.Utility.PlaceType.BirthPoint1: | |||
| case Preparation.Utility.PlaceType.BirthPoint2: | |||
| case Preparation.Utility.PlaceType.BirthPoint3: | |||
| case Preparation.Utility.PlaceType.BirthPoint4: | |||
| case Preparation.Utility.PlaceType.BirthPoint5: | |||
| case Preparation.Utility.PlaceType.Null: | |||
| msg.Place = PlaceType.Land; | |||
| break; | |||
| // case Preparation.Utility.PlaceType.Invisible: | |||
| // msg.MessageOfHuman.Place = Communication.Proto.PlaceType.Invisible; | |||
| // break; | |||
| default: | |||
| msg.Place = PlaceType.NullPlaceType; | |||
| break; | |||
| } | |||
| //Character的储存方式可能得改,用enum type存道具和子弹,不应该用对象 | |||
| //现在懒得改了,有时间再重整一波 | |||
| if (player.PropInventory == null) | |||
| msg.Prop.Add(PropType.NullPropType); | |||
| msg.StudentMessage.Prop.Add(PropType.NullPropType); | |||
| else | |||
| { | |||
| switch (player.PropInventory.GetPropType()) | |||
| { | |||
| case Preparation.Utility.PropType.Gem: | |||
| msg.Prop.Add(PropType.NullPropType); | |||
| msg.StudentMessage.Prop.Add(PropType.NullPropType); | |||
| break; | |||
| /*case Preparation.Utility.PropType.addLIFE: | |||
| msg.MessageOfHuman.Prop = Communication.Proto.PropType.AddLife; | |||
| msg.StudentMessage.MessageOfHuman.Prop = Communication.Proto.PropType.AddLife; | |||
| break; | |||
| case Preparation.Utility.PropType.addSpeed: | |||
| msg.MessageOfHuman.Prop = Communication.Proto.PropType.AddSpeed; | |||
| msg.StudentMessage.MessageOfHuman.Prop = Communication.Proto.PropType.AddSpeed; | |||
| break; | |||
| case Preparation.Utility.PropType.Shield: | |||
| msg.MessageOfHuman.Prop = Communication.Proto.PropType.Shield; | |||
| msg.StudentMessage.MessageOfHuman.Prop = Communication.Proto.PropType.Shield; | |||
| break; | |||
| case Preparation.Utility.PropType.Spear: | |||
| msg.MessageOfHuman.Prop = Communication.Proto.PropType.Spear; | |||
| msg.StudentMessage.MessageOfHuman.Prop = Communication.Proto.PropType.Spear; | |||
| break; | |||
| default: | |||
| msg.Prop = PropType.NullPropType; | |||
| msg.StudentMessage.Prop = PropType.NullPropType; | |||
| break;*/ | |||
| } | |||
| } | |||
| /*switch (player.PassiveSkillType) 需要对接一下,proto里似乎没有这个 | |||
| { | |||
| case Preparation.Utility.PassiveSkillType.RecoverAfterBattle: | |||
| msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.RecoverAfterBattle; | |||
| break; | |||
| case Preparation.Utility.PassiveSkillType.SpeedUpWhenLeavingGrass: | |||
| msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.SpeedUpWhenLeavingGrass; | |||
| break; | |||
| case Preparation.Utility.PassiveSkillType.Vampire: | |||
| msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.Vampire; | |||
| break; | |||
| default: | |||
| msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.NullPassiveSkillType; | |||
| break; | |||
| } | |||
| switch (player.CommonSkillType) | |||
| { | |||
| case Preparation.Utility.ActiveSkillType.BecomeAssassin: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeAssassin; | |||
| break; | |||
| case Preparation.Utility.ActiveSkillType.BecomeVampire: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeVampire; | |||
| break; | |||
| case Preparation.Utility.ActiveSkillType.NuclearWeapon: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.NuclearWeapon; | |||
| break; | |||
| case Preparation.Utility.ActiveSkillType.SuperFast: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.SuperFast; | |||
| break; | |||
| default: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.NullActiveSkillType; | |||
| break; | |||
| } | |||
| switch (player.BulletOfPlayer) | |||
| { | |||
| case Preparation.Utility.BulletType.AtomBomb: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.AtomBomb; | |||
| break; | |||
| case Preparation.Utility.BulletType.OrdinaryBullet: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.OrdinaryBullet; | |||
| break; | |||
| case Preparation.Utility.BulletType.FastBullet: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.FastBullet; | |||
| break; | |||
| case Preparation.Utility.BulletType.LineBullet: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.LineBullet; | |||
| break; | |||
| default: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.NullBulletType; | |||
| break; | |||
| }*/ | |||
| return msg; | |||
| } | |||
| private static MessageOfTricker? Butcher(Character player) | |||
| private static MessageOfObj? Tricker(Character player) | |||
| { | |||
| MessageOfTricker msg = new MessageOfTricker(); | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| if (!player.IsGhost()) return null; | |||
| msg.X = player.Position.x; | |||
| msg.Y = player.Position.y; | |||
| msg.Speed = player.MoveSpeed; | |||
| msg.Damage = 0; | |||
| msg.TimeUntilSkillAvailable = 0; | |||
| //msg.Place = 0; 下面写了 | |||
| //msg.Prop = PropType.NullPropType; // 下面写 | |||
| msg.TrickerType = TrickerType.NullTrickerType; // 下面写 | |||
| msg.Guid = 0; | |||
| msg.Movable = false; | |||
| msg.PlayerId = 0; | |||
| msg.ViewRange = 0; | |||
| msg.Radius = 0; | |||
| //msg.Buff[0] = ButcherBuffType.NullSbuffType; 下面写了 | |||
| /* THUAI5中的内容 | |||
| msg.BulletNum = player.BulletNum; | |||
| msg.CanMove = player.CanMove; | |||
| msg.CD = player.CD; | |||
| msg.GemNum = player.GemNum; | |||
| msg.Guid = player.ID; | |||
| msg.IsDeceased = player.IsDeceased; | |||
| msg.LifeNum = player.DeathCount + 1; | |||
| msg.Radius = player.Radius; | |||
| msg.TimeUntilCommonSkillAvailable = player.TimeUntilCommonSkillAvailable; | |||
| msg.TeamID = player.TeamID; | |||
| msg.PlayerID = player.PlayerID; | |||
| msg.IsInvisible = player.IsInvisible; | |||
| msg.FacingDirection = player.FacingDirection; | |||
| msg.TrickerMessage.X = player.Position.x; | |||
| msg.TrickerMessage.Y = player.Position.y; | |||
| msg.TrickerMessage.Speed = player.MoveSpeed; | |||
| msg.TrickerMessage.Damage = 0; | |||
| msg.TrickerMessage.TimeUntilSkillAvailable = 0; | |||
| //msg.TrickerMessage.Place = 0; 下面写了 | |||
| //msg.TrickerMessage.Prop = PropType.NullPropType; // 下面写 | |||
| msg.TrickerMessage.TrickerType = TrickerType.NullTrickerType; // 下面写 | |||
| msg.TrickerMessage.Guid = 0; | |||
| msg.TrickerMessage.Movable = false; | |||
| msg.TrickerMessage.PlayerId = 0; | |||
| msg.TrickerMessage.ViewRange = 0; | |||
| msg.TrickerMessage.Radius = 0; | |||
| //msg.TrickerMessage.Buff[0] = ButcherBuffType.NullSbuffType; 下面写了 | |||
| //应该要发队伍分数,这里先发个人分数 | |||
| msg.MessageOfHuman.Score = player.Score; | |||
| //这条暂时没啥用 | |||
| msg.MessageOfHuman.TimeUntilUltimateSkillAvailable = 0; | |||
| msg.MessageOfHuman.Vampire = player.Vampire;*/ | |||
| foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | |||
| { | |||
| @@ -235,16 +139,16 @@ namespace Server | |||
| switch (kvp.Key) // ButcherBuffType具体内容待定 | |||
| { | |||
| case Preparation.Utility.BuffType.Spear: | |||
| msg.Buff.Add(TrickerBuffType.NullTbuffType); | |||
| msg.TrickerMessage.Buff.Add(TrickerBuffType.NullTbuffType); | |||
| break; | |||
| case Preparation.Utility.BuffType.AddLIFE: | |||
| msg.Buff.Add(TrickerBuffType.NullTbuffType); | |||
| msg.TrickerMessage.Buff.Add(TrickerBuffType.NullTbuffType); | |||
| break; | |||
| case Preparation.Utility.BuffType.Shield: | |||
| msg.Buff.Add(TrickerBuffType.NullTbuffType); | |||
| msg.TrickerMessage.Buff.Add(TrickerBuffType.NullTbuffType); | |||
| break; | |||
| case Preparation.Utility.BuffType.AddSpeed: | |||
| msg.Buff.Add(TrickerBuffType.NullTbuffType); | |||
| msg.TrickerMessage.Buff.Add(TrickerBuffType.NullTbuffType); | |||
| break; | |||
| default: | |||
| break; | |||
| @@ -254,261 +158,205 @@ namespace Server | |||
| /*switch (player.Place) | |||
| { | |||
| case Preparation.Utility.PlaceType.Land: | |||
| msg.Place = PlaceType.Land; | |||
| msg.TrickerMessage.Place = PlaceType.Land; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass1: | |||
| msg.Place = PlaceType.Grass; | |||
| msg.TrickerMessage.Place = PlaceType.Grass; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass2: | |||
| msg.Place = PlaceType.Grass; | |||
| msg.TrickerMessage.Place = PlaceType.Grass; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass3: | |||
| msg.Place = PlaceType.Grass; | |||
| msg.TrickerMessage.Place = PlaceType.Grass; | |||
| break; | |||
| // case Preparation.Utility.PlaceType.Invisible: | |||
| // msg.MessageOfHuman.Place = Communication.Proto.PlaceType.Invisible; | |||
| // msg.TrickerMessage.MessageOfHuman.Place = Communication.Proto.PlaceType.Invisible; | |||
| // break; | |||
| default: | |||
| msg.Place = PlaceType.NullPlaceType; | |||
| msg.TrickerMessage.Place = PlaceType.NullPlaceType; | |||
| break; | |||
| }*/ | |||
| //Character的储存方式可能得改,用enum type存道具和子弹,不应该用对象 | |||
| //现在懒得改了,有时间再重整一波 | |||
| /*if (player.PropInventory == null) | |||
| msg.Prop = PropType.NullPropType; | |||
| msg.TrickerMessage.Prop = PropType.NullPropType; | |||
| else | |||
| { | |||
| switch (player.PropInventory.GetPropType()) | |||
| { | |||
| case Preparation.Utility.PropType.Gem: | |||
| msg.Prop = PropType.NullPropType; | |||
| msg.TrickerMessage.Prop = PropType.NullPropType; | |||
| break; | |||
| case Preparation.Utility.PropType.addLIFE: | |||
| msg.MessageOfHuman.Prop = Communication.Proto.PropType.AddLife; | |||
| msg.TrickerMessage.MessageOfHuman.Prop = Communication.Proto.PropType.AddLife; | |||
| break; | |||
| case Preparation.Utility.PropType.addSpeed: | |||
| msg.MessageOfHuman.Prop = Communication.Proto.PropType.AddSpeed; | |||
| msg.TrickerMessage.MessageOfHuman.Prop = Communication.Proto.PropType.AddSpeed; | |||
| break; | |||
| case Preparation.Utility.PropType.Shield: | |||
| msg.MessageOfHuman.Prop = Communication.Proto.PropType.Shield; | |||
| msg.TrickerMessage.MessageOfHuman.Prop = Communication.Proto.PropType.Shield; | |||
| break; | |||
| case Preparation.Utility.PropType.Spear: | |||
| msg.MessageOfHuman.Prop = Communication.Proto.PropType.Spear; | |||
| msg.TrickerMessage.MessageOfHuman.Prop = Communication.Proto.PropType.Spear; | |||
| break; | |||
| default: | |||
| msg.Prop = PropType.NullPropType; | |||
| msg.TrickerMessage.Prop = PropType.NullPropType; | |||
| break; | |||
| } | |||
| }*/ | |||
| /*switch (player.PassiveSkillType) 需要对接一下,proto里似乎没有这个 | |||
| { | |||
| case Preparation.Utility.PassiveSkillType.RecoverAfterBattle: | |||
| msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.RecoverAfterBattle; | |||
| break; | |||
| case Preparation.Utility.PassiveSkillType.SpeedUpWhenLeavingGrass: | |||
| msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.SpeedUpWhenLeavingGrass; | |||
| break; | |||
| case Preparation.Utility.PassiveSkillType.Vampire: | |||
| msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.Vampire; | |||
| break; | |||
| default: | |||
| msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.NullPassiveSkillType; | |||
| break; | |||
| } | |||
| switch (player.CommonSkillType) | |||
| { | |||
| case Preparation.Utility.ActiveSkillType.BecomeAssassin: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeAssassin; | |||
| break; | |||
| case Preparation.Utility.ActiveSkillType.BecomeVampire: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeVampire; | |||
| break; | |||
| case Preparation.Utility.ActiveSkillType.NuclearWeapon: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.NuclearWeapon; | |||
| break; | |||
| case Preparation.Utility.ActiveSkillType.SuperFast: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.SuperFast; | |||
| break; | |||
| default: | |||
| msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.NullActiveSkillType; | |||
| break; | |||
| } | |||
| switch (player.BulletOfPlayer) | |||
| { | |||
| case Preparation.Utility.BulletType.AtomBomb: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.AtomBomb; | |||
| break; | |||
| case Preparation.Utility.BulletType.OrdinaryBullet: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.OrdinaryBullet; | |||
| break; | |||
| case Preparation.Utility.BulletType.FastBullet: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.FastBullet; | |||
| break; | |||
| case Preparation.Utility.BulletType.LineBullet: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.LineBullet; | |||
| break; | |||
| default: | |||
| msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.NullBulletType; | |||
| break; | |||
| }*/ | |||
| return msg; | |||
| } | |||
| /*private static MessageToClient.Types.GameObjMessage Bullet(Bullet bullet) | |||
| private static MessageOfObj Bullet(Bullet bullet) | |||
| { | |||
| MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); | |||
| msg.MessageOfBullet = new MessageOfBullet(); | |||
| msg.MessageOfBullet.FacingDirection = bullet.FacingDirection; | |||
| msg.MessageOfBullet.Guid = bullet.ID; | |||
| msg.MessageOfBullet.BombRange = BulletFactory.BulletBombRange(bullet.TypeOfBullet); | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.BulletMessage.X = bullet.Position.x; | |||
| msg.BulletMessage.Y = bullet.Position.y; | |||
| //msg.BulletMessage.FacingDirection = bullet.FacingDirection; // XY转double? | |||
| msg.BulletMessage.Guid = bullet.ID; | |||
| msg.BulletMessage.Team = PlayerType.NullPlayerType; | |||
| msg.BulletMessage.Place = PlaceType.NullPlaceType; | |||
| msg.BulletMessage.BombRange = 0; | |||
| switch (bullet.TypeOfBullet) | |||
| { | |||
| case Preparation.Utility.BulletType.AtomBomb: | |||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.AtomBomb; | |||
| msg.BulletMessage.Type = BulletType.AtomBomb; | |||
| break; | |||
| case Preparation.Utility.BulletType.OrdinaryBullet: | |||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.OrdinaryBullet; | |||
| msg.BulletMessage.Type = BulletType.OrdinaryBullet; | |||
| break; | |||
| case Preparation.Utility.BulletType.FastBullet: | |||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.FastBullet; | |||
| msg.BulletMessage.Type = BulletType.FastBullet; | |||
| break; | |||
| case Preparation.Utility.BulletType.LineBullet: | |||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.LineBullet; | |||
| msg.BulletMessage.Type = BulletType.LineBullet; | |||
| break; | |||
| default: | |||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.NullBulletType; | |||
| msg.BulletMessage.Type = BulletType.NullBulletType; | |||
| break; | |||
| } | |||
| msg.MessageOfBullet.X = bullet.Position.x; | |||
| msg.MessageOfBullet.Y = bullet.Position.y; | |||
| if (bullet.Parent != null) | |||
| msg.MessageOfBullet.ParentTeamID = bullet.Parent.TeamID; | |||
| switch (bullet.Place) | |||
| //if (bullet.Parent != null) | |||
| //msg.BulletMessage.MessageOfBullet.ParentTeamID = bullet.Parent.TeamID; | |||
| /*switch (bullet.Place) | |||
| { | |||
| case Preparation.Utility.PlaceType.Null: | |||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.Null; | |||
| msg.BulletMessage.MessageOfBullet.Place = Communication.Proto.PlaceType.Null; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass: | |||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass; | |||
| msg.BulletMessage.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass: | |||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass; | |||
| msg.BulletMessage.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass: | |||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass; | |||
| msg.BulletMessage.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass; | |||
| break; | |||
| default: | |||
| msg.MessageOfBullet.Place = Communication.Proto.PlacccceType.NullPlaceType; | |||
| msg.BulletMessage.MessageOfBullet.Place = Communication.Proto.PlacccceType.NullPlaceType; | |||
| break; | |||
| } | |||
| }*/ | |||
| return msg; | |||
| }*/ | |||
| } | |||
| private static MessageOfProp Prop(Prop prop) | |||
| private static MessageOfObj Prop(Prop prop) | |||
| { | |||
| MessageOfProp msg = new MessageOfProp(); | |||
| //msg.Type = PropType.NullPropType; 下面写 | |||
| msg.X = prop.Position.x; | |||
| msg.Y = prop.Position.y; | |||
| msg.FacingDirection = 0; | |||
| msg.Guid = 0; | |||
| msg.Place = PlaceType.NullPlaceType; | |||
| msg.Size = 0; | |||
| msg.IsMoving = false; | |||
| /* THUAI5中的内容 | |||
| msg.MessageOfProp.FacingDirection = prop.FacingDirection; | |||
| msg.MessageOfProp.Guid = prop.ID; | |||
| msg.MessageOfProp.IsMoving = prop.IsMoving;*/ | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| //msg.PropMessage.Type = PropType.NullPropType; 下面写 | |||
| msg.PropMessage.X = prop.Position.x; | |||
| msg.PropMessage.Y = prop.Position.y; | |||
| msg.PropMessage.FacingDirection = 0; | |||
| msg.PropMessage.Guid = 0; | |||
| msg.PropMessage.Place = PlaceType.NullPlaceType; | |||
| msg.PropMessage.Size = 0; | |||
| msg.PropMessage.IsMoving = false; | |||
| switch (prop.GetPropType()) | |||
| { | |||
| /*case Preparation.Utility.PropType.Gem: | |||
| msg.Type = PropType.Gem; | |||
| msg.PropMessage.Type = PropType.Gem; | |||
| break; | |||
| case Preparation.Utility.PropType.addLIFE: | |||
| msg.Type = PropType.AddLife; | |||
| msg.PropMessage.Type = PropType.AddLife; | |||
| break; | |||
| case Preparation.Utility.PropType.addSpeed: | |||
| msg.Type = PropType.AddSpeed; | |||
| msg.PropMessage.Type = PropType.AddSpeed; | |||
| break; | |||
| case Preparation.Utility.PropType.Shield: | |||
| msg.Type = PropType.Shield; | |||
| msg.PropMessage.Type = PropType.Shield; | |||
| break; | |||
| case Preparation.Utility.PropType.Spear: | |||
| msg.Type = PropType.Spear; | |||
| msg.PropMessage.Type = PropType.Spear; | |||
| break;*/ | |||
| default: | |||
| msg.Type = PropType.NullPropType; | |||
| msg.PropMessage.Type = PropType.NullPropType; | |||
| break; | |||
| } | |||
| /*if(prop is Gem) | |||
| { | |||
| msg.MessageOfProp.Size = ((Gem)prop).Size; | |||
| msg.PropMessage.MessageOfProp.Size = ((Gem)prop).Size; | |||
| } | |||
| else | |||
| { | |||
| msg.MessageOfProp.Size = 1; | |||
| msg.PropMessage.MessageOfProp.Size = 1; | |||
| } | |||
| switch (prop.Place) | |||
| { | |||
| case Preparation.Utility.PlaceType.Null: | |||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.Null; | |||
| msg.PropMessage.MessageOfProp.Place = Communication.Proto.PlaceType.Null; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass: | |||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.Grass; | |||
| msg.PropMessage.MessageOfProp.Place = Communication.Proto.PlaceType.Grass; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass: | |||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.Grass; | |||
| msg.PropMessage.MessageOfProp.Place = Communication.Proto.PlaceType.Grass; | |||
| break; | |||
| case Preparation.Utility.PlaceType.Grass: | |||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.Grass; | |||
| msg.PropMessage.MessageOfProp.Place = Communication.Proto.PlaceType.Grass; | |||
| break; | |||
| default: | |||
| msg.MessageOfProp.Place = Communication.Proto.PlacccceType.NullPlaceType; | |||
| msg.PropMessage.MessageOfProp.Place = Communication.Proto.PlacccceType.NullPlaceType; | |||
| break; | |||
| }*/ | |||
| return msg; | |||
| } | |||
| /*private static MessageOfBombedBullet BombedBullet(BombedBullet bombedBullet) | |||
| private static MessageOfObj BombedBullet(BombedBullet bombedBullet) | |||
| { | |||
| MessageOfBombedBullet msg = new MessageOfBombedBullet; | |||
| MessageOfObj msg = new MessageOfObj(); | |||
| msg.FacingDirection = bombedBullet.FacingDirection; | |||
| msg.X = bombedBullet.bulletHasBombed.Position.x; | |||
| msg.Y = bombedBullet.bulletHasBombed.Position.y; | |||
| msg.MappingID = bombedBullet.MappingID; | |||
| msg.BombRange = BulletFactory.BulletBombRange(bombedBullet.bulletHasBombed.TypeOfBullet); | |||
| msg.BombedBulletMessage.X = bombedBullet.bulletHasBombed.Position.x; | |||
| msg.BombedBulletMessage.Y = bombedBullet.bulletHasBombed.Position.y; | |||
| //msg.BombedBulletMessage.FacingDirection = bombedBullet.FacingDirection; XY类型转double? | |||
| msg.BombedBulletMessage.MappingId = bombedBullet.MappingID; | |||
| msg.BombedBulletMessage.BombRange = BulletFactory.BulletRadius(bombedBullet.bulletHasBombed.TypeOfBullet); // 待确认 | |||
| switch (bombedBullet.bulletHasBombed.TypeOfBullet) | |||
| { | |||
| case Preparation.Utility.BulletType.OrdinaryBullet: | |||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.OrdinaryBullet; | |||
| msg.BombedBulletMessage.Type = BulletType.OrdinaryBullet; | |||
| break; | |||
| case Preparation.Utility.BulletType.AtomBomb: | |||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.AtomBomb; | |||
| msg.BombedBulletMessage.Type = BulletType.AtomBomb; | |||
| break; | |||
| case Preparation.Utility.BulletType.FastBullet: | |||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.FastBullet; | |||
| msg.BombedBulletMessage.Type = BulletType.FastBullet; | |||
| break; | |||
| case Preparation.Utility.BulletType.LineBullet: | |||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.LineBullet; | |||
| msg.BombedBulletMessage.Type = BulletType.LineBullet; | |||
| break; | |||
| default: | |||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.NullBulletType; | |||
| msg.BombedBulletMessage.Type = BulletType.NullBulletType; | |||
| break; | |||
| } | |||
| return msg; | |||
| }*/ | |||
| } | |||
| /*private static MessageToClient.Types.GameObjMessage PickedProp(PickedProp pickedProp) | |||
| private static MessageOfObj PickedProp(PickedProp pickedProp) | |||
| { | |||
| MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); | |||
| msg.MessageOfPickedProp = new MessageOfPickedProp(); | |||
| MessageOfObj msg = new MessageOfObj(); // MessageOfObj中没有PickedProp | |||
| /*msg.MessageOfPickedProp = new MessageOfPickedProp(); | |||
| msg.MessageOfPickedProp.MappingID = pickedProp.MappingID; | |||
| msg.MessageOfPickedProp.X = pickedProp.PropHasPicked.Position.x; | |||
| @@ -534,8 +382,8 @@ namespace Server | |||
| default: | |||
| msg.MessageOfPickedProp.Type = Communication.Proto.PropType.NullPropType; | |||
| break; | |||
| } | |||
| }*/ | |||
| return msg; | |||
| }*/ | |||
| } | |||
| } | |||
| } | |||
| @@ -7,6 +7,7 @@ using System.Net.Http.Headers; | |||
| using Gaming; | |||
| using GameClass.GameObj; | |||
| using Preparation.Utility; | |||
| using Playback; | |||
| namespace Server | |||
| @@ -37,7 +38,7 @@ namespace Server | |||
| private readonly object messageToAllClientsLock = new(); | |||
| public static readonly long SendMessageToClientIntervalInMilliseconds = 50; | |||
| private readonly Semaphore endGameInfoSema = new(0, 1); | |||
| // private MessageWriter? mwr = null; | |||
| private MessageWriter? mwr = null; | |||
| public SemaphoreSlim StartGameTest() | |||
| { | |||
| @@ -85,14 +86,24 @@ namespace Server | |||
| var waitHandle = new SemaphoreSlim(gameState == true ? 1 : 0); // 注意修改 | |||
| new Thread(() => | |||
| { | |||
| bool flag = true; | |||
| new FrameRateTaskExecutor<int> | |||
| ( | |||
| () => game.GameMap.Timer.IsGaming, | |||
| ReportGame, | |||
| 1000, | |||
| () => | |||
| { | |||
| ReportGame(); // 最后发一次消息,唤醒发消息的线程,防止发消息的线程由于有概率处在 Wait 状态而卡住 | |||
| if (flag == true) | |||
| { | |||
| ReportGame(GameState.GameStart); | |||
| flag = false; | |||
| } | |||
| else ReportGame(GameState.GameRunning); | |||
| }, | |||
| SendMessageToClientIntervalInMilliseconds, | |||
| () => | |||
| { | |||
| ReportGame(GameState.GameEnd); // 最后发一次消息,唤醒发消息的线程,防止发消息的线程由于有概率处在 Wait 状态而卡住 | |||
| OnGameEnd(); | |||
| return 0; | |||
| } | |||
| ).Start(); | |||
| @@ -109,12 +120,41 @@ namespace Server | |||
| public void WaitForEnd() | |||
| { | |||
| this.endGameSem.Wait(); | |||
| mwr?.Dispose(); | |||
| } | |||
| public void ReportGame() | |||
| private void OnGameEnd() | |||
| { | |||
| game.ClearAllLists(); | |||
| mwr?.Flush(); | |||
| //if (options.ResultFileName != DefaultArgumentOptions.FileName) | |||
| //SaveGameResult(options.ResultFileName + ".json"); | |||
| //SendGameResult(); | |||
| endGameInfoSema.Release(); | |||
| } | |||
| public void ReportGame(GameState gameState, bool requiredGaming = true) | |||
| { | |||
| //currentGameInfo = null; | |||
| var gameObjList = game.GetGameObj(); | |||
| lock (messageToAllClientsLock) | |||
| { | |||
| //currentGameInfo.MapMessage = (Messa(game.GameMap)); | |||
| switch (gameState) | |||
| { | |||
| case GameState.GameRunning: | |||
| case GameState.GameStart: | |||
| case GameState.GameEnd: | |||
| foreach (GameObj gameObj in gameObjList) | |||
| { | |||
| currentGameInfo.ObjMessage.Add(CopyInfo.Auto(gameObj)); | |||
| } | |||
| currentGameInfo.GameState = gameState; | |||
| mwr?.WriteOne(currentGameInfo); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| foreach (var kvp in semaDict) | |||
| { | |||
| @@ -145,7 +185,19 @@ namespace Server | |||
| return true; | |||
| return false; | |||
| } | |||
| private MessageOfMap MapMsg(Map map) | |||
| { | |||
| MessageOfMap msgOfMap = new MessageOfMap(); | |||
| for (int i = 0; i < GameData.rows; i++) | |||
| { | |||
| msgOfMap.Row.Add(new MessageOfMap.Types.Row()); | |||
| for (int j = 0; j < GameData.cols; j++) | |||
| { | |||
| //msgOfMap.Row[i].Col.Add((int)map.ProtoGameMap[i, j]); int转placetype | |||
| } | |||
| } | |||
| return msgOfMap; | |||
| } | |||
| public override Task<BoolRes> TryConnection(IDMsg request, ServerCallContext context) | |||
| { | |||
| Console.WriteLine($"TryConnection ID: {request.PlayerId}"); | |||
| @@ -224,7 +276,8 @@ namespace Server | |||
| } while (game.GameMap.Timer.IsGaming); | |||
| } | |||
| public override Task<BoolRes> Trick(TrickMsg request, ServerCallContext context) | |||
| public override Task<BoolRes> Attack(AttackMsg request, ServerCallContext context) | |||
| { | |||
| game.Attack(request.PlayerId, request.Angle); | |||
| BoolRes boolRes = new(); | |||
| @@ -239,7 +292,7 @@ namespace Server | |||
| public override Task<MoveRes> Move(MoveMsg request, ServerCallContext context) | |||
| { | |||
| Console.WriteLine($"Move ID: {request.PlayerId}, TimeInMilliseconds: {request.TimeInMilliseconds}"); | |||
| Console.WriteLine($"SetPos ID: {request.PlayerId}, TimeInMilliseconds: {request.TimeInMilliseconds}"); | |||
| var gameID = communicationToGameID[PlayerTypeToTeamID(request.PlayerType), request.PlayerId]; | |||
| game.MovePlayer(gameID, (int)request.TimeInMilliseconds, request.Angle); | |||
| // 之后game.MovePlayer可能改为bool类型 | |||
| @@ -348,7 +401,7 @@ namespace Server | |||
| { | |||
| try | |||
| { | |||
| //mwr = new MessageWriter(options.FileName, options.TeamCount, options.PlayerCountPerTeam); | |||
| mwr = new MessageWriter(options.FileName, options.TeamCount, options.PlayerCountPerTeam); | |||
| } | |||
| catch | |||
| { | |||
| @@ -21,6 +21,7 @@ | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\..\dependency\proto\Protos.csproj" /> | |||
| <ProjectReference Include="..\..\playback\Playback\Playback.csproj" /> | |||
| <ProjectReference Include="..\GameClass\GameClass.csproj" /> | |||
| <ProjectReference Include="..\GameEngine\GameEngine.csproj" /> | |||
| <ProjectReference Include="..\Gaming\Gaming.csproj" /> | |||
| @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Protos", "..\dependency\pro | |||
| EndProject | |||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameEngine", "GameEngine\GameEngine.csproj", "{1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}" | |||
| EndProject | |||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playback", "..\playback\Playback\Playback.csproj", "{FF22960A-6BD9-4C80-A029-9A39FB8F64C4}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| Debug|Any CPU = Debug|Any CPU | |||
| @@ -51,6 +53,10 @@ Global | |||
| {1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| {FF22960A-6BD9-4C80-A029-9A39FB8F64C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {FF22960A-6BD9-4C80-A029-9A39FB8F64C4}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {FF22960A-6BD9-4C80-A029-9A39FB8F64C4}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {FF22960A-6BD9-4C80-A029-9A39FB8F64C4}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| @@ -1,7 +1,7 @@ | |||
| # 规则Logic | |||
| ## 说明 | |||
| - 版本V1.003 | |||
| - 版本V2.0 | |||
| - 该规则直接服务于Sever,并非选手版本 | |||
| - *斜体表示Logic底层尚未(完全)实现* | |||
| - []表示待决定 | |||
| @@ -23,13 +23,11 @@ | |||
| - 底层实现中的属性,不代表界面全部都需要展示,也可能需要额外展示信息 | |||
| - 只展示外部需要的属性,部分属性被省略 | |||
| ### Bgm | |||
| - *double bgmVolume* | |||
| - *BgmType bgmType* | |||
| 对于枚举类BgmType | |||
| 1. *不详的感觉:监管者进入(求生者的警戒半径/监管者的隐蔽度)时,求生者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离)* | |||
| 2. *期待搞事的感觉:求生者进入(监管者的警戒半径/求生者的隐蔽度)时,监管者收到;监管者距离求生者越近,Bgm音量2.越大。bgmVolume=(警戒半径/二者距离)* | |||
| 3. *修理电机的声音: 警戒半径内有电机正在被修理时,全员收到;bgmVolume=(警戒半径/二者距离)*电机修理程度/10300000* | |||
| ### BgmType | |||
| - 枚举类BgmType | |||
| 1. 不详的感觉:监管者进入(求生者的警戒半径/监管者的隐蔽度)时,求生者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离) | |||
| 2. 期待搞事的感觉:求生者进入(监管者的警戒半径/求生者的隐蔽度)时,监管者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/可被发觉的最近的求生者距离) | |||
| 3. 修理电机的声音: 警戒半径内有电机正在被修理时收到;bgmVolume=(警戒半径*电机修理程度/二者距离)/10300000 | |||
| ~~~csharp | |||
| public enum BgmType | |||
| { | |||
| @@ -58,6 +56,7 @@ | |||
| ### 物体 | |||
| - 位置 | |||
| - 位置地形 | |||
| - ID | |||
| - 类型 | |||
| - 面向角度 | |||
| @@ -95,20 +94,20 @@ | |||
| IsClimbingThroughWindows = 15, | |||
| } | |||
| ~~~ | |||
| - *Bgm(数组)* | |||
| - *Bgm(字典)* | |||
| - 得分 | |||
| - ~~回血率/原始回血率~~ | |||
| - 当前子弹类型 | |||
| - 原始子弹类型 | |||
| - 持有道具*(最多三个)(数组)* | |||
| - 持有道具 *(最多三个)(列表)* | |||
| - 是否隐身 | |||
| - 队伍ID | |||
| - 玩家ID | |||
| - 当前Buff | |||
| - 职业类型 | |||
| - 拥有的被动技能(数组) | |||
| - 拥有的主动技能(数组) | |||
| - 各个主动技能CD(数组) | |||
| - 拥有的被动技能(列表) | |||
| - 拥有的主动技能(列表) | |||
| - 各个主动技能CD(字典) | |||
| - *警戒半径* | |||
| - *double 隐蔽度* | |||
| - *翻墙速度* | |||
| @@ -1,5 +1,5 @@ | |||
| using Communication.Proto; | |||
| using Google.Protobuf; | |||
| using Google.Protobuf; | |||
| using Protobuf; | |||
| using System; | |||
| using System.IO; | |||
| using System.IO.Compression; | |||
| @@ -1,5 +1,5 @@ | |||
| using Communication.Proto; | |||
| using Google.Protobuf; | |||
| using Protobuf; | |||
| using System; | |||
| using System.IO; | |||
| using System.IO.Compression; | |||
| @@ -1,9 +1,17 @@ | |||
| <Project Sdk="Microsoft.NET.Sdk"> | |||
| <Project Sdk="Microsoft.NET.Sdk"> | |||
| <PropertyGroup> | |||
| <TargetFrameworks>net6.0;netstandard2.1</TargetFrameworks> | |||
| <TargetFrameworks>net6.0</TargetFrameworks> | |||
| <ImplicitUsings>enable</ImplicitUsings> | |||
| <Nullable>enable</Nullable> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <PackageReference Include="Google.Protobuf" Version="3.22.1" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\..\dependency\proto\Protos.csproj" /> | |||
| </ItemGroup> | |||
| </Project> | |||