Restructure repository to include all source folders

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,50 @@
#ifndef __SERIALIZE_SIEGEOBJECT_DATA_H__
#define __SERIALIZE_SIEGEOBJECT_DATA_H__
#include "BroadCastSiegeObjectData.h"
// 전방 참조
class CSiegeObject;
namespace BroadCastSiege
{
class CSerializeSiegeObjectData
{
public:
CSerializeSiegeObjectData();
~CSerializeSiegeObjectData();
// ==================================================================================
// 초기화
void Initialize( const CSiegeObject& siegeObject );
// ==================================================================================
// Data 를 셋팅
void PrepareData( const CSiegeObject& siegeObject );
void PrepareBroadCastData( const CSiegeObject& siegeObject );
void PrepareDeltaData( const CSiegeObject& siegeObject );
void ClearDeltaData() { m_wDeltaBroadCastDataLen = 0; }
// ==================================================================================
// Get 함수
const CSiegeObjectData& GetSiegeObjectData() { return m_LastSiegeObjectData; }
const char* GetBroadCastData() { return m_aryBroadCastData; }
unsigned short GetBroadCastDataLen() { return m_wBroadCastDataLen; }
const char* GetDeltaData() { return m_aryDeltaBroadCastData; }
unsigned short GetDeltaDataLen() { return m_wDeltaBroadCastDataLen; }
private:
CSiegeObjectData m_LastSiegeObjectData;
unsigned short m_wBroadCastDataLen;
unsigned short m_wDeltaBroadCastDataLen;
char m_aryBroadCastData[ MAX_SIEGEOBJECT_DATA_SIZE ];
char m_aryDeltaBroadCastData[ MAX_SIEGEOBJECT_DATA_SIZE ];
};
}
#endif __SERIALIZE_SIEGEOBJECT_DATA_H__

View File

@@ -0,0 +1,879 @@
#include "stdafx.h"
#include "SiegeArms.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>
CSiegeArms::CSiegeArms(MonsterCreateInfo& MonsterCreate, unsigned long dwOwnerID, unsigned char cNation,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState,
unsigned char cUpgradeStep)
: CSiegeObject(MonsterCreate, dwOwnerID, cNation, dwHP, wObjectType, cState, cUpgradeStep)
{
}
CSiegeArms::~CSiegeArms()
{
}
bool CSiegeArms::AttackCID(CCharacter* lpRideChar, AtType attackType, AtNode& attackNode, unsigned short& wError)
{
PERFORMANCE_CHECK(FunctionTimingCheck);
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 CSiegeArms::Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal)
{
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 CSiegeArms::MissileAttack(AtType attackType, float fDir, float nRange, float fAngle, char cTargetType)
{
if (Siege::LONG_RANGE_SIEGE_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 CSiegeArms::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;
// 타고 있던 캐릭터는 죽는다.
if (0 != m_dwRiderCID)
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if (lpRider)
{
lpRider->GetOff();
lpRider->Kill(pOffencer);
}
m_dwRiderCID = 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;
}
bool CSiegeArms::ToStarterKit(bool bMaterial)
{
unsigned short wItemProtoTypeID;
switch (m_wObjectType)
{
case Siege::SHORT_RANGE_SIEGE_ARMS: wItemProtoTypeID = Item::EtcItemID::SHORT_RANGE_ARMS_KIT_ID; break;
case Siege::LONG_RANGE_SIEGE_ARMS: wItemProtoTypeID = Item::EtcItemID::LONG_RANGE_ARMS_KIT_ID; break;
case Siege::AIRSHIP: wItemProtoTypeID = Item::EtcItemID::AIRSHIP_KIT_ID; break;
}
// 스타트킷 아이템 생성
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem(wItemProtoTypeID);
if (NULL == lpItem)
{
ERRLOG0(g_Log, "길드 요새 생성 스타트킷 아이템 생성에 실패했습니다.");
return false;
}
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter( m_dwOwnerID );
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);
}
if(bMaterial)
{
GiveBackSiegeMaterial();
}
return true;
}
// 우선 임시로 주석 처리.
if(bMaterial)
{
GiveBackSiegeMaterial();
}
// 바닥에 아이템 떨어뜨리기
CCell::ItemInfo itemInfo;
GetCellPos().m_lpCell->SetItem(GetCurrentPos(), lpItem, 0, 0, CCell::NONE, itemInfo);
return true;
}
bool CSiegeArms::GiveBackSiegeMaterial()
{
// 업그레이드에 따른 갯수 설정
unsigned char cNum = 0;
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(m_wObjectType);
if (lpProtoType)
{
cNum = Siege::SIEGE_ARMS_UPGRADE_MATERIAL_NUM / 2;
}
if (cNum > 0)
{
// 자재 아이템 돌려주기
Item::CItem* lpMaterial = Item::CItemFactory::GetInstance().CreateItem(Item::EtcItemID::SIEGE_MATERIAL_ID);
if (NULL == lpMaterial)
{
ERRLOG0(g_Log, "공성 병기 제작용 자재 아이템 생성에 실패했습니다.");
return false;
}
lpMaterial->SetNumOrDurability(cNum);
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter(m_dwOwnerID);
if (NULL != lpCharacter)
{
if (false == lpCharacter->GiveItem(lpMaterial))
{
ERRLOG0(g_Log, "공성 병기 제작용 자재를 돌려주는데 실패하였습니다.");
DELETE_ITEM(lpMaterial);
return false;
}
// GievItem 으로 스택된 경우
if (lpMaterial->IsSet(Item::DetailData::STACKABLE) && 0 == lpMaterial->GetNumOrDurability())
{
DELETE_ITEM(lpMaterial);
}
return true;
}
CCell::ItemInfo itemInfo;
GetCellPos().m_lpCell->SetItem(GetCurrentPos(), lpMaterial, 0, 0, CCell::NONE, itemInfo);
}
return true;
}
unsigned short CSiegeArms::GetRepairMaterialNum()
{
unsigned long dwHp = m_CreatureStatus.m_StatusInfo.m_nMaxHP - m_CreatureStatus.m_nNowHP;
unsigned long dwPerHp = m_CreatureStatus.m_StatusInfo.m_nMaxHP/Siege::SIEGE_ARMS_REPAIR_HP_PER_MATERIAL;
unsigned short wNum = static_cast<unsigned short>(dwHp / dwPerHp);
if((dwHp%dwPerHp)!=0)
{
wNum++;
}
return wNum;
}
bool CSiegeArms::Build(unsigned char cUpgradeStep)
{
m_cState = Siege::COMPLETE;
m_cUpgradeStep = cUpgradeStep;
UpdateObjectInfo();
// 공성 병기가 있는 반경 5셀 이내에 전송
PktSiegeArmsCmd pktSAC;
pktSAC.m_dwCID = GetOwnerID();
pktSAC.m_dwArmsID = m_dwCID;
pktSAC.m_cState = m_cState;
pktSAC.m_dwValue = m_cUpgradeStep;
pktSAC.m_cSubCmd = PktSiegeArmsCmd::SIEGE_CREATE_ARMS_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktSAC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
}
return true;
}
bool CSiegeArms::Repair(unsigned short wRepairHP)
{
m_cState = Siege::COMPLETE;
UpdateObjectInfo(Siege::REPAIR_HP, wRepairHP);
// 공성 병기가 있는 반경 5셀 이내에 전송
PktSiegeArmsCmd pktSAC;
pktSAC.m_dwCID = GetOwnerID();
pktSAC.m_dwArmsID = m_dwCID;
pktSAC.m_cState = m_cState;
pktSAC.m_dwValue = m_CreatureStatus.m_nNowHP;
pktSAC.m_cSubCmd = PktSiegeArmsCmd::SIEGE_REPAIR_ARMS_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktSAC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
}
return true;
}
bool CSiegeArms::Destroy(unsigned char cOffencerNation, bool bTakeGold)
{
m_cState = Siege::DESTROYED;
m_cUpgradeStep = 0;
m_CreatureStatus.m_nNowHP = 0;
// 공성 병기가 있는 반경 5셀 이내에 전송
PktSiegeArmsCmd pktSAC;
pktSAC.m_dwCID = GetOwnerID();
pktSAC.m_dwArmsID = m_dwCID;
pktSAC.m_cState = m_cState;
pktSAC.m_dwValue = m_cUpgradeStep;
pktSAC.m_cSubCmd = PktSiegeArmsCmd::SIEGE_DESTROY_ARMS;
// CreatureManager 에서 삭제
CCreatureManager::GetInstance().DeleteCreature(m_dwCID);
char* szPacket = reinterpret_cast<char *>(&pktSAC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
}
return true;
}
bool CSiegeArms::Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwNoValue, unsigned char cSubCmd)
{
m_cState = cState;
UpdateObjectInfo();
// 공성 병기가 있는 반경 5셀 이내에 전송
PktSiegeArmsCmd pktSAC;
pktSAC.m_dwCID = GetOwnerID();
pktSAC.m_dwArmsID = m_dwCID;
pktSAC.m_cState = m_cState;
pktSAC.m_dwValue = dwValue1;
pktSAC.m_cSubCmd = cSubCmd;
char* szPacket = reinterpret_cast<char *>(&pktSAC);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd, 0, 0))
{
// Vincent - 브로드 캐스트 테스트 코드
//SendToRadiusCell(szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
SendToRange(Siege::BROADCAST_RADIUS, szPacket, sizeof(PktSiegeArmsCmd), CmdSiegeArmsCmd);
}
return true;
}
bool CSiegeArms::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 (lpDBAgentDispatch)
{
GameClientSendPacket::SendCharSiegeArmsCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), dwCID, m_dwCID,
0, PktSiegeArmsCmd::SIEGE_RIDE_ARMS, PktBase::NO_SERVER_ERR);
}
// 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 CSiegeArms::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());
// 공성병기에서 내렸다는 정보를 DBAgent 서버에 알려준다.
if (0 != lpDBAgentDispatch)
{
CSendStream& AgentSendStream = lpDBAgentDispatch->GetSendStream();
GameClientSendPacket::SendCharSiegeArmsCmdToDBAgent(AgentSendStream, dwCID, m_dwCID,
0, PktSiegeArmsCmd::SIEGE_GETOFF_ARMS, PktBase::NO_SERVER_ERR);
}
// 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;
}

View File

@@ -0,0 +1,48 @@
#ifndef _BASE_SIEGE_ARMS_OBJECT_H_
#define _BASE_SIEGE_ARMS_OBJECT_H_
#pragma once
#include <Creature/Siege/SiegeObject.h>
#include <Creature/Siege/SiegeConstants.h>
using namespace Siege;
class CSiegeArms : public CSiegeObject
{
public:
virtual ~CSiegeArms();
// 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 Build(unsigned char cUpgradeStep = 0);
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 dwNoValue, unsigned char cSubCmd);
// 스타터킷 아이템으로 전환 (bMaterial 이 true 이면 자재의 50% 를 돌려준다.)
bool ToStarterKit(bool bMaterial = false);
bool GiveBackSiegeMaterial(); // 공성 병기 자재 돌려주기
unsigned short GetRepairMaterialNum(); // 수리시 필요한 자재수
// Rider 관련 정보
virtual bool Ride(unsigned long dwCID); // 병기 탑승
virtual bool GetOff(unsigned long dwCID); // 병기 내림 (중계 서버로 패킷 전송 : 사용안할시 파괴를 위해)
protected:
CSiegeArms(MonsterCreateInfo& MonsterCreate, unsigned long dwOwnerID, unsigned char cNation,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState, unsigned char cUpgradeStep);
friend class CSiegeObjectMgr;
};
#endif _BASE_SIEGE_ARMS_OBJECT_H_

View File

@@ -0,0 +1,313 @@
#ifndef _SIEGE_CONSTANTS_H_
#define _SIEGE_CONSTANTS_H_
namespace Siege
{
enum SiegeObjectType
{
// 몬스터 Kind ID 를 사용한다.
EMBLEM = 5000, // 성 상징물 ( DESTORYING, CHANGING, DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
GATE = 5324, // 성문 ( DESTORYING, CHANGING, DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
BACKDOOR = 5378, // 뒷문 ( Only COMPLETE 상태만 존재 )
CAMP = 5379, // 길드 요새 (캠프) ( DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
CASTLE_ARMS_NPC = 5433, // 병기 관리 NPC ( Only COMPLETE 상태만 존재 )
GUARD = 5434, // 가드 ( DESTORYING, CHANGING, DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
SHORT_RANGE_CASTLE_ARMS = 5488, // 근거리 수성 병기 ( DESTORYING, CHANGING, DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
LONG_RANGE_CASTLE_ARMS = 5542, // 원거리 수성 병기 ( DESTORYING, CHANGING, DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
SHORT_RANGE_SIEGE_ARMS = 5596, // 근거릭 공성 병기 ( CHANGING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
LONG_RANGE_SIEGE_ARMS = 5650, // 원거리 공성 병기 ( CHANGING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
AIRSHIP = 5704, // 비공정 ( CHANGING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
MINING_CAMP = 5758, // 채굴기 (캠프) ( DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
CAMP_SHOP = 5812, // 상점타입요새 (캠프) ( DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
KARTERANT_WEAPON = 5866, // 카르테란트 월드 웨폰 (캠프) ( DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
MERKADIA_WEAPON = 5920, // 메르카디아 월드 웨폰 (캠프) ( DEVELOPING, CANCELING, RESTORING, DESTROYED 1개만 존재 )
DEFAULT_TYPE = 0 // 디폴트 전달인자용
};
enum State
{
// KID => SiegeObjectType
// + (State * UPGRADE_KIND_NUM)
// + UpgradeStep
// KID (성 상징물) => SiegeObjectType
// + (State * JEWEL_TYPE_NUM * UPGRADE_KIND_NUM)
// + (JewelType * UPGRADE_KIND_NUM)
// + UpgradeStep
COMPLETE = 0, // 성 상징물 소환 완료, 길드 요새 구축 완료, 가드, 경비병, 근거리, 원거리, 비공정 개발 완료
UPGRADING = 1, // 성 상징물, 길드 요새, 성문, 가드, 근거리/원거리 수성병기 업그레이드 중인 상태
REPARING = 2, // 길드 요새, 성문, 근거리/원거리, 비공정 수리 상태
DESTROYING = 3, // 길드 요새, 근거리/원거리, 비공정 파괴해서 스타트 킷으로 돌리는 상태
CHANGING = 4, // 길드 요새 변형중
DEVELOPING = 5, // 성 상징물 소환중, 길드 요새 구축중, 가드, 경비병, 근거리, 원거리, 비공정 개발중
CANCELING = 6, // 길드 요새, 근거리/원거리, 비공정 개발을 취소중인 상태 (스타트킷 아이템으로 돌아간다.)
RESTORING = 7, // 성문 복구 상태
DESTROYED = 8, // 성문 파괴 상태
// SubState
NONE = 0,
MINE = 1, // 아군 성 상징물
ENEMY = 2, // 적군 성 상징물
OPENED = 1, // 성문 열린 상태
CLOSED = 2, // 성문 닫힌 상태
MINING_OFF = 0, // 채굴기 정지 상태
MINING_ON = 1, // 채굴기 가동 상태
MINING_READY = 2, // 채굴기 가동 준비 상태
WEAPON_EMPTY = 0, // 월드웨폰 무기 비장전 상태
WEAPON_CHARGE = 1, // 무기 장전중
WEAPON_READY = 2, // 무기 장전된 상태
WEAPON_FIRE = 3, // 무기 발사중
};
enum JewelType
{
// 보석 순서가 바뀌면 안됨!!
// 아이템 스크립트의 순서대로이다.
NO_JEWEL = 0, // 보석 없음
RUBY = 1, // 루비
EMERALD = 2, // 에메랄드
SAPPHIRE = 3, // 사파이어
DIAMOND = 4, // 다이아몬드
BLACKMOON = 5, // 블랙문
JEWEL_PROTOTYPE_ID = 1900,
JEWEL_KIND = 5,
JEWEL_TYPE_NUM = 6, // 상징물 업그레이트 보석 타입 종류수 (없음 포함)
};
enum CampConst
{
CAMP_BUILDING_RADIUS = 315, // 캠프 구축 반경 (m)
CAMP_REPAIR_GOLD_PER_HP = 10, // HP 단위당 10 골드
CAMP_UPGRADE_MATERIAL_NUM = 30, // 길드 요새 한단계 업그레이드시 필요한 자재수
MINING_CAMP_UPGRADE_MATERIAL_NUM = 50, // 채굴기 한단계 업그레이드시 필요한 자재수
CAMP_SHOP_UPGRADE_MATERIAL_NUM = 40, // 길드 요새 상점 한단계 업그레이드시 필요한 자재수
WEAPON_CHARGE_MATERIAL_NUM = 50, // 월드 웨폰 무기 장전에 필요한 자재수
CAMP_REPAIR_HP_PER_MATERIAL = 5000, // 자재 하나당 수리되는 HP
FAME_FOR_CAMP_BUILDING_TEST = 0,//1000, // 길드 요새 구축시 길드의 필요 명성치 (테섭용)
FAME_FOR_CAMP_BUILDING = 0,//2000, // 길드 요새 구축시 길드의 필요 명성치 (정섭용)
FAME_FOR_WEAPON_BUILDING_TEST = 1000, // 월드 웨폰 구축시 길드의 필요 명성치 (테섭용)
FAME_FOR_WEAPON_BUILDING = 2000, // 월드 웨폰 구축시 길드의 필요 명성치 (정섭용)
FAME_FOR_SELF_DESTROY_CAMP = -50, // 길드 요새 파괴시 깍이는 명성치 (자신이 파괴)
FAME_FOR_DESTROYED_CAMP = -100, // 길드 요새 파괴시 깍이는 명성치 (다른 사람이 파괴)
GOLD_FOR_SELF_DESTROY_CAMP = 500000, // 길드 요새 파괴시 돌려주는 돈 (자신이 파괴)
GOLD_FOR_CANCEL_CAMP = 1000000, // 길드 요새 구축 해제시 돌려주는 돈
CAMP_ATTACK_RANGE = 45, // 요새 공격 범위 (반경 45m)
// 시간 업데이트 타입
TYPE_REMAIN_TIME = 0, // 시간 변수 업데이트
TYPE_LAST_USE_TIME = 1, // 마지막 사용 시간 업데이트
// 채굴 아이템 타입 종류
ACCUMULATED_MINERAL = 1, // 누적된 광물 아이템
TEMPORARY_MINERAL = 2, // 임시 보관된 광물 아이템
MAX_MINERAL_NUM = 1000, // 광물 최고 스택 갯수
// 상점 타입 관련
MAX_TAX = 100, // 최대 세율
// 월드 웨폰 관련
WEAPON_FIRE_WARNING_COUNT = 10, // 월드 웨폰 발사 경고 표시 횟수 (10번 : 10초)
WEAPON_REBUILD_SIEGE_TIME_COUNT = 3, // 공성 시간이 3번 지나야 다시 지을수 있음 (부서진 국가일 경우)
WEAPON_DAMAGE_RANGE = 256, // 월드 웨폰 데미지 반경 (256m)
WEAPON_DAMAGE = 10000 // 월드 웨폰 절대 데미지
};
enum EmblemConst
{
MAX_EMBLEM_UPGRADE = 3, // 성 상징물 업그레이드 최대치
// TODO : 상위 보석 2종류가 추가되면 Const::MAX_UPGRADE_NUM 를 사용한다.
EMBLEM_ATTACK_HEIGHT_ERROR = 5, // 높이차가 최소 5m 안에 있는 적만 공격이 가능
};
enum GateConst
{
GATE_REPAIR_MIN_PER_HP = 1, // HP 1당 1분의 수리 시간
};
enum CastleArmsConst
{
CASTLE_ARMS_REPAIR_TIME = 1, // 수성 병기 수리 시간 (1분)
CASTLE_ARMS_REPAIR_GOLD_PER_UNIT = 10000, // 수리 비용 단위당 10000 골드 ( 단위는 10%당 )
FAME_FOR_DESTROY_CASTLE_ARMS = -500, // 수성 병기를 NPC 로 변형할때 깍이는 명성치
};
enum SiegeArmsConst
{
SIEGE_ARMS_REPAIR_TIME = 1, // 공성 병기 수리 시간 (1분).
SIEGE_ARMS_UPGRADE_MATERIAL_NUM = 10, // 공성 병기 한단계 업그레이드시 필요한 자재수
SIEGE_ARMS_REPAIR_HP_PER_MATERIAL = 10, // 자재 하나당 10% 의 HP 수리
AIRSHIP_RIDER_NUM = 10, // 수송선 최대 탑승 인원
AIRSHIP_PASSENGER_NUM = 9, // 수송선 승객 최대 인원
};
enum Const
{
INSIDE = 0, // 뒷문 사용( 안으로 )
OUTSIDE = 1, // 뒷문 사용( 밖으로 )
MAX_UPGRADE_NUM = 5, // 공성 오브젝트 업그레이드 최대치
UPGRADE_KIND_NUM = 6, // 업그레이트 종류 수 (0 ~ 5)
VIRTUAL_CID_START_NUM = 1000, // 길드 요새, 공성 병기의 가상 CID 인덱스 시작 번호
BROADCAST_CELL_SIZE = 5, // 브로드캐스트 반경 (5셀)
BROADCAST_RADIUS = 150, // 브로드캐스트 반경 (150m)
BROADCAST_SQUARED_RADIUS = 22500, // 브로드캐스트 반경 제곱값 (150 x 150)
MAX_HP_UPDATE_COUNT = 5, // 5 번에 한번은 DB 중계서버로 HP 정보 갱신
MAX_REPAIR_GOLD = 99999999, // 수리를 할수 없는 객체를 수리할때 드는 디폴트 비용 (에러나게 하기위해)
MAX_SIEGE_OBJECT = 5,
CREATE_LEVEL_LIMIT = 80
};
enum RiderValue
{
NOT_RIDER = 0, // 탑승하지 않았음
RIDER_FOR_OWNER = 1, // 주인으로 탑승한 상태
RIDER_FOR_PASSENGER = 2 // 승객으로 탑승한 상태
};
enum TimeValue
{
/*
// Test : 길드 요새 테스트용 시간
DEFAULT_TIME_VALUE = 1, // 1 분
DEFAULT_REPAIR_TIME = 30, // 30 초
CAMP_ENCAMPING_TIME = 1, // 1 분
CAMP_CANCELING_TIME = 1, // 1 분
CAMP_REPAIR_TIME = 30, // 30 초
CAMP_TO_STARTKIT_TIME = 1, // 1 분
CAMP_CHANGING_TIME = 1, // 1 분
CAMP_LEAST_USE_TIME = 7, // 7 일
CAMP_ENCAMPING_INTERVAL = 10, // 10 분 간격으로 구축중 메세지 보냄
CAMP_ATTACKED_INTERVAL = 60000, // 60000 ms (1분)
MINING_CAMP_GAIN_COUNT = 1, // 1 회
CAMP_SHOP_TRANSFER_COUNT = 1, // 1 회
WEAPON_CHARGE_TIME = 1, // 1 분
*/
DEFAULT_TIME_VALUE = 10, // 10 분
DEFAULT_REPAIR_TIME = 30, // 30 초
CAMP_ENCAMPING_TIME = 30, // 30 분
CAMP_CANCELING_TIME = 20, // 20 분
CAMP_REPAIR_TIME = 30, // 30 초
CAMP_TO_STARTKIT_TIME = 10, // 10 분
CAMP_CHANGING_TIME = 10, // 10 분
CAMP_LEAST_USE_TIME = 7, // 7 일
CAMP_ENCAMPING_INTERVAL = 10, // 10 분 간격으로 구축중 메세지 보냄
CAMP_ATTACKED_INTERVAL = 60000, // 60000 ms (1분)
MINING_CAMP_GAIN_COUNT = 1, // 1 회
CAMP_SHOP_TRANSFER_COUNT = 1, // 1 회
WEAPON_CHARGE_TIME = 10, // 10 분
EMBLEM_SUMMON_TIME = 1, // 1 분
EMBLEM_ATTACKED_INTERVAL = 60000, // 60000 ms (1분)
SIEGE_ARMS_BUILDING_TIME = 3, // 3 분
SIEGE_ARMS_TO_STARTKIT_TIME = 3, // 1 분
SIEGE_ARMS_LEAST_USE_TIME = 10, // 10 분
CASTLE_ARMS_LEAST_USE_TIME = 10, // 10 분
};
enum eReturn
{
RET_OK = 0,
RET_DESTROY_CAMP = 1, // 길드 요새 객체 삭제
RET_DESTROY_SIEGE_ARMS = 2, // 공성 병기 객체 삭제
RET_CHANGE_TYPE = 3, // 길드 요새 타입 변형
};
enum eUpdateHPType
{
NOW_HP = 0, // 현재 HP 그대로
UPGRADE_HP = 1, // 업그레이드된 HP 로
REPAIR_HP = 2, // 수리된 HP 로
FULL_HP = 3, // Full HP 로
};
static int GetKID(unsigned short wObjectType, unsigned char cState, unsigned char cUpgradeType, unsigned char cUpgradeStep)
{
int nKID = 0;
if (Siege::EMBLEM == wObjectType)
{
nKID = wObjectType
+ (cState * Siege::JEWEL_TYPE_NUM * Siege::UPGRADE_KIND_NUM)
+ (cUpgradeType * Siege::UPGRADE_KIND_NUM)
+ cUpgradeStep;
}
else
{
nKID = wObjectType
+ (cState * Siege::UPGRADE_KIND_NUM)
+ cUpgradeStep;
}
return nKID;
}
static int GetUpgradeMaterialNum(unsigned short wObjectType)
{
int nNum = 0;
switch (wObjectType)
{
case Siege::MINING_CAMP:
nNum = Siege::MINING_CAMP_UPGRADE_MATERIAL_NUM;
break;
case Siege::CAMP_SHOP:
nNum = Siege::CAMP_SHOP_UPGRADE_MATERIAL_NUM;
break;
case Siege::CAMP:
case Siege::KARTERANT_WEAPON:
case Siege::MERKADIA_WEAPON:
nNum = Siege::CAMP_UPGRADE_MATERIAL_NUM;
break;
}
return nNum;
}
static int GetChangingTypeMaterialNum(unsigned short wBeforeType, unsigned short wAfterType, unsigned char cUpgradeStep)
{
int nBeforeNum = GetUpgradeMaterialNum(wBeforeType);
int nAfterNum = GetUpgradeMaterialNum(wAfterType);
if (nBeforeNum < nAfterNum)
{
return ( (nAfterNum - nBeforeNum) * cUpgradeStep );
}
return 0;
}
}
#endif // _SIEGE_CONSTANTS_H_

View File

@@ -0,0 +1,946 @@
#include "stdafx.h"
#include "SiegeObject.h"
#include <Utility/Math/Math.h>
#include <Utility/Setup/ServerSetup.h>
#include <Item/ItemFactory.h>
#include <Network/ClientSocket/ClientConstants.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/Packet/WrapPacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.h>
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Creature/Monster/MonsterMgr.h>
#include <Creature/EnemyCheck.h>
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
#include <Map/FieldMap/CellManager.h>
#include <Map/DuelMap/DuelCellManager.h>
#include <Skill/SkillTable.h>
#include <Skill/SkillMgr.h>
#include <GameTime/GameTimeConstants.h>
#include <GameTime/GameTimeMgr.h>
CSiegeObject::CSiegeObject(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject)
: CSkillMonster(MonsterCreate), m_dwCampOrCastleID(CastleObject.m_dwCastleID), m_dwOwnerID(CastleObject.m_dwOwnerID),
m_cNation(CastleObject.m_cNation), m_dwGID(0), m_wObjectType(CastleObject.m_wObjectType), m_cState(CastleObject.m_cState),
m_cSubState(CastleObject.m_cSubState), m_cUpgradeStep(CastleObject.m_cUpgradeStep), m_cUpgradeType(CastleObject.m_cUpgradeType),
m_cMaterial(0), m_cSiegeCount(0), m_fDefaultDir(CastleObject.m_fDefaultDir), m_dwRiderCID(0), m_cHPUpdateCount(0),
m_dwBroadCastSize(0), m_dwDeltaSize(0)
{
InitMonster(MonsterCreate.m_Pos);
m_MotionInfo.m_fDirection = m_fDefaultDir;
m_CreatureStatus.m_nNowHP = static_cast<unsigned short>(CastleObject.m_dwHP);
switch (m_wObjectType)
{
case Siege::GUARD: m_wDefaultSearchRange = 32; break;
case Siege::EMBLEM: m_wDefaultSearchRange = 32; break;
}
// 브로드캐스팅 데이터 준비
m_SerializeSiegeObjectData.PrepareData( *this );
m_SerializeSiegeObjectData.ClearDeltaData();
}
CSiegeObject::CSiegeObject(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, bool bFullHP)
: CSkillMonster(MonsterCreate), m_dwCampOrCastleID(dwCampID), m_dwOwnerID(0), m_wObjectType(wObjectType),
m_dwGID(dwGID), m_cNation(0), m_cState(cState), m_cSubState(cSubState), m_cUpgradeStep(cUpgradeStep),
m_cUpgradeType(0), m_cMaterial(cMaterial), m_cSiegeCount(cSiegeCount), m_fDefaultDir(0), m_dwRiderCID(0),
m_cHPUpdateCount(0), m_dwBroadCastSize(0),m_dwDeltaSize(0)
{
InitMonster(MonsterCreate.m_Pos);
if (bFullHP) m_CreatureStatus.m_nNowHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
else m_CreatureStatus.m_nNowHP = static_cast<unsigned short>(dwHP);
// 브로드캐스팅 데이터 준비
m_SerializeSiegeObjectData.PrepareData( *this );
m_SerializeSiegeObjectData.ClearDeltaData();
}
CSiegeObject::CSiegeObject(MonsterCreateInfo& MonsterCreate, unsigned long dwOwnerID, unsigned char cNation,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState, unsigned char cUpgradeStep)
: CSkillMonster(MonsterCreate), m_dwCampOrCastleID(0), m_dwOwnerID(dwOwnerID), m_wObjectType(wObjectType), m_cNation(cNation),
m_dwGID(0), m_cState(cState), m_cSubState(0), m_cUpgradeStep(cUpgradeStep), m_cUpgradeType(0), m_cMaterial(0),
m_cSiegeCount(0), m_fDefaultDir(0), m_dwRiderCID(0), m_cHPUpdateCount(0), m_dwBroadCastSize(0),m_dwDeltaSize(0)
{
InitMonster(MonsterCreate.m_Pos);
if (0 == dwHP) m_CreatureStatus.m_nNowHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
else m_CreatureStatus.m_nNowHP = static_cast<unsigned short>(dwHP);
// 브로드캐스팅 데이터 준비
m_SerializeSiegeObjectData.PrepareData( *this );
m_SerializeSiegeObjectData.ClearDeltaData();
}
CSiegeObject::~CSiegeObject()
{
}
EnemyCheck::EnemyType CSiegeObject::IsEnemy(CCreature* lpTarget, unsigned char* cResult)
{
if (NULL == lpTarget)
{
ERRLOG1(g_Log, "CID:0x%08x 피아식별할 타겟이 없습니다.", m_dwCID);
return EnemyCheck::EC_NEUTRAL;
}
BattleInclination::CharData ownerInfo;
BattleInclination::CharData targetInfo;
BattleInclination::SetCharData(*this, ownerInfo);
BattleInclination::SetCharData(*lpTarget, targetInfo);
BattleInclination::RelationData relationInfo;
BattleInclination::SetRelationData(*this, *lpTarget, relationInfo);
unsigned char cResultFromStruct = 0;
unsigned long dwResult = EnemyCheck::CCheckTable::GetInstance().IsEnemyFromStruct(
ownerInfo, targetInfo, relationInfo, cResultFromStruct);
return static_cast<EnemyCheck::EnemyType>(dwResult);
}
unsigned char CSiegeObject::IsRider(unsigned long dwCID) const
{
if (m_dwRiderCID == dwCID)
{
return Siege::RIDER_FOR_OWNER;
}
return Siege::NOT_RIDER;
}
bool CSiegeObject::SkillAttack()
{
unsigned char cSkillLockCount = m_cUpgradeStep + 1;
unsigned char cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, MAX_PATTERN = 2 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 조건 대상 행동
// A B A : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// A B D : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
void CSiegeObject::UpdateObjectInfo(unsigned char cHPType, unsigned short wRepairHP)
{
unsigned short wPrevMaxHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
unsigned short wNowHP = m_CreatureStatus.m_nNowHP;
int nKID = Siege::GetKID(m_wObjectType, m_cState, m_cUpgradeType, m_cUpgradeStep);
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoType)
{
m_CreatureStatus = lpProtoType->m_CreatureStatus;
m_MonsterInfo = lpProtoType->m_MonsterInfo;
m_CreatureStatus.m_StatusInfo.CalculateSubStatus();
}
unsigned long dwTempHp;
switch (cHPType)
{
case Siege::NOW_HP:
m_CreatureStatus.m_nNowHP = wNowHP;
SendHPUpdateToDBAgent();
break;
case Siege::UPGRADE_HP:
dwTempHp = wNowHP + m_CreatureStatus.m_StatusInfo.m_nMaxHP - wPrevMaxHP;
m_CreatureStatus.m_nNowHP = static_cast<unsigned short>(
std::min(dwTempHp, static_cast<unsigned long>(m_CreatureStatus.m_StatusInfo.m_nMaxHP)));
SendHPUpdateToDBAgent(true);
break;
case Siege::REPAIR_HP:
dwTempHp = wNowHP + wRepairHP;
m_CreatureStatus.m_nNowHP = static_cast<unsigned short>(
std::min(dwTempHp, static_cast<unsigned long>(m_CreatureStatus.m_StatusInfo.m_nMaxHP)));
SendHPUpdateToDBAgent(true);
break;
case Siege::FULL_HP:
m_CreatureStatus.m_nNowHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
SendHPUpdateToDBAgent(true);
break;
}
}
void CSiegeObject::UpgradeByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep)
{
// 성 관련 오브젝트로 상징물의 업그레이드 효과를 받는 오브젝트만
if (!IsCastleObject())
{
return;
}
int nKID = Siege::GetKID(Siege::EMBLEM, Siege::COMPLETE, cUpgradeType, cUpgradeStep);
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoType)
{
// 업그레이드 속도 향상
m_MonsterInfo.m_cUpgradeSpeed -= static_cast<unsigned char>(m_MonsterInfo.m_cUpgradeSpeed
* lpProtoType->m_MonsterInfo.m_fUpgradeSpeedUp);
// 개발 속도 향상
m_MonsterInfo.m_cDevelopSpeed -= static_cast<unsigned char>(m_MonsterInfo.m_cDevelopSpeed
* lpProtoType->m_MonsterInfo.m_fDevelopSpeedUp);
// 방어력 향상
m_CreatureStatus.m_StatusInfo.m_wArmor += static_cast<short>(m_CreatureStatus.m_StatusInfo.m_wArmor
* lpProtoType->m_MonsterInfo.m_fDefenseUp);
m_CreatureStatus.m_StatusInfo.m_wMagicResist += static_cast<short>(m_CreatureStatus.m_StatusInfo.m_wMagicResist
* lpProtoType->m_MonsterInfo.m_fDefenseUp);
// HP 향상
unsigned short wAddHP = static_cast<unsigned short>(m_CreatureStatus.m_StatusInfo.m_nMaxHP
* lpProtoType->m_MonsterInfo.m_fHPUp);
m_CreatureStatus.m_StatusInfo.m_nMaxHP += wAddHP;
m_CreatureStatus.m_nNowHP += wAddHP;
// 공격력 향상
m_CreatureStatus.m_StatusInfo.m_lMinDamage += static_cast<long>(m_CreatureStatus.m_StatusInfo.m_lMinDamage
* lpProtoType->m_MonsterInfo.m_fOffenseUp);
m_CreatureStatus.m_StatusInfo.m_lMaxDamage += static_cast<long>(m_CreatureStatus.m_StatusInfo.m_lMaxDamage
* lpProtoType->m_MonsterInfo.m_fOffenseUp);
m_CreatureStatus.m_StatusInfo.m_wMagicPower += static_cast<short>(m_CreatureStatus.m_StatusInfo.m_wMagicPower
* lpProtoType->m_MonsterInfo.m_fOffenseUp);
// 생성 비용 절감
m_MonsterInfo.m_dwDevelopGold -= static_cast<unsigned long>(m_MonsterInfo.m_dwDevelopGold
* lpProtoType->m_MonsterInfo.m_fDevelopGoldDown);
// 업그레이드 비용 절감
m_MonsterInfo.m_dwUpgradeGold -= static_cast<unsigned long>(m_MonsterInfo.m_dwUpgradeGold
* lpProtoType->m_MonsterInfo.m_fUpgradeGoldDown);
}
}
void CSiegeObject::DegradeByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep)
{
// 성 관련 오브젝트로 상징물의 업그레이드 효과를 받는 오브젝트만
if (!IsCastleObject())
{
return;
}
int nKID = Siege::GetKID(Siege::EMBLEM, Siege::COMPLETE, cUpgradeType, cUpgradeStep);
const CMonsterMgr::MonsterProtoType* lpProtoTypeEmblem = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
nKID = GetKID();
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoType && lpProtoTypeEmblem)
{
unsigned short wNowHP = m_CreatureStatus.m_nNowHP -
static_cast<unsigned short>(m_CreatureStatus.m_nNowHP * lpProtoTypeEmblem->m_MonsterInfo.m_fHPUp);
m_CreatureStatus = lpProtoType->m_CreatureStatus;
m_MonsterInfo = lpProtoType->m_MonsterInfo;
m_CreatureStatus.m_nNowHP = wNowHP;
}
}
unsigned short CSiegeObject::GetDefaultNowHP()
{
// 성 관련 오브젝트로 상징물의 업그레이드 효과를 받는 오브젝트만
if (IsCastleObject())
{
Castle::CCastle* lpCastle = Castle::CCastleMgr::GetInstance().GetCastle( GetCastleID() );
CSiegeObject* lpEmblem = lpCastle->GetCastleEmblem();
if (lpCastle && lpEmblem)
{
int nKID = lpEmblem->GetKID();
const CMonsterMgr::MonsterProtoType* lpProtoTypeEmblem = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoTypeEmblem)
{
return m_CreatureStatus.m_nNowHP - static_cast<unsigned short>(m_CreatureStatus.m_nNowHP * lpProtoTypeEmblem->m_MonsterInfo.m_fHPUp);
}
}
}
return m_CreatureStatus.m_nNowHP;
}
void CSiegeObject::MoveTo(const Position& NewPosition)
{
CAggresiveCreature::MoveTo(NewPosition, false);
}
unsigned long CSiegeObject::GetRepairHP()
{
return m_CreatureStatus.m_StatusInfo.m_nMaxHP - m_CreatureStatus.m_nNowHP;
}
int CSiegeObject::GetKID() const
{
return Siege::GetKID(m_wObjectType, m_cState, m_cUpgradeType, m_cUpgradeStep);
}
void CSiegeObject::GetRiders( unsigned long* pRiders ) const
{
pRiders[0] = m_dwRiderCID;
}
unsigned char CSiegeObject::GetRiderNum() const
{
return (0 == m_dwRiderCID) ? 0 : 1;
}
unsigned long CSiegeObject::GetDevelopGold(unsigned short wDefaultObjectType) const
{
wDefaultObjectType = (wDefaultObjectType == Siege::DEFAULT_TYPE) ? m_wObjectType : wDefaultObjectType;
int nKID = Siege::GetKID(wDefaultObjectType, Siege::DEVELOPING, m_cUpgradeType, m_cUpgradeStep);
const CMonsterMgr::MonsterProtoType* pProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if ( pProtoType )
{
return pProtoType->m_MonsterInfo.m_dwDevelopGold;
}
ERRLOG1(g_Log, "개발 비용 오류 ( KID : %d )", nKID);
return 0;
}
unsigned char CSiegeObject::GetDevelopSpeed(unsigned short wDefaultObjectType) const
{
wDefaultObjectType = (wDefaultObjectType == Siege::DEFAULT_TYPE) ? m_wObjectType : wDefaultObjectType;
int nKID = Siege::GetKID(wDefaultObjectType, Siege::DEVELOPING, m_cUpgradeType, m_cUpgradeStep);
const CMonsterMgr::MonsterProtoType* pProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if ( pProtoType )
{
return pProtoType->m_MonsterInfo.m_cDevelopSpeed;
}
ERRLOG1(g_Log, "개발 시간 오류 ( KID : %d )", nKID);
return Siege::DEFAULT_TIME_VALUE;
}
unsigned long CSiegeObject::GetUpgradeGold() const
{
int nKID = Siege::GetKID(m_wObjectType, Siege::UPGRADING, m_cUpgradeType, m_cUpgradeStep);
if ( m_cUpgradeStep < Siege::MAX_UPGRADE_NUM )
{
const CMonsterMgr::MonsterProtoType* pProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if ( pProtoType )
{
return pProtoType->m_MonsterInfo.m_dwUpgradeGold;
}
}
ERRLOG1(g_Log, "업그레이드 비용 오류 ( KID : %d )", nKID);
return 0;
}
unsigned char CSiegeObject::GetUpgradeSpeed(unsigned char cDefaultUpgradeType) const
{
cDefaultUpgradeType = (cDefaultUpgradeType == Siege::NO_JEWEL) ? m_cUpgradeType : cDefaultUpgradeType;
int nKID = Siege::GetKID(m_wObjectType, Siege::UPGRADING, cDefaultUpgradeType, m_cUpgradeStep);
if ( m_cUpgradeStep < Siege::MAX_UPGRADE_NUM )
{
const CMonsterMgr::MonsterProtoType* pProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if ( pProtoType )
{
return pProtoType->m_MonsterInfo.m_cUpgradeSpeed;
}
}
ERRLOG1(g_Log, "업그레이드 시간 오류 ( KID : %d )", nKID);
return Siege::DEFAULT_TIME_VALUE;
}
float CSiegeObject::GetBonusRate() const
{
return m_MonsterInfo.m_fBonusRate;
}
unsigned long CSiegeObject::GetGID() const
{
// 병기에 타고 있는 캐릭터가 있으면, 병기에 탑승한 캐릭터의 GetGID() 호출
if ( IsRidable() && 0 != m_dwRiderCID )
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if (lpRider)
{
return lpRider->GetGID();
}
}
return m_dwGID;
}
unsigned char CSiegeObject::GetNation() const
{
// 병기에 타고 있는 캐릭터가 있으면, 병기에 탑승한 캐릭터의 GetNation() 호출
if ( IsRidable() && 0 != m_dwRiderCID )
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if (lpRider)
{
return lpRider->GetNation();
}
}
if ( IsCastleObject() || IsSiegeArms() )
{
return m_cNation;
}
else
{
// 해당 Guild 의 GetNation() 호출
CGuild* lpGuild = CGuildMgr::GetInstance().GetGuild(m_dwGID);
if (NULL != lpGuild)
{
return lpGuild->GetNation();
}
}
// 성주가 없는 공성 병기에 대해서 무국적을 리턴한다.
return Creature::STATELESS;
}
bool CSiegeObject::IsPeaceMode()
{
// 병기에 타고 있는 캐릭터가 있으면, 병기에 탑승한 캐릭터의 GetNation() 호출
if ( IsRidable() && 0 != m_dwRiderCID )
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if (lpRider)
{
return lpRider->IsPeaceMode();
}
}
if ( IsCastleObject() || IsSiegeArms() )
{
return false;
}
else
{
// 해당 Guild 의 GetNation() 호출
CGuild* lpGuild = CGuildMgr::GetInstance().GetGuild(m_dwGID);
if (NULL != lpGuild)
{
return lpGuild->IsPeaceMode();
}
}
// 성주가 없는 공성 병기에 대해서 전쟁모드
return false;
}
CAggresiveCreature* CSiegeObject::GetDuelOpponent(void) const
{
// 병기에 타고 있는 캐릭터가 있으면, 병기에 탑승한 캐릭터의 GetNation() 호출
if ( IsRidable() && 0 != m_dwRiderCID )
{
CCharacter* lpRider = CCreatureManager::GetInstance().GetCharacter(m_dwRiderCID);
if (lpRider)
{
return lpRider->GetDuelOpponent();
}
}
return NULL;
}
bool CSiegeObject::IsWorldWeapon() const
{
if (Siege::KARTERANT_WEAPON == m_wObjectType || Siege::MERKADIA_WEAPON == m_wObjectType)
{
return true;
}
return false;
}
/*
bool CSiegeObject::SerializeOut(char* szBuffer_In, unsigned short& wDataLen)
{
PktSiegeBroadCast* lpBroadCast = reinterpret_cast<PktSiegeBroadCast*>(szBuffer_In);
lpBroadCast->m_dwCID = m_dwCID;
lpBroadCast->m_dwOwnerID = m_dwOwnerID; // 성, 길드 요새, 소유캐릭터 ID
lpBroadCast->m_dwGID = m_dwGID; // 길드 ID
if (IsEmblem() && m_dwEnemyGID)
{
lpBroadCast->m_dwGID = m_dwEnemyGID; // 길드 ID
}
lpBroadCast->m_wObjectType = m_wObjectType;
lpBroadCast->m_dwNowHP = m_CreatureStatus.m_nNowHP;
lpBroadCast->m_dwMaxHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
lpBroadCast->m_cState = m_cState;
lpBroadCast->m_cSubState = m_cSubState;
lpBroadCast->m_cUpgradeStep = m_cUpgradeStep;
lpBroadCast->m_cUpgradeType = m_cUpgradeType;
lpBroadCast->m_fDefaultDir = m_fDefaultDir;
std::fill_n(&lpBroadCast->m_dwRiderID[0], int(Siege::AIRSHIP_RIDER_NUM), 0);
GetRiders( lpBroadCast->m_dwRiderID );
float fY = GetCurrentPos().m_fPointY;
const MotionInfo& motionInfo = GetMotionInfo();
if (IsSiegeArms())
{
if (Siege::AIRSHIP != m_wObjectType)
{
fY = 0.0f;
}
}
lpBroadCast->m_NetworkPos.Initialize(GetCurrentPos().m_fPointX, fY, GetCurrentPos().m_fPointZ, motionInfo.m_fDirection,
(0 == motionInfo.m_dwFrame) ? 0.0f : motionInfo.m_fVelocity / motionInfo.m_dwFrame);
wDataLen = sizeof(PktSiegeBroadCast);
return true;
}
*/
/*
void CSiegeObject::SendToRadiusCell(const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In)
{
// 반경 5셀에 패킷 전송
CCell* lpCenterCell = GetCellPos().m_lpCell;
if (lpCenterCell)
{
int nCellWidth = Siege::BROADCAST_CELL_SIZE * 2 - 1;
int nCellHeight = Siege::BROADCAST_CELL_SIZE * 2 - 1;
int i, j;
CCell* lpStartCell = NULL;
for (i=1; i<Siege::BROADCAST_CELL_SIZE; ++i)
{
lpStartCell = lpCenterCell->GetConnectCell(CCell::LEFT);
if (NULL == lpStartCell)
{
nCellWidth = nCellWidth - (Siege::BROADCAST_CELL_SIZE - i);
break;
}
lpCenterCell = lpStartCell;
}
for (i=1; i<Siege::BROADCAST_CELL_SIZE; ++i)
{
lpStartCell = lpCenterCell->GetConnectCell(CCell::UP);
if (NULL == lpStartCell)
{
nCellHeight = nCellHeight - (Siege::BROADCAST_CELL_SIZE - i);
break;
}
lpCenterCell = lpStartCell;
}
lpStartCell = lpCenterCell;
for (i=0; i<nCellHeight; ++i)
{
CCell* lpTempCell = lpStartCell;
for (j=0; j<nCellWidth; ++j)
{
if (lpTempCell)
{
lpTempCell->SendAllCharacter(szPacket, dwPacketSize, cCMD_In);
lpTempCell = lpTempCell->GetConnectCell(CCell::RIGHT);
}
else break;
}
lpStartCell = lpStartCell->GetConnectCell(CCell::DOWN);
if (NULL == lpStartCell) break;
}
}
}
*/
void CSiegeObject::RangeTest()
{
// 브로드캐스트 반경을 벗어난 캐릭터들을 제거한다. (제거하라고 알려준다.)
PktNewSiegeBroadCast pktNSBC;
ZeroMemory(&pktNSBC, sizeof(PktNewSiegeBroadCast));
pktNSBC.m_dwCID = GetCID();
pktNSBC.m_cType = BroadCastSiege::BROADCAST_DELETE_DATA;
bool bDeletePacketCrypt = true;
char* szPacket = reinterpret_cast<char *>(&pktNSBC);
if (!PacketWrap::WrapCrypt(szPacket, sizeof(PktNewSiegeBroadCast), CmdNewSiegeBroadCast, 0, 0))
{
ERRLOG0(g_Log, "Siege Broadcast delete data packet crypt fail..");
bDeletePacketCrypt = false;
}
BroadCastSet::iterator itr = m_BroadCastSet.begin();
while (itr != m_BroadCastSet.end())
{
CCharacter* lpCharacter = CCreatureManager::GetInstance().GetCharacter( (*itr) );
if (lpCharacter)
{
if (lpCharacter->GetCurrentPos().GetSquaredDistance( GetCurrentPos() ) > Siege::BROADCAST_SQUARED_RADIUS)
{
itr = m_BroadCastSet.erase(itr);
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch && bDeletePacketCrypt)
{
lpDispatch->GetSendStream().PutBuffer(szPacket, sizeof(PktNewSiegeBroadCast), CmdNewSiegeBroadCast);
}
}
else
{
++itr;
}
}
else
{
itr = m_BroadCastSet.erase(itr);
}
}
// BroadCast Data 와 Delta Data 를 압축해둔다.
char szSrcBuffer[ BroadCastSiege::MAX_SIEGEBROADCAST_BUFFER_SIZE ] ;
char szDeltaSrcBuffer[ BroadCastSiege::MAX_SIEGEBROADCAST_BUFFER_SIZE ] ;
m_dwBroadCastSize = BroadCastSiege::MAX_SIEGEBROADCAST_BUFFER_SIZE;
m_dwDeltaSize = 0;
PktNewSiegeBroadCast* lpPktNewSiegeBC = reinterpret_cast<PktNewSiegeBroadCast*>( szSrcBuffer );
char* lpDataPos = reinterpret_cast<char*>( lpPktNewSiegeBC + 1 );
lpPktNewSiegeBC->m_dwCID = GetCID();
lpPktNewSiegeBC->m_cType = BroadCastSiege::BROADCAST_ALL_DATA;
memcpy( lpDataPos, m_SerializeSiegeObjectData.GetBroadCastData(), m_SerializeSiegeObjectData.GetBroadCastDataLen() );
PacketWrap::WrapCompress( m_szBroadCastBuffer, m_dwBroadCastSize, szSrcBuffer,
static_cast<unsigned short>( sizeof(PktNewSiegeBroadCast) + m_SerializeSiegeObjectData.GetBroadCastDataLen() ),
CmdNewSiegeBroadCast, 0, 0 );
if ( m_SerializeSiegeObjectData.GetDeltaDataLen() > 0 )
{
m_dwDeltaSize = BroadCastSiege::MAX_SIEGEBROADCAST_BUFFER_SIZE;
lpPktNewSiegeBC = reinterpret_cast<PktNewSiegeBroadCast*>( szDeltaSrcBuffer ) ;
lpDataPos = reinterpret_cast<char*>( lpPktNewSiegeBC + 1 );
lpPktNewSiegeBC->m_dwCID = GetCID();
lpPktNewSiegeBC->m_cType = BroadCastSiege::BROADCAST_DELTA_DATA;
memcpy( lpDataPos, m_SerializeSiegeObjectData.GetDeltaData(), m_SerializeSiegeObjectData.GetDeltaDataLen() );
PacketWrap::WrapCompress( m_szDeltaBuffer, m_dwDeltaSize, szDeltaSrcBuffer,
static_cast<unsigned short>( sizeof(PktNewSiegeBroadCast) + m_SerializeSiegeObjectData.GetDeltaDataLen() ),
CmdNewSiegeBroadCast, 0, 0 );
}
// 범위내에 있는 캐릭터노드들을 검사한다.
CCharSphereTree::GetInstance().RangeTest( GetCurrentPos(), Siege::BROADCAST_RADIUS, this );
}
void CSiegeObject::RangeTestCallBack( const Position& centerPos, float fDistance, CCharSphereNode* pNode )
{
if ( pNode )
{
BroadCastSet::iterator itr = m_BroadCastSet.find(pNode->GetCID());
if (itr != m_BroadCastSet.end())
{
CCharacter* lpCharacter = pNode->GetCharacter();
if (NULL != lpCharacter)
{
// 기존에 있던 캐릭터이다. Delta 정보만 보내준다.
if (m_SerializeSiegeObjectData.GetDeltaDataLen() > 0)
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
lpDispatch->GetSendStream().PutBuffer(m_szDeltaBuffer, m_dwDeltaSize, CmdNewSiegeBroadCast);
}
}
}
}
else
{
// 새로 추가된 캐릭터이다. BroadCast 로 모든 정보를 보낸다.
CCharacter* lpCharacter = pNode->GetCharacter();
if (NULL != lpCharacter)
{
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
lpDispatch->GetSendStream().PutBuffer(m_szBroadCastBuffer, m_dwBroadCastSize, CmdNewSiegeBroadCast);
}
m_BroadCastSet.insert( pNode->GetCID() );
}
}
}
}
void CSiegeObject::SendToRange( float fRadius, const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In )
{
CCharSphereTree::GetInstance().SendToRange( GetCurrentPos(), fRadius, szPacket, dwPacketSize, cCMD_In );
}
void CSiegeObject::SendHPUpdateToDBAgent(bool bForce)
{
++m_cHPUpdateCount;
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
switch (m_wObjectType)
{
case Siege::CAMP: // 길드 요새
case Siege::CAMP_SHOP: // 길드 요새 상점
case Siege::MINING_CAMP: // 채굴기
case Siege::KARTERANT_WEAPON: // 월드 웨폰
case Siege::MERKADIA_WEAPON: // 월드 웨폰
{
if (bForce || m_cHPUpdateCount >= MAX_HP_UPDATE_COUNT)
{
GameClientSendPacket::SendCharCampCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), 0, GetCampID(),
m_CreatureStatus.m_nNowHP, 0, PktCampCmd::CAMP_UPDATE_HP,
PktBase::NO_SERVER_ERR);
m_cHPUpdateCount = 0;
}
}
break;
case Siege::EMBLEM: // 성 상징물
case Siege::GATE: // 성문
case Siege::GUARD: // 가드
case Siege::SHORT_RANGE_CASTLE_ARMS: // 근거리 수성 병기
case Siege::LONG_RANGE_CASTLE_ARMS: // 원거리 수성 병기
{
if (bForce || m_cHPUpdateCount >= MAX_HP_UPDATE_COUNT)
{
GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), 0, GetCastleID(), m_dwCID,
GetDefaultNowHP(), 0, PktCastleCmd::CASTLE_UPDATE_HP,
PktBase::NO_SERVER_ERR);
m_cHPUpdateCount = 0;
}
}
break;
case Siege::SHORT_RANGE_SIEGE_ARMS: // 근거리 공성 병기
case Siege::LONG_RANGE_SIEGE_ARMS: // 원거리 공성 병기
case Siege::AIRSHIP: // 수송선
{
if (bForce || m_cHPUpdateCount >= MAX_HP_UPDATE_COUNT)
{
GameClientSendPacket::SendCharSiegeArmsCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), m_dwOwnerID, m_dwCID,
m_CreatureStatus.m_nNowHP, PktSiegeArmsCmd::SIEGE_UPDATE_HP,
PktBase::NO_SERVER_ERR);
m_cHPUpdateCount = 0;
}
}
}
}
}
bool CSiegeObject::IsCamp() const
{
switch ( m_wObjectType )
{
case Siege::CAMP:
case Siege::CAMP_SHOP:
case Siege::MINING_CAMP:
case Siege::KARTERANT_WEAPON:
case Siege::MERKADIA_WEAPON:
return true;
}
return false;
}
bool CSiegeObject::IsCastleObject() const
{
switch ( m_wObjectType )
{
case Siege::EMBLEM :
case Siege::GATE :
case Siege::BACKDOOR :
case Siege::CASTLE_ARMS_NPC :
case Siege::GUARD :
case Siege::SHORT_RANGE_CASTLE_ARMS :
case Siege::LONG_RANGE_CASTLE_ARMS :
{
return true;
}
}
return false;
}
bool CSiegeObject::IsCastleArms() const
{
switch ( m_wObjectType )
{
case Siege::GUARD :
case Siege::SHORT_RANGE_CASTLE_ARMS :
case Siege::LONG_RANGE_CASTLE_ARMS :
case Siege::CASTLE_ARMS_NPC :
{
return true;
}
}
return false;
}
bool CSiegeObject::IsSiegeArms() const
{
switch ( m_wObjectType )
{
case Siege::SHORT_RANGE_SIEGE_ARMS :
case Siege::LONG_RANGE_SIEGE_ARMS :
case Siege::AIRSHIP :
{
return true;
}
}
return false;
}
bool CSiegeObject::IsRidable() const
{
switch ( m_wObjectType )
{
case Siege::SHORT_RANGE_CASTLE_ARMS :
case Siege::LONG_RANGE_CASTLE_ARMS :
case Siege::SHORT_RANGE_SIEGE_ARMS :
case Siege::LONG_RANGE_SIEGE_ARMS :
case Siege::AIRSHIP :
{
return true;
}
}
return false;
}
void CSiegeObject::SetMaterialNum(unsigned char cMaterial)
{
m_cMaterial = cMaterial;
}
void CSiegeObject::SetNation(unsigned char cNation)
{
m_cNation = cNation;
if (IsEmblem() && Creature::STATELESS == cNation)
{
m_cState = Siege::COMPLETE;
m_cSubState = Siege::NONE;
}
}

View File

@@ -0,0 +1,216 @@
#ifndef _SIEGE_OBJECT_H_
#define _SIEGE_OBJECT_H_
#pragma once
#include <list>
#include <map>
#include <Creature/Monster/PatternMonster.h>
#include <Creature/Siege/SiegeConstants.h>
#include <Creature/Character/SphereTree/CharSphereTree.h>
#include <Creature/Siege/SerializeSiegeObjectData.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
using namespace Siege;
// 전방 참조
class CCharacter;
namespace BroadCastSiege
{
class CSerializeSiegeObjectData;
}
class CSiegeObject : public CSkillMonster, public ICharSphereTreeCallBack
{
public:
typedef std::set<unsigned long> BroadCastSet;
virtual ~CSiegeObject();
// ---------------------------------------------------------------------
// CSkillMonster 로부터 상속받은 함수
// 적아군 식별
virtual EnemyCheck::EnemyType IsEnemy(CCreature* lpTarget, unsigned char* cResult = NULL);
// 자체 공격 능력 구현 함수
virtual void NormalBehavior(unsigned long dwTick) { }
virtual void AttackBehavior(unsigned long dwTick) { }
virtual void SearchPlayer(void) { }
bool SkillAttack(void);
virtual bool Dead(CAggresiveCreature* pOffencer) { return false; }
virtual void MoveTo(const Position& NewPosition);
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// 캐릭터가 조종해서 공격할 경우 Attack Packet 처리 함수
virtual bool AttackCID(CCharacter* lpRideChar, AtType attackType, AtNode& attackNode, unsigned short& wError) { return false; }
virtual bool Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders,
unsigned char* cDefenserJudges, unsigned short* wDefenserMPHeal) { return false; }
virtual bool MissileAttack(AtType attackType, float fDir, float nRange, float fAngle, char cTargetType) { return false; }
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// SiegeObject 처리 함수
virtual bool Build(unsigned char cUpgradeStep = 0) { return false; } // 구축 완료시 처리
virtual bool Cancel() { return false; } // 구축 취소 완료
virtual bool Upgrade(unsigned char cUpgradeStep) { return false; } // 업그레이드 완료시 처리
virtual bool Repair(unsigned short wRepairHP) { return false; } // 수리 완료시 처리
virtual bool Restore() { return false; } // 복구 완료시 처리
virtual bool Destroy(unsigned char cOffencerNation, bool bTakeGold) { return false; } // 파괴 완료시 처리 (자체 파괴, 파괴됨)
virtual bool Destroy(unsigned long dwOffencerGID) { return false; } // 길드 요새 파괴 완료시 처리 (자체 파괴, 파괴됨)
virtual bool Update(unsigned char cState, unsigned long dwValue1, unsigned long dwValue2, unsigned long dwOwnerID, unsigned char cSubCmd) { return false; } // 정보 업데이트
virtual bool ChangeType(unsigned short wChangeType) { return false; } // 변형 완료시 처리
// 스타터킷 아이템으로 전환 (bFullMaterial 이 true 이면 자재의 90%를 돌려준다.)
virtual bool ToStarterKit(bool bFullMaterial=false) { return false; }
virtual unsigned long GetRepairGold() const { return Siege::MAX_REPAIR_GOLD; }
unsigned long GetRepairHP();
// Rider 관련 정보
virtual unsigned char IsRider(unsigned long dwCID) const; // 해당 캐릭터가 병기에 탑승해 있는가?
virtual bool Ride(unsigned long dwCID) { return false; } // 병기 탑승
virtual bool GetOff(unsigned long dwCID) { return false; } // 병기 내림
virtual void AllGetOff() { GetOff(m_dwRiderCID); } // 병기에서 모두 내림
// 성 오브젝트 관련된 함수 (상징물 업그레이드에 따른 효과)
void UpgradeByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep);
void DegradeByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep);
unsigned short GetDefaultNowHP(); // 상징물의 업그레이드 효과가 없는 현재 HP 얻기
// ---------------------------------------------------------------------
// 반경 5셀에 패킷을 전송하는 함수
void SendHPUpdateToDBAgent(bool bForce=false);
// 이전 브로드캐스트 함수
// void SendToRadiusCell(const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In);
// bool SerializeOut(char* szBuffer_In, unsigned short& wDataLen);
void RangeTest();
virtual void RangeTestCallBack( const Position& centerPos, float fDistance, CCharSphereNode* pNode );
void SendToRange( float fRadius, const char* szPacket, unsigned long dwPacketSize, unsigned char cCMD_In );
BroadCastSiege::CSerializeSiegeObjectData& GetSerializeData(void) { return m_SerializeSiegeObjectData; }
// Get / Set 함수
bool IsEmblem() const { return (Siege::EMBLEM == m_wObjectType); }
bool IsCamp() const;
bool IsGate() const { return (Siege::GATE == m_wObjectType); }
bool IsGuard() const { return (Siege::GUARD == m_wObjectType); }
bool IsCastleObject() const;
bool IsCastleArms() const;
bool IsSiegeArms() const;
bool IsRidable() const; // 탈수 있는 오브젝트인가?
bool IsCastleNPC() const { return (m_wObjectType == Siege::BACKDOOR || m_wObjectType == Siege::CASTLE_ARMS_NPC); }
bool IsMaxHP() const { return (m_CreatureStatus.m_nNowHP == m_CreatureStatus.m_StatusInfo.m_nMaxHP); }
bool IsPeaceMode();
bool IsRideArms(void) const { return false; }
bool IsWorldWeapon() const;
unsigned short GetObjectType() const { return m_wObjectType; }
unsigned long GetCID() const { return m_dwCID; }
unsigned long GetNowHP() const { return m_CreatureStatus.m_nNowHP; }
unsigned long GetMaxHP() const { return m_CreatureStatus.m_StatusInfo.m_nMaxHP; }
unsigned char GetState() const { return m_cState; }
unsigned char GetSubState() const { return m_cSubState; }
unsigned char GetUpgradeStep() const { return m_cUpgradeStep; }
unsigned char GetUpgradeType() const { return m_cUpgradeType; }
const Position& GetPosition() const { return m_CurrentPos; }
unsigned char GetMaterialNum() const { return m_cMaterial; }
unsigned char GetSiegeCount() const { return m_cSiegeCount; }
float GetDefaultDir() const { return m_fDefaultDir; }
unsigned long GetRiderCID() const { return m_dwRiderCID; }
virtual void GetRiders( unsigned long* pRiders ) const;
virtual unsigned char GetRiderNum() const;
unsigned long GetDevelopGold(unsigned short wDefaultObjectType=Siege::DEFAULT_TYPE) const;
unsigned char GetDevelopSpeed(unsigned short wDefaultObjectType=Siege::DEFAULT_TYPE) const;
unsigned long GetUpgradeGold() const;
unsigned char GetUpgradeSpeed(unsigned char cDefaultUpgradeType=Siege::NO_JEWEL) const;
float GetBonusRate() const;
int GetKID() const;
unsigned char GetNation() const;
unsigned long GetGID() const;
CAggresiveCreature* GetDuelOpponent(void) const;
char GetConsumeMPCount(void) { return m_cConsumeMPCount; }
unsigned long GetCastleID() const { return m_dwCampOrCastleID; }
unsigned long GetCampID() const { return m_dwCampOrCastleID; }
unsigned long GetOwnerID() const { return m_dwOwnerID; }
void SetGID(unsigned long dwGID) { m_dwGID = dwGID; }
void SetFullMP() { m_CreatureStatus.m_nNowMP = m_CreatureStatus.m_StatusInfo.m_nMaxMP; }
void SetState(unsigned char cState) { m_cState = cState; }
void SetSubState(unsigned char cSubState) { m_cSubState = cSubState; }
void SetMaterialNum(unsigned char cMaterial);
void SetSiegeCount(unsigned char cSiegeCount) { m_cSiegeCount = cSiegeCount; }
void SetNation(unsigned char cNation);
protected:
CSiegeObject(MonsterCreateInfo& MonsterCreate, const CastleObjectInfo& CastleObject);
CSiegeObject(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, bool bFullHP);
CSiegeObject(MonsterCreateInfo& MonsterCreate, unsigned long dwOwnerID, unsigned char cNation,
unsigned long dwHP, unsigned short wObjectType, unsigned char cState, unsigned char cUpgradeStep);
void UpdateObjectInfo(unsigned char cHPType=Siege::NOW_HP, unsigned short wRepairHP=0); // 상태, 업그레이드 변화시에 내부값들 변경
unsigned long m_dwCampOrCastleID; // CastleID : 성 상징물, 성문, 뒷문, 가드, 근거리/원거리 수성병기
// CampID : 길드 요새 상징물, 경비병
unsigned long m_dwOwnerID; // CID : 근거리/원거리 수성병기, 근거리/원거리 공성병기, 비공정
unsigned long m_dwGID; // GID : 길드 요새 및 월드 웨폰의 소유 길드의 길드ID
// 주의!! 공성전 관련 오브젝트는 GID 를 사용하지 않는다!!
unsigned char m_cNation; // 공성전 관련 오브젝트가 사용하는 국가
unsigned short m_wObjectType; // 공성 관련 오브젝트 타입
unsigned char m_cState; // 현재 오브젝트의 상태
unsigned char m_cSubState; // 현재 오브젝트의 서브 상태 (Use EMBLEM & GATE)
unsigned char m_cUpgradeStep; // 현재 오브젝트의 업그레이드 단계
unsigned char m_cUpgradeType; // 성 상징물의 업그레이드 타입 (Only EMBLEM)
unsigned char m_cMaterial; // 공성 자재 갯수
unsigned char m_cSiegeCount; // 공성 지난 횟수
float m_fDefaultDir; // 수성 병기의 기본 방향
unsigned long m_dwRiderCID; // 수성 병기, 공성 병기에 탑승한 캐릭터의 CID
unsigned char m_cHPUpdateCount; // HP 정보 업데이트 카운트
// Rider 정보가 필요한 이유
// 1. 몇명이나 타고 있는지 알수가 없다.
// 2. 누군가가 내렸을 경우 누가 내렸는지 알수가 없다.
//
// * 드랍쉽에 타고 내릴때 소유주체크를 하면 안된다.
// ----------------------------------------------------------------------------------------
// New BroadCast Data
BroadCastSiege::CSerializeSiegeObjectData m_SerializeSiegeObjectData; // 공성 오브젝트 데이터 보관 및 변경된 데이터 보관
char m_szBroadCastBuffer[ BroadCastSiege::MAX_SIEGEBROADCAST_BUFFER_SIZE ];
char m_szDeltaBuffer[ BroadCastSiege::MAX_SIEGEBROADCAST_BUFFER_SIZE ];
unsigned long m_dwBroadCastSize;
unsigned long m_dwDeltaSize;
BroadCastSet m_BroadCastSet;
// ----------------------------------------------------------------------------------------
friend class CSiegeObjectMgr;
};
#endif // _SIEGE_OBJECT_H_

View File

@@ -0,0 +1,745 @@
#include "stdafx.h"
#include "SiegeObject.h"
#include <Utility/Registry/RegFunctions.h>
#include <Utility/Math/Math.h>
#include <Skill/Spell/Spell.h>
#include <Skill/SkillMgr.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
#include <Creature/CreatureManager.h>
#include <Map/FieldMap/CellManager.h>
#include <Creature/Monster/Monster.h>
#include <Creature/Monster/PatternMonster.h>
#include <Castle/Castle.h>
#include <Castle/CastleMgr.h>
void CSiegeObject::NormalBehavior(unsigned long dwTick)
{
if ( !IsEmblem() && !IsGuard() )
{
return;
}
if (false == Castle::CCastleMgr::GetInstance().IsSiegeTime())
{
return;
}
// 선공 몹 처리
if (NULL == m_lpTarget && true == m_MonsterInfo.m_bFirstAttack)
{
SearchPlayer();
}
}
void CSiegeObject::AttackBehavior(unsigned long dwTick)
{
PERFORMANCE_CHECK(FunctionTimingCheck)
if ( !IsEmblem() && !IsGuard() )
{
return;
}
if (false == Castle::CCastleMgr::GetInstance().IsSiegeTime())
{
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 (IsEmblem() && 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();
}
/* else
{
m_MotionInfo.m_wAction = MonsterInfo::Z3D_CA_WALK; // 일단 걷기로 세팅
GetMotion(m_MotionInfo.m_wAction, m_MotionInfo); // 걷기 모션 정보를 얻어온다.
// 몬스터가 이동 공격을 할만한 거리인가 체크
if (fDistance < fAttackRange + m_MotionInfo.m_fVelocity)
{
// 걸어가면서 공격한다.
WalkAttackAction(fDistance - fAttackRange + 0.1f);
}
else
{
// 달려간다
RunAction(fDistance, m_lpTarget->GetCurrentPos().m_fPointX, m_lpTarget->GetCurrentPos().m_fPointZ);
// 내 활동구역을 벗어나따. 돌아가자~!
if (true == m_MonsterInfo.m_bReturnPosition && true == IsReturn())
{
m_wSearchRange = OutsideSearchRange;
CancelTarget();
}
}
}
*/
if (false == m_bAttacking)
{
m_lCurrentFrame = m_MotionInfo.m_dwFrame;
}
}
else
{
// 공격 범위 안이다.
if (false == m_bAttacking)
{
if (true == SkillAttack()) // 스킬 공격을 사용하는가 체크
{
SkillAttackAction();
}
/* else
{
AttackAction();
}
*/
}
}
}
bool CSiegeObject::SkillAttack()
{
unsigned char cSkillLockCount = m_cUpgradeStep + 1;
unsigned char cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
// MON_TODO : 5단계 마법(LockCount : 4)은 아직 사용할수 없다.
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, MAX_PATTERN = 2 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 조건 대상 행동
// A B A : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : 공격 스킬열에 해당하는 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[SkillPattern::ATTACK_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = SkillPattern::ATTACK_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[SkillPattern::ATTACK_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// A B D : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : 범위 스킬열에 해당하는 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[SkillPattern::EXTENT_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = SkillPattern::EXTENT_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[SkillPattern::EXTENT_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
void CSiegeObject::UpdateObjectInfo(unsigned char cHPType, unsigned short wRepairHP)
{
unsigned short wPrevMaxHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
unsigned short wNowHP = m_CreatureStatus.m_nNowHP;
int nKID = Siege::GetKID(m_wObjectType, m_cState, m_cUpgradeType, m_cUpgradeStep);
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoType)
{
m_CreatureStatus = lpProtoType->m_CreatureStatus;
m_MonsterInfo = lpProtoType->m_MonsterInfo;
m_CreatureStatus.m_StatusInfo.CalculateSubStatus();
}
switch (cHPType)
{
case Siege::NOW_HP:
m_CreatureStatus.m_nNowHP = wNowHP;
break;
case Siege::UPGRADE_HP:
m_CreatureStatus.m_nNowHP = wNowHP + m_CreatureStatus.m_StatusInfo.m_nMaxHP - wPrevMaxHP;
SendHPUpdateToDBAgent();
break;
case Siege::REPAIR_HP:
m_CreatureStatus.m_nNowHP = wNowHP + wRepairHP;
SendHPUpdateToDBAgent(true);
break;
case Siege::FULL_HP:
m_CreatureStatus.m_nNowHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
SendHPUpdateToDBAgent(true);
break;
}
}
void CSiegeObject::UpgradeByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep)
{
int nKID = Siege::GetKID(Siege::EMBLEM, Siege::COMPLETE, cUpgradeType, cUpgradeStep);
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoType)
{
// 업그레이드 속도 향상
m_MonsterInfo.m_cUpgradeSpeed -= static_cast<unsigned char>(m_MonsterInfo.m_cUpgradeSpeed
* lpProtoType->m_MonsterInfo.m_fUpgradeSpeedUp);
// 개발 속도 향상
m_MonsterInfo.m_cDevelopSpeed -= static_cast<unsigned char>(m_MonsterInfo.m_cDevelopSpeed
* lpProtoType->m_MonsterInfo.m_fDevelopSpeedUp);
// 방어력 향상
m_CreatureStatus.m_StatusInfo.m_wArmor += static_cast<short>(m_CreatureStatus.m_StatusInfo.m_wArmor
* lpProtoType->m_MonsterInfo.m_fDefenseUp);
m_CreatureStatus.m_StatusInfo.m_wMagicResist += static_cast<short>(m_CreatureStatus.m_StatusInfo.m_wMagicResist
* lpProtoType->m_MonsterInfo.m_fDefenseUp);
// HP 향상
unsigned short wAddHP = static_cast<unsigned short>(m_CreatureStatus.m_StatusInfo.m_nMaxHP
* lpProtoType->m_MonsterInfo.m_fHPUp);
m_CreatureStatus.m_StatusInfo.m_nMaxHP += wAddHP;
m_CreatureStatus.m_nNowHP += wAddHP;
// 공격력 향상
m_CreatureStatus.m_StatusInfo.m_wMinDamage += static_cast<short>(m_CreatureStatus.m_StatusInfo.m_wMinDamage
* lpProtoType->m_MonsterInfo.m_fOffenseUp);
m_CreatureStatus.m_StatusInfo.m_wMaxDamage += static_cast<short>(m_CreatureStatus.m_StatusInfo.m_wMaxDamage
* lpProtoType->m_MonsterInfo.m_fOffenseUp);
m_CreatureStatus.m_StatusInfo.m_wMagicPower += static_cast<short>(m_CreatureStatus.m_StatusInfo.m_wMagicPower
* lpProtoType->m_MonsterInfo.m_fOffenseUp);
// 생성 비용 절감
m_MonsterInfo.m_dwDevelopGold -= static_cast<unsigned long>(m_MonsterInfo.m_dwDevelopGold
* lpProtoType->m_MonsterInfo.m_fDevelopGoldDown);
// 업그레이드 비용 절감
m_MonsterInfo.m_dwUpgradeGold -= static_cast<unsigned long>(m_MonsterInfo.m_dwUpgradeGold
* lpProtoType->m_MonsterInfo.m_fUpgradeGoldDown);
}
}
void CSiegeObject::DegradeByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep)
{
int nKID = Siege::GetKID(Siege::EMBLEM, Siege::COMPLETE, cUpgradeType, cUpgradeStep);
const CMonsterMgr::MonsterProtoType* lpProtoTypeEmblem = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
nKID = GetKID();
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoType && lpProtoTypeEmblem)
{
unsigned short wNowHP = m_CreatureStatus.m_nNowHP -
static_cast<unsigned short>(m_CreatureStatus.m_nNowHP * lpProtoTypeEmblem->m_MonsterInfo.m_fHPUp);
m_CreatureStatus = lpProtoType->m_CreatureStatus;
m_MonsterInfo = lpProtoType->m_MonsterInfo;
m_CreatureStatus.m_nNowHP = wNowHP;
}
}
unsigned short CSiegeObject::GetDefaultNowHP()
{
Castle::CCastle* lpCastle = Castle::CCastleMgr::GetInstance().GetCastle( GetCastleID() );
CSiegeObject* lpEmblem = lpCastle->GetCastleEmblem();
if (lpCastle && lpEmblem)
{
int nKID = lpEmblem->GetKID();
const CMonsterMgr::MonsterProtoType* lpProtoTypeEmblem = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoTypeEmblem)
{
return m_CreatureStatus.m_nNowHP - static_cast<unsigned short>(m_CreatureStatus.m_nNowHP * lpProtoTypeEmblem->m_MonsterInfo.m_fHPUp);
}
}
return m_CreatureStatus.m_nNowHP;
}
void CSiegeObject::SetACTByObjectType()
{
switch (m_wObjectType)
{
case Siege::EMBLEM: m_cAttackableCreatureType = Creature::ACT_EMBLEM; break;
case Siege::GATE: m_cAttackableCreatureType = Creature::ACT_GATE; break;
case Siege::BACKDOOR: m_cAttackableCreatureType = Creature::ACT_NONE; break;
case Siege::CAMP: m_cAttackableCreatureType = Creature::ACT_CAMP; break;
case Siege::CASTLE_ARMS_NPC: m_cAttackableCreatureType = Creature::ACT_NONE; break;
case Siege::GUARD: m_cAttackableCreatureType = Creature::ACT_GUARD; break;
case Siege::SHORT_RANGE_CASTLE_ARMS: m_cAttackableCreatureType = Creature::ACT_SHORT_RANGE_CASTLE_ARMS; break;
case Siege::LONG_RANGE_CASTLE_ARMS: m_cAttackableCreatureType = Creature::ACT_LONG_RANGE_CASTLE_ARMS; break;
case Siege::SHORT_RANGE_SIEGE_ARMS: m_cAttackableCreatureType = Creature::ACT_SHORT_RANGE_SIEGE_ARMS; break;
case Siege::LONG_RANGE_SIEGE_ARMS: m_cAttackableCreatureType = Creature::ACT_LONG_RANGE_SIEGE_ARMS; break;
case Siege::AIRSHIP: m_cAttackableCreatureType = Creature::ACT_AIRSHIP; break;
}
}
void CSiegeObject::SearchPlayer()
{
switch (m_wObjectType)
{
case Siege::GUARD:
SearchAirShip();
break;
case Siege::EMBLEM:
SearchEnemyPlayer();
break;
}
}
void CSiegeObject::SearchAirShip()
{
// 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 && CCreature::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);
}
}
void CSiegeObject::SearchEnemyPlayer()
{
// 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 &&
CCreature::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);
}
}
void CSiegeObject::MoveTo(const Position& NewPosition)
{
CAggresiveCreature::MoveTo(NewPosition, false);
for (int i=1; i<Siege::AIRSHIP_LIMIT_NUM; ++i)
{
if (0 != m_dwRideCID[i])
{
CCharacter* lpRideCharacter = CCreatureManager::GetInstance().GetCharacter(m_dwRideCID[i]);
if (lpRideCharacter)
{
lpRideCharacter->MoveTo(NewPosition, false);
}
}
}
}
bool CSiegeObject::Dead(CAggresiveCreature* pOffencer)
{
switch (m_wObjectType)
{
case Siege::CAMP: return CampDead(pOffencer);
case Siege::EMBLEM: return EmblemDead(pOffencer);
case Siege::GATE: return GateDead(pOffencer);
case Siege::GUARD: return GuardDead(pOffencer);
case Siege::SHORT_RANGE_CASTLE_ARMS:
case Siege::LONG_RANGE_CASTLE_ARMS:
return CastleArmsDead(pOffencer);
case Siege::AIRSHIP:
case Siege::SHORT_RANGE_SIEGE_ARMS:
case Siege::LONG_RANGE_SIEGE_ARMS:
return SiegeArmsDead(pOffencer);
default: break;
}
return false;
}
bool CSiegeObject::CampDead(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 )
{
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 CSiegeObject::EmblemDead(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_dwEnemyGID = reinterpret_cast<CCharacter*>(pOffencer)->GetGID();
return GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), GetCastleID(), m_dwCID,
m_dwEnemyGID, 0, PktCastleCmd::CASTLE_UPDATE_EMBLEM, PktBase::NO_SERVER_ERR);
}
return false;
}
bool CSiegeObject::GateDead(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(), GetCastleID(), m_dwCID,
0, 0, PktCastleCmd::CASTLE_DESTROY_GATE, PktBase::NO_SERVER_ERR);
}
return false;
}
bool CSiegeObject::GuardDead(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;
SetACTByObjectType();
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(), GetCastleID(), m_dwCID,
PktCastleCmd::DESTROY, 0, PktCastleCmd::CASTLE_DESTROY_ARMS,
PktBase::NO_SERVER_ERR);
}
return false;
}
bool CSiegeObject::CastleArmsDead(CAggresiveCreature* pOffencer)
{
if (NULL == pOffencer) return false;
if (STATE_ID_DIE == m_nCurrentState) return false;
if ( !IsCastleArms() || IsGuard() ) 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;
SetACTByObjectType();
// 타고 있던 캐릭터는 죽는다.
for (int i=0; i<Siege::AIRSHIP_LIMIT_NUM; ++i)
{
CCharacter* lpRideChar = CCreatureManager::GetInstance().GetCharacter(m_dwRideCID[i]);
if (lpRideChar)
{
m_dwRideCID[i] = 0;
lpRideChar->GetOff();
lpRideChar->Kill(pOffencer);
}
}
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (lpDBAgentDispatch)
{
return GameClientSendPacket::SendCharCastleCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), GetCastleID(), m_dwCID,
PktCastleCmd::DESTROY, 0, PktCastleCmd::CASTLE_DESTROY_ARMS,
PktBase::NO_SERVER_ERR);
}
return false;
}
bool CSiegeObject::SiegeArmsDead(CAggresiveCreature* pOffencer)
{
if (NULL == pOffencer) return false;
if (STATE_ID_DIE == m_nCurrentState) return false;
if ( !IsSiegeArms() ) return false;
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_bCasting = false;
// 타고 있던 캐릭터는 죽는다.
for (int i=0; i<Siege::AIRSHIP_LIMIT_NUM; ++i)
{
CCharacter* lpRideChar = CCreatureManager::GetInstance().GetCharacter(m_dwRideCID[i]);
if (lpRideChar)
{
lpRideChar->GetOff();
lpRideChar->Kill(pOffencer);
}
m_dwRideCID[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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
#ifndef _SIEGE_OBJECT_MANAGER_H_
#define _SIEGE_OBJECT_MANAGER_H_
#pragma once
#include <map>
#include <Creature/Siege/SiegeConstants.h>
#include <Network/Stream/SendStream.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
// 전방 참조
class CSiegeObject;
class CCamp;
class CSiegeObjectMgr
{
public:
~CSiegeObjectMgr();
static CSiegeObjectMgr& GetInstance();
// 타입 정의
typedef std::map<unsigned long, CSiegeObject*> SiegeObjectMap; // <CID, CSiegeObject*>
typedef std::map<unsigned long, CCamp*> CampMap; // <CampID, CCamp*>
void Destroy();
void DestroyAllCamp();
// DBAgent Server 접속시 DB 에서 얻어온 정보로 객체 생성함수
CSiegeObject* CreateCastleObject(CastleObjectInfo& CastleObject);
// 클라이언트의 요청으로 DBAgentServer 를 거쳐서 객체를 생성하는 함수
CSiegeObject* CreateCamp(unsigned long dwCID, unsigned long dwCampID, unsigned long dwGID, unsigned long dwHP,
unsigned char cState, unsigned char cUpgradeStep, const CampRight& campRight,
Position Pos, unsigned char cMaterial, bool bFullHP);
CSiegeObject* CreateWorldWeapon(unsigned long dwCID, unsigned long dwCampID, unsigned long dwGID, unsigned long dwHP,
unsigned char cState, unsigned char cUpgradeStep, const CampRight& campRight,
Position Pos, unsigned char cMaterial, bool bFullHP, unsigned short wObjectType);
CSiegeObject* CreateSiegeArms(unsigned long dwCID, unsigned long dwOwnerID, unsigned char cNation, unsigned long dwHP,
unsigned short wObjectType, unsigned char cState, unsigned char cUpgradeStep,
Position Pos);
CSiegeObject* ChangeCampType(unsigned long dwCampID, unsigned short wChangeType);
CCamp* GetCamp(unsigned long dwCampID);
CSiegeObject* GetSiegeObject(unsigned long dwObjectID);
bool DeleteSiegeObject(unsigned long dwCID);
bool DeleteSiegeObject(CSiegeObject* lpObject);
bool DeleteCamp(unsigned long dwCampID);
bool ExistBuildingOrDestroyingCamp(unsigned long dwGID);
bool ExistCampInRadius(const Position& Pos);
bool ExistCompleteCampInRadius(const Position& Pos, unsigned long dwGID);
void HasCampByGID(unsigned long dwGID, bool& bHasDevelopingCamp, bool& bHasCompleteCamp);
bool HasSiegeArms(unsigned long dwCID); // 해당 CID 가 공성 병기 소유 여부.
unsigned long GetSiegeObjectCount(void);
// ------------------------------------------------------------------------------------------------------
// 월드 웨폰 관련 함수
void SetWorldWeaponInfo(unsigned char cKarRemainSiegeTime, unsigned char cMerRemainSiegeTime);
void UpdateWorldWeaponInfo(unsigned char cNation, unsigned char cRemainSiegeTime);
bool EnableCreateWorldWeapon(unsigned char cNation, unsigned short& wError) const;
void DecreaseWeaponRemainSiegeTime();
CCamp* GetWorldWeapon() const { return m_lpWorldWeapon; }
void RequestWorldWeaponCreation() { m_bRequestWorldWeaponCreation = true; }
void ReleaseWorldWeaponCreation() { m_bRequestWorldWeaponCreation = false; }
// ------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
// Process 함수
void ProcessAllSiegeObject();
void ProcessCampRegenHPAndMP();
void ProcessMiningCamp(unsigned long dwProcessType);
void ProcessCampShopUpdate(bool bForce);
void ProcessWorldWeaponFire();
// ------------------------------------------------------------------------------------------------------
// DBAgentServer -> GameServer 요새 정보 전송
bool SerializeIn(char* lpBuffer, unsigned short wSize, unsigned long dwCID);
// Client 에게 길드 요새 정보 전송
bool SendCampInfo(CSendStream& SendStream);
// 해당 성의 모든 오브젝트의 주인이 바뀌었다는것을 병기의 반경 5셀에 있는 캐릭터에게 알려준다.
bool SendChangeMaster(unsigned long dwCastleID, unsigned char cNation);
bool SendLoseOwnership(unsigned long dwCastleID);
// 모든 공성관련 오브젝트들을 반경 5셀에 있는 캐릭터들에게 알려준다.
// Vincent - 브로드 캐스트 테스트 코드
void PrepareBroadCast();
void BroadCast();
private:
CSiegeObjectMgr();
// DBAgent Server 접속시 DB 에서 얻어온 정보로 객체 생성함수
CSiegeObject* CreateCamp(char* lpBuffer, unsigned short wSize, unsigned long dwCID);
CSiegeObject* CreateMiningCamp(char* lpBuffer, unsigned short wSize, unsigned long dwCID);
CSiegeObject* CreateCampShop(char* lpBuffer, unsigned short wSize, unsigned long dwCID);
CSiegeObject* CreateWorldWeapon(char* lpBuffer, unsigned short wSize, unsigned long dwCID, unsigned char cNation);
SiegeObjectMap m_SiegeObjectMap;
CampMap m_CampMap;
// ------------------------------------------------------------------------------------------------------
// 월드 웨폰 관련
CCamp* m_lpWorldWeapon; // 월드 웨폰 (하나 뿐이므로 따로 관리)
unsigned char m_cKarRemainSiegeTime; // 카르테란트 월드 웨폰이 부서진후 지나야할 공성 시간 횟수
unsigned char m_cMerRemainSiegeTime; // 카르테란트 월드 웨폰이 부서진후 지나야할 공성 시간 횟수
bool m_bRequestWorldWeaponCreation; // 월드 웨폰 생성을 DBAgentServer 에 요청한 상태인가
// ------------------------------------------------------------------------------------------------------
};
#endif // _SIEGE_OBJECT_MANAGER_H_

View File

@@ -0,0 +1,200 @@
//--------------------------------------------------------------------------------------------------------------------------
// File Name: WorldWeapon.cpp
//
// Programmer: Zergra( Park Jongtae ) in GamaSoft corp.
//
// File Desciption: 월드 웨폰
//
// Date: 2004. 12. 14. (화)
//--------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------
// Headers
//--------------------------------------------------------------------------------------------------------------------------
#include "WorldWeapon.h"
#include <Creature/Siege/SiegeObjectMgr.h>
#include <Community/Guild/Guild.h>
#include <Community/Guild/GuildMgr.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/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Network/Dispatch/GameClient/SendCharCastle.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
// class CWorldWeapon
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------------------------------------------------------------
// Constrcutor, Destructor
//--------------------------------------------------------------------------------------------------------------------------
CWorldWeapon::CWorldWeapon(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_iFireX(0), m_iFireZ(0)
{
}
CWorldWeapon::~CWorldWeapon()
{
}
//--------------------------------------------------------------------------------------------------------------------------
// Member Functions
//--------------------------------------------------------------------------------------------------------------------------
bool CWorldWeapon::Build(unsigned char cUpgradeStep)
{
CCamp::Build(cUpgradeStep);
// 월드 웨폰 인챈트 적용
unsigned char cNation = Creature::STATELESS;
switch (m_wObjectType)
{
case Siege::KARTERANT_WEAPON: cNation = Creature::KARTERANT; break;
case Siege::MERKADIA_WEAPON: cNation = Creature::MERKADIA; break;
}
CCreatureManager::GetInstance().AddWorldWeaponEnchant(reinterpret_cast<CAggresiveCreature*>(this), cNation);
return true;
}
bool CWorldWeapon::Destroy(unsigned long dwOffencerGID)
{
// 월드 웨폰일 경우 파괴 정보 업데이트
if (Siege::KARTERANT_WEAPON == m_wObjectType)
{
CSiegeObjectMgr::GetInstance().UpdateWorldWeaponInfo(Creature::KARTERANT, Siege::WEAPON_REBUILD_SIEGE_TIME_COUNT);
}
else if (Siege::MERKADIA_WEAPON == m_wObjectType)
{
CSiegeObjectMgr::GetInstance().UpdateWorldWeaponInfo(Creature::MERKADIA, Siege::WEAPON_REBUILD_SIEGE_TIME_COUNT);
}
return CCamp::Destroy(dwOffencerGID);
}
bool CWorldWeapon::UpdateWeaponState(unsigned char cWeaponState, int iX, int iZ)
{
m_iFireX = iX;
m_iFireZ = iZ;
m_cSubState = cWeaponState;
m_iWorldWeaponCount = Siege::WEAPON_FIRE_WARNING_COUNT;
// 해당 진지가 있는 반경 5셀 이내에 전송
PktCampCmd pktCC;
pktCC.m_dwCID = m_dwCID;
pktCC.m_dwCampID = GetCampID();
pktCC.m_cState = m_cState;
pktCC.m_dwValue1 = iX;
pktCC.m_dwValue2 = iZ;
pktCC.m_cSubCmd = 0;
switch (cWeaponState)
{
case Siege::WEAPON_CHARGE:
{
pktCC.m_cSubCmd = PktCampCmd::WORLDWEAPON_CHARGE;
pktCC.m_dwValue1 = m_cMaterial;
pktCC.m_dwValue2 = 0;
}
break;
case Siege::WEAPON_READY: pktCC.m_cSubCmd = PktCampCmd::WORLDWEAPON_CHARGE_COMPLETE; break;
case Siege::WEAPON_FIRE: pktCC.m_cSubCmd = PktCampCmd::WORLDWEAPON_FIRE; 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;
}
bool CWorldWeapon::FireWarning()
{
// 경고 메세지를 모든 캐릭터에게 전송
PktCampMessage pktCampMessage;
memset(&pktCampMessage, 0, sizeof(PktCampMessage));
pktCampMessage.m_dwCampID = GetCampID();
pktCampMessage.m_dwGID = GetGID();
pktCampMessage.m_cZone = CServerSetup::GetInstance().GetServerZone();
pktCampMessage.m_cMsgCmd = PktCampMessage::MSGCMD_WEAPON_FIRE_WARNING;
pktCampMessage.m_cRemainTime = static_cast<unsigned char>(m_iWorldWeaponCount);
pktCampMessage.m_cNation = GetNation();
pktCampMessage.m_bNotify = true;
pktCampMessage.m_nValue1 = m_iFireX;
pktCampMessage.m_nValue2 = m_iFireZ;
Guild::CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(m_dwGID);
if (NULL != lpGuild)
{
strcpy(pktCampMessage.m_szGuildName, lpGuild->GetName());
}
char* szPacket = reinterpret_cast<char* >(&pktCampMessage);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampMessage), CmdCampMessage, 0, 0))
{
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCampMessage), CmdCampMessage);
}
--m_iWorldWeaponCount;
return true;
}
bool CWorldWeapon::Fire()
{
// 월드 웨폰일 경우 파괴 정보 업데이트
if (Siege::KARTERANT_WEAPON == m_wObjectType)
{
CSiegeObjectMgr::GetInstance().UpdateWorldWeaponInfo(Creature::KARTERANT, Siege::WEAPON_REBUILD_SIEGE_TIME_COUNT);
}
else if (Siege::MERKADIA_WEAPON == m_wObjectType)
{
CSiegeObjectMgr::GetInstance().UpdateWorldWeaponInfo(Creature::MERKADIA, Siege::WEAPON_REBUILD_SIEGE_TIME_COUNT);
}
// DB 중계 서버에 월드 웨폰 객체 삭제 명령 전송 (발사 완료, 시간 기록)
// 중계 서버로 패킷 전송
GET_SINGLE_DISPATCH(lpDBAgentDispatch, CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (NULL != lpDBAgentDispatch)
{
GameClientSendPacket::SendCharCampCmdToDBAgent(lpDBAgentDispatch->GetSendStream(), 0, GetCampID(),
0, 0, PktCampCmd::WORLDWEAPON_FIRE_COMPLETE, PktBase::NO_SERVER_ERR);
}
// 모든 캐릭터에게 월드 웨폰 발사 명령 전송 (이펙트 처리 및 월드 웨폰 객체 삭제를 위해)
PktCampCmd pktCampCmd;
pktCampCmd.m_dwCID = m_dwCID;
pktCampCmd.m_dwCampID = GetCampID();
pktCampCmd.m_cState = GetState();
pktCampCmd.m_dwValue1 = m_iFireX;
pktCampCmd.m_dwValue2 = m_iFireZ;
pktCampCmd.m_cSubCmd = PktCampCmd::WORLDWEAPON_FIRE_COMPLETE;
char* szPacket = reinterpret_cast<char *>(&pktCampCmd);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktCampCmd), CmdCampCmd, 0, 0))
{
CCreatureManager::GetInstance().SendAllCharacter(szPacket, sizeof(PktCampCmd), CmdCampCmd);
}
// 모든 캐릭터 및 공성 오브젝트에 대해서 데미지 처리
CCreatureManager::GetInstance().ProcessWorldWeaponDamage(m_iFireX, m_iFireZ, m_cUpgradeStep);
// 모든 캐릭터의 월드 웨폰 인챈트 해제
CCreatureManager::GetInstance().ClearWorldWeaponEnchant();
return true;
}

View File

@@ -0,0 +1,54 @@
//--------------------------------------------------------------------------------------------------------------------------
// File Name: WorldWeapon.h
//
// Programmer: Zergra( Park Jongtae ) in GamaSoft corp.
//
// File Desciption: 월드 웨폰
//
// Date: 2004. 12. 14. (화)
//--------------------------------------------------------------------------------------------------------------------------
#ifndef _WORLD_WEAPON_OBJECT_H_
#define _WORLD_WEAPON_OBJECT_H_
#pragma once
//--------------------------------------------------------------------------------------------------------------------------
// Headers
//--------------------------------------------------------------------------------------------------------------------------
#include <Creature/Siege/Camp.h>
//--------------------------------------------------------------------------------------------------------------------------
// Classes
//--------------------------------------------------------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
// class CWorldWeapon
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
class CWorldWeapon : public CCamp
{
public:
virtual ~CWorldWeapon();
bool Build(unsigned char cUpgradeStep = 0); // 월드 웨폰 구축 완료
bool Destroy(unsigned long dwOffencerGID = 0); // 월드 웨폰 파괴 완료 (자체 파괴, 파괴됨)
bool UpdateWeaponState(unsigned char cWeaponState, int iX = 0, int iZ = 0);
bool FireWarning();
bool Fire();
int GetFireCount() const { return m_iWorldWeaponCount; }
private:
CWorldWeapon(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);
int m_iFireX, m_iFireZ; // 발사 좌표
int m_iWorldWeaponCount; // 월드 웨폰 발사 카운트
friend class CSiegeObjectMgr;
};
#endif //_WORLD_WEAPON_OBJECT_H_