952 lines
77 KiB
C++
952 lines
77 KiB
C++
#include "stdafx.h"
|
|
#include "PlayerAttributes.h"
|
|
|
|
//==================================================================================================
|
|
#include "Struct.h"
|
|
#include "SCPlayer.h"
|
|
#include "CommonCharacterFormula.h"
|
|
#include "KBMoveCheckManager.h"
|
|
|
|
//==================================================================================================
|
|
|
|
PlayerAttributes::PlayerAttributes()
|
|
: owner_(NULL)
|
|
{
|
|
// _NA_000251_20100727_SOCKET_SYSTEM
|
|
socket_option_attributes_.Clear();
|
|
}
|
|
|
|
PlayerAttributes::~PlayerAttributes()
|
|
{
|
|
}
|
|
|
|
void PlayerAttributes::SetOwner(IPlayerAttributeLinker* owner)
|
|
{
|
|
owner_ = owner;
|
|
}
|
|
|
|
//==================================================================================================
|
|
// CHANGES: f110217.2L, add clone interface to support stat configuration preview
|
|
void PlayerAttributes::Clone(PlayerAttributes* dest)
|
|
{
|
|
CopyMemory(dest->attr_values_, attr_values_,
|
|
static_cast<size_t>(last_field_end_ - reinterpret_cast<int8_t*>(attr_values_)));
|
|
dest->RegisterAll(true);
|
|
//_NA_000251_20100727_SOCKET_SYSTEM
|
|
socket_option_attributes_.Clone(&dest->socket_option_attributes_);
|
|
|
|
//_NA_002935_20110704_ITEM_SCORE
|
|
set_option_attributes_.Clone(&dest->set_option_attributes_);
|
|
dest->owner_ = this->owner_;
|
|
CheckMemoryTest();
|
|
}
|
|
|
|
// CHANGES: f110802.2L
|
|
void PlayerAttributes::CheckMemoryTest()
|
|
{
|
|
#if defined(_NA000000_SPEED_HACK_PREVENTION_SUPPORT) && !defined(_SERVER)
|
|
uint8_t test_block[sizeof(*this)];
|
|
PlayerAttributes* const dummy = reinterpret_cast<PlayerAttributes*>(test_block);
|
|
//
|
|
CopyMemory(&dummy->m_STR, &this->m_STR,
|
|
static_cast<size_t>(dummy->last_field_end_ - reinterpret_cast<int8_t*>(&dummy->m_STR)));
|
|
|
|
if (AttrValue* const attr_value = &dummy->m_DEX)
|
|
{
|
|
attr_value->bound_type_ = eBOUNDTYPE_ZP_1000;
|
|
attr_value->update_type_ = eUPDATETYPE_SUM_RATIO;
|
|
attr_value->Update();
|
|
};
|
|
if (AttrValue* const attr_value = &dummy->m_MoveSpeedRatio)
|
|
{
|
|
attr_value->bound_type_ = eBOUNDTYPE_ZP_1000;
|
|
attr_value->update_type_ = eUPDATETYPE_NOTHING;
|
|
attr_value->Update();
|
|
int calc_value = ns_formula::internal::CalcMoveSpeedRatio(
|
|
dummy->m_DEX.calc_value_.value_,
|
|
attr_value->item_value_.value_,
|
|
attr_value->skill_value_.value_);
|
|
|
|
calc_value *= (100 + static_cast<const AttrValue&>(*attr_value)[eATTR_KIND_RATIO]);
|
|
calc_value /= 100;
|
|
attr_value->calc_value_.value_ = calc_value;
|
|
attr_value->Update();
|
|
};
|
|
if (AttrValue* const attr_value = &dummy->m_AttSpeedRatio)
|
|
{
|
|
attr_value->bound_type_ = eBOUNDTYPE_ZP_1000;
|
|
attr_value->update_type_ = eUPDATETYPE_NOTHING;
|
|
attr_value->Update();
|
|
int calc_value = ns_formula::internal::CalcAttackSpeedRatio(
|
|
owner_->GetCharType(),
|
|
owner_->GetWeaponKind(),
|
|
dummy->m_DEX.calc_value_.value_,
|
|
attr_value->item_value_.value_,
|
|
attr_value->skill_value_.value_);
|
|
|
|
calc_value *= (100 + static_cast<const AttrValue&>(*attr_value)[eATTR_KIND_RATIO]);
|
|
calc_value /= 100;
|
|
attr_value->calc_value_.value_ = calc_value;
|
|
attr_value->Update();
|
|
};
|
|
|
|
if (::memcmp(&dummy->m_STR, &m_STR,
|
|
static_cast<size_t>(dummy->last_field_end_ -
|
|
reinterpret_cast<int8_t*>(&dummy->m_STR))) != 0)
|
|
{
|
|
nsSync::MemoryHackMonitor::Instance()->ForceIncreaseClientTestSerial();
|
|
};
|
|
// forcely cleared.
|
|
RtlSecureZeroMemory(test_block, sizeof(test_block));
|
|
#endif
|
|
}
|
|
|
|
//==================================================================================================
|
|
|
|
void PlayerAttributes::SetCharInfo(const BASE_PLAYERINFO* player_info,
|
|
int recover_hp, int recover_mp, int recover_sd)
|
|
{
|
|
assert(player_info != NULL);
|
|
|
|
// 기본 스탯들 [eATTR_KIND_BASE]
|
|
m_STR.base_value_.value_ = player_info->m_sStrength;
|
|
m_DEX.base_value_.value_ = player_info->m_sDexterity;
|
|
m_VIT.base_value_.value_ = player_info->m_sVitality;
|
|
m_INT.base_value_.value_ = player_info->m_sInteligence;
|
|
m_SPR.base_value_.value_ = player_info->m_sSpirit;
|
|
;{ // __NA_S00015_20080828_NEW_HOPE_SHADOW
|
|
m_STR.Update();
|
|
m_DEX.Update();
|
|
m_VIT.Update();
|
|
m_INT.Update();
|
|
m_SPR.Update();
|
|
};
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
// 숙련도
|
|
m_Experty1.base_value_.value_ = player_info->m_sSkillStat1;
|
|
m_Experty2.base_value_.value_ = player_info->m_sSkillStat2;
|
|
#endif
|
|
// 시야
|
|
m_SightRange.base_value_.value_ = 130;
|
|
|
|
// HP, MP 회복률
|
|
m_RecoverHP.base_value_.value_ = recover_hp;
|
|
m_RecoverMP.base_value_.value_ = recover_mp;
|
|
m_RecoverSD.base_value_.value_ = recover_sd;
|
|
|
|
// 최대 HP, MP
|
|
const eCHAR_TYPE char_type = owner_->GetCharType();
|
|
const LEVELTYPE char_level = owner_->GetLevel();
|
|
m_MaxHP.base_value_.value_ = CalcHP(char_type, char_level, GetVIT());
|
|
m_MaxMP.base_value_.value_ = CalcMP(char_type, char_level, GetSPR());
|
|
m_MaxSD.base_value_.value_ = CalcSD(char_type, char_level);
|
|
|
|
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
|
|
// 최대 FP, FP회복율
|
|
m_RecoverFP.base_value_.value_ = 0;
|
|
m_MaxFP.base_value_.value_ = CalcFP(char_type);
|
|
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
|
|
|
|
m_RiderSpeedRatio.Init();
|
|
}
|
|
|
|
//==================================================================================================
|
|
|
|
#if defined(_SERVER)
|
|
|
|
// NOTE, f100828.1L, change a name of 'GetCharInfo' to 'SynchronizeBaseStatus'.
|
|
// this method use to synchronizing
|
|
// between a base status value of BASE_PLAYERINFO and it of Attributes.
|
|
void PlayerAttributes::SynchronizeBaseStatus() //GetCharInfo(BASE_PLAYERINFO* OUT pInfo);
|
|
{
|
|
BASE_PLAYERINFO* player_info = owner_->GetCharacterInfo();
|
|
assert(player_info != NULL);
|
|
player_info->m_sStrength = static_cast<short>(m_STR.base_value_.value_);
|
|
player_info->m_sDexterity = static_cast<short>(m_DEX.base_value_.value_);
|
|
player_info->m_sVitality = static_cast<short>(m_VIT.base_value_.value_);
|
|
player_info->m_sInteligence = static_cast<short>(m_INT.base_value_.value_);
|
|
player_info->m_sSpirit = static_cast<short>(m_SPR.base_value_.value_);
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
player_info->m_sSkillStat1 = 0;
|
|
player_info->m_sSkillStat2 = 0;
|
|
#else
|
|
player_info->m_sSkillStat1 = static_cast<short>(m_Experty1.base_value_);
|
|
player_info->m_sSkillStat2 = static_cast<short>(m_Experty2.base_value_);
|
|
#endif
|
|
player_info->m_iMaxHP = m_MaxHP.base_value_.value_;
|
|
player_info->m_iMaxMP = m_MaxMP.base_value_.value_;
|
|
player_info->m_iMaxSD = m_MaxSD.base_value_.value_;
|
|
}
|
|
#else //if !defined(_SERVER)
|
|
// NOTE, f100829.1L, rollback to support a client logic
|
|
void PlayerAttributes::GetCharInfo(BASE_PLAYERINFO* OUT player_info)
|
|
{
|
|
player_info->m_sStrength = static_cast<short>(m_STR.base_value_.value_);
|
|
player_info->m_sDexterity = static_cast<short>(m_DEX.base_value_.value_);
|
|
player_info->m_sVitality = static_cast<short>(m_VIT.base_value_.value_);
|
|
player_info->m_sInteligence = static_cast<short>(m_INT.base_value_.value_);
|
|
player_info->m_sSpirit = static_cast<short>(m_SPR.base_value_.value_);
|
|
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
player_info->m_sSkillStat1 = 0;
|
|
player_info->m_sSkillStat2 = 0;
|
|
#else
|
|
player_info->m_sSkillStat1 = static_cast<short>(m_Experty1.base_value_);
|
|
player_info->m_sSkillStat2 = static_cast<short>(m_Experty2.base_value_);
|
|
#endif
|
|
|
|
player_info->m_iMaxHP = m_MaxHP.base_value_.value_;
|
|
player_info->m_iMaxMP = m_MaxMP.base_value_.value_;
|
|
player_info->m_iMaxSD = m_MaxSD.base_value_.value_;
|
|
}
|
|
#endif
|
|
|
|
//==================================================================================================
|
|
|
|
void PlayerAttributes::Update()
|
|
{
|
|
//----------------------------------------------------------------------------------------------
|
|
// early instance for busy access fields
|
|
const eCHAR_TYPE char_type = owner_->GetCharType();
|
|
const LEVELTYPE char_level = owner_->GetLevel();
|
|
const DWORD weapon_kind = owner_->GetWeaponKind();
|
|
//----------------------------------------------------------------------------------------------
|
|
// 기본 스탯들
|
|
;{
|
|
m_STR.Update();
|
|
m_DEX.Update();
|
|
m_VIT.Update();
|
|
m_INT.Update();
|
|
m_SPR.Update();
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
m_Experty1.Update();
|
|
m_Experty2.Update();
|
|
#endif
|
|
// 스텟을 이용하여 HP, MP 계산
|
|
m_MaxHP.base_value_.value_ = CalcHP(char_type, char_level, GetVIT());
|
|
m_MaxMP.base_value_.value_ = CalcMP(char_type, char_level, GetSPR());
|
|
m_MaxSD.base_value_.value_ = CalcSD(char_type, char_level);
|
|
m_MaxHP.Update();
|
|
m_MaxMP.Update();
|
|
m_MaxSD.Update();
|
|
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
|
|
m_MaxFP.Update();
|
|
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
|
|
|
|
// NOTE, update attribute values to player info. a real process of this call is server only.
|
|
SynchronizeBaseStatus();
|
|
};
|
|
//----------------------------------------------------------------------------------------------
|
|
const short stat_dex = static_cast<short>(GetDEX());
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
// HP, MP, SD 회복률
|
|
UpdateRecoveries(char_type);
|
|
//----------------------------------------------------------------------------------------------
|
|
// Attack power & Defence power
|
|
;{
|
|
UpdateAttackPower();
|
|
UpdateDefense();
|
|
};
|
|
//----------------------------------------------------------------------------------------------
|
|
;{
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
// 공격 타입별 추가 방어력
|
|
for (int i = 0; i < eATTACK_TYPE_MAX; ++i)
|
|
{
|
|
m_BonusDefense[i].Update();
|
|
}
|
|
// 아머 타입별 추가 데미지(퍼센트)
|
|
for (int i = eARMOR_HARD; i < eARMOR_TYPE_MAX; ++i)
|
|
{
|
|
m_BonusDamage[i].Update();
|
|
m_BonusPercentDamage[i].Update();
|
|
}
|
|
// 데미지 감소
|
|
for (int i = 0; i < eATTACK_TYPE_MAX; ++i)
|
|
{
|
|
m_ReduceDamage[i].Update();
|
|
}
|
|
//
|
|
for (int i = 0; i < eATTACK_TYPE_MAX; ++i)
|
|
{
|
|
m_ReduceDefenseRate[i].Update();
|
|
}
|
|
#endif
|
|
};
|
|
//----------------------------------------------------------------------------------------------
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
if (AttrValue* attr = &m_PhysicalAttackRate)
|
|
{ // 공격 성공률 (단위:percentage * 100)
|
|
attr->base_value_.value_ = CalcPhysicalAttackRateBase(char_type, char_level, stat_dex);
|
|
double calc = (attr->item_value_.value_ + attr->skill_value_.value_) *
|
|
ns_formula::AttackRateConstants_EquipmentWeight;
|
|
int ratio = attr->item_ratio_.value_ + attr->skill_ratio_.value_;
|
|
if (ratio != 0 && calc != 0.0f) {
|
|
calc *= (100.0f + ratio);
|
|
calc /= 100.0f;
|
|
}
|
|
|
|
attr->calc_value_.value_ = util::LL_Round(attr->base_value_.value_ + calc);
|
|
attr->Update();
|
|
}
|
|
if (AttrValue* attr = &m_PhysicalAvoidRate)
|
|
{ // 공격 방어율 (단위:percentage * 100)
|
|
#ifdef _NA_008486_20150914_TOTAL_BALANCE
|
|
attr->base_value_.value_ = CalcPhysicalAvoidRateBase(char_type, char_level, (short)GetVIT());
|
|
#else
|
|
attr->base_value_.value_ = CalcPhysicalAvoidRateBase(char_type, char_level, stat_dex);
|
|
#endif //_NA_008486_20150914_TOTAL_BALANCE
|
|
// CHANGES: f110209.6L, changes formula by planner request
|
|
int option_sum_value = attr->item_value_.value_ + attr->skill_value_.value_;
|
|
double calc = m_ArmorPhysicalAvoidRate.item_value_.value_ *
|
|
ns_formula::AttackAvoidRateConstants_EquipmentWeight;
|
|
; calc += (attr->base_value_.value_ + option_sum_value);
|
|
int ratio = attr->item_ratio_.value_ + attr->skill_ratio_.value_;
|
|
if (ratio != 0 && calc != 0.0f) {
|
|
calc *= (100.0f + ratio);
|
|
calc /= 100.0f;
|
|
}
|
|
attr->calc_value_.value_ = util::LL_Round(calc);
|
|
attr->Update();
|
|
}
|
|
#else
|
|
;{
|
|
// 공격 성공률, 회피율
|
|
m_PhysicalAttackRate.base_value_.value_ = \
|
|
CalcPhysicalAttackRateBase(char_type, char_level, stat_dex);
|
|
m_PhysicalAvoidRate.base_value_.value_ = \
|
|
CalcPhysicalAvoidRateBase(char_type, char_level, stat_dex);
|
|
m_PhysicalAttackRate.Update();
|
|
m_PhysicalAvoidRate.Update();
|
|
};
|
|
#endif
|
|
//----------------------------------------------------------------------------------------------
|
|
// range attributes
|
|
;{
|
|
|
|
m_MoveSpeedRatio.Update();
|
|
#ifdef _NA_008486_20150914_TOTAL_BALANCE
|
|
int calc_value = CalcMoveSpeedRatio(m_MoveSpeedRatio.item_value_.value_,
|
|
m_MoveSpeedRatio.skill_value_.value_, char_level);
|
|
#else
|
|
int calc_value = CalcMoveSpeedRatio(stat_dex, \
|
|
m_MoveSpeedRatio.item_value_.value_, m_MoveSpeedRatio.skill_value_.value_);
|
|
#endif //_NA_008486_20150914_TOTAL_BALANCE
|
|
|
|
calc_value *= (100 + static_cast<const AttrValue&>(m_MoveSpeedRatio)[eATTR_KIND_RATIO]);
|
|
calc_value /= 100;
|
|
m_MoveSpeedRatio.calc_value_.value_ = calc_value;
|
|
m_MoveSpeedRatio.calc_value_.touch(static_cast<eBOUNDTYPE>(m_MoveSpeedRatio.bound_type_));
|
|
};
|
|
;{
|
|
m_AttSpeedRatio.Update();
|
|
int calc_value = CalcAttackSpeedRatio(char_type, weapon_kind, stat_dex, \
|
|
m_AttSpeedRatio.item_value_, m_AttSpeedRatio.skill_value_);
|
|
calc_value *= (100 + static_cast<const AttrValue&>(m_AttSpeedRatio)[eATTR_KIND_RATIO]);
|
|
calc_value /= 100;
|
|
m_AttSpeedRatio.calc_value_.value_ = calc_value;
|
|
m_AttSpeedRatio.calc_value_.touch(static_cast<eBOUNDTYPE>(m_AttSpeedRatio.bound_type_));
|
|
};
|
|
;{
|
|
// 사거리 보너스
|
|
for (int i = eATTR_KIND_ITEM; i <= eATTR_KIND_RATIO; ++i)
|
|
{
|
|
eATTR_KIND attr_kind = static_cast<eATTR_KIND>(i);
|
|
const int apply_value = static_cast<const AttrValue&>(m_AllRangeBonus)[attr_kind];
|
|
if (apply_value)
|
|
{
|
|
m_NormalRangeBonus[attr_kind] += apply_value;
|
|
m_SkillRangeBonus[attr_kind] += apply_value;
|
|
}
|
|
}
|
|
m_NormalRangeBonus.Update();
|
|
m_SkillRangeBonus.Update();
|
|
m_AllRangeBonus.Init(); // <-
|
|
|
|
// 시야
|
|
m_SightRange.Update();
|
|
m_RiderSpeedRatio.Update();
|
|
};
|
|
;{
|
|
// 크리티컬 데미지
|
|
m_CriticalRatioChange.base_value_.value_ = \
|
|
CalcPhyCriticalBaseRatio(char_type, char_level, stat_dex);
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
m_MagicCriticalRatio.base_value_.value_ = \
|
|
CalcMagicCriticalBaseRatio(char_type, char_level, GetSPR());
|
|
#endif
|
|
|
|
for (int i = eATTR_KIND_ITEM; i <= eATTR_KIND_RATIO; ++i)
|
|
{
|
|
eATTR_KIND attr_kind = static_cast<eATTR_KIND>(i);
|
|
const int apply_value = \
|
|
static_cast<const AttrValue&>(m_CriticalRatioBonus)[attr_kind];
|
|
if (apply_value)
|
|
{
|
|
m_CriticalRatioChange[attr_kind] += apply_value;
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
m_MagicCriticalRatio[attr_kind] += apply_value;
|
|
#endif
|
|
}
|
|
}
|
|
m_CriticalRatioChange.Update();
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
m_MagicCriticalRatio.Update();
|
|
#endif
|
|
m_CriticalRatioBonus.Init();
|
|
//
|
|
m_CriticalDamageBonus.Update();
|
|
};
|
|
;{
|
|
// 스킬 공격력, 스킬 퍼센트 추가 데미지
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
m_SkillAttackPower.base_value_.value_ = CalcSkillAttackPower(char_type, GetINT());
|
|
#endif
|
|
m_SkillAttackPower.Update();
|
|
m_SkillPercentDamage.Update();
|
|
};
|
|
;{
|
|
m_AbsorbHP.Update();
|
|
m_AbsorbMP.Update();
|
|
};
|
|
;{
|
|
m_BonusCastingTime.Update();
|
|
m_BonusSkillCoolTime.Update();
|
|
m_IncreaseSkillDuration.Update();
|
|
m_DecreaseSkillDuration.Update();
|
|
};
|
|
;{
|
|
m_AttackIncRate.Update();
|
|
m_DefenseIncRate.Update();
|
|
m_AreaAttackRatio.Update();
|
|
m_ReflectDamageRatio.Update();
|
|
m_DecDamage.Update();
|
|
m_DoubleDamageRatio.Update(); // 데미지의 두배가 적용될 확률
|
|
//m_DoubleDamageRatio.Update();
|
|
m_IncreaseMinDamage.Update();
|
|
m_IncreaseMaxDamage.Update();
|
|
m_AddDamage.Update();
|
|
};
|
|
;{
|
|
m_EhterDamageRatio.Update();
|
|
m_EhterPvEDamageRatio.Update(); // NOTE: f110601.4L
|
|
m_DecreasePVPDamage.Update();
|
|
m_LuckMonIncDamage.Update(); // 幸运怪追加伤害
|
|
//m_LuckMonIncDamage.Update();
|
|
m_BypassDeffenceRatio.Update();
|
|
|
|
#ifdef _NA_008540_20151027_ADD_ITEMOPTION_ELITE4
|
|
m_DecreaseDamageNPC.Update();
|
|
m_DecreaseDamageBerserker.Update();
|
|
m_DecreaseDamageDragonKnight.Update();
|
|
m_DecreaseDamageValkyrie.Update();
|
|
m_DecreaseDamageElementalist.Update();
|
|
m_DecreaseDamageShadow.Update();
|
|
m_DecreaseDamageMystic.Update();
|
|
m_DecreaseDamageHellroid.Update();
|
|
m_DecreaseDamageWitchBlade.Update();
|
|
#endif //_NA_008540_20151027_ADD_ITEMOPTION_ELITE4
|
|
|
|
#ifdef _NA_003740_20210704_SKILL_LEVLEUP_OPTION
|
|
m_SkillLevelUp.Update(); //技能等级提升 by:十里坡剑神 2021-07-04
|
|
#endif //_NA_003740_20210704_SKILL_LEVLEUP_OPTION
|
|
|
|
#ifdef _20220109_ADD_ATTACKDECDAMAGE
|
|
m_PassDecDamage.Update();
|
|
#endif
|
|
|
|
};
|
|
;{
|
|
m_IncreaseReserveHPRatio.Update();
|
|
m_MPSpendIncrease.Update();
|
|
m_ResistBadStatusRatio.Update();
|
|
};
|
|
;{
|
|
m_SkillLevelAll.Update();
|
|
m_DecreaseLimitStat.Update();
|
|
m_DecreaseItemDuraRatio.Update();
|
|
m_BonusMoneyRatio.Update();
|
|
m_BonusExpRatio.Update();
|
|
m_ResurrectionRatio.Update(); // 사망시 스스로 부활 할 확률
|
|
m_CompositeIncRatio.Update(); // 조합 성공률 증가
|
|
m_AutoPickup.Update();
|
|
m_IncreaseEnchantRatio.Update(); // 인첸트 성공률 증가
|
|
};
|
|
;{ //_NA_001231_20081216_ADD_OPTION_KIND
|
|
m_ResistHolding.Update();
|
|
m_ResistSleep.Update();
|
|
m_ResistPoison.Update();
|
|
m_ResistKnockBack.Update();
|
|
m_ResistDown.Update();
|
|
m_ResistStun.Update();
|
|
};
|
|
|
|
;{
|
|
m_PCBang.Update();
|
|
};
|
|
|
|
#ifdef _NA_006937_20131030_ABILITY_AND_STATE_CHANGE_CRITICAL
|
|
m_EnemyCriticalRatioChange.Update();
|
|
#endif //_NA_006937_20131030_ABILITY_AND_STATE_CHANGE_CRITICAL
|
|
#ifdef _NA_006680_20130426_ITEM_OPTION_ADD_AND_MODIFY
|
|
;{
|
|
m_AttackDamageAbsorbSDRatio.Update();
|
|
m_AttackDamageAbsorbHPRatio.Update();
|
|
};
|
|
#endif // _NA_006680_20130426_ITEM_OPTION_ADD_AND_MODIFY
|
|
#ifdef _NA_007330_20140620_GUILD_SYSTEM_EXTENSION
|
|
;{
|
|
m_CraftCostRatio.Update();
|
|
m_CraftPreventExtinctionMaterialRatio.Update();
|
|
m_EnchantCostRatio.Update();
|
|
m_EnchantPreventDestroyNDowngradeItemRatio.Update();
|
|
m_RecoverPotionCooltimeRatio.Update();
|
|
m_RecoverPotionRecoveryRatio.Update();
|
|
m_QuestRewardExpRatio.Update();
|
|
m_MaxDamageRatio.Update();
|
|
m_DominationMapobjectDamageRatio.Update();
|
|
m_ShopRepairHeimRatio.Update();
|
|
m_ShopBuyHeimRatio.Update();
|
|
};
|
|
#endif // _NA_007330_20140620_GUILD_SYSTEM_EXTENSION
|
|
#ifdef _NA_007667_20141001_WITCHBLADE_SKILL
|
|
;{
|
|
m_IncreaseDamageRatio.Update();
|
|
};
|
|
#endif //_NA_007667_20141001_WITCHBLADE_SKILL
|
|
#ifdef _NA_008486_20150914_TOTAL_BALANCE
|
|
;{
|
|
m_DebuffDuration.base_value_.value_ = CalcDebuffDuration((short)GetSPR());
|
|
m_DebuffDuration.Update();
|
|
};
|
|
#endif //_NA_008486_20150914_TOTAL_BALANCE
|
|
|
|
}
|
|
|
|
//==================================================================================================
|
|
|
|
void PlayerAttributes::UpdateAttackPower()
|
|
{
|
|
//----------------------------------------------------------------------------------------------
|
|
// early instance for busy access fields
|
|
const eCHAR_TYPE char_type = owner_->GetCharType();
|
|
//const LEVELTYPE char_level = owner_->GetLevel();
|
|
//const DWORD weapon_kind = owner_->GetWeaponKind();
|
|
const short stat_str = static_cast<short>(GetSTR());
|
|
const short stat_dex = static_cast<short>(GetDEX());
|
|
const short stat_int = static_cast<short>(GetINT());
|
|
//----------------------------------------------------------------------------------------------
|
|
// 기본 공격력 계산
|
|
//-----------------------------------------------------------------
|
|
// 근거리, 원거리
|
|
m_BaseMeleeMinAttPower.base_value_.value_ = CalcMinMeleeAttackPower(char_type, stat_str, stat_dex);
|
|
m_BaseMeleeMaxAttPower.base_value_.value_ = CalcMaxMeleeAttackPower(char_type, stat_str, stat_dex);
|
|
m_BaseRangeMinAttPower.base_value_.value_ = CalcMinRangeAttackPower(char_type, stat_str, stat_dex);
|
|
m_BaseRangeMaxAttPower.base_value_.value_ = CalcMaxRangeAttackPower(char_type, stat_str, stat_dex);
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
// 마법
|
|
m_BaseMagicMinAttPower.base_value_.value_ = CalcMagicAttackPower(true, stat_int);
|
|
m_BaseMagicMaxAttPower.base_value_.value_ = CalcMagicAttackPower(false, stat_int);
|
|
#endif
|
|
// 스텟에 의한 Base + 아이템에 의한 Base
|
|
m_BaseMeleeMinAttPower.Update();
|
|
m_BaseMeleeMaxAttPower.Update();
|
|
m_BaseRangeMinAttPower.Update();
|
|
m_BaseRangeMaxAttPower.Update();
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
m_BaseMagicMinAttPower.Update();
|
|
m_BaseMagicMaxAttPower.Update();
|
|
#endif
|
|
//-----------------------------------------------------------------
|
|
// 옵션 공격력
|
|
//-----------------------------------------------------------------
|
|
// 옵션 전체(물리, 마법) 공격력을 적용한다.
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
m_OptionPhysicalAttPower.Update();
|
|
#else
|
|
for (int i = eATTR_KIND_ITEM; i <= eATTR_KIND_RATIO; ++i)
|
|
{
|
|
eATTR_KIND attr_kind = static_cast<eATTR_KIND>(i);
|
|
const int apply_value = \
|
|
static_cast<const AttrValue&>(m_OptionAllAttPower)[attr_kind];
|
|
if (apply_value)
|
|
{
|
|
m_OptionPhysicalAttPower[attr_kind] += apply_value;
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
m_OptionMagicAttPower[attr_kind] += apply_value;
|
|
#endif
|
|
};
|
|
}
|
|
|
|
// 옵션 공격력의 eATTR_KIND_CALC 값을 계산한다.
|
|
m_OptionPhysicalAttPower.Update();
|
|
m_OptionMagicAttPower.Update();
|
|
// 전체 반영 파라미터인 경우 초기화를 해야 다음번에 다시 계산되지 않는다.
|
|
m_OptionAllAttPower.Init();
|
|
#endif
|
|
//
|
|
int iAveragePhyAttPower = (GetBaseMeleeMinAttPower() + GetBaseMeleeMaxAttPower() +
|
|
GetBaseRangeMinAttPower() + GetBaseRangeMaxAttPower()) / 4;
|
|
|
|
m_OptionPhysicalAttPower.calc_value_.value_ += \
|
|
(iAveragePhyAttPower + GetOptionPhysicalAttPower()) * \
|
|
static_cast<const AttrValue&>(m_OptionPhysicalAttPower)[eATTR_KIND_RATIO] / 100;
|
|
m_OptionPhysicalAttPower.calc_value_.touch(\
|
|
static_cast<eBOUNDTYPE>(m_OptionPhysicalAttPower.bound_type_));
|
|
//
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
int iAverageMagicAttPower = (GetBaseMagicMinAttPower() + GetBaseMagicMaxAttPower()) / 2;
|
|
m_OptionMagicAttPower.calc_value_.value_ +=
|
|
(iAverageMagicAttPower + GetOptionMagicAttPower()) *
|
|
static_cast<const AttrValue&>(m_OptionMagicAttPower)[eATTR_KIND_RATIO] / 100;
|
|
m_OptionMagicAttPower.calc_value_.touch(\
|
|
static_cast<eBOUNDTYPE>(m_OptionMagicAttPower.bound_type_));
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------
|
|
// 속성 공격력
|
|
//-----------------------------------------------------------------
|
|
// 속성 전체 공격력을 적용한다.
|
|
;{
|
|
int magical_all_attacks[eATTR_KIND_RATIO - eATTR_KIND_ITEM + 1];
|
|
int* apply_value_it = magical_all_attacks;
|
|
for (int i = eATTR_KIND_ITEM; i <= eATTR_KIND_RATIO; ++i) {
|
|
*apply_value_it++ = \
|
|
static_cast<const AttrValue&>(m_MagicalAllAttPower)[static_cast<eATTR_KIND>(i)];
|
|
}
|
|
for (int j = eATTACK_TYPE_WATER; j <= eATTACK_TYPE_DARKNESS; ++j)
|
|
{
|
|
AttrValue& attr_value = m_MagicalAttPower[j];
|
|
apply_value_it = magical_all_attacks;
|
|
// f100930.6L, fixed a missing increase iteration
|
|
for (int i = eATTR_KIND_ITEM; i <= eATTR_KIND_RATIO; ++i, ++apply_value_it)
|
|
{
|
|
eATTR_KIND attr_kind = static_cast<eATTR_KIND>(i);
|
|
if (int apply_value = *apply_value_it) {
|
|
attr_value[attr_kind] += apply_value;
|
|
}
|
|
}
|
|
}
|
|
#if SUN_CODE_BACKUP
|
|
for (int i = eATTACK_TYPE_WATER; i <= eATTACK_TYPE_DARKNESS; ++i)
|
|
{
|
|
for (int j = eATTR_KIND_ITEM; j <= eATTR_KIND_RATIO; ++j)
|
|
{
|
|
eAttrKind = (eATTR_KIND)j;
|
|
m_MagicalAttPower[i][eAttrKind] += m_MagicalAllAttPower[eAttrKind];
|
|
}
|
|
}
|
|
#endif
|
|
};
|
|
|
|
// 속성 공격력의 eATTR_KIND_CALC 값을 계산한다.
|
|
for (int i = 0; i < eATTACK_TYPE_MAX; ++i)
|
|
{
|
|
m_MagicalAttPower[i].Update();
|
|
}
|
|
|
|
// 전체 반영 파라미터인 경우 초기화를 해야 다음번에 다시 계산되지 않는다.
|
|
m_MagicalAllAttPower.Init();
|
|
}
|
|
|
|
//==================================================================================================
|
|
|
|
void PlayerAttributes::UpdateDefense()
|
|
{
|
|
//----------------------------------------------------------------------------------------------
|
|
// early instance for busy access fields
|
|
const eCHAR_TYPE char_type = owner_->GetCharType();
|
|
//const LEVELTYPE char_level = owner_->GetLevel();
|
|
//const DWORD weapon_kind = owner_->GetWeaponKind();
|
|
const int stat_vit = static_cast<short>(GetVIT());
|
|
const short stat_spr = static_cast<short>(GetSPR());
|
|
//----------------------------------------------------------------------------------------------
|
|
;{
|
|
// 기본 방어력 계산
|
|
//-----------------------------------------------------------------
|
|
// 근거리, 원거리
|
|
DWORD physicalDefense = CalcPhyBaseDef(char_type, stat_vit);
|
|
m_BaseMeleeDefPower.base_value_.value_ = physicalDefense;
|
|
m_BaseRangeDefPower.base_value_.value_ = physicalDefense;
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
// 마법
|
|
m_BaseMagicDefPower.base_value_.value_ = CalcMagicBaseDef(char_type, stat_spr);
|
|
#endif
|
|
// 스텟에 의한 Base + 아이템에 의한 Base
|
|
m_BaseMeleeDefPower.Update();
|
|
m_BaseRangeDefPower.Update();
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
m_BaseMagicDefPower.Update();
|
|
#endif
|
|
};
|
|
;{
|
|
//-----------------------------------------------------------------
|
|
// 옵션 방어력
|
|
//-----------------------------------------------------------------
|
|
// 옵션 전체(물리, 마법) 방어력을 적용한다.
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
m_OptionPhysicalDefPower.Update();
|
|
#else
|
|
for (int i = eATTR_KIND_ITEM; i <= eATTR_KIND_RATIO; ++i)
|
|
{
|
|
eATTR_KIND attr_kind = static_cast<eATTR_KIND>(i);
|
|
const int apply_value = \
|
|
static_cast<const AttrValue&>(m_OptionAllDefPower)[attr_kind];
|
|
if (apply_value)
|
|
{
|
|
m_OptionPhysicalDefPower[attr_kind] += apply_value;
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
m_OptionMagicDefPower[attr_kind] += apply_value;
|
|
#endif
|
|
}
|
|
}
|
|
// 옵션 공격력의 eATTR_KIND_CALC 값을 계산한다.
|
|
m_OptionPhysicalDefPower.Update();
|
|
m_OptionMagicDefPower.Update();
|
|
// 전체 반영 파라미터인 경우 초기화를 해야 다음번에 다시 계산되지 않는다.
|
|
m_OptionAllDefPower.Init();
|
|
#endif
|
|
//
|
|
int iAverageDefPower = (GetBaseMeleeDefPower() + GetBaseRangeDefPower()) / 2;
|
|
// 옵션 방어력까지 포함해서 비율을 적용시킨다.
|
|
m_OptionPhysicalDefPower.calc_value_.value_ += \
|
|
(iAverageDefPower + GetOptionPhysicalDefPower()) * \
|
|
static_cast<const AttrValue&>(m_OptionPhysicalDefPower)[eATTR_KIND_RATIO] / 100;
|
|
m_OptionPhysicalDefPower.calc_value_.touch(\
|
|
static_cast<eBOUNDTYPE>(m_OptionPhysicalDefPower.bound_type_));
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
//
|
|
m_OptionMagicDefPower.calc_value_.value_ += \
|
|
(GetBaseMagicDefPower() + GetOptionMagicDefPower()) * \
|
|
static_cast<const AttrValue&>(m_OptionMagicDefPower)[eATTR_KIND_RATIO] / 100;
|
|
m_OptionMagicDefPower.calc_value_.touch(\
|
|
static_cast<eBOUNDTYPE>(m_OptionMagicDefPower.bound_type_));
|
|
#endif
|
|
};
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
;{
|
|
// 공격 대상 방어력 감소율 (EP1: same as m_ReduceDefenseRate)
|
|
m_ReducePhysicalTargetDefenseRatio.Update();
|
|
#ifdef _NA_004994_20120614_RATE_OF_DEFENCE_POWER_REDUCTION_OF_TARGET
|
|
m_ReduceElementTargetDefenseRatioAll.Update();
|
|
#endif // _NA_004994_20120614_RATE_OF_DEFENCE_POWER_REDUCTION_OF_TARGET
|
|
const int all_apply_value = m_ReduceElementTargetDefenseRatioAll.calc_value_.value_;
|
|
for (AttrValue* it = m_ReduceElementTargetDefenseRatio;
|
|
it != &m_ReduceElementTargetDefenseRatio[_countof(m_ReduceElementTargetDefenseRatio)];
|
|
++it)
|
|
{
|
|
it->Update();
|
|
it->calc_value_.value_ += all_apply_value;
|
|
it->calc_value_.touch(static_cast<eBOUNDTYPE>(it->bound_type_));
|
|
}
|
|
};
|
|
;{
|
|
// 공격에 대한 데미지 감소율
|
|
m_ReducePhysicalDamageRatio.Update();
|
|
m_ReduceElementDamageRatioAll.Update();
|
|
const int all_apply_value = m_ReduceElementDamageRatioAll.calc_value_.value_;
|
|
// 원소 방어력 ranges = [-100%, +XXX% ]
|
|
for (AttrValue* it = m_ReduceElementDamageRatio;
|
|
it != &m_ReduceElementDamageRatio[_countof(m_ReduceElementDamageRatio)];
|
|
++it)
|
|
{
|
|
it->Update();
|
|
it->calc_value_.value_ += all_apply_value;
|
|
it->calc_value_.touch(static_cast<eBOUNDTYPE>(it->bound_type_));
|
|
}
|
|
};
|
|
#else //if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
;{
|
|
//-----------------------------------------------------------------
|
|
// 속성 방어력
|
|
//-----------------------------------------------------------------
|
|
// 속성 전체 방어력을 적용한다.
|
|
int magical_all_defences[eATTR_KIND_RATIO - eATTR_KIND_ITEM + 1];
|
|
int* apply_value_it = magical_all_defences;
|
|
for (int i = eATTR_KIND_ITEM; i <= eATTR_KIND_RATIO; ++i) {
|
|
*apply_value_it++ = \
|
|
static_cast<const AttrValue&>(m_MagicalAllDefPower)[static_cast<eATTR_KIND>(i)];
|
|
}
|
|
for (int j = eATTACK_TYPE_WATER; j <= eATTACK_TYPE_DARKNESS; ++j)
|
|
{
|
|
AttrValue& attr_value = m_MagicalDefPower[j];
|
|
apply_value_it = magical_all_defences;
|
|
// f100930.6L, fixed a missing increase iteration
|
|
for (int i = eATTR_KIND_ITEM; i <= eATTR_KIND_RATIO; ++i, ++apply_value_it)
|
|
{
|
|
eATTR_KIND attr_kind = static_cast<eATTR_KIND>(i);
|
|
if (int apply_value = *apply_value_it) {
|
|
attr_value[attr_kind] += apply_value;
|
|
}
|
|
}
|
|
}
|
|
#if SUN_CODE_BACKUP
|
|
for (int i = eATTACK_TYPE_WATER; i <= eATTACK_TYPE_DARKNESS; ++i)
|
|
{
|
|
for (int j = eATTR_KIND_ITEM; j <= eATTR_KIND_RATIO; ++j)
|
|
{
|
|
eAttrKind = (eATTR_KIND)j;
|
|
m_MagicalDefPower[i][eAttrKind] += m_MagicalAllDefPower[eAttrKind];
|
|
}
|
|
}
|
|
#endif
|
|
// 속성 방어력의 eATTR_KIND_CALC 값을 계산한다.
|
|
for (int i = 0; i < eATTACK_TYPE_MAX; ++i)
|
|
{
|
|
m_MagicalDefPower[i].Update();
|
|
}
|
|
|
|
// 전체 반영 파라미터인 경우 초기화를 해야 다음번에 다시 계산되지 않는다.
|
|
m_MagicalAllDefPower.Init();
|
|
};
|
|
#endif
|
|
}
|
|
|
|
#ifdef _SERVER
|
|
//==================================================================================================
|
|
// CHANGES: f110413.1L, added interface to support light recovery quantity calculation for server
|
|
// NOTE: HP, MP and SD recovery have busy update processes.
|
|
// if server is to avoid busy updating, detach the recovery parts from full update calling.
|
|
// the usage of this method is calling on changed recoveries in run-time service
|
|
// not to initialize like the 'SetCharInfo'
|
|
void PlayerAttributes::UpdateChangedRecoveries(const BASE_PLAYERINFO* player_info,
|
|
bool hp_update, int hp_recovery_quantity,
|
|
bool mp_update, int mp_recovery_quantity,
|
|
bool sd_update, int sd_recovery_quantity)
|
|
{
|
|
bool need_update = false;
|
|
if (hp_update) {
|
|
m_RecoverHP.base_value_.value_ = hp_recovery_quantity;
|
|
need_update = true;
|
|
};
|
|
if (mp_update) {
|
|
m_RecoverMP.base_value_.value_ = mp_recovery_quantity;
|
|
need_update = true;
|
|
};
|
|
if (sd_update) {
|
|
m_RecoverSD.base_value_.value_ = sd_recovery_quantity;
|
|
need_update = true;
|
|
};
|
|
|
|
if (need_update) {
|
|
// HP, MP and SD recovery don't effect other attributes, also don't notify to client
|
|
UpdateRecoveries(static_cast<eCHAR_TYPE>(player_info->m_byClassCode));
|
|
};
|
|
}
|
|
|
|
// CHANGES: f110413.1L
|
|
void PlayerAttributes::UpdateRecoveries(eCHAR_TYPE char_type)
|
|
{
|
|
m_RecoverHP.Update();
|
|
// CHANGES: f110322.4L, prevent the berserker's recovery quantity
|
|
// that has over than minus value by items or skills attribute value
|
|
// unlike plan's requirements.
|
|
if (char_type != eCHAR_BERSERKER) {
|
|
m_RecoverMP.Update();
|
|
}
|
|
else {
|
|
m_RecoverMP.calc_value_.value_ = -1;
|
|
}
|
|
m_RecoverSD.Update();
|
|
}
|
|
#endif //
|
|
|
|
//==================================================================================================
|
|
|
|
void PlayerAttributes::Display()
|
|
{
|
|
// (WAVERIX)(NOTE!)
|
|
// 유저에게 디버깅하기 아주 딱 좋게 게임 로직 스펙을 공개하네요. -.-;;; 주의합시다...
|
|
// 보안은 담당자만 하는게 아닙니다. 누구나가 지킬 것은 지켜야죠. 말짱 도루묵은 제발 주의해주시길...
|
|
// 현 시각 07/11/13 16:00 - 이걸 보니 정말 맥 빠지네요...ㅠㅠ
|
|
#ifdef _DEBUG
|
|
SUNLOG(eDEV_LOG,
|
|
_T("============================ Display Attribute ==============================="));
|
|
SUNLOG(eDEV_LOG, _T("|MaxHP=%d|MaxMP=%d|MaxSD=%d|"), GetMaxHP(), GetMaxMP(), GetMaxSD());
|
|
SUNLOG(eDEV_LOG, _T("|RecoverHPRatio=%d|RecoverMPRatio=%d|RecoverSDRatio=%d|"),
|
|
GetRecoverHP(), GetRecoverMP(), GetRecoverSD());
|
|
//_T("==============================================================================")
|
|
|
|
// 공격력
|
|
// 스텟에 의한 Base + 아이템에 의한 Base
|
|
SUNLOG(eDEV_LOG, "基本近距离最小攻击力 = %d, 基本近距离最大攻击力 = %d, 选项物理攻击力 = %d",
|
|
GetBaseMeleeMinAttPower(), GetBaseMeleeMaxAttPower(), GetOptionPhysicalAttPower());
|
|
SUNLOG(eDEV_LOG, "基本远距离最小攻击力= %d, 基本远距离最大攻击力= %d",
|
|
GetBaseRangeMinAttPower(), GetBaseRangeMaxAttPower());
|
|
SUNLOG(eDEV_LOG, "基本魔法最小攻击力= %d, 基本魔法最大攻击力= %d, 选项魔法攻击力= %d",
|
|
GetBaseMagicMinAttPower(), GetBaseMagicMaxAttPower(), GetOptionMagicAttPower());
|
|
|
|
// 방어력
|
|
// 스텟에 의한 Base + 아이템에 의한 Base
|
|
SUNLOG(eDEV_LOG, "基本近身防御力= %d, 选项物理防御力= %d",
|
|
GetBaseMeleeDefPower(), GetOptionPhysicalDefPower());
|
|
SUNLOG(eDEV_LOG, "基本远程防御力= %d", GetBaseRangeDefPower());
|
|
SUNLOG(eDEV_LOG, "基本魔法防御力= %d", GetBaseMagicDefPower());
|
|
|
|
// 공격 성공률, 회피율
|
|
SUNLOG(eDEV_LOG, "攻击成功率 = %d, 攻击回避率 = %d",
|
|
GetPhysicalAttackRate(), GetPhysicalAvoidRate());
|
|
// 속도(이동속도, 공격속도, 패스트리커버리)
|
|
SUNLOG(eDEV_LOG, "移动速度 = %d, 攻击速度 = %d ",
|
|
GetMoveSpeedRatio(), GetAttSpeedRatio());
|
|
// 스킬 공격력, 스킬 퍼센트 추가 데미지
|
|
SUNLOG(eDEV_LOG, "技能攻击力 = %d, 技能百分比追加伤害 = %d ",
|
|
GetSkillAttackPower(), GetSkillPercentDamage());
|
|
|
|
// 기본 스탯들
|
|
SUNLOG(eDEV_LOG, "力量 = %d, 敏捷 = %d, 体力 = %d, 地力 = %d, 精神力 = %d",
|
|
GetSTR(), GetDEX(), GetVIT(), GetINT(), GetSPR());
|
|
SUNLOG(eDEV_LOG, "熟练度1 = %d, 熟练度2 = %d", GetExperty1(), GetExperty2());
|
|
|
|
// 속성 공격력
|
|
SUNLOG(eDEV_LOG, "属性攻击力物 = %d, 火 = %d, 风 = %d, 地 = %d, 暗黑 = %d ",
|
|
GetMagicalAttackPower(eATTACK_TYPE_WATER), GetMagicalAttackPower(eATTACK_TYPE_FIRE),
|
|
GetMagicalAttackPower(eATTACK_TYPE_WIND), GetMagicalAttackPower(eATTACK_TYPE_EARTH),
|
|
GetMagicalAttackPower(eATTACK_TYPE_DARKNESS));
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
#else
|
|
//속성 방여력
|
|
SUNLOG(eDEV_LOG, "属性防御力物 = %d, 火 = %d, 风 = %d, 地 = %d, 暗黑 = %d ",
|
|
GetMagicalDefense(eATTACK_TYPE_WATER), GetMagicalDefense(eATTACK_TYPE_FIRE),
|
|
GetMagicalDefense(eATTACK_TYPE_WIND), GetMagicalDefense(eATTACK_TYPE_EARTH),
|
|
GetMagicalDefense(eATTACK_TYPE_DARKNESS));
|
|
#endif
|
|
SUNLOG(
|
|
eDEV_LOG,
|
|
"特殊能力复活 = %d, 双重伤害 = %d, 幸运兽攻击力 = %d, 组合成功率 = %d, 投资成功率 = %d",
|
|
GetResurrectionRatio(),
|
|
GetDoubleDamageRatio(),
|
|
GetLuckMonIncDamage(),
|
|
GetCompositeIncRatio(),
|
|
GetIncreaseEnchantRatio()
|
|
);
|
|
|
|
SUNLOG(eDEV_LOG, "最小伤害增加,减少(放大率) = %d, %d, "
|
|
"最大伤害增加,减少(放大率)= %d, %d, 防御力无视几率 = %d ",
|
|
GetIncreaseMinDamage(), GetRatioIncreaseMinDamage(),
|
|
GetIncreaseMaxDamage(), GetRatioIncreaseMaxDamage(),
|
|
GetBypassDeffenceRatio());
|
|
|
|
SUNLOG(eDEV_LOG, "物理攻击力 = %d, 魔法攻击力 = %d, "
|
|
"物理防御力 = %d, 魔法防御力 = %d, 伤害减少 = %d(放大率:%d)",
|
|
GetOptionPhysicalAttPower(), GetOptionMagicAttPower(),
|
|
GetOptionPhysicalDefPower(), GetOptionMagicDefPower(),
|
|
GetDecDamage(), GetRatioDecDamage());
|
|
|
|
SUNLOG(eDEV_LOG, "耐久度 = %d, 持续时间增加 = %d, 持续时间 = %d, (奖励 = %d, 技能 = %d)",
|
|
GetDecreaseItemDuraRatio(), GetIncreaseSkillDuration(),
|
|
GetDecreaseSkillDuration(), GetNormalRangeBonus(), GetSkillRangeBonus());
|
|
|
|
SUNLOG(eDEV_LOG, "醚伤害变化: %d", GetEhterDamageRatio());
|
|
|
|
// 속성 공격력
|
|
SUNLOG(eDEV_LOG, "不良状态抵抗比率 : 全部 = %d, 保持 = %d, 沉睡= %d, "
|
|
"中毒= %d, 击退 = %d, 不良状态抵抗率下降 = %d, 击晕 = %d, pvp 伤害 = %d(减少:%d)",
|
|
GetResistBadStatusRatio(), GetResistHolding(), GetResistSleep(),
|
|
GetResistPoison(), GetResistKnockBack(), GetResistDown(),
|
|
GetResistStun(), GetDecreasePVPDamage(), GetRatioDecreasePVPDamage());
|
|
#endif //_DEBUG
|
|
}
|
|
|