CN1602 is the local client. KR is the official tree with unpacked Data, System, and Sound. Packed .wpk files stay off git.
1125 lines
43 KiB
C++
1125 lines
43 KiB
C++
#include "stdafx.h"
|
|
#include "SkillAttrCalculator.h"
|
|
|
|
#include <Const.h>
|
|
#include <SCItemSlot.h>
|
|
|
|
#include "Attribute.h"
|
|
#include "SkillInfoParser.h"
|
|
#include "SetItemOptionInfoParser.h"
|
|
#include "./RidingInfoParser.h"
|
|
|
|
//==================================================================================================
|
|
|
|
// (f100812.2L)
|
|
void SkillAttrCalculator::ModifyAttrKindAndCalculatedValue(uint8_t value_type, //eVALUE_TYPE
|
|
uint8_t* attr_kind, //eATTR_KIND
|
|
int * value)
|
|
//eVALUE_TYPE value_type, int* attr_kind, int* value)
|
|
{
|
|
*attr_kind = eATTR_KIND_SKILL;
|
|
float calced_value = static_cast<float>(*value);
|
|
|
|
if (value_type == VALUE_TYPE_PERCENT_PER_MAX ||
|
|
value_type == VALUE_TYPE_PERCENT_PER_CUR)
|
|
{
|
|
*attr_kind = eATTR_KIND_SKILL_RATIO;
|
|
calced_value /= 10.0f;
|
|
}
|
|
else if (value_type == VALUE_TYPE_RATIO_VALUE)
|
|
{
|
|
calced_value /= 10.0f;
|
|
}
|
|
//
|
|
*value = static_cast<int>(calced_value);
|
|
}
|
|
|
|
float
|
|
SkillAttrCalculator::AddAttr(eATTR_TYPE attr_type, int value_type, int value, bool skip_update)
|
|
{
|
|
if (attr_type <= eATTR_TYPE_INVALID || eATTR_MAX <= attr_type)
|
|
{
|
|
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]|attr_type|%d|invalid type value", attr_type);
|
|
return 0;
|
|
}
|
|
|
|
eATTR_KIND attr_kind = eATTR_KIND_SKILL;
|
|
float calced_value = static_cast<float>(value);
|
|
|
|
if (value_type == VALUE_TYPE_PERCENT_PER_MAX ||
|
|
value_type == VALUE_TYPE_PERCENT_PER_CUR)
|
|
{
|
|
attr_kind = eATTR_KIND_SKILL_RATIO;
|
|
calced_value /= 10.0f;
|
|
}
|
|
else if (value_type == VALUE_TYPE_RATIO_VALUE)
|
|
{
|
|
calced_value /= 10.0f;
|
|
}
|
|
|
|
// (f100812.2L) change an check filter
|
|
// 공격력, 방어력은 Option 수치를 변경할 뿐 Base 수치를 변경하지는 않는다!
|
|
if (IsPassedAddAttr(attr_type) == false) {
|
|
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]|Can't change attr_type|%d|", attr_type);
|
|
return 0;
|
|
}
|
|
//
|
|
static const Attributes::Operation attr_array_for_all_stats[] = {
|
|
eATTR_STR, eATTR_DEX, eATTR_VIT, eATTR_INT, eATTR_SPR
|
|
};
|
|
const int convert_value = static_cast<int>(calced_value);
|
|
//
|
|
switch (attr_type)
|
|
{
|
|
case eATTR_INCREASE_STAT_POINT:
|
|
{
|
|
// (f100721.1L)
|
|
int value_array[_countof(attr_array_for_all_stats)] = {
|
|
convert_value, convert_value, convert_value, convert_value, convert_value
|
|
};
|
|
//
|
|
attributes_.GroupOperate(attr_array_for_all_stats->eOper_Add, attr_kind,
|
|
attr_array_for_all_stats, value_array,
|
|
_countof(attr_array_for_all_stats));
|
|
}
|
|
break;
|
|
default:
|
|
attributes_[attr_type][attr_kind] += convert_value;
|
|
break;
|
|
}
|
|
|
|
// (CHANGES) (WAVERIX)
|
|
if (skip_update == false) {
|
|
attributes_.UpdateEx();
|
|
}
|
|
|
|
return calced_value;
|
|
}
|
|
|
|
float
|
|
SkillAttrCalculator::DelAttr(eATTR_TYPE attr_type, int value_type, int value, bool skip_update)
|
|
{
|
|
if (attr_type <= eATTR_TYPE_INVALID || eATTR_MAX <= attr_type)
|
|
{
|
|
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]|attr_type|%d|invalid type value", attr_type);
|
|
return 0;
|
|
}
|
|
|
|
if (!IsRecoverAttr(attr_type)) {
|
|
return 0;
|
|
}
|
|
|
|
eATTR_KIND attr_kind = eATTR_KIND_SKILL;
|
|
float calced_value = static_cast<float>(value);
|
|
|
|
if (value_type == VALUE_TYPE_PERCENT_PER_MAX || value_type == VALUE_TYPE_PERCENT_PER_CUR)
|
|
{
|
|
attr_kind = eATTR_KIND_SKILL_RATIO;
|
|
calced_value /= 10.0f;
|
|
}
|
|
else if (value_type == VALUE_TYPE_RATIO_VALUE)
|
|
{
|
|
calced_value /= 10.0f;
|
|
}
|
|
|
|
// (REMARK) (WAVERIX) 이 위치가 AddAttr과 코드가 다르다. 정확한 이유를 파악할 필요가 있다.
|
|
static const Attributes::Operation attr_array_for_all_stats[] = {
|
|
eATTR_STR, eATTR_DEX, eATTR_VIT, eATTR_INT, eATTR_SPR
|
|
};
|
|
//
|
|
const int convert_value = static_cast<int>(calced_value);
|
|
//
|
|
switch (attr_type)
|
|
{
|
|
case eATTR_INCREASE_STAT_POINT:
|
|
{
|
|
// (f100721.1L)
|
|
int value_array[_countof(attr_array_for_all_stats)] = {
|
|
convert_value, convert_value, convert_value, convert_value, convert_value
|
|
};
|
|
//
|
|
attributes_.GroupOperate(attr_array_for_all_stats->eOper_Sub, attr_kind,
|
|
attr_array_for_all_stats, value_array,
|
|
_countof(attr_array_for_all_stats));
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
attributes_[attr_type][attr_kind] -= convert_value;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// (CHANGES) (WAVERIX)
|
|
if (skip_update == false) {
|
|
attributes_.UpdateEx();
|
|
}
|
|
return calced_value;
|
|
}
|
|
|
|
void
|
|
SkillAttrCalculator::Clear()
|
|
{
|
|
// (f100720.3L)
|
|
attributes_.ClearRelatedAttrKind(eATTR_KIND_SKILL);
|
|
/*
|
|
for (eATTR_TYPE attr_type = eATTR_TYPE_INVALID;
|
|
attr_type < eATTR_MAX;
|
|
attr_type = static_cast<eATTR_TYPE>(attr_type + 1))
|
|
{
|
|
if (!attributes_.IsNULL(attr_type))
|
|
{
|
|
attributes_[attr_type][eATTR_KIND_SKILL] = 0;
|
|
attributes_[attr_type][eATTR_KIND_SKILL_RATIO] = 0;
|
|
}
|
|
}
|
|
*/
|
|
attributes_.Update();
|
|
}
|
|
|
|
int
|
|
SkillAttrCalculator::ApplyDragonFormation(BOOL bApply, SLOTCODE skill_code, DWORD cur_hp)
|
|
{
|
|
int change_hp = 0;
|
|
float cur_hp_ratio = static_cast<float>(cur_hp) / attributes_.GetMaxHP();
|
|
|
|
const SkillScriptInfo* const base_skill_info = \
|
|
SkillInfoParser::Instance()->GetSkillInfo(skill_code);
|
|
if (base_skill_info == NULL) {
|
|
return 0;
|
|
}
|
|
// CHANGES; f110124.1L, changes register routine
|
|
const uint8_t oper_type \
|
|
= bApply ? Attributes::Operation::eOper_Add : Attributes::Operation::eOper_Sub;
|
|
//
|
|
// 변신 능력치1 적용
|
|
const BASE_ABILITYINFO* ability_info = \
|
|
base_skill_info->GetAbilityInfo(eABILITY_DRAGON_TRANSFORMATION1);
|
|
if (ability_info)
|
|
{
|
|
Attributes::OperationRecord record_array[2] =
|
|
{
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_STR,
|
|
ability_info->m_iParam[0] },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_DEX,
|
|
ability_info->m_iParam[1] },
|
|
};
|
|
attributes_.GroupBatchOperate(record_array, _countof(record_array));
|
|
};
|
|
// 변신 능력치2 적용
|
|
ability_info = base_skill_info->GetAbilityInfo(eABILITY_DRAGON_TRANSFORMATION2);
|
|
if (ability_info)
|
|
{
|
|
Attributes::OperationRecord record_array[5] =
|
|
{
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_INT,
|
|
ability_info->m_iOption1 },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_SPR,
|
|
ability_info->m_iOption2 },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_VIT,
|
|
ability_info->m_iParam[0] },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_MAX_HP,
|
|
static_cast<int>(ability_info->m_iParam[1] / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_ATTACK_SPEED,
|
|
static_cast<int>(ability_info->m_iParam[2] / 10.f) },
|
|
};
|
|
attributes_.GroupBatchOperate(record_array, _countof(record_array));
|
|
};
|
|
// 변신 능력치2 적용
|
|
ability_info = base_skill_info->GetAbilityInfo(eABILITY_DRAGON_TRANSFORMATION3);
|
|
if (ability_info)
|
|
{
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
Attributes::OperationRecord record_array[6] =
|
|
{
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL_RATIO,
|
|
eATTR_OPTION_PHYSICAL_ATTACK_POWER,
|
|
static_cast<int>(ability_info->m_iOption1 / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL_RATIO,
|
|
eATTR_MAGICAL_ALL_ATTACK_POWER,
|
|
static_cast<int>(ability_info->m_iOption2 / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL_RATIO,
|
|
eATTR_OPTION_PHYSICAL_DEFENSE_POWER,
|
|
static_cast<int>(ability_info->m_iParam[0] / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_DEL_MAGICAL_ALL_DAMAGE,
|
|
static_cast<int>(ability_info->m_iParam[1] / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_PHYSICAL_ATTACK_BLOCK_RATIO,
|
|
// CHANGES: f110124.1L, change percentage to permilage
|
|
static_cast<int>(ability_info->m_iParam[2] / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_MOVE_SPEED,
|
|
static_cast<int>(ability_info->m_iParam[3] / 10.f) },
|
|
};
|
|
attributes_.GroupBatchOperate(record_array, _countof(record_array));
|
|
#else
|
|
//
|
|
Attributes::OperationRecord record_array[6] =
|
|
{
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL_RATIO,
|
|
eATTR_OPTION_PHYSICAL_ATTACK_POWER,
|
|
static_cast<int>(ability_info->m_iOption1 / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL_RATIO,
|
|
eATTR_MAGICAL_ALL_ATTACK_POWER,
|
|
static_cast<int>(ability_info->m_iOption2 / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL_RATIO,
|
|
eATTR_OPTION_PHYSICAL_DEFENSE_POWER,
|
|
static_cast<int>(ability_info->m_iParam[0] / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL_RATIO,
|
|
eATTR_OPTION_MAGICAL_DEFENSE_POWER,
|
|
static_cast<int>(ability_info->m_iParam[1] / 10.f) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_PHYSICAL_ATTACK_BLOCK_RATIO,
|
|
static_cast<int>(ability_info->m_iParam[2]) },
|
|
{ oper_type,
|
|
eATTR_KIND_SKILL,
|
|
eATTR_MOVE_SPEED,
|
|
static_cast<int>(ability_info->m_iParam[3] / 10.f) },
|
|
};
|
|
attributes_.GroupBatchOperate(record_array, _countof(record_array));
|
|
#endif
|
|
};
|
|
#ifdef _NA_008695_20160212_DRAGON_NIGHT_DRAGONTANS_BUG2
|
|
attributes_.UpdateEx();
|
|
#else
|
|
attributes_.Update();
|
|
#endif //(_NA_008695_20160212_DRAGON_NIGHT_DRAGONTANS_BUG2)
|
|
|
|
if (cur_hp_ratio) {
|
|
int temp = static_cast<int>(attributes_.GetMaxHP() * cur_hp_ratio);
|
|
change_hp = max(1, temp) - cur_hp;
|
|
}
|
|
else {
|
|
change_hp = 0;
|
|
}
|
|
|
|
return change_hp;
|
|
//
|
|
#if SUN_CODE_BACKUP
|
|
// 변신 능력치1 적용
|
|
const BASE_ABILITYINFO* ability_info = \
|
|
base_skill_info->GetAbilityInfo(eABILITY_DRAGON_TRANSFORMATION1);
|
|
if (ability_info)
|
|
{
|
|
// (f100721.1L)
|
|
static const Attributes::Operation attr_group[] = { eATTR_STR, eATTR_DEX };
|
|
const int value_array[_countof(attr_group)] = {
|
|
ability_info->m_iParam[0], ability_info->m_iParam[1]
|
|
};
|
|
//
|
|
attributes_.GroupOperate((bApply ? attr_group->eOper_Add : attr_group->eOper_Sub),
|
|
eATTR_KIND_SKILL,
|
|
attr_group, value_array,
|
|
_countof(attr_group));
|
|
}
|
|
|
|
// 변신 능력치2 적용
|
|
ability_info = base_skill_info->GetAbilityInfo(eABILITY_DRAGON_TRANSFORMATION2);
|
|
if (ability_info)
|
|
{
|
|
// (f100721.1L)
|
|
static const Attributes::Operation attr_group[] = {
|
|
eATTR_INT, eATTR_SPR, eATTR_VIT, eATTR_ATTACK_SPEED
|
|
};
|
|
const int value_array[_countof(attr_group)] = {
|
|
ability_info->m_iOption1, ability_info->m_iOption2, ability_info->m_iParam[0],
|
|
static_cast<int>(ability_info->m_iParam[2] / 10.f)
|
|
};
|
|
attributes_.GroupOperate((bApply ? attr_group->eOper_Add : attr_group->eOper_Sub),
|
|
eATTR_KIND_SKILL,
|
|
attr_group, value_array, _countof(attr_group));
|
|
if (attributes_.GetMaxHP())
|
|
{
|
|
if (bApply)
|
|
{
|
|
attributes_[eATTR_MAX_HP][eATTR_KIND_SKILL_RATIO]
|
|
+= static_cast<int>(ability_info->m_iParam[1] / 10.f);
|
|
}
|
|
else
|
|
{
|
|
attributes_[eATTR_MAX_HP][eATTR_KIND_SKILL_RATIO]
|
|
-= static_cast<int>(ability_info->m_iParam[1] / 10.f);
|
|
}
|
|
attributes_[eATTR_MAX_HP].Update();
|
|
}
|
|
}
|
|
|
|
// 변신 능력치3 적용
|
|
ability_info = base_skill_info->GetAbilityInfo(eABILITY_DRAGON_TRANSFORMATION3);
|
|
if (ability_info)
|
|
{
|
|
// (f100721.1L)
|
|
const Attributes::Operation::eOper oper_type \
|
|
= bApply ? Attributes::Operation::eOper_Add : Attributes::Operation::eOper_Sub;
|
|
;{
|
|
static const Attributes::Operation attr_skill_array[] = {
|
|
eATTR_OPTION_PHYSICAL_ATTACK_POWER, eATTR_OPTION_MAGICAL_ATTACK_POWER,
|
|
eATTR_OPTION_PHYSICAL_DEFENSE_POWER, eATTR_OPTION_MAGICAL_DEFENSE_POWER
|
|
};
|
|
const int value_array[_countof(attr_skill_array)] = {
|
|
static_cast<int>(ability_info->m_iOption1 / 10.f),
|
|
static_cast<int>(ability_info->m_iOption2 / 10.f),
|
|
static_cast<int>(ability_info->m_iParam[0] / 10.f),
|
|
static_cast<int>(ability_info->m_iParam[1] / 10.f)
|
|
};
|
|
attributes_.GroupOperate(oper_type, eATTR_KIND_SKILL_RATIO,
|
|
attr_skill_array, value_array,
|
|
_countof(attr_skill_array));
|
|
};
|
|
//
|
|
;{
|
|
static const Attributes::Operation attr_skill_array[] = {
|
|
eATTR_PHYSICAL_ATTACK_BLOCK_RATIO, eATTR_MOVE_SPEED
|
|
};
|
|
const int value_array[_countof(attr_skill_array)] = {
|
|
ability_info->m_iParam[2],
|
|
static_cast<int>(ability_info->m_iParam[3] / 10.f)
|
|
};
|
|
attributes_.GroupOperate(oper_type, eATTR_KIND_SKILL,
|
|
attr_skill_array, value_array,
|
|
_countof(attr_skill_array));
|
|
};
|
|
//
|
|
}
|
|
|
|
attributes_.Update();
|
|
|
|
if (cur_hp_ratio) {
|
|
int temp = static_cast<int>(attributes_.GetMaxHP() * cur_hp_ratio);
|
|
change_hp = max(1, temp) - cur_hp;
|
|
}
|
|
else {
|
|
change_hp = 0;
|
|
}
|
|
|
|
return change_hp;
|
|
#endif //SUN_CODE_BACKUP
|
|
}
|
|
|
|
//==================================================================================================
|
|
// NOTE: f110808.5L, adrenaline configuration
|
|
const Attributes::Operation nsSkill::AdrenalineConfiguration::kSkillAttrTypeArray[MaxCounts] =
|
|
{
|
|
eATTR_BASE_MELEE_MIN_ATTACK_POWER,
|
|
eATTR_BASE_MELEE_MAX_ATTACK_POWER,
|
|
eATTR_BASE_RANGE_MIN_ATTACK_POWER,
|
|
eATTR_BASE_RANGE_MAX_ATTACK_POWER,
|
|
//#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
// eATTR_BASE_MAGICAL_MIN_ATTACK_POWER, eATTR_BASE_MAGICAL_MAX_ATTACK_POWER,
|
|
// eATTR_BASE_MAGICAL_DEFENSE_POWER,
|
|
//#endif
|
|
//
|
|
eATTR_BASE_MELEE_DEFENSE_POWER,
|
|
eATTR_BASE_RANGE_DEFENSE_POWER,
|
|
//
|
|
eATTR_MOVE_SPEED,
|
|
eATTR_ATTACK_SPEED,
|
|
eATTR_PHYSICAL_ATTACK_SUCCESS_RATIO,
|
|
eATTR_MAX_HP
|
|
};
|
|
|
|
//==================================================================================================
|
|
// NOTE: f110808.5L, adrenaline configuration
|
|
bool SkillAttrCalculator::GetCurrentAdrenalineConfiguration(
|
|
nsSkill::AdrenalineConfiguration* const adrenaline_data,
|
|
SLOTCODE skill_code, const Attributes& summoner_attr)
|
|
{
|
|
ZeroMemory(adrenaline_data, sizeof(*adrenaline_data));
|
|
//
|
|
const SkillScriptInfo* base_skill_info = \
|
|
SkillInfoParser::Instance()->GetSkillInfo(skill_code);
|
|
if (base_skill_info == NULL)
|
|
{
|
|
SUNLOG(eCRITICAL_LOG, _T("|["__FUNCTION__"]|Msg=can't find skill(%d)"), skill_code);
|
|
return false;
|
|
};
|
|
const BASE_ABILITYINFO* ability_info = base_skill_info->GetAbilityInfo(eABILITY_ADRENALINE);
|
|
if (ability_info == NULL)
|
|
{
|
|
SUNLOG(eCRITICAL_LOG,
|
|
_T("|["__FUNCTION__"]|Msg=can't find the adrenaline ability in skill(%d)"),
|
|
skill_code);
|
|
return false;
|
|
};
|
|
|
|
const float parameter0 = ability_info->m_iParam[0] / 1000.f;
|
|
const float parameter1 = ability_info->m_iParam[1] / 1000.f;
|
|
const float parameter3 = ability_info->m_iParam[3] / 1000.f;
|
|
|
|
int index = 0;
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type ==
|
|
eATTR_BASE_MELEE_MIN_ATTACK_POWER);
|
|
// 물리 데미지 증감
|
|
*value_slot = static_cast<int>(summoner_attr.GetBaseMeleeMinAttPower() * parameter0);
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type ==
|
|
eATTR_BASE_MELEE_MAX_ATTACK_POWER);
|
|
// 물리 데미지 증감
|
|
*value_slot = static_cast<int>(summoner_attr.GetBaseMeleeMaxAttPower() * parameter0);
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type ==
|
|
eATTR_BASE_RANGE_MIN_ATTACK_POWER);
|
|
// 범위 데미지 증감
|
|
*value_slot = static_cast<int>(summoner_attr.GetBaseRangeMinAttPower() * parameter0);
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type ==
|
|
eATTR_BASE_RANGE_MAX_ATTACK_POWER);
|
|
// 범위 데미지 증감
|
|
*value_slot = static_cast<int>(summoner_attr.GetBaseRangeMaxAttPower() * parameter0);
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type ==
|
|
eATTR_BASE_MELEE_DEFENSE_POWER);
|
|
// 물리 방어력 증감 비율
|
|
*value_slot = static_cast<int>(summoner_attr.GetBaseMeleeDefPower() * parameter1);
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type ==
|
|
eATTR_BASE_RANGE_DEFENSE_POWER);
|
|
// 범위 방어력 증감
|
|
*value_slot = static_cast<int>(summoner_attr.GetBaseRangeDefPower() * parameter1);
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type == eATTR_MOVE_SPEED);
|
|
// 이동속도
|
|
*value_slot = static_cast<int>(summoner_attr.GetBaseMoveSpeedRatio() * parameter3);
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type == eATTR_ATTACK_SPEED);
|
|
// 공격속도
|
|
*value_slot = static_cast<int>(summoner_attr.GetBaseAttSpeedRatio() * parameter3);
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type ==
|
|
eATTR_PHYSICAL_ATTACK_SUCCESS_RATIO);
|
|
// 공격성공 증감
|
|
*value_slot = static_cast<int>(summoner_attr.GetBasePhysicalAttackRate() *
|
|
(ability_info->m_iOption2 / 1000.f));
|
|
++index;
|
|
};
|
|
if (int* value_slot = &adrenaline_data->skill_attr_values[index])
|
|
{
|
|
assert(adrenaline_data->kSkillAttrTypeArray[index].attr_type == eATTR_MAX_HP);
|
|
// 최대 HP 증감
|
|
*value_slot = static_cast<int>(summoner_attr.GetMaxHP() *
|
|
(ability_info->m_iOption1 / 1000.f));
|
|
++index;
|
|
};
|
|
if (FlowControl::FCAssert(index == _countof(adrenaline_data->skill_attr_values)) == false) {
|
|
__debugbreak();
|
|
return false;
|
|
};
|
|
return true;
|
|
};
|
|
//==================================================================================================
|
|
#if SUN_CODE_RENEWAL // CHANGES: f110808.5L, prevent the overlapped attribute calculation
|
|
bool SkillAttrCalculator::ApplyAdrenaline(bool applied, SLOTCODE skill_code,
|
|
const nsSkill::AdrenalineConfiguration& adrenaline_data)
|
|
{
|
|
__UNUSED(skill_code);
|
|
const Attributes::Operation::eOper oper_type = applied ?
|
|
attributes_.Operation::eOper_Add : attributes_.Operation::eOper_Sub;
|
|
|
|
attributes_.GroupOperate(oper_type, eATTR_KIND_SKILL,
|
|
adrenaline_data.kSkillAttrTypeArray, adrenaline_data.skill_attr_values,
|
|
_countof(adrenaline_data.kSkillAttrTypeArray));
|
|
attributes_.Update();
|
|
//
|
|
return true;
|
|
};
|
|
//==================================================================================================
|
|
#else //SUN_CODE_BACKUP
|
|
// NOTE: f110808.5L, custom logic,
|
|
// this method has implicit the overlapped attribute calculation problems
|
|
//==================================================================================================
|
|
bool
|
|
SkillAttrCalculator::ApplyAdrenaline(BOOL bApply, SLOTCODE skill_code,
|
|
const Attributes& summoner_attr)
|
|
{
|
|
const SkillScriptInfo* base_skill_info = \
|
|
SkillInfoParser::Instance()->GetSkillInfo(skill_code);
|
|
if (base_skill_info == NULL)
|
|
{
|
|
SUNLOG(eCRITICAL_LOG,
|
|
"SkillAttrCalculator::ApplyAdrenaline() Not Exist skill_code [Skill:%d]",
|
|
skill_code);
|
|
return false;
|
|
}
|
|
|
|
const BASE_ABILITYINFO* ability_info = base_skill_info->GetAbilityInfo(eABILITY_ADRENALINE);
|
|
// (f100721.1L)
|
|
if (ability_info)
|
|
{
|
|
static const Attributes::Operation attr_skill_array[] = {
|
|
eATTR_BASE_MELEE_MIN_ATTACK_POWER, eATTR_BASE_MELEE_MAX_ATTACK_POWER,
|
|
eATTR_BASE_RANGE_MIN_ATTACK_POWER, eATTR_BASE_RANGE_MAX_ATTACK_POWER,
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
eATTR_BASE_MAGICAL_MIN_ATTACK_POWER, eATTR_BASE_MAGICAL_MAX_ATTACK_POWER,
|
|
eATTR_BASE_MAGICAL_DEFENSE_POWER,
|
|
#endif
|
|
//
|
|
eATTR_BASE_MELEE_DEFENSE_POWER, eATTR_BASE_RANGE_DEFENSE_POWER,
|
|
//
|
|
eATTR_MOVE_SPEED,
|
|
eATTR_ATTACK_SPEED,
|
|
eATTR_PHYSICAL_ATTACK_SUCCESS_RATIO,
|
|
eATTR_MAX_HP
|
|
};
|
|
//
|
|
const float parameter0 = ability_info->m_iParam[0] / 1000.f;
|
|
const float parameter1 = ability_info->m_iParam[1] / 1000.f;
|
|
const float parameter3 = ability_info->m_iParam[3] / 1000.f;
|
|
|
|
const int value_array[_countof(attr_skill_array)] = {
|
|
// 물리 데미지 증감
|
|
static_cast<int>(summoner_attr.GetBaseMeleeMinAttPower() * parameter0),
|
|
static_cast<int>(summoner_attr.GetBaseMeleeMaxAttPower() * parameter0),
|
|
// 범위 데미지 증감
|
|
static_cast<int>(summoner_attr.GetBaseRangeMinAttPower() * parameter0),
|
|
static_cast<int>(summoner_attr.GetBaseRangeMaxAttPower() * parameter0),
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
// 마법 데미지 증감
|
|
static_cast<int>(summoner_attr.GetBaseMagicMinAttPower() * parameter0),
|
|
static_cast<int>(summoner_attr.GetBaseMagicMaxAttPower() * parameter0),
|
|
// 마법 방어력 증감
|
|
static_cast<int>(summoner_attr.GetBaseMagicDefPower() * parameter1),
|
|
#endif
|
|
// 물리 방어력 증감 비율
|
|
static_cast<int>(summoner_attr.GetBaseMeleeDefPower() * parameter1),
|
|
// 범위 방어력 증감
|
|
static_cast<int>(summoner_attr.GetBaseRangeDefPower() * parameter1),
|
|
// 이동속도
|
|
static_cast<int>(summoner_attr.GetBaseMoveSpeedRatio() * parameter3),
|
|
// 공격속도
|
|
static_cast<int>(summoner_attr.GetBaseAttSpeedRatio() * parameter3),
|
|
// 공격성공 증감
|
|
static_cast<int>(summoner_attr.GetBasePhysicalAttackRate() *
|
|
(ability_info->m_iOption2 / 1000.f)),
|
|
// 최대 HP 증감
|
|
static_cast<int>(summoner_attr.GetMaxHP() * (ability_info->m_iOption1 / 1000.f))
|
|
};
|
|
//
|
|
const Attributes::Operation::eOper oper_type \
|
|
= bApply ? attr_skill_array->eOper_Add : attr_skill_array->eOper_Sub;
|
|
|
|
attributes_.GroupOperate(oper_type, eATTR_KIND_SKILL,
|
|
attr_skill_array, value_array,
|
|
_countof(attr_skill_array));
|
|
|
|
attributes_.Update();
|
|
}
|
|
else
|
|
{
|
|
SUNLOG(eCRITICAL_LOG,
|
|
"SkillAttrCalculator::ApplyAdrenaline() Not Exist Ability [Ability:%d]",
|
|
eABILITY_ADRENALINE);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
#endif //SUN_CODE_BACKUP
|
|
//==================================================================================================
|
|
|
|
inline void
|
|
SkillAttrCalculator::_CalcAttrValue(bool is_add, AttrValue& attr_value,
|
|
int value_type, int value)
|
|
{
|
|
eATTR_KIND attr_kind = eATTR_KIND_SKILL;
|
|
float calced_value = static_cast<float>(value);
|
|
|
|
if (value_type == VALUE_TYPE_PERCENT_PER_MAX ||
|
|
value_type == VALUE_TYPE_PERCENT_PER_CUR)
|
|
{
|
|
attr_kind = eATTR_KIND_SKILL_RATIO;
|
|
calced_value = value / 10.0f;
|
|
}
|
|
else if (value_type == VALUE_TYPE_RATIO_VALUE)
|
|
{
|
|
calced_value = value / 10.0f;
|
|
}
|
|
|
|
if (is_add) {
|
|
attr_value[attr_kind] += static_cast<int>(calced_value);
|
|
}
|
|
else {
|
|
attr_value[attr_kind] -= static_cast<int>(calced_value);
|
|
}
|
|
}
|
|
|
|
//#define _WAVERIX_RIDER_TEST_
|
|
#ifdef _WAVERIX_RIDER_TEST_
|
|
static char rider_log_block[2048];
|
|
static int rider_log_written = 0;
|
|
|
|
#define _WAVERIX_TEST_LOG(msg_proc) \
|
|
{ \
|
|
rider_log_written += _snprintf msg_proc ; \
|
|
}
|
|
#define _WAVERIX_TEST_BUFFER_CTRL() \
|
|
&rider_log_block[rider_log_written], _countof(rider_log_block) - rider_log_written
|
|
|
|
#define _WAVERIX_TEST_LOG_CLEAR() \
|
|
{ \
|
|
printf("%s\n", rider_log_block); \
|
|
rider_log_written = 0; \
|
|
}
|
|
|
|
#else
|
|
|
|
#define _WAVERIX_TEST_LOG(msg_proc)
|
|
#define _WAVERIX_TEST_BUFFER_CTRL()
|
|
#define _WAVERIX_TEST_LOG_CLEAR()
|
|
|
|
#endif
|
|
|
|
bool
|
|
SkillAttrCalculator::ApplyRidingRider(bool is_apply, BOOL is_pc_room,
|
|
const SCItemSlot& rider_item,
|
|
const nsSlot::RiderUpdateBits& total_apply_x8bits)
|
|
{
|
|
if (rider_item.IsRiderItem() == false) {
|
|
return false;
|
|
}
|
|
BOOST_STATIC_ASSERT(0 < BASE_ITEMINFO::_MAX_OPTION_NUM &&
|
|
BASE_ITEMINFO::_MAX_OPTION_NUM <= sizeof(DWORD64));
|
|
typedef util::SEQ_BYTE_MASK<eITEM_EFFECT_ON_EQUIP, BASE_ITEMINFO::_MAX_OPTION_NUM> \
|
|
OPTION_EFFECT_EQUIP_MASK;
|
|
|
|
AttrValue& speed_attr = attributes_.GetAttrValueRiderSpeedRatio();
|
|
if (is_apply) {
|
|
//speed_attr.Init();
|
|
const BASE_ITEMINFO* item_info = rider_item.GetItemInfo();
|
|
RidingInfoParser* const pRidingInfoParser = RidingInfoParser::Instance();
|
|
const sRiderInfo* rider_info = pRidingInfoParser->GetInfo(item_info->m_wSkillCode);
|
|
int base_rider_speed_ratio = rider_info ? rider_info->set_speed_ : 100;
|
|
speed_attr[eATTR_KIND_BASE] = base_rider_speed_ratio;
|
|
|
|
_WAVERIX_TEST_LOG((_WAVERIX_TEST_BUFFER_CTRL(),
|
|
"|IsApply=%d|SpeedAttrs|Calc=%d|Base=%d|Item=%d|Skill=%d"
|
|
"|ItemRatio=%d|SkillRatio=%d|TotalRatio=%d|\n",
|
|
is_apply,
|
|
(int)speed_attr[eATTR_KIND_CALC], (int)speed_attr[eATTR_KIND_BASE],
|
|
(int)speed_attr[eATTR_KIND_ITEM], (int)speed_attr[eATTR_KIND_SKILL],
|
|
(int)speed_attr[eATTR_KIND_ITEM_RATIO],
|
|
(int)speed_attr[eATTR_KIND_SKILL_RATIO],
|
|
(int)speed_attr[eATTR_KIND_RATIO]));
|
|
}
|
|
bool need_applied = false;
|
|
|
|
// total_apply_x8bits : 0 - add apply rider option
|
|
// 1 - sub apply rider option
|
|
// 2~4 - add apply parts option
|
|
// 5~7 - sub apply parts option
|
|
|
|
const BASE_ITEMINFO* rider_item_info = rider_item.GetItemInfo();
|
|
|
|
bool rider_option_apply = true;
|
|
if (is_apply) {
|
|
if (total_apply_x8bits.add_rider == 0) {
|
|
rider_option_apply = false;
|
|
}
|
|
}
|
|
else {
|
|
if (total_apply_x8bits.sub_rider == 0) {
|
|
rider_option_apply = false;
|
|
}
|
|
}
|
|
|
|
_WAVERIX_TEST_LOG((_WAVERIX_TEST_BUFFER_CTRL(),
|
|
"|TotalBits=%08X|Apply=%d|AddRider=%d|SubRider=%d|\n",
|
|
total_apply_x8bits.value, is_apply,
|
|
total_apply_x8bits.add_rider, total_apply_x8bits.sub_rider));
|
|
|
|
if (rider_option_apply)
|
|
{
|
|
int upperbound_selector = is_pc_room ? 2 : 1; //Normal|PC option groups
|
|
for (int selector = 0; selector < upperbound_selector; ++selector) {
|
|
const BASE_ITEMINFO::OptionEffects* __option_effects = selector == 0 ?
|
|
&rider_item_info->m_byOptionExEffect : &rider_item_info->m_byPCOptionExEffect;
|
|
|
|
const BASE_ITEMINFO::OptionEffects& option_effects = *__option_effects;
|
|
|
|
bool apply = ARR_CHECK_IS_CONTAIN(OPTION_EFFECT_EQUIP_MASK::MASK, option_effects);
|
|
if (apply == false) {
|
|
continue;
|
|
}
|
|
const BASE_ITEMINFO::OptionIndexes* __option_indexes = selector == 0 ?
|
|
&rider_item_info->m_wOptionIndex : &rider_item_info->m_wPCOptionIndex;
|
|
const BASE_ITEMINFO::OptionTypes* __option_types = selector == 0 ?
|
|
&rider_item_info->m_byOptionType : &rider_item_info->m_byPCOptionType;
|
|
const BASE_ITEMINFO::OptionValues* __option_values = selector == 0 ?
|
|
&rider_item_info->m_iOptionValue : &rider_item_info->m_iPCOptionValue;
|
|
|
|
const BASE_ITEMINFO::OptionIndexes& option_indexes = *__option_indexes;
|
|
const BASE_ITEMINFO::OptionTypes& option_types = *__option_types;
|
|
const BASE_ITEMINFO::OptionValues& option_values = *__option_values;
|
|
|
|
for (int op = 0; op < _countof(option_indexes); ++op)
|
|
{
|
|
bool is_valid_values = option_indexes[op] != 0 &&
|
|
option_values[op] != 0 &&
|
|
option_indexes[op] < _countof(g_ITEM_OPTION);
|
|
if (is_valid_values == false) {
|
|
continue;
|
|
}
|
|
const eATTR_TYPE attr_type = g_ITEM_OPTION[option_indexes[op]];
|
|
_WAVERIX_TEST_LOG((_WAVERIX_TEST_BUFFER_CTRL(),
|
|
"| |%sRider|ArrayIndex=%d"
|
|
"|OptionIndex=%d|OptionType=%d|OptionValue=%d|PC=%d|\n",
|
|
is_apply ? "Add" : "Sub",
|
|
op, option_indexes[op], attr_type, option_values[op],
|
|
selector));
|
|
|
|
if (attr_type == eATTR_MOVE_SPEED) {
|
|
_CalcAttrValue(is_apply, speed_attr, option_types[op], option_values[op]);
|
|
continue;
|
|
}
|
|
|
|
if (_CanApplyRiderOption(attr_type) == false) {
|
|
continue;
|
|
}
|
|
if (is_apply) {
|
|
AddAttr(attr_type, option_types[op], option_values[op], true /*skip update*/);
|
|
}
|
|
else {
|
|
DelAttr(attr_type, option_types[op], option_values[op], true /*skip update*/);
|
|
}
|
|
need_applied = true;
|
|
} //end 'for'
|
|
} //end 'for' selector
|
|
}
|
|
|
|
// total_apply_x8bits : 0 - add apply rider option
|
|
// 1 - sub apply rider option
|
|
// 2~4 - add apply parts option
|
|
// 5~7 - sub apply parts option
|
|
|
|
BYTE parts_apply = total_apply_x8bits.value >> 2; //skip 0,1
|
|
// ---- current state : can't used total_apply_x8bits.(add|sub)_xxx
|
|
if (is_apply == false) {
|
|
parts_apply >>= 3;
|
|
}
|
|
eRIDER_PARTS_POS pos = { 0, };
|
|
BOOST_STATIC_ASSERT(pos.Counts == 3);
|
|
SCRiderPartsData parts_data_block[pos.Counts] = { 0, }; // (CHANGES) support set item (full option)
|
|
int set_apply_counts[pos.Counts] = { 0, };
|
|
|
|
for (int parts_inc = 0; parts_inc < pos.Counts; ++parts_inc) {
|
|
SCRiderPartsData& parts_data = parts_data_block[parts_inc];
|
|
bool apply_step = !!(parts_apply & 1);
|
|
parts_apply >>= 1;
|
|
|
|
pos.value = static_cast<BYTE>(parts_inc) + pos.StartIndex;
|
|
if (rider_item.GetRiderPartsInfo(pos, &parts_data) == 0) {
|
|
continue;
|
|
}
|
|
// empty도 성공으로 반환한다. 또는 하기와 같은 상황에서는 item 정보가 없는 경우도 존재
|
|
if (parts_data.item_info_ == NULL) {
|
|
continue;
|
|
}
|
|
// timeout된 경우 감안해서 no-count처리를 수행한다.
|
|
// add의 경우는 호출자 쪽에서 timeout이 감안된다.
|
|
if (parts_data.item_info_->set_group_code != 0) {
|
|
++set_apply_counts[parts_inc];
|
|
}
|
|
// apply option
|
|
if (apply_step == false) {
|
|
continue;
|
|
}
|
|
// Client 처리상의 문제:
|
|
// 클라이언트도 SCItemSlot::Update가 수행되며, 클라 로컬 시간을 기준으로 is_expired와 같은
|
|
// 상태 변경이 발생할 수 있다.
|
|
// 입력 인자인 parts_apply를 이용해서
|
|
|
|
// 이 차이점을 이용해서 클라이언트는 이전 상태를 제거할 경우 정확히 제거가 가능할 것이다.
|
|
// 이후 서버로부터 받은 정보로 Add처리를 하면, 갱신된 정보로 적용이 가능하다.
|
|
|
|
const BASE_ITEMINFO* parts_item_info = parts_data.item_info_;
|
|
|
|
int upperbound_selector = is_pc_room ? 2 : 1; //Normal|PC option groups
|
|
for (int selector = 0; selector < upperbound_selector; ++selector) {
|
|
|
|
const BASE_ITEMINFO::OptionEffects* __option_effects = selector == 0 ?
|
|
&parts_item_info->m_byOptionExEffect : &parts_item_info->m_byPCOptionExEffect;
|
|
|
|
const BASE_ITEMINFO::OptionEffects& option_effects = *__option_effects;
|
|
|
|
bool apply = ARR_CHECK_IS_CONTAIN(OPTION_EFFECT_EQUIP_MASK::MASK, option_effects);
|
|
if (apply == false) {
|
|
continue;
|
|
}
|
|
const BASE_ITEMINFO::OptionIndexes* __option_indexes = selector == 0 ?
|
|
&parts_item_info->m_wOptionIndex : &parts_item_info->m_wPCOptionIndex;
|
|
const BASE_ITEMINFO::OptionTypes* __option_types = selector == 0 ?
|
|
&parts_item_info->m_byOptionType : &parts_item_info->m_byPCOptionType;
|
|
const BASE_ITEMINFO::OptionValues* __option_values = selector == 0 ?
|
|
&parts_item_info->m_iOptionValue : &parts_item_info->m_iPCOptionValue;
|
|
|
|
const BASE_ITEMINFO::OptionIndexes& option_indexes = *__option_indexes;
|
|
const BASE_ITEMINFO::OptionTypes& option_types = *__option_types;
|
|
const BASE_ITEMINFO::OptionValues& option_values = *__option_values;
|
|
|
|
for (int op = 0; op < _countof(option_indexes); ++op)
|
|
{
|
|
bool is_valid_values = option_indexes[op] != 0 &&
|
|
option_values[op] != 0 &&
|
|
option_indexes[op] < _countof(g_ITEM_OPTION);
|
|
if (is_valid_values == false) {
|
|
continue;
|
|
}
|
|
const eATTR_TYPE attr_type = g_ITEM_OPTION[option_indexes[op]];
|
|
_WAVERIX_TEST_LOG((_WAVERIX_TEST_BUFFER_CTRL(),
|
|
"| |%sParts%d|ArrayIndex=%d"
|
|
"|OptionIndex=%d|OptionType=%d|OptionValue=%d|PC=%d|\n",
|
|
is_apply ? "Add" : "Sub", parts_inc,
|
|
op, option_indexes[op], attr_type, option_values[op], selector));
|
|
|
|
if (attr_type == eATTR_MOVE_SPEED) {
|
|
_CalcAttrValue(is_apply, speed_attr, option_types[op], option_values[op]);
|
|
continue;
|
|
}
|
|
|
|
if (_CanApplyRiderOption(attr_type) == false) {
|
|
continue;
|
|
}
|
|
if (is_apply) {
|
|
AddAttr(attr_type, option_types[op], option_values[op], true /*skip update*/);
|
|
}
|
|
else {
|
|
DelAttr(attr_type, option_types[op], option_values[op], true /*skip update*/);
|
|
}
|
|
need_applied = true;
|
|
} //end 'for'
|
|
} //end 'for' selector
|
|
} //end 'for' number of parts
|
|
|
|
//--------------------------------------------------------------------------
|
|
// (NOTE) SetItem효과는 FullOption만 지원한다. 기존 ItemAttrCalculator에서
|
|
// 개별 옵션 적용이 되는 이유는 모든 스탯 초기화 후에 재계산을 수행하기 때문
|
|
parts_apply = total_apply_x8bits.value >> 2; //skip 0,1
|
|
// ---- current state : can't used total_apply_x8bits.(add|sub)_xxx
|
|
if (is_apply == 0) {
|
|
parts_apply >>= 3;
|
|
}
|
|
parts_apply &= 0x7;
|
|
|
|
if (parts_apply)
|
|
{
|
|
SetItemOptionInfoParser* set_item_parser = SetItemOptionInfoParser::Instance();
|
|
const SETITEM_OPTION_INFO* setitem_option = 0;
|
|
for (SCRiderPartsData* it_1st = parts_data_block;
|
|
it_1st != &parts_data_block[_countof(parts_data_block)];
|
|
++it_1st)
|
|
{
|
|
if (it_1st->item_info_ == 0) {
|
|
continue;
|
|
}
|
|
WORD set_type_1st = it_1st->item_info_->set_group_code;
|
|
if (set_type_1st == 0) {
|
|
continue;
|
|
}
|
|
if (it_1st->item_info_->m_bySetOptionType != it_1st->item_info_->eSetItem_Active) {
|
|
continue;
|
|
}
|
|
setitem_option = set_item_parser->Find(set_type_1st);
|
|
if (setitem_option == 0) {
|
|
continue;
|
|
}
|
|
// n^2 searching
|
|
int same_set_types = 0;
|
|
for (SCRiderPartsData* it_2nd = parts_data_block;
|
|
it_2nd != &parts_data_block[_countof(parts_data_block)];
|
|
++it_2nd)
|
|
{
|
|
if (it_2nd->item_info_ == 0) {
|
|
continue;
|
|
}
|
|
//
|
|
WORD set_type_2nd = it_2nd->item_info_->set_group_code;
|
|
if (set_type_2nd == 0) {
|
|
continue;
|
|
}
|
|
if (it_2nd->item_info_->m_bySetOptionType != it_2nd->item_info_->eSetItem_Active) {
|
|
continue;
|
|
}
|
|
if (set_type_1st == set_type_2nd) {
|
|
++same_set_types;
|
|
}
|
|
}
|
|
// full
|
|
if (setitem_option->m_byActiveFullOptionNum == same_set_types) {
|
|
break;
|
|
}
|
|
setitem_option = 0;
|
|
}
|
|
|
|
// (NOTE) add상태는 변경 후의 계산, sub상태는 변경 전의 계산이다.
|
|
// 따라서 sub일 경우라면 full상태일 경우가 사라지는 상황이므로 full검사로 옵션 제거가 가능.
|
|
|
|
if (setitem_option)
|
|
{
|
|
for (int full_inc = 0; full_inc < _countof(setitem_option->m_FullOption); ++full_inc)
|
|
{
|
|
const SET_ITEM_OPTION& full_option = setitem_option->m_FullOption[full_inc];
|
|
|
|
bool is_valid_values = full_option.m_byOptionKind != 0 &&
|
|
full_option.m_nOption != 0 &&
|
|
full_option.m_byOptionKind < _countof(g_ITEM_OPTION);
|
|
if (is_valid_values == false) {
|
|
continue;
|
|
}
|
|
const eATTR_TYPE attr_type = g_ITEM_OPTION[full_option.m_byOptionKind];
|
|
_WAVERIX_TEST_LOG((_WAVERIX_TEST_BUFFER_CTRL(),
|
|
"| |FullOption_%s|Index=%d"
|
|
"|OptionIndex=%d|OptionType=%d|OptionValue=%d|\n",
|
|
is_apply ? "Add" : "Sub", full_inc,
|
|
full_option.m_byOptionKind, attr_type, full_option.m_nOption));
|
|
|
|
if (attr_type == eATTR_MOVE_SPEED) {
|
|
_CalcAttrValue(is_apply, speed_attr, full_option.m_byUseType, full_option.m_nOption);
|
|
continue;
|
|
}
|
|
|
|
if (_CanApplyRiderOption(attr_type) == false) {
|
|
continue;
|
|
}
|
|
if (is_apply) { /*skip update*/
|
|
AddAttr(attr_type, full_option.m_byUseType, full_option.m_nOption, true);
|
|
}
|
|
else { /*skip update*/
|
|
DelAttr(attr_type, full_option.m_byUseType, full_option.m_nOption, true);
|
|
}
|
|
need_applied = true;
|
|
}
|
|
} //end apply set
|
|
}
|
|
//--------------------------------------------------------------------------
|
|
|
|
#ifdef _SERVER
|
|
if (need_applied) {
|
|
attributes_.UpdateEx(); // (NOTE) ...좀 더 생각해 보자.
|
|
}
|
|
else {
|
|
speed_attr.Update();
|
|
}
|
|
#else
|
|
if (need_applied) {
|
|
attributes_.Update();
|
|
}
|
|
else {
|
|
speed_attr.Update();
|
|
}
|
|
#endif
|
|
|
|
_WAVERIX_TEST_LOG((_WAVERIX_TEST_BUFFER_CTRL(),
|
|
"|IsApply=%d|SpeedAttrs|Calc=%d|Base=%d|Item=%d|Skill=%d"
|
|
"|ItemRatio=%d|SkillRatio=%d|TotalRatio=%d|\n",
|
|
is_apply,
|
|
(int)speed_attr[eATTR_KIND_CALC], (int)speed_attr[eATTR_KIND_BASE],
|
|
(int)speed_attr[eATTR_KIND_ITEM], (int)speed_attr[eATTR_KIND_SKILL],
|
|
(int)speed_attr[eATTR_KIND_ITEM_RATIO],
|
|
(int)speed_attr[eATTR_KIND_SKILL_RATIO],
|
|
(int)speed_attr[eATTR_KIND_RATIO]));
|
|
_WAVERIX_TEST_LOG_CLEAR();
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef _NA_008220_20150414_BUG_ETHERBULLET_ATTR
|
|
float SkillAttrCalculator::ClearAttr( eATTR_TYPE attr_type, int value_type, bool skip_update /*= false*/ )
|
|
{
|
|
if (attr_type <= eATTR_TYPE_INVALID || eATTR_MAX <= attr_type)
|
|
{
|
|
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]|attr_type|%d|invalid type value", attr_type);
|
|
return 0;
|
|
}
|
|
|
|
if (!IsRecoverAttr(attr_type)) {
|
|
return 0;
|
|
}
|
|
|
|
eATTR_KIND attr_kind = eATTR_KIND_SKILL;
|
|
if (value_type == VALUE_TYPE_PERCENT_PER_MAX || value_type == VALUE_TYPE_PERCENT_PER_CUR)
|
|
{
|
|
attr_kind = eATTR_KIND_SKILL_RATIO;
|
|
}
|
|
|
|
// (REMARK) (WAVERIX) 이 위치가 AddAttr과 코드가 다르다. 정확한 이유를 파악할 필요가 있다.
|
|
static const Attributes::Operation attr_array_for_all_stats[] = {
|
|
eATTR_STR, eATTR_DEX, eATTR_VIT, eATTR_INT, eATTR_SPR
|
|
};
|
|
|
|
switch (attr_type)
|
|
{
|
|
case eATTR_INCREASE_STAT_POINT:
|
|
{
|
|
// (f100721.1L)
|
|
int value_array[_countof(attr_array_for_all_stats)] = {0, 0, 0, 0, 0};
|
|
//
|
|
attributes_.GroupOperate(attr_array_for_all_stats->eOper_Sub, attr_kind,
|
|
attr_array_for_all_stats, value_array,
|
|
_countof(attr_array_for_all_stats));
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
attributes_[attr_type][attr_kind] = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// (CHANGES) (WAVERIX)
|
|
if (skip_update == false) {
|
|
attributes_.UpdateEx();
|
|
}
|
|
return 0;
|
|
}
|
|
#endif //_NA_008220_20150414_BUG_ETHERBULLET_ATTR
|