936 lines
26 KiB
C++
936 lines
26 KiB
C++
#include "precompile.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "obstacle.h"
|
|
#include "metamgr.h"
|
|
#include "room.h"
|
|
#include "collider.h"
|
|
#include "building.h"
|
|
#include "human.h"
|
|
#include "app.h"
|
|
#include "typeconvert.h"
|
|
#include "bullet.h"
|
|
#include "perfmonitor.h"
|
|
#include "roomobstacle.h"
|
|
#include "loot.h"
|
|
#include "explosion.h"
|
|
#include "entityfactory.h"
|
|
|
|
enum ObstacleDataFlags_e
|
|
{
|
|
kHealth = 1
|
|
};
|
|
|
|
Obstacle::Obstacle():Entity()
|
|
{
|
|
++PerfMonitor::Instance()->entity_num[ET_Obstacle];
|
|
}
|
|
|
|
Obstacle::~Obstacle()
|
|
{
|
|
--PerfMonitor::Instance()->entity_num[ET_Obstacle];
|
|
}
|
|
|
|
void Obstacle::Initialize()
|
|
{
|
|
Entity::Initialize();
|
|
health_ = meta->i->hp();
|
|
RecalcSelfCollider();
|
|
}
|
|
|
|
void Obstacle::RecalcSelfCollider()
|
|
{
|
|
if (is_door_) {
|
|
//门
|
|
if (!self_collider2_) {
|
|
self_collider2_ = new AabbCollider();
|
|
self_collider2_->owner = this;
|
|
AddEntityCollider(self_collider2_);
|
|
a8::Vec2 old_pos = GetPos();
|
|
{
|
|
SetPos(a8::Vec2(building_->GetX() + door_state1_->x() - building_->meta->i->tilewidth() / 2.0,
|
|
building_->GetY() + door_state1_->y() - building_->meta->i->tileheight() / 2.0)
|
|
);
|
|
self_collider2_->_min = a8::Vec2(0.0f - door_state0_->width() / 2.0f,
|
|
0.0f - door_state0_->height() / 2.0f);
|
|
self_collider2_->_max = a8::Vec2(door_state0_->width() / 2.0f, door_state0_->height() / 2.0f);
|
|
permanent_map_service->AddCollider(self_collider2_);
|
|
}
|
|
{
|
|
SetPos(a8::Vec2(building_->GetX() + door_state0_->x() - building_->meta->i->tilewidth() / 2.0,
|
|
building_->GetY() + door_state0_->y() - building_->meta->i->tileheight() / 2.0)
|
|
);
|
|
self_collider2_->_min = a8::Vec2(0.0f - door_state1_->width() / 2.0f,
|
|
0.0f - door_state1_->height() / 2.0f);
|
|
self_collider2_->_max = a8::Vec2(door_state1_->width() / 2.0f, door_state1_->height() / 2.0f);
|
|
permanent_map_service->AddCollider(self_collider2_);
|
|
}
|
|
SetPos(old_pos);
|
|
}
|
|
} else if (!Throughable()){
|
|
switch (meta->i->type()) {
|
|
case 1:
|
|
{
|
|
if (!self_collider_) {
|
|
self_collider_ = new CircleCollider();
|
|
self_collider_->owner = this;
|
|
AddEntityCollider(self_collider_);
|
|
}
|
|
self_collider_->pos = a8::Vec2();
|
|
self_collider_->rad = meta->i->height() / 2.0;
|
|
permanent_map_service->AddCollider(self_collider_);
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
if (!self_collider2_) {
|
|
self_collider2_ = new AabbCollider();
|
|
self_collider2_->owner = this;
|
|
AddEntityCollider(self_collider2_);
|
|
}
|
|
self_collider2_->_min = a8::Vec2(meta->i->width() / -2.0f, meta->i->height() / -2.0f);
|
|
self_collider2_->_max = a8::Vec2(meta->i->width() / 2.0f, meta->i->height() / 2.0f);
|
|
permanent_map_service->AddCollider(self_collider2_);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
abort();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!self_collider_) {
|
|
self_collider_ = new CircleCollider();
|
|
self_collider_->owner = this;
|
|
AddEntityCollider(self_collider_);
|
|
self_collider_->pos = a8::Vec2();
|
|
self_collider_->rad = 0.0f;
|
|
}
|
|
for (auto collider : colliders_) {
|
|
collider->tag = collider_tag;
|
|
collider->param1 = collider_param1;
|
|
collider->param2 = collider_param2;
|
|
if (IsHalfWallCollider()) {
|
|
a8::SetBitFlag(collider->tag, kHalfWallTag);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Obstacle::FillMFObjectPart(Room* room, Human* hum, cs::MFObjectPart* part_data)
|
|
{
|
|
part_data->set_object_type(ET_Obstacle);
|
|
cs::MFObstaclePart* p = part_data->mutable_union_obj_2();
|
|
p->set_obj_uniid(GetUniId());
|
|
TypeConvert::ToPb(GetPos(), p->mutable_pos());
|
|
p->set_scale(1.0f);
|
|
}
|
|
|
|
void Obstacle::FillMFObjectFull(Room* room, Human* hum, cs::MFObjectFull* full_data)
|
|
{
|
|
full_data->set_object_type(ET_Obstacle);
|
|
if (IsClientCached(hum)) {
|
|
int object_flags = 0;
|
|
a8::SetBitFlag(object_flags, kOfReadCache);
|
|
full_data->set_obj_uniid(GetUniId());
|
|
full_data->set_object_flags(object_flags);
|
|
return;
|
|
}
|
|
|
|
cs::MFObstacleFull* p = full_data->mutable_union_obj_2();
|
|
if (CanClientCache(hum)) {
|
|
int object_flags = 0;
|
|
a8::SetBitFlag(object_flags, kOfWriteCache);
|
|
#if 0
|
|
full_data->set_obj_uniid(GetUniId());
|
|
#endif
|
|
full_data->set_object_flags(object_flags);
|
|
AddClientCache(hum);
|
|
}
|
|
p->set_obj_uniid(GetUniId());
|
|
TypeConvert::ToPb(GetPos(), p->mutable_pos());
|
|
p->set_scale(1.0f);
|
|
|
|
p->set_obstacle_id(meta->i->thing_id());
|
|
p->set_health(GetHealth(room));
|
|
p->set_dead(IsDead(room));
|
|
p->set_dead_at_thisframe(IsDead(room) ? GetDeadFrameNo(room) <= room->GetFrameNo() : false);
|
|
|
|
if (!IsPermanent()) {
|
|
RoomObstacle* ob = (RoomObstacle*)this;
|
|
p->set_born_frameno(ceil(ob->born_frameno / 2.0));
|
|
}
|
|
|
|
p->set_is_door(is_door_);
|
|
if (is_door_ && IsPermanent()) {
|
|
ObstacleData* data = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(data->flags, kHealth)) {
|
|
data->health = health_;
|
|
a8::SetBitFlag(data->flags, kHealth);
|
|
}
|
|
|
|
p->set_door_id(door_id_);
|
|
p->set_door_open_times(data->door_open_times);
|
|
p->set_door_old_state((int)data->door_state);
|
|
p->set_door_new_state((int)data->door_state);
|
|
p->set_door_house_uniid(door_house_uniid_);
|
|
p->set_door_house_id(building_->meta->i->mapid());
|
|
if (data->door_state == DoorStateClose) {
|
|
p->set_door_width(door_state0_->width());
|
|
p->set_door_height(door_state0_->height());
|
|
} else {
|
|
p->set_door_width(door_state1_->width());
|
|
p->set_door_height(door_state1_->height());
|
|
}
|
|
}
|
|
if (meta->i->thing_type() == kObstacleHideHouse) {
|
|
ObstacleData* data = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(data->flags, kHealth)) {
|
|
data->health = health_;
|
|
a8::SetBitFlag(data->flags, kHealth);
|
|
}
|
|
if (GetInteractionData(hum)) {
|
|
#if 1
|
|
p->set_button_name("3");
|
|
#else
|
|
p->set_button_name("脱离");
|
|
#endif
|
|
} else {
|
|
#if 1
|
|
p->set_button_name("2");
|
|
#else
|
|
p->set_button_name("躲藏");
|
|
#endif
|
|
}
|
|
} else {
|
|
if (IsOpenInteraction()) {
|
|
if (IsDead(hum->room)) {
|
|
p->set_button_name("");
|
|
} else {
|
|
#if 1
|
|
p->set_button_name("1");
|
|
#else
|
|
p->set_button_name("开启");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Obstacle::GetAabbBox(AabbCollider& aabb_box)
|
|
{
|
|
if (self_collider2_) {
|
|
aabb_box = *self_collider2_;
|
|
return;
|
|
}
|
|
if (self_collider_) {
|
|
aabb_box.active = true;
|
|
aabb_box.owner = this;
|
|
aabb_box.tag = collider_tag;
|
|
aabb_box._min.x = -self_collider_->rad;
|
|
aabb_box._min.y = -self_collider_->rad;
|
|
aabb_box._max.x = self_collider_->rad;
|
|
aabb_box._max.y = self_collider_->rad;
|
|
return;
|
|
}
|
|
aabb_box.tag = collider_tag;
|
|
aabb_box.active = true;
|
|
aabb_box.owner = this;
|
|
}
|
|
|
|
void Obstacle::GetCircleBox(CircleCollider& circle_box)
|
|
{
|
|
|
|
}
|
|
|
|
bool Obstacle::IsDead(Room* room)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
return p->dead;
|
|
} else {
|
|
return dead_;
|
|
}
|
|
|
|
}
|
|
|
|
long long Obstacle::GetDeadFrameNo(Room* room)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
return p->dead_frameno;
|
|
} else {
|
|
return dead_frameno_;
|
|
}
|
|
}
|
|
|
|
void Obstacle::OnPreCollision(Room* room)
|
|
{
|
|
if (!is_door_) {
|
|
return;
|
|
}
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
if (p->door_state == DoorStateClose) {
|
|
self_collider2_->_min = a8::Vec2(0.0f - door_state0_->width() / 2.0f,
|
|
0.0f - door_state0_->height() / 2.0f);
|
|
self_collider2_->_max = a8::Vec2(door_state0_->width() / 2.0f,
|
|
door_state0_->height() / 2.0f);
|
|
} else {
|
|
self_collider2_->_min = a8::Vec2(0.0f - door_state1_->width() / 2.0f,
|
|
0.0f - door_state1_->height() / 2.0f);
|
|
self_collider2_->_max = a8::Vec2(door_state1_->width() / 2.0f,
|
|
door_state1_->height() / 2.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Obstacle::SetDoorInfo(Building* building, int door_id_x)
|
|
{
|
|
MetaData::Building::Door* door_meta = &building->meta->doors[door_id_x];
|
|
is_door_ = true;
|
|
door_id_ = door_meta->door_id;
|
|
building_ = building;
|
|
door_house_uniid_ = building_->GetUniId();
|
|
door_state0_ = door_meta->state0;
|
|
door_state1_ = door_meta->state1;
|
|
}
|
|
|
|
bool Obstacle::IsDoor()
|
|
{
|
|
return is_door_;
|
|
}
|
|
|
|
DoorState_e Obstacle::GetDoorState(Room *room)
|
|
{
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
return p->door_state;
|
|
}
|
|
|
|
void Obstacle::SetDoorState(Room* room, DoorState_e state)
|
|
{
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
p->door_state = state;
|
|
}
|
|
|
|
void Obstacle::SetBuilding(Building* building)
|
|
{
|
|
building_ = building;
|
|
}
|
|
|
|
Building* Obstacle::GetBuilding()
|
|
{
|
|
return building_;
|
|
}
|
|
|
|
const metatable::DoorObjJson* Obstacle::GetDoorState0()
|
|
{
|
|
return door_state0_;
|
|
}
|
|
|
|
const metatable::DoorObjJson* Obstacle::GetDoorState1()
|
|
{
|
|
return door_state1_;
|
|
}
|
|
|
|
void Obstacle::IncDoorOpenTimes(Room* room)
|
|
{
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
++(p->door_open_times);
|
|
}
|
|
|
|
float Obstacle::GetHealth(Room* room)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
return p->health;
|
|
} else {
|
|
return health_;
|
|
}
|
|
}
|
|
|
|
void Obstacle::SetHealth(Room* room, float value)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
p->health = value;
|
|
} else {
|
|
health_ = value;
|
|
}
|
|
}
|
|
|
|
void Obstacle::Die(Room* room)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
p->health = 0;
|
|
p->dead = true;
|
|
p->dead_frameno = room->GetFrameNo();
|
|
} else {
|
|
health_ = 0;
|
|
dead_ = true;
|
|
dead_frameno_ = room->GetFrameNo();
|
|
}
|
|
}
|
|
|
|
bool Obstacle::IsPermanent()
|
|
{
|
|
if (is_permanent) {
|
|
if (GetUniId() >= FIXED_OBJECT_MAXID) {
|
|
abort();
|
|
}
|
|
} else {
|
|
if (GetUniId() < FIXED_OBJECT_MAXID) {
|
|
abort();
|
|
}
|
|
}
|
|
return is_permanent;
|
|
}
|
|
|
|
bool Obstacle::Throughable()
|
|
{
|
|
return meta->i->bullet_hit() == kBulletHitPass &&
|
|
meta->i->collision_hit() == kCollisionHitPass &&
|
|
meta->i->explosion_hit() == kExplosionHitPass;
|
|
}
|
|
|
|
int Obstacle::GetTeamId(Room* room)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
return p->team_id;
|
|
} else {
|
|
return team_id_;
|
|
}
|
|
}
|
|
|
|
void Obstacle::SetTeamId(Room* room, int team_id)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
p->team_id = team_id;
|
|
} else {
|
|
team_id_ = team_id;
|
|
}
|
|
}
|
|
|
|
int Obstacle::GetMasterId(Room* room)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
return p->master_id;
|
|
} else {
|
|
return master_id_;
|
|
}
|
|
}
|
|
|
|
void Obstacle::SetMasterId(Room* room, int master_id)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
p->master_id = master_id;
|
|
} else {
|
|
master_id_ = master_id;
|
|
}
|
|
}
|
|
|
|
void Obstacle::OnBulletHit(Bullet* bullet)
|
|
{
|
|
if (meta->i->bullet_hit() == kBulletHitEatDmg) {
|
|
return;
|
|
}
|
|
if (bullet->IsPreBattleBullet()) {
|
|
return;
|
|
}
|
|
|
|
if (!IsDead(bullet->room)) {
|
|
if (meta->receive_special_damage_type != 0 &&
|
|
((bullet->gun_meta->special_damage_type & meta->receive_special_damage_type) == 0)) {
|
|
return;
|
|
}
|
|
if (meta->i->thing_type() == kObstacleOilBucket) {
|
|
Entity* real_object = AsRoomObstacle()->GetRealObject(bullet->room);
|
|
if (real_object->IsEntityType(ET_Loot)) {
|
|
Loot* loot = (Loot*)real_object;
|
|
if (loot->pickuped) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
float dmg = bullet->GetAtk();
|
|
float def = 0;
|
|
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
|
|
SetHealth(bullet->room, std::max(0.0f, GetHealth(bullet->room) - finaly_dmg));
|
|
if (GetHealth(bullet->room) <= 0.01f) {
|
|
Die(bullet->room);
|
|
if (meta->i->thing_type() == kObstacleOilBucket) {
|
|
Entity* real_object = AsRoomObstacle()->GetRealObject(bullet->room);
|
|
if (real_object->IsEntityType(ET_Loot)) {
|
|
Loot* loot = (Loot*)real_object;
|
|
loot->pickuped = true;
|
|
bullet->room->RemoveObjectLater(loot);
|
|
bullet->room->RemoveObjectLater(AsRoomObstacle());
|
|
RoomObstacle* obstacle = bullet->room->CreateObstacle
|
|
(
|
|
meta->int_param1,
|
|
GetPos().x,
|
|
GetPos().y
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if (IsDead(bullet->room)) {
|
|
ProcDieExplosion(bullet->room);
|
|
bullet->sender.Get()->DropItems(this);
|
|
if (meta->i->thing_type() == kObstacleOilBucket) {
|
|
MetaData::MapThing* bomb_meta = MetaMgr::Instance()->GetMapThing(meta->int_param1);
|
|
if (bomb_meta) {
|
|
RoomObstacle* obstacle = bullet->room->CreateObstacle
|
|
(
|
|
bomb_meta->i->thing_id(),
|
|
GetPos().x,
|
|
GetPos().y
|
|
);
|
|
}
|
|
}
|
|
}
|
|
BroadcastFullState(bullet->room);
|
|
#ifdef DEBUG
|
|
bullet->sender.Get()->SendDebugMsg
|
|
(
|
|
a8::Format("bullet weapon_id:%d atk:%f",
|
|
{
|
|
bullet->gun_meta->i->id(),
|
|
bullet->GetAtk()
|
|
})
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Obstacle::OnExplosionHit(Explosion* e)
|
|
{
|
|
if (meta->i->explosion_hit() == kExplosionHitEatDmg) {
|
|
return;
|
|
}
|
|
if (IsDead(e->GetRoom())) {
|
|
return;
|
|
}
|
|
if (e->GetDmg() < 0.001f) {
|
|
return;
|
|
}
|
|
if (meta->receive_special_damage_type != 0 &&
|
|
((e->GetSpecialDamageType() & meta->receive_special_damage_type) == 0)) {
|
|
return;
|
|
}
|
|
if (e->IsPreBattleExplosion()) {
|
|
return;
|
|
}
|
|
|
|
float dmg = e->GetDmg();
|
|
float def = 0;
|
|
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
|
|
SetHealth(e->GetRoom(), std::max(0.0f, GetHealth(e->GetRoom()) - finaly_dmg));
|
|
if (GetHealth(e->GetRoom()) <= 0.01f) {
|
|
Die(e->GetRoom());
|
|
}
|
|
if (IsDead(e->GetRoom())) {
|
|
ProcDieExplosion(e->GetRoom());
|
|
if (meta->HasDrop()) {
|
|
e->GetRoom()->ScatterDrop(GetPos(), meta->RandDrop());
|
|
}
|
|
if (meta->i->thing_type() == kObstacleOilBucket) {
|
|
MetaData::MapThing* bomb_meta = MetaMgr::Instance()->GetMapThing(meta->int_param1);
|
|
if (bomb_meta) {
|
|
RoomObstacle* obstacle = e->GetRoom()->CreateObstacle
|
|
(
|
|
bomb_meta->i->thing_id(),
|
|
GetPos().x,
|
|
GetPos().y
|
|
);
|
|
}
|
|
}
|
|
}
|
|
BroadcastFullState(e->GetRoom());
|
|
}
|
|
|
|
bool Obstacle::CanThroughable(Creature* c)
|
|
{
|
|
if (IsDead(c->room)) {
|
|
return true;
|
|
}
|
|
switch (meta->i->collision_hit()) {
|
|
case kCollisionHitPass:
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Obstacle::CanThroughable(Bullet* bullet)
|
|
{
|
|
switch (meta->i->bullet_hit()) {
|
|
case kBulletHitPass:
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
case kBulletHitOnlySpecDmg:
|
|
{
|
|
return !(meta->receive_special_damage_type != 0 &&
|
|
((bullet->gun_meta->special_damage_type & meta->receive_special_damage_type) != 0));
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Obstacle::DoInteraction(Human* sender)
|
|
{
|
|
if (IsDoor()) {
|
|
if (GetDoorState(sender->room) == DoorStateClose) {
|
|
SetDoorState(sender->room, DoorStateOpen);
|
|
float x = GetBuilding()->GetX() + GetDoorState1()->x() - GetBuilding()->meta->i->tilewidth() / 2.0;
|
|
float y = GetBuilding()->GetY() + GetDoorState1()->y() - GetBuilding()->meta->i->tileheight() / 2.0;
|
|
SetPos(a8::Vec2(x, y));
|
|
} else {
|
|
SetDoorState(sender->room, DoorStateClose);
|
|
float x = GetBuilding()->GetX() + GetDoorState0()->x() - GetBuilding()->meta->i->tilewidth() / 2.0;
|
|
float y = GetBuilding()->GetY() + GetDoorState0()->y() - GetBuilding()->meta->i->tileheight() / 2.0;
|
|
SetPos(a8::Vec2(x, y));
|
|
}
|
|
IncDoorOpenTimes(sender->room);
|
|
RecalcSelfCollider();
|
|
sender->room->TraverseHumanList
|
|
(a8::XParams(),
|
|
[this] (Human* hum, a8::XParams& param) -> bool
|
|
{
|
|
hum->AddToNewObjects(this);
|
|
if (TestCollision(hum->room, hum)) {
|
|
hum->SetLastCollisionDoor(this);
|
|
} else if (hum->GetLastCollisionDoor() == this) {
|
|
hum->SetLastCollisionDoor(nullptr);
|
|
}
|
|
return true;
|
|
});
|
|
return true;
|
|
}
|
|
switch (meta->i->thing_type()) {
|
|
case kObstacleHideHouse:
|
|
{
|
|
DoHideHouseInteraction(sender);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
if (!IsDead(sender->room) && IsOpenInteraction()) {
|
|
Die(sender->room);
|
|
sender->DropItems(this);
|
|
BroadcastFullState(sender->room);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int Obstacle::OnCollisionTrigger(Creature* c, ColliderComponent* collider)
|
|
{
|
|
if (IsDead(c->room)) {
|
|
return 0;
|
|
}
|
|
switch (meta->i->collision_hit()) {
|
|
case kCollisionHitPass:
|
|
{
|
|
return 0;
|
|
}
|
|
break;
|
|
case kCollisionHitBlock:
|
|
{
|
|
Global::last_collider = collider;
|
|
return 1;
|
|
}
|
|
break;
|
|
case kCollisionHitDeadAndDrop:
|
|
{
|
|
if (c->room->GetGasData().gas_mode != GasInactive &&
|
|
meta->HasDrop() &&
|
|
c->IsHuman()) {
|
|
Die(c->room);
|
|
c->DropItems(this);
|
|
BroadcastFullState(c->room);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
case kCollisionHitSpecEvent:
|
|
{
|
|
if (ProcSpecEvent(c, collider)) {
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
Global::last_collider = collider;
|
|
return 1;
|
|
}
|
|
|
|
void Obstacle::DoHideHouseInteraction(Human* sender)
|
|
{
|
|
ObstacleData* data = sender->room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(data->flags, kHealth)) {
|
|
data->health = health_;
|
|
a8::SetBitFlag(data->flags, kHealth);
|
|
}
|
|
|
|
auto p = GetInteractionData(sender);
|
|
if (p) {
|
|
ClearObstacleBuff(sender);
|
|
sender->SetPos(std::get<1>(*p));
|
|
data->interaction_humans->erase(sender->GetUniId());
|
|
} else {
|
|
a8::Vec2 old_pos = sender->GetPos();
|
|
sender->SetPos(GetPos());
|
|
AddObstacleBuff(sender);
|
|
if (!data->interaction_humans) {
|
|
data->interaction_humans = new std::map<int, std::tuple<long long, a8::Vec2>>();
|
|
}
|
|
(*data->interaction_humans)[sender->GetUniId()] = std::make_tuple
|
|
(sender->room->GetFrameNo(), old_pos);
|
|
}
|
|
sender->AddToNewObjects(this);
|
|
}
|
|
|
|
std::tuple<long long, a8::Vec2>* Obstacle::GetInteractionData(Human* sender)
|
|
{
|
|
if (IsPermanent()) {
|
|
ObstacleData* p = sender->room->GetPermanentObstacleData(GetUniId());
|
|
if (!a8::HasBitFlag(p->flags, kHealth)) {
|
|
p->health = health_;
|
|
a8::SetBitFlag(p->flags, kHealth);
|
|
}
|
|
if (!p->interaction_humans) {
|
|
return nullptr;
|
|
}
|
|
auto itr = p->interaction_humans->find(sender->GetUniId());
|
|
return itr != p->interaction_humans->end() ? &itr->second : nullptr;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void Obstacle::AddObstacleBuff(Creature* c)
|
|
{
|
|
a8::Vec2 old_context_dir = c->context_dir;
|
|
a8::Vec2 old_context_pos = c->context_pos;
|
|
c->context_dir = c->GetAttackDir();
|
|
c->context_pos = c->GetPos();
|
|
for (int buff_id : meta->buff_list) {
|
|
c->TryAddBuff(c, buff_id);
|
|
}
|
|
c->context_pos = old_context_pos;
|
|
c->context_dir = old_context_dir;
|
|
}
|
|
|
|
void Obstacle::ClearObstacleBuff(Creature* c)
|
|
{
|
|
for (int buff_id : meta->buff_list) {
|
|
c->RemoveBuffById(buff_id);
|
|
}
|
|
}
|
|
|
|
bool Obstacle::IsTouchInteraction()
|
|
{
|
|
return meta->i->interaction_mode() == 1;
|
|
}
|
|
|
|
bool Obstacle::IsOpenInteraction()
|
|
{
|
|
return meta->i->interaction_mode() == 2;
|
|
}
|
|
|
|
bool Obstacle::IsRoomObstacle()
|
|
{
|
|
return !IsPermanent();
|
|
}
|
|
|
|
RoomObstacle* Obstacle::AsRoomObstacle()
|
|
{
|
|
return (RoomObstacle*)this;
|
|
}
|
|
|
|
bool Obstacle::CanSeeMe(Human* hum)
|
|
{
|
|
switch (meta->i->view_mode()) {
|
|
case 1:
|
|
{
|
|
return GetMasterId(hum->room) == hum->GetUniId();
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
return GetTeamId(hum->room) == hum->team_id;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool Obstacle::IsHalfWallCollider()
|
|
{
|
|
return meta->i->collision_hit() == kCollisionHitPass &&
|
|
meta->i->bullet_hit() != kBulletHitPass;
|
|
}
|
|
|
|
bool Obstacle::Attackable(Room* room)
|
|
{
|
|
if (IsDead(room)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Obstacle::ReceiveExplosionDmg(Explosion* explosion)
|
|
{
|
|
switch (meta->i->explosion_hit()) {
|
|
case kExplosionHitPass:
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case kExplosionHitAnyDmg:
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
case kExplosionHitOnlySpecDmg:
|
|
{
|
|
if (meta->receive_special_damage_type == 0) {
|
|
return true;
|
|
}
|
|
return (meta->receive_special_damage_type & explosion->GetSpecialDamageType()) != 0;
|
|
}
|
|
break;
|
|
case kExplosionHitEatDmg:
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Obstacle::ProcSpecEvent(Creature* c, ColliderComponent* collider)
|
|
{
|
|
switch (meta->i->thing_type()) {
|
|
case kObstacleSpring:
|
|
{
|
|
if (c->IsHuman()) {
|
|
if (!c->AsHuman()->GetCar()) {
|
|
AddObstacleBuff(c);
|
|
a8::SetBitFlag(c->status, CS_Collisioning);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kObstacleMine:
|
|
case kObstacleTrap:
|
|
{
|
|
if (c->team_id != GetTeamId(c->room)) {
|
|
AddObstacleBuff(c);
|
|
Die(c->room);
|
|
ProcDieExplosion(c->room);
|
|
BroadcastFullState(c->room);
|
|
}
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Obstacle::ProcDieExplosion(Room* room)
|
|
{
|
|
if (meta->i->damage_dia() > 0.01f) {
|
|
std::shared_ptr<Explosion> explosion = EntityFactory::Instance()->MakeExplosion();
|
|
explosion->IndifferenceAttack(
|
|
room,
|
|
GetPos(),
|
|
meta->i->damage_dia(),
|
|
meta->i->explosion_effect(),
|
|
meta->i->damage()
|
|
);
|
|
}
|
|
}
|