Browse Source

Merge pull request #535 from eesast/dev

fix: 🔒 refactor the moveEngine
tags/v0.1.0
Changli Tang GitHub 2 years ago
parent
commit
bd9ccc71ad
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 738 additions and 380 deletions
  1. +6
    -6
      CAPI/cpp/API/include/API.h
  2. +1
    -1
      CAPI/cpp/API/include/Communication.h
  3. +1
    -1
      CAPI/cpp/API/include/logic.h
  4. +4
    -2
      CAPI/cpp/API/include/structures.h
  5. +5
    -2
      CAPI/cpp/API/include/utils.hpp
  6. +4
    -4
      CAPI/cpp/API/src/API.cpp
  7. +2
    -2
      CAPI/cpp/API/src/Communication.cpp
  8. +6
    -6
      CAPI/cpp/API/src/DebugAPI.cpp
  9. +2
    -2
      CAPI/cpp/API/src/logic.cpp
  10. +38
    -10
      CAPI/cpp/proto/Message2Server.pb.cc
  11. +36
    -0
      CAPI/cpp/proto/Message2Server.pb.h
  12. +40
    -39
      CAPI/cpp/proto/MessageType.pb.cc
  13. +4
    -3
      CAPI/cpp/proto/MessageType.pb.h
  14. +4
    -4
      CAPI/python/PyAPI/API.py
  15. +2
    -2
      CAPI/python/PyAPI/Communication.py
  16. +6
    -6
      CAPI/python/PyAPI/DebugAPI.py
  17. +2
    -2
      CAPI/python/PyAPI/Interface.py
  18. +2
    -2
      CAPI/python/PyAPI/logic.py
  19. +2
    -1
      CAPI/python/PyAPI/structures.py
  20. +9
    -3
      CAPI/python/PyAPI/utils.py
  21. +2
    -2
      CAPI/python/requirements.txt
  22. +1
    -0
      dependency/proto/Message2Server.proto
  23. +4
    -3
      dependency/proto/MessageType.proto
  24. +2
    -1
      docs/GameRules.md
  25. +13
    -2
      docs/游戏机制与平衡性调整更新草案.md
  26. +5
    -2
      docs/版本更新说明.md
  27. +51
    -5
      installer/Installer/MainWindow.xaml
  28. +2
    -2
      installer/Installer/Model.cs
  29. +108
    -35
      installer/Installer/ViewModel.cs
  30. +1
    -3
      logic/GameClass/GameObj/Bullet/Bullet.cs
  31. +0
    -5
      logic/GameClass/GameObj/Character/Character.Ghost.cs
  32. +28
    -21
      logic/GameClass/GameObj/Character/Character.cs
  33. +57
    -4
      logic/GameClass/GameObj/Map/Window.cs
  34. +32
    -4
      logic/GameClass/GameObj/Moveable.cs
  35. +115
    -112
      logic/GameEngine/MoveEngine.cs
  36. +93
    -63
      logic/Gaming/ActionManager.cs
  37. +2
    -0
      logic/Gaming/AttackManager.cs
  38. +38
    -14
      logic/Gaming/CharacterManager.cs
  39. +1
    -1
      logic/Gaming/PropManager.cs
  40. +3
    -1
      logic/Preparation/Interface/IMoveable.cs
  41. +1
    -0
      logic/Preparation/Utility/EnumType.cs
  42. +2
    -2
      logic/Preparation/Utility/GameData.cs
  43. +1
    -0
      logic/Preparation/Utility/Transformation.cs

+ 6
- 6
CAPI/cpp/API/include/API.h View File

@@ -60,7 +60,7 @@ public:
virtual bool PickProp(THUAI6::PropType prop) = 0; virtual bool PickProp(THUAI6::PropType prop) = 0;
virtual bool UseProp(THUAI6::PropType prop) = 0; virtual bool UseProp(THUAI6::PropType prop) = 0;
virtual bool ThrowProp(THUAI6::PropType prop) = 0; virtual bool ThrowProp(THUAI6::PropType prop) = 0;
virtual bool UseSkill(int32_t skillID) = 0;
virtual bool UseSkill(int32_t skillID, int32_t skillParam) = 0;
virtual bool SendMessage(int64_t toID, std::string message, bool binary) = 0; virtual bool SendMessage(int64_t toID, std::string message, bool binary) = 0;
virtual bool HaveMessage() = 0; virtual bool HaveMessage() = 0;
virtual std::pair<int64_t, std::string> GetMessage() = 0; virtual std::pair<int64_t, std::string> GetMessage() = 0;
@@ -108,7 +108,7 @@ public:
virtual std::future<bool> PickProp(THUAI6::PropType prop) = 0; virtual std::future<bool> PickProp(THUAI6::PropType prop) = 0;
virtual std::future<bool> UseProp(THUAI6::PropType prop) = 0; virtual std::future<bool> UseProp(THUAI6::PropType prop) = 0;
virtual std::future<bool> ThrowProp(THUAI6::PropType prop) = 0; virtual std::future<bool> ThrowProp(THUAI6::PropType prop) = 0;
virtual std::future<bool> UseSkill(int32_t skillID) = 0;
virtual std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) = 0;
virtual std::future<bool> Attack(double angleInRadian) = 0; virtual std::future<bool> Attack(double angleInRadian) = 0;


virtual std::future<bool> OpenDoor() = 0; virtual std::future<bool> OpenDoor() = 0;
@@ -236,7 +236,7 @@ public:
std::future<bool> PickProp(THUAI6::PropType prop) override; std::future<bool> PickProp(THUAI6::PropType prop) override;
std::future<bool> UseProp(THUAI6::PropType prop) override; std::future<bool> UseProp(THUAI6::PropType prop) override;
std::future<bool> ThrowProp(THUAI6::PropType prop) override; std::future<bool> ThrowProp(THUAI6::PropType prop) override;
std::future<bool> UseSkill(int32_t skillID) override;
std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) override;


std::future<bool> Attack(double angleInRadian) override; std::future<bool> Attack(double angleInRadian) override;


@@ -329,7 +329,7 @@ public:
std::future<bool> PickProp(THUAI6::PropType prop) override; std::future<bool> PickProp(THUAI6::PropType prop) override;
std::future<bool> UseProp(THUAI6::PropType prop) override; std::future<bool> UseProp(THUAI6::PropType prop) override;
std::future<bool> ThrowProp(THUAI6::PropType prop) override; std::future<bool> ThrowProp(THUAI6::PropType prop) override;
std::future<bool> UseSkill(int32_t skillID) override;
std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) override;


std::future<bool> OpenDoor() override; std::future<bool> OpenDoor() override;
std::future<bool> CloseDoor() override; std::future<bool> CloseDoor() override;
@@ -410,7 +410,7 @@ public:
std::future<bool> PickProp(THUAI6::PropType prop) override; std::future<bool> PickProp(THUAI6::PropType prop) override;
std::future<bool> UseProp(THUAI6::PropType prop) override; std::future<bool> UseProp(THUAI6::PropType prop) override;
std::future<bool> ThrowProp(THUAI6::PropType prop) override; std::future<bool> ThrowProp(THUAI6::PropType prop) override;
std::future<bool> UseSkill(int32_t skillID) override;
std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) override;


std::future<bool> Attack(double angleInRadian) override; std::future<bool> Attack(double angleInRadian) override;


@@ -488,7 +488,7 @@ public:
std::future<bool> PickProp(THUAI6::PropType prop) override; std::future<bool> PickProp(THUAI6::PropType prop) override;
std::future<bool> UseProp(THUAI6::PropType prop) override; std::future<bool> UseProp(THUAI6::PropType prop) override;
std::future<bool> ThrowProp(THUAI6::PropType prop) override; std::future<bool> ThrowProp(THUAI6::PropType prop) override;
std::future<bool> UseSkill(int32_t skillID) override;
std::future<bool> UseSkill(int32_t skillID, int32_t skillParam = 0) override;


std::future<bool> OpenDoor() override; std::future<bool> OpenDoor() override;
std::future<bool> CloseDoor() override; std::future<bool> CloseDoor() override;


+ 1
- 1
CAPI/cpp/API/include/Communication.h View File

@@ -31,7 +31,7 @@ public:
bool PickProp(THUAI6::PropType prop, int64_t playerID); bool PickProp(THUAI6::PropType prop, int64_t playerID);
bool UseProp(THUAI6::PropType prop, int64_t playerID); bool UseProp(THUAI6::PropType prop, int64_t playerID);
bool ThrowProp(THUAI6::PropType prop, int64_t playerID); bool ThrowProp(THUAI6::PropType prop, int64_t playerID);
bool UseSkill(int32_t skillID, int64_t playerID);
bool UseSkill(int32_t skillID, int32_t skillParam, int64_t playerID);
bool SendMessage(int64_t toID, std::string message, bool binary, int64_t playerID); bool SendMessage(int64_t toID, std::string message, bool binary, int64_t playerID);
bool OpenDoor(int64_t playerID); bool OpenDoor(int64_t playerID);
bool CloseDoor(int64_t playerID); bool CloseDoor(int64_t playerID);


+ 1
- 1
CAPI/cpp/API/include/logic.h View File

@@ -116,7 +116,7 @@ private:
bool PickProp(THUAI6::PropType prop) override; bool PickProp(THUAI6::PropType prop) override;
bool UseProp(THUAI6::PropType prop) override; bool UseProp(THUAI6::PropType prop) override;
bool ThrowProp(THUAI6::PropType prop) override; bool ThrowProp(THUAI6::PropType prop) override;
bool UseSkill(int32_t skillID) override;
bool UseSkill(int32_t skillID, int32_t skillParam) override;


bool SendMessage(int64_t toID, std::string message, bool binary) override; bool SendMessage(int64_t toID, std::string message, bool binary) override;
bool HaveMessage() override; bool HaveMessage() override;


+ 4
- 2
CAPI/cpp/API/include/structures.h View File

@@ -62,6 +62,7 @@ namespace THUAI6
AddHpOrAp = 6, AddHpOrAp = 6,
ShieldOrSpear = 7, ShieldOrSpear = 7,
RecoveryFromDizziness = 8, RecoveryFromDizziness = 8,
CraftingBench = 9,
}; };


enum class BulletType : unsigned char enum class BulletType : unsigned char
@@ -71,7 +72,7 @@ namespace THUAI6
CommonAttackOfTricker = 2, CommonAttackOfTricker = 2,
BombBomb = 3, BombBomb = 3,
JumpyDumpty = 4, JumpyDumpty = 4,
AtomBomb = 5,
Strike = 5,
}; };


// 玩家类型 // 玩家类型
@@ -363,6 +364,7 @@ namespace THUAI6
{PropType::AddHpOrAp, "AddHpOrAp"}, {PropType::AddHpOrAp, "AddHpOrAp"},
{PropType::ShieldOrSpear, "ShieldOrSpear"}, {PropType::ShieldOrSpear, "ShieldOrSpear"},
{PropType::RecoveryFromDizziness, "RecoveryFromDizziness"}, {PropType::RecoveryFromDizziness, "RecoveryFromDizziness"},
{PropType::CraftingBench, "CraftingBench"},


}; };


@@ -372,7 +374,7 @@ namespace THUAI6
{BulletType::CommonAttackOfTricker, "CommonAttackOfTricker"}, {BulletType::CommonAttackOfTricker, "CommonAttackOfTricker"},
{BulletType::BombBomb, "BombBomb"}, {BulletType::BombBomb, "BombBomb"},
{BulletType::JumpyDumpty, "JumpyDumpty"}, {BulletType::JumpyDumpty, "JumpyDumpty"},
{BulletType::AtomBomb, "AtomBomb"},
{BulletType::Strike, "Strike"},
}; };


inline std::map<StudentBuffType, std::string> studentBuffDict{ inline std::map<StudentBuffType, std::string> studentBuffDict{


+ 5
- 2
CAPI/cpp/API/include/utils.hpp View File

@@ -108,6 +108,7 @@ namespace Proto2THUAI6
{protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE, THUAI6::PropType::AddLifeOrClairaudience}, {protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE, THUAI6::PropType::AddLifeOrClairaudience},
{protobuf::PropType::SHIELD_OR_SPEAR, THUAI6::PropType::ShieldOrSpear}, {protobuf::PropType::SHIELD_OR_SPEAR, THUAI6::PropType::ShieldOrSpear},
{protobuf::PropType::RECOVERY_FROM_DIZZINESS, THUAI6::PropType::RecoveryFromDizziness}, {protobuf::PropType::RECOVERY_FROM_DIZZINESS, THUAI6::PropType::RecoveryFromDizziness},
{protobuf::PropType::CRAFTING_BENCH, THUAI6::PropType::CraftingBench},
}; };


inline std::map<protobuf::PlayerType, THUAI6::PlayerType> playerTypeDict{ inline std::map<protobuf::PlayerType, THUAI6::PlayerType> playerTypeDict{
@@ -186,7 +187,7 @@ namespace Proto2THUAI6
{protobuf::BulletType::COMMON_ATTACK_OF_TRICKER, THUAI6::BulletType::CommonAttackOfTricker}, {protobuf::BulletType::COMMON_ATTACK_OF_TRICKER, THUAI6::BulletType::CommonAttackOfTricker},
{protobuf::BulletType::BOMB_BOMB, THUAI6::BulletType::BombBomb}, {protobuf::BulletType::BOMB_BOMB, THUAI6::BulletType::BombBomb},
{protobuf::BulletType::JUMPY_DUMPTY, THUAI6::BulletType::JumpyDumpty}, {protobuf::BulletType::JUMPY_DUMPTY, THUAI6::BulletType::JumpyDumpty},
{protobuf::BulletType::ATOM_BOMB, THUAI6::BulletType::AtomBomb},
{protobuf::BulletType::STRIKE, THUAI6::BulletType::Strike},
}; };


inline std::map<protobuf::MessageOfObj::MessageOfObjCase, THUAI6::MessageOfObj> messageOfObjDict{ inline std::map<protobuf::MessageOfObj::MessageOfObjCase, THUAI6::MessageOfObj> messageOfObjDict{
@@ -373,6 +374,7 @@ namespace THUAI62Proto
{THUAI6::PropType::AddLifeOrClairaudience, protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE}, {THUAI6::PropType::AddLifeOrClairaudience, protobuf::PropType::ADD_LIFE_OR_CLAIRAUDIENCE},
{THUAI6::PropType::AddSpeed, protobuf::PropType::ADD_SPEED}, {THUAI6::PropType::AddSpeed, protobuf::PropType::ADD_SPEED},
{THUAI6::PropType::ShieldOrSpear, protobuf::PropType::SHIELD_OR_SPEAR}, {THUAI6::PropType::ShieldOrSpear, protobuf::PropType::SHIELD_OR_SPEAR},
{THUAI6::PropType::CraftingBench, protobuf::PropType::CRAFTING_BENCH},
}; };


inline std::map<THUAI6::PlayerType, protobuf::PlayerType> playerTypeDict{ inline std::map<THUAI6::PlayerType, protobuf::PlayerType> playerTypeDict{
@@ -486,11 +488,12 @@ namespace THUAI62Proto
return attackMsg; return attackMsg;
} }


inline protobuf::SkillMsg THUAI62ProtobufSkill(int32_t skillID, int64_t id)
inline protobuf::SkillMsg THUAI62ProtobufSkill(int32_t skillID, int32_t skillParam, int64_t id)
{ {
protobuf::SkillMsg skillMsg; protobuf::SkillMsg skillMsg;
skillMsg.set_skill_id(skillID); skillMsg.set_skill_id(skillID);
skillMsg.set_player_id(id); skillMsg.set_player_id(id);
skillMsg.set_skill_param(skillParam);
return skillMsg; return skillMsg;
} }
} // namespace THUAI62Proto } // namespace THUAI62Proto


+ 4
- 4
CAPI/cpp/API/src/API.cpp View File

@@ -106,16 +106,16 @@ std::future<bool> TrickerAPI::ThrowProp(THUAI6::PropType prop)
{ return logic.ThrowProp(prop); }); { return logic.ThrowProp(prop); });
} }


std::future<bool> StudentAPI::UseSkill(int32_t skillID)
std::future<bool> StudentAPI::UseSkill(int32_t skillID, int32_t skillParam)
{ {
return std::async(std::launch::async, [=]() return std::async(std::launch::async, [=]()
{ return logic.UseSkill(skillID); });
{ return logic.UseSkill(skillID, skillParam); });
} }


std::future<bool> TrickerAPI::UseSkill(int32_t skillID)
std::future<bool> TrickerAPI::UseSkill(int32_t skillID, int32_t skillParam)
{ {
return std::async(std::launch::async, [=]() return std::async(std::launch::async, [=]()
{ return logic.UseSkill(skillID); });
{ return logic.UseSkill(skillID, skillParam); });
} }


std::future<bool> StudentAPI::OpenDoor() std::future<bool> StudentAPI::OpenDoor()


+ 2
- 2
CAPI/cpp/API/src/Communication.cpp View File

@@ -91,7 +91,7 @@ bool Communication::ThrowProp(THUAI6::PropType prop, int64_t playerID)
return false; return false;
} }


bool Communication::UseSkill(int32_t skillID, int64_t playerID)
bool Communication::UseSkill(int32_t skillID, int32_t skillParam, int64_t playerID)
{ {
{ {
std::lock_guard<std::mutex> lock(mtxLimit); std::lock_guard<std::mutex> lock(mtxLimit);
@@ -101,7 +101,7 @@ bool Communication::UseSkill(int32_t skillID, int64_t playerID)
} }
protobuf::BoolRes useSkillResult; protobuf::BoolRes useSkillResult;
ClientContext context; ClientContext context;
auto request = THUAI62Proto::THUAI62ProtobufSkill(skillID, playerID);
auto request = THUAI62Proto::THUAI62ProtobufSkill(skillID, skillParam, playerID);
auto status = THUAI6Stub->UseSkill(&context, request, &useSkillResult); auto status = THUAI6Stub->UseSkill(&context, request, &useSkillResult);
if (status.ok()) if (status.ok())
return useSkillResult.act_success(); return useSkillResult.act_success();


+ 6
- 6
CAPI/cpp/API/src/DebugAPI.cpp View File

@@ -212,21 +212,21 @@ std::future<bool> TrickerDebugAPI::ThrowProp(THUAI6::PropType prop)
return result; }); return result; });
} }


std::future<bool> StudentDebugAPI::UseSkill(int32_t skillID)
std::future<bool> StudentDebugAPI::UseSkill(int32_t skillID, int32_t skillParam)
{ {
logger->info("UseSkill: skillID={}, called at {}ms", skillID, Time::TimeSinceStart(startPoint));
logger->info("UseSkill: skillID={}, skillParam={}, called at {}ms", skillID, skillParam, Time::TimeSinceStart(startPoint));
return std::async(std::launch::async, [=]() return std::async(std::launch::async, [=]()
{ auto result = logic.UseSkill(skillID);
{ auto result = logic.UseSkill(skillID, skillParam);
if (!result) if (!result)
logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint)); logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint));
return result; }); return result; });
} }


std::future<bool> TrickerDebugAPI::UseSkill(int32_t skillID)
std::future<bool> TrickerDebugAPI::UseSkill(int32_t skillID, int32_t skillParam)
{ {
logger->info("UseSkill: skillID={}, called at {}ms", skillID, Time::TimeSinceStart(startPoint));
logger->info("UseSkill: skillID={}, skillParam={}, called at {}ms", skillID, skillParam, Time::TimeSinceStart(startPoint));
return std::async(std::launch::async, [=]() return std::async(std::launch::async, [=]()
{ auto result = logic.UseSkill(skillID);
{ auto result = logic.UseSkill(skillID, skillParam);
if (!result) if (!result)
logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint)); logger->warn("UseSkill: failed at {}ms", Time::TimeSinceStart(startPoint));
return result; }); return result; });


+ 2
- 2
CAPI/cpp/API/src/logic.cpp View File

@@ -208,10 +208,10 @@ bool Logic::ThrowProp(THUAI6::PropType prop)
return pComm->ThrowProp(prop, playerID); return pComm->ThrowProp(prop, playerID);
} }


bool Logic::UseSkill(int32_t skill)
bool Logic::UseSkill(int32_t skill, int32_t skillParam)
{ {
logger->debug("Called UseSkill"); logger->debug("Called UseSkill");
return pComm->UseSkill(skill, playerID);
return pComm->UseSkill(skill, skillParam, playerID);
} }


bool Logic::SendMessage(int64_t toID, std::string message, bool binary) bool Logic::SendMessage(int64_t toID, std::string message, bool binary)


+ 38
- 10
CAPI/cpp/proto/Message2Server.pb.cc View File

@@ -180,7 +180,7 @@ namespace protobuf
::_pbi::ConstantInitialized ::_pbi::ConstantInitialized
) : ) :
_impl_{ _impl_{
/*decltype(_impl_.player_id_)*/ int64_t{0}, /*decltype(_impl_.skill_id_)*/ 0, /*decltype(_impl_._cached_size_)*/ {}}
/*decltype(_impl_.player_id_)*/ int64_t{0}, /*decltype(_impl_.skill_id_)*/ 0, /*decltype(_impl_.skill_param_)*/ 0, /*decltype(_impl_._cached_size_)*/ {}}
{ {
} }
struct SkillMsgDefaultTypeInternal struct SkillMsgDefaultTypeInternal
@@ -274,6 +274,7 @@ const uint32_t TableStruct_Message2Server_2eproto::offsets[] PROTOBUF_SECTION_VA
~0u, // no _inlined_string_donated_ ~0u, // no _inlined_string_donated_
PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.player_id_), PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.player_id_),
PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.skill_id_), PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.skill_id_),
PROTOBUF_FIELD_OFFSET(::protobuf::SkillMsg, _impl_.skill_param_),
}; };
static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
{0, -1, -1, sizeof(::protobuf::PlayerMsg)}, {0, -1, -1, sizeof(::protobuf::PlayerMsg)},
@@ -313,9 +314,9 @@ const char descriptor_table_protodef_Message2Server_2eproto[] PROTOBUF_SECTION_V
"\t\n\007message\"-\n\tAttackMsg\022\021\n\tplayer_id\030\001 \001" "\t\n\007message\"-\n\tAttackMsg\022\021\n\tplayer_id\030\001 \001"
"(\003\022\r\n\005angle\030\002 \001(\001\"\032\n\005IDMsg\022\021\n\tplayer_id\030" "(\003\022\r\n\005angle\030\002 \001(\001\"\032\n\005IDMsg\022\021\n\tplayer_id\030"
"\001 \001(\003\"<\n\021TreatAndRescueMsg\022\021\n\tplayer_id\030" "\001 \001(\003\"<\n\021TreatAndRescueMsg\022\021\n\tplayer_id\030"
"\001 \001(\003\022\024\n\014to_player_id\030\002 \001(\003\"/\n\010SkillMsg\022"
"\021\n\tplayer_id\030\001 \001(\003\022\020\n\010skill_id\030\002 \001(\005b\006pr"
"oto3";
"\001 \001(\003\022\024\n\014to_player_id\030\002 \001(\003\"D\n\010SkillMsg\022"
"\021\n\tplayer_id\030\001 \001(\003\022\020\n\010skill_id\030\002 \001(\005\022\023\n\013"
"skill_param\030\003 \001(\005b\006proto3";
static const ::_pbi::DescriptorTable* const descriptor_table_Message2Server_2eproto_deps[1] = { static const ::_pbi::DescriptorTable* const descriptor_table_Message2Server_2eproto_deps[1] = {
&::descriptor_table_MessageType_2eproto, &::descriptor_table_MessageType_2eproto,
}; };
@@ -323,7 +324,7 @@ static ::_pbi::once_flag descriptor_table_Message2Server_2eproto_once;
const ::_pbi::DescriptorTable descriptor_table_Message2Server_2eproto = { const ::_pbi::DescriptorTable descriptor_table_Message2Server_2eproto = {
false, false,
false, false,
684,
705,
descriptor_table_protodef_Message2Server_2eproto, descriptor_table_protodef_Message2Server_2eproto,
"Message2Server.proto", "Message2Server.proto",
&descriptor_table_Message2Server_2eproto_once, &descriptor_table_Message2Server_2eproto_once,
@@ -2341,10 +2342,10 @@ namespace protobuf
SkillMsg* const _this = this; SkillMsg* const _this = this;
(void)_this; (void)_this;
new (&_impl_) Impl_{ new (&_impl_) Impl_{
decltype(_impl_.player_id_){}, decltype(_impl_.skill_id_){}, /*decltype(_impl_._cached_size_)*/ {}};
decltype(_impl_.player_id_){}, decltype(_impl_.skill_id_){}, decltype(_impl_.skill_param_){}, /*decltype(_impl_._cached_size_)*/ {}};


_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
::memcpy(&_impl_.player_id_, &from._impl_.player_id_, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_id_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_id_));
::memcpy(&_impl_.player_id_, &from._impl_.player_id_, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_param_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_param_));
// @@protoc_insertion_point(copy_constructor:protobuf.SkillMsg) // @@protoc_insertion_point(copy_constructor:protobuf.SkillMsg)
} }


@@ -2355,7 +2356,7 @@ namespace protobuf
(void)arena; (void)arena;
(void)is_message_owned; (void)is_message_owned;
new (&_impl_) Impl_{ new (&_impl_) Impl_{
decltype(_impl_.player_id_){int64_t{0}}, decltype(_impl_.skill_id_){0}, /*decltype(_impl_._cached_size_)*/ {}};
decltype(_impl_.player_id_){int64_t{0}}, decltype(_impl_.skill_id_){0}, decltype(_impl_.skill_param_){0}, /*decltype(_impl_._cached_size_)*/ {}};
} }


SkillMsg::~SkillMsg() SkillMsg::~SkillMsg()
@@ -2386,7 +2387,7 @@ namespace protobuf
// Prevent compiler warnings about cached_has_bits being unused // Prevent compiler warnings about cached_has_bits being unused
(void)cached_has_bits; (void)cached_has_bits;


::memset(&_impl_.player_id_, 0, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_id_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_id_));
::memset(&_impl_.player_id_, 0, static_cast<size_t>(reinterpret_cast<char*>(&_impl_.skill_param_) - reinterpret_cast<char*>(&_impl_.player_id_)) + sizeof(_impl_.skill_param_));
_internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
} }


@@ -2421,6 +2422,16 @@ namespace protobuf
else else
goto handle_unusual; goto handle_unusual;
continue; continue;
// int32 skill_param = 3;
case 3:
if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24))
{
_impl_.skill_param_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
CHK_(ptr);
}
else
goto handle_unusual;
continue;
default: default:
goto handle_unusual; goto handle_unusual;
} // switch } // switch
@@ -2469,6 +2480,13 @@ namespace protobuf
target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_skill_id(), target); target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_skill_id(), target);
} }


// int32 skill_param = 3;
if (this->_internal_skill_param() != 0)
{
target = stream->EnsureSpace(target);
target = ::_pbi::WireFormatLite::WriteInt32ToArray(3, this->_internal_skill_param(), target);
}

if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields()))
{ {
target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
@@ -2500,6 +2518,12 @@ namespace protobuf
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_skill_id()); total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_skill_id());
} }


// int32 skill_param = 3;
if (this->_internal_skill_param() != 0)
{
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_skill_param());
}

return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
} }


@@ -2528,6 +2552,10 @@ namespace protobuf
{ {
_this->_internal_set_skill_id(from._internal_skill_id()); _this->_internal_set_skill_id(from._internal_skill_id());
} }
if (from._internal_skill_param() != 0)
{
_this->_internal_set_skill_param(from._internal_skill_param());
}
_this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
} }


@@ -2550,7 +2578,7 @@ namespace protobuf
using std::swap; using std::swap;
_internal_metadata_.InternalSwap(&other->_internal_metadata_); _internal_metadata_.InternalSwap(&other->_internal_metadata_);
::PROTOBUF_NAMESPACE_ID::internal::memswap< ::PROTOBUF_NAMESPACE_ID::internal::memswap<
PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.skill_id_) + sizeof(SkillMsg::_impl_.skill_id_) - PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.player_id_)>(
PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.skill_param_) + sizeof(SkillMsg::_impl_.skill_param_) - PROTOBUF_FIELD_OFFSET(SkillMsg, _impl_.player_id_)>(
reinterpret_cast<char*>(&_impl_.player_id_), reinterpret_cast<char*>(&_impl_.player_id_),
reinterpret_cast<char*>(&other->_impl_.player_id_) reinterpret_cast<char*>(&other->_impl_.player_id_)
); );


+ 36
- 0
CAPI/cpp/proto/Message2Server.pb.h View File

@@ -1813,6 +1813,7 @@ namespace protobuf
{ {
kPlayerIdFieldNumber = 1, kPlayerIdFieldNumber = 1,
kSkillIdFieldNumber = 2, kSkillIdFieldNumber = 2,
kSkillParamFieldNumber = 3,
}; };
// int64 player_id = 1; // int64 player_id = 1;
void clear_player_id(); void clear_player_id();
@@ -1833,6 +1834,16 @@ namespace protobuf
int32_t _internal_skill_id() const; int32_t _internal_skill_id() const;
void _internal_set_skill_id(int32_t value); void _internal_set_skill_id(int32_t value);


public:
// int32 skill_param = 3;
void clear_skill_param();
int32_t skill_param() const;
void set_skill_param(int32_t value);

private:
int32_t _internal_skill_param() const;
void _internal_set_skill_param(int32_t value);

public: public:
// @@protoc_insertion_point(class_scope:protobuf.SkillMsg) // @@protoc_insertion_point(class_scope:protobuf.SkillMsg)


@@ -1847,6 +1858,7 @@ namespace protobuf
{ {
int64_t player_id_; int64_t player_id_;
int32_t skill_id_; int32_t skill_id_;
int32_t skill_param_;
mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
}; };
union union
@@ -2597,6 +2609,30 @@ namespace protobuf
// @@protoc_insertion_point(field_set:protobuf.SkillMsg.skill_id) // @@protoc_insertion_point(field_set:protobuf.SkillMsg.skill_id)
} }


// int32 skill_param = 3;
inline void SkillMsg::clear_skill_param()
{
_impl_.skill_param_ = 0;
}
inline int32_t SkillMsg::_internal_skill_param() const
{
return _impl_.skill_param_;
}
inline int32_t SkillMsg::skill_param() const
{
// @@protoc_insertion_point(field_get:protobuf.SkillMsg.skill_param)
return _internal_skill_param();
}
inline void SkillMsg::_internal_set_skill_param(int32_t value)
{
_impl_.skill_param_ = value;
}
inline void SkillMsg::set_skill_param(int32_t value)
{
_internal_set_skill_param(value);
// @@protoc_insertion_point(field_set:protobuf.SkillMsg.skill_param)
}

#ifdef __GNUC__ #ifdef __GNUC__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif // __GNUC__ #endif // __GNUC__


+ 40
- 39
CAPI/cpp/proto/MessageType.pb.cc View File

@@ -30,49 +30,49 @@ static constexpr ::_pbi::MigrationSchema* schemas = nullptr;
static constexpr ::_pb::Message* const* file_default_instances = nullptr; static constexpr ::_pb::Message* const* file_default_instances = nullptr;


const char descriptor_table_protodef_MessageType_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = const char descriptor_table_protodef_MessageType_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
"\n\021MessageType.proto\022\010protobuf*\202\001\n\nBullet"
"Type\022\024\n\020NULL_BULLET_TYPE\020\000\022\020\n\014FLYING_KNI"
"FE\020\001\022\034\n\030COMMON_ATTACK_OF_TRICKER\020\002\022\r\n\tBO"
"MB_BOMB\020\003\022\020\n\014JUMPY_DUMPTY\020\004\022\r\n\tATOM_BOMB"
"\020\005*\241\001\n\tPlaceType\022\023\n\017NULL_PLACE_TYPE\020\000\022\010\n"
"\004LAND\020\001\022\010\n\004WALL\020\002\022\t\n\005GRASS\020\003\022\r\n\tCLASSROO"
"M\020\004\022\010\n\004GATE\020\005\022\017\n\013HIDDEN_GATE\020\006\022\n\n\006WINDOW"
"\020\007\022\t\n\005DOOR3\020\010\022\t\n\005DOOR5\020\t\022\t\n\005DOOR6\020\n\022\t\n\005C"
"HEST\020\013*8\n\tShapeType\022\023\n\017NULL_SHAPE_TYPE\020\000"
"\022\n\n\006CIRCLE\020\001\022\n\n\006SQUARE\020\002*\256\001\n\010PropType\022\022\n"
"\016NULL_PROP_TYPE\020\000\022\r\n\tADD_SPEED\020\001\022\035\n\031ADD_"
"LIFE_OR_CLAIRAUDIENCE\020\002\022\020\n\014ADD_HP_OR_AP\020"
"\003\022\023\n\017SHIELD_OR_SPEAR\020\004\022\010\n\004KEY3\020\005\022\010\n\004KEY5"
"\020\006\022\010\n\004KEY6\020\007\022\033\n\027RECOVERY_FROM_DIZZINESS\020"
"\010*n\n\017StudentBuffType\022\023\n\017NULL_SBUFF_TYPE\020"
"\000\022\025\n\021STUDENT_ADD_SPEED\020\001\022\014\n\010ADD_LIFE\020\002\022\n"
"\n\006SHIELD\020\003\022\025\n\021STUDENT_INVISIBLE\020\004*\251\002\n\013Pl"
"ayerState\022\017\n\013NULL_STATUS\020\000\022\010\n\004IDLE\020\001\022\014\n\010"
"LEARNING\020\002\022\014\n\010ADDICTED\020\003\022\010\n\004QUIT\020\004\022\r\n\tGR"
"ADUATED\020\005\022\013\n\007TREATED\020\006\022\013\n\007RESCUED\020\007\022\013\n\007S"
"TUNNED\020\010\022\014\n\010TREATING\020\t\022\014\n\010RESCUING\020\n\022\014\n\010"
"SWINGING\020\013\022\r\n\tATTACKING\020\014\022\013\n\007LOCKING\020\r\022\r"
"\n\tRUMMAGING\020\016\022\014\n\010CLIMBING\020\017\022\023\n\017OPENING_A"
"_CHEST\020\020\022\027\n\023USING_SPECIAL_SKILL\020\021\022\022\n\016OPE"
"NING_A_GATE\020\022*~\n\017TrickerBuffType\022\023\n\017NULL"
"_TBUFF_TYPE\020\000\022\025\n\021TRICKER_ADD_SPEED\020\001\022\t\n\005"
"SPEAR\020\002\022\n\n\006ADD_AP\020\003\022\021\n\rCLAIRAUDIENCE\020\004\022\025"
"\n\021TRICKER_INVISIBLE\020\005*J\n\nPlayerType\022\024\n\020N"
"ULL_PLAYER_TYPE\020\000\022\022\n\016STUDENT_PLAYER\020\001\022\022\n"
"\016TRICKER_PLAYER\020\002*\177\n\013StudentType\022\025\n\021NULL"
"_STUDENT_TYPE\020\000\022\013\n\007ATHLETE\020\001\022\013\n\007TEACHER\020"
"\002\022\026\n\022STRAIGHT_A_STUDENT\020\003\022\t\n\005ROBOT\020\004\022\016\n\n"
"TECH_OTAKU\020\005\022\014\n\010SUNSHINE\020\006*Z\n\013TrickerTyp"
"e\022\025\n\021NULL_TRICKER_TYPE\020\000\022\014\n\010ASSASSIN\020\001\022\010"
"\n\004KLEE\020\002\022\022\n\016A_NOISY_PERSON\020\003\022\010\n\004IDOL\020\004*P"
"\n\tGameState\022\023\n\017NULL_GAME_STATE\020\000\022\016\n\nGAME"
"_START\020\001\022\020\n\014GAME_RUNNING\020\002\022\014\n\010GAME_END\020\003"
"b\006proto3";
"\n\021MessageType.proto\022\010protobuf*\177\n\nBulletT"
"ype\022\024\n\020NULL_BULLET_TYPE\020\000\022\020\n\014FLYING_KNIF"
"E\020\001\022\034\n\030COMMON_ATTACK_OF_TRICKER\020\002\022\r\n\tBOM"
"B_BOMB\020\003\022\020\n\014JUMPY_DUMPTY\020\004\022\n\n\006STRIKE\020\005*\241"
"\001\n\tPlaceType\022\023\n\017NULL_PLACE_TYPE\020\000\022\010\n\004LAN"
"D\020\001\022\010\n\004WALL\020\002\022\t\n\005GRASS\020\003\022\r\n\tCLASSROOM\020\004\022"
"\010\n\004GATE\020\005\022\017\n\013HIDDEN_GATE\020\006\022\n\n\006WINDOW\020\007\022\t"
"\n\005DOOR3\020\010\022\t\n\005DOOR5\020\t\022\t\n\005DOOR6\020\n\022\t\n\005CHEST"
"\020\013*8\n\tShapeType\022\023\n\017NULL_SHAPE_TYPE\020\000\022\n\n\006"
"CIRCLE\020\001\022\n\n\006SQUARE\020\002*\302\001\n\010PropType\022\022\n\016NUL"
"L_PROP_TYPE\020\000\022\r\n\tADD_SPEED\020\001\022\035\n\031ADD_LIFE"
"_OR_CLAIRAUDIENCE\020\002\022\020\n\014ADD_HP_OR_AP\020\003\022\023\n"
"\017SHIELD_OR_SPEAR\020\004\022\010\n\004KEY3\020\005\022\010\n\004KEY5\020\006\022\010"
"\n\004KEY6\020\007\022\033\n\027RECOVERY_FROM_DIZZINESS\020\010\022\022\n"
"\016CRAFTING_BENCH\020\t*n\n\017StudentBuffType\022\023\n\017"
"NULL_SBUFF_TYPE\020\000\022\025\n\021STUDENT_ADD_SPEED\020\001"
"\022\014\n\010ADD_LIFE\020\002\022\n\n\006SHIELD\020\003\022\025\n\021STUDENT_IN"
"VISIBLE\020\004*\251\002\n\013PlayerState\022\017\n\013NULL_STATUS"
"\020\000\022\010\n\004IDLE\020\001\022\014\n\010LEARNING\020\002\022\014\n\010ADDICTED\020\003"
"\022\010\n\004QUIT\020\004\022\r\n\tGRADUATED\020\005\022\013\n\007TREATED\020\006\022\013"
"\n\007RESCUED\020\007\022\013\n\007STUNNED\020\010\022\014\n\010TREATING\020\t\022\014"
"\n\010RESCUING\020\n\022\014\n\010SWINGING\020\013\022\r\n\tATTACKING\020"
"\014\022\013\n\007LOCKING\020\r\022\r\n\tRUMMAGING\020\016\022\014\n\010CLIMBIN"
"G\020\017\022\023\n\017OPENING_A_CHEST\020\020\022\027\n\023USING_SPECIA"
"L_SKILL\020\021\022\022\n\016OPENING_A_GATE\020\022*~\n\017Tricker"
"BuffType\022\023\n\017NULL_TBUFF_TYPE\020\000\022\025\n\021TRICKER"
"_ADD_SPEED\020\001\022\t\n\005SPEAR\020\002\022\n\n\006ADD_AP\020\003\022\021\n\rC"
"LAIRAUDIENCE\020\004\022\025\n\021TRICKER_INVISIBLE\020\005*J\n"
"\nPlayerType\022\024\n\020NULL_PLAYER_TYPE\020\000\022\022\n\016STU"
"DENT_PLAYER\020\001\022\022\n\016TRICKER_PLAYER\020\002*\177\n\013Stu"
"dentType\022\025\n\021NULL_STUDENT_TYPE\020\000\022\013\n\007ATHLE"
"TE\020\001\022\013\n\007TEACHER\020\002\022\026\n\022STRAIGHT_A_STUDENT\020"
"\003\022\t\n\005ROBOT\020\004\022\016\n\nTECH_OTAKU\020\005\022\014\n\010SUNSHINE"
"\020\006*Z\n\013TrickerType\022\025\n\021NULL_TRICKER_TYPE\020\000"
"\022\014\n\010ASSASSIN\020\001\022\010\n\004KLEE\020\002\022\022\n\016A_NOISY_PERS"
"ON\020\003\022\010\n\004IDOL\020\004*P\n\tGameState\022\023\n\017NULL_GAME"
"_STATE\020\000\022\016\n\nGAME_START\020\001\022\020\n\014GAME_RUNNING"
"\020\002\022\014\n\010GAME_END\020\003b\006proto3";
static ::_pbi::once_flag descriptor_table_MessageType_2eproto_once; static ::_pbi::once_flag descriptor_table_MessageType_2eproto_once;
const ::_pbi::DescriptorTable descriptor_table_MessageType_2eproto = { const ::_pbi::DescriptorTable descriptor_table_MessageType_2eproto = {
false, false,
false, false,
1488,
1504,
descriptor_table_protodef_MessageType_2eproto, descriptor_table_protodef_MessageType_2eproto,
"MessageType.proto", "MessageType.proto",
&descriptor_table_MessageType_2eproto_once, &descriptor_table_MessageType_2eproto_once,
@@ -179,6 +179,7 @@ namespace protobuf
case 6: case 6:
case 7: case 7:
case 8: case 8:
case 9:
return true; return true;
default: default:
return false; return false;


+ 4
- 3
CAPI/cpp/proto/MessageType.pb.h View File

@@ -57,13 +57,13 @@ namespace protobuf
COMMON_ATTACK_OF_TRICKER = 2, COMMON_ATTACK_OF_TRICKER = 2,
BOMB_BOMB = 3, BOMB_BOMB = 3,
JUMPY_DUMPTY = 4, JUMPY_DUMPTY = 4,
ATOM_BOMB = 5,
STRIKE = 5,
BulletType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), BulletType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
BulletType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() BulletType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
}; };
bool BulletType_IsValid(int value); bool BulletType_IsValid(int value);
constexpr BulletType BulletType_MIN = NULL_BULLET_TYPE; constexpr BulletType BulletType_MIN = NULL_BULLET_TYPE;
constexpr BulletType BulletType_MAX = ATOM_BOMB;
constexpr BulletType BulletType_MAX = STRIKE;
constexpr int BulletType_ARRAYSIZE = BulletType_MAX + 1; constexpr int BulletType_ARRAYSIZE = BulletType_MAX + 1;


const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* BulletType_descriptor(); const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* BulletType_descriptor();
@@ -163,12 +163,13 @@ namespace protobuf
KEY5 = 6, KEY5 = 6,
KEY6 = 7, KEY6 = 7,
RECOVERY_FROM_DIZZINESS = 8, RECOVERY_FROM_DIZZINESS = 8,
CRAFTING_BENCH = 9,
PropType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(), PropType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
PropType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max() PropType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
}; };
bool PropType_IsValid(int value); bool PropType_IsValid(int value);
constexpr PropType PropType_MIN = NULL_PROP_TYPE; constexpr PropType PropType_MIN = NULL_PROP_TYPE;
constexpr PropType PropType_MAX = RECOVERY_FROM_DIZZINESS;
constexpr PropType PropType_MAX = CRAFTING_BENCH;
constexpr int PropType_ARRAYSIZE = PropType_MAX + 1; constexpr int PropType_ARRAYSIZE = PropType_MAX + 1;


const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PropType_descriptor(); const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PropType_descriptor();


+ 4
- 4
CAPI/python/PyAPI/API.py View File

@@ -43,8 +43,8 @@ class StudentAPI(IStudentAPI, IGameTimer):
def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]:
return self.__pool.submit(self.__logic.ThrowProp, propType) return self.__pool.submit(self.__logic.ThrowProp, propType)


def UseSkill(self, skillID: int) -> Future[bool]:
return self.__pool.submit(self.__logic.UseSkill, skillID)
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
return self.__pool.submit(self.__logic.UseSkill, skillID, skillParam)


# 与地图交互相关 # 与地图交互相关
def OpenDoor(self) -> Future[bool]: def OpenDoor(self) -> Future[bool]:
@@ -224,8 +224,8 @@ class TrickerAPI(ITrickerAPI, IGameTimer):
def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]:
return self.__pool.submit(self.__logic.ThrowProp, propType) return self.__pool.submit(self.__logic.ThrowProp, propType)


def UseSkill(self, skillID: int) -> Future[bool]:
return self.__pool.submit(self.__logic.UseSkill, skillID)
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
return self.__pool.submit(self.__logic.UseSkill, skillID, skillParam)


# 与地图交互相关 # 与地图交互相关
def OpenDoor(self) -> Future[bool]: def OpenDoor(self) -> Future[bool]:


+ 2
- 2
CAPI/python/PyAPI/Communication.py View File

@@ -91,14 +91,14 @@ class Communication:
else: else:
return throwResult.act_success return throwResult.act_success


def UseSkill(self, skillID: int, playerID: int) -> bool:
def UseSkill(self, skillID: int, skillParam: int, playerID: int) -> bool:
try: try:
with self.__mtxLimit: with self.__mtxLimit:
if self.__counter >= self.__limit: if self.__counter >= self.__limit:
return False return False
self.__counter += 1 self.__counter += 1
useResult = self.__THUAI6Stub.UseSkill( useResult = self.__THUAI6Stub.UseSkill(
THUAI62Proto.THUAI62ProtobufSkill(skillID, playerID)
THUAI62Proto.THUAI62ProtobufSkill(skillID, skillParam, playerID)
) )
except grpc.RpcError as e: except grpc.RpcError as e:
return False return False


+ 6
- 6
CAPI/python/PyAPI/DebugAPI.py View File

@@ -132,13 +132,13 @@ class StudentDebugAPI(IStudentAPI, IGameTimer):


return self.__pool.submit(logThrow) return self.__pool.submit(logThrow)


def UseSkill(self, skillID: int) -> Future[bool]:
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms"
f"UseSkill: skillID = {skillID}, skillParam = {skillParam}, called at {self.__GetTime()}ms"
) )


def logUse() -> bool: def logUse() -> bool:
result = self.__logic.UseSkill(skillID)
result = self.__logic.UseSkill(skillID, skillParam)
if not result: if not result:
self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms") self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms")
return result return result
@@ -600,13 +600,13 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer):


return self.__pool.submit(logThrow) return self.__pool.submit(logThrow)


def UseSkill(self, skillID: int) -> Future[bool]:
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
self.__logger.info( self.__logger.info(
f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms"
f"UseSkill: skillID = {skillID}, skillParam = {skillParam}, called at {self.__GetTime()}ms"
) )


def logUse() -> bool: def logUse() -> bool:
result = self.__logic.UseSkill(skillID)
result = self.__logic.UseSkill(skillID, skillParam)
if not result: if not result:
self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms") self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms")
return result return result


+ 2
- 2
CAPI/python/PyAPI/Interface.py View File

@@ -80,7 +80,7 @@ class ILogic(metaclass=ABCMeta):
pass pass


@abstractmethod @abstractmethod
def UseSkill(self, skillID: int) -> bool:
def UseSkill(self, skillID: int, skillParam: int) -> bool:
pass pass


@abstractmethod @abstractmethod
@@ -201,7 +201,7 @@ class IAPI(metaclass=ABCMeta):
pass pass


@abstractmethod @abstractmethod
def UseSkill(self, skillID: int) -> Future[bool]:
def UseSkill(self, skillID: int, skillParam: int = 0) -> Future[bool]:
pass pass


@abstractmethod @abstractmethod


+ 2
- 2
CAPI/python/PyAPI/logic.py View File

@@ -190,9 +190,9 @@ class Logic(ILogic):
self.__logger.debug("Called ThrowProp") self.__logger.debug("Called ThrowProp")
return self.__comm.ThrowProp(propType, self.__playerID) return self.__comm.ThrowProp(propType, self.__playerID)


def UseSkill(self, skillID: int) -> bool:
def UseSkill(self, skillID: int, skillParam: int) -> bool:
self.__logger.debug("Called UseSkill") self.__logger.debug("Called UseSkill")
return self.__comm.UseSkill(skillID, self.__playerID)
return self.__comm.UseSkill(skillID, skillParam, self.__playerID)


def SendMessage(self, toID: int, message: Union[str, bytes]) -> bool: def SendMessage(self, toID: int, message: Union[str, bytes]) -> bool:
self.__logger.debug("Called SendMessage") self.__logger.debug("Called SendMessage")


+ 2
- 1
CAPI/python/PyAPI/structures.py View File

@@ -52,6 +52,7 @@ class PropType(Enum):
AddHpOrAp = 6 AddHpOrAp = 6
ShieldOrSpear = 7 ShieldOrSpear = 7
RecoveryFromDizziness = 8 RecoveryFromDizziness = 8
CraftingBench = 9




class BulletType(Enum): class BulletType(Enum):
@@ -60,7 +61,7 @@ class BulletType(Enum):
CommonAttackOfTricker = 2 CommonAttackOfTricker = 2
BombBomb = 3 BombBomb = 3
JumpyDumpty = 4 JumpyDumpty = 4
AtomBomb = 5
Strike = 5




class StudentType(Enum): class StudentType(Enum):


+ 9
- 3
CAPI/python/PyAPI/utils.py View File

@@ -109,6 +109,7 @@ class Proto2THUAI6(NoInstance):
MessageType.ADD_LIFE_OR_CLAIRAUDIENCE: THUAI6.PropType.AddLifeOrClairaudience, MessageType.ADD_LIFE_OR_CLAIRAUDIENCE: THUAI6.PropType.AddLifeOrClairaudience,
MessageType.SHIELD_OR_SPEAR: THUAI6.PropType.ShieldOrSpear, MessageType.SHIELD_OR_SPEAR: THUAI6.PropType.ShieldOrSpear,
MessageType.RECOVERY_FROM_DIZZINESS: THUAI6.PropType.RecoveryFromDizziness, MessageType.RECOVERY_FROM_DIZZINESS: THUAI6.PropType.RecoveryFromDizziness,
MessageType.CRAFTING_BENCH: THUAI6.PropType.CraftingBench,
} }


playerTypeDict: Final[dict] = { playerTypeDict: Final[dict] = {
@@ -187,7 +188,7 @@ class Proto2THUAI6(NoInstance):
MessageType.BOMB_BOMB: THUAI6.BulletType.BombBomb, MessageType.BOMB_BOMB: THUAI6.BulletType.BombBomb,
MessageType.COMMON_ATTACK_OF_TRICKER: THUAI6.BulletType.CommonAttackOfTricker, MessageType.COMMON_ATTACK_OF_TRICKER: THUAI6.BulletType.CommonAttackOfTricker,
MessageType.JUMPY_DUMPTY: THUAI6.BulletType.JumpyDumpty, MessageType.JUMPY_DUMPTY: THUAI6.BulletType.JumpyDumpty,
MessageType.ATOM_BOMB: THUAI6.BulletType.AtomBomb,
MessageType.STRIKE: THUAI6.BulletType.Strike,
} }


# 用于将Proto的对象转为THUAI6的对象 # 用于将Proto的对象转为THUAI6的对象
@@ -357,6 +358,7 @@ class THUAI62Proto(NoInstance):
THUAI6.PropType.AddLifeOrClairaudience: MessageType.ADD_LIFE_OR_CLAIRAUDIENCE, THUAI6.PropType.AddLifeOrClairaudience: MessageType.ADD_LIFE_OR_CLAIRAUDIENCE,
THUAI6.PropType.AddSpeed: MessageType.ADD_SPEED, THUAI6.PropType.AddSpeed: MessageType.ADD_SPEED,
THUAI6.PropType.ShieldOrSpear: MessageType.SHIELD_OR_SPEAR, THUAI6.PropType.ShieldOrSpear: MessageType.SHIELD_OR_SPEAR,
THUAI6.PropType.CraftingBench: MessageType.CRAFTING_BENCH,
} }


# 用于将THUAI6的对象转为Proto的对象 # 用于将THUAI6的对象转为Proto的对象
@@ -421,5 +423,9 @@ class THUAI62Proto(NoInstance):
return Message2Server.AttackMsg(player_id=id, angle=angle) return Message2Server.AttackMsg(player_id=id, angle=angle)


@staticmethod @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
)

+ 2
- 2
CAPI/python/requirements.txt View File

@@ -1,3 +1,3 @@
grpcio==1.54.0
grpcio-tools==1.54.0
grpcio==1.54.2
grpcio-tools==1.54.2
numpy numpy

+ 1
- 0
dependency/proto/Message2Server.proto View File

@@ -61,6 +61,7 @@ message SkillMsg
{ {
int64 player_id = 1; int64 player_id = 1;
int32 skill_id = 2; int32 skill_id = 2;
int32 skill_param = 3;
} }


// 基本继承于THUAI5,为了使发送的信息尽可能不被浪费,暂定不发这类大包。 // 基本继承于THUAI5,为了使发送的信息尽可能不被浪费,暂定不发这类大包。


+ 4
- 3
dependency/proto/MessageType.proto View File

@@ -9,7 +9,7 @@ enum BulletType
COMMON_ATTACK_OF_TRICKER = 2; COMMON_ATTACK_OF_TRICKER = 2;
BOMB_BOMB = 3; BOMB_BOMB = 3;
JUMPY_DUMPTY = 4; JUMPY_DUMPTY = 4;
ATOM_BOMB = 5;
STRIKE = 5;
} }


enum PlaceType // 地图中的所有物件类型 enum PlaceType // 地图中的所有物件类型
@@ -49,6 +49,7 @@ enum PropType // 地图中的可拾取道具类型
KEY5 = 6; KEY5 = 6;
KEY6 = 7; KEY6 = 7;
RECOVERY_FROM_DIZZINESS = 8; RECOVERY_FROM_DIZZINESS = 8;
CRAFTING_BENCH = 9;
} }


enum StudentBuffType // 人类可用的增益效果类型 enum StudentBuffType // 人类可用的增益效果类型
@@ -78,7 +79,7 @@ enum PlayerState
LOCKING = 13; LOCKING = 13;
RUMMAGING = 14; RUMMAGING = 14;
CLIMBING = 15; // 翻窗 CLIMBING = 15; // 翻窗
OPENING_A_CHEST =16;
OPENING_A_CHEST = 16;
USING_SPECIAL_SKILL = 17; USING_SPECIAL_SKILL = 17;
OPENING_A_GATE =18; OPENING_A_GATE =18;
} }
@@ -109,7 +110,7 @@ enum StudentType
TEACHER = 2; TEACHER = 2;
STRAIGHT_A_STUDENT = 3; STRAIGHT_A_STUDENT = 3;
ROBOT = 4; ROBOT = 4;
TECH_OTAKU =5;
TECH_OTAKU = 5;
SUNSHINE = 6; SUNSHINE = 6;
} }




+ 2
- 1
docs/GameRules.md View File

@@ -309,7 +309,7 @@ $$
- 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。 - 不鼓励选手面向地图编程,因为移动过程中你可以受到多种干扰使得移动结果不符合你的预期;因此建议小步移动,边移动边考虑之后的行为。


### 人物 ### 人物
- 眩晕状态中的玩家不能再次被眩晕
- 眩晕状态中的玩家不能再次被眩晕(除非是ShowTime)


### 初始状态 ### 初始状态
- 初赛玩家出生点固定且一定为空地 - 初赛玩家出生点固定且一定为空地
@@ -342,6 +342,7 @@ $$
- 开锁门进度中断后清空 - 开锁门进度中断后清空


### 窗 ### 窗
- 由于窗户被占用导致翻窗失败会使先前行动停止
- 翻越窗户是一种交互行为,翻窗一共有两个过程 - 翻越窗户是一种交互行为,翻窗一共有两个过程
- 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置 - 跳上窗:从当前位置到窗边缘中点,位置瞬移,时间=距离/爬窗速度。中断时,停留在原位置
- 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心 - 爬窗:从窗一侧边缘中点到另一侧格子中心,位置渐移,时间=距离/爬窗速度。中断时,停留在另一侧格子中心


+ 13
- 2
docs/游戏机制与平衡性调整更新草案.md View File

@@ -1,5 +1,5 @@
# 游戏机制与平衡性调整更新草案 # 游戏机制与平衡性调整更新草案
v1.5
v1.6


## 说明 ## 说明
- 该草案尚未完全确定,请大家不要过分依靠该文档进行修改自己的代码 - 该草案尚未完全确定,请大家不要过分依靠该文档进行修改自己的代码
@@ -21,9 +21,9 @@ v1.5
- 未攻击至目标时的后摇改为1200ms - 未攻击至目标时的后摇改为1200ms
- 增强为“可以攻击未写完的作业” - 增强为“可以攻击未写完的作业”
- 增强为“可以攻击使门被打开(可以重新被锁上)” - 增强为“可以攻击使门被打开(可以重新被锁上)”
- 改为“当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上90°,180° ,270° 发出3个小炸弹”
- 小炸弹JumpyDumpty - 小炸弹JumpyDumpty
- 小炸弹不受道具增益影响 - 小炸弹不受道具增益影响
- 修改为“小炸弹与自己无碰撞体积”
- strike(新增) - strike(新增)
- 可以攻击未写完的作业 - 可以攻击未写完的作业


@@ -105,3 +105,14 @@ v1.5
- 普通攻击改为strike - 普通攻击改为strike
- Klee - Klee
- 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙) - 被动技能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倍速"

+ 5
- 2
docs/版本更新说明.md View File

@@ -21,11 +21,14 @@
- docs:更新了 游戏机制与平衡性调整更新草案.pdf - docs:更新了 游戏机制与平衡性调整更新草案.pdf
- change:更改了地图的文件路径 - change:更改了地图的文件路径


# 最新更新
# 5月10日更新
- fix:修复JumpyDumpty的初始位置错误的问题 - fix:修复JumpyDumpty的初始位置错误的问题
- fix:修正和重新说明攻击距离 - fix:修正和重新说明攻击距离
- **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2** - **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2**
- hotfix:修复小炸弹初始化类型错误的问题 - hotfix:修复小炸弹初始化类型错误的问题
- remove:去除了“实际上唤醒或勉励不同的人是有效的” - remove:去除了“实际上唤醒或勉励不同的人是有效的”
- **重复发出同一类型的交互指令和移动指令是无效的** - **重复发出同一类型的交互指令和移动指令是无效的**
- feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`**
- feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`**

# 最新更新
- docs:更新了 游戏机制与平衡性调整更新草案.pdf

+ 51
- 5
installer/Installer/MainWindow.xaml View File

@@ -52,7 +52,7 @@
<Button Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Name="UninstBtn" Content="卸载选手包" Command="{Binding ClickUninstCommand}" Visibility="{Binding MenuVis}" /> <Button Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Name="UninstBtn" Content="卸载选手包" Command="{Binding ClickUninstCommand}" Visibility="{Binding MenuVis}" />
<Button Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="2" Name="MenuBackBtn" Content="回到登陆界面" Command="{Binding ClickBackCommand}" Visibility="{Binding MenuVis}" /> <Button Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="2" Name="MenuBackBtn" Content="回到登陆界面" Command="{Binding ClickBackCommand}" Visibility="{Binding MenuVis}" />


<TextBlock Grid.Row="3" Grid.Column="3" Text="正在处理……" Grid.ColumnSpan="2" Visibility="{Binding ProgressVis}"/>
<TextBlock Grid.Row="3" Grid.Column="3" Text="{Binding ProcessingIntro}" Grid.ColumnSpan="2" Visibility="{Binding ProgressVis}"/>
<ProgressBar Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="7" Minimum="0" Maximum="100" Name="Progress" Visibility="{Binding ProgressVis}" IsIndeterminate="True"/> <ProgressBar Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="7" Minimum="0" Maximum="100" Name="Progress" Visibility="{Binding ProgressVis}" IsIndeterminate="True"/>


<TextBlock Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="4" Text="操作完成!你可以继续操作或退出" Visibility="{Binding CompleteVis}"/> <TextBlock Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="4" Text="操作完成!你可以继续操作或退出" Visibility="{Binding CompleteVis}"/>
@@ -81,10 +81,21 @@
<TextBlock Grid.Row="5" Grid.Column="1" Foreground="Red" Text=" 用户名或密码错误!" Visibility="{Binding LoginFailVis}"/> <TextBlock Grid.Row="5" Grid.Column="1" Foreground="Red" Text=" 用户名或密码错误!" Visibility="{Binding LoginFailVis}"/>
</Grid> </Grid>
</StackPanel> </StackPanel>
<Button Grid.Row="7" Grid.Column="1" Name="Login" Content="登录" Command="{Binding ClickLoginCommand}" Visibility="{Binding LoginVis}"/>
<Button Grid.Row="7" Grid.Column="2" Name="Launch" FontSize="11" Content="{Binding LaunchBtnCont}" Command="{Binding ClickLaunchCommand}" Visibility="{Binding LoginVis}"/>
<Button Grid.Row="7" Grid.Column="3" Name="ShiftLanguage" FontSize="11" Content="更改语言" Command="{Binding ClickShiftLanguageCommand}" Visibility="{Binding LaunchVis}"/>
<StackPanel Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Name="Login" Content="登录" Command="{Binding ClickLoginCommand}" Visibility="{Binding LoginVis}"/>
<Button Grid.Row="0" Grid.Column="1" Name="Launch" Content="{Binding LaunchBtnCont}" Command="{Binding ClickLaunchCommand}" Visibility="{Binding LoginVis}" IsEnabled="{Binding UpdatePlanned}"/>
<Button Grid.Row="0" Grid.Column="2" Name="ShiftLanguage" FontSize="11" Content="配置启动器" Command="{Binding ClickShiftLanguageCommand}" Visibility="{Binding LaunchVis}" IsEnabled="False"/>
</Grid>
</StackPanel>
<Button Grid.Row="7" Grid.Column="4" Grid.ColumnSpan="2" Name="Edit" Content="修改文件" Command="{Binding ClickEditCommand}" Visibility="{Binding LoginVis}"/> <Button Grid.Row="7" Grid.Column="4" Grid.ColumnSpan="2" Name="Edit" Content="修改文件" Command="{Binding ClickEditCommand}" Visibility="{Binding LoginVis}"/>




@@ -114,5 +125,40 @@
<Button Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" Content="申请对战" IsEnabled="False" Command="{Binding ClickRequestCommand}" Visibility="{Binding WebVis}" /> <Button Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" Content="申请对战" IsEnabled="False" Command="{Binding ClickRequestCommand}" Visibility="{Binding WebVis}" />
<TextBox Grid.Row="4" Grid.Column="3" Grid.ColumnSpan="2" Text="暂不支持" IsEnabled="False" Visibility="{Binding WebVis}" /> <TextBox Grid.Row="4" Grid.Column="3" Grid.ColumnSpan="2" Text="暂不支持" IsEnabled="False" Visibility="{Binding WebVis}" />
<!--objects below are enabled-->
<StackPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="7">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Name ="ConfigServer" Content="服务端" Command="{Binding ClickCfgServCommand}" IsEnabled="{Binding ServerCfgNotSelected}" Visibility="{Binding CfgVis}"/>
<Button Grid.Column="1" Name ="ConfigAI" Content="客户端" Command="{Binding ClickCfgCliCommand}" IsEnabled="{Binding CliCfhNotSelected}" Visibility="{Binding CfgVis}"/>
<Button Grid.Column="2" Name ="ConfigCliend" Content="图形界面" Command="{Binding ClickCfgGUICommand}" IsEnabled="{Binding GUICfgNotSelected}" Visibility="{Binding CfgVis}"/>
</Grid>
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="1" Grid.RowSpan="5" Grid.ColumnSpan="7">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
</StackPanel>
</Grid> </Grid>
</Window> </Window>

+ 2
- 2
installer/Installer/Model.cs View File

@@ -405,7 +405,7 @@ namespace starter.viewmodel.settings
/// <summary> /// <summary>
/// 关于介绍的屏幕显示信息 /// 关于介绍的屏幕显示信息
/// </summary> /// </summary>
public enum Status { newUser, menu, move, working, disconnected, error, successful, login, web };
public enum Status { newUser, menu, move, working, initializing, disconnected, error, successful, login, web, launch };
public Status status public Status status
{ {
get; set; get; set;
@@ -881,7 +881,7 @@ namespace Downloader
int upcnt = updateFileName.Count; int upcnt = updateFileName.Count;
if (upcnt <= 20) if (upcnt <= 20)
{ {
while (newFileName.TryDequeue(out var filename))
while (updateFileName.TryDequeue(out var filename))
{ {
try try
{ {


+ 108
- 35
installer/Installer/ViewModel.cs View File

@@ -22,6 +22,7 @@ namespace starter.viewmodel.settings
//定义BackgroundWorker //定义BackgroundWorker
BackgroundWorker asyncDownloader; BackgroundWorker asyncDownloader;
BackgroundWorker asyncUpdater; BackgroundWorker asyncUpdater;
BackgroundWorker asyncInitializer;
/// <summary> /// <summary>
/// Model object /// Model object
/// </summary> /// </summary>
@@ -38,7 +39,7 @@ namespace starter.viewmodel.settings
Status = SettingsModel.Status.working; Status = SettingsModel.Status.working;
string currentDirectory = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName) string currentDirectory = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName)
?? throw new Exception("Fail to get current directory"); ?? throw new Exception("Fail to get current directory");
MessageBox.Show("更新器工作正常");
//MessageBox.Show("更新器工作正常");
if (!Program.Tencent_cos_download.SelfUpdateDismissed()) if (!Program.Tencent_cos_download.SelfUpdateDismissed())
{ {
switch (Program.Tencent_cos_download.CheckSelfVersion()) 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; UpdateInfoVis = Visibility.Collapsed;


if (Downloader.Program.Tencent_cos_download.CheckAlreadyDownload())
asyncInitializer = new BackgroundWorker();
asyncInitializer.WorkerReportsProgress = true;
asyncInitializer.DoWork += AsyncInitializer_DoWork;
asyncInitializer.RunWorkerCompleted += AsyncInitializer_RunWorkerCompleted;

asyncInitializer.RunWorkerAsync();
Status = SettingsModel.Status.initializing;

}

private void AsyncInitializer_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
{
if (e.Result == null)
{
Status = SettingsModel.Status.error;
}
else if ((int)e.Result == 1)
{ {
obj.checkUpdate();
Status = SettingsModel.Status.login; Status = SettingsModel.Status.login;
this.RaisePropertyChanged("WindowWidth");
this.RaisePropertyChanged("LaunchVis"); this.RaisePropertyChanged("LaunchVis");
if (obj.RecallUser())
RememberMe = true;
else
RememberMe = false;
this.RaisePropertyChanged("RememberMe"); this.RaisePropertyChanged("RememberMe");
this.RaisePropertyChanged("SwitchOSBtnCont"); this.RaisePropertyChanged("SwitchOSBtnCont");
//在启动时立刻检查更新,确保选手启动最新版选手包
//若有更新,将启动键改为更新键;
//相应地,使用login界面启动;
//结构:上方为登录框架,下方有“修改选手包”按钮

//下面几行是用来运行测试的代码
//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 else
{ {
Route = Environment.GetEnvironmentVariable("USERPROFILE") + "\\THUAI6"; Route = Environment.GetEnvironmentVariable("USERPROFILE") + "\\THUAI6";
Status = SettingsModel.Status.newUser; Status = SettingsModel.Status.newUser;
this.RaisePropertyChanged("WindowWidth");
}
}

private void AsyncInitializer_DoWork(object? sender, DoWorkEventArgs e)
{
if (asyncInitializer.CancellationPending)
{
Status = SettingsModel.Status.error;
e.Cancel = true;
return;
}
else
{
//实例化BackgroundWorker
asyncDownloader = new BackgroundWorker();
asyncUpdater = new BackgroundWorker();
//指示BackgroundWorker是否可以报告进度更新
//当该属性值为True是,将可以成功调用ReportProgress方法,否则将引发InvalidOperationException异常。
asyncDownloader.WorkerReportsProgress = true;
asyncUpdater.WorkerReportsProgress = true;
//挂载方法:
asyncDownloader.DoWork += AsyncDownloader_DoWork;
asyncUpdater.DoWork += AsyncUpdater_DoWork;
//完成通知器:
asyncDownloader.RunWorkerCompleted += AsyncDownloader_RunWorkerCompleted;
asyncUpdater.RunWorkerCompleted += AsyncUpdater_RunWorkerCompleted;

if (Downloader.Program.Tencent_cos_download.CheckAlreadyDownload())
{
obj.checkUpdate();

if (obj.RecallUser())
RememberMe = true;
else
RememberMe = false;

//在启动时立刻检查更新,确保选手启动最新版选手包
//若有更新,将启动键改为更新键;
//相应地,使用login界面启动;
//结构:上方为登录框架,下方有“修改选手包”按钮

//下面几行是用来运行测试的代码
//Program.RunProgram.StartServerForDebug("0.0.0.0",8888,4,1,600,"video");
//Program.RunProgram.RunCpp("127.0.0.1",8888,4,1,false,true,false);
//Program.RunProgram.RunGUIClient("127.0.0.1", 8888, 0, true);

e.Result = 1;
}
else
{
e.Result = 2;
}
} }
} }


@@ -153,6 +196,7 @@ namespace starter.viewmodel.settings
this.RaisePropertyChanged("UpdateInfo"); this.RaisePropertyChanged("UpdateInfo");
this.RaisePropertyChanged("LaunchBtnCont"); this.RaisePropertyChanged("LaunchBtnCont");
} }
this.RaisePropertyChanged("UpdatePlanned");
} }
} }


@@ -257,6 +301,7 @@ namespace starter.viewmodel.settings
this.RaisePropertyChanged("LaunchVis"); this.RaisePropertyChanged("LaunchVis");
this.RaisePropertyChanged("NewUserVis"); this.RaisePropertyChanged("NewUserVis");
this.RaisePropertyChanged("ConfirmBtnCont"); this.RaisePropertyChanged("ConfirmBtnCont");
this.RaisePropertyChanged("ProcessingIntro");
} }
} }
public string Intro public string Intro
@@ -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 public string AbortOrSelLanguage
{ {
get get
@@ -453,7 +513,7 @@ namespace starter.viewmodel.settings
{ {
get get
{ {
return Status == SettingsModel.Status.working ? Visibility.Visible : Visibility.Collapsed;
return (Status == SettingsModel.Status.working || Status == SettingsModel.Status.initializing) ? Visibility.Visible : Visibility.Collapsed;
} }
} }
public Visibility CompleteVis public Visibility CompleteVis
@@ -508,6 +568,13 @@ namespace starter.viewmodel.settings
return obj.status == SettingsModel.Status.login && (!obj.UpdatePlanned) ? Visibility.Visible : Visibility.Collapsed; return obj.status == SettingsModel.Status.login && (!obj.UpdatePlanned) ? Visibility.Visible : Visibility.Collapsed;
} }
} }
public Visibility CfgVis
{
get
{
return obj.status == SettingsModel.Status.launch ? Visibility.Visible : Visibility.Collapsed;
}
}


public string UpdateBtnCont public string UpdateBtnCont
{ {
@@ -534,9 +601,9 @@ namespace starter.viewmodel.settings
if (obj.UpdatePlanned) if (obj.UpdatePlanned)
ans = "更新"; ans = "更新";
else if (obj.launchLanguage == SettingsModel.LaunchLanguage.cpp) else if (obj.launchLanguage == SettingsModel.LaunchLanguage.cpp)
ans = "启动c++包";
ans = "启动选手包";
else else
ans = "启动python包";
ans = "启动选手包";
return ans; return ans;
} }
} }
@@ -570,6 +637,11 @@ namespace starter.viewmodel.settings
} }
} }


public bool UpdatePlanned
{
get { return obj.UpdatePlanned; }
}

public string RouteSelectWindow(string type) public string RouteSelectWindow(string type)
{ {
if (type == "Folder") if (type == "Folder")
@@ -719,6 +791,7 @@ namespace starter.viewmodel.settings
this.RaisePropertyChanged("UpdateBtnCont"); this.RaisePropertyChanged("UpdateBtnCont");
this.RaisePropertyChanged("UpdateInfo"); this.RaisePropertyChanged("UpdateInfo");
this.RaisePropertyChanged("LaunchVis"); this.RaisePropertyChanged("LaunchVis");
this.RaisePropertyChanged("UpdatePlanned");
} }
})); }));
} }


+ 1
- 3
logic/GameClass/GameObj/Bullet/Bullet.cs View File

@@ -1,6 +1,5 @@
using Preparation.Interface; using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
@@ -27,7 +26,6 @@ namespace GameClass.GameObj
public bool HasSpear => hasSpear; public bool HasSpear => hasSpear;


/// <summary> /// <summary>
/// 与THUAI4不同的一个攻击判定方案,通过这个函数判断爆炸时能否伤害到target
/// </summary> /// </summary>
/// <param name="target">被尝试攻击者</param> /// <param name="target">被尝试攻击者</param>
/// <returns>是否可以攻击到</returns> /// <returns>是否可以攻击到</returns>
@@ -36,7 +34,7 @@ namespace GameClass.GameObj


public override bool IgnoreCollideExecutor(IGameObj targetObj) 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) if (targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet)
return true; return true;
return false; return false;


+ 0
- 5
logic/GameClass/GameObj/Character/Character.Ghost.cs View File

@@ -1,10 +1,5 @@
using Preparation.Interface; using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {


+ 28
- 21
logic/GameClass/GameObj/Character/Character.cs View File

@@ -324,7 +324,17 @@ namespace GameClass.GameObj
return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued && playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued
&& playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack && 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() public bool InteractingWithMapWithoutMoving()
@@ -345,8 +355,9 @@ namespace GameClass.GameObj
{ {
lock (actionLock) lock (actionLock)
return !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped 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); || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
} }
private GameObj? whatInteractingWith = null; private GameObj? whatInteractingWith = null;
@@ -354,28 +365,24 @@ namespace GameClass.GameObj


public long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) 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) 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() public long SetPlayerStateNaturally()


+ 57
- 4
logic/GameClass/GameObj/Map/Window.cs View File

@@ -1,5 +1,7 @@
using Preparation.Interface; using Preparation.Interface;
using Preparation.Utility; using Preparation.Utility;
using System.Numerics;
using System;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
@@ -25,15 +27,66 @@ namespace GameClass.GameObj
return false; return false;
} }


private XY stage = new(0, 0);
public XY Stage
{
get
{
GameObjReaderWriterLock.EnterReadLock();
try
{
return stage;
}
finally { GameObjReaderWriterLock.ExitReadLock(); }
}
}

private Character? whoIsClimbing = null; private Character? whoIsClimbing = null;
public Character? WhoIsClimbing 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(); }
} }
} }
} }

+ 32
- 4
logic/GameClass/GameObj/Moveable.cs View File

@@ -8,8 +8,23 @@ namespace GameClass.GameObj
{ {
protected readonly object actionLock = new(); protected readonly object actionLock = new();
public object ActionLock => actionLock; public object ActionLock => actionLock;
//player.actionLock>其他.actionLock
private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); private readonly ReaderWriterLockSlim moveReaderWriterLock = new();
public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock;

private Semaphore threadNum = new(1, 1);
public Semaphore ThreadNum
{
get
{
return threadNum;
}
set
{
threadNum = value;
}
}

protected long stateNum = 0; protected long stateNum = 0;
public long StateNum 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) 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; return moveVec * moveVec;
} }




+ 115
- 112
logic/GameEngine/MoveEngine.cs View File

@@ -20,18 +20,6 @@ namespace GameEngine


private readonly ITimer gameTimer; private readonly ITimer gameTimer;
private readonly Action<IMoveable> EndMove; private readonly Action<IMoveable> EndMove;
public readonly uint[,] ProtoGameMap;
public PlaceType GetPlaceType(XY Position)
{
try
{
return (PlaceType)ProtoGameMap[Position.x / GameData.numOfPosGridPerCell, Position.y / GameData.numOfPosGridPerCell];
}
catch
{
return PlaceType.Null;
}
}


public IGameObj? CheckCollision(IMoveable obj, XY Pos) public IGameObj? CheckCollision(IMoveable obj, XY Pos)
{ {
@@ -52,7 +40,6 @@ namespace GameEngine
Action<IMoveable> EndMove Action<IMoveable> EndMove
) )
{ {
this.ProtoGameMap = gameMap.ProtoGameMap;
this.gameTimer = gameMap.Timer; this.gameTimer = gameMap.Timer;
this.EndMove = EndMove; this.EndMove = EndMove;
this.OnCollision = OnCollision; this.OnCollision = OnCollision;
@@ -64,21 +51,57 @@ namespace GameEngine
/// </summary> /// </summary>
/// <param name="obj">移动物体,默认obj.Rigid为true</param> /// <param name="obj">移动物体,默认obj.Rigid为true</param>
/// <param name="moveVec">移动的位移向量</param> /// <param name="moveVec">移动的位移向量</param>
private void MoveMax(IMoveable obj, XY moveVec)
private bool MoveMax(IMoveable obj, XY moveVec, long stateNum)
{ {
/*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/
XY nextPos = obj.Position + moveVec; XY nextPos = obj.Position + moveVec;
double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec);
maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond); maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond);
obj.MovingSetPos(new XY(moveVec, maxLen));
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; if (!gameTimer.IsGaming) return;
lock (obj.ActionLock) lock (obj.ActionLock)
{ {
if (!obj.IsAvailableForMove) return;
if (!obj.IsAvailableForMove) { EndMove(obj); return; }
obj.IsMoving = true; obj.IsMoving = true;
} }
new Thread new Thread
@@ -87,9 +110,9 @@ namespace GameEngine
{ {
double moveVecLength = 0.0; double moveVecLength = 0.0;
XY res = new(direction, moveVecLength); XY res = new(direction, moveVecLength);
double deltaLen = 0; // 转向,并用deltaLen存储行走的误差
double deltaLen = (double)0.0; // 转向,并用deltaLen存储行走的误差
IGameObj? collisionObj = null; IGameObj? collisionObj = null;
bool isDestroyed = false;
bool isEnded = false;


bool flag; // 循环标志 bool flag; // 循环标志
do do
@@ -106,34 +129,82 @@ namespace GameEngine
break; break;
case AfterCollision.Destroyed: case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
isEnded = true;
break; break;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
break; break;
} }
} while (flag); } while (flag);


if (!isDestroyed)
if (isEnded)
{ {
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)) switch (OnCollision(obj, collisionObj, res))
{ {
case AfterCollision.ContinueCheck: case AfterCollision.ContinueCheck:
@@ -141,87 +212,19 @@ namespace GameEngine
break; break;
case AfterCollision.Destroyed: case AfterCollision.Destroyed:
Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game.");
isDestroyed = true;
return false;
isEnded = true;
break;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
if (threadNum == 0 || obj.StateNum == threadNum)
MoveMax(obj, res);
MoveMax(obj, res, stateNum);
moveVecLength = 0; moveVecLength = 0;
res = new XY(direction, moveVecLength); res = new XY(direction, moveVecLength);
break; break;
} }
} while (flag);

if (threadNum == 0 || 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(); ).Start();


+ 93
- 63
logic/Gaming/ActionManager.cs View File

@@ -36,26 +36,59 @@ namespace Gaming
public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection)
{ {
if (moveTimeInMilliseconds < 5) return false; if (moveTimeInMilliseconds < 5) return false;
if (!playerToMove.Commandable()) 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; return true;
} }


public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection)
{ {
if (!playerToMove.Commandable() && playerToMove.PlayerState != PlayerStateType.Stunned) return false;
characterManager.BeStunned(playerToMove, moveTimeInMilliseconds);
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, 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; return true;
} }


public bool Stop(Character player) 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; return false;
} }
@@ -279,12 +312,11 @@ namespace Gaming
} }
public bool ClimbingThroughWindow(Character player) public bool ClimbingThroughWindow(Character player)
{ {
if (!player.Commandable())
return false;
Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window); Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window);
if (windowForClimb == null) return false;


if (windowForClimb == null || windowForClimb.WhoIsClimbing != null)
return false;
long stateNum = characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows, windowForClimb);
if (stateNum == -1) return false;


XY windowToPlayer = new( XY windowToPlayer = new(
(Math.Abs(player.Position.x - windowForClimb.Position.x) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.x > windowForClimb.Position.x ? 1 : -1)) : 0, (Math.Abs(player.Position.x - windowForClimb.Position.x) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.x > windowForClimb.Position.x ? 1 : -1)) : 0,
@@ -296,59 +328,57 @@ namespace Gaming
if (player.IsGhost() && !characterInWindow.IsGhost()) if (player.IsGhost() && !characterInWindow.IsGhost())
characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position)); characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position));
return false; return false;
}*/
}


//Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer);
// gameMap.Add(addWall);
Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer);
gameMap.Add(addWall);*/


characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows);
long threadNum = player.StateNum;
windowForClimb.WhoIsClimbing = player;
new Thread 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(); { IsBackground = true }.Start();


return true; return true;
@@ -400,7 +430,6 @@ namespace Gaming
timeInterval: GameData.frameDuration, timeInterval: GameData.frameDuration,
finallyReturn: () => 0 finallyReturn: () => 0
) )

.Start(); .Start();
if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor)
{ {
@@ -462,6 +491,7 @@ namespace Gaming
}, },
EndMove: obj => EndMove: obj =>
{ {
obj.ThreadNum.Release();
// Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64); // Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64);
} }
); );


+ 2
- 0
logic/Gaming/AttackManager.cs View File

@@ -195,7 +195,9 @@ namespace Gaming
{ {
Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); Debugger.Output(bullet, "Attack in " + bullet.Position.ToString());
gameMap.Add(bullet); gameMap.Add(bullet);

moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms

if (bullet.CastTime > 0) if (bullet.CastTime > 0)
{ {
characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack);


logic/Gaming/CharacterManager .cs → logic/Gaming/CharacterManager.cs View File

@@ -25,28 +25,52 @@ namespace Gaming
if (nowPlayerState == value) return -1; if (nowPlayerState == value) return -1;
switch (nowPlayerState) 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: case PlayerStateType.Addicted:
if (value == PlayerStateType.Rescued) if (value == PlayerStateType.Rescued)
return player.ChangePlayerStateInOneThread(value, gameObj); return player.ChangePlayerStateInOneThread(value, gameObj);
else
else if (value == PlayerStateType.Null)
return player.ChangePlayerState(value, gameObj); return player.ChangePlayerState(value, gameObj);
else return -1;
case PlayerStateType.Rescued: case PlayerStateType.Rescued:
if (value == PlayerStateType.Addicted) if (value == PlayerStateType.Addicted)
return player.ChangePlayerStateInOneThread(value, gameObj); 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); 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: default:
if (player.NoHp()) return -1;
return player.ChangePlayerState(value, gameObj); return player.ChangePlayerState(value, gameObj);
} }
} }
@@ -163,7 +187,7 @@ namespace Gaming
newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position)); newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position));
else newPlayer.AddBgm(BgmType.GhostIsComing, 0); else newPlayer.AddBgm(BgmType.GhostIsComing, 0);
} }
if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && !newPlayer.NoHp() && newPlayer.PlayerState != PlayerStateType.Stunned && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange)
if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && newPlayer.CanPinDown() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange)
{ {
TimePinningDown += GameData.checkInterval; TimePinningDown += GameData.checkInterval;
newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded); newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded);

+ 1
- 1
logic/Gaming/PropManager.cs View File

@@ -54,7 +54,7 @@ namespace Gaming
else player.AddAp(GameData.PropDuration); else player.AddAp(GameData.PropDuration);
break; break;
case PropType.RecoveryFromDizziness: case PropType.RecoveryFromDizziness:
if (player.PlayerState == PlayerStateType.Stunned)
if (player.PlayerState == PlayerStateType.Stunned || player.PlayerState == PlayerStateType.Charmed)
{ {
player.AddScore(GameData.ScorePropRecoverFromDizziness); player.AddScore(GameData.ScorePropRecoverFromDizziness);
player.SetPlayerStateNaturally(); player.SetPlayerStateNaturally();


+ 3
- 1
logic/Preparation/Interface/IMoveable.cs View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using Preparation.Utility; using Preparation.Utility;


namespace Preparation.Interface namespace Preparation.Interface
@@ -11,7 +12,8 @@ namespace Preparation.Interface
public bool IsRemoved { get; } public bool IsRemoved { get; }
public bool IsAvailableForMove { get; } public bool IsAvailableForMove { get; }
public long StateNum { 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 void ReSetCanMove(bool value);
public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞
{ {


+ 1
- 0
logic/Preparation/Utility/EnumType.cs View File

@@ -24,6 +24,7 @@ namespace Preparation.Utility
ClimbingThroughWindows = 15, ClimbingThroughWindows = 15,
UsingSkill = 16, UsingSkill = 16,
OpeningTheDoorway = 17, OpeningTheDoorway = 17,
Charmed = 18,
} }
public enum GameObjType public enum GameObjType
{ {


+ 2
- 2
logic/Preparation/Utility/GameData.cs View File

@@ -6,13 +6,13 @@ namespace Preparation.Utility
public static class GameData public static class GameData
{ {
#region 基本常数 #region 基本常数
public const int numOfStepPerSecond = 20; // 每秒行走的步数
public const int numOfStepPerSecond = 100; // 每秒行走的步数


public const int tolerancesLength = 3; public const int tolerancesLength = 3;
public const int adjustLength = 3; public const int adjustLength = 3;


public const int frameDuration = 50; // 每帧时长 public const int frameDuration = 50; // 每帧时长
public const int checkInterval = 50; // 检查位置标志、补充子弹的帧时长
public const int checkInterval = 10;
public const long gameDuration = 600000; // 游戏时长600000ms = 10min public const long gameDuration = 600000; // 游戏时长600000ms = 10min


public const int LimitOfStopAndMove = 15; public const int LimitOfStopAndMove = 15;


+ 1
- 0
logic/Preparation/Utility/Transformation.cs View File

@@ -116,6 +116,7 @@ namespace Preparation.Utility
case Preparation.Utility.PlayerStateType.Rescuing: case Preparation.Utility.PlayerStateType.Rescuing:
return PlayerState.Rescuing; return PlayerState.Rescuing;
case Preparation.Utility.PlayerStateType.Stunned: case Preparation.Utility.PlayerStateType.Stunned:
case Preparation.Utility.PlayerStateType.Charmed:
return PlayerState.Stunned; return PlayerState.Stunned;
case Preparation.Utility.PlayerStateType.Swinging: case Preparation.Utility.PlayerStateType.Swinging:
return PlayerState.Swinging; return PlayerState.Swinging;


Loading…
Cancel
Save