499 lines
14 KiB
C++
499 lines
14 KiB
C++
#include "precompile.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "car.h"
|
|
#include "collider.h"
|
|
#include "human.h"
|
|
#include "room.h"
|
|
#include "metamgr.h"
|
|
#include "loot.h"
|
|
#include "perfmonitor.h"
|
|
#include "typeconvert.h"
|
|
#include "bullet.h"
|
|
#include "explosion.h"
|
|
#include "obstacle.h"
|
|
#include "ability.h"
|
|
|
|
Car::Car():Creature()
|
|
{
|
|
++PerfMonitor::Instance()->entity_num[ET_Car];
|
|
}
|
|
|
|
Car::~Car()
|
|
{
|
|
--PerfMonitor::Instance()->entity_num[ET_Car];
|
|
}
|
|
|
|
void Car::Initialize()
|
|
{
|
|
Creature::Initialize();
|
|
hero_meta_ = MetaMgr::Instance()->GetPlayer(meta->i->heroid());
|
|
if (!hero_meta_) {
|
|
abort();
|
|
}
|
|
RecalcSelfCollider();
|
|
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(hero_meta_->i->default_weapon());
|
|
if (weapon_meta) {
|
|
weapons[GUN_SLOT1].weapon_idx = GUN_SLOT1;
|
|
weapons[GUN_SLOT1].weapon_id = weapon_meta->i->id();
|
|
weapons[GUN_SLOT1].weapon_lv = 1;
|
|
weapons[GUN_SLOT1].meta = weapon_meta;
|
|
weapons[GUN_SLOT1].Recalc();
|
|
weapons[GUN_SLOT1].ammo = weapons[GUN_SLOT1].GetClipVolume();
|
|
SetCurrWeapon(&weapons[GUN_SLOT1]);
|
|
}
|
|
born_frameno_ = room->GetFrameNo();
|
|
SetDef(hero_meta_->i->def());
|
|
SetHP(hero_meta_->i->health());
|
|
SetMaxHP(std::max(GetHP(), GetMaxHP()));
|
|
TryAddBuff(this, meta->car_deactive_buff_id);
|
|
cur_oil_ = meta->i->max_oil();
|
|
}
|
|
|
|
void Car::FillMFObjectPart(Room* room, Human* hum, cs::MFObjectPart* part_data)
|
|
{
|
|
part_data->set_object_type(ET_Car);
|
|
cs::MFCarPart* p = part_data->mutable_union_obj_11();
|
|
p->set_obj_uniid(GetUniId());
|
|
TypeConvert::ToPb(GetPos(), p->mutable_pos());
|
|
TypeConvert::ToPb(GetAttackDir(), p->mutable_dir());
|
|
}
|
|
|
|
void Car::FillMFObjectFull(Room* room, Human* hum, cs::MFObjectFull* full_data)
|
|
{
|
|
full_data->set_object_type(ET_Car);
|
|
cs::MFCarFull* p = full_data->mutable_union_obj_11();
|
|
p->set_obj_uniid(GetUniId());
|
|
TypeConvert::ToPb(GetPos(), p->mutable_pos());
|
|
TypeConvert::ToPb(GetAttackDir(), p->mutable_dir());
|
|
p->set_car_id(meta->i->id());
|
|
p->set_heroid(meta->i->heroid());
|
|
p->set_driver(driver_ ? driver_->GetUniId() : 0);
|
|
for (auto hum : passengers_) {
|
|
auto less_data = p->add_passengers();
|
|
hum->FillMFObjectLess(room, hum, less_data);
|
|
}
|
|
p->set_born_frameno(ceil(born_frameno_ / 2.0));
|
|
p->set_dead(dead);
|
|
p->set_health(GetHP());
|
|
p->set_max_health(GetMaxHP());
|
|
p->set_oil(cur_oil_);
|
|
p->set_max_oil(meta->i->max_oil());
|
|
p->set_bullet_num(weapons[GUN_SLOT1].ammo);
|
|
p->set_seat_num(meta->int_param2);
|
|
FillBuffList(hum, p->mutable_buff_list());
|
|
}
|
|
|
|
Human* Car::GetPassengerBySeat(int seat)
|
|
{
|
|
for (auto hum : passengers_) {
|
|
if (hum->GetSeat() == seat) {
|
|
return hum;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void Car::GetDown(Human* passenger)
|
|
{
|
|
if (later_removed_) {
|
|
return;
|
|
}
|
|
if (passengers_.find(passenger) == passengers_.end()) {
|
|
return;
|
|
}
|
|
if (driver_ == passenger) {
|
|
driver_ = nullptr;
|
|
}
|
|
passengers_.erase(passenger);
|
|
passenger->SetCar(nullptr);
|
|
passenger->SetSeat(0);
|
|
passenger->second_weapon = Weapon();
|
|
passenger->CancelAction();
|
|
passenger->RemoveBuffByEffectId(kBET_Driver);
|
|
passenger->RemoveBuffByEffectId(kBET_Passenger);
|
|
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
|
|
if (passengers_.empty()) {
|
|
team_id = 0;
|
|
room->TakeOffCarObject(GetUniId(), GetPos());
|
|
RemoveBuffByEffectId(kBET_CarActive);
|
|
TryAddBuff(this, meta->car_deactive_buff_id);
|
|
if (meta->i->buffid() != 0) {
|
|
passenger->RemoveBuffById(meta->i->buffid());
|
|
}
|
|
}
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
room->NotifyUiUpdate();
|
|
}
|
|
|
|
void Car::GetOn(Human* passenger)
|
|
{
|
|
if (later_removed_) {
|
|
return;
|
|
}
|
|
if (meta->int_param2 <= 0) {
|
|
abort();
|
|
}
|
|
if (meta->int_param2 <= passengers_.size()) {
|
|
return;
|
|
}
|
|
if (passengers_.find(passenger) != passengers_.end()) {
|
|
return;
|
|
}
|
|
if (passenger->GetCar()) {
|
|
return;
|
|
}
|
|
for (auto hum : passengers_) {
|
|
if (hum->team_id != passenger->team_id) {
|
|
return;
|
|
}
|
|
}
|
|
if (passengers_.empty()) {
|
|
team_id = passenger->team_id;
|
|
}
|
|
int seat = AllocSeat();
|
|
if (seat < 0) {
|
|
return;
|
|
}
|
|
passengers_.insert(passenger);
|
|
if (!driver_) {
|
|
driver_ = passenger;
|
|
driver_->SetAttackDir(a8::Vec2::RIGHT);
|
|
SetAttackDir(driver_->GetAttackDir());
|
|
room->TakeOnCarObject(GetUniId());
|
|
}
|
|
passenger->SetCar(this);
|
|
passenger->SetSeat(seat);
|
|
passenger->SetPos(GetPos());
|
|
{
|
|
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff
|
|
(driver_ == passenger ? DRIVER_BUFFID : PASSENGER_BUFFID);
|
|
if (buff_meta) {
|
|
passenger->AddBuff(passenger, buff_meta, 1);
|
|
}
|
|
}
|
|
if (meta->i->buffid() != 0) {
|
|
passenger->TryAddBuff(passenger, meta->i->buffid());
|
|
}
|
|
passenger->CancelAction();
|
|
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
|
|
if (passengers_.size() == 1) {
|
|
RemoveBuffByEffectId(kBET_CarDeactive);
|
|
TryAddBuff(this, meta->car_active_buff_id);
|
|
}
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
room->NotifyUiUpdate();
|
|
}
|
|
|
|
void Car::SwitchSeat(Human* passenger, int seat)
|
|
{
|
|
if (later_removed_) {
|
|
return;
|
|
}
|
|
if (seat < 0 || seat >= meta->int_param2) {
|
|
return;
|
|
}
|
|
if (!IsPassenger(passenger)) {
|
|
return;
|
|
}
|
|
if (GetPassengerBySeat(seat)) {
|
|
return;
|
|
}
|
|
|
|
if (seat == 0) {
|
|
passenger->RemoveBuffByEffectId(kBET_Passenger);
|
|
passenger->SetSeat(seat);
|
|
driver_ = passenger;
|
|
passenger->MustBeAddBuff(passenger, DRIVER_BUFFID);
|
|
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
} else {
|
|
passenger->SetSeat(seat);
|
|
if (driver_ == passenger) {
|
|
driver_ = nullptr;
|
|
passenger->RemoveBuffByEffectId(kBET_Driver);
|
|
passenger->MustBeAddBuff(passenger, PASSENGER_BUFFID);
|
|
} else {
|
|
Buff* buff = passenger->GetBuffByEffectId(kBET_Passenger);
|
|
buff->CalcPassengerShotOffset();
|
|
passenger->need_sync_active_player = true;
|
|
}
|
|
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
|
|
SyncAroundPlayers(__FILE__, __LINE__, __func__);
|
|
}
|
|
}
|
|
|
|
bool Car::CanShot(Human* passenger)
|
|
{
|
|
if (!IsPassenger(passenger)) {
|
|
return false;
|
|
}
|
|
return passenger->GetSeat() < meta->shoot_offsets.size() &&
|
|
std::get<0>(meta->shoot_offsets[passenger->GetSeat()]);
|
|
}
|
|
|
|
int Car::AllocSeat()
|
|
{
|
|
int seat = -1;
|
|
for (int i = 0; i < meta->int_param2; ++i) {
|
|
bool found = false;
|
|
for (auto hum : passengers_) {
|
|
if (hum->GetSeat() == i) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
seat = i;
|
|
break;
|
|
}
|
|
}
|
|
return seat;
|
|
}
|
|
|
|
void Car::SyncPos()
|
|
{
|
|
if (driver_) {
|
|
SetPos(driver_->GetPos());
|
|
SetMoveDir(driver_->GetMoveDir());
|
|
for (auto hum : passengers_) {
|
|
if (hum != driver_) {
|
|
hum->SetPos(GetPos());
|
|
hum->SetMoveDir(GetMoveDir());
|
|
room->grid_service->MoveCreature(hum);
|
|
}
|
|
}
|
|
room->grid_service->MoveCreature(this);
|
|
}
|
|
}
|
|
|
|
float Car::GetRadius()
|
|
{
|
|
return hero_meta_->i->radius();
|
|
}
|
|
|
|
float Car::GetSpeed()
|
|
{
|
|
float speed = hero_meta_->i->move_speed();
|
|
speed *= 1 + GetAbility()->GetAttrRate(kHAT_Speed);
|
|
return speed;
|
|
}
|
|
|
|
void Car::OnBulletHit(Bullet* bullet)
|
|
{
|
|
if (!IsDead(room)) {
|
|
float dmg = bullet->GetAtk() + bullet->gun_meta->i->atk_mech();
|
|
float def = GetDef() * (1 + GetAbility()->GetAttrRate(kHAT_Def)) +
|
|
GetAbility()->GetAttrAbs(kHAT_Def);
|
|
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
|
|
finaly_dmg = std::max(finaly_dmg, 0.0f);
|
|
if (bullet->meta->buff_meta) {
|
|
MustBeAddBuff(bullet->sender.Get(), bullet->meta->i->buffid());
|
|
}
|
|
if (!bullet->IsPreBattleBullet()) {
|
|
DecHP(finaly_dmg,
|
|
bullet->sender.Get()->GetUniId(),
|
|
bullet->sender.Get()->GetName(),
|
|
bullet->gun_meta->i->id());
|
|
}
|
|
if (bullet->meta->buff_meta) {
|
|
MustBeAddBuff(this, bullet->meta->i->buffid());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Car::OnExplosionHit(Explosion* e)
|
|
{
|
|
if (IsInvincible()) {
|
|
return;
|
|
}
|
|
if (dead) {
|
|
return;
|
|
}
|
|
if (e->IsPreBattleExplosion()) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Jump) ||
|
|
HasBuffEffect(kBET_Fly)) {
|
|
return;
|
|
}
|
|
|
|
float dmg = e->GetDmg();
|
|
float def = GetDef() * (1 + GetAbility()->GetAttrRate(kHAT_Def)) +
|
|
GetAbility()->GetAttrAbs(kHAT_Def);
|
|
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
|
|
finaly_dmg = std::max(finaly_dmg, 0.0f);
|
|
#ifdef DEBUG
|
|
{
|
|
room->BroadcastDebugMsg(a8::Format("explosion dmg:%d def:%d finaly_dmg:%d",
|
|
{dmg,
|
|
def,
|
|
finaly_dmg}));
|
|
}
|
|
#endif
|
|
#if 1
|
|
DecHP(finaly_dmg,
|
|
1,
|
|
"",
|
|
1);
|
|
#else
|
|
DecHP(finaly_dmg,
|
|
sender.Get()->GetUniId(),
|
|
sender.Get()->GetName(),
|
|
gun_meta->i->id());
|
|
#endif
|
|
}
|
|
|
|
void Car::DecHP(float dec_hp, int killer_id, const std::string& killer_name, int weapon_id)
|
|
{
|
|
if (dec_hp < 0.001f) {
|
|
return;
|
|
}
|
|
float old_health = GetHP();
|
|
float new_health = std::max(0.0f, GetHP() - dec_hp);
|
|
SetHP(std::max(0.0f, new_health));
|
|
if (GetHP() <= 0.0001f && !IsDead(room)) {
|
|
BeKill(killer_id, killer_name, weapon_id);
|
|
}
|
|
room->frame_event.AddHpChg(GetWeakPtrRef());
|
|
float hp_rate = GetHP() / GetMaxHP();
|
|
int new_buff_idx = cur_buff_idx_;
|
|
while (new_buff_idx + 1 < meta->car_buff_list.size()) {
|
|
if (hp_rate > std::get<0>(meta->car_buff_list[new_buff_idx + 1])) {
|
|
break;
|
|
}
|
|
++new_buff_idx;
|
|
}
|
|
if (new_buff_idx != cur_buff_idx_) {
|
|
if (cur_buff_id_ != 0) {
|
|
RemoveBuffById(cur_buff_id_);
|
|
}
|
|
cur_buff_idx_ = new_buff_idx;
|
|
cur_buff_id_ = std::get<1>(meta->car_buff_list[cur_buff_idx_]);
|
|
if (cur_buff_id_ != 0) {
|
|
MustBeAddBuff(this, cur_buff_id_);
|
|
#ifdef DEBUG
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void Car::BeKill(int killer_id, const std::string& killer_name, int weapon_id)
|
|
{
|
|
dead = true;
|
|
room->TakeOnCarObject(GetUniId());
|
|
BroadcastDeleteState(room);
|
|
RemoveFromAroundPlayers(room);
|
|
room->grid_service->RemoveCreature(this);
|
|
room->RemoveObjectLater(this);
|
|
int team_id = 0;
|
|
for (Human* passenger : passengers_) {
|
|
team_id = passenger->team_id;
|
|
passenger->SetCar(nullptr);
|
|
passenger->SetSeat(0);
|
|
passenger->second_weapon = Weapon();
|
|
passenger->CancelAction();
|
|
passenger->RemoveBuffByEffectId(kBET_Driver);
|
|
passenger->RemoveBuffByEffectId(kBET_Passenger);
|
|
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
|
|
}
|
|
Explosion explosion;
|
|
explosion.IndifferenceAttack(
|
|
room,
|
|
GetPos(),
|
|
meta->i->explosion_range(),
|
|
meta->i->explosion_effect(),
|
|
meta->i->atk()
|
|
);
|
|
room->NotifyUiUpdate();
|
|
}
|
|
|
|
void Car::GetAabbBox(AabbCollider& aabb_box)
|
|
{
|
|
aabb_box.active = true;
|
|
aabb_box.owner = this;
|
|
aabb_box._min.x = -hero_meta_->i->radius();
|
|
aabb_box._min.y = -hero_meta_->i->radius();
|
|
aabb_box._max.x = hero_meta_->i->radius();
|
|
aabb_box._max.y = hero_meta_->i->radius();
|
|
}
|
|
|
|
void Car::GetHitAabbBox(AabbCollider& aabb_box)
|
|
{
|
|
aabb_box.active = true;
|
|
aabb_box.owner = this;
|
|
aabb_box._min.x = -hero_meta_->i->hit_radius();
|
|
aabb_box._min.y = -hero_meta_->i->hit_radius();
|
|
aabb_box._max.x = hero_meta_->i->hit_radius();
|
|
aabb_box._max.y = hero_meta_->i->hit_radius();
|
|
aabb_box.MoveCenter(hero_meta_->i->hit_offset_x(), hero_meta_->i->hit_offset_y());
|
|
}
|
|
|
|
void Car::SendDebugMsg(const std::string& debug_msg)
|
|
{
|
|
#if 1
|
|
room->BroadcastDebugMsg("载具debugmsg: " + debug_msg);
|
|
#else
|
|
for (auto& passenger : passengers_) {
|
|
passenger->SendDebugMsg("载具debugmsg: " + debug_msg);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool Car::IsPassenger(Human* hum)
|
|
{
|
|
return passengers_.find(hum) != passengers_.end();
|
|
}
|
|
|
|
void Car::SetAttackDir(const a8::Vec2& attack_dir)
|
|
{
|
|
Creature::SetAttackDir(attack_dir);
|
|
}
|
|
|
|
void Car::DecOil(float dec_oil)
|
|
{
|
|
dec_oil *= 1 - GetAbility()->GetAttrRate(kHAT_CarOil);
|
|
cur_oil_ -= dec_oil;
|
|
cur_oil_ = std::max(0.0f, cur_oil_);
|
|
if (!HasOil()) {
|
|
if (driver_) {
|
|
driver_->SetAttackDir(a8::Vec2::RIGHT);
|
|
}
|
|
SetAttackDir(a8::Vec2::RIGHT);
|
|
}
|
|
}
|
|
|
|
float Car::GetMaxOil()
|
|
{
|
|
return meta->i->max_oil();
|
|
}
|
|
|
|
void Car::DropItems(Obstacle* obstacle)
|
|
{
|
|
if (obstacle->meta->i->drop() != 0) {
|
|
room->ScatterDrop(obstacle->GetPos(), obstacle->meta->i->drop());
|
|
}
|
|
}
|
|
|
|
void Car::RecalcSelfCollider()
|
|
{
|
|
if (!self_collider_) {
|
|
self_collider_ = new CircleCollider();
|
|
self_collider_->owner = this;
|
|
AddEntityCollider(self_collider_);
|
|
}
|
|
self_collider_->pos = a8::Vec2(hero_meta_->i->move_offset_x(), hero_meta_->i->move_offset_y());
|
|
self_collider_->rad = hero_meta_->i->radius();
|
|
}
|
|
|
|
bool Car::NeedCreatureCollision()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void Car::CheckCreatureCollision()
|
|
{
|
|
|
|
}
|