Compare commits

...

19 Commits

Author SHA1 Message Date
Uleat 7d45de865d Merge branch 'master' of https://github.com/EQEmu/Server into teleport_door 2020-01-14 19:01:18 -05:00
Akkadius 331032f4f4 Push a scan when mobs first construct [skip ci] 2020-01-14 16:14:19 -06:00
Uleat 61790ef195 Wasn't quite dead... (Removed substring call from silent saylink parsing) 2020-01-13 19:01:42 -05:00
Uleat 5446234fd3 Modified teleport doors to not 'open' 2020-01-13 02:14:51 -05:00
Uleat 9bdb70b2f0 Fatality! (Fix for event_say parse events not observing the correct parser for their situation) 2020-01-13 01:47:39 -05:00
Uleat c438819ed6 Fix for command redirect of '#bot' using EVENT_COMMAND invocation 2020-01-12 22:40:05 -05:00
Uleat 7a791dda3c Merge branch 'master' of https://github.com/EQEmu/Server 2020-01-12 21:11:51 -05:00
Uleat 6366a3fa38 Fix for silent saylinks and EVENT_COMMAND calls 2020-01-12 21:11:43 -05:00
Akkadius 306a08b9ac Unify / streamline scanning logic calls, do a manual scan on enter and cleanup logging 2020-01-12 19:37:53 -06:00
Uleat 467f8d7867 Merge branch 'master' of https://github.com/EQEmu/Server 2020-01-12 01:33:17 -05:00
Uleat 6c2100a650 Fix for perl EVENT_COMMAND not exporting single arguments 2020-01-12 01:32:51 -05:00
Akkadius 9dacd0bd7a Remove aa_timers from character tables [skip ci] 2020-01-11 18:19:27 -06:00
Akkadius 3c6cdd0905 Update character_table_list.txt for now until replaced [skip ci] 2020-01-11 18:02:01 -06:00
Akkadius d1fb74ff5f Fix bot table 2020-01-10 02:59:52 -06:00
Akkadius f81b9d8244 Update worlddb.cpp 2020-01-10 02:54:34 -06:00
Akkadius b9e87abb3c Implement Character Soft Deletes 2020-01-10 02:54:29 -06:00
Uleat 451b0180c9 Merge pull request #943 from EQEmu/script_commands
Reworked EVENT_COMMAND handling and addressed script commands not present during help use
2020-01-07 21:43:32 -05:00
Akkadius 77ae4f0c3f Update missing aliases for when log compilation flag is turned off [skip ci] 2020-01-07 16:09:14 -06:00
Uleat 852d951b65 Reworked EVENT_COMMAND handling and addressed script commands not present during help use 2020-01-06 15:41:05 -05:00
26 changed files with 471 additions and 6009 deletions
+73 -50
View File
@@ -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
View File
@@ -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);
-1
View File
@@ -33,7 +33,6 @@ namespace DatabaseSchema {
static std::vector<std::string> GetPlayerTables()
{
std::vector<std::string> tables = {
"aa_timers",
"account",
"account_ip",
"account_flags",
+36
View File
@@ -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)
+1
View File
@@ -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
View File
@@ -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
+18 -2
View File
@@ -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"
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1
View File
@@ -87,6 +87,7 @@ typedef enum {
EVENT_DEATH_ZONE,
EVENT_USE_SKILL,
EVENT_COMBINE_VALIDATE,
EVENT_BOT_COMMAND,
_LargestEventID
} QuestEventID;
+3 -1
View File
@@ -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;
+19
View File
@@ -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) {
+2
View File
@@ -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,
+2
View File
@@ -459,6 +459,8 @@ Mob::Mob(
#ifdef BOTS
m_manual_follow = false;
#endif
mob_scan_close.Trigger();
}
Mob::~Mob()
-6
View File
@@ -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);