Additional housing features (UI items panel)

Furthering issue #124
- spawn_instance_data now tracks unique item id
- inside door widget now lists all items in the house, can move or pickup from UI
- Support for examine item by unique item id in house instance
This commit is contained in:
Image 2020-06-01 23:14:55 -04:00
parent 23898ae6d5
commit 313b060328
11 changed files with 239 additions and 31 deletions

View file

@ -2721,9 +2721,61 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
}
break;
}
case COMMAND_MOVE_ITEM:
{
int32 id = 0;
if (sep->IsNumber(0))
id = atoul(sep->arg[0]);
Spawn* spawn = 0;
if (id == 0)
{
spawn = cmdTarget;
}
else
spawn = client->GetCurrentZone()->GetSpawnFromUniqueItemID(id);
if (!spawn || !client->HasOwnerOrEditAccess() || !spawn->GetPickupItemID())
break;
client->SendMoveObjectMode(spawn, 0);
break;
}
case COMMAND_PICKUP:
{
int32 id = 0;
if (sep->IsNumber(0))
id = atoul(sep->arg[0]);
Spawn* spawn = 0;
if (id == 0)
{
spawn = cmdTarget;
}
else
spawn = client->GetCurrentZone()->GetSpawnFromUniqueItemID(id);
if (!spawn || !client->HasOwnerOrEditAccess() || !spawn->GetPickupItemID())
break;
client->AddItem(spawn->GetPickupItemID(), 1);
Query query;
query.RunQuery2(Q_INSERT, "delete from spawn_instance_data where spawn_id = %u and spawn_location_id = %u and pickup_item_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), spawn->GetPickupItemID());
if (database.RemoveSpawnFromSpawnLocation(spawn)) {
client->GetCurrentZone()->RemoveSpawn(false, spawn, true, true);
}
// we had a UI Window displayed, update the house items
if ( id > 0 )
client->GetCurrentZone()->SendHouseItems(client);
break;
}
case COMMAND_HOUSE:
{
PrintSep(sep, "COMMAND_HOUSE");
if (sep && sep->IsNumber(0))
{
int32 unique_id = atoi(sep->arg[0]);
@ -2745,12 +2797,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
hz = world.GetHouseZone(ph->house_id);
ClientPacketFunctions::SendBaseHouseWindow(client, hz, ph, client->GetPlayer()->GetID());
client->GetCurrentZone()->SendHouseItems(client);
}
break;
}
case COMMAND_HOUSE_UI:
{
PrintSep(sep, "COMMAND_HOUSEUI");
if (sep && sep->IsNumber(0) && client->GetCurrentZone()->GetInstanceType() == Instance_Type::NONE)
{
int32 unique_id = atoi(sep->arg[0]);

View file

@ -878,6 +878,8 @@ private:
#define COMMAND_GM 513
#define COMMAND_HOUSE_UI 514
#define COMMAND_HOUSE 515
#define COMMAND_MOVE_ITEM 516
#define COMMAND_PICKUP 517
#define GET_AA_XML 751
#define ADD_AA 752

View file

@ -95,6 +95,7 @@ void ClientPacketFunctions::SendHousingList(Client* client) {
packet->setArrayDataByName("unknown2", 1, i);
}
client->QueuePacket(packet->serialize());
safe_delete(packet);
}
void ClientPacketFunctions::SendBaseHouseWindow(Client* client, HouseZone* hz, PlayerHouse* ph, int32 spawnID) {

View file

@ -105,6 +105,7 @@ Spawn::Spawn(){
MCommandMutex.SetName("Entity::MCommandMutex");
has_spawn_proximities = false;
pickup_item_id = 0;
pickup_unique_item_id = 0;
}
Spawn::~Spawn(){

View file

@ -1052,7 +1052,13 @@ public:
pickup_item_id = itemid;
}
void SetPickupUniqueItemID(int32 uniqueid)
{
pickup_unique_item_id = uniqueid;
}
int32 GetPickupItemID() { return pickup_item_id; }
int32 GetPickupUniqueItemID() { return pickup_unique_item_id; }
protected:
bool send_spawn_changes;
@ -1079,6 +1085,7 @@ protected:
int8 merchant_type;
int32 transporter_id;
int32 pickup_item_id;
int32 pickup_unique_item_id;
map<int32, vector<int16>* > required_quests;
map<int32, LUAHistory> required_history;
EquipmentItemList equipment_list;

View file

@ -393,6 +393,7 @@ void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){
ClientPacketFunctions::SendHouseVisitWindow(client, world.GetAllPlayerHousesByHouseID(m_houseID));
ClientPacketFunctions::SendBaseHouseWindow(client, hz, ph, id);
client->GetCurrentZone()->SendHouseItems(client);
}
else {
if (hz)

View file

@ -6708,13 +6708,14 @@ void WorldDatabase::GetHouseSpawnInstanceData(ZoneServer* zone, Spawn* spawn)
DatabaseResult result;
database_new.Select(&result, "SELECT pickup_item_id\n"
database_new.Select(&result, "SELECT pickup_item_id, pickup_unique_item_id\n"
" FROM spawn_instance_data\n"
" WHERE spawn_id = %u and spawn_location_id = %u",
spawn->GetDatabaseID(),spawn->GetSpawnLocationID());
if (result.GetNumRows() > 0 && result.Next()) {
spawn->SetPickupItemID(result.GetInt32(0));
spawn->SetPickupUniqueItemID(result.GetInt32(1));
}
}

View file

@ -1862,7 +1862,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
if (upkeep_due > (Timer::GetUnixTimeStamp() + 7257600)) // 84 days max upkeep to pay https://eq2.zam.com/wiki/Housing_%28EQ2%29#Upkeep
{
Message(CHANNEL_COLOR_YELLOW, "You cannot pay more than 1 month of upkeep.");
Message(CHANNEL_COLOR_YELLOW, "You cannot pay more than 3 months of upkeep.");
break;
}
}
@ -2381,8 +2381,28 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
return;
}
request->LoadPacketData(app->pBuffer, app->size);
int32 id = request->getType_int32_ByName("id");
Item* item = GetPlayer()->item_list.GetItemFromUniqueID(id, true);
Item* item = 0;
// translate from unique id to spawn id for houses
Spawn* spawn = this->GetCurrentZone()->GetSpawnFromUniqueItemID(id);
bool wasSpawn = false;
if (spawn)
{
item = master_item_list.GetItem(spawn->GetPickupItemID());
if (item)
{
wasSpawn = true;
item = new Item(item);
item->details.unique_id = spawn->GetPickupUniqueItemID();
}
}
if (!item)
item = GetPlayer()->item_list.GetItemFromUniqueID(id, true);
if (!item)
item = GetPlayer()->GetEquipmentList()->GetItemFromUniqueID(id);
if (!item)
@ -2392,6 +2412,8 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
EQ2Packet* app = item->serialize(GetVersion(), false, GetPlayer());
//DumpPacket(app);
QueuePacket(app);
if (wasSpawn)
delete item;
}
else {
LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id);
@ -8654,28 +8676,6 @@ bool Client::HandleHouseEntityCommands(Spawn* spawn, int32 spawnid, string comma
}
return true;
}
else if (!HasOwnerOrEditAccess())
{
//SimpleMessage(CHANNEL_COLOR_RED, "This is not your home!");
return false;
}
else if (command == "house_spawn_move")
{
SendMoveObjectMode(spawn, 0);
return true;
}
else if (command == "house_spawn_pickup" && spawn->GetPickupItemID())
{
AddItem(spawn->GetPickupItemID(), 1);
Query query;
query.RunQuery2(Q_INSERT, "delete from spawn_instance_data where spawn_id = %u and spawn_location_id = %u and pickup_item_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), spawn->GetPickupItemID());
if (database.RemoveSpawnFromSpawnLocation(spawn)) {
GetCurrentZone()->RemoveSpawn(false, spawn, true, true);
}
return true;
}
return false;
}
@ -8741,13 +8741,14 @@ bool Client::PopulateHouseSpawnFinalize()
int32 uniqueID = GetPlacementUniqueItemID();
Item* uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
tmp->SetPickupItemID(uniqueItem->details.item_id);
tmp->SetPickupUniqueItemID(uniqueID);
if (uniqueItem)
{
if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE)
{
Query query;
query.RunQuery2(Q_INSERT, "insert into spawn_instance_data set spawn_id = %u, spawn_location_id = %u, pickup_item_id = %u", tmp->GetDatabaseID(), tmp->GetSpawnLocationID(), tmp->GetPickupItemID());
query.RunQuery2(Q_INSERT, "insert into spawn_instance_data set spawn_id = %u, spawn_location_id = %u, pickup_item_id = %u, pickup_unique_item_id = %u", tmp->GetDatabaseID(), tmp->GetSpawnLocationID(), tmp->GetPickupItemID(), uniqueID);
}
database.DeleteItem(GetCharacterID(), uniqueItem, 0);

View file

@ -799,7 +799,7 @@ bool ZoneServer::AddCloseSpawnsToSpawnGroup(Spawn* spawn, float radius){
void ZoneServer::RepopSpawns(Client* client, Spawn* in_spawn){
vector<Spawn*>* spawns = in_spawn->GetSpawnGroup();
PacketStruct* packet = configReader.getStruct("WS_DestroyGhostCmd", client->GetVersion());;
PacketStruct* packet = configReader.getStruct("WS_DestroyGhostCmd", client->GetVersion());
if(spawns){
if(!packet)
return;
@ -2903,9 +2903,9 @@ void ZoneServer::AddSpawn(Spawn* spawn) {
if (GetInstanceType() == PERSONAL_HOUSE_INSTANCE && spawn->IsObject())
{
spawn->AddSecondaryEntityCommand("Examine", 20, "house_spawn_examine", "", 0, 0);
spawn->AddSecondaryEntityCommand("Move", 20, "house_spawn_move", "", 0, 0);
spawn->AddSecondaryEntityCommand("Move", 20, "move_item", "", 0, 0);
spawn->AddSecondaryEntityCommand("Pack in Moving Crate", 20, "house_spawn_pack_in_moving_crate", "", 0, 0);
spawn->AddSecondaryEntityCommand("Pick Up", 20, "house_spawn_pickup", "", 0, 0);
spawn->AddSecondaryEntityCommand("Pick Up", 20, "pickup", "", 0, 0);
spawn->SetShowCommandIcon(1);
}
@ -7338,4 +7338,111 @@ void ZoneServer::SetSpawnScript(SpawnEntry* entry, Spawn* spawn)
break;
}
}
}
vector<HouseItem> ZoneServer::GetHouseItems(Client* client)
{
if (!client->GetCurrentZone()->GetInstanceID() || !client->HasOwnerOrEditAccess())
return std::vector<HouseItem>();
PacketStruct* packet = configReader.getStruct("WS_HouseItemsList", client->GetVersion());
std::vector<HouseItem> items;
map<int32, Spawn*>::iterator itr;
Spawn* spawn = 0;
MSpawnList.readlock(__FUNCTION__, __LINE__);
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
spawn = itr->second;
if (spawn && spawn->IsObject() && spawn->GetPickupItemID())
{
HouseItem tmpItem;
tmpItem.item_id = spawn->GetPickupItemID();
tmpItem.unique_id = spawn->GetPickupUniqueItemID();
tmpItem.spawn_id = spawn->GetID();
tmpItem.item = master_item_list.GetItem(spawn->GetPickupItemID());
if (!tmpItem.item)
continue;
items.push_back(tmpItem);
}
}
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
return items;
}
void ZoneServer::SendHouseItems(Client* client)
{
if (!client->GetCurrentZone()->GetInstanceID() || !client->HasOwnerOrEditAccess())
return;
PacketStruct* packet = configReader.getStruct("WS_HouseItemsList", client->GetVersion());
std::vector<HouseItem> items = GetHouseItems(client);
// setting this to 1 puts it on the door widget
packet->setDataByName("is_widget_door", 1);
packet->setArrayLengthByName("num_items", items.size());
for (int i = 0; i < items.size(); i++)
{
HouseItem tmpItem = items[i];
packet->setArrayDataByName("unique_id", tmpItem.unique_id, i); // unique_id is in fact the item_id...
packet->setArrayDataByName("item_name", tmpItem.item->name.c_str(), i);
packet->setArrayDataByName("status_reduction", tmpItem.item->houseitem_info->status_rent_reduction, i);
// location, 0 = floor, 1 = ceiling
//packet->setArrayDataByName("location", 1, i, 0);
// item_state int8
// 0 = normal (cannot pick up item / move item / toggle visibility)
// 1 = virtual (toggle visibility available, no move item)
// 2 = hidden (cannot pick up item / move item / toggle visibility)
// 3 = virtual/hidden/toggle visibility
// 4 = none (cannot pick up item / move item / toggle visibility)
// 5 = none, toggle visibility (cannot pick up item / move item)
// 8 = none (cannot pick up item / move item / toggle visibility)
//packet->setArrayDataByName("item_state", tmpvalue, i, 0);
// makes it so we don't have access to move item/retrieve item
// cannot use in conjunction with ui_tab_flag1/ui_tab_flag2
//packet->setArrayDataByName("tradeable", 1, i);
//packet->setArrayDataByName("item_description", "failboat", i);
// access to move item/retrieve item, do not use in conjunction with tradeable
packet->setArrayDataByName("ui_tab_flag1", 1, i, 0);
packet->setArrayDataByName("ui_tab_flag2", 1, i, 0);
// both of these can serve as description fields (only one should be used they populate the same area below the item name)
//packet->setArrayDataByName("first_item_description", "test", i);
//packet->setArrayDataByName("second_item_description", "Description here!", i);
packet->setArrayDataByName("icon", tmpItem.item->details.icon, i);
}
EQ2Packet* pack = packet->serialize();
client->QueuePacket(pack);
safe_delete(packet);
}
Spawn* ZoneServer::GetSpawnFromUniqueItemID(int32 unique_id)
{
if (!GetInstanceID() || GetInstanceType() != Instance_Type::PERSONAL_HOUSE_INSTANCE)
return nullptr;
map<int32, Spawn*>::iterator itr;
Spawn* spawn = 0;
MSpawnList.readlock(__FUNCTION__, __LINE__);
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
spawn = itr->second;
if (spawn && spawn->IsObject() && spawn->GetPickupUniqueItemID() == unique_id)
{
Spawn* tmpSpawn = spawn;
MSpawnList.releasereadlock();
return tmpSpawn;
}
}
MSpawnList.releasereadlock();
return nullptr;
}

View file

@ -154,6 +154,13 @@ struct TrackedSpawn {
float distance;
};
struct HouseItem {
int32 spawn_id;
int32 item_id;
int32 unique_id;
Item* item;
};
class Widget;
class Client;
class Sign;
@ -625,6 +632,10 @@ public:
return LoadingData;
}
vector<HouseItem> GetHouseItems(Client* client);
Spawn* GetSpawnFromUniqueItemID(int32 unique_id);
void SendHouseItems(Client* client);
MutexMap<int32, int32> house_object_database_lookup; // 1st int32 = model type, 2nd int32 = spawn id
private:
/* Private Functions */

View file

@ -32975,6 +32975,30 @@ to zero and treated like placeholders." />
</Data>
<Data ElementName="unknown7" Type="int16" />
</Struct>
<Struct Name="WS_HouseItemsList" ClientVersion="60114" OpcodeName="OP_HouseItemsList">
<Data ElementName="num_items" Type="int32" />
<Data ElementName="items_array" Type="Array" ArraySizeVariable="num_items">
<Data ElementName="unique_id" Type="int32" />
<Data ElementName="item_name" Type="EQ2_16Bit_String" />
<Data ElementName="status_reduction" Type="int32" />
<Data ElementName="unknown1" Type="int32" />
<Data ElementName="unknown2" Type="int32" />
<Data ElementName="tradeable" Type="int8" /> <!-- when 0 should must? item_description -->
<Data ElementName="is_notrade" Type="EQ2_16Bit_String" IfVariableNotEquals="tradeable_%i"/>
<Data ElementName="unknown5" Type="int8"/>
<Data ElementName="ui_tab_flag1" Type="int8"/>
<Data ElementName="first_item_description" Type="EQ2_16Bit_String" IfVariableNotSet="ui_tab_flag1_%i"/>
<Data ElementName="ui_tab_flag2" Type="int8"/>
<Data ElementName="second_item_description" Type="EQ2_16Bit_String" IfVariableNotSet="ui_tab_flag2_%i"/>
<Data ElementName="icon" Type="int16" />
<Data ElementName="location" Type="int8" />
<Data ElementName="item_state" Type="int8"/>
<Data ElementName="item_state_extended" Type="int8" size="3" /> <!-- could be more of the item_state -->
</Data>
<Data ElementName="unknown7" Type="int8" />
<!-- setting to 1 causes it to populate on the items tab with the widget door aka /house command. 0 its a popup (moving crate). -->
<Data ElementName="is_widget_door" Type="int16" />
</Struct>
<Struct Name="WS_HouseItemsList" ClientVersion="63119" OpcodeName="OP_HouseItemsList">
<Data ElementName="num_items" Type="int32" />
<Data ElementName="items_array" Type="Array" ArraySizeVariable="num_items">