mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-14 15:41:30 +00:00
[Quest API] Add EVENT_READ_ITEM to Perl/Lua (#4497)
* [Quest API] Add EVENT_READ_ITEM to Perl/Lua * Add item_id export * Add item export. * Update client.cpp
This commit is contained in:
parent
8568cf7d49
commit
e3198edb86
@ -756,4 +756,10 @@ namespace PCNPCOnlyFlagType {
|
|||||||
constexpr int NPC = 2;
|
constexpr int NPC = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace BookType {
|
||||||
|
constexpr uint8 Scroll = 0;
|
||||||
|
constexpr uint8 Book = 1;
|
||||||
|
constexpr uint8 ItemInfo = 2;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /*COMMON_EMU_CONSTANTS_H*/
|
#endif /*COMMON_EMU_CONSTANTS_H*/
|
||||||
|
|||||||
@ -47,6 +47,7 @@
|
|||||||
#include "repositories/character_corpses_repository.h"
|
#include "repositories/character_corpses_repository.h"
|
||||||
#include "repositories/skill_caps_repository.h"
|
#include "repositories/skill_caps_repository.h"
|
||||||
#include "repositories/inventory_repository.h"
|
#include "repositories/inventory_repository.h"
|
||||||
|
#include "repositories/books_repository.h"
|
||||||
|
|
||||||
namespace ItemField
|
namespace ItemField
|
||||||
{
|
{
|
||||||
@ -1391,30 +1392,28 @@ const EQ::ItemData* SharedDatabase::IterateItems(uint32* id) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SharedDatabase::GetBook(const char *txtfile, int16 *language)
|
Book_Struct SharedDatabase::GetBook(const std::string& text_file)
|
||||||
{
|
{
|
||||||
char txtfile2[20];
|
const auto& l = BooksRepository::GetWhere(
|
||||||
std::string txtout;
|
*this,
|
||||||
strcpy(txtfile2, txtfile);
|
fmt::format(
|
||||||
|
"`name` = '{}'",
|
||||||
|
Strings::Escape(text_file)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const std::string query = StringFormat("SELECT txtfile, language FROM books WHERE name = '%s'", txtfile2);
|
Book_Struct b;
|
||||||
auto results = QueryDatabase(query);
|
|
||||||
if (!results.Success()) {
|
if (l.empty()) {
|
||||||
txtout.assign(" ",1);
|
return b;
|
||||||
return txtout;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (results.RowCount() == 0) {
|
const auto& e = l.front();
|
||||||
LogError("No book to send, ({})", txtfile);
|
|
||||||
txtout.assign(" ",1);
|
|
||||||
return txtout;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& row = results.begin();
|
b.language = e.language;
|
||||||
txtout.assign(row[0],strlen(row[0]));
|
b.text = e.txtfile;
|
||||||
*language = static_cast<int16>(Strings::ToInt(row[1]));
|
|
||||||
|
|
||||||
return txtout;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create appropriate EQ::ItemInstance class
|
// Create appropriate EQ::ItemInstance class
|
||||||
|
|||||||
@ -41,8 +41,7 @@ struct NPCFactionList;
|
|||||||
struct FactionAssociations;
|
struct FactionAssociations;
|
||||||
|
|
||||||
|
|
||||||
namespace EQ
|
namespace EQ {
|
||||||
{
|
|
||||||
|
|
||||||
struct ItemData;
|
struct ItemData;
|
||||||
class ItemInstance;
|
class ItemInstance;
|
||||||
@ -50,6 +49,12 @@ namespace EQ
|
|||||||
class MemoryMappedFile;
|
class MemoryMappedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Book_Struct
|
||||||
|
{
|
||||||
|
uint8 language;
|
||||||
|
std::string text;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This object is inherited by world and zone's DB object,
|
This object is inherited by world and zone's DB object,
|
||||||
and is mainly here to facilitate shared memory, and other
|
and is mainly here to facilitate shared memory, and other
|
||||||
@ -114,7 +119,7 @@ public:
|
|||||||
int admin
|
int admin
|
||||||
);
|
);
|
||||||
|
|
||||||
std::string GetBook(const char *txtfile, int16 *language);
|
Book_Struct GetBook(const std::string& text_file);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* items
|
* items
|
||||||
|
|||||||
108
zone/client.cpp
108
zone/client.cpp
@ -2299,52 +2299,67 @@ void Client::SetGM(bool toggle) {
|
|||||||
UpdateWho();
|
UpdateWho();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::ReadBook(BookRequest_Struct *book) {
|
void Client::ReadBook(BookRequest_Struct* book)
|
||||||
int16 book_language=0;
|
{
|
||||||
char *txtfile = book->txtfile;
|
const std::string& text_file = book->txtfile;
|
||||||
|
|
||||||
if(txtfile[0] == '0' && txtfile[1] == '\0') {
|
if (text_file.empty()) {
|
||||||
//invalid book... coming up on non-book items.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string booktxt2 = content_db.GetBook(txtfile, &book_language);
|
auto b = content_db.GetBook(text_file);
|
||||||
int length = booktxt2.length();
|
|
||||||
|
|
||||||
if (booktxt2[0] != '\0') {
|
if (!b.text.empty()) {
|
||||||
#if EQDEBUG >= 6
|
auto outapp = new EQApplicationPacket(OP_ReadBook, b.text.size() + sizeof(BookText_Struct));
|
||||||
LogInfo("Client::ReadBook() textfile:[{}] Text:[{}]", txtfile, booktxt2.c_str());
|
auto inst = const_cast<EQ::ItemInstance*>(m_inv[book->invslot]);
|
||||||
#endif
|
|
||||||
auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct));
|
|
||||||
|
|
||||||
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer;
|
auto t = (BookText_Struct*) outapp->pBuffer;
|
||||||
out->window = book->window;
|
|
||||||
out->type = book->type;
|
|
||||||
out->invslot = book->invslot;
|
|
||||||
out->target_id = book->target_id;
|
|
||||||
out->can_cast = 0; // todo: implement
|
|
||||||
out->can_scribe = false;
|
|
||||||
|
|
||||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END)
|
t->window = book->window;
|
||||||
{
|
t->type = book->type;
|
||||||
const EQ::ItemInstance* inst = m_inv[book->invslot];
|
t->invslot = book->invslot;
|
||||||
if (inst && inst->GetItem())
|
t->target_id = book->target_id;
|
||||||
{
|
t->can_cast = 0; // todo: implement
|
||||||
auto recipe = TradeskillRecipeRepository::GetWhere(content_db,
|
t->can_scribe = false;
|
||||||
fmt::format("learned_by_item_id = {} LIMIT 1", inst->GetItem()->ID));
|
|
||||||
out->type = inst->GetItem()->Book;
|
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END) {
|
||||||
out->can_scribe = !recipe.empty();
|
if (inst && inst->GetItem()) {
|
||||||
|
auto recipe = TradeskillRecipeRepository::GetWhere(
|
||||||
|
content_db,
|
||||||
|
fmt::format(
|
||||||
|
"learned_by_item_id = {} LIMIT 1",
|
||||||
|
inst->GetItem()->ID
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
t->type = inst->GetItem()->Book;
|
||||||
|
t->can_scribe = !recipe.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(out->booktext, booktxt2.c_str(), length);
|
memcpy(t->booktext, b.text.c_str(), b.text.size());
|
||||||
|
|
||||||
if (EQ::ValueWithin(book_language, Language::CommonTongue, Language::Unknown27)) {
|
if (EQ::ValueWithin(b.language, Language::CommonTongue, Language::Unknown27)) {
|
||||||
if (m_pp.languages[book_language] < Language::MaxValue) {
|
if (m_pp.languages[b.language] < Language::MaxValue) {
|
||||||
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language]));
|
GarbleMessage(t->booktext, (Language::MaxValue - m_pp.languages[b.language]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send only books and scrolls to this event
|
||||||
|
if (parse->PlayerHasQuestSub(EVENT_READ_ITEM) && t->type != BookType::ItemInfo) {
|
||||||
|
std::vector<std::any> args = {
|
||||||
|
b.text,
|
||||||
|
t->can_cast,
|
||||||
|
t->can_scribe,
|
||||||
|
t->invslot,
|
||||||
|
t->target_id,
|
||||||
|
t->type,
|
||||||
|
inst
|
||||||
|
};
|
||||||
|
|
||||||
|
parse->EventPlayer(EVENT_READ_ITEM, this, book->txtfile, inst ? inst->GetID() : 0, &args);
|
||||||
|
}
|
||||||
|
|
||||||
QueuePacket(outapp);
|
QueuePacket(outapp);
|
||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
}
|
}
|
||||||
@ -10916,23 +10931,24 @@ void Client::SetIPExemption(int exemption_amount)
|
|||||||
|
|
||||||
void Client::ReadBookByName(std::string book_name, uint8 book_type)
|
void Client::ReadBookByName(std::string book_name, uint8 book_type)
|
||||||
{
|
{
|
||||||
int16 book_language = 0;
|
auto b = content_db.GetBook(book_name);
|
||||||
std::string book_text = content_db.GetBook(book_name.c_str(), &book_language);
|
|
||||||
int length = book_text.length();
|
|
||||||
|
|
||||||
if (book_text[0] != '\0') {
|
if (!b.text.empty()) {
|
||||||
LogDebug("Client::ReadBookByName() Book Name: [{}] Text: [{}]", book_name, book_text.c_str());
|
LogDebug("Book Name: [{}] Text: [{}]", book_name, b.text);
|
||||||
auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct));
|
|
||||||
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer;
|
|
||||||
out->window = 0xFF;
|
|
||||||
out->type = book_type;
|
|
||||||
out->invslot = 0;
|
|
||||||
|
|
||||||
memcpy(out->booktext, book_text.c_str(), length);
|
auto outapp = new EQApplicationPacket(OP_ReadBook, b.text.size() + sizeof(BookText_Struct));
|
||||||
|
|
||||||
if (EQ::ValueWithin(book_language, Language::CommonTongue, Language::Unknown27)) {
|
auto o = (BookText_Struct *) outapp->pBuffer;
|
||||||
if (m_pp.languages[book_language] < Language::MaxValue) {
|
|
||||||
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language]));
|
o->window = std::numeric_limits<uint8>::max();
|
||||||
|
o->type = book_type;
|
||||||
|
o->invslot = 0;
|
||||||
|
|
||||||
|
memcpy(o->booktext, b.text.c_str(), b.text.size());
|
||||||
|
|
||||||
|
if (EQ::ValueWithin(b.language, Language::CommonTongue, Language::Unknown27)) {
|
||||||
|
if (m_pp.languages[b.language] < Language::MaxValue) {
|
||||||
|
GarbleMessage(o->booktext, (Language::MaxValue - m_pp.languages[b.language]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -196,6 +196,7 @@ struct RespawnOption
|
|||||||
float heading;
|
float heading;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// do not ask what all these mean because I have no idea!
|
// do not ask what all these mean because I have no idea!
|
||||||
// named from the client's CEverQuest::GetInnateDesc, they're missing some
|
// named from the client's CEverQuest::GetInnateDesc, they're missing some
|
||||||
enum eInnateSkill {
|
enum eInnateSkill {
|
||||||
|
|||||||
@ -13046,14 +13046,14 @@ void Client::Handle_OP_ReadBook(const EQApplicationPacket *app)
|
|||||||
LogError("Wrong size: OP_ReadBook, size=[{}], expected [{}]", app->size, sizeof(BookRequest_Struct));
|
LogError("Wrong size: OP_ReadBook, size=[{}], expected [{}]", app->size, sizeof(BookRequest_Struct));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer;
|
|
||||||
ReadBook(book);
|
auto b = (BookRequest_Struct*) app->pBuffer;
|
||||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF)
|
ReadBook(b);
|
||||||
{
|
|
||||||
EQApplicationPacket EndOfBook(OP_FinishWindow, 0);
|
if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
|
||||||
QueuePacket(&EndOfBook);
|
EQApplicationPacket end_of_book(OP_FinishWindow, 0);
|
||||||
|
QueuePacket(&end_of_book);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app)
|
void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app)
|
||||||
|
|||||||
@ -203,6 +203,7 @@ const char* QuestEventSubroutines[_LargestEventID] = {
|
|||||||
"EVENT_ENTITY_VARIABLE_UPDATE",
|
"EVENT_ENTITY_VARIABLE_UPDATE",
|
||||||
"EVENT_AA_LOSS",
|
"EVENT_AA_LOSS",
|
||||||
"EVENT_SPELL_BLOCKED",
|
"EVENT_SPELL_BLOCKED",
|
||||||
|
"EVENT_READ_ITEM",
|
||||||
|
|
||||||
// Add new events before these or Lua crashes
|
// Add new events before these or Lua crashes
|
||||||
"EVENT_SPELL_EFFECT_BOT",
|
"EVENT_SPELL_EFFECT_BOT",
|
||||||
@ -2488,6 +2489,28 @@ void PerlembParser::ExportEventVariables(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case EVENT_READ_ITEM: {;
|
||||||
|
ExportVar(package_name.c_str(), "item_id", extra_data);
|
||||||
|
ExportVar(package_name.c_str(), "text_file", data);
|
||||||
|
|
||||||
|
if (extra_pointers && extra_pointers->size() == 7) {
|
||||||
|
ExportVar(package_name.c_str(), "book_text", std::any_cast<std::string>(extra_pointers->at(0)).c_str());
|
||||||
|
ExportVar(package_name.c_str(), "can_cast", std::any_cast<int8>(extra_pointers->at(1)));
|
||||||
|
ExportVar(package_name.c_str(), "can_scribe", std::any_cast<int8>(extra_pointers->at(2)));
|
||||||
|
ExportVar(package_name.c_str(), "slot_id", std::any_cast<int16>(extra_pointers->at(3)));
|
||||||
|
ExportVar(package_name.c_str(), "target_id", std::any_cast<int>(extra_pointers->at(4)));
|
||||||
|
ExportVar(package_name.c_str(), "type", std::any_cast<uint8>(extra_pointers->at(5)));
|
||||||
|
ExportVar(
|
||||||
|
package_name.c_str(),
|
||||||
|
"item",
|
||||||
|
"QuestItem",
|
||||||
|
std::any_cast<EQ::ItemInstance*>(extra_pointers->at(6))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,6 +144,7 @@ typedef enum {
|
|||||||
EVENT_ENTITY_VARIABLE_UPDATE,
|
EVENT_ENTITY_VARIABLE_UPDATE,
|
||||||
EVENT_AA_LOSS,
|
EVENT_AA_LOSS,
|
||||||
EVENT_SPELL_BLOCKED,
|
EVENT_SPELL_BLOCKED,
|
||||||
|
EVENT_READ_ITEM,
|
||||||
|
|
||||||
// Add new events before these or Lua crashes
|
// Add new events before these or Lua crashes
|
||||||
EVENT_SPELL_EFFECT_BOT,
|
EVENT_SPELL_EFFECT_BOT,
|
||||||
|
|||||||
@ -6907,7 +6907,8 @@ luabind::scope lua_register_events() {
|
|||||||
luabind::value("entity_variable_delete", static_cast<int>(EVENT_ENTITY_VARIABLE_DELETE)),
|
luabind::value("entity_variable_delete", static_cast<int>(EVENT_ENTITY_VARIABLE_DELETE)),
|
||||||
luabind::value("entity_variable_set", static_cast<int>(EVENT_ENTITY_VARIABLE_SET)),
|
luabind::value("entity_variable_set", static_cast<int>(EVENT_ENTITY_VARIABLE_SET)),
|
||||||
luabind::value("entity_variable_update", static_cast<int>(EVENT_ENTITY_VARIABLE_UPDATE)),
|
luabind::value("entity_variable_update", static_cast<int>(EVENT_ENTITY_VARIABLE_UPDATE)),
|
||||||
luabind::value("aa_loss", static_cast<int>(EVENT_AA_LOSS))
|
luabind::value("aa_loss", static_cast<int>(EVENT_AA_LOSS)),
|
||||||
|
luabind::value("read", static_cast<int>(EVENT_READ_ITEM))
|
||||||
)];
|
)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -184,7 +184,8 @@ const char *LuaEvents[_LargestEventID] = {
|
|||||||
"event_entity_variable_set",
|
"event_entity_variable_set",
|
||||||
"event_entity_variable_update",
|
"event_entity_variable_update",
|
||||||
"event_aa_loss",
|
"event_aa_loss",
|
||||||
"event_spell_blocked"
|
"event_spell_blocked",
|
||||||
|
"event_read_item"
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
@ -348,6 +349,7 @@ LuaParser::LuaParser() {
|
|||||||
PlayerArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_player_entity_variable;
|
PlayerArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_player_entity_variable;
|
||||||
PlayerArgumentDispatch[EVENT_AA_LOSS] = handle_player_aa_loss;
|
PlayerArgumentDispatch[EVENT_AA_LOSS] = handle_player_aa_loss;
|
||||||
PlayerArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_player_spell_blocked;
|
PlayerArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_player_spell_blocked;
|
||||||
|
PlayerArgumentDispatch[EVENT_READ_ITEM] = handle_player_read_item;
|
||||||
|
|
||||||
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
|
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
|
||||||
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
|
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
|
||||||
|
|||||||
@ -1745,6 +1745,49 @@ void handle_player_spell_blocked(
|
|||||||
lua_setfield(L, -2, "cast_spell");
|
lua_setfield(L, -2, "cast_spell");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handle_player_read_item(
|
||||||
|
QuestInterface *parse,
|
||||||
|
lua_State* L,
|
||||||
|
Client* client,
|
||||||
|
std::string data,
|
||||||
|
uint32 extra_data,
|
||||||
|
std::vector<std::any> *extra_pointers
|
||||||
|
)
|
||||||
|
{
|
||||||
|
lua_pushstring(L, data.c_str());
|
||||||
|
lua_setfield(L, -2, "text_file");
|
||||||
|
|
||||||
|
lua_pushinteger(L, extra_data);
|
||||||
|
lua_setfield(L, -2, "item_id");
|
||||||
|
|
||||||
|
if (extra_pointers) {
|
||||||
|
if (extra_pointers->size() == 7) {
|
||||||
|
lua_pushstring(L, std::any_cast<std::string>(extra_pointers->at(0)).c_str());
|
||||||
|
lua_setfield(L, -2, "book_text");
|
||||||
|
|
||||||
|
lua_pushboolean(L, std::any_cast<int8>(extra_pointers->at(1)));
|
||||||
|
lua_setfield(L, -2, "can_cast");
|
||||||
|
|
||||||
|
lua_pushboolean(L, std::any_cast<int8>(extra_pointers->at(2)));
|
||||||
|
lua_setfield(L, -2, "can_scribe");
|
||||||
|
|
||||||
|
lua_pushinteger(L, std::any_cast<int16>(extra_pointers->at(3)));
|
||||||
|
lua_setfield(L, -2, "slot_id");
|
||||||
|
|
||||||
|
lua_pushinteger(L, std::any_cast<int>(extra_pointers->at(4)));
|
||||||
|
lua_setfield(L, -2, "target_id");
|
||||||
|
|
||||||
|
lua_pushinteger(L, std::any_cast<uint8>(extra_pointers->at(5)));
|
||||||
|
lua_setfield(L, -2, "type");
|
||||||
|
|
||||||
|
Lua_ItemInst l_item(std::any_cast<EQ::ItemInstance*>(extra_pointers->at(6)));
|
||||||
|
luabind::adl::object l_item_o = luabind::adl::object(L, l_item);
|
||||||
|
l_item_o.push(L);
|
||||||
|
lua_setfield(L, -2, "item");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Item
|
// Item
|
||||||
void handle_item_click(
|
void handle_item_click(
|
||||||
QuestInterface *parse,
|
QuestInterface *parse,
|
||||||
|
|||||||
@ -855,6 +855,15 @@ void handle_player_spell_blocked(
|
|||||||
std::vector<std::any> *extra_pointers
|
std::vector<std::any> *extra_pointers
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void handle_player_read_item(
|
||||||
|
QuestInterface *parse,
|
||||||
|
lua_State* L,
|
||||||
|
Client* client,
|
||||||
|
std::string data,
|
||||||
|
uint32 extra_data,
|
||||||
|
std::vector<std::any> *extra_pointers
|
||||||
|
);
|
||||||
|
|
||||||
// Item
|
// Item
|
||||||
void handle_item_click(
|
void handle_item_click(
|
||||||
QuestInterface *parse,
|
QuestInterface *parse,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user