[Quest API] Add EVENT_CRYSTAL_GAIN and EVENT_CRYSTAL_LOSS to Perl/Lua (#3735)

* [Quest API] Add EVENT_CRYSTAL_GAIN and EVENT_CRYSTAL_LOSS

- Add `$client->AddEbonCrystals(amount)`.
- Add `$client->AddRadiantCrystals(amount)`.
- Add `$client->RemoveEbonCrystals(amount)`.
- Add `$client->RemoveRadiantCrystals(amount)`.
- Add `EVENT_CRYSTAL_GAIN`.
- Add `EVENT_CRYSTAL_LOSS`.
- Export `$ebon_amount`, `$radiant_amount`, and `$is_reclaim`.

- Add `client:AddEbonCrystals(amount)`.
- Add `client:AddRadiantCrystals(amount)`.
- Add `client:RemoveEbonCrystals(amount)`.
- Add `client:RemoveRadiantCrystals(amount)`.
- Add `event_crystal_gain`.
- Add `event_crystal_loss`.
- Export `e.ebon_amount`, `e.radiant_amount`, and `e.is_reclaim`.

- Allows operators to add or remove Ebon/Radiant Crystals directly.
- Allows operators to track gain/loss of Ebon/Radiant Crystals.

* Update perl_client.cpp

* Update lua_client.cpp
This commit is contained in:
Alex King 2023-12-16 23:47:13 -05:00 committed by GitHub
parent 7e651877c7
commit 0c9c2e25c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 252 additions and 51 deletions

View File

@ -124,6 +124,12 @@ struct LDoNTrapTemplate
uint8 locked;
};
enum CrystalReclaimTypes
{
Ebon = 5,
Radiant = 4,
};
///////////////////////////////////////////////////////////////////////////////

View File

@ -5727,26 +5727,89 @@ void Client::AddPVPPoints(uint32 Points)
SendPVPStats();
}
void Client::AddCrystals(uint32 radiant, uint32 ebon)
{
m_pp.currentRadCrystals += radiant;
m_pp.careerRadCrystals += radiant;
m_pp.currentEbonCrystals += ebon;
m_pp.careerEbonCrystals += ebon;
void Client::AddEbonCrystals(uint32 amount, bool is_reclaim) {
m_pp.currentEbonCrystals += amount;
m_pp.careerEbonCrystals += amount;
SaveCurrency();
SendCrystalCounts();
// newer clients handle message client side (older clients likely used eqstr 5967 and 5968, this matches live)
if (radiant > 0)
{
MessageString(Chat::Yellow, YOU_RECEIVE, fmt::format("{} Radiant Crystals", radiant).c_str());
}
MessageString(
Chat::Yellow,
YOU_RECEIVE,
fmt::format(
"{} {}",
amount,
database.CreateItemLink(RuleI(Zone, EbonCrystalItemID))
).c_str()
);
if (ebon > 0)
{
MessageString(Chat::Yellow, YOU_RECEIVE, fmt::format("{} Ebon Crystals", ebon).c_str());
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_GAIN)) {
const std::string &export_string = fmt::format(
"{} 0 {}",
amount,
is_reclaim ? 1 : 0
);
parse->EventPlayer(EVENT_CRYSTAL_GAIN, this, export_string, 0);
}
}
void Client::AddRadiantCrystals(uint32 amount, bool is_reclaim) {
m_pp.currentRadCrystals += amount;
m_pp.careerRadCrystals += amount;
SaveCurrency();
SendCrystalCounts();
MessageString(
Chat::Yellow,
YOU_RECEIVE,
fmt::format(
"{} {}",
amount,
database.CreateItemLink(RuleI(Zone, RadiantCrystalItemID))
).c_str()
);
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_GAIN)) {
const std::string &export_string = fmt::format(
"0 {} {}",
amount,
is_reclaim ? 1 : 0
);
parse->EventPlayer(EVENT_CRYSTAL_GAIN, this, export_string, 0);
}
}
void Client::RemoveEbonCrystals(uint32 amount, bool is_reclaim) {
m_pp.currentEbonCrystals -= amount;
SaveCurrency();
SendCrystalCounts();
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_LOSS)) {
const std::string &export_string = fmt::format(
"{} 0 {}",
amount,
is_reclaim ? 1 : 0
);
parse->EventPlayer(EVENT_CRYSTAL_LOSS, this, export_string, 0);
}
}
void Client::RemoveRadiantCrystals(uint32 amount, bool is_reclaim) {
m_pp.currentRadCrystals -= amount;
SaveCurrency();
SendCrystalCounts();
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_LOSS)) {
const std::string &export_string = fmt::format(
"0 {} {}",
amount,
is_reclaim ? 1 : 0
);
parse->EventPlayer(EVENT_CRYSTAL_LOSS, this, export_string, 0);
}
}

View File

@ -611,11 +611,14 @@ public:
void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; }
uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; }
void AddPVPPoints(uint32 Points);
void AddEbonCrystals(uint32 amount, bool is_reclaim = false);
void AddRadiantCrystals(uint32 amount, bool is_reclaim = false);
void RemoveEbonCrystals(uint32 amount, bool is_reclaim = false);
void RemoveRadiantCrystals(uint32 amount, bool is_reclaim = false);
uint32 GetRadiantCrystals() { return m_pp.currentRadCrystals; }
void SetRadiantCrystals(uint32 value);
uint32 GetEbonCrystals() { return m_pp.currentEbonCrystals; }
void SetEbonCrystals(uint32 value);
void AddCrystals(uint32 Radiant, uint32 Ebon);
void SendCrystalCounts();
uint64 GetExperienceForKill(Mob *against);

View File

@ -5475,59 +5475,50 @@ void Client::Handle_OP_CreateObject(const EQApplicationPacket *app)
void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app)
{
VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct);
CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer;
auto *cr = (CrystalReclaim_Struct *) app->pBuffer;
const uint32 requestQty = cr->amount;
const bool isRadiant = cr->type == 4;
const bool isEbon = cr->type == 5;
const uint32 quantity = cr->amount;
const bool is_radiant = cr->type == CrystalReclaimTypes::Radiant;
const bool is_ebon = cr->type == CrystalReclaimTypes::Ebon;
// Check: Valid type requested.
if (!isRadiant && !isEbon) {
if (!is_radiant && !is_ebon) {
return;
}
// Check: Valid quantity requested.
if (requestQty < 1) {
if (quantity < 1) {
return;
}
// Check: Valid client state to make request.
// In this situation the client is either desynced or attempting an exploit.
const uint32 currentQty = isRadiant ? GetRadiantCrystals() : GetEbonCrystals();
if (currentQty == 0) {
const uint32 current_quantity = is_radiant ? GetRadiantCrystals() : GetEbonCrystals();
if (!current_quantity) {
return;
}
// Prevent the client from creating more than they have.
const uint32 amount = EQ::ClampUpper(requestQty, currentQty);
const uint32 itemID = isRadiant ? RuleI(Zone, RadiantCrystalItemID) : RuleI(Zone, EbonCrystalItemID);
// Summon crystals for player.
const bool success = SummonItem(itemID, amount);
const uint32 amount = EQ::ClampUpper(quantity, current_quantity);
const uint32 item_id = is_radiant ? RuleI(Zone, RadiantCrystalItemID) : RuleI(Zone, EbonCrystalItemID);
const bool success = SummonItem(item_id, amount);
if (!success) {
return;
}
// Deduct crystals from client and update them.
if (isRadiant) {
m_pp.currentRadCrystals -= amount;
m_pp.careerRadCrystals -= amount;
if (is_ebon) {
RemoveEbonCrystals(amount, true);
} else if (is_radiant) {
RemoveRadiantCrystals(amount, true);
}
else if (isEbon) {
m_pp.currentEbonCrystals -= amount;
m_pp.careerEbonCrystals -= amount;
}
SaveCurrency();
SendCrystalCounts();
}
void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app)
{
uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor);
uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor);
const uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor);
const uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor);
if ((ebon + radiant) > 0) {
AddCrystals(radiant, ebon);
AddEbonCrystals(ebon, true);
AddRadiantCrystals(radiant, true);
}
}

View File

@ -191,6 +191,9 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_LDON_POINTS_LOSS",
"EVENT_ALT_CURRENCY_GAIN",
"EVENT_ALT_CURRENCY_LOSS",
"EVENT_CRYSTAL_GAIN",
"EVENT_CRYSTAL_LOSS",
// Add new events before these or Lua crashes
"EVENT_SPELL_EFFECT_BOT",
"EVENT_SPELL_EFFECT_BUFF_TIC_BOT"
@ -2281,6 +2284,15 @@ void PerlembParser::ExportEventVariables(
break;
}
case EVENT_CRYSTAL_GAIN:
case EVENT_CRYSTAL_LOSS: {
Seperator sep(data);
ExportVar(package_name.c_str(), "ebon_amount", sep.arg[0]);
ExportVar(package_name.c_str(), "radiant_amount", sep.arg[1]);
ExportVar(package_name.c_str(), "is_reclaim", sep.arg[2]);
break;
}
default: {
break;
}

View File

@ -133,6 +133,8 @@ typedef enum {
EVENT_LDON_POINTS_LOSS,
EVENT_ALT_CURRENCY_GAIN,
EVENT_ALT_CURRENCY_LOSS,
EVENT_CRYSTAL_GAIN,
EVENT_CRYSTAL_LOSS,
// Add new events before these or Lua crashes
EVENT_SPELL_EFFECT_BOT,

View File

@ -1211,7 +1211,24 @@ void Lua_Client::AddPVPPoints(uint32 points) {
void Lua_Client::AddCrystals(uint32 radiant, uint32 ebon) {
Lua_Safe_Call_Void();
self->AddCrystals(radiant, ebon);
if (ebon != 0) {
if (ebon > 0) {
self->AddEbonCrystals(ebon);
return;
}
self->RemoveEbonCrystals(ebon);
}
if (radiant != 0) {
if (radiant > 0) {
self->AddRadiantCrystals(radiant);
return;
}
self->RemoveRadiantCrystals(radiant);
}
}
void Lua_Client::SetEbonCrystals(uint32 value) {
@ -3176,6 +3193,30 @@ void Lua_Client::GrantAllAAPoints(uint8 unlock_level)
self->GrantAllAAPoints(unlock_level);
}
void Lua_Client::AddEbonCrystals(uint32 amount)
{
Lua_Safe_Call_Void();
self->AddEbonCrystals(amount);
}
void Lua_Client::AddRadiantCrystals(uint32 amount)
{
Lua_Safe_Call_Void();
self->AddRadiantCrystals(amount);
}
void Lua_Client::RemoveEbonCrystals(uint32 amount)
{
Lua_Safe_Call_Void();
self->RemoveEbonCrystals(amount);
}
void Lua_Client::RemoveRadiantCrystals(uint32 amount)
{
Lua_Safe_Call_Void();
self->RemoveRadiantCrystals(amount);
}
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>())
@ -3187,6 +3228,7 @@ luabind::scope lua_register_client() {
.def("AddEXP", (void(Lua_Client::*)(uint32))&Lua_Client::AddEXP)
.def("AddEXP", (void(Lua_Client::*)(uint32,int))&Lua_Client::AddEXP)
.def("AddEXP", (void(Lua_Client::*)(uint32,int,bool))&Lua_Client::AddEXP)
.def("AddEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::AddEbonCrystals)
.def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32))&Lua_Client::AddExpeditionLockout)
.def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32, std::string))&Lua_Client::AddExpeditionLockout)
.def("AddExpeditionLockoutDuration", (void(Lua_Client::*)(std::string, std::string, int))&Lua_Client::AddExpeditionLockoutDuration)
@ -3202,6 +3244,7 @@ luabind::scope lua_register_client() {
.def("AddPlatinum", (void(Lua_Client::*)(uint32))&Lua_Client::AddPlatinum)
.def("AddPlatinum", (void(Lua_Client::*)(uint32,bool))&Lua_Client::AddPlatinum)
.def("AddPVPPoints", (void(Lua_Client::*)(uint32))&Lua_Client::AddPVPPoints)
.def("AddRadiantCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::AddRadiantCrystals)
.def("AddSkill", (void(Lua_Client::*)(int,int))&Lua_Client::AddSkill)
.def("Admin", (int16(Lua_Client::*)(void))&Lua_Client::Admin)
.def("ApplySpell", (void(Lua_Client::*)(int))&Lua_Client::ApplySpell)
@ -3554,7 +3597,9 @@ luabind::scope lua_register_client() {
.def("ResetCastbarCooldownBySlot", (void(Lua_Client::*)(int))&Lua_Client::ResetCastbarCooldownBySlot)
.def("ResetCastbarCooldownBySpellID", (void(Lua_Client::*)(uint32))&Lua_Client::ResetCastbarCooldownBySpellID)
.def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer)
.def("RemoveEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveEbonCrystals)
.def("ResetItemCooldown", (void(Lua_Client::*)(uint32))&Lua_Client::ResetItemCooldown)
.def("RemoveRadiantCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveRadiantCrystals)
.def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade)
.def("RewardFaction", (void(Lua_Client::*)(int,int))&Lua_Client::RewardFaction)
.def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save)

View File

@ -482,6 +482,10 @@ public:
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
void GrantAllAAPoints();
void GrantAllAAPoints(uint8 unlock_level);
void AddEbonCrystals(uint32 amount);
void AddRadiantCrystals(uint32 amount);
void RemoveEbonCrystals(uint32 amount);
void RemoveRadiantCrystals(uint32 amount);
void ApplySpell(int spell_id);
void ApplySpell(int spell_id, int duration);

View File

@ -6614,7 +6614,9 @@ luabind::scope lua_register_events() {
luabind::value("ldon_points_gain", static_cast<int>(EVENT_LDON_POINTS_GAIN)),
luabind::value("ldon_points_loss", static_cast<int>(EVENT_LDON_POINTS_LOSS)),
luabind::value("alt_currency_gain", static_cast<int>(EVENT_ALT_CURRENCY_GAIN)),
luabind::value("alt_currency_loss", static_cast<int>(EVENT_ALT_CURRENCY_LOSS))
luabind::value("alt_currency_loss", static_cast<int>(EVENT_ALT_CURRENCY_LOSS)),
luabind::value("crystal_gain", static_cast<int>(EVENT_CRYSTAL_GAIN)),
luabind::value("crystal_loss", static_cast<int>(EVENT_CRYSTAL_LOSS))
)];
}

View File

@ -173,7 +173,9 @@ const char *LuaEvents[_LargestEventID] = {
"event_ldon_points_gain",
"event_ldon_points_loss",
"event_alt_currency_gain",
"event_alt_currency_loss"
"event_alt_currency_loss",
"event_crystal_gain",
"event_crystal_loss"
};
extern Zone *zone;
@ -310,6 +312,8 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[EVENT_LDON_POINTS_LOSS] = handle_player_ldon_points_gain_loss;
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_GAIN] = handle_player_alt_currency_gain_loss;
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_LOSS] = handle_player_alt_currency_gain_loss;
PlayerArgumentDispatch[EVENT_CRYSTAL_GAIN] = handle_player_crystal_gain_loss;
PlayerArgumentDispatch[EVENT_CRYSTAL_LOSS] = handle_player_crystal_gain_loss;
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;

View File

@ -1518,6 +1518,26 @@ void handle_player_ldon_points_gain_loss(
lua_setfield(L, -2, "points");
}
void handle_player_crystal_gain_loss(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Seperator sep(data.c_str());
lua_pushnumber(L, Strings::ToUnsignedInt(sep.arg[0]));
lua_setfield(L, -2, "ebon_amount");
lua_pushnumber(L, Strings::ToUnsignedInt(sep.arg[1]));
lua_setfield(L, -2, "radiant_amount");
lua_pushboolean(L, Strings::ToBool(sep.arg[2]));
lua_setfield(L, -2, "is_reclaim");
}
void handle_player_alt_currency_gain_loss(
QuestInterface *parse,
lua_State* L,

View File

@ -761,6 +761,15 @@ void handle_player_alt_currency_gain_loss(
std::vector<std::any> *extra_pointers
);
void handle_player_crystal_gain_loss(
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,

View File

@ -1159,7 +1159,23 @@ void Perl_Client_AddPVPPoints(Client* self, uint32 points) // @categories Curren
void Perl_Client_AddCrystals(Client* self, uint32 radiant_count, uint32 ebon_count) // @categories Currency and Points
{
self->AddCrystals(radiant_count, ebon_count);
if (ebon_count != 0) {
if (ebon_count > 0) {
self->AddEbonCrystals(ebon_count);
return;
}
self->RemoveEbonCrystals(ebon_count);
}
if (radiant_count != 0) {
if (radiant_count > 0) {
self->AddRadiantCrystals(radiant_count);
return;
}
self->RemoveRadiantCrystals(radiant_count);
}
}
void Perl_Client_SetEbonCrystals(Client* self, uint32 value)
@ -2992,6 +3008,26 @@ void Perl_Client_GrantAllAAPoints(Client* self, uint8 unlock_level)
self->GrantAllAAPoints(unlock_level);
}
void Perl_Client_AddEbonCrystals(Client* self, uint32 amount)
{
self->AddEbonCrystals(amount);
}
void Perl_Client_AddRadiantCrystals(Client* self, uint32 amount)
{
self->AddRadiantCrystals(amount);
}
void Perl_Client_RemoveEbonCrystals(Client* self, uint32 amount)
{
self->RemoveEbonCrystals(amount);
}
void Perl_Client_RemoveRadiantCrystals(Client* self, uint32 amount)
{
self->RemoveRadiantCrystals(amount);
}
void perl_register_client()
{
perl::interpreter perl(PERL_GET_THX);
@ -3006,6 +3042,7 @@ void perl_register_client()
package.add("AddEXP", (void(*)(Client*, uint32))&Perl_Client_AddEXP);
package.add("AddEXP", (void(*)(Client*, uint32, uint8))&Perl_Client_AddEXP);
package.add("AddEXP", (void(*)(Client*, uint32, uint8, bool))&Perl_Client_AddEXP);
package.add("AddEbonCrystals", &Perl_Client_AddEbonCrystals);
package.add("AddExpeditionLockout", (void(*)(Client*, std::string, std::string, uint32))&Perl_Client_AddExpeditionLockout);
package.add("AddExpeditionLockout", (void(*)(Client*, std::string, std::string, uint32, std::string))&Perl_Client_AddExpeditionLockout);
package.add("AddExpeditionLockoutDuration", (void(*)(Client*, std::string, std::string, int))&Perl_Client_AddExpeditionLockoutDuration);
@ -3021,6 +3058,7 @@ void perl_register_client()
package.add("AddPlatinum", (void(*)(Client*, uint32))&Perl_Client_AddPlatinum);
package.add("AddPlatinum", (void(*)(Client*, uint32, bool))&Perl_Client_AddPlatinum);
package.add("AddPVPPoints", &Perl_Client_AddPVPPoints);
package.add("AddRadiantCrystals", &Perl_Client_AddRadiantCrystals);
package.add("AddSkill", &Perl_Client_AddSkill);
package.add("Admin", &Perl_Client_Admin);
package.add("ApplySpell", (void(*)(Client*, int))&Perl_Client_ApplySpell);
@ -3352,6 +3390,7 @@ void perl_register_client()
package.add("ReloadDataBuckets", &Perl_Client_ReloadDataBuckets);
package.add("RemoveAllExpeditionLockouts", (void(*)(Client*))&Perl_Client_RemoveAllExpeditionLockouts);
package.add("RemoveAllExpeditionLockouts", (void(*)(Client*, std::string))&Perl_Client_RemoveAllExpeditionLockouts);
package.add("RemoveEbonCrystals", &Perl_Client_RemoveEbonCrystals);
package.add("RemoveExpeditionLockout", &Perl_Client_RemoveExpeditionLockout);
package.add("RemoveFromInstance", &Perl_Client_RemoveFromInstance);
package.add("RemoveItem", (void(*)(Client*, uint32))&Perl_Client_RemoveItem);
@ -3359,6 +3398,7 @@ void perl_register_client()
package.add("RemoveLDoNLoss", &Perl_Client_RemoveLDoNLoss);
package.add("RemoveLDoNWin", &Perl_Client_RemoveLDoNWin);
package.add("RemoveNoRent", &Perl_Client_RemoveNoRent);
package.add("RemoveRadiantCrystals", &Perl_Client_RemoveRadiantCrystals);
package.add("ResetAA", &Perl_Client_ResetAA);
package.add("ResetAllDisciplineTimers", &Perl_Client_ResetAllDisciplineTimers);
package.add("ResetAllCastbarCooldowns", &Perl_Client_ResetAllCastbarCooldowns);

View File

@ -1101,9 +1101,9 @@ void ClientTaskState::RewardTask(Client *c, const TaskInformation *ti, ClientTas
if (ti->reward_points > 0) {
if (ti->reward_point_type == static_cast<int32_t>(zone->GetCurrencyID(RADIANT_CRYSTAL))) {
c->AddCrystals(ti->reward_points, 0);
c->AddRadiantCrystals(ti->reward_points);
} else if (ti->reward_point_type == static_cast<int32_t>(zone->GetCurrencyID(EBON_CRYSTAL))) {
c->AddCrystals(0, ti->reward_points);
c->AddEbonCrystals(ti->reward_points);
} else {
for (const auto& ac : zone->AlternateCurrencies) {
if (ti->reward_point_type == ac.id) {