Files
Sun1602/ProgramCommon/SkillAttrCalculator.cpp
T
git 9cb257e7ae Add GameClients drops and keep current 1602 source work.
CN1602 is the local client. KR is the official tree with unpacked Data, System, and Sound. Packed .wpk files stay off git.
2026-08-30 23:24:31 +03:00

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