/* * exchangemanager.cpp * * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.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; 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 #include "bulkobjects/pscharacterloader.h" #include "bulkobjects/dictionary.h" #include "bulkobjects/psitem.h" #include "bulkobjects/psglyph.h" #include "bulkobjects/psnpcdialog.h" #include "util/log.h" #include "util/psconst.h" #include "util/psstring.h" #include "util/eventmanager.h" #include "globals.h" #include "psserver.h" #include "psserverchar.h" #include "clients.h" #include "gem.h" #include "chatmanager.h" #include "cachemanager.h" #include "playergroup.h" #include "exchangemanager.h" #include "invitemanager.h" /** Question client. Handles YesNo response of client for trade request. */ class PendingTradeInvite : public PendingInvite { public: PendingTradeInvite( Client *inviter, Client *invitee, const char *question ) : PendingInvite( inviter, invitee, true, question, "Accept", "Reject", "You have offered %s to trade with you.", "%s has offered you for a trade.", "%s has accepted your trade offer.", "You have accepted the trade offer.", "%s has rejected your trade offer.", "You have rejected %s's trade offer.", psQuestionMessage::generalConfirm ) { } void HandleAnswer(const csString & answer) { Client * invitedClient = psserver->GetConnections()->Find(clientnum); if ( !invitedClient ) return; PendingInvite::HandleAnswer(answer); Client * inviterClient = psserver->GetConnections()->Find(inviterClientNum); if ( !inviterClient ) return; psCharacter* invitedCharData = invitedClient->GetCharacterData(); psCharacter* inviterCharData = inviterClient->GetCharacterData(); const char* invitedCharFirstName = invitedCharData->GetCharName(); const char* inviterCharFirstName = inviterCharData->GetCharName(); if ( answer == "no" ) { // Inform proposer that the trade has been rejected csString message; message.Format( "%s has rejected your trade offer.", invitedCharFirstName ); psserver->SendSystemError( inviterClient->GetClientNum(), message.GetData() ); return; } // Ignore the answer if the invited client is already in trade if (invitedClient->ExchangeID()) { psserver->SendSystemError(invitedClient->GetClientNum(), "You are already busy with another trade"); psserver->SendSystemError(inviterClient->GetClientNum(), "%s is already busy with another trade", invitedCharFirstName); return; } // Ignore the answer if the inviter client is already in trade if (inviterClient->ExchangeID()) { psserver->SendSystemError(inviterClient->GetClientNum(), "You are already busy with another trade"); psserver->SendSystemError(invitedClient->GetClientNum(), "%s is already busy with another trade", inviterCharFirstName); return; } Exchange* exchange = new PlayerToPlayerExchange( inviterClient, invitedClient, psserver->GetExchangeManager() ); // Check range if ( !exchange->CheckRange(inviterClient->GetClientNum(), inviterClient->GetActor(), invitedClient->GetActor()) ) { psserver->SendSystemError(inviterClient->GetClientNum(), "%s is too far away to trade.", invitedCharFirstName ); delete exchange; return; } // Overwrite client trading state bool oldTradingStopped = inviterCharData->SetTradingStopped(false); if ( !inviterCharData->ReadyToExchange() ) { psserver->SendSystemInfo( inviterClient->GetClientNum(), "You are not ready to trade." ); inviterCharData->SetTradingStopped( oldTradingStopped ); delete exchange; return; } inviterCharData->SetTradingStopped( oldTradingStopped ); if ( !invitedCharData->ReadyToExchange() ) { psserver->SendSystemInfo( inviterClient->GetClientNum(), "Target is not ready to trade." ); delete exchange; return; } inviterClient->ExchangeID( exchange->GetID() ); invitedClient->ExchangeID( exchange->GetID() ); psserver->GetExchangeManager()->AddExchange( exchange ); } }; /** Sends text message describing change of offers to user 'clientNum'. * otherName - name of the other character (the person that the user exchanges with) * object - item that was moved (e.g. "2x sword") * actionOfClient - did the user that we send the message to do the operation ? (or the other character?) * movedToOffer - was the object moved to offered stuff ? (or removed from offered stuff and returned to inventory?) */ /************* void SendTextDescription(int clientNum, const csString & otherName, const csString & object, bool actionOfClient, bool movedToOffer) { psString text; if (actionOfClient) { if (movedToOffer) text.AppendFmt("You have just offered %s to %s.", object.GetData(), otherName.GetData()); else text.AppendFmt("You have just removed %s from offer.", object.GetData()); } else { if (movedToOffer) text.AppendFmt("%s has just offered %s to you.", otherName.GetData(), object.GetData()); else text.AppendFmt("%s has just removed %s from offer.", otherName.GetData(), object.GetData()); } psserver->SendSystemInfo(clientNum, text); } csString MakeMoneyObject(int coin, int count) { csString object; object = count; object += "x "; switch (coin) { case MONEY_TRIAS: object += "tria"; break; case MONEY_OCTAS: object += "octa"; break; case MONEY_HEXAS: object += "hexa"; break; case MONEY_CIRCLES: object += "circle"; break; } return object; } void SendTextDescriptionOfMoney(int clientNum, const csString & otherName, psMoney oldMoney, psMoney newMoney, bool actionOfClient) { csString object; if (oldMoney.GetTrias() != newMoney.GetTrias()) object = MakeMoneyObject(MONEY_TRIAS, abs(oldMoney.GetTrias() - newMoney.GetTrias())); else if (oldMoney.GetOctas() != newMoney.GetOctas()) object = MakeMoneyObject(MONEY_OCTAS, abs(oldMoney.GetOctas() - newMoney.GetOctas())); else if (oldMoney.GetHexas() != newMoney.GetHexas()) object = MakeMoneyObject(MONEY_HEXAS, abs(oldMoney.GetHexas() - newMoney.GetHexas())); else if (oldMoney.GetCircles() != newMoney.GetCircles()) object = MakeMoneyObject(MONEY_CIRCLES, abs(oldMoney.GetCircles() - newMoney.GetCircles())); SendTextDescription(clientNum, otherName, object, actionOfClient, newMoney > oldMoney); } ***************/ bool Exchange::CheckRange(int clientNum, gemObject * ourActor, gemObject * otherActor) { if (ourActor->RangeTo(otherActor) > RANGE_TO_SELECT) { psserver->SendSystemInfo(clientNum, "You are not in range to trade with %s.", otherActor->GetName()); return false; } return true; } psCharacter *GetClientCharacter(int clientNum) { Client * client = psserver->GetConnections()->Find(clientNum); if (client == NULL) return NULL; return client->GetCharacterData(); } /******** const char * GetClientName(int clientNum) { Client * client = psserver->GetConnections()->Find(clientNum); if (client == NULL) return NULL; return client->GetName(); } ********/ /*********************************************************************************** * * class ExchangingCharacter * ***********************************************************************************/ ExchangingCharacter::ExchangingCharacter(int clientNum) { int i; client = clientNum; for (i = 0; i < EXCHANGE_SLOT_COUNT; i++) offering[i] = NULL; } psItem * ExchangingCharacter::RemoveItem(int itemNum, int count, int& remain) { if (itemNum<0 || itemNum>=EXCHANGE_SLOT_COUNT) return NULL; psItem* item = offering[itemNum]; if (item == NULL) return NULL; if (count == -1) count = item->GetStackCount(); if (item->GetStackCount() <= count) { offering[itemNum] = NULL; remain = 0; return item; } else { psItem* split = item->SplitStack(count); if (split == NULL) return NULL; remain = item->GetStackCount(); return split; } } int ExchangingCharacter::InsertItem(int itemNum, psItem *& item, bool test) { if (itemNum<0 || itemNum>=EXCHANGE_SLOT_COUNT) return -1; psItem* itemInSlot = offering[itemNum]; if (itemInSlot == NULL) { if(!test) offering[itemNum] = item; return 0; } else { int remainder = itemInSlot->CombineStack(item, test); if(remainder != 0 || test) return remainder; item = itemInSlot; // Point to new location } return 0; } void ExchangingCharacter::TransferMoney( int targetClientNum ) { psCharacter* target,*cclient; if ( targetClientNum == 0 ) return; target = GetClientCharacter(targetClientNum); cclient = GetClientCharacter(client); if (target == NULL) return; TransferMoney(target); } void ExchangingCharacter::TransferMoney( psCharacter *target ) { psMoney money; money.Adjust( MONEY_TRIAS, offeringMoney.Get(MONEY_TRIAS) ); money.Adjust( MONEY_HEXAS, offeringMoney.Get(MONEY_HEXAS) ); money.Adjust( MONEY_OCTAS, offeringMoney.Get(MONEY_OCTAS) ); money.Adjust( MONEY_CIRCLES, offeringMoney.Get(MONEY_CIRCLES) ); target->AdjustMoney( money ); offeringMoney.Set(0, 0, 0, 0); } void ExchangingCharacter::TransferOffer(int targetClientNum) { psCharacter * me = GetClientCharacter(client); psCharacter * target; psItem * item; if (targetClientNum != 0) { target = GetClientCharacter(targetClientNum); if (target == NULL) return; } else target = NULL; for (int i=0; i < EXCHANGE_SLOT_COUNT; i++) { item = offering[i]; if (item != NULL) { if (target != NULL) { if ( target->MoveToInventory(item) ) { // If this is a purified glyph, we need to reset it for the new character if ( item && item->GetPurifyStatus() && target != me ) dynamic_cast(item)->UnPurify(); } else { // Drop item on ground!! target->DropItem( item ); csString buf; buf.Format("%s, %s, %s, \"%s\", %u, %d", me->GetCharFullName(), target->GetCharFullName(), "P2P - Item drop", item->GetName(), (unsigned int)item->GetStackCount(), 0 ); psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf); } } else CacheManager::GetSingleton().RemoveInstance(item); } offering[i] = NULL; } // Money is handled in TransferMoney } void ExchangingCharacter::TransferOffer(psCharacter *npc) { for (int i=0; i < EXCHANGE_SLOT_COUNT; i++) { psItem *item = offering[i]; if (item != NULL) { #ifdef GIVE_GOES_TO_NPC_INVENTORY if ( !npc->Inventory().PutInBulk(item) ) { // Drop item on ground!! npc->DropItem(item); } #else item->Destroy(); #endif } offering[i] = NULL; } } int ExchangingCharacter::GetMoneyCount( int moneyType ) { return offeringMoney.Get(moneyType); } int ExchangingCharacter::GetTotalMoneyCount() //added RMS { return offeringMoney.GetTotal(); } void ExchangingCharacter::AdjustMoney(int type, int delta) { offeringMoney.Adjust( type, delta ); } void ExchangingCharacter::AdjustMoney(const psMoney& amount) { offeringMoney += amount; } void ExchangingCharacter::UpdateReceivingMoney( psMoney& money ) { psExchangeMoneyMsg message( client, CONTAINER_RECEIVING_MONEY, money.Get(MONEY_TRIAS), money.Get(MONEY_HEXAS), money.Get(MONEY_CIRCLES), money.Get(MONEY_OCTAS) ); message.SendMessage(); } void ExchangingCharacter::UpdateOfferingMoney() { psExchangeMoneyMsg message( client, CONTAINER_OFFERING_MONEY, offeringMoney.Get(MONEY_TRIAS), offeringMoney.Get(MONEY_HEXAS), offeringMoney.Get(MONEY_CIRCLES), offeringMoney.Get(MONEY_OCTAS) ); message.SendMessage(); } void ExchangingCharacter::GetOffering(csString& buff) { csString moneystr = offeringMoney.ToString(); buff.Format("", moneystr.GetData() ); unsigned int z; for ( z = 0; z < EXCHANGE_SLOT_COUNT; z++ ) { psItem *item = offering[z]; if (item) { csString itemStr; itemStr.Format( "", item->GetName(), z, item->GetStackCount(), item->GetSumWeight(), item->GetImageName()); buff.Append(itemStr); } } buff.Append(""); } void ExchangingCharacter::GetSimpleOffering(csString& buff, bool exact) { csString moneystr = offeringMoney.ToString(); if (exact) { buff.Format("", moneystr.GetData() ); } else { buff.Format("", offeringMoney.GetTotal()); } unsigned int z; for ( z = 0; z < EXCHANGE_SLOT_COUNT; z++ ) { psItem *item = offering[z]; if (item) { csString itemStr; itemStr.Format("", item->GetName(), item->GetStackCount()); buff.Append(itemStr); } } buff.Append(""); } float ExchangingCharacter::GetSumSize() { float size = 0.0; psItem *item = NULL; for (int i=0; i < EXCHANGE_SLOT_COUNT; i++) { item = offering[i]; if (item != NULL) { size += item->GetSumSize(); } } return size; } float ExchangingCharacter::GetSumWeight() { float weight = 0.0; psItem *item = NULL; for (int i=0; i < EXCHANGE_SLOT_COUNT; i++) { item = offering[i]; if (item != NULL) { weight += item->GetSumWeight(); } } return weight; } /*********************************************************************************** * * class Exchange * ***********************************************************************************/ /** * Common base for different kinds of exchanges */ Exchange::Exchange(Client* starter, csRef manager) : playerChar( starter->GetClientNum() ) { id = next_id++; exchangeEnded = false; exchangeSuccess = false; exchangeMgr = manager; starterClient = starter; this->player = starter->GetClientNum(); starter->GetActor()->RegisterCallback( this ); } Exchange::~Exchange() { if ( !exchangeSuccess ) { playerChar.TransferOffer(player); playerChar.TransferMoney(player); psserver->GetCharManager()->SendInventory(player); } SendEnd( player ); starterClient->ExchangeID(0); } int Exchange::AddItem(Client* client, psItem *& item, int toSlot, bool test) { if ( client == starterClient ) { int remainder = playerChar.InsertItem( toSlot, item, test); if(remainder != -1 && !test) { UpdateOffering( starterClient, item, toSlot); } return remainder; } return -1; } int Exchange::GetMoneyCount( Client* client, int moneyType ) { if ( client == starterClient ) return playerChar.GetMoneyCount(moneyType); return 0; } bool Exchange::AdjustMoney( Client* client, int moneyType, int newMoney ) { if ( client == starterClient ) { playerChar.AdjustMoney( moneyType, newMoney); playerChar.UpdateOfferingMoney(); return true; } return false; } bool Exchange::AdjustMoney( Client* client, const psMoney& amount ) { if ( client == starterClient ) { playerChar.AdjustMoney( amount); playerChar.UpdateOfferingMoney(); return true; } return false; } psItem* Exchange::GetItem(Client* client, int fromSlot) { if ( client == starterClient ) { return playerChar.GetOffering()[fromSlot]; } return NULL; } psItem* Exchange::RemoveItem(Client* client, int fromSlot, int stackCount, int &remain ) { if ( client == starterClient ) { psItem* item = playerChar.RemoveItem(fromSlot, stackCount, remain); RemoveOffering( starterClient, fromSlot, remain ); return item; } return NULL; } void Exchange::SendEnd(int clientNum) { psExchangeEndMsg msg(clientNum); psserver->GetEventManager()->SendMessage(msg.msg); } void Exchange::RemoveOffering( Client* client, int slot, int remain ) { psExchangeRemoveItemMsg msg( client->GetClientNum(), CONTAINER_EXCHANGE_OFFERING, slot, remain ); psserver->GetEventManager()->SendMessage(msg.msg); } void Exchange::RemoveReceiving( Client* client, int slot, int remain ) { psExchangeRemoveItemMsg msg( client->GetClientNum(), CONTAINER_EXCHANGE_RECEIVING, slot, remain ); psserver->GetEventManager()->SendMessage(msg.msg); } void Exchange::UpdateOffering( Client* client, psItem* item, int toSlot ) { csString name( item->GetName() ); csString icon( item->GetImageName() ); psExchangeAddItemMsg msg( client->GetClientNum(), name, CONTAINER_EXCHANGE_OFFERING, toSlot, item->GetStackCount(), icon ); psserver->GetEventManager()->SendMessage( msg.msg ); } void Exchange::UpdateReceiving( Client* client, psItem* item, int toSlot ) { csString name( item->GetName() ); csString icon( item->GetImageName() ); psExchangeAddItemMsg msg( client->GetClientNum(), name, CONTAINER_EXCHANGE_RECEIVING, toSlot, item->GetStackCount(), icon ); psserver->GetEventManager()->SendMessage( msg.msg ); } psItem* Exchange::GetStarterOffer(int slot) { if (slot < 0 || slot >= EXCHANGE_SLOT_COUNT) return NULL; psItem **offering = playerChar.GetOffering(); return offering[slot]; } int Exchange::next_id = 1; //------------------------------------------------------------------------------ PlayerToPlayerExchange::PlayerToPlayerExchange(Client* player, Client* target, csRef manager) :Exchange(player, manager), targetChar(target->GetClientNum()) { targetClient = target; this->target = target->GetClientNum(); playerAccepted = targetAccepted = false; SendRequestToBoth(); targetClient->GetActor()->RegisterCallback( this ); } void PlayerToPlayerExchange::SendRequestToBoth() { csString targetName( targetClient->GetName() ); psExchangeRequestMsg one( starterClient->GetClientNum(), targetName, true ); psserver->GetEventManager()->SendMessage( one.msg ); csString clientName( starterClient->GetName() ); psExchangeRequestMsg two( targetClient->GetClientNum(), clientName, true ); psserver->GetEventManager()->SendMessage( two.msg ); } void PlayerToPlayerExchange::DeleteObjectCallback(iDeleteNotificationObject * object) { // Make sure both actors are disconnected. starterClient->GetActor()->UnregisterCallback( this ); targetClient->GetActor()->UnregisterCallback( this ); HandleEnd( starterClient ); exchangeMgr->DeleteExchange( this ); } PlayerToPlayerExchange::~PlayerToPlayerExchange() { if ( !exchangeSuccess ) { targetChar.TransferOffer(target); targetChar.TransferMoney(target); psserver->GetCharManager()->SendInventory(target); } SendEnd( target ); targetClient->ExchangeID(0); starterClient->GetActor()->UnregisterCallback( this ); targetClient->GetActor()->UnregisterCallback( this ); } int PlayerToPlayerExchange::AddItem(Client* client, psItem *& item, int toSlot, bool test ) { // If the client is the starter then update the targets receive list int remainder = Exchange::AddItem( client, item, toSlot, test ); if ( remainder != -1) { if(test) return remainder; UpdateReceiving( targetClient, item, toSlot ); } else if ( client == targetClient ) { remainder = targetChar.InsertItem( toSlot, item, test); if(test) return remainder; UpdateOffering( targetClient, item, toSlot ); UpdateReceiving( starterClient, item, toSlot ); } else return remainder; //Remove accepted state playerAccepted = false; targetAccepted = false; SendExchangeToBoth(); return remainder; } int PlayerToPlayerExchange::GetMoneyCount( Client *client, int moneyType) { if ( client == starterClient ) return playerChar.GetMoneyCount(moneyType); if ( client == targetClient ) return targetChar.GetMoneyCount(moneyType); return 0; } bool PlayerToPlayerExchange::AdjustMoney( Client* client, int moneyType, int newMoney ) { if ( Exchange::AdjustMoney( client, moneyType, newMoney ) ) { targetChar.UpdateReceivingMoney( playerChar.offeringMoney ); } else if (client == targetClient ) { targetChar.AdjustMoney( moneyType, newMoney); targetChar.UpdateOfferingMoney(); playerChar.UpdateReceivingMoney( targetChar.offeringMoney ); } //Remove accepted state playerAccepted = false; targetAccepted = false; SendExchangeToBoth(); return true; } bool PlayerToPlayerExchange::AdjustMoney( Client* client, const psMoney& money ) { if ( Exchange::AdjustMoney( client, money ) ) { targetChar.UpdateReceivingMoney( playerChar.offeringMoney ); } else if (client == targetClient ) { targetChar.AdjustMoney( money); targetChar.UpdateOfferingMoney(); playerChar.UpdateReceivingMoney( targetChar.offeringMoney ); } //Remove accepted state playerAccepted = false; targetAccepted = false; SendExchangeToBoth(); return true; } psItem* PlayerToPlayerExchange::GetItem( Client* client, int fromSlot) { psItem *item = NULL; item = Exchange::GetItem( client, fromSlot); if(!item) item = targetChar.GetOffering()[fromSlot]; return item; } psItem* PlayerToPlayerExchange::RemoveItem( Client* client, int fromSlot, int stackCount, int &remain ) { psItem* item = 0; int left; item = Exchange::RemoveItem( client, fromSlot, stackCount, left ); if ( item ) { RemoveReceiving( targetClient, fromSlot, left ); } else if ( client == targetClient ) { item = targetChar.RemoveItem( fromSlot, stackCount, left ); RemoveOffering( targetClient, fromSlot, left ); RemoveReceiving( starterClient, fromSlot, left ); } //Remove accepted state playerAccepted = false; targetAccepted = false; SendExchangeToBoth(); return item; } bool PlayerToPlayerExchange::CheckExchange(int clientNum, bool checkRange) { ClientConnectionSet * clients = psserver->GetConnections(); Client * playerClient = clients->Find(player); if (playerClient == NULL) { exchangeEnded = true; return false; } Client * targetClient = clients->Find(target); if (targetClient == NULL) { exchangeEnded = true; return false; } if (checkRange) { if (clientNum == player) { if ( ! CheckRange(player, playerClient->GetActor(), targetClient->GetActor()) ) { psserver->GetCharManager()->SendInventory(clientNum); return false; } } else { if ( ! CheckRange(target, targetClient->GetActor(), playerClient->GetActor()) ) { psserver->GetCharManager()->SendInventory(clientNum); return false; } } } return true; } bool PlayerToPlayerExchange::HandleAccept(Client * client) { if ( ! CheckExchange(client->GetClientNum(), true)) return exchangeEnded; if (client->GetClientNum() == player) { float capacityBalance = targetChar.GetSumSize()- playerChar.GetSumSize(); float weightBalance = targetChar.GetSumWeight() - playerChar.GetSumWeight(); if ( starterClient->GetCharacterData()->Inventory().HasSpace( capacityBalance ) && starterClient->GetCharacterData()->Inventory().HasWeight( weightBalance ) ) { playerAccepted = true; } else { psserver->SendSystemError( starterClient->GetClientNum(), "This is too much for you to carry!"); } } else { float capacityBalance = playerChar.GetSumSize()- targetChar.GetSumSize(); float weightBalance = playerChar.GetSumWeight() - targetChar.GetSumWeight(); if ( targetClient->GetCharacterData()->Inventory().HasSpace( capacityBalance ) && targetClient->GetCharacterData()->Inventory().HasWeight( weightBalance ) ) { targetAccepted = true; } else { psserver->SendSystemError( targetClient->GetClientNum(), "This is too much for you to carry!"); } } if (playerAccepted && targetAccepted) { Debug1(LOG_EXCHANGES,client->GetClientNum(),"Both Exchanged\n"); Debug2(LOG_EXCHANGES,client->GetClientNum(),"Exchange %d have been accepted by both clients\n",id); ClientConnectionSet * clients = psserver->GetConnections(); const char* playerName; const char* targetName; if (player !=0) playerName = clients->Find(player)->GetName(); else playerName = "None"; if (target !=0) targetName = clients->Find(target)->GetName(); else targetName = "None"; csString buf; csString items; psItem **offering = playerChar.GetOffering(); for (int i=0; i < EXCHANGE_SLOT_COUNT; i++) { psItem *item = offering[i]; if (item != NULL) { buf.Format("%s, ", item->GetName()); items.Append(buf); } } if (!items.IsEmpty()) { items.Truncate(items.Length() - 2); csString buf; buf.Format("%s, %s, %s, \"%s\", %d, %d", playerName, targetName, "P2P Exchange", items.GetData(), 0, 0); psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf); } items.Clear(); offering = targetChar.GetOffering(); for (int i=0; i < EXCHANGE_SLOT_COUNT; i++) { psItem *item = offering[i]; if (item != NULL) { buf.Format("%s, ", item->GetName()); items.Append(buf); } } if (!items.IsEmpty()) { items.Truncate(items.Length() - 2); csString buf; buf.Format("%s, %s, %s, \"%s\", %d, %d", targetName, playerName, "P2P Exchange", items.GetData(), 0, 0); psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf); } csString itemsOffered; // RMS: Output items start client gave target client if (ExchangedItems(playerChar, itemsOffered)) { psserver->SendSystemBaseInfo( targetClient->GetClientNum(), "%s gave %s %s.", starterClient->GetActor()->GetName(), targetClient->GetActor()->GetName(), itemsOffered.GetData()); // RMS: Trade cancelled without items being offered psserver->SendSystemBaseInfo( starterClient->GetClientNum(), "%s gave %s %s.", starterClient->GetActor()->GetName(), targetClient->GetActor()->GetName(), itemsOffered.GetData()); // RMS: Trade cancelled without items being offered } // RMS: Output items target client gave start client if (ExchangedItems(targetChar, itemsOffered)) { psserver->SendSystemBaseInfo( targetClient->GetClientNum(), "%s gave %s %s.", targetClient->GetActor()->GetName(), starterClient->GetActor()->GetName(), itemsOffered.GetData()); // RMS: Trade cancelled without items being offered psserver->SendSystemBaseInfo( starterClient->GetClientNum(), "%s gave %s %s.", targetClient->GetActor()->GetName(), starterClient->GetActor()->GetName(), itemsOffered.GetData()); // RMS: Trade cancelled without items being offered } playerChar.TransferOffer(target); playerChar.TransferMoney(target); targetChar.TransferOffer(player); targetChar.TransferMoney(player); psserver->SendSystemOK(targetClient->GetClientNum(),"Trade complete"); psserver->SendSystemOK(starterClient->GetClientNum(),"Trade complete"); psserver->GetCharManager()->UpdateItemViews(player); psserver->GetCharManager()->UpdateItemViews(target); SendEnd(player); SendEnd(target); exchangeSuccess = true; return true; } else SendExchangeToBoth(); return false; } void PlayerToPlayerExchange::HandleEnd(Client * client) { csString itemsOffered; // RMS: Target client if (ExchangedItems(playerChar, itemsOffered)) psserver->SendSystemResult( targetClient->GetClientNum(), "Trade declined" ); // RMS: Trade cancelled without items being offered else psserver->SendSystemResult( targetClient->GetClientNum(), "Trade cancelled"); // RMS: Starter client if (ExchangedItems(targetChar, itemsOffered)) psserver->SendSystemResult( starterClient->GetClientNum(), "Trade declined" ); // RMS: Trade cancelled without items being offered else psserver->SendSystemResult( starterClient->GetClientNum(), "Trade cancelled"); if (client->GetClientNum() == player) SendEnd(target); else SendEnd(player); } bool PlayerToPlayerExchange::ExchangedItems(ExchangingCharacter givingChar, csString& text) { psItem **offering = givingChar.GetOffering(); text.Clear(); // RMS: Loop through all items offered for (int i=0; i < EXCHANGE_SLOT_COUNT; i++) { psItem *item = offering[i]; if (item != NULL) { text += item->GetQuantityName(); text += ", "; } } // RMS: Add the money count if (givingChar.GetTotalMoneyCount() > 0) { text.AppendFmt("%i Trias, ", givingChar.GetTotalMoneyCount()); } if (!text.IsEmpty()) { text.Truncate(text.Length() - 2); return true; } return false; } bool PlayerToPlayerExchange::SendExchange(int clientNum) { if ( starterClient->GetClientNum() == clientNum ) { psExchangeStatusMsg mesg( clientNum, playerAccepted, targetAccepted ); psserver->GetEventManager()->SendMessage( mesg.msg ); } else { psExchangeStatusMsg mesg( clientNum, targetAccepted, playerAccepted ); psserver->GetEventManager()->SendMessage( mesg.msg ); } return true; } void PlayerToPlayerExchange::SendExchangeToBoth() { SendExchange(player); SendExchange(target); } bool PlayerToPlayerExchange::Involves(psCharacter * character) { return (GetClientCharacter(player) == character) || (GetClientCharacter(target) == character); } psItem* PlayerToPlayerExchange::GetTargetOffer(int slot) { if (slot < 0 || slot >= EXCHANGE_SLOT_COUNT) return NULL; psItem **offering = targetChar.GetOffering(); return offering[slot]; } /*********************************************************************************** * * class NPCExchange * ***********************************************************************************/ PlayerToNPCExchange::PlayerToNPCExchange(Client* player, gemObject* target, csRef manager) : Exchange(player, manager) { this->target = target; csString targetName( target->GetName() ); psExchangeRequestMsg one( starterClient->GetClientNum(), targetName, false ); psserver->GetEventManager()->SendMessage( one.msg ); target->RegisterCallback( this ); } PlayerToNPCExchange::~PlayerToNPCExchange() { starterClient->GetActor()->UnregisterCallback( this ); target->UnregisterCallback( this ); } gemObject * PlayerToNPCExchange::GetTargetGEM() { return target; } bool PlayerToNPCExchange::CheckExchange(int clientNum, bool checkRange) { ClientConnectionSet * clients = psserver->GetConnections(); Client * playerClient = clients->Find(player); if (playerClient == NULL) { exchangeEnded = true; return false; } gemObject * targetGEM = GetTargetGEM(); if (targetGEM == NULL) { exchangeEnded = true; return false; } if (checkRange) if ( ! CheckRange(clientNum, playerClient->GetActor(), targetGEM) ) { psserver->GetCharManager()->SendInventory(clientNum); return false; } return true; } void PlayerToNPCExchange::HandleEnd(Client * client) { return; } bool PlayerToNPCExchange::CheckXMLResponse(Client * client, psNPCDialog *dlg, csString trigger) { NpcResponse *resp = dlg->FindXMLResponse(client, trigger); if (resp && resp->type != NpcResponse::ERROR_RESPONSE) { if (resp->ExecuteScript(client, (gemNPC *)target)) { // Give offered items to npc playerChar.TransferOffer(target->GetCharacterData()); playerChar.TransferMoney(target->GetCharacterData()); exchangeSuccess = true; client->SetLastResponse(resp->id); return true; } } return false; } bool PlayerToNPCExchange::HandleAccept(Client * client) { if ( ! CheckExchange(client->GetClientNum(), true)) return exchangeEnded; // Send to the npc as an event trigger psNPCDialog *dlg = target->GetNPCDialogPtr(); if (dlg == NULL) psserver->SendSystemError(client->GetClientNum(), "%s does not respond to gifts.", target->GetName()); else { csString trigger; // Check if NPC require the exact among of money playerChar.GetSimpleOffering(trigger,true); if (!CheckXMLResponse(client,dlg,trigger)) { // Check if NPC is OK with all the money total in trias playerChar.GetSimpleOffering(trigger,false); if (!CheckXMLResponse(client,dlg,trigger)) { psserver->SendSystemError(client->GetClientNum(), "%s does not need that.", target->GetName()); } } } psserver->SendSystemOK(client->GetClientNum(),"Trade complete"); psserver->GetCharManager()->SendInventory(player); SendEnd(player); return true; } bool PlayerToNPCExchange::Involves(psCharacter * character) { gemObject * gem = GetTargetGEM(); if (gem!=NULL && gem->GetCharacterData()==character) return true; return (GetClientCharacter(player) == character); } void PlayerToNPCExchange::DeleteObjectCallback(iDeleteNotificationObject * object) { starterClient->GetActor()->UnregisterCallback( this ); target->UnregisterCallback( this ); HandleEnd( starterClient ); exchangeMgr->DeleteExchange( this ); } /*********************************************************************************** * * class ExchangeManager * ***********************************************************************************/ ExchangeManager::ExchangeManager(ClientConnectionSet *pClnts) { clients = pClnts; psserver->GetEventManager()->Subscribe(this, MSGTYPE_GUIEXCHANGE, REQUIRE_READY_CLIENT|REQUIRE_ALIVE); psserver->GetEventManager()->Subscribe(this, MSGTYPE_EXCHANGE_REQUEST, REQUIRE_READY_CLIENT|REQUIRE_ALIVE|REQUIRE_TARGETACTOR); psserver->GetEventManager()->Subscribe(this, MSGTYPE_EXCHANGE_ACCEPT, REQUIRE_READY_CLIENT|REQUIRE_ALIVE); psserver->GetEventManager()->Subscribe(this, MSGTYPE_EXCHANGE_END, REQUIRE_READY_CLIENT|REQUIRE_ALIVE); } ExchangeManager::~ExchangeManager() { if (psserver->GetEventManager()) { psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_GUIEXCHANGE); psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_EXCHANGE_REQUEST); psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_EXCHANGE_ACCEPT); psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_EXCHANGE_END); } } void ExchangeManager::StartExchange( Client* client, bool withPlayer ) { // Make sure we are in a peaceful mode before trading if ( client->GetActor()->GetMode() != PSCHARACTER_MODE_PEACE ) { psserver->SendSystemInfo(client->GetClientNum(), "Can only trade when not engaged in combat"); return; } if ( client->ExchangeID() ) { psserver->SendSystemError(client->GetClientNum(), "You are already busy with another trade" ); return; } Client * targetClient = 0; gemObject * target; target = client->GetTargetObject(); if ( target->IsAlive() == false ) { psserver->SendSystemError(client->GetClientNum(), "Cannot give items to dead things!"); return; } // if the command was "/give": if (!withPlayer) { if ( !target->GetNPCPtr() ) { psserver->SendSystemError(client->GetClientNum(), "You can give items to NPC only"); return; } Exchange* exchange = new PlayerToNPCExchange(client, target, this); client->ExchangeID( exchange->GetID() ); exchanges.Push (exchange); } // if the command was "/trade": else { if ( target->GetNPCPtr() ) { psserver->SendSystemError(client->GetClientNum(), "You can trade with other players only"); return; } targetClient = clients->FindPlayer(target->GetPlayerID()); if (targetClient == NULL) { psserver->SendSystemError(client->GetClientNum(), "Cannot find your target!"); return; } if ( client == targetClient ) { psserver->SendSystemError(client->GetClientNum(),"You can not trade with yourself"); return; } if ( targetClient->ExchangeID() ) { psserver->SendSystemError(client->GetClientNum(), "%s is busy with another trade", targetClient->GetName() ); return; } // Must ask the other player before starting the trade csString question; question.Format( "Do you want to trade with %s ?", client->GetCharacterData()->GetCharName() ); PendingQuestion *invite = new PendingTradeInvite( client, targetClient, question.GetData() ); psserver->questionmanager->SendQuestion(invite); } } void ExchangeManager::HandleMessage(MsgEntry *me,Client *client) { switch ( me->GetType() ) { case MSGTYPE_EXCHANGE_END: { Notify2( LOG_TRADE, "Trade %d Rejected", client->ExchangeID() ); Exchange* exchange = GetExchange( client->ExchangeID() ); if ( exchange ) { exchange->HandleEnd(client); DeleteExchange(exchange); } else { Warning2( LOG_TRADE, "Trade %d Not located", client->ExchangeID() ); } break; } case MSGTYPE_EXCHANGE_REQUEST: { Notify2( LOG_TRADE, "Trade Requested from %u", client->GetClientNum() ); psExchangeRequestMsg msg(me); StartExchange( client, msg.withPlayer ); break; } case MSGTYPE_EXCHANGE_ACCEPT: { Notify3( LOG_TRADE, "Exchange %d Accept from %d", client->ExchangeID(), client->GetClientNum() ); Exchange* exchange = GetExchange( client->ExchangeID() ); if ( exchange ) { if (exchange->HandleAccept(client)) { DeleteExchange(exchange); } } break; } } } Exchange * ExchangeManager::GetExchange(int id) { // TODO: Make this a hashmap for (size_t n = 0; n < exchanges.Length(); n++){ if (exchanges[n]->GetID() == id) return exchanges[n]; } Error2("Can't find exchange: %d !!!",id); return NULL; } void ExchangeManager::DeleteExchange(Exchange * exchange) { exchanges.Delete(exchange); // deletes the exchange object }