4166 lines
134 KiB
C++
4166 lines
134 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 "app.h"
|
|
#include "roommgr.h"
|
|
#include "android.h"
|
|
#include "gamelog.h"
|
|
#include "typeconvert.h"
|
|
#include "obstacle.h"
|
|
#include "player.h"
|
|
#include "buff.h"
|
|
#include "roomobstacle.h"
|
|
#include "aicomponent.h"
|
|
#include "jsondatamgr.h"
|
|
|
|
#include "framework/cpp/utils.h"
|
|
#include "framework/cpp/httpclientpool.h"
|
|
|
|
const int kReviveTimeAdd = 12;
|
|
const int kSkinNum = 4;
|
|
const int kREVIVE_BUFF_ID = 0;
|
|
|
|
void InternalShot(Human* hum,
|
|
MetaData::Equip* weapon_meta,
|
|
MetaData::EquipUpgrade* weapon_upgrade_meta,
|
|
MetaData::Equip* bullet_meta,
|
|
int weapon_lv,
|
|
int skill_id,
|
|
float fly_distance,
|
|
bool is_tank_skin)
|
|
{
|
|
for (auto& tuple : weapon_meta->bullet_born_offset) {
|
|
a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple));
|
|
bullet_born_offset.Rotate(hum->attack_dir.CalcAngle(a8::Vec2::UP));
|
|
a8::Vec2 bullet_born_pos = hum->GetPos() + bullet_born_offset;
|
|
if (hum->room->OverBorder(bullet_born_pos, 0)) {
|
|
return;
|
|
}
|
|
}
|
|
hum->room->frame_event.AddShot(hum);
|
|
for (auto& tuple : weapon_meta->bullet_born_offset) {
|
|
a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple));
|
|
bullet_born_offset.Rotate(hum->attack_dir.CalcAngle(a8::Vec2::UP));
|
|
a8::Vec2 bullet_born_pos = hum->GetPos() + bullet_born_offset;
|
|
a8::Vec2 bullet_dir = hum->attack_dir;
|
|
float bullet_angle = std::get<2>(tuple);
|
|
if (weapon_meta->i->bullet_angle() >= 0.10f) {
|
|
int angle = (int)weapon_meta->i->bullet_angle() * 1000;
|
|
#if 0
|
|
if (curr_weapon->GetUpgradeMeta()) {
|
|
angle -= curr_weapon->GetAttrValue(kHAT_BulletAngle) * 1000;
|
|
}
|
|
#endif
|
|
if (angle > 0) {
|
|
bullet_angle += (rand() % angle) / 1000.0f * (rand() % 2 == 0 ? 1 : -1);
|
|
}
|
|
}
|
|
bullet_dir.Rotate(bullet_angle / 180.0f);
|
|
if (hum->GetCar().car_id != 0) {
|
|
bullet_born_pos.x += MetaMgr::Instance()->horse_shoot_x;
|
|
bullet_born_pos.y += MetaMgr::Instance()->horse_shoot_y;
|
|
}
|
|
hum->room->frame_event.AddBullet(hum,
|
|
weapon_meta,
|
|
weapon_lv,
|
|
bullet_born_pos,
|
|
bullet_dir,
|
|
fly_distance);
|
|
if (hum->room->BattleStarted()) {
|
|
hum->room->CreateBullet(hum,
|
|
weapon_meta,
|
|
weapon_upgrade_meta,
|
|
bullet_meta,
|
|
bullet_born_pos,
|
|
bullet_dir,
|
|
fly_distance,
|
|
is_tank_skin);
|
|
}
|
|
}
|
|
}
|
|
|
|
Human::Human():MoveableEntity()
|
|
{
|
|
default_weapon.weapon_idx = 0;
|
|
default_weapon.weapon_id = 12101;
|
|
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;
|
|
}
|
|
for (int i = 0; i < kSkinNum; ++i) {
|
|
Skin& skin = a8::FastAppend(skins);
|
|
skin.skin_id = 0;
|
|
skin.skin_lv = 1;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
Human::~Human()
|
|
{
|
|
}
|
|
|
|
void Human::Initialize()
|
|
{
|
|
MoveableEntity::Initialize();
|
|
skill_xtimer_attacher_.xtimer = &room->xtimer;
|
|
RecalcSelfCollider();
|
|
volume_ = meta->volume;
|
|
observers_.insert(this);
|
|
ability.hp = meta->i->health();
|
|
for (auto& weapon : spec_weapons) {
|
|
if (weapon.meta) {
|
|
ability.hp += weapon.meta ? weapon.GetAttrValue(kHAT_MaxHp) : 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
float Human::GetSpeed()
|
|
{
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_JumpTo);
|
|
if (buff) {
|
|
return buff->meta->param2;
|
|
}
|
|
}
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_BePull);
|
|
if (buff) {
|
|
return buff->meta->param2;
|
|
}
|
|
}
|
|
if (downed) {
|
|
return meta->i->move_speed3();
|
|
} else {
|
|
if (shot_hold) {
|
|
if (curr_weapon->weapon_idx == GUN_SLOT1 ||
|
|
curr_weapon->weapon_idx == GUN_SLOT2) {
|
|
if (action_type != AT_Reload) {
|
|
return meta->i->shot_speed();
|
|
}
|
|
}
|
|
} else if (aiming) {
|
|
return std::max(1, meta->i->aiming_speed());
|
|
} else if (action_type != AT_None) {
|
|
return std::max(1, meta->i->reload_speed());
|
|
}
|
|
float speed = meta->i->move_speed();
|
|
speed = (speed + buff_attr_abs_[kHAT_Speed]) * (1 + buff_attr_rate_[kHAT_Speed]);
|
|
return std::max(speed, 1.0f);
|
|
}
|
|
}
|
|
|
|
float Human::GetSpeed4()
|
|
{
|
|
return meta->i->move_speed4();
|
|
}
|
|
|
|
void Human::FillMFObjectPart(Room* room, Human* hum, cs::MFObjectPart* part_data)
|
|
{
|
|
part_data->set_object_type(ET_Player);
|
|
cs::MFPlayerPart* p = part_data->mutable_union_obj_1();
|
|
p->set_obj_uniid(GetEntityUniId());
|
|
TypeConvert::ToPb(GetPos(), p->mutable_pos());
|
|
TypeConvert::ToPb(attack_dir, p->mutable_dir());
|
|
}
|
|
|
|
void Human::FillMFObjectFull(Room* room, Human* hum, cs::MFObjectFull* full_data)
|
|
{
|
|
full_data->set_object_type(ET_Player);
|
|
cs::MFPlayerFull* p = full_data->mutable_union_obj_1();
|
|
p->set_obj_uniid(GetEntityUniId());
|
|
TypeConvert::ToPb(GetPos(), p->mutable_pos());
|
|
TypeConvert::ToPb(attack_dir, p->mutable_dir());
|
|
|
|
p->set_health(GetHP());
|
|
p->set_max_health(GetMaxHP());
|
|
p->set_dead(dead);
|
|
p->set_downed(downed);
|
|
p->set_disconnected(disconnected);
|
|
p->set_anim_type(anim_type);
|
|
p->set_anim_seq(anim_seq);
|
|
for (auto itr : skins) {
|
|
auto skin = p->add_skin();
|
|
itr.ToPB(skin);
|
|
}
|
|
p->set_backpack(backpack);
|
|
p->set_helmet(helmet);
|
|
p->set_chest(chest);
|
|
curr_weapon->ToPB(p->mutable_weapon());
|
|
p->set_energy_shield(energy_shield);
|
|
#if 1
|
|
{
|
|
p->set_max_energy_shield(max_energy_shield);
|
|
}
|
|
#endif
|
|
if (guild_id != 0) {
|
|
p->set_guild_id(guild_id);
|
|
}
|
|
p->set_vip(vip);
|
|
p->set_sdmg(sdmg);
|
|
p->set_kill_count(stats.kills);
|
|
if (emoji1 != 0) {
|
|
p->set_emoji1(emoji1);
|
|
}
|
|
if (emoji2 != 0) {
|
|
p->set_emoji2(emoji2);
|
|
}
|
|
if (parachute != 0) {
|
|
p->set_parachute(parachute);
|
|
}
|
|
FillBodyState(p->mutable_states());
|
|
FillBuffList(p->mutable_buff_list());
|
|
if (dead) {
|
|
p->set_killer_name(stats.killer_name);
|
|
p->set_killer_id(stats.killer_id);
|
|
if (real_dead){
|
|
p->set_can_revive(false);
|
|
} else {
|
|
p->set_can_revive(true);
|
|
int countdown = 0;
|
|
if (revive_timer) {
|
|
countdown = std::ceil(room->xtimer.GetRemainTime(revive_timer) / SERVER_FRAME_RATE);
|
|
} else {
|
|
a8::UdpLog::Instance()->Warning("Human::FillMFObjectfull revive_timer == nullptr "
|
|
"dead_frameno:%d dead_times:%d alive_count:%d "
|
|
"room.frameno:%d",
|
|
{
|
|
dead_frameno,
|
|
dead_times,
|
|
room->AliveCount(),
|
|
room->GetFrameNo()
|
|
});
|
|
}
|
|
countdown = std::max(0, countdown - kReviveTimeAdd);
|
|
p->set_revive_countdown(countdown);
|
|
}
|
|
}
|
|
if (room->GetRoomMode() == kZombieMode) {
|
|
p->set_charid(meta->i->id());
|
|
}
|
|
}
|
|
|
|
void Human::FillMFPlayerStats(cs::MFPlayerStats* stats_pb)
|
|
{
|
|
stats_pb->set_player_id(GetEntityUniId());
|
|
stats_pb->set_player_avatar_url(avatar_url);
|
|
|
|
if (!dead) {
|
|
stats_pb->set_time_alive(room->GetFrameNo() * 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_pass_score(stats.pass_score);
|
|
stats_pb->set_rank_score(stats.rank_score);
|
|
stats_pb->set_has_pass(has_pass);
|
|
|
|
stats_pb->set_dead(dead);
|
|
stats_pb->set_killer_id(stats.killer_id);
|
|
stats_pb->set_killer_name(stats.killer_name);
|
|
{
|
|
Player* killer = room->GetPlayerByUniId(stats.killer_id);
|
|
if (killer) {
|
|
stats_pb->set_killer_avatar_url(killer->avatar_url);
|
|
stats_pb->set_killer_account_id(killer->account_id);
|
|
} else {
|
|
stats_pb->set_killer_avatar_url("");
|
|
stats_pb->set_killer_account_id("");
|
|
}
|
|
}
|
|
|
|
stats_pb->set_account_id(account_id);
|
|
if (guild_id != 0) {
|
|
stats_pb->set_guild_id(guild_id);
|
|
}
|
|
|
|
for (auto& pair : stats.items) {
|
|
auto p = stats_pb->add_items();
|
|
p->set_key(pair.first);
|
|
p->set_value(pair.second);
|
|
}
|
|
for (auto& pair : stats.extra_drop) {
|
|
auto p = stats_pb->add_extra_drop();
|
|
p->set_key(pair.first);
|
|
p->set_value(pair.second);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
bool Human::IsDead(Room * room)
|
|
{
|
|
return dead;
|
|
}
|
|
|
|
long long Human::GetDeadFrameNo(Room* room)
|
|
{
|
|
return dead_frameno;
|
|
}
|
|
|
|
long long Human::GetRealDeadFrameNo(Room* room)
|
|
{
|
|
return real_dead_frameno;
|
|
}
|
|
|
|
void Human::FillMFTeamData(cs::MFTeamData* team_data, bool is_game_over)
|
|
{
|
|
{
|
|
last_sync_teamdata_frameno_ = room->GetFrameNo();
|
|
|
|
team_data->set_player_id(GetEntityUniId());
|
|
if (is_game_over || !real_dead || room->GetFrameNo() - GetRealDeadFrameNo(room) < 4) {
|
|
TypeConvert::ToPb(GetPos(), 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_dead(dead);
|
|
team_data->set_downed(downed);
|
|
}
|
|
|
|
if (is_game_over || room->GetGasData().gas_mode == GasInactive) {
|
|
team_data->set_name(name);
|
|
}
|
|
|
|
if (room->GetGasData().gas_mode != GasInactive &&
|
|
room->GetFrameNo() - room->GetBattleStartFrameNo() < 4) {
|
|
team_data->set_name(name);
|
|
team_data->set_disconnected(socket_handle == 0);
|
|
}
|
|
|
|
if (is_game_over) {
|
|
team_data->set_account_id(account_id);
|
|
team_data->set_avatar_url(avatar_url);
|
|
team_data->set_user_value1(user_value1);
|
|
team_data->set_user_value2(user_value2);
|
|
team_data->set_user_value3(user_value3);
|
|
if (guild_id != 0) {
|
|
team_data->set_guild_id(guild_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::Shot(a8::Vec2& target_dir, bool& shot_ok)
|
|
{
|
|
shot_ok = false;
|
|
if (!curr_weapon->meta) {
|
|
return;
|
|
}
|
|
if (downed) {
|
|
return;
|
|
}
|
|
|
|
if (curr_weapon->weapon_idx != 0 &&
|
|
curr_weapon->ammo <= 0) {
|
|
AutoLoadingBullet();
|
|
return;
|
|
}
|
|
if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) <
|
|
curr_weapon->GetAttrValue(kHAT_FireRate)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
InternalShot(this,
|
|
curr_weapon->meta,
|
|
curr_weapon->GetUpgradeMeta(),
|
|
curr_weapon->bullet_meta,
|
|
curr_weapon->weapon_lv,
|
|
0,
|
|
5,
|
|
false);
|
|
|
|
--curr_weapon->ammo;
|
|
int slot_id = curr_weapon->meta->i->_inventory_slot();
|
|
switch (slot_id) {
|
|
case 5:
|
|
{
|
|
//手雷
|
|
if (GetInventory(slot_id) > 0) {
|
|
DecInventory(slot_id, 1);
|
|
++curr_weapon->ammo;
|
|
} else {
|
|
int weapon_idx = curr_weapon->weapon_idx;
|
|
*curr_weapon = Weapon();
|
|
curr_weapon->weapon_idx = weapon_idx;
|
|
if (weapons[SMOKE_SLOT].weapon_id != 0) {
|
|
curr_weapon = &weapons[SMOKE_SLOT];
|
|
} else {
|
|
curr_weapon = &weapons[0];
|
|
}
|
|
AutoLoadingBullet();
|
|
}
|
|
need_sync_active_player = true;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
break;
|
|
case 6:
|
|
{
|
|
//烟雾弹
|
|
if (GetInventory(slot_id) > 0) {
|
|
DecInventory(slot_id, 1);
|
|
++curr_weapon->ammo;
|
|
} else {
|
|
int weapon_idx = curr_weapon->weapon_idx;
|
|
*curr_weapon = Weapon();
|
|
curr_weapon->weapon_idx = weapon_idx;
|
|
if (weapons[FRAG_SLOT].weapon_id != 0) {
|
|
curr_weapon = &weapons[FRAG_SLOT];
|
|
} else {
|
|
curr_weapon = &weapons[0];
|
|
}
|
|
AutoLoadingBullet();
|
|
}
|
|
need_sync_active_player = true;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
break;
|
|
}
|
|
last_shot_frameno_ = room->GetFrameNo();
|
|
if (!need_sync_active_player) {
|
|
room->frame_event.AddBulletNumChg(this);
|
|
}
|
|
shot_ok = true;
|
|
}
|
|
|
|
void Human::CarShot(a8::Vec2& target_dir)
|
|
{
|
|
if (!car_weapon.meta) {
|
|
return;
|
|
}
|
|
|
|
if (car_weapon.weapon_idx != 0 &&
|
|
car_weapon.ammo <= 0) {
|
|
AutoLoadingBullet();
|
|
return;
|
|
}
|
|
|
|
if (action_type == AT_Reload) {
|
|
CancelAction();
|
|
}
|
|
if (action_type == AT_Reload ||
|
|
action_type == AT_Rescue ||
|
|
action_type == AT_UseItem ||
|
|
action_type == AT_Relive) {
|
|
CancelAction();
|
|
}
|
|
|
|
InternalShot(this,
|
|
car_weapon.meta,
|
|
car_weapon.GetUpgradeMeta(),
|
|
car_weapon.bullet_meta,
|
|
car_weapon.weapon_lv,
|
|
0,
|
|
5,
|
|
false);
|
|
|
|
--car_weapon.ammo;
|
|
last_shot_frameno_ = room->GetFrameNo();
|
|
}
|
|
|
|
void Human::RecalcSelfCollider()
|
|
{
|
|
if (!self_collider_) {
|
|
self_collider_ = new CircleCollider();
|
|
self_collider_->owner = this;
|
|
AddEntityCollider(self_collider_);
|
|
}
|
|
self_collider_->pos = a8::Vec2();
|
|
self_collider_->rad = meta->i->radius();
|
|
Buff* buff = GetBuffByEffectId(kBET_Car);
|
|
if (buff) {
|
|
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(buff->meta->param4);
|
|
if (equip_meta &&
|
|
equip_meta->i->rad() > 1 &&
|
|
equip_meta->i->equip_type() == EQUIP_TYPE_CAR
|
|
) {
|
|
self_collider_->rad = equip_meta->i->rad();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Human::IsCollisionInMapService()
|
|
{
|
|
Global::last_collider = nullptr;
|
|
if (room->OverBorder(GetPos(), meta->i->radius())){
|
|
return true;
|
|
}
|
|
|
|
std::set<ColliderComponent*> colliders;
|
|
room->map_service->GetColliders(room, GetX(), GetY(), colliders);
|
|
|
|
AabbCollider aabb_box;
|
|
GetAabbBox(aabb_box);
|
|
for (ColliderComponent* collider : colliders) {
|
|
switch (collider->owner->GetEntityType()) {
|
|
case ET_Obstacle:
|
|
{
|
|
Obstacle* obstacle = (Obstacle*)collider->owner;
|
|
#if 1
|
|
if (!obstacle->IsDead(room) &&
|
|
(
|
|
(collider->type == CT_Aabb && aabb_box.Intersect((ColliderComponent*)collider)) ||
|
|
(collider->type == CT_Circle && self_collider_->Intersect((ColliderComponent*)collider))
|
|
)
|
|
) {
|
|
#else
|
|
if (!collider->owner->dead && TestCollision((ColliderComponent*)collider)) {
|
|
#endif
|
|
if (last_collision_door_ != collider->owner) {
|
|
if (!obstacle->IsDead(room) &&
|
|
obstacle->Attackable() &&
|
|
obstacle->meta->i->drop() != 0 &&
|
|
room->GetGasData().gas_mode != GasInactive &&
|
|
(!obstacle->IsTerminatorAirDropBox(room) || GetRace() == kHumanRace)
|
|
) {
|
|
obstacle->Die(room);
|
|
if (obstacle->IsDead(room)) {
|
|
#if 0
|
|
if (obstacle->meta->i->damage_dia() > 0.01f &&
|
|
obstacle->meta->i->damage() > 0.01f) {
|
|
obstacle->Explosion(this);
|
|
}
|
|
#endif
|
|
DropItems(obstacle);
|
|
}
|
|
obstacle->BroadcastFullState(room);
|
|
if (obstacle->IsTerminatorAirDropBox(room) &&
|
|
GetRace() == kHumanRace &&
|
|
!HasBuffEffect(kBET_Terminator)) {
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(TERMINATOR_BUFF_ID);
|
|
if (buff_meta) {
|
|
AddBuff(this, buff_meta, 1);
|
|
}
|
|
}
|
|
} else {
|
|
Global::last_collider = collider;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ET_Building:
|
|
{
|
|
#if 1
|
|
if (
|
|
(
|
|
(collider->type == CT_Aabb && aabb_box.Intersect((ColliderComponent*)collider)) ||
|
|
(collider->type == CT_Circle && self_collider_->Intersect((ColliderComponent*)collider))
|
|
)
|
|
) {
|
|
#else
|
|
if (!collider->owner->dead && TestCollision((ColliderComponent*)collider)) {
|
|
#endif
|
|
if (last_collision_door_ != collider->owner) {
|
|
Global::last_collider = collider;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Human::FindPathInMapService()
|
|
{
|
|
a8::Vec2 old_pos = GetPos();
|
|
ColliderComponent* last_collider = Global::last_collider;
|
|
if (last_collider) {
|
|
switch (last_collider->type) {
|
|
case CT_Aabb:
|
|
{
|
|
}
|
|
break;
|
|
case CT_Circle:
|
|
{
|
|
a8::Vec2 extend_dir = GetPos() - last_collider->owner->GetPos();
|
|
if (std::abs(extend_dir.x) > FLT_EPSILON ||
|
|
std::abs(extend_dir.y) > FLT_EPSILON) {
|
|
extend_dir.Normalize();
|
|
{
|
|
#if 0
|
|
a8::Vec2 extend_dir_inverse(extend_dir.y, extend_dir.x);
|
|
float angle = extend_dir_inverse.CalcAngle(move_dir);
|
|
if (angle > 0.001f) {
|
|
extend_dir.Rotate(-1/180.0f);
|
|
} else {
|
|
extend_dir.Rotate(1/180.0f);
|
|
}
|
|
#endif
|
|
extend_dir.Rotate(1/180.0f);
|
|
}
|
|
float distance = ((CircleCollider*)last_collider)->rad + meta->i->radius();
|
|
SetPos(last_collider->owner->GetPos() + extend_dir * (distance + 1));
|
|
if (IsCollisionInMapService()) {
|
|
SetPos(old_pos);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
{
|
|
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) { //相互垂直
|
|
//向上
|
|
SetPos(old_pos + a8::Vec2::UP);
|
|
if (!IsCollisionInMapService()) {
|
|
return;
|
|
} else {
|
|
//向下
|
|
SetPos(old_pos + a8::Vec2::DOWN);
|
|
if (!IsCollisionInMapService()) {
|
|
return;
|
|
}
|
|
}
|
|
} else if (up_dot > 0.001f) { //基本相同
|
|
SetPos(old_pos + (at_left_side ? a8::Vec2::LEFT : a8::Vec2::RIGHT));
|
|
if (!IsCollisionInMapService()) {
|
|
return;
|
|
} else {
|
|
//向上
|
|
SetPos(old_pos + a8::Vec2::UP);
|
|
if (!IsCollisionInMapService()) {
|
|
return;
|
|
}
|
|
}
|
|
} else if (up_dot < 0.001f) { //基本相反
|
|
SetPos(old_pos + (at_left_side ? a8::Vec2::LEFT : a8::Vec2::RIGHT));
|
|
if (!IsCollisionInMapService()) {
|
|
return;
|
|
} else {
|
|
//向下
|
|
SetPos(old_pos + a8::Vec2::DOWN);
|
|
if (!IsCollisionInMapService()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SetPos(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->GetFrameNo() - last_use_skill_frameno_) * FRAME_RATE_MS)) {
|
|
ProcSkillPhase(phase);
|
|
++curr_skill_phase;
|
|
}
|
|
} else {
|
|
playing_skill = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::UpdatePoisoning()
|
|
{
|
|
if (dead) {
|
|
return;
|
|
}
|
|
bool need_notify = poisoning_time > 1000;
|
|
while (poisoning_time > 1000) {
|
|
if (room->GetGasData().is_last_gas) {
|
|
DecHP(room->GetGasData().new_area_meta->i->hurt(), VP_SafeArea, "毒圈", VW_SafeArea);
|
|
} else {
|
|
DecHP(room->GetGasData().old_area_meta->i->hurt(), VP_SafeArea, "毒圈", VW_SafeArea);
|
|
}
|
|
if (dead) {
|
|
poisoning_time = 0;
|
|
break;
|
|
}
|
|
poisoning_time -= 1000;
|
|
}
|
|
if (need_notify && IsEntitySubType(EST_Player)) {
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
|
|
void Human::SyncAroundPlayers(const char* file, int line, const char* func)
|
|
{
|
|
if (a8::HasBitFlag(status, HS_Disable)) {
|
|
return;
|
|
}
|
|
#ifdef DEBUG
|
|
#if 0
|
|
room->CheckPartObjects();
|
|
a8::UdpLog::Instance()->Debug("room_idx:%d syncaround begin %s %d %s",
|
|
{
|
|
room->GetRoomIdx(),
|
|
file,
|
|
line,
|
|
func
|
|
});
|
|
#endif
|
|
#endif
|
|
TouchAllLayerHumanList
|
|
(
|
|
[this, file, line, func] (Human* hum, bool& stop)
|
|
{
|
|
hum->AddToNewObjects(this);
|
|
#ifdef DEBUG
|
|
#if 0
|
|
{
|
|
std::string objs_str;
|
|
for (auto& obj : hum->part_objects) {
|
|
objs_str += a8::Format("%d ", {(long long)obj});
|
|
}
|
|
a8::UdpLog::Instance()->Debug("hum1 %d %s", {(long long)hum, objs_str});
|
|
}
|
|
{
|
|
std::string objs_str;
|
|
for (auto& obj : part_objects) {
|
|
objs_str += a8::Format("%d ", {(long long)obj});
|
|
}
|
|
a8::UdpLog::Instance()->Debug("hum2 %d %s", {(long long)this, objs_str});
|
|
}
|
|
room->CheckPartObjects(hum, this);
|
|
hum->InPartObjects(this);
|
|
#endif
|
|
#endif
|
|
assert(hum->part_objects.find(this) != hum->part_objects.end());
|
|
if (hum->part_objects.find(this) == hum->part_objects.end()) {
|
|
static long long last_debugout_tick = 0;
|
|
if (a8::XGetTickCount() - last_debugout_tick > 1000 * 10) {
|
|
last_debugout_tick = a8::XGetTickCount();
|
|
a8::UdpLog::Instance()->Warning
|
|
("SyncAroundPlayers error room_idx:%d, file:%s line:%d func:%s",
|
|
{
|
|
room->GetRoomIdx(),
|
|
file,
|
|
line,
|
|
func
|
|
});
|
|
}
|
|
}
|
|
});
|
|
#ifdef DEBUG
|
|
#if 0
|
|
room->CheckPartObjects();
|
|
a8::UdpLog::Instance()->Debug("syncaround end %s %d %s",
|
|
{
|
|
file,
|
|
line,
|
|
func
|
|
});
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void Human::AutoLoadingBullet(bool manual)
|
|
{
|
|
Weapon* p_weapon = curr_weapon;
|
|
if (car_weapon.meta) {
|
|
p_weapon = &car_weapon;
|
|
}
|
|
if (p_weapon->weapon_idx != 0 &&
|
|
(p_weapon->ammo <= 0 ||
|
|
(manual && p_weapon->ammo < p_weapon->GetClipVolume()))
|
|
) {
|
|
MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(p_weapon->meta->i->use_bullet());
|
|
if (bullet_meta &&
|
|
bullet_meta->i->_inventory_slot() >= 0 &&
|
|
bullet_meta->i->_inventory_slot() < (int)inventory_.size()
|
|
) {
|
|
if (GetInventory(bullet_meta->i->_inventory_slot()) > 0) {
|
|
if (on_loading_bullet) {
|
|
on_loading_bullet();
|
|
}
|
|
StartAction(AT_Reload,
|
|
p_weapon->GetAttrValue(kHAT_ReloadTime),
|
|
p_weapon->weapon_id,
|
|
p_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;
|
|
}
|
|
action_duration = std::max(0, action_duration);
|
|
this->action_type = action_type;
|
|
this->action_frameno = room->GetFrameNo();
|
|
this->action_duration = action_duration;
|
|
this->action_item_id = item_id;
|
|
this->action_target_id = target_id;
|
|
need_sync_active_player = true;
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
}
|
|
|
|
void Human::CancelAction()
|
|
{
|
|
if (action_type == AT_Relive) {
|
|
Entity* entity = room->GetEntityByUniId(action_target_id);
|
|
if (!entity->IsEntityType(ET_Player)) {
|
|
Human* hum = (Human*)entity;
|
|
if (hum->action_type == AT_Rescue) {
|
|
hum->CancelAction();
|
|
}
|
|
}
|
|
}
|
|
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::FillSMGameOver(cs::SMGameOver& msg)
|
|
{
|
|
if (stats.rank <= 0) {
|
|
std::vector<Human*> human_list;
|
|
room->TouchHumanList(a8::XParams(),
|
|
[&human_list] (Human* hum, a8::XParams& param) -> bool
|
|
{
|
|
if (hum->leave_frameno_ == 0 ||
|
|
hum->leave_frameno_ > hum->room->GetBattleStartFrameNo()) {
|
|
human_list.push_back(hum);
|
|
}
|
|
return true;
|
|
});
|
|
std::sort(human_list.begin(), human_list.end(),
|
|
[] (Human* a, Human* b )
|
|
{
|
|
if (a->real_dead && b->real_dead) {
|
|
if (a->dead_frameno == b->dead_frameno) {
|
|
return a->GetEntityUniId() < b->GetEntityUniId();
|
|
} else {
|
|
return a->dead_frameno == 0 ||
|
|
(b->dead_frameno != 0 && a->dead_frameno > b->dead_frameno);
|
|
}
|
|
} else {
|
|
if (a->real_dead) {
|
|
return false;
|
|
}
|
|
if (b->real_dead) {
|
|
return true;
|
|
}
|
|
return a->GetEntityUniId() < b->GetEntityUniId();
|
|
}
|
|
});
|
|
int rank = human_list.size();
|
|
for (size_t i = 0; i < human_list.size(); ++i) {
|
|
if (human_list[i] == this) {
|
|
rank = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
if (room->GetAliveTeamNum() == 1) {
|
|
std::set<Human*>* alive_team = room->GetAliveTeam();
|
|
if (alive_team == team_members) {
|
|
rank = 1;
|
|
}
|
|
}
|
|
stats.rank = rank;
|
|
}
|
|
|
|
msg.set_team_id(team_id);
|
|
msg.set_team_rank(stats.rank);
|
|
msg.set_team_allcnt(1);
|
|
msg.set_game_over(room->IsGameOver());
|
|
msg.set_victory(!dead);
|
|
msg.set_room_uuid(a8::XValue(room->GetRoomUuid()));
|
|
|
|
{
|
|
for (auto& itr : *team_members) {
|
|
if (itr != this) {
|
|
itr->FillMFTeamData(msg.add_team_data(), true);
|
|
}
|
|
}
|
|
}
|
|
{
|
|
cs::MFPlayerStats* p = msg.add_player_stats();
|
|
FillMFPlayerStats(p);
|
|
}
|
|
{
|
|
for (auto& pair : spoils_items) {
|
|
auto p = msg.add_spoils_items();
|
|
p->add_values(pair.first);
|
|
p->add_values(pair.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::BeKill(int killer_id, const std::string& killer_name, int weapon_id)
|
|
{
|
|
#ifdef DEBUG
|
|
room->CheckPartObjects();
|
|
#endif
|
|
if (!dead && !room->IsGameOver() && !real_dead) {
|
|
lethal_weapon = weapon_id;
|
|
Entity* hum = room->GetEntityByUniId(killer_id);
|
|
if (hum && hum->IsEntityType(ET_Player)) {
|
|
if (killer_id == GetEntityUniId()) {
|
|
std::string msg = a8::Format("%s 自杀",
|
|
{
|
|
killer_name,
|
|
});
|
|
SendRollMsg(msg);
|
|
} else {
|
|
((Human*)hum)->stats.kills++;
|
|
((Human*)hum)->stats.last_kill_frameno = room->GetFrameNo();
|
|
((Human*)hum)->kill_humans.insert(this);
|
|
((Human*)hum)->SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
if (weapon_id == VW_Tank) {
|
|
std::string msg = a8::Format("%s 使用 %s 干掉了 %s",
|
|
{
|
|
killer_name,
|
|
"载具",
|
|
name
|
|
});
|
|
SendRollMsg(msg);
|
|
} else {
|
|
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(weapon_id);
|
|
if (equip_meta) {
|
|
std::string msg = a8::Format("%s 使用 %s 干掉了 %s",
|
|
{
|
|
killer_name,
|
|
equip_meta->i->name(),
|
|
name
|
|
});
|
|
SendRollMsg(msg);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
switch (weapon_id) {
|
|
case VW_SafeArea:
|
|
{
|
|
std::string msg = a8::Format("%s 被毒圈干掉",
|
|
{
|
|
name
|
|
});
|
|
SendRollMsg(msg);
|
|
}
|
|
break;
|
|
case VW_Spectate:
|
|
{
|
|
std::string msg = a8::Format("%s 自杀",
|
|
{
|
|
name
|
|
});
|
|
SendRollMsg(msg);
|
|
}
|
|
break;
|
|
case VW_SelfDetonate:
|
|
{
|
|
std::string msg = a8::Format("%s 被炸死",
|
|
{
|
|
name
|
|
});
|
|
SendRollMsg(msg);
|
|
}
|
|
break;
|
|
case VW_Mine:
|
|
{
|
|
std::string msg = a8::Format("%s 被地雷炸死",
|
|
{
|
|
name
|
|
});
|
|
SendRollMsg(msg);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
++stats.dead_times;
|
|
stats.killer_id = killer_id;
|
|
stats.killer_name = killer_name;
|
|
stats.weapon_id = weapon_id;
|
|
if (room->GetRoomMode() == kZombieMode) {
|
|
dead = true;
|
|
downed = false;
|
|
real_dead = true;
|
|
dead_frameno = room->GetFrameNo();
|
|
DoGetDown();
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
ClearLordMode();
|
|
ClearBuffList();
|
|
room->frame_event.AddDead(this,
|
|
meta->i->revive_time() * 1000);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(meta->i->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->OnHumanDie(this);
|
|
} else {
|
|
dead = true;
|
|
downed = false;
|
|
ability.hp = 0.0f;
|
|
dead_frameno = room->GetFrameNo();
|
|
++dead_times;
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
ClearLordMode();
|
|
#ifdef DEBUG
|
|
room->CheckPartObjects();
|
|
#endif
|
|
int max_revive_times = MetaMgr::Instance()->GetSysParamAsInt("max_revive_times", 1);
|
|
if (weapon_id != VW_Spectate &&
|
|
dead_times <= max_revive_times &&
|
|
room->AliveCount() >= 5 &&
|
|
IsEntitySubType(EST_Player)) {
|
|
Revive();
|
|
} else {
|
|
real_dead = true;
|
|
OnDie();
|
|
}
|
|
DoGetDown();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::DecHP(float dec_hp, int killer_id, const std::string& killer_name, int weapon_id)
|
|
{
|
|
#ifdef DEBUG
|
|
#if 0
|
|
if (IsPlayer()) {
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
auto downed_func =
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
if (!hum->downed) {
|
|
hum->FreeDownedTimer();
|
|
return;
|
|
}
|
|
if (hum->dead) {
|
|
hum->FreeDownedTimer();
|
|
return;
|
|
}
|
|
if (!hum->HasLiveTeammate()) {
|
|
hum->FreeDownedTimer();
|
|
hum->BeKill(param.param1, param.param2, param.param3);
|
|
return;
|
|
}
|
|
int dec_hp = MetaMgr::Instance()->GetSysParamAsInt("downed_dec_hp");
|
|
hum->DecHP(dec_hp, param.param1, param.param2, param.param3);
|
|
};
|
|
if (energy_shield > 0.001f) {
|
|
energy_shield = std::max(0.0f, energy_shield - dec_hp);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
} else {
|
|
float old_health = GetHP();
|
|
float new_health = std::max(0.0f, GetHP() - dec_hp);
|
|
AdjustDecHp(old_health, new_health);
|
|
ability.hp = std::max(0.0f, new_health);
|
|
if (GetHP() - old_health > 0.001f) {
|
|
stats.damage_amount_in += GetHP() - old_health;
|
|
}
|
|
if (GetHP() <= 0.0001f && !dead) {
|
|
if (downed) {
|
|
if (downed_timer) {
|
|
room->xtimer.DeleteTimer(downed_timer);
|
|
}
|
|
downed = false;
|
|
downed_timer = nullptr;
|
|
BeKill(killer_id, killer_name, weapon_id);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
} else {
|
|
if (HasNoDownedTeammate()) {
|
|
ability.hp = MetaMgr::Instance()->GetSysParamAsInt("downed_recover_hp");
|
|
downed = true;
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
CancelAction();
|
|
DoGetDown();
|
|
downed_timer = room->xtimer.AddRepeatTimerAndAttach
|
|
(
|
|
SERVER_FRAME_RATE,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(killer_id)
|
|
.SetParam2(killer_name)
|
|
.SetParam3(weapon_id),
|
|
downed_func,
|
|
&xtimer_attacher.timer_list_
|
|
);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
} else {
|
|
BeKill(killer_id, killer_name, weapon_id);
|
|
}
|
|
}
|
|
}
|
|
room->frame_event.AddHpChg(this);
|
|
}
|
|
}
|
|
|
|
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::ClearPartObjects()
|
|
{
|
|
part_objects.clear();
|
|
}
|
|
|
|
int Human::GetPartObjectsCount()
|
|
{
|
|
return part_objects.size();
|
|
}
|
|
|
|
bool Human::InPartObjects(Human* target)
|
|
{
|
|
return part_objects.find(target) != part_objects.end();
|
|
}
|
|
|
|
void Human::RemoveObjects(Entity* entity)
|
|
{
|
|
del_objects.insert(entity->GetEntityUniId());
|
|
}
|
|
|
|
void Human::AddOutObjects(Entity* entity)
|
|
{
|
|
out_objects.insert(entity->GetEntityUniId());
|
|
}
|
|
|
|
void Human::RemoveOutObjects(Entity* entity)
|
|
{
|
|
out_objects.erase(entity->GetEntityUniId());
|
|
}
|
|
|
|
bool Human::HasLiveTeammate()
|
|
{
|
|
if (room->GetRoomMode() == kZombieMode) {
|
|
return true;
|
|
}
|
|
if (team_members) {
|
|
for (auto& hum : *team_members) {
|
|
if (hum != this && !hum->dead) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Human::HasNoDownedTeammate()
|
|
{
|
|
if (room->GetRoomMode() == kZombieMode) {
|
|
if (GetRace() == kHumanRace) {
|
|
bool has_now_downed_teammate = false;
|
|
Human* myself = this;
|
|
room->TouchHumanList
|
|
(
|
|
a8::XParams(),
|
|
[myself, &has_now_downed_teammate] (Human* hum, const a8::XParams& param)
|
|
{
|
|
if (myself != hum &&
|
|
!hum->dead &&
|
|
!hum->downed &&
|
|
hum->GetRace() == kHumanRace) {
|
|
has_now_downed_teammate = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
);
|
|
return has_now_downed_teammate;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
if (team_members) {
|
|
for (auto& hum : *team_members) {
|
|
if (hum != this && !hum->dead && !hum->downed) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Human::CanUseSkill()
|
|
{
|
|
if (downed) {
|
|
return false;
|
|
}
|
|
if (!skill_meta_) {
|
|
return false;
|
|
}
|
|
if (GetSkillLeftTime() > 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Human::DoSkill()
|
|
{
|
|
if (action_type == AT_Reload ||
|
|
action_type == AT_UseItem
|
|
) {
|
|
CancelAction();
|
|
}
|
|
if (CanUseSkill()) {
|
|
ResetSkill();
|
|
playing_skill = true;
|
|
last_use_skill_frameno_ = room->GetFrameNo();
|
|
if (skill_meta_->i->skill_target() == kST_Self
|
|
) {
|
|
skill_target_id = GetEntityUniId();
|
|
}
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id);
|
|
if (entity && entity->IsEntityType(ET_Player)) {
|
|
Human* hum = (Human*)entity;
|
|
std::set<Entity*> target_list;
|
|
skill_target_pos = hum->GetPos();
|
|
SelectSkillTargets(hum->GetPos(), 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();
|
|
#if 1
|
|
room->frame_event.AddSkillCdChg(this);
|
|
#else
|
|
need_sync_active_player = true;
|
|
#endif
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
}
|
|
use_skill = false;
|
|
}
|
|
|
|
void Human::DoGetDown()
|
|
{
|
|
if (car_.car_id != 0) {
|
|
int loot_uniid = room->CreateLoot(car_.car_id, GetPos(), 1, 1);
|
|
Entity* loot_entity = room->GetEntityByUniId(loot_uniid);
|
|
if (loot_entity && loot_entity->IsEntityType(ET_Loot)) {
|
|
((Loot*)loot_entity)->bullet_num = 0;
|
|
((Loot*)loot_entity)->param1 = 0;
|
|
((Loot*)loot_entity)->param2 = 0;
|
|
room->UpdateCarObject(car_.car_uniid,
|
|
loot_entity->GetEntityUniId(),
|
|
loot_entity->GetPos());
|
|
}
|
|
room->TakeOffCarObject(loot_uniid, GetPos());
|
|
if (car_.meta->i->buffid()) {
|
|
RemoveBuffById(car_.meta->i->buffid());
|
|
RecalcSelfCollider();
|
|
}
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
room->NotifyUiUpdate();
|
|
car_ = HumanCar();
|
|
car_weapon = Weapon();
|
|
CancelAction();
|
|
}
|
|
}
|
|
|
|
void Human::FindLocation()
|
|
{
|
|
Entity* target = nullptr;
|
|
TouchAllLayerEntityList
|
|
(
|
|
[this, &target] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Obstacle:
|
|
{
|
|
if (!target) {
|
|
if (TestCollision(room, entity)) {
|
|
target = entity;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ET_Building:
|
|
{
|
|
if (!target || !target->IsEntityType(ET_Building)) {
|
|
AabbCollider aabb_box;
|
|
entity->GetAabbBox(aabb_box);
|
|
if (TestCollision(room, &aabb_box)) {
|
|
target = entity;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
if (target) {
|
|
FindLocationWithTarget(target);
|
|
}
|
|
}
|
|
|
|
void Human::RefreshView()
|
|
{
|
|
TouchAllLayerHumanList
|
|
(
|
|
[this] (Human* hum, bool& stop)
|
|
{
|
|
hum->AddToNewObjects(this);
|
|
hum->AddToPartObjects(this);
|
|
AddToNewObjects(hum);
|
|
AddToPartObjects(hum);
|
|
});
|
|
TouchAllLayerEntityList
|
|
(
|
|
[this] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
case ET_Obstacle:
|
|
case ET_Loot:
|
|
{
|
|
AddToNewObjects(entity);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
void Human::OnGridListChange(std::set<GridCell*>& old_grids,
|
|
std::set<GridCell*>& inc_grids,
|
|
std::set<GridCell*>& dec_grids
|
|
)
|
|
{
|
|
ProcIncGridList(old_grids, inc_grids, dec_grids);
|
|
ProcDecGridList(old_grids, inc_grids, dec_grids);
|
|
|
|
if (IsPlayer() && on_grid_chg) {
|
|
on_grid_chg(this);
|
|
}
|
|
}
|
|
|
|
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->GetFrameNo() - 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);
|
|
}
|
|
}
|
|
#if 1
|
|
for (auto itr : skins) {
|
|
auto skin = player_data->add_skin();
|
|
itr.ToPB(skin);
|
|
}
|
|
#else
|
|
skin.ToPB(player_data->mutable_skin());
|
|
#endif
|
|
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);
|
|
}
|
|
player_data->set_energy_shield(energy_shield);
|
|
#if 1
|
|
{
|
|
player_data->set_max_energy_shield(max_energy_shield);
|
|
}
|
|
#endif
|
|
#if 0
|
|
if (skill_meta) {
|
|
if (last_use_skill_frameno_ == 0) {
|
|
player_data->set_skill_left_time(0);
|
|
player_data->set_skill_cd_time(skill_meta->i->cd_time() * 1000);
|
|
} else {
|
|
int passed_time = (room->GetFrameNo() - last_use_skill_frameno_) * FRAME_RATE_MS;
|
|
int skill_left_time = std::max(0, skill_meta->i->cd_time() * 1000 - passed_time);
|
|
player_data->set_skill_left_time(skill_left_time);
|
|
player_data->set_skill_cd_time(skill_meta->i->cd_time() * 1000);
|
|
}
|
|
}
|
|
#endif
|
|
FillBodyState(player_data->mutable_states());
|
|
FillItemList(player_data->mutable_items());
|
|
}
|
|
|
|
void Human::FillMFGasData(cs::MFGasData* gas_data)
|
|
{
|
|
gas_data->set_mode(room->GetGasData().gas_mode);
|
|
if (room->GetGasData().gas_mode == GasInactive) {
|
|
long long duration = room->GetGasInactiveTime() * SERVER_FRAME_RATE -
|
|
(room->GetFrameNo() - room->GetGasData().gas_start_frameno);
|
|
gas_data->set_duration(std::max(duration * 50, (long long)1000) / 1000);
|
|
} else if (room->GetGasData().gas_mode == GasMoving) {
|
|
if (room->GetGasData().new_area_meta->i->shrink_speed() > 0.01f) {
|
|
long long duration = (room->GetGasData().old_area_meta->i->rad() - room->GetGasData().new_area_meta->i->rad()) /
|
|
room->GetGasData().new_area_meta->i->shrink_speed();
|
|
++duration;
|
|
gas_data->set_duration(++duration);
|
|
} else {
|
|
gas_data->set_duration(0);
|
|
}
|
|
} else {
|
|
if (room->GetGasData().old_area_meta->i->wait_time() <= 0) {
|
|
gas_data->set_duration(0);
|
|
} else {
|
|
long long duration = room->GetGasData().old_area_meta->i->wait_time() * 20 -
|
|
(room->GetFrameNo() - room->GetGasData().gas_start_frameno);
|
|
gas_data->set_duration(std::max(duration * 50, (long long)1000) / 1000);
|
|
}
|
|
}
|
|
TypeConvert::ToPb(room->GetGasData().pos_old, gas_data->mutable_pos_old());
|
|
TypeConvert::ToPb(room->GetGasData().pos_new, gas_data->mutable_pos_new());
|
|
gas_data->set_rad_old(room->GetGasData().rad_old);
|
|
gas_data->set_rad_new(room->GetGasData().rad_new);
|
|
}
|
|
|
|
bool Human::CanSee(const Human* hum) const
|
|
{
|
|
return room->grid_service->InView(GetGridId(), hum->GetGridId());
|
|
}
|
|
|
|
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];
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::RecalcBaseAttr()
|
|
{
|
|
ability.def = meta->i->def();
|
|
MetaData::Equip* chest_meta = MetaMgr::Instance()->GetEquip(chest);
|
|
if (chest_meta) {
|
|
ability.def += chest_meta->i->def();
|
|
}
|
|
MetaData::Equip* helmet_meta = MetaMgr::Instance()->GetEquip(helmet);
|
|
if (helmet_meta) {
|
|
ability.def += helmet_meta->i->def();
|
|
}
|
|
ability.max_hp = std::max(ability.hp, ability.max_hp);
|
|
}
|
|
|
|
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::FillBodyState(::google::protobuf::RepeatedPtrField<::cs::MFBodyState>* states)
|
|
{
|
|
if (pain_killer_timer) {
|
|
int passed_time = (room->GetFrameNo() - 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);
|
|
|
|
cs::MFBodyState* state = states->Add();
|
|
state->set_state_type(1);
|
|
state->set_left_time(left_time);
|
|
state->set_lasting_time(anodyne_max_time * 1000);
|
|
}
|
|
}
|
|
|
|
void Human::FillBuffList(::google::protobuf::RepeatedPtrField<::cs::MFBuff>* pb_buff_list)
|
|
{
|
|
for (auto& itr : buff_list_) {
|
|
auto buff = pb_buff_list->Add();
|
|
itr.FillMFBuff(buff);
|
|
}
|
|
}
|
|
|
|
void Human::FillItemList(::google::protobuf::RepeatedPtrField<::cs::MFPair>* pb_item_list)
|
|
{
|
|
for (auto& pair : items_) {
|
|
auto p = pb_item_list->Add();
|
|
p->set_key(pair.first);
|
|
p->set_value(pair.second);
|
|
}
|
|
}
|
|
|
|
void Human::AddObserver(Human* observer)
|
|
{
|
|
observers_.insert(observer);
|
|
}
|
|
|
|
void Human::RemoveObserver(Human* observer)
|
|
{
|
|
observers_.erase(observer);
|
|
}
|
|
|
|
void Human::SendUpdateMsg()
|
|
{
|
|
if (!follow_target_ && !a8::HasBitFlag(status, HS_Disable) && IsPlayer()) {
|
|
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->GetGasData().gas_start_frameno) {
|
|
last_sync_gas_frameno = room->GetGasData().gas_start_frameno;
|
|
FillMFGasData(msg->mutable_gas_data());
|
|
}
|
|
NotifyObservers(msg, active_player_data_pb);
|
|
delete msg;
|
|
|
|
if (active_player_data_pb) {
|
|
delete active_player_data_pb;
|
|
}
|
|
++send_msg_times;
|
|
} else {
|
|
need_sync_active_player = false;
|
|
}
|
|
ClearFrameData();
|
|
}
|
|
|
|
void Human::SendGameOver()
|
|
{
|
|
if (IsEntitySubType(EST_Player)) {
|
|
//!!!必须要在SendNotifyMsg之前注意哦
|
|
if (!sent_battlereport_) {
|
|
SendBattleReport();
|
|
sent_battlereport_ = true;
|
|
GameLog::Instance()->GameEnd((Player*)this);
|
|
}
|
|
{
|
|
cs::SMGameOver msg;
|
|
FillSMGameOver(msg);
|
|
SendNotifyMsg(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
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->GetFrameNo() - action_frameno) * 1.0f / SERVER_FRAME_RATE) * 1000
|
|
);
|
|
if (duration <= 0) {
|
|
switch (action_type) {
|
|
case AT_Reload:
|
|
{
|
|
ProcReloadAction();
|
|
}
|
|
break;
|
|
case AT_UseItem:
|
|
{
|
|
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 = GetHP();
|
|
ability.hp += item_meta->i->heal();
|
|
ability.hp = std::min(GetHP(), GetMaxHP());
|
|
stats.heal_amount += GetHP() - old_health;
|
|
DecInventory(item_meta->i->_inventory_slot(), 1);
|
|
need_sync_active_player = true;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
}
|
|
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->GetFrameNo() - 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->GetFrameNo();
|
|
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->GetHP();
|
|
hum->ability.hp += param.param1.GetDouble();
|
|
hum->ability.hp = std::min(hum->GetHP(), hum->GetMaxHP());
|
|
hum->stats.heal_amount += hum->GetHP() - old_health;
|
|
hum->SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
if (hum->room->GetFrameNo() - 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;
|
|
}
|
|
}
|
|
break;
|
|
case AT_Relive:
|
|
{
|
|
Entity* entity = room->GetEntityByUniId(action_target_id);
|
|
if (!entity->IsEntityType(ET_Player)) {
|
|
return;
|
|
}
|
|
Human* hum = (Human*)entity;
|
|
if (hum->action_type == AT_Rescue) {
|
|
hum->CancelAction();
|
|
return;
|
|
}
|
|
if (!hum->dead && hum->downed) {
|
|
hum->ability.hp = MetaMgr::Instance()->GetSysParamAsInt("downed_relive_recover_hp");
|
|
hum->downed = false;
|
|
if (hum->downed_timer) {
|
|
room->xtimer.DeleteTimer(hum->downed_timer);
|
|
hum->downed_timer = nullptr;
|
|
}
|
|
++hum->stats.rescue_member;
|
|
}
|
|
hum->SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ResetAction();
|
|
}
|
|
}
|
|
|
|
void Human::SendUIUpdate()
|
|
{
|
|
cs::SMUiUpdate notifymsg;
|
|
notifymsg.set_alive_count(room->AliveCount());
|
|
notifymsg.set_kill_count(stats.kills);
|
|
room->FillSMUiUpdate(notifymsg);
|
|
SendNotifyMsg(notifymsg);
|
|
}
|
|
|
|
void Human::SendWxVoip()
|
|
{
|
|
cs::SMWxVoip notifymsg;
|
|
if (!team_uuid.empty()) {
|
|
notifymsg.set_group_id(a8::XValue(room->GetRoomUuid()).GetString() + "_" + a8::XValue(team_id).GetString());
|
|
}
|
|
SendNotifyMsg(notifymsg);
|
|
}
|
|
|
|
void Human::SendSysPiaoMsg(const std::string& msg, int color, int duration)
|
|
{
|
|
cs::SMSysPiaoMsg notifymsg;
|
|
notifymsg.set_msg(msg);
|
|
notifymsg.set_color(color);
|
|
notifymsg.set_duration(duration);
|
|
SendNotifyMsg(notifymsg);
|
|
}
|
|
|
|
void Human::SendShowCountdown(const std::string& msg, int countdown)
|
|
{
|
|
cs::SMShowCountdown notifymsg;
|
|
notifymsg.set_msg(msg);
|
|
notifymsg.set_countdown(countdown);
|
|
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;
|
|
}
|
|
|
|
bool Human::HasSpecMove()
|
|
{
|
|
return GetBuffByEffectId(kBET_JumpTo) ||
|
|
GetBuffByEffectId(kBET_BePull);
|
|
}
|
|
|
|
void Human::_UpdateSpecMove()
|
|
{
|
|
if (!HasSpecMove()) {
|
|
return;
|
|
}
|
|
bool move_end = false;
|
|
float target_distance = target_pos.Distance(GetPos());
|
|
if (target_distance <= 0.000001f) {
|
|
move_end = true;
|
|
} else {
|
|
a8::Vec2 old_move_dir = move_dir;
|
|
move_dir = target_pos - GetPos();
|
|
move_dir.Normalize();
|
|
bool is_collision = false;
|
|
std::function<bool ()> old_on_move_collision = on_move_collision;
|
|
on_move_collision =
|
|
[&is_collision] () {
|
|
is_collision = true;
|
|
return false;
|
|
};
|
|
|
|
_UpdateMove(std::min((int)target_distance, (int)GetSpeed()));
|
|
|
|
on_move_collision = old_on_move_collision;
|
|
move_dir = old_move_dir;
|
|
target_distance = target_pos.Distance(GetPos());
|
|
if (is_collision || target_distance <= 1.0001f) {
|
|
move_end = true;
|
|
}
|
|
}
|
|
if (move_end) {
|
|
Buff* buff = GetBuffByEffectId(kBET_JumpTo);
|
|
if (buff) {
|
|
if (buff->skill_meta == skill_meta_ &&
|
|
!skill_meta_->phases.empty()) {
|
|
std::set<Entity*> target_list;
|
|
metatable::Skill* mutable_skill_meta = (metatable::Skill*)skill_meta_->i;
|
|
float old_skill_distance = skill_meta_->i->skill_distance();
|
|
mutable_skill_meta->set_skill_distance(skill_meta_->phases[0].param1.GetDouble());
|
|
SelectSkillTargets(GetPos(), target_list);
|
|
mutable_skill_meta->set_skill_distance(old_skill_distance);
|
|
|
|
TriggerBuff(target_list, kBTT_SkillHit);
|
|
}
|
|
RemoveBuffByEffectId(kBET_JumpTo);
|
|
}
|
|
buff = GetBuffByEffectId(kBET_BePull);
|
|
if (buff) {
|
|
MetaData::Buff* new_buff = MetaMgr::Instance()->GetBuff(1039);
|
|
if (new_buff) {
|
|
AddBuff(this, new_buff, 1);
|
|
}
|
|
RemoveBuffByEffectId(kBET_BePull);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::_UpdateMove(int speed)
|
|
{
|
|
do {
|
|
int distance = std::min(5, speed);
|
|
_InternalUpdateMove(distance);
|
|
speed -= distance;
|
|
} while (speed > 0);
|
|
}
|
|
|
|
void Human::ChangeToRace(RaceType_e race, int level)
|
|
{
|
|
if (race != kHumanRace &&
|
|
race != kZombieRace) {
|
|
abort();
|
|
}
|
|
if (race_ != race && IsAndroid()) {
|
|
Android* android = (Android*)this;
|
|
android->ai->Reset();
|
|
if (race == kHumanRace) {
|
|
android->ai->SetAiMode(kHumanAiMode);
|
|
} else if (race == kZombieRace) {
|
|
android->ai->SetAiMode(kZombieAiMode);
|
|
}
|
|
}
|
|
race_ = race;
|
|
level_ = level;
|
|
if (race_ == kHumanRace) {
|
|
meta = MetaMgr::Instance()->GetPlayer(HUMAN_RACE_META_START_ID + level_ - 1);
|
|
if (!meta) {
|
|
abort();
|
|
}
|
|
exp_ = meta->i->exp();
|
|
OnMetaChange();
|
|
} else if (race_ == kZombieRace) {
|
|
DoGetDown();
|
|
meta = MetaMgr::Instance()->GetPlayer(ZOMBIE_RACE_META_START_ID + level_ - 1);
|
|
if (!meta) {
|
|
abort();
|
|
}
|
|
exp_ = meta->i->exp();
|
|
OnMetaChange();
|
|
} else {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
void Human::ChangeToRaceAndNotify(RaceType_e race, int level)
|
|
{
|
|
ChangeToRace(race, level);
|
|
room->frame_event.AddZombieIdChg(this);
|
|
}
|
|
|
|
void Human::WinExp(Human* sender, int exp)
|
|
{
|
|
if (race_ != kHumanRace &&
|
|
race_ != kZombieRace) {
|
|
abort();
|
|
}
|
|
if (room->debug_trace) {
|
|
a8::UdpLog::Instance()->Debug
|
|
(
|
|
"WinExp before sender_id:%d uniid:%d race:%d level:%d exp:%d add_exp:%d ",
|
|
{
|
|
sender->GetEntityUniId(),
|
|
GetEntityUniId(),
|
|
GetRace(),
|
|
GetLevel(),
|
|
GetExp(),
|
|
exp
|
|
}
|
|
);
|
|
}
|
|
exp_ += exp;
|
|
MetaData::Player* old_meta = meta;
|
|
int start_meta_id = race_ == kHumanRace ? HUMAN_RACE_META_START_ID : ZOMBIE_RACE_META_START_ID;
|
|
do {
|
|
MetaData::Player* tmp_meta = MetaMgr::Instance()->GetPlayer(start_meta_id + level_);
|
|
if (!tmp_meta) {
|
|
break;
|
|
}
|
|
if (exp_ >= tmp_meta->i->exp()) {
|
|
meta = tmp_meta;
|
|
skill_meta_ = MetaMgr::Instance()->GetSkill(meta->i->active_skill());
|
|
ResetSkill();
|
|
++level_;
|
|
} else {
|
|
break;
|
|
}
|
|
} while (true);
|
|
if (old_meta != meta) {
|
|
room->frame_event.AddZombieIdChg(this);
|
|
OnMetaChange();
|
|
if (GetRace() == kZombieRace && meta->i->level() == 3) {
|
|
room->OnZombieAppear(this);
|
|
}
|
|
}
|
|
if (room->debug_trace) {
|
|
a8::UdpLog::Instance()->Debug
|
|
(
|
|
"WinExp after sender_id:%d uniid:%d race:%d level:%d exp:%d add_exp:%d ",
|
|
{
|
|
sender->GetEntityUniId(),
|
|
GetEntityUniId(),
|
|
GetRace(),
|
|
GetLevel(),
|
|
GetExp(),
|
|
exp
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
bool Human::IsEnemy(Human* hum)
|
|
{
|
|
if (room->GetRoomMode() == kZombieMode) {
|
|
return hum->GetRace() != GetRace();
|
|
} else {
|
|
return team_id != hum->team_id;
|
|
}
|
|
}
|
|
|
|
void Human::RecalTalentAttr()
|
|
{
|
|
|
|
}
|
|
|
|
void Human::RecalSkinAttr()
|
|
{
|
|
|
|
}
|
|
|
|
void Human::_InternalUpdateMove(float speed)
|
|
{
|
|
float nx = move_dir.x * speed;
|
|
float ny = move_dir.y * speed;
|
|
a8::Vec2 old_pos = GetPos();
|
|
|
|
#if 1
|
|
SetPos(old_pos + a8::Vec2(nx, ny));
|
|
if (!IsCollisionInMapService()) {
|
|
room->grid_service->MoveHuman(this);
|
|
return;
|
|
} else {
|
|
if (Global::last_collider && Global::last_collider->type == CT_Circle) {
|
|
SetPos(old_pos + a8::Vec2(nx, ny));
|
|
if (self_collider_->Intersect(Global::last_collider)) {
|
|
CircleCollider* circle_collider = (CircleCollider*)Global::last_collider;
|
|
a8::Vec2 tmp_dir = GetPos() - (circle_collider->owner->GetPos() + circle_collider->pos);
|
|
float len = circle_collider->rad + self_collider_->rad + 1;
|
|
float rate = len - tmp_dir.Norm();
|
|
tmp_dir.Normalize();
|
|
a8::Vec2 new_dir = tmp_dir * rate;
|
|
SetPos(GetPos() + new_dir);
|
|
}
|
|
if (!IsCollisionInMapService()) {
|
|
room->grid_service->MoveHuman(this);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SetPos(old_pos + a8::Vec2(nx, 0));
|
|
if (IsCollisionInMapService()) {
|
|
if (on_move_collision && !on_move_collision()) {
|
|
SetPos(old_pos);
|
|
return;
|
|
}
|
|
nx = 0;
|
|
}
|
|
|
|
SetPos(old_pos + a8::Vec2(nx, ny));
|
|
if (IsCollisionInMapService()) {
|
|
if (on_move_collision && !on_move_collision()) {
|
|
SetPos(old_pos);
|
|
return;
|
|
}
|
|
ny = 0;
|
|
}
|
|
|
|
SetPos(old_pos + a8::Vec2(nx, ny));
|
|
room->grid_service->MoveHuman(this);
|
|
}
|
|
|
|
void Human::ClearFrameData()
|
|
{
|
|
if (!new_objects.empty()) {
|
|
new_objects.clear();
|
|
}
|
|
if (!del_objects.empty()) {
|
|
if (!a8::HasBitFlag(status, HS_Disable)) {
|
|
for (auto& itr : del_objects) {
|
|
Entity* entity = room->GetEntityByUniId(itr);
|
|
if (entity) {
|
|
RemovePartObjects(entity);
|
|
if (entity->IsEntityType(ET_Player)) {
|
|
Human* hum = (Human*)entity;
|
|
hum->RemovePartObjects(this);
|
|
}
|
|
#ifdef DEBUG
|
|
room->CheckPartObjects();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
del_objects.clear();
|
|
}
|
|
if (!out_objects.empty()) {
|
|
if (!a8::HasBitFlag(status, HS_Disable)) {
|
|
for (auto& itr : out_objects) {
|
|
Entity* entity = room->GetEntityByUniId(itr);
|
|
if (entity) {
|
|
RemovePartObjects(entity);
|
|
if (entity->IsEntityType(ET_Player)) {
|
|
Human* hum = (Human*)entity;
|
|
hum->RemovePartObjects(this);
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
room->CheckPartObjects();
|
|
#endif
|
|
}
|
|
}
|
|
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_bullet_nums_.empty()) {
|
|
chged_bullet_nums_.clear();
|
|
}
|
|
if (!chged_hps_.empty()) {
|
|
chged_hps_.clear();
|
|
}
|
|
if (!chged_buffs_.empty()) {
|
|
chged_buffs_.clear();
|
|
}
|
|
if (!chged_skillcds_.empty()) {
|
|
chged_skillcds_.clear();
|
|
}
|
|
if (!chged_items_.empty()) {
|
|
chged_items_.clear();
|
|
}
|
|
if (!chged_weapon_ammo_.empty()) {
|
|
chged_weapon_ammo_.clear();
|
|
}
|
|
if (!chged_level_.empty()) {
|
|
chged_level_.clear();
|
|
}
|
|
if (!chged_exp_.empty()) {
|
|
chged_exp_.clear();
|
|
}
|
|
if (!chged_race_.empty()) {
|
|
chged_race_.clear();
|
|
}
|
|
if (!chged_zombieid_.empty()) {
|
|
chged_zombieid_.clear();
|
|
}
|
|
if (!dead_alive_objs_.empty()) {
|
|
dead_alive_objs_.clear();
|
|
}
|
|
}
|
|
|
|
void Human::GenBattleReportData(a8::MutableXObject* params)
|
|
{
|
|
params->SetVal("room_mode", room->GetRoomMode());
|
|
int rank = 0;
|
|
{
|
|
std::vector<Human*> human_list;
|
|
room->TouchHumanList(a8::XParams(),
|
|
[&human_list] (Human* hum, a8::XParams& param) -> bool
|
|
{
|
|
human_list.push_back(hum);
|
|
return true;
|
|
});
|
|
std::sort(human_list.begin(), human_list.end(),
|
|
[] (Human* a, Human* b )
|
|
{
|
|
if (a->real_dead && b->real_dead) {
|
|
if (a->dead_frameno == b->dead_frameno) {
|
|
return a->GetEntityUniId() < b->GetEntityUniId();
|
|
} else {
|
|
return a->dead_frameno == 0 ||
|
|
(b->dead_frameno != 0 && a->dead_frameno > b->dead_frameno);
|
|
}
|
|
} else {
|
|
if (a->real_dead) {
|
|
return false;
|
|
}
|
|
if (b->real_dead) {
|
|
return true;
|
|
}
|
|
return a->GetEntityUniId() < b->GetEntityUniId();
|
|
}
|
|
});
|
|
rank = human_list.size();
|
|
for (size_t i = 0; i < human_list.size(); ++i) {
|
|
if (human_list[i] == this) {
|
|
rank = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
if (room->GetAliveTeamNum() == 1) {
|
|
std::set<Human*>* alive_team = room->GetAliveTeam();
|
|
if (alive_team == team_members) {
|
|
rank = 1;
|
|
}
|
|
}
|
|
}
|
|
stats.rank = rank;
|
|
params->SetVal("account_id", account_id);
|
|
params->SetVal("map_id", room->GetMapMeta()->i->map_id());
|
|
params->SetVal("map_name", room->GetMapMeta()->i->map_name());
|
|
params->SetVal("map_tpl_name", room->GetMapTplName());
|
|
params->SetVal("game_time", time(nullptr));
|
|
params->SetVal("hurt", stats.damage_amount_in);
|
|
params->SetVal("rank", rank);
|
|
params->SetVal("kills", stats.kills);
|
|
params->SetVal("harm", stats.damage_amount_out);
|
|
params->SetVal("add_HP", stats.heal_amount);
|
|
if (!dead) {
|
|
params->SetVal("alive_time", room->GetFrameNo() * 1000.0f / SERVER_FRAME_RATE);
|
|
} else {
|
|
params->SetVal("alive_time", dead_frameno * 1000.0f / SERVER_FRAME_RATE);
|
|
}
|
|
params->SetVal("team_status", team_members && team_members->size() > 1 ? 1 : 0);
|
|
params->SetVal("room_uuid", room->GetRoomUuid());
|
|
|
|
int snipe_kill = 0;
|
|
int rifle_kill = 0;
|
|
int pistol_kill = 0;
|
|
int submachine_kill = 0;
|
|
for (Human* hum : kill_humans) {
|
|
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(hum->lethal_weapon);
|
|
if (equip_meta) {
|
|
switch (equip_meta->i->equip_subtype()) {
|
|
case 7:
|
|
{
|
|
//狙击枪
|
|
++snipe_kill;
|
|
}
|
|
break;
|
|
case 5:
|
|
{
|
|
//步枪
|
|
++rifle_kill;
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
//手枪
|
|
++pistol_kill;
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
//冲锋枪
|
|
++submachine_kill;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
params->SetVal("snipe_kill", snipe_kill);
|
|
params->SetVal("rifle_kill", rifle_kill);
|
|
params->SetVal("pistol_kill", pistol_kill);
|
|
params->SetVal("submachine_kill", submachine_kill);
|
|
|
|
params->SetVal("rescue_member", stats.rescue_member);
|
|
{
|
|
float rank_param = MetaMgr::Instance()->GetRankRewardParam(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);
|
|
}
|
|
{
|
|
std::string items_str;
|
|
MetaData::RankReward* rank_reward_meta = MetaMgr::Instance()->GetRankReward(rank);
|
|
#ifdef DEBUG1
|
|
{
|
|
#else
|
|
if (rank_reward_meta && rank_reward_meta->i->drop() > 0) {
|
|
#endif
|
|
#ifdef DEBUG1
|
|
{
|
|
#else
|
|
if (rand() % 100 < rank_reward_meta->i->drop()) {
|
|
#endif
|
|
MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquip(grow_weapon.weapon_id);
|
|
if (item_meta) {
|
|
MetaData::Drop* drop_meta = MetaMgr::Instance()->GetDrop(item_meta->i->drop_id());
|
|
if (drop_meta) {
|
|
std::vector<std::tuple<int, int, int>> drop_items;
|
|
drop_meta->RandItems(drop_items);
|
|
for (auto& item : drop_items) {
|
|
int item_id = std::get<0>(item);
|
|
int item_num = std::get<1>(item);
|
|
stats.items.push_back(std::make_pair(
|
|
item_id,
|
|
item_num
|
|
));
|
|
items_str += a8::Format("%d:%d|", {item_id, item_num});
|
|
#ifdef DEBUG
|
|
SendDebugMsg
|
|
(a8::Format("drop weapon_id:%d drop_id:%d item_id:%d item_num:%d",
|
|
{
|
|
grow_weapon.weapon_id,
|
|
item_meta->i->drop_id(),
|
|
item_id,
|
|
item_num
|
|
}));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}//end if rank_reward_meta
|
|
if (!items_str.empty() && items_str[items_str.size() - 1] == '|') {
|
|
items_str.erase(items_str.begin() + items_str.size() - 1);
|
|
}
|
|
params->SetVal("items", items_str);
|
|
}
|
|
{
|
|
stats.pass_score = MetaMgr::Instance()->GetKillPointParam1(stats.kills);
|
|
stats.pass_score += MetaMgr::Instance()->GetRankPointParam1(rank);
|
|
stats.rank_score = MetaMgr::Instance()->GetKillPointParam2(stats.kills);
|
|
if (room->IsMiniRoom()) {
|
|
stats.rank_score += MetaMgr::Instance()->GetRankPointParam3(rank);
|
|
} else {
|
|
stats.rank_score += MetaMgr::Instance()->GetRankPointParam2(rank);
|
|
}
|
|
}
|
|
params->SetVal("score", 0);
|
|
params->SetVal("pass_score", has_pass ? stats.pass_score * 2 : stats.pass_score);
|
|
params->SetVal("rank_score", stats.rank_score);
|
|
}
|
|
|
|
void Human::GenZbModeBattleReportData(a8::MutableXObject* params)
|
|
{
|
|
params->SetVal("room_mode", room->GetRoomMode());
|
|
int rank = 0;
|
|
{
|
|
std::vector<Human*> human_list;
|
|
room->TouchHumanList(a8::XParams(),
|
|
[&human_list] (Human* hum, a8::XParams& param) -> bool
|
|
{
|
|
human_list.push_back(hum);
|
|
return true;
|
|
});
|
|
std::sort(human_list.begin(), human_list.end(),
|
|
[] (Human* a, Human* b )
|
|
{
|
|
if (a->stats.kills > b->stats.kills) {
|
|
return true;
|
|
}
|
|
if (a->stats.kills < b->stats.kills) {
|
|
return false;
|
|
}
|
|
if (a->stats.last_kill_frameno < b->stats.last_kill_frameno) {
|
|
return true;
|
|
}
|
|
return a->GetEntityUniId() < b->GetEntityUniId();
|
|
});
|
|
rank = human_list.size();
|
|
for (size_t i = 0; i < human_list.size(); ++i) {
|
|
if (human_list[i] == this) {
|
|
rank = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
stats.rank = rank;
|
|
params->SetVal("account_id", account_id);
|
|
params->SetVal("map_id", room->GetMapMeta()->i->map_id());
|
|
params->SetVal("game_time", time(nullptr));
|
|
params->SetVal("rank", rank);
|
|
params->SetVal("kills", stats.kills);
|
|
params->SetVal("room_uuid", room->GetRoomUuid());
|
|
}
|
|
|
|
void Human::DeadDrop()
|
|
{
|
|
auto SkinCanDrop =
|
|
[this] (Skin* skin) -> bool
|
|
{
|
|
#ifdef DEBUG
|
|
return false;
|
|
#else
|
|
if (JsonDataMgr::Instance()->channel == kTouTiaoChannelId) {
|
|
return false;
|
|
} else {
|
|
return skin->skin_id != 0;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
if (GetRace() == kHumanRace && !HasBuffEffect(kBET_Terminator)) {
|
|
for (auto& weapon : weapons) {
|
|
if (weapon.weapon_id != 0 && weapon.weapon_id != default_weapon.weapon_id) {
|
|
a8::Vec2 drop_pos = GetPos();
|
|
room->DropItem(drop_pos, weapon.weapon_id, 1, weapon.weapon_lv);
|
|
}
|
|
}
|
|
{
|
|
curr_weapon = &weapons[0];
|
|
}
|
|
}
|
|
{
|
|
Skin* old_skin = GetSkinByIdx(kSkinSlot_HAT);
|
|
if (old_skin && old_skin->skin_id != 0 && SkinCanDrop(old_skin)) {
|
|
a8::Vec2 dir = a8::Vec2::UP;
|
|
dir.Rotate(a8::RandAngle());
|
|
room->CreateLoot(old_skin->skin_id, GetPos() + dir * (40 + rand() % 50), 1, 1);
|
|
*old_skin = Skin();
|
|
}
|
|
}
|
|
{
|
|
Skin* old_skin = GetSkinByIdx(kSkinSlot_CLOTH);
|
|
if (old_skin && old_skin->skin_id != 0 && SkinCanDrop(old_skin)) {
|
|
a8::Vec2 dir = a8::Vec2::UP;
|
|
dir.Rotate(a8::RandAngle());
|
|
room->CreateLoot(old_skin->skin_id, GetPos() + dir * (40 + rand() % 50), 1, 1);
|
|
*old_skin = Skin();
|
|
}
|
|
}
|
|
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->i->equip_type() == EQUIP_TYPE_BULLET) {
|
|
switch (equip_meta->i->_inventory_slot()) {
|
|
case IS_FRAG:
|
|
case IS_SMOKE:
|
|
{
|
|
a8::Vec2 drop_pos = GetPos();
|
|
room->DropItem(drop_pos, equip_meta->i->id(), inventory_[slot], 1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
a8::Vec2 drop_pos = GetPos();
|
|
room->DropItem(drop_pos, equip_meta->i->id(), inventory_[slot], 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::SendBattleReport()
|
|
{
|
|
a8::MutableXObject* params = a8::MutableXObject::NewObject();
|
|
if (room->GetRoomMode() == kZombieMode) {
|
|
GenZbModeBattleReportData(params);
|
|
} else {
|
|
GenBattleReportData(params);
|
|
}
|
|
auto on_ok = [] (a8::XParams& param, a8::XObject& data)
|
|
{
|
|
};
|
|
auto on_error = [] (a8::XParams& param, const std::string& response)
|
|
{
|
|
a8::UdpLog::Instance()->Error("battleReport http error params: %s response: %s",
|
|
{
|
|
param.param2,
|
|
response
|
|
});
|
|
};
|
|
std::string url;
|
|
if (!f8::IsOnlineEnv()) {
|
|
if (App::Instance()->HasFlag(3)) {
|
|
url = "http://192.168.100.41/webapp/index.php?c=Role&a=battleReport";
|
|
} else {
|
|
if (JsonDataMgr::Instance()->channel != 0) {
|
|
url = a8::Format("https://game2004api-test.kingsome.cn/%d/webapp/index.php?c=Role&a=battleReport",
|
|
{
|
|
JsonDataMgr::Instance()->channel
|
|
});
|
|
} else {
|
|
url = "https://game2004api-test.kingsome.cn/webapp/index.php?c=Role&a=battleReport";
|
|
}
|
|
}
|
|
} else {
|
|
if (JsonDataMgr::Instance()->channel != 0) {
|
|
if (kTouTiaoChannelId == JsonDataMgr::Instance()->channel) {
|
|
url = a8::Format("https://game2004api-al.kingsome.cn/%d/webapp/index.php?c=Role&a=battleReport",
|
|
{
|
|
JsonDataMgr::Instance()->channel
|
|
});
|
|
} else {
|
|
url = a8::Format("https://game2004api.kingsome.cn/%d/webapp/index.php?c=Role&a=battleReport",
|
|
{
|
|
JsonDataMgr::Instance()->channel
|
|
});
|
|
}
|
|
} else {
|
|
url = "https://game2004api.kingsome.cn/webapp/index.php?c=Role&a=battleReport";
|
|
}
|
|
}
|
|
JsonDataMgr::Instance()->GetBattleReportUrl(url);
|
|
std::string data;
|
|
params->ToUrlEncodeStr(data);
|
|
f8::HttpClientPool::Instance()->HttpGet(
|
|
a8::XParams()
|
|
.SetSender(room->GetRoomUuid())
|
|
.SetParam1(GetEntityUniId())
|
|
.SetParam2(data),
|
|
on_ok,
|
|
on_error,
|
|
url.c_str(),
|
|
*params,
|
|
MAX_SYS_HTTP_NUM + (GetEntityUniId() % MAX_USER_HTTP_NUM)
|
|
);
|
|
delete params;
|
|
}
|
|
|
|
void Human::ProcLootSkin(Loot* entity, MetaData::Equip* item_meta)
|
|
{
|
|
switch (item_meta->i->equip_subtype()) {
|
|
case 11:
|
|
{
|
|
//装饰
|
|
Skin* old_skin = GetSkinByIdx(kSkinSlot_HAT);
|
|
if (old_skin) {
|
|
if (old_skin->skin_id != 0) {
|
|
a8::Vec2 dir = a8::Vec2::UP;
|
|
dir.Rotate(a8::RandAngle());
|
|
room->CreateLoot(old_skin->skin_id, GetPos() + dir * (40 + rand() % 50), 1, 1);
|
|
}
|
|
|
|
*old_skin = Skin();
|
|
old_skin->skin_id = item_meta->i->id();
|
|
old_skin->skin_lv = 1;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
break;
|
|
case 12:
|
|
{
|
|
//衣服
|
|
Skin* old_skin = GetSkinByIdx(kSkinSlot_CLOTH);
|
|
if (old_skin) {
|
|
if (old_skin->skin_id != 0) {
|
|
a8::Vec2 dir = a8::Vec2::UP;
|
|
dir.Rotate(a8::RandAngle());
|
|
room->CreateLoot(old_skin->skin_id, GetPos() + dir * (40 + rand() % 50), 1, 1);
|
|
}
|
|
|
|
*old_skin = Skin();
|
|
old_skin->skin_id = item_meta->i->id();
|
|
old_skin->skin_lv = 1;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Human::ProcLootCar(Loot* entity, MetaData::Equip* item_meta)
|
|
{
|
|
if (room->GetGasData().gas_mode == GasInactive) {
|
|
return;
|
|
}
|
|
if (GetPos().Distance(entity->GetPos()) > MetaMgr::Instance()->max_mount_horse_distance) {
|
|
return;
|
|
}
|
|
if (downed) {
|
|
return;
|
|
}
|
|
if (car_.car_id != 0) {
|
|
int loot_uniid = room->CreateLoot(car_.car_id, GetPos(), 1, 1);
|
|
Entity* loot_entity = room->GetEntityByUniId(loot_uniid);
|
|
if (loot_entity && loot_entity->IsEntityType(ET_Loot)) {
|
|
((Loot*)loot_entity)->bullet_num = 0;
|
|
((Loot*)loot_entity)->param1 = 0;
|
|
((Loot*)loot_entity)->param2 = 0;
|
|
room->UpdateCarObject(car_.car_uniid,
|
|
loot_entity->GetEntityUniId(),
|
|
loot_entity->GetPos());
|
|
}
|
|
room->TakeOffCarObject(loot_uniid, GetPos());
|
|
if (car_.meta->i->buffid()) {
|
|
RemoveBuffById(car_.meta->i->buffid());
|
|
}
|
|
car_weapon = Weapon();
|
|
}
|
|
car_.car_uniid = entity->GetEntityUniId();
|
|
car_.car_id = item_meta->i->id();
|
|
car_.meta = item_meta;
|
|
SetPos(entity->GetPos());
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(car_.meta->i->buffid());
|
|
if (buff_meta) {
|
|
AddBuff(this, buff_meta, 1);
|
|
}
|
|
CancelAction();
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
room->TakeOnCarObject(car_.car_uniid);
|
|
room->NotifyUiUpdate();
|
|
}
|
|
|
|
void Human::ProcCamoutflage(Loot* entity, MetaData::Equip* item_meta)
|
|
{
|
|
AddItem(item_meta->i->id(), 1);
|
|
}
|
|
|
|
void Human::ProcSpoils(Loot* entity, MetaData::Equip* item_meta)
|
|
{
|
|
if (spoils_items.find(item_meta->i->id()) !=
|
|
spoils_items.end()) {
|
|
spoils_items[item_meta->i->id()] += entity->count;
|
|
} else {
|
|
spoils_items[item_meta->i->id()] = entity->count;
|
|
}
|
|
}
|
|
|
|
void Human::FindLocationWithTarget(Entity* target)
|
|
{
|
|
a8::Vec2 old_pos = GetPos();
|
|
a8::Vec2 new_pos = GetPos();
|
|
AabbCollider a_collider;
|
|
GetAabbBox(a_collider);
|
|
AabbCollider target_collider;
|
|
target->GetAabbBox(target_collider);
|
|
{
|
|
bool ret = a_collider.CalcSafePoint(&target_collider, new_pos);
|
|
if (!ret) {
|
|
abort();
|
|
}
|
|
}
|
|
a8::Vec2 new_pos_dir = new_pos - old_pos;
|
|
new_pos_dir.Normalize();
|
|
float distance = (new_pos - old_pos).Norm();
|
|
for (int i = distance; i < 10000000; i += 5) {
|
|
SetPos(old_pos + new_pos_dir * i);
|
|
room->grid_service->MoveHuman(this);
|
|
|
|
Entity* building = nullptr;
|
|
std::set<GridCell*> tmp_grids;
|
|
room->grid_service->GetAllCellsByXy(room, GetX(), GetY(), tmp_grids);
|
|
room->grid_service->TouchAllLayerEntityList
|
|
(
|
|
room->GetRoomIdx(),
|
|
tmp_grids,
|
|
[this, &building] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
{
|
|
if (TestCollision(room, entity)) {
|
|
building = entity;
|
|
stop = true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
if (!building) {
|
|
bool is_collision = false;
|
|
std::set<ColliderComponent*> colliders;
|
|
room->map_service->GetColliders(room, GetX(), GetY(), colliders);
|
|
for (ColliderComponent* collider : colliders) {
|
|
if (TestCollision(room, collider)) {
|
|
is_collision = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!is_collision) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::OnDie()
|
|
{
|
|
real_dead_frameno = room->GetFrameNo();
|
|
room->OnHumanDie(this);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
if (team_members) {
|
|
for (auto& hum : *team_members) {
|
|
if (hum != this && hum->action_type == AT_Relive &&
|
|
hum->action_target_id == GetEntityUniId()) {
|
|
hum->CancelAction();
|
|
}
|
|
}
|
|
}
|
|
{
|
|
std::set<Human*> over_humans;
|
|
if (!leave_) {
|
|
if (!HasNoDownedTeammate()) {
|
|
if (team_members) {
|
|
for (auto& member : *team_members) {
|
|
if (member->real_dead) {
|
|
over_humans.insert(member);
|
|
}
|
|
}
|
|
} else {
|
|
over_humans.insert(this);
|
|
}
|
|
} else {
|
|
over_humans.insert(this);
|
|
}
|
|
}
|
|
if (room->GetAliveTeamNum() == 1) {
|
|
std::set<Human*>* alive_team = room->GetAliveTeam();
|
|
if (alive_team) {
|
|
for (Human* member : *alive_team) {
|
|
over_humans.insert(member);
|
|
}
|
|
}
|
|
}
|
|
for (Human* hum : over_humans) {
|
|
hum->SendGameOver();
|
|
}
|
|
}
|
|
DeadDrop();
|
|
}
|
|
|
|
void Human::FreeDownedTimer()
|
|
{
|
|
if (downed_timer) {
|
|
room->xtimer.DeleteTimer(downed_timer);
|
|
downed_timer = nullptr;
|
|
}
|
|
}
|
|
|
|
void Human::FreeReviveTimer()
|
|
{
|
|
if (revive_timer) {
|
|
room->xtimer.DeleteTimer(revive_timer);
|
|
revive_timer = nullptr;
|
|
}
|
|
}
|
|
|
|
void Human::RandSkin()
|
|
{
|
|
std::vector<int> ids = {1, 2, 3, 4, 5, 6};
|
|
for (int i = 0; i < kSkinNum; ++i) {
|
|
int rand_idx = rand() % ids.size();
|
|
int skin_id = ids[rand_idx];
|
|
if (i == 1) {
|
|
skin_id = 13001 + (rand() % 6);
|
|
} else if (i == 2) {
|
|
skin_id = 15001 + (rand() % 6);
|
|
}
|
|
Skin& skin = skins[i];
|
|
skin.skin_id = skin_id;
|
|
skin.skin_lv = 1;
|
|
ids.erase(ids.begin() + rand_idx);
|
|
}
|
|
}
|
|
|
|
void Human::SetSkin(int idx, int skin_id)
|
|
{
|
|
if (idx < kSkinNum) {
|
|
Skin& skin = skins[idx];
|
|
skin.skin_id = skin_id;
|
|
skin.skin_lv = 1;
|
|
}
|
|
}
|
|
|
|
Skin* Human::GetSkinByIdx(int idx)
|
|
{
|
|
int i = 0;
|
|
for (auto& skin : skins) {
|
|
if (i == idx) {
|
|
return &skin;
|
|
}
|
|
++i;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
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->GetFrameNo() - last_use_skill_frameno_) * FRAME_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 (GetLevel() <= 1) {
|
|
return skill_meta_->i->skill_cd() * 1000;
|
|
} else {
|
|
return skill_meta_->i->skill_cd() * 1000 - (GetLevel() - 1) * skill_meta_->i->cold_time_up();
|
|
}
|
|
}
|
|
|
|
int Human::GetSkillLv()
|
|
{
|
|
return GetLevel();
|
|
}
|
|
|
|
float Human::GetSkillAtkAdd(int skill_id)
|
|
{
|
|
if (!skill_meta_) {
|
|
return 0.0f;
|
|
}
|
|
if (skill_meta_->i->skill_id() != skill_id) {
|
|
return 0.0f;
|
|
}
|
|
if (GetSkillLv() > 0) {
|
|
return skill_meta_->value_up * GetSkillLv();
|
|
} else {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
void Human::TriggerOneObjectBuff(Entity* target, BuffTriggerType_e trigger_type)
|
|
{
|
|
if (!target->IsEntityType(ET_Player)) {
|
|
return;
|
|
}
|
|
Human* hum = (Human*)target;
|
|
if (hum->dead) {
|
|
return;
|
|
}
|
|
auto itr = skill_meta_->trigger_type_buffs.find(trigger_type);
|
|
if (itr != skill_meta_->trigger_type_buffs.end()) {
|
|
for (MetaData::Buff* buff_meta : itr->second) {
|
|
switch (buff_meta->i->buff_target()) {
|
|
case kBuffTargetSelf: //自己
|
|
{
|
|
if (hum == this) {
|
|
hum->AddBuff(this, buff_meta, GetSkillLv(), skill_meta_);
|
|
}
|
|
}
|
|
break;
|
|
case kBuffTargetFriendly: //友军
|
|
{
|
|
if (hum->team_id == team_id) {
|
|
hum->AddBuff(this, buff_meta, GetSkillLv(), skill_meta_);
|
|
}
|
|
}
|
|
break;
|
|
case kBuffTargetEnemy: //敌军
|
|
{
|
|
if (hum->team_id != team_id) {
|
|
hum->AddBuff(this, buff_meta, GetSkillLv(), skill_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(Human* caster,
|
|
MetaData::Buff* buff_meta,
|
|
int skill_lv,
|
|
MetaData::Skill* buff_skill_meta)
|
|
{
|
|
if (GetBuffById(buff_meta->i->buff_id())) {
|
|
return;
|
|
}
|
|
if (IsImmuneBuffEffect(buff_meta->i->buff_effect())) {
|
|
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(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
Buff* buff = &a8::FastAppend(buff_list_);
|
|
buff->skill_lv = skill_lv;
|
|
buff->owner = this;
|
|
buff->meta = buff_meta;
|
|
buff->skill_meta = buff_skill_meta;
|
|
buff->add_frameno = room->GetFrameNo();
|
|
buff->xtimer_attacher.xtimer = &room->xtimer;
|
|
buff_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(caster, buff);
|
|
}
|
|
|
|
bool Human::IsImmuneBuffEffect(int buff_effect)
|
|
{
|
|
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
|
|
if (itr->meta->IsImmuneBuffEffect(buff_effect)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
#if 0
|
|
switch (itr->meta->i->buff_effect()) {
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
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::ProcBuffEffect(Human* caster, Buff* buff)
|
|
{
|
|
switch (buff->meta->i->buff_effect()) {
|
|
case kBET_ChgAttr:
|
|
case kBET_Car:
|
|
{
|
|
RecalcBuffAttr();
|
|
if (buff->meta->i->buff_effect() == kBET_Car) {
|
|
RecalcSelfCollider();
|
|
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(buff->meta->param4);
|
|
if (equip_meta &&
|
|
equip_meta->i->equip_type() == EQUIP_TYPE_CAR &&
|
|
equip_meta->i->equip_subtype() == 1
|
|
) {
|
|
MetaData::Equip* car_weapon_meta = MetaMgr::Instance()->GetEquip(equip_meta->int_param1);
|
|
if (car_weapon_meta) {
|
|
car_weapon.weapon_idx = 100;
|
|
car_weapon.weapon_id = car_weapon_meta->i->id();
|
|
car_weapon.weapon_lv = 1;
|
|
car_weapon.meta = car_weapon_meta;
|
|
car_weapon.Recalc();
|
|
car_weapon.ammo = car_weapon.GetClipVolume();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kBET_TurnOver:
|
|
{
|
|
if (curr_skill_phase < skill_meta_->phases.size()) {
|
|
MetaData::SkillPhase* phase = &skill_meta_->phases[curr_skill_phase];
|
|
if (phase->time_offset >= (int)((room->GetFrameNo() - last_use_skill_frameno_) * FRAME_RATE_MS)) {
|
|
if (HasBuffEffect(kBET_Car)) {
|
|
_UpdateMove(phase->param1.GetDouble() * 1.5);
|
|
} else {
|
|
_UpdateMove(phase->param1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kBET_Camouflage:
|
|
{
|
|
if (aiming) {
|
|
aiming = false;
|
|
}
|
|
if (action_type != AT_None) {
|
|
CancelAction();
|
|
}
|
|
}
|
|
break;
|
|
case kBET_BePull:
|
|
{
|
|
if (caster == this) {
|
|
abort();
|
|
}
|
|
float target_distance = caster->GetPos().Distance(GetPos());
|
|
if (target_distance <= 0.000001f) {
|
|
SetPos(caster->GetPos());
|
|
target_pos = caster->GetPos();
|
|
} else {
|
|
if (target_distance <= buff->meta->param3) {
|
|
SetPos(caster->GetPos());
|
|
target_pos = caster->GetPos();
|
|
} else {
|
|
move_dir = caster->GetPos() - GetPos();
|
|
move_dir.Normalize();
|
|
target_pos = GetPos() + move_dir * (target_distance - buff->meta->param3);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kBET_JumpTo:
|
|
{
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id);
|
|
if (entity) {
|
|
float target_distance = entity->GetPos().Distance(GetPos());
|
|
if (target_distance <= 0.000001f) {
|
|
SetPos(entity->GetPos());
|
|
skill_target_pos = entity->GetPos();
|
|
} else {
|
|
if (target_distance <= buff->meta->param3) {
|
|
SetPos(entity->GetPos());
|
|
skill_target_pos = entity->GetPos();
|
|
} else {
|
|
move_dir = entity->GetPos() - GetPos();
|
|
move_dir.Normalize();
|
|
skill_dir = move_dir;
|
|
skill_target_pos = GetPos() + move_dir * (target_distance - buff->meta->param3);
|
|
}
|
|
}
|
|
}
|
|
target_pos = skill_target_pos;
|
|
}
|
|
break;
|
|
case kBET_Pull:
|
|
{
|
|
int i = 0;
|
|
}
|
|
break;
|
|
case kBET_Terminator:
|
|
{
|
|
if (GetRace() == kHumanRace &&
|
|
MetaMgr::Instance()->terminator_meta &&
|
|
meta != MetaMgr::Instance()->terminator_meta) {
|
|
WinExp(this, MetaMgr::Instance()->terminator_meta->i->exp() + 1);
|
|
room->NotifySysPiao("终结者出现", a8::MkRgb(255, 0, 0), 3);
|
|
OnChgToTerminator();
|
|
}
|
|
}
|
|
break;
|
|
case kBET_PlayShotAni:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case kBET_Vertigo:
|
|
{
|
|
int i = 0;
|
|
}
|
|
break;
|
|
case kBET_DecHp:
|
|
{
|
|
#if 0
|
|
float def = hum->ability.def * (1 + hum->GetBuffAttrRate(kHAT_Def)) +
|
|
hum->GetBuffAttrAbs(kHAT_Def);
|
|
#endif
|
|
DecHP(buff->meta->param1,
|
|
caster->GetEntityUniId(),
|
|
caster->name,
|
|
0);
|
|
}
|
|
break;
|
|
case kBET_DelayAddBuff:
|
|
{
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
SERVER_FRAME_RATE * buff->meta->param1,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(caster)
|
|
.SetParam2(buff->meta->param2)
|
|
.SetParam3(caster->skill_meta_),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
Human* caster = (Human*)param.param1.GetUserData();
|
|
int buff_id = param.param2;
|
|
MetaData::Skill* skill = (MetaData::Skill*)param.param3.GetUserData();
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
|
|
if (buff_meta && skill == caster->skill_meta_ && caster) {
|
|
hum->AddBuff(caster, buff_meta, caster->GetSkillLv(), skill);
|
|
}
|
|
},
|
|
&buff->xtimer_attacher.timer_list_);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int Human::GetItemNum(int item_id)
|
|
{
|
|
auto itr = items_.find(item_id);
|
|
return itr != items_.end() ? itr->second : 0;
|
|
}
|
|
|
|
void Human::AddItem(int item_id, int item_num)
|
|
{
|
|
auto itr = items_.find(item_id);
|
|
if (itr != items_.end()) {
|
|
itr->second += item_num;
|
|
} else {
|
|
items_[item_id] = item_num;
|
|
}
|
|
|
|
if (room && room->GetFrameNo() > join_frameno) {
|
|
room->frame_event.AddItemChg(this, item_id, items_[item_id]);
|
|
} else {
|
|
if (item_num <= 0) {
|
|
battling_items_.insert(item_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::DecItem(int item_id, int item_num)
|
|
{
|
|
auto itr = items_.find(item_id);
|
|
if (itr != items_.end()) {
|
|
itr->second -= item_num;
|
|
room->frame_event.AddItemChg(this, item_id, std::max(0, itr->second));
|
|
if (itr->second <= 0) {
|
|
if (battling_items_.find(item_id) == battling_items_.end()) {
|
|
items_.erase(itr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
float Human::GetBuffAttrAbs(int attr_type)
|
|
{
|
|
if (IsValidHumanAttr(attr_type)) {
|
|
return buff_attr_abs_[attr_type];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
float Human::GetBuffAttrRate(int attr_type)
|
|
{
|
|
if (IsValidHumanAttr(attr_type)) {
|
|
return buff_attr_rate_[attr_type];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
float Human::GetAttrAbs(int attr_id)
|
|
{
|
|
float attr_abs_val = GetBuffAttrAbs(attr_id);
|
|
if (attr_id == kHAT_Atk || attr_id == kHAT_Def) {
|
|
Buff* buff = GetBuffByEffectId(kBET_Car);
|
|
if (buff) {
|
|
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(buff->meta->param4);
|
|
if (equip_meta) {
|
|
switch (attr_id) {
|
|
case kHAT_Atk:
|
|
{
|
|
attr_abs_val += equip_meta->i->atk();
|
|
}
|
|
break;
|
|
case kHAT_Def:
|
|
{
|
|
attr_abs_val += equip_meta->i->def();
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return attr_abs_val;
|
|
}
|
|
|
|
float Human::GetAttrRate(int attr_id)
|
|
{
|
|
float attr_rate_val = GetBuffAttrRate(attr_id);
|
|
return attr_rate_val;
|
|
}
|
|
|
|
bool Human::IsPlayer() const
|
|
{
|
|
return IsEntitySubType(EST_Player);
|
|
}
|
|
|
|
bool Human::IsAndroid() const
|
|
{
|
|
return IsEntitySubType(EST_Android);
|
|
}
|
|
|
|
void Human::DropItems(Obstacle* obstacle)
|
|
{
|
|
bool is_treasure_box = false;
|
|
if (obstacle->IsEntitySubType(EST_RoomObstacle)) {
|
|
is_treasure_box = ((RoomObstacle*)obstacle)->is_treasure_box;
|
|
}
|
|
int drop_id = obstacle->meta->i->drop();
|
|
if (drop_id == 0) {
|
|
return;
|
|
}
|
|
if (room->GetRoomType() == RT_NewBrid && IsPlayer()) {
|
|
if (is_treasure_box) {
|
|
if (box_drop_times_ < MetaMgr::Instance()->newbie_airdrop.size()) {
|
|
drop_id = MetaMgr::Instance()->newbie_airdrop[box_drop_times_];
|
|
}
|
|
} else {
|
|
if (normal_drop_times_ < MetaMgr::Instance()->newbie_drop.size()) {
|
|
if (normal_drop_times_ == 0) {
|
|
}
|
|
drop_id = MetaMgr::Instance()->newbie_drop[normal_drop_times_];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (drop_id == 0) {
|
|
drop_id = obstacle->meta->i->drop();
|
|
}
|
|
room->ScatterDrop(obstacle->GetPos(), drop_id);
|
|
if (is_treasure_box) {
|
|
++box_drop_times_;
|
|
} else {
|
|
++normal_drop_times_;
|
|
}
|
|
#ifdef DEBUG
|
|
#if 0
|
|
a8::UdpLog::Instance()->Debug("DropItems normal:%d box:%d drop_id:%d is_treasure_box:%d",
|
|
{
|
|
normal_drop_times_,
|
|
box_drop_times_,
|
|
drop_id,
|
|
is_treasure_box ? 1 : 0
|
|
});
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void Human::RecalcBuffAttr()
|
|
{
|
|
buff_attr_abs_ = {};
|
|
buff_attr_rate_ = {};
|
|
for (auto& buff : buff_list_) {
|
|
if (buff.meta->i->buff_effect() == kBET_ChgAttr ||
|
|
buff.meta->i->buff_effect() == kBET_Car) {
|
|
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::Revive()
|
|
{
|
|
if (room->GetRoomMode() == kZombieMode) {
|
|
if (GetRace() == kHumanRace) {
|
|
ChangeToRaceAndNotify(kZombieRace, 1);
|
|
}
|
|
dead = false;
|
|
downed = false;
|
|
real_dead = false;
|
|
ability.hp = GetMaxHP();
|
|
ClearBuffList();
|
|
{
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(kREVIVE_BUFF_ID);
|
|
if (buff_meta) {
|
|
AddBuff(this, buff_meta, 1);
|
|
}
|
|
}
|
|
room->frame_event.AddRevive(this);
|
|
room->OnHumanRevive(this);
|
|
|
|
ResetSkill();
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
} else {
|
|
auto callback =
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
hum->dead = true;
|
|
hum->real_dead = true;
|
|
hum->downed = false;
|
|
hum->OnDie();
|
|
};
|
|
int wait_revive_time = MetaMgr::Instance()->GetSysParamAsInt("revive_time", 25) + kReviveTimeAdd;
|
|
revive_timer = room->xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * wait_revive_time,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
callback,
|
|
&xtimer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
hum->revive_timer = nullptr;
|
|
});
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
|
|
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::SelectSkillTargets(const a8::Vec2& target_pos, std::set<Entity*>& target_list)
|
|
{
|
|
switch (skill_meta_->i->skill_target()) {
|
|
case kST_All:
|
|
{
|
|
TouchAllLayerHumanList
|
|
(
|
|
[this, &target_pos, &target_list] (Human* hum, bool& stop)
|
|
{
|
|
if (hum->GetPos().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);
|
|
TouchAllLayerHumanList
|
|
(
|
|
[this, &target_pos, &target_list] (Human* hum, bool& stop)
|
|
{
|
|
if ((hum == this || hum->team_id == team_id) &&
|
|
hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) {
|
|
target_list.insert(hum);
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case kST_FriendlyExcludeSelf:
|
|
{
|
|
TouchAllLayerHumanList
|
|
(
|
|
[this, &target_pos, &target_list] (Human* hum, bool& stop)
|
|
{
|
|
if ((hum != this && hum->team_id == team_id) &&
|
|
hum->GetPos().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->IsEntityType(ET_Player)) {
|
|
Human* hum = (Human*)entity;
|
|
if (IsEnemy(hum)) {
|
|
target_list.insert(hum);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kST_EnemyGroup:
|
|
{
|
|
TouchAllLayerHumanList
|
|
(
|
|
[this, &target_pos, &target_list] (Human* hum, bool& stop)
|
|
{
|
|
if ((hum->team_id != team_id) &&
|
|
hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) {
|
|
target_list.insert(hum);
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case kST_EnemyAndObject:
|
|
{
|
|
TouchAllLayerHumanList
|
|
(
|
|
[this, &target_pos, &target_list] (Human* hum, bool& stop)
|
|
{
|
|
if ((hum->team_id != team_id) &&
|
|
hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) {
|
|
target_list.insert(hum);
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case kST_EnemyAndSelf:
|
|
{
|
|
TouchAllLayerHumanList
|
|
(
|
|
[this, &target_pos, &target_list] (Human* hum, bool& stop)
|
|
{
|
|
if ((hum == this || this->IsEnemy(hum)) &&
|
|
hum->GetPos().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->IsEntityType(ET_Player)) {
|
|
Human* hum = (Human*)entity;
|
|
if (IsEnemy(hum)) {
|
|
target_list.insert(hum);
|
|
}
|
|
}
|
|
target_list.insert(this);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Human::ProcSkillPhase(MetaData::SkillPhase* phase)
|
|
{
|
|
switch (phase->func_id) {
|
|
case kSkill_TurnOver:
|
|
{
|
|
}
|
|
break;
|
|
case kSkill_JumpTo:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case kSkill_Shot:
|
|
{
|
|
Entity* entity = room->GetEntityByUniId(skill_target_id);
|
|
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(phase->param1.GetInt());
|
|
if (weapon_meta && entity) {
|
|
float target_distance = entity->GetPos().Distance(GetPos());
|
|
MetaData::EquipUpgrade* weapon_upgrade_meta =
|
|
MetaMgr::Instance()->GetEquipUpgrade(weapon_meta->i->id());
|
|
MetaData::Equip* bullet_meta =
|
|
MetaMgr::Instance()->GetEquip(weapon_meta->i->use_bullet());
|
|
if (bullet_meta && target_distance > 0.00001f) {
|
|
a8::Vec2 old_attack_dir = attack_dir;
|
|
attack_dir = entity->GetPos() - GetPos();
|
|
attack_dir.Normalize();
|
|
InternalShot
|
|
(
|
|
this,
|
|
weapon_meta,
|
|
weapon_upgrade_meta,
|
|
bullet_meta,
|
|
1,
|
|
skill_meta_->i->skill_id(),
|
|
target_distance,
|
|
false);
|
|
attack_dir = old_attack_dir;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kSkill_Pull:
|
|
{
|
|
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Human::AutoChgWeapon()
|
|
{
|
|
if (weapons[GUN_SLOT1].weapon_id != 0) {
|
|
curr_weapon = &weapons[GUN_SLOT1];
|
|
} else if (weapons[GUN_SLOT2].weapon_id != 0) {
|
|
curr_weapon = &weapons[GUN_SLOT2];
|
|
} else {
|
|
curr_weapon = &weapons[0];
|
|
}
|
|
}
|
|
|
|
void Human::CancelRevive()
|
|
{
|
|
if (dead && revive_timer && !real_dead) {
|
|
dead = true;
|
|
real_dead = true;
|
|
downed = false;
|
|
FreeDownedTimer();
|
|
OnDie();
|
|
FreeReviveTimer();
|
|
}
|
|
}
|
|
|
|
void Human::ProcNewBieLogic()
|
|
{
|
|
if (a8::HasBitFlag(status, HS_AlreadyProcNewBieLogic)) {
|
|
return;
|
|
}
|
|
a8::SetBitFlag(status, HS_AlreadyProcNewBieLogic);
|
|
//刷一个机器人
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(SERVER_FRAME_RATE * MetaMgr::Instance()->newbie_first_robot_appeartime,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
hum->room->ShuaGuideAndroid(hum);
|
|
},
|
|
&xtimer_attacher.timer_list_
|
|
);
|
|
//6秒后出现空投
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(SERVER_FRAME_RATE * MetaMgr::Instance()->newbie_airdrop_appeartime,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
hum->room->InitAirDrop();
|
|
},
|
|
&xtimer_attacher.timer_list_
|
|
);
|
|
}
|
|
|
|
void Human::ClearLordMode()
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_LordMode);
|
|
if (buff) {
|
|
std::vector<std::string> strings;
|
|
a8::Split(buff->meta->i->buff_param1(), strings, ':');
|
|
for (auto& str : strings) {
|
|
int buff_id = a8::XValue(str);
|
|
RemoveBuffById(buff_id);
|
|
}
|
|
RemoveBuffByEffectId(kBET_LordMode);
|
|
}
|
|
}
|
|
|
|
void Human::AdjustDecHp(float old_health, float& new_health)
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_NewProtect);
|
|
if (buff) {
|
|
if (new_health < GetMaxHP() * buff->meta->param1) {
|
|
new_health = std::max(GetMaxHP() * buff->meta->param1, (float)0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::OnEnable()
|
|
{
|
|
a8::UnSetBitFlag(status, HS_Disable);
|
|
enable_frameno = room->GetFrameNo();
|
|
room->grid_service->AddHuman(this);
|
|
FindLocation();
|
|
RefreshView();
|
|
}
|
|
|
|
void Human::OnDisable()
|
|
{
|
|
a8::SetBitFlag(status, HS_Disable);
|
|
RemoveFromScene();
|
|
ClearFrameData();
|
|
ClearPartObjects();
|
|
}
|
|
|
|
void Human::GetViewObjects(std::set<Entity*>& view_objects)
|
|
{
|
|
TouchAllLayerHumanList
|
|
(
|
|
[&view_objects] (Human* hum, bool& stop)
|
|
{
|
|
view_objects.insert(hum);
|
|
});
|
|
TouchAllLayerEntityList
|
|
(
|
|
[&view_objects] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
case ET_Obstacle:
|
|
case ET_Loot:
|
|
{
|
|
view_objects.insert(entity);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
void Human::NotifyObservers(cs::SMUpdate* msg, cs::MFActivePlayerData* active_player_data_pb)
|
|
{
|
|
bool refreshed_view = false;
|
|
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(), false);
|
|
}
|
|
}
|
|
}
|
|
if (observer != this && !observer->follow_synced_active_player) {
|
|
msg->set_active_player_id(GetEntityUniId());
|
|
FillMFActivePlayerData(msg->mutable_active_player_data());
|
|
if (!refreshed_view) {
|
|
std::set<Entity*> view_objects;
|
|
GetViewObjects(view_objects);
|
|
for (Entity* entity : view_objects) {
|
|
if (new_objects.find(entity) == new_objects.end()) {
|
|
entity->FillMFObjectFull(room, this, msg->add_full_objects());
|
|
}
|
|
}
|
|
refreshed_view = true;
|
|
}
|
|
observer->follow_synced_active_player = true;
|
|
} else {
|
|
if (active_player_data_pb) {
|
|
msg->set_active_player_id(GetEntityUniId());
|
|
*msg->mutable_active_player_data() = *active_player_data_pb;
|
|
} else {
|
|
msg->clear_active_player_id();
|
|
msg->clear_active_player_data();
|
|
}
|
|
}
|
|
observer->SendNotifyMsg(*msg);
|
|
}
|
|
}
|
|
|
|
void Human::ProcIncGridList(std::set<GridCell*>& old_grids,
|
|
std::set<GridCell*>& inc_grids,
|
|
std::set<GridCell*>& dec_grids)
|
|
{
|
|
room->grid_service->TouchAllLayerHumanList
|
|
(
|
|
room->GetRoomIdx(),
|
|
inc_grids,
|
|
[this, &old_grids] (Human* hum, bool& stop)
|
|
{
|
|
if (!room->grid_service->HumanInGridList(hum, old_grids)) {
|
|
hum->AddToNewObjects(this);
|
|
hum->AddToPartObjects(this);
|
|
hum->RemoveOutObjects(this);
|
|
AddToNewObjects(hum);
|
|
AddToPartObjects(hum);
|
|
RemoveOutObjects(hum);
|
|
}
|
|
});
|
|
room->grid_service->TouchAllLayerEntityList
|
|
(
|
|
room->GetRoomIdx(),
|
|
inc_grids,
|
|
[this] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
case ET_Obstacle:
|
|
case ET_Loot:
|
|
{
|
|
AddToNewObjects(entity);
|
|
RemoveOutObjects(entity);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
void Human::ProcDecGridList(std::set<GridCell*>& old_grids,
|
|
std::set<GridCell*>& inc_grids,
|
|
std::set<GridCell*>& dec_grids)
|
|
{
|
|
room->grid_service->TouchAllLayerHumanList
|
|
(
|
|
room->GetRoomIdx(),
|
|
dec_grids,
|
|
[this] (Human* hum, bool& stop)
|
|
{
|
|
if (!room->grid_service->HumanInGridList(hum, GetGridList())) {
|
|
AddOutObjects(hum);
|
|
hum->AddOutObjects(this);
|
|
#ifdef DEBUG
|
|
#if 0
|
|
a8::UdpLog::Instance()->Debug("addoutobjects %d %d",
|
|
{
|
|
(long long)hum,
|
|
hum->GetEntityUniId()
|
|
});
|
|
#endif
|
|
#endif
|
|
}
|
|
});
|
|
room->grid_service->TouchAllLayerEntityList
|
|
(
|
|
room->GetRoomIdx(),
|
|
dec_grids,
|
|
[this] (Entity* entity, bool& stop)
|
|
{
|
|
if (!room->grid_service->EntityInGridList(room, entity, GetGridList())) {
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
case ET_Obstacle:
|
|
case ET_Loot:
|
|
{
|
|
AddOutObjects(entity);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void Human::RemoveFromScene()
|
|
{
|
|
room->grid_service->DeatchHuman(this);
|
|
room->TouchHumanList
|
|
(
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (Human* hum, a8::XParams& param)
|
|
{
|
|
Human* target = (Human*)param.sender.GetUserData();
|
|
hum->RemovePartObjects(target);
|
|
return true;
|
|
});
|
|
last_collision_door_ = nullptr;
|
|
}
|
|
|
|
ObjectSyncFlags* Human::GetObjectSyncFlags(int obj_uniid)
|
|
{
|
|
if ((size_t)obj_uniid < fixed_object_sync_flags_.size()) {
|
|
return &fixed_object_sync_flags_[obj_uniid];
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void Human::ResetSkill()
|
|
{
|
|
use_skill = false;
|
|
curr_skill_phase = 0;
|
|
skill_dir = a8::Vec2();
|
|
skill_target_pos = a8::Vec2();
|
|
skill_param1 = 0.0f;
|
|
playing_skill = false;
|
|
}
|
|
|
|
void Human::AddPassiveSkill(MetaData::Skill* skill_meta)
|
|
{
|
|
if (!HasPassiveSkill(skill_meta)) {
|
|
xtimer_list* tmp_timer = room->xtimer.AddRepeatTimerAndAttach
|
|
(
|
|
SERVER_FRAME_RATE * skill_meta->i->skill_cd(),
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(skill_meta),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
MetaData::Skill* skill_meta = (MetaData::Skill*)param.param1.GetUserData();
|
|
hum->ClearPassiveSkillBuff(skill_meta);
|
|
hum->AddPassiveSkillBuff(skill_meta);
|
|
},
|
|
&xtimer_attacher.timer_list_);
|
|
passive_skill_metas_[skill_meta] = tmp_timer;
|
|
AddPassiveSkillBuff(skill_meta);
|
|
if (skill_meta->i->skill_cd() > 10000) {
|
|
//永久被动被动技能
|
|
AddPassiveSkillBuff(skill_meta);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::RemovePassiveSkill(MetaData::Skill* skill_meta)
|
|
{
|
|
auto itr = passive_skill_metas_.find(skill_meta);
|
|
if (itr != passive_skill_metas_.end()) {
|
|
ClearPassiveSkillBuff(skill_meta);
|
|
room->xtimer.DeleteTimer(itr->second);
|
|
passive_skill_metas_.erase(itr);
|
|
}
|
|
}
|
|
|
|
void Human::ClearPassiveSkill()
|
|
{
|
|
std::vector<MetaData::Skill*> del_skills;
|
|
del_skills.reserve(passive_skill_metas_.size());
|
|
for (auto& pair : passive_skill_metas_) {
|
|
del_skills.push_back(pair.first);
|
|
}
|
|
for (MetaData::Skill* skill_meta : del_skills) {
|
|
RemovePassiveSkill(skill_meta);
|
|
}
|
|
}
|
|
|
|
bool Human::HasPassiveSkill(MetaData::Skill* skill_meta)
|
|
{
|
|
return passive_skill_metas_.find(skill_meta) != passive_skill_metas_.end();
|
|
}
|
|
|
|
void Human::ClearPassiveSkillBuff(MetaData::Skill* skill_meta)
|
|
{
|
|
for (int buff_id : skill_meta->buff_list) {
|
|
Buff* buff = GetBuffById(buff_id);
|
|
if (buff &&
|
|
(buff->meta->i->buff_target() == kBuffTargetSelf ||
|
|
buff->meta->i->buff_target() == kBuffTargetFriendly)) {
|
|
RemoveBuffById(buff_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::AddPassiveSkillBuff(MetaData::Skill* skill_meta)
|
|
{
|
|
if (!skill_meta) {
|
|
return;
|
|
}
|
|
MetaData::Skill* old_curr_skill = skill_meta_;
|
|
int old_skill_target_id = skill_target_id;
|
|
a8::Vec2 old_skill_target_pos = skill_target_pos;
|
|
|
|
skill_meta_ = skill_meta;
|
|
skill_target_id = GetEntityUniId();
|
|
skill_target_pos = GetPos();
|
|
|
|
std::set<Entity*> target_list;
|
|
SelectSkillTargets(GetPos(), target_list);
|
|
TriggerBuff(target_list, kBTT_UseSkill);
|
|
|
|
skill_meta_= old_curr_skill;
|
|
skill_target_id = old_skill_target_id;
|
|
skill_target_pos = old_skill_target_pos;
|
|
}
|
|
|
|
void Human::OnMetaChange()
|
|
{
|
|
if (GetRace() == kZombieRace) {
|
|
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(meta->i->normal_skill());
|
|
if (weapon_meta) {
|
|
default_weapon.weapon_idx = 0;
|
|
default_weapon.weapon_id = weapon_meta->i->id();
|
|
default_weapon.weapon_lv = 1;
|
|
default_weapon.ammo = 1;
|
|
default_weapon.meta = weapon_meta;
|
|
default_weapon.Recalc();
|
|
weapons[0] = default_weapon;
|
|
curr_weapon = &weapons[0];
|
|
}
|
|
}
|
|
ability.hp = meta->i->health();
|
|
for (auto& weapon : spec_weapons) {
|
|
if (weapon.meta) {
|
|
ability.hp += weapon.meta ? weapon.GetAttrValue(kHAT_MaxHp) : 0;
|
|
}
|
|
}
|
|
room->frame_event.AddHpChg(this);
|
|
RecalcBaseAttr();
|
|
skill_meta_ = MetaMgr::Instance()->GetSkill(meta->i->active_skill());
|
|
ResetSkill();
|
|
MetaData::Skill* passive_skill_meta = MetaMgr::Instance()->GetSkill(meta->i->passive_skill());
|
|
ClearPassiveSkill();
|
|
if (passive_skill_meta) {
|
|
AddPassiveSkill(passive_skill_meta);
|
|
}
|
|
}
|
|
|
|
void Human::OnChgToTerminator()
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_Terminator);
|
|
if (buff) {
|
|
std::vector<std::string> strings;
|
|
a8::Split(buff->meta->i->buff_param4(), strings, ':');
|
|
if (strings.size() >= 3) {
|
|
for (size_t i = 0; i < strings.size(); ++i) {
|
|
int weapon_id = a8::XValue(strings[i]);
|
|
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(weapon_id);
|
|
if (weapon_meta) {
|
|
Weapon* weapon = nullptr;
|
|
switch (i) {
|
|
case 0:
|
|
{
|
|
weapon = &weapons[0];
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
weapon = &weapons[GUN_SLOT1];
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
weapon = &weapons[GUN_SLOT2];
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
weapon->weapon_idx = i;
|
|
weapon->weapon_id = weapon_meta->i->id();
|
|
weapon->weapon_lv = 1;
|
|
weapon->meta = weapon_meta;
|
|
weapon->Recalc();
|
|
weapon->ammo = weapon->GetClipVolume();
|
|
if (i == 0) {
|
|
default_weapon = *weapon;
|
|
}
|
|
}
|
|
}
|
|
}//end if
|
|
need_sync_active_player = true;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
|
|
void Human::ProcReloadAction()
|
|
{
|
|
Weapon* p_weapon = curr_weapon;
|
|
if (car_weapon.meta) {
|
|
p_weapon = &car_weapon;
|
|
}
|
|
|
|
if (p_weapon->weapon_idx == action_target_id &&
|
|
p_weapon->weapon_id == action_item_id &&
|
|
p_weapon->weapon_idx != 0) {
|
|
MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(p_weapon->meta->i->use_bullet());
|
|
if (bullet_meta) {
|
|
int ammo = p_weapon->ammo;
|
|
if (ammo < p_weapon->GetClipVolume()) {
|
|
if (bullet_meta->i->_inventory_slot() >= 0 &&
|
|
bullet_meta->i->_inventory_slot() < IS_END) {
|
|
if (GetInventory(bullet_meta->i->_inventory_slot()) > 0) {
|
|
int add_num = 0;
|
|
if (GetInventory(bullet_meta->i->_inventory_slot()) <=
|
|
p_weapon->GetClipVolume() - ammo) {
|
|
add_num = GetInventory(bullet_meta->i->_inventory_slot());
|
|
if (p_weapon->meta->i->reloadtype() == 1) {
|
|
add_num = 1;
|
|
}
|
|
DecInventory(bullet_meta->i->_inventory_slot(), add_num);
|
|
} else {
|
|
add_num = p_weapon->GetClipVolume() - ammo;
|
|
if (p_weapon->meta->i->reloadtype() == 1) {
|
|
add_num = 1;
|
|
}
|
|
DecInventory(bullet_meta->i->_inventory_slot(), add_num);
|
|
}
|
|
p_weapon->ammo += add_num;
|
|
need_sync_active_player = true;
|
|
if (p_weapon->meta->i->reloadtype() == 1) {
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(1,
|
|
a8::XParams()
|
|
.SetSender(this)
|
|
.SetParam1(p_weapon->weapon_idx)
|
|
.SetParam2(p_weapon->weapon_id),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Human* hum = (Human*)param.sender.GetUserData();
|
|
hum->NextReload(param.param2, param.param1);
|
|
},
|
|
&xtimer_attacher.timer_list_
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void Human::NextReload(int prev_weapon_id, int prev_weapon_idx)
|
|
{
|
|
if (action_type != AT_None) {
|
|
return;
|
|
}
|
|
Weapon* p_weapon = curr_weapon;
|
|
if (car_weapon.meta) {
|
|
p_weapon = &car_weapon;
|
|
}
|
|
if (p_weapon &&
|
|
p_weapon->weapon_id == prev_weapon_id &&
|
|
p_weapon->weapon_idx == prev_weapon_idx) {
|
|
AutoLoadingBullet(true);
|
|
}
|
|
}
|