eqemu-server/zone/doors.cpp
Mitch Freeman f21cc170df
[Feature] Evolving Item Support for RoF2 (#4496)
* basic evolving items framework created

* Implement evolving tab in the inventory window

* Implement experience and number of kills

* Move zone evolving map to a evolvingitemsmanager class

* rework gm commands

* rework GetInventory

* wip

* wip loot testing

* Fix Duplicate Message

* reworked evolving item looting, swapping, etc

* reworked const functions for evolving methods

* Functioning Player Trade of evolving items test item_id is 89550

* First pass of Final Result link working

* First pass of item upgrading when reaching 100%

* Add strings and logic for displaying the evolving item xp transfer window in Corathus

* Prototype of xp transfer window sending items

* WIP for evolve xp transfer

* WIP for evolve xp transfer.  First tests passed

* XP Transfer Cleanup

* XP Transfer Cleanup

* Add Rule for evolving items equip timer/  default is 30 secs

* Add logging and player events

Add logging and player events

* Formatting

* Database updates

* Updates for linux build

* Perl/Cleanup

* Command cleanup

* Lua

* Added a crash condition check if final item id is blank or not found.

* Review Changes

Updates to resolve review comments and a rebase.

* migrate to content_db for items_evolving_details

migrate to content_db for items_evolving_details

* Simplify, don't hit database unless evolving

* Update 2025_01_19_items_evolving_details.sql

* Update client.cpp

* Update manifest with items_evolving_details

* character_id vs char_id

* Remove _Struct from structs

* Remove license header in evolving.cpp

* Move evolving constants from eq_constants.h to evolving.h since it is more specific

* Update database_schema.h

* General cleanup

* Be more specific with `evolving_items` vs `evolving`

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-01-19 18:10:19 -06:00

973 lines
26 KiB
C++

/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../common/global_define.h"
#include "../common/eqemu_logsys.h"
#include "../common/strings.h"
#include "../common/repositories/doors_repository.h"
#include "client.h"
#include "doors.h"
#include "entity.h"
#include "guild_mgr.h"
#include "mob.h"
#include "string_ids.h"
#include "worldserver.h"
#include "zonedb.h"
#include "../common/evolving_items.h"
#include "../common/repositories/criteria/content_filter_criteria.h"
#include <string.h>
#define OPEN_DOOR 0x02
#define CLOSE_DOOR 0x03
#define OPEN_INVDOOR 0x03
#define CLOSE_INVDOOR 0x02
extern EntityList entity_list;
extern WorldServer worldserver;
Doors::Doors(const DoorsRepository::Doors &door) :
m_close_timer(door.close_timer_ms),
m_position(door.pos_x, door.pos_y, door.pos_z, door.heading),
m_destination(door.dest_x, door.dest_y, door.dest_z, door.dest_heading)
{
strn0cpy(m_zone_name, door.zone.c_str(), sizeof(m_zone_name));
strn0cpy(m_door_name, door.name.c_str(), sizeof(m_door_name));
strn0cpy(m_destination_zone_name, door.dest_zone.c_str(), sizeof(m_destination_zone_name));
// destination helpers
if (!door.dest_zone.empty() && Strings::ToLower(door.dest_zone) != "none" && !door.dest_zone.empty()) {
m_has_destination_zone = true;
}
if (!door.dest_zone.empty() && !door.zone.empty() && Strings::EqualFold(door.dest_zone, door.zone)) {
m_same_destination_zone = true;
}
m_database_id = door.id;
m_door_id = door.doorid;
m_incline = door.incline;
m_open_type = door.opentype;
m_guild_id = door.guild;
m_lockpick = door.lockpick;
m_key_item_id = door.keyitem;
m_no_key_ring = door.nokeyring;
m_trigger_door = door.triggerdoor;
m_trigger_type = door.triggertype;
triggered = false;
m_door_param = door.door_param;
m_size = door.size;
m_invert_state = door.invert_state;
// if the target zone is the same as the current zone, use the instance of the current zone
// if we don't use the same instance_id that the client was sent, the client will forcefully
// issue a zone change request when they should be simply moving to a different point in the same zone
// because the client will think the zone point target is different from the current instance
if (door.dest_zone == zone->GetShortName() && m_destination_instance_id == 0) {
m_destination_instance_id = zone->GetInstanceID();
}
m_destination_instance_id = door.dest_instance;
m_is_ldon_door = door.is_ldon_door;
m_dz_switch_id = door.dz_switch_id;
m_client_version_mask = door.client_version_mask;
SetOpenState(false);
m_close_timer.Disable();
m_disable_timer = (door.disable_timer == 1 ? true : false);
m_is_blacklisted_to_open = GetIsDoorBlacklisted();
}
Doors::Doors(const char *model, const glm::vec4 &position, uint8 open_type, uint16 size) :
m_close_timer(5000),
m_position(position),
m_destination(glm::vec4())
{
strn0cpy(m_zone_name, zone->GetShortName(), 32);
strn0cpy(m_door_name, model, 32);
strn0cpy(m_destination_zone_name, "NONE", 32);
m_database_id = content_db.GetDoorsCountPlusOne();
m_door_id = content_db.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion());
m_open_type = open_type;
m_size = size;
m_incline = 0;
m_guild_id = 0;
m_lockpick = 0;
m_key_item_id = 0;
m_no_key_ring = 0;
m_trigger_door = 0;
m_trigger_type = 0;
triggered = false;
m_door_param = 0;
m_invert_state = 0;
m_is_ldon_door = 0;
m_client_version_mask = 4294967295u;
m_disable_timer = 0;
m_destination_instance_id = 0;
SetOpenState(false);
m_close_timer.Disable();
}
Doors::~Doors()
{
}
bool Doors::Process()
{
if (m_close_timer.Enabled() && m_close_timer.Check() && IsDoorOpen()) {
LogDoorsDetail("door open and timer triggered door_id [{}] open_type [{}]", GetDoorID(), m_open_type);
if (m_open_type == 40 || GetTriggerType() == 1) {
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct *md = (MoveDoor_Struct *) outapp->pBuffer;
md->doorid = m_door_id;
md->action = m_invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR;
entity_list.QueueClients(0, outapp);
safe_delete(outapp);
}
triggered = false;
m_close_timer.Disable();
SetOpenState(false);
}
return true;
}
void Doors::HandleClick(Client *sender, uint8 trigger)
{
Log(Logs::Detail, Logs::Doors,
"%s clicked door %s (dbid %d, eqid %d) at %s",
sender->GetName(),
m_door_name,
m_database_id,
m_door_id,
to_string(m_position).c_str()
);
Log(Logs::Detail, Logs::Doors,
"incline %d, open_type %d, lockpick %d, key %d, nokeyring %d, trigger %d type %d, param %d",
m_incline,
m_open_type,
m_lockpick,
m_key_item_id,
m_no_key_ring,
m_trigger_door,
m_trigger_type,
m_door_param
);
Log(Logs::Detail, Logs::Doors,
"disable_timer '%s',size %d, invert %d, dest: %s %s",
(m_disable_timer ? "true" : "false"),
m_size,
m_invert_state,
m_destination_zone_name,
to_string(m_destination).c_str()
);
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
auto *move_door_packet = (MoveDoor_Struct *) outapp->pBuffer;
move_door_packet->doorid = m_door_id;
if (IsLDoNDoor()) {
if (sender) {
if (RuleI(Adventure, ItemIDToEnablePorts) != 0) {
if (!sender->KeyRingCheck(RuleI(Adventure, ItemIDToEnablePorts))) {
if (sender->GetInv().HasItem(RuleI(Adventure, ItemIDToEnablePorts)) == INVALID_INDEX) {
sender->MessageString(Chat::Red, DUNGEON_SEALED);
safe_delete(outapp);
return;
}
else {
sender->KeyRingAdd(RuleI(Adventure, ItemIDToEnablePorts));
}
}
}
if (!sender->GetPendingAdventureDoorClick()) {
sender->PendingAdventureDoorClick();
auto pack = new ServerPacket(
ServerOP_AdventureClickDoor,
sizeof(ServerPlayerClickedAdventureDoor_Struct)
);
/**
* Adventure door
*/
ServerPlayerClickedAdventureDoor_Struct *adventure_door_click;
adventure_door_click = (ServerPlayerClickedAdventureDoor_Struct *) pack->pBuffer;
strcpy(adventure_door_click->player, sender->GetName());
adventure_door_click->zone_id = zone->GetZoneID();
adventure_door_click->id = GetDoorDBID();
worldserver.SendPacket(pack);
safe_delete(pack);
}
safe_delete(outapp);
return;
}
}
if (sender && m_dz_switch_id != 0) {
sender->UpdateTasksOnTouchSwitch(m_dz_switch_id);
if (sender->TryMovePCDynamicZoneSwitch(m_dz_switch_id)) {
safe_delete(outapp);
return;
}
}
uint32 required_key_item = GetKeyItem();
uint8 disable_add_to_key_ring = GetNoKeyring();
bool player_has_key = false;
uint32 player_key = 0;
const EQ::ItemInstance *lock_pick_item = sender->GetInv().GetItem(EQ::invslot::slotCursor);
// If classic key on cursor rule, check for it, otherwise owning it ok.
if (RuleB(Doors, RequireKeyOnCursor)) {
if (lock_pick_item != nullptr &&
lock_pick_item->GetItem()->ID == required_key_item) {
player_has_key = true;
}
}
else if (sender->GetInv().HasItem(required_key_item, 1) != INVALID_INDEX) {
player_has_key = true;
}
if (player_has_key) {
player_key = required_key_item;
}
/**
* Object is not triggered
*/
if (GetTriggerType() == 255) {
/**
* Door is only triggered by an object
*/
if (trigger == 1) {
if (!IsDoorOpen() || (m_open_type == 58)) {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
}
else {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
}
}
else {
safe_delete(outapp);
return;
}
}
// enforce flags before they hit zoning process
auto z = GetZone(m_destination_zone_name, 0);
if (z && !z->flag_needed.empty() && Strings::IsNumber(z->flag_needed) && Strings::ToInt(z->flag_needed) == 1) {
if (!sender->HasZoneFlag(z->zoneidnumber)) {
if (!sender->GetGM()) {
LogInfo(
"Character [{}] does not have the flag to be in this zone [{}]!",
sender->GetCleanName(),
z->flag_needed
);
sender->MessageString(Chat::LightBlue, DOORS_LOCKED);
} else {
sender->Message(Chat::White, "Your GM flag allows you to use this door.");
}
}
}
/**
* Guild Doors
*
* Door is not locked
*/
bool is_guild_door = (GetGuildID() > 0) && (GetGuildID() == sender->GuildID());
bool is_door_not_locked = ((required_key_item == 0) && (GetLockpick() == 0) && (GetGuildID() == 0));
bool is_door_open_and_open_able = (IsDoorOpen() && (m_open_type == 58));
if (is_door_not_locked || is_door_open_and_open_able || is_guild_door) {
if (!IsDoorOpen() || (GetOpenType() == 58)) {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
}
else {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
}
}
else {
/**
* Guild Doors
*/
if (GetGuildID() > 0) {
std::string guild_name;
const bool has_guild_name = guild_mgr.GetGuildNameByID(m_guild_id, guild_name);
if (!sender->GetGM()) {
std::string door_message;
if (has_guild_name) {
door_message = fmt::format(
"Only members of the <{}> guild may enter here.",
guild_name
);
} else {
door_message = "Door is locked by an unknown guild.";
}
sender->Message(Chat::LightBlue, door_message.c_str());
safe_delete(outapp);
return;
} else {
sender->Message(
Chat::White,
fmt::format(
"Your GM flag allows you to use this door{}.",
(
has_guild_name ?
fmt::format(
" assigned to the <{}> guild",
guild_name
) :
""
)
).c_str()
);
}
}
/**
* Key required
* If using a lock_pick_item leave messaging to that code below
*/
if (lock_pick_item == nullptr && !IsDoorOpen()) {
sender->Message(Chat::LightBlue, "This is locked...");
}
/**
* GM can always open locks
*/
if (sender->GetGM()) {
sender->MessageString(Chat::LightBlue, DOORS_GM);
if (!IsDoorOpen() || (m_open_type == 58)) {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
}
else {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
}
}
/**
* Player has something they are trying to open it with
*/
else if (player_key) {
/**
* Key required and client is using the right key
*/
if (required_key_item &&
(required_key_item == player_key)) {
if (!disable_add_to_key_ring) {
sender->KeyRingAdd(player_key);
}
sender->Message(Chat::LightBlue, "You got it open!");
if (!IsDoorOpen() || (m_open_type == 58)) {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
}
else {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
}
}
}
/**
* Try Lock pick
*/
else if (lock_pick_item != nullptr) {
if (sender->GetSkill(EQ::skills::SkillPickLock)) {
Timer *pick_lock_timer = sender->GetPickLockTimer();
if (lock_pick_item->GetItem()->ItemType == EQ::item::ItemTypeLockPick) {
if (!pick_lock_timer->Check()) {
// Stop full scale mad spamming
safe_delete(outapp);
return;
}
float player_pick_lock_skill = sender->GetSkill(EQ::skills::SkillPickLock);
LogSkills("Client has lockpicks: skill=[{}]", player_pick_lock_skill);
if (GetLockpick() <= player_pick_lock_skill) {
// Stop full scale spamming
pick_lock_timer->Start(1000, true);
if (!IsDoorOpen()) {
sender->CheckIncreaseSkill(EQ::skills::SkillPickLock, nullptr, 1);
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? OPEN_DOOR
: OPEN_INVDOOR);
sender->MessageString(Chat::LightBlue, DOORS_SUCCESSFUL_PICK);
}
else {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? CLOSE_DOOR
: CLOSE_INVDOOR);
}
}
else {
sender->MessageString(Chat::LightBlue, DOORS_INSUFFICIENT_SKILL);
safe_delete(outapp);
return;
}
}
else {
sender->MessageString(Chat::LightBlue, DOORS_NO_PICK);
safe_delete(outapp);
return;
}
}
else {
sender->MessageString(Chat::LightBlue, DOORS_CANT_PICK);
safe_delete(outapp);
return;
}
}
/**
* Locked door and nothing to open it with
*/
else {
/**
* Search for key on keyring
*/
if (sender->KeyRingCheck(required_key_item)) {
player_key = required_key_item;
sender->Message(Chat::LightBlue, "You got it open!"); // more debug spam
if (!IsDoorOpen() || (m_open_type == 58)) {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
}
else {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
}
}
else {
sender->MessageString(Chat::LightBlue, DOORS_LOCKED);
safe_delete(outapp);
return;
}
}
}
entity_list.QueueClients(sender, outapp, false);
if (!IsDoorOpen() || (m_open_type == 58)) {
if (!m_disable_timer) {
m_close_timer.Start();
}
if (!HasDestinationZone()) {
SetOpenState(true);
}
}
else {
m_close_timer.Disable();
if (!m_disable_timer) {
SetOpenState(false);
}
}
/*
* Everything past this point assumes we opened the door
* and met all the requirements for opening
* everything to do with closed doors has already been taken care of
* we return because we don't want people using teleports on an unlocked door (exploit!)
*/
if ((move_door_packet->action == CLOSE_DOOR && m_invert_state == 0) ||
(move_door_packet->action == CLOSE_INVDOOR && m_invert_state == 1)) {
safe_delete(outapp);
return;
}
safe_delete(outapp);
if ((GetTriggerDoorID() != 0) && (GetTriggerType() == 1)) {
Doors *trigger_door_entity = entity_list.FindDoor(GetTriggerDoorID());
if (trigger_door_entity && !trigger_door_entity->triggered) {
triggered = true;
trigger_door_entity->HandleClick(sender, 1);
}
else {
triggered = false;
}
}
else if ((GetTriggerDoorID() != 0) && (GetTriggerType() != 1)) {
Doors *trigger_door_entity = entity_list.FindDoor(GetTriggerDoorID());
if (trigger_door_entity && !trigger_door_entity->triggered) {
triggered = true;
trigger_door_entity->HandleClick(sender, 0);
}
else {
triggered = false;
}
}
// teleport door
if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) {
bool has_key_required = (required_key_item && required_key_item == player_key);
if (sender->GetGM() && !has_key_required) {
has_key_required = true;
sender->Message(Chat::White, "Your GM flag allows you to open this door without a key.");
}
if (IsDestinationZoneSame() && (!required_key_item)) {
if (!disable_add_to_key_ring) {
sender->KeyRingAdd(player_key);
}
sender->MovePC(
zone->GetZoneID(),
zone->GetInstanceID(),
m_destination.x,
m_destination.y,
m_destination.z,
m_destination.w
);
}
else if ((!IsDoorOpen() || m_open_type == 58) && has_key_required) {
if (!disable_add_to_key_ring) {
sender->KeyRingAdd(player_key);
}
if (ZoneID(m_destination_zone_name) == zone->GetZoneID()) {
sender->MovePC(
zone->GetZoneID(),
zone->GetInstanceID(),
m_destination.x,
m_destination.y,
m_destination.z,
m_destination.w
);
}
else {
sender->MovePC(
ZoneID(m_destination_zone_name),
static_cast<uint32>(m_destination_instance_id),
m_destination.x,
m_destination.y,
m_destination.z,
m_destination.w
);
}
}
if ((!IsDoorOpen() || m_open_type == 58) && (!required_key_item)) {
if (ZoneID(m_destination_zone_name) == zone->GetZoneID()) {
sender->MovePC(
zone->GetZoneID(),
zone->GetInstanceID(),
m_destination.x,
m_destination.y,
m_destination.z,
m_destination.w
);
}
else {
sender->MovePC(
ZoneID(m_destination_zone_name),
static_cast<uint32>(m_destination_instance_id),
m_destination.x,
m_destination.y,
m_destination.z,
m_destination.w
);
}
}
}
if (GetOpenType() == 40 && GetZone(GetDoorZone(),0)) {
sender->SendEvolveXPTransferWindow();
}
}
void Doors::Open(Mob *sender, bool alt_mode)
{
if (sender) {
if (GetTriggerType() == 255 || GetTriggerDoorID() > 0 || GetLockpick() != 0 || GetKeyItem() != 0 ||
m_open_type == 59 || m_open_type == 58 ||
!sender->IsNPC()) { // this object isnt triggered or door is locked - NPCs should not open locked doors!
return;
}
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct *md = (MoveDoor_Struct *) outapp->pBuffer;
md->doorid = m_door_id;
md->action = m_invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR;
entity_list.QueueCloseClients(sender, outapp, false, 200);
safe_delete(outapp);
if (!alt_mode) { // original function
if (!m_is_open) {
if (!m_disable_timer) {
m_close_timer.Start();
}
m_is_open = true;
}
else {
m_close_timer.Disable();
if (!m_disable_timer) {
m_is_open = false;
}
}
}
else { // alternative function
if (!m_disable_timer) {
m_close_timer.Start();
}
m_is_open = true;
}
}
}
void Doors::ForceOpen(Mob *sender, bool alt_mode)
{
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct *md = (MoveDoor_Struct *) outapp->pBuffer;
md->doorid = m_door_id;
md->action = m_invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR;
entity_list.QueueClients(sender, outapp, false);
safe_delete(outapp);
if (!alt_mode) { // original function
if (!m_is_open) {
if (!m_disable_timer) {
LogDoorsDetail("door_id [{}] starting timer", m_door_id);
m_close_timer.Start();
}
m_is_open = true;
}
else {
LogDoorsDetail("door_id [{}] disable timer", m_door_id);
m_close_timer.Disable();
if (!m_disable_timer) {
m_is_open = false;
}
}
}
else { // alternative function
if (!m_disable_timer) {
LogDoorsDetail("door_id [{}] alt starting timer", m_door_id);
m_close_timer.Start();
}
m_is_open = true;
}
}
void Doors::ForceClose(Mob *sender, bool alt_mode)
{
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct *move_door_packet;
move_door_packet = (MoveDoor_Struct *) outapp->pBuffer;
move_door_packet->doorid = m_door_id;
move_door_packet->action = m_invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; // change from original (open to close)
entity_list.QueueClients(sender, outapp, false);
safe_delete(outapp);
if (!alt_mode) { // original function
if (!m_is_open) {
if (!m_disable_timer) {
m_close_timer.Start();
}
m_is_open = true;
}
else {
m_close_timer.Disable();
m_is_open = false;
}
}
else { // alternative function
if (m_is_open) {
m_close_timer.Trigger();
}
}
}
void Doors::ToggleState(Mob *sender)
{
if (GetTriggerDoorID() > 0 || GetLockpick() != 0 || GetKeyItem() != 0 || m_open_type == 58 ||
m_open_type == 40) { // borrowed some NPCOpen criteria
return;
}
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct *move_door_packet;
move_door_packet = (MoveDoor_Struct *) outapp->pBuffer;
move_door_packet->doorid = m_door_id;
if (!m_is_open) {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
m_is_open = true;
}
else {
move_door_packet->action = static_cast<uint8>(m_invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
m_is_open = false;
}
entity_list.QueueClients(sender, outapp, false);
safe_delete(outapp);
}
uint32 ZoneDatabase::GetDoorsCountPlusOne()
{
const uint32 max_door_id = static_cast<uint32>(DoorsRepository::GetMaxId(*this));
return (max_door_id + 1);
}
int ZoneDatabase::GetDoorsDBCountPlusOne(std::string zone_short_name, int16 version)
{
const auto query = fmt::format(
"SELECT COALESCE(MAX(doorid), 1) FROM doors "
"WHERE zone = '{}' AND (version = {} OR version = -1)",
zone_short_name,
version
);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
return -1;
}
auto row = results.begin();
if (!row[0]) {
return 0;
}
return Strings::ToInt(row[0]) + 1;
}
std::vector<DoorsRepository::Doors> ZoneDatabase::LoadDoors(const std::string &zone_name, int16 version)
{
auto door_entries = DoorsRepository::GetWhere(
*this, fmt::format(
"zone = '{}' AND (version = {} OR version = -1) {} ORDER BY doorid ASC",
zone_name, version, ContentFilterCriteria::apply()));
LogDoors("Loaded [{}] doors for [{}] version [{}]", Strings::Commify(door_entries.size()), zone_name, version);
return door_entries;
}
void Doors::SetLocation(float x, float y, float z)
{
entity_list.DespawnAllDoors();
m_position = glm::vec4(x, y, z, m_position.w);
entity_list.RespawnAllDoors();
}
void Doors::SetPosition(const glm::vec4 &position)
{
entity_list.DespawnAllDoors();
m_position = position;
entity_list.RespawnAllDoors();
}
void Doors::SetIncline(int in)
{
entity_list.DespawnAllDoors();
m_incline = in;
entity_list.RespawnAllDoors();
}
void Doors::SetInvertState(int in)
{
entity_list.DespawnAllDoors();
m_invert_state = in;
entity_list.RespawnAllDoors();
}
void Doors::SetOpenType(uint8 in)
{
entity_list.DespawnAllDoors();
m_open_type = in;
entity_list.RespawnAllDoors();
}
void Doors::SetDoorName(const char *name)
{
entity_list.DespawnAllDoors();
memset(m_door_name, 0, sizeof(m_door_name));
strncpy(m_door_name, name, sizeof(m_door_name));
entity_list.RespawnAllDoors();
}
void Doors::SetSize(uint16 in)
{
entity_list.DespawnAllDoors();
m_size = in;
entity_list.RespawnAllDoors();
}
void Doors::SetDisableTimer(bool flag)
{
m_disable_timer = flag;
}
void Doors::CreateDatabaseEntry()
{
if (content_db.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()) - 1 >= 255) {
return;
}
const auto& l = DoorsRepository::GetWhere(
content_db,
fmt::format(
"zone = '{}' AND doorid = {}",
zone->GetShortName(),
GetDoorID()
)
);
if (!l.empty()) {
auto e = l[0];
e.name = GetDoorName();
e.pos_x = GetX();
e.pos_y = GetY();
e.pos_z = GetZ();
e.heading = GetHeading();
e.opentype = GetOpenType();
e.guild = static_cast<uint16>(GetGuildID());
e.lockpick = GetLockpick();
e.keyitem = GetKeyItem();
e.door_param = static_cast<uint8>(GetDoorParam());
e.invert_state = static_cast<uint8>(GetInvertState());
e.incline = GetIncline();
e.size = GetSize();
auto updated = DoorsRepository::UpdateOne(content_db, e);
if (!updated) {
LogError(
"Failed to update door in Zone [{}] Version [{}] Database ID [{}] ID [{}]",
zone->GetShortName(),
zone->GetInstanceVersion(),
GetDoorDBID(),
GetDoorID()
);
}
return;
}
auto e = DoorsRepository::NewEntity();
e.id = GetDoorDBID();
e.doorid = GetDoorID();
e.zone = zone->GetShortName();
e.version = zone->GetInstanceVersion();
e.name = GetDoorName();
e.pos_x = GetX();
e.pos_y = GetY();
e.pos_z = GetZ();
e.heading = GetHeading();
e.opentype = GetOpenType();
e.guild = static_cast<uint16>(GetGuildID());
e.lockpick = GetLockpick();
e.keyitem = GetKeyItem();
e.door_param = static_cast<uint8>(GetDoorParam());
e.invert_state = static_cast<uint8>(GetInvertState());
e.incline = GetIncline();
e.size = GetSize();
const auto& n = DoorsRepository::InsertOne(content_db, e);
if (!n.id) {
LogError(
"Failed to create door in Zone [{}] Version [{}] Database ID [{}] ID [{}]",
zone->GetShortName(),
zone->GetInstanceVersion(),
GetDoorDBID(),
GetDoorID()
);
}
}
float Doors::GetX()
{
return m_position.x;
}
float Doors::GetY()
{
return m_position.y;
}
float Doors::GetZ()
{
return m_position.z;
}
float Doors::GetHeading()
{
return m_position.w;
}
bool Doors::HasDestinationZone() const
{
return m_has_destination_zone;
}
bool Doors::IsDestinationZoneSame() const
{
return m_same_destination_zone;
}
// IsDoorBlacklisted has a static list of doors that are blacklisted
// from being opened by NPCs. This is used to prevent NPCs from opening
// doors that are not meant to be opened by NPCs.
bool Doors::GetIsDoorBlacklisted()
{
std::vector<std::string> blacklist = {
"TOGGLE",
"PNDRESSER101",
};
for (auto& name : blacklist) {
std::string door_name = GetDoorName();
if (name == door_name) {
return true;
}
}
return false;
}
bool Doors::IsDoorBlacklisted() {
return m_is_blacklisted_to_open;
}