zaela-Server/zone/object.cpp

1070 lines
27 KiB
C++
Raw Permalink Normal View History

2013-05-09 10:44:08 -04:00
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net)
2013-02-16 16:14:39 -08:00
2013-05-09 10:44:08 -04:00
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.
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
2013-02-16 16:14:39 -08:00
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
2013-05-09 10:44:08 -04:00
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
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
2013-02-16 16:14:39 -08:00
*/
#include "../common/global_define.h"
2014-08-21 19:33:02 -07:00
#include "../common/string_util.h"
2014-12-15 18:03:33 -06:00
#include "client.h"
#include "entity.h"
#include "mob.h"
#include "object.h"
2013-02-16 16:14:39 -08:00
2014-08-21 23:46:01 -07:00
#include "quest_parser_collection.h"
2014-12-15 18:03:33 -06:00
#include "zonedb.h"
#include <iostream>
2013-02-16 16:14:39 -08:00
const char DEFAULT_OBJECT_NAME[] = "IT63_ACTORDEF";
const char DEFAULT_OBJECT_NAME_SUFFIX[] = "_ACTORDEF";
extern Zone* zone;
extern EntityList entity_list;
// Loading object from database
Object::Object(uint32 id, uint32 type, uint32 icon, const Object_Struct& object, const EQEmu::ItemInstance* inst)
2013-02-16 16:14:39 -08:00
: respawn_timer(0), decay_timer(300000)
{
2013-05-06 13:07:41 -04:00
2013-05-04 18:06:58 -07:00
user = nullptr;
last_user = nullptr;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Initialize members
m_id = id;
m_type = type;
m_icon = icon;
m_inuse = false;
2013-05-04 18:06:58 -07:00
m_inst = nullptr;
2013-02-16 16:14:39 -08:00
m_ground_spawn=false;
// Copy object data
memcpy(&m_data, &object, sizeof(Object_Struct));
if (inst) {
m_inst = inst->Clone();
decay_timer.Start();
} else {
decay_timer.Disable();
}
respawn_timer.Disable();
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Set drop_id to zero - it will be set when added to zone with SetID()
m_data.drop_id = 0;
m_data.size = object.size;
m_data.tilt_x = object.tilt_x;
m_data.tilt_y = object.tilt_y;
2013-02-16 16:14:39 -08:00
}
//creating a re-ocurring ground spawn.
Object::Object(const EQEmu::ItemInstance* inst, char* name,float max_x,float min_x,float max_y,float min_y,float z,float heading,uint32 respawntimer)
: respawn_timer(respawntimer * 1000), decay_timer(300000)
2013-02-16 16:14:39 -08:00
{
2013-05-06 13:07:41 -04:00
2013-05-04 18:06:58 -07:00
user = nullptr;
last_user = nullptr;
2013-02-16 16:14:39 -08:00
m_max_x=max_x;
m_max_y=max_y;
m_min_x=min_x;
m_min_y=min_y;
m_id = 0;
2013-05-04 18:06:58 -07:00
m_inst = (inst) ? inst->Clone() : nullptr;
2013-02-16 16:14:39 -08:00
m_type = OT_DROPPEDITEM;
m_icon = 0;
m_inuse = false;
m_ground_spawn = true;
decay_timer.Disable();
// Set as much struct data as we can
memset(&m_data, 0, sizeof(Object_Struct));
m_data.heading = heading;
m_data.z = z;
m_data.zone_id = zone->GetZoneID();
respawn_timer.Disable();
strcpy(m_data.object_name, name);
RandomSpawn(false);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Hardcoded portion for unknown members
m_data.unknown024 = 0x7f001194;
m_data.unknown076 = 0x0000d5fe;
m_data.unknown084 = 0xFFFFFFFF;
}
// Loading object from client dropping item on ground
Object::Object(Client* client, const EQEmu::ItemInstance* inst)
2013-02-16 16:14:39 -08:00
: respawn_timer(0), decay_timer(300000)
{
2013-05-04 18:06:58 -07:00
user = nullptr;
last_user = nullptr;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Initialize members
m_id = 0;
2013-05-04 18:06:58 -07:00
m_inst = (inst) ? inst->Clone() : nullptr;
2013-02-16 16:14:39 -08:00
m_type = OT_DROPPEDITEM;
m_icon = 0;
m_inuse = false;
m_ground_spawn = false;
// Set as much struct data as we can
memset(&m_data, 0, sizeof(Object_Struct));
m_data.heading = client->GetHeading();
m_data.x = client->GetX();
m_data.y = client->GetY();
if (client->ClientVersion() >= EQEmu::versions::ClientVersion::RoF2)
{
// RoF2 places items at player's Z, which is 0.625 of their height.
m_data.z = client->GetZ() - (client->GetSize() * 0.625f);
}
else
{
m_data.z = client->GetZ();
}
2013-02-16 16:14:39 -08:00
m_data.zone_id = zone->GetZoneID();
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
decay_timer.Start();
respawn_timer.Disable();
// Hardcoded portion for unknown members
m_data.unknown024 = 0x7f001194;
m_data.unknown076 = 0x0000d5fe;
m_data.unknown084 = 0xFFFFFFFF;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Set object name
if (inst) {
const EQEmu::ItemData* item = inst->GetItem();
2013-02-16 16:14:39 -08:00
if (item && item->IDFile) {
if (strlen(item->IDFile) == 0) {
strcpy(m_data.object_name, DEFAULT_OBJECT_NAME);
}
else {
// Object name is idfile + _ACTORDEF
uint32 len_idfile = strlen(inst->GetItem()->IDFile);
uint32 len_copy = sizeof(m_data.object_name) - len_idfile - 1;
if (len_copy > sizeof(DEFAULT_OBJECT_NAME_SUFFIX)) {
len_copy = sizeof(DEFAULT_OBJECT_NAME_SUFFIX);
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
memcpy(&m_data.object_name[0], inst->GetItem()->IDFile, len_idfile);
memcpy(&m_data.object_name[len_idfile], DEFAULT_OBJECT_NAME_SUFFIX, len_copy);
}
}
else {
strcpy(m_data.object_name, DEFAULT_OBJECT_NAME);
}
}
}
Object::Object(const EQEmu::ItemInstance *inst, float x, float y, float z, float heading, uint32 decay_time)
2013-02-16 16:14:39 -08:00
: respawn_timer(0), decay_timer(decay_time)
{
2013-05-04 18:06:58 -07:00
user = nullptr;
last_user = nullptr;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Initialize members
m_id = 0;
2013-05-04 18:06:58 -07:00
m_inst = (inst) ? inst->Clone() : nullptr;
2013-02-16 16:14:39 -08:00
m_type = OT_DROPPEDITEM;
m_icon = 0;
m_inuse = false;
m_ground_spawn = false;
// Set as much struct data as we can
memset(&m_data, 0, sizeof(Object_Struct));
m_data.heading = heading;
m_data.x = x;
m_data.y = y;
m_data.z = z;
m_data.zone_id = zone->GetZoneID();
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (decay_time)
decay_timer.Start();
respawn_timer.Disable();
// Hardcoded portion for unknown members
m_data.unknown024 = 0x7f001194;
m_data.unknown076 = 0x0000d5fe;
m_data.unknown084 = 0xFFFFFFFF;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Set object name
if (inst) {
const EQEmu::ItemData* item = inst->GetItem();
2013-02-16 16:14:39 -08:00
if (item && item->IDFile) {
if (strlen(item->IDFile) == 0) {
strcpy(m_data.object_name, DEFAULT_OBJECT_NAME);
}
else {
// Object name is idfile + _ACTORDEF
uint32 len_idfile = strlen(inst->GetItem()->IDFile);
uint32 len_copy = sizeof(m_data.object_name) - len_idfile - 1;
if (len_copy > sizeof(DEFAULT_OBJECT_NAME_SUFFIX)) {
len_copy = sizeof(DEFAULT_OBJECT_NAME_SUFFIX);
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
memcpy(&m_data.object_name[0], inst->GetItem()->IDFile, len_idfile);
memcpy(&m_data.object_name[len_idfile], DEFAULT_OBJECT_NAME_SUFFIX, len_copy);
}
}
else {
strcpy(m_data.object_name, DEFAULT_OBJECT_NAME);
}
}
}
Object::Object(const char *model, float x, float y, float z, float heading, uint8 type, uint32 decay_time)
: respawn_timer(0), decay_timer(decay_time)
{
2013-05-04 18:06:58 -07:00
user = nullptr;
last_user = nullptr;
2017-01-21 01:47:06 -05:00
EQEmu::ItemInstance* inst = new EQEmu::ItemInstance(ItemInstWorldContainer);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Initialize members
m_id = 0;
2013-05-04 18:06:58 -07:00
m_inst = (inst) ? inst->Clone() : nullptr;
2013-02-16 16:14:39 -08:00
m_type = type;
m_icon = 0;
m_inuse = false;
m_ground_spawn = false;
// Set as much struct data as we can
memset(&m_data, 0, sizeof(Object_Struct));
m_data.heading = heading;
m_data.x = x;
m_data.y = y;
m_data.z = z;
m_data.zone_id = zone->GetZoneID();
if (decay_time)
decay_timer.Start();
respawn_timer.Disable();
//Hardcoded portion for unknown members
m_data.unknown024 = 0x7f001194;
m_data.unknown076 = 0x0000d5fe;
m_data.unknown084 = 0xFFFFFFFF;
if(model)
strcpy(m_data.object_name, model);
else
strcpy(m_data.object_name, "IT64_ACTORDEF"); //default object name if model isn't specified for some unknown reason
2017-01-21 01:47:06 -05:00
safe_delete(inst);
2013-02-16 16:14:39 -08:00
}
Object::~Object()
{
safe_delete(m_inst);
2013-05-04 18:06:58 -07:00
if(user != nullptr) {
user->SetTradeskillObject(nullptr);
2013-02-16 16:14:39 -08:00
}
}
void Object::SetID(uint16 set_id)
{
// Invoke base class
Entity::SetID(set_id);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Store new id as drop_id
m_data.drop_id = (uint32)this->GetID();
}
// Reset state of object back to zero
void Object::ResetState()
{
safe_delete(m_inst);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
m_id = 0;
m_type = 0;
m_icon = 0;
memset(&m_data, 0, sizeof(Object_Struct));
}
bool Object::Save()
{
if (m_id) {
// Update existing
database.UpdateObject(m_id, m_type, m_icon, m_data, m_inst);
}
else {
// Doesn't yet exist, add now
m_id = database.AddObject(m_type, m_icon, m_data, m_inst);
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return true;
}
uint16 Object::VarSave()
{
if (m_id) {
// Update existing
database.UpdateObject(m_id, m_type, m_icon, m_data, m_inst);
}
else {
// Doesn't yet exist, add now
m_id = database.AddObject(m_type, m_icon, m_data, m_inst);
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
return m_id;
}
// Remove object from database
void Object::Delete(bool reset_state)
{
if (m_id != 0) {
database.DeleteObject(m_id);
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (reset_state) {
ResetState();
}
}
const EQEmu::ItemInstance* Object::GetItem(uint8 index) {
if (index < EQEmu::legacy::TYPE_WORLD_SIZE) {
return m_inst->GetItem(index);
}
return nullptr;
}
2013-02-16 16:14:39 -08:00
// Add item to object (only logical for world tradeskill containers
void Object::PutItem(uint8 index, const EQEmu::ItemInstance* inst)
2013-02-16 16:14:39 -08:00
{
if (index > 9) {
Log(Logs::General, Logs::Error, "Object::PutItem: Invalid index specified (%i)", index);
2013-02-16 16:14:39 -08:00
return;
}
2013-05-06 13:07:41 -04:00
if (m_inst && m_inst->IsType(EQEmu::item::ItemClassBag)) {
2013-02-16 16:14:39 -08:00
if (inst) {
m_inst->PutItem(index, *inst);
}
else {
m_inst->DeleteItem(index);
}
database.SaveWorldContainer(zone->GetZoneID(),m_id,m_inst);
// This is _highly_ inefficient, but for now it will work: Save entire object to database
Save();
}
}
void Object::Close() {
m_inuse = false;
2013-05-04 18:06:58 -07:00
if(user != nullptr)
2013-02-16 16:14:39 -08:00
{
last_user = user;
// put any remaining items from the world container back into the player's inventory to avoid item loss
2013-05-09 10:44:08 -04:00
// if they close the container without removing all items
EQEmu::ItemInstance* container = this->m_inst;
2013-05-04 18:06:58 -07:00
if(container != nullptr)
2013-02-16 16:14:39 -08:00
{
2016-10-16 21:36:39 -04:00
for (uint8 i = EQEmu::inventory::containerBegin; i < EQEmu::inventory::ContainerCount; i++)
2013-02-16 16:14:39 -08:00
{
EQEmu::ItemInstance* inst = container->PopItem(i);
2013-05-04 18:06:58 -07:00
if(inst != nullptr)
2013-02-16 16:14:39 -08:00
{
user->MoveItemToInventory(inst, true);
}
}
}
2013-05-04 18:06:58 -07:00
user->SetTradeskillObject(nullptr);
2013-02-16 16:14:39 -08:00
}
2013-05-04 18:06:58 -07:00
user = nullptr;
2013-02-16 16:14:39 -08:00
}
// Remove item from container
void Object::DeleteItem(uint8 index)
{
if (m_inst && m_inst->IsType(EQEmu::item::ItemClassBag)) {
2013-02-16 16:14:39 -08:00
m_inst->DeleteItem(index);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// This is _highly_ inefficient, but for now it will work: Save entire object to database
Save();
}
}
// Pop item out of container
EQEmu::ItemInstance* Object::PopItem(uint8 index)
2013-02-16 16:14:39 -08:00
{
EQEmu::ItemInstance* inst = nullptr;
2013-05-06 13:07:41 -04:00
if (m_inst && m_inst->IsType(EQEmu::item::ItemClassBag)) {
2013-02-16 16:14:39 -08:00
inst = m_inst->PopItem(index);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// This is _highly_ inefficient, but for now it will work: Save entire object to database
Save();
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return inst;
}
void Object::CreateSpawnPacket(EQApplicationPacket* app)
{
app->SetOpcode(OP_GroundSpawn);
app->pBuffer = new uchar[sizeof(Object_Struct)];
app->size = sizeof(Object_Struct);
memcpy(app->pBuffer, &m_data, sizeof(Object_Struct));
}
void Object::CreateDeSpawnPacket(EQApplicationPacket* app)
{
app->SetOpcode(OP_ClickObject);
app->pBuffer = new uchar[sizeof(ClickObject_Struct)];
app->size = sizeof(ClickObject_Struct);
memset(app->pBuffer, 0, sizeof(ClickObject_Struct));
ClickObject_Struct* co = (ClickObject_Struct*) app->pBuffer;
co->drop_id = m_data.drop_id;
co->player_id = 0;
}
bool Object::Process(){
if(m_type == OT_DROPPEDITEM && decay_timer.Enabled() && decay_timer.Check()) {
// Send click to all clients (removes entity on client)
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct));
2013-02-16 16:14:39 -08:00
ClickObject_Struct* click_object = (ClickObject_Struct*)outapp->pBuffer;
click_object->drop_id = GetID();
2013-05-04 18:06:58 -07:00
entity_list.QueueClients(nullptr, outapp, false);
2013-02-16 16:14:39 -08:00
safe_delete(outapp);
// Remove object
database.DeleteObject(m_id);
return false;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if(m_ground_spawn && respawn_timer.Check()){
RandomSpawn(true);
}
return true;
}
void Object::RandomSpawn(bool send_packet) {
if(!m_ground_spawn)
return;
2013-05-06 13:07:41 -04:00
m_data.x = zone->random.Real(m_min_x, m_max_x);
m_data.y = zone->random.Real(m_min_y, m_max_y);
if(m_data.z == BEST_Z_INVALID) {
glm::vec3 me;
me.x = m_data.x;
me.y = m_data.y;
me.z = 0;
glm::vec3 hit;
float best_z = zone->zonemap->FindClosestZ(me, &hit);
if (best_z != BEST_Z_INVALID) {
m_data.z = best_z + 0.1f;
}
}
Log(Logs::Detail, Logs::Zone_Server, "Object::RandomSpawn(%s): %d (%.2f, %.2f, %.2f)", m_data.object_name, m_inst->GetID(), m_data.x, m_data.y, m_data.z);
2013-02-16 16:14:39 -08:00
respawn_timer.Disable();
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if(send_packet) {
EQApplicationPacket app;
CreateSpawnPacket(&app);
2013-05-04 18:06:58 -07:00
entity_list.QueueClients(nullptr, &app, true);
2013-02-16 16:14:39 -08:00
}
}
bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
{
if(m_ground_spawn){//This is a Cool Groundspawn
2013-05-09 10:44:08 -04:00
respawn_timer.Start();
2013-02-16 16:14:39 -08:00
}
if (m_type == OT_DROPPEDITEM) {
bool cursordelete = false;
if (m_inst && sender) {
2013-05-06 13:07:41 -04:00
// if there is a lore conflict, delete the offending item from the server inventory
2013-02-16 16:14:39 -08:00
// the client updates itself and takes care of sending "duplicate lore item" messages
auto item = m_inst->GetItem();
if(sender->CheckLoreConflict(item)) {
int16 loreslot = sender->GetInv().HasItem(item->ID, 0, invWhereBank);
if (loreslot != INVALID_INDEX) // if the duplicate is in the bank, delete it.
2013-02-16 16:14:39 -08:00
sender->DeleteItemInInventory(loreslot);
else
cursordelete = true; // otherwise, we delete the new one
}
if (item->RecastDelay)
m_inst->SetRecastTimestamp(
database.GetItemRecastTimestamp(sender->CharacterID(), item->RecastType));
2013-02-16 16:14:39 -08:00
char buf[10];
snprintf(buf, 9, "%u", item->ID);
2013-02-16 16:14:39 -08:00
buf[9] = '\0';
std::vector<EQEmu::Any> args;
args.push_back(m_inst);
if(parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, buf, this->GetID(), &args))
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct));
memcpy(outapp->pBuffer, click_object, sizeof(ClickObject_Struct));
ClickObject_Struct* co = (ClickObject_Struct*)outapp->pBuffer;
co->drop_id = 0;
entity_list.QueueClients(nullptr, outapp, false);
safe_delete(outapp);
// No longer using a tradeskill object
sender->SetTradeskillObject(nullptr);
user = nullptr;
return true;
}
2013-02-16 16:14:39 -08:00
// Transfer item to client
2016-10-16 21:36:39 -04:00
sender->PutItemInInventory(EQEmu::inventory::slotCursor, *m_inst, false);
sender->SendItemPacket(EQEmu::inventory::slotCursor, m_inst, ItemPacketTrade);
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
if(cursordelete) // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet
2016-10-16 21:36:39 -04:00
sender->DeleteItemInInventory(EQEmu::inventory::slotCursor);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if(!m_ground_spawn)
safe_delete(m_inst);
// No longer using a tradeskill object
2013-05-04 18:06:58 -07:00
sender->SetTradeskillObject(nullptr);
user = nullptr;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Send click to all clients (removes entity on client)
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct));
2013-02-16 16:14:39 -08:00
memcpy(outapp->pBuffer, click_object, sizeof(ClickObject_Struct));
2013-05-04 18:06:58 -07:00
entity_list.QueueClients(nullptr, outapp, false);
2013-02-16 16:14:39 -08:00
safe_delete(outapp);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Remove object
database.DeleteObject(m_id);
if(!m_ground_spawn)
2013-05-09 10:44:08 -04:00
entity_list.RemoveEntity(this->GetID());
2013-02-16 16:14:39 -08:00
} else {
// Tradeskill item
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_ClickObjectAction, sizeof(ClickObjectAction_Struct));
2013-02-16 16:14:39 -08:00
ClickObjectAction_Struct* coa = (ClickObjectAction_Struct*)outapp->pBuffer;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//TODO: there is prolly a better way to do this.
m_inuse = true;
coa->type = m_type;
coa->unknown16 = 0x0a;
coa->drop_id = click_object->drop_id;
coa->player_id = click_object->player_id;
coa->icon = m_icon;
strn0cpy(coa->object_name, m_display_name, 64);
2013-02-16 16:14:39 -08:00
//if this is not the main user, send them a close and a message
if (user == nullptr || user == sender) {
coa->open = 0x01;
2013-02-16 16:14:39 -08:00
}
else {
coa->open = 0x00;
2013-05-06 13:07:41 -04:00
if (sender->ClientVersion() >= EQEmu::versions::ClientVersion::RoF) {
coa->drop_id = 0xFFFFFFFF;
sender->Message(0, "Someone else is using that. Try again later.");
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if(sender->IsLooting())
{
coa->open = 0x00;
user = sender;
}
sender->QueuePacket(outapp);
safe_delete(outapp);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//if the object allready had a user, we are done
2013-05-04 18:06:58 -07:00
if(user != nullptr)
2013-02-16 16:14:39 -08:00
return(false);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Starting to use this object
sender->SetTradeskillObject(this);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
user = sender;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Send items inside of container
if (m_inst && m_inst->IsType(EQEmu::item::ItemClassBag)) {
2013-02-16 16:14:39 -08:00
//Clear out no-drop and no-rent items first if different player opens it
if(user != last_user)
m_inst->ClearByFlags(byFlagSet, byFlagSet);
2013-05-06 13:07:41 -04:00
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_ClientReady, 0);
2013-02-16 16:14:39 -08:00
sender->QueuePacket(outapp);
safe_delete(outapp);
2016-10-16 21:36:39 -04:00
for (uint8 i = EQEmu::inventory::containerBegin; i < EQEmu::inventory::ContainerCount; i++) {
const EQEmu::ItemInstance* inst = m_inst->GetItem(i);
2013-02-16 16:14:39 -08:00
if (inst) {
//sender->GetInv().PutItem(i+4000,inst);
sender->SendItemPacket(i, inst, ItemPacketWorldContainer);
}
}
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return true;
}
// Add new Zone Object (theoretically only called for items dropped to ground)
uint32 ZoneDatabase::AddObject(uint32 type, uint32 icon, const Object_Struct& object, const EQEmu::ItemInstance* inst)
2013-02-16 16:14:39 -08:00
{
uint32 database_id = 0;
uint32 item_id = 0;
int16 charges = 0;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (inst && inst->GetItem()) {
item_id = inst->GetItem()->ID;
charges = inst->GetCharges();
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// SQL Escape object_name
uint32 len = strlen(object.object_name) * 2 + 1;
2016-05-25 16:10:28 -04:00
auto object_name = new char[len];
2013-02-16 16:14:39 -08:00
DoEscapeString(object_name, object.object_name, strlen(object.object_name));
2013-05-06 13:07:41 -04:00
2014-08-18 17:21:27 -07:00
// Save new record for object
std::string query = StringFormat("INSERT INTO object "
"(zoneid, xpos, ypos, zpos, heading, "
"itemid, charges, objectname, type, icon) "
"values (%i, %f, %f, %f, %f, %i, %i, '%s', %i, %i)",
object.zone_id, object.x, object.y, object.z, object.heading,
item_id, charges, object_name, type, icon);
safe_delete_array(object_name);
auto results = QueryDatabase(query);
if (!results.Success()) {
Log(Logs::General, Logs::Error, "Unable to insert object: %s", results.ErrorMessage().c_str());
2014-08-18 17:21:27 -07:00
return 0;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2014-08-18 17:21:27 -07:00
// Save container contents, if container
if (inst && inst->IsType(EQEmu::item::ItemClassBag))
2014-08-18 17:21:27 -07:00
SaveWorldContainer(object.zone_id, database_id, inst);
2013-02-16 16:14:39 -08:00
return database_id;
}
// Update information about existing object in database
void ZoneDatabase::UpdateObject(uint32 id, uint32 type, uint32 icon, const Object_Struct& object, const EQEmu::ItemInstance* inst)
2013-02-16 16:14:39 -08:00
{
uint32 item_id = 0;
int16 charges = 0;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (inst && inst->GetItem()) {
item_id = inst->GetItem()->ID;
charges = inst->GetCharges();
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// SQL Escape object_name
uint32 len = strlen(object.object_name) * 2 + 1;
2016-05-25 16:10:28 -04:00
auto object_name = new char[len];
2013-02-16 16:14:39 -08:00
DoEscapeString(object_name, object.object_name, strlen(object.object_name));
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// Save new record for object
std::string query = StringFormat("UPDATE object SET "
"zoneid = %i, xpos = %f, ypos = %f, zpos = %f, heading = %f, "
"itemid = %i, charges = %i, objectname = '%s', type = %i, icon = %i, "
"size = %f, tilt_x = %f, tilt_y = %f "
"WHERE id = %i",
object.zone_id, object.x, object.y, object.z, object.heading,
item_id, charges, object_name, type, icon,
object.size, object.tilt_x, object.tilt_y, id);
safe_delete_array(object_name);
auto results = QueryDatabase(query);
if (!results.Success()) {
Log(Logs::General, Logs::Error, "Unable to update object: %s", results.ErrorMessage().c_str());
return;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
// Save container contents, if container
if (inst && inst->IsType(EQEmu::item::ItemClassBag))
SaveWorldContainer(object.zone_id, id, inst);
2013-02-16 16:14:39 -08:00
}
Ground_Spawns* ZoneDatabase::LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs) {
std::string query = StringFormat("SELECT max_x, max_y, max_z, "
"min_x, min_y, heading, name, "
"item, max_allowed, respawn_timer "
"FROM ground_spawns "
"WHERE zoneid = %i AND (version = %u OR version = -1) "
"LIMIT 50", zone_id, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
return gs;
2013-02-16 16:14:39 -08:00
}
int spawnIndex=0;
for (auto row = results.begin(); row != results.end(); ++row, ++spawnIndex) {
gs->spawn[spawnIndex].max_x=atof(row[0]);
gs->spawn[spawnIndex].max_y=atof(row[1]);
gs->spawn[spawnIndex].max_z=atof(row[2]);
gs->spawn[spawnIndex].min_x=atof(row[3]);
gs->spawn[spawnIndex].min_y=atof(row[4]);
gs->spawn[spawnIndex].heading=atof(row[5]);
strcpy(gs->spawn[spawnIndex].name,row[6]);
gs->spawn[spawnIndex].item=atoi(row[7]);
gs->spawn[spawnIndex].max_allowed=atoi(row[8]);
gs->spawn[spawnIndex].respawntimer=atoi(row[9]);
}
2013-02-16 16:14:39 -08:00
return gs;
}
2013-02-16 16:14:39 -08:00
void ZoneDatabase::DeleteObject(uint32 id)
{
// delete record of object
std::string query = StringFormat("DELETE FROM object WHERE id = %i", id);
auto results = QueryDatabase(query);
if (!results.Success()) {
Log(Logs::General, Logs::Error, "Unable to delete object: %s", results.ErrorMessage().c_str());
2013-02-16 16:14:39 -08:00
}
}
uint32 Object::GetDBID()
{
return this->m_id;
}
uint32 Object::GetType()
{
return this->m_type;
}
void Object::SetType(uint32 type)
{
this->m_type = type;
this->m_data.object_type = type;
}
uint32 Object::GetIcon()
{
return this->m_icon;
}
float Object::GetX()
2013-05-06 13:07:41 -04:00
{
2013-02-16 16:14:39 -08:00
return this->m_data.x;
}
float Object::GetY()
{
return this->m_data.y;
}
float Object::GetZ()
{
return this->m_data.z;
}
float Object::GetHeadingData()
{
return this->m_data.heading;
}
float Object::GetTiltX()
{
return this->m_data.tilt_x;
}
float Object::GetTiltY()
{
return this->m_data.tilt_y;
}
2013-02-16 16:14:39 -08:00
void Object::SetX(float pos)
{
this->m_data.x = pos;
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetY(float pos)
{
this->m_data.y = pos;
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetTiltX(float pos)
{
this->m_data.tilt_x = pos;
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetTiltY(float pos)
{
this->m_data.tilt_y = pos;
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetDisplayName(const char *in_name)
{
strn0cpy(m_display_name, in_name, 64);
}
2013-02-16 16:14:39 -08:00
void Object::Depop()
{
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
this->CreateDeSpawnPacket(app);
entity_list.QueueClients(0, app);
safe_delete(app);
entity_list.RemoveObject(this->GetID());
}
void Object::Repop()
{
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetZ(float pos)
{
this->m_data.z = pos;
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetModelName(const char* modelname)
{
strn0cpy(m_data.object_name, modelname, sizeof(m_data.object_name)); // 32 is the max for chars in object_name, this should be safe
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetSize(float size)
{
m_data.size = size;
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetSolidType(uint16 solidtype)
{
m_data.solidtype = solidtype;
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
float Object::GetSize()
{
return m_data.size;
}
uint16 Object::GetSolidType()
{
return m_data.solidtype;
}
2013-02-16 16:14:39 -08:00
const char* Object::GetModelName()
{
return this->m_data.object_name;
}
void Object::SetIcon(uint32 icon)
{
this->m_icon = icon;
}
uint32 Object::GetItemID()
{
if (this->m_inst == 0)
{
return 0;
}
const EQEmu::ItemData* item = this->m_inst->GetItem();
2013-02-16 16:14:39 -08:00
if (item == 0)
{
return 0;
}
return item->ID;
}
void Object::SetItemID(uint32 itemid)
{
safe_delete(this->m_inst);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (itemid)
{
this->m_inst = database.CreateItem(itemid);
}
}
void Object::GetObjectData(Object_Struct* Data)
{
if (Data)
{
memcpy(Data, &this->m_data, sizeof(this->m_data));
}
}
void Object::SetObjectData(Object_Struct* Data)
{
if (Data)
{
memcpy(&this->m_data, Data, sizeof(this->m_data));
}
}
void Object::GetLocation(float* x, float* y, float* z)
{
if (x)
{
*x = this->m_data.x;
}
if (y)
{
*y = this->m_data.y;
}
if (z)
{
*z = this->m_data.z;
}
}
void Object::SetLocation(float x, float y, float z)
{
this->m_data.x = x;
this->m_data.y = y;
this->m_data.z = z;
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::GetHeading(float* heading)
{
if (heading)
{
*heading = this->m_data.heading;
}
}
void Object::SetHeading(float heading)
{
this->m_data.heading = heading;
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket();
auto app2 = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
this->CreateDeSpawnPacket(app);
this->CreateSpawnPacket(app2);
entity_list.QueueClients(0, app);
entity_list.QueueClients(0, app2);
safe_delete(app);
safe_delete(app2);
}
void Object::SetEntityVariable(const char *id, const char *m_var)
{
std::string n_m_var = m_var;
o_EntityVariables[id] = n_m_var;
}
const char* Object::GetEntityVariable(const char *id)
{
if(!id)
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
2016-05-25 16:10:28 -04:00
auto iter = o_EntityVariables.find(id);
2013-02-16 16:14:39 -08:00
if(iter != o_EntityVariables.end())
{
return iter->second.c_str();
}
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
}
bool Object::EntityVariableExists(const char * id)
{
if(!id)
return false;
2016-05-25 16:10:28 -04:00
auto iter = o_EntityVariables.find(id);
2013-02-16 16:14:39 -08:00
if(iter != o_EntityVariables.end())
{
return true;
}
return false;
}