Merge branch 'eesast:dev' into dev
This commit is contained in:
commit
1b2d418d6a
|
@ -0,0 +1,15 @@
|
|||
[*.cs]
|
||||
# 针对枚举成员的命名规则:要求使用 PascalCase
|
||||
dotnet_naming_rule.enum_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.enum_members_should_be_pascal_case.symbols = enum_members
|
||||
dotnet_naming_rule.enum_members_should_be_pascal_case.style = pascal_caser
|
||||
|
||||
# 定义符号:这里适用于所有 const 字段(通常枚举成员会被识别为 const 字段)
|
||||
dotnet_naming_symbols.enum_members.applicable_kinds = field
|
||||
dotnet_naming_symbols.enum_members.required_modifiers = const
|
||||
|
||||
# 定义命名风格:PascalCase
|
||||
dotnet_naming_style.pascal_caser.capitalization = pascal_case
|
||||
dotnet_naming_style.pascal_caser.required_prefix =
|
||||
dotnet_naming_style.pascal_caser.required_suffix =
|
||||
dotnet_naming_style.pascal_caser.word_separator =
|
|
@ -219,23 +219,25 @@ namespace THUAI8
|
|||
CharacterState characterActiveState;
|
||||
|
||||
CharacterState blindState;
|
||||
double blindTime;
|
||||
CharacterState knockbackState;
|
||||
double knockbackTime;
|
||||
long blindTime;
|
||||
// CharacterState knockbackState;
|
||||
// double knockbackTime;
|
||||
CharacterState stunnedState;
|
||||
double stunnedTime;
|
||||
long stunnedTime;
|
||||
CharacterState invisibleState;
|
||||
double invisibleTime;
|
||||
CharacterState healingState;
|
||||
double healingTime;
|
||||
CharacterState berserkState;
|
||||
double berserkTime;
|
||||
long invisibleTime;
|
||||
// CharacterState healingState;
|
||||
// double healingTime;
|
||||
// CharacterState berserkState;
|
||||
// double berserkTime;
|
||||
CharacterState burnedState;
|
||||
double burnedTime;
|
||||
CharacterState harmCutState;
|
||||
double harmCutTime;
|
||||
long burnedTime;
|
||||
double harmCut;
|
||||
long harmCutTime;
|
||||
CharacterState deceasedState;
|
||||
|
||||
CharacterState characterPassiveState;
|
||||
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
|
||||
|
@ -244,29 +246,30 @@ namespace THUAI8
|
|||
int32_t viewRange;
|
||||
|
||||
int32_t commonAttack;
|
||||
double commonAttackCD;
|
||||
long commonAttackCD;
|
||||
int32_t commonAttackRange;
|
||||
|
||||
double skillAttackCD;
|
||||
long skillAttackCD;
|
||||
|
||||
int32_t economyDepletion;
|
||||
int32_t killScore;
|
||||
|
||||
int32_t hp;
|
||||
|
||||
EquipmentType shieldEquipment;
|
||||
int32_t shild;
|
||||
EquipmentType shoesEquipment;
|
||||
double shoesTime;
|
||||
int32_t shieldEquipment;
|
||||
// int32_t shild;
|
||||
int32_t shoesEquipment;
|
||||
long shoesEquipmentTime;
|
||||
long purificationEquipmentTime;
|
||||
|
||||
CharacterBuffType attackBuff;
|
||||
double attackBuffTime;
|
||||
CharacterBuffType defenseBuff;
|
||||
double defenseBuffTime;
|
||||
CharacterBuffType speedBuff;
|
||||
double speedBuffTime;
|
||||
CharacterBuffType visionBuff;
|
||||
double visionBuffTime;
|
||||
// CharacterBuffType attackBuff;
|
||||
long attackBuffTime;
|
||||
// CharacterBuffType defenseBuff;
|
||||
// long defenseBuffTime;
|
||||
// CharacterBuffType speedBuff;
|
||||
long speedBuffTime;
|
||||
// CharacterBuffType visionBuff;
|
||||
long visionBuffTime;
|
||||
};
|
||||
|
||||
struct Team
|
||||
|
|
|
@ -508,7 +508,7 @@ void Logic::LoadBufferCase(const protobuf::MessageOfObj& item)
|
|||
break;
|
||||
case THUAI8::MessageOfObj::TrapMessage:
|
||||
// 待定
|
||||
if (item.trap_message().team_id() == teamID || AssistFunction::HaveView(x, y, item.trap_message().x(), item.trap_message().y(), viewRange, bufferState->gameMap) && currentState->characterSelf->visionBuff == THUAI8::CharacterBuffType::VisionBuff)
|
||||
if (item.trap_message().team_id() == teamID || AssistFunction::HaveView(x, y, item.trap_message().x(), item.trap_message().y(), viewRange, bufferState->gameMap) && currentState->characterSelf->visionBuffTime > 0)
|
||||
{
|
||||
auto pos = THUAI8::cellxy_t(
|
||||
AssistFunction::GridToCell(item.trap_message().x()),
|
||||
|
@ -603,7 +603,7 @@ void Logic::LoadBufferCase(const protobuf::MessageOfObj& item)
|
|||
{
|
||||
for (const auto& character : bufferState->characters)
|
||||
{
|
||||
if (AssistFunction::HaveView(character->x, character->y, targetX, targetY, character->viewRange, bufferState->gameMap) && character->visionBuff == THUAI8::CharacterBuffType::VisionBuff)
|
||||
if (AssistFunction::HaveView(character->x, character->y, targetX, targetY, character->viewRange, bufferState->gameMap) && character->visionBuffTime > 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import PyAPI.structures as THUAI8
|
||||
from PyAPI.Interface import ICharacterAPI, ITeamAPI, IAI
|
||||
from PyAPI.utils import AssistFunction
|
||||
from typing import Union, Final, cast, List
|
||||
from PyAPI.constants import Constants
|
||||
import queue
|
||||
import time
|
||||
|
||||
class Setting:
|
||||
# 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新,大致一帧更新一次
|
||||
@staticmethod
|
||||
def Asynchronous() -> bool:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def ShipTypes() -> List[THUAI8.CharacterType]:
|
||||
return [
|
||||
THUAI8.CharacterType.Monk,
|
||||
THUAI8.CharacterType.MonkeyKing,
|
||||
THUAI8.CharacterType.Pigsy,
|
||||
THUAI8.CharacterType.ShaWujing,
|
||||
THUAI8.CharacterType.Whitedragonhorse,
|
||||
THUAI8.CharacterType.JiuTouYuanSheng,
|
||||
THUAI8.CharacterType.Honghaier,
|
||||
THUAI8.CharacterType.Gyuumao,
|
||||
THUAI8.CharacterType.Princess_Iron_Fan,
|
||||
THUAI8.CharacterType.Spider,
|
||||
]
|
||||
numOfGridPerCell: Final[int] = 1000
|
||||
|
||||
class AI(IAI):
|
||||
def __init__(self, pID: int):
|
||||
self.__playerID = pID
|
||||
|
||||
def ShipPlay(self, api: IShipAPI) -> None:
|
||||
# 公共操作
|
||||
if self.__playerID == 1:
|
||||
# player1的操作
|
||||
return
|
||||
elif self.__playerID == 2:
|
||||
# player2的操作
|
||||
return
|
||||
elif self.__playerID == 3:
|
||||
# player3的操作
|
||||
return
|
||||
elif self.__playerID == 4:
|
||||
# player4的操作
|
||||
return
|
||||
elif self.__playerID == 5:
|
||||
# player4的操作
|
||||
return
|
||||
|
||||
def TeamPlay(self, api: ITeamAPI) -> None:
|
||||
# player0的操作
|
||||
return
|
|
@ -0,0 +1,245 @@
|
|||
from PyAPI.structures import THUAI8
|
||||
from PyAPI.Interface import ILogic, IAI, IGameTimer, ICharacterAPI, ITeamAPI
|
||||
from concurrent.futures import ThreadPoolExecutor, Future
|
||||
from typing import List, Optional, Tuple, cast, Union
|
||||
import math
|
||||
|
||||
|
||||
class IAPI:
|
||||
@staticmethod
|
||||
def CellToGrid(cell: int) -> int:
|
||||
return cell * 1000 + 500
|
||||
|
||||
@staticmethod
|
||||
def GridToCell(grid: int) -> int:
|
||||
return grid // 1000
|
||||
|
||||
|
||||
class CharacterAPI(ICharacterAPI, IGameTimer):
|
||||
def __init__(self, logic: ILogic) -> None:
|
||||
self.__logic = logic
|
||||
self.__pool = ThreadPoolExecutor(20)
|
||||
|
||||
# region 实现IGameTimer接口
|
||||
def StartTimer(self) -> None:
|
||||
pass
|
||||
|
||||
def EndTimer(self) -> None:
|
||||
pass
|
||||
|
||||
def Play(self, ai: IAI) -> None:
|
||||
ai.Play(self)
|
||||
# endregion
|
||||
|
||||
# region 实现IAPI接口
|
||||
def SendTextMessage(self, toPlayerID: int, message: str) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Send, toPlayerID, message, False)
|
||||
|
||||
def SendBinaryMessage(self, toPlayerID: int, message: str) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Send, toPlayerID, message, True)
|
||||
|
||||
def HaveMessage(self) -> bool:
|
||||
return self.__logic.HaveMessage()
|
||||
|
||||
def GetMessage(self) -> Tuple[int, str]:
|
||||
return self.__logic.GetMessage()
|
||||
|
||||
def GetFrameCount(self) -> int:
|
||||
return self.__logic.GetCounter()
|
||||
|
||||
def Wait(self) -> bool:
|
||||
return self.__logic.WaitThread()
|
||||
|
||||
def EndAllAction(self) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.EndAllAction)
|
||||
|
||||
def GetCharacters(self) -> List[THUAI8.Character]:
|
||||
return self.__logic.GetCharacters()
|
||||
|
||||
def GetEnemyCharacters(self) -> List[THUAI8.Character]:
|
||||
return self.__logic.GetEnemyCharacters()
|
||||
|
||||
def GetFullMap(self) -> List[List[THUAI8.PlaceType]]:
|
||||
return self.__logic.GetFullMap()
|
||||
|
||||
def GetGameInfo(self) -> THUAI8.GameInfo:
|
||||
return self.__logic.GetGameInfo()
|
||||
|
||||
def GetPlaceType(self, cellX: int, cellY: int) -> THUAI8.PlaceType:
|
||||
return self.__logic.GetPlaceType(cellX, cellY)
|
||||
|
||||
def GetEnconomyResourceState(self, cellX: int, cellY: int) -> Optional[THUAI8.EconomyResourceState]:
|
||||
return self.__logic.GetEnconomyResourceState(cellX, cellY)
|
||||
|
||||
def GetAdditionResourceState(self, cellX: int, cellY: int) -> Optional[THUAI8.AdditionResourceState]:
|
||||
return self.__logic.GetAdditionResourceState(cellX, cellY)
|
||||
|
||||
def GetConstructionState(self, cellX: int, cellY: int) -> Optional[THUAI8.ConstructionState]:
|
||||
return self.__logic.GetConstructionState(cellX, cellY)
|
||||
|
||||
def GetPlayerGUIDs(self) -> List[int]:
|
||||
return self.__logic.GetPlayerGUIDs()
|
||||
|
||||
def GetEnergy(self) -> int:
|
||||
return self.__logic.GetEnergy()
|
||||
|
||||
def GetScore(self) -> int:
|
||||
return self.__logic.GetScore()
|
||||
|
||||
def Print(self, string: str) -> None:
|
||||
pass
|
||||
|
||||
def PrintCharacter(self) -> None:
|
||||
pass
|
||||
|
||||
def PrintTeam(self) -> None:
|
||||
pass
|
||||
|
||||
def PrintSelfInfo(self) -> None:
|
||||
pass
|
||||
# endregion
|
||||
|
||||
# region 实现ICharacterAPI接口
|
||||
def Move(self, speed: int, timeInMilliseconds: int, angleInRadian: float) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Move, speed, timeInMilliseconds, angleInRadian)
|
||||
|
||||
def MoveRight(self, speed: int, timeInMilliseconds: int) -> Future[bool]:
|
||||
return self.Move(speed, timeInMilliseconds, math.pi/2)
|
||||
|
||||
def MoveUp(self, speed: int, timeInMilliseconds: int) -> Future[bool]:
|
||||
return self.Move(speed, timeInMilliseconds, math.pi)
|
||||
|
||||
def MoveLeft(self, speed: int, timeInMilliseconds: int) -> Future[bool]:
|
||||
return self.Move(speed, timeInMilliseconds, math.pi*3/2)
|
||||
|
||||
def MoveDown(self, speed: int, timeInMilliseconds: int) -> Future[bool]:
|
||||
return self.Move(speed, timeInMilliseconds, 0)
|
||||
|
||||
def Skill_Attack(self, attackedPlayerID: int) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Skill_Attack, attackedPlayerID)
|
||||
|
||||
def Common_Attack(self, attackedPlayerID: int) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Common_Attack, attackedPlayerID)
|
||||
|
||||
def Recover(self, recover: int) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Recover, recover)
|
||||
|
||||
def Harvest(self) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Produce)
|
||||
|
||||
def Rebuild(self, constructionType: THUAI8.ConstructionType) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Rebuild, constructionType)
|
||||
|
||||
def Construct(self, constructionType: THUAI8.ConstructionType) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Construct, constructionType)
|
||||
|
||||
def GetSelfInfo(self) -> THUAI8.Character:
|
||||
return cast(THUAI8.Character, self.__logic.CharacterGetSelfInfo())
|
||||
|
||||
def HaveView(self, targetX: int, targetY: int) -> bool:
|
||||
self_info = self.GetSelfInfo()
|
||||
return self.__logic.HaveView(
|
||||
self_info.x, self_info.y,
|
||||
targetX, targetY,
|
||||
self_info.viewRange
|
||||
)
|
||||
# endregion
|
||||
|
||||
|
||||
class TeamAPI(ITeamAPI, IGameTimer):
|
||||
def __init__(self, logic: ILogic) -> None:
|
||||
self.__logic = logic
|
||||
self.__pool = ThreadPoolExecutor(20)
|
||||
|
||||
# region 实现IGameTimer接口
|
||||
def StartTimer(self) -> None:
|
||||
pass
|
||||
|
||||
def EndTimer(self) -> None:
|
||||
pass
|
||||
|
||||
def Play(self, ai: IAI) -> None:
|
||||
ai.Play(self)
|
||||
# endregion
|
||||
|
||||
# region 实现IAPI接口
|
||||
def SendTextMessage(self, toPlayerID: int, message: str) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Send, toPlayerID, message, False)
|
||||
|
||||
def SendBinaryMessage(self, toPlayerID: int, message: str) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Send, toPlayerID, message, True)
|
||||
|
||||
def HaveMessage(self) -> bool:
|
||||
return self.__logic.HaveMessage()
|
||||
|
||||
def GetMessage(self) -> Tuple[int, str]:
|
||||
return self.__logic.GetMessage()
|
||||
|
||||
def GetFrameCount(self) -> int:
|
||||
return self.__logic.GetCounter()
|
||||
|
||||
def Wait(self) -> bool:
|
||||
return self.__logic.WaitThread()
|
||||
|
||||
def EndAllAction(self) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.EndAllAction)
|
||||
|
||||
def GetCharacters(self) -> List[THUAI8.Character]:
|
||||
return self.__logic.GetCharacters()
|
||||
|
||||
def GetEnemyCharacters(self) -> List[THUAI8.Character]:
|
||||
return self.__logic.GetEnemyCharacters()
|
||||
|
||||
def GetFullMap(self) -> List[List[THUAI8.PlaceType]]:
|
||||
return self.__logic.GetFullMap()
|
||||
|
||||
def GetGameInfo(self) -> THUAI8.GameInfo:
|
||||
return self.__logic.GetGameInfo()
|
||||
|
||||
def GetPlaceType(self, cellX: int, cellY: int) -> THUAI8.PlaceType:
|
||||
return self.__logic.GetPlaceType(cellX, cellY)
|
||||
|
||||
def GetEnconomyResourceState(self, cellX: int, cellY: int) -> Optional[THUAI8.EconomyResourceState]:
|
||||
return self.__logic.GetEnconomyResourceState(cellX, cellY)
|
||||
|
||||
def GetAdditionResourceState(self, cellX: int, cellY: int) -> Optional[THUAI8.AdditionResourceState]:
|
||||
return self.__logic.GetAdditionResourceState(cellX, cellY)
|
||||
|
||||
def GetConstructionState(self, cellX: int, cellY: int) -> Optional[THUAI8.ConstructionState]:
|
||||
return self.__logic.GetConstructionState(cellX, cellY)
|
||||
|
||||
def GetPlayerGUIDs(self) -> List[int]:
|
||||
return self.__logic.GetPlayerGUIDs()
|
||||
|
||||
def GetEnergy(self) -> int:
|
||||
return self.__logic.GetEnergy()
|
||||
|
||||
def GetScore(self) -> int:
|
||||
return self.__logic.GetScore()
|
||||
|
||||
def Print(self, string: str) -> None:
|
||||
pass
|
||||
|
||||
def PrintCharacter(self) -> None:
|
||||
pass
|
||||
|
||||
def PrintTeam(self) -> None:
|
||||
pass
|
||||
|
||||
def PrintSelfInfo(self) -> None:
|
||||
pass
|
||||
# endregion
|
||||
|
||||
# region 实现ITeamAPI接口
|
||||
def GetSelfInfo(self) -> THUAI8.Team:
|
||||
return cast(THUAI8.Team, self.__logic.TeamGetSelfInfo())
|
||||
|
||||
def InstallEquipment(self, playerID: int, equipmentType: THUAI8.EquipmentType) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.InstallEquipment, playerID, equipmentType)
|
||||
|
||||
def Recycle(self, playerID: int) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.Recycle, playerID)
|
||||
|
||||
def BuildCharacter(self, CharacterType: THUAI8.CharacterType, birthIndex: int) -> Future[bool]:
|
||||
return self.__pool.submit(self.__logic.BuildCharacter, CharacterType, birthIndex)
|
||||
# endregion
|
|
@ -0,0 +1,325 @@
|
|||
import PyAPI.structures as THUAI8
|
||||
import logging
|
||||
import os
|
||||
import datetime
|
||||
from concurrent.futures import ThreadPoolExecutor, Future
|
||||
from typing import List, Tuple, Optional, Dict, Any, Union
|
||||
import math
|
||||
from PyAPI.structures import PlaceType, ConstructionType, CharacterType, GameInfo, Character, ConstructionState, EconomyResourceState, AdditionResourceState
|
||||
from PyAPI.Interface import ILogic, IAI
|
||||
|
||||
PI = math.pi
|
||||
|
||||
class CharacterDebugAPI:
|
||||
def __init__(self, logic: ILogic, file: bool, print: bool, warnOnly: bool, CharacterID: int):
|
||||
self.logic = logic
|
||||
self.playerID = CharacterID
|
||||
self.startPoint = datetime.datetime.now()
|
||||
self.__pool = ThreadPoolExecutor(20)
|
||||
self.logger = logging.getLogger(f"api {self.playerID}")
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
|
||||
formatter = logging.Formattter(
|
||||
f"[api {self.playerID}] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s",
|
||||
"%H:%M:%S"
|
||||
)
|
||||
|
||||
if not os.path.exists("logs"):
|
||||
os.makedirs("logs")
|
||||
|
||||
fileHandler = logging.FileHandler(f"logs/api-{self.playerID}-log.txt", mode="w+", encoding="utf-8")
|
||||
printHandler = logging.StreamHandler()
|
||||
|
||||
fileHandler.setFormatter(formatter)
|
||||
printHandler.setFormatter(formatter)
|
||||
|
||||
fileHandler.setLevel(logging.TRACE if file else logging.OFF)
|
||||
print_level = logging.WARN if warnOnly else (logging.INFO if print else logging.OFF)
|
||||
printHandler.setLevel(print_level)
|
||||
|
||||
self.logger.addHandler(fileHandler)
|
||||
self.logger.addHandler(printHandler)
|
||||
self.logger.propagate = False
|
||||
|
||||
def StartTimer(self) -> None:
|
||||
self.startPoint = datetime.datetime.now()
|
||||
self.logger.info("=== AI.play() ===")
|
||||
self.logger.info(f"StartTimer: {self.startPoint.strftime('%H:%M:%S')}")
|
||||
|
||||
def EndTimer(self) -> None:
|
||||
elapsed = (datetime.datetime.now() - self.startPoint).total_seconds() * 1000
|
||||
self.logger.info(f"Time elapsed: {elapsed:.2f}ms")
|
||||
|
||||
def GetFrameCount(self) -> int:
|
||||
return self.logic.GetCounter()
|
||||
|
||||
def SendTextMessage(self, toID: int, message: str) -> Future[bool]:
|
||||
self.logger.info(f"SendTextMessage: toID={toID}, message={message}, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logSend, toID, message, False)
|
||||
|
||||
def SendBinaryMessage(self, toID: int, message: bytes) -> Future[bool]:
|
||||
self.logger.info(f"SendBinaryMessage: toID={toID}, message={message}, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logSend, toID, message, True)
|
||||
|
||||
def __logSend(self, toID: int, message: Union[str, bytes], isBinary: bool) -> bool:
|
||||
result = self.logic.Send(toID, message, isBinary)
|
||||
if not result:
|
||||
self.logger.warning(f"Send failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def HaveMessage(self) -> bool:
|
||||
self.logger.info(f"HaveMessage: called at {self.__GetTime()}ms")
|
||||
result = self.logic.HaveMessage()
|
||||
if not result:
|
||||
self.logger.warning(f"HaveMessage failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetMessage(self) -> Tuple[int, str]:
|
||||
self.logger.info(f"GetMessage: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetMessage()
|
||||
if result[0] == -1:
|
||||
self.logger.warning(f"GetMessage failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def Wait(self) -> bool:
|
||||
self.logger.info(f"Wait: called at {self.__GetTime()}ms")
|
||||
return False if self.logic.GetCounter() == -1 else self.logic.WaitThread()
|
||||
|
||||
def Move(self, timeInMilliseconds: int, angleInRadian: float) -> Future[bool]:
|
||||
self.logger.info(f"Move: time={timeInMilliseconds}ms, angle={angleInRadian}rad, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logMove, timeInMilliseconds, angleInRadian)
|
||||
|
||||
def __logMove(self, time: int, angle: float) -> bool:
|
||||
result = self.logic.Move(time, angle)
|
||||
if not result:
|
||||
self.logger.warning(f"Move failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def MoveDown(self, time: int) -> Future[bool]:
|
||||
return self.Move(time, 0)
|
||||
|
||||
def MoveRight(self, time: int) -> Future[bool]:
|
||||
return self.Move(time, PI * 0.5)
|
||||
|
||||
def MoveUp(self, time: int) -> Future[bool]:
|
||||
return self.Move(time, PI)
|
||||
|
||||
def MoveLeft(self, time: int) -> Future[bool]:
|
||||
return self.Move(time, PI * 1.5)
|
||||
|
||||
def Skill_Attack(self, angleInRadian: float) -> Future[bool]:
|
||||
self.logger.info(f"Skill_Attack: angle={angleInRadian}rad, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logSkillAttack, angleInRadian)
|
||||
|
||||
def __logSkillAttack(self, angle: float) -> bool:
|
||||
result = self.logic.SkillAttack(angle)
|
||||
if not result:
|
||||
self.logger.warning(f"Skill_Attack failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def Common_Attack(self, angleInRadian: float) -> Future[bool]:
|
||||
self.logger.info(f"Common_Attack: angle={angleInRadian}rad, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logCommonAttack, angleInRadian)
|
||||
|
||||
def __logCommonAttack(self, angle: float) -> bool:
|
||||
result = self.logic.CommonAttack(angle)
|
||||
if not result:
|
||||
self.logger.warning(f"Common_Attack failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def Recover(self, recover: int) -> Future[bool]:
|
||||
self.logger.info(f"Recover: {recover}, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logRecover, recover)
|
||||
|
||||
def __logRecover(self, recover: int) -> bool:
|
||||
result = self.logic.Recover(recover)
|
||||
if not result:
|
||||
self.logger.warning(f"Recover failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def Harvest(self) -> Future[bool]:
|
||||
self.logger.info(f"Harvest: called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logHarvest)
|
||||
|
||||
def __logHarvest(self) -> bool:
|
||||
result = self.logic.Harvest()
|
||||
if not result:
|
||||
self.logger.warning(f"Harvest failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def Rebuild(self, constructionType: ConstructionType) -> Future[bool]:
|
||||
self.logger.info(f"Rebuild: {constructionType}, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logRebuild, constructionType)
|
||||
|
||||
def __logRebuild(self, ct: ConstructionType) -> bool:
|
||||
result = self.logic.Rebuild(ct)
|
||||
if not result:
|
||||
self.logger.warning(f"Rebuild failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def Construct(self, constructionType: ConstructionType) -> Future[bool]:
|
||||
self.logger.info(f"Construct: {constructionType}, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logConstruct, constructionType)
|
||||
|
||||
def __logConstruct(self, ct: ConstructionType) -> bool:
|
||||
result = self.logic.Construct(ct)
|
||||
if not result:
|
||||
self.logger.warning(f"Construct failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetCharacters(self) -> List[Character]:
|
||||
self.logger.info(f"GetCharacters: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetCharacters()
|
||||
if not result:
|
||||
self.logger.warning(f"GetCharacters failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetEnemyCharacters(self) -> List[Character]:
|
||||
self.logger.info(f"GetEnemyCharacters: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetEnemyCharacters()
|
||||
if not result:
|
||||
self.logger.warning(f"GetEnemyCharacters failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetFullMap(self) -> List[List[PlaceType]]:
|
||||
self.logger.info(f"GetFullMap: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetFullMap()
|
||||
if not result:
|
||||
self.logger.warning(f"GetFullMap failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetGameInfo(self) -> GameInfo:
|
||||
self.logger.info(f"GetGameInfo: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetGameInfo()
|
||||
if not result:
|
||||
self.logger.warning(f"GetGameInfo failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetPlaceType(self, cellX: int, cellY: int) -> Optional[PlaceType]:
|
||||
self.logger.info(f"GetPlaceType: cellX={cellX}, cellY={cellY}, called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetPlaceType(cellX, cellY)
|
||||
if not result:
|
||||
self.logger.warning(f"GetPlaceType failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetEnconomyResourceState(self, cellX: int, cellY: int) -> Optional[EconomyResourceState]:
|
||||
self.logger.info(f"GetEnconomyResourceState: cellX={cellX}, cellY={cellY}, called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetEnconomyResourceState(cellX, cellY)
|
||||
if not result:
|
||||
self.logger.warning(f"GetEnconomyResourceState failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetAdditionResourceState(self, cellX: int, cellY: int) -> Optional[AdditionResourceState]:
|
||||
self.logger.info(f"GetAdditionResourceState: cellX={cellX}, cellY={cellY}, called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetAdditionResourceState(cellX, cellY)
|
||||
if not result:
|
||||
self.logger.warning(f"GetAdditionResourceState failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetConstructionState(self, cellX: int, cellY: int) -> Optional[ConstructionState]:
|
||||
self.logger.info(f"GetConstructionState: cellX={cellX}, cellY={cellY}, called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetConstructionState(cellX, cellY)
|
||||
if not result:
|
||||
self.logger.warning(f"GetConstructionState failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetPlayerGUIDs(self) -> List[int]:
|
||||
self.logger.info(f"GetPlayerGUIDs: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetPlayerGUIDs()
|
||||
if not result:
|
||||
self.logger.warning(f"GetPlayerGUIDs failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetEnergy(self) -> int:
|
||||
self.logger.info(f"GetEnergy: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetEnergy()
|
||||
if result == -1:
|
||||
self.logger.warning(f"GetEnergy failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetScore(self) -> int:
|
||||
self.logger.info(f"GetScore: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetScore()
|
||||
if result == -1:
|
||||
self.logger.warning(f"GetScore failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def GetSelfInfo(self) -> Character:
|
||||
self.logger.info(f"GetSelfInfo: called at {self.__GetTime()}ms")
|
||||
result = self.logic.GetSelfInfo()
|
||||
if not result:
|
||||
self.logger.warning(f"GetSelfInfo failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def Print(self, string: str) -> None:
|
||||
self.logger.info(string)
|
||||
|
||||
def PrintCharacter(self) -> None:
|
||||
for char in self.logic.GetCharacters():
|
||||
self.logger.info("******Character Info******")
|
||||
self.logger.info(f"type={char.characterType}, ID={char.characterID}, GUID={char.guid}, x={char.x}, y={char.y}")
|
||||
self.logger.info(f"state={char.characterState}, speed={char.speed}, view={char.viewRange}, facing={char.facingDirection}")
|
||||
self.logger.info("**************************")
|
||||
|
||||
def PrintSelfInfo(self) -> None:
|
||||
selfInfo = self.GetSelfInfo()
|
||||
self.logger.info("******Self Info******")
|
||||
self.logger.info(f"type={selfInfo.characterType}, ID={selfInfo.characterID}, GUID={selfInfo.guid}")
|
||||
self.logger.info(f"x={selfInfo.x}, y={selfInfo.y}, state={selfInfo.characterState}")
|
||||
self.logger.info("*********************")
|
||||
|
||||
def EndAllAction(self) -> Future[bool]:
|
||||
self.logger.info(f"EndAllAction: called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.logic.EndAllAction)
|
||||
|
||||
def __GetTime(self) -> float:
|
||||
return (datetime.datetime.now() - self.startPoint).total_seconds() * 1000
|
||||
|
||||
def Play(self, ai: IAI) -> None:
|
||||
ai.play(self)
|
||||
|
||||
class TeamDebugAPI(CharacterDebugAPI):
|
||||
def __init__(self, logic: ILogic, file: bool, print: bool, warnOnly: bool, playerID: int):
|
||||
super().__init__(logic, file, print, warnOnly, playerID)
|
||||
# 覆盖父类logger配置
|
||||
self.logger.handlers.clear()
|
||||
formatter = logging.Formatter(
|
||||
f"[api{self.playerID}] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s",
|
||||
"%H:%M:%S"
|
||||
)
|
||||
fileHandler = logging.FileHandler(f"logs/api-{self.playerID}-log.txt", mode="w+", encoding="utf-8")
|
||||
printHandler = logging.StreamHandler()
|
||||
fileHandler.setFormatter(formatter)
|
||||
printHandler.setFormatter(formatter)
|
||||
fileHandler.setLevel(logging.TRACE if file else logging.OFF)
|
||||
print_level = logging.WARN if warnOnly else (logging.INFO if print else logging.OFF)
|
||||
printHandler.setLevel(print_level)
|
||||
self.logger.addHandler(fileHandler)
|
||||
self.logger.addHandler(printHandler)
|
||||
|
||||
def InstallEquipment(self, playerID: int, equipmentType: Any) -> Future[bool]:
|
||||
self.logger.info(f"InstallEquipment: playerID={playerID}, type={equipmentType}, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logInstall, playerID, equipmentType)
|
||||
|
||||
def __logInstall(self, pid: int, et: Any) -> bool:
|
||||
result = self.logic.InstallEquipment(pid, et)
|
||||
if not result:
|
||||
self.logger.warning(f"InstallEquipment failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def BuildCharacter(self, characterType: CharacterType, birthIndex: int) -> Future[bool]:
|
||||
self.logger.info(f"BuildCharacter: type={characterType}, index={birthIndex}, called at {self.__GetTime()}ms")
|
||||
return self.__pool.submit(self.__logBuild, characterType, birthIndex)
|
||||
|
||||
def __logBuild(self, ct: CharacterType, bi: int) -> bool:
|
||||
result = self.logic.BuildCharacter(ct, bi)
|
||||
if not result:
|
||||
self.logger.warning(f"BuildCharacter failed at {self.__GetTime()}ms")
|
||||
return result
|
||||
|
||||
def PrintSelfInfo(self) -> None:
|
||||
selfInfo = self.logic.GetSelfInfo()
|
||||
self.logger.info("******Team Info******")
|
||||
self.logger.info(f"teamID={selfInfo.teamID}, playerID={selfInfo.playerID}")
|
||||
self.logger.info(f"score={selfInfo.score}, energy={selfInfo.energy}")
|
||||
self.logger.info("*********************")
|
|
@ -25,56 +25,62 @@ message MessageOfCharacter
|
|||
|
||||
// 被动状态
|
||||
CharacterState blind_state = 6;
|
||||
double blind_time = 7;
|
||||
CharacterState knockback_state = 8;
|
||||
double knockback_time = 9;
|
||||
CharacterState stunned_state = 10;
|
||||
double stunned_time = 11;
|
||||
CharacterState invisible_state = 12;
|
||||
double invisible_time = 13;
|
||||
CharacterState healing_state = 14;
|
||||
double healing_time = 15;
|
||||
CharacterState berserk_state = 16;
|
||||
double berserk_time = 17;
|
||||
CharacterState burned_state = 18;
|
||||
double burned_time = 19;
|
||||
CharacterState harm_cut_state = 20;
|
||||
double harm_cut_time = 21;
|
||||
CharacterState deceased_state = 22; // 死亡状态不需要持续时间
|
||||
int64 blind_time = 7;
|
||||
// CharacterState knockback_state = 8;
|
||||
// double knockback_time = 9;
|
||||
CharacterState stunned_state = 8;
|
||||
int64 stunned_time = 9;
|
||||
CharacterState invisible_state = 10;
|
||||
int64 invisible_time = 11;
|
||||
// CharacterState healing_state = 14;
|
||||
// long healing_time = 15;
|
||||
// CharacterState berserk_state = 16;
|
||||
// long berserk_time = 17;
|
||||
CharacterState burned_state = 12;
|
||||
int64 burned_time = 13;
|
||||
double harm_cut = 14;
|
||||
int64 harm_cut_time = 15;
|
||||
CharacterState deceased_state = 16; // 死亡状态不需要持续时间
|
||||
|
||||
int32 x = 23;
|
||||
int32 y = 24;
|
||||
CharacterState character_passive_state = 17; // 最新被动状态状态
|
||||
|
||||
double facing_direction = 25;
|
||||
int32 speed = 26;
|
||||
int32 view_range = 27;
|
||||
int32 x = 18;
|
||||
int32 y = 19;
|
||||
|
||||
int32 common_attack = 28;
|
||||
double common_attack_cd = 29;
|
||||
int32 common_attack_range = 30;
|
||||
double facing_direction = 20;
|
||||
int32 speed = 21;
|
||||
int32 view_range = 22;
|
||||
|
||||
double skill_attack_cd = 31;
|
||||
int32 common_attack = 23;
|
||||
int64 common_attack_cd = 24;
|
||||
int32 common_attack_range = 25;
|
||||
|
||||
int32 economy_depletion = 32; // 经济资源消耗
|
||||
int32 kill_score = 33; // 击杀得分
|
||||
int64 skill_attack_cd = 26;
|
||||
|
||||
int32 hp = 34;
|
||||
int32 economy_depletion = 27; // 经济资源消耗
|
||||
int32 kill_score = 28; // 击杀得分
|
||||
|
||||
int32 hp = 29;
|
||||
|
||||
// 其它装备都是直接消耗,不需要记录
|
||||
EquipmentType shield_equipment = 35; // NULL_EQUIPMENT_TYPE表示没有装备
|
||||
int32 shild = 36; // 护盾剩余
|
||||
EquipmentType shoes_equipment = 37;
|
||||
double shoes_time = 38;
|
||||
int32 shield_equipment = 30; // 护盾剩余值,只含装备
|
||||
// EquipmentType shoes_equipment = ; // 鞋子装备
|
||||
int32 shoes_equipment = 31;
|
||||
int64 shoes_equipment_time = 32;
|
||||
// EquipmentType purification_equipment = ;
|
||||
int64 purification_equipment_time = 33;
|
||||
// EquipmentType invisibility_equipment = ;
|
||||
// long invisibility_equipment_time = ;
|
||||
// EquipmentType berserk_equipment = ;
|
||||
// long berserk_equipment_time = ;
|
||||
|
||||
// 加成资源的Buff
|
||||
CharacterBuffType attack_buff = 39;
|
||||
double attack_buff_time = 40;
|
||||
CharacterBuffType defense_buff = 41;
|
||||
double defense_buff_time = 42;
|
||||
CharacterBuffType speed_buff = 43;
|
||||
double speed_buff_time = 44;
|
||||
CharacterBuffType vision_buff = 45;
|
||||
double vision_buff_time = 46;
|
||||
// CharacterBuffType attack_buff = 39;
|
||||
int64 attack_buff_time = 34;
|
||||
// CharacterBuffType speed_buff = 43;
|
||||
int64 speed_buff_time = 35;
|
||||
// CharacterBuffType vision_buff = 45;
|
||||
int64 vision_buff_time = 36;
|
||||
}
|
||||
|
||||
message MessageOfBarracks
|
||||
|
|
|
@ -61,7 +61,6 @@ enum CharacterState // 角色状态
|
|||
CONSTRUCTING = 5;
|
||||
MOVING = 6;
|
||||
|
||||
|
||||
BLIND = 7;
|
||||
KNOCKED_BACK = 8;
|
||||
STUNNED = 9;
|
||||
|
@ -69,8 +68,8 @@ enum CharacterState // 角色状态
|
|||
HEALING = 11;
|
||||
BERSERK = 12;
|
||||
BURNED = 13;
|
||||
HARM_CUT = 14;
|
||||
DECEASED = 15;
|
||||
// HARM_CUT = 14;
|
||||
DECEASED = 14;
|
||||
}
|
||||
|
||||
enum CharacterBuffType
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
# 接口一览
|
||||
```
|
||||
//通用接口
|
||||
//发送信息、接受信息,注意收消息是无消息则返回-1和空“string”
|
||||
virtual std::future<bool> SendTextMessage(int32_t toPlayerID, std::string) = 0;
|
||||
virtual std::future<bool> SendBinaryMessage(int32_t toPlayerID, std::string) = 0;
|
||||
[[nodiscard]] virtual bool HaveMessage() = 0;
|
||||
[[nodiscard]] virtual std::pair<int32_t, std::string> GetMessage() = 0;
|
||||
|
||||
//获取游戏目前所进行的帧数
|
||||
[[nodiscard]] virtual int32_t GetFrameCount() const = 0;
|
||||
|
||||
//等待下一帧
|
||||
virtual bool Wait() = 0;
|
||||
virtual std::future<bool> EndAllAction() = 0;
|
||||
[[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI8::Character>> GetCharacters() const = 0;
|
||||
[[nodiscard]] virtual std::vector<std::shared_ptr<const THUAI8::Character>> GetEnemyCharacters() const = 0;
|
||||
[[nodiscard]] virtual std::vector<std::vector<THUAI8::PlaceType>> GetFullMap() const = 0;
|
||||
[[nodiscard]] virtual std::shared_ptr<const THUAI8::GameInfo> GetGameInfo() const = 0;
|
||||
[[nodiscard]] virtual THUAI8::PlaceType GetPlaceType(int32_t cellX, int32_t cellY) const = 0;
|
||||
[[nodiscard]] virtual std::optional<THUAI8::EconomyResourceState> GetEnconomyResourceState(int32_t cellX, int32_t cellY) const = 0;
|
||||
[[nodiscard]] virtual std::optional<THUAI8::AdditionResourceState> GetAdditionResourceState(int32_t cellX, int32_t cellY) const = 0;
|
||||
[[nodiscard]] virtual std::optional<THUAI8::ConstructionState> GetConstructionState(int32_t cellX, int32_t cellY) const = 0;
|
||||
[[nodiscard]] virtual std::vector<int64_t> GetPlayerGUIDs() const = 0;
|
||||
[[nodiscard]] virtual int32_t GetEnergy() const = 0;
|
||||
[[nodiscard]] virtual int32_t GetScore() const = 0;
|
||||
|
||||
//控制角色进行移动
|
||||
virtual std::future<bool> Move(int32_t speed, int64_t timeInMilliseconds, double angleInRadian) = 0;
|
||||
//向特定方向移动
|
||||
virtual std::future<bool> MoveRight(int32_t speed, int64_t timeInMilliseconds) = 0;
|
||||
virtual std::future<bool> MoveUp(int32_t speed, int64_t timeInMilliseconds) = 0;
|
||||
virtual std::future<bool> MoveLeft(int32_t speed, int64_t timeInMilliseconds) = 0;
|
||||
virtual std::future<bool> MoveDown(int32_t speed, int64_t timeInMilliseconds) = 0;
|
||||
virtual std::future<bool> Skill_Attack(int64_t attackedPlayerID) = 0;
|
||||
virtual std::future<bool> Common_Attack(int64_t attackedPlayerID) = 0;
|
||||
virtual std::future<bool> Recover(int64_t recover) = 0;
|
||||
virtual std::future<bool> Harvest() = 0;
|
||||
virtual std::future<bool> Rebuild(THUAI8::ConstructionType constructionType) = 0;
|
||||
virtual std::future<bool> Construct(THUAI8::ConstructionType constructionType) = 0;
|
||||
virtual std::shared_ptr<const THUAI8::Character> GetSelfInfo() const = 0;
|
||||
virtual bool HaveView(int32_t targetX, int32_t targetY) const = 0;
|
||||
|
||||
[[nodiscard]] virtual std::shared_ptr<const THUAI8::Team> GetSelfInfo() const = 0;
|
||||
virtual std::future<bool> InstallEquipment(int32_t playerID, THUAI8::EquipmentType equipmenttype) = 0;
|
||||
virtual std::future<bool> Recycle(int32_t playerID) = 0;
|
||||
virtual std::future<bool> BuildCharacter(THUAI8::CharacterType CharacterType, int32_t birthIndex) = 0;
|
||||
|
||||
// 获取指定格子中心的坐标
|
||||
[[nodiscard]] static inline int32_t CellToGrid(int32_t cell) noexcept
|
||||
{
|
||||
return cell * numOfGridPerCell + numOfGridPerCell / 2;
|
||||
}
|
||||
|
||||
// 获取指定坐标点所位于的格子的 X 序号
|
||||
[[nodiscard]] static inline int32_t GridToCell(int32_t grid) noexcept
|
||||
{
|
||||
return grid / numOfGridPerCell;
|
||||
}
|
||||
|
||||
// 用于DEBUG的输出函数,选手仅在开启Debug模式的情况下可以使用
|
||||
|
||||
virtual void Print(std::string str) const = 0;
|
||||
virtual void PrintCharacter() const = 0;
|
||||
virtual void PrintTeam() const = 0;
|
||||
virtual void PrintSelfInfo() const = 0;
|
|
@ -1,4 +1,4 @@
|
|||
# THUAI8游戏选题(改稿)
|
||||
# THUAI8 西游真经劫
|
||||
|
||||
## 游戏背景
|
||||
|
||||
|
@ -199,7 +199,7 @@
|
|||
|
||||
|
||||
| 名称 | 功能 | 花费/破坏得分 | 血量 | 建造时间 |
|
||||
| :------: | :----------------------------------------------------------: | :-----------: | :--: | :------: |
|
||||
| :------: | :--------------------------------------------------------------------------------------------------: | :-----------: | :---: | :------: |
|
||||
| 兵营 | 角色出生点,开局自带一座兵营,至多可再修建两座兵营。同时半径1000范围内的己方角色可持续回血(10点/s) | 1w/6000 | 600 | 15s |
|
||||
| 农场 | 每座农场每秒产出100经济,至多可再修建4个农场。 | 8000/4000 | 400 | 10s |
|
||||
| 坑洞陷阱 | 修建后敌方不可见。当敌方角色触碰以陷阱为中心的九宫格时,受到每秒20点的伤害,持续5s,同时会暴露视野 | 1000 | / | 5s |
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 338 KiB |
|
@ -0,0 +1,262 @@
|
|||
// MapHelper.cs
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Media;
|
||||
using debug_interface.Models;
|
||||
using debug_interface.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace debug_interface.Controls
|
||||
{
|
||||
public static class MapHelper
|
||||
{
|
||||
private static Dictionary<int, Rectangle> cellRectangles = new Dictionary<int, Rectangle>();
|
||||
private static Grid? gridContainer;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化地图网格
|
||||
/// </summary>
|
||||
public static Grid CreateMapGrid(MapViewModel mapViewModel)
|
||||
{
|
||||
// 清空现有记录
|
||||
cellRectangles.Clear();
|
||||
|
||||
// 创建Grid容器,设置为50x50的网格
|
||||
var grid = new Grid();
|
||||
gridContainer = grid;
|
||||
|
||||
// 定义列
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
}
|
||||
|
||||
// 定义行
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
}
|
||||
|
||||
// 绘制单元格
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
for (int j = 0; j < 50; j++)
|
||||
{
|
||||
int index = i * 50 + j;
|
||||
|
||||
if (index < mapViewModel.MapCells.Count)
|
||||
{
|
||||
var cell = mapViewModel.MapCells[index];
|
||||
|
||||
// 创建矩形单元格
|
||||
var rectangle = new Rectangle
|
||||
{
|
||||
Fill = cell.DisplayColor,
|
||||
Margin = new Thickness(0),
|
||||
[Grid.RowProperty] = i,
|
||||
[Grid.ColumnProperty] = j
|
||||
};
|
||||
|
||||
// 为单元格添加文本(如果有)
|
||||
if (!string.IsNullOrEmpty(cell.DisplayText))
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = cell.DisplayText,
|
||||
FontSize = 6,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||
[Grid.RowProperty] = i,
|
||||
[Grid.ColumnProperty] = j,
|
||||
ZIndex = 1 // 确保文本在矩形上方
|
||||
};
|
||||
grid.Children.Add(textBlock);
|
||||
}
|
||||
|
||||
// 存储矩形引用以便后续更新
|
||||
cellRectangles[index] = rectangle;
|
||||
grid.Children.Add(rectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加网格线(在单元格上方)
|
||||
AddGridLines(grid);
|
||||
|
||||
return grid;
|
||||
}
|
||||
/// <summary>
|
||||
/// 初始化地图网格
|
||||
/// </summary>
|
||||
public static void InitializeMapGrid(Grid grid, MapViewModel mapViewModel)
|
||||
{
|
||||
// 清空所有现有内容
|
||||
grid.Children.Clear();
|
||||
grid.RowDefinitions.Clear();
|
||||
grid.ColumnDefinitions.Clear();
|
||||
cellRectangles.Clear();
|
||||
gridContainer = grid;
|
||||
|
||||
// 定义列
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
}
|
||||
|
||||
// 定义行
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
}
|
||||
|
||||
// 绘制单元格
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
for (int j = 0; j < 50; j++)
|
||||
{
|
||||
int index = i * 50 + j;
|
||||
|
||||
if (index < mapViewModel.MapCells.Count)
|
||||
{
|
||||
var cell = mapViewModel.MapCells[index];
|
||||
|
||||
// 创建矩形单元格
|
||||
var rectangle = new Rectangle
|
||||
{
|
||||
Fill = cell.DisplayColor,
|
||||
Margin = new Thickness(0),
|
||||
[Grid.RowProperty] = i,
|
||||
[Grid.ColumnProperty] = j
|
||||
};
|
||||
|
||||
// 为单元格添加文本(如果有)
|
||||
if (!string.IsNullOrEmpty(cell.DisplayText))
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = cell.DisplayText,
|
||||
FontSize = 6,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||
[Grid.RowProperty] = i,
|
||||
[Grid.ColumnProperty] = j,
|
||||
ZIndex = 1 // 确保文本在矩形上方
|
||||
};
|
||||
grid.Children.Add(textBlock);
|
||||
}
|
||||
|
||||
// 存储矩形引用以便后续更新
|
||||
cellRectangles[index] = rectangle;
|
||||
grid.Children.Add(rectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加网格线(在单元格上方)
|
||||
AddGridLines(grid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加网格线
|
||||
/// </summary>
|
||||
private static void AddGridLines(Grid grid)
|
||||
{
|
||||
// 添加水平网格线
|
||||
for (int i = 0; i <= 50; i++)
|
||||
{
|
||||
var line = new Line
|
||||
{
|
||||
StartPoint = new Point(0, 0),
|
||||
EndPoint = new Point(1, 0),
|
||||
Stroke = Brushes.Gray,
|
||||
StrokeThickness = 1,
|
||||
Stretch = Stretch.Fill,
|
||||
ZIndex = 2 // 确保网格线在最上层
|
||||
};
|
||||
|
||||
if (i < 50) // 最后一行不需要添加
|
||||
{
|
||||
line.SetValue(Grid.RowProperty, i);
|
||||
line.SetValue(Grid.ColumnSpanProperty, 50);
|
||||
line.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Bottom;
|
||||
grid.Children.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加垂直网格线
|
||||
for (int j = 0; j <= 50; j++)
|
||||
{
|
||||
var line = new Line
|
||||
{
|
||||
StartPoint = new Point(0, 0),
|
||||
EndPoint = new Point(0, 1),
|
||||
Stroke = Brushes.Gray,
|
||||
StrokeThickness = 1,
|
||||
Stretch = Stretch.Fill,
|
||||
ZIndex = 2 // 确保网格线在最上层
|
||||
};
|
||||
|
||||
if (j < 50) // 最后一列不需要添加
|
||||
{
|
||||
line.SetValue(Grid.ColumnProperty, j);
|
||||
line.SetValue(Grid.RowSpanProperty, 50);
|
||||
line.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right;
|
||||
grid.Children.Add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新单元格颜色
|
||||
/// </summary>
|
||||
public static void UpdateCellColor(int x, int y, IBrush color)
|
||||
{
|
||||
int index = x * 50 + y;
|
||||
if (cellRectangles.ContainsKey(index))
|
||||
{
|
||||
cellRectangles[index].Fill = color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新单元格文本
|
||||
/// </summary>
|
||||
public static void UpdateCellText(int x, int y, string text)
|
||||
{
|
||||
if (gridContainer == null) return;
|
||||
|
||||
// 查找对应位置的TextBlock并更新
|
||||
foreach (var child in gridContainer.Children)
|
||||
{
|
||||
if (child is TextBlock textBlock &&
|
||||
Grid.GetRow(textBlock) == x &&
|
||||
Grid.GetColumn(textBlock) == y)
|
||||
{
|
||||
textBlock.Text = text;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到现有的TextBlock,创建新的
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = text,
|
||||
FontSize = 6,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||
[Grid.RowProperty] = x,
|
||||
[Grid.ColumnProperty] = y,
|
||||
ZIndex = 1
|
||||
};
|
||||
gridContainer.Children.Add(textBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,16 +24,7 @@ namespace debug_interface.ViewModels
|
|||
// 可能值: "空置","开采","攻击","释放技能","建造","移动"
|
||||
[ObservableProperty]
|
||||
private string activeState = "空置";
|
||||
//public string ActiveState
|
||||
//{
|
||||
// get => activeState;
|
||||
// set
|
||||
// {
|
||||
// if (value == activeState) return;
|
||||
// activeState = value;
|
||||
// OnPropertyChanged(nameof(DisplayStates));
|
||||
// }
|
||||
//}
|
||||
|
||||
// 被动状态(可叠加)
|
||||
// 可能值: "致盲","击退","定身","隐身" 等,可由服务器控制增减
|
||||
public ObservableCollection<string> PassiveStates { get; } = new ObservableCollection<string>();
|
||||
|
|
|
@ -10,30 +10,10 @@
|
|||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<!-- Map Cells Grid - no binding in XAML -->
|
||||
<ItemsControl Name="MapItemsControl">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="50" Rows="50" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type models:MapCell}">
|
||||
<Border Background="{Binding DisplayColor}"
|
||||
BorderThickness="0.4"
|
||||
Width="15"
|
||||
Height="15"
|
||||
BorderBrush="Gray">
|
||||
<TextBlock Text="{Binding DisplayText}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="6"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<!-- 使用空的Grid,将在代码中填充 -->
|
||||
<Grid Name="MapGrid" />
|
||||
|
||||
<!-- Character Positions Canvas - with transparent background -->
|
||||
<Canvas Name="CharacterCanvas" Width="750" Height="750" Background="Transparent" />
|
||||
<!-- Character Positions Canvas -->
|
||||
<Canvas Name="CharacterCanvas" Background="Transparent" />
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -4,18 +4,22 @@ using Avalonia.Controls;
|
|||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.VisualTree;
|
||||
using debug_interface.Controls;
|
||||
using debug_interface.Models;
|
||||
using debug_interface.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
|
||||
namespace debug_interface.Views
|
||||
{
|
||||
public partial class MapView : UserControl
|
||||
{
|
||||
|
||||
private Canvas? characterCanvas;
|
||||
private ItemsControl? mapItemsControl;
|
||||
private Dictionary<string, Ellipse> characterEllipses = new Dictionary<string, Ellipse>();
|
||||
private Grid? mapGrid;
|
||||
private Dictionary<string, Control> characterElements = new Dictionary<string, Control>();
|
||||
private MainWindowViewModel? viewModel;
|
||||
|
||||
public MapView()
|
||||
|
@ -26,10 +30,9 @@ namespace debug_interface.Views
|
|||
this.DataContextChanged += MapView_DataContextChanged;
|
||||
}
|
||||
|
||||
|
||||
private void MapView_DataContextChanged(object? sender, EventArgs e)
|
||||
{
|
||||
// When the data context changes, get the parent window's DataContext
|
||||
// which should be the MainWindowViewModel
|
||||
var mainWindow = this.FindAncestorOfType<MainWindow>();
|
||||
if (mainWindow != null && mainWindow.DataContext is MainWindowViewModel vm)
|
||||
{
|
||||
|
@ -40,9 +43,8 @@ namespace debug_interface.Views
|
|||
private void MapView_AttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
characterCanvas = this.FindControl<Canvas>("CharacterCanvas");
|
||||
mapItemsControl = this.FindControl<ItemsControl>("MapItemsControl");
|
||||
mapGrid = this.FindControl<Grid>("MapGrid"); // 修改这里,对应XAML
|
||||
|
||||
// Get the MainWindowViewModel from the parent MainWindow
|
||||
var mainWindow = this.FindAncestorOfType<MainWindow>();
|
||||
if (mainWindow != null && mainWindow.DataContext is MainWindowViewModel vm)
|
||||
{
|
||||
|
@ -50,9 +52,9 @@ namespace debug_interface.Views
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void SetupViewModel(MainWindowViewModel vm)
|
||||
{
|
||||
// Clean up previous handlers if any
|
||||
if (viewModel != null)
|
||||
{
|
||||
viewModel.RedTeamCharacters.CollectionChanged -= RedTeamCharacters_CollectionChanged;
|
||||
|
@ -61,32 +63,52 @@ namespace debug_interface.Views
|
|||
|
||||
viewModel = vm;
|
||||
|
||||
// Set the ItemsSource programmatically
|
||||
if (mapItemsControl != null && viewModel.MapVM != null)
|
||||
// 初始化地图网格
|
||||
if (mapGrid != null && viewModel.MapVM != null)
|
||||
{
|
||||
mapItemsControl.ItemsSource = viewModel.MapVM.MapCells;
|
||||
// 直接使用现有的mapGrid
|
||||
MapHelper.InitializeMapGrid(mapGrid, viewModel.MapVM);
|
||||
|
||||
}
|
||||
|
||||
// Listen for changes to character collections
|
||||
// 监听角色集合变化
|
||||
viewModel.RedTeamCharacters.CollectionChanged += RedTeamCharacters_CollectionChanged;
|
||||
viewModel.BlueTeamCharacters.CollectionChanged += BlueTeamCharacters_CollectionChanged;
|
||||
|
||||
// Initialize existing characters
|
||||
// 初始化角色
|
||||
RefreshCharacters();
|
||||
|
||||
// Initialize random positions if not set
|
||||
InitializeRandomPositions();
|
||||
|
||||
// 监听地图单元格变化(如果模型提供了这种能力)
|
||||
if (viewModel.MapVM != null)
|
||||
{
|
||||
// 如果MapCell类型实现了INotifyPropertyChanged,您可以在这里监听属性变化
|
||||
foreach (var cell in viewModel.MapVM.MapCells)
|
||||
{
|
||||
cell.PropertyChanged += (s, e) => {
|
||||
if (s is MapCell mapCell)
|
||||
{
|
||||
if (e.PropertyName == nameof(MapCell.DisplayColor))
|
||||
{
|
||||
MapHelper.UpdateCellColor(mapCell.CellX, mapCell.CellY, mapCell.DisplayColor);
|
||||
}
|
||||
else if (e.PropertyName == nameof(MapCell.DisplayText))
|
||||
{
|
||||
MapHelper.UpdateCellText(mapCell.CellX, mapCell.CellY, mapCell.DisplayText);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshCharacters()
|
||||
{
|
||||
if (characterCanvas == null || viewModel == null) return;
|
||||
|
||||
// Clear existing characters
|
||||
characterCanvas.Children.Clear();
|
||||
characterEllipses.Clear();
|
||||
characterElements.Clear();
|
||||
|
||||
// Re-add all characters
|
||||
InitializeCharacters(viewModel.RedTeamCharacters, Colors.Red);
|
||||
InitializeCharacters(viewModel.BlueTeamCharacters, Colors.Blue);
|
||||
}
|
||||
|
@ -117,6 +139,7 @@ namespace debug_interface.Views
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void InitializeCharacters<T>(System.Collections.ObjectModel.ObservableCollection<T> characters, Color color) where T : CharacterViewModel
|
||||
{
|
||||
if (characterCanvas == null) return;
|
||||
|
@ -126,44 +149,72 @@ namespace debug_interface.Views
|
|||
var character = characters[i];
|
||||
var id = color == Colors.Red ? $"red_{i}" : $"blue_{i}";
|
||||
|
||||
var ellipse = new Ellipse
|
||||
// 创建一个Grid作为容器,包含边框和文本/图标
|
||||
var grid = new Grid
|
||||
{
|
||||
Width = 12,
|
||||
Height = 12,
|
||||
Fill = new SolidColorBrush(color),
|
||||
Stroke = new SolidColorBrush(Colors.White),
|
||||
StrokeThickness = 1,
|
||||
Width = 15,
|
||||
Height = 15,
|
||||
};
|
||||
|
||||
// 创建带颜色边框的圆形
|
||||
var borderellipse = new Ellipse
|
||||
{
|
||||
Width = 15,
|
||||
Height = 15,
|
||||
Fill = new SolidColorBrush(Colors.White), // 白色背景
|
||||
Stroke = new SolidColorBrush(color), // 队伍颜色边框
|
||||
StrokeThickness = 2,
|
||||
Tag = character.Name,
|
||||
};
|
||||
|
||||
// Set tooltip
|
||||
ToolTip.SetTip(ellipse, character.Name);
|
||||
grid.Children.Add(borderellipse);
|
||||
|
||||
// Set initial position
|
||||
Canvas.SetLeft(ellipse, character.PosY * 15);
|
||||
Canvas.SetTop(ellipse, character.PosX * 15);
|
||||
// ===== 选项1: 显示数字编号 =====
|
||||
// 如果不需要数字编号,注释掉下面这段代码
|
||||
//var textBlock = new TextBlock
|
||||
//{
|
||||
// Text = (i + 1).ToString(), // 使用编号(从1开始)
|
||||
// HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||
// VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||
// FontSize = 8,
|
||||
// Foreground = new SolidColorBrush(color), // 文本颜色与队伍颜色一致
|
||||
// FontWeight = FontWeight.Bold,
|
||||
//};
|
||||
//grid.Children.Add(textBlock);
|
||||
|
||||
characterCanvas.Children.Add(ellipse);
|
||||
characterEllipses[id] = ellipse;
|
||||
// 设置提示信息
|
||||
ToolTip.SetTip(grid, character.Name);
|
||||
|
||||
// Set up property changed handlers
|
||||
// 设置初始位置
|
||||
Canvas.SetLeft(grid, character.PosY * 15);
|
||||
Canvas.SetTop(grid, character.PosX * 15);
|
||||
|
||||
characterCanvas.Children.Add(grid);
|
||||
|
||||
// 存储Grid到字典中
|
||||
characterElements[id] = grid;
|
||||
|
||||
// 设置属性更改处理器
|
||||
character.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(CharacterViewModel.PosX) || e.PropertyName == nameof(CharacterViewModel.PosY))
|
||||
{
|
||||
UpdateCharacterPosition(ellipse, character.PosX, character.PosY);
|
||||
// 更新Grid的位置
|
||||
UpdateCharacterPosition(grid, character.PosX, character.PosY);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCharacterPosition(Ellipse ellipse, int x, int y)
|
||||
// 修改位置更新方法,接受任何UIElement
|
||||
private void UpdateCharacterPosition(Control element, int x, int y)
|
||||
{
|
||||
// Convert grid position to pixels
|
||||
Canvas.SetLeft(ellipse, y * 15);
|
||||
Canvas.SetTop(ellipse, x * 15);
|
||||
// 转换网格位置为像素
|
||||
Canvas.SetLeft(element, y * 15);
|
||||
Canvas.SetTop(element, x * 15);
|
||||
}
|
||||
|
||||
|
||||
private void RedTeamCharacters_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
// When collection changes, refresh all characters for simplicity
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
<AssemblyName>debug_interface</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -28,11 +29,6 @@
|
|||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\eesast_logo_32x32.png" />
|
||||
<None Remove="Assets\nailong.jpg" />
|
||||
<None Remove="Assets\tangseng.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.5" />
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.30.2" />
|
||||
<PackageReference Include="Grpc" Version="2.46.6" />
|
||||
<PackageReference Include="Grpc.Core" Version="2.46.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\dependency\proto\Proto.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,44 @@
|
|||
using Grpc.Core;
|
||||
using Protobuf;
|
||||
|
||||
namespace ClientTest
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static Task Main(string[] args)
|
||||
{
|
||||
Thread.Sleep(3000);
|
||||
Channel channel = new("127.0.0.1:8888", ChannelCredentials.Insecure);
|
||||
var client = new AvailableService.AvailableServiceClient(channel);
|
||||
CharacterMsg playerInfo = new()
|
||||
{
|
||||
CharacterId = 0,
|
||||
TeamId = 0,
|
||||
CharacterType = CharacterType.TangSeng
|
||||
};
|
||||
var call = client.AddCharacter(playerInfo);
|
||||
MoveMsg moveMsg = new()
|
||||
{
|
||||
CharacterId = 0,
|
||||
TeamId = 0,
|
||||
TimeInMilliseconds = 100,
|
||||
Angle = 0
|
||||
};
|
||||
int tot = 0;
|
||||
/*while (call.ResponseStream.MoveNext().Result)
|
||||
{
|
||||
var currentGameInfo = call.ResponseStream.Current;
|
||||
if (currentGameInfo.GameState == GameState.GameStart) break;
|
||||
}*/
|
||||
while (true)
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
MoveRes boolRes = client.Move(moveMsg);
|
||||
//if (boolRes.ActSuccess == false) break;
|
||||
tot++;
|
||||
if (tot % 10 == 0) moveMsg.Angle += 1;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ public class Character : Movable, ICharacter
|
|||
public InVariableRange<long> AttackPower { get; }
|
||||
public InVariableRange<long> AttackSize { get; }
|
||||
public InVariableRange<long> Shield { get; }
|
||||
public InVariableRange<long> NiuShield { get; }
|
||||
public InVariableRange<long> Shoes { get; }//移速加成(注意是加成值,实际移速为基础移速+移速加成)
|
||||
public CharacterType CharacterType { get; }
|
||||
public bool trapped { get; set; } = false;
|
||||
|
@ -30,6 +31,7 @@ public class Character : Movable, ICharacter
|
|||
public bool blind { get; set; } = false;
|
||||
public double HarmCut = 0.0;//伤害减免,该值范围为0-1,为比例减伤。
|
||||
public double ATKFrequency = 1.0;//攻击频率,即每秒攻击次数。
|
||||
public long LastAttackTime = long.MaxValue;
|
||||
public long TrapTime = long.MaxValue;
|
||||
public long CageTime = long.MaxValue;
|
||||
public long BurnedTime = long.MaxValue;
|
||||
|
@ -42,10 +44,15 @@ public class Character : Movable, ICharacter
|
|||
public long QuickStepTime = long.MaxValue;
|
||||
public int CrazyManNum = 0;
|
||||
public int EconomyDepletion = 0;
|
||||
public bool IsShield = false;
|
||||
public bool CanSeeAll = false;//视野之灵buff生效时为true
|
||||
public long WideViewTime = long.MaxValue;//视野之灵计时器
|
||||
public bool Purified = false;//净化药水效果,该效果下免疫控制
|
||||
public long PurifiedTime = long.MaxValue;
|
||||
public long ShoesTime = long.MaxValue;//鞋子buff计时器
|
||||
public bool IsShoes = false;
|
||||
public long BerserkTime = long.MaxValue;//狂暴buff计时器
|
||||
public bool IsBerserk = false;
|
||||
public void StartSkillCD()
|
||||
{
|
||||
skillCD = Environment.TickCount64;
|
||||
|
@ -244,6 +251,7 @@ public class Character : Movable, ICharacter
|
|||
ViewRange = Occupation.ViewRange;
|
||||
Shoes = new(0);
|
||||
Shield = new(0);
|
||||
NiuShield = new(0);
|
||||
AttackSize = new(Occupation.BaseAttackSize);
|
||||
AttackPower = new(Occupation.AttackPower);
|
||||
MoneyPool = pool;
|
||||
|
@ -280,24 +288,45 @@ public class Character : Movable, ICharacter
|
|||
}
|
||||
case EquipmentType.SMALL_SHIELD:
|
||||
{
|
||||
if (IsShield)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Shield.AddPositiveV(GameData.Shield1);
|
||||
SubMoney(EquipmentFactory.FindCost(equiptype));
|
||||
IsShield = true;
|
||||
return true;
|
||||
}
|
||||
case EquipmentType.MEDIUM_SHIELD:
|
||||
{
|
||||
if (IsShield)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Shield.AddPositiveV(GameData.Shield2);
|
||||
SubMoney(EquipmentFactory.FindCost(equiptype));
|
||||
IsShield = true;
|
||||
return true;
|
||||
}
|
||||
case EquipmentType.LARGE_SHIELD:
|
||||
{
|
||||
if (IsShield)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Shield.AddPositiveV(GameData.Shield3);
|
||||
SubMoney(EquipmentFactory.FindCost(equiptype));
|
||||
IsShield = true;
|
||||
return true;
|
||||
}
|
||||
case EquipmentType.SPEEDBOOTS:
|
||||
{
|
||||
if (IsShoes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IsShoes = true;
|
||||
ShoesTime = Environment.TickCount64;
|
||||
Shoes.AddPositiveV(GameData.ShoesSpeed);
|
||||
SubMoney(EquipmentFactory.FindCost(equiptype));
|
||||
return true;
|
||||
|
@ -305,11 +334,18 @@ public class Character : Movable, ICharacter
|
|||
case EquipmentType.INVISIBILITY_POTION:
|
||||
{
|
||||
SetCharacterState(CharacterState1, CharacterState.INVISIBLE);//此处缺少时间限制
|
||||
visible = false;
|
||||
SubMoney(EquipmentFactory.FindCost(equiptype));
|
||||
return true;
|
||||
}
|
||||
case EquipmentType.BERSERK_POTION:
|
||||
{
|
||||
if (IsBerserk)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IsBerserk = true;
|
||||
BerserkTime = Environment.TickCount64;
|
||||
SetCharacterState(CharacterState1, CharacterState.BERSERK);//此处缺少时间限制
|
||||
AttackPower.AddPositiveV((long)(0.2 * AttackPower.GetValue()));
|
||||
ATKFrequency = GameData.CrazyATKFreq;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using GameClass.GameObj.Areas;
|
||||
using GameClass.GameObj.Equipments;
|
||||
using GameClass.MapGenerator;
|
||||
using Preparation.Interface;
|
||||
using Preparation.Utility;
|
||||
|
@ -21,6 +22,7 @@ namespace GameClass.GameObj.Map
|
|||
public PlaceType[,] ProtoGameMap => protoGameMap;
|
||||
|
||||
private readonly MyTimer timer = new();
|
||||
public List<Home> Homes { get; }
|
||||
public IMyTimer Timer => timer;
|
||||
private readonly long currentHomeNum = 0;
|
||||
public bool TeamExists(long teamID)
|
||||
|
@ -242,9 +244,16 @@ namespace GameClass.GameObj.Map
|
|||
case PlaceType.SPACE:
|
||||
Add(new Space(GameData.GetCellCenterPos(i, j)));
|
||||
break;
|
||||
case PlaceType.HOME:
|
||||
if (i < 25)
|
||||
Add(new Home(GameData.GetCellCenterPos(i, j), currentHomeNum++, 1));
|
||||
else
|
||||
Add(new Home(GameData.GetCellCenterPos(i, j), currentHomeNum++, 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Homes = GameObjDict[GameObjType.HOME].Cast<Home>()?.ToNewList()!;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class BaiLongma : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.BaiLongmaHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.BaiLongmacost;
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class HongHaier : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.HongHaierHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.HongHaiercost;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class JiuLing : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.JiuLingHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = 0;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class Monkid : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.MonkidHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.Monkidcost;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class NiuMowang : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.NiuMowangHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.NiuMowangcost;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class Pawn : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.PawnHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.Pawncost;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class ShaWujing : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.ShaWujingHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.ShaWujingcost;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class SunWukong : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.SunWukongHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.SunWukongcost;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class TangSeng : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.TangSengHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = 0;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class TieShan : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.TieShanHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.TieShancost;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class ZhiZhujing : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.ZhiZhujingHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.ZhiZhujingcost;
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GameClass.GameObj.Occupations
|
|||
{
|
||||
public class ZhuBajie : IOccupation
|
||||
{
|
||||
public int MoveSpeed { get; } = GameData.NumOfStepPerSecond;
|
||||
public int MoveSpeed { get; } = GameData.BaseCharacterSpeed;
|
||||
public int MaxHp { get; } = GameData.ZhuBajieHP;
|
||||
public int ViewRange { get; } = GameData.Viewrange;
|
||||
public int Cost { get; } = GameData.ZhuBajiecost;
|
||||
|
|
|
@ -132,7 +132,7 @@ namespace GameEngine
|
|||
return true;
|
||||
}
|
||||
|
||||
public void MoveObj(IMovable obj, int moveTime, double direction, long stateNum)
|
||||
public void MoveObj(IMovable obj, int moveTime, double direction, long stateNum, long Shoes = 0)
|
||||
{
|
||||
GameEngineLogging.logger.ConsoleLogDebug(
|
||||
Logger.ObjInfo(obj)
|
||||
|
@ -143,7 +143,6 @@ namespace GameEngine
|
|||
if (!obj.IsAvailableForMove) { EndMove(obj); return; }
|
||||
obj.IsMoving.SetROri(true);
|
||||
}
|
||||
|
||||
new Thread
|
||||
(
|
||||
() =>
|
||||
|
@ -237,7 +236,7 @@ namespace GameEngine
|
|||
do
|
||||
{
|
||||
flag = false;
|
||||
moveVecLength = (double)deltaLen + leftTime * obj.MoveSpeed / GameData.NumOfPosGridPerCell;
|
||||
moveVecLength = (double)deltaLen + leftTime * (obj.MoveSpeed + Shoes) / GameData.NumOfPosGridPerCell;
|
||||
res = new XY(direction, moveVecLength);
|
||||
if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
|
||||
{
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace Gaming
|
|||
characterToMove.ThreadNum.Release();
|
||||
return;
|
||||
}
|
||||
moveEngine.MoveObj(characterToMove, moveTimeInMilliseconds, moveDirection, characterToMove.StateNum);
|
||||
moveEngine.MoveObj(characterToMove, moveTimeInMilliseconds, moveDirection, characterToMove.StateNum, characterToMove.Shoes);
|
||||
Thread.Sleep(moveTimeInMilliseconds);
|
||||
//characterToMove.ResetCharacterState(stateNum);
|
||||
}
|
||||
|
@ -61,6 +61,34 @@ namespace Gaming
|
|||
{ IsBackground = true }.Start();
|
||||
return true;
|
||||
}
|
||||
public bool KnockBackCharacter(Character characterToMove, double moveDirection)
|
||||
{
|
||||
long stateNum = characterToMove.SetCharacterState(characterToMove.CharacterState1, CharacterState.KNOCKED_BACK);
|
||||
CharacterState tempState = characterToMove.CharacterState2;
|
||||
if (stateNum == -1)
|
||||
{
|
||||
ActionManagerLogging.logger.ConsoleLogDebug("Character can not be knocked back");
|
||||
return false;
|
||||
}
|
||||
new Thread
|
||||
(
|
||||
() =>
|
||||
{
|
||||
characterToMove.ThreadNum.WaitOne();
|
||||
if (!characterToMove.StartThread(stateNum))
|
||||
{
|
||||
characterToMove.ThreadNum.Release();
|
||||
return;
|
||||
}
|
||||
moveEngine.MoveObj(characterToMove, GameData.KnockedBackTime, moveDirection, characterToMove.StateNum, GameData.KnockedBackSpeed);
|
||||
Thread.Sleep(GameData.KnockedBackTime);
|
||||
characterToMove.SetCharacterState(characterToMove.CharacterState1, tempState);
|
||||
//characterToMove.ResetCharacterState(stateNum);
|
||||
}
|
||||
)
|
||||
{ IsBackground = true }.Start();
|
||||
return true;
|
||||
}
|
||||
public static bool Stop(Character character)
|
||||
{
|
||||
lock (character.ActionLock)
|
||||
|
|
|
@ -58,7 +58,11 @@ namespace Gaming
|
|||
{
|
||||
return false;
|
||||
}
|
||||
long nowtime = Environment.TickCount64;
|
||||
if (nowtime - character.LastAttackTime < 1 / (character.ATKFrequency * 1000))
|
||||
return false;
|
||||
characterManager.BeAttacked(gameobj, character);
|
||||
character.LastAttackTime = nowtime;
|
||||
if (character.CharacterState2 == CharacterState.INVISIBLE || character.visible == false)
|
||||
{
|
||||
character.visible = true;
|
||||
|
@ -80,7 +84,11 @@ namespace Gaming
|
|||
{
|
||||
return false;
|
||||
}
|
||||
long nowtime = Environment.TickCount64;
|
||||
if (nowtime - character.LastAttackTime < 1 / (character.ATKFrequency * 1000))
|
||||
return false;
|
||||
ARManager.BeAttacked(gameobj, character);
|
||||
character.LastAttackTime = nowtime;
|
||||
if (character.CharacterState2 == CharacterState.INVISIBLE)
|
||||
character.SetCharacterState(character.CharacterState1, CharacterState.NULL_CHARACTER_STATE);//破隐
|
||||
return true;
|
||||
|
@ -99,7 +107,11 @@ namespace Gaming
|
|||
{
|
||||
return false;
|
||||
}
|
||||
long nowtime = Environment.TickCount64;
|
||||
if (nowtime - character.LastAttackTime < 1 / (character.ATKFrequency * 1000))
|
||||
return false;
|
||||
gameobj.BeAttacked(character);
|
||||
character.LastAttackTime = nowtime;
|
||||
if (character.CharacterState2 == CharacterState.INVISIBLE)
|
||||
character.SetCharacterState(character.CharacterState1, CharacterState.NULL_CHARACTER_STATE);//破隐
|
||||
return true;
|
||||
|
|
|
@ -44,14 +44,27 @@ namespace Gaming
|
|||
return;
|
||||
}
|
||||
long subHP = (long)(obj.AttackPower * (1 - character.HarmCut));
|
||||
if (character.Shield > 0)
|
||||
/*if (character.Shield > 0)
|
||||
{
|
||||
character.Shield.SubPositiveV(subHP);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.HP.SubPositiveV(subHP);
|
||||
}*/
|
||||
character.NiuShield.SubPositiveV(subHP);
|
||||
if (character.NiuShield > subHP)
|
||||
{
|
||||
return;
|
||||
}
|
||||
subHP -= character.NiuShield;
|
||||
character.Shield.SubPositiveV(subHP);
|
||||
if (character.Shield > subHP)
|
||||
{
|
||||
return;
|
||||
}
|
||||
subHP -= character.Shield;
|
||||
character.HP.SubPositiveV(subHP);
|
||||
if (character.HP == 0)
|
||||
{
|
||||
long score = 0;
|
||||
|
@ -67,7 +80,6 @@ namespace Gaming
|
|||
Remove(character);
|
||||
}
|
||||
}
|
||||
|
||||
public void BeAttacked(Character character, long AP)//此部分适用于中立资源攻击及技能攻击
|
||||
{
|
||||
long subHP = (long)(AP * (1 - character.HarmCut));
|
||||
|
@ -276,6 +288,34 @@ namespace Gaming
|
|||
|
||||
}
|
||||
}
|
||||
public void CheckBerkserk(Character character)
|
||||
{
|
||||
long nowtime = Environment.TickCount64;
|
||||
if (character.IsBerserk)
|
||||
{
|
||||
if (nowtime - character.BerserkTime >= GameData.CrazyTime)
|
||||
{
|
||||
character.AttackPower.SetRNow(character.Occupation.AttackPower);
|
||||
character.Shoes.SubPositiveV(GameData.CrazySpeed);
|
||||
character.ATKFrequency = GameData.ATKFreq;
|
||||
character.BerserkTime = long.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void CheckShoes(Character character)
|
||||
{
|
||||
long nowtime = Environment.TickCount64;
|
||||
if (character.IsShoes)
|
||||
{
|
||||
if (nowtime - character.ShoesTime >= GameData.ShoesTime)
|
||||
{
|
||||
character.Shoes.SubPositiveV(GameData.ShoesSpeed);
|
||||
character.ShoesTime = long.MaxValue;
|
||||
character.IsShoes = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ namespace Gaming
|
|||
private readonly Map gameMap;
|
||||
public Map GameMap => gameMap;
|
||||
private readonly Random random = new();
|
||||
public long AddPlayer(PlayerInitInfo playerInitInfo)
|
||||
public long AddCharacter(PlayerInitInfo playerInitInfo)
|
||||
{
|
||||
if (teamList[(int)playerInitInfo.teamID].CharacterNum >= GameData.CharacterTotalNumMax)
|
||||
{
|
||||
|
@ -341,9 +341,9 @@ namespace Gaming
|
|||
gameMap = new(mapResource);
|
||||
characterManager = new(this, gameMap);
|
||||
ARManager = new(this, gameMap, characterManager);
|
||||
skillCastManager = new(this, gameMap, characterManager, ARManager);
|
||||
actionManager = new(this, gameMap, characterManager);
|
||||
attackManager = new(this, gameMap, characterManager);
|
||||
skillCastManager = new(this, gameMap, characterManager, ARManager, actionManager);
|
||||
teamList = [];
|
||||
gameMap.GameObjDict[GameObjType.HOME].Cast<GameObj>()?.ForEach(
|
||||
delegate (GameObj gameObj)
|
||||
|
@ -380,7 +380,7 @@ namespace Gaming
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public bool Attack(long teamID, long characterID, double angle, long ATKteamID, long ATKcharacterID)
|
||||
public bool Attack(long teamID, long characterID, long ATKteamID, long ATKcharacterID)
|
||||
{
|
||||
if (!gameMap.Timer.IsGaming)
|
||||
return false;
|
||||
|
|
|
@ -22,12 +22,14 @@ namespace Gaming
|
|||
private readonly CharacterManager characterManager;
|
||||
private readonly MoveEngine moveEngine;
|
||||
private readonly A_ResourceManager ARManager;
|
||||
public SkillCastManager(Game game, Map gameMap, CharacterManager characterManager, A_ResourceManager a_ResourceManager)
|
||||
private readonly ActionManager actionManager;
|
||||
public SkillCastManager(Game game, Map gameMap, CharacterManager characterManager, A_ResourceManager a_ResourceManager, ActionManager actionManager)
|
||||
{
|
||||
this.game = game;
|
||||
this.gameMap = gameMap;
|
||||
this.characterManager = characterManager;
|
||||
this.ARManager = a_ResourceManager;
|
||||
this.actionManager = actionManager;
|
||||
moveEngine = new(
|
||||
gameMap: gameMap,
|
||||
OnCollision: (obj, collisionObj, moveVec) =>
|
||||
|
@ -202,7 +204,18 @@ namespace Gaming
|
|||
if (ObjBeingShot.Purified == true)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
ObjBeingShot.SetCharacterState(ObjBeingShot.CharacterState1, CharacterState.KNOCKED_BACK);
|
||||
double angleToBeKnockedBack;
|
||||
double tantheta = (ObjBeingShot.Position.y - character.Position.y) / (ObjBeingShot.Position.x - character.Position.x);
|
||||
if ((ObjBeingShot.Position.x - character.Position.x) > 0)
|
||||
angleToBeKnockedBack = Math.Atan(tantheta);
|
||||
else if ((ObjBeingShot.Position.y - character.Position.y) > 0)
|
||||
angleToBeKnockedBack = Math.PI - Math.Atan(tantheta);
|
||||
else
|
||||
angleToBeKnockedBack = -Math.PI - Math.Atan(tantheta);
|
||||
actionManager.KnockBackCharacter(ObjBeingShot, angleToBeKnockedBack);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
|
|
|
@ -4,12 +4,15 @@ namespace Preparation.Utility
|
|||
{
|
||||
public static class GameData
|
||||
{
|
||||
public const int NumOfStepPerSecond = 2500; // 每秒行走基础步数.由于移速buff的存在,角色的具体移动速度会发生变化,相应代码需调整
|
||||
public const int NumOfStepPerSecond = 100; // 每秒行走步数
|
||||
public const int BaseCharacterSpeed = 2500; // 角色基础移动速度
|
||||
public const int FrameDuration = 50; // 每帧时长
|
||||
public const int CheckInterval = 10; // 检查间隔
|
||||
public const uint GameDurationInSecond = 60 * 10; // 游戏时长
|
||||
public const int LimitOfStopAndMove = 15; // 停止和移动的最大间隔
|
||||
public const int ProduceSpeedPerSecond = 200; // 每秒生产值
|
||||
public const int KnockedBackTime = 50;
|
||||
public const int KnockedBackSpeed = 1500; // 击退速度(额外速度,需加上基础移速)
|
||||
|
||||
public const int TolerancesLength = 3;
|
||||
public const int AdjustLength = 3;
|
||||
|
@ -192,6 +195,7 @@ namespace Preparation.Utility
|
|||
|
||||
public const int ShoesCost = 1500;
|
||||
public const int ShoesSpeed = 500;
|
||||
public const int ShoesTime = 60000;
|
||||
|
||||
public const int PurificationCost = 2000;
|
||||
public const int PurificationTime = 30000;
|
||||
|
@ -203,6 +207,7 @@ namespace Preparation.Utility
|
|||
public const int CrazyTime = 30000;
|
||||
public const double CrazyPower = 1.2;
|
||||
public const double CrazyATKFreq = 1.25;
|
||||
public const double ATKFreq = 1.0;
|
||||
public const int CrazySpeed = 300;
|
||||
public const int ScoreFarmPerSecond = 100;
|
||||
public const int MaxCharacterNum = 1;
|
||||
|
|
|
@ -75,26 +75,31 @@ namespace Server
|
|||
|
||||
CharacterType = Transformation.CharacterTypeToProto(player.CharacterType),
|
||||
|
||||
CharacterActiveState = Transformation.CharacterStateToProto(player.CharacterActiveState),
|
||||
CharacterActiveState = Transformation.CharacterStateToProto(player.CharacterState1),
|
||||
|
||||
// 待修改,被动状态用CharacterStateType还是bool
|
||||
BlindState = (player.blind) ? Protobuf.CharacterState.BLIND : Protobuf.CharacterState.NULL_CHARACTER_STATE,
|
||||
BlindTime = (double)player.BlindTime, // 待修改,时间是否应该用double
|
||||
BlindState = (player.blind) ? Protobuf.CharacterState.Blind : Protobuf.CharacterState.NullCharacterState,
|
||||
BlindTime = player.BlindTime,
|
||||
// 待修改,Character.cs中没有knockedback
|
||||
KnockbackState = (player.knockedback) ? Protobuf.CharacterState.KNOCKED_BACK : Protobuf.CharacterState.NULL_CHARACTER_STATE,
|
||||
KnockbackTime = (double)player.KnockedBackTime,
|
||||
StunnedState = (player.stunned) ? Protobuf.CharacterState.STUNNED : Protobuf.CharacterState.NULL_CHARACTER_STATE,
|
||||
StunnedTime = (double)player.StunnedTime,
|
||||
InvisibleState = (player.visible) ? Protobuf.CharacterState.NULL_CHARACTER_STATE : Protobuf.CharacterState.INVISIBLE,
|
||||
// KnockbackState = (player.knockedback) ? Protobuf.CharacterState.KnockedBack : Protobuf.CharacterState.NullCharacterState,
|
||||
// KnockbackTime = player.KnockedBackTime,
|
||||
StunnedState = (player.stunned) ? Protobuf.CharacterState.Stunned : Protobuf.CharacterState.NullCharacterState,
|
||||
StunnedTime = player.StunnedTime,
|
||||
InvisibleState = (player.visible) ? Protobuf.CharacterState.NullCharacterState : Protobuf.CharacterState.Invisible,
|
||||
// 待修改,Character.cs中没有InvisibleTime
|
||||
InvisibleTime = (double)player.InvisibleTime,
|
||||
HealingState = (player.healing) ? Protobuf.CharacterState.HEALING : Protobuf.CharacterState.NULL_CHARACTER_STATE,
|
||||
HealingTime = (double)player.HealingTime,
|
||||
BerserkState = (player.crazyman) ? Protobuf.CharacterState.BERSERK : Protobuf.CharacterState.NULL_CHARACTER_STATE,
|
||||
BerserkTime = (double)CrazyManTime,
|
||||
BurnedState = (player.burned) ? Protobuf.CharacterState.BURNED : Protobuf.CharacterState.NULL_CHARACTER_STATE,
|
||||
BurnedTime = (double)BurnedTime,
|
||||
DeceasedState = (player.deceased) ? Protobuf.CharacterState.DECEASED : Protobuf.CharacterState.NULL_CHARACTER_STATE,
|
||||
// InvisibleTime = (double)player.InvisibleTime,
|
||||
// 貌似不需要治疗时间
|
||||
// HealingState = (player.healing) ? Protobuf.CharacterState.Healing : Protobuf.CharacterState.NullCharacterState,
|
||||
// HealingTime = (double)player.HealingTime,
|
||||
// 待修改,crazyman不知道是buff还是药水
|
||||
// BerserkState = (player.CrazyManNum == 1) ? Protobuf.CharacterState.Berserk : Protobuf.CharacterState.NullCharacterState,
|
||||
// BerserkTime = CrazyManTime,
|
||||
BurnedState = (player.burned) ? Protobuf.CharacterState.Burned : Protobuf.CharacterState.NullCharacterState,
|
||||
BurnedTime = player.BurnedTime,
|
||||
HarmCut = player.HarmCut,
|
||||
HarmCutTime = player.HarmCutTime,
|
||||
DeceasedState = (player.CharacterState2 == Preparation.Utility.CharacterState.DECEASED) ? Protobuf.CharacterState.Deceased : Protobuf.CharacterState.NullCharacterState,
|
||||
|
||||
CharacterPassiveState = Transformation.CharacterStateToProto(player.CharacterState2),
|
||||
|
||||
X = player.Position.x,
|
||||
Y = player.Position.y,
|
||||
|
@ -105,22 +110,36 @@ namespace Server
|
|||
|
||||
CommonAttack = (int)player.AttackPower,
|
||||
// 待修改,Character.cs中没有CommonAttackCD
|
||||
CommonAttackCD = (double)player.AttackCD,
|
||||
CommonAttackCd = (int)(1 / player.ATKFrequency),
|
||||
CommonAttackRange = (int)player.AttackSize,
|
||||
|
||||
SkillAttackCD = (double)player.skillCD,
|
||||
SkillAttackCd = player.skillCD,
|
||||
|
||||
EconomyDepletion = player.EconomyDepletion,
|
||||
KillScore = (int)player.GetCost(),
|
||||
|
||||
HP = (int)player.HP,
|
||||
Hp = (int)player.HP,
|
||||
|
||||
// 待修改,Character.cs中没有区分ShieldEquipment\ShoesEquipment类型
|
||||
Shield = player.Shield,
|
||||
Shoes = player.Shoes,
|
||||
// 待修改,Shield要分两类
|
||||
ShieldEquipment = (int)player.Shield, // 加成值,只包含护盾装备
|
||||
ShoesEquipment = (int)player.Shoes, // 加成值
|
||||
ShoesEquipmentTime = player.QuickStepTime, // 包含所有速度加成的时间
|
||||
// 待修改,Transformation缺东西
|
||||
// PurificationEquipment = (player.Purified) ? Protobuf.EquipmentType.PurificationPotion : Protobuf.PurificationEquipmentType.NullEquipmentType,
|
||||
PurificationEquipmentTime = player.PurifiedTime,
|
||||
// 待修改,Character.cs没有隐身时间,没有狂暴药水
|
||||
// InvisibilityEquipment = player.Invisibility,
|
||||
// InvisibilityEquipmentTime = player.InsvisibilityTime,
|
||||
// Berserk = player.CrazyManNum, // 数值,1~3表示等级,0表示没有
|
||||
// BerserkTime = player.CrazyManTime,
|
||||
|
||||
//
|
||||
//AttackBuff =
|
||||
// 待修改,Transformation缺东西
|
||||
// AttackBuff = (player.CrazyManNum == 1) ? Protobuf.CharacterBuffType.AttackBuff1 : (player.CrazyManNum == 2) ? Protobuf.CharacterBuffType.AttackBuff2 : (player.CrazyManNum == 3) ? Protobuf.CharacterBuffType.AttackBuff3 : Protobuf.CharacterBuffType.NullAttackBuff,
|
||||
AttackBuffTime = player.CrazyManTime,
|
||||
// 待修改
|
||||
SpeedBuffTime = player.QuickStepTime,
|
||||
// VisionBuff = (player.CanSeeAll) ? Protobuf.CharacterBuffType.VisionBuff : Protobuf.CharacterBuffType.NullCharacterBuffType,
|
||||
VisionBuffTime = player.WideViewTime,
|
||||
}
|
||||
};
|
||||
return msg;
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace Server
|
|||
private readonly object spectatorJoinLock = new();
|
||||
protected object spectatorLock = new();
|
||||
protected bool isSpectatorJoin = false;
|
||||
int a_b_c_d = 1;
|
||||
protected bool IsSpectatorJoin
|
||||
{
|
||||
get
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"profiles": {
|
||||
"Server": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "--port 8888"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -136,7 +136,7 @@ namespace Server
|
|||
lock (addPlayerLock)
|
||||
{
|
||||
Game.PlayerInitInfo playerInitInfo = new(request.TeamId, request.CharacterId, Transformation.CharacterTypeFromProto(request.CharacterType), request.SideFlag);
|
||||
long newPlayerID = game.AddPlayer(playerInitInfo);
|
||||
long newPlayerID = game.AddCharacter(playerInitInfo);
|
||||
if (newPlayerID == GameObj.invalidID)
|
||||
{
|
||||
GameServerLogging.logger.ConsoleLogDebug("FAIL AddPlayer");
|
||||
|
@ -385,7 +385,7 @@ namespace Server
|
|||
// var gameID = communicationToGameID[request.TeamId][request.PlayerId];
|
||||
boolRes.ActSuccess = game.Attack(
|
||||
request.TeamId, request.CharacterId,
|
||||
request.AttackRange, request.AttackedCharacterId, request.AttackedTeam);
|
||||
request.AttackedCharacterId, request.AttackedTeam);
|
||||
GameServerLogging.logger.ConsoleLogDebug("END Attack");
|
||||
return Task.FromResult(boolRes);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
|
|
@ -17,6 +17,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playback", "PlayBack\Playba
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proto", "..\dependency\proto\Proto.csproj", "{E8E71849-1F1C-422E-B5F2-38483D23F6BD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientTest", "ClientTest\ClientTest.csproj", "{240D57BD-671E-4A84-9A26-82150229B98E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{11DF0425-CEB7-43A8-8626-B42A61BDF972}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\.editorconfig = ..\.editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -51,6 +58,10 @@ Global
|
|||
{E8E71849-1F1C-422E-B5F2-38483D23F6BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E8E71849-1F1C-422E-B5F2-38483D23F6BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E8E71849-1F1C-422E-B5F2-38483D23F6BD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{240D57BD-671E-4A84-9A26-82150229B98E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{240D57BD-671E-4A84-9A26-82150229B98E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{240D57BD-671E-4A84-9A26-82150229B98E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{240D57BD-671E-4A84-9A26-82150229B98E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
Loading…
Reference in New Issue