2018-11-10 19:00:13 +01:00
|
|
|
#include "HousingMgr.h"
|
|
|
|
#include <Logging/Logger.h>
|
|
|
|
#include <Database/DatabaseDef.h>
|
|
|
|
#include <Exd/ExdDataGenerated.h>
|
2018-11-13 23:46:10 +01:00
|
|
|
#include <Network/PacketContainer.h>
|
|
|
|
#include <Network/PacketDef/Zone/ServerZoneDef.h>
|
|
|
|
#include <Network/PacketWrappers/ActorControlPacket142.h>
|
|
|
|
#include <Network/PacketWrappers/ActorControlPacket143.h>
|
|
|
|
#include <Network/CommonActorControl.h>
|
2018-11-10 19:00:13 +01:00
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
#include "Actor/Player.h"
|
|
|
|
|
2018-11-19 11:55:29 +01:00
|
|
|
#include "TerritoryMgr.h"
|
2018-11-10 19:00:13 +01:00
|
|
|
#include "Zone.h"
|
|
|
|
#include "HousingZone.h"
|
|
|
|
#include "HousingMgr.h"
|
|
|
|
#include "Land.h"
|
|
|
|
#include "Framework.h"
|
2018-11-20 21:32:13 +01:00
|
|
|
#include "ServerMgr.h"
|
2018-11-13 23:46:10 +01:00
|
|
|
|
|
|
|
using namespace Core::Common;
|
|
|
|
using namespace Core::Network;
|
|
|
|
using namespace Core::Network::Packets;
|
|
|
|
using namespace Core::Network::Packets::Server;
|
2018-11-10 19:00:13 +01:00
|
|
|
|
|
|
|
extern Core::Framework g_fw;
|
|
|
|
|
2018-11-19 11:55:29 +01:00
|
|
|
Core::HousingMgr::HousingMgr()
|
2018-11-10 19:00:13 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Core::HousingMgr::~HousingMgr()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::HousingMgr::init()
|
|
|
|
{
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-16 17:07:22 +01:00
|
|
|
uint32_t Core::HousingMgr::toLandSetId( uint16_t territoryTypeId, uint8_t wardId ) const
|
|
|
|
{
|
|
|
|
return ( static_cast< uint32_t >( territoryTypeId ) << 16 ) | wardId;
|
|
|
|
}
|
|
|
|
|
2018-11-19 11:55:29 +01:00
|
|
|
Core::Data::HousingZonePtr Core::HousingMgr::getHousingZoneByLandSetId( uint32_t id )
|
2018-11-10 19:00:13 +01:00
|
|
|
{
|
2018-11-19 11:55:29 +01:00
|
|
|
auto pTeriMgr = g_fw.get< TerritoryMgr >();
|
|
|
|
return std::dynamic_pointer_cast< HousingZone >( pTeriMgr->getZoneByLandSetId( id ) );
|
2018-11-10 19:00:13 +01:00
|
|
|
}
|
|
|
|
|
2018-11-19 11:55:29 +01:00
|
|
|
Core::LandPtr Core::HousingMgr::getLandByOwnerId( uint32_t id )
|
2018-11-10 19:00:13 +01:00
|
|
|
{
|
2018-11-19 11:55:29 +01:00
|
|
|
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
|
|
|
auto res = pDb->query( "SELECT LandSetId, LandId FROM land WHERE OwnerId = " + std::to_string( id ) );
|
|
|
|
|
|
|
|
if( !res->next() )
|
2018-11-10 19:00:13 +01:00
|
|
|
return nullptr;
|
|
|
|
|
2018-11-19 11:55:29 +01:00
|
|
|
auto hZone = getHousingZoneByLandSetId( res->getUInt( 1 ) );
|
2018-11-10 19:00:13 +01:00
|
|
|
|
2018-11-19 11:55:29 +01:00
|
|
|
if( !hZone )
|
|
|
|
return nullptr;
|
2018-11-11 14:27:39 +01:00
|
|
|
|
2018-11-19 11:55:29 +01:00
|
|
|
return hZone->getLand( res->getUInt( 2 ) );
|
2018-11-10 19:00:13 +01:00
|
|
|
}
|
2018-11-13 23:46:10 +01:00
|
|
|
|
2018-11-16 17:07:22 +01:00
|
|
|
void Core::HousingMgr::sendLandSignOwned( Entity::Player& player, uint8_t wardId, uint8_t plotId, uint16_t territoryTypeId )
|
2018-11-13 23:46:10 +01:00
|
|
|
{
|
2018-11-16 17:07:22 +01:00
|
|
|
player.setActiveLand( plotId, wardId );
|
2018-11-13 23:46:10 +01:00
|
|
|
|
2018-11-16 17:07:22 +01:00
|
|
|
auto landSetId = toLandSetId( territoryTypeId, wardId );
|
|
|
|
auto hZone = getHousingZoneByLandSetId( landSetId );
|
2018-11-13 23:46:10 +01:00
|
|
|
|
|
|
|
if( !hZone )
|
|
|
|
return;
|
|
|
|
|
2018-11-16 17:07:22 +01:00
|
|
|
auto land = hZone->getLand( plotId );
|
2018-11-13 23:46:10 +01:00
|
|
|
if( !land )
|
|
|
|
{
|
|
|
|
land = getLandByOwnerId( player.getId() );
|
|
|
|
}
|
|
|
|
|
|
|
|
auto landInfoSignPacket = makeZonePacket< Server::FFXIVIpcLandInfoSign >( player.getId() );
|
|
|
|
uint32_t playerId = land->getPlayerOwner();
|
2018-11-20 21:32:13 +01:00
|
|
|
std::string playerName = g_fw.get< Core::ServerMgr >()->getPlayerNameFromDb( playerId );
|
2018-11-13 23:46:10 +01:00
|
|
|
//memcpy( &landInfoSignPacket->data().estateGreeting, "Hello World", 11 );
|
|
|
|
//memcpy( &landInfoSignPacket->data().estateName, land->getLandName().c_str(), land->getLandName().size() );
|
2018-11-14 10:00:16 +01:00
|
|
|
landInfoSignPacket->data().houseSize = land->getSize();
|
2018-11-13 23:46:10 +01:00
|
|
|
landInfoSignPacket->data().houseType = static_cast< uint8_t >( land->getLandType() );
|
2018-11-26 23:15:42 +01:00
|
|
|
landInfoSignPacket->data().landIdent.landId = land->getLandId();
|
|
|
|
landInfoSignPacket->data().landIdent.wardNum = land->getWardNum();
|
|
|
|
landInfoSignPacket->data().landIdent.worldId = 67;
|
|
|
|
landInfoSignPacket->data().landIdent.territoryTypeId = land->getTerritoryTypeId();
|
2018-11-13 23:46:10 +01:00
|
|
|
landInfoSignPacket->data().ownerId = player.getContentId(); // should be real owner contentId, not player.contentId()
|
|
|
|
memcpy( &landInfoSignPacket->data().ownerName, playerName.c_str(), playerName.size() );
|
2018-11-26 23:15:42 +01:00
|
|
|
|
2018-11-13 23:46:10 +01:00
|
|
|
player.queuePacket( landInfoSignPacket );
|
|
|
|
}
|
|
|
|
|
2018-11-16 17:07:22 +01:00
|
|
|
void Core::HousingMgr::sendLandSignFree( Entity::Player& player, uint8_t wardId, uint8_t plotId, uint16_t territoryTypeId )
|
2018-11-13 23:46:10 +01:00
|
|
|
{
|
2018-11-16 17:07:22 +01:00
|
|
|
player.setActiveLand( plotId, wardId );
|
2018-11-13 23:46:10 +01:00
|
|
|
|
2018-11-16 17:07:22 +01:00
|
|
|
auto landSetId = toLandSetId( territoryTypeId, wardId );
|
|
|
|
auto hZone = getHousingZoneByLandSetId( landSetId );
|
2018-11-13 23:46:10 +01:00
|
|
|
|
|
|
|
if( !hZone )
|
|
|
|
return;
|
|
|
|
|
2018-11-16 17:07:22 +01:00
|
|
|
auto land = hZone->getLand( plotId );
|
2018-11-13 23:46:10 +01:00
|
|
|
auto plotPricePacket = makeZonePacket< Server::FFXIVIpcLandPriceUpdate >( player.getId() );
|
|
|
|
plotPricePacket->data().price = land->getCurrentPrice();
|
|
|
|
plotPricePacket->data().timeLeft = land->getDevaluationTime();
|
|
|
|
player.queuePacket( plotPricePacket );
|
|
|
|
}
|
2018-11-15 12:40:02 +01:00
|
|
|
|
2018-11-19 09:40:44 +01:00
|
|
|
Core::LandPurchaseResult Core::HousingMgr::purchaseLand( Entity::Player& player, uint8_t plot, uint8_t state )
|
2018-11-15 12:40:02 +01:00
|
|
|
{
|
|
|
|
auto pHousing = std::dynamic_pointer_cast< HousingZone >( player.getCurrentZone() );
|
|
|
|
|
|
|
|
auto plotPrice = pHousing->getLand( plot )->getCurrentPrice();
|
|
|
|
auto gilAvailable = player.getCurrency( CurrencyType::Gil );
|
|
|
|
auto pLand = pHousing->getLand( plot );
|
|
|
|
|
|
|
|
if( !pLand )
|
|
|
|
return LandPurchaseResult::ERR_INTERNAL;
|
|
|
|
|
|
|
|
if( pLand->getState() != HouseState::forSale )
|
|
|
|
return LandPurchaseResult::ERR_NOT_AVAILABLE;
|
2018-11-20 22:52:57 +11:00
|
|
|
|
2018-11-15 12:40:02 +01:00
|
|
|
if( gilAvailable < plotPrice )
|
|
|
|
return LandPurchaseResult::ERR_NOT_ENOUGH_GIL;
|
2018-11-20 22:52:57 +11:00
|
|
|
|
|
|
|
|
2018-11-15 12:40:02 +01:00
|
|
|
switch( static_cast< LandPurchaseMode >( state ) )
|
|
|
|
{
|
|
|
|
case LandPurchaseMode::FC:
|
|
|
|
player.sendDebug( "Free company house purchase aren't supported at this time." );
|
|
|
|
return LandPurchaseResult::ERR_INTERNAL;
|
2018-11-20 22:52:57 +11:00
|
|
|
|
2018-11-15 12:40:02 +01:00
|
|
|
case LandPurchaseMode::PRIVATE:
|
|
|
|
{
|
|
|
|
|
|
|
|
auto pOldLand = getLandByOwnerId( player.getId() );
|
|
|
|
|
|
|
|
if( pOldLand )
|
|
|
|
return LandPurchaseResult::ERR_NO_MORE_LANDS_FOR_CHAR;
|
2018-11-15 22:30:59 +01:00
|
|
|
|
2018-11-15 12:40:02 +01:00
|
|
|
player.removeCurrency( CurrencyType::Gil, plotPrice );
|
|
|
|
pLand->setPlayerOwner( player.getId() );
|
|
|
|
pLand->setState( HouseState::sold );
|
|
|
|
pLand->setLandType( Common::LandType::Private );
|
2018-11-15 22:30:59 +01:00
|
|
|
|
2018-11-27 21:11:02 +11:00
|
|
|
player.setLandState( LandStateSlot::Private, 0x00, plot,
|
2018-11-15 12:40:02 +01:00
|
|
|
pHousing->getWardNum(), pHousing->getTerritoryTypeId() );
|
2018-11-15 22:30:59 +01:00
|
|
|
|
2018-11-27 21:11:02 +11:00
|
|
|
player.sendLandStateSlot( static_cast< uint8_t >( LandType::Private ), plot, pHousing->getWardNum(),
|
2018-11-15 22:30:59 +01:00
|
|
|
pHousing->getTerritoryTypeId() );
|
|
|
|
|
2018-11-15 12:40:02 +01:00
|
|
|
//pLand->setLandName( "Private Estate" + std::to_string( pHousing->getWardNum() ) + "-" + std::to_string( plot ) );
|
|
|
|
pLand->updateLandDb();
|
2018-11-15 22:30:59 +01:00
|
|
|
|
2018-11-15 12:40:02 +01:00
|
|
|
pHousing->sendLandUpdate( plot );
|
|
|
|
return LandPurchaseResult::SUCCESS;
|
|
|
|
}
|
2018-11-20 22:52:57 +11:00
|
|
|
|
2018-11-15 12:40:02 +01:00
|
|
|
default:
|
|
|
|
return LandPurchaseResult::ERR_INTERNAL;
|
|
|
|
}
|
2018-11-20 22:52:57 +11:00
|
|
|
|
2018-11-15 12:40:02 +01:00
|
|
|
}
|
|
|
|
|
2018-11-17 01:16:44 +01:00
|
|
|
bool Core::HousingMgr::relinquishLand( Entity::Player& player, uint8_t plot )
|
|
|
|
{
|
|
|
|
// TODO: Fix "permissions" being sent incorrectly
|
|
|
|
// TODO: Add checks for land state before relinquishing
|
|
|
|
auto pHousing = std::dynamic_pointer_cast< HousingZone >( player.getCurrentZone() );
|
|
|
|
|
|
|
|
auto pLand = pHousing->getLand( plot );
|
|
|
|
auto plotMaxPrice = pLand->getCurrentPrice();
|
2018-11-20 21:21:22 +01:00
|
|
|
auto landOwnerId = pLand->getPlayerOwner();
|
|
|
|
|
|
|
|
// can't relinquish when you are not the owner
|
|
|
|
// TODO: actually use permissions here for FC houses
|
|
|
|
if( landOwnerId != player.getId() )
|
|
|
|
{
|
|
|
|
auto msgPkt = makeActorControl143( player.getId(), ActorControl::LogMsg, 3304, 0 );
|
|
|
|
player.queuePacket( msgPkt );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// unable to relinquish if there is a house built
|
|
|
|
// TODO: additionally check for yard items
|
|
|
|
if( pLand->getHouse() )
|
|
|
|
{
|
|
|
|
auto msgPkt = makeActorControl143( player.getId(), ActorControl::LogMsg, 3315, 0 );
|
|
|
|
player.queuePacket( msgPkt );
|
|
|
|
return false;
|
|
|
|
}
|
2018-11-17 01:16:44 +01:00
|
|
|
|
|
|
|
pLand->setCurrentPrice( pLand->getMaxPrice() );
|
|
|
|
pLand->setPlayerOwner( 0 );
|
|
|
|
pLand->setState( HouseState::forSale );
|
|
|
|
pLand->setLandType( Common::LandType::none );
|
|
|
|
pLand->updateLandDb();
|
|
|
|
|
2018-11-27 21:11:02 +11:00
|
|
|
player.setLandState( LandStateSlot::Private, 0x00, 0xFF, 0xFF, 0xFF );
|
2018-11-17 01:16:44 +01:00
|
|
|
|
2018-11-27 21:11:02 +11:00
|
|
|
player.sendLandStateSlot( static_cast< uint8_t >( LandType::Private ), 0xFF, 0xFF, 0xFF );
|
2018-11-17 01:16:44 +01:00
|
|
|
|
|
|
|
auto screenMsgPkt2 = makeActorControl143( player.getId(), ActorControl::LogMsg, 3351, 0x1AA,
|
|
|
|
pLand->getWardNum() + 1, plot + 1 );
|
|
|
|
player.queuePacket( screenMsgPkt2 );
|
|
|
|
pHousing->sendLandUpdate( plot );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-20 22:52:57 +11:00
|
|
|
void Core::HousingMgr::sendWardLandInfo( Entity::Player& player, uint8_t wardId, uint16_t territoryTypeId )
|
|
|
|
{
|
|
|
|
auto landSetId = toLandSetId( territoryTypeId, wardId );
|
|
|
|
auto hZone = getHousingZoneByLandSetId( landSetId );
|
|
|
|
|
|
|
|
if( !hZone )
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto wardInfoPacket = makeZonePacket< Server::FFXIVIpcHousingWardInfo >( player.getId() );
|
2018-11-26 23:15:42 +01:00
|
|
|
wardInfoPacket->data().landIdent.wardNum = wardId;
|
|
|
|
wardInfoPacket->data().landIdent.territoryTypeId = territoryTypeId;
|
2018-11-20 22:52:57 +11:00
|
|
|
|
|
|
|
for( int i = 0; i < 60; i++ )
|
|
|
|
{
|
|
|
|
auto land = hZone->getLand( i );
|
|
|
|
assert( land );
|
|
|
|
|
|
|
|
auto& entry = wardInfoPacket->data().houseInfoEntry[ i ];
|
|
|
|
|
2018-11-23 17:45:37 +11:00
|
|
|
// retail always sends the house price in this packet, even after the house has been sold
|
|
|
|
// so I guess we do the same
|
2018-11-20 22:52:57 +11:00
|
|
|
entry.housePrice = land->getCurrentPrice();
|
|
|
|
|
2018-11-23 17:45:37 +11:00
|
|
|
if( land->getState() == Common::HouseState::forSale )
|
|
|
|
continue;
|
|
|
|
|
2018-11-20 22:52:57 +11:00
|
|
|
switch( land->getLandType() )
|
|
|
|
{
|
|
|
|
case LandType::FreeCompany:
|
|
|
|
entry.infoFlags = Common::WardEstateFlags::IsEstateOwned | Common::WardEstateFlags::IsFreeCompanyEstate;
|
|
|
|
|
|
|
|
// todo: send FC name
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LandType::Private:
|
|
|
|
entry.infoFlags = Common::WardEstateFlags::IsEstateOwned;
|
|
|
|
|
|
|
|
auto owner = land->getPlayerOwner();
|
2018-11-20 21:32:13 +01:00
|
|
|
std::string playerName = g_fw.get< Core::ServerMgr >()->getPlayerNameFromDb( owner );
|
2018-11-20 22:52:57 +11:00
|
|
|
memcpy( &entry.estateOwnerName, playerName.c_str(), playerName.size() );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo: check we have an estate message and set the flag
|
|
|
|
// todo: check if estate allows public entry
|
|
|
|
}
|
|
|
|
|
|
|
|
player.queuePacket( wardInfoPacket );
|
|
|
|
}
|
|
|
|
|
2018-11-24 15:17:18 +11:00
|
|
|
void Core::HousingMgr::buildPresetEstate( Entity::Player& player, uint8_t plotNum, uint32_t presetItem )
|
|
|
|
{
|
|
|
|
auto hZone = std::dynamic_pointer_cast< HousingZone >( player.getCurrentZone() );
|
|
|
|
|
|
|
|
if( !hZone )
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto pLand = hZone->getLand( plotNum );
|
|
|
|
if( !pLand )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// todo: when doing FC houses, look up the type from the original purchase and check perms from FC and set state accordingly
|
|
|
|
if( pLand->getPlayerOwner() != player.getId() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// todo: check if permit is in inventory and remove one
|
|
|
|
|
2018-11-25 01:55:53 +11:00
|
|
|
if( !pLand->setPreset( presetItem ) )
|
|
|
|
return;
|
|
|
|
|
2018-11-24 15:17:18 +11:00
|
|
|
pLand->setState( HouseState::privateHouse );
|
|
|
|
pLand->setLandType( LandType::Private );
|
|
|
|
hZone->sendLandUpdate( plotNum );
|
|
|
|
|
|
|
|
auto pSuccessBuildingPacket = makeActorControl142( player.getId(), ActorControl::BuildPresetResponse, plotNum );
|
|
|
|
|
|
|
|
player.queuePacket( pSuccessBuildingPacket );
|
2018-11-26 23:32:22 +11:00
|
|
|
|
|
|
|
pLand->updateLandDb();
|
|
|
|
|
|
|
|
// start house built event
|
|
|
|
// CmnDefHousingBuildHouse_00149
|
|
|
|
player.eventStart( player.getId(), 0x000B0095, Event::EventHandler::EventType::Housing, 1, 1 );
|
|
|
|
// todo: wtf are these flags
|
|
|
|
player.playScene( 0x000B0095, 0, 4164955899, 0, 1, plotNum, nullptr );
|
|
|
|
|
|
|
|
// todo: send perms/flags for house
|
2018-11-24 15:17:18 +11:00
|
|
|
}
|