aozhiwei eb337405de 1
2019-07-19 11:14:14 +08:00

2040 lines
68 KiB
C++

#include "precompile.h"
#include <float.h>
#include <a8/mutable_xobject.h>
#include <a8/collision.h>
#include "human.h"
#include "cs_proto.pb.h"
#include "metamgr.h"
#include "room.h"
#include "bullet.h"
#include "collider.h"
#include "loot.h"
#include "building.h"
#include "hero.h"
#include "app.h"
#include "roommgr.h"
#include "android.h"
#include "gamelog.h"
#include "typeconvert.h"
#include "obstacle.h"
#include "framework/cpp/utils.h"
#include "framework/cpp/httpclientpool.h"
const int GRASS_HIDE_BUFF_ID = 1016;
Human::Human():Entity()
{
default_weapon.weapon_idx = 0;
default_weapon.weapon_id = 12002;
default_weapon.weapon_lv = 1;
default_weapon.ammo = 1;
default_weapon.meta = MetaMgr::Instance()->GetEquip(default_weapon.weapon_id);
default_weapon.Recalc();
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.weapon_lv = 0;
weapon.ammo = 0;
}
weapons[0] = default_weapon;
curr_weapon = &weapons[0];
inventory_[IS_1XSCOPE] = 1;
if (MetaMgr::Instance()->fighting_mode) {
inventory_[IS_9MM] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_556MM] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_762MM] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_12GAUGE] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_RPG] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_TANK] = FIGHTING_MODE_BULLET_NUM;
}
}
Human::~Human()
{
}
void Human::Initialize()
{
Entity::Initialize();
skill_xtimer_attacher_.xtimer = &room->xtimer;
RecalcSelfCollider();
volume_ = meta->volume;
observers_.insert(this);
}
float Human::GetSpeed()
{
if (a8::HasBitFlag(status, HS_Assaulting)) {
Buff* buff = GetBuffByEffectId(BET_Assault);
if (buff) {
return buff->meta->param2;
}
}
float speed = ability.speed;
if (action_type == AT_Reload) {
speed = ability.reload_speed;
} else if (shot_hold) {
if (curr_weapon->weapon_idx == GUN_SLOT1 ||
curr_weapon->weapon_idx == GUN_SLOT2) {
speed = ability.shot_speed;
}
}
speed = (speed + buff_attr_abs_[HAT_Speed]) * (1 + buff_attr_rate_[HAT_Speed]);
return std::max(speed, 1.0f);
}
float Human::GetSpeed4()
{
return ability.speed;
}
void Human::FillMFObjectPart(cs::MFObjectPart* part_data)
{
part_data->set_object_type(ET_Player);
cs::MFPlayerPart* p = part_data->mutable_union_obj_1();
p->set_obj_uniid(entity_uniid);
TypeConvert::ToPb(pos, p->mutable_pos());
TypeConvert::ToPb(attack_dir, p->mutable_dir());
p->set_health(GetHP());
p->set_max_health(GetMaxHP());
}
void Human::FillMFObjectFull(cs::MFObjectFull* full_data)
{
full_data->set_object_type(ET_Player);
cs::MFPlayerFull* p = full_data->mutable_union_obj_1();
p->set_obj_uniid(entity_uniid);
TypeConvert::ToPb(pos, p->mutable_pos());
TypeConvert::ToPb(attack_dir, p->mutable_dir());
p->set_health(GetHP());
p->set_max_health(GetMaxHP());
p->set_dead(dead);
#if 0
p->set_downed(downed);
#endif
p->set_disconnected(disconnected);
if (skin_jlf.skin_id != 0) {
skin_jlf.ToPB(p->mutable_skin());
} else {
skin.ToPB(p->mutable_skin());
}
p->set_backpack(backpack);
p->set_helmet(helmet);
p->set_chest(chest);
curr_weapon->ToPB(p->mutable_weapon());
#if 0
p->set_energy_shield(energy_shield);
{
p->set_max_energy_shield(max_energy_shield);
}
#endif
}
void Human::FillMFPlayerStats(cs::MFPlayerStats* stats_pb)
{
#if 0
stats_pb->set_player_id(entity_uniid);
stats_pb->set_player_avatar_url(avatar_url);
if (!dead) {
stats_pb->set_time_alive(room->frame_no * 1000.0f / SERVER_FRAME_RATE);
} else {
stats_pb->set_time_alive(dead_frameno * 1000.0f / SERVER_FRAME_RATE);
}
stats_pb->set_kills(stats.kills);
stats_pb->set_damage_amount(stats.damage_amount_out);
stats_pb->set_heal_amount(stats.heal_amount);
stats_pb->set_history_time_alive(stats.history_time_alive);
stats_pb->set_history_kills(stats.history_kills);
stats_pb->set_history_damage_amount(stats.history_damage_amount);
stats_pb->set_history_heal_amount(stats.history_heal_amount);
stats_pb->set_gold(stats.gold);
stats_pb->set_score(stats.score);
stats_pb->set_dead(dead);
stats_pb->set_killer_id(stats.killer_id);
stats_pb->set_killer_name(stats.killer_name);
stats_pb->set_account_id(account_id);
#endif
}
void Human::GetAabbBox(AabbCollider& aabb_box)
{
if (!meta) {
abort();
}
aabb_box.active = true;
aabb_box.owner = this;
aabb_box._min.x = -meta->i->radius();
aabb_box._min.y = -meta->i->radius();
aabb_box._max.x = meta->i->radius();
aabb_box._max.y = meta->i->radius();
}
void Human::FillMFTeamData(cs::MFTeamData* team_data)
{
team_data->set_player_id(entity_uniid);
team_data->set_name(name);
TypeConvert::ToPb(pos, team_data->mutable_pos());
TypeConvert::ToPb(attack_dir, team_data->mutable_dir());
team_data->set_health(GetHP());
team_data->set_max_health(GetMaxHP());
team_data->set_disconnected(false);
team_data->set_dead(dead);
#if 0
team_data->set_downed(downed);
#endif
}
void Human::Shot()
{
if (!curr_weapon->meta) {
return;
}
if (curr_weapon->ammo <= 0) {
AutoLoadingBullet();
return;
}
if (action_type == AT_Reload) {
CancelAction();
}
DirectShot(curr_weapon->meta, 0);
if (curr_weapon->meta->NeedTrace()) {
room->xtimer.AddDeadLineTimerAndAttach(curr_weapon->meta->i->time() * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this)
.SetParam1(curr_weapon->meta->i->id())
.SetParam2(shot_target_id)
.SetParam3(a8::MakeInt64(pos.x, pos.y)),
Bullet::ProcMissible,
&xtimer_attacher.timer_list_);
}
--curr_weapon->ammo;
if (curr_weapon->ammo <= 0) {
AutoLoadingBullet();
}
last_shot_frameno_ = room->frame_no;
need_sync_active_player = true;
}
void Human::DirectShot(MetaData::Equip* bullet_meta, int skill_id)
{
for (size_t i = 0; i < bullet_meta->bullet_born_offset.size(); ++i) {
auto& tuple = bullet_meta->bullet_born_offset[i];
room->xtimer.AddDeadLineTimerAndAttach(std::get<0>(tuple) / FRAME_RATE_MS,
a8::XParams()
.SetSender(this)
.SetParam1(bullet_meta)
.SetParam2(skill_id)
.SetParam3(i),
[] (const a8::XParams& param)
{
Human* sender = (Human*)param.sender.GetUserData();
MetaData::Equip* bullet_meta = (MetaData::Equip*)param.param1.GetUserData();
if (sender) {
sender->InternalShot(bullet_meta,
param.param2,
param.param3);
}
},
&xtimer_attacher.timer_list_);
}
OnAttack();
}
void Human::RecalcSelfCollider()
{
if (!self_collider_) {
self_collider_ = new CircleCollider();
self_collider_->owner = this;
AddCollider(self_collider_);
}
self_collider_->pos = a8::Vec2();
self_collider_->rad = meta->i->radius();
}
bool Human::IsCollisionInMapService()
{
if (room->OverBorder(pos, meta->i->radius())){
return true;
}
std::set<ColliderComponent*> colliders;
room->map_service.GetColliders(pos.x, pos.y, colliders);
for (const ColliderComponent* collider : colliders) {
switch (collider->owner->entity_type) {
case ET_Obstacle:
case ET_Building:
{
if (!collider->owner->dead && TestCollision((ColliderComponent*)collider)) {
if (last_collision_door != collider->owner) {
return true;
}
}
}
break;
default:
break;
}
}
return false;
}
void Human::FindPath()
{
a8::Vec2 old_pos = pos;
{
float up_dot = a8::Vec2::UP.Dot(move_dir);
bool at_left_side = a8::Vec2::LEFT.Dot(move_dir) > 0.0001f;
if (std::abs(up_dot) <= 0.001f) { //相互垂直
//向上
pos = old_pos + a8::Vec2::UP;
if (!IsCollisionInMapService()) {
return;
} else {
//向下
pos = old_pos + a8::Vec2::DOWN;
if (!IsCollisionInMapService()) {
return;
}
}
} else if (up_dot > 0.001f) { //基本相同
pos = old_pos + (at_left_side ? a8::Vec2::LEFT : a8::Vec2::RIGHT);
if (!IsCollisionInMapService()) {
return;
} else {
//向上
pos = old_pos + a8::Vec2::UP;
if (!IsCollisionInMapService()) {
return;
}
}
} else if (up_dot < 0.001f) { //基本相反
pos = old_pos + (at_left_side ? a8::Vec2::LEFT : a8::Vec2::RIGHT);
if (!IsCollisionInMapService()) {
return;
} else {
//向下
pos = old_pos + a8::Vec2::DOWN;
if (!IsCollisionInMapService()) {
return;
}
}
}
}
pos = old_pos;
}
void Human::FindPathInMapService()
{
a8::Vec2 old_pos = pos;
{
float up_dot = a8::Vec2::UP.Dot(move_dir);
bool at_left_side = a8::Vec2::LEFT.Dot(move_dir) > 0.0001f;
if (std::abs(up_dot) <= 0.001f) { //相互垂直
//向上
pos = old_pos + a8::Vec2::UP;
if (!IsCollisionInMapService()) {
return;
} else {
//向下
pos = old_pos + a8::Vec2::DOWN;
if (!IsCollisionInMapService()) {
return;
}
}
} else if (up_dot > 0.001f) { //基本相同
pos = old_pos + (at_left_side ? a8::Vec2::LEFT : a8::Vec2::RIGHT);
if (!IsCollisionInMapService()) {
return;
} else {
//向上
pos = old_pos + a8::Vec2::UP;
if (!IsCollisionInMapService()) {
return;
}
}
} else if (up_dot < 0.001f) { //基本相反
pos = old_pos + (at_left_side ? a8::Vec2::LEFT : a8::Vec2::RIGHT);
if (!IsCollisionInMapService()) {
return;
} else {
//向下
pos = old_pos + a8::Vec2::DOWN;
if (!IsCollisionInMapService()) {
return;
}
}
}
}
pos = old_pos;
}
float Human::GetRadius()
{
return meta->i->radius();
}
float Human::GetHP()
{
return ability.hp;
}
float Human::GetMaxHP()
{
return ability.max_hp;
}
void Human::UpdateSkill()
{
if (skill_meta) {
if (curr_skill_phase < skill_meta->phases.size()) {
MetaData::SkillPhase* phase = &skill_meta->phases[curr_skill_phase];
if (phase->time_offset >= (int)((room->frame_no - last_use_skill_frameno_) * FRAME_RATE_MS)) {
ProcSkillPhase(phase);
++curr_skill_phase;
}
} else {
playing_skill = false;
}
}
}
void Human::SyncAroundPlayers()
{
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
hum->new_objects.insert(this);
}
}
}
void Human::AutoLoadingBullet(bool manual)
{
if ((curr_weapon->ammo <= 0 ||
(manual && curr_weapon->ammo < curr_weapon->GetClipVolume()))
) {
StartAction(AT_Reload,
curr_weapon->meta->i->reload_time(),
curr_weapon->weapon_id,
curr_weapon->weapon_idx);
return;
}
}
void Human::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;
}
this->action_type = action_type;
this->action_frameno = room->frame_no;
this->action_duration = action_duration;
this->action_item_id = item_id;
this->action_target_id = target_id;
need_sync_active_player = true;
}
void Human::CancelAction()
{
ResetAction();
}
void Human::ResetAction()
{
action_type = AT_None;
action_duration = 0;
action_frameno = 0;
action_item_id = 0;
action_target_id = 0;
need_sync_active_player = true;
}
void Human::BeKill(int killer_id, const std::string& killer_name, int weapon_id)
{
if (!dead && !room->game_over) {
lethal_weapon = weapon_id;
{
Entity* entity = room->GetEntityByUniId(killer_id);
if (entity && entity->entity_type == ET_Player) {
Human* killer = (Human*)entity;
killer->stats.dead_times++;
}
}
++stats.dead_times;
stats.killer_id = killer_id;
stats.killer_name = killer_name;
stats.weapon_id = weapon_id;
dead = true;
dead_frameno = room->frame_no;
room->xtimer.AddDeadLineTimerAndAttach(MetaMgr::Instance()->revive_time * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Human* hum = (Human*)param.sender.GetUserData();
hum->Revive();
},
&xtimer_attacher.timer_list_
);
room->frame_event.AddDead(this);
room->OnHumanDie(this);
{
OnLeaveGrass();
RemoveBuffById(GRASS_HIDE_BUFF_ID);
}
SyncAroundPlayers();
}
}
void Human::DecHP(float dec_hp, int killer_id, const std::string& killer_name, int weapon_id)
{
{
float old_health = GetHP();
ability.hp = std::max(0.0f, GetHP() - dec_hp);
if (GetHP() - old_health > 0.001f) {
stats.damage_amount_in += GetHP() - old_health;
}
if (GetHP() <= 0.0001f && !dead) {
BeKill(killer_id, killer_name, weapon_id);
}
}
last_attacker_id = killer_id;
last_attacker_name = killer_name;
last_attacker_weapon_id = weapon_id;
SyncAroundPlayers();
}
void Human::AddToNewObjects(Entity* entity)
{
new_objects.insert(entity);
}
void Human::AddToPartObjects(Entity* entity)
{
part_objects.insert(entity);
}
void Human::RemovePartObjects(Entity* entity)
{
part_objects.erase(entity);
}
void Human::RemoveObjects(Entity* entity)
{
del_objects.insert(entity->entity_uniid);
}
void Human::AddOutObjects(Entity* entity)
{
out_objects.insert(entity->entity_uniid);
}
void Human::RemoveOutObjects(Entity* entity)
{
out_objects.erase(entity->entity_uniid);
}
bool Human::HasLiveTeammate()
{
if (team_members) {
for (auto& hum : *team_members) {
if (hum != this && !hum->dead) {
return true;
}
}
}
return false;
}
void Human::DoSkill()
{
if (skill_meta && GetSkillLeftTime() <= 0 && !a8::HasBitFlag(status, HS_Assaulting)) {
use_skill = false;
curr_skill_phase = 0;
skill_dir = a8::Vec2();
skill_target_pos = a8::Vec2();
skill_param1 = 0.0f;
playing_skill = true;
last_use_skill_frameno_ = room->frame_no;
Entity* entity = room->GetEntityByUniId(skill_target_id);
if (entity && entity->entity_type == ET_Player) {
Human* hum = (Human*)entity;
std::set<Entity*> target_list;
skill_target_pos = hum->pos;
SelectSkillTargets(hum->pos, target_list);
TriggerBuff(target_list, BTT_UseSkill);
if (!skill_meta->phases.empty() && skill_meta->phases[0].time_offset <= 0) {
UpdateSkill();
}
} else {
playing_skill = false;
}
OnAttack();
}
use_skill = false;
need_sync_active_player = true;
}
void Human::FindLocation()
{
Entity* target = nullptr;
for (auto& grid : grid_list) {
for (Entity* entity : grid->entity_list) {
switch (entity->entity_type) {
case ET_Obstacle:
{
if (!target) {
if (TestCollision(entity)) {
target = entity;
}
}
}
break;
case ET_Building:
{
if (!target || target->entity_type != ET_Building) {
AabbCollider aabb_box;
entity->GetAabbBox(aabb_box);
if (TestCollision(&aabb_box)) {
target = entity;
}
}
}
break;
default:
break;
}
}
}
if (target) {
FindLocationWithTarget(target);
}
}
void Human::RefreshView()
{
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
hum->AddToNewObjects(this);
hum->AddToPartObjects(this);
AddToNewObjects(hum);
AddToPartObjects(hum);
}
for (Entity* entity : cell->entity_list) {
switch (entity->entity_type) {
case ET_Building:
case ET_Obstacle:
case ET_Hero:
{
AddToNewObjects(entity);
}
break;
default:
{
}
break;
}
}
}
}
void Human::OnGridListChange(std::set<GridCell*>& old_grid_list,
std::set<GridCell*>& inc_grid_list,
std::set<GridCell*>& dec_grid_list
)
{
for (GridCell* cell : inc_grid_list) {
for (Human* hum : cell->human_list) {
if (!room->grid_service.HumanInGridList(hum, old_grid_list)) {
hum->AddToNewObjects(this);
hum->AddToPartObjects(this);
hum->RemoveOutObjects(this);
AddToNewObjects(hum);
AddToPartObjects(hum);
RemoveOutObjects(hum);
}
}
for (Entity* entity : cell->entity_list) {
if (!room->grid_service.EntityInGridList(entity, old_grid_list)) {
switch (entity->entity_type) {
case ET_Building:
case ET_Obstacle:
case ET_Hero:
{
AddToNewObjects(entity);
RemoveOutObjects(entity);
}
break;
default:
{
}
break;
}
}
}
}
for (GridCell* cell : dec_grid_list) {
for (Human* hum : cell->human_list) {
if (!room->grid_service.HumanInGridList(hum, grid_list)) {
AddOutObjects(hum);
hum->AddOutObjects(this);
}
}
for (Entity* entity : cell->entity_list) {
if (!room->grid_service.EntityInGridList(entity, grid_list)) {
switch (entity->entity_type) {
case ET_Building:
case ET_Obstacle:
case ET_Hero:
{
AddOutObjects(entity);
}
break;
default:
{
}
break;
}
}
}
}
}
void Human::FillMFActivePlayerData(cs::MFActivePlayerData* player_data)
{
{
player_data->set_action_type(action_type);
if (action_type != AT_None) {
int duration = std::max(0,
action_duration -
(int)((room->frame_no - action_frameno) * 1.0f / SERVER_FRAME_RATE) * 1000
);
player_data->set_action_item_id(action_item_id);
player_data->set_action_duration(duration);
player_data->set_action_target_id(action_target_id);
}
}
skin.ToPB(player_data->mutable_skin());
player_data->set_backpack(backpack);
player_data->set_helmet(helmet);
player_data->set_chest(chest);
player_data->set_health(GetHP());
player_data->set_max_health(GetMaxHP());
player_data->set_cur_weapon_idx(curr_weapon->weapon_idx);
player_data->set_cur_scope(curr_scope_idx);
for (auto& weapon : weapons) {
auto p = player_data->add_weapons();
weapon.ToPB(p);
}
for (auto& num : inventory_) {
player_data->add_inventory(num);
}
#if 0
player_data->set_energy_shield(energy_shield);
{
player_data->set_max_energy_shield(max_energy_shield);
}
#endif
if (skill_meta) {
player_data->set_skill_left_time(GetSkillLeftTime());
player_data->set_skill_cd_time(GetSkillCd());
}
}
void Human::FillMFGasData(cs::MFGasData* gas_data)
{
gas_data->set_mode(room->gas_data.gas_mode);
if (room->gas_data.gas_mode == GasInactive) {
long long duration = MetaMgr::Instance()->gas_inactive_time * SERVER_FRAME_RATE -
(room->frame_no - room->gas_data.gas_start_frameno);
gas_data->set_duration(std::max(duration * 50, (long long)1000) / 1000);
} else {
#if 0
if (room->gas_data.old_area_meta->i->wait_time() <= 0) {
gas_data->set_duration(0);
} else {
long long duration = room->gas_data.old_area_meta->i->wait_time() * 20 -
(room->frame_no - room->gas_data.gas_start_frameno);
gas_data->set_duration(std::max(duration * 50, (long long)1000) / 1000);
}
#endif
}
TypeConvert::ToPb(room->gas_data.pos_old, gas_data->mutable_pos_old());
TypeConvert::ToPb(room->gas_data.pos_new, gas_data->mutable_pos_new());
gas_data->set_rad_old(room->gas_data.rad_old);
gas_data->set_rad_new(room->gas_data.rad_new);
}
bool Human::CanSee(const Human* hum) const
{
return room->grid_service.InView(grid_id, hum->grid_id);
}
void Human::RecalcVolume()
{
MetaData::Equip* backpack_meta = MetaMgr::Instance()->GetEquip(backpack);
if (backpack_meta) {
for (size_t i = 0; i < backpack_meta->volume.size(); ++i) {
volume_[i] = meta->volume[i] + backpack_meta->volume[i];
}
}
}
int Human::GetInventory(int slot_id)
{
if (!IsValidSlotId(slot_id)) {
abort();
}
return inventory_[slot_id];
}
void Human::AddInventory(int slot_id, int num)
{
assert(num > 0);
if (!IsValidSlotId(slot_id)) {
abort();
}
inventory_[slot_id] += num;
}
void Human::DecInventory(int slot_id, int num)
{
assert(num > 0);
if (!IsValidSlotId(slot_id)) {
abort();
}
inventory_[slot_id] -= num;
}
int Human::GetVolume(int slot_id)
{
if (!IsValidSlotId(slot_id)) {
abort();
}
return volume_[slot_id];
}
void Human::RecoverHp(int inc_hp)
{
if (!dead) {
ability.hp += inc_hp;
ability.hp = std::max(GetHP(), GetMaxHP());
}
}
void Human::SummonHero()
{
#if 0
Hero* hero = room->CreateHero(this);
if (hero) {
summon_hero_frameno_ = room->frame_no;
a8::SetBitFlag(status, HS_SummonHero);
room->xtimer.AddDeadLineTimerAndAttach(skill_meta->last_time * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this)
.SetParam1(hero->entity_uniid),
[] (const a8::XParams& param)
{
},
&skill_xtimer_attacher_.timer_list_,
[] (const a8::XParams& param)
{
Human* hum = (Human*)param.sender.GetUserData();
Entity* hero = hum->room->GetEntityByUniId(param.param1);
if (hero && hero->entity_type == ET_Hero) {
hum->room->RemoveObjectLater(hero);
}
a8::UnSetBitFlag(hum->status, HS_SummonHero);
hum->need_sync_active_player = true;
hum->BroadcastFullState();
}
);
need_sync_active_player = true;
BroadcastFullState();
}
#endif
}
void Human::AddObserver(Human* observer)
{
observers_.insert(observer);
}
void Human::RemoveObserver(Human* observer)
{
observers_.erase(observer);
}
void Human::SendUpdateMsg()
{
if (!follow_target_) {
if (send_msg_times == 0) {
room->FetchBuilding(this);
}
cs::MFActivePlayerData* active_player_data_pb = nullptr;
if (send_msg_times == 0 || need_sync_active_player) {
active_player_data_pb = new cs::MFActivePlayerData();
FillMFActivePlayerData(active_player_data_pb);
need_sync_active_player = false;
}
cs::SMUpdate* msg = room->frame_maker.MakeUpdateMsg(this);
if (send_msg_times == 0 || last_sync_gas_frameno < room->gas_data.gas_start_frameno) {
last_sync_gas_frameno = room->gas_data.gas_start_frameno;
FillMFGasData(msg->mutable_gas_data());
}
bool refreshed_view = false;
std::set<Entity*> view_objects;
for (Human* observer : observers_) {
msg->clear_team_data();
if (observer->team_members) {
for (auto& itr : *observer->team_members) {
if (itr != observer) {
itr->FillMFTeamData(msg->add_team_data());
}
}
}
if (observer != this && !observer->follow_synced_active_player) {
msg->set_active_player_id(entity_uniid);
FillMFActivePlayerData(msg->mutable_active_player_data());
if (!refreshed_view) {
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
view_objects.insert(hum);
}
for (Entity* entity : cell->entity_list) {
switch (entity->entity_type) {
case ET_Building:
case ET_Obstacle:
case ET_Hero:
{
view_objects.insert(entity);
}
break;
default:
{
}
break;
}
}
}
for (Entity* entity : view_objects) {
if (new_objects.find(entity) == new_objects.end()) {
entity->FillMFObjectFull(msg->add_full_objects());
}
}
refreshed_view = true;
}
observer->follow_synced_active_player = true;
} else {
if (active_player_data_pb) {
msg->set_active_player_id(entity_uniid);
*msg->mutable_active_player_data() = *active_player_data_pb;
} else {
msg->clear_active_player_id();
msg->clear_active_player_data();
}
}
observer->SendNotifyMsg(*msg);
}
delete msg;
if (active_player_data_pb) {
delete active_player_data_pb;
}
++send_msg_times;
}
ClearFrameData();
}
void Human::FollowTarget(Human* target)
{
if (target == this) {
return;
}
if (follow_target_) {
follow_target_->RemoveObserver(this);
}
target->AddObserver(this);
follow_target_ = target;
follow_synced_active_player = false;
}
void Human::SendDebugMsg(const std::string& debug_msg)
{
cs::SMDebugMsg notify_msg;
notify_msg.set_debug_msg(debug_msg);
SendNotifyMsg(notify_msg);
}
void Human::SendRollMsg(const std::string& roll_msg)
{
room->xtimer.AddDeadLineTimerAndAttach(
0,
a8::XParams()
.SetSender(this)
.SetParam1(roll_msg),
[] (const a8::XParams& param)
{
Human* target = (Human*)param.sender.GetUserData();
std::string roll_msg = param.param1;
target->room->TouchHumanList(a8::XParams(),
[target, roll_msg] (Human* hum, a8::XParams& param) -> bool
{
if (target != hum) {
cs::SMRollMsg msg;
msg.set_msg(roll_msg);
hum->SendNotifyMsg(msg);
}
return true;
});
},
&xtimer_attacher.timer_list_
);
}
void Human::UpdateAction()
{
int duration = std::max(0,
action_duration -
(int)((room->frame_no - action_frameno) * 1.0f / SERVER_FRAME_RATE) * 1000
);
if (duration <= 0) {
switch (action_type) {
case AT_Reload:
{
if (curr_weapon->weapon_idx == action_target_id &&
curr_weapon->weapon_id == action_item_id
) {
{
DirectReload();
}
}
}
break;
case AT_UseItem:
{
#if 0
switch (action_item_id) {
case IS_HEALTHKIT:
{
MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquipBySlotId(action_item_id);
if (item_meta){
if (GetInventory(item_meta->i->_inventory_slot()) > 0) {
float old_health = health;
health += item_meta->i->heal();
health = std::min(health, GetMaxHP());
stats.heal_amount += health - old_health;
DecInventory(item_meta->i->_inventory_slot(), 1);
need_sync_active_player = true;
SyncAroundPlayers();
}
}
}
break;
case IS_PAIN_KILLER:
{
MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquipBySlotId(action_item_id);
if (item_meta){
if (GetInventory(item_meta->i->_inventory_slot()) > 0) {
if (pain_killer_timer) {
int passed_time = (room->frame_no - pain_killer_frameno) * FRAME_RATE_MS;
int left_time = std::max(0, pain_killer_lastingtime * 1000 - passed_time);
int anodyne_max_time = MetaMgr::Instance()->GetSysParamAsInt("anodyne_max_time");
left_time = std::min(left_time, anodyne_max_time * 1000);
pain_killer_lastingtime += std::min(item_meta->i->time() * 1000, anodyne_max_time * 1000 - left_time) / 1000;
need_sync_active_player = true;
} else {
pain_killer_frameno = room->frame_no;
pain_killer_lastingtime = item_meta->i->time();
pain_killer_timer = room->xtimer.AddRepeatTimerAndAttach(
SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this)
.SetParam1(item_meta->i->heal()),
[] (const a8::XParams& param)
{
Human* hum = (Human*)param.sender.GetUserData();
float old_health = hum->health;
hum->health += param.param1.GetDouble();
hum->health = std::min(hum->health, hum->GetMaxHP());
hum->stats.heal_amount += hum->health - old_health;
hum->SyncAroundPlayers();
if (hum->room->frame_no - hum->pain_killer_frameno > hum->pain_killer_lastingtime * SERVER_FRAME_RATE) {
hum->room->xtimer.DeleteTimer(hum->pain_killer_timer);
hum->pain_killer_timer = nullptr;
}
},
&xtimer_attacher.timer_list_
);
}
DecInventory(item_meta->i->_inventory_slot(), 1);
need_sync_active_player = true;
}
}
}
break;
default:
{
}
break;
}
#endif
}
break;
default:
break;
}
ResetAction();
}
}
void Human::DirectReload()
{
int ammo = curr_weapon->ammo;
if (ammo < curr_weapon->GetClipVolume()) {
if (GetInventory(WEAPON_SLOT) > 0) {
int add_num = 0;
if (GetInventory(WEAPON_SLOT) <=
curr_weapon->GetClipVolume() - ammo) {
add_num = GetInventory(WEAPON_SLOT);
DecInventory(WEAPON_SLOT, add_num);
} else {
add_num = curr_weapon->GetClipVolume() - ammo;
DecInventory(WEAPON_SLOT, add_num);
}
curr_weapon->ammo += add_num;
need_sync_active_player = true;;
}
}
}
void Human::SendUIUpdate()
{
cs::SMUiUpdate notifymsg;
notifymsg.set_alive_count(room->AliveCount());
notifymsg.set_kill_count(stats.kills);
SendNotifyMsg(notifymsg);
}
void Human::SendWxVoip()
{
cs::SMWxVoip notifymsg;
if (!team_uuid.empty()) {
notifymsg.set_group_id(a8::XValue(room->room_uuid).GetString() + team_uuid);
}
SendNotifyMsg(notifymsg);
}
int Human::GetWeaponConfigLv(int weapon_id)
{
auto itr = weapon_configs.find(weapon_id);
return itr != weapon_configs.end() ? itr->second : 0;
}
int Human::GetSkinConfigLv(int skin_id)
{
auto itr = skin_configs.find(skin_id);
return itr != skin_configs.end() ? itr->second : 0;
}
void Human::SetSkinInfo(int skin_id)
{
skin.skin_id = skin_id;
if (skin.skin_id != 0){
skin.skin_lv = std::max(1, GetSkinConfigLv(skin.skin_id));
}
}
const Skin& Human::GetSkin()
{
if (skin_jlf.skin_id != 0) {
return skin_jlf;
}
return skin;
}
int Human::SkinId()
{
if (skin_jlf.skin_id != 0) {
return skin_jlf.skin_id;
}
return skin.skin_id;
}
int Human::SkinLv()
{
if (skin_jlf.skin_id != 0) {
return skin_jlf.skin_lv;
}
return skin.skin_lv;
}
int Human::GetSkillLeftTime()
{
if (skill_meta) {
if (last_use_skill_frameno_ == 0) {
return 0;
} else {
int passed_time = (room->frame_no - last_use_skill_frameno_) * FRAME_RATE_MS;
int skill_left_time = std::max(0, skill_meta->i->skill_cd() * 1000 - passed_time);
return skill_left_time;
}
}
return 0;
}
int Human::GetSkillCd()
{
return skill_meta ? skill_meta->i->skill_cd() * 1000 : 0;
}
void Human::TriggerBuff(std::set<Entity*>& target_list, BuffTriggerType_e trigger_type)
{
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) {
for (Entity* entity : target_list) {
if (entity->entity_type != ET_Player) {
continue;
}
Human* hum = (Human*)entity;
switch (buff_meta->i->buff_target()) {
case 1: //自己
{
if (hum == this) {
hum->AddBuff(buff_meta);
}
}
break;
case 2: //友军
{
if (hum->team_id == team_id) {
hum->AddBuff(buff_meta);
}
}
break;
case 3: //敌军
{
if (hum->team_id != team_id) {
hum->AddBuff(buff_meta);
}
}
break;
}
}//end for target_list
}//end for buff_list
}
}
void Human::AddBuff(MetaData::Buff* buff_meta)
{
if (GetBuffById(buff_meta->i->buff_id())) {
return;
}
if (!buff_meta->EffectCanStack()) {
Buff* buff = GetBuffByEffectId(buff_meta->i->buff_effect());
if (buff) {
RemoveBuffById(buff->meta->i->buff_id());
}
}
Buff* buff = &a8::FastAppend(buff_list_);
buff->owner = this;
buff->meta = buff_meta;
//buff->skill_meta
buff->add_frameno = room->frame_no;
buff->xtimer_attacher.xtimer = &room->xtimer;
buff_effect_[buff->meta->i->buff_effect()] = buff;
room->frame_event.AddBuff(this, buff);
{
room->xtimer.AddDeadLineTimerAndAttach(
buff_meta->i->duration_time() * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this)
.SetParam1(buff_meta->i->buff_id()),
[] (const a8::XParams& param)
{
Human* hum = (Human*)param.sender.GetUserData();
hum->RemoveBuffById(param.param1);
},
&buff->xtimer_attacher.timer_list_
);
}
ProcBuffEffect(buff);
}
void Human::RemoveBuffById(int buff_id)
{
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
if (itr->meta->i->buff_id() == buff_id) {
if (buff_effect_[itr->meta->i->buff_effect()] == &(*itr)) {
buff_effect_[itr->meta->i->buff_effect()] = nullptr;
}
buff_list_.erase(itr);
room->frame_event.RemoveBuff(this, buff_id);
break;
}
}
}
void Human::RemoveBuffByEffectId(int buff_effect_id)
{
Buff* buff = GetBuffByEffectId(buff_effect_id);
if (buff) {
RemoveBuffById(buff->meta->i->buff_id());
}
}
bool Human::HasBuffEffect(int buff_effect_id)
{
return GetBuffByEffectId(buff_effect_id) != nullptr;
}
void Human::RecalcBuffAttr()
{
buff_attr_abs_ = {};
buff_attr_rate_ = {};
for (auto& buff : buff_list_) {
if (buff.meta->i->buff_effect() == BET_ChgAttr) {
int attr_type = (int)buff.meta->param1;
int calc_type = (int)buff.meta->param2;
if (IsValidHumanAttr(attr_type)) {
if (calc_type == 1) {
buff_attr_abs_[attr_type] += buff.meta->param3;
} else if (calc_type == 2) {
buff_attr_rate_[attr_type] += buff.meta->param3;
}
}
}
}
}
void Human::ProcBuffEffect(Buff* buff)
{
switch (buff->meta->i->buff_effect()) {
case BET_ChgAttr:
{
RecalcBuffAttr();
}
break;
case BET_LastBurn:
{
room->xtimer.AddRepeatTimerAndAttach(
SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this)
.SetParam1(buff),
Buff::ProcLastBurn,
&buff->xtimer_attacher.timer_list_
);
}
break;
case BET_ReleaseDcgr:
{
room->xtimer.AddRepeatTimerAndAttach(
(buff->meta->param1 / 1000) * FRAME_RATE_MS,
a8::XParams()
.SetSender(this)
.SetParam1(buff),
Buff::ProcReleaseDcgr,
&buff->xtimer_attacher.timer_list_
);
}
break;
case BET_ReleaseFireBomb:
{
room->xtimer.AddRepeatTimerAndAttach(
(buff->meta->param1 / 1000) * FRAME_RATE_MS,
a8::XParams()
.SetSender(this)
.SetParam1(buff),
Buff::ProcReleaseFireBomb,
&buff->xtimer_attacher.timer_list_
);
}
break;
case BET_Assault:
{
a8::SetBitFlag(status, HS_Assaulting);
Entity* entity = room->GetEntityByUniId(skill_target_id);
if (entity) {
move_dir = entity->pos - pos;
move_dir.Normalize();
skill_target_pos = entity->pos;
skill_dir = skill_target_pos - pos;
skill_dir.Normalize();
}
}
break;
default:
break;
}
}
void Human::OnAttack()
{
GrassTempShow();
}
void Human::OnHit()
{
GrassTempShow();
}
void Human::OnEnterGrass()
{
if (a8::HasBitFlag(status, HS_InGrass)) {
return;
}
a8::SetBitFlag(status, HS_InGrass);
if (leave_grass_timer_list_) {
room->xtimer.DeleteTimer(leave_grass_timer_list_);
leave_grass_timer_list_ = nullptr;
}
if (grass_hide_timer_list_) {
room->xtimer.DeleteTimer(grass_hide_timer_list_);
grass_hide_timer_list_ = nullptr;
}
auto hide_func =
[] (const a8::XParams& param)
{
Human* hum = (Human*)param.sender.GetUserData();
hum->grass_hide_timer_list_ = nullptr;
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(GRASS_HIDE_BUFF_ID);
if (buff_meta) {
hum->AddBuff(buff_meta);
}
};
grass_hide_timer_list_ =
room->xtimer.AddDeadLineTimerAndAttach(MetaMgr::Instance()->grass_invisible_time * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this),
hide_func,
&xtimer_attacher.timer_list_);
}
void Human::OnLeaveGrass()
{
a8::UnSetBitFlag(status, HS_InGrass);
if (grass_hide_timer_list_) {
room->xtimer.DeleteTimer(grass_hide_timer_list_);
grass_hide_timer_list_ = nullptr;
}
if (leave_grass_timer_list_) {
room->xtimer.DeleteTimer(leave_grass_timer_list_);
}
leave_grass_timer_list_ =
room->xtimer.AddDeadLineTimerAndAttach(MetaMgr::Instance()->grass_show_time * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Human* hum = (Human*)param.sender.GetUserData();
hum->leave_grass_timer_list_ = nullptr;
hum->RemoveBuffById(GRASS_HIDE_BUFF_ID);
},
&xtimer_attacher.timer_list_);
}
void Human::CheckGrass()
{
bool in_grass = false;
std::set<ColliderComponent*> colliders;
room->map_service.GetGrassColliders(pos.x, pos.y, colliders);
for (const ColliderComponent* collider : colliders) {
switch (collider->owner->entity_type) {
case ET_Obstacle:
case ET_Building:
{
if (TestCollision((ColliderComponent*)collider)) {
in_grass = true;
break;
}
}
break;
default:
break;
}
}
if (in_grass) {
if (!a8::HasBitFlag(status, HS_InGrass)) {
OnEnterGrass();
}
} else {
if (a8::HasBitFlag(status, HS_InGrass)) {
OnLeaveGrass();
}
}
}
void Human::GrassTempShow()
{
if (a8::HasBitFlag(status, HS_InGrass)) {
if (HasBuffEffect(BET_InGrass)) {
RemoveBuffByEffectId(BET_InGrass);
}
if (grass_hide_timer_list_) {
room->xtimer.ModifyTimer(grass_hide_timer_list_,
MetaMgr::Instance()->grass_invisible_time2 * SERVER_FRAME_RATE);
} else {
auto hide_func =
[] (const a8::XParams& param)
{
Human* hum = (Human*)param.sender.GetUserData();
hum->grass_hide_timer_list_ = nullptr;
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(GRASS_HIDE_BUFF_ID);
if (buff_meta) {
hum->AddBuff(buff_meta);
}
};
grass_hide_timer_list_ =
room->xtimer.AddDeadLineTimerAndAttach(MetaMgr::Instance()->grass_invisible_time2 * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this),
hide_func,
&xtimer_attacher.timer_list_);
}
}
}
float* Human::GetAbilityById(int attr_id)
{
switch (attr_id) {
case HAT_Hp:
return &ability.hp;
break;
case HAT_HPRecover:
return &ability.hp_recover;
break;
case HAT_Atk:
return &ability.atk;
break;
case HAT_Def:
return &ability.def;
break;
case HAT_Speed:
return &ability.speed;
break;
case HAT_ShotRange:
return &ability.shot_range;
break;
case HAT_ShotSpeed:
return &ability.shot_speed;
break;
case HAT_ReloadSpeed:
return &ability.reload_speed;
break;
default:
return nullptr;
break;
}
}
void Human::RecalcBaseAttr()
{
ability = HumanAbility();
if (skin_meta) {
for (auto& pair : skin_meta->attr_init) {
float* p_attr = GetAbilityById(pair.first);
if (p_attr) {
*p_attr += pair.second;
}
}
for (auto& pair : skin_meta->attr_up) {
float* p_attr = GetAbilityById(pair.first);
if (p_attr) {
*p_attr += (SkinLv() / std::get<1>(pair.second)) * std::get<0>(pair.second);
}
}
}
if (driver_meta) {
for (auto& pair : driver_meta->attr_up) {
float* p_attr = GetAbilityById(pair.first);
if (p_attr) {
*p_attr += (driver.driver_lv / std::get<1>(pair.second)) * std::get<0>(pair.second);
}
}
for (auto& pair : driver_meta->passive_skill_attr) {
float* p_attr = GetAbilityById(pair.first);
if (p_attr && driver.driver_lv >= std::get<1>(pair.second)) {
*p_attr += std::get<0>(pair.second);
}
}
}
ability.max_hp = ability.hp;
}
void Human::_UpdateMove(int speed)
{
for (int i = 0; i < speed; ++i) {
a8::Vec2 old_pos = pos;
pos = pos + move_dir;
if (IsCollisionInMapService()) {
pos = old_pos;
FindPathInMapService();
if (rand() % 3 == 0) {
i += 1;
}
}
room->grid_service.MoveHuman(this);
}
CheckGrass();
}
void Human::_UpdateAssaultMove()
{
Buff* buff = GetBuffByEffectId(BET_Assault);
if (!buff || (room->frame_no - buff->add_frameno) * FRAME_RATE_MS < buff->meta->param1) {
return;
}
bool move_end = false;
if (skill_target_pos.Distance(pos) <= 0.000001f) {
pos = skill_target_pos;
move_end = true;
} else {
a8::Vec2 tmp_move_dir = skill_target_pos - pos;
int speed = buff->meta->param2;
for (int i = 0; i < speed; ++i) {
pos = pos + tmp_move_dir;
if (skill_target_pos.Distance(pos) <= 0.000001f) {
pos = skill_target_pos;
move_end = true;
break;
}
}
}
CheckGrass();
if (move_end) {
if (!skill_meta->phases.empty()) {
MetaData::SkillPhase* phase = &skill_meta->phases[0];
std::set<Entity*> target_list;
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
if (hum->team_id != team_id && hum->pos.Distance(skill_target_pos) < hum->GetRadius()) {
if (!hum->HasBuffEffect(BET_Invincible)) {
hum->DecHP(phase->param1.GetDouble(), entity_uniid, name, 0);
target_list.insert(hum);
}
a8::Vec2 pull_dir = skill_dir;
if (std::abs(pull_dir.x) > FLT_EPSILON ||
std::abs(pull_dir.y) > FLT_EPSILON) {
pull_dir.Normalize();
hum->PullHuman(pull_dir, phase->param2.GetDouble());
}
}
}
}
TriggerBuff(target_list, BTT_SkillHit);
room->frame_event.AddExplosion(0, pos, 2);
room->grid_service.MoveHuman(this);
}
RemoveBuffById(buff->meta->i->buff_id());
a8::UnSetBitFlag(status, HS_Assaulting);
}
}
void Human::PullHuman(const a8::Vec2& pull_dir, float distance)
{
for (int i = 0; i < distance; ++i) {
a8::Vec2 old_pos = pos;
pos = pos + pull_dir;
if (IsCollisionInMapService()) {
pos = old_pos;
break;
}
room->grid_service.MoveHuman(this);
}
CheckGrass();
}
void Human::ClearFrameData()
{
if (!new_objects.empty()) {
new_objects.clear();
}
if (!del_objects.empty()) {
for (auto& itr : del_objects) {
Entity* entity = room->GetEntityByUniId(itr);
if (entity) {
part_objects.erase(entity);
}
}
del_objects.clear();
}
if (!out_objects.empty()) {
for (auto& itr : out_objects) {
Entity* entity = room->GetEntityByUniId(itr);
if (entity) {
part_objects.erase(entity);
}
}
out_objects.clear();
}
if (!shots_.empty()) {
shots_.clear();
}
if (!bullets_.empty()) {
bullets_.clear();
}
if (!explosions_.empty()) {
explosions_.clear();
}
if (!smokes_.empty()) {
smokes_.clear();
}
if (!emotes_.empty()) {
emotes_.clear();
}
if (!chged_buffs_.empty()) {
chged_buffs_.clear();
}
if (!revive_objs_.empty()) {
revive_objs_.clear();
}
if (!dead_objs_.empty()) {
dead_objs_.clear();
}
if (!property_chged_humans_.empty()) {
property_chged_humans_.clear();
}
}
void Human::DeadDrop()
{
for (auto& weapon : weapons) {
if (weapon.weapon_id != 0 && weapon.weapon_id != default_weapon.weapon_id) {
a8::Vec2 drop_pos = pos;
room->DropItem(drop_pos, weapon.weapon_id, 1, weapon.weapon_lv);
}
}
for (size_t slot = 0; slot < inventory_.size(); ++slot) {
if (inventory_[slot] > 0) {
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquipBySlotId(slot);
if (equip_meta) {
if (!equip_meta->CanDrop()) {
return;
}
a8::Vec2 drop_pos = pos;
room->DropItem(drop_pos, equip_meta->i->id(), inventory_[slot], 1);
}
}
}
}
void Human::Revive()
{
dead = false;
ability.hp = GetMaxHP();
{
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(1003);
if (buff_meta) {
AddBuff(buff_meta);
}
}
room->frame_event.AddRevive(this);
room->OnHumanRevive(this);
CheckGrass();
use_skill = false;
curr_skill_phase = 0;
skill_dir = a8::Vec2();
skill_param1 = 0.0f;
playing_skill = false;
}
void Human::SelectSkillTargets(const a8::Vec2& target_pos, std::set<Entity*>& target_list)
{
switch (skill_meta->i->skill_target()) {
case ST_All:
{
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
if (hum->pos.Distance(target_pos) < skill_meta->i->skill_distance()) {
target_list.insert(hum);
}
}
}
}
break;
case ST_Self:
{
target_list.insert(this);
}
break;
case ST_FriendlyIncludeSelf:
{
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
if ((hum == this || hum->team_id == team_id) ||
hum->pos.Distance(target_pos) < skill_meta->i->skill_distance()) {
target_list.insert(hum);
}
}
}
}
break;
case ST_FriendlyExcludeSelf:
{
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
if ((hum != this && hum->team_id == team_id) ||
hum->pos.Distance(target_pos) < skill_meta->i->skill_distance()) {
target_list.insert(hum);
}
}
}
}
break;
case ST_EnemySingle:
{
Entity* entity = room->GetEntityByUniId(skill_target_id);
if (entity && entity->entity_type == ET_Player) {
Human* hum = (Human*)entity;
if (hum->team_id != team_id) {
target_list.insert(hum);
}
}
}
break;
case ST_EnemyGroup:
{
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
if ((hum->team_id != team_id) ||
hum->pos.Distance(target_pos) < skill_meta->i->skill_distance()) {
target_list.insert(hum);
}
}
}
}
break;
case ST_EnemyAndObject:
{
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
if ((hum->team_id != team_id) ||
hum->pos.Distance(target_pos) < skill_meta->i->skill_distance()) {
target_list.insert(hum);
}
}
}
}
break;
case ST_EnemyAndSelf:
{
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
if ((hum == this || hum->team_id != team_id) ||
hum->pos.Distance(target_pos) < skill_meta->i->skill_distance()) {
target_list.insert(hum);
}
}
}
}
break;
}
}
Buff* Human::GetBuffById(int buff_id)
{
for (Buff& buff : buff_list_) {
if (buff.meta->i->buff_id() == buff_id) {
return &buff;
}
}
return nullptr;
}
Buff* Human::GetBuffByEffectId(int effect_id)
{
return IsValidBuffEffect(effect_id) ? buff_effect_[effect_id] : nullptr;
}
void Human::ProcSkillPhase(MetaData::SkillPhase* phase)
{
auto frame_check_func =
[] (const a8::XParams& param)
{
Obstacle* obstacle = (Obstacle*)param.sender.GetUserData();
if (!obstacle->dead) {
obstacle->room->TouchHumanList(a8::XParams(),
[obstacle] (Human* hum, a8::XParams&) -> bool
{
if (obstacle->master->team_id != hum->team_id) {
if (hum->pos.Distance(obstacle->pos) <
hum->meta->i->radius()) {
obstacle->dead = true;
obstacle->Explosion();
return false;
}
}
return true;
});
}
};
auto explosion_check_func =
[] (const a8::XParams& param)
{
Obstacle* obstacle = (Obstacle*)param.sender.GetUserData();
if (!obstacle->dead) {
obstacle->dead = true;
obstacle->Explosion();
}
};
switch (phase->func_id) {
case Skill_Jump:
{
#if 0
a8::Vec2 old_pos = pos;
pos = skill_target_pos;
if (IsCollisionInMapService()) {
pos = old_pos;
} else {
for (auto& cell : grid_list) {
for (Human* hum : cell->human_list) {
if (hum->team_id != team_id && hum->pos.Distance(skill_target_pos) < hum->GetRadius()) {
if (!hum->HasBuffEffect(BET_Invincible)) {
hum->DecHP(phase->param1.GetDouble(), entity_uniid, name, 0);
}
a8::Vec2 pull_dir = pos - old_pos;
if (std::abs(pull_dir.x) > FLT_EPSILON ||
std::abs(pull_dir.y) > FLT_EPSILON) {
pull_dir.Normalize();
hum->PullHuman(pull_dir, phase->param2.GetDouble());
}
}
}
}
room->frame_event.AddExplosion(0, pos, 2);
room->grid_service.MoveHuman(this);
CheckGrass();
}
#endif
}
break;
case Skill_Shot:
{
MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(phase->param1.GetInt());
if (bullet_meta) {
DirectShot(bullet_meta, skill_meta->i->skill_id());
if (bullet_meta->NeedTrace()) {
room->xtimer.AddDeadLineTimerAndAttach(bullet_meta->i->time() * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this)
.SetParam1(phase->param1.GetInt())
.SetParam2(skill_target_id),
Bullet::ProcMissible,
&xtimer_attacher.timer_list_);
}
}
}
break;
case Skill_SummonObject:
{
Obstacle* obstacle = room->CreateObstacle(phase->param1.GetInt(), pos.x, pos.y);
if (obstacle) {
obstacle->master = this;
room->xtimer.AddRepeatTimerAndAttach(2,
a8::XParams()
.SetSender(obstacle),
frame_check_func,
&obstacle->xtimer_attacher.timer_list_);
room->xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * 5,
a8::XParams()
.SetSender(obstacle),
explosion_check_func,
&obstacle->xtimer_attacher.timer_list_);
}
}
break;
default:
break;
}
}
void Human::InternalShot(MetaData::Equip* bullet_meta, int skill_id, size_t offset_idx)
{
if (offset_idx >= bullet_meta->bullet_born_offset.size()) {
return;
}
if (dead) {
return;
}
const auto& born_points = std::get<1>(bullet_meta->bullet_born_offset[offset_idx]);
for (auto& born_point : born_points) {
a8::Vec2 bullet_born_offset = born_point.born_offset;
bullet_born_offset.Rotate(attack_dir.CalcAngle(a8::Vec2::UP));
a8::Vec2 bullet_born_pos = pos + bullet_born_offset;
if (room->OverBorder(bullet_born_pos, 0)) {
return;
}
}
if (offset_idx <= 0) {
room->frame_event.AddShot(this);
}
for (auto& born_point : born_points) {
a8::Vec2 bullet_born_offset = born_point.born_offset;
bullet_born_offset.Rotate(attack_dir.CalcAngle(a8::Vec2::UP));
a8::Vec2 bullet_born_pos = pos + bullet_born_offset;
a8::Vec2 bullet_dir = attack_dir;
float bullet_angle = born_point.angle;
if (bullet_meta->i->bullet_angle() >= 0.10f) {
int angle = (int)bullet_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 hit_time = 0;
if (bullet_meta->NeedTrace()) {
hit_time = bullet_meta->i->time() * 1000;
}
if (bullet_meta->NeedTrace()) {
if (skill_id != 0) {
room->frame_event.AddBullet(this, bullet_meta->i->id(),
curr_weapon->weapon_lv, bullet_born_pos, bullet_dir,
fly_distance, hit_time, skill_target_id, skill_id);
} else {
room->frame_event.AddBullet(this, bullet_meta->i->id(),
curr_weapon->weapon_lv, bullet_born_pos, bullet_dir,
fly_distance, hit_time, shot_target_id);
}
} else {
room->frame_event.AddBullet(this, bullet_meta->i->id(),
curr_weapon->weapon_lv,
bullet_born_pos, bullet_dir,
fly_distance, hit_time, 0, skill_id);
}
if (room->BattleStarted()) {
if (bullet_meta->i->equip_subtype() != BulletType_Missile) {
room->CreateBullet(this, bullet_meta, bullet_born_pos, bullet_dir, fly_distance);
}
}
}
OnAttack();
}