Restructure repository to include all source folders

Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-29 20:17:20 +09:00
parent 5d3cd64a25
commit dd97ddec92
11602 changed files with 1446576 additions and 0 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,133 @@
#ifndef _CASTLE_H_
#define _CASTLE_H_
#pragma once
#include <map>
#include <Creature/CreatureStructure.h>
#include <Creature/Siege/SiegeConstants.h>
#include <Castle/CastleConstants.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
// 전방 참조
class CSiegeObject;
class CSendStream;
namespace Castle
{
class CCastle
{
public:
~CCastle();
// 타입 정의
typedef std::map<unsigned short, unsigned short> MineralInfoMap; // <wMineralID, wAmount>
typedef std::map<unsigned long, CSiegeObject*> CastleObjectMap; // <ObjectCID, lpCastleObject>
// 중계 서버로 부터 받아온 성의 정보 셋팅
bool SetTaxInfo(const CastleTaxInfo& castleTaxInfo);
bool SetMineralInfo(unsigned short wMineralID, unsigned short wAmount, unsigned char cFlag);
void UpdateTaxMoveInfo(unsigned long* dwMoveTempMoney, unsigned long* dwResultTaxMoney);
void UpdateSiegeCountInfo(unsigned char cSiegeCount, unsigned char cInvincibleCount, unsigned short wTotalSiegeCount);
void SendTempTaxInfo(); // 중계서버로 임시 세금 정보 업데이트
bool SendMineralInfo(CSendStream& SendStream, unsigned char cMineralType=Siege::ACCUMULATED_MINERAL); // 광물 정보 전송
void AllRespawn(unsigned char cExceptNation); // 성주변 x셀에 있는 상대국적 캐릭터를 리스폰 시킨다.
bool ChangeCastleMaster(unsigned char cNation); // 소유 국가 변경
void LoseOwnership(); // 성 중립화
void ChangeCastleObjectNation(unsigned char cNation); // 수성 오브젝트들의 국적을 변경
void DestroyAllCastleArms(bool bKillRider); // 수성 병기들을 병기 관리 NPC 로 만든다.
void AddUpgradeItem(unsigned short wItemID, int iIndex, unsigned char cNum); // 업그레이드 보석 아이템 넣기
void DeleteUpgradeItem(int iIndex, unsigned char cNum); // 업그레이드 보석 아이템 빼기
void InitUpgradeItem(); // 업그레이드 보석 아이템 초기화
void AddTempTaxMoney(unsigned char cType, unsigned long dwMoney); // 임시 세금 축적
bool AddMineral(unsigned char cFlag, unsigned short wMineralID, unsigned short wAmount); // 광물 세금 축척
void TakeTaxMoney(unsigned char cType, unsigned long dwTaxMoney); // 세금 회수
bool GainMineral(unsigned short wMineralID, unsigned short wAmount); // 광물 세금 회수
bool InsertCastleObject(CSiegeObject* lpCastleObject); // 성 오브젝트 추가
bool DeleteCastleObject(unsigned long dwCID); // 성 병기 삭제
bool DeleteCastleObject(CSiegeObject* lpCastleObject); // 성 병기 삭제
bool HasCastleArms(unsigned long dwCID);
CSiegeObject* GetCastleEmblem() const;
CSiegeObject* GetCastleObject(unsigned long dwCastleObjectID); // 성 오브젝트 얻어오기
void UpgradeByEmblem();
void DegradeByEmblem();
void Process();
bool CheckRight(unsigned char cRightType, unsigned long dwCID, unsigned long dwGID);
void SetEnableSiege(bool bEnable);
void SetTax(unsigned char cType, unsigned char cTax);
void SetRight(CastleRight castleRight);
bool IsCastleOfNation(unsigned char cNation);
bool IsTaxChangable(unsigned char cType);
void EnableTaxChange();
bool HasAnotherCastleArms(unsigned long dwCID) const;
unsigned long GetCastleID() const { return m_dwCastleID; }
unsigned char GetNation() const { return m_cNation; }
unsigned char GetNameID() const { return m_cNameID; }
unsigned char GetZone() const { return m_cZone; }
unsigned char GetChannel() const { return m_cChannel; }
const Position& GetBackDoorPos(unsigned long dwPos) const { return m_BackDoorPos[dwPos]; }
unsigned char GetTax(unsigned char cType) const { return m_CastleTax[cType].m_cTax; }
unsigned long GetTaxMoney(unsigned char cType) const { return m_CastleTax[cType].m_dwTaxMoney; }
unsigned char GetSiegeCount() const { return m_cSiegeCount; }
unsigned char GetInvincibleCount() const { return m_cInvincibleCount; }
unsigned short GetTotalSiegeCount() const { return m_wTotalSiegeCount; }
unsigned short GetTotalGainTaxCount() const { return (m_wTotalSiegeCount / Castle::TEMP_TAX_GAIN_COUNT); }
unsigned long GetTotalTaxMoney() const { return m_dwTotalTaxMoney; }
unsigned short GetUpgradeItemID() const { return m_wItemID; }
unsigned char GetUpgradeItemNum() const { return m_cTotalItemNum; }
unsigned char GetUpgradeItemNum(int iIndex) const { return m_cItemNum[iIndex]; }
unsigned char GetUpgradeStep() const;
unsigned short GetMineralNum(unsigned char cMineralType, unsigned short wMineralID) const;
void SetBonusRate(float fRate) { m_fBonusRate = fRate; }
private:
CCastle(const CastleInfoDB& CastleInfo);
CastleObjectMap m_CastleObjectMap; // 성 관련 오브젝트맵
unsigned long m_dwCastleID; // 성 혹은 마을 ID
unsigned char m_cNation; // 성을 소유한 국가
unsigned char m_cZone; // 성이 있는 존 번호
unsigned char m_cChannel; // 성이 있는 채널 번호
unsigned char m_cNameID; // 성 이름 ID ( 1부터~~ )
unsigned char m_cSiegeCount; // 공성을 치른 횟수
unsigned char m_cInvincibleCount; // 무적 횟수
unsigned short m_wTotalSiegeCount; // 누적 공성 횟수
unsigned long m_dwTotalTaxMoney; // 누적 세금 회수량
unsigned short m_wItemID; // 업그레이드에 사용할 보석 아이템 ID
unsigned char m_cTotalItemNum; // 업그레이드에 사용할 보석 아이템 갯수 총합
unsigned char m_cItemNum[Castle::EMBLEM_UPGRADE_JEWEL_POS_NUM]; // 업그레이드에 사용할 보석 개별 아이템 갯수
CastleRight m_CastleRight; // 성 관리 권한
Position m_BackDoorPos[2]; // 뒷문 사용시 안/밖위치
CastleTaxInfo m_CastleTax[Castle::MAX_TAX_TYPE]; // 세율 및 세금 정보
MineralInfoMap m_AccumulatedMineralMap; // 누적 광물 세금
MineralInfoMap m_TemporaryMineralMap; // 임시 광물 세금
bool m_bEnableSiege; // 현재 공성이 가능한가?
float m_fBonusRate; // 상징물 업그레이드에 의한 총 세금 수입 보너스 (%)
unsigned char m_cTempTaxUpdateCount; // 임시 세금을 DB 중계 서버로 보내기전 카운터
unsigned long m_dwTemporayTempTaxMoney; // DB 중계로 업데이트전에 모아진 임시 세금
friend class CCastleMgr;
};
}
#endif // _CASTLE_H_

View File

@@ -0,0 +1,75 @@
#include "stdafx.h"
#include "CastleBlessMgr.h"
#include <Castle/CastleConstants.h>
#include "GMMemory.h"
using namespace Castle;
CCastleBlessMgr::sCastleBlessInfo CCastleBlessMgr::m_CastleBlessInfo[ MAX_BLESS_INFO_NUM ] =
{
sCastleBlessInfo(0, 0, 5, 320.0f, Castle::TITLE_NEW),
sCastleBlessInfo(1, 1, 10, 320.0f, Castle::TITLE_NEW),
sCastleBlessInfo(2, 1, 20, 400.0f, Castle::TITLE_SETTLED),
sCastleBlessInfo(3, 2, 30, 480.0f, Castle::TITLE_FLOURISHED),
sCastleBlessInfo(4, 2, 40, 560.0f, Castle::TITLE_HONORABLE),
sCastleBlessInfo(5, 3, 50, 640.0f, Castle::TITLE_GLORIOUS)
};
CCastleBlessMgr::CCastleBlessMgr()
{
Initialize();
}
CCastleBlessMgr::~CCastleBlessMgr()
{
Destroy();
}
CCastleBlessMgr& CCastleBlessMgr::GetInstance()
{
static CCastleBlessMgr ms_this;
return ms_this;
}
bool CCastleBlessMgr::Initialize()
{
return true;
}
void CCastleBlessMgr::Destroy()
{
}
unsigned char CCastleBlessMgr::GetTitle(unsigned short wGainTaxCount, unsigned char cUpgradeStep) const
{
int iIndex = FindProperIndex(wGainTaxCount, cUpgradeStep);
return m_CastleBlessInfo[ iIndex ].m_cTitle;
}
unsigned char CCastleBlessMgr::GetBonusPercent(unsigned short wGainTaxCount, unsigned char cUpgradeStep) const
{
int iIndex = FindProperIndex(wGainTaxCount, cUpgradeStep);
return m_CastleBlessInfo[ iIndex ].m_cBonusPercent;
}
float CCastleBlessMgr::GetBlessArea(unsigned short wGainTaxCount, unsigned char cUpgradeStep) const
{
int iIndex = FindProperIndex(wGainTaxCount, cUpgradeStep);
return m_CastleBlessInfo[ iIndex ].m_fArea;
}
int CCastleBlessMgr::FindProperIndex(unsigned short wGainTaxCount, unsigned char cUpgradeStep) const
{
int iIndex = 0;
for (; iIndex<MAX_BLESS_INFO_NUM; ++iIndex)
{
if (m_CastleBlessInfo[ iIndex ].m_wGainTaxCount > wGainTaxCount ||
m_CastleBlessInfo[ iIndex ].m_cUpgradeStep > cUpgradeStep)
{
return (iIndex - 1);
}
}
return (iIndex - 1);
}

View File

@@ -0,0 +1,57 @@
#ifndef _CASTLE_BLESS_MANAGER_H_
#define _CASTLE_BLESS_MANAGER_H_
#pragma once
#define g_CastleBlessMgr Castle::CCastleBlessMgr::GetInstance()
#include <map>
namespace Castle
{
class CCastleBlessMgr
{
private:
enum Const
{
MAX_BLESS_INFO_NUM = 6
};
struct sCastleBlessInfo
{
unsigned short m_wGainTaxCount; // 세금 획득 횟수(임시 -> 누적) = 총 공성 횟수 / Castle::TEMP_TAX_GAIN_COUNT
unsigned char m_cUpgradeStep; // 필요 업그레이드
unsigned char m_cBonusPercent; // 증가 수확량
float m_fArea; // 영역(반경 m 단위)
unsigned char m_cTitle; // 칭호
sCastleBlessInfo(unsigned short wGainTaxCount, unsigned char cUpgradeStep, unsigned char cBonusPercent,
float fArea, unsigned char cTitle)
: m_wGainTaxCount(wGainTaxCount), m_cUpgradeStep(cUpgradeStep), m_cBonusPercent(cBonusPercent),
m_fArea(fArea), m_cTitle(cTitle)
{
}
};
public:
~CCastleBlessMgr();
static CCastleBlessMgr& GetInstance();
unsigned char GetTitle(unsigned short wGainTaxCount, unsigned char cUpgradeStep) const;
unsigned char GetBonusPercent(unsigned short wGainTaxCount, unsigned char cUpgradeStep) const;
float GetBlessArea(unsigned short wGainTaxCount, unsigned char cUpgradeStep) const;
private:
CCastleBlessMgr();
int FindProperIndex(unsigned short wGainTaxCount, unsigned char cUpgradeStep) const;
bool Initialize();
void Destroy();
static sCastleBlessInfo m_CastleBlessInfo[ MAX_BLESS_INFO_NUM ] ;
};
}
#endif // _CASTLE_BLESS_MANAGER_H_

View File

@@ -0,0 +1,102 @@
#ifndef _CASTLE_CONSTANTS_H_
#define _CASTLE_CONSTANTS_H_
namespace Castle
{
enum Type
{
NONE = 0,
CASTLE = 1,
CAMP = 2
};
enum IndentifyBit
{
STATUE_BIT = 0x80000000, // 다크 카나번 석상 (몬스터 비트와 같다.)
CASTLE_BIT = 0x40000000, // 성
CAMP_BIT = 0x20000000, // 길드 요새
TOWN_BIT = 0x10000000, // 마을
ABTOWN_BIT = 0x0F000000, // 어빌리티용 추가 타운비트
SIEGE_BIT = 0x00FF0000, // 공성시간에 공성측 국가가 사용하는 임시 리스폰 포인트 비트
// CASTLE_BIT | SIEGE_BIT 로 사용!!
// ex) 0x40FF1001 : CastleNameID 가 1 이고 임시 리스폰 포인트 2번
CASTLE_NAME_BIT_SHIFT = 12 // 다른 채널에 성이 존재할수 있기 때문에 스크립트에 들어가는 성 ID 대신
// CASTLE_BIT | (CastleNameID << CASTLE_NAME_BIT_SHIFT) 의 값을 사용한다.
// ex) 0x40001000 : CastleNameID 가 1 이다.
};
enum TaxType
{
TRADE_TAX = 0, // 상점 구매/판매
REPAIR_TAX = 1, // 상점 수리
RESMELT_TAX = 2, // 대장장이 재련
CAMP_GOLD_TAX = 3, // 요새 Gold
CAMP_MINERAL_TAX = 4, // 요새 광물
MAX_TAX_TYPE = 5
};
enum TaxChangable
{
TAX_DISABLE = 0, // 세율 변경 불가능
TAX_ENABLE = 1 // 세율 변경 가능
};
enum MineralType
{
ACCUMULATED_MINERAL = 1, // 누적된 광물 아이템
TEMPORARY_MINERAL = 2, // 임시 보관된 광물 아이템
};
enum CastleName
{
TEMP_CASTLE_NAME_0 = 0,
TEMP_CASTLE_NAME_1 = 1,
TEMP_CASTLE_NAME_2 = 2,
TEMP_CASTLE_NAME_3 = 3,
TEMP_CASTLE_NAME_4 = 4,
MAX_CASTLE_NAME_NUM = 5,
MAX_CASTLE_NAME_LEN = 32
};
enum CastleTitle
{
TITLE_NEW = 0, // 칭호 '새로운'
TITLE_SETTLED = 1, // 칭호 '정착한'
TITLE_FLOURISHED = 2, // 칭호 '번창한'
TITLE_HONORABLE = 3, // 칭호 '명예로운'
TITLE_GLORIOUS = 4, // 칭호 '영광의'
MAX_TITLE_NUM = 5
};
enum Const
{
CASTLE_EXP_BONUS_RADIUS = 300, // 공성 경험치 보상 반경
MAX_CASTLE_NUM = 10,
MAX_CASTLE_MINERAL_NUM = 200,
MAX_CASTLE_OBJECT_NUM = 250,
MAX_TEMP_TAX_UPDATE_COUNT = 10,
MAX_TEMP_TAX_AMOUNT = 50000,
EMBLEM_UPGRADE_JEWEL_NUM = 90,
EMBLEM_UPGRADE_JEWEL_NUM_PER_POS = 10,
EMBLEM_UPGRADE_JEWEL_POS_NUM = 9,
FAME_FOR_CAPTURE_CASTLE = 10000, // 공성에서 성을 차지한 길드원들이 얻는 명성치
FAME_FOR_FRIENDLY_GUILD = 5000, // 공성에서 성을 차지한 우호길드의 길드원들이 얻는 명성치
INVINCIBLE_COUNT = 0, // 성주 변경시 주어지는 무적 공성 횟수 (0회)
TEMP_TAX_GAIN_COUNT = 1, // 임시 세금을 누적 세금으로 옮길시 필요한 공성 횟수 (1회)
DAY_END_TIME = 0 // 하루가 끝나는 시간 (밤 12시)
};
}
#endif // _CASTLE_CONSTANTS_H_

View File

@@ -0,0 +1,986 @@
#include "stdafx.h"
#include "CastleDBComponent.h"
#include <Log/ServerLog.h>
#include <Castle/CastleConstants.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
#include <DB/DBDefine.h>
#include <DB/DBComponent.h>
#include <Utility/Math/Math.h>
#include <Utility/Debug/PerformanceCheck.h>
#include <Creature/CreatureStructure.h>
#include <Creature/Siege/SiegeConstants.h>
// -------------------------------------------------------------------------------------------------------------------------- //
// 성 관련 DB 처리
bool DBComponent::CastleDB::InsertCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cMineralType,
unsigned short wMineralID, unsigned short wAmount)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"INSERT INTO TblCastleMineralInfo(nCastleID, snMineralID, snAmount, tnFlag) VALUES(%d, %d, %d, %d)",
dwCastleID, wMineralID, wAmount, cMineralType);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
switch (cMineralType)
{
case Siege::ACCUMULATED_MINERAL:
SERLOG2(g_Log, "성 누적 광물 세금 추가 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
case Siege::TEMPORARY_MINERAL:
SERLOG2(g_Log, "성 임시 광물 세금 추가 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
}
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cNation, unsigned char cSiegeCount,
unsigned char cInvincibleCount, unsigned short wTotalSiegeCount, unsigned long dwTotalTaxMoney,
unsigned short wItemID, unsigned char cItemNum)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "dbo.USPCastleUpdate '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d'",
dwCastleID, cNation, cSiegeCount, cInvincibleCount, wTotalSiegeCount, dwTotalTaxMoney, wItemID, cItemNum);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleUpgradeItemInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned short wItemID, unsigned char cItemNum)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCastleInfo SET snItemID=%d, tnItemNum=%d WHERE nCastleID=%d", wItemID, cItemNum, dwCastleID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 상징물 보석 아이템 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleSiegeCount(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cSiegeCount,
unsigned char cInvincibleCount, unsigned short wTotalSiegeCount)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCastleInfo SET tnSiegeCount=%d, tnInvincibleCount=%d, snTotalSiegeCount=%d WHERE nCastleID=%d",
cSiegeCount, cInvincibleCount, wTotalSiegeCount, dwCastleID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 공성 횟수 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleTotalTaxMoney(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned long dwTotalTaxMoney)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCastleInfo SET nTotalTaxMoney=%d WHERE nCastleID=%d", dwTotalTaxMoney, dwCastleID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성의 지난 세금 회수량 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleRight(CDBComponent& DBComponent, unsigned long dwCastleID, char* szRight, unsigned short wSize)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCastleInfo SET bRight=0x");
char* lpDest = DBComponent.GetQueryBuffer() + strlen(DBComponent.GetQueryBuffer());
char* lpPos = szRight;
for (int nIndex = 0; nIndex < wSize; ++nIndex, ++lpPos, lpDest += 2)
{
Math::Convert::Hex08ToStr(lpDest, *lpPos);
}
const int MAX_FOOTER = 512;
char szFooter[MAX_FOOTER];
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
sprintf(szFooter, " WHERE nCastleID=%d", dwCastleID);
strcat(DBComponent.GetQueryBuffer(), szFooter);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "성 관리 권한 변경 실패 : %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleTax(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cType, unsigned char cTax)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCastleTaxInfo SET tnTax=%d WHERE nCastleID=%d AND tnType=%d", cTax, dwCastleID, cType);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 세율 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleTaxChangable(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cType, unsigned char cTaxChangable)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCastleTaxInfo SET tnTaxChangable=%d WHERE nCastleID=%d AND tnType=%d", cTaxChangable, dwCastleID, cType);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 세율 변경 가능 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleTaxMoney(CDBComponent& DBComponent, unsigned long dwCastleID,
unsigned char cType, unsigned long dwTempTaxMoney, unsigned long dwTaxMoney)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCastleTaxInfo SET nTempMoney=%d, nTaxMoney=%d WHERE nCastleID=%d AND tnType=%d",
dwTempTaxMoney, dwTaxMoney, dwCastleID, cType);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 세금 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cMineralType,
unsigned short wMineralID, unsigned short wAmount)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCastleMineralInfo SET snAmount=%d WHERE nCastleID=%d AND tnFlag=%d AND snMineralID=%d",
wAmount, dwCastleID, cMineralType, wMineralID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 광물 세금 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::DeleteCastleTaxInfo(CDBComponent& DBComponent, unsigned long dwCastleID)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCastleTaxInfo SET tnTax=0, nTempMoney=0, nTaxMoney=0, tnTaxChangable=%d WHERE nCastleID=%d", Castle::TAX_ENABLE, dwCastleID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 세율 및 세금 초기화 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::DeleteCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"DELETE FROM TblCastleMineralInfo WHERE nCastleID=%d", dwCastleID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 광물 세금 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::DeleteCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cMineralType)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"DELETE FROM TblCastleMineralInfo WHERE nCastleID=%d AND tnFlag=%d", dwCastleID, cMineralType);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 광물 세금 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::DeleteCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID,
unsigned char cMineralType, unsigned short wMineralID)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCastleID = (dwCastleID & ~Castle::CASTLE_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"DELETE FROM TblCastleMineralInfo WHERE nCastleID=%d AND tnFlag=%d AND snMineralID=%d", dwCastleID, cMineralType, wMineralID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 광물 세금 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
// -------------------------------------------------------------------------------------------------------------------------- //
// 성 오브젝트 DB 처리
bool DBComponent::CastleDB::UpdateCastleObjectHP(CDBComponent& DBComponent, unsigned long dwCID, unsigned long dwHP)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCID = (dwCID & ~Creature::SIEGE_OBJECT_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCastleCreatureInfo SET nHP=%d WHERE nCreatureID=%d", dwHP, dwCID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 오브젝트 HP 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleObjectType(CDBComponent& DBComponent, unsigned long dwCID, unsigned short wObjectType, unsigned long dwOwnerID)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCID = (dwCID & ~Creature::SIEGE_OBJECT_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCastleCreatureInfo SET snObjectType=%d, nOwnerID=%d WHERE nCreatureID=%d", wObjectType, dwOwnerID, dwCID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 오브젝트 타입 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleObjectState(CDBComponent& DBComponent, unsigned long dwCID, unsigned char cState, unsigned char cSubState)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCID = (dwCID & ~Creature::SIEGE_OBJECT_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCastleCreatureInfo SET tnState=%d, tnSubState=%d WHERE nCreatureID=%d",
cState, cSubState, dwCID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 오브젝트 상태 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleObjectUpgrade(CDBComponent& DBComponent, unsigned long dwCID, unsigned char cUpgradeStep, unsigned char cUpgradeType)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCID = (dwCID & ~Creature::SIEGE_OBJECT_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCastleCreatureInfo SET tnUpgradeStep=%d, tnUpgradeType=%d WHERE nCreatureID=%d",
cUpgradeStep, cUpgradeType, dwCID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "성 오브젝트 업그레이드 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleObjectTime(CDBComponent& DBComponent, unsigned long dwCID, SYSTEMTIME RemainTime)
{
char strTime[MAX_PATH] = "";
if (0 == RemainTime.wYear)
{
sprintf(strTime, "NULL");
}
else
{
sprintf(strTime, "cast('%d-%d-%d %d:%d:%d' as smalldatetime)", RemainTime.wYear, RemainTime.wMonth, RemainTime.wDay,
RemainTime.wHour, RemainTime.wMinute, RemainTime.wSecond, RemainTime.wMilliseconds);
}
dwCID = (dwCID & ~Creature::SIEGE_OBJECT_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCastleCreatureInfo SET RemainTime=%s WHERE nCreatureID=%d", strTime, dwCID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "성 오브젝트 남은 시간 변경 실패: %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCastleObjectLastUseTime(CDBComponent& DBComponent, unsigned long dwCID, SYSTEMTIME LastUseTime)
{
char strTime[MAX_PATH] = "";
if (0 == LastUseTime.wYear)
{
sprintf(strTime, "NULL");
}
else
{
sprintf(strTime, "cast('%d-%d-%d %d:%d:%d' as smalldatetime)", LastUseTime.wYear, LastUseTime.wMonth, LastUseTime.wDay,
LastUseTime.wHour, LastUseTime.wMinute, LastUseTime.wSecond, LastUseTime.wMilliseconds);
}
dwCID = (dwCID & ~Creature::SIEGE_OBJECT_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCastleCreatureInfo SET LastUseTime=%s WHERE nCreatureID=%d", strTime, dwCID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "성 오브젝트 마지막 사용 시간 변경 실패: %s", DBComponent.GetErrorString());
return false;
}
return true;
}
// -------------------------------------------------------------------------------------------------------------------------- //
// -------------------------------------------------------------------------------------------------------------------------- //
// 길드 요새 관련 DB 처리
bool DBComponent::CastleDB::InsertCampInfo(CDBComponent& DBComponent, unsigned long dwGID, unsigned short wObjectType,
unsigned char cZone, unsigned char cChannel, const POS& Pos,
unsigned long* dwCampID, unsigned short* wError)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
unsigned long dwResult[2];
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "dbo.USPCampCreate '%d', '%d', '%d', '%d', '%f', '%f', '%f'",
dwGID, wObjectType, cZone, cChannel, Pos.fPointX, Pos.fPointY, Pos.fPointZ);
if (false == DBComponent.ExecuteQueryGetData(DBComponent.GetQueryBuffer(), (void *)dwResult))
{
SERLOG2(g_Log, "길드 요새 생성 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
*dwCampID = dwResult[0];
*wError = static_cast<unsigned short>(dwResult[0]); // 0 이면 에러, 0 보다 크면 성공
return true;
}
bool DBComponent::CastleDB::InsertMiningCampMineralInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMineralType,
unsigned short wMineralID, unsigned short wAmount)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"INSERT INTO TblCampMineralInfo(nCampID, snMineralID, snAmount, tnFlag) VALUES(%d, %d, %d, %d)",
dwCampID, wMineralID, wAmount, cMineralType);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
switch (cMineralType)
{
case Siege::ACCUMULATED_MINERAL:
SERLOG2(g_Log, "채굴기 누적 광물 추가 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
case Siege::TEMPORARY_MINERAL:
SERLOG2(g_Log, "채굴기 임시 광물 추가 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
}
return false;
}
return true;
}
bool DBComponent::CastleDB::InsertCampShopInfo(CDBComponent& DBComponent, unsigned long dwCampID)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"INSERT INTO TblCampShopInfo(CampID, Item, TempMoney, Tax) VALUES(%d, NULL, 0, 0)", dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "길드 요새 상점 정보 추가 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::InsertWorldWeaponInfo(CDBComponent& DBComponent, unsigned char cZone, unsigned char cChannel, unsigned char cNation)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
// 이미 있으면 업데이트로, 없다면 인서트를
const int MAX_ROWS = 20;
int nGetRows = 0;
char szBuffer[CDBComponent::QUERY_BUFFER_LEN];
_snprintf(szBuffer, CDBComponent::QUERY_BUFFER_LEN - 1,
"SELECT TblWorldWeaponInfo.tnZone, "
"TblWorldWeaponInfo.tnChannel, "
"TblWorldWeaponInfo.tnKarRemainSiegeTime, "
"TblWorldWeaponInfo.tnMerRemainSiegeTime "
"FROM TblWorldWeaponInfo ");
szBuffer[CDBComponent::QUERY_BUFFER_LEN - 1] = 0;
if (!DBComponent.ExecuteQuery(szBuffer))
{
ERRLOG0(g_Log, "WorldWeaponInfo DB 얻어오기 실패");
return false;
}
WorldWeaponInfoDB* weaponInfo = new WorldWeaponInfoDB[MAX_ROWS];
memset(weaponInfo, 0, sizeof(WorldWeaponInfoDB) * MAX_ROWS);
if (DBComponent.GetData((void**)weaponInfo, sizeof(WorldWeaponInfoDB), MAX_ROWS, &nGetRows))
{
if (0 == nGetRows)
{
// 새로 추가
switch (cNation)
{
case Creature::KARTERANT:
{
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"INSERT INTO TblWorldWeaponInfo(tnZone, tnChannel, tnKarRemainSiegeTime, tnMerRemainSiegeTime) VALUES(%d, %d, %d, 0)",
cZone, cChannel, Siege::WEAPON_REBUILD_SIEGE_TIME_COUNT);
}
break;
case Creature::MERKADIA:
{
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"INSERT INTO TblWorldWeaponInfo(tnZone, tnChannel, tnKarRemainSiegeTime, tnMerRemainSiegeTime) VALUES(%d, %d, 0, %d)",
cZone, cChannel, Siege::WEAPON_REBUILD_SIEGE_TIME_COUNT);
}
break;
}
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "월드 웨폰 파괴 정보 추가 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
}
else
{
// 업데이트
switch (cNation)
{
case Creature::KARTERANT:
{
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblWorldWeaponInfo SET tnKarRemainSiegeTime=%d WHERE tnZone=%d AND tnChannel=%d",
Siege::WEAPON_REBUILD_SIEGE_TIME_COUNT, cZone, cChannel);
}
break;
case Creature::MERKADIA:
{
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblWorldWeaponInfo SET tnMerRemainSiegeTime=%d WHERE tnZone=%d AND tnChannel=%d",
Siege::WEAPON_REBUILD_SIEGE_TIME_COUNT, cZone, cChannel);
}
break;
}
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "월드 웨폰 파괴 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
}
}
return true;
}
bool DBComponent::CastleDB::DeleteCampInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned short wCampType)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "dbo.USPCampDelete %d", dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "길드 요새 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
if ( Siege::MINING_CAMP == wCampType )
{
DeleteMiningCampMineralInfo(DBComponent, dwCampID, Siege::ACCUMULATED_MINERAL);
DeleteMiningCampMineralInfo(DBComponent, dwCampID, Siege::TEMPORARY_MINERAL);
}
return true;
}
bool DBComponent::CastleDB::DeleteMiningCampMineralInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMineralType)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"DELETE FROM TblCampMineralInfo WHERE nCampID=%d AND tnFlag=%d", dwCampID, cMineralType);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
switch (cMineralType)
{
case Siege::ACCUMULATED_MINERAL:
SERLOG2(g_Log, "채굴기 누적 보관 광물 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
case Siege::TEMPORARY_MINERAL:
SERLOG2(g_Log, "채굴기 임시 보관 광물 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
}
return false;
}
return true;
}
bool DBComponent::CastleDB::DeleteMiningCampMineralInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMineralType, unsigned short wMineralID)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"DELETE FROM TblCampMineralInfo WHERE nCampID=%d AND tnFlag=%d AND snMineralID=%d", dwCampID, cMineralType, wMineralID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
switch (cMineralType)
{
case Siege::ACCUMULATED_MINERAL:
SERLOG2(g_Log, "채굴기 누적 보관 광물 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
case Siege::TEMPORARY_MINERAL:
SERLOG2(g_Log, "채굴기 임시 보관 광물 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
}
return false;
}
return true;
}
bool DBComponent::CastleDB::DeleteCampShopInfo(CDBComponent& DBComponent, unsigned long dwCampID)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"DELETE FROM TblCampShopInfo WHERE CampID=%d", dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "길드 요새 상점 정보 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::DeleteWorldWeaponInfo(CDBComponent& DBComponent, unsigned char cZone, unsigned char cChannel)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"DELETE FROM TblWorldWeaponInfo WHERE tnZone=%d AND tnChannel=%d", cZone, cChannel);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "월드 웨폰 파괴 정보 삭제 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampRight(CDBComponent& DBComponent, unsigned long dwCampID, char* szRight, unsigned short wSize)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCampInfo SET bRight=0x");
char* lpDest = DBComponent.GetQueryBuffer() + strlen(DBComponent.GetQueryBuffer());
char* lpPos = szRight;
for (int nIndex = 0; nIndex < wSize; ++nIndex, ++lpPos, lpDest += 2)
{
Math::Convert::Hex08ToStr(lpDest, *lpPos);
}
const int MAX_FOOTER = 512;
char szFooter[MAX_FOOTER];
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
sprintf(szFooter, " WHERE nCampID=%d", dwCampID);
strcat(DBComponent.GetQueryBuffer(), szFooter);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "길드 요새 관리 권한 변경 실패 : %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampHP(CDBComponent& DBComponent, unsigned long dwCampID, unsigned long dwHP)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCampInfo SET nHP=%d WHERE nCampID=%d", dwHP, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "길드 요새 HP 업데이트 실패 : %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampType(CDBComponent& DBComponent, unsigned long dwCampID, unsigned short wType)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCampInfo SET snObjectType=%d WHERE nCampID=%d", wType, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "길드 요새 타입 업데이트 실패 : %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampState(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cState)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCampInfo SET tnState=%d WHERE nCampID=%d", cState, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "길드 요새 상태 업데이트 실패 : %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampSubState(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cSubState)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCampInfo SET tnSubState=%d WHERE nCampID=%d", cSubState, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "길드 요새 서브 상태 업데이트 실패 : %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampUpgrade(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cUpgradeStep)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCampInfo SET tnUpgradeStep=%d WHERE nCampID=%d", cUpgradeStep, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "길드 요새 업그레이드 업데이트 실패 : %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampTime(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cType, SYSTEMTIME TimeValue)
{
char strTime[MAX_PATH] = "";
if (0 == TimeValue.wYear)
{
sprintf(strTime, "NULL");
}
else
{
sprintf(strTime, "cast('%d-%d-%d %d:%d:%d' as smalldatetime)", TimeValue.wYear, TimeValue.wMonth, TimeValue.wDay,
TimeValue.wHour, TimeValue.wMinute, TimeValue.wSecond, TimeValue.wMilliseconds);
}
char strColumn[MAX_PATH] = "";
switch (cType)
{
case Siege::TYPE_REMAIN_TIME:
sprintf(strColumn, "RemainTime");
break;
case Siege::TYPE_LAST_USE_TIME:
sprintf(strColumn, "LastUseTime");
break;
}
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(), "UPDATE TblCampInfo SET %s=%s WHERE nCampID=%d", strColumn, strTime, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG1(g_Log, "길드 요새 시간 변경 실패: %s", DBComponent.GetErrorString());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateMiningCampMineralInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMineralType,
unsigned short wMineralID, unsigned short wAmount)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCampMineralInfo SET snAmount=%d WHERE nCampID=%d AND tnFlag=%d AND snMineralID=%d", wAmount, dwCampID, cMineralType, wMineralID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
switch (cMineralType)
{
case Siege::ACCUMULATED_MINERAL:
SERLOG2(g_Log, "채굴기 누적 보관 광물 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
case Siege::TEMPORARY_MINERAL:
SERLOG2(g_Log, "채굴기 임시 보관 광물 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
break;
}
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampShopInfo(CDBComponent& DBComponent, unsigned long dwCampID,
const char* lpItemBuffer, unsigned long dwItemBufferSize, unsigned char cItemNum,
unsigned long dwTempSafe, unsigned char cTax)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), CDBComponent::MAX_QUERY_LENGTH,
"SELECT Item FROM TblCampShopInfo WHERE CampID=%d", dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer(), OleDB::Rowset_Update))
{
SERLOG2(g_Log, "길드 요새 상점 정보 업데이트 실패 : %s : Query:%s",
DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
const int MAX_BUFFER_SIZE = sizeof(unsigned long) + sizeof(unsigned char) + CampShopInfoDB::MAX_CONTAINER_SIZE;
char szBuffer[MAX_BUFFER_SIZE] = { 0, };
unsigned long* lpTotalSize = reinterpret_cast<unsigned long*>(szBuffer);
unsigned char* lpItemNum = reinterpret_cast<unsigned char*>(lpTotalSize + 1);
char* lpData = reinterpret_cast<char*>(lpItemNum + 1);
*lpTotalSize = sizeof(unsigned long) + sizeof(unsigned char) + dwItemBufferSize + sizeof(unsigned long) * cItemNum;
*lpItemNum = cItemNum;
memcpy(lpData, lpItemBuffer, dwItemBufferSize + sizeof(unsigned long) * cItemNum);
if (false == DBComponent.SetBinaryData(1, (OleDB::LPSET_BINARY)szBuffer))
{
SERLOG2(g_Log, "길드 요새 상점 정보 업데이트 실패 : %s : Query:%s",
DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCampShopInfo SET TempMoney=%d, Tax=%d WHERE CampID=%d", dwTempSafe, cTax, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "길드 요새 상점 정보 업데이트 실패 : %s : Query:%s",
DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateFertilityInfo(CDBComponent& DBComponent, unsigned char cZone, unsigned char cChannel,
unsigned long dwVeinColor, unsigned long dwFertility)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblFertilityInfo SET nFertility=%d WHERE nVeinColor=%d AND tnZone=%d AND tnChannel=%d",
dwFertility, dwVeinColor, cZone, cChannel);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "지력 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateWorldWeaponInfo(CDBComponent& DBComponent, unsigned char cZone, unsigned char cChannel,
unsigned char cKarRemainSiegeTime, unsigned char cMerRemainSiegeTime)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblWorldWeaponInfo SET tnKarRemainSiegeTime=%d, tnMerRemainSiegeTime=%d WHERE tnZone=%d AND tnChannel=%d",
cKarRemainSiegeTime, cMerRemainSiegeTime, cZone, cChannel);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "월드 웨폰 파괴 정보 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampMaterial(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMaterial)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCampInfo SET tnMaterial=%d WHERE nCampID=%d", cMaterial, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "길드 요새 자재 수 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
bool DBComponent::CastleDB::UpdateCampSiegeCount(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cSiegeCount)
{
DBOBJECT_PERFORMANCE_CHECK(FunctionTimingCheck);
dwCampID = (dwCampID & ~Castle::CAMP_BIT);
_snprintf(DBComponent.GetQueryBuffer(), DBComponent.GetQueryBufferLen(),
"UPDATE TblCampInfo SET tnSiegeCount=%d WHERE nCampID=%d", cSiegeCount, dwCampID);
if (false == DBComponent.ExecuteQuery(DBComponent.GetQueryBuffer()))
{
SERLOG2(g_Log, "길드 요새 지난 공성 횟수 업데이트 실패 : %s : Query:%s", DBComponent.GetErrorString(), DBComponent.GetQueryBuffer());
return false;
}
return true;
}
// -------------------------------------------------------------------------------------------------------------------------- //

View File

@@ -0,0 +1,68 @@
#ifndef _RYL_CASTLE_DB_COMPONENT_H_
#define _RYL_CASTLE_DB_COMPONENT_H_
// forward decl.
struct POS;
struct TIME;
class CDBComponent;
namespace DBComponent
{
namespace CastleDB
{
// --------------------------------------------------------------------------------------------
// 공성 및 성 관련 - CastleDBComponent.cpp ----------------------------------------------------
bool InsertCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cMineralType, unsigned short wMineralID, unsigned short wAmount);
bool UpdateCastleInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cNation, unsigned char cSiegeCount,
unsigned char cInvincibleCount, unsigned short wTotalSiegeCount, unsigned long dwTotalTaxMoney,
unsigned short wItemID, unsigned char cItemNum);
bool UpdateCastleUpgradeItemInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned short wItemID, unsigned char cItemNum);
bool UpdateCastleSiegeCount(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cSiegeCount, unsigned char cInvincibleCount, unsigned short wTotalSiegeCount);
bool UpdateCastleTotalTaxMoney(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned long dwTotalTaxMoney);
bool UpdateCastleRight(CDBComponent& DBComponent, unsigned long dwCastleID, char* szRight, unsigned short wSize);
bool UpdateCastleTax(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cType, unsigned char cTax);
bool UpdateCastleTaxChangable(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cType, unsigned char cTaxChangable);
bool UpdateCastleTaxMoney(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cType, unsigned long dwTempTaxMoney, unsigned long dwTaxMoney);
bool UpdateCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cMineralType, unsigned short wMineralID, unsigned short wAmount);
bool DeleteCastleTaxInfo(CDBComponent& DBComponent, unsigned long dwCastleID);
bool DeleteCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID);
bool DeleteCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cMineralType);
bool DeleteCastleMineralInfo(CDBComponent& DBComponent, unsigned long dwCastleID, unsigned char cMineralType, unsigned short wMineralID);
// 수성 병기
bool UpdateCastleObjectHP(CDBComponent& DBComponent, unsigned long dwCID, unsigned long dwHP);
bool UpdateCastleObjectType(CDBComponent& DBComponent, unsigned long dwCID, unsigned short wObjectType, unsigned long dwOwnerID);
bool UpdateCastleObjectState(CDBComponent& DBComponent, unsigned long dwCID, unsigned char cState, unsigned char cSubState);
bool UpdateCastleObjectUpgrade(CDBComponent& DBComponent, unsigned long dwCID, unsigned char cUpgradeStep, unsigned char cUpgradeType);
bool UpdateCastleObjectTime(CDBComponent& DBComponent, unsigned long dwCID, SYSTEMTIME RemainTime);
bool UpdateCastleObjectLastUseTime(CDBComponent& DBComponent, unsigned long dwCID, SYSTEMTIME LastUseTime);
// 길드 요새
bool InsertCampInfo(CDBComponent& DBComponent, unsigned long dwGID, unsigned short wObjectType, unsigned char cZone, unsigned char cChannel, const POS& Pos, unsigned long* dwCampID, unsigned short* wError);
bool InsertMiningCampMineralInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMineralType, unsigned short wMineralID, unsigned short wAmount);
bool InsertCampShopInfo(CDBComponent& DBComponent, unsigned long dwCampID);
bool InsertWorldWeaponInfo(CDBComponent& DBComponent, unsigned char cZone, unsigned char cChannel, unsigned char cNation);
bool DeleteCampInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned short wCampType);
bool DeleteMiningCampMineralInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMineralType);
bool DeleteMiningCampMineralInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMineralType, unsigned short wMineralID);
bool DeleteCampShopInfo(CDBComponent& DBComponent, unsigned long dwCampID);
bool DeleteWorldWeaponInfo(CDBComponent& DBComponent, unsigned char cZone, unsigned char cChannel);
bool UpdateCampRight(CDBComponent& DBComponent, unsigned long dwCampID, char* szRight, unsigned short wSize);
bool UpdateCampHP(CDBComponent& DBComponent, unsigned long dwCampID, unsigned long dwHP);
bool UpdateCampType(CDBComponent& DBComponent, unsigned long dwCampID, unsigned short wType);
bool UpdateCampState(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cState);
bool UpdateCampSubState(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cSubState);
bool UpdateCampUpgrade(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cUpgradeStep);
bool UpdateCampTime(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cType, SYSTEMTIME TimeValue);
bool UpdateMiningCampMineralInfo(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMineralType, unsigned short wMineralID, unsigned short wAmount);
bool UpdateCampShopInfo(CDBComponent& DBComponent, unsigned long dwCampID, const char* lpItemBuffer, unsigned long dwItemBufferSize, unsigned char cItemNum, unsigned long dwTempSafe, unsigned char cTax);
bool UpdateFertilityInfo(CDBComponent& DBComponent, unsigned char cZone, unsigned char cChannel, unsigned long dwVeinColor, unsigned long dwFertility);
bool UpdateWorldWeaponInfo(CDBComponent& DBComponent, unsigned char cZone, unsigned char cChannel, unsigned char cKarRemainSiegeTime, unsigned char cMerRemainSiegeTime);
bool UpdateCampMaterial(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cMaterial);
bool UpdateCampSiegeCount(CDBComponent& DBComponent, unsigned long dwCampID, unsigned char cSiegeCount);
}
}
#endif

View File

@@ -0,0 +1,498 @@
#include "stdafx.h"
#include "CastleMgr.h"
#include <Castle/Castle.h>
#include <Castle/CastleConstants.h>
#include <Castle/CastleBlessMgr.h>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/CharRespawnMgr.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
using namespace Castle;
using namespace Siege;
CCastleMgr::CCastleMgr()
{
Initialize();
}
CCastleMgr::~CCastleMgr()
{
Destroy();
}
CCastleMgr& CCastleMgr::GetInstance()
{
static CCastleMgr ms_this;
return ms_this;
}
bool CCastleMgr::Initialize()
{
return true;
}
void CCastleMgr::Destroy()
{
if (0 == m_CastleMap.size()) return;
CastleMap::iterator CastleItr = m_CastleMap.begin();
CastleMap::iterator CastleEnd = m_CastleMap.end();
while (CastleItr != CastleEnd)
{
CCastle* lpCastle = CastleItr->second;
if (lpCastle)
{
delete lpCastle;
lpCastle = NULL;
}
++CastleItr;
}
m_CastleMap.clear();
}
void CCastleMgr::Process()
{
if (0 == m_CastleMap.size()) return;
CastleMap::iterator CastleItr = m_CastleMap.begin();
CastleMap::iterator CastleEnd = m_CastleMap.end();
while (CastleItr != CastleEnd)
{
CCastle* lpCastle = CastleItr->second;
if (lpCastle) lpCastle->Process();
++CastleItr;
}
}
CCastle* CCastleMgr::GetCastle(unsigned long dwCastleID)
{
CastleMap::iterator itr = m_CastleMap.find(dwCastleID);
if (itr != m_CastleMap.end())
{
return itr->second;
}
return NULL;
}
CCastle* CCastleMgr::GetCastleByNameID(unsigned long dwCastleNameID)
{
CastleMap::iterator itr = m_CastleMap.begin();
while (itr != m_CastleMap.end())
{
CCastle* lpCastle = itr->second;
if (lpCastle && lpCastle->GetNameID() == dwCastleNameID)
{
return lpCastle;
}
++itr;
}
return NULL;
}
// CASTLE_TODO : 성이 길드 소유가 아니므로 막아둔다.
//CCastle* CCastleMgr::GetCastleByGID(unsigned long dwGID)
//{
// CastleMap::iterator itr = m_CastleMap.begin();
//
// while (itr != m_CastleMap.end())
// {
// CCastle* lpCastle = itr->second;
// if (lpCastle && lpCastle->GetGID() == dwGID)
// {
// return lpCastle;
// }
//
// ++itr;
// }
//
// return NULL;
//}
CCastle* CCastleMgr::GetCastleInBlessArea(const Position& Pos)
{
CastleMap::iterator itr = m_CastleMap.begin();
while (itr != m_CastleMap.end())
{
CCastle* lpCastle = itr->second;
if (NULL != lpCastle)
{
CSiegeObject* lpEmblem = lpCastle->GetCastleEmblem();
if (NULL != lpEmblem)
{
float fBlessArea = CCastleBlessMgr::GetInstance().GetBlessArea(
lpCastle->GetTotalGainTaxCount(), lpEmblem->GetUpgradeStep());
const float fDX = Pos.m_fPointX - lpEmblem->GetPosition().m_fPointX;
const float fDZ = Pos.m_fPointZ - lpEmblem->GetPosition().m_fPointZ;
const float fDistance = sqrtf( (fDX * fDX) + (fDZ * fDZ) );
if (fDistance <= fBlessArea)
{
return lpCastle;
}
}
}
++itr;
}
return NULL;
}
unsigned char CCastleMgr::GetCastleNum() const
{
return static_cast<unsigned char>(m_CastleMap.size());
}
unsigned char CCastleMgr::GetCastleNum(unsigned char cNation, unsigned char cZone) const
{
unsigned char cNum = 0;
CastleMap::const_iterator itr = m_CastleMap.begin();
CastleMap::const_iterator end = m_CastleMap.end();
while (itr != end)
{
const CCastle* pCastle = itr->second;
if (pCastle &&
pCastle->GetNation() == cNation &&
pCastle->GetZone() == cZone)
{
++cNum;
}
++itr;
}
return cNum;
}
unsigned char CCastleMgr::GetCastleNum(unsigned char cNation, unsigned char cZone, const Position& Pos) const
{
unsigned char cNum = 0;
CastleMap::const_iterator itr = m_CastleMap.begin();
CastleMap::const_iterator end = m_CastleMap.end();
while (itr != end)
{
const CCastle* pCastle = itr->second;
if (pCastle &&
pCastle->GetNation() == cNation &&
pCastle->GetZone() == cZone)
{
const CSiegeObject* pEmblem = pCastle->GetCastleEmblem();
if (pEmblem)
{
const float fDX = Pos.m_fPointX - pEmblem->GetPosition().m_fPointX;
const float fDZ = Pos.m_fPointZ - pEmblem->GetPosition().m_fPointZ;
const float fDistance = sqrtf( (fDX * fDX) + (fDZ * fDZ) );
if (fDistance <= Castle::CASTLE_EXP_BONUS_RADIUS)
{
++cNum;
}
}
}
++itr;
}
return cNum;
}
void CCastleMgr::GetCastleSiegeInfo(CastleSiegeInfo* lpCastleSiegeInfo, unsigned char& cNum, unsigned short& wSize)
{
if (NULL == lpCastleSiegeInfo)
{
return;
}
for (CastleMap::iterator itr = m_CastleMap.begin(); itr != m_CastleMap.end(); ++itr)
{
CCastle* lpCastle = itr->second;
if (NULL == lpCastle)
{
continue;
}
lpCastleSiegeInfo->m_dwCastleID = lpCastle->m_dwCastleID;
lpCastleSiegeInfo->m_bEnableSiege = lpCastle->m_bEnableSiege;
++lpCastleSiegeInfo;
++cNum;
wSize += sizeof(CastleSiegeInfo);
}
}
bool CCastleMgr::ExistCastleInRadius(const Position& Pos)
{
CastleMap::iterator itr = m_CastleMap.begin();
while (itr != m_CastleMap.end())
{
CCastle* lpCastle = itr->second;
if (lpCastle)
{
CSiegeObject* lpEmblem = lpCastle->GetCastleEmblem();
if (lpEmblem)
{
const float fDX = Pos.m_fPointX - lpEmblem->GetPosition().m_fPointX;
const float fDZ = Pos.m_fPointZ - lpEmblem->GetPosition().m_fPointZ;
const float fDistance = sqrtf( (fDX * fDX) + (fDZ * fDZ) );
if (fDistance <= Siege::CAMP_BUILDING_RADIUS)
{
return true;
}
}
}
++itr;
}
return false;
}
bool CCastleMgr::ExistSiegeInRadius(const Position& Pos)
{
if(Pos.m_fPointX>=1890 && Pos.m_fPointX<=2205)
{
if(Pos.m_fPointZ>=2695 && Pos.m_fPointZ<=2835)
{
return true;
}
}
return false;
}
bool CCastleMgr::SendCastleInfo(CSendStream& SendStream)
{
CastleMap::iterator itr = m_CastleMap.begin();
CastleMap::iterator end = m_CastleMap.end();
while (itr != end)
{
CCastle* lpCastle = itr->second;
if (lpCastle)
{
char* lpBuffer = SendStream.GetBuffer(sizeof(PktCreateCastle));
if (lpBuffer)
{
PktCreateCastle* lpPktCreateCastle = reinterpret_cast<PktCreateCastle*>(lpBuffer);
lpPktCreateCastle->m_dwCastleID = lpCastle->m_dwCastleID;
lpPktCreateCastle->m_cNation = lpCastle->m_cNation;
lpPktCreateCastle->m_cZone = lpCastle->m_cZone;
lpPktCreateCastle->m_cNameID = lpCastle->m_cNameID;
lpPktCreateCastle->m_cUpgradeStep = lpCastle->GetUpgradeStep();
lpPktCreateCastle->m_cInvincibleCount = lpCastle->m_cInvincibleCount;
lpPktCreateCastle->m_wTotalSiegeCount = lpCastle->m_wTotalSiegeCount;
lpPktCreateCastle->m_dwTotalTaxMoney = lpCastle->m_dwTotalTaxMoney;
lpPktCreateCastle->m_fPosX = lpCastle->GetCastleEmblem()->GetPosition().m_fPointX ;
lpPktCreateCastle->m_fPosY = lpCastle->GetCastleEmblem()->GetPosition().m_fPointY ;
lpPktCreateCastle->m_fPosZ = lpCastle->GetCastleEmblem()->GetPosition().m_fPointZ ;
lpPktCreateCastle->m_wItemID = lpCastle->m_wItemID;
lpPktCreateCastle->m_CastleRight = lpCastle->m_CastleRight;
for (int i=0; i<Castle::EMBLEM_UPGRADE_JEWEL_POS_NUM; ++i)
{
lpPktCreateCastle->m_cItemNum[ i ] = lpCastle->m_cItemNum[ i ] ;
}
for (int i=0; i<Castle::MAX_TAX_TYPE; ++i)
{
lpPktCreateCastle->m_CastleTax[ i ] = lpCastle->m_CastleTax[ i ];
}
SendStream.WrapCrypt(sizeof(PktCreateCastle), CmdCreateCastle, 0, PktBase::NO_SERVER_ERR);
}
}
++itr;
}
return true;
}
bool CCastleMgr::SerializeIn(char* lpBuffer_In, unsigned short wBufferSize_In, unsigned char cObjectNum)
{
// lpBuffer_In = CastleInfoDB + CastleTaxInfo * MAX_TAX_TYPE
// + CastleMineralInfo + CastleMineral * (AccumulatedNum + TemporaryNum)
// + CastleObjectInfo * cObjectNum
CastleInfoDB* lpCastleInfo = reinterpret_cast<CastleInfoDB*>(lpBuffer_In);
CCastle* lpCastle = new CCastle(*lpCastleInfo);
if (lpCastle)
{
if (false == m_CastleMap.insert(std::make_pair(lpCastle->GetCastleID(), lpCastle)).second)
{
delete lpCastle;
return false;
}
CastleTaxInfo* lpCastleTaxInfo = reinterpret_cast<CastleTaxInfo*>( lpCastleInfo + 1 );
for (int i=0; i<MAX_TAX_TYPE; ++i)
{
lpCastle->SetTaxInfo(*lpCastleTaxInfo);
++lpCastleTaxInfo;
}
CastleMineralInfo* lpCastleMineralInfo = reinterpret_cast<CastleMineralInfo*>( lpCastleTaxInfo );
CastleMineral* lpCastleMinerl = reinterpret_cast<CastleMineral*>( lpCastleMineralInfo + 1 );
unsigned char cAccumulatedNum = lpCastleMineralInfo->m_cAccumulatedNum;
unsigned char cTemporaryNum = lpCastleMineralInfo->m_cTemporaryNum;
for (int i=0; i<cAccumulatedNum; ++i)
{
lpCastle->SetMineralInfo( lpCastleMinerl->m_wMineralID, lpCastleMinerl->m_wAmount, Castle::ACCUMULATED_MINERAL );
++lpCastleMinerl;
}
for (int i=0; i<cTemporaryNum; ++i)
{
lpCastle->SetMineralInfo( lpCastleMinerl->m_wMineralID, lpCastleMinerl->m_wAmount, Castle::TEMPORARY_MINERAL );
++lpCastleMinerl;
}
CastleObjectInfo* lpCastleObjectInfo = reinterpret_cast<CastleObjectInfo*>( lpCastleMinerl );
for (int i=0; i<cObjectNum; ++i)
{
CSiegeObject* lpCastleObject = CSiegeObjectMgr::GetInstance().CreateCastleObject(*lpCastleObjectInfo);
if (lpCastleObject)
{
lpCastle->InsertCastleObject(lpCastleObject);
}
++lpCastleObjectInfo;
}
// 상징물 업그레이드 효과 적용
lpCastle->UpgradeByEmblem();
// 리스폰 포인트 추가
CSiegeObject* lpEmblem = lpCastle->GetCastleEmblem();
if (lpEmblem && Creature::STATELESS != lpCastle->GetNation())
{
CCharRespawnMgr::GetInstance().SetCastleRespawnPointNation(lpCastle->GetNameID(), lpCastle->GetNation());
}
// NPC 리스트중에 해당 성에 속한 NPC 의 Nation 을 셋팅
CCreatureManager::GetInstance().SetNationToCastleNPC(lpCastle->GetCastleID(), lpCastle->GetNation());
return true;
}
return false;
}
void CCastleMgr::ProcessEmblemRegenHPAndMP()
{
CastleMap::iterator itr = m_CastleMap.begin();
while (itr != m_CastleMap.end())
{
CCastle* lpCastle = itr->second;
if (lpCastle && lpCastle->GetCastleEmblem())
{
lpCastle->GetCastleEmblem()->RegenHPAndMP(0, 0, true);
lpCastle->GetCastleEmblem()->SendHPUpdateToDBAgent();
}
++itr;
}
}
void CCastleMgr::EnableTaxChange()
{
CastleMap::iterator itr = m_CastleMap.begin();
while (itr != m_CastleMap.end())
{
CCastle* lpCastle = itr->second;
if (lpCastle)
{
lpCastle->EnableTaxChange();
}
++itr;
}
}
void CCastleMgr::DestroyAllCastleArms()
{
CastleMap::iterator itr = m_CastleMap.begin();
while (itr != m_CastleMap.end())
{
CCastle* lpCastle = itr->second;
if (lpCastle)
{
lpCastle->DestroyAllCastleArms(false);
}
++itr;
}
}
bool CCastleMgr::HasCastleArms(unsigned long dwCID)
{
CastleMap::iterator itr = m_CastleMap.begin();
while (itr != m_CastleMap.end())
{
CCastle* lpCastle = itr->second;
if (lpCastle)
{
if(lpCastle->HasCastleArms(dwCID))
{
return true;
}
}
++itr;
}
return false;
}
unsigned char CCastleMgr::GetNation()
{
CastleMap::iterator itr = m_CastleMap.begin();
while (itr != m_CastleMap.end())
{
CCastle* lpCastle = itr->second;
if (lpCastle)
{
return lpCastle->GetNation();
}
++itr;
}
return Creature::STATELESS;
}

View File

@@ -0,0 +1,87 @@
#ifndef _CASTLE_MANAGER_H_
#define _CASTLE_MANAGER_H_
#pragma once
#include <map>
#include <Network/Stream/SendStream.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
#include <Creature/CreatureStructure.h>
// 전방 참조
class CSiegeObject;
class CSiegeObjectMgr;
namespace Castle
{
// 전방 참조
class CCastle;
class CCastleMgr
{
public:
~CCastleMgr();
static CCastleMgr& GetInstance();
typedef std::map<unsigned long, CCastle*> CastleMap; // <CastleID, lpCastle>
bool Initialize();
void Destroy();
void Process();
CCastle* GetCastle(unsigned long dwCastleID);
CCastle* GetCastleByNameID(unsigned long dwCastleNameID);
// CASTLE_TODO : 성이 길드 소유가 아니므로 막아둔다.
// CCastle* GetCastleByGID(unsigned long dwGID);
CCastle* GetCastleInBlessArea(const Position& Pos);
void GetCastleSiegeInfo(CastleSiegeInfo* lpCastleSiegeInfo, unsigned char& cNum, unsigned short& wSize);
// 성의 갯수를 리턴
unsigned char GetCastleNum() const;
// 해당 존, 해당 국가의 성의 갯수를 리턴
unsigned char GetCastleNum(unsigned char cNation, unsigned char cZone) const;
// 해당 존, 해당 국가의 보상 반경내에 있는 성의 갯수를 리턴
unsigned char GetCastleNum(unsigned char cNation, unsigned char cZone, const Position& Pos) const;
// Pos 반경에 성이 존재하는지 체크
bool ExistCastleInRadius(const Position& Pos);
// Pos 반경에 공성 병기를 생성할 수 있는지 체크
bool ExistSiegeInRadius(const Position& Pos);
// 패킷 전송
bool SendCastleInfo(CSendStream& SendStream);
// DBAgentServer 로부터 성 정보를 얻어옴
bool SerializeIn(char* lpBuffer_In, unsigned short wBufferSize_In, unsigned char cObjectNum);
// 성 상징물 HP Regen 처리
void ProcessEmblemRegenHPAndMP();
// 공성 시간후 성들의 세율 변경 가능 정보 초기화
void EnableTaxChange();
// 수성 병기를 병기 관리 NPC 로 변경
void DestroyAllCastleArms();
// 수성 병기 체크.
bool HasCastleArms(unsigned long dwCID);
// 성 소유주.
unsigned char GetNation();
private:
CCastleMgr();
CastleMap m_CastleMap;
};
}
#endif // _CASTLE_MANAGER_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,194 @@
#ifndef _GUILD_H_
#define _GUILD_H_
#pragma once
#include <map>
#include <vector>
#include <Network/Packet/PacketStruct/GuildPacket.h>
#include <Community/Guild/GuildConstants.h>
// 전방 참조
class CAggresiveCreature;
class CCharacter;
class CCell;
class CSession;
namespace Guild
{
// 길드 이름에 부적절한 글자가 있는지 살핀다.
bool CheckGuildName(const char* szGuildName);
class CGuild
{
protected:
// 길드 클래스는 길드 매니저에서만 생성 가능 (삭제는 유틸을 이용하는 관계로 public)
CGuild(void);
CGuild(unsigned long dwGID, unsigned char cNation, char* szName);
CGuild(GuildInfoDB& guildInfo);
friend class CGuildMgr;
public:
~CGuild(void);
public:
// 클라이언트에게 정보를 받아 체크하고 중계 서버로 보내준다.
// (TODO : 각종 에러들은 필요시 넘버링해준다. 일단은 모두 SERVER_ERROR.)
unsigned short SetMark(unsigned long dwSenderID, char *szMark);
bool SetLevel(unsigned long dwMaster, unsigned char cLevel);
bool SetRight(unsigned long dwMaster, GuildRight guildRight);
bool SetTitle(unsigned long dwSuperior, unsigned long dwFollower, unsigned char cTitle, unsigned short &wError);
bool JoinMember(unsigned long dwCID, unsigned char cTitle, unsigned short &wError);
bool JoinMemberDB(GuildMemberDB& guildMemberDB);
virtual bool JoinMember(MemberInfo& memberInfo);
bool TacticsMember(unsigned long dwCID, unsigned char cTitle, unsigned char cType, unsigned short &wError);
bool SetTacticsTitle(unsigned long dwSuperior, unsigned long dwFollower, unsigned short wCmd, unsigned char cTitle, unsigned char cType, unsigned short &wError);
bool SetTacticsTitle(unsigned long dwCID, unsigned char cTitle);
bool IsMember(unsigned long dwCID);
bool IsTacticsWaitMember(unsigned long dwCID);
bool KickMember(unsigned long dwSuperior, unsigned long dwFollower, unsigned short &wError);
bool KickTacticsMember(unsigned long dwSuperior, unsigned long dwFollower, unsigned short wCmd, unsigned short &wError);
bool LeaveTacticsMember(unsigned long dwCID, unsigned long dwReferenceID, unsigned short wCmd);
bool UpdateMemberInfo(unsigned long dwCID, unsigned long dwValue, unsigned char cCmd);
// 중계 서버로부터 정보를 받아 처리하고 클라이언트에게 알려준다.
unsigned short SetMark(unsigned long dwSenderID, char *szMark, unsigned long dwGold);
bool SetLevel(unsigned char cLevel, unsigned long dwGold);
bool SetRight(GuildRight guildRight);
virtual bool SetTitle(unsigned long dwCID, unsigned char cTitle);
bool SetRelation(unsigned long dwTargetGID, unsigned char cRelationType, unsigned char cActorType=Guild::BY_MINE);
bool UpdateMemberInfo(unsigned long dwCID, MemberListInfo memberListInfo, MemberDetailInfo memberDetailInfo);
// -------------------------------------------------------------------------------------------
MemberInfo GetMaster(void);
unsigned char GetTitle(unsigned long dwCID);
const unsigned long GetGID(void) { return m_dwGID; }
const char* GetName(void) { return m_strName; }
const unsigned long GetFame(void) { return m_dwFame; }
const GuildRight& GetRight(void) { return m_GuildRight; }
const unsigned long GetGold(void) { return m_dwGold; }
const unsigned char GetCurrentAllMember(void) const; // 모든 멤버.
const unsigned char GetTotalMemberNum(void) const; // 용병을 포함한 모든 멤버
const unsigned char GetCurrentMemberNum(void) const; // 가입대기자, 용병을 제외한 멤버
const unsigned char GetMemberNum(void) const; // 용병을 제외한 멤버수
const unsigned char GetTacticsNum(void) const; // 용병 수
const float GetGuildBonus(unsigned char cRealmPoint, float fPercent = 5.0f); // 길드 보너스 관련 함수
const unsigned char GetLoginMemberNum(void) const; // 게임중인 길드원수
const unsigned char GetMaxMemberNum(void) const; // 최대 길드원수
const unsigned char GetNation(void) const { return m_cInclination; }
const bool IsPeaceMode(void);
void SetGuildMiles(unsigned long dwGuildMiles);
const unsigned long GetGuildMiles() const { return m_dwGuildMiles; }
bool AddGold(unsigned long dwGold);
bool DeductGold(unsigned long dwGold);
void ReleaseGold(unsigned long dwGold);
bool AutoRouting(CAggresiveCreature* lpPickkingCreature, Item::CItem* lpItem, unsigned long& dwGold);
int GetNearMemberList(CCell *pCell, CCharacter **aryNearCharacterList);
void UpgradeMemberRespawnSpeedByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep);
void DegradeMemberRespawnSpeedByEmblem();
unsigned char GetRelation(unsigned long dwGID, unsigned char cActorType=Guild::BY_MINE);
int GetNumOfRelation(unsigned char cRelationType, unsigned char cActorType=Guild::BY_MINE);
unsigned long GetRelationGID(unsigned char cRelationType, unsigned char cActorType=Guild::BY_MINE);
// Vincent - GetRelationGID() 함수는 적대 선언을 1개 이상 할수 있을경우에는 변경되어야한다.!!
bool IsEnemyGuild(unsigned long dwGID);
bool InviteMember(unsigned long dwMember, unsigned long dwGuest, unsigned short &wError);
bool LeaveMember(unsigned long dwCID);
bool LogInOutMember(unsigned long dwCID, unsigned long dwServerID);
const GuildLargeInfoNode GetLargeInfo(unsigned char cIndexOfPage, unsigned short wRank, CGuild* lpRelationGuild);
bool SendGuildSafe(unsigned long dwCID, char* szCharName, unsigned char cCmd);
bool SendMemberList(CCharacter* lpCharacter, unsigned char cMemberType, unsigned char cSortCmd, unsigned char cPage);
void SendAllMember(const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In);
void SendCurrentMember(const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In);
bool SendHostilityList(CCharacter* lpCharacter, unsigned char cCurrentPage, unsigned char cPageState);
void ChangeMemberName(unsigned long dwCID, const char* szChangeName);
TIME* GetGuildPointTime(void) { return &m_tmGuildPoint; }
unsigned char GetGuildPointFlag(void) { return m_cGuildPointFlag; }
void SetGuildPointFlag(unsigned char cFlag) { m_cGuildPointFlag = cFlag; }
// 길드 관련 메소드
void SetGuildSafe(char cGuildSafe) { m_cGuildSafe = cGuildSafe; }
char GetGuildSafe() { return m_cGuildSafe; }
protected:
bool InsertRelation(unsigned long dwGID, unsigned long dwTargetGID, unsigned char cRelation);
void CalculateGuildFame(void);
// ------------------------------------------------------------------
// 멤버 변수
char m_cGuildSafe;
unsigned long m_dwGID; // 길드 아이디
char m_strName[MAX_GUILD_NAME_LEN]; // 길드 이름
char m_szMark[MAX_MARK_SIZE]; // 길드 마크
unsigned char m_cInclination; // 길드 성향 (Guild::Inclination 참고)
unsigned char m_cLevel; // 길드 레벨
unsigned long m_dwFame; // 길드 명성
unsigned long m_dwGold; // 길드 금고
unsigned char m_cGuildPointFlag; // 길드 재정비 관련.
TIME m_tmLastLogout; /* 최종 길드원 로그아웃 시간
(일정 시간동안 길드원이 아무도 로그인하지 않으면 길드가 삭제된다.) */
TIME m_tmCheckMember; // 길드원 수가 일정 인원 미만으로 떨어져 길드가 삭제되기까지의 시간
TIME m_tmGMLastLogout; /* 길드 마스터의 로그아웃 시간
(일정 시간동안 길드 마스터가 로그인하지 않으면 다음 서열의 길드원이 길드 마스터가 된다.) */
TIME m_tmChangeInclination; // 길드 성향을 변경한 시간 (24시간 이내에는 다시 변경할 수 없다.)
TIME m_tmGuildPoint; // 길드 재정비 관련 시간.
GuildRight m_GuildRight; // 길드 권한
MemberList m_MemberList; // 길드원
RelationMap m_Relation[Guild::MAX_RELATION_ACTOR]; // 내가 맺은 길드 관계 / 다른 길드에 의해 맺어진 길드 관계
unsigned long m_dwGuildMiles;
};
};
#endif

View File

@@ -0,0 +1,180 @@
#ifndef _GUILD_CONSTANTS_H_
#define _GUILD_CONSTANTS_H_
namespace Guild
{
enum Const
{
MAX_GUILD_NAME_LEN = 11, // 길드 이름의 최대 길이 (실제 길이)
MAX_GUILD_NAME_FOR_DB = 20, // 길드 이름의 최대 길이 (DB 저장용)
MAX_MEMBER_NAME_LEN = 16, // 길드원 이름의 최대 길이 (일반적인 캐릭터 이름의 최대 길이와 동일)
MAX_MARK_SIZE = 18 * 12 * 2 + 1, // 길드 마크 크기 (18 * 12 크기의 16비트 칼라의 BMP 파일, 첫 1바이트는 마크 존재 여부)
MAX_RIGHT_SIZE = 50, // 길드 권한 구조체 크기
MAX_MEMBER_NUM = 100, // 멤버 수가 가질 수 있는 최대 크기 (가입대기자 때문에 여유분을 더 둔다.)
MAX_RELATION_NUM = 255, // 우호/적대 관계 리스트의 최대 크기 (카운터 적대 신청때문에 여유분을 더 둔다.)
MAX_HOSTILITY_APPLY_NUM = 49, // 대상으로 받을수 있는 최대 적대 관계 수
MAX_HOSTILITY_NUM = 1, // 자신이 적대 선언할 수 있는 최대 수
MAX_ALERT_NUM = 1, // 자신이 경계 적대 선언할 수 있는 최대 수
MAX_LEVEL = 5, // 길드의 최대 레벨
CREATE_LEVEL = 30, // 길드 생성시 요구되는 최소 캐릭터 레벨
CREATE_LEVEL_FOR_CHINA = 15, // (중국용) 길드 생성시 요구되는 최소 캐릭터 레벨
SET_MARK_GOLD = 5000000, // 마크 변경시 필요한 금액
FAME_LEAVE_PENALTY = 1000, // 길드마스터의 승인 없이 탈퇴시 깎이는 명성
MAX_SMALL_NODE_NUM = 500, // 한번에 전송 가능한 길드 간략 정보의 최대 숫자
MEMBER_INFO_UPDATE_COUNT = 10, // 길드원 정보를 다른 서버에 업데이트하는 타이밍
DELETE_GUILD_MEMBER_NUM = 4, // 길드가 유지될 수 있는 최소 인원
MINUTES_PER_HOUR = 60,
// 해외의 요청을 수용하여 탈퇴 대기 시간이 없어졌습니다. (2005-06-13 by 로딘)
LEAVE_WAIT_TIME = 0, // 탈퇴 대기 기간 (단위 : 분)
LEAVE_WAIT_TIME_FOR_CHINA = 10080, // (중국용) 탈퇴 대기 기간 (단위 : 분)
DELETE_GUILD_LOGOUT_TIME = 43200, // 길드원이 로그인하지 않아 길드가 해체될 때까지의 기간 (단위 : 분)
MEMBER_NUM_DELETE_TIME = 1440, // 길드원이 DELETE_GUILD_MEMBER_NUM 미만이 된 후 길드 해체까지의 기간 (단위 : 분)
TRANFER_MASTER_TIME = 20160, // 오랫동안 접속하지 않은 길드마스터의 권한 양도 기간 (단위 : 분)
CHANGE_INCLINATION_TIME = 1440, // 길드 성향 변경 후 다시 변경할 수 있을 때까지의 기간 (단위 : 분)
RELATION_WAIT_TIME = 1440, // 관계 변경 대기 시간 (단위 : 분)
/*
// Test : 길드 테스트를 위한 시간
LEAVE_WAIT_TIME_FOR_CHINA = 10, // (중국용) 탈퇴 대기 기간 (단위 : 분)
DELETE_GUILD_LOGOUT_TIME = 30, // 길드원이 로그인하지 않아 길드가 해체될 때까지의 기간 (단위 : 분)
MEMBER_NUM_DELETE_TIME = 15, // 길드원이 DELETE_GUILD_MEMBER_NUM 미만이 된 후 길드 해체까지의 기간 (단위 : 분)
TRANFER_MASTER_TIME = 20, // 오랫동안 접속하지 않은 길드마스터의 권한 양도 기간 (단위 : 분)
CHANGE_INCLINATION_TIME = 10, // 길드 성향 변경 후 다시 변경할 수 있을 때까지의 기간 (단위 : 분)
RELATION_WAIT_TIME = 1, // 관계 변경 대기 시간 (단위 : 분)
*/
MEMBER_WAIT_BIT = 0x80000000, // 가입 대기자에게 마크를 달지못하게 하는 플래그
TIME_GUILD_POINT = 30, // 길드전 재정비 시간(분단위).
TACTICE_JON_LEVEL = 1, // 용병 가입 레벨.
TACTICS = 1, // 용병.
TACTICS_WAIT = 2, // 용병 가입 대기자.
};
enum Title
{
NONE = 0,
MASTER = 1, // 길드 마스터
MIDDLE_ADMIN = 2, // 중간 관리자
COMMON = 3, // 일반 길드원
LEAVE_WAIT = 4, // 탈퇴 대기자
JOIN_WAIT = 5, // 가입 대기자
MAX_TITLE = 6
};
enum MemberType
{
TYPE_MEMBER = 0, // 멤버
TYPE_TACTICS = 1 // 용병
};
enum GuildSort
{
GUILD_FAME = 0, // 순위(명성)
GUILD_NAME = 1, // 길드명
GUILD_HOSTILITY = 2, // 적대길드
GUILD_NEUTRALITY = 3, // 비적대길드
GUILD_MINE = 4, // 자신의 길드
GUILD_REQUEST = 5 // 길드 리스트 요청 (셀 로긴 등에 의한 무작위 정보 요청)
};
enum MemberSort
{
MEMBER_TITLE = 0, // 직위순
MEMBER_CLASS = 1, // 클래스별 소트
MEMBER_LEVEL = 2, // 레벨별 소트
MEMBER_FAME = 3, // 명성별 소트
MEMBER_NAME = 4, // 이름별 소트
MEMBER_GOLD = 5, // 소지금액별 소트
MEMBER_POSITION = 6, // 위치별 소트
MEMBER_JOIN_WAIT = 7, // 신청중인 플레이어
MEMBER_WAR_ON = 8, // 길드 전쟁 참여중인 멤버 (이름순으로 소트)
MEMBER_WAR_OFF = 9, // 길드 전쟁 불참중인 멤버 (이름순으로 소트)
TACTICS_JOIN_WAIT = 10, // 신청중인 용병 (높은 레벨 순)
TACTICS_ACTIVE = 11 // 활동중인 용병 (높은 레벨 순)
};
enum Relation
{
NEUTRALITY = 0, // 중립 관계
HOSTILITY = 1, // 적대
COUNTER_HOSTILITY = 2, // 카운터 적대
ALERT_HOSTILITY = 3, // 경계 적대
MINE = 4, // 자신의 길드
TARGET_NEUTRALITY = 5, // A->B적대를 하면 DB값이 1로 저장되고 이상태에서 피스를 선언하면
// 해당값이 5로 바뀐다. 5로 바뀐뒤에는 B길드가 적대선언삭제를 해주면
// 이때 DB에서 사라져 완전히 평화관계가 된다.
ALL_HOSTILITY = 6 // 모든 적대 관계
};
enum RelationActor
{
BY_MINE = 0, // 자신이 맺은 길드 관계
BY_TARGET = 1, // 대상으로부터 받은 길드 관계
MAX_RELATION_ACTOR = 2
};
// 중계 서버 전용
enum TimeUpdateType
{
// 수정시엔 CDBObject::UpdateGuildTime 함수를 주의
LAST_LOGOUT = 0, // 길드원이 마지막으로 로그아웃한 시각
MEMBER_NUM_DOWN = 1, // 길드원 수가 3인 이하가 된 시각
MASTER_LOGOUT = 2 // 마스터가 마지막으로 로그아웃한 시각
};
enum ErrType
{
S_SUCCESS = 0,
// CGuild::SetTitle 함수용
E_NOT_MEMBER = 1, // 길드원이 아님
E_DISSOLVE_GUILD = 2, // 길드 해체
E_NOT_CHANGE = 3, // 변경된 바 없음
E_DB_ERROR = 4, // DB 에러
E_NOT_TEMP_MASTER = 5 // 임시 마스터가 될 길드원이 없음
};
enum AddFameType
{
// CGuildDB::AddAllMemberFame, AddFriendGuildMemberFame, AddMemberFame 함수용
TYPE_VALUE = 0, // Fame 값을 그대로 더해준다.
TYPE_HALF = 1, // 명성치를 1/2로 만든다.
TYPE_QUARTER = 2 // 명성치를 1/4로 만든다.
};
// 게임 서버 전용
enum MemberInfoUpdateType
{
// CGuild::UpdateMemberInfo 함수 참조
TYPE_LEVEL = 0,
TYPE_CLASS = 1,
TYPE_FAME = 2,
TYPE_GOLD = 3,
TYPE_WARFLAG = 4
};
const unsigned char ms_aryMaxMemberNum[MAX_LEVEL] = { 10, 20, 30, 40, 50 };
const unsigned long ms_arySetLevelFame[MAX_LEVEL] = { 0, 1000, 3000, 5000, 10000 };
const unsigned long ms_arySetLevelFameForUSA[MAX_LEVEL] = { 0, 400, 2000, 5000, 10000 };
const unsigned long ms_arySetLevelGold[MAX_LEVEL] = { 1000000, 5000000, 10000000, 50000000, 100000000 };
const unsigned long ms_arySetLevelGoldForChina[MAX_LEVEL] = { 100000, 5000000, 10000000, 50000000, 100000000 };
};
#endif

View File

@@ -0,0 +1,739 @@
#include "stdafx.h"
#include <Utility/CheckSum/Crc32Static.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Stream/SendStream.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/WrapPacket.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/Character.h>
#include "GuildUtil.h"
#include "Guild.h"
#include "GuildMgr.h"
using namespace Guild;
CGuildMgr& CGuildMgr::GetInstance()
{
static CGuildMgr guildMgr;
return guildMgr;
}
CGuildMgr::CGuildMgr(void)
{
}
CGuildMgr::~CGuildMgr(void)
{
Destroy();
}
void CGuildMgr::Destroy(void)
{
std::for_each(m_GuildMap.begin(), m_GuildMap.end(), FnDeleteSecond());
m_GuildMap.clear();
m_GuildNameMap.clear();
}
bool CGuildMgr::CreateGuild(unsigned long dwMasterID, unsigned long dwGuildID, unsigned char cInclination, char* szGuildName)
{
CGuild* lpGuild = new CGuild(dwGuildID, cInclination, szGuildName);
if (false == m_GuildMap.insert(GuildMap::value_type(dwGuildID, lpGuild)).second)
{
delete lpGuild;
return false;
}
if (false == m_GuildNameMap.insert(GuildNameMap::value_type(std::string(szGuildName), lpGuild)).second)
{
m_GuildMap.erase(dwGuildID);
delete lpGuild;
return false;
}
PktCreateGuild pktCG;
pktCG.m_dwCID = 0;
pktCG.m_dwGID = dwGuildID;
pktCG.m_cInclination = cInclination;
::memcpy(pktCG.m_szGuildName, szGuildName, MAX_GUILD_NAME_LEN);
char* szPacket = reinterpret_cast<char *>(&pktCG);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCreateGuild), CmdCreateGuild, 0, 0))
{
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCreateGuild), CmdCreateGuild);
}
return true;
}
bool CGuildMgr::GetMemberList(unsigned long dwGID, const MemberList* pMemberList)
{
GuildMap::iterator it = m_GuildMap.find(dwGID);
if (it == m_GuildMap.end())
{
return false;
}
CGuild* lpGuild = it->second;
if(lpGuild)
{
pMemberList = &lpGuild->m_MemberList;
return true;
}
return false;
}
bool CGuildMgr::DissolveGuild(unsigned long dwGID)
{
GuildMap::iterator it = m_GuildMap.find(dwGID);
if (it == m_GuildMap.end())
{
return false;
}
typedef std::vector<unsigned long> DissolveCIDList;
CGuild* lpGuild = it->second;
MemberList& memberList = lpGuild->m_MemberList;
MemberList::iterator member_pos = memberList.begin();
MemberList::iterator member_end = memberList.end();
DissolveCIDList dissolveList;
dissolveList.reserve(memberList.size());
while (member_pos != member_end)
{
dissolveList.push_back(member_pos->m_dwCID);
++member_pos;
}
DissolveCIDList::iterator dissolve_pos = dissolveList.begin();
DissolveCIDList::iterator dissolve_end = dissolveList.end();
while (dissolve_pos != dissolve_end)
{
lpGuild->LeaveMember(*dissolve_pos);
++dissolve_pos;
}
m_GuildNameMap.erase(lpGuild->m_strName);
m_GuildMap.erase(it);
delete lpGuild;
PktCreateGuild pktCG;
pktCG.m_dwGID = dwGID;
char* szPacket = reinterpret_cast<char*>(&pktCG);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCreateGuild), CmdCreateGuild, 0, PktCreateGuild::FAIL_DISSOLVE_GUILD))
{
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCreateGuild), CmdCreateGuild);
}
return true;
}
CGuild* CGuildMgr::GetGuild(unsigned long dwGID)
{
GuildMap::iterator it = m_GuildMap.find(dwGID);
if (it == m_GuildMap.end()) { return NULL; }
return it->second;
}
CGuild* CGuildMgr::GetGuild(char* szName)
{
GuildNameMap::iterator it = m_GuildNameMap.find(szName);
if (it == m_GuildNameMap.end()) { return NULL; }
return it->second;
}
bool CGuildMgr::SetRelation(unsigned long dwGID, unsigned long dwTargetGID, unsigned char cRelationType)
{
CGuild* lpGuild = GetGuild(dwGID);
CGuild* lpTargetGuild = GetGuild(dwTargetGID);
if (NULL == lpGuild || NULL == lpTargetGuild) { return false; }
if(Guild::TARGET_NEUTRALITY == cRelationType)
{
if (!lpGuild->SetRelation(dwTargetGID, cRelationType, Guild::BY_TARGET))
{
return false;
}
if (!lpTargetGuild->SetRelation(dwGID, cRelationType, Guild::BY_MINE))
{
return false;
}
return true;
}
lpGuild->SetRelation(dwTargetGID, cRelationType, Guild::BY_MINE);
lpTargetGuild->SetRelation(dwGID, cRelationType, Guild::BY_TARGET);
return true;
}
bool CGuildMgr::SendGuildList(unsigned long dwCID, unsigned char cSortCmd, unsigned char cPage, unsigned char cNum, GuildCheckSumNode* lpNode)
{
PERFORMANCE_CHECK(FunctionTimingCheck);
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (NULL == lpCharacter)
{
ERRLOG1(g_Log, "CID:0x%08x 존재하지 않는 캐릭터가 길드 리스트를 요청하였습니다.", dwCID);
return false;
}
if (cNum > PktGuildList::MAX_NUM_PER_PAGE)
{
ERRLOG2(g_Log, "CID:0x%08x 길드 리스트의 한 페이지에 찍히는 수를 초과하여 요청하였습니다. 요청노드수:%d",
dwCID, cNum);
cNum = PktGuildList::MAX_NUM_PER_PAGE;
}
const int MAX_BUFFER = sizeof(PktGuildList) + PktGuildList::MAX_NUM_PER_PAGE * sizeof(GuildLargeInfoNode);
char szBuffer[MAX_BUFFER];
PktGuildList* lpPktGLAck = reinterpret_cast<PktGuildList *>(szBuffer);
GuildSmallInfoNode* lpSmallInfoNode = reinterpret_cast<GuildSmallInfoNode *>(lpPktGLAck + 1);
lpPktGLAck->m_dwCID = dwCID;
lpPktGLAck->m_cSortCmd = cSortCmd;
lpPktGLAck->m_cPage = cPage;
lpPktGLAck->m_cSmallNodeNum = 0;
lpPktGLAck->m_cLargeNodeNum = 0;
GuildLargeInfoNode aryCurrentInfoList[PktGuildList::MAX_NUM_PER_PAGE];
// 무작위 요청의 경우 원하는 GID를 알아둔다.
if (Guild::GUILD_REQUEST == cSortCmd)
{
for (int nIndex = 0; nIndex < cNum; ++nIndex)
{
aryCurrentInfoList[nIndex].m_dwGID = (lpNode + nIndex)->m_dwGID;
}
}
GetSortingPageList(lpCharacter, cSortCmd, cPage, aryCurrentInfoList);
int nClientIndex = 0;
int nServerIndex = 0;
// 무작위 요청은 무조건 전체 정보를 보내준다. (대부분 마크가 없어서 요청한 경우임)
if (Guild::GUILD_REQUEST != cSortCmd)
{
for (; nClientIndex < cNum && nServerIndex < PktGuildList::MAX_NUM_PER_PAGE; ++nClientIndex, ++nServerIndex)
{
unsigned long dwCheckSum = 0;
CCrc32Static::BufferCrc32(reinterpret_cast<const char *>(&aryCurrentInfoList[nServerIndex]),
sizeof(GuildSmallInfoNode), dwCheckSum);
if (NULL != lpNode)
{
if (lpNode->m_dwGID == aryCurrentInfoList[nServerIndex].m_dwGID)
{
// 클라이언트와 서버의 체크섬 정보가 틀릴 경우 간략 정보를 보낸다.
if (lpNode->m_dwCheckSum != dwCheckSum)
{
*lpSmallInfoNode = static_cast<GuildSmallInfoNode>(aryCurrentInfoList[nServerIndex]);
++lpSmallInfoNode;
++lpPktGLAck->m_cSmallNodeNum;
}
// 클라이언트와 서버의 GID 정보가 일치할 경우 전체 정보를 보내지 않는다.
aryCurrentInfoList[nServerIndex].m_dwGID = 0;
}
}
++lpNode;
}
}
GuildLargeInfoNode* lpLargeInfoNode = reinterpret_cast<GuildLargeInfoNode *>(lpSmallInfoNode);
for (nServerIndex = 0; nServerIndex < PktGuildList::MAX_NUM_PER_PAGE; ++nServerIndex)
{
if (0 != aryCurrentInfoList[nServerIndex].m_dwGID)
{
*lpLargeInfoNode = aryCurrentInfoList[nServerIndex];
++lpLargeInfoNode;
++lpPktGLAck->m_cLargeNodeNum;
}
}
unsigned char cSmallNodeNum = lpPktGLAck->m_cSmallNodeNum;
unsigned char cLargeNodeNum = lpPktGLAck->m_cLargeNodeNum;
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
CSendStream& SendStream = lpDispatch->GetSendStream();
return SendStream.WrapCompress(szBuffer, sizeof(PktGuildList) +
cSmallNodeNum * sizeof(GuildSmallInfoNode) + cLargeNodeNum * sizeof(GuildLargeInfoNode),
CmdGuildList, 0, 0);
}
return true;
}
bool CGuildMgr::SendGuildRelationInfo(unsigned long dwCID, unsigned long dwGID)
{
PERFORMANCE_CHECK(FunctionTimingCheck);
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (NULL == lpCharacter)
{
ERRLOG1(g_Log, "CID:0x%08x 존재하지 않는 캐릭터가 길드 관계 리스트를 요청하였습니다.", dwCID);
return false;
}
CGuild* lpRequestGuild = GetGuild(dwGID);
if (NULL == lpRequestGuild)
{
ERRLOG1(g_Log, "GID:0x%08x 존재하지 않는 길드의 관계 리스트를 요청하였습니다.", dwGID);
return false;
}
const int MAX_BUFFER = sizeof(PktGuildRelationInfo) + Guild::MAX_RELATION_NUM * sizeof(GuildRelationInfoNode);
char szBuffer[MAX_BUFFER];
PktGuildRelationInfo* lpPktGRAck = reinterpret_cast<PktGuildRelationInfo *>(szBuffer);
GuildRelationInfoNode* lpRelationNode = reinterpret_cast<GuildRelationInfoNode *>(lpPktGRAck + 1);
lpPktGRAck->m_dwCID = dwCID;
lpPktGRAck->m_dwGID = dwGID;
lpPktGRAck->m_wNodeNum = 0;
CGuild* lpGuild = NULL;
GuildLargeInfoNode LargeInfo;
GuildMap::iterator itr = m_GuildMap.begin();
while (itr != m_GuildMap.end())
{
lpGuild = itr->second;
if (NULL != lpGuild)
{
LargeInfo = lpGuild->GetLargeInfo(0, 0, lpRequestGuild);
if (Guild::NEUTRALITY != LargeInfo.m_cRelationByMine ||
Guild::NEUTRALITY != LargeInfo.m_cRelationByTarget)
{
++lpPktGRAck->m_wNodeNum;
lpRelationNode->m_dwGID = LargeInfo.m_dwGID;
lpRelationNode->m_cRelationByMine = LargeInfo.m_cRelationByMine;
lpRelationNode->m_cRelationByTarget = LargeInfo.m_cRelationByTarget;
++lpRelationNode;
}
}
++itr;
}
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
CSendStream& SendStream = lpDispatch->GetSendStream();
return SendStream.WrapCompress(szBuffer, sizeof(PktGuildRelationInfo) +
lpPktGRAck->m_wNodeNum * sizeof(GuildRelationInfoNode),
CmdGuildRelationInfo, 0, 0);
}
return false;
}
bool CGuildMgr::GetSortingPageList(CCharacter* lpCharacter, unsigned char cSortCmd, unsigned char cPage,
GuildLargeInfoNode* aryCurrentInfoList)
{
CGuild* lpMyGuild = GetGuild(lpCharacter->GetGID());
unsigned short wStartOfPage = cPage * PktGuildList::MAX_NUM_PER_PAGE;
// 명성순으로 소트
typedef std::pair<unsigned long, CGuild *> GuildPair;
unsigned short wGuildNum = static_cast<unsigned short>(m_GuildMap.size());
if (wGuildNum < wStartOfPage)
{
if (false == m_GuildMap.empty())
{
ERRLOG3(g_Log, "CID:0x%08x 요청 불가능한 페이지를 요청하였습니다. 길드수:%d, 페이지:%d",
lpCharacter->GetCID(), wGuildNum, cPage);
}
for (unsigned char cIndex = 0; cIndex < PktGuildList::MAX_NUM_PER_PAGE; ++cIndex)
{
aryCurrentInfoList[cIndex].m_dwGID = 0;
}
return false;
}
std::vector<GuildPair> sortVector;
GuildPair NullPair(0, (Guild::CGuild*)NULL);
sortVector.reserve(wGuildNum);
sortVector.assign(wGuildNum, NullPair);
std::partial_sort_copy(m_GuildMap.begin(), m_GuildMap.end(), sortVector.begin(), sortVector.end(), CompareGuildFame());
if (true == sortVector.empty())
{
for (unsigned char cIndex = 0; cIndex < PktGuildList::MAX_NUM_PER_PAGE; ++cIndex)
{
aryCurrentInfoList[cIndex].m_dwGID = 0;
}
return true;
}
unsigned short wVectorSize = static_cast<unsigned short>(sortVector.size());
switch (cSortCmd)
{
case Guild::GUILD_FAME:
{
unsigned char cIndex = 0;
unsigned short wRank = wStartOfPage;
for (; cIndex < PktGuildList::MAX_NUM_PER_PAGE && wRank < wVectorSize; ++cIndex, ++wRank)
{
CGuild* lpGuild = GetGuild(sortVector[wRank].first);
if (NULL == lpGuild)
{
ERRLOG1(g_Log, "GID:0x%08x 존재하지 않는 길드가 소트 벡터에 들어있습니다.", sortVector[wRank].first);
break;
}
aryCurrentInfoList[cIndex] = lpGuild->GetLargeInfo(cIndex, wRank + 1, lpMyGuild);
}
break;
}
case Guild::GUILD_NAME:
{
GuildNameMap::iterator it = m_GuildNameMap.begin();
std::advance(it, wStartOfPage);
for (unsigned char cIndex = 0; cIndex < PktGuildList::MAX_NUM_PER_PAGE && it != m_GuildNameMap.end();
++cIndex, ++it)
{
CGuild* lpGuild = it->second;
if (NULL == lpGuild) { break; }
for (unsigned short wRank = 0; wRank < wGuildNum; ++wRank)
{
if (sortVector[wRank].second == lpGuild)
{
aryCurrentInfoList[cIndex] = lpGuild->GetLargeInfo(cIndex, wRank + 1, lpMyGuild);
break;
}
}
}
break;
}
case Guild::GUILD_HOSTILITY:
{
if ( NULL != lpMyGuild )
{
unsigned short wGuildNum = static_cast<unsigned short>(m_GuildNameMap.size());
std::vector<unsigned long> sortVector;
sortVector.reserve(wGuildNum);
unsigned char cRelationByMine, cRelationByTarget ;
for (GuildNameMap::iterator it = m_GuildNameMap.begin(); it != m_GuildNameMap.end(); ++it)
{
CGuild* lpGuild = it->second;
if (NULL == lpGuild) { break; }
cRelationByMine = lpMyGuild->GetRelation( lpGuild->GetGID() ) ;
cRelationByTarget = lpGuild->GetRelation( lpMyGuild->GetGID() ) ;
if (Guild::HOSTILITY == cRelationByMine ||
Guild::ALERT_HOSTILITY == cRelationByMine ||
Guild::COUNTER_HOSTILITY == cRelationByMine ||
Guild::TARGET_NEUTRALITY == cRelationByMine ||
Guild::HOSTILITY == cRelationByTarget ||
Guild::ALERT_HOSTILITY == cRelationByTarget ||
Guild::COUNTER_HOSTILITY == cRelationByTarget ||
Guild::TARGET_NEUTRALITY == cRelationByTarget)
{
sortVector.push_back(lpGuild->GetGID());
}
}
if (true == sortVector.empty())
{
for (unsigned char cIndex = 0; cIndex < PktGuildList::MAX_NUM_PER_PAGE; ++cIndex)
{
aryCurrentInfoList[cIndex].m_dwGID = 0;
}
return true;
}
unsigned short wVectorSize = static_cast<unsigned short>(sortVector.size());
unsigned char cIndex = 0;
unsigned short wRank = wStartOfPage;
for (; cIndex < PktGuildList::MAX_NUM_PER_PAGE && wRank < wVectorSize; ++cIndex, ++wRank)
{
CGuild* lpGuild = GetGuild(sortVector[wRank]);
if (NULL == lpGuild)
{
ERRLOG1(g_Log, "GID:0x%08x 존재하지 않는 길드가 소트 벡터에 들어있습니다.", sortVector[wRank]);
break;
}
aryCurrentInfoList[cIndex] = lpGuild->GetLargeInfo(cIndex, wRank + 1, lpMyGuild);
}
}
break;
}
case Guild::GUILD_NEUTRALITY:
{
if ( NULL != lpMyGuild )
{
unsigned short wGuildNum = static_cast<unsigned short>(m_GuildNameMap.size());
std::vector<unsigned long> sortVector;
sortVector.reserve(wGuildNum);
unsigned char cRelationByMine, cRelationByTarget ;
for (GuildNameMap::iterator it = m_GuildNameMap.begin(); it != m_GuildNameMap.end(); ++it)
{
CGuild* lpGuild = it->second;
if (NULL == lpGuild) { break; }
cRelationByMine = lpMyGuild->GetRelation( lpGuild->GetGID() ) ;
cRelationByTarget = lpGuild->GetRelation( lpMyGuild->GetGID() ) ;
if (Guild::NEUTRALITY == cRelationByMine && Guild::NEUTRALITY == cRelationByTarget)
{
sortVector.push_back(lpGuild->GetGID());
}
}
if (true == sortVector.empty())
{
for (unsigned char cIndex = 0; cIndex < PktGuildList::MAX_NUM_PER_PAGE; ++cIndex)
{
aryCurrentInfoList[cIndex].m_dwGID = 0;
}
return true;
}
unsigned short wVectorSize = static_cast<unsigned short>(sortVector.size());
unsigned char cIndex = 0;
unsigned short wRank = wStartOfPage;
for (; cIndex < PktGuildList::MAX_NUM_PER_PAGE && wRank < wVectorSize; ++cIndex, ++wRank)
{
CGuild* lpGuild = GetGuild(sortVector[wRank]);
if (NULL == lpGuild)
{
ERRLOG1(g_Log, "GID:0x%08x 존재하지 않는 길드가 소트 벡터에 들어있습니다.", sortVector[wRank]);
break;
}
aryCurrentInfoList[cIndex] = lpGuild->GetLargeInfo(cIndex, wRank + 1, lpMyGuild);
}
}
break;
}
case Guild::GUILD_MINE:
{
if (NULL != lpMyGuild)
{
for (unsigned short wRank = 0; wRank < wGuildNum; ++wRank)
{
if (sortVector[wRank].second == lpMyGuild)
{
aryCurrentInfoList[0] = GuildLargeInfoNode(lpMyGuild->m_dwGID, 0, lpMyGuild->m_cInclination,
wRank + 1, lpMyGuild->m_dwFame, lpMyGuild->m_cLevel, lpMyGuild->GetCurrentMemberNum(),
lpMyGuild->GetMaster().m_strName, lpMyGuild->m_strName, lpMyGuild->m_szMark, MINE, MINE);
break;
}
}
}
break;
}
case Guild::GUILD_REQUEST:
{
for (unsigned char cIndex = 0; cIndex < PktGuildList::MAX_NUM_PER_PAGE; ++cIndex)
{
if (0 == aryCurrentInfoList[cIndex].m_dwGID) { break; }
GuildMap::iterator it = m_GuildMap.find(aryCurrentInfoList[cIndex].m_dwGID);
if (it == m_GuildMap.end())
{
// 서버와 클라이언트의 시간차로 없어진 길드 정보를 요청할 수도 있습니다. (이 경우는 무시)
aryCurrentInfoList[cIndex].m_dwGID = 0;
continue;
}
CGuild* lpGuild = it->second;
if (NULL == lpGuild) { break; }
for (unsigned short wRank = 0; wRank < wGuildNum; ++wRank)
{
if (sortVector[wRank].second == lpGuild)
{
aryCurrentInfoList[cIndex] = lpGuild->GetLargeInfo(cIndex, wRank + 1, lpMyGuild);
break;
}
}
}
break;
}
default:
{
for (unsigned char cIndex = 0; cIndex < PktGuildList::MAX_NUM_PER_PAGE; ++cIndex)
{
aryCurrentInfoList[cIndex].m_dwGID = 0;
}
return false;
}
}
return true;
}
bool CGuildMgr::SerializeIn(char* lpBuffer_In, unsigned short wBufferSize_In, unsigned char cTotalMemberNum,
unsigned char cRelationByMineNum, unsigned char cRelationByTargetNum)
{
GuildInfoDB* lpGuildInfo = reinterpret_cast<GuildInfoDB *>(lpBuffer_In);
CGuild* lpGuild = new CGuild(*lpGuildInfo);
if (false == m_GuildMap.insert(GuildMap::value_type(lpGuild->GetGID(), lpGuild)).second)
{
delete lpGuild;
return false;
}
if (false == m_GuildNameMap.insert(GuildNameMap::value_type(std::string(lpGuild->GetName()), lpGuild)).second)
{
m_GuildMap.erase(lpGuild->GetGID());
delete lpGuild;
return false;
}
int nIndex = 0;
GuildMemberDB* lpGuildMember = reinterpret_cast<GuildMemberDB *>(lpGuildInfo + 1);
for (nIndex = 0; nIndex < cTotalMemberNum; ++nIndex, ++lpGuildMember)
{
lpGuild->JoinMemberDB(*lpGuildMember);
}
GuildRelationDB* lpGuildRelation = reinterpret_cast<GuildRelationDB*>(lpGuildMember);
for (nIndex = 0; nIndex < cRelationByMineNum + cRelationByTargetNum; ++nIndex, ++lpGuildRelation)
{
lpGuild->InsertRelation(lpGuildRelation->m_dwGID, lpGuildRelation->m_dwTargetGID, lpGuildRelation->m_cRelation);
}
return true;
}
bool CGuildMgr::SerializeOut(CGuild* lpGuild_In, char* lpBuffer_Out, unsigned short& wBufferSize_Out,
unsigned char& cTotalMemberNum, unsigned char& cRelationByMineNum, unsigned char& cRelationByTargetNum)
{
wBufferSize_Out = 0;
cTotalMemberNum = cRelationByMineNum = cRelationByTargetNum = 0;
// 길드 데이터
GuildInfoDB* lpGuildInfoDB = reinterpret_cast<GuildInfoDB *>(lpBuffer_Out);
lpGuildInfoDB->m_dwGID = lpGuild_In->m_dwGID;
strncpy(lpGuildInfoDB->m_strName, lpGuild_In->m_strName, MAX_GUILD_NAME_LEN);
lpGuildInfoDB->m_cInclination = lpGuild_In->m_cInclination;
lpGuildInfoDB->m_cLevel = lpGuild_In->m_cLevel;
lpGuildInfoDB->m_dwFame = lpGuild_In->m_dwFame;
lpGuildInfoDB->m_dwGold = lpGuild_In->m_dwGold;
lpGuildInfoDB->m_tmLastLogout = lpGuild_In->m_tmLastLogout;
lpGuildInfoDB->m_tmCheckMember = lpGuild_In->m_tmCheckMember;
lpGuildInfoDB->m_tmGMLastLogout = lpGuild_In->m_tmGMLastLogout;
lpGuildInfoDB->m_tmChangeInclination = lpGuild_In->m_tmChangeInclination;
::memcpy(lpGuildInfoDB->m_szMark, lpGuild_In->m_szMark, MAX_MARK_SIZE);
::memcpy(lpGuildInfoDB->m_szRight, &lpGuild_In->m_GuildRight, sizeof(GuildRight));
wBufferSize_Out += sizeof(GuildInfoDB);
// 멤버 데이터
GuildMemberDB* lpGuildMemberDB = reinterpret_cast<GuildMemberDB *>(lpGuildInfoDB + 1);
for (MemberList::iterator MemberIt = lpGuild_In->m_MemberList.begin();
MemberIt != lpGuild_In->m_MemberList.end() && cTotalMemberNum < MAX_MEMBER_NUM;
++MemberIt, ++lpGuildMemberDB, ++cTotalMemberNum)
{
MemberInfo& memberInfo = *MemberIt;
lpGuildMemberDB->m_dwGID = lpGuild_In->m_dwGID;
lpGuildMemberDB->m_dwCID = memberInfo.m_dwCID;
strncpy(lpGuildMemberDB->m_strName, memberInfo.m_strName, MAX_MEMBER_NAME_LEN);
lpGuildMemberDB->m_dwRank = memberInfo.m_MemberListInfo.m_cRank;
lpGuildMemberDB->m_dwTitle = memberInfo.m_MemberListInfo.m_cTitle;
lpGuildMemberDB->m_cLevel = memberInfo.m_MemberListInfo.m_cLevel;
lpGuildMemberDB->m_wClass = memberInfo.m_MemberListInfo.m_cClass;
lpGuildMemberDB->m_cGuildWarFlag = memberInfo.m_MemberListInfo.m_cGuildWarFlag;
lpGuildMemberDB->m_dwFame = memberInfo.m_MemberDetailInfo.m_dwFame;
lpGuildMemberDB->m_dwGold = memberInfo.m_MemberDetailInfo.m_dwGold;
lpGuildMemberDB->m_LeaveGuildTime = memberInfo.m_LeaveGuildTime;
lpGuildMemberDB->m_cTactics = memberInfo.m_cTactics;
wBufferSize_Out += sizeof(GuildMemberDB);
}
// 관계 데이터
GuildRelationDB* lpGuildRelationDB = reinterpret_cast<GuildRelationDB *>(lpGuildMemberDB);
for (RelationMap::iterator itr = lpGuild_In->m_Relation[Guild::BY_MINE].begin();
itr != lpGuild_In->m_Relation[Guild::BY_MINE].end() && cRelationByMineNum < MAX_RELATION_NUM;
++itr, ++lpGuildRelationDB, ++cRelationByMineNum)
{
lpGuildRelationDB->m_dwGID = lpGuild_In->m_dwGID;
lpGuildRelationDB->m_dwTargetGID = itr->first;
lpGuildRelationDB->m_cRelation = itr->second;
wBufferSize_Out += sizeof(GuildRelationDB);
}
for (RelationMap::iterator itr = lpGuild_In->m_Relation[Guild::BY_TARGET].begin();
itr != lpGuild_In->m_Relation[Guild::BY_TARGET].end() && cRelationByTargetNum < MAX_RELATION_NUM;
++itr, ++lpGuildRelationDB, ++cRelationByTargetNum)
{
lpGuildRelationDB->m_dwGID = itr->first;
lpGuildRelationDB->m_dwTargetGID = lpGuild_In->m_dwGID;
lpGuildRelationDB->m_cRelation = itr->second;
wBufferSize_Out += sizeof(GuildRelationDB);
}
return true;
}

View File

@@ -0,0 +1,69 @@
#ifndef _GUILD_MGR_H_
#define _GUILD_MGR_H_
#pragma once
#include <string>
#include <Community/Guild/GuildStructure.h>
// 전방 참조
struct GuildCheckSumNode;
struct GuildLargeInfoNode;
class CCharacter;
namespace Guild
{
// 전방 참조
class CGuild;
class CGuildMgr
{
public:
static CGuildMgr& GetInstance();
CGuildMgr(void);
~CGuildMgr(void);
bool Initialize(void);
void Destroy(void);
bool SerializeIn(char* lpBuffer_In, unsigned short wBufferSize_In,
unsigned char cTotalMemberNum, unsigned char cRelationByMineNum, unsigned char cRelationByTargetNum);
bool CreateGuild(unsigned long dwMasterID, unsigned long dwGuildID, unsigned char cInclination, char* szGuildName);
bool DissolveGuild(unsigned long dwGID);
CGuild* GetGuild(unsigned long dwGID);
CGuild* GetGuild(char* szName);
bool SetRelation(unsigned long dwGID, unsigned long dwTargetGID, unsigned char cRelationType);
void ProcessGuild(void);
bool SendGuildList(unsigned long dwCID, unsigned char cSortCmd, unsigned char cPage,
unsigned char cNum, GuildCheckSumNode* lpNode);
bool SendGuildRelationInfo(unsigned long dwCID, unsigned long dwGID);
bool GetMemberList(unsigned long dwGID, const MemberList* pMemberList);
protected:
bool GetSortingPageList(CCharacter* lpCharacter, unsigned char cSortCmd, unsigned char cPage,
GuildLargeInfoNode* aryCurrentInfoList);
bool SerializeOut(CGuild* lpGuild_In, char* lpBuffer_Out, unsigned short& wBufferSize_Out,
unsigned char& cTotalMemberNum, unsigned char& cRelationByMineNum, unsigned char& cRelationByTargetNum);
GuildMap m_GuildMap; // GID를 이용한 검색용
GuildNameMap m_GuildNameMap; // 길드명 순으로 소트용
private:
static CGuildMgr ms_this;
};
};
#endif

View File

@@ -0,0 +1,78 @@
#ifndef _GUILD_STRUCTURE_H_
#define _GUILD_STRUCTURE_H_
#include <map>
#include <vector>
#include <Community/Guild/GuildConstants.h>
namespace Guild
{
// 전방 참조
class CGuild;
struct MemberListInfo
{
unsigned char m_cRank; // 서열 (한 길드내에선 유니크한 숫자. 길드마스터를 0으로 하여 순차적으로 구성)
unsigned char m_cTitle; // 직위 (Guild::Title의 상수를 참고)
unsigned char m_cLevel; // 레벨
unsigned char m_cClass; // 클래스
unsigned char m_cGuildWarFlag; // 길드전쟁 참여 플래그 (Creature::WarFlag의 상수를 참고)
MemberListInfo()
: m_cRank(0), m_cTitle(0), m_cLevel(0), m_cClass(0), m_cGuildWarFlag(0)
{ }
MemberListInfo(unsigned char cRank, unsigned char cTitle, unsigned char cLevel, unsigned char cClass, unsigned char cGuildWarFlag)
: m_cRank(cRank), m_cTitle(cTitle), m_cLevel(cLevel), m_cClass(cClass), m_cGuildWarFlag(cGuildWarFlag)
{ }
};
struct MemberDetailInfo
{
unsigned long m_dwFame;
unsigned long m_dwGold;
MemberDetailInfo()
: m_dwFame(0), m_dwGold(0)
{ }
MemberDetailInfo(unsigned long dwFame, unsigned long dwGold)
: m_dwFame(dwFame), m_dwGold(dwGold)
{ }
};
struct MemberInfo
{
// 변하지 않는 정보
unsigned long m_dwCID;
char m_strName[MAX_MEMBER_NAME_LEN];
// 따로 처리되는 정보
unsigned long m_dwServerID;
TIME m_LeaveGuildTime;
unsigned char m_cUpdateCount;
unsigned char m_cTactics;
// 가끔 변하는 정보 (변경될 때마다 업데이트)
MemberListInfo m_MemberListInfo;
// 자주 변하는 정보 (일정 횟수 이상 변경되면 업데이트)
MemberDetailInfo m_MemberDetailInfo;
MemberInfo()
: m_dwCID(0), m_dwServerID(0), m_cUpdateCount(0), m_cTactics(0), m_MemberListInfo(), m_MemberDetailInfo()
{
std::fill_n(m_strName, int(MAX_MEMBER_NAME_LEN), 0);
::memset(&m_LeaveGuildTime, 0, sizeof(TIME));
}
};
typedef std::map<unsigned long, CGuild *> GuildMap;
typedef std::map<std::string, CGuild *> GuildNameMap;
typedef std::vector<MemberInfo> MemberList;
typedef std::map<unsigned long, unsigned char> RelationMap; // <GID, cRelation>
};
#endif

View File

@@ -0,0 +1,174 @@
#ifndef _GUILD_UTIL_H_
#define _GUILD_UTIL_H_
#include "GuildMgr.h"
#include "Guild.h"
namespace Guild
{
struct FnDeleteSecond
{
template<typename PairType>
bool operator() (PairType& pair)
{
if (NULL != pair.second)
{
delete pair.second;
}
return true;
}
};
struct IsCurrentMember
{
bool operator () (const MemberInfo& first) const
{
unsigned char cTitle = first.m_MemberListInfo.m_cTitle;
return (JOIN_WAIT != cTitle && 0 == first.m_cTactics);
}
};
struct IsCurrentLoginMember
{
bool operator () (const MemberInfo& first) const
{
return (0 != first.m_dwServerID);
}
};
struct IsTacticsMember
{
bool operator () (const MemberInfo& first) const
{
return (0 != first.m_cTactics);
}
};
struct IsNotTacticsMember
{
bool operator () (const MemberInfo& first) const
{
return (0 == first.m_cTactics);
}
};
struct SumMemberFame
{
unsigned long operator () (const unsigned long init, const MemberInfo& first) const
{
return ((true == IsCurrentMember()(first)) ? init + first.m_MemberDetailInfo.m_dwFame : init);
}
};
struct CompareGuildFame
{
bool operator () (const GuildMap::value_type& lhs, const GuildMap::value_type& rhs) const
{
return lhs.second->GetFame() > rhs.second->GetFame();
}
};
struct CompareMemberName
{
bool operator () (const MemberInfo& lhs, const MemberInfo& rhs) const
{
return std::string(lhs.m_strName) < std::string(rhs.m_strName);
}
};
struct CompareMemberTitle
{
bool operator () (const MemberInfo& lhs, const MemberInfo& rhs) const
{
if (lhs.m_MemberListInfo.m_cTitle == rhs.m_MemberListInfo.m_cTitle)
{
return CompareMemberName()(lhs, rhs);
}
return lhs.m_MemberListInfo.m_cTitle < rhs.m_MemberListInfo.m_cTitle;
}
};
struct CompareMemberClass
{
bool operator () (const MemberInfo& lhs, const MemberInfo& rhs) const
{
if (lhs.m_MemberListInfo.m_cClass == rhs.m_MemberListInfo.m_cClass)
{
return CompareMemberName()(lhs, rhs);
}
return lhs.m_MemberListInfo.m_cClass < rhs.m_MemberListInfo.m_cClass;
}
};
struct CompareMemberLevel
{
bool operator () (const MemberInfo& lhs, const MemberInfo& rhs) const
{
if (lhs.m_MemberListInfo.m_cLevel == rhs.m_MemberListInfo.m_cLevel)
{
return CompareMemberName()(lhs, rhs);
}
return lhs.m_MemberListInfo.m_cLevel > rhs.m_MemberListInfo.m_cLevel;
}
};
struct CompareMemberFame
{
bool operator () (const MemberInfo& lhs, const MemberInfo& rhs) const
{
if (lhs.m_MemberDetailInfo.m_dwFame == rhs.m_MemberDetailInfo.m_dwFame)
{
return CompareMemberName()(lhs, rhs);
}
return lhs.m_MemberDetailInfo.m_dwFame > rhs.m_MemberDetailInfo.m_dwFame;
}
};
struct CompareMemberGold
{
bool operator () (const MemberInfo& lhs, const MemberInfo& rhs) const
{
if (lhs.m_MemberDetailInfo.m_dwGold == rhs.m_MemberDetailInfo.m_dwGold)
{
return CompareMemberName()(lhs, rhs);
}
return lhs.m_MemberDetailInfo.m_dwGold > rhs.m_MemberDetailInfo.m_dwGold;
}
};
struct CompareMemberPosition
{
bool operator () (const MemberInfo& lhs, const MemberInfo& rhs) const
{
if (lhs.m_dwServerID == rhs.m_dwServerID)
{
return CompareMemberName()(lhs, rhs);
}
return lhs.m_dwServerID < rhs.m_dwServerID;
}
};
struct CompareRelation
{
bool operator () (const GuildHostilityInfoNode& lhs, const GuildHostilityInfoNode& rhs) const
{
if (lhs.m_cRelationType == rhs.m_cRelationType)
{
return std::string(lhs.m_szName) < std::string(rhs.m_szName);
}
return lhs.m_cRelationType > rhs.m_cRelationType;
}
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,276 @@
#ifndef _RYL_GAME_LIBRARY_PARTY_H_
#define _RYL_GAME_LIBRARY_PARTY_H_
#include <set>
#include <boost/pool/pool_alloc.hpp>
#include <DB/DBdefine.h>
#include <Skill/Spell/PartySpellMgr.h>
#include <Network/Packet/PacketStruct/CharCommunityPacket.h>
#include <Network/Packet/PacketStruct/CharAttackPacket.h>
#include <Network/Packet/PacketStruct/PartyPacket.h>
// 전방 참조
class CCell;
class CCharacter;
class CMonster;
class CPacketBuffer;
struct Position;
namespace Item
{
class CItem;
};
namespace Quest
{
struct QuestNode;
};
#define PARTYLOG(x) (void*)0
/*
#define PARTYLOG(x) x
*/
class CParty
{
protected:
CParty();
CParty(const PARTY& PartyInfo);
CPartySpellMgr m_PartySpellMgr;
PARTY_EX m_Party;
public:
virtual ~CParty();
// ----------------------------------------------------------------------------------------
// interface
CPartySpellMgr& GetPartySpellMgr() { return m_PartySpellMgr; }
unsigned long GetUID(void) { return m_Party.m_dwPartyID; }
void SetUID(unsigned long dwPartyID) { m_Party.m_dwPartyID = dwPartyID; }
int GetMemberNum(void) { return m_Party.m_cMemberNum; }
bool IsMember(unsigned long dwMemberCID);
void ChangeName(unsigned long dwCID, const char* szChangedName);
virtual bool Join(unsigned long dwSenderCID, unsigned long dwReferenceID,
const char* strSenderName, unsigned short wMapIndex = 0) { return false; }
virtual int Leave(unsigned long dwSenderCID, unsigned long dwReferenceID = 0,
unsigned short wMapIndex = 0) { return 0; }
virtual bool Destory(unsigned long dwSenderCID, unsigned long dwReferenceID) { return false; }
virtual bool Login(unsigned long dwSenderCID, unsigned long dwReferenceID) { return false; }
virtual bool ReLogin(CCharacter* llpCharacter) { return false; }
virtual int Logout(unsigned long dwSenderCID, unsigned long dwReferenceID) { return 0; }
virtual void PrepareLogout(unsigned long dwMemberID) { }
void SetLeader(unsigned long dwLeaderCID) { m_Party.m_dwLeaderID = dwLeaderCID; }
unsigned long GetLeader(void) { return m_Party.m_dwLeaderID; }
bool IsLeader(unsigned long dwLeaderCID) { return (m_Party.m_dwLeaderID == dwLeaderCID) ? true : false; }
// 파티원 정보 업데이트 //
bool UpdatePartyMemberInfo(unsigned long dwCID, unsigned long dwGID, unsigned short wClass, char cLevel, bool bAutoRouting);
bool UpdatePartyMemberLevel(unsigned long dwCID, char cLevel);
bool UpdatePartyMemberClass(unsigned long dwCID, unsigned short wClass);
bool UpdatePartyMemberGuild(unsigned long dwCID, unsigned long dwGID);
bool UpdatePartyMemberServer(unsigned long dwCID, unsigned long dwServerID);
bool UpdatePartyMemberAutoRouting(unsigned long dwCID, bool bAutoRouting);
virtual bool TransferLeader(unsigned long dwLeaderCID) { return false; }
virtual unsigned char GetLoggedMemberAverageLevel(void) { return 0; }
virtual bool AdjustAutoRouting(unsigned long dwTargetID, bool bSwitch) { return false; }
// ----------------------------------------------------------------------------------------
// 파티원 전체에 영향을 주는 명령
virtual void MovePos(POS NewPos, char cZone, const bool bSitDown) { }
virtual void MoveZone(POS NewPos, char Zone, char Channel) { }
virtual bool AutoRouting(CAggresiveCreature* lpPickkingCreature, Item::CItem* lpItem, unsigned long& dwGold) { return false; }
virtual bool Attack(AtType attackType, CAggresiveCreature** pDefenders, unsigned char* cDefenserJudges,
CAggresiveCreature* lpOffencer, float fDistance, unsigned char cTargetType) { return false; }
// --------------------------------------------------------------------------------------
// 일정주기로 호출되는 함수
virtual void UpdateData() { };
};
class CMonsterParty : public CParty
{
public:
typedef std::set<unsigned long, std::less<unsigned long>,
boost::fast_pool_allocator<unsigned long> > PartyTargetSet;
protected:
CMonster* m_pMemberPointer[PARTY::MAX_MEM];
float m_fHelpRange; // 싸우고 있는 파티원을 도와주러 가는 거리
PartyTargetSet m_partyTargets; // 파티원이 싸우고 있는 상대의 CID
public:
CMonsterParty(const PARTY& PartyInfo, unsigned short wMapIndex = 0);
CMonsterParty();
virtual ~CMonsterParty();
enum Const
{
DEFAULT_HELP_RANGE = 36,
ERROR_OF_LEVEL_GAP = 10
};
PartyTargetSet& GetPartyTargetSet(void) { return m_partyTargets; }
float GetHelpRange(void) { return m_fHelpRange; }
unsigned short GetAvgLevel(void);
unsigned short GetHighestLevel(void);
int GetMemberTypeNum(void);
bool Join(unsigned long dwSenderCID, unsigned long dwReferenceID = 0, const char* strSenderName = NULL, unsigned short wMapIndex = 0);
int Leave(unsigned long dwSenderCID, unsigned long dwReferenceID = 0, unsigned short wMapIndex = 0);
bool Destory(unsigned long dwSenderCID, unsigned long dwReferenceID);
CMonster* GetMember(unsigned int nIndex)
{
if (nIndex < 0 || nIndex >= PARTY::MAX_MEM) return NULL;
return m_pMemberPointer[nIndex];
}
// ----------------------------------------------------------------------------------------
// 파티원 전체에 영향을 주는 명령
bool Attack(AtType attackType, CAggresiveCreature** pDefenders, unsigned char* cDefenserJudges,
CAggresiveCreature* lpOffencer, float fDistance, unsigned char cTargetType);
};
class CCharacterParty : public CParty
{
protected:
CCharacter* m_pMemberPointer[PARTY::MAX_MEM];
bool m_bAutoRouting[PARTY::MAX_MEM];
CCharacterParty* m_pHostileParty;
CCharacter* m_pFightingMember[PARTY::MAX_MEM];
PartyMemberData m_partyMemberData[PARTY::MAX_MEM];
void SendPartyCmdInfo(PktPC::PartyCmd Command, CCharacter *lpCharacter);
void SendPartyCommand(PktPC::PartyCmd Command, const char *SenderName_In, unsigned long dwSenderCID, unsigned long ReceiverCID);
static void SetUpdateFlag(PartyMemberData& partyMemberData, CCharacter& character, char*& szPacket, bool bUpdateForce);
public:
// 해외 서버에서 사용하는 상수
enum ForeignConst
{
ADDITIONAL_EXP_CAP_MIN_PARTY_MENMBER = 2, // 최소 2명 이상의 파티원이 근처에 있을 때
ADDITIONAL_EXP_CAP_PERCENTAGE = 40, // 40 % 추가 Exp Cap 보너스
} ;
CCharacterParty(const PARTY& PartyInfo, bool bCreate);
CCharacterParty();
~CCharacterParty();
// 파티원중 휴먼의 비율을 반환한다.
unsigned char GetRaceRate(void);
bool Join(unsigned long dwSenderCID, unsigned long dwReferenceID, const char* strSenderName, unsigned short wMapIndex = 0);
int Leave(unsigned long dwSenderCID, unsigned long dwReferenceID = 0, unsigned short wMapIndex = 0);
bool Destory(unsigned long dwSenderCID, unsigned long dwReferenceID);
bool Login(unsigned long dwSenderCID, unsigned long dwReferenceID);
bool ReLogin(CCharacter* llpCharacter);
int Logout(unsigned long dwSenderCID, unsigned long dwReferenceID);
void PrepareLogout(unsigned long dwMemberID);
bool TransferLeader(unsigned long dwLeaderCID);
CCharacter** GetMemberPointerList(void) { return m_pMemberPointer; }
unsigned char GetLoggedMemberAverageLevel(void);
int GetNearMemberList(CCell *pCell, bool bAutoRouting, CCharacter **aryNearCharacterList, unsigned char &cHighestLevel);
bool AdjustAutoRouting(unsigned long dwTargetID, bool bSwitch);
// ----------------------------------------------------------------------------------------
// 팀배틀 관련 메소드
CCharacterParty* GetHostileParty(void) { return m_pHostileParty; }
bool StartTeamBattle(CCharacterParty* pHostileParty);
int DropMember(CCharacter* pDropMember, PktDuC::DuelCmd eCmd);
void EndTeamBattle(void);
bool MakeTeamBattleInfo(char* szPacket, unsigned short& dwPacketSize, CCharacter* pChallenger, unsigned char cCmd);
// ----------------------------------------------------------------------------------------
// 파티원 전체에 영향을 주는 명령
void MovePos(POS NewPos, char cZone, const bool bSitDown);
void MoveZone(POS NewPos, char Zone, char Channel);
bool AutoRouting(CAggresiveCreature* lpPickkingCreature, Item::CItem* lpItem, unsigned long& dwGold);
bool Attack(AtType attackType, CAggresiveCreature** pDefenders, unsigned char* cDefenserJudges,
CAggresiveCreature* lpOffencer, float fDistance, unsigned char cTargetType);
// ----------------------------------------------------------------------------------------
// 파티 퀘스트 관련 함수
bool StartQuest(Quest::QuestNode* lpQuestNode, const Position& CenterPos, unsigned long dwNPCID);
bool CheckTrigger(unsigned long dwExceptCID, unsigned char cTriggerKind, unsigned long dwReferenceID, Position Pos, short wCount);
void OperateTrigger(unsigned short wQuestID, unsigned char cPhase, unsigned char cTrigger, unsigned char cCount, Position Pos);
// ----------------------------------------------------------------------------------------
// Send 관련
void SendPartyInfo(CCharacter *lpCharacter);
void SendAllLoggedMember(const char* szPacket, unsigned long dwPacketSize, unsigned long dwExclusion, unsigned char cCMD_In);
void SendNotNearCellMember(const char* szPacket, unsigned long dwPacketSize, CCell* lpCell, unsigned char cCMD_In);
void SendDivisionExp(CCharacter* lpCharacter, CAggresiveCreature* lpDeadCreature, unsigned long dwExp, int nStandardLevel);
void SendDropMember(CCharacter* pDropMember, PktDuC::DuelCmd eCmd);
void SendRecall(CCharacter* lpCaster);
void SendAutoRouting(unsigned long dwCharID, unsigned short wItemID, unsigned char cCmd);
void SendPartyAddress(unsigned long dwCharID, const SOCKADDR_IN& PublicAddress, const SOCKADDR_IN& PrivateAddress);
void SendPartyMemberDataToDBAgent(unsigned long dwSenderID, unsigned long dwGID, unsigned short wClass, unsigned long dwServerID,
char cLevel, const char* strSenderName, unsigned short usCmd);
void SendDivisionFame(CCharacter* lpWinnerCharacter, CCharacter* lpLoserCharacter, unsigned char cCmd, unsigned char cAcquireAmount);
// 캐릭터 정보가 업데이트 되었을 때 다른 존에 있는 캐릭터 정보를 보내는 함수
void SendMemberInfoNZone(const char* szPacket, unsigned long dwPacketSize, unsigned long dwSenderID, unsigned char cCMD_In);
// 캐릭터 접속시 다른 존에 있는 캐릭터 정보를 보내는 함수
bool SendMemberInfoAllNZone(unsigned long dwCID);
// 로그아웃이나 삭제 메세지를 보낸다.
bool SendMemberInfoOutNZone(unsigned long dwCID, unsigned short wCmd);
// 일정 주기로 호출되는 함수 (여기서는 캐릭터 좌표 및 기타 등의 정보를 업데이트 해준다.)
void UpdateData();
};
#endif

View File

@@ -0,0 +1,260 @@
#include "stdafx.h"
#include <Creature/CreatureManager.h>
#include <Creature/Character/Character.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CharCommunityPacket.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Stream/SendStream.h>
#include "PartyMgr.h"
CPartyMgr& CPartyMgr::GetInstance()
{
static CPartyMgr partyMgr;
return partyMgr;
}
CPartyMgr::CPartyMgr()
: m_tempUID(0)
{
m_PartyMap.clear();
m_MemberFindPartyMap.clear();
m_PartyFindMemberMap.clear();
}
CPartyMgr::~CPartyMgr()
{
DestoryPartyList();
}
void CPartyMgr::DestoryPartyList()
{
// ERRLOG1(g_Log, "EDITH :%d 파티가 존재합니다.", m_PartyMap.size());
for (PartyMap::iterator it = m_PartyMap.begin(); it != m_PartyMap.end(); ++it)
{
delete it->second;
}
m_PartyMap.clear();
// edith 2008.05.29 파티 맵 초기화 추가
m_MemberFindPartyMap.clear();
m_PartyFindMemberMap.clear();
}
void CPartyMgr::UpdatePartyData()
{
PartyMap::iterator pos = m_PartyMap.begin();
PartyMap::iterator end = m_PartyMap.end();
for (; pos != end; ++pos)
{
pos->second->UpdateData();
}
}
bool CPartyMgr::RequestPartyInfoToDB(unsigned long dwLeaderCID, unsigned long dwPartyUID)
{
return true;
}
bool CPartyMgr::AddParty(CParty *pParty)
{
unsigned long dwPartyUID = pParty->GetUID();
PartyMap::iterator it = m_PartyMap.find(dwPartyUID);
if (it != m_PartyMap.end()) {
return false;
}
// edith 2008.05.29 파티맵 수정
// m_PartyMap.insert(PartyMap::value_type(pParty->GetUID(), pParty)).second;
m_PartyMap.insert(PartyMap::value_type(pParty->GetUID(), pParty));
return GetParty(dwPartyUID);
}
CParty* CPartyMgr::GetParty(unsigned long dwPartyUID)
{
PartyMap::iterator it = m_PartyMap.find(dwPartyUID);
if (it == m_PartyMap.end()) {
return NULL;
}
return it->second;
}
bool CPartyMgr::DeleteParty(unsigned long dwPartyUID)
{
PartyMap::iterator it = m_PartyMap.find(dwPartyUID);
if (it == m_PartyMap.end()) {
return false;
}
delete it->second;
m_PartyMap.erase(it);
return true;
}
bool CPartyMgr::AddFindPartyList(unsigned long dwCID)
{
MemberFindPartyMap::iterator it = m_MemberFindPartyMap.find(dwCID);
if (it != m_MemberFindPartyMap.end()) {
return false;
}
CCharacter* pCharacter = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (NULL == pCharacter) {
return false;
}
return m_MemberFindPartyMap.insert(MemberFindPartyMap::value_type(dwCID, pCharacter)).second;
}
bool CPartyMgr::DeleteFindPartyList(unsigned long dwCID)
{
MemberFindPartyMap::iterator it = m_MemberFindPartyMap.find(dwCID);
if (it == m_MemberFindPartyMap.end()) {
return false;
}
m_MemberFindPartyMap.erase(it);
return true;
}
bool CPartyMgr::AddFindMemberList(unsigned long dwPartyUID)
{
PartyFindMemberMap::iterator it = m_PartyFindMemberMap.find(dwPartyUID);
if (it != m_PartyFindMemberMap.end())
{
ERRLOG1(g_Log, "PID:0x%08x 파티원 찾기 리스트에 이미 있는 파티를 추가하려 합니다.", dwPartyUID);
return false;
}
CParty* pParty = CPartyMgr::GetInstance().GetParty(dwPartyUID);
if (NULL == pParty)
{
ERRLOG1(g_Log, "PID:0x%08x 존재하지 않는 파티입니다.", dwPartyUID);
return false;
}
return m_PartyFindMemberMap.insert(PartyFindMemberMap::value_type(dwPartyUID, pParty)).second;
}
bool CPartyMgr::DeleteFindMemberList(unsigned long dwPartyUID)
{
PartyFindMemberMap::iterator it = m_PartyFindMemberMap.find(dwPartyUID);
if (it == m_PartyFindMemberMap.end())
{
return false;
}
m_PartyFindMemberMap.erase(it);
return true;
}
void CPartyMgr::SendPartyFind(CCharacter* lpCharacter)
{
if (NULL != lpCharacter)
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
const int MAX_BUFFER_SIZE = sizeof(MemberFindParty) * PktPFAck::MAX_LIST +
sizeof(PartyFindMember) * PktPFAck::MAX_LIST + sizeof(PktPFAck);
char szBuffer[MAX_BUFFER_SIZE];
char* lpBufferPos = szBuffer + sizeof(PktPFAck);
PktPFAck* lpPktPFAck = reinterpret_cast<PktPFAck*>(szBuffer);
int nMemberFindPartyNum = 0;
int nPartyFindMemberNum = 0;
const int nMyLevel = lpCharacter->GetStatus().m_nLevel;
for (MemberFindPartyMap::iterator it = m_MemberFindPartyMap.begin();
it != m_MemberFindPartyMap.end() && nMemberFindPartyNum < PktPFAck::MAX_LIST; ++it)
{
CCharacter* lpEntryCharacter = it->second;
if (NULL != lpEntryCharacter)// && lpCharacter != lpEntryCharacter)
{
const int nEntryCharLevel = lpEntryCharacter->GetStatus().m_nLevel;
if (::abs(nEntryCharLevel - nMyLevel) <= PktPFAck::MAX_DIFF_LEVEL_VIEW &&
EnemyCheck::EC_ENEMY != lpCharacter->IsEnemy(lpEntryCharacter))
{
MemberFindParty* lpEntryMemberFindParty = reinterpret_cast<MemberFindParty*>(lpBufferPos);
strncpy(lpEntryMemberFindParty->m_strName, lpEntryCharacter->GetCharacterName(), CHAR_INFOST::MAX_NAME_LEN);
lpEntryMemberFindParty->m_cLevel= static_cast<unsigned char>(nEntryCharLevel);
lpEntryMemberFindParty->m_cClass = static_cast<unsigned char>(lpEntryCharacter->GetClass());
lpBufferPos += sizeof(MemberFindParty);
++nMemberFindPartyNum;
}
}
}
for (PartyFindMemberMap::iterator it = m_PartyFindMemberMap.begin();
it != m_PartyFindMemberMap.end() && nPartyFindMemberNum < PktPFAck::MAX_LIST; ++it)
{
CParty* lpEntryParty = it->second;
// 자기가 속한 파티는 제외
if (NULL != lpEntryParty ) // && !lpEntryParty->IsMember(lpCharacter->GetCID()))
{
const int nAverageLevel = lpEntryParty->GetLoggedMemberAverageLevel();
if (::abs(nAverageLevel - nMyLevel) <= PktPFAck::MAX_DIFF_LEVEL_VIEW)
{
PartyFindMember* lpEntryPartyFindMember = reinterpret_cast<PartyFindMember*>(lpBufferPos);
CCharacter* lpLeaderCharacter = CCreatureManager::GetInstance().GetCharacter(lpEntryParty->GetLeader());
if (NULL != lpLeaderCharacter)
{
if (EnemyCheck::EC_ENEMY != lpCharacter->IsEnemy(lpLeaderCharacter))
{
strncpy(lpEntryPartyFindMember->m_strLeaderName,
lpLeaderCharacter->GetCharacterName(), CHAR_INFOST::MAX_NAME_LEN);
lpEntryPartyFindMember->m_cAverageLevel = static_cast<unsigned char>(nAverageLevel);
lpEntryPartyFindMember->m_cMemberNum = lpEntryParty->GetMemberNum();
lpBufferPos += sizeof(PartyFindMember);
++nPartyFindMemberNum;
}
}
}
}
}
const unsigned short usPacketSize = static_cast<unsigned short>(sizeof(PktPFAck) +
sizeof(MemberFindParty) * nMemberFindPartyNum + sizeof(PartyFindMember) * nPartyFindMemberNum);
lpPktPFAck->m_dwCharID = lpCharacter->GetCID();
lpPktPFAck->m_cMemberFindPartyNum = nMemberFindPartyNum;
lpPktPFAck->m_cPartyFindMemberNum = nPartyFindMemberNum;
lpDispatch->GetSendStream().WrapCompress(szBuffer, usPacketSize, CmdCharPartyFind, 0, 0);
}
}
}

View File

@@ -0,0 +1,57 @@
#ifndef _RYL_GAME_LIBRARY_PARTY_MGR_H_
#define _RYL_GAME_LIBRARY_PARTY_MGR_H_
#include <Thread/Lock.h>
#include <map>
#include <boost/pool/pool_alloc.hpp>
#include "Party.h"
class CPartyMgr
{
public:
static CPartyMgr& GetInstance();
void DestoryPartyList();
bool RequestPartyInfoToDB(unsigned long dwLeaderCID, unsigned long dwPartyUID);
bool AddParty(CParty *pParty);
CParty* GetParty(unsigned long dwPartyUID);
bool DeleteParty(unsigned long dwPartyUID);
bool AddFindPartyList(unsigned long dwCID);
bool DeleteFindPartyList(unsigned long dwCID);
bool AddFindMemberList(unsigned long dwPartyUID);
bool DeleteFindMemberList(unsigned long dwPartyUID);
unsigned long GetTempUID(void) { m_tempUID++; return m_tempUID; }
void SendPartyFind(CCharacter* pCharacter);
void UpdatePartyData();
protected:
CPartyMgr();
~CPartyMgr();
typedef std::map<unsigned long, CParty*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CParty* > > > PartyMap;
typedef std::map<unsigned long, CCharacter*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CCharacter* > > > MemberFindPartyMap;
typedef std::map<unsigned long, CParty*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CParty* > > > PartyFindMemberMap;
PartyMap m_PartyMap;
MemberFindPartyMap m_MemberFindPartyMap;
PartyFindMemberMap m_PartyFindMemberMap;
unsigned long m_tempUID;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
#ifndef _CAGGRESIVE_CREATURE_H_
#define _CAGGRESIVE_CREATURE_H_
#include <Thread/Lock.h>
#include <Network/Packet/PacketStruct/CharAttackPacketStruct.h>
#include <Creature/Character/CharacterStructure.h>
#include <Utility/Time/Pulse/Pulse.h>
#include <Utility/Setup/ServerSetup.h>
#include <Skill/SkillStructure.h>
#include <Skill/Spell/SpellMgr.h>
#include "Threat.h"
#include "Creature.h"
// 전방 참조
class CParty;
struct TakeType;
namespace Skill
{
class CFunctions;
}
// Creature가 있는 Cell에 대한정보 (셀 좌표와 셀을 가리키는 포인터)
struct CellPosition
{
CCell* m_lpCell;
unsigned short m_wMapIndex;
unsigned char m_cCellX;
unsigned char m_cCellZ;
unsigned short m_wInX;
unsigned short m_wInZ;
CellPosition();
CellPosition(const Position& WorldPos);
bool MoveTo(const Position& WorldPos);
};
class CAggresiveCreature : public CCreature
{
public:
enum Const
{
REGEN_TIME = 50, // HP/MP 회복 주기
MAX_LEVEL_GAP = 20, // 최대 레벨갭
AGGRAVATION_NUM = 41, // 레벨갭에 의한 가중치 범위 (-20 ~ 20)
MAX_CASTE = 10 // 최대 계급
};
CAggresiveCreature(unsigned long dwCID);
virtual ~CAggresiveCreature();
// ------------------------------------------------------------------------
// 위치 관련
const CellPosition& GetCellPos(void) const { return m_CellPos; }
void SetMapIndex(unsigned short wMapIndex) { m_CellPos.m_wMapIndex = wMapIndex; }
const unsigned short GetMapIndex(void) const { return m_CellPos.m_wMapIndex; }
float CalcDir2D(const float fSrcX, const float fSrcY, const float fDstX, const float fDstY);
// 이동 (크리쳐있을때)
MoveInfo MoveTo(const Position& NewPosition, bool bSitDown); // 앉아 있으면 bSitMode가 true
// ------------------------------------------------------------------------
// 챈트/인챈트 관련
void CalculateEnchantStatus(void)
{
m_EquipStatus.CalculateEnchantInfo(m_aryEnchantLevel, m_CreatureStatus.m_StatusInfo);
}
void SetEnchantLevel(Skill::SpellID::Type eSpellType, unsigned short wLevel)
{
m_aryEnchantLevel[eSpellType] = wLevel;
}
unsigned short GetEnchantLevel(Skill::SpellID::Type eSpellType)
{
return m_aryEnchantLevel[eSpellType];
}
void GetEnchantLevel(unsigned short* wEnchantLevel)
{
std::copy(&(m_aryEnchantLevel[0]), &(m_aryEnchantLevel[Skill::SpellID::MAX_SPELL_TYPE]), wEnchantLevel);
}
// ------------------------------------------------------------------------
// 레벨갭 관련
virtual const int CalculateFixLevelGap(CAggresiveCreature *pDefender) = 0;
const int CalculateLevelGap(CAggresiveCreature *pDefender);
const int CalculateLevelGap(int nOffencerLevel, int nDefenderLevel);
const float CalculateLevelGapAffect(CAggresiveCreature *pDefender);
// ------------------------------------------------------------------------
// 전투 관련
bool MultiAttack(AtType attackType, unsigned char nDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges,
Position CenterPos, float fDir, float nRange, float fAngle, char cTargetType);
unsigned short MeleeAttack(CAggresiveCreature* lpTarget, Creature::StatusType eHandType,
const float fLevelGap, unsigned char &cDefenserJudge, unsigned int ExType = 0);
virtual unsigned short ApplyDamage(AtType attackType, CAggresiveCreature* pOffencer, unsigned char &cOffencerJudge,
unsigned char &cDefenserJudge, unsigned short& wOffencerMPHeal, unsigned short& wDefenserMPHeal, unsigned short &wError);
virtual bool CalculateEquipDurability(unsigned short wAttackType) { return true; }
virtual bool Attack(AtType attackType, unsigned char nDefenderNum, CAggresiveCreature** pDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal) = 0;
virtual bool AttackUsingBow(unsigned short wType) { return true; }
virtual bool RegenHPAndMP(unsigned short usAdditionalHP, unsigned short usAdditionalMP, bool bAddDefaultRegenValue);
inline virtual bool Dead(CAggresiveCreature* pOffencer);
// ------------------------------------------------------------------------
// 기타 Get/Set 함수
void SetParty(CParty* pParty) { m_pParty = pParty; }
CParty* GetParty(void) { return m_pParty; }
CharacterStatus& GetCharStatus(void) { return m_CharacterStatus; }
CreatureStatus& GetStatus(void) { return m_CreatureStatus; }
CThreat& GetThreat(void) { return m_Threat; }
virtual const FightStatus& GetEtcTypeStatus(Creature::StatusType eType) { return m_EquipStatus; }
virtual bool HasSkill(unsigned short usSkillType, unsigned char cLockCount, unsigned char cLevel) { return true; }
virtual short GetSkillLockCount(unsigned short usSkillType) { return 0; }
virtual char GetConsumeMPCount(void) { return 0; }
virtual void ConsumeMPCount(void) { }
virtual unsigned long GetUID(void) const { return 0; }
virtual const unsigned long GetFame(void) { return 0; }
virtual char GetEliteBonus(void) { return 0; }
virtual unsigned short GetClass(void) { return 0; }
virtual CAggresiveCreature* GetDuelOpponent(void) { return NULL; }
virtual bool IsPeaceMode(void) { return false; }
virtual bool IsRideArms(void) const { return false; }
virtual unsigned long GetGID(void) const { return 0; }
virtual unsigned short GetObjectType() const { return 0; }
virtual EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget, unsigned char* cResult = NULL) = 0;
virtual unsigned char GetNation(void) const = 0;
virtual void SaveSpell(BOOL bDead = FALSE) { return ; }
inline CSpellMgr& GetSpellMgr(void) { return m_SpellMgr; }
bool IsLogout(void) const { return m_bLogout; }
virtual bool ItemDump(char* pBuffer, int* nBufferSize_InOut) const { return true; }
virtual Item::CItem* SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError) = 0;
virtual unsigned char GetRealmPoint(void) = 0;
friend class Skill::CFunctions;
protected:
unsigned long m_dwLastTime; // 마지막 체크 시간(사망/부활)
CParty* m_pParty; // 파티 정보를 가리키는 포인터
CThreat m_Threat; // 스레트 리스트
CharacterStatus m_CharacterStatus; // 캐릭터가 갖는 STR, INT, DEX 등의 능력치
CreatureStatus m_CreatureStatus; // 실제 전투 등 수식에서 사용되는 능력치
FightStatus m_EquipStatus; // 기본 스탯, 장비 효과까지 적용된 능력치
CSpellMgr m_SpellMgr;
unsigned short m_aryEnchantLevel[Skill::SpellID::MAX_SPELL_TYPE];
CellPosition m_CellPos; // 셀 위치
bool m_bLogout;
bool m_bSitDown;
bool m_bPadding[2];
unsigned short CalculateDamage(const FightStatus& OffencerStatusInfo, const float fLevelGap, unsigned char &cDefenserJudge, unsigned int ExType = 0);
};
inline bool CAggresiveCreature::Dead(CAggresiveCreature* pOffencer)
{
m_dwLastTime = CPulse::GetInstance().GetLastTick();
// Enchant는 전부 없애버린다.
m_SpellMgr.GetAffectedInfo().Disenchant(Skill::SpellType::NONE,
Skill::SpellTarget::ALL_ENCHANT, Skill::Disenchant::NONE, 1, Skill::Disenchant::INFINITE_NUM);
m_SpellMgr.GetAffectedInfo().Disenchant(Skill::SpellType::ETERNAL_SPELL,
Skill::SpellTarget::ALL_ENCHANT, Skill::Disenchant::NONE, 1, Skill::Disenchant::INFINITE_NUM);
m_SpellMgr.GetCastingInfo().ClearEnchant();
// Chant는, 내가 건 스킬만 Disable한다. ( 나에게 걸린건 굳이 Disable할 필요 없다. )
m_SpellMgr.GetCastingInfo().ClearChant();
return true;
}
#endif

View File

@@ -0,0 +1,726 @@
#include "stdafx.h"
#include "CharRespawnMgr.h"
#include <Utility/Math/Math.h>
#include <Utility/DelimitedFile.h>
#include <Utility/Setup/ServerSetup.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/Character.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/Camp.h>
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
#include <GameTime/GameTimeMgr.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Packet/PacketStruct/CharAttackPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/WrapPacket.h>
const char* CCharRespawnMgr::ms_szRespawnScriptFileName = "./Script/Game/RespawnScript.txt";
CCharRespawnMgr& CCharRespawnMgr::GetInstance()
{
static CCharRespawnMgr ms_this;
return ms_this;
}
CCharRespawnMgr::CCharRespawnMgr()
{
}
CCharRespawnMgr::~CCharRespawnMgr()
{
m_ProtoTypeList.clear();
m_AllZoneTownList.clear();
}
bool CCharRespawnMgr::LoadRespawnFromFile(const char* szFileName)
{
int nLineCount = 0;
char strTemp[MAX_PATH];
CDelimitedFile DelimitedFile; // 객체 소멸시, 자동 Close.
RespawnProtoType tempProtoType;
Position tempRespawnPos;
m_ProtoTypeList.clear();
m_AllZoneTownList.clear();
// 매크로에 로그 코드 삽입을 잊지 말 것.
// 매크로에서 \뒤에 공백이나 문자 삽입되지 않도록 주의할 것.
// ( '이스케이프 시퀀스가 잘못되었습니다' 에러 발생 )
#define READ_DATA(ColumnName, Argument) \
if (!DelimitedFile.ReadData(Argument)) { \
ERRLOG2(g_Log, "리스폰 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
}
#define READ_STRING(ColumnName, Buffer, BufferSize) \
if (!DelimitedFile.ReadString(Buffer, BufferSize)) { \
ERRLOG2(g_Log, "리스폰 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
}
if (!DelimitedFile.Open(szFileName ? szFileName : ms_szRespawnScriptFileName))
{
ERRLOG1(g_Log, "%s 파일을 열 수 없습니다.", szFileName ? szFileName : ms_szRespawnScriptFileName);
return false;
}
while (DelimitedFile.ReadLine())
{
++nLineCount;
// 순서가 바뀌면 곤란하다니깐~!!! (버럭!)
tempProtoType.m_dwGID = 0;
READ_STRING("TownID", strTemp, MAX_PATH);
tempProtoType.m_dwTownID = Math::Convert::Atoi(strTemp);
READ_DATA("Nation", tempProtoType.m_cNation);
READ_DATA("Zone", tempProtoType.m_cZone);
int iRespawnPosCount = 0;
READ_DATA("RespawnPosCount", iRespawnPosCount);
if (CServerSetup::GetInstance().GetServerZone() == tempProtoType.m_cZone)
{
for (int i=0; i<iRespawnPosCount; ++i)
{
READ_DATA("RespawnPosX", tempRespawnPos.m_fPointX);
READ_DATA("RespawnPosY", tempRespawnPos.m_fPointY);
READ_DATA("RespawnPosZ", tempRespawnPos.m_fPointZ);
tempProtoType.m_RespawnPosList.push_back(tempRespawnPos);
}
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(tempProtoType.m_dwTownID);
if (itr == m_ProtoTypeList.end())
{
m_ProtoTypeList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
// 마을일 경우 모든 존의 마을 리스폰 정보 맵에 집어 넣는다.
if (0 != (tempProtoType.m_dwTownID & Castle::TOWN_BIT))
{
m_AllZoneTownList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
}
}
else
{
ERRLOG1(g_Log, "리스폰 스크립트에 마을 혹은 성의 ID가 같은 것이 두개이상 존재합니다. TownID : %d", tempProtoType.m_dwTownID);
return false;
}
RespawnCharMap::iterator CharItr = m_CharList.find(tempProtoType.m_dwTownID);
if (CharItr == m_CharList.end())
{
RespawnCharList tempRespawnCharList;
tempRespawnCharList.clear();
m_CharList.insert(std::make_pair(tempProtoType.m_dwTownID, tempRespawnCharList));
}
}
else
{
// 마을일 경우 모든 존의 마을 리스폰 정보 맵에 집어 넣는다.
if (0 != (tempProtoType.m_dwTownID & Castle::TOWN_BIT))
{
for (int i=0; i<iRespawnPosCount; ++i)
{
READ_DATA("RespawnPosX", tempRespawnPos.m_fPointX);
READ_DATA("RespawnPosY", tempRespawnPos.m_fPointY);
READ_DATA("RespawnPosZ", tempRespawnPos.m_fPointZ);
tempProtoType.m_RespawnPosList.push_back(tempRespawnPos);
}
RespawnProtoTypeMap::iterator itr = m_AllZoneTownList.find(tempProtoType.m_dwTownID);
if (itr == m_AllZoneTownList.end())
{
m_AllZoneTownList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
}
else
{
ERRLOG1(g_Log, "리스폰 스크립트에 마을 혹은 성의 ID가 같은 것이 두개이상 존재합니다. TownID : %d", tempProtoType.m_dwTownID);
return false;
}
}
}
tempProtoType.m_RespawnPosList.clear();
}
return true;
}
bool CCharRespawnMgr::AddCampRespawnPoint(unsigned long dwCampID, unsigned long dwGID, const Position& RespawnPos)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwCampID);
if (itr != m_ProtoTypeList.end())
{
ERRLOG1(g_Log, "CampID : 0x%08x 이미 존재하는 리스폰 포인트(길드 요새) 입니다.", dwCampID);
return false;
}
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(dwGID);
if (lpGuild)
{
RespawnProtoType tempProtoType;
tempProtoType.m_dwTownID = dwCampID;
tempProtoType.m_dwGID = dwGID;
tempProtoType.m_cNation = lpGuild->GetNation();
tempProtoType.m_cZone = CServerSetup::GetInstance().GetServerZone();
tempProtoType.m_RespawnPosList.push_back(RespawnPos);
m_ProtoTypeList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
RespawnCharList tempCharList;
tempCharList.clear();
m_CharList.insert(std::make_pair(tempProtoType.m_dwTownID, tempCharList));
return true;
}
return false;
}
bool CCharRespawnMgr::DeleteCampRespawnPoint(unsigned long dwCampID)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwCampID);
if (itr != m_ProtoTypeList.end())
{
m_ProtoTypeList.erase(itr);
return true;
}
ERRLOG1(g_Log, "CampID : 0x%08x 존재하지 않는 리스폰 포인트(길드 요새) 입니다.", dwCampID);
return false;
}
bool CCharRespawnMgr::AddRealmStatueRespawnPoint(unsigned long dwIndex, unsigned char cNation, const Position& RespawnPos)
{
unsigned long dwStatueID = Castle::STATUE_BIT | (dwIndex << 16);
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwStatueID);
if (itr != m_ProtoTypeList.end())
{
ERRLOG2(g_Log, "Statue ID : 0x%08x (0x%08x) 이미 존재하는 리스폰 포인트(다크 카나번 석상) 입니다.", dwStatueID, dwIndex);
return false;
}
RespawnProtoType tempProtoType;
tempProtoType.m_dwTownID = dwStatueID;
tempProtoType.m_dwGID = 0;
tempProtoType.m_cNation = cNation;
tempProtoType.m_cZone = CServerSetup::GetInstance().GetServerZone();
tempProtoType.m_RespawnPosList.push_back(RespawnPos);
m_ProtoTypeList.insert(std::make_pair(tempProtoType.m_dwTownID, tempProtoType));
RespawnCharList tempCharList;
tempCharList.clear();
m_CharList.insert(std::make_pair(tempProtoType.m_dwTownID, tempCharList));
return true;
}
bool CCharRespawnMgr::DeleteRealmStatueRespawnPoint(unsigned long dwIndex)
{
unsigned long dwStatueID = Castle::STATUE_BIT | (dwIndex << 16);
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwStatueID);
if (itr != m_ProtoTypeList.end())
{
m_ProtoTypeList.erase(itr);
return true;
}
ERRLOG2(g_Log, "Statue ID : 0x%08x (0x%08x) 존재하지 않는 리스폰 포인트(다크 카나번 석상) 입니다.", dwStatueID, dwIndex);
return false;
}
bool CCharRespawnMgr::SetCastleRespawnPointNation(unsigned long dwCastleNameID, unsigned char cNation)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.begin();
RespawnProtoTypeMap::iterator end = m_ProtoTypeList.end();
bool bRetFound = false;
while (itr != end)
{
RespawnProtoType& protoType = itr->second;
if (0 != (Castle::CASTLE_BIT & protoType.m_dwTownID))
{
unsigned long dwMask = dwCastleNameID << Castle::CASTLE_NAME_BIT_SHIFT;
if (dwMask == (dwMask & protoType.m_dwTownID))
{
if (0 == (Castle::SIEGE_BIT & protoType.m_dwTownID))
{
// 수성
bRetFound = true;
protoType.m_cNation = cNation;
}
else
{
// 공성
switch (cNation)
{
case Creature::KARTERANT:
protoType.m_cNation = Creature::MERKADIA;
break;
case Creature::MERKADIA:
protoType.m_cNation = Creature::KARTERANT;
break;
case Creature::ALMIGHTY_PIRATE:
case Creature::STATELESS:
default:
protoType.m_cNation = Creature::STATELESS;
break;
}
}
}
}
++itr;
}
if (false == bRetFound)
{
ERRLOG1(g_Log, "CastleNameID : %d 존재하지 않는 리스폰 포인트(성) 입니다.", dwCastleNameID);
}
return bRetFound;
}
/*
void CCharRespawnMgr::DelCharacter(unsigned long dwTownID, unsigned long dwCID)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwTownID);
if (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
RespawnCharMap::iterator it = m_CharList.find(dwTownID);
if (it != m_CharList.end())
{
RespawnCharList& tempCharList = it->second;
RespawnCharList::iterator CharListItr = tempCharList.begin();
while (CharListItr != tempCharList.end())
{
RespawnCharInfo& tempCharInfo = *CharListItr;
if(tempCharInfo.m_dwCID == dwCID)
{
CharListItr = tempCharList.erase(CharListItr);
return;
}
++CharListItr;
}
}
}
}
*/
CCharRespawnMgr::eReturn CCharRespawnMgr::AddCharacter(unsigned long dwTownID, unsigned long dwCID)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwTownID);
if (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
if (0 != (dwTownID & Castle::ABTOWN_BIT))
{
return RET_TOWN;
}
if (0 != (dwTownID & Castle::TOWN_BIT))
{
return RET_TOWN;
}
RespawnCharMap::iterator it = m_CharList.find(dwTownID);
if (it != m_CharList.end())
{
unsigned long dwRemainSec = 0;
RespawnCharList& tempCharList = it->second;
if (0 == tempCharList.size())
{
dwRemainSec = static_cast<unsigned long>(RESPAWN_DELAY_SEC);
}
else
{
RespawnCharList::reverse_iterator CharInfoItr = tempCharList.rbegin();
RespawnCharInfo& tempCharInfo = *CharInfoItr;
dwRemainSec = static_cast<unsigned long>(tempCharInfo.m_dwRemainSec + RESPAWN_DELAY_SEC);
}
tempCharList.push_back(RespawnCharInfo(dwCID, dwRemainSec));
return RET_TRUE;
}
++itr;
}
return RET_FALSE;
}
void CCharRespawnMgr::ProcessRespawn()
{
RespawnCharMap::iterator itr = m_CharList.begin();
while (itr != m_CharList.end())
{
if (0 != (itr->first & Castle::TOWN_BIT))
{
// 마을은 그냥 넘어간다.
++itr;
continue;
}
RespawnCharList& tempCharList = itr->second;
if (tempCharList.size() > 0)
{
RespawnCharList::iterator CharListItr = tempCharList.begin();
while (CharListItr != tempCharList.end())
{
RespawnCharInfo& tempCharInfo = *CharListItr;
if (0 < tempCharInfo.m_dwRemainSec)
{
--tempCharInfo.m_dwRemainSec;
// 2000초(약 30분). 이 숫자를 넘으면 로그를 찍는다
if (2000 < tempCharInfo.m_dwRemainSec)
{
ERRLOG2(g_Log, "CID:0x%08x/RemainSec:%u/리스폰 시간 이상",
tempCharInfo.m_dwCID, tempCharInfo.m_dwRemainSec);
}
}
else if (0 == tempCharInfo.m_dwRemainSec)
{
// 리스폰 시키고, 하나를 빼낸다.
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(tempCharInfo.m_dwCID);
if (lpCharacter)
{
if(lpCharacter->GetRespawnTownID() == 0)
{
CharListItr = tempCharList.erase(CharListItr);
continue;
}
RespawnProtoTypeMap::iterator ProtoTypeItr = m_ProtoTypeList.find(itr->first);
if (ProtoTypeItr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = ProtoTypeItr->second;
size_t iIndex = Math::Random::ComplexRandom(static_cast<int>(tempProtoType.m_RespawnPosList.size()));
Position resapwnPos = tempProtoType.m_RespawnPosList[iIndex];
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
lpCharacter->Respawn(CCharRespawnMgr::RST_FORTRESS, resapwnPos);
if (0 != lpCharacter->GetPID())
{
// 파티원 리스폰을 알린다.
GameClientSendPacket::SendCharDeadToParty(lpCharacter, 0, PktDeadInfo::RESPAWN);
}
}
else
{
Position resapwnPos = GetDefaultRespawnPos(lpCharacter->GetNation());
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
lpCharacter->Respawn(CCharRespawnMgr::RST_FORTRESS, resapwnPos);
if (0 != lpCharacter->GetPID())
{
// 파티원 리스폰을 알린다.
GameClientSendPacket::SendCharDeadToParty(lpCharacter, 0, PktDeadInfo::RESPAWN);
}
}
CharListItr = tempCharList.erase(CharListItr);
continue;
}
}
++CharListItr;
}
}
++itr;
}
}
const Position CCharRespawnMgr::GetDefaultRespawnPos(unsigned char cNation)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.begin();
while (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
if (0 != (tempProtoType.m_dwTownID & Castle::TOWN_BIT))
{
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
int iRespawnPosCount = static_cast<int>( tempProtoType.m_RespawnPosList.size() );
int iIndex = Math::Random::SimpleRandom(GetTickCount(), iRespawnPosCount);
Position resapwnPos = tempProtoType.m_RespawnPosList[iIndex];
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
return resapwnPos;
}
}
++itr;
}
ERRLOG2(g_Log, "존의 기본 리스폰 위치를 찾을수 없습니다. Zone : %d, Nation : %d",
CServerSetup::GetInstance().GetServerZone(), cNation);
return Position(0, 0, 0);
}
const Position CCharRespawnMgr::GetTownRespawnPos(unsigned char cZone, unsigned char cNation)
{
RespawnProtoTypeMap::iterator itr = m_AllZoneTownList.begin();
while (itr != m_AllZoneTownList.end())
{
RespawnProtoType& tempProtoType = itr->second;
if (tempProtoType.m_cZone == cZone)
{
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
int iRespawnPosCount = static_cast<int>( tempProtoType.m_RespawnPosList.size() );
int iIndex = Math::Random::SimpleRandom(GetTickCount(), iRespawnPosCount);
Position resapwnPos = tempProtoType.m_RespawnPosList[iIndex];
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
return resapwnPos;
}
}
++itr;
}
ERRLOG2(g_Log, "존의 기본 리스폰 위치를 찾을수 없습니다. Zone : %d, Nation : %d", cZone, cNation);
return Position(0, 0, 0);
}
const Position CCharRespawnMgr::GetRespawnPos(unsigned long dwTownID)
{
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.find(dwTownID);
if (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
size_t iIndex = Math::Random::ComplexRandom(static_cast<int>(tempProtoType.m_RespawnPosList.size()));
Position resapwnPos = tempProtoType.m_RespawnPosList[iIndex];
resapwnPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
resapwnPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 10) - 5;
return resapwnPos;
}
ERRLOG2(g_Log, "존의 해당 리스폰 위치를 찾을수 없습니다. Zone : %d, dwTownID : %d",
CServerSetup::GetInstance().GetServerZone(), dwTownID);
return Position(0, 0, 0);
}
bool CCharRespawnMgr::SendRespawnInfo(unsigned long dwCID)
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (NULL == lpCharacter)
{
ERRLOG1(g_Log, "CID : 0x%08x 리스폰 하려는 캐릭터가 존재하지 않습니다.", dwCID);
return false;
}
const unsigned short BufferSize = sizeof(PktRsInfoAck) + sizeof(RespawnArea) * PktRsInfoAck::MAX_RESPAWN_POINT;
char szBuffer[BufferSize];
unsigned short wBufferSize = 0;
unsigned char cRespawnAreaNum = 0; // 리스폰 포인트 수
unsigned char cNation = lpCharacter->GetNation();
RespawnArea* lpRespawnArea = reinterpret_cast<RespawnArea*>(szBuffer + sizeof(PktRsInfoAck));
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(lpCharacter->GetGID());
RespawnProtoTypeMap::iterator itr = m_ProtoTypeList.begin();
while (itr != m_ProtoTypeList.end())
{
RespawnProtoType& tempProtoType = itr->second;
if (0 != (tempProtoType.m_dwTownID & Castle::ABTOWN_BIT))
{
if( lpCharacter->GetAbilityValue(Skill::Type::AB_RESPAWN_EX))
{
// 자신의 국가가 속한 마을이라면 패킷에 추가한다.
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
}
else if (0 != (tempProtoType.m_dwTownID & Castle::TOWN_BIT))
{
// 자신의 국가가 속한 마을이라면 패킷에 추가한다.
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
else if (lpGuild && Guild::JOIN_WAIT != lpGuild->GetTitle(dwCID) && 0 != (tempProtoType.m_dwTownID & Castle::CAMP_BIT))
{
// 진지라면 자기 길드의 곳이라면 패킷에 추가한다.
if (lpGuild->GetGID() == tempProtoType.m_dwGID)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
else if (0 != (tempProtoType.m_dwTownID & Castle::CASTLE_BIT))
{
// 자신과 같은 국적 수성/공성 리스폰 포인트라면 패킷에 추가한다.
if (cNation == tempProtoType.m_cNation)
{
// 공성측 리스폰 포인트는 공성 시간에만 활성화 된다.
bool bAddRespawnPoint = true;
if (0 != (tempProtoType.m_dwTownID & Castle::SIEGE_BIT))
{
if (!CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
bAddRespawnPoint = false;
}
}
if (bAddRespawnPoint)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
}
else if (0 != (tempProtoType.m_dwTownID & Castle::STATUE_BIT))
{
// 다크 카나번 석상이라면 자신의 국가와 일치(종족과 일치)하면 패킷에 추가한다.
if (Creature::STATELESS == tempProtoType.m_cNation || cNation == tempProtoType.m_cNation)
{
lpRespawnArea->m_dwTownID = tempProtoType.m_dwTownID;
lpRespawnArea->m_RespawnPos.fPointX = tempProtoType.m_RespawnPosList[0].m_fPointX;
lpRespawnArea->m_RespawnPos.fPointY = tempProtoType.m_RespawnPosList[0].m_fPointY;
lpRespawnArea->m_RespawnPos.fPointZ = tempProtoType.m_RespawnPosList[0].m_fPointZ;
wBufferSize += sizeof(RespawnArea);
++cRespawnAreaNum;
++lpRespawnArea;
}
}
++itr;
}
// edith 2009.07.21 워프나 포탈 사용시 현재 좌표갱신하기 (불법이동 방지 검출 피하기 위해서)
if(lpCharacter)
lpCharacter->SetCurrentPos(lpRespawnArea->m_RespawnPos.fPointX, lpRespawnArea->m_RespawnPos.fPointY, lpRespawnArea->m_RespawnPos.fPointZ);
PktRsInfoAck* lpRsInfoAck = reinterpret_cast<PktRsInfoAck*>(szBuffer);
lpRsInfoAck->m_dwCharID = dwCID;
lpRsInfoAck->m_cRsAreaNum = cRespawnAreaNum;
lpRsInfoAck->m_wSize = wBufferSize;
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
CSendStream& SendStream = lpDispatch->GetSendStream();
return SendStream.WrapCompress(szBuffer, sizeof(PktRsInfoAck) + wBufferSize, CmdCharRespawnInfo, 0, 0);
}
return false;
}
bool CCharRespawnMgr::SendRespawnAreaInfo(unsigned long dwCID, unsigned long dwTownID, bool bCount)
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (NULL == lpCharacter)
{
ERRLOG1(g_Log, "CID : 0x%08x 리스폰 하려는 캐릭터가 존재하지 않습니다.", dwCID);
return false;
}
RespawnProtoTypeMap::iterator RespawnItr = m_ProtoTypeList.find(dwTownID);
if (RespawnItr == m_ProtoTypeList.end())
{
ERRLOG1(g_Log, "TownID : 0x%08x 리스폰 지점이 존재하지 않습니다.", dwTownID);
return false;
}
RespawnCharMap::iterator itr = m_CharList.find(dwTownID);
if (itr != m_CharList.end())
{
RespawnCharList& tempCharList = itr->second;
PktRsAreaInfoAck pktRsAreaInfoAck;
pktRsAreaInfoAck.m_dwCharID = dwCID;
pktRsAreaInfoAck.m_dwTownID = dwTownID;
pktRsAreaInfoAck.m_bCount = bCount;
pktRsAreaInfoAck.m_nWaitOrder = static_cast<unsigned short>(tempCharList.size());
unsigned long dwSec = RESPAWN_DELAY_SEC;
if (0 != tempCharList.size())
{
RespawnCharList::reverse_iterator CharInfoItr = tempCharList.rbegin();
RespawnCharInfo& tempCharInfo = *CharInfoItr;
dwSec += tempCharInfo.m_dwRemainSec;
}
// 시간 계산
pktRsAreaInfoAck.m_cRemainHour = static_cast<unsigned char>(dwSec / SEC_PER_HOUR);
pktRsAreaInfoAck.m_cRemainMin = static_cast<unsigned char>((dwSec / SEC_PER_MIN) / MIN_PER_HOUR);
pktRsAreaInfoAck.m_cRemainSec = static_cast<unsigned char>((dwSec % SEC_PER_HOUR) % SEC_PER_MIN);
char* szPacket = reinterpret_cast<char *>(&pktRsAreaInfoAck);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktRsAreaInfoAck), CmdCharRespawnAreaInfo, 0, 0))
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
return lpDispatch->GetSendStream().PutBuffer(szPacket, sizeof(PktRsAreaInfoAck), CmdCharRespawnAreaInfo);
}
}
}
ERRLOG1(g_Log, "dwTownID : 0x%08x 해당 마을(성, 길드 요새, 다크 카나번 석상)이 리스폰 포인트로 존재하지 않습니다.", dwTownID);
return false;
}

View File

@@ -0,0 +1,110 @@
#ifndef _CHARACTER_RESPAWN_MANAGER_H_
#define _CHARACTER_RESPAWN_MANAGER_H_
#pragma once
#include <Creature/CreatureStructure.h>
class CCharRespawnMgr
{
public:
enum Const
{
RESPAWN_DELAY_SEC = 5,
SEC_PER_HOUR = 3600,
SEC_PER_MIN = 60,
MIN_PER_HOUR = 60
};
enum eReturn
{
RET_FALSE = 0,
RET_TRUE = 1,
RET_TOWN = 2
};
enum RespawnType
{
RST_TOWN = 0, // 죽거나 포탈 이용으로 마을에서 부활 (일반적인 리스폰)
RST_FORTRESS = 1, // 죽거나 포탈 이용으로 요새나 성 및 다크 카나번 석상에서 부활
RST_RESURRECTION = 2, // 레저렉션으로 부활
RST_BATTLE = 3 // 배틀 그라운드나 배틀 로한에서 리스폰 시
};
struct RespawnProtoType
{
unsigned long m_dwTownID; // 마을, 길드 요새, 성 ID, 다크 카나번 석상 ID(0x800x0000)
unsigned long m_dwGID; // 길드 요새
unsigned char m_cNation; // 국적 (휴먼, 아칸, 제3세력)
unsigned char m_cZone; // 존
std::vector<Position> m_RespawnPosList; // 리스폰 위치
};
struct RespawnCharInfo
{
unsigned long m_dwCID;
unsigned long m_dwRemainSec;
RespawnCharInfo(unsigned long dwCID, unsigned long dwSec)
: m_dwCID(dwCID), m_dwRemainSec(dwSec) { }
};
typedef std::list<RespawnCharInfo> RespawnCharList;
typedef std::map<unsigned long, RespawnProtoType> RespawnProtoTypeMap;
typedef std::map<unsigned long, RespawnCharList> RespawnCharMap;
~CCharRespawnMgr();
static CCharRespawnMgr& GetInstance();
// ----------------------------------------------------------------------------
// 리스폰 스크립트 (RespawnScript.txt)
// 스크립트의 0x40001000 은 Castle::CASTLE_BIT | (성의 NameID << Castle::CASTLE_NAME_BIT_SHIFT) 입니다.
// 이후에 성 정보를 DB중계 서버로부터 받아와서 Map 정보에 제대로된 CastleID 가 key 로 들어갑니다.
bool LoadRespawnFromFile(const char* szFileName = 0);
// 요새 리스폰 포인트 사용
bool AddCampRespawnPoint(unsigned long dwCampID, unsigned long dwGID, const Position& RespawnPos);
bool DeleteCampRespawnPoint(unsigned long dwCampID);
// 다크 카나번 석상 리스폰 포인트 사용
// TownID 로 0x80010000 처럼 Castle::STATUE_BIT | (석상 Index) << 16) 입니다.
bool AddRealmStatueRespawnPoint(unsigned long dwIndex, unsigned char cNation, const Position& RespawnPos);
bool DeleteRealmStatueRespawnPoint(unsigned long dwIndex);
// 성 리스폰 포인트 사용
// 수성측 성 리스폰 포인트 키 : CASTLE_BIT | (NameID << Castle::CASTLE_NAME_BIT_SHIFT)
// 공성측 임시 리스폰 포인트 키 : CASTLE_BIT | SIEGE_BIT | (NameID << Castle::CASTLE_NAME_BIT_SHIFT) | ID(갯수별)
bool SetCastleRespawnPointNation(unsigned long dwCastleNameID, unsigned char cNation);
eReturn AddCharacter(unsigned long dwTownID, unsigned long dwCID);
// void DelCharacter(unsigned long dwTownID, unsigned long dwCID);
void ProcessRespawn();
const Position GetDefaultRespawnPos(unsigned char cNation);
const Position GetRespawnPos(unsigned long dwTownID);
const Position GetTownRespawnPos(unsigned char cZone, unsigned char cNation);
// ----------------------------------------------------------------------------
// Send 함수
bool SendRespawnInfo(unsigned long dwCID);
bool SendRespawnAreaInfo(unsigned long dwCID, unsigned long dwTownID, bool bCount=false);
private:
CCharRespawnMgr();
RespawnProtoTypeMap m_ProtoTypeList; // 현재 존의 리스폰 포인트 정보
RespawnProtoTypeMap m_AllZoneTownList; // 전 존의 마을 리스폰 포인트 정보
RespawnCharMap m_CharList;
static const char* ms_szRespawnScriptFileName;
};
#endif

View File

@@ -0,0 +1,485 @@
#include "stdafx.h"
#include <Item/Container/ItemContainer.h>
#include <Item/Container/ContainerConstant.h>
#include <Item/Container/ExchangeContainer.h>
#include <Item/Container/EquipmentsContainer.h>
#include <Log/GameLog.h>
#include <Log/CharacterLog.h>
#include <Log/LogStruct.h>
#include <Utility/Setup/ServerSetup.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/FieldMap//VirtualArea/VirtualArea.h>
#include <Map/FieldMap//VirtualArea/VirtualAreaMgr.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Skill/SkillMgr.h>
#include <Skill/SkillTable.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharEtc.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
#include <Creature/CreatureManager.h>
#include <Creature/NPC/NPC.h>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Creature/Siege/CampShop.h>
#include <Community/Guild/GuildMgr.h>
#include <Community/Guild/Guild.h>
#include "Character.h"
CCharacter::CCharacter(unsigned long dwCID, unsigned long dwSessionID)
: CAggresiveCreature(dwCID),
m_dwUID(0), m_dwSessionID(dwSessionID),
m_nLogoutCount(LOGOUT_COUNT), m_nDBUpdateCount(DBUPDATE_COUNT), m_cConsumeMPCount(0),
m_lpGameClientDispatch(NULL), m_lpSummonee(NULL),
m_cOperationFlags(0), m_cHandPos(0), m_cRidePos(0), m_friendList(dwCID, &CXRefFriends::GetInstance()), m_banList(dwCID, &CXRefBans::GetInstance()),
m_dwLastUpdateExTime(0), m_cMoveUpdateExCount(0), m_dwRideArmsCID(0), m_dwRespawnSpeed(RESPAWN_PULSE),
m_dwProtectGateCID(0), m_dwLastShoutTime(0),
m_dwLastSendPartyAttackInfoTime(0), m_bChatBan(false),
m_cAccountNation(0), // WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
m_cNameChangeCount(0),
m_cGuildWarFlag(Creature::WAR_OFF),
m_cRealmWarFlag(Creature::WAR_OFF),
m_dwExchangeID(0),
m_cGMModelFlag(0),
m_cRealmPoint(0),
m_dwPlayTime(0),
m_lPremiumTime(0),
m_cGuildSafe(-1),
m_iAbilityPoint(0),
m_iUseAbilityPoint(0),
m_iAdminAbilityPoint(0),
m_bRealmWarBuffer(FALSE),
m_bDead(FALSE),
m_eLastDeadType(DEAD_BY_NONE),
m_dwRespawnTownID(0)
{
m_StartTime = CTime::GetCurrentTime();
m_StartPremiumTime = CTime::GetCurrentTime();
memset(&m_FightInfo, 0, sizeof(CharacterFightInfo));
memset(&m_DBData, 0, sizeof(m_DBData));
memset(&m_CharacterStatus, 0, sizeof(m_CharacterStatus));
memset(m_szAccountName, 0, sizeof(char) * CHAR_INFOST::MAX_ACCOUNT_LEN);
memset(m_AbilityValue, 0, sizeof(m_AbilityValue));
std::fill_n(m_wHistoryQuest, int(Quest::MAX_HISTORY_QUEST), 0);
std::fill_n(m_cUsingMastery, int(MAX_USING_MASTERY), char(Skill::NONE_MASTERY));
std::fill_n(m_bPadding, int(PAD_BYTE), 0x65);
}
CCharacter::~CCharacter()
{
for(int nCount = 0; nCount < PAD_BYTE; ++nCount)
{
if (0x65 != (unsigned char)m_bPadding[nCount])
{
SERLOG1(g_Log, "CID:0x%08x 캐릭터의 패딩 바이트가 바뀌었습니다. "
"버퍼 오버플로우가 일어났을 수 있습니다.", m_dwCID);
break;
}
}
std::fill_n(m_bPadding, int(PAD_BYTE), 0x67);
SetDispatcher(NULL);
m_SpellMgr.GetAffectedInfo().ClearAll();
}
bool CCharacter::Initialize(CGameClientDispatch* lpGameClientDispatch)
{
SetDispatcher(lpGameClientDispatch);
if (!m_Inventory.Initialize(m_dwCID,
ContainerConstant::INVENTORY_WIDTH,
ContainerConstant::INVENTORY_HEIGHT,
ContainerConstant::MAX_INVENTORY_TAB))
{
return false;
}
if (!m_Equipments.Initialize(this,
Item::EquipmentPos::MAX_EQUPMENT_POS))
{
return false;
}
if (!m_ExtraSpace.Initialize(m_dwCID,
Item::ExtraSpacePos::MAX_EXTRA_SPACE_NUM))
{
return false;
}
if (!m_Exchange.Initialize(this,
ContainerConstant::EXCHANGE_WIDTH,
ContainerConstant::EXCHANGE_HEIGHT))
{
return false;
}
if (!m_Deposit.Initialize(this,
ContainerConstant::DEPOSIT_WIDTH,
ContainerConstant::DEPOSIT_HEIGHT,
ContainerConstant::MAX_DEPOSIT_TAB))
{
return false;
}
if (!m_Stall.Initialize(m_dwCID,
ContainerConstant::STALL_WIDTH,
ContainerConstant::STALL_HEIGHT))
{
return false;
}
if (!m_TempInven.Initialize(this, Item::MAX_TEMP_INVEN_ITEM_NUM))
{
return false;
}
return true;
}
void CCharacter::SetDispatcher(CGameClientDispatch* lpGameClientDispatch)
{
DETLOG5(g_Log, "UID:%d/CID:0x%08x(0x%p)/DispatchPointer:0x%p에서 -> 0x%p로 바꿉니다.",
m_dwUID, m_dwCID, this, m_lpGameClientDispatch, lpGameClientDispatch);
if (NULL != m_lpGameClientDispatch)
{
m_lpGameClientDispatch->SetCharacter(NULL);
if (false == IsOperationFlagSet(CHAR_ZONE_MOVED))
{
m_lpGameClientDispatch->Disconnect();
}
}
m_lpGameClientDispatch = lpGameClientDispatch;
if (NULL != m_lpGameClientDispatch)
{
m_bLogout = false;
m_dwUID = m_lpGameClientDispatch->GetUID();
m_lpGameClientDispatch->SetCharacter(this);
}
else
{
if (false == m_bLogout)
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(m_Stall.GetOtherOwner());
// 노점상 마무리
if (0 != strcmp(m_Stall.GetStallName(), ""))
{
m_Stall.Close();
m_Stall.SendCharStallOpen("");
}
if (0 != m_Stall.GetOtherOwner())
{
if (Creature::CT_PC == Creature::GetCreatureType(m_Stall.GetOtherOwner()))
{
if (NULL != lpCharacter)
{
lpCharacter->GetStall().Leave(this);
lpCharacter->GetStall().SendCharStallEnter(m_dwCID, 0);
}
}
else
{
CCampShop* lpShop =
reinterpret_cast<CCampShop*>(CCreatureManager::GetInstance().GetSiegeObject(m_Stall.GetOtherOwner()));
if (NULL != lpShop)
{
lpShop->GetContainer().Leave(this);
lpShop->GetContainer().SendCharStallEnter(m_dwCID, 0);
}
}
}
m_bLogout = true;
CCreatureManager::GetInstance().EnqueueLogout(this);
}
}
}
bool CCharacter::GetCharacterView(CHAR_VIEW& charView)
{
charView.CID = m_dwCID;
memcpy(charView.Name, m_DBData.m_Info.Name, CHAR_INFOST::MAX_NAME_LEN); // 캐릭터 이름
charView.Sex = m_DBData.m_Info.Sex; // 캐릭터 성
charView.Hair = m_DBData.m_Info.Hair; // 캐릭터 머리 모양
charView.Face = m_DBData.m_Info.Face; // 캐릭터 얼굴 모양
charView.Race = m_DBData.m_Info.Race; // 캐릭터 종족
charView.Class = m_DBData.m_Info.Class; // 캐릭터 클래스
charView.Fame = m_DBData.m_Info.Fame; // 명성
charView.Mileage = m_DBData.m_Info.Mileage; // 마일리지
charView.GID = m_DBData.m_Info.GID; // 캐릭터 길드
charView.PID = m_DBData.m_Info.PID; // 캐릭터 파티
charView.Level = m_DBData.m_Info.Level; // 캐릭터 레벨
m_Equipments.GetEquipmentView(charView.Equip); // 장비(겉보기)
return true;
}
bool CCharacter::BindPositionToNPC(const unsigned long dwNPCID)
{
/*
GAMELOG::ERRType eReturnCode = 0;
CNPC* pNPC = CCreatureManager::GetInstance().GetNPC(dwNPCID);
if (NULL != pNPC&& true == pNPC->IsBindable() &&
pNPC->GetCurrentPos().GetDistance(m_CurrentPos) < CNPC::MAX_CONTACT_RANGE)
{
int nZone = CServerSetup::GetInstance().GetServerZone();
if (GetMapIndex() != 0)
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (NULL != lpVirtualArea)
{
nZone = static_cast<int>(lpVirtualArea->GetVirtualZone());
}
}
if (pNPC->GetZone() == nZone)
{
m_DBData.m_Pos.SavePoint.fPointX = m_CurrentPos.m_fPointX;
m_DBData.m_Pos.SavePoint.fPointY = m_CurrentPos.m_fPointY;
m_DBData.m_Pos.SavePoint.fPointZ = m_CurrentPos.m_fPointZ;
}
}
else
{
eReturnCode = 1;
}
// BindPosition로그를 남긴다.
GAMELOG::LogCharBindPos(*this, dwNPCID, eReturnCode);
return (0 == eReturnCode);
*/
return true;
}
bool CCharacter::ControlOption(const RejectOption Reject, bool bLogin)
{
m_RejectOption = Reject;
// TODO : 키 세팅 저장
return true;
}
unsigned char CCharacter::GetNation(void) const
{
CGuild* lpGuild = CGuildMgr::GetInstance().GetGuild(GetGID());
if (NULL != lpGuild)
{
if (Guild::JOIN_WAIT != lpGuild->GetTitle(m_dwCID))
{
return lpGuild->GetNation();
}
}
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
return m_cAccountNation;
/*
switch (m_DBData.m_Info.Race)
{
case CClass::HUMAN: return Creature::KARTERANT;
case CClass::AKHAN: return Creature::MERKADIA;
}
ERRLOG2(g_Log, "CID:0x%08x 캐릭터의 종족 설정이 이상하여 제3국가로 사용합니다. 종족:%d",
m_dwCID, m_DBData.m_Info.Race);
return Creature::ALMIGHTY_PIRATE;
*/
}
void CCharacter::SetPID(unsigned long dwPID)
{
m_DBData.m_Info.PID = dwPID;
}
void CCharacter::SetGID(unsigned long dwGID)
{
m_DBData.m_Info.GID = dwGID;
}
ChState CCharacter::GetState(void)
{
ChState state;
state.m_wIP = m_DBData.m_Info.IP;
state.m_wSTR = m_DBData.m_Info.STR;
state.m_wDEX = m_DBData.m_Info.DEX;
state.m_wCON = m_DBData.m_Info.CON;
state.m_wINT = m_DBData.m_Info.INT;
state.m_wWIS = m_DBData.m_Info.WIS;
return state;
}
void CCharacter::SetFame(unsigned long dwFame)
{
char cOldEliteBonus = GetEliteBonus();
m_DBData.m_Info.Fame = dwFame;
char cNewEliteBonus = GetEliteBonus();
if (cNewEliteBonus != cOldEliteBonus)
{
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharEliteBonus(
m_lpGameClientDispatch->GetSendStream(), cNewEliteBonus);
}
}
// 길드원 정보 업데이트
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(GetGID());
if (NULL != lpGuild)
{
lpGuild->UpdateMemberInfo(m_dwCID, m_DBData.m_Info.Fame, Guild::TYPE_FAME);
}
// 퀘스트 트리거 체크
CheckTrigger(Quest::TRIGGER_FAME, dwFame, GetCurrentPos(), 1);
}
bool CCharacter::SetDuelOpponent(CCharacter* lpCharacter)
{
const POS DuelPos1 = { 3099, 1137, 3215 }; // 듀얼 스타트 위치
const POS DuelPos2 = { 3156, 1137, 3209 };
if (CServerSetup::GetInstance().GetDuelModeCheck())
{
CCell* lpDuelCell = NULL;
unsigned long Cell_ID = 0;
if (NULL == lpCharacter)
{
// 듀열 셀
Cell_ID = m_FightInfo.m_dwCellID;
lpDuelCell = CDuelCellManager::GetInstance().GetCell(Cell_ID);
if (NULL == lpDuelCell)
{
ERRLOG1(g_Log, "(CID:0x%08x) 듀얼 셀 얻기에 실패했습니다.", GetCID());
return false;
}
// 듀얼 셀에서 제거
lpDuelCell->DeleteCreature(m_dwCID);
if (0 == lpDuelCell->GetCharacterNum())
{
// 셀 제거
CDuelCellManager::GetInstance().DestroyCell(Cell_ID);
}
// 정보 설정
m_FightInfo.m_pDuelOpponent = lpCharacter;
m_FightInfo.m_dwCellID = 0;
// 이전 셀로 복귀
m_CellPos.MoveTo(m_CurrentPos);
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG1(g_Log, "(CID:0x%08x) 현재 셀 없음.", GetCID());
return false;
}
m_CellPos.m_lpCell->SetCreature(m_dwCID, this);
GameClientSendPacket::SendCharBindPosition(*this, 0, 1,
m_DBData.m_Pos.LastPoint, CServerSetup::GetInstance().GetServerZone(), 0);
}
else
{
POS DuelPos = {0,};
Cell_ID = lpCharacter->m_FightInfo.m_dwCellID;
if (0 == Cell_ID)
{
Cell_ID = GetCID();
lpDuelCell = CDuelCellManager::GetInstance().CreateCell(Cell_ID);
if (NULL == lpDuelCell)
{
ERRLOG0(g_Log, "듀얼 셀 생성에 실패했습니다.");
return false;
}
DuelPos = DuelPos1;
}
else
{
lpDuelCell = CDuelCellManager::GetInstance().GetCell(Cell_ID);
if (NULL == lpDuelCell)
{
ERRLOG0(g_Log, "듀얼 셀 생성에 실패했습니다.");
return false;
}
DuelPos = DuelPos2;
}
// 이전 셀에서 제거
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG1(g_Log, "(CID:0x%08x) 현재 셀 없음.", GetCID());
return false;
}
m_CellPos.m_lpCell->DeleteCreature(GetCID());
m_CellPos.m_lpCell = NULL;
// 듀얼 셀로 추가
lpDuelCell->SetCreature(m_dwCID, this);
GameClientSendPacket::SendCharBindPosition(*this, 0, 1, DuelPos, 100, 0);
// 정보 설정
m_FightInfo.m_pDuelOpponent = lpCharacter;
m_FightInfo.m_dwCellID = Cell_ID;
}
}
else
{
m_FightInfo.m_pDuelOpponent = lpCharacter;
}
return true;
}
void CCharacter::SetGuildWarFlag(unsigned char cFlag)
{
if (cFlag >= Creature::WAR_OFF && cFlag <= Creature::WAR_INSTANCE)
{
m_cGuildWarFlag = cFlag;
// 길드원 정보 업데이트
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(GetGID());
if (NULL != lpGuild)
{
lpGuild->UpdateMemberInfo(m_dwCID, m_cGuildWarFlag, Guild::TYPE_WARFLAG);
}
}
}

View File

@@ -0,0 +1,818 @@
#ifndef _CCHARACTER_H_
#define _CCHARACTER_H_
#include <Item/Container/ItemContainer.h>
#include <Item/Container/EquipmentsContainer.h>
#include <Item/Container/ExchangeContainer.h>
#include <Item/Container/DepositContainer.h>
#include <Item/Container/StallContainer.h>
#include <Item/Container/TempInvenContainer.h>
#include <Item/Container/InventoryContainer.h>
#include <Network/Packet/PacketStruct/CharLoginOutPacketStruct.h>
#include <Network/Packet/PacketStruct/CharItemPacketStruct.h>
#include <Network/Packet/PacketStruct/CharCommunityPacketStruct.h>
#include <Network/Packet/PacketStruct/CharStatusPacketStruct.h>
#include <Network/Packet/PacketStruct/CharConfigPacketStruct.h>
#include <Creature/AggresiveCreature.h>
#include <Creature/CreatureManager.h>
#include <Community/FriendList.h>
#include <Community/BanList.h>
#include <Quest/QuestStruct.h>
#include <Utility/Setup/ServerSetup.h>
#include "CharacterClass.h"
#include <Network/Broadcast/SerializeCharacterData.h>
#include <atltime.h>
#include <GameTime/GameTimeMgr.h>
#include <GameTime/GameTimeConstants.h>
// 전방 참조
class CChatPacket;
class CGameClientDispatch;
struct GiveItemInfo;
namespace Broadcast2nd
{
class CSerializeCharacterData;
};
namespace Item
{
class CItem;
class CEquipment;
class CUseItem;
struct ItemPos;
};
namespace BattleInclination
{
void SetCharData(CCreature& creature, BattleInclination::CharData& charData, bool bSetRideArmsInfo = false);
void SetRelationData(CCreature& ownerCreature, CCreature& targetCreature, BattleInclination::RelationData& relationData);
};
namespace RealmSkill
{
void RealmInchantAdd(CCharacter* lpCharacter);
void RealmInchantRemove(CCharacter* lpCharacter);
};
class CCharacter : public CAggresiveCreature
{
public:
// DB중계에서 테이블로 읽어서 TempInven으로 주어진 아이템의 CreationID를 저장한다.
typedef std::vector<unsigned long> GivenItemList;
enum Const
{
LOGOUT_PULSE = 5,
LOGOUT_COUNT = 5,
DBUPDATE_PULSE = 10,
DBUPDATE_COUNT = 180,
BATTLE_GROUND_PULSE = 600,
RESPAWN_PULSE = 100, // 10 초
MELEE_ATTACK_RANGE = 10, // 캐릭터의 근접 공격 제한 거리
RANGE_ATTACK_RANGE = 50 // 캐릭터의 원거리 공격 제한 거리
};
enum OperationFlag
{
CHAR_INFO_LOADED = ( 1 << 0),
CHAR_ZONE_MOVED = ( 1 << 1),
MOVEZONE_PROCESSED = ( 1 << 2),
SERVERZONE_PROCESSED = ( 1 << 3)
};
enum DURABILITY_DECRESE_PERCENTAGE
{
// edith 아이템의 내구도 랜덤값
WEAPON_DURABILITY_DECRESE_PERCENTAGE = 100, // 200
ARMOUR_DURABILITY_DECRESE_PERCENTAGE = 25, // 50
SHIELD_DURABILITY_DECRESE_PERCENTAGE = 10, // 20
STARTAXDURABILITY = 10, // 최대 내구도 감소폭 시작값
DOWNMAXDURABILITY = 10 // 최대 내구도 감소폭
};
enum DURABILITY_DECRESE_PERSENT
{
DURABILITY_DECREASE_PERSENT_BY_MONSTER = 20,
DURABILITY_DECREASE_PERSENT_BY_SUICIDE = 3
};
enum LAST_DEAD_TYPE
{
DEAD_BY_NONE,
DEAD_BY_SUICIDE,
DEAD_BY_MONSTER,
DEAD_BY_CHARACTER
};
enum RESPAWN_HP_MP_RECOVERY_PERCENTAGE
{
TOWN_RECOVERY_PERCENTAGE = 25,
FORTRESS_RECOVERY_PERCENTAGE = 50,
RESURRECTION_RECOVERY_PERCENTAGE = 75
};
bool Initialize(CGameClientDispatch* lpGameClientDispatch); // 초기화
// --------------------------------------------------------------------------------------------
// 캐릭터 로그인시 처리될 부분들 --------------------------------------------------------------
void PrepareLogin(void);
bool Login(void); // 캐릭터 정보가 캐릭터 테이블에 로드됨(로그인함)
bool IsLogined(void) { return IsOperationFlagSet(CHAR_INFO_LOADED); }
// --------------------------------------------------------------------------------------------
// 캐릭터 로그아웃시 처리될 부분들 ------------------------------------------------------------
inline void ResetLogoutCount(void) { m_nLogoutCount = LOGOUT_COUNT; }
inline bool StillAlive(void) { return --m_nLogoutCount >= 0; }
inline void SetDBUpdateCount(LONG nDBUpdateCount) { m_nDBUpdateCount = nDBUpdateCount; }
// 캐릭터를 맵에서 로그아웃시키고, 데이터를 DB로 보낸다.
bool Logout(DBUpdateData::UpdateType eUpdateType = DBUpdateData::LOGOUT);
// --------------------------------------------------------------------------------------------
// 캐릭터 데이터 업데이트 - CharacterDataUpdate.cpp -------------------------------------------
bool GetCharacterInfo(char* pBuffer, int* nBufferSize_InOut, unsigned short* lpUpdateLen);
bool SetCharacterInfo(char* pBuffer, unsigned short usUpdateLen[DBUpdateData::MAX_UPDATE_DB]);
bool DBUpdate(DBUpdateData::UpdateType eUpdateType);
bool DBUpdateForce(DBUpdateData::UpdateType eUpdateType)
{
m_nDBUpdateCount = -1;
return DBUpdate(DBUpdateData::UPDATE);
}
bool MoveZoneProcess(unsigned long dwServerID);
bool ItemDump(char* pBuffer, int* nBufferSize_InOut) const;
// --------------------------------------------------------------------------------------------
// 아이템, 돈 이동 및 사용 관련 메소드 - CharacterItem.cpp ------------------------------------
inline unsigned long GetGold(void) const { return m_DBData.m_Info.Gold; }
unsigned long GetGold(unsigned char cPos);
bool AddGold(unsigned long dwGold, bool bNotice);
bool DeductGold(unsigned long dwGold, bool bNotice);
bool MoveGold(unsigned long dwGold, unsigned char cSrcPos,
unsigned char cDstPos, unsigned short& usError); // 돈 옮기기
bool DeductMileage(unsigned long dwMileage, bool bNotice);
bool MoveItem(const TakeType takeType, bool bChk = true); // 아이템 이동 및 스택
bool SwapItem(const TakeType SrcType, const TakeType DstType); // 아이템 위치 스왑
Item::CItem* SplitItem(const TakeType takeType); // 아이템 개수 나누기
bool Pickup(Item::CItem* lpItem, Item::ItemPos dstPos); // 아이템 집기
Item::CItem* Drop(Item::ItemPos SrcPos, unsigned char cNum); // 아이템 버리기
// 아이템을 인벤토리에 넣어준다. (자리가 없으면 임시 인벤토리에 넣는다.)
bool GiveItem(Item::CItem* lpItem);
bool TestItem(Item::CItem* lpItem); // 아이템을 넣을 수 있는지 체크한다.
bool UseLottery(unsigned short usItemID);
bool UseCashItem(unsigned long dwSender, unsigned long dwReceiver, Item::ItemPos itemPos, unsigned short wCashType, unsigned short& wError);
bool UseStartKit(unsigned short wObjectType, unsigned short& wError);
bool CheckItemRuneSkill(Item::ItemPos SrcPos, Item::ItemPos DstPos, bool bChk); // 아이템 이동시 룬 속성 체크
const Broadcast2nd::CNetworkEquipGrade GetNetworkEquipGrade(void); // 이펙트를 위한 장비 그레이드 정보
// --------------------------------------------------------------------------------------------
// 캐릭터 인벤토리 관련(아이템 Get, Set, Reset) - CharacterInventoryItem.cpp ------------------
public:
Item::CItem* GetItem(Item::ItemPos SrcPos);
bool SetItem(Item::ItemPos SrcPos, Item::CItem* lpItemBase);
bool RemoveItem(Item::ItemPos SrcPos);
// --------------------------------------------------------------------------------------------
// 아이템 팔고 사고 수리하는 메서드 - CharacterTradeItem.cpp ----------------------------------
unsigned long RepairItem(const unsigned long dwNPCID, Item::ItemPos itemPos, unsigned short& wError);
unsigned long RepairAllItem(const unsigned long dwNPCID, unsigned short& wError);
Item::CItem* SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError);
// --------------------------------------------------------------------------------------------
// 스테이터스 관련 메소드 - CharacterStatus.cpp -----------------------------------------------
// 능력치 및 스테이터스 데이터를 DBData에서 읽어서 재계산. HP, MP가 Full이 된다.
bool CalculateStatusData(bool bFullHPandMP);
bool CalculateAbility(const SKILL& skill); // 어빌리티의 정보를 갱신한다.
bool ChangeClass(unsigned char cClassType); // CClass 참조
bool IncrementExp(unsigned long dwExp); // 레벨업 상황이 되면 레벨업한다.
bool GetHuntingExp(CAggresiveCreature* lpDeadCreature, unsigned long dwExp, unsigned char cMemberNum);
unsigned short AddState(unsigned char Type_In, ChState& ChState_Out);
bool StateRedistribution(ChState& State); // 스탯을 해당 클래스의 처음 상태로 돌리는 함수
bool StatusRetrain(ChState& State, Item::ItemPos InvenPos); // 스탯을 일정량 돌려받는 처리를 하는 함수 (망각의 돌 사용)
bool ChangeWeaponAndShield(unsigned char cSelect); // 무기 바꾸기
bool ChangeRide(unsigned char cSelect); // 말 타기 내리기
bool CalculateEquipDurability(unsigned short wAttackType);
unsigned char GetCurrentDurability(unsigned char cEquipmentIndex);
bool CalculateAllEquipDurability(unsigned char cDecreasePersent);
// HP와 MP를 Regen한다. 추가 값이 0인 경우는 기본 값으로 Regen한다.
virtual bool RegenHPAndMP(unsigned short usAdditionalHP, unsigned short usAdditionalMP, bool bAddDefaultRegenValue);
// Admin 명령으로 레벨을 다운 시킬때 사용하는 함수 (레벨 1의 초기 클래스로 만들어 버린다.)
bool InitLevel1Char(unsigned char cClassType);
bool CheckRenameWarrant(Item::ItemPos InvenPos, bool bItemAccept);
// --------------------------------------------------------------------------------------------
// 전투 관련 메소드 - CharacterFight.cpp ------------------------------------------------------
virtual const int CalculateFixLevelGap(CAggresiveCreature *pDefender);
void Casting(AtType attackType, AtNode& attackNode);
bool AttackCID(AtType attackType, AtNode& attackNode, unsigned short& wError);
bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** pDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenderMPHeal);
bool AttackUsingBow(unsigned short wType);
bool UseAmmo(Item::CUseItem* pUseItem);
virtual bool Dead(CAggresiveCreature* pOffencer);
bool Respawn(unsigned char cType = 0, const Position& Pos = Position::Position());
bool AutoRespawn(void); // 배틀 그라운드에서 초렙 존의 위치로 자동 리스폰
bool IsPeaceMode(void);
EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget, unsigned char* lpResult = NULL);
void SendAttackedToParty(unsigned long dwAttackerID, AtType atType,
unsigned short wDamage, unsigned char cDefenserJudge, unsigned short wMPHeal);
char GetEliteBonus(void);
char GetCaste(void);
void CalculateFame(CCharacter* lpWinnerCharacter);
// --------------------------------------------------------------------------------------------
// 스킬 관련 메소드 - CharacterSkill.cpp ------------------------------------------------------
unsigned short ReadSkill(SKILLSLOT SkillSlot, unsigned short wSkillID, unsigned short wSkillLockCount);
bool RedistributionSkill(void);
bool CalculateMaxSkillSlot(void);
bool SkillCreate(Item::CUseItem* pUseItem);
bool SkillErase(unsigned char Index_In, Item::ItemPos InvenPos);
bool SkillLock(unsigned char Index_In);
bool SkillUnLock(unsigned char Index_In, Item::ItemPos* InvenPos, bool b_SkillFifthUnlock = false);
bool SkillFifthLock(unsigned short wSKilID);
bool SkillFifthUnlock(unsigned short wSKilID);
virtual bool HasSkill(unsigned short usSkillType, unsigned char cLockCount, unsigned char cLevel);
short GetSkillLockCount(unsigned short usSkillType);
short GetSkillLevel(unsigned short usSkillType);
short GetSkillSlotIndex(unsigned short usSkillType);
bool AddWorldWeaponEnchant(CAggresiveCreature* lpWeapon, unsigned char cNation);
bool ClearWorldWeaponEnchant(void);
void CheckSkillVaild(void);
bool AbilityCreate(Item::CUseItem* pUseItem);
bool AbilityErase(unsigned char Index_In, Item::ItemPos InvenPos);
// --------------------------------------------------------------------------------------------
// 퀵슬롯 관련 메소드 - CharacterQuickSlot.cpp ------------------------------------------------
void UpdateQuickSlotSkill(SKILLSLOT Slot);
bool MoveQuickSlot(const TakeType takeType, const unsigned short usSkillID, unsigned char cLockCount, unsigned char cSkillLevel);
// --------------------------------------------------------------------------------------------
// 캐릭터 컨트롤 관련 메소드 - CharacterControl.cpp -------------------------------------------
bool DropItem(unsigned short usProtoTypeID, unsigned char cNum);
bool MovePos(Position Pos, char cZone, const bool bSitDown);
bool MoveZone(POS NewPos, char cZone, char Channel);
bool Kill(CAggresiveCreature* lpAttacker);
bool NotifyInfo(unsigned long dwAdminCID);
bool DuelInit(unsigned char cCmd);
// 길드전, 국가전에 해당하는 존으로 이동
void MoveToGuildWarZone();
void MoveToRealmWarZone();
// --------------------------------------------------------------------------------------------
// 캐릭터 퀘스트 관련 메소드 - CharacterQuest.cpp ---------------------------------------------
bool CheckQuest(Quest::QuestNode* lpQuestNode, unsigned short& wError);
bool HasQuest(unsigned short wQuestID);
bool GiveQuest(Quest::QuestNode* lpQuestNode);
bool StartPhase(unsigned short wQuestID, unsigned char cPhase);
bool CancelQuest(unsigned short wQuestID, bool bFinished = false);
void CheckTrigger(unsigned char cTriggerKind, unsigned long dwReferenceID, Position Pos, short wCount);
unsigned short OperateTrigger(unsigned short wQuestID, unsigned char cPhase, unsigned char cTrigger,
unsigned char cCount, Position Pos, unsigned char cType = 0);
void PendingQuest(Quest::TriggerMsg* pTriggerMsg);
//----------------------------------------------------------------------------------------------
// 친구 정보 업데이트
void FriendInfoUpdate(unsigned long dwUID, unsigned long dwCID, unsigned long dwGID, unsigned short wClass, char cLevel, unsigned long dwServerID);
private:
Quest::ErrorCode ExecuteEvent(Quest::ExecutingQuest executingQuest, const Quest::TriggerNode* triggerNode, Position Pos);
void EventEnd(unsigned short wQuestID, bool bSave);
void EventAward(unsigned long dwExp, unsigned long dwGold, unsigned long dwFame, unsigned long dwMileage);
public:
// 스킬 관련 //
void SkillClear();
bool ClearGarbage(vector<Item::ItemGarbage>& vecItemGarbage, unsigned char cCmd);
bool HasExecutingQuest(unsigned short wQuestID);
bool HasHistoryQuest(unsigned short wQuestID);
bool InsertHistoryQuest(unsigned short wQuestID);
bool DeleteHistoryQuest(unsigned short wQuestID);
virtual void CalculateStatusByQuest(void);
// --------------------------------------------------------------------------------------------
// 세션 관련 메소드 -------------------------------------------------------------------------
CGameClientDispatch* GetDispatcher(void) { return m_lpGameClientDispatch; }
void SetDispatcher(CGameClientDispatch* lpGameClientDispatch);
// --------------------------------------------------------------------------------------------
// 기타 메소드 -------------------------------------------------------------------------------
bool GetCharacterView(CHAR_VIEW& charView);
// TODO : AggresiveCreature로 올리고 GetName으로 수정합시다. (Creature로 올려도 좋고...)
const char* GetCharacterName() const { return m_DBData.m_Info.Name; }
void SetCharacterName(const char* szName)
{
strncpy(m_DBData.m_Info.Name, szName, CHAR_INFOST::MAX_NAME_LEN);
m_DBData.m_Info.Name[CHAR_INFOST::MAX_NAME_LEN - 1] = 0;
}
const char* GetAccountName() const { return m_szAccountName; }
void SetAccountName(const char* szAccountName)
{
strncpy(m_szAccountName, szAccountName, CHAR_INFOST::MAX_ACCOUNT_LEN);
m_szAccountName[CHAR_INFOST::MAX_ACCOUNT_LEN - 1] = 0;
}
bool BindPositionToNPC(const unsigned long dwNPCID); // 리스폰 위치를 NPC위치에 바인딩
bool ControlOption(const RejectOption Reject, bool bLogin); // 각종 옵션 조정
void UpgradeRespawnSpeedByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep);
void DegradeRespawnSpeedByEmblem();
virtual unsigned long GetUID(void) const { return m_dwUID; }
char GetConsumeMPCount(void) { return m_cConsumeMPCount; }
unsigned short GetClass(void) { return m_DBData.m_Info.Class; }
unsigned char GetLevel(void) { return m_DBData.m_Info.Level; }
CClass::RaceType GetRace(void) { return static_cast<CClass::RaceType>(m_DBData.m_Info.Race); }
unsigned char GetNation(void) const;
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
void SetNation(unsigned char cNation) { m_cAccountNation = cNation; }
unsigned long GetPID(void) const { return m_DBData.m_Info.PID; }
void SetPID(unsigned long dwPID);
unsigned long GetGID(void) const { return m_DBData.m_Info.GID; }
void SetGID(unsigned long dwGID);
unsigned char GetHair(void) { return m_DBData.m_Info.Hair; }
unsigned char GetFace(void) { return m_DBData.m_Info.Face; }
unsigned char GetSex(void) { return m_DBData.m_Info.Sex; } // 0 : 남자, 1 : 여자
unsigned char GetHand(void) { return m_cHandPos; }
unsigned char GetRide(void) { return m_cRidePos; }
ChState GetState(void);
const SKILL& GetSkill(void) { return m_DBData.m_Skill; }
const unsigned long GetFame(void) { return m_DBData.m_Info.Fame; }
void SetFame(unsigned long dwFame);
const unsigned long GetMileage(void) { return m_DBData.m_Info.Mileage; }
void SetMileage(unsigned long dwMileage) { m_DBData.m_Info.Mileage = dwMileage; }
bool IsAdmin(void) const { return (0 < m_DBData.m_cAdminLevel); }
unsigned char GetAdminLevel() const { return m_DBData.m_cAdminLevel; }
void SetAdminLevel(unsigned char cAdminLevel) { m_DBData.m_cAdminLevel = cAdminLevel; }
bool IsRideArms(void) const { return (0 != m_dwRideArmsCID); }
unsigned long GetRideArmsCID(void) const { return m_dwRideArmsCID; }
void Ride(unsigned long dwArmsID) { m_dwRideArmsCID = dwArmsID; }
void GetOff(void) { m_dwRideArmsCID = 0; }
void SetProtectGateCID(unsigned long dwCID) { m_dwProtectGateCID = dwCID; }
const RejectOption& GetRejectOption(void) { return m_RejectOption; }
CAggresiveCreature* GetDuelOpponent(void) { return m_FightInfo.m_pDuelOpponent; }
bool SetDuelOpponent(CCharacter* pCharacter);
CMonster* GetSummonee(void) { return m_lpSummonee; }
void SetSummonee(CMonster* lpSummonee) { m_lpSummonee = lpSummonee; }
bool CanShout(void) const;
void Shouted(void);
unsigned long GetLastTime() const { return m_dwLastTime; }
CFriendList& GetFriendList(void) { return m_friendList; }
CBanList& GetBanList(void) { return m_banList; }
inline void SaveToDBData();
inline Item::CItemContainer* GetItemContainer(unsigned char cPos);
Item::CInventoryContainer& GetInventory(void) { return m_Inventory; }
Item::CEquipmentsContainer& GetEquipments(void) { return m_Equipments; }
Item::CListContainer& GetExtra(void) { return m_ExtraSpace; }
Item::CExchangeContainer& GetExchange(void) { return m_Exchange; }
Item::CDepositContainer& GetDeposit(void) { return m_Deposit; }
Item::CCharacterShopContainer& GetStall(void) { return m_Stall; }
Item::CTempInvenContainer& GetTempInven(void) { return m_TempInven; }
// DB중계서버의 요청으로 아이템을 임시인벤에 넣어준다.
bool GiveItemByDBAgentRequest(GiveItemInfo& giveItemInfo);
inline unsigned short* GetHistoryQuest(void) { return m_wHistoryQuest; }
inline Quest::ExecutingQuest* GetExecutingQuest(void) { return m_ExecutingQuest; }
const FightStatus& GetEtcTypeStatus(Creature::StatusType eType) { return m_EtcTypeStatus[eType]; }
Broadcast2nd::CSerializeCharacterData& GetSerializeData(void) { return m_SerializeCharacterData; }
friend class Skill::CFunctions;
void SetOperationFlag(OperationFlag eOperationFlag) { m_cOperationFlags |= eOperationFlag; }
void ResetOperationFlag(OperationFlag eOperationFlag) { m_cOperationFlags &= ~eOperationFlag; }
bool IsOperationFlagSet(OperationFlag eOperationFlag) { return 0 != (m_cOperationFlags & eOperationFlag); }
unsigned long GetLastMoveUpdateTime(void) const { return m_dwLastUpdateExTime; }
void SetLastMoveUpdateTime(unsigned long dwLastUpdateTime) { m_dwLastUpdateExTime = dwLastUpdateTime; }
unsigned char GetMoveUpdateCount(void) const { return m_cMoveUpdateExCount; }
void IncrementMoveUpdateCount(void) { ++m_cMoveUpdateExCount; }
bool IsChatBan(void) const { return m_bChatBan; }
void SetChatBan(bool bBan) { m_bChatBan = bBan; }
void SetGMModelFlag(unsigned char cGMModelFlag) { m_cGMModelFlag = cGMModelFlag; }
unsigned char GetGMModelFlag() { return m_cGMModelFlag; }
unsigned char GetNameChangeCount() const { return m_cNameChangeCount; }
void SetNameChangeCount(unsigned char cNameChangeCount) { m_cNameChangeCount = cNameChangeCount; }
unsigned char GetRealmWarFlag() const { return m_cRealmWarFlag; }
unsigned char GetGuildWarFlag() const { return m_cGuildWarFlag; }
void SetRealmWarFlag(unsigned char cFlag) { m_cRealmWarFlag = cFlag; }
void SetGuildWarFlag(unsigned char cFlag);
unsigned char GetTacticsFlag() const { return m_cTactics; }
void SetTacticsFlag(unsigned char cTactics) { m_cTactics = cTactics; }
// 국가 전쟁 공헌훈장 포인트.
virtual unsigned char GetRealmPoint() { return m_cRealmPoint; }
void SetRealmPoint(unsigned char cRealmPoint) { m_cRealmPoint = cRealmPoint; }
int GetMagicChancePoint()
{
// 매찬 포인트 계상하는곳
int iMCPoint = 0;
// edtih 2009.06.20 룬에의한 매직찬스값 추가
iMCPoint = (int)m_EquipStatus.m_fLuckResistRate;
// if(GetRealmPoint() > 0)
// iMCPoint = GetRealmPoint()*5; // 랠름포인트는 1개당 5퍼센트씩
// 매찬에 스틸핸드가 미치는 영향.
if(GetEnchantInfo().GetFlag(Skill::SpellID::StealHand))
{
// 3-0부터 1래벨당 1퍼센트씩 매찬기능 추가
int iLevel = GetEnchantLevel(Skill::SpellID::StealHand)-12;
if(iLevel > 0)
iMCPoint += iLevel;
}
return iMCPoint;
}
void SetPlayTime(unsigned int dwPlayTime)
{
m_StartTime = CTime::GetCurrentTime();
m_dwPlayTime = dwPlayTime;
}
unsigned int GetPlayTime() { return m_dwPlayTime; }
void SetPremiumService(long lPremiumTime, int iPremiumType)
{
// 프리미엄 타임 남은 시간
m_StartPremiumTime = CTime::GetCurrentTime();
m_lPremiumTime = lPremiumTime;
m_iPremiumType = iPremiumType;
}
long GetPremiumTime() { return m_lPremiumTime; }
int GetPremiumType() { return m_iPremiumType; }
float GetPremiumPt()
{
if(m_iPremiumType == 1)
return 0.5f;
else if(m_iPremiumType == 2)
return 0.3f;
return 0.0f;
}
bool CheckPremiumTime()
{
if(m_lPremiumTime == 0)
return false;
CTime nowTime = CTime::GetCurrentTime();
CTimeSpan tm(m_StartPremiumTime.GetDay(), m_StartPremiumTime.GetHour(), m_StartPremiumTime.GetMinute(), m_StartPremiumTime.GetSecond());
CTime tmPrev = nowTime-tm;
int iTime = (tmPrev.GetHour()*60)+tmPrev.GetMinute();
//
iTime = m_lPremiumTime-iTime;
if(iTime <= 0)
{
m_iPremiumType = 0;
m_lPremiumTime = 0;
return false;
}
return true;
}
float GetAwardPer()
{
float fEventAdd = 0.0f;
if (CGameTimeMgr::GetInstance().GetCurrentEventTime() == GameTime::EVENT_All20)
fEventAdd = 0.2f;
else if (CGameTimeMgr::GetInstance().GetCurrentEventTime() == GameTime::EVENT_All50)
fEventAdd = 0.5f;
if(GameRYL::CHINA != CServerSetup::GetInstance().GetNationType())
return 1.0f+fEventAdd;
if(m_dwPlayTime == 0)
return 1.0f+fEventAdd;
CTime nowTime = CTime::GetCurrentTime();
CTimeSpan tm(m_StartTime.GetDay(), m_StartTime.GetHour(), m_StartTime.GetMinute(), m_StartTime.GetSecond());
CTime tmPrev = nowTime-tm;
int iTime = (tmPrev.GetHour()*60)+tmPrev.GetMinute();
iTime += m_dwPlayTime;
/*
// 테스트를 위해 플레이타음을 약간 바꿔준다.
if(iTime <= 2)
{
return 1.0f;
}
else if(2 < iTime && iTime <= 4)
{
return 0.5f;
}
*/
if(iTime <= 180)
{
return 1.0f;
}
else if(180 < iTime && iTime <= 300)
{
return 0.5f;
}
return 0.0f;
}
// 교환 관련 메소드.
unsigned long GetExchangeID() const { return m_dwExchangeID; }
void SetExchangeID(unsigned long dwExchangeID) { m_dwExchangeID = dwExchangeID; }
SPELL GetSpell() { return m_DBData.m_Spell; }
void SetSpell(SPELL spell) { m_DBData.m_Spell = spell; }
void SaveSpell(BOOL bDead = FALSE);
// 길드 관련 메소드
void SetGuildSafe(char cGuildSafe) { m_cGuildSafe = cGuildSafe; }
char GetGuildSafe() { return m_cGuildSafe; }
void SetDead(BOOL bDead) { m_bDead = bDead; }
BOOL IsDead() { return m_bDead; }
void SetRealmWarBuffer(BOOL bEnable) { m_bRealmWarBuffer = bEnable; }
BOOL IsRealmWarBuffer() { return m_bRealmWarBuffer; }
// 리스폰 타운 정보 아이디
void SetRespawnTownID(unsigned long dwTownID) { m_dwRespawnTownID = dwTownID; }
unsigned long GetRespawnTownID() const { return m_dwRespawnTownID; }
void SetAdminAbilityPoint(int point) { m_iAdminAbilityPoint = point; }
int GetAbilityPoint() { return m_iAbilityPoint; }
int GetUseAbilityPoint() { return m_iUseAbilityPoint; }
void UpdateUseAbilityPoint();
unsigned short GetAbilityValue(int nAbilityType)
{
if(nAbilityType >= Skill::Type::MAX_ABILITY_TYPE )
return 0;
return m_AbilityValue[nAbilityType];
}
CCharacter(unsigned long dwCID, unsigned long dwSessionID);
virtual ~CCharacter();
protected:
enum
{
MAX_USING_MASTERY = 2, // 동시에 최대로 적용 가능한 마스터리수
PAD_BYTE = 3 // byte alignment 를 맞추기 위한 배열의 크기
};
CharacterFightInfo m_FightInfo; // 듀얼 정보
// PeaceModeInfo m_PeaceMode; // 반전 모드 정보
Item::CInventoryContainer m_Inventory; // 인벤토리
Item::CEquipmentsContainer m_Equipments; // 장비
Item::CListContainer m_ExtraSpace; // 추가 공간
Item::CExchangeContainer m_Exchange; // 교환창
Item::CDepositContainer m_Deposit; // 창고
Item::CCharacterShopContainer m_Stall; // 노점상
Item::CTempInvenContainer m_TempInven; // 임시 인벤토리
GivenItemList m_GivenItemList; // DB중계로부터 받은 아이템들의 CreationID
CFriendList m_friendList; // 친구 리스트
CBanList m_banList; // 거부 리스트
unsigned short m_wHistoryQuest[Quest::MAX_HISTORY_QUEST]; // 수행 완료한 퀘스트
Quest::ExecutingQuest m_ExecutingQuest[Quest::MAX_EXECUTING_QUEST]; // 수행중인 퀘스트
CharacterDBData m_DBData; // DB에서 읽어 오는 데이터
RejectOption m_RejectOption; // 각종 거부 옵션
FightStatus m_EtcTypeStatus[Creature::MAX_STATUS_TYPE]; // 특수한 경우에 사용되는 스탯들
unsigned long m_dwUID; // 유저 ID
unsigned long m_dwSessionID; // 세션 ID - DB에 업데이트할 때 쓰인다.
unsigned long m_dwLastShoutTime; // 마지막으로 외친 시간
unsigned long m_dwLastSendPartyAttackInfoTime; // 마지막으로 파티원에게 공격당했다는 정보를 보낸 시간
int m_nLogoutCount; // 로그아웃시까지 남은 카운트 수 저장 (0 이하가 될 수 있으므로 반드시 signed로!)
int m_nDBUpdateCount; // DB Update때까지 남은 카운트 수 저장 (0 이하가 될 수 있으므로 반드시 signed로!)
unsigned long m_dwExchangeID; // 교환할 유저 ID.
unsigned char m_cGMModelFlag; // GM 모델로 변경 체크.
CGameClientDispatch* m_lpGameClientDispatch;
CMonster* m_lpSummonee;
unsigned long m_dwLastUpdateExTime; // 마지막 이동 업데이트 시간
unsigned long m_dwRideArmsCID; // 타고 있는 공성관련 병기의 CID
unsigned long m_dwProtectGateCID; // 성문막기를 하고 있는 성문의 CID
unsigned long m_dwRespawnSpeed; // 리스폰 속도 (pulse 사용)
LAST_DEAD_TYPE m_eLastDeadType; // 마지막에 어떻게 죽었는지 체크
Broadcast2nd::CSerializeCharacterData m_SerializeCharacterData; // 캐릭터 데이터 보관 및 변경된 데이터 보관
char m_szAccountName[CHAR_INFOST::MAX_ACCOUNT_LEN];
char m_cUsingMastery[MAX_USING_MASTERY]; // 적용중인 마스터리 종류
char m_cConsumeMPCount; // MP 소모까지 남은 카운트 수 저장
char m_cOperationFlags; // 각종 처리가 완료되었는지를 검사하는 루틴
char m_cHandPos; // HandPos
char m_cRidePos; // Ride Pos
char m_cMoveUpdateExCount; // 이동 업데이트 Count
bool m_bChatBan; // 채팅 금지
unsigned char m_cNameChangeCount; // 이름 바꿀 수 있는 회수
unsigned char m_cGuildWarFlag; // 길드 전쟁 참여 플래그
unsigned char m_cRealmWarFlag; // 국가 전챙 참여 플래그
unsigned char m_cTactics; // 용병 플레그.
char m_cGuildSafe; // edith 2008.03.15 길드 입출금 관련 플래스
unsigned char m_cRealmPoint; // 국가 전쟁 공헌훈장 포인트.
unsigned int m_dwPlayTime; // 중국판을 위한 게임 연속 플레이시간.
CTime m_StartTime;
long m_lPremiumTime; // 프리미엄 서비스
int m_iPremiumType;
CTime m_StartPremiumTime;
BOOL m_bDead;
BOOL m_bRealmWarBuffer;
unsigned long m_dwRespawnTownID;
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
unsigned char m_cAccountNation; // 계정 국적
char m_bPadding[PAD_BYTE]; // 4Byte Alignment를 맞추기 위한 도구
// edith 2009.10.22 어빌리티 작업중..
int m_iAbilityPoint;
int m_iUseAbilityPoint;
int m_iAdminAbilityPoint; // 어드민이 임시적으로 주는 포인트 (기본 0, 값은 저장되지 않음)
// edith 2009.11.10 어빌리티 능력치 제어
unsigned short m_AbilityValue[Skill::Type::MAX_ABILITY_TYPE];
};
inline void CCharacter::SaveToDBData()
{
m_DBData.m_Info.Exp = m_CreatureStatus.m_nExp;
m_DBData.m_Info.Level = m_CreatureStatus.m_nLevel;
m_DBData.m_Info.HP = m_CreatureStatus.m_nNowHP;
m_DBData.m_Info.MP = m_CreatureStatus.m_nNowMP;
m_DBData.m_Pos.LastPoint.fPointX = m_CurrentPos.m_fPointX;
m_DBData.m_Pos.LastPoint.fPointY = m_CurrentPos.m_fPointY;
m_DBData.m_Pos.LastPoint.fPointZ = m_CurrentPos.m_fPointZ;
}
inline Item::CItemContainer* CCharacter::GetItemContainer(unsigned char cPos)
{
switch(cPos)
{
case TakeType::TS_EQUIP: return &m_Equipments;
case TakeType::TS_INVEN: return &m_Inventory;
case TakeType::TS_TEMP: return &m_ExtraSpace;
case TakeType::TS_EXTRA: return &m_ExtraSpace;
case TakeType::TS_EXCHANGE: return &m_Exchange;
case TakeType::TS_DEPOSIT: return &m_Deposit;
case TakeType::TS_STALL: return &m_Stall;
case TakeType::TS_TEMPINVEN: return &m_TempInven;
}
return NULL;
}
#endif

View File

@@ -0,0 +1,670 @@
#include "stdafx.h"
#include <Creature/Character/CharacterStructure.h>
#include <Network/Packet/PacketStruct/CharStatusPacketStruct.h>
#include "CharacterClass.h"
#include "GMMemory.h"
// 홀수 레벨일 때 증가량을 기준으로 작성.
CClass ClassTable[CClass::MAX_CLASS] =
{
CClass(),
CClass(CClass::Fighter, CClass::Fighter, CClass::DEFAULT_CLASS, CClass::HUMAN, CClass::STR, 1, CClass::CON, 1, false),
CClass(CClass::Rogue, CClass::Rogue, CClass::DEFAULT_CLASS, CClass::HUMAN, CClass::DEX, 1, CClass::STR, 1, false),
CClass(CClass::Mage, CClass::Mage, CClass::DEFAULT_CLASS, CClass::HUMAN, CClass::INT, 1, CClass::DEX, 1, false),
CClass(CClass::Acolyte, CClass::Acolyte, CClass::DEFAULT_CLASS, CClass::HUMAN, CClass::WIS, 1, CClass::CON, 1, false),
CClass(CClass::Defender, CClass::Fighter, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::STR, 2, CClass::CON, 1, true),
CClass(CClass::Warrior, CClass::Fighter, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::STR, 2, CClass::CON, 1, false),
CClass(CClass::Assassin, CClass::Rogue, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::DEX, 2, CClass::STR, 1, false),
CClass(CClass::Archer, CClass::Rogue, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::DEX, 2, CClass::STR, 1, true),
CClass(CClass::Sorcerer, CClass::Mage, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::INT, 2, CClass::DEX, 1, false),
CClass(CClass::Enchanter, CClass::Mage, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::INT, 2, CClass::DEX, 1, true),
CClass(CClass::Priest, CClass::Acolyte, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::WIS, 2, CClass::CON, 1, true),
CClass(CClass::Cleric, CClass::Acolyte, CClass::JOB_CHANGE_1ST, CClass::HUMAN, CClass::WIS, 2, CClass::CON, 1, false),
CClass(),
CClass(),
CClass(),
CClass(),
CClass(CClass::Combatant, CClass::Combatant, CClass::DEFAULT_CLASS, CClass::AKHAN, CClass::STR, 1, CClass::NONE_STAT, 0, false),
CClass(CClass::Officiator, CClass::Officiator, CClass::DEFAULT_CLASS, CClass::AKHAN, CClass::DEX, 1, CClass::NONE_STAT, 0, false),
CClass(CClass::Templar, CClass::Combatant, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::STR, 2, CClass::CON, 1, true),
CClass(CClass::Attacker, CClass::Combatant, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::STR, 2, CClass::CON, 1, false),
CClass(CClass::Gunner, CClass::Combatant, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::DEX, 2, CClass::STR, 1, true),
CClass(CClass::RuneOff, CClass::Officiator, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::INT, 2, CClass::DEX, 1, false),
CClass(CClass::LifeOff, CClass::Officiator, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::WIS, 2, CClass::DEX, 1, false),
CClass(CClass::ShadowOff, CClass::Officiator, CClass::JOB_CHANGE_1ST, CClass::AKHAN, CClass::DEX, 2, CClass::STR, 1, false)
};
CClass::CClass(const JobType eJobType, const JobType ePrevJobType, const JobLevel eJobLevel, const RaceType eRace,
const CClass::StatusType eType1, const unsigned char cIncrement1,
const CClass::StatusType eType2, const unsigned char cIncrement2,
const bool bLevelSwap)
: m_eJobType(eJobType), m_ePrevJobType(ePrevJobType), m_eJobLevel(eJobLevel),
m_eRace(eRace), m_bLevelSwap(bLevelSwap)
{
m_eIncrementType[0] = eType1; m_eIncrementType[1] = eType2;
m_cIncrementValue[0] = cIncrement1; m_cIncrementValue[1] = cIncrement2;
}
CClass::CClass()
: m_eJobType(NONE_JOB), m_ePrevJobType(NONE_JOB),
m_eJobLevel(DEFAULT_CLASS), m_eRace(MAX_RACE), m_bLevelSwap(false)
{
m_eIncrementType[0] = NONE_STAT; m_eIncrementType[1] = NONE_STAT;
m_cIncrementValue[0] = 0; m_cIncrementValue[1] = 0;
}
void CClass::LevelUp(CHAR_INFOST* InfoSt)
{
unsigned char cIndex1 = m_bLevelSwap ? ((1 == InfoSt->Level % 2) ? 0 : 1) : 0;
unsigned char cIndex2 = m_bLevelSwap ? ((1 == InfoSt->Level % 2) ? 1 : 0) : 1;
IncrementByType(InfoSt, 0, cIndex1);
IncrementByType(InfoSt, 1, cIndex2);
if (AKHAN == GetRace(static_cast<unsigned char>(InfoSt->Class)))
{
if (JOB_CHANGE_1ST == GetJobLevel(static_cast<unsigned char>(InfoSt->Class)))
{
InfoSt->IP += 2;
}
}
else
{
InfoSt->IP += 2;
}
}
unsigned char CClass::GetRequiredIP(unsigned short usClass, unsigned char cType)
{
if (usClass >= MAX_CLASS || cType >= MAX_TYPE) { return 0xFF; }
if (ClassTable[usClass].m_eJobLevel == JOB_CHANGE_1ST)
{
return (ClassTable[usClass].m_eIncrementType[0] == cType ||
ClassTable[usClass].m_eIncrementType[1] == cType) ? 2 : 1;
}
for (int nClassIndex = 0; nClassIndex < MAX_CLASS; nClassIndex++)
{
if (ClassTable[nClassIndex].m_eJobType == usClass)
{
if (ClassTable[nClassIndex].m_eIncrementType[0] == cType ||
ClassTable[nClassIndex].m_eIncrementType[1] == cType)
{
return 2;
}
}
}
return 1;
}
bool CClass::JobChange(CharacterDBData* DBData, unsigned char cClassType)
{
if (DBData->m_Info.Level < 10) { return false; }
switch (m_eJobLevel)
{
case DEFAULT_CLASS: return DowngradeClass(DBData, cClassType);
case JOB_CHANGE_1ST: return UpgradeClass(DBData, cClassType);
}
return false;
}
// edith 2008.02.18 아칸전직시 스탯을 주는걸 인간과 동일, 아칸도 랩업당 +2의 스킬포인트주어지고 전직시에 주지 않는다.
bool CClass::UpgradeClass(CharacterDBData* DBData, unsigned char cClassType)
{
if (DBData->m_Info.Class != ClassTable[cClassType].m_ePrevJobType ||
ClassTable[DBData->m_Info.Class].m_eJobLevel != DEFAULT_CLASS)
{
return false;
}
// 환원되는 IP 계산을 위한 임시 변수들
unsigned short StatusA = 0;
unsigned short StatusB = 0;
unsigned short StatusC = 0;
switch (cClassType)
{
// 인간
case Defender:
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.CON += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
StatusA = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.CON - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.DEX + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Defender;
break;
case Warrior:
DBData->m_Info.STR += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.STR - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.CON - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.DEX + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Warrior;
break;
case Assassin:
DBData->m_Info.DEX += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.DEX - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.STR - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Assassin;
break;
case Archer:
DBData->m_Info.DEX += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
StatusA = DBData->m_Info.DEX - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Archer;
break;
case Sorcerer:
DBData->m_Info.INT += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.INT - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.DEX - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.STR + DBData->m_Info.CON + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Sorcerer;
break;
case Enchanter:
DBData->m_Info.INT += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.DEX += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
StatusA = DBData->m_Info.INT - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.DEX - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.STR + DBData->m_Info.CON + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Enchanter;
break;
case Priest:
DBData->m_Info.WIS += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.CON += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
StatusA = DBData->m_Info.WIS - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.CON - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.STR + DBData->m_Info.DEX + DBData->m_Info.INT - 60;
DBData->m_Info.Class = Priest;
break;
case Cleric:
DBData->m_Info.WIS += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.WIS - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.CON - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.STR + DBData->m_Info.DEX + DBData->m_Info.INT - 60;
DBData->m_Info.Class = Cleric;
break;
// 아칸
case Templar:
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
DBData->m_Info.CON += static_cast<int>((DBData->m_Info.Level - 1) * 1.5f);
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.CON - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.DEX + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Templar;
break;
case Attacker:
DBData->m_Info.STR += DBData->m_Info.Level - 1;
DBData->m_Info.CON += DBData->m_Info.Level - 1;
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.STR - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.CON - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.DEX + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Attacker;
break;
case Gunner:
DBData->m_Info.DEX += static_cast<int>((DBData->m_Info.Level - 1) * 1.5f);
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f + 0.5f);
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.DEX - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = Gunner;
break;
case RuneOff:
DBData->m_Info.INT += (DBData->m_Info.Level - 1) * 2;
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.INT - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.DEX - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.STR + DBData->m_Info.CON + DBData->m_Info.WIS - 60;
DBData->m_Info.Class = RuneOff;
break;
case LifeOff:
DBData->m_Info.WIS += (DBData->m_Info.Level - 1) * 2;
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.WIS - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.DEX - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.STR + DBData->m_Info.CON + DBData->m_Info.INT - 60;
DBData->m_Info.Class = LifeOff;
break;
case ShadowOff:
DBData->m_Info.STR += DBData->m_Info.Level - 1;
DBData->m_Info.DEX += DBData->m_Info.Level - 1;
StatusA = DBData->m_Info.DEX - (20 + (DBData->m_Info.Level - 1) * 2);
StatusB = DBData->m_Info.STR - (20 + DBData->m_Info.Level - 1);
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
/*
DBData->m_Info.STR += static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f);
DBData->m_Info.DEX += static_cast<int>((DBData->m_Info.Level - 1) / 2.0f);
DBData->m_Info.IP += (DBData->m_Info.Level - 1) * 2;
StatusA = DBData->m_Info.STR - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f));
StatusB = DBData->m_Info.DEX - (20 + static_cast<int>((DBData->m_Info.Level - 1) * 1.5f + 0.5f));
StatusC = DBData->m_Info.CON + DBData->m_Info.INT + DBData->m_Info.WIS - 60;
*/
DBData->m_Info.Class = ShadowOff;
break;
default:
return false;
}
DBData->m_Info.IP += 10 + (DBData->m_Info.Level - 1) * 2 - ((StatusA + StatusB) * 2 + StatusC) - DBData->m_Info.IP;
return true;
}
bool CClass::DowngradeClass(CharacterDBData* DBData, unsigned char cClassType)
{
// 현재는 거너만 가능하다.
if (Gunner != DBData->m_Info.Class) { return false; }
if (cClassType != ClassTable[DBData->m_Info.Class].m_ePrevJobType ||
ClassTable[DBData->m_Info.Class].m_eJobLevel != JOB_CHANGE_1ST)
{
return false;
}
// 레벨이 10% 감소한다.
DBData->m_Info.Level -= static_cast<char>(DBData->m_Info.Level * 0.1f);
DBData->m_Info.Level = max(DBData->m_Info.Level, 10);
DBData->m_Info.Exp = 0;
// 명성치가 0이 된다.
DBData->m_Info.Fame = 0;
// 스탯이 초기화된다.
switch (cClassType)
{
case Combatant:
DBData->m_Info.STR = 20 + DBData->m_Info.Level - 1;
DBData->m_Info.DEX = 20;
DBData->m_Info.CON = 20;
DBData->m_Info.INT = 20;
DBData->m_Info.WIS = 20;
DBData->m_Info.Class = Combatant;
DBData->m_Info.IP = 10;
break;
default: return false;
}
// 스킬, 퀵슬롯 초기화
DBData->m_Skill = SKILL::SKILL();
DBData->m_Quick = QUICK::QUICK();
return true;
}
bool CClass::InitializeClass(CharacterDBData* DBData, unsigned char cClassType)
{
if (ClassTable[cClassType].m_eJobLevel != DEFAULT_CLASS)
{
return false;
}
// 레벨이 10% 감소한다.
DBData->m_Info.Level = 1;
DBData->m_Info.Exp = 0;
// 명성치가 0이 된다.
DBData->m_Info.Fame = 0;
// 스탯이 초기화된다.
DBData->m_Info.STR = 20;
DBData->m_Info.DEX = 20;
DBData->m_Info.CON = 20;
DBData->m_Info.INT = 20;
DBData->m_Info.WIS = 20;
DBData->m_Info.IP = 10;
DBData->m_Info.Class = cClassType;
// 스킬, 퀵슬롯 초기화
DBData->m_Skill = SKILL::SKILL();
DBData->m_Quick = QUICK::QUICK();
return true;
}
bool CClass::CheckState(ChState& State, unsigned char cLevel)
{
// unsigned short이면 해킹으로 인해 65535가 되면 검출할수 있는 방법이 없다. int로 바꾸자.
// unsigned short wTIP = State.m_wIP;
int wTIP = State.m_wIP;
switch (m_eJobType)
{
case Fighter:
wTIP += (State.m_wSTR - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Rogue:
wTIP += (State.m_wSTR - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wDEX - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Mage:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Acolyte:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20 - (cLevel - 1)) * 2;
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Defender:
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Warrior:
wTIP += (State.m_wSTR - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Assassin:
wTIP += (State.m_wSTR - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Archer:
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wDEX - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Sorcerer:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Enchanter:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Priest:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Cleric:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20 - (cLevel - 1) * 2) * 2;
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Combatant:
wTIP += (State.m_wSTR - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > 10) { return false; }
break;
case Officiator:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > 10) { return false; }
break;
case Templar:
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Attacker:
wTIP += (State.m_wSTR - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wDEX - 20);
wTIP += (State.m_wCON - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case Gunner:
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wDEX - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case RuneOff:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case LifeOff:
wTIP += (State.m_wSTR - 20);
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20 - (cLevel - 1) * 2) * 2;
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
break;
case ShadowOff:
wTIP += (State.m_wSTR - 20 - (cLevel - 1) * 1) * 2;
wTIP += (State.m_wDEX - 20 - (cLevel - 1) * 2) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
/*
wTIP += (State.m_wSTR - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f + 0.5f)) * 2;
wTIP += (State.m_wDEX - 20 - static_cast<unsigned short>((cLevel - 1) * 1.5f)) * 2;
wTIP += (State.m_wCON - 20);
wTIP += (State.m_wINT - 20);
wTIP += (State.m_wWIS - 20);
if (wTIP > (10 + (cLevel - 1) * 2)) { return false; }
*/
break;
}
return true;
}
bool CClass::CheckMinState(ChState& State, unsigned char cLevel)
{
if (State.m_wSTR < GetMinState(STR, cLevel)) { return false; }
if (State.m_wDEX < GetMinState(DEX, cLevel)) { return false; }
if (State.m_wCON < GetMinState(CON, cLevel)) { return false; }
if (State.m_wINT < GetMinState(INT, cLevel)) { return false; }
if (State.m_wWIS < GetMinState(WIS, cLevel)) { return false; }
return true;
}
unsigned short CClass::GetMinState(StatusType eType, unsigned char cLevel)
{
// 스탯 기본값
unsigned short wResult = 20;
cLevel = cLevel - 1;
if (eType == m_eIncrementType[0] || eType == m_eIncrementType[1])
{
// 레벨에 관계없이 똑같이 성장하는 경우
if (false == m_bLevelSwap)
{
if (eType == m_eIncrementType[0])
{
wResult += cLevel * m_cIncrementValue[0];
}
if (eType == m_eIncrementType[1])
{
wResult += cLevel * m_cIncrementValue[1];
}
}
// 레벨에 따라 스탯이 번갈아가며 성장하는 경우
else
{
if (eType == m_eIncrementType[0])
{
wResult += static_cast<unsigned short>(cLevel * 1.5f);
}
if (eType == m_eIncrementType[1])
{
wResult += static_cast<unsigned short>(cLevel * 1.5f + 0.5f);
}
}
}
return wResult;
}
CClass::JobLevel CClass::GetJobLevel(unsigned char cClass)
{
if (cClass >= MAX_CLASS) { return DEFAULT_CLASS; }
return ClassTable[cClass].m_eJobLevel;
}
unsigned char CClass::GetPreviousJob(unsigned char cClass)
{
if (cClass >= MAX_CLASS) { return NONE_JOB; }
return ClassTable[cClass].m_ePrevJobType;
}
CClass::RaceType CClass::GetRace(unsigned char cClass)
{
if (cClass >= MAX_CLASS) { return MAX_RACE; }
return ClassTable[cClass].m_eRace;
}

View File

@@ -0,0 +1,120 @@
#ifndef _CHARACTER_CLASS_H_
#define _CHARACTER_CLASS_H_
#include <DB/DBDefine.h>
// 전방 참조
struct CharacterDBData;
struct ChState;
class CClass
{
public:
enum JobType
{
NONE_JOB = 0,
// 인간 기본클래스
Fighter = 1, Rogue = 2,
Mage = 3, Acolyte = 4,
// 인간 1st클래스
Defender = 5, Warrior = 6,
Assassin = 7, Archer = 8,
Sorcerer = 9, Enchanter = 10,
Priest = 11, Cleric = 12,
// 아칸 기본클래스
Combatant = 17, Officiator = 18,
// 아칸 1st클래스
Templar = 19, Attacker = 20,
Gunner = 21, RuneOff = 22,
LifeOff = 23, ShadowOff = 24,
MAX_CLASS = 25
};
enum StatusType
{
NONE_STAT = 0,
STR = 1,
DEX = 2,
CON = 3,
INT = 4,
WIS = 5,
MAX_TYPE = 6
};
enum JobLevel
{
NONE_CLASS = 0,
DEFAULT_CLASS = 1,
JOB_CHANGE_1ST = 2
};
enum RaceType
{
HUMAN = 0,
AKHAN = 1,
MAX_RACE = 2
};
CClass(const JobType eJobType, const JobType ePrevJobType, const JobLevel eJobLevel, const RaceType eRace,
const StatusType eType1, const unsigned char cIncrement1,
const StatusType eType2, const unsigned char cIncrement2,
const bool bLevelSwap);
CClass();
void LevelUp(CHAR_INFOST* InfoSt);
bool JobChange(CharacterDBData* DBData, unsigned char cClassType);
bool UpgradeClass(CharacterDBData* DBData, unsigned char cClassType);
bool DowngradeClass(CharacterDBData* DBData, unsigned char cClassType);
bool InitializeClass(CharacterDBData* DBData, unsigned char cClassType); // 레벨 1의 기본 클래스로 만든다.
bool CheckState(ChState& State, unsigned char cLevel);
bool CheckMinState(ChState& State, unsigned char cLevel);
unsigned short GetMinState(StatusType eType, unsigned char cLevel);
static JobLevel GetJobLevel(unsigned char cClass);
static unsigned char GetPreviousJob(unsigned char cClass);
static unsigned char GetRequiredIP(unsigned short usClass, unsigned char cType);
static RaceType GetRace(unsigned char cClass);
protected:
StatusType m_eIncrementType[2]; // 성장시 추가 능력치 타입
unsigned char m_cIncrementValue[2]; // 성장시 추가 능력치 값
JobType m_eJobType; // 현재 직업
JobType m_ePrevJobType; // 바로 하위 클래스의 직업
JobLevel m_eJobLevel; // 현재 직업의 레벨
RaceType m_eRace; // 소속 종족
bool m_bLevelSwap; // 레벨에 따라 스탯이 번갈아가면서 성장함 (짝수 2/1 성장이면 홀수 1/2 성장)
inline void IncrementByType(CHAR_INFOST* InfoSt,
unsigned char cTypeIndex, unsigned char cIncrementIndex);
};
extern CClass ClassTable[CClass::MAX_CLASS];
inline void CClass::IncrementByType(CHAR_INFOST* InfoSt,
unsigned char cTypeIndex, unsigned char cIncrementIndex)
{
switch(m_eIncrementType[cTypeIndex]) {
case STR: InfoSt->STR += m_cIncrementValue[cIncrementIndex]; break;
case DEX: InfoSt->DEX += m_cIncrementValue[cIncrementIndex]; break;
case CON: InfoSt->CON += m_cIncrementValue[cIncrementIndex]; break;
case INT: InfoSt->INT += m_cIncrementValue[cIncrementIndex]; break;
case WIS: InfoSt->WIS += m_cIncrementValue[cIncrementIndex]; break;
}
}
#endif

View File

@@ -0,0 +1,29 @@
#include "stdafx.h"
#include "Character.h"
#include <Creature/CreatureManager.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Packet/ChatPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CharCommunityPacket.h>
#include <mmsystem.h>
bool CCharacter::CanShout() const
{
const long MAX_SHOUT_DELAY = 15000;
unsigned long dwCurrentTime = timeGetTime();
return (0 == m_dwLastShoutTime || MAX_SHOUT_DELAY < dwCurrentTime - m_dwLastShoutTime);
}
void CCharacter::Shouted()
{
m_dwLastShoutTime = timeGetTime();
if(0 == m_dwLastShoutTime)
{
++m_dwLastShoutTime;
}
}

View File

@@ -0,0 +1,315 @@
#include "stdafx.h"
#include "Character.h"
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharEtc.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/GameClient/SendCharLoginOut.h>
#include <Network/Dispatch/GameClient/SendCharCommunity.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
#include <Network/Packet/ChatPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CharStatusPacket.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Item/Item.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/Cell.h>
#include <Map/FieldMap/CellManager.h>
#include <Community/Party/Party.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/CharRespawnMgr.h>
#include <Log/CharacterLog.h>
#include <Utility/Math/Math.h>
#include <utility/Setup/ServerSetup.h>
bool CCharacter::DropItem(unsigned short usProtoTypeID, unsigned char cNum)
{
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem(usProtoTypeID);
if (NULL != lpItem)
{
lpItem->SetNumOrDurability(min(cNum, lpItem->GetMaxNumOrDurability()));
// edith 2009.08.19 DropItem 어드민 명령어로 장비 드랍시 시즌레코드 수정.
Item::CEquipment* lpEquip = Item::CEquipment::DowncastToEquipment(lpItem);
if(lpEquip && lpEquip->GetSeasonRecord() == 0)
lpEquip->SetNewEquip();
if (false == GiveItem(lpItem))
{
ERRLOG2(g_Log, "CID:%10u %d종류의 아이템을 떨구지 못했습니다.",
GetCID(), usProtoTypeID);
DELETE_ITEM(lpItem);
return false;
}
// GievItem 으로 스택된 경우
if (lpItem->IsSet(Item::DetailData::STACKABLE) && 0 == lpItem->GetNumOrDurability())
{
DELETE_ITEM(lpItem);
}
}
return true;
}
bool CCharacter::MovePos(Position Pos, char cZone, const bool bSitDown)
{
if (0 != CCellManager::GetInstance().GetCell(m_CellPos.m_wMapIndex, Pos.PositionToPOS()))
{
DETLOG5(g_Log, "CID:%10u 워프 명령을 사용하였습니다. NewPos : %.1f, %.1f, %.1f, Zone : %d",
m_dwCID, Pos.m_fPointX, Pos.m_fPointY, Pos.m_fPointZ, cZone);
// edith 2009.07.21 워프나 포탈 사용시 현재 좌표갱신하기 (불법이동 방지 검출 피하기 위해서)
m_CurrentPos.m_fPointX = Pos.m_fPointX;
m_CurrentPos.m_fPointZ = Pos.m_fPointZ;
return GameClientSendPacket::SendCharBindPosition(*this, 0, PktBP::BP_WARP, Pos, cZone, 0);
}
return false;
}
bool CCharacter::MoveZone(POS NewPos, char cZone, char Channel)
{
if (IsOperationFlagSet(CCharacter::MOVEZONE_PROCESSED))
{
return true;
}
// BG_TODO : 배틀 그라운드 전용 서버군 작업시 추가해야 한다.
/*
if (cZone == SERVER_ID::ZONE3)
{
SYSTEMTIME systemTime;
GetSystemTime(&systemTime);
// 배틀 그라운드 휴식 시간동안에는 진입을 할수 없다.
if ((systemTime.wMinute >= CCreatureManager::STATUE_REST_TIME_1ST_START && systemTime.wMinute <= CCreatureManager::STATUE_REST_TIME_1ST_END) ||
(systemTime.wMinute >= CCreatureManager::STATUE_REST_TIME_2ND_START && systemTime.wMinute <= CCreatureManager::STATUE_REST_TIME_2ND_END))
{
return false;
}
}
*/
if (NULL != m_lpGameClientDispatch)
{
CDBRequest DBRequest(*m_lpGameClientDispatch, 600);
if (DBRequest.IsValid())
{
if (GameClientSendPacket::SendMoveZoneToDBAgent(DBRequest.GetSendStream(),
NewPos, DBRequest.GetRequestKey(), GetUID(), cZone, Channel))
{
char* szPeaceMode = (true == IsPeaceMode()) ? "평화모드" : "전쟁모드";
DETLOG5(g_Log, "UID:%d/CID:%10u(0x%p)/RequestKey:%d/DispatchPointer:0x%p 존 이동 명령을 사용하였습니다.",
m_dwUID, m_dwCID, this, DBRequest.GetRequestKey(), m_lpGameClientDispatch);
DETLOG8(g_Log, "UID:%d/CID:%10u/ 존이동 당시의 정보입니다. NewPos : (%.1f, %.1f, %.1f), Zone : %d, Channel : %d (%s)",
m_dwUID, m_dwCID, NewPos.fPointX, NewPos.fPointY, NewPos.fPointZ, cZone, Channel, szPeaceMode);
GAMELOG::LogZoneMove(*this, cZone, Channel, 0);
SetOperationFlag(CCharacter::MOVEZONE_PROCESSED);
m_lpGameClientDispatch->PushRequestKey(DBRequest.GetRequestKey());
return true;
}
DBRequest.CancelRequest();
}
}
return false;
}
// 길드전, 국가전에 해당하는 존으로 이동
void CCharacter::MoveToGuildWarZone()
{
const Position targetPos = CCharRespawnMgr::GetInstance().GetTownRespawnPos(SERVER_ID::CAPITAL, GetNation());
if (CServerSetup::GetInstance().GetServerZone() == SERVER_ID::CAPITAL)
{
MoveTo(targetPos, false);
POS pos;
pos.fPointX = targetPos.m_fPointX;
pos.fPointY = targetPos.m_fPointY;
pos.fPointZ = targetPos.m_fPointZ;
GameClientSendPacket::SendCharBindPosition(*this, 0, PktBP::BP_WARP, pos, CServerSetup::GetInstance().GetServerZone(), 0);
}
else
{
POS pos;
pos.fPointX = targetPos.m_fPointX;
pos.fPointY = targetPos.m_fPointY;
pos.fPointZ = targetPos.m_fPointZ;
MoveZone(pos, SERVER_ID::CAPITAL, -1);
}
}
void CCharacter::MoveToRealmWarZone()
{
const int iLevel = GetLevel();
int iZone = SERVER_ID::STONE_WAR1;
// edith 2009.06.13 고렙쟁과 저렙쟁 구분
if(iLevel <= 60)
iZone = SERVER_ID::STONE_WAR1;
else //if(iLevel <= 80)
iZone = SERVER_ID::STONE_WAR2;
// else
// iZone = SERVER_ID::STONE_WAR3;
const Position targetPos = CCharRespawnMgr::GetInstance().GetTownRespawnPos(iZone, GetNation());
if (CServerSetup::GetInstance().GetServerZone() == iZone)
{
MoveTo(targetPos, false);
POS pos;
pos.fPointX = targetPos.m_fPointX;
pos.fPointY = targetPos.m_fPointY;
pos.fPointZ = targetPos.m_fPointZ;
GameClientSendPacket::SendCharBindPosition(*this, 0, PktBP::BP_WARP, pos, CServerSetup::GetInstance().GetServerZone(), 0);
}
else
{
POS pos;
pos.fPointX = targetPos.m_fPointX;
pos.fPointY = targetPos.m_fPointY;
pos.fPointZ = targetPos.m_fPointZ;
MoveZone(pos, iZone, -1);
}
}
bool CCharacter::Kill(CAggresiveCreature* lpAttacker)
{
if (true == Dead(NULL))
{
AtType Type = {0,};
Type.m_wType = 1;
const unsigned short wDamage = m_CreatureStatus.m_nNowHP;
m_CreatureStatus.m_nNowHP = 0;
// 공격자가 NULL 이면 운영자가 다른 존에 있는 캐릭터를 운영자 명령으로 죽인 경우이다.
// 이럴때는 자기가 죽인것으로 처리한다.
if (NULL == lpAttacker) lpAttacker = this;
if (NULL != m_lpGameClientDispatch)
{
return GameClientSendPacket::SendCharAttacked(m_lpGameClientDispatch->GetSendStream(),
lpAttacker, this, Type, 0, wDamage, 0, 0, PktBase::NO_SERVER_ERR);
}
return true;
}
return false;
}
bool CCharacter::NotifyInfo(unsigned long dwAdminCID)
{
char szMessage[PktChat::PktChatMaxSize] = "";
int nLen = _snprintf(szMessage, PktChat::PktChatMaxSize,
"Name:%s, Class:%d, HP:%3d%%, MP:%3d%%, Zone:%d, X:%.1f, Y:%.1f, Z:%.1f, "
"MinD:%d, MaxD:%d, HitRate:%d, Evade:%d, Armor:%d",
GetCharacterName(), m_DBData.m_Info.Class,
m_CreatureStatus.m_nNowHP * 100 / m_CreatureStatus.m_StatusInfo.m_nMaxHP,
m_CreatureStatus.m_nNowMP * 100 / m_CreatureStatus.m_StatusInfo.m_nMaxMP,
CServerSetup::GetInstance().GetServerZone(), m_CurrentPos.m_fPointX, m_CurrentPos.m_fPointY, m_CurrentPos.m_fPointZ,
m_CreatureStatus.m_StatusInfo.m_lMinDamage, m_CreatureStatus.m_StatusInfo.m_lMaxDamage,
m_CreatureStatus.m_StatusInfo.m_wHitRate,
m_CreatureStatus.m_StatusInfo.m_wEvade,
m_CreatureStatus.m_StatusInfo.m_wArmor);
if (0 < nLen)
{
szMessage[PktChat::PktChatMaxSize - 1] = 0;
// 채팅 서버로 정보를 보낸다.
GET_SINGLE_DISPATCH(lpChatDispatch, CChatDispatch,
CChatDispatch::GetDispatchTable());
if (0 != lpChatDispatch)
{
const Position& pos = GetCurrentPos();
char strAdminCID[CHAR_INFOST::MAX_NAME_LEN] = "";
strcpy(strAdminCID, "0x");
char* strHexPos = (strAdminCID + 2);
Math::Convert::Hex32ToStr(strHexPos, dwAdminCID);
CChatRequestPacket chatReqPacket(szMessage,
PktChat::NOTIFY_CHAR_INFO, 0, GetUID(), GetCID(),
static_cast<unsigned short>(pos.m_fPointX),
static_cast<unsigned short>(pos.m_fPointY),
static_cast<unsigned short>(pos.m_fPointZ),
strAdminCID, 1);
if (chatReqPacket.IsValid())
{
return lpChatDispatch->GetSendStream().PutBuffer(
chatReqPacket.GetCompressedPacket(),
chatReqPacket.GetCompressedSize(), CmdCharChat);
}
}
return true;
}
return false;
}
bool CCharacter::DuelInit(unsigned char cCmd)
{
// 듀얼중이라면 듀얼 취소
CCharacter* lpDuelOpponent = static_cast<CCharacter*>(GetDuelOpponent());
if (NULL != lpDuelOpponent)
{
CGameClientDispatch* lpOpponentDispatch = lpDuelOpponent->GetDispatcher();
if (NULL != lpOpponentDispatch)
{
GameClientSendPacket::SendCharDuelCmd(lpOpponentDispatch->GetSendStream(), m_dwCID,
lpDuelOpponent->GetCID(), cCmd, PktDuC::NO_SERVER_ERR);
}
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharDuelCmd(m_lpGameClientDispatch->GetSendStream(), m_dwCID,
lpDuelOpponent->GetCID(), cCmd, PktDuC::NO_SERVER_ERR);
}
lpDuelOpponent->SetDuelOpponent(NULL);
SetDuelOpponent(NULL);
}
// 팀배틀에서 빠짐
if (NULL != m_pParty)
{
if (NULL != reinterpret_cast<CCharacterParty* >(m_pParty)->GetHostileParty())
{
if (PktDuC::DUC_CANCEL == cCmd ||
0 == reinterpret_cast<CCharacterParty* >(m_pParty)->DropMember(this, static_cast<PktDuC::DuelCmd>(cCmd)))
{
reinterpret_cast<CCharacterParty* >(m_pParty)->GetHostileParty()->EndTeamBattle();
reinterpret_cast<CCharacterParty* >(m_pParty)->EndTeamBattle();
}
}
}
return true;
}

View File

@@ -0,0 +1,328 @@
#include "stdafx.h"
#include <Item/Item.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Utility/Math/Math.h>
#include <Utility/Setup/ServerSetup.h>
#include "CharacterClass.h"
#include "CharacterCreate.h"
bool CharCreate::CheckCharCreateName(const char *Name_In, bool HanCheck_In)
{
const unsigned short MIN_CHAR_NAME = 4;
const unsigned short MAX_CHAR_NAME = 20;
if(Name_In == NULL)
{
return false;
}
// 길이 제한
size_t Len = strlen(Name_In);
if(Len < MIN_CHAR_NAME || Len > MAX_CHAR_NAME)
{
return false;
}
LPBYTE CheckName = (LPBYTE)Name_In;
if(true == HanCheck_In)
{
// 제한 문자 검사
int ACount = 0;
for(unsigned short LCount = 0; LCount < Len; LCount++)
{
if((CheckName[LCount] & 0x80) == 0x80)
{
// 2Byte 문자 체크
if(CheckName[LCount + 1] == NULL)
{
return false;
}
// 허용 범위 체크 (한글)
if(CheckName[LCount] < 0xB0 || CheckName[LCount] > 0xC9)
{
return false;
}
if(CheckName[LCount + 1] < 0xA1 || CheckName[LCount + 1] > 0xFE)
{
return false;
}
// 한글 부분 불 허용
for(ACount = 0; ACount < ALLOW_HAN_NUM; ACount++)
{
if(MAKEWORD(CheckName[LCount + 1], CheckName[LCount]) == AllowHans[ACount])
{
break;
}
}
if(ACount != ALLOW_HAN_NUM)
{
return false;
}
LCount += 1;
continue;
}
else
{
// 영문자 허용
if((CheckName[LCount] >= 'A' && CheckName[LCount] <= 'Z') ||
(CheckName[LCount] >= 'a' && CheckName[LCount] <= 'z'))
{
continue;
}
// 숫자 허용
if(CheckName[LCount] >= '0' && CheckName[LCount] <= '9')
{
continue;
}
// 특수 기호 부분 허용
for(ACount = 0; ACount < ALLOW_LETTER_NUM; ACount++)
{
if(CheckName[LCount] == AllowLetters[ACount])
{
break;
}
}
if(ACount == ALLOW_LETTER_NUM)
{
return false;
}
}
}
}
else
{
if(!strcmp(Name_In, ""))
{
return false;
}
/////////////////////////////////////////////////////////////
// 영문, 숫자, 특수기호만 입력가능.
// 제한 문자 검사
int ACount = 0;
for(unsigned short LCount = 0; LCount < Len; LCount++)
{
// 영문자 허용
if((CheckName[LCount] >= 'A' && CheckName[LCount] <= 'Z') ||
(CheckName[LCount] >= 'a' && CheckName[LCount] <= 'z'))
{
continue;
}
// 숫자 허용
if(CheckName[LCount] >= '0' && CheckName[LCount] <= '9')
{
continue;
}
// 특수 기호 부분 허용
for(ACount = 0; ACount < ALLOW_LETTER_NUM; ACount++)
{
if(CheckName[LCount] == AllowLetters[ACount])
{
break;
}
}
if(ACount == ALLOW_LETTER_NUM)
{
return false;
}
}
// 특수기호 사용못함
for(int LCount = 0; LCount < DISALLOW_LETTER_NUM; LCount++)
{
if(_tcschr(Name_In, DisAllowLetters[LCount]) != NULL)
{
return false;
}
}
}
return true;
}
bool CharCreate::CheckCharCreateData(CHAR_CREATE &CharCreate_In)
{
if (CharCreate_In.Race == CClass::HUMAN)
{
if(CharCreate_In.Equip[Item::EquipmentPos::SHIRT] < 201 ||
CharCreate_In.Equip[Item::EquipmentPos::SHIRT] > 209)
{
return false;
}
if(601 != CharCreate_In.Equip[Item::EquipmentPos::BOOTS])
{
return false;
}
if(CClass::Fighter == CharCreate_In.Class)
{ // 전사
if(701 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON_HAND1])
{
return false;
}
}
else if(CClass::Rogue == CharCreate_In.Class)
{ // 로그
if(1601 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON_HAND1])
{
return false;
}
}
else if(CClass::Mage == CharCreate_In.Class)
{ // 메이지
if(1501 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON_HAND1])
{
return false;
}
}
else if(CClass::Acolyte == CharCreate_In.Class)
{ // 어콜라이트
if(801 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON_HAND1])
{
return false;
}
}
if(CharCreate_In.STR < 20 || CharCreate_In.CON < 20 ||
CharCreate_In.DEX < 20 || CharCreate_In.INT < 20 || CharCreate_In.WIS < 20)
{
return false;
}
if(CharCreate_In.STR + CharCreate_In.CON +
CharCreate_In.DEX + CharCreate_In.INT + CharCreate_In.WIS > 105)
{
return false;
}
}
else if (CharCreate_In.Race == CClass::AKHAN)
{
if(CClass::Combatant == CharCreate_In.Class)
{ // 컨배턴트
if(5401 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON])
{
return false;
}
if(5101 != CharCreate_In.Equip[Item::EquipmentPos::BODY])
{
return false;
}
if(5201 != CharCreate_In.Equip[Item::EquipmentPos::PELVIS])
{
return false;
}
}
else if(CClass::Officiator == CharCreate_In.Class)
{ // 오피세이터
if(5801 != CharCreate_In.Equip[Item::EquipmentPos::WEAPON])
{
return false;
}
if(5110 != CharCreate_In.Equip[Item::EquipmentPos::BODY])
{
return false;
}
if(5210 != CharCreate_In.Equip[Item::EquipmentPos::PELVIS])
{
return false;
}
}
if(CharCreate_In.STR < 20 || CharCreate_In.CON < 20 ||
CharCreate_In.DEX < 20 || CharCreate_In.INT < 20 || CharCreate_In.WIS < 20)
{
return false;
}
if(CharCreate_In.STR + CharCreate_In.CON + CharCreate_In.DEX +
CharCreate_In.INT + CharCreate_In.WIS > 105)
{
return false;
}
}
else
{
return false;
}
return true;
}
unsigned long CharCreate::GetDefaultStartGold(void)
{
return START_GOLD;
}
POS CharCreate::GetDefaultCharacterPos(unsigned long dwRace, unsigned long dwRacePlayerNum)
{
if (true == CServerSetup::GetInstance().IsBattleAuthServer() ||
true == CServerSetup::GetInstance().IsBattleGameServer())
{
POS StartPos = BGServerStartPos[dwRace][Math::Random::ComplexRandom(MAX_LOBBY_RESPAWN_POS)];
StartPos.fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
StartPos.fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
return StartPos;
}
// 통합존의 생성 위치는 하나이다.
unsigned long dwIndex = 0;
if (CClass::MAX_RACE <= dwRace)
{
dwRace = 0;
}
for (; dwIndex < MAX_START_POS_NUM; ++dwIndex)
{
if (dwRacePlayerNum < StartPointVariation[dwIndex])
{
if (0 < dwIndex)
{
dwIndex = Math::Random::ComplexRandom(dwIndex + 1);
}
break;
}
}
POS startPos = StartPosNum[dwRace][dwIndex];
startPos.fPointX += static_cast<float>(Math::Random::ComplexRandom(14)) - 7.0f;
startPos.fPointZ += static_cast<float>(Math::Random::ComplexRandom(14)) - 7.0f;
return startPos;
}
inline bool operator == (const POS& lhs, const POS& rhs)
{
return (lhs.fPointX == rhs.fPointX) && (lhs.fPointY == rhs.fPointY) && (lhs.fPointZ == rhs.fPointZ);
}
inline bool operator != (const POS& lhs, const POS& rhs)
{
return !(lhs == rhs);
}

View File

@@ -0,0 +1,105 @@
#include "CharacterClass.h"
namespace CharCreate
{
enum Const
{
START_GOLD = 0,
MAX_START_POS_NUM = 6,
MAX_LOBBY_RESPAWN_POS = 2
};
// 파트 1용 리젠포인트 갔다 써라.
const POS StartPosNum[CClass::MAX_RACE][MAX_START_POS_NUM] = {
// 휴먼
{
{ 2740, 23, 606 },
{ 2738, 24, 647 },
{ 2801, 22, 644 },
{ 1442, 12, 1973 },
{ 1464, 12, 2064 },
{ 1419, 12, 2065 }
},
// 아칸
{
{ 2446, 33, 3208 },
{ 2370, 32, 3233 },
{ 2324, 32, 3244 },
{ 2059, 65, 1663 },
{ 2014, 65, 1648 },
{ 2009, 65, 1690 }
}
};
/*
const POS StartPosNum[CClass::MAX_RACE][MAX_START_POS_NUM] = {
// 휴먼
{
{ 2812, 15, 2498 },
{ 2817, 15, 2471 },
{ 2792, 15, 2515 },
{ 2792, 15, 2515 },
{ 2792, 15, 2515 },
{ 2792, 15, 2515 }
},
// 아칸
{
{ 2812, 15, 2498 },
{ 2834, 15, 2482 },
{ 2804, 15, 2514 },
{ 2804, 15, 2514 },
{ 2804, 15, 2514 },
{ 2804, 15, 2514 }
}
};
*/
const POS BGServerStartPos[CClass::MAX_RACE][MAX_LOBBY_RESPAWN_POS] = {
// Human
{
{ 2165, 1135, 1005 },
{ 1727, 1135, 1005 }
},
// Akhan
{
{ 2119, 1132, 1841 },
{ 1683, 1132, 1841 }
}
};
const unsigned long StartPointVariation[MAX_START_POS_NUM] = {
500, 1000, 1500, 2000, 2500, 0xFFFFFFFF
};
const unsigned short ALLOW_HAN_NUM = 39;
const unsigned short AllowHans[ALLOW_HAN_NUM] = {
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', '', '',
'', '', '', ''
};
const unsigned short ALLOW_LETTER_NUM = 2;
const char AllowLetters[ALLOW_LETTER_NUM] = {
'-', '_'
};
const unsigned short DISALLOW_LETTER_NUM = 9;
const char DisAllowLetters[DISALLOW_LETTER_NUM] = {
' ', '\'', '\"', '#', '~', '!', '@', '[', ']'
};
bool CheckCharCreateName(const char *Name_In, bool HanCheck_In);
bool CheckCharCreateData(CHAR_CREATE &CharCreate_In);
unsigned long GetDefaultStartGold(void);
POS GetDefaultCharacterPos(unsigned long dwRace, unsigned long dwRacePlayerNum);
};

View File

@@ -0,0 +1,356 @@
#include "stdafx.h"
#include "Character.h"
#include <Community/Party/Party.h>
#include <Community/Guild/GuildConstants.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include <Utility/Math/Math.h>
#include <Log/LogCommands.h>
#include <Log/CharacterLog.h>
#include <Item/Container/ItemContainer.h>
#include <Item/ItemFactory.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CharLoginOutPacket.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <RylChatServer/ChatGameServerDispatch.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
bool CCharacter::DBUpdate(DBUpdateData::UpdateType eUpdateType)
{
if(!IsOperationFlagSet(CHAR_INFO_LOADED) || (m_bLogout && DBUpdateData::LOGOUT != eUpdateType))
{
return false;
}
if(--m_nDBUpdateCount > 0)
{
return false;
}
m_nDBUpdateCount = DBUPDATE_COUNT;
int nTotalSize = sizeof(PktDBUpdate) + DBUpdateData::MAX_DBUPDATE_SIZE;
char szCharInfo[sizeof(PktDBUpdate) + DBUpdateData::MAX_DBUPDATE_SIZE];
char* lpCharacterInfo = szCharInfo + sizeof(PktDBUpdate);
PktDBUpdate* lpPktDBUpdate = reinterpret_cast<PktDBUpdate*>(szCharInfo);
memset(lpPktDBUpdate, 0, sizeof(PktDBUpdate));
unsigned short usLogError = 0;
unsigned char cLogCMD = (DBUpdateData::LOGOUT == eUpdateType) ?
GAMELOG::CMD::CHAR_LOGOUT : GAMELOG::CMD::CHAR_DBUPDATE;
unsigned char cAdmin = (true == IsAdmin()) ? 1 : 0;
if(!GetCharacterInfo(lpCharacterInfo, &nTotalSize, lpPktDBUpdate->m_usUpdate))
{
nTotalSize = 0; usLogError = 1;
ERRLOG1(g_Log, "CID:0x%08x DBUpdate실패 : 데이터를 복사해 올 수 없습니다.", m_dwCID);
}
else
{
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 == lpDBAgentDispatch)
{
usLogError = 2;
ERRLOG1(g_Log, "CID:0x%08x DBUpdate실패 : DBAgentDispatch 를 얻을 수 없습니다.", m_dwCID);
}
else
{
CSendStream& AgentSendStream = lpDBAgentDispatch->GetSendStream();
// 창고 데이터 업데이트(창고가 열려 있으면 업데이트한다.)
// 순서 주의! DB에 Update할 때는 창고 업데이트 후 캐릭터를 업데이트한다.
if(!m_Deposit.DBUpdate(AgentSendStream))
{
ERRLOG1(g_Log, "CID:0x%08x 창고 업데이트 실패", m_dwCID);
}
lpPktDBUpdate->m_dlItemSerial = Item::CItemFactory::GetInstance().GetItemUID();
lpPktDBUpdate->m_dwSessionID = m_dwSessionID;
lpPktDBUpdate->m_dwUserID = m_dwUID;
lpPktDBUpdate->m_dwCharID = m_dwCID;
lpPktDBUpdate->m_TypeCode = eUpdateType;
lpPktDBUpdate->m_dwRequestKey = 0;
lpPktDBUpdate->m_Address.S_un.S_addr = 0;
lpPktDBUpdate->m_cAdminLevel = 0;
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
lpPktDBUpdate->m_cAccountNation = 0;
lpPktDBUpdate->m_cNameChangeCount = 0;
lpPktDBUpdate->m_cGuildWarFlag = 0;
lpPktDBUpdate->m_cRealmWarFlag = 0;
lpPktDBUpdate->m_cRealmPoint = 0;
lpPktDBUpdate->m_cTacticsFlag = 0;
if(!AgentSendStream.WrapCompress(reinterpret_cast<char*>(lpPktDBUpdate),
static_cast<unsigned short>(sizeof(PktDBUpdate) + nTotalSize), CmdDBUpdateData, 0, 0))
{
usLogError = 3;
ERRLOG1(g_Log, "CID:0x%08x DBUpdate실패 : WrapCompress를 실패했습니다.", m_dwCID);
}
}
}
if(DBUpdateData::LOGOUT == eUpdateType || 0 != usLogError)
{
SOCKADDR_IN remoteAddr;
if (0 != m_lpGameClientDispatch)
{
remoteAddr = m_lpGameClientDispatch->GetRemoteAddr().get_addr_in();
}
else
{
memset(&remoteAddr, 0, sizeof(SOCKADDR_IN));
}
GAMELOG::LogCharLoginOut(m_dwUID, this, &remoteAddr, lpCharacterInfo, nTotalSize,
lpPktDBUpdate->m_usUpdate, cLogCMD, usLogError);
}
LOG_INOUT(
char szExp[64];
const char* szUpdateType = "Unknown";
switch (eUpdateType)
{
case DBUpdateData::LOGIN: szUpdateType = "Login"; break;
case DBUpdateData::LOGOUT: szUpdateType = "Logout"; break;
case DBUpdateData::UPDATE: szUpdateType = "Update"; break;
case DBUpdateData::ADMIN_LOGIN: szUpdateType = "AdminLogin"; break;
case DBUpdateData::ZONEMOVE: szUpdateType = "ZoneMove"; break;
}
unsigned long dwDispatchUID = (NULL != m_lpGameClientDispatch) ?
m_lpGameClientDispatch->GetUID() : 0;
Math::Convert::Hex64ToStr(szExp, m_DBData.m_Info.Exp);
DETLOG8(g_Log, "UID:%d/CID:0x%08x(0x%p)/DispatchUID:%d 의 캐릭터 정보를 DBAgent에 업데이트합니다. "
" 업데이트 타입은 %s입니다. 몇가지 기본 정보를 찍습니다. %s(lev:%2d, exp:%s)",
m_dwUID, m_dwCID, this, dwDispatchUID, szUpdateType, m_DBData.m_Info.Name,
m_DBData.m_Info.Level, szExp)
);
return true;
}
bool CCharacter::GetCharacterInfo(char* pBuffer, int* nBufferSize_InOut, unsigned short* lpUpdateLen)
{
unsigned long dwSize = 0;
unsigned short usTotalSize = 0;
if(!IsOperationFlagSet(CHAR_INFO_LOADED))
{
ERRLOG1(g_Log, "CID:0x%08x 캐릭터 정보가 세팅되지 않은 상태에서 캐릭터 정보를 얻으려 하였습니다.",
m_dwCID);
return false;
}
SaveToDBData();
for (int nCount = 0; nCount < DBUpdateData::MAX_UPDATE_DB; ++nCount)
{
switch (nCount)
{
case DBUpdateData::STATUS_UPDATE:
{
*reinterpret_cast<CHAR_INFOST*>(pBuffer) = m_DBData.m_Info;
lpUpdateLen[nCount] = sizeof(CHAR_INFOST);
}
break;
case DBUpdateData::POSITION_UPDATE:
*reinterpret_cast<CHAR_POS*>(pBuffer) = m_DBData.m_Pos;
lpUpdateLen[nCount] = sizeof(CHAR_POS);
break;
case DBUpdateData::SKILL_UPDATE:
*reinterpret_cast<SKILL*>(pBuffer) = m_DBData.m_Skill;
lpUpdateLen[nCount] = sizeof(SKILL);
break;
case DBUpdateData::QUICKSLOT_UPDATE:
*reinterpret_cast<QUICK*>(pBuffer) = m_DBData.m_Quick;
lpUpdateLen[nCount] = sizeof(QUICK);
break;
case DBUpdateData::SPELL_UPDATE:
*reinterpret_cast<SPELL*>(pBuffer) = m_DBData.m_Spell;
lpUpdateLen[nCount] = sizeof(SPELL);
break;
case DBUpdateData::ITEM_EQUIP_UPDATE:
dwSize = *nBufferSize_InOut; m_Equipments.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
case DBUpdateData::ITEM_INVEN_UPDATE:
dwSize = *nBufferSize_InOut; m_Inventory.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
case DBUpdateData::ITEM_EXTRA_UPDATE:
dwSize = *nBufferSize_InOut; m_ExtraSpace.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
case DBUpdateData::ITEM_EXCHANGE_UPDATE:
dwSize = *nBufferSize_InOut; m_Exchange.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
case DBUpdateData::ITEM_TEMPINVEN_UPDATE:
dwSize = *nBufferSize_InOut; m_TempInven.SerializeOut(pBuffer, dwSize);
lpUpdateLen[nCount] = static_cast<unsigned short>(dwSize);
break;
}
pBuffer += lpUpdateLen[nCount];
usTotalSize += lpUpdateLen[nCount];
*nBufferSize_InOut -= dwSize;
}
*nBufferSize_InOut = usTotalSize;
return true;
}
bool CCharacter::SetCharacterInfo(char* pBuffer, unsigned short usUpdateLen[DBUpdateData::MAX_UPDATE_DB])
{
for (int nCount = 0; nCount < DBUpdateData::MAX_UPDATE_DB; ++nCount)
{
unsigned long dwUpdateLen = usUpdateLen[nCount];
switch (nCount)
{
case DBUpdateData::STATUS_UPDATE: m_DBData.m_Info = *reinterpret_cast<CHAR_INFOST*>(pBuffer); break;
case DBUpdateData::POSITION_UPDATE: m_DBData.m_Pos = *reinterpret_cast<CHAR_POS*>(pBuffer); break;
case DBUpdateData::SKILL_UPDATE: m_DBData.m_Skill = *reinterpret_cast<SKILL*>(pBuffer); break;
case DBUpdateData::QUICKSLOT_UPDATE: m_DBData.m_Quick = *reinterpret_cast<QUICK*>(pBuffer); break;
case DBUpdateData::SPELL_UPDATE: m_DBData.m_Spell = *reinterpret_cast<SPELL*>(pBuffer); break;
case DBUpdateData::ITEM_EQUIP_UPDATE: m_Equipments.SerializeIn(pBuffer, dwUpdateLen); break;
case DBUpdateData::ITEM_INVEN_UPDATE: m_Inventory.SerializeIn(pBuffer, dwUpdateLen); break;
case DBUpdateData::ITEM_EXTRA_UPDATE: m_ExtraSpace.SerializeIn(pBuffer, dwUpdateLen); break;
case DBUpdateData::ITEM_EXCHANGE_UPDATE: m_Exchange.SerializeIn(pBuffer, dwUpdateLen); break;
case DBUpdateData::ITEM_TEMPINVEN_UPDATE: m_TempInven.SerializeIn(pBuffer, dwUpdateLen); break;
}
pBuffer += dwUpdateLen;
}
m_CreatureStatus.Init(m_DBData.m_Info);
m_CurrentPos.m_fPointX = m_DBData.m_Pos.LastPoint.fPointX;
m_CurrentPos.m_fPointY = m_DBData.m_Pos.LastPoint.fPointY;
m_CurrentPos.m_fPointZ = m_DBData.m_Pos.LastPoint.fPointZ;
return CalculateStatusData(false);
}
bool CCharacter::MoveZoneProcess(unsigned long dwServerID)
{
// 파티에 존이동했음을 보냄
if (0 != m_pParty)
{
CCharacterParty* lpParty = static_cast<CCharacterParty*>(m_pParty);
lpParty->SendPartyMemberDataToDBAgent(m_dwCID, 0, 0, dwServerID, 0,
GetCharacterName(), PktDD::SCmdMoveZonePartyMem);
}
// 친구 리스트에 존이동 메세지를 보낸다 //
FriendInfoUpdate(GetUID(), GetCID(), GetGID(), GetClass(), GetLevel(), dwServerID);
m_bLogout = true;
SetOperationFlag(CHAR_ZONE_MOVED);
return Logout(DBUpdateData::ZONEMOVE);
}
void CCharacter::FriendInfoUpdate(unsigned long dwUID, unsigned long dwCID, unsigned long dwGID, unsigned short wClass,
char cLevel, unsigned long dwServerID)
{
// 친구 리스트에 업데이트 메세지를 보낸다 //
GET_SINGLE_DISPATCH(lpChatDispatch, CChatDispatch, CChatDispatch::GetDispatchTable());
if(lpChatDispatch)
{
char* lpBuffer = lpChatDispatch->GetSendStream().GetBuffer(sizeof(PktFriendDB));
if(lpBuffer)
{
PktFriendDB* lpPktFriendDB = reinterpret_cast<PktFriendDB*>(lpBuffer);
lpPktFriendDB->m_dwOwnerUID = dwUID;
lpPktFriendDB->m_dwOwnerCID = dwCID;
lpPktFriendDB->m_dwReferenceUID = 0;
lpPktFriendDB->m_dwReferenceCID = 0;
lpPktFriendDB->m_dwData = 0;
lpPktFriendDB->m_cCmd = PktFriendDB::FRIEND_INFO_UPDATE;
lpPktFriendDB->m_dwGID = dwGID;
lpPktFriendDB->m_wClass = wClass;
lpPktFriendDB->m_cLevel = cLevel;
lpPktFriendDB->m_dwServerID = dwServerID;
lpChatDispatch->GetSendStream().WrapCrypt(sizeof(PktFriendDB), CmdFriendDB, 0, 0);
}
}
}
bool CCharacter::ItemDump(char* pBuffer, int* nBufferSize_InOut) const
{
using namespace GAMELOG;
sItemDump* lpItemDump = reinterpret_cast<sItemDump*>(pBuffer);
char* lpItems = reinterpret_cast<char*>(&lpItemDump[1]);
std::fill_n(lpItemDump->m_usDataSize, int(sItemDump::MAX_DUMP), 0);
unsigned long dwSize = 0;
unsigned short usTotalSize = sizeof(sItemDump);
for (int nCount = 0; nCount < sItemDump::MAX_DUMP; ++nCount)
{
switch (nCount)
{
case sItemDump::EQUIP_DUMP:
dwSize = *nBufferSize_InOut;
m_Equipments.SerializeOut(lpItems, dwSize);
lpItemDump->m_usDataSize[nCount] = static_cast<unsigned short>(dwSize);
break;
case sItemDump::INVEN_DUMP:
dwSize = *nBufferSize_InOut;
m_Inventory.SerializeOut(lpItems, dwSize);
lpItemDump->m_usDataSize[nCount] = static_cast<unsigned short>(dwSize);
break;
case sItemDump::EXTRA_DUMP:
dwSize = *nBufferSize_InOut;
m_ExtraSpace.SerializeOut(lpItems, dwSize);
lpItemDump->m_usDataSize[nCount] = static_cast<unsigned short>(dwSize);
break;
case sItemDump::EXCHANGE_DUMP:
dwSize = *nBufferSize_InOut;
m_Exchange.SerializeOut(lpItems, dwSize);
lpItemDump->m_usDataSize[nCount] = static_cast<unsigned short>(dwSize);
break;
}
lpItems += lpItemDump->m_usDataSize[nCount];
usTotalSize += lpItemDump->m_usDataSize[nCount];
*nBufferSize_InOut -= dwSize;
}
*nBufferSize_InOut = usTotalSize;
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
#include "stdafx.h"
#include <Item/Container/ItemContainer.h>
#include <Item/Container/EquipmentsContainer.h>
#include <Item/Container/ExchangeContainer.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Packet/PacketStruct/CharCommunityPacket.h>
#include "Character.h"
Item::CItem* CCharacter::GetItem(Item::ItemPos SrcPos)
{
switch (SrcPos.m_cPos)
{
case TakeType::TS_EQUIP: return m_Equipments.GetItem(SrcPos);
case TakeType::TS_INVEN: return m_Inventory.GetItem(SrcPos);
case TakeType::TS_TEMP: SrcPos.m_cIndex = Item::ExtraSpacePos::HOLDITEM_POS;
case TakeType::TS_EXTRA: return m_ExtraSpace.GetItem(SrcPos);
case TakeType::TS_EXCHANGE: return m_Exchange.GetItem(SrcPos);
case TakeType::TS_DEPOSIT: return m_Deposit.GetItem(SrcPos);
case TakeType::TS_STALL: return m_Stall.GetItem(SrcPos);
case TakeType::TS_TEMPINVEN: return m_TempInven.GetItem(SrcPos);
}
return NULL;
}
bool CCharacter::RemoveItem(Item::ItemPos SrcPos)
{
Item::CItem* lpItem = GetItem(SrcPos);
if (NULL == lpItem)
{
ERRLOG3(g_Log, "CID:0x%08x 삭제할 아이템이 존재하지 않습니다. SrcPos:(%d/%d)",
m_dwCID, SrcPos.m_cPos, SrcPos.m_cIndex);
return false;
}
// 노점상에서의 삭제가 아닌데 노점상 정보가 남아 있을 시... (잘못하면 서버가 죽을 수 있습니다.)
if (TakeType::TS_STALL != SrcPos.m_cPos && TakeType::TS_STALL == lpItem->GetRealPos().m_cPos)
{
// RemoveItem() 으로 RealPos 가 바뀌므로 미리 저장해두고, 그 값으로 SendRemoveItem 에 보내준다. by Vincent
Item::ItemPos realPos = lpItem->GetRealPos();
m_Stall.RemoveItem(lpItem->GetRealPos());
unsigned char cNum = (lpItem->IsSet(Item::DetailData::STACKABLE)) ? lpItem->GetNumOrDurability() : 1;
m_Stall.SendRemoveItem(TakeType::TakeType(realPos, realPos, cNum), PktStRI::SC_CANCEL, "");
}
switch (SrcPos.m_cPos)
{
case TakeType::TS_EQUIP: return m_Equipments.RemoveItem(SrcPos);
case TakeType::TS_INVEN: return m_Inventory.RemoveItem(SrcPos);
case TakeType::TS_TEMP: SrcPos.m_cIndex = Item::ExtraSpacePos::HOLDITEM_POS;
case TakeType::TS_EXTRA: return m_ExtraSpace.RemoveItem(SrcPos);
case TakeType::TS_EXCHANGE: return m_Exchange.RemoveItem(SrcPos);
case TakeType::TS_DEPOSIT: return m_Deposit.RemoveItem(SrcPos);
case TakeType::TS_STALL:
{
bool bResult = m_Stall.RemoveItem(SrcPos);
if (true == bResult)
{
unsigned char cNum = (lpItem->IsSet(Item::DetailData::STACKABLE)) ? lpItem->GetNumOrDurability() : 1;
m_Stall.SendRemoveItem(TakeType::TakeType(SrcPos, SrcPos, cNum), PktStRI::SC_CANCEL, "");
}
return bResult;
}
case TakeType::TS_TEMPINVEN: return m_TempInven.RemoveItem(SrcPos);
}
return true;
}
bool CCharacter::SetItem(Item::ItemPos SrcPos, Item::CItem* lpItem)
{
switch (SrcPos.m_cPos)
{
case TakeType::TS_EQUIP: return m_Equipments.SetItem(SrcPos, lpItem);
case TakeType::TS_INVEN: return m_Inventory.SetItem(SrcPos, lpItem);
case TakeType::TS_TEMP: SrcPos.m_cIndex = Item::ExtraSpacePos::HOLDITEM_POS;
case TakeType::TS_EXTRA: return m_ExtraSpace.SetItem(SrcPos, lpItem);
case TakeType::TS_EXCHANGE: return m_Exchange.SetItem(SrcPos, lpItem);
case TakeType::TS_DEPOSIT: return m_Deposit.SetItem(SrcPos, lpItem);
case TakeType::TS_STALL: return m_Stall.SetItem(SrcPos, lpItem);
case TakeType::TS_TEMPINVEN: return m_TempInven.SetItem(SrcPos, lpItem);
}
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,911 @@
#include "stdafx.h"
#include <Log/LogCommands.h>
#include <Log/CharacterLog.h>
#include <Utility/Math/Math.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Packet/PacketStruct/CharAttackPacket.h>
#include <Network/Packet/PacketStruct/CharItemPacket.h>
#include <Network/Packet/PacketStruct/CharStatusPacket.h>
#include <Network/Packet/PacketStruct/ServerLogPacket.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentPacketParse.h>
#include <Network/Dispatch/DBAgent/RegularAgentDispatch.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharLoginOut.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/GameClient/SendCharCommunity.h>
#include <Network/Dispatch/GameClient/SendCharQuest.h>
#include <Network/Dispatch/GameClient/SendCharEtc.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/GameLog/SendLogPacket.h>
#include <Network/Dispatch/DBAgent/CastlePacketParse.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
#include <Map/FieldMap/Cell.h>
#include <Map/FieldMap/VirtualArea/VirtualArea.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Map/FieldMap/CellManager.h>
#include <Item/ItemFactory.h>
#include <Item/Container/ExchangeContainer.h>
#include <Skill/SkillTable.h>
#include <Skill/Spell/SpellUtil.h>
#include <Skill/Spell/SpellTable.h>
#include <Skill/Spell/GlobalSpellMgr.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/CharRespawnMgr.h>
#include <Creature/Monster/Monster.h>
#include <Creature/Character/ExpTable.h>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/Camp.h>
#include <Creature/Siege/CastleGate.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
#include <Community/Guild/GuildMgr.h>
#include <Community/Guild/Guild.h>
#include <Community/Party/PartyMgr.h>
#include <GameTime/GameTimeConstants.h>
#include <GameTime/GameTimeMgr.h>
#include "Character.h"
#include "CharacterCreate.h"
#include "SphereTree/CharSphereTree.h"
// forward decl.
void CheckDuplicatedItem(CCharacter& character);
void SendItemDuplicatedLog(CSendStream& SendStream, unsigned __int64 dwItemSerial,
Item::CItemOwnerInfo& itemOwnerInfo, unsigned long dwItemQty);
void CCharacter::PrepareLogin()
{
// HP/MP세팅
m_CreatureStatus.m_nNowHP = m_DBData.m_Info.HP;
m_CreatureStatus.m_nNowMP = m_DBData.m_Info.MP;
// 복사 아이템 검사
CheckDuplicatedItem(*this);
// edith 2009.02.07 석상전 위치 내의 지역에서 로그인하는거 방지
if (SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3)
{
// 좌표 검사
if (!CCellManager::GetInstance().CheckPositionInZone(m_CurrentPos))
{
if (CServerSetup::GetInstance().IsBattleGameServer())
{
m_CurrentPos = CharCreate::BGServerStartPos[GetRace()][Math::Random::ComplexRandom(CharCreate::MAX_LOBBY_RESPAWN_POS)];
m_CurrentPos.m_fPointX += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
m_CurrentPos.m_fPointZ += Math::Random::SimpleRandom(GetTickCount(), 20) - 10;
}
else
{
m_CurrentPos = CCharRespawnMgr::GetInstance().GetDefaultRespawnPos(GetNation());
}
m_DBData.m_Pos.LastPoint.fPointX = m_CurrentPos.m_fPointX;
m_DBData.m_Pos.LastPoint.fPointY = m_CurrentPos.m_fPointY;
m_DBData.m_Pos.LastPoint.fPointZ = m_CurrentPos.m_fPointZ;
}
}
// 브로드캐스팅 데이터 준비
m_SerializeCharacterData.PrepareData(*this);
m_SerializeCharacterData.ClearDeltaData();
SetOperationFlag(CHAR_INFO_LOADED);
}
bool CCharacter::Login()
{
if (NULL == m_lpGameClientDispatch)
{
return false;
}
CSendStream& SendStream = m_lpGameClientDispatch->GetSendStream();
/*
#ifndef NO_GAMEGUARD
// 로그인 하기전에 게임가드 처리
// edith 2009.08.11 게임가드 테스트
if (NULL != m_lpGameClientDispatch)// && 0 == GetAdminLevel())
{
// edith 2009.08.11 게임가드 2.5 업그레이드
// if (false == m_lpGameClientDispatch->IsAuth())
// {
// ERRLOG1(g_Log, "CID:0x%08x 게임 가드 인증 코드를 보내지않아 연결을 끊습니다.", GetCID());
// m_lpGameClientDispatch->Disconnect();
// return false;
// }
GG_AUTH_DATA* lpAuthData = NULL;
if (false == m_lpGameClientDispatch->GetAuthQuery(&lpAuthData))
{
ERRLOG1(g_Log, "CID:0x%08x 게임 가드 인증 코드(2) 체크에 실패하여 연결을 끊습니다.", GetCID());
m_lpGameClientDispatch->Disconnect();
return false;
}
GameClientSendPacket::SendCSAuth(SendStream,
GetCID(), m_lpGameClientDispatch->GetAuthCode(), lpAuthData, PktBase::NO_SERVER_ERR);
}
#endif
*/
// DELETE_ME : 더이상 클라이언트가 반전 모드로의 변경은 불가능하다. 개념만 남아있을 뿐. (2005-05-31 by 로딘)
/*
// 전쟁 모드 제한
if (SERVER_ID::ZONE12 == CServerSetup::GetInstance().GetServerZone() ||
SERVER_ID::CAPITAL == CServerSetup::GetInstance().GetServerZone())
{
m_PeaceMode.m_bPeace = true;
}
else
{
m_PeaceMode.m_bPeace = false;
}
SetPeaceMode(m_PeaceMode, false);
GameClientSendPacket::SendCharPeaceMode(SendStream, m_dwCID, 0, m_PeaceMode.m_bPeace, 0);
*/
// 배틀 그라운드 서버군에서 레벨은 40으로 고정
if (SERVER_ID::BATTLE_SERVER == CServerSetup::GetInstance().GetServerZone())
{
// 레벨은 40으로 고정
while (static_cast<unsigned long>(m_CreatureStatus.m_nLevel) < 40)
{
if (false == IncrementExp(static_cast<unsigned long>(
EXP::ExpTable[m_CreatureStatus.m_nLevel - 1])))
{
break;
}
}
}
// Cell 일 설정되지 않은 상태이므로 MoveTo 함수를 한번 호출해준다.
MoveTo(GetCurrentPos(), false);
// edith 2009.06.13 16번 존 / 17번 존에서 무적시간 조정
int InvincibleTime = 30;
if (SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3)
{
InvincibleTime = 15;
char cZone = CServerSetup::GetInstance().GetServerZone();
// edith 2009.06.13 석상전에서 로그인할 경우 스타팅 포인트에서 리스폰되어서 시작
// 위치를 이동시키려면 MovePos 즉 워프를 이용해서 해당좌표로 강제이동시킨다.
const Position targetPos = CCharRespawnMgr::GetInstance().GetTownRespawnPos(cZone, GetNation());
MovePos(targetPos, cZone, false);
// 죽은 상태면 리스폰 시킨다.
if (0 == m_CreatureStatus.m_nNowHP)
{
Respawn();
}
}
// 로그인시 30초간 무적
Skill::CAddSpell<CInvincibleSpell>(CSpell::Spell_Info(Skill::CProcessTable::ProcessInfo::m_NullProtoType, this,
Skill::SpellType::MAGICAL_SPELL, Skill::SpellID::Invincible, 1, InvincibleTime))(this);
// 월드 웨폰이 존재하면 월드 웨폰 인챈트 적용
CCamp* lpWorldWeapon = CSiegeObjectMgr::GetInstance().GetWorldWeapon();
if (lpWorldWeapon)
{
AddWorldWeaponEnchant(reinterpret_cast<CAggresiveCreature*>(lpWorldWeapon), lpWorldWeapon->GetNation());
}
// DB에서 스펠을 읽어와 다시 걸어준다.
const SPELL spell = GetSpell();
GetSpellMgr().SetSpell(spell);
// 길드 정보를 준다.
CGuild* lpGuild = 0;
if (0 != GetGID() && 0 != (lpGuild = CGuildMgr::GetInstance().GetGuild(GetGID())))
{
unsigned short wError = PktBase::NO_SERVER_ERR;
unsigned char cTitle = lpGuild->GetTitle(m_dwCID);
if (Guild::NONE == cTitle)
{
wError = PktBase::SERVER_ERROR;
}
GameClientSendPacket::SendCharMyGuildInfo(SendStream,
lpGuild->GetGold(), lpGuild->GetRight(), cTitle, wError);
}
// 성 정보 전송
GameClientSendPacket::SendCharCastleInfo(SendStream);
// 게임 시간 정보 전송
GameClientSendPacket::SendCharGameTimeInfo(SendStream);
// 길드 요새 정보 전송
GameClientSendPacket::SendCharCampInfo(SendStream);
// CASTLE_TODO : 성이 길드 소유가 아니므로 일단 막아둔다.
// 리스폰 속도 향상 적용
// if (0 != GetGID())
// {
// Castle::CCastle* lpCastle = Castle::CCastleMgr::GetInstance().GetCastleByGID(GetGID());
// if (lpCastle)
// {
// CSiegeObject* lpEmblem = lpCastle->GetCastleEmblem();
// if (lpEmblem && lpEmblem->GetUpgradeStep() > 0)
// {
// UpgradeRespawnSpeedByEmblem(lpEmblem->GetUpgradeType(), lpEmblem->GetUpgradeStep());
// }
// }
// }
// 엘리트 보너스 정보 전송
GameClientSendPacket::SendCharEliteBonus(SendStream, GetEliteBonus());
// 거부 옵션 정보 전송
GameClientSendPacket::SendCharControlOption(SendStream, m_dwCID, m_RejectOption);
// 어빌리티 포인트 업데이트
UpdateUseAbilityPoint();
// edith 2008.06.03 공헌훈장 포인트효과
// 컨텐츠 : 다크 카나번 국가 전쟁
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
// 국가전쟁 공헌훈장 포인트 효과.
if (CGameTimeMgr::GetInstance().IsRealmWarTime() &&
(SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3))
{
RealmSkill::RealmInchantAdd(this);
}
}
// Admin 캐릭터에는 예외처리 (QA 쪽 요청).
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
if(GetAdminLevel())
{
goto lb_move;
}
}
/*
if (true == CServerSetup::GetInstance().UseContents(GameRYL::NEWZONE_ZONE9))
{
if(GetAdminLevel())
{
goto lb_move;
}
}
*/
// 길드전, 국가전 참여 캐릭터는 해당 시간에 로그인시 존 이동 처리를 해준다.
if (CGameTimeMgr::GetInstance().IsGuildWarTime() &&
(GetGuildWarFlag() == Creature::WAR_ON || GetGuildWarFlag() == Creature::WAR_INSTANCE))
{
MoveToGuildWarZone();
}
// 공성전 중 로그인할 경우 공성측은 리스폰 지역으로 이동시킨다.
// edith 2008.07.17 캐피탈존에 로그인했는데 공성시간이면 리스폰지역으로 이동함.
if(CServerSetup::GetInstance().GetServerZone() == SERVER_ID::CAPITAL)
{
if(CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
using namespace DBAgentPacketParse;
SiegeMovePos(this);
}
}
// 컨텐츠 : 다크 카나번 국가 전쟁
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
if ((CGameTimeMgr::GetInstance().IsRealmWarReadyTime() || CGameTimeMgr::GetInstance().IsRealmWarTime()) &&
(GetRealmWarFlag() == Creature::WAR_ON || GetRealmWarFlag() == Creature::WAR_INSTANCE) &&
(SERVER_ID::STONE_WAR1 < CServerSetup::GetInstance().GetServerZone() || CServerSetup::GetInstance().GetServerZone() > SERVER_ID::STONE_WAR3) )
{
// 랠름전 시간인데 전투참가가 되어있고.하지만 16번이 아니니 강제로 16번으로 이동.
MoveToRealmWarZone();
}
if (CGameTimeMgr::GetInstance().IsRealmWarTime() &&
GetRealmWarFlag() == Creature::WAR_OFF &&
(SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3))
{
// edith 2008.07.08 쟁에 참여를 안했는데 16번 존이고.. 랠름전 시간이면 강제 참여
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 != lpDBAgentDispatch)
{
GameClientSendPacket::SendWarOnOff(lpDBAgentDispatch->GetSendStream(), GetCID(), GameTime::REALM, Creature::WAR_INSTANCE, 0);
}
// 전쟁시간에 로그인했는데 얼래 내가 석상전 맵에서 로그인을 했어.. 이르면 리스폰위치로 강제이동해..
// edith 2008.07.08 전쟁에 참가안한 사람이지만 16번존에 존재하면 리스폰위치로 강제 이동시킨다.
// MoveToRealmWarZone();
}
lb_move:
// edith 2008.06.03 석상 인첸트효과
// 다크 카나번에 로그인시
if (SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3)
{
// 석상 정보를 보내준다.
CCreatureManager::GetInstance().SendRealmStatueDisplayInfo(SendStream);
bool bAddRealmStatueEnchant = false;
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
// 국가 전쟁 시간이 아니라면, 석상 인챈트 효과를 걸어준다.
if (!CGameTimeMgr::GetInstance().IsRealmWarTime())
{
bAddRealmStatueEnchant = true;
}
}
/*
// 공선전용 맵과 석상전용 맵이 따로기 때문에 해당 로직 불필요
if (true == CServerSetup::GetInstance().UseContents(GameRYL::SIEGE))
{
// 공성전 컨텐츠 포함시 공성전 시간도 아니라면, 석상 인챈트 효과를 걸어준다.
if (CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
bAddRealmStatueEnchant = false;
}
}
*/
/*
// 석상 인챈트 효과를 적용할 수 있는 상황이라면....
if (bAddRealmStatueEnchant)
{
// edith 2008.06.03 석상전이 끝날때 11시간 버프를 걸어준다.
CCreatureManager::GetInstance().AddRealmStatueEnchant(this);
}
*/
}
// 신규존 로그인시
else if (CServerSetup::GetInstance().GetServerZone() == SERVER_ID::ZONE5)
{
// 생명축출기 정보를 보내준다.
CCreatureManager::GetInstance().SendRealmStatueDisplayInfo(SendStream);
}
}
LOG_INOUT(
const int MAX_LOG_BUFFER = 1024;
char szBuffer[MAX_LOG_BUFFER];
unsigned long dwDispatchUID = m_lpGameClientDispatch->GetUID();
const SOCKADDR_IN& sockAddr = m_lpGameClientDispatch->GetRemoteAddr().get_addr_in();
_snprintf(szBuffer, MAX_LOG_BUFFER - 1,
"UID:%d/CID:0x%08x(0x%p)/DispatchUID:%d/DispatchPointer:0x%p 로그인에 성공하였습니다. "
"몇가지 기본 정보를 찍습니다. %s(lev:%2d, exp:%016I64u) IP:%15s:%5u",
m_dwUID, m_dwCID, this, dwDispatchUID, m_lpGameClientDispatch,
m_DBData.m_Info.Name, m_DBData.m_Info.Level, m_DBData.m_Info.Exp,
inet_ntoa(sockAddr.sin_addr), ntohs(sockAddr.sin_port));
szBuffer[MAX_LOG_BUFFER - 1] = 0;
DETLOG0(g_Log, szBuffer);
);
return true;
}
bool CCharacter::Logout(DBUpdateData::UpdateType eUpdateType)
{
// 배틀그라운드의 경우 (죽은 상태라면) 리스폰 큐에서 제거
if (SERVER_ID::ZONE3 == CServerSetup::GetInstance().GetServerZone())
{
CCreatureManager::GetInstance().PopRespawnQueue(this);
}
// 죽었는데 리스폰 안하고 로그아웃하면
// 내구도가 안깍이는 버그가 있어서 로그아웃할때 부활한 상태가 아니면 내구도를 깍아버린다.
if(IsDead())
{
switch(m_eLastDeadType)
{
case DEAD_BY_NONE: // 올 수 없음
case DEAD_BY_CHARACTER: // 캐릭터에 의해 사망
break;
case DEAD_BY_SUICIDE: // 자살
// 자살시 내구도 감소
CalculateAllEquipDurability(DURABILITY_DECREASE_PERSENT_BY_SUICIDE);
break;
case DEAD_BY_MONSTER: // 몬스터에 의해 사망
if (!CServerSetup::GetInstance().GetDeathPenaltyEvent())
{
unsigned char cDecreasePersent = 0;
if(m_AbilityValue[Skill::Type::AB_ENDUR_SHILD] != 0)
cDecreasePersent = DURABILITY_DECREASE_PERSENT_BY_MONSTER*m_AbilityValue[Skill::Type::AB_ENDUR_SHILD]/100;
// 몬스터에게 죽었을 때 내구도 감소
CalculateAllEquipDurability(DURABILITY_DECREASE_PERSENT_BY_MONSTER-cDecreasePersent);
}
break;
}
}
// 월드 웨폰 인챈트를 제거한다.
ClearWorldWeaponEnchant();
// 성문을 막고 있던 중이었다면... 성문 막기를 취소한다.
if (0 != m_dwProtectGateCID)
{
CCastleGate* lpGate = reinterpret_cast<CCastleGate*>( CSiegeObjectMgr::GetInstance().GetSiegeObject(m_dwProtectGateCID) );
if (lpGate)
{
lpGate->DeleteProtectGate(this);
// 클라이언트에게 전송
CGameClientDispatch* lpDispatch = GetDispatcher();
if (NULL != lpDispatch)
{
GameClientSendPacket::SendCharCastleCmd(lpDispatch->GetSendStream(), lpGate->GetCastleID(),
lpGate->GetCID(), 0, 0,
PktCastleCmd::CASTLE_GATE_PROTECT_CANCEL,
PktCastleCmd::NO_SERVER_ERR);
}
}
}
// 셀 로그아웃
if (NULL != m_CellPos.m_lpCell)
{
m_CellPos.m_lpCell->DeleteCreature(m_dwCID);
m_CellPos.m_lpCell = NULL;
}
// 채팅 서버로 로그아웃을 던진다.
m_SerializeCharacterData.SendChatLogout(*this);
// BG_TODO : 듀얼이나, 던젼의 경우에는.. 다시 처리해야 할지도 모른다.
// VirtualArea 에 있었다면, VirtualArea 의 캐릭터리스트에서 제거
if (0 != GetMapIndex())
{
VirtualArea::CVirtualAreaMgr::GetInstance().LeaveVirtualArea(this);
}
// 소환수가 있다면 없앤다.
if (NULL != m_lpSummonee)
{
m_lpSummonee->Dead(NULL);
}
// 거래중이라면 거래 취소
CCharacter* lpExchangeCharacter = m_Exchange.GetExchangeCharacter();
if (NULL != lpExchangeCharacter)
{
m_Exchange.ExchangeOK(false);
CGameClientDispatch* lpExchangerDispatch = lpExchangeCharacter->GetDispatcher();
if (NULL != lpExchangerDispatch)
{
GameClientSendPacket::SendCharExchangeCmd(lpExchangerDispatch->GetSendStream(),
m_dwCID, lpExchangeCharacter->GetCID(), PktExC::EXC_QUIT, PktExC::NO_SERVER_ERR);
}
}
// 듀얼 초기화
DuelInit(PktDuC::DUC_LOGOUT);
// 파티찾기 리스트에서 삭제
CPartyMgr::GetInstance().DeleteFindPartyList(m_dwCID);
// 파티 주문 제거.
if (0 != GetPID())
{
// 파티 로그아웃
if (NULL != m_pParty)
{
m_pParty->PrepareLogout(m_dwCID);
}
else
{
ERRLOG3(g_Log, "CID:0x%08x 파티에 속한 녀석의 파티 포인터가 NULL입니다. PID:0x%08x, PartyPointer:0x%08x",
m_dwCID, m_DBData.m_Info.PID, m_pParty);
}
}
// 캐릭터에 현제 걸려있는 스펠들을 DB에 저장한다.
// 죽은게 아니면 스펠을 다시 읽지 않는다.
// 죽은직후 스펠을 저장한후 스펠을 삭제하기 때문에 여기서 다시 스펠을 저장하면
// 스펠이 사라진다.
if(!IsDead())
{
const SPELL spell = GetSpellMgr().GetAffectedInfo().GetSpellInfo();
SetSpell(spell);
}
// 환전소 기능을 사용해서 임시 객체가 남아있다면 삭제
CRegularAgentDispatch::GetTempCharacterMgr().EraseChar(m_dwCID);
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 != lpDBAgentDispatch)
{
CSendStream& AgentSendStream = lpDBAgentDispatch->GetSendStream();
// 퀘스트 정보 저장
GameClientSendPacket::SendCharQuestInfo(AgentSendStream, this);
// 환경 설정 정보 저장
GameClientSendPacket::SendConfigInfoDB(AgentSendStream, this);
}
// 병기에 탑승중이라면 내린것으로 간주
if (IsRideArms())
{
CSiegeObject* lpArms = CSiegeObjectMgr::GetInstance().GetSiegeObject(m_dwRideArmsCID);
if (lpArms)
{
// 수송선 조종사가 나간경우
if (Siege::RIDER_FOR_OWNER == lpArms->IsRider(m_dwCID))
{
lpArms->AllGetOff();
}
else
{
m_dwRideArmsCID = 0;
lpArms->GetOff(m_dwCID);
}
}
}
int nTotalSize = sizeof(PktDBUpdate) + DBUpdateData::MAX_DBUPDATE_SIZE;
char szCharBuffer[sizeof(PktDBUpdate) + DBUpdateData::MAX_DBUPDATE_SIZE];
char* lpCharacterInfo = szCharBuffer + sizeof(PktDBUpdate);
PktDBUpdate* lpPktDBUpdate = reinterpret_cast<PktDBUpdate*>(szCharBuffer);
memset(lpPktDBUpdate, 0, sizeof(PktDBUpdate));
unsigned short usError = 0;
unsigned char cAdmin = (true == IsAdmin()) ? 1 : 0;
bool bCharacterUpdate = GetCharacterInfo(lpCharacterInfo, &nTotalSize, lpPktDBUpdate->m_usUpdate);
if (!bCharacterUpdate)
{
nTotalSize = 0;
usError = 1;
std::fill_n(lpPktDBUpdate->m_usUpdate, unsigned short(DBUpdateData::MAX_UPDATE_DB), 0);
ERRLOG1(g_Log, "CID:0x%08x 데이터를 복사해 올 수 없습니다. DB에 업데이트 할 수 없습니다.", m_dwCID);
}
else if (0 != lpDBAgentDispatch)
{
if (IsOperationFlagSet(CHAR_INFO_LOADED))
{
CSendStream& AgentSendStream = lpDBAgentDispatch->GetSendStream();
// 창고 데이터 업데이트(창고가 열려 있으면 업데이트한다.)
// 순서 주의! DB에 Update할 때는 창고 업데이트 후, 캐릭터를 업데이트한다.
if (!m_Deposit.DBUpdate(AgentSendStream))
{
ERRLOG1(g_Log, "CID:0x%08x 창고 업데이트 실패", m_dwCID);
}
// 로그아웃 정보 DBAgent에 보내기
lpPktDBUpdate->m_dlItemSerial = Item::CItemFactory::GetInstance().GetItemUID();
lpPktDBUpdate->m_dwSessionID = m_dwSessionID;
lpPktDBUpdate->m_dwUserID = m_dwUID;
lpPktDBUpdate->m_dwCharID = m_dwCID;
lpPktDBUpdate->m_TypeCode = eUpdateType;
lpPktDBUpdate->m_dwRequestKey = 0;
lpPktDBUpdate->m_cAdminFlag = GetGMModelFlag();
lpPktDBUpdate->m_dwRequestKey = 0;
lpPktDBUpdate->m_Address.S_un.S_addr = 0;
lpPktDBUpdate->m_cAdminLevel = 0;
// WORK_LIST 2.4 계정 국적을 게임서버의 캐릭터가 가지도록 구현
//lpPktDBUpdate->m_cPadding = 0;
lpPktDBUpdate->m_cAccountNation = 0;
lpPktDBUpdate->m_cNameChangeCount = 0;
lpPktDBUpdate->m_cGuildWarFlag = 0;
lpPktDBUpdate->m_cRealmWarFlag = 0;
lpPktDBUpdate->m_cRealmPoint = 0;
lpPktDBUpdate->m_cTacticsFlag = 0;
lpPktDBUpdate->m_PlayTime = 0;
lpPktDBUpdate->m_PremiumTime = 0;
lpPktDBUpdate->m_PremiumType = 0;
if (AgentSendStream.WrapCompress(reinterpret_cast<char*>(lpPktDBUpdate),
static_cast<unsigned short>(sizeof(PktDBUpdate) + nTotalSize), CmdDBUpdateData, 0, 0))
{
LOG_INOUT(
char szExp[64];
Math::Convert::Hex64ToStr(szExp, m_DBData.m_Info.Exp);
DETLOG7(g_Log, "UID:%d/CID:0x%08x(0x%p)/DispatchPointer:0x%p 의 캐릭터 정보를 DBAgent에 업데이트합니다. 유저 로그아웃을 처리합니다."
" 로그아웃에 성공하였습니다. 몇가지 기본 정보를 찍습니다. %s(lev:%2d, exp:%s)",
m_dwUID, m_dwCID, this, m_lpGameClientDispatch, m_DBData.m_Info.Name, m_DBData.m_Info.Level, szExp)
);
}
else
{
ERRLOG1(g_Log, "CID:0x%08x DB에 캐릭터 업데이트를 할 수 없습니다. 전송에 실패했습니다.", m_dwCID);
}
}
else
{
// 데이터가 세팅되지 않았으니, 정상 로그아웃은 아니다.
DBAgentPacketParse::SendAbnormalLogout(
m_dwUID, m_dwCID, m_dwSessionID, 0, m_lpGameClientDispatch);
}
}
else
{
ERRLOG2(g_Log, "CID:0x%08x/AgentSession:0x%p/ DBUpdate failed.",
m_dwCID, lpDBAgentDispatch);
}
SOCKADDR_IN remoteAddr;
if (0 != m_lpGameClientDispatch)
{
remoteAddr = m_lpGameClientDispatch->GetRemoteAddr().get_addr_in();
}
else
{
memset(&remoteAddr, 0, sizeof(SOCKADDR_IN));
}
// 게임로그에 캐릭터 로그아웃 남기기
GAMELOG::LogCharLoginOut(m_dwUID, this, &remoteAddr, lpCharacterInfo, nTotalSize,
lpPktDBUpdate->m_usUpdate, GAMELOG::CMD::CHAR_LOGOUT, usError);
// 로그 서버에 로그아웃 보내기.
// SendLogPacket::CharLogout(*this);
LOG_INOUT(DETLOG4(g_Log, "UID:%d/CID:0x%08x(0x%p)/DispatchPointer:0x%p 다음 유저의 로그아웃을 처리합니다. 다음 유저를 제거합니다.",
m_dwUID, m_dwCID, this, m_lpGameClientDispatch));
return CCreatureManager::GetInstance().DeleteCreature(m_dwCID);
}
void CheckDuplicatedItem(CCharacter& character)
{
using namespace Item;
CItemOwnerInfo itemOwnerInfo(
character.GetCharacterName(), character.GetUID(), character.GetCID());
CItemOwnerInfo* lpDuplicateOwner = 0;
CItemFactory& itemFactory = Item::CItemFactory::GetInstance();
// first : 소유자 정보 / second : 복사 아이템 Qty
typedef std::pair<CItemOwnerInfo*, unsigned long> DuplicateInfo;
typedef std::multimap<unsigned __int64, DuplicateInfo, std::less<unsigned __int64>,
boost::fast_pool_allocator<std::pair<unsigned __int64, DuplicateInfo> > > DuplicatedMap;
DuplicatedMap duplicatedItemMap;
// 복사 데이터가 있으면 DB중계로 송신하기 위해서..
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch,
CDBAgentDispatch::GetDispatchTable());
// 아이템 컨테이너들을 배열에 넣는다.
const int MAX_CONTAINER_SET = 7;
CItemContainer* containerSet[MAX_CONTAINER_SET] =
{
&character.GetInventory(),
&character.GetEquipments(),
&character.GetExtra(),
&character.GetExchange(),
&character.GetDeposit(),
&character.GetStall(),
&character.GetTempInven(),
};
CItemContainer** lppContainerPos = containerSet;
CItemContainer** lppContainerEnd = containerSet + MAX_CONTAINER_SET;
for(; lppContainerPos != lppContainerEnd; ++lppContainerPos)
{
CItemContainer& itemContainer = **lppContainerPos;
CItemContainer::iterator pos = itemContainer.begin();
CItemContainer::iterator end = itemContainer.end();
for(; pos != end; ++pos)
{
CItem* lpItem = *pos;
if (0 != lpItem)
{
unsigned __int64 dwItemSerial = lpItem->GetUID();
lpDuplicateOwner = itemFactory.AddItemMap(dwItemSerial, itemOwnerInfo);
if (0 != lpDuplicateOwner)
{
// 복사 발견. 일단 나와 이녀석의 시리얼 및 정보를 리스트 등으로 기억해두었다가
// 개수를 세어서 나중에 한번에 보낸다.
// 내 아이템 정보 : dwItemSerial, itemOwnerInfo
// 상대 아이템 정보 : dwItemSerial, lpDuplicateOwner
std::pair<DuplicatedMap::iterator, DuplicatedMap::iterator> result =
duplicatedItemMap.equal_range(dwItemSerial);
bool bAddedQty = false;
bool bAddedDuplicateOwner = false;
for(;result.first != result.second; ++result.first)
{
unsigned __int64 dwItemUID = result.first->first;
// first : 소유자 정보 / second : 복사 아이템 Qty
DuplicateInfo& duplicateInfo = result.first->second;
CItemOwnerInfo& duplicateOwnerInfo = *duplicateInfo.first;
if (duplicateOwnerInfo.GetCID() == itemOwnerInfo.GetCID())
{
// 소유자가 같은 아이템이다. Qty를 증가시켜준다.
// Qty는 복제된 아이템을 '내가' 몇개나 가지고 있는지를 나타낸다.
++duplicateInfo.second;
bAddedQty = true;
}
else if (duplicateOwnerInfo.GetCID() == lpDuplicateOwner->GetCID())
{
// 이전에 가지고 있던 사람이 내가 아니라 다른 사람이다.
bAddedDuplicateOwner = true;
}
}
if (!bAddedQty)
{
// 숫자가 추가되지 않았으면, 항목에 새로 추가.
duplicatedItemMap.insert(result.second, DuplicatedMap::value_type(dwItemSerial,
DuplicateInfo(&itemOwnerInfo, (itemOwnerInfo.GetCID() == lpDuplicateOwner->GetCID()) ? 2 : 1)));
}
if (!bAddedDuplicateOwner && lpDuplicateOwner->GetCID() != itemOwnerInfo.GetCID())
{
duplicatedItemMap.insert(result.second,
DuplicatedMap::value_type(dwItemSerial, DuplicateInfo(lpDuplicateOwner, 1)));
}
}
}
}
}
DuplicatedMap::iterator pos = duplicatedItemMap.begin();
DuplicatedMap::iterator end = duplicatedItemMap.end();
if (0 != lpDBAgentDispatch)
{
// 전송
for(; pos != end; ++pos)
{
SendItemDuplicatedLog(lpDBAgentDispatch->GetSendStream(),
pos->first, *pos->second.first, pos->second.second);
}
}
else
{
// 로그를 남긴다.
for(; pos != end; ++pos)
{
DuplicateInfo& dupInfo = pos->second;
ERRLOG5(g_Log, "UID:%10u / CID:%10u / CharName:%s / ItemSerial:0x%I64X / Qty:%u / 아이템 복사가 발견되었습니다",
dupInfo.first->GetUID(), dupInfo.first->GetCID(), dupInfo.first->GetName(), pos->first, dupInfo.second);
}
}
}
void SendItemDuplicatedLog(CSendStream& SendStream, unsigned __int64 dwItemSerial,
Item::CItemOwnerInfo& itemOwnerInfo, unsigned long dwItemQty)
{
PktItemDuplicated* lpPktItemDuplicated =
reinterpret_cast<PktItemDuplicated*>(SendStream.GetBuffer(sizeof(PktItemDuplicated)));
if (0 != lpPktItemDuplicated)
{
lpPktItemDuplicated->m_cLogCmd = PktServerLog::ITEM_DUPLICATED_LOG;
strncpy(lpPktItemDuplicated->m_szName, itemOwnerInfo.GetName(), PktItemDuplicated::MAX_NAME - 1);
lpPktItemDuplicated->m_szName[PktItemDuplicated::MAX_NAME - 1] = 0;
lpPktItemDuplicated->m_dwItemSerial = dwItemSerial;
lpPktItemDuplicated->m_dwUID = itemOwnerInfo.GetUID();
lpPktItemDuplicated->m_dwCID = itemOwnerInfo.GetCID();
lpPktItemDuplicated->m_dwQty = dwItemQty;
SendStream.WrapHeader(sizeof(PktItemDuplicated), CmdServerLog, 0, 0);
}
}
void RealmSkill::RealmInchantAdd(CCharacter* lpCharacter)
{
// edith 2010.01.02 공헌 훈장의 개념이 바뀌면서 공헌훈장 버프를 제외한다.
return;
// edith 2008.05.28 공헌 훈장별 버프 설정
// 공헌 훈장 포인트 세팅 //
unsigned char cLevel[2][6] =
{
0, 1, 1, 2, 2, 3,
0, 0, 1, 1, 2, 2
};
if(!lpCharacter)
{
return;
}
unsigned char cPoint = lpCharacter->GetRealmPoint();
if(!cPoint)
{
return;
}
// HP 효과.
if(cLevel[0][cPoint])
{
Skill::CAddSpell<CRealmHPSpell>(CSpell::Spell_Info(Skill::CProcessTable::ProcessInfo::m_NullProtoType, lpCharacter,
Skill::SpellType::REALM_SPELL, Skill::SpellID::RealmHP, cLevel[0][cPoint], CSpell::INFINITE_DURATION))(lpCharacter);
}
// MP 효과.
if(cLevel[1][cPoint])
{
Skill::CAddSpell<CRealmMPSpell>(CSpell::Spell_Info(Skill::CProcessTable::ProcessInfo::m_NullProtoType, lpCharacter,
Skill::SpellType::REALM_SPELL, Skill::SpellID::RealmMP, cLevel[1][cPoint], CSpell::INFINITE_DURATION))(lpCharacter);
}
}
void RealmSkill::RealmInchantRemove(CCharacter* lpCharacter)
{
if(!lpCharacter)
return;
// HP 효과.
if(lpCharacter->GetSpellMgr().GetAffectedInfo().GetSpell(Skill::SpellID::RealmHP))
{
lpCharacter->GetSpellMgr().GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::RealmHP);
}
// MP 효과.
if(lpCharacter->GetSpellMgr().GetAffectedInfo().GetSpell(Skill::SpellID::RealmMP))
{
lpCharacter->GetSpellMgr().GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::RealmMP);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,124 @@
#include "stdafx.h"
#include <Item/Item.h>
#include <Skill/SkillMgr.h>
#include "Character.h"
void CCharacter::UpdateQuickSlotSkill(SKILLSLOT Slot)
{
for (int nQSlot = 0; nQSlot < QUICK::MAX_QUICK_NUM; ++nQSlot)
{
QUICKSLOT& QuickSlot = m_DBData.m_Quick.Slots[nQSlot];
if (QUICKSLOT::SKILL == QuickSlot.nType
&& QuickSlot.wID == Slot.SKILLINFO.wSkill)
{
QuickSlot.nSkillLevel = Slot.SKILLINFO.cSkillLevel;
QuickSlot.nSkillLockCount = Slot.SKILLINFO.cLockCount;
}
}
}
bool CCharacter::MoveQuickSlot(const TakeType takeType, const unsigned short usSkillID, unsigned char cLockCount, unsigned char cSkillLevel)
{
bool bResult = 0;
if (QUICK::MAX_QUICK_NUM <= takeType.m_dstPos.m_cIndex)
{
ERRLOG3(g_Log, "CID:%10u 퀵슬롯 이동 이상. 퀵슬롯 인덱스 위치가 이상합니다."
"Dst(%2d,%2d)", m_dwCID, takeType.m_dstPos.m_cPos, takeType.m_dstPos.m_cIndex);
return false;
}
switch (takeType.m_srcPos.m_cPos)
{
case TakeType::TS_INVEN:
case TakeType::TS_TEMP:
{
Item::CItem* pItem = GetItem(takeType.m_srcPos);
if (NULL == pItem)
{
ERRLOG3(g_Log, "CID:%10u 퀵슬롯 이동 이상. 아이템을 얻어올 수 없습니다. Src(%2d,%2d)",
m_dwCID, takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex);
}
else if (pItem->IsSet(Item::DetailData::QUICKSLOT_IN))
{
m_DBData.m_Quick.Slots[takeType.m_dstPos.m_cIndex] =
QUICKSLOT(QUICKSLOT::ITEM, 0, 0, pItem->GetPrototypeID());
return true;
}
} break;
case TakeType::TS_SSLOT:
{
if (SKILL::MAX_SLOT_NUM <= takeType.m_srcPos.m_cIndex)
{
ERRLOG3(g_Log, "CID:%10u 옮기려는 스킬 위치가 이상합니다. Src(%2d,%2d)",
m_dwCID, takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex);
}
else if (TakeType::TS_QSLOT == takeType.m_dstPos.m_cPos)
{
const Skill::ProtoType* lpProtoType = CSkillMgr::GetInstance().GetSkillProtoType(usSkillID);
if (NULL == lpProtoType)
{
ERRLOG2(g_Log, "CID:%10u 옮기려는 스킬의 아이디가 존재하지 않습니다. ID:0x%04x", m_dwCID, usSkillID);
return false;
}
unsigned char cSkillLockCount = 0;
unsigned char cSkillCurLevel = 1;
// 액션 스크립트이면
if(lpProtoType->m_eSkillType == Skill::Type::ACTION)
{
cSkillLockCount = cLockCount;
cSkillCurLevel = cSkillLevel;
}
else
{
SKILLSLOT SkillSlot = m_DBData.m_Skill.SSlot[takeType.m_srcPos.m_cIndex];
if (false == lpProtoType->m_bIsClassSkill)
{
cSkillLockCount = SkillSlot.SKILLINFO.cLockCount;
cSkillCurLevel = SkillSlot.SKILLINFO.cSkillLevel;
}
}
m_DBData.m_Quick.Slots[takeType.m_dstPos.m_cIndex] =
QUICKSLOT(QUICKSLOT::SKILL, cSkillLockCount, cSkillCurLevel, usSkillID);
return true;
}
} break;
case TakeType::TS_QSLOT:
{
if (QUICK::MAX_QUICK_NUM <= takeType.m_srcPos.m_cIndex)
{
ERRLOG2(g_Log, "CID:%10u 옮기려는 퀵슬롯 위치가 이상합니다. 스킬 위치 %d",
m_dwCID, takeType.m_srcPos.m_cIndex);
}
else
{
if (TakeType::TS_QSLOT == takeType.m_dstPos.m_cPos)
{
std::swap(m_DBData.m_Quick.Slots[takeType.m_srcPos.m_cIndex],
m_DBData.m_Quick.Slots[takeType.m_dstPos.m_cIndex]);
}
else
{
m_DBData.m_Quick.Slots[takeType.m_srcPos.m_cIndex] =
QUICKSLOT(QUICKSLOT::NONE, 0, 0, 0);
}
return true;
}
} break;
}
return false;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
#include "stdafx.h"
#include "CharacterStructure.h"
#include "GMMemory.h"
CharacterFightInfo::CharacterFightInfo()
: m_pDuelOpponent(NULL)
{
}

View File

@@ -0,0 +1,50 @@
#ifndef _CHARACTER_STRUCTURE_H_
#define _CHARACTER_STRUCTURE_H_
#include <Creature/CreatureStructure.h>
// 전방 참조
class CCharacter;
// -----------------------------------------------------------------------
// DB 에서 처음에 읽어 오는 데이터들. 중간중간 저장해서 DB에 갱신함.
// 되도록이면 게임 처리중에는 건드리지 않는다. ( 레벨업 등등을 제외하고 ... )
struct CharacterDBData
{
CHAR_INFOST m_Info; // 외모, 이름, 능력치, hp,mp 등 기본 정보
CHAR_POS m_Pos; // 종료시 위치 정보, 저장된 위치 정보 ( Respawn 장소 )
SKILL m_Skill;
QUICK m_Quick;
SPELL m_Spell;
unsigned char m_cAdminLevel;
};
typedef CharacterDBData* LPCharacterDBData;
// 유저 접속 정보. 유저의 IP를 넣음.
struct ConnectInfo
{
SOCKADDR_IN m_siAgentHost; // agent UDP address of host
SOCKADDR_IN m_siPrivateHost; // private UDP address of host
SOCKADDR_IN m_siPublicHost; // public UDP address of host
};
typedef ConnectInfo* LPConnectInfo;
// 캐릭터끼리의 전투 정보 (듀얼, 전투)
struct CharacterFightInfo
{
CCharacter* m_pDuelOpponent;
POS m_Pos;
unsigned long m_dwCellID;
__int64 m_nRestoreExp; // 스킬 리저렉션을 통해 부활할 경우 일정량의 경험치가 복구될 수 있다.
CharacterFightInfo();
};
#endif

View File

@@ -0,0 +1,337 @@
#include "stdafx.h"
#include <Item/Item.h>
#include <Item/ItemFactory.h>
#include <Creature/NPC/NPC.h>
#include <Creature/CreatureManager.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Dispatch/GameClient/SendCharCommunity.h>
#include <Network/Packet/PacketStruct/CharCommunityPacket.h>
#include <Map/FieldMap/VirtualArea/VirtualArea.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Log/ItemLog.h>
#include "Character.h"
unsigned long CCharacter::RepairItem(const unsigned long dwNPCID, Item::ItemPos itemPos, unsigned short& wError)
{
CCreature* lpCreature = CCreatureManager::GetInstance().GetCreature(dwNPCID);
if (NULL == lpCreature)
{
ERRLOG2(g_Log, "CID:%10u 수리해줄 크리쳐가 존재하지 않습니다. 수리 실패. CID:%10u", m_dwCID, dwNPCID);
wError = PktRpI::FAIL_NOT_CREATURE;
return 0;
}
if (Creature::CT_NPC == Creature::GetCreatureType(dwNPCID))
{
int nZone = CServerSetup::GetInstance().GetServerZone();
if (GetMapIndex() != 0)
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (NULL != lpVirtualArea)
{
nZone = static_cast<int>(lpVirtualArea->GetVirtualZone());
}
}
CNPC* lpNPC = reinterpret_cast<CNPC*>(lpCreature);
if (lpNPC->GetZone() != nZone)
{
ERRLOG3(g_Log, "CID:%10u NPC의 존 번호가 다릅니다. Server Zone : %d, NPC Zone : %d", m_dwCID, nZone, lpNPC->GetZone());
wError = PktRpI::FAIL_NOT_NPCZONE;
return 0;
}
}
Item::CItem* lpItem = GetItem(itemPos);
if (NULL == lpItem)
{
ERRLOG3(g_Log, "CID:%10u 아이템이 %d Pos, %d Index에 없습니다", m_dwCID, itemPos.m_cPos, itemPos.m_cIndex);
wError = PktRpI::FAIL_NOT_POSITEM;
return 0;
}
if (true == lpItem->IsSet(Item::DetailData::STACKABLE))
{
ERRLOG1(g_Log, "CID:%10u 스택 아이템이어서 수리할 수 없습니다", m_dwCID);
wError = PktRpI::FAIL_NOT_STACKABLE;
return 0;
}
if (false == lpItem->IsSet(Item::DetailData::EQUIP))
{
ERRLOG1(g_Log, "CID:%10u 장비 아이템이 아니어서 수리할 수 없습니다", m_dwCID);
wError = PktRpI::FAIL_NOT_EQUIP;
return 0;
}
if (lpItem->GetNumOrDurability() >= lpItem->GetMaxNumOrDurability())
{
// ERRLOG4(g_Log, "CID:%10u 장비가 이미 최대 내구도에 도달해서, 수리할 수 없습니다."
// " 종류ID:%d, 현재 내구도:%d, 최대 내구도:%d", m_dwCID, lpItem->GetPrototypeID(),
// lpItem->GetNumOrDurability(), lpItem->GetMaxNumOrDurability());
wError = PktRpI::FAIL_FULL_DRUA;
return 0;
}
Item::CEquipment* lpEquipment = Item::CEquipment::DowncastToEquipment(lpItem);
unsigned long dwGold = lpCreature->RepairItem(lpEquipment, m_DBData.m_Info.Gold);
if (0 != dwGold)
{
CalculateStatusData(false);
}
else
{
wError = PktRpAI::FAIL_NOT_REPAIR;
}
return dwGold;
}
unsigned long CCharacter::RepairAllItem(const unsigned long dwNPCID, unsigned short& wError)
{
CCreature* lpCreature = CCreatureManager::GetInstance().GetCreature(dwNPCID);
if (NULL == lpCreature)
{
ERRLOG2(g_Log, "CID:%10u 수리해줄 크리쳐가 존재하지 않습니다. 수리 실패. CID:%10u", m_dwCID, dwNPCID);
wError = PktRpAI::FAIL_NOT_CREATURE;
return 0;
}
if (Creature::CT_NPC == Creature::GetCreatureType(dwNPCID))
{
int nZone = CServerSetup::GetInstance().GetServerZone();
if (GetMapIndex() != 0)
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (NULL != lpVirtualArea)
{
nZone = static_cast<int>(lpVirtualArea->GetVirtualZone());
}
}
CNPC* lpNPC = reinterpret_cast<CNPC*>(lpCreature);
if (lpNPC->GetZone() != nZone)
{
ERRLOG3(g_Log, "CID:%10u NPC의 존 번호가 다릅니다. Server Zone : %d, NPC Zone : %d", m_dwCID, nZone, lpNPC->GetZone());
wError = PktRpAI::FAIL_NOT_NPCZONE;
return 0;
}
}
unsigned long dwRepairGold = 0;
Item::ItemPos itemPos;
itemPos.m_cPos = TakeType::TS_EQUIP;
// 1단계 검사
for (unsigned char cIndex = 0; cIndex < Item::EquipmentPos::MAX_EQUPMENT_POS; ++cIndex)
{
if (GetRace() == CClass::AKHAN &&
(Item::EquipmentPos::SHIRT == cIndex ||
Item::EquipmentPos::TUNIC == cIndex ||
Item::EquipmentPos::SHIELD_HAND2 == cIndex ||
Item::EquipmentPos::WEAPON_HAND2 < cIndex))
{
continue;
}
itemPos.m_cIndex = cIndex;
Item::CItem* lpItem = GetItem(itemPos);
if (NULL == lpItem) { continue; }
if (true == lpItem->IsSet(Item::DetailData::STACKABLE))
{
// 화살/볼트의 경우 수리 불가
continue;
}
if (false == lpItem->IsSet(Item::DetailData::EQUIP))
{
ERRLOG3(g_Log, "CID:%10u 수리하는 아이템이 장비 아이템이 아닙니다. (%d Pos, %d Index)",
m_dwCID, itemPos.m_cPos, itemPos.m_cIndex);
continue;
}
if (lpItem->GetNumOrDurability() >= lpItem->GetMaxNumOrDurability())
{
// 풀내구의 장비는 스킵
continue;
}
// 골드를 계산한다.
Item::CEquipment* lpEquipment = Item::CEquipment::DowncastToEquipment(lpItem);
if(lpEquipment)
dwRepairGold += lpEquipment->GetRepairPrice();
}
// 돈이 실제보다 많이들면 에러를 리턴한다.
if (dwRepairGold > GetGold())
{
wError = PktRpAI::NOT_ENOUGH_GOLD;
return 0;
}
dwRepairGold = 0;
// 2단계 실제 수리
for (unsigned char cIndex = 0; cIndex < Item::EquipmentPos::MAX_EQUPMENT_POS; ++cIndex)
{
if (GetRace() == CClass::AKHAN &&
(Item::EquipmentPos::SHIRT == cIndex ||
Item::EquipmentPos::TUNIC == cIndex ||
Item::EquipmentPos::SHIELD_HAND2 == cIndex ||
Item::EquipmentPos::WEAPON_HAND2 < cIndex))
{
continue;
}
itemPos.m_cIndex = cIndex;
Item::CItem* lpItem = GetItem(itemPos);
if (NULL == lpItem) { continue; }
if (true == lpItem->IsSet(Item::DetailData::STACKABLE))
{
// 화살/볼트의 경우 수리 불가
continue;
}
if (false == lpItem->IsSet(Item::DetailData::EQUIP))
{
ERRLOG3(g_Log, "CID:%10u 수리하는 아이템이 장비 아이템이 아닙니다. (%d Pos, %d Index)",
m_dwCID, itemPos.m_cPos, itemPos.m_cIndex);
continue;
}
if (lpItem->GetNumOrDurability() >= lpItem->GetMaxNumOrDurability())
{
// 풀내구의 장비는 스킵
continue;
}
Item::CEquipment* lpEquipment = Item::CEquipment::DowncastToEquipment(lpItem);
if(lpEquipment)
dwRepairGold += lpCreature->RepairItem(lpEquipment, m_DBData.m_Info.Gold);
}
if (0 != dwRepairGold)
{
CalculateStatusData(false);
}
else
{
wError = PktRpAI::FAIL_NOT_REPAIR;
}
return dwRepairGold;
}
Item::CItem* CCharacter::SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError)
{
Item::CItem* lpItem = m_Stall.GetItem(takeType.m_srcPos);
if (NULL == lpItem) { return NULL; }
unsigned long dwCustomerCID = lpCustomer->GetCID();
unsigned long dwCurrentGold = lpCustomer->GetGold();
dwPrice = lpItem->GetBuyPrice() * takeType.m_cNum;
if (dwPrice > dwCurrentGold)
{
ERRLOG2(g_Log, "노점상 오류 : 돈이 부족합니다. 가격:%d, 소지금:%d", dwPrice, dwCurrentGold);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
// 가질수 있는 최대 소지금 초과
if (GetGold() > ULONG_MAX - dwPrice)
{
ERRLOG2(g_Log, "노점상 오류 : 거래후 Gold가 최대 소지금을 초과합니다. 가격:%d, 소지금:%d", dwPrice, GetGold());
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_GOLD_OVERFLOW);
return NULL;
}
if (false == lpCustomer->GetInventory().TestItem(takeType.m_dstPos, lpItem->GetPrototypeID(), takeType.m_cNum))
{
Item::CItemContainer* lpItemContainer = lpCustomer->GetItemContainer(takeType.m_dstPos.m_cPos);
if (NULL != lpItemContainer)
{
lpItemContainer->DumpItemInfo();
}
else
{
ERRLOG1(g_Log, "CID:%10u 아이템 덤프를 출력할 수 없습니다.", dwCustomerCID);
}
ERRLOG4(g_Log, "CID:%10u 아이템 종류:%d를 (%2d:%2d)에 아이템 넣기 실패.",
dwCustomerCID, lpItem->GetPrototypeID(), takeType.m_dstPos.m_cPos, takeType.m_dstPos.m_cIndex);
return NULL;
}
bool bStackable = lpItem->IsSet(Item::DetailData::STACKABLE);
unsigned char cNumOrDurability = lpItem->GetNumOrDurability();
Item::ItemPos ItemPos = lpItem->GetPos();
// 스택 가능한 아이템인 경우, 개수제한 확인.
if (!(bStackable && (cNumOrDurability < takeType.m_cNum)))
{
// 스택이 불가능하거나, 전부 파는 경우에는 아이템 제거.
if (!bStackable || (bStackable && (takeType.m_cNum == lpItem->GetNumOrDurability())))
{
if (false == m_Stall.RemoveItem(takeType.m_srcPos))
{
ERRLOG3(g_Log, "CID:%10u 아이템을 (%2d, %4x)위치로부터 지우는 데 실패했습니다.",
m_dwCID, takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
else
{
RemoveItem(ItemPos);
m_Stall.SendRemoveItem(takeType, PktStRI::SC_CANCEL, lpCustomer->GetCharacterName());
}
}
else
{
lpItem->SetNumOrDurability(cNumOrDurability - takeType.m_cNum);
m_Stall.SendRemoveItem(takeType, PktStRI::SC_CANCEL, lpCustomer->GetCharacterName());
unsigned short usProtoTypeID = lpItem->GetPrototypeID();
lpItem = Item::CItemFactory::GetInstance().CreateItem(usProtoTypeID);
if (NULL == lpItem)
{
ERRLOG1(g_Log, "노점상 오류 : 아이템 생성 실패. ProtoTypeID : %d", usProtoTypeID);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
lpItem->SetNumOrDurability(takeType.m_cNum);
}
AddGold(dwPrice, false);
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharTradeItem(m_lpGameClientDispatch->GetSendStream(), this, lpCustomer->GetCID(),
Item::ItemPos(), NULL, ItemPos, takeType.m_cNum, 0);
}
}
else
{
ERRLOG4(g_Log, "노점상 오류 : (%2d, %4x)의 아이템 개수 : %d개 팔려는 아이템 개수 : %d개",
takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex, cNumOrDurability, takeType.m_cNum);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::NO_SERVER_ERR);
return lpItem;
}

View File

@@ -0,0 +1,336 @@
#ifndef _EXP_TABLE_H_
#define _EXP_TABLE_H_
#include <Utility/Setup/ServerSetup.h>
namespace EXP
{
static unsigned char GetUsingMaxLevel(void)
{
if (true == CServerSetup::GetInstance().UseContents(GameRYL::LEVEL_LIMIT_100))
{
return 100;
}
else if (true == CServerSetup::GetInstance().UseContents(GameRYL::LEVEL_LIMIT_90))
{
return 90;
}
else
{
return 80;
}
}
const __int64 ExpTable[Creature::PC_MAX_LEVEL] = {
200 ,
300 ,
422 ,
571 ,
752 ,
974 ,
1244 ,
1574 ,
1976 ,
2467 ,
3066 ,
3796 ,
4687 ,
5775 ,
7101 ,
8719 ,
10693 ,
13102 ,
16040 ,
19625 ,
23999 ,
29335 ,
35844 ,
43786 ,
53475 ,
65296 ,
79717 ,
97310 ,
118774 ,
144961 ,
176908 ,
215884 ,
263434 ,
321446 ,
392220 ,
478564 ,
583905 ,
712420 ,
869208 ,
1060490 ,
1293854 ,
1578557 ,
1925896 ,
2349649 ,
2866628 ,
3497342 ,
4266813 ,
5205568 ,
6350849 ,
7748092 ,
9452728 ,
11532384 ,
14069565 ,
17164925 ,
20941264 ,
25548398 ,
31169102 ,
38026360 ,
46392216 ,
56598559 ,
69050298 ,
84241420 ,
102774588 ,
125385054 ,
152969821 ,
186623238 ,
227680407 ,
277770152 ,
338879641 ,
413433218 ,
504388583 ,
615354127 ,
750732091 ,
915893207 ,
1117389768 ,
1363215573 ,
1663123055 ,
2029010183 ,
2475392479 ,
3019978881 ,
3684374291 ,
4494936691 ,
5483822818 ,
6690263894 ,
8162122007 ,
9957788905 ,
12148502520 ,
14821173130 ,
18081831275 ,
22059834211 ,
26912997794 ,
32833857365 ,
40057306041 ,
48869913426 ,
59621294436 ,
72737979268 ,
88740334762 ,
108263208466 ,
132081114385 ,
132081114385 // 100레벨 부터 이해 못해-_-
};
const unsigned long ExpCapTable[Creature::PC_MAX_LEVEL] = {
58,
62,
67,
72,
82,
86,
96,
202,
221,
240,
259,
278,
298,
326,
355,
384,
413,
442,
480,
518,
566,
605,
653,
710,
768,
826,
893,
970,
1046,
1133,
1219,
1315,
1421,
1536,
1661,
1795,
1939,
2093,
2266,
2438,
2640,
2851,
3082,
3322,
3590,
3878,
4186,
4522,
4886,
5280,
5702,
6163,
6653,
7181,
7757,
8381,
9053,
9773,
10560,
11405,
12317,
13306,
14371,
15514,
16762,
18096,
19546,
21110,
22800,
24624,
26602,
28723,
31027,
33504,
36192,
39082,
42211,
45590,
49238,
53174,
57427,
62026,
66989,
72346,
78134,
84384,
91133,
98419,
106301,
114806,
123984,
133910,
144624,
156192,
168682,
182179,
196752,
212496,
229498,
247853
};
const float ExpConvertTable[Creature::PC_MAX_LEVEL] =
{
1.2f,
0.652173913f,
0.395833333f,
0.221052632f,
0.147058824f,
0.107142857f,
0.080434783f,
0.06056338f,
0.04952381f,
0.064356436f,
0.051724138f,
0.044887781f,
0.038745387f,
0.033426184f,
0.031149302f,
0.029661017f,
0.027516779f,
0.027027027f,
0.025438596f,
0.023050847f,
0.021866667f,
0.020726496f,
0.020138889f,
0.019400856f,
0.018979834f,
0.019104478f,
0.019107744f,
0.019010043f,
0.019446154f,
0.018932528f,
0.018588137f,
0.018548387f,
0.018669076f,
0.019495352f,
0.020523708f,
0.021702486f,
0.023075468f,
0.024573379f,
0.025590848f,
0.026882439f,
0.028578261f,
0.03020531f,
0.032458698f,
0.034889868f,
0.037479936f,
0.040498899f,
0.044010767f,
0.047801858f,
0.052198744f,
0.056302083f,
0.060962822f,
0.066287215f,
0.072166196f,
0.078949329f,
0.086399724f,
0.094809356f,
0.104353011f,
0.115183537f,
0.122776247f,
0.131411711f,
0.141578839f,
0.153099017f,
0.166192541f,
0.181208054f,
0.197992027f,
0.216885605f,
0.238397966f,
0.262553292f,
0.289851868f,
0.308373111f,
0.330063006f,
0.35528795f,
0.384373361f,
0.417348834f,
0.454948957f,
0.497478302f,
0.545998585f,
0.600566859f,
0.662433912f,
0.701894612f,
0.749179452f,
0.804414873f,
0.868368606f,
0.941942784f,
1.025947304f,
1.1215602f,
1.230182368f,
1.353346121f,
1.492902584f,
1.209087319f,
1.071556113f,
1.001289906f,
0.968735572f,
0.960942686f,
0.971115208f,
0.995614754f,
1.032601272f,
1.081251392f,
1.141193513f,
1.198466357f // 100레벨 부터 이해 못해-_-
};
};
#endif

View File

@@ -0,0 +1,775 @@
#include "CharSphereTree.h"
#include <cassert>
#include <Creature/Character/Character.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
CCharSphereTree& CCharSphereTree::GetInstance()
{
static CCharSphereTree ms_this( SphereConst::MAX_SPHERE_NODE,
SphereConst::DEFAULT_ROOT_SIZE,
SphereConst::DEFAULT_LEAF_SIZE,
SphereConst::DEFAULT_GRAVY );
return ms_this;
}
CCharSphereNode::CCharSphereNode() :
m_dwCID(0),
m_pCharacter(NULL),
m_pParent(NULL),
m_pChildren(NULL),
m_pPrevSibling(NULL),
m_pNextSibling(NULL),
m_ppRecomuteFifo(NULL),
m_ppIntegrateFifo(NULL),
m_iChildCount(0),
m_dwFlag(0),
m_fBindingDistance2(0),
m_pLink(NULL),
m_pTree(NULL)
{
}
CCharSphereNode::CCharSphereNode( CCharSphereTree* pTree, const Position& Pos, float fRadius, void* pLink ) :
CSphere(Pos, fRadius),
m_dwCID(0),
m_pCharacter(NULL),
m_pParent(NULL),
m_pChildren(NULL),
m_pPrevSibling(NULL),
m_pNextSibling(NULL),
m_ppRecomuteFifo(NULL),
m_ppIntegrateFifo(NULL),
m_iChildCount(0),
m_dwFlag(0),
m_fBindingDistance2(0),
m_pLink(pLink),
m_pTree(pTree)
{
}
void
CCharSphereNode::Init( CCharSphereTree* pTree, const Position& Pos, float fRadius, void* pLink )
{
m_pTree = pTree;
m_Center = Pos;
SetRadius( fRadius );
m_pLink = pLink;
}
void
CCharSphereNode::AddChild( CCharSphereNode* pChild )
{
CCharSphereNode* pOldChild = m_pChildren;
m_pChildren = pChild;
pChild->SetNextSibling( pOldChild );
pChild->SetPrevSibling( NULL );
pChild->SetParent( this );
if ( pOldChild )
{
pOldChild->SetPrevSibling( pChild );
}
++m_iChildCount;
float fDist = Distance2( pChild );
float fRadius = sqrtf( fDist ) + pChild->GetRadius();
assert( fRadius <= GetRadius() );
}
void
CCharSphereNode::DeleteChild( CCharSphereNode* pChild )
{
assert( m_iChildCount );
assert( m_pChildren );
#ifdef _DEBUG
// pChild 가 현재 노드의 자식인지 체크
CCharSphereNode* pChildren = m_pChildren;
bool bFound = false;
while ( pChildren )
{
if ( pChildren == pChild )
{
assert( !bFound );
bFound = true;
break;
}
pChildren = pChildren->GetNextSibling();
}
assert( bFound );
#endif
// Prev 와 Next Sibling 다시 설정
CCharSphereNode* pPrev = pChild->GetPrevSibling();
if ( pPrev )
{
CCharSphereNode* pNext = pChild->GetNextSibling();
pPrev->SetNextSibling( pNext );
if ( pNext ) pNext->SetPrevSibling( pPrev );
}
else
{
CCharSphereNode* pNext = pChild->GetNextSibling();
m_pChildren = pNext;
if ( m_pChildren ) m_pChildren->SetPrevSibling( NULL );
}
--m_iChildCount;
if ( 0 == m_iChildCount && HasSphereNodeFlag( SNF_SUPERSPHERE ) )
{
m_pTree->DeleteSphere( this );
}
}
void
CCharSphereNode::Unlink()
{
// Recompute & Integrate Fifo 에서 제거한다.
if ( m_ppRecomuteFifo )
{
*m_ppRecomuteFifo = NULL;
m_ppRecomuteFifo = NULL;
}
if ( m_ppIntegrateFifo )
{
*m_ppIntegrateFifo = NULL;
m_ppIntegrateFifo = NULL;
}
if ( m_pParent )
{
m_pParent->DeleteChild( this );
}
assert( !m_pChildren );
m_pParent = NULL;
}
void
CCharSphereNode::NewPos( const Position& Pos )
{
// 새로운 좌표 설정
m_Center = Pos;
// 부모가 있고, 재구성되어야 할 플래그가 없다면, 재구성을 할지를 판단한다.
if ( m_pParent && !HasSphereNodeFlag( SNF_INTEGRATE ) )
{
// 부모와의 거리의 제곱값을 계산한다.
float fDistance2 = Distance2( m_pParent );
// 거리가 바인딩 거리를 벗어난 경우
if ( fDistance2 >= m_fBindingDistance2 )
{
// 재계산 플래그가 없다면, 부모의 크기가 재계산되어야 한다.
if ( !m_pParent->HasSphereNodeFlag( SNF_RECOMPUTE ) )
{
m_pTree->AddRecompute( m_pParent );
}
// 부모와의 연결을 끊고, 해당 Root 노드에 연결한다.
Unlink();
m_pTree->AddIntegrate( this );
}
}
}
void
CCharSphereNode::NewPos( float fX, float fY, float fZ )
{
NewPos( Position(fX, fY, fZ) );
}
void
CCharSphereNode::NewPosRadius( const Position& Pos, float fRadius )
{
// 새로운 좌표 설정
m_Center = Pos;
// 부모가 있고, 재구성되어야 할 플래그가 없다면, 재구성을 할지를 판단한다.
if ( m_pParent && !HasSphereNodeFlag( SNF_INTEGRATE ) )
{
// 반지름이 변경 되었는지를 체크해서 변경되었다면,
// 반지름을 다시 설정하고, 바인딩 거리를 재계산한다.
if ( fRadius != GetRadius() )
{
SetRadius( fRadius );
ComputeBindingDistance( m_pParent );
}
// 부모와의 거리의 제곱값을 계산한다.
float fDistance2 = Distance2( m_pParent );
// 거리가 바인딩 거리를 벗어난 경우
if ( fDistance2 >= m_fBindingDistance2 )
{
// 재계산 플래그가 없다면, 부모의 크기가 재계산되어야 한다.
if ( !m_pParent->HasSphereNodeFlag( SNF_RECOMPUTE ) )
{
m_pTree->AddRecompute( m_pParent );
}
// 부모와의 연결을 끊고, 해당 Root 노드에 연결한다.
Unlink();
m_pTree->AddIntegrate( this );
}
// 자식이 부모의 바인딩 거리안에 있을 경우
else
{
// 부모의 바인딩 거리를 줄일수 있을수도 있기 때문에, 재계산을 한다.
if ( !m_pParent->HasSphereNodeFlag( SNF_RECOMPUTE ) )
{
m_pTree->AddRecompute( m_pParent );
}
}
}
}
void
CCharSphereNode::NewPosRadius( float fX, float fY, float fZ, float fRadius )
{
NewPosRadius( Position(fX, fY, fZ), fRadius );
}
float
CCharSphereNode::Distance2( const CCharSphereNode* pNode )
{
return static_cast<float>( m_Center.GetSquaredDistance( pNode->GetCenter() ) );
}
void
CCharSphereNode::ComputeBindingDistance( const CCharSphereNode* pParent )
{
m_fBindingDistance2 = pParent->GetRadius() - GetRadius();
if ( m_fBindingDistance2 <= 0 ) m_fBindingDistance2 = 0;
else m_fBindingDistance2 *= m_fBindingDistance2;
}
bool
CCharSphereNode::Recompute( float fGravy )
{
if ( !m_pChildren ) return true; // 제거 요망
if ( HasSphereNodeFlag( SNF_ROOT_NODE ) ) return false; // Root 는 재계산하지 않는다.
Position sumPos;
int iCount = 0;
// 자식들의 위치를 모두 더해서 평균을 구해서,
// 새로운 중심 좌표를 계산해 낸다.
CCharSphereNode* pChild = m_pChildren;
while ( pChild )
{
sumPos.m_fPointX += pChild->GetCenter().m_fPointX;
sumPos.m_fPointY += pChild->GetCenter().m_fPointY;
sumPos.m_fPointZ += pChild->GetCenter().m_fPointZ;
++iCount;
pChild = pChild->GetNextSibling();
}
if ( iCount )
{
float fRecip = 1.0f / float(iCount);
sumPos.m_fPointX *= fRecip;
sumPos.m_fPointY *= fRecip;
sumPos.m_fPointZ *= fRecip;
Position oldCenter = m_Center;
m_Center = sumPos; // 새로 구해준 중심 좌표로 설정
float fMaxRadius = 0;
// 자식들이 포함되는가 확인한다.
pChild = m_pChildren;
while ( pChild )
{
float fDistance2 = Distance2( pChild );
float fRadius = sqrtf( fDistance2 ) + pChild->GetRadius();
if ( fRadius > fMaxRadius )
{
fMaxRadius = fRadius;
if ( (fMaxRadius + fGravy) >= GetRadius() )
{
m_Center = oldCenter;
ClearShpereNodeFlag( SNF_RECOMPUTE );
return false;
}
}
pChild = pChild->GetNextSibling();
}
// 더 커진 반지름으로 재 설정
fMaxRadius += fGravy;
SetRadius( fMaxRadius );
// 자식들의 바인딩 거리를 재 계산
pChild = m_pChildren;
while ( pChild )
{
pChild->ComputeBindingDistance( this );
pChild = pChild->GetNextSibling();
}
}
ClearShpereNodeFlag( SNF_RECOMPUTE );
return false;
}
CCharSphereTree::CCharSphereTree( int iMaxSphereNode, float fRootSize, float fLeafSize, float fGravy ) :
m_fMaxRootSize(fRootSize), m_fMaxLeafSize(fLeafSize), m_fSuperSphereGravy(fGravy)
{
iMaxSphereNode *= 4;
m_Recompute = new CCharSphereNodeFifo( iMaxSphereNode );
m_Integrate = new CCharSphereNodeFifo( iMaxSphereNode );
// Root Tree 생성
Position rootPos;
m_pRoot = new CCharSphereNode();
m_pRoot->Init( this, rootPos, 65535, NULL );
m_pRoot->SetSphereNodeFlag( eSphereNodeFlag(SNF_ROOT_NODE | SNF_SUPERSPHERE | SNF_ROOT_TREE) );
// Leaf Tree 생성
m_pLeaf = new CCharSphereNode();
m_pLeaf->Init( this, rootPos, 16384, NULL );
m_pLeaf->SetSphereNodeFlag( eSphereNodeFlag(SNF_ROOT_NODE | SNF_SUPERSPHERE | SNF_LEAF_TREE) );
}
CCharSphereTree::~CCharSphereTree()
{
DeleteAllChildSphere( m_pRoot );
DeleteAllChildSphere( m_pLeaf );
SAFE_DELETE( m_pRoot );
SAFE_DELETE( m_pLeaf );
SAFE_DELETE( m_Recompute );
SAFE_DELETE( m_Integrate );
m_CharSphereMap.clear();
}
void
CCharSphereTree::Process()
{
// 재계산 되어야 할 목록에 있는 노드를 재계산한다.
// 만약 leaf node 가 부모를 벗어났다면, 부모가 재 구성되거나, 자식이 없다면, 제거되어야 한다.
if ( m_Recompute->GetCount() > 0 )
{
bool bKill;
CCharSphereNode* pTempNode = NULL;
int iCount = m_Recompute->GetCount();
for (int i=0; i<iCount; ++i)
{
pTempNode = m_Recompute->Pop();
if ( !pTempNode ) continue;
pTempNode->SetRecomputeFifo( NULL );
bKill = pTempNode->Recompute( m_fSuperSphereGravy );
if ( bKill )
{
DeleteSphere( pTempNode );
}
}
}
// 재구성 되어야 할 목록에 있는 노드를 재구성한다.
if ( m_Integrate->GetCount() > 0 )
{
CCharSphereNode* pTempNode = NULL;
int iCount = m_Integrate->GetCount();
for (int i=0; i<iCount; ++i)
{
pTempNode = m_Integrate->Pop();
if ( !pTempNode ) continue;
pTempNode->SetIntegrateFifo( NULL );
if ( pTempNode->HasSphereNodeFlag( SNF_ROOT_TREE ) )
Integrate( pTempNode, m_pRoot, m_fMaxRootSize ); // integrate this one single dude against the root node.
else
Integrate( pTempNode, m_pLeaf, m_fMaxLeafSize ); // integrate this one single dude against the root node.
}
}
}
bool
CCharSphereTree::AddCharacter( unsigned long dwCID, CCharacter* pCharacter, const Position& Pos )
{
CCharSphereNode* pNode = AddSphere( Pos, SphereConst::CHAR_RADIUS, NULL );
if ( pNode )
{
pNode->SetCID( dwCID );
pNode->SetCharacter( pCharacter );
pNode->SetSphereNodeFlag( SNF_ACTOR_NODE );
return m_CharSphereMap.insert( std::make_pair(dwCID, pNode) ).second;
}
return false;
}
bool
CCharSphereTree::DeleteCharacter( unsigned long dwCID )
{
CharSphereMap::iterator itr = m_CharSphereMap.find( dwCID );
if ( itr != m_CharSphereMap.end() )
{
CCharSphereNode* pNode = itr->second;
if ( pNode )
{
DeleteSphere( pNode );
}
m_CharSphereMap.erase( itr );
return true;
}
return false;
}
void
CCharSphereTree::MoveTo( unsigned long dwCID, const Position& NewPos )
{
CharSphereMap::iterator itr = m_CharSphereMap.find( dwCID );
if ( itr != m_CharSphereMap.end() )
{
CCharSphereNode* pNode = itr->second;
if ( pNode && pNode->GetCenter() != NewPos )
{
pNode->NewPos( NewPos );
}
}
}
CCharSphereNode*
CCharSphereTree::AddSphere( const Position& Pos, float fRadius, void* pLink, unsigned long dwFlag )
{
// 새로운 노드를 만든다.
CCharSphereNode* pNode = new CCharSphereNode();
assert( pNode );
if ( pNode )
{
// Root 와 Leaf 중에 한쪽의 멤버로 붙여주고, 재 구성 리스트에 넣는다.
if ( dwFlag & SNF_ROOT_TREE )
{
pNode->Init( this, Pos, fRadius, pLink );
pNode->SetSphereNodeFlag( SNF_ROOT_TREE );
AddIntegrate( pNode );
}
else
{
pNode->Init( this, Pos, fRadius, pLink );
pNode->SetSphereNodeFlag( SNF_LEAF_TREE );
AddIntegrate( pNode );
}
}
return pNode;
}
void
CCharSphereTree::DeleteSphere( CCharSphereNode* pNode )
{
// Root node 는 사용자가 지워준다.
if ( pNode->HasSphereNodeFlag( SNF_ROOT_NODE ) ) return;
// Leaf node tree 의 supersphere 는 링크된 root node tree 의 node 를 지워준다.
if ( pNode->HasSphereNodeFlag( SNF_SUPERSPHERE ) && pNode->HasSphereNodeFlag( SNF_LEAF_TREE) )
{
CCharSphereNode* pLink = (CCharSphereNode*) pNode->GetLink();
DeleteSphere( pLink );
}
pNode->Unlink();
SAFE_DELETE( pNode );
}
void
CCharSphereTree::DeleteAllChildSphere( CCharSphereNode* pRootNode )
{
CCharSphereNode* pTempNode = NULL;
CCharSphereNode* pChild = pRootNode->GetChildren();
while ( pChild )
{
if ( pChild->GetChildCount() )
{
DeleteAllChildSphere( pChild );
}
pTempNode = pChild;
pChild = pChild->GetNextSibling();
SAFE_DELETE( pTempNode );
}
}
void
CCharSphereTree::AddRecompute( CCharSphereNode* pNode )
{
if ( !pNode->HasSphereNodeFlag( SNF_RECOMPUTE ) )
{
if ( pNode->GetChildCount() )
{
pNode->SetSphereNodeFlag( SNF_RECOMPUTE );
CCharSphereNode** ppFifo = m_Recompute->Push( pNode );
pNode->SetRecomputeFifo( ppFifo );
}
else
{
DeleteSphere( pNode );
}
}
}
void
CCharSphereTree::AddIntegrate( CCharSphereNode* pNode )
{
if ( pNode->HasSphereNodeFlag( SNF_ROOT_TREE ) )
{
m_pRoot->AddChild( pNode );
}
else
{
m_pLeaf->AddChild( pNode );
}
pNode->SetSphereNodeFlag( SNF_INTEGRATE );
CCharSphereNode** ppFifo = m_Integrate->Push( pNode );
pNode->SetIntegrateFifo( ppFifo );
}
void
CCharSphereTree::Integrate( CCharSphereNode* pNode, CCharSphereNode* pSuperSphere, float fNodeSize )
{
// 재구성 단계 첫번째로 pNode 와 가장 가까이 있는 SuperSphere 를 찾는다.
CCharSphereNode* pSearch = pSuperSphere->GetChildren();
CCharSphereNode* pNearestNode1st = NULL; // pNode 를 완전히 둘러쌀수 있는 SuperSphere
CCharSphereNode* pNearestNode2nd = NULL; // pNode 를 추가하기 위해 약간 크기를 증가해야 하는 SuperSphere
float fNearDist1st2 = 1e9;
float fNearDist2nd2 = 1e9;
while ( pSearch )
{
if ( !pSearch->HasSphereNodeFlag( SNF_ROOT_NODE ) &&
pSearch->HasSphereNodeFlag( SNF_SUPERSPHERE ) &&
pSearch->GetChildCount() )
{
float fDistance2 = pNode->Distance2( pSearch );
if ( pNearestNode1st )
{
// 찾아 놓은 완전히 포함하는 SuperSphere 보다 더 가까운 녀석을 찾은 경우
if ( fDistance2 < fNearDist1st2 )
{
float fDist = sqrtf( fDistance2 ) + pNode->GetRadius();
if ( fDist <= pSearch->GetRadius() )
{
pNearestNode1st = pSearch;
fNearDist1st2 = fDistance2;
}
}
}
else
{
float fDist = sqrtf( fDistance2 ) + pNode->GetRadius() - pSearch->GetRadius();
if ( fDist < fNearDist2nd2 )
{
if ( fDist < 0 )
{
// 완전히 포함하는 SuperSphere 를 찾은 경우
pNearestNode1st = pSearch;
fNearDist1st2 = fDistance2;
}
else
{
// 완전히 포함하지는 못하지만, 반지름을 키워서 포함할 수 있는
// 가능성이 있는 SuperSphere 를 찾은 경우
pNearestNode2nd = pSearch;
fNearDist2nd2 = fDist;
}
}
}
}
pSearch = pSearch->GetNextSibling();
}
// pNode 를 완전히 포함할 수 있는 SuperSphere 를 찾은 경우
if ( pNearestNode1st )
{
// pNode 를 pNearestNode1st 의 자식으로 붙여주면 된다.
pNode->Unlink();
pNearestNode1st->AddChild( pNode );
pNearestNode1st->Recompute( m_fSuperSphereGravy );
pNode->ComputeBindingDistance( pNearestNode1st );
// 만약 leaf node 의 supersphere 라면, root tree 의 link node 또한 좌표와 크기를 갱신해준다.
if ( pNearestNode1st->HasSphereNodeFlag( SNF_LEAF_TREE ) )
{
CCharSphereNode* pLink = (CCharSphereNode*) pNearestNode1st->GetLink();
pLink->NewPosRadius( pNearestNode1st->GetCenter(), pNearestNode1st->GetRadius() );
}
}
else
{
bool bCreateNewSphere = true;
// 크기를 약간 키우면 pNode 를 포함할 수 있을만한 SuperSphere 를 찾은 경우
if ( pNearestNode2nd )
{
float fNewSize = fNearDist2nd2 + pNearestNode2nd->GetRadius() + m_fSuperSphereGravy;
if ( fNewSize <= fNodeSize )
{
pNode->Unlink();
pNearestNode2nd->SetRadius( fNewSize );
pNearestNode2nd->AddChild( pNode );
pNearestNode2nd->Recompute( m_fSuperSphereGravy );
pNode->ComputeBindingDistance( pNearestNode2nd );
if ( pNearestNode2nd->HasSphereNodeFlag( SNF_LEAF_TREE ) )
{
CCharSphereNode* pLink = (CCharSphereNode*) pNearestNode2nd->GetLink();
pLink->NewPosRadius( pNearestNode2nd->GetCenter(), pNearestNode2nd->GetRadius() );
}
bCreateNewSphere = false;
}
}
// 새로운 SuperSphere 를 만들어야 하는 경우
if ( bCreateNewSphere )
{
assert( pSuperSphere->HasSphereNodeFlag( SNF_ROOT_NODE ) );
pNode->Unlink();
CCharSphereNode* pParent = new CCharSphereNode();
assert( pParent );
pParent->Init( this, pNode->GetCenter(), pNode->GetRadius() + m_fSuperSphereGravy, NULL );
if ( pSuperSphere->HasSphereNodeFlag( SNF_ROOT_TREE ) )
{
pParent->SetSphereNodeFlag( eSphereNodeFlag(SNF_SUPERSPHERE | SNF_ROOT_TREE) );
}
else
{
pParent->SetSphereNodeFlag( eSphereNodeFlag(SNF_SUPERSPHERE | SNF_LEAF_TREE) );
}
pParent->AddChild( pNode );
pSuperSphere->AddChild( pParent );
pParent->Recompute( m_fSuperSphereGravy );
pNode->ComputeBindingDistance( pParent );
if ( pParent->HasSphereNodeFlag( SNF_LEAF_TREE ) )
{
// root node 에 link 를 생성해야 한다.
CCharSphereNode* pLink = AddSphere( pParent->GetCenter(), pParent->GetRadius(), pParent, SNF_ROOT_TREE );
pParent->SetLink( pLink );
}
}
}
pNode->ClearShpereNodeFlag( SNF_INTEGRATE );
}
void
CCharSphereNode::RangeTest( const Position& centerPos, float fDistance, ICharSphereTreeCallBack* pCallBack )
{
float fDist = static_cast<float>( m_Center.GetDistance( centerPos ) );
if ( (fDist - fDistance) > GetRadius() ) return;
if ( HasSphereNodeFlag( SNF_SUPERSPHERE ) )
{
CCharSphereNode* pNode = m_pChildren;
while ( pNode )
{
pNode->RangeTest( centerPos, fDistance, pCallBack );
pNode = pNode->GetNextSibling();
}
}
else
{
if ( HasSphereNodeFlag( SNF_ROOT_TREE ) )
{
CCharSphereNode* pLink = (CCharSphereNode*) GetLink();
if ( pLink )
{
pLink->RangeTest( centerPos, fDistance, pCallBack ) ;
}
}
else
{
pCallBack->RangeTestCallBack( centerPos, fDistance, this );
}
}
}
void
CCharSphereTree::RangeTest( const Position& centerPos, float fDistance, ICharSphereTreeCallBack* pCallBack )
{
m_pRoot->RangeTest( centerPos, fDistance, pCallBack );
}
void
CCharSphereNode::SendToRange( const Position& centerPos, float fDistance, const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In )
{
float fDist = static_cast<float>( m_Center.GetDistance( centerPos ) );
if ( (fDist - fDistance) > GetRadius() ) return;
if ( HasSphereNodeFlag( SNF_SUPERSPHERE ) )
{
CCharSphereNode* pNode = m_pChildren;
while ( pNode )
{
pNode->SendToRange( centerPos, fDistance, szPacket, dwPacketSize, cCMD_In );
pNode = pNode->GetNextSibling();
}
}
else
{
if ( HasSphereNodeFlag( SNF_ROOT_TREE ) )
{
CCharSphereNode* pLink = (CCharSphereNode*) GetLink();
if ( pLink )
{
pLink->SendToRange( centerPos, fDistance, szPacket, dwPacketSize, cCMD_In ) ;
}
}
else
{
// Packet 전송
if (NULL != m_pCharacter)
{
CGameClientDispatch* lpDispatcher = m_pCharacter->GetDispatcher();
if (NULL != lpDispatcher)
{
lpDispatcher->GetSendStream().PutBuffer( szPacket, dwPacketSize, cCMD_In );
}
}
}
}
}
void
CCharSphereTree::SendToRange( const Position& centerPos, float fDistance, const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In )
{
m_pRoot->SendToRange( centerPos, fDistance, szPacket, dwPacketSize, cCMD_In );
}

View File

@@ -0,0 +1,273 @@
#ifndef __CHARACTER_SPHERE_TREE_H__
#define __CHARACTER_SPHERE_TREE_H__
#include "Sphere.h"
#include "CharSphereTreeConstants.h"
using namespace SphereConst;
#include <map>
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if ((p)) { delete (p); (p) = NULL; } }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if ((p)) { delete [] (p); (p) = NULL; } }
#endif
//======================================================================================
struct Position;
class CCharacter;
class CSphere;
class CCharSphereNode;
class CCharSphereTree;
class CCharSphereNodeFifo;
class ICharSphereTreeCallBack;
//======================================================================================
class ICharSphereTreeCallBack
{
public:
virtual void RangeTestCallBack( const Position& centerPos, float fDistance, CCharSphereNode* pNode )
{
}
private:
};
//======================================================================================
class CCharSphereNode : public CSphere
{
public:
CCharSphereNode();
CCharSphereNode( CCharSphereTree* pTree, const Position& Pos, float fRadius, void* pLink );
void Init( CCharSphereTree* pTree, const Position& Pos, float fRadius, void* pLink );
// ===================================================================
// Flag 함수
void SetSphereNodeFlag( eSphereNodeFlag flag ) { m_dwFlag |= flag; }
void ClearShpereNodeFlag( eSphereNodeFlag flag ) { m_dwFlag &= ~flag; }
bool HasSphereNodeFlag( eSphereNodeFlag flag ) const
{
if ( m_dwFlag & flag ) return true;
return false;
}
// ===================================================================
// 캐릭터 관련 함수
void SetCID( unsigned long dwCID ) { m_dwCID = dwCID; }
unsigned long GetCID() const { return m_dwCID; }
void SetCharacter(CCharacter* pCharacter) { m_pCharacter = pCharacter; }
CCharacter* GetCharacter() const { return m_pCharacter; }
// ===================================================================
// 부모 & 자식 함수
void SetParent( CCharSphereNode* pParent ) { m_pParent = pParent; }
CCharSphereNode* GetParent() const { return m_pParent; }
void AddChild( CCharSphereNode* pChild );
void DeleteChild( CCharSphereNode* pChild );
CCharSphereNode* GetChildren() const { return m_pChildren; }
int GetChildCount() const { return m_iChildCount; }
// ===================================================================
// 같은 레벨의 형제 함수
void SetPrevSibling( CCharSphereNode* pNode ) { m_pPrevSibling = pNode; }
void SetNextSibling( CCharSphereNode* pNode ) { m_pNextSibling = pNode; }
CCharSphereNode* GetPrevSibling() const { return m_pPrevSibling; }
CCharSphereNode* GetNextSibling() const { return m_pNextSibling; }
// ===================================================================
// Fifo 포인터 관리 함수
void SetRecomputeFifo( CCharSphereNode** ppFifo ) { m_ppRecomuteFifo = ppFifo; }
void SetIntegrateFifo( CCharSphereNode** ppFifo ) { m_ppIntegrateFifo = ppFifo; }
// ===================================================================
// 부모와 자식 모두의 연결을 끊는 함수 (실제로는 부모와만 연결을 끊는다.)
void Unlink();
// ===================================================================
// Root tree node 의 Link ( Leaf tree 의 Root 가 아닌 SuperSphere node 가 가진다 )
void SetLink( void* pLink ) { m_pLink = pLink; }
void* GetLink() const { return m_pLink; }
// ===================================================================
// 좌표 변경 함수
void NewPos( const Position& Pos );
void NewPos( float fX, float fY, float fZ );
void NewPosRadius( const Position& Pos, float fRadius );
void NewPosRadius( float fX, float fY, float fZ, float fRadius );
// ===================================================================
// 거리 및 바인딩거리 계산 함수
float Distance2( const CCharSphereNode* pNode );
void ComputeBindingDistance( const CCharSphereNode* pParent );
// ===================================================================
// 크기 재계산 함수 ( true 리턴시에는 제거해야함 )
bool Recompute( float fGravy );
// ===================================================================
// 범위 테스트용 함수
void RangeTest( const Position& centerPos, float fDistance, ICharSphereTreeCallBack* pCallBack );
void SendToRange( const Position& centerPos, float fDistance, const char* szPacket,
unsigned long dwPacketSize, unsigned char cCMD_In );
protected:
private:
unsigned long m_dwCID;
CCharacter* m_pCharacter;
CCharSphereNode* m_pParent;
CCharSphereNode* m_pChildren;
CCharSphereNode* m_pPrevSibling;
CCharSphereNode* m_pNextSibling;
CCharSphereNode** m_ppRecomuteFifo; // Recompute Fifo 내의 위치 포인터
CCharSphereNode** m_ppIntegrateFifo; // Integrate Fifo 내의 위치 포인터
int m_iChildCount;
unsigned long m_dwFlag;
float m_fBindingDistance2;
void* m_pLink;
CCharSphereTree* m_pTree;
};
//======================================================================================
class CCharSphereTree
{
public:
CCharSphereTree( int iMaxSphereNode, float fRootSize, float fLeafSize, float fGravy );
virtual ~CCharSphereTree();
typedef std::map< unsigned long, CCharSphereNode* > CharSphereMap;
static CCharSphereTree& GetInstance();
// ===================================================================
// Tree 의 Node 를 재계산하고 재구성하는 처리 함수
void Process();
// ===================================================================
// 캐릭터 관련 함수
bool AddCharacter( unsigned long dwCID, CCharacter* pCharacter, const Position& Pos ) ;
bool DeleteCharacter( unsigned long dwCID );
void MoveTo( unsigned long dwCID, const Position& NewPos );
// ===================================================================
// CCharSphereNode 를 생성(캐릭터 제외)해서 재구성하는 함수 와 삭제하는 함수
CCharSphereNode* AddSphere( const Position& Pos, float fRadius, void* pLink, unsigned long dwFlag = SNF_LEAF_TREE );
void DeleteSphere( CCharSphereNode* pNode );
// ===================================================================
// 재계산, 재구성 리스트에 Node 추가 함수
void AddRecompute( CCharSphereNode* pNode );
void AddIntegrate( CCharSphereNode* pNode );
// ===================================================================
// Tree 를 재구성하는 함수
void Integrate( CCharSphereNode* pNode, CCharSphereNode* pSuperSphere, float fNodeSize );
// ===================================================================
// 범위 테스트용 함수
void RangeTest( const Position& centerPos, float fDistance, ICharSphereTreeCallBack* pCallBack );
void SendToRange( const Position& centerPos, float fDistance, const char* szPacket,
unsigned long dwPacketSize, unsigned char cCMD_In );
protected:
// ===================================================================
// 모든 자식 Node 를 메모리 해제하는 함수
void DeleteAllChildSphere( CCharSphereNode* pRootNode );
private:
CCharSphereNode* m_pRoot;
CCharSphereNode* m_pLeaf;
CCharSphereNodeFifo* m_Recompute;
CCharSphereNodeFifo* m_Integrate;
float m_fMaxRootSize;
float m_fMaxLeafSize;
float m_fSuperSphereGravy;
CharSphereMap m_CharSphereMap;
};
//======================================================================================
class CCharSphereNodeFifo
{
public:
CCharSphereNodeFifo( int iSize )
{
m_iCount = 0;
m_iStackPointer = 0;
m_iBottom = 0;
m_iSize = iSize;
m_ppFifo = new CCharSphereNode *[ m_iSize ];
};
~CCharSphereNodeFifo()
{
SAFE_DELETE_ARRAY( m_ppFifo );
};
CCharSphereNode** Push( CCharSphereNode* pNode )
{
m_iCount++;
CCharSphereNode** ret = &m_ppFifo[ m_iStackPointer ];
m_ppFifo[ m_iStackPointer ] = pNode;
++m_iStackPointer;
if ( m_iStackPointer == m_iSize ) m_iStackPointer = 0;
return ret;
};
CCharSphereNode* Pop()
{
while ( m_iStackPointer != m_iBottom )
{
--m_iCount;
CCharSphereNode* ret = m_ppFifo[ m_iBottom ];
++m_iBottom;
if ( m_iBottom == m_iSize ) m_iBottom = 0;
if ( ret ) return ret;
}
return NULL;
}
bool Flush( CCharSphereNode* pNode )
{
if ( m_iStackPointer == m_iBottom ) return false;
int i = m_iBottom;
while ( i != m_iStackPointer )
{
if ( m_ppFifo[i] == pNode )
{
m_ppFifo[i] = NULL;
return true;
}
i++;
if ( i == m_iSize ) i = 0;
}
return false;
};
int GetCount(void) const { return m_iCount; }
private:
int m_iCount;
int m_iStackPointer;
int m_iBottom;
int m_iSize;
CCharSphereNode** m_ppFifo;
};
#endif __CHARACTER_SPHERE_TREE_H__

View File

@@ -0,0 +1,34 @@
#ifndef __SPHERE_TREE_CONST_H__
#define __SPHERE_TREE_CONST_H__
namespace SphereConst
{
enum eSphereNodeFlag
{
SNF_ROOT_NODE = (1<<0), // this is the root node
SNF_SUPERSPHERE = (1<<1), // this is a supersphere, allocated and deleted by us
SNF_ACTOR_NODE = (1<<2), // this is real actor node
SNF_ROOT_TREE = (1<<3), // member of the root tree
SNF_LEAF_TREE = (1<<4), // member of the leaf node tree
SNF_RECOMPUTE = (1<<5), // needs recomputed bounding sphere
SNF_INTEGRATE = (1<<6) // needs to be reintegrated into tree
};
enum eViewState
{
VS_INSIDE, // completely inside the frustum.
VS_PARTIAL, // partially inside and partially outside the frustum.
VS_OUTSIDE // completely outside the frustum
};
enum eConst
{
CHAR_RADIUS = 2, // 캐릭터 반경 2m
MAX_SPHERE_NODE = 2000, // MAX 2000 명까지
DEFAULT_ROOT_SIZE = 256, // Root node 의 사이즈
DEFAULT_LEAF_SIZE = 64, // Leaf node 의 사이즈
DEFAULT_GRAVY = 15 // 부모원과 자식원의 반지름 차
};
};
#endif __SPHERE_TREE_CONST_H__

View File

@@ -0,0 +1,214 @@
#ifndef __SPHERE_H__
#define __SPHERE_H__
#include <math.h>
#include <Creature/CreatureStructure.h>
//======================================================================================
// 3차원 좌표 구조체
/*
#ifndef EPSILON
#define EPSILON 0.001
#endif
struct sVector3
{
union
{
struct
{
float x;
float y;
float z;
};
float v[3];
};
sVector3()
{
x = 0;
y = 0;
z = 0;
}
sVector3( float fX, float fY, float fZ )
{
x = fX;
y = fY;
z = fZ;
}
float Length() const
{
return sqrtf( x*x + y*y + z*z );
}
float Distance( const sVector3& inPos ) const
{
sVector3 tempVector( inPos.x - x, inPos.y - y, inPos.z - z );
return tempVector.Length();
}
float Distance2( const sVector3& inPos ) const
{
return ( (inPos.x - x) * (inPos.x - x) + (inPos.y - y) * (inPos.y - y) + (inPos.z - z) * (inPos.z - z) );
}
bool operator == ( const sVector3& inPos ) const
{
if ( fabs(x - inPos.x) <= EPSILON && fabs(y - inPos.y) <= EPSILON && fabs(z - inPos.z) <= EPSILON )
{
return true;
}
return false;
}
bool operator != ( const sVector3& inPos ) const
{
if ( fabs(x - inPos.x) > EPSILON || fabs(y - inPos.y) > EPSILON || fabs(z - inPos.z) > EPSILON )
{
return true;
}
return false;
}
};
*/
//======================================================================================
// 기본 구 클래스
class CSphere
{
public:
CSphere();
CSphere( float fX, float fY, float fZ, float fRadius );
CSphere( const Position& inPos, float fRadius );
// ===============================================================
void Set( float fX, float fY, float fZ, float fRadius );
void Set( const Position& inPos, float fRadius );
void SetRadius( float fRadius );
float GetX() const { return m_Center.m_fPointX; }
float GetY() const { return m_Center.m_fPointY; }
float GetZ() const { return m_Center.m_fPointZ; }
const Position& GetCenter() const { return m_Center; }
float GetRadius() const { return m_fRadius; }
float GetRadius2() const { return m_fRadius2; }
// ===============================================================
// 3차원 좌표가 구안에 있는지 검사
// 구의 반지름 + fDistance 안에 있는지 검사를 한다.
bool InSphere( float fX, float fY, float fZ, float fDistance );
bool InSphere( const Position& inPos, float fDistance );
// 2차원 좌표가 구안에 있는지 검사
// 구의 반지름 + fDistance 안에 있는지 검사를 한다.
bool InSphereXY( float fX, float fY, float fDistance );
bool InSphereXY( const Position& inPos, float fDistance );
protected:
Position m_Center; // 구의 중심 좌표
private:
float m_fRadius; // 구의 반지름
float m_fRadius2; // 구의 반지름 제곲 값
};
inline
CSphere::CSphere() :
m_fRadius(0), m_fRadius2(0)
{
}
inline
CSphere::CSphere( float fX, float fY, float fZ, float fRadius ) :
m_fRadius(fRadius), m_fRadius2(fRadius * fRadius)
{
}
inline
CSphere::CSphere( const Position& inPos, float fRadius ) :
m_Center(inPos), m_fRadius(fRadius), m_fRadius2(fRadius * fRadius)
{
}
inline void
CSphere::Set( float fX, float fY, float fZ, float fRadius )
{
m_Center.m_fPointX = fX;
m_Center.m_fPointY = fY;
m_Center.m_fPointZ = fZ;
m_fRadius = fRadius;
m_fRadius2 = fRadius * fRadius;
}
inline void
CSphere::Set( const Position& inPos, float fRadius )
{
m_Center = inPos;
m_fRadius = fRadius;
m_fRadius2 = fRadius * fRadius;
}
inline void
CSphere::SetRadius( float fRadius )
{
m_fRadius = fRadius;
m_fRadius2 = fRadius * fRadius;
}
inline bool
CSphere::InSphere( float fX, float fY, float fZ, float fDistance )
{
float dx = fX - m_Center.m_fPointX;
float dy = fY - m_Center.m_fPointY;
float dz = fZ - m_Center.m_fPointZ;
float dist = sqrtf( dx*dx + dy*dy + dz*dz );
if ( dist < (m_fRadius + fDistance) ) return true;
return false;
}
inline bool
CSphere::InSphere( const Position& inPos, float fDistance )
{
float dx = inPos.m_fPointX - m_Center.m_fPointX;
float dy = inPos.m_fPointY - m_Center.m_fPointY;
float dz = inPos.m_fPointZ - m_Center.m_fPointZ;
float dist = sqrtf( dx*dx + dy*dy + dz*dz );
if ( dist < (m_fRadius + fDistance) ) return true;
return false;
}
inline bool
CSphere::InSphereXY( float fX, float fY, float fDistance )
{
float dx = fX - m_Center.m_fPointX;
float dy = fY - m_Center.m_fPointY;
float dist = sqrtf( dx*dx + dy*dy );
if ( dist < (m_fRadius + fDistance) ) return true;
return false;
}
inline bool
CSphere::InSphereXY( const Position& inPos, float fDistance )
{
float dx = inPos.m_fPointX - m_Center.m_fPointX;
float dy = inPos.m_fPointY - m_Center.m_fPointY;
float dist = sqrtf( dx*dx + dy*dy );
if ( dist < (m_fRadius + fDistance) ) return true;
return false;
}
#endif __SPHERE_H__

View File

@@ -0,0 +1,81 @@
#include "stdafx.h"
#include "TempCharacter.h"
CTempCharacter::CTempCharacter()
: m_dwUID(0), m_dwCID(0), m_nDataRequestCount(0), m_cGroup(-1), m_cFlag(0)
{
memset(&m_szCharacterName, 0, sizeof(char) * CHAR_INFOST::MAX_NAME_LEN);
memset(&m_CharInfoEX, 0, sizeof(CHAR_INFOEX));
memset(&m_Quest, 0, sizeof(QUEST));
memset(&m_History, 0, sizeof(HISTORY));
memset(&m_Config, 0, sizeof(CONFIG));
memset(&m_StoreInfo, 0, sizeof(STORE_INFO));
}
CTempCharacter::~CTempCharacter()
{
}
CTempCharacterMgr::~CTempCharacterMgr()
{
isMapCharList::iterator pos = m_mapTempChar.begin();
isMapCharList::iterator end = m_mapTempChar.end();
for(;pos != end; ++pos)
{
m_tempCharPool.destroy(pos->second);
}
m_mapTempChar.clear();
}
// CID/Group을 키로 하고, 일치하는 클래스가 있으면 가져온다. 없으면 생성한 후 리턴한다.
CTempCharacter* CTempCharacterMgr::GetCharacter(unsigned long dwBattleCID, unsigned char cGroup)
{
std::pair<isMapCharList::iterator, isMapCharList::iterator>
result = m_mapTempChar.equal_range(dwBattleCID);
CTempCharacter* lpCharacter = 0;
for(; result.first != result.second; ++result.first)
{
lpCharacter = result.first->second;
if(cGroup == lpCharacter->GetGroup())
{
return lpCharacter;
}
}
// 못찾았다. 하나 삽입한다.
lpCharacter = m_tempCharPool.construct();
if(0 != lpCharacter)
{
lpCharacter->SetGroup(cGroup);
m_mapTempChar.insert(result.first, std::make_pair(dwBattleCID, lpCharacter));
}
return lpCharacter;
}
// 캐릭터 로그아웃시 호출한다.
bool CTempCharacterMgr::EraseChar(unsigned long dwBattleCID)
{
std::pair<isMapCharList::iterator, isMapCharList::iterator>
result = m_mapTempChar.equal_range(dwBattleCID);
for(; result.first != result.second;)
{
m_tempCharPool.destroy(result.first->second);
m_mapTempChar.erase(result.first++);
}
return true;
}

View File

@@ -0,0 +1,105 @@
#ifndef _TEMP_CHARACTER_H_
#define _TEMP_CHARACTER_H_
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/object_pool.hpp>
#include <map>
#include <DB/DBdefine.h>
class CTempCharacterMgr;
// TODO : 환전소(배틀그라운드 서버군)용으로 제작된 클래스입니다.
// 운영툴에서 사용하고 있는 CModifyCharacter의 기능 일부를 차용해왔는데...
// 추후 CModifyCharacter가 이 클래스를 상속받아 약간 수정하면 좋을 듯 합니다.
class CTempCharacter
{
public:
enum Const
{
SLOT_INFO_SET = (1 << 0), // 정섭 UID/CID/이름 이 세팅되었는지 여부
CHAR_DATA_SET = (1 << 1), // 캐릭터 데이터가 세팅되었는지 여부
SLOT_INFO_REQ = (1 << 2), // 정섭 UID/CID/이름 을 요청하였음.
CHAR_DATA_REQ = (1 << 3) // 캐릭터 데이터를 요청하였음
};
CTempCharacter();
~CTempCharacter();
unsigned char GetGroup(void) { return m_cGroup; }
void SetGroup(unsigned char cGroup) { m_cGroup = cGroup; }
// 임시 객체이므로 아이디를 마음대로 설정할 수 있다. 다른 곳(상/하위 클래스)에서 사용하면 위험하다.
void SetUID(unsigned long dwUID) { m_dwUID = dwUID; }
void SetCID(unsigned long dwCID) { m_dwCID = dwCID; }
// 저장되는 UID/CID는 정섭 UID, 정섭 CID이다. (배틀로한과는 다르다!)
unsigned long GetUID() const { return m_dwUID; }
unsigned long GetCID() const { return m_dwCID; }
const char* GetCharacterName() const { return m_szCharacterName; }
void SetCharacterName(const char* szName)
{
strncpy(m_szCharacterName, szName, CHAR_INFOST::MAX_NAME_LEN);
m_szCharacterName[CHAR_INFOST::MAX_NAME_LEN] = 0;
}
CHAR_INFOEX& GetCharInfoEx() { return m_CharInfoEX; }
QUEST& GetQuest() { return m_Quest; }
HISTORY& GetHistory() { return m_History; }
CONFIG& GetConfig() { return m_Config; }
STORE_INFO& GetStoreInfo() { return m_StoreInfo; }
bool IsSetData(unsigned char cFlag) const { return 0 != (m_cFlag & cFlag); }
void SetData(unsigned char cFlag) { m_cFlag |= cFlag; }
void ResetData(unsigned char cFlag) { m_cFlag &= ~cFlag; }
void ClearData() { m_cFlag = 0; }
int AddDataRequestCount() { return ++m_nDataRequestCount; }
int ReleaseDataRequestCount() { return --m_nDataRequestCount; }
int GetDataRequestCount() { return m_nDataRequestCount; }
private:
unsigned long m_dwUID;
unsigned long m_dwCID;
int m_nDataRequestCount;
char m_szCharacterName[CHAR_INFOST::MAX_NAME_LEN];
CHAR_INFOEX m_CharInfoEX;
QUEST m_Quest;
HISTORY m_History;
CONFIG m_Config;
STORE_INFO m_StoreInfo;
unsigned char m_cGroup; // 속한 서버군
unsigned char m_cFlag;
};
class CTempCharacterMgr
{
public:
// Key : 배틀로한 캐릭터 CID / Value : 캐릭터 정보
typedef std::multimap<unsigned long, CTempCharacter*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CTempCharacter*> > > isMapCharList;
// CID/Group을 키로 하고, 일치하는 클래스가 있으면 가져온다. 없으면 생성한 후 리턴한다.
CTempCharacter* GetCharacter(unsigned long dwBattleCID, unsigned char cGroup);
// 캐릭터 로그아웃시 호출한다.
bool EraseChar(unsigned long dwBattleCID);
~CTempCharacterMgr();
private:
boost::object_pool<CTempCharacter> m_tempCharPool;
isMapCharList m_mapTempChar;
};
#endif

View File

@@ -0,0 +1,5 @@
#include "stdafx.h"
#include "Creature.h"

View File

@@ -0,0 +1,75 @@
#ifndef _CCREATURE_H_
#define _CCREATURE_H_
#include "stdafx.h"
#include <DB/DBdefine.h>
#include <Network/Packet/PacketStruct/CharItemPacketStruct.h>
#include "EnemyCheck.h"
#include "CreatureStructure.h"
class CCell;
class CCharacter;
class CParty;
namespace Item
{
class CItem;
};
class CCreature
{
public:
enum MoveInfo
{
S_CELL_CHANGED = 1,
S_CELL_UNCHANGED = 2,
F_CELL_CANNOTCHANGE = 3,
F_TOO_FAST = 4
};
CCreature(unsigned long dwCID) : m_dwCID(dwCID) { }
virtual ~CCreature() { }
const Position& GetCurrentPos(void) const { return m_CurrentPos; }
const Position& GetCastingPos(void) const { return m_CastingPos; }
void SetCurrentPos(const Position& pos) { m_CurrentPos = pos; }
void SetCurrentPos(float fX, float fY, float fZ) { m_CurrentPos.m_fPointX = fX; m_CurrentPos.m_fPointY = fY; m_CurrentPos.m_fPointZ = fZ; }
void SetCastingPos() { m_CastingPos = m_CurrentPos; }
void SetCastingPos(const Position& pos) { m_CastingPos = pos; }
void SetCastingPos(float fX, float fY, float fZ) { m_CastingPos.m_fPointX = fX; m_CastingPos.m_fPointY = fY; m_CastingPos.m_fPointZ = fZ; }
const MotionInfo& GetMotionInfo(void) const { return m_MotionInfo; }
void SetDirection(float fDir) { m_MotionInfo.m_fDirection = fDir; }
EnchantInfo& GetEnchantInfo(void) { return m_EnchantInfo; }
unsigned long GetCID(void) const { return m_dwCID; }
virtual unsigned long RepairItem(Item::CEquipment* pEquipment, unsigned long& dwCurrentGold) { return 0; }
virtual Item::CItem* SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError) = 0;
virtual EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget, unsigned char* cResult = NULL) = 0;
virtual unsigned char GetNation() const = 0;
virtual CParty* GetParty() = 0;
virtual bool IsPeaceMode() = 0;
virtual unsigned long GetGID() const = 0;
virtual bool IsRideArms() const = 0;
protected:
Position m_CurrentPos; // 현재 위치
Position m_CastingPos; // 스킬 캐스팅 위치
MotionInfo m_MotionInfo; // 모션 정보
EnchantInfo m_EnchantInfo; // 챈트/인챈트 정보
unsigned long m_dwCID; // 캐릭터 CID.
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,329 @@
#ifndef _CCREAUTRE_MANAGER_H_
#define _CCREAUTRE_MANAGER_H_
#pragma once
#pragma warning(disable:4800)
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <map>
#include <list>
#include <functional>
#include <Thread/Lock.h>
#include <DB/DBdefine.h>
#include <Creature/Character/CharacterClass.h>
#include <Network/Stream/SendStream.h>
#include "CreatureStructure.h"
// 전방 참조
class CCreature;
class CAggresiveCreature;
class CCharacter;
class CMonster;
class CNPC;
class CSiegeObject;
namespace Item
{
class CShopContainer;
};
// SingleThread 클래스
class CCreatureManager
{
private:
// Map에서 second 멤버로 함수자 실행
template<typename FnSecondProcess, typename PairType>
class CProcessSecond : public std::unary_function<FnSecondProcess, bool>
{
public:
CProcessSecond(FnSecondProcess& fnSecondProcess) : m_fnSecondProcess(fnSecondProcess) { }
bool operator() (PairType& Pair) { m_fnSecondProcess(Pair.second); return true; }
protected:
FnSecondProcess& m_fnSecondProcess;
};
public:
typedef std::map<unsigned long, CCharacter*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CCharacter*> > > CharacterMap;
typedef std::map<unsigned long, CMonster*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CMonster*> > > MonsterMap;
typedef std::map<unsigned short, unsigned short, std::less<unsigned short>,
boost::fast_pool_allocator<std::pair<unsigned short, unsigned short> > > AdminMonsterUIDMap; // < KindID, NextUID(상위16비트) >
typedef std::map<unsigned long, CNPC*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CNPC*> > > NPCMap;
// 이건 boost::pool쓰지 말 것.
typedef std::list<CCharacter*> CharacterList;
typedef std::multimap<unsigned long, CCharacter*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CCharacter*> > > CharacterMultimap;
typedef std::map<unsigned long, CSiegeObject*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CSiegeObject*> > > SiegeObjectMap;
static CCreatureManager& GetInstance();
enum eAdminMonster
{
NO_BLANK_UID = 0x8FFF,
INIT_UID = 0x8FFE
};
// --------------------------------------------------------------------------------------------
// interface
// 캐릭터 생성 및 삭제를 위한 Wrapper.
CCharacter* CreateCharacter(unsigned long dwCID, unsigned long dwSessionID);
void DeleteCharacter(CCharacter* lpCharacter);
// Creature를 매니저에 등록 및 삭제.
bool AddCreature(CCreature* lpCreature);
bool DeleteCreature(unsigned long dwCID);
CCreature* GetCreature(unsigned long dwCID);
CAggresiveCreature* GetAggresiveCreature(unsigned long dwCID);
CNPC* GetNPC(unsigned long dwCID);
CMonster* GetMonster(unsigned long dwCID);
CSiegeObject* GetSiegeObject(unsigned long dwCID);
CCharacter* GetCharacter(unsigned long dwCID);
CCharacter* GetCharacter(const char* szCharacterName);
void DestoryCharacterList();
void DestoryMonsterList();
void DestorySiegeObjectList();
void DestoryNPCList();
void DestroyAll();
void EnqueueLogout(CCharacter* lpCharacter);
bool CancelLogout(CCharacter* lpCharacter);
bool ProcessCharacterLogout(void);
void ProcessSummonMonsterDead(void);
void SetNationToCastleNPC(unsigned long dwCastleID, unsigned char cNation);
template<typename FnCharacterProcess>
inline void ProcessAllCharacter(FnCharacterProcess fnCharacterProcess)
{
std::for_each(m_CharacterMap.begin(), m_CharacterMap.end(),
CProcessSecond<FnCharacterProcess, CharacterMap::value_type>(fnCharacterProcess));
}
template<typename FnMonsterProcess>
inline void ProcessAllMonster(FnMonsterProcess fnMonsterProcess)
{
std::for_each(m_MonsterMap.begin(), m_MonsterMap.end(),
CProcessSecond<FnMonsterProcess, MonsterMap::value_type>(fnMonsterProcess));
}
template<typename FnNPCProcess>
inline void ProcessAllNPC(FnNPCProcess fnNPCProcess)
{
std::for_each(m_NPCMap.begin(), m_NPCMap.end(),
CProcessSecond<FnNPCProcess, NPCMap::value_type>(fnNPCProcess));
}
unsigned short GetCharacterNum(unsigned char cNation) { return m_wCharacterNum[cNation]; }
unsigned short GetCharacterNum(void) { return static_cast<unsigned short>(m_CharacterMap.size()); }
unsigned short GetMonsterNum(void) { return static_cast<unsigned short>(m_MonsterMap.size()); }
unsigned short GetSiegeObjectNum(void) { return static_cast<unsigned short>(m_SiegeObjectMap.size()); }
unsigned short GetAvailableMonsterUID(unsigned short wKindID); // 몬스터를 생성할때 CID가 겹치지 않게 하기 위해서 호출하는 함수
bool IsSummonee(unsigned long dwCID);
Item::CShopContainer* GetShopContainer(unsigned long dwCID);
void SendAllCharacter(const char* szBuffer, unsigned long dwLength,
unsigned char cCMD_In, bool bSendAllMap = true);
void SendFindPartyMsg(unsigned long dwSenderCID, const char* szCompressedPacket, unsigned long dwPacketSize);
// bAll 이 true 이면 모두,
// false 이면 MapIndex 가 0 인 Character 들만...
bool ChangeCharacterName(unsigned long dwCID, const char* szChangedName);
// 길드전과 국가전 전쟁 시간시에 존이동 처리 함수
void MoveCharacterToGuildWarZone(unsigned char cType, bool bOn = true);
void MoveCharacterToRealmWarZone(unsigned char cType, bool bOn = true);
// 길드전과 국가전 전쟁이 끝난후 플래그 끄기
void ClearGuildWarInstanceFlag();
void ClearRealmWarInstanceFlag();
// --------------------------------------------------------------------------------------------
// 국가 전쟁 석상 관련
public:
// 국가전 석상 초기값 셋팅 및 상태 초기화 함수
void SetRealmStatueInfo(unsigned long dwIndex, unsigned short wKID, unsigned long dwLostHP);
void InitRealmStatue();
void InitRealmLoadingStatue();
bool SendRealmStatueDisplayInfo(CSendStream& SendStream);
bool SendRealmStatueDisplayInfo(unsigned long dwIndex = 0, bool bAttacked = false);
void CalculateRealmStatueEnchantInfo();
void AddRealmStatueEnchant(CCharacter* lpCharacter);
void AddRealmStatueEnchant();
void ClearRealmStatueEnchant();
private:
int GetRealmStatueNum();
struct sRealmEnchant
{
unsigned char m_cHPPercent;
unsigned char m_cMPPercent;
unsigned char m_cExpPercent;
unsigned char m_cDropRate;
};
sRealmEnchant m_RealmEnchantInfo[CClass::MAX_RACE];
// --------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------
// 공성전 보상 관련
private:
void AddCastleBonus();
// --------------------------------------------------------------------------------------------
private:
CCreatureManager();
~CCreatureManager();
NPCMap m_NPCMap;
MonsterMap m_MonsterMap;
AdminMonsterUIDMap m_AdminMonsterUIDMap;
CharacterMap m_CharacterMap;
CharacterMultimap m_CharacterNameMap;
// CSiegeObjectMgr 가 대부분의 관리를 하고,
// CCreatureMgr 은 리스트만 가지고 있음
SiegeObjectMap m_SiegeObjectMap;
CharacterList m_LogoutWaitList;
boost::pool<> m_CharacterPool;
// --------------------------------------------------------------------------------------------
// 엘리트 보너스 관련
public:
enum FightResult
{
WIN = 0,
LOSE = 1
};
void CalculateEliteBonus(unsigned short *usPeopleNum);
void SetEliteBonus(EliteBonus::EliteBonusData eliteBonus);
const EliteBonus::EliteBonusData& GetEliteBonus(void) { return m_EliteBonus; }
void SetAutoBalance(bool bAutoBalance) { m_bAutoBalance = bAutoBalance; }
bool GetAutoBalance(void) { return m_bAutoBalance; }
float GetFameBonus(CClass::RaceType eRace, FightResult eResult);
private:
EliteBonus::EliteBonusData m_EliteBonus;
bool m_bAutoBalance;
// --------------------------------------------------------------------------------------------
// 월드 웨폰 관련
public:
void AddWorldWeaponEnchant(CAggresiveCreature* lpWeapon, unsigned char cNation);
void ClearWorldWeaponEnchant();
void ProcessWorldWeaponDamage(int iX, int iZ, unsigned char cUpgradeStep);
// --------------------------------------------------------------------------------------------
// 배틀 그라운드 관련
public:
void PushRespawnQueue(CCharacter* lpCharacter, unsigned char cPointNumber);
void PopRespawnQueue(CCharacter* lpCharacter);
void ProcessRespawnQueue(void);
bool SendRespawnQueue(unsigned long dwCID);
unsigned char GetBonusTurn(const unsigned short wMapIndex);
void ProcessBattleGround();
enum StatueConst
{
STATUE_REST_TIME_1ST_START = 25, // 서버 시간으로 25분과 55분부터 5분간 석상이 없애고,
STATUE_REST_TIME_1ST_END = 29, // 모든 캐릭터들을 초렙존으로 리스폰 시킨다.
STATUE_REST_TIME_2ND_START = 55,
STATUE_REST_TIME_2ND_END = 59,
STATUE_RESPAWN_TIME_1ST = 0, // 서버 시간으로 0분과 30분에는 석상이 초기화 상태로 소환되어야 한다.
STATUE_RESPAWN_TIME_2ND = 30,
};
private:
bool m_bRest; // 석상이 하나도 없는 휴식시간인가?
private:
enum RespawnPoint
{
POINT_HUMAN1 = 0,
POINT_HUMAN2 = 1,
POINT_HUMAN3 = 2,
POINT_AKHAN1 = 3,
POINT_AKHAN2 = 4,
POINT_AKHAN3 = 5,
MAX_POINT_NUM = 6
};
struct BattleGroundRespawnInfo
{
CCharacter* m_lpCharacter;
Position m_RespawnPos;
long m_lLeftTime;
BattleGroundRespawnInfo()
: m_lpCharacter(NULL), m_lLeftTime(0)
{ }
BattleGroundRespawnInfo(CCharacter* lpCharacter, Position Pos, long lTime)
: m_lpCharacter(lpCharacter), m_RespawnPos(Pos), m_lLeftTime(lTime)
{ }
};
typedef std::list<BattleGroundRespawnInfo> RespawnQueue;
RespawnQueue m_lstRespawn[MAX_POINT_NUM];
Position m_RespawnPoint[MAX_POINT_NUM];
unsigned long m_dwLastUpdateTime;
unsigned short m_wCharacterNum[CClass::MAX_RACE];
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,536 @@
#ifndef _CREATURE_STRUCTURE_H_
#define _CREATURE_STRUCTURE_H_
#include <winsock2.h>
#include <windows.h>
#include <cmath>
#include <DB/DBDefine.h>
// 전방 참조
struct CreatureStatus;
struct CharacterStatus;
struct SKILL;
namespace Item
{
class CItem;
class CEquipment;
namespace EquipType
{
enum DoubleSwordType;
}
}
namespace BattleInclination
{
enum Const
{
IS_PEACEMODE = ( 1 << 0 ), // 평화모드인 경우 세팅
IS_SAFETY_ZONE = ( 1 << 1 ), // 세이프티 존 내부에 있는 경우 세팅
HAS_CAMP = ( 1 << 2 ), // 길드 요새를 가진 길드에 속해 있을 경우 세팅
HAS_DEVELOPING_CAMP = ( 1 << 3 ), // 건설중인 길드 요새를 가진 길드에 속해 있을 경우 세팅
IS_CASTLE_OBJECT = ( 1 << 4 ), // 공성 Object이면서 수성병기인 경우만 세팅
IS_CASTLE_NPC = ( 1 << 5 ), // 공성 Object이면서 수성 병기의 병기 관리 NPC 와 뒷문 NPC
IS_GOD_MODE = ( 1 << 6 ), // G모드인 경우 세팅
IS_TEAM_BATTLE = ( 1 << 7 ), // 팀배틀 중인 경우 세팅
};
enum VSType
{
VS_CHARACTER = ( 1 << 0 ), // PvP
VS_PARTY = ( 1 << 1 ), // 파티간 배틀
VS_GUILD = ( 1 << 2 ), // 길드간 배틀
};
// 피아식별시 주체가 되는 캐릭터 정보
// 여기서 데이터 추가하거나 삭제하는 경우,
// 브로드캐스팅 시, 채팅서버로 데이터 전송하는 부분도 수정해야한다.
struct CharData
{
bool IsPeaceMode(void) const { return 0 != (m_cFlags & IS_PEACEMODE); } // 평화모드인 경우
bool IsSafetyZone(void) const { return 0 != (m_cFlags & IS_SAFETY_ZONE); } // 내가 Safety존 내부에 있는지
bool HasCamp(void) const { return 0 != (m_cFlags & HAS_CAMP); } // 속한 길드가 길드 요새를 가지고 있는지...
bool HasDevelopingCamp(void) const { return 0 != (m_cFlags & HAS_DEVELOPING_CAMP); } // 속한 길드가 길드 요새를 건설중인지...
bool IsCastleObject(void) const { return 0 != (m_cFlags & IS_CASTLE_OBJECT); } // 공성병기인 경우, 수성병기인지...
bool IsCastleNPC(void) const { return 0 != (m_cFlags & IS_CASTLE_NPC); } // 수성 NPC 인 경우
bool IsGODMode(void) const { return 0 != (m_cFlags & IS_GOD_MODE); } // G모드인 경우
bool IsTeamBattle(void) const { return 0 != (m_cFlags & IS_TEAM_BATTLE); } // 팀배틀 중인 경우
void SetPeaceMode(bool bPeaceMode) { m_cFlags |= bPeaceMode ? IS_PEACEMODE : 0; }
void SetSafetyZone(bool bInSafetyZone) { m_cFlags |= bInSafetyZone ? IS_SAFETY_ZONE : 0; }
void SetHasCamp(bool bHasCamp) { m_cFlags |= bHasCamp ? HAS_CAMP : 0; }
void SetHasDevelopingCamp(bool bHasDevelopingCamp) { m_cFlags |= bHasDevelopingCamp ? HAS_DEVELOPING_CAMP : 0; }
void SetCastleObject(bool bCastleObject) { m_cFlags |= bCastleObject ? IS_CASTLE_OBJECT : 0; }
void SetCastleNPC(bool bCastleNPC) { m_cFlags |= bCastleNPC ? IS_CASTLE_NPC : 0; }
void SetGODMode(bool bGODMode) { m_cFlags |= bGODMode ? IS_GOD_MODE : 0; }
void SetTeamBattle(bool bTeamBattle) { m_cFlags |= bTeamBattle ? IS_TEAM_BATTLE : 0; }
unsigned long m_dwCID; // 캐릭터 CID
unsigned long m_dwGID; // 캐릭터 GID
unsigned long m_dwCastleID; // 공성 병기인 경우, 속한 성의 ID를 넣는다.
unsigned char m_cSiegeState; // 공성 병기인 경우, 공성 병기 상태를 넣는다.
unsigned char m_cNation; // 캐릭터 국적
unsigned char m_cAdminLevel; // 운영자 레벨
unsigned char m_cFlags; // 기타 정보
unsigned long m_dwPID; // 캐릭터에 파티 정보
unsigned char m_cTacticsFlag; // 캐릭터에 용병 정보
unsigned char m_cRealmWarFlag;
unsigned char m_cGuildWarFlag;
unsigned char m_cCreatureType; // 크리쳐 타입
unsigned short m_wObjectType; // 스트럭쳐 정보
unsigned short m_wKind; // 스트럭쳐 종류
unsigned char m_cRide; // 병기에 탑승 여부.
};
// 피아식별시 쓰이는 캐릭터간의 관계 (일단은 채팅 서버까진 갈 필요 없는 정보로 구성)
struct RelationData
{
unsigned char m_cVSFlag; // PvP, 팀배틀 정보
bool m_bHostileGuild; // 적대 길드인가?
};
};
namespace Creature
{
enum Const
{
PC_MAX_LEVEL = 100, // 캐릭터 최대 레벨
MONSTER_MAX_LEVEL = 150, // 몬스터 최대 레벨
// edith 2008.02.27 길드가입 레벨 제한 제거
LEVEL_ABLE_WAR = 1 // 전쟁이 가능한 최소 레벨 (길드 가입/전쟁 모드 제한)
};
enum IdentifyCIDBit
{
MONSTER_KIND_BIT = 0x0000FFFF,
MONSTER_BIT = 0x80000000,
NPC_BIT = 0x40000000,
SIEGE_OBJECT_BIT = 0x10000000,
SUMMON_MONSTER_BIT = 0xA0000000, // MONSTER_BIT (0x80000000) + 0x20000000
NPC_LINK_BIT = 0x50000000, // 소환 완료된 석상의 경우 이 비트가 세트된 NPC와 링크되어 메달상점의 역할을 한다.
// NPC_BIT (0x40000000) + 0x10000000
// 석상 KID (MonsterStructure.h 에도 정의되어 있음)
MIN_STATUE_KID = 4001,
MAX_STATUE_KID = 4070
};
enum CreatureType
{
CT_PC = 0,
CT_NPC = 1,
CT_MONSTER = 2,
CT_SUMMON = 3,
CT_STRUCT = 4,
CT_SIEGE_OBJECT = 5,
CT_NONE_TYPE = 99
};
enum Nation
{
STATELESS = 0, // 무국적 (몬스터, NPC)
KARTERANT = 1, // 카르테란트
MERKADIA = 2, // 메르카디아
ALMIGHTY_PIRATE = 3, // 신의해적단
MAX_NATION = 4
};
enum WarFlag
{
WAR_OFF = 0, // 전쟁 참여 안함
WAR_ON = 1, // 전쟁 참여
WAR_INSTANCE = 2 // 인스턴스 전쟁 참여
};
enum StatusType
{
RIGHT_PASSIVE_TYPE = 0, // 패시브 스킬이 적용된 오른손 장비 스탯 (기본값으로 CreatureStatus에 바로 사용된다.)
LEFT_PASSIVE_TYPE = 1, // 패시브 스킬이 적용된 왼손 장비 스탯 (이도류)
RIGHT_NON_PASSIVE_TYPE = 2, // 패시브 스킬이 적용되지 않은 오른손 장비 스탯 (오른손 무기를 사용하는 스킬)
LEFT_NON_PASSIVE_TYPE = 3, // 패시브 스킬이 적용되지 않은 왼손 장비 스탯 (왼손 무기(스킬암)를 사용하는 스킬)
MAX_STATUS_TYPE = 4
};
// 서버에서 사용하는 Creature 판별 함수들
// 클라이언트에서는 CRYLGameData::GetClientIdentity() 함수 사용!!
static bool IsSummonMonster(unsigned long dwCID)
{
return (Creature::SUMMON_MONSTER_BIT == (dwCID & Creature::SUMMON_MONSTER_BIT));
}
static bool IsStruct(unsigned long dwCID)
{
unsigned short wKID = static_cast<unsigned short>(dwCID & Creature::MONSTER_KIND_BIT);
return (wKID >= MIN_STATUE_KID && wKID <= MAX_STATUE_KID);
}
static bool IsLinkedNPC(unsigned long dwCID)
{
return (Creature::NPC_LINK_BIT == (dwCID & Creature::NPC_LINK_BIT));
}
static CreatureType GetCreatureType(unsigned long dwCID)
{
if (dwCID & Creature::MONSTER_BIT)
{
if (IsSummonMonster(dwCID)) return Creature::CT_SUMMON;
else if (IsStruct(dwCID)) return Creature::CT_STRUCT;
return Creature::CT_MONSTER;
}
else if (dwCID & Creature::NPC_BIT) return Creature::CT_NPC;
else if (dwCID & Creature::SIEGE_OBJECT_BIT) return Creature::CT_SIEGE_OBJECT;
return Creature::CT_PC;
}
inline static const char* GetNationName(unsigned char cNation)
{
switch(cNation)
{
case KARTERANT: return "KARTERANT";
case MERKADIA: return "MERKADIA";
case ALMIGHTY_PIRATE: return "ALMIGHTY_PIRATE";
}
return "STATELESS";
}
inline static const char* GetShortNationName(unsigned char cNation)
{
switch(cNation)
{
case KARTERANT: return "KR";
case MERKADIA: return "ME";
case ALMIGHTY_PIRATE: return "AP";
}
return "ST";
}
};
// 엘리트 보너스 정보
namespace EliteBonus
{
enum Const
{
MAX_BONUS_LEVEL = 10,
MAX_BONUS_DROP_ITEM = 3
};
// 엘리트 보너스에 의한 경험치/드랍률 보너스가 제거되었습니다. (2004-09-17 by 로딘)
static short usBonusExp[MAX_BONUS_LEVEL] = {
100 + 0,
100 + 0,
100 + 0,
100 + 0,
100 + 0,
100 + 0,
100 + 0,
100 + 0,
100 + 0,
100 + 0
};
static unsigned char usBonusItemDropRate[MAX_BONUS_LEVEL][MAX_BONUS_DROP_ITEM] = {
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 }
};
struct EliteBonusData
{
char m_cNation;
unsigned char m_cLevel;
EliteBonusData();
EliteBonusData(char cNation, unsigned char cLevel);
};
}
// 맵 이동 정보
struct Position
{
float m_fPointX; // Point X 좌표
float m_fPointY; // Point Y 좌표
float m_fPointZ; // Point Z 좌표
inline Position();
inline Position(const POS& Pos);
inline Position(const Position& Pos);
inline Position(float fPointX, float fPointY, float fPointZ);
inline POS PositionToPOS() const;
inline unsigned long GetDistance(const Position& rhs) const
{
return static_cast<unsigned long>(sqrt(
(m_fPointX - rhs.m_fPointX) * (m_fPointX - rhs.m_fPointX) +
(m_fPointY - rhs.m_fPointY) * (m_fPointY - rhs.m_fPointY) +
(m_fPointZ - rhs.m_fPointZ) * (m_fPointZ - rhs.m_fPointZ)));
}
inline unsigned long GetSquaredDistance(const Position& rhs) const
{
return static_cast<unsigned long>(
(m_fPointX - rhs.m_fPointX) * (m_fPointX - rhs.m_fPointX) +
(m_fPointY - rhs.m_fPointY) * (m_fPointY - rhs.m_fPointY) +
(m_fPointZ - rhs.m_fPointZ) * (m_fPointZ - rhs.m_fPointZ));
}
inline bool operator == (const Position& rhs) const
{
if ( m_fPointX == rhs.m_fPointX && m_fPointY == rhs.m_fPointY && m_fPointZ == rhs.m_fPointZ )
{
return true;
}
return false;
}
inline bool operator != (const Position& rhs) const
{
if ( m_fPointX != rhs.m_fPointX || m_fPointY != rhs.m_fPointY || m_fPointZ != rhs.m_fPointZ )
{
return true;
}
return false;
}
inline void operator () (const Position& rhs)
{
m_fPointX = rhs.m_fPointX;
m_fPointY = rhs.m_fPointY;
m_fPointZ = rhs.m_fPointZ;
}
};
inline Position::Position()
: m_fPointX(0.0f), m_fPointY(0.0f), m_fPointZ(0.0f) { }
inline Position::Position(const POS& Pos)
: m_fPointX(Pos.fPointX), m_fPointY(Pos.fPointY), m_fPointZ(Pos.fPointZ) { }
inline Position::Position(const Position& Pos)
: m_fPointX(Pos.m_fPointX), m_fPointY(Pos.m_fPointY), m_fPointZ(Pos.m_fPointZ) { }
inline Position::Position(float fPointX, float fPointY, float fPointZ)
: m_fPointX(fPointX), m_fPointY(fPointY), m_fPointZ(fPointZ) { }
inline POS Position::PositionToPOS() const
{
POS stPos;
stPos.fPointX = m_fPointX;
stPos.fPointY = m_fPointY;
stPos.fPointZ = m_fPointZ;
return stPos;
}
// 동작 정보
struct MotionInfo
{
float m_fDirection; // 바라보는 방향
float m_fVelocity; // 속도
unsigned short m_wAction; // 취하는 행동
unsigned long m_dwFrame; // 모션 프레임 ( 몬스터, NPC가 사용 )
MotionInfo();
};
// 인챈트 정보
struct EnchantInfo
{
enum
{
ULONG_BIT = 32, // unsigned long (32바이트)
MAX_ARRAY = 4 // 스펠의 종류는 UCHAR_BIT * MAX_ARRAY
};
unsigned long m_dwStatusFlag[MAX_ARRAY]; // 챈트/인챈트 플래그
EnchantInfo();
EnchantInfo( const EnchantInfo& Info ) // 대입을 위한 복사 생성자
{
for( int i = 0 ; i < MAX_ARRAY ; ++i )
{
m_dwStatusFlag[ i ] = Info.m_dwStatusFlag[ i ];
}
}
void SetFlag(unsigned char cSpellKind);
void ResetFlag(unsigned char cSpellKind);
void ClearFlag(void);
bool GetFlag(unsigned char cSpellKind) const;
};
// 데미지 계산시의 추가 효과 정보
struct CalculateDamageInfo
{
bool m_bForceDRC; // '참'이면 기본 DRC를 무시하고 추가 DRC로 대체
float m_fDRC; // 추가 DRC
short m_nOffenceRevision; // 추가 공격보정
short m_nMinDamage; // 추가 최소데미지
short m_nMaxDamage; // 추가 최대데미지
CalculateDamageInfo()
: m_bForceDRC(false), m_fDRC(0.0f), m_nOffenceRevision(0), m_nMinDamage(0), m_nMaxDamage(0)
{ }
CalculateDamageInfo(bool bForceDRC, float fDRC, short nOffenceRevision, short nMinDamage, short nMaxDamage)
: m_bForceDRC(bForceDRC), m_fDRC(fDRC),
m_nOffenceRevision(nOffenceRevision), m_nMinDamage(nMinDamage), m_nMaxDamage(nMaxDamage)
{ }
};
typedef CalculateDamageInfo* LPCalculateDamageInfo;
// 캐릭터에 대한 속성
struct CharacterStatus
{
short m_nSTR; // 캐릭터 STR
short m_nDEX; // 캐릭터 DEX
short m_nCON; // 캐릭터 CON
short m_nINT; // 캐릭터 INT
short m_nWIS; // 캐릭터 WIS
CharacterStatus();
CharacterStatus(CHAR_INFOST& characterDBData);
void Init(CHAR_INFOST& characterDBData);
};
typedef CharacterStatus* LPCharacterStatus;
struct FightStatus
{
// 계산 상태 (중복 계산 방지)
enum CalculateState
{
CS_NONE = 0, // 초기화
CS_BASE_INFO = 1, // 기본 스탯으로 계산을 완료한 상태
CS_EQUIP_INFO = 2, // 장비 효과를 추가 계산한 상태
CS_ENCHANT_INFO = 3 // 인챈트 효과를 받은 상태
};
enum Const
{
MAX_INCHANT_HP = 15000,
MAX_INCHANT_MP = 15000
};
long m_lMinDamage; // 최소데미지
long m_lMaxDamage; // 최대데미지
short m_wArmor; // 방어력
short m_wHitRate; // 명중
short m_wEvade; // 회피
unsigned short m_nMaxHP; // HP 최대값
short m_wHPRegen; // HP 회복
unsigned short m_nMaxMP; // MP 최대값
short m_wMPRegen; // MP 회복
short m_wCritical; // 크리티컬
short m_wBlock; // 블록
short m_wSpeed; // 속도
short m_wCoolDownRevision; // 쿨다운보정
short m_wSkillPoint; // 스킬포인트
short m_wMagicPower; // 마법력
short m_wMagicResist; // 마법저항
int m_wLuck; // 행운값
//--// start..
short m_wFrost; // 냉기/정지 속성
short m_wFire; // 불/독 속성
short m_wElectro; // 전지/충격 속성
short m_wDarkness; // 암흑/빛 속성
//--// end..
float m_fDefenceRate; // 방어%
float m_fCriticalMagnification; // 크리티컬배율
float m_fCriticalRate; // 크리티컬%
float m_fBlockRate; // 블럭률%
float m_fSpeedRate; // 속도%
float m_fCoolDownRevisionRate; // 쿨다운보정%
float m_fMagicResistRate; // 마법저항%
float m_fLuckResistRate; // 럭키찬스%
float m_fLevelExplosion; // 레벨 익스플로젼
unsigned char m_cCriticalType; // 크리티컬 타입
unsigned char m_cComboCount; // 공격 콤보 횟수
unsigned char m_cCalculateState; // 계산 상태
FightStatus();
bool CalculateBaseInfo(const CharacterStatus& characterStatus, const unsigned short wLevel, const unsigned char cClass);
bool CalculateEquipInfo(const Item::CEquipment** ppEquip, const bool bFirstWeaponSlot, const bool bRightHand, const bool bRideFlag,
const SKILL& skill, const unsigned char cLevel, const unsigned short wRiderAbility, const unsigned short wLuckAbility, const unsigned short wLevelAbility,
const CharacterStatus& characterStatus, const unsigned char cClass, FightStatus& resultStatus) const;
bool CalculateEnchantInfo(const unsigned short* aryEnchantLevel, FightStatus& resultStatus) const;
void CalculateSubStatus(void);
static bool CheckEquipable(const Item::CEquipment* lpEquip, const CharacterStatus& characterStatus,
const unsigned char cClass, const unsigned char cLevel, const unsigned short wLevelAbility); //--//
};
#pragma pack(8)
//------------------------------------------------------------------------------------
// TODO : memory alignment 주의할 것.
// : 4byte단위로 alignment 가 되도록 할 것.
// : 메모리 부족시에는 크기가 많이 필요 없는 멤버는 short등으로 줄일 것.
// 무기나 도구에 의해서 영향을 받은 능력치를 저장 (게임상에서 모든 계산은 이것으로 함)
struct CreatureStatus
{
__int64 m_nExp; // 사람인 경우는 자신의 경험점, 몬스터인 경우는 나누어 줄 경험점
int m_nLevel; // 레벨
unsigned short m_nNowHP; // 현재 HP
unsigned short m_nNowMP; // 현재 MP (몬스터의 경우는 무조건 최대값으로 초기화)
FightStatus m_StatusInfo; // 대부분의 정보가 이곳에 있음
CreatureStatus();
CreatureStatus(CHAR_INFOST& characterDBData);
void Init(CHAR_INFOST& characterDBData);
};
typedef CreatureStatus* LPCreatureStatus;
#pragma pack()
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,268 @@
#ifndef _ENEMYCHECK_H_
#define _ENEMYCHECK_H_
#include <Pattern/Singleton.h>
#include "CreatureStructure.h"
// 적아군 식별 관련 (서버, 클라이언트 공용으로 사용)
namespace EnemyCheck
{
enum EnemyType
{
// 리턴 타입
EC_ENEMY = 0, // 적군
EC_NEUTRAL = 1, // 중립
EC_FRIEND = 2, // 아군
EC_ERROR = 3 // 에러
};
enum EnemyCheckType
{
// 체크 타입
EC_CHECK_ENEMY = 0, // 적식별
EC_CHECK_NEUTRAL_EXCETION = 1, // 중립 예외 처리
EC_CHECK_FRIEND = 2, // 아군 식별
EC_CHECK_FRIEND_2ND = 3, // 2차 아군 식별
MAX_CHECK_TYPE = 4 // 식별룰 체크 루틴 수
};
enum EnemyCheckRule
{
RULE_HUNTING = 0, // 헌팅룰
RULE_GUILD_WAR = 1, // 길드전룰
RULE_REALM_WAR = 2, // 렐름전룰
RULE_STATUE_WAR = 3, // 석상전룰
RULE_SIEGE_WAR = 4, // 공성전룰
MAX_RULE_NUM = 5
};
// -------------------------------------------------------------------------
// Siege 관련 함수
bool IsCamp(unsigned short wObjectType); // 길드 요새
bool IsCastleArms(unsigned short wObjectType); // 수성 병기
bool IsSiegeArms(unsigned short wObjectType); // 공성 병기
bool IsStruct(unsigned short wKind); // 스트럭쳐
bool IsGate(unsigned short wObjectType); // 성문
bool IsEmblem(unsigned short wObjectType); // 성 상징물
bool IsCastleNPC(unsigned short wObjectType); // 병기 관리 NPC
// -------------------------------------------------------------------------
// 양국 체제 이후 추가된 내용
typedef bool (*FnProcess)(BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo,
BattleInclination::RelationData& relationInfo, unsigned char& cResult);
typedef bool (*FnCheck)(BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo,
EnemyCheck::EnemyCheckRule eRule);
class CCheckTable : public CSingleton<CCheckTable>
{
public:
bool Initialize(void);
void SetEnemyCheckRule(EnemyCheckRule eRule) { m_eRule = eRule; }
EnemyCheckRule GetEnemyCheckRule(void) { return m_eRule; }
unsigned long IsEnemyFromStruct(BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo,
BattleInclination::RelationData& relationInfo, unsigned char& cResult);
private:
CCheckTable();
~CCheckTable();
unsigned long GetEnemyType(unsigned char cEnemyCheckType) const; // 체크 타입에 해당하는 적식별 결과 리턴
bool m_bInit; // 초기화 여부
EnemyCheckRule m_eRule; // 현재 적용 중인 피아식별 룰
FnProcess m_fnIsEnemy[MAX_CHECK_TYPE][MAX_RULE_NUM]; // 각 상황에 따른 피아식별 함수들의 테이블
FnCheck m_fnCheckSkip[MAX_CHECK_TYPE]; // 각 상황에 따른 피아식별을 스킵해야 하는지 체크하는 함수들의 테이블
static CCheckTable ms_this;
};
class CFunctions
{
private:
// 결과값에 의미를 둔다기보단, 숫자가 겹치는 걸 방지하기 위해 enum으로 설정
enum ResultType
{
// EC_CHECK_ENEMY 의 결과값
EN_COMMON_PVP = 1, // 명시적 PvP로 선언된 적 (듀얼)
EN_COMMON_STATELESS_MONSTER, // 무국적(제3국) 몬스터
EN_HUNTING_VSPARTY_VSGUILD, // 명시적 선언으로 선언된 그룹 덩어리 (파티/길드 듀얼)
EN_GUILDWAR_HOSTILITY_GUILD, // 자신과 대상이 길드전에 참여중이며 적대 관계 일때
EN_REALMWAR_ENEMY_PC_MONSTER_NPC, // 상대 국적의 PC, 몬스터, NPC
EN_STATUEWAR_ENEMY_PC_MONSTER_STRUCT, // 상대 국적의 PC, 몬스터, 스트럭처
EN_STATUEWAR_STATELESS_STRUCT, // 제 3국적 스트럭처
EN_SIEGE_ENEMY_PC_MONSTER, // 상대 국적의 PC, 몬스터
EN_SIEGE_ENEMY_GATE_EMBLEM_CASTLE_ARMS, // 상대 국적의 성문, 성 상징물, 수성 병기
EN_SIEGE_ENEMY_SIEGE_ARMS, // 상대 국적이 소환한 공성 병기.
// EC_CHECK_NEUTRAL_EXCETION 의 결과값
NE_COMMON_GMODE = 21, // 상대가 본인이 아니며 G모드일 경우 중립 처리
NE_COMMON_ENEMY_NPC, // 상대 국적의 NPC
NE_HUNTING_NON_BATTLER, // 전투에 참여하지 않은 모든 인원
NE_HUNTING_OTHER_BATTLER, // 자신과 다른 팀배틀에 참여중인 모든 인원
NE_HUNTING_BATTLER, // 팀배틀에 참여중인 모든 인원
NE_GUILDWAR_IN_SAFETYZONE, // 내가 세이프티 존에 있을 경우 나와 적대 관계인 모든 인원
NE_GUILDWAR_NON_BATTLER, // 길드전 참여중이지 않은 모든 인원
NE_GUILDWAR_OTHER_BATTLER, // 자신의 길드가 아닌 길드전쟁 참여자
NE_GUILDWAR_BATTLER, // 길드전 참여중인 모든 인원
NE_GUILDWAR_MYGUILD, // 자신의 길드원 전원
NE_REALMWAR_ENEMY_STRUCT, // 자신과 다른 국적의 석상 스트럭쳐
NE_REALMWAR_ENEMY_GATE_EMBLEM_SIEGEOBJECT, // 상대 국적의 성내 소환물, 성의 대문, 소환한 공성 병기
NE_STATUEWAR_ENEMY_GATE_EMBLEM_SIEGEOBJECT, // 상대 국적의 성내 소환물, 성의 대문, 소환한 공성 병기
NE_SIEGE_ENEMY_STRUCT, // 자신과 다른 국적의 석상 스트럭쳐
// EC_CHECK_FRIEND 의 결과값
FR_COMMON_MYPARTY = 41, // 명시적으로 선언된 같은 파티
FR_COMMON_MYGUILD, // 명시적으로 선언된 같은 길드
FR_COMMON_MYSELF, // 자기 자신
FR_GUILDWAR_MYGUILD, // 내가 참여한 길드 그룹
FR_REALMWAR_MYNATION_PC_NPC_MONSTER_STRUCT, // 자국 국적의 PC, NPC, 몬스터, 석상
FR_REALMWAR_MYNATION_GATE_EMBLEM_SIEGEOBJECT, // 자국 국적의 소환물, 성의 대문, 공성 병기.
FR_STATUEWAR_MYNATION_PC_NPC_MONSTER_STRUCT, // 자국 국적의 PC, NPC, 몬스터, 석상
FR_STATUEWAR_MYNATION_GATE_EMBLEM_SIEGEOBJECT, // 자국 국적의 소환물, 성의 대문, 공성 병기.
FR_SIEGEWAR_MYNATION_PC_NPC_MONSTER_STRUCT, // 자국 국적의 PC, NPC, 몬스터, 석상
FR_SIEGEWAR_MYNATION_SIEGEOBJECT, // 자국 국적의 소환물, 성의 대문, 수성병기, 공성 병기.
// EC_CHECK_FRIEND_2ND 의 결과값
FR2_COMMON_MYNATION_PC_NPC = 61, // 자국 PC, NPC
FR2_COMMON_STATELESS_NPC, // 무국적 NPC
};
// 공격 가능 여부 체크를 위한 크리쳐 타입
enum AttackableCreatureType
{
ACT_PC = 0, // PC
ACT_MONSTER = 1, // 몬스터
ACT_EMBLEM = 2, // 성 상징물
ACT_GATE = 3, // 성문
ACT_CAMP = 4, // 길드 요새
ACT_GUARD = 5, // 가드
ACT_SHORT_RANGE_CASTLE_ARMS = 6, // 근거리 수성 병기
ACT_LONG_RANGE_CASTLE_ARMS = 7, // 원거리 수성 병기
ACT_SHORT_RANGE_SIEGE_ARMS = 8, // 근거리 공성 병기
ACT_LONG_RANGE_SIEGE_ARMS = 9, // 원거리 공성 병기
ACT_AIRSHIP = 10, // 수송선
ACT_NONE = 11, // 뒷문, NPC, 병기관리NPC
MAX_ATTACKABLE_CREATURE = 12, // 공성 시간 및 일반 시간에 공격가능한 대상 수
};
// 공격 가능 여부 리턴값
enum ReturnACT
{
RET_NONE = 0, // 공격 불가능
RET_ALLWAYS = 1, // 항상 공격 가능
RET_DEVELOPING = 2, // 개발 중일때만 공격 가능
};
const static unsigned char ms_AttackableTable[MAX_ATTACKABLE_CREATURE][MAX_ATTACKABLE_CREATURE]; // 각 크리쳐 타입에 따른 공격 가능 여부 테이블
friend class CCheckTable;
// 피아식별 함수 정의 매크로
#define DECLARE_ENEMY_CKECK(CheckRule) \
static bool CheckRule(BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo, \
BattleInclination::RelationData& relationInfo, unsigned char& cResult)
#define DEFINE_ENEMY_CKECK(CheckRule) \
bool EnemyCheck::CFunctions::##CheckRule( \
BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo, \
BattleInclination::RelationData& relationInfo, unsigned char& cResult)
#define USE_ENEMY_CKECK(CheckRule) CheckRule(ownerInfo, targetInfo, relationInfo, cResult)
// 피아식별 루틴 스킵 체크 함수 정의 매크로
#define DECLARE_SKIP_CKECK(CheckType) \
static bool CheckType(BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo, \
EnemyCheck::EnemyCheckRule eRule)
#define DEFINE_SKIP_CHECK(CheckType) \
bool EnemyCheck::CFunctions::##CheckType( \
BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo, \
EnemyCheck::EnemyCheckRule eRule)
// 공격 가능 여부 체크 함수 정의 매크로
#define DECLARE_ATTACKABLE_CHECK(FuncName) \
static bool FuncName(BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo)
#define DEFINE_ATTACKABLE_CHECK(FuncName) \
bool EnemyCheck::CFunctions::##FuncName( \
BattleInclination::CharData& ownerInfo, BattleInclination::CharData& targetInfo)
// 공통
DECLARE_ENEMY_CKECK(IsEnemyCommon);
DECLARE_ENEMY_CKECK(IsNeutralExceptionCommon);
DECLARE_ENEMY_CKECK(IsFriendCommon);
DECLARE_ENEMY_CKECK(IsFriend2ndCommon);
// 헌팅룰
DECLARE_ENEMY_CKECK(IsEnemyForHunting);
DECLARE_ENEMY_CKECK(IsNeutralExceptionForHunting);
DECLARE_ENEMY_CKECK(IsFriendForHunting);
DECLARE_ENEMY_CKECK(IsFriend2ndForHunting);
// 길드전룰
DECLARE_ENEMY_CKECK(IsEnemyForGuildWar);
DECLARE_ENEMY_CKECK(IsNeutralExceptionForGuildWar);
DECLARE_ENEMY_CKECK(IsFriendForGuildWar);
DECLARE_ENEMY_CKECK(IsFriend2ndForGuildWar);
// 렐름전룰
DECLARE_ENEMY_CKECK(IsEnemyForRealmWar);
DECLARE_ENEMY_CKECK(IsNeutralExceptionForRealmWar);
DECLARE_ENEMY_CKECK(IsFriendForRealmWar);
DECLARE_ENEMY_CKECK(IsFriend2ndForRealmWar);
// 석상전룰
DECLARE_ENEMY_CKECK(IsEnemyForStatueWar);
DECLARE_ENEMY_CKECK(IsNeutralExceptionForStatueWar);
DECLARE_ENEMY_CKECK(IsFriendForStatueWar);
DECLARE_ENEMY_CKECK(IsFriend2ndForStatueWar);
// 공성전룰
DECLARE_ENEMY_CKECK(IsEnemyForSiegeWar);
DECLARE_ENEMY_CKECK(IsNeutralExceptionForSiegeWar);
DECLARE_ENEMY_CKECK(IsFriendForSiegeWar);
DECLARE_ENEMY_CKECK(IsFriend2ndForSiegeWar);
// 피아식별 스킵 체크
DECLARE_SKIP_CKECK(IsEnemySkip);
DECLARE_SKIP_CKECK(IsNeutralExceptionSkip);
DECLARE_SKIP_CKECK(IsFriendSkip);
DECLARE_SKIP_CKECK(IsFriend2ndSkip);
// 공격 가능 여부 체크 함수
DECLARE_ATTACKABLE_CHECK(IsAttackable);
static unsigned char FindAttackableCreatureType(BattleInclination::CharData& charInfo);
};
}
#endif

View File

@@ -0,0 +1,676 @@
#include "stdafx.h"
#include <Utility/Math/Math.h>
#include <Utility/Setup/ServerSetup.h>
#include <Item/Item.h>
#include <Item/ItemMgr.h>
#include <Item/ItemFactory.h>
#include <Creature/CreatureManager.h>
#include <Creature/Character/Character.h>
#include <GameEvent/GameEventMgr.h>
#include "Monster.h"
#include "AwardTable.h"
using namespace AwardTable;
CAward CAward::ms_this;
unsigned long CAward::GetAward(unsigned char cItemKind, CMonster* lpDeadMonster, CCharacter* lpAttackCharacter,
Item::CItem** lpItem_Out)
{
if (NULL == lpDeadMonster || NULL == lpAttackCharacter)
{
ERRLOG2(g_Log, "CAward::GetAward 포인터 오류입니다. Monster:0x%p, Character:0x%p", lpDeadMonster, lpAttackCharacter);
return 0;
}
// edith 드랍 아이템의 속성별 확율
// 이벤트 아이템이 드랍될 타이밍인지 체크한다.
unsigned long dwResultItem = CGameEventMgr::GetInstance().PopDropEventItem();
if (0 == dwResultItem)
{
const unsigned char cLevel = lpDeadMonster->GetStatus().m_nLevel;
const unsigned int iPattern = lpDeadMonster->GetPattern();
using namespace Item::EquipType;
OptionType eOptionType = MAX_OPTION_TYPE;
Grade eGrade = MAX_GRADE;
float fAddPer = 1.0f;
switch (cItemKind)
{
case MonsterInfo::ORIGINAL1:
case MonsterInfo::ORIGINAL2:
case MonsterInfo::ORIGINAL3:
case MonsterInfo::ORIGINAL4:
case MonsterInfo::ORIGINAL5:
dwResultItem = lpDeadMonster->GetAwardItem(cItemKind);
break;
case MonsterInfo::COIN:
// 골드에 스틸핸드가 미치는 영향
if(lpAttackCharacter->GetEnchantInfo().GetFlag(Skill::SpellID::StealHand))
{
// 배수로 확정한다. 1이면
int iLevel = lpAttackCharacter->GetEnchantLevel(Skill::SpellID::StealHand);
// 기본 1.0
fAddPer += ((float)iLevel/10.0f);
}
if( lpAttackCharacter->GetAbilityValue(Skill::Type::AB_GETUP_GOLD) )
{
fAddPer += ((float)lpAttackCharacter->GetAbilityValue(Skill::Type::AB_GETUP_GOLD)/100.0f);
}
dwResultItem = GetAwardCoin(cLevel, fAddPer);
break;
case MonsterInfo::GEM: dwResultItem = GetAwardGem(cLevel); break;
case MonsterInfo::METAL: dwResultItem = GetAwardMetal(cLevel); break;
case MonsterInfo::POTION: dwResultItem = GetAwardPotion(cLevel); break;
case MonsterInfo::SKILL:
dwResultItem = GetAwardSkill(cLevel, lpAttackCharacter->GetRace());
break;
case MonsterInfo::CASH_ITEM: dwResultItem = GetAwardCashItem(lpAttackCharacter->GetRace(), cLevel, iPattern); break;
case MonsterInfo::LOTTERY: dwResultItem = GetAwardLottery(); break;
case MonsterInfo::F_STANDARD_EQUIP: eOptionType = STANDARD_OPTION; eGrade = F_GRADE; break;
case MonsterInfo::D_STANDARD_EQUIP: eOptionType = STANDARD_OPTION; eGrade = D_GRADE; break;
case MonsterInfo::C_STANDARD_EQUIP: eOptionType = STANDARD_OPTION; eGrade = C_GRADE; break;
case MonsterInfo::B_STANDARD_EQUIP: eOptionType = STANDARD_OPTION; eGrade = B_GRADE; break;
case MonsterInfo::A_STANDARD_EQUIP: eOptionType = STANDARD_OPTION; eGrade = A_GRADE; break;
case MonsterInfo::F_OVER_EQUIP: eOptionType = OVER_OPTION; eGrade = F_GRADE; break;
case MonsterInfo::D_OVER_EQUIP: eOptionType = OVER_OPTION; eGrade = D_GRADE; break;
case MonsterInfo::C_OVER_EQUIP: eOptionType = OVER_OPTION; eGrade = C_GRADE; break;
case MonsterInfo::B_OVER_EQUIP: eOptionType = OVER_OPTION; eGrade = B_GRADE; break;
case MonsterInfo::A_OVER_EQUIP: eOptionType = OVER_OPTION; eGrade = A_GRADE; break;
case MonsterInfo::NO_EQUIP: eOptionType = NO_OPTION; break;
//--// start..
case MonsterInfo::F_RING: dwResultItem = GetAwardRing(cLevel, F_GRADE); break;
case MonsterInfo::D_RING: dwResultItem = GetAwardRing(cLevel, D_GRADE); break;
case MonsterInfo::C_RING: dwResultItem = GetAwardRing(cLevel, C_GRADE); break;
case MonsterInfo::B_RING: dwResultItem = GetAwardRing(cLevel, B_GRADE); break;
case MonsterInfo::A_RING: dwResultItem = GetAwardRing(cLevel, A_GRADE); break;
case MonsterInfo::F_NECKLACE: dwResultItem = GetAwardNecklace(cLevel, F_GRADE); break;
case MonsterInfo::D_NECKLACE: dwResultItem = GetAwardNecklace(cLevel, D_GRADE); break;
case MonsterInfo::C_NECKLACE: dwResultItem = GetAwardNecklace(cLevel, C_GRADE); break;
case MonsterInfo::B_NECKLACE: dwResultItem = GetAwardNecklace(cLevel, B_GRADE); break;
case MonsterInfo::A_NECKLACE: dwResultItem = GetAwardNecklace(cLevel, A_GRADE); break;
case MonsterInfo::F_RUNE: dwResultItem = GetAwardRune(cLevel, F_GRADE); break;
case MonsterInfo::D_RUNE: dwResultItem = GetAwardRune(cLevel, D_GRADE); break;
case MonsterInfo::C_RUNE: dwResultItem = GetAwardRune(cLevel, C_GRADE); break;
case MonsterInfo::B_RUNE: dwResultItem = GetAwardRune(cLevel, B_GRADE); break;
case MonsterInfo::A_RUNE: dwResultItem = GetAwardRune(cLevel, A_GRADE); break;
case MonsterInfo::DESTRUCTION_RUNE: dwResultItem = GetAwardDestructionRune(); break;
//--// end..
default: return 0;
}
Item::CItemFactory& ItemFactory = Item::CItemFactory::GetInstance();
if (MAX_OPTION_TYPE != eOptionType)
{
// 장비의 경우
dwResultItem = GetAwardEquipment(eOptionType, cLevel, lpAttackCharacter, false);
*lpItem_Out = ItemFactory.CreateItem(static_cast<unsigned short>(dwResultItem));
Item::CEquipment* lpEquip = Item::CEquipment::DowncastToEquipment(*lpItem_Out);
if (NULL != lpEquip)
{
// edith 2008.01.14 장비에 옵션을 랜덤으로 건다.(설명)
if(lpEquip->AddRandomOption(eGrade, lpDeadMonster->GetOptionBaseNum(eOptionType), lpAttackCharacter->GetMagicChancePoint()))
lpEquip->SetNewEquip(2);
else
lpEquip->SetNewEquip();
}
}
else
{
if (!(dwResultItem & CCell::MONEY_BIT))
{
// 돈이 아닌 기타 잡템
*lpItem_Out = ItemFactory.CreateItem(static_cast<unsigned short>(dwResultItem));
if (NULL != *lpItem_Out)
{
(*lpItem_Out)->SetNumOrDurability(1);
}
}
}
}
return dwResultItem;
}
unsigned long CAward::GetAwardCoin(unsigned char cDeadMonsterLevel, float cAddPer)
{
// edith 2009.06.13 골드 드랍양 조절
// const unsigned long dwMinMoney = static_cast<unsigned long>(cDeadMonsterLevel) * 2;
// const unsigned long dwMaxMoney = static_cast<unsigned long>(cDeadMonsterLevel) * 4 + 10;
const unsigned long dwMinMoney = static_cast<unsigned long>(cDeadMonsterLevel) * 3 + 6;
const unsigned long dwMaxMoney = static_cast<unsigned long>(cDeadMonsterLevel) * 6 + 12;
unsigned long dwMoneyAmount =
static_cast<unsigned long>( min(Math::Random::ComplexRandom(dwMaxMoney + 1, dwMinMoney) * (CServerSetup::GetInstance().GetDropDefault() / 100.0f) * cAddPer,
float(CCell::MAX_MONEY_AMOUNT)) );
dwMoneyAmount = min(dwMoneyAmount, ULONG_MAX & ~CCell::TYPE_CHECK_BIT);
return (CCell::MONEY_BIT + dwMoneyAmount);
}
unsigned long CAward::GetAwardGem(unsigned char cDeadMonsterLevel)
{
cDeadMonsterLevel = std::min(cDeadMonsterLevel, unsigned char(MAX_MONSTER_LEVEL));
const int nPoint = Math::Random::ComplexRandom(std::accumulate(&(aryGemDropTable[(cDeadMonsterLevel - 1) / 10][0]),
&(aryGemDropTable[(cDeadMonsterLevel - 1) / 10][GEM_DROP_TABLE_ROW]), 0));
int nSpace = 0;
for (int nAwardTableIndex = 0; nAwardTableIndex < GEM_DROP_TABLE_ROW; ++nAwardTableIndex)
{
nSpace += aryGemDropTable[(cDeadMonsterLevel - 1) / 10][nAwardTableIndex];
if (nSpace > nPoint)
{
return (Item::EtcItemID::GEM_START_ID +
Math::Random::ComplexRandom(Item::MAX_GEM_KIND) + (nAwardTableIndex * Item::MAX_GEM_KIND));
}
}
return 0;
}
//--// start..
unsigned long CAward::GetAwardRing(unsigned char cLevel, unsigned char cGrade)
{
return Item::CItemMgr::GetInstance().GetRingDropItem(cLevel, cGrade);
}
unsigned long CAward::GetAwardNecklace(unsigned char cLevel, unsigned char cGrade)
{
return Item::CItemMgr::GetInstance().GetNecklaceDropItem(cLevel, cGrade);
}
unsigned long CAward::GetAwardRune(unsigned char cLevel, unsigned char cGrade)
{
return Item::CItemMgr::GetInstance().GetRuneDropItem(cLevel, cGrade);
}
unsigned long CAward::GetAwardDestructionRune()
{
return Item::EtcItemID::DESTRUCTION_RUNE;
}
//--// end..
unsigned long CAward::GetAwardMetal(unsigned char cDeadMonsterLevel)
{
cDeadMonsterLevel = std::min(cDeadMonsterLevel, unsigned char(MAX_MONSTER_LEVEL));
const int nPoint = Math::Random::ComplexRandom(std::accumulate(&(aryMetalDropTable[(cDeadMonsterLevel - 1) / 10][0]),
&(aryMetalDropTable[(cDeadMonsterLevel - 1) / 10][METAL_DROP_TABLE_ROW]), 0));
int nSpace = 0;
for (int nAwardTableIndex = 0; nAwardTableIndex < METAL_DROP_TABLE_ROW; ++nAwardTableIndex)
{
nSpace += aryMetalDropTable[(cDeadMonsterLevel - 1) / 10][nAwardTableIndex];
if (nSpace > nPoint)
{
// 코어시스템
if(nAwardTableIndex == 6)
{
// edith 2009.09.17 코어시스템
// 현재로서는 각성의 코어만 있으므로 각성의 코어를 떨군다.
return Item::EtcItemID::AWAKENCORE;
}
else
{
int iMetalID = (Item::EtcItemID::MINERAL_START_ID + nAwardTableIndex);
if(iMetalID > Item::EtcItemID::MINERAL_END_ID)
iMetalID = Item::EtcItemID::MINERAL_END_ID;
return iMetalID;
}
}
}
return 0;
}
unsigned long CAward::GetAwardPotion(unsigned char cDeadMonsterLevel)
{
cDeadMonsterLevel = std::min(cDeadMonsterLevel, unsigned char(MAX_MONSTER_LEVEL));
const int nPoint = Math::Random::ComplexRandom(std::accumulate(&(aryPotionDropTable[(cDeadMonsterLevel - 1) / 10][0]),
&(aryPotionDropTable[(cDeadMonsterLevel - 1) / 10][POTION_DROP_TABLE_ROW]), 0));
int nSpace = 0;
for (int nAwardTableIndex = 0; nAwardTableIndex < POTION_DROP_TABLE_ROW; ++nAwardTableIndex)
{
nSpace += aryPotionDropTable[(cDeadMonsterLevel - 1) / 10][nAwardTableIndex];
if (nSpace > nPoint)
{
if(nAwardTableIndex <= 3)
{
const int iType = Math::Random::ComplexRandom(2);
// 빵들.
if(iType == 0)
{
switch (nAwardTableIndex)
{
case 0: return Item::EtcItemID::BREAD;
case 1: return Item::EtcItemID::RARE_STEAK;
case 2: return Item::EtcItemID::WELLDONE_STEAK;
case 3: return Item::EtcItemID::BACON;
}
}
else if(iType == 1)
{
switch (nAwardTableIndex)
{
case 0: return Item::EtcItemID::MANNA1;
case 1: return Item::EtcItemID::MANNA2;
case 2: return Item::EtcItemID::MANNA3;
case 3: return Item::EtcItemID::MANNA4;
}
}
else if(iType == 2)
{
switch (nAwardTableIndex)
{
case 0: return Item::EtcItemID::SOUP1;
case 1: return Item::EtcItemID::SOUP2;
case 2: return Item::EtcItemID::SOUP3;
case 3: return Item::EtcItemID::SOUP4;
}
}
}
else
{
// 4부터 포션 0랩 포션
// 포션계열,
int iLevel = nAwardTableIndex-4;
const int iType = Math::Random::ComplexRandom(14);
switch(iType)
{
case 0: return Item::EtcItemID::POTION1+iLevel;
case 1: return Item::EtcItemID::POTION2+iLevel;
case 2: return Item::EtcItemID::POTION3+iLevel;
case 3: return Item::EtcItemID::POTION4+iLevel;
case 4: return Item::EtcItemID::POTION5+iLevel;
case 5: return Item::EtcItemID::POTION6+iLevel;
case 6: return Item::EtcItemID::POTION7+iLevel;
case 7: return Item::EtcItemID::POTION8+iLevel;
case 8: return Item::EtcItemID::POTION9+iLevel;
case 9: return Item::EtcItemID::POTION10+iLevel;
case 10: return Item::EtcItemID::POTION11+iLevel;
case 11: return Item::EtcItemID::POTION12+iLevel;
case 12: return Item::EtcItemID::POTION13+iLevel;
case 13: return Item::EtcItemID::POTION14+iLevel;
// case 14: return Item::EtcItemID::POTION15+iLevel;
}
}
}
}
return 0;
}
unsigned long CAward::GetAwardSkill(unsigned char cDeadMonsterLevel, char cAttackCharacterNation)
{
cDeadMonsterLevel = std::min(cDeadMonsterLevel, unsigned char(MAX_MONSTER_LEVEL));
const short nDeadMonsterLevelRange = (cDeadMonsterLevel - 1) / 10;
const short nPoint = static_cast<short>(Math::Random::ComplexRandom(arySkillBookDropTable[nDeadMonsterLevelRange] * 3));
short nSkillLevel = (101 <= cDeadMonsterLevel) ? 4 : nPoint % arySkillBookDropTable[nDeadMonsterLevelRange];
// edith 2008.05.07 스킬북 드랍 설정
if (true == CServerSetup::GetInstance().UseContents(GameRYL::LEVEL_LIMIT_90))
{
// 4단계까지 드랍 가능
nSkillLevel = min(nSkillLevel, short(3));
}
else
{
// 3단계까지 드랍 가능
nSkillLevel = min(nSkillLevel, short(2));
}
/*
else if (true == CServerSetup::GetInstance().UseContents(GameRYL::LEVEL_LIMIT_80))
{
// 4단계까지 드랍 가능
nSkillLevel = min(nSkillLevel, short(3));
}
else
{
// 3단계까지 드랍 가능
nSkillLevel = min(nSkillLevel, short(2));
}
*/
const CClass::JobLevel nSkillType = (nPoint < arySkillBookDropTable[nDeadMonsterLevelRange] || 4 <= nSkillLevel) ?
CClass::JOB_CHANGE_1ST : CClass::DEFAULT_CLASS;
int nSkillKind = (4 <= nSkillLevel) ? FIFTHSKILL_KIND : SKILL_KIND;
unsigned short* arySkillShuffleList = new unsigned short[nSkillKind];
for(int i = 0; i < nSkillKind; i++)
{
arySkillShuffleList[i] = 0;
}
if(4 <= nSkillLevel)
{
std::copy(aryDropableFifthSkillbookList, aryDropableFifthSkillbookList + nSkillKind, arySkillShuffleList);
}
else
{
std::copy(aryDropableSkillbookList, aryDropableSkillbookList + nSkillKind, arySkillShuffleList);
}
std::random_shuffle(arySkillShuffleList, arySkillShuffleList + nSkillKind);
for (int nAwardTableIndex = 0; nAwardTableIndex < nSkillKind; nAwardTableIndex++)
{
const unsigned char cSkillBookClass = (arySkillShuffleList[nAwardTableIndex] & ~Skill::SKILL_MASK) >> 8;
if ((cSkillBookClass >> 4) == cAttackCharacterNation && CClass::GetJobLevel(cSkillBookClass) == nSkillType)
{
return Item::CItemMgr::GetInstance().GetItemIDFromSkillID(arySkillShuffleList[nAwardTableIndex], nSkillLevel);
}
}
delete arySkillShuffleList;
return 0;
}
unsigned short CAward::GetAwardEquipment(Item::EquipType::OptionType eOptionType,
unsigned char cDeadMonsterLevel, CCharacter* lpCharacter, bool bQuestAward)
{
unsigned short wItemID = 0;
// 솔로잉
if (NULL == lpCharacter->GetParty() || true == bQuestAward)
{
// edith 2008.01.14 40프로 확율로 클래스에 알맞는 장비 드랍
// 40%의 확률로 클래스에 알맞은 장비를 드랍한다.
if (Math::Random::ComplexRandom(100) < (unsigned long)(40+lpCharacter->GetAbilityValue(Skill::Type::AB_MYEQUIP_UP)))
{
unsigned char aryShuffleList[PRIMARY_EQUIP_TABLE_ROW] = { 0, };
std::copy(aryPrimaryEquipList[lpCharacter->GetClass()],
aryPrimaryEquipList[lpCharacter->GetClass()] + PRIMARY_EQUIP_TABLE_ROW,
aryShuffleList);
std::random_shuffle(aryShuffleList, aryShuffleList + PRIMARY_EQUIP_TABLE_ROW);
Item::CItemType& itemType = Item::CItemType::GetInstance();
for (unsigned char cIndex = 0; cIndex < PRIMARY_EQUIP_TABLE_ROW; ++cIndex)
{
Item::ItemType::Type eItemType = static_cast<Item::ItemType::Type>(aryShuffleList[cIndex]);
// edith 2008.01.14 옵션타입 알아오기 (스탠다드옵션, 오버옵션, 논옵션)
if (MAX_ITEM_TYPE != aryShuffleList[cIndex] &&
true == itemType.IsCorrectOptionType(eOptionType, eItemType))
{
wItemID = Item::CItemMgr::GetInstance().GetDropItem(eItemType, cDeadMonsterLevel);
if (0 == wItemID) { continue; }
break;
}
}
}
else
{
if (CClass::HUMAN == lpCharacter->GetRace())
{
wItemID = SelectItemByRace(eOptionType, aryHumanEquipTypeList, HUMAN_EQUIP_TYPE_NUM, cDeadMonsterLevel);
}
else if (CClass::AKHAN == lpCharacter->GetRace())
{
wItemID = SelectItemByRace(eOptionType, aryAkhanEquipTypeList, AKHAN_EQUIP_TYPE_NUM, cDeadMonsterLevel);
}
else
{
ERRLOG2(g_Log, "CID:0x%08x 캐릭터의 종족이 이상합니다. 종족:%d",
lpCharacter->GetCID(), lpCharacter->GetRace());
return 0;
}
}
}
// 파티 플레이
else
{
CCharacterParty* lpParty = reinterpret_cast<CCharacterParty *>(lpCharacter->GetParty());
CCharacter* aryNearCharacterList[PARTY::MAX_MEM];
unsigned char cHighestLevel = 0;
const unsigned char cNearPartyMemberNum = lpParty->GetNearMemberList(lpCharacter->GetCellPos().m_lpCell,
true, aryNearCharacterList, cHighestLevel);
if (0 == cNearPartyMemberNum) { return 0; }
unsigned char cHumanNum = 0;
for (unsigned char cMemberIndex = 0; cMemberIndex < cNearPartyMemberNum; ++cMemberIndex)
{
if (CClass::HUMAN == aryNearCharacterList[cMemberIndex]->GetRace())
{
++cHumanNum;
}
}
// 파티원의 종족 비율에 의해 드랍될 장비 타입의 확률이 결정된다.
if (static_cast<unsigned long>(cHumanNum * 100.0f / cNearPartyMemberNum) > Math::Random::ComplexRandom(100))
{
wItemID = SelectItemByRace(eOptionType, aryHumanEquipTypeList, HUMAN_EQUIP_TYPE_NUM, cDeadMonsterLevel);
}
else
{
wItemID = SelectItemByRace(eOptionType, aryAkhanEquipTypeList, AKHAN_EQUIP_TYPE_NUM, cDeadMonsterLevel);
}
}
if (0 == wItemID)
{
ERRLOG0(g_Log, "드랍할 장비를 결정하지 못하였습니다.");
return 0;
}
return wItemID;
}
unsigned short CAward::GetQuestEquipmentClass(Item::EquipType::OptionType eOptionType, unsigned char cClass,
unsigned char cDeadMonsterLevel, CCharacter* lpCharacter)
{
unsigned short wItemID = 0;
unsigned char aryShuffleList[PRIMARY_EQUIP_TABLE_ROW] = { 0, };
std::copy(aryPrimaryEquipList[cClass],
aryPrimaryEquipList[cClass] + PRIMARY_EQUIP_TABLE_ROW,
aryShuffleList);
std::random_shuffle(aryShuffleList, aryShuffleList + PRIMARY_EQUIP_TABLE_ROW);
Item::CItemType& itemType = Item::CItemType::GetInstance();
for (unsigned char cIndex = 0; cIndex < PRIMARY_EQUIP_TABLE_ROW; ++cIndex)
{
Item::ItemType::Type eItemType = static_cast<Item::ItemType::Type>(aryShuffleList[cIndex]);
// edith 2008.01.14 옵션타입 알아오기 (스탠다드옵션, 오버옵션, 논옵션)
if (MAX_ITEM_TYPE != aryShuffleList[cIndex] &&
true == itemType.IsCorrectOptionType(eOptionType, eItemType))
{
wItemID = Item::CItemMgr::GetInstance().GetDropItem(eItemType, cDeadMonsterLevel);
if (0 == wItemID) { continue; }
break;
}
}
if (0 == wItemID)
{
ERRLOG0(g_Log, "드랍할 장비를 결정하지 못하였습니다.");
return 0;
}
return wItemID;
}
unsigned long CAward::GetAwardCashItem(char cAttackCharacterNation, unsigned char cDeadMonsterLevel, unsigned int cDeadMonsterPattern)
{
cDeadMonsterLevel = std::min(cDeadMonsterLevel, unsigned char(MAX_MONSTER_LEVEL));
int iMonLevel = (cDeadMonsterLevel - 1) / 10;
if(cDeadMonsterPattern == MonsterInfo::PATTERN_CHIEF) // 치프면 11
iMonLevel = 11;
else if(cDeadMonsterPattern == MonsterInfo::PATTERN_NAMED) // 네임드면 12 테이블
iMonLevel = 12;
const int nPoint = Math::Random::ComplexRandom(std::accumulate(&(aryCashDropTable[iMonLevel][0]),
&(aryCashDropTable[iMonLevel][CASH_DROP_TABLE_ROW]), 0));
// 캐쉬템의 드랍아이템 리스트는 여기에
int nSpace = 0;
for (int nAwardTableIndex = 0; nAwardTableIndex < CASH_DROP_TABLE_ROW; ++nAwardTableIndex)
{
nSpace += aryCashDropTable[(cDeadMonsterLevel - 1) / 10][nAwardTableIndex];
if (nSpace > nPoint)
{
if(nAwardTableIndex == 0) // 망각의돌
return Item::EtcItemID::OBLIVION_STONE;
else if(nAwardTableIndex == 4) // 내구도석
return Item::EtcItemID::ENDURANCESTONE;
else if(nAwardTableIndex == 5) // 부활의돌
return Item::EtcItemID::REBIRTH_STONE;
else if(nAwardTableIndex == 6) // 탈것
{
const int iType = Math::Random::ComplexRandom(2);
if(cAttackCharacterNation == 0) //휴먼
{
if(iType == 0)
return Item::EtcItemID::RIDEH1;
else
return Item::EtcItemID::RIDEH2;
}
else // 아칸
{
if(iType == 0)
return Item::EtcItemID::RIDEA1;
else
return Item::EtcItemID::RIDEA2;
}
}
else if(nAwardTableIndex == 3) // 오브
{
const int iType = Math::Random::ComplexRandom(2);
if(iType == 0)
return Item::EtcItemID::ORB_EXP;
else
return Item::EtcItemID::ORB_LUCK;
}
else if(nAwardTableIndex == 1 || nAwardTableIndex == 2) // 유료 포션
{
int iLevel = nAwardTableIndex-1;
int iType = 0;
const int iRage = Math::Random::ComplexRandom(100);
if(iRage < 60)
{
iType = Math::Random::ComplexRandom(2);
}
else
{
iType = 2+Math::Random::ComplexRandom(12);
}
switch(iType)
{
case 0: return Item::EtcItemID::CASHPOTION1+iLevel;
case 1: return Item::EtcItemID::CASHPOTION2+iLevel;
case 2: return Item::EtcItemID::CASHPOTION3+iLevel;
case 3: return Item::EtcItemID::CASHPOTION4+iLevel;
case 4: return Item::EtcItemID::CASHPOTION5+iLevel;
case 5: return Item::EtcItemID::CASHPOTION6+iLevel;
case 6: return Item::EtcItemID::CASHPOTION7+iLevel;
case 7: return Item::EtcItemID::CASHPOTION8+iLevel;
case 8: return Item::EtcItemID::CASHPOTION9+iLevel;
case 9: return Item::EtcItemID::CASHPOTION10+iLevel;
case 10: return Item::EtcItemID::CASHPOTION11+iLevel;
case 11: return Item::EtcItemID::CASHPOTION12+iLevel;
case 12: return Item::EtcItemID::CASHPOTION13+iLevel;
case 13: return Item::EtcItemID::CASHPOTION14+iLevel;
// case 14: return Item::EtcItemID::CASHPOTION15+iLevel;
}
}
}
}
ERRLOG0(g_Log, "드랍할 캐쉬아이템을 결정하지 못하였습니다.");
return 0;//Item::EtcItemID::OBLIVION_STONE;
}
unsigned long CAward::GetAwardLottery(void)
{
if (false == CServerSetup::GetInstance().GetLotteryEvent())
{
ERRLOG0(g_Log, "복권 이벤트를 하고 있지 않은데, 복권 드랍을 요청하였습니다.");
return 0;
}
return CGameEventMgr::GetInstance().GetLotteryEvent().GetLottery();
}
unsigned short CAward::SelectItemByRace(Item::EquipType::OptionType eOptionType,
const unsigned char* aryItemType, unsigned char cMaxItemType, unsigned char cLevel)
{
unsigned char aryShuffleList[UCHAR_MAX] = { 0, };
std::copy(aryItemType, aryItemType + cMaxItemType, aryShuffleList);
std::random_shuffle(aryShuffleList, aryShuffleList + cMaxItemType);
unsigned short wItemID = 0;
for (unsigned char cIndex = 0; cIndex < cMaxItemType; ++cIndex)
{
Item::ItemType::Type eItemType = static_cast<Item::ItemType::Type>(aryShuffleList[cIndex]);
if (true == Item::CItemType::GetInstance().IsCorrectOptionType(eOptionType, eItemType))
{
wItemID = Item::CItemMgr::GetInstance().GetDropItem(eItemType, cLevel);
if (0 != wItemID)
{
break;
}
}
}
return wItemID;
}

View File

@@ -0,0 +1,329 @@
#pragma once
#ifndef _AWARD_TABLE_H_
#define _AWARD_TABLE_H_
#include <Item/ItemConstants.h>
#include <Creature/Character/CharacterClass.h>
#include <Creature/AggresiveCreature.h>
class CMonster;
class CCharacter;
namespace AwardTable
{
enum Const
{
MAX_DROP_ITEM = 5, // 한 몬스터가 최대로 떨구는 아이템 수
MAX_MONSTER_LEVEL = 109, // 몬스터의 최고 레벨
// (보석, 광물, 물약, 스킬북 드랍 테이블용으로 실제와는 다르다. Creature::MONSTER_MAX_LEVEL 참고)
SKILL_KIND = 88, // 스킬(북)의 종류 (스킬북이 없는 스킬도 있다.)
FIFTHSKILL_KIND = 15, // 스킬(북)의 종류 (스킬북이 없는 스킬도 있다.)
GEM_DROP_TABLE_COLUMN = 11, // 보석 드랍 테이블 세로 크기
GEM_DROP_TABLE_ROW = 3, // 보석 드랍 테이블 가로 크기
METAL_DROP_TABLE_COLUMN = 11, // 광물 드랍 테이블 세로 크기
METAL_DROP_TABLE_ROW = 7, // 광물 드랍 테이블 가로 크기
POTION_DROP_TABLE_COLUMN = 11, // 물약 드랍 테이블 세로 크기
POTION_DROP_TABLE_ROW = 8, // 물약 드랍 테이블 가로 크기
CASH_DROP_TABLE_COLUMN = 13, // 캐쉬 드랍 테이블 세로 크기
CASH_DROP_TABLE_ROW = 7, // 캐쉬 드랍 테이블 가로 크기
SKILLBOOK_DROP_TABLE_COLUMN = 11, // 스킬북 드랍 테이블 세로 크기
EQUIP_MAX_NUM_PER_LEVEL = 15, // 레벨당 드랍되는 장비의 종류수
BLACK_MARKET_TABLE_COLUMN = 228, // 암시장 테이블 세로 크기
BLACK_MARKET_TABLE_ROW = 13, // 암시장 테이블 가로 크기
PRIMARY_EQUIP_TABLE_ROW = 9, // 장비 드랍 우선 순위 테이블 가로 크기
HUMAN_EQUIP_TYPE_NUM = 19, // 휴먼 장비 타입 갯수
AKHAN_EQUIP_TYPE_NUM = 19 // 아칸 장비 타입 갯수
};
// 아이템 드랍률
// Desc : 각 값은 n/1000 의 확률을 의미한다.
const unsigned short aryItemDropRate[CAggresiveCreature::AGGRAVATION_NUM][MAX_DROP_ITEM] = {
{ 1, 1, 1, 1, 1 }, // -20
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 91, 1, 1, 1, 1 },
{ 189, 1, 1, 1, 1 },
{ 280, 1, 1, 1, 1 },
{ 364, 1, 1, 1, 1 },
{ 441, 1, 1, 1, 1 }, // -5
{ 511, 1, 1, 1, 1 },
{ 574, 1, 1, 1, 1 },
{ 623, 1, 1, 1, 1 },
{ 665, 91, 1, 1, 1 }, // -1
{ 700, 189, 1, 1, 1 }, // 0
{ 710, 280, 1, 1, 1 }, // +1
{ 720, 364, 1, 1, 1 },
{ 730, 441, 1, 1, 1 },
{ 740, 511, 1, 1, 1 },
{ 750, 574, 1, 1, 1 }, // +5
{ 760, 623, 91, 1, 1 },
{ 800, 665, 189, 1, 1 },
{ 800, 700, 280, 1, 1 },
{ 800, 710, 364, 91, 1 }, // +9
{ 800, 720, 441, 189, 1 },
{ 800, 730, 511, 280, 1 },
{ 800, 740, 574, 364, 91 }, // +12
{ 900, 750, 623, 441, 189 },
{ 900, 760, 665, 511, 280 },
{ 900, 800, 700, 574, 364 }, // +15
{ 900, 800, 710, 623, 441 }, // +16
{ 900, 800, 720, 665, 511 },
{ 900, 800, 730, 700, 574 },
{ 900, 800, 740, 710, 623 },
{ 900, 900, 750, 720, 665 } // +20
};
/*
const unsigned short aryItemDropRate[CAggresiveCreature::AGGRAVATION_NUM][MAX_DROP_ITEM] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 161, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 195, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 228, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 262, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 296, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 392, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 363, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 396, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 430, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 464, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 497, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 531, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 564, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 598, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 632, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 665, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 699, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 732, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 766, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 800, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 900, 51, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 900, 252, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 900, 504, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 900, 807, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 900, 900, 260, 1, 1, 1, 1, 1, 1, 1 },
{ 900, 900, 663, 1, 1, 1, 1, 1, 1, 1 },
{ 900, 900, 900, 216, 1, 1, 1, 1, 1, 1 },
{ 900, 900, 900, 720, 1, 1, 1, 1, 1, 1 },
{ 900, 900, 900, 900, 375, 1, 1, 1, 1, 1 },
{ 900, 900, 900, 900, 900, 80, 1, 1, 1, 1 },
{ 900, 900, 900, 900, 900, 735, 1, 1, 1, 1 },
{ 900, 900, 900, 900, 900, 900, 540, 1, 1, 1 },
{ 900, 900, 900, 900, 900, 900, 900, 396, 1, 1 },
{ 900, 900, 900, 900, 900, 900, 900, 900, 303, 1 },
{ 900, 900, 900, 900, 900, 900, 900, 900, 900, 260 },
{ 900, 900, 900, 900, 900, 900, 900, 900, 900, 900 },
{ 900, 900, 900, 900, 900, 900, 900, 900, 900, 900 },
{ 900, 900, 900, 900, 900, 900, 900, 900, 900, 900 },
{ 900, 900, 900, 900, 900, 900, 900, 900, 900, 900 },
{ 900, 900, 900, 900, 900, 900, 900, 900, 900, 900 }
};
*/
const short aryGemDropTable[GEM_DROP_TABLE_COLUMN][GEM_DROP_TABLE_ROW] = {
// 보석조각 작은보석 일반보석
{ 100, 0, 0 }, // 10
{ 90, 10, 0 }, // 20
{ 70, 20, 10 }, // 30
{ 50, 30, 20 }, // 40
{ 30, 40, 30 }, // 50
{ 10, 50, 40 }, // 60
{ 10, 30, 60 }, // 70
{ 0, 20, 80 }, // 80
{ 0, 10, 90 }, // 90
{ 0, 10, 90 }, // 100
{ 0, 0, 100 } // 110
};
const short aryMetalDropTable[METAL_DROP_TABLE_COLUMN][METAL_DROP_TABLE_ROW] = {
// 실빈 미스릴 이터니움 소울메탈 라타리움 고대의돌 코어
{ 90, 0, 0, 0, 0, 0, 10 }, // 10
{ 81, 9, 0, 0, 0, 0, 10 }, // 20
{ 63, 18, 9, 0, 0, 0, 10 }, // 30
{ 45, 27, 18, 0, 0, 0, 10 }, // 40
{ 27, 36, 27, 0, 0, 0, 10 }, // 50
{ 9, 45, 36, 0, 0, 0, 10 }, // 60
{ 9, 27, 54, 0, 0, 0, 10 }, // 70
{ 0, 18, 72, 0, 0, 0, 10 }, // 80
{ 0, 9, 81, 0, 0, 0, 10 }, // 90
{ 0, 9, 81, 0, 0, 0, 10 }, // 100
{ 0, 0, 90, 0, 0, 0, 10 } // 110
};
const short aryPotionDropTable[POTION_DROP_TABLE_COLUMN][POTION_DROP_TABLE_ROW] = {
// 빵 생고기 구운고기 베이컨 버프물약1 버프물약2 버프물약3 버프물약4
// 만나1 만나2 만나3 만나4 레벨 레벨 레벨 레벨
// 스프1 스프2 스프3 스프4
{ 70, 0, 0, 0, 16, 8, 4, 2 }, // 10
{ 35, 35, 0, 0, 16, 8, 4, 2 }, // 20
{ 23, 23, 24, 0, 16, 8, 4, 2 }, // 30
{ 17, 17, 18, 18, 16, 8, 4, 2 }, // 40
{ 0, 23, 23, 24, 16, 8, 4, 2 }, // 50
{ 0, 23, 35, 24, 16, 8, 4, 2 }, // 60
{ 0, 0, 35, 35, 16, 8, 4, 2 }, // 70
{ 0, 0, 1, 70, 16, 8, 4, 2 }, // 80
{ 0, 0, 0, 70, 16, 8, 4, 2 }, // 90
{ 0, 0, 0, 70, 16, 8, 4, 2 }, // 100
{ 0, 0, 0, 70, 16, 8, 4, 2 } // 110
};
const short aryCashDropTable[CASH_DROP_TABLE_COLUMN][CASH_DROP_TABLE_ROW] = {
// 망각의돌 Holy Almighty 오브 내구도석 부활의돌 마운트
{ 20, 60, 10, 10, 0, 0, 0 }, // 10
{ 20, 60, 10, 10, 0, 0, 0 }, // 20
{ 20, 60, 10, 10, 0, 0, 0 }, // 30
{ 20, 60, 10, 10, 0, 0, 0 }, // 40
{ 20, 60, 10, 10, 0, 0, 0 }, // 50
{ 20, 60, 10, 10, 0, 0, 0 }, // 60
{ 20, 60, 10, 10, 0, 0, 0 }, // 70
{ 20, 60, 10, 10, 0, 0, 0 }, // 80
{ 20, 60, 10, 10, 0, 0, 0 }, // 90
{ 20, 60, 10, 10, 0, 0, 0 }, // 100
{ 20, 60, 10, 10, 0, 0, 0 }, // 110
{ 20, 60, 10, 10, 0, 0, 0 }, // 치프
{ 0, 20, 50, 16, 8, 4, 2 }, // 네임드
};
const short arySkillBookDropTable[SKILLBOOK_DROP_TABLE_COLUMN] = {
1, 1, 2, 2, 2,
3, 3, 3, 4, 4,
5
};
const unsigned short aryDropableSkillbookList[SKILL_KIND] = {
// 인간 공통 스킬 17
0x8104, 0x8102, 0x8103, 0x8105, 0x8203, 0x8205, 0x8302, 0x8303, 0x8402, 0x8405,
0x8106, 0x8107, 0x8202, 0x8304, 0x8305, 0x8306, 0x8404,
// 인간 상위 스킬 29
0x8503, 0x8504, 0x8602, 0x8603, 0x8604, 0x8702, 0x8704, 0x8705, 0x8706, 0x8803,
0x8804, 0x8805, 0x8902, 0x8903, 0x8904, 0x8905, 0x8A02, 0x8A04, 0x8A05, 0x8A06,
0x8B02, 0x8B03, 0x8B04, 0x8B05, 0x8B06, 0x8C02, 0x8C03, 0x8C04, 0x8C08,
// 아칸 공통 스킬 17
0x9102, 0x9105, 0x9104, 0x9202, 0x9106, 0x9206, 0x9207, 0x9213, 0x9209, 0x9108,
0x9109, 0x9110, 0x9204, 0x9208, 0x9210, 0x9212, 0x9214,
// 아칸 상위 스킬 25
0x9302, 0x9305, 0x9307, 0x9402, 0x9404, 0x9405, 0x9407, 0x9502, 0x9504, 0x9506,
0x9602, 0x9603, 0x9604, 0x9605, 0x9606, 0x9703, 0x9704, 0x9706, 0x9802, 0x9803,
0x9804, 0x9805, 0x9303, 0x9708, 0x9709
};
const unsigned short aryDropableFifthSkillbookList[FIFTHSKILL_KIND] = {
// 인간 5단계 스킬
0x8603, 0x8503, 0x8704, 0x8804, 0x8902, 0x8a05, 0x8c02, 0x8b04,
// 아칸 5단계 스킬
0x9402, 0x9302, 0x9502, 0x9606, 0x9603, 0x9706, 0x9804
};
using namespace Item::ItemType;
const unsigned char aryPrimaryEquipList[CClass::MAX_CLASS][PRIMARY_EQUIP_TABLE_ROW] = {
{ MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE },
{ CON_HELM, CON_GLOVE, CON_BOOTS, CON_ARMOUR, ONEHANDED_SWORD, TWOHANDED_SWORD, ONEHANDED_AXE, TWOHANDED_AXE, SHIELD }, // 파이터
{ DEX_HELM, DEX_GLOVE, DEX_BOOTS, DEX_ARMOUR, DAGGER, BOW, CROSSBOW, SHIELD, MAX_ITEM_TYPE }, // 로그
{ DEX_HELM, DEX_GLOVE, DEX_BOOTS, DEX_ARMOUR, STAFF, DAGGER, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 메이지
{ CON_HELM, CON_GLOVE, CON_BOOTS, CON_ARMOUR, ONEHANDED_BLUNT, TWOHANDED_BLUNT, SHIELD, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 어콜라이트
{ CON_HELM, CON_GLOVE, CON_BOOTS, CON_ARMOUR, ONEHANDED_BLUNT, TWOHANDED_BLUNT, ONEHANDED_AXE, TWOHANDED_AXE, SHIELD }, // 디펜더
{ CON_HELM, CON_GLOVE, CON_BOOTS, CON_ARMOUR, ONEHANDED_SWORD, TWOHANDED_SWORD, ONEHANDED_AXE, TWOHANDED_AXE, MAX_ITEM_TYPE }, // 워리어
{ DEX_HELM, DEX_GLOVE, DEX_BOOTS, DEX_ARMOUR, DAGGER, ONEHANDED_SWORD, ONEHANDED_AXE, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 어쌔신
{ DEX_HELM, DEX_GLOVE, DEX_BOOTS, DEX_ARMOUR, DAGGER, BOW, CROSSBOW, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 아처
{ DEX_HELM, DEX_GLOVE, DEX_BOOTS, DEX_ARMOUR, STAFF, DAGGER, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 소서러
{ DEX_HELM, DEX_GLOVE, DEX_BOOTS, DEX_ARMOUR, STAFF, BOW, CROSSBOW, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 엔찬터
{ CON_HELM, CON_GLOVE, CON_BOOTS, CON_ARMOUR, ONEHANDED_BLUNT, TWOHANDED_BLUNT, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 프리스트
{ CON_HELM, CON_GLOVE, CON_BOOTS, CON_ARMOUR, ONEHANDED_BLUNT, TWOHANDED_BLUNT, SHIELD, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 클레릭
{ MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE },
{ MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE },
{ MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE },
{ MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE },
{ CON_HEAD, CON_PROTECT_A, CON_PELVIS, CON_BODY, COM_SWORD, COM_BLUNT, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 컴배턴트
{ DEX_HEAD, DEX_PROTECT_A, DEX_PELVIS, DEX_BODY, OPP_AXE, OPP_SLUSHER, OPP_TALON, OPP_SYTHE, MAX_ITEM_TYPE }, // 오피세이터
{ CON_HEAD, CON_PROTECT_A, CON_PELVIS, CON_BODY, COM_SWORD, COM_BLUNT, SKILL_A_GUARD, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 템플러
{ CON_HEAD, CON_PROTECT_A, CON_PELVIS, CON_BODY, COM_SWORD, COM_BLUNT, SKILL_A_ATTACK, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 어태커
{ CON_HEAD, CON_PROTECT_A, CON_PELVIS, CON_BODY, COM_SWORD, COM_BLUNT, SKILL_A_GUN, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 거너
{ DEX_HEAD, DEX_PROTECT_A, DEX_PELVIS, DEX_BODY, OPP_SYTHE, OPP_SLUSHER, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 룬오프
{ DEX_HEAD, DEX_PROTECT_A, DEX_PELVIS, DEX_BODY, OPP_HAMMER, OPP_AXE, MAX_ITEM_TYPE, MAX_ITEM_TYPE, MAX_ITEM_TYPE }, // 라이프오프
{ DEX_HEAD, DEX_PROTECT_A, DEX_PELVIS, DEX_BODY, OPP_TALON, OPP_SLUSHER, SKILL_A_KNIFE, MAX_ITEM_TYPE, MAX_ITEM_TYPE } // 쉐도우오프
};
const unsigned char aryHumanEquipTypeList[HUMAN_EQUIP_TYPE_NUM] = {
CON_ARMOUR, CON_HELM, CON_GLOVE, CON_BOOTS,
DEX_ARMOUR, DEX_HELM, DEX_GLOVE, DEX_BOOTS,
ONEHANDED_SWORD, TWOHANDED_SWORD, ONEHANDED_AXE, TWOHANDED_AXE,
ONEHANDED_BLUNT, TWOHANDED_BLUNT, BOW, CROSSBOW,
STAFF, DAGGER, SHIELD
};
const unsigned char aryAkhanEquipTypeList[AKHAN_EQUIP_TYPE_NUM] = {
CON_BODY, CON_HEAD, CON_PELVIS, CON_PROTECT_A,
DEX_BODY, DEX_HEAD, DEX_PELVIS, DEX_PROTECT_A,
COM_BLUNT, COM_SWORD, OPP_HAMMER, OPP_AXE,
OPP_SLUSHER, OPP_TALON, OPP_SYTHE,
SKILL_A_GUARD, SKILL_A_ATTACK, SKILL_A_GUN, SKILL_A_KNIFE
};
class CAward : public CSingleton<CAward>
{
public:
unsigned long GetAward(unsigned char cItemKind, CMonster* lpDeadMonster, CCharacter* lpAttackCharacter,
Item::CItem** lpItem_Out);
unsigned short GetAwardEquipment(Item::EquipType::OptionType eOptionType,
unsigned char cDeadMonsterLevel, CCharacter* lpAttackCharacter, bool bQuestAward);
unsigned short GetQuestEquipmentClass(Item::EquipType::OptionType eOptionType, unsigned char cClass,
unsigned char cDeadMonsterLevel, CCharacter* lpAttackCharacter);
private:
unsigned long GetAwardCoin(unsigned char cDeadMonsterLevel, float cAddPer);
unsigned long GetAwardGem(unsigned char cDeadMonsterLevel);
unsigned long GetAwardMetal(unsigned char cDeadMonsterLevel);
unsigned long GetAwardPotion(unsigned char cDeadMonsterLevel);
unsigned long GetAwardSkill(unsigned char cDeadMonsterLevel, char cAttackCharacterNation);
unsigned long GetAwardCashItem(char cAttackCharacterNation, unsigned char cDeadMonsterLevel, unsigned int cDeadMonsterPattern);
unsigned long GetAwardLottery(void);
//--// start..
unsigned long GetAwardRing(unsigned char cLevel, unsigned char cGrade);
unsigned long GetAwardNecklace(unsigned char cLevel, unsigned char cGrade);
unsigned long GetAwardRune(unsigned char cLevel, unsigned char cGrade);
unsigned long GetAwardDestructionRune();
//--// end..
unsigned short SelectItemByRace(Item::EquipType::OptionType eOptionType,
const unsigned char* aryItemType, unsigned char cMaxItemType, unsigned char cLevel);
static CAward ms_this;
};
}
#endif

View File

@@ -0,0 +1,76 @@
// FSM.cpp: implementation of the CFSM2 class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <algorithm>
#include "FSM.h"
#include "FSMState.h"
CFSM CFSM::ms_this;
CFSM::CFSM(void)
: m_nStateNum(0)
{
std::fill_n(m_lpFSMState, int(MAX_STATE), reinterpret_cast<CFSMState*>(0));
}
CFSM::~CFSM()
{
CFSMState** lppFSMPastEnd = m_lpFSMState + MAX_STATE;
for(CFSMState** lppFSM = m_lpFSMState; lppFSM != lppFSMPastEnd; ++lppFSM)
{
delete *lppFSM;
*lppFSM = NULL;
}
}
int CFSM::StateTransition(int nCrurrentState, int Input)
{
if (!nCrurrentState)
{
return nCrurrentState;
}
CFSMState* lpState = GetState(nCrurrentState);
if (NULL == lpState)
{
return nCrurrentState;
}
nCrurrentState = lpState->GetOutput(Input);
return nCrurrentState;
}
CFSMState* CFSM::GetState(int StateID)
{
CFSMState** lppFSMPastEnd = m_lpFSMState + MAX_STATE;
for(CFSMState** lppFSM = m_lpFSMState; lppFSM != lppFSMPastEnd; ++lppFSM)
{
if(NULL == *lppFSM)
{
return NULL;
}
if(StateID == (*lppFSM)->GetID())
{
return *lppFSM;
}
}
return NULL;
}
bool CFSM::AddState(CFSMState* lpNewState)
{
if(m_nStateNum >= MAX_STATE)
{
return false;
}
m_lpFSMState[m_nStateNum++] = lpNewState;
return true;
}

View File

@@ -0,0 +1,61 @@
// FSM.h: interface for the CFSM class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_FSM_H__5BDFA770_7869_4B08_B0BC_D7C6AC789C9D__INCLUDED_)
#define AFX_FSM_H__5BDFA770_7869_4B08_B0BC_D7C6AC789C9D__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <Pattern/Singleton.h>
#include "FSMState.h"
enum MONSTER_STATE_OR_INPUT
{
STATE_ID_NULL = 0,
STATE_ID_NORMAL,
STATE_ID_ATTACK,
STATE_ID_RETURN,
STATE_ID_ESCAPE,
STATE_ID_DIE,
INPUT_ID_SEEN_PLAYER = 100,
INPUT_ID_LOW_HP,
INPUT_ID_ZERO_HP,
INPUT_ID_LEAVE_PLAYER,
INPUT_ID_ARRIVAL_SITE,
INPUT_ID_ATTACKED_PLAYER,
INPUT_ID_CMD_ATTACK,
INPUT_ID_CMD_ESCAPE
};
class CFSM : public CSingleton<CFSM>
{
public:
enum { MAX_STATE = 8 };
~CFSM(void); // clean up memory usage
bool AddState(CFSMState* lpNewState); // add a FSMstate object pointer to the map
CFSMState* GetState(int StateID); // return the FSMstate object pointer
int StateTransition(int nCrurrentState, int Input); // perform a state transition based on input and current state
protected:
CFSM(void); // set initial state of the FSM
int m_nStateNum;
CFSMState* m_lpFSMState[MAX_STATE]; // map containing all states of this FSM
static CFSM ms_this;
};
#endif // !defined(AFX_FSM_H__5BDFA770_7869_4B08_B0BC_D7C6AC789C9D__INCLUDED_)

View File

@@ -0,0 +1,130 @@
// FSMState.cpp: implementation of the CFSMState2 class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <algorithm>
#include "FSMState.h"
CFSMState::CFSMState(int StateID, int Transitions)
: m_dwStateID(StateID), m_dwNumberOfTransistions((Transitions) ? Transitions : 1)
{
m_pdwInputs = new int[m_dwNumberOfTransistions];
if (NULL == m_pdwInputs)
{
ERRLOG1(g_Log, "m_pdwInputs의 메모리 할당에 실패하였습니다. 할당 개수는 %d개 입니다.",
m_dwNumberOfTransistions);
}
else
{
std::fill_n(m_pdwInputs, m_dwNumberOfTransistions, 0);
m_pdwOutputState = new int[m_dwNumberOfTransistions];
if (NULL == m_pdwOutputState)
{
ERRLOG1(g_Log, "m_pdwOutputState의 메모리 할당에 실패하였습니다. 할당 개수는 %d개 입니다.",
m_dwNumberOfTransistions);
}
else
{
std::fill_n(m_pdwOutputState, m_dwNumberOfTransistions, 0);
}
}
}
CFSMState::~CFSMState()
{
if(NULL != m_pdwInputs)
{
delete m_pdwInputs;
m_pdwInputs = NULL;
}
if(NULL != m_pdwOutputState)
{
delete m_pdwOutputState;
m_pdwOutputState = NULL;
}
}
void CFSMState::AddTransition(int Input, int OutputID)
{
int nTransition = 0;
for(; nTransition < m_dwNumberOfTransistions; ++nTransition)
{
if(0 == m_pdwOutputState[nTransition])
{
break;
}
}
if(nTransition >= m_dwNumberOfTransistions)
{
return;
}
m_pdwInputs[nTransition] = Input;
m_pdwOutputState[nTransition] = OutputID;
}
int CFSMState::GetOutput(int Input)
{
int OutputID = m_dwStateID; // output state to be returned
for(int nTransition = 0;
nTransition < m_dwNumberOfTransistions; ++nTransition)
{
if(0 == m_pdwOutputState[nTransition])
{
break;
}
if(Input == m_pdwInputs[nTransition])
{
OutputID = m_pdwOutputState[nTransition]; // output state id
break;
}
}
return OutputID;
}
void CFSMState::DeleteTransition(int OutputID)
{
int nTransition = 0;
for(;nTransition < m_dwNumberOfTransistions; ++nTransition)
{
if(OutputID == m_pdwOutputState[nTransition])
{
break;
}
}
if(nTransition >= m_dwNumberOfTransistions)
{
return;
}
m_pdwInputs[nTransition] = 0;
m_pdwOutputState[nTransition] = 0;
for(;nTransition < (m_dwNumberOfTransistions - 1); ++nTransition)
{
if (!m_pdwOutputState[nTransition])
{
break;
}
m_pdwInputs[nTransition] = m_pdwInputs[nTransition+1];
m_pdwOutputState[nTransition] = m_pdwOutputState[nTransition+1];
}
m_pdwInputs[nTransition] = 0;
m_pdwOutputState[nTransition] = 0;
}

View File

@@ -0,0 +1,38 @@
// FSMState.h: interface for the CFSMState class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_FSMSTATE_H__4D5ECC39_C7A5_47E7_A774_9E79707B18D8__INCLUDED_)
#define AFX_FSMSTATE_H__4D5ECC39_C7A5_47E7_A774_9E79707B18D8__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CFSMState
{
private:
int m_dwNumberOfTransistions; // maximum number of states supported by this state
int *m_pdwInputs; // input array for tranistions
int *m_pdwOutputState; // output state array
int m_dwStateID; // the unique ID of this state
int FindTransitionIndex(int nID);
public:
explicit CFSMState(int StateID, int Transitions);
~CFSMState(void);
void AddTransition(int Input, int OutputID);
void DeleteTransition(int OutputID);
int GetOutput(int Input);
inline int GetID(void) { return m_dwStateID; }
};
#endif // !defined(AFX_FSMSTATE_H__4D5ECC39_C7A5_47E7_A774_9E79707B18D8__INCLUDED_)

View File

@@ -0,0 +1,898 @@
///////////////////////////////////////////////////////////////////////////////////
//
// Purpose : 몬스터 정보를 저장 & 관리하는 클래스
//
///////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <Utility/Registry/RegFunctions.h>
#include <Utility/Math/Math.h>
#include <Utility/Time/Pulse/Pulse.h>
#include <Utility/DelimitedFile.h>
#include <Utility/Setup/ServerSetup.h>
#include <Network/ClientSocket/ClientConstants.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Creature/CreatureManager.h>
#include <Creature/Monster/PatternMonster.h>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/EnemyCheck.h>
#include <Community/Party/Party.h>
#include <Community/Party/PartyMgr.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
#include <GameTime/GameTimeConstants.h>
#include <GameTime/GameTimeMgr.h>
#include <Map/FieldMap/CellManager.h>
#include <Skill/Spell/Spell.h>
#include <Log/CharacterLog.h>
#include "AwardTable.h"
#include "Monster.h"
#include "MonsterShout.h"
LONG CMonster::ms_NormalBehaviorSendCount = 0;
LONG CMonster::ms_AttackBehaviorSendCount = 0;
LONG CMonster::ms_ReturnBehaviorSendCount = 0;
LONG CMonster::ms_EscapeBehaviorSendCount = 0;
LONG CMonster::ms_DeadBehaviorSendCount = 0;
///////////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
///////////////////////////////////////////////////////////////////////////////////
CMonster::CMonster()
: m_lpTarget(NULL), m_dwLastBehaviorTick(0), m_lCurrentFrame(0), m_bAttacking(false), m_nCurrentState(0), m_wSearchRange(0),
m_nNormalMovingDelay(0), m_nLeaveMovingNum(0), m_bAvoid(false), m_bLongRangeAttacked(false), m_bAdminCmdSummon(false),
m_bScout(false), m_nMovingPattern(0), m_OriginalPosition(), CAggresiveCreature(0), m_dwPID(0), m_wRespawnArea(0)
{
m_wDefaultSearchRange = MONSTER_SEARCH_RANGE;
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::CMonster
//
// Description : 사용자 생성자
//
// Inputs : MonsterCreate - 몬스터 생성 정보
//
// Outputs : None.
//
// Returns : None.
///////////////////////////////////////////////////////////////////////////////////
CMonster::CMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: m_lpTarget(NULL), m_dwLastBehaviorTick(0), m_lCurrentFrame(0), m_bAttacking(false), m_nCurrentState(0), m_wSearchRange(0),
m_nNormalMovingDelay(0), m_nLeaveMovingNum(0), m_bAvoid(false), m_bLongRangeAttacked(false), m_bAdminCmdSummon(bAdminCmdSummon),
m_bScout(MonsterCreate.m_bScout), m_nMovingPattern(MonsterCreate.m_nMovingPattern), m_wRespawnArea(MonsterCreate.m_wRespawnArea),
m_OriginalPosition(MonsterCreate.m_Pos), CAggresiveCreature(MonsterCreate.m_dwCID), m_dwPID(MonsterCreate.m_dwPID)
{
const CMonsterMgr::MonsterProtoType* pProtoType =
CMonsterMgr::GetInstance().GetMonsterProtoType(MonsterCreate.m_nKID);
if (NULL == pProtoType)
{
ERRLOG1(g_Log, "알맞은 프로토타입이 없습니다. MonsterProtoType.txt를 확인해주십시오. KID:%d", MonsterCreate.m_nKID);
return;
}
m_CreatureStatus = pProtoType->m_CreatureStatus;
m_MonsterInfo = pProtoType->m_MonsterInfo;
m_CreatureStatus.m_StatusInfo.CalculateSubStatus();
// 게임중에 챈트 효과 계산을 위해 존재합니다.
m_EquipStatus = pProtoType->m_CreatureStatus.m_StatusInfo;
m_EquipStatus.m_cCalculateState = FightStatus::CS_EQUIP_INFO;
m_wDefaultSearchRange = MONSTER_SEARCH_RANGE;
}
CMonster::~CMonster()
{
m_SpellMgr.GetAffectedInfo().ClearAll();
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::InitMonster
//
// Description : 몬스터 초기화
//
// Inputs : Pos - 몬스터의 위치
// bDead - 처음 로긴 때는 true, 리스폰 시엔 false
//
// Outputs : None.
//
// Returns : 성공 여부.
///////////////////////////////////////////////////////////////////////////////////
bool CMonster::InitMonster(Position &Pos, bool bDead)
{
m_CellPos.MoveTo(Pos);
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG4(g_Log, "CID:0x%08x 범위를 벗어난 셀에서 몬스터가 로긴하였습니다. X:%.1f, Y:%.1f, Z:%.1f",
m_dwCID, Pos.m_fPointX, Pos.m_fPointY, Pos.m_fPointZ);
return false;
}
m_CurrentPos = Pos;
m_wSearchRange = m_wDefaultSearchRange;
m_lpTarget = NULL;
m_lCurrentFrame = 0;
m_bAttacking = false;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
ZeroMemory(&m_MotionInfo, sizeof(m_MotionInfo));
if (bDead)
{
m_nCurrentState = STATE_ID_DIE;
m_CreatureStatus.m_nNowHP = 0;
m_CreatureStatus.m_nNowMP = 0;
}
else
{
m_CellPos.m_lpCell->SetCreature(m_dwCID, this);
m_nCurrentState = STATE_ID_NORMAL;
m_CreatureStatus.m_nNowHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
m_CreatureStatus.m_nNowMP = m_CreatureStatus.m_StatusInfo.m_nMaxMP;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::GetMotion
//
// Description : 모션 정보를 얻음
//
// Inputs : MotionID - 모션 ID
//
// Outputs : Motion - 모션 정보
//
// Returns : bool - 정보가 없는 ID면 false.
///////////////////////////////////////////////////////////////////////////////////
bool CMonster::GetMotion(unsigned long MotionID, MotionInfo &Motion)
{
int nIndex = 0;
switch (MotionID)
{
case MonsterInfo::Z3D_CA_WALK: nIndex = 0; break;
case MonsterInfo::Z3D_CA_RUN: nIndex = 1; break;
case MonsterInfo::Z3D_CA_ATTACK: nIndex = 2; break;
case MonsterInfo::Z3D_CA_CASTING: nIndex = 3; break;
default: return false;
}
// 방향은 복사하면 안 된다.
Motion.m_wAction = m_MonsterInfo.m_MonsterMotions[nIndex].m_wAction;
Motion.m_dwFrame = m_MonsterInfo.m_MonsterMotions[nIndex].m_dwFrame;
Motion.m_fVelocity = m_MonsterInfo.m_MonsterMotions[nIndex].m_fVelocity;
return true;
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::IsOverlap
//
// Description : 몬스터가 겹쳐져 있는가?
//
// Inputs : None.
//
// Outputs : None.
//
// Returns : bool - 위 질문에 대한 Yes/No
///////////////////////////////////////////////////////////////////////////////////
bool CMonster::IsOverlap(void)
{
for (int nCellCount = 0; nCellCount < CCell::CONNECT_NUM; ++nCellCount)
{
CCell* pCell = m_CellPos.m_lpCell->GetConnectCell(nCellCount);
if (NULL == pCell || false == pCell->IsMonster())
{
continue;
}
CMonster* lpTempMonster = pCell->GetFirstMonster();
while (NULL != lpTempMonster)
{
if (this != lpTempMonster)
{
const float fDX = lpTempMonster->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX;
const float fDZ = lpTempMonster->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ;
if (fDX * fDX + fDZ * fDZ <= 1) {
return true;
}
}
lpTempMonster = pCell->GetNextMonster();
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::UpdateBehavior
//
// Description : 몬스터의 행동 설정
//
// Inputs : dwTick - 현재 틱카운트
//
// Outputs : None.
//
// Returns : None.
///////////////////////////////////////////////////////////////////////////////////
void CMonster::UpdateBehavior(unsigned long dwTick)
{
// 네임드 몬스터는 스턴/석화에 걸리지 않는다.
if (MonsterInfo::PATTERN_NAMED == m_MonsterInfo.m_cSkillPattern)
{
m_SpellMgr.GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::Stun);
m_SpellMgr.GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::StoneForm);
}
// 스턴/석화시엔 아무 행동도 하지 않는다.
if (GetEnchantInfo().GetFlag(Skill::SpellID::Stun) ||
GetEnchantInfo().GetFlag(Skill::SpellID::StoneForm))
{
m_lCurrentFrame = FPS;
return;
}
// 상태에 따른 몬스터 행동 처리를 한다.
switch (m_nCurrentState)
{
case STATE_ID_NORMAL: NormalBehavior(dwTick); break; // 보통 상태
case STATE_ID_ATTACK: AttackBehavior(dwTick); break; // 공격 상태 (거리가 멀어지면 쫓아가고...)
case STATE_ID_RETURN: ReturnBehavior(dwTick); break; // 리턴 상태 (제 위치로 돌아가는...)
case STATE_ID_ESCAPE: EscapeBehavior(); break; // 도망 상태
case STATE_ID_DIE: DeadBehavior(dwTick); break; // 죽은 상태 (리스폰 시간이 지나면 리스폰 하지~)
}
/*
// 몬스터 정보 로그
char logString[MAX_PATH];
switch (m_nCurrentState)
{
case STATE_ID_NORMAL: strcpy(logString, "Normal"); break;
case STATE_ID_ATTACK: strcpy(logString, "Attack"); break;
case STATE_ID_RETURN: strcpy(logString, "Return"); break;
case STATE_ID_ESCAPE: strcpy(logString, "Escape"); break;
case STATE_ID_DIE: strcpy(logString, "Die"); break;
}
DETLOG3(g_Log, "몬스터 정보 로그 - * State : %s\t* fX : %f\t* fZ : %f",
logString, GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointZ);
*/
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::Process
//
// Description : 몬스터 프로세싱
//
// Inputs : None.
//
// Outputs : None.
//
// Returns : bool - 모션 출력 중엔 false
///////////////////////////////////////////////////////////////////////////////////
bool CMonster::Process()
{
const unsigned long dwTick = CPulse::GetInstance().GetLastTick();
if (dwTick - m_dwLastBehaviorTick < FPS) {
return false;
}
if ((GetSerialNumber() % 3) != (CPulse::GetInstance().GetCurrentPulse() % 3))
{
return false;
}
const unsigned long dwFrame = ((dwTick - m_dwLastBehaviorTick) / FPS);
// 공격 모션은 따로 처리합니다. (이동하면서 공격 가능)
if (m_lCurrentFrame <= 0 || m_bAttacking == true)
{
if (m_lCurrentFrame <= 0 && m_bAttacking == true) {
m_bAttacking = false;
}
UpdateBehavior(dwTick);
}
// 틱 갱신
m_dwLastBehaviorTick = dwTick;
unsigned long dwSlowlyRate = (true == GetEnchantInfo().GetFlag(Skill::SpellID::Frozen)) ? 2 : 1;
m_lCurrentFrame -= (dwFrame / dwSlowlyRate);
return true;
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::MultiAttack
//
// Description : 몬스터 범위 공격 시 범위 내 타겟을 검색
//
// Inputs : None.
//
// Outputs : None.
//
// Returns : None.
///////////////////////////////////////////////////////////////////////////////////
bool CMonster::MultiAttack(void)
{
unsigned char cDefenderNum = 1;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM];
unsigned char nDefenserJudges[AtNode::MAX_DEFENDER_NUM];
ppAggresiveCreature[0] = m_lpTarget;
// TODO : 공격하는 방향을 설정합시다. (현재는 무조건 정면)
short nMaxDefenderNum = AtNode::MAX_DEFENDER_NUM;
std::fill_n(&nDefenserJudges[0], nMaxDefenderNum, ClientConstants::Judge_Front);
float fDir = CalcDir2D(GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointZ,
m_lpTarget->GetCurrentPos().m_fPointX, m_lpTarget->GetCurrentPos().m_fPointZ);
AtType attackType;
attackType.m_wType = AtType::RIGHT_MELEE;
char cTargetType = Skill::Target::ENEMY;
return CAggresiveCreature::MultiAttack(attackType, cDefenderNum, ppAggresiveCreature, nDefenserJudges,
GetCurrentPos(), fDir, m_MonsterInfo.m_wAttackRange / 100.0f, m_MonsterInfo.m_fAttackAngle, cTargetType);
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::CancelTarget
//
// Description : 타겟의 취소
//
// Inputs : bool bSaveThreat - 맞은 쓰레트 값을 유지하는가?
//
// Outputs : None.
//
// Returns : None.
///////////////////////////////////////////////////////////////////////////////////
void CMonster::CancelTarget(bool bSaveThreat)
{
if (NULL != m_lpTarget)
{
if (!bSaveThreat)
{
m_lpTarget->GetThreat().DeleteThreatened(this);
m_Threat.DeleteThreat(m_lpTarget);
}
// 파티가 있었다면.. 파티의 타겟에서 제거한다.
CMonsterParty* lpParty = reinterpret_cast<CMonsterParty*>(GetParty());
if (NULL != lpParty)
{
CMonsterParty::PartyTargetSet::iterator itr = lpParty->GetPartyTargetSet().find(m_lpTarget->GetCID());
if (itr != lpParty->GetPartyTargetSet().end())
{
lpParty->GetPartyTargetSet().erase(itr);
}
}
}
m_lpTarget = NULL;
m_bAttacking = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_LEAVE_PLAYER);
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::Dead
//
// Description : 몬스터의 사망 처리
//
// Inputs : None.
//
// Outputs : None.
//
// Returns : 성공 여부. (false면 타겟도 없는 주제에 죽은 경우)
///////////////////////////////////////////////////////////////////////////////////
bool CMonster::Dead(CAggresiveCreature* pOffencer)
{
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP);
// 어워드 처리
unsigned long aryItemID[AwardTable::MAX_DROP_ITEM + EliteBonus::MAX_BONUS_DROP_ITEM] = { 0, };
Item::CItem* aryItem[AwardTable::MAX_DROP_ITEM + EliteBonus::MAX_BONUS_DROP_ITEM] = { 0, };
unsigned long dwOwnerID = 0;
unsigned char cItemNum = m_Threat.GetAward(aryItemID, aryItem, &dwOwnerID);
Position SetPosition;
CCell::ItemInfo itemInfo;
CCell* lpDropCell = GetCellPos().m_lpCell;
if (NULL != lpDropCell)
{
for (unsigned char cDropIndex = 0; cDropIndex < cItemNum; ++cDropIndex)
{
if (0 != aryItemID[cDropIndex])
{
SetPosition = GetCurrentPos();
SetPosition.m_fPointX += cDropIndex;
if (cItemNum / 2 <= cDropIndex)
{
SetPosition.m_fPointX -= cItemNum / 2;
SetPosition.m_fPointZ += 1.0f;
}
unsigned long dwGold = (NULL == aryItem[cDropIndex]) ? (aryItemID[cDropIndex] & ~CCell::TYPE_CHECK_BIT) : 0;
if(pOffencer->GetStatus().m_nLevel<=GOLD_INC_LEVEL_LIMIT)
{
dwGold = static_cast<unsigned long>( dwGold * 1.5f );
}
lpDropCell->SetItem(SetPosition, aryItem[cDropIndex], dwGold, dwOwnerID, CCell::PARTY, itemInfo);
}
}
}
// 경험치를 분배한다.
const long lMaxThreat = m_Threat.GetMaxThreatAmount();
m_Threat.DivisionExp();
// 셀에서 몬스터를 일단 제거한다.
m_CellPos.m_lpCell->DeleteCreature(m_dwCID);
CAggresiveCreature *pCreature = m_Threat.GetMaxThreatCreature();
if (NULL != pCreature)
{
if (m_CreatureStatus.m_nLevel - pCreature->GetStatus().m_nLevel >= 3 &&
lMaxThreat > m_CreatureStatus.m_StatusInfo.m_nMaxHP * 0.8f)
{ // 레벨차가 3 이상 나는 몬스터를 잡았을 경우 로그를 찍음
RULLOG5(g_Log, "몬스터께서 돌아가셨습니다... Monster : 0x%08x (%d), Player : 0x%08x (%d), 획득 경험치 비율 : %.1f%%",
m_dwCID, m_CreatureStatus.m_nLevel, m_Threat.GetMaxThreatCreature()->GetCID(),
m_Threat.GetMaxThreatCreature()->GetStatus().m_nLevel, lMaxThreat * 100.0f / m_CreatureStatus.m_StatusInfo.m_nMaxHP);
}
CCharacter* lpOffencerCharacter =
(Creature::CT_PC == Creature::GetCreatureType(pCreature->GetCID()))
? static_cast<CCharacter*>(pCreature) : 0;
if (0 != lpOffencerCharacter)
{
// 퀘스트 트리거 발동
lpOffencerCharacter->CheckTrigger(Quest::TRIGGER_KILL, m_MonsterInfo.m_dwKID, m_CurrentPos, 1);
// 몬스터 살해 로그를 남긴다. 남기는 항목은 몬스터 레벨/떨군 아이템ID 및 등급, 개수등이다
GAMELOG::LogMonsterDead(*lpOffencerCharacter, GetCID(),
m_CreatureStatus.m_nLevel, aryItemID, cItemNum);
}
}
// 파티에서 제거
CParty* lpParty = GetParty();
if (NULL != lpParty)
{
lpParty->Leave(GetCID(), 0, GetMapIndex());
}
return CAggresiveCreature::Dead(pOffencer);
}
EnemyCheck::EnemyType CMonster::IsEnemy(CCreature* lpTarget, unsigned char* cResult)
{
if (NULL != lpTarget)
{
switch (Creature::GetCreatureType(lpTarget->GetCID()))
{
case Creature::CT_PC:
case Creature::CT_SUMMON:
case Creature::CT_SIEGE_OBJECT:
{
return lpTarget->IsEnemy(this);
}
case Creature::CT_NPC:
case Creature::CT_MONSTER:
case Creature::CT_STRUCT:
{
if (GetNation() == lpTarget->GetNation())
{
return EnemyCheck::EC_FRIEND;
}
return EnemyCheck::EC_ENEMY;
}
}
}
ERRLOG1(g_Log, "CID:0x%08x 피아식별할 타겟이 없습니다.", m_dwCID);
return EnemyCheck::EC_NEUTRAL;
}
void CMonster::Respawn(unsigned long dwTick)
{
Position RespawnPos(m_OriginalPosition.m_fPointX, m_OriginalPosition.m_fPointY, m_OriginalPosition.m_fPointZ);
if (m_nMovingPattern != FIXED && m_wRespawnArea > 0)
{
RespawnPos.m_fPointX += static_cast<float>(Math::Random::SimpleRandom(dwTick, m_wRespawnArea*2) - m_wRespawnArea);
RespawnPos.m_fPointY += m_OriginalPosition.m_fPointY;
RespawnPos.m_fPointZ += static_cast<float>(Math::Random::SimpleRandom(dwTick, m_wRespawnArea*2) - m_wRespawnArea);
}
InitMonster(RespawnPos);
m_nCurrentState = STATE_ID_NORMAL;
// 파티에 추가
CParty* lpParty = CPartyMgr::GetInstance().GetParty(GetPID());
if (NULL != lpParty)
{
SetParty(lpParty);
lpParty->Join(GetCID(), 0, NULL, GetMapIndex());
}
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::SendMove
//
// Description : 몬스터 이동 패킷 보내기
//
// Inputs : None.
//
// Outputs : Send를 보낸 횟수.
//
// Returns : None.
///////////////////////////////////////////////////////////////////////////////////
LONG CMonster::SendMove(unsigned short nAniNum)
{
// 길드 요새와 상징물은 MonMove 를 보내지 않는다. (공격시 방향 전환을 하지않도록...)
if (Creature::CT_SIEGE_OBJECT == Creature::GetCreatureType(m_dwCID))
{
CSiegeObject* lpSiegeObject = reinterpret_cast<CSiegeObject* >(this);
if (lpSiegeObject && (lpSiegeObject->IsCamp() || lpSiegeObject->IsEmblem()))
{
return 0;
}
}
LONG nSendCount = 0;
if (true == GetEnchantInfo().GetFlag(Skill::SpellID::Hold) ||
true == GetEnchantInfo().GetFlag(Skill::SpellID::Stun) ||
true == GetEnchantInfo().GetFlag(Skill::SpellID::StoneForm))
{
m_MotionInfo.m_fVelocity = 0;
}
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG0(g_Log, "몬스터 이동 패킷 보내기 실패 : 몬스터가 셀을 벗어나 있습니다.");
return nSendCount;
}
PktMM pktMM;
memset(&pktMM, 0, sizeof(PktMM));
pktMM.m_dwMonID = m_dwCID;
pktMM.m_NetworkPos = CNetworkPos(m_CurrentPos.m_fPointX, m_CurrentPos.m_fPointY, m_CurrentPos.m_fPointZ,
m_MotionInfo.m_fDirection, (0 == m_MotionInfo.m_dwFrame) ? 0.0f : m_MotionInfo.m_fVelocity / m_MotionInfo.m_dwFrame);
pktMM.m_cAct = static_cast<unsigned char>(m_MotionInfo.m_wAction);
pktMM.m_cAniNum = static_cast<unsigned char>(nAniNum);
// BroadCasting 한 번을 기준으로 카운트
if (0 != m_CellPos.m_lpCell)
{
++nSendCount;
m_CellPos.m_lpCell->SendAllNearCellCharacter(&pktMM, sizeof(PktMM), CmdMonMove);
}
else
{
ERRLOG4(g_Log, "CID:0x%08x 이상한 위치에 몬스터가 있습니다. (%f,%f,%f)", m_dwCID,
m_CurrentPos.m_fPointX, m_CurrentPos.m_fPointY, m_CurrentPos.m_fPointZ);
}
m_nLeaveMovingNum = nAniNum;
return nSendCount;
}
const int CMonster::CalculateFixLevelGap(CAggresiveCreature *pDefender)
{
// << 고정 레벨 갭 장치 >>
// - 캐릭터와 몬스터의 레벨에 상관없이 스크립트에 정의된 수치만큼 몬스터의 레벨이 높은 걸로 취급한다.
// - 보스몹 등에 이용한다.
if (true == m_MonsterInfo.m_bFixLevelGap)
{
const unsigned char cFixLevelGap = m_CreatureStatus.m_nLevel - m_MonsterInfo.m_cFixLevelGap;
if (cFixLevelGap < pDefender->GetStatus().m_nLevel)
{
return m_MonsterInfo.m_cFixLevelGap;
}
}
return CAggresiveCreature::CalculateLevelGap(pDefender);
}
///////////////////////////////////////////////////////////////////////////////////
// Function : CMonster::Attack
//
// Description : 몬스터 공격 패킷 보내기
//
// Inputs : pDefender - 방어자의 포인터
//
// Outputs : None.
//
// Returns : None.
///////////////////////////////////////////////////////////////////////////////////
bool CMonster::Attack(AtType attackType, unsigned char cDefenderNum,
CAggresiveCreature** ppDefenders, unsigned char* cDefenderJudges, unsigned short* wDefenderMPHeal)
{
if (m_CreatureStatus.m_nNowHP == 0)
{
ERRLOG1(g_Log, "CID:0x%08x 죽은 몬스터가 공격하려고 하였습니다.", m_dwCID);
return false;
}
if (cDefenderNum > AtNode::MAX_DEFENDER_NUM)
{
ERRLOG2(g_Log, "CID:0x%08x 몬스터가 공격할 때, 방어자의 숫자가 최대 방어자 숫자를 넘었습니다. 방어자수 : %d",
m_dwCID, cDefenderNum);
cDefenderNum = AtNode::MAX_DEFENDER_NUM;
}
// MON_TODO : by Vincent - 2004 : 2 : 25
DefenserNode Defenser[AtNode::MAX_DEFENDER_NUM] = {0, };
int nDefenserCount = 0;
// ------------
for (unsigned char cDefender = 0; cDefender < cDefenderNum; ++cDefender)
{
if (NULL == ppDefenders[cDefender]) { continue; }
CCharacter* lpCharacter = NULL;
CMonster* lpSummonee = NULL;
// 타켓이 캐릭터인 경우
if (Creature::CT_PC == Creature::GetCreatureType(ppDefenders[cDefender]->GetCID()))
{
lpCharacter = reinterpret_cast<CCharacter *>(ppDefenders[cDefender]);
lpSummonee = lpCharacter->GetSummonee();
}
else
{
// 타겟이 소환수인 경우
if (Creature::IsSummonMonster(ppDefenders[cDefender]->GetCID()))
{
lpCharacter = reinterpret_cast<CSummonMonster *>(ppDefenders[cDefender])->GetMaster();
}
}
if (NULL == lpCharacter) { continue; }
unsigned char cOffencerJudge = 0;
unsigned short wOffencerMPHeal = 0;
unsigned short wError = PktBase::NO_SERVER_ERR;
const unsigned short wDamage =
ppDefenders[cDefender]->ApplyDamage(attackType, this, cOffencerJudge, cDefenderJudges[cDefender], wOffencerMPHeal, wDefenderMPHeal[cDefender], wError);
if (NULL != lpSummonee)
{
lpSummonee->GuardMe(this, wDamage);
}
lpCharacter->CalculateEquipDurability((ClientConstants::Judge_Guard == cDefenderJudges[cDefender]) ?
AtType::GUARD : AtType::DEFENCE);
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
GameClientSendPacket::SendCharAttacked(lpDispatch->GetSendStream(), this, ppDefenders[cDefender],
attackType, m_MotionInfo.m_fDirection, wDamage, cDefenderJudges[cDefender], wDefenderMPHeal[cDefender], wError);
}
Defenser[nDefenserCount].m_cJudge = cDefenderJudges[nDefenserCount];
Defenser[nDefenserCount].m_dwCharID = ppDefenders[nDefenserCount]->GetCID();
Defenser[nDefenserCount].m_wMaxHP = ppDefenders[nDefenserCount]->GetStatus().m_StatusInfo.m_nMaxHP;
Defenser[nDefenserCount].m_wMaxMP = ppDefenders[nDefenserCount]->GetStatus().m_StatusInfo.m_nMaxMP;
Defenser[nDefenserCount].m_sCurrHP = ppDefenders[nDefenserCount]->GetStatus().m_nNowHP;
Defenser[nDefenserCount].m_sCurrMP = ppDefenders[nDefenserCount]->GetStatus().m_nNowMP;
Defenser[nDefenserCount].m_wMPHeal = wDefenderMPHeal[nDefenserCount];
Defenser[nDefenserCount].m_wDamage = wDamage;
++nDefenserCount;
}
CCell* lpCell = m_CellPos.m_lpCell;
if (NULL != lpCell)
{
lpCell->SendAttackInfo(GetCID(), attackType, static_cast<unsigned char>(nDefenserCount), &Defenser[0]);
}
return true;
}
bool CMonster::HasSkill()
{
for (int i=0; i<MonsterInfo::MAX_SKILL_PATTERN; ++i)
{
if (0 != m_MonsterInfo.m_wSkillID[i]) return true;
}
return false;
}
int CMonster::GetUseSkillNum() // 몇가지의 스킬을 가지고 있는가?
{
int count = 0;
for (int i=0; i<MonsterInfo::MAX_SKILL_PATTERN; ++i)
{
if (0 != m_MonsterInfo.m_wSkillID[i]) ++count;
}
return count;
}
unsigned long CMonster::GetDropRate(unsigned char cIndex)
{
switch (cIndex)
{
case MonsterInfo::LOTTERY:
{
if (false == CServerSetup::GetInstance().GetLotteryEvent())
{
return 0;
}
break;
}
case MonsterInfo::F_RING:
case MonsterInfo::D_RING:
case MonsterInfo::C_RING:
case MonsterInfo::B_RING:
case MonsterInfo::A_RING:
case MonsterInfo::F_NECKLACE:
case MonsterInfo::D_NECKLACE:
case MonsterInfo::C_NECKLACE:
case MonsterInfo::B_NECKLACE:
case MonsterInfo::A_NECKLACE:
{
if (false == CServerSetup::GetInstance().UseContents(GameRYL::ACCESSORY))
{
return 0;
}
break;
}
case MonsterInfo::F_RUNE:
case MonsterInfo::D_RUNE:
case MonsterInfo::C_RUNE:
case MonsterInfo::B_RUNE:
case MonsterInfo::A_RUNE:
case MonsterInfo::DESTRUCTION_RUNE:
{
if (false == CServerSetup::GetInstance().UseContents(GameRYL::RUNE))
{
return 0;
}
break;
}
}
return m_MonsterInfo.m_aryDropRate[cIndex];
}
bool CMonster::IsDeadSummonMonster(void)
{
if (m_bAdminCmdSummon)
{
return (STATE_ID_DIE == m_nCurrentState);
}
return false;
}
void CMonster::LogMonsterMoveCount()
{
SERLOG5(g_Log, "몬스터의 이동 전송 회수 로그를 출력합니다. "
"NormalBehaviorSendCount:%10d, AttackBehaviorSendCount:%10d, ReturnBehaviorSendCount:%10d, "
"EscapeBehaviorSendCount:%10d, DeadBehaviorSendCount:%10d ",
ms_NormalBehaviorSendCount, ms_AttackBehaviorSendCount, ms_ReturnBehaviorSendCount,
ms_EscapeBehaviorSendCount, ms_DeadBehaviorSendCount);
ms_NormalBehaviorSendCount = ms_AttackBehaviorSendCount = ms_ReturnBehaviorSendCount =
ms_EscapeBehaviorSendCount = ms_DeadBehaviorSendCount = 0;
}
unsigned short CMonster::ApplyDamage(AtType attackType, CAggresiveCreature* pOffencer, unsigned char &cOffencerJudge,
unsigned char &cDefenserJudge, unsigned short& wOffencerMPHeal, unsigned short& wDefenserMPHeal,
unsigned short &wError)
{
unsigned short usDamage = CAggresiveCreature::ApplyDamage(
attackType, pOffencer, cOffencerJudge, cDefenserJudge, wOffencerMPHeal, wDefenserMPHeal, wError);
// 몬스터 외치기 타입 찾기
CMonsterShout::Behavior eBehavior = CMonsterShout::NORMAL_ATTACK;
unsigned short usShoutSkill_ID = 0;
if (0 == (attackType.m_wType & AtType::SKILL_BIT))
{
// 스킬 공격이 아니다.
if (cDefenserJudge == ClientConstants::Judge_Critical)
{
eBehavior = CMonsterShout::CRITICAL_ATTACKED;
}
}
else
{
eBehavior = CMonsterShout::SKILL_ATTACKED;
usShoutSkill_ID = attackType.m_wType;
}
const char* szCharacterName = 0;
if (0 != pOffencer && Creature::CT_PC == Creature::GetCreatureType(pOffencer->GetCID()))
{
szCharacterName = static_cast<CCharacter*>(pOffencer)->GetCharacterName();
}
// 외치기 검색해서 외침.
CMonsterShout::GetInstance().Shout(m_dwCID, m_MonsterInfo.m_dwKID,
static_cast<unsigned short>(m_CurrentPos.m_fPointX),
static_cast<unsigned short>(m_CurrentPos.m_fPointZ),
eBehavior, szCharacterName, usShoutSkill_ID);
return usDamage;
}

View File

@@ -0,0 +1,271 @@
#ifndef _CMONSTER_H_
#define _CMONSTER_H_
#include <Creature/Character/Character.h>
#include <Creature/Monster/FSM/FSM.h>
#include <Map/FieldMap/Cell.h>
#include <Skill/Spell/SpellKind.h>
#include <GameEvent/GameEventMgr.h>
#include <Community/Party/Party.h>
#include "MonsterMgr.h"
const unsigned long LongRange = 45; // 장거리 공격에 의해 쫓아가는 거리.
const unsigned long OutsideSearchRange = 10; // 영역 밖에서의 시야
const unsigned long ReturnRange = 20; // 영역
const unsigned long MoveRange = 15; // 영역 안에서의 최대 이동거리.
// 전방 참조
class CStatue;
class CMonster : public CAggresiveCreature
{
public:
enum MovingPattern { AREA = 0, FIXED, ROUTE, NONAREA };
enum _Const
{
MONSTER_SEARCH_RANGE = 8,
BOSS_SEARCH_RANGE = 15,
NAMED_SEARCH_RANGE = 15,
CHIEF_SEARCH_RANGE = 15,
MAGE_SEARCH_RANGE = 18,
ACOLYTE_SEARCH_RANGE = 18,
GUARD_SEARCH_RANGE = 15,
GOLD_INC_LEVEL_LIMIT = 20,
RUN_ANI_LIMIT_MAX = 3,
RUN_ANI_LIMIT_MIN = 1,
RATE_MOVING_AND_PLAYER = 10,
MAX_MONSTER_UID = 0xFFFF - 0x8000 - 0x4000,
MONSTER_PARTY_BIT = 0x80000000
};
struct MonsterCreateInfo; // 몬스터 정보 구조체
typedef MonsterCreateInfo *LPMonsterCreateInfo;
CMonster();
CMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CMonster();
// --------------------------------------------------------------------------------------------
// 초기 설정
bool InitMonster(Position& Pos, bool bDead = false);
virtual bool GetMotion(unsigned long MotionID, MotionInfo& Motion); // 몬스터 모션 정보를 얻음.
// --------------------------------------------------------------------------------------------
// 몬스터 행동 관련
bool CheckPartyTarget(); // 몬스터 파티의 타겟을 체크해서 패턴을 변경하는가?
virtual void SearchPlayer(void); // 플레이어 검색
bool IsOverlap(void); // 다른 몬스터와 겹치는가 검색
bool Process(void); // 몬스터 행동 처리 - 내부에서 UpdateBehavior호출
void UpdateBehavior(unsigned long dwTick); // 몬스터 행동 업데이트
virtual void NormalBehavior(unsigned long dwTick); // 보통 상태
virtual void AttackBehavior(unsigned long dwTick); // 공격 상태
virtual void ReturnBehavior(unsigned long dwTick); // 귀환 상태
virtual void EscapeBehavior(void); // 도망 상태
virtual void DeadBehavior(unsigned long dwTick); // 죽은 상태
// Action 관련 처리
void AttackAction();
void WalkAttackAction(float fVelocity=0.1f);
void RunAction(float fDistance, float fDstX, float fDstZ);
bool IsReturn();
inline Position CalculateCoor(void); // 이동 후의 좌표 계산
virtual const int CalculateFixLevelGap(CAggresiveCreature *pDefender);
virtual bool Attack(AtType attackType, unsigned char cDefenderNum,
CAggresiveCreature** ppDefenders, unsigned char* cDefenderJudges, unsigned short* wDefenderMPHeal);
inline virtual void Attacked(void); // 공격을 받음
bool MultiAttack(void); // 타겟을 찾아 공격 (내부에서 Attack함수를 호출)
void CancelTarget(bool bSaveThreat = false); // 타겟을 취소
virtual bool Dead(CAggresiveCreature* pOffencer); // 죽었을 때 호출되는 함수
virtual EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget, unsigned char* cResult = NULL);
virtual void Respawn(unsigned long dwTick);
// --------------------------------------------------------------------------------------------
// 파티 관련
void SetPID(unsigned long dwPID) { m_dwPID = dwPID; }
unsigned long GetPID(void) { return m_dwPID; }
// --------------------------------------------------------------------------------------------
// 패킷을 만들어서 보내는 함수들
virtual LONG SendMove(unsigned short nAniNum);
// --------------------------------------------------------------------------------------------
// 기타 함수
virtual Item::CItem* SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError) { return NULL; }
bool HasSkill(void); // 스킬을 사용하는 몬스터인가?
int GetUseSkillNum(void); // 몇가지의 스킬을 가지고 있는가?
inline unsigned char GetOptionBaseNum(Item::EquipType::OptionType eType);
unsigned long GetDropRate(unsigned char nIndex);
unsigned long GetAwardItem(int nIndex) { return m_MonsterInfo.m_aryAwardItem[nIndex]; }
inline CStatue* DowncastToStatue(void);
// 몬스터 일련번호 얻기
int GetSerialNumber(void) { return ((m_dwCID - Creature::MONSTER_BIT) >> 16); }
unsigned short GetKID(void) { return static_cast<unsigned short>(m_MonsterInfo.m_dwKID); }
unsigned char GetPattern(void) { return m_MonsterInfo.m_cSkillPattern; }
int GetCurrentState(void) { return m_nCurrentState; }
short GetAttackRange(void) { return m_MonsterInfo.m_wAttackRange; }
bool IsDeadSummonMonster(void);
virtual void SetGuard(bool bGuard) { return; }
virtual unsigned short ApplyDamage(AtType attackType, CAggresiveCreature* pOffencer, unsigned char &cOffencerJudge,
unsigned char &cDefenserJudge, unsigned short& wOffencerMPHeal, unsigned short& wDefenserMPHeal, unsigned short &wError);
virtual void GuardMe(CAggresiveCreature* lpTarget, unsigned short wThreat) { return; }
virtual void AttackCmd(CAggresiveCreature* lpTarget, unsigned short wThreat) { return; }
virtual bool IsPeaceMode(void) { return false; }
virtual bool IsRideArms(void) const { return false; }
virtual unsigned char GetNation() const { return m_MonsterInfo.m_cNation; }
virtual unsigned char GetRealmPoint() { return 0; }
static void LogMonsterMoveCount(void);
Broadcast2nd::CSerializeMonsterData& GetSerializeData() { return m_SerializeMonsterData; }
protected:
// 각종 상수들
enum _Timing
{
FPS = 30
};
MonsterInfo m_MonsterInfo;
Position m_OriginalPosition; // 몬스터의 초기 위치
unsigned long m_dwLastBehaviorTick; // 마지막으로 행동한 시점의 Tick
Broadcast2nd::CSerializeMonsterData m_SerializeMonsterData;
CAggresiveCreature* m_lpTarget; // 공격할 타겟
long m_lCurrentFrame; // 현재 진행중인 Frame을 나타냄. 동작이 끝났는지를 확인하기 위해 삽입
int m_nNormalMovingDelay; // 노말 상태의 이동 타이밍 간격 (주위 플레이어 수에 따라 유동적)
int m_nLeaveMovingNum; // 남은 이동 수
int m_nMovingPattern; // 이동 패턴 (영역, 붙박이, 루트, 비영역)
int m_nCurrentState; // 현재 State
unsigned short m_wSearchRange; // 몬스터의 시야
unsigned short m_wDefaultSearchRange; // 몬스터의 기본 시야
bool m_bLongRangeAttacked; // 원거리 공격? (원거리 공격에 맞으면 시야를 넓혀 타겟을 찾음)
bool m_bScout; // 스카우터인가?
bool m_bAttacking; // 공격 중인가?
unsigned short m_wRespawnArea; // 리스폰 지점에서의 랜덤영역값
// 옵션 요소.
bool m_bAvoid; // 피할 턴인가? (피하기 옵션이 켜진 경우)
bool m_bAdminCmdSummon; // Admin 명령으로 소환한 몬스터인가?
// 파티 관련 PID
unsigned long m_dwPID; // 몬스터 파티 아이디
static LONG ms_NormalBehaviorSendCount;
static LONG ms_AttackBehaviorSendCount;
static LONG ms_ReturnBehaviorSendCount;
static LONG ms_EscapeBehaviorSendCount;
static LONG ms_DeadBehaviorSendCount;
};
struct CMonster::MonsterCreateInfo
{
Position m_Pos;
unsigned long m_dwCID;
unsigned long m_dwPID;
int m_nKID;
int m_nMovingPattern;
bool m_bScout;
unsigned short m_wRespawnArea;
MonsterCreateInfo()
: m_dwCID(0), m_nKID(0), m_dwPID(0), m_bScout(false), m_nMovingPattern(0), m_wRespawnArea(0)
{
}
};
Position CMonster::CalculateCoor(void)
{
float fVel = (true == GetEnchantInfo().GetFlag(Skill::SpellID::Slow)) ?
m_MotionInfo.m_fVelocity / 4 : m_MotionInfo.m_fVelocity;
return Position(m_CurrentPos.m_fPointX + fVel * cosf(m_MotionInfo.m_fDirection),
m_CurrentPos.m_fPointY, m_CurrentPos.m_fPointZ + fVel * sinf(m_MotionInfo.m_fDirection));
}
void CMonster::Attacked(void)
{
m_bLongRangeAttacked = true;
if (m_nCurrentState == STATE_ID_NORMAL || m_nCurrentState == STATE_ID_RETURN)
{
m_lCurrentFrame = 0;
}
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ATTACKED_PLAYER);
}
unsigned char CMonster::GetOptionBaseNum(Item::EquipType::OptionType eType)
{
switch (eType)
{
case Item::EquipType::STANDARD_OPTION: return m_MonsterInfo.m_cStandardBaseNum;
case Item::EquipType::OVER_OPTION: return m_MonsterInfo.m_cOverBaseNum;
}
return 0;
}
CStatue* CMonster::DowncastToStatue(void)
{
if (0 != this &&
MonsterInfo::MIN_STATUE_KID <= m_MonsterInfo.m_dwKID &&
MonsterInfo::MAX_STATUE_KID >= m_MonsterInfo.m_dwKID)
{
return reinterpret_cast<CStatue *>(this);
}
return NULL;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,460 @@
#include "stdafx.h"
#include <Utility/Math/Math.h>
#include <Utility/Compress/MiniLZO/MiniLZOWrapper.h>
#include <Utility/Resource/EnsureCleanup.h>
#include <Network/XORCrypt/XORCrypt.h>
// 클라이언트에서도 쓰이므로 include를 명시한다.
#include <Log/ServerLog.h>
#define ENCODEHEADER(Start_In, Length_In, PageNum_In, PageVer_In) CXORCrypt::GetInstance().EncodeHeader((Start_In), (Length_In), (PageNum_In), (PageVer_In))
#define DECODEHEADER(Start_In, Length_In, PageNum_In, PageVer_In) CXORCrypt::GetInstance().DecodeHeader((Start_In), (Length_In), (PageNum_In), (PageVer_In))
#define COMPRESS(In, In_len, Out, Out_len) CMiniLZO::Compress((In), (In_len), (Out), (Out_len))
#define DECOMPRESS(In, In_len, Out, Out_len) CMiniLZO::Decompress((In), (In_len), (Out), (Out_len))
#include <vector>
#include <algorithm>
#include <functional>
#include "MonsterMgr.h"
#include "GMMemory.h"
CMonsterMgr CMonsterMgr::ms_this;
const char* CMonsterMgr::m_szMonsterScriptFileName = "./Script/Game/MonsterProtoType.txt";
CMonsterMgr::CMonsterMgr(void)
: m_nMonsterNum(0), m_ProtoTypeArray(NULL)
{
}
CMonsterMgr::~CMonsterMgr(void)
{
ClearProtoType();
}
void CMonsterMgr::ClearProtoType()
{
if (NULL != m_ProtoTypeArray)
{
delete [] m_ProtoTypeArray;
}
}
bool CMonsterMgr::LoadMonstersFromFile(const char* szFileName)
{
int nIndex = 0;
int nLineCount = 0;
char strTemp[MAX_PATH];
float fTemp = 0;
CDelimitedFile DelimitedFile; // 객체 소멸시, 자동 Close.
std::vector<MonsterProtoType> monsterProtoTypeVector;
monsterProtoTypeVector.reserve(1000);
MonsterProtoType tempProtoType;
// 매크로에 로그 코드 삽입을 잊지 말 것.
// 매크로에서 \뒤에 공백이나 문자 삽입되지 않도록 주의할 것.
// ( '이스케이프 시퀀스가 잘못되었습니다' 에러 발생 )
#define READ_DATA(ColumnName, Argument) \
if (!DelimitedFile.ReadData(Argument)) { \
ERRLOG2(g_Log, "몬스터 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
}
#define READ_STRING(ColumnName, Buffer, BufferSize) \
if (!DelimitedFile.ReadString(Buffer, BufferSize)) { \
ERRLOG2(g_Log, "몬스터 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
}
#define READ_DATA_ARRAY(ColumnName, Argument, ArgumentNum) \
for (nIndex=0; nIndex < ArgumentNum; ++nIndex) { \
READ_DATA(ColumnName, Argument[nIndex]); \
}
#define READ_SKILLID_ARRAY(ColumnName, Argument, ArgumentNum) \
for (nIndex=0; nIndex < ArgumentNum; ++nIndex) { \
if (!DelimitedFile.ReadString(strTemp, MAX_PATH)) { \
ERRLOG2(g_Log, "몬스터 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
} \
char strString[MAX_PATH] = "0x0000"; \
strcat(strString, (strTemp+2)); \
Argument[nIndex] = static_cast<unsigned short>(Math::Convert::Atoi(strString)); \
}
#define READ_DATA_BOOL(ColumnName, Argument) \
if (!DelimitedFile.ReadString(strTemp, MAX_PATH)) { \
ERRLOG2(g_Log, "몬스터 스크립트 읽기 실패 : %d행 %s컬럼에서 에러 발생!", nLineCount, #ColumnName); \
return false; \
} \
Argument = (!strcmp(strTemp, "O")) ? true : false;
if (!DelimitedFile.Open(szFileName ? szFileName : m_szMonsterScriptFileName))
{
ERRLOG1(g_Log, "%s 파일을 열 수 없습니다.", szFileName ? szFileName : m_szMonsterScriptFileName);
return false;
}
while (DelimitedFile.ReadLine())
{
++nLineCount;
// 순서가 바뀌면 곤란하다니깐~!!! (버럭!)
READ_DATA("KID", tempProtoType.m_MonsterInfo.m_dwKID);
READ_STRING("이름", tempProtoType.m_MonsterInfo.m_strName, MonsterInfo::MAX_NAME_LEN);
READ_STRING("모델링 형태 플래그", tempProtoType.m_MonsterInfo.m_strModelingFlag, MonsterInfo::MAX_MODELING_FLAG_LENGTH);
READ_DATA_BOOL("LOD 사용 유무", tempProtoType.m_MonsterInfo.m_bUseLOD);
READ_DATA("국적", tempProtoType.m_MonsterInfo.m_cNation);
READ_STRING("스킬패턴", strTemp, MAX_PATH);
tempProtoType.m_MonsterInfo.m_cSkillPattern = MonsterInfo::GetMonsterPattern(strTemp);
READ_DATA_BOOL("인챈트무시여부", tempProtoType.m_MonsterInfo.m_bIgnoreEnchant);
// 모션 정보
tempProtoType.m_MonsterInfo.m_MonsterMotions[0].m_wAction = MonsterInfo::Z3D_CA_WALK;
READ_DATA("걷기프레임", tempProtoType.m_MonsterInfo.m_MonsterMotions[0].m_dwFrame);
READ_DATA("걷기거리", tempProtoType.m_MonsterInfo.m_MonsterMotions[0].m_fVelocity);
tempProtoType.m_MonsterInfo.m_MonsterMotions[0].m_fVelocity /= 100.0f; // 스크립트 값은cm 임. 미터로 변환함.
tempProtoType.m_MonsterInfo.m_MonsterMotions[1].m_wAction = MonsterInfo::Z3D_CA_RUN;
READ_DATA("달리기프레임", tempProtoType.m_MonsterInfo.m_MonsterMotions[1].m_dwFrame);
READ_DATA("달리기거리", tempProtoType.m_MonsterInfo.m_MonsterMotions[1].m_fVelocity);
tempProtoType.m_MonsterInfo.m_MonsterMotions[1].m_fVelocity /= 100.0f; // 스크립트 값은cm 임. 미터로 변환함.
tempProtoType.m_MonsterInfo.m_MonsterMotions[2].m_wAction = MonsterInfo::Z3D_CA_ATTACK;
READ_DATA("공격프레임", tempProtoType.m_MonsterInfo.m_MonsterMotions[2].m_dwFrame);
tempProtoType.m_MonsterInfo.m_MonsterMotions[3].m_wAction = MonsterInfo::Z3D_CA_CASTING;
READ_DATA("스킬프레임", tempProtoType.m_MonsterInfo.m_MonsterMotions[3].m_dwFrame);
READ_DATA_ARRAY("타격박스", tempProtoType.m_MonsterInfo.m_fHitBox, MonsterInfo::MAX_HITBOX_NUM);
READ_DATA("공격거리", tempProtoType.m_MonsterInfo.m_wAttackRange);
READ_DATA("공격각도", tempProtoType.m_MonsterInfo.m_fAttackAngle);
// 기본 정보
READ_DATA("기본 경험점", tempProtoType.m_CreatureStatus.m_nExp);
READ_DATA("레벨", tempProtoType.m_CreatureStatus.m_nLevel);
READ_DATA("최소데미지", tempProtoType.m_CreatureStatus.m_StatusInfo.m_lMinDamage);
READ_DATA("최대데미지", tempProtoType.m_CreatureStatus.m_StatusInfo.m_lMaxDamage);
READ_DATA("명중", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wHitRate);
READ_DATA("방어력", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wArmor);
READ_DATA("회피", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wEvade);
READ_DATA("블록", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wBlock);
READ_DATA("DRC", fTemp); // 기획팀의 편의를 위해 스크립트에서만 존재
READ_DATA("크리티컬", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wCritical);
READ_DATA("크리티컬타입", tempProtoType.m_CreatureStatus.m_StatusInfo.m_cCriticalType);
READ_DATA("마법력", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wMagicPower);
READ_DATA("저항력", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wMagicResist);
READ_DATA("속도", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wSpeed);
READ_DATA("HP Max", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMaxHP);
READ_DATA("MP Max", tempProtoType.m_CreatureStatus.m_StatusInfo.m_nMaxMP);
READ_DATA("HP 회복량", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wHPRegen);
READ_DATA("MP 회복량", tempProtoType.m_CreatureStatus.m_StatusInfo.m_wMPRegen);
// 기타
READ_DATA("사이즈", tempProtoType.m_MonsterInfo.m_fSize);
READ_DATA("스킬레벨", tempProtoType.m_MonsterInfo.m_cSkillLevel);
READ_DATA_BOOL("충돌타입여부", tempProtoType.m_MonsterInfo.m_bCollision);
READ_DATA_BOOL("선제공격여부", tempProtoType.m_MonsterInfo.m_bFirstAttack);
READ_DATA_BOOL("귀환여부", tempProtoType.m_MonsterInfo.m_bReturnPosition);
READ_DATA_BOOL("고정레벨갭적용여부", tempProtoType.m_MonsterInfo.m_bFixLevelGap);
READ_DATA("고정레벨갭", tempProtoType.m_MonsterInfo.m_cFixLevelGap);
READ_DATA("스킬사용 확률", tempProtoType.m_MonsterInfo.m_wSkillUseRate);
READ_SKILLID_ARRAY("몬스터 스킬ID", tempProtoType.m_MonsterInfo.m_wSkillID, MonsterInfo::MAX_SKILL_PATTERN);
READ_DATA("부여스킬 스펠타입", tempProtoType.m_MonsterInfo.m_cEnchantSpellType);
READ_DATA("챈트스킬 스펠타입", tempProtoType.m_MonsterInfo.m_cChantSpellType);
READ_DATA("스킬이펙트 사이즈", tempProtoType.m_MonsterInfo.m_fSkillEffectSize);
READ_DATA("리스폰타임", tempProtoType.m_MonsterInfo.m_dwRespawnTime);
READ_DATA_ARRAY("오리지날아이템", tempProtoType.m_MonsterInfo.m_aryAwardItem, MonsterInfo::MAX_ORIGINAL_ITEM_NUM);
READ_DATA_ARRAY("아이템드랍률", tempProtoType.m_MonsterInfo.m_aryDropRate, MonsterInfo::MAX_AWARD_KIND);
READ_DATA("Standard-Option Item Base", tempProtoType.m_MonsterInfo.m_cStandardBaseNum);
READ_DATA("Over-Option Item Base", tempProtoType.m_MonsterInfo.m_cOverBaseNum);
READ_STRING("석상 효과 타입", strTemp, MAX_PATH);
tempProtoType.m_MonsterInfo.m_cStatueEffectType = MonsterInfo::GetStatueEffectType(strTemp);
READ_DATA("석상 효과 퍼센트", tempProtoType.m_MonsterInfo.m_cStatueEffectPercent);
READ_DATA_BOOL("석상 리스폰 가능 여부", tempProtoType.m_MonsterInfo.m_bStatueRespawnEnable);
// 공성 오브젝트 정보 읽어들이기
READ_DATA("개발 비용", tempProtoType.m_MonsterInfo.m_dwDevelopGold);
READ_DATA("개발 속도", tempProtoType.m_MonsterInfo.m_cDevelopSpeed);
READ_DATA("업그레이드 비용", tempProtoType.m_MonsterInfo.m_dwUpgradeGold);
READ_DATA("업그레이드 속도", tempProtoType.m_MonsterInfo.m_cUpgradeSpeed);
READ_DATA("개발 비용 절감", tempProtoType.m_MonsterInfo.m_fDevelopGoldDown);
READ_DATA("개발 속도 향상", tempProtoType.m_MonsterInfo.m_fDevelopSpeedUp);
READ_DATA("업그레이드 비용 절감", tempProtoType.m_MonsterInfo.m_fUpgradeGoldDown);
READ_DATA("업그레이드 속도 향상", tempProtoType.m_MonsterInfo.m_fUpgradeSpeedUp);
READ_DATA("방어력 향상", tempProtoType.m_MonsterInfo.m_fDefenseUp);
READ_DATA("공경력 향상", tempProtoType.m_MonsterInfo.m_fOffenseUp);
READ_DATA("HP 향상", tempProtoType.m_MonsterInfo.m_fHPUp);
READ_DATA("총 수입 보너스", tempProtoType.m_MonsterInfo.m_fBonusRate);
READ_DATA("PC 리스폰 속도 향상", tempProtoType.m_MonsterInfo.m_fRespawnSpeedUp);
monsterProtoTypeVector.push_back(tempProtoType);
}
std::sort(monsterProtoTypeVector.begin(), monsterProtoTypeVector.end());
for (std::vector<MonsterProtoType>::iterator itr = monsterProtoTypeVector.begin();
itr != monsterProtoTypeVector.end() - 1; ++itr)
{
if (itr->m_MonsterInfo.m_dwKID == (itr+1)->m_MonsterInfo.m_dwKID)
{
ERRLOG1(g_Log, "겹치는 몬스터 종류 ID가 있습니다. 종류ID:%d", itr->m_MonsterInfo.m_dwKID);
return false;
}
}
m_nMonsterNum = monsterProtoTypeVector.size();
m_ProtoTypeArray = new MonsterProtoType[m_nMonsterNum];
if (NULL == m_ProtoTypeArray)
{
ERRLOG0(g_Log, "몬스터 스크립트 초기화 실패 : 메모리 부족");
return false;
}
std::copy(monsterProtoTypeVector.begin(), monsterProtoTypeVector.end(), m_ProtoTypeArray);
return true;
}
class CFindProtoTypeFromKID : public std::unary_function<CMonsterMgr::MonsterProtoType, bool>
{
public:
explicit CFindProtoTypeFromKID(unsigned long dwKID)
: m_dwKID(dwKID)
{ }
bool operator() (CMonsterMgr::MonsterProtoType& protoType)
{
return (m_dwKID == protoType.m_MonsterInfo.m_dwKID);
}
private:
const unsigned long m_dwKID;
};
const CMonsterMgr::MonsterProtoType* CMonsterMgr::GetMonsterProtoType(unsigned long dwKID)
{
MonsterProtoType* lpFirst = m_ProtoTypeArray;
MonsterProtoType* lpLast = m_ProtoTypeArray + m_nMonsterNum;
MonsterProtoType* lpMid = NULL;
size_t nCount = m_nMonsterNum;
size_t nCount2 = 0;
for (; 0 < nCount; )
{
nCount2 = nCount / 2;
lpMid = lpFirst + nCount2;
if (lpMid->m_MonsterInfo.m_dwKID < dwKID)
{
lpFirst = ++lpMid, nCount -= nCount2 + 1;
}
else
{
nCount = nCount2;
}
}
return (lpFirst != lpLast && !(dwKID < lpFirst->m_MonsterInfo.m_dwKID)) ? lpFirst : NULL;
}
const CMonsterMgr::MonsterProtoType* CMonsterMgr::GetMonsterProtoType(char* szName)
{
for (size_t nIndex = 0; nIndex < m_nMonsterNum; nIndex++)
{
if (0 == strncmp(szName, m_ProtoTypeArray[nIndex].m_MonsterInfo.m_strName, MonsterInfo::MAX_NAME_LEN))
{
return m_ProtoTypeArray + nIndex;
}
}
return NULL;
}
bool CMonsterMgr::LoadMonstersFromBinary(const char* szFileNameBinary)
{
HANDLE hFile = CreateFile((0 == szFileNameBinary) ? m_szMonsterScriptFileName : szFileNameBinary,
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) { return false; }
CEnsureCloseHandle readFile(hFile);
unsigned long dwRead = 0;
unsigned long dwFileHighSize = 0;
unsigned long dwFileSize = GetFileSize(hFile, &dwFileHighSize);
char* lpAllocated = new char[dwFileSize];
if (NULL == lpAllocated)
{
return false;
}
CEnsureDeleteArray<char> allocated(lpAllocated);
if (!ReadFile(hFile, lpAllocated, dwFileSize, &dwRead, NULL))
{
return false;
}
MonsterProtoType* lpProtoType = 0;
MonsterInfo* lpMonsterInfo = 0;
CreatureStatus* lpCreatureStatus = 0;
unsigned long dwStructSize = 0;
unsigned long dwHeaderSize = sizeof(unsigned long) + *reinterpret_cast<unsigned long*>(lpAllocated) + sizeof(unsigned long);
unsigned long dwBufferSize = *reinterpret_cast<unsigned long*>(lpAllocated + dwHeaderSize - sizeof(unsigned long));
char* lpBuffer = new char[dwBufferSize];
if (NULL == lpBuffer)
{
return false;
}
CEnsureDeleteArray<char> buffer(lpBuffer);
char* lpBufferStartPointer = lpBuffer;
DECOMPRESS(lpAllocated + dwHeaderSize, dwFileSize - dwHeaderSize, lpBuffer, &dwBufferSize);
DECODEHEADER(lpBuffer, dwBufferSize, 3, 2);
m_nMonsterNum = dwBufferSize / (sizeof(MonsterInfo) + sizeof(CreatureStatus));
m_ProtoTypeArray = new MonsterProtoType[m_nMonsterNum];
if (NULL == m_ProtoTypeArray)
{
return false;
}
for (size_t nIndex = 0; nIndex < m_nMonsterNum; ++nIndex)
{
lpMonsterInfo = reinterpret_cast<MonsterInfo*>(lpBuffer);
lpCreatureStatus = reinterpret_cast<CreatureStatus*>(lpBuffer + sizeof(MonsterInfo));
m_ProtoTypeArray[nIndex].m_MonsterInfo = MonsterInfo(*lpMonsterInfo);
dwBufferSize -= sizeof(MonsterInfo);
lpBuffer += sizeof(MonsterInfo);
m_ProtoTypeArray[nIndex].m_CreatureStatus = CreatureStatus(*lpCreatureStatus);
dwBufferSize -= sizeof(CreatureStatus);
lpBuffer += sizeof(CreatureStatus);
}
return true;
}
bool CMonsterMgr::SaveMonstersToBinary(const char* szFileNameBinary, const char* szTrashFile)
{
if (0 == m_ProtoTypeArray)
{
return false;
}
HANDLE hFile = CreateFile((0 == szFileNameBinary) ? m_szMonsterScriptFileName : szFileNameBinary,
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) { return false; }
CEnsureCloseHandle writeFile(hFile);
const size_t MAX_SCRIPT_FILE_SIZE = m_nMonsterNum * (sizeof(MonsterInfo) + sizeof(CreatureStatus));
char *pInputBuffer = new char[MAX_SCRIPT_FILE_SIZE];
char *pOutputBuffer = new char[MAX_SCRIPT_FILE_SIZE];
CEnsureDeleteArray<char> input(pInputBuffer);
CEnsureDeleteArray<char> output(pOutputBuffer);
if (0 == pInputBuffer || 0 == pOutputBuffer)
{
return false;
}
char *InputStartPointer = pInputBuffer;
char *OutputStartPointer = pOutputBuffer;
unsigned long dwInputBufferSize = 0;
unsigned long dwOutputBufferSize = 0;
for (size_t nCount = 0; nCount < m_nMonsterNum; ++nCount)
{
memcpy(pInputBuffer, &m_ProtoTypeArray[nCount].m_MonsterInfo, sizeof(MonsterInfo));
dwInputBufferSize += sizeof(MonsterInfo);
pInputBuffer += sizeof(MonsterInfo);
memcpy(pInputBuffer, &m_ProtoTypeArray[nCount].m_CreatureStatus, sizeof(CreatureStatus));
dwInputBufferSize += sizeof(CreatureStatus);
pInputBuffer += sizeof(CreatureStatus);
}
ENCODEHEADER(InputStartPointer, dwInputBufferSize, 3, 2);
COMPRESS(InputStartPointer, dwInputBufferSize, pOutputBuffer, &dwOutputBufferSize);
unsigned long dwWritten = 0;
// 쓰레기(더미) 자료
HANDLE hTrashFile = CreateFile(szTrashFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hTrashFile == INVALID_HANDLE_VALUE)
{
ERRLOG1(g_Log, "%s 파일을 열 수 없습니다.", szTrashFile);
return false;
}
CEnsureCloseHandle trashFile(hTrashFile);
unsigned long dwRead = 0;
unsigned long dwFileHighSize = 0;
unsigned long dwFileSize = GetFileSize(hTrashFile, &dwFileHighSize);
char* lpAllocated = new char[dwFileSize];
if (NULL == lpAllocated)
{
ERRLOG0(g_Log, "메모리 할당에 실패하였습니다.");
return false;
}
CEnsureDeleteArray<char> allocated(lpAllocated);
if (false == ReadFile(hTrashFile, lpAllocated, dwFileSize, &dwRead, NULL))
{
ERRLOG0(g_Log, "쓰레기 파일을 읽을 수 없습니다.");
return false;
}
WriteFile(hFile, &dwFileSize, sizeof(unsigned long), &dwWritten, 0);
WriteFile(hFile, lpAllocated, dwFileSize, &dwWritten, 0);
// 올바른 자료
WriteFile(hFile, &dwInputBufferSize, sizeof(unsigned long), &dwWritten, 0);
WriteFile(hFile, pOutputBuffer, dwOutputBufferSize, &dwWritten, 0);
return true;
}

View File

@@ -0,0 +1,53 @@
#ifndef _MONSTER_MGR_H_
#define _MONSTER_MGR_H_
#pragma once
#define g_MonsterMgr CMonsterMgr::GetInstance()
#include <Pattern/Singleton.h>
#include <Utility/DelimitedFile.h>
#include <Creature/Character/CharacterStructure.h>
#include "MonsterStructure.h"
class CMonsterMgr : public CSingleton<CMonsterMgr>
{
public:
struct MonsterProtoType
{
MonsterInfo m_MonsterInfo;
CreatureStatus m_CreatureStatus;
inline bool operator < (MonsterProtoType& rhs)
{ return m_MonsterInfo.m_dwKID < rhs.m_MonsterInfo.m_dwKID; }
};
~CMonsterMgr();
bool LoadMonstersFromFile(const char* szFileName = 0);
bool LoadMonstersFromBinary(const char* szFileNameBinary = 0);
bool SaveMonstersToBinary(const char* szFileNameBinary = 0, const char* szTrashFile = 0);
void ClearProtoType();
const MonsterProtoType* GetMonsterProtoType(unsigned long dwKID);
const MonsterProtoType* GetMonsterProtoType(char* szName);
size_t GetMonsterKindNum(void) { return m_nMonsterNum; }
private:
CMonsterMgr();
static const char* m_szMonsterScriptFileName;
static CMonsterMgr ms_this;
MonsterProtoType* m_ProtoTypeArray;
size_t m_nMonsterNum;
};
#endif

View File

@@ -0,0 +1,319 @@
#include "stdafx.h"
#include "MonsterShout.h"
#include "MonsterMgr.h"
#include "ScriptEngine/ScriptEngine.h"
#include <Network/Packet/ChatPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Dispatch/Chat/ChatDispatch.h>
#include <Map/FieldMap/Cell.h>
#include <Utility/Math/Math.h>
#include <Creature/CreatureManager.h>
#include <Creature/Monster/Monster.h>
const char* CMonsterShout::ms_DefaultFileName = "./Script/Game/MonsterChat.gsf";
CMonsterShout& CMonsterShout::GetInstance()
{
static CMonsterShout monsterShout;
return monsterShout;
}
CMonsterShout::CMonsterShout()
{
}
CMonsterShout::~CMonsterShout()
{
ShoutMap::iterator pos = m_ShoutMap.begin();
ShoutMap::iterator end = m_ShoutMap.end();
for(; pos != end; ++pos)
{
ShoutInfo& shoutInfo = pos->second;
ChatNode* lpNode = shoutInfo.m_lpNextNode;
ChatNode* lpDeleteNode = lpNode;
while(0 != lpNode)
{
lpDeleteNode = lpNode;
lpNode = lpNode->m_lpNextNode;
delete lpDeleteNode;
}
}
}
void SetShoutText(int nKID, int nBehavior, int nSkill_ID,
int nChatType, int nPercentage, const char* szMessage)
{
CMonsterShout::ShoutInfo shoutInfo;
CMonsterShout::ChatNode chatNode;
memset(&shoutInfo, 0, sizeof(CMonsterShout::ShoutInfo));
memset(&chatNode, 0, sizeof(CMonsterShout::ChatNode));
shoutInfo.m_nKID = nKID;
shoutInfo.m_nBehavior = nBehavior;
shoutInfo.m_nSkill_ID = nSkill_ID;
shoutInfo.m_nChatType = nChatType;
chatNode.m_nPercentage = nPercentage;
const CMonsterMgr::MonsterProtoType* lpMonsterProtoType =
CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if(0 != szMessage && 0 != lpMonsterProtoType)
{
if(shoutInfo.m_nBehavior != CMonsterShout::RESPAWN)
{
_snprintf(chatNode.m_szMonsterChat, MAX_PATH - 1, "%s : %s",
lpMonsterProtoType->m_MonsterInfo.m_strName, szMessage);
}
else
{
_snprintf(chatNode.m_szMonsterChat, MAX_PATH - 1, "%s", szMessage);
}
chatNode.m_szMonsterChat[MAX_PATH - 1] = 0;
chatNode.m_usChatLength =
static_cast<unsigned short>(strlen(chatNode.m_szMonsterChat)) + 1;
CMonsterShout::GetInstance().AddMonsterShout(shoutInfo, chatNode);
}
}
bool CMonsterShout::LoadScript(const char* szFileName)
{
SCRIPT Script = _SE_Create(szFileName);
if (NULL == Script)
{
return false;
}
_SE_RegisterFunction(Script, SetShoutText, T_VOID,
"MonsterChat", T_INT, T_INT, T_INT, T_INT, T_INT, T_STRING, 0);
_SE_Execute(Script);
_SE_Destroy(Script);
return true;
}
void CMonsterShout::Shout(unsigned long dwMonsterCID, unsigned long nKID,
unsigned short usXPos, unsigned short usZPos,
Behavior eBehavior, const char* szName, unsigned short usSkill_ID)
{
// KID로 검색한다.
// 확률 및 요건을 검사한다.
std::pair<ShoutMap::iterator, ShoutMap::iterator> result = m_ShoutMap.equal_range(nKID);
std::vector<int> shoutedList;
for(; result.first != result.second; ++result.first)
{
ShoutInfo& shoutInfo = result.first->second;
bool bChatSend = false;
if(shoutInfo.m_nBehavior == eBehavior)
{
if(SKILL_ATTACK == eBehavior || SKILL_ATTACKED == eBehavior)
{
// 스킬에 공격당한 경우는 스킬 타입도 확인해야 한다.
if(shoutInfo.m_nSkill_ID == 0xFFFF || shoutInfo.m_nSkill_ID == usSkill_ID)
{
bChatSend = true;
}
}
else
{
bChatSend = true;
}
// 이미 외친 ChatType이면 외치지 않는다.
if(shoutedList.end() !=
std::find(shoutedList.begin(), shoutedList.end(), shoutInfo.m_nChatType))
{
bChatSend = false;
}
int nRand = Math::Random::ComplexRandom(100, 1);
if(bChatSend && (nRand < shoutInfo.m_nTotalPercentage))
{
// 여러 메시지중에서 어느 메시지를 보낼지를 결정한다.
int nPercentage = 0;
ChatNode* lpNode = shoutInfo.m_lpNextNode;
while(0 != lpNode)
{
if(nPercentage <= nRand && nRand <= nPercentage + lpNode->m_nPercentage)
{
// 원하는 구간 안에 랜덤값이 있으면 Break;
break;
}
nPercentage += lpNode->m_nPercentage;
lpNode = lpNode->m_lpNextNode;
}
if(0 != lpNode)
{
shoutedList.push_back(shoutInfo.m_nChatType);
// 몬스터 채팅을 채팅서버로 보낸다. usState는 X좌표, usError는 Z좌표이다.
// 노멀 채팅일때만 좌표가 의미가 있다.
char szChatBuffer[PktChat::PktChatMaxSize * 2];
char szNameBuffer[PktChat::PktChatMaxSize];
szChatBuffer[0] = 0;
szNameBuffer[0] = 0;
if(0 != szName)
{
// 이름 복사하기..
_snprintf(szNameBuffer, PktChat::PktChatMaxSize - 1, "%s", szName);
szNameBuffer[PktChat::PktChatMaxSize - 1];
}
const char* szTarget = 0;
char* szTargetPos = 0;
size_t nTargetLen = 0;
char* szMessageResult = 0;
size_t nMessageLen = 0;
switch(shoutInfo.m_nBehavior)
{
case NORMAL_ATTACK:
case SKILL_ATTACK:
case CRITICAL_ATTACK:
szTarget = "$DEFNAME$";
break;
case NORMAL_ATTACKED:
case SKILL_ATTACKED:
case CRITICAL_ATTACKED:
szTarget = "$ATTNAME$";
break;
case DEAD:
szTarget = "$KILLERNAME$";
break;
}
if(0 != szTarget && (szTargetPos = strstr(lpNode->m_szMonsterChat, szTarget)))
{
// 치환해서 문자열 복사.
nTargetLen = strlen(szTarget);
const char* szLastMessage = szTargetPos + nTargetLen;
size_t nPreMessageLen = szTargetPos - lpNode->m_szMonsterChat;
memcpy(szChatBuffer, lpNode->m_szMonsterChat, nPreMessageLen);
nMessageLen = _snprintf(szChatBuffer + nPreMessageLen,
PktChat::PktChatMaxSize * 2 - 1, "%s%s", szNameBuffer, szLastMessage);
szMessageResult = szChatBuffer;
nMessageLen += nPreMessageLen;
}
else
{
szMessageResult = lpNode->m_szMonsterChat;
nMessageLen = lpNode->m_usChatLength;
}
PktChat::PktChatCmd eChatCmd = static_cast<PktChat::PktChatCmd>(shoutInfo.m_nChatType);
CChatPacket chatPacket(szMessageResult, dwMonsterCID, eChatCmd, 0);
if(chatPacket.IsValid())
{
// 챗 타입은 공지/일반/외치기 뿐이다.
switch(eChatCmd)
{
case PktChat::SHOUT:
case PktChat::NOTICE:
// 몬스터가 속한 존에만 전부 전송한다.
CCreatureManager::GetInstance().SendAllCharacter(
chatPacket.GetCompressedPacket(),
chatPacket.GetCompressedSize(), CmdCharChat);
break;
case PktChat::NORMAL:
{
// 몬스터 주변 8개 셀에 전부 메시지를 보낸다.
CMonster* lpMonster =
CCreatureManager::GetInstance().GetMonster(dwMonsterCID);
CCell* lpCell = 0;
if(0 != lpMonster && 0 != (lpCell = lpMonster->GetCellPos().m_lpCell))
{
lpCell->SendAllNearCellCharacter(
chatPacket.GetPacketData(),
chatPacket.GetPacketSize(), CmdCharChat);
}
}
break;
}
}
}
}
}
};
}
void CMonsterShout::AddMonsterShout(ShoutInfo& shoutInfo, ChatNode& chatNode)
{
std::pair<ShoutMap::iterator, ShoutMap::iterator> result = m_ShoutMap.equal_range(shoutInfo.m_nKID);
for(; result.first != result.second; ++result.first)
{
ShoutInfo& foundShoutInfo = result.first->second;
if(foundShoutInfo.m_nKID == shoutInfo.m_nKID &&
foundShoutInfo.m_nBehavior == shoutInfo.m_nBehavior &&
foundShoutInfo.m_nSkill_ID == shoutInfo.m_nSkill_ID &&
foundShoutInfo.m_nChatType == shoutInfo.m_nChatType)
{
foundShoutInfo.m_nTotalPercentage += chatNode.m_nPercentage;
ChatNode* lpNode = foundShoutInfo.m_lpNextNode;
while(0 != lpNode && 0 != lpNode->m_lpNextNode)
{
lpNode = lpNode->m_lpNextNode;
}
if(0 != lpNode && 0 == lpNode->m_lpNextNode)
{
lpNode->m_lpNextNode = new ChatNode(chatNode);
}
break;
}
}
if(result.first == result.second)
{
shoutInfo.m_lpNextNode = new ChatNode(chatNode);
shoutInfo.m_nTotalPercentage = chatNode.m_nPercentage;
m_ShoutMap.insert(std::make_pair(shoutInfo.m_nKID, shoutInfo));
}
}

View File

@@ -0,0 +1,69 @@
#ifndef _MONSTER_SHOUT_H_
#define _MONSTER_SHOUT_H_
#include <map>
#include <string>
class CMonsterShout
{
public:
enum Behavior
{
NORMAL_ATTACK = 0,
NORMAL_ATTACKED = 1,
SKILL_ATTACK = 2,
SKILL_ATTACKED = 3,
CRITICAL_ATTACK = 4,
CRITICAL_ATTACKED = 5,
RESPAWN = 6,
DEAD = 7
};
struct ChatNode
{
ChatNode* m_lpNextNode;
int m_nPercentage;
unsigned short m_usChatLength;
char m_szMonsterChat[MAX_PATH];
};
struct ShoutInfo
{
int m_nKID;
int m_nBehavior;
int m_nSkill_ID;
int m_nChatType;
int m_nTotalPercentage;
ChatNode* m_lpNextNode;
};
static CMonsterShout& GetInstance();
bool LoadScript(const char* szFileName = ms_DefaultFileName);
void AddMonsterShout(ShoutInfo& shoutInfo, ChatNode& chatNode);
Behavior GetBehavior(unsigned short usAttackType, unsigned char cDefenseJudge);
void Shout(unsigned long dwMonsterCID, unsigned long nKID,
unsigned short usXPos, unsigned short usZPos,
Behavior eBehavior, const char* szName = 0, unsigned short usSkill_ID = 0);
private:
CMonsterShout();
~CMonsterShout();
static const char* ms_DefaultFileName;
// KID / ShoutInfo
typedef std::multimap<int, ShoutInfo> ShoutMap;
ShoutMap m_ShoutMap;
};
#endif

View File

@@ -0,0 +1,421 @@
#include "stdafx.h"
#include <algorithm>
#include <Creature/Character/CharacterClass.h>
#include "MonsterStructure.h"
#include "GMMemory.h"
MonsterInfo::MonsterInfo()
: m_dwKID(0), m_dwRespawnTime(0), m_fSize(0), m_fAttackAngle(0), m_cSkillPattern(0), m_cSkillLevel(0),
m_cNation(Creature::STATELESS), m_cFixLevelGap(0), m_bFixLevelGap(false), m_bCollision(false),
m_bFirstAttack(false), m_bReturnPosition(false),
m_cEnchantSpellType(0), m_cChantSpellType(0), m_wSkillUseRate(0), m_fSkillEffectSize(1.0f),
m_cStatueEffectType(SE_NONE), m_cStatueEffectPercent(0), m_bStatueRespawnEnable(false)
{
std::fill_n(m_aryAwardItem, int(MAX_ORIGINAL_ITEM_NUM), 0);
std::fill_n(m_aryDropRate, int(MAX_AWARD_KIND), 0);
std::fill_n(m_strName, int(MAX_NAME_LEN), 0);
std::fill_n(m_strModelingFlag, int(MAX_MODELING_FLAG_LENGTH), 0);
std::fill_n(m_fHitBox, int(MAX_HITBOX_NUM), 0.0f);
std::fill_n(m_wSkillID, int(MAX_SKILL_PATTERN), 0);
}
MonsterInfo::MonsterPattern MonsterInfo::GetMonsterPattern(const char* szMonsterType)
{
struct TypeAndName
{
const char* m_szName;
const MonsterPattern m_MonsterPattern;
TypeAndName(const char* szName, const MonsterPattern ePattern)
: m_szName(szName), m_MonsterPattern(ePattern) { }
};
static TypeAndName monsterTypeName[MAX_PATTERN] =
{
TypeAndName("Common", PATTERN_COMMON),
TypeAndName("Warrior", PATTERN_WARRIOR),
TypeAndName("Defender", PATTERN_DEFENDER),
TypeAndName("Mage", PATTERN_MAGE),
TypeAndName("Acolyte", PATTERN_ACOLYTE),
TypeAndName("Boss", PATTERN_BOSS),
TypeAndName("BG", PATTERN_BG),
TypeAndName("Summon", PATTERN_SUMMON),
TypeAndName("Structure", PATTERN_STRUCTURE),
TypeAndName("Named", PATTERN_NAMED),
TypeAndName("Chief", PATTERN_CHIEF),
TypeAndName("Object", PATTERN_OBJECT),
TypeAndName("Guard", PATTERN_GUARD),
TypeAndName("Gather", PATTERN_GATHER)
};
TypeAndName* lpTypeNamePastEnd = monsterTypeName + MAX_PATTERN;
for (TypeAndName* lpTypeName = monsterTypeName; lpTypeName != lpTypeNamePastEnd; ++lpTypeName)
{
if (0 == strcmp(szMonsterType, lpTypeName->m_szName))
{
return lpTypeName->m_MonsterPattern;
}
}
return PATTERN_COMMON;
}
MonsterInfo::StatueEffectType MonsterInfo::GetStatueEffectType(const char* szStatueEffectType)
{
struct TypeAndName
{
const char* m_szName;
const StatueEffectType m_StatueEffectType;
TypeAndName(const char* szName, const StatueEffectType eEffectType)
: m_szName(szName), m_StatueEffectType(eEffectType) { }
};
static TypeAndName effectTypeName[MAX_EFFECT_TYPE_NUM] =
{
TypeAndName("None", SE_NONE),
TypeAndName("HP", SE_HP),
TypeAndName("MP", SE_MP),
TypeAndName("EXP", SE_EXP),
TypeAndName("DropRate", SE_DROPRATE)
};
TypeAndName* lpTypeNamePastEnd = effectTypeName + MAX_EFFECT_TYPE_NUM;
for (TypeAndName* lpTypeName = effectTypeName; lpTypeName != lpTypeNamePastEnd; ++lpTypeName)
{
if (0 == strcmp(szStatueEffectType, lpTypeName->m_szName))
{
return lpTypeName->m_StatueEffectType;
}
}
return SE_NONE;
}
unsigned short MonsterInfo::GetCompleteStatueKID(unsigned short wNowKID)
{
switch (wNowKID)
{
// 1·¹º§
case MonsterInfo::STATUE_HUMAN_LOADING1: return MonsterInfo::STATUE_HUMAN_COMPLETE1; break;
case MonsterInfo::STATUE_AKHAN_LOADING1: return MonsterInfo::STATUE_AKHAN_COMPLETE1; break;
case MonsterInfo::BG_STATUE_HUMAN_LOADING1: return MonsterInfo::BG_STATUE_HUMAN_COMPLETE1; break;
case MonsterInfo::BG_STATUE_AKHAN_LOADING1: return MonsterInfo::BG_STATUE_AKHAN_COMPLETE1; break;
case MonsterInfo::POWER_STATUE_HUMAN_LOADING1: return MonsterInfo::POWER_STATUE_HUMAN_COMPLETE1; break;
case MonsterInfo::POWER_STATUE_AKHAN_LOADING1: return MonsterInfo::POWER_STATUE_AKHAN_COMPLETE1; break;
case MonsterInfo::INT_STATUE_HUMAN_LOADING1: return MonsterInfo::INT_STATUE_HUMAN_COMPLETE1; break;
case MonsterInfo::INT_STATUE_AKHAN_LOADING1: return MonsterInfo::INT_STATUE_AKHAN_COMPLETE1; break;
case MonsterInfo::EXP_STATUE_HUMAN_LOADING1: return MonsterInfo::EXP_STATUE_HUMAN_COMPLETE1; break;
case MonsterInfo::EXP_STATUE_AKHAN_LOADING1: return MonsterInfo::EXP_STATUE_AKHAN_COMPLETE1; break;
case MonsterInfo::WEALTH_STATUE_HUMAN_LOADING1: return MonsterInfo::WEALTH_STATUE_HUMAN_COMPLETE1; break;
case MonsterInfo::WEALTH_STATUE_AKHAN_LOADING1: return MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE1; break;
case MonsterInfo::LIFE_EXTRACT_HUMAN_LOADING1: return MonsterInfo::LIFE_EXTRACT_HUMAN_COMPLETE1; break;
case MonsterInfo::LIFE_EXTRACT_AKHAN_LOADING1: return MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE1; break;
// 2·¹º§
case MonsterInfo::STATUE_HUMAN_LOADING2: return MonsterInfo::STATUE_HUMAN_COMPLETE2; break;
case MonsterInfo::STATUE_AKHAN_LOADING2: return MonsterInfo::STATUE_AKHAN_COMPLETE2; break;
case MonsterInfo::BG_STATUE_HUMAN_LOADING2: return MonsterInfo::BG_STATUE_HUMAN_COMPLETE2; break;
case MonsterInfo::BG_STATUE_AKHAN_LOADING2: return MonsterInfo::BG_STATUE_AKHAN_COMPLETE2; break;
case MonsterInfo::POWER_STATUE_HUMAN_LOADING2: return MonsterInfo::POWER_STATUE_HUMAN_COMPLETE2; break;
case MonsterInfo::POWER_STATUE_AKHAN_LOADING2: return MonsterInfo::POWER_STATUE_AKHAN_COMPLETE2; break;
case MonsterInfo::INT_STATUE_HUMAN_LOADING2: return MonsterInfo::INT_STATUE_HUMAN_COMPLETE2; break;
case MonsterInfo::INT_STATUE_AKHAN_LOADING2: return MonsterInfo::INT_STATUE_AKHAN_COMPLETE2; break;
case MonsterInfo::EXP_STATUE_HUMAN_LOADING2: return MonsterInfo::EXP_STATUE_HUMAN_COMPLETE2; break;
case MonsterInfo::EXP_STATUE_AKHAN_LOADING2: return MonsterInfo::EXP_STATUE_AKHAN_COMPLETE2; break;
case MonsterInfo::WEALTH_STATUE_HUMAN_LOADING2: return MonsterInfo::WEALTH_STATUE_HUMAN_COMPLETE2; break;
case MonsterInfo::WEALTH_STATUE_AKHAN_LOADING2: return MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE2; break;
case MonsterInfo::LIFE_EXTRACT_HUMAN_LOADING2: return MonsterInfo::LIFE_EXTRACT_HUMAN_COMPLETE2; break;
case MonsterInfo::LIFE_EXTRACT_AKHAN_LOADING2: return MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE2; break;
}
return wNowKID;
}
unsigned short MonsterInfo::GetLoadingStatueKID(unsigned char cRace, unsigned short wNowKID)
{
switch (cRace)
{
case CClass::HUMAN:
{
// 1·¹º§
if (MonsterInfo::STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::STATUE_HUMAN_LOADING1;
}
else if (MonsterInfo::BG_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::BG_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::BG_STATUE_HUMAN_LOADING1;
}
else if (MonsterInfo::POWER_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::POWER_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::POWER_STATUE_HUMAN_LOADING1;
}
else if (MonsterInfo::INT_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::INT_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::INT_STATUE_HUMAN_LOADING1;
}
else if (MonsterInfo::EXP_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::EXP_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::EXP_STATUE_HUMAN_LOADING1;
}
else if (MonsterInfo::WEALTH_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::WEALTH_STATUE_HUMAN_LOADING1;
}
else if (MonsterInfo::LIFE_EXTRACT_NEUTRALITY1 <= wNowKID && MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::LIFE_EXTRACT_HUMAN_LOADING1;
}
// 2·¹º§
else if (MonsterInfo::STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::STATUE_HUMAN_LOADING2;
}
else if (MonsterInfo::BG_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::BG_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::BG_STATUE_HUMAN_LOADING2;
}
else if (MonsterInfo::POWER_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::POWER_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::POWER_STATUE_HUMAN_LOADING2;
}
else if (MonsterInfo::INT_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::INT_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::INT_STATUE_HUMAN_LOADING2;
}
else if (MonsterInfo::EXP_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::EXP_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::EXP_STATUE_HUMAN_LOADING2;
}
else if (MonsterInfo::WEALTH_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::WEALTH_STATUE_HUMAN_LOADING2;
}
else if (MonsterInfo::LIFE_EXTRACT_NEUTRALITY2 <= wNowKID && MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::LIFE_EXTRACT_HUMAN_LOADING2;
}
}
break;
case CClass::AKHAN:
{
// 1·¹º§
if (MonsterInfo::STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::STATUE_AKHAN_LOADING1;
}
else if (MonsterInfo::BG_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::BG_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::BG_STATUE_AKHAN_LOADING1;
}
else if (MonsterInfo::POWER_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::POWER_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::POWER_STATUE_AKHAN_LOADING1;
}
else if (MonsterInfo::INT_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::INT_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::INT_STATUE_AKHAN_LOADING1;
}
else if (MonsterInfo::EXP_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::EXP_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::EXP_STATUE_AKHAN_LOADING1;
}
else if (MonsterInfo::WEALTH_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::WEALTH_STATUE_AKHAN_LOADING1;
}
else if (MonsterInfo::LIFE_EXTRACT_NEUTRALITY1 <= wNowKID && MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::LIFE_EXTRACT_AKHAN_LOADING1;
}
// 2·¹º§
else if (MonsterInfo::STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::STATUE_AKHAN_LOADING2;
}
else if (MonsterInfo::BG_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::BG_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::BG_STATUE_AKHAN_LOADING2;
}
else if (MonsterInfo::POWER_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::POWER_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::POWER_STATUE_AKHAN_LOADING2;
}
else if (MonsterInfo::INT_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::INT_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::INT_STATUE_AKHAN_LOADING2;
}
else if (MonsterInfo::EXP_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::EXP_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::EXP_STATUE_AKHAN_LOADING2;
}
else if (MonsterInfo::WEALTH_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::WEALTH_STATUE_AKHAN_LOADING2;
}
else if (MonsterInfo::LIFE_EXTRACT_NEUTRALITY2 <= wNowKID && MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::LIFE_EXTRACT_AKHAN_LOADING2;
}
}
break;
}
return GetDefaultStatueKID(wNowKID);
}
unsigned short MonsterInfo::GetDefaultStatueKID(unsigned short wNowKID)
{
// 1·¹º§
if (MonsterInfo::STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::STATUE_NEUTRALITY1;
}
else if (MonsterInfo::BG_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::BG_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::BG_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::POWER_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::POWER_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::POWER_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::INT_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::INT_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::INT_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::EXP_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::EXP_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::EXP_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::WEALTH_STATUE_NEUTRALITY1 <= wNowKID && MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::WEALTH_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::LIFE_EXTRACT_NEUTRALITY1 <= wNowKID && MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE1 >= wNowKID)
{
return MonsterInfo::LIFE_EXTRACT_NEUTRALITY1;
}
// 2·¹º§
else if (MonsterInfo::STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::STATUE_NEUTRALITY2;
}
else if (MonsterInfo::BG_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::BG_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::BG_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::POWER_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::POWER_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::POWER_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::INT_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::INT_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::INT_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::EXP_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::EXP_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::EXP_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::WEALTH_STATUE_NEUTRALITY2 <= wNowKID && MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::WEALTH_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::LIFE_EXTRACT_NEUTRALITY2 <= wNowKID && MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE2 >= wNowKID)
{
return MonsterInfo::LIFE_EXTRACT_NEUTRALITY2;
}
return wNowKID;
}
bool MonsterInfo::IsLoadingStatueKID(unsigned short wNowKID)
{
switch (wNowKID)
{
// 1·¹º§
case MonsterInfo::STATUE_HUMAN_LOADING1:
case MonsterInfo::STATUE_AKHAN_LOADING1:
case MonsterInfo::BG_STATUE_HUMAN_LOADING1:
case MonsterInfo::BG_STATUE_AKHAN_LOADING1:
case MonsterInfo::POWER_STATUE_HUMAN_LOADING1:
case MonsterInfo::POWER_STATUE_AKHAN_LOADING1:
case MonsterInfo::INT_STATUE_HUMAN_LOADING1:
case MonsterInfo::INT_STATUE_AKHAN_LOADING1:
case MonsterInfo::EXP_STATUE_HUMAN_LOADING1:
case MonsterInfo::EXP_STATUE_AKHAN_LOADING1:
case MonsterInfo::WEALTH_STATUE_HUMAN_LOADING1:
case MonsterInfo::WEALTH_STATUE_AKHAN_LOADING1:
case MonsterInfo::LIFE_EXTRACT_HUMAN_LOADING1:
case MonsterInfo::LIFE_EXTRACT_AKHAN_LOADING1:
// 2·¹º§
case MonsterInfo::STATUE_HUMAN_LOADING2:
case MonsterInfo::STATUE_AKHAN_LOADING2:
case MonsterInfo::BG_STATUE_HUMAN_LOADING2:
case MonsterInfo::BG_STATUE_AKHAN_LOADING2:
case MonsterInfo::POWER_STATUE_HUMAN_LOADING2:
case MonsterInfo::POWER_STATUE_AKHAN_LOADING2:
case MonsterInfo::INT_STATUE_HUMAN_LOADING2:
case MonsterInfo::INT_STATUE_AKHAN_LOADING2:
case MonsterInfo::EXP_STATUE_HUMAN_LOADING2:
case MonsterInfo::EXP_STATUE_AKHAN_LOADING2:
case MonsterInfo::WEALTH_STATUE_HUMAN_LOADING2:
case MonsterInfo::WEALTH_STATUE_AKHAN_LOADING2:
case MonsterInfo::LIFE_EXTRACT_HUMAN_LOADING2:
case MonsterInfo::LIFE_EXTRACT_AKHAN_LOADING2:
return true;
default:
return false;
}
return false;
}
bool MonsterInfo::IsRaceCompleteStatueKID(unsigned short wNowKID)
{
switch (wNowKID)
{
// 1·¹º§
case MonsterInfo::STATUE_HUMAN_COMPLETE1:
case MonsterInfo::STATUE_AKHAN_COMPLETE1:
case MonsterInfo::BG_STATUE_HUMAN_COMPLETE1:
case MonsterInfo::BG_STATUE_AKHAN_COMPLETE1:
case MonsterInfo::POWER_STATUE_HUMAN_COMPLETE1:
case MonsterInfo::POWER_STATUE_AKHAN_COMPLETE1:
case MonsterInfo::INT_STATUE_HUMAN_COMPLETE1:
case MonsterInfo::INT_STATUE_AKHAN_COMPLETE1:
case MonsterInfo::EXP_STATUE_HUMAN_COMPLETE1:
case MonsterInfo::EXP_STATUE_AKHAN_COMPLETE1:
case MonsterInfo::WEALTH_STATUE_HUMAN_COMPLETE1:
case MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE1:
case MonsterInfo::LIFE_EXTRACT_HUMAN_COMPLETE1:
case MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE1:
// 2·¹º§
case MonsterInfo::STATUE_HUMAN_COMPLETE2:
case MonsterInfo::STATUE_AKHAN_COMPLETE2:
case MonsterInfo::BG_STATUE_HUMAN_COMPLETE2:
case MonsterInfo::BG_STATUE_AKHAN_COMPLETE2:
case MonsterInfo::POWER_STATUE_HUMAN_COMPLETE2:
case MonsterInfo::POWER_STATUE_AKHAN_COMPLETE2:
case MonsterInfo::INT_STATUE_HUMAN_COMPLETE2:
case MonsterInfo::INT_STATUE_AKHAN_COMPLETE2:
case MonsterInfo::EXP_STATUE_HUMAN_COMPLETE2:
case MonsterInfo::EXP_STATUE_AKHAN_COMPLETE2:
case MonsterInfo::WEALTH_STATUE_HUMAN_COMPLETE2:
case MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE2:
case MonsterInfo::LIFE_EXTRACT_HUMAN_COMPLETE2:
case MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE2:
return true;
default:
return false;
}
return false;
}

View File

@@ -0,0 +1,313 @@
#ifndef _MONSTER_STRUCTURE_H_
#define _MONSTER_STRUCTURE_H_
#include <Creature/CreatureStructure.h>
#pragma pack(8)
struct MonsterInfo
{
enum MaxNumber
{
MAX_MOTION_NUM = 4, // 최대 모션 개수
MAX_ORIGINAL_ITEM_NUM = 5, // 몬스터 보유 잡템 최대수
MAX_NAME_LEN = 64, // 몬스터 이름 길이
MAX_MODELING_FLAG_LENGTH = 32 // 모델링 데이터 이름 길이
};
enum MonsterPattern
{
PATTERN_COMMON = 0,
PATTERN_WARRIOR = 1, // 워리어
PATTERN_DEFENDER = 2, // 디펜더
PATTERN_MAGE = 3, // 메이지
PATTERN_ACOLYTE = 4, // 어콜라이트
PATTERN_BOSS = 5, // 보스
PATTERN_BG = 6, // 배경(?)
PATTERN_SUMMON = 7, // 소환수
PATTERN_STRUCTURE = 8, // 구조물 (석상 종류)
PATTERN_NAMED = 9, // 네임드
PATTERN_CHIEF = 10, // 치프
PATTERN_OBJECT = 11, // 오브젝트 (꽃, 버섯 등등..)
PATTERN_GUARD = 12, // 경비병
PATTERN_GATHER = 13, // 수집 타입
MAX_PATTERN = 14
};
enum SkillPattern
{
ATTACK_SKILL = 0, // 공격열 스킬
RECOVER_SKILL = 1, // 회복열 스킬
ENCHANT_SKILL = 2, // 부여열 스킬
EXTENT_SKILL = 3, // 범위열 스킬
BOSS_CHANT_SKILL = 4, // 보스의 챈트 스킬
MAX_SKILL_PATTERN = 5
};
enum HitBox
{
X_PLUS = 0,
X_MINUS = 1,
Y_PLUS = 2,
Y_MINUS = 3,
MAX_HITBOX_NUM = 4
};
enum Z3D_CHR_ACTION
{
Z3D_CA_RUN = 1, // 달리기
Z3D_CA_ATTACK = 2, // 공격
Z3D_CA_WALK = 8, // 걷기
Z3D_CA_CASTING = 24 // 캐스팅
};
enum AwardKind
{
ORIGINAL1 = 0, // 잡템1
ORIGINAL2 = 1, // 잡템2
ORIGINAL3 = 2, // 잡템3
ORIGINAL4 = 3, // 잡템4
ORIGINAL5 = 4, // 잡템5
COIN = 5, // 돈
GEM = 6, // 보석
METAL = 7, // 광물
POTION = 8, // 물약
SKILL = 9, // 스킬북
CASH_ITEM = 10, // 캐쉬템
LOTTERY = 11, // 복권
F_STANDARD_EQUIP = 12, // Standard-Option Item (Grade F)
D_STANDARD_EQUIP = 13, // Standard-Option Item (Grade D)
C_STANDARD_EQUIP = 14, // Standard-Option Item (Grade C)
B_STANDARD_EQUIP = 15, // Standard-Option Item (Grade B)
A_STANDARD_EQUIP = 16, // Standard-Option Item (Grade A)
F_OVER_EQUIP = 17, // Over-Option Item (Grade F)
D_OVER_EQUIP = 18, // Over-Option Item (Grade D)
C_OVER_EQUIP = 19, // Over-Option Item (Grade C)
B_OVER_EQUIP = 20, // Over-Option Item (Grade B)
A_OVER_EQUIP = 21, // Over-Option Item (Grade A)
NO_EQUIP = 22, // No-Option Item
//--// start..
F_RING = 23, // 반지 (Grade F)
D_RING = 24, // 반지 (Grade D)
C_RING = 25, // 반지 (Grade C)
B_RING = 26, // 반지 (Grade B)
A_RING = 27, // 반지 (Grade A)
F_NECKLACE = 28, // 목걸이 (Grade F)
D_NECKLACE = 29, // 목걸이 (Grade D)
C_NECKLACE = 30, // 목걸이 (Grade C)
B_NECKLACE = 31, // 목걸이 (Grade B)
A_NECKLACE = 32, // 목걸이 (Grade A)
F_RUNE = 33, // 룬 (Grade F)
D_RUNE = 34, // 룬 (Grade D)
C_RUNE = 35, // 룬 (Grade C)
B_RUNE = 36, // 룬 (Grade B)
A_RUNE = 37, // 룬 (Grade A)
DESTRUCTION_RUNE = 38, // 소멸의 룬
MAX_AWARD_KIND = 39
//--// end..
};
enum SpeacialKindID
{
// 소환수
SUMMON_KINDLING = 3500,
SUMMON_FLAMEWALKER = 3506,
SUMMON_BURNINGSHIELD = 3513,
SUMMON_FIREWING = 3520,
SUMMON_DEATHBURN = 3527,
// 1레벨 석상
// 명예의 석상
STATUE_NEUTRALITY1 = 4001,
STATUE_HUMAN_LOADING1 = 4002,
STATUE_HUMAN_COMPLETE1 = 4003,
STATUE_AKHAN_LOADING1 = 4004,
STATUE_AKHAN_COMPLETE1 = 4005,
// BattleGround Server 명예의 석상
BG_STATUE_NEUTRALITY1 = 4006,
BG_STATUE_HUMAN_LOADING1 = 4007,
BG_STATUE_HUMAN_COMPLETE1 = 4008,
BG_STATUE_AKHAN_LOADING1 = 4009,
BG_STATUE_AKHAN_COMPLETE1 = 4010,
// 다크 카나번 석상
POWER_STATUE_NEUTRALITY1 = 4011, // 힘의 석상
POWER_STATUE_HUMAN_LOADING1 = 4012,
POWER_STATUE_HUMAN_COMPLETE1 = 4013,
POWER_STATUE_AKHAN_LOADING1 = 4014,
POWER_STATUE_AKHAN_COMPLETE1 = 4015,
INT_STATUE_NEUTRALITY1 = 4016, // 지능의 석상
INT_STATUE_HUMAN_LOADING1 = 4017,
INT_STATUE_HUMAN_COMPLETE1 = 4018,
INT_STATUE_AKHAN_LOADING1 = 4019,
INT_STATUE_AKHAN_COMPLETE1 = 4020,
EXP_STATUE_NEUTRALITY1 = 4021, // 경험의 석상
EXP_STATUE_HUMAN_LOADING1 = 4022,
EXP_STATUE_HUMAN_COMPLETE1 = 4023,
EXP_STATUE_AKHAN_LOADING1 = 4024,
EXP_STATUE_AKHAN_COMPLETE1 = 4025,
WEALTH_STATUE_NEUTRALITY1 = 4026, // 부의 석상
WEALTH_STATUE_HUMAN_LOADING1 = 4027,
WEALTH_STATUE_HUMAN_COMPLETE1 = 4028,
WEALTH_STATUE_AKHAN_LOADING1 = 4029,
WEALTH_STATUE_AKHAN_COMPLETE1 = 4030,
LIFE_EXTRACT_NEUTRALITY1 = 4031, // 생명 축출기
LIFE_EXTRACT_HUMAN_LOADING1 = 4032,
LIFE_EXTRACT_HUMAN_COMPLETE1 = 4033,
LIFE_EXTRACT_AKHAN_LOADING1 = 4034,
LIFE_EXTRACT_AKHAN_COMPLETE1 = 4035,
// 2레벨 석상
// 명예의 석상
STATUE_NEUTRALITY2 = 4036,
STATUE_HUMAN_LOADING2 = 4037,
STATUE_HUMAN_COMPLETE2 = 4038,
STATUE_AKHAN_LOADING2 = 4039,
STATUE_AKHAN_COMPLETE2 = 4040,
// BattleGround Server 명예의 석상
BG_STATUE_NEUTRALITY2 = 4041,
BG_STATUE_HUMAN_LOADING2 = 4042,
BG_STATUE_HUMAN_COMPLETE2 = 4043,
BG_STATUE_AKHAN_LOADING2 = 4044,
BG_STATUE_AKHAN_COMPLETE2 = 4045,
// 다크 카나번 석상
POWER_STATUE_NEUTRALITY2 = 4046, // 힘의 석상
POWER_STATUE_HUMAN_LOADING2 = 4047,
POWER_STATUE_HUMAN_COMPLETE2 = 4048,
POWER_STATUE_AKHAN_LOADING2 = 4049,
POWER_STATUE_AKHAN_COMPLETE2 = 4050,
INT_STATUE_NEUTRALITY2 = 4051, // 지능의 석상
INT_STATUE_HUMAN_LOADING2 = 4052,
INT_STATUE_HUMAN_COMPLETE2 = 4053,
INT_STATUE_AKHAN_LOADING2 = 4054,
INT_STATUE_AKHAN_COMPLETE2 = 4055,
EXP_STATUE_NEUTRALITY2 = 4056, // 경험의 석상
EXP_STATUE_HUMAN_LOADING2 = 4057,
EXP_STATUE_HUMAN_COMPLETE2 = 4058,
EXP_STATUE_AKHAN_LOADING2 = 4059,
EXP_STATUE_AKHAN_COMPLETE2 = 4060,
WEALTH_STATUE_NEUTRALITY2 = 4061, // 부의 석상
WEALTH_STATUE_HUMAN_LOADING2 = 4062,
WEALTH_STATUE_HUMAN_COMPLETE2 = 4063,
WEALTH_STATUE_AKHAN_LOADING2 = 4064,
WEALTH_STATUE_AKHAN_COMPLETE2 = 4065,
LIFE_EXTRACT_NEUTRALITY2 = 4066, // 생명 축출기
LIFE_EXTRACT_HUMAN_LOADING2 = 4067,
LIFE_EXTRACT_HUMAN_COMPLETE2 = 4068,
LIFE_EXTRACT_AKHAN_LOADING2 = 4069,
LIFE_EXTRACT_AKHAN_COMPLETE2 = 4070,
// 석상 KID (CreatureStructure.h 에도 정의되어 있음)
MIN_STATUE_KID = 4001,
MAX_STATUE_KID = 4070
};
enum StatueEffectType
{
SE_NONE = 0,
SE_HP = 1, // Max HP 상승 효과
SE_MP = 2, // Max MP 상승 효과
SE_EXP = 3, // 획득 Exp 상승 효과
SE_DROPRATE = 4, // 획득 아이템 DropRate 상승 효과
MAX_EFFECT_TYPE_NUM = 5
};
MotionInfo m_MonsterMotions[MAX_MOTION_NUM]; // 몬스터의 행동 정보 (걷기, 달리기, 공격, 캐스팅)
char m_strName[MAX_NAME_LEN]; // 이름
char m_strModelingFlag[MAX_MODELING_FLAG_LENGTH]; // 모델링 형태 플래그
float m_fHitBox[MAX_HITBOX_NUM]; // 타격박스
unsigned short m_aryAwardItem[MAX_ORIGINAL_ITEM_NUM]; // 몬스터가 가지고 있는 오리지날 아이템들
unsigned long m_aryDropRate[MAX_AWARD_KIND]; // 아이템 종류별 드랍률
unsigned char m_cStandardBaseNum; // Standard-Option 장비의 최소 + 갯수
unsigned char m_cOverBaseNum; // Over-Option 장비의 최소 + 갯수
unsigned long m_dwKID; // 종류 ID
unsigned long m_dwRespawnTime; // 리스폰 타임
float m_fSize; // 사이즈
float m_fAttackAngle; // 공격 각도
short m_wAttackRange; // 공격 거리
unsigned char m_cSkillPattern; // 스킬 패턴
unsigned char m_cSkillLevel; // 스킬 레벨
unsigned char m_cFixLevelGap; // 고정 레벨갭
bool m_bFixLevelGap; // 고정 레벨갭 적용 여부
char m_cNation; // 국가
unsigned short m_wSkillUseRate; // 스킬 사용 확률
unsigned short m_wSkillID[MAX_SKILL_PATTERN]; // 몬스터 스킬 ID
unsigned char m_cEnchantSpellType; // 부여 스킬에 의한 스펠타입 ( Skill::SpellID::Type )
unsigned char m_cChantSpellType; // 파티 챈트 스킬에 의한 스펠타입 ( Skill::SpellID::Type )
float m_fSkillEffectSize; // 스킬 이펙트 사이즈 (인간: 1.0)
bool m_bIgnoreEnchant; // 챈트, 인챈트 무시 여부
bool m_bCollision; // 충돌 체크 여부
bool m_bFirstAttack; // 선제공격 여부
bool m_bReturnPosition; // 타겟이 도망가면 제자리로 돌아가는지 여부
bool m_bUseLOD; // LOD 처리 유무
// 공성 오브젝트 관련
unsigned long m_dwDevelopGold; // 개발 비용
unsigned char m_cDevelopSpeed; // 개발 속도 (단위 : 분)
unsigned long m_dwUpgradeGold; // 업그레이드 비용
unsigned char m_cUpgradeSpeed; // 업그레이드 속도 (단위 : 분)
float m_fDevelopGoldDown; // 개발 비용 절감 수치 (%)
float m_fDevelopSpeedUp; // 개발 속도 향상 수치 (%)
float m_fUpgradeGoldDown; // 업그레이드 비용 절감 수치 (%)
float m_fUpgradeSpeedUp; // 업그레이드 속도 향상 수치 (%)
float m_fDefenseUp; // 방어력 향상 수치 (%)
float m_fOffenseUp; // 공격력 향상 수치 (%)
float m_fHPUp; // HP 향상 수치 (%)
float m_fBonusRate; // 총 수입 보너스 (%)
float m_fRespawnSpeedUp; // PC 리스폰 속도 향상 수치 (%)
// 국가전 석상 효과 관련
unsigned char m_cStatueEffectType; // 석상 효과 타입
unsigned char m_cStatueEffectPercent; // 석상 효과 퍼센트
bool m_bStatueRespawnEnable; // 석상 리스폰 가능 여부
MonsterInfo();
static MonsterPattern GetMonsterPattern(const char* szMonsterType);
static StatueEffectType GetStatueEffectType(const char* szStatueEffectType);
static unsigned short GetCompleteStatueKID(unsigned short wNowKID);
static unsigned short GetLoadingStatueKID(unsigned char cRace, unsigned short wNowKID);
static unsigned short GetDefaultStatueKID(unsigned short wNowKID);
static bool IsLoadingStatueKID(unsigned short wNowKID);
static bool IsRaceCompleteStatueKID(unsigned short wNowKID);
};
#pragma pack()
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,438 @@
#ifndef _PATTERN_MONSTER_H_
#define _PATTERN_MONSTER_H_
#include "Monster.h"
// -------------------------------------------------------------------------------------
// 소환수
class CSummonMonster : public CMonster
{
public:
CSummonMonster();
CSummonMonster(MonsterCreateInfo& MonsterCreate, CCharacter* lpMaster);
virtual ~CSummonMonster();
void NormalBehavior(unsigned long dwTick);
void AttackBehavior(unsigned long dwTick);
void ReturnBehavior(unsigned long dwTick);
void DeadBehavior(unsigned long dwTick);
void GuardMe(CAggresiveCreature* lpTarget, unsigned short wThreat);
void AttackCmd(CAggresiveCreature* lpTarget, unsigned short wThreat);
void Attacked(void);
bool Attack(AtType attackType, unsigned char cDefenderNum,
CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal);
bool Dead(CAggresiveCreature* pOffencer);
EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget);
bool GetMotion(unsigned long MotionID, MotionInfo &Motion);
void SetGuard(bool bGuard) { m_bGuard = bGuard; }
CCharacter* GetMaster(void) { return m_lpMaster; }
unsigned char GetNation() const
{
if (NULL == m_lpMaster)
{
return Creature::STATELESS;
}
return m_lpMaster->GetNation();
}
protected:
enum
{
SUPPORT_DISTANCE = 3,
SUMMON_TIME = 4 * FPS
};
CCharacter* m_lpMaster;
bool m_bGuard;
bool m_bPadding[3];
};
// -------------------------------------------------------------------------------------
// 명예의 석상
class CStatue : public CMonster
{
public:
enum
{
MAX_HP_UPDATE_COUNT = 10, // 10번에 한번 HP 업데이트
STATUE_ATTACKED_TIME = 3000, // 공격 당했을때 3초당 1번씩 패킷 전송
STATUE_LOADING_TIME = 300000, // 석상 소환 시간(5분)
BG_STATUE_LOADING_TIME = 120000, // 배틀 그라운드 서버군 석상 소환 시간 (2분)
UPDATE_INFO_CYCLE = 150, // 석상 정보 업데이트 주기(5초)
BONUS_DISTANCE = 100, // 공헌메달 보너스를 받을 수 있는 거리
BONUS_MILEAGE = 100, // 한번에 얻는 공헌메달 보너스의 양
BONUS_TIME = 12 // 공헌메달 보너스를 얻는 주기(1분)
};
CStatue();
CStatue(MonsterCreateInfo& MonsterCreate, CStatue* lpParent);
virtual ~CStatue();
void NormalBehavior(unsigned long dwTick);
void AttackBehavior(unsigned long dwTick);
void ReturnBehavior(unsigned long dwTick);
void DeadBehavior(unsigned long dwTick);
void GiveMileage(char cGiveType);
virtual bool RegenHPAndMP(unsigned short usAdditionalHP, unsigned short usAdditionalMP, bool bAddDefaultRegenValue);
bool Dead(CAggresiveCreature* pOffencer);
bool Rest(void); // 배틀 그라운드에 석상이 하나도 나오지 않도록 쉬게하는 처리
// Dead 와 동일하나 다음 석상을 소환하지 않는다.
EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget);
bool CreateLinkStatue(unsigned short wKind);
CStatue* GetLinkStatue(unsigned short wKind);
Item::CItem* SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError);
Position& GetOriginalPos() { return m_OriginalPosition; }
// 국가전 석상 인챈트 효과 종류
unsigned char GetRealmStatueEnchantType() const { return m_MonsterInfo.m_cStatueEffectType; }
// 국가전 석상 인챈트 효과 퍼센트
unsigned char GetRealmStatueEnchantPercent() const { return m_MonsterInfo.m_cStatueEffectPercent; }
// 국가전 석상 리스폰 가능 여부
bool EnableRespawn() const { return m_MonsterInfo.m_bStatueRespawnEnable; }
// DBAgentServer 로 KID, HP 업데이트 패킷 보내는 함수
bool SendKIDUpdate();
bool SendHPUpdate(bool bForce=false);
// Client 로 Attacked 정보 보내기
void SendAttacked();
protected:
unsigned long m_dwDuration;
unsigned short m_wBonusTurn;
unsigned char m_cHPUpdateCount;
unsigned long m_dwLastAttackedTime;
CStatue* m_lpParent; // 석상의 초기 상태 (명예의 석상은 5가지 석상이 한 세트로 링크되어 있다.)
CNPC* m_lpLinkNPC; // 메달 상점 역할을 위해 링크된 NPC
};
class CStatueInfo
{
public:
CStatueInfo(CStatue* lpStatue, bool bBonusTurn, char cGiveType);
bool operator () (CCharacter* lpCharacter);
private:
CStatue* m_lpStatue;
bool m_bBonusTurn;
char m_cGiveType;
};
// -------------------------------------------------------------------------------------
// GM의 축복
// edith 2010.01.16 GM의 축복의 메달 기능 구현 1분에 1~5개씩 메달 부여
// StatueInfo 와 같은 역활을 하기 때문에 이곳에 구현함.
class CBlessMileageInfo
{
public:
CBlessMileageInfo(char cBlessType);
bool operator () (CCharacter* lpCharacter);
private:
char m_cBlessType;
char m_cAddMileage;
};
// -------------------------------------------------------------------------------------
// 수집 몬스터
class CGatherMonster : public CMonster
{
public:
CGatherMonster();
CGatherMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CGatherMonster();
virtual void NormalBehavior(unsigned long dwTick);
virtual void AttackBehavior(unsigned long dwTick);
virtual void ReturnBehavior(unsigned long dwTick);
};
// -------------------------------------------------------------------------------------
// 오브젝트 몬스터 (꽃, 나무, 버섯류)
class CObjectMonster : public CMonster
{
public:
CObjectMonster();
CObjectMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CObjectMonster();
virtual void NormalBehavior(unsigned long dwTick);
virtual void AttackBehavior(unsigned long dwTick);
virtual void ReturnBehavior(unsigned long dwTick);
};
// -------------------------------------------------------------------------------------
// 패턴 몬스터
class CSkillMonster : public CMonster
{
public:
virtual ~CSkillMonster();
char GetConsumeMPCount(void) { return m_cConsumeMPCount; }
bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenderJudges, unsigned short* wDefenderMPHeal);
virtual void NormalBehavior(unsigned long dwTick);
virtual void AttackBehavior(unsigned long dwTick);
// Action 관련 처리
void CastingAttackAction();
void SkillAttackAction();
enum SkillPattern
{
A_SKILL = 0,
B_SKILL = 1,
C_SKILL = 2,
D_SKILL = 3,
BOSS_CHANT_SKILL = 4,
MAX_SKILL_PATTERN = 5
};
enum EnemyFindType
{
TYPE_LV = 0,
TYPE_HP = 1,
TYPE_HIGHEST = 0,
TYPE_LOWEST = 1
};
enum Const
{
USE_SKILL_MIN_LEVEL = 4,
USE_SKILL_LEVEL = 5 // 4레벨 스킬보다 낮은 스킬을 가진 몬스터가 사용하는 고정 스킬 레벨
// (1단계 스킬을 사용하는 몬스터는 제외)
};
public:
virtual LONG SendMove(unsigned short nAniNum);
protected:
CSkillMonster();
CSkillMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
bool Dead(CAggresiveCreature* pOffencer); // 죽었을 때 호출되는 함수
// 자신의 주변에서 셀크기(32m) 반경내에 있는 아군 파티원중에 가장 레벨이 높은 몬스터를 리턴
CAggresiveCreature* FindHighestLVMember();
// 해당 스킬의 범위 안에 있는 녀석들 중
// 인챈트 부여 스킬이 걸리지 않은 가장 레벨이 높은 몬스터를 찾아서 리턴해주는 함수
CAggresiveCreature* FindHighestLVMember(const AtType& attackType);
// 파티원 중에서 HP 가 fRemain 퍼센트 이하로 남아있는 파티원이 있는지 검색
// bRegin 이 TRUE 이면 해당 공격의 범위 안에 있는 파티원 중에서 찾는다.
CAggresiveCreature* FindLowestHPMember(const AtType& attackType, bool bRegin, float fRemainHP = 0.5f);
// 패턴에 맞는 적을 찾는 함수
// Type1 : LV, HP
// Type2 : Highest, Lowest
CAggresiveCreature* FindEnemy(const AtType& attackType, unsigned char cType1, unsigned char cType2);
float CalculateDistance(CAggresiveCreature* pTarget);
virtual bool SkillAttack(void) { return false; }
// 일반적인 스킬 사용 함수
bool UseSkill(AtType attackType, CAggresiveCreature** ppDefenders, char cSkillPattern);
// 캐스팅 액션이 끝난후 나가는 스킬 사용 함수
bool UseCastedSkill(void);
unsigned long m_lastCastTime[MAX_SKILL_PATTERN]; // 마지막으로 스킬을 사용한 시간
char m_cConsumeMPCount; // MP 소모까지 남은 카운트 수 저장
// Casting
bool m_bCasting; // 마법 캐스팅 중인가?
int m_nCastingCount; // 캐스팅 동작 횟수
AtType m_attackType; // 캐스팅이 끝난후 나갈 스킬
unsigned long m_dwTargetCID; // 스킬의 대상 CID (죽거나 나가버릴수 있기때문에 CID로 체크)
char m_cSkillPattern; // m_lastCastTime 을 갱신하기 위해 사용한 스킬의 type index (m_skillID 의 Index 이다.)
};
class CDefenderMonster : public CSkillMonster
{
public:
CDefenderMonster();
CDefenderMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CDefenderMonster();
protected:
bool SkillAttack(void);
};
class CWarriorMonster : public CSkillMonster
{
public:
CWarriorMonster();
CWarriorMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CWarriorMonster();
protected:
bool SkillAttack(void);
};
class CAcolyteMonster : public CSkillMonster
{
public:
enum Distance
{
NEAR_DIST_TO_TARGET = 10,
FAR_DIST_TO_TARGET = 15
};
CAcolyteMonster();
CAcolyteMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CAcolyteMonster();
void AttackBehavior(unsigned long dwTick);
protected:
bool SkillAttack(void);
};
class CMageMonster : public CSkillMonster
{
public:
enum Distance
{
NEAR_DIST_TO_TARGET = 10,
FAR_DIST_TO_TARGET = 15
};
CMageMonster();
CMageMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CMageMonster();
void AttackBehavior(unsigned long dwTick);
protected:
bool SkillAttack(void);
};
class CBossMonster : public CSkillMonster
{
public:
CBossMonster();
CBossMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CBossMonster();
protected:
bool SkillAttack(void);
};
class CChiefMonster : public CSkillMonster
{
public:
CChiefMonster();
CChiefMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CChiefMonster();
protected:
bool SkillAttack(void);
};
class CNamedMonster : public CSkillMonster
{
public:
CNamedMonster();
CNamedMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CNamedMonster();
void Respawn(unsigned long dwTick);
protected:
bool SkillAttack(void);
Position m_RespawnPosition; // 리스폰 중심 좌표
};
class CGuardMonster : public CSkillMonster
{
public:
CGuardMonster();
CGuardMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon = false);
virtual ~CGuardMonster();
void SearchPlayer();
protected:
bool SkillAttack(void);
};
#endif

View File

@@ -0,0 +1,351 @@
#include "stdafx.h"
#include "VirtualMonsterMgr.h"
#include <Creature/Creature.h>
#include <Creature/Monster/Monster.h>
#include <Creature/Monster/PatternMonster.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
CVirtualMonsterMgr::CVirtualMonsterMgr()
: m_usSummonCount(0)
{
}
CVirtualMonsterMgr::~CVirtualMonsterMgr()
{
DestroyMonsterList();
}
struct FnDeleteSecond
{
template<typename PairType>
bool operator() (PairType& pair) { if(NULL != pair.second) { delete pair.second; } return true; }
};
struct FnLeaveParty
{
template<typename PairType>
bool operator() (PairType& pair)
{
if(NULL != pair.second)
{
CParty* lpParty = (pair.second)->GetParty();
if (lpParty)
{
lpParty->Leave((pair.second)->GetCID(), 0, (pair.second)->GetMapIndex());
}
}
return true;
}
};
void CVirtualMonsterMgr::DestroyMonsterList()
{
std::for_each(m_MonsterMap.begin(), m_MonsterMap.end(), FnLeaveParty());
std::for_each(m_MonsterMap.begin(), m_MonsterMap.end(), FnDeleteSecond());
m_MonsterMap.clear();
m_AdminMonsterUIDMap.clear();
}
bool CVirtualMonsterMgr::AddMonster(CMonster* lpMonster)
{
if (NULL == lpMonster) return false;
unsigned long dwCID = lpMonster->GetCID();
bool bResult = false;
Creature::CreatureType eCreatureType = Creature::GetCreatureType(dwCID);
if (Creature::CT_MONSTER == eCreatureType ||
Creature::CT_SUMMON == eCreatureType ||
Creature::CT_STRUCT == eCreatureType)
{
bResult = m_MonsterMap.insert(std::make_pair(dwCID, reinterpret_cast<CMonster *>(lpMonster))).second;
unsigned short wKindID = static_cast<unsigned short>( (dwCID & Creature::MONSTER_KIND_BIT) );
if ( bResult && m_AdminMonsterUIDMap.end() == m_AdminMonsterUIDMap.find(wKindID) )
{
m_AdminMonsterUIDMap.insert(std::make_pair(wKindID, INIT_UID)).second;
}
}
return bResult;
}
CCreature* CVirtualMonsterMgr::GetCreature(unsigned long dwCID)
{
Creature::CreatureType eCreatureType = Creature::GetCreatureType(dwCID);
if (Creature::CT_MONSTER == eCreatureType || Creature::CT_SUMMON == eCreatureType || Creature::CT_STRUCT == eCreatureType)
return (CCreature *)GetMonster(dwCID);
return (CCreature *)NULL;
}
CAggresiveCreature* CVirtualMonsterMgr::GetAggresiveCreature(unsigned long dwCID)
{
Creature::CreatureType eCreatureType = Creature::GetCreatureType(dwCID);
if (Creature::CT_MONSTER == eCreatureType || Creature::CT_SUMMON == eCreatureType || Creature::CT_STRUCT == eCreatureType)
return (CAggresiveCreature *)GetMonster(dwCID);
return (CAggresiveCreature *)NULL;
}
CMonster* CVirtualMonsterMgr::GetMonster(unsigned long dwCID)
{
MonsterMap::iterator pos = m_MonsterMap.find(dwCID);
return (pos != m_MonsterMap.end()) ? pos->second : NULL;
}
void CVirtualMonsterMgr::ProcessAllMonster()
{
MonsterMap::iterator pos = m_MonsterMap.begin();
MonsterMap::iterator end = m_MonsterMap.end();
for (; pos != end; )
{
CMonster* lpMonster = pos->second;
if (lpMonster)
{
lpMonster->Process();
}
++pos;
}
}
void CVirtualMonsterMgr::ProcessMonsterRegenHPAndMP()
{
MonsterMap::iterator pos = m_MonsterMap.begin();
MonsterMap::iterator end = m_MonsterMap.end();
for (; pos != end; )
{
CMonster* lpMonster = pos->second;
if (lpMonster)
{
lpMonster->RegenHPAndMP(0, 0, true);
}
++pos;
}
}
void CVirtualMonsterMgr::ProcessSummonMonsterDead(void)
{
MonsterMap::iterator pos = m_MonsterMap.begin();
MonsterMap::iterator end = m_MonsterMap.end();
for (; pos != end; )
{
CMonster* lpMonster = pos->second;
if (lpMonster && true == lpMonster->IsDeadSummonMonster())
{
pos = m_MonsterMap.erase(pos);
delete lpMonster;
}
++pos;
}
}
bool CVirtualMonsterMgr::IsSummonee(unsigned long dwCID)
{
return Creature::IsSummonMonster(dwCID);
}
bool CVirtualMonsterMgr::SummonMonster(int nKID, Position Pos, CCharacter* lpMaster)
{
CMonster::MonsterCreateInfo tempInfo;
tempInfo.m_dwCID = Creature::SUMMON_MONSTER_BIT + (m_usSummonCount << 16) + nKID;
if (0 != GetMonster(tempInfo.m_dwCID))
{
ERRLOG0(g_Log, "몬스터 소환에 실패하였습니다.");
return false;
}
tempInfo.m_nKID = nKID;
tempInfo.m_Pos = Pos;
CMonster* lpSummonMonster = 0;
if (0 != lpMaster)
{
CAggresiveCreature* lpSummonee = lpMaster->GetSummonee();
if (0 != lpSummonee)
{
lpSummonee->Dead(0);
}
}
lpSummonMonster = new CSummonMonster(tempInfo, lpMaster);
if (0 == lpSummonMonster)
{
ERRLOG0(g_Log, "몬스터 소환에 실패하였습니다.");
return false;
}
lpSummonMonster->SetMapIndex(lpMaster->GetMapIndex());
if (false == lpSummonMonster->InitMonster(tempInfo.m_Pos))
{
ERRLOG0(g_Log, "소환 몬스터 초기화에 실패하였습니다.");
return false;
}
AddMonster(lpSummonMonster);
m_usSummonCount++;
if (CMonster::MAX_MONSTER_UID == m_usSummonCount) { m_usSummonCount = 0; }
if (0 != lpMaster)
{
lpMaster->SetSummonee(lpSummonMonster);
GameClientSendPacket::SendCharSummon(lpMaster->GetCID(), lpSummonMonster);
}
else
{
lpSummonMonster->SendMove(CMonster::RUN_ANI_LIMIT_MIN);
}
return true;
}
bool CVirtualMonsterMgr::AdminSummonMonster(int nKID, Position Pos, unsigned short wMapIndex)
{
CMonster::MonsterCreateInfo tempInfo;
CMonster* lpNewMonster = 0;
// nKID 가 정상인지 체크
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (0 == lpProtoType)
{
ERRLOG1(g_Log, "KID : %d 몬스터를 소환하는데 실패하였습니다.", nKID);
return false;
}
// 존당 최대 몬스터 수를 초과하는지 체크
if (GetMonsterNum() >= CMonster::MAX_MONSTER_UID)
{
ERRLOG0(g_Log, "현재 존에 몬스터 수가 최대입니다. 더이상 몬스터를 생성할 수 없습니다.");
return false;
}
// 몬스터 맵에서 적당한 숫자를 넘겨준다. (CID 를 유니크하게 하기 위해서)
unsigned long dwUID = GetAvailableMonsterUID(static_cast<unsigned short>(nKID));
if (dwUID == NO_BLANK_UID)
{
ERRLOG1(g_Log, "KindID:%d 해당 종류의 몬스터 수가 최대입니다. 더이상 몬스터를 생성할 수 없습니다.", nKID);
return false;
}
tempInfo.m_dwCID = (dwUID << 16) + nKID;
tempInfo.m_nKID = nKID;
tempInfo.m_dwPID = 0;
tempInfo.m_Pos = Pos;
tempInfo.m_bScout = false;
tempInfo.m_nMovingPattern = 0;
tempInfo.m_wRespawnArea = CCell::CELL_DISTANCE;
// MON_TODO : 몬스터 타입에 맞게 생성
switch (lpProtoType->m_MonsterInfo.m_cSkillPattern)
{
case MonsterInfo::PATTERN_DEFENDER:
lpNewMonster = new CDefenderMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_WARRIOR:
lpNewMonster = new CWarriorMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_ACOLYTE:
lpNewMonster = new CAcolyteMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_MAGE:
lpNewMonster = new CMageMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_BOSS:
lpNewMonster = new CBossMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_NAMED:
lpNewMonster = new CNamedMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_CHIEF:
lpNewMonster = new CChiefMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_OBJECT:
lpNewMonster = new CObjectMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_GUARD:
lpNewMonster = new CGuardMonster(tempInfo, true);
break;
case MonsterInfo::PATTERN_GATHER:
lpNewMonster = new CGatherMonster(tempInfo, true);
break;
default:
lpNewMonster = new CMonster(tempInfo, true);
break;
};
if (0 == lpNewMonster)
{
ERRLOG0(g_Log, "몬스터 생성에 실패하였습니다.");
return false;
}
lpNewMonster->SetMapIndex(wMapIndex);
if (false == lpNewMonster->InitMonster(tempInfo.m_Pos))
{
ERRLOG0(g_Log, "몬스터 초기화에 실패하였습니다.");
return false;
}
return AddMonster(lpNewMonster);
}
unsigned short CVirtualMonsterMgr::GetAvailableMonsterUID(unsigned short wKindID)
{
if (m_AdminMonsterUIDMap.end() == m_AdminMonsterUIDMap.find(wKindID))
{
m_AdminMonsterUIDMap.insert(std::make_pair(wKindID, INIT_UID)).second;
}
unsigned long nUID = ((m_AdminMonsterUIDMap[wKindID] << 16) | wKindID);
if (NULL != GetMonster(nUID))
{
if (m_AdminMonsterUIDMap[wKindID] == INIT_UID)
{
// 여유 공간이 없다면...
return NO_BLANK_UID;
}
m_AdminMonsterUIDMap[wKindID] = INIT_UID - 1;
return INIT_UID;
}
return m_AdminMonsterUIDMap[wKindID]--;
}

View File

@@ -0,0 +1,70 @@
#ifndef _VIRTUAL_AREA_MONSTER_MANAGER_H_
#define _VIRTUAL_AREA_MONSTER_MANAGER_H_
#pragma once
#pragma warning(disable:4800)
#include <map>
#include <functional>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
// 전방 참조
class CCreature;
class CAggresiveCreature;
class CCharacter;
class CMonster;
struct Position;
// Singleton 아님!!
class CVirtualMonsterMgr
{
public:
CVirtualMonsterMgr();
~CVirtualMonsterMgr();
enum eAdminMonster
{
NO_BLANK_UID = 0x8FFF,
INIT_UID = 0x8FFE
};
bool AddMonster(CMonster* lpMonster);
CCreature* GetCreature(unsigned long dwCID);
CAggresiveCreature* GetAggresiveCreature(unsigned long dwCID);
CMonster* GetMonster(unsigned long dwCID);
bool IsSummonee(unsigned long dwCID);
bool SummonMonster(int nKID, Position Pos, CCharacter* lpMaster);
bool AdminSummonMonster(int nKID, Position Pos, unsigned short wMapIndex);
void ProcessAllMonster();
void ProcessMonsterRegenHPAndMP();
void ProcessSummonMonsterDead(void);
unsigned short GetAvailableMonsterUID(unsigned short wKindID);
unsigned short GetMonsterNum(void) { return static_cast<unsigned short>(m_MonsterMap.size()); }
// Type 정의
typedef std::map<unsigned long, CMonster*, std::less<unsigned long>,
boost::fast_pool_allocator<std::pair<unsigned long, CMonster*> > > MonsterMap;
typedef std::map<unsigned short, unsigned short> AdminMonsterUIDMap; // < KindID, NextUID(상위16비트) >
MonsterMap& GetMonsterMap() { return m_MonsterMap; }
private:
void DestroyMonsterList();
MonsterMap m_MonsterMap;
AdminMonsterUIDMap m_AdminMonsterUIDMap;
// 룬 오프 소환 몬스터 카운터 수
unsigned short m_usSummonCount;
};
#endif // _VIRTUAL_AREA_MONSTER_MANAGER_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
#ifndef _CNPC_H_
#define _CNPC_H_
#include <Item/Item.h>
#include <ScriptEngine/ScriptEngine.h>
#include <Creature/Creature.h>
// 전방 참조
namespace Quest
{
struct QuestNode;
};
class CNPC : public CCreature
{
public:
enum Const
{
MAX_CONTACT_RANGE = 15,
DISPLAY_CYCLE = 1800000 // 30분
};
struct GoodInfo
{
unsigned short m_wItemID;
unsigned char m_cRace;
unsigned char m_cTabPage;
Item::EquipType::Grade m_ePreGrade;
Item::EquipType::Grade m_eCurrentGrade;
GoodInfo();
GoodInfo(unsigned short wItemID, unsigned char cRace, unsigned char cTabPage);
bool operator < (const GoodInfo& rhs) { return (m_wItemID < rhs.m_wItemID); }
};
CNPC(unsigned long dwCID, int nZone, int nTownID, bool bBelongToCastle);
virtual ~CNPC();
// 기본은 스크립트에서 읽어오되, 공성의 Guild 소속에 따라 변경될 수 있습니다.
virtual unsigned char GetNation(void) const;
void SetNation(unsigned char cNation) { m_cNation = cNation; }
virtual EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget, unsigned char* cResult = NULL ) { return EnemyCheck::EC_NEUTRAL; }
virtual CParty* GetParty(void) { return 0; }
virtual bool IsPeaceMode(void) { return true; }
virtual bool IsRideArms(void) const { return false; }
virtual unsigned long GetGID(void) const { return m_dwGID; }
bool IsBelongToCastle(void) const;
int GetZone(void) { return m_nZone; }
unsigned long GetTownID(void) const { return m_dwTownOrNameID; }
unsigned long GetCastleNameID(void) const { return m_dwTownOrNameID; }
unsigned long GetDisplayTime(void) const { return m_dwDisplayTime; }
void SetGID(unsigned long dwGID) { m_dwGID = dwGID; }
bool GetQuest(CCharacter* lpCharacter, unsigned short wQuestID, Quest::QuestNode** lppQuestNode);
virtual unsigned long RepairItem(Item::CEquipment* pEquipment, unsigned long& dwCurrentGold);
Item::CItem* SellToCharacter(CCharacter* lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long& dwPrice, unsigned short wCouponID, unsigned short &usError);
unsigned char GetEquipShopInfo(unsigned char cRace, unsigned char cTabPage,
unsigned short *aryItemID, unsigned char *aryItemGrade);
static bool LoadNPCInfo(void);
// -- internal use for load NPC objects
void SortGoods(void) { std::sort(m_Goods.begin(), m_Goods.end()); }
void SortQuests(void) { std::sort(m_Quests.begin(), m_Quests.end()); }
void SetFlags(unsigned long dwFlags) { m_dwFlags |= dwFlags; } // 상인 속성 추가로 세팅
void AddGoodsToNPC(GoodInfo goodInfo) { m_Goods.push_back(goodInfo); }
void AddQuestsToNPC(unsigned short wQuestID) { m_Quests.push_back(wQuestID); }
void SetItemDropGrade(unsigned char cDropType, unsigned char cGradeF, unsigned char cGradeD,
unsigned char cGradeC, unsigned char cGradeB, unsigned char cGradeA);
void SetItemDropBaseNum(unsigned char cDropType, unsigned char cGradeF, unsigned char cGradeD,
unsigned char cGradeC, unsigned char cGradeB, unsigned char cGradeA);
void SetRandomGrade(void);
void SetNPCPosition(Position& position) { m_CurrentPos = position; }
private:
std::vector<GoodInfo> m_Goods;
std::vector<unsigned short> m_Quests;
int m_nZone;
unsigned long m_dwTownOrNameID;
unsigned long m_dwGID; // Vincent : GID 를 Creature 로 옮길까?
unsigned long m_dwFlags;
unsigned long m_dwDisplayTime;
unsigned char m_cGradeRate[Item::EquipType::MAX_OPTION_TYPE][Item::EquipType::MAX_GRADE];
unsigned char m_cBaseNum[Item::EquipType::MAX_OPTION_TYPE][Item::EquipType::MAX_GRADE];
unsigned char m_cNation;
};
#endif

View File

@@ -0,0 +1,251 @@
#include "stdafx.h"
#include "Airship.h"
#include <Skill/SkillTable.h>
#include <Skill/SkillMgr.h>
#include <Utility/Math/Math.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/ClientSocket/ClientConstants.h>
CAirship::CAirship(MonsterCreateInfo& MonsterCreate, unsigned long dwOwnerID, unsigned char cNation,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState,
unsigned char cUpgradeStep)
: CSiegeArms(MonsterCreate, dwOwnerID, cNation, dwHP, wObjectType, cState, cUpgradeStep)
{
std::fill_n(&m_dwPassengerCID[0], int(Siege::AIRSHIP_PASSENGER_NUM), 0);
}
CAirship::~CAirship()
{
}
bool CAirship::Dead(CAggresiveCreature* pOffencer)
{
if (NULL == pOffencer) return false;
if (STATE_ID_DIE == m_nCurrentState) return false;
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_bCasting = false;
// 타고 있던 캐릭터는 죽는다.
CCharacter* lpRider = NULL;
if (m_dwRiderCID)
{
lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if ( lpRider )
{
lpRider->GetOff();
lpRider->Kill(pOffencer);
}
m_dwRiderCID = 0;
}
for (int i=0; i<Siege::AIRSHIP_PASSENGER_NUM; ++i)
{
lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwPassengerCID[i]);
if ( lpRider )
{
lpRider->GetOff();
lpRider->Kill(pOffencer);
}
m_dwPassengerCID[i] = 0;
}
// 크리쳐 매니져에서 삭제 (해당 셀에서도 삭제한다.)
CCreatureManager::GetInstance().DeleteCreature(m_dwCID);
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
return GameClientSendPacket::SendCharSiegeArmsCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), 0, m_dwCID,
0, PktSiegeArmsCmd::SIEGE_DESTROY_ARMS,
PktSiegeArmsCmd::NO_SERVER_ERR);
}
return false;
}
void CAirship::MoveTo(const Position& NewPosition)
{
CSiegeObject::MoveTo(NewPosition);
for (int i=0; i<Siege::AIRSHIP_PASSENGER_NUM; ++i)
{
if (0 != m_dwPassengerCID[i])
{
CCharacter* lpPassenger = CCreatureManager::GetInstance().GetCharacter(m_dwPassengerCID[i]);
if (lpPassenger)
{
lpPassenger->MoveTo(NewPosition, false);
}
}
}
}
unsigned char CAirship::IsRider(unsigned long dwCID) const
{
if (m_dwRiderCID == dwCID)
{
return Siege::RIDER_FOR_OWNER;
}
else
{
for (int i=0; i<Siege::AIRSHIP_PASSENGER_NUM; ++i)
{
if (dwCID == m_dwPassengerCID[i])
{
return Siege::RIDER_FOR_PASSENGER;
}
}
}
return Siege::NOT_RIDER;
}
bool CAirship::Ride(unsigned long dwCID)
{
if (dwCID == m_dwOwnerID)
{
return CSiegeArms::Ride(dwCID);
}
else if (Siege::NOT_RIDER == IsRider(dwCID))
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (lpRider)
{
for (int i=0; i<Siege::AIRSHIP_PASSENGER_NUM; ++i)
{
if (0 == m_dwPassengerCID[i] && 0 != m_cNation && m_cNation == lpRider->GetNation())
{
m_dwPassengerCID[i] = dwCID;
lpRider->Ride(m_dwCID);
lpRider->SkillClear();
// Ride 함수 자체에서 클라이언트에게 패킷을 보내준다.
PktSiegeArmsCmd pktSAC;
pktSAC.m_dwCID = GetOwnerID();
pktSAC.m_dwArmsID = m_dwCID;
pktSAC.m_cState = m_cState;
pktSAC.m_dwValue = dwCID;
pktSAC.m_cSubCmd = PktSiegeArmsCmd::SIEGE_RIDE_ARMS;
char* szPacket = reinterpret_cast<char *>(&pktSAC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd, 0, 0))
{
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
}
return true;
}
}
}
}
return false;
}
bool CAirship::GetOff(unsigned long dwCID)
{
if (dwCID == m_dwRiderCID)
{
return CSiegeArms::GetOff(dwCID);
}
else if (Siege::NOT_RIDER != IsRider(dwCID))
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (lpRider)
{
for (int i=0; i<Siege::AIRSHIP_PASSENGER_NUM; ++i)
{
if (dwCID == m_dwPassengerCID[i])
{
m_dwPassengerCID[i] = 0;
lpRider->GetOff();
// GetOff 함수 자체에서 클라이언트에게 패킷을 보내준다.
PktSiegeArmsCmd pktSAC;
pktSAC.m_dwCID = GetOwnerID();
pktSAC.m_dwArmsID = m_dwCID;
pktSAC.m_cState = m_cState;
pktSAC.m_dwValue = dwCID;
pktSAC.m_cSubCmd = PktSiegeArmsCmd::SIEGE_GETOFF_ARMS;
char* szPacket = reinterpret_cast<char *>(&pktSAC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd, 0, 0))
{
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
}
return true;
}
}
}
}
return false;
}
void CAirship::AllGetOff()
{
// 주인이 타고 있으면, 주인부터 내린다.
if (0 != m_dwRiderCID)
{
GetOff(m_dwRiderCID);
}
// 손님 내리기
for (int i=0; i<Siege::AIRSHIP_PASSENGER_NUM; ++i)
{
if (0 != m_dwPassengerCID[i])
{
GetOff(m_dwPassengerCID[i]);
}
}
}
void CAirship::GetRiders(unsigned long* pRiders) const
{
CSiegeObject::GetRiders(pRiders);
std::copy( &m_dwPassengerCID[0], &m_dwPassengerCID[ Siege::AIRSHIP_PASSENGER_NUM ], &pRiders[1] );
}
unsigned char CAirship::GetRiderNum() const
{
unsigned char cRiderNum = CSiegeObject::GetRiderNum();
for (int i=0; i<Siege::AIRSHIP_PASSENGER_NUM; ++i)
{
if (0 != m_dwPassengerCID[i])
{
++cRiderNum;
}
}
return cRiderNum;
}

View File

@@ -0,0 +1,49 @@
#ifndef _AIRSHIP_OBJECT_H_
#define _AIRSHIP_OBJECT_H_
#pragma once
#include <Creature/Siege/SiegeArms.h>
using namespace Siege;
class CAirship : public CSiegeArms
{
public:
virtual ~CAirship();
// CSkillMonster 의 기능
void NormalBehavior(unsigned long dwTick) { }
void AttackBehavior(unsigned long dwTick) { }
void SearchPlayer(void) { }
bool Dead(CAggresiveCreature* pOffencer);
void MoveTo(const Position& NewPosition);
// 드랍쉽은 아래 함수의 구현이 없다.
bool AttackCID(CCharacter* lpRideChar, AtType attackType, AtNode& attackNode, unsigned short& wError) { return false; }
bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders,
unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal) { return false; }
bool MissileAttack(AtType attackType, float fDir, float nRange, float fAngle, char cTargetType) { return false; }
// Rider 관련 정보
unsigned char IsRider(unsigned long dwCID) const; // 해당 캐릭터가 병기에 탑승해 있는가?
bool Ride(unsigned long dwCID); // 병기 탑승
bool GetOff(unsigned long dwCID); // 병기 내림 (중계 서버로 패킷 전송)
void AllGetOff(); // 병기에서 모두 내림 (패킷 전송)
void GetRiders( unsigned long* pRiders ) const;
unsigned char GetRiderNum() const;
private:
CAirship(MonsterCreateInfo& MonsterCreate, unsigned long dwOwnerID, unsigned char cNation,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState, unsigned char cUpgradeStep);
unsigned long m_dwPassengerCID[Siege::AIRSHIP_PASSENGER_NUM]; // 승객 CID
friend class CSiegeObjectMgr;
};
#endif _AIRSHIP_OBJECT_H_

View File

@@ -0,0 +1,136 @@
#include "stdafx.h"
#include "BroadCastSiegeObjectData.h"
BroadCastSiege::CSiegeObjectData::CSiegeObjectData() :
m_dwCID(0), m_dwUpdateDataFlag(0)
{
memset( &m_OwershipInfo, 0, sizeof( sOwnerShipInfo ) );
memset( &m_StateInfo, 0, sizeof( sStateInfo ) );
memset( &m_HPInfo, 0, sizeof( sHPInfo ) );
memset( &m_PosInfo, 0, sizeof( sPosInfo ) );
memset( &m_MaterialInfo, 0, sizeof( sMaterialInfo ) );
memset( &m_RiderInfo, 0, sizeof( sRiderInfo ) );
m_PosInfo.m_NetworkPos.Initialize( 0, 0, 0, 0, 0 );
}
BroadCastSiege::CSiegeObjectData::~CSiegeObjectData()
{
}
const void
BroadCastSiege::CSiegeObjectData::GetRiders( unsigned long* pRiders ) const
{
std::copy( &m_RiderInfo.m_dwRiderID[0], &m_RiderInfo.m_dwRiderID[Siege::AIRSHIP_RIDER_NUM ], pRiders );
}
const bool
BroadCastSiege::CSiegeObjectData::IsSameRiders( unsigned long* pRiders ) const
{
for ( int i=0; i<Siege::AIRSHIP_RIDER_NUM; ++i )
{
if ( m_RiderInfo.m_dwRiderID[i] != pRiders[i] )
{
return false;
}
}
return true;
}
void
BroadCastSiege::CSiegeObjectData::SetRiders( unsigned long* pRiders )
{
std::copy( &pRiders[0], &pRiders[ Siege::AIRSHIP_RIDER_NUM ], m_RiderInfo.m_dwRiderID );
}
void
BroadCastSiege::CSiegeObjectData::ClearRiders()
{
std::fill_n( &m_RiderInfo.m_dwRiderID[0], int( Siege::AIRSHIP_RIDER_NUM ), 0 );
}
// ===================================================================================
// Data 처리 함수
void
BroadCastSiege::CSiegeObjectData::ClearData()
{
memset( &m_OwershipInfo, 0, sizeof( sOwnerShipInfo ) );
memset( &m_StateInfo, 0, sizeof( sStateInfo ) );
memset( &m_HPInfo, 0, sizeof( sHPInfo ) );
memset( &m_PosInfo, 0, sizeof( sPosInfo ) );
memset( &m_MaterialInfo, 0, sizeof( sMaterialInfo ) );
memset( &m_RiderInfo, 0, sizeof( sRiderInfo ) );
m_PosInfo.m_NetworkPos.Initialize( 0, 0, 0, 0, 0 );
m_dwCID = m_dwUpdateDataFlag = 0;
}
//! 버퍼와 버퍼 길이를 인자로 받아서 데이터를 업데이트한다.
//! 리턴시에 버퍼 길이에, 사용한 버퍼 길이를 넣어 준다.
//! 리턴값은 성공/실패 여부를 리턴한다.
bool
BroadCastSiege::CSiegeObjectData::UpdateData( unsigned long dwCID, const char* szData, int& iBufferSize_InOut )
{
// 데이터 순서는 다음과 같다.
// UpdateFlag( 4 byte )
// 기본 소유 정보 ( 3 * 4 + 1 byte = 13 byte )
// 상태 정보 ( 6 byte )
// HP 정보 ( 2 * 4 byte = 8 byte )
// 위치 정보 ( 12 byte )
// 자재 정보 ( 1 byte )
// 탑승자 정보 ( 10 * 4 byte = 40 byte )
// Total -> 84 byte
unsigned long dwUpdateFlag = 0;
const char* szDataPos = szData;
const int iBufferSize = iBufferSize_InOut;
if ( iBufferSize < sizeof(unsigned long) )
{
return false;
}
// Update Flag 읽어오기
COPY_AND_ADVANCE_SRC( &dwUpdateFlag, szDataPos, sizeof(unsigned long) );
// 읽어온 UpdateFlag로, 필요한 데이터의 양을 추산한다. 데이터 양이 부족하면 에러를 뱉는다.
int iEstimateBufferSize = EstimateBufferSize( dwUpdateFlag );
if ( iBufferSize < iEstimateBufferSize )
{
// 버퍼 크기가 추산한 길이보다 작은 경우는 실패.
return false;
}
// 데이터 복사. 순서 바뀌면 죽는다!!.
m_dwCID = dwCID;
if ( dwUpdateFlag & DELTA_OWNERSHIP ) { COPY_AND_ADVANCE_SRC( &m_OwershipInfo, szDataPos, sizeof( sOwnerShipInfo ) ); }
if ( dwUpdateFlag & DELTA_STATE ) { COPY_AND_ADVANCE_SRC( &m_StateInfo, szDataPos, sizeof( sStateInfo ) ); }
if ( dwUpdateFlag & DELTA_HP ) { COPY_AND_ADVANCE_SRC( &m_HPInfo, szDataPos, sizeof( sHPInfo ) ); }
if ( dwUpdateFlag & DELTA_POS ) { COPY_AND_ADVANCE_SRC( &m_PosInfo, szDataPos, sizeof( sPosInfo ) ); }
if ( dwUpdateFlag & DELTA_MATERIAL ) { COPY_AND_ADVANCE_SRC( &m_MaterialInfo, szDataPos, sizeof( sMaterialInfo ) ); }
if ( dwUpdateFlag & DELTA_RIDER ) { COPY_AND_ADVANCE_SRC( &m_RiderInfo, szDataPos, sizeof( sRiderInfo ) ); }
iBufferSize_InOut = static_cast<int>( szDataPos - szData );
// SetDataFlag(Broadcast2nd::CHARDATA_CHANGED);
return true;
}
int
BroadCastSiege::CSiegeObjectData::EstimateBufferSize( unsigned long dwUpdateFlag )
{
int iEstimateBufferSize = 0;
if ( dwUpdateFlag & DELTA_OWNERSHIP ) { iEstimateBufferSize += sizeof( sOwnerShipInfo ); }
if ( dwUpdateFlag & DELTA_STATE ) { iEstimateBufferSize += sizeof( sStateInfo ); }
if ( dwUpdateFlag & DELTA_HP ) { iEstimateBufferSize += sizeof( sHPInfo ); }
if ( dwUpdateFlag & DELTA_POS ) { iEstimateBufferSize += sizeof( sPosInfo ); }
if ( dwUpdateFlag & DELTA_MATERIAL ) { iEstimateBufferSize += sizeof( sMaterialInfo ); }
if ( dwUpdateFlag & DELTA_RIDER ) { iEstimateBufferSize += sizeof( sRiderInfo ); }
return iEstimateBufferSize;
}

View File

@@ -0,0 +1,212 @@
#ifndef __BROADCAST_SIEGEOBJECT_DATA_H__
#define __BROADCAST_SIEGEOBJECT_DATA_H__
#include <winsock2.h>
#include <windows.h>
#include <Network/Packet/PacketStruct/CharMovePacket.h>
#include <Creature/Siege/SiegeConstants.h>
//! 본 파일은 서버/클라이언트가 전부 사용하는 파일이다.
//! 공성 오브젝트 데이터 중 클라이언트가 꼭 알아야 할 내용들을 담고 있다.
namespace BroadCastSiege
{
inline void COPY_AND_ADVANCE_SRC( void* dst, const char*& src, size_t size )
{
memcpy( dst, src, size ); src += size;
}
inline void COPY_AND_ADVANCE_DST( char*& dst, const void* src, size_t size )
{
memcpy( dst, src, size ); dst += size;
}
#define FLOAT_EPSILON 0.001
// Update Data Flag
enum SiegeDeltaInfo
{
DELTA_OWNERSHIP = 1 << 0, // dwCampOrCastleID, OwnerID, GID, cNation
DELTA_STATE = 1 << 1, // wObjectType, cState, cSubState, cUpgradeStep, cUpgradeType
DELTA_HP = 1 << 2, // HP 정보
DELTA_POS = 1 << 3, // fDir, Pos
DELTA_MATERIAL = 1 << 4, // cMaterial
DELTA_RIDER = 1 << 5, // dwRiderCID[]
};
enum Const
{
// UpdateFlag ( 4 byte )
// OwnerShipInfo ( 13 byte )
// StateInfo( 6 byte )
// HPInfo ( 8 byte )
// PosInfo ( 12 byte )
// MaterialInfo ( 1 byte )
// RiderInfo ( 40 byte )
// Total : 84 byte
MAX_SIEGEOBJECT_DATA_SIZE = 120,
MAX_SIEGEBROADCAST_BUFFER_SIZE = 160 // sizeof(PktNewSiegeBroadCast) + MAX_SIEGEOBJECT_DATA_SIZE
};
enum BroadCastType
{
BROADCAST_ALL_DATA = 1, // 모든 데이터 전송
BROADCAST_DELTA_DATA = 2, // 변경된 데이터 전송
BROADCAST_DELETE_DATA = 3 // 해당 크리쳐 삭제 데이터 전송
};
// 소유 정보 (성ID, 길드 요새ID, 소유자CID), 길드ID
struct sOwnerShipInfo
{
unsigned long m_dwCampOrCastleID;
unsigned long m_dwOwnerCID;
unsigned long m_dwGID;
unsigned char m_cNation;
};
// 공성 오브젝트 상태 정보
struct sStateInfo
{
unsigned short m_wObjectType;
unsigned char m_cState;
unsigned char m_cSubState;
unsigned char m_cUpgradeStep;
unsigned char m_cUpgradeType;
};
// HP 정보
struct sHPInfo
{
unsigned long m_dwNowHP;
unsigned long m_dwMaxHP;
};
// 위치 정보
struct sPosInfo
{
float m_fDefaultDir;
CNetworkPos m_NetworkPos;
};
// 자재 정보
struct sMaterialInfo
{
unsigned char m_cMaterial;
};
// 탑승자 정보
struct sRiderInfo
{
unsigned long m_dwRiderID[Siege::AIRSHIP_RIDER_NUM];
};
// =============================================================================================
// SiegeObject Data
class CSiegeObjectData
{
public:
CSiegeObjectData();
~CSiegeObjectData();
// ===================================================================================
// Get 함수
const unsigned long GetCID() const { return m_dwCID; }
const sOwnerShipInfo& GetOwnerShipInfo() const { return m_OwershipInfo; }
const unsigned long GetCastleID() const { return m_OwershipInfo.m_dwCampOrCastleID; }
const unsigned long GetCampID() const { return m_OwershipInfo.m_dwCampOrCastleID; }
const unsigned long GetOwnerCID() const { return m_OwershipInfo.m_dwOwnerCID; }
const unsigned long GetGID() const { return m_OwershipInfo.m_dwGID; }
const unsigned char GetNation() const { return m_OwershipInfo.m_cNation; }
const sStateInfo& GetStateInfo() const { return m_StateInfo; }
const unsigned short GetObjectType() const { return m_StateInfo.m_wObjectType; }
const unsigned char GetState() const { return m_StateInfo.m_cState; }
const unsigned char GetSubState() const { return m_StateInfo.m_cSubState; }
const unsigned char GetUpgradeType() const { return m_StateInfo.m_cUpgradeType; }
const unsigned char GetUpgradeStep() const { return m_StateInfo.m_cUpgradeStep; }
const sHPInfo& GetHPInfo() const { return m_HPInfo; }
const unsigned long GetNowHP() const { return m_HPInfo.m_dwNowHP; }
const unsigned long GetMaxHP() const { return m_HPInfo.m_dwMaxHP; }
const sPosInfo& GetPosInfo() const { return m_PosInfo; }
const float GetDefaultDir() const { return m_PosInfo.m_fDefaultDir; }
const CNetworkPos& GetNetworkPos() const { return m_PosInfo.m_NetworkPos; }
const sMaterialInfo& GetMaterialInfo() const { return m_MaterialInfo; }
const unsigned char GetMaterialNum() const { return m_MaterialInfo.m_cMaterial; }
const sRiderInfo& GetRiderInfo() const { return m_RiderInfo; }
const void GetRiders( unsigned long* pPassenger ) const;
const bool IsSameRiders( unsigned long * pPassenger ) const;
// ===================================================================================
// Set 함수
void SetOwnerShipInfo( const sOwnerShipInfo& info ) { m_OwershipInfo = info; }
void SetCastleID( unsigned long dwCastleID ) { m_OwershipInfo.m_dwCampOrCastleID = dwCastleID; }
void SetCampID( unsigned long dwCampID ) { m_OwershipInfo.m_dwCampOrCastleID = dwCampID; }
void SetOwnerCID( unsigned long dwOwnerCID ) { m_OwershipInfo.m_dwOwnerCID = dwOwnerCID; }
void SetGID( unsigned long dwGID ) { m_OwershipInfo.m_dwGID = dwGID; }
void SetNation( unsigned char cNation ) { m_OwershipInfo.m_cNation = cNation; }
void SetStateInfo( const sStateInfo& info ) { m_StateInfo = info; }
void SetObjectType( unsigned short wType ) { m_StateInfo.m_wObjectType = wType; }
void SetState( unsigned char cState ) { m_StateInfo.m_cState = cState; }
void SetSubState( unsigned char cSubState ) { m_StateInfo.m_cSubState = cSubState; }
void SetUpgradeType( unsigned char cType ) { m_StateInfo.m_cUpgradeType = cType; }
void SetUpgradeStep( unsigned char cStep ) { m_StateInfo.m_cUpgradeStep = cStep; }
void SetHPInfo( const sHPInfo& info ) { m_HPInfo = info; }
void SetNowHP( unsigned long dwNowHP ) { m_HPInfo.m_dwNowHP = dwNowHP; }
void SetMaxHP( unsigned long dwMaxHP ) { m_HPInfo.m_dwMaxHP = dwMaxHP; }
void SetPosInfo( const sPosInfo& info ) { m_PosInfo = info; }
void SetDefaultDir( float fDir ) { m_PosInfo.m_fDefaultDir = fDir; }
void SetNetworkPos( const CNetworkPos& pos) { m_PosInfo.m_NetworkPos = pos; }
void SetMaterialInfo( const sMaterialInfo& info ) { m_MaterialInfo = info; }
void SetMaterialNum( unsigned char cMaterial ) { m_MaterialInfo.m_cMaterial = cMaterial; }
void SetRiderInfo( const sRiderInfo& info ) { m_RiderInfo = info; }
void SetRiders( unsigned long* pPassenger );
void ClearRiders();
// ===================================================================================
// Data 처리 함수
void ClearData();
//! 버퍼와 버퍼 길이를 인자로 받아서 데이터를 업데이트한다.
//! 리턴시에 버퍼 길이에, 사용한 버퍼 길이를 넣어 준다.
//! 리턴값은 성공/실패 여부를 리턴한다.
bool UpdateData( unsigned long dwCID, const char* szData, int& iBufferSize_InOut );
static int EstimateBufferSize( unsigned long dwUpdateFlag );
// ===================================================================================
// Data Flag 관련 함수
void SetUpdateDataFlag( unsigned long dwFlag ) { m_dwUpdateDataFlag |= dwFlag; }
bool HasUpdateDataFlag( unsigned long dwFlag ) { return 0 != (m_dwUpdateDataFlag & dwFlag); }
void ResetUpdateDataFlag( unsigned long dwFlag ) { m_dwUpdateDataFlag &= ~dwFlag; }
void ClearUpdateDataFlag() { m_dwUpdateDataFlag = 0; }
unsigned long GetDataFlag() const { return m_dwUpdateDataFlag; }
private:
unsigned long m_dwCID;
sOwnerShipInfo m_OwershipInfo;
sStateInfo m_StateInfo;
sHPInfo m_HPInfo;
sPosInfo m_PosInfo;
sMaterialInfo m_MaterialInfo;
sRiderInfo m_RiderInfo;
unsigned long m_dwUpdateDataFlag;
};
}
#endif __BROADCAST_SIEGEOBJECT_DATA_H__

View File

@@ -0,0 +1,602 @@
#include "stdafx.h"
#include "Camp.h"
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include <Skill/SkillTable.h>
#include <Skill/SkillMgr.h>
#include <Utility/Math/Math.h>
#include <Item/Item.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/ClientSocket/ClientConstants.h>
CCamp::CCamp(MonsterCreateInfo& MonsterCreate, unsigned long dwCampID, unsigned long dwGID,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState,
unsigned char cSubState, unsigned char cUpgradeStep, unsigned char cMaterial,
unsigned char cSiegeCount, const CampRight& campRight, bool bFullHP)
: CSiegeObject(MonsterCreate, dwCampID, dwGID, dwHP, wObjectType, cState, cSubState, cUpgradeStep, cMaterial, cSiegeCount, bFullHP),
m_CampRight(campRight), m_dwLastAttackedTick(0), m_dwCmdSenderCID(0)
{
}
CCamp::~CCamp()
{
}
void CCamp::NormalBehavior(unsigned long dwTick)
{
// 선공 처리
if (NULL == m_lpTarget && true == m_MonsterInfo.m_bFirstAttack)
{
SearchPlayer();
}
}
void CCamp::AttackBehavior(unsigned long dwTick)
{
PERFORMANCE_CHECK(FunctionTimingCheck)
// 구축중, 구축 취소중, 파괴되었으면 공격할 수 없다.
if (Siege::DEVELOPING == m_cState ||
Siege::CANCELING == m_cState ||
Siege::DESTROYED == m_cState)
{
CancelTarget();
return;
}
// 마법 캐스팅 중일때는.. 아무런 다른 행동을 해서는 안된다.
if (true == m_bCasting)
{
CastingAttackAction();
return;
}
m_lpTarget = m_Threat.GetTarget();
if (NULL == m_lpTarget ||
(m_lpTarget && true == m_lpTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Hide)))
{
CancelTarget();
return;
}
const float fDY = fabs(m_lpTarget->GetCurrentPos().m_fPointY - GetCurrentPos().m_fPointY);
const float fDX = m_lpTarget->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX;
const float fDZ = m_lpTarget->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ;
const float fDistance = sqrtf((fDX * fDX) + (fDZ * fDZ));
// 거리 체크
if (fDistance > Siege::CAMP_ATTACK_RANGE || 0 == m_lpTarget->GetStatus().m_nNowHP)
{
CancelTarget();
return;
}
if (0 >= m_lCurrentFrame)
{
// 공격 범위 밖이다.
if (true == SkillAttack()) // 스킬 공격을 사용하는가 체크
{
SkillAttackAction();
}
if (false == m_bAttacking)
{
m_lCurrentFrame = m_MotionInfo.m_dwFrame;
}
}
else
{
// 공격 범위 안이다.
if (false == m_bAttacking)
{
if (true == SkillAttack()) // 스킬 공격을 사용하는가 체크
{
SkillAttackAction();
}
}
}
// 방향을 고정시켜둔다.
m_MotionInfo.m_fDirection = 0;
}
void CCamp::SearchPlayer(void)
{
// TODO : 해상도 조절을 통해 float 계산을 없애보자.
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG1(g_Log, "CID:0X%08 공성 오브젝트가 셀 범위 밖에 있습니다.", m_dwCID);
return;
}
CCell* pCell = NULL;
CCharacter* pTempTarget = NULL;
CCharacter* pCurrentTarget = NULL;
const float fSquareSearchRange = (float)(Siege::CAMP_ATTACK_RANGE * Siege::CAMP_ATTACK_RANGE);
for (int nCellCount = 0; nCellCount < CCell::CONNECT_NUM; ++nCellCount)
{
pCell = m_CellPos.m_lpCell->GetConnectCell(nCellCount);
if (NULL == pCell || false == pCell->IsCharacter())
{
continue;
}
pTempTarget = pCell->GetFirstCharacter();
while (NULL != pTempTarget)
{
const float fDistY = fabs(pTempTarget->GetCurrentPos().m_fPointY - GetCurrentPos().m_fPointY);
if (pTempTarget->GetStatus().m_nNowHP > 0 && EnemyCheck::EC_ENEMY == IsEnemy(pTempTarget))
{
if (false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Stealth) &&
false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Invincible) &&
false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Hide))
{
const float fDX = pTempTarget->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX;
const float fDZ = pTempTarget->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ;
const float fDistance = (fDX * fDX) + (fDZ * fDZ);
if (fDistance < fSquareSearchRange)
{
pCurrentTarget = pTempTarget;
break;
}
}
}
pTempTarget = pCell->GetNextCharacter();
}
}
if (NULL != pCurrentTarget)
{
m_Threat.AddToThreatList(pCurrentTarget, 1);
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_SEEN_PLAYER);
}
}
bool CCamp::Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal)
{
return CSkillMonster::Attack(attackType, cDefenderNum, ppDefenders, cDefenserJudges, wDefenserMPHeal);
}
bool CCamp::Dead(CAggresiveCreature* pOffencer)
{
if (STATE_ID_DIE == m_nCurrentState) { return false; }
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_bCasting = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP);
// 크리쳐 매니져에서 삭제 (해당 셀에서도 삭제한다.)
CCreatureManager::GetInstance().DeleteCreature(m_dwCID);
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
if ( pOffencer )
{
DETLOG3(g_Log, "길드 요새 이벤트 로그 : 파괴된 요새 아이디(0x%08x), 파괴한 캐릭터 아이디(0x%08x), 파괴한 길드 아이디(0x%08x)",
GetCampID(), pOffencer->GetCID(), pOffencer->GetGID());
return GameClientSendPacket::SendCharCampCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), pOffencer->GetCID(), GetCampID(),
pOffencer->GetGID(), 0, PktCampCmd::CAMP_DESTROY, PktBase::NO_SERVER_ERR);
}
else
{
return GameClientSendPacket::SendCharCampCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), 0, GetCampID(),
0, 0, PktCampCmd::CAMP_DESTROY, PktBase::NO_SERVER_ERR);
}
}
return false;
}
bool CCamp::ToStarterKit(bool bFullMaterial)
{
// 아이템 생성
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem(Item::EtcItemID::CAMP_KIT_ID);
if (NULL == lpItem)
{
ERRLOG0(g_Log, "길드 요새 생성 스타트킷 아이템 생성에 실패했습니다.");
return false;
}
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(m_dwGID);
if (NULL != lpGuild)
{
Guild::MemberInfo MasterInfo = lpGuild->GetMaster();
unsigned long dwMasterID = MasterInfo.m_dwCID;
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter( dwMasterID );
if (NULL != lpCharacter)
{
if (false == lpCharacter->GiveItem(lpItem))
{
ERRLOG0(g_Log, "길드 요새 생성 스타트킷을 돌려주는데 실패하였습니다.");
DELETE_ITEM(lpItem);
return false;
}
// GievItem 으로 스택된 경우
if (lpItem->IsSet(Item::DetailData::STACKABLE) && 0 == lpItem->GetNumOrDurability())
{
DELETE_ITEM(lpItem);
}
return true;
}
}
// 바닥에 아이템 떨어뜨리기
CCell::ItemInfo itemInfo;
GetCellPos().m_lpCell->SetItem(GetCurrentPos(), lpItem, 0, 0, CCell::NONE, itemInfo);
return true;
}
// 길드 요새 파괴시 길드 마스터에게 돈을 돌려준다. (로그인해있을때만)
// (로그아웃해 있을 때는 중계 서버에서 돈을 돌려주는 처리를 한다.)
void CCamp::AddGoldToMaster(unsigned long dwGold)
{
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(m_dwGID);
if (lpGuild)
{
Guild::MemberInfo& MasterInfo = lpGuild->GetMaster();
CCharacter* lpMaster = CCreatureManager::GetInstance().GetCharacter( MasterInfo.m_dwCID );
if (lpMaster)
{
lpMaster->AddGold(dwGold, true);
}
}
}
// 길드 요새 구축 완료
bool CCamp::Build(unsigned char cUpgradeStep)
{
m_cState = Siege::COMPLETE;
UpdateObjectInfo();
// 해당 진지가 있는 반경 5셀 이내에 전송
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = 0;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCampCmd::CAMP_BUILD_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCampCmd), CmdCampCmd);
//SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCampCmd), CmdCampCmd);
// Vincent - 모든 캐릭터에게 전송해야 라지맵 정보가 제대로 갱신된다.
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCampCmd), CmdCampCmd);
return true;
}
return false;
}
bool CCamp::Cancel()
{
const int MAX_BUFFER = sizeof(PktCampCmd);
char szBuffer[MAX_BUFFER];
PktCampCmd* lpPktCC = reinterpret_cast<PktCampCmd*>(szBuffer);
lpPktCC->m_dwCID = m_dwCID;
lpPktCC->m_dwCampID = GetCampID();
lpPktCC->m_cState = Siege::DESTROYED;
lpPktCC->m_dwValue1 = 0;
lpPktCC->m_dwValue2 = 0;
lpPktCC->m_cSubCmd = PktCampCmd::CAMP_DESTROY;
if (true == PacketWrap::WrapCrypt(szBuffer, MAX_BUFFER, CmdCampCmd, 0 ,0))
{
CCreatureManager::GetInstance().SendAllCharacter(szBuffer, MAX_BUFFER, CmdCampCmd);
return true;
}
return false;
}
// 길드 요새 업그레이드 완료
bool CCamp::Upgrade(unsigned char cUpgradeStep)
{
m_cState = Siege::COMPLETE;
m_cUpgradeStep = cUpgradeStep;
UpdateObjectInfo(Siege::UPGRADE_HP);
// 해당 진지가 있는 반경 5셀 이내에 전송
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cUpgradeStep;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCampCmd::CAMP_UPGRADE_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCampCmd), CmdCampCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCampCmd), CmdCampCmd);
}
return true;
}
// 길드 요새 수리 완료
bool CCamp::Repair(unsigned short wRepairHP)
{
m_cState = Siege::COMPLETE;
UpdateObjectInfo(Siege::REPAIR_HP, wRepairHP);
// 해당 진지가 있는 반경 5셀 이내에 전송
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_CreatureStatus.m_nNowHP;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCampCmd::CAMP_REPAIR_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCampCmd), CmdCampCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCampCmd), CmdCampCmd);
}
return true;
}
bool CCamp::Destroy(unsigned long dwOffencerGID)
{
const int MAX_BUFFER = sizeof(PktCampCmd);
char szBuffer[MAX_BUFFER];
PktCampCmd* lpPktCC = reinterpret_cast<PktCampCmd*>(szBuffer);
lpPktCC->m_dwCID = m_dwCID;
lpPktCC->m_dwCampID = GetCampID();
lpPktCC->m_cState = Siege::DESTROYED;
lpPktCC->m_dwValue1 = 0;
lpPktCC->m_dwValue2 = 0;
lpPktCC->m_cSubCmd = PktCampCmd::CAMP_DESTROY;
if (true == PacketWrap::WrapCrypt(szBuffer, MAX_BUFFER, CmdCampCmd, 0 ,0))
{
CCreatureManager::GetInstance().SendAllCharacter(szBuffer, MAX_BUFFER, CmdCampCmd);
// 자재 드랍
CCell* lpCell = CCellManager::GetInstance().GetCell(0,
static_cast<unsigned long>(GetCurrentPos().m_fPointX),
static_cast<unsigned long>(GetCurrentPos().m_fPointY),
static_cast<unsigned long>(GetCurrentPos().m_fPointZ));
if (NULL == lpCell)
{
ERRLOG4(g_Log, "CampID:0x%08x 자재를 드랍할 길드 요새의 위치가 이상합니다. X:%.1f, Y:%.1f, Z:%.1f",
GetCampID(), GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointY, GetCurrentPos().m_fPointZ);
return false;
}
unsigned char cAmount = m_cMaterial + Siege::GetUpgradeMaterialNum(m_wObjectType) * m_cUpgradeStep / 2;
while (cAmount > 0)
{
Item::CItem* lpMaterial = Item::CItemFactory::GetInstance().CreateItem(Item::EtcItemID::SIEGE_MATERIAL_ID);
if (NULL == lpMaterial)
{
ERRLOG0(g_Log, "공성 병기 제작용 자재 아이템 생성에 실패했습니다.");
return false;
}
unsigned char cMaxNum = lpMaterial->GetMaxNumOrDurability();
if (cAmount < cMaxNum)
{
lpMaterial->SetNumOrDurability(cAmount);
cMaxNum = cAmount;
cAmount = 0;
}
else
{
lpMaterial->SetNumOrDurability(cMaxNum);
cAmount -= cMaxNum;
}
CCell::ItemInfo itemInfo;
lpCell->SetItem(GetCurrentPos(), lpMaterial, 0, dwOffencerGID,
(0 == dwOffencerGID) ? CCell::NONE : CCell::GUILD, itemInfo);
}
return true;
}
return false;
}
bool CCamp::ChangeType(unsigned short wChangeType)
{
m_cState = Siege::COMPLETE;
// 해당 진지가 있는 반경 5셀 이내에 전송
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = wChangeType;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCampCmd::CAMP_CHANGE_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCampCmd), CmdCampCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCampCmd), CmdCampCmd);
}
return true;
}
// 길드 요새 정보 업데이트
bool CCamp::Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwNoValue, unsigned char cSubCmd)
{
m_cState = cState;
UpdateObjectInfo();
// 해당 진지가 있는 반경 5셀 이내에 전송
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = dwValue1;
pktCC.m_dwValue2 = dwValue2;
pktCC.m_cSubCmd = cSubCmd;
// 자재 소모를 필요로한 명령은 남은 자재의 갯수를 보내준다.
switch (cSubCmd)
{
case PktCampCmd::CAMP_UPGRADE:
{
m_cUpgradeStep = static_cast<unsigned char>(dwValue1);
pktCC.m_dwValue2 = m_cMaterial;
}
break;
case PktCampCmd::CAMP_REPAIR:
case PktCampCmd::CAMP_CHANGE_TYPE:
{
pktCC.m_dwValue2 = m_cMaterial;
}
break;
}
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCampCmd), CmdCampCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCampCmd), CmdCampCmd);
}
return true;
}
void CCamp::SetRight(CampRight campRight)
{
m_CampRight = campRight;
// 해당 길드의 길드원들에게 전송
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(GetGID());
if (lpGuild)
{
PktCampRight pktCR;
pktCR.m_dwCID = m_dwCID;
pktCR.m_dwCampID = GetCampID();
pktCR.m_CampRight = m_CampRight;
char* szPacket = reinterpret_cast<char *>(&pktCR);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampRight), CmdCampRight, 0, 0))
{
lpGuild->SendAllMember(szPacket, sizeof(PktCampRight), CmdCampRight);
}
}
}
bool CCamp::CheckRight(unsigned char cRightType, unsigned long dwCID, unsigned long dwGID)
{
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(m_dwGID);
if (lpGuild)
{
if (m_CampRight.Check(cRightType, lpGuild->GetTitle(dwCID)))
{
return true;
}
}
return false;
}
void CCamp::SendAttackedMessage()
{
unsigned long dwNowTime = timeGetTime();
if ( dwNowTime - m_dwLastAttackedTick >= Siege::CAMP_ATTACKED_INTERVAL )
{
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
if (IsWorldWeapon())
{
GameClientSendPacket::SendCharCampMessageToDBAgent(lpDBAgentDispatch->GetSendStream(), GetCampID(),
PktCampMessage::MSGCMD_WEAPON_ATTACKED, PktBase::NO_SERVER_ERR);
}
else
{
GameClientSendPacket::SendCharCampMessageToDBAgent(lpDBAgentDispatch->GetSendStream(), GetCampID(),
PktCampMessage::MSGCMD_ATTACKED, PktBase::NO_SERVER_ERR);
}
}
m_dwLastAttackedTick = dwNowTime;
}
}
unsigned long CCamp::GetRepairGold() const
{
int nDiffHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP - m_CreatureStatus.m_nNowHP;
return nDiffHP * Siege::CAMP_REPAIR_GOLD_PER_HP;
}

View File

@@ -0,0 +1,73 @@
#ifndef _CAMP_OBJECT_H_
#define _CAMP_OBJECT_H_
#pragma once
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/SiegeConstants.h>
using namespace Siege;
class CCamp : public CSiegeObject
{
public:
virtual ~CCamp();
// CSkillMonster 의 기능
void NormalBehavior(unsigned long dwTick);
void AttackBehavior(unsigned long dwTick);
void SearchPlayer(void);
bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal);
bool Dead(CAggresiveCreature* pOffencer);
// 길드 요새 관련 함수
virtual bool Build(unsigned char cUpgradeStep = 0); // 길드 요새 구축 완료
bool Cancel(); // 길드 요새 구축 취소 완료
bool Upgrade(unsigned char cUpgradeStep); // 길드 요새 업그레이드 완료
bool Repair(unsigned short wRepairHP); // 길드 요새 수리 완료
virtual bool Destroy(unsigned long dwOffencerGID = 0); // 길드 요새 파괴 완료 (자체 파괴, 파괴됨)
bool Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwNoValue, unsigned char cSubCmd); // 길드 요새 정보 업데이트
bool ChangeType(unsigned short wChangeType); // 길드 요새 타입 변형 완료
void SetRight(CampRight campRight);
bool CheckRight(unsigned char cRightType, unsigned long dwCID, unsigned long dwGID);
// 길드 요새 파괴시 길드 마스터에게 돈을 돌려준다. (로그인해있을때만)
// (로그아웃한 경우는 중계에서 알아서 처리해준다)
void AddGoldToMaster(unsigned long dwGold);
// 스타터킷 아이템으로 전환 (bFullMaterial 이 true 이면 자재의 90%를 돌려준다.)
bool ToStarterKit(bool bFullMaterial = false);
// 패킷 전송 함수
void SendAttackedMessage(); // To DBAgentServer
// Get / Set 함수
const CampRight& GetCampRight() const { return m_CampRight; }
unsigned long GetRepairGold() const;
bool UpdateMaterialNum(unsigned char cMaterial);
protected:
CCamp(MonsterCreateInfo& MonsterCreate, unsigned long dwCampID, unsigned long dwGID,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState, unsigned char cSubState,
unsigned char cUpgradeStep, unsigned char cMaterial, unsigned char cSiegeCount,
const CampRight& campRight, bool bFullHP);
unsigned long m_dwLastAttackedTick; // 마지막으로 공격 받은 시간
CampRight m_CampRight; // 길드 요새 관리 권한
unsigned long m_dwCmdSenderCID; // 길드 요새 명령 패킷을 요청한 캐릭터의 CID (업그레이드와 타입 변형에만 사용)
friend class CSiegeObjectMgr;
};
#endif _CAMP_OBJECT_H_

View File

@@ -0,0 +1,366 @@
#include "stdafx.h"
#include <Utility/Math/Math.h>
#include <Log/ItemLog.h>
#include <Log/LogStruct.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/WrapPacket.h>
#include <Item/Item.h>
#include <Item/ItemFactory.h>
#include <Item/Container/ContainerConstant.h>
#include <Map/FieldMap/CellManager.h>
#include <Castle/Castle.h>
#include <Castle/CastleConstants.h>
#include <Castle/CastleMgr.h>
#include <Castle/CastleBlessMgr.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include "CampShop.h"
CCampShop::CCampShop(MonsterCreateInfo& MonsterCreate, unsigned long dwCampID, unsigned long dwGID,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState,
unsigned char cSubState, unsigned char cUpgradeStep, unsigned char cMaterial,
unsigned char cSiegeCount, const CampRight& campRight, bool bFullHP)
: CCamp(MonsterCreate, dwCampID, dwGID, dwHP, wObjectType, cState, cSubState, cUpgradeStep, cMaterial, cSiegeCount, campRight, bFullHP),
m_dwTempSafe(0), m_cTax(0)
{
m_Container.Initialize(m_dwCID, ContainerConstant::CAMPSHOP_WIDTH, ContainerConstant::CAMPSHOP_HEIGHT);
}
CCampShop::~CCampShop(void)
{
}
void CCampShop::SetTax(unsigned char cTax)
{
m_cTax = cTax;
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cTax;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCampCmd::CAMP_SHOP_CHANGE_TAX;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (true == PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, 0))
{
m_Container.SendAllCustomer(szPacket, sizeof(PktCampCmd), false, CmdCampCmd);
}
}
bool CCampShop::AddGold(unsigned long dwGold)
{
if (m_dwTempSafe <= ULONG_MAX - dwGold)
{
m_dwTempSafe += dwGold;
m_Container.IncreaseUpdateCount();
return true;
}
ERRLOG2(g_Log, "CID:%10u 길드 요새 상점의 임시 금고에 돈 오버플로우가 발생했습니다. : %dGold", m_dwCID, dwGold);
return false;
}
bool CCampShop::DeductGold(unsigned long dwGold)
{
if (dwGold <= m_dwTempSafe)
{
m_dwTempSafe -= dwGold;
m_Container.IncreaseUpdateCount();
return true;
}
ERRLOG2(g_Log, "CID:%10u 길드 요새 상점의 임시 금고에 돈 언더플로우가 발생했습니다. : %dGold", m_dwCID, dwGold);
return false;
}
void CCampShop::DBUpdate(bool bForce)
{
if (true == m_Container.CheckUpdateCount() || true == bForce)
{
const int MAX_BUFFER_SIZE = sizeof(PktCampShopInfo) + CampShopInfoDB::MAX_CONTAINER_SIZE;
char szBuffer[MAX_BUFFER_SIZE] = { 0, };
unsigned long dwBufferSize = CampShopInfoDB::MAX_CONTAINER_SIZE;
unsigned short wTotalSize = sizeof(PktCampShopInfo);
PktCampShopInfo* lpPktCampShopInfo = reinterpret_cast<PktCampShopInfo* >(szBuffer);
char* lpItemBuffer = reinterpret_cast<char* >(lpPktCampShopInfo + 1);
m_Container.SerializeOut(lpItemBuffer, dwBufferSize);
wTotalSize += static_cast<unsigned short>(dwBufferSize);
unsigned long* lpItemPriceBuffer = reinterpret_cast<unsigned long*>(lpItemBuffer + dwBufferSize);
unsigned char cItemNum = 0;
m_Container.StallPriceOut(lpItemPriceBuffer, cItemNum);
wTotalSize += sizeof(unsigned long) * cItemNum;
lpPktCampShopInfo->m_dwCampID = m_dwOwnerID;
lpPktCampShopInfo->m_CampShopInfo.m_dwTempSafe = m_dwTempSafe;
lpPktCampShopInfo->m_CampShopInfo.m_cTax = m_cTax;
lpPktCampShopInfo->m_CampShopInfo.m_dwBufferSize = dwBufferSize;
lpPktCampShopInfo->m_CampShopInfo.m_cItemNum = cItemNum;
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (NULL != lpDBAgentDispatch)
{
CSendStream& SendStream = lpDBAgentDispatch->GetSendStream();
SendStream.WrapCompress(szBuffer, wTotalSize, CmdCampShopInfo, 0, PktBase::NO_SERVER_ERR);
}
}
}
bool CCampShop::SerializeIn(unsigned long dwTempSafe, unsigned char cTax,
char* lpItemBuffer, unsigned long dwBufferSize, unsigned char cItemNum)
{
m_dwTempSafe = dwTempSafe;
m_cTax = cTax;
if (false == m_Container.SerializeIn(lpItemBuffer, dwBufferSize))
{
ERRLOG1(g_Log, "CID:0x%08x 길드 요새 상점의 아이템 목록을 SerializeIn 하는데 실패하였습니다.", m_dwCID);
return false;
}
return m_Container.StallPriceIn(reinterpret_cast<unsigned long*>(lpItemBuffer + dwBufferSize), cItemNum);
}
Item::CItem* CCampShop::SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError)
{
Item::CItem* lpItem = m_Container.GetItem(takeType.m_srcPos);
if (NULL == lpItem) { return NULL; }
unsigned long dwCustomerCID = lpCustomer->GetCID();
unsigned long dwCurrentGold = lpCustomer->GetGold();
unsigned long dwBuyPrice = lpItem->GetBuyPrice();
unsigned short usPrototypeID = lpItem->GetPrototypeID();
dwPrice = dwBuyPrice * takeType.m_cNum;
unsigned long dwTakeGold = dwPrice;
if (dwPrice > dwCurrentGold)
{
ERRLOG2(g_Log, "길드 요새 상점 오류 : 돈이 부족합니다. 가격:%d, 소지금:%d", dwPrice, dwCurrentGold);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
if (false == lpCustomer->GetInventory().TestItem(takeType.m_dstPos, lpItem->GetPrototypeID(), takeType.m_cNum))
{
Item::CItemContainer* lpItemContainer = lpCustomer->GetItemContainer(takeType.m_dstPos.m_cPos);
if (NULL != lpItemContainer)
{
lpItemContainer->DumpItemInfo();
}
else
{
ERRLOG1(g_Log, "CID:%10u 아이템 덤프를 출력할 수 없습니다.", dwCustomerCID);
}
ERRLOG4(g_Log, "CID:%10u 아이템 종류:%d를 (%2d:%2d)에 아이템 넣기 실패.",
dwCustomerCID, lpItem->GetPrototypeID(), takeType.m_dstPos.m_cPos, takeType.m_dstPos.m_cIndex);
return NULL;
}
bool bStackable = lpItem->IsSet(Item::DetailData::STACKABLE);
unsigned char cNumOrDurability = lpItem->GetNumOrDurability();
Item::ItemPos ItemPos = lpItem->GetPos();
// 스택 가능한 아이템인 경우, 개수제한 확인
if (!(bStackable && (cNumOrDurability < takeType.m_cNum)))
{
// 스택이 불가능하거나, 전부 파는 경우에는 아이템 제거
if (!bStackable || (bStackable && (takeType.m_cNum == lpItem->GetNumOrDurability())))
{
if (false == m_Container.RemoveItem(takeType.m_srcPos))
{
ERRLOG3(g_Log, "CID:%10u 아이템을 (%2d, %4x)위치로부터 지우는 데 실패했습니다.",
m_dwCID, takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
else
{
m_Container.SendRemoveItem(takeType, PktStRI::SC_CAMP_SELL, "");
}
}
else
{
lpItem->SetNumOrDurability(cNumOrDurability - takeType.m_cNum);
m_Container.SendRemoveItem(takeType, PktStRI::SC_CAMP_SELL, "");
lpItem = Item::CItemFactory::GetInstance().CreateItem(usPrototypeID);
if (NULL == lpItem)
{
ERRLOG1(g_Log, "길드 요새 상점 오류 : 아이템 생성 실패. ProtoTypeID : %d", usPrototypeID);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
lpItem->SetNumOrDurability(takeType.m_cNum);
}
/*
// CASTLE_TODO : 성이 길드 소유가 아니므로 성의 축복 보너스 기능은 막아둔다.
// edith 세금 부분 추가 (주석처리 뺐음)
// 길드 요새가 성의 축복 영역에 들어있다면, 축복 보너스와 세금 처리를 한다.
Castle::CCastle* lpCastle = Castle::CCastleMgr::GetInstance().GetCastleInBlessArea( GetPosition() );
if (NULL != lpCastle && 0 != lpCastle->GetGID())
{
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild( GetGID() );
if (NULL != lpGuild && false == lpGuild->IsEnemyGuild(lpCastle->GetGID()))
{
unsigned char cBlessBonus = Castle::CCastleBlessMgr::GetInstance().GetBonusPercent(
lpCastle->GetTotalGainTaxCount(), lpCastle->GetUpgradeStep());
// 성의 축복 보너스 만큼 더해준다.
unsigned long dwBlessBonusTax = static_cast<unsigned long>(dwPrice * (cBlessBonus / 100.0f));
dwTakeGold += dwBlessBonusTax;
// 성에 길드 요새 Gold 세금을 낸다.
unsigned long dwCastleTax = static_cast<unsigned long>(dwPrice * (lpCastle->GetTax(Castle::CAMP_GOLD_TAX) / 100.0f));
dwCastleTax = std::min(dwCastleTax, dwTakeGold);
if (dwCastleTax > 0)
{
lpCastle->AddTempTaxMoney(Castle::CAMP_GOLD_TAX, dwCastleTax);
dwTakeGold -= dwCastleTax;
}
}
}
*/
AddGold(dwTakeGold);
DETLOG4(g_Log, "길드 요새 상점 : %d 타입 아이템 판매로 %u 만큼의 돈을 얻었습니다."
" 아이템의 개별 가격은 %u이고 판매 개수는 %d 입니다",
usPrototypeID, dwPrice, dwBuyPrice, takeType.m_cNum);
}
else
{
ERRLOG4(g_Log, "길드 요새 상점 오류 : (%2d, %4x)의 아이템 개수 : %d개 팔려는 아이템 개수 : %d개",
takeType.m_srcPos.m_cPos, takeType.m_srcPos.m_cIndex, cNumOrDurability, takeType.m_cNum);
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::FAIL_ITEM_BUY);
return NULL;
}
GAMELOG::LogTradeItem(*this, dwCustomerCID, dwPrice, lpItem, takeType.m_srcPos, PktTr::TRC_SELL, PktTr::NO_SERVER_ERR);
return lpItem;
}
bool CCampShop::Destroy(unsigned long dwOffencerGID)
{
CCamp::Destroy(dwOffencerGID);
// 입장객을 퇴장시킨다.
GetContainer().Close();
CCell* lpCell = CCellManager::GetInstance().GetCell(0,
static_cast<unsigned long>(GetCurrentPos().m_fPointX),
static_cast<unsigned long>(GetCurrentPos().m_fPointY),
static_cast<unsigned long>(GetCurrentPos().m_fPointZ));
if (NULL == lpCell)
{
ERRLOG4(g_Log, "CampID:0x%08x 아이템을 드랍할 길드 요새 상점의 위치가 이상합니다. X:%.1f, Y:%.1f, Z:%.1f",
GetCampID(), GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointY, GetCurrentPos().m_fPointZ);
return false;
}
// 임시 금고 드랍
if (0 != m_dwTempSafe)
{
CCell::ItemInfo itemInfo;
const Position Pos(GetCurrentPos().m_fPointX + Math::Random::ComplexRandom(40) - 20,
GetCurrentPos().m_fPointY,
GetCurrentPos().m_fPointZ + Math::Random::ComplexRandom(40) - 20);
lpCell->SetItem(Pos, NULL, m_dwTempSafe, dwOffencerGID,
(0 == dwOffencerGID) ? CCell::NONE : CCell::GUILD, itemInfo);
}
GetContainer().DropItem(lpCell, GetCurrentPos(), dwOffencerGID);
return true;
}
unsigned long CCampShop::RepairItem(Item::CEquipment* lpEquipment, unsigned long& dwCurrentGold)
{
if (NULL != lpEquipment)
{
// 길드 요새 상정의 세율 적용 (수리)
unsigned long dwTax = static_cast<unsigned long>(lpEquipment->GetRepairPrice() * (m_cTax / 100.0f));
const unsigned long dwRepairGold = lpEquipment->GetRepairPrice() + dwTax;
if (dwRepairGold <= dwCurrentGold)
{
dwCurrentGold -= dwRepairGold;
/*
// CASTLE_TODO : 성이 길드 소유가 아니므로 성의 축복 보너스 기능은 막아둔다.
// edith 세금 부분 추가 (주석처리 뺐음)
// 길드 요새가 성의 축복 영역에 들어있다면, 축복 보너스와 세금 처리를 한다.
Castle::CCastle* lpCastle = Castle::CCastleMgr::GetInstance().GetCastleInBlessArea( GetPosition() );
if (NULL != lpCastle && 0 != lpCastle->GetGID())
{
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild( GetGID() );
if (NULL != lpGuild && false == lpGuild->IsEnemyGuild(lpCastle->GetGID()))
{
unsigned char cBlessBonus = Castle::CCastleBlessMgr::GetInstance().GetBonusPercent(
lpCastle->GetTotalGainTaxCount(), lpCastle->GetUpgradeStep());
// 성의 축복 보너스 만큼 더해준다.
unsigned long dwBlessBonusTax = static_cast<unsigned long>(dwRepairGold * (cBlessBonus / 100.0f));
dwTax += dwBlessBonusTax;
// 성에 길드 요새 Gold 세금을 낸다.
unsigned long dwCastleTax = static_cast<unsigned long>(dwRepairGold * (lpCastle->GetTax(Castle::CAMP_GOLD_TAX) / 100.0f));
dwCastleTax = std::min(dwCastleTax, dwTax);
if (dwCastleTax > 0)
{
lpCastle->AddTempTaxMoney(Castle::CAMP_GOLD_TAX, dwCastleTax);
dwTax -= dwCastleTax;
}
}
}
//
*/
m_dwTempSafe += dwTax;
lpEquipment->SetNumOrDurability(lpEquipment->GetMaxNumOrDurability());
return dwRepairGold;
}
}
return 0;
}
bool CCampShop::ItemDump(char* pBuffer, int* nBufferSize_InOut) const
{
using namespace GAMELOG;
sItemDump* lpItemDump = reinterpret_cast<sItemDump*>(pBuffer);
char* lpItems = reinterpret_cast<char*>(&lpItemDump[1]);
std::fill_n(lpItemDump->m_usDataSize, int(sItemDump::MAX_DUMP), 0);
unsigned long dwSize = 0;
unsigned short usTotalSize = sizeof(sItemDump);
dwSize = *nBufferSize_InOut;
m_Container.SerializeOut(lpItems, dwSize);
usTotalSize += static_cast<unsigned short>(dwSize);
*nBufferSize_InOut = usTotalSize;
return true;
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include <Creature/Siege/Camp.h>
#include <Item/Container/StallContainer.h>
namespace Item
{
class CEquipment;
}
class CCampShop : public CCamp
{
public:
virtual ~CCampShop(void);
Item::CCampShopContainer& GetContainer(void) { return m_Container; }
unsigned long GetTempSafe(void) { return m_dwTempSafe; }
unsigned char GetTax(void) { return m_cTax; }
void SetTax(unsigned char cTax);
bool AddGold(unsigned long dwGold);
bool DeductGold(unsigned long dwGold);
void DBUpdate(bool bForce);
bool SerializeIn(unsigned long dwTempSafe, unsigned char cTax,
char* lpItemBuffer, unsigned long dwBufferSize, unsigned char cItemNum);
Item::CItem* SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError);
bool Destroy(unsigned long dwOffencerGID = 0);
virtual unsigned long RepairItem(Item::CEquipment* lpEquipment, unsigned long& dwCurrentGold);
virtual bool ItemDump(char* pBuffer, int* nBufferSize_InOut) const;
private:
CCampShop(MonsterCreateInfo& MonsterCreate, unsigned long dwCampID, unsigned long dwGID,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState, unsigned char cSubState,
unsigned char cUpgradeStep, unsigned char cMaterial, unsigned char cSiegeCount,
const CampRight& campRight, bool bFullHP);
Item::CCampShopContainer m_Container; // 판매 물품용 컨테이너
unsigned long m_dwTempSafe; // 임시 금고
unsigned char m_cTax; // 세율
friend class CSiegeObjectMgr;
};

View File

@@ -0,0 +1,859 @@
#include "stdafx.h"
#include "CastleArms.h"
#include <Skill/SkillTable.h>
#include <Skill/SkillMgr.h>
#include <Utility/Math/Math.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/ClientSocket/ClientConstants.h>
CCastleArms::CCastleArms(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject)
: CSiegeObject(MonsterCreate, CastleObject)
{
}
CCastleArms::~CCastleArms()
{
}
bool CCastleArms::AttackCID(CCharacter* lpRideChar, AtType attackType, AtNode& attackNode, unsigned short& wError)
{
PERFORMANCE_CHECK(FunctionTimingCheck);
if (Siege::CASTLE_ARMS_NPC == m_wObjectType)
{
wError = PktAtAck::SERVER_ERROR;
return true;
}
if (NULL == lpRideChar || m_CreatureStatus.m_nNowHP == 0)
{
wError = PktAtAck::FAIL_ALREADY_DEAD;
return true;
}
if (0 == (attackType.m_wType & AtType::SKILL_BIT))
{
wError = PktAtAck::FAIL_NOT_SIEGE_ATTACK;
return false;
}
const Skill::ProtoType* pThisSkill = CSkillMgr::GetInstance().GetSkillProtoType(attackType.m_wType);
if (NULL == pThisSkill)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x", m_dwCID, attackType.m_wType);
return false;
}
const unsigned short wLockCount = GetSkillLockCount(attackType.m_wType);
if (wLockCount < 0 || wLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT)
{
ERRLOG3(g_Log, "CID:0x%08x 쓰려는 스킬의 락카운트가 이상합니다. SkillType : 0x%04x, LockCount : %d",
m_dwCID, attackType.m_wType, wLockCount);
return false;
}
unsigned char cDefenderNum = attackNode.m_wDefenserNum > AtNode::MAX_DEFENDER_NUM ?
AtNode::MAX_DEFENDER_NUM : attackNode.m_wDefenserNum; // 최대 방어자 수 제한
CAggresiveCreature* lpAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
unsigned short wDefenserMPHeal[AtNode::MAX_DEFENDER_NUM] = {0, };
char cTargetType = Skill::Target::ENEMY;
if (0 == cDefenderNum)
{
if (0 != attackType.m_cMissileAttack)
{
return MissileAttack(attackType, 0, pThisSkill[attackType.m_cSkillLockCount].m_fEffectExtent,
Math::Const::PI * 2, cTargetType);
}
else
{
// 캐스팅에 실패한 경우
return Attack(attackType, cDefenderNum, lpAggresiveCreature, attackNode.m_cDefenserJudge, wDefenserMPHeal);
}
}
else
{
if (0 == pThisSkill[wLockCount].m_fMaxRange && 0 == pThisSkill[attackType.m_cSkillLockCount].m_fEffectExtent)
{
if (m_dwCID != attackNode.m_dwDefenser[0])
{
ERRLOG2(g_Log, "CID:0x%08x 자기자신에게만 쓸 수 있는 스킬입니다. SkillID:0x%04x",
m_dwCID, attackType.m_wType);
return false;
}
}
// 공성병기는 몬스터를 공격할수 없다.
Creature::CreatureType eCreatureType = Creature::GetCreatureType(attackNode.m_dwDefenser[0]);
if (Creature::CT_MONSTER == eCreatureType || Creature::CT_STRUCT == eCreatureType)
{
wError = PktAtAck::FAIL_TO_MONSTER;
return false;
}
// 스킬 거리 체크
CAggresiveCreature* lpTargetCreature = NULL;
// Target Creature 얻어오기
if (0 != GetMapIndex())
{
lpTargetCreature = CCreatureManager::GetInstance().GetAggresiveCreature(attackNode.m_dwDefenser[0]);
if (lpTargetCreature && lpTargetCreature->GetMapIndex() != GetMapIndex()) lpTargetCreature = NULL;
}
else
{
lpTargetCreature = CCreatureManager::GetInstance().GetAggresiveCreature(attackNode.m_dwDefenser[0]);
}
// Target Creature 처리하기
if (NULL != lpTargetCreature)
{
float fSquareTargetDistance = (m_CurrentPos.m_fPointX - lpTargetCreature->GetCurrentPos().m_fPointX) *
(m_CurrentPos.m_fPointX - lpTargetCreature->GetCurrentPos().m_fPointX) +
(m_CurrentPos.m_fPointZ - lpTargetCreature->GetCurrentPos().m_fPointZ) *
(m_CurrentPos.m_fPointZ - lpTargetCreature->GetCurrentPos().m_fPointZ);
float fSquareEffectDistance = (pThisSkill[wLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE) *
(pThisSkill[wLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE);
if (fSquareTargetDistance > fSquareEffectDistance)
{
wError = PktAtAck::FAIL_TOO_FAR;
return false;
}
}
if (pThisSkill[attackType.m_cSkillLockCount].m_eTargetType == Skill::Target::FRIEND ||
pThisSkill[attackType.m_cSkillLockCount].m_eTargetType == Skill::Target::DEAD_FRIEND ||
pThisSkill[attackType.m_cSkillLockCount].m_eTargetType == Skill::Target::FRIEND_EXCEPT_SELF ||
pThisSkill[attackType.m_cSkillLockCount].m_eTargetType == Skill::Target::FRIEND_OBJECT ||
pThisSkill[attackType.m_cSkillLockCount].m_eTargetType == Skill::Target::PARTY ||
pThisSkill[attackType.m_cSkillLockCount].m_eTargetType == Skill::Target::SUMMON)
{
//cTargetType = Skill::Target::FRIEND;
wError = PktAtAck::FAIL_FRIENDLY_ATTACK;
return false;
}
// 클라이언트가 넘겨준 타겟들을 체크한다. (범위 마법에 걸리는 타겟은 따로 체크)
for (unsigned char cDefender = 0; cDefender < cDefenderNum; ++cDefender)
{
// Target Creature 얻기
CAggresiveCreature* lpCreature = NULL;
Creature::CreatureType eCreatureType = Creature::GetCreatureType(attackNode.m_dwDefenser[cDefender]);
if (Creature::CT_MONSTER == eCreatureType || Creature::CT_STRUCT == eCreatureType)
{
continue;
}
lpCreature = CCreatureManager::GetInstance().GetAggresiveCreature(attackNode.m_dwDefenser[cDefender]);
if (lpCreature && lpCreature->GetMapIndex() != GetMapIndex())
{
lpCreature = NULL;
}
if (NULL != lpCreature)
{
// 긍정적인 공격(-_-)
if (Skill::Target::FRIEND == cTargetType)
{
wError = PktAtAck::FAIL_FRIENDLY_ATTACK;
return false;
}
// 부정적인 공격(진짜 공격)
else
{
// 자기를 중심으로 하는 범위형 스킬의 경우 타겟을 자신으로 세팅합니다.
// (이 경우 this는 MultiAttack() 함수가 타켓에서 제외시켜 줍니다.)
if (EnemyCheck::EC_ENEMY == IsEnemy(lpCreature) || lpCreature == this)
{
lpAggresiveCreature[cDefender] = lpCreature;
}
}
}
}
if (0 == cDefenderNum || NULL == lpAggresiveCreature[0])
{
return Attack(attackType, cDefenderNum, lpAggresiveCreature, attackNode.m_cDefenserJudge, wDefenserMPHeal);
}
// 범위 마법 체크
if (0 != pThisSkill[attackType.m_cSkillLockCount].m_fEffectExtent)
{
if (Skill::Target::PARTY == pThisSkill[attackType.m_cSkillLockCount].m_eTargetType)
{
wError = PktAtAck::FAIL_FRIENDLY_ATTACK;
return false;
}
else
{
return CAggresiveCreature::MultiAttack(attackType, cDefenderNum, lpAggresiveCreature, attackNode.m_cDefenserJudge,
lpAggresiveCreature[0]->GetCurrentPos(), 0, pThisSkill[attackType.m_cSkillLockCount].m_fEffectExtent,
Math::Const::PI * 2, cTargetType);
}
}
}
return Attack(attackType, cDefenderNum, lpAggresiveCreature, attackNode.m_cDefenserJudge, wDefenserMPHeal);
}
bool CCastleArms::Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal)
{
if (Siege::CASTLE_ARMS_NPC == m_wObjectType)
{
return false;
}
if (m_CreatureStatus.m_nNowHP == 0)
{
ERRLOG1(g_Log, "CID:0x%08x 죽은 공성 오브젝트가 공격하려고 하였습니다.", m_dwCID);
return false;
}
if (cDefenderNum > AtNode::MAX_DEFENDER_NUM)
{
ERRLOG2(g_Log, "CID:0x%08x 공성 오브젝트가 공격할 때, 방어자의 숫자가 최대 방어자 숫자를 넘었습니다. 방어자수 : %d",
m_dwCID, cDefenderNum);
cDefenderNum = AtNode::MAX_DEFENDER_NUM;
}
if (0 == (attackType.m_wType & AtType::SKILL_BIT) && 0 == cDefenderNum)
{
ERRLOG0(g_Log, "스킬이 아닌 일반 공격은, 반드시 타겟이 있을 경우에만 서버로 보내야 합니다.");
return false;
}
if (0 != (attackType.m_wType & AtType::SKILL_BIT))
{
const Skill::ProtoType* pSkillProtoType = CSkillMgr::GetInstance().GetSkillProtoType(attackType.m_wType);
if (NULL == pSkillProtoType)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x", m_dwCID, attackType.m_wType);
return false;
}
}
if ( IsRidable() )
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if (!lpRider)
{
ERRLOG2(g_Log, "CID:0x%08x 공성 오브젝트에 타고있는 캐릭터가 없습니다. RiderCID : 0x%08x", m_dwCID, m_dwRiderCID);
return false;
}
// 공격시 무적 상태가 풀린다.
lpRider->GetSpellMgr().GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::Invincible);
}
unsigned char cOffencerJudge = 0;
unsigned short wOffencerMPHeal = 0;
unsigned short wError = PktAtAck::NO_SERVER_ERR;
const int MAX_BUFFER = sizeof(PktAtAck) + AtNode::MAX_DEFENDER_NUM * sizeof(DefenserNode);
char szBuffer[MAX_BUFFER];
PktAtAck* lpPktAtAck = reinterpret_cast<PktAtAck*>(szBuffer);
DefenserNode* lpDefenserNode = reinterpret_cast<DefenserNode*>(lpPktAtAck + 1);
m_cConsumeMPCount = std::min(cDefenderNum, unsigned char(AtNode::MAX_MONSTER_DEFENDER_NUM));
unsigned char cDefender = 0;
unsigned char cIndex = 0;
for (; cIndex < cDefenderNum; ++cIndex)
{
// MP 소모 타이밍까지의 카운트 (범위 마법은 한 번만 MP 소모)
--m_cConsumeMPCount;
if (NULL == ppDefenders[cIndex])
{
continue;
}
if (0 == ppDefenders[cIndex]->GetStatus().m_nNowHP)
{
continue;
}
// 최대 방어자 수 제한 (몬스터는 캐릭터와는 별도 처리)
Creature::CreatureType eCreatureType = Creature::GetCreatureType(ppDefenders[cIndex]->GetCID());
if (Creature::CT_MONSTER == eCreatureType || Creature::CT_STRUCT == eCreatureType)
{
continue;
}
// TODO : 공격 방향을 설정해줍시다.
cDefenserJudges[cDefender] = ClientConstants::Judge_Front;
wDefenserMPHeal[cDefender] = 0;
const unsigned short nPrevHP = ppDefenders[cIndex]->GetStatus().m_nNowHP;
const unsigned short nPrevMP = ppDefenders[cIndex]->GetStatus().m_nNowMP;
const unsigned short wPrevAttackerHP = m_CreatureStatus.m_nNowHP;
// 대미지 반영
lpDefenserNode[cDefender].m_wDamage = ppDefenders[cIndex]->ApplyDamage(attackType, this, cOffencerJudge,
cDefenserJudges[cDefender], wOffencerMPHeal, wDefenserMPHeal[cDefender], wError);
const unsigned short nNowHP = ppDefenders[cIndex]->GetStatus().m_nNowHP;
const unsigned short nNowMP = ppDefenders[cIndex]->GetStatus().m_nNowMP;
// 스킬에 의한 자살 방지
if (0 == m_CreatureStatus.m_nNowHP)
{
m_CreatureStatus.m_nNowHP = wPrevAttackerHP;
wError = PktAtAck::FAIL_SUICIDE;
break;
}
else
{
if (Creature::CT_PC == Creature::GetCreatureType(ppDefenders[cIndex]->GetCID()))
{
CCharacter* lpDefendCharacter = (CCharacter *)ppDefenders[cIndex];
CMonster* lpSummonee = lpDefendCharacter->GetSummonee();
if (NULL != lpSummonee)
{
lpSummonee->GuardMe(this, lpDefenserNode[cDefender].m_wDamage);
}
lpDefendCharacter->CalculateEquipDurability((ClientConstants::Judge_Guard == cDefenserJudges[cDefender]) ?
AtType::GUARD : AtType::DEFENCE);
CGameClientDispatch* lpDispatch = lpDefendCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
GameClientSendPacket::SendCharAttacked(lpDispatch->GetSendStream(), this, lpDefendCharacter,
attackType, m_MotionInfo.m_fDirection, lpDefenserNode[cDefender].m_wDamage,
cDefenserJudges[cDefender], wDefenserMPHeal[cDefender], PktBase::NO_SERVER_ERR);
}
}
// 공격 패킷 만들기
lpDefenserNode[cDefender].m_dwCharID = ppDefenders[cIndex]->GetCID();
lpDefenserNode[cDefender].m_sCurrHP = nNowHP;
lpDefenserNode[cDefender].m_sCurrMP = nNowMP;
lpDefenserNode[cDefender].m_wMaxHP = ppDefenders[cIndex]->GetStatus().m_StatusInfo.m_nMaxHP;
lpDefenserNode[cDefender].m_wMaxMP = ppDefenders[cIndex]->GetStatus().m_StatusInfo.m_nMaxMP;
lpDefenserNode[cDefender].m_wMPHeal = wDefenserMPHeal[cDefender];
lpDefenserNode[cDefender].m_cJudge = cDefenserJudges[cDefender];
}
++cDefender;
}
if (0 != (attackType.m_wType & AtType::SKILL_BIT))
{
if (0 == cDefender)
{
Skill::CFunctions::ConsumeMP(attackType, this, 0);
}
}
lpPktAtAck->m_dwCharID = m_dwCID;
lpPktAtAck->m_AtType = attackType;
lpPktAtAck->m_wHP = m_CreatureStatus.m_nNowHP;
lpPktAtAck->m_wMP = m_CreatureStatus.m_nNowMP;
lpPktAtAck->m_wMPHeal = wOffencerMPHeal;
lpPktAtAck->m_cJudge = cOffencerJudge;
lpPktAtAck->m_cDefenserNum = cDefender;
if ( IsRidable() )
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if (lpRider && lpRider->GetDispatcher())
{
CSendStream& SendStream = (lpRider->GetDispatcher())->GetSendStream();
if (true == SendStream.WrapCompress(
szBuffer, sizeof(PktAtAck) + cDefender * sizeof(DefenserNode), CmdCharAttack, 0, wError) &&
PktBase::NO_SERVER_ERR == wError)
{
CCell* lpCell = GetCellPos().m_lpCell;
if (lpCell)
{
lpCell->SendAttackInfo(m_dwCID, attackType, cDefender, lpDefenserNode);
return true;
}
}
}
}
else
{
CCell* lpCell = GetCellPos().m_lpCell;
if (lpCell)
{
lpCell->SendAttackInfo(m_dwCID, attackType, cDefender, lpDefenserNode);
return true;
}
}
return false;
}
bool CCastleArms::MissileAttack(AtType attackType, float fDir, float nRange, float fAngle, char cTargetType)
{
if (Siege::LONG_RANGE_CASTLE_ARMS != m_wObjectType)
{
return false;
}
CCell* lpCell = NULL;
// 듀얼 상태라면 듀얼 셀로 처리
if(CServerSetup::GetInstance().GetDuelModeCheck() && NULL != GetDuelOpponent())
{
lpCell = CDuelCellManager::GetInstance().GetCell(GetCID());
}
else
{
lpCell = CCellManager::GetInstance().GetCell(m_CellPos.m_wMapIndex,
static_cast<unsigned long>(attackType.m_DstPos.fPointX),
static_cast<unsigned long>(attackType.m_DstPos.fPointY),
static_cast<unsigned long>(attackType.m_DstPos.fPointZ));
}
if (NULL == lpCell)
{
ERRLOG0(g_Log, "CID:0x%08x 공격 목표 지점의 셀이 존재하지 않습니다.");
return false;
}
CAggresiveCreature* ppDefenders[AtNode::MAX_DEFENDER_NUM] = {0, };
unsigned char cDefenserJudges[AtNode::MAX_DEFENDER_NUM] = {0, };
unsigned short wDefenserMPHeal[AtNode::MAX_DEFENDER_NUM] = {0, };
int nDefenderNum = 0;
for (int nDirection = 0; nDirection < CCell::CONNECT_NUM && nDefenderNum < AtNode::MAX_DEFENDER_NUM; ++nDirection)
{
CCell* lpConnectCell = lpCell->GetConnectCell(nDirection);
if (NULL == lpConnectCell)
{
continue;
}
CAggresiveCreature* lpTempTarget = lpConnectCell->GetFirstAggresiveCreature();
// 타겟이 없거나, 방어자 수가 최대가 되면, 루프를 빠져나갑니다.
while (NULL != lpTempTarget && nDefenderNum < AtNode::MAX_DEFENDER_NUM)
{
// 공격에 대한 예외상황
bool bException = false;
// 병기에 탑승한 캐릭터는 공격하지 않는다. 대신 병기를 공격한다.
if (Creature::CT_PC == Creature::GetCreatureType(lpTempTarget->GetCID()))
{
CCharacter* lpRideChar = reinterpret_cast<CCharacter*>(lpTempTarget);
if (true == lpRideChar->IsRideArms())
{
bException = true;
}
}
EnemyCheck::EnemyType eTargetType = IsEnemy(lpTempTarget);
if ((EnemyCheck::EC_NEUTRAL == eTargetType) ||
(Skill::Target::FRIEND == cTargetType && EnemyCheck::EC_ENEMY == eTargetType) ||
(Skill::Target::ENEMY == cTargetType && EnemyCheck::EC_FRIEND == eTargetType))
{
bException = true;
}
// 겹치는 게 있으면 처리하지 않는다.
for (int nIndex = 0; nIndex < nDefenderNum; nIndex++)
{
if (ppDefenders[nIndex] == lpTempTarget)
{
bException = true;
break;
}
}
if (false == bException)
{
const float fDX = lpTempTarget->GetCurrentPos().m_fPointX - attackType.m_DstPos.fPointX;
const float fDZ = lpTempTarget->GetCurrentPos().m_fPointZ - attackType.m_DstPos.fPointZ;
const float fDistance = (fDX * fDX) + (fDZ * fDZ);
const float fSquareAttackRange = nRange * nRange;
if (fDistance <= fSquareAttackRange)
{
const float fTempfDir = CalcDir2D(attackType.m_DstPos.fPointX, attackType.m_DstPos.fPointZ,
lpTempTarget->GetCurrentPos().m_fPointX, lpTempTarget->GetCurrentPos().m_fPointZ);
const float fDifference = (fTempfDir >= fDir) ? (fTempfDir-fDir) : (fDir-fTempfDir);
if (fDifference <= fAngle && 0 < lpTempTarget->GetStatus().m_nNowHP)
{
ppDefenders[nDefenderNum] = lpTempTarget;
cDefenserJudges[nDefenderNum] = ClientConstants::Judge_Front;
wDefenserMPHeal[nDefenderNum] = 0;
++nDefenderNum;
}
}
}
lpTempTarget = lpConnectCell->GetNextAggresiveCreature();
}
}
if (AtNode::MAX_DEFENDER_NUM < nDefenderNum)
{
SERLOG0(g_Log, "스택 오버런 : 방어자수가 최대치를 넘어셨습니다.");
}
return Attack(attackType, nDefenderNum, ppDefenders, cDefenserJudges, wDefenserMPHeal);
}
bool CCastleArms::Dead(CAggresiveCreature* pOffencer)
{
if (Siege::CASTLE_ARMS_NPC == m_wObjectType)
{
return true;
}
if (NULL == pOffencer) return false;
if (STATE_ID_DIE == m_nCurrentState) return false;
//m_wObjectType = Siege::CASTLE_ARMS_NPC;
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_bCasting = false;
// 타고 있던 캐릭터는 죽는다.
if (0 != m_dwRiderCID)
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if (lpRider)
{
//lpRider->GetOff();
lpRider->Kill(pOffencer);
}
//m_dwRiderCID = 0;
m_dwOwnerID = 0;
}
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
return GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), pOffencer->GetCID(),
GetCastleID(), m_dwCID, PktCastleCmd::DESTROY, 0,
PktCastleCmd::CASTLE_DESTROY_ARMS, PktBase::NO_SERVER_ERR);
}
return false;
}
bool CCastleArms::Upgrade(unsigned char cUpgradeStep)
{
m_cState = Siege::COMPLETE;
m_cUpgradeStep = cUpgradeStep;
UpdateObjectInfo(Siege::UPGRADE_HP);
// 수성 병기가 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cUpgradeStep; // 업그레이드 단계
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_UPGRADE_ARMS_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleArms::Repair(unsigned short wRepairHP)
{
m_cState = Siege::COMPLETE;
UpdateObjectInfo(Siege::REPAIR_HP, wRepairHP);
// 수성 병기가 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = wRepairHP;
pktCC.m_dwValue2 = m_CreatureStatus.m_nNowHP; // 현재 HP
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_REPAIR_ARMS_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleArms::Destroy(unsigned char cOffencerNation, bool bTakeGold)
{
if (!IsCastleArms() || Siege::CASTLE_ARMS_NPC == m_wObjectType)
{
m_dwOwnerID = 0;
return false;
}
// 생산 비용의 절반을 돌려준다.
if (true == bTakeGold)
{
CCharacter* lpOwner = CCreatureManager::GetInstance().GetCharacter(m_dwOwnerID);
if (0 != lpOwner)
{
lpOwner->AddGold(m_MonsterInfo.m_dwDevelopGold / 2, true);
}
}
m_wObjectType = Siege::CASTLE_ARMS_NPC;
m_cState = Siege::COMPLETE;
m_cUpgradeStep = 0;
m_dwOwnerID = 0;
UpdateObjectInfo();
m_CreatureStatus.m_nNowHP = 0;
// 수성 병기가 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCID = 0;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_wObjectType; // 병기 관리 NPC Type ID
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_DESTROY_ARMS;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleArms::Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwOnwerID, unsigned char cSubCmd)
{
if (!IsCastleArms())
{
return false;
}
m_cState = cState;
if (cSubCmd == PktCastleCmd::CASTLE_CREATE_ARMS)
{
dwValue2 = (dwValue2 & 0x0000FFFF);
m_wObjectType = static_cast<unsigned short>(dwValue2);
m_dwOwnerID = dwOnwerID;
// 개인 금고에서 dwValue1 만큼의 돈을 깍는다.
CCharacter* lpOwner = CCreatureManager::GetInstance().GetCharacter(m_dwOwnerID);
if (0 != lpOwner)
{
lpOwner->DeductGold(dwValue1, true);
}
UpdateObjectInfo(Siege::FULL_HP);
}
else if (cSubCmd == PktCastleCmd::CASTLE_REPAIR_ARMS)
{
// 개인 금고에서 dwValue1 만큼의 돈을 깍는다.
CCharacter* lpOwner = CCreatureManager::GetInstance().GetCharacter(m_dwOwnerID);
if (0 != lpOwner)
{
lpOwner->DeductGold(dwValue1, true);
}
UpdateObjectInfo();
}
else
{
UpdateObjectInfo();
}
// 수성 병기가 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCID = m_dwOwnerID;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = dwValue1;
pktCC.m_dwValue2 = dwValue2;
pktCC.m_cSubCmd = cSubCmd;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleArms::ChangeType(unsigned short wType)
{
m_wObjectType = wType;
m_cState = Siege::COMPLETE;
m_cUpgradeStep = 0;
UpdateObjectInfo();
// 수성 병기가 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = wType; // 병기 타입
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_CREATE_ARMS_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleArms::Ride(unsigned long dwCID)
{
if (0 == m_dwRiderCID)
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (lpRider && dwCID == m_dwOwnerID)
{
m_dwRiderCID = dwCID;
lpRider->Ride(m_dwCID);
lpRider->SkillClear();
// 중계 서버에 보내줘야 사용하고 있음을 체크할 수 있다.
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (NULL == lpDBAgentDispatch)
{
ERRLOG0(g_Log, "에이전트 얻기 실패.");
return false;
}
else
{
GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(),
dwCID, m_dwCampOrCastleID, m_dwCID, 0, 0, PktCastleCmd::CASTLE_RIDE_ARMS, PktBase::NO_SERVER_ERR);
}
// Ride 함수 자체에서 패킷을 보내준다.
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = dwCID; // 탑승자 CID
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_RIDE_ARMS;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
}
return false;
}
bool CCastleArms::GetOff(unsigned long dwCID)
{
if (Siege::NOT_RIDER != IsRider(dwCID))
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(dwCID);
if (lpRider)
{
m_dwRiderCID = 0;
lpRider->GetOff();
// 중계 서버에 보내줘야 사용하고 있지 않음을 체크할 수 있다.
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (NULL == lpDBAgentDispatch)
{
ERRLOG0(g_Log, "에이전트 얻기 실패.");
return false;
}
else
{
GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(),
dwCID, m_dwCampOrCastleID, m_dwCID, 0, 0, PktCastleCmd::CASTLE_GETOFF_ARMS, PktBase::NO_SERVER_ERR);
}
// GetOff 함수 자체에서 패킷을 보내준다.
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = dwCID; // 내린 사람 CID
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_GETOFF_ARMS;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
}
return false;
}

View File

@@ -0,0 +1,42 @@
#ifndef _BASE_CASTLE_ARMS_OBJECT_H_
#define _BASE_CASTLE_ARMS_OBJECT_H_
#pragma once
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/SiegeConstants.h>
using namespace Siege;
class CCastleArms : public CSiegeObject
{
public:
virtual ~CCastleArms();
// CSkillMonster 의 기능
virtual bool AttackCID(CCharacter* lpRideChar, AtType attackType, AtNode& attackNode, unsigned short& wError);
virtual bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal);
virtual bool MissileAttack(AtType attackType, float fDir, float nRange, float fAngle, char cTargetType);
virtual bool Dead(CAggresiveCreature* pOffencer);
// 수성 병기 관련 함수
bool Upgrade(unsigned char cUpgradeStep);
bool Repair(unsigned short wRepairHP);
bool Destroy(unsigned char cOffencerNation = 0, bool bTakeGold = false );
bool Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwOwnerID, unsigned char cSubCmd);
bool ChangeType(unsigned short wType);
// Rider 관련 정보
virtual bool Ride(unsigned long dwCID); // 병기 탑승
virtual bool GetOff(unsigned long dwCID); // 병기 내림
protected:
CCastleArms(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject);
friend class CSiegeObjectMgr;
};
#endif _BASE_CASTLE_ARMS_OBJECT_H_

View File

@@ -0,0 +1,368 @@
#include "stdafx.h"
#include "CastleEmblem.h"
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
#include <Skill/SkillTable.h>
#include <Skill/SkillMgr.h>
#include <Utility/Math/Math.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/ClientSocket/ClientConstants.h>
#include <GameTime/GameTimeConstants.h>
#include <GameTime/GameTimeMgr.h>
CCastleEmblem::CCastleEmblem(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject)
: CSiegeObject(MonsterCreate, CastleObject), m_cEnemyNation(Creature::STATELESS), m_dwLastAttackedTick( 0 )
{
}
CCastleEmblem::~CCastleEmblem()
{
}
void CCastleEmblem::NormalBehavior(unsigned long dwTick)
{
if (!CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
return;
}
// 선공 몹 처리
if (NULL == m_lpTarget && true == m_MonsterInfo.m_bFirstAttack)
{
SearchPlayer();
}
}
void CCastleEmblem::AttackBehavior(unsigned long dwTick)
{
PERFORMANCE_CHECK(FunctionTimingCheck)
if (!CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
CancelTarget();
return;
}
// 상징물이 소환 완료 상태가 아닐 때는 아무런 다른 행동을 해서는 안된다.
if (m_cState != Siege::COMPLETE)
{
return;
}
// 마법 캐스팅 중일때는.. 아무런 다른 행동을 해서는 안된다.
if (true == m_bCasting)
{
CastingAttackAction();
return;
}
m_lpTarget = m_Threat.GetTarget();
if (NULL == m_lpTarget ||
(m_lpTarget && true == m_lpTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Hide)))
{
CancelTarget();
return;
}
const float fDY = fabs(m_lpTarget->GetCurrentPos().m_fPointY - GetCurrentPos().m_fPointY);
const float fDX = m_lpTarget->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX;
const float fDZ = m_lpTarget->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ;
const float fDistance = sqrtf((fDX * fDX) + (fDZ * fDZ));
if (fDY > Siege::EMBLEM_ATTACK_HEIGHT_ERROR)
{
CancelTarget();
return;
}
if (((fDistance > m_wSearchRange) && false == m_bLongRangeAttacked) || (0 == m_lpTarget->GetStatus().m_nNowHP))
{
CancelTarget();
return;
}
const float fAttackRange = m_MonsterInfo.m_wAttackRange / 100.0f;
if (fDistance > fAttackRange && 0 >= m_lCurrentFrame)
{
// 공격 범위 밖이다.
if (true == SkillAttack()) // 스킬 공격을 사용하는가 체크
{
SkillAttackAction();
}
if (false == m_bAttacking)
{
m_lCurrentFrame = m_MotionInfo.m_dwFrame;
}
}
else
{
// 공격 범위 안이다.
if (false == m_bAttacking)
{
if (true == SkillAttack()) // 스킬 공격을 사용하는가 체크
{
SkillAttackAction();
}
}
}
}
void CCastleEmblem::SearchPlayer(void)
{
// TODO : 해상도 조절을 통해 float 계산을 없애보자.
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG1(g_Log, "CID:0X%08 공성 오브젝트가 셀 범위 밖에 있습니다.", m_dwCID);
return;
}
CCell* pCell = NULL;
CCharacter* pTempTarget = NULL;
CCharacter* pCurrentTarget = NULL;
// const float fSquareSearchRange = (float)(m_wSearchRange * m_wSearchRange);
const float fSquareSearchRange = (float)(32 * 32);
for (int nCellCount = 0; nCellCount < CCell::CONNECT_NUM; ++nCellCount)
{
pCell = m_CellPos.m_lpCell->GetConnectCell(nCellCount);
if (NULL == pCell || false == pCell->IsCharacter())
{
continue;
}
pTempTarget = pCell->GetFirstCharacter();
while (NULL != pTempTarget)
{
const float fDistY = fabs(pTempTarget->GetCurrentPos().m_fPointY - GetCurrentPos().m_fPointY);
if (fDistY <= Siege::EMBLEM_ATTACK_HEIGHT_ERROR &&
pTempTarget->GetStatus().m_nNowHP > 0 &&
EnemyCheck::EC_ENEMY == IsEnemy(pTempTarget))
{
if (false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Stealth) &&
false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Invincible) &&
false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Hide))
{
const float fDX = pTempTarget->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX;
const float fDZ = pTempTarget->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ;
const float fDistance = (fDX * fDX) + (fDZ * fDZ);
if (fDistance < fSquareSearchRange)
{
pCurrentTarget = pTempTarget;
break;
}
}
}
pTempTarget = pCell->GetNextCharacter();
}
}
if (NULL != pCurrentTarget)
{
m_Threat.AddToThreatList(pCurrentTarget, 1);
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_SEEN_PLAYER);
}
}
bool CCastleEmblem::Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal)
{
return (m_cState == Siege::COMPLETE) ? CSkillMonster::Attack(attackType, cDefenderNum, ppDefenders, cDefenserJudges, wDefenserMPHeal) : false;
}
bool CCastleEmblem::Dead(CAggresiveCreature* pOffencer)
{
if (NULL == pOffencer) return false;
if (STATE_ID_DIE == m_nCurrentState) return false;
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_bCasting = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP);
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
m_cEnemyNation = reinterpret_cast<CCharacter*>(pOffencer)->GetNation();
return GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), pOffencer->GetCID(),
GetCastleID(), m_dwCID, m_cEnemyNation, 0,
PktCastleCmd::CASTLE_UPDATE_EMBLEM, PktBase::NO_SERVER_ERR);
}
return false;
}
bool CCastleEmblem::Upgrade(unsigned char cUpgradeStep)
{
// 성 오브젝트의 업그레이드 효과를 없애준다.
Castle::CCastle* lpCastle = Castle::CCastleMgr::GetInstance().GetCastle(GetCastleID());
if (lpCastle)
{
lpCastle->DegradeByEmblem();
}
// 업그레이드 처리
m_cState = Siege::COMPLETE;
m_cUpgradeStep = cUpgradeStep;
UpdateObjectInfo(Siege::UPGRADE_HP);
// 업그레이트 타입에 따른 처리를 해야한다.
if (lpCastle)
{
lpCastle->UpgradeByEmblem();
}
// 모든 캐릭터에게 전송한다.!!
// 성 상징물 업그레이드 단계는 성의 정보에 필요하므로 모든 캐릭터에게 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cUpgradeStep; // 업그레이드 단계
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_UPGRADE_EMBLEM_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
return true;
}
// 상징물 업데이트 (소환중, 소환완료, 업그레이드중)
bool CCastleEmblem::Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwNoValue, unsigned char cSubCmd)
{
if ( !IsEmblem() ) return false;
m_cState = cState;
PktCastleCmd pktCC;
if (PktCastleCmd::CASTLE_UPGRADE_EMBLEM == cSubCmd)
{
m_cUpgradeType = static_cast<unsigned char>(dwValue1);
UpdateObjectInfo();
}
else if (PktCastleCmd::CASTLE_UPDATE_EMBLEM == cSubCmd)
{
// 업그레이드 효과 없애주기
Castle::CCastle* lpCastle = Castle::CCastleMgr::GetInstance().GetCastle(GetCastleID());
if (lpCastle)
{
lpCastle->DegradeByEmblem();
}
switch (dwValue1)
{
case Siege::MINE:
{
m_cSubState = Siege::MINE;
std::swap(m_cNation, m_cEnemyNation);
m_cEnemyNation = Creature::STATELESS;
dwValue1 = m_cNation;
}
break;
case Siege::ENEMY:
{
m_cSubState = Siege::ENEMY;
std::swap(m_cNation, m_cEnemyNation);
dwValue1 = m_cNation;
}
break;
}
m_cUpgradeType = Siege::NO_JEWEL;
m_cUpgradeStep = 0;
UpdateObjectInfo(Siege::FULL_HP);
// 30초간 무적
Skill::CAddSpell<CInvincibleSpell>(
CSpell::Spell_Info(Skill::CProcessTable::ProcessInfo::m_NullProtoType, this,
Skill::SpellType::MAGICAL_SPELL, Skill::SpellID::Invincible, 1, 30))(this);
}
else if (PktCastleCmd::CASTLE_SUMMON_EMBLEM_COMPLETE == cSubCmd)
{
m_cSubState = Siege::MINE;
m_cEnemyNation = Creature::STATELESS;
UpdateObjectInfo(Siege::FULL_HP);
}
// 메세지 처리 때문에 존의 모든 인원에게 보낸다.
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = dwValue1;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = cSubCmd;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
void CCastleEmblem::SendAttackedMessage()
{
unsigned long dwNowTime = timeGetTime();
if ( dwNowTime - m_dwLastAttackedTick >= Siege::EMBLEM_ATTACKED_INTERVAL )
{
m_dwLastAttackedTick = dwNowTime;
// 메세지 처리 때문에 존의 모든 인원에게 보낸다.
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = 0;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_EMBLEM_ATTACKED;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
}
}

View File

@@ -0,0 +1,44 @@
#ifndef _CASTLE_EMBLEM_OBJECT_H_
#define _CASTLE_EMBLEM_OBJECT_H_
#pragma once
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/SiegeConstants.h>
using namespace Siege;
class CCastleEmblem : public CSiegeObject
{
public:
virtual ~CCastleEmblem();
// CSkillMonster 의 기능
void NormalBehavior(unsigned long dwTick);
void AttackBehavior(unsigned long dwTick);
void SearchPlayer(void);
bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal);
bool Dead(CAggresiveCreature* pOffencer);
// 성 상징물 관련 함수
bool Upgrade(unsigned char cUpgradeStep);
bool Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwNoValue, unsigned char cSubCmd); // 상징물 업데이트 (소환중, 소환완료, 업그레이드중)
// Get / Set 함수
unsigned char GetEnemyNation() const { return m_cEnemyNation; }
// Send 함수
void SendAttackedMessage();
private:
CCastleEmblem(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject);
unsigned char m_cEnemyNation; // 상징물을 부슨 적의 국적
unsigned long m_dwLastAttackedTick; // 마지막으로 공격 받은 시간
friend class CSiegeObjectMgr;
};
#endif _CASTLE_EMBLEM_OBJECT_H_

View File

@@ -0,0 +1,432 @@
#include "stdafx.h"
#include "CastleGate.h"
#include <Skill/SkillTable.h>
#include <Skill/SkillMgr.h>
#include <Utility/Math/Math.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/ClientSocket/ClientConstants.h>
CCastleGate::CCastleGate(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject)
: CSiegeObject(MonsterCreate, CastleObject)
{
}
CCastleGate::~CCastleGate()
{
}
bool CCastleGate::Dead(CAggresiveCreature* pOffencer)
{
if (NULL == pOffencer) return false;
if (STATE_ID_DIE == m_nCurrentState) return false;
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_bCasting = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP);
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
return GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), pOffencer->GetCID(),
GetCastleID(), m_dwCID, 0, 0, PktCastleCmd::CASTLE_DESTROY_GATE,
PktBase::NO_SERVER_ERR);
}
return false;
}
void CCastleGate::AddProtectGate(CCharacter* lpProtectChar)
{
if (lpProtectChar && lpProtectChar->GetStatus().m_nNowHP > 0)
{
ProtectCIDList::iterator itr = std::find(m_ProtectCharList.begin(), m_ProtectCharList.end(), lpProtectChar->GetCID());
if (itr == m_ProtectCharList.end())
{
m_ProtectCharList.push_back(lpProtectChar->GetCID());
lpProtectChar->SetProtectGateCID(m_dwCID);
++m_CreatureStatus.m_StatusInfo.m_wBlock;
SendProtectGateInfo();
}
}
}
void CCastleGate::DeleteProtectGate(CCharacter* lpProtectChar)
{
if (lpProtectChar)
{
ProtectCIDList::iterator itr = std::find(m_ProtectCharList.begin(), m_ProtectCharList.end(), lpProtectChar->GetCID());
if (itr != m_ProtectCharList.end())
{
m_ProtectCharList.erase(itr);
lpProtectChar->SetProtectGateCID(0);
if (0 < m_CreatureStatus.m_StatusInfo.m_wBlock)
{
--m_CreatureStatus.m_StatusInfo.m_wBlock;
}
SendProtectGateInfo();
}
}
}
void CCastleGate::DivideDamage(CAggresiveCreature* pOffencer, unsigned short wDamage)
{
if (NULL == pOffencer)
{
return;
}
int nNum = static_cast<int>(m_ProtectCharList.size());
wDamage = static_cast<unsigned short>(wDamage * 100 / 1.33f);
unsigned short wCharDamage = (nNum == 0) ? 0 : wDamage / nNum;
ProtectCIDList::iterator itr = m_ProtectCharList.begin();
while (itr != m_ProtectCharList.end())
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter((*itr));
if (lpCharacter)
{
unsigned short wNowHP = lpCharacter->GetStatus().m_nNowHP;
const unsigned short wThreatAmount = (wNowHP < wCharDamage) ? wNowHP : wCharDamage;
lpCharacter->GetThreat().AddToThreatList(pOffencer, wThreatAmount);
lpCharacter->GetStatus().m_nNowHP = (wNowHP > wCharDamage) ? wNowHP - wCharDamage : 0;
if (0 == lpCharacter->GetStatus().m_nNowHP)
{
lpCharacter->Dead(pOffencer);
lpCharacter->GetThreat().ClearAll();
}
AtType attackType;
attackType.m_wType = AtType::RIGHT_MELEE;
CGameClientDispatch* pCharacterDispatcher = lpCharacter->GetDispatcher();
if (NULL != pCharacterDispatcher)
{
GameClientSendPacket::SendCharAttacked(pCharacterDispatcher->GetSendStream(), pOffencer, lpCharacter,
attackType, 0, wCharDamage, ClientConstants::Judge_Front, 0, PktBase::NO_SERVER_ERR);
}
}
}
}
void CCastleGate::SendProtectGateInfo()
{
int nNum = static_cast<int>(m_ProtectCharList.size());
// 블럭률 계산식 : INT((99 * 블럭수치 / 2) / (블럭수치 / 2 + 50) * 100) / 100
int nBlockRate = static_cast<int>((99 * m_CreatureStatus.m_StatusInfo.m_wBlock / 2) / (m_CreatureStatus.m_StatusInfo.m_wBlock / 2 + 50) * 100) / 100;
if (0 == nNum) return;
ProtectCIDList::iterator itr = m_ProtectCharList.begin();
while (itr != m_ProtectCharList.end())
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter((*itr));
if (lpCharacter)
{
CGameClientDispatch* pCharacterDispatcher = lpCharacter->GetDispatcher();
if (NULL != pCharacterDispatcher)
{
GameClientSendPacket::SendCharCastleCmd(pCharacterDispatcher->GetSendStream(), GetCastleID(), m_dwCID,
nBlockRate, m_CreatureStatus.m_nNowHP,
PktCastleCmd::CASTLE_GATE_PROTECT_INFO, PktBase::NO_SERVER_ERR);
}
}
++itr;
}
}
void CCastleGate::Open()
{
if ( !IsGate() ) return;
m_cSubState = Siege::OPENED;
// 성문이 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cSubState;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_GATE_OPEN;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
}
void CCastleGate::Close()
{
if ( !IsGate() ) return;
m_cSubState = Siege::CLOSED;
// 성문이 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cSubState;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_GATE_CLOSE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
}
void CCastleGate::ForceOpen()
{
if ( !IsGate() ) return;
if ( Siege::COMPLETE == m_cState && Siege::OPENED == m_cSubState )
{
return;
}
m_cState = Siege::COMPLETE;
m_cSubState = Siege::OPENED;
m_nCurrentState = STATE_ID_NORMAL;
UpdateObjectInfo(Siege::FULL_HP, 0);
// 성문이 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cSubState;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_GATE_OPEN;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
}
void CCastleGate::ForceClose()
{
if ( !IsGate() ) return;
if ( Siege::COMPLETE == m_cState && Siege::CLOSED == m_cSubState )
{
return;
}
m_cState = Siege::COMPLETE;
m_cSubState = Siege::CLOSED;
m_nCurrentState = STATE_ID_NORMAL;
UpdateObjectInfo(Siege::FULL_HP, 0);
// 성문이 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cSubState;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_GATE_CLOSE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
}
bool CCastleGate::Upgrade(unsigned char cUpgradeStep)
{
m_cState = Siege::COMPLETE;
m_cUpgradeStep = cUpgradeStep;
UpdateObjectInfo(Siege::UPGRADE_HP);
// 성문이 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cUpgradeStep; // 업그레이드 단계
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_UPGRADE_GATE_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleGate::Repair(unsigned short wRepairHP)
{
m_cState = Siege::COMPLETE;
UpdateObjectInfo(Siege::REPAIR_HP, wRepairHP);
// 성문이 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_CreatureStatus.m_nNowHP; // 현재 HP
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_REPAIR_GATE_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleGate::Restore()
{
m_cState = Siege::COMPLETE;
m_cUpgradeStep = 0;
m_nCurrentState = STATE_ID_NORMAL;
UpdateObjectInfo();
// 성문이 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_CreatureStatus.m_nNowHP; // 현재 HP
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_RESTORE_GATE_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleGate::Destroy(unsigned char cOffencerNation, bool bTakeGold)
{
m_cState = Siege::DESTROYED;
m_cSubState = Siege::OPENED;
m_cUpgradeStep = 0;
m_CreatureStatus.m_nNowHP = 0;
UpdateObjectInfo();
// 메세지 처리 때문에 존의 모든 인원에게 보낸다.
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = 0;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = PktCastleCmd::CASTLE_DESTROY_GATE;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
bool CCastleGate::Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwNoValue, unsigned char cSubCmd)
{
m_cState = cState;
if (PktCastleCmd::CASTLE_RESTORE_GATE == cSubCmd)
{
UpdateObjectInfo(Siege::FULL_HP);
}
else
{
UpdateObjectInfo();
}
// 성문이 있는 반경 5셀 이내에 전송
PktCastleCmd pktCC;
pktCC.m_dwCastleID = GetCastleID();
pktCC.m_dwCastleObjectID = m_dwCID;
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = dwValue1;
pktCC.m_dwValue2 = 0;
pktCC.m_cSubCmd = cSubCmd;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCastleCmd), CmdCastleCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCastleCmd), CmdCastleCmd);
}
return true;
}
unsigned long CCastleGate::GetRepairGold() const
{
// int nDiffHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP - m_CreatureStatus.m_nNowHP;
// int nShare = nDiffHP / Siege::CASTLE_ARMS_REPAIR_HP_UNIT;
// int nLeftOver = nDiffHP % Siege::CASTLE_ARMS_REPAIR_HP_UNIT;
// if (nLeftOver > 0) ++nShare;
//
// return nShare * Siege::CASTLE_ARMS_REPAIR_GOLD_PER_UNIT;
return 0;
}

View File

@@ -0,0 +1,51 @@
#ifndef _CASTLE_GATE_OBJECT_H_
#define _CASTLE_GATE_OBJECT_H_
#pragma once
#include <list>
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/SiegeConstants.h>
using namespace Siege;
class CCastleGate : public CSiegeObject
{
public:
typedef std::list<unsigned long> ProtectCIDList;
virtual ~CCastleGate();
// CSkillMonster 의 기능
bool Dead(CAggresiveCreature* pOffencer);
// 성문 관련 함수
void AddProtectGate(CCharacter* lpProtectChar);
void DeleteProtectGate(CCharacter* lpProtectChar);
void DivideDamage(CAggresiveCreature* pOffencer, unsigned short wDamage);
void SendProtectGateInfo();
void Open();
void Close();
void ForceOpen();
void ForceClose();
bool Upgrade(unsigned char cUpgradeStep);
bool Repair(unsigned short wRepairHP);
bool Restore();
bool Destroy(unsigned char cOffencerNation = 0, bool bTakeGold = false );
bool Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwNoValue, unsigned char cSubCmd);
// Get / Set 함수
unsigned long GetRepairGold() const;
private:
CCastleGate(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject);
ProtectCIDList m_ProtectCharList; // 성문 막기중인 캐릭터 CID 리스트
friend class CSiegeObjectMgr;
};
#endif _CASTLE_GATE_OBJECT_H_

View File

@@ -0,0 +1,204 @@
#include "stdafx.h"
#include "CastleGuard.h"
#include <Castle/CastleMgr.h>
#include <Skill/SkillTable.h>
#include <Skill/SkillMgr.h>
#include <Utility/Math/Math.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharItem.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/ClientSocket/ClientConstants.h>
#include <GameTime/GameTimeConstants.h>
#include <GameTime/GameTimeMgr.h>
CCastleGuard::CCastleGuard(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject)
: CCastleArms(MonsterCreate, CastleObject)
{
}
CCastleGuard::~CCastleGuard()
{
}
void CCastleGuard::NormalBehavior(unsigned long dwTick)
{
if (!CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
return;
}
// 선공 몹 처리
if (NULL == m_lpTarget && true == m_MonsterInfo.m_bFirstAttack)
{
SearchPlayer();
}
}
void CCastleGuard::AttackBehavior(unsigned long dwTick)
{
PERFORMANCE_CHECK(FunctionTimingCheck)
if (!CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
CancelTarget();
return;
}
// 마법 캐스팅 중일때는.. 아무런 다른 행동을 해서는 안된다.
if (true == m_bCasting)
{
CastingAttackAction();
return;
}
m_lpTarget = m_Threat.GetTarget();
if (NULL == m_lpTarget ||
(m_lpTarget && true == m_lpTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Hide)))
{
CancelTarget();
return;
}
const float fDY = fabs(m_lpTarget->GetCurrentPos().m_fPointY - GetCurrentPos().m_fPointY);
const float fDX = m_lpTarget->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX;
const float fDZ = m_lpTarget->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ;
const float fDistance = sqrtf((fDX * fDX) + (fDZ * fDZ));
if (((fDistance > m_wSearchRange) && false == m_bLongRangeAttacked) || (0 == m_lpTarget->GetStatus().m_nNowHP))
{
CancelTarget();
return;
}
const float fAttackRange = m_MonsterInfo.m_wAttackRange / 100.0f;
if (fDistance > fAttackRange && 0 >= m_lCurrentFrame)
{
// 공격 범위 밖이다.
if (true == SkillAttack()) // 스킬 공격을 사용하는가 체크
{
SkillAttackAction();
}
if (false == m_bAttacking)
{
m_lCurrentFrame = m_MotionInfo.m_dwFrame;
}
}
else
{
// 공격 범위 안이다.
if (false == m_bAttacking)
{
if (true == SkillAttack()) // 스킬 공격을 사용하는가 체크
{
SkillAttackAction();
}
}
}
}
void CCastleGuard::SearchPlayer(void)
{
// TODO : 해상도 조절을 통해 float 계산을 없애보자.
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG1(g_Log, "CID:0X%08 공성 오브젝트가 셀 범위 밖에 있습니다.", m_dwCID);
return;
}
CCell* pCell = NULL;
CSiegeObject* pTempTarget = NULL;
CSiegeObject* pCurrentTarget = NULL;
// const float fSquareSearchRange = (float)(m_wSearchRange * m_wSearchRange);
const float fSquareSearchRange = (float)(45 * 45);
for (int nCellCount = 0; nCellCount < CCell::CONNECT_NUM; ++nCellCount)
{
pCell = m_CellPos.m_lpCell->GetConnectCell(nCellCount);
if (NULL == pCell || false == pCell->IsSiegeObject())
{
continue;
}
pTempTarget = pCell->GetFirstAirShip();
while (NULL != pTempTarget)
{
if (pTempTarget->GetStatus().m_nNowHP > 0 && EnemyCheck::EC_ENEMY == IsEnemy(pTempTarget))
{
if (false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Stealth) &&
false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Invincible) &&
false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Hide))
{
const float fDX = pTempTarget->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX;
const float fDZ = pTempTarget->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ;
const float fDistance = (fDX * fDX) + (fDZ * fDZ);
if (fDistance < fSquareSearchRange)
{
pCurrentTarget = pTempTarget;
break;
}
}
}
pTempTarget = pCell->GetNextAirShip();
}
}
if (NULL != pCurrentTarget)
{
m_Threat.AddToThreatList(pCurrentTarget, 1);
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_SEEN_PLAYER);
}
}
bool CCastleGuard::Dead(CAggresiveCreature* pOffencer)
{
if (NULL == pOffencer) return false;
if (STATE_ID_DIE == m_nCurrentState) return false;
m_wObjectType = Siege::CASTLE_ARMS_NPC;
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_bCasting = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP);
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
return GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), pOffencer->GetCID(),
GetCastleID(), m_dwCID, PktCastleCmd::DESTROY, 0,
PktCastleCmd::CASTLE_DESTROY_ARMS, PktBase::NO_SERVER_ERR);
}
return false;
}

View File

@@ -0,0 +1,39 @@
#ifndef _CASTLE_GUARD_OBJECT_H_
#define _CASTLE_GUARD_OBJECT_H_
#pragma once
#include <Creature/Siege/CastleArms.h>
using namespace Siege;
class CCastleGuard : public CCastleArms
{
public:
virtual ~CCastleGuard();
// CSkillMonster 의 기능
void NormalBehavior(unsigned long dwTick);
void AttackBehavior(unsigned long dwTick);
void SearchPlayer(void);
bool Dead(CAggresiveCreature* pOffencer);
// 가드는 자동 공격이므로 아래 함수의 구현이 없다.
bool AttackCID(CCharacter* lpRideChar, AtType attackType, AtNode& attackNode, unsigned short& wError) { return false; }
bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders,
unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal) { return false; }
bool MissileAttack(AtType attackType, float fDir, float nRange, float fAngle, char cTargetType) { return false; }
// Rider 관련 정보
bool Ride(unsigned long dwCID) { return false; } // 병기 탑승
bool GetOff(unsigned long dwCID) { return false; } // 병기 내림 (중계 서버로 패킷 전송)
private:
CCastleGuard(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject);
friend class CSiegeObjectMgr;
};
#endif _CASTLE_GUARD_OBJECT_H_

View File

@@ -0,0 +1,637 @@
#include "stdafx.h"
#include "MiningCamp.h"
#include <Network/Stream/SendStream.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include <Item/ItemStructure.h>
#include <Item/ItemFactory.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/FieldMap/MineralVeinMgr.h>
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
#include <Castle/CastleBlessMgr.h>
#include <Utility/Math/Math.h>
CMiningCamp::CMiningCamp(MonsterCreateInfo& MonsterCreate, unsigned long dwCampID, unsigned long dwGID,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState,
unsigned char cSubState, unsigned char cUpgradeStep, unsigned char cMaterial,
unsigned char cSiegeCount, const CampRight& campRight, bool bFullHP)
: CCamp(MonsterCreate, dwCampID, dwGID, dwHP, wObjectType, cState, cSubState, cUpgradeStep, cMaterial, cSiegeCount, campRight, bFullHP)
{
}
CMiningCamp::~CMiningCamp()
{
}
bool CMiningCamp::Destroy(unsigned long dwOffencerGID)
{
CCamp::Destroy(dwOffencerGID);
CCell* lpCell = CCellManager::GetInstance().GetCell(0,
static_cast<unsigned long>(GetCurrentPos().m_fPointX),
static_cast<unsigned long>(GetCurrentPos().m_fPointY),
static_cast<unsigned long>(GetCurrentPos().m_fPointZ));
if (NULL == lpCell)
{
ERRLOG4(g_Log, "CampID:0x%08x 아이템을 드랍할 채굴기의 위치가 이상합니다. X:%.1f, Y:%.1f, Z:%.1f",
GetCampID(), GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointY, GetCurrentPos().m_fPointZ);
return false;
}
// 임시 광물 드랍
MineralInfoMap::iterator itr = m_TemporaryMineralMap.begin();
MineralInfoMap::iterator end = m_TemporaryMineralMap.end();
while (itr != end)
{
unsigned short wMineralID = itr->first;
unsigned short wAmount = itr->second;
while (wAmount > 0)
{
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem( wMineralID );
if (NULL == lpItem)
{
ERRLOG2(g_Log, "CampID:0x%08x 채굴기 드랍 광물 아이템 생성에 실패하였습니다. ItemID:%d", GetCampID(), wMineralID);
break;
}
unsigned char cItemNum = lpItem->GetMaxNumOrDurability();
if (wAmount < cItemNum)
{
cItemNum = static_cast<unsigned char>(wAmount);
lpItem->SetNumOrDurability(cItemNum);
wAmount = 0;
}
else
{
lpItem->SetNumOrDurability(cItemNum);
wAmount -= cItemNum;
}
CCell::ItemInfo itemInfo;
const Position Pos(GetCurrentPos().m_fPointX + Math::Random::ComplexRandom(40) - 20,
GetCurrentPos().m_fPointY,
GetCurrentPos().m_fPointZ + Math::Random::ComplexRandom(40) - 20);
lpCell->SetItem(Pos, lpItem, 0, dwOffencerGID,
(0 == dwOffencerGID) ? CCell::NONE : CCell::GUILD, itemInfo);
}
++itr;
}
// 누적 광물 드랍
itr = m_AccumulatedMineralMap.begin();
end = m_AccumulatedMineralMap.end();
while (itr != end)
{
unsigned short wMineralID = itr->first;
unsigned short wAmount = itr->second;
while (wAmount > 0)
{
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem( wMineralID );
if (NULL == lpItem)
{
ERRLOG2(g_Log, "CampID:0x%08x 채굴기 드랍 광물 아이템 생성에 실패하였습니다. ItemID:%d", GetCampID(), wMineralID);
break;
}
unsigned char cItemNum = lpItem->GetMaxNumOrDurability();
if (wAmount < cItemNum)
{
cItemNum = static_cast<unsigned char>(wAmount);
lpItem->SetNumOrDurability(cItemNum);
wAmount = 0;
}
else
{
lpItem->SetNumOrDurability( cItemNum );
wAmount -= cItemNum;
}
CCell::ItemInfo itemInfo;
const Position Pos(GetCurrentPos().m_fPointX + Math::Random::ComplexRandom(40) - 20,
GetCurrentPos().m_fPointY,
GetCurrentPos().m_fPointZ + Math::Random::ComplexRandom(40) - 20);
lpCell->SetItem(Pos, lpItem, 0, dwOffencerGID,
(0 == dwOffencerGID) ? CCell::NONE : CCell::GUILD, itemInfo);
}
++itr;
}
return true;
}
bool CMiningCamp::OnOff(unsigned char cSubState)
{
m_cSubState = cSubState;
if (Siege::MINING_OFF == m_cSubState)
{
m_cSiegeCount = 0;
// 임시 광물 날리기
m_TemporaryMineralMap.clear();
}
else if (Siege::MINING_ON == m_cSubState)
{
m_cSiegeCount = 0;
}
// 해당 진지가 있는 반경 5셀 이내에 전송
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = m_cSubState;
pktCC.m_dwValue2 = Siege::MINING_CAMP_GAIN_COUNT - m_cSiegeCount;
pktCC.m_cSubCmd = PktCampCmd::MINING_CAMP_ON_OFF;
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCampCmd), CmdCampCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCampCmd), CmdCampCmd);
}
return true;
}
bool CMiningCamp::GainMineral(unsigned short wMineralID, unsigned short wAmount)
{
unsigned short wError = PktCampCmd::NO_SERVER_ERR;
MineralInfoMap::iterator itr = m_AccumulatedMineralMap.find(wMineralID);
if ( itr != m_AccumulatedMineralMap.end() )
{
if ( itr->second >= wAmount )
{
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild( m_dwGID );
if ( lpGuild )
{
CCharacter* lpMaster = CCreatureManager::GetInstance().GetCharacter( lpGuild->GetMaster().m_dwCID );
if ( lpMaster )
{
unsigned short tempAmount = wAmount;
while (tempAmount != 0)
{
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem(wMineralID);
if (NULL == lpItem)
{
ERRLOG2(g_Log, "CampID:0x%08x 아이템 스크립트에 존재하지 않는 광물을 생성하려 하였습니다. ItemID:%d", GetCampID(), wMineralID);
wError = PktCampCmd::SERVER_ERROR;
break;
}
if (true == lpItem->IsSet(Item::DetailData::STACKABLE))
{
if (lpItem->GetMaxNumOrDurability() > tempAmount)
{
lpItem->SetNumOrDurability( static_cast<unsigned char>(tempAmount) );
tempAmount = 0;
}
else
{
lpItem->SetNumOrDurability( lpItem->GetMaxNumOrDurability() );
tempAmount -= lpItem->GetMaxNumOrDurability();
}
}
else
{
--tempAmount;
}
if (false == lpMaster->GiveItem(lpItem))
{
ERRLOG1(g_Log, "CID:0x%08x 누적 광물 아이템을 받는데 실패하였습니다.", lpGuild->GetMaster().m_dwCID);
DELETE_ITEM(lpItem);
wError = PktCampCmd::SERVER_ERROR;
break;
}
// GievItem 으로 스택된 경우
if (NULL != lpItem)
{
if (lpItem->IsSet(Item::DetailData::STACKABLE) && 0 == lpItem->GetNumOrDurability())
{
DELETE_ITEM(lpItem);
}
}
}
if (wError == PktCampCmd::NO_SERVER_ERR)
{
itr->second -= wAmount;
wAmount = itr->second;
if (0 == itr->second)
{
m_AccumulatedMineralMap.erase( itr );
}
}
}
else
{
ERRLOG1(g_Log, "CID : 0x%08x 길드 마스터가 존재하지 않습니다.", lpGuild->GetMaster().m_dwCID);
wError = PktCampCmd::SERVER_ERROR;
}
}
else
{
ERRLOG1(g_Log, "GID:0x%08x 해당 길드가 존재하지 않습니다.", m_dwGID);
wError = PktCampCmd::SERVER_ERROR;
}
}
else
{
ERRLOG4(g_Log, "지금 누적된 광물수보다 많은 양의 광물을 획득하려 합니다. CampID:0x%08x, MineralID:%d, CurrentAmount:%d, GainAmount:%d",
GetCampID(), wMineralID, itr->second, wAmount);
wError = PktCampCmd::SERVER_ERROR;
}
}
// 해당 요새가 있는 반경 5셀 이내에 전송
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = wMineralID;
pktCC.m_dwValue2 = wAmount; // 최종 남아 있는 갯수를 보내준다.
pktCC.m_cSubCmd = PktCampCmd::MINING_CAMP_GAIN_MINERAL;
if (wError == PktCampCmd::SERVER_ERROR)
{
pktCC.m_dwValue1 = 0;
pktCC.m_dwValue2 = 0;
}
char* szPacket = reinterpret_cast<char *>(&pktCC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, wError))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktCampCmd), CmdCampCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktCampCmd), CmdCampCmd);
}
return true;
}
bool CMiningCamp::AddMineral(unsigned char cFlag, const CampMineralInfo& campMineralInfo)
{
if (campMineralInfo.m_wAmount > 0)
{
switch (cFlag)
{
case Siege::ACCUMULATED_MINERAL:
{
m_AccumulatedMineralMap.insert( std::make_pair(campMineralInfo.m_wMineralID, campMineralInfo.m_wAmount) ).second;
}
break;
case Siege::TEMPORARY_MINERAL:
{
m_TemporaryMineralMap.insert( std::make_pair(campMineralInfo.m_wMineralID, campMineralInfo.m_wAmount) ).second;
}
break;
default: return false;
}
}
return true;
}
bool CMiningCamp::AddMineral(unsigned char cFlag, unsigned short wMineralID, unsigned short wAmount)
{
switch (cFlag)
{
case Siege::ACCUMULATED_MINERAL:
{
MineralInfoMap::iterator pos = m_AccumulatedMineralMap.find( wMineralID );
if ( pos != m_AccumulatedMineralMap.end() )
{
pos->second += wAmount;
if ( pos->second > Siege::MAX_MINERAL_NUM )
{
pos->second = Siege::MAX_MINERAL_NUM;
}
}
else if (wAmount > 0)
{
m_AccumulatedMineralMap.insert( std::make_pair(wMineralID, wAmount) ).second;
}
}
break;
case Siege::TEMPORARY_MINERAL:
{
MineralInfoMap::iterator pos = m_TemporaryMineralMap.find( wMineralID );
if ( pos != m_TemporaryMineralMap.end() )
{
pos->second += wAmount;
if ( pos->second > Siege::MAX_MINERAL_NUM )
{
pos->second = Siege::MAX_MINERAL_NUM;
}
}
else if (wAmount > 0)
{
m_TemporaryMineralMap.insert( std::make_pair(wMineralID, wAmount) ).second;
}
}
break;
default: return false;
}
return true;
}
bool CMiningCamp::SendMineralInfo(CSendStream& SendStream, unsigned char cMineralType)
{
unsigned short wSize = 0;
char szBuffer[1024];
PktMiningCampMineralInfo* lpPktMineralInfo = reinterpret_cast<PktMiningCampMineralInfo*>(szBuffer);
CampMineralInfo* lpCampMineralInfo = reinterpret_cast<CampMineralInfo*>(lpPktMineralInfo + 1);
lpPktMineralInfo->m_dwCampID = GetCampID();
lpPktMineralInfo->m_dwRemainTime = Siege::MINING_CAMP_GAIN_COUNT;
lpPktMineralInfo->m_cState = m_cSubState;
lpPktMineralInfo->m_cMineralType = cMineralType;
if ( Siege::MINING_ON == m_cSubState && 0 != m_cSiegeCount )
{
lpPktMineralInfo->m_dwRemainTime = static_cast<unsigned long>( Siege::MINING_CAMP_GAIN_COUNT - m_cSiegeCount );
}
switch ( cMineralType )
{
case Siege::ACCUMULATED_MINERAL:
{
lpPktMineralInfo->m_cNum = static_cast<unsigned char>( m_AccumulatedMineralMap.size() );
MineralInfoMap::iterator itr = m_AccumulatedMineralMap.begin();
MineralInfoMap::iterator end = m_AccumulatedMineralMap.end();
while (itr != end)
{
lpCampMineralInfo->m_wMineralID = itr->first;
lpCampMineralInfo->m_wAmount = itr->second;
++itr;
++lpCampMineralInfo;
wSize += sizeof(CampMineralInfo);
}
lpPktMineralInfo->m_wSize = wSize;
}
break;
case Siege::TEMPORARY_MINERAL:
{
lpPktMineralInfo->m_cNum = static_cast<unsigned char>( m_TemporaryMineralMap.size() );
MineralInfoMap::iterator itr = m_TemporaryMineralMap.begin();
MineralInfoMap::iterator end = m_TemporaryMineralMap.end();
while (itr != end)
{
lpCampMineralInfo->m_wMineralID = itr->first;
lpCampMineralInfo->m_wAmount = itr->second;
++itr;
++lpCampMineralInfo;
wSize += sizeof(CampMineralInfo);
}
lpPktMineralInfo->m_wSize = wSize;
}
break;
}
return SendStream.WrapCompress(szBuffer, static_cast<unsigned short>(sizeof(PktMiningCampMineralInfo) + wSize), CmdMiningCampMineralInfo, 0, 0);
}
unsigned short CMiningCamp::GetMineralNum(unsigned char cFlag, unsigned short wMineralID) const
{
switch (cFlag)
{
case Siege::ACCUMULATED_MINERAL:
{
MineralInfoMap::const_iterator itr = m_AccumulatedMineralMap.find(wMineralID);
if (itr != m_AccumulatedMineralMap.end())
{
return itr->second;
}
}
break;
case Siege::TEMPORARY_MINERAL:
{
MineralInfoMap::const_iterator itr = m_TemporaryMineralMap.find(wMineralID);
if (itr != m_TemporaryMineralMap.end())
{
return itr->second;
}
}
break;
}
return 0;
}
unsigned short CMiningCamp::GetMineralTypeNum(unsigned char cFlag) const
{
switch (cFlag)
{
case Siege::ACCUMULATED_MINERAL:
{
return static_cast<unsigned short>( m_AccumulatedMineralMap.size() );
}
break;
case Siege::TEMPORARY_MINERAL:
{
return static_cast<unsigned short>( m_TemporaryMineralMap.size() );
}
break;
}
return 0;
}
void CMiningCamp::ProcessMining(unsigned long dwProcessType)
{
// 1. 가동중이던 채굴기의 임시 광물에 새로 추가해준다.
if ( PktProcessMining::TEMPORARY_PROCESS == dwProcessType )
{
MineralVeinInfo* lpVeinInfo = CMineralVeinMgr::GetInstance().GetMineralVein(GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointZ);
if ( lpVeinInfo && 0 < lpVeinInfo->m_dwNowFertility )
{
int nMiningCampNum = CMineralVeinMgr::GetInstance().GetMiningCampNum( lpVeinInfo->m_dwVeinColor );
if (0 < nMiningCampNum)
{
MineralInfoList::const_iterator itr = lpVeinInfo->m_lstMineralInfo.begin();
MineralInfoList::const_iterator end = lpVeinInfo->m_lstMineralInfo.end();
unsigned long dwMaxFertility = lpVeinInfo->m_dwMaxFertility;
unsigned long dwNowFertility = lpVeinInfo->m_dwNowFertility;
float fRate = (float)dwNowFertility / (float)dwMaxFertility;
bool bCampMineralChange = false, bCastleMineralChange = false;
Castle::CCastle* lpBlessCastle = NULL ;
while (itr != end)
{
// 지력에 의해 -_- 나오는 광석의 양이 한정되어있다.
// 이값에 의해 나오는 광석은 0이 될수도 있따. 즉 이럴경우 광석이 안나올 확율이 높은 확율로 있다.
const MineralInfo& tempInfo = (*itr);
unsigned short wAmount = static_cast<unsigned short>(
Math::Random::ComplexRandom(tempInfo.m_cMax + 1, tempInfo.m_cMin) * fRate / nMiningCampNum );
if (wAmount > 0)
{
/*
// CASTLE_TODO : 성이 길드 소유가 아니므로 성의 축복 보너스 기능은 막아둔다.
// edith 세금 부분 추가 (주석처리 뺐음)
// 길드 요새가 성의 축복 영역에 들어있다면, 축복 보너스와 세금 처리를 한다.
lpBlessCastle = Castle::CCastleMgr::GetInstance().GetCastleInBlessArea( GetPosition() );
if (NULL != lpBlessCastle && 0 != lpBlessCastle->GetGID())
{
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild( GetGID() );
if (NULL != lpGuild && false == lpGuild->IsEnemyGuild(lpBlessCastle->GetGID()))
{
unsigned char cBlessBonus = Castle::CCastleBlessMgr::GetInstance().GetBonusPercent(
lpBlessCastle->GetTotalGainTaxCount(), lpBlessCastle->GetUpgradeStep());
// 성의 축복 보너스 만큼 더해준다.
unsigned short wBlessBonusAmount = static_cast<unsigned short>(wAmount * (cBlessBonus / 100.0f));
wBlessBonusAmount = std::max(wBlessBonusAmount, static_cast<unsigned short>(1));
// 성에 길드 요새 광물 세금을 낸다.
unsigned short wCastleTaxAmount = static_cast<unsigned short>(wAmount * (lpBlessCastle->GetTax(Castle::CAMP_MINERAL_TAX) / 100.0f));
wCastleTaxAmount = std::max(wCastleTaxAmount, static_cast<unsigned short>(1));
wAmount += wBlessBonusAmount;
wCastleTaxAmount = std::min(wCastleTaxAmount, wAmount);
wAmount -= wCastleTaxAmount;
bCastleMineralChange = true;
lpBlessCastle->AddMineral(Siege::TEMPORARY_MINERAL, tempInfo.m_dwMineralID, wCastleTaxAmount);
}
}
//
*/
if (wAmount > 0)
{
bCampMineralChange = true;
AddMineral(Siege::TEMPORARY_MINERAL, tempInfo.m_dwMineralID, wAmount);
}
}
++itr;
}
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 == lpDBAgentDispatch)
{
ERRLOG0(g_Log, "에이전트 얻기 실패.");
}
else
{
if (bCampMineralChange)
{
// 임시 광물에 추가 될 경우에 DB 중계 서버로 임시 광물 정보 보내기
SendMineralInfo(lpDBAgentDispatch->GetSendStream(), Siege::TEMPORARY_MINERAL);
}
if (bCastleMineralChange && NULL != lpBlessCastle)
{
// 임시 광물 세금이 추가 될 경우에 DB 중계 서버로 임시 광물 세금 정보 보내기
lpBlessCastle->SendMineralInfo(lpDBAgentDispatch->GetSendStream(), Siege::TEMPORARY_MINERAL);
}
}
}
}
}
// 2. 7일이 지난 채굴기의 임시 광물을 누적 광물로 옮겨준다. (임시 광물 삭제)
if ( PktProcessMining::ACCUMULATED_PROCESS == dwProcessType )
{
++m_cSiegeCount ;
if ( m_cSiegeCount >= Siege::MINING_CAMP_GAIN_COUNT )
{
MineralInfoMap::iterator itr = m_TemporaryMineralMap.begin();
MineralInfoMap::iterator end = m_TemporaryMineralMap.end();
while (itr != end)
{
AddMineral(Siege::ACCUMULATED_MINERAL, itr->first, itr->second);
++itr;
}
m_cSiegeCount = 0;
m_TemporaryMineralMap.clear();
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 == lpDBAgentDispatch)
{
ERRLOG0(g_Log, "에이전트 얻기 실패.");
}
else
{
// 중계 서버로 채굴 완료 메세지 전송
GameClientSendPacket::SendCharCampMessageToDBAgent(lpDBAgentDispatch->GetSendStream(), GetCampID(),
PktCampMessage::MSGCMD_MINING_END, PktBase::NO_SERVER_ERR);
// DB 중계 서버로 임시 광물 및 누적 광물 리스트 보내기
SendMineralInfo(lpDBAgentDispatch->GetSendStream(), Siege::TEMPORARY_MINERAL);
SendMineralInfo(lpDBAgentDispatch->GetSendStream(), Siege::ACCUMULATED_MINERAL);
}
/*
// 길드 마스터에게 누적 광물 정보 보내기
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild( m_dwGID );
if ( lpGuild )
{
CCharacter* lpMaster = CCreatureManager::GetInstance().GetCharacter( lpGuild->GetMaster().m_dwCID );
if ( lpMaster && lpMaster->GetDispatcher() )
{
SendMineralInfo(lpMaster->GetDispatcher()->GetSendStream(), Siege::ACCUMULATED_MINERAL);
}
}
*/
}
}
}

View File

@@ -0,0 +1,51 @@
#ifndef _MINING_CAMP_H_
#define _MINING_CAMP_H_
#pragma once
#include <map>
#include <Creature/Siege/Camp.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
using namespace Siege;
class CSendStream;
class CMiningCamp : public CCamp
{
public:
virtual ~CMiningCamp();
typedef std::map<unsigned short, unsigned short> MineralInfoMap; // <wMineralID, wAmount>
void ProcessMining(unsigned long dwProcessType);
bool Destroy(unsigned long dwOffencerGID = 0); // 채굴기 파괴 완료시 처리
bool OnOff(unsigned char cSubState);
bool GainMineral(unsigned short wMineralID, unsigned short wAmount);
bool AddMineral(unsigned char cFlag, const CampMineralInfo& campMineralInfo);
bool AddMineral(unsigned char cFlag, unsigned short wMineralID, unsigned short wAmount);
unsigned short GetMineralNum(unsigned char cFlag, unsigned short wMineralID) const;
unsigned short GetMineralTypeNum(unsigned char cFlag) const;
// 광물 정보 전송
bool SendMineralInfo(CSendStream& SendStream, unsigned char cMineralType=Siege::ACCUMULATED_MINERAL);
protected:
CMiningCamp(MonsterCreateInfo& MonsterCreate, unsigned long dwCampID, unsigned long dwGID,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState, unsigned char cSubState,
unsigned char cUpgradeStep, unsigned char cMaterial, unsigned char cSiegeCount,
const CampRight& campRight, bool bFullHP);
MineralInfoMap m_AccumulatedMineralMap;
MineralInfoMap m_TemporaryMineralMap;
friend class CSiegeObjectMgr;
};
#endif // _MINING_CAMP_H_

View File

@@ -0,0 +1,297 @@
#include "stdafx.h"
#include "SerializeSiegeObjectData.h"
#include <Creature/Siege/SiegeObject.h>
BroadCastSiege::CSerializeSiegeObjectData::CSerializeSiegeObjectData() :
m_wBroadCastDataLen(0), m_wDeltaBroadCastDataLen(0)
{
}
BroadCastSiege::CSerializeSiegeObjectData::~CSerializeSiegeObjectData()
{
}
void
BroadCastSiege::CSerializeSiegeObjectData::Initialize( const CSiegeObject& siegeObject )
{
// Data 얻어오기
sOwnerShipInfo ownerShipInfo;
ownerShipInfo.m_dwCampOrCastleID = siegeObject.GetCampID();
ownerShipInfo.m_dwOwnerCID = siegeObject.GetOwnerID();
ownerShipInfo.m_dwGID = siegeObject.GetGID();
ownerShipInfo.m_cNation = siegeObject.GetNation();
sStateInfo stateInfo;
stateInfo.m_wObjectType = siegeObject.GetObjectType();
stateInfo.m_cState = siegeObject.GetState();
stateInfo.m_cSubState = siegeObject.GetSubState();
stateInfo.m_cUpgradeStep = siegeObject.GetUpgradeStep();
stateInfo.m_cUpgradeType = siegeObject.GetUpgradeType();
sHPInfo hpInfo;
hpInfo.m_dwNowHP = siegeObject.GetNowHP();
hpInfo.m_dwMaxHP = siegeObject.GetMaxHP();
sPosInfo posInfo;
const Position& pos = siegeObject.GetPosition();
const MotionInfo& motionInfo = siegeObject.GetMotionInfo();
posInfo.m_fDefaultDir = siegeObject.GetDefaultDir();
posInfo.m_NetworkPos = CNetworkPos( pos.m_fPointX, pos.m_fPointY, pos.m_fPointZ, motionInfo.m_fDirection,
(0 == motionInfo.m_dwFrame) ? 0.0f : motionInfo.m_fVelocity / motionInfo.m_dwFrame );
sMaterialInfo materialInfo;
materialInfo.m_cMaterial = siegeObject.GetMaterialNum();
sRiderInfo riderInfo;
std::fill_n(&riderInfo.m_dwRiderID[0], int(Siege::AIRSHIP_RIDER_NUM), 0);
siegeObject.GetRiders( riderInfo.m_dwRiderID );
// 얻어온 Data 로 초기화
m_LastSiegeObjectData.SetOwnerShipInfo( ownerShipInfo );
m_LastSiegeObjectData.SetStateInfo( stateInfo );
m_LastSiegeObjectData.SetHPInfo( hpInfo );
m_LastSiegeObjectData.SetPosInfo( posInfo );
m_LastSiegeObjectData.SetMaterialInfo( materialInfo );
m_LastSiegeObjectData.SetRiderInfo( riderInfo );
// BroadCast 버퍼 셋팅
PrepareData( siegeObject );
}
// ==================================================================================
// Data 를 셋팅
void
BroadCastSiege::CSerializeSiegeObjectData::PrepareData( const CSiegeObject& siegeObject )
{
PrepareBroadCastData( siegeObject );
PrepareDeltaData( siegeObject );
}
void
BroadCastSiege::CSerializeSiegeObjectData::PrepareBroadCastData( const CSiegeObject& siegeObject )
{
assert( CSiegeObjectData::EstimateBufferSize(0xFFFFFFFF) < MAX_SIEGEOBJECT_DATA_SIZE && "MAX_SIEGEOBJECT_DATA_SIZE 를 늘려주세요!" );
m_wBroadCastDataLen = 0;
char* szDataPos = m_aryBroadCastData;
// UpdateDataFlag 복사 (모든 Data 를 보낸다.)
unsigned long dwUpdateFlag = 0xFFFFFFFF;
COPY_AND_ADVANCE_DST( szDataPos, &dwUpdateFlag, sizeof(unsigned long) );
// 소유권 정보 복사
sOwnerShipInfo ownerShipInfo;
ownerShipInfo.m_dwCampOrCastleID = siegeObject.GetCampID();
ownerShipInfo.m_dwOwnerCID = siegeObject.GetOwnerID();
ownerShipInfo.m_dwGID = siegeObject.GetGID();
ownerShipInfo.m_cNation = siegeObject.GetNation();
COPY_AND_ADVANCE_DST( szDataPos, &ownerShipInfo, sizeof(sOwnerShipInfo) );
// 상태 정보 복사
sStateInfo stateInfo;
stateInfo.m_wObjectType = siegeObject.GetObjectType();
stateInfo.m_cState = siegeObject.GetState();
stateInfo.m_cSubState = siegeObject.GetSubState();
stateInfo.m_cUpgradeStep = siegeObject.GetUpgradeStep();
stateInfo.m_cUpgradeType = siegeObject.GetUpgradeType();
COPY_AND_ADVANCE_DST( szDataPos, &stateInfo, sizeof(sStateInfo) );
// HP 정보 복사
sHPInfo hpInfo;
hpInfo.m_dwNowHP = siegeObject.GetNowHP();
hpInfo.m_dwMaxHP = siegeObject.GetMaxHP();
COPY_AND_ADVANCE_DST( szDataPos, &hpInfo, sizeof(sHPInfo) );
// 위치 정보 복사
sPosInfo posInfo;
const Position& pos = siegeObject.GetPosition();
const MotionInfo& motionInfo = siegeObject.GetMotionInfo();
/*float fY = pos.m_fPointY;
if ( siegeObject.IsSiegeArms() && Siege::AIRSHIP != siegeObject.GetObjectType() )
{
fY = 0.0f;
}*/
posInfo.m_fDefaultDir = siegeObject.GetDefaultDir();
posInfo.m_NetworkPos = CNetworkPos( pos.m_fPointX, pos.m_fPointY, pos.m_fPointZ, motionInfo.m_fDirection,
(0 == motionInfo.m_dwFrame) ? 0.0f : motionInfo.m_fVelocity / motionInfo.m_dwFrame );
COPY_AND_ADVANCE_DST( szDataPos, &posInfo, sizeof(sPosInfo) );
// 자재 정보 복사
sMaterialInfo materialInfo;
materialInfo.m_cMaterial = siegeObject.GetMaterialNum();
COPY_AND_ADVANCE_DST( szDataPos, &materialInfo, sizeof(sMaterialInfo) );
// 탑승자 정보 복사
sRiderInfo riderInfo;
std::fill_n(&riderInfo.m_dwRiderID[0], int(Siege::AIRSHIP_RIDER_NUM), 0);
siegeObject.GetRiders( riderInfo.m_dwRiderID );
COPY_AND_ADVANCE_DST( szDataPos, &riderInfo, sizeof(sRiderInfo) );
// 데이터 길이를 구한다.
m_wBroadCastDataLen = static_cast<unsigned short>( szDataPos - m_aryBroadCastData );
}
void
BroadCastSiege::CSerializeSiegeObjectData::PrepareDeltaData( const CSiegeObject& siegeObject )
{
assert( CSiegeObjectData::EstimateBufferSize(0xFFFFFFFF) < MAX_SIEGEOBJECT_DATA_SIZE && "MAX_SIEGEOBJECT_DATA_SIZE 를 늘려주세요!" );
// 데이터를 비교해서 Delta를 구한다.
m_wDeltaBroadCastDataLen = 0;
char* szDataPos = m_aryDeltaBroadCastData;
// UpdateFlag 는 복사할 위치를 기억해두고, 최종적으로 기록한다.
unsigned long dwDeltaUpdateFlag = 0;
char* szUpdateFlagCopyPos = szDataPos;
szDataPos += sizeof(unsigned long);
// 소유권 정보 비교 및 복사
if ( m_LastSiegeObjectData.GetCampID() != siegeObject.GetCampID() ||
m_LastSiegeObjectData.GetOwnerCID() != siegeObject.GetOwnerID() ||
m_LastSiegeObjectData.GetGID() != siegeObject.GetGID() ||
m_LastSiegeObjectData.GetNation() != siegeObject.GetNation() )
{
sOwnerShipInfo ownerShipInfo;
ownerShipInfo.m_dwCampOrCastleID = siegeObject.GetCampID();
ownerShipInfo.m_dwOwnerCID = siegeObject.GetOwnerID();
ownerShipInfo.m_dwGID = siegeObject.GetGID();
ownerShipInfo.m_cNation = siegeObject.GetNation();
COPY_AND_ADVANCE_DST( szDataPos, &ownerShipInfo, sizeof(sOwnerShipInfo) );
dwDeltaUpdateFlag |= DELTA_OWNERSHIP;
m_LastSiegeObjectData.SetOwnerShipInfo( ownerShipInfo );
}
// 상태 정보 비교 및 복사
if ( m_LastSiegeObjectData.GetObjectType() != siegeObject.GetObjectType() ||
m_LastSiegeObjectData.GetState() != siegeObject.GetState() ||
m_LastSiegeObjectData.GetSubState() != siegeObject.GetSubState() ||
m_LastSiegeObjectData.GetUpgradeStep() != siegeObject.GetUpgradeStep() ||
m_LastSiegeObjectData.GetUpgradeType() != siegeObject.GetUpgradeType() )
{
sStateInfo stateInfo;
stateInfo.m_wObjectType = siegeObject.GetObjectType();
stateInfo.m_cState = siegeObject.GetState();
stateInfo.m_cSubState = siegeObject.GetSubState();
stateInfo.m_cUpgradeStep = siegeObject.GetUpgradeStep();
stateInfo.m_cUpgradeType = siegeObject.GetUpgradeType();
COPY_AND_ADVANCE_DST( szDataPos, &stateInfo, sizeof(sStateInfo) );
dwDeltaUpdateFlag |= DELTA_STATE;
m_LastSiegeObjectData.SetStateInfo( stateInfo );
}
// HP 정보 비교 및 복사
if ( m_LastSiegeObjectData.GetNowHP() != siegeObject.GetNowHP() ||
m_LastSiegeObjectData.GetMaxHP() != siegeObject.GetMaxHP() )
{
sHPInfo hpInfo;
hpInfo.m_dwNowHP = siegeObject.GetNowHP();
hpInfo.m_dwMaxHP = siegeObject.GetMaxHP();
COPY_AND_ADVANCE_DST( szDataPos, &hpInfo, sizeof(sHPInfo) );
dwDeltaUpdateFlag |= DELTA_HP;
m_LastSiegeObjectData.SetHPInfo( hpInfo );
}
// 위치 정보 비교 및 복사
const MotionInfo& motionInfo = siegeObject.GetMotionInfo();
CNetworkPos netPos = CNetworkPos( siegeObject.GetCurrentPos().m_fPointX, siegeObject.GetCurrentPos().m_fPointY,
siegeObject.GetCurrentPos().m_fPointZ, motionInfo.m_fDirection,
(0 == motionInfo.m_dwFrame) ? 0.0f : motionInfo.m_fVelocity / motionInfo.m_dwFrame );
// Code by By Minbobo
if( (netPos!=m_LastSiegeObjectData.GetNetworkPos()) ||
(m_LastSiegeObjectData.GetDefaultDir()!=siegeObject.GetDefaultDir()) )
{
sPosInfo posInfo;
const Position& pos = siegeObject.GetPosition();
float fY = pos.m_fPointY;
if ( siegeObject.IsSiegeArms() && Siege::AIRSHIP != siegeObject.GetObjectType() )
{
fY = 0.0f;
}
posInfo.m_fDefaultDir = siegeObject.GetDefaultDir();
posInfo.m_NetworkPos = CNetworkPos( pos.m_fPointX, fY, pos.m_fPointZ, motionInfo.m_fDirection,
(0 == motionInfo.m_dwFrame) ? 0.0f : motionInfo.m_fVelocity / motionInfo.m_dwFrame );
COPY_AND_ADVANCE_DST( szDataPos, &posInfo, sizeof(sPosInfo) );
dwDeltaUpdateFlag |= DELTA_POS;
posInfo.m_NetworkPos = netPos;
m_LastSiegeObjectData.SetPosInfo( posInfo );
}
/*if ( (m_LastSiegeObjectData.GetDefaultDir() - siegeObject.GetDefaultDir()) > FLOAT_EPSILON ||
(m_LastSiegeObjectData.GetNetworkPos().GetXPos() - netPos.GetXPos()) > FLOAT_EPSILON ||
(m_LastSiegeObjectData.GetNetworkPos().GetYPos() - netPos.GetYPos()) > FLOAT_EPSILON ||
(m_LastSiegeObjectData.GetNetworkPos().GetZPos() - netPos.GetZPos()) > FLOAT_EPSILON )
{
sPosInfo posInfo;
const Position& pos = siegeObject.GetPosition();
float fY = pos.m_fPointY;
if ( siegeObject.IsSiegeArms() && Siege::AIRSHIP != siegeObject.GetObjectType() )
{
fY = 0.0f;
}
posInfo.m_fDefaultDir = siegeObject.GetDefaultDir();
posInfo.m_NetworkPos = CNetworkPos( pos.m_fPointX, fY, pos.m_fPointZ, motionInfo.m_fDirection,
(0 == motionInfo.m_dwFrame) ? 0.0f : motionInfo.m_fVelocity / motionInfo.m_dwFrame );
COPY_AND_ADVANCE_DST( szDataPos, &posInfo, sizeof(sPosInfo) );
dwDeltaUpdateFlag |= DELTA_POS;
posInfo.m_NetworkPos = netPos;
m_LastSiegeObjectData.SetPosInfo( posInfo );
}*/
// 자재 정보 비교 및 복사
if ( m_LastSiegeObjectData.GetMaterialNum() != siegeObject.GetMaterialNum() )
{
sMaterialInfo materialInfo;
materialInfo.m_cMaterial = siegeObject.GetMaterialNum();
COPY_AND_ADVANCE_DST( szDataPos, &materialInfo, sizeof(sMaterialInfo) );
dwDeltaUpdateFlag |= DELTA_MATERIAL;
m_LastSiegeObjectData.SetMaterialInfo( materialInfo );
}
// 탑승자 정보 비교 및 복사
unsigned long dwRiders[ Siege::AIRSHIP_RIDER_NUM ];
std::fill_n(dwRiders, int(Siege::AIRSHIP_RIDER_NUM), 0);
siegeObject.GetRiders( dwRiders );
if ( !m_LastSiegeObjectData.IsSameRiders( dwRiders ) )
{
sRiderInfo riderInfo;
std::fill_n(&riderInfo.m_dwRiderID[0], int(Siege::AIRSHIP_RIDER_NUM), 0);
siegeObject.GetRiders( riderInfo.m_dwRiderID );
COPY_AND_ADVANCE_DST( szDataPos, &riderInfo, sizeof(sRiderInfo) );
dwDeltaUpdateFlag |= DELTA_RIDER;
m_LastSiegeObjectData.SetRiders( riderInfo.m_dwRiderID );
}
// 복사 완료 및 데이터 길이 계산.
memcpy( szUpdateFlagCopyPos, &dwDeltaUpdateFlag, sizeof(unsigned long) );
m_wDeltaBroadCastDataLen = static_cast<unsigned short>( szDataPos - m_aryDeltaBroadCastData);
// 변경된것이 없다면,
if ( 0 == dwDeltaUpdateFlag )
{
m_wDeltaBroadCastDataLen = 0;
}
}

Some files were not shown because too many files have changed in this diff Show More