1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-25 19:17:45 +00:00

Merge remote-tracking branch 'remotes/origin/develop' into develop_5.25

This commit is contained in:
collett 2020-05-12 18:00:50 +09:00
commit 0fc6c9b2dd
12 changed files with 182 additions and 86 deletions

View file

@ -159,44 +159,46 @@ namespace Sapphire::Common
ModelRing2 = 9
};
enum EquipSlotCategory : uint8_t
enum class EquipSlotCategory : uint8_t
{
Unequippable = 0,
// main slots
CharaMainHand = 1,
CharaOffHand = 2,
CharaHead = 3,
CharaBody = 4,
CharaHands = 5,
CharaWaist = 6,
CharaLegs = 7,
CharaFeet = 8,
CharaEars = 9,
CharaNeck = 10,
CharaWrist = 11,
CharaRing = 12,
CharaSoulCrystal = 17,
CharaMainHand = 0,
CharaOffHand = 1,
CharaHead = 2,
CharaBody = 3,
CharaHands = 4,
CharaWaist = 5,
CharaLegs = 6,
CharaFeet = 7,
CharaEars = 8,
CharaNeck = 9,
CharaWrist = 10,
CharaRing = 11,
CharaSoulCrystal = 12,
// specials
/* following slots not seem to exist any more.
when multi-slot gear is moved into equipment slot, normal slot listed above is used.
client will move any incompatible gears into armory but no InventoryModifiyHandler is sent.
server need to move those silently in order to sync with client.
*/
/*! Cannot equip gear to offhand slot */
MainTwoHandedWeapon = 13,
//MainTwoHandedWeapon = 13,
/*! Can be equipped in either main or offhand slot */
MainOrOffHand = 14, // unused
//MainOrOffHand = 14, // unused
/*! Cannot equip gear to head */
BodyDisallowHead = 15,
//BodyDisallowHead = 15,
/*! Cannot equip gear to hands, legs and feet slots */
BodyDisallowHandsLegsFeet = 16,
//BodyDisallowHandsLegsFeet = 16,
/*! Cannot equip gear to feet slot */
LegsDisallowFeet = 18,
//LegsDisallowFeet = 18,
/*! Cannot equp gear to head, hands, legs, feet slots */
BodyDisallowAll = 19,
//BodyDisallowAll = 19,
/*! Cannot equip gear to hands slot */
BodyDisallowHands = 20,
//BodyDisallowHands = 20,
/*! Cannot equip gear to legs & feet slots */
BodyDisallowLegsFeet = 21,
//BodyDisallowLegsFeet = 21,
};
enum InventoryType : uint16_t
@ -226,8 +228,8 @@ namespace Sapphire::Common
ArmoryWaist = 3204,
ArmoryLegs = 3205,
ArmoryFeet = 3206,
ArmoryNeck = 3207,
ArmoryEar = 3208,
ArmoryEar = 3207,
ArmoryNeck = 3208,
ArmoryWrist = 3209,
ArmoryRing = 3300,
@ -1291,6 +1293,16 @@ namespace Sapphire::Common
} whm;
};
enum class LootMessageType : uint8_t
{
GetItem1 = 1, // p1: actorId, p4: itemId (HQ: itemId + 1,000,000 lol), p5: amount
GetItem2 = 3, // p1: actorId, p2: itemId, p3: amount, seems like same thing as GetItem1 but different param position.
FailedToGetLootNoFreeInventorySlot = 5, // p1: actorId
LootRolled = 7, // p1: actorId, p2: itemId, p3: amount
GetGil = 9, // p1: gil
EmptyCoffer = 11, // seems like no param
};
using PlayerStateFlagList = std::vector< PlayerStateFlag >;
}

View file

@ -377,8 +377,8 @@ namespace Sapphire::Network::Packets
PerformNoteHandler = 0x029B, // updated 4.3
MapInteractionHandler = 0x0285, // updated 5.25
ShopMessage = 0x00C1, // updated 5.25
LootMessage = 0x00B1, // updated 5.25
};
////////////////////////////////////////////////////////////////////////////////

View file

@ -2018,6 +2018,19 @@ namespace Sapphire::Network::Packets::Server
uint32_t unknown7;
};
struct FFXIVIpcLootMessage : FFXIVIpcBasePacket< LootMessage >
{
Common::LootMessageType msgType;
uint8_t padding[3];
uint32_t param1;
uint32_t param2;
uint32_t param3;
uint32_t param4;
uint32_t param5;
uint32_t param6;
uint32_t param7;
};
}
#endif /*_CORE_NETWORK_PACKETS_SERVER_IPC_H*/

View file

@ -2284,6 +2284,13 @@ void Sapphire::Entity::Player::addBuyBackItemForShop( uint32_t shopId, const Sap
void Sapphire::Entity::Player::clearBuyBackMap()
{
for( auto& list : m_shopBuyBackMap )
{
for( auto& entry : list.second )
{
deleteItemDb( entry.item );
}
}
m_shopBuyBackMap.clear();
}

View file

@ -360,7 +360,7 @@ namespace Sapphire::Entity
uint32_t getModelForSlot( Common::GearModelSlot slot );
/*! add amount to the currency of type */
void addCurrency( Common::CurrencyType type, uint32_t amount );
void addCurrency( Common::CurrencyType type, uint32_t amount, bool sendLootMessage = false );
/*! remove amount from the currency of type */
void removeCurrency( Common::CurrencyType type, uint32_t amount );
@ -928,8 +928,8 @@ namespace Sapphire::Entity
InvSlotPair getFreeBagSlot();
ItemPtr addItem( uint32_t catalogId, uint32_t quantity = 1, bool isHq = false, bool slient = false, bool canMerge = true );
ItemPtr addItem( ItemPtr itemToAdd, bool slient = false, bool canMerge = true );
ItemPtr addItem( uint32_t catalogId, uint32_t quantity = 1, bool isHq = false, bool silent = false, bool canMerge = true, bool sendLootMessage = false );
ItemPtr addItem( ItemPtr itemToAdd, bool silent = false, bool canMerge = true, bool sendLootMessage = false );
void moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot );
@ -964,7 +964,7 @@ namespace Sapphire::Entity
uint32_t getCrystal( Common::CrystalType type );
/*! add amount to the crystal of type */
void addCrystal( Common::CrystalType type, uint32_t amount );
void addCrystal( Common::CrystalType type, uint32_t amount, bool sendLootMessage = false );
/*! remove amount from the crystals of type */
void removeCrystal( Common::CrystalType type, uint32_t amount );
@ -980,7 +980,7 @@ namespace Sapphire::Entity
void setActiveLand( uint8_t land, uint8_t ward );
Common::ActiveLand getActiveLand() const;
Sapphire::ItemPtr dropInventoryItem( Common::InventoryType type, uint16_t slotId, bool slient = false );
Sapphire::ItemPtr dropInventoryItem( Common::InventoryType type, uint16_t slotId, bool silent = false );
// Job UI
//////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -298,7 +298,7 @@ void Sapphire::Entity::Player::unequipSoulCrystal( ItemPtr pItem )
}
// TODO: these next functions are so similar that they could likely be simplified
void Sapphire::Entity::Player::addCurrency( CurrencyType type, uint32_t amount )
void Sapphire::Entity::Player::addCurrency( CurrencyType type, uint32_t amount, bool sendLootMessage )
{
auto slot = static_cast< uint8_t >( static_cast< uint8_t >( type ) - 1 );
auto currItem = m_storageMap[ Currency ]->getItem( slot );
@ -321,6 +321,22 @@ void Sapphire::Entity::Player::addCurrency( CurrencyType type, uint32_t amount )
Common::InventoryType::Currency,
*currItem );
queuePacket( invUpdate );
if( sendLootMessage )
{
switch( type )
{
case CurrencyType::Gil:
{
auto lootMsg = makeZonePacket< FFXIVIpcLootMessage >( getId() );
lootMsg->data().msgType = Common::LootMessageType::GetGil;
lootMsg->data().param1 = amount;
queuePacket( lootMsg );
break;
}
}
}
}
void Sapphire::Entity::Player::removeCurrency( Common::CurrencyType type, uint32_t amount )
@ -346,7 +362,7 @@ void Sapphire::Entity::Player::removeCurrency( Common::CurrencyType type, uint32
}
void Sapphire::Entity::Player::addCrystal( Common::CrystalType type, uint32_t amount )
void Sapphire::Entity::Player::addCrystal( Common::CrystalType type, uint32_t amount, bool sendLootMessage )
{
auto currItem = m_storageMap[ Crystal ]->getItem( static_cast< uint8_t >( type ) - 1 );
@ -371,7 +387,21 @@ void Sapphire::Entity::Player::addCrystal( Common::CrystalType type, uint32_t am
Common::InventoryType::Crystal,
*currItem );
queuePacket( invUpdate );
queuePacket( makeActorControlSelf( getId(), ItemObtainIcon, static_cast< uint8_t >( type ) + 1, amount ) );
if( sendLootMessage )
{
auto lootMsg = makeZonePacket< FFXIVIpcLootMessage >( getId() );
lootMsg->data().msgType = Common::LootMessageType::GetItem2;
lootMsg->data().param1 = getId();
lootMsg->data().param2 = currItem->getId();
lootMsg->data().param3 = amount;
queuePacket( lootMsg );
}
auto soundEffectPacket = makeZonePacket< FFXIVIpcInventoryActionAck >( getId() );
soundEffectPacket->data().sequence = 0xFFFFFFFF;
soundEffectPacket->data().type = 6;
queuePacket( soundEffectPacket );
}
void Sapphire::Entity::Player::removeCrystal( Common::CrystalType type, uint32_t amount )
@ -524,7 +554,7 @@ bool Sapphire::Entity::Player::isObtainable( uint32_t catalogId, uint8_t quantit
return true;
}
Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool silent, bool canMerge )
Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool silent, bool canMerge, bool sendLootMessage )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto itemInfo = exdData.get< Sapphire::Data::Item >( itemToAdd->getId() );
@ -548,7 +578,7 @@ Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool sil
// add the related armoury bag to the applicable bags and try and fill a free slot there before falling back to regular inventory
if( itemInfo->isEquippable && getEquipDisplayFlags() & StoreNewItemsInArmouryChest )
{
auto bag = World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( itemInfo->equipSlotCategory );
auto bag = World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( static_cast< Common::EquipSlotCategory >( itemInfo->equipSlotCategory ) );
bags.insert( bags.begin(), bag );
}
@ -591,19 +621,31 @@ Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool sil
item->setStackSize( newStackSize );
writeItem( item );
if( !silent )
{
auto slotUpdate = std::make_shared< UpdateInventorySlotPacket >( getId(), slot, bag, *item );
queuePacket( slotUpdate );
}
// return existing stack if we have no overflow - items fit into a preexisting stack
if( itemToAdd->getStackSize() == 0 )
{
queuePacket( makeActorControlSelf( getId(), ItemObtainIcon, itemToAdd->getId(), originalQuantity ) );
if( sendLootMessage )
{
auto lootMsg = makeZonePacket< FFXIVIpcLootMessage >( getId() );
lootMsg->data().msgType = Common::LootMessageType::GetItem2;
lootMsg->data().param1 = getId();
lootMsg->data().param2 = itemToAdd->isHq() ? itemToAdd->getId() + 1000000 : itemToAdd->getId();
lootMsg->data().param3 = originalQuantity;
queuePacket( lootMsg );
}
if( !silent )
{
auto soundEffectPacket = makeZonePacket< FFXIVIpcInventoryActionAck >( getId() );
soundEffectPacket->data().sequence = 0xFFFFFFFF;
soundEffectPacket->data().type = 6;
queuePacket( soundEffectPacket );
}
return item;
}
@ -630,23 +672,31 @@ Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool sil
auto invUpdate = std::make_shared< UpdateInventorySlotPacket >( getId(), freeBagSlot.second, freeBagSlot.first, *itemToAdd );
queuePacket( invUpdate );
queuePacket( makeActorControlSelf( getId(), ItemObtainIcon, itemToAdd->getId(), originalQuantity ) );
auto soundEffectPacket = makeZonePacket< FFXIVIpcInventoryActionAck >( getId() );
soundEffectPacket->data().sequence = 0xFFFFFFFF;
soundEffectPacket->data().type = 6;
queuePacket( soundEffectPacket );
}
if( sendLootMessage )
{
auto lootMsg = makeZonePacket< FFXIVIpcLootMessage >( getId() );
lootMsg->data().msgType = Common::LootMessageType::GetItem2;
lootMsg->data().param1 = getId();
lootMsg->data().param2 = itemToAdd->isHq() ? itemToAdd->getId() + 1000000 : itemToAdd->getId();
lootMsg->data().param3 = originalQuantity;
queuePacket( lootMsg );
}
return itemToAdd;
}
Sapphire::ItemPtr Sapphire::Entity::Player::addItem( uint32_t catalogId, uint32_t quantity, bool isHq, bool silent, bool canMerge )
Sapphire::ItemPtr Sapphire::Entity::Player::addItem( uint32_t catalogId, uint32_t quantity, bool isHq, bool silent, bool canMerge, bool sendLootMessage )
{
auto item = createItem( catalogId, quantity );
item->setHq( isHq );
return addItem( item, silent, canMerge );
return addItem( item, silent, canMerge, sendLootMessage );
}
void
@ -802,7 +852,7 @@ void Sapphire::Entity::Player::swapItem( uint16_t fromInventoryId, uint8_t fromS
&& !World::Manager::ItemMgr::isArmory( fromInventoryId ) )
{
updateContainer( fromInventoryId, fromSlotId, nullptr );
fromInventoryId = World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( toSlot );
fromInventoryId = World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( static_cast< Common::EquipSlotCategory >( toSlot ) );
fromSlotId = static_cast < uint8_t >( m_storageMap[ fromInventoryId ]->getFreeSlot() );
}
@ -929,7 +979,7 @@ uint32_t Sapphire::Entity::Player::getNextInventorySequence()
return m_inventorySequence++;
}
Sapphire::ItemPtr Sapphire::Entity::Player::dropInventoryItem( Sapphire::Common::InventoryType type, uint16_t slotId, bool slient )
Sapphire::ItemPtr Sapphire::Entity::Player::dropInventoryItem( Sapphire::Common::InventoryType type, uint16_t slotId, bool silent )
{
auto& container = m_storageMap[ type ];
@ -941,7 +991,7 @@ Sapphire::ItemPtr Sapphire::Entity::Player::dropInventoryItem( Sapphire::Common:
container->removeItem( slotId, false );
updateContainer( type, slotId, nullptr );
if( !slient )
if( !silent )
{
auto invUpdate = std::make_shared< UpdateInventorySlotPacket >( getId(), slotId, static_cast< uint16_t >( type ) );
queuePacket( invUpdate );

View file

@ -1065,7 +1065,7 @@ bool Sapphire::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t opti
{
for( uint32_t i = 0; i < rewardItemCount; i++ )
{
addItem( questInfo->itemReward0.at( i ), questInfo->itemCountReward0.at( i ) );
addItem( questInfo->itemReward0.at( i ), questInfo->itemCountReward0.at( i ), false, false, true, true );
}
}
@ -1076,14 +1076,14 @@ bool Sapphire::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t opti
auto itemId = questInfo->itemReward1.at( i );
if( itemId == optionalChoice )
{
addItem( itemId, questInfo->itemCountReward1.at( i ) );
addItem( itemId, questInfo->itemCountReward1.at( i ), false, false, true, true );
break;
}
}
}
if( gilReward > 0 )
addCurrency( CurrencyType::Gil, gilReward );
addCurrency( CurrencyType::Gil, gilReward, true );
return true;
}

View file

@ -57,6 +57,7 @@ namespace Sapphire::Event
Craft = 0x000A,
CustomTalk = 0x000B,
CompanyLeveOfficer = 0x000C,
Array = 0x000D,
CraftLeve = 0x000E,
GimmickAccessor = 0x000F,
GimmickBill = 0x0010,
@ -72,8 +73,11 @@ namespace Sapphire::Event
Story = 0x001A,
SpecialShop = 0x001B,
BahamutGuide = 0x001C,
InstanceContentGuide = 0x001D,
HousingAethernet = 0x001E,
FcTalk = 0x001F,
Adventure = 0x0021,
DailyQuestSupply = 0x0022,
ICDirector = 0x8003,
QuestBattleDirector = 0x8006,
};

View file

@ -27,56 +27,56 @@ bool Sapphire::World::Manager::ItemMgr::isArmory( uint16_t containerId )
}
uint16_t Sapphire::World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( uint8_t slotId )
uint16_t Sapphire::World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( Common::EquipSlotCategory slot )
{
switch( slotId )
switch( slot )
{
case Common::CharaHead:
case Common::EquipSlotCategory::CharaHead:
return Common::ArmoryHead;
case Common::CharaBody:
case Common::BodyDisallowHead:
case Common::BodyDisallowHandsLegsFeet:
case Common::BodyDisallowAll:
case Common::BodyDisallowHands:
case Common::BodyDisallowLegsFeet:
case Common::EquipSlotCategory::CharaBody:
//case Common::EquipSlotCategory::BodyDisallowHead:
//case Common::EquipSlotCategory::BodyDisallowHandsLegsFeet:
//case Common::EquipSlotCategory::BodyDisallowAll:
//case Common::EquipSlotCategory::BodyDisallowHands:
//case Common::EquipSlotCategory::BodyDisallowLegsFeet:
return Common::ArmoryBody;
case Common::CharaEars:
case Common::EquipSlotCategory::CharaEars:
return Common::ArmoryEar;
case Common::CharaFeet:
case Common::EquipSlotCategory::CharaFeet:
return Common::ArmoryFeet;
case Common::CharaHands:
case Common::EquipSlotCategory::CharaHands:
return Common::ArmoryHand;
case Common::CharaLegs:
case Common::LegsDisallowFeet:
case Common::EquipSlotCategory::CharaLegs:
//case Common::EquipSlotCategory::LegsDisallowFeet:
return Common::ArmoryLegs;
case Common::CharaMainHand:
case Common::MainTwoHandedWeapon:
case Common::MainOrOffHand:
case Common::EquipSlotCategory::CharaMainHand:
//case Common::EquipSlotCategory::MainTwoHandedWeapon:
//case Common::EquipSlotCategory::MainOrOffHand:
return Common::ArmoryMain;
case Common::CharaOffHand:
case Common::EquipSlotCategory::CharaOffHand:
return Common::ArmoryOff;
case Common::CharaRing:
case Common::EquipSlotCategory::CharaRing:
return Common::ArmoryRing;
case Common::CharaWaist:
case Common::EquipSlotCategory::CharaWaist:
return Common::ArmoryWaist;
case Common::CharaWrist:
case Common::EquipSlotCategory::CharaWrist:
return Common::ArmoryWrist;
case Common::CharaNeck:
case Common::EquipSlotCategory::CharaNeck:
return Common::ArmoryNeck;
case Common::CharaSoulCrystal:
case Common::EquipSlotCategory::CharaSoulCrystal:
return Common::ArmorySoulCrystal;
default:

View file

@ -20,7 +20,7 @@ namespace Sapphire::World::Manager
static bool isOneHandedWeapon( Common::ItemUICategory weaponCategory );
static bool isArmory( uint16_t containerId );
static bool isEquipment( uint16_t containerId );
static uint16_t getCharaEquipSlotCategoryToArmoryId( uint8_t slotId );
static uint16_t getCharaEquipSlotCategoryToArmoryId( Common::EquipSlotCategory slot );
static Common::ContainerType getContainerType( uint32_t containerId );
};

View file

@ -345,14 +345,23 @@ void Sapphire::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACK
player.sendUrgent( "Syntaxerror." );
return;
}
if( param1 <= 0x12 ) // crystal
{
targetPlayer->addCrystal( static_cast< Common::CrystalType >( param1 ), quantity, true );
}
else // item
{
// decode using the epic SE style HQ item id
bool isHq = param1 > 1000000;
if( !targetPlayer->addItem( param1, quantity ) )
player.sendUrgent( "Item #{0} could not be added to inventory.", param1 );
if( !targetPlayer->addItem( isHq ? param1 - 1000000 : param1, quantity, isHq, false, true, true ) )
player.sendUrgent( "Item #{0} could not be added to inventory.", isHq ? param1 - 1000000 : param1 );
break;
}
}
case GmCommand::Gil:
{
targetPlayer->addCurrency( CurrencyType::Gil, param1 );
targetPlayer->addCurrency( CurrencyType::Gil, param1, true );
player.sendNotice( "Added {0} Gil for {1}", param1, targetPlayer->getName() );
break;
}

View file

@ -72,6 +72,7 @@ void Sapphire::World::Session::close()
// remove the session from the player
if( m_pPlayer )
{
m_pPlayer->clearBuyBackMap();
// do one last update to db
m_pPlayer->updateSql();
// reset the zone, so the zone handler knows to remove the actor