#include "precompile.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" 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); 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("脱离"); #else p->set_button_name("1"); #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("3"); #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 (!IsDead(bullet->room) && !IsTerminatorAirDropBox(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() * (1 + bullet->sender.Get()->GetAttrRate(kHAT_Atk)) + bullet->sender.Get()->GetAttrAbs(kHAT_Atk); 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)) { if (meta->i->damage_dia() > 0.01f && meta->i->damage() > 0.01f) { Explosion explosion; explosion.IndifferenceAttack( bullet->room, GetPos(), meta->i->damage_dia(), meta->i->damage(), meta->i->explosion_effect()); } bullet->sender.Get()->DropItems(this); } 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; } 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())) { if (meta->i->damage_dia() > 0.01f && meta->i->damage() > 0.01f) { Explosion explosion; explosion.IndifferenceAttack( e->GetRoom(), GetPos(), meta->i->damage_dia(), meta->i->damage(), meta->i->explosion_effect()); } if (meta->i->drop() != 0) { e->GetRoom()->ScatterDrop(GetPos(), meta->i->drop()); } } 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) { return 0; #if 0 opt_result = kOptPass; switch (meta->i->thing_type()) { case kObstacleSpring: { if (c->IsHuman()) { if (!c->AsHuman()->GetCar()) { AddObstacleBuff(c); a8::SetBitFlag(c->status, CS_Collisioning); opt_result = kOptBreak; } } } break; case kObstacleMine: case kObstacleTrap: { if (c->team_id != GetTeamId(c->room)) { AddObstacleBuff(c); #if 0 Explosion(); #endif Die(c->room); BroadcastFullState(c->room); } } break; default: { } break; } #endif #if 0 Global::last_collider = collider; #endif #if 0 if (!obstacle->IsDead(room) && #if 0 obstacle->Attackable(this) && #endif obstacle->meta->i->drop() != 0 && obstacle->IsTouchInteraction() && room->GetGasData().gas_mode != GasInactive && (!obstacle->IsTerminatorAirDropBox(room) || GetRace() == kHumanRace) ) { obstacle->Die(room); if (obstacle->IsDead(room)) { #if 0 if (obstacle->meta->i->damage_dia() > 0.01f && obstacle->meta->i->damage() > 0.01f) { obstacle->Explosion(this); } #endif DropItems(obstacle); } obstacle->BroadcastFullState(room); if (obstacle->IsTerminatorAirDropBox(room) && GetRace() == kHumanRace && !HasBuffEffect(kBET_Terminator)) { MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(TERMINATOR_BUFF_ID); if (buff_meta) { AddBuff(this, buff_meta, 1); } } #endif } 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>(); } (*data->interaction_humans)[sender->GetUniId()] = std::make_tuple (sender->room->GetFrameNo(), old_pos); } sender->AddToNewObjects(this); } std::tuple* 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_buff_vec2_param1 = c->buff_vec2_param1; c->buff_vec2_param1 = c->GetPos(); for (int buff_id : meta->buff_list) { c->TryAddBuff(c, buff_id); } c->buff_vec2_param1 = old_buff_vec2_param1; } 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; }