Merge branch 'eesast:dev' into dev

This commit is contained in:
Zhengxi Yao 2025-04-01 00:23:14 +08:00 committed by GitHub
commit 1b2d418d6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 1447 additions and 208 deletions

15
.editorconfig Normal file
View File

@ -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 =

View File

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

View File

@ -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;

55
CAPI/python/AI.py Normal file
View File

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

245
CAPI/python/API.py Normal file
View File

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

325
CAPI/python/DebugAPI.py Normal file
View File

@ -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("*********************")

View File

@ -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 economy_depletion = 27; //
int32 kill_score = 28; //
int32 hp = 34;
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

View File

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

66
docs/CAPI.md Normal file
View File

@ -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;

View File

@ -1,4 +1,4 @@
# THUAI8游戏选题(改稿)
# THUAI8 西游真经劫
## 游戏背景
@ -198,12 +198,12 @@
各类型建筑的功能、损耗见下表:
| 名称 | 功能 | 花费/破坏得分 | 血量 | 建造时间 |
| :------: | :----------------------------------------------------------: | :-----------: | :--: | :------: |
| 兵营 | 角色出生点开局自带一座兵营至多可再修建两座兵营。同时半径1000范围内的己方角色可持续回血10点/s | 1w/6000 | 600 | 15s |
| 农场 | 每座农场每秒产出100经济至多可再修建4个农场。 | 8000/4000 | 400 | 10s |
| 坑洞陷阱 | 修建后敌方不可见。当敌方角色触碰以陷阱为中心的九宫格时受到每秒20点的伤害持续5s同时会暴露视野 | 1000 | / | 5s |
| 牢笼陷阱 | 修建后敌方不可见。当敌方角色触碰以陷阱为中心的九宫格时会被定身30s无法移动且暴露视野 | 1000 | / | 5s |
| 名称 | 功能 | 花费/破坏得分 | 血量 | 建造时间 |
| :------: | :--------------------------------------------------------------------------------------------------: | :-----------: | :---: | :------: |
| 兵营 | 角色出生点开局自带一座兵营至多可再修建两座兵营。同时半径1000范围内的己方角色可持续回血10点/s | 1w/6000 | 600 | 15s |
| 农场 | 每座农场每秒产出100经济至多可再修建4个农场。 | 8000/4000 | 400 | 10s |
| 坑洞陷阱 | 修建后敌方不可见。当敌方角色触碰以陷阱为中心的九宫格时受到每秒20点的伤害持续5s同时会暴露视野 | 1000 | / | 5s |
| 牢笼陷阱 | 修建后敌方不可见。当敌方角色触碰以陷阱为中心的九宫格时会被定身30s无法移动且暴露视野 | 1000 | / | 5s |
**建筑的修建**:除”坑洞陷阱“和”牢笼陷阱“外,地图上有特定的建筑点位,角色移动到建筑点位九宫格内时,可以开始修建建筑。”坑洞陷阱“和”牢笼陷阱“可以在地图上任意一处”空地“或”草丛“修建,角色修建完成,会出现在距离角色中心最近的方格处。此两种建筑形同”空地“和”草丛“,在未触发情况下不会阻隔角色移动。

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

View File

@ -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);
}
}
}
}

View File

@ -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>();

View File

@ -1,4 +1,4 @@
<!--MapView.axaml-->
<!-- MapView.axaml -->
<UserControl
x:Class="debug_interface.Views.MapView"
xmlns="https://github.com/avaloniaui"
@ -9,31 +9,11 @@
xmlns:vm="using:debug_interface.ViewModels"
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将在代码中填充 -->
<Grid Name="MapGrid" />
<!-- Character Positions Canvas - with transparent background -->
<Canvas Name="CharacterCanvas" Width="750" Height="750" Background="Transparent" />
</Grid>
<!-- Character Positions Canvas -->
<Canvas Name="CharacterCanvas" Background="Transparent" />
</Grid>
</UserControl>

View File

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

View File

@ -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" />

View File

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

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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()!;
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

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

View File

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

View File

@ -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;

View File

@ -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;
}
}
}
}
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -0,0 +1,8 @@
{
"profiles": {
"Server": {
"commandName": "Project",
"commandLineArgs": "--port 8888"
}
}
}

View File

@ -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);
}

View File

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

View File

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