mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-26 14:37:44 +00:00
Merge pull request #428 from NotAdam/housing
Add ward selection, ward info & some script cleanup
This commit is contained in:
commit
cfca3f4e2f
16 changed files with 256 additions and 115 deletions
|
@ -814,6 +814,30 @@ namespace Core::Common
|
|||
heart = 0x06
|
||||
};
|
||||
|
||||
enum HousingAppeal : uint8_t
|
||||
{
|
||||
NoAppeal = 0,
|
||||
Emporium = 1,
|
||||
Botique = 2,
|
||||
DesignerHome = 3,
|
||||
MessageBook = 4,
|
||||
Tavern = 5,
|
||||
Eatery = 6,
|
||||
ImmersiveExperience = 7,
|
||||
Aquarium = 9,
|
||||
Sanctum = 10,
|
||||
Venue = 11,
|
||||
};
|
||||
|
||||
enum WardEstateFlags : uint8_t
|
||||
{
|
||||
IsEstateOwned = 1,
|
||||
IsPublicEstate = 2,
|
||||
HasEstateMessage = 4,
|
||||
EstateFlagUnknown = 8,
|
||||
IsFreeCompanyEstate = 16,
|
||||
};
|
||||
|
||||
using PlayerStateFlagList = std::vector< PlayerStateFlag >;
|
||||
|
||||
}
|
||||
|
|
|
@ -292,8 +292,10 @@ enum ActorControlType : uint16_t
|
|||
RequestHousingBuildPreset = 0x44C,
|
||||
RequestLandSignFree = 0x451,
|
||||
RequestLandSignOwned = 0x452,
|
||||
RequestWardLandInfo = 0x453,
|
||||
RequestLandRelinquish = 0x454,
|
||||
RequestEstateRename = 0x45A,
|
||||
RequestEstateGreeting = 0x45C,
|
||||
RequestHousingItemUI = 0x463,
|
||||
RequestSharedEstateSettings = 0x46F,
|
||||
|
||||
|
|
|
@ -192,11 +192,12 @@ namespace Core::Network::Packets
|
|||
LandRename = 0x0226, // updated 4.4
|
||||
|
||||
LandPermissionSlot = 0x0228, // updated 4.4
|
||||
|
||||
LandPermission = 0x0229, // updated 4.4
|
||||
|
||||
LandSetYardInitialize = 0x022C, // updated 4.4
|
||||
|
||||
HousingWardInfo = 0x022F, // updated 4.4
|
||||
|
||||
YardObjectMove = 0x0230, // updated 4.4
|
||||
|
||||
SharedEstateSettingsResponse = 0x023C, // updated 4.4
|
||||
|
|
|
@ -1720,6 +1720,24 @@ struct FFXIVIpcLandSetYardInitialize : FFXIVIpcBasePacket< LandSetYardInitialize
|
|||
uint32_t unknown4; //unused
|
||||
};
|
||||
|
||||
struct FFXIVIpcHousingWardInfo : FFXIVIpcBasePacket< HousingWardInfo >
|
||||
{
|
||||
uint16_t unknown1;
|
||||
|
||||
uint16_t wardId;
|
||||
uint16_t territoryTypeId;
|
||||
|
||||
uint16_t unknown2;
|
||||
|
||||
struct HouseInfoEntry
|
||||
{
|
||||
uint32_t housePrice;
|
||||
uint8_t infoFlags;
|
||||
Common::HousingAppeal houseAppeal[3];
|
||||
char estateOwnerName[30];
|
||||
} houseInfoEntry[60];
|
||||
};
|
||||
|
||||
/**
|
||||
* Structural representation of the packet sent by the server
|
||||
* to show the current shared estate settings
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
|
||||
#define ACTION_ATTUNE 0x13
|
||||
|
||||
#define AetheryteBaseId 0x50000
|
||||
#define AETHERYTE_MENU_AETHERNET 1
|
||||
#define AETHERYTE_MENU_HOUSING 2
|
||||
#define AETHERYTE_MENU_HOME_POINT 3
|
||||
#define AETHERYTE_MENU_FAVORITE_POINT 4
|
||||
#define AETHERYTE_MENU_FAVORITE_POINT_SECURITY_TOKEN 5
|
||||
|
||||
using namespace Core;
|
||||
|
||||
class Aethernet :
|
||||
public Sapphire::ScriptAPI::EventScript
|
||||
{
|
||||
public:
|
||||
Aethernet() :
|
||||
Sapphire::ScriptAPI::EventScript( EVENTSCRIPT_AETHERNET_ID )
|
||||
{
|
||||
}
|
||||
|
||||
void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override
|
||||
{
|
||||
if( player.isAetheryteRegistered( eventId & 0xFFFF ) )
|
||||
{
|
||||
player.playScene( eventId, 2, 0, []( Entity::Player& player, const Event::SceneResult& result )
|
||||
{
|
||||
if( result.param1 == 256 )
|
||||
{
|
||||
player.teleport( result.param2, 2 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
player.eventActionStart( eventId, ACTION_ATTUNE,
|
||||
[]( Entity::Player& player, uint32_t eventId, uint64_t additional )
|
||||
{
|
||||
player.registerAetheryte( eventId & 0xFFFF );
|
||||
player.playScene( eventId, 3, 0, 0, 0 );
|
||||
},
|
||||
[]( Entity::Player& ply, uint32_t evntId, uint64_t additional )
|
||||
{
|
||||
|
||||
}, 0 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
|
@ -1,33 +1,65 @@
|
|||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
|
||||
#include <Framework.h>
|
||||
#include <Exd/ExdDataGenerated.h>
|
||||
|
||||
#define ACTION_ATTUNE 0x13
|
||||
#define ACTION_TELEPORT 0x4
|
||||
|
||||
#define AetheryteBaseId 0x50000
|
||||
#define AETHERYTE_MENU_AETHERNET 1
|
||||
#define AETHERYTE_MENU_HOUSING 2
|
||||
#define AETHERYTE_MENU_HOME_POINT 3
|
||||
#define AETHERYTE_MENU_FAVORITE_POINT 4
|
||||
#define AETHERYTE_MENU_FAVORITE_POINT_SECURITY_TOKEN 5
|
||||
|
||||
using namespace Core;
|
||||
|
||||
class Aetheryte :
|
||||
public Sapphire::ScriptAPI::EventScript
|
||||
{
|
||||
private:
|
||||
constexpr static auto ACTION_ATTUNE = 0x13;
|
||||
constexpr static auto ACTION_TELEPORT = 0x4;
|
||||
|
||||
constexpr static auto AETHERYTE_MENU_AETHERNET = 1;
|
||||
constexpr static auto AETHERYTE_MENU_HOUSING = 2;
|
||||
constexpr static auto AETHERYTE_MENU_HOME_POINT = 3;
|
||||
constexpr static auto AETHERYTE_MENU_FAVORITE_POINT = 4;
|
||||
constexpr static auto AETHERYTE_MENU_FAVORITE_POINT_SECURITY_TOKEN = 5;
|
||||
|
||||
public:
|
||||
Aetheryte() :
|
||||
Sapphire::ScriptAPI::EventScript( EVENTSCRIPT_AETHERYTE_ID )
|
||||
Sapphire::ScriptAPI::EventScript( 0x00050000 )
|
||||
{
|
||||
}
|
||||
|
||||
void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override
|
||||
void aethernet( uint32_t eventId, Entity::Player& player, uint64_t actorId )
|
||||
{
|
||||
if( player.isAetheryteRegistered( eventId & 0xFFFF ) )
|
||||
{
|
||||
player.playScene( eventId, 0, 1, []( Entity::Player& player, const Event::SceneResult& result )
|
||||
player.playScene( eventId, 2, 0, [this]( Entity::Player& player, const Event::SceneResult& result )
|
||||
{
|
||||
if( result.param1 == 256 )
|
||||
{
|
||||
player.teleport( result.param2, 2 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
player.eventActionStart( eventId, ACTION_ATTUNE,
|
||||
[]( Entity::Player& player, uint32_t eventId, uint64_t additional )
|
||||
{
|
||||
player.registerAetheryte( eventId & 0xFFFF );
|
||||
player.playScene( eventId, 3, 0, 0, 0 );
|
||||
},
|
||||
[]( Entity::Player& ply, uint32_t evntId, uint64_t additional )
|
||||
{
|
||||
|
||||
}, 0 );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void aetheryte( uint32_t eventId, Entity::Player& player, uint64_t actorId )
|
||||
{
|
||||
if( player.isAetheryteRegistered( eventId & 0xFFFF ) )
|
||||
{
|
||||
player.playScene( eventId, 0, 1, [this]( Entity::Player& player, const Event::SceneResult& result )
|
||||
{
|
||||
if( result.param1 == 256 ) // set homepoint
|
||||
{
|
||||
|
@ -55,7 +87,7 @@ public:
|
|||
else
|
||||
{
|
||||
player.eventActionStart( eventId, ACTION_ATTUNE,
|
||||
[]( Entity::Player& player, uint32_t eventId, uint64_t additional )
|
||||
[this]( Entity::Player& player, uint32_t eventId, uint64_t additional )
|
||||
{
|
||||
player.registerAetheryte( eventId & 0xFFFF );
|
||||
|
||||
|
@ -73,4 +105,20 @@ public:
|
|||
{}, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override
|
||||
{
|
||||
auto pExdData = getFramework()->get< Core::Data::ExdDataGenerated >();
|
||||
if( !pExdData )
|
||||
return;
|
||||
|
||||
auto aetherInfo = pExdData->get< Core::Data::Aetheryte >( eventId & 0xFFFF );
|
||||
if( !aetherInfo )
|
||||
return;
|
||||
|
||||
if( aetherInfo->isAetheryte )
|
||||
aetheryte( eventId, player, actorId );
|
||||
else
|
||||
aethernet( eventId, player, actorId );
|
||||
}
|
||||
};
|
|
@ -8,7 +8,7 @@ class GilShop :
|
|||
{
|
||||
public:
|
||||
GilShop() :
|
||||
Sapphire::ScriptAPI::EventScript( 0x00040001 )
|
||||
Sapphire::ScriptAPI::EventScript( 0x00040000 )
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#include <Actor/Player.h>
|
||||
|
||||
#include <Exd/ExdDataGenerated.h>
|
||||
#include <Zone/TerritoryMgr.h>
|
||||
#include <Zone/HousingMgr.h>
|
||||
#include <Zone/ZonePosition.h>
|
||||
#include <Framework.h>
|
||||
|
||||
using namespace Core;
|
||||
|
@ -16,35 +19,75 @@ public:
|
|||
|
||||
void inner( Entity::Player& player, const Event::SceneResult& result )
|
||||
{
|
||||
if( result.param1 != 256 )
|
||||
if( result.param1 == 256 ) // exit
|
||||
{
|
||||
std::function< void( Entity::Player&, const Event::SceneResult& ) > fn = std::bind( &WarpTaxi::inner, this, std::placeholders::_1, std::placeholders::_2 );
|
||||
player.eventFinish( 1310721, 0 );
|
||||
player.eventFinish( getId(), 1 );
|
||||
}
|
||||
else if( result.param1 == 768 ) // teleport to ward
|
||||
{
|
||||
player.eventFinish( 1310721, 0 );
|
||||
player.eventFinish( getId(), 1 );
|
||||
|
||||
player.playScene( 1310721, 0, HIDE_HOTBAR, 0, 1, 341, fn );
|
||||
// todo: this is shit, move to housingmgr? handle moving players in and out of it there?
|
||||
auto exdData = getFramework()->get< Core::Data::ExdDataGenerated >();
|
||||
|
||||
auto warp = exdData->get< Core::Data::Warp >( getId() );
|
||||
if( !warp )
|
||||
return;
|
||||
|
||||
auto level = exdData->get< Core::Data::Level >( warp->level );
|
||||
if( !level )
|
||||
{
|
||||
|
||||
// fetch from cache
|
||||
auto teriMgr = getFramework()->get< Core::TerritoryMgr >();
|
||||
|
||||
auto pos = teriMgr->getTerritoryPosition( warp->level );
|
||||
if( !pos )
|
||||
return;
|
||||
|
||||
// lookup instance
|
||||
auto housingMgr = getFramework()->get< Core::HousingMgr >();
|
||||
auto landSetId = housingMgr->toLandSetId( 341, result.param3 );
|
||||
auto hZone = housingMgr->getHousingZoneByLandSetId( landSetId );
|
||||
|
||||
if( !hZone )
|
||||
return;
|
||||
|
||||
player.setPos( pos->getTargetPosition() );
|
||||
player.setRot( pos->getTargetRotation() );
|
||||
player.setInstance( hZone );
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player.playScene( 1310721, 0, HIDE_HOTBAR, 0, 1, 341, std::bind( &WarpTaxi::inner, this, std::placeholders::_1, std::placeholders::_2 ) );
|
||||
}
|
||||
}
|
||||
|
||||
void inner2( Entity::Player& player, uint64_t actorId )
|
||||
{
|
||||
player.eventStart( actorId, 1310721, Event::EventHandler::Nest, 0, 0 );
|
||||
std::function< void( Entity::Player&, const Event::SceneResult& ) > fn = std::bind( &WarpTaxi::inner, this, std::placeholders::_1, std::placeholders::_2 );
|
||||
player.playScene( 1310721, 0, HIDE_HOTBAR, 0, 1, 341, fn );
|
||||
player.playScene( getId(), 0, HIDE_HOTBAR, 0, 0, 32529, [this, actorId]( Entity::Player& player, const Event::SceneResult& result )
|
||||
{
|
||||
player.eventStart( actorId, 1310721, Event::EventHandler::Nest, 1, 0 );
|
||||
|
||||
player.playScene( 1310721, 0, HIDE_HOTBAR, 0, 1, 341, std::bind( &WarpTaxi::inner, this, std::placeholders::_1, std::placeholders::_2 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override
|
||||
{
|
||||
auto exddata = getFramework()->get< Core::Data::ExdDataGenerated >();
|
||||
if( !exddata )
|
||||
auto exdData = getFramework()->get< Core::Data::ExdDataGenerated >();
|
||||
if( !exdData )
|
||||
return;
|
||||
|
||||
auto warp = exddata->get< Core::Data::Warp >( eventId );
|
||||
auto warp = exdData->get< Core::Data::Warp >( eventId );
|
||||
if( !warp )
|
||||
return;
|
||||
|
||||
player.eventStart( actorId, warp->defaultTalk1, Event::EventHandler::Nest, 0, 0, std::bind( &WarpTaxi::inner2, this, std::placeholders::_1, std::placeholders::_2 ) );
|
||||
player.playScene( warp->defaultTalk1, 0, HIDE_HOTBAR, [warp, this]( Entity::Player& player, const Event::SceneResult& result )
|
||||
{
|
||||
|
||||
} );
|
||||
player.playScene( warp->defaultTalk1, 0, HIDE_HOTBAR, 0, 0, 7, nullptr );
|
||||
}
|
||||
};
|
|
@ -77,10 +77,10 @@ std::string Core::Event::getEventName( uint32_t eventId )
|
|||
case Event::EventHandler::EventHandlerType::Shop:
|
||||
{
|
||||
auto shopInfo = pExdData->get< Core::Data::GilShop >( eventId );
|
||||
std::string name = shopInfo->name;
|
||||
|
||||
if( shopInfo )
|
||||
return name;
|
||||
return shopInfo->name;
|
||||
|
||||
return unknown + "GilShop";
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -344,6 +344,16 @@ void Core::Network::GameConnection::clientTriggerHandler( const Packets::FFXIVAR
|
|||
pHousingMgr->sendLandSignOwned( player, ward, plot, territoryId );
|
||||
break;
|
||||
}
|
||||
case ClientTriggerType::RequestWardLandInfo:
|
||||
{
|
||||
auto pHousingMgr = g_fw.get< HousingMgr >();
|
||||
if( !pHousingMgr )
|
||||
break;
|
||||
|
||||
pHousingMgr->sendWardLandInfo( player, param12, param11 );
|
||||
|
||||
break;
|
||||
}
|
||||
case ClientTriggerType::RequestLandRelinquish:
|
||||
{
|
||||
auto plot = static_cast< uint8_t >( param12 & 0xFF );
|
||||
|
|
|
@ -503,6 +503,15 @@ void Core::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACKET_R
|
|||
}
|
||||
break;
|
||||
}
|
||||
case GmCommand::Kick:
|
||||
{
|
||||
// todo: this doesn't kill their session straight away, should do this properly but its good for when you get stuck for now
|
||||
targetPlayer->setMarkedForRemoval();
|
||||
|
||||
player.sendNotice( "Kicked " + targetPlayer->getName() );
|
||||
|
||||
break;
|
||||
}
|
||||
case GmCommand::TeriInfo:
|
||||
{
|
||||
auto pCurrentZone = player.getCurrentZone();
|
||||
|
|
|
@ -10,11 +10,6 @@
|
|||
#define EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
// todo: this is shit
|
||||
// constant script ids for certain events
|
||||
#define EVENTSCRIPT_AETHERYTE_ID 0x50000
|
||||
#define EVENTSCRIPT_AETHERNET_ID 0x50001
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class Framework;
|
||||
|
|
|
@ -168,32 +168,25 @@ void Core::Scripting::ScriptMgr::onPlayerFirstEnterWorld( Entity::Player& player
|
|||
|
||||
bool Core::Scripting::ScriptMgr::onTalk( Entity::Player& player, uint64_t actorId, uint32_t eventId )
|
||||
{
|
||||
|
||||
auto pExdData = g_fw.get< Data::ExdDataGenerated >();
|
||||
uint16_t eventType = eventId >> 16;
|
||||
uint32_t scriptId = eventId;
|
||||
|
||||
// todo: replace this shit with something more flexible allowing for handlers for an entire type without a bunch of if statements
|
||||
// aethernet/aetherytes need to be handled separately
|
||||
if( eventType == Event::EventHandler::EventHandlerType::Aetheryte )
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::EventScript >( eventId & 0xFFFF0000 );
|
||||
if( script )
|
||||
{
|
||||
auto aetherInfo = pExdData->get< Core::Data::Aetheryte >( eventId & 0xFFFF );
|
||||
scriptId = EVENTSCRIPT_AETHERYTE_ID;
|
||||
if( !aetherInfo->isAetheryte )
|
||||
scriptId = EVENTSCRIPT_AETHERNET_ID;
|
||||
script->onTalk( eventId, player, actorId );
|
||||
return true;
|
||||
}
|
||||
else if( eventType == Event::EventHandler::EventHandlerType::Shop )
|
||||
else
|
||||
{
|
||||
scriptId = 0x00040001;
|
||||
}
|
||||
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::EventScript >( scriptId );
|
||||
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::EventScript >( eventId );
|
||||
if( !script )
|
||||
return false;
|
||||
|
||||
script->onTalk( eventId, player, actorId );
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Core::Scripting::ScriptMgr::onEnterTerritory( Entity::Player& player, uint32_t eventId,
|
||||
uint16_t param1, uint16_t param2 )
|
||||
{
|
||||
|
|
|
@ -203,3 +203,50 @@ bool Core::HousingMgr::relinquishLand( Entity::Player& player, uint8_t plot )
|
|||
return true;
|
||||
}
|
||||
|
||||
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() );
|
||||
wardInfoPacket->data().wardId = wardId;
|
||||
wardInfoPacket->data().territoryTypeId = territoryTypeId;
|
||||
|
||||
for( int i = 0; i < 60; i++ )
|
||||
{
|
||||
auto land = hZone->getLand( i );
|
||||
assert( land );
|
||||
|
||||
auto& entry = wardInfoPacket->data().houseInfoEntry[ i ];
|
||||
|
||||
entry.housePrice = land->getCurrentPrice();
|
||||
|
||||
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();
|
||||
std::string playerName = g_fw.get< Core::ServerZone >()->getPlayerNameFromDb( owner );
|
||||
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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace Core
|
|||
void sendLandSignFree( Entity::Player& player, uint8_t wardId, uint8_t plotId, uint16_t territoryTypeId );
|
||||
LandPurchaseResult purchaseLand( Entity::Player& player, uint8_t plot, uint8_t state );
|
||||
|
||||
void sendWardLandInfo( Entity::Player& player, uint8_t wardId, uint16_t territoryTypeId );
|
||||
|
||||
bool relinquishLand( Entity::Player& player, uint8_t plot );
|
||||
|
||||
private:
|
||||
|
|
|
@ -127,6 +127,7 @@ int main()
|
|||
result += generateEnum( "Tribe", 0, "uint8_t" );
|
||||
result += generateEnum( "Town", 0, "uint8_t" );
|
||||
result += generateEnum( "Weather", 1, "uint8_t" );
|
||||
result += generateEnum( "HosuingAppeal", 0, "uint8_t" );
|
||||
result += "}\n";
|
||||
result += "}\n#endif\n";
|
||||
g_log.info( result );
|
||||
|
|
Loading…
Add table
Reference in a new issue