| @@ -60,7 +60,7 @@ public: | |||
| virtual bool PickProp(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, int32_t skillParam) = 0; | |||
| virtual bool SendMessage(int64_t toID, std::string message, bool binary) = 0; | |||
| virtual bool HaveMessage() = 0; | |||
| virtual std::pair<int64_t, std::string> GetMessage() = 0; | |||
| @@ -108,7 +108,7 @@ public: | |||
| virtual std::future<bool> PickProp(THUAI6::PropType prop) = 0; | |||
| virtual std::future<bool> 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, int32_t skillParam = 0) = 0; | |||
| virtual std::future<bool> Attack(double angleInRadian) = 0; | |||
| virtual std::future<bool> OpenDoor() = 0; | |||
| @@ -236,7 +236,7 @@ public: | |||
| std::future<bool> PickProp(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, int32_t skillParam = 0) override; | |||
| std::future<bool> Attack(double angleInRadian) override; | |||
| @@ -329,7 +329,7 @@ public: | |||
| std::future<bool> PickProp(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, int32_t skillParam = 0) override; | |||
| std::future<bool> OpenDoor() override; | |||
| std::future<bool> CloseDoor() override; | |||
| @@ -410,7 +410,7 @@ public: | |||
| std::future<bool> PickProp(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, int32_t skillParam = 0) override; | |||
| std::future<bool> Attack(double angleInRadian) override; | |||
| @@ -488,7 +488,7 @@ public: | |||
| std::future<bool> PickProp(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, int32_t skillParam = 0) override; | |||
| std::future<bool> OpenDoor() override; | |||
| std::future<bool> CloseDoor() override; | |||
| @@ -31,7 +31,7 @@ public: | |||
| bool PickProp(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, int32_t skillParam, int64_t playerID); | |||
| bool SendMessage(int64_t toID, std::string message, bool binary, int64_t playerID); | |||
| bool OpenDoor(int64_t playerID); | |||
| bool CloseDoor(int64_t playerID); | |||
| @@ -116,7 +116,7 @@ private: | |||
| bool PickProp(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, int32_t skillParam) override; | |||
| bool SendMessage(int64_t toID, std::string message, bool binary) override; | |||
| bool HaveMessage() override; | |||
| @@ -62,6 +62,7 @@ namespace THUAI6 | |||
| AddHpOrAp = 6, | |||
| ShieldOrSpear = 7, | |||
| RecoveryFromDizziness = 8, | |||
| CraftingBench = 9, | |||
| }; | |||
| enum class BulletType : unsigned char | |||
| @@ -71,7 +72,7 @@ namespace THUAI6 | |||
| CommonAttackOfTricker = 2, | |||
| BombBomb = 3, | |||
| JumpyDumpty = 4, | |||
| AtomBomb = 5, | |||
| Strike = 5, | |||
| }; | |||
| // 玩家类型 | |||
| @@ -363,6 +364,7 @@ namespace THUAI6 | |||
| {PropType::AddHpOrAp, "AddHpOrAp"}, | |||
| {PropType::ShieldOrSpear, "ShieldOrSpear"}, | |||
| {PropType::RecoveryFromDizziness, "RecoveryFromDizziness"}, | |||
| {PropType::CraftingBench, "CraftingBench"}, | |||
| }; | |||
| @@ -372,7 +374,7 @@ namespace THUAI6 | |||
| {BulletType::CommonAttackOfTricker, "CommonAttackOfTricker"}, | |||
| {BulletType::BombBomb, "BombBomb"}, | |||
| {BulletType::JumpyDumpty, "JumpyDumpty"}, | |||
| {BulletType::AtomBomb, "AtomBomb"}, | |||
| {BulletType::Strike, "Strike"}, | |||
| }; | |||
| inline std::map<StudentBuffType, std::string> studentBuffDict{ | |||
| @@ -108,6 +108,7 @@ namespace Proto2THUAI6 | |||
| {protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE, THUAI6::PropType::AddLifeOrClairaudience}, | |||
| {protobuf::PropType::SHIELD_OR_SPEAR, THUAI6::PropType::ShieldOrSpear}, | |||
| {protobuf::PropType::RECOVERY_FROM_DIZZINESS, THUAI6::PropType::RecoveryFromDizziness}, | |||
| {protobuf::PropType::CRAFTING_BENCH, THUAI6::PropType::CraftingBench}, | |||
| }; | |||
| inline std::map<protobuf::PlayerType, THUAI6::PlayerType> playerTypeDict{ | |||
| @@ -186,7 +187,7 @@ namespace Proto2THUAI6 | |||
| {protobuf::BulletType::COMMON_ATTACK_OF_TRICKER, THUAI6::BulletType::CommonAttackOfTricker}, | |||
| {protobuf::BulletType::BOMB_BOMB, THUAI6::BulletType::BombBomb}, | |||
| {protobuf::BulletType::JUMPY_DUMPTY, THUAI6::BulletType::JumpyDumpty}, | |||
| {protobuf::BulletType::ATOM_BOMB, THUAI6::BulletType::AtomBomb}, | |||
| {protobuf::BulletType::STRIKE, THUAI6::BulletType::Strike}, | |||
| }; | |||
| inline std::map<protobuf::MessageOfObj::MessageOfObjCase, THUAI6::MessageOfObj> messageOfObjDict{ | |||
| @@ -373,6 +374,7 @@ namespace THUAI62Proto | |||
| {THUAI6::PropType::AddLifeOrClairaudience, protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE}, | |||
| {THUAI6::PropType::AddSpeed, protobuf::PropType::ADD_SPEED}, | |||
| {THUAI6::PropType::ShieldOrSpear, protobuf::PropType::SHIELD_OR_SPEAR}, | |||
| {THUAI6::PropType::CraftingBench, protobuf::PropType::CRAFTING_BENCH}, | |||
| }; | |||
| inline std::map<THUAI6::PlayerType, protobuf::PlayerType> playerTypeDict{ | |||
| @@ -486,11 +488,12 @@ namespace THUAI62Proto | |||
| return attackMsg; | |||
| } | |||
| inline protobuf::SkillMsg THUAI62ProtobufSkill(int32_t skillID, int64_t id) | |||
| inline protobuf::SkillMsg THUAI62ProtobufSkill(int32_t skillID, int32_t skillParam, int64_t id) | |||
| { | |||
| protobuf::SkillMsg skillMsg; | |||
| skillMsg.set_skill_id(skillID); | |||
| skillMsg.set_player_id(id); | |||
| skillMsg.set_skill_param(skillParam); | |||
| return skillMsg; | |||
| } | |||
| } // namespace THUAI62Proto | |||
| @@ -106,16 +106,16 @@ std::future<bool> TrickerAPI::ThrowProp(THUAI6::PropType prop) | |||
| { return logic.ThrowProp(prop); }); | |||
| } | |||
| std::future<bool> StudentAPI::UseSkill(int32_t skillID) | |||
| std::future<bool> StudentAPI::UseSkill(int32_t skillID, int32_t skillParam) | |||
| { | |||
| return std::async(std::launch::async, [=]() | |||
| { return logic.UseSkill(skillID); }); | |||
| { return logic.UseSkill(skillID, skillParam); }); | |||
| } | |||
| std::future<bool> TrickerAPI::UseSkill(int32_t skillID) | |||
| std::future<bool> TrickerAPI::UseSkill(int32_t skillID, int32_t skillParam) | |||
| { | |||
| return std::async(std::launch::async, [=]() | |||
| { return logic.UseSkill(skillID); }); | |||
| { return logic.UseSkill(skillID, skillParam); }); | |||
| } | |||
| std::future<bool> StudentAPI::OpenDoor() | |||
| @@ -91,7 +91,7 @@ bool Communication::ThrowProp(THUAI6::PropType prop, int64_t playerID) | |||
| return false; | |||
| } | |||
| bool Communication::UseSkill(int32_t skillID, int64_t playerID) | |||
| bool Communication::UseSkill(int32_t skillID, int32_t skillParam, int64_t playerID) | |||
| { | |||
| { | |||
| std::lock_guard<std::mutex> lock(mtxLimit); | |||
| @@ -101,7 +101,7 @@ bool Communication::UseSkill(int32_t skillID, int64_t playerID) | |||
| } | |||
| protobuf::BoolRes useSkillResult; | |||
| ClientContext context; | |||
| auto request = THUAI62Proto::THUAI62ProtobufSkill(skillID, playerID); | |||
| auto request = THUAI62Proto::THUAI62ProtobufSkill(skillID, skillParam, playerID); | |||
| auto status = THUAI6Stub->UseSkill(&context, request, &useSkillResult); | |||
| if (status.ok()) | |||
| return useSkillResult.act_success(); | |||
| @@ -212,21 +212,21 @@ std::future<bool> TrickerDebugAPI::ThrowProp(THUAI6::PropType prop) | |||
| return result; }); | |||
| } | |||
| std::future<bool> StudentDebugAPI::UseSkill(int32_t skillID) | |||
| std::future<bool> StudentDebugAPI::UseSkill(int32_t skillID, int32_t skillParam) | |||
| { | |||
| logger->info("UseSkill: skillID={}, called at {}ms", skillID, Time::TimeSinceStart(startPoint)); | |||
| logger->info("UseSkill: skillID={}, skillParam={}, called at {}ms", skillID, skillParam, Time::TimeSinceStart(startPoint)); | |||
| return std::async(std::launch::async, [=]() | |||
| { auto result = logic.UseSkill(skillID); | |||
| { auto result = logic.UseSkill(skillID, skillParam); | |||
| if (!result) | |||
| logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||
| return result; }); | |||
| } | |||
| std::future<bool> TrickerDebugAPI::UseSkill(int32_t skillID) | |||
| std::future<bool> TrickerDebugAPI::UseSkill(int32_t skillID, int32_t skillParam) | |||
| { | |||
| logger->info("UseSkill: skillID={}, called at {}ms", skillID, Time::TimeSinceStart(startPoint)); | |||
| logger->info("UseSkill: skillID={}, skillParam={}, called at {}ms", skillID, skillParam, Time::TimeSinceStart(startPoint)); | |||
| return std::async(std::launch::async, [=]() | |||
| { auto result = logic.UseSkill(skillID); | |||
| { auto result = logic.UseSkill(skillID, skillParam); | |||
| if (!result) | |||
| logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint)); | |||
| return result; }); | |||
| @@ -208,10 +208,10 @@ bool Logic::ThrowProp(THUAI6::PropType prop) | |||
| return pComm->ThrowProp(prop, playerID); | |||
| } | |||
| bool Logic::UseSkill(int32_t skill) | |||
| bool Logic::UseSkill(int32_t skill, int32_t skillParam) | |||
| { | |||
| logger->debug("Called UseSkill"); | |||
| return pComm->UseSkill(skill, playerID); | |||
| return pComm->UseSkill(skill, skillParam, playerID); | |||
| } | |||
| bool Logic::SendMessage(int64_t toID, std::string message, bool binary) | |||
| @@ -180,7 +180,7 @@ namespace protobuf | |||
| ::_pbi::ConstantInitialized | |||
| ) : | |||
| _impl_{ | |||
| /*decltype(_impl_.player_id_)*/ int64_t{0}, /*decltype(_impl_.skill_id_)*/ 0, /*decltype(_impl_._cached_size_)*/ {}} | |||
| /*decltype(_impl_.player_id_)*/ int64_t{0}, /*decltype(_impl_.skill_id_)*/ 0, /*decltype(_impl_.skill_param_)*/ 0, /*decltype(_impl_._cached_size_)*/ {}} | |||
| { | |||
| } | |||
| struct SkillMsgDefaultTypeInternal | |||
| @@ -274,6 +274,7 @@ const uint32_t TableStruct_Message2Server_2eproto::offsets[] PROTOBUF_SECTION_VA | |||
| ~0u, // no _inlined_string_donated_ | |||
| PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.player_id_), | |||
| PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.skill_id_), | |||
| PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.skill_param_), | |||
| }; | |||
| static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { | |||
| {0, -1, -1, sizeof(::protobuf::PlayerMsg)}, | |||
| @@ -313,9 +314,9 @@ const char descriptor_table_protodef_Message2Server_2eproto[] PROTOBUF_SECTION_V | |||
| "\t\n\007message\"-\n\tAttackMsg\022\021\n\tplayer_id\030\001 \001" | |||
| "(\003\022\r\n\005angle\030\002 \001(\001\"\032\n\005IDMsg\022\021\n\tplayer_id\030" | |||
| "\001 \001(\003\"<\n\021TreatAndRescueMsg\022\021\n\tplayer_id\030" | |||
| "\001 \001(\003\022\024\n\014to_player_id\030\002 \001(\003\"/\n\010SkillMsg\022" | |||
| "\021\n\tplayer_id\030\001 \001(\003\022\020\n\010skill_id\030\002 \001(\005b\006pr" | |||
| "oto3"; | |||
| "\001 \001(\003\022\024\n\014to_player_id\030\002 \001(\003\"D\n\010SkillMsg\022" | |||
| "\021\n\tplayer_id\030\001 \001(\003\022\020\n\010skill_id\030\002 \001(\005\022\023\n\013" | |||
| "skill_param\030\003 \001(\005b\006proto3"; | |||
| static const ::_pbi::DescriptorTable* const descriptor_table_Message2Server_2eproto_deps[1] = { | |||
| &::descriptor_table_MessageType_2eproto, | |||
| }; | |||
| @@ -323,7 +324,7 @@ static ::_pbi::once_flag descriptor_table_Message2Server_2eproto_once; | |||
| const ::_pbi::DescriptorTable descriptor_table_Message2Server_2eproto = { | |||
| false, | |||
| false, | |||
| 684, | |||
| 705, | |||
| descriptor_table_protodef_Message2Server_2eproto, | |||
| "Message2Server.proto", | |||
| &descriptor_table_Message2Server_2eproto_once, | |||
| @@ -2341,10 +2342,10 @@ namespace protobuf | |||
| SkillMsg* const _this = this; | |||
| (void)_this; | |||
| new (&_impl_) Impl_{ | |||
| decltype(_impl_.player_id_){}, decltype(_impl_.skill_id_){}, /*decltype(_impl_._cached_size_)*/ {}}; | |||
| decltype(_impl_.player_id_){}, decltype(_impl_.skill_id_){}, decltype(_impl_.skill_param_){}, /*decltype(_impl_._cached_size_)*/ {}}; | |||
| _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); | |||
| ::memcpy(&_impl_.player_id_, &from._impl_.player_id_, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_id_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_id_)); | |||
| ::memcpy(&_impl_.player_id_, &from._impl_.player_id_, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_param_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_param_)); | |||
| // @@protoc_insertion_point(copy_constructor:protobuf.SkillMsg) | |||
| } | |||
| @@ -2355,7 +2356,7 @@ namespace protobuf | |||
| (void)arena; | |||
| (void)is_message_owned; | |||
| new (&_impl_) Impl_{ | |||
| decltype(_impl_.player_id_){int64_t{0}}, decltype(_impl_.skill_id_){0}, /*decltype(_impl_._cached_size_)*/ {}}; | |||
| decltype(_impl_.player_id_){int64_t{0}}, decltype(_impl_.skill_id_){0}, decltype(_impl_.skill_param_){0}, /*decltype(_impl_._cached_size_)*/ {}}; | |||
| } | |||
| SkillMsg::~SkillMsg() | |||
| @@ -2386,7 +2387,7 @@ namespace protobuf | |||
| // Prevent compiler warnings about cached_has_bits being unused | |||
| (void)cached_has_bits; | |||
| ::memset(&_impl_.player_id_, 0, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_id_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_id_)); | |||
| ::memset(&_impl_.player_id_, 0, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_param_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_param_)); | |||
| _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); | |||
| } | |||
| @@ -2421,6 +2422,16 @@ namespace protobuf | |||
| else | |||
| goto handle_unusual; | |||
| continue; | |||
| // int32 skill_param = 3; | |||
| case 3: | |||
| if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) | |||
| { | |||
| _impl_.skill_param_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); | |||
| CHK_(ptr); | |||
| } | |||
| else | |||
| goto handle_unusual; | |||
| continue; | |||
| default: | |||
| goto handle_unusual; | |||
| } // switch | |||
| @@ -2469,6 +2480,13 @@ namespace protobuf | |||
| target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_skill_id(), target); | |||
| } | |||
| // int32 skill_param = 3; | |||
| if (this->_internal_skill_param() != 0) | |||
| { | |||
| target = stream->EnsureSpace(target); | |||
| target = ::_pbi::WireFormatLite::WriteInt32ToArray(3, this->_internal_skill_param(), target); | |||
| } | |||
| if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) | |||
| { | |||
| target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( | |||
| @@ -2500,6 +2518,12 @@ namespace protobuf | |||
| total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_skill_id()); | |||
| } | |||
| // int32 skill_param = 3; | |||
| if (this->_internal_skill_param() != 0) | |||
| { | |||
| total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_skill_param()); | |||
| } | |||
| return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); | |||
| } | |||
| @@ -2528,6 +2552,10 @@ namespace protobuf | |||
| { | |||
| _this->_internal_set_skill_id(from._internal_skill_id()); | |||
| } | |||
| if (from._internal_skill_param() != 0) | |||
| { | |||
| _this->_internal_set_skill_param(from._internal_skill_param()); | |||
| } | |||
| _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); | |||
| } | |||
| @@ -2550,7 +2578,7 @@ namespace protobuf | |||
| using std::swap; | |||
| _internal_metadata_.InternalSwap(&other->_internal_metadata_); | |||
| ::PROTOBUF_NAMESPACE_ID::internal::memswap< | |||
| PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.skill_id_) + sizeof(SkillMsg::_impl_.skill_id_) - PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.player_id_)>( | |||
| PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.skill_param_) + sizeof(SkillMsg::_impl_.skill_param_) - PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.player_id_)>( | |||
| reinterpret_cast<char*>(&_impl_.player_id_), | |||
| reinterpret_cast<char*>(&other->_impl_.player_id_) | |||
| ); | |||
| @@ -1813,6 +1813,7 @@ namespace protobuf | |||
| { | |||
| kPlayerIdFieldNumber = 1, | |||
| kSkillIdFieldNumber = 2, | |||
| kSkillParamFieldNumber = 3, | |||
| }; | |||
| // int64 player_id = 1; | |||
| void clear_player_id(); | |||
| @@ -1833,6 +1834,16 @@ namespace protobuf | |||
| int32_t _internal_skill_id() const; | |||
| void _internal_set_skill_id(int32_t value); | |||
| public: | |||
| // int32 skill_param = 3; | |||
| void clear_skill_param(); | |||
| int32_t skill_param() const; | |||
| void set_skill_param(int32_t value); | |||
| private: | |||
| int32_t _internal_skill_param() const; | |||
| void _internal_set_skill_param(int32_t value); | |||
| public: | |||
| // @@protoc_insertion_point(class_scope:protobuf.SkillMsg) | |||
| @@ -1847,6 +1858,7 @@ namespace protobuf | |||
| { | |||
| int64_t player_id_; | |||
| int32_t skill_id_; | |||
| int32_t skill_param_; | |||
| mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; | |||
| }; | |||
| union | |||
| @@ -2597,6 +2609,30 @@ namespace protobuf | |||
| // @@protoc_insertion_point(field_set:protobuf.SkillMsg.skill_id) | |||
| } | |||
| // int32 skill_param = 3; | |||
| inline void SkillMsg::clear_skill_param() | |||
| { | |||
| _impl_.skill_param_ = 0; | |||
| } | |||
| inline int32_t SkillMsg::_internal_skill_param() const | |||
| { | |||
| return _impl_.skill_param_; | |||
| } | |||
| inline int32_t SkillMsg::skill_param() const | |||
| { | |||
| // @@protoc_insertion_point(field_get:protobuf.SkillMsg.skill_param) | |||
| return _internal_skill_param(); | |||
| } | |||
| inline void SkillMsg::_internal_set_skill_param(int32_t value) | |||
| { | |||
| _impl_.skill_param_ = value; | |||
| } | |||
| inline void SkillMsg::set_skill_param(int32_t value) | |||
| { | |||
| _internal_set_skill_param(value); | |||
| // @@protoc_insertion_point(field_set:protobuf.SkillMsg.skill_param) | |||
| } | |||
| #ifdef __GNUC__ | |||
| #pragma GCC diagnostic pop | |||
| #endif // __GNUC__ | |||
| @@ -30,49 +30,49 @@ static constexpr ::_pbi::MigrationSchema* schemas = nullptr; | |||
| static constexpr ::_pb::Message* const* file_default_instances = nullptr; | |||
| const char descriptor_table_protodef_MessageType_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = | |||
| "\n\021MessageType.proto\022\010protobuf*\202\001\n\nBullet" | |||
| "Type\022\024\n\020NULL_BULLET_TYPE\020\000\022\020\n\014FLYING_KNI" | |||
| "FE\020\001\022\034\n\030COMMON_ATTACK_OF_TRICKER\020\002\022\r\n\tBO" | |||
| "MB_BOMB\020\003\022\020\n\014JUMPY_DUMPTY\020\004\022\r\n\tATOM_BOMB" | |||
| "\020\005*\241\001\n\tPlaceType\022\023\n\017NULL_PLACE_TYPE\020\000\022\010\n" | |||
| "\004LAND\020\001\022\010\n\004WALL\020\002\022\t\n\005GRASS\020\003\022\r\n\tCLASSROO" | |||
| "M\020\004\022\010\n\004GATE\020\005\022\017\n\013HIDDEN_GATE\020\006\022\n\n\006WINDOW" | |||
| "\020\007\022\t\n\005DOOR3\020\010\022\t\n\005DOOR5\020\t\022\t\n\005DOOR6\020\n\022\t\n\005C" | |||
| "HEST\020\013*8\n\tShapeType\022\023\n\017NULL_SHAPE_TYPE\020\000" | |||
| "\022\n\n\006CIRCLE\020\001\022\n\n\006SQUARE\020\002*\256\001\n\010PropType\022\022\n" | |||
| "\016NULL_PROP_TYPE\020\000\022\r\n\tADD_SPEED\020\001\022\035\n\031ADD_" | |||
| "LIFE_OR_CLAIRAUDIENCE\020\002\022\020\n\014ADD_HP_OR_AP\020" | |||
| "\003\022\023\n\017SHIELD_OR_SPEAR\020\004\022\010\n\004KEY3\020\005\022\010\n\004KEY5" | |||
| "\020\006\022\010\n\004KEY6\020\007\022\033\n\027RECOVERY_FROM_DIZZINESS\020" | |||
| "\010*n\n\017StudentBuffType\022\023\n\017NULL_SBUFF_TYPE\020" | |||
| "\000\022\025\n\021STUDENT_ADD_SPEED\020\001\022\014\n\010ADD_LIFE\020\002\022\n" | |||
| "\n\006SHIELD\020\003\022\025\n\021STUDENT_INVISIBLE\020\004*\251\002\n\013Pl" | |||
| "ayerState\022\017\n\013NULL_STATUS\020\000\022\010\n\004IDLE\020\001\022\014\n\010" | |||
| "LEARNING\020\002\022\014\n\010ADDICTED\020\003\022\010\n\004QUIT\020\004\022\r\n\tGR" | |||
| "ADUATED\020\005\022\013\n\007TREATED\020\006\022\013\n\007RESCUED\020\007\022\013\n\007S" | |||
| "TUNNED\020\010\022\014\n\010TREATING\020\t\022\014\n\010RESCUING\020\n\022\014\n\010" | |||
| "SWINGING\020\013\022\r\n\tATTACKING\020\014\022\013\n\007LOCKING\020\r\022\r" | |||
| "\n\tRUMMAGING\020\016\022\014\n\010CLIMBING\020\017\022\023\n\017OPENING_A" | |||
| "_CHEST\020\020\022\027\n\023USING_SPECIAL_SKILL\020\021\022\022\n\016OPE" | |||
| "NING_A_GATE\020\022*~\n\017TrickerBuffType\022\023\n\017NULL" | |||
| "_TBUFF_TYPE\020\000\022\025\n\021TRICKER_ADD_SPEED\020\001\022\t\n\005" | |||
| "SPEAR\020\002\022\n\n\006ADD_AP\020\003\022\021\n\rCLAIRAUDIENCE\020\004\022\025" | |||
| "\n\021TRICKER_INVISIBLE\020\005*J\n\nPlayerType\022\024\n\020N" | |||
| "ULL_PLAYER_TYPE\020\000\022\022\n\016STUDENT_PLAYER\020\001\022\022\n" | |||
| "\016TRICKER_PLAYER\020\002*\177\n\013StudentType\022\025\n\021NULL" | |||
| "_STUDENT_TYPE\020\000\022\013\n\007ATHLETE\020\001\022\013\n\007TEACHER\020" | |||
| "\002\022\026\n\022STRAIGHT_A_STUDENT\020\003\022\t\n\005ROBOT\020\004\022\016\n\n" | |||
| "TECH_OTAKU\020\005\022\014\n\010SUNSHINE\020\006*Z\n\013TrickerTyp" | |||
| "e\022\025\n\021NULL_TRICKER_TYPE\020\000\022\014\n\010ASSASSIN\020\001\022\010" | |||
| "\n\004KLEE\020\002\022\022\n\016A_NOISY_PERSON\020\003\022\010\n\004IDOL\020\004*P" | |||
| "\n\tGameState\022\023\n\017NULL_GAME_STATE\020\000\022\016\n\nGAME" | |||
| "_START\020\001\022\020\n\014GAME_RUNNING\020\002\022\014\n\010GAME_END\020\003" | |||
| "b\006proto3"; | |||
| "\n\021MessageType.proto\022\010protobuf*\177\n\nBulletT" | |||
| "ype\022\024\n\020NULL_BULLET_TYPE\020\000\022\020\n\014FLYING_KNIF" | |||
| "E\020\001\022\034\n\030COMMON_ATTACK_OF_TRICKER\020\002\022\r\n\tBOM" | |||
| "B_BOMB\020\003\022\020\n\014JUMPY_DUMPTY\020\004\022\n\n\006STRIKE\020\005*\241" | |||
| "\001\n\tPlaceType\022\023\n\017NULL_PLACE_TYPE\020\000\022\010\n\004LAN" | |||
| "D\020\001\022\010\n\004WALL\020\002\022\t\n\005GRASS\020\003\022\r\n\tCLASSROOM\020\004\022" | |||
| "\010\n\004GATE\020\005\022\017\n\013HIDDEN_GATE\020\006\022\n\n\006WINDOW\020\007\022\t" | |||
| "\n\005DOOR3\020\010\022\t\n\005DOOR5\020\t\022\t\n\005DOOR6\020\n\022\t\n\005CHEST" | |||
| "\020\013*8\n\tShapeType\022\023\n\017NULL_SHAPE_TYPE\020\000\022\n\n\006" | |||
| "CIRCLE\020\001\022\n\n\006SQUARE\020\002*\302\001\n\010PropType\022\022\n\016NUL" | |||
| "L_PROP_TYPE\020\000\022\r\n\tADD_SPEED\020\001\022\035\n\031ADD_LIFE" | |||
| "_OR_CLAIRAUDIENCE\020\002\022\020\n\014ADD_HP_OR_AP\020\003\022\023\n" | |||
| "\017SHIELD_OR_SPEAR\020\004\022\010\n\004KEY3\020\005\022\010\n\004KEY5\020\006\022\010" | |||
| "\n\004KEY6\020\007\022\033\n\027RECOVERY_FROM_DIZZINESS\020\010\022\022\n" | |||
| "\016CRAFTING_BENCH\020\t*n\n\017StudentBuffType\022\023\n\017" | |||
| "NULL_SBUFF_TYPE\020\000\022\025\n\021STUDENT_ADD_SPEED\020\001" | |||
| "\022\014\n\010ADD_LIFE\020\002\022\n\n\006SHIELD\020\003\022\025\n\021STUDENT_IN" | |||
| "VISIBLE\020\004*\251\002\n\013PlayerState\022\017\n\013NULL_STATUS" | |||
| "\020\000\022\010\n\004IDLE\020\001\022\014\n\010LEARNING\020\002\022\014\n\010ADDICTED\020\003" | |||
| "\022\010\n\004QUIT\020\004\022\r\n\tGRADUATED\020\005\022\013\n\007TREATED\020\006\022\013" | |||
| "\n\007RESCUED\020\007\022\013\n\007STUNNED\020\010\022\014\n\010TREATING\020\t\022\014" | |||
| "\n\010RESCUING\020\n\022\014\n\010SWINGING\020\013\022\r\n\tATTACKING\020" | |||
| "\014\022\013\n\007LOCKING\020\r\022\r\n\tRUMMAGING\020\016\022\014\n\010CLIMBIN" | |||
| "G\020\017\022\023\n\017OPENING_A_CHEST\020\020\022\027\n\023USING_SPECIA" | |||
| "L_SKILL\020\021\022\022\n\016OPENING_A_GATE\020\022*~\n\017Tricker" | |||
| "BuffType\022\023\n\017NULL_TBUFF_TYPE\020\000\022\025\n\021TRICKER" | |||
| "_ADD_SPEED\020\001\022\t\n\005SPEAR\020\002\022\n\n\006ADD_AP\020\003\022\021\n\rC" | |||
| "LAIRAUDIENCE\020\004\022\025\n\021TRICKER_INVISIBLE\020\005*J\n" | |||
| "\nPlayerType\022\024\n\020NULL_PLAYER_TYPE\020\000\022\022\n\016STU" | |||
| "DENT_PLAYER\020\001\022\022\n\016TRICKER_PLAYER\020\002*\177\n\013Stu" | |||
| "dentType\022\025\n\021NULL_STUDENT_TYPE\020\000\022\013\n\007ATHLE" | |||
| "TE\020\001\022\013\n\007TEACHER\020\002\022\026\n\022STRAIGHT_A_STUDENT\020" | |||
| "\003\022\t\n\005ROBOT\020\004\022\016\n\nTECH_OTAKU\020\005\022\014\n\010SUNSHINE" | |||
| "\020\006*Z\n\013TrickerType\022\025\n\021NULL_TRICKER_TYPE\020\000" | |||
| "\022\014\n\010ASSASSIN\020\001\022\010\n\004KLEE\020\002\022\022\n\016A_NOISY_PERS" | |||
| "ON\020\003\022\010\n\004IDOL\020\004*P\n\tGameState\022\023\n\017NULL_GAME" | |||
| "_STATE\020\000\022\016\n\nGAME_START\020\001\022\020\n\014GAME_RUNNING" | |||
| "\020\002\022\014\n\010GAME_END\020\003b\006proto3"; | |||
| static ::_pbi::once_flag descriptor_table_MessageType_2eproto_once; | |||
| const ::_pbi::DescriptorTable descriptor_table_MessageType_2eproto = { | |||
| false, | |||
| false, | |||
| 1488, | |||
| 1504, | |||
| descriptor_table_protodef_MessageType_2eproto, | |||
| "MessageType.proto", | |||
| &descriptor_table_MessageType_2eproto_once, | |||
| @@ -179,6 +179,7 @@ namespace protobuf | |||
| case 6: | |||
| case 7: | |||
| case 8: | |||
| case 9: | |||
| return true; | |||
| default: | |||
| return false; | |||
| @@ -57,13 +57,13 @@ namespace protobuf | |||
| COMMON_ATTACK_OF_TRICKER = 2, | |||
| BOMB_BOMB = 3, | |||
| JUMPY_DUMPTY = 4, | |||
| ATOM_BOMB = 5, | |||
| STRIKE = 5, | |||
| BulletType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), | |||
| BulletType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() | |||
| }; | |||
| bool BulletType_IsValid(int value); | |||
| constexpr BulletType BulletType_MIN = NULL_BULLET_TYPE; | |||
| constexpr BulletType BulletType_MAX = ATOM_BOMB; | |||
| constexpr BulletType BulletType_MAX = STRIKE; | |||
| constexpr int BulletType_ARRAYSIZE = BulletType_MAX + 1; | |||
| const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* BulletType_descriptor(); | |||
| @@ -163,12 +163,13 @@ namespace protobuf | |||
| KEY5 = 6, | |||
| KEY6 = 7, | |||
| RECOVERY_FROM_DIZZINESS = 8, | |||
| CRAFTING_BENCH = 9, | |||
| PropType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), | |||
| PropType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() | |||
| }; | |||
| bool PropType_IsValid(int value); | |||
| constexpr PropType PropType_MIN = NULL_PROP_TYPE; | |||
| constexpr PropType PropType_MAX = RECOVERY_FROM_DIZZINESS; | |||
| constexpr PropType PropType_MAX = CRAFTING_BENCH; | |||
| constexpr int PropType_ARRAYSIZE = PropType_MAX + 1; | |||
| const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PropType_descriptor(); | |||
| @@ -43,8 +43,8 @@ class StudentAPI(IStudentAPI, IGameTimer): | |||
| def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||
| return self.__pool.submit(self.__logic.ThrowProp, propType) | |||
| def UseSkill(self, skillID: int) -> Future[bool]: | |||
| return self.__pool.submit(self.__logic.UseSkill, skillID) | |||
| def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]: | |||
| return self.__pool.submit(self.__logic.UseSkill, skillID, skillParam) | |||
| # 与地图交互相关 | |||
| def OpenDoor(self) -> Future[bool]: | |||
| @@ -224,8 +224,8 @@ class TrickerAPI(ITrickerAPI, IGameTimer): | |||
| def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: | |||
| return self.__pool.submit(self.__logic.ThrowProp, propType) | |||
| def UseSkill(self, skillID: int) -> Future[bool]: | |||
| return self.__pool.submit(self.__logic.UseSkill, skillID) | |||
| def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]: | |||
| return self.__pool.submit(self.__logic.UseSkill, skillID, skillParam) | |||
| # 与地图交互相关 | |||
| def OpenDoor(self) -> Future[bool]: | |||
| @@ -91,14 +91,14 @@ class Communication: | |||
| else: | |||
| return throwResult.act_success | |||
| def UseSkill(self, skillID: int, playerID: int) -> bool: | |||
| def UseSkill(self, skillID: int, skillParam: int, playerID: int) -> bool: | |||
| try: | |||
| with self.__mtxLimit: | |||
| if self.__counter >= self.__limit: | |||
| return False | |||
| self.__counter += 1 | |||
| useResult = self.__THUAI6Stub.UseSkill( | |||
| THUAI62Proto.THUAI62ProtobufSkill(skillID, playerID) | |||
| THUAI62Proto.THUAI62ProtobufSkill(skillID, skillParam, playerID) | |||
| ) | |||
| except grpc.RpcError as e: | |||
| return False | |||
| @@ -132,13 +132,13 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): | |||
| return self.__pool.submit(logThrow) | |||
| def UseSkill(self, skillID: int) -> Future[bool]: | |||
| def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]: | |||
| self.__logger.info( | |||
| f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms" | |||
| f"UseSkill: skillID = {skillID}, skillParam = {skillParam}, called at {self.__GetTime()}ms" | |||
| ) | |||
| def logUse() -> bool: | |||
| result = self.__logic.UseSkill(skillID) | |||
| result = self.__logic.UseSkill(skillID, skillParam) | |||
| if not result: | |||
| self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms") | |||
| return result | |||
| @@ -600,13 +600,13 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): | |||
| return self.__pool.submit(logThrow) | |||
| def UseSkill(self, skillID: int) -> Future[bool]: | |||
| def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]: | |||
| self.__logger.info( | |||
| f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms" | |||
| f"UseSkill: skillID = {skillID}, skillParam = {skillParam}, called at {self.__GetTime()}ms" | |||
| ) | |||
| def logUse() -> bool: | |||
| result = self.__logic.UseSkill(skillID) | |||
| result = self.__logic.UseSkill(skillID, skillParam) | |||
| if not result: | |||
| self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms") | |||
| return result | |||
| @@ -80,7 +80,7 @@ class ILogic(metaclass=ABCMeta): | |||
| pass | |||
| @abstractmethod | |||
| def UseSkill(self, skillID: int) -> bool: | |||
| def UseSkill(self, skillID: int, skillParam: int) -> bool: | |||
| pass | |||
| @abstractmethod | |||
| @@ -201,7 +201,7 @@ class IAPI(metaclass=ABCMeta): | |||
| pass | |||
| @abstractmethod | |||
| def UseSkill(self, skillID: int) -> Future[bool]: | |||
| def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]: | |||
| pass | |||
| @abstractmethod | |||
| @@ -190,9 +190,9 @@ class Logic(ILogic): | |||
| self.__logger.debug("Called ThrowProp") | |||
| return self.__comm.ThrowProp(propType, self.__playerID) | |||
| def UseSkill(self, skillID: int) -> bool: | |||
| def UseSkill(self, skillID: int, skillParam: int) -> bool: | |||
| self.__logger.debug("Called UseSkill") | |||
| return self.__comm.UseSkill(skillID, self.__playerID) | |||
| return self.__comm.UseSkill(skillID, skillParam, self.__playerID) | |||
| def SendMessage(self, toID: int, message: Union[str, bytes]) -> bool: | |||
| self.__logger.debug("Called SendMessage") | |||
| @@ -52,6 +52,7 @@ class PropType(Enum): | |||
| AddHpOrAp = 6 | |||
| ShieldOrSpear = 7 | |||
| RecoveryFromDizziness = 8 | |||
| CraftingBench = 9 | |||
| class BulletType(Enum): | |||
| @@ -60,7 +61,7 @@ class BulletType(Enum): | |||
| CommonAttackOfTricker = 2 | |||
| BombBomb = 3 | |||
| JumpyDumpty = 4 | |||
| AtomBomb = 5 | |||
| Strike = 5 | |||
| class StudentType(Enum): | |||
| @@ -109,6 +109,7 @@ class Proto2THUAI6(NoInstance): | |||
| MessageType.ADD_LIFE_OR_CLAIRAUDIENCE: THUAI6.PropType.AddLifeOrClairaudience, | |||
| MessageType.SHIELD_OR_SPEAR: THUAI6.PropType.ShieldOrSpear, | |||
| MessageType.RECOVERY_FROM_DIZZINESS: THUAI6.PropType.RecoveryFromDizziness, | |||
| MessageType.CRAFTING_BENCH: THUAI6.PropType.CraftingBench, | |||
| } | |||
| playerTypeDict: Final[dict] = { | |||
| @@ -187,7 +188,7 @@ class Proto2THUAI6(NoInstance): | |||
| MessageType.BOMB_BOMB: THUAI6.BulletType.BombBomb, | |||
| MessageType.COMMON_ATTACK_OF_TRICKER: THUAI6.BulletType.CommonAttackOfTricker, | |||
| MessageType.JUMPY_DUMPTY: THUAI6.BulletType.JumpyDumpty, | |||
| MessageType.ATOM_BOMB: THUAI6.BulletType.AtomBomb, | |||
| MessageType.STRIKE: THUAI6.BulletType.Strike, | |||
| } | |||
| # 用于将Proto的对象转为THUAI6的对象 | |||
| @@ -357,6 +358,7 @@ class THUAI62Proto(NoInstance): | |||
| THUAI6.PropType.AddLifeOrClairaudience: MessageType.ADD_LIFE_OR_CLAIRAUDIENCE, | |||
| THUAI6.PropType.AddSpeed: MessageType.ADD_SPEED, | |||
| THUAI6.PropType.ShieldOrSpear: MessageType.SHIELD_OR_SPEAR, | |||
| THUAI6.PropType.CraftingBench: MessageType.CRAFTING_BENCH, | |||
| } | |||
| # 用于将THUAI6的对象转为Proto的对象 | |||
| @@ -421,5 +423,9 @@ class THUAI62Proto(NoInstance): | |||
| return Message2Server.AttackMsg(player_id=id, angle=angle) | |||
| @staticmethod | |||
| def THUAI62ProtobufSkill(skillID: int, id: int) -> Message2Server.SkillMsg: | |||
| return Message2Server.SkillMsg(player_id=id, skill_id=skillID) | |||
| def THUAI62ProtobufSkill( | |||
| skillID: int, skillParam: int, id: int | |||
| ) -> Message2Server.SkillMsg: | |||
| return Message2Server.SkillMsg( | |||
| player_id=id, skill_id=skillID, skill_param=skillParam | |||
| ) | |||
| @@ -1,3 +1,3 @@ | |||
| grpcio==1.54.0 | |||
| grpcio-tools==1.54.0 | |||
| grpcio==1.54.2 | |||
| grpcio-tools==1.54.2 | |||
| numpy | |||
| @@ -61,6 +61,7 @@ message SkillMsg | |||
| { | |||
| int64 player_id = 1; | |||
| int32 skill_id = 2; | |||
| int32 skill_param = 3; | |||
| } | |||
| // 基本继承于THUAI5,为了使发送的信息尽可能不被浪费,暂定不发这类大包。 | |||
| @@ -9,7 +9,7 @@ enum BulletType | |||
| COMMON_ATTACK_OF_TRICKER = 2; | |||
| BOMB_BOMB = 3; | |||
| JUMPY_DUMPTY = 4; | |||
| ATOM_BOMB = 5; | |||
| STRIKE = 5; | |||
| } | |||
| enum PlaceType // 地图中的所有物件类型 | |||
| @@ -49,6 +49,7 @@ enum PropType // 地图中的可拾取道具类型 | |||
| KEY5 = 6; | |||
| KEY6 = 7; | |||
| RECOVERY_FROM_DIZZINESS = 8; | |||
| CRAFTING_BENCH = 9; | |||
| } | |||
| enum StudentBuffType // 人类可用的增益效果类型 | |||
| @@ -78,7 +79,7 @@ enum PlayerState | |||
| LOCKING = 13; | |||
| RUMMAGING = 14; | |||
| CLIMBING = 15; // 翻窗 | |||
| OPENING_A_CHEST =16; | |||
| OPENING_A_CHEST = 16; | |||
| USING_SPECIAL_SKILL = 17; | |||
| OPENING_A_GATE =18; | |||
| } | |||
| @@ -109,7 +110,7 @@ enum StudentType | |||
| TEACHER = 2; | |||
| STRAIGHT_A_STUDENT = 3; | |||
| ROBOT = 4; | |||
| TECH_OTAKU =5; | |||
| TECH_OTAKU = 5; | |||
| SUNSHINE = 6; | |||
| } | |||
| @@ -309,7 +309,7 @@ $$ | |||
| - 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。 | |||
| ### 人物 | |||
| - 眩晕状态中的玩家不能再次被眩晕 | |||
| - 眩晕状态中的玩家不能再次被眩晕(除非是ShowTime) | |||
| ### 初始状态 | |||
| - 初赛玩家出生点固定且一定为空地 | |||
| @@ -342,6 +342,7 @@ $$ | |||
| - 开锁门进度中断后清空 | |||
| ### 窗 | |||
| - 由于窗户被占用导致翻窗失败会使先前行动停止 | |||
| - 翻越窗户是一种交互行为,翻窗一共有两个过程 | |||
| - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 | |||
| - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 | |||
| @@ -1,5 +1,5 @@ | |||
| # 游戏机制与平衡性调整更新草案 | |||
| v1.5 | |||
| v1.6 | |||
| ## 说明 | |||
| - 该草案尚未完全确定,请大家不要过分依靠该文档进行修改自己的代码 | |||
| @@ -21,9 +21,9 @@ v1.5 | |||
| - 未攻击至目标时的后摇改为1200ms | |||
| - 增强为“可以攻击未写完的作业” | |||
| - 增强为“可以攻击使门被打开(可以重新被锁上)” | |||
| - 改为“当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上90°,180° ,270° 发出3个小炸弹” | |||
| - 小炸弹JumpyDumpty | |||
| - 小炸弹不受道具增益影响 | |||
| - 修改为“小炸弹与自己无碰撞体积” | |||
| - strike(新增) | |||
| - 可以攻击未写完的作业 | |||
| @@ -105,3 +105,14 @@ v1.5 | |||
| - 普通攻击改为strike | |||
| - Klee | |||
| - 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙) | |||
| - 主动技能SparksNSplash(新增): | |||
| - CD:45s, 持续时间:10s | |||
| - 技能使用瞬间,对于输入的额外参数PlayerID代表的角色,距离最近的本已停止运动的小炸弹开始追踪该角色(每50ms向该角色直线移动) | |||
| - 主动技能 蹦蹦炸弹 JumpyBomb | |||
| - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上0°,45°,90°,135°,180°,225°,270°,315° 发出8个小炸弹 | |||
| - Idol | |||
| 主动技能ShowTime改为 | |||
| "持续时间内 | |||
| - 使警戒范围外的学生眩晕并每**500ms**发送向自己移动**500ms**的指令(速度为学生本应速度*二者距离/警戒范围) | |||
| - 对于视野范围(不是可视区域)内的学生每**500ms**加**1500**的沉迷度 | |||
| - 捣蛋鬼变为0.8倍速" | |||
| @@ -21,11 +21,14 @@ | |||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | |||
| - change:更改了地图的文件路径 | |||
| # 最新更新 | |||
| # 5月10日更新 | |||
| - fix:修复JumpyDumpty的初始位置错误的问题 | |||
| - fix:修正和重新说明攻击距离 | |||
| - **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2** | |||
| - hotfix:修复小炸弹初始化类型错误的问题 | |||
| - remove:去除了“实际上唤醒或勉励不同的人是有效的” | |||
| - **重复发出同一类型的交互指令和移动指令是无效的** | |||
| - feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`** | |||
| - feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`** | |||
| # 最新更新 | |||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | |||
| @@ -52,7 +52,7 @@ | |||
| <Button Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Name="UninstBtn" Content="卸载选手包" Command="{Binding ClickUninstCommand}" Visibility="{Binding MenuVis}" /> | |||
| <Button Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="2" Name="MenuBackBtn" Content="回到登陆界面" Command="{Binding ClickBackCommand}" Visibility="{Binding MenuVis}" /> | |||
| <TextBlock Grid.Row="3" Grid.Column="3" Text="正在处理……" Grid.ColumnSpan="2" Visibility="{Binding ProgressVis}"/> | |||
| <TextBlock Grid.Row="3" Grid.Column="3" Text="{Binding ProcessingIntro}" Grid.ColumnSpan="2" Visibility="{Binding ProgressVis}"/> | |||
| <ProgressBar Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="7" Minimum="0" Maximum="100" Name="Progress" Visibility="{Binding ProgressVis}" IsIndeterminate="True"/> | |||
| <TextBlock Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="4" Text="操作完成!你可以继续操作或退出" Visibility="{Binding CompleteVis}"/> | |||
| @@ -81,10 +81,21 @@ | |||
| <TextBlock Grid.Row="5" Grid.Column="1" Foreground="Red" Text=" 用户名或密码错误!" Visibility="{Binding LoginFailVis}"/> | |||
| </Grid> | |||
| </StackPanel> | |||
| <Button Grid.Row="7" Grid.Column="1" Name="Login" Content="登录" Command="{Binding ClickLoginCommand}" Visibility="{Binding LoginVis}"/> | |||
| <Button Grid.Row="7" Grid.Column="2" Name="Launch" FontSize="11" Content="{Binding LaunchBtnCont}" Command="{Binding ClickLaunchCommand}" Visibility="{Binding LoginVis}"/> | |||
| <Button Grid.Row="7" Grid.Column="3" Name="ShiftLanguage" FontSize="11" Content="更改语言" Command="{Binding ClickShiftLanguageCommand}" Visibility="{Binding LaunchVis}"/> | |||
| <StackPanel Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="3"> | |||
| <Grid> | |||
| <Grid.RowDefinitions> | |||
| <RowDefinition Height="20"/> | |||
| </Grid.RowDefinitions> | |||
| <Grid.ColumnDefinitions> | |||
| <ColumnDefinition Width="70"/> | |||
| <ColumnDefinition Width="70"/> | |||
| <ColumnDefinition Width="60"/> | |||
| </Grid.ColumnDefinitions> | |||
| <Button Grid.Row="0" Grid.Column="0" Name="Login" Content="登录" Command="{Binding ClickLoginCommand}" Visibility="{Binding LoginVis}"/> | |||
| <Button Grid.Row="0" Grid.Column="1" Name="Launch" Content="{Binding LaunchBtnCont}" Command="{Binding ClickLaunchCommand}" Visibility="{Binding LoginVis}" IsEnabled="{Binding UpdatePlanned}"/> | |||
| <Button Grid.Row="0" Grid.Column="2" Name="ShiftLanguage" FontSize="11" Content="配置启动器" Command="{Binding ClickShiftLanguageCommand}" Visibility="{Binding LaunchVis}" IsEnabled="False"/> | |||
| </Grid> | |||
| </StackPanel> | |||
| <Button Grid.Row="7" Grid.Column="4" Grid.ColumnSpan="2" Name="Edit" Content="修改文件" Command="{Binding ClickEditCommand}" Visibility="{Binding LoginVis}"/> | |||
| @@ -114,5 +125,40 @@ | |||
| <Button Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" Content="申请对战" IsEnabled="False" Command="{Binding ClickRequestCommand}" Visibility="{Binding WebVis}" /> | |||
| <TextBox Grid.Row="4" Grid.Column="3" Grid.ColumnSpan="2" Text="暂不支持" IsEnabled="False" Visibility="{Binding WebVis}" /> | |||
| <!--objects below are enabled--> | |||
| <StackPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="7"> | |||
| <Grid> | |||
| <Grid.RowDefinitions> | |||
| <RowDefinition Height="20"/> | |||
| </Grid.RowDefinitions> | |||
| <Grid.ColumnDefinitions> | |||
| <ColumnDefinition Width="90"/> | |||
| <ColumnDefinition Width="90"/> | |||
| <ColumnDefinition Width="90"/> | |||
| <ColumnDefinition Width="*"/> | |||
| </Grid.ColumnDefinitions> | |||
| <Button Grid.Column="0" Name ="ConfigServer" Content="服务端" Command="{Binding ClickCfgServCommand}" IsEnabled="{Binding ServerCfgNotSelected}" Visibility="{Binding CfgVis}"/> | |||
| <Button Grid.Column="1" Name ="ConfigAI" Content="客户端" Command="{Binding ClickCfgCliCommand}" IsEnabled="{Binding CliCfhNotSelected}" Visibility="{Binding CfgVis}"/> | |||
| <Button Grid.Column="2" Name ="ConfigCliend" Content="图形界面" Command="{Binding ClickCfgGUICommand}" IsEnabled="{Binding GUICfgNotSelected}" Visibility="{Binding CfgVis}"/> | |||
| </Grid> | |||
| </StackPanel> | |||
| <StackPanel Grid.Row="3" Grid.Column="1" Grid.RowSpan="5" Grid.ColumnSpan="7"> | |||
| <Grid> | |||
| <Grid.RowDefinitions> | |||
| <RowDefinition Height="20"/> | |||
| <RowDefinition Height="20"/> | |||
| <RowDefinition Height="20"/> | |||
| <RowDefinition Height="20"/> | |||
| <RowDefinition Height="20"/> | |||
| </Grid.RowDefinitions> | |||
| <Grid.ColumnDefinitions> | |||
| <ColumnDefinition Width="50"/> | |||
| <ColumnDefinition Width="75"/> | |||
| <ColumnDefinition Width="25"/> | |||
| <ColumnDefinition Width="75"/> | |||
| <ColumnDefinition Width="*"/> | |||
| </Grid.ColumnDefinitions> | |||
| </Grid> | |||
| </StackPanel> | |||
| </Grid> | |||
| </Window> | |||
| @@ -405,7 +405,7 @@ namespace starter.viewmodel.settings | |||
| /// <summary> | |||
| /// 关于介绍的屏幕显示信息 | |||
| /// </summary> | |||
| public enum Status { newUser, menu, move, working, disconnected, error, successful, login, web }; | |||
| public enum Status { newUser, menu, move, working, initializing, disconnected, error, successful, login, web, launch }; | |||
| public Status status | |||
| { | |||
| get; set; | |||
| @@ -881,7 +881,7 @@ namespace Downloader | |||
| int upcnt = updateFileName.Count; | |||
| if (upcnt <= 20) | |||
| { | |||
| while (newFileName.TryDequeue(out var filename)) | |||
| while (updateFileName.TryDequeue(out var filename)) | |||
| { | |||
| try | |||
| { | |||
| @@ -22,6 +22,7 @@ namespace starter.viewmodel.settings | |||
| //定义BackgroundWorker | |||
| BackgroundWorker asyncDownloader; | |||
| BackgroundWorker asyncUpdater; | |||
| BackgroundWorker asyncInitializer; | |||
| /// <summary> | |||
| /// Model object | |||
| /// </summary> | |||
| @@ -38,7 +39,7 @@ namespace starter.viewmodel.settings | |||
| Status = SettingsModel.Status.working; | |||
| string currentDirectory = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName) | |||
| ?? throw new Exception("Fail to get current directory"); | |||
| MessageBox.Show("更新器工作正常"); | |||
| //MessageBox.Show("更新器工作正常"); | |||
| if (!Program.Tencent_cos_download.SelfUpdateDismissed()) | |||
| { | |||
| switch (Program.Tencent_cos_download.CheckSelfVersion()) | |||
| @@ -53,49 +54,91 @@ namespace starter.viewmodel.settings | |||
| } | |||
| } | |||
| //实例化BackgroundWorker | |||
| asyncDownloader = new BackgroundWorker(); | |||
| asyncUpdater = new BackgroundWorker(); | |||
| //指示BackgroundWorker是否可以报告进度更新 | |||
| //当该属性值为True是,将可以成功调用ReportProgress方法,否则将引发InvalidOperationException异常。 | |||
| asyncDownloader.WorkerReportsProgress = true; | |||
| asyncUpdater.WorkerReportsProgress = true; | |||
| //挂载方法: | |||
| asyncDownloader.DoWork += AsyncDownloader_DoWork; | |||
| asyncUpdater.DoWork += AsyncUpdater_DoWork; | |||
| //完成通知器: | |||
| asyncDownloader.RunWorkerCompleted += AsyncDownloader_RunWorkerCompleted; | |||
| asyncUpdater.RunWorkerCompleted += AsyncUpdater_RunWorkerCompleted; | |||
| UpdateInfoVis = Visibility.Collapsed; | |||
| if (Downloader.Program.Tencent_cos_download.CheckAlreadyDownload()) | |||
| asyncInitializer = new BackgroundWorker(); | |||
| asyncInitializer.WorkerReportsProgress = true; | |||
| asyncInitializer.DoWork += AsyncInitializer_DoWork; | |||
| asyncInitializer.RunWorkerCompleted += AsyncInitializer_RunWorkerCompleted; | |||
| asyncInitializer.RunWorkerAsync(); | |||
| Status = SettingsModel.Status.initializing; | |||
| } | |||
| private void AsyncInitializer_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e) | |||
| { | |||
| if (e.Result == null) | |||
| { | |||
| Status = SettingsModel.Status.error; | |||
| } | |||
| else if ((int)e.Result == 1) | |||
| { | |||
| obj.checkUpdate(); | |||
| Status = SettingsModel.Status.login; | |||
| this.RaisePropertyChanged("WindowWidth"); | |||
| this.RaisePropertyChanged("LaunchVis"); | |||
| if (obj.RecallUser()) | |||
| RememberMe = true; | |||
| else | |||
| RememberMe = false; | |||
| this.RaisePropertyChanged("RememberMe"); | |||
| this.RaisePropertyChanged("SwitchOSBtnCont"); | |||
| //在启动时立刻检查更新,确保选手启动最新版选手包 | |||
| //若有更新,将启动键改为更新键; | |||
| //相应地,使用login界面启动; | |||
| //结构:上方为登录框架,下方有“修改选手包”按钮 | |||
| //下面几行是用来运行测试的代码 | |||
| //Program.RunProgram.StartServerForDebug("0.0.0.0",8888,4,1,600,"video"); | |||
| //Program.RunProgram.RunCpp("127.0.0.1",8888,4,1,false,true,false); | |||
| //Program.RunProgram.RunGUIClient("127.0.0.1", 8888, 0, true); | |||
| this.RaisePropertyChanged("UpdateBtnCont"); | |||
| this.RaisePropertyChanged("LaunchBtnCont"); | |||
| this.RaisePropertyChanged("Updateinfo"); | |||
| this.RaisePropertyChanged("UpdatePlanned"); | |||
| } | |||
| else | |||
| { | |||
| Route = Environment.GetEnvironmentVariable("USERPROFILE") + "\\THUAI6"; | |||
| Status = SettingsModel.Status.newUser; | |||
| this.RaisePropertyChanged("WindowWidth"); | |||
| } | |||
| } | |||
| private void AsyncInitializer_DoWork(object? sender, DoWorkEventArgs e) | |||
| { | |||
| if (asyncInitializer.CancellationPending) | |||
| { | |||
| Status = SettingsModel.Status.error; | |||
| e.Cancel = true; | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| //实例化BackgroundWorker | |||
| asyncDownloader = new BackgroundWorker(); | |||
| asyncUpdater = new BackgroundWorker(); | |||
| //指示BackgroundWorker是否可以报告进度更新 | |||
| //当该属性值为True是,将可以成功调用ReportProgress方法,否则将引发InvalidOperationException异常。 | |||
| asyncDownloader.WorkerReportsProgress = true; | |||
| asyncUpdater.WorkerReportsProgress = true; | |||
| //挂载方法: | |||
| asyncDownloader.DoWork += AsyncDownloader_DoWork; | |||
| asyncUpdater.DoWork += AsyncUpdater_DoWork; | |||
| //完成通知器: | |||
| asyncDownloader.RunWorkerCompleted += AsyncDownloader_RunWorkerCompleted; | |||
| asyncUpdater.RunWorkerCompleted += AsyncUpdater_RunWorkerCompleted; | |||
| if (Downloader.Program.Tencent_cos_download.CheckAlreadyDownload()) | |||
| { | |||
| obj.checkUpdate(); | |||
| if (obj.RecallUser()) | |||
| RememberMe = true; | |||
| else | |||
| RememberMe = false; | |||
| //在启动时立刻检查更新,确保选手启动最新版选手包 | |||
| //若有更新,将启动键改为更新键; | |||
| //相应地,使用login界面启动; | |||
| //结构:上方为登录框架,下方有“修改选手包”按钮 | |||
| //下面几行是用来运行测试的代码 | |||
| //Program.RunProgram.StartServerForDebug("0.0.0.0",8888,4,1,600,"video"); | |||
| //Program.RunProgram.RunCpp("127.0.0.1",8888,4,1,false,true,false); | |||
| //Program.RunProgram.RunGUIClient("127.0.0.1", 8888, 0, true); | |||
| e.Result = 1; | |||
| } | |||
| else | |||
| { | |||
| e.Result = 2; | |||
| } | |||
| } | |||
| } | |||
| @@ -153,6 +196,7 @@ namespace starter.viewmodel.settings | |||
| this.RaisePropertyChanged("UpdateInfo"); | |||
| this.RaisePropertyChanged("LaunchBtnCont"); | |||
| } | |||
| this.RaisePropertyChanged("UpdatePlanned"); | |||
| } | |||
| } | |||
| @@ -257,6 +301,7 @@ namespace starter.viewmodel.settings | |||
| this.RaisePropertyChanged("LaunchVis"); | |||
| this.RaisePropertyChanged("NewUserVis"); | |||
| this.RaisePropertyChanged("ConfirmBtnCont"); | |||
| this.RaisePropertyChanged("ProcessingIntro"); | |||
| } | |||
| } | |||
| public string Intro | |||
| @@ -298,6 +343,21 @@ namespace starter.viewmodel.settings | |||
| } | |||
| } | |||
| } | |||
| public string ProcessingIntro | |||
| { | |||
| get | |||
| { | |||
| switch (Status) | |||
| { | |||
| case SettingsModel.Status.working: | |||
| return "正在下载"; | |||
| case SettingsModel.Status.initializing: | |||
| return "正在检查更新"; | |||
| default: | |||
| return ""; | |||
| } | |||
| } | |||
| } | |||
| public string AbortOrSelLanguage | |||
| { | |||
| get | |||
| @@ -453,7 +513,7 @@ namespace starter.viewmodel.settings | |||
| { | |||
| get | |||
| { | |||
| return Status == SettingsModel.Status.working ? Visibility.Visible : Visibility.Collapsed; | |||
| return (Status == SettingsModel.Status.working || Status == SettingsModel.Status.initializing) ? Visibility.Visible : Visibility.Collapsed; | |||
| } | |||
| } | |||
| public Visibility CompleteVis | |||
| @@ -508,6 +568,13 @@ namespace starter.viewmodel.settings | |||
| return obj.status == SettingsModel.Status.login && (!obj.UpdatePlanned) ? Visibility.Visible : Visibility.Collapsed; | |||
| } | |||
| } | |||
| public Visibility CfgVis | |||
| { | |||
| get | |||
| { | |||
| return obj.status == SettingsModel.Status.launch ? Visibility.Visible : Visibility.Collapsed; | |||
| } | |||
| } | |||
| public string UpdateBtnCont | |||
| { | |||
| @@ -534,9 +601,9 @@ namespace starter.viewmodel.settings | |||
| if (obj.UpdatePlanned) | |||
| ans = "更新"; | |||
| else if (obj.launchLanguage == SettingsModel.LaunchLanguage.cpp) | |||
| ans = "启动c++包"; | |||
| ans = "启动选手包"; | |||
| else | |||
| ans = "启动python包"; | |||
| ans = "启动选手包"; | |||
| return ans; | |||
| } | |||
| } | |||
| @@ -570,6 +637,11 @@ namespace starter.viewmodel.settings | |||
| } | |||
| } | |||
| public bool UpdatePlanned | |||
| { | |||
| get { return obj.UpdatePlanned; } | |||
| } | |||
| public string RouteSelectWindow(string type) | |||
| { | |||
| if (type == "Folder") | |||
| @@ -719,6 +791,7 @@ namespace starter.viewmodel.settings | |||
| this.RaisePropertyChanged("UpdateBtnCont"); | |||
| this.RaisePropertyChanged("UpdateInfo"); | |||
| this.RaisePropertyChanged("LaunchVis"); | |||
| this.RaisePropertyChanged("UpdatePlanned"); | |||
| } | |||
| })); | |||
| } | |||
| @@ -1,6 +1,5 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -27,7 +26,6 @@ namespace GameClass.GameObj | |||
| public bool HasSpear => hasSpear; | |||
| /// <summary> | |||
| /// 与THUAI4不同的一个攻击判定方案,通过这个函数判断爆炸时能否伤害到target | |||
| /// </summary> | |||
| /// <param name="target">被尝试攻击者</param> | |||
| /// <returns>是否可以攻击到</returns> | |||
| @@ -36,7 +34,7 @@ namespace GameClass.GameObj | |||
| public override bool IgnoreCollideExecutor(IGameObj targetObj) | |||
| { | |||
| if (targetObj == Parent && CanMove) return true; | |||
| if (targetObj == Parent) return true; | |||
| if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet) | |||
| return true; | |||
| return false; | |||
| @@ -1,10 +1,5 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Numerics; | |||
| using System.Runtime.InteropServices; | |||
| using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -324,7 +324,17 @@ namespace GameClass.GameObj | |||
| return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped | |||
| && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued | |||
| && playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack | |||
| && playerState != PlayerStateType.ClimbingThroughWindows && playerState != PlayerStateType.Stunned); | |||
| && playerState != PlayerStateType.ClimbingThroughWindows | |||
| && playerState != PlayerStateType.Stunned && playerState != PlayerStateType.Charmed); | |||
| } | |||
| } | |||
| public bool CanPinDown() | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped | |||
| && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued | |||
| && playerState != PlayerStateType.Stunned && playerState != PlayerStateType.Charmed); | |||
| } | |||
| } | |||
| public bool InteractingWithMapWithoutMoving() | |||
| @@ -345,8 +355,9 @@ namespace GameClass.GameObj | |||
| { | |||
| lock (actionLock) | |||
| return !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped | |||
| || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued | |||
| || playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned | |||
| || playerState == PlayerStateType.Addicted | |||
| || playerState == PlayerStateType.Rescued || playerState == PlayerStateType.Treated | |||
| || playerState == PlayerStateType.Stunned || playerState == PlayerStateType.Charmed | |||
| || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | |||
| } | |||
| private GameObj? whatInteractingWith = null; | |||
| @@ -354,28 +365,24 @@ namespace GameClass.GameObj | |||
| public long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| whatInteractingWith = gameObj; | |||
| if (value != PlayerStateType.Moving) | |||
| IsMoving = false; | |||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | |||
| return ++stateNum; | |||
| } | |||
| //只能被SetPlayerState引用 | |||
| whatInteractingWith = gameObj; | |||
| if (value != PlayerStateType.Moving) | |||
| IsMoving = false; | |||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | |||
| return ++stateNum; | |||
| } | |||
| public long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| whatInteractingWith = gameObj; | |||
| if (value != PlayerStateType.Moving) | |||
| IsMoving = false; | |||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | |||
| return stateNum; | |||
| } | |||
| //只能被SetPlayerState引用 | |||
| whatInteractingWith = gameObj; | |||
| if (value != PlayerStateType.Moving) | |||
| IsMoving = false; | |||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | |||
| return stateNum; | |||
| } | |||
| public long SetPlayerStateNaturally() | |||
| @@ -1,5 +1,7 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System.Numerics; | |||
| using System; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -25,15 +27,66 @@ namespace GameClass.GameObj | |||
| return false; | |||
| } | |||
| private XY stage = new(0, 0); | |||
| public XY Stage | |||
| { | |||
| get | |||
| { | |||
| GameObjReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return stage; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitReadLock(); } | |||
| } | |||
| } | |||
| private Character? whoIsClimbing = null; | |||
| public Character? WhoIsClimbing | |||
| { | |||
| get => whoIsClimbing; | |||
| set | |||
| get | |||
| { | |||
| GameObjReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return whoIsClimbing; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitReadLock(); } | |||
| } | |||
| } | |||
| public bool TryToClimb(Character character) | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| if (whoIsClimbing == null) | |||
| { | |||
| stage = new(0, 0); | |||
| whoIsClimbing = character; | |||
| return true; | |||
| } | |||
| else return false; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| public void FinishClimbing() | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| whoIsClimbing = null; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| public void Enter2Stage(XY xy) | |||
| { | |||
| GameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (gameObjLock) | |||
| whoIsClimbing = value; | |||
| stage = xy; | |||
| } | |||
| finally { GameObjReaderWriterLock.ExitWriteLock(); } | |||
| } | |||
| } | |||
| } | |||
| @@ -8,8 +8,23 @@ namespace GameClass.GameObj | |||
| { | |||
| protected readonly object actionLock = new(); | |||
| public object ActionLock => actionLock; | |||
| //player.actionLock>其他.actionLock | |||
| private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; | |||
| private Semaphore threadNum = new(1, 1); | |||
| public Semaphore ThreadNum | |||
| { | |||
| get | |||
| { | |||
| return threadNum; | |||
| } | |||
| set | |||
| { | |||
| threadNum = value; | |||
| } | |||
| } | |||
| protected long stateNum = 0; | |||
| public long StateNum | |||
| { | |||
| @@ -61,14 +76,27 @@ namespace GameClass.GameObj | |||
| } | |||
| // 移动,改变坐标 | |||
| public long MovingSetPos(XY moveVec) | |||
| public long MovingSetPos(XY moveVec, long stateNo) | |||
| { | |||
| if (moveVec.x != 0 || moveVec.y != 0) | |||
| lock (actionLock) | |||
| { | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| facingDirection = moveVec; | |||
| this.position += moveVec; | |||
| lock (actionLock) | |||
| { | |||
| if (!canMove || isRemoved) return -1; | |||
| if (stateNo != stateNum) return -1; | |||
| facingDirection = moveVec; | |||
| this.position += moveVec; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| moveReaderWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| return moveVec * moveVec; | |||
| } | |||
| @@ -20,18 +20,6 @@ namespace GameEngine | |||
| private readonly ITimer gameTimer; | |||
| private readonly Action<IMoveable> EndMove; | |||
| public readonly uint[,] ProtoGameMap; | |||
| public PlaceType GetPlaceType(XY Position) | |||
| { | |||
| try | |||
| { | |||
| return (PlaceType)ProtoGameMap[Position.x / GameData.numOfPosGridPerCell, Position.y / GameData.numOfPosGridPerCell]; | |||
| } | |||
| catch | |||
| { | |||
| return PlaceType.Null; | |||
| } | |||
| } | |||
| public IGameObj? CheckCollision(IMoveable obj, XY Pos) | |||
| { | |||
| @@ -52,7 +40,6 @@ namespace GameEngine | |||
| Action<IMoveable> EndMove | |||
| ) | |||
| { | |||
| this.ProtoGameMap = gameMap.ProtoGameMap; | |||
| this.gameTimer = gameMap.Timer; | |||
| this.EndMove = EndMove; | |||
| this.OnCollision = OnCollision; | |||
| @@ -64,21 +51,57 @@ namespace GameEngine | |||
| /// </summary> | |||
| /// <param name="obj">移动物体,默认obj.Rigid为true</param> | |||
| /// <param name="moveVec">移动的位移向量</param> | |||
| private void MoveMax(IMoveable obj, XY moveVec) | |||
| private bool MoveMax(IMoveable obj, XY moveVec, long stateNum) | |||
| { | |||
| /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ | |||
| XY nextPos = obj.Position + moveVec; | |||
| double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); | |||
| maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); | |||
| obj.MovingSetPos(new XY(moveVec, maxLen)); | |||
| return (obj.MovingSetPos(new XY(moveVec, maxLen), stateNum)) >= 0; | |||
| } | |||
| private bool LoopDo(IMoveable obj, double direction, ref double deltaLen, long stateNum) | |||
| { | |||
| double moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; | |||
| XY res = new(direction, moveVecLength); | |||
| // 越界情况处理:如果越界,则与越界方块碰撞 | |||
| bool flag; // 循环标志 | |||
| do | |||
| { | |||
| flag = false; | |||
| IGameObj? collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res); | |||
| if (collisionObj == null) | |||
| break; | |||
| switch (OnCollision(obj, collisionObj, res)) | |||
| { | |||
| case AfterCollision.ContinueCheck: | |||
| flag = true; | |||
| break; | |||
| case AfterCollision.Destroyed: | |||
| Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | |||
| return false; | |||
| case AfterCollision.MoveMax: | |||
| if (!MoveMax(obj, res, stateNum)) return false; | |||
| moveVecLength = 0; | |||
| res = new XY(direction, moveVecLength); | |||
| break; | |||
| } | |||
| } while (flag); | |||
| long moveL = obj.MovingSetPos(res, stateNum); | |||
| if (moveL == -1) return false; | |||
| deltaLen = deltaLen + moveVecLength - Math.Sqrt(moveL); | |||
| return true; | |||
| } | |||
| public void MoveObj(IMoveable obj, int moveTime, double direction, long threadNum) | |||
| public void MoveObj(IMoveable obj, int moveTime, double direction, long stateNum) | |||
| { | |||
| if (!gameTimer.IsGaming) return; | |||
| lock (obj.ActionLock) | |||
| { | |||
| if (!obj.IsAvailableForMove) return; | |||
| if (!obj.IsAvailableForMove) { EndMove(obj); return; } | |||
| obj.IsMoving = true; | |||
| } | |||
| new Thread | |||
| @@ -87,9 +110,9 @@ namespace GameEngine | |||
| { | |||
| double moveVecLength = 0.0; | |||
| XY res = new(direction, moveVecLength); | |||
| double deltaLen = 0; // 转向,并用deltaLen存储行走的误差 | |||
| double deltaLen = (double)0.0; // 转向,并用deltaLen存储行走的误差 | |||
| IGameObj? collisionObj = null; | |||
| bool isDestroyed = false; | |||
| bool isEnded = false; | |||
| bool flag; // 循环标志 | |||
| do | |||
| @@ -106,34 +129,82 @@ namespace GameEngine | |||
| break; | |||
| case AfterCollision.Destroyed: | |||
| Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | |||
| isDestroyed = true; | |||
| isEnded = true; | |||
| break; | |||
| case AfterCollision.MoveMax: | |||
| break; | |||
| } | |||
| } while (flag); | |||
| if (!isDestroyed) | |||
| if (isEnded) | |||
| { | |||
| new FrameRateTaskExecutor<int>( | |||
| () => gameTimer.IsGaming && obj.CanMove && !obj.IsRemoved && obj.IsMoving, | |||
| () => | |||
| obj.IsMoving = false; | |||
| EndMove(obj); | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| if (moveTime >= GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond) | |||
| { | |||
| Thread.Sleep(GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond); | |||
| new FrameRateTaskExecutor<int>( | |||
| () => gameTimer.IsGaming, | |||
| () => | |||
| { | |||
| if (obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) | |||
| return !(isEnded = true); | |||
| return !(isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum)); | |||
| }, | |||
| GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond, | |||
| () => | |||
| { | |||
| return 0; | |||
| }, | |||
| maxTotalDuration: moveTime - GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond | |||
| ) | |||
| { | |||
| moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; | |||
| res = new XY(direction, moveVecLength); | |||
| //对人特殊处理 | |||
| if (threadNum > 0 && obj.StateNum != threadNum) return false; | |||
| // 越界情况处理:如果越界,则与越界方块碰撞 | |||
| bool flag; // 循环标志 | |||
| do | |||
| AllowTimeExceed = true, | |||
| MaxTolerantTimeExceedCount = ulong.MaxValue, | |||
| TimeExceedAction = b => | |||
| { | |||
| flag = false; | |||
| collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res); | |||
| if (collisionObj == null) | |||
| break; | |||
| if (b) | |||
| Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!"); | |||
| #if DEBUG | |||
| else | |||
| { | |||
| Console.WriteLine("Debug info: Object moving time exceed for once."); | |||
| } | |||
| #endif | |||
| } | |||
| }.Start(); | |||
| if (!isEnded && obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) | |||
| isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum); | |||
| } | |||
| if (isEnded) | |||
| { | |||
| obj.IsMoving = false; | |||
| EndMove(obj); | |||
| return; | |||
| } | |||
| if (obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) | |||
| { | |||
| int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond); | |||
| if (leftTime > 0) | |||
| { | |||
| Thread.Sleep(leftTime); // 多移动的在这里补回来 | |||
| } | |||
| do | |||
| { | |||
| flag = false; | |||
| moveVecLength = (double)deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; | |||
| res = new XY(direction, moveVecLength); | |||
| if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) | |||
| { | |||
| obj.MovingSetPos(res, stateNum); | |||
| } | |||
| else | |||
| { | |||
| switch (OnCollision(obj, collisionObj, res)) | |||
| { | |||
| case AfterCollision.ContinueCheck: | |||
| @@ -141,87 +212,19 @@ namespace GameEngine | |||
| break; | |||
| case AfterCollision.Destroyed: | |||
| Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | |||
| isDestroyed = true; | |||
| return false; | |||
| isEnded = true; | |||
| break; | |||
| case AfterCollision.MoveMax: | |||
| if (threadNum == 0 || obj.StateNum == threadNum) | |||
| MoveMax(obj, res); | |||
| MoveMax(obj, res, stateNum); | |||
| moveVecLength = 0; | |||
| res = new XY(direction, moveVecLength); | |||
| break; | |||
| } | |||
| } while (flag); | |||
| if (threadNum == 0 || obj.StateNum == threadNum) | |||
| deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res)); | |||
| return true; | |||
| }, | |||
| GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond, | |||
| () => | |||
| { | |||
| int leftTime = moveTime % (GameData.numOfPosGridPerCell / GameData.numOfStepPerSecond); | |||
| bool flag; | |||
| do | |||
| { | |||
| flag = false; | |||
| if (!isDestroyed) | |||
| { | |||
| moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; | |||
| res = new XY(direction, moveVecLength); | |||
| if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) | |||
| { | |||
| if (threadNum == 0 || obj.StateNum == threadNum) | |||
| obj.MovingSetPos(res); | |||
| } | |||
| else | |||
| { | |||
| switch (OnCollision(obj, collisionObj, res)) | |||
| { | |||
| case AfterCollision.ContinueCheck: | |||
| flag = true; | |||
| break; | |||
| case AfterCollision.Destroyed: | |||
| Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); | |||
| isDestroyed = true; | |||
| break; | |||
| case AfterCollision.MoveMax: | |||
| if (threadNum == 0 || obj.StateNum == threadNum) | |||
| MoveMax(obj, res); | |||
| moveVecLength = 0; | |||
| res = new XY(direction, moveVecLength); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } while (flag); | |||
| if (leftTime > 0 && obj.IsMoving) | |||
| { | |||
| Thread.Sleep(leftTime); // 多移动的在这里补回来 | |||
| } | |||
| lock (obj.ActionLock) | |||
| obj.IsMoving = false; // 结束移动 | |||
| EndMove(obj); | |||
| return 0; | |||
| }, | |||
| maxTotalDuration: moveTime | |||
| ) | |||
| { | |||
| AllowTimeExceed = true, | |||
| MaxTolerantTimeExceedCount = ulong.MaxValue, | |||
| TimeExceedAction = b => | |||
| { | |||
| if (b) | |||
| Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!"); | |||
| #if DEBUG | |||
| else | |||
| { | |||
| Console.WriteLine("Debug info: Object moving time exceed for once."); | |||
| } | |||
| #endif | |||
| } | |||
| }.Start(); | |||
| } while (flag); | |||
| } | |||
| obj.IsMoving = false; // 结束移动 | |||
| EndMove(obj); | |||
| } | |||
| } | |||
| ).Start(); | |||
| @@ -36,26 +36,59 @@ namespace Gaming | |||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (moveTimeInMilliseconds < 5) return false; | |||
| if (!playerToMove.Commandable()) return false; | |||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, | |||
| characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving)); | |||
| long stateNum = characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| playerToMove.ThreadNum.WaitOne(); | |||
| if (stateNum != playerToMove.StateNum) | |||
| playerToMove.ThreadNum.Release(); | |||
| else | |||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, stateNum); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| return true; | |||
| } | |||
| public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (!playerToMove.Commandable() && playerToMove.PlayerState != PlayerStateType.Stunned) return false; | |||
| characterManager.BeStunned(playerToMove, moveTimeInMilliseconds); | |||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, playerToMove.StateNum); | |||
| if (playerToMove.CharacterType == CharacterType.Robot) return false; | |||
| long stateNum = characterManager.SetPlayerState(playerToMove, PlayerStateType.Charmed); | |||
| if (stateNum == -1) return false; | |||
| new Thread | |||
| (() => | |||
| { | |||
| playerToMove.ThreadNum.WaitOne(); | |||
| if (stateNum != playerToMove.StateNum) | |||
| playerToMove.ThreadNum.Release(); | |||
| else | |||
| { | |||
| moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, playerToMove.StateNum); | |||
| Thread.Sleep(moveTimeInMilliseconds); | |||
| lock (playerToMove.ActionLock) | |||
| { | |||
| if (stateNum == playerToMove.StateNum) | |||
| playerToMove.SetPlayerStateNaturally(); | |||
| } | |||
| } | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| return true; | |||
| } | |||
| public bool Stop(Character player) | |||
| { | |||
| if (player.Commandable()) | |||
| lock (player.ActionLock) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| return true; | |||
| if (player.Commandable()) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| @@ -279,12 +312,11 @@ namespace Gaming | |||
| } | |||
| public bool ClimbingThroughWindow(Character player) | |||
| { | |||
| if (!player.Commandable()) | |||
| return false; | |||
| Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window); | |||
| if (windowForClimb == null) return false; | |||
| if (windowForClimb == null || windowForClimb.WhoIsClimbing != null) | |||
| return false; | |||
| long stateNum = characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows, windowForClimb); | |||
| if (stateNum == -1) return false; | |||
| XY windowToPlayer = new( | |||
| (Math.Abs(player.Position.x - windowForClimb.Position.x) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.x > windowForClimb.Position.x ? 1 : -1)) : 0, | |||
| @@ -296,59 +328,57 @@ namespace Gaming | |||
| if (player.IsGhost() && !characterInWindow.IsGhost()) | |||
| characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position)); | |||
| return false; | |||
| }*/ | |||
| } | |||
| //Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer); | |||
| // gameMap.Add(addWall); | |||
| Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer); | |||
| gameMap.Add(addWall);*/ | |||
| characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows); | |||
| long threadNum = player.StateNum; | |||
| windowForClimb.WhoIsClimbing = player; | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, | |||
| loopToDo: () => { }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0, | |||
| maxTotalDuration: (int)((windowToPlayer + windowForClimb.Position - player.Position).Length() * 1000 / player.MoveSpeed) | |||
| ) | |||
| .Start(); | |||
| if (player.PlayerState != PlayerStateType.ClimbingThroughWindows) | |||
| { | |||
| windowForClimb.WhoIsClimbing = null; | |||
| return; | |||
| } | |||
| player.ReSetPos(windowToPlayer + windowForClimb.Position); | |||
| player.MoveSpeed = player.SpeedOfClimbingThroughWindows; | |||
| moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle(), threadNum); | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, | |||
| loopToDo: () => | |||
| ( | |||
| () => | |||
| { | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0, | |||
| maxTotalDuration: (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed) | |||
| ) | |||
| .Start(); | |||
| XY PosJumpOff = windowForClimb.Position - 2 * windowToPlayer; | |||
| player.ReSetPos(PosJumpOff); | |||
| player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); | |||
| windowForClimb.WhoIsClimbing = null; | |||
| // gameMap.Remove(addWall); | |||
| if (threadNum == player.StateNum) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| } | |||
| } | |||
| ) | |||
| player.ThreadNum.WaitOne(); | |||
| if (stateNum != player.StateNum) | |||
| { | |||
| player.ThreadNum.Release(); | |||
| } | |||
| else | |||
| { | |||
| if (!windowForClimb.TryToClimb(player)) | |||
| { | |||
| player.ThreadNum.Release(); | |||
| player.SetPlayerStateNaturally(); | |||
| } | |||
| else | |||
| { | |||
| Thread.Sleep((int)((windowToPlayer + windowForClimb.Position - player.Position).Length() * 1000 / player.MoveSpeed)); | |||
| lock (player.ActionLock) | |||
| { | |||
| if (player.StateNum != stateNum) return; | |||
| player.ReSetPos(windowToPlayer + windowForClimb.Position); | |||
| windowForClimb.Enter2Stage(windowForClimb.Position - 2 * windowToPlayer); | |||
| } | |||
| player.MoveSpeed = player.SpeedOfClimbingThroughWindows; | |||
| moveEngine.MoveObj(player, GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2, (-1 * windowToPlayer).Angle(), stateNum); | |||
| Thread.Sleep(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2); | |||
| player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); | |||
| lock (player.ActionLock) | |||
| { | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| characterManager.SetPlayerState(player); | |||
| windowForClimb.FinishClimbing(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| return true; | |||
| @@ -400,7 +430,6 @@ namespace Gaming | |||
| timeInterval: GameData.frameDuration, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | |||
| { | |||
| @@ -462,6 +491,7 @@ namespace Gaming | |||
| }, | |||
| EndMove: obj => | |||
| { | |||
| obj.ThreadNum.Release(); | |||
| // Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64); | |||
| } | |||
| ); | |||
| @@ -195,7 +195,9 @@ namespace Gaming | |||
| { | |||
| Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); | |||
| gameMap.Add(bullet); | |||
| moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms | |||
| if (bullet.CastTime > 0) | |||
| { | |||
| characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); | |||
| @@ -25,28 +25,52 @@ namespace Gaming | |||
| if (nowPlayerState == value) return -1; | |||
| switch (nowPlayerState) | |||
| { | |||
| case PlayerStateType.OpeningTheChest: | |||
| if (player.NoHp()) return -1; | |||
| ((Chest)player.WhatInteractingWith!).StopOpen(); | |||
| return player.ChangePlayerState(value, gameObj); | |||
| case PlayerStateType.OpeningTheDoorway: | |||
| if (player.NoHp()) return -1; | |||
| Doorway doorway = (Doorway)player.WhatInteractingWith!; | |||
| doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime; | |||
| doorway.OpenStartTime = 0; | |||
| return player.ChangePlayerState(value, gameObj); | |||
| case PlayerStateType.Escaped: | |||
| case PlayerStateType.Deceased: | |||
| return -1; | |||
| case PlayerStateType.Addicted: | |||
| if (value == PlayerStateType.Rescued) | |||
| return player.ChangePlayerStateInOneThread(value, gameObj); | |||
| else | |||
| else if (value == PlayerStateType.Null) | |||
| return player.ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Rescued: | |||
| if (value == PlayerStateType.Addicted) | |||
| return player.ChangePlayerStateInOneThread(value, gameObj); | |||
| else | |||
| else if (value == PlayerStateType.Null) | |||
| return player.ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.TryingToAttack: | |||
| case PlayerStateType.Stunned: | |||
| case PlayerStateType.Charmed: | |||
| case PlayerStateType.Swinging: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows) | |||
| return player.ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.ClimbingThroughWindows: | |||
| if (value != PlayerStateType.Moving) | |||
| { | |||
| Window window = (Window)player.WhatInteractingWith!; | |||
| window.FinishClimbing(); | |||
| if (window.Stage.x == 0) | |||
| player.ThreadNum.Release(); | |||
| else player.ReSetPos(window.Stage); | |||
| return player.ChangePlayerState(value, gameObj); | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.OpeningTheChest: | |||
| ((Chest)player.WhatInteractingWith!).StopOpen(); | |||
| return player.ChangePlayerState(value, gameObj); | |||
| case PlayerStateType.OpeningTheDoorway: | |||
| Doorway doorway = (Doorway)player.WhatInteractingWith!; | |||
| doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime; | |||
| doorway.OpenStartTime = 0; | |||
| return player.ChangePlayerState(value, gameObj); | |||
| default: | |||
| if (player.NoHp()) return -1; | |||
| return player.ChangePlayerState(value, gameObj); | |||
| } | |||
| } | |||
| @@ -163,7 +187,7 @@ namespace Gaming | |||
| newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position)); | |||
| else newPlayer.AddBgm(BgmType.GhostIsComing, 0); | |||
| } | |||
| if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && !newPlayer.NoHp() && newPlayer.PlayerState != PlayerStateType.Stunned && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange) | |||
| if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && newPlayer.CanPinDown() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange) | |||
| { | |||
| TimePinningDown += GameData.checkInterval; | |||
| newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded); | |||
| @@ -54,7 +54,7 @@ namespace Gaming | |||
| else player.AddAp(GameData.PropDuration); | |||
| break; | |||
| case PropType.RecoveryFromDizziness: | |||
| if (player.PlayerState == PlayerStateType.Stunned) | |||
| if (player.PlayerState == PlayerStateType.Stunned || player.PlayerState == PlayerStateType.Charmed) | |||
| { | |||
| player.AddScore(GameData.ScorePropRecoverFromDizziness); | |||
| player.SetPlayerStateNaturally(); | |||
| @@ -1,4 +1,5 @@ | |||
| using System; | |||
| using System.Threading; | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| @@ -11,7 +12,8 @@ namespace Preparation.Interface | |||
| public bool IsRemoved { get; } | |||
| public bool IsAvailableForMove { get; } | |||
| public long StateNum { get; } | |||
| public long MovingSetPos(XY moveVec); | |||
| public Semaphore ThreadNum { get; set; } | |||
| public long MovingSetPos(XY moveVec, long stateNum); | |||
| public void ReSetCanMove(bool value); | |||
| public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 | |||
| { | |||
| @@ -24,6 +24,7 @@ namespace Preparation.Utility | |||
| ClimbingThroughWindows = 15, | |||
| UsingSkill = 16, | |||
| OpeningTheDoorway = 17, | |||
| Charmed = 18, | |||
| } | |||
| public enum GameObjType | |||
| { | |||
| @@ -6,13 +6,13 @@ namespace Preparation.Utility | |||
| public static class GameData | |||
| { | |||
| #region 基本常数 | |||
| public const int numOfStepPerSecond = 20; // 每秒行走的步数 | |||
| public const int numOfStepPerSecond = 100; // 每秒行走的步数 | |||
| public const int tolerancesLength = 3; | |||
| public const int adjustLength = 3; | |||
| public const int frameDuration = 50; // 每帧时长 | |||
| public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | |||
| public const int checkInterval = 10; | |||
| public const long gameDuration = 600000; // 游戏时长600000ms = 10min | |||
| public const int LimitOfStopAndMove = 15; | |||
| @@ -116,6 +116,7 @@ namespace Preparation.Utility | |||
| case Preparation.Utility.PlayerStateType.Rescuing: | |||
| return PlayerState.Rescuing; | |||
| case Preparation.Utility.PlayerStateType.Stunned: | |||
| case Preparation.Utility.PlayerStateType.Charmed: | |||
| return PlayerState.Stunned; | |||
| case Preparation.Utility.PlayerStateType.Swinging: | |||
| return PlayerState.Swinging; | |||