[Quest API] Add client->SummonBaggedItems(bag_item_id, bag_items_ref) to Perl/Lua.

Alternative apis using arrays of hash items for EQEmu/Server#1575

Perl usage:
```pl
    # create as an array, pass as reference
    my @bag_items = (
      { item_id => 1001, charges => 1 },
      { item_id => 1002, charges => 1 },
      { item_id => 10037, charges => 10 },
    );
    $client->SummonBaggedItems(17403, \@bag_items);

    # create directly as an array reference
    my $bag_items = [
      { item_id => 1001, charges => 1 },
      { item_id => 1002, charges => 1 },
      { item_id => 10037, charges => 10 },
    ];
    $client->SummonBaggedItems(17403, $bag_items); ```

Lua Usage:
```lua
    local bag_items = {
      { item_id = 1001, charges = 1 },
      { item_id = 1002, charges = 1 },
      { item_id = 10037, charges = 10 }
    }
    e.other:SummonBaggedItems(17403, bag_items);
This commit is contained in:
hg 2021-10-02 12:00:00 -04:00 committed by GitHub
parent 9a413cf553
commit 5560b198ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 143 additions and 1 deletions

View File

@ -10567,3 +10567,65 @@ std::vector<Client *> Client::GetPartyMembers()
return clients_to_update;
}
void Client::SummonBaggedItems(uint32 bag_item_id, const std::vector<ServerLootItem_Struct>& bag_items)
{
if (bag_items.empty())
{
return;
}
// todo: maybe some common functions for SE_SummonItem and SE_SummonItemIntoBag
const EQ::ItemData* bag_item = database.GetItem(bag_item_id);
if (!bag_item)
{
Message(Chat::Red, fmt::format("Unable to summon item [{}]. Item not found.", bag_item_id).c_str());
return;
}
if (CheckLoreConflict(bag_item))
{
DuplicateLoreMessage(bag_item_id);
return;
}
int bag_item_charges = 1; // just summoning a single bag
EQ::ItemInstance* summoned_bag = database.CreateItem(bag_item_id, bag_item_charges);
if (!summoned_bag || !summoned_bag->IsClassBag())
{
Message(Chat::Red, fmt::format("Failed to summon bag item [{}]", bag_item_id).c_str());
safe_delete(summoned_bag);
return;
}
for (const auto& item : bag_items)
{
uint8 open_slot = summoned_bag->FirstOpenSlot();
if (open_slot == 0xff)
{
Message(Chat::Red, "Attempting to summon item in to bag, but there is no room in the summoned bag!");
break;
}
const EQ::ItemData* current_item = database.GetItem(item.item_id);
if (CheckLoreConflict(current_item))
{
DuplicateLoreMessage(item.item_id);
}
else
{
EQ::ItemInstance* summoned_bag_item = database.CreateItem(item.item_id, item.charges);
if (summoned_bag_item)
{
summoned_bag->PutItem(open_slot, *summoned_bag_item);
safe_delete(summoned_bag_item);
}
}
}
PushItemOnCursor(*summoned_bag);
SendItemPacket(EQ::invslot::slotCursor, summoned_bag, ItemPacketLimbo);
safe_delete(summoned_bag);
}

View File

@ -913,6 +913,7 @@ public:
void PutLootInInventory(int16 slot_id, const EQ::ItemInstance &inst, ServerLootItem_Struct** bag_item_data = 0);
bool AutoPutLootInInventory(EQ::ItemInstance& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0);
bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, bool attuned = false, uint16 to_slot = EQ::invslot::slotCursor, uint32 ornament_icon = 0, uint32 ornament_idfile = 0, uint32 ornament_hero_model = 0);
void SummonBaggedItems(uint32 bag_item_id, const std::vector<ServerLootItem_Struct>& bag_items);
void SetStats(uint8 type,int16 set_val);
void IncStats(uint8 type,int16 increase_val);
void DropItem(int16 slot_id, bool recurse = true);

View File

@ -2229,6 +2229,31 @@ void Lua_Client::UntrainDiscBySpellID(uint16 spell_id, bool update_client) {
self->UntrainDiscBySpellID(spell_id, update_client);
}
void Lua_Client::SummonBaggedItems(uint32 bag_item_id, luabind::adl::object bag_items_table) {
Lua_Safe_Call_Void();
if (luabind::type(bag_items_table) != LUA_TTABLE) {
return;
}
std::vector<ServerLootItem_Struct> bagged_items;
luabind::raw_iterator end; // raw_iterator uses lua_rawget
for (luabind::raw_iterator it(bag_items_table); it != end; ++it)
{
// verify array element is a table for item details
if (luabind::type(*it) == LUA_TTABLE)
{
// no need to try/catch, quest lua parser already catches exceptions
ServerLootItem_Struct item{};
item.item_id = luabind::object_cast<uint32>((*it)["item_id"]);
item.charges = luabind::object_cast<int16>((*it)["charges"]);
bagged_items.emplace_back(item);
}
}
self->SummonBaggedItems(bag_item_id, bagged_items);
}
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>())
@ -2604,7 +2629,8 @@ luabind::scope lua_register_client() {
.def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem)
.def("SetGMStatus", (void(Lua_Client::*)(int32))&Lua_Client::SetGMStatus)
.def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16))&Lua_Client::UntrainDiscBySpellID)
.def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID);
.def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID)
.def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems);
}
luabind::scope lua_register_inventory_where() {

View File

@ -215,6 +215,7 @@ public:
bool attuned);
void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5,
bool attuned, int to_slot);
void SummonBaggedItems(uint32 bag_item_id, luabind::adl::object bag_items_table);
void SetStats(int type, int value);
void IncStats(int type, int value);
void DropItem(int slot_id);

View File

@ -5712,6 +5712,57 @@ XS(XS_Client_UntrainDiscBySpellID) {
XSRETURN_EMPTY;
}
XS(XS_Client_SummonBaggedItems); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_SummonBaggedItems) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: Client::SummonBaggedItems(THIS, uint32 bag_item_id, ARRAYREF bag_items_array)"); // @categories Inventory and Items, Script Utility
}
Client* THIS;
VALIDATE_THIS_IS_CLIENT;
uint32 bag_item_id = (uint32) SvUV(ST(1));
// verify we're receiving a reference to an array type
SV* bag_items_avref = ST(2);
if (!bag_items_avref || !SvROK(bag_items_avref) || SvTYPE(SvRV(bag_items_avref)) != SVt_PVAV)
{
Perl_croak(aTHX_ "Client::SummonBaggedItems second argument is not a reference to an array");
}
// dereference into the array
AV* bag_items_av = (AV*)SvRV(bag_items_avref);
std::vector<ServerLootItem_Struct> bagged_items;
auto count = av_len(bag_items_av) + 1;
for (int i = 0; i < count; ++i)
{
SV** element = av_fetch(bag_items_av, i, 0);
// verify array element is a hash reference containing item details
if (element && SvROK(*element) && SvTYPE(SvRV(*element)) == SVt_PVHV)
{
HV* hash = (HV*)SvRV(*element); // dereference
SV** item_id_ptr = hv_fetchs(hash, "item_id", false);
SV** item_charges_ptr = hv_fetchs(hash, "charges", false);
if (item_id_ptr && item_charges_ptr)
{
ServerLootItem_Struct item{};
item.item_id = static_cast<uint32>(SvUV(*item_id_ptr));
item.charges = static_cast<int16>(SvIV(*item_charges_ptr));
bagged_items.emplace_back(item);
}
}
}
THIS->SummonBaggedItems(bag_item_id, bagged_items);
XSRETURN_EMPTY;
}
#ifdef __cplusplus
extern "C"
#endif
@ -6001,6 +6052,7 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "Sit"), XS_Client_Sit, file, "$");
newXSproto(strcpy(buf, "SlotConvert2"), XS_Client_SlotConvert2, file, "$$");
newXSproto(strcpy(buf, "Stand"), XS_Client_Stand, file, "$");
newXSproto(strcpy(buf, "SummonBaggedItems"), XS_Client_SummonBaggedItems, file, "$$$");
newXSproto(strcpy(buf, "SummonItem"), XS_Client_SummonItem, file, "$$;$$$$$$$$");
newXSproto(strcpy(buf, "TakeMoneyFromPP"), XS_Client_TakeMoneyFromPP, file, "$$;$");
newXSproto(strcpy(buf, "TGB"), XS_Client_TGB, file, "$");