3365 lines
106 KiB
C++
3365 lines
106 KiB
C++
#include "precompile.h"
|
|
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
#include <a8/mutable_xobject.h>
|
|
#include <a8/collision.h>
|
|
|
|
#include <f8/utils.h>
|
|
#include <f8/udplog.h>
|
|
|
|
#include "human.h"
|
|
#include "room.h"
|
|
#include "bullet.h"
|
|
#include "loot.h"
|
|
#include "app.h"
|
|
#include "roommgr.h"
|
|
#include "android.h"
|
|
#include "gamelog.h"
|
|
#include "typeconvert.h"
|
|
#include "obstacle.h"
|
|
#include "roomobstacle.h"
|
|
#include "player.h"
|
|
#include "buff.h"
|
|
#include "car.h"
|
|
#include "roomobstacle.h"
|
|
#include "jsondatamgr.h"
|
|
#include "skill.h"
|
|
#include "incubator.h"
|
|
#include "team.h"
|
|
#include "explosion.h"
|
|
#include "killmgr.h"
|
|
#include "tracemgr.h"
|
|
#include "httpproxy.h"
|
|
#include "skillhelper.h"
|
|
#include "shot.h"
|
|
#include "battledatacontext.h"
|
|
#include "mapinstance.h"
|
|
#include "movement.h"
|
|
#include "pbutils.h"
|
|
#include "trigger.h"
|
|
#include "ability.h"
|
|
|
|
#include "mt/Param.h"
|
|
#include "mt/Hero.h"
|
|
#include "mt/Equip.h"
|
|
#include "mt/Buff.h"
|
|
#include "mt/Skill.h"
|
|
#include "mt/SkillNumber.h"
|
|
#include "mt/GunTalentGrow.h"
|
|
#include "mt/SafeArea.h"
|
|
#include "mt/MapThing.h"
|
|
#include "mt/Text.h"
|
|
#include "mt/Map.h"
|
|
#include "mt/PveGemini.h"
|
|
#include "mt/PveGeminiMode.h"
|
|
#include "mt/RankReward.h"
|
|
#include "mt/RankPoint.h"
|
|
#include "mt/KillReward.h"
|
|
#include "mt/KillPoint.h"
|
|
#include "mt/Drop.h"
|
|
|
|
#include "pbutils.h"
|
|
|
|
static double TopXFunc (Human* sender,
|
|
std::function<int (Human*, Human*)> cmpFunc,
|
|
bool need_group,
|
|
std::function<bool (Human*)> validFunc)
|
|
{
|
|
std::vector<std::vector<Human*>> rank_list;
|
|
double topx = 0.0f;
|
|
#if 0
|
|
sender->room->TraverseHumanList
|
|
(a8::XParams(),
|
|
[cmpFunc, &rank_list, need_group] (Human* hum, a8::XParams& param) -> bool
|
|
{
|
|
bool found = false;
|
|
if (need_group) {
|
|
for (auto& list : rank_list) {
|
|
if (cmpFunc(list[0], hum) == 0) {
|
|
list.push_back(hum);
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
if (!found) {
|
|
rank_list.push_back(std::vector<Human*>({hum}));
|
|
}
|
|
return true;
|
|
});
|
|
std::sort
|
|
(rank_list.begin(), rank_list.end(),
|
|
[cmpFunc] (const std::vector<Human*>& a, const std::vector<Human*>& b )
|
|
{
|
|
a8::XPrintf("id1:%d dead:%d id2:%d dead:%d cmpRet:%d\n",
|
|
{
|
|
a[0]->GetUniId(),
|
|
a[0]->dead ? 1 :0,
|
|
|
|
b[0]->GetUniId(),
|
|
b[0]->dead ? 1 :0,
|
|
cmpFunc(a[0], b[0])
|
|
});
|
|
return cmpFunc(a[0], b[0]) > 0;
|
|
});
|
|
|
|
int rank = rank_list.size();
|
|
for (int i = 0; i < rank_list.size(); ++i){
|
|
bool found = false;
|
|
for (Human* hum : rank_list[i]) {
|
|
if (hum == sender){
|
|
rank = i + 1;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if 1
|
|
double topx = (double)rank / 40.0f;
|
|
if (!validFunc(sender)) {
|
|
topx = 40.0f / 40.0f;
|
|
}
|
|
#else
|
|
double topx = (double)rank / rank_list.size();
|
|
#endif
|
|
#endif
|
|
return topx;
|
|
};
|
|
|
|
void PlayerStats::Statement(Human* sender)
|
|
{
|
|
sender->stats.ranked_topx = (double)sender->stats.rank / sender->room->GetHumanNum();
|
|
sender->stats.kills_topx = TopXFunc
|
|
(
|
|
sender,
|
|
[] (Human* a, Human* b) -> int
|
|
{
|
|
if (a->stats.kills == b->stats.kills) {
|
|
return 0;
|
|
} else if (a->stats.kills > b->stats.kills) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
},
|
|
true,
|
|
[] (Human* hum)
|
|
{
|
|
return hum->stats.kills > 0;
|
|
}
|
|
);
|
|
sender->stats.hero_topx = TopXFunc
|
|
(
|
|
sender,
|
|
[] (Human* a, Human* b) -> int
|
|
{
|
|
long long a_value = 0;
|
|
long long b_value = 0;
|
|
if (a->GetBattleContext()) {
|
|
int hero_lv = 0;
|
|
int quality = 0;
|
|
a->GetBattleContext()->GetHeroLvQuality(hero_lv, quality);
|
|
a_value = hero_lv + quality;
|
|
}
|
|
if (b->GetBattleContext()) {
|
|
int hero_lv = 0;
|
|
int quality = 0;
|
|
b->GetBattleContext()->GetHeroLvQuality(hero_lv, quality);
|
|
b_value = hero_lv + quality;
|
|
}
|
|
if (a_value == b_value) {
|
|
return 0;
|
|
} else if (a_value > b_value) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
},
|
|
true,
|
|
[] (Human* hum)
|
|
{
|
|
if (hum->GetBattleContext()) {
|
|
int hero_lv = 0;
|
|
int quality = 0;
|
|
hum->GetBattleContext()->GetHeroLvQuality(hero_lv, quality);
|
|
return hero_lv + quality > 0;
|
|
}
|
|
return false;
|
|
}
|
|
);
|
|
sender->stats.weapon_topx = TopXFunc
|
|
(
|
|
sender,
|
|
[] (Human* a, Human* b) -> int
|
|
{
|
|
long long a_value = 0;
|
|
long long b_value = 0;
|
|
if (a->GetBattleContext()) {
|
|
int weapon_lv = 0;
|
|
int quality = 0;
|
|
a->GetBattleContext()->GetWeaponLvQuality(weapon_lv, quality);
|
|
a_value = weapon_lv + quality;
|
|
}
|
|
if (b->GetBattleContext()) {
|
|
int weapon_lv = 0;
|
|
int quality = 0;
|
|
b->GetBattleContext()->GetWeaponLvQuality(weapon_lv, quality);
|
|
b_value = weapon_lv + quality;
|
|
}
|
|
if (a_value == b_value) {
|
|
return 0;
|
|
} else if (a_value > b_value) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
},
|
|
true,
|
|
[] (Human* hum)
|
|
{
|
|
if (hum->GetBattleContext()) {
|
|
int weapon_lv = 0;
|
|
int quality = 0;
|
|
hum->GetBattleContext()->GetWeaponLvQuality(weapon_lv, quality);
|
|
return weapon_lv + quality > 0;
|
|
}
|
|
return false;
|
|
}
|
|
);
|
|
sender->stats.survival_topx = TopXFunc
|
|
(
|
|
sender,
|
|
[] (Human* a, Human* b) -> int
|
|
{
|
|
if (a->dead && b->dead) {
|
|
if (a->dead_frameno == b->dead_frameno) {
|
|
return 0;
|
|
} else if (a->dead_frameno > b->dead_frameno) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (!a->dead && !b->dead) {
|
|
return 0;
|
|
} else {
|
|
if (!a->dead) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
true,
|
|
[] (Human* hum)
|
|
{
|
|
return true;
|
|
}
|
|
);
|
|
if (sender->GetBattleContext()) {
|
|
sender->GetBattleContext()->CalcBattleStat(this);
|
|
}
|
|
statemented = 1;
|
|
}
|
|
|
|
WeaponStats& PlayerStats::MustBeWeapon(int weapon_id)
|
|
{
|
|
auto itr = weapon_stats.find(weapon_id);
|
|
if (itr == weapon_stats.end()){
|
|
WeaponStats weapon;
|
|
weapon_stats[weapon_id] = weapon;
|
|
itr = weapon_stats.find(weapon_id);
|
|
}
|
|
return itr->second;
|
|
}
|
|
|
|
void PlayerStats::IncWeaponKills(int weapon_id, int val)
|
|
{
|
|
MustBeWeapon(weapon_id).kills += val;
|
|
}
|
|
|
|
void PlayerStats::IncWeaponDamageOut(int weapon_id, int val)
|
|
{
|
|
MustBeWeapon(weapon_id).damage_out += val;
|
|
}
|
|
|
|
void PlayerStats::IncWeaponObtainCount(int weapon_id, int val)
|
|
{
|
|
MustBeWeapon(weapon_id).obtain_count += val;
|
|
}
|
|
|
|
void PlayerStats::IncWeaponUseTimes(int weapon_id, int val)
|
|
{
|
|
MustBeWeapon(weapon_id).use_times += val;
|
|
}
|
|
|
|
HeroStats& PlayerStats::MustBeHero(int hero_id)
|
|
{
|
|
auto itr = hero_stats.find(hero_id);
|
|
if (itr == hero_stats.end()){
|
|
HeroStats hero;
|
|
hero_stats[hero_id] = hero;
|
|
itr = hero_stats.find(hero_id);
|
|
}
|
|
return itr->second;
|
|
}
|
|
|
|
void PlayerStats::SetHeroSkillLv(int hero_id, int skill_lv)
|
|
{
|
|
MustBeHero(hero_id).skill_lv = std::max(MustBeHero(hero_id).skill_lv, skill_lv);
|
|
}
|
|
|
|
void PlayerStats::SetHeroWeaponLv(int hero_id, int weapon_lv)
|
|
{
|
|
MustBeHero(hero_id).weapon_lv = std::max(MustBeHero(hero_id).weapon_lv, weapon_lv);
|
|
}
|
|
|
|
void PlayerStats::ParseReward(Human* hum, a8::XObject& xobj)
|
|
{
|
|
if (!xobj.IsObject()) {
|
|
AdjustRewardData();
|
|
return;
|
|
}
|
|
auto reward_xobj = xobj.At("reward");
|
|
if (!reward_xobj || !reward_xobj->IsObject()) {
|
|
AdjustRewardData();
|
|
return;
|
|
}
|
|
over_reward.total_ceg = reward_xobj->At("total_ceg")->AsXValue().GetString();
|
|
{
|
|
auto hero_xobj = reward_xobj->At("hero");
|
|
if (hero_xobj && hero_xobj->IsObject()) {
|
|
over_reward.hero.curr_gold = hero_xobj->At("curr_ceg") ?
|
|
hero_xobj->At("curr_ceg")->AsXValue().GetString() : "0";
|
|
over_reward.hero.obtain_gold = hero_xobj->At("obtain_ceg")->AsXValue().GetString();
|
|
over_reward.hero.gold_limit = hero_xobj->At("ceg_uplimit")->AsXValue().GetString();
|
|
if (hum->GetBattleContext()->hero_dto &&
|
|
hum->GetBattleContext()->hero_dto->IsObject()) {
|
|
over_reward.hero.id = hum->GetBattleContext()->hero_dto->At("hero_id")->AsXValue();
|
|
}
|
|
}
|
|
}
|
|
{
|
|
auto weapon_xobj = reward_xobj->At("weapon1");
|
|
if (weapon_xobj && weapon_xobj->IsObject()) {
|
|
over_reward.weapon1.curr_gold = weapon_xobj->At("curr_ceg") ?
|
|
weapon_xobj->At("curr_ceg")->AsXValue().GetString() : "";
|
|
over_reward.weapon1.obtain_gold = weapon_xobj->At("obtain_ceg")->AsXValue().GetString();
|
|
over_reward.weapon1.gold_limit = weapon_xobj->At("ceg_uplimit")->AsXValue().GetString();
|
|
if (hum->GetBattleContext()->weapon_dto1 &&
|
|
hum->GetBattleContext()->weapon_dto1->IsObject()) {
|
|
over_reward.weapon1.id = hum->GetBattleContext()->weapon_dto1->At("gun_id")->AsXValue();
|
|
}
|
|
}
|
|
}
|
|
{
|
|
auto weapon_xobj = reward_xobj->At("weapon2");
|
|
if (weapon_xobj && weapon_xobj->IsObject()) {
|
|
over_reward.weapon2.curr_gold = weapon_xobj->At("curr_ceg") ?
|
|
weapon_xobj->At("curr_ceg")->AsXValue().GetString() : "0";
|
|
over_reward.weapon2.obtain_gold = weapon_xobj->At("obtain_ceg")->AsXValue().GetString();
|
|
over_reward.weapon2.gold_limit = weapon_xobj->At("ceg_uplimit")->AsXValue().GetString();
|
|
if (hum->GetBattleContext()->weapon_dto2 &&
|
|
hum->GetBattleContext()->weapon_dto2->IsObject()) {
|
|
over_reward.weapon2.id = hum->GetBattleContext()->weapon_dto2->At("gun_id")->AsXValue();
|
|
}
|
|
}
|
|
}
|
|
{
|
|
auto items_xobj = reward_xobj->At("items");
|
|
if (items_xobj && items_xobj->IsArray()) {
|
|
for (int i = 0; i < items_xobj->Size(); ++i) {
|
|
auto item_xobj = items_xobj->At(i);
|
|
if (item_xobj && item_xobj->IsObject()) {
|
|
over_reward.items.push_back
|
|
(std::make_tuple
|
|
(
|
|
item_xobj->At("item_id")->AsXValue(),
|
|
item_xobj->At("item_num")->AsXValue()
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
{
|
|
auto score_info_xobj = xobj.At("score_info");
|
|
if (score_info_xobj && score_info_xobj->IsObject()) {
|
|
old_rank = score_info_xobj->At("old_rank")->AsXValue();
|
|
new_rank = score_info_xobj->At("new_rank")->AsXValue();
|
|
|
|
old_score = score_info_xobj->At("old_score")->AsXValue();
|
|
new_score = score_info_xobj->At("new_score")->AsXValue();
|
|
}
|
|
}
|
|
AdjustRewardData();
|
|
}
|
|
|
|
void PlayerStats::AdjustRewardData()
|
|
{
|
|
auto EmptyStrToZero =
|
|
[] (std::string& str) {
|
|
if (str.empty()) {
|
|
str = "0";
|
|
}
|
|
};
|
|
EmptyStrToZero(over_reward.total_ceg);
|
|
{
|
|
EmptyStrToZero(over_reward.hero.curr_gold);
|
|
EmptyStrToZero(over_reward.hero.obtain_gold);
|
|
EmptyStrToZero(over_reward.hero.gold_limit);
|
|
}
|
|
{
|
|
EmptyStrToZero(over_reward.weapon1.curr_gold);
|
|
EmptyStrToZero(over_reward.weapon1.obtain_gold);
|
|
EmptyStrToZero(over_reward.weapon1.gold_limit);
|
|
}
|
|
{
|
|
EmptyStrToZero(over_reward.weapon2.curr_gold);
|
|
EmptyStrToZero(over_reward.weapon2.obtain_gold);
|
|
EmptyStrToZero(over_reward.weapon2.gold_limit);
|
|
}
|
|
}
|
|
|
|
Human::Human():Creature()
|
|
{
|
|
default_weapon.weapon_idx = 0;
|
|
default_weapon.weapon_id = 30101;
|
|
default_weapon.ammo = 1;
|
|
default_weapon.meta = mt::Equip::GetById(default_weapon.weapon_id);
|
|
default_weapon.Recalc();
|
|
for (int i = 0; i < kSkinNum; ++i) {
|
|
Skin& skin = a8::FastAppend(skins);
|
|
skin.skin_id = 0;
|
|
skin.skin_lv = 1;
|
|
}
|
|
weapons[0] = default_weapon;
|
|
|
|
if (mt::Param::s().fighting_mode) {
|
|
AddInventory(IS_9MM, FIGHTING_MODE_BULLET_NUM);
|
|
AddInventory(IS_556MM, FIGHTING_MODE_BULLET_NUM);
|
|
AddInventory(IS_762MM, FIGHTING_MODE_BULLET_NUM);
|
|
AddInventory(IS_12GAUGE, FIGHTING_MODE_BULLET_NUM);
|
|
AddInventory(IS_RPG, FIGHTING_MODE_BULLET_NUM);
|
|
AddInventory(IS_ICE, FIGHTING_MODE_BULLET_NUM);
|
|
}
|
|
}
|
|
|
|
Human::~Human()
|
|
{
|
|
int i = 0;
|
|
}
|
|
|
|
void Human::Initialize()
|
|
{
|
|
Creature::Initialize();
|
|
volume_ = meta->_volume;
|
|
observers_.insert(this);
|
|
SetCurrWeapon(&weapons[0]);
|
|
SetOxygen(mt::Param::s().dive_oxygen_total);
|
|
}
|
|
|
|
float Human::GetSpeed()
|
|
{
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_JumpTo);
|
|
if (buff) {
|
|
return buff->meta->_buff_param2;
|
|
}
|
|
}
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_HoldShield);
|
|
if (buff) {
|
|
return buff->meta->_buff_param1;
|
|
}
|
|
}
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_Jump);
|
|
if (buff) {
|
|
return meta->jump_speed();
|
|
}
|
|
}
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_BePull);
|
|
if (buff) {
|
|
return buff->meta->_buff_param2;
|
|
}
|
|
}
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_Sprint);
|
|
if (buff) {
|
|
return buff->meta->_buff_param2;
|
|
}
|
|
}
|
|
float speed = 1.0;
|
|
if (downed) {
|
|
speed = meta->move_speed3();
|
|
} else {
|
|
if (shot_hold) {
|
|
if (GetCurrWeapon()->weapon_idx == GUN_SLOT1 ||
|
|
GetCurrWeapon()->weapon_idx == GUN_SLOT2) {
|
|
if (action_type != AT_Reload) {
|
|
speed = meta->shot_speed() * (1 + GetAbility()->GetAttrRate(kHAT_ShotSpeed));
|
|
if (HasBuffEffect(kBET_InWater)) {
|
|
speed *= mt::Param::s().water_move_coefficient;
|
|
}
|
|
return speed;
|
|
}
|
|
}
|
|
} else if (aiming) {
|
|
speed = std::max(1, meta->aiming_speed());
|
|
if (HasBuffEffect(kBET_InWater)) {
|
|
speed *= mt::Param::s().water_move_coefficient;
|
|
}
|
|
return speed;
|
|
} else if (action_type == AT_Reload) {
|
|
speed = std::max(1, meta->reload_speed());
|
|
if (HasBuffEffect(kBET_InWater)) {
|
|
speed *= mt::Param::s().water_move_coefficient;
|
|
}
|
|
return speed;
|
|
} else if (action_type == AT_UseItem) {
|
|
speed = std::max(1, meta->useitem_speed());
|
|
if (HasBuffEffect(kBET_InWater)) {
|
|
speed *= mt::Param::s().water_move_coefficient;
|
|
}
|
|
return speed;
|
|
}
|
|
speed = meta->move_speed();
|
|
}
|
|
float old_speed = speed;
|
|
#if 1
|
|
speed = (speed * SERVER_FRAME_RATE *
|
|
(1 + GetAbility()->GetSpeedAddition() - GetAbility()->GetSpeedRuduce())) / SERVER_FRAME_RATE;
|
|
#else
|
|
speed = (speed + GetAbility()->GetAttrAbs(kHAT_Speed)) *
|
|
(1 + GetAbility()->GetAttrRate(kHAT_Speed));
|
|
#endif
|
|
if (HasBuffEffect(kBET_InWater)) {
|
|
speed *= mt::Param::s().water_move_coefficient;
|
|
}
|
|
return std::max(speed, 1.0f);
|
|
}
|
|
|
|
float Human::GetSpeed4()
|
|
{
|
|
return meta->move_speed4();
|
|
}
|
|
|
|
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::CarShot(const glm::vec3& target_dir)
|
|
{
|
|
if (!second_weapon.meta) {
|
|
return;
|
|
}
|
|
|
|
if (second_weapon.weapon_idx != 0 &&
|
|
second_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,
|
|
second_weapon.meta,
|
|
second_weapon.bullet_meta,
|
|
nullptr,
|
|
5,
|
|
0,
|
|
0);
|
|
|
|
--second_weapon.ammo;
|
|
last_shot_frameno_ = room->GetFrameNo();
|
|
}
|
|
|
|
void Human::BeKill(int killer_id, const std::string& killer_name, int weapon_id,
|
|
int real_killer_id, const std::string& real_killer_name)
|
|
{
|
|
if (!dead && !room->IsGameOver() && !real_dead && !a8::HasBitFlag(status, CS_PreDieSuspended)) {
|
|
GetTrigger()->PreDie(killer_id, weapon_id);
|
|
#ifdef DEBUG
|
|
{
|
|
if (!f8::IsTestEnv()) {
|
|
a8::XPrintf("BeKill killer_id:%d killer_name:%s over_delay_time:%d\n",
|
|
{
|
|
killer_id,
|
|
killer_name,
|
|
over_delay_time
|
|
});
|
|
}
|
|
}
|
|
#endif
|
|
if (over_delay_time > 0) {
|
|
std::shared_ptr<KillInfo> info = std::make_shared<KillInfo>();
|
|
info->killer_id = killer_id;
|
|
info->killer_name = killer_name;
|
|
info->weapon_id = weapon_id;
|
|
info->real_killer_id = real_killer_id;
|
|
info->real_killer_name = real_killer_name;
|
|
|
|
a8::SetBitFlag(status, CS_PreDieSuspended);
|
|
room->xtimer.SetTimeoutEx
|
|
(over_delay_time / FRAME_RATE_MS,
|
|
[this, info] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
over_delay_time = 0;
|
|
a8::UnSetBitFlag(status, CS_PreDieSuspended);
|
|
InternalBeKill
|
|
(
|
|
info->killer_id,
|
|
info->killer_name,
|
|
info->weapon_id,
|
|
info->real_killer_id,
|
|
info->real_killer_name
|
|
);
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
} else {
|
|
InternalBeKill(killer_id, killer_name, weapon_id, real_killer_id, real_killer_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::DecHP(float dec_hp, int killer_id, const std::string& killer_name, int weapon_id,
|
|
int real_killer_id, const std::string& real_killer_name)
|
|
{
|
|
if (!room->BattleStarted()) {
|
|
return;
|
|
}
|
|
#ifdef DEBUG
|
|
#if 0
|
|
if (IsPlayer()) {
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_Shield);
|
|
if (buff) {
|
|
dec_hp = std::max((float)1.0, dec_hp - buff->meta->_buff_param2);
|
|
}
|
|
}
|
|
if (HasBuffEffect(kBET_Invincible)) {
|
|
return;
|
|
}
|
|
{
|
|
Human* hum = room->GetHumanByUniId(real_killer_id);
|
|
if (hum) {
|
|
attacker_hash_[real_killer_id] = room->GetFrameNo();
|
|
}
|
|
}
|
|
struct DownedInfo
|
|
{
|
|
int killer_id = 0;
|
|
std::string killer_name;
|
|
int weapon_id = 0;
|
|
int real_killer_id = 0;
|
|
std::string real_killer_name;
|
|
};
|
|
|
|
last_receive_dmg_frameno = room->GetFrameNo();
|
|
float old_hp = GetHP();
|
|
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);
|
|
SetHP(std::max(0.0f, new_health));
|
|
if (old_health - GetHP() > 0.001f) {
|
|
stats.damage_amount_in += old_health - GetHP();
|
|
GetTrigger()->ReceiveDmg();
|
|
}
|
|
if (GetHP() <= 0.0001f && !dead) {
|
|
if (downed) {
|
|
if (!downed_timer.expired()) {
|
|
room->xtimer.Delete(downed_timer);
|
|
}
|
|
downed = false;
|
|
BeKill(killer_id, killer_name, weapon_id, real_killer_id, real_killer_name);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
} else {
|
|
if (HasNoDownedTeammate() && !room->IsPveRoom()) {
|
|
SetHP(mt::Param::GetIntParam("downed_recover_hp"));
|
|
downed = true;
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
CancelAction();
|
|
DoGetDown();
|
|
std::shared_ptr<DownedInfo> info = std::make_shared<DownedInfo>();
|
|
info->killer_id = killer_id;
|
|
info->killer_name = killer_name;
|
|
info->weapon_id = weapon_id;
|
|
info->real_killer_id = real_killer_id;
|
|
info->real_killer_name = real_killer_name;
|
|
downed_timer = room->xtimer.SetIntervalWpEx
|
|
(
|
|
SERVER_FRAME_RATE,
|
|
[this, info] (int event, const a8::Args* args) mutable
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
if (!downed) {
|
|
FreeDownedTimer();
|
|
return;
|
|
}
|
|
if (dead) {
|
|
FreeDownedTimer();
|
|
return;
|
|
}
|
|
if (!HasLiveTeammate()) {
|
|
FreeDownedTimer();
|
|
BeKill(info->killer_id, info->killer_name, info->weapon_id,
|
|
info->real_killer_id, info->real_killer_name);
|
|
return;
|
|
}
|
|
int dec_hp = mt::Param::GetIntParam("downed_dec_hp");
|
|
DecHP(dec_hp, info->killer_id, info->killer_name, info->weapon_id,
|
|
info->real_killer_id, info->real_killer_name);
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
TryAddBuff(this, kDownBuffId);
|
|
} else {
|
|
BeKill(killer_id, killer_name, weapon_id, real_killer_id, real_killer_name);
|
|
}
|
|
}
|
|
}
|
|
room->frame_event.AddHpChg(GetWeakPtrRef());
|
|
}
|
|
GetTrigger()->HpChg();
|
|
}
|
|
|
|
void Human::AddToNewObjects(Entity* entity)
|
|
{
|
|
framedata_.new_objects[entity->GetUniId()] = entity->GetEntityWeakPtrRef();
|
|
}
|
|
|
|
void Human::AddToImageObjects(Creature* c)
|
|
{
|
|
framedata_.image_objects[c->GetUniId()] = c->GetWeakPtrRef();
|
|
}
|
|
|
|
void Human::AddToPartObjects(Entity* entity)
|
|
{
|
|
#ifdef DEBUG1
|
|
{
|
|
if (!follow_target_) {
|
|
for (Human* ob : observers_) {
|
|
if (ob->IsPlayer()) {
|
|
TraceMgr::Instance()->Trace
|
|
(a8::Format("add part_object %d->%d frameno:%d %d",
|
|
{
|
|
GetUniId(),
|
|
ob->GetUniId(),
|
|
room->GetFrameNo(),
|
|
entity->GetUniId()
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
PartObject part_obj;
|
|
part_obj.entity_uniid = entity->GetUniId();
|
|
part_obj.entity_type = entity->GetEntityType();
|
|
part_obj.entity_subtype = entity->GetEntitySubType();
|
|
part_obj.add_frameno = room->GetFrameNo();
|
|
part_obj.object = entity->GetEntityWeakPtrRef();
|
|
framedata_.part_objects[entity->GetUniId()] = part_obj;
|
|
entity->OnAddToTargetPartObject(this);
|
|
}
|
|
|
|
void Human::RemovePartObjects(Entity* entity)
|
|
{
|
|
#ifdef DEBUG1
|
|
if (!follow_target_) {
|
|
for (Human* observer : observers_) {
|
|
if (observer->IsPlayer()) {
|
|
observer->SendDebugMsg(a8::Format("view_debug %d->%d frameno:%d remove_part_obj:%d",
|
|
{
|
|
GetUniId(),
|
|
observer->GetUniId(),
|
|
room->GetFrameNo(),
|
|
entity->GetUniId()
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
framedata_.part_objects.erase(entity->GetUniId());
|
|
entity->OnRemoveFromTargetPartObject(this);
|
|
}
|
|
|
|
void Human::ClearPartObjects()
|
|
{
|
|
framedata_.part_objects.clear();
|
|
}
|
|
|
|
int Human::GetPartObjectsCount()
|
|
{
|
|
return framedata_.part_objects.size();
|
|
}
|
|
|
|
bool Human::InNewObjects(Entity* target)
|
|
{
|
|
return framedata_.new_objects.find(target->GetUniId()) != framedata_.new_objects.end();
|
|
}
|
|
|
|
bool Human::InPartObjects(Entity* target)
|
|
{
|
|
return framedata_.part_objects.find(target->GetUniId()) != framedata_.part_objects.end();
|
|
}
|
|
|
|
void Human::RemoveObjects(Entity* entity)
|
|
{
|
|
framedata_.del_objects.insert(entity->GetUniId());
|
|
framedata_.new_objects.erase(entity->GetUniId());
|
|
}
|
|
|
|
void Human::AddOutObjects(Entity* entity)
|
|
{
|
|
framedata_.out_objects.insert(entity->GetUniId());
|
|
}
|
|
|
|
void Human::RemoveOutObjects(Entity* entity)
|
|
{
|
|
framedata_.out_objects.erase(entity->GetUniId());
|
|
}
|
|
|
|
bool Human::HasLiveTeammate()
|
|
{
|
|
bool has = false;
|
|
if (GetTeam()) {
|
|
GetTeam()->TraverseMembers
|
|
(
|
|
[this, &has] (Human* member)
|
|
{
|
|
if (member != this && !member->dead) {
|
|
has = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
return has;
|
|
}
|
|
|
|
bool Human::HasNoDownedTeammate()
|
|
{
|
|
bool has = false;
|
|
if (GetTeam()) {
|
|
GetTeam()->TraverseMembers
|
|
(
|
|
[this, &has] (Human* member)
|
|
{
|
|
if (member != this && !member->dead && !member->downed) {
|
|
has = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
return has;
|
|
}
|
|
|
|
int Human::GetNearbyTeammateNum(float range)
|
|
{
|
|
int num = 0;
|
|
if (GetTeam()) {
|
|
GetTeam()->TraverseMembers
|
|
(
|
|
[this, &num, range] (Human* member)
|
|
{
|
|
if (member != this && !member->dead && !member->downed) {
|
|
if (member->GetPos().Distance2D2(GetPos()) <= range) {
|
|
++num;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
return num;
|
|
}
|
|
|
|
void Human::DoJump()
|
|
{
|
|
if (HasBuffEffect(kBET_Fly)) {
|
|
a8::UnSetBitFlag(status, CS_DisableAttack);
|
|
RemoveBuffByEffectId(kBET_Fly);
|
|
MustBeAddBuff(this, kThroughWall_BUFFID);
|
|
MustBeAddBuff(this, JUMP_BUFFID);
|
|
jump_frameno_ = room->GetFrameNo();
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
|
|
void Human::DoGetOn(int obj_uniid)
|
|
{
|
|
if (room->GetGasData().GetGasMode() == GasInactive) {
|
|
return;
|
|
}
|
|
Entity* entity = room->GetEntityByUniId(obj_uniid);
|
|
if (!entity) {
|
|
return;
|
|
}
|
|
if (GetPos().Distance2D2(entity->GetPos()) > mt::Param::s().max_mount_horse_distance) {
|
|
return;
|
|
}
|
|
if (downed) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Become)) {
|
|
return;
|
|
}
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Loot:
|
|
{
|
|
DoGetOnWithLoot((Loot*)entity);
|
|
}
|
|
break;
|
|
case ET_Car:
|
|
{
|
|
DoGetOnWithCar((Car*)entity);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Human::RefreshView()
|
|
{
|
|
#if 1
|
|
room->grid_service->TraverseCreatures
|
|
(
|
|
room->GetRoomIdx(),
|
|
GetGridList(),
|
|
[this] (Creature* c, bool& stop)
|
|
{
|
|
c->AddToNewObjects(this);
|
|
c->AddToPartObjects(this);
|
|
AddToNewObjects(c);
|
|
AddToPartObjects(c);
|
|
});
|
|
#else
|
|
TraverseAllLayerHumanList
|
|
(
|
|
[this] (Human* hum, bool& stop)
|
|
{
|
|
hum->AddToNewObjects(this);
|
|
hum->AddToPartObjects(this);
|
|
AddToNewObjects(hum);
|
|
AddToPartObjects(hum);
|
|
});
|
|
#endif
|
|
TraverseAllLayerEntityList
|
|
(
|
|
[this] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
case ET_Obstacle:
|
|
case ET_Loot:
|
|
case ET_MapBlock:
|
|
{
|
|
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::SyncAroundPlayers(const char* file, int line, const char* func)
|
|
{
|
|
#if 0
|
|
if (a8::HasBitFlag(status, CS_Disable)) {
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef DEBUG
|
|
#if 0
|
|
room->CheckPartObjects();
|
|
f8::UdpLog::Instance()->Debug("room_idx:%d syncaround begin %s %d %s",
|
|
{
|
|
room->GetRoomIdx(),
|
|
file,
|
|
line,
|
|
func
|
|
});
|
|
#endif
|
|
#endif
|
|
TraverseAllLayerHumanList
|
|
(
|
|
[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});
|
|
}
|
|
f8::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});
|
|
}
|
|
f8::UdpLog::Instance()->Debug("hum2 %d %s", {(long long)this, objs_str});
|
|
}
|
|
room->CheckPartObjects(hum, this);
|
|
hum->InPartObjects(this);
|
|
#endif
|
|
#endif
|
|
assert(hum->InPartObjects(this));
|
|
if (!hum->InPartObjects(this)) {
|
|
static long long last_debugout_tick = 0;
|
|
if (a8::XGetTickCount() - last_debugout_tick > 1000 * 10) {
|
|
last_debugout_tick = a8::XGetTickCount();
|
|
f8::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();
|
|
f8::UdpLog::Instance()->Debug("syncaround end %s %d %s",
|
|
{
|
|
file,
|
|
line,
|
|
func
|
|
});
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void Human::RecalcVolume()
|
|
{
|
|
const mt::Equip* backpack_meta = mt::Equip::GetById(backpack);
|
|
for (size_t i = 0; i < IS_END; ++i) {
|
|
volume_[i] = meta->_volume[i];
|
|
volume_[i] += buff_inventory_[i].num;
|
|
if (backpack_meta) {
|
|
volume_[i] += backpack_meta->_volume[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::RecalcBaseAttr()
|
|
{
|
|
#if 0
|
|
xxxxxxxx
|
|
const mt::Equip* chest_meta = mt::Equip::GetById(chest);
|
|
float def = meta->def();
|
|
if (chest_meta) {
|
|
def += chest_meta->def();
|
|
}
|
|
const mt::Equip* helmet_meta = mt::Equip::GetById(helmet);
|
|
if (helmet_meta) {
|
|
def += helmet_meta->def();
|
|
}
|
|
SetDef(def);
|
|
#endif
|
|
}
|
|
|
|
int Human::GetVolume(int slot_id)
|
|
{
|
|
if (!IsValidSlotId(slot_id)) {
|
|
A8_ABORT();
|
|
}
|
|
return volume_[slot_id];
|
|
}
|
|
|
|
void Human::RecoverHp(int inc_hp)
|
|
{
|
|
if (!dead) {
|
|
float hp = GetHP();
|
|
hp += inc_hp;
|
|
SetHP(hp);
|
|
SetHP(std::min(GetHP(), GetMaxHP()));
|
|
}
|
|
}
|
|
|
|
void Human::AddObserver(Human* observer)
|
|
{
|
|
observers_.insert(observer);
|
|
}
|
|
|
|
void Human::RemoveObserver(Human* observer)
|
|
{
|
|
observers_.erase(observer);
|
|
}
|
|
|
|
bool Human::HasObserver()
|
|
{
|
|
return observers_.size() > 1;
|
|
}
|
|
|
|
void Human::FollowTarget(Human* target)
|
|
{
|
|
if (target == this) {
|
|
return;
|
|
}
|
|
int pre_uniid = 0;
|
|
if (follow_target_) {
|
|
pre_uniid = follow_target_->GetUniId();
|
|
follow_target_->RemoveObserver(this);
|
|
}
|
|
target->AddObserver(this);
|
|
follow_target_ = target;
|
|
follow_synced_active_player = false;
|
|
#ifdef DEBUG
|
|
SendDebugMsg(a8::Format("观战 watch %d->%d->%d frameno:%d",
|
|
{
|
|
room->GetFrameNo(),
|
|
pre_uniid,
|
|
follow_target_->GetUniId(),
|
|
GetUniId()
|
|
}));
|
|
#endif
|
|
}
|
|
|
|
void Human::UpdateAction()
|
|
{
|
|
int passed_time = (room->GetFrameNo() - action_frameno) * FRAME_RATE_MS;
|
|
int left_time = std::max(0, action_duration - passed_time);
|
|
if (left_time <= 0) {
|
|
switch (action_type) {
|
|
case AT_Reload:
|
|
{
|
|
ProcReloadAction();
|
|
}
|
|
break;
|
|
case AT_UseItem:
|
|
{
|
|
ProcUseItemAction();
|
|
}
|
|
break;
|
|
case AT_Relive:
|
|
{
|
|
ProcReliveAction();
|
|
}
|
|
break;
|
|
case AT_Rescue:
|
|
{
|
|
RemoveBuffByEffectId(kBET_InRescue);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
ResetAction();
|
|
}
|
|
}
|
|
|
|
void Human::UpdateMove()
|
|
{
|
|
Creature::UpdateMove();
|
|
}
|
|
|
|
void Human::GenBattleReportData(a8::MutableXObject* params)
|
|
{
|
|
params->SetVal("room_mode", room->GetRoomMode());
|
|
int rank = 0;
|
|
{
|
|
std::vector<Human*> human_list;
|
|
room->TraverseHumanList(
|
|
[&human_list] (Human* hum) -> 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->GetUniId() < b->GetUniId();
|
|
} 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->GetUniId() < b->GetUniId();
|
|
}
|
|
});
|
|
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) {
|
|
if (room->GetAliveTeam() == GetTeam()) {
|
|
rank = 1;
|
|
}
|
|
}
|
|
stats.rank = rank;
|
|
if (room->IsPveRoom()) {
|
|
if (stats.pve_kill_boss) {
|
|
stats.victory = true;
|
|
stats.settlement_color = 1;
|
|
}
|
|
} else {
|
|
stats.victory = stats.rank == 1;
|
|
if (GetTeam()->GetAliveNum() <= 0) {
|
|
GetTeam()->team_rank = room->GetAliveTeamNum();
|
|
stats.pvp_settlement_type = GetTeam()->GetMemberNum() > 1 ? 1 : 0;
|
|
stats.settlement_color = 1;
|
|
} else {
|
|
if (room->GetAliveTeamNum() == 1) {
|
|
if (room->GetAliveTeam() == GetTeam()) {
|
|
GetTeam()->team_rank = 1;
|
|
stats.pvp_settlement_type = GetTeam()->GetMemberNum() > 1 ? 1 : 0;
|
|
stats.settlement_color = 1;
|
|
}
|
|
}
|
|
}
|
|
if (rank < 10) {
|
|
stats.settlement_color = 1;
|
|
}
|
|
if (stats.victory) {
|
|
GetTeam()->team_rank = 0;
|
|
}
|
|
}
|
|
}
|
|
if (!stats.statemented) {
|
|
stats.Statement(this);
|
|
}
|
|
params->SetVal("account_id", account_id);
|
|
params->SetVal("session_id", session_id);
|
|
params->SetVal("team_id", team_id);
|
|
params->SetVal("pve_instance_id", room->IsPveRoom() ? room->pve_instance->gemini_id() : 0);
|
|
params->SetVal("pve_instance_mode", room->IsPveRoom() ? room->pve_mode_meta->id() : 0);
|
|
params->SetVal("battle_uuid", battle_uuid);
|
|
params->SetVal("match_mode", room->IsPveRoom() ? 2 : 0);
|
|
params->SetVal("is_valid_battle", is_valid_battle);
|
|
params->SetVal("payload", payload);
|
|
params->SetVal("map_id", room->GetMapMeta()->map_id());
|
|
params->SetVal("map_name", room->GetMapMeta()->map_name());
|
|
params->SetVal("team_mode", GetTeam() && GetTeam()->GetMemberNum() > 1 ? 1 : 0);
|
|
params->SetVal("map_tpl_name", room->GetMapTplName());
|
|
params->SetVal("room_uuid", room->GetRoomUuid());
|
|
params->SetVal("room_mode", room->GetRoomMode());
|
|
params->SetVal("hero_id", meta->id());
|
|
params->SetVal("hero_uniid", hero_uniid);
|
|
params->SetVal("game_time", time(nullptr)); //?
|
|
int alive_time = room->GetFrameNo() * FRAME_RATE_MS;
|
|
if (!dead) {
|
|
params->SetVal("alive_time", alive_time);
|
|
} else {
|
|
alive_time = (dead_frameno - room->GetBattleStartFrameNo()) * FRAME_RATE_MS;
|
|
if (room->GetBattleStartFrameNo() <= 0) {
|
|
alive_time = 0;
|
|
}
|
|
params->SetVal("alive_time", alive_time);
|
|
}
|
|
params->SetVal("weapon_uuid1", spec_weapons.size() > 0 ? spec_weapons[0].weapon_uniid : 0);
|
|
params->SetVal("weapon_uuid2", spec_weapons.size() > 1 ? spec_weapons[1].weapon_uniid : 0);
|
|
params->SetVal("ranked", rank);
|
|
params->SetVal("kills", stats.kills);
|
|
params->SetVal("damage_out", stats.damage_amount_out);
|
|
params->SetVal("rescue_teammate_times", stats.rescue_member);
|
|
params->SetVal("diving_times", stats.diving_times);
|
|
params->SetVal("damage_in", stats.damage_amount_in);
|
|
params->SetVal("recover_hp", stats.heal_amount);
|
|
params->SetVal("open_airdrop_times", stats.open_airdrop_times);
|
|
params->SetVal("use_medicine_times", stats.use_medicine_times);
|
|
params->SetVal("destory_car_times", stats.destory_car_times);
|
|
params->SetVal("use_camouflage_times", stats.use_camouflage_times);
|
|
params->SetVal("use_skill_times", stats.use_skill_times);
|
|
params->SetVal("ride_car_move_distance", stats.ride_car_move_distance);
|
|
params->SetVal("ride_car_kills", stats.ride_car_kills);
|
|
params->SetVal("max_hero_skill_lv", stats.max_hero_skill_lv);
|
|
{
|
|
params->SetVal("pve_rank_score", stats.pve_rank_score);
|
|
params->SetVal("pve_kill_boss", stats.pve_kill_boss);
|
|
}
|
|
{
|
|
std::string weapons_type;
|
|
std::string weapons_slot;
|
|
for (auto& pair : stats.weapon_stats) {
|
|
auto& weapon = pair.second;
|
|
if (weapon.kills || weapon.damage_out || weapon.obtain_count) {
|
|
weapons_type += a8::Format("%d:%d:%d:%d|",
|
|
{
|
|
weapon.weapon_id,
|
|
weapon.kills,
|
|
weapon.damage_out,
|
|
weapon.obtain_count
|
|
});
|
|
}
|
|
if (weapon.use_times) {
|
|
weapons_slot += a8::Format("%d:%d|",
|
|
{
|
|
weapon.weapon_id,
|
|
weapon.use_times
|
|
});
|
|
}
|
|
}
|
|
params->SetVal("weapons_type", weapons_type);
|
|
params->SetVal("weapons_slot", weapons_slot);
|
|
}
|
|
{
|
|
std::string heros;
|
|
for (auto& pair : stats.hero_stats) {
|
|
auto& hero = pair.second;
|
|
if (hero.skill_lv || hero.weapon_lv) {
|
|
heros += a8::Format("%d:%d:%d|",
|
|
{
|
|
hero.hero_id,
|
|
hero.skill_lv,
|
|
hero.weapon_lv,
|
|
});
|
|
}
|
|
}
|
|
params->SetVal("heros", heros);
|
|
}
|
|
{
|
|
float rank_param = mt::RankReward::GetRankRewardParam(rank);
|
|
float kill_param = mt::KillReward::GetKillRewardParam(stats.kills);
|
|
int coin_num = (rank_param * mt::Param::s().rank_param) +
|
|
(kill_param * mt::Param::s().kill_param);
|
|
stats.gold = coin_num;
|
|
params->SetVal("coin_num", coin_num);
|
|
}
|
|
{
|
|
std::string items_str;
|
|
for (auto& pair : battlein_items) {
|
|
items_str += a8::Format("%d:%d|",
|
|
{pair.first,
|
|
pair.second
|
|
});
|
|
}
|
|
params->SetVal("items", items_str);
|
|
}
|
|
{
|
|
stats.pass_score = mt::KillPoint::GetKillPointParam1(stats.kills);
|
|
stats.pass_score += mt::RankPoint::GetRankPointParam1(rank);
|
|
stats.rank_score = mt::KillPoint::GetKillPointParam2(stats.kills);
|
|
stats.rank_score += mt::RankPoint::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);
|
|
{
|
|
params->SetVal("ranked_topx", stats.ranked_topx);
|
|
params->SetVal("kills_topx", stats.kills_topx);
|
|
params->SetVal("hero_topx", stats.hero_topx);
|
|
params->SetVal("weapon_topx", stats.weapon_topx);
|
|
params->SetVal("survival_topx", stats.survival_topx);
|
|
|
|
params->SetVal("hero_stats_uniid", stats.pb_hero_stats.hero_uniid);
|
|
params->SetVal("hero_stats_name", stats.pb_hero_stats.hero_name);
|
|
params->SetVal("hero_stats_hero_id", stats.pb_hero_stats.hero_id);
|
|
params->SetVal("hero_stats_reward_ceg", stats.pb_hero_stats.reward_ceg);
|
|
params->SetVal("hero_stats_today_get_ceg", stats.pb_hero_stats.today_get_ceg);
|
|
params->SetVal("hero_stats_ceg_uplimit", stats.pb_hero_stats.ceg_uplimit);
|
|
|
|
for (int i = 0; i < stats.pb_weapons_stats.size(); ++i) {
|
|
auto& p = stats.pb_weapons_stats.at(i);
|
|
std::string i_str = a8::XValue(i).GetString();
|
|
params->SetVal("weapon_stats_uniid" + i_str, p.weapon_uniid);
|
|
params->SetVal("weapon_stats_name" + i_str, p.weapon_name);
|
|
params->SetVal("weapon_stats_weapon_id" + i_str, p.weapon_id);
|
|
params->SetVal("weapon_stats_reward_ceg" + i_str, p.reward_ceg);
|
|
params->SetVal("weapon_stats_today_get_ceg" + i_str, p.today_get_ceg);
|
|
params->SetVal("weapon_stats_ceg_uplimit" + i_str, p.ceg_uplimit);
|
|
}
|
|
}
|
|
{
|
|
std::shared_ptr<a8::MutableXObject> post_data = a8::MutableXObject::CreateObject();
|
|
std::shared_ptr<a8::MutableXObject> team_list = a8::MutableXObject::CreateArray();
|
|
room->TraverseTeams
|
|
(
|
|
[team_list] (Team* team)
|
|
{
|
|
std::shared_ptr<a8::MutableXObject> team_data = a8::MutableXObject::CreateObject();
|
|
std::shared_ptr<a8::MutableXObject> members_list = a8::MutableXObject::CreateArray();
|
|
|
|
team->TraverseMembers
|
|
(
|
|
[members_list] (Human* hum)
|
|
{
|
|
std::shared_ptr<a8::MutableXObject> member = a8::MutableXObject::CreateObject();
|
|
member->SetVal("account_id", hum->account_id);
|
|
members_list->Push(*member.get());
|
|
return true;
|
|
});
|
|
|
|
team_data->SetVal("team_id", team->GetTeamId());
|
|
team_data->SetVal("members", *members_list.get());
|
|
team_list->Push(*team_data.get());
|
|
return true;
|
|
});
|
|
post_data->SetVal("team_list", *team_list.get());
|
|
params->SetVal("__POST", post_data->ToJsonStr());
|
|
}
|
|
}
|
|
|
|
void Human::DeadDrop()
|
|
{
|
|
#ifdef DEBUG
|
|
a8::XPrintf("DeadDrop\n", {});
|
|
//room->DropItem(GetPos(), 30908, 1, 1);
|
|
//return;
|
|
#endif
|
|
auto SkinCanDrop =
|
|
[this] (Skin* skin) -> bool
|
|
{
|
|
#ifdef DEBUG
|
|
return false;
|
|
#else
|
|
return skin->skin_id != 0;
|
|
#endif
|
|
};
|
|
|
|
if (a8::HasBitFlag(status, CS_DeadNoDrop)) {
|
|
return;
|
|
}
|
|
if (!(HasBuffEffect(kBET_Become) && GetBuffByEffectId(kBET_Become)->FreezeOperate())) {
|
|
for (auto& weapon : weapons) {
|
|
if (weapon.weapon_id != 0 &&
|
|
weapon.weapon_id != default_weapon.weapon_id
|
|
) {
|
|
Position drop_pos = GetPos();
|
|
room->DropItem(drop_pos, weapon.weapon_id, 1, 1);
|
|
if (weapon.ammo > 0 && weapon.bullet_meta && weapon.ammo < 200) {
|
|
if (IsPlayer() &&
|
|
weapon.bullet_meta &&
|
|
IsValidSlotId(weapon.bullet_meta->_inventory_slot())) {
|
|
AddInventory(weapon.bullet_meta->_inventory_slot(), weapon.ammo);
|
|
}
|
|
}
|
|
if (IsAndroid() && weapon.bullet_meta) {
|
|
int drop_num = weapon.bullet_meta->group_num();
|
|
room->DropItem(GetPos(), weapon.bullet_meta->id(), drop_num, 1);
|
|
}
|
|
weapon.Clear();
|
|
}
|
|
}
|
|
{
|
|
weapons[0] = default_weapon;
|
|
SetCurrWeapon(&weapons[0]);
|
|
}
|
|
}
|
|
{
|
|
Skin* old_skin = GetSkinByIdx(kSkinSlot_HAT);
|
|
if (old_skin && old_skin->skin_id != 0 && SkinCanDrop(old_skin)) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.AddGlmVec3(dir * (40.0f + rand() % 50));
|
|
room->CreateLoot(old_skin->skin_id, drop_pos, 1, 1);
|
|
*old_skin = Skin();
|
|
}
|
|
}
|
|
{
|
|
Skin* old_skin = GetSkinByIdx(kSkinSlot_CLOTH);
|
|
if (old_skin && old_skin->skin_id != 0 && SkinCanDrop(old_skin)) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.AddGlmVec3(dir * (40.0f + rand() % 50));
|
|
room->CreateLoot(old_skin->skin_id, drop_pos, 1, 1);
|
|
*old_skin = Skin();
|
|
}
|
|
}
|
|
{
|
|
//头盔
|
|
if (helmet != 0) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.AddGlmVec3(dir * (40.0f + rand() % 50));
|
|
room->CreateLoot(helmet, drop_pos, 1, 1);
|
|
helmet = 0;
|
|
}
|
|
//衣服
|
|
if (chest != 0) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.AddGlmVec3(dir * (40.0f + rand() % 50));
|
|
room->CreateLoot(chest, drop_pos, 1, 1);
|
|
chest = 0;
|
|
}
|
|
//背包
|
|
if (backpack != 0) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.AddGlmVec3(dir * (40.0f + rand() % 50));
|
|
room->CreateLoot(backpack, drop_pos, 1, 1);
|
|
backpack = 0;
|
|
}
|
|
}
|
|
for (size_t slot = 0; slot < GetInventoryData().size(); ++slot) {
|
|
if (GetInventory(slot) > 0 && !mt::Param::s().fighting_mode) {
|
|
const mt::Equip* equip_meta = mt::Equip::GetByIdBySlotId(slot);
|
|
if (equip_meta) {
|
|
int drop_num = equip_meta->group_num();
|
|
if (equip_meta->equip_type() == EQUIP_TYPE_BULLET) {
|
|
switch (equip_meta->_inventory_slot()) {
|
|
case IS_FRAG:
|
|
case IS_SMOKE:
|
|
case IS_POSION_GAS_BOMB:
|
|
case IS_MOLOTOR_COCKTAIL:
|
|
case IS_TRAP:
|
|
case IS_MINE:
|
|
case IS_C4:
|
|
case IS_SHIELD_WALL:
|
|
case IS_SINGAL_GUN:
|
|
case IS_OIL_BUCKET:
|
|
{
|
|
if (drop_num > 0) {
|
|
Position drop_pos = GetPos();
|
|
if (IsPlayer()) {
|
|
room->DropItem(drop_pos, equip_meta->id(), GetInventory(slot), 1);
|
|
} else {
|
|
room->DropItem(drop_pos, equip_meta->id(), drop_num, 1);
|
|
}
|
|
DecInventory(slot, GetInventory(slot));
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
if (drop_num > 0) {
|
|
Position drop_pos = GetPos();
|
|
if (IsPlayer()) {
|
|
room->DropItem(drop_pos, equip_meta->id(), GetInventory(slot), 1);
|
|
} else {
|
|
#if 1
|
|
if (GetCurrWeapon()) {
|
|
|
|
}
|
|
#else
|
|
room->DropItem(drop_pos, equip_meta->id(), drop_num, 1);
|
|
#endif
|
|
}
|
|
DecInventory(slot, GetInventory(slot));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
Position drop_pos = GetPos();
|
|
room->DropItem(drop_pos, equip_meta->id(), GetInventory(slot), 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
}
|
|
|
|
void Human::SendBattleReport()
|
|
{
|
|
std::shared_ptr<a8::MutableXObject> params = a8::MutableXObject::CreateObject();
|
|
GenBattleReportData(params.get());
|
|
std::string url;
|
|
JsonDataMgr::Instance()->GetApiUrl(url);
|
|
if (url.find('?') != std::string::npos) {
|
|
url += "c=Battle&a=battleReport";
|
|
} else {
|
|
url += "?c=Battle&a=battleReport";
|
|
}
|
|
std::string data;
|
|
params->ToUrlEncodeStr(data);
|
|
if (stats.is_run_away) {
|
|
sending_battlereport_ = false;
|
|
already_report_battle_ = true;
|
|
SendGameOver();
|
|
} else {
|
|
long long room_uuid = room->GetRoomUuid();
|
|
std::string sender_id = account_id;
|
|
HttpProxy::Instance()->HttpGet
|
|
(
|
|
[room_uuid, sender_id, data]
|
|
(bool ok, a8::XObject* rsp_obj, f8::HttpContext* ctx)
|
|
{
|
|
if (!ok) {
|
|
f8::UdpLog::Instance()->Error("battleReport http error params: %s response: %s",
|
|
{
|
|
data,
|
|
""
|
|
});
|
|
}
|
|
Room* room = RoomMgr::Instance()->GetRoomByUuid(room_uuid);
|
|
if (!room) {
|
|
return;
|
|
}
|
|
Player* hum = room->GetPlayerByAccountId(sender_id);
|
|
if (!hum) {
|
|
return;
|
|
}
|
|
if (ok) {
|
|
hum->sending_battlereport_ = false;
|
|
hum->already_report_battle_ = true;
|
|
hum->stats.ParseReward(hum, *rsp_obj);
|
|
hum->SendGameOver();
|
|
hum->SendBattleSettlement();
|
|
} else {
|
|
hum->sending_battlereport_ = false;
|
|
}
|
|
},
|
|
url.c_str(),
|
|
*params.get()
|
|
);
|
|
}
|
|
}
|
|
|
|
void Human::ProcLootSkin(AddItemDTO& dto)
|
|
{
|
|
switch (dto.item_meta->equip_subtype()) {
|
|
case 11:
|
|
{
|
|
//装饰
|
|
Skin* old_skin = GetSkinByIdx(kSkinSlot_HAT);
|
|
if (old_skin) {
|
|
if (old_skin->skin_id != 0) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.AddGlmVec3(dir * (40.0f + rand() % 50));
|
|
room->CreateLoot(old_skin->skin_id, drop_pos, 1, 1);
|
|
}
|
|
|
|
*old_skin = Skin();
|
|
old_skin->skin_id = dto.item_meta->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) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.AddGlmVec3(dir * (40.0f + rand() % 50));
|
|
room->CreateLoot(old_skin->skin_id, drop_pos, 1, 1);
|
|
}
|
|
|
|
*old_skin = Skin();
|
|
old_skin->skin_id = dto.item_meta->id();
|
|
old_skin->skin_lv = 1;
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
dto.handled = true;
|
|
}
|
|
|
|
void Human::ProcLootCar(AddItemDTO& dto)
|
|
{
|
|
DoGetOn(dto.uniid);
|
|
dto.handled = true;
|
|
}
|
|
|
|
void Human::ProcNormalItem(AddItemDTO& dto)
|
|
{
|
|
AddItem(dto.item_meta->id(), 1);
|
|
dto.handled = true;
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
}
|
|
|
|
void Human::ProcSpoils(AddItemDTO& dto)
|
|
{
|
|
dto.handled = true;
|
|
}
|
|
|
|
void Human::OnDie()
|
|
{
|
|
{
|
|
if (HasBuffEffect(kBET_Camouflage)) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
DoGetDown();
|
|
}
|
|
real_dead_frameno = room->GetFrameNo();
|
|
room->OnHumanDie(this);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
if (GetTeam()) {
|
|
GetTeam()->TraverseMembers
|
|
(
|
|
[this] (Human* member) -> bool
|
|
{
|
|
if (this != member &&
|
|
member->action_type == AT_Relive &&
|
|
member->action_target_id == GetUniId()) {
|
|
member->CancelAction();
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
if (real_dead) {
|
|
std::set<Human*> over_humans;
|
|
if (!leave_) {
|
|
if (!HasNoDownedTeammate()) {
|
|
if (GetTeam()) {
|
|
GetTeam()->GetOveredHumans(over_humans);
|
|
} else {
|
|
over_humans.insert(this);
|
|
}
|
|
} else {
|
|
over_humans.insert(this);
|
|
}
|
|
}
|
|
if (room->GetAliveTeamNum() == 1) {
|
|
Team* alive_team = room->GetAliveTeam();
|
|
if (alive_team) {
|
|
alive_team->GetOveredHumans(over_humans);
|
|
}
|
|
}
|
|
for (Human* hum : over_humans) {
|
|
hum->SendGameOver();
|
|
}
|
|
}
|
|
if (observers_.size() > 1) {
|
|
std::vector<Human*> exclude_self_observers;
|
|
for (auto& observer : observers_) {
|
|
if (observer != this) {
|
|
exclude_self_observers.push_back(observer);
|
|
}
|
|
}
|
|
for (auto& observer : exclude_self_observers) {
|
|
observers_.erase(observer);
|
|
observer->OnWatcherDie(this);
|
|
}
|
|
}
|
|
DeadDrop();
|
|
}
|
|
|
|
void Human::FreeDownedTimer()
|
|
{
|
|
RemoveBuffById(kDownBuffId);
|
|
if (!downed_timer.expired()) {
|
|
room->xtimer.Delete(downed_timer);
|
|
}
|
|
}
|
|
|
|
void Human::FreeReviveTimer()
|
|
{
|
|
if (!revive_timer.expired()) {
|
|
room->xtimer.Delete(revive_timer);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void Human::AddBuffPostProc(Creature* caster, Buff* buff)
|
|
{
|
|
}
|
|
|
|
int Human::GetItemNum(int item_id)
|
|
{
|
|
auto itr = items_.find(item_id);
|
|
return itr != items_.end() ? itr->second : 0;
|
|
}
|
|
|
|
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(GetWeakPtrRef(), item_id, std::max(0, itr->second));
|
|
if (itr->second <= 0) {
|
|
if (battling_items_.find(item_id) == battling_items_.end()) {
|
|
//items_.erase(itr); //为啥?????
|
|
}
|
|
items_.erase(itr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::DropItems(Obstacle* obstacle)
|
|
{
|
|
bool is_treasure_box = false;
|
|
int drop_id = obstacle->meta->RandDrop();
|
|
if (drop_id == 0) {
|
|
return;
|
|
}
|
|
|
|
if (drop_id == 0) {
|
|
drop_id = obstacle->meta->RandDrop();
|
|
}
|
|
room->ScatterDrop(obstacle->GetPos(), drop_id);
|
|
if (is_treasure_box) {
|
|
++box_drop_times_;
|
|
} else {
|
|
++normal_drop_times_;
|
|
}
|
|
}
|
|
|
|
void Human::Revive()
|
|
{
|
|
{
|
|
int wait_revive_time = mt::Param::GetIntParam("revive_time", 25) +
|
|
kReviveTimeAdd;
|
|
revive_timer = room->xtimer.SetTimeoutWpEx
|
|
(SERVER_FRAME_RATE * wait_revive_time,
|
|
[this] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
dead = true;
|
|
real_dead = true;
|
|
downed = false;
|
|
OnDie();
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
|
|
void Human::CancelRevive()
|
|
{
|
|
if (dead && !revive_timer.expired() && !real_dead) {
|
|
dead = true;
|
|
real_dead = true;
|
|
downed = false;
|
|
FreeDownedTimer();
|
|
OnDie();
|
|
FreeReviveTimer();
|
|
}
|
|
}
|
|
|
|
void Human::AdjustDecHp(float old_health, float& new_health)
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_NewProtect);
|
|
if (buff) {
|
|
if (new_health < GetMaxHP() * buff->meta->_buff_param1) {
|
|
new_health = std::max(GetMaxHP() * buff->meta->_buff_param1, (float)0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::OnEnable()
|
|
{
|
|
a8::UnSetBitFlag(status, CS_Disable);
|
|
enable_frameno = room->GetFrameNo();
|
|
room->grid_service->MoveCreature(this);
|
|
FindLocation();
|
|
RefreshView();
|
|
}
|
|
|
|
void Human::OnDisable()
|
|
{
|
|
a8::SetBitFlag(status, CS_Disable);
|
|
RemoveFromScene();
|
|
framedata_.ClearFrameData(this);
|
|
ClearPartObjects();
|
|
}
|
|
|
|
void Human::GetViewObjects(std::set<Entity*>& view_objects)
|
|
{
|
|
TraverseCreatures
|
|
(
|
|
[&view_objects] (Creature* c, bool& stop)
|
|
{
|
|
view_objects.insert(c);
|
|
});
|
|
TraverseAllLayerEntityList
|
|
(
|
|
[&view_objects] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
case ET_Obstacle:
|
|
case ET_Loot:
|
|
case ET_MapBlock:
|
|
{
|
|
view_objects.insert(entity);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
void Human::ProcIncGridList(std::set<GridCell*>& old_grids,
|
|
std::set<GridCell*>& inc_grids,
|
|
std::set<GridCell*>& dec_grids)
|
|
{
|
|
room->grid_service->TraverseCreatures
|
|
(
|
|
room->GetRoomIdx(),
|
|
inc_grids,
|
|
[this, &old_grids] (Creature* c, bool& stop)
|
|
{
|
|
if (!room->grid_service->CreatureInGridList(c, old_grids)) {
|
|
c->AddToNewObjects(this);
|
|
c->AddToPartObjects(this);
|
|
c->RemoveOutObjects(this);
|
|
AddToNewObjects(c);
|
|
AddToPartObjects(c);
|
|
RemoveOutObjects(c);
|
|
}
|
|
});
|
|
room->grid_service->TraverseAllLayerEntityList
|
|
(
|
|
room->GetRoomIdx(),
|
|
inc_grids,
|
|
[this] (Entity* entity, bool& stop)
|
|
{
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Building:
|
|
case ET_Obstacle:
|
|
case ET_Loot:
|
|
case ET_MapBlock:
|
|
{
|
|
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->TraverseCreatures
|
|
(
|
|
room->GetRoomIdx(),
|
|
dec_grids,
|
|
[this] (Creature* c, bool& stop)
|
|
{
|
|
if (!room->grid_service->CreatureInGridList(c, GetGridList())) {
|
|
AddOutObjects(c);
|
|
c->AddOutObjects(this);
|
|
#ifdef DEBUG
|
|
#if 0
|
|
f8::UdpLog::Instance()->Debug("addoutobjects %d %d",
|
|
{
|
|
(long long)c,
|
|
c->GetUniId()
|
|
});
|
|
#endif
|
|
#endif
|
|
}
|
|
});
|
|
room->grid_service->TraverseAllLayerEntityList
|
|
(
|
|
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:
|
|
case ET_MapBlock:
|
|
{
|
|
AddOutObjects(entity);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void Human::RemoveFromScene()
|
|
{
|
|
room->grid_service->DeatchHuman(this);
|
|
room->TraverseHumanList
|
|
(
|
|
[this] (Human* hum)
|
|
{
|
|
hum->RemovePartObjects(this);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void Human::ProcReloadAction()
|
|
{
|
|
Weapon* p_weapon = GetCurrWeapon();
|
|
if (second_weapon.meta) {
|
|
p_weapon = &second_weapon;
|
|
}
|
|
|
|
if (p_weapon->weapon_idx == action_target_id &&
|
|
p_weapon->weapon_id == action_item_id &&
|
|
p_weapon->weapon_idx != 0) {
|
|
const mt::Equip* bullet_meta = mt::Equip::GetById(p_weapon->meta->use_bullet());
|
|
if (bullet_meta) {
|
|
int ammo = p_weapon->ammo;
|
|
if (ammo < p_weapon->GetClipVolume(this)) {
|
|
if (bullet_meta->_inventory_slot() >= 0 &&
|
|
bullet_meta->_inventory_slot() < IS_END) {
|
|
if (GetInventory(bullet_meta->_inventory_slot()) > 0) {
|
|
int add_num = 0;
|
|
if (GetInventory(bullet_meta->_inventory_slot()) <=
|
|
p_weapon->GetClipVolume(this) - ammo) {
|
|
add_num = GetInventory(bullet_meta->_inventory_slot());
|
|
if (p_weapon->meta->reloadtype() == 1) {
|
|
add_num = 1;
|
|
}
|
|
DecInventory(bullet_meta->_inventory_slot(), add_num);
|
|
} else {
|
|
add_num = p_weapon->GetClipVolume(this) - ammo;
|
|
if (p_weapon->meta->reloadtype() == 1) {
|
|
add_num = 1;
|
|
}
|
|
DecInventory(bullet_meta->_inventory_slot(), add_num);
|
|
}
|
|
p_weapon->ammo += add_num;
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
if (p_weapon->meta->reloadtype() == 1) {
|
|
int weapon_idx = p_weapon->weapon_idx;
|
|
int weapon_id = p_weapon->weapon_id;
|
|
room->xtimer.SetTimeoutEx
|
|
(1,
|
|
[this, weapon_idx, weapon_id]
|
|
(int event, const a8::Args* args)
|
|
{
|
|
NextReload(weapon_id, weapon_idx);
|
|
},
|
|
&xtimer_attacher);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void Human::ProcUseItemAction()
|
|
{
|
|
const mt::Equip* item_meta = mt::Equip::GetByIdBySlotId(action_item_id);
|
|
if (!item_meta) {
|
|
return;
|
|
}
|
|
if (GetInventory(item_meta->_inventory_slot()) <= 0) {
|
|
return;
|
|
}
|
|
switch (action_item_id) {
|
|
case IS_HEALTHKIT:
|
|
{
|
|
+stats.use_medicine_times;
|
|
AddHp(item_meta->heal() * (1 + GetAbility()->GetAttrRate(kHAT_DrugEfficacy)));
|
|
DecInventory(item_meta->_inventory_slot(), 1);
|
|
GetTrigger()->UseItemAction(action_item_id);
|
|
}
|
|
break;
|
|
case IS_PAIN_KILLER:
|
|
{
|
|
+stats.use_medicine_times;
|
|
if (!pain_killer_timer.expired()) {
|
|
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 = mt::Param::GetIntParam("anodyne_max_time");
|
|
left_time = std::min(left_time, anodyne_max_time * 1000);
|
|
pain_killer_lastingtime += std::min(item_meta->time() * 1000,
|
|
anodyne_max_time * 1000 - left_time) / 1000;
|
|
} else {
|
|
pain_killer_frameno = room->GetFrameNo();
|
|
pain_killer_lastingtime = item_meta->time();
|
|
if (pain_killer_lastingtime > 0) {
|
|
auto heal = item_meta->heal();
|
|
pain_killer_timer = room->xtimer.SetIntervalWpEx
|
|
(
|
|
SERVER_FRAME_RATE,
|
|
[this, heal] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
AddHp(heal);
|
|
if (room->GetFrameNo() - pain_killer_frameno >
|
|
pain_killer_lastingtime * SERVER_FRAME_RATE) {
|
|
room->xtimer.Delete(pain_killer_timer);
|
|
}
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
}
|
|
AddHp(item_meta->heal());
|
|
}
|
|
DecInventory(item_meta->_inventory_slot(), 1);
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
GetTrigger()->UseItemAction(action_item_id);
|
|
}
|
|
break;
|
|
case IS_SHEN_BAO:
|
|
{
|
|
+stats.use_medicine_times;
|
|
if (!shen_bao_timer.expired()) {
|
|
a8::Args args({item_meta->time()});
|
|
room->xtimer.FireEvent(shen_bao_timer, kShenBaoAddTimeTimerEvent, &args);
|
|
} else {
|
|
int exec_time = 0;
|
|
int total_time = item_meta->time();
|
|
int heal = item_meta->heal();
|
|
shen_bao_timer = room->xtimer.SetIntervalWpEx
|
|
(
|
|
SERVER_FRAME_RATE,
|
|
[this, exec_time, total_time, heal] (int event, const a8::Args* args) mutable
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
exec_time += 1;
|
|
if (exec_time >= total_time || dead) {
|
|
room->xtimer.DeleteCurrentTimer();
|
|
} else {
|
|
AddHp(heal);
|
|
}
|
|
} else if (kShenBaoAddTimeTimerEvent == event) {
|
|
total_time += args->Get<int>(0);
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
AddHp(heal);
|
|
}
|
|
DecInventory(item_meta->_inventory_slot(), 1);
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
GetTrigger()->UseItemAction(action_item_id);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Human::ProcReliveAction()
|
|
{
|
|
RemoveBuffByEffectId(kBET_Rescuer);
|
|
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();
|
|
}
|
|
if (!dead && downed) {
|
|
SetHP(mt::Param::GetFloatParam("downed_relive_recover_hp") * GetMaxHP());
|
|
downed = false;
|
|
if (!downed_timer.expired()) {
|
|
room->xtimer.Delete(downed_timer);
|
|
}
|
|
++hum->stats.rescue_member;
|
|
if (hum->guild_id != 0 && hum->guild_id == guild_id) {
|
|
++hum->stats.rescue_guild_member;
|
|
}
|
|
if (GetBuffById(kDownBuffId)) {
|
|
RemoveBuffById(kDownBuffId);
|
|
}
|
|
}
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
|
|
void Human::OnBuffRemove(Buff& buff)
|
|
{
|
|
Creature::OnBuffRemove(buff);
|
|
}
|
|
|
|
void Human::NextReload(int prev_weapon_id, int prev_weapon_idx)
|
|
{
|
|
if (action_type != AT_None) {
|
|
return;
|
|
}
|
|
Weapon* p_weapon = GetCurrWeapon();
|
|
if (second_weapon.meta) {
|
|
p_weapon = &second_weapon;
|
|
}
|
|
if (p_weapon &&
|
|
p_weapon->weapon_id == prev_weapon_id &&
|
|
p_weapon->weapon_idx == prev_weapon_idx) {
|
|
AutoLoadingBullet(true);
|
|
}
|
|
}
|
|
|
|
void Human::DoGetDown()
|
|
{
|
|
if (GetCar()) {
|
|
GetCar()->GetDown(this);
|
|
}
|
|
}
|
|
|
|
void Human::DoGetOnWithLoot(Loot* entity)
|
|
{
|
|
const mt::Equip* item_meta = mt::Equip::GetById(entity->item_id);
|
|
if (!item_meta) {
|
|
return;
|
|
}
|
|
if (GetCar()) {
|
|
GetCar()->GetDown(this);
|
|
}
|
|
Car* car = room->CreateCar(
|
|
this,
|
|
entity->GetUniId(),
|
|
item_meta,
|
|
entity->GetPos(),
|
|
team_id
|
|
);
|
|
car->GetOn(this);
|
|
#ifdef DEBUG
|
|
a8::XPrintf("DoGetOnWithLoot uniid:%d car_uniid:%d\n", {car->GetUniId(), car->car_uniid});
|
|
#endif
|
|
}
|
|
|
|
void Human::DoGetOnWithCar(Car* car)
|
|
{
|
|
car->GetOn(this);
|
|
}
|
|
|
|
void Human::DoSkillPreProc(int skill_id, int target_ids)
|
|
{
|
|
if (action_type == AT_Reload ||
|
|
action_type == AT_UseItem
|
|
) {
|
|
CancelAction();
|
|
}
|
|
}
|
|
|
|
void Human::DoSkillPostProc(bool used, int skill_id, int target_id)
|
|
{
|
|
if (used) {
|
|
++stats.skill_times;
|
|
Skill* skill = GetSkill(skill_id);
|
|
if (skill) {
|
|
skill->DecTimes();
|
|
room->frame_event.AddSkillCdChg(AllocWeakPtr(), skill_id, skill->GetLeftTime());
|
|
room->frame_event.AddSkillCurrTimesChg(AllocWeakPtr(), skill_id, skill->GetCurrTimes());
|
|
}
|
|
OnAttack();
|
|
}
|
|
}
|
|
|
|
float Human::GetRadius()
|
|
{
|
|
return meta->radius();
|
|
}
|
|
|
|
float Human::GetHitRadius()
|
|
{
|
|
return meta->hit_radius();
|
|
}
|
|
|
|
void Human::UpdateViewObjects()
|
|
{
|
|
if (view_objects_.size() >= 2) {
|
|
std::vector<Human*> deleted_humans;
|
|
for (Human* hum : view_objects_) {
|
|
if (hum->dead ||
|
|
hum->GetPos().ManhattanDistance2D(GetPos()) > mt::Param::s().view_objects_out_distance) {
|
|
deleted_humans.push_back(hum);
|
|
}
|
|
}
|
|
for (Human* hum : deleted_humans) {
|
|
view_objects_.erase(hum);
|
|
}
|
|
}
|
|
if (view_objects_.size() < 2) {
|
|
TraverseAllLayerHumanList
|
|
(
|
|
[this] (Human* hum, bool& stop)
|
|
{
|
|
if (hum->IsAndroid() && !hum->dead && view_objects_.find(hum) == view_objects_.end()) {
|
|
if (hum->GetPos().ManhattanDistance2D(GetPos()) <
|
|
mt::Param::s().view_objects_in_distance) {
|
|
view_objects_.insert(hum);
|
|
}
|
|
if (view_objects_.size() >= 2) {
|
|
stop = true;
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (view_objects_.size() < 2) {
|
|
room->GetIncubator()->AllocAndroid(this, 1 + rand() % 2);
|
|
if (!refresh_view_timer_.expired()) {
|
|
room->xtimer.ModifyTime
|
|
(refresh_view_timer_,
|
|
SERVER_FRAME_RATE * (mt::Param::s().refresh_view_time + (rand() % 3))
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::GMAddItem(int item_id, int item_num)
|
|
{
|
|
const mt::Equip* item_meta = mt::Equip::GetById(item_id);
|
|
if (!item_meta) {
|
|
return;
|
|
}
|
|
AddItemDTO dto;
|
|
dto.uniid = 0;
|
|
dto.item_id = item_id;
|
|
dto.count = item_num;
|
|
dto.item_level = 1;
|
|
dto.handled = false;
|
|
dto.item_meta = item_meta;
|
|
ProcAddItemDto(dto);
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
|
|
void Human::OnBulletHit(IBullet* bullet)
|
|
{
|
|
if (IsInvincible()) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Jump) ||
|
|
HasBuffEffect(kBET_Fly)) {
|
|
return;
|
|
}
|
|
{
|
|
Buff* buff = GetBuffByEffectId(kBET_Camouflage);
|
|
if (buff && buff->meta->_int_buff_param2 == 1) {
|
|
return;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
#if 0
|
|
if (IsPlayer()) {
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
if (bullet->GetSender().Get()) {
|
|
bullet->GetSender().Get()->GetTrigger()->BulletHit(bullet, this);
|
|
}
|
|
|
|
RemoveBuffByEffectId(kBET_PeaceMode);
|
|
RemoveBuffByEffectId(kBET_Hide);
|
|
GetTrigger()->Attacked(bullet->GetSender().Get());
|
|
if (!dead && (bullet->IsBomb() || bullet->GetSender().Get()->team_id != team_id)) {
|
|
float finaly_dmg = 0;
|
|
if (bullet->GetSkillMeta() && SkillHelper::ProcBulletDmg(bullet, this, finaly_dmg)) {
|
|
} else {
|
|
finaly_dmg = bullet->GetSender().Get()->GetBattleContext()->CalcDmg(this, bullet);
|
|
}
|
|
if (bullet->GetSender().Get()->IsHuman()) {
|
|
bullet->GetSender().Get()->AsHuman()->stats.damage_amount_out += finaly_dmg;
|
|
}
|
|
if (bullet->GetBulletMeta()->_buff_meta) {
|
|
MustBeAddBuff(bullet->GetSender().Get(), bullet->GetBulletMeta()->buffid());
|
|
}
|
|
if (bullet->GetSender().Get()->GetBattleContext()->IsCrit()) {
|
|
room->frame_event.AddPropChg
|
|
(
|
|
GetWeakPtrRef(),
|
|
kPropCritDmg,
|
|
0,
|
|
bullet->GetSender().Get()->GetBattleContext()->GetCritDmg()
|
|
);
|
|
}
|
|
if (bullet->GetSender().Get() && bullet->GetSender().Get()->IsCar() && bullet->GetPassenger().Get()) {
|
|
if (!bullet->IsPreBattleBullet()) {
|
|
DecHP(finaly_dmg,
|
|
bullet->GetPassenger().Get()->GetUniId(),
|
|
bullet->GetPassenger().Get()->GetName(),
|
|
bullet->GetGunMeta()->id(),
|
|
bullet->GetPassenger().Get()->GetUniId(),
|
|
bullet->GetPassenger().Get()->GetName()
|
|
);
|
|
}
|
|
} else {
|
|
if (!bullet->IsPreBattleBullet()) {
|
|
if (bullet->GetSender().Get() && bullet->GetSender().Get()->IsHuman()) {
|
|
bullet->GetSender().Get()->AsHuman()->stats.IncWeaponDamageOut
|
|
(bullet->GetGunMeta()->id(), finaly_dmg);
|
|
}
|
|
DecHP(finaly_dmg,
|
|
bullet->GetSender().Get()->GetUniId(),
|
|
bullet->GetSender().Get()->GetName(),
|
|
bullet->GetGunMeta()->id(),
|
|
bullet->GetSender().Get()->GetUniId(),
|
|
bullet->GetSender().Get()->GetName()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::OnExplosionHit(Explosion* e)
|
|
{
|
|
if (IsInvincible()) {
|
|
return;
|
|
}
|
|
if (dead) {
|
|
return;
|
|
}
|
|
if (e->IsPreBattleExplosion()) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Jump) ||
|
|
HasBuffEffect(kBET_Fly)) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Dive) && !mt::Param::s().dive_explosion_dmg_switch) {
|
|
return;
|
|
}
|
|
int real_killer_id = 0;
|
|
std::string real_killer_name;
|
|
if (e->GetSender().Get()) {
|
|
real_killer_id = e->GetSender().Get()->GetUniId();
|
|
real_killer_name = e->GetSender().Get()->GetName();
|
|
}
|
|
|
|
RemoveBuffByEffectId(kBET_PeaceMode);
|
|
float finaly_dmg = GetBattleContext()->CalcDmg(e);
|
|
#if 1
|
|
{
|
|
#else
|
|
if (e->GetSender().Get()) {
|
|
#endif
|
|
DecHP(finaly_dmg,
|
|
VP_Explosion,
|
|
"",
|
|
e->GetExplosionEffect(),
|
|
real_killer_id,
|
|
real_killer_name);
|
|
}
|
|
}
|
|
|
|
void Human::SendRollMsgEx(KillInfo& info,
|
|
const char* fmt,
|
|
std::initializer_list<a8::XValue> args
|
|
)
|
|
{
|
|
}
|
|
|
|
void Human::ProcUseItem(int item_id)
|
|
{
|
|
if (downed) {
|
|
return;
|
|
}
|
|
const mt::Equip* item_meta = mt::Equip::GetById(item_id);
|
|
if (item_meta && GetItemNum(item_id) > 0) {
|
|
if (item_meta->use_scene() == 1 && !HasBuffEffect(kBET_InWater)) {
|
|
SendSysPiaoMsg(TEXT("only_inwater_use", "only in water use"),
|
|
a8::MkRgb(255, 0, 0),
|
|
3
|
|
);
|
|
return;
|
|
}
|
|
if (GetCar() && item_meta->equip_type() == EQUIP_TYPE_CAMOUFLAGE) {
|
|
return;
|
|
}
|
|
std::shared_ptr<Ability> old_context_ability = context_ability;
|
|
glm::vec3 old_context_dir = context_dir;
|
|
Position old_context_pos = context_pos;
|
|
context_dir = GetAttackDir();
|
|
context_pos = GetPos();
|
|
if (item_meta->buffid() != 0) {
|
|
TryAddBuff(this, item_meta->buffid());
|
|
}
|
|
DecItem(item_id, 1);
|
|
context_dir = old_context_dir;
|
|
context_pos = old_context_pos;
|
|
context_ability = old_context_ability;
|
|
}
|
|
}
|
|
|
|
Weapon* Human::TakeonWeapon(const mt::Equip* equip_meta)
|
|
{
|
|
if (equip_meta->equip_type() != EQUIP_TYPE_WEAPON) {
|
|
return nullptr;
|
|
}
|
|
if (equip_meta->equip_subtype() == 1) {
|
|
return nullptr;
|
|
}
|
|
|
|
Weapon* weapon = nullptr;
|
|
for (int idx = GUN_SLOT1; idx <= GUN_SLOT2; ++idx) {
|
|
if (weapons[idx].weapon_id == 0) {
|
|
weapon = &weapons[idx];
|
|
weapon->weapon_idx = idx;
|
|
break;
|
|
}
|
|
}
|
|
return weapon;
|
|
}
|
|
|
|
void Human::StartRefreshViewTimer()
|
|
{
|
|
if (!refresh_view_timer_.expired()) {
|
|
return;
|
|
}
|
|
refresh_view_timer_ = room->xtimer.SetIntervalWpEx
|
|
(
|
|
SERVER_FRAME_RATE * mt::Param::s().refresh_view_time,
|
|
[this] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
UpdateViewObjects();
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
}
|
|
|
|
void Human::ProcLootBag(AddItemDTO& dto)
|
|
{
|
|
const mt::Equip* old_item_meta = mt::Equip::GetById(backpack);
|
|
if (old_item_meta) {
|
|
if (old_item_meta->equip_lv() >= dto.item_meta->equip_lv()) {
|
|
return;
|
|
}
|
|
room->DropItem(GetPos(), old_item_meta->id(), 1, 1);
|
|
}
|
|
backpack = dto.item_meta->id();
|
|
RecalcVolume();
|
|
dto.handled = true;
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
|
|
void Human::ProcLootProtection(AddItemDTO& dto)
|
|
{
|
|
if (dto.item_meta->equip_subtype() == 1) {
|
|
//盔甲
|
|
const mt::Equip* old_item_meta = mt::Equip::GetById(chest);
|
|
if (old_item_meta) {
|
|
if (old_item_meta->equip_lv() >= dto.item_meta->equip_lv()) {
|
|
return;
|
|
}
|
|
room->DropItem(GetPos(), old_item_meta->id(), 1, 1);
|
|
}
|
|
chest = dto.item_meta->id();
|
|
RecalcBaseAttr();
|
|
} else if (dto.item_meta->equip_subtype() == 2) {
|
|
//头盔
|
|
const mt::Equip* old_item_meta = mt::Equip::GetById(helmet);
|
|
if (old_item_meta) {
|
|
if (old_item_meta->equip_lv() >= dto.item_meta->equip_lv()) {
|
|
return;
|
|
}
|
|
room->DropItem(GetPos(), old_item_meta->id(), 1, 1);
|
|
}
|
|
helmet = dto.item_meta->id();
|
|
RecalcBaseAttr();
|
|
}
|
|
dto.handled = true;
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
|
|
void Human::ProcLootSpecItem(AddItemDTO& dto)
|
|
{
|
|
if (dto.item_meta->_inventory_slot() >= 0 &&
|
|
dto.item_meta->_inventory_slot() < IS_END) {
|
|
if (GetInventory(dto.item_meta->_inventory_slot()) >=
|
|
GetVolume(dto.item_meta->_inventory_slot())
|
|
) {
|
|
return;
|
|
}
|
|
int add_num = GetVolume(dto.item_meta->_inventory_slot()) -
|
|
GetInventory(dto.item_meta->_inventory_slot());
|
|
add_num = std::min(dto.count, add_num);
|
|
|
|
AddInventory(dto.item_meta->_inventory_slot(), add_num);
|
|
switch (dto.item_meta->_inventory_slot()) {
|
|
case IS_FRAG:
|
|
case IS_SMOKE:
|
|
{
|
|
Weapon* weapon = &weapons[SPEC1_SLOT_BEGIN +
|
|
(dto.item_meta->_inventory_slot() - SPEC1_IS_BEGIN)
|
|
];
|
|
weapon->weapon_id = dto.item_id;
|
|
weapon->ammo += dto.count;
|
|
weapon->meta = dto.item_meta;
|
|
weapon->Recalc();
|
|
#if 0
|
|
DecInventory(dto.item_meta->_inventory_slot(), add_num);
|
|
#endif
|
|
}
|
|
break;
|
|
case IS_1XSCOPE:
|
|
case IS_2XSCOPE:
|
|
case IS_4XSCOPE:
|
|
case IS_8XSCOPE:
|
|
case IS_15XSCOPE:
|
|
{
|
|
if (dto.item_meta->_inventory_slot() - IS_1XSCOPE > curr_scope_idx) {
|
|
curr_scope_idx = dto.item_meta->_inventory_slot() - IS_1XSCOPE;
|
|
}
|
|
}
|
|
break;
|
|
case IS_POSION_GAS_BOMB:
|
|
case IS_MOLOTOR_COCKTAIL:
|
|
case IS_TRAP:
|
|
case IS_MINE:
|
|
{
|
|
Weapon* weapon = &weapons[SPEC2_SLOT_BEGIN +
|
|
(dto.item_meta->_inventory_slot() - SPEC2_IS_BEGIN)
|
|
];
|
|
weapon->weapon_id = dto.item_id;
|
|
weapon->ammo += dto.count;
|
|
weapon->meta = dto.item_meta;
|
|
weapon->Recalc();
|
|
#if 0
|
|
DecInventory(dto.item_meta->_inventory_slot(), add_num);
|
|
#endif
|
|
}
|
|
break;
|
|
case IS_C4:
|
|
case IS_SHIELD_WALL:
|
|
case IS_SINGAL_GUN:
|
|
case IS_OIL_BUCKET:
|
|
{
|
|
Weapon* weapon = &weapons[SPEC3_SLOT_BEGIN +
|
|
(dto.item_meta->_inventory_slot() - SPEC3_IS_BEGIN)
|
|
];
|
|
weapon->weapon_id = dto.item_id;
|
|
weapon->ammo += dto.count;
|
|
weapon->meta = dto.item_meta;
|
|
weapon->Recalc();
|
|
#if 0
|
|
DecInventory(dto.item_meta->_inventory_slot(), add_num);
|
|
#endif
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
int i = 0;
|
|
#if 0
|
|
A8_ABORT();
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
if (add_num < dto.count) {
|
|
//刷新数量
|
|
Entity* entity = room->GetEntityByUniId(dto.uniid);
|
|
if (entity && entity->IsEntityType(ET_Loot)) {
|
|
((Loot*)entity)->count -= add_num;
|
|
((Loot*)entity)->BroadcastFullState(room);
|
|
}
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
if (action_type == AT_None) {
|
|
AutoLoadingBullet();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (action_type == AT_None) {
|
|
AutoLoadingBullet();
|
|
}
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
dto.handled = true;
|
|
}
|
|
|
|
void Human::ProcGiftPackage(AddItemDTO& dto)
|
|
{
|
|
if (dto.item_meta->equip_type() == EQUIP_TYPE_GIFT_PACKAGE) {
|
|
const mt::Drop* drop_meta = mt::Drop::GetById(dto.item_meta->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);
|
|
auto itr = battlein_items.find(item_id);
|
|
if (itr != battlein_items.end()) {
|
|
itr->second += item_num;
|
|
} else {
|
|
battlein_items[item_id] = item_num;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
dto.handled = true;
|
|
}
|
|
|
|
void Human::ProcLootWeapon(AddItemDTO& dto)
|
|
{
|
|
//装备
|
|
if (dto.item_meta->equip_subtype() == 1) {
|
|
//近战
|
|
if (default_weapon.weapon_id != weapons[0].weapon_id) {
|
|
return;
|
|
} else {
|
|
weapons[0].weapon_idx = 0;
|
|
weapons[0].weapon_id = dto.item_id;
|
|
weapons[0].ammo = 0;
|
|
weapons[0].meta = dto.item_meta;
|
|
weapons[0].Recalc();
|
|
}
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
} else {
|
|
if (FreezeOperate()) {
|
|
return;
|
|
}
|
|
bool switch_gun = false;
|
|
Weapon* weapon = TakeonWeapon(dto.item_meta);
|
|
if (weapon && GetCurrWeapon() != weapon) {
|
|
if (GetCurrWeapon()->weapon_idx == 0) {
|
|
switch_gun = true;
|
|
}
|
|
}
|
|
if (!weapon) {
|
|
return;
|
|
}
|
|
weapon->weapon_id = dto.item_id;
|
|
weapon->ammo = 0;
|
|
weapon->meta = dto.item_meta;
|
|
weapon->Recalc();
|
|
if (HasBuffEffect(kBET_Car)) {
|
|
} else {
|
|
AutoLoadingBullet();
|
|
}
|
|
if (switch_gun) {
|
|
SetCurrWeapon(weapon);
|
|
}
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
dto.handled = true;
|
|
}
|
|
|
|
void Human::LootInteraction(Loot* entity)
|
|
{
|
|
#ifdef DEBUG
|
|
a8::XPrintf("LootInteraction start %d:%d\n", {entity->GetUniId(), entity->item_id});
|
|
#endif
|
|
if (entity->pickuped ||
|
|
entity->count <= 0) {
|
|
#ifdef DEBUG
|
|
a8::XPrintf("LootInteraction error1 %d:%d\n", {entity->GetUniId(), entity->item_id});
|
|
#endif
|
|
return;
|
|
}
|
|
AddItemDTO dto;
|
|
dto.uniid = entity->GetUniId();
|
|
dto.item_id = entity->item_id;
|
|
dto.count = entity->count;
|
|
dto.item_level = entity->item_level;
|
|
dto.handled = false;
|
|
dto.item_meta = mt::Equip::GetById(entity->item_id);
|
|
ProcAddItemDto(dto);
|
|
|
|
if (dto.handled) {
|
|
entity->pickuped = true;
|
|
room->RemoveObjectLater(entity);
|
|
}
|
|
}
|
|
|
|
void Human::ProcAddItemDto(AddItemDTO& dto)
|
|
{
|
|
if (!dto.item_meta) {
|
|
#ifdef DEBUG
|
|
a8::XPrintf("LootInteraction error20 %d:%d\n", {dto.uniid, dto.item_id});
|
|
#endif
|
|
return;
|
|
}
|
|
switch (dto.item_meta->equip_type()) {
|
|
case EQUIP_TYPE_WEAPON:
|
|
{
|
|
ProcLootWeapon(dto);
|
|
}
|
|
break;
|
|
case EQUIP_TYPE_PROTECTION:
|
|
{
|
|
ProcLootProtection(dto);
|
|
}
|
|
break;
|
|
case EQUIP_TYPE_BAG:
|
|
{
|
|
ProcLootBag(dto);
|
|
}
|
|
break;
|
|
case EQUIP_TYPE_OLDSKIN:
|
|
{
|
|
A8_ABORT();
|
|
}
|
|
break;
|
|
case EQUIP_TYPE_SKIN:
|
|
{
|
|
ProcLootSkin(dto);
|
|
}
|
|
break;
|
|
case EQUIP_TYPE_CAR:
|
|
{
|
|
ProcLootCar(dto);
|
|
}
|
|
break;
|
|
case EQUIP_TYPE_SPOILS:
|
|
{
|
|
ProcSpoils(dto);
|
|
}
|
|
break;
|
|
case EQUIP_TYPE_CAMOUFLAGE:
|
|
case EQUIP_TYPE_SINGAL_EMITTER:
|
|
{
|
|
ProcNormalItem(dto);
|
|
}
|
|
break;
|
|
case EQUIP_TYPE_GIFT_PACKAGE:
|
|
{
|
|
ProcGiftPackage(dto);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
ProcLootSpecItem(dto);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Human::DropWeapon(int weapon_idx, int num)
|
|
{
|
|
num = std::max(1, num);
|
|
switch (weapon_idx) {
|
|
case 101:
|
|
case 102:
|
|
case 103:
|
|
{
|
|
int slot_id = -1;
|
|
if (weapon_idx == 101) {
|
|
slot_id = IS_HEALTHKIT;
|
|
} else if (weapon_idx == 102) {
|
|
slot_id = IS_PAIN_KILLER;
|
|
} else if (weapon_idx == 103) {
|
|
slot_id = IS_SHEN_BAO;
|
|
}
|
|
if (slot_id >= 0 && GetInventory(slot_id) > 0) {
|
|
const mt::Equip* item_meta = mt::Equip::GetByIdBySlotId(slot_id);
|
|
if (item_meta) {
|
|
num = std::max(1, GetInventory(slot_id));
|
|
DecInventory(slot_id, num);
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
|
|
glm::vec3 drop_dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(drop_dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.AddGlmVec3(drop_dir * (25.0f + rand() % 50));
|
|
room->DropItem(drop_pos, item_meta->id(), num, 1);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
{
|
|
|
|
}
|
|
break;
|
|
}
|
|
if (weapon_idx < 0 ||
|
|
weapon_idx >= weapons.size()) {
|
|
return;
|
|
}
|
|
bool drop_ok = false;
|
|
Weapon* weapon = &weapons[weapon_idx];
|
|
int weapon_id = weapon->weapon_id;
|
|
int weapon_ammo = weapon->ammo;
|
|
const mt::Equip* weapon_meta = weapon->meta;
|
|
if (weapon->weapon_id != 0) {
|
|
switch (weapon->weapon_idx) {
|
|
case GUN_SLOT0:
|
|
{
|
|
if (weapon->weapon_id != default_weapon.weapon_id) {
|
|
drop_ok = true;
|
|
*weapon = default_weapon;
|
|
}
|
|
}
|
|
break;
|
|
case GUN_SLOT1:
|
|
{
|
|
drop_ok = true;
|
|
*weapon = Weapon();
|
|
weapon->weapon_idx = weapon_idx;
|
|
if (GetCurrWeapon() == weapon) {
|
|
if (weapons[GUN_SLOT2].weapon_id != 0) {
|
|
SetCurrWeapon(&weapons[GUN_SLOT2]);
|
|
} else {
|
|
SetCurrWeapon(&weapons[0]);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case GUN_SLOT2:
|
|
{
|
|
drop_ok = true;
|
|
*weapon = Weapon();
|
|
weapon->weapon_idx = weapon_idx;
|
|
if (GetCurrWeapon() == weapon) {
|
|
if (weapons[GUN_SLOT1].weapon_id != 0) {
|
|
SetCurrWeapon(&weapons[GUN_SLOT1]);
|
|
} else {
|
|
SetCurrWeapon(&weapons[0]);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case FRAG_SLOT:
|
|
{
|
|
weapon_ammo = std::min(weapon->ammo, num);
|
|
if (weapon_ammo > 0) {
|
|
drop_ok = true;
|
|
weapon->ammo = std::max(0, weapon->ammo - weapon_ammo);
|
|
int slot_id = weapon->meta->_inventory_slot();
|
|
DecInventory(slot_id, weapon_ammo);
|
|
if (weapon->ammo <= 0) {
|
|
*weapon = Weapon();
|
|
weapon->weapon_idx = weapon_idx;
|
|
if (GetCurrWeapon() == weapon) {
|
|
if (weapons[GUN_SLOT1].weapon_id != 0) {
|
|
SetCurrWeapon(&weapons[GUN_SLOT1]);
|
|
} else if (weapons[GUN_SLOT2].weapon_id != 0) {
|
|
SetCurrWeapon(&weapons[GUN_SLOT2]);
|
|
} else {
|
|
SetCurrWeapon(&weapons[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SMOKE_SLOT:
|
|
{
|
|
weapon_ammo = std::min(weapon->ammo, num);
|
|
if (weapon_ammo > 0) {
|
|
drop_ok = true;
|
|
weapon->ammo = std::max(0, weapon->ammo - weapon_ammo);
|
|
int slot_id = weapon->meta->_inventory_slot();
|
|
DecInventory(slot_id, weapon_ammo);
|
|
if (weapon->ammo <= 0) {
|
|
*weapon = Weapon();
|
|
weapon->weapon_idx = weapon_idx;
|
|
if (GetCurrWeapon() == weapon) {
|
|
if (weapons[GUN_SLOT1].weapon_id != 0) {
|
|
SetCurrWeapon(&weapons[GUN_SLOT1]);
|
|
} else if (weapons[GUN_SLOT2].weapon_id != 0) {
|
|
SetCurrWeapon(&weapons[GUN_SLOT2]);
|
|
} else {
|
|
SetCurrWeapon(&weapons[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
weapon_ammo = std::min(weapon->ammo, num);
|
|
if (weapon_ammo > 0) {
|
|
drop_ok = true;
|
|
weapon->ammo = std::max(0, weapon->ammo - weapon_ammo);
|
|
int slot_id = weapon->meta->_inventory_slot();
|
|
DecInventory(slot_id, weapon_ammo);
|
|
if (weapon->ammo <= 0) {
|
|
*weapon = Weapon();
|
|
weapon->weapon_idx = weapon_idx;
|
|
if (GetCurrWeapon() == weapon) {
|
|
Weapon* next_weapon = ChooseNextSpecWeapon(weapon_idx);
|
|
if (!next_weapon) {
|
|
next_weapon = AutoChgWeapon();
|
|
}
|
|
if (next_weapon) {
|
|
SetCurrWeapon(next_weapon);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (drop_ok) {
|
|
bool create_loot = true;
|
|
if (weapon_ammo > 0) {
|
|
const mt::Equip* bullet_meta = mt::Equip::GetById(weapon_meta->use_bullet());
|
|
if (bullet_meta && bullet_meta->_inventory_slot() > 0) {
|
|
#if 1
|
|
int drop_num = weapon_ammo;
|
|
#else
|
|
int volume = GetVolume(bullet_meta->_inventory_slot());
|
|
int inventory = GetInventory(bullet_meta->_inventory_slot());
|
|
int add_inventory = std::min(weapon_ammo, volume - std::min(volume, inventory));
|
|
if (add_inventory > 0 &&
|
|
!(weapon_idx == FRAG_SLOT || weapon_idx == SMOKE_SLOT)) {
|
|
AddInventory(bullet_meta->_inventory_slot(), add_inventory);
|
|
}
|
|
int drop_num = weapon_ammo - add_inventory;
|
|
#endif
|
|
if (drop_num > 0) {
|
|
glm::vec3 drop_dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(drop_dir, a8::RandAngle());
|
|
Position drop_pos = GetPos();
|
|
drop_pos.FromGlmVec3(GetPos().ToGlmVec3() + drop_dir * (25.0f + rand() % 50));
|
|
#if 1
|
|
{
|
|
room->DropItem(drop_pos, bullet_meta->id(), drop_num, 1);
|
|
create_loot = false;
|
|
}
|
|
#else
|
|
if (bullet_meta->_inventory_slot() == IS_FRAG ||
|
|
bullet_meta->_inventory_slot() == IS_SMOKE) {
|
|
//只有手雷和烟雾弹会掉落
|
|
room->DropItem(drop_pos, bullet_meta->id(), drop_num, 1);
|
|
create_loot = false;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
if (create_loot ||
|
|
weapon_idx == GUN_SLOT0 ||
|
|
weapon_idx == GUN_SLOT1 ||
|
|
weapon_idx == GUN_SLOT2) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, a8::RandAngle());
|
|
Position pos = GetPos();
|
|
pos.FromGlmVec3(GetPos().ToGlmVec3() + dir * (40.0f + rand() % 50));
|
|
room->CreateLoot(weapon_id, pos, 1, 1);
|
|
}
|
|
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::DoFollow(int target_id)
|
|
{
|
|
if (target_id == 0){
|
|
if (follow_target.Get()) {
|
|
follow_target.Detach();
|
|
room->frame_event.AddPropChg(GetWeakPtrRef(), kPropFollowTarget, 0, 0, true);
|
|
if (!follow_target_timer_.expired()) {
|
|
room->xtimer.Delete(follow_target_timer_);
|
|
}
|
|
}
|
|
} else {
|
|
if (GetTeam()) {
|
|
Human* member = GetTeam()->GetMemberByUniId(target_id);
|
|
if (member && member->CanFollow(this)) {
|
|
#ifdef DEBUG
|
|
a8::XPrintf("DoFollow %d->%d\n", {GetUniId(), member->GetUniId()});
|
|
#endif
|
|
if (follow_target.Get()) {
|
|
follow_target.Detach();
|
|
if (!follow_target_timer_.expired()) {
|
|
room->xtimer.Delete(follow_target_timer_);
|
|
}
|
|
}
|
|
follow_target_last_chg_move_dir_times_ = -1;
|
|
follow_target.Attach(member);
|
|
room->frame_event.AddPropChg(GetWeakPtrRef(), kPropFollowTarget, 0, target_id, true);
|
|
follow_target_timer_ = room->xtimer.SetIntervalWpEx
|
|
(
|
|
400 / FRAME_RATE_MS,
|
|
[this] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
if (!follow_target.Get()) {
|
|
if (IsPlayer()) {
|
|
AsPlayer()->moving = false;
|
|
AsPlayer()->moved_frames = 0;
|
|
}
|
|
room->xtimer.DeleteCurrentTimer();
|
|
return;
|
|
}
|
|
if (room->GetGasData().GetGasMode() != GasInactive &&
|
|
!(follow_target.Get()->HasBuffEffect(kBET_Fly) ||
|
|
follow_target.Get()->HasBuffEffect(kBET_Jump))
|
|
){
|
|
if (IsPlayer()) {
|
|
AsPlayer()->moving = false;
|
|
AsPlayer()->moved_frames = 0;
|
|
}
|
|
follow_target.Detach();
|
|
room->xtimer.DeleteCurrentTimer();
|
|
return;
|
|
}
|
|
|
|
if (follow_target.Get()->HasBuffEffect(kBET_Jump)) {
|
|
if (HasBuffEffect(kBET_Jump)) {
|
|
FollowToTarget();
|
|
} else {
|
|
DoJump();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
}
|
|
}
|
|
}
|
|
IncFollowTimes();
|
|
}
|
|
|
|
void Human::DoDive()
|
|
{
|
|
if (!HasBuffEffect(kBET_InWater)) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Dive)) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Driver) ||
|
|
HasBuffEffect(kBET_Passenger)) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Become)) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Camouflage) ) {
|
|
RemoveBuffByEffectId(kBET_Camouflage);
|
|
}
|
|
MustBeAddBuff(this, kDiveBuffId);
|
|
}
|
|
|
|
void Human::OnWatcherDie(Human* watcher)
|
|
{
|
|
if (follow_target_ == watcher) {
|
|
if (socket_handle != 0 && IsPlayer()) {
|
|
((Player*)this)->AsyncRequestWatchWar(true);
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
A8_ABORT();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Human::TraverseObservers(std::function<void (Human*, bool&)> func)
|
|
{
|
|
bool stop;
|
|
for (auto& observer : observers_) {
|
|
func(observer, stop);
|
|
if (stop) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Human::AddOxygen(int val)
|
|
{
|
|
oxygen_ += val;
|
|
oxygen_ = std::min(mt::Param::s().dive_oxygen_total, oxygen_);
|
|
}
|
|
|
|
void Human::DecOxygen(int val)
|
|
{
|
|
oxygen_ -= val;
|
|
oxygen_ = std::max(0, oxygen_);
|
|
}
|
|
|
|
void Human::WinPveScore(int score)
|
|
{
|
|
stats.pve_rank_score += score;
|
|
}
|
|
|
|
void Human::InternalBeKill(int killer_id, const std::string& killer_name, int weapon_id,
|
|
int real_killer_id, const std::string& real_killer_name)
|
|
{
|
|
#ifdef DEBUG
|
|
if (IsPlayer()) {
|
|
a8::XPrintf("BeKill %d\n", {GetUniId()});
|
|
}
|
|
#endif
|
|
if (!dead && !room->IsGameOver() && !real_dead) {
|
|
KillInfo info;
|
|
{
|
|
info.killer_id = killer_id;
|
|
info.killer_name = killer_name;
|
|
info.weapon_id = weapon_id;
|
|
info.real_killer_id = real_killer_id;
|
|
info.real_killer_name = real_killer_name;
|
|
}
|
|
|
|
{
|
|
++stats.dead_times;
|
|
stats.killer_id = real_killer_id;
|
|
stats.killer_name = real_killer_name;
|
|
stats.weapon_id = weapon_id;
|
|
}
|
|
{
|
|
Entity* killer = room->GetEntityByUniId(real_killer_id);
|
|
if (killer && killer->IsCreature(room)) {
|
|
((Creature*)killer)->GetTrigger()->Kill(this, weapon_id);
|
|
}
|
|
}
|
|
if (room->IsPveRoom()) {
|
|
dead = true;
|
|
real_dead = false;
|
|
downed = false;
|
|
SetHP(0.0f);
|
|
if (real_killer_id != GetUniId() && GetTeam() && GetTeam()->HasReviveCoin(this)) {
|
|
real_dead = true;
|
|
dead_frameno = room->GetFrameNo();
|
|
GetTrigger()->Die(killer_id, weapon_id);
|
|
if (real_dead) {
|
|
real_dead = false;
|
|
OnDie();
|
|
KillMgr::Instance()->OnHumanDead(this, &info);
|
|
room->frame_event.AddDead(GetWeakPtrRef(), 1000 * mt::Param::s().revive_time);
|
|
dead_timer = room->xtimer.SetTimeoutWpEx
|
|
(mt::Param::s().revive_time * SERVER_FRAME_RATE,
|
|
[this] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
real_dead = true;
|
|
SendGameOver();
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
} else {
|
|
OnDie();
|
|
KillMgr::Instance()->OnHumanDead(this, &info);
|
|
room->frame_event.AddDead(GetWeakPtrRef(), 0);
|
|
}
|
|
} else {
|
|
real_dead = true;
|
|
dead_frameno = room->GetFrameNo();
|
|
GetTrigger()->Die(killer_id, weapon_id);
|
|
OnDie();
|
|
KillMgr::Instance()->OnHumanDead(this, &info);
|
|
room->frame_event.AddDead(GetWeakPtrRef(), 0);
|
|
}
|
|
} else {
|
|
dead = true;
|
|
real_dead = true;
|
|
downed = false;
|
|
SetHP(0.0f);
|
|
dead_frameno = room->GetFrameNo();
|
|
GetTrigger()->Die(killer_id, weapon_id);
|
|
OnDie();
|
|
KillMgr::Instance()->OnHumanDead(this, &info);
|
|
room->frame_event.AddDead(GetWeakPtrRef(), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
int Human::GetTeamMode()
|
|
{
|
|
return GetTeam()->GetMemberNum() <= 1 ? 0 : 1;
|
|
}
|
|
|
|
void Human::CalcAssists(Human* target)
|
|
{
|
|
if (GetTeam() && GetTeam()->GetMemberNum() > 1) {
|
|
GetTeam()->TraverseMembers
|
|
(
|
|
[target, this] (Human* hum)
|
|
{
|
|
if (hum->GetUniId() == this->GetUniId()) {
|
|
return true;
|
|
}
|
|
int assist_time = mt::Param::GetIntParam("assist_time", 5);
|
|
auto itr = target->attacker_hash_.find(hum->GetUniId());
|
|
if (itr != target->attacker_hash_.end()) {
|
|
if (hum->room->GetFrameNo() - itr->second <
|
|
SERVER_FRAME_RATE * assist_time) {
|
|
++hum->stats.assist;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
}
|