DragonNest/Server/DNVillageServer/DNUserSkill.cpp
2024-12-19 09:48:26 +08:00

1564 lines
No EOL
48 KiB
C++

#include "StdAfx.h"
#include "DNUserSkill.h"
#include "DNUserSession.h"
#include "DNGameDataManager.h"
#include "DNLogConnection.h"
#include "DNMissionSystem.h"
#include "DNDBConnectionManager.h"
#include "DNDBConnection.h"
extern TVillageConfig g_Config;
CDNUserSkill::CDNUserSkill(CDNUserSession *pUserObj)
: m_pUserSession(pUserObj)
{
m_pSkillData = m_pUserSession->GetSkillData( true );
for(DWORD i=0; i<DualSkill::Type::MAX; i++ )
m_SkillList[i].clear();
}
CDNUserSkill::~CDNUserSkill(void)
{
for(DWORD i=0; i<DualSkill::Type::MAX; i++ )
m_SkillList[i].clear();
}
void CDNUserSkill::LoadUserData()
{
int nUseSkillPoint = 0;
int nOpendSkillPageCount = m_pUserSession->GetItem()->GetSkillPageCount();
for(int nSkillCount =0 ; nSkillCount < nOpendSkillPageCount; nSkillCount++)
{
for (int i = 0; i < SKILLMAX; i++){
if (m_pSkillData[nSkillCount].SkillList[i].nSkillID <= 0) continue;
// 데이터에 없는 스킬 ID가 DB 에 저장되어있다면 읽어들이지 않는다.
const TSkillData* pSkillData = g_pDataManager->GetSkillData( m_pSkillData[nSkillCount].SkillList[i].nSkillID );
if( NULL == pSkillData )
continue;
m_SkillList[nSkillCount][m_pSkillData[nSkillCount].SkillList[i].nSkillID] = m_pSkillData[nSkillCount].SkillList[i];
CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO SkillInfo;
SkillInfo.iSkillID = m_pSkillData[nSkillCount].SkillList[i].nSkillID;
SkillInfo.iSkillLevel = m_pSkillData[nSkillCount].SkillList[i].cSkillLevel;
SkillInfo.bCurrentLock = m_pSkillData[nSkillCount].SkillList[i].bLock;
m_vlPossessedSkill[nSkillCount].push_back( SkillInfo );
if (m_pSkillData[nSkillCount].SkillList[i].nSkillID > 0){
for (int j = 0; j < m_pSkillData[nSkillCount].SkillList[i].cSkillLevel; j++){
nUseSkillPoint += g_pDataManager->GetNeedSkillPoint(m_pSkillData[nSkillCount].SkillList[i].nSkillID, j + 1);
}
}
}
// 문장스킬 체크
const TItem *pItem = m_pUserSession->GetItem()->GetGlyph( GLYPH_SPECIALSKILL );
if( pItem && pItem->nItemID ) {
if( pItem->nItemID > 0 ) {
TItemData *pItemData = g_pDataManager->GetItemData( pItem->nItemID );
if( pItemData && (pItemData->nSkillID > 0) ) {
TSkillData *pSkillData = g_pDataManager->GetSkillData( pItemData->nSkillID );
TSkill Skill;
Skill.nSkillID = pSkillData->nSkillID;
Skill.cSkillLevel = 1;
Skill.nCoolTime = 0;
Skill.bLock = false;
m_SkillList[nSkillCount][pItemData->nSkillID] = Skill;
CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO SkillInfo;
SkillInfo.iSkillID = pItemData->nSkillID;
SkillInfo.iSkillLevel = Skill.cSkillLevel;
SkillInfo.bCurrentLock = Skill.bLock;
m_vlPossessedSkill[nSkillCount].push_back( SkillInfo );
}
}
}
}
#ifdef PRE_ADD_SP_REVISION
#ifdef _FINAL_BUILD
// 보유 스킬 포인트 검증. 틀어져 있더라도 보정 후 클라에게 따로 패킷은 안보낸다.
// 여기 지나간 다음에 클라이언트에게 정보가 보내지기 때문에..
CheckAndRevisionSkillPoint( false );
#endif // #ifdef _FINAL_BUILD
#endif // #ifdef PRE_ADD_SP_REVISION
}
void CDNUserSkill::SaveUserData(TUserData *pUserData)
{
if (m_SkillList[GetCurrentSkillPage()].empty()) return;
memset(&(pUserData->Skill[DualSkill::Type::Primary].SkillList), 0, sizeof(TSkill) * SKILLMAX);
TMapSkillList::iterator iter;
int nCount = 0;
for (iter = m_SkillList[GetCurrentSkillPage()].begin(); iter != m_SkillList[GetCurrentSkillPage()].end(); ++iter){
pUserData->Skill[DualSkill::Type::Primary].SkillList[nCount] = iter->second;
pUserData->Skill[DualSkill::Type::Primary].SkillList[nCount].nCoolTime = 0;
nCount++;
}
}
// 스킬 트리로 바뀌면서 addskill 치트에서만 쓰이는 함수입니다..
void CDNUserSkill::AddSkill( int nSkillID, int iLevel/* = 0*/ )
{
// 20080901 로그서버 스킬추가[획득]
if ( nSkillID <= 0 )
{
m_pUserSession->SendAddSkill(nSkillID, ERROR_SKILL_ACQUIREFAIL);
return;
}
// 획득한 스킬 또 획득할 순 없음.
if( FindSkill( nSkillID ) )
{
m_pUserSession->SendAddSkill(nSkillID, ERROR_SKILL_ACQUIREFAIL);
return;
}
TSkill Skill;
memset(&Skill, 0, sizeof(TSkill));
Skill.nSkillID = nSkillID;
Skill.cSkillLevel = iLevel;
//Skill.bLock = true; // 최초에 스킬 얻었을 때는 언락 상태. (레벨 0 상태)
Skill.bLock = false;
m_SkillList[GetCurrentSkillPage()][nSkillID] = Skill;
CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO SkillInfo;
SkillInfo.iSkillID = nSkillID;
SkillInfo.iSkillLevel = Skill.cSkillLevel;
SkillInfo.bCurrentLock = Skill.bLock;
m_vlPossessedSkill[GetCurrentSkillPage()].push_back( SkillInfo );
if( m_pUserSession )
{
m_pUserSession->GetDBConnection()->QueryAddSkill(m_pUserSession, nSkillID, Skill.cSkillLevel, 0, 0, DBDNWorldDef::SkillChangeCode::GainByAdmin);
m_pUserSession->SendAddSkill(nSkillID, ERROR_NONE);
m_pUserSession->GetEventSystem()->OnEvent( EventSystem::OnSkillAdd, 1, EventSystem::SkillID, nSkillID );
}
}
void CDNUserSkill::DelSkill(int nSkillID)
{
if (nSkillID <= 0){
m_pUserSession->SendDelSkill(nSkillID, ERROR_SKILL_DELETEFAIL);
return;
}
if (m_SkillList[GetCurrentSkillPage()].empty()){
m_pUserSession->SendDelSkill(nSkillID, ERROR_SKILL_ISNOT_YOURS);
return;
}
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find(nSkillID);
if (iter != m_SkillList[GetCurrentSkillPage()].end()){
m_SkillList[GetCurrentSkillPage()].erase(iter);
vector<CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO>::iterator iter = m_vlPossessedSkill[GetCurrentSkillPage()].begin();
for( iter; m_vlPossessedSkill[GetCurrentSkillPage()].end() != iter; ++iter )
{
if( iter->iSkillID == nSkillID )
{
m_vlPossessedSkill[GetCurrentSkillPage()].erase( iter );
break;
}
}
m_pUserSession->SendDelSkill(nSkillID, ERROR_NONE);
return;
}
if( m_pUserSession )
{
m_pUserSession->GetDBConnection()->QueryDelSkill(m_pUserSession, nSkillID, DBDNWorldDef::SkillChangeCode::DelByAdmin, false, GetCurrentSkillPage() );
m_pUserSession->SendDelSkill(nSkillID, ERROR_SKILL_ISNOT_YOURS);
}
}
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
int CDNUserSkill::SkillLevelUp( int nSkillID, int nUsePoint )
#else
void CDNUserSkill::SkillLevelUp( int nSkillID, int nUsePoint )
#endif
{
#ifdef PRE_ADD_SP_REVISION
// #29463 이슈 관련 현재 보유 스킬 포인트와 스킬 소유 리스트가 다른 경우엔 여기서 보정해준다.
#ifdef _FINAL_BUILD
CheckAndRevisionSkillPoint();
#endif // #ifdef _FINAL_BUILD
#endif // #ifdef PRE_ADD_SP_REVISION
bool bReqSuccess = false;
int nError = ERROR_NONE;
bool bFinded = false;
if (nSkillID <= 0)
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_GENERIC_UNKNOWNERROR;;
#else
return;
#endif//#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
if (m_SkillList[GetCurrentSkillPage()].empty())
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_GENERIC_UNKNOWNERROR;;
#else
return;
#endif//#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find(nSkillID);
if (iter != m_SkillList[GetCurrentSkillPage()].end())
bFinded = true;
else
{
_ASSERT( "스킬 리스트에서 스킬을 찾을 수 없습니다." );
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_GENERIC_UNKNOWNERROR;;
#else
return;
#endif //#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
}
TSkill& SkillInfo = iter->second;
DNTableFileFormat* pSkillLevelTable = GetDNTable( CDnTableDB::TSKILLLEVEL );
vector<int> vlSkillLevelList;
if( pSkillLevelTable->GetItemIDListFromField( "_SkillIndex", nSkillID, vlSkillLevelList ) == 0 )
{
_ASSERT( "스킬 테이블 데이터 오류" );
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_GENERIC_UNKNOWNERROR;;
#else
return;
#endif//#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
}
#if defined(PRE_FIX_NEXTSKILLINFO)
int nLevelUpCount = 1; // 스킬레벨은 무조건 1씩 올리게 되어있음.
SKILL_LEVEL_TABLE_IDS::iterator findIter;
SKILL_LEVEL_INFO* pTableInfo = g_pDataManager->GetSkillLevelTableIDList(nSkillID, 0);
int nNeedSkillPoint = 0;
bool bValidPacket = false;
if( NULL != pTableInfo )
{
int nSkillLevel = SkillInfo.cSkillLevel + nLevelUpCount;
findIter = pTableInfo->_SkillLevelTableIDs.find(nSkillLevel);
int SkillLevelTableID = -1;
if (findIter != pTableInfo->_SkillLevelTableIDs.end())
SkillLevelTableID = findIter->second;
if( -1 != SkillLevelTableID )
{
nNeedSkillPoint = pSkillLevelTable->GetFieldFromLablePtr( SkillLevelTableID, "_NeedSkillPoint" )->GetInteger();
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
if( nNeedSkillPoint > 0 )
#else
if( nNeedSkillPoint == nUsePoint )
#endif
bValidPacket = true;
}
}
if( false == bValidPacket )
{
// 패킷의 데이터가 서버와 다르므로 에러로 처리해서 돌려보낸다.
nError = ERROR_SKILL_LEVELUPFAIL_INVALID_SKILLPOINT;
bFinded = false;
}
#else
int iSkillLevelTableID = -1;
for( int i = 0; i < (int)vlSkillLevelList.size(); ++i )
{
int iNowLevel = pSkillLevelTable->GetFieldFromLablePtr( vlSkillLevelList.at(i), "_SkillLevel" )->GetInteger();
if( iNowLevel == SkillInfo.cSkillLevel )
{
iSkillLevelTableID = vlSkillLevelList.at( i );
break;
}
}
if( -1 == iSkillLevelTableID )
{
_ASSERT( "스킬 테이블 데이터 오류" );
return;
}
int nNeedSkillPoint = 0;
int nLevelUpCount = 0;
while( nUsePoint != nNeedSkillPoint )
{
++nLevelUpCount;
nNeedSkillPoint += pSkillLevelTable->GetFieldFromLablePtr( iSkillLevelTableID+nLevelUpCount, "_NeedSkillPoint" )->GetInteger();
if( nUsePoint < nNeedSkillPoint )
{
nError = ERROR_SKILL_LEVELUPFAIL_INVALID_SKILLPOINT;
bFinded = false;
break;
}
}
#endif // PRE_FIX_NEXTSKILLINFO
// 해당 스킬을 갖고 있는지 체크
// 스킬 안 갖고 있는 경우엔 초기 데이터가 뭔지 물어봐서 알아내자.
//bool bFinded = FindSkill( nSkillID );
if( bFinded )
{
// 보유한 스킬 포인트가 사용하겠다고 날아온 포인트보다 작음
int iAvailSPByJob = GetAvailSkillPointByJob( nSkillID );
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
if( nNeedSkillPoint <= iAvailSPByJob )
#else
if( nNeedSkillPoint <= iAvailSPByJob && nUsePoint == nNeedSkillPoint )
#endif
{
int iLevelFrom = GetSkillLevel( nSkillID );
// 스킬 레벨 제한 체크
TSkillData *pSkillData = g_pDataManager->GetSkillData(nSkillID);
if (!pSkillData){
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_SKILL_LEVELUPFAIL_ISNOT_YOURS;;
#else
m_pUserSession->SendSkillLevelUp( nSkillID, 0, ERROR_SKILL_LEVELUPFAIL_ISNOT_YOURS );
return;
#endif//#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
}
int iNewLevel = iLevelFrom + nLevelUpCount;
if( iNewLevel <= pSkillData->nMaxLevel )
{
TSkillLevelData *pSkillLevel = g_pDataManager->GetSkillLevelData(nSkillID, iNewLevel);
if (!pSkillLevel){
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_SKILL_LEVELUPFAIL_ISNOT_YOURS;
#else
m_pUserSession->SendSkillLevelUp( nSkillID, 0, ERROR_SKILL_LEVELUPFAIL_ISNOT_YOURS );
return;
#endif
}
if( pSkillLevel->cLevelLimit <= (int)m_pUserSession->GetLevel() )
{
if( 0 < m_pUserSession->GetSkillPoint() )
{
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
SetSkillLevel( nSkillID, iNewLevel, DBDNWorldDef::SkillChangeCode::Use, false ); // 스킬포인트 안에서 차감
#else
SetSkillLevel( nSkillID, iNewLevel, DBDNWorldDef::SkillChangeCode::Use ); // 스킬포인트 안에서 차감
#endif //#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
bReqSuccess = true;
}
else
{
// 스킬 레벨업 포인트 부족함. ERROR_SKILL_LEVELUPFAIL_INSUFFICIENCY_SKILLPOINT 클라로 리턴
nError = ERROR_SKILL_LEVELUPFAIL_INSUFFICIENCY_SKILLPOINT;
}
}
else
{
//TODO: 캐릭터의 레벨이 스킬의 제한 레벨 보다 작습니다..
nError = ERROR_SKILL_LEVELUPFAIL_INSUFFICIENCY_CHARACTERLEVEL;
}
}
else
{
//TODO: 최대로 정해져 있는 스킬 레벨 이상을 올리려고 함..
nError = ERROR_SKILL_LEVELUPFAIL_ALREADY_MAX;
}
}
else
{
// TODO: 실제 사용 스킬포인트보다 보유 포인트보다 적거나 테이블의 데이터와 다름.
nError = ERROR_SKILL_LEVELUPFAIL_INVALID_SKILLPOINT;
}
}
else
{
// 해당 스킬 보유하고 있지 않음. ERROR_SKILL_LEVELUPFAIL_ISNOT_YOURS 클라로 리턴
nError = ERROR_SKILL_LEVELUPFAIL_ISNOT_YOURS;
}
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return nError;
#else
if (!bReqSuccess)
m_pUserSession->SendSkillLevelUp( nSkillID, 0, nError );
#endif
}
bool CDNUserSkill::FindSkill(int nSkillID)
{
if (nSkillID <= 0) return false;
if (m_SkillList[GetCurrentSkillPage()].empty()) return false;
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find(nSkillID);
if (iter != m_SkillList[GetCurrentSkillPage()].end()){
return true;
}
return false;
}
int CDNUserSkill::GetSkillLevel( int nSkillID )
{
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find( nSkillID );
if( iter != m_SkillList[GetCurrentSkillPage()].end() ) {
return iter->second.cSkillLevel;
}
return -1;
}
void CDNUserSkill::SetSkillLevel( int nSkillID, int nLevel, int nLogCode, bool bSendSkillLevelUp /*= true*/ )
{
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find( nSkillID );
if( iter != m_SkillList[GetCurrentSkillPage()].end() ) {
int nGap = nLevel - iter->second.cSkillLevel;
iter->second.cSkillLevel = nLevel;
//////////////////////////////////////////////////////////////////////////
// 게임서버와 마찬가지로 레벨이 한꺼번에 변하는 경우도 감안.
// 현재 1레벨씩만 올리게 되어있지만 게임서버와 동일하게 맞춰준다.
int nNeedSkillPoint = 0;
for( int i = 0; i < nGap; ++i )
nNeedSkillPoint += g_pDataManager->GetNeedSkillPoint(nSkillID, nLevel-i);
//////////////////////////////////////////////////////////////////////////
if (nNeedSkillPoint > 0) m_pUserSession->ChangeSkillPoint(-nNeedSkillPoint, nSkillID, false, 0, GetCurrentSkillPage() );
m_pUserSession->GetDBConnection()->QueryModSkillLevel( m_pUserSession, nSkillID, nLevel, iter->second.nCoolTime, -nNeedSkillPoint, nLogCode); // db저장: 스킬포인트까지 같이 업데이트
if( bSendSkillLevelUp )
m_pUserSession->SendSkillLevelUp( nSkillID, nLevel, ERROR_NONE );
vector<CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO>::iterator iter = m_vlPossessedSkill[GetCurrentSkillPage()].begin();
for( iter; m_vlPossessedSkill[GetCurrentSkillPage()].end() != iter; ++iter )
{
if( iter->iSkillID == nSkillID )
{
iter->iSkillLevel = nLevel;
break;
}
}
m_pUserSession->GetEventSystem()->OnEvent( EventSystem::OnSkillLevelUp, 2, EventSystem::SkillID, nSkillID, EventSystem::SkillLevel, nLevel );
}
}
// 스킬 언락. 레벨 0인 상태로 캐릭터의 보유 스킬에 추가.
void CDNUserSkill::UnLockSkill( int nSkillID )
{
TSkill Skill;
memset(&Skill, 0, sizeof(TSkill));
Skill.nSkillID = nSkillID;
Skill.cSkillLevel = 0;
Skill.bLock = false; // 락 변수는 필요 없을듯;;
m_SkillList[GetCurrentSkillPage()][nSkillID] = Skill;
// 따로 필요한 정보만 모아놓은 벡터에 추가.
CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO SkillInfo;
SkillInfo.iSkillID = nSkillID;
SkillInfo.iSkillLevel = 0;
SkillInfo.bCurrentLock = false;
m_vlPossessedSkill[GetCurrentSkillPage()].push_back( SkillInfo );
m_pUserSession->SendUnlockSkill( nSkillID, ERROR_NONE );
m_pUserSession->GetDBConnection()->QueryAddSkill(m_pUserSession, nSkillID, SkillInfo.iSkillLevel, 0, DBDNWorldDef::SkillChangeCode::GainByBook, 0);
// TODO: 스킬 획득 관련 미션 쪽 물어봐서 처리.
//m_pUserSession->GetEventSystem()->OnEvent( EventSystem::OnSkillAdd, 1, EventSystem::SkillID, nSkillID );
return;
}
// 마을에서 스킬 트레이너 NPC 로 돈으로 언락. 현재 획득까지 처리.
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
int CDNUserSkill::UnLockSkillByMoney( int nSkillID )
#else
void CDNUserSkill::UnLockSkillByMoney( int nSkillID )
#endif
{
#ifdef PRE_ADD_SP_REVISION
// #29463 이슈 관련 현재 보유 스킬 포인트와 스킬 소유 리스트가 다른 경우엔 여기서 보정해준다.
#ifdef _FINAL_BUILD
CheckAndRevisionSkillPoint();
#endif // #ifdef _FINAL_BUILD
#endif // #ifdef PRE_ADD_SP_REVISION
if (nSkillID <= 0){
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_SKILL_ACQUIREFAIL;
#else
m_pUserSession->SendAcquireSkill( nSkillID, ERROR_SKILL_ACQUIREFAIL );
return;
#endif //#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
}
// 보유하고 있는 돈으로 되는지 확인.
TSkillData* pSkillData = g_pDataManager->GetSkillData( nSkillID );
if (!pSkillData){
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_SKILL_ACQUIREFAIL;
#else
m_pUserSession->SendAcquireSkill( nSkillID, ERROR_SKILL_ACQUIREFAIL );
return;
#endif
}
// 같이 못배우게 한 스킬은 배울 수 없다.
if( IsExclusiveSkill( nSkillID, pSkillData->nExclusiveID ) )
{
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_SKILL_ACQUIRE_FAIL_EXCLUSIVE;
#else
m_pUserSession->SendAcquireSkill( nSkillID, ERROR_SKILL_ACQUIRE_FAIL_EXCLUSIVE );
return;
#endif
}
// #36858 글로벌 스킬로 서로 엮여 있다면 해당 그룹중에 하나만 배워도 부모 스킬 조건없이 배울 수 있다.
bool bAlreadyGlobalSkillAcquired = false;
if( HasSameGlobalIDSkill( pSkillData->nGlobalSkillGroup ) )
bAlreadyGlobalSkillAcquired = true;
INT64 iNowMoney = m_pUserSession->GetCoin();
if( iNowMoney < pSkillData->nUnlockPrice )
{
//m_pUserSession->SendUnlockSkill( nSkillID, ERROR_SKILL_UNLOCK_NOT_ENOUGH_MONEY );
// 클라에서 메시지 핸들링 보여주는 것을 전부 획득에서 처리하므로..
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_SKILL_UNLOCK_NOT_ENOUGH_MONEY;
#else
m_pUserSession->SendAcquireSkill( nSkillID, ERROR_SKILL_UNLOCK_NOT_ENOUGH_MONEY );
return;
#endif
}
bool bAlreadyPossess = this->FindSkill( nSkillID );
if( bAlreadyPossess )
{
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_SKILL_UNLOCK_ALREADY_OPEN;
#else
m_pUserSession->SendAcquireSkill( nSkillID, ERROR_SKILL_UNLOCK_ALREADY_OPEN );
return;
#endif
}
// 그러면 획득 조건을 검사해서 획득함.
int nRetCode = ERROR_NONE;
CDnSkillTreeSystem* pSkillTreeSystem = g_pDataManager->GetSkillTreeSystem();
#ifdef PRE_ADD_ONLY_SKILLBOOK
if( pSkillTreeSystem )
{
bool bExistSkill = false;
bool bNeedSkillBook = pSkillTreeSystem->IsNeedSkillBook( nSkillID, bExistSkill );
if( bExistSkill && bNeedSkillBook )
{
return ERROR_SKILL_UNLOCK_FAIL;
}
}
#endif // PRE_ADD_ONLY_SKILLBOOK
CDnSkillTreeSystem::S_OUTPUT Output;
CDnSkillTreeSystem::S_TRY_ACQUIRE TryAcquire( GetPossessedSkillInfo() );
TryAcquire.iCurrentCharLevel = m_pUserSession->GetLevel();
TryAcquire.iTryAcquireSkillID = nSkillID;
// 직업별로 정해져 있는 쓸 수 있는 SP 를 기준으로 한다.
TryAcquire.iHasSkillPoint = GetAvailSkillPointByJob( nSkillID );
pSkillTreeSystem->TryAcquireSkill( TryAcquire, &Output );
bool bIgnoreParentSkillCondition = ( (CDnSkillTreeSystem::R_DONT_HAVE_PARENT_SKILL == Output.eResult) ||
(CDnSkillTreeSystem::R_LOCKED_PARENTSKILL == Output.eResult) ||
(CDnSkillTreeSystem::R_NOT_ENOUGH_PARENT_SKILL_LEVEL == Output.eResult) ) &&
true == bAlreadyGlobalSkillAcquired;
if( CDnSkillTreeSystem::R_SUCCESS == Output.eResult || true == bIgnoreParentSkillCondition )
{
// 스킬 리스트에 스킬 추가.
TSkill Skill;
memset(&Skill, 0, sizeof(TSkill));
Skill.nSkillID = nSkillID;
Skill.cSkillLevel = 0;
Skill.bLock = false; // 락 변수는 필요 없을듯;;
m_SkillList[GetCurrentSkillPage()][nSkillID] = Skill;
// 따로 필요한 정보만 모아놓은 벡터에 추가.
CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO SkillInfo;
SkillInfo.iSkillID = nSkillID;
SkillInfo.iSkillLevel = 0;
SkillInfo.bCurrentLock = false;
m_vlPossessedSkill[GetCurrentSkillPage()].push_back( SkillInfo );
// 돈 차감
m_pUserSession->DelCoin( pSkillData->nUnlockPrice, DBDNWorldDef::CoinChangeCode::DoNotDBSave, 0 );
// 언락 패킷 클라로 보냄.
#if !defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
m_pUserSession->SendUnlockSkill( nSkillID, ERROR_NONE );
#endif // #if !defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
m_pUserSession->GetDBConnection()->QueryAddSkill(m_pUserSession, nSkillID, SkillInfo.iSkillLevel, 0, DBDNWorldDef::SkillChangeCode::GainByBuy, pSkillData->nUnlockPrice);
// TODO: 스킬 획득 관련 미션 쪽 물어봐서 처리.
//m_pUserSession->GetEventSystem()->OnEvent( EventSystem::OnSkillAdd, 1, EventSystem::SkillID, nSkillID );
SetSkillLevel(nSkillID, 1, DBDNWorldDef::SkillChangeCode::Use, false); // 함수안에서 스킬포인트 수정
// 획득 성공 결과를 최종 통보.CDnSkillTreeSystem::S_OUTPUT Output;CDnSkillTreeSystem::S_OUTPUT Output;
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_NONE;
#else
m_pUserSession->SendAcquireSkill( nSkillID, ERROR_NONE );
#endif
}
else
{
switch( Output.eResult )
{
// 캐릭터 요구레벨이 모자람.
case CDnSkillTreeSystem::R_NOT_ENOUGH_CHAR_LEVEL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_CHAR_LEVEL;
break;
// 선행(부모) 스킬이 없음.
case CDnSkillTreeSystem::R_DONT_HAVE_PARENT_SKILL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_DONT_HAVE_PARENT_SKILL;
break;
// 부모 스킬의 레벨이 충족되지 않음.
case CDnSkillTreeSystem::R_NOT_ENOUGH_PARENT_SKILL_LEVEL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_PARENT_SKILL_LEVEL;
break;
// 스킬 포인트가 모자라서 스킬을 획득할 수 없음.
case CDnSkillTreeSystem::R_NOT_ENOUGH_SKILLPOINT_TO_ACQUIRE:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_SKILLPOINT;
break;
default:
nRetCode = ERROR_SKILL_UNLOCK_FAIL;
break;
}
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return nRetCode;
#else
m_pUserSession->SendAcquireSkill( nSkillID, nRetCode );
#endif
}
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_NONE;
#endif
}
bool CDNUserSkill::UseSkillBook( int nItemID )
{
#ifdef PRE_ADD_SP_REVISION
// #29463 이슈 관련 현재 보유 스킬 포인트와 스킬 소유 리스트가 다른 경우엔 여기서 보정해준다.
#ifdef _FINAL_BUILD
CheckAndRevisionSkillPoint();
#endif // #ifdef _FINAL_BUILD
#endif // #ifdef PRE_ADD_SP_REVISION
CDnSkillTreeSystem* pSkillTreeSystem = g_pDataManager->GetSkillTreeSystem();
// 스킬북 사용, 트레이너에게 돈 지불 시에 스킬 언락 및 획득까지 일괄 처리되도록 변경됨.
// SP 가 부족하거나 선행 스킬 없거나, 선행 스킬의 레벨이 낮거나, 캐릭터의 레벨이 낮은 경우 전부 체크해서 통과 되었을 경우에
// 스킬 언락 및 1렙으로 찍어준다.
// 이미 언락한 스킬은 다시 언락할 수 없다.
// 언락된 상태인 레벨 0일 때도 스킬 소유로 할 것인지는 고민해보자.
int iSkillIDToUnLock = pSkillTreeSystem->FindSkillBySkillBook( nItemID );
if( 0 == iSkillIDToUnLock )
{
g_Log.Log( LogType::_ERROR, L"[UseSkillBook Fail!!!] Can't find SkillID that using %d SkillBook Item ID\n", iSkillIDToUnLock );
_ASSERT( !"해당 스킬북 아이템 ID 로 설정된 스킬을 찾을 수 없음." );
return false;
}
// 같이 못배우게 한 스킬은 배울 수 없다.
const TSkillData* pSkillData = g_pDataManager->GetSkillData( iSkillIDToUnLock );
if( IsExclusiveSkill( iSkillIDToUnLock, pSkillData->nExclusiveID ) )
{
m_pUserSession->SendAcquireSkill( iSkillIDToUnLock, ERROR_SKILL_ACQUIRE_FAIL_EXCLUSIVE );
return false;
}
// #36858 글로벌 스킬로 서로 엮여 있다면 해당 그룹중에 하나만 배워도 부모 스킬 조건없이 배울 수 있다.
bool bAlreadyGlobalSkillAcquired = false;
if( HasSameGlobalIDSkill( pSkillData->nGlobalSkillGroup ) )
bAlreadyGlobalSkillAcquired = true;
bool bSuccess = false;
bool bAlreadyPossess = this->FindSkill( iSkillIDToUnLock );
int nRetCode = ERROR_NONE;
int nResultSkillID = 0;
if( false == bAlreadyPossess )
{
CDnSkillTreeSystem::S_OUTPUT Output;
CDnSkillTreeSystem::S_TRY_UNLOCK CurrentSkillInfo;
CurrentSkillInfo.iTryUnlockSkillID = iSkillIDToUnLock;
CurrentSkillInfo.iCurrentCharLevel = m_pUserSession->GetLevel();
CurrentSkillInfo.iSkillBookItemID = nItemID;
// 스킬이 요구하는 직업이 히스토리에 있는지 적절히 찾아서 처리.
TSkillData* pSkillData = g_pDataManager->GetSkillData( iSkillIDToUnLock );
if (pSkillData == NULL)
{
g_Log.Log( LogType::_ERROR, L"[UseSkillBook - GetSkillData Fail!!!] Can't find %d SkillID\n", iSkillIDToUnLock );
return false;
}
CurrentSkillInfo.iJobID = -1;
for( int i = 0; i < JOBMAX; ++i )
{
BYTE cJob = m_pUserSession->GetStatusData()->cJobArray[ i ];
if( 0 < cJob )
{
if( cJob == pSkillData->nNeedJobID )
{
CurrentSkillInfo.iJobID = pSkillData->nNeedJobID;
}
}
else
break;
}
if( -1 != CurrentSkillInfo.iJobID )
{
pSkillTreeSystem->TryUnLockSkill( CurrentSkillInfo, &Output );
// 결과에 따라 클라이언트에 통보.
if( CDnSkillTreeSystem::R_SUCCESS == Output.eResult )
{
// 성공.
nResultSkillID = iSkillIDToUnLock;
// 그러면 획득 조건을 검사.
CDnSkillTreeSystem* pSkillTreeSystem = g_pDataManager->GetSkillTreeSystem();
CDnSkillTreeSystem::S_TRY_ACQUIRE TryAcquire( GetPossessedSkillInfo() );
TryAcquire.iCurrentCharLevel = m_pUserSession->GetLevel();
TryAcquire.iTryAcquireSkillID = nResultSkillID;
// 직업별로 정해져 있는 쓸 수 있는 SP 를 기준으로 한다.
TryAcquire.iHasSkillPoint = GetAvailSkillPointByJob( nResultSkillID );
pSkillTreeSystem->TryAcquireSkill( TryAcquire, &Output );
#if defined(PRE_ADD_SKILL_LEVELUP_LIMIT_BY_SP)
std::vector<int> nNeedSPValues;
pSkillTreeSystem->GetNeedSPValuesByJob(nResultSkillID, nNeedSPValues);
std::vector<int> jobHistory;
GetJobHistory(m_pUserSession->GetStatusData()->cJobArray, jobHistory);
bool bAvailableSPByJob = IsAvailableSPByJob(jobHistory, nNeedSPValues);
if (bAvailableSPByJob == false)
Output.eResult = CDnSkillTreeSystem::R_NOT_ENOUGH_SKILLPOINT_TO_ACQUIRE;
#endif // PRE_ADD_SKILL_LEVELUP_LIMIT_BY_SP
bool bIgnoreParentSkillCondition = ( (CDnSkillTreeSystem::R_DONT_HAVE_PARENT_SKILL == Output.eResult) ||
(CDnSkillTreeSystem::R_LOCKED_PARENTSKILL == Output.eResult) ||
(CDnSkillTreeSystem::R_NOT_ENOUGH_PARENT_SKILL_LEVEL == Output.eResult) ) &&
true == bAlreadyGlobalSkillAcquired;
if( CDnSkillTreeSystem::R_SUCCESS == Output.eResult ||
true == bIgnoreParentSkillCondition )
{
// 클라에 통보 후 유저객체에서 스킬정보 꺼내서 레벨 0으로 셋팅... 및 스킬 추가.
UnLockSkill( nResultSkillID ); // SendUnlockSkill() 내부에서 호출되어 패킷 나감.
bSuccess = true;
SetSkillLevel( nResultSkillID, 1, DBDNWorldDef::SkillChangeCode::Use, false );
// 획득 성공 결과를 최종 통보.
m_pUserSession->SendAcquireSkill( nResultSkillID, ERROR_NONE );
}
else
{
switch( Output.eResult )
{
// 캐릭터 요구레벨이 모자람.
case CDnSkillTreeSystem::R_NOT_ENOUGH_CHAR_LEVEL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_CHAR_LEVEL;
break;
// 선행(부모) 스킬이 없음.
case CDnSkillTreeSystem::R_DONT_HAVE_PARENT_SKILL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_DONT_HAVE_PARENT_SKILL;
break;
//// 부모 스킬이 락이 되어있음.
//case CDnSkillTreeSystem::R_LOCKED_PARENTSKILL:
// break;
// 부모 스킬의 레벨이 충족되지 않음.
case CDnSkillTreeSystem::R_NOT_ENOUGH_PARENT_SKILL_LEVEL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_PARENT_SKILL_LEVEL;
break;
// 스킬 포인트가 모자라서 스킬을 획득할 수 없음.
case CDnSkillTreeSystem::R_NOT_ENOUGH_SKILLPOINT_TO_ACQUIRE:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_SKILLPOINT;
break;
}
}
}
else
{
nRetCode = ERROR_SKILL_UNLOCK_FAIL;
}
}
else
{
nRetCode = ERROR_SKILL_UNLOCK_MISMATCH_JOB;
}
}
else
{
// 이미 언락 한 상태임.
nRetCode = ERROR_SKILL_UNLOCK_ALREADY_OPEN;
}
if( false == bSuccess )
{
//m_pUserSession->SendUnlockSkill( nResultSkillID, nRetCode );
m_pUserSession->SendAcquireSkill( nResultSkillID, nRetCode );
}
return bSuccess;
}
// 스킬 획득. (레벨 0에서 1로 바꿈)
// 스킬 리셋 후 다시 획득시 이게 호출된다.
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
int CDNUserSkill::AcquireSkill( int nSkillID )
#else
void CDNUserSkill::AcquireSkill( int nSkillID )
#endif
{
#ifdef PRE_ADD_SP_REVISION
// #29463 이슈 관련 현재 보유 스킬 포인트와 스킬 소유 리스트가 다른 경우엔 여기서 보정해준다.
#ifdef _FINAL_BUILD
CheckAndRevisionSkillPoint();
#endif // #ifdef _FINAL_BUILD
#endif // #ifdef PRE_ADD_SP_REVISION
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find( nSkillID );
int nRetCode = ERROR_NONE;
if( iter != m_SkillList[GetCurrentSkillPage()].end() )
{
_ASSERT( false == iter->second.bLock );
_ASSERT( 0 == iter->second.cSkillLevel );
// 스킬 리셋 후 재획득 하는 경우에 상호 배타적인 스킬이 있는지 체크
// 같이 못배우게 한 스킬은 배울 수 없다.
TSkillData* pSkillData = g_pDataManager->GetSkillData( nSkillID );
if( m_pUserSession->GetSkill()->IsExclusiveSkill( nSkillID, pSkillData->nExclusiveID ) )
{
// 기본적으로는 클라에서 막기 때문에 서버로 패킷을 보낸다면 핵이다.
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return ERROR_SKILL_ACQUIRE_FAIL_EXCLUSIVE;
#else
m_pUserSession->SendAcquireSkill( nSkillID, ERROR_SKILL_ACQUIRE_FAIL_EXCLUSIVE );
return;
#endif // #if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
}
// #36858 글로벌 스킬로 서로 엮여 있다면 해당 그룹중에 하나만 배워도 부모 스킬 조건없이 배울 수 있다.
bool bAlreadyGlobalSkillAcquired = false;
if( HasSameGlobalIDSkill( pSkillData->nGlobalSkillGroup ) )
bAlreadyGlobalSkillAcquired = true;
CDnSkillTreeSystem* pSkillTreeSystem = g_pDataManager->GetSkillTreeSystem();
CDnSkillTreeSystem::S_TRY_ACQUIRE TryAcquire( GetPossessedSkillInfo() );
CDnSkillTreeSystem::S_OUTPUT Output;
TryAcquire.iCurrentCharLevel = m_pUserSession->GetLevel();
TryAcquire.iTryAcquireSkillID = nSkillID;
// 직업별로 정해져 있는 쓸 수 있는 SP 를 기준으로 한다.
TryAcquire.iHasSkillPoint = GetAvailSkillPointByJob( nSkillID );
// 스킬이 요구하는 직업이 히스토리에 있는지 적절히 찾아서 처리.
TryAcquire.iJobID = -1;
if (pSkillData){
for( int i = 0; i < JOBMAX; ++i )
{
BYTE cJob = m_pUserSession->GetStatusData()->cJobArray[ i ];
if( 0 < cJob )
{
if( cJob == pSkillData->nNeedJobID )
{
TryAcquire.iJobID = pSkillData->nNeedJobID;
}
}
else
break;
}
}
if( -1 != TryAcquire.iJobID )
{
pSkillTreeSystem->TryAcquireSkill( TryAcquire, &Output );
bool bIgnoreParentSkillCondition = ( (CDnSkillTreeSystem::R_DONT_HAVE_PARENT_SKILL == Output.eResult) ||
(CDnSkillTreeSystem::R_LOCKED_PARENTSKILL == Output.eResult) ||
(CDnSkillTreeSystem::R_NOT_ENOUGH_PARENT_SKILL_LEVEL == Output.eResult) ) &&
true == bAlreadyGlobalSkillAcquired;
if( CDnSkillTreeSystem::R_SUCCESS == Output.eResult ||
true == bIgnoreParentSkillCondition )
{
SetSkillLevel( nSkillID, 1, DBDNWorldDef::SkillChangeCode::Use, false );
// TODO: 스킬 획득 관련 미션 쪽 물어봐서 처리.
//m_pUserSession->GetEventSystem()->OnEvent( EventSystem::OnSkillAdd, 1, EventSystem::SkillID, nSkillID );
}
else
{
switch( Output.eResult )
{
// 캐릭터 요구레벨이 모자람.
case CDnSkillTreeSystem::R_NOT_ENOUGH_CHAR_LEVEL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_CHAR_LEVEL;
break;
// 선행(부모) 스킬이 없음.
case CDnSkillTreeSystem::R_DONT_HAVE_PARENT_SKILL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_DONT_HAVE_PARENT_SKILL;
break;
//// 부모 스킬이 락이 되어있음.
//case CDnSkillTreeSystem::R_LOCKED_PARENTSKILL:
// break;
// 부모 스킬의 레벨이 충족되지 않음.
case CDnSkillTreeSystem::R_NOT_ENOUGH_PARENT_SKILL_LEVEL:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_PARENT_SKILL_LEVEL;
break;
// 스킬 포인트가 모자라서 스킬을 획득할 수 없음.
case CDnSkillTreeSystem::R_NOT_ENOUGH_SKILLPOINT_TO_ACQUIRE:
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_NOT_ENOUGH_SKILLPOINT;
break;
}
}
}
else
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_MISMATCH_JOB;
}
else
{
// 해당 스킬 보유하고 있지 않음. ERROR_SKILL_LEVELUPFAIL_ISNOT_YOURS 클라로 리턴
nRetCode = ERROR_SKILL_ACQUIRE_FAIL_ISNOT_YOURS;
}
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
return nRetCode;
#else
m_pUserSession->SendAcquireSkill( nSkillID, nRetCode );
#endif
}
// 치트키 전용 스킬 획득 함수..
void CDNUserSkill::CheatAcquireSkill( int nSkillID )
{
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find( nSkillID );
int nRetCode = ERROR_NONE;
if( iter != m_SkillList[GetCurrentSkillPage()].end() )
{
SetSkillLevel( nSkillID, 1, DBDNWorldDef::SkillChangeCode::ModSkillLevelByAdmin, false );
m_pUserSession->SendAcquireSkill( nSkillID, nRetCode );
}
}
#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
void CDNUserSkill::ReservationSkillList(CSReservationSkillListReq* pUnlockSkillByMoneyReq)
{
if( pUnlockSkillByMoneyReq->nCount <= 0 )
return;
SCReservationSkillListAck ReservationSkillAck;
memset(&ReservationSkillAck, 0, sizeof(SCReservationSkillListAck));
for( int i=0; i<pUnlockSkillByMoneyReq->nCount; ++i)
{
memcpy(&ReservationSkillAck.tReservationSkillAck[i].tReservationSkill, &pUnlockSkillByMoneyReq->tReservationSkill[i], sizeof(TReservationSkillReq));
if(pUnlockSkillByMoneyReq->tReservationSkill[i].cType == ReservationSKillList::Type::UnLock)
{
ReservationSkillAck.tReservationSkillAck[i].nResult = UnLockSkillByMoney(pUnlockSkillByMoneyReq->tReservationSkill[i].nSkillID);
}
else if(pUnlockSkillByMoneyReq->tReservationSkill[i].cType == ReservationSKillList::Type::Acquire)
{
ReservationSkillAck.tReservationSkillAck[i].nResult = AcquireSkill(pUnlockSkillByMoneyReq->tReservationSkill[i].nSkillID);
}
else if(pUnlockSkillByMoneyReq->tReservationSkill[i].cType == ReservationSKillList::Type::LevelUp)
{
int nResult = ERROR_NONE;
int nCurLevel = GetSkillLevel(pUnlockSkillByMoneyReq->tReservationSkill[i].nSkillID);
if( nCurLevel >= pUnlockSkillByMoneyReq->tReservationSkill[i].nLevel)
{
nResult = ERROR_GENERIC_UNKNOWNERROR;
}
else
{
for( int j=nCurLevel;j<pUnlockSkillByMoneyReq->tReservationSkill[i].nLevel;++j)
{
nResult = SkillLevelUp(pUnlockSkillByMoneyReq->tReservationSkill[i].nSkillID);
if( nResult != ERROR_NONE)
break;
}
}
ReservationSkillAck.tReservationSkillAck[i].nResult = nResult;
}
else
break;
++ReservationSkillAck.nCount;
if(ReservationSkillAck.tReservationSkillAck[i].nResult != ERROR_NONE ) // 도중에 1개라도 에러나면 그냥 마무리..
break;
}
int nLen = sizeof(SCReservationSkillListAck) - sizeof(ReservationSkillAck.tReservationSkillAck) + (sizeof(TReservationSkillAck)*ReservationSkillAck.nCount);
m_pUserSession->AddSendData(SC_SKILL, eSkill::SC_RESERVATION_SKILL_LIST_ACK, (char*)&ReservationSkillAck, nLen);
}
#endif //#if defined(PRE_ADD_SKILL_LEVELUP_RESERVATION)
void CDNUserSkill::ResetSkill( int nSkillPage )
{
// 전직시에 모든 스킬 초기화.
// 나머진 캐시템 갖고 해야함.
ResetAllSkill( nSkillPage );
m_pUserSession->GetDBConnection()->QueryResetSkill(m_pUserSession, nSkillPage);
}
void CDNUserSkill::WaitForUseSkillResetCashItemFromDBServer( vector<int>& vlSkillIDsToReset )
{
m_vlWaitingSkillIDsToReset = vlSkillIDsToReset;
}
void CDNUserSkill::OnResponseSkillResetCashItemFromDBServer( bool bSuccess )
{
if( bSuccess )
{
// 받아놓았던 스킬 아이디들을 리셋. 스킬 포인트는 DB에서 받자마자 처리해주므로 여기서 신경 안써도 됨.
int aiDefaultSkills[ DEFAULTSKILLMAX ] = { 0 };
g_pDataManager->GetCreateDefaultSkill( m_pUserSession->GetClassID(), aiDefaultSkills );
int nCount = 0;
for( int i = 0; i != (int)m_vlWaitingSkillIDsToReset.size(); ++i )
{
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find( m_vlWaitingSkillIDsToReset.at(i) );
_ASSERT( m_SkillList[GetCurrentSkillPage()].end() != iter );
if( m_SkillList[GetCurrentSkillPage()].end() == iter )
continue;
bool bDefaultSkill = false;
for( int k = 0; k < DEFAULTSKILLMAX; ++k )
{
if( aiDefaultSkills[ k ] == iter->second.nSkillID )
{
bDefaultSkill = true;
break;
}
}
if( bDefaultSkill )
{
iter->second.cSkillLevel = 1;
}
else
iter->second.cSkillLevel = 0;
for( UINT k = 0; k < m_vlPossessedSkill[GetCurrentSkillPage()].size(); ++k )
{
if( iter->second.nSkillID == m_vlPossessedSkill[GetCurrentSkillPage()].at( k ).iSkillID )
m_vlPossessedSkill[GetCurrentSkillPage()].at( k ).iSkillLevel = iter->second.cSkillLevel;
}
nCount++;
}
}
m_vlWaitingSkillIDsToReset.clear();
}
void CDNUserSkill::GatherThisJobSkill( BYTE cJob, IN OUT vector<int>& vlResult )
{
// 추후 속도에 문제가 된다면 따로 리스트를 구성해 사용하도록 한다.
vector<CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO>::iterator iter = m_vlPossessedSkill[GetCurrentSkillPage()].begin();
for ( iter; iter != m_vlPossessedSkill[GetCurrentSkillPage()].end(); ++iter )
{
TSkillData* pSkillData = g_pDataManager->GetSkillData( iter->iSkillID );
if (!pSkillData) continue;
if( pSkillData->nNeedJobID == cJob )
{
// 획득까지 한 상태여야 실제 스킬 보유 상태임.
if( 0 < iter->iSkillLevel )
vlResult.push_back( iter->iSkillID );
}
}
}
int CDNUserSkill::GetLevelUpSkillPoint( int nPrevLevel, int nCurLevel )
{
if( nPrevLevel == nCurLevel ) return 0;
int nSkillPoint = 0;
for( int i=nPrevLevel+1; i<=nCurLevel; i++ ) {
nSkillPoint += CPlayerLevelTable::GetInstance().GetValue( m_pUserSession->GetClassID(), i, CPlayerLevelTable::SkillPoint );
}
return nSkillPoint;
}
bool CDNUserSkill::IsExclusiveSkill( int nSkillID, int nExclusiveID )
{
if( 0 != nExclusiveID )
{
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].begin();
for(iter; iter != m_SkillList[GetCurrentSkillPage()].end(); ++iter)
{
const TSkill& ExistSkillData = iter->second;
if( ExistSkillData.nSkillID <= 0 )
continue;
if( nSkillID == ExistSkillData.nSkillID )
continue;
// 레벨 0 짜리로 리셋된 것은 얻은 것이 아니므로 비교대상이 아님.
if( 0 == ExistSkillData.cSkillLevel )
continue;
const TSkillData* pSkillData = g_pDataManager->GetSkillData( ExistSkillData.nSkillID );
if( 0 != pSkillData->nExclusiveID )
{
if( nExclusiveID == pSkillData->nExclusiveID )
return true;
}
}
}
return false;
}
int CDNUserSkill::GetUsedSPByJob( int nJobID )
{
int iResult = 0;
for( int i = 0; i < (int)m_vlPossessedSkill[GetCurrentSkillPage()].size(); ++i )
{
int iSkillID = m_vlPossessedSkill[GetCurrentSkillPage()].at( i ).iSkillID;
const TSkillData* pSkillData = g_pDataManager->GetSkillData( iSkillID );
// 파이널 빌드일때만 NULL 체크. 테스트 단계에서는 죽도록..
#if defined(_FINAL_BUILD)
if( NULL == pSkillData )
continue;
#endif // #if defined(_FINAL_BUILD)
if( pSkillData->nNeedJobID == nJobID )
{
int iLevel = m_vlPossessedSkill[GetCurrentSkillPage()].at( i ).iSkillLevel;
int iSize = (int)pSkillData->vLevelDataList.size();
for( int k = 0; k < min(iLevel, iSize); ++k )
iResult += pSkillData->vLevelDataList.at( k ).nNeedSkillPoint;
if (iLevel > iSize)
g_Log.Log(LogType::_ERROR, L"GetUsedSPByJob - Wrong Skill Size(WORSETID:%d, CHARID:%d, iLevel:%d, iSize:%d)\r\n", m_pUserSession->GetWorldSetID(), m_pUserSession->GetCharacterDBID(), iLevel, iSize);
}
}
return iResult;
}
int CDNUserSkill::GetAvailSkillPointByJob( int nSkillID )
{
int iWholeSP = GetLevelUpSkillPoint( 1, m_pUserSession->GetLevel() );
float fAvailSPRatioByJob = m_pUserSession->GetAvailSkillPointRatioByJob( nSkillID );
const TSkillData* pSkillDataToLevelUp = g_pDataManager->GetSkillData( nSkillID );
int iUsedSPThisJob = 0;
if (pSkillDataToLevelUp)
iUsedSPThisJob = GetUsedSPByJob( pSkillDataToLevelUp->nNeedJobID );
// 전체 사용가능 SP 보다 직업 SP 가 남은 것이 많으면 전체 사용가능 SP 가 진짜이므로 해당 포인트로 리턴.
int iAvailPoint = int(fAvailSPRatioByJob*iWholeSP)-iUsedSPThisJob;
if( m_pUserSession->GetSkillPoint() < iAvailPoint )
iAvailPoint = m_pUserSession->GetSkillPoint();
return iAvailPoint;
}
void CDNUserSkill::OnAttachEquip( TItem *pItem )
{
if( pItem->nItemID > 0 ) {
TItemData *pItemData = g_pDataManager->GetItemData( pItem->nItemID );
if( pItemData && (pItemData->nSkillID > 0) ) {
if( !FindSkill( pItemData->nSkillID ) ) {
TSkill Skill;
memset(&Skill, 0, sizeof(TSkill));
Skill.nSkillID = pItemData->nSkillID;
Skill.cSkillLevel = 1;
Skill.bLock = false;
m_SkillList[GetCurrentSkillPage()][pItemData->nSkillID] = Skill;
CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO SkillInfo;
SkillInfo.iSkillID = pItemData->nSkillID;
SkillInfo.iSkillLevel = Skill.cSkillLevel;
SkillInfo.bCurrentLock = Skill.bLock;
m_vlPossessedSkill[GetCurrentSkillPage()].push_back( SkillInfo );
}
}
}
}
void CDNUserSkill::OnDetachEquip( TItem *pItem )
{
if( pItem->nItemID > 0 ) {
TItemData *pItemData = g_pDataManager->GetItemData( pItem->nItemID );
if( pItemData && (pItemData->nSkillID > 0) ) {
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].find( pItemData->nSkillID );
if (iter != m_SkillList[GetCurrentSkillPage()].end()){
m_SkillList[GetCurrentSkillPage()].erase(iter);
vector<CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO>::iterator iter = m_vlPossessedSkill[GetCurrentSkillPage()].begin();
for( iter; m_vlPossessedSkill[GetCurrentSkillPage()].end() != iter; ++iter )
{
if( iter->iSkillID == pItemData->nSkillID )
{
m_vlPossessedSkill[GetCurrentSkillPage()].erase( iter );
break;
}
}
}
}
}
}
#ifdef PRE_ADD_SP_REVISION
#ifdef _FINAL_BUILD
void CDNUserSkill::CheckAndRevisionSkillPoint( bool bSendRevisionSPToClient/* = true*/ )
{
if( NULL == m_pUserSession )
return;
// #29463 이슈 관련 현재 보유 스킬 포인트와 스킬 소유 리스트가 다른 경우엔 여기서 보정해준다.
int nUseSkillPoint = 0;
int nWholeSP = GetLevelUpSkillPoint( 1, m_pUserSession->GetLevel() );
// 디폴트 스킬을 얻어와서 현재 디폴트 스킬들만 있고 1렙이라면 스킬을 하나도 찍은 게 없으므로 리셋할 수 없다.
int aiDefaultSkills[ DEFAULTSKILLMAX ] = { 0 };
g_pDataManager->GetCreateDefaultSkill( m_pUserSession->GetClassID(), aiDefaultSkills );
int iNumSkill = (int)m_vlPossessedSkill[GetCurrentSkillPage()].size();
for( int i = 0; i < iNumSkill; ++i )
{
CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO& PossessedSkill = m_vlPossessedSkill[GetCurrentSkillPage()].at( i );
// 디폴트 스킬이고 레벨 1이면 스킬 포인트 계산에서 뺀다.
bool bDefaultSkill = false;
bool bDefaultSkillOneLevel = false;
for( int k = 0; k < DEFAULTSKILLMAX; ++k )
{
if( aiDefaultSkills[ k ] == 0 )
break;
if( PossessedSkill.iSkillID == aiDefaultSkills[ k ] )
{
bDefaultSkill = true;
if( 1 == PossessedSkill.iSkillLevel )
{
bDefaultSkillOneLevel = true;
}
break;
}
}
if( bDefaultSkill && bDefaultSkillOneLevel )
continue;
for( int k = 0; k < PossessedSkill.iSkillLevel; ++k )
{
// 디폴트 스킬은 1레벨의 스킬 포인트를 계산하지 않는다.
// 테이블에 필요 SP 가 0으로 되어있지만 만에 하나 잘못 입력되었을 경우를 대비해 코드에서 막는다.
if( bDefaultSkill && 0 == k )
continue;
nUseSkillPoint += g_pDataManager->GetNeedSkillPoint(PossessedSkill.iSkillID, k + 1);
}
}
int nLeftSkillPoint = nWholeSP - nUseSkillPoint;
// 현재 저장되어있는 스킬 포인트와 값이 다르다면 보정처리.
int nNowSkillPoint = m_pUserSession->GetSkillPoint();
// 만약 초과해서 이미 스킬을 찍은 상태라면 다음에 스킬리셋하고 스킬 획득할때 보정되도록 그냥 여기선 넘긴다.
if( nLeftSkillPoint < 0 )
return;
if( nNowSkillPoint != nLeftSkillPoint )
{
m_pUserSession->SetSkillPoint( nLeftSkillPoint, GetCurrentSkillPage() );
#ifdef PRE_FIX_SP_REVISION_CHANGE_SP
m_pUserSession->GetDBConnection()->QuerySetSkillPoint( m_pUserSession, nLeftSkillPoint, GetCurrentSkillPage() );
#else
int nSkillPointDelta = nLeftSkillPoint - nNowSkillPoint;
if( 0 < nSkillPointDelta )
{
m_pUserSession->GetDBConnection()->QueryIncreaseSkillPoint( m_pUserSession, nSkillPointDelta, DBDNWorldDef::SkillPointCode::Repair, GetCurrentSkillPage() );
}
else
{
// 감소 쿼리에도 감소될 스킬 포인트를 양수로 넘겨주면 DB 측에서 해당 포인트 차감.
m_pUserSession->GetDBConnection()->QueryDecreaseSkillPoint( m_pUserSession, -nSkillPointDelta, GetCurrentSkillPage() );
}
#endif // #ifdef PRE_FIX_SP_REVISION_CHANGE_SP
// 클라이언트에게도 스킬 포인트 패킷을 밀어준다.
if( bSendRevisionSPToClient )
m_pUserSession->SendPushSkillPoint( nLeftSkillPoint );
}
}
#endif // #ifdef _FINAL_BUILD
#endif // #ifdef PRE_ADD_SP_REVISION
void CDNUserSkill::ResetAllSkill(int nSkillPage )
{
// 기본 스킬을 제외하고 모든 보유 스킬을 0으로 리셋. 소모된 스킬 포인트 복구시킴.
// 기본 스킬은 1렙으로.
int aiDefaultSkills[ DEFAULTSKILLMAX ] = { 0 };
g_pDataManager->GetCreateDefaultSkill( m_pUserSession->GetClassID(), aiDefaultSkills );
TMapSkillList::iterator iter;
int nCount = 0;
if( nSkillPage < DualSkill::Type::Primary || nSkillPage >= DualSkill::Type::MAX )
return;
for (iter = m_SkillList[nSkillPage].begin(); iter != m_SkillList[nSkillPage].end(); ++iter)
{
bool bDefaultSkill = false;
for( int i = 0; i < DEFAULTSKILLMAX; ++i )
{
if( aiDefaultSkills[ i ] == iter->second.nSkillID )
{
bDefaultSkill = true;
break;
}
}
if( bDefaultSkill )
{
iter->second.cSkillLevel = 1;
}
else
iter->second.cSkillLevel = 0;
for( int i = 0; i < (int)m_vlPossessedSkill[nSkillPage].size(); ++i )
{
if( iter->second.nSkillID == m_vlPossessedSkill[nSkillPage].at( i ).iSkillID )
m_vlPossessedSkill[nSkillPage].at( i ).iSkillLevel = iter->second.cSkillLevel;
}
nCount++;
}
}
#ifdef PRE_ADD_CHANGEJOB_CASHITEM
void CDNUserSkill::OnResponseChangeJobCode( USHORT wTotalSkillPoint )
{
for(int nSkillPage = DualSkill::Type::Primary; nSkillPage < DualSkill::Type::MAX ; nSkillPage++ )
{
ResetAllSkill( nSkillPage );
m_pUserSession->SetSkillPoint( wTotalSkillPoint, nSkillPage );
}
}
#endif // #ifdef PRE_ADD_CHANGEJOB_CASHITEM
bool CDNUserSkill::HasSameGlobalIDSkill( int iGlobalSkillGroupID )
{
bool bResult = false;
if( 0 < iGlobalSkillGroupID )
{
TMapSkillList::iterator iter = m_SkillList[GetCurrentSkillPage()].begin();
for(iter; iter != m_SkillList[GetCurrentSkillPage()].end(); ++iter)
{
const TSkillData* pExistSkillData = g_pDataManager->GetSkillData( iter->second.nSkillID );
if( pExistSkillData )
{
if( pExistSkillData->nGlobalSkillGroup == iGlobalSkillGroupID )
{
bResult = true;
break;
}
}
}
}
return bResult;
}
void CDNUserSkill::ApplyExpendedSkillPage( int nSkillPage )
{
if( nSkillPage == GetCurrentSkillPage() || nSkillPage <= DualSkill::Type::Primary || nSkillPage >= DualSkill::Type::MAX)
return;
int aiDefaultSkills[ DEFAULTSKILLMAX ] = { 0 };
int addCount = 0;
int nUseSkillPoint = 0;
g_pDataManager->GetCreateDefaultSkill( m_pUserSession->GetClassID(), aiDefaultSkills );
for(int i=0; i<DEFAULTSKILLMAX; i++ )
{
if( aiDefaultSkills[i] > 0 )
{
m_pSkillData[nSkillPage].SkillList[addCount].cSkillLevel = 1;
m_pSkillData[nSkillPage].SkillList[addCount].nSkillID = aiDefaultSkills[i];
m_pSkillData[nSkillPage].SkillList[addCount].nCoolTime = 0;
addCount++;
}
}
for (int i = 0; i < SKILLMAX; i++){
if (m_pSkillData[nSkillPage].SkillList[i].nSkillID <= 0) continue;
// 데이터에 없는 스킬 ID가 DB 에 저장되어있다면 읽어들이지 않는다.
const TSkillData* pSkillData = g_pDataManager->GetSkillData( m_pSkillData[nSkillPage].SkillList[i].nSkillID );
if( NULL == pSkillData )
continue;
m_SkillList[nSkillPage][m_pSkillData[nSkillPage].SkillList[i].nSkillID] = m_pSkillData[nSkillPage].SkillList[i];
CDnSkillTreeSystem::S_POSSESSED_SKILL_INFO SkillInfo;
SkillInfo.iSkillID = m_pSkillData[nSkillPage].SkillList[i].nSkillID;
SkillInfo.iSkillLevel = m_pSkillData[nSkillPage].SkillList[i].cSkillLevel;
SkillInfo.bCurrentLock = m_pSkillData[nSkillPage].SkillList[i].bLock;
m_vlPossessedSkill[nSkillPage].push_back( SkillInfo );
if (m_pSkillData[nSkillPage].SkillList[i].nSkillID > 0){
for (int j = 0; j < m_pSkillData[nSkillPage].SkillList[i].cSkillLevel; j++){
nUseSkillPoint += g_pDataManager->GetNeedSkillPoint(m_pSkillData[nSkillPage].SkillList[i].nSkillID, j + 1);
}
}
}
}
int CDNUserSkill::GetCurrentSkillPage()
{
return m_pUserSession->GetSkillPage();
}
#if defined(PRE_ADD_SKILL_LEVELUP_LIMIT_BY_SP)
void CDNUserSkill::GetJobHistory(BYTE *cJobArray, vector<int>& jobHistory)
{
for (int j = 0; j < JOBMAX; j++){
if (cJobArray[j] <= 0) continue;
jobHistory.push_back((int)cJobArray[j]);
}
}
bool CDNUserSkill::IsAvailableSPByJob(vector<int>& jobHistory, vector<int>& needSPValues)
{
bool isAvailableSPByJob = true;
int nJobCount = (int)jobHistory.size();
int nJobID = -1;
for (int i = 0; i < nJobCount; ++i)
{
if (IsAvailableSPByJob(jobHistory, needSPValues, i) == false)
{
isAvailableSPByJob = false;
break;
}
}
return isAvailableSPByJob;
}
bool CDNUserSkill::IsAvailableSPByJob(std::vector<int>& jobHistory, vector<int>& needSPValues, int nIndex)
{
bool isAvailableSPByJob = false;
int nJobCount = (int)jobHistory.size();
int nNeedSPValue = 0;
int nSPValueCount = (int)needSPValues.size();
if (nIndex >= 0 && nIndex < nSPValueCount)
nNeedSPValue = needSPValues[nIndex];
//전직이 되지 않은 경우??
if (nIndex >= nJobCount)
{
if (nNeedSPValue <= 0)
return true;
else
return false;
}
int nJobID = -1;
nJobID = jobHistory[nIndex];
int iUsedSkillPointInThisJob = GetUsedSPByJob( nJobID );
//해당 JobID에 사용한 SP값이 확인용 값보다 커야 사용 가능..
if (iUsedSkillPointInThisJob >= nNeedSPValue)
isAvailableSPByJob = true;
return isAvailableSPByJob;
}
#endif // PRE_ADD_SKILL_LEVELUP_LIMIT_BY_SP