398 lines
11 KiB
C++
398 lines
11 KiB
C++
#include "precompile.h"
|
|
|
|
#include "hero.h"
|
|
#include "human.h"
|
|
#include "room.h"
|
|
#include "metamgr.h"
|
|
#include "loot.h"
|
|
#include "perfmonitor.h"
|
|
#include "typeconvert.h"
|
|
#include "hero.ai.h"
|
|
#include "mine_sweeper.ai.h"
|
|
#include "obstacle.h"
|
|
#include "collider.h"
|
|
#include "bullet.h"
|
|
#include "explosion.h"
|
|
#include "roomobstacle.h"
|
|
|
|
Hero::Hero():Creature()
|
|
{
|
|
++PerfMonitor::Instance()->entity_num[ET_Hero];
|
|
}
|
|
|
|
Hero::~Hero()
|
|
{
|
|
DetachFromMaster();
|
|
if (ai) {
|
|
A8_SAFE_DELETE(ai);
|
|
}
|
|
--PerfMonitor::Instance()->entity_num[ET_Hero];
|
|
}
|
|
|
|
void Hero::Initialize()
|
|
{
|
|
Creature::Initialize();
|
|
RecalcSelfCollider();
|
|
SetHP(meta->i->health());
|
|
SetMaxHP(GetHP());
|
|
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(meta->i->default_weapon());
|
|
if (weapon_meta) {
|
|
Weapon& weapon = weapons[GUN_SLOT1];
|
|
weapon.weapon_idx = GUN_SLOT1;
|
|
weapon.weapon_id = weapon_meta->i->id();
|
|
weapon.weapon_lv = 1;
|
|
weapon.ammo = 10000;
|
|
weapon.meta = weapon_meta;
|
|
weapon.Recalc();
|
|
SetCurrWeapon(&weapon);
|
|
}
|
|
SetInfiniteBulletMode();
|
|
InitAI();
|
|
}
|
|
|
|
void Hero::FillMFObjectPart(Room* room, Human* hum, cs::MFObjectPart* part_data)
|
|
{
|
|
part_data->set_object_type(ET_Hero);
|
|
cs::MFHeroPart* p = part_data->mutable_union_obj_10();
|
|
p->set_obj_uniid(GetUniId());
|
|
TypeConvert::ToPb(GetPos(), p->mutable_pos());
|
|
TypeConvert::ToPb(GetAttackDir(), p->mutable_dir());
|
|
}
|
|
|
|
void Hero::FillMFObjectFull(Room* room, Human* hum, cs::MFObjectFull* full_data)
|
|
{
|
|
full_data->set_object_type(ET_Hero);
|
|
cs::MFHeroFull* p = full_data->mutable_union_obj_10();
|
|
p->set_obj_uniid(GetUniId());
|
|
TypeConvert::ToPb(GetPos(), p->mutable_pos());
|
|
TypeConvert::ToPb(GetAttackDir(), p->mutable_dir());
|
|
p->set_heroid(meta->i->id());
|
|
p->set_master_uniid(master.Get() ? master.Get()->GetUniId() : 0);
|
|
p->set_dead(dead);
|
|
p->set_health(GetHP());
|
|
p->set_max_health(GetMaxHP());
|
|
FillBuffList(hum, p->mutable_buff_list());
|
|
}
|
|
|
|
void Hero::Update(int delta_time)
|
|
{
|
|
ai->Update(delta_time);
|
|
++updated_times_;
|
|
}
|
|
|
|
void Hero::OnExplosionHit(Explosion* e)
|
|
{
|
|
if (IsInvincible()) {
|
|
return;
|
|
}
|
|
if (dead) {
|
|
return;
|
|
}
|
|
if (e->IsPreBattleExplosion()) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Jump) ||
|
|
HasBuffEffect(kBET_Fly)) {
|
|
return;
|
|
}
|
|
|
|
RemoveBuffByEffectId(kBET_PeaceMode);
|
|
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
|
|
DecHP(finaly_dmg,
|
|
VP_Explosion,
|
|
"",
|
|
e->GetExplosionEffect());
|
|
}
|
|
|
|
void Hero::OnBulletHit(Bullet* bullet)
|
|
{
|
|
if (IsInvincible()) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Jump) ||
|
|
HasBuffEffect(kBET_Fly)) {
|
|
return;
|
|
}
|
|
|
|
RemoveBuffByEffectId(kBET_PeaceMode);
|
|
if (!IsDead(room) && (bullet->IsBomb() || bullet->sender.Get()->team_id != team_id)) {
|
|
float dmg = bullet->GetAtk();
|
|
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->sender.Get()->IsHuman()) {
|
|
bullet->sender.Get()->AsHuman()->stats.damage_amount_out += finaly_dmg;
|
|
}
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
|
|
float Hero::GetSpeed()
|
|
{
|
|
float speed = meta->i->move_speed();
|
|
return speed;
|
|
}
|
|
|
|
void Hero::_UpdateMove(int speed)
|
|
{
|
|
do {
|
|
int distance = std::min(5, speed);
|
|
InternalUpdateMove(distance);
|
|
speed -= distance;
|
|
} while (speed > 0);
|
|
}
|
|
|
|
void Hero::InternalUpdateMove(float speed)
|
|
{
|
|
a8::Vec2 old_pos = GetPos();
|
|
a8::Vec2 new_pos = GetPos() + GetMoveDir() * speed;
|
|
|
|
SetPos(new_pos);
|
|
if (!CheckCollision()) {
|
|
room->grid_service->MoveCreature(this);
|
|
} else {
|
|
if (on_move_collision && !on_move_collision()) {
|
|
SetPos(old_pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hero::RecalcSelfCollider()
|
|
{
|
|
if (!self_collider_) {
|
|
self_collider_ = new CircleCollider();
|
|
self_collider_->owner = this;
|
|
AddEntityCollider(self_collider_);
|
|
}
|
|
self_collider_->pos = a8::Vec2(meta->i->move_offset_x(), meta->i->move_offset_y());
|
|
self_collider_->rad = meta->i->radius();
|
|
}
|
|
|
|
|
|
float Hero::GetRadius()
|
|
{
|
|
return meta->i->radius();
|
|
}
|
|
|
|
float Hero::GetHitRadius()
|
|
{
|
|
return meta->i->hit_radius();
|
|
}
|
|
|
|
void Hero::GetAabbBox(AabbCollider& aabb_box)
|
|
{
|
|
if (!meta) {
|
|
abort();
|
|
}
|
|
aabb_box.active = true;
|
|
aabb_box.owner = this;
|
|
aabb_box._min.x = -GetRadius();
|
|
aabb_box._min.y = -GetRadius();
|
|
aabb_box._max.x = GetRadius();
|
|
aabb_box._max.y = GetRadius();
|
|
aabb_box.MoveCenter(meta->i->move_offset_x(), meta->i->move_offset_y());
|
|
}
|
|
|
|
void Hero::GetHitAabbBox(AabbCollider& aabb_box)
|
|
{
|
|
if (!meta) {
|
|
abort();
|
|
}
|
|
aabb_box.active = true;
|
|
aabb_box.owner = this;
|
|
aabb_box._min.x = -GetHitRadius();
|
|
aabb_box._min.y = -GetHitRadius();
|
|
aabb_box._max.x = GetHitRadius();
|
|
aabb_box._max.y = GetHitRadius();
|
|
aabb_box.MoveCenter(meta->i->hit_offset_x(), meta->i->hit_offset_y());
|
|
}
|
|
|
|
void Hero::DetachFromMaster()
|
|
{
|
|
if (!detached_) {
|
|
if (master.Get()) {
|
|
master.Get()->SlaveOnRemove(this);
|
|
}
|
|
detached_ = true;
|
|
}
|
|
}
|
|
|
|
void Hero::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());
|
|
}
|
|
|
|
void Hero::BeKill(int killer_id, const std::string& killer_name, int weapon_id)
|
|
{
|
|
dead = true;
|
|
if (meta->i->dead_drop() != 0) {
|
|
room->ScatterDrop(GetPos(), meta->i->dead_drop());
|
|
}
|
|
room->frame_event.AddDead(GetWeakPtrRef(), 0);
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
meta->i->delay_delete() / FRAME_RATE_MS,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Hero* hero = (Hero*)param.sender.GetUserData();
|
|
hero->BroadcastDeleteState(hero->room);
|
|
hero->RemoveFromAroundPlayers(hero->room);
|
|
hero->room->grid_service->RemoveCreature(hero);
|
|
hero->room->RemoveObjectLater(hero);
|
|
hero->delete_frameno = hero->room->GetFrameNo();
|
|
std::vector<Human*> watch_list;
|
|
hero->room->GetPartObjectWatchList(hero, watch_list);
|
|
if (!watch_list.empty()) {
|
|
abort();
|
|
}
|
|
},
|
|
&xtimer_attacher.timer_list_);
|
|
GetTrigger()->Die();
|
|
}
|
|
|
|
void Hero::OnAddToTargetPartObject(Entity* target)
|
|
{
|
|
if (delete_frameno > 0) {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
void Hero::OnRemoveFromTargetPartObject(Entity* target)
|
|
{
|
|
|
|
}
|
|
|
|
void Hero::InitAI()
|
|
{
|
|
MetaData::AI* ai_meta = MetaMgr::Instance()->GetHeroAI(meta->i->ai());
|
|
if (!ai_meta) {
|
|
#ifdef DEBUG
|
|
abort();
|
|
#endif
|
|
ai_meta = MetaMgr::Instance()->GetHeroAI(ANDROID_AI_ID_START);
|
|
}
|
|
if (!ai_meta) {
|
|
abort();
|
|
}
|
|
if (ai_meta->i->ai_kind() == kAI_MineSweeper) {
|
|
ai = new MineSweeperAI;
|
|
ai->ai_meta = ai_meta;
|
|
} else {
|
|
ai = new HeroAI;
|
|
ai->ai_meta = ai_meta;
|
|
}
|
|
ai->owner = this;
|
|
int buff_uniid = MustBeAddBuff(this,
|
|
kPeaceModeBuffId
|
|
);
|
|
if (buff_uniid) {
|
|
Buff* buff = GetBuffByUniId(buff_uniid);
|
|
if (buff) {
|
|
int peace_time = 1000 * 6;
|
|
if (ai->ai_meta->i->ai_kind() == kAI_MineSweeper) {
|
|
peace_time = 0;
|
|
}
|
|
if (ai->ai_meta->i->peace_time() > 0) {
|
|
peace_time += ai->ai_meta->i->peace_time();
|
|
}
|
|
room->xtimer.ModifyTimer(buff->remover_timer, peace_time / FRAME_RATE_MS);
|
|
}
|
|
}
|
|
if (ai_meta->i->ai_kind() == kAI_MineSweeper) {
|
|
int time = 500 + rand() % 500;
|
|
int idle_time = 300 + rand() % 300;
|
|
((HeroAI*)ai)->ForceRandomWalk(time, idle_time);
|
|
}
|
|
}
|
|
|
|
void Hero::OnBattleStart(Room* room)
|
|
{
|
|
if (master.Get()) {
|
|
Destory();
|
|
}
|
|
}
|
|
|
|
void Hero::Destory()
|
|
{
|
|
#if DEBUG
|
|
room->BroadcastDebugMsg(a8::Format("hero destory uniid:%d pos:%d,%d",
|
|
{
|
|
GetUniId(),
|
|
GetPos().x,
|
|
GetPos().y
|
|
}));
|
|
#endif
|
|
room->xtimer.AddDeadLineTimerAndAttach
|
|
(
|
|
0,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Hero* hero = (Hero*)param.sender.GetUserData();
|
|
if (hero->delete_frameno == 0) {
|
|
hero->BroadcastDeleteState(hero->room);
|
|
hero->RemoveFromAroundPlayers(hero->room);
|
|
hero->room->grid_service->RemoveCreature(hero);
|
|
hero->room->RemoveObjectLater(hero);
|
|
hero->delete_frameno = hero->room->GetFrameNo();
|
|
std::vector<Human*> watch_list;
|
|
hero->room->GetPartObjectWatchList(hero, watch_list);
|
|
if (!watch_list.empty()) {
|
|
abort();
|
|
}
|
|
}
|
|
},
|
|
&xtimer_attacher.timer_list_);
|
|
}
|
|
|
|
std::string Hero::GetName()
|
|
{
|
|
return meta->i->name();
|
|
}
|
|
|
|
void Hero::DropItems(Obstacle* obstacle)
|
|
{
|
|
bool is_treasure_box = false;
|
|
if (obstacle->IsEntitySubType(EST_RoomObstacle)) {
|
|
is_treasure_box = ((RoomObstacle*)obstacle)->is_treasure_box;
|
|
}
|
|
int drop_id = obstacle->meta->RandDrop();
|
|
if (drop_id == 0) {
|
|
return;
|
|
}
|
|
room->ScatterDrop(obstacle->GetPos(), drop_id);
|
|
}
|