| @@ -0,0 +1,199 @@ | |||||
| - [CAPI接口](#capi接口) | |||||
| - [接口解释](#接口解释) | |||||
| - [主动指令](#主动指令) | |||||
| - [移动](#移动) | |||||
| - [使用技能](#使用技能) | |||||
| - [人物](#人物) | |||||
| - [攻击](#攻击) | |||||
| - [学习与毕业](#学习与毕业) | |||||
| - [勉励与唤醒](#勉励与唤醒) | |||||
| - [地图互动](#地图互动) | |||||
| - [道具](#道具) | |||||
| - [信息获取](#信息获取) | |||||
| - [队内信息](#队内信息) | |||||
| - [查询可视范围内的信息](#查询可视范围内的信息) | |||||
| - [查询特定位置物体的信息,下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。](#查询特定位置物体的信息下面的-cellx-和-celly-指的是地图格数而非绝对坐标) | |||||
| - [其他](#其他) | |||||
| - [辅助函数](#辅助函数) | |||||
| - [接口一览](#接口一览) | |||||
| # CAPI接口 | |||||
| ## 接口解释 | |||||
| ### 主动指令 | |||||
| #### 移动 | |||||
| - `std::future<bool> Move(int64_t timeInMilliseconds, double angleInRadian)`:移动,`timeInMilliseconds` 为移动时间,单位毫秒;`angleInRadian` 表示移动方向,单位弧度,使用极坐标,**竖直向下方向为x轴,水平向右方向为y轴** | |||||
| - `std::future<bool> MoveRight(uint32_t timeInMilliseconds)`即向右移动,`MoveLeft`、`MoveDown`、`MoveUp`同理 | |||||
| #### 使用技能 | |||||
| - `std::future<bool> UseSkill(int32_t skillID)`:使用对应序号的主动技能 | |||||
| #### 人物 | |||||
| - `std::future<bool> EndAllAction()`:可以使不处在不可行动状态中的玩家终止当前行动 | |||||
| #### 攻击 | |||||
| - `std::future<bool> Attack(double angleInRadian)`:`angleInRadian`为攻击方向 | |||||
| #### 学习与毕业 | |||||
| - `std::future<bool> StartLearning()`:在教室里开始做作业 | |||||
| - `std::future<bool> StartOpenGate()`:开始开启校门 | |||||
| - `std::future<bool> Graduate()`:从开启的校门或隐藏校门毕业。 | |||||
| #### 勉励与唤醒 | |||||
| - `std::future<bool> StartEncourageMate(int64_t mateID)`:勉励对应玩家ID的学生。 | |||||
| - `std::future<bool> StartRouseMate(int64_t mateID)`:唤醒对应玩家ID的沉迷的学生。 | |||||
| #### 地图互动 | |||||
| - `std::future<bool> OpenDoor()`:开门 | |||||
| - `std::future<bool> CloseDoor()`:关门 | |||||
| - `std::future<bool> SkipWindow()`:翻窗 | |||||
| - `std::future<bool> StartOpenChest()`:开箱 | |||||
| #### 道具 | |||||
| - `bool PickProp(THUAI6::PropType prop)`捡起与自己处于同一个格子(cell)的道具。 | |||||
| - `bool UseProp(THUAI6::PropType prop)`使用对应类型的道具 | |||||
| - `bool ThrowProp(THUAI6::PropType prop)`将对应类型的道具扔在原地 | |||||
| ### 信息获取 | |||||
| #### 队内信息 | |||||
| - `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 指的是地图格数,而非绝对坐标。 | |||||
| - `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`:查询特定位置箱子开启进度 | |||||
| - `int32_t GetGateProgress(int32_t cellX, int32_t cellY) const`:查询特定位置校门开启进度 | |||||
| - `int32_t GetClassroomProgress(int32_t cellX, int32_t cellY) const`:查询特定位置教室作业完成进度 | |||||
| - `THUAI6::HiddenGateState GetHiddenGateState(int32_t cellX, int32_t cellY) const`::查询特定位置隐藏校门状态 | |||||
| - `int32_t GetDoorProgress(int32_t cellX, int32_t cellY) const`:查询特定位置门开启状态 | |||||
| #### 其他 | |||||
| - `std::shared_ptr<const THUAI6::GameInfo> GetGameInfo() const`:查询当前游戏状态\ | |||||
| - `std::vector<int64_t> GetPlayerGUIDs() const`:获取所有玩家的GUID\ | |||||
| - `int GetFrameCount() const`:获取目前所进行的帧数\ | |||||
| - `std::shared_ptr<const THUAI6::Tricker> GetSelfInfo() const`或`std::shared_ptr<const THUAI6::Student> GetSelfInfo() const`:获取自己的信息 | |||||
| - `std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const`:返回整张地图的地形信息。 | |||||
| ### 辅助函数 | |||||
| `static inline int CellToGrid(int cell) noexcept`:将地图格数 cell 转换为绝对坐标grid。 | |||||
| `static inline int GridToCell(int grid) noexcept`:将绝对坐标 grid 转换为地图格数cell。 | |||||
| 下面为用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用 | |||||
| ~~~c | |||||
| void Print(std::string str) const; | |||||
| void PrintStudent() const; | |||||
| void PrintTricker() const; | |||||
| void PrintProp() const; | |||||
| void PrintSelfInfo() const; | |||||
| ~~~ | |||||
| ## 接口一览 | |||||
| ~~~csharp | |||||
| // 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 | |||||
| virtual std::future<bool> Move(int64_t timeInMilliseconds, double angleInRadian) = 0; | |||||
| // 向特定方向移动 | |||||
| virtual std::future<bool> MoveRight(int64_t timeInMilliseconds) = 0; | |||||
| virtual std::future<bool> MoveUp(int64_t timeInMilliseconds) = 0; | |||||
| virtual std::future<bool> MoveLeft(int64_t timeInMilliseconds) = 0; | |||||
| virtual std::future<bool> MoveDown(int64_t timeInMilliseconds) = 0; | |||||
| // 捡道具、使用技能 | |||||
| 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; | |||||
| [[nodiscard]] virtual bool HaveMessage() = 0; | |||||
| [[nodiscard]] virtual std::pair<int64_t, std::string> GetMessage() = 0; | |||||
| // 等待下一帧 | |||||
| virtual std::future<bool> Wait() = 0; | |||||
| // 获取视野内可见的学生/捣蛋鬼的信息 | |||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const = 0; | |||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const = 0; | |||||
| // 获取视野内可见的道具信息 | |||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const = 0; | |||||
| // 获取地图信息,视野外的地图统一为Land | |||||
| [[nodiscard]] virtual std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const = 0; | |||||
| [[nodiscard]] virtual THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const = 0; | |||||
| [[nodiscard]] virtual bool IsDoorOpen(int32_t cellX, int32_t cellY) const = 0; | |||||
| [[nodiscard]] virtual int32_t GetChestProgress(int32_t cellX, int32_t cellY) const = 0; | |||||
| [[nodiscard]] virtual int32_t GetGateProgress(int32_t cellX, int32_t cellY) const = 0; | |||||
| [[nodiscard]] virtual int32_t GetClassroomProgress(int32_t cellX, int32_t cellY) const = 0; | |||||
| [[nodiscard]] virtual THUAI6::HiddenGateState GetHiddenGateState(int32_t cellX, int32_t cellY) const = 0; | |||||
| [[nodiscard]] virtual int32_t GetDoorProgress(int32_t cellX, int32_t cellY) const = 0; | |||||
| [[nodiscard]] virtual std::shared_ptr<const THUAI6::GameInfo> GetGameInfo() const = 0; | |||||
| // 获取所有玩家的GUID | |||||
| [[nodiscard]] virtual std::vector<int64_t> GetPlayerGUIDs() const = 0; | |||||
| // 获取游戏目前所进行的帧数 | |||||
| [[nodiscard]] virtual int GetFrameCount() const = 0; | |||||
| /*****选手可能用的辅助函数*****/ | |||||
| // 获取指定格子中心的坐标 | |||||
| [[nodiscard]] static inline int CellToGrid(int cell) noexcept | |||||
| { | |||||
| return cell * numOfGridPerCell + numOfGridPerCell / 2; | |||||
| } | |||||
| // 获取指定坐标点所位于的格子的 X 序号 | |||||
| [[nodiscard]] static inline int GridToCell(int grid) noexcept | |||||
| { | |||||
| return grid / numOfGridPerCell; | |||||
| } | |||||
| // 用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用 | |||||
| virtual void Print(std::string str) const = 0; | |||||
| virtual void PrintStudent() const = 0; | |||||
| virtual void PrintTricker() const = 0; | |||||
| virtual void PrintProp() const = 0; | |||||
| virtual void PrintSelfInfo() const = 0; | |||||
| }; | |||||
| class IStudentAPI : public IAPI | |||||
| { | |||||
| public: | |||||
| /*****学生阵营的特定函数*****/ | |||||
| virtual std::future<bool> StartLearning() = 0; | |||||
| virtual std::future<bool> StartEncourageMate(int64_t mateID) = 0; | |||||
| virtual std::future<bool> StartRouseMate(int64_t mateID) = 0; | |||||
| virtual std::future<bool> Graduate() = 0; | |||||
| [[nodiscard]] virtual std::shared_ptr<const THUAI6::Student> GetSelfInfo() const = 0; | |||||
| }; | |||||
| class ITrickerAPI : public IAPI | |||||
| { | |||||
| public: | |||||
| /*****捣蛋鬼阵营的特定函数*****/ | |||||
| [[nodiscard]] virtual std::shared_ptr<const THUAI6::Tricker> GetSelfInfo() const = 0; | |||||
| }; | |||||
| ~~~ | |||||
| @@ -0,0 +1,931 @@ | |||||
| # 工具使用合集 | |||||
| [toc] | |||||
| ## Visual Studio使用说明 | |||||
| 待更新... | |||||
| ## cmd脚本的参数修改 | |||||
| 待更新... | |||||
| ## C++接口必看 | |||||
| **在此鸣谢\xfgg/\xfgg/\xfgg/,看到这里的选手可以到选手群膜一膜!!! ** | |||||
| 除非特殊指明,以下代码均在 MSVC 19.28.29913 /std:c++17 与 g++ 10.2 for linux -std=c++17 两个平台下通过。 | |||||
| 由于我们的比赛最终会运行在Linux平台上,因此程设课上学到的一些只适用于Windows的C++操作很可能并不能正确执行。此外,代码中使用了大量Modern C++中的新特性,可能会使选手在编程过程中遇到较大困难。因此,此处介绍一些比赛中使用C++接口必须了解的知识。 | |||||
| ### 计时相关 | |||||
| 编写代码过程中,我们可能需要获取系统时间等一系列操作,C++ 标准库提供了这样的行为。尤其注意**不要**使用 Windows 平台上的 `GetTickCount` 或者 `GetTickCount64` !!! 应当使用 `std::chrono` | |||||
| 头文件:`#include <chrono>` | |||||
| 可以用于获取时间戳,从而用于计时、例如计算某个操作花费的时间,或者协调队友间的合作。 | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| #include <chrono> | |||||
| int main() | |||||
| { | |||||
| auto sec = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); | |||||
| auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); | |||||
| std::cout << "从 1970 年元旦到现在的:秒数" << sec << ";毫秒数:" << msec << std::endl; | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| ### 线程睡眠 | |||||
| 由于移动过程中会阻塞人物角色,因此玩家可能要在移动后让线程休眠一段时间,直到移动结束。C++ 标准库中使线程休眠需要包含头文件:`#include <thread>`。示例用法: | |||||
| ```cpp | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(20)); // 休眠 20 毫秒 | |||||
| std::this_thread::sleep_for(std::chrono::seconds(2)); // 休眠 2 秒 | |||||
| // 下面这个也能休眠 200 毫秒 | |||||
| std::this_thread::sleep_until(std::chrono::system_clock::now() += std::chrono::milliseconds(200)); | |||||
| ``` | |||||
| 休眠过程中,线程将被阻塞,而不继续进行,直到休眠时间结束方继续向下执行。 | |||||
| ### 异步接口的使用 | |||||
| 本届比赛中,我们可能会看到类似 `std::future<bool>` 这样类型的接口返回值,这实际上是一个异步接口。在调用同步接口后,在接口内的函数未执行完之前,线程通常会阻塞住;但是异步接口的调用通常不会阻塞当前线程,而是会另外开启一个线程进行操作,当前线程则继续向下执行。当调用 `get()` 方法时,将返回异步接口的值,若此时异步接口内的函数依然未执行完,则会阻塞当前线程。 | |||||
| 如果不需要返回值或没有返回值,但是希望接口内的函数执行完之后再进行下一步,即将接口当做常规的同步接口来调用,也可以调用 `wait()` 方法。 | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| #include <thread> | |||||
| #include <future> | |||||
| #include <chrono> | |||||
| int f_sync() | |||||
| { | |||||
| std::this_thread::sleep_for(std::chrono::seconds(1)); | |||||
| return 8; | |||||
| } | |||||
| std::future<int> f_async() | |||||
| { | |||||
| return std::async(std::launch::async, []() | |||||
| { std::this_thread::sleep_for(std::chrono::seconds(1)); | |||||
| return 8; }); | |||||
| } | |||||
| int main() | |||||
| { | |||||
| auto start = std::chrono::system_clock::now(); | |||||
| std::cout << std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(std::chrono::system_clock::now() - start).count() << std::endl; | |||||
| auto x = f_async(); | |||||
| std::cout << std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(std::chrono::system_clock::now() - start).count() << std::endl; | |||||
| std::cout << x.get() << std::endl; | |||||
| std::cout << std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(std::chrono::system_clock::now() - start).count() << std::endl; | |||||
| auto y = f_sync(); | |||||
| std::cout << std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(std::chrono::system_clock::now() - start).count() << std::endl; | |||||
| std::cout << y << std::endl; | |||||
| std::cout << std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(std::chrono::system_clock::now() - start).count() << std::endl; | |||||
| } | |||||
| ``` | |||||
| ### `auto`类型推导 | |||||
| C++11开始支持使用 `auto` 自动推导变量类型,废除了原有的作为 storage-class-specifier 的作用: | |||||
| ```c++ | |||||
| int i = 4; | |||||
| auto x = i; // auto 被推导为 int,x 是 int 类型 | |||||
| auto& y = i; // auto 仍被推导为 int,y 是 int& 类型 | |||||
| auto&& z = i; // auto 被推导为 int&,z 是 int&&&,被折叠为 int&,即 z 与 y 同类型 | |||||
| auto&& w = 4; // auto 被推导为 int,w 是 int&& 类型 | |||||
| ``` | |||||
| ### STL相关 | |||||
| #### std::vector | |||||
| 头文件:`#include <vector>`,类似于可变长的数组,支持下标运算符 `[]` 访问其元素,此时与 C 风格数组用法相似。支持 `size` 成员函数获取其中的元素数量。 | |||||
| 创建一个 `int` 型的 `vector` 对象: | |||||
| ```cpp | |||||
| std::vector<int> v { 9, 1, 2, 3, 4 }; // 初始化 vector 有五个元素,v[0] = 9, ... | |||||
| v.emplace_back(10); // 向 v 尾部添加一个元素,该元素饿构造函数的参数为 10(对于 int,只有一个语法意义上的构造函数,无真正的构造函数),即现在 v 有六个元素,v[5] 的值是10 | |||||
| v.pop_back(); // 把最后一个元素删除,现在 v 还是 { 9, 1, 2, 3, 4 } | |||||
| ``` | |||||
| 遍历其中所有元素的方式: | |||||
| ```cpp | |||||
| // std::vector<int> v; | |||||
| for (int i = 0; i < (int)v.size(); ++i) | |||||
| { | |||||
| /*可以通过 v[i] 对其进行访问*/ | |||||
| } | |||||
| for (auto itr = v.begin(); itr != v.end(); ++itr) | |||||
| { | |||||
| /* | |||||
| * itr 作为迭代器,可以通过其访问 vector 中的元素。其用法与指针几乎完全相同。 | |||||
| * 可以通过 *itr 得到元素;以及 itr-> 的用法也是支持的 | |||||
| * 实际上它内部就是封装了指向 vector 中元素的指针 | |||||
| * 此外还有 v.cbegin()、v.rbegin()、v.crbegin() 等 | |||||
| * v.begin()、v.end() 也可写为 begin(v)、end(v) | |||||
| */ | |||||
| } | |||||
| for (auto&& elem : v) | |||||
| { | |||||
| /* | |||||
| * elem 即是 v 中每个元素的引用,也可写成 auto& elem : v | |||||
| * 它完全等价于: | |||||
| * { | |||||
| * auto&& __range = v; | |||||
| * auto&& __begin = begin(v); | |||||
| * auto&& __end = end(v); | |||||
| * for (; __begin != __end; ++__begin) | |||||
| * { | |||||
| * auto&& elem = *__begin; | |||||
| * // Some code | |||||
| * } | |||||
| * } | |||||
| */ | |||||
| } | |||||
| ``` | |||||
| 例如: | |||||
| ```cpp | |||||
| for (auto elem&& : v) { std::cout << elem << ' '; } | |||||
| std::cout << std::endl; | |||||
| ``` | |||||
| 作为 STL 的容器之一,其具有容器的通用接口。但是由于这比较复杂,在此难以一一展开。有兴趣的同学可以在下方提供的链接里进行查阅。 | |||||
| **注:请千万不要试图使用 `std::vector<bool>`,若需使用,请用 `std::vector<char>` 代替!** | |||||
| 更多用法参见(点击进入):[cppreference_vector](https://zh.cppreference.com/w/cpp/container/vector) | |||||
| #### std::array | |||||
| 头文件:`#include <array>`,C 风格数组的类封装版本。 | |||||
| 用法与 C 风格的数组是基本相似的,例如: | |||||
| ```cpp | |||||
| std::array<double, 5> arr { 9.0, 8.0, 7.0, 6.0, 5.0 }; | |||||
| std::cout << arr[2] << std::endl; // 输出 7.0 | |||||
| ``` | |||||
| 同时也支持各种容器操作: | |||||
| ```cpp | |||||
| double sum = 0.0; | |||||
| for (auto itr = begin(arr); itr != end(arr); ++itr) | |||||
| { | |||||
| sum += *itr; | |||||
| } | |||||
| // sum 结果是 35 | |||||
| ``` | |||||
| 更多用法参见(点击进入):[cppreference_array](https://zh.cppreference.com/w/cpp/container/array)。 | |||||
| ## Python接口必看 | |||||
| 比赛中的Python接口大多使用异步接口,即返回一个类似于 `Future[bool]` 的值。为了获取实际的值,需要调用 `result()` 方法。 | |||||
| ```python | |||||
| from concurrent.futures import Future, ThreadPoolExecutor | |||||
| import time | |||||
| class Cls: | |||||
| def __init__(self): | |||||
| self.__pool: ThreadPoolExecutor = ThreadPoolExecutor(10) | |||||
| def Test(self, a: int, b: int) -> Future[int]: | |||||
| def test(): | |||||
| time.sleep(0.5) | |||||
| return a + b | |||||
| return self.__pool.submit(test) | |||||
| if __name__ == '__main__': | |||||
| f1 = Cls().Test(1, 2) | |||||
| print(time.time()) | |||||
| print(f1.result()) | |||||
| print(time.time()) | |||||
| ``` | |||||
| ## C++相关小知识 | |||||
| ### lambda表达式 | |||||
| #### lambda表达式概述 | |||||
| lambda 表达式是 C++ 发展史上的一个重大事件,也是 C++ 支持函数式编程的重要一环。可以说,lambda 表达式不仅给 C++ 程序员带来了极大的便利,也开创了 C++ 的一个崭新的编程范式。但是同时 lambda 表达式也带来了诸多的语法难题,使用容易,但精通极难。 | |||||
| lambda 表达式确实是一个非常有用的语法特性。至少个人在学了 lambda 表达式之后,编写 C++ 代码就再也没有离开过。因为,它真的是非常的方便与易用。 | |||||
| lambda 表达式首先可以看做是一个临时使用的函数。它的一般格式如下: | |||||
| ```c++ | |||||
| [捕获列表] + lambda 声明(可选) + 复合语句 | |||||
| lambda 声明指的是: | |||||
| (参数列表) + 一堆修饰符(可选) | |||||
| ``` | |||||
| 下面是一个简单的例子: | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| using namespace std; | |||||
| int main(void) | |||||
| { | |||||
| auto GetOne = []{ return 1; }; // GetOne 是一个 lambda 表达式 | |||||
| cout << GetOne() << endl; // 使用起来就像一个函数,输出 1 | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| 它还可以有参数: | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| using namespace std; | |||||
| int main(void) | |||||
| { | |||||
| auto GetSum = [](int x, int y){ return x + y; }; | |||||
| cout << GetSum(2, 3) << endl; // 5 | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| 或者临时调用: | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| using namespace std; | |||||
| int main(void) | |||||
| { | |||||
| cout << [](int x, int y){ return x + y; }(2, 3) << endl; // 5 | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| #### lambda 表达式的捕获 | |||||
| ##### 捕获的概念 | |||||
| lambda 表达式是不能够直接使用函数内的局部变量的(之后你将会看到这是为什么)。如果需要使用函数内的局部变量,需要手动进行捕获。捕获的方式有两种:按值捕获与按引用捕获。按值捕获,只会获得该值,而按引用捕获,则会获得函数内局部变量的引用。声明要捕获的变量就在 lambda 表达式的 `[]` 内: | |||||
| + `[]`:不捕获任何局部变量 | |||||
| + `[x]`:按值捕获变量 `x` | |||||
| + `[&y]`:按引用捕获变量 `y` | |||||
| + `[=]`:按值捕获全部局部变量 | |||||
| + `[&]`:按引用捕获全部局部变量 | |||||
| + `[&, x]`:除了 `x` 按值捕获之外,其他变量均按引用捕获 | |||||
| + `[=, &y]`:什么意思不用我都说了吧 | |||||
| + `[r = x]`:声明一个变量 `r` ,捕获 `x` 的值 | |||||
| + `[&r = y]`:声明一个引用 `r`,捕获 `y` 的引用 | |||||
| + `[x, y, &z, w = p, &r = q]`:作为练习 | |||||
| + `[&, x, y, p = z]`:这个也作为练习 | |||||
| 这样我们就可以写出下面的代码了: | |||||
| ```cpp | |||||
| #include <iostream> | |||||
| using namespace std; | |||||
| int main(void) | |||||
| { | |||||
| int x, y, z; | |||||
| cin >> x >> y; | |||||
| [x, y, &z](){ z = x + y; }(); | |||||
| cout << z << endl; // z = x + y | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| ##### 捕获 `this` 与 `*this` | |||||
| 当 lambda 表达式位于类的成员函数内时,该如何使用该类的成员变量呢?我们知道,在类的成员函数体内使用成员变量,都是通过 `this` 指针访问的,此处 `this` 作为成员函数的一个参数,因此只需要捕获 `this` 指针,就可以在 lambda 体内访问其成员变量了! | |||||
| 捕获时,我们可以选择捕获 `[this]`,也可以捕获 `[*this]`。区别是,前者捕获的是 `this` 指针本身,而后者是按值捕获 `this` 指针所指向的对象,也就是以 `*this` 为参数复制构造了一个新的对象。看下面的代码: | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| using namespace std; | |||||
| struct Foo | |||||
| { | |||||
| int m_bar; | |||||
| void Func() | |||||
| { | |||||
| [this]() | |||||
| { | |||||
| cout << ++m_bar << endl; | |||||
| }(); | |||||
| } | |||||
| }; | |||||
| int main() | |||||
| { | |||||
| Foo foo; | |||||
| foo.m_bar = 999; | |||||
| foo.Func(); // 输出 1000 | |||||
| } | |||||
| ``` | |||||
| ##### 附注 | |||||
| 需要注意的是,lambda 表达式的捕获发生在 **lambda 表达式定义处**,而不是 lambda 表达式调用处,比如: | |||||
| ```c++ | |||||
| int a = 4; | |||||
| auto f = [a]() { cout << a << endl; }; // 此时捕获 a,值是 4 | |||||
| a = 9; | |||||
| f(); // 输出 4,而非 9 | |||||
| ``` | |||||
| > **C++ 真奇妙:不需要捕获的情况** | |||||
| > | |||||
| > 看这特殊的引用块就知道,本段内容仅作介绍,感觉较难者请跳过本块。 | |||||
| > | |||||
| > 有时,即使是局部变量,不需要捕获也可以编译通过。这是 C++ 标准对编译器实现做出的妥协。这种现象叫做“常量折叠(constant folding)”;与之相对的是不能直接使用,必须进行捕获的情况,通常称作“odr-used”。这两个概念比较复杂,在此不做过多展开。看下面的例子: | |||||
| > | |||||
| > ```c++ | |||||
| > int Func1(const int& x) { return x; } | |||||
| > void Func2() | |||||
| > { | |||||
| > const int x = 4; | |||||
| > []() | |||||
| > { | |||||
| > int y = x; // OK, constant folding | |||||
| > int z = Func1(x); // Compile error! odr-used! x is not captured! | |||||
| > }(); | |||||
| > } | |||||
| > ``` | |||||
| > | |||||
| > 但是个别较老的编译器即使是 odr-used 也可能会编译通过 | |||||
| #### lambda 表达式的修饰符 `mutable` | |||||
| lambda 表达式可以有一些修饰符,例如 `noexcept`、`mutable `等,这里仅介绍 `mutable`。 | |||||
| lambda 表达式按值捕获变量时,捕获的变量默认是不可修改: | |||||
| ```c++ | |||||
| int a = 4; | |||||
| auto f = [a]() | |||||
| { | |||||
| ++a; // Compile error: a cannot be modified! | |||||
| }; | |||||
| ``` | |||||
| 但是我们可以通过加 `mutable` 关键字让它达到这个目的: | |||||
| ```c++ | |||||
| int a = 4; | |||||
| auto f = [a]() mutable | |||||
| { | |||||
| ++a; // OK | |||||
| cout << a << endl; | |||||
| }; | |||||
| f(); //输出 5 | |||||
| cout << a << endl; //输出 4 | |||||
| ``` | |||||
| 需要注意的是,按值捕获变量是生成了一个新的变量副本,而非原来的变量,所以在 lambda 外的 `a` 的值仍然是 `4` | |||||
| #### lambda 表达式的本质 | |||||
| 本段内容仅是粗略地讲述,不做深入讨论。读者也可以跳过本块。 | |||||
| 上面说了这么多语法规定,但是 lamdba 表达式究竟是什么?知道了这个可以帮助我们理解 lambda 表达式的这些规定。 | |||||
| C++17 标准中如此定义 lambda 的类型: | |||||
| > The type of a *lambda-expression* (which is also the type of the closure object ) is a unique, unnamed non-union class type, called the closure type.... | |||||
| lambda 表达式类型是一个独一无二的、没有名字的、并且不是联合体的类类型。我们把它叫做“**closure type**”。 | |||||
| 后面还有一堆关于它性质的约束,这里就不展开了,大致上就是编译器可以自由决定它的很多性质,有兴趣的可以去翻阅《ISO/IEC 14882: 2017》第 8.1.5.1 款。 | |||||
| 大体来看,一个 lamdba 表达式与一个类是大致上相同的。也就是说,lambda 表达式: | |||||
| ```c++ | |||||
| int a = 0, b = 0; | |||||
| auto f = [a, &b](int x) { return a + b + x; } | |||||
| f(5); | |||||
| ``` | |||||
| 和下面的代码大致相同: | |||||
| ```c++ | |||||
| int a = 0, b = 0; | |||||
| class __lambda__ | |||||
| { | |||||
| private: | |||||
| int a; | |||||
| int& b; | |||||
| public: | |||||
| __lambda__(int& a, int& b) : a(a), b(b) {} | |||||
| auto operator(int x) const { return a + b + x; } | |||||
| }; | |||||
| __lambda__ f(a, b); | |||||
| f.operator()(5); | |||||
| ``` | |||||
| 不过它们两个**并不完全相同**。首先,不同编译器的实现本身就有不同;另外,它们在语法上的规定也有一些差别。篇幅所限,在此不做过多展开。 | |||||
| #### lambda 表达式的应用 | |||||
| 看了上面这么多介绍,你可能要问:这东西能用什么用处?为什么不直接写个函数,或者是干脆不用 lambda 表达式而直接写在函数体里呢?有这个疑问是正常的。因为我上面给的例子都是可以不用 lambda 表达式就能轻松解决的。但是,lambda 表达式在很多应用场景具有不可替代的优势。最简单的例子,比如在局部,你要重复某些操作,但是另写一个函数又不是很方便,就可以用 lambda 表达式完成。此外,它最大的作用就是在函数式编程中,或者是其他需要回调函数的情况,以 lambda 表达式作为函数的参数以作为回调函数。在下面的教程中,例如多线程、智能指针,我们将会多次用到 lambda 表达式。届时你将会看到使用 lambda 表达式是多么的方便。 | |||||
| #### 关于 lambda 表达式的其他说明 | |||||
| lambda 表达式还有很多有趣之处,例如泛型 lambda、返回 lambda 表达式的 lamdba 表达式,此外 `decltype` 在 lambda 表达式中的使用也是光怪陆离……总之,lambda 表达式非常有趣。 | |||||
| 到了这里,相信你对 lambda 表达式已经有了相当的理解,就让我们来做一道简单的练习吧(狗头) | |||||
| > 请给出下面程序的输出(该程序选自《ISO/IEC 14882: 2017 Programming Language --- C++》第 107 页): | |||||
| > | |||||
| > ```c++ | |||||
| >#include <iostream> | |||||
| > using namespace std; | |||||
| > | |||||
| > int main() | |||||
| > { | |||||
| > int a = 1, b = 1, c = 1; | |||||
| > auto m1 = [a, &b, &c]() mutable | |||||
| > { | |||||
| > auto m2 = [a, b, &c]() mutable | |||||
| > { | |||||
| > cout << a << b << c; | |||||
| > a = 4; b = 4; c = 4; | |||||
| > }; | |||||
| > a = 3; b = 3; c = 3; | |||||
| > m2(); | |||||
| > }; | |||||
| > a = 2; b = 2; c = 2; | |||||
| > m1(); | |||||
| > cout << a << b << c << endl; | |||||
| > return 0; | |||||
| > } | |||||
| > ``` | |||||
| > 相信聪明的你一下就看出了答案。没错,答案就是我们小学二年级学习的数字:**123234**!怎么样,你答对了吗? | |||||
| > | |||||
| 如果阅读本文之后你觉得 lambda 表达式很有趣,欢迎阅读 《ISO/IEC 14882: 2017 Programming Language --- C++》110~120 页,或点击进入网址:[cppreference_lambda](https://zh.cppreference.com/w/cpp/language/lambda) 获取更多信息。 | |||||
| ### std::thread | |||||
| 头文件:`#include <thread>`。用于开启新的线程。示例代码: | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| #include <thread> | |||||
| #include <functional> | |||||
| void Func(int x, int& cnt) | |||||
| { | |||||
| for (int i = 0; i < 110; ++i) | |||||
| { | |||||
| std::cout << "In Func: " << x << std::endl; | |||||
| ++cnt; | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(20)); | |||||
| } | |||||
| } | |||||
| int main() | |||||
| { | |||||
| int cnt = 0; | |||||
| // 由于这种情况下函数的调用与传参不是同时的,提供参数在函数调用之前,因此以引用方式传递参数时需要用 std::ref | |||||
| std::thread thr(Func, 2021, std::ref(cnt)); | |||||
| for (int i = 0; i < 50; ++i) | |||||
| { | |||||
| std::cout << "In main: " << 110 << std::endl; | |||||
| ++cnt; | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(20)); | |||||
| } | |||||
| thr.join(); // 等待子线程结束,在 thr 析构前若未 detach 则必须调用此函数,等待过程中主线程 main 被阻塞 | |||||
| std::cout << "Count: " << cnt << std::endl; | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| 或者使用 lambda 表达式达到同样效果: | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| #include <thread> | |||||
| #include <functional> | |||||
| int main() | |||||
| { | |||||
| int cnt = 0, x = 2021; | |||||
| std::thread thr | |||||
| ( | |||||
| [x, &cnt]() | |||||
| { | |||||
| for (int i = 0; i < 110; ++i) | |||||
| { | |||||
| std::cout << "In Func: " << x << std::endl; | |||||
| ++cnt; | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(20)); | |||||
| } | |||||
| } | |||||
| ); | |||||
| for (int i = 0; i < 50; ++i) | |||||
| { | |||||
| std::cout << "In main: " << 110 << std::endl; | |||||
| ++cnt; | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(20)); | |||||
| } | |||||
| thr.join(); | |||||
| std::cout << "Count: " << cnt << std::endl; | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| 如果不希望等待子线程结束,`main` 结束则程序结束,则可以构造临时对象调用 `detach` 函数: | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| #include <thread> | |||||
| #include <functional> | |||||
| int main() | |||||
| { | |||||
| int cnt = 0, x = 2021; | |||||
| std::thread | |||||
| ( | |||||
| [x, &cnt]() | |||||
| { | |||||
| for (int i = 0; i < 110; ++i) | |||||
| { | |||||
| std::cout << "In Func: " << x << std::endl; | |||||
| ++cnt; | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(20)); | |||||
| } | |||||
| } | |||||
| ).detach(); | |||||
| for (int i = 0; i < 50; ++i) | |||||
| { | |||||
| std::cout << "In main: " << 110 << std::endl; | |||||
| ++cnt; | |||||
| std::this_thread::sleep_for(std::chrono::milliseconds(20)); | |||||
| } | |||||
| std::cout << "Count: " << cnt << std::endl; | |||||
| return 0; | |||||
| } | |||||
| ``` | |||||
| 更多内容请参看(点击进入):[cppreference_thread](https://en.cppreference.com/w/cpp/thread/thread) | |||||
| ### 智能指针 | |||||
| #### 总述 | |||||
| 头文件:`include <memory>` | |||||
| 智能指针是 C++ 标准库中对指针的封装,它的好处是可以不需要 `delete`,而自动对其指向的资源进行释放,这在一定程度上降低了 C++ 程序员管理内存的难度,但同时智能指针的使用也具有一定的技巧。 | |||||
| 智能指针主要有三种:`shared_ptr`、`weak_ptr`、`unique_ptr`。 | |||||
| #### `std::shared_ptr` | |||||
| ##### 概览 | |||||
| `shared_ptr` 可以说是最常用的智能指针了。它的用法最为灵活,内部实现方式是**引用计数**。即,它会记录有多少个 `shared_ptr` 正在指向某个资源,并当指向该资源的智能指针数为零时,调用相应的释放函数(默认为 `delete` 操作符)释放该资源。 | |||||
| 像 `new` 会在自由存储区动态获取一块内存并返回其一样,如果要动态分配一块内存并得到其智能指针,可以使用 `std::make_shared` 模板,例如: | |||||
| ```c++ | |||||
| #include <memory> | |||||
| void Func() | |||||
| { | |||||
| int* p = new int(110); // 在自由存储区 new 一个 int 对象,初值为 110 | |||||
| auto sp = std::make_shared<int>(110); // 在自由存储区 new 一个 int 对象,初值为 110 | |||||
| // sp 被自动推导为 std::shared_ptr<int> 类型 | |||||
| delete p; // 释放内存 | |||||
| // 编译器调用 sp 的析构函数,并将其指向的 int 释放掉 | |||||
| } | |||||
| ``` | |||||
| 关于引用计数: | |||||
| ```cpp | |||||
| #include <memory> | |||||
| void Func() | |||||
| { | |||||
| int x = 110; | |||||
| { | |||||
| auto sp1 = std::make_shared<int>(x); // 得到一个 int,初值为 110。 | |||||
| // 上述此语句执行过后,只有一个智能指针 sp1 指向这个 int,引用计数为 1 | |||||
| { | |||||
| auto sp2 = sp1; // 构造一个智能指针 sp2,指向 sp1 指向的内存,并将引用计数+1 | |||||
| // 故此处引用计数为2 | |||||
| std::cout << *sp2 << std::endl; // 输出 110 | |||||
| // 此处 sp2 生存期已到,调用 sp2 的析构函数,使引用计数-1,因此此时引用计数为1 | |||||
| } | |||||
| // 此处 sp1 生命期也已经到了,调用 sp1 析构函数,引用计数再-1,故引用计数降为0 | |||||
| // 也就是不再有智能指针指向它了,调用 delete 释放内存 | |||||
| } | |||||
| } | |||||
| ``` | |||||
| 将普通指针交给智能指针托管: | |||||
| ```cpp | |||||
| int* p = new int(110); | |||||
| int* q = new int(110); | |||||
| std::shared_ptr sp(p); // 把 p 指向的内存交给 sp 托管,此后 p 便不需要 delete,sp 析构时会自动释放 | |||||
| std::shared_ptr sq; // sq 什么也不托管 | |||||
| sq.reset(q); // 让 sq 托管 q | |||||
| //此后 p 与 q 便不需要再 delete | |||||
| ``` | |||||
| 需要注意的是,这种写法是非常危险的,既可能导致 `p` 与 `q` 变为野指针,也可能造成重复 `delete`,我们应该更多使用 make_shared。 | |||||
| ##### 自定义释放函数 | |||||
| 之前说过 ,默认情况下是释放内存的函数是 `delete` 运算符,但有时我们并不希望这样。比如下面的几个情况: | |||||
| + 使用智能指针托管动态数组 | |||||
| ```cpp | |||||
| #include <memory> | |||||
| void IntArrayDeleter(int* p) { delete[] p; } | |||||
| int main() | |||||
| { | |||||
| std::shared_ptr<int> sp(new int[10], IntArrayDeleter); // 让 IntArrayDeleter 作为释放资源的函数 | |||||
| // sp 析构时自动调用 IntArrayDeleter 释放该 int 数组 | |||||
| return 0; | |||||
| } | |||||
| // 或者利用 lambda 表达式:std::shared_ptr<int> sp(new int[10], [](int* p) { delete[] p; }); | |||||
| ``` | |||||
| + 释放系统资源 | |||||
| 在编程过程中,难免与操作系统打交道,这时我们可能需要获取一系列的系统资源,并还给操作系统(实际上 `new` 和 `delete` 也就是一个例子)。一个比较有特色的例子就是 Windows API。在传统的 Win32 程序中,如果我们要在屏幕上进行绘制图形,我们首先需要获取设备的上下文信息,才能在设备上进行绘图。设想这样一个情景:我们有一个窗口,已经获得了指向这个窗口的句柄(即指针)`hWnd`,我们要在窗口上绘图,就要通过这个窗口句柄获取设备上下文信息。代码如下: | |||||
| ```c++ | |||||
| HDC hdc; // DC: Device context,一个指向 DC 的句柄(HANDLE) | |||||
| hdc = GetDC(hWnd); // 获取设备上下文 | |||||
| /*执行绘图操作*/ | |||||
| ReleaseDC(hWnd, hdc); // 绘图完毕,将设备上下文资源释放,归还给 Windows 系统 | |||||
| ``` | |||||
| 使用智能指针对其进行托管,代码如下: | |||||
| ```c++ | |||||
| // 使用 lambda 表达式写法(推荐) | |||||
| std::shared_ptr<void> sp(GetDC(hWnd), [hWnd](void* hdc) { ReleaseDC(hWnd, (HDC)hdc); }); | |||||
| ``` | |||||
| ```cpp | |||||
| // 不使用 lambda 表达式的写法: | |||||
| struct Releaser | |||||
| { | |||||
| HWND hWnd; | |||||
| Releaser(HWND hWnd) : hWnd(hWnd) {} | |||||
| void operator()(void* hdc) | |||||
| { | |||||
| ReleaseDC(hWnd, (HDC)hdc); | |||||
| } | |||||
| }; | |||||
| void PaintFunc() | |||||
| { | |||||
| /*...*/ | |||||
| std::shared_ptr<void> sp(GetDC(hWnd), Releaser(hWnd)); | |||||
| /*...*/ | |||||
| } | |||||
| ``` | |||||
| ##### 常见的错误用法 | |||||
| `std::shared_ptr` 虽然方便,但是也有一些错误用法,这个是常见的: | |||||
| ```c++ | |||||
| #include <memory> | |||||
| void Func() | |||||
| { | |||||
| int* p = new int(110); | |||||
| std::shared_ptr<int> sp(p); // 让 sp 托管 p | |||||
| std::shared_ptr<int> sq(p); // 让 sq 托管 p | |||||
| // Runtime Error! 程序至此崩溃 | |||||
| } | |||||
| ``` | |||||
| 这是因为,只有复制构造函数里面才有使引用计数加1的操作。即当我们写 `std::shared_ptr<int> sq = sp` 的时候,确实引用计数变成了2,但是我们都用一个外部的裸指针 `p` 去初始化 `sp` 和 `sq`,智能指针并不能感知到它们托管的内存相同。所以 `sp` 和 `sq` 所托管的内存被看做是独立的。这样,当它们析构的时候,均会释放它们所指的内存,因此同一块内存被释放了两次,导致程序出错。所以个人还是推荐使用 `make_shared` ,而不是用裸指针去获取内存。 | |||||
| 另一个著名的错误用法,请继续阅读 `std::weak_ptr`。 | |||||
| #### `std::weak_ptr` | |||||
| 看完了上面的 `shared_ptr` 的讲述,相信你已经对使用智能指针胸有成竹了。一切都用 `shared_ptr`、`make_shared` 就万事大吉了嘛!但事情可能没那么简单。看下面的例子: | |||||
| ```c++ | |||||
| #include <iostream> | |||||
| #include <memory> | |||||
| class B; | |||||
| class A | |||||
| { | |||||
| public: | |||||
| void SetB(const std::shared_ptr<B>& ipB) | |||||
| { | |||||
| pB = ipB; | |||||
| } | |||||
| private: | |||||
| std::shared_ptr<B> pB; | |||||
| }; | |||||
| class B | |||||
| { | |||||
| public: | |||||
| void SetA(const std::shared_ptr<A>& ipA) | |||||
| { | |||||
| pA = ipA; | |||||
| } | |||||
| private: | |||||
| std::shared_ptr<A> pA; | |||||
| }; | |||||
| void Func() | |||||
| { | |||||
| auto pA = std::make_shared<A>(); | |||||
| auto pB = std::make_shared<B>(); | |||||
| pA->SetB(pB); | |||||
| pB->SetA(pA); | |||||
| // 内存泄露!!! | |||||
| } | |||||
| /*...*/ | |||||
| ``` | |||||
| 太糟糕了!上面的 `pA` 指向的的对象和 `pB` 指向的对象一直到程序结束之前永远不会被释放!如果不相信,可以在它们的析构函数里输出些什么试一试。相信学习了引用计数的你,一定能想出来原因。我们就把它当作一道思考题作为练习:为什么这两个对象不会被释放呢?(提示:注意只有引用计数降为0的时候才会释放) | |||||
| 实际上,`std::shared_ptr` 并不是乱用的。它除了作为一个指针之外,还表明了一种逻辑上的归属关系。从逻辑上看,类的成员代表一种归属权的关系,类的成员属于这个类。拥有 `shared_ptr` 作为**成员**的对象,是对 `shared_ptr` 所指向的对象具有所有权的,`shared_ptr` 也是基于这个理念设计的。但是,有时候我们并不希望这是个所有权的关系,例如我们有双亲和孩子的指针作为“人”的成员,但是人与人之间是平等相待和谐共处的,我们不能说一个人是另一个人的附属品。这时候,`std::weak_ptr` 便应运而生了! | |||||
| `std::weak_ptr` 与 `shared_ptr` 的区别是,它指向一个资源,并不会增加引用计数。当指向一个资源的 `shared_ptr` 的数量为 0 的时候,即使还有 `weak_ptr` 在指,资源也会被释放掉。也是因此,`weak_ptr`也是存在悬垂指针的可能的,即它指向的资源已经被释放掉。 也是因此,`weak_ptr` 不允许直接地被解引用,必须先转换为相应的 `shared_ptr` 才能解引用,获取其所指的资源。它的用法如下: | |||||
| ```cpp | |||||
| auto sp = std::make_shared<int>(5); | |||||
| std::weak_ptr<int> wp = sp; // 正确,让 wp 指向 sp 指向的资源 | |||||
| // std::shared_ptr<int> sp1 = wp; // 错误,weak_ptr 不能直接赋值给 shared_ptr | |||||
| /* Do something */ | |||||
| if (wp.expired()) | |||||
| { | |||||
| std::cout << "The resource has been released!" << std::endl; | |||||
| } | |||||
| else | |||||
| { | |||||
| // std::cout << *wp << std::endl; // Compile error! weak_ptr 不能直接使用! | |||||
| auto sp1 = wp.lock(); // 从 weak_ptr 中恢复出 shared_ptr,sp1 的类型为 std::shared_ptr<int> | |||||
| std::cout << *sp1 << std::endl; | |||||
| } | |||||
| ``` | |||||
| 从类的设计本身来看,`weak_ptr` 不会增加引用计数;从逻辑上看,`weak_ptr` 描述了一种联系,即 `weak_ptr` 的拥有者与其指向的对象之间不是一种归属关系,而是一种较弱的联系。一个类的对象只需知道另一个类的对象是谁,而不对其拥有占有权,这时候用 `weak_ptr` 是合适的。 | |||||
| 上面的 `A` 类和 `B` 类的问题,将 `A` 和 `B` 成员从 `shared_ptr` 换成 `weak_ptr` 就会解决内存泄露的问题了! | |||||
| #### `std::unique_ptr` | |||||
| `std::unique_ptr` 顾名思义,独有的指针,即资源只能同时为一个 `unique_ptr` 所占有。它部分涉及到 `xvalue` 、右值引用与移动语义的问题,在此不做过多展开。 | |||||
| 更多关于智能指针的知识,可以参考(点击进入): | |||||
| + [cppreference_shared_ptr](https://zh.cppreference.com/w/cpp/memory/shared_ptr) | |||||
| + [cppreference_weak_ptr](https://zh.cppreference.com/w/cpp/memory/weak_ptr) | |||||
| + [cppreference_unique_ptr](https://zh.cppreference.com/w/cpp/memory/unique_ptr) | |||||
| @@ -35,6 +35,7 @@ public: | |||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const = 0; | [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const = 0; | ||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const = 0; | [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const = 0; | ||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const = 0; | [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const = 0; | ||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const = 0; | |||||
| [[nodiscard]] virtual std::shared_ptr<const THUAI6::Student> StudentGetSelfInfo() const = 0; | [[nodiscard]] virtual std::shared_ptr<const THUAI6::Student> StudentGetSelfInfo() const = 0; | ||||
| [[nodiscard]] virtual std::shared_ptr<const THUAI6::Tricker> TrickerGetSelfInfo() const = 0; | [[nodiscard]] virtual std::shared_ptr<const THUAI6::Tricker> TrickerGetSelfInfo() const = 0; | ||||
| @@ -54,6 +55,7 @@ public: | |||||
| virtual bool Move(int64_t time, double angle) = 0; | virtual bool Move(int64_t time, double angle) = 0; | ||||
| virtual bool PickProp(THUAI6::PropType prop) = 0; | virtual bool PickProp(THUAI6::PropType prop) = 0; | ||||
| virtual bool UseProp(THUAI6::PropType prop) = 0; | virtual bool UseProp(THUAI6::PropType prop) = 0; | ||||
| virtual bool ThrowProp(THUAI6::PropType prop) = 0; | |||||
| virtual bool UseSkill(int32_t skillID) = 0; | virtual bool UseSkill(int32_t skillID) = 0; | ||||
| virtual bool SendMessage(int64_t toID, std::string message) = 0; | virtual bool SendMessage(int64_t toID, std::string message) = 0; | ||||
| virtual bool HaveMessage() = 0; | virtual bool HaveMessage() = 0; | ||||
| @@ -67,8 +69,8 @@ public: | |||||
| virtual bool Graduate() = 0; | virtual bool Graduate() = 0; | ||||
| virtual bool StartLearning() = 0; | virtual bool StartLearning() = 0; | ||||
| virtual bool StartTreatMate(int64_t mateID) = 0; | |||||
| virtual bool StartRescueMate(int64_t mateID) = 0; | |||||
| virtual bool StartEncourageMate(int64_t mateID) = 0; | |||||
| virtual bool StartRouseMate(int64_t mateID) = 0; | |||||
| virtual bool OpenDoor() = 0; | virtual bool OpenDoor() = 0; | ||||
| virtual bool CloseDoor() = 0; | virtual bool CloseDoor() = 0; | ||||
| @@ -99,6 +101,7 @@ public: | |||||
| // 捡道具、使用技能 | // 捡道具、使用技能 | ||||
| virtual std::future<bool> PickProp(THUAI6::PropType prop) = 0; | virtual std::future<bool> PickProp(THUAI6::PropType prop) = 0; | ||||
| virtual std::future<bool> UseProp(THUAI6::PropType prop) = 0; | virtual std::future<bool> UseProp(THUAI6::PropType prop) = 0; | ||||
| virtual std::future<bool> ThrowProp(THUAI6::PropType prop) = 0; | |||||
| virtual std::future<bool> UseSkill(int32_t skillID) = 0; | virtual std::future<bool> UseSkill(int32_t skillID) = 0; | ||||
| virtual std::future<bool> Attack(double angleInRadian) = 0; | virtual std::future<bool> Attack(double angleInRadian) = 0; | ||||
| @@ -124,7 +127,9 @@ public: | |||||
| // 获取视野内可见的道具信息 | // 获取视野内可见的道具信息 | ||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const = 0; | [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const = 0; | ||||
| // 获取地图信息,视野外的地图统一为Land | |||||
| // 获取视野内可见的子弹信息 | |||||
| [[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const = 0; | |||||
| [[nodiscard]] virtual std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const = 0; | [[nodiscard]] virtual std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const = 0; | ||||
| [[nodiscard]] virtual THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const = 0; | [[nodiscard]] virtual THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const = 0; | ||||
| @@ -172,8 +177,8 @@ public: | |||||
| /*****学生阵营的特定函数*****/ | /*****学生阵营的特定函数*****/ | ||||
| virtual std::future<bool> StartLearning() = 0; | virtual std::future<bool> StartLearning() = 0; | ||||
| virtual std::future<bool> StartTreatMate(int64_t mateID) = 0; | |||||
| virtual std::future<bool> StartRescueMate(int64_t mateID) = 0; | |||||
| virtual std::future<bool> StartEncourageMate(int64_t mateID) = 0; | |||||
| virtual std::future<bool> StartRouseMate(int64_t mateID) = 0; | |||||
| virtual std::future<bool> Graduate() = 0; | virtual std::future<bool> Graduate() = 0; | ||||
| [[nodiscard]] virtual std::shared_ptr<const THUAI6::Student> GetSelfInfo() const = 0; | [[nodiscard]] virtual std::shared_ptr<const THUAI6::Student> GetSelfInfo() const = 0; | ||||
| }; | }; | ||||
| @@ -221,6 +226,7 @@ public: | |||||
| std::future<bool> PickProp(THUAI6::PropType prop) override; | std::future<bool> PickProp(THUAI6::PropType prop) override; | ||||
| std::future<bool> UseProp(THUAI6::PropType prop) override; | std::future<bool> UseProp(THUAI6::PropType prop) override; | ||||
| std::future<bool> ThrowProp(THUAI6::PropType prop) override; | |||||
| std::future<bool> UseSkill(int32_t skillID) override; | std::future<bool> UseSkill(int32_t skillID) override; | ||||
| std::future<bool> Attack(double angleInRadian) override; | std::future<bool> Attack(double angleInRadian) override; | ||||
| @@ -243,6 +249,8 @@ public: | |||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | ||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const override; | |||||
| [[nodiscard]] std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const override; | [[nodiscard]] std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const override; | ||||
| [[nodiscard]] THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const override; | [[nodiscard]] THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const override; | ||||
| @@ -258,8 +266,8 @@ public: | |||||
| [[nodiscard]] std::vector<int64_t> GetPlayerGUIDs() const override; | [[nodiscard]] std::vector<int64_t> GetPlayerGUIDs() const override; | ||||
| std::future<bool> StartLearning() override; | std::future<bool> StartLearning() override; | ||||
| std::future<bool> StartTreatMate(int64_t mateID) override; | |||||
| std::future<bool> StartRescueMate(int64_t mateID) override; | |||||
| std::future<bool> StartEncourageMate(int64_t mateID) override; | |||||
| std::future<bool> StartRouseMate(int64_t mateID) override; | |||||
| std::future<bool> Graduate() override; | std::future<bool> Graduate() override; | ||||
| [[nodiscard]] std::shared_ptr<const THUAI6::Student> GetSelfInfo() const override; | [[nodiscard]] std::shared_ptr<const THUAI6::Student> GetSelfInfo() const override; | ||||
| @@ -308,6 +316,7 @@ public: | |||||
| std::future<bool> PickProp(THUAI6::PropType prop) override; | std::future<bool> PickProp(THUAI6::PropType prop) override; | ||||
| std::future<bool> UseProp(THUAI6::PropType prop) override; | std::future<bool> UseProp(THUAI6::PropType prop) override; | ||||
| std::future<bool> ThrowProp(THUAI6::PropType prop) override; | |||||
| std::future<bool> UseSkill(int32_t skillID) override; | std::future<bool> UseSkill(int32_t skillID) override; | ||||
| std::future<bool> OpenDoor() override; | std::future<bool> OpenDoor() override; | ||||
| @@ -328,6 +337,8 @@ public: | |||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | ||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const override; | |||||
| [[nodiscard]] std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const override; | [[nodiscard]] std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const override; | ||||
| [[nodiscard]] THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const override; | [[nodiscard]] THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const override; | ||||
| @@ -383,6 +394,7 @@ public: | |||||
| std::future<bool> PickProp(THUAI6::PropType prop) override; | std::future<bool> PickProp(THUAI6::PropType prop) override; | ||||
| std::future<bool> UseProp(THUAI6::PropType prop) override; | std::future<bool> UseProp(THUAI6::PropType prop) override; | ||||
| std::future<bool> ThrowProp(THUAI6::PropType prop) override; | |||||
| std::future<bool> UseSkill(int32_t skillID) override; | std::future<bool> UseSkill(int32_t skillID) override; | ||||
| std::future<bool> Attack(double angleInRadian) override; | std::future<bool> Attack(double angleInRadian) override; | ||||
| @@ -405,6 +417,8 @@ public: | |||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | ||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const override; | |||||
| [[nodiscard]] std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const override; | [[nodiscard]] std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const override; | ||||
| [[nodiscard]] THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const override; | [[nodiscard]] THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const override; | ||||
| @@ -420,8 +434,8 @@ public: | |||||
| [[nodiscard]] std::vector<int64_t> GetPlayerGUIDs() const override; | [[nodiscard]] std::vector<int64_t> GetPlayerGUIDs() const override; | ||||
| std::future<bool> StartLearning() override; | std::future<bool> StartLearning() override; | ||||
| std::future<bool> StartTreatMate(int64_t mateID) override; | |||||
| std::future<bool> StartRescueMate(int64_t mateID) override; | |||||
| std::future<bool> StartEncourageMate(int64_t mateID) override; | |||||
| std::future<bool> StartRouseMate(int64_t mateID) override; | |||||
| std::future<bool> Graduate() override; | std::future<bool> Graduate() override; | ||||
| [[nodiscard]] virtual std::shared_ptr<const THUAI6::Student> GetSelfInfo() const override; | [[nodiscard]] virtual std::shared_ptr<const THUAI6::Student> GetSelfInfo() const override; | ||||
| @@ -455,6 +469,7 @@ public: | |||||
| std::future<bool> PickProp(THUAI6::PropType prop) override; | std::future<bool> PickProp(THUAI6::PropType prop) override; | ||||
| std::future<bool> UseProp(THUAI6::PropType prop) override; | std::future<bool> UseProp(THUAI6::PropType prop) override; | ||||
| std::future<bool> ThrowProp(THUAI6::PropType prop) override; | |||||
| std::future<bool> UseSkill(int32_t skillID) override; | std::future<bool> UseSkill(int32_t skillID) override; | ||||
| std::future<bool> OpenDoor() override; | std::future<bool> OpenDoor() override; | ||||
| @@ -475,6 +490,8 @@ public: | |||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | ||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const override; | |||||
| [[nodiscard]] std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const override; | [[nodiscard]] std::vector<std::vector<THUAI6::PlaceType>> GetFullMap() const override; | ||||
| [[nodiscard]] THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const override; | [[nodiscard]] THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const override; | ||||
| @@ -26,6 +26,7 @@ public: | |||||
| bool Move(int64_t time, double angle, int64_t playerID); | bool Move(int64_t time, double angle, int64_t playerID); | ||||
| bool PickProp(THUAI6::PropType prop, int64_t playerID); | bool PickProp(THUAI6::PropType prop, int64_t playerID); | ||||
| bool UseProp(THUAI6::PropType prop, int64_t playerID); | bool UseProp(THUAI6::PropType prop, int64_t playerID); | ||||
| bool ThrowProp(THUAI6::PropType prop, int64_t playerID); | |||||
| bool UseSkill(int32_t skillID, int64_t playerID); | bool UseSkill(int32_t skillID, int64_t playerID); | ||||
| bool SendMessage(int64_t toID, std::string message, int64_t playerID); | bool SendMessage(int64_t toID, std::string message, int64_t playerID); | ||||
| bool OpenDoor(int64_t playerID); | bool OpenDoor(int64_t playerID); | ||||
| @@ -38,8 +39,8 @@ public: | |||||
| bool Graduate(int64_t playerID); | bool Graduate(int64_t playerID); | ||||
| bool StartLearning(int64_t playerID); | bool StartLearning(int64_t playerID); | ||||
| bool StartTreatMate(int64_t playerID, int64_t mateID); | |||||
| bool StartRescueMate(int64_t playerID, int64_t mateID); | |||||
| bool StartEncourageMate(int64_t playerID, int64_t mateID); | |||||
| bool StartRouseMate(int64_t playerID, int64_t mateID); | |||||
| bool Attack(double angle, int64_t playerID); | bool Attack(double angle, int64_t playerID); | ||||
| @@ -2,17 +2,300 @@ | |||||
| #ifndef CONSTANTS_H | #ifndef CONSTANTS_H | ||||
| #define CONSTANTS_H | #define CONSTANTS_H | ||||
| #ifndef SCCI | |||||
| #define SCCI static const constexpr inline | |||||
| #endif | |||||
| namespace Constants | namespace Constants | ||||
| { | { | ||||
| static const constexpr inline numOfGridPerCell = 1000; | |||||
| // 地图相关 | |||||
| SCCI int numOfGridPerCell = 1000; // 单位坐标数 | |||||
| SCCI int rows = 50; // 地图行数 | |||||
| SCCI int cols = 50; // 地图列数 | |||||
| SCCI int numOfClassroom = 10; // 教室数量 | |||||
| SCCI int numOfChest = 8; // 宝箱数量 | |||||
| SCCI int maxClassroomProgress = 10000000; // 教室最大进度 | |||||
| SCCI int maxDoorProgress = 10000000; // 开关门最大进度 | |||||
| SCCI int maxChestProgress = 10000000; // 宝箱最大进度 | |||||
| SCCI int maxGateProgress = 18000; // 大门最大进度 | |||||
| SCCI int numOfRequiredClassroomForGate = 7; // 打开大门需要完成的教室数量 | |||||
| SCCI int numOfRequiredClassroomForHiddenGate = 3; // 打开隐藏门需要完成的教室数量 | |||||
| // 人物属性相关 | |||||
| SCCI int basicEncourageSpeed = 100; | |||||
| SCCI int basicFixSpeed = 123; | |||||
| SCCI int basicSpeedOfOpeningOrLocking = 4000; | |||||
| SCCI int basicStudentSpeedOfClimbingThroughWindows = 611; | |||||
| SCCI int basicTrickerSpeedOfClimbingThroughWindows = 1270; | |||||
| SCCI int basicSpeedOfOpenChest = 1000; | |||||
| SCCI int basicHp = 3000000; | |||||
| SCCI int basicMaxGamingAddiction = 60000; | |||||
| SCCI int basicEncouragementDegree = 1500000; | |||||
| SCCI int basicTimeOfRouse = 1000; | |||||
| SCCI int basicStudentSpeed = 1270; | |||||
| SCCI int basicTrickerSpeed = 1504; | |||||
| SCCI double basicConcealment = 1; | |||||
| SCCI int basicStudentAlertnessRadius = 15 * numOfGridPerCell; | |||||
| SCCI int basicTrickerAlertnessRadius = 17 * numOfGridPerCell; | |||||
| SCCI int basicStudentViewRange = 10 * numOfGridPerCell; | |||||
| SCCI int basicTrickerViewRange = 15 * numOfGridPerCell; | |||||
| SCCI int PinningDownRange = 5 * numOfGridPerCell; | |||||
| SCCI int maxNumOfProp = 3; // 人物道具栏容量 | |||||
| // 攻击相关 | |||||
| SCCI int basicApOfTricker = 1500000; | |||||
| SCCI int basicCD = 3000; // 初始子弹冷却 | |||||
| SCCI int basicCastTime = 500; // 基本前摇时间 | |||||
| SCCI int basicBackswing = 800; // 基本后摇时间 | |||||
| SCCI int basicRecoveryFromHit = 3700; // 基本命中攻击恢复时长 | |||||
| SCCI int basicStunnedTimeOfStudent = 4300; | |||||
| SCCI int basicBulletMoveSpeed = 3700; // 基本子弹移动速度 | |||||
| SCCI double basicRemoteAttackRange = 3000; // 基本远程攻击范围 | |||||
| SCCI double basicAttackShortRange = 1100; // 基本近程攻击范围 | |||||
| SCCI double basicBulletBombRange = 1000; // 基本子弹爆炸范围 | |||||
| // 道具相关 | |||||
| SCCI int apPropAdd = basicApOfTricker * 12 / 10; | |||||
| SCCI int apSpearAdd = basicApOfTricker * 6 / 10; | |||||
| // 职业相关 | |||||
| struct Assassin | |||||
| { | |||||
| SCCI int moveSpeed = basicTrickerSpeed * 11 / 10; | |||||
| SCCI double concealment = 1.5; | |||||
| SCCI int alertnessRadius = basicTrickerAlertnessRadius * 13 / 10; | |||||
| SCCI int viewRange = basicTrickerViewRange * 12 / 10; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = basicTrickerSpeedOfClimbingThroughWindows; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest; | |||||
| }; | |||||
| struct Klee | |||||
| { | |||||
| SCCI int moveSpeed = basicTrickerSpeed; | |||||
| SCCI double concealment = 1; | |||||
| SCCI int alertnessRadius = basicTrickerAlertnessRadius; | |||||
| SCCI int viewRange = basicTrickerViewRange; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = basicTrickerSpeedOfClimbingThroughWindows; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest * 11 / 10; | |||||
| }; | |||||
| struct ANoisyPerson | |||||
| { | |||||
| SCCI int moveSpeed = (int)(basicTrickerSpeed * 1.07); | |||||
| SCCI double concealment = 0.8; | |||||
| SCCI int alertnessRadius = basicTrickerAlertnessRadius * 9 / 10; | |||||
| SCCI int viewRange = basicTrickerViewRange; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = basicTrickerSpeedOfClimbingThroughWindows * 11 / 10; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest * 11 / 10; | |||||
| }; | |||||
| struct Idol | |||||
| { | |||||
| SCCI int moveSpeed = basicTrickerSpeed; | |||||
| SCCI double concealment = 0.75; | |||||
| SCCI int alertnessRadius = basicTrickerAlertnessRadius; | |||||
| SCCI int viewRange = basicTrickerViewRange * 11 / 10; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = basicTrickerSpeedOfClimbingThroughWindows; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest; | |||||
| }; | |||||
| struct Athlete | |||||
| { | |||||
| SCCI int moveSpeed = basicStudentSpeed * 105 / 100; | |||||
| SCCI int maxHp = basicHp; | |||||
| SCCI int maxAddiction = basicMaxGamingAddiction * 9 / 10; | |||||
| SCCI int fixSpeed = basicFixSpeed * 6 / 10; | |||||
| SCCI int encourageSpeed = basicEncourageSpeed * 9 / 10; | |||||
| SCCI double concealment = 0.9; | |||||
| SCCI int alertnessRadius = basicStudentAlertnessRadius; | |||||
| SCCI int viewRange = basicStudentViewRange * 11 / 10; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = basicStudentSpeedOfClimbingThroughWindows * 12 / 10; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest; | |||||
| }; | |||||
| struct Teacher | |||||
| { | |||||
| SCCI int moveSpeed = basicStudentSpeed * 9 / 10; | |||||
| SCCI int maxHp = basicHp * 10; | |||||
| SCCI int maxAddiction = basicMaxGamingAddiction * 10; | |||||
| SCCI int fixSpeed = basicFixSpeed * 0; | |||||
| SCCI int encourageSpeed = basicEncourageSpeed * 8 / 10; | |||||
| SCCI double concealment = 0.5; | |||||
| SCCI int alertnessRadius = basicStudentAlertnessRadius / 2; | |||||
| SCCI int viewRange = basicStudentViewRange * 9 / 10; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = basicStudentSpeedOfClimbingThroughWindows / 2; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest; | |||||
| }; | |||||
| struct StraightAStudent | |||||
| { | |||||
| SCCI int moveSpeed = basicStudentSpeed * 96 / 100; | |||||
| SCCI int maxHp = basicHp * 11 / 10; | |||||
| SCCI int maxAddiction = basicMaxGamingAddiction * 13 / 10; | |||||
| SCCI int fixSpeed = basicFixSpeed * 11 / 10; | |||||
| SCCI int encourageSpeed = basicEncourageSpeed; | |||||
| SCCI double concealment = 0.9; | |||||
| SCCI int alertnessRadius = basicStudentAlertnessRadius * 9 / 10; | |||||
| SCCI int viewRange = basicStudentViewRange * 9 / 10; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = basicStudentSpeedOfClimbingThroughWindows * 10 / 12; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest; | |||||
| }; | |||||
| struct Robot | |||||
| { | |||||
| SCCI int moveSpeed = basicStudentSpeed; | |||||
| SCCI int maxHp = basicHp * 0.4; | |||||
| SCCI int maxAddiction = basicMaxGamingAddiction * 0; | |||||
| SCCI int fixSpeed = basicFixSpeed; | |||||
| SCCI int encourageSpeed = 0; | |||||
| SCCI double concealment = 1; | |||||
| SCCI int alertnessRadius = basicStudentAlertnessRadius * 1; | |||||
| SCCI int viewRange = basicStudentViewRange; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = 1; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest; | |||||
| }; | |||||
| struct TechOtaku | |||||
| { | |||||
| SCCI int moveSpeed = basicStudentSpeed * 3 / 4; | |||||
| SCCI int maxHp = basicHp * 9 / 10; | |||||
| SCCI int maxAddiction = basicMaxGamingAddiction * 11 / 10; | |||||
| SCCI int fixSpeed = basicFixSpeed * 11 / 10; | |||||
| SCCI int encourageSpeed = basicEncourageSpeed * 9 / 10; | |||||
| SCCI double concealment = 1; | |||||
| SCCI int alertnessRadius = basicStudentAlertnessRadius; | |||||
| SCCI int viewRange = basicStudentViewRange * 9 / 10; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking; | |||||
| SCCI int speedOfClimbingThroughWindows = basicStudentSpeedOfClimbingThroughWindows * 3 / 4; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest; | |||||
| }; | |||||
| struct Sunshine | |||||
| { | |||||
| SCCI int moveSpeed = basicStudentSpeed; | |||||
| SCCI int maxHp = basicHp * 32 / 30; | |||||
| SCCI int maxAddiction = basicMaxGamingAddiction * 11 / 10; | |||||
| SCCI int fixSpeed = basicFixSpeed; | |||||
| SCCI int encourageSpeed = basicEncourageSpeed * 12 / 10; | |||||
| SCCI double concealment = 1; | |||||
| SCCI int alertnessRadius = basicStudentAlertnessRadius; | |||||
| SCCI int viewRange = basicStudentViewRange; | |||||
| SCCI int speedOfOpeningOrLocking = basicSpeedOfOpeningOrLocking * 7 / 10; | |||||
| SCCI int speedOfClimbingThroughWindows = basicStudentSpeedOfClimbingThroughWindows; | |||||
| SCCI int speedOfOpenChest = basicSpeedOfOpenChest * 9 / 10; | |||||
| }; | |||||
| // 技能相关 | |||||
| SCCI int maxNumOfSkill = 3; | |||||
| SCCI int commonSkillCD = 30000; // 普通技能标准冷却时间 | |||||
| SCCI int commonSkillTime = 10000; // 普通技能标准持续时间 | |||||
| SCCI int timeOfTrickerStunnedWhenCharge = 7220; | |||||
| SCCI int timeOfStudentStunnedWhenCharge = 2090; | |||||
| SCCI int timeOfTrickerStunnedWhenPunish = 3070; | |||||
| SCCI int timeOfTrickerSwingingAfterHowl = 800; | |||||
| SCCI int timeOfStudentStunnedWhenHowl = 5500; | |||||
| SCCI int timeOfStunnedWhenJumpyDumpty = 3070; | |||||
| SCCI double addedTimeOfSpeedWhenInspire = 0.6; | |||||
| SCCI int timeOfAddingSpeedWhenInspire = 6000; | |||||
| struct CanBeginToCharge | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 2; | |||||
| SCCI int durationTime = commonSkillTime * 3 / 10; | |||||
| }; | |||||
| struct BecomeInvisible | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 4 / 3; | |||||
| SCCI int durationTime = commonSkillTime; | |||||
| }; | |||||
| struct Punish | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 1; | |||||
| SCCI int durationTime = commonSkillTime * 0; | |||||
| }; | |||||
| struct Rouse | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 4; | |||||
| SCCI int durationTime = commonSkillTime * 0; | |||||
| }; | |||||
| struct StudentConstants | |||||
| struct Encourage | |||||
| { | { | ||||
| SCCI int skillCD = commonSkillCD * 4; | |||||
| SCCI int durationTime = commonSkillTime * 0; | |||||
| }; | }; | ||||
| struct TrickerConstants | |||||
| struct Inspire | |||||
| { | { | ||||
| SCCI int skillCD = commonSkillCD * 4; | |||||
| SCCI int durationTime = commonSkillTime * 0; | |||||
| }; | }; | ||||
| struct Howl | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 25 / 30; | |||||
| SCCI int durationTime = commonSkillTime * 0; | |||||
| }; | |||||
| struct ShowTime | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 3; | |||||
| SCCI int durationTime = commonSkillTime * 1; | |||||
| }; | |||||
| struct JumpyBomb | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD / 2; | |||||
| SCCI int durationTime = commonSkillTime * 3 / 10; | |||||
| }; | |||||
| struct UseKnife | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 1; | |||||
| SCCI int durationTime = commonSkillTime / 10; | |||||
| }; | |||||
| struct UseRobot | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD / 300; | |||||
| SCCI int durationTime = commonSkillTime * 0; | |||||
| }; | |||||
| struct WriteAnswers | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 1; | |||||
| SCCI int durationTime = commonSkillTime * 0; | |||||
| }; | |||||
| struct SummonGolem | |||||
| { | |||||
| SCCI int skillCD = commonSkillCD * 1; | |||||
| SCCI int durationTime = commonSkillTime * 0; | |||||
| }; | |||||
| } // namespace Constants | } // namespace Constants | ||||
| #endif | #endif | ||||
| @@ -94,6 +94,7 @@ private: | |||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const override; | [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Tricker>> GetTrickers() const override; | ||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const override; | [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Student>> GetStudents() const override; | ||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Prop>> GetProps() const override; | ||||
| [[nodiscard]] std::vector<std::shared_ptr<const THUAI6::Bullet>> GetBullets() const override; | |||||
| [[nodiscard]] std::shared_ptr<const THUAI6::Student> StudentGetSelfInfo() const override; | [[nodiscard]] std::shared_ptr<const THUAI6::Student> StudentGetSelfInfo() const override; | ||||
| [[nodiscard]] std::shared_ptr<const THUAI6::Tricker> TrickerGetSelfInfo() const override; | [[nodiscard]] std::shared_ptr<const THUAI6::Tricker> TrickerGetSelfInfo() const override; | ||||
| [[nodiscard]] THUAI6::HiddenGateState GetHiddenGateState(int32_t cellX, int32_t cellY) const override; | [[nodiscard]] THUAI6::HiddenGateState GetHiddenGateState(int32_t cellX, int32_t cellY) const override; | ||||
| @@ -113,6 +114,7 @@ private: | |||||
| bool Move(int64_t time, double angle) override; | bool Move(int64_t time, double angle) override; | ||||
| bool PickProp(THUAI6::PropType prop) override; | bool PickProp(THUAI6::PropType prop) override; | ||||
| bool UseProp(THUAI6::PropType prop) override; | bool UseProp(THUAI6::PropType prop) override; | ||||
| bool ThrowProp(THUAI6::PropType prop) override; | |||||
| bool UseSkill(int32_t skillID) override; | bool UseSkill(int32_t skillID) override; | ||||
| bool SendMessage(int64_t toID, std::string message) override; | bool SendMessage(int64_t toID, std::string message) override; | ||||
| @@ -123,8 +125,8 @@ private: | |||||
| bool StartLearning() override; | bool StartLearning() override; | ||||
| bool StartTreatMate(int64_t mateID) override; | |||||
| bool StartRescueMate(int64_t mateID) override; | |||||
| bool StartEncourageMate(int64_t mateID) override; | |||||
| bool StartRouseMate(int64_t mateID) override; | |||||
| bool Attack(double angle) override; | bool Attack(double angle) override; | ||||
| @@ -94,7 +94,7 @@ namespace THUAI6 | |||||
| Assassin = 1, | Assassin = 1, | ||||
| Klee = 2, | Klee = 2, | ||||
| ANoisyPerson = 3, | ANoisyPerson = 3, | ||||
| TrickerType4 = 4, | |||||
| Idol = 4, | |||||
| }; | }; | ||||
| // 学生Buff类型 | // 学生Buff类型 | ||||
| @@ -126,11 +126,11 @@ namespace THUAI6 | |||||
| Addicted = 3, | Addicted = 3, | ||||
| Quit = 4, | Quit = 4, | ||||
| Graduated = 5, | Graduated = 5, | ||||
| Treated = 6, | |||||
| Rescued = 7, | |||||
| Encouraged = 6, | |||||
| Roused = 7, | |||||
| Stunned = 8, | Stunned = 8, | ||||
| Treating = 9, | |||||
| Rescuing = 10, | |||||
| Encouraging = 9, | |||||
| Rousing = 10, | |||||
| Swinging = 11, | Swinging = 11, | ||||
| Attacking = 12, | Attacking = 12, | ||||
| Locking = 13, | Locking = 13, | ||||
| @@ -195,9 +195,9 @@ namespace THUAI6 | |||||
| int32_t determination; // 剩余毅力 | int32_t determination; // 剩余毅力 | ||||
| int32_t addiction; // 沉迷程度 | int32_t addiction; // 沉迷程度 | ||||
| int32_t learningSpeed; | int32_t learningSpeed; | ||||
| int32_t treatSpeed; | |||||
| int32_t treatProgress; | |||||
| int32_t rescueProgress; | |||||
| int32_t encourageSpeed; | |||||
| int32_t encourageProgress; | |||||
| int32_t rouseProgress; | |||||
| double dangerAlert; | double dangerAlert; | ||||
| std::vector<StudentBuffType> buff; // buff | std::vector<StudentBuffType> buff; // buff | ||||
| }; | }; | ||||
| @@ -292,6 +292,7 @@ namespace THUAI6 | |||||
| {TrickerType::Assassin, "Assassin"}, | {TrickerType::Assassin, "Assassin"}, | ||||
| {TrickerType::Klee, "Klee"}, | {TrickerType::Klee, "Klee"}, | ||||
| {TrickerType::ANoisyPerson, "ANoisyPerson"}, | {TrickerType::ANoisyPerson, "ANoisyPerson"}, | ||||
| {TrickerType::Idol, "Idol"}, | |||||
| }; | }; | ||||
| inline std::map<PlayerState, std::string> playerStateDict{ | inline std::map<PlayerState, std::string> playerStateDict{ | ||||
| @@ -301,11 +302,11 @@ namespace THUAI6 | |||||
| {PlayerState::Addicted, "Addicted"}, | {PlayerState::Addicted, "Addicted"}, | ||||
| {PlayerState::Quit, "Quit"}, | {PlayerState::Quit, "Quit"}, | ||||
| {PlayerState::Graduated, "Graduated"}, | {PlayerState::Graduated, "Graduated"}, | ||||
| {PlayerState::Treated, "Treated"}, | |||||
| {PlayerState::Rescued, "Rescued"}, | |||||
| {PlayerState::Encouraged, "Encouraged"}, | |||||
| {PlayerState::Roused, "Roused"}, | |||||
| {PlayerState::Stunned, "Stunned"}, | {PlayerState::Stunned, "Stunned"}, | ||||
| {PlayerState::Treating, "Treating"}, | |||||
| {PlayerState::Rescuing, "Rescuing"}, | |||||
| {PlayerState::Encouraging, "Encouraging"}, | |||||
| {PlayerState::Rousing, "Rousing"}, | |||||
| {PlayerState::Swinging, "Swinging"}, | {PlayerState::Swinging, "Swinging"}, | ||||
| {PlayerState::Attacking, "Attacking"}, | {PlayerState::Attacking, "Attacking"}, | ||||
| {PlayerState::Locking, "Locking"}, | {PlayerState::Locking, "Locking"}, | ||||
| @@ -119,7 +119,7 @@ namespace Proto2THUAI6 | |||||
| {protobuf::TrickerType::ASSASSIN, THUAI6::TrickerType::Assassin}, | {protobuf::TrickerType::ASSASSIN, THUAI6::TrickerType::Assassin}, | ||||
| {protobuf::TrickerType::KLEE, THUAI6::TrickerType::Klee}, | {protobuf::TrickerType::KLEE, THUAI6::TrickerType::Klee}, | ||||
| {protobuf::TrickerType::A_NOISY_PERSON, THUAI6::TrickerType::ANoisyPerson}, | {protobuf::TrickerType::A_NOISY_PERSON, THUAI6::TrickerType::ANoisyPerson}, | ||||
| {protobuf::TrickerType::TRICKERTYPE4, THUAI6::TrickerType::TrickerType4}, | |||||
| {protobuf::TrickerType::IDOL, THUAI6::TrickerType::Idol}, | |||||
| }; | }; | ||||
| inline std::map<protobuf::StudentBuffType, THUAI6::StudentBuffType> studentBuffTypeDict{ | inline std::map<protobuf::StudentBuffType, THUAI6::StudentBuffType> studentBuffTypeDict{ | ||||
| @@ -146,11 +146,11 @@ namespace Proto2THUAI6 | |||||
| {protobuf::PlayerState::ADDICTED, THUAI6::PlayerState::Addicted}, | {protobuf::PlayerState::ADDICTED, THUAI6::PlayerState::Addicted}, | ||||
| {protobuf::PlayerState::QUIT, THUAI6::PlayerState::Quit}, | {protobuf::PlayerState::QUIT, THUAI6::PlayerState::Quit}, | ||||
| {protobuf::PlayerState::GRADUATED, THUAI6::PlayerState::Graduated}, | {protobuf::PlayerState::GRADUATED, THUAI6::PlayerState::Graduated}, | ||||
| {protobuf::PlayerState::RESCUED, THUAI6::PlayerState::Rescued}, | |||||
| {protobuf::PlayerState::TREATED, THUAI6::PlayerState::Treated}, | |||||
| {protobuf::PlayerState::RESCUED, THUAI6::PlayerState::Roused}, | |||||
| {protobuf::PlayerState::TREATED, THUAI6::PlayerState::Encouraged}, | |||||
| {protobuf::PlayerState::STUNNED, THUAI6::PlayerState::Stunned}, | {protobuf::PlayerState::STUNNED, THUAI6::PlayerState::Stunned}, | ||||
| {protobuf::PlayerState::RESCUING, THUAI6::PlayerState::Rescuing}, | |||||
| {protobuf::PlayerState::TREATING, THUAI6::PlayerState::Treating}, | |||||
| {protobuf::PlayerState::RESCUING, THUAI6::PlayerState::Rousing}, | |||||
| {protobuf::PlayerState::TREATING, THUAI6::PlayerState::Encouraging}, | |||||
| {protobuf::PlayerState::SWINGING, THUAI6::PlayerState::Swinging}, | {protobuf::PlayerState::SWINGING, THUAI6::PlayerState::Swinging}, | ||||
| {protobuf::PlayerState::ATTACKING, THUAI6::PlayerState::Attacking}, | {protobuf::PlayerState::ATTACKING, THUAI6::PlayerState::Attacking}, | ||||
| {protobuf::PlayerState::LOCKING, THUAI6::PlayerState::Locking}, | {protobuf::PlayerState::LOCKING, THUAI6::PlayerState::Locking}, | ||||
| @@ -240,9 +240,9 @@ namespace Proto2THUAI6 | |||||
| student->facingDirection = studentMsg.facing_direction(); | student->facingDirection = studentMsg.facing_direction(); | ||||
| student->bulletType = bulletTypeDict[studentMsg.bullet_type()]; | student->bulletType = bulletTypeDict[studentMsg.bullet_type()]; | ||||
| student->learningSpeed = studentMsg.learning_speed(); | student->learningSpeed = studentMsg.learning_speed(); | ||||
| student->treatSpeed = studentMsg.treat_speed(); | |||||
| student->treatProgress = studentMsg.treat_progress(); | |||||
| student->rescueProgress = studentMsg.rescue_progress(); | |||||
| student->encourageSpeed = studentMsg.treat_speed(); | |||||
| student->encourageProgress = studentMsg.treat_progress(); | |||||
| student->rouseProgress = studentMsg.rescue_progress(); | |||||
| student->dangerAlert = studentMsg.danger_alert(); | student->dangerAlert = studentMsg.danger_alert(); | ||||
| student->timeUntilSkillAvailable.clear(); | student->timeUntilSkillAvailable.clear(); | ||||
| for (int i = 0; i < studentMsg.time_until_skill_available().size(); i++) | for (int i = 0; i < studentMsg.time_until_skill_available().size(); i++) | ||||
| @@ -385,7 +385,7 @@ namespace THUAI62Proto | |||||
| {THUAI6::TrickerType::Assassin, protobuf::TrickerType::ASSASSIN}, | {THUAI6::TrickerType::Assassin, protobuf::TrickerType::ASSASSIN}, | ||||
| {THUAI6::TrickerType::Klee, protobuf::TrickerType::KLEE}, | {THUAI6::TrickerType::Klee, protobuf::TrickerType::KLEE}, | ||||
| {THUAI6::TrickerType::ANoisyPerson, protobuf::TrickerType::A_NOISY_PERSON}, | {THUAI6::TrickerType::ANoisyPerson, protobuf::TrickerType::A_NOISY_PERSON}, | ||||
| {THUAI6::TrickerType::TrickerType4, protobuf::TrickerType::TRICKERTYPE4}, | |||||
| {THUAI6::TrickerType::Idol, protobuf::TrickerType::IDOL}, | |||||
| }; | }; | ||||
| // inline std::map<THUAI6::TrickerBuffType, protobuf::TrickerBuffType> trickerBuffTypeDict{ | // inline std::map<THUAI6::TrickerBuffType, protobuf::TrickerBuffType> trickerBuffTypeDict{ | ||||
| @@ -1,6 +1,7 @@ | |||||
| #include <vector> | #include <vector> | ||||
| #include <thread> | #include <thread> | ||||
| #include "AI.h" | #include "AI.h" | ||||
| #include "constants.h" | |||||
| // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | // 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | ||||
| extern const bool asynchronous = false; | extern const bool asynchronous = false; | ||||
| @@ -89,6 +89,18 @@ std::future<bool> TrickerAPI::UseProp(THUAI6::PropType prop) | |||||
| { return logic.UseProp(prop); }); | { return logic.UseProp(prop); }); | ||||
| } | } | ||||
| std::future<bool> StudentAPI::ThrowProp(THUAI6::PropType prop) | |||||
| { | |||||
| return std::async(std::launch::async, [=]() | |||||
| { return logic.ThrowProp(prop); }); | |||||
| } | |||||
| std::future<bool> TrickerAPI::ThrowProp(THUAI6::PropType prop) | |||||
| { | |||||
| return std::async(std::launch::async, [=]() | |||||
| { return logic.ThrowProp(prop); }); | |||||
| } | |||||
| std::future<bool> StudentAPI::UseSkill(int32_t skillID) | std::future<bool> StudentAPI::UseSkill(int32_t skillID) | ||||
| { | { | ||||
| return std::async(std::launch::async, [=]() | return std::async(std::launch::async, [=]() | ||||
| @@ -255,6 +267,16 @@ std::vector<std::shared_ptr<const THUAI6::Prop>> TrickerAPI::GetProps() const | |||||
| return logic.GetProps(); | return logic.GetProps(); | ||||
| } | } | ||||
| std::vector<std::shared_ptr<const THUAI6::Bullet>> StudentAPI::GetBullets() const | |||||
| { | |||||
| return logic.GetBullets(); | |||||
| } | |||||
| std::vector<std::shared_ptr<const THUAI6::Bullet>> TrickerAPI::GetBullets() const | |||||
| { | |||||
| return logic.GetBullets(); | |||||
| } | |||||
| std::vector<std::vector<THUAI6::PlaceType>> StudentAPI::GetFullMap() const | std::vector<std::vector<THUAI6::PlaceType>> StudentAPI::GetFullMap() const | ||||
| { | { | ||||
| return logic.GetFullMap(); | return logic.GetFullMap(); | ||||
| @@ -361,16 +383,16 @@ std::future<bool> StudentAPI::StartLearning() | |||||
| { return logic.StartLearning(); }); | { return logic.StartLearning(); }); | ||||
| } | } | ||||
| std::future<bool> StudentAPI::StartTreatMate(int64_t mateID) | |||||
| std::future<bool> StudentAPI::StartEncourageMate(int64_t mateID) | |||||
| { | { | ||||
| return std::async(std::launch::async, [=]() | return std::async(std::launch::async, [=]() | ||||
| { return logic.StartTreatMate(mateID); }); | |||||
| { return logic.StartEncourageMate(mateID); }); | |||||
| } | } | ||||
| std::future<bool> StudentAPI::StartRescueMate(int64_t mateID) | |||||
| std::future<bool> StudentAPI::StartRouseMate(int64_t mateID) | |||||
| { | { | ||||
| return std::async(std::launch::async, [=]() | return std::async(std::launch::async, [=]() | ||||
| { return logic.StartRescueMate(mateID); }); | |||||
| { return logic.StartRouseMate(mateID); }); | |||||
| } | } | ||||
| std::future<bool> StudentAPI::Graduate() | std::future<bool> StudentAPI::Graduate() | ||||
| @@ -51,6 +51,18 @@ bool Communication::UseProp(THUAI6::PropType prop, int64_t playerID) | |||||
| return false; | return false; | ||||
| } | } | ||||
| bool Communication::ThrowProp(THUAI6::PropType prop, int64_t playerID) | |||||
| { | |||||
| protobuf::BoolRes throwPropResult; | |||||
| ClientContext context; | |||||
| auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID); | |||||
| auto status = THUAI6Stub->ThrowProp(&context, request, &throwPropResult); | |||||
| if (status.ok()) | |||||
| return throwPropResult.act_success(); | |||||
| else | |||||
| return false; | |||||
| } | |||||
| bool Communication::UseSkill(int32_t skillID, int64_t playerID) | bool Communication::UseSkill(int32_t skillID, int64_t playerID) | ||||
| { | { | ||||
| protobuf::BoolRes useSkillResult; | protobuf::BoolRes useSkillResult; | ||||
| @@ -171,7 +183,7 @@ bool Communication::StartLearning(int64_t playerID) | |||||
| return false; | return false; | ||||
| } | } | ||||
| bool Communication::StartRescueMate(int64_t playerID, int64_t mateID) | |||||
| bool Communication::StartRouseMate(int64_t playerID, int64_t mateID) | |||||
| { | { | ||||
| protobuf::BoolRes saveStudentResult; | protobuf::BoolRes saveStudentResult; | ||||
| ClientContext context; | ClientContext context; | ||||
| @@ -183,7 +195,7 @@ bool Communication::StartRescueMate(int64_t playerID, int64_t mateID) | |||||
| return false; | return false; | ||||
| } | } | ||||
| bool Communication::StartTreatMate(int64_t playerID, int64_t mateID) | |||||
| bool Communication::StartEncourageMate(int64_t playerID, int64_t mateID) | |||||
| { | { | ||||
| protobuf::BoolRes healStudentResult; | protobuf::BoolRes healStudentResult; | ||||
| ClientContext context; | ClientContext context; | ||||
| @@ -187,6 +187,26 @@ std::future<bool> TrickerDebugAPI::UseProp(THUAI6::PropType prop) | |||||
| return result; }); | return result; }); | ||||
| } | } | ||||
| std::future<bool> StudentDebugAPI::ThrowProp(THUAI6::PropType prop) | |||||
| { | |||||
| logger->info("ThrowProp: prop={}, called at {}ms", THUAI6::propTypeDict[prop], Time::TimeSinceStart(startPoint)); | |||||
| return std::async(std::launch::async, [=]() | |||||
| { auto result = logic.ThrowProp(prop); | |||||
| if (!result) | |||||
| logger->warn("ThrowProp: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||||
| return result; }); | |||||
| } | |||||
| std::future<bool> TrickerDebugAPI::ThrowProp(THUAI6::PropType prop) | |||||
| { | |||||
| logger->info("ThrowProp: prop={}, called at {}ms", THUAI6::propTypeDict[prop], Time::TimeSinceStart(startPoint)); | |||||
| return std::async(std::launch::async, [=]() | |||||
| { auto result = logic.ThrowProp(prop); | |||||
| if (!result) | |||||
| logger->warn("ThrowProp: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||||
| return result; }); | |||||
| } | |||||
| std::future<bool> StudentDebugAPI::UseSkill(int32_t skillID) | std::future<bool> StudentDebugAPI::UseSkill(int32_t skillID) | ||||
| { | { | ||||
| logger->info("UseSkill: skillID={}, called at {}ms", skillID, Time::TimeSinceStart(startPoint)); | logger->info("UseSkill: skillID={}, called at {}ms", skillID, Time::TimeSinceStart(startPoint)); | ||||
| @@ -435,6 +455,16 @@ std::vector<std::shared_ptr<const THUAI6::Prop>> TrickerDebugAPI::GetProps() con | |||||
| return logic.GetProps(); | return logic.GetProps(); | ||||
| } | } | ||||
| std::vector<std::shared_ptr<const THUAI6::Bullet>> StudentDebugAPI::GetBullets() const | |||||
| { | |||||
| return logic.GetBullets(); | |||||
| } | |||||
| std::vector<std::shared_ptr<const THUAI6::Bullet>> TrickerDebugAPI::GetBullets() const | |||||
| { | |||||
| return logic.GetBullets(); | |||||
| } | |||||
| std::vector<std::vector<THUAI6::PlaceType>> StudentDebugAPI::GetFullMap() const | std::vector<std::vector<THUAI6::PlaceType>> StudentDebugAPI::GetFullMap() const | ||||
| { | { | ||||
| return logic.GetFullMap(); | return logic.GetFullMap(); | ||||
| @@ -545,23 +575,23 @@ std::future<bool> StudentDebugAPI::StartLearning() | |||||
| return result; }); | return result; }); | ||||
| } | } | ||||
| std::future<bool> StudentDebugAPI::StartRescueMate(int64_t mateID) | |||||
| std::future<bool> StudentDebugAPI::StartRouseMate(int64_t mateID) | |||||
| { | { | ||||
| logger->info("StartRescueMate: mate id={}, called at {}ms", mateID, Time::TimeSinceStart(startPoint)); | |||||
| logger->info("StartRouseMate: mate id={}, called at {}ms", mateID, Time::TimeSinceStart(startPoint)); | |||||
| return std::async(std::launch::async, [=]() | return std::async(std::launch::async, [=]() | ||||
| { auto result = logic.StartRescueMate(mateID); | |||||
| { auto result = logic.StartRouseMate(mateID); | |||||
| if (!result) | if (!result) | ||||
| logger->warn("StartRescueMate: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||||
| logger->warn("StartRouseMate: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||||
| return result; }); | return result; }); | ||||
| } | } | ||||
| std::future<bool> StudentDebugAPI::StartTreatMate(int64_t mateID) | |||||
| std::future<bool> StudentDebugAPI::StartEncourageMate(int64_t mateID) | |||||
| { | { | ||||
| logger->info("StartTreatMate: mate id={}, called at {}ms", mateID, Time::TimeSinceStart(startPoint)); | |||||
| logger->info("StartEncourageMate: mate id={}, called at {}ms", mateID, Time::TimeSinceStart(startPoint)); | |||||
| return std::async(std::launch::async, [=]() | return std::async(std::launch::async, [=]() | ||||
| { auto result = logic.StartTreatMate(mateID); | |||||
| { auto result = logic.StartEncourageMate(mateID); | |||||
| if (!result) | if (!result) | ||||
| logger->warn("StartTreatMate: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||||
| logger->warn("StartEncourageMate: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||||
| return result; }); | return result; }); | ||||
| } | } | ||||
| @@ -631,7 +661,7 @@ void StudentDebugAPI::PrintStudent() const | |||||
| props += THUAI6::propTypeDict[prop] + ", "; | props += THUAI6::propTypeDict[prop] + ", "; | ||||
| logger->info("state={}, bullet={}, props={}", THUAI6::playerStateDict[student->playerState], THUAI6::bulletTypeDict[student->bulletType], props); | logger->info("state={}, bullet={}, props={}", THUAI6::playerStateDict[student->playerState], THUAI6::bulletTypeDict[student->bulletType], props); | ||||
| logger->info("type={}, determination={}, addiction={}, danger alert={}", THUAI6::studentTypeDict[student->studentType], student->determination, student->addiction, student->dangerAlert); | logger->info("type={}, determination={}, addiction={}, danger alert={}", THUAI6::studentTypeDict[student->studentType], student->determination, student->addiction, student->dangerAlert); | ||||
| logger->info("learning speed={}, treat speed={}, treat progress={}, rescue progress={}", student->learningSpeed, student->treatSpeed, student->treatProgress, student->rescueProgress); | |||||
| logger->info("learning speed={}, encourage speed={}, encourage progress={}, rouse progress={}", student->learningSpeed, student->encourageSpeed, student->encourageProgress, student->rouseProgress); | |||||
| std::string studentBuff = ""; | std::string studentBuff = ""; | ||||
| for (const auto& buff : student->buff) | for (const auto& buff : student->buff) | ||||
| studentBuff += THUAI6::studentBuffDict[buff] + ", "; | studentBuff += THUAI6::studentBuffDict[buff] + ", "; | ||||
| @@ -656,7 +686,7 @@ void TrickerDebugAPI::PrintStudent() const | |||||
| props += THUAI6::propTypeDict[prop] + ", "; | props += THUAI6::propTypeDict[prop] + ", "; | ||||
| logger->info("state={}, bullet={}, props={}", THUAI6::playerStateDict[student->playerState], THUAI6::bulletTypeDict[student->bulletType], props); | logger->info("state={}, bullet={}, props={}", THUAI6::playerStateDict[student->playerState], THUAI6::bulletTypeDict[student->bulletType], props); | ||||
| logger->info("type={}, determination={}, addiction={}, danger alert={}", THUAI6::studentTypeDict[student->studentType], student->determination, student->addiction, student->dangerAlert); | logger->info("type={}, determination={}, addiction={}, danger alert={}", THUAI6::studentTypeDict[student->studentType], student->determination, student->addiction, student->dangerAlert); | ||||
| logger->info("learning speed={}, treat speed={}, treat progress={}, rescue progress={}", student->learningSpeed, student->treatSpeed, student->treatProgress, student->rescueProgress); | |||||
| logger->info("learning speed={}, encourage speed={}, encourage progress={}, rouse progress={}", student->learningSpeed, student->encourageSpeed, student->encourageProgress, student->rouseProgress); | |||||
| std::string studentBuff = ""; | std::string studentBuff = ""; | ||||
| for (const auto& buff : student->buff) | for (const auto& buff : student->buff) | ||||
| studentBuff += THUAI6::studentBuffDict[buff] + ", "; | studentBuff += THUAI6::studentBuffDict[buff] + ", "; | ||||
| @@ -748,7 +778,7 @@ void StudentDebugAPI::PrintSelfInfo() const | |||||
| props += THUAI6::propTypeDict[prop] + ", "; | props += THUAI6::propTypeDict[prop] + ", "; | ||||
| logger->info("state={}, bullet={}, props={}", THUAI6::playerStateDict[student->playerState], THUAI6::bulletTypeDict[student->bulletType], props); | logger->info("state={}, bullet={}, props={}", THUAI6::playerStateDict[student->playerState], THUAI6::bulletTypeDict[student->bulletType], props); | ||||
| logger->info("type={}, determination={}, addiction={}, danger alert={}", THUAI6::studentTypeDict[student->studentType], student->determination, student->addiction, student->dangerAlert); | logger->info("type={}, determination={}, addiction={}, danger alert={}", THUAI6::studentTypeDict[student->studentType], student->determination, student->addiction, student->dangerAlert); | ||||
| logger->info("learning speed={}, treat speed={}, treat progress={}, rescue progress={}", student->learningSpeed, student->treatSpeed, student->treatProgress, student->rescueProgress); | |||||
| logger->info("learning speed={}, encourage speed={}, encourage progress={}, rouse progress={}", student->learningSpeed, student->encourageSpeed, student->encourageProgress, student->rouseProgress); | |||||
| std::string studentBuff = ""; | std::string studentBuff = ""; | ||||
| for (const auto& buff : student->buff) | for (const auto& buff : student->buff) | ||||
| studentBuff += THUAI6::studentBuffDict[buff] + ", "; | studentBuff += THUAI6::studentBuffDict[buff] + ", "; | ||||
| @@ -51,6 +51,15 @@ std::vector<std::shared_ptr<const THUAI6::Prop>> Logic::GetProps() const | |||||
| return temp; | return temp; | ||||
| } | } | ||||
| std::vector<std::shared_ptr<const THUAI6::Bullet>> Logic::GetBullets() const | |||||
| { | |||||
| std::unique_lock<std::mutex> lock(mtxState); | |||||
| std::vector<std::shared_ptr<const THUAI6::Bullet>> temp; | |||||
| temp.assign(currentState->bullets.begin(), currentState->bullets.end()); | |||||
| logger->debug("Called GetBullets"); | |||||
| return temp; | |||||
| } | |||||
| std::shared_ptr<const THUAI6::Student> Logic::StudentGetSelfInfo() const | std::shared_ptr<const THUAI6::Student> Logic::StudentGetSelfInfo() const | ||||
| { | { | ||||
| std::unique_lock<std::mutex> lock(mtxState); | std::unique_lock<std::mutex> lock(mtxState); | ||||
| @@ -193,6 +202,12 @@ bool Logic::UseProp(THUAI6::PropType prop) | |||||
| return pComm->UseProp(prop, playerID); | return pComm->UseProp(prop, playerID); | ||||
| } | } | ||||
| bool Logic::ThrowProp(THUAI6::PropType prop) | |||||
| { | |||||
| logger->debug("Called ThrowProp"); | |||||
| return pComm->ThrowProp(prop, playerID); | |||||
| } | |||||
| bool Logic::UseSkill(int32_t skill) | bool Logic::UseSkill(int32_t skill) | ||||
| { | { | ||||
| logger->debug("Called UseSkill"); | logger->debug("Called UseSkill"); | ||||
| @@ -236,16 +251,16 @@ bool Logic::StartLearning() | |||||
| return pComm->StartLearning(playerID); | return pComm->StartLearning(playerID); | ||||
| } | } | ||||
| bool Logic::StartTreatMate(int64_t mateID) | |||||
| bool Logic::StartEncourageMate(int64_t mateID) | |||||
| { | { | ||||
| logger->debug("Called StartTreatMate"); | |||||
| return pComm->StartTreatMate(playerID, mateID); | |||||
| logger->debug("Called StartEncourageMate"); | |||||
| return pComm->StartEncourageMate(playerID, mateID); | |||||
| } | } | ||||
| bool Logic::StartRescueMate(int64_t mateID) | |||||
| bool Logic::StartRouseMate(int64_t mateID) | |||||
| { | { | ||||
| logger->debug("Called StartRescueMate"); | |||||
| return pComm->StartRescueMate(playerID, mateID); | |||||
| logger->debug("Called StartRouseMate"); | |||||
| return pComm->StartRouseMate(playerID, mateID); | |||||
| } | } | ||||
| bool Logic::Attack(double angle) | bool Logic::Attack(double angle) | ||||
| @@ -528,6 +543,15 @@ void Logic::LoadBuffer(protobuf::MessageToClient& message) | |||||
| } | } | ||||
| case THUAI6::MessageOfObj::TrickerMessage: | case THUAI6::MessageOfObj::TrickerMessage: | ||||
| { | { | ||||
| bool flag = false; | |||||
| for (int i = 0; i < item.tricker_message().buff_size(); i++) | |||||
| if (Proto2THUAI6::trickerBuffTypeDict[item.tricker_message().buff(i)] == THUAI6::TrickerBuffType::Invisible) | |||||
| { | |||||
| flag = true; | |||||
| break; | |||||
| } | |||||
| if (flag) | |||||
| break; | |||||
| if (AssistFunction::HaveView(bufferState->studentSelf->viewRange, bufferState->studentSelf->x, bufferState->studentSelf->y, item.tricker_message().x(), item.tricker_message().y(), bufferState->gameMap)) | if (AssistFunction::HaveView(bufferState->studentSelf->viewRange, bufferState->studentSelf->x, bufferState->studentSelf->y, item.tricker_message().x(), item.tricker_message().y(), bufferState->gameMap)) | ||||
| { | { | ||||
| bufferState->trickers.push_back(Proto2THUAI6::Protobuf2THUAI6Tricker(item.tricker_message())); | bufferState->trickers.push_back(Proto2THUAI6::Protobuf2THUAI6Tricker(item.tricker_message())); | ||||
| @@ -674,6 +698,23 @@ void Logic::LoadBuffer(protobuf::MessageToClient& message) | |||||
| } | } | ||||
| case THUAI6::MessageOfObj::StudentMessage: | case THUAI6::MessageOfObj::StudentMessage: | ||||
| { | { | ||||
| bool flag = false; | |||||
| for (const auto& buff : bufferState->trickerSelf->buff) | |||||
| if (buff == THUAI6::TrickerBuffType::Clairaudience) | |||||
| { | |||||
| flag = true; | |||||
| bufferState->students.push_back(Proto2THUAI6::Protobuf2THUAI6Student(item.student_message())); | |||||
| logger->debug("Add Student!"); | |||||
| break; | |||||
| } | |||||
| for (int i = 0; i < item.student_message().buff_size(); i++) | |||||
| if (Proto2THUAI6::studentBuffTypeDict[item.student_message().buff(i)] == THUAI6::StudentBuffType::Invisible) | |||||
| { | |||||
| flag = true; | |||||
| break; | |||||
| } | |||||
| if (flag) | |||||
| break; | |||||
| if (AssistFunction::HaveView(bufferState->trickerSelf->viewRange, bufferState->trickerSelf->x, bufferState->trickerSelf->y, item.student_message().x(), item.student_message().y(), bufferState->gameMap)) | if (AssistFunction::HaveView(bufferState->trickerSelf->viewRange, bufferState->trickerSelf->x, bufferState->trickerSelf->y, item.student_message().x(), item.student_message().y(), bufferState->gameMap)) | ||||
| { | { | ||||
| bufferState->students.push_back(Proto2THUAI6::Protobuf2THUAI6Student(item.student_message())); | bufferState->students.push_back(Proto2THUAI6::Protobuf2THUAI6Student(item.student_message())); | ||||
| @@ -1,31 +0,0 @@ | |||||
| | |||||
| Microsoft Visual Studio Solution File, Format Version 12.00 | |||||
| # Visual Studio Version 17 | |||||
| VisualStudioVersion = 17.0.32014.148 | |||||
| MinimumVisualStudioVersion = 10.0.40219.1 | |||||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "API", "API\API.vcxproj", "{B9AC3133-177D-453C-8066-ED4702D3F36A}" | |||||
| EndProject | |||||
| Global | |||||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||||
| Debug|x64 = Debug|x64 | |||||
| Debug|x86 = Debug|x86 | |||||
| Release|x64 = Release|x64 | |||||
| Release|x86 = Release|x86 | |||||
| EndGlobalSection | |||||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||||
| {B9AC3133-177D-453C-8066-ED4702D3F36A}.Debug|x64.ActiveCfg = Debug|x64 | |||||
| {B9AC3133-177D-453C-8066-ED4702D3F36A}.Debug|x64.Build.0 = Debug|x64 | |||||
| {B9AC3133-177D-453C-8066-ED4702D3F36A}.Debug|x86.ActiveCfg = Debug|Win32 | |||||
| {B9AC3133-177D-453C-8066-ED4702D3F36A}.Debug|x86.Build.0 = Debug|Win32 | |||||
| {B9AC3133-177D-453C-8066-ED4702D3F36A}.Release|x64.ActiveCfg = Release|x64 | |||||
| {B9AC3133-177D-453C-8066-ED4702D3F36A}.Release|x64.Build.0 = Release|x64 | |||||
| {B9AC3133-177D-453C-8066-ED4702D3F36A}.Release|x86.ActiveCfg = Release|Win32 | |||||
| {B9AC3133-177D-453C-8066-ED4702D3F36A}.Release|x86.Build.0 = Release|Win32 | |||||
| EndGlobalSection | |||||
| GlobalSection(SolutionProperties) = preSolution | |||||
| HideSolutionNode = FALSE | |||||
| EndGlobalSection | |||||
| GlobalSection(ExtensibilityGlobals) = postSolution | |||||
| SolutionGuid = {372B1478-522C-4EEB-A527-983D310A3F50} | |||||
| EndGlobalSection | |||||
| EndGlobal | |||||
| @@ -62,17 +62,16 @@ const char descriptor_table_protodef_MessageType_2eproto[] PROTOBUF_SECTION_VARI | |||||
| "\016TRICKER_PLAYER\020\002*q\n\013StudentType\022\025\n\021NULL" | "\016TRICKER_PLAYER\020\002*q\n\013StudentType\022\025\n\021NULL" | ||||
| "_STUDENT_TYPE\020\000\022\013\n\007ATHLETE\020\001\022\013\n\007TEACHER\020" | "_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" | "\002\022\026\n\022STRAIGHT_A_STUDENT\020\003\022\t\n\005ROBOT\020\004\022\016\n\n" | ||||
| "TECH_OTAKU\020\005*b\n\013TrickerType\022\025\n\021NULL_TRIC" | |||||
| "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_" | "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\020\n\014TRICKERTYPE4\020\004*P\n\tGame" | |||||
| "State\022\023\n\017NULL_GAME_STATE\020\000\022\016\n\nGAME_START" | |||||
| "\020\001\022\020\n\014GAME_RUNNING\020\002\022\014\n\010GAME_END\020\003b\006prot" | |||||
| "o3"; | |||||
| "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 ::_pbi::once_flag descriptor_table_MessageType_2eproto_once; | static ::_pbi::once_flag descriptor_table_MessageType_2eproto_once; | ||||
| const ::_pbi::DescriptorTable descriptor_table_MessageType_2eproto = { | const ::_pbi::DescriptorTable descriptor_table_MessageType_2eproto = { | ||||
| false, | false, | ||||
| false, | false, | ||||
| 1482, | |||||
| 1474, | |||||
| descriptor_table_protodef_MessageType_2eproto, | descriptor_table_protodef_MessageType_2eproto, | ||||
| "MessageType.proto", | "MessageType.proto", | ||||
| &descriptor_table_MessageType_2eproto_once, | &descriptor_table_MessageType_2eproto_once, | ||||
| @@ -368,13 +368,13 @@ namespace protobuf | |||||
| ASSASSIN = 1, | ASSASSIN = 1, | ||||
| KLEE = 2, | KLEE = 2, | ||||
| A_NOISY_PERSON = 3, | A_NOISY_PERSON = 3, | ||||
| TRICKERTYPE4 = 4, | |||||
| IDOL = 4, | |||||
| TrickerType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), | TrickerType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), | ||||
| TrickerType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() | TrickerType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() | ||||
| }; | }; | ||||
| bool TrickerType_IsValid(int value); | bool TrickerType_IsValid(int value); | ||||
| constexpr TrickerType TrickerType_MIN = NULL_TRICKER_TYPE; | constexpr TrickerType TrickerType_MIN = NULL_TRICKER_TYPE; | ||||
| constexpr TrickerType TrickerType_MAX = TRICKERTYPE4; | |||||
| constexpr TrickerType TrickerType_MAX = IDOL; | |||||
| constexpr int TrickerType_ARRAYSIZE = TrickerType_MAX + 1; | constexpr int TrickerType_ARRAYSIZE = TrickerType_MAX + 1; | ||||
| const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* TrickerType_descriptor(); | const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* TrickerType_descriptor(); | ||||
| @@ -1,6 +1,8 @@ | |||||
| import PyAPI.structures as THUAI6 | import PyAPI.structures as THUAI6 | ||||
| from PyAPI.Interface import IStudentAPI, ITrickerAPI, IAI | from PyAPI.Interface import IStudentAPI, ITrickerAPI, IAI | ||||
| from typing import Union, Final, cast | from typing import Union, Final, cast | ||||
| from PyAPI.constants import Constants | |||||
| import queue | |||||
| import time | import time | ||||
| @@ -9,7 +11,7 @@ class Setting: | |||||
| # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 | ||||
| @staticmethod | @staticmethod | ||||
| def asynchronous() -> bool: | def asynchronous() -> bool: | ||||
| return False | |||||
| return True | |||||
| # 选手必须修改该函数的返回值来选择自己的阵营 | # 选手必须修改该函数的返回值来选择自己的阵营 | ||||
| @staticmethod | @staticmethod | ||||
| @@ -41,50 +43,112 @@ class AssistFunction: | |||||
| return grid // numOfGridPerCell | return grid // numOfGridPerCell | ||||
| arrive: bool = False | |||||
| path = [] | |||||
| cur = 0 | |||||
| fixedclass = [] | |||||
| class AI(IAI): | class AI(IAI): | ||||
| # 选手在这里实现自己的逻辑,要求和上面选择的阵营保持一致 | # 选手在这里实现自己的逻辑,要求和上面选择的阵营保持一致 | ||||
| def StudentPlay(self, api: IStudentAPI) -> None: | def StudentPlay(self, api: IStudentAPI) -> None: | ||||
| api.Attack(float('nan')) | |||||
| time.sleep(0.5) | |||||
| api.PrintSelfInfo() | |||||
| # api.SendMessage(4, "Hello World!") | |||||
| # api.PrintSelfInfo() | |||||
| # global arrive | |||||
| # if not arrive: | |||||
| # if api.GetSelfInfo().x < 25500: | |||||
| # api.MoveDown(50) | |||||
| # global fixedclass | |||||
| # selfInfo = api.GetSelfInfo() | |||||
| # available = [THUAI6.PlaceType.Land, | |||||
| # THUAI6.PlaceType.Grass, THUAI6.PlaceType.Door3, THUAI6.PlaceType.Door6, THUAI6.PlaceType.Door5, THUAI6.PlaceType.Gate] | |||||
| # def bfs(x, y): | |||||
| # if api.GetPlaceType(x, y) not in available: | |||||
| # return [] | |||||
| # def GetSuccessors(x, y): | |||||
| # successors = [] | |||||
| # if x > 0 and api.GetPlaceType(x - 1, y) in available: | |||||
| # successors.append((x - 1, y)) | |||||
| # if x < 49 and api.GetPlaceType(x + 1, y) in available: | |||||
| # successors.append((x + 1, y)) | |||||
| # if y > 0 and api.GetPlaceType(x, y - 1) in available: | |||||
| # successors.append((x, y - 1)) | |||||
| # if y < 49 and api.GetPlaceType(x, y + 1) in available: | |||||
| # successors.append((x, y + 1)) | |||||
| # return successors | |||||
| # selfX = AssistFunction.GridToCell(api.GetSelfInfo().x) | |||||
| # selfY = AssistFunction.GridToCell(api.GetSelfInfo().y) | |||||
| # frontier = queue.Queue() | |||||
| # frontier.put((selfX, selfY, [])) | |||||
| # visited = [] | |||||
| # while not frontier.empty(): | |||||
| # currentX, currentY, path = frontier.get() | |||||
| # if currentX == x and currentY == y: | |||||
| # return path | |||||
| # for nextX, nextY in GetSuccessors(currentX, currentY): | |||||
| # if (nextX, nextY) not in visited: | |||||
| # visited.append((nextX, nextY)) | |||||
| # frontier.put((nextX, nextY, path + [(nextX, nextY)])) | |||||
| # return [] | |||||
| # def GoTo(x, y): | |||||
| # global path, cur | |||||
| # if path != [] and cur < len(path): | |||||
| # selfX = api.GetSelfInfo().x | |||||
| # selfY = api.GetSelfInfo().y | |||||
| # nextX, nextY = path[cur] | |||||
| # nextX = AssistFunction.CellToGrid(nextX) | |||||
| # nextY = AssistFunction.CellToGrid(nextY) | |||||
| # if selfX < nextX - 100: | |||||
| # api.MoveDown(10) | |||||
| # time.sleep(0.01) | |||||
| # return | |||||
| # if selfX > nextX + 100: | |||||
| # api.MoveUp(10) | |||||
| # time.sleep(0.01) | |||||
| # return | |||||
| # if selfY < nextY - 100: | |||||
| # api.MoveRight(10) | |||||
| # time.sleep(0.01) | |||||
| # return | |||||
| # if selfY > nextY + 100: | |||||
| # api.MoveLeft(10) | |||||
| # time.sleep(0.01) | |||||
| # return | |||||
| # cur += 1 | |||||
| # return | # return | ||||
| # if api.GetSelfInfo().y > 10500: | |||||
| # api.MoveLeft(50) | |||||
| # else: | |||||
| # path = bfs(x, y) | |||||
| # cur = 0 | |||||
| # return | # return | ||||
| # arrive = True | |||||
| # else: | |||||
| # api.SkipWindow() | |||||
| # # time.sleep(1) | |||||
| # api.PrintSelfInfo() | |||||
| # if api.GetSelfInfo().y < 18500: | |||||
| # api.MoveRight(50) | |||||
| # if (AssistFunction.GridToCell(api.GetSelfInfo().x), AssistFunction.GridToCell(api.GetSelfInfo().y)) == (6, 6) and api.GetGateProgress(5, 6) < 18000: | |||||
| # api.StartOpenGate() | |||||
| # return | # return | ||||
| # api.StartLearning() | |||||
| # if api.GetSelfInfo().y > 7000: | |||||
| # api.MoveLeft(50) | |||||
| # if (AssistFunction.GridToCell(api.GetSelfInfo().x), AssistFunction.GridToCell(api.GetSelfInfo().y)) == (6, 6) and api.GetGateProgress(5, 6) >= 18000: | |||||
| # api.Graduate() | |||||
| # return | # return | ||||
| # if api.GetSelfInfo().x > 20500: | |||||
| # api.MoveUp(50) | |||||
| # return | |||||
| # if api.GetSelfInfo().y > 4500: | |||||
| # api.MoveLeft(50) | |||||
| # if len(fixedclass) == 7: | |||||
| # GoTo(6, 6) | |||||
| # return | # return | ||||
| # if api.GetPlaceType(AssistFunction.GridToCell(api.GetSelfInfo().x) + 1, AssistFunction.GridToCell(api.GetSelfInfo().y)) == THUAI6.PlaceType.ClassRoom: | |||||
| # api.Print("Trying to fix!") | |||||
| # if api.GetClassroomProgress(AssistFunction.GridToCell(api.GetSelfInfo().x) + 1, AssistFunction.GridToCell(api.GetSelfInfo().y)) < 103000: | |||||
| # api.StartLearning() | |||||
| # return | |||||
| # else: | |||||
| # if (AssistFunction.GridToCell(api.GetSelfInfo().x) + 1, AssistFunction.GridToCell(api.GetSelfInfo().y)) not in fixedclass: | |||||
| # fixedclass.append( | |||||
| # (AssistFunction.GridToCell(api.GetSelfInfo().x) + 1, AssistFunction.GridToCell(api.GetSelfInfo().y))) | |||||
| # for i in range(50): | |||||
| # for j in range(50): | |||||
| # if api.GetPlaceType(i, j) == THUAI6.PlaceType.ClassRoom and (i, j) not in fixedclass: | |||||
| # if api.GetPlaceType(i - 1, j) in available: | |||||
| # GoTo(i - 1, j) | |||||
| # return | |||||
| api.PrintTricker() | api.PrintTricker() | ||||
| return | |||||
| def TrickerPlay(self, api: ITrickerAPI) -> None: | def TrickerPlay(self, api: ITrickerAPI) -> None: | ||||
| api.UseSkill(0) | |||||
| api.UseSkill(1) | |||||
| api.PrintSelfInfo() | |||||
| return | return | ||||
| @@ -41,6 +41,9 @@ class StudentAPI(IStudentAPI, IGameTimer): | |||||
| def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: | def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: | ||||
| return self.__pool.submit(self.__logic.UseProp, propType) | return self.__pool.submit(self.__logic.UseProp, propType) | ||||
| def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||||
| return self.__pool.submit(self.__logic.ThrowProp, propType) | |||||
| def UseSkill(self, skillID: int) -> Future[bool]: | def UseSkill(self, skillID: int) -> Future[bool]: | ||||
| return self.__pool.submit(self.__logic.UseSkill, skillID) | return self.__pool.submit(self.__logic.UseSkill, skillID) | ||||
| @@ -99,6 +102,9 @@ class StudentAPI(IStudentAPI, IGameTimer): | |||||
| def GetProps(self) -> List[THUAI6.Prop]: | def GetProps(self) -> List[THUAI6.Prop]: | ||||
| return self.__logic.GetProps() | return self.__logic.GetProps() | ||||
| def GetBullets(self) -> List[THUAI6.Bullet]: | |||||
| return self.__logic.GetBullets() | |||||
| def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | ||||
| return self.__logic.GetFullMap() | return self.__logic.GetFullMap() | ||||
| @@ -151,11 +157,11 @@ class StudentAPI(IStudentAPI, IGameTimer): | |||||
| def StartLearning(self) -> Future[bool]: | def StartLearning(self) -> Future[bool]: | ||||
| return self.__pool.submit(self.__logic.StartLearning) | return self.__pool.submit(self.__logic.StartLearning) | ||||
| def StartTreatMate(self, mateID: int) -> Future[bool]: | |||||
| return self.__pool.submit(self.__logic.StartTreatMate, mateID) | |||||
| def StartEncourageMate(self, mateID: int) -> Future[bool]: | |||||
| return self.__pool.submit(self.__logic.StartEncourageMate, mateID) | |||||
| def StartRescueMate(self, mateID: int) -> Future[bool]: | |||||
| return self.__pool.submit(self.__logic.StartRescueMate, mateID) | |||||
| def StartRouseMate(self, mateID: int) -> Future[bool]: | |||||
| return self.__pool.submit(self.__logic.StartRouseMate, mateID) | |||||
| def GetSelfInfo(self) -> THUAI6.Student: | def GetSelfInfo(self) -> THUAI6.Student: | ||||
| return cast(THUAI6.Student, self.__logic.GetSelfInfo()) | return cast(THUAI6.Student, self.__logic.GetSelfInfo()) | ||||
| @@ -208,6 +214,9 @@ class TrickerAPI(ITrickerAPI, IGameTimer): | |||||
| def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: | def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: | ||||
| return self.__pool.submit(self.__logic.UseProp, propType) | return self.__pool.submit(self.__logic.UseProp, propType) | ||||
| def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||||
| return self.__pool.submit(self.__logic.ThrowProp, propType) | |||||
| def UseSkill(self, skillID: int) -> Future[bool]: | def UseSkill(self, skillID: int) -> Future[bool]: | ||||
| return self.__pool.submit(self.__logic.UseSkill, skillID) | return self.__pool.submit(self.__logic.UseSkill, skillID) | ||||
| @@ -266,6 +275,9 @@ class TrickerAPI(ITrickerAPI, IGameTimer): | |||||
| def GetProps(self) -> List[THUAI6.Prop]: | def GetProps(self) -> List[THUAI6.Prop]: | ||||
| return self.__logic.GetProps() | return self.__logic.GetProps() | ||||
| def GetBullets(self) -> List[THUAI6.Bullet]: | |||||
| return self.__logic.GetBullets() | |||||
| def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | ||||
| return self.__logic.GetFullMap() | return self.__logic.GetFullMap() | ||||
| @@ -48,7 +48,7 @@ class Communication: | |||||
| else: | else: | ||||
| return pickResult.act_success | return pickResult.act_success | ||||
| def UseProp(self, propType: THUAI6.PropType, playerID: int): | |||||
| def UseProp(self, propType: THUAI6.PropType, playerID: int) -> bool: | |||||
| try: | try: | ||||
| useResult = self.__THUAI6Stub.UseProp( | useResult = self.__THUAI6Stub.UseProp( | ||||
| THUAI62Proto.THUAI62ProtobufProp(propType, playerID)) | THUAI62Proto.THUAI62ProtobufProp(propType, playerID)) | ||||
| @@ -57,6 +57,15 @@ class Communication: | |||||
| else: | else: | ||||
| return useResult.act_success | return useResult.act_success | ||||
| def ThrowProp(self, propType: THUAI6.PropType, playerID: int) -> bool: | |||||
| try: | |||||
| throwResult = self.__THUAI6Stub.ThrowProp( | |||||
| THUAI62Proto.THUAI62ProtobufProp(propType, playerID)) | |||||
| except grpc.RpcError as e: | |||||
| return False | |||||
| else: | |||||
| return throwResult.act_success | |||||
| def UseSkill(self, skillID: int, playerID: int) -> bool: | def UseSkill(self, skillID: int, playerID: int) -> bool: | ||||
| try: | try: | ||||
| useResult = self.__THUAI6Stub.UseSkill( | useResult = self.__THUAI6Stub.UseSkill( | ||||
| @@ -93,7 +102,7 @@ class Communication: | |||||
| else: | else: | ||||
| return learnResult.act_success | return learnResult.act_success | ||||
| def StartTreatMate(self, playerID: int, mateID: int) -> bool: | |||||
| def StartEncourageMate(self, playerID: int, mateID: int) -> bool: | |||||
| try: | try: | ||||
| helpResult = self.__THUAI6Stub.StartTreatMate( | helpResult = self.__THUAI6Stub.StartTreatMate( | ||||
| THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID)) | THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID)) | ||||
| @@ -102,7 +111,7 @@ class Communication: | |||||
| else: | else: | ||||
| return helpResult.act_success | return helpResult.act_success | ||||
| def StartRescueMate(self, playerID: int, mateID: int) -> bool: | |||||
| def StartRouseMate(self, playerID: int, mateID: int) -> bool: | |||||
| try: | try: | ||||
| helpResult = self.__THUAI6Stub.StartRescueMate( | helpResult = self.__THUAI6Stub.StartRescueMate( | ||||
| THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID)) | THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID)) | ||||
| @@ -109,6 +109,19 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): | |||||
| return self.__pool.submit(logUse) | return self.__pool.submit(logUse) | ||||
| def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||||
| self.__logger.info( | |||||
| f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms") | |||||
| def logThrow() -> bool: | |||||
| result = self.__logic.ThrowProp(propType) | |||||
| if not result: | |||||
| self.__logger.warning( | |||||
| f"ThrowProp: failed at {self.__GetTime()}ms") | |||||
| return result | |||||
| return self.__pool.submit(logThrow) | |||||
| def UseSkill(self, skillID: int) -> Future[bool]: | def UseSkill(self, skillID: int) -> Future[bool]: | ||||
| self.__logger.info( | self.__logger.info( | ||||
| f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms") | f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms") | ||||
| @@ -261,6 +274,9 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): | |||||
| def GetProps(self) -> List[THUAI6.Prop]: | def GetProps(self) -> List[THUAI6.Prop]: | ||||
| return self.__logic.GetProps() | return self.__logic.GetProps() | ||||
| def GetBullets(self) -> List[THUAI6.Bullet]: | |||||
| return self.__logic.GetBullets() | |||||
| def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | ||||
| return self.__logic.GetFullMap() | return self.__logic.GetFullMap() | ||||
| @@ -310,7 +326,7 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): | |||||
| self.__logger.info( | self.__logger.info( | ||||
| f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") | f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") | ||||
| self.__logger.info( | self.__logger.info( | ||||
| f"learning speed={student.learningSpeed}, treat speed={student.treatSpeed}, treat progress={student.treatProgress}, rescue progress={student.rescueProgress}") | |||||
| f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}") | |||||
| studentBuff = "" | studentBuff = "" | ||||
| for buff in student.buff: | for buff in student.buff: | ||||
| studentBuff += buff.name + ", " | studentBuff += buff.name + ", " | ||||
| @@ -363,7 +379,7 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): | |||||
| self.__logger.info( | self.__logger.info( | ||||
| f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") | f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") | ||||
| self.__logger.info( | self.__logger.info( | ||||
| f"learning speed={student.learningSpeed}, treat speed={student.treatSpeed}, treat progress={student.treatProgress}, rescue progress={student.rescueProgress}") | |||||
| f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}") | |||||
| studentBuff = "" | studentBuff = "" | ||||
| for buff in student.buff: | for buff in student.buff: | ||||
| studentBuff += buff.name + ", " | studentBuff += buff.name + ", " | ||||
| @@ -398,31 +414,31 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): | |||||
| return self.__pool.submit(logStart) | return self.__pool.submit(logStart) | ||||
| def StartTreatMate(self, mateID: int) -> Future[bool]: | |||||
| def StartEncourageMate(self, mateID: int) -> Future[bool]: | |||||
| self.__logger.info( | self.__logger.info( | ||||
| f"StartTreatMate: called at {self.__GetTime()}ms") | |||||
| f"StartEncourageMate: called at {self.__GetTime()}ms") | |||||
| def logStartTreatMate() -> bool: | |||||
| result = self.__logic.StartTreatMate(mateID) | |||||
| def logStartEncourageMate() -> bool: | |||||
| result = self.__logic.StartEncourageMate(mateID) | |||||
| if not result: | if not result: | ||||
| self.__logger.warning( | self.__logger.warning( | ||||
| f"StartTreatMate: failed at {self.__GetTime()}ms") | |||||
| f"StartEncourageMate: failed at {self.__GetTime()}ms") | |||||
| return result | return result | ||||
| return self.__pool.submit(logStartTreatMate) | |||||
| return self.__pool.submit(logStartEncourageMate) | |||||
| def StartRescueMate(self, mateID: int) -> Future[bool]: | |||||
| def StartRouseMate(self, mateID: int) -> Future[bool]: | |||||
| self.__logger.info( | self.__logger.info( | ||||
| f"StartRescueMate: called at {self.__GetTime()}ms") | |||||
| f"StartRouseMate: called at {self.__GetTime()}ms") | |||||
| def logStartRescueMate() -> bool: | |||||
| result = self.__logic.StartRescueMate(mateID) | |||||
| def logStartRouseMate() -> bool: | |||||
| result = self.__logic.StartRouseMate(mateID) | |||||
| if not result: | if not result: | ||||
| self.__logger.warning( | self.__logger.warning( | ||||
| f"StartRescueMate: failed at {self.__GetTime()}ms") | |||||
| f"StartRouseMate: failed at {self.__GetTime()}ms") | |||||
| return result | return result | ||||
| return self.__pool.submit(logStartRescueMate) | |||||
| return self.__pool.submit(logStartRouseMate) | |||||
| def GetSelfInfo(self) -> THUAI6.Student: | def GetSelfInfo(self) -> THUAI6.Student: | ||||
| return cast(THUAI6.Student, self.__logic.GetSelfInfo()) | return cast(THUAI6.Student, self.__logic.GetSelfInfo()) | ||||
| @@ -545,6 +561,19 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): | |||||
| return self.__pool.submit(logUse) | return self.__pool.submit(logUse) | ||||
| def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||||
| self.__logger.info( | |||||
| f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms") | |||||
| def logThrow() -> bool: | |||||
| result = self.__logic.ThrowProp(propType) | |||||
| if not result: | |||||
| self.__logger.warning( | |||||
| f"ThrowProp: failed at {self.__GetTime()}ms") | |||||
| return result | |||||
| return self.__pool.submit(logThrow) | |||||
| def UseSkill(self, skillID: int) -> Future[bool]: | def UseSkill(self, skillID: int) -> Future[bool]: | ||||
| self.__logger.info( | self.__logger.info( | ||||
| f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms") | f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms") | ||||
| @@ -697,6 +726,9 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): | |||||
| def GetProps(self) -> List[THUAI6.Prop]: | def GetProps(self) -> List[THUAI6.Prop]: | ||||
| return self.__logic.GetProps() | return self.__logic.GetProps() | ||||
| def GetBullets(self) -> List[THUAI6.Bullet]: | |||||
| return self.__logic.GetBullets() | |||||
| def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | ||||
| return self.__logic.GetFullMap() | return self.__logic.GetFullMap() | ||||
| @@ -746,7 +778,7 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): | |||||
| self.__logger.info( | self.__logger.info( | ||||
| f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") | f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") | ||||
| self.__logger.info( | self.__logger.info( | ||||
| f"learning speed={student.learningSpeed}, treat speed={student.treatSpeed}, treat progress={student.treatProgress}, rescue progress={student.rescueProgress}") | |||||
| f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}") | |||||
| studentBuff = "" | studentBuff = "" | ||||
| for buff in student.buff: | for buff in student.buff: | ||||
| studentBuff += buff.name + ", " | studentBuff += buff.name + ", " | ||||
| @@ -20,6 +20,10 @@ class ILogic(metaclass=ABCMeta): | |||||
| def GetProps(self) -> List[THUAI6.Prop]: | def GetProps(self) -> List[THUAI6.Prop]: | ||||
| pass | pass | ||||
| @abstractmethod | |||||
| def GetBullets(self) -> List[THUAI6.Bullet]: | |||||
| pass | |||||
| @abstractmethod | @abstractmethod | ||||
| def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]: | def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]: | ||||
| pass | pass | ||||
| @@ -72,6 +76,10 @@ class ILogic(metaclass=ABCMeta): | |||||
| def UseProp(self, propType: THUAI6.PropType) -> bool: | def UseProp(self, propType: THUAI6.PropType) -> bool: | ||||
| pass | pass | ||||
| @abstractmethod | |||||
| def ThrowProp(self, propType: THUAI6.PropType) -> bool: | |||||
| pass | |||||
| @abstractmethod | @abstractmethod | ||||
| def UseSkill(self, skillID: int) -> bool: | def UseSkill(self, skillID: int) -> bool: | ||||
| pass | pass | ||||
| @@ -139,11 +147,11 @@ class ILogic(metaclass=ABCMeta): | |||||
| pass | pass | ||||
| @abstractmethod | @abstractmethod | ||||
| def StartTreatMate(self, mateID: int) -> bool: | |||||
| def StartEncourageMate(self, mateID: int) -> bool: | |||||
| pass | pass | ||||
| @abstractmethod | @abstractmethod | ||||
| def StartRescueMate(self, mateID: int) -> bool: | |||||
| def StartRouseMate(self, mateID: int) -> bool: | |||||
| pass | pass | ||||
| @@ -184,6 +192,10 @@ class IAPI(metaclass=ABCMeta): | |||||
| def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: | def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: | ||||
| pass | pass | ||||
| @abstractmethod | |||||
| def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||||
| pass | |||||
| @abstractmethod | @abstractmethod | ||||
| def UseSkill(self, skillID: int) -> Future[bool]: | def UseSkill(self, skillID: int) -> Future[bool]: | ||||
| pass | pass | ||||
| @@ -258,6 +270,10 @@ class IAPI(metaclass=ABCMeta): | |||||
| def GetProps(self) -> List[THUAI6.Prop]: | def GetProps(self) -> List[THUAI6.Prop]: | ||||
| pass | pass | ||||
| @abstractmethod | |||||
| def GetBullets(self) -> List[THUAI6.Bullet]: | |||||
| pass | |||||
| @abstractmethod | @abstractmethod | ||||
| def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]: | def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]: | ||||
| pass | pass | ||||
| @@ -334,11 +350,11 @@ class IStudentAPI(IAPI, metaclass=ABCMeta): | |||||
| pass | pass | ||||
| @abstractmethod | @abstractmethod | ||||
| def StartTreatMate(self, mateID: int) -> Future[bool]: | |||||
| def StartEncourageMate(self, mateID: int) -> Future[bool]: | |||||
| pass | pass | ||||
| @abstractmethod | @abstractmethod | ||||
| def StartRescueMate(self, mateID: int) -> Future[bool]: | |||||
| def StartRouseMate(self, mateID: int) -> Future[bool]: | |||||
| pass | pass | ||||
| @abstractmethod | @abstractmethod | ||||
| @@ -0,0 +1,295 @@ | |||||
| from ast import Constant | |||||
| from asyncio import constants | |||||
| class NoInstance: | |||||
| def __call__(self): | |||||
| raise TypeError("This class cannot be instantiated.") | |||||
| class Constants(NoInstance): | |||||
| numOfGridPerCell = 1000 # 单位坐标数 | |||||
| rows = 50 # 地图行数 | |||||
| cols = 50 # 地图列数 | |||||
| numOfClassroom = 10 # 教室数量 | |||||
| numOfChest = 8 # 宝箱数量 | |||||
| maxClassroomProgress = 10000000 # 教室最大进度 | |||||
| maxDoorProgress = 10000000 # 开关门最大进度 | |||||
| maxChestProgress = 10000000 # 宝箱最大进度 | |||||
| maxGateProgress = 18000 # 大门最大进度 | |||||
| numOfRequiredClassroomForGate = 7 # 打开大门需要完成的教室数量 | |||||
| numOfRequiredClassroomForHiddenGate = 3 # 打开隐藏门需要完成的教室数量 | |||||
| # 人物属性相关 | |||||
| basicEncourageSpeed = 100 | |||||
| basicLearnSpeed = 123 | |||||
| basicSpeedOfOpeningOrLocking = 4000 | |||||
| basicStudentSpeedOfClimbingThroughWindows = 611 | |||||
| basicTrickerSpeedOfClimbingThroughWindows = 1270 | |||||
| basicSpeedOfOpenChest = 1000 | |||||
| basicHp = 3000000 | |||||
| basicMaxGamingAddiction = 60000 | |||||
| basicEncouragementDegree = 1500000 | |||||
| basicTimeOfRouse = 1000 | |||||
| basicStudentSpeed = 1270 | |||||
| basicTrickerSpeed = 1504 | |||||
| basicConcealment = 1.0 | |||||
| basicStudentAlertnessRadius = 15 * numOfGridPerCell | |||||
| basicTrickerAlertnessRadius = 17 * numOfGridPerCell | |||||
| basicStudentViewRange = 10 * numOfGridPerCell | |||||
| basicTrickerViewRange = 15 * numOfGridPerCell | |||||
| PinningDownRange = 5 * numOfGridPerCell | |||||
| maxNumOfProp = 3 # 人物道具栏容量 | |||||
| # 攻击相关 | |||||
| basicApOfTricker = 1500000 | |||||
| basicCD = 3000 # 初始子弹冷却 | |||||
| basicCastTime = 500 # 基本前摇时间 | |||||
| basicBackswing = 800 # 基本后摇时间 | |||||
| basicRecoveryFromHit = 3700 # 基本命中攻击恢复时长 | |||||
| basicStunnedTimeOfStudent = 4300 | |||||
| basicBulletmoveSpeed = 3700 # 基本子弹移动速度 | |||||
| basicRemoteAttackRange = 3000 # 基本远程攻击范围 | |||||
| basicAttackShortRange = 1100 # 基本近程攻击范围 | |||||
| basicBulletBombRange = 1000 # 基本子弹爆炸范围 | |||||
| # 道具相关 | |||||
| apPropAdd = basicApOfTricker * 12 / 10 | |||||
| apSpearAdd = basicApOfTricker * 6 / 10 | |||||
| # 技能相关 | |||||
| maxNumOfSkill = 3 | |||||
| commonSkillCD = 30000 # 普通技能标准冷却时间 | |||||
| commonSkillTime = 10000 # 普通技能标准持续时间 | |||||
| timeOfTrickerStunnedWhenCharge = 7220 | |||||
| timeOfStudentStunnedWhenCharge = 2090 | |||||
| timeOfTrickerStunnedWhenPunish = 3070 | |||||
| timeOfTrickerSwingingAfterHowl = 800 | |||||
| timeOfStudentStunnedWhenHowl = 5500 | |||||
| timeOfStunnedWhenJumpyDumpty = 3070 | |||||
| addedTimeOfSpeedWhenInspire = 0.6 | |||||
| timeOfAddingSpeedWhenInspire = 6000 | |||||
| class Assassin: | |||||
| moveSpeed = (int)(1.1 * Constants.basicTrickerSpeed) | |||||
| concealment = 1.5 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(1.3 * Constants.basicTrickerAlertnessRadius) | |||||
| viewRange = (int)(1.2 * Constants.basicTrickerViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) | |||||
| class Klee: | |||||
| moveSpeed = (int)(1.0 * Constants.basicTrickerSpeed) | |||||
| concealment = 1.0 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(1.0 * Constants.basicTrickerAlertnessRadius) | |||||
| viewRange = (int)(1.0 * Constants.basicTrickerViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.1 * Constants.basicSpeedOfOpenChest) | |||||
| class ANoisyPerson: | |||||
| moveSpeed = (int)(1.07 * Constants.basicTrickerSpeed) | |||||
| concealment = 0.8 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(0.9 * Constants.basicTrickerAlertnessRadius) | |||||
| viewRange = (int)(1.0 * Constants.basicTrickerViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 1.1 * Constants.basicTrickerSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.1 * Constants.basicSpeedOfOpenChest) | |||||
| class Idol: | |||||
| moveSpeed = (int)(1.0 * Constants.basicTrickerSpeed) | |||||
| concealment = 0.75 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(1.0 * Constants.basicTrickerAlertnessRadius) | |||||
| viewRange = (int)(1.1 * Constants.basicTrickerViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) | |||||
| class Athlete: | |||||
| moveSpeed = (int)(1.05 * Constants.basicStudentSpeed) | |||||
| maxHp = (int)(1.0 * Constants.basicHp) | |||||
| maxAddiction = (int)(0.9 * Constants.basicMaxGamingAddiction) | |||||
| LearnSpeed = (int)(0.6 * Constants.basicLearnSpeed) | |||||
| EncourageSpeed = (int)(0.9 * Constants.basicEncourageSpeed) | |||||
| concealment = 0.9 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) | |||||
| viewRange = (int)(1.1 * Constants.basicStudentViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 1.2 * Constants.basicStudentSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) | |||||
| class Teacher: | |||||
| moveSpeed = (int)(0.9 * Constants.basicStudentSpeed) | |||||
| maxHp = (int)(10.0 * Constants.basicHp) | |||||
| maxAddiction = (int)(10.0 * Constants.basicMaxGamingAddiction) | |||||
| LearnSpeed = (int)(0.0 * Constants.basicLearnSpeed) | |||||
| EncourageSpeed = (int)(0.8 * Constants.basicEncourageSpeed) | |||||
| concealment = 0.5 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(0.5 * Constants.basicStudentAlertnessRadius) | |||||
| viewRange = (int)(0.9 * Constants.basicStudentViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 0.5 * Constants.basicStudentSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) | |||||
| class StraightAStudent: | |||||
| moveSpeed = (int)(0.96 * Constants.basicStudentSpeed) | |||||
| maxHp = (int)(1.1 * Constants.basicHp) | |||||
| maxAddiction = (int)(1.3 * Constants.basicMaxGamingAddiction) | |||||
| LearnSpeed = (int)(1.1 * Constants.basicLearnSpeed) | |||||
| EncourageSpeed = (int)(Constants.basicEncourageSpeed) | |||||
| concealment = 0.9 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(0.9 * Constants.basicStudentAlertnessRadius) | |||||
| viewRange = (int)(0.9 * Constants.basicStudentViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 0.83333 * Constants.basicStudentSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) | |||||
| class Robot: | |||||
| moveSpeed = (int)(1.0 * Constants.basicStudentSpeed) | |||||
| maxHp = (int)(0.4 * Constants.basicHp) | |||||
| maxAddiction = (int)(0.0 * Constants.basicMaxGamingAddiction) | |||||
| LearnSpeed = (int)(1.0 * Constants.basicLearnSpeed) | |||||
| EncourageSpeed = 0 | |||||
| concealment = 1.0 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) | |||||
| viewRange = (int)(1.0 * Constants.basicStudentViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 0.0016 * Constants.basicStudentSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) | |||||
| class TechOtaku: | |||||
| moveSpeed = (int)(0.75 * Constants.basicStudentSpeed) | |||||
| maxHp = (int)(0.9 * Constants.basicHp) | |||||
| maxAddiction = (int)(1.1 * Constants.basicMaxGamingAddiction) | |||||
| LearnSpeed = (int)(1.1 * Constants.basicLearnSpeed) | |||||
| EncourageSpeed = (int)(0.9 * Constants.basicEncourageSpeed) | |||||
| concealment = 1.0 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) | |||||
| viewRange = (int)(0.9 * Constants.basicStudentViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 1.0 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 0.75 * Constants.basicStudentSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) | |||||
| class Sunshine: | |||||
| moveSpeed = (int)(1.0 * Constants.basicStudentSpeed) | |||||
| maxHp = (int)(1.0667 * Constants.basicHp) | |||||
| maxAddiction = (int)(1.1 * Constants.basicMaxGamingAddiction) | |||||
| LearnSpeed = (int)(1.0 * Constants.basicLearnSpeed) | |||||
| EncourageSpeed = (int)(1.2 * Constants.basicEncourageSpeed) | |||||
| concealment = 1.0 * Constants.basicConcealment | |||||
| alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) | |||||
| viewRange = (int)(1.0 * Constants.basicStudentViewRange) | |||||
| speedOfOpeningOrLocking = (int)( | |||||
| 0.7 * Constants.basicSpeedOfOpeningOrLocking) | |||||
| speedOfClimbingThroughWindows = (int)( | |||||
| 1.0 * Constants.basicStudentSpeedOfClimbingThroughWindows) | |||||
| speedOfOpenChest = (int)(0.9 * Constants.basicSpeedOfOpenChest) | |||||
| class CanBeginToCharge: | |||||
| skillCD = (int)(2 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.3 * Constants.commonSkillTime) | |||||
| class BecomeInvisible: | |||||
| skillCD = (int)(4 * Constants.commonSkillCD / 3) | |||||
| durationTime = (int)(Constants.commonSkillTime) | |||||
| class Punish: | |||||
| skillCD = (int)(1.0 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.0 * Constants.commonSkillTime) | |||||
| class Rouse: | |||||
| skillCD = (int)(4.0 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.0 * Constants.commonSkillTime) | |||||
| class Encourage: | |||||
| skillCD = (int)(4.0 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.0 * Constants.commonSkillTime) | |||||
| class Inspire: | |||||
| skillCD = (int)(4.0 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.0 * Constants.commonSkillTime) | |||||
| class Howl: | |||||
| skillCD = (int)(0.8333 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0 * Constants.commonSkillTime) | |||||
| class ShowTime: | |||||
| skillCD = (int)(3.0 * Constants.commonSkillCD) | |||||
| durationTime = (int)(1.0 * Constants.commonSkillTime) | |||||
| class JumpyBomb: | |||||
| skillCD = (int)(0.5 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.3 * Constants.commonSkillTime) | |||||
| class UseKnife: | |||||
| skillCD = (int)(1.0 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.1 * Constants.commonSkillTime) | |||||
| class UseRobot: | |||||
| skillCD = (int)(0.0017 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.0 * Constants.commonSkillTime) | |||||
| class WriteAnswers: | |||||
| skillCD = (int)(1.0 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.0 * Constants.commonSkillTime) | |||||
| class SummonGolem: | |||||
| skillCD = (int)(1.0 * Constants.commonSkillCD) | |||||
| durationTime = (int)(0.0 * Constants.commonSkillTime) | |||||
| @@ -2,6 +2,7 @@ import os | |||||
| from typing import List, Union, Callable, Tuple | from typing import List, Union, Callable, Tuple | ||||
| import threading | import threading | ||||
| import logging | import logging | ||||
| import copy | |||||
| import proto.MessageType_pb2 as MessageType | import proto.MessageType_pb2 as MessageType | ||||
| import proto.Message2Server_pb2 as Message2Server | import proto.Message2Server_pb2 as Message2Server | ||||
| import proto.Message2Clients_pb2 as Message2Clients | import proto.Message2Clients_pb2 as Message2Clients | ||||
| @@ -71,27 +72,32 @@ class Logic(ILogic): | |||||
| def GetTrickers(self) -> List[THUAI6.Tricker]: | def GetTrickers(self) -> List[THUAI6.Tricker]: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetTrickers") | self.__logger.debug("Called GetTrickers") | ||||
| return self.__currentState.trickers | |||||
| return copy.deepcopy(self.__currentState.trickers) | |||||
| def GetStudents(self) -> List[THUAI6.Student]: | def GetStudents(self) -> List[THUAI6.Student]: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetStudents") | self.__logger.debug("Called GetStudents") | ||||
| return self.__currentState.students | |||||
| return copy.deepcopy(self.__currentState.students) | |||||
| def GetProps(self) -> List[THUAI6.Prop]: | def GetProps(self) -> List[THUAI6.Prop]: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetProps") | self.__logger.debug("Called GetProps") | ||||
| return self.__currentState.props | |||||
| return copy.deepcopy(self.__currentState.props) | |||||
| def GetBullets(self) -> List[THUAI6.Bullet]: | |||||
| with self.__mtxState: | |||||
| self.__logger.debug("Called GetBullets") | |||||
| return copy.deepcopy(self.__currentState.bullets) | |||||
| def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]: | def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetSelfInfo") | self.__logger.debug("Called GetSelfInfo") | ||||
| return self.__currentState.self | |||||
| return copy.deepcopy(self.__currentState.self) | |||||
| def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | def GetFullMap(self) -> List[List[THUAI6.PlaceType]]: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetFullMap") | self.__logger.debug("Called GetFullMap") | ||||
| return self.__currentState.gameMap | |||||
| return copy.deepcopy(self.__currentState.gameMap) | |||||
| def GetPlaceType(self, x: int, y: int) -> THUAI6.PlaceType: | def GetPlaceType(self, x: int, y: int) -> THUAI6.PlaceType: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| @@ -99,13 +105,13 @@ class Logic(ILogic): | |||||
| self.__logger.warning("Invalid position") | self.__logger.warning("Invalid position") | ||||
| return THUAI6.PlaceType.NullPlaceType | return THUAI6.PlaceType.NullPlaceType | ||||
| self.__logger.debug("Called GetPlaceType") | self.__logger.debug("Called GetPlaceType") | ||||
| return self.__currentState.gameMap[x][y] | |||||
| return copy.deepcopy(self.__currentState.gameMap[x][y]) | |||||
| def IsDoorOpen(self, x: int, y: int) -> bool: | def IsDoorOpen(self, x: int, y: int) -> bool: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called IsDoorOpen") | self.__logger.debug("Called IsDoorOpen") | ||||
| if (x, y) in self.__currentState.mapInfo.doorState: | if (x, y) in self.__currentState.mapInfo.doorState: | ||||
| return self.__currentState.mapInfo.doorState[(x, y)] | |||||
| return copy.deepcopy(self.__currentState.mapInfo.doorState[(x, y)]) | |||||
| else: | else: | ||||
| self.__logger.warning("Door not found") | self.__logger.warning("Door not found") | ||||
| return False | return False | ||||
| @@ -114,7 +120,7 @@ class Logic(ILogic): | |||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetClassroomProgress") | self.__logger.debug("Called GetClassroomProgress") | ||||
| if (x, y) in self.__currentState.mapInfo.classroomState: | if (x, y) in self.__currentState.mapInfo.classroomState: | ||||
| return self.__currentState.mapInfo.classroomState[(x, y)] | |||||
| return copy.deepcopy(self.__currentState.mapInfo.classroomState[(x, y)]) | |||||
| else: | else: | ||||
| self.__logger.warning("Classroom not found") | self.__logger.warning("Classroom not found") | ||||
| return 0 | return 0 | ||||
| @@ -123,7 +129,7 @@ class Logic(ILogic): | |||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetChestProgress") | self.__logger.debug("Called GetChestProgress") | ||||
| if (x, y) in self.__currentState.mapInfo.chestState: | if (x, y) in self.__currentState.mapInfo.chestState: | ||||
| return self.__currentState.mapInfo.chestState[(x, y)] | |||||
| return copy.deepcopy(self.__currentState.mapInfo.chestState[(x, y)]) | |||||
| else: | else: | ||||
| self.__logger.warning("Chest not found") | self.__logger.warning("Chest not found") | ||||
| return 0 | return 0 | ||||
| @@ -132,7 +138,7 @@ class Logic(ILogic): | |||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetGateProgress") | self.__logger.debug("Called GetGateProgress") | ||||
| if (x, y) in self.__currentState.mapInfo.gateState: | if (x, y) in self.__currentState.mapInfo.gateState: | ||||
| return self.__currentState.mapInfo.gateState[(x, y)] | |||||
| return copy.deepcopy(self.__currentState.mapInfo.gateState[(x, y)]) | |||||
| else: | else: | ||||
| self.__logger.warning("Gate not found") | self.__logger.warning("Gate not found") | ||||
| return 0 | return 0 | ||||
| @@ -141,7 +147,7 @@ class Logic(ILogic): | |||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetHiddenGateState") | self.__logger.debug("Called GetHiddenGateState") | ||||
| if (x, y) in self.__currentState.mapInfo.hiddenGateState: | if (x, y) in self.__currentState.mapInfo.hiddenGateState: | ||||
| return self.__currentState.mapInfo.hiddenGateState[(x, y)] | |||||
| return copy.deepcopy(self.__currentState.mapInfo.hiddenGateState[(x, y)]) | |||||
| else: | else: | ||||
| self.__logger.warning("HiddenGate not found") | self.__logger.warning("HiddenGate not found") | ||||
| return THUAI6.HiddenGateState.Null | return THUAI6.HiddenGateState.Null | ||||
| @@ -150,7 +156,7 @@ class Logic(ILogic): | |||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetDoorProgress") | self.__logger.debug("Called GetDoorProgress") | ||||
| if (x, y) in self.__currentState.mapInfo.doorProgress: | if (x, y) in self.__currentState.mapInfo.doorProgress: | ||||
| return self.__currentState.mapInfo.doorProgress[(x, y)] | |||||
| return copy.deepcopy(self.__currentState.mapInfo.doorProgress[(x, y)]) | |||||
| else: | else: | ||||
| self.__logger.warning("Door not found") | self.__logger.warning("Door not found") | ||||
| return 0 | return 0 | ||||
| @@ -158,7 +164,7 @@ class Logic(ILogic): | |||||
| def GetGameInfo(self) -> THUAI6.GameInfo: | def GetGameInfo(self) -> THUAI6.GameInfo: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| self.__logger.debug("Called GetGameInfo") | self.__logger.debug("Called GetGameInfo") | ||||
| return self.__currentState.gameInfo | |||||
| return copy.deepcopy(self.__currentState.gameInfo) | |||||
| def Move(self, time: int, angle: float) -> bool: | def Move(self, time: int, angle: float) -> bool: | ||||
| self.__logger.debug("Called Move") | self.__logger.debug("Called Move") | ||||
| @@ -172,6 +178,10 @@ class Logic(ILogic): | |||||
| self.__logger.debug("Called UseProp") | self.__logger.debug("Called UseProp") | ||||
| return self.__comm.UseProp(propType, self.__playerID) | return self.__comm.UseProp(propType, self.__playerID) | ||||
| def ThrowProp(self, propType: THUAI6.PropType) -> bool: | |||||
| self.__logger.debug("Called ThrowProp") | |||||
| return self.__comm.ThrowProp(propType, self.__playerID) | |||||
| def UseSkill(self, skillID: int) -> bool: | def UseSkill(self, skillID: int) -> bool: | ||||
| self.__logger.debug("Called UseSkill") | self.__logger.debug("Called UseSkill") | ||||
| return self.__comm.UseSkill(skillID, self.__playerID) | return self.__comm.UseSkill(skillID, self.__playerID) | ||||
| @@ -198,11 +208,11 @@ class Logic(ILogic): | |||||
| def GetCounter(self) -> int: | def GetCounter(self) -> int: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| return self.__counterState | |||||
| return copy.deepcopy(self.__counterState) | |||||
| def GetPlayerGUIDs(self) -> List[int]: | def GetPlayerGUIDs(self) -> List[int]: | ||||
| with self.__mtxState: | with self.__mtxState: | ||||
| return self.__playerGUIDs | |||||
| return copy.deepcopy(self.__playerGUIDs) | |||||
| # IStudentAPI使用的接口 | # IStudentAPI使用的接口 | ||||
| @@ -214,13 +224,13 @@ class Logic(ILogic): | |||||
| self.__logger.debug("Called StartLearning") | self.__logger.debug("Called StartLearning") | ||||
| return self.__comm.StartLearning(self.__playerID) | return self.__comm.StartLearning(self.__playerID) | ||||
| def StartTreatMate(self, mateID: int) -> bool: | |||||
| self.__logger.debug("Called StartTreatMate") | |||||
| return self.__comm.StartTreatMate(self.__playerID, mateID) | |||||
| def StartEncourageMate(self, mateID: int) -> bool: | |||||
| self.__logger.debug("Called StartEncourageMate") | |||||
| return self.__comm.StartEncourageMate(self.__playerID, mateID) | |||||
| def StartRescueMate(self, mateID: int) -> bool: | |||||
| self.__logger.debug("Called StartRescueMate") | |||||
| return self.__comm.StartRescueMate(self.__playerID, mateID) | |||||
| def StartRouseMate(self, mateID: int) -> bool: | |||||
| self.__logger.debug("Called StartRouseMate") | |||||
| return self.__comm.StartRouseMate(self.__playerID, mateID) | |||||
| def Attack(self, angle: float) -> bool: | def Attack(self, angle: float) -> bool: | ||||
| self.__logger.debug("Called Trick") | self.__logger.debug("Called Trick") | ||||
| @@ -348,6 +358,8 @@ class Logic(ILogic): | |||||
| self.__logger.debug("Add Student!") | self.__logger.debug("Add Student!") | ||||
| for item in message.obj_message: | for item in message.obj_message: | ||||
| if item.WhichOneof("message_of_obj") == "tricker_message": | if item.WhichOneof("message_of_obj") == "tricker_message": | ||||
| if MessageType.TRICKER_INVISIBLE in item.tricker_message.buff: | |||||
| continue | |||||
| if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.tricker_message.x, item.tricker_message.y, self.__bufferState.gameMap): | if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.tricker_message.x, item.tricker_message.y, self.__bufferState.gameMap): | ||||
| self.__bufferState.trickers.append( | self.__bufferState.trickers.append( | ||||
| Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message)) | Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message)) | ||||
| @@ -440,6 +452,13 @@ class Logic(ILogic): | |||||
| self.__logger.debug("Add Tricker!") | self.__logger.debug("Add Tricker!") | ||||
| for item in message.obj_message: | for item in message.obj_message: | ||||
| if item.WhichOneof("message_of_obj") == "student_message": | if item.WhichOneof("message_of_obj") == "student_message": | ||||
| if THUAI6.TrickerBuffType.Clairaudience in self.__bufferState.self.buff: | |||||
| self.__bufferState.students.append( | |||||
| Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)) | |||||
| self.__logger.debug("Add Student!") | |||||
| continue | |||||
| if MessageType.STUDENT_INVISIBLE in item.student_message.buff: | |||||
| continue | |||||
| if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.student_message.x, item.student_message.y, self.__bufferState.gameMap): | if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.student_message.x, item.student_message.y, self.__bufferState.gameMap): | ||||
| self.__bufferState.students.append( | self.__bufferState.students.append( | ||||
| Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)) | Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)) | ||||
| @@ -39,7 +39,6 @@ def THUAI6Main(argv: List[str], AIBuilder: Callable) -> None: | |||||
| file = args.file | file = args.file | ||||
| screen = args.screen | screen = args.screen | ||||
| warnOnly = args.warnOnly | warnOnly = args.warnOnly | ||||
| print(warnOnly) | |||||
| logic = Logic(pID) | logic = Logic(pID) | ||||
| logic.Main(AIBuilder, sIP, sPort, file, screen, warnOnly) | logic.Main(AIBuilder, sIP, sPort, file, screen, warnOnly) | ||||
| @@ -7,6 +7,7 @@ if sys.version_info < (3, 9): | |||||
| else: | else: | ||||
| Tuple = tuple | Tuple = tuple | ||||
| class GameState(Enum): | class GameState(Enum): | ||||
| NullGameState = 0 | NullGameState = 0 | ||||
| GameStart = 1 | GameStart = 1 | ||||
| @@ -76,6 +77,7 @@ class TrickerType(Enum): | |||||
| Assassin = 1 | Assassin = 1 | ||||
| Klee = 2 | Klee = 2 | ||||
| ANoisyPerson = 3 | ANoisyPerson = 3 | ||||
| Idol = 4 | |||||
| class StudentBuffType(Enum): | class StudentBuffType(Enum): | ||||
| @@ -102,11 +104,11 @@ class PlayerState(Enum): | |||||
| Addicted = 3 | Addicted = 3 | ||||
| Quit = 4 | Quit = 4 | ||||
| Graduated = 5 | Graduated = 5 | ||||
| Treated = 6 | |||||
| Rescued = 7 | |||||
| Encouraged = 6 | |||||
| Roused = 7 | |||||
| Stunned = 8 | Stunned = 8 | ||||
| Treating = 9 | |||||
| Rescuing = 10 | |||||
| Encouraging = 9 | |||||
| Rousing = 10 | |||||
| Swinging = 11 | Swinging = 11 | ||||
| Attacking = 12 | Attacking = 12 | ||||
| Locking = 13 | Locking = 13 | ||||
| @@ -161,10 +163,10 @@ class Student(Player): | |||||
| studentType: StudentType | studentType: StudentType | ||||
| determination: int | determination: int | ||||
| addiction: int | addiction: int | ||||
| treatProgress: int | |||||
| rescueProgress: int | |||||
| encourageProgress: int | |||||
| rouseProgress: int | |||||
| learningSpeed: int | learningSpeed: int | ||||
| treatSpeed: int | |||||
| encourageSpeed: int | |||||
| dangerAlert: float | dangerAlert: float | ||||
| buff: List[StudentBuffType] = [] | buff: List[StudentBuffType] = [] | ||||
| @@ -111,7 +111,8 @@ class Proto2THUAI6(NoInstance): | |||||
| MessageType.NULL_TRICKER_TYPE: THUAI6.TrickerType.NullTrickerType, | MessageType.NULL_TRICKER_TYPE: THUAI6.TrickerType.NullTrickerType, | ||||
| MessageType.ASSASSIN: THUAI6.TrickerType.Assassin, | MessageType.ASSASSIN: THUAI6.TrickerType.Assassin, | ||||
| MessageType.KLEE: THUAI6.TrickerType.Klee, | MessageType.KLEE: THUAI6.TrickerType.Klee, | ||||
| MessageType.A_NOISY_PERSON: THUAI6.TrickerType.ANoisyPerson, } | |||||
| MessageType.A_NOISY_PERSON: THUAI6.TrickerType.ANoisyPerson, | |||||
| MessageType.IDOL: THUAI6.TrickerType.Idol, } | |||||
| studentBuffTypeDict: Final[dict] = { | studentBuffTypeDict: Final[dict] = { | ||||
| MessageType.NULL_SBUFF_TYPE: THUAI6.StudentBuffType.NullStudentBuffType, | MessageType.NULL_SBUFF_TYPE: THUAI6.StudentBuffType.NullStudentBuffType, | ||||
| @@ -135,11 +136,11 @@ class Proto2THUAI6(NoInstance): | |||||
| MessageType.ADDICTED: THUAI6.PlayerState.Addicted, | MessageType.ADDICTED: THUAI6.PlayerState.Addicted, | ||||
| MessageType.QUIT: THUAI6.PlayerState.Quit, | MessageType.QUIT: THUAI6.PlayerState.Quit, | ||||
| MessageType.GRADUATED: THUAI6.PlayerState.Graduated, | MessageType.GRADUATED: THUAI6.PlayerState.Graduated, | ||||
| MessageType.RESCUED: THUAI6.PlayerState.Rescued, | |||||
| MessageType.TREATED: THUAI6.PlayerState.Treated, | |||||
| MessageType.RESCUED: THUAI6.PlayerState.Roused, | |||||
| MessageType.TREATED: THUAI6.PlayerState.Encouraged, | |||||
| MessageType.STUNNED: THUAI6.PlayerState.Stunned, | MessageType.STUNNED: THUAI6.PlayerState.Stunned, | ||||
| MessageType.RESCUING: THUAI6.PlayerState.Rescuing, | |||||
| MessageType.TREATING: THUAI6.PlayerState.Treating, | |||||
| MessageType.RESCUING: THUAI6.PlayerState.Rousing, | |||||
| MessageType.TREATING: THUAI6.PlayerState.Encouraging, | |||||
| MessageType.SWINGING: THUAI6.PlayerState.Swinging, | MessageType.SWINGING: THUAI6.PlayerState.Swinging, | ||||
| MessageType.ATTACKING: THUAI6.PlayerState.Attacking, | MessageType.ATTACKING: THUAI6.PlayerState.Attacking, | ||||
| MessageType.LOCKING: THUAI6.PlayerState.Locking, | MessageType.LOCKING: THUAI6.PlayerState.Locking, | ||||
| @@ -206,9 +207,9 @@ class Proto2THUAI6(NoInstance): | |||||
| student.facingDirection = studentMsg.facing_direction | student.facingDirection = studentMsg.facing_direction | ||||
| student.bulletType = Proto2THUAI6.bulletTypeDict[studentMsg.bullet_type] | student.bulletType = Proto2THUAI6.bulletTypeDict[studentMsg.bullet_type] | ||||
| student.learningSpeed = studentMsg.learning_speed | student.learningSpeed = studentMsg.learning_speed | ||||
| student.treatSpeed = studentMsg.treat_speed | |||||
| student.treatProgress = studentMsg.treat_progress | |||||
| student.rescueProgress = studentMsg.rescue_progress | |||||
| student.encourageSpeed = studentMsg.treat_speed | |||||
| student.encourageProgress = studentMsg.treat_progress | |||||
| student.rouseProgress = studentMsg.rescue_progress | |||||
| student.dangerAlert = studentMsg.danger_alert | student.dangerAlert = studentMsg.danger_alert | ||||
| student.timeUntilSkillAvailable.clear() | student.timeUntilSkillAvailable.clear() | ||||
| for time in studentMsg.time_until_skill_available: | for time in studentMsg.time_until_skill_available: | ||||
| @@ -314,7 +315,8 @@ class THUAI62Proto(NoInstance): | |||||
| THUAI6.TrickerType.NullTrickerType: MessageType.NULL_TRICKER_TYPE, | THUAI6.TrickerType.NullTrickerType: MessageType.NULL_TRICKER_TYPE, | ||||
| THUAI6.TrickerType.Assassin: MessageType.ASSASSIN, | THUAI6.TrickerType.Assassin: MessageType.ASSASSIN, | ||||
| THUAI6.TrickerType.Klee: MessageType.KLEE, | THUAI6.TrickerType.Klee: MessageType.KLEE, | ||||
| THUAI6.TrickerType.ANoisyPerson: MessageType.A_NOISY_PERSON, } | |||||
| THUAI6.TrickerType.ANoisyPerson: MessageType.A_NOISY_PERSON, | |||||
| THUAI6.TrickerType.Idol: MessageType.IDOL, } | |||||
| propTypeDict: Final[dict] = { | propTypeDict: Final[dict] = { | ||||
| THUAI6.PropType.NullPropType: MessageType.NULL_PROP_TYPE, | THUAI6.PropType.NullPropType: MessageType.NULL_PROP_TYPE, | ||||
| @@ -1,4 +1,6 @@ | |||||
| #!/usr/bin/env bash | #!/usr/bin/env bash | ||||
| python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 0 -d -o & | python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 0 -d -o & | ||||
| # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 1 -d -o & | |||||
| # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 1 -d & | |||||
| # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 2 -d & | |||||
| # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 3 -d & | |||||
| @@ -118,7 +118,7 @@ enum TrickerType | |||||
| ASSASSIN = 1; | ASSASSIN = 1; | ||||
| KLEE = 2; | KLEE = 2; | ||||
| A_NOISY_PERSON = 3; | A_NOISY_PERSON = 3; | ||||
| TRICKERTYPE4 = 4; | |||||
| IDOL = 4; | |||||
| } | } | ||||
| // 游戏进行状态 | // 游戏进行状态 | ||||
| @@ -3,6 +3,8 @@ using System; | |||||
| using System.Windows.Input; | using System.Windows.Input; | ||||
| using System.Globalization; | using System.Globalization; | ||||
| using System.Windows.Data; | using System.Windows.Data; | ||||
| using System.Windows.Controls; | |||||
| using System.Windows; | |||||
| namespace starter.viewmodel.common | namespace starter.viewmodel.common | ||||
| { | { | ||||
| @@ -98,4 +100,58 @@ namespace starter.viewmodel.common | |||||
| } | } | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Password 附加属性,来自https://blog.csdn.net/qq_43562262/article/details/121786337 | |||||
| /// </summary> | |||||
| public class PasswordHelper | |||||
| { | |||||
| public static readonly DependencyProperty PasswordProperty = DependencyProperty.RegisterAttached("Password", typeof(string), typeof(PasswordHelper), | |||||
| new PropertyMetadata(new PropertyChangedCallback(OnPropertyChanged))); | |||||
| public static string GetPassword(DependencyObject d) | |||||
| { | |||||
| return (string)d.GetValue(PasswordProperty); | |||||
| } | |||||
| public static void SetPassword(DependencyObject d, string value) | |||||
| { | |||||
| d.SetValue(PasswordProperty, value); | |||||
| } | |||||
| public static readonly DependencyProperty AttachProperty = DependencyProperty.RegisterAttached("Attach", typeof(string), typeof(PasswordHelper), | |||||
| new PropertyMetadata(new PropertyChangedCallback(OnAttachChanged))); | |||||
| public static string GetAttach(DependencyObject d) | |||||
| { | |||||
| return (string)d.GetValue(AttachProperty); | |||||
| } | |||||
| public static void SetAttach(DependencyObject d, string value) | |||||
| { | |||||
| d.SetValue(AttachProperty, value); | |||||
| } | |||||
| static bool _isUpdating = false; | |||||
| private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |||||
| { | |||||
| PasswordBox pb = d as PasswordBox; | |||||
| pb.PasswordChanged -= Pb_PasswordChanged; | |||||
| if (!_isUpdating) | |||||
| (d as PasswordBox).Password = e.NewValue.ToString(); | |||||
| pb.PasswordChanged += Pb_PasswordChanged; | |||||
| } | |||||
| private static void OnAttachChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |||||
| { | |||||
| PasswordBox pb = d as PasswordBox; | |||||
| pb.PasswordChanged += Pb_PasswordChanged; | |||||
| } | |||||
| private static void Pb_PasswordChanged(object sender, RoutedEventArgs e) | |||||
| { | |||||
| PasswordBox pb = sender as PasswordBox; | |||||
| _isUpdating = true; | |||||
| SetPassword(pb, pb.Password); | |||||
| _isUpdating = false; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -71,7 +71,7 @@ | |||||
| <TextBlock Grid.Row="1" Grid.Column="0" Text="账号:" Visibility="{Binding LoginVis}" /> | <TextBlock Grid.Row="1" Grid.Column="0" Text="账号:" Visibility="{Binding LoginVis}" /> | ||||
| <TextBlock Grid.Row="3" Grid.Column="0" Text="密码:" Visibility="{Binding LoginVis}" /> | <TextBlock Grid.Row="3" Grid.Column="0" Text="密码:" Visibility="{Binding LoginVis}" /> | ||||
| <TextBox Grid.Row="1" Grid.Column="1" Name="Username" Visibility="{Binding LoginVis}" Text="{Binding Username}" /> | <TextBox Grid.Row="1" Grid.Column="1" Name="Username" Visibility="{Binding LoginVis}" Text="{Binding Username}" /> | ||||
| <TextBox Grid.Row="3" Grid.Column="1" Name="Password" Visibility="{Binding LoginVis}" Text="{Binding Password}" /> | |||||
| <PasswordBox Grid.Row="3" Grid.Column="1" Name="Password" Visibility="{Binding LoginVis}" c:PasswordHelper.Attach="True" c:PasswordHelper.Password="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> | |||||
| <!--<CheckBox Grid.Row="5" Grid.Column="0" Visibility="{Binding LoginVis}">记住我</CheckBox>--> | <!--<CheckBox Grid.Row="5" Grid.Column="0" Visibility="{Binding LoginVis}">记住我</CheckBox>--> | ||||
| <TextBlock Grid.Row="5" Grid.Column="1" Foreground="Red" Text=" 用户名或密码错误!" Visibility="{Binding LoginFailVis}"/> | <TextBlock Grid.Row="5" Grid.Column="1" Foreground="Red" Text=" 用户名或密码错误!" Visibility="{Binding LoginFailVis}"/> | ||||
| </Grid> | </Grid> | ||||
| @@ -124,7 +124,7 @@ namespace starter.viewmodel.settings | |||||
| } | } | ||||
| } | } | ||||
| public async Task<bool> Login() | |||||
| public async Task<int> Login() | |||||
| { | { | ||||
| return await web.LoginToEEsast(client, Username, Password); | return await web.LoginToEEsast(client, Username, Password); | ||||
| } | } | ||||
| @@ -1058,43 +1058,50 @@ namespace WebConnect | |||||
| { | { | ||||
| public enum language { cpp, py }; | public enum language { cpp, py }; | ||||
| public static string logintoken = ""; | public static string logintoken = ""; | ||||
| async public Task<bool> LoginToEEsast(HttpClient client, string useremail, string password) | |||||
| async public Task<int> LoginToEEsast(HttpClient client, string useremail, string password) | |||||
| { | { | ||||
| string token = ""; | string token = ""; | ||||
| using (var response = await client.PostAsync("https://api.eesast.com/users/login", JsonContent.Create(new | |||||
| { | |||||
| email = useremail, | |||||
| password = password, | |||||
| }))) | |||||
| try | |||||
| { | { | ||||
| switch (response.StatusCode) | |||||
| using (var response = await client.PostAsync("https://api.eesast.com/users/login", JsonContent.Create(new | |||||
| { | { | ||||
| case System.Net.HttpStatusCode.OK: | |||||
| Console.WriteLine("Success login"); | |||||
| token = (System.Text.Json.JsonSerializer.Deserialize(await response.Content.ReadAsStreamAsync(), typeof(LoginResponse), new JsonSerializerOptions() | |||||
| { | |||||
| PropertyNamingPolicy = JsonNamingPolicy.CamelCase, | |||||
| }) as LoginResponse) | |||||
| ?.Token ?? | |||||
| throw new Exception("no token!"); | |||||
| logintoken = token; | |||||
| SaveToken(); | |||||
| var info = JsonConvert.DeserializeObject<Dictionary<string, string>>(await response.Content.ReadAsStringAsync()); | |||||
| Downloader.UserInfo._id = info["_id"]; | |||||
| Downloader.UserInfo.email = info["email"]; | |||||
| break; | |||||
| email = useremail, | |||||
| password = password, | |||||
| }))) | |||||
| { | |||||
| switch (response.StatusCode) | |||||
| { | |||||
| case System.Net.HttpStatusCode.OK: | |||||
| Console.WriteLine("Success login"); | |||||
| token = (System.Text.Json.JsonSerializer.Deserialize(await response.Content.ReadAsStreamAsync(), typeof(LoginResponse), new JsonSerializerOptions() | |||||
| { | |||||
| PropertyNamingPolicy = JsonNamingPolicy.CamelCase, | |||||
| }) as LoginResponse) | |||||
| ?.Token ?? | |||||
| throw new Exception("no token!"); | |||||
| logintoken = token; | |||||
| SaveToken(); | |||||
| var info = JsonConvert.DeserializeObject<Dictionary<string, string>>(await response.Content.ReadAsStringAsync()); | |||||
| Downloader.UserInfo._id = info["_id"]; | |||||
| Downloader.UserInfo.email = info["email"]; | |||||
| break; | |||||
| default: | |||||
| int code = ((int)response.StatusCode); | |||||
| Console.WriteLine(code); | |||||
| if (code == 401) | |||||
| { | |||||
| //Console.WriteLine("邮箱或密码错误!"); | |||||
| return false; | |||||
| } | |||||
| break; | |||||
| default: | |||||
| int code = ((int)response.StatusCode); | |||||
| Console.WriteLine(code); | |||||
| if (code == 401) | |||||
| { | |||||
| //Console.WriteLine("邮箱或密码错误!"); | |||||
| return -1; | |||||
| } | |||||
| break; | |||||
| } | |||||
| return 0; | |||||
| } | } | ||||
| return true; | |||||
| } | |||||
| catch | |||||
| { | |||||
| return -2; | |||||
| } | } | ||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -481,15 +481,19 @@ namespace starter.viewmodel.settings | |||||
| { | { | ||||
| clickLoginCommand = new BaseCommand(new Action<object>(async o => | clickLoginCommand = new BaseCommand(new Action<object>(async o => | ||||
| { | { | ||||
| if (!(await obj.Login())) | |||||
| switch (await obj.Login()) | |||||
| { | { | ||||
| obj.LoginFailed = true; | |||||
| } | |||||
| else | |||||
| { | |||||
| obj.LoginFailed = false; | |||||
| Status = SettingsModel.Status.web; | |||||
| this.RaisePropertyChanged("CoverVis"); | |||||
| case -1: | |||||
| obj.LoginFailed = true; | |||||
| break; | |||||
| case 0: | |||||
| obj.LoginFailed = false; | |||||
| Status = SettingsModel.Status.web; | |||||
| this.RaisePropertyChanged("CoverVis"); | |||||
| break; | |||||
| case -2: | |||||
| MessageBox.Show("无法连接服务器,请检查网络情况", "网络错误", MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK); | |||||
| break; | |||||
| } | } | ||||
| this.RaisePropertyChanged("LoginFailVis"); | this.RaisePropertyChanged("LoginFailVis"); | ||||
| })); | })); | ||||
| @@ -400,3 +400,5 @@ FodyWeavers.xsd | |||||
| #THUAI playback file | #THUAI playback file | ||||
| *.thuaipb | *.thuaipb | ||||
| #private cmd | |||||
| cmd/gameServerOfSanford.cmd | |||||
| @@ -36,7 +36,7 @@ namespace Client | |||||
| public string PlaybackFile { get; set; } = DefaultArgumentOptions.FileName; | public string PlaybackFile { get; set; } = DefaultArgumentOptions.FileName; | ||||
| [Option("playbackSpeed", Required = false, HelpText = "The speed of the playback, between 0.25 and 4.0")] | [Option("playbackSpeed", Required = false, HelpText = "The speed of the playback, between 0.25 and 4.0")] | ||||
| public double PlaybackSpeed { get; set; } = 1.0; | |||||
| public double PlaybackSpeed { get; set; } = 2.0; | |||||
| } | } | ||||
| } | } | ||||
| @@ -241,7 +241,7 @@ namespace Client | |||||
| playerMsg.TrickerType = TrickerType.ANoisyPerson; | playerMsg.TrickerType = TrickerType.ANoisyPerson; | ||||
| break; | break; | ||||
| case 4: | case 4: | ||||
| playerMsg.TrickerType = TrickerType._4; | |||||
| playerMsg.TrickerType = TrickerType.Idol; | |||||
| break; | break; | ||||
| case 0: | case 0: | ||||
| default: | default: | ||||
| @@ -413,19 +413,6 @@ namespace Client | |||||
| { | { | ||||
| human = obj.StudentMessage; | human = obj.StudentMessage; | ||||
| } | } | ||||
| if (obj.StudentMessage.PlayerId < GameData.numOfStudent) | |||||
| { | |||||
| IStudentType occupation = (IStudentType)OccupationFactory.FindIOccupation(Transformation.ToStudentType(obj.StudentMessage.StudentType)); | |||||
| totalLife[obj.StudentMessage.PlayerId] = occupation.MaxHp; | |||||
| totalDeath[obj.StudentMessage.PlayerId] = occupation.MaxGamingAddiction; | |||||
| int i = 0; | |||||
| foreach (var skill in occupation.ListOfIActiveSkill) | |||||
| { | |||||
| var iActiveSkill = SkillFactory.FindIActiveSkill(skill); | |||||
| coolTime[i, obj.StudentMessage.PlayerId] = iActiveSkill.SkillCD; | |||||
| ++i; | |||||
| } | |||||
| } | |||||
| listOfHuman.Add(obj.StudentMessage); | listOfHuman.Add(obj.StudentMessage); | ||||
| break; | break; | ||||
| case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | ||||
| @@ -433,14 +420,6 @@ namespace Client | |||||
| { | { | ||||
| butcher = obj.TrickerMessage; | butcher = obj.TrickerMessage; | ||||
| } | } | ||||
| IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerMessage.TrickerType)); | |||||
| int j = 0; | |||||
| foreach (var skill in occupation1.ListOfIActiveSkill) | |||||
| { | |||||
| var iActiveSkill = SkillFactory.FindIActiveSkill(skill); | |||||
| coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD; | |||||
| ++j; | |||||
| } | |||||
| listOfButcher.Add(obj.TrickerMessage); | listOfButcher.Add(obj.TrickerMessage); | ||||
| break; | break; | ||||
| case MessageOfObj.MessageOfObjOneofCase.PropMessage: | case MessageOfObj.MessageOfObjOneofCase.PropMessage: | ||||
| @@ -514,11 +493,15 @@ namespace Client | |||||
| case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: | case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: | ||||
| listOfHiddenGate.Add(obj.HiddenGateMessage); | listOfHiddenGate.Add(obj.HiddenGateMessage); | ||||
| break; | break; | ||||
| case MessageOfObj.MessageOfObjOneofCase.MapMessage: | |||||
| GetMap(obj.MapMessage); | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| listOfAll.Add(content.AllMessage); | listOfAll.Add(content.AllMessage); | ||||
| break; | break; | ||||
| case GameState.GameEnd: | case GameState.GameEnd: | ||||
| MessageBox.Show("Game Over!"); | |||||
| foreach (var obj in content.ObjMessage) | foreach (var obj in content.ObjMessage) | ||||
| { | { | ||||
| switch (obj.MessageOfObjCase) | switch (obj.MessageOfObjCase) | ||||
| @@ -581,23 +564,16 @@ namespace Client | |||||
| return true; | return true; | ||||
| if (humanOrButcher && human != null) | if (humanOrButcher && human != null) | ||||
| { | { | ||||
| if (human.Guid == msg.Guid) // 自己能看见自己 | |||||
| if (msg.Place == human.Place) | |||||
| return true; | return true; | ||||
| } | } | ||||
| if (msg.Place == Protobuf.PlaceType.Grass || msg.Place == Protobuf.PlaceType.Gate || msg.Place == Protobuf.PlaceType.HiddenGate) | |||||
| return false; | |||||
| if (msg.Place == Protobuf.PlaceType.Land || msg.Place == Protobuf.PlaceType.Classroom) | |||||
| return true; | |||||
| if (humanOrButcher && human != null) | |||||
| { | |||||
| if (msg.Place != human.Place) | |||||
| return false; | |||||
| } | |||||
| else if (!humanOrButcher && butcher != null) | else if (!humanOrButcher && butcher != null) | ||||
| { | { | ||||
| if (msg.Place != butcher.Place) | |||||
| return false; | |||||
| if (msg.Place == butcher.Place) | |||||
| return true; | |||||
| } | } | ||||
| if (msg.Place == Protobuf.PlaceType.Grass) | |||||
| return false; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -610,20 +586,21 @@ namespace Client | |||||
| if (butcher.Guid == msg.Guid) // 自己能看见自己 | if (butcher.Guid == msg.Guid) // 自己能看见自己 | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (msg.Place == Protobuf.PlaceType.Grass || msg.Place == Protobuf.PlaceType.Gate || msg.Place == Protobuf.PlaceType.HiddenGate) | |||||
| return false; | |||||
| if (msg.Place == Protobuf.PlaceType.Land || msg.Place == Protobuf.PlaceType.Classroom) | |||||
| return true; | |||||
| if (humanOrButcher && human != null) | if (humanOrButcher && human != null) | ||||
| { | { | ||||
| if (msg.Place != human.Place) | |||||
| return false; | |||||
| } | |||||
| else if (!humanOrButcher && butcher != null) | |||||
| { | |||||
| if (msg.Place != butcher.Place) | |||||
| return false; | |||||
| if (msg.TrickerType == Protobuf.TrickerType.Assassin) | |||||
| { | |||||
| foreach (var buff in msg.Buff) | |||||
| { | |||||
| if (buff == Protobuf.TrickerBuffType.TrickerInvisible) | |||||
| return false; | |||||
| } | |||||
| } | |||||
| if (msg.Place == human.Place) | |||||
| return true; | |||||
| } | } | ||||
| if (msg.Place == Protobuf.PlaceType.Grass) | |||||
| return false; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -631,18 +608,18 @@ namespace Client | |||||
| { | { | ||||
| if (isSpectatorMode) | if (isSpectatorMode) | ||||
| return true; | return true; | ||||
| if (msg.Place == Protobuf.PlaceType.Land) | |||||
| return true; | |||||
| if (humanOrButcher && human != null) | if (humanOrButcher && human != null) | ||||
| { | { | ||||
| if (msg.Place != human.Place) | |||||
| return false; | |||||
| if (msg.Place == human.Place) | |||||
| return true; | |||||
| } | } | ||||
| else if (!humanOrButcher && butcher != null) | else if (!humanOrButcher && butcher != null) | ||||
| { | { | ||||
| if (msg.Place != butcher.Place) | |||||
| return false; | |||||
| if (msg.Place == butcher.Place) | |||||
| return true; | |||||
| } | } | ||||
| if (msg.Place == Protobuf.PlaceType.Grass) | |||||
| return false; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -650,70 +627,117 @@ namespace Client | |||||
| { | { | ||||
| if (isSpectatorMode) | if (isSpectatorMode) | ||||
| return true; | return true; | ||||
| if (msg.Place == Protobuf.PlaceType.Land) | |||||
| return true; | |||||
| if (humanOrButcher && human != null) | if (humanOrButcher && human != null) | ||||
| { | { | ||||
| if (msg.Place != human.Place) | |||||
| return false; | |||||
| if (msg.Place == human.Place) | |||||
| return true; | |||||
| } | } | ||||
| else if (!humanOrButcher && butcher != null) | else if (!humanOrButcher && butcher != null) | ||||
| { | { | ||||
| if (msg.Place != butcher.Place) | |||||
| return false; | |||||
| if (msg.Place == butcher.Place) | |||||
| return true; | |||||
| } | } | ||||
| if (msg.Place == Protobuf.PlaceType.Grass) | |||||
| return false; | |||||
| return true; | |||||
| } | |||||
| private bool CanSee(MessageOfBombedBullet msg) | |||||
| { | |||||
| if (isSpectatorMode) | |||||
| return true; | |||||
| //if (humanOrButcher && human != null) | |||||
| //{ | |||||
| // if (msg.Place == human.Place) | |||||
| // return true; | |||||
| //} | |||||
| //else if (!humanOrButcher && butcher != null) | |||||
| //{ | |||||
| // if (msg.Place == butcher.Place) | |||||
| // return true; | |||||
| //} | |||||
| //if (msg.Place == Protobuf.PlaceType.Grass) | |||||
| // return false; | |||||
| return true; | return true; | ||||
| } | } | ||||
| private void Refresh(object? sender, EventArgs e) //log未更新 | private void Refresh(object? sender, EventArgs e) //log未更新 | ||||
| { | { | ||||
| // Bonus(); | |||||
| if (WindowState == WindowState.Maximized) | |||||
| MaxButton.Content = "❐"; | |||||
| else | |||||
| MaxButton.Content = "🗖"; | |||||
| if (StatusBarsOfSurvivor != null) | |||||
| lock (drawPicLock) // 加锁是必要的,画图操作和接收信息操作不能同时进行 | |||||
| { | { | ||||
| for (int i = 0; i < GameData.numOfStudent; i++) | |||||
| // Bonus(); | |||||
| if (WindowState == WindowState.Maximized) | |||||
| MaxButton.Content = "❐"; | |||||
| else | |||||
| MaxButton.Content = "🗖"; | |||||
| foreach (var obj in listOfHuman) | |||||
| { | { | ||||
| StatusBarsOfSurvivor[i].SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); | |||||
| StatusBarsOfSurvivor[i].NewData(totalLife, totalDeath, coolTime); | |||||
| if (obj.PlayerId < GameData.numOfStudent) | |||||
| { | |||||
| IStudentType occupation = (IStudentType)OccupationFactory.FindIOccupation(Transformation.ToStudentType(obj.StudentType)); | |||||
| totalLife[obj.PlayerId] = occupation.MaxHp; | |||||
| totalDeath[obj.PlayerId] = occupation.MaxGamingAddiction; | |||||
| int i = 0; | |||||
| foreach (var skill in occupation.ListOfIActiveSkill) | |||||
| { | |||||
| var iActiveSkill = SkillFactory.FindIActiveSkill(skill); | |||||
| coolTime[i, obj.PlayerId] = iActiveSkill.SkillCD; | |||||
| ++i; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | |||||
| if (StatusBarsOfHunter != null) | |||||
| { | |||||
| StatusBarsOfHunter.SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); | |||||
| StatusBarsOfHunter.NewData(totalLife, totalDeath, coolTime); | |||||
| } | |||||
| if (StatusBarsOfCircumstance != null) | |||||
| StatusBarsOfCircumstance.SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); | |||||
| // 完成窗口信息更新 | |||||
| if (!isClientStocked) | |||||
| { | |||||
| unit = Math.Sqrt(UpperLayerOfMap.ActualHeight * UpperLayerOfMap.ActualWidth) / 50; | |||||
| unitHeight = UpperLayerOfMap.ActualHeight / 50; | |||||
| unitWidth = UpperLayerOfMap.ActualWidth / 50; | |||||
| try | |||||
| foreach (var obj in listOfButcher) | |||||
| { | |||||
| IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType)); | |||||
| int j = 0; | |||||
| foreach (var skill in occupation1.ListOfIActiveSkill) | |||||
| { | |||||
| var iActiveSkill = SkillFactory.FindIActiveSkill(skill); | |||||
| coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD; | |||||
| ++j; | |||||
| } | |||||
| } | |||||
| if (StatusBarsOfSurvivor != null) | |||||
| { | |||||
| for (int i = 0; i < GameData.numOfStudent; i++) | |||||
| { | |||||
| StatusBarsOfSurvivor[i].SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); | |||||
| StatusBarsOfSurvivor[i].NewData(totalLife, totalDeath, coolTime); | |||||
| } | |||||
| } | |||||
| if (StatusBarsOfHunter != null) | |||||
| { | { | ||||
| // if (log != null) | |||||
| //{ | |||||
| // string temp = ""; | |||||
| // for (int i = 0; i < dataDict[GameObjType.Character].Count; i++) | |||||
| // { | |||||
| // temp += Convert.ToString(dataDict[GameObjType.Character][i].MessageOfCharacter.TeamID) + "\n"; | |||||
| // } | |||||
| // log.Content = temp; | |||||
| // } | |||||
| UpperLayerOfMap.Children.Clear(); | |||||
| // if ((communicator == null || !communicator.Client.IsConnected) && !isPlaybackMode) | |||||
| //{ | |||||
| // UnderLayerOfMap.Children.Clear(); | |||||
| // throw new Exception("Client is unconnected."); | |||||
| // } | |||||
| // else | |||||
| //{ | |||||
| lock (drawPicLock) // 加锁是必要的,画图操作和接收信息操作不能同时进行 | |||||
| StatusBarsOfHunter.SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); | |||||
| StatusBarsOfHunter.NewData(totalLife, totalDeath, coolTime); | |||||
| } | |||||
| if (StatusBarsOfCircumstance != null) | |||||
| StatusBarsOfCircumstance.SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); | |||||
| // 完成窗口信息更新 | |||||
| if (!isClientStocked) | |||||
| { | |||||
| unit = Math.Sqrt(UpperLayerOfMap.ActualHeight * UpperLayerOfMap.ActualWidth) / 50; | |||||
| unitHeight = UpperLayerOfMap.ActualHeight / 50; | |||||
| unitWidth = UpperLayerOfMap.ActualWidth / 50; | |||||
| try | |||||
| { | { | ||||
| // if (log != null) | |||||
| //{ | |||||
| // string temp = ""; | |||||
| // for (int i = 0; i < dataDict[GameObjType.Character].Count; i++) | |||||
| // { | |||||
| // temp += Convert.ToString(dataDict[GameObjType.Character][i].MessageOfCharacter.TeamID) + "\n"; | |||||
| // } | |||||
| // log.Content = temp; | |||||
| // } | |||||
| UpperLayerOfMap.Children.Clear(); | |||||
| // if ((communicator == null || !communicator.Client.IsConnected) && !isPlaybackMode) | |||||
| //{ | |||||
| // UnderLayerOfMap.Children.Clear(); | |||||
| // throw new Exception("Client is unconnected."); | |||||
| // } | |||||
| // else | |||||
| //{ | |||||
| foreach (var data in listOfAll) | foreach (var data in listOfAll) | ||||
| { | { | ||||
| StatusBarsOfCircumstance.SetValue(data, gateOpened, isEmergencyDrawed, isEmergencyOpened, playerID); | StatusBarsOfCircumstance.SetValue(data, gateOpened, isEmergencyDrawed, isEmergencyOpened, playerID); | ||||
| @@ -734,7 +758,22 @@ namespace Client | |||||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * radiusTimes, data.X * unitHeight / 1000.0 - unitHeight * radiusTimes, 0, 0), | Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * radiusTimes, data.X * unitHeight / 1000.0 - unitHeight * radiusTimes, 0, 0), | ||||
| Fill = Brushes.BlueViolet, | Fill = Brushes.BlueViolet, | ||||
| }; | }; | ||||
| TextBox num = new() | |||||
| { | |||||
| FontSize = 7 * UpperLayerOfMap.ActualHeight / 650, | |||||
| Width = 2 * radiusTimes * unitWidth, | |||||
| Height = 2 * radiusTimes * unitHeight, | |||||
| Text = Convert.ToString(data.PlayerId), | |||||
| HorizontalAlignment = HorizontalAlignment.Left, | |||||
| VerticalAlignment = VerticalAlignment.Top, | |||||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * radiusTimes, data.X * unitHeight / 1000.0 - unitHeight * radiusTimes, 0, 0), | |||||
| Background = Brushes.Transparent, | |||||
| BorderBrush = Brushes.Transparent, | |||||
| IsReadOnly = true, | |||||
| Foreground = Brushes.White, | |||||
| }; | |||||
| UpperLayerOfMap.Children.Add(icon); | UpperLayerOfMap.Children.Add(icon); | ||||
| UpperLayerOfMap.Children.Add(num); | |||||
| } | } | ||||
| } | } | ||||
| foreach (var data in listOfButcher) | foreach (var data in listOfButcher) | ||||
| @@ -796,11 +835,11 @@ namespace Client | |||||
| { | { | ||||
| Ellipse icon = new() | Ellipse icon = new() | ||||
| { | { | ||||
| Width = 10, | |||||
| Height = 10, | |||||
| Width = 2 * bulletRadiusTimes * unitWidth, | |||||
| Height = 2 * bulletRadiusTimes * unitHeight, | |||||
| HorizontalAlignment = HorizontalAlignment.Left, | HorizontalAlignment = HorizontalAlignment.Left, | ||||
| VerticalAlignment = VerticalAlignment.Top, | VerticalAlignment = VerticalAlignment.Top, | ||||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth / 2, data.X * unitHeight / 1000.0 - unitHeight / 2, 0, 0), | |||||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * bulletRadiusTimes, data.X * unitHeight / 1000.0 - unitHeight * bulletRadiusTimes, 0, 0), | |||||
| Fill = Brushes.Red, | Fill = Brushes.Red, | ||||
| }; | }; | ||||
| switch (data.Type) | switch (data.Type) | ||||
| @@ -821,42 +860,49 @@ namespace Client | |||||
| } | } | ||||
| foreach (var data in listOfBombedBullet) | foreach (var data in listOfBombedBullet) | ||||
| { | { | ||||
| switch (data.Type) | |||||
| if (CanSee(data)) | |||||
| { | { | ||||
| case Protobuf.BulletType.BombBomb: | |||||
| { | |||||
| Ellipse icon = new(); | |||||
| double bombRange = data.BombRange / 1000; | |||||
| icon.Width = bombRange * unitWidth; | |||||
| icon.Height = bombRange * unitHeight; | |||||
| icon.HorizontalAlignment = HorizontalAlignment.Left; | |||||
| icon.VerticalAlignment = VerticalAlignment.Top; | |||||
| icon.Margin = new Thickness(data.Y * unitWidth / 1000.0 - bombRange * unitWidth / 2, data.X * unitHeight / 1000.0 - bombRange * unitHeight / 2, 0, 0); | |||||
| icon.Fill = Brushes.Red; | |||||
| UpperLayerOfMap.Children.Add(icon); | |||||
| break; | |||||
| } | |||||
| case Protobuf.BulletType.JumpyDumpty: | |||||
| { | |||||
| Ellipse icon = new Ellipse(); | |||||
| double bombRange = data.BombRange / 1000; | |||||
| icon.Width = bombRange * unitWidth; | |||||
| icon.Height = bombRange * unitHeight; | |||||
| icon.HorizontalAlignment = HorizontalAlignment.Left; | |||||
| icon.VerticalAlignment = VerticalAlignment.Top; | |||||
| icon.Margin = new Thickness(data.Y * unitWidth / 1000.0 - bombRange * unitWidth / 2, data.X * unitHeight / 1000.0 - bombRange * unitHeight / 2, 0, 0); | |||||
| icon.Fill = Brushes.Red; | |||||
| UpperLayerOfMap.Children.Add(icon); | |||||
| switch (data.Type) | |||||
| { | |||||
| case Protobuf.BulletType.BombBomb: | |||||
| { | |||||
| double bombRange = 1.0 * data.BombRange / Preparation.Utility.GameData.numOfPosGridPerCell; | |||||
| Ellipse icon = new() | |||||
| { | |||||
| Width = 2 * bombRange * unitWidth, | |||||
| Height = 2 * bombRange * unitHeight, | |||||
| HorizontalAlignment = HorizontalAlignment.Left, | |||||
| VerticalAlignment = VerticalAlignment.Top, | |||||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * bombRange, data.X * unitHeight / 1000.0 - unitHeight * bombRange, 0, 0), | |||||
| Fill = Brushes.DarkRed, | |||||
| }; | |||||
| UpperLayerOfMap.Children.Add(icon); | |||||
| break; | |||||
| } | |||||
| case Protobuf.BulletType.JumpyDumpty: | |||||
| { | |||||
| double bombRange = 1.0 * data.BombRange / Preparation.Utility.GameData.numOfPosGridPerCell; | |||||
| Ellipse icon = new() | |||||
| { | |||||
| Width = 2 * bombRange * unitWidth, | |||||
| Height = 2 * bombRange * unitHeight, | |||||
| HorizontalAlignment = HorizontalAlignment.Left, | |||||
| VerticalAlignment = VerticalAlignment.Top, | |||||
| Margin = new Thickness(data.Y * unitWidth / 1000.0 - unitWidth * bombRange, data.X * unitHeight / 1000.0 - unitHeight * bombRange, 0, 0), | |||||
| Fill = Brushes.DarkRed, | |||||
| }; | |||||
| UpperLayerOfMap.Children.Add(icon); | |||||
| break; | |||||
| } | |||||
| //case Protobuf.BulletType.LineBullet: | |||||
| // { | |||||
| // double bombRange = data.BombRange / 1000; | |||||
| // DrawLaser(new Point(data.Y * unitWidth / 1000.0, data.X * unitHeight / 1000.0), -data.FacingDirection + Math.PI / 2, bombRange * unitHeight, 0.5 * unitWidth); | |||||
| // break; | |||||
| // } | |||||
| default: | |||||
| break; | break; | ||||
| } | |||||
| //case Protobuf.BulletType.LineBullet: | |||||
| // { | |||||
| // double bombRange = data.BombRange / 1000; | |||||
| // DrawLaser(new Point(data.Y * unitWidth / 1000.0, data.X * unitHeight / 1000.0), -data.FacingDirection + Math.PI / 2, bombRange * unitHeight, 0.5 * unitWidth); | |||||
| // break; | |||||
| // } | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| foreach (var data in listOfClassroom) | foreach (var data in listOfClassroom) | ||||
| @@ -978,17 +1024,18 @@ namespace Client | |||||
| } | } | ||||
| //} | //} | ||||
| ZoomMap(); | ZoomMap(); | ||||
| } | |||||
| catch (Exception exc) | |||||
| { | |||||
| ErrorDisplayer error = new("Error: " + exc.ToString()); | |||||
| error.Show(); | |||||
| isClientStocked = true; | |||||
| PorC.Content = "▶"; | |||||
| } | } | ||||
| } | } | ||||
| catch (Exception exc) | |||||
| { | |||||
| ErrorDisplayer error = new("Error: " + exc.ToString()); | |||||
| error.Show(); | |||||
| isClientStocked = true; | |||||
| PorC.Content = "▶"; | |||||
| } | |||||
| counter++; | |||||
| } | } | ||||
| counter++; | |||||
| } | } | ||||
| // 键盘控制,未完善 | // 键盘控制,未完善 | ||||
| @@ -1210,10 +1257,18 @@ namespace Client | |||||
| isClientStocked = true; | isClientStocked = true; | ||||
| PorC.Content = "▶"; | PorC.Content = "▶"; | ||||
| } | } | ||||
| else | |||||
| else if (!isPlaybackMode) | |||||
| { | { | ||||
| isClientStocked = false; | |||||
| PorC.Content = "⏸"; | |||||
| try | |||||
| { | |||||
| isClientStocked = false; | |||||
| PorC.Content = "⏸"; | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| ErrorDisplayer error = new("发生错误。以下是系统报告:\n" + ex.ToString()); | |||||
| error.Show(); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| // 未复现 | // 未复现 | ||||
| @@ -1407,7 +1462,9 @@ namespace Client | |||||
| bool isSpectatorMode = false; | bool isSpectatorMode = false; | ||||
| bool isEmergencyOpened = false; | bool isEmergencyOpened = false; | ||||
| bool isEmergencyDrawed = false; | bool isEmergencyDrawed = false; | ||||
| bool isDataFixed = false; | |||||
| const double radiusTimes = 1.0 * Preparation.Utility.GameData.characterRadius / Preparation.Utility.GameData.numOfPosGridPerCell; | const double radiusTimes = 1.0 * Preparation.Utility.GameData.characterRadius / Preparation.Utility.GameData.numOfPosGridPerCell; | ||||
| const double bulletRadiusTimes = 1.0 * GameData.bulletRadius / Preparation.Utility.GameData.numOfPosGridPerCell; | |||||
| private int[] totalLife = new int[4] { 100, 100, 100, 100 }, totalDeath = new int[4] { 100, 100, 100, 100 }; | private int[] totalLife = new int[4] { 100, 100, 100, 100 }, totalDeath = new int[4] { 100, 100, 100, 100 }; | ||||
| private int[,] coolTime = new int[3, 5] { { 100, 100, 100, 100, 100 }, { 100, 100, 100, 100, 100 }, { 100, 100, 100, 100, 100 } }; | private int[,] coolTime = new int[3, 5] { { 100, 100, 100, 100, 100 }, { 100, 100, 100, 100, 100 }, { 100, 100, 100, 100, 100 } }; | ||||
| } | } | ||||
| @@ -7,6 +7,7 @@ using Protobuf; | |||||
| using Playback; | using Playback; | ||||
| using System.Threading; | using System.Threading; | ||||
| using Timothy.FrameRateTask; | using Timothy.FrameRateTask; | ||||
| using System.Windows; | |||||
| namespace Client | namespace Client | ||||
| { | { | ||||
| @@ -79,183 +80,194 @@ namespace Client | |||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| new Thread(() => | new Thread(() => | ||||
| { | { | ||||
| new FrameRateTaskExecutor<int> | |||||
| ( | |||||
| () => !endFile, | |||||
| () => | |||||
| { | |||||
| var content = Reader.ReadOne(); | |||||
| if (content == null) | |||||
| endFile = true; | |||||
| else | |||||
| int i = 0; | |||||
| try | |||||
| { | |||||
| new FrameRateTaskExecutor<int> | |||||
| ( | |||||
| () => !endFile, | |||||
| () => | |||||
| { | { | ||||
| lock (dataLock) | |||||
| var content = Reader.ReadOne(); | |||||
| i++; | |||||
| if (content == null) | |||||
| { | |||||
| MessageBox.Show($"End! {i}"); | |||||
| endFile = true; | |||||
| } | |||||
| else | |||||
| { | { | ||||
| listOfHuman.Clear(); | |||||
| listOfButcher.Clear(); | |||||
| listOfProp.Clear(); | |||||
| listOfBombedBullet.Clear(); | |||||
| listOfBullet.Clear(); | |||||
| listOfAll.Clear(); | |||||
| listOfChest.Clear(); | |||||
| listOfClassroom.Clear(); | |||||
| listOfDoor.Clear(); | |||||
| listOfHiddenGate.Clear(); | |||||
| listOfGate.Clear(); | |||||
| switch (content.GameState) | |||||
| lock (dataLock) | |||||
| { | { | ||||
| case GameState.GameStart: | |||||
| foreach (var obj in content.ObjMessage) | |||||
| { | |||||
| switch (obj.MessageOfObjCase) | |||||
| listOfHuman.Clear(); | |||||
| listOfButcher.Clear(); | |||||
| listOfProp.Clear(); | |||||
| listOfBombedBullet.Clear(); | |||||
| listOfBullet.Clear(); | |||||
| listOfAll.Clear(); | |||||
| listOfChest.Clear(); | |||||
| listOfClassroom.Clear(); | |||||
| listOfDoor.Clear(); | |||||
| listOfHiddenGate.Clear(); | |||||
| listOfGate.Clear(); | |||||
| switch (content.GameState) | |||||
| { | |||||
| case GameState.GameStart: | |||||
| foreach (var obj in content.ObjMessage) | |||||
| { | { | ||||
| case MessageOfObj.MessageOfObjOneofCase.StudentMessage: | |||||
| //if (humanOrButcher && obj.StudentMessage.PlayerId == playerID) | |||||
| //{ | |||||
| // human = obj.StudentMessage; | |||||
| //} | |||||
| listOfHuman.Add(obj.StudentMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | |||||
| //if (!humanOrButcher && obj.TrickerMessage.PlayerId == playerID) | |||||
| //{ | |||||
| // butcher = obj.TrickerMessage; | |||||
| //} | |||||
| listOfButcher.Add(obj.TrickerMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.PropMessage: | |||||
| listOfProp.Add(obj.PropMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BombedBulletMessage: | |||||
| listOfBombedBullet.Add(obj.BombedBulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BulletMessage: | |||||
| listOfBullet.Add(obj.BulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ChestMessage: | |||||
| listOfChest.Add(obj.ChestMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ClassroomMessage: | |||||
| listOfClassroom.Add(obj.ClassroomMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.DoorMessage: | |||||
| listOfDoor.Add(obj.DoorMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.GateMessage: | |||||
| listOfGate.Add(obj.GateMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: | |||||
| listOfHiddenGate.Add(obj.HiddenGateMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.MapMessage: | |||||
| break; | |||||
| switch (obj.MessageOfObjCase) | |||||
| { | |||||
| case MessageOfObj.MessageOfObjOneofCase.StudentMessage: | |||||
| //if (humanOrButcher && obj.StudentMessage.PlayerId == playerID) | |||||
| //{ | |||||
| // human = obj.StudentMessage; | |||||
| //} | |||||
| listOfHuman.Add(obj.StudentMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | |||||
| //if (!humanOrButcher && obj.TrickerMessage.PlayerId == playerID) | |||||
| //{ | |||||
| // butcher = obj.TrickerMessage; | |||||
| //} | |||||
| listOfButcher.Add(obj.TrickerMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.PropMessage: | |||||
| listOfProp.Add(obj.PropMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BombedBulletMessage: | |||||
| listOfBombedBullet.Add(obj.BombedBulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BulletMessage: | |||||
| listOfBullet.Add(obj.BulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ChestMessage: | |||||
| listOfChest.Add(obj.ChestMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ClassroomMessage: | |||||
| listOfClassroom.Add(obj.ClassroomMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.DoorMessage: | |||||
| listOfDoor.Add(obj.DoorMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.GateMessage: | |||||
| listOfGate.Add(obj.GateMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: | |||||
| listOfHiddenGate.Add(obj.HiddenGateMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.MapMessage: | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | |||||
| listOfAll.Add(content.AllMessage); | |||||
| break; | |||||
| case GameState.GameRunning: | |||||
| foreach (var obj in content.ObjMessage) | |||||
| { | |||||
| switch (obj.MessageOfObjCase) | |||||
| listOfAll.Add(content.AllMessage); | |||||
| break; | |||||
| case GameState.GameRunning: | |||||
| foreach (var obj in content.ObjMessage) | |||||
| { | { | ||||
| case MessageOfObj.MessageOfObjOneofCase.StudentMessage: | |||||
| //if (humanOrButcher && obj.StudentMessage.PlayerId == playerID) | |||||
| //{ | |||||
| // human = obj.StudentMessage; | |||||
| //} | |||||
| listOfHuman.Add(obj.StudentMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | |||||
| //if (!humanOrButcher && obj.TrickerMessage.PlayerId == playerID) | |||||
| //{ | |||||
| // butcher = obj.TrickerMessage; | |||||
| //} | |||||
| listOfButcher.Add(obj.TrickerMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.PropMessage: | |||||
| listOfProp.Add(obj.PropMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BombedBulletMessage: | |||||
| listOfBombedBullet.Add(obj.BombedBulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BulletMessage: | |||||
| listOfBullet.Add(obj.BulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ChestMessage: | |||||
| listOfChest.Add(obj.ChestMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ClassroomMessage: | |||||
| listOfClassroom.Add(obj.ClassroomMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.DoorMessage: | |||||
| listOfDoor.Add(obj.DoorMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: | |||||
| listOfHiddenGate.Add(obj.HiddenGateMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.GateMessage: | |||||
| listOfGate.Add(obj.GateMessage); | |||||
| break; | |||||
| switch (obj.MessageOfObjCase) | |||||
| { | |||||
| case MessageOfObj.MessageOfObjOneofCase.StudentMessage: | |||||
| //if (humanOrButcher && obj.StudentMessage.PlayerId == playerID) | |||||
| //{ | |||||
| // human = obj.StudentMessage; | |||||
| //} | |||||
| listOfHuman.Add(obj.StudentMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | |||||
| //if (!humanOrButcher && obj.TrickerMessage.PlayerId == playerID) | |||||
| //{ | |||||
| // butcher = obj.TrickerMessage; | |||||
| //} | |||||
| listOfButcher.Add(obj.TrickerMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.PropMessage: | |||||
| listOfProp.Add(obj.PropMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BombedBulletMessage: | |||||
| listOfBombedBullet.Add(obj.BombedBulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BulletMessage: | |||||
| listOfBullet.Add(obj.BulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ChestMessage: | |||||
| listOfChest.Add(obj.ChestMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ClassroomMessage: | |||||
| listOfClassroom.Add(obj.ClassroomMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.DoorMessage: | |||||
| listOfDoor.Add(obj.DoorMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: | |||||
| listOfHiddenGate.Add(obj.HiddenGateMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.GateMessage: | |||||
| listOfGate.Add(obj.GateMessage); | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | |||||
| listOfAll.Add(content.AllMessage); | |||||
| break; | |||||
| listOfAll.Add(content.AllMessage); | |||||
| break; | |||||
| case GameState.GameEnd: | |||||
| foreach (var obj in content.ObjMessage) | |||||
| { | |||||
| switch (obj.MessageOfObjCase) | |||||
| case GameState.GameEnd: | |||||
| MessageBox.Show("Game Over!"); | |||||
| foreach (var obj in content.ObjMessage) | |||||
| { | { | ||||
| case MessageOfObj.MessageOfObjOneofCase.StudentMessage: | |||||
| listOfHuman.Add(obj.StudentMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | |||||
| listOfButcher.Add(obj.TrickerMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.PropMessage: | |||||
| listOfProp.Add(obj.PropMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BombedBulletMessage: | |||||
| listOfBombedBullet.Add(obj.BombedBulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BulletMessage: | |||||
| listOfBullet.Add(obj.BulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ChestMessage: | |||||
| listOfChest.Add(obj.ChestMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ClassroomMessage: | |||||
| listOfClassroom.Add(obj.ClassroomMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.DoorMessage: | |||||
| listOfDoor.Add(obj.DoorMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: | |||||
| listOfHiddenGate.Add(obj.HiddenGateMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.GateMessage: | |||||
| listOfGate.Add(obj.GateMessage); | |||||
| break; | |||||
| switch (obj.MessageOfObjCase) | |||||
| { | |||||
| case MessageOfObj.MessageOfObjOneofCase.StudentMessage: | |||||
| listOfHuman.Add(obj.StudentMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: | |||||
| listOfButcher.Add(obj.TrickerMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.PropMessage: | |||||
| listOfProp.Add(obj.PropMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BombedBulletMessage: | |||||
| listOfBombedBullet.Add(obj.BombedBulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.BulletMessage: | |||||
| listOfBullet.Add(obj.BulletMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ChestMessage: | |||||
| listOfChest.Add(obj.ChestMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.ClassroomMessage: | |||||
| listOfClassroom.Add(obj.ClassroomMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.DoorMessage: | |||||
| listOfDoor.Add(obj.DoorMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: | |||||
| listOfHiddenGate.Add(obj.HiddenGateMessage); | |||||
| break; | |||||
| case MessageOfObj.MessageOfObjOneofCase.GateMessage: | |||||
| listOfGate.Add(obj.GateMessage); | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | |||||
| listOfAll.Add(content.AllMessage); | |||||
| break; | |||||
| listOfAll.Add(content.AllMessage); | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| }, | |||||
| frame, | |||||
| () => | |||||
| }, | |||||
| frame, | |||||
| () => | |||||
| { | |||||
| Sema.Release(); | |||||
| return 1; | |||||
| } | |||||
| ) | |||||
| { AllowTimeExceed = true }.Start(); | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | { | ||||
| Sema.Release(); | |||||
| //MessageBox.Show("Game Over!"); | |||||
| return 1; | |||||
| MessageBox.Show(ex.Message); | |||||
| } | } | ||||
| ) | |||||
| { AllowTimeExceed = true }.Start(); | |||||
| }) | }) | ||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| return map; | return map; | ||||
| @@ -2,7 +2,7 @@ | |||||
| "profiles": { | "profiles": { | ||||
| "Client": { | "Client": { | ||||
| "commandName": "Project", | "commandName": "Project", | ||||
| "commandLineArgs": "--ip 127.0.0.1 --cl --port 8888 --characterID 4 --type 2 --occupation 2" | |||||
| "commandLineArgs": "--cl --port 8888 --characterID 2031" | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -12,6 +12,7 @@ using System.Windows.Media; | |||||
| using System.Windows.Media.Imaging; | using System.Windows.Media.Imaging; | ||||
| using System.Windows.Shapes; | using System.Windows.Shapes; | ||||
| using Protobuf; | using Protobuf; | ||||
| using Preparation.Utility; | |||||
| namespace Client | namespace Client | ||||
| { | { | ||||
| @@ -31,10 +32,13 @@ namespace Client | |||||
| } | } | ||||
| public void SetFontSize(double fontsize) | public void SetFontSize(double fontsize) | ||||
| { | { | ||||
| status.FontSize = 13 * fontsize / 12; | |||||
| time.FontSize = 14 * fontsize / 12; | |||||
| name.FontSize = 14 * fontsize / 12; | |||||
| scoresOfStudents.FontSize = scoresOfTrickers.FontSize = fontsize; | |||||
| if (fontsize != 0) | |||||
| { | |||||
| status.FontSize = 13 * fontsize / 12; | |||||
| time.FontSize = 14 * fontsize / 12; | |||||
| name.FontSize = 14 * fontsize / 12; | |||||
| scoresOfStudents.FontSize = scoresOfTrickers.FontSize = fontsize; | |||||
| } | |||||
| } | } | ||||
| public void SetValue(MessageOfAll obj, bool gateOpened, bool hiddenGateRefreshed, bool hiddenGateOpened, long playerId) | public void SetValue(MessageOfAll obj, bool gateOpened, bool hiddenGateRefreshed, bool hiddenGateOpened, long playerId) | ||||
| @@ -53,14 +57,18 @@ namespace Client | |||||
| { | { | ||||
| time.Text += Convert.ToString(sec); | time.Text += Convert.ToString(sec); | ||||
| } | } | ||||
| if (playerId == 4) | |||||
| if (playerId == GameData.numOfStudent) | |||||
| { | { | ||||
| name.Text = "🚀 Tricker's"; | name.Text = "🚀 Tricker's"; | ||||
| } | } | ||||
| else | |||||
| else if (playerId < GameData.numOfStudent) | |||||
| { | { | ||||
| name.Text = "🚀 Student" + Convert.ToString(playerId) + "'s"; | name.Text = "🚀 Student" + Convert.ToString(playerId) + "'s"; | ||||
| } | } | ||||
| else | |||||
| { | |||||
| name.Text = "🚀 Spectator's"; | |||||
| } | |||||
| if (obj.SubjectFinished < Preparation.Utility.GameData.numOfGeneratorRequiredForRepair) | if (obj.SubjectFinished < Preparation.Utility.GameData.numOfGeneratorRequiredForRepair) | ||||
| { | { | ||||
| status.Text = "📱: " + Convert.ToString(obj.SubjectFinished) + "\n🚪: "; | status.Text = "📱: " + Convert.ToString(obj.SubjectFinished) + "\n🚪: "; | ||||
| @@ -32,7 +32,8 @@ namespace Client | |||||
| } | } | ||||
| public void SetFontSize(double fontsize) | public void SetFontSize(double fontsize) | ||||
| { | { | ||||
| serial.FontSize = scores.FontSize = state.FontSize = status.FontSize = activeSkill0.FontSize = activeSkill1.FontSize = activeSkill2.FontSize = prop0.FontSize = prop1.FontSize = prop2.FontSize = fontsize; | |||||
| if (fontsize != 0) | |||||
| serial.FontSize = scores.FontSize = state.FontSize = status.FontSize = activeSkill0.FontSize = activeSkill1.FontSize = activeSkill2.FontSize = prop0.FontSize = prop1.FontSize = prop2.FontSize = fontsize; | |||||
| } | } | ||||
| private void SetStaticValue(MessageOfTricker obj) | private void SetStaticValue(MessageOfTricker obj) | ||||
| @@ -48,8 +49,8 @@ namespace Client | |||||
| case TrickerType.ANoisyPerson: | case TrickerType.ANoisyPerson: | ||||
| serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nANoisyPerson"; | serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nANoisyPerson"; | ||||
| break; | break; | ||||
| case TrickerType._4: | |||||
| serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nTrickerType4"; | |||||
| case TrickerType.Idol: | |||||
| serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nIdol"; | |||||
| break; | break; | ||||
| case TrickerType.NullTrickerType: | case TrickerType.NullTrickerType: | ||||
| serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nNullTrickerType"; | serial.Text = "👥" + Convert.ToString(1) + "👻" + Convert.ToString(obj.PlayerId) + "\nNullTrickerType"; | ||||
| @@ -31,7 +31,8 @@ namespace Client | |||||
| } | } | ||||
| public void SetFontSize(double fontsize) | public void SetFontSize(double fontsize) | ||||
| { | { | ||||
| serial.FontSize = scores.FontSize = status.FontSize = activeSkill0.FontSize = activeSkill1.FontSize = activeSkill2.FontSize = prop0.FontSize = prop1.FontSize = prop2.FontSize = fontsize; | |||||
| if (fontsize != 0) | |||||
| serial.FontSize = scores.FontSize = status.FontSize = activeSkill0.FontSize = activeSkill1.FontSize = activeSkill2.FontSize = prop0.FontSize = prop1.FontSize = prop2.FontSize = fontsize; | |||||
| } | } | ||||
| private void SetStaticValue(MessageOfStudent obj) | private void SetStaticValue(MessageOfStudent obj) | ||||
| { | { | ||||
| @@ -69,10 +69,10 @@ namespace GameClass.GameObj | |||||
| ap = value; | ap = value; | ||||
| } | } | ||||
| } | } | ||||
| public override int Speed => GameData.basicBulletMoveSpeed * 2; | |||||
| public override int Speed => GameData.basicBulletMoveSpeed * 25 / 10; | |||||
| public override bool IsRemoteAttack => true; | public override bool IsRemoteAttack => true; | ||||
| public override int CastTime => GameData.basicCastTime; | |||||
| public override int CastTime => GameData.basicCastTime * 4 / 5; | |||||
| public override int Backswing => 0; | public override int Backswing => 0; | ||||
| public override int RecoveryFromHit => 0; | public override int RecoveryFromHit => 0; | ||||
| public const int cd = GameData.basicBackswing / 2; | public const int cd = GameData.basicBackswing / 2; | ||||
| @@ -137,7 +137,6 @@ namespace GameClass.GameObj | |||||
| switch (gameObjType) | switch (gameObjType) | ||||
| { | { | ||||
| case GameObjType.Character: | case GameObjType.Character: | ||||
| case GameObjType.Generator: | |||||
| return true; | return true; | ||||
| default: | default: | ||||
| return false; | return false; | ||||
| @@ -184,7 +183,6 @@ namespace GameClass.GameObj | |||||
| switch (gameObjType) | switch (gameObjType) | ||||
| { | { | ||||
| case GameObjType.Character: | case GameObjType.Character: | ||||
| case GameObjType.Generator: | |||||
| return true; | return true; | ||||
| default: | default: | ||||
| return false; | return false; | ||||
| @@ -300,14 +300,6 @@ namespace GameClass.GameObj | |||||
| if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving; | if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving; | ||||
| return playerState; | return playerState; | ||||
| } | } | ||||
| set | |||||
| { | |||||
| if (value != PlayerStateType.Moving) | |||||
| lock (gameObjLock) | |||||
| IsMoving = false; | |||||
| lock (gameObjLock) playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||||
| } | |||||
| } | } | ||||
| public bool NoHp() => (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped | public bool NoHp() => (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped | ||||
| @@ -323,6 +315,30 @@ namespace GameClass.GameObj | |||||
| || playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned | || playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned | ||||
| || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | ||||
| private GameObj? whatInteractingWith = null; | |||||
| public GameObj? WhatInteractingWith => whatInteractingWith; | |||||
| public void ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||||
| { | |||||
| lock (gameObjLock) | |||||
| { | |||||
| whatInteractingWith = gameObj; | |||||
| if (value != PlayerStateType.Moving) | |||||
| IsMoving = false; | |||||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | |||||
| } | |||||
| } | |||||
| public void SetPlayerStateNaturally() | |||||
| { | |||||
| lock (gameObjLock) | |||||
| { | |||||
| whatInteractingWith = null; | |||||
| playerState = PlayerStateType.Null; | |||||
| } | |||||
| } | |||||
| private int score = 0; | private int score = 0; | ||||
| public int Score | public int Score | ||||
| { | { | ||||
| @@ -382,21 +398,6 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /// <summary> | |||||
| /// 角色携带的信息 | |||||
| /// </summary> | |||||
| private string message = "THUAI6"; | |||||
| public string Message | |||||
| { | |||||
| get => message; | |||||
| set | |||||
| { | |||||
| lock (gameObjLock) | |||||
| { | |||||
| message = value; | |||||
| } | |||||
| } | |||||
| } | |||||
| #region 道具和buff相关属性、方法 | #region 道具和buff相关属性、方法 | ||||
| private Prop[] propInventory = new Prop[GameData.maxNumOfPropInPropInventory] | private Prop[] propInventory = new Prop[GameData.maxNumOfPropInPropInventory] | ||||
| @@ -521,6 +522,12 @@ namespace GameClass.GameObj | |||||
| return this.HasShield; | return this.HasShield; | ||||
| case BuffType.AddLife: | case BuffType.AddLife: | ||||
| return this.HasLIFE; | return this.HasLIFE; | ||||
| case BuffType.AddAp: | |||||
| return this.HasAp; | |||||
| case BuffType.Clairaudience: | |||||
| return this.HasClairaudience; | |||||
| case BuffType.Invisible: | |||||
| return this.HasInvisible; | |||||
| default: | default: | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -20,20 +20,25 @@ namespace GameClass.GameObj | |||||
| private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() }; | private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() }; | ||||
| public Prop[] PropInChest => propInChest; | public Prop[] PropInChest => propInChest; | ||||
| private int openDegree = 0; | |||||
| public int OpenDegree | |||||
| private int openStartTime = 0; | |||||
| public int OpenStartTime => openStartTime; | |||||
| private Character? whoOpen = null; | |||||
| public Character? WhoOpen => whoOpen; | |||||
| public void Open(int startTime, Character character) | |||||
| { | { | ||||
| get => openDegree; | |||||
| set | |||||
| lock (gameObjLock) | |||||
| { | { | ||||
| if (value > 0) | |||||
| lock (gameObjLock) | |||||
| openDegree = (value > GameData.degreeOfOpenedChest) ? GameData.degreeOfOpenedChest : value; | |||||
| else | |||||
| lock (gameObjLock) | |||||
| openDegree = 0; | |||||
| openStartTime = startTime; | |||||
| whoOpen = character; | |||||
| } | |||||
| } | |||||
| public void StopOpen() | |||||
| { | |||||
| lock (gameObjLock) | |||||
| { | |||||
| openStartTime = 0; | |||||
| whoOpen = null; | |||||
| } | } | ||||
| } | } | ||||
| public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedChest); | |||||
| } | } | ||||
| } | } | ||||
| @@ -35,14 +35,14 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| } | } | ||||
| private bool isOpening = false; | |||||
| public bool IsOpening | |||||
| private int openStartTime = 0; | |||||
| public int OpenStartTime | |||||
| { | { | ||||
| get => isOpening; | |||||
| get => openStartTime; | |||||
| set | set | ||||
| { | { | ||||
| lock (gameObjLock) | lock (gameObjLock) | ||||
| isOpening = value; | |||||
| openStartTime = value; | |||||
| } | } | ||||
| } | } | ||||
| @@ -61,6 +61,6 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| } | } | ||||
| public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedDoorway); | |||||
| public bool IsOpen() => (openDegree == GameData.degreeOfOpenedDoorway); | |||||
| } | } | ||||
| } | } | ||||
| @@ -221,6 +221,20 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| return flag; | return flag; | ||||
| } | } | ||||
| public bool RemoveJustFromMap(GameObj gameObj) | |||||
| { | |||||
| GameObjLockDict[gameObj.Type].EnterWriteLock(); | |||||
| bool flag; | |||||
| try | |||||
| { | |||||
| flag = GameObjDict[gameObj.Type].Remove(gameObj); | |||||
| } | |||||
| finally | |||||
| { | |||||
| GameObjLockDict[gameObj.Type].ExitWriteLock(); | |||||
| } | |||||
| return flag; | |||||
| } | |||||
| public void Add(GameObj gameObj) | public void Add(GameObj gameObj) | ||||
| { | { | ||||
| GameObjLockDict[gameObj.Type].EnterWriteLock(); | GameObjLockDict[gameObj.Type].EnterWriteLock(); | ||||
| @@ -0,0 +1,346 @@ | |||||
| # 规则 | |||||
| V4.4 | |||||
| - [规则](#规则) | |||||
| - [简则](#简则) | |||||
| - [地图](#地图) | |||||
| - [人物](#人物) | |||||
| - [攻击](#攻击) | |||||
| - [交互](#交互) | |||||
| - [学习与毕业](#学习与毕业) | |||||
| - [勉励](#勉励) | |||||
| - [沉迷与唤醒](#沉迷与唤醒) | |||||
| - [门](#门) | |||||
| - [窗](#窗) | |||||
| - [箱子](#箱子) | |||||
| - [信息相关](#信息相关) | |||||
| - [可视范围](#可视范围) | |||||
| - [道具](#道具) | |||||
| - [得分](#得分) | |||||
| - [捣蛋鬼](#捣蛋鬼) | |||||
| - [学生](#学生) | |||||
| - [职业与技能](#职业与技能) | |||||
| - [捣蛋鬼](#捣蛋鬼-1) | |||||
| - [Assassin](#assassin) | |||||
| - [Klee](#klee) | |||||
| - [喧哗者ANoisyPerson](#喧哗者anoisyperson) | |||||
| - [Idol](#idol) | |||||
| - [学生(\&老师)](#学生老师) | |||||
| - [运动员](#运动员) | |||||
| - [教师](#教师) | |||||
| - [学霸](#学霸) | |||||
| - [开心果](#开心果) | |||||
| - [细则](#细则) | |||||
| - [特殊说明](#特殊说明) | |||||
| - [移动](#移动) | |||||
| - [人物](#人物-1) | |||||
| - [初始状态](#初始状态) | |||||
| - [道具](#道具-1) | |||||
| - [交互](#交互-1) | |||||
| - [学习与毕业](#学习与毕业-1) | |||||
| - [攻击](#攻击-1) | |||||
| - [沉迷与唤醒](#沉迷与唤醒-1) | |||||
| - [门](#门-1) | |||||
| - [窗](#窗-1) | |||||
| - [箱子](#箱子-1) | |||||
| - [得分](#得分-1) | |||||
| - [信息相关](#信息相关-1) | |||||
| ## 简则 | |||||
| ### 地图 | |||||
| - 地图为矩形区域,游戏对象坐标为(x, y),x和y均为整数。 | |||||
| - **x坐标轴正方向竖直向下,y坐标轴正方向水平向右**; | |||||
| - **极坐标以x坐标轴为极轴,角度逆时针为正方向**。 | |||||
| - 地图由50 * 50个格子构成,其中每个格子代表1000 * 1000的正方形。每个格子的编号(CellX,CellY)可以计算得到: | |||||
| - $$CellX=\frac{x}{1000},CellY=\frac{y}{1000}$$ | |||||
| - 格子有对应区域类型:陆地、墙、草地、教室、校门、隐藏校门、门、窗、箱子 | |||||
| ### 人物 | |||||
| - 人物半径为800 | |||||
| - 人物共有17种不可叠加的状态: | |||||
| 1. (可)移动状态 | |||||
| 2. 学习 | |||||
| 3. 被勉励 | |||||
| 4. 在勉励 | |||||
| 5. 开或锁门 | |||||
| 6. 翻箱 | |||||
| 7. 使用技能 | |||||
| 8. 开启校门 | |||||
| 9. 唤醒他人中 | |||||
| - 之后八项为不可行动状态 | |||||
| 1. 被唤醒中 | |||||
| 2. 沉迷 | |||||
| 3. 退学 | |||||
| 4. 毕业 | |||||
| 5. 被眩晕 | |||||
| 6. 前摇 | |||||
| 7. 后摇 | |||||
| 8. 翻窗 | |||||
| ### 攻击 | |||||
| - 攻击类型CommonAttackOfGhost攻击未写完的作业,会造成对应攻击力的损坏 | |||||
| - 捣蛋鬼攻击交互状态或前后摇的学生,将使学生眩晕4.3s | |||||
| | 攻击(子弹)类型 |搞蛋鬼的一般攻击CommonAttackOfGhost| 飞刀FlyingKnife | 蹦蹦炸弹BombBomb | 小炸弹JumpyDumpty | | |||||
| | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | |||||
| | 子弹爆炸范围 | 0 | 0 | 1000 | 500 | | |||||
| | 子弹攻击距离 | 1100 | 39000 | 1100 | 2200 | | |||||
| | 攻击力 | 1500000 | 1200000 | 1,800,000 | 900000 | | |||||
| | 移动速度/s | 3700 | 9250 | 3000 | 4300 | | |||||
| | 前摇(ms) | 297 | 400 | 366 | - | | |||||
| |未攻击至目标时的后摇(ms)| 800 | 0 | 3700 | - | | |||||
| |攻击至目标时的后摇(ms)| 3700 | 0 | 3700 | - | | |||||
| | CD(ms) | 800 | 400 | 3000 | - | | |||||
| | 最大子弹容量 | 1 | 1 | 1 | - | | |||||
| ### 交互 | |||||
| - 除了翻窗,交互目标与交互者在一个**九宫格**方可交互 | |||||
| - 交互进度每毫秒增加对应交互速度的值 | |||||
| - 作业,门,箱子完成/开启进度达到10000000为完成 | |||||
| #### 学习与毕业 | |||||
| - 每张地图都有10间教室,学生需要完成其中的**7间**教室的作业,才可以开启任意校门。 | |||||
| - 开启校门所需时间为18秒,开启的进度不清空 | |||||
| - 当**3间**教室的作业完成时,隐藏校门在3-5个刷新点之一随机显现;当只剩1名学生时,隐藏校门自动打开。 | |||||
| - 从开启的校门或隐藏校门离开是学生终极目标 | |||||
| #### 勉励 | |||||
| - 当被勉励程度达到当前损失的毅力值或1500000时,勉励完成,学生毅力增加对应被勉励程度。 | |||||
| - 勉励中断时,被勉励程度保留;遭到攻击时被勉励程度清空 | |||||
| #### 沉迷与唤醒 | |||||
| - 学习毅力归零时,学生原地进入沉迷状态,每毫秒增加1沉迷度 | |||||
| - 唤醒需要时间1秒,之后学习毅力恢复至1/2。沉迷程度不清空。 | |||||
| - 进入沉迷状态时,如果学生原沉迷程度在(0,该玩家最大沉迷度/3)中,沉迷程度直接变为其最大沉迷度/3;原沉迷程度在[其最大沉迷度/3,其最大沉迷度x2/3)中,沉迷程度直接变为其最大沉迷度x2/3;原沉迷程度大于其最大沉迷度x2/3,从游戏中出局; | |||||
| - 当学生沉迷程度达到其最大沉迷程度时,从游戏中出局 | |||||
| #### 门 | |||||
| - 门分别属于三个教学区:三教,五教,六教 | |||||
| - 拥有对应教学区的钥匙才能开锁对应的门 | |||||
| - 锁门过程中,门所在格子内有人会使锁门过程中断 | |||||
| #### 窗 | |||||
| - 翻窗时玩家应当在窗前后左右一个格子内 | |||||
| #### 箱子 | |||||
| - 开箱后将有2个随机道具掉落在玩家位置。 | |||||
| ### 信息相关 | |||||
| - Bgm (在structures.h/.py中的student类或Tricker类中作为其属性) | |||||
| 1. 不详的感觉(dangerAlert):如果捣蛋鬼进入(学生的警戒半径/捣蛋鬼的隐蔽度)的距离,则学生的dangerAlert=(int)(警戒半径/二者距离) | |||||
| 2. 期待搞事的感觉(trickDesire):如果有学生进入(捣蛋鬼的警戒半径/学生的隐蔽度)的距离,则捣蛋鬼trickDesire=(int)(警戒半径/可被发觉的最近的学生距离) | |||||
| 3. 学习的声音(classVolume): 警戒半径内有人学习时,捣蛋鬼classVolume=(int)((警戒半径x学习进度百分比)/二者距离) | |||||
| - 可以向其他每一个队友发送不超过256字节的信息 | |||||
| ### 可视范围 | |||||
| - 小于视野半径,且受限于墙和草地 | |||||
| - 对于在从草地中的物体,物体与玩家连线上均为草地方可见 | |||||
| ### 道具 | |||||
| - 玩家同时最多拥有三个道具 | |||||
| - 可以捡起与自己处于同一个格子(cell)的道具 | |||||
| - 可将道具扔在玩家位置 | |||||
| | 道具 | 对学生增益 | 学生得分条件 | 对搞蛋鬼增益 | 搞蛋鬼得分条件 | | |||||
| | :-------- | :-------------------------------------- | :-----------------| :-------------------------------------- |:-----------------| | |||||
| | Key3 | 能开启3教的门 |不得分| 能开启3教的门 |不得分| | |||||
| | Key5 | 能开启5教的门 |不得分| 能开启5教的门 |不得分| | |||||
| | Key6 | 能开启6教的门 |不得分| 能开启6教的门 |不得分| | |||||
| | AddSpeed | 2倍速,持续10s | 得分10| 2倍速,持续10s |得分10| | |||||
| | AddLifeOrClairaudience |若在10s内Hp归零,该增益消失以使Hp保留100|在10s内Hp归零,得分100 |10秒内得知全场玩家信息|得分10 | | |||||
| | AddHpOrAp |回血750000 | 回血成功,得分10 | 10秒内下一次攻击增伤1800000|不得分 | | |||||
| | ShieldOrSpear | 10秒内能抵挡一次伤害 | 10秒内成功抵挡一次伤害,得分50 |10秒内下一次攻击能破盾,如果对方无盾,则增伤900000| 10秒内破盾,得分50 | | |||||
| | RecoveryFromDizziness | 使用瞬间从眩晕状态中恢复 | 成功从眩晕状态中恢复,得分30|使用瞬间从眩晕状态中恢复 | 成功从眩晕状态中恢复,得分30| | |||||
| ## 得分 | |||||
| ### 捣蛋鬼 | |||||
| - 对学生造成伤害时,得伤害*100/基本伤害(1500000)分。 | |||||
| - 对作业造成破坏时,每破坏n%的作业,得n分 | |||||
| - 使学生沉迷时,得50分。 | |||||
| - 使学生眩晕时,得20*眩晕时长(/s)分。 | |||||
| - 每淘汰一个学生,得1000分 | |||||
| ### 学生 | |||||
| - 学生每完成n%的作业,得2n分 | |||||
| - 学生与捣蛋鬼处于小于5000的距离认为在牵制状态,得(牵制时长(ms)*0.00246)分 | |||||
| - 使捣蛋鬼进入眩晕状态时,得20*眩晕时长(/s)分。 | |||||
| - 成功唤醒一名同学,得100分 | |||||
| - 成功勉励一名同学,得50*勉励程度/基本勉励程度(1500000)分 | |||||
| - 毕业,得1000分 | |||||
| ## 职业与技能 | |||||
| ### 捣蛋鬼 | |||||
| | 捣蛋鬼职业 | Assassin | Klee | 喧哗者ANoisyPerson | Idol | | |||||
| | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | |||||
| | 移动速度/s | 1980 | 1800 | 1926 | 1,800 | | |||||
| | 隐蔽度 | 1.5 | 1 | 0.8 | 0.75| | |||||
| | 警戒范围 | 22,100 | 17000 | 15,300 | 17000 | |||||
| | 视野范围 | 18,000 | 15000 | 15000 | 16,500| | |||||
| | 开锁门速度 | 4000 | 4000 | 4000 |4000 | | |||||
| | 翻窗速度 | 1270 | 1270 | 1,397 | 1270| | |||||
| | 翻箱速度 | 1000 | 1100 | 1000 |1000| | |||||
| #### Assassin | |||||
| - 普通攻击为 CommonAttackOfGhost | |||||
| - 主动技能 | |||||
| - 隐身 BecomeInvisible | |||||
| - CD:40s 持续时间:10s | |||||
| - 在持续时间内玩家隐身 | |||||
| - 使用瞬间得分15 | |||||
| - 使用飞刀 | |||||
| - CD:30s 持续时间:1s | |||||
| - 在持续时间内,攻击类型变为飞刀 | |||||
| - 不直接得分 | |||||
| #### Klee | |||||
| - 普通攻击为 CommonAttackOfGhost | |||||
| - 主动技能 | |||||
| - 蹦蹦炸弹 JumpyBomb | |||||
| - CD:15s 持续时间:3s | |||||
| - 在持续时间内,攻击类型变为蹦蹦炸弹 | |||||
| - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上90°,270° 发出2个小炸弹 | |||||
| - 2个小炸弹运动停止前会因为碰撞爆炸,停止运动后学生碰撞会造成眩晕3.07s | |||||
| - 不直接得分,通过眩晕等获得对应得分 | |||||
| #### 喧哗者ANoisyPerson | |||||
| - 普通攻击为 CommonAttackOfGhost | |||||
| - 主动技能 | |||||
| - 嚎叫 Howl | |||||
| - CD:25s | |||||
| - 使用瞬间,在视野半径范围内(不是可视区域)的学生被眩晕5500ms,自己进入800ms的后摇 | |||||
| - 通过眩晕获得对应得分 | |||||
| - 特性 | |||||
| - 在场所有学生Bgm系统被设为无用的值 | |||||
| #### Idol | |||||
| - 普通攻击为 CommonAttackOfGhost | |||||
| - 主动技能 | |||||
| - ShowTime | |||||
| - CD: 75s 持续时间:10s | |||||
| - 持续时间内每一帧向所有学生发送向自己移动的指令。 | |||||
| ### 学生(&老师) | |||||
| | 学生职业 | 教师Teacher | 健身狂Athlete | 学霸StraightAStudent | 开心果Sunshine | | |||||
| | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | |||||
| | 移动速度 | 1350 | 1575 | 1440 | 1500 | | |||||
| | 最大毅力值 | 30000000 | 3000000 | 3300000 | 3200000 | | |||||
| | 最大沉迷度 | 600000 | 54,000 | 78,000 | 66,000 | | |||||
| | 学习一科速度 | 0 | 73 | 135 | 123 | | |||||
| | 勉励速度 | 80 | 90 | 100 | 120 | | |||||
| | 隐蔽度 | 0.5 | 0.9 | 0.9 | 0.8 | | |||||
| | 警戒范围 | 7500 | 15000 | 13,500 | 15000 | | |||||
| | 视野范围 | 9,000 | 11000 | 9,000 | 10000 | | |||||
| | 开锁门速度 | 4000 | 4000 | 4000 | 2800 | | |||||
| | 翻窗速度 | 635 | 1,524 | 1,058 | 1270 | | |||||
| | 翻箱速度 | 1000 | 1000 | 1000 | 900 | | |||||
| #### 运动员 | |||||
| - 主动技能 | |||||
| - 冲撞 CanBeginToCharge | |||||
| - CD:60s 持续时间:3s | |||||
| - 在持续时间内,速度变为三倍,期间撞到捣蛋鬼,会导致捣蛋鬼眩晕7.22s,学生眩晕2.09s | |||||
| - 通过眩晕获得对应得分 | |||||
| #### 教师 | |||||
| - 主动技能 | |||||
| - 惩罚 Punish | |||||
| - CD:30s | |||||
| - 使用瞬间,在可视范围内的翻窗、开锁门、攻击前后摇的捣蛋鬼会被眩晕(3070+300*已受伤害/基本伤害(1500000))ms, | |||||
| - 通过眩晕获得对应得分 | |||||
| - 特性 | |||||
| - 无法获得牵制得分 | |||||
| - 无法毕业 | |||||
| #### 学霸 | |||||
| - 特性 | |||||
| - 冥想 | |||||
| - 当玩家处于可接受指令状态且不在修机时,会积累学习进度,速度为40/ms | |||||
| - 受到攻击(并非伤害)或学习或进入不可接受治疗状态(包括翻窗)学习进度清零 | |||||
| - 主动技能5 | |||||
| - 写答案 WriteAnswers | |||||
| - CD:30s | |||||
| - 使用瞬间,对于可互动范围内的一台电机增加这个学习进度 | |||||
| - 通过修机获得对应得分 | |||||
| #### 开心果 | |||||
| - 主动技能 | |||||
| - 唤醒 Rouse | |||||
| - CD:120s | |||||
| - 使用瞬间,唤醒可视范围内一个沉迷中的人 | |||||
| - 通过唤醒获得对应得分 | |||||
| - 勉励 Encourage | |||||
| - CD:120s | |||||
| - 使用瞬间,为可视范围内随机一个毅力不足的人试图补充750000的毅力值 | |||||
| - 获得勉励750000的毅力值对应得分 | |||||
| - 鼓舞 Inspire | |||||
| - CD:120s | |||||
| - 使用瞬间,可视范围内学生(包括自己)获得持续6秒的1.6倍速Buff | |||||
| - 每鼓舞一个学生得分10 | |||||
| ## 细则 | |||||
| ### 特殊说明 | |||||
| - 不加说明,这里“学生”往往包括职业“教师” | |||||
| ### 移动 | |||||
| - 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。 | |||||
| ### 人物 | |||||
| - EndAllAction()及Move指令调用数总和一帧内不超过10次 | |||||
| ### 初始状态 | |||||
| - 玩家出生点固定且一定为空地 | |||||
| ### 道具 | |||||
| - 使用钥匙相当于销毁 | |||||
| ### 交互 | |||||
| - 在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令 | |||||
| - 实际上唤醒或勉励不同的人是有效的 | |||||
| - 被唤醒或被勉励不属于交互状态,翻窗属于交互状态 | |||||
| ### 学习与毕业 | |||||
| - 一个校门同时最多可以由一人开启 | |||||
| - 隐藏校门与校门对于人有碰撞体积 | |||||
| ### 攻击 | |||||
| - 无论近战远程均产生bullet表示攻击 | |||||
| - 前摇期间攻击被打断时,子弹消失。 | |||||
| - 每次学生受到攻击后会损失对应子弹的攻击力的学习毅力 | |||||
| - 此处,前摇指 从播放攻击动作开始 攻击者不能交互 的时间 | |||||
| ### 沉迷与唤醒 | |||||
| - 不能两人同时唤醒一个人 | |||||
| - 在被救时沉迷度不增加 | |||||
| ### 门 | |||||
| - 每个教学区都有2把钥匙 | |||||
| - 一扇门只允许同时一个人开锁门 | |||||
| - 开锁门未完成前,门状态表现为原来的状态 | |||||
| - 开锁门进度中断后清空 | |||||
| ### 窗 | |||||
| - 翻越窗户是一种交互行为,翻窗一共有两个过程 | |||||
| - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 | |||||
| - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 | |||||
| - 攻击可以穿过窗,道具可以在窗上 | |||||
| - 有人正在翻越窗户时,其他玩家均不可以翻越该窗户。 | |||||
| - 通常情况下捣蛋鬼翻越窗户的速度高于学生。 | |||||
| ### 箱子 | |||||
| - 地图上有8个箱子 | |||||
| - 同一时刻只允许一人进行开启 | |||||
| - 未开启完成的箱子在下一次需要重新开始开启。 | |||||
| - 箱子开启后其中道具才可以被观测和拿取 | |||||
| - 箱子道具不刷新 | |||||
| ### 得分 | |||||
| - 眩晕或毅力值归零时无牵制得分 | |||||
| ### 信息相关 | |||||
| - Bgm在没有符合条件的情况下,值为0。 | |||||
| @@ -23,31 +23,31 @@ namespace Gaming | |||||
| { | { | ||||
| if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) | if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) | ||||
| { | { | ||||
| if (CharacterManager.BeStunned((Character)player, GameData.TimeOfStunnedWhenJumpyDumpty)) | |||||
| if (characterManager.BeStunned((Character)player, GameData.TimeOfStunnedWhenJumpyDumpty)) | |||||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.TimeOfStunnedWhenJumpyDumpty)); | player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.TimeOfStunnedWhenJumpyDumpty)); | ||||
| gameMap.Remove((GameObj)collisionObj); | gameMap.Remove((GameObj)collisionObj); | ||||
| } | } | ||||
| } | } | ||||
| if (player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost()) | if (player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost()) | ||||
| { | { | ||||
| if (CharacterManager.BeStunned((Character)collisionObj, GameData.TimeOfGhostFaintingWhenCharge)) | |||||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.TimeOfGhostFaintingWhenCharge)); | |||||
| CharacterManager.BeStunned(player, GameData.TimeOfStudentFaintingWhenCharge); | |||||
| if (characterManager.BeStunned((Character)collisionObj, GameData.TimeOfGhostStunnedWhenCharge)) | |||||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.TimeOfGhostStunnedWhenCharge)); | |||||
| characterManager.BeStunned(player, GameData.TimeOfStudentStunnedWhenCharge); | |||||
| } | } | ||||
| } | } | ||||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | ||||
| { | { | ||||
| if (!playerToMove.Commandable()) return false; | |||||
| playerToMove.PlayerState = PlayerStateType.Moving; | |||||
| if (!playerToMove.Commandable() || !TryToStop()) return false; | |||||
| characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving); | |||||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection); | moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection); | ||||
| return true; | return true; | ||||
| } | } | ||||
| public static bool Stop(Character player) | |||||
| public bool Stop(Character player) | |||||
| { | { | ||||
| if (player.Commandable()) | |||||
| if (player.Commandable() || !TryToStop()) | |||||
| { | { | ||||
| player.PlayerState = PlayerStateType.Null; | |||||
| characterManager.SetPlayerState(player); | |||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | return false; | ||||
| @@ -63,7 +63,7 @@ namespace Gaming | |||||
| return false; | return false; | ||||
| ++generatorForFix.NumOfFixing; | ++generatorForFix.NumOfFixing; | ||||
| player.PlayerState = PlayerStateType.Fixing; | |||||
| characterManager.SetPlayerState(player, PlayerStateType.Fixing); | |||||
| new Thread | new Thread | ||||
| ( | ( | ||||
| () => | () => | ||||
| @@ -74,7 +74,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) | if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) | ||||
| { | { | ||||
| player.PlayerState = PlayerStateType.Null; | |||||
| characterManager.SetPlayerState(player); | |||||
| gameMap.NumOfRepairedGenerators++; | gameMap.NumOfRepairedGenerators++; | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -96,31 +96,21 @@ namespace Gaming | |||||
| if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorway) | if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorway) | ||||
| return false; | return false; | ||||
| Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); | Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); | ||||
| if (doorwayToOpen == null || doorwayToOpen.IsOpening || !doorwayToOpen.PowerSupply) | |||||
| if (doorwayToOpen == null || doorwayToOpen.OpenStartTime > 0 || !doorwayToOpen.PowerSupply) | |||||
| return false; | return false; | ||||
| player.PlayerState = PlayerStateType.OpeningTheDoorway; | |||||
| doorwayToOpen.IsOpening = true; | |||||
| characterManager.SetPlayerState(player, PlayerStateType.OpeningTheDoorway, doorwayToOpen); | |||||
| int startTime = doorwayToOpen.OpenStartTime = gameMap.Timer.nowTime(); | |||||
| new Thread | new Thread | ||||
| ( | ( | ||||
| () => | () => | ||||
| { | { | ||||
| new FrameRateTaskExecutor<int>( | |||||
| loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheDoorway && gameMap.Timer.IsGaming && doorwayToOpen.OpenDegree < GameData.degreeOfOpenedDoorway, | |||||
| loopToDo: () => | |||||
| { | |||||
| doorwayToOpen.OpenDegree += GameData.frameDuration; | |||||
| }, | |||||
| timeInterval: GameData.frameDuration, | |||||
| finallyReturn: () => 0 | |||||
| ) | |||||
| Thread.Sleep(GameData.degreeOfOpenedDoorway - doorwayToOpen.OpenDegree); | |||||
| .Start(); | |||||
| doorwayToOpen.IsOpening = false; | |||||
| if (doorwayToOpen.OpenDegree >= GameData.degreeOfOpenedDoorway) | |||||
| if (doorwayToOpen.OpenStartTime == startTime) | |||||
| { | { | ||||
| if (player.PlayerState == PlayerStateType.OpeningTheDoorway) | |||||
| player.PlayerState = PlayerStateType.Null; | |||||
| doorwayToOpen.OpenDegree = GameData.degreeOfOpenedDoorway; | |||||
| player.SetPlayerStateNaturally(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -132,7 +122,7 @@ namespace Gaming | |||||
| public bool Escape(Student player) | public bool Escape(Student player) | ||||
| { | { | ||||
| if (!(player.Commandable()) || player.CharacterType == CharacterType.Robot) | |||||
| if (!(player.Commandable()) || player.CharacterType == CharacterType.Robot || player.CharacterType == CharacterType.Teacher) | |||||
| return false; | return false; | ||||
| Doorway? doorwayForEscape = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); | Doorway? doorwayForEscape = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); | ||||
| if (doorwayForEscape != null && doorwayForEscape.IsOpen()) | if (doorwayForEscape != null && doorwayForEscape.IsOpen()) | ||||
| @@ -163,7 +153,7 @@ namespace Gaming | |||||
| playerTreated = gameMap.StudentForInteract(player.Position); | playerTreated = gameMap.StudentForInteract(player.Position); | ||||
| if (playerTreated == null) return false; | if (playerTreated == null) return false; | ||||
| } | } | ||||
| if (player == playerTreated || (!player.Commandable()) || player.PlayerState == PlayerStateType.Treating || | |||||
| if (player == playerTreated || (!player.Commandable()) || playerTreated.PlayerState == PlayerStateType.Treated || | |||||
| (!playerTreated.Commandable()) || | (!playerTreated.Commandable()) || | ||||
| playerTreated.HP == playerTreated.MaxHp || !GameData.ApproachToInteract(playerTreated.Position, player.Position)) | playerTreated.HP == playerTreated.MaxHp || !GameData.ApproachToInteract(playerTreated.Position, player.Position)) | ||||
| return false; | return false; | ||||
| @@ -172,22 +162,22 @@ namespace Gaming | |||||
| ( | ( | ||||
| () => | () => | ||||
| { | { | ||||
| playerTreated.PlayerState = PlayerStateType.Treated; | |||||
| player.PlayerState = PlayerStateType.Treating; | |||||
| characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated); | |||||
| characterManager.SetPlayerState(player, PlayerStateType.Treating); | |||||
| new FrameRateTaskExecutor<int>( | new FrameRateTaskExecutor<int>( | ||||
| loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && player.PlayerState == PlayerStateType.Treating && gameMap.Timer.IsGaming, | loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && player.PlayerState == PlayerStateType.Treating && gameMap.Timer.IsGaming, | ||||
| loopToDo: () => | loopToDo: () => | ||||
| { | { | ||||
| if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) | if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) | ||||
| playerTreated.PlayerState = PlayerStateType.Null; | |||||
| characterManager.SetPlayerState(playerTreated); | |||||
| }, | }, | ||||
| timeInterval: GameData.frameDuration, | timeInterval: GameData.frameDuration, | ||||
| finallyReturn: () => 0 | finallyReturn: () => 0 | ||||
| ) | ) | ||||
| .Start(); | .Start(); | ||||
| if (player.PlayerState == PlayerStateType.Treating) player.PlayerState = PlayerStateType.Null; | |||||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) playerTreated.PlayerState = PlayerStateType.Null; | |||||
| if (player.PlayerState == PlayerStateType.Treating) characterManager.SetPlayerState(player); | |||||
| else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated); | |||||
| } | } | ||||
| ) | ) | ||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| @@ -200,11 +190,10 @@ namespace Gaming | |||||
| playerRescued = gameMap.StudentForInteract(player.Position); | playerRescued = gameMap.StudentForInteract(player.Position); | ||||
| if (playerRescued == null) return false; | if (playerRescued == null) return false; | ||||
| } | } | ||||
| if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || player == playerRescued | |||||
| || !GameData.ApproachToInteract(playerRescued.Position, player.Position) || playerRescued.TimeOfRescue > 0) | |||||
| if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position)) | |||||
| return false; | return false; | ||||
| player.PlayerState = PlayerStateType.Rescuing; | |||||
| playerRescued.PlayerState = PlayerStateType.Rescued; | |||||
| characterManager.SetPlayerState(player, PlayerStateType.Rescuing); | |||||
| characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued); | |||||
| new Thread | new Thread | ||||
| ( | ( | ||||
| @@ -226,14 +215,14 @@ namespace Gaming | |||||
| { | { | ||||
| if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue) | if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue) | ||||
| { | { | ||||
| playerRescued.PlayerState = PlayerStateType.Null; | |||||
| characterManager.SetPlayerState(playerRescued); | |||||
| playerRescued.HP = playerRescued.MaxHp / 2; | playerRescued.HP = playerRescued.MaxHp / 2; | ||||
| player.AddScore(GameData.StudentScoreRescue); | player.AddScore(GameData.StudentScoreRescue); | ||||
| } | } | ||||
| else | else | ||||
| playerRescued.PlayerState = PlayerStateType.Addicted; | |||||
| characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted); | |||||
| } | } | ||||
| if (player.PlayerState == PlayerStateType.Rescuing) player.PlayerState = PlayerStateType.Null; | |||||
| if (player.PlayerState == PlayerStateType.Rescuing) characterManager.SetPlayerState(player); | |||||
| playerRescued.TimeOfRescue = 0; | playerRescued.TimeOfRescue = 0; | ||||
| } | } | ||||
| ) | ) | ||||
| @@ -247,30 +236,21 @@ namespace Gaming | |||||
| return false; | return false; | ||||
| Chest? chestToOpen = (Chest?)gameMap.OneForInteract(player.Position, GameObjType.Chest); | Chest? chestToOpen = (Chest?)gameMap.OneForInteract(player.Position, GameObjType.Chest); | ||||
| if (chestToOpen == null || chestToOpen.OpenDegree > 0) | |||||
| if (chestToOpen == null || chestToOpen.OpenStartTime > 0) | |||||
| return false; | return false; | ||||
| player.PlayerState = PlayerStateType.OpeningTheChest; | |||||
| characterManager.SetPlayerState(player, PlayerStateType.OpeningTheChest, chestToOpen); | |||||
| int startTime = gameMap.Timer.nowTime(); | |||||
| chestToOpen.Open(startTime, player); | |||||
| new Thread | new Thread | ||||
| ( | ( | ||||
| () => | () => | ||||
| { | { | ||||
| new FrameRateTaskExecutor<int>( | |||||
| loopCondition: () => player.PlayerState == PlayerStateType.OpeningTheChest && gameMap.Timer.IsGaming && (!chestToOpen.IsOpen()), | |||||
| loopToDo: () => | |||||
| { | |||||
| chestToOpen.OpenDegree += GameData.frameDuration * player.SpeedOfOpenChest; | |||||
| }, | |||||
| timeInterval: GameData.frameDuration, | |||||
| finallyReturn: () => 0 | |||||
| ) | |||||
| .Start(); | |||||
| Thread.Sleep(GameData.degreeOfOpenedChest / player.SpeedOfOpenChest); | |||||
| if (chestToOpen.IsOpen()) | |||||
| if (chestToOpen.OpenStartTime == startTime) | |||||
| { | { | ||||
| if (player.PlayerState == PlayerStateType.OpeningTheChest) | |||||
| player.PlayerState = PlayerStateType.Null; | |||||
| player.SetPlayerStateNaturally(); | |||||
| for (int i = 0; i < GameData.maxNumOfPropInChest; ++i) | for (int i = 0; i < GameData.maxNumOfPropInChest; ++i) | ||||
| { | { | ||||
| Prop prop = chestToOpen.PropInChest[i]; | Prop prop = chestToOpen.PropInChest[i]; | ||||
| @@ -279,8 +259,6 @@ namespace Gaming | |||||
| gameMap.Add(prop); | gameMap.Add(prop); | ||||
| } | } | ||||
| } | } | ||||
| else chestToOpen.OpenDegree = 0; | |||||
| } | } | ||||
| ) | ) | ||||
| @@ -312,7 +290,7 @@ namespace Gaming | |||||
| //Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer); | //Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer); | ||||
| // gameMap.Add(addWall); | // gameMap.Add(addWall); | ||||
| player.PlayerState = PlayerStateType.ClimbingThroughWindows; | |||||
| characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows); | |||||
| windowForClimb.WhoIsClimbing = player; | windowForClimb.WhoIsClimbing = player; | ||||
| new Thread | new Thread | ||||
| ( | ( | ||||
| @@ -354,7 +332,7 @@ namespace Gaming | |||||
| // gameMap.Remove(addWall); | // gameMap.Remove(addWall); | ||||
| if (player.PlayerState == PlayerStateType.ClimbingThroughWindows) | if (player.PlayerState == PlayerStateType.ClimbingThroughWindows) | ||||
| { | { | ||||
| player.PlayerState = PlayerStateType.Null; | |||||
| characterManager.SetPlayerState(player); | |||||
| } | } | ||||
| } | } | ||||
| @@ -394,7 +372,7 @@ namespace Gaming | |||||
| } | } | ||||
| if (!flag) return false; | if (!flag) return false; | ||||
| player.PlayerState = PlayerStateType.LockingOrOpeningTheDoor; | |||||
| characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor); | |||||
| new Thread | new Thread | ||||
| ( | ( | ||||
| () => | () => | ||||
| @@ -404,7 +382,6 @@ namespace Gaming | |||||
| loopToDo: () => | loopToDo: () => | ||||
| { | { | ||||
| flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null); | flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null); | ||||
| Preparation.Utility.Debugger.Output(doorToLock, flag.ToString()); | |||||
| doorToLock.OpenOrLockDegree += GameData.frameDuration * player.SpeedOfOpeningOrLocking; | doorToLock.OpenOrLockDegree += GameData.frameDuration * player.SpeedOfOpeningOrLocking; | ||||
| }, | }, | ||||
| timeInterval: GameData.frameDuration, | timeInterval: GameData.frameDuration, | ||||
| @@ -417,7 +394,7 @@ namespace Gaming | |||||
| doorToLock.IsOpen = (!doorToLock.IsOpen); | doorToLock.IsOpen = (!doorToLock.IsOpen); | ||||
| } | } | ||||
| if (player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor) | if (player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor) | ||||
| player.PlayerState = PlayerStateType.Null; | |||||
| characterManager.SetPlayerState(player); | |||||
| doorToLock.OpenOrLockDegree = 0; | doorToLock.OpenOrLockDegree = 0; | ||||
| } | } | ||||
| @@ -449,6 +426,33 @@ namespace Gaming | |||||
| } | } | ||||
| } | } | ||||
| */ | */ | ||||
| private object numLock = new object(); | |||||
| private int lastTime = 0; | |||||
| private int numStop = 0; | |||||
| private int NumStop => numStop; | |||||
| private bool TryToStop() | |||||
| { | |||||
| lock (numLock) | |||||
| { | |||||
| int time = gameMap.Timer.nowTime(); | |||||
| if (time / GameData.frameDuration > lastTime) | |||||
| { | |||||
| lastTime = time / GameData.frameDuration; | |||||
| numStop = 1; | |||||
| return true; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (numStop == GameData.LimitOfStopAndMove) | |||||
| return false; | |||||
| else | |||||
| { | |||||
| ++numStop; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| private readonly Map gameMap; | private readonly Map gameMap; | ||||
| private readonly CharacterManager characterManager; | private readonly CharacterManager characterManager; | ||||
| @@ -64,6 +64,29 @@ namespace Gaming | |||||
| } | } | ||||
| } | } | ||||
| public bool TryRemoveBullet(Bullet bullet) | |||||
| { | |||||
| bullet.CanMove = false; | |||||
| if (gameMap.Remove(bullet)) | |||||
| { | |||||
| if (bullet.BulletBombRange > 0) | |||||
| { | |||||
| BombedBullet bombedBullet = new(bullet); | |||||
| gameMap.Add(bombedBullet); | |||||
| new Thread | |||||
| (() => | |||||
| { | |||||
| Thread.Sleep(GameData.frameDuration * 5); | |||||
| gameMap.RemoveJustFromMap(bombedBullet); | |||||
| } | |||||
| ) | |||||
| { IsBackground = true }.Start(); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| else return false; | |||||
| } | |||||
| private void BulletBomb(Bullet bullet, GameObj? objBeingShot) | private void BulletBomb(Bullet bullet, GameObj? objBeingShot) | ||||
| { | { | ||||
| #if DEBUG | #if DEBUG | ||||
| @@ -72,23 +95,18 @@ namespace Gaming | |||||
| else | else | ||||
| Debugger.Output(bullet, "bombed without objBeingShot"); | Debugger.Output(bullet, "bombed without objBeingShot"); | ||||
| #endif | #endif | ||||
| bullet.CanMove = false; | |||||
| if (gameMap.Remove(bullet) && bullet.BulletBombRange > 0) | |||||
| gameMap.Add(new BombedBullet(bullet)); | |||||
| if (!TryRemoveBullet(bullet)) return; | |||||
| if (bullet.BulletBombRange == 0) | if (bullet.BulletBombRange == 0) | ||||
| { | { | ||||
| if (objBeingShot == null) | if (objBeingShot == null) | ||||
| { | { | ||||
| CharacterManager.BackSwing((Character?)bullet.Parent, bullet.Backswing); | |||||
| characterManager.BackSwing((Character)bullet.Parent, bullet.Backswing); | |||||
| return; | return; | ||||
| } | } | ||||
| Debugger.Output(bullet, bullet.TypeOfBullet.ToString()); | |||||
| BombObj(bullet, objBeingShot); | BombObj(bullet, objBeingShot); | ||||
| CharacterManager.BackSwing((Character?)bullet.Parent, bullet.RecoveryFromHit); | |||||
| characterManager.BackSwing((Character)bullet.Parent, bullet.RecoveryFromHit); | |||||
| return; | return; | ||||
| } | } | ||||
| @@ -105,6 +123,7 @@ namespace Gaming | |||||
| if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null) | if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null) | ||||
| { | { | ||||
| bullet.Parent.BulletOfPlayer = BulletType.JumpyDumpty; | bullet.Parent.BulletOfPlayer = BulletType.JumpyDumpty; | ||||
| Debugger.Output(bullet.Parent, bullet.Parent.CharacterType.ToString() + " " + bullet.Parent.BulletNum.ToString()); | |||||
| Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI / 2.0); | Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI / 2.0); | ||||
| Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI * 3.0 / 2.0); | Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI * 3.0 / 2.0); | ||||
| } | } | ||||
| @@ -140,21 +159,17 @@ namespace Gaming | |||||
| if (objBeingShot == null) | if (objBeingShot == null) | ||||
| { | { | ||||
| CharacterManager.BackSwing((Character?)bullet.Parent, bullet.Backswing); | |||||
| characterManager.BackSwing((Character)bullet.Parent, bullet.Backswing); | |||||
| } | } | ||||
| else | else | ||||
| CharacterManager.BackSwing((Character?)bullet.Parent, bullet.RecoveryFromHit); | |||||
| characterManager.BackSwing((Character)bullet.Parent, bullet.RecoveryFromHit); | |||||
| } | } | ||||
| public bool Attack(Character? player, double angle) | |||||
| public bool Attack(Character player, double angle) | |||||
| { // 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange | { // 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange | ||||
| if (player == null) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| if (player.BulletOfPlayer == BulletType.Null || !player.Commandable()) | |||||
| if (player.BulletOfPlayer == BulletType.Null) | |||||
| return false; | return false; | ||||
| Debugger.Output(player, player.CharacterType.ToString() + "Attack in " + player.BulletOfPlayer.ToString()); | |||||
| XY res = player.Position + new XY // 子弹紧贴人物生成。 | XY res = player.Position + new XY // 子弹紧贴人物生成。 | ||||
| ( | ( | ||||
| @@ -166,15 +181,14 @@ namespace Gaming | |||||
| if (bullet != null) | if (bullet != null) | ||||
| { | { | ||||
| Debugger.Output(player, "Attack in" + bullet.ToString()); | |||||
| Debugger.Output(player, "Attack in " + bullet.ToString()); | |||||
| bullet.AP += player.TryAddAp() ? GameData.ApPropAdd : 0; | bullet.AP += player.TryAddAp() ? GameData.ApPropAdd : 0; | ||||
| bullet.CanMove = true; | bullet.CanMove = true; | ||||
| gameMap.Add(bullet); | gameMap.Add(bullet); | ||||
| moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(player.BulletOfPlayer)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms | moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(player.BulletOfPlayer)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms | ||||
| if (bullet.CastTime > 0) | if (bullet.CastTime > 0) | ||||
| { | { | ||||
| player.PlayerState = PlayerStateType.TryingToAttack; | |||||
| characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); | |||||
| new Thread | new Thread | ||||
| (() => | (() => | ||||
| @@ -184,22 +198,19 @@ namespace Gaming | |||||
| loopToDo: () => | loopToDo: () => | ||||
| { | { | ||||
| }, | }, | ||||
| timeInterval: GameData.frameDuration, | |||||
| timeInterval: GameData.checkInterval, | |||||
| finallyReturn: () => 0, | finallyReturn: () => 0, | ||||
| maxTotalDuration: bullet.CastTime | maxTotalDuration: bullet.CastTime | ||||
| ) | ) | ||||
| .Start(); | .Start(); | ||||
| if (gameMap.Timer.IsGaming) | if (gameMap.Timer.IsGaming) | ||||
| { | { | ||||
| if (player.PlayerState == PlayerStateType.TryingToAttack) | if (player.PlayerState == PlayerStateType.TryingToAttack) | ||||
| { | { | ||||
| player.PlayerState = PlayerStateType.Null; | |||||
| characterManager.SetPlayerState(player); | |||||
| } | } | ||||
| else | |||||
| bullet.IsMoving = false; | |||||
| gameMap.Remove(bullet); | |||||
| else TryRemoveBullet(bullet); | |||||
| } | } | ||||
| } | } | ||||
| ) | ) | ||||
| @@ -7,6 +7,7 @@ using GameEngine; | |||||
| using Preparation.Interface; | using Preparation.Interface; | ||||
| using Timothy.FrameRateTask; | using Timothy.FrameRateTask; | ||||
| using System.Numerics; | using System.Numerics; | ||||
| using System.Timers; | |||||
| namespace Gaming | namespace Gaming | ||||
| { | { | ||||
| @@ -21,6 +22,23 @@ namespace Gaming | |||||
| this.gameMap = gameMap; | this.gameMap = gameMap; | ||||
| } | } | ||||
| public void SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||||
| { | |||||
| switch (player.PlayerState) | |||||
| { | |||||
| case PlayerStateType.OpeningTheChest: | |||||
| ((Chest)player.WhatInteractingWith).StopOpen(); | |||||
| break; | |||||
| case PlayerStateType.OpeningTheDoorway: | |||||
| Doorway doorway = (Doorway)player.WhatInteractingWith; | |||||
| doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime; | |||||
| doorway.OpenStartTime = 0; | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| player.ChangePlayerState(value, gameObj); | |||||
| } | |||||
| public Character? AddPlayer(XY pos, int teamID, int playerID, CharacterType characterType, Character? parent = null) | public Character? AddPlayer(XY pos, int teamID, int playerID, CharacterType characterType, Character? parent = null) | ||||
| { | { | ||||
| @@ -123,8 +141,7 @@ namespace Gaming | |||||
| bgmVolume = newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position); | bgmVolume = newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position); | ||||
| } | } | ||||
| } | } | ||||
| if (bgmVolume > 0) | |||||
| newPlayer.AddBgm(BgmType.StudentIsApproaching, bgmVolume); | |||||
| newPlayer.AddBgm(BgmType.StudentIsApproaching, bgmVolume); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -132,9 +149,13 @@ namespace Gaming | |||||
| { | { | ||||
| if (person.IsGhost()) | if (person.IsGhost()) | ||||
| { | { | ||||
| if (!noise && XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment)) | |||||
| newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position)); | |||||
| if (newPlayer.CharacterType != CharacterType.Teacher && XY.Distance(newPlayer.Position, person.Position) <= newPlayer.ViewRange) | |||||
| if (!noise) | |||||
| { | |||||
| if (XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment)) | |||||
| newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position)); | |||||
| else newPlayer.AddBgm(BgmType.GhostIsComing, 0); | |||||
| } | |||||
| if (newPlayer.CharacterType != CharacterType.Teacher && !newPlayer.NoHp() && newPlayer.PlayerState != PlayerStateType.Stunned && XY.Distance(newPlayer.Position, person.Position) <= GameData.PinningDownRange) | |||||
| { | { | ||||
| TimePinningDown += GameData.checkInterval; | TimePinningDown += GameData.checkInterval; | ||||
| newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded); | newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded); | ||||
| @@ -165,8 +186,7 @@ namespace Gaming | |||||
| bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position); | bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position); | ||||
| } | } | ||||
| } | } | ||||
| if (bgmVolume > 0) | |||||
| newPlayer.AddBgm(BgmType.GeneratorIsBeingFixed, bgmVolume); | |||||
| newPlayer.AddBgm(BgmType.GeneratorIsBeingFixed, bgmVolume); | |||||
| } | } | ||||
| finally | finally | ||||
| { | { | ||||
| @@ -216,7 +236,7 @@ namespace Gaming | |||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| player.PlayerState = PlayerStateType.Addicted; | |||||
| SetPlayerState(player, PlayerStateType.Addicted); | |||||
| new Thread | new Thread | ||||
| (() => | (() => | ||||
| { | { | ||||
| @@ -246,23 +266,23 @@ namespace Gaming | |||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| } | } | ||||
| public static bool BeStunned(Character player, int time) | |||||
| public bool BeStunned(Character player, int time) | |||||
| { | { | ||||
| if (player.PlayerState == PlayerStateType.Stunned || player.NoHp() || player.CharacterType == CharacterType.Robot) return false; | if (player.PlayerState == PlayerStateType.Stunned || player.NoHp() || player.CharacterType == CharacterType.Robot) return false; | ||||
| new Thread | new Thread | ||||
| (() => | (() => | ||||
| { | { | ||||
| player.PlayerState = PlayerStateType.Stunned; | |||||
| SetPlayerState(player, PlayerStateType.Stunned); | |||||
| Thread.Sleep(time); | Thread.Sleep(time); | ||||
| if (player.PlayerState == PlayerStateType.Stunned) | if (player.PlayerState == PlayerStateType.Stunned) | ||||
| player.PlayerState = PlayerStateType.Null; | |||||
| SetPlayerState(player); | |||||
| } | } | ||||
| ) | ) | ||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| return true; | return true; | ||||
| } | } | ||||
| public static bool TryBeAwed(Student character, Bullet bullet) | |||||
| public bool TryBeAwed(Student character, Bullet bullet) | |||||
| { | { | ||||
| if (character.CanBeAwed()) | if (character.CanBeAwed()) | ||||
| { | { | ||||
| @@ -292,6 +312,7 @@ namespace Gaming | |||||
| { | { | ||||
| ((WriteAnswers)student.FindIActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation = 0; | ((WriteAnswers)student.FindIActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation = 0; | ||||
| } | } | ||||
| student.SetDegreeOfTreatment0(); | |||||
| #if DEBUG | #if DEBUG | ||||
| Debugger.Output(bullet, " 's AP is " + bullet.AP.ToString()); | Debugger.Output(bullet, " 's AP is " + bullet.AP.ToString()); | ||||
| #endif | #endif | ||||
| @@ -328,7 +349,6 @@ namespace Gaming | |||||
| bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp)); | bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp)); | ||||
| bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * subHp)); | bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * subHp)); | ||||
| } | } | ||||
| student.SetDegreeOfTreatment0(); | |||||
| if (student.HP <= 0) | if (student.HP <= 0) | ||||
| student.TryActivatingLIFE(); // 如果有复活甲 | student.TryActivatingLIFE(); // 如果有复活甲 | ||||
| @@ -337,11 +357,11 @@ namespace Gaming | |||||
| else TryBeAwed(student, bullet); | else TryBeAwed(student, bullet); | ||||
| } | } | ||||
| public static bool BackSwing(Character? player, int time) | |||||
| public bool BackSwing(Character player, int time) | |||||
| { | { | ||||
| if (player == null || time <= 0) return false; | |||||
| if (time <= 0) return false; | |||||
| if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false; | if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false; | ||||
| player.PlayerState = PlayerStateType.Swinging; | |||||
| SetPlayerState(player, PlayerStateType.Swinging); | |||||
| new Thread | new Thread | ||||
| (() => | (() => | ||||
| @@ -350,7 +370,7 @@ namespace Gaming | |||||
| if (player.PlayerState == PlayerStateType.Swinging) | if (player.PlayerState == PlayerStateType.Swinging) | ||||
| { | { | ||||
| player.PlayerState = PlayerStateType.Null; | |||||
| SetPlayerState(player); | |||||
| } | } | ||||
| } | } | ||||
| ) | ) | ||||
| @@ -167,7 +167,7 @@ namespace Gaming | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | Character? player = gameMap.FindPlayerToAction(playerID); | ||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| return ActionManager.Stop(player); | |||||
| return actionManager.Stop(player); | |||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -215,15 +215,16 @@ namespace Gaming | |||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| public void Attack(long playerID, double angle) | |||||
| public bool Attack(long playerID, double angle) | |||||
| { | { | ||||
| if (!gameMap.Timer.IsGaming) | if (!gameMap.Timer.IsGaming) | ||||
| return; | |||||
| return false; | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | Character? player = gameMap.FindPlayerToAction(playerID); | ||||
| if (player != null) | |||||
| if (player != null && player.Commandable()) | |||||
| { | { | ||||
| _ = attackManager.Attack(player, angle); | |||||
| return attackManager.Attack(player, angle); | |||||
| } | } | ||||
| return false; | |||||
| } | } | ||||
| public void UseProp(long playerID, PropType propType = PropType.Null) | public void UseProp(long playerID, PropType propType = PropType.Null) | ||||
| { | { | ||||
| @@ -232,7 +233,7 @@ namespace Gaming | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | Character? player = gameMap.FindPlayerToAction(playerID); | ||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| PropManager.UseProp(player, propType); | |||||
| propManager.UseProp(player, propType); | |||||
| } | } | ||||
| } | } | ||||
| public void ThrowProp(long playerID, PropType propType = PropType.Null) | public void ThrowProp(long playerID, PropType propType = PropType.Null) | ||||
| @@ -374,7 +375,7 @@ namespace Gaming | |||||
| characterManager = new CharacterManager(gameMap); | characterManager = new CharacterManager(gameMap); | ||||
| attackManager = new AttackManager(gameMap, characterManager); | attackManager = new AttackManager(gameMap, characterManager); | ||||
| actionManager = new ActionManager(gameMap, characterManager); | actionManager = new ActionManager(gameMap, characterManager); | ||||
| propManager = new PropManager(gameMap); | |||||
| propManager = new PropManager(gameMap, characterManager); | |||||
| skillManager = new SkillManager(gameMap, actionManager, attackManager, propManager, characterManager); | skillManager = new SkillManager(gameMap, actionManager, attackManager, propManager, characterManager); | ||||
| } | } | ||||
| } | } | ||||
| @@ -16,10 +16,10 @@ namespace Gaming | |||||
| private class PropManager | private class PropManager | ||||
| { | { | ||||
| private readonly Map gameMap; | private readonly Map gameMap; | ||||
| private readonly CharacterManager characterManager; | |||||
| private readonly List<XY> availableCellForGenerateProp; | private readonly List<XY> availableCellForGenerateProp; | ||||
| public static void UseProp(Character player, PropType propType) | |||||
| public void UseProp(Character player, PropType propType) | |||||
| { | { | ||||
| if (player.IsResetting || player.CharacterType == CharacterType.Robot) | if (player.IsResetting || player.CharacterType == CharacterType.Robot) | ||||
| return; | return; | ||||
| @@ -48,7 +48,7 @@ namespace Gaming | |||||
| if (!player.IsGhost()) | if (!player.IsGhost()) | ||||
| if (player.HP < player.MaxHp) | if (player.HP < player.MaxHp) | ||||
| { | { | ||||
| player.HP += GameData.basicTreatmentDegree; | |||||
| player.HP += GameData.basicTreatmentDegree / 2; | |||||
| player.AddScore(GameData.ScorePropAddHp); | player.AddScore(GameData.ScorePropAddHp); | ||||
| } | } | ||||
| else player.AddAp(GameData.PropDuration); | else player.AddAp(GameData.PropDuration); | ||||
| @@ -57,7 +57,7 @@ namespace Gaming | |||||
| if (player.PlayerState == PlayerStateType.Stunned) | if (player.PlayerState == PlayerStateType.Stunned) | ||||
| { | { | ||||
| player.AddScore(GameData.ScorePropRecoverFromDizziness); | player.AddScore(GameData.ScorePropRecoverFromDizziness); | ||||
| player.PlayerState = PlayerStateType.Null; | |||||
| player.SetPlayerStateNaturally(); | |||||
| } | } | ||||
| break; | break; | ||||
| default: | default: | ||||
| @@ -210,8 +210,9 @@ namespace Gaming | |||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| */ | */ | ||||
| } | } | ||||
| public PropManager(Map gameMap) // 道具不能扔过墙 | |||||
| public PropManager(Map gameMap, CharacterManager characterManager) // 道具不能扔过墙 | |||||
| { | { | ||||
| this.characterManager = characterManager; | |||||
| this.gameMap = gameMap; | this.gameMap = gameMap; | ||||
| /* this.moveEngine = new MoveEngine( | /* this.moveEngine = new MoveEngine( | ||||
| gameMap: gameMap, | gameMap: gameMap, | ||||
| @@ -28,6 +28,50 @@ namespace Gaming | |||||
| { }); | { }); | ||||
| } | } | ||||
| public bool ShowTime(Character player) | |||||
| { | |||||
| if ((!player.Commandable())) return false; | |||||
| IActiveSkill skill = player.FindIActiveSkill(ActiveSkillType.ShowTime); | |||||
| return ActiveSkillEffect(skill, player, () => | |||||
| { | |||||
| new Thread | |||||
| ( | |||||
| () => | |||||
| { | |||||
| new FrameRateTaskExecutor<int>( | |||||
| loopCondition: () => player.Commandable() && gameMap.Timer.IsGaming, | |||||
| loopToDo: () => | |||||
| { | |||||
| gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); | |||||
| try | |||||
| { | |||||
| foreach (Character person in gameMap.GameObjDict[GameObjType.Character]) | |||||
| { | |||||
| if (!person.IsGhost()) | |||||
| actionManager.MovePlayer(person, GameData.frameDuration, (player.Position - person.Position).Angle()); | |||||
| } | |||||
| } | |||||
| finally | |||||
| { | |||||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | |||||
| } | |||||
| }, | |||||
| timeInterval: GameData.frameDuration, | |||||
| finallyReturn: () => 0 | |||||
| ) | |||||
| .Start(); | |||||
| } | |||||
| ) | |||||
| { IsBackground = true }.Start(); | |||||
| }, | |||||
| () => | |||||
| { | |||||
| } | |||||
| ); | |||||
| } | |||||
| public static bool BecomeInvisible(Character player) | public static bool BecomeInvisible(Character player) | ||||
| { | { | ||||
| if ((!player.Commandable())) return false; | if ((!player.Commandable())) return false; | ||||
| @@ -120,8 +164,8 @@ namespace Gaming | |||||
| { | { | ||||
| if (!character.IsGhost() && XY.Distance(character.Position, player.Position) <= player.ViewRange) | if (!character.IsGhost() && XY.Distance(character.Position, player.Position) <= player.ViewRange) | ||||
| { | { | ||||
| if (CharacterManager.BeStunned(character, GameData.TimeOfStudentFaintingWhenHowl)) | |||||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.TimeOfStudentFaintingWhenHowl)); | |||||
| if (characterManager.BeStunned(character, GameData.TimeOfStudentStunnedWhenHowl)) | |||||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.TimeOfStudentStunnedWhenHowl)); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -130,7 +174,7 @@ namespace Gaming | |||||
| { | { | ||||
| gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); | ||||
| } | } | ||||
| CharacterManager.BackSwing(player, GameData.TimeOfGhostSwingingAfterHowl); | |||||
| characterManager.BackSwing(player, GameData.TimeOfGhostSwingingAfterHowl); | |||||
| Debugger.Output(player, "howled!"); | Debugger.Output(player, "howled!"); | ||||
| }, | }, | ||||
| () => | () => | ||||
| @@ -149,11 +193,11 @@ namespace Gaming | |||||
| { | { | ||||
| if (character.IsGhost() && | if (character.IsGhost() && | ||||
| (character.PlayerState == PlayerStateType.TryingToAttack || character.PlayerState == PlayerStateType.Swinging | (character.PlayerState == PlayerStateType.TryingToAttack || character.PlayerState == PlayerStateType.Swinging | ||||
| || character.PlayerState == PlayerStateType.UsingSkill) | |||||
| || character.PlayerState == PlayerStateType.UsingSkill || character.PlayerState == PlayerStateType.LockingOrOpeningTheDoor || character.PlayerState == PlayerStateType.ClimbingThroughWindows) | |||||
| && gameMap.CanSee(player, character)) | && gameMap.CanSee(player, character)) | ||||
| { | { | ||||
| if (CharacterManager.BeStunned(character, GameData.TimeOfGhostFaintingWhenPunish)) | |||||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.TimeOfGhostFaintingWhenPunish)); | |||||
| if (characterManager.BeStunned(character, GameData.TimeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP))) | |||||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.TimeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP))); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -180,7 +224,7 @@ namespace Gaming | |||||
| { | { | ||||
| if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) | if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) | ||||
| { | { | ||||
| character.PlayerState = PlayerStateType.Null; | |||||
| characterManager.SetPlayerState(character); | |||||
| character.HP = GameData.RemainHpWhenAddLife; | character.HP = GameData.RemainHpWhenAddLife; | ||||
| ((Student)character).TimeOfRescue = 0; | ((Student)character).TimeOfRescue = 0; | ||||
| player.AddScore(GameData.StudentScoreRescue); | player.AddScore(GameData.StudentScoreRescue); | ||||
| @@ -210,8 +254,8 @@ namespace Gaming | |||||
| { | { | ||||
| if ((character.HP < character.MaxHp) && gameMap.CanSee(player, character)) | if ((character.HP < character.MaxHp) && gameMap.CanSee(player, character)) | ||||
| { | { | ||||
| player.AddScore(GameData.StudentScoreTreat(character.MaxHp - character.HP)); | |||||
| character.HP = character.MaxHp; | |||||
| player.AddScore(GameData.StudentScoreTreat(GameData.AddHpWhenEncourage)); | |||||
| character.HP += GameData.AddHpWhenEncourage; | |||||
| ((Student)character).SetDegreeOfTreatment0(); | ((Student)character).SetDegreeOfTreatment0(); | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -52,6 +52,9 @@ namespace Gaming | |||||
| case ActiveSkillType.Rouse: | case ActiveSkillType.Rouse: | ||||
| Rouse(character); | Rouse(character); | ||||
| break; | break; | ||||
| case ActiveSkillType.ShowTime: | |||||
| ShowTime(character); | |||||
| break; | |||||
| default: | default: | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -10,8 +10,10 @@ namespace Preparation.Interface | |||||
| public int Score { get; } | public int Score { get; } | ||||
| public void AddScore(int add); | public void AddScore(int add); | ||||
| public double Vampire { get; } | public double Vampire { get; } | ||||
| public PlayerStateType PlayerState { get; set; } | |||||
| public PlayerStateType PlayerState { get; } | |||||
| public BulletType BulletOfPlayer { get; set; } | public BulletType BulletOfPlayer { get; set; } | ||||
| public CharacterType CharacterType { get; } | |||||
| public int BulletNum { get; } | |||||
| public bool IsGhost(); | public bool IsGhost(); | ||||
| } | } | ||||
| @@ -91,6 +91,37 @@ namespace Preparation.Interface | |||||
| public int speedOfOpenChest = (int)(GameData.basicSpeedOfOpenChest * 1.1); | public int speedOfOpenChest = (int)(GameData.basicSpeedOfOpenChest * 1.1); | ||||
| public int SpeedOfOpenChest => speedOfOpenChest; | public int SpeedOfOpenChest => speedOfOpenChest; | ||||
| } | } | ||||
| public class Idol : IGhostType | |||||
| { | |||||
| private const int moveSpeed = GameData.basicGhostMoveSpeed; | |||||
| public int MoveSpeed => moveSpeed; | |||||
| private const int maxHp = GameData.basicHp; | |||||
| public int MaxHp => maxHp; | |||||
| public BulletType InitBullet => BulletType.CommonAttackOfGhost; | |||||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.ShowTime }); | |||||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||||
| public double concealment = GameData.basicConcealment * 3 / 4; | |||||
| public double Concealment => concealment; | |||||
| public int alertnessRadius = GameData.basicGhostAlertnessRadius; | |||||
| public int AlertnessRadius => alertnessRadius; | |||||
| public int viewRange = GameData.basicGhostViewRange * 11 / 10; | |||||
| public int ViewRange => viewRange; | |||||
| public int speedOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking; | |||||
| public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; | |||||
| public int speedOfClimbingThroughWindows = GameData.basicGhostSpeedOfClimbingThroughWindows; | |||||
| public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | |||||
| public int speedOfOpenChest = GameData.basicSpeedOfOpenChest; | |||||
| public int SpeedOfOpenChest => speedOfOpenChest; | |||||
| } | |||||
| public class ANoisyPerson : IGhostType | public class ANoisyPerson : IGhostType | ||||
| { | { | ||||
| private const int moveSpeed = (int)(GameData.basicGhostMoveSpeed * 1.07); | private const int moveSpeed = (int)(GameData.basicGhostMoveSpeed * 1.07); | ||||
| @@ -124,7 +155,7 @@ namespace Preparation.Interface | |||||
| } | } | ||||
| public class Teacher : IStudentType | public class Teacher : IStudentType | ||||
| { | { | ||||
| private const int moveSpeed = GameData.basicStudentMoveSpeed * 3 / 4; | |||||
| private const int moveSpeed = GameData.basicStudentMoveSpeed * 9 / 10; | |||||
| public int MoveSpeed => moveSpeed; | public int MoveSpeed => moveSpeed; | ||||
| private const int maxHp = GameData.basicHp * 10; | private const int maxHp = GameData.basicHp * 10; | ||||
| @@ -141,7 +172,7 @@ namespace Preparation.Interface | |||||
| public const int fixSpeed = 0; | public const int fixSpeed = 0; | ||||
| public int FixSpeed => fixSpeed; | public int FixSpeed => fixSpeed; | ||||
| public const int treatSpeed = (int)(GameData.basicTreatSpeed * 0.7); | |||||
| public const int treatSpeed = (int)(GameData.basicTreatSpeed * 0.8); | |||||
| public int TreatSpeed => treatSpeed; | public int TreatSpeed => treatSpeed; | ||||
| public const double concealment = GameData.basicConcealment * 0.5; | public const double concealment = GameData.basicConcealment * 0.5; | ||||
| @@ -164,7 +195,7 @@ namespace Preparation.Interface | |||||
| } | } | ||||
| public class Athlete : IStudentType | public class Athlete : IStudentType | ||||
| { | { | ||||
| private const int moveSpeed = GameData.basicStudentMoveSpeed * 11 / 10; | |||||
| private const int moveSpeed = GameData.basicStudentMoveSpeed * 105 / 100; | |||||
| public int MoveSpeed => moveSpeed; | public int MoveSpeed => moveSpeed; | ||||
| private const int maxHp = GameData.basicHp; | private const int maxHp = GameData.basicHp; | ||||
| @@ -181,7 +212,7 @@ namespace Preparation.Interface | |||||
| public const int fixSpeed = GameData.basicFixSpeed * 6 / 10; | public const int fixSpeed = GameData.basicFixSpeed * 6 / 10; | ||||
| public int FixSpeed => fixSpeed; | public int FixSpeed => fixSpeed; | ||||
| public const int treatSpeed = GameData.basicTreatSpeed * 8 / 10; | |||||
| public const int treatSpeed = GameData.basicTreatSpeed * 9 / 10; | |||||
| public int TreatSpeed => treatSpeed; | public int TreatSpeed => treatSpeed; | ||||
| public const double concealment = GameData.basicConcealment * 0.9; | public const double concealment = GameData.basicConcealment * 0.9; | ||||
| @@ -204,7 +235,7 @@ namespace Preparation.Interface | |||||
| } | } | ||||
| public class StraightAStudent : IStudentType | public class StraightAStudent : IStudentType | ||||
| { | { | ||||
| private const int moveSpeed = (int)(GameData.basicStudentMoveSpeed * 0.8); | |||||
| private const int moveSpeed = (int)(GameData.basicStudentMoveSpeed * 0.96); | |||||
| public int MoveSpeed => moveSpeed; | public int MoveSpeed => moveSpeed; | ||||
| private const int maxHp = (int)(GameData.basicHp * 1.1); | private const int maxHp = (int)(GameData.basicHp * 1.1); | ||||
| @@ -221,7 +252,7 @@ namespace Preparation.Interface | |||||
| public const int fixSpeed = GameData.basicFixSpeed * 11 / 10; | public const int fixSpeed = GameData.basicFixSpeed * 11 / 10; | ||||
| public int FixSpeed => fixSpeed; | public int FixSpeed => fixSpeed; | ||||
| public const int treatSpeed = GameData.basicTreatSpeed * 8 / 10; | |||||
| public const int treatSpeed = GameData.basicTreatSpeed; | |||||
| public int TreatSpeed => treatSpeed; | public int TreatSpeed => treatSpeed; | ||||
| public const double concealment = GameData.basicConcealment * 0.9; | public const double concealment = GameData.basicConcealment * 0.9; | ||||
| @@ -261,7 +292,7 @@ namespace Preparation.Interface | |||||
| public const int fixSpeed = GameData.basicFixSpeed; | public const int fixSpeed = GameData.basicFixSpeed; | ||||
| public int FixSpeed => fixSpeed; | public int FixSpeed => fixSpeed; | ||||
| public const int treatSpeed = GameData.basicTreatSpeed * 8 / 10; | |||||
| public const int treatSpeed = 0; | |||||
| public int TreatSpeed => treatSpeed; | public int TreatSpeed => treatSpeed; | ||||
| public const double concealment = GameData.basicConcealment; | public const double concealment = GameData.basicConcealment; | ||||
| @@ -341,7 +372,7 @@ namespace Preparation.Interface | |||||
| public const int fixSpeed = GameData.basicFixSpeed; | public const int fixSpeed = GameData.basicFixSpeed; | ||||
| public int FixSpeed => fixSpeed; | public int FixSpeed => fixSpeed; | ||||
| public const int treatSpeed = GameData.basicTreatSpeed * 2; | |||||
| public const int treatSpeed = GameData.basicTreatSpeed * 12 / 10; | |||||
| public int TreatSpeed => treatSpeed; | public int TreatSpeed => treatSpeed; | ||||
| public const double concealment = GameData.basicConcealment; | public const double concealment = GameData.basicConcealment; | ||||
| @@ -353,13 +384,13 @@ namespace Preparation.Interface | |||||
| public int viewRange = GameData.basicStudentViewRange; | public int viewRange = GameData.basicStudentViewRange; | ||||
| public int ViewRange => viewRange; | public int ViewRange => viewRange; | ||||
| public int speedOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking; | |||||
| public int speedOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking * 7 / 10; | |||||
| public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; | public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; | ||||
| public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows; | public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows; | ||||
| public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | ||||
| public int speedOfOpenChest = GameData.basicSpeedOfOpenChest; | |||||
| public int speedOfOpenChest = GameData.basicSpeedOfOpenChest * 9 / 10; | |||||
| public int SpeedOfOpenChest => speedOfOpenChest; | public int SpeedOfOpenChest => speedOfOpenChest; | ||||
| } | } | ||||
| @@ -383,6 +414,8 @@ namespace Preparation.Interface | |||||
| return new ANoisyPerson(); | return new ANoisyPerson(); | ||||
| case CharacterType.TechOtaku: | case CharacterType.TechOtaku: | ||||
| return new TechOtaku(); | return new TechOtaku(); | ||||
| case CharacterType.Idol: | |||||
| return new Idol(); | |||||
| case CharacterType.Athlete: | case CharacterType.Athlete: | ||||
| default: | default: | ||||
| return new Athlete(); | return new Athlete(); | ||||
| @@ -18,8 +18,8 @@ namespace Preparation.Interface | |||||
| } | } | ||||
| public class CanBeginToCharge : IActiveSkill | public class CanBeginToCharge : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.commonSkillCD * 24 / 30; | |||||
| public int DurationTime => GameData.commonSkillTime * 5 / 10; | |||||
| public int SkillCD => GameData.commonSkillCD * 2; | |||||
| public int DurationTime => GameData.commonSkillTime * 3 / 10; | |||||
| private readonly object commonSkillLock = new object(); | private readonly object commonSkillLock = new object(); | ||||
| public object ActiveSkillLock => commonSkillLock; | public object ActiveSkillLock => commonSkillLock; | ||||
| @@ -33,8 +33,8 @@ namespace Preparation.Interface | |||||
| public class BecomeInvisible : IActiveSkill | public class BecomeInvisible : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.commonSkillCD; | |||||
| public int DurationTime => GameData.commonSkillTime * 6 / 10; | |||||
| public int SkillCD => GameData.commonSkillCD * 4 / 3; | |||||
| public int DurationTime => GameData.commonSkillTime; | |||||
| private readonly object commonSkillLock = new object(); | private readonly object commonSkillLock = new object(); | ||||
| public object ActiveSkillLock => commonSkillLock; | public object ActiveSkillLock => commonSkillLock; | ||||
| @@ -63,7 +63,7 @@ namespace Preparation.Interface | |||||
| public class Rouse : IActiveSkill | public class Rouse : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.commonSkillCD * 2; | |||||
| public int SkillCD => GameData.commonSkillCD * 4; | |||||
| public int DurationTime => 0; | public int DurationTime => 0; | ||||
| private readonly object commonSkillLock = new object(); | private readonly object commonSkillLock = new object(); | ||||
| @@ -78,7 +78,7 @@ namespace Preparation.Interface | |||||
| public class Encourage : IActiveSkill | public class Encourage : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.commonSkillCD * 2; | |||||
| public int SkillCD => GameData.commonSkillCD * 4; | |||||
| public int DurationTime => 0; | public int DurationTime => 0; | ||||
| private readonly object commonSkillLock = new object(); | private readonly object commonSkillLock = new object(); | ||||
| @@ -93,7 +93,7 @@ namespace Preparation.Interface | |||||
| public class Inspire : IActiveSkill | public class Inspire : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.commonSkillCD * 2; | |||||
| public int SkillCD => GameData.commonSkillCD * 4; | |||||
| public int DurationTime => 0; | public int DurationTime => 0; | ||||
| private readonly object commonSkillLock = new object(); | private readonly object commonSkillLock = new object(); | ||||
| @@ -121,6 +121,21 @@ namespace Preparation.Interface | |||||
| } | } | ||||
| } | } | ||||
| public class ShowTime : IActiveSkill | |||||
| { | |||||
| public int SkillCD => GameData.commonSkillCD * 3; | |||||
| public int DurationTime => GameData.commonSkillTime; | |||||
| private readonly object commonSkillLock = new(); | |||||
| public object ActiveSkillLock => commonSkillLock; | |||||
| public bool isBeingUsed = false; | |||||
| public bool IsBeingUsed | |||||
| { | |||||
| get => isBeingUsed; set => isBeingUsed = value; | |||||
| } | |||||
| } | |||||
| public class JumpyBomb : IActiveSkill | public class JumpyBomb : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.commonSkillCD / 2; | public int SkillCD => GameData.commonSkillCD / 2; | ||||
| @@ -137,7 +152,7 @@ namespace Preparation.Interface | |||||
| public class UseKnife : IActiveSkill | public class UseKnife : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.commonSkillCD * 2 / 3; | |||||
| public int SkillCD => GameData.commonSkillCD; | |||||
| public int DurationTime => GameData.commonSkillTime / 10; | public int DurationTime => GameData.commonSkillTime / 10; | ||||
| private readonly object commonSkillLock = new object(); | private readonly object commonSkillLock = new object(); | ||||
| public object ActiveSkillLock => commonSkillLock; | public object ActiveSkillLock => commonSkillLock; | ||||
| @@ -151,9 +166,9 @@ namespace Preparation.Interface | |||||
| public class UseRobot : IActiveSkill | public class UseRobot : IActiveSkill | ||||
| { | { | ||||
| public int SkillCD => GameData.frameDuration; | |||||
| public int SkillCD => GameData.commonSkillCD / 300; | |||||
| public int DurationTime => 0; | public int DurationTime => 0; | ||||
| private readonly object commonSkillLock = new object(); | |||||
| private readonly object commonSkillLock = new(); | |||||
| public object ActiveSkillLock => commonSkillLock; | public object ActiveSkillLock => commonSkillLock; | ||||
| public bool isBeingUsed = false; | public bool isBeingUsed = false; | ||||
| @@ -260,6 +275,8 @@ namespace Preparation.Interface | |||||
| return new Rouse(); | return new Rouse(); | ||||
| case ActiveSkillType.Inspire: | case ActiveSkillType.Inspire: | ||||
| return new Inspire(); | return new Inspire(); | ||||
| case ActiveSkillType.ShowTime: | |||||
| return new ShowTime(); | |||||
| default: | default: | ||||
| return new NullSkill(); | return new NullSkill(); | ||||
| } | } | ||||
| @@ -293,6 +310,8 @@ namespace Preparation.Interface | |||||
| return ActiveSkillType.UseRobot; | return ActiveSkillType.UseRobot; | ||||
| case Rouse: | case Rouse: | ||||
| return ActiveSkillType.Rouse; | return ActiveSkillType.Rouse; | ||||
| case ShowTime: | |||||
| return ActiveSkillType.ShowTime; | |||||
| default: | default: | ||||
| return ActiveSkillType.Null; | return ActiveSkillType.Null; | ||||
| } | } | ||||
| @@ -83,6 +83,7 @@ namespace Preparation.Utility | |||||
| ANoisyPerson = 7, | ANoisyPerson = 7, | ||||
| Robot = 8, | Robot = 8, | ||||
| Sunshine = 9, | Sunshine = 9, | ||||
| Idol = 10, | |||||
| } | } | ||||
| public enum ActiveSkillType // 主动技能 | public enum ActiveSkillType // 主动技能 | ||||
| { | { | ||||
| @@ -100,6 +101,7 @@ namespace Preparation.Utility | |||||
| Rouse = 11, | Rouse = 11, | ||||
| Encourage = 12, | Encourage = 12, | ||||
| Inspire = 13, | Inspire = 13, | ||||
| ShowTime = 14, | |||||
| } | } | ||||
| public enum PassiveSkillType | public enum PassiveSkillType | ||||
| { | { | ||||
| @@ -15,6 +15,8 @@ namespace Preparation.Utility | |||||
| public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | ||||
| public const long gameDuration = 600000; // 游戏时长600000ms = 10min | public const long gameDuration = 600000; // 游戏时长600000ms = 10min | ||||
| public const int LimitOfStopAndMove = 15; | |||||
| public const int MinSpeed = 1; // 最小速度 | public const int MinSpeed = 1; // 最小速度 | ||||
| public const int MaxSpeed = int.MaxValue; // 最大速度 | public const int MaxSpeed = int.MaxValue; // 最大速度 | ||||
| @@ -90,7 +92,7 @@ namespace Preparation.Utility | |||||
| public const int basicTreatSpeed = 100; | public const int basicTreatSpeed = 100; | ||||
| public const int basicFixSpeed = 123; | public const int basicFixSpeed = 123; | ||||
| public const int basicSpeedOfOpeningOrLocking = 4130; | |||||
| public const int basicSpeedOfOpeningOrLocking = 4000; | |||||
| public const int basicStudentSpeedOfClimbingThroughWindows = 611; | public const int basicStudentSpeedOfClimbingThroughWindows = 611; | ||||
| public const int basicGhostSpeedOfClimbingThroughWindows = 1270; | public const int basicGhostSpeedOfClimbingThroughWindows = 1270; | ||||
| public const int basicSpeedOfOpenChest = 1000; | public const int basicSpeedOfOpenChest = 1000; | ||||
| @@ -105,9 +107,9 @@ namespace Preparation.Utility | |||||
| #if DEBUG | #if DEBUG | ||||
| public const int basicStudentMoveSpeed = 9000;// 基本移动速度,单位:s-1 | public const int basicStudentMoveSpeed = 9000;// 基本移动速度,单位:s-1 | ||||
| #else | #else | ||||
| public const int basicStudentMoveSpeed = 1270; | |||||
| public const int basicStudentMoveSpeed = 1500; | |||||
| #endif | #endif | ||||
| public const int basicGhostMoveSpeed = (int)(basicStudentMoveSpeed * 45.0 / 38); | |||||
| public const int basicGhostMoveSpeed = (int)(basicStudentMoveSpeed * 1.2); | |||||
| public const int characterMaxSpeed = 12000; // 最大速度 | public const int characterMaxSpeed = 12000; // 最大速度 | ||||
| @@ -116,6 +118,8 @@ namespace Preparation.Utility | |||||
| public const int basicGhostAlertnessRadius = 17 * numOfPosGridPerCell; | public const int basicGhostAlertnessRadius = 17 * numOfPosGridPerCell; | ||||
| public const int basicStudentViewRange = 10 * numOfPosGridPerCell; | public const int basicStudentViewRange = 10 * numOfPosGridPerCell; | ||||
| public const int basicGhostViewRange = 15 * numOfPosGridPerCell; | public const int basicGhostViewRange = 15 * numOfPosGridPerCell; | ||||
| public const int PinningDownRange = 5 * numOfPosGridPerCell; | |||||
| public const int maxNumOfPropInPropInventory = 3; | public const int maxNumOfPropInPropInventory = 3; | ||||
| public static XY PosWhoDie = new XY(1, 1); | public static XY PosWhoDie = new XY(1, 1); | ||||
| @@ -127,6 +131,7 @@ namespace Preparation.Utility | |||||
| CharacterType.Assassin => true, | CharacterType.Assassin => true, | ||||
| CharacterType.Klee => true, | CharacterType.Klee => true, | ||||
| CharacterType.ANoisyPerson => true, | CharacterType.ANoisyPerson => true, | ||||
| CharacterType.Idol => true, | |||||
| _ => false, | _ => false, | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -137,47 +142,47 @@ namespace Preparation.Utility | |||||
| return damage * 100 / basicApOfGhost; | return damage * 100 / basicApOfGhost; | ||||
| } | } | ||||
| public const int TrickerScoreStudentBeAddicted = 50; | public const int TrickerScoreStudentBeAddicted = 50; | ||||
| public const int TrickerScoreDestroyRobot = 20; | |||||
| public const int TrickerScoreDestroyRobot = 30; | |||||
| public const int TrickerScoreStudentDie = 1000; | public const int TrickerScoreStudentDie = 1000; | ||||
| public static int TrickerScoreStudentBeStunned(int time) | public static int TrickerScoreStudentBeStunned(int time) | ||||
| { | { | ||||
| return time; | |||||
| return time * 20 / 1000; | |||||
| } | } | ||||
| public static int TrickerScoreDamageGenerator(int degree) | public static int TrickerScoreDamageGenerator(int degree) | ||||
| { | { | ||||
| return degree * 200 / degreeOfFixedGenerator; | |||||
| return degree * 100 / degreeOfFixedGenerator; | |||||
| } | } | ||||
| public static int StudentScoreFix(int degreeOfFix) | public static int StudentScoreFix(int degreeOfFix) | ||||
| { | { | ||||
| return degreeOfFix * 200 / degreeOfFixedGenerator; | return degreeOfFix * 200 / degreeOfFixedGenerator; | ||||
| } | } | ||||
| public const int StudentScoreFixed = 25; | |||||
| //public const int StudentScoreFixed = 25; | |||||
| public static int StudentScorePinDown(int timeOfPinningDown) | public static int StudentScorePinDown(int timeOfPinningDown) | ||||
| { | { | ||||
| return 0; | |||||
| return (int)(timeOfPinningDown * 0.00246); | |||||
| } | } | ||||
| public static int StudentScoreTrickerBeStunned(int time) | public static int StudentScoreTrickerBeStunned(int time) | ||||
| { | { | ||||
| return time; | |||||
| return time * 20 / 1000; | |||||
| } | } | ||||
| public const int StudentScoreRescue = 100; | public const int StudentScoreRescue = 100; | ||||
| public static int StudentScoreTreat(int degree) | public static int StudentScoreTreat(int degree) | ||||
| { | { | ||||
| return degree; | |||||
| return degree * 50 / basicTreatmentDegree; | |||||
| } | } | ||||
| public const int StudentScoreEscape = 1000; | public const int StudentScoreEscape = 1000; | ||||
| public const int ScorePropRemainHp = 20; | |||||
| public const int ScorePropUseShield = 20; | |||||
| public const int ScorePropUseSpear = 20; | |||||
| public const int ScorePropAddAp = 10; | |||||
| public const int ScorePropRemainHp = 100; | |||||
| public const int ScorePropUseShield = 50; | |||||
| public const int ScorePropUseSpear = 50; | |||||
| public const int ScorePropAddAp = 0; | |||||
| public const int ScorePropAddSpeed = 10; | public const int ScorePropAddSpeed = 10; | ||||
| public const int ScorePropClairaudience = 10; | public const int ScorePropClairaudience = 10; | ||||
| public const int ScorePropAddHp = 20; | |||||
| public const int ScorePropRecoverFromDizziness = 20; | |||||
| public const int ScorePropAddHp = 10; | |||||
| public const int ScorePropRecoverFromDizziness = 30; | |||||
| public const int ScoreBecomeInvisible = 10; | |||||
| public const int ScoreBecomeInvisible = 15; | |||||
| public const int ScoreInspire = ScorePropAddSpeed; | public const int ScoreInspire = ScorePropAddSpeed; | ||||
| #endregion | #endregion | ||||
| #region 攻击与子弹相关 | #region 攻击与子弹相关 | ||||
| @@ -185,7 +190,7 @@ namespace Preparation.Utility | |||||
| public const int MinAP = 0; // 最小攻击力 | public const int MinAP = 0; // 最小攻击力 | ||||
| public const int MaxAP = int.MaxValue; // 最大攻击力 | public const int MaxAP = int.MaxValue; // 最大攻击力 | ||||
| public const int factorDamageGenerator = 2;//子弹对电机的破坏=factorDamageGenerator*AP; | |||||
| public const int factorDamageGenerator = 1;//子弹对电机的破坏=factorDamageGenerator*AP; | |||||
| public const int bulletRadius = 200; // 默认子弹半径 | public const int bulletRadius = 200; // 默认子弹半径 | ||||
| public const int basicBulletNum = 3; // 基本初始子弹量 | public const int basicBulletNum = 3; // 基本初始子弹量 | ||||
| @@ -205,19 +210,22 @@ namespace Preparation.Utility | |||||
| public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | ||||
| public const int commonSkillTime = 10000; // 普通技能标准持续时间 | public const int commonSkillTime = 10000; // 普通技能标准持续时间 | ||||
| public const int TimeOfGhostFaintingWhenCharge = 7220; | |||||
| public const int TimeOfStudentFaintingWhenCharge = 2090; | |||||
| public const int TimeOfGhostStunnedWhenCharge = 7220; | |||||
| public const int TimeOfStudentStunnedWhenCharge = 2090; | |||||
| public const int TimeOfGhostFaintingWhenPunish = 3070; | |||||
| public const int TimeOfGhostStunnedWhenPunish = 3070; | |||||
| public const int factorOfTimeStunnedWhenPunish = 300 / basicApOfGhost; | |||||
| public const int TimeOfGhostSwingingAfterHowl = 3070; | |||||
| public const int TimeOfStudentFaintingWhenHowl = 6110; | |||||
| public const int TimeOfGhostSwingingAfterHowl = 800; | |||||
| public const int TimeOfStudentStunnedWhenHowl = 5500; | |||||
| public const int TimeOfStunnedWhenJumpyDumpty = 3070; | public const int TimeOfStunnedWhenJumpyDumpty = 3070; | ||||
| public const double AddedTimeOfSpeedWhenInspire = 0.6; | public const double AddedTimeOfSpeedWhenInspire = 0.6; | ||||
| public const int TimeOfAddingSpeedWhenInspire = 6000; | public const int TimeOfAddingSpeedWhenInspire = 6000; | ||||
| public const int AddHpWhenEncourage = basicHp / 4; | |||||
| #endregion | #endregion | ||||
| #region 道具相关 | #region 道具相关 | ||||
| public const int PropRadius = numOfPosGridPerCell / 2; | public const int PropRadius = numOfPosGridPerCell / 2; | ||||
| @@ -233,6 +233,8 @@ namespace Preparation.Utility | |||||
| return Protobuf.TrickerType.Klee; | return Protobuf.TrickerType.Klee; | ||||
| case Preparation.Utility.CharacterType.ANoisyPerson: | case Preparation.Utility.CharacterType.ANoisyPerson: | ||||
| return Protobuf.TrickerType.ANoisyPerson; | return Protobuf.TrickerType.ANoisyPerson; | ||||
| case CharacterType.Idol: | |||||
| return TrickerType.Idol; | |||||
| default: | default: | ||||
| return Protobuf.TrickerType.NullTrickerType; | return Protobuf.TrickerType.NullTrickerType; | ||||
| } | } | ||||
| @@ -247,6 +249,8 @@ namespace Preparation.Utility | |||||
| return Preparation.Utility.CharacterType.Klee; | return Preparation.Utility.CharacterType.Klee; | ||||
| case Protobuf.TrickerType.ANoisyPerson: | case Protobuf.TrickerType.ANoisyPerson: | ||||
| return Preparation.Utility.CharacterType.ANoisyPerson; | return Preparation.Utility.CharacterType.ANoisyPerson; | ||||
| case TrickerType.Idol: | |||||
| return CharacterType.Idol; | |||||
| default: | default: | ||||
| return Preparation.Utility.CharacterType.Null; | return Preparation.Utility.CharacterType.Null; | ||||
| } | } | ||||
| @@ -75,10 +75,7 @@ namespace Preparation.Utility | |||||
| return Math.Atan2(y, x); | return Math.Atan2(y, x); | ||||
| } | } | ||||
| public override bool Equals(object obj) | |||||
| { | |||||
| throw new NotImplementedException(); | |||||
| } | |||||
| public override bool Equals(object obj) => throw new NotImplementedException(); | |||||
| public override int GetHashCode() | public override int GetHashCode() | ||||
| { | { | ||||
| @@ -3,13 +3,14 @@ using System.Collections.Generic; | |||||
| using GameClass.GameObj; | using GameClass.GameObj; | ||||
| using System.Numerics; | using System.Numerics; | ||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| using Gaming; | |||||
| namespace Server | namespace Server | ||||
| { | { | ||||
| public static class CopyInfo | public static class CopyInfo | ||||
| { | { | ||||
| public static MessageOfObj? Auto(GameObj gameObj) | |||||
| public static MessageOfObj? Auto(GameObj gameObj, int time) | |||||
| { | { | ||||
| switch (gameObj.Type) | switch (gameObj.Type) | ||||
| { | { | ||||
| @@ -29,9 +30,9 @@ namespace Server | |||||
| case Preparation.Utility.GameObjType.Generator: | case Preparation.Utility.GameObjType.Generator: | ||||
| return Classroom((Generator)gameObj); | return Classroom((Generator)gameObj); | ||||
| case Preparation.Utility.GameObjType.Chest: | case Preparation.Utility.GameObjType.Chest: | ||||
| return Chest((Chest)gameObj); | |||||
| return Chest((Chest)gameObj, time); | |||||
| case Preparation.Utility.GameObjType.Doorway: | case Preparation.Utility.GameObjType.Doorway: | ||||
| return Gate((Doorway)gameObj); | |||||
| return Gate((Doorway)gameObj, time); | |||||
| case Preparation.Utility.GameObjType.EmergencyExit: | case Preparation.Utility.GameObjType.EmergencyExit: | ||||
| if (((EmergencyExit)gameObj).CanOpen) | if (((EmergencyExit)gameObj).CanOpen) | ||||
| return HiddenGate((EmergencyExit)gameObj); | return HiddenGate((EmergencyExit)gameObj); | ||||
| @@ -43,133 +44,156 @@ namespace Server | |||||
| } | } | ||||
| public static MessageOfObj? Auto(MessageOfNews news) | public static MessageOfObj? Auto(MessageOfNews news) | ||||
| { | { | ||||
| MessageOfObj objMsg = new(); | |||||
| objMsg.NewsMessage = news; | |||||
| MessageOfObj objMsg = new() | |||||
| { | |||||
| NewsMessage = news | |||||
| }; | |||||
| return objMsg; | return objMsg; | ||||
| } | } | ||||
| private static MessageOfObj? Student(Student player) | private static MessageOfObj? Student(Student player) | ||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| if (player.IsGhost()) return null; | if (player.IsGhost()) return null; | ||||
| msg.StudentMessage = new(); | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| StudentMessage = new() | |||||
| { | |||||
| X = player.Position.x, | |||||
| Y = player.Position.y, | |||||
| Speed = player.MoveSpeed, | |||||
| Determination = player.HP, | |||||
| Addiction = player.GamingAddiction, | |||||
| Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)player.Place), | |||||
| Guid = player.ID, | |||||
| PlayerState = Transformation.ToPlayerState((PlayerStateType)player.PlayerState), | |||||
| PlayerId = player.PlayerID, | |||||
| ViewRange = player.ViewRange, | |||||
| Radius = player.Radius, | |||||
| DangerAlert = (player.BgmDictionary.ContainsKey(BgmType.GhostIsComing)) ? player.BgmDictionary[BgmType.GhostIsComing] : 0, | |||||
| Score = player.Score, | |||||
| TreatProgress = player.DegreeOfTreatment, | |||||
| RescueProgress = player.TimeOfRescue, | |||||
| msg.StudentMessage.X = player.Position.x; | |||||
| msg.StudentMessage.Y = player.Position.y; | |||||
| msg.StudentMessage.Speed = player.MoveSpeed; | |||||
| msg.StudentMessage.Determination = player.HP; | |||||
| msg.StudentMessage.Addiction = player.GamingAddiction; | |||||
| BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer), | |||||
| LearningSpeed = player.FixSpeed, | |||||
| TreatSpeed = player.TreatSpeed, | |||||
| FacingDirection = player.FacingDirection.Angle(), | |||||
| StudentType = Transformation.ToStudentType(player.CharacterType) | |||||
| } | |||||
| }; | |||||
| foreach (var keyValue in player.TimeUntilActiveSkillAvailable) | foreach (var keyValue in player.TimeUntilActiveSkillAvailable) | ||||
| msg.StudentMessage.TimeUntilSkillAvailable.Add(keyValue.Value); | msg.StudentMessage.TimeUntilSkillAvailable.Add(keyValue.Value); | ||||
| for (int i = 0; i < GameData.maxNumOfSkill - player.TimeUntilActiveSkillAvailable.Count(); ++i) | |||||
| for (int i = 0; i < GameData.maxNumOfSkill - player.TimeUntilActiveSkillAvailable.Count; ++i) | |||||
| msg.StudentMessage.TimeUntilSkillAvailable.Add(-1); | msg.StudentMessage.TimeUntilSkillAvailable.Add(-1); | ||||
| foreach (var value in player.PropInventory) | foreach (var value in player.PropInventory) | ||||
| msg.StudentMessage.Prop.Add(Transformation.ToPropType(value.GetPropType())); | msg.StudentMessage.Prop.Add(Transformation.ToPropType(value.GetPropType())); | ||||
| msg.StudentMessage.Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)player.Place); | |||||
| msg.StudentMessage.Guid = player.ID; | |||||
| msg.StudentMessage.PlayerState = Transformation.ToPlayerState((PlayerStateType)player.PlayerState); | |||||
| msg.StudentMessage.PlayerId = player.PlayerID; | |||||
| msg.StudentMessage.ViewRange = player.ViewRange; | |||||
| msg.StudentMessage.Radius = player.Radius; | |||||
| msg.StudentMessage.DangerAlert = (player.BgmDictionary.ContainsKey(BgmType.GhostIsComing)) ? player.BgmDictionary[BgmType.GhostIsComing] : 0; | |||||
| msg.StudentMessage.Score = player.Score; | |||||
| msg.StudentMessage.TreatProgress = player.DegreeOfTreatment; | |||||
| msg.StudentMessage.RescueProgress = player.TimeOfRescue; | |||||
| foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | ||||
| { | { | ||||
| if (kvp.Value) | if (kvp.Value) | ||||
| msg.StudentMessage.Buff.Add(Transformation.ToStudentBuffType(kvp.Key)); | msg.StudentMessage.Buff.Add(Transformation.ToStudentBuffType(kvp.Key)); | ||||
| } | } | ||||
| msg.StudentMessage.BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer); | |||||
| msg.StudentMessage.LearningSpeed = player.FixSpeed; | |||||
| msg.StudentMessage.TreatSpeed = player.TreatSpeed; | |||||
| msg.StudentMessage.FacingDirection = player.FacingDirection.Angle(); | |||||
| msg.StudentMessage.StudentType = Transformation.ToStudentType(player.CharacterType); | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| private static MessageOfObj? Tricker(Character player) | private static MessageOfObj? Tricker(Character player) | ||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| if (!player.IsGhost()) return null; | if (!player.IsGhost()) return null; | ||||
| msg.TrickerMessage = new(); | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| TrickerMessage = new() | |||||
| { | |||||
| X = player.Position.x, | |||||
| Y = player.Position.y, | |||||
| Speed = player.MoveSpeed, | |||||
| Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)player.Place), | |||||
| TrickerType = Transformation.ToTrickerType(player.CharacterType), | |||||
| Guid = player.ID, | |||||
| Score = player.Score, | |||||
| PlayerId = player.PlayerID, | |||||
| ViewRange = player.ViewRange, | |||||
| Radius = player.Radius, | |||||
| PlayerState = Transformation.ToPlayerState((PlayerStateType)player.PlayerState), | |||||
| TrickDesire = (player.BgmDictionary.ContainsKey(BgmType.StudentIsApproaching)) ? player.BgmDictionary[BgmType.StudentIsApproaching] : 0, | |||||
| ClassVolume = (player.BgmDictionary.ContainsKey(BgmType.GeneratorIsBeingFixed)) ? player.BgmDictionary[BgmType.GeneratorIsBeingFixed] : 0, | |||||
| FacingDirection = player.FacingDirection.Angle(), | |||||
| BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer) | |||||
| } | |||||
| }; | |||||
| msg.TrickerMessage.X = player.Position.x; | |||||
| msg.TrickerMessage.Y = player.Position.y; | |||||
| msg.TrickerMessage.Speed = player.MoveSpeed; | |||||
| foreach (var keyValue in player.TimeUntilActiveSkillAvailable) | foreach (var keyValue in player.TimeUntilActiveSkillAvailable) | ||||
| msg.TrickerMessage.TimeUntilSkillAvailable.Add(keyValue.Value); | msg.TrickerMessage.TimeUntilSkillAvailable.Add(keyValue.Value); | ||||
| for (int i = 0; i < GameData.maxNumOfSkill - player.TimeUntilActiveSkillAvailable.Count(); ++i) | |||||
| for (int i = 0; i < GameData.maxNumOfSkill - player.TimeUntilActiveSkillAvailable.Count; ++i) | |||||
| msg.TrickerMessage.TimeUntilSkillAvailable.Add(-1); | msg.TrickerMessage.TimeUntilSkillAvailable.Add(-1); | ||||
| msg.TrickerMessage.Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)player.Place); | |||||
| foreach (var value in player.PropInventory) | foreach (var value in player.PropInventory) | ||||
| msg.TrickerMessage.Prop.Add(Transformation.ToPropType(value.GetPropType())); | msg.TrickerMessage.Prop.Add(Transformation.ToPropType(value.GetPropType())); | ||||
| msg.TrickerMessage.TrickerType = Transformation.ToTrickerType(player.CharacterType); | |||||
| msg.TrickerMessage.Guid = player.ID; | |||||
| msg.TrickerMessage.Score = player.Score; | |||||
| msg.TrickerMessage.PlayerId = player.PlayerID; | |||||
| msg.TrickerMessage.ViewRange = player.ViewRange; | |||||
| msg.TrickerMessage.Radius = player.Radius; | |||||
| msg.TrickerMessage.PlayerState = Transformation.ToPlayerState((PlayerStateType)player.PlayerState); | |||||
| msg.TrickerMessage.TrickDesire = (player.BgmDictionary.ContainsKey(BgmType.StudentIsApproaching)) ? player.BgmDictionary[BgmType.StudentIsApproaching] : 0; | |||||
| msg.TrickerMessage.ClassVolume = (player.BgmDictionary.ContainsKey(BgmType.GeneratorIsBeingFixed)) ? player.BgmDictionary[BgmType.GeneratorIsBeingFixed] : 0; | |||||
| msg.TrickerMessage.FacingDirection = player.FacingDirection.Angle(); | |||||
| msg.TrickerMessage.BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer); | |||||
| foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | ||||
| { | { | ||||
| if (kvp.Value) | if (kvp.Value) | ||||
| msg.TrickerMessage.Buff.Add(Transformation.ToTrickerBuffType(kvp.Key)); | msg.TrickerMessage.Buff.Add(Transformation.ToTrickerBuffType(kvp.Key)); | ||||
| } | } | ||||
| return msg; | return msg; | ||||
| } | } | ||||
| private static MessageOfObj Bullet(Bullet bullet) | private static MessageOfObj Bullet(Bullet bullet) | ||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| msg.BulletMessage = new(); | |||||
| msg.BulletMessage.X = bullet.Position.x; | |||||
| msg.BulletMessage.Y = bullet.Position.y; | |||||
| msg.BulletMessage.FacingDirection = bullet.FacingDirection.Angle(); | |||||
| msg.BulletMessage.Guid = bullet.ID; | |||||
| msg.BulletMessage.Team = (bullet.Parent.IsGhost()) ? PlayerType.TrickerPlayer : PlayerType.StudentPlayer; | |||||
| msg.BulletMessage.Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)bullet.Place); | |||||
| msg.BulletMessage.BombRange = bullet.BulletBombRange; | |||||
| msg.BulletMessage.Speed = bullet.Speed; | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| BulletMessage = new() | |||||
| { | |||||
| Type = Transformation.ToBulletType(bullet.TypeOfBullet), | |||||
| X = bullet.Position.x, | |||||
| Y = bullet.Position.y, | |||||
| FacingDirection = bullet.FacingDirection.Angle(), | |||||
| Guid = bullet.ID, | |||||
| Team = (bullet.Parent.IsGhost()) ? PlayerType.TrickerPlayer : PlayerType.StudentPlayer, | |||||
| Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)bullet.Place), | |||||
| BombRange = bullet.BulletBombRange, | |||||
| Speed = bullet.Speed | |||||
| } | |||||
| }; | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| private static MessageOfObj Prop(Prop prop) | private static MessageOfObj Prop(Prop prop) | ||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| msg.PropMessage = new(); | |||||
| msg.PropMessage.Type = Transformation.ToPropType(prop.GetPropType()); | |||||
| msg.PropMessage.X = prop.Position.x; | |||||
| msg.PropMessage.Y = prop.Position.y; | |||||
| msg.PropMessage.FacingDirection = prop.FacingDirection.Angle(); | |||||
| msg.PropMessage.Guid = prop.ID; | |||||
| msg.PropMessage.Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)prop.Place); | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| PropMessage = new() | |||||
| { | |||||
| Type = Transformation.ToPropType(prop.GetPropType()), | |||||
| X = prop.Position.x, | |||||
| Y = prop.Position.y, | |||||
| FacingDirection = prop.FacingDirection.Angle(), | |||||
| Guid = prop.ID, | |||||
| Place = Transformation.ToPlaceType((Preparation.Utility.PlaceType)prop.Place) | |||||
| } | |||||
| }; | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| private static MessageOfObj BombedBullet(BombedBullet bombedBullet) | private static MessageOfObj BombedBullet(BombedBullet bombedBullet) | ||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| msg.BombedBulletMessage = new(); | |||||
| msg.BombedBulletMessage.X = bombedBullet.bulletHasBombed.Position.x; | |||||
| msg.BombedBulletMessage.Y = bombedBullet.bulletHasBombed.Position.y; | |||||
| msg.BombedBulletMessage.FacingDirection = bombedBullet.FacingDirection.Angle(); | |||||
| msg.BombedBulletMessage.MappingId = bombedBullet.MappingID; | |||||
| msg.BombedBulletMessage.BombRange = bombedBullet.bulletHasBombed.BulletBombRange; | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| BombedBulletMessage = new() | |||||
| { | |||||
| Type = Transformation.ToBulletType(bombedBullet.bulletHasBombed.TypeOfBullet), | |||||
| X = bombedBullet.bulletHasBombed.Position.x, | |||||
| Y = bombedBullet.bulletHasBombed.Position.y, | |||||
| FacingDirection = bombedBullet.FacingDirection.Angle(), | |||||
| MappingId = bombedBullet.MappingID, | |||||
| BombRange = bombedBullet.bulletHasBombed.BulletBombRange | |||||
| } | |||||
| }; | |||||
| // Debugger.Output(bombedBullet, bombedBullet.Place.ToString()+" "+bombedBullet.Position.ToString()); | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| @@ -187,49 +211,71 @@ namespace Server | |||||
| private static MessageOfObj Classroom(Generator generator) | private static MessageOfObj Classroom(Generator generator) | ||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| msg.ClassroomMessage = new(); | |||||
| msg.ClassroomMessage.X = generator.Position.x; | |||||
| msg.ClassroomMessage.Y = generator.Position.y; | |||||
| msg.ClassroomMessage.Progress = generator.DegreeOfRepair; | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| ClassroomMessage = new() | |||||
| { | |||||
| X = generator.Position.x, | |||||
| Y = generator.Position.y, | |||||
| Progress = generator.DegreeOfRepair | |||||
| } | |||||
| }; | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| private static MessageOfObj Gate(Doorway doorway) | |||||
| private static MessageOfObj Gate(Doorway doorway, int time) | |||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| msg.GateMessage = new(); | |||||
| msg.GateMessage.X = doorway.Position.x; | |||||
| msg.GateMessage.Y = doorway.Position.y; | |||||
| msg.GateMessage.Progress = doorway.OpenDegree; | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| GateMessage = new() | |||||
| { | |||||
| X = doorway.Position.x, | |||||
| Y = doorway.Position.y | |||||
| } | |||||
| }; | |||||
| int progress = ((doorway.OpenStartTime > 0) ? (time - doorway.OpenStartTime) : 0) + doorway.OpenDegree; | |||||
| msg.GateMessage.Progress = (progress > GameData.degreeOfOpenedDoorway) ? GameData.degreeOfOpenedDoorway : progress; | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| private static MessageOfObj HiddenGate(EmergencyExit Exit) | private static MessageOfObj HiddenGate(EmergencyExit Exit) | ||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| msg.HiddenGateMessage = new(); | |||||
| msg.HiddenGateMessage.X = Exit.Position.x; | |||||
| msg.HiddenGateMessage.Y = Exit.Position.y; | |||||
| msg.HiddenGateMessage.Opened = Exit.IsOpen; | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| HiddenGateMessage = new() | |||||
| { | |||||
| X = Exit.Position.x, | |||||
| Y = Exit.Position.y, | |||||
| Opened = Exit.IsOpen | |||||
| } | |||||
| }; | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| private static MessageOfObj Door(Door door) | private static MessageOfObj Door(Door door) | ||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| msg.DoorMessage = new(); | |||||
| msg.DoorMessage.X = door.Position.x; | |||||
| msg.DoorMessage.Y = door.Position.y; | |||||
| msg.DoorMessage.Progress = door.OpenOrLockDegree; | |||||
| msg.DoorMessage.IsOpen = door.IsOpen; | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| DoorMessage = new() | |||||
| { | |||||
| X = door.Position.x, | |||||
| Y = door.Position.y, | |||||
| Progress = door.OpenOrLockDegree, | |||||
| IsOpen = door.IsOpen | |||||
| } | |||||
| }; | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| private static MessageOfObj Chest(Chest chest) | |||||
| private static MessageOfObj Chest(Chest chest, int time) | |||||
| { | { | ||||
| MessageOfObj msg = new MessageOfObj(); | |||||
| msg.ChestMessage = new(); | |||||
| msg.ChestMessage.X = chest.Position.x; | |||||
| msg.ChestMessage.Y = chest.Position.y; | |||||
| msg.ChestMessage.Progress = chest.OpenDegree; | |||||
| MessageOfObj msg = new() | |||||
| { | |||||
| ChestMessage = new() | |||||
| { | |||||
| X = chest.Position.x, | |||||
| Y = chest.Position.y | |||||
| } | |||||
| }; | |||||
| int progress = (chest.OpenStartTime > 0) ? ((time - chest.OpenStartTime) * chest.WhoOpen.SpeedOfOpenChest) : 0; | |||||
| msg.ChestMessage.Progress = (progress > GameData.degreeOfOpenedChest) ? GameData.degreeOfOpenedChest : progress; | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| } | } | ||||
| @@ -130,30 +130,34 @@ namespace Server | |||||
| case GameState.GameRunning: | case GameState.GameRunning: | ||||
| case GameState.GameEnd: | case GameState.GameEnd: | ||||
| case GameState.GameStart: | case GameState.GameStart: | ||||
| currentGameInfo.ObjMessage.Add(currentMapMsg); | |||||
| if (gameState == GameState.GameStart || IsSpectatorJoin) | |||||
| { | |||||
| currentGameInfo.ObjMessage.Add(currentMapMsg); | |||||
| IsSpectatorJoin = false; | |||||
| } | |||||
| int time = game.GameMap.Timer.nowTime(); | |||||
| foreach (GameObj gameObj in gameObjList) | foreach (GameObj gameObj in gameObjList) | ||||
| { | { | ||||
| MessageOfObj? msg = CopyInfo.Auto(gameObj); | |||||
| if (msg != null) currentGameInfo.ObjMessage.Add(CopyInfo.Auto(gameObj)); | |||||
| MessageOfObj? msg = CopyInfo.Auto(gameObj, time); | |||||
| if (msg != null) currentGameInfo.ObjMessage.Add(msg); | |||||
| } | } | ||||
| lock (newsLock) | lock (newsLock) | ||||
| { | { | ||||
| foreach (var news in currentNews) | foreach (var news in currentNews) | ||||
| { | { | ||||
| MessageOfObj? msg = CopyInfo.Auto(news); | MessageOfObj? msg = CopyInfo.Auto(news); | ||||
| if (msg != null) currentGameInfo.ObjMessage.Add(CopyInfo.Auto(news)); | |||||
| if (msg != null) currentGameInfo.ObjMessage.Add(msg); | |||||
| } | } | ||||
| currentNews.Clear(); | currentNews.Clear(); | ||||
| } | } | ||||
| currentGameInfo.GameState = gameState; | currentGameInfo.GameState = gameState; | ||||
| currentGameInfo.AllMessage = GetMessageOfAll(); | |||||
| currentGameInfo.AllMessage = GetMessageOfAll(time); | |||||
| mwr?.WriteOne(currentGameInfo); | mwr?.WriteOne(currentGameInfo); | ||||
| break; | break; | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| foreach (var kvp in semaDict) | foreach (var kvp in semaDict) | ||||
| { | { | ||||
| kvp.Value.Item1.Release(); | kvp.Value.Item1.Release(); | ||||
| @@ -208,10 +212,10 @@ namespace Server | |||||
| return false; | return false; | ||||
| } | } | ||||
| private MessageOfAll GetMessageOfAll() | |||||
| private MessageOfAll GetMessageOfAll(int time) | |||||
| { | { | ||||
| MessageOfAll msg = new MessageOfAll(); | MessageOfAll msg = new MessageOfAll(); | ||||
| msg.GameTime = game.GameMap.Timer.nowTime(); | |||||
| msg.GameTime = time; | |||||
| msg.SubjectFinished = (int)game.GameMap.NumOfRepairedGenerators; | msg.SubjectFinished = (int)game.GameMap.NumOfRepairedGenerators; | ||||
| msg.StudentGraduated = (int)game.GameMap.NumOfEscapedStudent; | msg.StudentGraduated = (int)game.GameMap.NumOfEscapedStudent; | ||||
| msg.StudentQuited = (int)game.GameMap.NumOfDeceasedStudent; | msg.StudentQuited = (int)game.GameMap.NumOfDeceasedStudent; | ||||
| @@ -2,7 +2,7 @@ | |||||
| "profiles": { | "profiles": { | ||||
| "Server": { | "Server": { | ||||
| "commandName": "Project", | "commandName": "Project", | ||||
| "commandLineArgs": "--ip 0.0.0.0 -p 8888 --studentCount 1 --trickerCount 0" | |||||
| "commandLineArgs": "--ip 0.0.0.0 -p 8888 --characterID 2030" | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -16,6 +16,22 @@ namespace Server | |||||
| { | { | ||||
| public partial class GameServer : AvailableService.AvailableServiceBase | public partial class GameServer : AvailableService.AvailableServiceBase | ||||
| { | { | ||||
| protected object spectatorLock = new object(); | |||||
| protected bool isSpectatorJoin = false; | |||||
| protected bool IsSpectatorJoin | |||||
| { | |||||
| get | |||||
| { | |||||
| lock (spectatorLock) | |||||
| return isSpectatorJoin; | |||||
| } | |||||
| set | |||||
| { | |||||
| lock (spectatorLock) | |||||
| isSpectatorJoin = value; | |||||
| } | |||||
| } | |||||
| public override Task<BoolRes> TryConnection(IDMsg request, ServerCallContext context) | public override Task<BoolRes> TryConnection(IDMsg request, ServerCallContext context) | ||||
| { | { | ||||
| #if DEBUG | #if DEBUG | ||||
| @@ -53,6 +69,7 @@ namespace Server | |||||
| { | { | ||||
| semaDict.Add(request.PlayerId, temp); | semaDict.Add(request.PlayerId, temp); | ||||
| } | } | ||||
| IsSpectatorJoin = true; | |||||
| } | } | ||||
| do | do | ||||
| { | { | ||||
| @@ -147,8 +164,7 @@ namespace Server | |||||
| return Task.FromResult(boolRes); | return Task.FromResult(boolRes); | ||||
| } | } | ||||
| var gameID = communicationToGameID[request.PlayerId]; | var gameID = communicationToGameID[request.PlayerId]; | ||||
| game.Attack(gameID, request.Angle); | |||||
| boolRes.ActSuccess = true; | |||||
| boolRes.ActSuccess = game.Attack(gameID, request.Angle); | |||||
| return Task.FromResult(boolRes); | return Task.FromResult(boolRes); | ||||
| } | } | ||||
| @@ -170,8 +186,7 @@ namespace Server | |||||
| return Task.FromResult(moveRes); | return Task.FromResult(moveRes); | ||||
| } | } | ||||
| var gameID = communicationToGameID[request.PlayerId]; | var gameID = communicationToGameID[request.PlayerId]; | ||||
| game.MovePlayer(gameID, (int)request.TimeInMilliseconds, request.Angle); | |||||
| moveRes.ActSuccess = true; | |||||
| moveRes.ActSuccess = game.MovePlayer(gameID, (int)request.TimeInMilliseconds, request.Angle); | |||||
| if (!game.GameMap.Timer.IsGaming) moveRes.ActSuccess = false; | if (!game.GameMap.Timer.IsGaming) moveRes.ActSuccess = false; | ||||
| return Task.FromResult(moveRes); | return Task.FromResult(moveRes); | ||||
| } | } | ||||
| @@ -4,7 +4,7 @@ start cmd /k ..\Server\bin\Debug\net6.0\Server.exe --ip 0.0.0.0 --port 8888 -- | |||||
| ping -n 2 127.0.0.1 > NUL | 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 2 | |||||
| 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 0 --type 1 --occupation 1 | start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 0 --type 1 --occupation 1 | ||||
| @@ -12,4 +12,6 @@ start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --ch | |||||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 2 --type 1 --occupation 3 | start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 2 --type 1 --occupation 3 | ||||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 3 --type 1 --occupation 3 | |||||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 3 --type 1 --occupation 3 | |||||
| ::start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 2030 | |||||
| @@ -1,5 +1,5 @@ | |||||
| @echo off | @echo off | ||||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --playbackFile .\test.thuaipb --playbackSpeed 3 | |||||
| start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --playbackFile .\test.thuaipb --playbackSpeed 1 | |||||
| ping -n 2 127.0.0.1 > NUL | ping -n 2 127.0.0.1 > NUL | ||||
| @@ -0,0 +1,28 @@ | |||||
| ## 键鼠控制 | |||||
| | 键位 | 效果 | | |||||
| | ------------ | ---------------------------------------------- | | |||||
| | 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号技能 | | |||||
| @@ -1,325 +0,0 @@ | |||||
| # 规则Logic | |||||
| ## 说明 | |||||
| - *斜体表示Logic底层尚未(完全)实现* | |||||
| - []表示待决定 | |||||
| ## 简要规则 | |||||
| ### 地图 | |||||
| - 地图为矩形区域,地图上的游戏对象坐标为(x, y),且x和y均为整数。 | |||||
| - **x坐标轴正方向竖直向下,y坐标轴正方向水平向右**; | |||||
| - **极坐标以x坐标轴为极轴,角度逆时针为正方向**。 | |||||
| - 地图由50 * 50个格子构成,其中每个格子代表1000 * 1000的正方形。每个格子的编号(CellX,CellY)可以计算得到: | |||||
| - 略 | |||||
| - 地图上的每个格子有自己的区域类型:陆地、墙、草地、电机、出口、紧急出口、门、窗、箱子 | |||||
| ### 人物 | |||||
| - 人物半径为800 | |||||
| - 游戏人物共有17种不可叠加的状态:(加^为学生独有) | |||||
| 1. 普通状态 | |||||
| 2. 学习^ | |||||
| 3. 被治疗^ | |||||
| 4. 在治疗^ | |||||
| 5. 开或锁门 | |||||
| 6. 翻箱 | |||||
| 7. 使用技能 | |||||
| 8. 正在毕业^ | |||||
| 9. 唤醒他人中^ | |||||
| 10. 被唤醒中(从沉迷状态中^ | |||||
| 11. 沉迷^ | |||||
| 12. 退学^ | |||||
| 13. 毕业^ | |||||
| 14. 被眩晕 | |||||
| 15. 前摇 | |||||
| 16. 后摇 | |||||
| 17. 翻窗 | |||||
| - 其中后8项为不可行动状态 | |||||
| - 不加声明,不可行动状态中的玩家各项指令是无效的 | |||||
| ### 交互 | |||||
| - 除了翻窗,交互目标与交互者在一个九宫格内则为可交互 | |||||
| - 翻窗时玩家应当在目标前后左右一个格子内 | |||||
| #### 破译与逃脱 | |||||
| - 每张地图都有10台电机,学生需要破译其中的**7台**,并开启任意大门(总所需时间为18秒)后从开启的大门逃脱,亦或在只剩1名学生的情况下从紧急出口逃脱; | |||||
| - 紧急出口会在电机破译完成3台的情况下在地图的3-5个固定紧急出口刷新点之一随机刷新显示。 | |||||
| - 当学生只剩1名时,紧急出口将会自动打开,该学生可从紧急出口逃脱。 | |||||
| - 大门开启的进度不清空 | |||||
| #### 攻击 | |||||
| - 无论近战远程均产生bullet以表示攻击所至距离 | |||||
| - 如果有前摇,前摇最大时间一般为攻击最远距离/bullet移动速度。前摇期间攻击被打断时,子弹消失。 | |||||
| - 无论搞蛋鬼或学生,攻击后,通常会出现一段无伤害判定的攻击动作后置时间,称为后摇。击中物体时后摇更长 | |||||
| - 搞蛋鬼可以攻击未修完的作业,造成(攻击力*2/100000)%的 | |||||
| - 捣蛋鬼攻击或一些特定技能击中以下状态的学生,将使学生眩晕4.3s | |||||
| 1. 处于前摇或后摇 | |||||
| 2. 治疗或解救他人 | |||||
| 3. 修理电机 | |||||
| 4. 开锁门 | |||||
| 5. 翻窗 | |||||
| 6. 开启箱子 | |||||
| #### 治疗 | |||||
| - 治疗时每毫秒增加相当于治疗者治疗速度的被治疗程度 | |||||
| - 当达到被治疗程度达到1500000或者最大血量与当前血量的差值时,治疗结束。 | |||||
| - 治疗中断时,被治疗程度保留;被治疗者遭到攻击时被治疗程度清空 | |||||
| #### 沉迷与唤醒 | |||||
| - 当学生血量归零时,学生自动原地进入沉迷状态,每毫秒增加1沉迷度 | |||||
| - 该学生可由其他的学生唤醒,唤醒后,血量恢复至1/2并可以重新行动。沉迷程度不清空。 | |||||
| - 一般情况下,唤醒时间为1秒。 | |||||
| - 进入沉迷状态时。如果学生原本沉迷程度在(0,其最大沉迷度/3)中,学生沉迷程度直接变为其最大沉迷度/3;如果学生原本沉迷程度在[其最大沉迷度/3,其最大沉迷度x2/3)中,学生沉迷程度直接变为其最大沉迷度x2/3;如果学生原本沉迷程度大于其最大沉迷度x2/3,从游戏中出局; | |||||
| - 当学生沉迷程度达到该玩家最大沉迷程度时,从游戏中出局 | |||||
| #### 门 | |||||
| - 门分别属于三个教学区:三教,五教,六教 | |||||
| - 需要拿到对应教学区的钥匙才能打开或锁住对应的门 | |||||
| - 钥匙只会出现在箱子中,每个教学区都有2把钥匙 | |||||
| - 当门所在格子内有人时,无法锁门 | |||||
| - 锁门时其他人可以进入门所在格子,锁门过程中断 | |||||
| #### 窗 | |||||
| - 通常情况下捣蛋鬼翻越窗户的速度高于学生。 | |||||
| - 有人正在翻越窗户时,其他玩家均不可以翻越该窗户。 | |||||
| #### 箱子 | |||||
| - 开启箱子后将有2个随机道具掉落在玩家位置。 | |||||
| - 开启箱子的基础持续时间为10秒。 | |||||
| - 箱子道具不刷新 | |||||
| #### Bgm | |||||
| 1. 不详的感觉:捣蛋鬼进入(学生的警戒半径/捣蛋鬼的隐蔽度)时,学生收到;捣蛋鬼距离学生越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离) | |||||
| 2. 期待搞事的感觉:学生进入(捣蛋鬼的警戒半径/学生的隐蔽度)时,捣蛋鬼收到;捣蛋鬼距离学生越近,Bgm音量越大。bgmVolume=(警戒半径/可被发觉的最近的学生距离) | |||||
| 3. 学习的声音: 捣蛋鬼警戒半径内有人学习时收到;bgmVolume=(警戒半径x学习进度百分比)/二者距离 | |||||
| ### 得分 | |||||
| #### 屠夫 | |||||
| - [Tricker对Student造成伤害时,得伤害*100/基本伤害(1500000)分。] | |||||
| - *[使用道具/技能得分]* | |||||
| - 不同道具/技能有不同得分 | |||||
| - 使Student进入沉迷状态时,得50分。 | |||||
| - 使人类进入眩晕状态时,得25分。 | |||||
| - 每淘汰一个Student,得1000分 | |||||
| - 主动解除眩晕,得15分 | |||||
| - 开锁门 | |||||
| #### 人类 | |||||
| - 修机得分 | |||||
| - 人类每修n%的电机,得n分 | |||||
| - 修完一台电机,额外得?分 | |||||
| - [牵制得分] | |||||
| - 使用道具/技能得分 | |||||
| - 不同道具/技能有不同得分 | |||||
| - 使屠夫进入特殊状态得分(如使之眩晕) | |||||
| - 救人 | |||||
| - 治疗 | |||||
| - 逃脱 | |||||
| - 解除眩晕 | |||||
| - 开锁门 | |||||
| ## 道具 | |||||
| | 道具 | 对学生增益 | [学生得分条件] | 对搞蛋鬼增益 | [搞蛋鬼得分条件] | | |||||
| | :-------- | :-------------------------------------- | :-----------------| :-------------------------------------- |:-----------------| | |||||
| | Key3 | 能开启3教的门 |不得分| 能开启3教的门 |不得分| | |||||
| | Key5 | 能开启5教的门 |不得分| 能开启3教的门 |不得分| | |||||
| | Key6 | 能开启6教的门 |不得分| 能开启3教的门 |不得分| | |||||
| | AddSpeed | 提高移动速度,持续10s |得分?| 提高移动速度,持续10s |得分?| | |||||
| | AddLifeOrClairaudience |若在10s内Hp归零,该增益消失以使Hp保留100|在10s内Hp归零,得分? |10秒内下一次攻击增伤1800000|10秒内有一次攻击,得分? | | |||||
| | AddHpOrAp |回血1500000 | 回血成功 | 10秒内下一次攻击增伤1800000|10秒内有一次攻击,得分? | | |||||
| | ShieldOrSpear | 10秒内能抵挡一次伤害 | 10秒内成功抵挡一次伤害 |10秒内下一次攻击能破盾,如果对方无盾,则增伤900000| 10秒内攻击中学生| | |||||
| | RecoveryFromDizziness | 使用瞬间从眩晕状态中恢复 | 成功从眩晕状态中恢复,得分?|使用瞬间从眩晕状态中恢复 | 成功从眩晕状态中恢复,得分?| | |||||
| ## 职业与技能 | |||||
| ### 捣蛋鬼 | |||||
| | 捣蛋鬼职业 | 基本量 | Assassin | Klee | 喧哗者ANoisyPerson | | |||||
| | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | |||||
| | 移动速度/s | 1,503 | 1.1 | 1 | 1.07 | | |||||
| | 隐蔽度 | 1.0 | 1.5 | 1 | 0.8 | | |||||
| | 警戒范围 | 17000 | 1.3 | 1 | 0.9 | | |||||
| | 视野范围 | 15000 | 1.2 | 1 | 1 | | |||||
| | 开锁门时间/s | 41.3% | 1 | 1 | 1 | | |||||
| | 翻窗速度 | 1270 | 1 | 1 | 1.1 | | |||||
| | 翻箱时间/s | 10% | 1 | 1.1 | 1 | | |||||
| #### 刺客 | |||||
| - 普通攻击为 搞蛋鬼的一般攻击 | |||||
| - 主动技能 | |||||
| - 隐身 | |||||
| - CD:30s 持续时间:6s | |||||
| - 在持续时间内玩家隐身 | |||||
| - 使用瞬间得分 | |||||
| - 使用飞刀 | |||||
| - CD:20s 持续时间:1s | |||||
| - 在持续时间内,攻击类型变为飞刀 | |||||
| - 不直接得分 | |||||
| #### Klee | |||||
| - 普通攻击为 搞蛋鬼的一般攻击 | |||||
| - 主动技能 | |||||
| - 蹦蹦炸弹 | |||||
| - CD:15s 持续时间:3s | |||||
| - 在持续时间内,攻击类型变为蹦蹦炸弹 | |||||
| - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上90°,270° 发出2个小炸弹 | |||||
| - 2个小炸弹运动停止前会因为碰撞爆炸,停止运动后学生碰撞会造成眩晕3.07s | |||||
| - 不直接得分,通过眩晕等获得对应得分 | |||||
| #### 喧哗者 | |||||
| - 普通攻击为 搞蛋鬼的一般攻击 | |||||
| - 主动技能 | |||||
| - 嚎叫 | |||||
| - CD:25s | |||||
| - 使用瞬间,在视野半径范围内(不是可视区域)的学生被眩晕6110ms,自己进入3070ms的后摇 | |||||
| - 通过眩晕获得对应得分 | |||||
| - 特性 | |||||
| - 在场所有学生Bgm系统被设为无用的值 | |||||
| ### 学生(&老师) | |||||
| | 学生职业 | 基本量 | 教师Teacher | 健身狂Athlete | 学霸StraightAStudent | 开心果Sunshine | | |||||
| | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | |||||
| | 移动速度 | 1,270 | 3 / 4 | 1.1 | 0.8 | 1 | | |||||
| | 最大毅力值 | 3000000 | 10 | 1 | 1.1 | 32/30 | | |||||
| | 最大沉迷度 | 60000 | 10 | 0.9 | 1.3 | 1.1 | | |||||
| | 学习一科速度/s | 1.23% | 0 | 0.6 | 1.1 | 1 | | |||||
| | 治疗速度 | 100 | 0.7 | 0.8 | 0.8 | 2 | | |||||
| | 隐蔽度 | 1.0 | 0.5 | 0.9 | 0.9 | 1 | | |||||
| | 警戒范围 | 15000 | 0.5 | 1 | 0.9 | 1 | | |||||
| | 视野范围 | 10000 | 0.9 | 1.1 | 0.9 | 1 | | |||||
| | 开锁门时间/s | 4.13% | 1 | 1 | 1 | 1 | | |||||
| | 翻窗速度 | 1270 | 0.5 | 1.2 | 10/12 | 1 | | |||||
| | 翻箱时间/s | 10% | 1 | 1 | 1 | 1 | | |||||
| #### 运动员 | |||||
| - 主动技能 | |||||
| - 冲撞 | |||||
| - CD:24s 持续时间:5s | |||||
| - 在持续时间内,速度变为三倍,期间撞到捣蛋鬼,会导致捣蛋鬼眩晕7.22s,学生眩晕2.09s | |||||
| - 通过眩晕获得对应得分 | |||||
| #### 教师 | |||||
| - 主动技能 | |||||
| - 惩罚 | |||||
| - CD:30s | |||||
| - 使用瞬间,在可视范围内的使用技能状态中、攻击前后摇的捣蛋鬼会被眩晕(3070)ms, | |||||
| - 通过眩晕获得对应得分 | |||||
| - 特性 | |||||
| - 教师无法获得牵制得分 | |||||
| #### 学霸 | |||||
| - 特性 | |||||
| - 冥想 | |||||
| - 当玩家处于可接受指令状态且不在修机时,会积累学习进度,速度为0.3%/ms | |||||
| - 受到攻击(并非伤害)或眩晕或翻窗(或攻击他人)学习进度清零 | |||||
| - 主动技能5 | |||||
| - 写答案 | |||||
| - CD:30s | |||||
| - 使用瞬间,对于可互动范围内的一台电机增加这个学习进度 | |||||
| - 通过修机获得对应得分 | |||||
| #### 开心果 | |||||
| - 主动技能 | |||||
| - 唤醒 | |||||
| - CD:60s | |||||
| - 使用瞬间,唤醒可视范围内一个沉迷中的人 | |||||
| - 通过唤醒获得对应得分 | |||||
| - 勉励 | |||||
| - CD:60s | |||||
| - 使用瞬间,治疗完成可视范围内一个毅力不足的人 | |||||
| - 通过治疗获得对应得分 | |||||
| - 鼓舞 | |||||
| - CD:60s | |||||
| - 使用瞬间,可视范围内学生(包括自己)获得持续6秒的1.6倍速Buff | |||||
| - 每鼓舞一个学生得分10 | |||||
| ### 攻击类型 | |||||
| | 攻击类型 |搞蛋鬼的一般攻击CommonAttackOfGhost| 飞刀FlyingKnife | 蹦蹦炸弹BombBomb | JumpyDumpty | | |||||
| | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | |||||
| | 子弹爆炸范围 | 0 | 0 | 1000 | 500 | | |||||
| | 子弹攻击距离 | 1100 | 39000 | 1100 | 2200 | | |||||
| | 攻击力 | 1500000 | 1,200,000 | 1,800,000 | 900000 | | |||||
| | 移动速度/s | 3700 | 7,400 | 3000 | 4300 | | |||||
| | 有无前摇(ms) | 297 | 500 | 366 | 0 | | |||||
| |未攻击至目标时的后摇(ms)| 800 | 0 | 3700 | 0 | | |||||
| |攻击至目标时的后摇(ms)| 3700 | 0 | 3700 | 0 | | |||||
| | CD(ms) | 800 | 400 | 3000 | - | | |||||
| | 最大子弹容量 | 1 | 1 | 1 | - | | |||||
| ## 游戏数据 | |||||
| 请自行查看Logic/Preparation/Utility/GameData.cs | |||||
| ## 细则 | |||||
| ### 特殊说明 | |||||
| - 不加说明,这里“学生”往往包括职业“教师” | |||||
| ### 初始状态 | |||||
| - 玩家出生点固定且一定为空地 | |||||
| ### 交互 | |||||
| - 在指令仍在进行时,重复发出同一类型的交互指令是无效的,你需要先发出Stop指令终止进行的指令 | |||||
| - 实际上救援或治疗不同的人是有效的 | |||||
| ### 破译与逃脱 | |||||
| - 紧急出口与大门对于人有碰撞体积 | |||||
| - 一个大门同时最多可以由一人开启 | |||||
| ### 攻击 | |||||
| - 每次学生受到攻击后会损失对应子弹的攻击力的血量 | |||||
| - 此处,前摇指 从播放攻击动作开始 攻击者不能交互 的时间 | |||||
| ### 沉迷与唤醒 | |||||
| - 在被救时沉迷度不增加 | |||||
| - 不能两人同时唤醒一个人 | |||||
| ### 门 | |||||
| - 一扇门只允许同时一个人开锁门 | |||||
| - 开锁门未完成前,门状态表现为原来的状态 | |||||
| - 开锁门进度中断后清空 | |||||
| ### 窗 | |||||
| - 攻击可以穿过窗,道具可以在窗上 | |||||
| - 翻越窗户是一种交互行为,翻窗一共有两个过程 | |||||
| - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 | |||||
| - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 | |||||
| ### 箱子 | |||||
| - 地图上有8个箱子 | |||||
| - 同一时刻只允许一人进行开启 | |||||
| - 未开启完成的箱子在下一次需要重新开始开启。 | |||||
| - 箱子开启后其中道具才可以被观测和拿取 | |||||
| ## 键鼠控制 | |||||
| | 键位 | 效果 | | |||||
| | ------------ | ---------------------------------------------- | | |||||
| | 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号技能 | | |||||
| @@ -27,7 +27,7 @@ namespace Playback | |||||
| public readonly uint teamCount; | public readonly uint teamCount; | ||||
| public readonly uint playerCount; | public readonly uint playerCount; | ||||
| const int bufferMaxSize = 1024 * 1024; // 1M | |||||
| const int bufferMaxSize = 10 * 1024 * 1024; // 10M | |||||
| public MessageReader(string fileName) | public MessageReader(string fileName) | ||||
| { | { | ||||
| @@ -83,7 +83,8 @@ namespace Playback | |||||
| public MessageToClient? ReadOne() | public MessageToClient? ReadOne() | ||||
| { | { | ||||
| beginRead: | beginRead: | ||||
| if (Finished) return null; | |||||
| if (Finished) | |||||
| return null; | |||||
| var pos = cos.Position; | var pos = cos.Position; | ||||
| try | try | ||||
| { | { | ||||
| @@ -94,9 +95,21 @@ namespace Playback | |||||
| catch (InvalidProtocolBufferException) | catch (InvalidProtocolBufferException) | ||||
| { | { | ||||
| var leftByte = buffer.Length - pos; // 上次读取剩余的字节 | var leftByte = buffer.Length - pos; // 上次读取剩余的字节 | ||||
| for (int i = 0; i < leftByte; ++i) | |||||
| if (buffer.Length < bufferMaxSize / 2) | |||||
| { | { | ||||
| buffer[i] = buffer[pos + i]; | |||||
| var newBuffer = new byte[bufferMaxSize]; | |||||
| for (int i = 0; i < leftByte; i++) | |||||
| { | |||||
| newBuffer[i] = buffer[pos + i]; | |||||
| } | |||||
| buffer = newBuffer; | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = 0; i < leftByte; ++i) | |||||
| { | |||||
| buffer[i] = buffer[pos + i]; | |||||
| } | |||||
| } | } | ||||
| var bufferSize = gzs.Read(buffer, (int)leftByte, (int)(buffer.Length - leftByte)) + leftByte; | var bufferSize = gzs.Read(buffer, (int)leftByte, (int)(buffer.Length - leftByte)) + leftByte; | ||||
| if (bufferSize == leftByte) | if (bufferSize == leftByte) | ||||
| @@ -12,7 +12,7 @@ namespace Playback | |||||
| private CodedOutputStream cos; | private CodedOutputStream cos; | ||||
| private MemoryStream ms; | private MemoryStream ms; | ||||
| private GZipStream gzs; | private GZipStream gzs; | ||||
| private const int memoryCapacity = 1024 * 1024; // 1M | |||||
| private const int memoryCapacity = 10 * 1024 * 1024; // 10M | |||||
| private static void ClearMemoryStream(MemoryStream msToClear) | private static void ClearMemoryStream(MemoryStream msToClear) | ||||
| { | { | ||||
| @@ -50,6 +50,7 @@ namespace Playback | |||||
| { | { | ||||
| cos.Flush(); | cos.Flush(); | ||||
| gzs.Write(ms.GetBuffer(), 0, (int)ms.Length); | gzs.Write(ms.GetBuffer(), 0, (int)ms.Length); | ||||
| gzs.Flush(); | |||||
| ClearMemoryStream(ms); | ClearMemoryStream(ms); | ||||
| fs.Flush(); | fs.Flush(); | ||||
| } | } | ||||