3731 lines
115 KiB
C++
3731 lines
115 KiB
C++
#include "precompile.h"
|
|
|
|
#include <float.h>
|
|
|
|
#include "creature.h"
|
|
#include "metamgr.h"
|
|
#include "room.h"
|
|
#include "skill.h"
|
|
#include "human.h"
|
|
#include "hero.h"
|
|
#include "collider.h"
|
|
#include "roomobstacle.h"
|
|
#include "trigger.h"
|
|
#include "car.h"
|
|
#include "bullet.h"
|
|
#include "skillhelper.h"
|
|
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <glm/vec2.hpp>
|
|
|
|
static const auto hero_transform =
|
|
glm::rotate(
|
|
glm::mat4(1.0),
|
|
glm::radians(65.0f),
|
|
glm::vec3(0.0, 0.0, 1.0)
|
|
);
|
|
|
|
struct BulletInfo
|
|
{
|
|
CreatureWeakPtr c;
|
|
long long weapon_uniid = 0;
|
|
MetaData::Equip* weapon_meta = nullptr;
|
|
MetaData::Equip* bullet_meta = nullptr;
|
|
MetaData::Skill* skill_meta = nullptr;
|
|
a8::Vec2 bullet_born_pos;
|
|
a8::Vec2 bullet_dir;
|
|
float fly_distance = 0;
|
|
int weapon_lv = 0;
|
|
int delay_time = 0;
|
|
int recoil_force = 0;
|
|
int invincible_buff_uniid = 0;
|
|
int trace_target_uniid = 0;
|
|
float track_change_time = 0;
|
|
int is_through = 0;
|
|
int hand = 0;
|
|
std::weak_ptr<a8::XTimerPtr> keep_shot_animi_timer_ptr;
|
|
};
|
|
|
|
static void InternalCreateBullet(BulletInfo& bullet_info)
|
|
{
|
|
if (!bullet_info.c.Get()) {
|
|
return;
|
|
}
|
|
Creature* c = bullet_info.c.Get();
|
|
if (c->dead) {
|
|
return;
|
|
}
|
|
if (c->downed) {
|
|
return;
|
|
}
|
|
if (bullet_info.delay_time <= 0) {
|
|
if (c->GetCurrWeapon()->meta->i->bullet_consume_type() == kBulletConsumeMulti) {
|
|
if (c->GetCurrWeapon()->ammo <= 0) {
|
|
return;
|
|
}
|
|
--c->GetCurrWeapon()->ammo;
|
|
if (c->GetCurrWeapon()->ammo <= 0) {
|
|
c->AutoLoadingBullet();
|
|
}
|
|
if ((c->IsPlayer() || c->IsCar())) {
|
|
c->room->frame_event.AddBulletNumChg(c->GetWeakPtrRef());
|
|
c->room->frame_event.AddWeaponAmmoChg(c->GetWeakPtrRef());
|
|
}
|
|
}
|
|
if (bullet_info.recoil_force > 0) {
|
|
if (c->GetCurrWeapon()->ammo <= 0) {
|
|
c->DoRecoilForce(bullet_info.recoil_force);
|
|
bullet_info.bullet_born_pos = bullet_info.bullet_born_pos -
|
|
(bullet_info.bullet_dir * bullet_info.recoil_force);
|
|
}
|
|
}
|
|
int bullet_uniid = 0;
|
|
if (MetaMgr::Instance()->prebattle_can_use_skill ||
|
|
!(c->HasBuffEffect(kBET_Jump) || c->HasBuffEffect(kBET_Fly))) {
|
|
bullet_uniid = c->room->CreateBullet
|
|
(c,
|
|
c->shot_passenger,
|
|
bullet_info.weapon_meta,
|
|
bullet_info.bullet_meta,
|
|
bullet_info.skill_meta,
|
|
bullet_info.bullet_born_pos,
|
|
bullet_info.bullet_dir,
|
|
bullet_info.fly_distance,
|
|
bullet_info.weapon_uniid,
|
|
bullet_info.trace_target_uniid,
|
|
bullet_info.hand,
|
|
bullet_info.keep_shot_animi_timer_ptr);
|
|
#ifdef DEBUG1
|
|
if (bullet_info.c.Get()->IsPlayer()) {
|
|
bullet_info.c.Get()->SendDebugMsg(a8::Format("CreateBullet id:%d",
|
|
{bullet_info.weapon_meta->i->id()}));
|
|
}
|
|
#endif
|
|
}
|
|
bullet_uniid = bullet_uniid ? bullet_uniid : c->room->AllocUniid();
|
|
c->room->frame_event.AddBullet
|
|
(bullet_uniid,
|
|
c->GetWeakPtrRef(),
|
|
bullet_info.weapon_meta,
|
|
bullet_info.weapon_lv,
|
|
bullet_info.bullet_born_pos,
|
|
bullet_info.bullet_dir,
|
|
bullet_info.fly_distance,
|
|
bullet_info.trace_target_uniid,
|
|
bullet_info.hand);
|
|
if (bullet_uniid && bullet_info.trace_target_uniid) {
|
|
c->AddTraceBullet(
|
|
bullet_uniid,
|
|
bullet_info.trace_target_uniid,
|
|
bullet_info.weapon_meta->i->id()
|
|
);
|
|
}
|
|
} else {
|
|
BulletInfo* info_copy = new BulletInfo();
|
|
*info_copy = bullet_info;
|
|
xtimer_list* timer = bullet_info.c.Get()->room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
bullet_info.delay_time / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(info_copy),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
BulletInfo* info_copy = (BulletInfo*)param.sender.GetUserData();
|
|
info_copy->delay_time = 0;
|
|
InternalCreateBullet(*info_copy);
|
|
},
|
|
&bullet_info.c.Get()->xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
BulletInfo* info_copy = (BulletInfo*)param.sender.GetUserData();
|
|
delete info_copy;
|
|
}
|
|
);
|
|
if (!c->room->BattleStarted()) {
|
|
c->room->AddToPostBattleAutoFreeList(timer);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CalcGunMuzzlePosition(Creature* c,
|
|
MetaData::Equip* weapon_meta,
|
|
MetaData::HeroShotAnimation* shot_animi,
|
|
glm::vec4& gun_muzzle_position,
|
|
int bulletIdx,
|
|
int bulletNum)
|
|
{
|
|
if (shot_animi) {
|
|
if (weapon_meta->i->double_gun()&&
|
|
bulletIdx > (int)(bulletNum / 2)) {
|
|
gun_muzzle_position +=
|
|
glm::vec4(
|
|
shot_animi->l_x,
|
|
shot_animi->l_y,
|
|
shot_animi->l_z,
|
|
0
|
|
);
|
|
} else {
|
|
if (c->IsCar() && !weapon_meta->i->double_gun()) {
|
|
switch (c->shot_hole) {
|
|
case 1:
|
|
{
|
|
gun_muzzle_position +=
|
|
glm::vec4(
|
|
shot_animi->p3_x,
|
|
shot_animi->p3_y,
|
|
shot_animi->p3_z,
|
|
0
|
|
);
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
gun_muzzle_position +=
|
|
glm::vec4(
|
|
shot_animi->p4_x,
|
|
shot_animi->p4_y,
|
|
shot_animi->p4_z,
|
|
0
|
|
);
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
gun_muzzle_position +=
|
|
glm::vec4(
|
|
shot_animi->p5_x,
|
|
shot_animi->p5_y,
|
|
shot_animi->p5_z,
|
|
0
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
gun_muzzle_position +=
|
|
glm::vec4(
|
|
shot_animi->r_x,
|
|
shot_animi->r_y,
|
|
shot_animi->r_z,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
}//end if
|
|
if (weapon_meta->gun_muzzle_position) {
|
|
gun_muzzle_position +=
|
|
glm::vec4(
|
|
std::get<0>(*weapon_meta->gun_muzzle_position.get()),
|
|
std::get<1>(*weapon_meta->gun_muzzle_position.get()),
|
|
std::get<2>(*weapon_meta->gun_muzzle_position.get()),
|
|
0
|
|
);
|
|
}
|
|
if (weapon_meta->movex_position) {
|
|
gun_muzzle_position +=
|
|
glm::vec4(
|
|
std::get<0>(*weapon_meta->movex_position.get()),
|
|
std::get<1>(*weapon_meta->movex_position.get()),
|
|
std::get<2>(*weapon_meta->movex_position.get()),
|
|
0
|
|
);
|
|
}
|
|
}
|
|
|
|
void InternalShot(Creature* c,
|
|
MetaData::Equip* weapon_meta,
|
|
MetaData::Equip* bullet_meta,
|
|
MetaData::Skill* skill_meta,
|
|
float fly_distance,
|
|
long long weapon_uniid,
|
|
int trace_target_uniid)
|
|
{
|
|
if (weapon_meta->i->_inventory_slot() == IS_TRAP ||
|
|
weapon_meta->i->_inventory_slot() == IS_MINE) {
|
|
if (!skill_meta) {
|
|
c->room->frame_event.AddShot(c->GetWeakPtrRef());
|
|
}
|
|
if (weapon_meta->i->cast_time() > 0) {
|
|
int buff_uniid = c->TryAddBuff(c, kVertigoBuffId);
|
|
Buff* buff = c->GetBuffByUniId(buff_uniid);
|
|
if (buff && buff->remover_timer) {
|
|
c->room->xtimer.ModifyTimer(buff->remover_timer, weapon_meta->i->cast_time() / FRAME_RATE_MS);
|
|
}
|
|
}
|
|
a8::Vec2 old_context_dir = c->context_dir;
|
|
a8::Vec2 old_context_pos = c->context_pos;
|
|
c->context_dir =c->GetAttackDir();
|
|
c->context_pos = c->GetPos() + c->GetAttackDir() * fly_distance;
|
|
MetaData::Buff * buff_meta = MetaMgr::Instance()->GetBuff(bullet_meta->i->buffid());
|
|
if (buff_meta) {
|
|
c->AddBuff(c,
|
|
buff_meta
|
|
);
|
|
}
|
|
c->context_dir = old_context_dir;
|
|
c->context_pos = old_context_pos;
|
|
return;
|
|
}
|
|
for (auto& tuple : weapon_meta->bullet_born_offset) {
|
|
a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple));
|
|
float bullet_born_angle = c->GetAttackDir().CalcAngleEx(a8::Vec2::UP);
|
|
if (c->GetAttackDir().x > 0.00001f) {
|
|
bullet_born_angle = -bullet_born_angle;
|
|
}
|
|
bullet_born_offset.Rotate(bullet_born_angle);
|
|
a8::Vec2 bullet_born_pos = c->GetPos() + bullet_born_offset;
|
|
if (c->room->OverBorder(bullet_born_pos, 0.0f)) {
|
|
return;
|
|
}
|
|
}
|
|
if (!skill_meta || weapon_meta->i->equip_type() == EQUIP_TYPE_THROW) {
|
|
c->room->frame_event.AddShot(c->GetWeakPtrRef());
|
|
}
|
|
int invincible_buff_uniid = 0;
|
|
if (weapon_meta->lock_time > 0) {
|
|
int buff_uniid = c->TryAddBuff(c, kVertigoBuffId);
|
|
Buff* buff = c->GetBuffByUniId(buff_uniid);
|
|
if (buff && buff->remover_timer) {
|
|
c->room->xtimer.ModifyTimer(buff->remover_timer, weapon_meta->lock_time / FRAME_RATE_MS);
|
|
}
|
|
}
|
|
if (weapon_meta->i->cast_time() > 0) {
|
|
int buff_uniid = c->TryAddBuff(c, kVertigoBuffId);
|
|
Buff* buff = c->GetBuffByUniId(buff_uniid);
|
|
if (buff && buff->remover_timer) {
|
|
c->room->xtimer.ModifyTimer(buff->remover_timer, weapon_meta->i->cast_time() / FRAME_RATE_MS);
|
|
}
|
|
}
|
|
MetaData::HeroShotAnimation* shot_animi = c->GetHeroMeta() ?
|
|
c->GetHeroMeta()->GetShotAnimi(weapon_meta->i->shootfire()) : nullptr;
|
|
std::weak_ptr<a8::XTimerPtr> keep_shot_animi_timer_ptr;
|
|
{
|
|
int shot_animi_time = (shot_animi ? shot_animi->t : 0);
|
|
if (weapon_meta && weapon_meta->i->equip_subtype() == GUN_SUB_EQUIP_TYPE_FLY_HOOk) {
|
|
keep_shot_animi_timer_ptr = c->TryDelayAddBuff(c, kKeepShotAnimiBuffId, shot_animi_time);
|
|
}
|
|
}
|
|
int bulletIdx = 0;
|
|
int bulletNum = weapon_meta->bullet_born_offset.size();
|
|
for (auto& tuple : weapon_meta->bullet_born_offset) {
|
|
++bulletIdx;
|
|
a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple));
|
|
a8::Vec2 bullet_born_pos = c->GetPos() + c->shoot_offset + bullet_born_offset;
|
|
a8::Vec2 bullet_dir = c->GetAttackDir();
|
|
float bullet_angle = std::get<2>(tuple);
|
|
if (weapon_meta->i->bullet_angle() >= 0.10f) {
|
|
int angle = (int)weapon_meta->i->bullet_angle() * 1000;
|
|
if (angle > 0) {
|
|
bullet_angle += (rand() % angle) / 1000.0f * (rand() % 2 == 0 ? 1 : -1);
|
|
}
|
|
}
|
|
bullet_dir.Rotate(bullet_angle / 180.0f);
|
|
int shot_animi_time = (shot_animi ? shot_animi->t : 0);
|
|
{
|
|
bool is_player = c->IsPlayer();
|
|
bool is_car = c->IsCar();
|
|
float bullet_born_angle = c->GetAttackDir().CalcAngleEx(a8::Vec2::RIGHT);
|
|
if (c->GetAttackDir().y > 0.00001f) {
|
|
bullet_born_angle = -bullet_born_angle;
|
|
}
|
|
float old_bullet_born_angle = bullet_born_angle;
|
|
bullet_born_offset.Rotate(bullet_born_angle);
|
|
|
|
auto transform = glm::rotate(hero_transform,
|
|
bullet_born_angle * A8_PI,
|
|
glm::vec3(0.0, 1.0, 0.0));
|
|
glm::vec4 gun_muzzle_position(0.0, 0.0, 0.0, 0.0);
|
|
CalcGunMuzzlePosition(c, weapon_meta, shot_animi, gun_muzzle_position, bulletIdx, bulletNum);
|
|
glm::vec4 v = transform * gun_muzzle_position;
|
|
bullet_born_offset = a8::Vec2(v.z *10*1, v.x*10*-1);
|
|
bullet_born_pos = c->GetPos() + bullet_born_offset;
|
|
if (c->IsPlayer() || c->IsCar()) {
|
|
#ifdef DEBUG
|
|
a8::XPrintf("idx:%d offset:%f,%f angle:%f old_angle:%f angle_xy:%f,%f %f %f gun_muzzle_position:%f,%f,%f pos:%f,%f gun_id:%d t:%d\n",
|
|
{
|
|
bulletIdx,
|
|
bullet_born_offset.x,
|
|
bullet_born_offset.y,
|
|
bullet_born_angle,
|
|
old_bullet_born_angle,
|
|
c->GetAttackDir().x,
|
|
c->GetAttackDir().y,
|
|
(bullet_born_angle * 180),
|
|
(bullet_born_angle - glm::radians(90.0f) / A8_PI) * 180,
|
|
gun_muzzle_position.x,
|
|
gun_muzzle_position.y,
|
|
gun_muzzle_position.z,
|
|
bullet_born_pos.x,
|
|
bullet_born_pos.y,
|
|
weapon_meta->i->id(),
|
|
shot_animi_time
|
|
});
|
|
#endif
|
|
}
|
|
}
|
|
{
|
|
BulletInfo* bullet_info = new BulletInfo();
|
|
bullet_info->c = c->GetWeakPtrRef();
|
|
bullet_info->weapon_uniid = weapon_uniid;
|
|
bullet_info->weapon_meta = weapon_meta;
|
|
bullet_info->skill_meta = skill_meta;
|
|
bullet_info->bullet_meta = bullet_meta;
|
|
bullet_info->bullet_born_pos = bullet_born_pos;
|
|
bullet_info->bullet_dir = bullet_dir;
|
|
bullet_info->fly_distance = fly_distance;
|
|
bullet_info->delay_time = std::get<3>(tuple);
|
|
bullet_info->recoil_force = std::get<4>(tuple);
|
|
bullet_info->invincible_buff_uniid = invincible_buff_uniid;
|
|
bullet_info->trace_target_uniid = trace_target_uniid;
|
|
bullet_info->keep_shot_animi_timer_ptr = keep_shot_animi_timer_ptr;
|
|
if (skill_meta &&
|
|
(skill_meta->GetMagicId() == MAGIC_AXXF ||
|
|
skill_meta->GetMagicId() == MAGIC_HJHX)) {
|
|
bullet_info->trace_target_uniid = c->GetSkillTargetId();
|
|
}
|
|
if (weapon_meta->i->double_gun() &&
|
|
bulletIdx > (int)(bulletNum / 2)) {
|
|
bullet_info->hand = 1;
|
|
}
|
|
#ifdef DEBUG
|
|
if (bullet_info->trace_target_uniid) {
|
|
a8::XPrintf("bullet trace_target_uniid:%d\n", {bullet_info->trace_target_uniid});
|
|
}
|
|
#endif
|
|
#if 1
|
|
InternalCreateBullet(*bullet_info);
|
|
delete bullet_info;
|
|
bullet_info = nullptr;
|
|
#else
|
|
c->room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
shot_animi_time / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(bullet_info),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
BulletInfo* bullet_info = (BulletInfo*)param.sender.GetUserData();
|
|
InternalCreateBullet(*bullet_info);
|
|
},
|
|
&c->xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
BulletInfo* bullet_info = (BulletInfo*)param.sender.GetUserData();
|
|
delete bullet_info;
|
|
}
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
c->GetTrigger()->Shot(weapon_meta);
|
|
if (weapon_meta->i->recoil_force() > 0.000001) {
|
|
c->DoRecoilForce(weapon_meta->i->recoil_force());
|
|
}
|
|
if (c->HasBuffEffect(kBET_Hide)) {
|
|
c->RemoveBuffByEffectId(kBET_Hide);
|
|
}
|
|
if (trace_target_uniid) {
|
|
c->LockAttackDir(1000);
|
|
}
|
|
if (c->aiming) {
|
|
c->aiming = false;
|
|
c->aiming_frameno = 0;
|
|
c->power_idx = -1;
|
|
c->ClearAimingBuffs();
|
|
}
|
|
}
|
|
|
|
Creature::Creature():MoveableEntity()
|
|
{
|
|
weak_ptr_chunk_.Set(this);
|
|
trigger_ = new Trigger(this);
|
|
trigger_->Init();
|
|
ability_ = std::make_shared<Ability>();
|
|
ability_->owner = GetWeakPtrRef();
|
|
weapons.reserve(MAX_WEAPON_NUM);
|
|
for (size_t i = 0; i < MAX_WEAPON_NUM; ++i) {
|
|
auto& weapon = a8::FastAppend(weapons);
|
|
weapon.weapon_idx = i;
|
|
weapon.weapon_id = 0;
|
|
weapon.ammo = 0;
|
|
}
|
|
|
|
for (size_t i = 0; i < inventory_.size(); ++i) {
|
|
inventory_[i].slot = i;
|
|
inventory_[i].itemid = 0;
|
|
inventory_[i].num = 0;
|
|
}
|
|
|
|
inventory_[IS_1XSCOPE].num = 1;
|
|
}
|
|
|
|
Creature::~Creature()
|
|
{
|
|
xtimer_attacher.ClearTimerList();
|
|
ClearBuffList();
|
|
for (auto& pair : skill_hash_) {
|
|
delete pair.second;
|
|
}
|
|
skill_hash_.clear();
|
|
ClearPassiveSkill();
|
|
trigger_->UnInit();
|
|
A8_SAFE_DELETE(trigger_);
|
|
room->grid_service->RemoveCreature(this);
|
|
}
|
|
|
|
bool Creature::HasBuffEffect(int buff_effect_id)
|
|
{
|
|
return GetBuffByEffectId(buff_effect_id) != nullptr;
|
|
}
|
|
|
|
Buff* Creature::GetBuffByEffectId(int effect_id)
|
|
{
|
|
if (!IsValidBuffEffect(effect_id)) {
|
|
return nullptr;
|
|
}
|
|
list_head* list = &buff_effect_[effect_id];
|
|
if (list_empty(list)) {
|
|
return nullptr;
|
|
} else {
|
|
Buff* buff = list_first_entry(list, Buff, effect_entry);
|
|
return buff;
|
|
}
|
|
}
|
|
|
|
Buff* Creature::GetBuffById(int buff_id)
|
|
{
|
|
for (Buff& buff : buff_list_) {
|
|
if (buff.meta->i->buff_id() == buff_id) {
|
|
return &buff;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Buff* Creature::GetBuffByUniId(int buff_uniid)
|
|
{
|
|
for (Buff& buff : buff_list_) {
|
|
if (buff.buff_uniid == buff_uniid) {
|
|
return &buff;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int Creature::GetBuffNum(int buff_id)
|
|
{
|
|
int num = 0;
|
|
for (Buff& buff : buff_list_) {
|
|
if (buff.meta->i->buff_id() == buff_id) {
|
|
++num;
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
|
|
int Creature::AddBuff(Creature* caster,
|
|
MetaData::Buff* buff_meta,
|
|
MetaData::Skill* buff_skill_meta,
|
|
bool no_check_immune)
|
|
{
|
|
if (buff_meta->only_spec_race) {
|
|
if (!a8::HasBitFlag(buff_meta->only_spec_race, GetEntityType())) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (buff_meta->exclude_spec_race) {
|
|
if (a8::HasBitFlag(buff_meta->exclude_spec_race, GetEntityType())) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (buff_meta->i->buff_interval() > 0) {
|
|
if (buff_interval_hash_.find(buff_meta->i->buff_id()) != buff_interval_hash_.end()) {
|
|
return 0;
|
|
}
|
|
buff_interval_hash_[buff_meta->i->buff_id()] = room->GetFrameNo();
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
buff_meta->i->buff_interval() / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(buff_meta->i->buff_id()),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
c->buff_interval_hash_.erase(param.param1);
|
|
},
|
|
&xtimer_attacher.timer_list_);
|
|
}
|
|
if (buff_meta->i->coexist_num() > 0){
|
|
if (GetBuffNum(buff_meta->i->buff_id()) >= buff_meta->i->coexist_num()) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (buff_meta->i->no_immune()) {
|
|
no_check_immune = 1;
|
|
}
|
|
if (!no_check_immune && IsImmuneBuffEffect(buff_meta->i->buff_effect())) {
|
|
return 0;
|
|
}
|
|
if (buff_meta->i->depend_effect() != 0 && !HasBuffEffect(buff_meta->i->depend_effect())) {
|
|
return 0;
|
|
}
|
|
if (!buff_meta->EffectCanStack()) {
|
|
Buff* buff = GetBuffByEffectId(buff_meta->i->buff_effect());
|
|
if (buff) {
|
|
RemoveBuffById(buff->meta->i->buff_id());
|
|
}
|
|
}
|
|
if (buff_meta->i->buff_effect() == kBET_OnceChgAttr) {
|
|
ProcOnceChgAttrBuff(buff_meta);
|
|
}
|
|
++buff_uniid_;
|
|
int new_buff_uniid = buff_uniid_;
|
|
Buff* buff = &a8::FastAppend(buff_list_);
|
|
buff->buff_uniid = new_buff_uniid;
|
|
buff->SetCaster(caster);
|
|
buff->owner = this;
|
|
buff->meta = buff_meta;
|
|
buff->skill_meta = buff_skill_meta;
|
|
buff->add_frameno = room->GetFrameNo();
|
|
buff->xtimer_attacher.xtimer = &room->xtimer;
|
|
buff->Init();
|
|
if (buff->meta->i->lock_move()) {
|
|
IncDisableMoveTimes();
|
|
}
|
|
list_add_tail(&buff->effect_entry, &buff_effect_[buff->meta->i->buff_effect()]);
|
|
if (buff->meta->i->depend_effect() != 0 &&
|
|
IsValidBuffEffect(buff->meta->i->depend_effect())) {
|
|
list_add_tail(&buff->depend_entry, &depend_effect_[buff->meta->i->depend_effect()]);
|
|
}
|
|
{
|
|
buff->remover_timer = room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
buff_meta->i->duration_time() * SERVER_FRAME_RATE,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(buff->buff_uniid)
|
|
.SetParam2(buff),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
c->RemoveBuffByUniId(param.param1);
|
|
},
|
|
&buff->xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Buff* buff = (Buff*)param.param2.GetUserData();
|
|
if (buff->remover_timer) {
|
|
buff->remover_timer = nullptr;
|
|
}
|
|
});
|
|
}
|
|
GetTrigger()->ActiveBuff(buff->meta);
|
|
ProcBuffEffect(caster, buff);
|
|
AddBuffPostProc(caster, buff);
|
|
if (!buff->meta->i->only_server()) {
|
|
room->frame_event.AddBuff(GetWeakPtrRef(), buff);
|
|
}
|
|
#ifdef DEBUG
|
|
{
|
|
SendDebugMsg(a8::Format("添加buff_id:%d effect:%d %s params:%d,%d,%d,%d,%d uniid:%d dur:%d",
|
|
{
|
|
buff_meta->i->buff_id(),
|
|
buff_meta->i->buff_effect(),
|
|
buff_meta->i->name(),
|
|
buff_meta->int_param1,
|
|
buff_meta->int_param2,
|
|
buff_meta->int_param3,
|
|
buff_meta->int_param4,
|
|
buff_meta->int_param5,
|
|
buff->buff_uniid,
|
|
room->xtimer.GetRemainTime(buff->remover_timer)
|
|
}));
|
|
}
|
|
#endif
|
|
if (!buff->meta->child_buff_list.empty()) {
|
|
for (int child_buff_id : buff->meta->child_buff_list) {
|
|
MetaData::Buff* child_buff_meta = MetaMgr::Instance()->GetBuff(child_buff_id);
|
|
if (child_buff_meta) {
|
|
AddBuff(caster, child_buff_meta, buff_skill_meta, true);
|
|
}
|
|
}
|
|
}
|
|
return new_buff_uniid;
|
|
}
|
|
|
|
bool Creature::IsImmuneBuffEffect(int buff_effect)
|
|
{
|
|
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
|
|
if (itr->meta->IsImmuneBuffEffect(buff_effect)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int Creature::MustBeAddBuff(Creature* caster, int buff_id)
|
|
{
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
|
|
if (!buff_meta) {
|
|
A8_ABORT();
|
|
}
|
|
return AddBuff(caster, buff_meta);
|
|
}
|
|
|
|
int Creature::TryAddBuff(Creature* caster, int buff_id)
|
|
{
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
|
|
if (buff_meta) {
|
|
return AddBuff(caster, buff_meta);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
std::weak_ptr<a8::XTimerPtr> Creature::TryDelayAddBuff(Creature* caster, int buff_id, int time)
|
|
{
|
|
xtimer_list* timer = room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
time / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(caster->GetUniId())
|
|
.SetParam2(buff_id),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
Entity* e = c->room->GetEntityByUniId(param.param1);
|
|
if (e->IsCreature(c->room) && !c->IsDead(c->room)) {
|
|
c->TryAddBuff((Creature*)e, param.param2);
|
|
}
|
|
},
|
|
&xtimer_attacher.timer_list_
|
|
);
|
|
return room->xtimer.GetTimerPtr(timer);
|
|
}
|
|
|
|
int Creature::TryAddBuffWithTarget(Creature* caster, int buff_id)
|
|
{
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
|
|
if (buff_meta) {
|
|
bool be_add = false;
|
|
switch (buff_meta->i->buff_target()) {
|
|
case kBuffTargetSelf:
|
|
{
|
|
be_add = caster == this;
|
|
}
|
|
break;
|
|
case kBuffTargetFriendly:
|
|
{
|
|
team_id == caster->team_id;
|
|
}
|
|
break;
|
|
case kBuffTargetEnemy:
|
|
{
|
|
team_id != caster->team_id;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
return TryAddBuff(caster, buff_id);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Creature::RemoveBuffById(int buff_id)
|
|
{
|
|
int buff_uniid = 0;
|
|
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
|
|
Buff& buff = *itr;
|
|
if (buff.meta->i->buff_id() == buff_id) {
|
|
buff_uniid = buff.buff_uniid;
|
|
break;
|
|
}
|
|
}
|
|
if (buff_uniid != 0) {
|
|
RemoveBuffByUniId(buff_uniid);
|
|
}
|
|
}
|
|
|
|
void Creature::RemoveBuffByUniId(int buff_uniid)
|
|
{
|
|
int buff_id = 0;
|
|
std::vector<std::tuple<MetaData::Buff*, Creature*>> removed_buffs;
|
|
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
|
|
Buff& buff = *itr;
|
|
if (buff.buff_uniid == buff_uniid) {
|
|
buff_id = buff.meta->i->buff_id();
|
|
removed_buffs.push_back(std::make_tuple(buff.meta, buff.GetCaster().Get()));
|
|
OnBuffRemove(buff);
|
|
buff.UnInit();
|
|
buff_list_.erase(itr);
|
|
break;
|
|
}
|
|
}
|
|
for (auto& tuple1 : removed_buffs) {
|
|
MetaData::Buff* buff_meta = std::get<0>(tuple1);
|
|
Creature* caster = std::get<1>(tuple1);
|
|
|
|
for (int child_buff_id : buff_meta->child_buff_list) {
|
|
RemoveBuffById(child_buff_id);
|
|
}
|
|
|
|
if (!HasBuffEffect(buff_meta->i->buff_effect()) &&
|
|
!list_empty(&depend_effect_[buff_meta->i->buff_effect()])) {
|
|
struct list_head work_list;
|
|
list_replace_init(&depend_effect_[buff_meta->i->buff_effect()], &work_list);
|
|
while (!list_empty(&work_list)) {
|
|
Buff* buff = list_first_entry(&work_list, Buff, depend_entry);
|
|
RemoveBuffById(buff->meta->i->buff_id());
|
|
}
|
|
}
|
|
GetTrigger()->DeactiveBuff(buff_meta);
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
SendDebugMsg(a8::Format("移除buff_id:%d uniid:%d",
|
|
{
|
|
buff_id,
|
|
buff_uniid
|
|
}));
|
|
}
|
|
#endif
|
|
|
|
for (const auto& tuple : buff_meta->post_remove_action) {
|
|
switch (std::get<0>(tuple)) {
|
|
case kRemoveBuffByIdAction:
|
|
{
|
|
for (int buff_id :std::get<1>(tuple)) {
|
|
RemoveBuffById(buff_id);
|
|
}
|
|
}
|
|
break;
|
|
case kRemoveBuffByEffectAction:
|
|
{
|
|
for (int buff_effect :std::get<1>(tuple)) {
|
|
RemoveBuffByEffectId(buff_effect);
|
|
}
|
|
}
|
|
break;
|
|
case kAddBuffAction:
|
|
{
|
|
for (int buff_id :std::get<1>(tuple)) {
|
|
TryAddBuff(caster, buff_id);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
RecalcBuffAttr();
|
|
}
|
|
|
|
void Creature::ClearBuffById(int buff_id)
|
|
{
|
|
while (GetBuffById(buff_id)) {
|
|
RemoveBuffById(buff_id);
|
|
}
|
|
}
|
|
|
|
void Creature::SendDebugMsg(const std::string& debug_msg)
|
|
{
|
|
|
|
}
|
|
|
|
void Creature::AddBuffPostProc(Creature* caster, Buff* buff)
|
|
{
|
|
|
|
}
|
|
|
|
void Creature::RecalcBuffAttr()
|
|
{
|
|
CheckAbilityUsed();
|
|
ability_->Clear();
|
|
bool need_refresh_hp = false;
|
|
for (auto& buff : buff_list_) {
|
|
if (buff.meta->i->buff_effect() == kBET_ChgAttr ||
|
|
buff.meta->i->buff_effect() == kBET_Car ||
|
|
buff.meta->i->buff_effect() == kBET_CrazyMode) {
|
|
int attr_type = (int)buff.meta->param1;
|
|
int calc_type = (int)buff.meta->param2;
|
|
if (IsValidHumanAttr(attr_type)) {
|
|
if (calc_type == 1) {
|
|
float* p = ability_->GetBuffAttrAbsPtr(attr_type);
|
|
if (p) {
|
|
*p += buff.meta->param3;
|
|
}
|
|
} else if (calc_type == 2) {
|
|
float* p = ability_->GetBuffAttrRatePtr(attr_type);
|
|
if (p) {
|
|
*p += buff.meta->param3;
|
|
}
|
|
}
|
|
}
|
|
if (buff.add_frameno == room->GetFrameNo() &&
|
|
attr_type == kHAT_MaxHp) {
|
|
need_refresh_hp = true;
|
|
}
|
|
}
|
|
}
|
|
for (auto& tuple : talent_list) {
|
|
auto talent_meta = MetaMgr::Instance()->GetTalent(std::get<0>(tuple), std::get<1>(tuple));
|
|
if (talent_meta) {
|
|
for (auto& tuple1 : talent_meta->addattr) {
|
|
int attr_type = std::get<0>(tuple1);
|
|
int attr_val = std::get<1>(tuple1);
|
|
if (talent_meta->i->addtype() == 0) {
|
|
float* p = ability_->GetBuffAttrAbsPtr(attr_type);
|
|
if (p) {
|
|
*p += attr_val;
|
|
}
|
|
} else if (talent_meta->i->addtype() == 1) {
|
|
float* p = ability_->GetBuffAttrRatePtr(attr_type);
|
|
if (p) {
|
|
*p += attr_val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (need_refresh_hp) {
|
|
SetHP(GetMaxHP());
|
|
GetTrigger()->HpChg();
|
|
}
|
|
if (IsHuman()) {
|
|
need_sync_active_player = true;
|
|
}
|
|
}
|
|
|
|
void Creature::OnBuffRemove(Buff& buff)
|
|
{
|
|
if (buff.meta->i->lock_move()) {
|
|
DecDisableMoveTimes();
|
|
}
|
|
switch (buff.meta->i->buff_effect()) {
|
|
case kBET_Sprint:
|
|
{
|
|
if (buff.meta->int_param5) {
|
|
DecDisableMoveDirTimes();
|
|
}
|
|
}
|
|
break;
|
|
case kBET_HoldShield:
|
|
{
|
|
buff.ProcRemoveHoldShield();
|
|
}
|
|
break;
|
|
case kBET_BePull:
|
|
{
|
|
int i = 0;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
if (!buff.meta->i->only_server()) {
|
|
room->frame_event.RemoveBuff(GetWeakPtrRef(), &buff);
|
|
}
|
|
}
|
|
|
|
void Creature::RemoveBuffByEffectId(int buff_effect_id)
|
|
{
|
|
Buff* buff = GetBuffByEffectId(buff_effect_id);
|
|
while (buff) {
|
|
RemoveBuffById(buff->meta->i->buff_id());
|
|
buff = GetBuffByEffectId(buff_effect_id);
|
|
}
|
|
}
|
|
|
|
void Creature::ClearBuffList()
|
|
{
|
|
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
|
|
list_del_init(&itr->effect_entry);
|
|
OnBuffRemove(*itr);
|
|
}
|
|
buff_list_.clear();
|
|
buff_effect_ = {};
|
|
RecalcBuffAttr();
|
|
}
|
|
|
|
void Creature::FillBuffList(Human* hum, ::google::protobuf::RepeatedPtrField<::cs::MFBuff>* pb_buff_list)
|
|
{
|
|
for (auto& itr : buff_list_) {
|
|
if (itr.NeedSync(hum)) {
|
|
auto buff = pb_buff_list->Add();
|
|
itr.FillMFBuff(buff);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Creature::FillSkillList(::google::protobuf::RepeatedPtrField< cs::MFSkill >*
|
|
pb_skill_list)
|
|
{
|
|
for (auto& pair : skill_hash_) {
|
|
auto skill = pb_skill_list->Add();
|
|
pair.second->FillMFSkill(skill);
|
|
}
|
|
}
|
|
|
|
void Creature::AddPassiveSkill(int skill_id)
|
|
{
|
|
MetaData::Skill* skill_meta = MetaMgr::Instance()->GetSkill(skill_id);
|
|
if (skill_meta && !GetPassiveSkill(skill_meta->i->skill_id())) {
|
|
Skill* skill = new Skill;
|
|
skill->owner = this;
|
|
skill->meta = skill_meta;
|
|
skill->xtimer_attacher.xtimer = &room->xtimer;
|
|
room->xtimer.AddRepeatTimerAndAttach
|
|
(
|
|
SERVER_FRAME_RATE * skill_meta->i->skill_cd(),
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(skill_id),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
Skill* skill = c->GetPassiveSkill(param.param1);
|
|
if (skill) {
|
|
}
|
|
},
|
|
&skill->xtimer_attacher.timer_list_);
|
|
skill->Initialzie();
|
|
passive_skill_hash_[skill_meta->i->skill_id()] = skill;
|
|
}
|
|
}
|
|
|
|
void Creature::RemovePassiveSkill(int skill_id)
|
|
{
|
|
auto itr = passive_skill_hash_.find(skill_id);
|
|
if (itr != passive_skill_hash_.end()) {
|
|
delete itr->second;
|
|
passive_skill_hash_.erase(itr);
|
|
}
|
|
}
|
|
|
|
void Creature::ClearPassiveSkill()
|
|
{
|
|
std::vector<int> del_skills;
|
|
del_skills.reserve(passive_skill_hash_.size());
|
|
for (auto& pair : passive_skill_hash_) {
|
|
del_skills.push_back(pair.first);
|
|
}
|
|
for (int skill_id : del_skills) {
|
|
RemovePassiveSkill(skill_id);
|
|
}
|
|
}
|
|
|
|
Skill* Creature::GetPassiveSkill(int skill_id)
|
|
{
|
|
auto itr = passive_skill_hash_.find(skill_id);
|
|
return itr != passive_skill_hash_.end() ? itr->second : nullptr;
|
|
}
|
|
|
|
void Creature::TriggerBuff(Skill* skill, std::set<Creature*>& target_list, BuffTriggerType_e trigger_type)
|
|
{
|
|
for (Creature* entity : target_list) {
|
|
TriggerOneObjectBuff(skill, entity, trigger_type);
|
|
}
|
|
if (trigger_type == kBTT_UseSkill && !dead) {
|
|
auto itr = skill->meta->trigger_type_buffs.find(trigger_type);
|
|
if (itr != skill->meta->trigger_type_buffs.end()) {
|
|
for (MetaData::Buff* buff_meta : itr->second) {
|
|
switch (buff_meta->i->buff_target()) {
|
|
case kBuffTargetSelf: //自己
|
|
{
|
|
AddBuff(this, buff_meta, skill->meta);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Creature::TriggerOneObjectBuff(Skill* skill, Creature* target, BuffTriggerType_e trigger_type)
|
|
{
|
|
if (target->dead) {
|
|
return;
|
|
}
|
|
auto itr = skill->meta->trigger_type_buffs.find(trigger_type);
|
|
if (itr != skill->meta->trigger_type_buffs.end()) {
|
|
for (MetaData::Buff* buff_meta : itr->second) {
|
|
switch (buff_meta->i->buff_target()) {
|
|
case kBuffTargetSelf: //自己
|
|
{
|
|
if (target == this && trigger_type != kBTT_UseSkill) {
|
|
target->AddBuff(this, buff_meta, skill->meta);
|
|
}
|
|
}
|
|
break;
|
|
case kBuffTargetFriendly: //友军
|
|
{
|
|
if (target->team_id == team_id) {
|
|
target->AddBuff(this, buff_meta, skill->meta);
|
|
}
|
|
}
|
|
break;
|
|
case kBuffTargetEnemy: //敌军
|
|
{
|
|
if (target->team_id != team_id) {
|
|
target->AddBuff(this, buff_meta, skill->meta);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Skill* Creature::GetSkill(int skill_id)
|
|
{
|
|
auto itr = skill_hash_.find(skill_id);
|
|
return itr != skill_hash_.end() ? itr->second : nullptr;
|
|
}
|
|
|
|
bool Creature::CanUseSkill(int skill_id)
|
|
{
|
|
Skill* skill = GetSkill(skill_id);
|
|
if (!skill) {
|
|
return false;
|
|
}
|
|
if (room->GetGasData().GetGasMode() == GasInactive &&
|
|
!MetaMgr::Instance()->prebattle_can_use_skill) {
|
|
return false;
|
|
}
|
|
if (HasBuffEffect(kBET_Vertigo) ||
|
|
HasBuffEffect(kBET_Jump) ||
|
|
HasBuffEffect(kBET_Fly) ||
|
|
HasBuffEffect(kBET_MachineGun) ||
|
|
HasBuffEffect(kBET_Sprint)) {
|
|
return false;
|
|
}
|
|
if (skill->force_use_times > 0) {
|
|
--skill->force_use_times;
|
|
return true;
|
|
}
|
|
if (dead) {
|
|
return false;
|
|
}
|
|
if (playing_skill) {
|
|
return false;
|
|
}
|
|
if (IsHuman() && AsHuman()->downed) {
|
|
return false;
|
|
}
|
|
return skill->GetCurrTimes() > 0 || skill->GetMinorType() != SMT_NONE;
|
|
}
|
|
|
|
void Creature::DoSkill(int skill_id,
|
|
int target_id,
|
|
const a8::Vec2& skill_dir,
|
|
float skill_distance,
|
|
const a8::Vec2& target_pos)
|
|
{
|
|
Skill* skill = GetSkill(skill_id);
|
|
if (skill && CanUseSkill(skill_id)) {
|
|
if (skill->GetMinorType()) {
|
|
skill->DoMinorMode();
|
|
return;
|
|
}
|
|
if (skill->meta->i->attack_dir_lock_time()) {
|
|
SetAttackDir(skill_dir);
|
|
IncDisableAttackDirTimes();
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
skill->meta->i->attack_dir_lock_time() / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
c->DecDisableAttackDirTimes();
|
|
},
|
|
&xtimer_attacher.timer_list_);
|
|
}
|
|
DoSkillPreProc(skill_id, target_id, target_pos);
|
|
ResetSkill();
|
|
skill_target_id_ = target_id;
|
|
skill_target_pos_ = target_pos;
|
|
skill_dir_ = skill_dir;
|
|
skill_distance_ = skill_distance;
|
|
curr_skill_ = skill;
|
|
playing_skill = true;
|
|
context_dir = skill_dir_;
|
|
context_pos = GetPos() + skill_dir_ * skill_distance_;
|
|
CurrentSkill()->last_use_frameno = room->GetFrameNo();
|
|
skill->LockCastPhase();
|
|
if (CurrentSkill()->meta->i->skill_target() == kST_Self
|
|
) {
|
|
skill_target_id_ = GetUniId();
|
|
}
|
|
if (CurrentSkill()->meta->i->skill_target() == kST_SpecDir) {
|
|
std::set<Creature*> target_list;
|
|
SelectSkillTargets(CurrentSkill(), GetPos(), target_list);
|
|
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
|
|
if (!CurrentSkill()->meta->phases.empty() &&
|
|
CurrentSkill()->meta->phases[0].time_offset <= 0) {
|
|
UpdateSkill();
|
|
}
|
|
} else {
|
|
if (skill_target_id_ == 0) {
|
|
if (CurrentSkill()->meta->i->skill_target() == kST_FriendlyIncludeSelf ||
|
|
CurrentSkill()->meta->i->skill_target() == kST_FriendlyExcludeSelf) {
|
|
skill_target_id_ = GetUniId();
|
|
}
|
|
}
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id_);
|
|
if (entity && entity->IsCreature(room)) {
|
|
Creature* c = (Creature*)entity;
|
|
std::set<Creature*> target_list;
|
|
skill_target_pos_ = c->GetPos();
|
|
SelectSkillTargets(CurrentSkill(), c->GetPos(), target_list);
|
|
if (!CurrentSkill()->meta->phases.empty() &&
|
|
CurrentSkill()->meta->phases[0].time_offset <= 0) {
|
|
if (CurrentSkill()->meta->phases[0].func_id == kSkill_HoldShield) {
|
|
if (HasBuffEffect(kBET_HoldShield)) {
|
|
RemoveBuffByEffectId(kBET_HoldShield);
|
|
playing_skill = false;
|
|
} else {
|
|
UpdateSkill();
|
|
}
|
|
} else {
|
|
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
|
|
UpdateSkill();
|
|
}
|
|
} else {
|
|
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
|
|
}
|
|
} else {
|
|
std::set<Creature*> target_list;
|
|
SelectSkillTargets(CurrentSkill(), GetPos(), target_list);
|
|
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
|
|
if (!CurrentSkill()->meta->phases.empty() &&
|
|
CurrentSkill()->meta->phases[0].func_id == kSkill_Shot) {
|
|
playing_skill = true;
|
|
} else {
|
|
playing_skill = false;
|
|
}
|
|
}
|
|
}
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
GetTrigger()->UseSkill(skill);
|
|
DoSkillPostProc(true, skill_id, target_id, target_pos);
|
|
if (IsHuman()) {
|
|
++AsHuman()->stats.use_skill_times;
|
|
}
|
|
#if 0
|
|
if (skill_id != TURN_OVER_SKILL_ID) {
|
|
#else
|
|
{
|
|
#endif
|
|
CreatureWeakPtr ptr;
|
|
ptr.Attach(this);
|
|
room->frame_event.AddPlaySkill(ptr, skill_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Creature::DoSkillPreProc(int skill_id, int target_id, const a8::Vec2& target_pos)
|
|
{
|
|
}
|
|
|
|
void Creature::DoSkillPostProc(bool used, int skill_id, int target_id, const a8::Vec2& target_pos)
|
|
{
|
|
}
|
|
|
|
void Creature::ResetSkill()
|
|
{
|
|
curr_skill_ = nullptr;
|
|
curr_skill_phase = 0;
|
|
skill_dir_ = a8::Vec2();
|
|
skill_target_pos_ = a8::Vec2();
|
|
skill_param1 = 0.0f;
|
|
playing_skill = false;
|
|
}
|
|
|
|
void Creature::UpdateSkill()
|
|
{
|
|
if (CurrentSkill() && !dead) {
|
|
if (curr_skill_phase < CurrentSkill()->meta->phases.size()) {
|
|
MetaData::SkillPhase* phase = &CurrentSkill()->meta->phases[curr_skill_phase];
|
|
if (phase->time_offset <= CurrentSkill()->GetPassedTime()) {
|
|
ProcSkillPhase(phase);
|
|
++curr_skill_phase;
|
|
}
|
|
} else {
|
|
playing_skill = false;
|
|
}
|
|
} else {
|
|
playing_skill = false;
|
|
}
|
|
}
|
|
|
|
void Creature::ProcSkillPhase(MetaData::SkillPhase* phase)
|
|
{
|
|
switch (phase->func_id) {
|
|
case kSkill_TurnOver:
|
|
{
|
|
}
|
|
break;
|
|
case kSkill_JumpTo:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case kSkill_Shot:
|
|
{
|
|
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(phase->param1.GetInt());
|
|
if (weapon_meta) {
|
|
MetaData::Equip* bullet_meta =
|
|
MetaMgr::Instance()->GetEquip(weapon_meta->i->use_bullet());
|
|
if (CurrentSkill()->meta->i->skill_target() == kST_SpecDir) {
|
|
if (std::abs(skill_dir_.x) > FLT_EPSILON ||
|
|
std::abs(skill_dir_.y) > FLT_EPSILON) {
|
|
float target_distance = 5;
|
|
if (weapon_meta->i->equip_type() == EQUIP_TYPE_THROW &&
|
|
weapon_meta->i->equip_subtype() == THROW_EQUIP_TYPE_ADD_BUFF) {
|
|
target_distance = skill_distance_;
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
(target_distance / weapon_meta->i->bullet_speed() + 0.3) * 1000.0f / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(context_pos.x)
|
|
.SetParam2(context_pos.y)
|
|
.SetParam3(weapon_meta),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
MetaData::Equip* equip_meta = (MetaData::Equip*)param.param3.GetUserData();
|
|
|
|
a8::Vec2 context_pos;
|
|
context_pos.x = param.param1.GetDouble();
|
|
context_pos.y = param.param2.GetDouble();
|
|
a8::Vec2 old_context_pos = c->context_pos;
|
|
c->context_pos = context_pos;
|
|
c->TryAddBuff(c, equip_meta->int_param1);
|
|
c->context_pos = old_context_pos;
|
|
},
|
|
&xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
|
|
});
|
|
}
|
|
if (bullet_meta && target_distance > 0.00001f) {
|
|
a8::Vec2 old_attack_dir = GetAttackDir();
|
|
a8::Vec2 attack_dir = skill_dir_;
|
|
attack_dir.Normalize();
|
|
SetAttackDir(attack_dir);
|
|
InternalShot
|
|
(
|
|
this,
|
|
weapon_meta,
|
|
bullet_meta,
|
|
CurrentSkill() ? CurrentSkill()->meta : nullptr,
|
|
target_distance,
|
|
0,
|
|
0);
|
|
SetAttackDir(old_attack_dir);
|
|
}
|
|
}
|
|
} else {
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id_);
|
|
if (entity) {
|
|
float target_distance = entity->GetPos().Distance(GetPos());
|
|
if (bullet_meta && target_distance > 0.00001f) {
|
|
a8::Vec2 old_attack_dir = GetAttackDir();
|
|
a8::Vec2 attack_dir = entity->GetPos() - GetPos();
|
|
attack_dir.Normalize();
|
|
SetAttackDir(attack_dir);
|
|
InternalShot
|
|
(
|
|
this,
|
|
weapon_meta,
|
|
bullet_meta,
|
|
CurrentSkill() ? CurrentSkill()->meta : nullptr,
|
|
target_distance,
|
|
0,
|
|
0);
|
|
SetAttackDir(old_attack_dir);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kSkill_Pull:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case kSkill_HoldShield:
|
|
{
|
|
}
|
|
break;
|
|
case kSkill_ForthBackJump:
|
|
{
|
|
a8::Vec2 old_dir = GetMoveDir();
|
|
a8::Vec2 old_pos = GetPos();
|
|
if (CurrentSkill()->GetMinorType() == SMT_NONE) {
|
|
int buff_id = phase->param3.GetInt();
|
|
TryAddBuff(this, buff_id);
|
|
#ifdef DEBUG
|
|
a8::XPrintf("old_pos:%f,%f", {GetPos().x, GetPos().y});
|
|
#endif
|
|
#if 1
|
|
_UpdateMove(SkillHelper::GetSjydhxForthDistance(CurrentSkill()->meta));
|
|
#else
|
|
_UpdateMove(phase->param1.GetInt());
|
|
#endif
|
|
float recover_hp = SkillHelper::GetSjydhxRecoverHp(this, CurrentSkill()->meta);
|
|
AddHp(recover_hp);
|
|
a8::Vec2 pre_dir = old_dir;
|
|
a8::Vec2 pre_pos = old_pos;
|
|
#ifdef DEBUG
|
|
a8::XPrintf("new_pos:%f,%f", {GetPos().x, GetPos().y});
|
|
#endif
|
|
CurrentSkill()->AddMinorMode
|
|
(
|
|
SMT_BLINK,
|
|
#if 1
|
|
SkillHelper::GetSjydhxForthDistance(CurrentSkill()->meta) * 1000,
|
|
#else
|
|
phase->param2.GetInt() * 1000,
|
|
#endif
|
|
[this, pre_pos, pre_dir, buff_id] () {
|
|
a8::Vec2 old_dir = GetMoveDir();
|
|
a8::Vec2 old_pos = GetPos();
|
|
float distance = GetPos().Distance(pre_pos);
|
|
if (distance > 0.0001f) {
|
|
a8::Vec2 new_dir = pre_pos - GetPos();
|
|
new_dir.Normalize();
|
|
SetMoveDir(new_dir);
|
|
TryAddBuff(this, buff_id);
|
|
_UpdateMove(distance);
|
|
}
|
|
SetMoveDir(old_dir);
|
|
}
|
|
);
|
|
}
|
|
SetMoveDir(old_dir);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
MetaData::SkillPhase* Creature::GetCurrSkillPhase()
|
|
{
|
|
return curr_skill_phase < CurrentSkill()->meta->phases.size() ?
|
|
&CurrentSkill()->meta->phases[curr_skill_phase] : nullptr;
|
|
}
|
|
|
|
Skill* Creature::CurrentSkill()
|
|
{
|
|
return curr_skill_;
|
|
}
|
|
|
|
void Creature::ActiveAllSkill()
|
|
{
|
|
for (auto& pair : skill_hash_) {
|
|
pair.second->Accelerate(-10000000);
|
|
}
|
|
}
|
|
|
|
void Creature::ProcBuffEffect(Creature* caster, Buff* buff)
|
|
{
|
|
#ifdef DEBUG
|
|
if (IsPlayer()) {
|
|
if (buff->meta->i->buff_effect() == kBET_Vertigo) {
|
|
int i = 0;
|
|
}
|
|
}
|
|
#endif
|
|
MetaData::Buff* buff_meta = buff->meta;
|
|
switch (buff->meta->i->buff_effect()) {
|
|
case kBET_ChgAttr:
|
|
case kBET_Car:
|
|
case kBET_CrazyMode:
|
|
{
|
|
RecalcBuffAttr();
|
|
if (buff->meta->i->buff_effect() == kBET_Car) {
|
|
RecalcSelfCollider();
|
|
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(buff->meta->param4);
|
|
if (equip_meta &&
|
|
equip_meta->i->equip_type() == EQUIP_TYPE_CAR &&
|
|
equip_meta->i->equip_subtype() == 1
|
|
) {
|
|
MetaData::Equip* spec_weapon_meta = MetaMgr::Instance()->GetEquip(equip_meta->int_param1);
|
|
if (spec_weapon_meta) {
|
|
second_weapon.weapon_idx = 100;
|
|
second_weapon.weapon_id = spec_weapon_meta->i->id();
|
|
second_weapon.meta = spec_weapon_meta;
|
|
second_weapon.Recalc();
|
|
second_weapon.ammo = second_weapon.GetClipVolume(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kBET_TurnOver:
|
|
{
|
|
buff->ProcTurnOver();
|
|
}
|
|
break;
|
|
case kBET_Camouflage:
|
|
{
|
|
if (aiming) {
|
|
aiming = false;
|
|
}
|
|
if (action_type != AT_None) {
|
|
CancelAction();
|
|
}
|
|
if (IsHuman()) {
|
|
++AsHuman()->stats.use_camouflage_times;
|
|
}
|
|
}
|
|
break;
|
|
case kBET_BePull:
|
|
{
|
|
if (caster == this) {
|
|
A8_ABORT();
|
|
}
|
|
if (caster->GetEntitySubType() == EST_Android) {
|
|
float target_distance = caster->GetPos().Distance(GetPos());
|
|
if (target_distance <= 0.000001f) {
|
|
SetPos(caster->GetPos());
|
|
target_pos = caster->GetPos();
|
|
} else {
|
|
if (target_distance <= buff->meta->param3) {
|
|
SetPos(caster->GetPos());
|
|
target_pos = caster->GetPos();
|
|
} else {
|
|
a8::Vec2 move_dir = caster->GetPos() - GetPos();
|
|
move_dir.Normalize();
|
|
SetMoveDir(move_dir);
|
|
target_pos = GetPos() + GetMoveDir() * (target_distance - buff->meta->param3);
|
|
}
|
|
}
|
|
} else {
|
|
caster->skill_dir_.Normalize();
|
|
target_pos = caster->GetPos() + caster->skill_dir_ * caster->skill_distance_;
|
|
a8::Vec2 move_dir = target_pos - GetPos();
|
|
move_dir.Normalize();
|
|
SetMoveDir(move_dir);
|
|
}
|
|
}
|
|
break;
|
|
case kBET_JumpTo:
|
|
{
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id_);
|
|
if (entity) {
|
|
float target_distance = entity->GetPos().Distance(GetPos());
|
|
if (target_distance <= 0.000001f) {
|
|
SetPos(entity->GetPos());
|
|
skill_target_pos_ = entity->GetPos();
|
|
} else {
|
|
if (target_distance <= buff->meta->param3) {
|
|
SetPos(entity->GetPos());
|
|
skill_target_pos_ = entity->GetPos();
|
|
} else {
|
|
a8::Vec2 move_dir = entity->GetPos() - GetPos();
|
|
move_dir.Normalize();
|
|
SetMoveDir(move_dir);
|
|
skill_dir_ = GetMoveDir();
|
|
skill_target_pos_ = GetPos() + GetMoveDir() * (target_distance - buff->meta->param3);
|
|
}
|
|
}
|
|
}
|
|
target_pos = skill_target_pos_;
|
|
}
|
|
break;
|
|
case kBET_Pull:
|
|
{
|
|
int i = 0;
|
|
}
|
|
break;
|
|
case kBET_Terminator:
|
|
{
|
|
#if 0
|
|
if (GetRace() == kHumanRace &&
|
|
MetaMgr::Instance()->terminator_meta &&
|
|
meta != MetaMgr::Instance()->terminator_meta) {
|
|
WinExp(this, MetaMgr::Instance()->terminator_meta->i->exp() + 1);
|
|
room->NotifySysPiao(TEXT("battle_server_terminator_appear", "终结者出现"), a8::MkRgb(255, 0, 0), 3);
|
|
OnChgToTerminator();
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case kBET_PlayShotAni:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case kBET_Vertigo:
|
|
{
|
|
int i = 0;
|
|
}
|
|
break;
|
|
case kBET_DecHp:
|
|
{
|
|
#if 0
|
|
float def = hum->ability.def * (1 + hum->GetBuffAttrRate(kHAT_Def)) +
|
|
hum->GetBuffAttrAbs(kHAT_Def);
|
|
#endif
|
|
#if 0
|
|
if (caster->GetEntityType() == ET_Player) {
|
|
DecHP(buff->meta->param1,
|
|
caster->GetUniId(),
|
|
((Human*)caster)->name,
|
|
0);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case kBET_DelayAddBuff:
|
|
{
|
|
buff->ProcDelayAddBuff();
|
|
}
|
|
break;
|
|
case kBET_IntervalAddBuff:
|
|
{
|
|
buff->ProcIntervalAddBuff();
|
|
}
|
|
break;
|
|
case kBET_OnceAddHp:
|
|
{
|
|
AddHp(buff->meta->param1);
|
|
}
|
|
break;
|
|
case kBET_SummonHero:
|
|
{
|
|
if (!dead || buff->meta->i->dead_valid() != 0) {
|
|
SummonHero(buff, GetPos(), GetMoveDir());
|
|
}
|
|
}
|
|
break;
|
|
case kBET_Shield:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case kBET_Hide:
|
|
{
|
|
buff->ProcHide();
|
|
}
|
|
break;
|
|
case kBET_SummonObstacle:
|
|
{
|
|
if (!dead || buff->meta->i->dead_valid() != 0) {
|
|
SummonObstacle(buff, buff->meta->param1, context_pos);
|
|
}
|
|
}
|
|
break;
|
|
case kBET_Sprint:
|
|
{
|
|
buff->ProcSprint();
|
|
}
|
|
break;
|
|
case kBET_FlashMove:
|
|
{
|
|
if (buff->meta->int_param1 == 0) {
|
|
a8::Vec2 old_pos = GetPos();
|
|
a8::Vec2 new_pos = GetPos() + skill_dir_ * std::max(skill_distance_, 300.0f);
|
|
SetPos(new_pos);
|
|
if (CollisonDetection()) {
|
|
SetPos(old_pos);
|
|
} else {
|
|
room->grid_service->MoveCreature(this);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kBET_BatchAddBuff:
|
|
{
|
|
buff->ProcBatchAddBuff();
|
|
}
|
|
break;
|
|
case kBET_BeRecycle:
|
|
{
|
|
buff->ProcBeRecycle();
|
|
}
|
|
break;
|
|
case kBET_Become:
|
|
{
|
|
buff->ProcBecome();
|
|
}
|
|
break;
|
|
case kBET_SelectTargetWithSelfPos:
|
|
{
|
|
buff->ProcSeletTargetWithSelfPos();
|
|
}
|
|
break;
|
|
case kBET_Driver:
|
|
{
|
|
buff->ProcDriver();
|
|
}
|
|
break;
|
|
case kBET_Passenger:
|
|
{
|
|
buff->ProcPassenger();
|
|
}
|
|
break;
|
|
case kBET_PullToWalkable:
|
|
{
|
|
buff->ProcPullToWalkable();
|
|
}
|
|
break;
|
|
case kBET_CondAddBuff:
|
|
{
|
|
if (!IsValidCondBuff(buff->meta->int_param1)) {
|
|
A8_ABORT();
|
|
}
|
|
list_add_tail(&buff->cond_entry, &cond_buffs_[buff->meta->int_param1]);
|
|
}
|
|
break;
|
|
case kBET_AddInventory:
|
|
{
|
|
for (int slot : buff->meta->param2_int_list) {
|
|
if (IsValidSlotId(slot)) {
|
|
buff_inventory_[slot].num += buff->meta->int_param1;
|
|
}
|
|
}
|
|
if (IsHuman()) {
|
|
AsHuman()->RecalcVolume();
|
|
}
|
|
need_sync_active_player = true;
|
|
}
|
|
break;
|
|
case kBET_AddCarBuff:
|
|
{
|
|
if (IsHuman() && AsHuman()->GetCar()) {
|
|
AsHuman()->GetCar()->TryAddBuff(this, buff->meta->int_param1);
|
|
}
|
|
}
|
|
break;
|
|
case kBET_RemoveCarBuff:
|
|
{
|
|
if (IsHuman() && AsHuman()->GetCar()) {
|
|
AsHuman()->GetCar()->RemoveBuffById(buff->meta->int_param1);
|
|
}
|
|
}
|
|
break;
|
|
case kBET_UseSkill:
|
|
{
|
|
Skill* skill = GetSkill(buff->meta->int_param1);
|
|
if (skill) {
|
|
++skill->force_use_times;
|
|
a8::Vec2 target_pos = GetPos();
|
|
DoSkill(
|
|
skill->meta->i->skill_id(),
|
|
GetUniId(),
|
|
GetAttackDir(),
|
|
0,
|
|
target_pos
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
case kBET_CamouflageAddition:
|
|
{
|
|
Buff* camouflage_buff = GetBuffByEffectId(kBET_Camouflage);
|
|
if (camouflage_buff && camouflage_buff->remover_timer && buff->meta->int_param1 > 0) {
|
|
int remain_time = room->xtimer.GetRemainTime(camouflage_buff->remover_timer) * FRAME_RATE_MS;
|
|
remain_time += buff->meta->int_param1;
|
|
room->xtimer.ModifyTimer(camouflage_buff->remover_timer, remain_time / FRAME_RATE_MS);
|
|
}
|
|
if (buff->meta->int_param2 != 0) {
|
|
++camouflage_move_addition_;
|
|
}
|
|
if (buff->meta->int_param3 != 0) {
|
|
++camouflage_aiming_addition_;
|
|
}
|
|
}
|
|
break;
|
|
case kBET_AutoShot:
|
|
{
|
|
buff->ProcAutoShot();
|
|
}
|
|
break;
|
|
case kBET_BeatBack:
|
|
{
|
|
buff->ProcBeatBack();
|
|
}
|
|
break;
|
|
case kBET_Disperse:
|
|
{
|
|
buff->ProcDisperse();
|
|
}
|
|
break;
|
|
case kBET_PeaceMode:
|
|
{
|
|
}
|
|
break;
|
|
case kBET_Dive:
|
|
{
|
|
buff->ProcDive();
|
|
}
|
|
break;
|
|
case kBET_InWater:
|
|
{
|
|
buff->ProcInWater();
|
|
}
|
|
break;
|
|
case kBET_Reverse:
|
|
{
|
|
buff->ProcReserve();
|
|
}
|
|
break;
|
|
case kBET_ReverseMove:
|
|
{
|
|
buff->ProcReserveMove();
|
|
}
|
|
break;
|
|
case kBET_MachineGun:
|
|
{
|
|
buff->ProcMachineGun();
|
|
}
|
|
break;
|
|
case kBET_HoldShield:
|
|
{
|
|
buff->ProcHoldShield();
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Creature::Initialize()
|
|
{
|
|
MoveableEntity::Initialize();
|
|
for (auto& node : buff_effect_) {
|
|
INIT_LIST_HEAD(&node);
|
|
}
|
|
for (auto& node : depend_effect_) {
|
|
INIT_LIST_HEAD(&node);
|
|
}
|
|
for (auto& node : cond_buffs_) {
|
|
INIT_LIST_HEAD(&node);
|
|
}
|
|
}
|
|
|
|
void Creature::SetMoveDir(const a8::Vec2& move_dir)
|
|
{
|
|
if (GetDisableMoveDirTimes() <= 0) {
|
|
MoveableEntity::SetMoveDir(move_dir);
|
|
}
|
|
}
|
|
|
|
void Creature::AddSkill(int skill_id)
|
|
{
|
|
MetaData::Skill* skill_meta = MetaMgr::Instance()->GetSkill(skill_id);
|
|
if (skill_meta && !GetSkill(skill_id)) {
|
|
if (skill_meta->i->skill_type() == 1) {
|
|
Skill* skill = new Skill;
|
|
skill->owner = this;
|
|
skill->meta = skill_meta;
|
|
skill->xtimer_attacher.xtimer = &room->xtimer;
|
|
skill->Initialzie();
|
|
skill_hash_[skill_id] = skill;
|
|
} else if (skill_meta->i->skill_type() == 2) {
|
|
AddPassiveSkill(skill_id);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Creature::ClearSkill()
|
|
{
|
|
Skill* reserve_skill = nullptr;
|
|
for (auto& pair : skill_hash_) {
|
|
if (pair.second->meta->IsTurnOverSkill()) {
|
|
reserve_skill = pair.second;
|
|
} else {
|
|
delete pair.second;
|
|
}
|
|
}
|
|
skill_hash_.clear();
|
|
if (reserve_skill) {
|
|
skill_hash_[reserve_skill->meta->i->skill_id()] = reserve_skill;
|
|
}
|
|
}
|
|
|
|
bool Creature::CanSee(const Creature* c) const
|
|
{
|
|
return room->grid_service->InView(GetGridId(), c->GetGridId());
|
|
}
|
|
|
|
bool Creature::IsPlayer() const
|
|
{
|
|
return IsHuman() && IsEntitySubType(EST_Player);
|
|
}
|
|
|
|
bool Creature::IsAndroid() const
|
|
{
|
|
return IsHuman() && IsEntitySubType(EST_Android);
|
|
}
|
|
|
|
bool Creature::IsHuman() const
|
|
{
|
|
return IsEntityType(ET_Player);
|
|
}
|
|
|
|
bool Creature::IsCar() const
|
|
{
|
|
return IsEntityType(ET_Car);
|
|
}
|
|
|
|
void Creature::StartAction(ActionType_e action_type,
|
|
int action_duration,
|
|
int item_id,
|
|
int target_id)
|
|
{
|
|
if (this->action_type == action_type &&
|
|
this->action_item_id == item_id &&
|
|
this->action_target_id == target_id) {
|
|
return;
|
|
}
|
|
action_duration = std::max(0, action_duration);
|
|
this->action_type = action_type;
|
|
this->action_frameno = room->GetFrameNo();
|
|
this->action_duration = action_duration;
|
|
this->action_item_id = item_id;
|
|
this->action_target_id = target_id;
|
|
need_sync_active_player = true;
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
}
|
|
|
|
void Creature::CancelAction()
|
|
{
|
|
if (action_type == AT_Relive) {
|
|
Entity* entity = room->GetEntityByUniId(action_target_id);
|
|
if (!entity->IsEntityType(ET_Player)) {
|
|
Human* hum = (Human*)entity;
|
|
if (hum->action_type == AT_Rescue) {
|
|
hum->CancelAction();
|
|
}
|
|
}
|
|
RemoveBuffByEffectId(kBET_Rescuer);
|
|
}
|
|
if (action_type == AT_Rescue) {
|
|
RemoveBuffByEffectId(kBET_InRescue);
|
|
}
|
|
ResetAction();
|
|
}
|
|
|
|
void Creature::ResetAction()
|
|
{
|
|
action_type = AT_None;
|
|
action_duration = 0;
|
|
action_frameno = 0;
|
|
action_item_id = 0;
|
|
action_target_id = 0;
|
|
need_sync_active_player = true;
|
|
if (reload_delay_timer_) {
|
|
room->xtimer.DeleteTimer(reload_delay_timer_);
|
|
reload_delay_timer_ = nullptr;
|
|
}
|
|
}
|
|
|
|
bool Creature::IsProperTarget(Creature* target, bool no_teammate)
|
|
{
|
|
if (target->dead) {
|
|
return false;
|
|
}
|
|
if (a8::HasBitFlag(target->status, CS_Disable)) {
|
|
return false;
|
|
}
|
|
if (!no_teammate && team_id == target->team_id) {
|
|
return false;
|
|
}
|
|
if (target->IsInvincible()) {
|
|
return false;
|
|
}
|
|
if (target->HasBuffEffect(kBET_Hide)) {
|
|
return false;
|
|
}
|
|
if (target->HasBuffEffect(kBET_Driver)) {
|
|
return false;
|
|
}
|
|
if (target->HasBuffEffect(kBET_Passenger)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Creature::IsEnemy(Creature* target)
|
|
{
|
|
return team_id != target->team_id;
|
|
}
|
|
|
|
void Creature::TraverseProperTargets(std::function<void (Creature*, bool&)> func)
|
|
{
|
|
auto callback =
|
|
[this, func] (Creature* c, bool& stop)
|
|
{
|
|
if (IsProperTarget(c)) {
|
|
func(c, stop);
|
|
}
|
|
};
|
|
room->grid_service->TraverseCreatures(room->GetRoomIdx(),
|
|
GetGridList(),
|
|
callback);
|
|
}
|
|
|
|
void Creature::TraverseProperTargetsNoTeammate(std::function<void (Creature*, bool&)> func)
|
|
{
|
|
auto callback =
|
|
[this, func] (Creature* c, bool& stop)
|
|
{
|
|
if (IsProperTarget(c, true)) {
|
|
func(c, stop);
|
|
}
|
|
};
|
|
room->grid_service->TraverseCreatures(room->GetRoomIdx(),
|
|
GetGridList(),
|
|
callback);
|
|
}
|
|
|
|
void Creature::UpdatePoisoning()
|
|
{
|
|
if (dead) {
|
|
poisoning_time = 0;
|
|
return;
|
|
}
|
|
bool need_notify = poisoning_time > 1000;
|
|
while (poisoning_time > 1000) {
|
|
float dmg = 0;
|
|
if (room->GetGasData().is_last_gas) {
|
|
dmg = room->GetGasData().new_area_meta->i->hurt();
|
|
} else {
|
|
dmg = room->GetGasData().old_area_meta->i->hurt();
|
|
}
|
|
if (room->IsPveRoom()) {
|
|
dmg = room->GetGasData().new_area_meta->i->hurt();
|
|
}
|
|
dmg *= 1 + GetAbility()->GetAttrRate(kHAT_PoisoningReduction);
|
|
if (room->IsPveRoom()) {
|
|
} else {
|
|
dmg = std::max(10.0f, dmg);
|
|
}
|
|
DecHP(dmg, VP_Gas, TEXT("battle_server_killer_gas", "毒圈"), VW_Gas);
|
|
if (dead) {
|
|
poisoning_time = 0;
|
|
break;
|
|
}
|
|
poisoning_time -= 1000;
|
|
}
|
|
if (need_notify && IsEntitySubType(EST_Player)) {
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
|
|
void Creature::Shot(a8::Vec2& target_dir, bool& shot_ok, float fly_distance, int trace_target_uniid)
|
|
{
|
|
shot_ok = false;
|
|
if (!GetCurrWeapon()->meta) {
|
|
return;
|
|
}
|
|
if (downed) {
|
|
return;
|
|
}
|
|
if (!GetCurrWeapon()->meta) {
|
|
return;
|
|
}
|
|
|
|
if (HasBuffEffect(kBET_Jump) ||
|
|
HasBuffEffect(kBET_Fly)) {
|
|
return;
|
|
}
|
|
|
|
if (GetCurrWeapon()->weapon_idx != 0 &&
|
|
GetCurrWeapon()->ammo <= 0) {
|
|
CheckLoadingBullet();
|
|
//AutoLoadingBullet();
|
|
return;
|
|
}
|
|
|
|
if (action_type == AT_Reload) {
|
|
CancelAction();
|
|
}
|
|
if (action_type == AT_Reload ||
|
|
action_type == AT_Rescue ||
|
|
action_type == AT_UseItem ||
|
|
action_type == AT_Relive) {
|
|
CancelAction();
|
|
}
|
|
|
|
if (IsCar()) {
|
|
if (room->GetFrameNo() - last_shot_frameno_ > 0) {
|
|
if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) <
|
|
GetCurrWeapon()->GetFireRate(this)
|
|
) {
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) <
|
|
GetCurrWeapon()->GetFireRate(this)
|
|
) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (GetCurrWeapon()->meta->power_charge.empty() || power_idx < 0) {
|
|
InternalShot(this,
|
|
GetCurrWeapon()->meta,
|
|
GetCurrWeapon()->bullet_meta,
|
|
GetCurrWeapon()->skill_meta,
|
|
fly_distance,
|
|
GetCurrWeapon()->weapon_uniid,
|
|
trace_target_uniid);
|
|
} else if (power_idx < GetCurrWeapon()->meta->power_charge.size()) {
|
|
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip
|
|
(std::get<1>(GetCurrWeapon()->meta->power_charge[power_idx]));
|
|
if (weapon_meta) {
|
|
MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(weapon_meta->i->use_bullet());;
|
|
|
|
if (bullet_meta) {
|
|
#ifdef DEBUG
|
|
SendDebugMsg(a8::Format("蓄力射击 %d 枪:%d 子弹:%d",
|
|
{
|
|
power_idx,
|
|
weapon_meta->i->id(),
|
|
bullet_meta->i->id()
|
|
}));
|
|
#endif
|
|
InternalShot(this,
|
|
weapon_meta,
|
|
bullet_meta,
|
|
nullptr,
|
|
fly_distance,
|
|
0,
|
|
trace_target_uniid);
|
|
} else {
|
|
A8_ABORT();
|
|
}
|
|
} else {
|
|
A8_ABORT();
|
|
}
|
|
} else {
|
|
A8_ABORT();
|
|
}
|
|
|
|
if (GetCurrWeapon()->weapon_idx != 0 &&
|
|
GetCurrWeapon()->meta->i->bullet_consume_type() == kBulletConsumeOne) {
|
|
--GetCurrWeapon()->ammo;
|
|
}
|
|
int slot_id = GetCurrWeapon()->meta->i->_inventory_slot();
|
|
#ifdef DEBUG
|
|
if (IsPlayer()) {
|
|
SendDebugMsg(a8::Format("使用武器 %s slot:%d", {GetCurrWeapon()->meta->i->name(), slot_id}));
|
|
}
|
|
#endif
|
|
bool auto_switch_weapon = false;
|
|
switch (slot_id) {
|
|
case IS_FRAG: //手雷
|
|
case IS_SMOKE: //烟雾弹
|
|
{
|
|
#if 1
|
|
DecInventory(slot_id, 1);
|
|
#endif
|
|
if (GetCurrWeapon()->ammo <= 0) {
|
|
if (GetInventory(slot_id) > 0) {
|
|
DecInventory(slot_id, 1);
|
|
++GetCurrWeapon()->ammo;
|
|
} else {
|
|
auto_switch_weapon = true;
|
|
}
|
|
}
|
|
need_sync_active_player = true;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
break;
|
|
case IS_POSION_GAS_BOMB: //毒气弹
|
|
case IS_MOLOTOR_COCKTAIL: //燃烧瓶
|
|
case IS_TRAP: //陷井
|
|
case IS_MINE: //地雷
|
|
{
|
|
#if 1
|
|
DecInventory(slot_id, 1);
|
|
#endif
|
|
if (GetCurrWeapon()->ammo <= 0) {
|
|
if (GetInventory(slot_id) > 0) {
|
|
DecInventory(slot_id, 1);
|
|
++GetCurrWeapon()->ammo;
|
|
} else {
|
|
auto_switch_weapon = true;
|
|
}
|
|
}
|
|
need_sync_active_player = true;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
break;
|
|
case IS_C4:
|
|
case IS_SHIELD_WALL:
|
|
case IS_SINGAL_GUN:
|
|
case IS_OIL_BUCKET:
|
|
{
|
|
#if 1
|
|
DecInventory(slot_id, 1);
|
|
#endif
|
|
if (GetCurrWeapon()->ammo <= 0) {
|
|
if (GetInventory(slot_id) > 0) {
|
|
DecInventory(slot_id, 1);
|
|
++GetCurrWeapon()->ammo;
|
|
} else {
|
|
auto_switch_weapon = true;
|
|
}
|
|
}
|
|
need_sync_active_player = true;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
if (auto_switch_weapon) {
|
|
AutoSwitchWeapon();
|
|
} else {
|
|
CheckLoadingBullet();
|
|
}
|
|
last_shot_frameno_ = room->GetFrameNo();
|
|
if ((IsPlayer() || IsCar())) {
|
|
room->frame_event.AddBulletNumChg(GetWeakPtrRef());
|
|
room->frame_event.AddWeaponAmmoChg(GetWeakPtrRef());
|
|
}
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
shot_ok = true;
|
|
}
|
|
|
|
void Creature::AutoLoadingBullet(bool manual)
|
|
{
|
|
Weapon* p_weapon = GetCurrWeapon();
|
|
if (second_weapon.meta) {
|
|
p_weapon = &second_weapon;
|
|
}
|
|
if (p_weapon->weapon_idx != 0 &&
|
|
(p_weapon->ammo <= 0 ||
|
|
(manual && p_weapon->ammo < p_weapon->GetClipVolume(this)))
|
|
) {
|
|
MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(p_weapon->meta->i->use_bullet());
|
|
if (bullet_meta &&
|
|
bullet_meta->i->_inventory_slot() >= 0 &&
|
|
bullet_meta->i->_inventory_slot() < (int)inventory_.size()
|
|
) {
|
|
if (GetInventory(bullet_meta->i->_inventory_slot()) > 0) {
|
|
if (on_loading_bullet) {
|
|
on_loading_bullet();
|
|
}
|
|
int duration_time = p_weapon->GetReloadTime(this) *
|
|
(1 + GetAbility()->GetAttrRate(kHAT_WeaponReloadTime));
|
|
StartAction(AT_Reload,
|
|
duration_time,
|
|
p_weapon->weapon_id,
|
|
p_weapon->weapon_idx);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
int Creature::GetInventory(int slot_id)
|
|
{
|
|
if (!IsValidSlotId(slot_id)) {
|
|
A8_ABORT();
|
|
}
|
|
return inventory_[slot_id].num;
|
|
}
|
|
|
|
void Creature::AddInventory(int slot_id, int num)
|
|
{
|
|
assert(num > 0);
|
|
if (!IsValidSlotId(slot_id)) {
|
|
A8_ABORT();
|
|
}
|
|
inventory_[slot_id].num += num;
|
|
}
|
|
|
|
void Creature::DecInventory(int slot_id, int num)
|
|
{
|
|
assert(num > 0);
|
|
if (!IsValidSlotId(slot_id)) {
|
|
A8_ABORT();
|
|
}
|
|
inventory_[slot_id].num -= num;
|
|
}
|
|
|
|
void Creature::CheckSpecObject()
|
|
{
|
|
std::set<ColliderComponent*> colliders;
|
|
room->map_service->GetSpecColliders(SPEC_MAP_OBJECT_FLAGS, room, GetPos().x, GetPos().y, colliders);
|
|
|
|
#ifdef DEBUG
|
|
int water_w = 0;
|
|
int water_h = 0;
|
|
#endif
|
|
long long old_cell_flags = cell_flags_;
|
|
cell_flags_ = 0;
|
|
for (const ColliderComponent* collider : colliders) {
|
|
switch (collider->owner->GetEntityType()) {
|
|
case ET_Obstacle:
|
|
case ET_Building:
|
|
case ET_Dummy:
|
|
case ET_MapBlock:
|
|
{
|
|
if (TestCollision(room, (ColliderComponent*)collider)) {
|
|
cell_flags_ |= collider->tag;
|
|
#ifdef DEBUG
|
|
if (a8::HasBitFlag(collider->tag, kColliderTag_Water)) {
|
|
water_w = collider->param1;
|
|
water_h = collider->param2;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (old_cell_flags != cell_flags_) {
|
|
if (!a8::SameBitFlag(old_cell_flags, cell_flags_, kColliderTag_Grass)) {
|
|
if (a8::HasBitFlag(cell_flags_, kColliderTag_Grass)) {
|
|
TryAddBuff(this, kInGrassBuffId);
|
|
} else {
|
|
RemoveBuffById(kInGrassBuffId);
|
|
}
|
|
}
|
|
|
|
if (!a8::SameBitFlag(old_cell_flags, cell_flags_, kColliderTag_Water)) {
|
|
if (a8::HasBitFlag(cell_flags_, kColliderTag_Water)) {
|
|
TryAddBuff(this, kInWaterBuffId);
|
|
} else {
|
|
RemoveBuffById(kInWaterBuffId);
|
|
}
|
|
}
|
|
|
|
if (!a8::SameBitFlag(old_cell_flags, cell_flags_, kColliderTag_Ice)) {
|
|
if (a8::HasBitFlag(cell_flags_, kColliderTag_Ice)) {
|
|
TryAddBuff(this, kInIceBuffId);
|
|
} else {
|
|
RemoveBuffById(kInIceBuffId);
|
|
}
|
|
}
|
|
|
|
if (!a8::SameBitFlag(old_cell_flags, cell_flags_, kColliderTag_MountainTop)) {
|
|
if (a8::HasBitFlag(cell_flags_, kColliderTag_MountainTop)) {
|
|
TryAddBuff(this, kInMountainTopBuffId);
|
|
} else {
|
|
RemoveBuffById(kInMountainTopBuffId);
|
|
}
|
|
}
|
|
|
|
}
|
|
#ifdef DEBUG
|
|
if (IsPlayer()) {
|
|
if (old_cell_flags != cell_flags_) {
|
|
std::string msg = "terrain changed old:";
|
|
{
|
|
if (a8::HasBitFlag(old_cell_flags, kColliderTag_Grass)) {
|
|
msg += " grass:1";
|
|
} else {
|
|
msg += " grass:0";
|
|
}
|
|
if (a8::HasBitFlag(old_cell_flags, kColliderTag_Water)) {
|
|
msg += " water:1";
|
|
} else {
|
|
msg += " water:0";
|
|
}
|
|
if (a8::HasBitFlag(old_cell_flags, kColliderTag_Ice)) {
|
|
msg += " ice:1";
|
|
} else {
|
|
msg += " ice:0";
|
|
}
|
|
if (a8::HasBitFlag(old_cell_flags, kColliderTag_MountainTop)) {
|
|
msg += " mountaintop:1";
|
|
} else {
|
|
msg += " mountaintop:0";
|
|
}
|
|
}
|
|
{
|
|
msg += " new:";
|
|
if (a8::HasBitFlag(cell_flags_, kColliderTag_Grass)) {
|
|
msg += " grass:1";
|
|
} else {
|
|
msg += " grass:0";
|
|
}
|
|
if (a8::HasBitFlag(cell_flags_, kColliderTag_Water)) {
|
|
msg += " water:1";
|
|
} else {
|
|
msg += " water:0";
|
|
}
|
|
if (a8::HasBitFlag(cell_flags_, kColliderTag_Ice)) {
|
|
msg += " ice:1";
|
|
} else {
|
|
msg += " ice:0";
|
|
}
|
|
if (a8::HasBitFlag(cell_flags_, kColliderTag_MountainTop)) {
|
|
msg += " mountaintop:1";
|
|
} else {
|
|
msg += " mountaintop:0";
|
|
}
|
|
}
|
|
msg += a8::Format(" o:%d n:%d w:%d h:%d", {old_cell_flags, cell_flags_, water_w, water_h});
|
|
SendDebugMsg(msg);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Creature::SummonObstacle(Buff* buff, int id, const a8::Vec2& target_pos)
|
|
{
|
|
if (buff->meta->int_param2 > 0) {
|
|
RemoveSurplusObstacle(buff->meta->i->buff_id(), id, buff->meta->int_param2);
|
|
}
|
|
float *p_rotate = nullptr;
|
|
float rotate = 0.0f;
|
|
a8::Vec2 pos = target_pos;
|
|
if (buff->skill_meta) {
|
|
switch (buff->skill_meta->GetMagicId()) {
|
|
case MAGIC_WLFB:
|
|
{
|
|
if (!GetAttackDir().IsZero()) {
|
|
pos = target_pos + GetAttackDir() * 100;
|
|
rotate = GetAttackDir().CalcAngleEx(a8::Vec2::UP);
|
|
if (GetAttackDir().x < 0.00001f) {
|
|
rotate = -rotate;
|
|
}
|
|
p_rotate = &rotate;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
RoomObstacle* obstacle = room->CreateObstacle(id, pos.x, pos.y);
|
|
if (obstacle) {
|
|
obstacle->buff_meta = buff->meta;
|
|
obstacle->skill_meta = buff->skill_meta;
|
|
obstacle->master.Attach(this);
|
|
obstacle->SetTeamId(room, team_id);
|
|
obstacle->SetMasterId(room, GetUniId());
|
|
obstacle->Active();
|
|
if (p_rotate) {
|
|
obstacle->SetRotate(*p_rotate);
|
|
}
|
|
obstacle->context_ability = context_ability;
|
|
slave_things_.push_back(std::make_tuple(buff->meta->i->buff_id(), obstacle));
|
|
#ifdef DEBUG
|
|
SendDebugMsg(a8::Format("召唤物件 buff_id:%d thing_id:%d pos:%f,%f target_pos:%f,%f",
|
|
{buff->meta->i->buff_id(),
|
|
id,
|
|
GetPos().x,
|
|
GetPos().y,
|
|
pos.x,
|
|
pos.y
|
|
}));
|
|
#endif
|
|
} else {
|
|
#ifdef DEBUG
|
|
A8_ABORT();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool Creature::CollisonDetection()
|
|
{
|
|
ColliderComponent* pickup_collider = nullptr;
|
|
return CollisonDetectionAndGetCollider(&pickup_collider);
|
|
}
|
|
|
|
bool Creature::CollisonDetectionAndGetCollider(ColliderComponent** pickup_collider)
|
|
{
|
|
bool through_wall = HasBuffEffect(kBET_ThroughWall) ||
|
|
HasBuffEffect(kBET_Fly);
|
|
AabbCollider aabb_box;
|
|
GetAabbBox(aabb_box);
|
|
return room->map_service->CollisionDetectionAndGetCollider
|
|
(
|
|
room,
|
|
through_wall,
|
|
GetPos(),
|
|
&aabb_box,
|
|
pickup_collider
|
|
);
|
|
}
|
|
|
|
void Creature::FillSkillCasterState(SkillCasterState* caster_state)
|
|
{
|
|
caster_state->caster.Attach(this);
|
|
caster_state->caster_pos = GetPos();
|
|
caster_state->caster_skill_id = CurrentSkill() ? CurrentSkill()->meta->i->skill_id() : 0;
|
|
caster_state->caster_skill_target_id = skill_target_id_;
|
|
caster_state->caster_skill_dir = skill_dir_;
|
|
caster_state->caster_skill_distance = skill_distance_;
|
|
caster_state->caster_skill_param1 = skill_param1;
|
|
}
|
|
|
|
void Creature::RecoverSkillCasterState(SkillCasterState* caster_state)
|
|
{
|
|
SetPos(caster_state->caster_pos);
|
|
skill_target_id_ = caster_state->caster_skill_target_id;
|
|
skill_dir_ = caster_state->caster_skill_dir;
|
|
skill_distance_ = caster_state->caster_skill_distance;
|
|
skill_param1 = caster_state->caster_skill_param1;
|
|
}
|
|
|
|
CreatureWeakPtr Creature::AllocWeakPtr()
|
|
{
|
|
CreatureWeakPtr ptr;
|
|
ptr.Attach(this);
|
|
return ptr;
|
|
}
|
|
|
|
CreatureWeakPtr& Creature::GetWeakPtrRef()
|
|
{
|
|
if (!weak_ptr_.Get()) {
|
|
weak_ptr_.Attach(this);
|
|
}
|
|
return weak_ptr_;
|
|
}
|
|
|
|
Weapon* Creature::AutoChgWeapon()
|
|
{
|
|
if (weapons[GUN_SLOT1].weapon_id != 0) {
|
|
SetCurrWeapon(&weapons[GUN_SLOT1]);
|
|
} else if (weapons[GUN_SLOT2].weapon_id != 0) {
|
|
SetCurrWeapon(&weapons[GUN_SLOT2]);
|
|
} else {
|
|
SetCurrWeapon(&weapons[0]);
|
|
}
|
|
return GetCurrWeapon();
|
|
}
|
|
|
|
Weapon* Creature::ChooseNextWeapon(int curr_weapon_slot_id, int begin_slot_id, int end_slot_id)
|
|
{
|
|
if (curr_weapon_slot_id < begin_slot_id) {
|
|
A8_ABORT();
|
|
}
|
|
if (curr_weapon_slot_id > end_slot_id) {
|
|
A8_ABORT();
|
|
}
|
|
if (begin_slot_id > end_slot_id) {
|
|
A8_ABORT();
|
|
}
|
|
if (begin_slot_id < 0 ||
|
|
end_slot_id < 0) {
|
|
A8_ABORT();
|
|
}
|
|
Weapon* next_weapon = nullptr;
|
|
for (int i = 1; i <= (end_slot_id - begin_slot_id + 1); ++i) {
|
|
int slot_id = begin_slot_id +
|
|
(i + curr_weapon_slot_id - begin_slot_id) % (end_slot_id - begin_slot_id + 1);
|
|
int idx = -1;
|
|
if (slot_id >= SPEC1_IS_BEGIN && slot_id <= SPEC1_IS_END) {
|
|
idx = SPEC1_SLOT_BEGIN + (slot_id - SPEC1_IS_BEGIN);
|
|
}
|
|
if (slot_id >= SPEC2_IS_BEGIN && slot_id <= SPEC2_IS_END) {
|
|
idx = SPEC2_SLOT_BEGIN + (slot_id - SPEC2_IS_BEGIN);
|
|
}
|
|
if (slot_id >= SPEC3_IS_BEGIN && slot_id <= SPEC3_IS_END) {
|
|
idx = SPEC3_SLOT_BEGIN + (slot_id - SPEC3_IS_BEGIN);
|
|
}
|
|
if (idx != -1 && weapons.at(idx).weapon_id != 0) {
|
|
next_weapon = &weapons[idx];
|
|
break;
|
|
}
|
|
}
|
|
return next_weapon;
|
|
}
|
|
|
|
void Creature::SetCurrWeapon(Weapon* weapon)
|
|
{
|
|
#ifdef DEBUG
|
|
bool found = false;
|
|
for (auto& p : weapons) {
|
|
if (&p == weapon) {
|
|
found = true;
|
|
}
|
|
}
|
|
if (!found) {
|
|
A8_ABORT();
|
|
}
|
|
#endif
|
|
bool need_recalc = curr_weapon_ != weapon;
|
|
if (auto_switch_weapon_timer_) {
|
|
room->xtimer.DeleteTimer(auto_switch_weapon_timer_);
|
|
}
|
|
if (curr_weapon_ != weapon) {
|
|
GetTrigger()->TakeonWeapon(curr_weapon_, weapon);
|
|
}
|
|
curr_weapon_ = weapon;
|
|
if (need_recalc) {
|
|
RecalcDtoAttr();
|
|
}
|
|
}
|
|
|
|
void Creature::ResetAllSkillCd()
|
|
{
|
|
for (auto& pair : skill_hash_) {
|
|
pair.second->ResetSkillCd();
|
|
}
|
|
}
|
|
|
|
void Creature::SummonHero(Buff* buff,
|
|
const a8::Vec2& pos,
|
|
const a8::Vec2& dir
|
|
)
|
|
{
|
|
for (auto& info : buff->meta->hero_infos) {
|
|
int through_wall = std::get<0>(info);
|
|
float x = std::get<1>(info);
|
|
float y = std::get<2>(info) ;
|
|
int hero_id = std::get<3>(info);
|
|
int num = std::get<4>(info);
|
|
int life_time = std::get<5>(info);
|
|
MetaData::Player* hero_meta = MetaMgr::Instance()->GetPlayer(hero_id);
|
|
if (hero_meta) {
|
|
for (int j = 0; j < num; ++j) {
|
|
bool can_create = false;
|
|
a8::Vec2 born_pos;
|
|
Hero* hero = nullptr;
|
|
for (int i = 0; i < 4; ++i) {
|
|
a8::Vec2 born_dir = dir;
|
|
a8::Vec2 born_offset(x, y);
|
|
born_offset.Rotate(born_dir.CalcAngle(a8::Vec2::UP));
|
|
born_offset.Rotate(i * 0.5);
|
|
born_pos = pos + born_offset;
|
|
can_create = TrySummonHero(hero_meta,
|
|
dir,
|
|
born_pos,
|
|
through_wall);
|
|
if (can_create) {
|
|
break;
|
|
}
|
|
}//end for i
|
|
if (!can_create) {
|
|
born_pos = GetPos();
|
|
can_create = TrySummonHero(hero_meta,
|
|
GetAttackDir(),
|
|
born_pos,
|
|
through_wall);
|
|
}
|
|
if (can_create) {
|
|
InternalSummonHero(buff,
|
|
hero_meta,
|
|
GetAttackDir(),
|
|
born_pos,
|
|
through_wall,
|
|
num,
|
|
life_time);
|
|
}
|
|
}//end for j
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Creature::FreezeOperate()
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_Become);
|
|
bool freeze = buff && buff->FreezeOperate();
|
|
if (!freeze) {
|
|
freeze = GetBuffByEffectId(kBET_Vertigo) ||
|
|
GetBuffByEffectId(kBET_Driver) ||
|
|
GetBuffByEffectId(kBET_MachineGun) ||
|
|
GetBuffByEffectId(kBET_Passenger);
|
|
}
|
|
return freeze;
|
|
}
|
|
|
|
void Creature::SlaveOnRemove(Entity* slave)
|
|
{
|
|
switch (slave->GetEntityType()) {
|
|
case ET_Hero:
|
|
{
|
|
for (auto itr = slave_heros_.begin(); itr != slave_heros_.end(); ++itr) {
|
|
if ((Entity*)std::get<1>(*itr) == slave) {
|
|
slave_heros_.erase(itr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ET_Obstacle:
|
|
{
|
|
for (auto itr = slave_things_.begin(); itr != slave_things_.end(); ++itr) {
|
|
if ((Entity*)std::get<1>(*itr) == slave) {
|
|
slave_things_.erase(itr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
A8_ABORT();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Creature::RemoveSurplusHero(int buff_id, int id, int num)
|
|
{
|
|
if (slave_heros_.size() >= num && num > 0) {
|
|
std::vector<Hero*> matched_heros;
|
|
for (auto& itr : slave_heros_) {
|
|
if (std::get<0>(itr) == buff_id &&
|
|
std::get<1>(itr)->meta->i->id() == id) {
|
|
matched_heros.push_back(std::get<1>(itr));
|
|
}
|
|
}
|
|
while (matched_heros.size() > num) {
|
|
matched_heros[0]->Destory();
|
|
matched_heros.erase(matched_heros.begin());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Creature::RemoveSurplusObstacle(int buff_id, int id, int num)
|
|
{
|
|
if (slave_things_.size() >= num && num > 0) {
|
|
std::vector<RoomObstacle*> matched_things;
|
|
for (auto& itr : slave_things_) {
|
|
if (std::get<0>(itr) == buff_id &&
|
|
std::get<1>(itr)->meta->i->thing_id() == id) {
|
|
matched_things.push_back(std::get<1>(itr));
|
|
}
|
|
}
|
|
while (matched_things.size() > num) {
|
|
matched_things[0]->Destory();
|
|
matched_things.erase(matched_things.begin());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Creature::IsInvincible()
|
|
{
|
|
return
|
|
HasBuffEffect(kBET_Invincible) ||
|
|
HasBuffEffect(kBET_AdPlaying) ||
|
|
HasBuffEffect(kBET_Driver) ||
|
|
HasBuffEffect(kBET_Passenger)
|
|
;
|
|
}
|
|
|
|
void Creature::ClearAimingBuffs()
|
|
{
|
|
for (int buff_id : aiming_buffs) {
|
|
RemoveBuffById(buff_id);
|
|
}
|
|
aiming_buffs.clear();
|
|
}
|
|
|
|
float Creature::GetHP()
|
|
{
|
|
return ability.hp;
|
|
}
|
|
|
|
float Creature::GetMaxHP()
|
|
{
|
|
#if 1
|
|
return ability.max_hp;
|
|
#else
|
|
return ability.max_hp * (1 + GetAbility()->GetAttrRate(kHAT_MaxHp)) +
|
|
GetAbility()->GetAttrAbs(kHAT_MaxHp);
|
|
#endif
|
|
}
|
|
|
|
void Creature::GetHitEnemys(std::set<Creature*>& enemys, float radius)
|
|
{
|
|
float min_distance = 9999999999;
|
|
room->grid_service->TraverseCreatures
|
|
(room->GetRoomIdx(),
|
|
GetGridList(),
|
|
[this, &enemys, radius, &min_distance] (Creature* c, bool& stop)
|
|
{
|
|
if (IsProperTarget(c)) {
|
|
float distance = GetPos().Distance(c->GetPos());
|
|
min_distance = std::min(min_distance, distance);
|
|
if (distance < radius) {
|
|
enemys.insert(c);
|
|
}
|
|
}
|
|
});
|
|
int i = 0;
|
|
}
|
|
|
|
void Creature::AddHp(float hp)
|
|
{
|
|
if (dead) {
|
|
return;
|
|
}
|
|
float old_health = GetHP();
|
|
ability.hp += hp;
|
|
ability.hp = std::min(GetHP(), GetMaxHP());
|
|
if (IsHuman()) {
|
|
Human* hum = (Human*)this;
|
|
hum->stats.heal_amount += GetHP() - old_health;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
|
|
void Creature::SetHP(float hp)
|
|
{
|
|
ability.hp = hp;
|
|
#ifdef DEBUG1
|
|
if (IsPlayer()) {
|
|
a8::XPrintf("hp:%f maxhp:%f\n",
|
|
{
|
|
GetHP(),
|
|
GetMaxHP()
|
|
});
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Creature::SetMaxHP(float max_hp)
|
|
{
|
|
ability.max_hp = max_hp;
|
|
#ifdef DEBUG1
|
|
if (IsPlayer()) {
|
|
a8::XPrintf("hp:%f maxhp:%f\n",
|
|
{
|
|
GetHP(),
|
|
GetMaxHP()
|
|
});
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool Creature::TryMove(const a8::Vec2& target_pos, a8::Vec2& out_pos)
|
|
{
|
|
bool move_ok = false;
|
|
a8::Vec2 old_pos = GetPos();
|
|
out_pos = GetPos();
|
|
|
|
SetPos(target_pos);
|
|
if (CollisonDetection()) {
|
|
out_pos = target_pos;
|
|
move_ok = true;
|
|
} else {
|
|
}
|
|
return move_ok;
|
|
}
|
|
|
|
void Creature::SetInfiniteBulletMode()
|
|
{
|
|
inventory_[IS_9MM].num = FIGHTING_MODE_BULLET_NUM;
|
|
inventory_[IS_556MM].num = FIGHTING_MODE_BULLET_NUM;
|
|
inventory_[IS_762MM].num = FIGHTING_MODE_BULLET_NUM;
|
|
inventory_[IS_12GAUGE].num = FIGHTING_MODE_BULLET_NUM;
|
|
inventory_[IS_RPG].num = FIGHTING_MODE_BULLET_NUM;
|
|
inventory_[IS_ICE].num = FIGHTING_MODE_BULLET_NUM;
|
|
}
|
|
|
|
void Creature::FindLocationWithTarget(Entity* target, ColliderComponent* target_collider)
|
|
{
|
|
a8::Vec2 old_pos = GetPos();
|
|
a8::Vec2 new_pos = GetPos();
|
|
AabbCollider a_collider;
|
|
GetAabbBox(a_collider);
|
|
{
|
|
if (target_collider) {
|
|
bool ret = a_collider.CalcSafePoint(target_collider, new_pos);
|
|
if (!ret) {
|
|
A8_ABORT();
|
|
}
|
|
} else {
|
|
AabbCollider aabb_collider;
|
|
target->GetAabbBox(aabb_collider);
|
|
bool ret = a_collider.CalcSafePoint(&aabb_collider, new_pos);
|
|
if (!ret) {
|
|
A8_ABORT();
|
|
}
|
|
}
|
|
}
|
|
a8::Vec2 new_pos_dir = a8::Vec2::UP;
|
|
//特殊情况处理
|
|
if (new_pos.ManhattanDistance(old_pos) > 0.0001f) {
|
|
new_pos_dir = new_pos - old_pos;
|
|
new_pos_dir.Normalize();
|
|
}
|
|
float distance = (new_pos - old_pos).Norm();
|
|
for (int i = distance; i < 10000000; i += 5) {
|
|
SetPos(old_pos + new_pos_dir * i);
|
|
room->grid_service->MoveCreature(this);
|
|
|
|
Entity* building = nullptr;
|
|
std::set<GridCell*> tmp_grids;
|
|
room->grid_service->GetAllCellsByXy(room, GetX(), GetY(), tmp_grids);
|
|
room->grid_service->TraverseAllLayerEntityList
|
|
(
|
|
room->GetRoomIdx(),
|
|
tmp_grids,
|
|
[this, &building] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
{
|
|
if (TestCollision(room, entity)) {
|
|
building = entity;
|
|
stop = true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
if (!building) {
|
|
bool is_collision = false;
|
|
std::set<ColliderComponent*> colliders;
|
|
room->map_service->GetColliders(room, GetX(), GetY(), colliders);
|
|
for (ColliderComponent* collider : colliders) {
|
|
if (TestCollision(room, collider)) {
|
|
is_collision = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!is_collision) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Creature::FindLocation()
|
|
{
|
|
ColliderComponent* target_collider = nullptr;
|
|
if (CollisonDetectionAndGetCollider(&target_collider)) {
|
|
if (!target_collider) {
|
|
A8_ABORT();
|
|
}
|
|
}
|
|
if (target_collider) {
|
|
FindLocationWithTarget(target_collider->owner, target_collider);
|
|
}
|
|
}
|
|
|
|
bool Creature::Attackable(Room* room)
|
|
{
|
|
if (dead) {
|
|
return false;
|
|
}
|
|
if (a8::HasBitFlag(status, CS_Disable)) {
|
|
return false;
|
|
}
|
|
if (IsInvincible()) {
|
|
return false;
|
|
}
|
|
if (HasBuffEffect(kBET_Hide)) {
|
|
return false;
|
|
}
|
|
if (HasBuffEffect(kBET_Driver)) {
|
|
return false;
|
|
}
|
|
if (HasBuffEffect(kBET_Passenger)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Creature::ReceiveExplosionDmg(Explosion* explosion)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool Creature::CheckCollision()
|
|
{
|
|
Global::last_collider = nullptr;
|
|
if (room->OverBorder(GetPos(), GetRadius())){
|
|
++collision_times_;
|
|
return true;
|
|
}
|
|
if (HasBuffEffect(kBET_ThroughWall) ||
|
|
HasBuffEffect(kBET_Fly)) {
|
|
return false;
|
|
}
|
|
|
|
std::set<ColliderComponent*> colliders;
|
|
room->map_service->GetColliders(room, GetX(), GetY(), colliders);
|
|
|
|
AabbCollider aabb_box;
|
|
GetAabbBox(aabb_box);
|
|
for (ColliderComponent* collider : colliders) {
|
|
if (last_collision_door_ != collider->owner) {
|
|
switch (collider->owner->GetEntityType()) {
|
|
case ET_Obstacle:
|
|
{
|
|
Obstacle* obstacle = (Obstacle*)collider->owner;
|
|
if (!obstacle->CanThroughable(this)) {
|
|
if (
|
|
(collider->type == CT_Aabb && aabb_box.Intersect(collider)) ||
|
|
(collider->type == CT_Circle && self_collider_->Intersect(collider))
|
|
) {
|
|
if (obstacle->OnCollisionTrigger(this, collider) == 1) {
|
|
++collision_times_;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ET_Building:
|
|
{
|
|
if (
|
|
(collider->type == CT_Aabb && aabb_box.Intersect(collider)) ||
|
|
(collider->type == CT_Circle && self_collider_->Intersect(collider))
|
|
) {
|
|
Global::last_collider = collider;
|
|
++collision_times_;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
case ET_Dummy:
|
|
{
|
|
if (
|
|
(collider->type == CT_Aabb && aabb_box.Intersect(collider)) ||
|
|
(collider->type == CT_Circle && self_collider_->Intersect(collider))
|
|
) {
|
|
Global::last_collider = collider;
|
|
++collision_times_;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Creature::CheckAbilityUsed()
|
|
{
|
|
if (ability_.use_count() > 1) {
|
|
std::shared_ptr<Ability> old_val = ability_;
|
|
ability_ = std::make_shared<Ability>();
|
|
*ability_.get() = *(old_val.get());
|
|
}
|
|
}
|
|
|
|
void Creature::RefreshHP()
|
|
{
|
|
|
|
}
|
|
|
|
void Creature::ProcOnceChgAttrBuff(MetaData::Buff* buff_meta)
|
|
{
|
|
if (dead) {
|
|
return;
|
|
}
|
|
switch (buff_meta->int_param1) {
|
|
case kHAT_Hp:
|
|
{
|
|
if (buff_meta->int_param2 == 1) {
|
|
//绝对值
|
|
if (buff_meta->param3 > 0) {
|
|
AddHp(buff_meta->param3);
|
|
} else if (buff_meta->param3 < 0) {
|
|
DecHP(std::abs(buff_meta->param3), VP_Buff, "", buff_meta->i->buff_id());
|
|
}
|
|
} else if (buff_meta->int_param2 == 2) {
|
|
//百分比
|
|
float chg_hp = ability.hp * buff_meta->param3;
|
|
if (chg_hp > 0.0001f) {
|
|
AddHp(chg_hp);
|
|
} else if (chg_hp < 0.0001f){
|
|
DecHP(std::abs(chg_hp), VP_Buff, "", buff_meta->i->buff_id());
|
|
}
|
|
}
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
break;
|
|
case kHAT_SkillTime:
|
|
{
|
|
if (buff_meta->int_param2 == 1) {
|
|
for (auto& pair : skill_hash_) {
|
|
pair.second->Accelerate(buff_meta->int_param3);
|
|
}
|
|
} else if (buff_meta->int_param2 == 2) {
|
|
for (auto& pair : skill_hash_) {
|
|
int time = pair.second->GetCd() * buff_meta->param3;
|
|
pair.second->Accelerate(time);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Creature::TraverseBuff(std::function<void (Buff*, bool&)> func)
|
|
{
|
|
bool stop = false;
|
|
for (auto& buff : buff_list_) {
|
|
func(&buff, stop);
|
|
if (stop) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Creature::TrySummonHero(MetaData::Player* hero_meta, a8::Vec2 dir, a8::Vec2 born_pos, bool through_wall)
|
|
{
|
|
AabbCollider collider;
|
|
collider._min.x = -hero_meta->i->radius();
|
|
collider._min.y = -hero_meta->i->radius();
|
|
collider._max.x = hero_meta->i->radius();
|
|
collider._max.y = hero_meta->i->radius();
|
|
collider.MoveCenter(hero_meta->i->move_offset_x(), hero_meta->i->move_offset_y());
|
|
return !room->map_service->CollisionDetection
|
|
(
|
|
room,
|
|
through_wall,
|
|
born_pos,
|
|
&collider
|
|
);
|
|
}
|
|
|
|
Hero* Creature::InternalSummonHero(Buff* buff, MetaData::Player* hero_meta, a8::Vec2 dir, a8::Vec2 born_pos,
|
|
bool through_wall, int num, int life_time)
|
|
{
|
|
struct SummonHeroInfo
|
|
{
|
|
MetaData::Player* hero_meta = nullptr;
|
|
a8::Vec2 dir;
|
|
a8::Vec2 born_pos;
|
|
bool through_wall = false;
|
|
int num = 0;
|
|
int life_time = 0;
|
|
};
|
|
if (TrySummonHero(hero_meta, dir, born_pos, through_wall)) {
|
|
int delay_time = 0;
|
|
for (auto& tuple : hero_meta->pre_appear_effect) {
|
|
RoomObstacle* obstacle = room->CreateObstacle
|
|
(
|
|
std::get<0>(tuple),
|
|
born_pos.x,
|
|
born_pos.y
|
|
);
|
|
if (obstacle) {
|
|
obstacle->DestoryAt(std::get<1>(tuple));
|
|
}
|
|
delay_time = std::max(std::get<1>(tuple), delay_time);
|
|
}
|
|
SummonHeroInfo* summon_info = new SummonHeroInfo;
|
|
summon_info->hero_meta = hero_meta;
|
|
summon_info->dir = dir;
|
|
summon_info->born_pos = born_pos;
|
|
summon_info->through_wall = through_wall;
|
|
summon_info->num = num;
|
|
summon_info->life_time = life_time;
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
delay_time / FRAME_RATE_MS + NEXT_FRAME_TIMER,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(summon_info)
|
|
.SetParam2(buff->meta->i->buff_id()),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* sender = (Creature*)param.sender.GetUserData();
|
|
SummonHeroInfo* summon_info = (SummonHeroInfo*)param.param1.GetUserData();
|
|
Hero* hero = sender->room->CreateHero
|
|
(sender,
|
|
summon_info->hero_meta,
|
|
summon_info->born_pos,
|
|
summon_info->dir,
|
|
sender->team_id
|
|
);
|
|
if (!hero) {
|
|
return;
|
|
}
|
|
int buff_id = param.param2;
|
|
sender->RemoveSurplusHero(buff_id,
|
|
summon_info->hero_meta->i->id(),
|
|
summon_info->num);
|
|
sender->slave_heros_.push_back(std::make_tuple(buff_id, hero));
|
|
hero->room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
summon_info->life_time / FRAME_RATE_MS,
|
|
a8::XParams(),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
},
|
|
&hero->xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
|
|
});
|
|
#ifdef DEBUG
|
|
sender->SendDebugMsg(a8::Format("summon hero id:%d pos:%f,%f",
|
|
{
|
|
summon_info->hero_meta->i->id(),
|
|
summon_info->born_pos.x,
|
|
summon_info->born_pos.y
|
|
}));
|
|
#endif
|
|
},
|
|
&xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
SummonHeroInfo* summon_info = (SummonHeroInfo*)param.param1.GetUserData();
|
|
delete summon_info;
|
|
});
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::string Creature::DebugOutBuffList()
|
|
{
|
|
std::string data;
|
|
for (auto& itr : buff_list_) {
|
|
data += a8::Format("buffid:%d effect:%d\n",
|
|
{
|
|
itr.meta->i->buff_id(),
|
|
itr.meta->i->buff_effect()
|
|
});
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void Creature::AutoSwitchWeapon()
|
|
{
|
|
auto switch_func =
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
int weapon_idx = param.param1;
|
|
MetaData::Equip* weapon_meta = (MetaData::Equip*)param.param2.GetUserData();
|
|
if (!(c->GetCurrWeapon()->weapon_idx == weapon_idx &&
|
|
c->GetCurrWeapon()->meta == weapon_meta)) {
|
|
return;
|
|
}
|
|
|
|
bool switch_ok = false;
|
|
int slot_id = weapon_meta->i->_inventory_slot();
|
|
if (c->GetCurrWeapon()->ammo <= 0 && slot_id > 0) {
|
|
if (c->GetInventory(slot_id) > 0) {
|
|
} else {
|
|
int weapon_idx = c->GetCurrWeapon()->weapon_idx;
|
|
*c->GetCurrWeapon() = Weapon();
|
|
c->GetCurrWeapon()->weapon_idx = weapon_idx;
|
|
Weapon* next_weapon = c->ChooseNextSpecWeapon(slot_id);
|
|
if (!next_weapon) {
|
|
next_weapon = c->AutoChgWeapon();
|
|
}
|
|
if (next_weapon) {
|
|
c->SetCurrWeapon(next_weapon);
|
|
switch_ok = true;
|
|
} else {
|
|
A8_ABORT();
|
|
}
|
|
}
|
|
}
|
|
if (switch_ok) {
|
|
c->need_sync_active_player = true;
|
|
c->SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
c->CheckLoadingBullet();
|
|
};
|
|
a8::XParams param;
|
|
param.SetSender(this);
|
|
param.SetParam1(GetCurrWeapon()->weapon_idx);
|
|
param.SetParam2(GetCurrWeapon()->meta);
|
|
if (GetCurrWeapon()->meta->i->auto_switch_weapon_time() > 0) {
|
|
if (auto_switch_weapon_timer_) {
|
|
room->xtimer.DeleteTimer(auto_switch_weapon_timer_);
|
|
}
|
|
auto_switch_weapon_timer_ = room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
GetCurrWeapon()->meta->i->auto_switch_weapon_time() / FRAME_RATE_MS,
|
|
param,
|
|
switch_func,
|
|
&xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
c->auto_switch_weapon_timer_ = nullptr;
|
|
});
|
|
} else {
|
|
switch_func(param);
|
|
}
|
|
}
|
|
|
|
void Creature::CheckLoadingBullet()
|
|
{
|
|
if (GetCurrWeapon()->weapon_idx != 0 &&
|
|
GetCurrWeapon()->ammo <= 0 &&
|
|
!reload_delay_timer_) {
|
|
#ifdef DEBUG
|
|
if (IsPlayer()) {
|
|
a8::XPrintf("CheckLoadingBullet1\n", {});
|
|
}
|
|
#endif
|
|
if (GetCurrWeapon()->meta->i->reload_delay_time() > 0) {
|
|
reload_delay_timer_ = room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
GetCurrWeapon()->meta->i->reload_delay_time() / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(GetCurrWeapon()->weapon_id),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
int weapon_id = param.param1;
|
|
if (c->GetCurrWeapon()->weapon_id == weapon_id) {
|
|
#ifdef DEBUG
|
|
if (c->IsPlayer()) {
|
|
a8::XPrintf("CheckLoadingBullet3\n", {});
|
|
}
|
|
#endif
|
|
c->AutoLoadingBullet();
|
|
}
|
|
},
|
|
&xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
if (c->reload_delay_timer_) {
|
|
c->reload_delay_timer_ = nullptr;
|
|
}
|
|
});
|
|
} else {
|
|
#ifdef DEBUG
|
|
if (IsPlayer()) {
|
|
a8::XPrintf("CheckLoadingBullet2\n", {});
|
|
}
|
|
#endif
|
|
AutoLoadingBullet();
|
|
}
|
|
}
|
|
}
|
|
|
|
Weapon* Creature::ChooseNextSpecWeapon(int curr_weapon_slot_id)
|
|
{
|
|
Weapon* next_weapon = nullptr;
|
|
int data[][3]= {
|
|
{SPEC1_IS_BEGIN, SPEC1_IS_END, 0},
|
|
{SPEC2_IS_BEGIN, SPEC2_IS_END, 0},
|
|
{SPEC3_IS_BEGIN, SPEC3_IS_END, 0}
|
|
};
|
|
int idx = -1;
|
|
for (int i = 0; i < sizeof(data) /sizeof(data[0]); ++i) {
|
|
int start_id = data[i][0];
|
|
int end_id = data[i][1];
|
|
if (curr_weapon_slot_id >= start_id &&
|
|
curr_weapon_slot_id <= end_id) {
|
|
idx = i;
|
|
data[i][2] = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (idx > -1) {
|
|
for (int i = 0; i < sizeof(data) /sizeof(data[0]); ++i) {
|
|
int real_i = (i + idx) % (sizeof(data) /sizeof(data[0]));
|
|
int start_id = data[real_i][0];
|
|
int end_id = data[real_i][1];
|
|
int flag = data[real_i][2];
|
|
next_weapon = ChooseNextWeapon(flag ? curr_weapon_slot_id : start_id, start_id, end_id);
|
|
if (next_weapon) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return next_weapon;
|
|
}
|
|
|
|
void Creature::OnBattleStart(Room* room)
|
|
{
|
|
#ifdef DEBUG1
|
|
if (IsPlayer()) {
|
|
SendDebugMsg(a8::Format("OnBattleStart clear buff", {}));
|
|
}
|
|
#endif
|
|
int try_count = 0;
|
|
while (try_count++ < 10) {
|
|
std::vector<int> del_buffs;
|
|
del_buffs.reserve(buff_list_.size());
|
|
for (auto& buff : buff_list_) {
|
|
if (!buff.meta->i->post_battle_valid()) {
|
|
if (!buff.skill_meta || buff.skill_meta->i->skill_type() != kPassiveSkill) {
|
|
del_buffs.push_back(buff.buff_uniid);
|
|
}
|
|
}
|
|
}
|
|
for (auto itr = del_buffs.rbegin(); itr != del_buffs.rend(); ++itr) {
|
|
RemoveBuffByUniId(*itr);
|
|
}
|
|
if (del_buffs.empty()) {
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if (try_count > 5) {
|
|
A8_ABORT();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool Creature::CanFollow(Creature* follower)
|
|
{
|
|
#ifdef DEBUG1
|
|
return false;
|
|
#endif
|
|
if (follower->GetUniId() == GetUniId()) {
|
|
return false;
|
|
}
|
|
#if 0
|
|
if (!follower->IsPlayer()) {
|
|
return false;
|
|
}
|
|
#endif
|
|
if (follower->team_id != team_id) {
|
|
return false;
|
|
}
|
|
#if 0
|
|
if (follower->follow_target.Get()) {
|
|
return false;
|
|
}
|
|
#endif
|
|
if (follower->HasBuffEffect(kBET_Jump)) {
|
|
return false;
|
|
}
|
|
if (!follower->HasBuffEffect(kBET_Fly) && room->GetGasData().GetGasMode() != GasInactive) {
|
|
return false;
|
|
}
|
|
|
|
#if 0
|
|
if (!IsPlayer()) {
|
|
return false;
|
|
}
|
|
#endif
|
|
if (HasBuffEffect(kBET_Jump)) {
|
|
return false;
|
|
}
|
|
if (!HasBuffEffect(kBET_Fly) && room->GetGasData().GetGasMode() != GasInactive) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Creature::FollowToTarget()
|
|
{
|
|
if (HasBuffEffect(kBET_Jump) &&
|
|
follow_target.Get() &&
|
|
follow_target.Get()->GetChgAttackDirTimes() > follow_target_last_chg_move_dir_times_) {
|
|
#if 1
|
|
{
|
|
#else
|
|
if (GetPos().ManhattanDistance(follow_target.Get()->GetPos()) > 70) {
|
|
#endif
|
|
a8::Vec2 dir = a8::Vec2::UP;
|
|
dir.Rotate(a8::RandAngle());
|
|
dir.Normalize();
|
|
a8::Vec2 target_pos = follow_target.Get()->GetPos() + dir * (30 + (rand() % 10));
|
|
if (GetPos().ManhattanDistance(target_pos) > 5) {
|
|
a8::Vec2 move_dir = target_pos - GetPos();
|
|
move_dir.Normalize();
|
|
SetMoveDir(move_dir);
|
|
}
|
|
follow_target_last_chg_move_dir_times_ = follow_target.Get()->GetChgAttackDirTimes();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Creature::DoRecoilForce(int distance)
|
|
{
|
|
if (distance > 0) {
|
|
a8::Vec2 old_move_dir = GetMoveDir();
|
|
MustBeAddBuff(this, kRecoilBuffId);
|
|
SetMoveDir(GetAttackDir() * -1);
|
|
_UpdateMove(distance);
|
|
SetMoveDir(old_move_dir);
|
|
}
|
|
}
|
|
|
|
void Creature::WinSkillExp(int win_exp)
|
|
{
|
|
if (!IsHuman()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
float Creature::GetAttrAbs(int attr_id)
|
|
{
|
|
float val = 0;
|
|
if (GetAbility()){
|
|
val += GetAbility()->GetAttrAbs(attr_id);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
float Creature::GetAttrRate(int attr_id)
|
|
{
|
|
float val = 0;
|
|
if (GetAbility()){
|
|
val += GetAbility()->GetAttrRate(attr_id);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
void Creature::RecalcDtoAttr()
|
|
{
|
|
if (!IsHuman()) {
|
|
return;
|
|
}
|
|
if (GetCurrWeapon()) {
|
|
if (GetBattleContext()) {
|
|
}
|
|
} else {
|
|
if (GetBattleContext()) {
|
|
}
|
|
}
|
|
}
|
|
|
|
void Creature::SetBattleContext(std::shared_ptr<BattleDataContext> c)
|
|
{
|
|
battle_context_ = c;
|
|
}
|
|
|
|
|
|
bool Creature::HasSpecMove()
|
|
{
|
|
return GetBuffByEffectId(kBET_JumpTo) ||
|
|
GetBuffByEffectId(kBET_BePull);
|
|
}
|
|
|
|
void Creature::_UpdateSpecMove()
|
|
{
|
|
if (!HasSpecMove()) {
|
|
return;
|
|
}
|
|
bool move_end = false;
|
|
float target_distance = target_pos.Distance(GetPos());
|
|
if (target_distance <= 0.000001f) {
|
|
move_end = true;
|
|
} else {
|
|
a8::Vec2 old_move_dir = GetMoveDir();
|
|
a8::Vec2 move_dir = target_pos - GetPos();
|
|
move_dir.Normalize();
|
|
SetMoveDir(move_dir);
|
|
bool is_collision = false;
|
|
std::function<bool ()> old_on_move_collision = on_move_collision;
|
|
on_move_collision =
|
|
[&is_collision] () {
|
|
is_collision = true;
|
|
return false;
|
|
};
|
|
|
|
_UpdateMove(std::min((int)target_distance, (int)GetSpeed()));
|
|
|
|
on_move_collision = old_on_move_collision;
|
|
move_dir = old_move_dir;
|
|
target_distance = target_pos.Distance(GetPos());
|
|
if (is_collision || target_distance <= 1.0001f) {
|
|
move_end = true;
|
|
}
|
|
}
|
|
if (move_end) {
|
|
Buff* buff = GetBuffByEffectId(kBET_JumpTo);
|
|
if (buff) {
|
|
if (CurrentSkill() &&
|
|
buff->skill_meta == CurrentSkill()->meta &&
|
|
!CurrentSkill()->meta->phases.empty()) {
|
|
std::set<Creature*> target_list;
|
|
metatable::Skill* mutable_skill_meta = (metatable::Skill*)CurrentSkill()->meta->i;
|
|
float old_skill_distance = CurrentSkill()->meta->i->skill_distance();
|
|
mutable_skill_meta->set_skill_distance(CurrentSkill()->meta->phases[0].param1.GetDouble());
|
|
SelectSkillTargets(CurrentSkill(), GetPos(), target_list);
|
|
mutable_skill_meta->set_skill_distance(old_skill_distance);
|
|
|
|
TriggerBuff(CurrentSkill(), target_list, kBTT_SkillHit);
|
|
}
|
|
RemoveBuffByEffectId(kBET_JumpTo);
|
|
}
|
|
buff = GetBuffByEffectId(kBET_BePull);
|
|
if (buff) {
|
|
MetaData::Buff* new_buff = MetaMgr::Instance()->GetBuff(1039);
|
|
if (new_buff) {
|
|
AddBuff(this, new_buff);
|
|
}
|
|
RemoveBuffByEffectId(kBET_BePull);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Creature::AutoNavigation(a8::Vec2 target_pos, float speed)
|
|
{
|
|
float distance = GetPos().Distance(target_pos);
|
|
if (distance < 0.001f) {
|
|
return;
|
|
}
|
|
if (speed < 0.001f) {
|
|
return;
|
|
}
|
|
|
|
struct NavContext
|
|
{
|
|
CreatureWeakPtr c;
|
|
a8::Vec2 src_pos;
|
|
a8::Vec2 target_pos;
|
|
a8::Vec2 dir;
|
|
int exec_frameno = 0;
|
|
float speed = 0.0f;
|
|
float distance = 0.0f;
|
|
};
|
|
NavContext* context = new NavContext();
|
|
context->c = GetWeakPtrRef();
|
|
context->src_pos = GetPos();
|
|
context->target_pos = target_pos;
|
|
context->dir = (target_pos - GetPos());
|
|
context->dir.Normalize();
|
|
context->speed = speed;
|
|
context->distance = distance;
|
|
|
|
IncDisableMoveTimes();
|
|
room->xtimer.AddRepeatTimerAndAttach
|
|
(1,
|
|
a8::XParams()
|
|
.SetSender(context),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
NavContext* context = (NavContext*)param.sender.GetUserData();
|
|
++context->exec_frameno;
|
|
|
|
Room* room = context->c.Get()->room;
|
|
Creature* c = context->c.Get();
|
|
|
|
a8::Vec2 dir = context->target_pos - c->GetPos();
|
|
dir.Normalize();
|
|
a8::Vec2 old_pos = c->GetPos();
|
|
float move_length = context->speed / (float)SERVER_FRAME_RATE;
|
|
float move_distance = std::min(move_length * context->exec_frameno, context->distance);
|
|
a8::Vec2 new_pos = context->src_pos + dir * move_distance;
|
|
c->SetPos(new_pos);
|
|
|
|
#ifdef DEBUG
|
|
a8::XPrintf("speed:%d src_pos:%f,%f new_pos:%f,%f move_distance:%f\n",
|
|
{
|
|
context->speed,
|
|
context->src_pos.x,
|
|
context->src_pos.y,
|
|
new_pos.x,
|
|
new_pos.y,
|
|
move_distance
|
|
});
|
|
#endif
|
|
bool ok = false;
|
|
if (c->CheckCollision()) {
|
|
c->SetPos(old_pos);
|
|
ok = true;
|
|
} else {
|
|
ok = std::abs(move_distance - context->distance) < 0.0001f;
|
|
room->grid_service->MoveCreature(c);
|
|
}
|
|
|
|
if (ok || c->dead) {
|
|
c->DecDisableMoveTimes();
|
|
delete context;
|
|
room->xtimer.DeleteTimer(room->xtimer.GetRunningTimer());
|
|
}
|
|
},
|
|
&xtimer_attacher.timer_list_);
|
|
}
|
|
|
|
|
|
void Creature::AddTraceBullet(int bullet_uniid, int target_uniid, int gun_id)
|
|
{
|
|
trace_bullet_hash_[bullet_uniid] = std::make_tuple(target_uniid, gun_id);
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
SERVER_FRAME_RATE * 10,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(bullet_uniid),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
c->trace_bullet_hash_.erase(param.param1);
|
|
Entity* entity = c->room->GetEntityByUniId(param.param1);
|
|
if (entity && entity->GetEntityType() == ET_Bullet) {
|
|
Bullet* bullet = (Bullet*)entity;
|
|
bullet->ForceRemove();
|
|
}
|
|
},
|
|
&xtimer_attacher.timer_list_);
|
|
}
|
|
|
|
void Creature::LockAttackDir(int time)
|
|
{
|
|
IncDisableAttackDirTimes();
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
time / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
},
|
|
&xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Creature* c = (Creature*)param.sender.GetUserData();
|
|
c->DecDisableAttackDirTimes();
|
|
}
|
|
);
|
|
}
|
|
|
|
void Creature::PullTarget(const a8::Vec2& target_pos)
|
|
{
|
|
|
|
}
|