diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 5ccdc8672..2053e559d 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1283,21 +1283,24 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) } } - char buf[88]; char q_corpse_name[64]; strcpy(q_corpse_name, corpse_name); - snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), - EntityList::RemoveNumbers(q_corpse_name)); - buf[87] = '\0'; + std::string buf = fmt::format("{} {} {} {}", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(q_corpse_name), GetID()); std::vector args; args.push_back(inst); args.push_back(this); - if (parse->EventPlayer(EVENT_LOOT, client, buf, 0, &args) != 0) { - lootitem->auto_loot = -1; - client->MessageString(Chat::Red, LOOT_NOT_ALLOWED, inst->GetItem()->Name); - client->QueuePacket(app); - delete inst; - return; + bool prevent_loot = false; + if (RuleB(Zone, UseZoneController)) { + auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID); + if (controller){ + if (parse->EventNPC(EVENT_LOOT_ZONE, controller, client, buf.c_str(), 0, &args) != 0) { + prevent_loot = true; + } + } + } + + if (parse->EventPlayer(EVENT_LOOT, client, buf.c_str(), 0, &args) != 0) { + prevent_loot = true; } if (!IsPlayerCorpse()) @@ -1306,17 +1309,24 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) auto dz = zone->GetDynamicZone(); if (dz && !dz->CanClientLootCorpse(client, GetNPCTypeID(), GetID())) { + prevent_loot = true; // note on live this message is only sent once on the first loot attempt of an open corpse client->MessageString(Chat::Loot, LOOT_NOT_ALLOWED, inst->GetItem()->Name); - lootitem->auto_loot = -1; // generates client eqstr 1370 "You may not loot that item from this corpse." - client->QueuePacket(app); - delete inst; - return; } } - // do we want this to have a fail option too? - parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0); + // do we want this to have a fail option too? Sure? + if (parse->EventItem(EVENT_LOOT, client, inst, this, buf.c_str(), 0) != 0) { + prevent_loot = true; + } + + if (prevent_loot) { + lootitem->auto_loot = -1; + client->QueuePacket(app); + safe_delete(inst); + return; + } + // safe to ACK now client->QueuePacket(app); diff --git a/zone/embparser.cpp b/zone/embparser.cpp index f4ea70356..43bcebe5c 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -124,7 +124,8 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_TEST_BUFF", "EVENT_COMBINE", "EVENT_CONSIDER", - "EVENT_CONSIDER_CORPSE" + "EVENT_CONSIDER_CORPSE", + "EVENT_LOOT_ZONE" }; PerlembParser::PerlembParser() : perl(nullptr) @@ -1411,12 +1412,14 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "version", zone->GetInstanceVersion()); break; } - + + case EVENT_LOOT_ZONE: case EVENT_LOOT: { Seperator sep(data); ExportVar(package_name.c_str(), "looted_id", sep.arg[0]); ExportVar(package_name.c_str(), "looted_charges", sep.arg[1]); ExportVar(package_name.c_str(), "corpse", sep.arg[2]); + ExportVar(package_name.c_str(), "corpse_id", sep.arg[3]); break; } diff --git a/zone/event_codes.h b/zone/event_codes.h index 4a915ce9c..0fd9d94b3 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -93,6 +93,7 @@ typedef enum { EVENT_COMBINE, EVENT_CONSIDER, EVENT_CONSIDER_CORPSE, + EVENT_LOOT_ZONE, _LargestEventID } QuestEventID; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index a9d724138..b6652302b 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -4040,7 +4040,8 @@ luabind::scope lua_register_events() { luabind::value("warp", static_cast(EVENT_WARP)), luabind::value("test_buff", static_cast(EVENT_TEST_BUFF)), luabind::value("consider", static_cast(EVENT_CONSIDER)), - luabind::value("consider_corpse", static_cast(EVENT_CONSIDER_CORPSE)) + luabind::value("consider_corpse", static_cast(EVENT_CONSIDER_CORPSE)), + luabind::value("loot_zone", static_cast(EVENT_LOOT_ZONE)) ]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 641f24040..093f11c62 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -135,7 +135,8 @@ const char *LuaEvents[_LargestEventID] = { "event_test_buff", "event_combine", "event_consider", - "event_consider_corpse" + "event_consider_corpse", + "event_loot_zone" }; extern Zone *zone; @@ -185,6 +186,7 @@ LuaParser::LuaParser() { NPCArgumentDispatch[EVENT_FEIGN_DEATH] = handle_npc_single_client; NPCArgumentDispatch[EVENT_ENTER_AREA] = handle_npc_area; NPCArgumentDispatch[EVENT_LEAVE_AREA] = handle_npc_area; + NPCArgumentDispatch[EVENT_LOOT_ZONE] = handle_npc_loot_zone; PlayerArgumentDispatch[EVENT_SAY] = handle_player_say; PlayerArgumentDispatch[EVENT_ENVIRONMENTAL_DAMAGE] = handle_player_environmental_damage; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 0734c9e19..e7711b36c 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -237,6 +237,24 @@ void handle_npc_null(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, s std::vector *extra_pointers) { } +void handle_npc_loot_zone(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, + std::vector *extra_pointers) { + Lua_Client l_client(reinterpret_cast(init)); + luabind::adl::object l_client_o = luabind::adl::object(L, l_client); + l_client_o.push(L); + lua_setfield(L, -2, "other"); + + Lua_ItemInst l_item(EQ::any_cast(extra_pointers->at(0))); + luabind::adl::object l_item_o = luabind::adl::object(L, l_item); + l_item_o.push(L); + lua_setfield(L, -2, "item"); + + Lua_Corpse l_corpse(EQ::any_cast(extra_pointers->at(1))); + luabind::adl::object l_corpse_o = luabind::adl::object(L, l_corpse); + l_corpse_o.push(L); + lua_setfield(L, -2, "corpse"); +} + //Player void handle_player_say(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector *extra_pointers) { diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 850116075..1d9d4b22c 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -41,6 +41,8 @@ void handle_npc_area(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, s std::vector *extra_pointers); void handle_npc_null(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector *extra_pointers); +void handle_npc_loot_zone(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, + std::vector *extra_pointers); //Player void handle_player_say(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,