mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +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;
|
||||
}
|
||||
|
||||
namespace BookType {
|
||||
constexpr uint8 Scroll = 0;
|
||||
constexpr uint8 Book = 1;
|
||||
constexpr uint8 ItemInfo = 2;
|
||||
}
|
||||
|
||||
#endif /*COMMON_EMU_CONSTANTS_H*/
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
#include "repositories/character_corpses_repository.h"
|
||||
#include "repositories/skill_caps_repository.h"
|
||||
#include "repositories/inventory_repository.h"
|
||||
#include "repositories/books_repository.h"
|
||||
|
||||
namespace ItemField
|
||||
{
|
||||
@ -1391,30 +1392,28 @@ const EQ::ItemData* SharedDatabase::IterateItems(uint32* id) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string SharedDatabase::GetBook(const char *txtfile, int16 *language)
|
||||
Book_Struct SharedDatabase::GetBook(const std::string& text_file)
|
||||
{
|
||||
char txtfile2[20];
|
||||
std::string txtout;
|
||||
strcpy(txtfile2, txtfile);
|
||||
const auto& l = BooksRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`name` = '{}'",
|
||||
Strings::Escape(text_file)
|
||||
)
|
||||
);
|
||||
|
||||
const std::string query = StringFormat("SELECT txtfile, language FROM books WHERE name = '%s'", txtfile2);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
txtout.assign(" ",1);
|
||||
return txtout;
|
||||
Book_Struct b;
|
||||
|
||||
if (l.empty()) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
LogError("No book to send, ({})", txtfile);
|
||||
txtout.assign(" ",1);
|
||||
return txtout;
|
||||
}
|
||||
const auto& e = l.front();
|
||||
|
||||
auto& row = results.begin();
|
||||
txtout.assign(row[0],strlen(row[0]));
|
||||
*language = static_cast<int16>(Strings::ToInt(row[1]));
|
||||
b.language = e.language;
|
||||
b.text = e.txtfile;
|
||||
|
||||
return txtout;
|
||||
return b;
|
||||
}
|
||||
|
||||
// Create appropriate EQ::ItemInstance class
|
||||
|
||||
@ -41,8 +41,7 @@ struct NPCFactionList;
|
||||
struct FactionAssociations;
|
||||
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace EQ {
|
||||
|
||||
struct ItemData;
|
||||
class ItemInstance;
|
||||
@ -50,6 +49,12 @@ namespace EQ
|
||||
class MemoryMappedFile;
|
||||
}
|
||||
|
||||
struct Book_Struct
|
||||
{
|
||||
uint8 language;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
/*
|
||||
This object is inherited by world and zone's DB object,
|
||||
and is mainly here to facilitate shared memory, and other
|
||||
@ -114,7 +119,7 @@ public:
|
||||
int admin
|
||||
);
|
||||
|
||||
std::string GetBook(const char *txtfile, int16 *language);
|
||||
Book_Struct GetBook(const std::string& text_file);
|
||||
|
||||
/**
|
||||
* items
|
||||
|
||||
108
zone/client.cpp
108
zone/client.cpp
@ -2299,52 +2299,67 @@ void Client::SetGM(bool toggle) {
|
||||
UpdateWho();
|
||||
}
|
||||
|
||||
void Client::ReadBook(BookRequest_Struct *book) {
|
||||
int16 book_language=0;
|
||||
char *txtfile = book->txtfile;
|
||||
void Client::ReadBook(BookRequest_Struct* book)
|
||||
{
|
||||
const std::string& text_file = book->txtfile;
|
||||
|
||||
if(txtfile[0] == '0' && txtfile[1] == '\0') {
|
||||
//invalid book... coming up on non-book items.
|
||||
if (text_file.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string booktxt2 = content_db.GetBook(txtfile, &book_language);
|
||||
int length = booktxt2.length();
|
||||
auto b = content_db.GetBook(text_file);
|
||||
|
||||
if (booktxt2[0] != '\0') {
|
||||
#if EQDEBUG >= 6
|
||||
LogInfo("Client::ReadBook() textfile:[{}] Text:[{}]", txtfile, booktxt2.c_str());
|
||||
#endif
|
||||
auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct));
|
||||
if (!b.text.empty()) {
|
||||
auto outapp = new EQApplicationPacket(OP_ReadBook, b.text.size() + sizeof(BookText_Struct));
|
||||
auto inst = const_cast<EQ::ItemInstance*>(m_inv[book->invslot]);
|
||||
|
||||
BookText_Struct *out = (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;
|
||||
auto t = (BookText_Struct*) outapp->pBuffer;
|
||||
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END)
|
||||
{
|
||||
const EQ::ItemInstance* inst = m_inv[book->invslot];
|
||||
if (inst && inst->GetItem())
|
||||
{
|
||||
auto recipe = TradeskillRecipeRepository::GetWhere(content_db,
|
||||
fmt::format("learned_by_item_id = {} LIMIT 1", inst->GetItem()->ID));
|
||||
out->type = inst->GetItem()->Book;
|
||||
out->can_scribe = !recipe.empty();
|
||||
t->window = book->window;
|
||||
t->type = book->type;
|
||||
t->invslot = book->invslot;
|
||||
t->target_id = book->target_id;
|
||||
t->can_cast = 0; // todo: implement
|
||||
t->can_scribe = false;
|
||||
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END) {
|
||||
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 (m_pp.languages[book_language] < Language::MaxValue) {
|
||||
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language]));
|
||||
if (EQ::ValueWithin(b.language, Language::CommonTongue, Language::Unknown27)) {
|
||||
if (m_pp.languages[b.language] < Language::MaxValue) {
|
||||
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);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
@ -10916,23 +10931,24 @@ void Client::SetIPExemption(int exemption_amount)
|
||||
|
||||
void Client::ReadBookByName(std::string book_name, uint8 book_type)
|
||||
{
|
||||
int16 book_language = 0;
|
||||
std::string book_text = content_db.GetBook(book_name.c_str(), &book_language);
|
||||
int length = book_text.length();
|
||||
auto b = content_db.GetBook(book_name);
|
||||
|
||||
if (book_text[0] != '\0') {
|
||||
LogDebug("Client::ReadBookByName() Book Name: [{}] Text: [{}]", book_name, book_text.c_str());
|
||||
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;
|
||||
if (!b.text.empty()) {
|
||||
LogDebug("Book Name: [{}] Text: [{}]", book_name, b.text);
|
||||
|
||||
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)) {
|
||||
if (m_pp.languages[book_language] < Language::MaxValue) {
|
||||
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language]));
|
||||
auto o = (BookText_Struct *) outapp->pBuffer;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
// do not ask what all these mean because I have no idea!
|
||||
// named from the client's CEverQuest::GetInnateDesc, they're missing some
|
||||
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));
|
||||
return;
|
||||
}
|
||||
BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer;
|
||||
ReadBook(book);
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF)
|
||||
{
|
||||
EQApplicationPacket EndOfBook(OP_FinishWindow, 0);
|
||||
QueuePacket(&EndOfBook);
|
||||
|
||||
auto b = (BookRequest_Struct*) app->pBuffer;
|
||||
ReadBook(b);
|
||||
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
|
||||
EQApplicationPacket end_of_book(OP_FinishWindow, 0);
|
||||
QueuePacket(&end_of_book);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app)
|
||||
|
||||
@ -203,6 +203,7 @@ const char* QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_ENTITY_VARIABLE_UPDATE",
|
||||
"EVENT_AA_LOSS",
|
||||
"EVENT_SPELL_BLOCKED",
|
||||
"EVENT_READ_ITEM",
|
||||
|
||||
// Add new events before these or Lua crashes
|
||||
"EVENT_SPELL_EFFECT_BOT",
|
||||
@ -2488,6 +2489,28 @@ void PerlembParser::ExportEventVariables(
|
||||
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: {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -144,6 +144,7 @@ typedef enum {
|
||||
EVENT_ENTITY_VARIABLE_UPDATE,
|
||||
EVENT_AA_LOSS,
|
||||
EVENT_SPELL_BLOCKED,
|
||||
EVENT_READ_ITEM,
|
||||
|
||||
// Add new events before these or Lua crashes
|
||||
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_set", static_cast<int>(EVENT_ENTITY_VARIABLE_SET)),
|
||||
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_update",
|
||||
"event_aa_loss",
|
||||
"event_spell_blocked"
|
||||
"event_spell_blocked",
|
||||
"event_read_item"
|
||||
};
|
||||
|
||||
extern Zone *zone;
|
||||
@ -348,6 +349,7 @@ LuaParser::LuaParser() {
|
||||
PlayerArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_player_entity_variable;
|
||||
PlayerArgumentDispatch[EVENT_AA_LOSS] = handle_player_aa_loss;
|
||||
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_CAST] = handle_item_click;
|
||||
|
||||
@ -1745,6 +1745,49 @@ void handle_player_spell_blocked(
|
||||
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
|
||||
void handle_item_click(
|
||||
QuestInterface *parse,
|
||||
|
||||
@ -855,6 +855,15 @@ void handle_player_spell_blocked(
|
||||
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
|
||||
void handle_item_click(
|
||||
QuestInterface *parse,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user