| @@ -1,22 +1,6 @@ | |||
| - [CAPI接口](#capi接口) | |||
| - [接口解释](#接口解释) | |||
| - [主动指令](#主动指令) | |||
| - [移动](#移动) | |||
| - [使用技能](#使用技能) | |||
| - [人物](#人物) | |||
| - [攻击](#攻击) | |||
| - [学习与毕业](#学习与毕业) | |||
| - [勉励与唤醒](#勉励与唤醒) | |||
| - [地图互动](#地图互动) | |||
| - [道具](#道具) | |||
| - [信息获取](#信息获取) | |||
| - [队内信息](#队内信息) | |||
| - [查询可视范围内的信息](#查询可视范围内的信息) | |||
| - [查询特定位置物体的信息,下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。](#查询特定位置物体的信息下面的-cellx-和-celly-指的是地图格数而非绝对坐标) | |||
| - [其他](#其他) | |||
| - [辅助函数](#辅助函数) | |||
| - [接口一览](#接口一览) | |||
| # CAPI接口 | |||
| [toc] | |||
| # CAPI接口(cpp) | |||
| ## 接口解释 | |||
| ### 主动指令 | |||
| @@ -66,7 +50,10 @@ | |||
| - `std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const` :返回所有可视道具的信息。 | |||
| - `std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const` :返回所有可视子弹(攻击)的信息。 | |||
| #### 查询特定位置物体的信息,下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。 | |||
| #### 查询特定位置物体的信息 | |||
| 下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。 | |||
| - `THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY)` :返回某一位置场地种类信息。场地种类详见 structure.h 。 | |||
| - `bool IsDoorOpen(int32_t cellX, int32_t cellY) const`:查询特定位置门是否开启 | |||
| - `int32_t GetChestProgress(int32_t cellX, int32_t cellY) const`:查询特定位置箱子开启进度 | |||
| @@ -88,7 +75,7 @@ | |||
| `static inline int GridToCell(int grid) noexcept`:将绝对坐标 grid 转换为地图格数cell。 | |||
| 下面为用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用 | |||
| ~~~c | |||
| ~~~c++ | |||
| void Print(std::string str) const; | |||
| void PrintStudent() const; | |||
| void PrintTricker() const; | |||
| @@ -97,7 +84,7 @@ | |||
| ~~~ | |||
| ## 接口一览 | |||
| ~~~csharp | |||
| ~~~c++ | |||
| // 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 | |||
| virtual std::future<bool> Move(int64_t timeInMilliseconds, double angleInRadian) = 0; | |||
| @@ -0,0 +1,318 @@ | |||
| [toc] | |||
| # CAPI接口(python) | |||
| ## 接口解释 | |||
| ### 主动指令 | |||
| #### 移动 | |||
| - `def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]`:移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为x轴,水平向右方向为y轴** | |||
| - `def MoveRight(self, timeInMilliseconds: int) -> Future[bool]`即向右移动,`MoveLeft`、`MoveDown`、`MoveUp`同理 | |||
| #### 使用技能 | |||
| - `def UseSkill(self, skillID: int) -> Future[bool]`:使用对应序号的主动技能 | |||
| #### 人物 | |||
| - `def EndAllAction(self) -> Future[bool]`:可以使不处在不可行动状态中的玩家终止当前行动 | |||
| #### 攻击 | |||
| - `def Attack(self, angle: float) -> Future[bool]`:`angleInRadian`为攻击方向 | |||
| #### 学习与毕业 | |||
| - `def StartLearning(self) -> Future[bool]`:在教室里开始做作业 | |||
| - `def StartOpenGate(self) -> Future[bool]`:开始开启校门 | |||
| - `def Graduate(self) -> Future[bool]`:从开启的校门或隐藏校门毕业。 | |||
| #### 勉励与唤醒 | |||
| - `def StartEncourageMate(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 PickProp(self, propType: THUAI6.PropType) -> Future[bool]`捡起与自己处于同一个格子(cell)的道具。 | |||
| - `def UseProp(self, propType: THUAI6.PropType) -> Future[bool]`使用对应类型的道具 | |||
| - `def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]`将对应类型的道具扔在原地 | |||
| ### 信息获取 | |||
| #### 队内信息 | |||
| - `std::future<bool> SendMessage(int64_t, std::string)`:给同队的队友发送消息。第一个参数指定发送的对象,第二个参数指定发送的内容,不得超过256字节。 | |||
| - `bool HaveMessage()`:是否有队友发来的尚未接收的信息。 | |||
| - `std::pair<int64_t, std::string> GetMessage()`:从玩家ID为第一个参数的队友获取信息。 | |||
| #### 查询可视范围内的信息 | |||
| - `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` :返回所有可视子弹(攻击)的信息。 | |||
| #### 查询特定位置物体的信息 | |||
| 下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。 | |||
| - `def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType` :返回某一位置场地种类信息。场地种类详见 structure.h 。 | |||
| - `def IsDoorOpen(self, cellX: int, cellY: int) -> bool`:查询特定位置门是否开启 | |||
| - `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 GetHiddenGateState(self, cellX: int, cellY: int) -> THUAI6.HiddenGateState`::查询特定位置隐藏校门状态 | |||
| - `def GetDoorProgress(self, cellX: int, cellY: int) -> 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 GetFullMap(self) -> List[List[THUAI6.PlaceType]]`:返回整张地图的地形信息。 | |||
| ### 辅助函数 | |||
| `def CellToGrid(cell: int) -> int`:将地图格数 cell 转换为绝对坐标grid。 | |||
| `def GridToCell(grid: int) -> int`:将绝对坐标 grid 转换为地图格数cell。 | |||
| 下面为用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用 | |||
| ~~~python | |||
| def Print(self, cont: str) -> None: | |||
| def PrintStudent(self) -> None: | |||
| def PrintTricker(self) -> None: | |||
| def PrintProp(self) -> None: | |||
| def PrintSelfInfo(self) -> None: | |||
| ~~~ | |||
| ## 接口一览 | |||
| ~~~python | |||
| class IAPI(metaclass=ABCMeta): | |||
| # 选手可执行的操作 | |||
| # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 | |||
| @abstractmethod | |||
| def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: | |||
| pass | |||
| # 向特定方向移动 | |||
| @abstractmethod | |||
| def MoveRight(self, timeInMilliseconds: int) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def MoveLeft(self, timeInMilliseconds: int) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def MoveUp(self, timeInMilliseconds: int) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def MoveDown(self, timeInMilliseconds: int) -> Future[bool]: | |||
| pass | |||
| # 道具和技能相关 | |||
| @abstractmethod | |||
| def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def UseSkill(self, skillID: int) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def Attack(self, angle: float) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def OpenDoor(self) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def CloseDoor(self) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def SkipWindow(self) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def StartOpenGate(self) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def StartOpenChest(self) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def EndAllAction(self) -> Future[bool]: | |||
| pass | |||
| # 消息相关,接收消息时无消息则返回(-1, '') | |||
| @abstractmethod | |||
| def SendMessage(self, toID: int, message: str) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def HaveMessage(self) -> bool: | |||
| pass | |||
| @abstractmethod | |||
| def GetMessage(self) -> Tuple[int, str]: | |||
| pass | |||
| # 等待下一帧 | |||
| @abstractmethod | |||
| def Wait(self) -> Future[bool]: | |||
| pass | |||
| # 获取各类游戏中的消息 | |||
| @abstractmethod | |||
| def GetFrameCount(self) -> int: | |||
| pass | |||
| @abstractmethod | |||
| def GetPlayerGUIDs(self) -> List[int]: | |||
| pass | |||
| @abstractmethod | |||
| def GetTrickers(self) -> List[THUAI6.Tricker]: | |||
| pass | |||
| @abstractmethod | |||
| def GetStudents(self) -> List[THUAI6.Student]: | |||
| pass | |||
| @abstractmethod | |||
| def GetProps(self) -> List[THUAI6.Prop]: | |||
| pass | |||
| @abstractmethod | |||
| def GetBullets(self) -> List[THUAI6.Bullet]: | |||
| pass | |||
| @abstractmethod | |||
| def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]: | |||
| pass | |||
| @abstractmethod | |||
| def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | |||
| pass | |||
| @abstractmethod | |||
| def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType: | |||
| pass | |||
| @abstractmethod | |||
| def IsDoorOpen(self, cellX: int, cellY: int) -> bool: | |||
| pass | |||
| @abstractmethod | |||
| def GetChestProgress(self, cellX: int, cellY: int) -> int: | |||
| pass | |||
| @abstractmethod | |||
| def GetGateProgress(self, cellX: int, cellY: int) -> int: | |||
| pass | |||
| @abstractmethod | |||
| def GetClassroomProgress(self, cellX: int, cellY: int) -> int: | |||
| pass | |||
| @abstractmethod | |||
| def GetDoorProgress(self, cellX: int, cellY: int) -> int: | |||
| pass | |||
| @abstractmethod | |||
| def GetHiddenGateState(self, cellX: int, cellY: int) -> THUAI6.HiddenGateState: | |||
| pass | |||
| @abstractmethod | |||
| def GetGameInfo(self) -> THUAI6.GameInfo: | |||
| pass | |||
| # 用于DEBUG的输出函数,仅在DEBUG模式下有效 | |||
| @abstractmethod | |||
| def Print(self, cont: str) -> None: | |||
| pass | |||
| @abstractmethod | |||
| def PrintStudent(self) -> None: | |||
| pass | |||
| @abstractmethod | |||
| def PrintTricker(self) -> None: | |||
| pass | |||
| @abstractmethod | |||
| def PrintProp(self) -> None: | |||
| pass | |||
| @abstractmethod | |||
| def PrintSelfInfo(self) -> None: | |||
| pass | |||
| class IStudentAPI(IAPI, metaclass=ABCMeta): | |||
| # 人类阵营的特殊函数 | |||
| @abstractmethod | |||
| def Graduate(self) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def StartLearning(self) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def StartEncourageMate(self, mateID: int) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def StartRouseMate(self, mateID: int) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| def GetSelfInfo(self) -> THUAI6.Student: | |||
| pass | |||
| class ITrickerAPI(IAPI, metaclass=ABCMeta): | |||
| # 屠夫阵营的特殊函数 | |||
| @abstractmethod | |||
| def GetSelfInfo(self) -> THUAI6.Tricker: | |||
| pass | |||
| ~~~ | |||
| @@ -4,11 +4,15 @@ | |||
| ## Visual Studio使用说明 | |||
| 待更新... | |||
| 选手开始编写代码之前,要先编译好服务器(logic)。具体方法是进入logic文件夹,分别编译`logic.sln`和client文件夹里的`client.sln`,然后就可以开始编写自己的代码。需要注意,选手需要分别在Debug和Release模式下编译,然后才可以分别运行`gameServer_dbg.cmd`和`gameServer_rls.cmd` | |||
| ## Python使用说明 | |||
| 选手编写Python代码之前需要安装必要的Python包,具体方法为:Windows下运行(需要在有Python环境的情况下运行)`generate_proto.cmd`,Linux下运行`generate_proto.sh`,然后可以开始进行代码编写。 | |||
| ## cmd脚本的参数修改 | |||
| 待更新... | |||
| 右键点击`.cmd`或`.bat`文件之后,选择编辑就可以开始修改文件。通过在一行的开头加上`::`,可以注释掉该行。 | |||
| ## C++接口必看 | |||
| @@ -42,12 +42,12 @@ private: | |||
| std::unique_ptr<Communication> pComm; | |||
| // ID、阵营记录 | |||
| int64_t playerID; | |||
| THUAI6::PlayerType playerType; | |||
| int64_t playerID; | |||
| // 类型记录 | |||
| THUAI6::StudentType studentType; | |||
| THUAI6::TrickerType trickerType; | |||
| THUAI6::StudentType studentType; | |||
| // GUID信息 | |||
| std::vector<int64_t> playerGUIDs; | |||
| @@ -5,6 +5,7 @@ | |||
| #include <vector> | |||
| #include <array> | |||
| #include <map> | |||
| #include <memory> | |||
| #include "structures.h" | |||
| @@ -5,6 +5,8 @@ | |||
| #include <cstdint> | |||
| #include <array> | |||
| #include <map> | |||
| #include <vector> | |||
| #include <string> | |||
| namespace THUAI6 | |||
| { | |||
| @@ -85,6 +87,7 @@ namespace THUAI6 | |||
| StraightAStudent = 3, | |||
| Robot = 4, | |||
| TechOtaku = 5, | |||
| Sunshine = 6, | |||
| }; | |||
| // 捣蛋鬼类型 | |||
| @@ -174,7 +177,7 @@ namespace THUAI6 | |||
| int32_t viewRange; // 视野范围 | |||
| int64_t playerID; // 玩家ID | |||
| int64_t guid; // 全局唯一ID | |||
| int16_t radius; // 圆形物体的半径或正方形物体的内切圆半径 | |||
| int32_t radius; // 圆形物体的半径或正方形物体的内切圆半径 | |||
| int32_t score; // 分数 | |||
| double facingDirection; // 朝向 | |||
| @@ -285,6 +288,7 @@ namespace THUAI6 | |||
| {StudentType::StraightAStudent, "StraightAStudent"}, | |||
| {StudentType::Robot, "Robot"}, | |||
| {StudentType::TechOtaku, "TechOtaku"}, | |||
| {StudentType::Sunshine, "Sunshine"}, | |||
| }; | |||
| inline std::map<TrickerType, std::string> trickerTypeDict{ | |||
| @@ -5,6 +5,8 @@ | |||
| #include <cstdint> | |||
| #include <cmath> | |||
| #include <map> | |||
| #include <vector> | |||
| #include "Message2Clients.pb.h" | |||
| #include "Message2Server.pb.h" | |||
| #include "MessageType.pb.h" | |||
| @@ -21,6 +23,11 @@ namespace AssistFunction | |||
| return grid / numOfGridPerCell; | |||
| } | |||
| [[nodiscard]] constexpr inline int GridToCell(double grid) noexcept | |||
| { | |||
| return int(grid) / numOfGridPerCell; | |||
| } | |||
| inline bool HaveView(int viewRange, int x, int y, int newX, int newY, std::vector<std::vector<THUAI6::PlaceType>>& map) | |||
| { | |||
| int deltaX = newX - x; | |||
| @@ -112,6 +119,7 @@ namespace Proto2THUAI6 | |||
| {protobuf::StudentType::STRAIGHT_A_STUDENT, THUAI6::StudentType::StraightAStudent}, | |||
| {protobuf::StudentType::ROBOT, THUAI6::StudentType::Robot}, | |||
| {protobuf::StudentType::TECH_OTAKU, THUAI6::StudentType::TechOtaku}, | |||
| {protobuf::StudentType::SUNSHINE, THUAI6::StudentType::Sunshine}, | |||
| }; | |||
| inline std::map<protobuf::TrickerType, THUAI6::TrickerType> trickerTypeDict{ | |||
| @@ -370,6 +378,7 @@ namespace THUAI62Proto | |||
| {THUAI6::StudentType::StraightAStudent, protobuf::StudentType::STRAIGHT_A_STUDENT}, | |||
| {THUAI6::StudentType::Robot, protobuf::StudentType::ROBOT}, | |||
| {THUAI6::StudentType::TechOtaku, protobuf::StudentType::TECH_OTAKU}, | |||
| {THUAI6::StudentType::Sunshine, protobuf::StudentType::SUNSHINE}, | |||
| }; | |||
| // inline std::map<THUAI6::StudentBuffType, protobuf::StudentBuffType> studentBuffTypeDict{ | |||
| @@ -415,7 +424,7 @@ namespace THUAI62Proto | |||
| return playerMsg; | |||
| } | |||
| inline protobuf::IDMsg THUAI62ProtobufID(int playerID) | |||
| inline protobuf::IDMsg THUAI62ProtobufID(int64_t playerID) | |||
| { | |||
| protobuf::IDMsg idMsg; | |||
| idMsg.set_player_id(playerID); | |||
| @@ -84,7 +84,7 @@ std::vector<std::vector<THUAI6::PlaceType>> Logic::GetFullMap() const | |||
| THUAI6::PlaceType Logic::GetPlaceType(int32_t cellX, int32_t cellY) const | |||
| { | |||
| std::unique_lock<std::mutex> lock(mtxState); | |||
| if (cellX < 0 || cellX >= currentState->gameMap.size() || cellY < 0 || cellY >= currentState->gameMap[0].size()) | |||
| if (cellX < 0 || uint64_t(cellX) >= currentState->gameMap.size() || cellY < 0 || uint64_t(cellY) >= currentState->gameMap[0].size()) | |||
| { | |||
| logger->warn("Invalid position!"); | |||
| return THUAI6::PlaceType::NullPlaceType; | |||
| @@ -55,19 +55,20 @@ const char descriptor_table_protodef_MessageType_2eproto[] PROTOBUF_SECTION_VARI | |||
| "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*q\n\013StudentType\022\025\n\021NULL" | |||
| "\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*Z\n\013TrickerType\022\025\n\021NULL_TRIC" | |||
| "KER_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\014GA" | |||
| "ME_RUNNING\020\002\022\014\n\010GAME_END\020\003b\006proto3"; | |||
| static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_MessageType_2eproto_once; | |||
| const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_MessageType_2eproto = { | |||
| "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"; | |||
| static ::_pbi::once_flag descriptor_table_MessageType_2eproto_once; | |||
| const ::_pbi::DescriptorTable descriptor_table_MessageType_2eproto = { | |||
| false, | |||
| false, | |||
| 1474, | |||
| 1488, | |||
| descriptor_table_protodef_MessageType_2eproto, | |||
| "MessageType.proto", | |||
| &descriptor_table_MessageType_2eproto_once, | |||
| @@ -288,6 +289,7 @@ namespace protobuf | |||
| case 3: | |||
| case 4: | |||
| case 5: | |||
| case 6: | |||
| return true; | |||
| default: | |||
| return false; | |||
| @@ -343,12 +343,13 @@ namespace protobuf | |||
| STRAIGHT_A_STUDENT = 3, | |||
| ROBOT = 4, | |||
| TECH_OTAKU = 5, | |||
| SUNSHINE = 6, | |||
| StudentType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), | |||
| StudentType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() | |||
| }; | |||
| bool StudentType_IsValid(int value); | |||
| constexpr StudentType StudentType_MIN = NULL_STUDENT_TYPE; | |||
| constexpr StudentType StudentType_MAX = TECH_OTAKU; | |||
| constexpr StudentType StudentType_MAX = SUNSHINE; | |||
| constexpr int StudentType_ARRAYSIZE = StudentType_MAX + 1; | |||
| const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* StudentType_descriptor(); | |||
| @@ -70,6 +70,7 @@ class StudentType(Enum): | |||
| StraightAStudent = 3 | |||
| Robot = 4 | |||
| TechOtaku = 5 | |||
| Sunshine = 6 | |||
| class TrickerType(Enum): | |||
| @@ -105,7 +105,8 @@ class Proto2THUAI6(NoInstance): | |||
| MessageType.TEACHER: THUAI6.StudentType.Teacher, | |||
| MessageType.STRAIGHT_A_STUDENT: THUAI6.StudentType.StraightAStudent, | |||
| MessageType.ROBOT: THUAI6.StudentType.Robot, | |||
| MessageType.TECH_OTAKU: THUAI6.StudentType.TechOtaku, } | |||
| MessageType.TECH_OTAKU: THUAI6.StudentType.TechOtaku, | |||
| MessageType.SUNSHINE: THUAI6.StudentType.Sunshine, } | |||
| trickerTypeDict: Final[dict] = { | |||
| MessageType.NULL_TRICKER_TYPE: THUAI6.TrickerType.NullTrickerType, | |||
| @@ -309,7 +310,8 @@ class THUAI62Proto(NoInstance): | |||
| THUAI6.StudentType.Teacher: MessageType.TEACHER, | |||
| THUAI6.StudentType.StraightAStudent: MessageType.STRAIGHT_A_STUDENT, | |||
| THUAI6.StudentType.Robot: MessageType.ROBOT, | |||
| THUAI6.StudentType.TechOtaku: MessageType.TECH_OTAKU, } | |||
| THUAI6.StudentType.TechOtaku: MessageType.TECH_OTAKU, | |||
| THUAI6.StudentType.Sunshine: MessageType.SUNSHINE, } | |||
| trickerTypeDict: Final[dict] = { | |||
| THUAI6.TrickerType.NullTrickerType: MessageType.NULL_TRICKER_TYPE, | |||
| @@ -8,5 +8,3 @@ python3 -m grpc_tools.protoc -I../../dependency/proto/ --python_out=./proto --py | |||
| python3 -m grpc_tools.protoc -I../../dependency/proto/ --python_out=./proto --pyi_out=./proto Message2Clients.proto | |||
| python3 -m grpc_tools.protoc -I../../dependency/proto/ --python_out=./proto --pyi_out=./proto Message2Server.proto | |||
| python3 -m grpc_tools.protoc -I../../dependency/proto/ --python_out=./proto --pyi_out=./proto --grpc_python_out=./proto Services.proto | |||
| @@ -110,6 +110,7 @@ enum StudentType | |||
| STRAIGHT_A_STUDENT = 3; | |||
| ROBOT = 4; | |||
| TECH_OTAKU =5; | |||
| SUNSHINE = 6; | |||
| } | |||
| enum TrickerType | |||
| @@ -221,6 +221,9 @@ namespace Client | |||
| case 5: | |||
| playerMsg.StudentType = StudentType.TechOtaku; | |||
| break; | |||
| case 6: | |||
| playerMsg.StudentType = StudentType.Sunshine; | |||
| break; | |||
| case 0: | |||
| default: | |||
| playerMsg.StudentType = StudentType.NullStudentType; | |||
| @@ -560,7 +563,7 @@ namespace Client | |||
| { | |||
| if (msg.PlayerState == PlayerState.Quit || msg.PlayerState == PlayerState.Graduated) | |||
| return false; | |||
| if (isSpectatorMode) | |||
| if (isSpectatorMode || isPlaybackMode) | |||
| return true; | |||
| if (humanOrButcher && human != null) | |||
| { | |||
| @@ -579,7 +582,7 @@ namespace Client | |||
| private bool CanSee(MessageOfTricker msg) | |||
| { | |||
| if (isSpectatorMode) | |||
| if (isSpectatorMode || isPlaybackMode) | |||
| return true; | |||
| if (!humanOrButcher && butcher != null) | |||
| { | |||
| @@ -606,7 +609,7 @@ namespace Client | |||
| private bool CanSee(MessageOfProp msg) | |||
| { | |||
| if (isSpectatorMode) | |||
| if (isSpectatorMode || isPlaybackMode) | |||
| return true; | |||
| if (humanOrButcher && human != null) | |||
| { | |||
| @@ -625,7 +628,7 @@ namespace Client | |||
| private bool CanSee(MessageOfBullet msg) | |||
| { | |||
| if (isSpectatorMode) | |||
| if (isSpectatorMode || isPlaybackMode) | |||
| return true; | |||
| if (humanOrButcher && human != null) | |||
| { | |||
| @@ -644,7 +647,7 @@ namespace Client | |||
| private bool CanSee(MessageOfBombedBullet msg) | |||
| { | |||
| if (isSpectatorMode) | |||
| if (isSpectatorMode || isPlaybackMode) | |||
| return true; | |||
| //if (humanOrButcher && human != null) | |||
| //{ | |||
| @@ -82,7 +82,6 @@ namespace Client | |||
| }; | |||
| new Thread(() => | |||
| { | |||
| int i = 0; | |||
| try | |||
| { | |||
| new FrameRateTaskExecutor<int> | |||
| @@ -91,10 +90,8 @@ namespace Client | |||
| () => | |||
| { | |||
| var content = Reader.ReadOne(); | |||
| i++; | |||
| if (content == null) | |||
| { | |||
| MessageBox.Show($"End! {i}"); | |||
| endFile = true; | |||
| } | |||
| else | |||
| @@ -2,7 +2,7 @@ | |||
| "profiles": { | |||
| "Client": { | |||
| "commandName": "Project", | |||
| "commandLineArgs": "--cl --port 8888 --characterID 2031" | |||
| "commandLineArgs": "--cl --playbackFile .\\video.thuaipb" | |||
| } | |||
| } | |||
| } | |||
| @@ -53,6 +53,9 @@ namespace Client | |||
| case StudentType.TechOtaku: | |||
| serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\nTechOtaku"; | |||
| break; | |||
| case StudentType.Sunshine: | |||
| serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\nSunshine"; | |||
| break; | |||
| case StudentType.NullStudentType: | |||
| serial.Text = "👥" + Convert.ToString(2) + "🧓" + Convert.ToString(obj.PlayerId) + "\nNullStudentType"; | |||
| break; | |||
| @@ -200,6 +200,8 @@ namespace Preparation.Utility | |||
| return Protobuf.StudentType.Robot; | |||
| case Preparation.Utility.CharacterType.TechOtaku: | |||
| return Protobuf.StudentType.TechOtaku; | |||
| case Preparation.Utility.CharacterType.Sunshine: | |||
| return Protobuf.StudentType.Sunshine; | |||
| default: | |||
| return Protobuf.StudentType.NullStudentType; | |||
| } | |||
| @@ -218,6 +220,8 @@ namespace Preparation.Utility | |||
| return Preparation.Utility.CharacterType.Robot; | |||
| case Protobuf.StudentType.TechOtaku: | |||
| return Preparation.Utility.CharacterType.TechOtaku; | |||
| case Protobuf.StudentType.Sunshine: | |||
| return Preparation.Utility.CharacterType.Sunshine; | |||
| default: | |||
| return Preparation.Utility.CharacterType.Null; | |||
| } | |||
| @@ -1,12 +1,12 @@ | |||
| @echo off | |||
| start cmd /k ..\Server\bin\Debug\net6.0\Server.exe --ip 0.0.0.0 --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName test | |||
| start cmd /k ..\Server\bin\Debug\net6.0\Server.exe --ip 0.0.0.0 --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video | |||
| ping -n 2 127.0.0.1 > NUL | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 4 --type 2 --occupation 1 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 4 --type 2 --occupation 4 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 0 --type 1 --occupation 1 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 0 --type 1 --occupation 6 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 1 --type 1 --occupation 2 | |||
| @@ -1,5 +1,5 @@ | |||
| @echo off | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --playbackFile .\test.thuaipb --playbackSpeed 1 | |||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --playbackFile .\video.thuaipb --playbackSpeed 1 | |||
| ping -n 2 127.0.0.1 > NUL | |||
| @@ -0,0 +1,167 @@ | |||
| # 使用文档 | |||
| 本文档仅供Windows选手参考,Linux选手仿照本文档即可 | |||
| ## 路径 | |||
| Windows选手使用脚本位于Win文件夹下,Linux选手使用脚本位于Linux文件夹下 | |||
| ## 游戏启动方式(For Debug) | |||
| 1. 首先启动Server:`RunServerForDebug.cmd` | |||
| 2. 再启动Client:Python使用`RunPython.cmd`,C++使用`RunCpp.cmd` | |||
| ## 实机体验游戏(ServerForPlay) | |||
| 启动游戏:ServerForPlay.cmd,其内部参数可自主设定,设定方式:右键-编辑 | |||
| Server脚本中参数格式一般如下: | |||
| ```shell | |||
| --ip 0.0.0.0 --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video | |||
| ``` | |||
| `--ip`是服务器ipv4地址 | |||
| - 在不同电脑间体验游戏时需要修改`--ip`这一参数,默认值为0.0.0.0 | |||
| - 对于windows选手,在连接同一wifi(如Tsinghua-Secure)的情况下,打开终端输入`ipconfig`,找到IPv4地址即可 | |||
| `--port`是服务器端口 | |||
| - 自己规定,默认值为8888 | |||
| `--studentCount`为本局学生个数 | |||
| - 取值为0,1,2,3,4,默认值为4 | |||
| `--trickerCount`为本局捣蛋鬼个数 | |||
| - 取值为0,1,默认值为1 | |||
| `--gameTimeInSecond`为本局最大游戏时长 | |||
| - 可自主设定,默认值为600(秒) | |||
| `--fileName`为回放文件的保存名称 | |||
| 此外,Server中还可以默认打开一个/多个WPF客户端,一般格式如下: | |||
| ```shell | |||
| --port 8888 --characterID 4 --type 2 --occupation 1 | |||
| ``` | |||
| `--ip`是服务器ipv4地址 | |||
| - 默认值为127.0.0.1,其余内容与Server相同,在不同电脑间连接时ip应相同 | |||
| `-port`是服务器端口 | |||
| - 默认值为8888,其余内容与Server相同,在不同电脑间连接时port应相同 | |||
| `--characterID`为玩家id | |||
| - 学生取值为0,1,2,3,捣蛋鬼取值为4 | |||
| `--type`为玩家类型 | |||
| - 学生取值为1,捣蛋鬼取值为2 | |||
| `--occupation`为职业选择,参考下表 | |||
| ### 职业对应表: | |||
| * 学生 | |||
| | 参数 | 职业类型 | | |||
| | ---- | ------------------------------- | | |||
| | 1 | Athlete | | |||
| | 2 | Teacher | | |||
| | 3 | StraightAStudent | | |||
| | 4 | Robot(目前未实现所有功能) | | |||
| | 5 | TechOtaku(目前未实现所有功能) | | |||
| | 6 | Sunshine | | |||
| * 捣蛋鬼 | |||
| | 参数 | 职业类型 | | |||
| | ---- | ------------ | | |||
| | 1 | Assassin | | |||
| | 2 | Klee | | |||
| | 3 | ANoisyPerson | | |||
| | 4 | Idol | | |||
| 此处只介绍职业的对应关系,详细的职业表请参考游戏规则部分。 | |||
| ## Client | |||
| ### C++接口 | |||
| 选手用Visual Studio打开CAPI.sln,编写AI.cpp,建议用Debug模式生成以方便自己调试,然后可以使用`RunCpp.cmd`启动。 | |||
| `RunCpp.cmd`的脚本参数格式如下: | |||
| ```shell | |||
| -I 127.0.0.1 -P 8888 -p 0 -d -o -w | |||
| ``` | |||
| `-I`为服务器IP,一般本地为`127.0.0.1`,联机的话是服务器的IP | |||
| `-P`为服务器端口,一般为8888 | |||
| `-p`为玩家id,一般学生为0~3,捣蛋鬼为4 | |||
| `-d`为保存Debug日志文件(在`/logs/`下) | |||
| `-o`为将日志输出到屏幕上 | |||
| `-w`只在开启`-o`的情况下生效,此时屏幕上只输出警告或报错(而日志文件依然完整保存)。 | |||
| ## WPF观看回放 | |||
| 使用`RunPlayback.cmd`,默认观看当前目录下的 video.thuaipb。 | |||
| > 可以通过更改脚本中FileName参数,来观看不同的回放文件 | |||
| ## cmd脚本的参数修改 | |||
| 右键点击`.cmd`或`.bat`文件之后,选择编辑就可以开始修改文件。通过在一行的开头加上`::`,可以注释掉该行。 | |||
| ## WPF简易调试界面 | |||
|  | |||
| ### 界面介绍 | |||
| * 固定方块:红色方块代表墙,绿色方块代表草,灰色方块代表窗户,黄色带锁标志的方块代表门 | |||
| * 动态方块:蓝色方块代表大门,橙色方块代表箱子,粉色方块代表教室,上面的数字均代表进度 | |||
| * 物件:橙色圆代表捣蛋鬼,紫色圆代表学生,且上面的编号对应学生的ID | |||
| * UI:左侧为UI,表示游戏内各类信息 | |||
| ### 实机体验之键鼠操作 | |||
| 仅当启动ServerForPlay.cmd时可以使用,观看回放时不能使用 | |||
| | 键位 | 效果 | | |||
| | ------------ | ---------------------------------------------- | | |||
| | W/NumPad8 | (Both)向上移动 | | |||
| | S/NumPad2 | (Both)向下移动 | | |||
| | D/NumPad6 | (Both)向右移动 | | |||
| | A/NumPad4 | (Both)向左移动 | | |||
| | J | (Tri)攻击,方向向上 | | |||
| | 鼠标双击某点 | (Tri)攻击,方向与从Tricker指向该点的向量相同 | | |||
| | K | (Stu)开始学习 | | |||
| | R | (Stu)开始唤醒(陷入沉迷状态的同伴) | | |||
| | T | (Stu)开始勉励(学习毅力下降的同伴) | | |||
| | G | (Stu)发出毕业请求 | | |||
| | H | (Stu)申请毕业(或称为开校门) | | |||
| | O | (Both)开(教学楼)门 | | |||
| | P | (Both)关(教学楼)门 | | |||
| | U | (Both)翻窗 | | |||
| | I | (Both)翻箱子 | | |||
| | E | (Both)结束当前行动,回到Idle状态 | | |||
| | F | (Both)随机捡起一个在周围的道具 | | |||
| | C | (Both)随机扔下一个已经持有的道具 | | |||
| | V | (Both)随机使用一个已经持有的道具 | | |||
| | B | (Both)使用0号技能 | | |||
| | N | (Both)使用1号技能 | | |||
| | M | (Both)使用2号技能 | | |||