mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
[Feature] Implement Custom Pet Names (#4594)
* rebase\tidy up to address commends * I blame git for this one * last typo * spaces * formating fixes I think? * Repository fixes * Cleanup --------- Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
parent
0acad18067
commit
31abaf8016
@ -50,6 +50,7 @@
|
||||
#include "../common/repositories/raid_members_repository.h"
|
||||
#include "../common/repositories/reports_repository.h"
|
||||
#include "../common/repositories/variables_repository.h"
|
||||
#include "../common/repositories/character_pet_name_repository.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
// Disgrace: for windows compile
|
||||
@ -313,6 +314,12 @@ bool Database::ReserveName(uint32 account_id, const std::string& name)
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& p = CharacterPetNameRepository::GetWhere(*this, where_filter);
|
||||
if (!p.empty()) {
|
||||
LogInfo("Account [{}] requested name [{}] but name is already taken by an Pet", account_id, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = CharacterDataRepository::NewEntity();
|
||||
|
||||
e.account_id = account_id;
|
||||
|
||||
@ -6320,6 +6320,19 @@ ALTER TABLE `data_buckets` ADD KEY `idx_account_id_key` (`account_id`, `key`);
|
||||
)",
|
||||
.content_schema_update = false
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9293,
|
||||
.description = "2025_01_10_create_pet_names_table.sql",
|
||||
.check = "SHOW TABLES LIKE 'character_pet_name'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
CREATE TABLE `character_pet_name` (
|
||||
`character_id` INT(11) NOT NULL PRIMARY KEY,
|
||||
`name` VARCHAR(64) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
)",
|
||||
},
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
// ManifestEntry{
|
||||
// .version = 9228,
|
||||
|
||||
@ -64,6 +64,7 @@ namespace DatabaseSchema {
|
||||
{"character_pet_buffs", "char_id"},
|
||||
{"character_pet_info", "char_id"},
|
||||
{"character_pet_inventory", "char_id"},
|
||||
{"character_pet_name", "character_id"},
|
||||
{"character_peqzone_flags", "id"},
|
||||
{"character_potionbelt", "id"},
|
||||
{"character_skills", "id"},
|
||||
|
||||
@ -77,6 +77,7 @@ N(OP_CashReward),
|
||||
N(OP_CastSpell),
|
||||
N(OP_ChangeSize),
|
||||
N(OP_ChannelMessage),
|
||||
N(OP_ChangePetName),
|
||||
N(OP_CharacterCreate),
|
||||
N(OP_CharacterCreateRequest),
|
||||
N(OP_CharInventory),
|
||||
@ -284,6 +285,8 @@ N(OP_InspectMessageUpdate),
|
||||
N(OP_InspectRequest),
|
||||
N(OP_InstillDoubt),
|
||||
N(OP_InterruptCast),
|
||||
N(OP_InvokeChangePetName),
|
||||
N(OP_InvokeChangePetNameImmediate),
|
||||
N(OP_ItemLinkClick),
|
||||
N(OP_ItemLinkResponse),
|
||||
N(OP_ItemLinkText),
|
||||
|
||||
@ -5819,6 +5819,21 @@ struct ChangeSize_Struct
|
||||
/*16*/
|
||||
};
|
||||
|
||||
struct ChangePetName_Struct {
|
||||
/*00*/ char new_pet_name[64];
|
||||
/*40*/ char pet_owner_name[64];
|
||||
/*80*/ int response_code;
|
||||
};
|
||||
|
||||
enum ChangePetNameResponse : int {
|
||||
Denied = 0, // 5167 You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name.
|
||||
Accepted = 1, // 5976 Your request for a name change was successful.
|
||||
Timeout = -3, // 5979 You must wait longer before submitting another name request. Please try again in a few minutes.
|
||||
NotEligible = -4, // 5980 Your character is not eligible for a name change.
|
||||
Pending = -5, // 5193 You already have a name change pending. Please wait until it is fully processed before attempting another name change.
|
||||
Unhandled = -1
|
||||
};
|
||||
|
||||
// New OpCode/Struct for SoD+
|
||||
struct GroupMakeLeader_Struct
|
||||
{
|
||||
|
||||
392
common/repositories/base/base_character_pet_name_repository.h
Normal file
392
common/repositories/base/base_character_pet_name_repository.h
Normal file
@ -0,0 +1,392 @@
|
||||
/**
|
||||
* DO NOT MODIFY THIS FILE
|
||||
*
|
||||
* This repository was automatically generated and is NOT to be modified directly.
|
||||
* Any repository modifications are meant to be made to the repository extending the base.
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_CHARACTER_PET_NAME_REPOSITORY_H
|
||||
#define EQEMU_BASE_CHARACTER_PET_NAME_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseCharacterPetNameRepository {
|
||||
public:
|
||||
struct CharacterPetName {
|
||||
int32_t character_id;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("character_id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"character_id",
|
||||
"name",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"character_id",
|
||||
"name",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string SelectColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("character_pet_name");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static CharacterPetName NewEntity()
|
||||
{
|
||||
CharacterPetName e{};
|
||||
|
||||
e.character_id = 0;
|
||||
e.name = "";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static CharacterPetName GetCharacterPetName(
|
||||
const std::vector<CharacterPetName> &character_pet_names,
|
||||
int character_pet_name_id
|
||||
)
|
||||
{
|
||||
for (auto &character_pet_name : character_pet_names) {
|
||||
if (character_pet_name.character_id == character_pet_name_id) {
|
||||
return character_pet_name;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static CharacterPetName FindOne(
|
||||
Database& db,
|
||||
int character_pet_name_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
character_pet_name_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
CharacterPetName e{};
|
||||
|
||||
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.name = row[1] ? row[1] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int character_pet_name_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
character_pet_name_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const CharacterPetName &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.character_id));
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.name) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.character_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static CharacterPetName InsertOne(
|
||||
Database& db,
|
||||
CharacterPetName e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.character_id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
e = NewEntity();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<CharacterPetName> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<CharacterPetName> All(Database& db)
|
||||
{
|
||||
std::vector<CharacterPetName> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
CharacterPetName e{};
|
||||
|
||||
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.name = row[1] ? row[1] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<CharacterPetName> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<CharacterPetName> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {}",
|
||||
BaseSelect(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
CharacterPetName e{};
|
||||
|
||||
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.name = row[1] ? row[1] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {}",
|
||||
TableName(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int Truncate(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"TRUNCATE TABLE {}",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int64 GetMaxId(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||
PrimaryKey(),
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COUNT(*) FROM {} {}",
|
||||
TableName(),
|
||||
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const CharacterPetName &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<CharacterPetName> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_CHARACTER_PET_NAME_REPOSITORY_H
|
||||
13
common/repositories/character_pet_name_repository.h
Normal file
13
common/repositories/character_pet_name_repository.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
|
||||
#define EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_character_pet_name_repository.h"
|
||||
|
||||
class CharacterPetNameRepository: public BaseCharacterPetNameRepository {
|
||||
public:
|
||||
// Custom extended repository methods here
|
||||
};
|
||||
|
||||
#endif //EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
|
||||
@ -42,7 +42,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9292
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9293
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
|
||||
|
||||
#endif
|
||||
|
||||
@ -735,3 +735,12 @@ OP_PickZone=0xaaba
|
||||
|
||||
#evolve item related
|
||||
OP_EvolveItem=0x7cfb
|
||||
|
||||
# This is bugged in ROF2
|
||||
# OP_InvokeChangePetNameImmediate is supposed to write DisablePetNameChangeReminder=0 to reset the 'nag reminder'
|
||||
# It actually sets DisableNameChangeReminder=0 (player name change nag reminder). Additionally, clicking the
|
||||
# 'Don't remind me later' button sets DisableNameChangeReminder=1
|
||||
# This can be fixed with a client patch.
|
||||
OP_InvokeChangePetNameImmediate=0x046d
|
||||
OP_InvokeChangePetName=0x4506
|
||||
OP_ChangePetName=0x5dab
|
||||
@ -66,6 +66,7 @@ extern volatile bool RunLoops;
|
||||
#include "../common/repositories/character_spells_repository.h"
|
||||
#include "../common/repositories/character_disciplines_repository.h"
|
||||
#include "../common/repositories/character_data_repository.h"
|
||||
#include "../common/repositories/character_pet_name_repository.h"
|
||||
#include "../common/repositories/discovered_items_repository.h"
|
||||
#include "../common/repositories/inventory_repository.h"
|
||||
#include "../common/repositories/keyring_repository.h"
|
||||
@ -1828,7 +1829,7 @@ void Client::FriendsWho(char *FriendsString) {
|
||||
Message(0, "Error: World server disconnected");
|
||||
else {
|
||||
auto pack =
|
||||
new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString));
|
||||
new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString));
|
||||
ServerFriendsWho_Struct* FriendsWho = (ServerFriendsWho_Struct*) pack->pBuffer;
|
||||
FriendsWho->FromID = GetID();
|
||||
strcpy(FriendsWho->FromName, GetName());
|
||||
@ -2209,7 +2210,7 @@ void Client::Stand() {
|
||||
}
|
||||
|
||||
void Client::Sit() {
|
||||
SetAppearance(eaSitting, false);
|
||||
SetAppearance(eaSitting, false);
|
||||
}
|
||||
|
||||
void Client::ChangeLastName(std::string last_name) {
|
||||
@ -4392,6 +4393,83 @@ void Client::KeyRingList()
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::IsPetNameChangeAllowed() {
|
||||
DataBucketKey k = GetScopedBucketKeys();
|
||||
k.key = "PetNameChangesAllowed";
|
||||
|
||||
auto b = DataBucket::GetData(k);
|
||||
if (!b.value.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Client::InvokeChangePetName(bool immediate) {
|
||||
if (!IsPetNameChangeAllowed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto packet_op = immediate ? OP_InvokeChangePetNameImmediate : OP_InvokeChangePetName;
|
||||
|
||||
auto outapp = new EQApplicationPacket(packet_op, 0);
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::GrantPetNameChange() {
|
||||
DataBucketKey k = GetScopedBucketKeys();
|
||||
k.key = "PetNameChangesAllowed";
|
||||
k.value = "true";
|
||||
DataBucket::SetData(k);
|
||||
|
||||
InvokeChangePetName(true);
|
||||
}
|
||||
|
||||
void Client::ClearPetNameChange() {
|
||||
DataBucketKey k = GetScopedBucketKeys();
|
||||
k.key = "PetNameChangesAllowed";
|
||||
|
||||
DataBucket::DeleteData(k);
|
||||
}
|
||||
|
||||
bool Client::ChangePetName(std::string new_name)
|
||||
{
|
||||
if (new_name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsPetNameChangeAllowed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pet = GetPet();
|
||||
if (!pet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pet->GetName() == new_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!database.CheckNameFilter(new_name) || database.IsNameUsed(new_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CharacterPetNameRepository::ReplaceOne(
|
||||
database,
|
||||
CharacterPetNameRepository::CharacterPetName{
|
||||
.character_id = static_cast<int32_t>(CharacterID()),
|
||||
.name = new_name
|
||||
}
|
||||
);
|
||||
|
||||
pet->TempName(new_name.c_str());
|
||||
|
||||
ClearPetNameChange();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Client::IsDiscovered(uint32 item_id) {
|
||||
const auto& l = DiscoveredItemsRepository::GetWhere(
|
||||
database,
|
||||
@ -5628,13 +5706,13 @@ bool Client::TryReward(uint32 claim_id)
|
||||
|
||||
if (amt == 1) {
|
||||
query = StringFormat("DELETE FROM account_rewards "
|
||||
"WHERE account_id = %i AND reward_id = %i",
|
||||
AccountID(), claim_id);
|
||||
"WHERE account_id = %i AND reward_id = %i",
|
||||
AccountID(), claim_id);
|
||||
auto results = database.QueryDatabase(query);
|
||||
} else {
|
||||
query = StringFormat("UPDATE account_rewards SET amount = (amount-1) "
|
||||
"WHERE account_id = %i AND reward_id = %i",
|
||||
AccountID(), claim_id);
|
||||
"WHERE account_id = %i AND reward_id = %i",
|
||||
AccountID(), claim_id);
|
||||
auto results = database.QueryDatabase(query);
|
||||
}
|
||||
|
||||
@ -8367,7 +8445,7 @@ int Client::GetQuiverHaste(int delay)
|
||||
for (int r = EQ::invslot::GENERAL_BEGIN; r <= EQ::invslot::GENERAL_END; r++) {
|
||||
pi = GetInv().GetItem(r);
|
||||
if (pi && pi->IsClassBag() && pi->GetItem()->BagType == EQ::item::BagTypeQuiver &&
|
||||
pi->GetItem()->BagWR > 0)
|
||||
pi->GetItem()->BagWR > 0)
|
||||
break;
|
||||
if (r == EQ::invslot::GENERAL_END)
|
||||
// we will get here if we don't find a valid quiver
|
||||
@ -9841,7 +9919,7 @@ const ExpeditionLockoutTimer* Client::GetExpeditionLockout(
|
||||
for (const auto& expedition_lockout : m_expedition_lockouts)
|
||||
{
|
||||
if ((include_expired || !expedition_lockout.IsExpired()) &&
|
||||
expedition_lockout.IsSameLockout(expedition_name, event_name))
|
||||
expedition_lockout.IsSameLockout(expedition_name, event_name))
|
||||
{
|
||||
return &expedition_lockout;
|
||||
}
|
||||
@ -9856,7 +9934,7 @@ std::vector<ExpeditionLockoutTimer> Client::GetExpeditionLockouts(
|
||||
for (const auto& lockout : m_expedition_lockouts)
|
||||
{
|
||||
if ((include_expired || !lockout.IsExpired()) &&
|
||||
lockout.GetExpeditionName() == expedition_name)
|
||||
lockout.GetExpeditionName() == expedition_name)
|
||||
{
|
||||
lockouts.emplace_back(lockout);
|
||||
}
|
||||
|
||||
@ -307,6 +307,11 @@ public:
|
||||
void KeyRingAdd(uint32 item_id);
|
||||
bool KeyRingCheck(uint32 item_id);
|
||||
void KeyRingList();
|
||||
bool IsPetNameChangeAllowed();
|
||||
void GrantPetNameChange();
|
||||
void ClearPetNameChange();
|
||||
void InvokeChangePetName(bool immediate = true);
|
||||
bool ChangePetName(std::string new_name);
|
||||
bool IsClient() const override { return true; }
|
||||
bool IsOfClientBot() const override { return true; }
|
||||
bool IsOfClientBotMerc() const override { return true; }
|
||||
@ -2267,6 +2272,7 @@ public:
|
||||
const std::string &GetMailKeyFull() const;
|
||||
const std::string &GetMailKey() const;
|
||||
void ShowZoneShardMenu();
|
||||
void Handle_OP_ChangePetName(const EQApplicationPacket *app);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -66,6 +66,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "../common/repositories/character_corpses_repository.h"
|
||||
#include "../common/repositories/guild_tributes_repository.h"
|
||||
#include "../common/repositories/buyer_buy_lines_repository.h"
|
||||
#include "../common/repositories/character_pet_name_repository.h"
|
||||
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/repositories/character_stats_record_repository.h"
|
||||
@ -160,6 +161,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade;
|
||||
ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell;
|
||||
ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage;
|
||||
ConnectedOpcodes[OP_ChangePetName] = &Client::Handle_OP_ChangePetName;
|
||||
ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs;
|
||||
ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks;
|
||||
ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname;
|
||||
@ -820,6 +822,10 @@ void Client::CompleteConnect()
|
||||
CharacterID()
|
||||
)
|
||||
);
|
||||
|
||||
if (IsPetNameChangeAllowed()) {
|
||||
InvokeChangePetName(false);
|
||||
}
|
||||
}
|
||||
|
||||
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
||||
@ -4568,6 +4574,27 @@ void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_ChangePetName(const EQApplicationPacket *app) {
|
||||
if (app->size != sizeof(ChangePetName_Struct)) {
|
||||
LogError("Got OP_ChangePetName of incorrect size. Expected [{}], got [{}].", sizeof(ChangePetName_Struct), app->size);
|
||||
return;
|
||||
}
|
||||
|
||||
auto p = (ChangePetName_Struct *) app->pBuffer;
|
||||
if (!IsPetNameChangeAllowed()) {
|
||||
p->response_code = ChangePetNameResponse::NotEligible;
|
||||
QueuePacket(app);
|
||||
return;
|
||||
}
|
||||
|
||||
p->response_code = ChangePetNameResponse::Denied;
|
||||
if (ChangePetName(p->new_pet_name)) {
|
||||
p->response_code = ChangePetNameResponse::Accepted;
|
||||
}
|
||||
|
||||
QueuePacket(app);
|
||||
}
|
||||
|
||||
void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app)
|
||||
{
|
||||
if (!RuleB(Spells, EnableBlockedBuffs))
|
||||
|
||||
@ -3458,6 +3458,12 @@ void Lua_Client::ShowZoneShardMenu()
|
||||
self->ShowZoneShardMenu();
|
||||
}
|
||||
|
||||
void Lua_Client::GrantPetNameChange()
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->GrantPetNameChange();
|
||||
}
|
||||
|
||||
void Lua_Client::SetAAEXPPercentage(uint8 percentage)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
@ -3568,6 +3574,7 @@ luabind::scope lua_register_client() {
|
||||
.def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill)
|
||||
.def("CashReward", &Lua_Client::CashReward)
|
||||
.def("ChangeLastName", (void(Lua_Client::*)(std::string))&Lua_Client::ChangeLastName)
|
||||
.def("GrantPetNameChange", &Lua_Client::GrantPetNameChange)
|
||||
.def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID)
|
||||
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill)
|
||||
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill)
|
||||
|
||||
@ -597,6 +597,7 @@ public:
|
||||
|
||||
bool ReloadDataBuckets();
|
||||
void ShowZoneShardMenu();
|
||||
void GrantPetNameChange();
|
||||
|
||||
Lua_Expedition CreateExpedition(luabind::object expedition_info);
|
||||
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players);
|
||||
|
||||
@ -3229,6 +3229,11 @@ perl::array Perl_Client_GetInventorySlots(Client* self)
|
||||
return result;
|
||||
}
|
||||
|
||||
void Perl_Client_GrantPetNameChange(Client* self)
|
||||
{
|
||||
self->GrantPetNameChange();
|
||||
}
|
||||
|
||||
void Perl_Client_SetAAEXPPercentage(Client* self, uint8 percentage)
|
||||
{
|
||||
self->SetAAEXPPercentage(percentage);
|
||||
@ -3335,6 +3340,7 @@ void perl_register_client()
|
||||
package.add("CanHaveSkill", &Perl_Client_CanHaveSkill);
|
||||
package.add("CashReward", &Perl_Client_CashReward);
|
||||
package.add("ChangeLastName", &Perl_Client_ChangeLastName);
|
||||
package.add("GrantPetNameChange", &Perl_Client_GrantPetNameChange);
|
||||
package.add("CharacterID", &Perl_Client_CharacterID);
|
||||
package.add("CheckIncreaseSkill", (bool(*)(Client*, int))&Perl_Client_CheckIncreaseSkill);
|
||||
package.add("CheckIncreaseSkill", (bool(*)(Client*, int, int))&Perl_Client_CheckIncreaseSkill);
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
|
||||
#include "../common/repositories/pets_repository.h"
|
||||
#include "../common/repositories/pets_beastlord_data_repository.h"
|
||||
#include "../common/repositories/character_pet_name_repository.h"
|
||||
|
||||
#include "entity.h"
|
||||
#include "client.h"
|
||||
@ -164,6 +165,12 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
|
||||
// 4 - Keep DB name
|
||||
// 5 - `s ward
|
||||
|
||||
if (IsClient() && !petname) {
|
||||
const auto vanity_name = CharacterPetNameRepository::FindOne(database, CastToClient()->CharacterID());
|
||||
if (!vanity_name.name.empty()) {
|
||||
petname = vanity_name.name.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if (petname != nullptr) {
|
||||
// Name was provided, use it.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user