550 lines
16 KiB
C++
550 lines
16 KiB
C++
#include "precompile.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "ability.h"
|
|
#include "buff.h"
|
|
#include "creature.h"
|
|
#include "netdata.h"
|
|
#include "room.h"
|
|
#include "attrhelper.h"
|
|
|
|
#include "mt/Equip.h"
|
|
#include "mt/Buff.h"
|
|
|
|
struct AttrAddition : public std::enable_shared_from_this<AttrAddition>
|
|
{
|
|
list_head entry;
|
|
list_head list_entry;
|
|
int attr_id;
|
|
float value;
|
|
std::shared_ptr<AttrAddition> holder;
|
|
std::shared_ptr<std::function<std::string()>> get_source;
|
|
|
|
AttrAddition(int attr_id, float value)
|
|
{
|
|
this->attr_id = attr_id;
|
|
this->value = value;
|
|
INIT_LIST_HEAD(&entry);
|
|
}
|
|
|
|
bool IsAdd()
|
|
{
|
|
return a8::GtOrEqZero(value);
|
|
}
|
|
|
|
};
|
|
|
|
static bool IsMulCalc(int attr_id)
|
|
{
|
|
switch (attr_id) {
|
|
case kHAT_pHealthRateIn:
|
|
case kHAT_vHealthRateIn:
|
|
case kHAT_pAttackRateIn:
|
|
case kHAT_vAttackRateIn:
|
|
case kHAT_pDefendRateIn:
|
|
case kHAT_pBlockRateIn:
|
|
case kHAT_pCritRateIn:
|
|
#if 0
|
|
case kHAT_vSpeedRateIn:
|
|
#endif
|
|
case kHAT_vHaste:
|
|
case kHAT_vDrain:
|
|
case kHAT_vTenacity:
|
|
case kHAT_vDamageTakenRateIn:
|
|
case kHAT_vDamageDealtRateIn:
|
|
case kHAT_vNormalDamageTakenRateIn: //受击方
|
|
case kHAT_vNormalDamageDealtRateIn: //攻击方
|
|
case kHAT_vSkillDamageTakenRateIn: //受击方
|
|
case kHAT_vSkillDamageDealtRateIn: //攻击方
|
|
case kHAT_vHealthGainRateIn:
|
|
case kHAT_vHealDealtRateIn:
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ability::Ability(CreatureWeakPtr owner)
|
|
{
|
|
for (auto& tuple : attr_add_) {
|
|
std::get<0>(tuple) = 0.0f;
|
|
INIT_LIST_HEAD(&std::get<1>(tuple));
|
|
}
|
|
for (auto& tuple : attr_dec_) {
|
|
std::get<0>(tuple) = 0.0f;
|
|
INIT_LIST_HEAD(&std::get<1>(tuple));
|
|
}
|
|
for (auto& list : attr_list_) {
|
|
INIT_LIST_HEAD(&list);
|
|
}
|
|
for (auto& tuple : vattr_add_) {
|
|
std::get<0>(tuple) = 0.0f;
|
|
INIT_LIST_HEAD(&std::get<1>(tuple));
|
|
}
|
|
for (auto& tuple : vattr_dec_) {
|
|
std::get<0>(tuple) = 0.0f;
|
|
INIT_LIST_HEAD(&std::get<1>(tuple));
|
|
}
|
|
for (auto& list : vattr_list_) {
|
|
INIT_LIST_HEAD(&list);
|
|
}
|
|
owner_ = owner;
|
|
}
|
|
|
|
Ability::~Ability()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void Ability::Clear()
|
|
{
|
|
for (auto& list : attr_list_) {
|
|
while (!list_empty(&list)) {
|
|
AttrAddition* e = list_first_entry(&list,
|
|
AttrAddition,
|
|
list_entry);
|
|
list_del_init(&e->entry);
|
|
list_del_init(&e->list_entry);
|
|
e->holder = nullptr;
|
|
}
|
|
}
|
|
for (auto& list : vattr_list_) {
|
|
while (!list_empty(&list)) {
|
|
AttrAddition* e = list_first_entry(&list,
|
|
AttrAddition,
|
|
list_entry);
|
|
list_del_init(&e->entry);
|
|
list_del_init(&e->list_entry);
|
|
e->holder = nullptr;
|
|
}
|
|
}
|
|
|
|
{
|
|
for (auto& tuple : attr_add_) {
|
|
std::get<0>(tuple) = 0.0f;
|
|
}
|
|
for (auto& tuple : attr_dec_) {
|
|
std::get<0>(tuple) = 0.0f;
|
|
}
|
|
for (auto& tuple : vattr_add_) {
|
|
std::get<0>(tuple) = 0.0f;
|
|
}
|
|
for (auto& tuple : vattr_dec_) {
|
|
std::get<0>(tuple) = 0.0f;
|
|
}
|
|
}
|
|
switch_times_ = {};
|
|
immune_tags_.clear();
|
|
}
|
|
|
|
void Ability::IncImmuneTimes(int tag)
|
|
{
|
|
auto itr = immune_tags_.find(tag);
|
|
if (itr != immune_tags_.end()) {
|
|
++itr->second;
|
|
} else {
|
|
immune_tags_[tag] = 1;
|
|
}
|
|
}
|
|
|
|
void Ability::DecImmuneTimes(int tag)
|
|
{
|
|
auto itr = immune_tags_.find(tag);
|
|
if (itr != immune_tags_.end()) {
|
|
--itr->second;
|
|
if (itr->second <= 0) {
|
|
immune_tags_.erase(itr);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Ability::CanImmune(int tag)
|
|
{
|
|
return immune_tags_.find(tag) != immune_tags_.end();
|
|
}
|
|
|
|
bool Ability::CanImmune(const std::set<int>& tags)
|
|
{
|
|
if (!immune_tags_.empty()) {
|
|
for (int tag : tags) {
|
|
if (CanImmune(tag)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AttrHandle Ability::AddAttr(int attr_id, float val)
|
|
{
|
|
float old_max_hp = owner_.Get()->GetMaxHP();
|
|
if (IsValidHumanAttr(attr_id) ||
|
|
IsValidHumanVirtualAttr(attr_id)) {
|
|
auto p = std::make_shared<AttrAddition>(attr_id, val);
|
|
p->holder = p;
|
|
if (p->IsAdd()) {
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
list_add_tail(&p->entry, &std::get<1>(attr_add_[attr_id]));
|
|
list_add_tail(&p->list_entry, &attr_list_[attr_id]);
|
|
} else {
|
|
list_add_tail(&p->entry, &std::get<1>(vattr_add_[attr_id - kHVAT_Begin]));
|
|
list_add_tail(&p->list_entry, &vattr_list_[attr_id - kHVAT_Begin]);
|
|
}
|
|
RecalcAttrAddition(attr_id);
|
|
} else {
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
list_add_tail(&p->entry, &std::get<1>(attr_dec_[attr_id]));
|
|
list_add_tail(&p->list_entry, &attr_list_[attr_id]);
|
|
} else {
|
|
list_add_tail(&p->entry, &std::get<1>(vattr_dec_[attr_id - kHVAT_Begin]));
|
|
list_add_tail(&p->list_entry, &vattr_list_[attr_id - kHVAT_Begin]);
|
|
}
|
|
RecalcAttrRuduce(attr_id);
|
|
}
|
|
if (std::fabs(owner_.Get()->GetMaxHP() - old_max_hp) > 0.000001f) {
|
|
owner_.Get()->SetMaxHP(owner_.Get()->GetMaxHP());
|
|
owner_.Get()->SetHP(owner_.Get()->GetHP() * (owner_.Get()->GetMaxHP() / old_max_hp));
|
|
owner_.Get()->room->frame_event.AddHpChg(owner_);
|
|
}
|
|
return p;
|
|
}
|
|
return AttrHandle();
|
|
}
|
|
|
|
void Ability::RemoveAttr(AttrHandle handle)
|
|
{
|
|
float old_max_hp = owner_.Get()->GetMaxHP();
|
|
if (!handle.expired()) {
|
|
auto p = handle.lock();
|
|
list_del_init(&p->entry);
|
|
list_del_init(&p->list_entry);
|
|
if (p->IsAdd()) {
|
|
RecalcAttrAddition(p->attr_id);
|
|
} else {
|
|
RecalcAttrRuduce(p->attr_id);
|
|
}
|
|
p->holder = nullptr;
|
|
if (std::fabs(owner_.Get()->GetMaxHP() - old_max_hp) > 0.000001f) {
|
|
owner_.Get()->SetMaxHP(owner_.Get()->GetMaxHP());
|
|
owner_.Get()->SetHP(owner_.Get()->GetHP() * (owner_.Get()->GetMaxHP() / old_max_hp));
|
|
owner_.Get()->room->frame_event.AddHpChg(owner_);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Ability::RecalcAttrAddition(int attr_id)
|
|
{
|
|
list_head* head = nullptr;
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
head = &std::get<1>(attr_add_[attr_id]);
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
head = &std::get<1>(vattr_add_[attr_id - kHVAT_Begin]);
|
|
} else {
|
|
return;
|
|
}
|
|
list_head* pos = nullptr;
|
|
list_head* next = nullptr;
|
|
float new_val = 0.0f;
|
|
bool inited = false;
|
|
list_for_each_safe(pos, next, head) {
|
|
AttrAddition* e = list_entry(pos,
|
|
AttrAddition,
|
|
entry);
|
|
if (IsMulCalc(attr_id)) {
|
|
if (!inited) {
|
|
new_val = 1;
|
|
inited = true;
|
|
}
|
|
new_val *= 1 + e->value;
|
|
} else {
|
|
new_val += e->value;
|
|
}
|
|
}
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
std::get<0>(attr_add_[attr_id]) = new_val;
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
std::get<0>(vattr_add_[attr_id - kHVAT_Begin]) = new_val;
|
|
}
|
|
owner_.Get()->GetNetData()->RecalcMaxHP();
|
|
owner_.Get()->GetNetData()->RecalcAttack();
|
|
owner_.Get()->GetNetData()->RecalcDef();
|
|
owner_.Get()->GetNetData()->RecalcBlock();
|
|
owner_.Get()->GetNetData()->RecalcCrit();
|
|
}
|
|
|
|
void Ability::RecalcAttrRuduce(int attr_id)
|
|
{
|
|
list_head* head = nullptr;
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
head = &std::get<1>(attr_dec_[attr_id]);
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
head = &std::get<1>(vattr_dec_[attr_id - kHVAT_Begin]);
|
|
} else {
|
|
return;
|
|
}
|
|
list_head* pos = nullptr;
|
|
list_head* next = nullptr;
|
|
float new_val = 0.0f;
|
|
bool inited = false;
|
|
list_for_each_safe(pos, next, head) {
|
|
AttrAddition* e = list_entry(pos,
|
|
AttrAddition,
|
|
entry);
|
|
if (IsMulCalc(attr_id)) {
|
|
if (!inited) {
|
|
new_val = 1;
|
|
inited = true;
|
|
}
|
|
new_val *= 1 + e->value;
|
|
} else {
|
|
new_val += e->value;
|
|
}
|
|
}
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
std::get<0>(attr_dec_[attr_id]) = new_val;
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
std::get<0>(vattr_dec_[attr_id - kHVAT_Begin]) = new_val;
|
|
}
|
|
owner_.Get()->GetNetData()->RecalcMaxHP();
|
|
owner_.Get()->GetNetData()->RecalcAttack();
|
|
owner_.Get()->GetNetData()->RecalcDef();
|
|
owner_.Get()->GetNetData()->RecalcBlock();
|
|
owner_.Get()->GetNetData()->RecalcCrit();
|
|
}
|
|
|
|
float Ability::GetAttrAddition(int attr_id)
|
|
{
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
return std::get<0>(attr_add_[attr_id]);
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
return std::get<0>(vattr_add_[attr_id - kHVAT_Begin]);
|
|
} else {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
float Ability::GetAttrRuduce(int attr_id)
|
|
{
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
return std::get<0>(attr_dec_[attr_id]);
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
return std::get<0>(vattr_dec_[attr_id - kHVAT_Begin]);
|
|
} else {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
void Ability::GMDelAttr(int attr_id, int idx)
|
|
{
|
|
list_head* head = nullptr;
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
head = &attr_list_[attr_id];
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
head = &vattr_list_[attr_id];
|
|
}
|
|
if (head) {
|
|
list_head* pos = nullptr;
|
|
list_head* next = nullptr;
|
|
int i = 0;
|
|
list_for_each_safe(pos, next, head) {
|
|
AttrAddition* e = list_entry(pos,
|
|
AttrAddition,
|
|
list_entry);
|
|
if (i == idx) {
|
|
RemoveAttr(e->shared_from_this());
|
|
return;
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Ability::GMClearAttr()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
std::vector<std::string> Ability::GMShowAttrs()
|
|
{
|
|
std::vector<std::string> strings;
|
|
strings.push_back(a8::Format("curr_hp:%f max_hp:%f atk:%f def:%f block:%f crit:%f level:%d exp:%f",
|
|
{
|
|
owner_.Get()->GetHP(),
|
|
owner_.Get()->GetNetData()->GetMaxHP(),
|
|
owner_.Get()->GetNetData()->GetAttack(),
|
|
owner_.Get()->GetNetData()->GetDef(),
|
|
owner_.Get()->GetNetData()->GetBlock(),
|
|
owner_.Get()->GetNetData()->GetCrit(),
|
|
owner_.Get()->GetHeroLevel(),
|
|
owner_.Get()->GetHeroExp()
|
|
}));
|
|
{
|
|
std::vector<std::string> tmp_strings;
|
|
int attr_id = 0;
|
|
for (auto& list : attr_list_) {
|
|
list_head* head = &list;
|
|
list_head* pos = nullptr;
|
|
list_head* next = nullptr;
|
|
std::string data = a8::Format("attr_id:%d attr_name:%s value:%f [",
|
|
{attr_id,
|
|
AttrHelper::GetAttrName(attr_id),
|
|
GetAttr(attr_id)});
|
|
int i = 0;
|
|
list_for_each_safe(pos, next, head) {
|
|
AttrAddition* e = list_entry(pos,
|
|
AttrAddition,
|
|
list_entry);
|
|
if (e->get_source) {
|
|
data += "" + a8::XValue(e->value).GetString() + (*e->get_source)() + ",";
|
|
} else {
|
|
data += "" + a8::XValue(e->value).GetString() + "<-none,";
|
|
}
|
|
++i;
|
|
}
|
|
if (i > 0) {
|
|
data += "]";
|
|
tmp_strings.push_back(data);
|
|
}
|
|
++attr_id;
|
|
}
|
|
strings.push_back("属性:");
|
|
if (tmp_strings.empty()) {
|
|
strings.push_back("无");
|
|
} else {
|
|
for (auto& str : tmp_strings) {
|
|
strings.push_back(str);
|
|
}
|
|
}
|
|
}
|
|
{
|
|
std::vector<std::string> tmp_strings;
|
|
int attr_id = 0;
|
|
for (auto& list : vattr_list_) {
|
|
list_head* head = &list;
|
|
list_head* pos = nullptr;
|
|
list_head* next = nullptr;
|
|
std::string data = a8::Format("attr_id:%d attr_name:%s value:%f [",
|
|
{attr_id,
|
|
AttrHelper::GetAttrName(attr_id),
|
|
GetAttr(attr_id)
|
|
});
|
|
int i = 0;
|
|
list_for_each_safe(pos, next, head) {
|
|
AttrAddition* e = list_entry(pos,
|
|
AttrAddition,
|
|
list_entry);
|
|
data += "" + a8::XValue(e->value).GetString() + ",";
|
|
++i;
|
|
}
|
|
if (i > 0) {
|
|
data += "]";
|
|
tmp_strings.push_back(data);
|
|
}
|
|
++attr_id;
|
|
}
|
|
strings.push_back("虚属性:");
|
|
if (tmp_strings.empty()) {
|
|
strings.push_back("无");
|
|
} else {
|
|
for (auto& str : tmp_strings) {
|
|
strings.push_back(str);
|
|
}
|
|
}
|
|
}
|
|
return strings;
|
|
}
|
|
|
|
void Ability::IncSwitch(int type)
|
|
{
|
|
if (type >= kSwitchTimeBegin && type < kSwitchTimeEnd) {
|
|
++switch_times_[type];
|
|
} else {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
void Ability::DecSwitch(int type)
|
|
{
|
|
if (type >= kSwitchTimeBegin && type < kSwitchTimeEnd) {
|
|
--switch_times_[type];
|
|
} else {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
int Ability::GetSwitchTimes(int type)
|
|
{
|
|
if (type >= kSwitchTimeBegin && type < kSwitchTimeEnd) {
|
|
return switch_times_[type];
|
|
} else {
|
|
abort();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
float Ability::GetAttr(int attr_id)
|
|
{
|
|
float val = 0.0f;
|
|
if (!HasAttr(attr_id)) {
|
|
return 0.0f;
|
|
}
|
|
if (IsMulCalc(attr_id)) {
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
val = (HasAddAttr(attr_id) ? std::get<0>(attr_add_[attr_id]) : 1) *
|
|
(HasDecAttr(attr_id) ? std::get<0>(attr_dec_[attr_id]) : 1);
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
val = (HasAddAttr(attr_id) ? std::get<0>(vattr_add_[attr_id - kHVAT_Begin]) : 1) *
|
|
(HasDecAttr(attr_id) ? std::get<0>(vattr_dec_[attr_id - kHVAT_Begin]) : 1);
|
|
}
|
|
val = val - 1.0f;
|
|
} else {
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
val = std::get<0>(attr_add_[attr_id]) +
|
|
std::get<0>(attr_dec_[attr_id]);
|
|
} else if (IsValidHumanVirtualAttr(attr_id)) {
|
|
val = std::get<0>(vattr_add_[attr_id - kHVAT_Begin]) +
|
|
std::get<0>(vattr_dec_[attr_id - kHVAT_Begin]);
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
|
|
bool Ability::HasAttr(int attr_id)
|
|
{
|
|
return HasAddAttr(attr_id) || HasDecAttr(attr_id);
|
|
}
|
|
|
|
bool Ability::HasAddAttr(int attr_id)
|
|
{
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
return !list_empty(&std::get<1>(attr_add_[attr_id]));
|
|
} if (IsValidHumanVirtualAttr(attr_id)) {
|
|
return !list_empty(&std::get<1>(vattr_add_[attr_id - kHVAT_Begin]));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Ability::HasDecAttr(int attr_id)
|
|
{
|
|
if (IsValidHumanAttr(attr_id)) {
|
|
return !list_empty(&std::get<1>(attr_dec_[attr_id]));
|
|
} if (IsValidHumanVirtualAttr(attr_id)) {
|
|
return !list_empty(&std::get<1>(vattr_dec_[attr_id - kHVAT_Begin]));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void Ability::SetSource(AttrHandle handle, std::shared_ptr<std::function<std::string()>> cb)
|
|
{
|
|
auto p = handle.lock();
|
|
p->get_source = cb;
|
|
}
|