2021-04-08 10:44:58 +08:00

214 lines
5.9 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 "obstacle.h"
#include "collider.h"
Hero::Hero():Creature()
{
++PerfMonitor::Instance()->entity_num[ET_Hero];
}
Hero::~Hero()
{
--PerfMonitor::Instance()->entity_num[ET_Hero];
}
void Hero::Initialize()
{
Creature::Initialize();
RecalcSelfCollider();
ai = new HeroAI;
ai->owner = this;
ai->SetAiLevel(8);
}
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(GetEntityUniId());
TypeConvert::ToPb(GetPos(), p->mutable_pos());
TypeConvert::ToPb(GetMoveDir(), 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(GetEntityUniId());
TypeConvert::ToPb(GetPos(), p->mutable_pos());
TypeConvert::ToPb(GetMoveDir(), p->mutable_dir());
p->set_heroid(meta->i->id());
p->set_master_uniid(master ? master->GetEntityUniId() : 0);
}
void Hero::Update(int delta_time)
{
ai->Update(delta_time);
++updated_times_;
}
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 (!IsCollisionInMapService()) {
room->grid_service->MoveCreature(this);
} else {
if (on_move_collision && !on_move_collision()) {
SetPos(old_pos);
}
}
}
bool Hero::IsCollisionInMapService()
{
if (room->OverBorder(GetPos(), meta->i->radius())){
return true;
}
if (HasBuffEffect(kBET_ThroughWall)) {
return false;
}
std::set<ColliderComponent*> colliders;
room->map_service->GetColliders(room, GetX(), GetY(), colliders);
AabbCollider aabb_box;
GetAabbBox(aabb_box);
for (ColliderComponent* collider : colliders) {
switch (collider->owner->GetEntityType()) {
case ET_Obstacle:
{
Obstacle* obstacle = (Obstacle*)collider->owner;
if (!obstacle->IsDead(room) &&
(
(collider->type == CT_Aabb && aabb_box.Intersect((ColliderComponent*)collider)) ||
(collider->type == CT_Circle && self_collider_->Intersect((ColliderComponent*)collider))
)
) {
if (last_collision_door_ != collider->owner) {
if (!obstacle->IsDead(room) &&
obstacle->Attackable() &&
obstacle->meta->i->drop() != 0 &&
room->GetGasData().gas_mode != GasInactive &&
(!obstacle->IsTerminatorAirDropBox(room))
) {
obstacle->Die(room);
if (obstacle->IsDead(room)) {
#if 0
DropItems(obstacle);
#endif
}
obstacle->BroadcastFullState(room);
if (obstacle->IsTerminatorAirDropBox(room) &&
!HasBuffEffect(kBET_Terminator)) {
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(TERMINATOR_BUFF_ID);
if (buff_meta) {
#if 0
AddBuff(this, buff_meta, 1);
#endif
}
}
} else {
Global::last_collider = collider;
return true;
}
}
}
}
break;
case ET_Building:
{
if (
(
(collider->type == CT_Aabb && aabb_box.Intersect((ColliderComponent*)collider)) ||
(collider->type == CT_Circle && self_collider_->Intersect((ColliderComponent*)collider))
)
) {
if (last_collision_door_ != collider->owner) {
Global::last_collider = collider;
return true;
}
}
}
break;
default:
break;
}
}
return false;
}
void Hero::RecalcSelfCollider()
{
if (!self_collider_) {
self_collider_ = new CircleCollider();
self_collider_->owner = this;
AddEntityCollider(self_collider_);
}
self_collider_->pos = a8::Vec2();
self_collider_->rad = meta->i->radius();
}
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();
}
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();
}