mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-26 20:22:29 +00:00
Compare commits
19 Commits
log
...
teleport_door
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d45de865d | |||
| 331032f4f4 | |||
| 61790ef195 | |||
| 5446234fd3 | |||
| 9bdb70b2f0 | |||
| c438819ed6 | |||
| 7a791dda3c | |||
| 6366a3fa38 | |||
| 306a08b9ac | |||
| 467f8d7867 | |||
| 6c2100a650 | |||
| 9dacd0bd7a | |||
| 3c6cdd0905 | |||
| d1fb74ff5f | |||
| f81b9d8244 | |||
| b9e87abb3c | |||
| 451b0180c9 | |||
| 77ae4f0c3f | |||
| 852d951b65 |
+73
-50
@@ -341,65 +341,88 @@ bool Database::ReserveName(uint32 account_id, char* name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Delete the character with the name "name"
|
||||
returns false on failure, true otherwise
|
||||
*/
|
||||
bool Database::DeleteCharacter(char *name) {
|
||||
uint32 charid = 0;
|
||||
if(!name || !strlen(name)) {
|
||||
/**
|
||||
* @param character_name
|
||||
* @return
|
||||
*/
|
||||
bool Database::DeleteCharacter(char *character_name) {
|
||||
uint32 character_id = 0;
|
||||
if(!character_name || !strlen(character_name)) {
|
||||
LogInfo("DeleteCharacter: request to delete without a name (empty char slot)");
|
||||
return false;
|
||||
}
|
||||
LogInfo("Database::DeleteCharacter name : [{}]", name);
|
||||
|
||||
/* Get id from character_data before deleting record so we can clean up the rest of the tables */
|
||||
std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", name);
|
||||
auto results = QueryDatabase(query);
|
||||
for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); }
|
||||
if (charid <= 0){
|
||||
LogError("Database::DeleteCharacter :: Character ({}) not found, stopping delete...", name);
|
||||
std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", character_name);
|
||||
auto results = QueryDatabase(query);
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
character_id = atoi(row[0]);
|
||||
}
|
||||
|
||||
if (character_id <= 0) {
|
||||
LogError("DeleteCharacter | Invalid Character ID [{}]", character_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
std::string delete_type = "hard-deleted";
|
||||
if (RuleB(Character, SoftDeletes)) {
|
||||
delete_type = "soft-deleted";
|
||||
std::string query = fmt::format(
|
||||
SQL(
|
||||
UPDATE
|
||||
character_data
|
||||
SET
|
||||
deleted_at = NOW()
|
||||
WHERE
|
||||
id = '{}'
|
||||
),
|
||||
character_id
|
||||
);
|
||||
|
||||
QueryDatabase(query);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LogInfo("DeleteCharacter | Character [{}] ({}) is being [{}]", character_name, character_id, delete_type);
|
||||
|
||||
query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", character_id); QueryDatabase(query);
|
||||
#ifdef BOTS
|
||||
query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", charid); // note: only use of GetMobTypeById()
|
||||
query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", character_id); // note: only use of GetMobTypeById()
|
||||
#else
|
||||
query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", charid);
|
||||
query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", character_id);
|
||||
#endif
|
||||
QueryDatabase(query);
|
||||
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ public:
|
||||
|
||||
bool AddToNameFilter(const char* name);
|
||||
bool CreateCharacter(uint32 account_id, char* name, uint16 gender, uint16 race, uint16 class_, uint8 str, uint8 sta, uint8 cha, uint8 dex, uint8 int_, uint8 agi, uint8 wis, uint8 face);
|
||||
bool DeleteCharacter(char* name);
|
||||
bool DeleteCharacter(char* character_name);
|
||||
bool MoveCharacterToZone(const char* charname, const char* zonename);
|
||||
bool MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid);
|
||||
bool MoveCharacterToZone(uint32 iCharID, const char* iZonename);
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace DatabaseSchema {
|
||||
static std::vector<std::string> GetPlayerTables()
|
||||
{
|
||||
std::vector<std::string> tables = {
|
||||
"aa_timers",
|
||||
"account",
|
||||
"account_ip",
|
||||
"account_flags",
|
||||
|
||||
@@ -842,6 +842,42 @@
|
||||
#define LogStatusDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAIScanClose(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAIScanCloseDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAIYellForHelp(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAIYellForHelpDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAICastBeneficialClose(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAICastBeneficialCloseDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAoeCast(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAoeCastDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogEntityManagement(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogEntityManagementDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogFlee(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogFleeDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ RULE_BOOL(Character, AllowCrossClassTrainers, false, "")
|
||||
RULE_BOOL(Character, PetsUseReagents, true, "Pets use reagent on spells")
|
||||
RULE_BOOL(Character, DismountWater, true, "Dismount horses when entering water")
|
||||
RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing")
|
||||
RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9145
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9146
|
||||
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,15 @@
|
||||
account
|
||||
account_ip
|
||||
account_flags
|
||||
account_rewards
|
||||
adventure_details
|
||||
adventure_stats
|
||||
buyer
|
||||
char_recipe_list
|
||||
character_auras
|
||||
character_activities
|
||||
character_alt_currency
|
||||
character_alternate_abilities
|
||||
character_auras
|
||||
character_bandolier
|
||||
character_bind
|
||||
character_buffs
|
||||
@@ -20,15 +26,22 @@ character_leadership_abilities
|
||||
character_material
|
||||
character_memmed_spells
|
||||
character_pet_buffs
|
||||
character_pet_info
|
||||
character_pet_inventory
|
||||
character_potionbelt
|
||||
character_skills
|
||||
character_spells
|
||||
character_tasks
|
||||
character_tribute
|
||||
completed_tasks
|
||||
data_buckets
|
||||
faction_values
|
||||
friends
|
||||
guild_bank
|
||||
guild_members
|
||||
guild_ranks
|
||||
guild_relations
|
||||
guilds
|
||||
instance_list_player
|
||||
inventory
|
||||
inventory_snapshots
|
||||
@@ -36,6 +49,9 @@ keyring
|
||||
mail
|
||||
player_titlesets
|
||||
quest_globals
|
||||
sharedbank
|
||||
timers
|
||||
titles
|
||||
zone_flags
|
||||
trader
|
||||
trader_audit
|
||||
zone_flags"
|
||||
@@ -399,6 +399,7 @@
|
||||
9143|2019_09_16_account_table_changes.sql|SHOW COLUMNS FROM `account` LIKE 'ls_id'|empty|
|
||||
9144|2019_11_09_logsys_description_update.sql|SELECT * FROM db_version WHERE version >= 9143|empty|
|
||||
9145|2019_12_24_banned_ips_update.sql|SHOW TABLES LIKE 'Banned_IPs'|not_empty|
|
||||
9146|2020_01_10_character_soft_deletes.sql|SHOW COLUMNS FROM `character_data` LIKE 'deleted_at'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `character_data` ADD COLUMN `deleted_at` datetime NULL DEFAULT NULL;
|
||||
+194
-146
@@ -31,23 +31,27 @@ extern std::vector<RaceClassAllocation> character_create_allocations;
|
||||
extern std::vector<RaceClassCombos> character_create_race_class_combos;
|
||||
|
||||
|
||||
// the current stuff is at the bottom of this function
|
||||
void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit)
|
||||
/**
|
||||
* @param account_id
|
||||
* @param out_app
|
||||
* @param client_version_bit
|
||||
*/
|
||||
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
|
||||
{
|
||||
/* Set Character Creation Limit */
|
||||
EQEmu::versions::ClientVersion client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(clientVersionBit);
|
||||
EQEmu::versions::ClientVersion
|
||||
client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(client_version_bit);
|
||||
size_t character_limit = EQEmu::constants::StaticLookup(client_version)->CharacterCreationLimit;
|
||||
|
||||
// Validate against absolute server max
|
||||
if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT)
|
||||
|
||||
if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT) {
|
||||
character_limit = EQEmu::constants::CHARACTER_CREATION_LIMIT;
|
||||
}
|
||||
|
||||
// Force Titanium clients to use '8'
|
||||
if (client_version == EQEmu::versions::ClientVersion::Titanium)
|
||||
if (client_version == EQEmu::versions::ClientVersion::Titanium) {
|
||||
character_limit = 8;
|
||||
|
||||
/* Get Character Info */
|
||||
std::string cquery = StringFormat(
|
||||
}
|
||||
|
||||
std::string character_list_query = StringFormat(
|
||||
"SELECT "
|
||||
"`id`, " // 0
|
||||
"name, " // 1
|
||||
@@ -71,237 +75,281 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou
|
||||
"zone_id " // 19
|
||||
"FROM "
|
||||
"character_data "
|
||||
"WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit);
|
||||
auto results = database.QueryDatabase(cquery);
|
||||
"WHERE `account_id` = %i AND deleted_at IS NULL ORDER BY `name` LIMIT %u",
|
||||
account_id,
|
||||
character_limit
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(character_list_query);
|
||||
|
||||
size_t character_count = results.RowCount();
|
||||
if (character_count == 0) {
|
||||
*outApp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *)(*outApp)->pBuffer;
|
||||
cs->CharCount = 0;
|
||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
|
||||
cs->CharCount = 0;
|
||||
cs->TotalChars = character_limit;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
|
||||
*outApp = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
||||
|
||||
unsigned char *buff_ptr = (*outApp)->pBuffer;
|
||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *)buff_ptr;
|
||||
unsigned char *buff_ptr = (*out_app)->pBuffer;
|
||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr;
|
||||
|
||||
cs->CharCount = character_count;
|
||||
cs->CharCount = character_count;
|
||||
cs->TotalChars = character_limit;
|
||||
|
||||
buff_ptr += sizeof(CharacterSelect_Struct);
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr;
|
||||
PlayerProfile_Struct pp;
|
||||
EQEmu::InventoryProfile inv;
|
||||
CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr;
|
||||
PlayerProfile_Struct player_profile_struct;
|
||||
EQEmu::InventoryProfile inventory_profile;
|
||||
|
||||
pp.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version));
|
||||
inv.SetInventoryVersion(client_version);
|
||||
inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
||||
player_profile_struct.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version));
|
||||
inventory_profile.SetInventoryVersion(client_version);
|
||||
inventory_profile.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
||||
|
||||
uint32 character_id = (uint32)atoi(row[0]);
|
||||
uint8 has_home = 0;
|
||||
uint8 has_bind = 0;
|
||||
uint32 character_id = (uint32) atoi(row[0]);
|
||||
uint8 has_home = 0;
|
||||
uint8 has_bind = 0;
|
||||
|
||||
memset(&pp, 0, sizeof(PlayerProfile_Struct));
|
||||
|
||||
/* Fill CharacterSelectEntry_Struct */
|
||||
memset(cse->Name, 0, sizeof(cse->Name));
|
||||
strcpy(cse->Name, row[1]);
|
||||
cse->Class = (uint8)atoi(row[4]);
|
||||
cse->Race = (uint32)atoi(row[3]);
|
||||
cse->Level = (uint8)atoi(row[5]);
|
||||
cse->ShroudClass = cse->Class;
|
||||
cse->ShroudRace = cse->Race;
|
||||
cse->Zone = (uint16)atoi(row[19]);
|
||||
cse->Instance = 0;
|
||||
cse->Gender = (uint8)atoi(row[2]);
|
||||
cse->Face = (uint8)atoi(row[15]);
|
||||
memset(&player_profile_struct, 0, sizeof(PlayerProfile_Struct));
|
||||
|
||||
for (uint32 matslot = 0; matslot < EQEmu::textures::materialCount; matslot++) { // Processed below
|
||||
cse->Equip[matslot].Material = 0;
|
||||
cse->Equip[matslot].Unknown1 = 0;
|
||||
cse->Equip[matslot].EliteModel = 0;
|
||||
cse->Equip[matslot].HerosForgeModel = 0;
|
||||
cse->Equip[matslot].Unknown2 = 0;
|
||||
cse->Equip[matslot].Color = 0;
|
||||
}
|
||||
memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name));
|
||||
strcpy(p_character_select_entry_struct->Name, row[1]);
|
||||
p_character_select_entry_struct->Class = (uint8) atoi(row[4]);
|
||||
p_character_select_entry_struct->Race = (uint32) atoi(row[3]);
|
||||
p_character_select_entry_struct->Level = (uint8) atoi(row[5]);
|
||||
p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class;
|
||||
p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race;
|
||||
p_character_select_entry_struct->Zone = (uint16) atoi(row[19]);
|
||||
p_character_select_entry_struct->Instance = 0;
|
||||
p_character_select_entry_struct->Gender = (uint8) atoi(row[2]);
|
||||
p_character_select_entry_struct->Face = (uint8) atoi(row[15]);
|
||||
|
||||
cse->Unknown15 = 0xFF;
|
||||
cse->Unknown19 = 0xFF;
|
||||
cse->DrakkinTattoo = (uint32)atoi(row[17]);
|
||||
cse->DrakkinDetails = (uint32)atoi(row[18]);
|
||||
cse->Deity = (uint32)atoi(row[6]);
|
||||
cse->PrimaryIDFile = 0; // Processed Below
|
||||
cse->SecondaryIDFile = 0; // Processed Below
|
||||
cse->HairColor = (uint8)atoi(row[9]);
|
||||
cse->BeardColor = (uint8)atoi(row[10]);
|
||||
cse->EyeColor1 = (uint8)atoi(row[11]);
|
||||
cse->EyeColor2 = (uint8)atoi(row[12]);
|
||||
cse->HairStyle = (uint8)atoi(row[13]);
|
||||
cse->Beard = (uint8)atoi(row[14]);
|
||||
cse->GoHome = 0; // Processed Below
|
||||
cse->Tutorial = 0; // Processed Below
|
||||
cse->DrakkinHeritage = (uint32)atoi(row[16]);
|
||||
cse->Unknown1 = 0;
|
||||
cse->Enabled = 1;
|
||||
cse->LastLogin = (uint32)atoi(row[7]); // RoF2 value: 1212696584
|
||||
cse->Unknown2 = 0;
|
||||
/* Fill End */
|
||||
for (uint32 material_slot = 0; material_slot < EQEmu::textures::materialCount; material_slot++) {
|
||||
p_character_select_entry_struct->Equip[material_slot].Material = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].EliteModel = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].Color = 0;
|
||||
}
|
||||
|
||||
p_character_select_entry_struct->Unknown15 = 0xFF;
|
||||
p_character_select_entry_struct->Unknown19 = 0xFF;
|
||||
p_character_select_entry_struct->DrakkinTattoo = (uint32) atoi(row[17]);
|
||||
p_character_select_entry_struct->DrakkinDetails = (uint32) atoi(row[18]);
|
||||
p_character_select_entry_struct->Deity = (uint32) atoi(row[6]);
|
||||
p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below
|
||||
p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below
|
||||
p_character_select_entry_struct->HairColor = (uint8) atoi(row[9]);
|
||||
p_character_select_entry_struct->BeardColor = (uint8) atoi(row[10]);
|
||||
p_character_select_entry_struct->EyeColor1 = (uint8) atoi(row[11]);
|
||||
p_character_select_entry_struct->EyeColor2 = (uint8) atoi(row[12]);
|
||||
p_character_select_entry_struct->HairStyle = (uint8) atoi(row[13]);
|
||||
p_character_select_entry_struct->Beard = (uint8) atoi(row[14]);
|
||||
p_character_select_entry_struct->GoHome = 0; // Processed Below
|
||||
p_character_select_entry_struct->Tutorial = 0; // Processed Below
|
||||
p_character_select_entry_struct->DrakkinHeritage = (uint32) atoi(row[16]);
|
||||
p_character_select_entry_struct->Unknown1 = 0;
|
||||
p_character_select_entry_struct->Enabled = 1;
|
||||
p_character_select_entry_struct->LastLogin = (uint32) atoi(row[7]); // RoF2 value: 1212696584
|
||||
p_character_select_entry_struct->Unknown2 = 0;
|
||||
|
||||
if (RuleB(World, EnableReturnHomeButton)) {
|
||||
int now = time(nullptr);
|
||||
if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome))
|
||||
cse->GoHome = 1;
|
||||
if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) {
|
||||
p_character_select_entry_struct->GoHome = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) {
|
||||
cse->Tutorial = 1;
|
||||
if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial))) {
|
||||
p_character_select_entry_struct->Tutorial = 1;
|
||||
}
|
||||
|
||||
/* Set Bind Point Data for any character that may possibly be missing it for any reason */
|
||||
cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5", character_id);
|
||||
auto results_bind = database.QueryDatabase(cquery);
|
||||
auto bind_count = results_bind.RowCount();
|
||||
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) {
|
||||
/**
|
||||
* Bind
|
||||
*/
|
||||
character_list_query = StringFormat(
|
||||
"SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5",
|
||||
character_id
|
||||
);
|
||||
auto results_bind = database.QueryDatabase(character_list_query);
|
||||
auto bind_count = results_bind.RowCount();
|
||||
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) {
|
||||
if (row_b[6] && atoi(row_b[6]) == 4) {
|
||||
has_home = 1;
|
||||
// If our bind count is less than 5, we need to actually make use of this data so lets parse it
|
||||
if (bind_count < 5) {
|
||||
pp.binds[4].zoneId = atoi(row_b[0]);
|
||||
pp.binds[4].instance_id = atoi(row_b[1]);
|
||||
pp.binds[4].x = atof(row_b[2]);
|
||||
pp.binds[4].y = atof(row_b[3]);
|
||||
pp.binds[4].z = atof(row_b[4]);
|
||||
pp.binds[4].heading = atof(row_b[5]);
|
||||
player_profile_struct.binds[4].zoneId = atoi(row_b[0]);
|
||||
player_profile_struct.binds[4].instance_id = atoi(row_b[1]);
|
||||
player_profile_struct.binds[4].x = atof(row_b[2]);
|
||||
player_profile_struct.binds[4].y = atof(row_b[3]);
|
||||
player_profile_struct.binds[4].z = atof(row_b[4]);
|
||||
player_profile_struct.binds[4].heading = atof(row_b[5]);
|
||||
}
|
||||
}
|
||||
if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; }
|
||||
if (row_b[6] && atoi(row_b[6]) == 0) { has_bind = 1; }
|
||||
}
|
||||
|
||||
if (has_home == 0 || has_bind == 0) {
|
||||
cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i",
|
||||
cse->Class, cse->Deity, cse->Race);
|
||||
auto results_bind = database.QueryDatabase(cquery);
|
||||
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
|
||||
character_list_query = StringFormat(
|
||||
"SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i",
|
||||
p_character_select_entry_struct->Class,
|
||||
p_character_select_entry_struct->Deity,
|
||||
p_character_select_entry_struct->Race
|
||||
);
|
||||
auto results_bind = database.QueryDatabase(character_list_query);
|
||||
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
|
||||
/* If a bind_id is specified, make them start there */
|
||||
if (atoi(row_d[1]) != 0) {
|
||||
pp.binds[4].zoneId = (uint32)atoi(row_d[1]);
|
||||
GetSafePoints(pp.binds[4].zoneId, 0, &pp.binds[4].x, &pp.binds[4].y, &pp.binds[4].z);
|
||||
player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[1]);
|
||||
GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &player_profile_struct.binds[4].x, &player_profile_struct.binds[4].y, &player_profile_struct.binds[4].z);
|
||||
}
|
||||
/* Otherwise, use the zone and coordinates given */
|
||||
/* Otherwise, use the zone and coordinates given */
|
||||
else {
|
||||
pp.binds[4].zoneId = (uint32)atoi(row_d[0]);
|
||||
player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[0]);
|
||||
float x = atof(row_d[2]);
|
||||
float y = atof(row_d[3]);
|
||||
float z = atof(row_d[4]);
|
||||
if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); }
|
||||
pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z;
|
||||
if (x == 0 && y == 0 && z == 0) { GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &x, &y, &z); }
|
||||
player_profile_struct.binds[4].x = x;
|
||||
player_profile_struct.binds[4].y = y;
|
||||
player_profile_struct.binds[4].z = z;
|
||||
}
|
||||
}
|
||||
pp.binds[0] = pp.binds[4];
|
||||
player_profile_struct.binds[0] = player_profile_struct.binds[4];
|
||||
/* If no home bind set, set it */
|
||||
if (has_home == 0) {
|
||||
std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
std::string query = StringFormat(
|
||||
"REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
" VALUES (%u, %u, %u, %f, %f, %f, %f, %i)",
|
||||
character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 4);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
character_id,
|
||||
player_profile_struct.binds[4].zoneId,
|
||||
0,
|
||||
player_profile_struct.binds[4].x,
|
||||
player_profile_struct.binds[4].y,
|
||||
player_profile_struct.binds[4].z,
|
||||
player_profile_struct.binds[4].heading,
|
||||
4
|
||||
);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
}
|
||||
/* If no regular bind set, set it */
|
||||
if (has_bind == 0) {
|
||||
std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
std::string query = StringFormat(
|
||||
"REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
" VALUES (%u, %u, %u, %f, %f, %f, %f, %i)",
|
||||
character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
character_id,
|
||||
player_profile_struct.binds[0].zoneId,
|
||||
0,
|
||||
player_profile_struct.binds[0].x,
|
||||
player_profile_struct.binds[0].y,
|
||||
player_profile_struct.binds[0].z,
|
||||
player_profile_struct.binds[0].heading,
|
||||
0
|
||||
);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
/* If our bind count is less than 5, then we have null data that needs to be filled in. */
|
||||
if (bind_count < 5) {
|
||||
// we know that home and main bind must be valid here, so we don't check those
|
||||
// we also use home to fill in the null data like live does.
|
||||
for (int i = 1; i < 4; i++) {
|
||||
if (pp.binds[i].zoneId != 0) // we assume 0 is the only invalid one ...
|
||||
for (int i = 1; i < 4; i++) {
|
||||
if (player_profile_struct.binds[i].zoneId != 0) { // we assume 0 is the only invalid one ...
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
std::string query = StringFormat(
|
||||
"REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
" VALUES (%u, %u, %u, %f, %f, %f, %f, %i)",
|
||||
character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, i);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
character_id,
|
||||
player_profile_struct.binds[4].zoneId,
|
||||
0,
|
||||
player_profile_struct.binds[4].x,
|
||||
player_profile_struct.binds[4].y,
|
||||
player_profile_struct.binds[4].z,
|
||||
player_profile_struct.binds[4].heading,
|
||||
i
|
||||
);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
/* Bind End */
|
||||
|
||||
/* Load Character Material Data for Char Select */
|
||||
cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id);
|
||||
auto results_b = database.QueryDatabase(cquery); uint8 slot = 0;
|
||||
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) {
|
||||
character_list_query = StringFormat(
|
||||
"SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u",
|
||||
character_id
|
||||
);
|
||||
auto results_b = database.QueryDatabase(character_list_query);
|
||||
uint8 slot = 0;
|
||||
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) {
|
||||
slot = atoi(row_b[0]);
|
||||
pp.item_tint.Slot[slot].Red = atoi(row_b[1]);
|
||||
pp.item_tint.Slot[slot].Green = atoi(row_b[2]);
|
||||
pp.item_tint.Slot[slot].Blue = atoi(row_b[3]);
|
||||
pp.item_tint.Slot[slot].UseTint = atoi(row_b[4]);
|
||||
player_profile_struct.item_tint.Slot[slot].Red = atoi(row_b[1]);
|
||||
player_profile_struct.item_tint.Slot[slot].Green = atoi(row_b[2]);
|
||||
player_profile_struct.item_tint.Slot[slot].Blue = atoi(row_b[3]);
|
||||
player_profile_struct.item_tint.Slot[slot].UseTint = atoi(row_b[4]);
|
||||
}
|
||||
/* Character Material Data End */
|
||||
|
||||
/* Load Inventory */
|
||||
// If we ensure that the material data is updated appropriately, we can do away with inventory loads
|
||||
if (GetCharSelInventory(accountID, cse->Name, &inv)) {
|
||||
const EQEmu::ItemData* item = nullptr;
|
||||
const EQEmu::ItemInstance* inst = nullptr;
|
||||
int16 invslot = 0;
|
||||
if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) {
|
||||
const EQEmu::ItemData *item = nullptr;
|
||||
const EQEmu::ItemInstance *inst = nullptr;
|
||||
int16 inventory_slot = 0;
|
||||
|
||||
for (uint32 matslot = EQEmu::textures::textureBegin; matslot < EQEmu::textures::materialCount; matslot++) {
|
||||
invslot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot);
|
||||
if (invslot == INVALID_INDEX) { continue; }
|
||||
inst = inv.GetItem(invslot);
|
||||
if (inst == nullptr) { continue; }
|
||||
inventory_slot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot);
|
||||
if (inventory_slot == INVALID_INDEX) { continue; }
|
||||
inst = inventory_profile.GetItem(inventory_slot);
|
||||
if (inst == nullptr) {
|
||||
continue;
|
||||
}
|
||||
item = inst->GetItem();
|
||||
if (item == nullptr) { continue; }
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matslot > 6) {
|
||||
uint32 idfile = 0;
|
||||
uint32 item_id_file = 0;
|
||||
// Weapon Models
|
||||
if (inst->GetOrnamentationIDFile() != 0) {
|
||||
idfile = inst->GetOrnamentationIDFile();
|
||||
cse->Equip[matslot].Material = idfile;
|
||||
item_id_file = inst->GetOrnamentationIDFile();
|
||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
||||
}
|
||||
else {
|
||||
if (strlen(item->IDFile) > 2) {
|
||||
idfile = atoi(&item->IDFile[2]);
|
||||
cse->Equip[matslot].Material = idfile;
|
||||
item_id_file = atoi(&item->IDFile[2]);
|
||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
||||
}
|
||||
}
|
||||
if (matslot == EQEmu::textures::weaponPrimary) {
|
||||
cse->PrimaryIDFile = idfile;
|
||||
p_character_select_entry_struct->PrimaryIDFile = item_id_file;
|
||||
}
|
||||
else {
|
||||
cse->SecondaryIDFile = idfile;
|
||||
p_character_select_entry_struct->SecondaryIDFile = item_id_file;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32 color = 0;
|
||||
if (pp.item_tint.Slot[matslot].UseTint) {
|
||||
color = pp.item_tint.Slot[matslot].Color;
|
||||
if (player_profile_struct.item_tint.Slot[matslot].UseTint) {
|
||||
color = player_profile_struct.item_tint.Slot[matslot].Color;
|
||||
}
|
||||
else {
|
||||
color = inst->GetColor();
|
||||
}
|
||||
|
||||
// Armor Materials/Models
|
||||
cse->Equip[matslot].Material = item->Material;
|
||||
cse->Equip[matslot].EliteModel = item->EliteMaterial;
|
||||
cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
||||
cse->Equip[matslot].Color = color;
|
||||
p_character_select_entry_struct->Equip[matslot].Material = item->Material;
|
||||
p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial;
|
||||
p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
||||
p_character_select_entry_struct->Equip[matslot].Color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Error loading inventory for %s\n", cse->Name);
|
||||
printf("Error loading inventory for %s\n", p_character_select_entry_struct->Name);
|
||||
}
|
||||
/* Load Inventory End */
|
||||
|
||||
buff_ptr += sizeof(CharacterSelectEntry_Struct);
|
||||
}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ struct CharacterSelect_Struct;
|
||||
class WorldDatabase : public SharedDatabase {
|
||||
public:
|
||||
bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc, bool isTitanium);
|
||||
void GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit);
|
||||
void GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit);
|
||||
int MoveCharacterToBind(int CharID, uint8 bindnum = 0);
|
||||
|
||||
void GetLauncherList(std::vector<std::string> &result);
|
||||
|
||||
@@ -3414,6 +3414,12 @@ void bot_command_help(Client *c, const Seperator *sep)
|
||||
c->Message(m_usage, "%c%s - %s", BOT_COMMAND_CHAR, command_iter.first.c_str(), command_iter.second->desc == nullptr ? "[no description]" : command_iter.second->desc);
|
||||
++bot_commands_shown;
|
||||
}
|
||||
if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, sep->msg, 0);
|
||||
if (i >= 1) {
|
||||
bot_commands_shown += i;
|
||||
}
|
||||
}
|
||||
c->Message(m_message, "%d bot command%s listed.", bot_commands_shown, bot_commands_shown != 1 ? "s" : "");
|
||||
c->Message(m_note, "type %ccommand [help | usage] for more information", BOT_COMMAND_CHAR);
|
||||
}
|
||||
|
||||
+26
-10
@@ -1106,40 +1106,56 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
case ChatChannel_Say: { /* Say */
|
||||
if(message[0] == COMMAND_CHAR) {
|
||||
if(command_dispatch(this, message) == -2) {
|
||||
if(parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
|
||||
if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Command '%s' not recognized.", message);
|
||||
}
|
||||
} else {
|
||||
if(!RuleB(Chat, SuppressCommandErrors))
|
||||
}
|
||||
else if (parse->PlayerHasQuestSub(EVENT_SAY)) {
|
||||
int i = parse->EventPlayer(EVENT_SAY, this, message, 0);
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (EQEmu::ProfanityManager::IsCensorshipActive())
|
||||
EQEmu::ProfanityManager::RedactMessage(message);
|
||||
|
||||
#ifdef BOTS
|
||||
if (message[0] == BOT_COMMAND_CHAR) {
|
||||
if (bot_command_dispatch(this, message) == -2) {
|
||||
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_BOT_COMMAND, this, message, 0);
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Bot command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
else if (parse->PlayerHasQuestSub(EVENT_SAY)) {
|
||||
int i = parse->EventPlayer(EVENT_SAY, this, message, 0);
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Bot command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!RuleB(Chat, SuppressCommandErrors))
|
||||
if (!RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Bot command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (EQEmu::ProfanityManager::IsCensorshipActive()) {
|
||||
EQEmu::ProfanityManager::RedactMessage(message);
|
||||
}
|
||||
|
||||
Mob* sender = this;
|
||||
if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft))
|
||||
sender = GetPet();
|
||||
|
||||
+25
-2
@@ -905,6 +905,8 @@ void Client::CompleteConnect()
|
||||
entity_list.RefreshClientXTargets(this);
|
||||
|
||||
worldserver.RequestTellQueue(GetName());
|
||||
|
||||
entity_list.ScanCloseMobs(close_mobs, this);
|
||||
}
|
||||
|
||||
// connecting opcode handlers
|
||||
@@ -8286,7 +8288,18 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
|
||||
if (GetTarget() && GetTarget()->IsNPC()) {
|
||||
if (silentsaylink) {
|
||||
parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0);
|
||||
parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0);
|
||||
|
||||
if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0);
|
||||
}
|
||||
#ifdef BOTS
|
||||
else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message(Chat::LightGray, "You say, '%s'", response.c_str());
|
||||
@@ -8296,7 +8309,17 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
|
||||
}
|
||||
else {
|
||||
if (silentsaylink) {
|
||||
parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0);
|
||||
if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0);
|
||||
}
|
||||
#ifdef BOTS
|
||||
else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message(Chat::LightGray, "You say, '%s'", response.c_str());
|
||||
|
||||
+1
-22
@@ -256,28 +256,7 @@ bool Client::Process() {
|
||||
* Used in aggro checks
|
||||
*/
|
||||
if (mob_close_scan_timer.Check()) {
|
||||
close_mobs.clear();
|
||||
|
||||
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
|
||||
auto &mob_list = entity_list.GetMobList();
|
||||
|
||||
for (auto itr : mob_list) {
|
||||
Mob *mob = itr.second;
|
||||
float distance = DistanceSquared(m_Position, mob->GetPosition());
|
||||
|
||||
if (mob->GetID() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mob->IsNPC() || mob->IsClient()) {
|
||||
if (distance <= scan_range) {
|
||||
close_mobs.insert(std::pair<uint16, Mob *>(mob->GetID(), mob));
|
||||
}
|
||||
else if ((mob->GetAggroRange() * mob->GetAggroRange()) > scan_range) {
|
||||
close_mobs.insert(std::pair<uint16, Mob *>(mob->GetID(), mob));
|
||||
}
|
||||
}
|
||||
}
|
||||
entity_list.ScanCloseMobs(close_mobs, this);
|
||||
}
|
||||
|
||||
bool may_use_attacks = false;
|
||||
|
||||
+8
-2
@@ -783,6 +783,12 @@ void command_help(Client *c, const Seperator *sep)
|
||||
commands_shown++;
|
||||
c->Message(Chat::White, " %c%s %s", COMMAND_CHAR, cur->first.c_str(), cur->second->desc == nullptr?"":cur->second->desc);
|
||||
}
|
||||
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, c, sep->msg, 0);
|
||||
if (i >= 1) {
|
||||
commands_shown += i;
|
||||
}
|
||||
}
|
||||
c->Message(Chat::White, "%d command%s listed.", commands_shown, commands_shown!=1?"s":"");
|
||||
|
||||
}
|
||||
@@ -13264,8 +13270,8 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
if (bot_command_dispatch(c, bot_message.c_str()) == -2) {
|
||||
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, c, bot_message, 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, bot_message, 0);
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
c->Message(Chat::Red, "Bot command '%s' not recognized.", bot_message.c_str());
|
||||
}
|
||||
|
||||
+29
-22
@@ -209,6 +209,8 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
uint8 disable_add_to_key_ring = GetNoKeyring();
|
||||
uint32 player_has_key = 0;
|
||||
uint32 player_key = 0;
|
||||
bool gm_key = false;
|
||||
bool picklock_success = false;
|
||||
|
||||
const EQEmu::ItemInstance *lock_pick_item = sender->GetInv().GetItem(EQEmu::invslot::slotCursor);
|
||||
player_has_key = static_cast<uint32>(sender->GetInv().HasItem(required_key_item, 1));
|
||||
@@ -217,6 +219,8 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
player_key = required_key_item;
|
||||
}
|
||||
|
||||
bool is_teleport_door = (strncmp(destination_zone_name, "NONE", strlen("NONE")) != 0);
|
||||
|
||||
/**
|
||||
* Object is not triggered
|
||||
*/
|
||||
@@ -226,7 +230,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
* Door is only triggered by an object
|
||||
*/
|
||||
if (trigger == 1) {
|
||||
if (!this->IsDoorOpen() || (open_type == 58)) {
|
||||
if ((!this->IsDoorOpen() || (open_type == 58)) && !is_teleport_door) {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
|
||||
} else {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
|
||||
@@ -247,7 +251,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
bool is_door_open_and_open_able = (this->IsDoorOpen() && (open_type == 58));
|
||||
|
||||
if (is_door_not_locked || is_door_open_and_open_able || is_guild_door) {
|
||||
if (!this->IsDoorOpen() || (this->GetOpenType() == 58)) {
|
||||
if ((!this->IsDoorOpen() || (this->GetOpenType() == 58)) && !is_teleport_door) {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
|
||||
} else {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
|
||||
@@ -283,12 +287,12 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
if (sender->GetGM()) {
|
||||
sender->MessageString(Chat::LightBlue, DOORS_GM);
|
||||
|
||||
if (!IsDoorOpen() || (open_type == 58)) {
|
||||
if ((!IsDoorOpen() || (open_type == 58)) && !is_teleport_door) {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
|
||||
} else {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
|
||||
}
|
||||
|
||||
gm_key = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +312,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
|
||||
sender->Message(Chat::LightBlue, "You got it open!");
|
||||
|
||||
if (!IsDoorOpen() || (open_type == 58)) {
|
||||
if ((!IsDoorOpen() || (open_type == 58)) && !is_teleport_door) {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
|
||||
} else {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
|
||||
@@ -328,12 +332,13 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
LogSkills("Client has lockpicks: skill=[{}]", player_pick_lock_skill);
|
||||
|
||||
if (GetLockpick() <= player_pick_lock_skill) {
|
||||
if (!IsDoorOpen()) {
|
||||
if (!IsDoorOpen() && !is_teleport_door) {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
|
||||
} else {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
|
||||
}
|
||||
sender->MessageString(Chat::LightBlue, DOORS_SUCCESSFUL_PICK);
|
||||
picklock_success = true;
|
||||
} else {
|
||||
sender->MessageString(Chat::LightBlue, DOORS_INSUFFICIENT_SKILL);
|
||||
safe_delete(outapp);
|
||||
@@ -362,7 +367,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
if (sender->KeyRingCheck(required_key_item)) {
|
||||
player_key = required_key_item;
|
||||
sender->Message(Chat::LightBlue, "You got it open!"); // more debug spam
|
||||
if (!IsDoorOpen() || (open_type == 58)) {
|
||||
if ((!IsDoorOpen() || (open_type == 58)) && !is_teleport_door) {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
|
||||
} else {
|
||||
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
|
||||
@@ -376,7 +381,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
}
|
||||
|
||||
entity_list.QueueClients(sender, outapp, false);
|
||||
if (!IsDoorOpen() || (open_type == 58)) {
|
||||
if ((!IsDoorOpen() || (open_type == 58)) && !is_teleport_door) {
|
||||
if (!disable_timer)
|
||||
close_timer.Start();
|
||||
SetOpenState(true);
|
||||
@@ -386,18 +391,6 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
SetOpenState(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything past this point assumes we opened the door
|
||||
* and met all the requirements for opening
|
||||
* everything to do with closed doors has already been taken care of
|
||||
* we return because we don't want people using teleports on an unlocked door (exploit!)
|
||||
*/
|
||||
|
||||
if ((move_door_packet->action == CLOSE_DOOR && invert_state == 0) || (move_door_packet->action == CLOSE_INVDOOR && invert_state == 1)) {
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
}
|
||||
|
||||
safe_delete(outapp);
|
||||
|
||||
if ((GetTriggerDoorID() != 0) && (GetTriggerType() == 1)) {
|
||||
@@ -421,8 +414,22 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
|
||||
/**
|
||||
* Teleport door
|
||||
*/
|
||||
if (((open_type == 57) || (open_type == 58)) &&
|
||||
(strncmp(destination_zone_name, "NONE", strlen("NONE")) != 0)) {
|
||||
if (((open_type == 57) || (open_type == 58)) && is_teleport_door) {
|
||||
|
||||
if (required_key_item != 0 && player_key == 0 && gm_key != true && picklock_success != true) {
|
||||
LogError(
|
||||
"Teleport door [%s][dbid:%u][eqid:%u][dest:%s] used by [%s] in zone [%s] without proper key [id:%u]",
|
||||
door_name,
|
||||
database_id,
|
||||
door_id,
|
||||
destination_zone_name,
|
||||
sender->GetName(),
|
||||
zone->GetShortName(),
|
||||
required_key_item
|
||||
);
|
||||
sender->Message(Chat::Red, "This door requires a key for teleportation that you do not possess!");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If click destination is same zone and doesn't require a key
|
||||
|
||||
+16
-3
@@ -98,7 +98,7 @@ const char *QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_DUEL_LOSE",
|
||||
"EVENT_ENCOUNTER_LOAD",
|
||||
"EVENT_ENCOUNTER_UNLOAD",
|
||||
"EVENT_SAY",
|
||||
"EVENT_COMMAND",
|
||||
"EVENT_DROP_ITEM",
|
||||
"EVENT_DESTROY_ITEM",
|
||||
"EVENT_FEIGN_DEATH",
|
||||
@@ -119,6 +119,7 @@ const char *QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_DEATH_ZONE",
|
||||
"EVENT_USE_SKILL",
|
||||
"EVENT_COMBINE_VALIDATE",
|
||||
"EVENT_BOT_COMMAND"
|
||||
};
|
||||
|
||||
PerlembParser::PerlembParser() : perl(nullptr)
|
||||
@@ -1542,9 +1543,12 @@ void PerlembParser::ExportEventVariables(
|
||||
}
|
||||
|
||||
case EVENT_COMMAND: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "command", (sep.arg[0] + 1));
|
||||
ExportVar(package_name.c_str(), "args", (sep.argnum >= 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0"));
|
||||
ExportVar(package_name.c_str(), "data", objid);
|
||||
ExportVar(package_name.c_str(), "text", data);
|
||||
ExportVar(package_name.c_str(), "data", "0");
|
||||
ExportVar(package_name.c_str(), "langid", "0");
|
||||
ExportVar(package_name.c_str(), "langid", extradata);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1610,6 +1614,15 @@ void PerlembParser::ExportEventVariables(
|
||||
ExportVar(package_name.c_str(), "tradeskill_id", tradeskill_id.c_str());
|
||||
break;
|
||||
}
|
||||
case EVENT_BOT_COMMAND: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "bot_command", (sep.arg[0] + 1));
|
||||
ExportVar(package_name.c_str(), "args", (sep.argnum >= 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0"));
|
||||
ExportVar(package_name.c_str(), "data", objid);
|
||||
ExportVar(package_name.c_str(), "text", data);
|
||||
ExportVar(package_name.c_str(), "langid", extradata);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
|
||||
+6
-1
@@ -2654,7 +2654,12 @@ void EntityList::ScanCloseMobs(std::unordered_map<uint16, Mob *> &close_mobs, Mo
|
||||
}
|
||||
}
|
||||
|
||||
LogAIScanClose("Close List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName());
|
||||
LogAIScanClose(
|
||||
"[{}] Scanning Close List | list_size [{}] moving [{}]",
|
||||
scanning_mob->GetCleanName(),
|
||||
close_mobs.size(),
|
||||
scanning_mob->IsMoving() ? "true" : "false"
|
||||
);
|
||||
}
|
||||
|
||||
bool EntityList::RemoveMerc(uint16 delete_id)
|
||||
|
||||
@@ -87,6 +87,7 @@ typedef enum {
|
||||
EVENT_DEATH_ZONE,
|
||||
EVENT_USE_SKILL,
|
||||
EVENT_COMBINE_VALIDATE,
|
||||
EVENT_BOT_COMMAND,
|
||||
_LargestEventID
|
||||
} QuestEventID;
|
||||
|
||||
|
||||
+3
-1
@@ -124,7 +124,8 @@ const char *LuaEvents[_LargestEventID] = {
|
||||
"event_spawn_zone",
|
||||
"event_death_zone",
|
||||
"event_use_skill",
|
||||
"event_combine_validate"
|
||||
"event_combine_validate",
|
||||
"event_bot_command"
|
||||
};
|
||||
|
||||
extern Zone *zone;
|
||||
@@ -208,6 +209,7 @@ LuaParser::LuaParser() {
|
||||
PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet;
|
||||
PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill;
|
||||
PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate;
|
||||
PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command;
|
||||
|
||||
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
|
||||
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
|
||||
|
||||
@@ -539,6 +539,25 @@ void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client*
|
||||
lua_setfield(L, -2, "tradeskill_id");
|
||||
}
|
||||
|
||||
void handle_player_bot_command(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any>* extra_pointers) {
|
||||
Seperator sep(data.c_str(), ' ', 10, 100, true);
|
||||
std::string bot_command(sep.arg[0] + 1);
|
||||
lua_pushstring(L, bot_command.c_str());
|
||||
lua_setfield(L, -2, "bot_command");
|
||||
|
||||
luabind::adl::object args = luabind::newtable(L);
|
||||
int max_args = sep.GetMaxArgNum();
|
||||
for (int i = 1; i < max_args; ++i) {
|
||||
if (strlen(sep.arg[i]) > 0) {
|
||||
args[i] = std::string(sep.arg[i]);
|
||||
}
|
||||
}
|
||||
|
||||
args.push(L);
|
||||
lua_setfield(L, -2, "args");
|
||||
}
|
||||
|
||||
//Item
|
||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any> *extra_pointers) {
|
||||
|
||||
@@ -99,6 +99,8 @@ void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client
|
||||
std::vector<EQEmu::Any> *extra_pointers);
|
||||
void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any>* extra_pointers);
|
||||
void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any> *extra_pointers);
|
||||
|
||||
//Item
|
||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
||||
|
||||
@@ -459,6 +459,8 @@ Mob::Mob(
|
||||
#ifdef BOTS
|
||||
m_manual_follow = false;
|
||||
#endif
|
||||
|
||||
mob_scan_close.Trigger();
|
||||
}
|
||||
|
||||
Mob::~Mob()
|
||||
|
||||
@@ -705,12 +705,6 @@ bool NPC::Process()
|
||||
SpellProcess();
|
||||
|
||||
if (mob_scan_close.Check()) {
|
||||
LogAIScanClose(
|
||||
"is_moving [{}] npc [{}] timer [{}]",
|
||||
moving ? "true" : "false",
|
||||
GetCleanName(),
|
||||
mob_scan_close.GetDuration()
|
||||
);
|
||||
|
||||
entity_list.ScanCloseMobs(close_mobs, this);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user