Browse Source

Deposit tab in the house supports deposits, track deposit history and escrow balance

In support of issue #125
- /house_deposit [??] [coin_amount] [status_amount] added
update query:
update commands set handler=518 where command='house_deposit';

- Escrow balance is tracked on the house
- Deposits are tracked for total and last deposit
- House upkeep payment now takes from escrow first
- character_house_deposits table added

CREATE TABLE `character_house_deposits` (
  `timestamp` int(10) unsigned NOT NULL DEFAULT 0,
  `house_id` int(10) unsigned NOT NULL DEFAULT 0,
  `instance_id` int(10) unsigned NOT NULL DEFAULT 0,
  `name` varchar(64) not null default '',
  `amount` bigint unsigned NOT NULL DEFAULT 0,
  `last_amount` bigint unsigned NOT NULL DEFAULT 0,
  `status` int(10) unsigned NOT NULL DEFAULT 0,
  `last_status` int(10) unsigned NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Image 1 year ago
parent
commit
a1f8fb9ed2

+ 11 - 0
DB/updates/house_deposits.sql

@@ -0,0 +1,11 @@
+update commands set handler=518 where command='house_deposit';
+CREATE TABLE `character_house_deposits` (
+  `timestamp` int(10) unsigned NOT NULL DEFAULT 0,
+  `house_id` int(10) unsigned NOT NULL DEFAULT 0,
+  `instance_id` int(10) unsigned NOT NULL DEFAULT 0,
+  `name` varchar(64) not null default '',
+  `amount` bigint unsigned NOT NULL DEFAULT 0,
+  `last_amount` bigint unsigned NOT NULL DEFAULT 0,
+  `status` int(10) unsigned NOT NULL DEFAULT 0,
+  `last_status` int(10) unsigned NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;

+ 41 - 1
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -2774,6 +2774,46 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 
 			break;
 		}
+		case COMMAND_HOUSE_DEPOSIT:
+		{
+			PrintSep(sep, "COMMAND_HOUSE_DEPOSIT");
+			// arg0 = ??? (set to 3)
+			// arg1 = coin (in copper)
+			// arg2 = status? (not implemented yet)
+			PlayerHouse* ph = world.GetPlayerHouseByInstanceID(client->GetCurrentZone()->GetInstanceID());
+			if (ph && sep && sep->IsNumber(1))
+			{
+				int64 outVal = strtoull(sep->arg[1], NULL, 0);
+				if (client->GetPlayer()->RemoveCoins(outVal))
+				{
+					char query[256];
+					map<string,Deposit>::iterator itr = ph->depositsMap.find(string(client->GetPlayer()->GetName()));
+					if (itr != ph->depositsMap.end())
+					{
+						snprintf(query, 256, "update character_house_deposits set timestamp = %u, amount = amount + %I64u, last_amount = %I64u where house_id = %u and instance_id = %u and name='%s'", Timer::GetUnixTimeStamp(), outVal, outVal, ph->house_id, ph->instance_id, client->GetPlayer()->GetName());
+					}
+					else
+						snprintf(query, 256, "insert into character_house_deposits set timestamp = %u, house_id = %u, instance_id = %u, name='%s', amount = %I64u", Timer::GetUnixTimeStamp(), ph->house_id, ph->instance_id, client->GetPlayer()->GetName(), outVal);
+
+					if (database.RunQuery(query, strnlen(query, 256)))
+					{
+						ph->escrow_coins += outVal;
+						database.UpdateHouseEscrow(ph->house_id, ph->instance_id, ph->escrow_coins);
+
+						database.LoadDeposits(ph);
+						client->PlaySound("coin_cha_ching");
+						HouseZone* hz = world.GetHouseZone(ph->house_id);
+						ClientPacketFunctions::SendBaseHouseWindow(client, hz, ph, client->GetPlayer()->GetID());
+					}
+					else
+					{
+						client->GetPlayer()->AddCoins(outVal);
+						client->SimpleMessage(CHANNEL_COLOR_RED, "Deposit failed!");
+					}
+				}
+			}
+			break;
+		}
 		case COMMAND_HOUSE:
 		{
 			if (sep && sep->IsNumber(0))
@@ -2781,7 +2821,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 				int32 unique_id = atoi(sep->arg[0]);
 				PlayerHouse* ph = world.GetPlayerHouseByUniqueID(unique_id);
 				HouseZone* hz = 0;
-
+					
 				if (ph)
 					hz = world.GetHouseZone(ph->house_id);
 				// there is a arg[1] that is true/false, but not sure what it is for investigate more later

+ 1 - 0
EQ2/source/WorldServer/Commands/Commands.h

@@ -880,6 +880,7 @@ private:
 #define COMMAND_HOUSE					515
 #define COMMAND_MOVE_ITEM				516
 #define COMMAND_PICKUP					517
+#define COMMAND_HOUSE_DEPOSIT			518
 
 #define GET_AA_XML						751
 #define ADD_AA							752

+ 40 - 0
EQ2/source/WorldServer/Housing/HousingDB.cpp

@@ -31,6 +31,13 @@ void WorldDatabase::SetHouseUpkeepDue(int32 char_id, int32 house_id, int32 insta
 }
 
 
+void WorldDatabase::UpdateHouseEscrow(int32 house_id, int32 instance_id, int64 amount) {
+	Query query;
+	string update = string("UPDATE character_houses set escrow_coins = %I64u where house_id = %u and instance_id = %u");
+	query.RunQuery2(Q_UPDATE, update.c_str(), amount, house_id, instance_id);
+}
+
+
 void WorldDatabase::RemovePlayerHouse(int32 char_id, int32 house_id) {
 }
 
@@ -44,4 +51,37 @@ void WorldDatabase::LoadPlayerHouses() {
 			world.AddPlayerHouse(atoul(row[1]), atoul(row[2]), atoi64(row[0]), atoul(row[3]), atoul(row[4]), atoi64(row[5]), atoul(row[6]), row[7]);
 		}
 	}
+}
+
+void WorldDatabase::LoadDeposits(PlayerHouse* ph)
+{
+	if (!ph)
+		return;
+	ph->deposits.clear();
+	ph->depositsMap.clear();
+
+	Query query;
+	MYSQL_ROW row;
+	MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select timestamp, amount, last_amount, status, last_status, name from character_house_deposits where house_id = %u and instance_id = %u order by timestamp asc", ph->house_id, ph->instance_id);
+
+	if (result && mysql_num_rows(result) > 0) {
+		while ((row = mysql_fetch_row(result))) {
+			Deposit d;
+			d.timestamp = atoul(row[0]);
+
+			int64 outVal = strtoull(row[1], NULL, 0);
+			d.amount = outVal;
+
+			outVal = strtoull(row[2], NULL, 0);
+			d.last_amount = outVal;
+
+			d.status = atoul(row[3]);
+			d.last_status = atoul(row[4]);
+
+			d.name = string(row[5]);
+
+			ph->deposits.push_back(d);
+			ph->depositsMap.insert(make_pair(d.name, d));
+		}
+	}
 }

+ 18 - 2
EQ2/source/WorldServer/Housing/HousingPackets.cpp

@@ -132,7 +132,6 @@ void ClientPacketFunctions::SendBaseHouseWindow(Client* client, HouseZone* hz, P
 
 		packet->setDataByName("escrow_balance_coins", ph->escrow_coins);
 		packet->setDataByName("escrow_balance_status", ph->escrow_status);
-
 		// temp - set priv level to owner for now
 		packet->setDataByName("privlage_level", 4);
 		// temp - set house type to personal house for now
@@ -146,6 +145,23 @@ void ClientPacketFunctions::SendBaseHouseWindow(Client* client, HouseZone* hz, P
 			packet->setDataByName("num_access", 0);
 			packet->setDataByName("public_access_level", 1);
 			packet->setDataByName("num_history", 0);
+
+			// allows deposits to be seen
+			packet->setDataByName("unknown3", ph->deposits.size() ? 1 : 0);
+
+			packet->setArrayLengthByName("num_deposit", ph->deposits.size());
+			list<Deposit>::iterator itr;
+			int d = 0;
+			for (itr = ph->deposits.begin(); itr != ph->deposits.end(); itr++)
+			{
+				packet->setArrayDataByName("deposit_name", itr->name.c_str(), d);
+				packet->setArrayDataByName("deposit_total_coin", itr->amount, d);
+				packet->setArrayDataByName("deposit_time_stamp", itr->timestamp, d);
+				packet->setArrayDataByName("deposit_last_coin", itr->last_amount, d);
+				packet->setArrayDataByName("deposit_total_status", itr->status, d);
+				packet->setArrayDataByName("deposit_last_status", itr->last_status, d);
+				d++;
+			}
 		}
 
 		client->QueuePacket(packet->serialize());
@@ -182,7 +198,7 @@ void ClientPacketFunctions::SendHouseVisitWindow(Client* client, vector<PlayerHo
 		client->QueuePacket(packet->serialize());
 	}
 	safe_delete(packet);
-}
+}	
 
 /*
 <Struct Name="WS_DisplayVisitScreen" ClientVersion="1193" OpcodeName="OP_DisplayInnVisitScreenMsg">

+ 1 - 3
EQ2/source/WorldServer/World.cpp

@@ -1856,7 +1856,7 @@ void World::AddPlayerHouse(int32 char_id, int32 house_id, int64 unique_id, int32
 		ph->escrow_status = escrow_status;
 		ph->upkeep_due = upkeep_due;
 		ph->player_name = player_name;
-
+		database.LoadDeposits(ph);
 		m_playerHouses[house_id][char_id] = ph;
 	}
 	MPlayerHouses.releasewritelock(__FUNCTION__, __LINE__);
@@ -1943,8 +1943,6 @@ vector<PlayerHouse*> World::GetAllPlayerHousesByHouseID(int32 house_id) {
 	return ret;
 }
 
-
-
 void World::PopulateTOVStatMap() {
 	//This function populates a map that converts changed CoE to ToV stats
 	tov_itemstat_conversion[0] = TOV_ITEM_STAT_HPREGEN;

+ 11 - 0
EQ2/source/WorldServer/World.h

@@ -191,6 +191,15 @@ struct HouseZone {
 	float		exit_heading;
 };
 
+struct Deposit {
+	int32 timestamp;
+	int64 amount;
+	int64 last_amount;
+	int32 status;
+	int32 last_status;
+	string name;
+};
+
 struct PlayerHouse {
 	int32 house_id;
 	int64 unique_id;
@@ -199,6 +208,8 @@ struct PlayerHouse {
 	int64 escrow_coins;
 	int32 escrow_status;
 	string player_name;
+	list<Deposit> deposits;
+	map<string, Deposit> depositsMap;
 };
 
 // Constants for STATs counters

+ 3 - 0
EQ2/source/WorldServer/WorldDatabase.h

@@ -49,6 +49,7 @@
 #include "Titles.h"
 #include "Rules/Rules.h"
 #include "Languages.h"
+#include "World.h"
 
 using namespace std;
 
@@ -503,7 +504,9 @@ public:
 	int64				AddPlayerHouse(int32 char_id, int32 house_id, int32 instance_id, int32 upkeep_due);
 	void				SetHouseUpkeepDue(int32 char_id, int32 house_id, int32 instance_id, int32 upkeep_due);
 	void				RemovePlayerHouse(int32 char_id, int32 house_id);
+	void				UpdateHouseEscrow(int32 house_id, int32 instance_id, int64 amount);
 	void				LoadPlayerHouses();
+	void				LoadDeposits(PlayerHouse* house);
 
 	/* World */
 	bool				CheckBannedIPs(const char* loginIP);

+ 23 - 2
EQ2/source/WorldServer/client.cpp

@@ -1883,10 +1883,27 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 						break;
 					}
 				}
-
+				int64 coinReq = hz->upkeep_coin;
+				int64 tmpRecoverCoins = 0;
+				bool escrowChange = false;
+				if (ph->escrow_coins && coinReq >= ph->escrow_coins) // more required to upkeep than in escrow, subtract what we have left
+				{
+					escrowChange = true;
+					tmpRecoverCoins = ph->escrow_coins;
+					coinReq -= ph->escrow_coins;
+				}
+				else if (ph->escrow_coins && coinReq && coinReq <= ph->escrow_coins)
+				{
+					escrowChange = true;
+					// more than enough in escrow, subtract and make our cost 0!
+					ph->escrow_coins -= coinReq;
+					coinReq = 0;
+				}
 				// TODO: Need support for upkeep_status, but alas status_points are not implemented!
-				if (!hz->upkeep_coin || hz->upkeep_coin && player->RemoveCoins(hz->upkeep_coin)) // TODO: Need option to take from bank if player does not have enough coin on them
+				if (!coinReq || coinReq && player->RemoveCoins(coinReq)) // TODO: Need option to take from bank if player does not have enough coin on them
 				{
+					if (escrowChange)
+						database.UpdateHouseEscrow(ph->house_id, ph->instance_id, ph->escrow_coins);
 					ph->upkeep_due = upkeep_due;
 					database.SetHouseUpkeepDue(GetCharacterID(), ph->house_id, ph->instance_id, ph->upkeep_due);
 					//ClientPacketFunctions::SendHousingList(this);
@@ -1895,6 +1912,10 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 				}
 				else
 				{
+					// recover the escrow we were going to use but could not spend due to lack of funds
+					if (tmpRecoverCoins)
+						ph->escrow_coins += tmpRecoverCoins;
+
 					SimpleMessage(CHANNEL_COLOR_YELLOW, "You do not have enough money to pay for upkeep.");
 					PlaySound("buy_failed");
 				}