2358 lines
77 KiB
C++
2358 lines
77 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 "android.ai.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(kMAX_WEAPON_NUM);
|
|
for (size_t i = 0; i < kMAX_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_[kIS_1XSCOPE] = 1;
|
|
|
|
if (MetaMgr::Instance()->fighting_mode) {
|
|
inventory_[kIS_9MM] = kFIGHTING_MODE_BULLET_NUM;
|
|
inventory_[kIS_556MM] = kFIGHTING_MODE_BULLET_NUM;
|
|
inventory_[kIS_762MM] = kFIGHTING_MODE_BULLET_NUM;
|
|
inventory_[kIS_12GAUGE] = kFIGHTING_MODE_BULLET_NUM;
|
|
inventory_[kIS_RPG] = kFIGHTING_MODE_BULLET_NUM;
|
|
inventory_[kIS_TANK] = kFIGHTING_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(kBET_Assault);
|
|
if (buff) {
|
|
return buff->meta->param2;
|
|
}
|
|
}
|
|
float speed = ability.speed;
|
|
if (action_type == kAT_Reload) {
|
|
speed = ability.reload_speed;
|
|
} else if (shot_hold) {
|
|
if (curr_weapon->weapon_idx == kGUN_SLOT1 ||
|
|
curr_weapon->weapon_idx == kGUN_SLOT2) {
|
|
speed = ability.shot_speed;
|
|
}
|
|
}
|
|
speed = (speed + buff_attr_abs_[kHAT_Speed]) * (1 + buff_attr_rate_[kHAT_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(kET_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(kET_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_energy_shield(energy_shield);
|
|
p->set_max_energy_shield(max_energy_shield);
|
|
p->set_disconnected(disconnected);
|
|
tank_.ToPB(p->mutable_skin());
|
|
if (tankskin.skin_id != 0) {
|
|
tankskin.ToPB(p->mutable_tankskin());
|
|
}
|
|
curr_weapon->ToPB(p->mutable_weapon());
|
|
#if 0
|
|
p->set_energy_shield(energy_shield);
|
|
{
|
|
p->set_max_energy_shield(max_energy_shield);
|
|
}
|
|
#endif
|
|
p->set_shot_range(ability.shot_range + buff_attr_abs_[kHAT_ShotRange]);
|
|
}
|
|
|
|
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::SetMeta(MetaData::Player* hum_meta)
|
|
{
|
|
meta_ = hum_meta;
|
|
}
|
|
|
|
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 == kAT_Reload) {
|
|
CancelAction();
|
|
}
|
|
|
|
if (HasBuffEffect(kBET_Hide)) {
|
|
RemoveBuffByEffectId(kBET_Hide);
|
|
}
|
|
|
|
DirectShot(curr_weapon->meta, 0);
|
|
if (curr_weapon->meta->NeedTrace()) {
|
|
room->xtimer.AddDeadLineTimerAndAttach(curr_weapon->meta->i->time() * kSERVER_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->frameno;
|
|
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) / kFRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(bullet_meta)
|
|
.SetParam2(a8::MakeInt64(skill_id, i))
|
|
.SetParam3(a8::MakeInt64(attack_dir.x * kTEN_W, attack_dir.y * kTEN_W)),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* sender = (Human*)param.sender.GetUserData();
|
|
MetaData::Equip* bullet_meta = (MetaData::Equip*)param.param1.GetUserData();
|
|
if (sender) {
|
|
int skill_id = a8::Low32(param.param2.GetInt64());
|
|
int offset_idx = a8::High32(param.param2.GetInt64());
|
|
float attack_x = a8::Low32(param.param3.GetInt64()) / (float)kTEN_W;
|
|
float attack_y = a8::High32(param.param3.GetInt64()) / (float)kTEN_W;
|
|
a8::Vec2 old_attack_dir = sender->attack_dir;
|
|
if (bullet_meta->bullet_born_offset.size() <= 1){
|
|
sender->attack_dir = a8::Vec2(attack_x, attack_y);
|
|
sender->attack_dir.Normalize();
|
|
}
|
|
sender->InternalShot(bullet_meta,
|
|
skill_id,
|
|
offset_idx);
|
|
sender->attack_dir = old_attack_dir;
|
|
}
|
|
},
|
|
&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 kET_Obstacle:
|
|
case kET_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->frameno - last_use_skill_frameno_) * kFRAME_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(kAT_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->frameno;
|
|
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 = kAT_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) {
|
|
if (immediately_revive_times_ > 0) {
|
|
--immediately_revive_times_;
|
|
dead = true;
|
|
status = 0;
|
|
ClearBuffList();
|
|
room->frame_event.AddDead(this, 0);
|
|
room->xtimer.AddDeadLineTimerAndAttach(2,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
hum->ImmediatelyRevive();
|
|
},
|
|
&xtimer_attacher.timer_list_
|
|
);
|
|
return;
|
|
}
|
|
|
|
lethal_weapon = weapon_id;
|
|
{
|
|
Entity* entity = room->GetEntityByUniId(killer_id);
|
|
if (entity && entity->entity_type == kET_Player) {
|
|
Human* killer = (Human*)entity;
|
|
killer->stats.kills++;
|
|
killer->stats.last_kill_timeseq = ++room->last_kill_timeseq;
|
|
}
|
|
}
|
|
++stats.dead_times;
|
|
stats.killer_id = killer_id;
|
|
stats.killer_name = killer_name;
|
|
stats.weapon_id = weapon_id;
|
|
dead = true;
|
|
dead_frameno = room->frameno;
|
|
room->xtimer.AddDeadLineTimerAndAttach(MetaMgr::Instance()->revive_time * kSERVER_FRAME_RATE,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
hum->Revive();
|
|
},
|
|
&xtimer_attacher.timer_list_
|
|
);
|
|
status = 0;
|
|
ClearBuffList();
|
|
room->frame_event.AddDead(this,
|
|
MetaMgr::Instance()->revive_time * 1000);
|
|
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)
|
|
{
|
|
if (energy_shield > 0.001f) {
|
|
energy_shield = std::max(0.0f, energy_shield - dec_hp);
|
|
if (energy_shield < 0.001f) {
|
|
RemoveBuffByEffectId(kBET_Shield);
|
|
}
|
|
} else {
|
|
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;
|
|
last_attacked_frameno = room->frameno;
|
|
if (entity_subtype == kEST_Android) {
|
|
Android* android = (Android*)this;
|
|
if (android->ai) {
|
|
android->ai->LockEnemy(last_attacker_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;
|
|
}
|
|
|
|
bool Human::CanUseSkill()
|
|
{
|
|
if (!skill_meta_) {
|
|
return false;
|
|
}
|
|
if (GetSkillLeftTime() > 0) {
|
|
return false;
|
|
}
|
|
if (a8::HasBitFlag(status, HS_Assaulting) ||
|
|
HasBuffEffect(kBET_Vertigo) ||
|
|
HasBuffEffect(kBET_Dcgr)
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Human::DoSkill()
|
|
{
|
|
if (CanUseSkill()) {
|
|
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->frameno;
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id);
|
|
if (entity && entity->entity_type == kET_Player) {
|
|
Human* hum = (Human*)entity;
|
|
std::set<Entity*> target_list;
|
|
skill_target_pos = hum->pos;
|
|
SelectSkillTargets(hum->pos, target_list);
|
|
TriggerBuff(target_list, kBTT_UseSkill);
|
|
if (!skill_meta_->phases.empty() && skill_meta_->phases[0].time_offset <= 0) {
|
|
UpdateSkill();
|
|
}
|
|
} else {
|
|
playing_skill = false;
|
|
}
|
|
++stats.use_skill_times;
|
|
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 kET_Obstacle:
|
|
{
|
|
if (!target) {
|
|
if (TestCollision(entity)) {
|
|
target = entity;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kET_Building:
|
|
{
|
|
if (!target || target->entity_type != kET_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 kET_Building:
|
|
case kET_Obstacle:
|
|
case kET_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 kET_Building:
|
|
case kET_Obstacle:
|
|
case kET_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 kET_Building:
|
|
case kET_Obstacle:
|
|
case kET_Hero:
|
|
{
|
|
AddOutObjects(entity);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::FillMFActivePlayerData(cs::MFActivePlayerData* player_data)
|
|
{
|
|
{
|
|
player_data->set_action_type(action_type);
|
|
if (action_type != kAT_None) {
|
|
int duration = std::max(0,
|
|
action_duration -
|
|
(int)((room->frameno - action_frameno) * 1.0f / kSERVER_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);
|
|
}
|
|
}
|
|
tank_.ToPB(player_data->mutable_skin());
|
|
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);
|
|
}
|
|
player_data->set_energy_shield(energy_shield);
|
|
player_data->set_max_energy_shield(max_energy_shield);
|
|
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 == kGasInactive) {
|
|
long long duration = MetaMgr::Instance()->gas_inactive_time * kSERVER_FRAME_RATE -
|
|
(room->frameno - 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->frameno - 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()
|
|
{
|
|
}
|
|
|
|
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::min(GetHP(), GetMaxHP());
|
|
ability.hp = std::max(GetHP(), 1.0f);
|
|
SyncAroundPlayers();
|
|
}
|
|
}
|
|
|
|
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 kET_Building:
|
|
case kET_Obstacle:
|
|
case kET_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->frameno - action_frameno) * 1.0f / kSERVER_FRAME_RATE) * 1000
|
|
);
|
|
if (duration <= 0) {
|
|
switch (action_type) {
|
|
case kAT_Reload:
|
|
{
|
|
if (curr_weapon->weapon_idx == action_target_id &&
|
|
curr_weapon->weapon_id == action_item_id
|
|
) {
|
|
{
|
|
DirectReload();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kAT_UseItem:
|
|
{
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ResetAction();
|
|
}
|
|
}
|
|
|
|
void Human::DirectReload()
|
|
{
|
|
int ammo = curr_weapon->ammo;
|
|
if (ammo < curr_weapon->GetClipVolume()) {
|
|
if (GetInventory(kWEAPON_SLOT) > 0) {
|
|
int add_num = 0;
|
|
if (GetInventory(kWEAPON_SLOT) <=
|
|
curr_weapon->GetClipVolume() - ammo) {
|
|
add_num = GetInventory(kWEAPON_SLOT);
|
|
DecInventory(kWEAPON_SLOT, add_num);
|
|
} else {
|
|
add_num = curr_weapon->GetClipVolume() - ammo;
|
|
DecInventory(kWEAPON_SLOT, add_num);
|
|
}
|
|
curr_weapon->ammo += add_num;
|
|
need_sync_active_player = true;;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::SendUIUpdate()
|
|
{
|
|
cs::SMUiUpdate notifymsg;
|
|
room->FillSMUiUpdate(notifymsg);
|
|
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::GetTankConfigLv(int tank_id)
|
|
{
|
|
auto itr = tank_configs.find(tank_id);
|
|
return itr != tank_configs.end() ? itr->second : 0;
|
|
}
|
|
|
|
void Human::SetTankInfo(int tank_id)
|
|
{
|
|
tank_.tank_id = tank_id;
|
|
if (tank_.tank_id != 0){
|
|
tank_.tank_lv = std::max(1, GetTankConfigLv(tank_.tank_id));
|
|
}
|
|
}
|
|
|
|
const Tank& Human::GetTank()
|
|
{
|
|
return tank_;
|
|
}
|
|
|
|
int Human::TankId()
|
|
{
|
|
return tank_.tank_id;
|
|
}
|
|
|
|
int Human::TankLv()
|
|
{
|
|
return tank_.tank_lv;
|
|
}
|
|
|
|
MetaData::Skill* Human::CurrentSkillMeta()
|
|
{
|
|
return skill_meta_;
|
|
}
|
|
|
|
int Human::GetSkillLeftTime()
|
|
{
|
|
if (skill_meta_) {
|
|
if (last_use_skill_frameno_ == 0) {
|
|
return 0;
|
|
} else {
|
|
int passed_time = (room->frameno - last_use_skill_frameno_) * kFRAME_RATE_MS;
|
|
int skill_left_time = std::max(0, GetSkillCd() - passed_time);
|
|
return skill_left_time;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Human::GetSkillCd()
|
|
{
|
|
if (!skill_meta_) {
|
|
return 0;
|
|
}
|
|
if (TankLv() <= 1) {
|
|
return skill_meta_->i->skill_cd() * 1000;
|
|
} else {
|
|
return skill_meta_->i->skill_cd() * 1000 - (TankLv() - 1) * skill_meta_->i->cold_time_up();
|
|
}
|
|
}
|
|
|
|
void Human::TriggerOneObjectBuff(Entity* target, BuffTriggerType_e trigger_type)
|
|
{
|
|
if (target->entity_type != kET_Player) {
|
|
return;
|
|
}
|
|
if (target->dead) {
|
|
return;
|
|
}
|
|
Human* hum = (Human*)target;
|
|
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 (hum == this) {
|
|
hum->AddBuff(buff_meta);
|
|
}
|
|
}
|
|
break;
|
|
case kBuffTargetFriendly: //友军
|
|
{
|
|
if (hum->team_id == team_id) {
|
|
hum->AddBuff(buff_meta);
|
|
}
|
|
}
|
|
break;
|
|
case kBuffTargetEnemy: //敌军
|
|
{
|
|
if (hum->team_id != team_id) {
|
|
hum->AddBuff(buff_meta);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::TriggerBuff(std::set<Entity*>& target_list, BuffTriggerType_e trigger_type)
|
|
{
|
|
for (Entity* entity : target_list) {
|
|
TriggerOneObjectBuff(entity, trigger_type);
|
|
}
|
|
}
|
|
|
|
void Human::AddBuff(MetaData::Buff* buff_meta)
|
|
{
|
|
if (buff_meta->i->buff_effect() == kBET_ImmediatelyRevive) {
|
|
++immediately_revive_times_;
|
|
return;
|
|
}
|
|
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());
|
|
}
|
|
}
|
|
if (buff_meta->i->buff_effect() == kBET_OnceChgAttr) {
|
|
if ((int)buff_meta->param1== kHAT_Hp) {
|
|
if ((int)buff_meta->param2 == 1) {
|
|
//绝对值
|
|
ability.hp += buff_meta->param3;
|
|
ability.hp = std::min(ability.max_hp, ability.hp);
|
|
} else if ((int)buff_meta->param2 == 2) {
|
|
//百分比
|
|
ability.hp *= 1 + buff_meta->param3;
|
|
ability.hp = std::min(ability.max_hp, ability.hp);
|
|
}
|
|
SyncAroundPlayers();
|
|
}
|
|
}
|
|
Buff* buff = &a8::FastAppend(buff_list_);
|
|
buff->owner = this;
|
|
buff->meta = buff_meta;
|
|
//buff->skill_meta
|
|
buff->add_frameno = room->frameno;
|
|
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() * kSERVER_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;
|
|
}
|
|
}
|
|
RecalcBuffAttr();
|
|
}
|
|
|
|
void Human::RemoveBuffByEffectId(int buff_effect_id)
|
|
{
|
|
Buff* buff = GetBuffByEffectId(buff_effect_id);
|
|
if (buff) {
|
|
RemoveBuffById(buff->meta->i->buff_id());
|
|
}
|
|
}
|
|
|
|
void Human::ClearBuffList()
|
|
{
|
|
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
|
|
if (buff_effect_[itr->meta->i->buff_effect()] == &(*itr)) {
|
|
buff_effect_[itr->meta->i->buff_effect()] = nullptr;
|
|
}
|
|
switch (itr->meta->i->buff_effect()) {
|
|
case kBET_Shield:
|
|
{
|
|
energy_shield = 0.0f;
|
|
max_energy_shield = 0.0f;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
room->frame_event.RemoveBuff(this, itr->meta->i->buff_id());
|
|
}
|
|
buff_list_.clear();
|
|
buff_effect_ = {};
|
|
buff_attr_abs_ = {};
|
|
buff_attr_rate_ = {};
|
|
RecalcBuffAttr();
|
|
}
|
|
|
|
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() == kBET_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 kBET_ChgAttr:
|
|
{
|
|
RecalcBuffAttr();
|
|
}
|
|
break;
|
|
case kBET_LastBurn:
|
|
{
|
|
room->xtimer.AddRepeatTimerAndAttach(
|
|
kSERVER_FRAME_RATE,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(buff),
|
|
Buff::ProcLastBurn,
|
|
&buff->xtimer_attacher.timer_list_
|
|
);
|
|
}
|
|
break;
|
|
case kBET_ReleaseDcgr:
|
|
{
|
|
room->xtimer.AddRepeatTimerAndAttach(
|
|
(buff->meta->param1 / 1000) * kFRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(buff),
|
|
Buff::ProcReleaseDcgr,
|
|
&buff->xtimer_attacher.timer_list_
|
|
);
|
|
}
|
|
break;
|
|
case kBET_ReleaseFireBomb:
|
|
{
|
|
room->xtimer.AddRepeatTimerAndAttach(
|
|
(buff->meta->param1 / 1000) * kFRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(buff),
|
|
Buff::ProcReleaseFireBomb,
|
|
&buff->xtimer_attacher.timer_list_
|
|
);
|
|
}
|
|
break;
|
|
case kBET_Assault:
|
|
{
|
|
a8::SetBitFlag(status, HS_Assaulting);
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id);
|
|
if (entity) {
|
|
if (entity->pos.Distance(pos) <= 0.000001f) {
|
|
pos = entity->pos;
|
|
skill_target_pos = entity->pos;
|
|
} else {
|
|
move_dir = entity->pos - pos;
|
|
move_dir.Normalize();
|
|
skill_target_pos = entity->pos;
|
|
skill_dir = skill_target_pos - pos;
|
|
skill_dir.Normalize();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kBET_OnceChgAttr:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case kBET_LastRecover:
|
|
{
|
|
room->xtimer.AddRepeatTimerAndAttach(
|
|
kSERVER_FRAME_RATE,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(buff),
|
|
Buff::ProcLastRecover,
|
|
&buff->xtimer_attacher.timer_list_
|
|
);
|
|
}
|
|
break;
|
|
case kBET_Shield:
|
|
{
|
|
energy_shield = buff->meta->param1;
|
|
max_energy_shield = buff->meta->param1;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Human::OnAttack()
|
|
{
|
|
GrassTempShow();
|
|
}
|
|
|
|
void Human::OnHit()
|
|
{
|
|
if (HasBuffEffect(kBET_Hide)) {
|
|
RemoveBuffByEffectId(kBET_Hide);
|
|
}
|
|
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 * kSERVER_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 * kSERVER_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::CheckSpecObject()
|
|
{
|
|
bool in_grass = false;
|
|
long long spec_flags = 0;
|
|
a8::SetBitFlag(spec_flags, kColliderTag_Grass);
|
|
a8::SetBitFlag(spec_flags, kColliderTag_Loot);
|
|
std::set<ColliderComponent*> colliders;
|
|
room->map_service.GetSpecColliders(spec_flags, pos.x, pos.y, colliders);
|
|
|
|
for (const ColliderComponent* collider : colliders) {
|
|
switch (collider->owner->entity_type) {
|
|
case kET_Loot:
|
|
{
|
|
if (TestCollision((ColliderComponent*)collider)) {
|
|
Loot* loot_entity = (Loot*)collider->owner;
|
|
if (!loot_entity->pickuped &&
|
|
loot_entity->count > 0) {
|
|
MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquip(loot_entity->item_id);
|
|
if (item_meta) {
|
|
switch (item_meta->i->equip_type()) {
|
|
case kEquipType_Buff:
|
|
{
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(item_meta->i->buff_id());
|
|
if (buff_meta) {
|
|
AddBuff(buff_meta);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
loot_entity->pickuped = true;
|
|
room->RemoveObjectLater(loot_entity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kET_Obstacle:
|
|
case kET_Building:
|
|
{
|
|
if (TestCollision((ColliderComponent*)collider)) {
|
|
in_grass = true;
|
|
}
|
|
}
|
|
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(kBET_InGrass)) {
|
|
RemoveBuffByEffectId(kBET_InGrass);
|
|
}
|
|
if (grass_hide_timer_list_) {
|
|
room->xtimer.ModifyTimer(grass_hide_timer_list_,
|
|
MetaMgr::Instance()->grass_invisible_time2 * kSERVER_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 * kSERVER_FRAME_RATE,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
hide_func,
|
|
&xtimer_attacher.timer_list_);
|
|
}
|
|
}
|
|
}
|
|
|
|
float* Human::GetAbilityById(int attr_id)
|
|
{
|
|
switch (attr_id) {
|
|
case kHAT_Hp:
|
|
return &ability.hp;
|
|
break;
|
|
case kHAT_HPRecover:
|
|
return &ability.hp_recover;
|
|
break;
|
|
case kHAT_Atk:
|
|
return &ability.atk;
|
|
break;
|
|
case kHAT_Def:
|
|
return &ability.def;
|
|
break;
|
|
case kHAT_Speed:
|
|
return &ability.speed;
|
|
break;
|
|
case kHAT_ShotRange:
|
|
return &ability.shot_range;
|
|
break;
|
|
case kHAT_ShotSpeed:
|
|
return &ability.shot_speed;
|
|
break;
|
|
case kHAT_ReloadSpeed:
|
|
return &ability.reload_speed;
|
|
break;
|
|
case kHAT_FireRate:
|
|
return &ability.fire_rate;
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Human::RecalcBaseAttr()
|
|
{
|
|
ability = HumanAbility();
|
|
if (tank_meta_) {
|
|
for (auto& pair : tank_meta_->attr_init) {
|
|
float* p_attr = GetAbilityById(pair.first);
|
|
if (p_attr) {
|
|
*p_attr += pair.second;
|
|
}
|
|
}
|
|
for (auto& pair : tank_meta_->attr_up) {
|
|
float* p_attr = GetAbilityById(pair.first);
|
|
if (p_attr) {
|
|
*p_attr += (TankLv() / std::get<1>(pair.second)) * std::get<0>(pair.second);
|
|
}
|
|
}
|
|
}
|
|
if (driver_meta_ && tank_meta_ && driver_meta_->i->g_type() == tank_meta_->i->type()) {
|
|
std::map<int, float> attr_coeff_hash;
|
|
for (auto& pair : driver_meta_->attr_up) {
|
|
if (attr_coeff_hash.find(pair.first) == attr_coeff_hash.end()) {
|
|
attr_coeff_hash[pair.first] = 0.0f;
|
|
}
|
|
attr_coeff_hash[pair.first] += std::get<0>(pair.second) + driver.driver_lv * std::get<1>(pair.second);
|
|
}
|
|
for (auto& pair : driver_meta_->passive_skill_attr) {
|
|
if (driver.driver_lv >= std::get<1>(pair.second)) {
|
|
if (attr_coeff_hash.find(pair.first) == attr_coeff_hash.end()) {
|
|
attr_coeff_hash[pair.first] = 0.0f;
|
|
}
|
|
attr_coeff_hash[pair.first] += std::get<0>(pair.second);
|
|
}
|
|
}
|
|
for (auto& pair : attr_coeff_hash) {
|
|
float* p_attr = GetAbilityById(pair.first);
|
|
if (p_attr) {
|
|
*p_attr = (*p_attr) * (1 + pair.second);
|
|
}
|
|
}
|
|
}
|
|
ability.max_hp = ability.hp;
|
|
}
|
|
|
|
void Human::SendGameOver()
|
|
{
|
|
if (entity_subtype == kEST_Player) {
|
|
if (!sending_gameover_) {
|
|
InternalSendGameOver();
|
|
}
|
|
}
|
|
}
|
|
|
|
float Human::BuffAttrAbs(int buff_effect_id)
|
|
{
|
|
if (buff_effect_id >= kBET_Begin && buff_effect_id < kBET_End) {
|
|
return buff_attr_abs_[buff_effect_id];
|
|
} else {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
float Human::BuffAttrRate(int buff_effect_id)
|
|
{
|
|
if (buff_effect_id >= kBET_Begin && buff_effect_id < kBET_End) {
|
|
return buff_attr_rate_[buff_effect_id];
|
|
} else {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
float Human::GetFireRate()
|
|
{
|
|
if (curr_weapon) {
|
|
return std::max(curr_weapon->meta->i->fire_rate() - ability.fire_rate, 1.0f);
|
|
} else {
|
|
return ability.fire_rate;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
CheckSpecObject();
|
|
}
|
|
|
|
void Human::_UpdateAssaultMove()
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_Assault);
|
|
if (!buff) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
CheckSpecObject();
|
|
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) < phase->param3.GetDouble()) {
|
|
if (!hum->HasBuffEffect(kBET_Invincible)) {
|
|
float power = phase->param1.GetDouble() + ability.atk;
|
|
float def = hum->ability.def;
|
|
float finally_dmg = power * (1 - def/MetaMgr::Instance()->K);
|
|
stats.damage_amount_out += finally_dmg;
|
|
hum->DecHP(finally_dmg, entity_uniid, name, 0);
|
|
target_list.insert(hum);
|
|
}
|
|
a8::Vec2 push_dir = hum->pos - skill_target_pos;
|
|
if (std::abs(push_dir.x) > FLT_EPSILON ||
|
|
std::abs(push_dir.y) > FLT_EPSILON) {
|
|
push_dir.Normalize();
|
|
skill_dir.Normalize();
|
|
a8::Vec2 old_push_dir = push_dir;
|
|
push_dir.x = old_push_dir.y;
|
|
push_dir.y = old_push_dir.x;
|
|
if (skill_dir.CalcAngle(push_dir) < 0.001f) {
|
|
push_dir = skill_dir;
|
|
}
|
|
hum->PushHuman(push_dir, phase->param2.GetDouble());
|
|
} else {
|
|
push_dir = skill_dir;
|
|
if (std::abs(push_dir.x) > FLT_EPSILON ||
|
|
std::abs(push_dir.y) > FLT_EPSILON) {
|
|
push_dir.Normalize();
|
|
hum->PushHuman(push_dir, phase->param2.GetDouble());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TriggerBuff(target_list, kBTT_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::PushHuman(const a8::Vec2& push_dir, float distance)
|
|
{
|
|
for (int i = 0; i < distance; ++i) {
|
|
a8::Vec2 old_pos = pos;
|
|
pos = pos + push_dir;
|
|
if (IsCollisionInMapService()) {
|
|
pos = old_pos;
|
|
break;
|
|
}
|
|
room->grid_service.MoveHuman(this);
|
|
}
|
|
CheckSpecObject();
|
|
}
|
|
|
|
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();
|
|
status = 0;
|
|
ClearBuffList();
|
|
{
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(1003);
|
|
if (buff_meta) {
|
|
AddBuff(buff_meta);
|
|
}
|
|
}
|
|
room->frame_event.AddRevive(this);
|
|
room->OnHumanRevive(this);
|
|
|
|
CheckSpecObject();
|
|
use_skill = false;
|
|
curr_skill_phase = 0;
|
|
skill_dir = a8::Vec2();
|
|
skill_param1 = 0.0f;
|
|
playing_skill = false;
|
|
SyncAroundPlayers();
|
|
}
|
|
|
|
void Human::ImmediatelyRevive()
|
|
{
|
|
dead = false;
|
|
ability.hp = GetMaxHP();
|
|
status = 0;
|
|
ClearBuffList();
|
|
{
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(1003);
|
|
if (buff_meta) {
|
|
AddBuff(buff_meta);
|
|
}
|
|
}
|
|
room->frame_event.AddRevive(this);
|
|
|
|
CheckSpecObject();
|
|
use_skill = false;
|
|
curr_skill_phase = 0;
|
|
skill_dir = a8::Vec2();
|
|
skill_param1 = 0.0f;
|
|
playing_skill = false;
|
|
SyncAroundPlayers();
|
|
}
|
|
|
|
void Human::SelectSkillTargets(const a8::Vec2& target_pos, std::set<Entity*>& target_list)
|
|
{
|
|
switch (skill_meta_->i->skill_target()) {
|
|
case kST_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 kST_Self:
|
|
{
|
|
target_list.insert(this);
|
|
}
|
|
break;
|
|
case kST_FriendlyIncludeSelf:
|
|
{
|
|
target_list.insert(this);
|
|
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 kST_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 kST_EnemySingle:
|
|
{
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id);
|
|
if (entity && entity->entity_type == kET_Player) {
|
|
Human* hum = (Human*)entity;
|
|
if (hum->team_id != team_id) {
|
|
target_list.insert(hum);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kST_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 kST_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 kST_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;
|
|
case kST_SingleEnemyAndSelf:
|
|
{
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id);
|
|
if (entity && entity->entity_type == kET_Player) {
|
|
Human* hum = (Human*)entity;
|
|
if (hum->team_id != team_id) {
|
|
target_list.insert(hum);
|
|
}
|
|
}
|
|
target_list.insert(this);
|
|
}
|
|
break;
|
|
default:
|
|
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 kSkill_Jump:
|
|
{
|
|
}
|
|
break;
|
|
case kSkill_Shot:
|
|
{
|
|
MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(phase->param1.GetInt());
|
|
if (bullet_meta) {
|
|
a8::Vec2 old_attack_dir = attack_dir;
|
|
if (bullet_meta->i->equip_subtype() == kBulletType_Trace) {
|
|
Human* skill_target = room->GetHumanByUniId(skill_target_id);
|
|
if (skill_target) {
|
|
attack_dir = skill_target->pos - pos;
|
|
attack_dir.Normalize();
|
|
}
|
|
}
|
|
DirectShot(bullet_meta, skill_meta_->i->skill_id());
|
|
attack_dir = old_attack_dir;
|
|
if (bullet_meta->NeedTrace()) {
|
|
room->xtimer.AddDeadLineTimerAndAttach(bullet_meta->i->time() * kSERVER_FRAME_RATE,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(phase->param1.GetInt())
|
|
.SetParam2(skill_target_id)
|
|
.SetParam3(a8::MakeInt64(pos.x, pos.y)),
|
|
Bullet::ProcMissible,
|
|
&xtimer_attacher.timer_list_);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kSkill_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(kSERVER_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() != kBulletType_Missile) {
|
|
room->CreateBullet(this, bullet_meta, bullet_born_pos, bullet_dir, fly_distance, skill_id);
|
|
}
|
|
}
|
|
}
|
|
OnAttack();
|
|
}
|
|
|
|
void Human::FillSMGameOver(cs::SMGameOver& msg)
|
|
{
|
|
msg.set_team_id(team_id);
|
|
msg.set_team_rank(stats.rank);
|
|
msg.set_team_allcnt(1);
|
|
msg.set_game_over(room->game_over);
|
|
msg.set_victory(!dead);
|
|
msg.set_room_uuid(a8::XValue(room->room_uuid));
|
|
|
|
cs::MFPlayerStats* p = msg.add_player_stats();
|
|
{
|
|
p->set_rank(stats.rank);
|
|
p->set_player_id(entity_uniid);
|
|
p->set_player_avatar_url(avatar_url);
|
|
p->set_account_id(account_id);
|
|
p->set_kills(stats.kills);
|
|
p->set_cup(11 - stats.rank);
|
|
p->set_gold(stats.gold);
|
|
p->set_dead_times(stats.dead_times);
|
|
|
|
for (auto& pair : stats.extra_drop) {
|
|
auto p1 = p->add_extra_drop();
|
|
p1->set_key(pair.first);
|
|
p1->set_value(pair.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::GenBattleReportData(a8::MutableXObject* params)
|
|
{
|
|
params->SetVal("account_id", account_id);
|
|
params->SetVal("session_id", session_id);
|
|
params->SetVal("map_id", room->map_meta->i->map_id());
|
|
params->SetVal("map_name", room->map_meta->i->map_name());
|
|
params->SetVal("map_tpl_name", room->map_tpl_name);
|
|
params->SetVal("game_time", time(nullptr));
|
|
params->SetVal("hurt", stats.damage_amount_in);
|
|
params->SetVal("rank", stats.rank);
|
|
params->SetVal("kills", stats.kills);
|
|
params->SetVal("harm", stats.damage_amount_out);
|
|
params->SetVal("skill", stats.use_skill_times);
|
|
params->SetVal("tank1_kill", 0);
|
|
params->SetVal("tank2_kill", 0);
|
|
params->SetVal("tank3_kill", 0);
|
|
if (tank_meta_) {
|
|
switch (tank_meta_->i->type()) {
|
|
case 1:
|
|
{
|
|
params->SetVal("tank1_kill", stats.kills);
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
params->SetVal("tank2_kill", stats.kills);
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
params->SetVal("tank3_kill", stats.kills);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
{
|
|
float rank_param = MetaMgr::Instance()->GetRankRewardParam(stats.rank);
|
|
float kill_param = MetaMgr::Instance()->GetKillRewardParam(stats.kills);
|
|
int coin_num = (rank_param * MetaMgr::Instance()->rank_param) +
|
|
(kill_param * MetaMgr::Instance()->kill_param);
|
|
stats.gold = coin_num;
|
|
params->SetVal("coin_num", coin_num);
|
|
}
|
|
{
|
|
stats.score = stats.rank;
|
|
params->SetVal("score", 11 - stats.score);
|
|
}
|
|
params->SetVal("room_uuid", room->room_uuid);
|
|
}
|
|
|
|
void Human::InternalSendGameOver()
|
|
{
|
|
if (entity_subtype != kEST_Player) {
|
|
return;
|
|
}
|
|
a8::MutableXObject* params = a8::MutableXObject::NewObject();
|
|
GenBattleReportData(params);
|
|
auto on_ok = [] (a8::XParams& param, a8::XObject& data)
|
|
{
|
|
long long room_uuid = param.sender;
|
|
int hum_uniid = param.param1;
|
|
Room* room = RoomMgr::Instance()->GetRoomByUuid(room_uuid);
|
|
if (room) {
|
|
room->pending_request--;
|
|
Entity* entity = room->GetEntityByUniId(hum_uniid);
|
|
if (entity && entity->entity_type == kET_Player) {
|
|
Human* hum = (Human*)entity;
|
|
hum->sending_gameover_ = false;
|
|
hum->already_report_battle_ = true;
|
|
hum->stats.history_time_alive = data.Get("alive_time_his");
|
|
hum->stats.history_kills = data.Get("kill_his");
|
|
hum->stats.history_damage_amount = data.Get("harm_his");
|
|
hum->stats.history_heal_amount = data.Get("add_HP_his");
|
|
{
|
|
std::string extra_drop = data.Get("extra_drop").GetString();
|
|
std::vector<std::string> strings1;
|
|
a8::Split(extra_drop, strings1, '|');
|
|
for (std::string& str1 : strings1) {
|
|
std::vector<std::string> strings2;
|
|
a8::Split(str1, strings2, ':');
|
|
if (strings2.size() == 2) {
|
|
hum->stats.extra_drop.push_back(std::make_pair(
|
|
a8::XValue(strings2[0]).GetInt(),
|
|
a8::XValue(strings2[1]).GetInt()
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
cs::SMGameOver msg;
|
|
hum->FillSMGameOver(msg);
|
|
hum->SendNotifyMsg(msg);
|
|
if (!hum->sent_game_end_ && hum->entity_subtype == kEST_Player) {
|
|
GameLog::Instance()->GameEnd((Player*)hum);
|
|
hum->sent_game_end_ = true;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
auto on_error = [] (a8::XParams& param, const std::string& response)
|
|
{
|
|
a8::UdpLog::Instance()->Error("battleReport http error params: %s response: %s",
|
|
{
|
|
param.param2,
|
|
response
|
|
});
|
|
long long room_uuid = param.sender;
|
|
int hum_uniid = param.param1;
|
|
Room* room = RoomMgr::Instance()->GetRoomByUuid(room_uuid);
|
|
if (room) {
|
|
room->pending_request--;
|
|
Entity* entity = room->GetEntityByUniId(hum_uniid);
|
|
if (entity && entity->entity_type == kET_Player) {
|
|
Human* hum = (Human*)entity;
|
|
hum->sending_gameover_ = false;
|
|
++hum->send_gameover_trycount_;
|
|
if (hum->send_gameover_trycount_ < 10){
|
|
hum->SendGameOver();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
std::string url;
|
|
if (!f8::IsOnlineEnv()) {
|
|
if (App::Instance()->HasFlag(3)) {
|
|
url = "http://game2002api.the7ys.com/webapp/index.php?c=Role&a=battleReport";
|
|
} else {
|
|
url = "https://game2002api-test.kingsome.cn/webapp/index.php?c=Role&a=battleReport";
|
|
}
|
|
} else {
|
|
url = "https://game2002api.kingsome.cn/webapp/index.php?c=Role&a=battleReport";
|
|
}
|
|
room->pending_request++;
|
|
std::string data;
|
|
params->ToUrlEncodeStr(data);
|
|
f8::HttpClientPool::Instance()->HttpGet(
|
|
a8::XParams()
|
|
.SetSender(room->room_uuid)
|
|
.SetParam1(entity_uniid)
|
|
.SetParam2(data),
|
|
on_ok,
|
|
on_error,
|
|
url.c_str(),
|
|
*params,
|
|
room->room_uuid
|
|
);
|
|
delete params;
|
|
sending_gameover_ = true;
|
|
}
|