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:
BIN
Server/RylServerProject/RylGameLibrary/CSAuth.lib
Normal file
BIN
Server/RylServerProject/RylGameLibrary/CSAuth.lib
Normal file
Binary file not shown.
1204
Server/RylServerProject/RylGameLibrary/Castle/Castle.cpp
Normal file
1204
Server/RylServerProject/RylGameLibrary/Castle/Castle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
133
Server/RylServerProject/RylGameLibrary/Castle/Castle.h
Normal file
133
Server/RylServerProject/RylGameLibrary/Castle/Castle.h
Normal 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_
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_
|
||||
102
Server/RylServerProject/RylGameLibrary/Castle/CastleConstants.h
Normal file
102
Server/RylServerProject/RylGameLibrary/Castle/CastleConstants.h
Normal 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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
@@ -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
|
||||
498
Server/RylServerProject/RylGameLibrary/Castle/CastleMgr.cpp
Normal file
498
Server/RylServerProject/RylGameLibrary/Castle/CastleMgr.cpp
Normal 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;
|
||||
|
||||
}
|
||||
87
Server/RylServerProject/RylGameLibrary/Castle/CastleMgr.h
Normal file
87
Server/RylServerProject/RylGameLibrary/Castle/CastleMgr.h
Normal 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_
|
||||
2232
Server/RylServerProject/RylGameLibrary/Community/guild/Guild.cpp
Normal file
2232
Server/RylServerProject/RylGameLibrary/Community/guild/Guild.cpp
Normal file
File diff suppressed because it is too large
Load Diff
194
Server/RylServerProject/RylGameLibrary/Community/guild/Guild.h
Normal file
194
Server/RylServerProject/RylGameLibrary/Community/guild/Guild.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
2543
Server/RylServerProject/RylGameLibrary/Community/party/Party.cpp
Normal file
2543
Server/RylServerProject/RylGameLibrary/Community/party/Party.cpp
Normal file
File diff suppressed because it is too large
Load Diff
276
Server/RylServerProject/RylGameLibrary/Community/party/Party.h
Normal file
276
Server/RylServerProject/RylGameLibrary/Community/party/Party.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,11 @@
|
||||
#include "stdafx.h"
|
||||
#include "CharacterStructure.h"
|
||||
#include "GMMemory.h"
|
||||
|
||||
|
||||
CharacterFightInfo::CharacterFightInfo()
|
||||
: m_pDuelOpponent(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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__
|
||||
@@ -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__
|
||||
@@ -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__
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Creature.h"
|
||||
|
||||
75
Server/RylServerProject/RylGameLibrary/Creature/Creature.h
Normal file
75
Server/RylServerProject/RylGameLibrary/Creature/Creature.h
Normal 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
|
||||
2060
Server/RylServerProject/RylGameLibrary/Creature/CreatureManager.cpp
Normal file
2060
Server/RylServerProject/RylGameLibrary/Creature/CreatureManager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
1145
Server/RylServerProject/RylGameLibrary/Creature/EnemyCheck.cpp
Normal file
1145
Server/RylServerProject/RylGameLibrary/Creature/EnemyCheck.cpp
Normal file
File diff suppressed because it is too large
Load Diff
268
Server/RylServerProject/RylGameLibrary/Creature/EnemyCheck.h
Normal file
268
Server/RylServerProject/RylGameLibrary/Creature/EnemyCheck.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_)
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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]--;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_
|
||||
1318
Server/RylServerProject/RylGameLibrary/Creature/Npc/NPC.cpp
Normal file
1318
Server/RylServerProject/RylGameLibrary/Creature/Npc/NPC.cpp
Normal file
File diff suppressed because it is too large
Load Diff
110
Server/RylServerProject/RylGameLibrary/Creature/Npc/NPC.h
Normal file
110
Server/RylServerProject/RylGameLibrary/Creature/Npc/NPC.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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__
|
||||
602
Server/RylServerProject/RylGameLibrary/Creature/Siege/Camp.cpp
Normal file
602
Server/RylServerProject/RylGameLibrary/Creature/Siege/Camp.cpp
Normal 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;
|
||||
}
|
||||
|
||||
73
Server/RylServerProject/RylGameLibrary/Creature/Siege/Camp.h
Normal file
73
Server/RylServerProject/RylGameLibrary/Creature/Siege/Camp.h
Normal 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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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
Reference in New Issue
Block a user