Files
Sun1602/Server/GameServer/SkillSystem/SkillSlotManager.cpp
T
2022-10-26 12:25:11 +08:00

2201 lines
74 KiB
C++

#include "stdafx.h"
#include "SkillSlotManager.h"
#include <SkillInfoParser.h>
#include <StyleQuickRegistInfoParser.h>
#include <SkillAttrCalculator.h>
#include <SCSkillSlotContainer.h>
#include <SCQuickStyleSlot.h>
#include <SCQuickStyleSlotContainer.h>
//==================================================================================================
#include "Player.h"
#include "SkillManager.h"
#include "ItemManager.h"
#include "SlotManager.h"
#include "QuickManager.h"
#include "QuickStyleManager.h"
#include "StatusManager.h"
#include "SummonManager.h"
#include "PassiveSkill.h"
#include "SkillFactory.h"
#include "CommonCharacterFormula.h"
//_NA_001990_ACSYSTEM_ADD
#include "AchievementManager.h"
SkillSlotManager::SkillSlotManager()
{
}
SkillSlotManager::~SkillSlotManager()
{
}
void SkillSlotManager::Init(SlotManager* const slot_manager)
{
player_ = slot_manager->player_;
slot_manager_ = slot_manager;
skill_container_ = \
static_cast<SCSkillSlotContainer*>(slot_manager->GetSlotContainer(SI_SKILL));
quick_container_ = \
static_cast<SCQuickSlotContainer*>(slot_manager->GetSlotContainer(SI_QUICK));
style_container_ = \
static_cast<SCQuickStyleSlotContainer*>(slot_manager->GetSlotContainer(SI_STYLE));
pre_style_code_ = 0;
passive_skills_.clear();
}
void SkillSlotManager::Release()
{
// old... (WAVERIX)(080603)(change logic) - decrease instance count
const SkillFactory* const skill_factory = SkillFactory::Instance();
FOREACH_CONTAINER(const SKILL_HASH::value_type& node, passive_skills_, SKILL_HASH)
{
Skill* const skill = node.second;
skill->Release();
skill_factory->FreeSkill(skill);
}
passive_skills_.clear();
}
bool SkillSlotManager::IsEmpty(POSTYPE pos)
{
return (slot_manager_->IsEmpty(SI_SKILL, pos) != false);
}
bool SkillSlotManager::ValidState()
{
return (slot_manager_->ValidState() != false);
}
// 무기 아이템 교환에 따른 스킬 적용 변경
void SkillSlotManager::AddWeapon(CODETYPE new_weapon_code)
{
const BASE_ITEMINFO* item_info = ItemInfoParser::Instance()->GetItemInfo(new_weapon_code);
if (item_info == NULL)
{
SUNLOG(eCRITICAL_LOG, _T("|["__FUNCTION__"]|斤口=can't find a weapon (%d)"),
new_weapon_code);
return;
}
// 패시브 스킬들의 기능이 살아난다.
UpdatePassiveSkills();
// 이전 스타일의 무기타입과 삽입하려는 무기의 타입이 같으면 이전 스타일을 복원해 준다.
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
const BASE_STYLEINFO* prev_style_info = skill_parser->GetStyleInfo(pre_style_code_);
if (prev_style_info && prev_style_info->m_iWeaponDefine == item_info->m_wType)
{
SetSelectStyle(pre_style_code_);
}
// 이전 스타일이 새로운 무기를 사용할 수 없다면 기본 스타일로 교체한다.
else
{
SetSelectStyle(player_->GetDefaultStyle(new_weapon_code));
};
// 퀵 스타일 슬롯의 정보들이 모두 바뀐다.
UpdateStyleSlots();
}
void SkillSlotManager::DelWeapon()
{
// 패시브 스킬들의 기능이 해제된다.
UpdatePassiveSkills();
// 이전 스타일 정보를 기억한다.
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
SLOTCODE style_code = player_->GetBaseSelectedStyle();
const BASE_STYLEINFO* base_style_info = skill_parser->GetStyleInfo(style_code);
if (base_style_info) {
pre_style_code_ = style_code;
}
// 주먹질 스타일로 교체한다.
StatusManager* const status_manager = player_->GetStatusManager();
style_code = player_->GetDefaultStyle(0);
if (status_manager->Status.IsDragonTransforming())
{
player_->SetBaseSelectedStyle(style_code);
}
else
{
SetSelectStyle(style_code);
}
// 퀵 스타일 슬롯의 정보들이 모두 바뀐다.
UpdateStyleSlots();
}
// CHANGES f100825.2L, change a routine to a total serializing skill stream converter
void SkillSlotManager::SerializeSkillStreamTotal(SKILL_TOTAL_INFO* const total_stream,
eSERIALIZE serialize_type)
{
const POSTYPE kMaxNumberOfSlots = skill_container_->GetMaxSlotNum();
BOOST_STATIC_ASSERT(MAX_SKILL_SLOT_NUM == _countof(total_stream->m_Slot));
//MAX_SKILL_SLOT_NUM == kMaxNumberOfSlots &&
//
if (serialize_type == SERIALIZE_LOAD)
{
//_NA_0_20110131_SKILLTREE_SYSTEM_RENEWAL
// 캐릭터에게 스킬 정보를 보낼때 스킬 트리의 첫행의 스킬을 익히기 위해 호출된다.(Default)
// 스킬 컨테이너에 스킬이 존재하지 않을 경우만 호출된다.
UnLockFirstSkillLine();
POSTYPE pos_index = 0;
SKILLSLOT* it = total_stream->m_Slot;
const SKILLSLOT* const end = &total_stream->m_Slot[kMaxNumberOfSlots];
for ( ; pos_index < kMaxNumberOfSlots && it != end; ++pos_index)
{
if (skill_container_->IsEmpty(pos_index)) {
continue;
}
it->m_Pos = pos_index;
skill_container_->SerializeSkillStream(pos_index, &it->m_Stream, serialize_type);
++it;
};
//
total_stream->m_Count = static_cast<POSTYPE>(it - total_stream->m_Slot);
}
else if (serialize_type == SERIALIZE_STORE)
{
const DWORD cur_max_hp = player_->GetMaxHP();
const DWORD cur_max_mp = player_->GetMaxMP();
const DWORD cur_max_sd = player_->GetMaxSD();
//
int number_of_stream_slots = total_stream->m_Count;
if (FlowControl::FCAssert(number_of_stream_slots <= kMaxNumberOfSlots) == false) {
return;
}
//
SKILLSLOT* it = total_stream->m_Slot;
const SKILLSLOT* const end = total_stream->m_Slot + number_of_stream_slots;
for ( ; it != end; ++it)
{
const SLOTCODE skill_code = it->m_Stream.Part.wCode;
WAVERIX_WRAPPER((WAVERIX_LC_CALL1("waverix_logskillcode", skill_code)));
skill_container_->SerializeSkillStream(it->m_Pos, &it->m_Stream, serialize_type);
// r100825.2L, regard the complexity of AddAttrForPassiveSkill.
if (AddAttrForPassiveSkill(skill_code) == false) {
// register a cool-time information if this skill is not a passive type.
player_->SetSkillCoolTime(skill_code, SKILL_COOLTIME_MARGIN);
}
}
//
if (cur_max_hp != player_->GetMaxHP()) {
player_->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedHP);
};
if (cur_max_mp != player_->GetMaxMP()) {
player_->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedMP);
};
if (cur_max_sd != player_->GetMaxSD()) {
player_->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedSD);
};
}
}
// CHANGES f100825.2L, removed. change a routine to a total serializing skill stream converter
/*
// 스킬 슬롯 갱신
void SkillSlotManager::SerializeSkillStream(POSTYPE pos, SKILLSTREAM* IN pStream, eSERIALIZE eType)
{
skill_container_->SerializeSkillStream(pos, pStream, eType);
if (eType == SERIALIZE_STORE)
{
DWORD maxHP = player_->GetMaxHP();
DWORD maxMP = player_->GetMaxMP();
DWORD maxSD = player_->GetMaxSD();
if (!AddAttrForPassiveSkill(pStream->Part.wCode))
{
// 패시브 스킬이 아닌경우 쿨타임 등록
player_->SetCoolTime(pStream->Part.wCode, SKILL_COOLTIME_MARGIN);
}
//
if (maxHP != player_->GetMaxHP())
{
player_->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedHP);
};
if (maxMP != player_->GetMaxMP())
{
player_->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedMP);
};
if (maxSD != player_->GetMaxSD())
{
player_->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedSD);
};
}
}
*/
//==================================================================================================
/*
// CHANGES, f100826.7L, removed
class QuickSkillLevelUp
{
BOOL complete_;
SLOTCODE cur_skill_code_;
SLOTCODE new_skill_code_;
public:
QuickSkillLevelUp(SLOTCODE cur_skill_code, SLOTCODE new_skill_code) :
cur_skill_code_(cur_skill_code), new_skill_code_(new_skill_code), complete_(0) {}
void operator() (SCSlot* slot)
{
SCQuickSlot* quick_slot = static_cast<SCQuickSlot*>(slot);
if (complete_) {
return;
}
if (quick_slot->GetOrgCode() != cur_skill_code_) {
return;
}
quick_slot->SetOrgCode(new_skill_code_);
complete_ = true;
}
};
*/
namespace nsSlot {
;
// CHANGES, f100826.7L, change a functor class to regard a locality.
struct QuickSlotOriginalCodeChanger
{
QuickSlotOriginalCodeChanger(SCQuickSlotContainer* quick_container,
SLOTCODE from, SLOTCODE to)
{
typedef SCQuickSlotContainer::SLOT_LINEAR_LIST SLOT_LINEAR_LIST;
const SLOT_LINEAR_LIST& slot_list = quick_container->GetSlotList();
FOREACH_CONTAINER(const SLOT_LINEAR_LIST::value_type& node, slot_list,
SLOT_LINEAR_LIST)
{
SCQuickSlot* const quick_slot = static_cast<SCQuickSlot*>(node.slot);
if (quick_slot->IsEmptySlot()) {
continue;
};
if (quick_slot->GetOrgCode() != from) {
continue;
}
quick_slot->SetOrgCode(to);
}
}
};
}; //end of namespace
//==================================================================================================
class SearchSkillAllLevelDown
{
SkillSlotManager* skill_slot_manager_;
public:
SearchSkillAllLevelDown(SkillSlotManager* skill_slot_manager)
: skill_slot_manager_(skill_slot_manager) {}
void operator()(SCSlot* slot)
{
SCSkillSlot* pSkillSlot = static_cast<SCSkillSlot*>(slot);
if (skill_slot_manager_ == NULL) {
return;
}
skill_slot_manager_->LevelDownSkillUntilSatisfaction(pSkillSlot->GetCode());
}
};
//--------------------------------------------------------------------------------------------------
//_NA_0_20110131_SKILLTREE_SYSTEM_RENEWAL
class SkillLevelDown
{
SkillSlotManager* skill_slot_manager_;
public:
SkillLevelDown(SkillSlotManager* skill_slot_manager)
: skill_slot_manager_(skill_slot_manager)
{
}
void operator()(const SCSlot* const slot)
{
if (skill_slot_manager_ == NULL)
{
return;
}
const SCSkillSlot* const skill_slot = static_cast<const SCSkillSlot* const>(slot);
const SkillScriptInfo* const skill_info = skill_slot->GetSkillInfo();
if (skill_info != NULL)
{
UnlearnSkill(skill_info->m_SkillClassCode);
}
}
void UnlearnSkill(const SLOTCODE skill_class)
{
Player* const player = skill_slot_manager_->player_;
SCSkillSlotContainer* const skill_container_ = skill_slot_manager_->skill_container_;
const SkillInfoParser* const skill_parser = SkillInfoParser::Instance();
const SkillJobTree::Desc* const job_desc =
skill_parser->FindAllJobDesc(player->GetCharInfo()->m_byClassCode, skill_class);
if (job_desc == NULL)
{
return;
}
// 현재 존재하는 스킬이어야 한다.
const SCSkillSlot* skill_slot = skill_container_->GetSkillSlotByClass(skill_class);
if (skill_slot == NULL)
{
return;
}
const SkillScriptInfo* const skill_info = skill_slot->GetSkillInfo();
if (skill_info == NULL)
{
return;
}
// 스킬 레벨을 낮추면서 각 레벨에 사용된 스킬포인트를 반납한다.
// 사용된 스킬 포인트를 반납한다.
SLOTCODE skill_code = skill_info->m_SkillCode;
for (int level = skill_info->m_wSkillLv; level >= 0; --level, --skill_code)
{
const SkillScriptInfo* const temp_skill_info = skill_parser->GetSkillInfo(skill_code);
if (temp_skill_info == NULL)
{
continue;
}
player->GetCharInfo()->m_iRemainSkill += temp_skill_info->m_byRequireSkillPoint;
}
// 위 루프에서 변경된 값을 새로 초기화한다.
skill_code = skill_info->m_SkillCode;
// 현재 스킬슬롯을 삭제한다.
skill_container_->DeleteSlot(skill_slot->GetPos(), NULL);
// QuickSlot에 스킬이 등록되어 있다면 삭제 시켜준다.
// 단, 스킬 트리의 1번째줄의 스킬인경우, 0레벨의 스킬로 교체한다.
QuickManager* const quick_manager = player->GetQuickManager();
if (quick_manager != NULL)
{
#ifdef _NA_004530_20120315_1202_SKILL_RENEWAL
quick_manager->UnlinkByCode(SI_SKILL, skill_code);
#else
if (job_desc->line_index == 0)
{
quick_manager->ChangeSkill(skill_code, job_desc->first_skill_code);
}
else
{
quick_manager->UnlinkByCode(SI_SKILL, skill_code);
}
#endif // _NA_004530_20120315_1202_SKILL_RENEWAL
}
// 패시브 스킬이라면 이전스킬을 삭제한다. <- (080120, WAVERIX) 이거 항상 성공하는 것 아닌감?
if (skill_slot_manager_->RemovePassiveSkill(skill_code) == false)
{
// 패시브 스킬이 아닌경우 쿨타임 제거
player->FreeSkillCoolTime(skill_code);
}
// 소환체 스킬이라면 이전스킬을 제거
SummonManager::Instance()->DelSummonedSkill(player->GetObjectKey(), skill_code);
GAMELOG->WriteSkillLevelDown(player, skill_code, false);
}
};
void SkillSlotManager::InitSkill()
{
if (ValidState() == false)
return;
SkillLevelDown Opr(this);
skill_container_->ForEachSlot(Opr);
#ifdef _NA_004530_20120315_1202_SKILL_RENEWAL
// 남아 있는 스킬 포인트를 계산한다.
int remain_skill_point = 0;
const LEVELTYPE cur_level = player_->GetLevel();
for (LEVELTYPE lv = 1; lv <= cur_level; ++lv)
{
remain_skill_point += ns_formula::GetSkillPoint(lv);
}
if (player_->GetRemainSkillPoint() != remain_skill_point)
{
player_->GetCharInfo()->m_iRemainSkill = remain_skill_point;
}
#endif
UnLockFirstSkillLine();
;{
MSG_CG_SKILL_REFRESH_CMD msg_cmd;
player_->SerializeSkillTotalInfo(msg_cmd.m_SkillInfo, SERIALIZE_LOAD);
player_->SendPacket(&msg_cmd, msg_cmd.GetSize());
};
;{
MSG_CG_CHARINFO_QUICK_CMD msg_cmd;
player_->GetQuickManager()->SerializeQuickTotalInfo(msg_cmd.m_QuickInfo, SERIALIZE_LOAD);
player_->SendPacket(&msg_cmd, msg_cmd.GetSize());
};
;{
MSG_CG_SKILL_POINT_REMAIN_CMD msg_cmd;
msg_cmd.m_iRemainSkill = player_->GetRemainSkillPoint();
player_->SendPacket(&msg_cmd, sizeof(msg_cmd));
};
}
void SkillSlotManager::LevelDownSkillUntilSatisfaction(SLOTCODE cur_skill_code)
{
SLOTCODE target_skill_code = cur_skill_code;
while (true)
{
//레벨을 다운시켜야 하나?
if (CanLevelDownSkill(target_skill_code))
{
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
const SkillScriptInfo* base_skill_info = skill_parser->GetSkillInfo(target_skill_code);
if (base_skill_info == NULL) {
return;
}
//스킬 레벨이 1이면...
if (1 == base_skill_info->m_wSkillLv)
{
if (CanUnLearnSkill((target_skill_code)))
{
UnLearnSkill(cur_skill_code);
}
else
{
LevelDownSkill(cur_skill_code, target_skill_code);
}
break;
}
else
{
target_skill_code--;
}
}
else
{
LevelDownSkill(cur_skill_code, target_skill_code);
break;
}
}
}
bool SkillSlotManager::CanLevelDownSkill(SLOTCODE cur_skill_code)
{
// cur_skill_code에 해당하는 스킬이 스킬스크립트에 존재하는지 체크
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
const SkillScriptInfo* base_skill_info = skill_parser->GetSkillInfo(cur_skill_code);
if (base_skill_info == NULL) {
return false;
}
#ifdef _NA_0_20100901_SKILLTREE_SYSTEM
return true;
#else
// 배운 스킬의 요구레벨을 만족못하면
if (player_->GetLevel() < base_skill_info->m_wRequireLv) {
return true;
}
// 배운 스킬의 요구숙련도를 만족못하면
if (player_->GetExperty1() < base_skill_info->m_wRequireSkillStat[0]) {
return true;
}
if (player_->GetExperty2() < base_skill_info->m_wRequireSkillStat[1]) {
return true;
}
return false;
#endif //_NA_0_20100901_SKILLTREE_SYSTEM
}
void SkillSlotManager::LevelDownSkill(SLOTCODE cur_skill_code, SLOTCODE lower_skill_code)
{
if (cur_skill_code == lower_skill_code) {
return;
}
// 현재 스킬슬롯
SCSkillSlot* cur_skill_slot = skill_container_->GetSkillSlotByCode(cur_skill_code);
if (cur_skill_slot == NULL) {
return;
}
//
const POSTYPE cur_slot_pos = cur_skill_slot->GetPos();
// 새롭게 스킬슬롯 정보를 셋팅한다.
SCSkillSlot making_skill_slot;
making_skill_slot.SetCode(lower_skill_code);
making_skill_slot.SetPos(cur_slot_pos);
//
const SkillScriptInfo* cur_skill_info = cur_skill_slot->GetSkillInfo();
const SkillScriptInfo* lower_skill_info = making_skill_slot.GetSkillInfo();
assert(lower_skill_info);
if (cur_skill_info->m_SkillClassCode != lower_skill_info->m_SkillClassCode) {
assert(cur_skill_info->m_SkillClassCode == lower_skill_info->m_SkillClassCode);
return;
}
//
skill_container_->DeleteSlot(cur_slot_pos, NULL);
skill_container_->InsertSlot(cur_slot_pos, making_skill_slot);
;{
// QuickSlot에 스킬이 등록되어 있다면 레벨다운 시켜준다.
// CHANGES, f100826.7L, change a functor class to regard a locality.
nsSlot::QuickSlotOriginalCodeChanger do_change_event(\
quick_container_,cur_skill_code, lower_skill_code);
};
if (lower_skill_info->m_bySkillType == SKILL_TYPE_PASSIVE)
{
// 패시브 스킬이라면 이전스킬을 제거하고 새로운 스킬을 추가한다.
RemovePassiveSkill(cur_skill_code);
AddAttrForPassiveSkill(lower_skill_code);
}
else
{ // 이전 스킬 쿨타임 제거 및 새스킬 쿨타임 등록
player_->FreeSkillCoolTime(cur_skill_code);
player_->SetSkillCoolTime(lower_skill_code, SKILL_COOLTIME_MARGIN);
}
// 소환체 스킬이라면 이전스킬을 제거하고 새로운 스킬을 추가한다.
if (cur_skill_info->m_bySkillUserType == eSKILL_USER_SUMMONED)
{
SummonManager* summon_manager = SummonManager::Instance();
DWORD summoner_key = player_->GetObjectKey(); // ... player key?
summon_manager->DelSummonedSkill(summoner_key, cur_skill_code);
summon_manager->AddSummonedSkill(summoner_key, lower_skill_code);
}
// NOTE, a current logic has only 1 level downgrade operation.
// need change a logic to apply multi-level downgrade.
//요구했던 스킬 포인트를 돌려주자.
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
int* const remain_skill_point = &player_->GetCharInfo()->m_iRemainSkill;
for (SLOTCODE skill_code = cur_skill_code; skill_code > lower_skill_code; --skill_code)
{
const SkillScriptInfo* base_skill_info = skill_parser->GetSkillInfo(skill_code);
if (base_skill_info == NULL) {
// WARNING : ... problem point ...
return;
}
(*remain_skill_point) += base_skill_info->m_byRequireSkillPoint;
}
//
GAMELOG->WriteSkillLevelDown(player_, lower_skill_code, true);
}
bool SkillSlotManager::CanUnLearnSkill(SLOTCODE cur_skill_code)
{
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
const SkillScriptInfo* base_skill_info = skill_parser->GetSkillInfo(cur_skill_code);
if (base_skill_info == NULL) {
return false;
}
//아이템으로 배우는 아이템은 UnLearn 시키지 않는다.
if (base_skill_info->m_bySkillAcquire) {
return false;
}
return true;
}
void SkillSlotManager::UnLearnSkill(SLOTCODE cur_skill_code)
{
// 현재 스킬슬롯
SCSkillSlot* cur_skill_slot = skill_container_->GetSkillSlotByCode(cur_skill_code);
if (!cur_skill_slot) {
return;
}
// 현재 스킬슬롯을 삭제한다.
skill_container_->DeleteSlot(cur_skill_slot->GetPos(), NULL);
// QuickSlot에 스킬이 등록되어 있다면 삭제 시켜준다.
QuickManager* quick_manager = player_->GetQuickManager();
if (!quick_manager) {
return;
}
quick_manager->UnlinkByCode(SI_SKILL, cur_skill_code);
// 패시브 스킬이라면 이전스킬을 삭제한다. <- (080120, WAVERIX) 이거 항상 성공하는 것 아닌감?
if (!RemovePassiveSkill(cur_skill_code))
{
// 패시브 스킬이 아닌경우 쿨타임 제거
player_->FreeSkillCoolTime(cur_skill_code);
}
// 소환체 스킬이라면 이전스킬을 제거
SummonManager::Instance()->DelSummonedSkill(player_->GetObjectKey(), cur_skill_code);
//요구했던 스킬 포인트를 돌려주자.
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
int* const remain_skill = &player_->GetCharInfo()->m_iRemainSkill;
SLOTCODE calc_skill_code = cur_skill_code;
while (const SkillScriptInfo* base_skill_info = skill_parser->GetSkillInfo(calc_skill_code))
{
(*remain_skill) += base_skill_info->m_byRequireSkillPoint;
// TODO: need a validation test.
// a downgrade calculation of the 1 level is changed to empty skill.
if (base_skill_info->m_wSkillLv == 1) {
break;
}
--calc_skill_code;
};
if (calc_skill_code == cur_skill_code) {
return;
}
//
GAMELOG->WriteSkillLevelDown(player_, cur_skill_code, false);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// REGION : Passive Skills
////////////////////////////////////////////////////////////////////////////////////////////////////
void SkillSlotManager::UpdatePassiveSkills()
{
FOREACH_CONTAINER(const SKILL_HASH::value_type& node, passive_skills_, SKILL_HASH)
{
Skill* const skill = node.second;
if (skill->GetSkillType() != SKILL_TYPE_PASSIVE)
{
continue;
};
if (player_->CanApplyPassiveSkill(skill->GetSkillCode()))
{
skill->StartExecute();
}
else
{
skill->EndExecute();
}
}
}
const Skill* SkillSlotManager::FindPassiveSkillfromSkillClass(const eSKILL skill_class) const
{
FOREACH_CONTAINER(const SKILL_HASH::value_type& node, passive_skills_, SKILL_HASH)
{
Skill* const skill = node.second;
if (skill->GetSkillClassCode() == skill_class)
{
return skill;
}
}
return NULL;
}
void SkillSlotManager::AddPassiveSkill(Skill* const skill)
{
if (skill == NULL)
{
return;
}
const SLOTCODE skill_code = skill->GetSkillCode();
if (FindPassiveSkill(skill_code) != NULL)
{
RemovePassiveSkill(skill_code);
}
passive_skills_[skill_code] = skill;
skill->StartExecute();
}
bool SkillSlotManager::AddAttrForPassiveSkill(const SLOTCODE skill_code)
{
const SkillInfoParser* const skill_parser = SkillInfoParser::Instance();
const SkillScriptInfo* const skill_script_info = skill_parser->GetSkillInfo(skill_code);
if (skill_script_info == NULL)
{
return false;
}
if (skill_script_info->m_bySkillType != SKILL_TYPE_PASSIVE)
{
return false;
}
const SkillFactory* const skill_factory = SkillFactory::Instance();
Skill* const new_skill = skill_factory->AllocSkill(SKILL_TYPE_PASSIVE, skill_script_info);
if (new_skill == NULL)
{
return false;
}
SkillInfo skill_info;
skill_info.skill_code_ = skill_code;
new_skill->Init(player_, &skill_info, skill_script_info);
AddPassiveSkill(new_skill);
return true;
}
bool SkillSlotManager::RemovePassiveSkill(const SLOTCODE skill_code)
{
Skill* const skill = FindPassiveSkill(skill_code);
if (skill == NULL)
{
return false;
}
passive_skills_.erase(skill_code);
skill->Release();
SkillFactory::Instance()->FreeSkill(skill);
return true;
}
//==================================================================================================
//==================================================================================================
// NOTE: The Episode I Passive skills & Style skills
//==================================================================================================
//==================================================================================================
#if SKILL_SYSTEM_VERSION < 2
namespace nsSlot {
;
struct QuickStyleUpdate
{
QuickStyleUpdate(IQuickStyleManager* manager, const BASE_StyleQuickRegistInfo* style_quick_info)
: manager_(manager), style_quick_info_(style_quick_info)
{}
void operator()(SCSlot* slot)
{
const SCSkillSlot* skill_slot = static_cast<SCSkillSlot*>(slot);
const SLOTCODE style_code = skill_slot->GetCode();
const BASE_STYLEINFO* base_style_info = skill_slot->GetStyleInfo();
if (base_style_info == NULL) {
SUNLOG(eCRITICAL_LOG, _T("|["__FUNCTION__"]|斤口=can't find a style info (%d)|"),
style_code);
return;
}
assert(base_style_info->IsStyle());
//
BOOST_STATIC_ASSERT(_countof(style_quick_info_->m_wStyleClassCode) == MAX_SKILL_REGIST_COUNT);
const uint16_t* it = style_quick_info_->m_wStyleClassCode,
* const end = &style_quick_info_->m_wStyleClassCode[\
_countof(style_quick_info_->m_wStyleClassCode)];
for ( ; it != end; ++it)
{
if (*it == 0) { // stop loop
return;
}
if (*it != base_style_info->m_SkillClassCode) {
continue;
}
// register a style to a quick style slot.
RC::eSTYLE_RESULT result = manager_->LinkStyle(style_code,
static_cast<POSTYPE>(it - style_quick_info_->m_wStyleClassCode));
__UNUSED(result);
assert(result == RC::RC_STYLE_SUCCESS);
break;
}
}
// data fields ...
IQuickStyleManager* const manager_;
const BASE_StyleQuickRegistInfo* const style_quick_info_;
//
__DISABLE_COPY(QuickStyleUpdate);
};
}; //end of namespace
//==================================================================================================
// IQuickStyleManager interface
bool SkillSlotManager::UpdateStyleSlots()
{
const eCHAR_TYPE char_type = player_->GetCharType();
const int weapon_kind = player_->GetWeaponKind();
StyleQuickRegistInfoParser* const style_regist_parser = StyleQuickRegistInfoParser::Instance();
const BASE_StyleQuickRegistInfo* style_quick_info = \
style_regist_parser->GetStyleQuickRegistInfo(char_type, weapon_kind);
// TODO : need more a case study.
style_container_->ClearAll();
if (style_quick_info == NULL) {
return false;
}
//
nsSlot::QuickStyleUpdate op(this, style_quick_info);
skill_container_->ForEachSlot(op);
return true;
}
//
// IQuickStyleManager interface
RC::eSTYLE_RESULT SkillSlotManager::LinkStyle(SLOTCODE style_code, POSTYPE pos)
{
if (ValidState() == false) {
return RC::RC_STYLE_INVALIDSTATE;
}
if (slot_manager_->ValidPos(SI_STYLE, pos, false) == false) {
return RC::RC_STYLE_INVALIDPOS;
}
//
if (style_container_->IsEmpty(pos) == false) {
return RC::RC_STYLE_INVALIDPOS;
}
// 어떤 것을 올릴 수 있는지에 대한 체크 필요!
SCQuickStyleSlot slot;
slot.SetOrgCode(style_code);
static_cast<SCSlotContainer*>(style_container_)->InsertSlot(pos, slot);
return RC::RC_STYLE_SUCCESS;
}
/*
// NOTE, a style quick slot container of a player has only link style operation.
// how to do an unlink style quick slot container? it can solve by clearing a slot container
// IQuickStyleManager interface
RC::eSTYLE_RESULT SkillSlotManager::UnlinkStyle(POSTYPE pos)
{
if (ValidState() == false) {
return RC::RC_STYLE_INVALIDSTATE;
}
if (slot_manager_->ValidPos(SI_STYLE, pos, true) == false) {
return RC::RC_STYLE_INVALIDPOS;
}
//
style_container_->DeleteSlot(pos, NULL);
return RC::RC_STYLE_SUCCESS;
}
// IQuickStyleManager interface
RC::eSTYLE_RESULT SkillSlotManager::MoveStyle(POSTYPE from_pos, POSTYPE to_pos)
{
if (ValidState() == false) {
return RC::RC_STYLE_INVALIDSTATE;
}
if (slot_manager_->ValidPos(SI_STYLE, from_pos, true) == false) {
return RC::RC_STYLE_INVALIDPOS;
}
if (slot_manager_->ValidPos(SI_STYLE, to_pos, false) == false) {
return RC::RC_STYLE_INVALIDPOS;
}
SCQuickStyleSlot from_slot;
style_container_->DeleteSlot(from_pos, &from_slot);
if (false != style_container_->IsEmpty(to_pos))
{
// 한 방향 이동
RC::eSLOT_INSERT_RESULT result = \
static_cast<SCSlotContainer*>(style_container_)->InsertSlot(to_pos, from_slot);
if (RC::RC_INSERT_SLOT_SUCCESS != result)
{
static_cast<SCSlotContainer*>(style_container_)->InsertSlot(from_pos, from_slot);
return SlotInsertResultConverter::ConvertToeStyleResult(result);
}
}
else
{
// 교환
SCQuickStyleSlot to_slot;
static_cast<SCSlotContainer*>(style_container_)->DeleteSlot(to_pos, &to_slot);
RC::eSLOT_INSERT_RESULT result = \
static_cast<SCSlotContainer*>(style_container_)->InsertSlot(from_pos, to_slot);
if (RC::RC_INSERT_SLOT_SUCCESS != result)
{
//rollback
static_cast<SCSlotContainer*>(style_container_)->InsertSlot(from_pos, from_slot);
static_cast<SCSlotContainer*>(style_container_)->InsertSlot(to_pos, to_slot);
return SlotInsertResultConverter::ConvertToeStyleResult(result);
}
//
result = static_cast<SCSlotContainer*>(style_container_)->InsertSlot(to_pos, from_slot);
if (RC::RC_INSERT_SLOT_SUCCESS != result)
{
//rollback
style_container_->DeleteSlot(from_pos, NULL);
static_cast<SCSlotContainer*>(style_container_)->InsertSlot(from_pos, from_slot);
static_cast<SCSlotContainer*>(style_container_)->InsertSlot(to_pos, to_slot);
return SlotInsertResultConverter::ConvertToeStyleResult(result);
}
}
return RC::RC_STYLE_SUCCESS;
}
*/
//
// IQuickStyleManager interface
void SkillSlotManager::SerializeStyleTotalInfo(STYLE_TOTAL_INFO& style_total_info,
eSERIALIZE serialize_type)
{
BOOST_STATIC_ASSERT(_countof(style_total_info.m_Slot) == STYLE_TOTAL_INFO::MAX_SLOT_NUM);
//
const int max_number_of_slots = style_container_->GetMaxSlotNum();
if (!FlowControl::FCAssert(max_number_of_slots == _countof(style_total_info.m_Slot))) {
__asm { int 3 };
return;
}
//
if (serialize_type == SERIALIZE_LOAD)
{
style_total_info.m_Count = 0;
STYLE_TOTAL_INFO::SLOT_TYPE* it = style_total_info.m_Slot,
* const end = &style_total_info.m_Slot[_countof(style_total_info.m_Slot)];
for (POSTYPE pos = 0; pos < _countof(style_total_info.m_Slot) && it != end; ++pos)
{
if (style_container_->IsEmpty(pos)) {
continue;
}
it->m_Pos = pos;
//
SCQuickStyleSlot& slot = static_cast<SCQuickStyleSlot&>(style_container_->GetSlot(pos));
slot.CopyOut(it->m_Stream);
//
++it;
}
style_total_info.m_Count = static_cast<POSTYPE>(it - style_total_info.m_Slot);
}
else if (serialize_type == SERIALIZE_STORE)
{
if (FlowControl::FCAssert(style_container_->GetSlotNum() == 0) == false) {
__asm { int 3 };
return;
};
const int number_of_slots = style_total_info.m_Count;
STYLE_TOTAL_INFO::SLOT_TYPE* it = style_total_info.m_Slot,
* const end = style_total_info.m_Slot + number_of_slots;
for ( ; it != end; ++it)
{
style_container_->InsertSlot(it->m_Pos, it->m_Stream);
}
}
}
//// IQuickStyleManager interface
//void SkillSlotManager::SerializeStyleStream(POSTYPE pos,
// STYLESTREAM* IN stream, eSERIALIZE serialize_type)
//{
// if (serialize_type == SERIALIZE_LOAD)
// {
// SCQuickStyleSlot& slot = static_cast<SCQuickStyleSlot&>(style_container_->GetSlot(pos));
// slot.CopyOut(*stream);
// }
// else if (serialize_type == SERIALIZE_STORE) {
// style_container_->InsertSlot(pos, *stream);
// }
//}
// 스타일 습득 관련
RC::eSKILL_RESULT SkillSlotManager::CanLevelUpStyle(SLOTCODE cur_style_code, SLOTCODE new_style_code)
{
if (!ValidState()) {
return RC::RC_SKILL_FAILED;
}
// 1렙씩 증가하도록 체크
if ((cur_style_code+1) != new_style_code) {
return RC::RC_SKILL_FAILED;
}
// cur_style_code에 해당하는 스타일을 습득하고 있는지 체크
SCSkillSlot* cur_skill_slot = skill_container_->GetSkillSlotByCode(cur_style_code);
if (cur_skill_slot == NULL) {
return RC::RC_SKILL_DOES_NOT_HAVE;
}
const BASE_STYLEINFO* skill_info = cur_skill_slot->GetStyleInfo();
if (skill_info == NULL) {
return RC::RC_SKILL_BASEINFO_NOTEXIST;
}
// 새로운 스킬의 요구조건을 만족하는지 체크
return CanLearnStyle(false, new_style_code);
}
//==================================================================================================
//=== Passive Skill ================================================================================
//==================================================================================================
class SearchPassiveSkillStyleAllUnLean
{
SkillSlotManager* skill_slot_manager_;
public:
SearchPassiveSkillStyleAllUnLean(SkillSlotManager* skill_slot_manager) : skill_slot_manager_(skill_slot_manager) {}
void operator () (SCSlot* pSlot)
{
SCSkillSlot* pSkillSlot = (SCSkillSlot*)pSlot;
if (!skill_slot_manager_)
return;
const SLOTCODE skillcode = pSkillSlot->GetCode();
if (pSkillSlot->IsStyle())
{
if (pSkillSlot->GetStyleInfo()->m_wRequireLV > 0)
skill_slot_manager_->UnLearnStyle(skillcode);
}
else
{
if (skill_slot_manager_->FindPassiveSkill(skillcode))
skill_slot_manager_->UnLearnSkill(skillcode);
}
}
};
void SkillSlotManager::InitPassiveSkillStyle()
{
if (!skill_container_)
return;
//스타일을 초기화 한다.
SearchPassiveSkillStyleAllUnLean Opr(this);
skill_container_->ForEachSlot(Opr);
//2010-01-15 버그수정:5690 문상현
//스타일을 초기화 하고 이전에 스타일이 초기화 되어서 사용할수 없다면 기본 스타일을 지정한다.
BASE_STYLEINFO* pPreBaseStylenfo = SkillInfoParser::Instance()->GetStyleInfo(pre_style_code_);
if (pPreBaseStylenfo && RC::RC_SKILL_SUCCESS != CanSelectStyle(pre_style_code_))
{
//패시브 초기화 할때 무기를 모두 벗는다.
//무기를 새로 장착할때 적용될수 있도록 이전 스타일 코드를 세팅한다.
pre_style_code_ = player_->GetDefaultStyle(pPreBaseStylenfo->m_iWeaponDefine);
}
//클라에게 스킬을 보내자
MSG_CG_SKILL_REFRESH_CMD SkillMsg;
player_->SerializeSkillTotalInfo(SkillMsg.m_SkillInfo, SERIALIZE_LOAD);
player_->SendPacket(&SkillMsg, SkillMsg.GetSize());
//클라에게 남은 스킬 포인트를 보내자
MSG_CG_SKILL_POINT_REMAIN_CMD CmdMsg;
CmdMsg.m_iRemainSkill = player_->GetRemainSkillPoint();
player_->SendPacket(&CmdMsg, sizeof(CmdMsg));
}
void SkillSlotManager::UnLearnStyle(SLOTCODE style_code)
{
// 현재 스타일 슬롯 정보를 얻는다.
SCSkillSlot* cur_skill_slot = skill_container_->GetSkillSlotByCode(style_code);
// 스타일의 위치를 찾는다.
POSTYPE StylePos = 0;
StylePos = cur_skill_slot->GetPos();
//(shogen)(090813)
//if (!StylePos) return;
// 스킬슬롯을 삭제한다.
if (!skill_container_->DeleteSlot(StylePos, NULL))
return;
// 퀵 스타일 슬롯의 정보들이 모두 바뀐다.
UpdateStyleSlots();
//요구했던 스킬 포인트를 돌려주자.
BASE_STYLEINFO* pBaseStyleInfo = NULL;
SLOTCODE NowSkillCode = style_code;
while (NowSkillCode)
{
pBaseStyleInfo = SkillInfoParser::Instance()->GetStyleInfo(NowSkillCode);
if (!pBaseStyleInfo) return;
player_->GetCharInfo()->m_iRemainSkill += pBaseStyleInfo->m_byRequireSkillPoint;
if (1 == pBaseStyleInfo->m_wStyleLV)
{
//요구 포인트가 0로면 생성시 배우는 스킬이므로 다시 배워라.
if (pBaseStyleInfo->m_byRequireSkillPoint == 0)
{
SKILLSLOT SkillSlot;
LearnStyle(pBaseStyleInfo->m_SkillCode, SkillSlot);
}
break;
}
NowSkillCode--;
}
// 스타일 Unlearn 로그 남기기
GAMELOG->WriteSkillLevelDown(player_, style_code, false);
// 스타일 획득 로그 남기기 끝
}
//==================================================================================================
//=== Quick Style ==================================================================================
//==================================================================================================
class QuickStyleLevelUp
{
BOOL m_bComplete;
SLOTCODE m_CurStyleCode;
SLOTCODE m_NewStyleCode;
public:
QuickStyleLevelUp(SLOTCODE cur_style_code, SLOTCODE new_style_code) : m_CurStyleCode(cur_style_code),
m_NewStyleCode(new_style_code), m_bComplete(0) {}
void operator () (SCSlot* pSlot)
{
SCQuickStyleSlot* pStyleSlot = (SCQuickStyleSlot*)pSlot;
if (m_bComplete) return;
if (pStyleSlot->GetOrgCode() != m_CurStyleCode) return;
pStyleSlot->SetOrgCode(m_NewStyleCode);
m_bComplete = true;
}
};
//--------------------------------------------------------------------------------------------------
void SkillSlotManager::LevelUpStyle(SLOTCODE cur_style_code, SLOTCODE new_style_code,
SKILLSLOT& OUT new_skill_slot)
{
if (cur_style_code == new_style_code) {
return;
}
// 현재 스킬슬롯
const SCSkillSlot* cur_style_slot = skill_container_->GetSkillSlotByCode(cur_style_code);
if (cur_style_slot == NULL) {
return;
};
const BASE_STYLEINFO* cur_style_info = cur_style_slot->GetStyleInfo();
assert(cur_style_info);
//
const POSTYPE cur_style_slot_pos = cur_style_slot->GetPos();
//
SCSkillSlot making_skill_slot;
making_skill_slot.SetCode(new_style_code);
making_skill_slot.SetPos(cur_style_slot_pos);
//
const BASE_STYLEINFO* making_style_info = making_skill_slot.GetStyleInfo();
if (making_style_info == NULL) {
return; // critical
}
if (cur_style_info->m_SkillClassCode != making_style_info->m_SkillClassCode) {
return; // critical
}
//
SCSkillSlot rollback_cur_slot = *cur_style_slot;
// 현재 스킬슬롯을 삭제한다.
if (!skill_container_->DeleteSlot(cur_style_slot_pos, NULL))
{
int* remain_skill_point = &player_->GetCharInfo()->m_iRemainSkill;
(*remain_skill_point) += making_style_info->m_byRequireSkillPoint;
return;
}
// 새롭게 스킬슬롯을 추가한다.
if (RC::RC_INSERT_SLOT_SUCCESS !=
skill_container_->InsertSlot(cur_style_slot_pos, making_skill_slot))
{
int* remain_skill_point = &player_->GetCharInfo()->m_iRemainSkill;
(*remain_skill_point) += making_style_info->m_byRequireSkillPoint;
// 이전 스타일을 복구해준다.
skill_container_->InsertSlot(cur_style_slot_pos, rollback_cur_slot);
return;
}
// new_skill_slot에 값을 셋팅한다. f100825.2L, fix up a value containing an invalid position.
new_skill_slot.m_Pos = cur_style_slot_pos;
making_skill_slot.CopyOut(new_skill_slot.m_Stream);
// QuickSlot에 스킬이 등록되어 있다면 레벨업 시켜준다.
QuickStyleLevelUp Opr(cur_style_code, new_style_code);
style_container_->ForEachSlot(Opr);
// 레벨업한 스타일이 내가 선택하고 있던 스타일이라면
// 새롭게 버프를 적용하고, 선택 스타일 코드를 새걸로 바꿔준다.
if (player_->GetBaseSelectedStyle() == cur_style_code)
{
StatusManager* const status_manager = player_->GetStatusManager();
if (status_manager->Status.IsDragonTransforming()) {
player_->SetBaseSelectedStyle(new_style_code);
}
else {
SetSelectStyle(new_style_code);
}
}
//
GAMELOG->WriteSkillLevelUp(player_, new_style_code, true);
}
RC::eSKILL_RESULT SkillSlotManager::CanLearnStyle(BOOL is_first_learn, SLOTCODE new_style_code)
{
if (ValidState() == false) {
return RC::RC_SKILL_FAILED;
}
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
// new_style_code에 해당하는 스타일이 스타일스크립트에 존재하는지 체크
const BASE_STYLEINFO* new_style_info = skill_parser->GetStyleInfo(new_style_code);
if (new_style_info == NULL) {
return RC::RC_SKILL_BASEINFO_NOTEXIST;
}
// 최초 습득일 경우 1렙인지 체크
if (is_first_learn && new_style_info->m_wStyleLV != 1)
{
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|斤口=request an invalid style learn process ")
_T("which is requested %d skill over than 1 level style on first learn, ")
_T("hack? bug?|CharName=%s|"),
new_style_code, player_->GetCharName());
return RC::RC_SKILL_FAILED;
}
// 스타일레벨이 만렙(LV10)인 경우, 오버가능 레벨 체크 필요!!!
if (new_style_info->m_wStyleLV > MAX_SKILL_LEVEL) {
return RC::RC_SKILL_MAX_LEVEL_LIMIT;
}
// 캐릭터 클래스 조건이 맞는지 체크
if (player_->GetCharType() != new_style_info->m_byClassDefine) {
return RC::RC_SKILL_CHAR_CLASS_LIMIT;
}
// 해당 클래스의 스타일을 이미 습득하고 있으며, 기존의 레벨이 새로운 레벨보다 같거나 높을 경우
SCSkillSlot* cur_skill_slot = \
skill_container_->GetSkillSlotByClass(new_style_info->m_SkillClassCode);
const BASE_STYLEINFO* cur_style_info = \
cur_skill_slot == NULL ? NULL : cur_skill_slot->GetStyleInfo();
if (cur_skill_slot)
{
if (cur_style_info->m_wStyleLV >= new_style_info->m_wStyleLV) {
return RC::RC_SKILL_ALREADY_EXIST_SKILL;
}
}
//
const int remains_skill_point = player_->GetCharInfo()->m_iRemainSkill;
// 남은 스킬포인트가 요구스킬포인트보다 적은 경우
if (remains_skill_point < new_style_info->m_byRequireSkillPoint) {
return RC::RC_SKILL_REMAIN_SKILLPOINT_LACK;
}
// 새로운 스타일의 요구레벨을 만족하는지 체크
if (player_->GetLevel() < new_style_info->m_wRequireLV) {
return RC::RC_SKILL_REQUIRE_LEVEL_LIMIT;
}
return RC::RC_SKILL_SUCCESS;
}
void SkillSlotManager::LearnStyle(SLOTCODE new_style_code, SKILLSLOT& OUT new_skill_slot)
{
// 새롭게 스킬슬롯 정보를 셋팅한다.
SCSkillSlot making_slot;
making_slot.SetCode(new_style_code);
//
const BASE_STYLEINFO* new_style_info = making_slot.GetStyleInfo();
if (new_style_info == NULL) {
assert(new_style_info != NULL);
return;
}
//
POSTYPE empty_pos = 0;
// f100930.6L, expected a NOT condition check, but it did occur a NOT-NOT condition check.
if (skill_container_->GetEmptyPos(empty_pos) == false) {
return;
}
// 스킬슬롯을 추가한다.
if (RC::RC_INSERT_SLOT_SUCCESS != skill_container_->InsertSlot(empty_pos, making_slot))
{
int* remain_skill_point = &player_->GetCharInfo()->m_iRemainSkill;
(*remain_skill_point) += new_style_info->m_byRequireSkillPoint;
return;
}
// new_skill_slot에 값을 셋팅한다. f100825.2L, fix up... find a related reference above section
new_skill_slot.m_Pos = empty_pos;
making_slot.CopyOut(new_skill_slot.m_Stream);
// 퀵 스타일 슬롯의 정보들이 모두 바뀐다.
UpdateStyleSlots();
//
GAMELOG->WriteSkillLevelUp(player_, new_style_code, false);
}
#endif //SKILL_SYSTEM_VERSION < 2
//==================================================================================================
//==================================================================================================
//==================================================================================================
// NOTE: Default Styles, this section is a generic style routines
//==================================================================================================
// 스타일 지정
RC::eSKILL_RESULT SkillSlotManager::CanSelectStyle(SLOTCODE new_style_code)
{
const SLOTCODE cur_style_code = player_->GetSelectedStyle();
// NOTE, f100825.1L, add a rule
if (cur_style_code == new_style_code) {
return RC::RC_SKILL_INVLIDPOS;
}
// 변신상태이면 스타일 지정 실패
StatusManager* const status_manager = player_->GetStatusManager();
if (status_manager->Status.IsDragonTransforming()) {
return RC::RC_SKILL_UNABLE_FUNCTION_FOR_TRANSFORMATION;
}
if (!ValidState()) {
return RC::RC_SKILL_FAILED;
}
// 해당하는 스타일을 습득하고 있는지 체크한다.
SCSkillSlot* new_skill_slot = skill_container_->GetSkillSlotByCode(new_style_code);
if (!new_skill_slot) {
return RC::RC_SKILL_DOES_NOT_HAVE;
}
// 해당하는 스타일이 스타일스크립트에 존재하는지 체크
const BASE_STYLEINFO* base_style_info = new_skill_slot->GetStyleInfo();
if (!base_style_info) {
return RC::RC_SKILL_BASEINFO_NOTEXIST;
}
// 캐릭터 클래스 체크
if (player_->GetCharType() != base_style_info->m_byClassDefine)
{
SUNLOG(eFULL_LOG, "[SkillManager::CanSelectStyle] The Type Of Character is Invalid! ");
return RC::RC_SKILL_CHAR_CLASS_LIMIT;
}
// 무기 제한
if (base_style_info->m_iWeaponDefine != -1)
{
int cur_weapon_kind = player_->GetWeaponKind();
if (cur_weapon_kind != base_style_info->m_iWeaponDefine)
{
SUNLOG(eFULL_LOG, _T("|["__FUNCTION__"]|斤口=the weapon type is invalid|"));
return RC::RC_SKILL_WEAPON_LIMIT;
}
}
// 요구레벨을 체크한다.
if (player_->GetLevel() < base_style_info->m_wRequireLV)
{
SUNLOG(eFULL_LOG, _T("|["__FUNCTION__"]|")
_T("Msg=the level of player not meet a requirement about a level|"));
return RC::RC_SKILL_REQUIRE_LEVEL_LIMIT;
}
return RC::RC_SKILL_SUCCESS;
}
//--------------------------------------------------------------------------------------------------
void SkillSlotManager::SetSelectStyle(SLOTCODE new_style_code)
{
const SLOTCODE cur_style_code = player_->GetSelectedStyle();
// NOTE, f100825.1L, add a rule
if (cur_style_code == new_style_code) {
return;
}
//
MSG_CG_STYLE_SELECT_STYLE_BRD msg_brd;
msg_brd.m_CurStyleCode = cur_style_code;
// 이전 버프를 해제한다.
ReleaseStyleBuff(cur_style_code);
// 스타일을 지정한다.
player_->SetBaseSelectedStyle(new_style_code);
// 새로 지정한 스타일의 버프를 적용한다.
ApplyStyleBuff(new_style_code);
// 주변에 패킷을 날린다.
msg_brd.m_NewStyleCode = new_style_code;
msg_brd.m_dwObjectKey = player_->GetObjectKey();
player_->SendPacketAround(&msg_brd, sizeof(msg_brd));
}
//--------------------------------------------------------------------------------------------------
#if SKILL_SYSTEM_VERSION < 2
void SkillSlotManager::ApplyStyleBuff(SLOTCODE style_code)
{
// CHANGES f100825.2L, change a skill attributes calculator process to a delayed update concept
const BASE_STYLEINFO* base_style_info = SkillInfoParser::Instance()->GetStyleInfo(style_code);
if (base_style_info == NULL) {
return;
};
// f100920.2L, add an attributes calculator skip filter
if (base_style_info->m_iAttackRate == 0 &&
base_style_info->m_iAvoidRate == 0 &&
base_style_info->m_iAttackSpeed == 0 &&
base_style_info->m_iBonusDefence == 0 &&
base_style_info->m_wCriticalBonus == 0)
{
return;
}
//
Attributes& attributes = player_->GetAttr();
SkillAttrCalculator skill_calculator(attributes);
// 공격 성공 증가
if (base_style_info->m_iAttackRate) {
skill_calculator.AddAttr(eATTR_PHYSICAL_ATTACK_SUCCESS_RATIO, VALUE_TYPE_VALUE,
base_style_info->m_iAttackRate, true);
}
// 블록 성공 증가
if (base_style_info->m_iAvoidRate) {
skill_calculator.AddAttr(eATTR_PHYSICAL_ATTACK_BLOCK_RATIO, VALUE_TYPE_VALUE,
base_style_info->m_iAvoidRate, true);
}
// 공속 증가
if (base_style_info->m_iAttackSpeed) {
skill_calculator.AddAttr(eATTR_ATTACK_SPEED, VALUE_TYPE_VALUE,
base_style_info->m_iAttackSpeed, true);
}
// 방어력 증가
if (base_style_info->m_iBonusDefence) {
skill_calculator.AddAttr(eATTR_OPTION_ALL_DEFENSE_POWER, VALUE_TYPE_VALUE,
base_style_info->m_iBonusDefence, true);
}
// 크리티컬 확률 증가
if (base_style_info->m_wCriticalBonus) {
skill_calculator.AddAttr(eATTR_ADD_ALL_CRITICAL_RATIO, VALUE_TYPE_VALUE,
base_style_info->m_wCriticalBonus, true);
}
//
attributes.UpdateEx();
}
//--------------------------------------------------------------------------------------------------
void SkillSlotManager::ReleaseStyleBuff(SLOTCODE style_code)
{
// CHANGES f100825.2L, change a skill attributes calculator process to a delayed update concept
const BASE_STYLEINFO* base_style_info = SkillInfoParser::Instance()->GetStyleInfo(style_code);
if (base_style_info == NULL) {
return;
};
// f100920.2L, add an attributes calculator skip filter
if (base_style_info->m_iAttackRate == 0 &&
base_style_info->m_iAvoidRate == 0 &&
base_style_info->m_iAttackSpeed == 0 &&
base_style_info->m_iBonusDefence == 0 &&
base_style_info->m_wCriticalBonus == 0)
{
return;
}
//
Attributes& attributes = player_->GetAttr();
SkillAttrCalculator skill_calculator(attributes);
// 공격 성공 감소
if (base_style_info->m_iAttackRate) {
skill_calculator.DelAttr(eATTR_PHYSICAL_ATTACK_SUCCESS_RATIO, VALUE_TYPE_VALUE,
base_style_info->m_iAttackRate, true);
}
// 블록 성공 감소
if (base_style_info->m_iAvoidRate) {
skill_calculator.DelAttr(eATTR_PHYSICAL_ATTACK_BLOCK_RATIO, VALUE_TYPE_VALUE,
base_style_info->m_iAvoidRate, true);
}
// 공속 감소
if (base_style_info->m_iAttackSpeed) {
skill_calculator.DelAttr(eATTR_ATTACK_SPEED, VALUE_TYPE_VALUE,
base_style_info->m_iAttackSpeed, true);
}
// 방어력 감소
if (base_style_info->m_iBonusDefence) {
skill_calculator.DelAttr(eATTR_OPTION_ALL_DEFENSE_POWER, VALUE_TYPE_VALUE,
base_style_info->m_iBonusDefence, true);
}
// 크리티컬 확률 감소
if (base_style_info->m_wCriticalBonus) {
skill_calculator.DelAttr(eATTR_ADD_ALL_CRITICAL_RATIO, VALUE_TYPE_VALUE,
base_style_info->m_wCriticalBonus, true);
}
//
attributes.UpdateEx();
}
#endif //SKILL_SYSTEM_VERSION < 2
//==================================================================================================
//==================================================================================================
//==================================================================================================
namespace inner_impl {
// 스킬 직업 줄에 사용된 스킬 포인트를 얻는다.
int GetUsedSkillPointFromJobLine(
const SkillJobTree* const job_tree,
const int line_index,
const SCSkillSlotContainer& skill_container)
{
const SkillInfoParser* const skill_parser = SkillInfoParser::Instance();
int used_skill_point = 0;
for (int col_index = 0; col_index < job_tree->GetColumnSize(); ++col_index)
{
const SkillJobTree::Desc* const job_desc = job_tree->GetDesc(line_index, col_index);
if (job_desc == NULL)
{
return -1;
}
const SCSkillSlot* const skill_slot =
skill_container.GetSkillSlotByClass(job_desc->skill_class_code);
if (skill_slot == NULL)
{
continue;
}
const SkillScriptInfo* const skill_info = skill_slot->GetSkillInfo();
if (skill_info == NULL)
{
return -1;
}
SLOTCODE skill_code = skill_info->m_SkillCode;
for (int level = skill_info->m_wSkillLv; level >= 0; --level, --skill_code)
{
const SkillScriptInfo* const temp_skill_info = skill_parser->GetSkillInfo(skill_code);
if (temp_skill_info == NULL)
{
continue;
}
used_skill_point += temp_skill_info->m_byRequireSkillPoint;
}
}
return used_skill_point;
}
// 스킬 줄 검사 방법
struct SkillLineTestMethod
{
enum Value
{
kTotalLine = 1, // 줄 전체 단위 검사
kJobLine = 2 // 직업 단위 검사
};
#ifdef _NA_004530_20120315_1202_SKILL_RENEWAL
enum { kTestMethod = kJobLine };
enum { kRequirePoint = REQUIRE_USED_SKILL_POINT_FOR_NEXT_LINE };
#else
enum { kTestMethod = kTotalLine };
enum { kRequirePoint = REQUIRE_USED_SKILL_POINT_FOR_NEXT_LINE };
#endif
};
template <int kTestMethod>
bool IsEnableSkillJobLine(
const int char_type,
const int last_skill_line,
const STLX_VECTOR<int>& last_job_lines,
const SCSkillSlotContainer& skill_container)
{
return false;
}
// 스킬 직업 줄이 선택 가능한지 검사한다. (줄 전체 단위 검사)
template <>
bool IsEnableSkillJobLine<SkillLineTestMethod::kTotalLine>(
const int char_type,
const int last_skill_line,
const STLX_VECTOR<int>& last_job_lines,
const SCSkillSlotContainer& skill_container)
{
if (last_skill_line < 2)
{
return true;
}
// 바로 아래 줄까지 사용된 스킬 포인트 합을 구한다.
int used_skill_point = 0;
const SkillInfoParser* const skill_parser = SkillInfoParser::Instance();
for (int line_index = 1; line_index < last_skill_line; ++line_index)
{
for (int job_type = 0; job_type < skill_parser->GetJobSize(); ++job_type)
{
const SkillJobTree* const job_tree = skill_parser->GetJobTree(char_type, job_type);
if (job_tree == NULL)
{
return false;
}
if (last_skill_line > job_tree->GetLineSize())
{
return false;
}
const int job_skill_point =
GetUsedSkillPointFromJobLine(job_tree, line_index - 1, skill_container);
if (job_skill_point > 0)
{
used_skill_point += job_skill_point;
}
}
}
const int require_point = (last_skill_line - 1) * SkillLineTestMethod::kRequirePoint;
if (used_skill_point < require_point)
{
return false;
}
return true;
}
// 스킬 직업 줄이 선택 가능한지 검사한다. (직업 단위 검사)
template <>
bool IsEnableSkillJobLine<SkillLineTestMethod::kJobLine>(
const int char_type,
const int last_skill_line,
const STLX_VECTOR<int>& last_job_lines,
const SCSkillSlotContainer& skill_container)
{
__UNUSED(last_skill_line);
const SkillInfoParser* const skill_parser = SkillInfoParser::Instance();
for (int job_type = 0; job_type < skill_parser->GetJobSize(); ++job_type)
{
const SkillJobTree* const job_tree = skill_parser->GetJobTree(char_type, job_type);
if (job_tree == NULL)
{
return false;
}
const int last_job_line = last_job_lines[job_type];
if (last_job_line > job_tree->GetLineSize())
{
return false;
}
for (int line_index = 1; line_index < last_job_line; ++line_index)
{
// 바로 아래 줄의 사용 포인트 합을 계산한다.
const int used_skill_point =
GetUsedSkillPointFromJobLine(job_tree, line_index - 1, skill_container);
if (used_skill_point < SkillLineTestMethod::kRequirePoint)
{
return false;
}
}
}
return true;
}
} // inner_impl
//_NA_0_20110131_SKILLTREE_SYSTEM_RENEWAL
// 해당 스킬을 익히기 위한 상위 스킬의 스킬레벨이 만족되는지 검사.
RC::eSKILL_RESULT SkillSlotManager::check_enough_upper_skill_level(
const SCSkillSlotContainer* skill_container, const SkillScriptInfo* skill_info) const
{
// 해당 스킬을 익히기 위한 상위 스킬이 존재하는 경우.
if (skill_info->m_UpperSkillClass != 0)
{
// 상위 스킬의 클래스 값이 존재하는지 찾는다.
const SCSkillSlot* const upper_skill = skill_container->GetSkillSlotByClass(skill_info->m_UpperSkillClass);
if (upper_skill == NULL)
{
return RC::RC_SKILL_CANT_FIND_UPPER_SKILL;
}
const SkillScriptInfo* const upper_skill_info = upper_skill->GetSkillInfo();
if (upper_skill_info == NULL)
{
return RC::RC_SKILL_BASEINFO_NOTEXIST;
}
// 상위 스킬의 레벨이 부족하다.
if (upper_skill_info->m_wSkillLv < skill_info->m_RequireUpperSkillLevel)
{
return RC::RC_SKILL_ISNOT_ENOUGH_UPPER_SKILL_LEVEL;
}
}
return RC::RC_SKILL_SUCCESS;
}
RC::eSKILL_RESULT SkillSlotManager::LevelUpSkill(
SKILL_TOTAL_INFO& OUT total_stream,
const SLOTCODE* skill_code_array,
const uint skill_code_count)
{
if (ValidState() == false)
{
return RC::RC_SKILL_FAILED;
}
const SkillInfoParser* const skill_parser = SkillInfoParser::Instance();
// 원본 스킬 컨테이너를 보관해 놓는다.
// 스킬의 레벨업시 임시 컨테이너에 적용시킨 후 모든 스킬 레벨업이 완료시 원본에 덮어쓴다.
SCSkillSlotContainer temp_skill_container;
skill_container_->Copy(&temp_skill_container);
// 레벨업할 스킬들이 유효한지 검사한다.
STLX_VECTOR<LevelUpData> levelup_data_array;
levelup_data_array.clear();
for (int i = 0; i < skill_code_count; ++i)
{
const SLOTCODE new_skill_code = skill_code_array[i];
const SkillScriptInfo* const new_skill_info = skill_parser->GetSkillInfo(new_skill_code);
if (new_skill_info == NULL)
{
return RC::RC_SKILL_BASEINFO_NOTEXIST;
}
LevelUpData levelup_data;
const RC::eSKILL_RESULT test_result =
skill_levelup_test(&levelup_data, &temp_skill_container, new_skill_info);
if (test_result != RC::RC_SKILL_SUCCESS)
{
return test_result;
}
levelup_data_array.push_back(levelup_data);
}
// 필요한 스킬 포인트와 마지막 줄번호를 얻는다.
uint require_total_skill_point = 0;
WORD last_skill_line = 0;
STLX_VECTOR<int> last_job_lines;
for (int job_type = 0; job_type < skill_parser->GetJobSize(); ++job_type)
{
last_job_lines.push_back(0);
}
for (size_t i = 0; i < levelup_data_array.size(); ++i)
{
const LevelUpData& levelup_data = levelup_data_array[i];
const SkillScriptInfo* const skill_info =
skill_parser->GetSkillInfo(levelup_data.new_skill_code);
if (skill_info == NULL)
{
return RC::RC_SKILL_FAILED;
}
const RC::eSKILL_RESULT upper_skill_check_result =
check_enough_upper_skill_level(&temp_skill_container, skill_info);
if (upper_skill_check_result != RC::RC_SKILL_SUCCESS)
{
return upper_skill_check_result;
}
require_total_skill_point += levelup_data.require_skill_point;
const int job_line = levelup_data.job_line_index + 1;
if (job_line > last_job_lines[levelup_data.job_type])
{
last_job_lines[levelup_data.job_type] = job_line;
if (job_line > last_skill_line)
{
last_skill_line = static_cast<WORD>(job_line);
}
}
}
// 남아있는 스킬 포인트가 충분한지 검사한다.
const int remain_skill_point = player_->GetCharInfo()->m_iRemainSkill;
if (remain_skill_point < require_total_skill_point)
{
return RC::RC_SKILL_REMAIN_SKILLPOINT_LACK;
}
// 사용 가능한 줄인지 검사한다.
const int char_type = player_->GetCharInfo()->m_byClassCode;
if (inner_impl::IsEnableSkillJobLine<inner_impl::SkillLineTestMethod::kTestMethod>(
char_type, last_skill_line, last_job_lines, temp_skill_container) == false)
{
return RC::RC_SKILL_DISABLE_SKILL_LINE;
}
// 직렬화 한다.
const uint serialize_count = serialize_skill_array(
total_stream, &temp_skill_container, skill_code_array, skill_code_count);
if (serialize_count != skill_code_count)
{
return RC::RC_SKILL_FAILED;
}
// 실제 스킬을 적용한다.
temp_skill_container.Copy(skill_container_);
for (size_t i = 0; i < levelup_data_array.size(); ++i)
{
const LevelUpData& levelup_data = levelup_data_array[i];
apply_skill_levelup(&levelup_data);
}
//_NA_001990_ACSYSTEM_ADD
// AC를 처리한다.
player_->GetACManager()->ProcessSkillStepUp(last_skill_line);
return RC::RC_SKILL_SUCCESS;
}
uint SkillSlotManager::serialize_skill_array(
SKILL_TOTAL_INFO& OUT total_stream,
SCSkillSlotContainer* skill_container,
const SLOTCODE* skill_code_array,
const uint skill_code_count) const
{
BOOST_STATIC_ASSERT(MAX_SKILL_SLOT_NUM == _countof(total_stream.m_Slot));
total_stream.m_Count = 0; // CHANGES: f110215.5L
SKILLSLOT* it = total_stream.m_Slot;
for (uint i = 0; i < skill_code_count; ++i)
{
const SLOTCODE skill_code = skill_code_array[i];
if (skill_code == 0)
{
continue;
}
const SCSkillSlot* const skill_slot = skill_container->GetSkillSlotByCode(skill_code);
if (skill_slot == NULL)
{
continue;
}
it->m_Pos = skill_slot->GetPos();
skill_container->SerializeSkillStream(skill_slot->GetPos(), &it->m_Stream, SERIALIZE_LOAD);
++it;
}
total_stream.m_Count = static_cast<POSTYPE>(it - total_stream.m_Slot);
return total_stream.m_Count;
}
void SkillSlotManager::apply_skill_levelup(const LevelUpData* levelup_data)
{
if (levelup_data->new_skill_code == 0)
{
return;
}
// 패시브 스킬이라면 이전스킬을 제거하고 새로운 스킬을 추가한다.
// 이전 스킬 쿨타임 제거 및 새스킬 쿨타임 등록
// QuickSlot에 스킬이 등록되어 있다면 레벨업 시켜준다.
if (levelup_data->old_skill_code)
{
nsSlot::QuickSlotOriginalCodeChanger do_change_event(
quick_container_, levelup_data->old_skill_code, levelup_data->new_skill_code);
if (levelup_data->is_passive_skill)
{
RemovePassiveSkill(levelup_data->old_skill_code);
}
else
{
player_->FreeSkillCoolTime(levelup_data->old_skill_code);
}
// 소환체 스킬이라면 이전스킬을 제거하고 새로운 스킬을 추가한다.
if (levelup_data->is_summon_skill)
{
SummonManager* const summon_manager = SummonManager::Instance();
const DWORD summoner_key = player_->GetObjectKey(); // ... player key?
summon_manager->DelSummonedSkill(summoner_key, levelup_data->old_skill_code);
}
}
if (levelup_data->is_passive_skill)
{
AddAttrForPassiveSkill(levelup_data->new_skill_code);
}
else
{
player_->SetSkillCoolTime(levelup_data->new_skill_code, SKILL_COOLTIME_MARGIN);
}
if (levelup_data->is_summon_skill)
{
SummonManager* const summon_manager = SummonManager::Instance();
const DWORD summoner_key = player_->GetObjectKey(); // ... player key?
summon_manager->AddSummonedSkill(summoner_key, levelup_data->new_skill_code);
}
player_->GetCharInfo()->m_iRemainSkill -= levelup_data->require_skill_point;
const bool is_levelup = (levelup_data->old_skill_code != 0) ? true : false;
GAMELOG->WriteSkillLevelUp(player_, levelup_data->new_skill_code, is_levelup);
}
RC::eSKILL_RESULT SkillSlotManager::skill_levelup_test(
LevelUpData* OUT levelup_data,
SCSkillSlotContainer* INOUT skill_container,
const SkillScriptInfo* new_skill_info) const
{
uint require_point = 0;
SLOTCODE cur_skill_code = 0;
const SLOTCODE new_skill_code = new_skill_info->m_SkillCode;
const SLOTCODE new_skill_class_code = new_skill_info->m_SkillClassCode;
// 새 스킬이 스킬트리에 등록되어 있는지 검사한다.
const eCHAR_TYPE character_class = (eCHAR_TYPE)player_->GetCharInfo()->m_byClassCode;
const SkillInfoParser* const skill_parser = SkillInfoParser::Instance();
const SkillJobTree::Desc* const job_desc =
skill_parser->FindAllJobDesc(character_class, new_skill_class_code);
if (job_desc == NULL)
{
SUNLOG(eCRITICAL_LOG, "[%s][%d] can not find skill desc", __FUNCTION__, __LINE__);
return RC::RC_SKILL_FAILED;
}
// 같은 종류의 스킬이라면
const SCSkillSlot* const cur_skill_slot =
skill_container->GetSkillSlotByClass(new_skill_class_code);
if (cur_skill_slot != NULL)
{
// 최대 레벨인지 검사한다.
const SkillScriptInfo* const cur_skill_info = cur_skill_slot->GetSkillInfo();
if (cur_skill_info == NULL)
{
return RC::RC_SKILL_BASEINFO_NOTEXIST;
}
if (cur_skill_info->m_wSkillLv >= MAX_SKILL_LEVEL)
{
return RC::RC_SKILL_MAX_LEVEL_LIMIT;
}
if (cur_skill_info->m_SkillCode == new_skill_code)
{
return RC::RC_SKILL_FAILED;
}
cur_skill_code = cur_skill_info->m_SkillCode;
// 레벨업할 스킬의 필요 포인트를 계산한다.
for (SLOTCODE skill_code = cur_skill_code + 1; skill_code <= new_skill_code; ++skill_code)
{
const SkillScriptInfo* const skill_info = skill_parser->GetSkillInfo(skill_code);
if (skill_info == NULL)
{
return RC::RC_SKILL_DOES_NOT_HAVE;
}
if (skill_info->m_SkillClassCode != new_skill_class_code)
{
return RC::RC_SKILL_DOES_NOT_HAVE;
}
require_point += skill_info->m_byRequireSkillPoint;
}
const POSTYPE skill_position = cur_skill_slot->GetPos();
SCSkillSlot new_skill_slot;
new_skill_slot.SetCode(new_skill_code);
new_skill_slot.SetPos(skill_position);
if (!skill_container->DeleteSlot(skill_position, NULL))
{
return RC::RC_SKILL_POSITION_INVALID;
}
if (skill_container->InsertSlot(skill_position, new_skill_slot) != RC::RC_INSERT_SLOT_SUCCESS)
{
return RC::RC_SKILL_FAILED;
}
}
else
{
POSTYPE skill_position = 0;
if (!skill_container->GetEmptyPos(skill_position))
{
return RC::RC_SKILL_POSITION_INVALID;
}
// 추가할 스킬의 필요 포인트를 계산한다.
for (SLOTCODE skill_code = job_desc->first_skill_code;
skill_code <= new_skill_code; ++skill_code)
{
const SkillScriptInfo* const skill_info = skill_parser->GetSkillInfo(skill_code);
if (skill_info == NULL)
{
return RC::RC_SKILL_DOES_NOT_HAVE;
}
if (skill_info->m_SkillClassCode != new_skill_class_code)
{
return RC::RC_SKILL_DOES_NOT_HAVE;
}
require_point += skill_info->m_byRequireSkillPoint;
}
SCSkillSlot new_skill_slot;
new_skill_slot.SetCode(new_skill_code);
new_skill_slot.SetPos(skill_position);
if (skill_container->InsertSlot(skill_position, new_skill_slot) != RC::RC_INSERT_SLOT_SUCCESS)
{
return RC::RC_SKILL_FAILED;
}
}
levelup_data->new_skill_code = new_skill_code;
levelup_data->old_skill_code = cur_skill_code;
levelup_data->require_skill_point = require_point;
levelup_data->skill_line = job_desc->line_index + 1;
levelup_data->is_summon_skill = (new_skill_info->m_bySkillUserType == eSKILL_USER_SUMMONED) ? true : false;
levelup_data->is_passive_skill = (new_skill_info->m_bySkillType == SKILL_TYPE_PASSIVE) ? true : false;
levelup_data->job_type = job_desc->job_type;
levelup_data->job_line_index = job_desc->line_index;
return RC::RC_SKILL_SUCCESS;
}
class FindSkillCount
{
SkillSlotManager* skill_slot_manager_;
uint skill_count_;
public:
FindSkillCount(SkillSlotManager* skill_slot_manager)
: skill_slot_manager_(skill_slot_manager), skill_count_(0) {}
void operator()(SCSlot* slot)
{
if (skill_slot_manager_ == NULL)
return;
SCSkillSlot* pSkillSlot = static_cast<SCSkillSlot*>(slot);
if (pSkillSlot->GetSkillInfo()) // 스킬일 경우만 값이 있다.
++skill_count_;
}
uint get_skill_count() const
{
return skill_count_;
}
};
void SkillSlotManager::UnLockFirstSkillLine()
{
#ifdef _NA_004530_20120315_1202_SKILL_RENEWAL
// 첫 번째 줄의 스킬도 기본으로 배우지 않는다.
return;
#else
FindSkillCount Opr(this);
skill_container_->ForEachSlot(Opr);
if (Opr.get_skill_count() > 0)
return;
const SkillInfoParser* skill_parser = SkillInfoParser::Instance();
eCHAR_TYPE character_class = (eCHAR_TYPE)player_->GetCharInfo()->m_byClassCode;
const SkillTree* skill_tree = skill_parser->get_skill_tree(character_class);
if (skill_tree == NULL)
{
ASSERT(0);
return;
}
// 원본 스킬 컨테이너를 보관해 놓는다.
// 스킬의 레벨업시 임시 컨테이너에 적용시킨 후 모든 스킬 레벨업이 완료시 원본에 덮어쓴다.
SCSkillSlotContainer temp_skill_container;
skill_container_->Copy(&temp_skill_container);
STLX_VECTOR<LevelUpData> levelup_data_array;
levelup_data_array.clear();
LevelUpData levelup_data;
// Line 번호는 1번 부터 시작한다.
const SkillTree::SkillDescript* skill_descript_array = skill_tree->get_skill_descript(1);
if (skill_descript_array == NULL)
{
ASSERT(0);
return;
}
const uint max_column = skill_tree->get_column_count();
for (uint column = 0 ; column < max_column ; ++column)
{
const SkillTree::SkillDescript* skill_descript = &(skill_descript_array[column]);
if (skill_descript->skill_class == 0)
continue;
const SkillScriptInfo* script_info = skill_parser->GetSkillInfo(skill_descript->first_skill_code);
if (script_info == NULL)
{
ASSERT(0);
return;
}
if (temp_skill_container.GetSkillSlotByClass(skill_descript->skill_class))
{
continue;
}
if (skill_descript->upper_skill_class != 0)
{
// 상위 스킬의 레벨 체크
if (RC::RC_SKILL_SUCCESS != check_enough_upper_skill_level(&temp_skill_container, script_info))
continue;
}
// 스킬 레벨업.
if (RC::RC_SKILL_SUCCESS != skill_levelup_test(&levelup_data, &temp_skill_container, script_info))
continue;
levelup_data_array.push_back(levelup_data);
}
// 실제 스킬을 적용한다.
temp_skill_container.Copy(skill_container_);
uint count = levelup_data_array.size();
for (uint idx = 0 ; idx < count ; ++idx)
{
const LevelUpData& levelup_data = levelup_data_array[idx];
apply_skill_levelup(&levelup_data);
}
#endif // _NA_004530_20120315_1202_SKILL_RENEWAL
}
#ifdef _NA_0_20100901_SKILLTREE_SYSTEM
namespace inner_impl {
class UsedSkillPoint
{
public:
UsedSkillPoint(const SkillSlotManager* skill_slot_manager) :
skill_slot_manager_(skill_slot_manager), used_skill_point_(0)
{
}
void operator()(const SCSlot* slot)
{
if (skill_slot_manager_ == NULL)
{
return;
}
const SCSkillSlot* const skill_slot = static_cast<const SCSkillSlot*>(slot);
const SkillScriptInfo* const skill_info = skill_slot->GetSkillInfo();
if (skill_info)
{
// 스킬 레벨을 낮추면서 각 레벨에 사용된 스킬포인트를 반납한다.
// 사용된 스킬 포인트를 반납한다.
SLOTCODE skill_code = skill_info->m_SkillCode;
for (int level = skill_info->m_wSkillLv; level >= 0; --level, --skill_code)
{
const SkillScriptInfo* const temp_skill_info =
SkillInfoParser::Instance()->GetSkillInfo(skill_code);
if (temp_skill_info == NULL)
{
continue;
}
used_skill_point_ += temp_skill_info->m_byRequireSkillPoint;
}
}
}
uint get_used_skill_point() const
{
return used_skill_point_;
}
private:
const SkillSlotManager* skill_slot_manager_;
uint used_skill_point_;
};
} // inner_impl
uint SkillSlotManager::get_used_skill_point() const
{
inner_impl::UsedSkillPoint oper(this);
skill_container_->ForEachSlot(oper);
return oper.get_used_skill_point();
}
#endif //_NA_0_20100901_SKILLTREE_SYSTEM
#ifdef _NA_007667_20141001_WITCHBLADE_SKILL
const SLOTCODE SkillSlotManager::FindSkill( const eSKILL skill_class ) const
{
SCSkillSlotContainer* skill_container = GetSkillSlotContainer();
SCSkillSlot* skill_slot = skill_container->GetSkillSlotByClass(skill_class);
if (skill_slot != NULL)
{
return skill_slot->GetSkillInfo()->m_SkillCode;
}
return 0;
}
#endif //_NA_007667_20141001_WITCHBLADE_SKILL