Merge pull request #2 from EQEmu/master

Get up to date
This commit is contained in:
Paul Coene 2020-02-05 14:40:12 -05:00 committed by GitHub
commit c419df52ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
130 changed files with 6362 additions and 8702 deletions

4
.gitignore vendored
View File

@ -33,4 +33,6 @@ perl/
*cbp *cbp
submodules/* submodules/*
cmake-build-debug/ cmake-build-debug/
.nfs.*

View File

@ -1,26 +1,18 @@
language: cpp language: cpp
compiler: gcc compiler: gcc
dist: trusty dist: bionic
before_install: addons:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test apt:
- sudo apt-get update -qq packages:
- mkdir $HOME/usr - libmysqlclient-dev
- export PATH="$HOME/usr/bin:$PATH" - libperl-dev
- wget https://cmake.org/files/v3.11/cmake-3.11.2-Linux-x86_64.sh - libboost-dev
- chmod +x cmake-3.11.2-Linux-x86_64.sh - liblua5.1-0-dev
- ./cmake-3.11.2-Linux-x86_64.sh --prefix=$HOME/usr --exclude-subdir --skip-license - zlib1g-dev
- uuid-dev
- libssl-dev
install:
- sudo apt-get install -qq g++-7
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 90
- sudo apt-get install libmysqlclient-dev
- sudo apt-get install libperl-dev
- sudo apt-get install libboost-dev
- sudo apt-get install liblua5.1-0-dev
- sudo apt-get install zlib1g-dev
- sudo apt-get install uuid-dev
- sudo apt-get install libssl-dev
script: script:
- cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LOGIN=ON - cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LOGIN=ON
- make -j2 - make -j2

View File

@ -122,6 +122,7 @@ SET(common_headers
cli/terminal_color.hpp cli/terminal_color.hpp
data_verification.h data_verification.h
database.h database.h
database_schema.h
dbcore.h dbcore.h
deity.h deity.h
emu_constants.h emu_constants.h

View File

@ -573,6 +573,20 @@ bool IsNonSpellFighterClass(uint8 class_id)
} }
} }
bool IsHybridClass(uint8 class_id)
{
switch (class_id) {
case PALADIN:
case RANGER:
case SHADOWKNIGHT:
case BARD:
case BEASTLORD:
return true;
default:
return false;
}
}
bool IsCasterClass(uint8 class_id) bool IsCasterClass(uint8 class_id)
{ {
switch (class_id) { switch (class_id) {

View File

@ -135,6 +135,7 @@ uint8 GetClassIDFromPlayerClassBit(uint32 player_class_bit);
bool IsFighterClass(uint8 class_id); bool IsFighterClass(uint8 class_id);
bool IsSpellFighterClass(uint8 class_id); bool IsSpellFighterClass(uint8 class_id);
bool IsNonSpellFighterClass(uint8 class_id); bool IsNonSpellFighterClass(uint8 class_id);
bool IsHybridClass(uint8 class_id);
bool IsCasterClass(uint8 class_id); bool IsCasterClass(uint8 class_id);
bool IsINTCasterClass(uint8 class_id); bool IsINTCasterClass(uint8 class_id);
bool IsWISCasterClass(uint8 class_id); bool IsWISCasterClass(uint8 class_id);

View File

@ -38,10 +38,6 @@ namespace EQEmuCommand {
void DisplayDebug(argh::parser &cmd) void DisplayDebug(argh::parser &cmd)
{ {
if (cmd[{"-d", "--debug"}]) { if (cmd[{"-d", "--debug"}]) {
std::cout << "Positional args:\n";
for (auto &pos_arg : cmd)
std::cout << '\t' << pos_arg << std::endl;
std::cout << "Positional args:\n"; std::cout << "Positional args:\n";
for (auto &pos_arg : cmd.pos_args()) for (auto &pos_arg : cmd.pos_args())
std::cout << '\t' << pos_arg << std::endl; std::cout << '\t' << pos_arg << std::endl;
@ -73,29 +69,37 @@ namespace EQEmuCommand {
{ {
bool arguments_filled = true; bool arguments_filled = true;
int index = 2;
for (auto &arg : arguments) { for (auto &arg : arguments) {
if (cmd(arg).str().empty()) { if (cmd(arg).str().empty() && cmd(index).str().empty()) {
arguments_filled = false; arguments_filled = false;
} }
index++;
} }
if (!arguments_filled || argc == 2) { if (!arguments_filled || argc == 2) {
std::string arguments_string; std::string arguments_string;
for (auto &arg : arguments) { for (auto &arg : arguments) {
arguments_string += " " + arg + "=*\n"; arguments_string += " " + arg;
} }
std::string options_string; std::string options_string;
for (auto &opt : options) { for (auto &opt : options) {
options_string += " " + opt + "\n"; options_string += " " + opt + "\n";
} }
std::cout << fmt::format( std::stringstream command_string;
"Command\n\n{0} \n\nArgs\n{1}\nOptions\n{2}",
argv[1], command_string <<
arguments_string, termcolor::colorize <<
options_string termcolor::yellow <<
) << std::endl; "\nCommand" <<
termcolor::reset << "\n\n" <<
termcolor::green << argv[1] << arguments_string << termcolor::reset << "\n" <<
termcolor::yellow << (!options_string.empty() ? "\nOptions\n" : "") <<
termcolor::reset << termcolor::cyan << options_string << termcolor::reset;
std::cout << command_string.str() << std::endl;
exit(1); exit(1);
} }
@ -123,10 +127,6 @@ namespace EQEmuCommand {
bool ran_command = false; bool ran_command = false;
for (auto &it: in_function_map) { for (auto &it: in_function_map) {
if (it.first == argv[1]) { if (it.first == argv[1]) {
std::cout << std::endl;
std::cout << "> " << termcolor::cyan << "Executing CLI Command" << termcolor::reset << std::endl;
std::cout << std::endl;
(it.second)(argc, argv, cmd, description); (it.second)(argc, argv, cmd, description);
ran_command = true; ran_command = true;
} }
@ -182,12 +182,13 @@ namespace EQEmuCommand {
} }
std::cout << std::endl; std::cout << std::endl;
}
else if (!ran_command) { std::exit(1);
std::cerr << "Unknown command [" << argv[1] << "] ! Try --help" << std::endl;
} }
exit(1); if (ran_command) {
std::exit(1);
}
} }
} }

View File

@ -45,6 +45,7 @@
#include "eq_packet_structs.h" #include "eq_packet_structs.h"
#include "extprofile.h" #include "extprofile.h"
#include "string_util.h" #include "string_util.h"
#include "database_schema.h"
extern Client client; extern Client client;
@ -124,7 +125,7 @@ uint32 Database::CheckLogin(const char* name, const char* password, const char *
//Get Banned IP Address List - Only return false if the incoming connection's IP address is not present in the banned_ips table. //Get Banned IP Address List - Only return false if the incoming connection's IP address is not present in the banned_ips table.
bool Database::CheckBannedIPs(const char* loginIP) bool Database::CheckBannedIPs(const char* loginIP)
{ {
std::string query = StringFormat("SELECT ip_address FROM Banned_IPs WHERE ip_address='%s'", loginIP); std::string query = StringFormat("SELECT ip_address FROM banned_ips WHERE ip_address='%s'", loginIP);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
@ -140,7 +141,7 @@ bool Database::CheckBannedIPs(const char* loginIP)
} }
bool Database::AddBannedIP(char* bannedIP, const char* notes) { bool Database::AddBannedIP(char* bannedIP, const char* notes) {
std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes); std::string query = StringFormat("INSERT into banned_ips SET ip_address='%s', notes='%s'", bannedIP, notes);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
if (!results.Success()) { if (!results.Success()) {
return false; return false;
@ -293,6 +294,37 @@ bool Database::SetAccountStatus(const char* name, int16 status) {
return true; return true;
} }
/**
* @param account_name
* @param status
* @return
*/
bool Database::SetAccountStatus(const std::string& account_name, int16 status)
{
LogInfo("Account [{}] is attempting to be set to status [{}]", account_name, status);
std::string query = fmt::format(
SQL(
UPDATE account SET status = {} WHERE name = '{}'
),
status,
account_name
);
auto results = QueryDatabase(query);
if (!results.Success()) {
return false;
}
if (results.RowsAffected() == 0) {
LogWarning("Account [{}] does not exist!", account_name);
return false;
}
return true;
}
/* This initially creates the character during character create */ /* This initially creates the character during character create */
bool Database::ReserveName(uint32 account_id, char* name) { bool Database::ReserveName(uint32 account_id, char* name) {
std::string query = StringFormat("SELECT `account_id`, `name` FROM `character_data` WHERE `name` = '%s'", name); std::string query = StringFormat("SELECT `account_id`, `name` FROM `character_data` WHERE `name` = '%s'", name);
@ -307,70 +339,81 @@ bool Database::ReserveName(uint32 account_id, char* name) {
query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name); query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name);
results = QueryDatabase(query); results = QueryDatabase(query);
if (!results.Success() || results.ErrorMessage() != ""){ return false; } if (!results.Success() || results.ErrorMessage() != ""){ return false; }
// Put character into the default guild if rule is being used.
int guild_id = RuleI(Character, DefaultGuild);
if (guild_id != 0) {
int character_id=results.LastInsertedID();
if (character_id > -1) {
query = StringFormat("INSERT INTO `guild_members` SET `char_id` = %i, `guild_id` = '%i'", character_id, guild_id);
results = QueryDatabase(query);
if (!results.Success() || results.ErrorMessage() != ""){
LogInfo("Could not put character [{}] into default Guild", name);
}
}
}
return true; return true;
} }
/* /**
Delete the character with the name "name" * @param character_name
returns false on failure, true otherwise * @return
*/ */
bool Database::DeleteCharacter(char *name) { bool Database::DeleteCharacter(char *character_name) {
uint32 charid = 0; uint32 character_id = 0;
if(!name || !strlen(name)) { if(!character_name || !strlen(character_name)) {
LogInfo("DeleteCharacter: request to delete without a name (empty char slot)"); LogInfo("DeleteCharacter: request to delete without a name (empty char slot)");
return false; 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'", character_name);
std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", name); auto results = QueryDatabase(query);
auto results = QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) {
for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); } character_id = atoi(row[0]);
if (charid <= 0){ }
LogError("Database::DeleteCharacter :: Character ({}) not found, stopping delete...", name);
if (character_id <= 0) {
LogError("DeleteCharacter | Invalid Character ID [{}]", character_name);
return false; return false;
} }
query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); QueryDatabase(query); std::string delete_type = "hard-deleted";
query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); QueryDatabase(query); if (RuleB(Character, SoftDeletes)) {
query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); delete_type = "soft-deleted";
query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); std::string query = fmt::format(
query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); SQL(
query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); QueryDatabase(query); UPDATE
query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); QueryDatabase(query); character_data
query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); QueryDatabase(query); SET
query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); QueryDatabase(query); name = SUBSTRING(CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), 1, 64),
query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); QueryDatabase(query); deleted_at = NOW()
query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); QueryDatabase(query); WHERE
query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); QueryDatabase(query); id = '{}'
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); character_id
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); 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); return true;
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); LogInfo("DeleteCharacter | Character [{}] ({}) is being [{}]", character_name, character_id, delete_type);
query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); QueryDatabase(query);
query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); QueryDatabase(query); for (const auto& iter : DatabaseSchema::GetCharacterTables()) {
query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); QueryDatabase(query); std::string table_name = iter.first;
query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); QueryDatabase(query); std::string character_id_column_name = iter.second;
query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); QueryDatabase(query);
query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); QueryDatabase(query); QueryDatabase(fmt::format("DELETE FROM {} WHERE {} = {}", table_name, character_id_column_name, character_id));
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);
#ifdef BOTS #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);
#endif
QueryDatabase(query); QueryDatabase(query);
#endif
return true; return true;
} }
@ -652,6 +695,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe
pp->RestTimer // " RestTimer) " pp->RestTimer // " RestTimer) "
); );
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
/* Save Bind Points */ /* Save Bind Points */
query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" 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), " " VALUES (%u, %u, %u, %f, %f, %f, %f, %i), "

View File

@ -107,7 +107,7 @@ public:
bool AddToNameFilter(const char* name); 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 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);
bool MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid); bool MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid);
bool MoveCharacterToZone(uint32 iCharID, const char* iZonename); bool MoveCharacterToZone(uint32 iCharID, const char* iZonename);
@ -120,7 +120,7 @@ public:
/* General Information Queries */ /* General Information Queries */
bool AddBannedIP(char* bannedIP, const char* notes); //Add IP address to the Banned_IPs table. bool AddBannedIP(char* bannedIP, const char* notes); //Add IP address to the banned_ips table.
bool AddGMIP(char* ip_address, char* name); bool AddGMIP(char* ip_address, char* name);
bool CheckBannedIPs(const char* loginIP); //Check incoming connection against banned IP table. bool CheckBannedIPs(const char* loginIP); //Check incoming connection against banned IP table.
bool CheckGMIPs(const char* loginIP, uint32 account_id); bool CheckGMIPs(const char* loginIP, uint32 account_id);
@ -179,6 +179,7 @@ public:
bool DeleteAccount(const char *name, const char* loginserver); bool DeleteAccount(const char *name, const char* loginserver);
bool GetLiveChar(uint32 account_id, char* cname); bool GetLiveChar(uint32 account_id, char* cname);
bool SetAccountStatus(const char* name, int16 status); bool SetAccountStatus(const char* name, int16 status);
bool SetAccountStatus(const std::string& account_name, int16 status);
bool SetLocalPassword(uint32 accid, const char* password); bool SetLocalPassword(uint32 accid, const char* password);
bool UpdateLiveChar(char* charname, uint32 account_id); bool UpdateLiveChar(char* charname, uint32 account_id);

342
common/database_schema.h Normal file
View File

@ -0,0 +1,342 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_DATABASE_SCHEMA_H
#define EQEMU_DATABASE_SCHEMA_H
#include <vector>
#include <map>
namespace DatabaseSchema {
/**
* Character-specific tables
*
* Does not included related meta-data tables such as 'guilds', 'accounts'
* @return
*/
static std::map<std::string, std::string> GetCharacterTables()
{
return {
{"adventure_stats", "player_id"},
{"buyer", "charid"},
{"char_recipe_list", "char_id"},
{"character_activities", "charid"},
{"character_alt_currency", "char_id"},
{"character_alternate_abilities", "id"},
{"character_auras", "id"},
{"character_bandolier", "id"},
{"character_bind", "id"},
{"character_buffs", "character_id"},
{"character_corpses", "id"},
{"character_currency", "id"},
{"character_data", "id"},
{"character_disciplines", "id"},
{"character_enabledtasks", "charid"},
{"character_inspect_messages", "id"},
{"character_item_recast", "id"},
{"character_languages", "id"},
{"character_leadership_abilities", "id"},
{"character_material", "id"},
{"character_memmed_spells", "id"},
{"character_pet_buffs", "char_id"},
{"character_pet_info", "char_id"},
{"character_pet_inventory", "char_id"},
{"character_potionbelt", "id"},
{"character_skills", "id"},
{"character_spells", "id"},
{"character_tasks", "charid"},
{"character_tribute", "id"},
{"completed_tasks", "charid"},
{"data_buckets", "id"},
{"faction_values", "char_id"},
{"friends", "charid"},
{"guild_members", "char_id"},
{"guilds", "id"},
{"instance_list_player", "id"},
{"inventory", "charid"},
{"inventory_snapshots", "charid"},
{"keyring", "char_id"},
{"mail", "charid"},
{"player_titlesets", "char_id"},
{"quest_globals", "charid"},
{"timers", "char_id"},
{"titles", "char_id"},
{"trader", "char_id"},
{"zone_flags", "charID"}
};
}
/**
* Gets all player and meta-data tables
*
* @return
*/
static std::vector<std::string> GetPlayerTables()
{
return {
"account",
"account_ip",
"account_flags",
"account_rewards",
"adventure_details",
"adventure_stats",
"buyer",
"char_recipe_list",
"character_activities",
"character_alt_currency",
"character_alternate_abilities",
"character_auras",
"character_bandolier",
"character_bind",
"character_buffs",
"character_corpse_items",
"character_corpses",
"character_currency",
"character_data",
"character_disciplines",
"character_enabledtasks",
"character_inspect_messages",
"character_item_recast",
"character_languages",
"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",
"keyring",
"mail",
"player_titlesets",
"quest_globals",
"sharedbank",
"timers",
"titles",
"trader",
"trader_audit",
"zone_flags"
};
}
/**
* Gets content tables
*
* @return
*/
static std::vector<std::string> GetContentTables()
{
return {
"aa_ability",
"aa_actions",
"aa_effects",
"aa_rank_effects",
"aa_rank_prereqs",
"aa_ranks",
"aa_required_level_cost",
"adventure_template",
"adventure_template_entry",
"adventure_template_entry_flavor",
"altadv_vars",
"alternate_currency",
"auras",
"base_data",
"blocked_spells",
"books",
"char_create_combinations",
"char_create_point_allocations",
"class_skill",
"damageshieldtypes",
"doors",
"faction_base_data",
"faction_list",
"faction_list_mod",
"fear_hints",
"fishing",
"forage",
"global_loot",
"goallists",
"graveyard",
"grid",
"grid_entries",
"ground_spawns",
"horses",
"instance_list",
"items",
"ldon_trap_entries",
"ldon_trap_templates",
"lootdrop",
"lootdrop_entries",
"loottable",
"loottable_entries",
"merchantlist",
"npc_emotes",
"npc_faction",
"npc_faction_entries",
"npc_scale_global_base",
"npc_spells",
"npc_spells_effects",
"npc_spells_effects_entries",
"npc_spells_entries",
"npc_types",
"npc_types_metadata",
"npc_types_tint",
"object",
"pets",
"pets_equipmentset",
"pets_equipmentset_entries",
"proximities",
"races",
"skill_caps",
"spawn2",
"spawn_condition_values",
"spawn_conditions",
"spawn_events",
"spawnentry",
"spawngroup",
"spells_new",
"start_zones",
"starting_items",
"task_activities",
"tasks",
"tasksets",
"titles",
"tradeskill_recipe",
"tradeskill_recipe_entries",
"traps",
"tribute_levels",
"tributes",
"veteran_reward_templates",
"zone",
"zone_points",
"zone_server",
"zoneserver_auth",
};
}
/**
* Gets server tables
*
* @return
*/
static std::vector<std::string> GetServerTables()
{
return {
"banned_ips",
"bugs",
"bug_reports",
"command_settings",
"db_str",
"discovered_items",
"eqtime",
"eventlog",
"gm_ips",
"hackers",
"ip_exemptions",
"launcher",
"launcher_zones",
"level_exp_mods",
"logsys_categories",
"name_filter",
"perl_event_export_settings",
"petitions",
"profanity_list",
"reports",
"rule_sets",
"rule_values",
"saylink",
"variables",
};
}
/**
* Gets state tables
* Tables that keep track of server state
*
* @return
*/
static std::vector<std::string> GetStateTables()
{
return {
"adventure_members",
"chatchannels",
"group_id",
"group_leaders",
"item_tick",
"lfguild",
"merchantlist_temp",
"object_contents",
"raid_details",
"raid_leaders",
"raid_members",
"respawn_times",
"spell_buckets",
"spell_globals",
};
}
/**
* Gets login tables
*
* @return
*/
static std::vector<std::string> GetLoginTables()
{
return {
"login_accounts",
"login_api_tokens",
"login_server_admins",
"login_server_list_types",
"login_world_servers",
};
}
/**
* Gets login tables
*
* @return
*/
static std::vector<std::string> GetVersionTables()
{
return {
"db_version",
"inventory_versions",
};
}
}
#endif //EQEMU_DATABASE_SCHEMA_H

View File

@ -115,14 +115,14 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
auto errorBuffer = new char[MYSQL_ERRMSG_SIZE]; auto errorBuffer = new char[MYSQL_ERRMSG_SIZE];
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
/* Implement Logging at the Root */ /**
* Error logging
*/
if (mysql_errno(&mysql) > 0 && strlen(query) > 0) { if (mysql_errno(&mysql) > 0 && strlen(query) > 0) {
if (LogSys.log_settings[Logs::MySQLError].is_category_enabled == 1) LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(&mysql), mysql_error(&mysql), query);
Log(Logs::General, Logs::MySQLError, "%i: %s \n %s", mysql_errno(&mysql), mysql_error(&mysql), query);
} }
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql), errorBuffer); return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql), errorBuffer);
} }
// successful query. get results. // successful query. get results.
@ -143,10 +143,8 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) { if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
if ((strncasecmp(query, "select", 6) == 0)) { if ((strncasecmp(query, "select", 6) == 0)) {
LogF( LogMySQLQuery(
Logs::General, "{0} ({1} row{2} returned) ({3}s)",
Logs::MySQLQuery,
"{0} ({1} row{2} returned) ({3}ms)",
query, query,
requestResult.RowCount(), requestResult.RowCount(),
requestResult.RowCount() == 1 ? "" : "s", requestResult.RowCount() == 1 ? "" : "s",
@ -154,10 +152,8 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
); );
} }
else { else {
LogF( LogMySQLQuery(
Logs::General, "{0} ({1} row{2} affected) ({3}s)",
Logs::MySQLQuery,
"{0} ({1} row{2} affected) ({3}ms)",
query, query,
requestResult.RowsAffected(), requestResult.RowsAffected(),
requestResult.RowsAffected() == 1 ? "" : "s", requestResult.RowsAffected() == 1 ? "" : "s",

View File

@ -317,6 +317,15 @@ namespace EQEmu
QuestControlGrid = -1 QuestControlGrid = -1
}; };
namespace consent {
enum eConsentType : uint8 {
Normal = 0,
Group,
Raid,
Guild
};
}; // namespace consent
} /*EQEmu*/ } /*EQEmu*/
#endif /*COMMON_EMU_CONSTANTS_H*/ #endif /*COMMON_EMU_CONSTANTS_H*/

View File

@ -35,6 +35,7 @@ static const uint32 MAX_MERC = 100;
static const uint32 MAX_MERC_GRADES = 10; static const uint32 MAX_MERC_GRADES = 10;
static const uint32 MAX_MERC_STANCES = 10; static const uint32 MAX_MERC_STANCES = 10;
static const uint32 BLOCKED_BUFF_COUNT = 20; static const uint32 BLOCKED_BUFF_COUNT = 20;
static const uint32 QUESTREWARD_COUNT = 8;
/* /*
@ -2180,14 +2181,7 @@ struct QuestReward_Struct
/*024*/ uint32 silver; // Gives silver to the client /*024*/ uint32 silver; // Gives silver to the client
/*028*/ uint32 gold; // Gives gold to the client /*028*/ uint32 gold; // Gives gold to the client
/*032*/ uint32 platinum; // Gives platinum to the client /*032*/ uint32 platinum; // Gives platinum to the client
/*036*/ uint32 item_id; /*036*/ int32 item_id[QUESTREWARD_COUNT]; // -1 for nothing
/*040*/ uint32 unknown040;
/*044*/ uint32 unknown044;
/*048*/ uint32 unknown048;
/*052*/ uint32 unknown052;
/*056*/ uint32 unknown056;
/*060*/ uint32 unknown060;
/*064*/ uint32 unknown064;
/*068*/ /*068*/
}; };

View File

@ -165,7 +165,7 @@ class EQEmuConfig
fconfig >> _config->_root; fconfig >> _config->_root;
_config->parse_config(); _config->parse_config();
} }
catch (std::exception) { catch (std::exception &) {
return false; return false;
} }
return true; return true;

View File

@ -579,3 +579,25 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name)
); );
} }
} }
/**
* Silence console logging
*/
void EQEmuLogSys::SilenceConsoleLogging()
{
for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) {
log_settings[log_index].log_to_console = 0;
log_settings[log_index].is_category_enabled = 0;
}
}
/**
* Enables console logging
*/
void EQEmuLogSys::EnableConsoleLogging()
{
for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) {
log_settings[log_index].log_to_console = Logs::General;
log_settings[log_index].is_category_enabled = 1;
}
}

View File

@ -107,6 +107,13 @@ namespace Logs {
Emergency, Emergency,
Alert, Alert,
Notice, Notice,
AIScanClose,
AIYellForHelp,
AICastBeneficialClose,
AoeCast,
EntityManagement,
Flee,
Aura,
MaxCategoryID /* Don't Remove this */ MaxCategoryID /* Don't Remove this */
}; };
@ -172,7 +179,14 @@ namespace Logs {
"Critical", "Critical",
"Emergency", "Emergency",
"Alert", "Alert",
"Notice" "Notice",
"AI Scan Close",
"AI Yell For Help",
"AI Cast Beneficial Close",
"AOE Cast",
"Entity Management",
"Flee",
"Aura",
}; };
} }
@ -279,6 +293,16 @@ public:
*/ */
void SetConsoleHandler(std::function<void(uint16 debug_level, uint16 log_type, const std::string&)> f) { on_log_console_hook = f; } void SetConsoleHandler(std::function<void(uint16 debug_level, uint16 log_type, const std::string&)> f) { on_log_console_hook = f; }
/**
* Silence console logging
*/
void SilenceConsoleLogging();
/**
* Turn on all console logging
*/
void EnableConsoleLogging();
private: private:
/** /**

View File

@ -491,6 +491,76 @@
OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0) } while (0)
#define LogAIScanClose(message, ...) do {\
if (LogSys.log_settings[Logs::AIScanClose].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::AIScanClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAIScanCloseDetail(message, ...) do {\
if (LogSys.log_settings[Logs::AIScanClose].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::AIScanClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAIYellForHelp(message, ...) do {\
if (LogSys.log_settings[Logs::AIYellForHelp].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::AIYellForHelp, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAIYellForHelpDetail(message, ...) do {\
if (LogSys.log_settings[Logs::AIYellForHelp].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::AIYellForHelp, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAICastBeneficialClose(message, ...) do {\
if (LogSys.log_settings[Logs::AICastBeneficialClose].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAICastBeneficialCloseDetail(message, ...) do {\
if (LogSys.log_settings[Logs::AICastBeneficialClose].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAoeCast(message, ...) do {\
if (LogSys.log_settings[Logs::AoeCast].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::AoeCast, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAoeCastDetail(message, ...) do {\
if (LogSys.log_settings[Logs::AoeCast].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::AoeCast, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogEntityManagement(message, ...) do {\
if (LogSys.log_settings[Logs::EntityManagement].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::EntityManagement, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogEntityManagementDetail(message, ...) do {\
if (LogSys.log_settings[Logs::EntityManagement].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::EntityManagement, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogFlee(message, ...) do {\
if (LogSys.log_settings[Logs::Flee].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Flee, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogFleeDetail(message, ...) do {\
if (LogSys.log_settings[Logs::Flee].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::Flee, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAura(message, ...) do {\
if (LogSys.log_settings[Logs::Aura].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Aura, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAuraDetail(message, ...) do {\
if (LogSys.log_settings[Logs::Aura].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::Aura, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\ #define Log(debug_level, log_category, message, ...) do {\
if (LogSys.log_settings[log_category].is_category_enabled == 1)\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
@ -782,6 +852,48 @@
#define LogStatusDetail(message, ...) do {\ #define LogStatusDetail(message, ...) do {\
} while (0) } 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 LogAura(message, ...) do {\
} while (0)
#define LogAuraDetail(message, ...) do {\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\ #define Log(debug_level, log_category, message, ...) do {\
} while (0) } while (0)

View File

@ -21,6 +21,10 @@ namespace EQ
uv_run(&m_loop, UV_RUN_NOWAIT); uv_run(&m_loop, UV_RUN_NOWAIT);
} }
void Run() {
uv_run(&m_loop, UV_RUN_DEFAULT);
}
uv_loop_t* Handle() { return &m_loop; } uv_loop_t* Handle() { return &m_loop; }
private: private:

View File

@ -5277,7 +5277,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings)
{ {
//! [StreamWriterBuilderDefaults] //! [StreamWriterBuilderDefaults]
(*settings)["commentStyle"] = "All"; (*settings)["commentStyle"] = "All";
(*settings)["indentation"] = "\t"; (*settings)["indentation"] = " ";
(*settings)["enableYAMLCompatibility"] = false; (*settings)["enableYAMLCompatibility"] = false;
(*settings)["dropNullPlaceholders"] = false; (*settings)["dropNullPlaceholders"] = false;
(*settings)["useSpecialFloats"] = false; (*settings)["useSpecialFloats"] = false;

View File

@ -32,7 +32,7 @@ EQ::JsonConfigFile EQ::JsonConfigFile::Load(
try { try {
ifs >> ret.m_root; ifs >> ret.m_root;
} }
catch (std::exception) { catch (std::exception &) {
return ret; return ret;
} }
@ -81,7 +81,7 @@ std::string EQ::JsonConfigFile::GetVariableString(
return m_root[title][parameter].asString(); return m_root[title][parameter].asString();
} }
} }
catch (std::exception) { catch (std::exception &) {
return default_value; return default_value;
} }
@ -105,7 +105,7 @@ int EQ::JsonConfigFile::GetVariableInt(
return m_root[title][parameter].asInt(); return m_root[title][parameter].asInt();
} }
} }
catch (std::exception) { catch (std::exception &) {
return default_value; return default_value;
} }
@ -129,7 +129,7 @@ bool EQ::JsonConfigFile::GetVariableBool(
return m_root[title][parameter].asBool(); return m_root[title][parameter].asBool();
} }
} }
catch (std::exception) { catch (std::exception &) {
return default_value; return default_value;
} }
@ -153,7 +153,7 @@ double EQ::JsonConfigFile::GetVariableDouble(
return m_root[title][parameter].asDouble(); return m_root[title][parameter].asDouble();
} }
} }
catch (std::exception) { catch (std::exception &) {
return default_value; return default_value;
} }

View File

@ -278,12 +278,6 @@ void LinkedListIterator<TYPE>::Replace(const TYPE& new_data)
template<class TYPE> template<class TYPE>
void LinkedListIterator<TYPE>::Reset() void LinkedListIterator<TYPE>::Reset()
{ {
if (!(&list))
{
current_element=0;
return;
}
if (dir == FORWARD) if (dir == FORWARD)
{ {
current_element = list.first; current_element = list.first;

View File

@ -19,9 +19,6 @@
#include "types.h" #include "types.h"
#include <cstring> #include <cstring>
#define ENC(c) (((c) & 0x3f) + ' ')
#define DEC(c) (((c) - ' ') & 0x3f)
std::map<int,std::string> DBFieldNames; std::map<int,std::string> DBFieldNames;
#ifndef WIN32 #ifndef WIN32
@ -333,64 +330,6 @@ void LoadItemDBFieldNames() {
DBFieldNames[113]="unknown115"; // ? (end quote) DBFieldNames[113]="unknown115"; // ? (end quote)
} }
void encode_length(unsigned long length, char *out)
{
char buf[4];
memcpy(buf,&length,sizeof(unsigned long));
encode_chunk(buf,3,out);
}
unsigned long encode(char *in, unsigned long length, char *out)
{
unsigned long used=0,len=0;
while(used<length) {
encode_chunk(in+used,length-used,out+len);
used+=3;
len+=4;
}
*(out+len)=0;
return len;
}
unsigned long decode_length(char *in)
{
int length;
char buf[4];
decode_chunk(in,&buf[0]);
buf[3]=0;
memcpy(&length,buf,sizeof(unsigned long));
return length;
}
void decode(char *in, char *out)
{
char *ptr=in;
char *outptr=out;
while(*ptr) {
decode_chunk(ptr,outptr);
ptr+=4;
outptr+=3;
}
*outptr=0;
}
void encode_chunk(char *in, int len, char *out)
{
*out=ENC(in[0] >> 2);
*(out+1)=ENC((in[0] << 4)|(((len<2 ? 0 : in[1]) >> 4) & 0xF));
*(out+2)=ENC(((len<2 ? 0 : in[1]) << 2)|(((len<3 ? 0 : in[2]) >> 6) & 0x3));
*(out+3)=ENC((len<3 ? 0 : in[2]));
}
void decode_chunk(char *in, char *out)
{
*out = DEC(*in) << 2 | DEC(in[1]) >> 4;
*(out+1) = DEC(in[1]) << 4 | DEC(in[2]) >> 2;
*(out+2) = DEC(in[2]) << 6 | DEC(in[3]);
}
void dump_message_column(unsigned char *buffer, unsigned long length, std::string leader, FILE *to) void dump_message_column(unsigned char *buffer, unsigned long length, std::string leader, FILE *to)
{ {
unsigned long i,j; unsigned long i,j;

View File

@ -17,13 +17,6 @@ int Tokenize(std::string s, std::map<int,std::string> & tokens, char delim='|');
void LoadItemDBFieldNames(); void LoadItemDBFieldNames();
void encode_length(unsigned long length, char *out);
unsigned long decode_length(char *in);
unsigned long encode(char *in, unsigned long length, char *out);
void decode(char *in, char *out);
void encode_chunk(char *in, int len, char *out);
void decode_chunk(char *in, char *out);
#ifndef WIN32 #ifndef WIN32
int print_stacktrace(); int print_stacktrace();
#endif #endif

View File

@ -116,17 +116,21 @@ bool EQ::Net::ConsoleServerConnection::SendChannelMessage(const ServerChannelMes
} }
switch (scm->chan_num) { switch (scm->chan_num) {
if (RuleB(Chat, ServerWideAuction)) { case 4: {
case 4: { if (RuleB(Chat, ServerWideAuction)) {
QueueMessage(fmt::format("{0} auctions, '{1}'", scm->from, scm->message)); QueueMessage(fmt::format("{0} auctions, '{1}'", scm->from, scm->message));
break; break;
} else { // I think we want default action in this case?
return false;
} }
} }
if (RuleB(Chat, ServerWideOOC)) { case 5: {
case 5: { if (RuleB(Chat, ServerWideOOC)) {
QueueMessage(fmt::format("{0} says ooc, '{1}'", scm->from, scm->message)); QueueMessage(fmt::format("{0} says ooc, '{1}'", scm->from, scm->message));
break; break;
} else { // I think we want default action in this case?
return false;
} }
} }

View File

@ -399,7 +399,7 @@ void EQ::Net::DaybreakConnection::Process()
ProcessQueue(); ProcessQueue();
} }
catch (std::exception ex) { catch (std::exception &ex) {
if (m_owner->m_on_error_message) { if (m_owner->m_on_error_message) {
m_owner->m_on_error_message(fmt::format("Error processing connection: {0}", ex.what())); m_owner->m_on_error_message(fmt::format("Error processing connection: {0}", ex.what()));
} }

View File

@ -89,9 +89,9 @@ namespace EQ {
public: public:
StaticPacket(void *data, size_t size) { m_data = data; m_data_length = size; m_max_data_length = size; } StaticPacket(void *data, size_t size) { m_data = data; m_data_length = size; m_max_data_length = size; }
virtual ~StaticPacket() { } virtual ~StaticPacket() { }
StaticPacket(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; } StaticPacket(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; m_max_data_length = o.m_max_data_length; }
StaticPacket& operator=(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; return *this; } StaticPacket& operator=(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; return *this; }
StaticPacket(StaticPacket &&o) { m_data = o.m_data; m_data_length = o.m_data_length; } StaticPacket(StaticPacket &&o) noexcept { m_data = o.m_data; m_data_length = o.m_data_length; }
virtual const void *Data() const { return m_data; } virtual const void *Data() const { return m_data; }
virtual void *Data() { return m_data; } virtual void *Data() { return m_data; }
@ -112,7 +112,7 @@ namespace EQ {
public: public:
DynamicPacket() { } DynamicPacket() { }
virtual ~DynamicPacket() { } virtual ~DynamicPacket() { }
DynamicPacket(DynamicPacket &&o) { m_data = std::move(o.m_data); } DynamicPacket(DynamicPacket &&o) noexcept { m_data = std::move(o.m_data); }
DynamicPacket(const DynamicPacket &o) { m_data = o.m_data; } DynamicPacket(const DynamicPacket &o) { m_data = o.m_data; }
DynamicPacket& operator=(const DynamicPacket &o) { m_data = o.m_data; return *this; } DynamicPacket& operator=(const DynamicPacket &o) { m_data = o.m_data; return *this; }
@ -127,4 +127,4 @@ namespace EQ {
std::vector<char> m_data; std::vector<char> m_data;
}; };
} }
} }

View File

@ -61,7 +61,7 @@ EQ::Net::WebsocketServer::WebsocketServer(const std::string &addr, int port)
auto &connection = iter->second; auto &connection = iter->second;
connection->GetWebsocketConnection()->ping("keepalive"); connection->GetWebsocketConnection()->ping("keepalive");
} }
catch (std::exception) { catch (std::exception &) {
iter->second->GetTCPConnection()->Disconnect(); iter->second->GetTCPConnection()->Disconnect();
} }
@ -157,7 +157,7 @@ void EQ::Net::WebsocketServer::DispatchEvent(WebsocketSubscriptionEvent evt, Jso
} }
} }
} }
catch (std::exception) { catch (std::exception &) {
} }
} }
@ -190,7 +190,7 @@ Json::Value EQ::Net::WebsocketServer::Login(WebsocketServerConnection *connectio
return ret; return ret;
} }
catch (std::exception) { catch (std::exception &) {
throw WebsocketException("Unable to process login request"); throw WebsocketException("Unable to process login request");
} }
} }
@ -212,7 +212,7 @@ Json::Value EQ::Net::WebsocketServer::Subscribe(WebsocketServerConnection *conne
catch (WebsocketException &ex) { catch (WebsocketException &ex) {
throw ex; throw ex;
} }
catch (std::exception) { catch (std::exception &) {
throw WebsocketException("Unable to process unsubscribe request"); throw WebsocketException("Unable to process unsubscribe request");
} }
} }
@ -234,7 +234,7 @@ Json::Value EQ::Net::WebsocketServer::Unsubscribe(WebsocketServerConnection *con
catch (WebsocketException &ex) { catch (WebsocketException &ex) {
throw ex; throw ex;
} }
catch (std::exception) { catch (std::exception &) {
throw WebsocketException("Unable to process unsubscribe request"); throw WebsocketException("Unable to process unsubscribe request");
} }
} }

View File

@ -37,6 +37,7 @@
#include <sstream> #include <sstream>
#include <numeric> #include <numeric>
#include <cassert> #include <cassert>
#include <cinttypes>
namespace RoF2 namespace RoF2
@ -3548,7 +3549,7 @@ namespace RoF2
{ {
eq->items[i].Unknown18 = 0; eq->items[i].Unknown18 = 0;
if (i < 80) { if (i < 80) {
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", emu->SerialNumber[i]); snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016" PRId64, emu->SerialNumber[i]);
eq->ItemCost[i] = emu->ItemCost[i]; eq->ItemCost[i] = emu->ItemCost[i];
} }
else { else {

View File

@ -190,10 +190,6 @@ bool PersistentTimer::Clear(Database *db) {
/* This function checks if the timer triggered */ /* This function checks if the timer triggered */
bool PersistentTimer::Expired(Database *db, bool iReset) { bool PersistentTimer::Expired(Database *db, bool iReset) {
if (this == nullptr) {
LogError("Null timer during ->Check()!?\n");
return(true);
}
uint32 current_time = get_current_time(); uint32 current_time = get_current_time();
if (current_time-start_time >= timer_time) { if (current_time-start_time >= timer_time) {
if (enabled && iReset) { if (enabled && iReset) {

View File

@ -157,6 +157,9 @@ RULE_BOOL(Character, OPClientUpdateVisualDebug, false, "Shows a pulse and forwar
RULE_BOOL(Character, AllowCrossClassTrainers, false, "") RULE_BOOL(Character, AllowCrossClassTrainers, false, "")
RULE_BOOL(Character, PetsUseReagents, true, "Pets use reagent on spells") RULE_BOOL(Character, PetsUseReagents, true, "Pets use reagent on spells")
RULE_BOOL(Character, DismountWater, true, "Dismount horses when entering water") 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_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Mercs) RULE_CATEGORY(Mercs)
@ -211,7 +214,7 @@ RULE_CATEGORY_END()
RULE_CATEGORY(World) RULE_CATEGORY(World)
RULE_INT(World, ZoneAutobootTimeoutMS, 60000, "") RULE_INT(World, ZoneAutobootTimeoutMS, 60000, "")
RULE_INT(World, ClientKeepaliveTimeoutMS, 65000, "") RULE_INT(World, ClientKeepaliveTimeoutMS, 65000, "")
RULE_BOOL(World, UseBannedIPsTable, false, "Toggle whether or not to check incoming client connections against the Banned_IPs table. Set this value to false to disable this feature") RULE_BOOL(World, UseBannedIPsTable, false, "Toggle whether or not to check incoming client connections against the banned_ips table. Set this value to false to disable this feature")
RULE_BOOL(World, EnableTutorialButton, true, "") RULE_BOOL(World, EnableTutorialButton, true, "")
RULE_BOOL(World, EnableReturnHomeButton, true, "") RULE_BOOL(World, EnableReturnHomeButton, true, "")
RULE_INT(World, MaxLevelForTutorial, 10, "") RULE_INT(World, MaxLevelForTutorial, 10, "")
@ -292,6 +295,7 @@ RULE_BOOL(Pathing, Find, true, "Enable pathing for FindPerson requests from the
RULE_BOOL(Pathing, Fear, true, "Enable pathing for fear") RULE_BOOL(Pathing, Fear, true, "Enable pathing for fear")
RULE_REAL(Pathing, NavmeshStepSize, 100.0f, "") RULE_REAL(Pathing, NavmeshStepSize, 100.0f, "")
RULE_REAL(Pathing, ShortMovementUpdateRange, 130.0f, "") RULE_REAL(Pathing, ShortMovementUpdateRange, 130.0f, "")
RULE_INT(Pathing, MaxNavmeshNodes, 4092, "Max navmesh nodes in a traversable path")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Watermap) RULE_CATEGORY(Watermap)
@ -517,7 +521,7 @@ RULE_INT(NPC, NPCToNPCAggroTimerMin, 500, "")
RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "") RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "")
RULE_BOOL(NPC, UseClassAsLastName, true, "Uses class archetype as LastName for npcs with none") RULE_BOOL(NPC, UseClassAsLastName, true, "Uses class archetype as LastName for npcs with none")
RULE_BOOL(NPC, NewLevelScaling, true, "Better level scaling, use old if new formulas would break your server") RULE_BOOL(NPC, NewLevelScaling, true, "Better level scaling, use old if new formulas would break your server")
RULE_INT(NPC, NPCGatePercent, 5, "% at which the NPC Will attempt to gate at") RULE_INT(NPC, NPCGatePercent, 20, "% at which the NPC Will attempt to gate at")
RULE_BOOL(NPC, NPCGateNearBind, false, "Will NPC attempt to gate when near bind location?") RULE_BOOL(NPC, NPCGateNearBind, false, "Will NPC attempt to gate when near bind location?")
RULE_INT(NPC, NPCGateDistanceBind, 75, "Distance from bind before NPC will attempt to gate") RULE_INT(NPC, NPCGateDistanceBind, 75, "Distance from bind before NPC will attempt to gate")
RULE_BOOL(NPC, NPCHealOnGate, true, "Will the NPC Heal on Gate") RULE_BOOL(NPC, NPCHealOnGate, true, "Will the NPC Heal on Gate")
@ -566,13 +570,13 @@ RULE_INT(Range, MobPositionUpdates, 600, "")
RULE_INT(Range, ClientPositionUpdates, 300, "") RULE_INT(Range, ClientPositionUpdates, 300, "")
RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "") RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "")
RULE_INT(Range, CriticalDamage, 80, "") RULE_INT(Range, CriticalDamage, 80, "")
RULE_INT(Range, ClientNPCScan, 300, "") RULE_INT(Range, MobCloseScanDistance, 600, "")
RULE_CATEGORY_END() RULE_CATEGORY_END()
#ifdef BOTS #ifdef BOTS
RULE_CATEGORY(Bots) RULE_CATEGORY(Bots)
RULE_INT(Bots, AAExpansion, 8, "Bots get AAs through this expansion") RULE_INT(Bots, BotExpansionSettings, 16383, "Sets the expansion settings for bot use. Defaults to all expansions enabled up to TSS")
RULE_BOOL(Bots, AllowCamelCaseNames, false, "Allows the use of 'MyBot' type names") RULE_BOOL(Bots, AllowCamelCaseNames, false, "Allows the use of 'MyBot' type names")
RULE_INT(Bots, CommandSpellRank, 1, "Filters bot command spells by rank (1, 2 and 3 are valid filters - any other number allows all ranks)") RULE_INT(Bots, CommandSpellRank, 1, "Filters bot command spells by rank (1, 2 and 3 are valid filters - any other number allows all ranks)")
RULE_INT(Bots, CreationLimit, 150, "Number of bots that each account can create") RULE_INT(Bots, CreationLimit, 150, "Number of bots that each account can create")
@ -599,6 +603,9 @@ RULE_INT(Bots, AllowedGenders, 0x3, "Bitmask of allowed bot genders")
RULE_BOOL(Bots, AllowOwnerOptionAltCombat, true, "When option is enabled, bots will use an auto-/shared-aggro combat model") RULE_BOOL(Bots, AllowOwnerOptionAltCombat, true, "When option is enabled, bots will use an auto-/shared-aggro combat model")
RULE_BOOL(Bots, AllowOwnerOptionAutoDefend, true, "When option is enabled, bots will defend their owner on enemy aggro") RULE_BOOL(Bots, AllowOwnerOptionAutoDefend, true, "When option is enabled, bots will defend their owner on enemy aggro")
RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel from leash owner before being pulled back (squared value)") RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel from leash owner before being pulled back (squared value)")
RULE_BOOL(Bots, AllowApplyPoisonCommand, true, "Allows the use of the bot command 'applypoison'")
RULE_BOOL(Bots, AllowApplyPotionCommand, true, "Allows the use of the bot command 'applypotion'")
RULE_BOOL(Bots, RestrictApplyPotionToRogue, true, "Restricts the bot command 'applypotion' to rogue-usable potions (i.e., poisons)")
RULE_CATEGORY_END() RULE_CATEGORY_END()
#endif #endif

View File

@ -262,9 +262,6 @@ public:
} }
ServerPacket* Copy() { ServerPacket* Copy() {
if (this == 0) {
return 0;
}
ServerPacket* ret = new ServerPacket(this->opcode, this->size); ServerPacket* ret = new ServerPacket(this->opcode, this->size);
if (this->size) if (this->size)
memcpy(ret->pBuffer, this->pBuffer, this->size); memcpy(ret->pBuffer, this->pBuffer, this->size);
@ -869,10 +866,12 @@ struct SpawnPlayerCorpse_Struct {
struct ServerOP_Consent_Struct { struct ServerOP_Consent_Struct {
char grantname[64]; char grantname[64];
char ownername[64]; char ownername[64];
char zonename[32];
uint8 permission; uint8 permission;
uint32 zone_id; uint32 zone_id;
uint16 instance_id; uint16 instance_id;
uint32 message_string_id; uint8 consent_type; // 0 = normal, 1 = group, 2 = raid, 3 = guild
uint32 consent_id;
}; };
struct ReloadTasks_Struct { struct ReloadTasks_Struct {

View File

@ -87,8 +87,9 @@
bool IsTargetableAESpell(uint16 spell_id) bool IsTargetableAESpell(uint16 spell_id)
{ {
if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) {
return true; return true;
}
return false; return false;
} }

View File

@ -607,7 +607,7 @@ typedef enum {
#define SE_LimitSpellGroup 385 // implemented - Limits to spell group(ie type 3 reuse reduction augs that are class specific and thus all share s SG) #define SE_LimitSpellGroup 385 // implemented - Limits to spell group(ie type 3 reuse reduction augs that are class specific and thus all share s SG)
#define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing #define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing
#define SE_CastOnCure 387 // implemented - Casts a spell on the cured person #define SE_CastOnCure 387 // implemented - Casts a spell on the cured person
//#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA) #define SE_SummonCorpseZone 388 // implemented - summons a corpse from any zone(nec AA)
#define SE_FcTimerRefresh 389 // implemented - Refresh spell icons #define SE_FcTimerRefresh 389 // implemented - Refresh spell icons
//#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. //#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited.
#define SE_LimitManaMax 391 // implemented #define SE_LimitManaMax 391 // implemented

View File

@ -222,7 +222,7 @@ bool StringIsNumber(const std::string &s) {
auto r = stod(s); auto r = stod(s);
return true; return true;
} }
catch (std::exception) { catch (std::exception &) {
return false; return false;
} }
} }

View File

@ -27,6 +27,12 @@
#include <fmt/format.h> #include <fmt/format.h>
#endif #endif
#ifdef _WINDOWS
#include <ctype.h>
#include <functional>
#include <algorithm>
#endif
#include "types.h" #include "types.h"
//std::string based //std::string based
@ -38,6 +44,38 @@ const std::string StringFormat(const char* format, ...);
const std::string vStringFormat(const char* format, va_list args); const std::string vStringFormat(const char* format, va_list args);
std::string implode(std::string glue, std::vector<std::string> src); std::string implode(std::string glue, std::vector<std::string> src);
/**
* @param str
* @param chars
* @return
*/
inline std::string &ltrim(std::string &str, const std::string &chars = "\t\n\v\f\r ")
{
str.erase(0, str.find_first_not_of(chars));
return str;
}
/**
* @param str
* @param chars
* @return
*/
inline std::string &rtrim(std::string &str, const std::string &chars = "\t\n\v\f\r ")
{
str.erase(str.find_last_not_of(chars) + 1);
return str;
}
/**
* @param str
* @param chars
* @return
*/
inline std::string &trim(std::string &str, const std::string &chars = "\t\n\v\f\r ")
{
return ltrim(rtrim(str, chars), chars);
}
template <typename T> template <typename T>
std::string implode(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src) std::string implode(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
{ {

View File

@ -1,20 +1,22 @@
/* EQEMu: Everquest Server Emulator /**
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) * EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
This program is free software; you can redistribute it and/or modify *
it under the terms of the GNU General Public License as published by * This program is free software; you can redistribute it and/or modify
the Free Software Foundation; version 2 of the License. * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, *
but WITHOUT ANY WARRANTY except by those people which sell it, which * This program is distributed in the hope that it will be useful,
are required to give you total support for your newly bought product; * but WITHOUT ANY WARRANTY except by those people which sell it, which
without even the implied warranty of MERCHANTABILITY or FITNESS FOR * are required to give you total support for your newly bought product;
A PARTICULAR PURPOSE. See the GNU General Public License for more details. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License *
along with this program; if not, write to the Free Software * You should have received a copy of the GNU General Public License
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * along with this program; if not, write to the Free Software
*/ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _EQEMU_VERSION_H #ifndef _EQEMU_VERSION_H
#define _EQEMU_VERSION_H #define _EQEMU_VERSION_H
@ -22,22 +24,24 @@
#define LOGIN_VERSION "0.8.0" #define LOGIN_VERSION "0.8.0"
#define EQEMU_PROTOCOL_VERSION "0.3.10" #define EQEMU_PROTOCOL_VERSION "0.3.10"
#define CURRENT_VERSION "1.1.3" #define CURRENT_VERSION "2.0"
/*
Everytime a Database SQL is added to Github,
increment CURRENT_BINARY_DATABASE_VERSION number and make sure you update the manifest
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9144 /**
* Every time a Database SQL is added to Github increment CURRENT_BINARY_DATABASE_VERSION
* number and make sure you update the manifest
*
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9148
#ifdef BOTS #ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026
#else #else
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
#endif #endif
#define COMPILE_DATE __DATE__ #define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__ #define COMPILE_TIME __TIME__
#ifndef WIN32 #ifndef WIN32

View File

@ -21,6 +21,10 @@
#endif #endif
/**
* @param mode
* @return
*/
std::string GetEncryptionByModeId(uint32 mode) std::string GetEncryptionByModeId(uint32 mode)
{ {
switch (mode) { switch (mode) {
@ -57,6 +61,13 @@ std::string GetEncryptionByModeId(uint32 mode)
} }
} }
/**
* @param buffer_in
* @param buffer_in_sz
* @param buffer_out
* @param enc
* @return
*/
const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc) const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc)
{ {
#ifdef EQEMU_USE_MBEDTLS #ifdef EQEMU_USE_MBEDTLS
@ -125,6 +136,10 @@ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buff
return buffer_out; return buffer_out;
} }
/**
* @param msg
* @return
*/
std::string eqcrypt_md5(const std::string &msg) std::string eqcrypt_md5(const std::string &msg)
{ {
std::string ret; std::string ret;
@ -159,6 +174,10 @@ std::string eqcrypt_md5(const std::string &msg)
return ret; return ret;
} }
/**
* @param msg
* @return
*/
std::string eqcrypt_sha1(const std::string &msg) std::string eqcrypt_sha1(const std::string &msg)
{ {
std::string ret; std::string ret;
@ -193,6 +212,10 @@ std::string eqcrypt_sha1(const std::string &msg)
return ret; return ret;
} }
/**
* @param msg
* @return
*/
std::string eqcrypt_sha512(const std::string &msg) std::string eqcrypt_sha512(const std::string &msg)
{ {
std::string ret; std::string ret;

View File

@ -38,6 +38,7 @@
struct LoginServer struct LoginServer
{ {
public: public:
LoginServer() : db(nullptr), server_manager(nullptr) { LoginServer() : db(nullptr), server_manager(nullptr) {
} }

View File

@ -112,6 +112,9 @@ namespace LoginserverCommandHandler {
return; return;
} }
server.token_manager = new LoginserverWebserver::TokenManager;
server.token_manager->LoadApiTokens();
for (auto &it : server.token_manager->loaded_api_tokens) { for (auto &it : server.token_manager->loaded_api_tokens) {
LogInfo( LogInfo(
"token [{0}] can_write [{1}] can_read [{2}]", "token [{0}] can_write [{1}] can_read [{2}]",
@ -133,8 +136,8 @@ namespace LoginserverCommandHandler {
description = "Creates Local Loginserver Account"; description = "Creates Local Loginserver Account";
std::vector<std::string> arguments = { std::vector<std::string> arguments = {
"--username", "{username}",
"--password" "{password}"
}; };
std::vector<std::string> options = { std::vector<std::string> options = {
"--email=*" "--email=*"
@ -147,8 +150,8 @@ namespace LoginserverCommandHandler {
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
AccountManagement::CreateLoginServerAccount( AccountManagement::CreateLoginServerAccount(
cmd("--username").str(), cmd(2).str(),
cmd("--password").str(), cmd(3).str(),
cmd("--email").str() cmd("--email").str()
); );
} }
@ -164,9 +167,9 @@ namespace LoginserverCommandHandler {
description = "Creates Loginserver World Administrator Account"; description = "Creates Loginserver World Administrator Account";
std::vector<std::string> arguments = { std::vector<std::string> arguments = {
"--username", "{username}",
"--password", "{password}",
"--email" "{email}"
}; };
std::vector<std::string> options = {}; std::vector<std::string> options = {};
@ -177,9 +180,9 @@ namespace LoginserverCommandHandler {
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
AccountManagement::CreateLoginserverWorldAdminAccount( AccountManagement::CreateLoginserverWorldAdminAccount(
cmd("--username").str(), cmd(2).str(),
cmd("--password").str(), cmd(3).str(),
cmd("--email").str() cmd(4).str()
); );
} }
@ -194,8 +197,8 @@ namespace LoginserverCommandHandler {
description = "Check user login credentials"; description = "Check user login credentials";
std::vector<std::string> arguments = { std::vector<std::string> arguments = {
"--username", "{username}",
"--password" "{password}"
}; };
std::vector<std::string> options = {}; std::vector<std::string> options = {};
@ -206,11 +209,11 @@ namespace LoginserverCommandHandler {
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
auto res = AccountManagement::CheckLoginserverUserCredentials( auto res = AccountManagement::CheckLoginserverUserCredentials(
cmd("--username").str(), cmd(2).str(),
cmd("--password").str() cmd(3).str()
); );
LogInfo("Credentials were {0}", res == true ? "accepted" : "not accepted"); LogInfo("Credentials were {0}", res != 0 ? "accepted" : "not accepted");
} }
/** /**
@ -224,8 +227,8 @@ namespace LoginserverCommandHandler {
description = "Change user login credentials"; description = "Change user login credentials";
std::vector<std::string> arguments = { std::vector<std::string> arguments = {
"--username", "{username}",
"--password" "{password}"
}; };
std::vector<std::string> options = {}; std::vector<std::string> options = {};
@ -236,8 +239,8 @@ namespace LoginserverCommandHandler {
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
AccountManagement::UpdateLoginserverUserCredentials( AccountManagement::UpdateLoginserverUserCredentials(
cmd("--username").str(), cmd(2).str(),
cmd("--password").str() cmd(3).str()
); );
} }
@ -252,8 +255,8 @@ namespace LoginserverCommandHandler {
description = "Check user external login credentials"; description = "Check user external login credentials";
std::vector<std::string> arguments = { std::vector<std::string> arguments = {
"--username", "{username}",
"--password" "{password}"
}; };
std::vector<std::string> options = {}; std::vector<std::string> options = {};
@ -264,8 +267,8 @@ namespace LoginserverCommandHandler {
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
auto res = AccountManagement::CheckExternalLoginserverUserCredentials( auto res = AccountManagement::CheckExternalLoginserverUserCredentials(
cmd("--username").str(), cmd(2).str(),
cmd("--password").str() cmd(3).str()
); );
LogInfo("Credentials were {0}", res ? "accepted" : "not accepted"); LogInfo("Credentials were {0}", res ? "accepted" : "not accepted");
@ -282,8 +285,8 @@ namespace LoginserverCommandHandler {
description = "Update world admin account password"; description = "Update world admin account password";
std::vector<std::string> arguments = { std::vector<std::string> arguments = {
"--username", "{username}",
"--password" "{password}"
}; };
std::vector<std::string> options = {}; std::vector<std::string> options = {};
@ -294,8 +297,8 @@ namespace LoginserverCommandHandler {
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName( AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName(
cmd("--username").str(), cmd(2).str(),
cmd("--password").str() cmd(3).str()
); );
} }
} }

View File

@ -43,22 +43,28 @@ void CatchSignal(int sig_num)
{ {
} }
int main(int argc, char **argv) void LoadDatabaseConnection()
{ {
RegisterExecutablePlatform(ExePlatformLogin); LogInfo("MySQL Database Init");
set_exception_handler();
LogInfo("Logging System Init"); server.db = new Database(
server.config.GetVariableString("database", "user", "root"),
server.config.GetVariableString("database", "password", ""),
server.config.GetVariableString("database", "host", "localhost"),
server.config.GetVariableString("database", "port", "3306"),
server.config.GetVariableString("database", "db", "peq")
);
if (argc == 1) { }
LogSys.LoadLogSettingsDefaults();
}
void LoadServerConfig()
{
server.config = EQ::JsonConfigFile::Load("login.json"); server.config = EQ::JsonConfigFile::Load("login.json");
LogInfo("Config System Init"); LogInfo("Config System Init");
/** /**
* options: logging * Logging
*/ */
server.options.Trace(server.config.GetVariableBool("logging", "trace", false)); server.options.Trace(server.config.GetVariableBool("logging", "trace", false));
server.options.WorldTrace(server.config.GetVariableBool("logging", "world_trace", false)); server.options.WorldTrace(server.config.GetVariableBool("logging", "world_trace", false));
@ -66,7 +72,7 @@ int main(int argc, char **argv)
server.options.DumpOutPackets(server.config.GetVariableBool("logging", "dump_packets_out", false)); server.options.DumpOutPackets(server.config.GetVariableBool("logging", "dump_packets_out", false));
/** /**
* options: worldservers * Worldservers
*/ */
server.options.RejectDuplicateServers( server.options.RejectDuplicateServers(
server.config.GetVariableBool( server.config.GetVariableBool(
@ -77,7 +83,7 @@ int main(int argc, char **argv)
server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true)); server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true));
/** /**
* options: account * Account
*/ */
server.options.AutoCreateAccounts(server.config.GetVariableBool("account", "auto_create_accounts", true)); server.options.AutoCreateAccounts(server.config.GetVariableBool("account", "auto_create_accounts", true));
server.options.AutoLinkAccounts(server.config.GetVariableBool("account", "auto_link_accounts", false)); server.options.AutoLinkAccounts(server.config.GetVariableBool("account", "auto_link_accounts", false));
@ -92,6 +98,9 @@ int main(int argc, char **argv)
); );
#endif #endif
/**
* Default Loginserver Name (Don't change)
*/
server.options.DefaultLoginServerName( server.options.DefaultLoginServerName(
server.config.GetVariableString( server.config.GetVariableString(
"general", "general",
@ -100,6 +109,10 @@ int main(int argc, char **argv)
) )
); );
/**
* Security
*/
#ifdef ENABLE_SECURITY #ifdef ENABLE_SECURITY
server.options.EncryptionMode(server.config.GetVariableInt("security", "mode", 13)); server.options.EncryptionMode(server.config.GetVariableInt("security", "mode", 13));
#else #else
@ -115,19 +128,41 @@ int main(int argc, char **argv)
true true
) )
); );
}
int main(int argc, char **argv)
{
RegisterExecutablePlatform(ExePlatformLogin);
set_exception_handler();
LogInfo("Logging System Init");
if (argc == 1) {
LogSys.LoadLogSettingsDefaults();
}
/**
* Command handler
*/
if (argc > 1) {
LogSys.SilenceConsoleLogging();
LoadServerConfig();
LoadDatabaseConnection();
LogSys.LoadLogSettingsDefaults();
LogSys.log_settings[Logs::Debug].log_to_console = static_cast<uint8>(Logs::General);
LogSys.log_settings[Logs::Debug].is_category_enabled = 1;
LoginserverCommandHandler::CommandHandler(argc, argv);
}
LoadServerConfig();
/** /**
* mysql connect * mysql connect
*/ */
LogInfo("MySQL Database Init"); LoadDatabaseConnection();
server.db = new Database(
server.config.GetVariableString("database", "user", "root"),
server.config.GetVariableString("database", "password", ""),
server.config.GetVariableString("database", "host", "localhost"),
server.config.GetVariableString("database", "port", "3306"),
server.config.GetVariableString("database", "db", "peq")
);
if (argc == 1) { if (argc == 1) {
server.db->LoadLogSettings(LogSys.log_settings); server.db->LoadLogSettings(LogSys.log_settings);
@ -195,14 +230,6 @@ int main(int argc, char **argv)
LoginserverWebserver::RegisterRoutes(api); LoginserverWebserver::RegisterRoutes(api);
} }
if (argc > 1) {
LogSys.LoadLogSettingsDefaults();
LogSys.log_settings[Logs::Debug].log_to_console = static_cast<uint8>(Logs::General);
LogSys.log_settings[Logs::Debug].is_category_enabled = 1;
LoginserverCommandHandler::CommandHandler(argc, argv);
}
LogInfo("[Config] [Logging] IsTraceOn [{0}]", server.options.IsTraceOn()); LogInfo("[Config] [Logging] IsTraceOn [{0}]", server.options.IsTraceOn());
LogInfo("[Config] [Logging] IsWorldTraceOn [{0}]", server.options.IsWorldTraceOn()); LogInfo("[Config] [Logging] IsWorldTraceOn [{0}]", server.options.IsWorldTraceOn());
LogInfo("[Config] [Logging] IsDumpInPacketsOn [{0}]", server.options.IsDumpInPacketsOn()); LogInfo("[Config] [Logging] IsDumpInPacketsOn [{0}]", server.options.IsDumpInPacketsOn());

File diff suppressed because it is too large Load Diff

36
utils/scripts/eqemu_server.pl Normal file → Executable file
View File

@ -516,13 +516,20 @@ sub check_for_input {
} }
sub check_for_world_bootup_database_update { sub check_for_world_bootup_database_update {
if ($OS eq "Windows") {
@db_version = split(': ', `world db_version`); my $world_path = "world";
} if (-e "bin/world") {
if ($OS eq "Linux") { $world_path = "bin/world";
@db_version = split(': ', `./world db_version`);
} }
#::: Get Binary DB version
if ($OS eq "Windows") {
@db_version = split(': ', `$world_path db_version`);
}
if ($OS eq "Linux") {
@db_version = split(': ', `./$world_path db_version`);
}
$binary_database_version = trim($db_version[1]); $binary_database_version = trim($db_version[1]);
$local_database_version = trim(get_mysql_result("SELECT version FROM db_version LIMIT 1")); $local_database_version = trim(get_mysql_result("SELECT version FROM db_version LIMIT 1"));
@ -806,7 +813,7 @@ sub fetch_utility_scripts {
sub setup_bots { sub setup_bots {
if ($OS eq "Windows") { if ($OS eq "Windows") {
fetch_latest_windows_binaries_bots(); fetch_latest_windows_appveyor_bots();
} }
if ($OS eq "Linux") { if ($OS eq "Linux") {
build_linux_source("bots"); build_linux_source("bots");
@ -814,7 +821,7 @@ sub setup_bots {
bots_db_management(); bots_db_management();
run_database_check(); run_database_check();
print "Bots should be setup, run your server and the #bot command should be available in-game\n"; print "Bots should be setup, run your server and the bot command should be available in-game (type '^help')\n";
} }
sub show_menu_prompt { sub show_menu_prompt {
@ -1686,7 +1693,7 @@ sub fetch_server_dlls {
sub fetch_peq_db_full { sub fetch_peq_db_full {
print "[Install] Downloading latest PEQ Database... Please wait...\n"; print "[Install] Downloading latest PEQ Database... Please wait...\n";
get_remote_file("http://edit.peqtgc.com/weekly/peq_beta.zip", "updates_staged/peq_beta.zip", 1); get_remote_file("http://edit.projecteq.net/weekly/peq_beta.zip", "updates_staged/peq_beta.zip", 1);
print "[Install] Downloaded latest PEQ Database... Extracting...\n"; print "[Install] Downloaded latest PEQ Database... Extracting...\n";
unzip('updates_staged/peq_beta.zip', 'updates_staged/peq_db/'); unzip('updates_staged/peq_beta.zip', 'updates_staged/peq_db/');
my $start_dir = "updates_staged/peq_db"; my $start_dir = "updates_staged/peq_db";
@ -1821,6 +1828,8 @@ sub quest_files_fetch {
if ($fc == 0) { if ($fc == 0) {
print "[Update] No Quest Updates found... \n\n"; print "[Update] No Quest Updates found... \n\n";
} }
rmtree("updates_staged/");
} }
sub lua_modules_fetch { sub lua_modules_fetch {
@ -2207,11 +2216,18 @@ sub get_bots_db_version {
} }
sub bots_db_management { sub bots_db_management {
my $world_path = "world";
if (-e "bin/world") {
$world_path = "bin/world";
}
#::: Get Binary DB version
if ($OS eq "Windows") { if ($OS eq "Windows") {
@db_version = split(': ', `world db_version`); @db_version = split(': ', `$world_path db_version`);
} }
if ($OS eq "Linux") { if ($OS eq "Linux") {
@db_version = split(': ', `./world db_version`); @db_version = split(': ', `./$world_path db_version`);
} }
#::: Main Binary Database version #::: Main Binary Database version

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,15 @@
account
account_ip
account_flags
account_rewards
adventure_details
adventure_stats adventure_stats
buyer
char_recipe_list char_recipe_list
character_auras
character_activities character_activities
character_alt_currency character_alt_currency
character_alternate_abilities character_alternate_abilities
character_auras
character_bandolier character_bandolier
character_bind character_bind
character_buffs character_buffs
@ -20,15 +26,22 @@ character_leadership_abilities
character_material character_material
character_memmed_spells character_memmed_spells
character_pet_buffs character_pet_buffs
character_pet_info
character_pet_inventory character_pet_inventory
character_potionbelt character_potionbelt
character_skills character_skills
character_spells character_spells
character_tasks
character_tribute character_tribute
completed_tasks completed_tasks
data_buckets
faction_values faction_values
friends friends
guild_bank
guild_members guild_members
guild_ranks
guild_relations
guilds
instance_list_player instance_list_player
inventory inventory
inventory_snapshots inventory_snapshots
@ -36,6 +49,9 @@ keyring
mail mail
player_titlesets player_titlesets
quest_globals quest_globals
sharedbank
timers timers
titles titles
zone_flags trader
trader_audit
zone_flags"

View File

@ -397,7 +397,11 @@
9141|2019_07_10_npc_flymode.sql|SHOW COLUMNS FROM `npc_types` LIKE 'flymode'|empty| 9141|2019_07_10_npc_flymode.sql|SHOW COLUMNS FROM `npc_types` LIKE 'flymode'|empty|
9142|2019_09_02_required_spawn_filter.sql|SHOW COLUMNS FROM `spawnentry` LIKE 'condition_value_filter'|empty| 9142|2019_09_02_required_spawn_filter.sql|SHOW COLUMNS FROM `spawnentry` LIKE 'condition_value_filter'|empty|
9143|2019_09_16_account_table_changes.sql|SHOW COLUMNS FROM `account` LIKE 'ls_id'|empty| 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 `logsys_categories`|not_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|
9147|2020_01_24_grid_centerpoint_wp.sql|SHOW COLUMNS FROM `grid_entries` LIKE 'centerpoint'|empty|
9148|2020_01_28_corpse_guild_consent_id.sql|SHOW COLUMNS FROM `character_corpses` LIKE 'guild_consent_id'|empty|
# Upgrade conditions: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not

View File

@ -21,9 +21,9 @@
9020|2018_08_13_bots_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `bot_step` = 0|not_empty| 9020|2018_08_13_bots_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `bot_step` = 0|not_empty|
9021|2018_10_09_bots_owner_options.sql|SHOW TABLES LIKE 'bot_owner_options'|empty| 9021|2018_10_09_bots_owner_options.sql|SHOW TABLES LIKE 'bot_owner_options'|empty|
9022|2019_02_07_bots_stance_type_update.sql|SELECT * FROM `bot_spell_casting_chances` WHERE `spell_type_index` = '255' AND `class_id` = '255' AND `stance_index` = '0'|not_empty| 9022|2019_02_07_bots_stance_type_update.sql|SELECT * FROM `bot_spell_casting_chances` WHERE `spell_type_index` = '255' AND `class_id` = '255' AND `stance_index` = '0'|not_empty|
9023|2019_06_22_bots_owner_option_stats_update.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'stats_update'|empty| 9023|2019_06_22_bots_owner_option_stats_update.sql|SELECT * FROM db_version WHERE bots_version >= 9023|empty|
9024|2019_06_27_bots_pet_get_lost.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'petgetlost'|empty| 9024|2019_06_27_bots_pet_get_lost.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'petgetlost'|empty|
9025|2019_08_26_bots_owner_option_spawn_message.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'spawn_message_enabled'|empty| 9025|2019_08_26_bots_owner_option_spawn_message.sql|SELECT * FROM db_version WHERE bots_version >= 9025|empty|
9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty| 9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty|
# Upgrade conditions: # Upgrade conditions:

View File

@ -0,0 +1,12 @@
-- Run this to un-reserve deleted characters
UPDATE
character_data
SET
name = SUBSTRING(
CONCAT(name, '-deleted-', UNIX_TIMESTAMP()),
1,
64
)
WHERE
deleted_at IS NOT NULL
AND name NOT LIKE '%-deleted-%';

View File

@ -0,0 +1,5 @@
RENAME TABLE `Banned_IPs` TO `Banned_IPs_`;
CREATE TABLE `banned_ips` (PRIMARY KEY (`ip_address`)) SELECT `ip_address`, `notes` FROM `Banned_IPs_`;
DROP TABLE IF EXISTS `Banned_IPs_`;

View File

@ -0,0 +1 @@
ALTER TABLE `character_data` ADD COLUMN `deleted_at` datetime NULL DEFAULT NULL;

View File

@ -0,0 +1,2 @@
alter table grid_entries add column `centerpoint` tinyint(4) not null default 0;
alter table spawngroup add column `wp_spawns` tinyint(1) unsigned not null default 0;

View File

@ -0,0 +1 @@
ALTER TABLE `character_corpses` ADD COLUMN `guild_consent_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `time_of_death`;

View File

@ -6,7 +6,7 @@ account_rewards
adventure_details adventure_details
adventure_members adventure_members
adventure_stats adventure_stats
Banned_IPs banned_ips
bugs bugs
buyer buyer
char_recipe_list char_recipe_list

View File

@ -14,7 +14,7 @@ SET(world_sources
lfplist.cpp lfplist.cpp
login_server.cpp login_server.cpp
login_server_list.cpp login_server_list.cpp
net.cpp main.cpp
queryserv.cpp queryserv.cpp
ucs.cpp ucs.cpp
web_interface.cpp web_interface.cpp
@ -22,6 +22,7 @@ SET(world_sources
wguild_mgr.cpp wguild_mgr.cpp
world_config.cpp world_config.cpp
world_console_connection.cpp world_console_connection.cpp
world_server_command_handler.cpp
worlddb.cpp worlddb.cpp
zonelist.cpp zonelist.cpp
zoneserver.cpp zoneserver.cpp
@ -51,6 +52,7 @@ SET(world_headers
world_config.h world_config.h
world_console_connection.h world_console_connection.h
world_tcp_connection.h world_tcp_connection.h
world_server_command_handler.h
worlddb.h worlddb.h
zonelist.h zonelist.h
zoneserver.h zoneserver.h

View File

@ -2119,102 +2119,3 @@ void AdventureManager::Save()
} }
} }
void AdventureManager::Load()
{
//disabled for now
return;
char *data = nullptr;
FILE *f = fopen("adventure_state.dat", "r");
if(f)
{
fseek(f, 0, SEEK_END);
long length = ftell(f);
if(length > 0)
{
data = new char[length];
fseek(f, 0, SEEK_SET);
fread(data, length, 1, f);
}
fclose(f);
}
if(data)
{
char *ptr = data;
int number_of_adventures = *((int*)ptr);
ptr += sizeof(int);
for(int i = 0; i < number_of_adventures; ++i)
{
int count = *((int*)ptr);
ptr += sizeof(int);
int a_count = *((int*)ptr);
ptr += sizeof(int);
int template_id = *((int*)ptr);
ptr += sizeof(int);
int status = *((int*)ptr);
ptr += sizeof(int);
int instance_id = *((int*)ptr);
ptr += sizeof(int);
int rem_time = *((int*)ptr);
ptr += sizeof(int);
int num_players = *((int*)ptr);
ptr += sizeof(int);
AdventureTemplate *t = GetAdventureTemplate(template_id);
if(t)
{
auto adv =
new Adventure(t, count, a_count, (AdventureStatus)status, instance_id, rem_time);
for(int j = 0; j < num_players; ++j)
{
adv->AddPlayer((const char*)ptr, false);
ptr += strlen((const char*)ptr);
ptr += 1;
}
adventure_list.push_back(adv);
}
else
{
for(int j = 0; j < num_players; ++j)
{
ptr += strlen((const char*)ptr);
ptr += 1;
}
}
}
int number_of_finished = *((int*)ptr);
ptr += sizeof(int);
for(int k = 0; k < number_of_finished; ++k)
{
AdventureFinishEvent afe;
afe.win = *((bool*)ptr);
ptr += sizeof(bool);
afe.points = *((int*)ptr);
ptr += sizeof(int);
afe.theme = *((int*)ptr);
ptr += sizeof(int);
afe.name = (const char*)ptr;
ptr += strlen((const char*)ptr);
ptr += 1;
finished_list.push_back(afe);
}
safe_delete_array(data);
}
}

View File

@ -34,7 +34,6 @@ public:
void AddFinishedEvent(AdventureFinishEvent fe) { finished_list.push_back(fe); Save(); } void AddFinishedEvent(AdventureFinishEvent fe) { finished_list.push_back(fe); Save(); }
bool PopFinishedEvent(const char *name, AdventureFinishEvent &fe); bool PopFinishedEvent(const char *name, AdventureFinishEvent &fe);
void Save(); void Save();
void Load();
Adventure **GetFinishedAdventures(const char *player, int &count); Adventure **GetFinishedAdventures(const char *player, int &count);
Adventure *GetActiveAdventure(const char *player); Adventure *GetActiveAdventure(const char *player);

View File

@ -1,20 +1,23 @@
/* EQEMu: Everquest Server Emulator /**
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) * EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../common/global_define.h" #include "../common/global_define.h"
#include <iostream> #include <iostream>
@ -86,6 +89,7 @@ union semun {
#include "../common/net/servertalk_server.h" #include "../common/net/servertalk_server.h"
#include "../zone/data_bucket.h" #include "../zone/data_bucket.h"
#include "world_server_command_handler.h"
ClientList client_list; ClientList client_list;
GroupLFPList LFPGroupList; GroupLFPList LFPGroupList;
@ -113,14 +117,30 @@ inline void UpdateWindowTitle(std::string new_title) {
#endif #endif
} }
int main(int argc, char** argv) { void LoadDatabaseConnections()
RegisterExecutablePlatform(ExePlatformWorld); {
LogSys.LoadLogSettingsDefaults(); LogInfo(
set_exception_handler(); "Connecting to MySQL [{}]@[{}]:[{}]",
Config->DatabaseUsername.c_str(),
Config->DatabaseHost.c_str(),
Config->DatabasePort
);
/** if (!database.Connect(
* Auto convert json config from xml Config->DatabaseHost.c_str(),
*/ Config->DatabaseUsername.c_str(),
Config->DatabasePassword.c_str(),
Config->DatabaseDB.c_str(),
Config->DatabasePort
)) {
LogError("Cannot continue without a database connection");
std::exit(1);
}
}
void CheckForXMLConfigUpgrade()
{
if (!std::ifstream("eqemu_config.json") && std::ifstream("eqemu_config.xml")) { if (!std::ifstream("eqemu_config.json") && std::ifstream("eqemu_config.xml")) {
CheckForServerScript(true); CheckForServerScript(true);
if(system("perl eqemu_server.pl convert_xml")); if(system("perl eqemu_server.pl convert_xml"));
@ -128,26 +148,105 @@ int main(int argc, char** argv) {
else { else {
CheckForServerScript(); CheckForServerScript();
} }
}
void LoadServerConfig()
{
LogInfo("Loading server configuration");
if (!WorldConfig::LoadConfig()) {
LogError("Loading server configuration failed");
std::exit(1);
}
}
void RegisterLoginservers()
{
if (Config->LoginCount == 0) {
if (Config->LoginHost.length()) {
loginserverlist.Add(
Config->LoginHost.c_str(),
Config->LoginPort,
Config->LoginAccount.c_str(),
Config->LoginPassword.c_str(),
Config->LoginLegacy
);
LogInfo("Added loginserver [{}]:[{}]", Config->LoginHost.c_str(), Config->LoginPort);
}
}
else {
LinkedList<LoginConfig *> loginlist = Config->loginlist;
LinkedListIterator<LoginConfig *> iterator(loginlist);
iterator.Reset();
while (iterator.MoreElements()) {
if (iterator.GetData()->LoginHost.length()) {
loginserverlist.Add(
iterator.GetData()->LoginHost.c_str(),
iterator.GetData()->LoginPort,
iterator.GetData()->LoginAccount.c_str(),
iterator.GetData()->LoginPassword.c_str(),
iterator.GetData()->LoginLegacy
);
LogInfo(
"Added loginserver [{}]:[{}]",
iterator.GetData()->LoginHost.c_str(),
iterator.GetData()->LoginPort
);
}
iterator.Advance();
}
}
}
/**
* World process entrypoint
*
* @param argc
* @param argv
* @return
*/
int main(int argc, char** argv) {
RegisterExecutablePlatform(ExePlatformWorld);
LogSys.LoadLogSettingsDefaults();
set_exception_handler();
/** /**
* Database version * Database version
*/ */
uint32 Database_Version = CURRENT_BINARY_DATABASE_VERSION; uint32 database_version = CURRENT_BINARY_DATABASE_VERSION;
uint32 Bots_Database_Version = CURRENT_BINARY_BOTS_DATABASE_VERSION; uint32 bots_database_version = CURRENT_BINARY_BOTS_DATABASE_VERSION;
if (argc >= 2) { if (argc >= 2) {
if (strcasecmp(argv[1], "db_version") == 0) { if (strcasecmp(argv[1], "db_version") == 0) {
std::cout << "Binary Database Version: " << Database_Version << " : " << Bots_Database_Version << std::endl; std::cout << "Binary Database Version: " << database_version << " : " << bots_database_version << std::endl;
return 0; return 0;
} }
} }
// Load server configuration /**
LogInfo("Loading server configuration"); * Command handler
if (!WorldConfig::LoadConfig()) { */
LogError("Loading server configuration failed"); if (argc > 1) {
return 1; LogSys.SilenceConsoleLogging();
/**
* Get Config
*/
WorldConfig::LoadConfig();
Config = WorldConfig::get();
/**
* Load database
*/
LoadDatabaseConnections();
LogSys.EnableConsoleLogging();
WorldserverCommandHandler::CommandHandler(argc, argv);
} }
CheckForXMLConfigUpgrade();
LoadServerConfig();
Config = WorldConfig::get(); Config = WorldConfig::get();
LogInfo("CURRENT_VERSION: [{}]", CURRENT_VERSION); LogInfo("CURRENT_VERSION: [{}]", CURRENT_VERSION);
@ -169,146 +268,23 @@ int main(int argc, char** argv) {
} }
#endif #endif
/** RegisterLoginservers();
* Add Loginserver LoadDatabaseConnections();
*/
if (Config->LoginCount == 0) {
if (Config->LoginHost.length()) {
loginserverlist.Add(
Config->LoginHost.c_str(),
Config->LoginPort,
Config->LoginAccount.c_str(),
Config->LoginPassword.c_str(),
Config->LoginLegacy
);
LogInfo("Added loginserver [{}]:[{}]", Config->LoginHost.c_str(), Config->LoginPort);
}
}
else {
LinkedList<LoginConfig *> loginlist = Config->loginlist;
LinkedListIterator<LoginConfig *> iterator(loginlist);
iterator.Reset();
while (iterator.MoreElements()) {
loginserverlist.Add(
iterator.GetData()->LoginHost.c_str(),
iterator.GetData()->LoginPort,
iterator.GetData()->LoginAccount.c_str(),
iterator.GetData()->LoginPassword.c_str(),
iterator.GetData()->LoginLegacy
);
LogInfo("Added loginserver [{}]:[{}]", iterator.GetData()->LoginHost.c_str(), iterator.GetData()->LoginPort);
iterator.Advance();
}
}
LogInfo("Connecting to MySQL [{}]@[{}]:[{}]", Config->DatabaseUsername.c_str(), Config->DatabaseHost.c_str(), Config->DatabasePort);
if (!database.Connect(
Config->DatabaseHost.c_str(),
Config->DatabaseUsername.c_str(),
Config->DatabasePassword.c_str(),
Config->DatabaseDB.c_str(),
Config->DatabasePort)) {
LogError("Cannot continue without a database connection");
return 1;
}
guild_mgr.SetDatabase(&database); guild_mgr.SetDatabase(&database);
/* Register Log System and Settings */ /**
* Logging
*/
database.LoadLogSettings(LogSys.log_settings); database.LoadLogSettings(LogSys.log_settings);
LogSys.StartFileLogs(); LogSys.StartFileLogs();
/**
* Parse simple CLI passes
*/
bool ignore_db = false; bool ignore_db = false;
if (argc >= 2) { if (argc >= 2) {
std::string tmp; if (strcasecmp(argv[1], "ignore_db") == 0) {
if (strcasecmp(argv[1], "help") == 0 || strcasecmp(argv[1], "?") == 0 || strcasecmp(argv[1], "/?") == 0 || strcasecmp(argv[1], "-?") == 0 || strcasecmp(argv[1], "-h") == 0 || strcasecmp(argv[1], "-help") == 0) {
std::cout << "Worldserver command line commands:" << std::endl;
std::cout << "adduser username password flag - adds a user account" << std::endl;
std::cout << "flag username flag - sets GM flag on the account" << std::endl;
std::cout << "startzone zoneshortname - sets the starting zone" << std::endl;
std::cout << "-holdzones - reboots lost zones" << std::endl;
return 0;
}
else if (strcasecmp(argv[1], "-holdzones") == 0) {
std::cout << "Reboot Zones mode ON" << std::endl;
holdzones = true;
}
else if (database.GetVariable("disablecommandline", tmp)) {
if (tmp.length() == 1) {
if (tmp[0] == '1') {
std::cerr << "Command line disabled in database... exiting" << std::endl;
return 1;
}
}
}
else if (strcasecmp(argv[1], "adduser") == 0) {
if (argc == 5) {
if (Seperator::IsNumber(argv[4])) {
if (atoi(argv[4]) >= 0 && atoi(argv[4]) <= 255) {
std::string user;
std::string loginserver;
ParseAccountString(argv[2], user, loginserver);
if (database.CreateAccount(argv[2], argv[3], atoi(argv[4]), loginserver.c_str(), 0) == 0) {
std::cerr << "database.CreateAccount failed." << std::endl;
return 1;
}
else {
std::cout << "Account created: Username='" << argv[2] << "', Password='" << argv[3] << "', status=" << argv[4] << std::endl;
return 0;
}
}
}
}
std::cout << "Usage: world adduser username password flag" << std::endl;
std::cout << "flag = 0, 1 or 2" << std::endl;
return 0;
}
else if (strcasecmp(argv[1], "flag") == 0) {
if (argc == 4) {
if (Seperator::IsNumber(argv[3])) {
if (atoi(argv[3]) >= 0 && atoi(argv[3]) <= 255) {
if (database.SetAccountStatus(argv[2], atoi(argv[3]))) {
std::cout << "Account flagged: Username='" << argv[2] << "', status=" << argv[3] << std::endl;
return 0;
}
else {
std::cerr << "database.SetAccountStatus failed." << std::endl;
return 1;
}
}
}
}
std::cout << "Usage: world flag username flag" << std::endl;
std::cout << "flag = 0-200" << std::endl;
return 0;
}
else if (strcasecmp(argv[1], "startzone") == 0) {
if (argc == 3) {
if (strlen(argv[2]) < 3) {
std::cerr << "Error: zone name too short" << std::endl;
return 1;
}
else if (strlen(argv[2]) > 15) {
std::cerr << "Error: zone name too long" << std::endl;
return 1;
}
else {
if (database.SetVariable("startzone", argv[2])) {
std::cout << "Starting zone changed: '" << argv[2] << "'" << std::endl;
return 0;
}
else {
std::cerr << "database.SetVariable failed." << std::endl;
return 1;
}
}
}
std::cout << "Usage: world startzone zoneshortname" << std::endl;
return 0;
}
else if (strcasecmp(argv[1], "ignore_db") == 0) {
ignore_db = true; ignore_db = true;
} }
else { else {
@ -360,7 +336,7 @@ int main(int argc, char** argv) {
if (!RuleManager::Instance()->UpdateOrphanedRules(&database)) { if (!RuleManager::Instance()->UpdateOrphanedRules(&database)) {
LogInfo("Failed to process 'Orphaned Rules' update operation."); LogInfo("Failed to process 'Orphaned Rules' update operation.");
} }
if (!RuleManager::Instance()->UpdateInjectedRules(&database, "default")) { if (!RuleManager::Instance()->UpdateInjectedRules(&database, "default")) {
LogInfo("Failed to process 'Injected Rules' for ruleset 'default' update operation."); LogInfo("Failed to process 'Injected Rules' for ruleset 'default' update operation.");
} }
@ -407,13 +383,6 @@ int main(int argc, char** argv) {
LogInfo("Loading launcher list"); LogInfo("Loading launcher list");
launcher_list.LoadList(); launcher_list.LoadList();
std::string tmp;
database.GetVariable("holdzones", tmp);
if (tmp.length() == 1 && tmp[0] == '1') {
holdzones = true;
}
LogInfo("Reboot zone modes [{}]", holdzones ? "ON" : "OFF");
LogInfo("Deleted [{}] stale player corpses from database", database.DeleteStalePlayerCorpses()); LogInfo("Deleted [{}] stale player corpses from database", database.DeleteStalePlayerCorpses());
LogInfo("Loading adventures"); LogInfo("Loading adventures");
@ -427,7 +396,6 @@ int main(int argc, char** argv) {
LogInfo("Unable to load adventure templates"); LogInfo("Unable to load adventure templates");
} }
adventure_manager.Load();
adventure_manager.LoadLeaderboardInfo(); adventure_manager.LoadLeaderboardInfo();
LogInfo("Purging expired instances"); LogInfo("Purging expired instances");

View File

@ -24,7 +24,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p)
std::stringstream ss(json_str); std::stringstream ss(json_str);
ss >> root; ss >> root;
} }
catch (std::exception) { catch (std::exception &) {
SendError("Could not parse request"); SendError("Could not parse request");
return; return;
} }
@ -40,7 +40,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p)
return; return;
} }
} }
catch (std::exception) { catch (std::exception &) {
SendError("Invalid request: method not supplied"); SendError("Invalid request: method not supplied");
return; return;
} }
@ -49,7 +49,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p)
try { try {
params = root["params"]; params = root["params"];
} }
catch (std::exception) { catch (std::exception &) {
params = nullptr; params = nullptr;
} }
@ -57,7 +57,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p)
try { try {
id = root["id"].asString(); id = root["id"].asString();
} }
catch (std::exception) { catch (std::exception &) {
id = ""; id = "";
} }
@ -82,7 +82,7 @@ void WebInterface::Send(const Json::Value &value)
p.PutString(0, ss.str()); p.PutString(0, ss.str());
m_connection->Send(ServerOP_WebInterfaceCall, p); m_connection->Send(ServerOP_WebInterfaceCall, p);
} }
catch (std::exception) { catch (std::exception &) {
//Log error //Log error
} }
} }
@ -116,7 +116,7 @@ void WebInterface::SendEvent(const Json::Value &value)
p.PutString(0, ss.str()); p.PutString(0, ss.str());
m_connection->Send(ServerOP_WebInterfaceEvent, p); m_connection->Send(ServerOP_WebInterfaceEvent, p);
} }
catch (std::exception) { catch (std::exception &) {
//Log error //Log error
} }
} }

View File

@ -0,0 +1,205 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "world_server_command_handler.h"
#include "../common/eqemu_logsys.h"
#include "../common/json/json.h"
#include "../common/version.h"
#include "worlddb.h"
#include "../common/database_schema.h"
namespace WorldserverCommandHandler {
/**
* @param argc
* @param argv
*/
void CommandHandler(int argc, char **argv)
{
if (argc == 1) { return; }
argh::parser cmd;
cmd.parse(argc, argv, argh::parser::PREFER_PARAM_FOR_UNREG_OPTION);
EQEmuCommand::DisplayDebug(cmd);
/**
* Declare command mapping
*/
auto function_map = EQEmuCommand::function_map;
/**
* Register commands
*/
function_map["world:version"] = &WorldserverCommandHandler::Version;
function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion;
function_map["database:set-account-status"] = &WorldserverCommandHandler::DatabaseSetAccountStatus;
function_map["database:schema"] = &WorldserverCommandHandler::DatabaseGetSchema;
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
}
/**
* @param argc
* @param argv
* @param cmd
* @param description
*/
void DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Shows database version";
if (cmd[{"-h", "--help"}]) {
return;
}
Json::Value database_version;
database_version["database_version"] = CURRENT_BINARY_DATABASE_VERSION;
database_version["bots_database_version"] = CURRENT_BINARY_BOTS_DATABASE_VERSION;
std::stringstream payload;
payload << database_version;
std::cout << payload.str() << std::endl;
}
/**
* @param argc
* @param argv
* @param cmd
* @param description
*/
void Version(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Shows server version";
if (cmd[{"-h", "--help"}]) {
return;
}
Json::Value database_version;
database_version["bots_database_version"] = CURRENT_BINARY_BOTS_DATABASE_VERSION;
database_version["compile_date"] = COMPILE_DATE;
database_version["compile_time"] = COMPILE_TIME;
database_version["database_version"] = CURRENT_BINARY_DATABASE_VERSION;
database_version["server_version"] = CURRENT_VERSION;
std::stringstream payload;
payload << database_version;
std::cout << payload.str() << std::endl;
}
/**
* @param argc
* @param argv
* @param cmd
* @param description
*/
void DatabaseSetAccountStatus(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Sets account status by account name";
std::vector<std::string> arguments = {
"{name}",
"{status}"
};
std::vector<std::string> options = {};
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
database.SetAccountStatus(
cmd(2).str(),
std::stoi(cmd(3).str())
);
}
/**
* @param argc
* @param argv
* @param cmd
* @param description
*/
void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Displays server database schema";
if (cmd[{"-h", "--help"}]) {
return;
}
Json::Value player_tables_json;
std::vector<std::string> player_tables = DatabaseSchema::GetPlayerTables();
for (const auto &table : player_tables) {
player_tables_json.append(table);
}
Json::Value content_tables_json;
std::vector<std::string> content_tables = DatabaseSchema::GetContentTables();
for (const auto &table : content_tables) {
content_tables_json.append(table);
}
Json::Value server_tables_json;
std::vector<std::string> server_tables = DatabaseSchema::GetServerTables();
for (const auto &table : server_tables) {
server_tables_json.append(table);
}
Json::Value login_tables_json;
std::vector<std::string> login_tables = DatabaseSchema::GetLoginTables();
for (const auto &table : login_tables) {
login_tables_json.append(table);
}
Json::Value state_tables_json;
std::vector<std::string> state_tables = DatabaseSchema::GetStateTables();
for (const auto &table : state_tables) {
state_tables_json.append(table);
}
Json::Value version_tables_json;
std::vector<std::string> version_tables = DatabaseSchema::GetVersionTables();
for (const auto &table : version_tables) {
version_tables_json.append(table);
}
Json::Value schema;
schema["content_tables"] = content_tables_json;
schema["login_tables"] = login_tables_json;
schema["player_tables"] = player_tables_json;
schema["server_tables"] = server_tables_json;
schema["state_tables"] = state_tables_json;
schema["version_tables"] = version_tables_json;
std::stringstream payload;
payload << schema;
std::cout << payload.str() << std::endl;
}
}

View File

@ -0,0 +1,36 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "iostream"
#include "../common/cli/eqemu_command_handler.h"
#ifndef EQEMU_WORLD_SERVER_COMMAND_HANDLER_H
#define EQEMU_WORLD_SERVER_COMMAND_HANDLER_H
namespace WorldserverCommandHandler {
void CommandHandler(int argc, char **argv);
void Version(int argc, char **argv, argh::parser &cmd, std::string &description);
void DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description);
void DatabaseSetAccountStatus(int argc, char **argv, argh::parser &cmd, std::string &description);
void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description);
};
#endif //EQEMU_WORLD_SERVER_COMMAND_HANDLER_H

View File

@ -31,23 +31,27 @@ extern std::vector<RaceClassAllocation> character_create_allocations;
extern std::vector<RaceClassCombos> character_create_race_class_combos; 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
EQEmu::versions::ClientVersion client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(clientVersionBit); client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(client_version_bit);
size_t character_limit = EQEmu::constants::StaticLookup(client_version)->CharacterCreationLimit; 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; character_limit = EQEmu::constants::CHARACTER_CREATION_LIMIT;
}
// Force Titanium clients to use '8' // Force Titanium clients to use '8'
if (client_version == EQEmu::versions::ClientVersion::Titanium) if (client_version == EQEmu::versions::ClientVersion::Titanium) {
character_limit = 8; character_limit = 8;
}
/* Get Character Info */
std::string cquery = StringFormat( std::string character_list_query = StringFormat(
"SELECT " "SELECT "
"`id`, " // 0 "`id`, " // 0
"name, " // 1 "name, " // 1
@ -71,237 +75,281 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou
"zone_id " // 19 "zone_id " // 19
"FROM " "FROM "
"character_data " "character_data "
"WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit); "WHERE `account_id` = %i AND deleted_at IS NULL ORDER BY `name` LIMIT %u",
auto results = database.QueryDatabase(cquery); account_id,
character_limit
);
auto results = database.QueryDatabase(character_list_query);
size_t character_count = results.RowCount(); size_t character_count = results.RowCount();
if (character_count == 0) { if (character_count == 0) {
*outApp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); *out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
CharacterSelect_Struct *cs = (CharacterSelect_Struct *)(*outApp)->pBuffer; CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
cs->CharCount = 0; cs->CharCount = 0;
cs->TotalChars = character_limit; cs->TotalChars = character_limit;
return; return;
} }
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count); 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; unsigned char *buff_ptr = (*out_app)->pBuffer;
CharacterSelect_Struct *cs = (CharacterSelect_Struct *)buff_ptr; CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr;
cs->CharCount = character_count; cs->CharCount = character_count;
cs->TotalChars = character_limit; cs->TotalChars = character_limit;
buff_ptr += sizeof(CharacterSelect_Struct); buff_ptr += sizeof(CharacterSelect_Struct);
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr; CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr;
PlayerProfile_Struct pp; PlayerProfile_Struct player_profile_struct;
EQEmu::InventoryProfile inv; EQEmu::InventoryProfile inventory_profile;
pp.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version)); player_profile_struct.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version));
inv.SetInventoryVersion(client_version); inventory_profile.SetInventoryVersion(client_version);
inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support 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]); uint32 character_id = (uint32) atoi(row[0]);
uint8 has_home = 0; uint8 has_home = 0;
uint8 has_bind = 0; uint8 has_bind = 0;
memset(&pp, 0, sizeof(PlayerProfile_Struct)); memset(&player_profile_struct, 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]);
for (uint32 matslot = 0; matslot < EQEmu::textures::materialCount; matslot++) { // Processed below memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name));
cse->Equip[matslot].Material = 0; strcpy(p_character_select_entry_struct->Name, row[1]);
cse->Equip[matslot].Unknown1 = 0; p_character_select_entry_struct->Class = (uint8) atoi(row[4]);
cse->Equip[matslot].EliteModel = 0; p_character_select_entry_struct->Race = (uint32) atoi(row[3]);
cse->Equip[matslot].HerosForgeModel = 0; p_character_select_entry_struct->Level = (uint8) atoi(row[5]);
cse->Equip[matslot].Unknown2 = 0; p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class;
cse->Equip[matslot].Color = 0; 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; for (uint32 material_slot = 0; material_slot < EQEmu::textures::materialCount; material_slot++) {
cse->Unknown19 = 0xFF; p_character_select_entry_struct->Equip[material_slot].Material = 0;
cse->DrakkinTattoo = (uint32)atoi(row[17]); p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0;
cse->DrakkinDetails = (uint32)atoi(row[18]); p_character_select_entry_struct->Equip[material_slot].EliteModel = 0;
cse->Deity = (uint32)atoi(row[6]); p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0;
cse->PrimaryIDFile = 0; // Processed Below p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0;
cse->SecondaryIDFile = 0; // Processed Below p_character_select_entry_struct->Equip[material_slot].Color = 0;
cse->HairColor = (uint8)atoi(row[9]); }
cse->BeardColor = (uint8)atoi(row[10]);
cse->EyeColor1 = (uint8)atoi(row[11]); p_character_select_entry_struct->Unknown15 = 0xFF;
cse->EyeColor2 = (uint8)atoi(row[12]); p_character_select_entry_struct->Unknown19 = 0xFF;
cse->HairStyle = (uint8)atoi(row[13]); p_character_select_entry_struct->DrakkinTattoo = (uint32) atoi(row[17]);
cse->Beard = (uint8)atoi(row[14]); p_character_select_entry_struct->DrakkinDetails = (uint32) atoi(row[18]);
cse->GoHome = 0; // Processed Below p_character_select_entry_struct->Deity = (uint32) atoi(row[6]);
cse->Tutorial = 0; // Processed Below p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below
cse->DrakkinHeritage = (uint32)atoi(row[16]); p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below
cse->Unknown1 = 0; p_character_select_entry_struct->HairColor = (uint8) atoi(row[9]);
cse->Enabled = 1; p_character_select_entry_struct->BeardColor = (uint8) atoi(row[10]);
cse->LastLogin = (uint32)atoi(row[7]); // RoF2 value: 1212696584 p_character_select_entry_struct->EyeColor1 = (uint8) atoi(row[11]);
cse->Unknown2 = 0; p_character_select_entry_struct->EyeColor2 = (uint8) atoi(row[12]);
/* Fill End */ 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)) { if (RuleB(World, EnableReturnHomeButton)) {
int now = time(nullptr); int now = time(nullptr);
if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) {
cse->GoHome = 1; p_character_select_entry_struct->GoHome = 1;
}
} }
if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) { if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial))) {
cse->Tutorial = 1; 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); * Bind
auto results_bind = database.QueryDatabase(cquery); */
auto bind_count = results_bind.RowCount(); character_list_query = StringFormat(
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { "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) { if (row_b[6] && atoi(row_b[6]) == 4) {
has_home = 1; 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 our bind count is less than 5, we need to actually make use of this data so lets parse it
if (bind_count < 5) { if (bind_count < 5) {
pp.binds[4].zoneId = atoi(row_b[0]); player_profile_struct.binds[4].zoneId = atoi(row_b[0]);
pp.binds[4].instance_id = atoi(row_b[1]); player_profile_struct.binds[4].instance_id = atoi(row_b[1]);
pp.binds[4].x = atof(row_b[2]); player_profile_struct.binds[4].x = atof(row_b[2]);
pp.binds[4].y = atof(row_b[3]); player_profile_struct.binds[4].y = atof(row_b[3]);
pp.binds[4].z = atof(row_b[4]); player_profile_struct.binds[4].z = atof(row_b[4]);
pp.binds[4].heading = atof(row_b[5]); 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) { 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", character_list_query = StringFormat(
cse->Class, cse->Deity, cse->Race); "SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i",
auto results_bind = database.QueryDatabase(cquery); p_character_select_entry_struct->Class,
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { 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 a bind_id is specified, make them start there */
if (atoi(row_d[1]) != 0) { if (atoi(row_d[1]) != 0) {
pp.binds[4].zoneId = (uint32)atoi(row_d[1]); player_profile_struct.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); 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 { 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 x = atof(row_d[2]);
float y = atof(row_d[3]); float y = atof(row_d[3]);
float z = atof(row_d[4]); float z = atof(row_d[4]);
if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } if (x == 0 && y == 0 && z == 0) { GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &x, &y, &z); }
pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = 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 no home bind set, set it */
if (has_home == 0) { 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)", " 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); character_id,
auto results_bset = QueryDatabase(query); 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 no regular bind set, set it */
if (has_bind == 0) { 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)", " 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); character_id,
auto results_bset = QueryDatabase(query); 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 our bind count is less than 5, then we have null data that needs to be filled in. */
if (bind_count < 5) { if (bind_count < 5) {
// we know that home and main bind must be valid here, so we don't check those // 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. // we also use home to fill in the null data like live does.
for (int i = 1; i < 4; i++) { for (int i = 1; i < 4; i++) {
if (pp.binds[i].zoneId != 0) // we assume 0 is the only invalid one ... if (player_profile_struct.binds[i].zoneId != 0) { // we assume 0 is the only invalid one ...
continue; 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)", " 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); character_id,
auto results_bset = QueryDatabase(query); 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 */ character_list_query = StringFormat(
cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); "SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u",
auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; character_id
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { );
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]); slot = atoi(row_b[0]);
pp.item_tint.Slot[slot].Red = atoi(row_b[1]); player_profile_struct.item_tint.Slot[slot].Red = atoi(row_b[1]);
pp.item_tint.Slot[slot].Green = atoi(row_b[2]); player_profile_struct.item_tint.Slot[slot].Green = atoi(row_b[2]);
pp.item_tint.Slot[slot].Blue = atoi(row_b[3]); player_profile_struct.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].UseTint = atoi(row_b[4]);
} }
/* Character Material Data End */
/* Load Inventory */ if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) {
// If we ensure that the material data is updated appropriately, we can do away with inventory loads const EQEmu::ItemData *item = nullptr;
if (GetCharSelInventory(accountID, cse->Name, &inv)) { const EQEmu::ItemInstance *inst = nullptr;
const EQEmu::ItemData* item = nullptr; int16 inventory_slot = 0;
const EQEmu::ItemInstance* inst = nullptr;
int16 invslot = 0;
for (uint32 matslot = EQEmu::textures::textureBegin; matslot < EQEmu::textures::materialCount; matslot++) { for (uint32 matslot = EQEmu::textures::textureBegin; matslot < EQEmu::textures::materialCount; matslot++) {
invslot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot); inventory_slot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot);
if (invslot == INVALID_INDEX) { continue; } if (inventory_slot == INVALID_INDEX) { continue; }
inst = inv.GetItem(invslot); inst = inventory_profile.GetItem(inventory_slot);
if (inst == nullptr) { continue; } if (inst == nullptr) {
continue;
}
item = inst->GetItem(); item = inst->GetItem();
if (item == nullptr) { continue; } if (item == nullptr) {
continue;
}
if (matslot > 6) { if (matslot > 6) {
uint32 idfile = 0; uint32 item_id_file = 0;
// Weapon Models // Weapon Models
if (inst->GetOrnamentationIDFile() != 0) { if (inst->GetOrnamentationIDFile() != 0) {
idfile = inst->GetOrnamentationIDFile(); item_id_file = inst->GetOrnamentationIDFile();
cse->Equip[matslot].Material = idfile; p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
} }
else { else {
if (strlen(item->IDFile) > 2) { if (strlen(item->IDFile) > 2) {
idfile = atoi(&item->IDFile[2]); item_id_file = atoi(&item->IDFile[2]);
cse->Equip[matslot].Material = idfile; p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
} }
} }
if (matslot == EQEmu::textures::weaponPrimary) { if (matslot == EQEmu::textures::weaponPrimary) {
cse->PrimaryIDFile = idfile; p_character_select_entry_struct->PrimaryIDFile = item_id_file;
} }
else { else {
cse->SecondaryIDFile = idfile; p_character_select_entry_struct->SecondaryIDFile = item_id_file;
} }
} }
else { else {
uint32 color = 0; uint32 color = 0;
if (pp.item_tint.Slot[matslot].UseTint) { if (player_profile_struct.item_tint.Slot[matslot].UseTint) {
color = pp.item_tint.Slot[matslot].Color; color = player_profile_struct.item_tint.Slot[matslot].Color;
} }
else { else {
color = inst->GetColor(); color = inst->GetColor();
} }
// Armor Materials/Models // Armor Materials/Models
cse->Equip[matslot].Material = item->Material; p_character_select_entry_struct->Equip[matslot].Material = item->Material;
cse->Equip[matslot].EliteModel = item->EliteMaterial; p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial;
cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot); p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
cse->Equip[matslot].Color = color; p_character_select_entry_struct->Equip[matslot].Color = color;
} }
} }
} }
else { 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); buff_ptr += sizeof(CharacterSelectEntry_Struct);
} }

View File

@ -30,7 +30,7 @@ struct CharacterSelect_Struct;
class WorldDatabase : public SharedDatabase { class WorldDatabase : public SharedDatabase {
public: public:
bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc, bool isTitanium); 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); int MoveCharacterToBind(int CharID, uint8 bindnum = 0);
void GetLauncherList(std::vector<std::string> &result); void GetLauncherList(std::vector<std::string> &result);

View File

@ -1057,110 +1057,42 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break; break;
} }
case ServerOP_Consent: { case ServerOP_Consent: {
// Message string id's likely to be used here are: zoneserver_list.SendPacket(pack); // update corpses in all zones
// CONSENT_YOURSELF = 399
// CONSENT_INVALID_NAME = 397
// TARGET_NOT_FOUND = 101
ZoneServer* zs;
ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer;
ClientListEntry* cle = client_list.FindCharacter(s->grantname);
if (cle) {
if (cle->instance() != 0)
{
zs = zoneserver_list.FindByInstanceID(cle->instance());
if (zs) {
zs->SendPacket(pack);
}
else
{
auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct));
ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
strcpy(scs->grantname, s->grantname);
strcpy(scs->ownername, s->ownername);
scs->permission = s->permission;
scs->zone_id = s->zone_id;
scs->instance_id = s->instance_id;
scs->message_string_id = 101;
zs = zoneserver_list.FindByInstanceID(s->instance_id);
if (zs) {
zs->SendPacket(pack);
}
else {
LogInfo("Unable to locate zone record for instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->instance_id);
}
safe_delete(pack);
}
}
else
{
zs = zoneserver_list.FindByZoneID(cle->zone());
if (zs) {
zs->SendPacket(pack);
}
else {
// send target not found back to requester
auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct));
ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
strcpy(scs->grantname, s->grantname);
strcpy(scs->ownername, s->ownername);
scs->permission = s->permission;
scs->zone_id = s->zone_id;
scs->message_string_id = 101;
zs = zoneserver_list.FindByZoneID(s->zone_id);
if (zs) {
zs->SendPacket(pack);
}
else {
LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id);
}
safe_delete(pack);
}
}
}
else {
// send target not found back to requester
auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct));
ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
strcpy(scs->grantname, s->grantname);
strcpy(scs->ownername, s->ownername);
scs->permission = s->permission;
scs->zone_id = s->zone_id;
scs->message_string_id = 397;
zs = zoneserver_list.FindByZoneID(s->zone_id);
if (zs) {
zs->SendPacket(pack);
}
else {
LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id);
}
safe_delete(pack);
}
break; break;
} }
case ServerOP_Consent_Response: { case ServerOP_Consent_Response: {
// Message string id's likely to be used here are:
// CONSENT_YOURSELF = 399
// CONSENT_INVALID_NAME = 397
// TARGET_NOT_FOUND = 101
ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer;
if (s->instance_id != 0)
{ ZoneServer* owner_zs = nullptr;
ZoneServer* zs = zoneserver_list.FindByInstanceID(s->instance_id); if (s->instance_id == 0) {
if (zs) { owner_zs = zoneserver_list.FindByZoneID(s->zone_id);
zs->SendPacket(pack);
}
else {
LogInfo("Unable to locate zone record for instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->instance_id);
}
} }
else else {
{ owner_zs = zoneserver_list.FindByInstanceID(s->instance_id);
ZoneServer* zs = zoneserver_list.FindByZoneID(s->zone_id); }
if (zs) {
zs->SendPacket(pack); if (owner_zs) {
} owner_zs->SendPacket(pack);
else { }
LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); else {
LogInfo("Unable to locate zone record for zone id [{}] or instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id, s->instance_id);
}
if (s->consent_type == EQEmu::consent::Normal) {
// send the message to the client being granted or denied permission
ClientListEntry* cle = client_list.FindCharacter(s->grantname);
if (cle) {
ZoneServer* granted_zs = nullptr;
if (cle->instance() == 0) {
granted_zs = zoneserver_list.FindByZoneID(cle->zone());
}
else {
granted_zs = zoneserver_list.FindByInstanceID(cle->instance());
}
// avoid sending twice if owner and granted are in same zone
if (granted_zs && granted_zs != owner_zs) {
granted_zs->SendPacket(pack);
}
} }
} }
break; break;

View File

@ -1467,12 +1467,20 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
} }
} }
if(IsClient()) { if (IsClient()) {
if(rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) { if (rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) {
return false; return false;
} }
} else { }
if(rank->expansion && !(RuleI(World, ExpansionSettings) & (1 << (rank->expansion - 1)))) { #ifdef BOTS
else if (IsBot()) {
if (rank->expansion && !(RuleI(Bots, BotExpansionSettings) & (1 << (rank->expansion - 1)))) {
return false;
}
}
#endif
else {
if (rank->expansion && !(RuleI(World, ExpansionSettings) & (1 << (rank->expansion - 1)))) {
return false; return false;
} }
} }

View File

@ -36,19 +36,6 @@
extern Zone* zone; extern Zone* zone;
//#define LOSDEBUG 6 //#define LOSDEBUG 6
//look around a client for things which might aggro the client.
void EntityList::CheckClientAggro(Client *around)
{
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
Mob *mob = it->second;
if (mob->IsClient()) //also ensures that mob != around
continue;
if (mob->CheckWillAggro(around) && !mob->CheckAggro(around))
mob->AddToHateList(around, 25);
}
}
void EntityList::DescribeAggro(Client *towho, NPC *from_who, float d, bool verbose) { void EntityList::DescribeAggro(Client *towho, NPC *from_who, float d, bool verbose) {
float d2 = d*d; float d2 = d*d;
@ -402,22 +389,6 @@ bool Mob::CheckWillAggro(Mob *mob) {
return(false); return(false);
} }
Mob* EntityList::AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange) {
if (!sender || !sender->IsNPC())
return(nullptr);
auto it = npc_list.begin();
while (it != npc_list.end()) {
Mob *mob = it->second;
if (sender->CheckWillAggro(mob))
return mob;
++it;
}
return nullptr;
}
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con) int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
{ {
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
@ -462,82 +433,11 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
return Count; return Count;
} }
void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { /**
if(!sender || !attacker) * @param target
return; * @param isSpellAttack
if (sender->GetPrimaryFaction() == 0 ) * @return
return; // well, if we dont have a faction set, we're gonna be indiff to everybody */
if (sender->HasAssistAggro())
return;
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
NPC *mob = it->second;
if (!mob)
continue;
if (mob->CheckAggro(attacker))
continue;
if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap))
break;
float r = mob->GetAssistRange();
r = r * r;
if (
mob != sender
&& mob != attacker
// && !mob->IsCorpse()
// && mob->IsAIControlled()
&& mob->GetPrimaryFaction() != 0
&& DistanceSquared(mob->GetPosition(), sender->GetPosition()) <= r
&& !mob->IsEngaged()
&& ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient()))
// If we're a pet we don't react to any calls for help if our owner is a client
)
{
//if they are in range, make sure we are not green...
//then jump in if they are our friend
if(mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY)
{
bool useprimfaction = false;
if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction())
{
const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID());
if(cf){
if(cf->assistprimaryfaction != 0)
useprimfaction = true;
}
}
if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE )
{
//attacking someone on same faction, or a friend
//Father Nitwit: make sure we can see them.
if(mob->CheckLosFN(sender)) {
#if (EQDEBUG>=5)
LogDebug("AIYellForHelp(\"[{}]\",\"[{}]\") [{}] attacking [{}] Dist [{}] Z [{}]",
sender->GetName(), attacker->GetName(), mob->GetName(),
attacker->GetName(), DistanceSquared(mob->GetPosition(),
sender->GetPosition()), std::abs(sender->GetZ()+mob->GetZ()));
#endif
mob->AddToHateList(attacker, 25, 0, false);
sender->AddAssistCap();
}
}
}
}
}
}
/*
returns false if attack should not be allowed
I try to list every type of conflict that's possible here, so it's easy
to see how the decision is made. Yea, it could be condensed and made
faster, but I'm doing it this way to make it readable and easy to modify
*/
bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
{ {

View File

@ -724,20 +724,20 @@ int Mob::GetClassRaceACBonus()
hardcap = 32; hardcap = 32;
softcap = 15; softcap = 15;
} }
int weight = IsClient() ? CastToClient()->CalcCurrentWeight() : 0; int weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10 : 0;
if (weight < hardcap - 1) { if (weight < hardcap - 1) {
int temp = level + 5; double temp = level + 5;
if (weight > softcap) { if (weight > softcap) {
double redux = (weight - softcap) * 6.66667; double redux = static_cast<double>(weight - softcap) * 6.66667;
redux = (100.0 - std::min(100.0, redux)) * 0.01; redux = (100.0 - std::min(100.0, redux)) * 0.01;
temp = std::max(0, static_cast<int>(temp * redux)); temp = std::max(0.0, temp * redux);
} }
ac_bonus = (4 * temp) / 3; ac_bonus = static_cast<int>((4.0 * temp) / 3.0);
} }
else if (weight > hardcap + 1) { else if (weight > hardcap + 1) {
int temp = level + 5; double temp = level + 5;
double multiplier = std::min(1.0, (weight - (hardcap - 10.0)) / 100.0); double multiplier = std::min(1.0, (weight - (static_cast<double>(hardcap) - 10.0)) / 100.0);
temp = (4 * temp) / 3; temp = (4.0 * temp) / 3.0;
ac_bonus -= static_cast<int>(temp * multiplier); ac_bonus -= static_cast<int>(temp * multiplier);
} }
} }
@ -850,11 +850,12 @@ int Mob::ACSum()
auto over_cap = ac - softcap; auto over_cap = ac - softcap;
ac = softcap + (over_cap * returns); ac = softcap + (over_cap * returns);
} }
LogCombat("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns); LogCombatDetail("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns);
} }
else { else {
LogCombat("ACSum ac [{}]", ac); LogCombatDetail("ACSum ac [{}]", ac);
} }
return ac; return ac;
} }
@ -2465,6 +2466,9 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil
entity_list.UnMarkNPC(GetID()); entity_list.UnMarkNPC(GetID());
entity_list.RemoveNPC(GetID()); entity_list.RemoveNPC(GetID());
// entity_list.RemoveMobFromCloseLists(this);
close_mobs.clear();
this->SetID(0); this->SetID(0);
if (killer != 0 && emoteid != 0) if (killer != 0 && emoteid != 0)

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,7 @@ private:
int m_owner; int m_owner;
int aura_id; // spell ID of the aura spell -1 if aura isn't from a casted spell int aura_id; // spell ID of the aura spell -1 if aura isn't from a casted spell
int spell_id; // spell we cast int spell_id; // spell we cast
int distance; // distance we remove float distance; // distance we remove
Timer remove_timer; // when we depop Timer remove_timer; // when we depop
Timer process_timer; // rate limit process calls Timer process_timer; // rate limit process calls
Timer cast_timer; // some auras pulse Timer cast_timer; // some auras pulse

View File

@ -83,6 +83,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
SetPauseAI(false); SetPauseAI(false);
m_alt_combat_hate_timer.Start(250); m_alt_combat_hate_timer.Start(250);
m_auto_defend_timer.Disable();
//m_combat_jitter_timer.Disable(); //m_combat_jitter_timer.Disable();
//SetCombatJitterFlag(false); //SetCombatJitterFlag(false);
SetGuardFlag(false); SetGuardFlag(false);
@ -180,6 +181,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
SetPauseAI(false); SetPauseAI(false);
m_alt_combat_hate_timer.Start(250); m_alt_combat_hate_timer.Start(250);
m_auto_defend_timer.Disable();
//m_combat_jitter_timer.Disable(); //m_combat_jitter_timer.Disable();
//SetCombatJitterFlag(false); //SetCombatJitterFlag(false);
SetGuardFlag(false); SetGuardFlag(false);
@ -236,8 +238,157 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
LoadAAs(); LoadAAs();
if (!database.botdb.LoadBuffs(this) && bot_owner) // copied from client CompleteConnect() handler - watch for problems
// (may have to move to post-spawn location if certain buffs still don't process correctly)
if (database.botdb.LoadBuffs(this) && bot_owner) {
//reapply some buffs
uint32 buff_count = GetMaxTotalSlots();
for (uint32 j1 = 0; j1 < buff_count; j1++) {
if (!IsValidSpell(buffs[j1].spellid))
continue;
const SPDat_Spell_Struct& spell = spells[buffs[j1].spellid];
int NimbusEffect = GetNimbusEffect(buffs[j1].spellid);
if (NimbusEffect) {
if (!IsNimbusEffectActive(NimbusEffect))
SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true);
}
for (int x1 = 0; x1 < EFFECT_COUNT; x1++) {
switch (spell.effectid[x1]) {
case SE_IllusionCopy:
case SE_Illusion: {
if (spell.base[x1] == -1) {
if (gender == 1)
gender = 0;
else if (gender == 0)
gender = 1;
SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF);
}
else if (spell.base[x1] == -2) // WTF IS THIS
{
if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12)
SendIllusionPacket(GetRace(), GetGender(), spell.base2[x1], spell.max[x1]);
}
else if (spell.max[x1] > 0)
{
SendIllusionPacket(spell.base[x1], 0xFF, spell.base2[x1], spell.max[x1]);
}
else
{
SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF);
}
switch (spell.base[x1]) {
case OGRE:
SendAppearancePacket(AT_Size, 9);
break;
case TROLL:
SendAppearancePacket(AT_Size, 8);
break;
case VAHSHIR:
case BARBARIAN:
SendAppearancePacket(AT_Size, 7);
break;
case HALF_ELF:
case WOOD_ELF:
case DARK_ELF:
case FROGLOK:
SendAppearancePacket(AT_Size, 5);
break;
case DWARF:
SendAppearancePacket(AT_Size, 4);
break;
case HALFLING:
case GNOME:
SendAppearancePacket(AT_Size, 3);
break;
default:
SendAppearancePacket(AT_Size, 6);
break;
}
break;
}
//case SE_SummonHorse: {
// SummonHorse(buffs[j1].spellid);
// //hasmount = true; //this was false, is that the correct thing?
// break;
//}
case SE_Silence:
{
Silence(true);
break;
}
case SE_Amnesia:
{
Amnesia(true);
break;
}
case SE_DivineAura:
{
invulnerable = true;
break;
}
case SE_Invisibility2:
case SE_Invisibility:
{
invisible = true;
SendAppearancePacket(AT_Invis, 1);
break;
}
case SE_Levitate:
{
if (!zone->CanLevitate())
{
//if (!GetGM())
//{
SendAppearancePacket(AT_Levitate, 0);
BuffFadeByEffect(SE_Levitate);
//Message(Chat::Red, "You can't levitate in this zone.");
//}
}
else {
SendAppearancePacket(AT_Levitate, 2);
}
break;
}
case SE_InvisVsUndead2:
case SE_InvisVsUndead:
{
invisible_undead = true;
break;
}
case SE_InvisVsAnimals:
{
invisible_animals = true;
break;
}
case SE_AddMeleeProc:
case SE_WeaponProc:
{
AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid, buffs[j1].casterlevel);
break;
}
case SE_DefensiveProc:
{
AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid);
break;
}
case SE_RangedProc:
{
AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid);
break;
}
}
}
}
}
else {
bot_owner->Message(Chat::Red, "&s for '%s'", BotDatabase::fail::LoadBuffs(), GetCleanName()); bot_owner->Message(Chat::Red, "&s for '%s'", BotDatabase::fail::LoadBuffs(), GetCleanName());
}
CalcBotStats(false); CalcBotStats(false);
hp_regen = CalcHPRegen(); hp_regen = CalcHPRegen();
@ -282,15 +433,61 @@ void Bot::SetBotSpellID(uint32 newSpellID) {
} }
void Bot::SetSurname(std::string bot_surname) { void Bot::SetSurname(std::string bot_surname) {
_surname = bot_surname.substr(0, 31); _surname = bot_surname.substr(0, 31);
if (spawned) {
auto outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct));
GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer;
strcpy(gmn->name, GetCleanName());
strcpy(gmn->gmname, GetCleanName());
strcpy(gmn->lastname, GetSurname().c_str());
gmn->unknown[0] = 1;
gmn->unknown[1] = 1;
gmn->unknown[2] = 1;
gmn->unknown[3] = 1;
entity_list.QueueClients(this, outapp);
safe_delete(outapp);
}
} }
void Bot::SetTitle(std::string bot_title) { void Bot::SetTitle(std::string bot_title) {
_title = bot_title.substr(0, 31);
_title = bot_title.substr(0, 31);
if (spawned) {
auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct));
SetTitleReply_Struct* strs = (SetTitleReply_Struct*)outapp->pBuffer;
strs->is_suffix = 0;
strn0cpy(strs->title, _title.c_str(), sizeof(strs->title));
strs->entity_id = GetID();
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
}
} }
void Bot::SetSuffix(std::string bot_suffix) { void Bot::SetSuffix(std::string bot_suffix) {
_suffix = bot_suffix.substr(0, 31);
_suffix = bot_suffix.substr(0, 31);
if (spawned) {
auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct));
SetTitleReply_Struct* strs = (SetTitleReply_Struct*)outapp->pBuffer;
strs->is_suffix = 1;
strn0cpy(strs->title, _suffix.c_str(), sizeof(strs->title));
strs->entity_id = GetID();
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
}
} }
uint32 Bot::GetBotArcheryRange() { uint32 Bot::GetBotArcheryRange() {
@ -1457,7 +1654,7 @@ int32 Bot::GenerateBaseHitPoints() {
} }
void Bot::LoadAAs() { void Bot::LoadAAs() {
int maxAAExpansion = RuleI(Bots, AAExpansion); //get expansion to get AAs up to
aa_ranks.clear(); aa_ranks.clear();
int id = 0; int id = 0;
@ -2014,6 +2211,17 @@ bool Bot::Process()
return false; return false;
} }
if (mob_scan_close.Check()) {
LogAIScanClose(
"is_moving [{}] bot [{}] timer [{}]",
moving ? "true" : "false",
GetCleanName(),
mob_scan_close.GetDuration()
);
entity_list.ScanCloseClientMobs(close_mobs, this);
}
SpellProcess(); SpellProcess();
if(tic_timer.Check()) { if(tic_timer.Check()) {
@ -2284,7 +2492,7 @@ void Bot::SetTarget(Mob* mob) {
} }
void Bot::SetStopMeleeLevel(uint8 level) { void Bot::SetStopMeleeLevel(uint8 level) {
if (IsCasterClass(GetClass()) || IsSpellFighterClass(GetClass())) if (IsCasterClass(GetClass()) || IsHybridClass(GetClass()))
_stopMeleeLevel = level; _stopMeleeLevel = level;
else else
_stopMeleeLevel = 255; _stopMeleeLevel = 255;
@ -2307,15 +2515,16 @@ void Bot::SetHoldMode() {
} }
// AI Processing for the Bot object // AI Processing for the Bot object
constexpr float MAX_CASTER_DISTANCE[PLAYER_CLASS_COUNT] = {
0, (34 * 34), (24 * 24), (28 * 28), (26 * 26), (42 * 42), 0, (30 * 30), 0, (38 * 38), (54 * 54), (48 * 48), (52 * 52), (50 * 50), (32 * 32), 0
// W C P R S D M B R S N W M E B B
// A L A N H R N R O H E I A N S E
// R R L G D U K D G M C Z G C T R
};
void Bot::AI_Process() void Bot::AI_Process()
{ {
constexpr float MAX_CASTER_DISTANCE[PLAYER_CLASS_COUNT] = {
0, (34 * 34), (24 * 24), (28 * 28), (26 * 26), (42 * 42), 0, 0, 0, (38 * 38), (54 * 54), (48 * 48), (52 * 52), (50 * 50), (30 * 30), 0
// W C P R S D M B R S N W M E B B
// A L A N H R N R O H E I A N S E
// R R L G D U K D G M C Z G C T R
};
#define TEST_COMBATANTS() if (!GetTarget() || GetAppearance() == eaDead) { return; } #define TEST_COMBATANTS() if (!GetTarget() || GetAppearance() == eaDead) { return; }
#define PULLING_BOT (GetPullingFlag() || GetReturningFlag()) #define PULLING_BOT (GetPullingFlag() || GetReturningFlag())
#define NOT_PULLING_BOT (!GetPullingFlag() && !GetReturningFlag()) #define NOT_PULLING_BOT (!GetPullingFlag() && !GetReturningFlag())
@ -2627,7 +2836,7 @@ void Bot::AI_Process()
return; return;
} }
else if (HasTargetReflection()) { else if (GetTarget()->GetHateList().size()) {
WipeHateList(); WipeHateList();
SetTarget(nullptr); SetTarget(nullptr);
@ -2711,14 +2920,14 @@ void Bot::AI_Process()
if (find_target) { if (find_target) {
if (IsRooted()) { if (IsRooted()) {
SetTarget(hate_list.GetClosestEntOnHateList(this)); SetTarget(hate_list.GetClosestEntOnHateList(this, true));
} }
else { else {
// This will keep bots on target for now..but, future updates will allow for rooting/stunning // This will keep bots on target for now..but, future updates will allow for rooting/stunning
SetTarget(hate_list.GetEscapingEntOnHateList(leash_owner, leash_distance)); SetTarget(hate_list.GetEscapingEntOnHateList(leash_owner, leash_distance));
if (!GetTarget()) { if (!GetTarget()) {
SetTarget(hate_list.GetEntWithMostHateOnList(this)); SetTarget(hate_list.GetEntWithMostHateOnList(this, nullptr, true));
} }
} }
} }
@ -3192,7 +3401,7 @@ void Bot::AI_Process()
BotRangedAttack(tar); BotRangedAttack(tar);
} }
} }
else if (!IsBotArcher() && (IsBotNonSpellFighter() || GetLevel() < GetStopMeleeLevel())) { else if (!IsBotArcher() && GetLevel() < GetStopMeleeLevel()) {
// We can't fight if we don't have a target, are stun/mezzed or dead.. // We can't fight if we don't have a target, are stun/mezzed or dead..
// Stop attacking if the target is enraged // Stop attacking if the target is enraged
@ -3400,9 +3609,15 @@ void Bot::AI_Process()
// This is as close as I could get without modifying the aggro mechanics and making it an expensive process... // This is as close as I could get without modifying the aggro mechanics and making it an expensive process...
// 'class Client' doesn't make use of hate_list... // 'class Client' doesn't make use of hate_list...
if (bot_owner->GetAggroCount() && bot_owner->GetBotOption(Client::booAutoDefend)) { if (RuleB(Bots, AllowOwnerOptionAutoDefend) && bot_owner->GetBotOption(Client::booAutoDefend)) {
if (RuleB(Bots, AllowOwnerOptionAutoDefend)) { if (!m_auto_defend_timer.Enabled()) {
m_auto_defend_timer.Start(zone->random.Int(250, 1250)); // random timer to simulate 'awareness' (cuts down on scanning overhead)
return;
}
if (m_auto_defend_timer.Check() && bot_owner->GetAggroCount()) {
if (NOT_HOLDING && NOT_PASSIVE) { if (NOT_HOLDING && NOT_PASSIVE) {
@ -3420,7 +3635,7 @@ void Bot::AI_Process()
} }
auto hater = entity_list.GetMob(hater_iter.spawn_id); auto hater = entity_list.GetMob(hater_iter.spawn_id);
if (hater && DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) { if (hater && !hater->IsMezzed() && DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) {
// This is roughly equivilent to npc attacking a client pet owner // This is roughly equivilent to npc attacking a client pet owner
AddToHateList(hater, 1); AddToHateList(hater, 1);
@ -3432,6 +3647,8 @@ void Bot::AI_Process()
GetPet()->SetTarget(hater); GetPet()->SetTarget(hater);
} }
m_auto_defend_timer.Disable();
return; return;
} }
} }
@ -4726,9 +4943,9 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::Sk
Mob *my_owner = GetBotOwner(); Mob *my_owner = GetBotOwner();
if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOption(Client::booDeathMarquee)) { if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOption(Client::booDeathMarquee)) {
if (killerMob) if (killerMob)
my_owner->CastToClient()->SendMarqueeMessage(Chat::Yellow, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killerMob->GetCleanName())); my_owner->CastToClient()->SendMarqueeMessage(Chat::Red, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killerMob->GetCleanName()));
else else
my_owner->CastToClient()->SendMarqueeMessage(Chat::Yellow, 510, 0, 1000, 3000, StringFormat("%s has been slain", GetCleanName())); my_owner->CastToClient()->SendMarqueeMessage(Chat::Red, 510, 0, 1000, 3000, StringFormat("%s has been slain", GetCleanName()));
} }
Mob *give_exp = hate_list.GetDamageTopOnHateList(this); Mob *give_exp = hate_list.GetDamageTopOnHateList(this);
@ -5017,7 +5234,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
return false; return false;
} }
int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 spell_id) int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 spell_id)
{ {
const SPDat_Spell_Struct &spell = spells[spell_id]; const SPDat_Spell_Struct &spell = spells[spell_id];
int32 value = 0; int32 value = 0;
@ -5171,6 +5388,10 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16
if (type == focusImprovedDamage && base1 > value) if (type == focusImprovedDamage && base1 > value)
value = base1; value = base1;
break; break;
case SE_ImprovedDamage2:
if (type == focusImprovedDamage2 && base1 > value)
value = base1;
break;
case SE_ImprovedHeal: case SE_ImprovedHeal:
if (type == focusImprovedHeal && base1 > value) if (type == focusImprovedHeal && base1 > value)
value = base1; value = base1;
@ -5282,6 +5503,11 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16
value = base1; value = base1;
break; break;
} }
case SE_FcDamageAmt2: {
if(type == focusFcDamageAmt2)
value = base1;
break;
}
case SE_FcDamageAmtCrit: { case SE_FcDamageAmtCrit: {
if(type == focusFcDamageAmtCrit) if(type == focusFcDamageAmtCrit)
value = base1; value = base1;
@ -5340,8 +5566,8 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16
return (value * lvlModifier / 100); return (value * lvlModifier / 100);
} }
int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { int32 Bot::GetBotFocusEffect(focusType bottype, uint16 spell_id) {
if (IsBardSong(spell_id) && bottype != BotfocusFcBaseEffects) if (IsBardSong(spell_id) && bottype != focusFcBaseEffects)
return 0; return 0;
int32 realTotal = 0; int32 realTotal = 0;
@ -5350,7 +5576,7 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) {
bool rand_effectiveness = false; bool rand_effectiveness = false;
//Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages
//In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance
if((bottype == BotfocusManaCost || bottype == BotfocusImprovedHeal || bottype == BotfocusImprovedDamage) && RuleB(Spells, LiveLikeFocusEffects)) if(RuleB(Spells, LiveLikeFocusEffects) && (bottype == focusManaCost || bottype == focusImprovedHeal || bottype == focusImprovedDamage || bottype == focusImprovedDamage2 || bottype == focusResistRate))
rand_effectiveness = true; rand_effectiveness = true;
//Check if item focus effect exists for the client. //Check if item focus effect exists for the client.
@ -5491,16 +5717,16 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) {
} }
} }
if(bottype == BotfocusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) if(bottype == focusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact))
return 100; return 100;
if(bottype == BotfocusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))) if(bottype == focusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id)))
return 0; return 0;
return (realTotal + realTotal2); return (realTotal + realTotal2);
} }
int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) { int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) {
if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id))
return 0; return 0;
@ -5630,7 +5856,21 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
return 0; return 0;
break; break;
case SE_ImprovedDamage: case SE_ImprovedDamage:
if (bottype == BotfocusImprovedDamage) { if (bottype == focusImprovedDamage) {
if(best_focus) {
if (focus_spell.base2[i] != 0)
value = focus_spell.base2[i];
else
value = focus_spell.base[i];
}
else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i])
value = focus_spell.base[i];
else
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]);
}
break;
case SE_ImprovedDamage2:
if (bottype == focusImprovedDamage2) {
if(best_focus) { if(best_focus) {
if (focus_spell.base2[i] != 0) if (focus_spell.base2[i] != 0)
value = focus_spell.base2[i]; value = focus_spell.base2[i];
@ -5644,7 +5884,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
} }
break; break;
case SE_ImprovedHeal: case SE_ImprovedHeal:
if (bottype == BotfocusImprovedHeal) { if (bottype == focusImprovedHeal) {
if(best_focus) { if(best_focus) {
if (focus_spell.base2[i] != 0) if (focus_spell.base2[i] != 0)
value = focus_spell.base2[i]; value = focus_spell.base2[i];
@ -5658,7 +5898,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
} }
break; break;
case SE_ReduceManaCost: case SE_ReduceManaCost:
if (bottype == BotfocusManaCost) { if (bottype == focusManaCost) {
if(best_focus) { if(best_focus) {
if (focus_spell.base2[i] != 0) if (focus_spell.base2[i] != 0)
value = focus_spell.base2[i]; value = focus_spell.base2[i];
@ -5672,39 +5912,39 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
} }
break; break;
case SE_IncreaseSpellHaste: case SE_IncreaseSpellHaste:
if (bottype == BotfocusSpellHaste && focus_spell.base[i] > value) if (bottype == focusSpellHaste && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_IncreaseSpellDuration: case SE_IncreaseSpellDuration:
if (bottype == BotfocusSpellDuration && focus_spell.base[i] > value) if (bottype == focusSpellDuration && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_SpellDurationIncByTic: case SE_SpellDurationIncByTic:
if (bottype == BotfocusSpellDurByTic && focus_spell.base[i] > value) if (bottype == focusSpellDurByTic && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_SwarmPetDuration: case SE_SwarmPetDuration:
if (bottype == BotfocusSwarmPetDuration && focus_spell.base[i] > value) if (bottype == focusSwarmPetDuration && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_IncreaseRange: case SE_IncreaseRange:
if (bottype == BotfocusRange && focus_spell.base[i] > value) if (bottype == focusRange && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_ReduceReagentCost: case SE_ReduceReagentCost:
if (bottype == BotfocusReagentCost && focus_spell.base[i] > value) if (bottype == focusReagentCost && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_PetPowerIncrease: case SE_PetPowerIncrease:
if (bottype == BotfocusPetPower && focus_spell.base[i] > value) if (bottype == focusPetPower && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_SpellResistReduction: case SE_SpellResistReduction:
if (bottype == BotfocusResistRate && focus_spell.base[i] > value) if (bottype == focusResistRate && focus_spell.base[i] > value)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_SpellHateMod: case SE_SpellHateMod:
if (bottype == BotfocusSpellHateMod) { if (bottype == focusSpellHateMod) {
if(value != 0) { if(value != 0) {
if(value > 0) { if(value > 0) {
if(focus_spell.base[i] > value) if(focus_spell.base[i] > value)
@ -5719,12 +5959,12 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
} }
break; break;
case SE_ReduceReuseTimer: { case SE_ReduceReuseTimer: {
if(bottype == BotfocusReduceRecastTime) if(bottype == focusReduceRecastTime)
value = (focus_spell.base[i] / 1000); value = (focus_spell.base[i] / 1000);
break; break;
} }
case SE_TriggerOnCast: { case SE_TriggerOnCast: {
if(bottype == BotfocusTriggerOnCast) { if(bottype == focusTriggerOnCast) {
if(zone->random.Int(0, 100) <= focus_spell.base[i]) if(zone->random.Int(0, 100) <= focus_spell.base[i])
value = focus_spell.base2[i]; value = focus_spell.base2[i];
else else
@ -5733,24 +5973,24 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
break; break;
} }
case SE_FcSpellVulnerability: { case SE_FcSpellVulnerability: {
if(bottype == BotfocusSpellVulnerability) if(bottype == focusSpellVulnerability)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
} }
case SE_BlockNextSpellFocus: { case SE_BlockNextSpellFocus: {
if(bottype == BotfocusBlockNextSpell) { if(bottype == focusBlockNextSpell) {
if(zone->random.Int(1, 100) <= focus_spell.base[i]) if(zone->random.Int(1, 100) <= focus_spell.base[i])
value = 1; value = 1;
} }
break; break;
} }
case SE_FcTwincast: { case SE_FcTwincast: {
if(bottype == BotfocusTwincast) if(bottype == focusTwincast)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
} }
case SE_SympatheticProc: { case SE_SympatheticProc: {
if(bottype == BotfocusSympatheticProc) { if(bottype == focusSympatheticProc) {
float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]); float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]);
if(zone->random.Real(0, 1) <= ProcChance) if(zone->random.Real(0, 1) <= ProcChance)
value = focus_id; value = focus_id;
@ -5760,49 +6000,54 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
break; break;
} }
case SE_FcDamageAmt: { case SE_FcDamageAmt: {
if(bottype == BotfocusFcDamageAmt) if(bottype == focusFcDamageAmt)
value = focus_spell.base[i];
break;
}
case SE_FcDamageAmt2: {
if(bottype == focusFcDamageAmt2)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
} }
case SE_FcDamageAmtCrit: { case SE_FcDamageAmtCrit: {
if(bottype == BotfocusFcDamageAmtCrit) if(bottype == focusFcDamageAmtCrit)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
} }
case SE_FcHealAmtIncoming: case SE_FcHealAmtIncoming:
if(bottype == BotfocusFcHealAmtIncoming) if(bottype == focusFcHealAmtIncoming)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_FcHealPctCritIncoming: case SE_FcHealPctCritIncoming:
if (bottype == BotfocusFcHealPctCritIncoming) if (bottype == focusFcHealPctCritIncoming)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_FcHealAmtCrit: case SE_FcHealAmtCrit:
if(bottype == BotfocusFcHealAmtCrit) if(bottype == focusFcHealAmtCrit)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_FcHealAmt: case SE_FcHealAmt:
if(bottype == BotfocusFcHealAmt) if(bottype == focusFcHealAmt)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_FcHealPctIncoming: case SE_FcHealPctIncoming:
if(bottype == BotfocusFcHealPctIncoming) if(bottype == focusFcHealPctIncoming)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
case SE_FcBaseEffects: { case SE_FcBaseEffects: {
if (bottype == BotfocusFcBaseEffects) if (bottype == focusFcBaseEffects)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
} }
case SE_FcDamagePctCrit: { case SE_FcDamagePctCrit: {
if(bottype == BotfocusFcDamagePctCrit) if(bottype == focusFcDamagePctCrit)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
} }
case SE_FcIncreaseNumHits: { case SE_FcIncreaseNumHits: {
if(bottype == BotfocusIncreaseNumHits) if(bottype == focusIncreaseNumHits)
value = focus_spell.base[i]; value = focus_spell.base[i];
break; break;
@ -6342,14 +6587,14 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
int32 Bot::CheckAggroAmount(uint16 spellid) { int32 Bot::CheckAggroAmount(uint16 spellid) {
int32 AggroAmount = Mob::CheckAggroAmount(spellid, nullptr); int32 AggroAmount = Mob::CheckAggroAmount(spellid, nullptr);
int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); int32 focusAggro = GetBotFocusEffect(focusSpellHateMod, spellid);
AggroAmount = (AggroAmount * (100 + focusAggro) / 100); AggroAmount = (AggroAmount * (100 + focusAggro) / 100);
return AggroAmount; return AggroAmount;
} }
int32 Bot::CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 heal_possible) { int32 Bot::CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 heal_possible) {
int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, target, heal_possible); int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, target, heal_possible);
int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); int32 focusAggro = GetBotFocusEffect(focusSpellHateMod, spellid);
AggroAmount = (AggroAmount * (100 + focusAggro) / 100); AggroAmount = (AggroAmount * (100 + focusAggro) / 100);
return AggroAmount; return AggroAmount;
} }
@ -6623,7 +6868,7 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
bool Critical = false; bool Critical = false;
int32 value_BaseEffect = 0; int32 value_BaseEffect = 0;
value_BaseEffect = (value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id) / 100)); value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100));
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
value -= ((GetLevel() - 40) * 20); value -= ((GetLevel() - 40) * 20);
@ -6651,16 +6896,18 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
ratio += RuleI(Spells, WizCritRatio); ratio += RuleI(Spells, WizCritRatio);
if (Critical) { if (Critical) {
value = (value_BaseEffect * ratio / 100); value = (value_BaseEffect * ratio / 100);
value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100); value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100);
value += (int(value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100) * ratio / 100); value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage2, spell_id) / 100);
value += (int(value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100) * ratio / 100);
if (target) { if (target) {
value += (int(value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100) * ratio / 100); value += (int(value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100) * ratio / 100);
value -= target->GetFcDamageAmtIncoming(this, spell_id); value -= target->GetFcDamageAmtIncoming(this, spell_id);
} }
value -= (GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id) * ratio / 100); value -= (GetBotFocusEffect(focusFcDamageAmtCrit, spell_id) * ratio / 100);
value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); value -= GetBotFocusEffect(focusFcDamageAmt, spell_id);
value -= GetBotFocusEffect(focusFcDamageAmt2, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5)
value += (GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value) * ratio / 100); value += (GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value) * ratio / 100);
@ -6672,15 +6919,17 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
} }
value = value_BaseEffect; value = value_BaseEffect;
value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100); value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100);
value += (value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100); value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage2, spell_id) / 100);
value += (value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100);
if (target) { if (target) {
value += (value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100); value += (value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100);
value -= target->GetFcDamageAmtIncoming(this, spell_id); value -= target->GetFcDamageAmtIncoming(this, spell_id);
} }
value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); value -= GetBotFocusEffect(focusFcDamageAmtCrit, spell_id);
value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); value -= GetBotFocusEffect(focusFcDamageAmt, spell_id);
value -= GetBotFocusEffect(focusFcDamageAmt2, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5)
value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
@ -6695,9 +6944,9 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
int32 chance = 0; int32 chance = 0;
int8 modifier = 1; int8 modifier = 1;
bool Critical = false; bool Critical = false;
value_BaseEffect = (value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id) / 100)); value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100));
value = value_BaseEffect; value = value_BaseEffect;
value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id) / 100); value += int(value_BaseEffect*GetBotFocusEffect(focusImprovedHeal, spell_id) / 100);
if(spells[spell_id].buffduration < 1) { if(spells[spell_id].buffduration < 1) {
chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance); chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance);
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
@ -6710,8 +6959,8 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
} }
value *= modifier; value *= modifier;
value += (GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier); value += (GetBotFocusEffect(focusFcHealAmtCrit, spell_id) * modifier);
value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); value += GetBotFocusEffect(focusFcHealAmt, spell_id);
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5)
@ -6736,7 +6985,7 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) {
int32 cast_reducer = 0; int32 cast_reducer = 0;
cast_reducer += GetBotFocusEffect(BotfocusSpellHaste, spell_id); cast_reducer += GetBotFocusEffect(focusSpellHaste, spell_id);
uint8 botlevel = GetLevel(); uint8 botlevel = GetLevel();
uint8 botclass = GetClass(); uint8 botclass = GetClass();
if (botlevel >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) && (botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD )) if (botlevel >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) && (botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD ))
@ -6871,7 +7120,7 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) {
} }
} }
int32 focus_redux = GetBotFocusEffect(BotfocusManaCost, spell_id); int32 focus_redux = GetBotFocusEffect(focusManaCost, spell_id);
if(focus_redux > 0) if(focus_redux > 0)
PercentManaReduction += zone->random.Real(1, (double)focus_redux); PercentManaReduction += zone->random.Real(1, (double)focus_redux);
@ -6898,14 +7147,14 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) {
float Bot::GetActSpellRange(uint16 spell_id, float range) { float Bot::GetActSpellRange(uint16 spell_id, float range) {
float extrange = 100; float extrange = 100;
extrange += GetBotFocusEffect(BotfocusRange, spell_id); extrange += GetBotFocusEffect(focusRange, spell_id);
return ((range * extrange) / 100); return ((range * extrange) / 100);
} }
int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) { int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) {
int increase = 100; int increase = 100;
increase += GetBotFocusEffect(BotfocusSpellDuration, spell_id); increase += GetBotFocusEffect(focusSpellDuration, spell_id);
int tic_inc = 0; tic_inc = GetBotFocusEffect(BotfocusSpellDurByTic, spell_id); int tic_inc = 0; tic_inc = GetBotFocusEffect(focusSpellDurByTic, spell_id);
if(IsBeneficialSpell(spell_id)) { if(IsBeneficialSpell(spell_id)) {
switch (GetAA(aaSpellCastingReinforcement)) { switch (GetAA(aaSpellCastingReinforcement)) {
@ -8703,15 +8952,21 @@ void Bot::CalcBotStats(bool showtext) {
GetBotOwner()->Message(Chat::Yellow, "Updating %s...", GetCleanName()); GetBotOwner()->Message(Chat::Yellow, "Updating %s...", GetCleanName());
} }
if(!IsValidRaceClassCombo()) { // this code is annoying since many classes change their name and illusions change the race id
/*if(!IsValidRaceClassCombo()) {
GetBotOwner()->Message(Chat::Yellow, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceIDName(GetRace()), GetClassIDName(GetClass(), GetLevel())); GetBotOwner()->Message(Chat::Yellow, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceIDName(GetRace()), GetClassIDName(GetClass(), GetLevel()));
GetBotOwner()->Message(Chat::Yellow, "Previous Bots Code releases did not check Race/Class combinations during create."); GetBotOwner()->Message(Chat::Yellow, "Previous Bots Code releases did not check Race/Class combinations during create.");
GetBotOwner()->Message(Chat::Yellow, "Unless you are experiencing heavy lag, you should delete and remake this bot."); GetBotOwner()->Message(Chat::Yellow, "Unless you are experiencing heavy lag, you should delete and remake this bot.");
} }*/
if(GetBotOwner()->GetLevel() != GetLevel()) if(GetBotOwner()->GetLevel() != GetLevel())
SetLevel(GetBotOwner()->GetLevel()); SetLevel(GetBotOwner()->GetLevel());
for (int sindex = 0; sindex <= EQEmu::skills::HIGHEST_SKILL; ++sindex) {
skills[sindex] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)sindex, GetLevel());
}
LoadAAs();
GenerateSpecialAttacks(); GenerateSpecialAttacks();
if(showtext) { if(showtext) {
@ -8989,6 +9244,20 @@ Bot* EntityList::GetBotByBotName(std::string botName) {
return Result; return Result;
} }
Client* EntityList::GetBotOwnerByBotEntityID(uint16 entityID) {
Client* Result = nullptr;
if (entityID > 0) {
for (std::list<Bot*>::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) {
Bot* tempBot = *botListItr;
if (tempBot && tempBot->GetID() == entityID) {
Result = tempBot->GetBotOwner()->CastToClient();
break;
}
}
}
return Result;
}
void EntityList::AddBot(Bot *newBot, bool SendSpawnPacket, bool dontqueue) { void EntityList::AddBot(Bot *newBot, bool SendSpawnPacket, bool dontqueue) {
if(newBot) { if(newBot) {
newBot->SetID(GetFreeID()); newBot->SetID(GetFreeID());
@ -9151,6 +9420,39 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) {
return; return;
} }
/**
* @param close_mobs
* @param scanning_mob
*/
void EntityList::ScanCloseClientMobs(std::unordered_map<uint16, Mob*>& close_mobs, Mob* scanning_mob)
{
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
close_mobs.clear();
for (auto& e : mob_list) {
auto mob = e.second;
if (!mob->IsClient()) {
continue;
}
if (mob->GetID() <= 0) {
continue;
}
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range) {
close_mobs.insert(std::pair<uint16, Mob*>(mob->GetID(), mob));
}
else if (mob->GetAggroRange() >= scan_range) {
close_mobs.insert(std::pair<uint16, Mob*>(mob->GetID(), mob));
}
}
LogAIScanClose("Close Client Mob List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName());
}
uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets) { uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets) {
uint8 needHealed = 0; uint8 needHealed = 0;
Group *g = nullptr; Group *g = nullptr;

View File

@ -99,40 +99,6 @@ class Bot : public NPC {
friend class Mob; friend class Mob;
public: public:
// Class enums // Class enums
enum BotfocusType { //focus types
BotfocusSpellHaste = 1,
BotfocusSpellDuration,
BotfocusRange,
BotfocusReagentCost,
BotfocusManaCost,
BotfocusImprovedHeal,
BotfocusImprovedDamage,
BotfocusImprovedDOT, //i dont know about this...
BotfocusFcDamagePctCrit,
BotfocusImprovedUndeadDamage,
BotfocusPetPower,
BotfocusResistRate,
BotfocusSpellHateMod,
BotfocusTriggerOnCast,
BotfocusSpellVulnerability,
BotfocusTwincast,
BotfocusSympatheticProc,
BotfocusFcDamageAmt,
BotfocusFcDamageAmtCrit,
BotfocusSpellDurByTic,
BotfocusSwarmPetDuration,
BotfocusReduceRecastTime,
BotfocusBlockNextSpell,
BotfocusFcHealPctIncoming,
BotfocusFcDamageAmtIncoming,
BotfocusFcHealAmtIncoming,
BotfocusFcBaseEffects,
BotfocusIncreaseNumHits,
BotfocusFcHealPctCritIncoming,
BotfocusFcHealAmt,
BotfocusFcHealAmtCrit,
};
enum BotTradeType { // types of trades a bot can do enum BotTradeType { // types of trades a bot can do
BotTradeClientNormal, BotTradeClientNormal,
BotTradeClientNoDropNoTrade BotTradeClientNoDropNoTrade
@ -383,6 +349,7 @@ public:
void EquipBot(std::string* errorMessage); void EquipBot(std::string* errorMessage);
bool CheckLoreConflict(const EQEmu::ItemData* item); bool CheckLoreConflict(const EQEmu::ItemData* item);
virtual void UpdateEquipmentLight() { m_Light.Type[EQEmu::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQEmu::lightsource::LightEquipment] = EQEmu::lightsource::TypeToLevel(m_Light.Type[EQEmu::lightsource::LightEquipment]); } virtual void UpdateEquipmentLight() { m_Light.Type[EQEmu::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQEmu::lightsource::LightEquipment] = EQEmu::lightsource::TypeToLevel(m_Light.Type[EQEmu::lightsource::LightEquipment]); }
const EQEmu::InventoryProfile& GetBotInv() const { return m_inv; }
// Static Class Methods // Static Class Methods
//static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped //static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped
@ -635,9 +602,9 @@ protected:
virtual void PetAIProcess(); virtual void PetAIProcess();
virtual void BotMeditate(bool isSitting); virtual void BotMeditate(bool isSitting);
virtual bool CheckBotDoubleAttack(bool Triple = false); virtual bool CheckBotDoubleAttack(bool Triple = false);
virtual int32 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id); virtual int32 GetBotFocusEffect(focusType bottype, uint16 spell_id);
virtual int32 CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false); virtual int32 CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false);
virtual int32 CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 spell_id); virtual int32 CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 spell_id);
virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client); virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client);
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
@ -692,6 +659,7 @@ private:
Timer m_evade_timer; // can be moved to pTimers at some point Timer m_evade_timer; // can be moved to pTimers at some point
Timer m_alt_combat_hate_timer; Timer m_alt_combat_hate_timer;
Timer m_auto_defend_timer;
//Timer m_combat_jitter_timer; //Timer m_combat_jitter_timer;
//bool m_combat_jitter_flag; //bool m_combat_jitter_flag;
bool m_guard_flag; bool m_guard_flag;

View File

@ -1321,6 +1321,8 @@ int bot_command_init(void)
if ( if (
bot_command_add("actionable", "Lists actionable command arguments and use descriptions", 0, bot_command_actionable) || bot_command_add("actionable", "Lists actionable command arguments and use descriptions", 0, bot_command_actionable) ||
bot_command_add("aggressive", "Orders a bot to use a aggressive discipline", 0, bot_command_aggressive) || bot_command_add("aggressive", "Orders a bot to use a aggressive discipline", 0, bot_command_aggressive) ||
bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", 0, bot_command_apply_poison) ||
bot_command_add("applypotion", "Applies cursor-held potion to a bot's effects", 0, bot_command_apply_potion) ||
bot_command_add("attack", "Orders bots to attack a designated target", 0, bot_command_attack) || bot_command_add("attack", "Orders bots to attack a designated target", 0, bot_command_attack) ||
bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", 0, bot_command_bind_affinity) || bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", 0, bot_command_bind_affinity) ||
bot_command_add("bot", "Lists the available bot management [subcommands]", 0, bot_command_bot) || bot_command_add("bot", "Lists the available bot management [subcommands]", 0, bot_command_bot) ||
@ -1369,7 +1371,7 @@ int bot_command_init(void)
bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", 0, bot_command_depart) || bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", 0, bot_command_depart) ||
bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", 0, bot_command_escape) || bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", 0, bot_command_escape) ||
bot_command_add("findaliases", "Find available aliases for a bot command", 0, bot_command_find_aliases) || bot_command_add("findaliases", "Find available aliases for a bot command", 0, bot_command_find_aliases) ||
bot_command_add("follow", "Orders bots to follow a designated target", 0, bot_command_follow) || bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", 0, bot_command_follow) ||
bot_command_add("guard", "Orders bots to guard their current positions", 0, bot_command_guard) || bot_command_add("guard", "Orders bots to guard their current positions", 0, bot_command_guard) ||
bot_command_add("healrotation", "Lists the available bot heal rotation [subcommands]", 0, bot_command_heal_rotation) || bot_command_add("healrotation", "Lists the available bot heal rotation [subcommands]", 0, bot_command_heal_rotation) ||
bot_command_add("healrotationadaptivetargeting", "Enables or disables adaptive targeting within the heal rotation instance", 0, bot_subcommand_heal_rotation_adaptive_targeting) || bot_command_add("healrotationadaptivetargeting", "Enables or disables adaptive targeting within the heal rotation instance", 0, bot_subcommand_heal_rotation_adaptive_targeting) ||
@ -1401,6 +1403,7 @@ int bot_command_init(void)
bot_command_add("inventoryremove", "Removes an item from a bot's inventory", 0, bot_subcommand_inventory_remove) || bot_command_add("inventoryremove", "Removes an item from a bot's inventory", 0, bot_subcommand_inventory_remove) ||
bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", 0, bot_subcommand_inventory_window) || bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", 0, bot_subcommand_inventory_window) ||
bot_command_add("invisibility", "Orders a bot to cast a cloak of invisibility, or allow them to be seen", 0, bot_command_invisibility) || bot_command_add("invisibility", "Orders a bot to cast a cloak of invisibility, or allow them to be seen", 0, bot_command_invisibility) ||
bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", 0, bot_command_item_use) ||
bot_command_add("levitation", "Orders a bot to cast a levitation spell", 0, bot_command_levitation) || bot_command_add("levitation", "Orders a bot to cast a levitation spell", 0, bot_command_levitation) ||
bot_command_add("lull", "Orders a bot to cast a pacification spell", 0, bot_command_lull) || bot_command_add("lull", "Orders a bot to cast a pacification spell", 0, bot_command_lull) ||
bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", 0, bot_command_mesmerize) || bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", 0, bot_command_mesmerize) ||
@ -2579,6 +2582,166 @@ void bot_command_aggressive(Client *c, const Seperator *sep)
c->Message(m_action, "%i of %i bots have used aggressive disciplines", success_count, candidate_count); c->Message(m_action, "%i of %i bots have used aggressive disciplines", success_count, candidate_count);
} }
void bot_command_apply_poison(Client *c, const Seperator *sep)
{
if (helper_command_disabled(c, RuleB(Bots, AllowApplyPoisonCommand), "applypoison")) {
return;
}
if (helper_command_alias_fail(c, "bot_command_apply_poison", sep->arg[0], "applypoison")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: <rogue_bot_target> %s", sep->arg[0]);
return;
}
Bot *my_rogue_bot = nullptr;
if (c->GetTarget() && c->GetTarget()->IsBot() && c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID() && c->GetTarget()->CastToBot()->GetClass() == ROGUE) {
my_rogue_bot = c->GetTarget()->CastToBot();
}
if (!my_rogue_bot) {
c->Message(m_fail, "You must target a rogue bot that you own to use this command!");
return;
}
if (my_rogue_bot->GetLevel() < 18) {
c->Message(m_fail, "Your rogue bot must be level 18 before %s can apply poison!", (my_rogue_bot->GetGender() == 1 ? "she" : "he"));
return;
}
const auto poison_instance = c->GetInv().GetItem(EQEmu::invslot::slotCursor);
if (!poison_instance) {
c->Message(m_fail, "No item found on cursor!");
return;
}
auto poison_data = poison_instance->GetItem();
if (!poison_data) {
c->Message(m_fail, "No data found for cursor item!");
return;
}
if (poison_data->ItemType == EQEmu::item::ItemTypePoison) {
if ((~poison_data->Races) & GetPlayerRaceBit(my_rogue_bot->GetRace())) {
c->Message(m_fail, "Invalid race for weapon poison!");
return;
}
if (poison_data->Proc.Level2 > my_rogue_bot->GetLevel()) {
c->Message(m_fail, "This poison is too powerful for your intended target!");
return;
}
// generalized from client ApplyPoison handler
double ChanceRoll = zone->random.Real(0, 1);
uint16 poison_skill = 95 + ((my_rogue_bot->GetLevel() - 18) * 5);
if (poison_skill > 200) {
poison_skill = 200;
}
bool apply_poison_chance = (ChanceRoll < (.75 + poison_skill / 1000));
if (apply_poison_chance && my_rogue_bot->AddProcToWeapon(poison_data->Proc.Effect, false, (my_rogue_bot->GetDEX() / 100) + 103, POISON_PROC)) {
c->Message(m_action, "Successfully applied %s to %s's weapon.", poison_data->Name, my_rogue_bot->GetCleanName());
}
else {
c->Message(m_fail, "Failed to apply %s to %s's weapon.", poison_data->Name, my_rogue_bot->GetCleanName());
}
c->DeleteItemInInventory(EQEmu::invslot::slotCursor, 1, true);
}
else {
c->Message(m_fail, "Item on cursor is not a weapon poison!");
return;
}
}
void bot_command_apply_potion(Client* c, const Seperator* sep)
{
if (helper_command_disabled(c, RuleB(Bots, AllowApplyPotionCommand), "applypotion")) {
return;
}
if (helper_command_alias_fail(c, "bot_command_apply_potion", sep->arg[0], "applypotion")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: <bot_target> %s", sep->arg[0]);
return;
}
Bot* my_bot = nullptr;
if (c->GetTarget() && c->GetTarget()->IsBot() && c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) {
my_bot = c->GetTarget()->CastToBot();
}
if (!my_bot) {
c->Message(m_fail, "You must target a bot that you own to use this command!");
return;
}
const auto potion_instance = c->GetInv().GetItem(EQEmu::invslot::slotCursor);
if (!potion_instance) {
c->Message(m_fail, "No item found on cursor!");
return;
}
auto potion_data = potion_instance->GetItem();
if (!potion_data) {
c->Message(m_fail, "No data found for cursor item!");
return;
}
if (potion_data->ItemType == EQEmu::item::ItemTypePotion && potion_data->Click.Effect > 0) {
if (RuleB(Bots, RestrictApplyPotionToRogue) && potion_data->Classes != PLAYER_CLASS_ROGUE_BIT) {
c->Message(m_fail, "This command is restricted to rogue poison potions only!");
return;
}
if ((~potion_data->Races) & GetPlayerRaceBit(my_bot->GetRace())) {
c->Message(m_fail, "Invalid race for potion!");
return;
}
if ((~potion_data->Classes) & GetPlayerClassBit(my_bot->GetClass())) {
c->Message(m_fail, "Invalid class for potion!");
return;
}
if (potion_data->Click.Level2 > my_bot->GetLevel()) {
c->Message(m_fail, "This potion is too powerful for your intended target!");
return;
}
// TODO: figure out best way to handle casting time/animation
if (my_bot->SpellFinished(potion_data->Click.Effect, my_bot, EQEmu::spells::CastingSlot::Item, 0)) {
c->Message(m_action, "Successfully applied %s to %s's buff effects.", potion_data->Name, my_bot->GetCleanName());
}
else {
c->Message(m_fail, "Failed to apply %s to %s's buff effects.", potion_data->Name, my_bot->GetCleanName());
}
c->DeleteItemInInventory(EQEmu::invslot::slotCursor, 1, true);
}
else {
c->Message(m_fail, "Item on cursor is not a potion!");
return;
}
}
void bot_command_attack(Client *c, const Seperator *sep) void bot_command_attack(Client *c, const Seperator *sep)
{ {
if (helper_command_alias_fail(c, "bot_command_attack", sep->arg[0], "attack")) { if (helper_command_alias_fail(c, "bot_command_attack", sep->arg[0], "attack")) {
@ -3051,6 +3214,7 @@ void bot_command_follow(Client *c, const Seperator *sep)
return; return;
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: (<friendly_target>) %s ([option: reset]) [actionable: byname | ownergroup | botgroup | namesgroup | healrotation | spawned] ([actionable_name])", sep->arg[0]); c->Message(m_usage, "usage: (<friendly_target>) %s ([option: reset]) [actionable: byname | ownergroup | botgroup | namesgroup | healrotation | spawned] ([actionable_name])", sep->arg[0]);
c->Message(m_usage, "usage: %s chain", sep->arg[0]);
return; return;
} }
const int ab_mask = ActionableBots::ABM_Type2; const int ab_mask = ActionableBots::ABM_Type2;
@ -3060,8 +3224,15 @@ void bot_command_follow(Client *c, const Seperator *sep)
int name_arg = 2; int name_arg = 2;
Mob* target_mob = nullptr; Mob* target_mob = nullptr;
std::string reset_arg = sep->arg[1]; std::string optional_arg = sep->arg[1];
if (!reset_arg.compare("reset")) { if (!optional_arg.compare("chain")) {
auto chain_count = helper_bot_follow_option_chain(c);
c->Message(m_action, "%i of your bots %s now chain following you", chain_count, (chain_count == 1 ? "is" : "are"));
return;
}
else if (!optional_arg.compare("reset")) {
reset = true; reset = true;
ab_arg = 2; ab_arg = 2;
name_arg = 3; name_arg = 3;
@ -3088,16 +3259,21 @@ void bot_command_follow(Client *c, const Seperator *sep)
bot_iter->SetFollowID(c->GetID()); bot_iter->SetFollowID(c->GetID());
else else
bot_iter->SetFollowID(my_group->GetLeader()->GetID()); bot_iter->SetFollowID(my_group->GetLeader()->GetID());
bot_iter->SetManualFollow(false);
} }
else { else {
if (bot_iter == target_mob) if (bot_iter == target_mob)
bot_iter->SetFollowID(c->GetID()); bot_iter->SetFollowID(c->GetID());
else else
bot_iter->SetFollowID(target_mob->GetID()); bot_iter->SetFollowID(target_mob->GetID());
bot_iter->SetManualFollow(true);
} }
} }
else { else {
bot_iter->SetFollowID(0); bot_iter->SetFollowID(0);
bot_iter->SetManualFollow(false);
} }
if (!bot_iter->GetPet()) if (!bot_iter->GetPet())
continue; continue;
@ -3238,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); 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; ++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_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); c->Message(m_note, "type %ccommand [help | usage] for more information", BOT_COMMAND_CHAR);
} }
@ -3403,6 +3585,105 @@ void bot_command_invisibility(Client *c, const Seperator *sep)
helper_no_available_bots(c, my_bot); helper_no_available_bots(c, my_bot);
} }
void bot_command_item_use(Client* c, const Seperator* sep)
{
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: %s ([empty])", sep->arg[0]);
return;
}
bool empty_only = false;
std::string arg1 = sep->arg[1];
if (arg1.compare("empty") == 0) {
empty_only = true;
}
const auto item_instance = c->GetInv().GetItem(EQEmu::invslot::slotCursor);
if (!item_instance) {
c->Message(m_fail, "No item found on cursor!");
return;
}
auto item_data = item_instance->GetItem();
if (!item_data) {
c->Message(m_fail, "No data found for cursor item!");
return;
}
if (item_data->ItemClass != EQEmu::item::ItemClassCommon || item_data->Slots == 0) {
c->Message(m_fail, "'%s' is not an equipable item!", item_data->Name);
return;
}
std::list<int16> equipable_slot_list;
for (int16 equipable_slot = EQEmu::invslot::EQUIPMENT_BEGIN; equipable_slot <= EQEmu::invslot::EQUIPMENT_END; ++equipable_slot) {
if (item_data->Slots & (1 << equipable_slot)) {
equipable_slot_list.push_back(equipable_slot);
}
}
std::string msg;
std::string text_link;
EQEmu::SayLinkEngine linker;
linker.SetLinkType(EQEmu::saylink::SayLinkItemInst);
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto bot_iter : sbl) {
if (!bot_iter) {
continue;
}
if (((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace())) || ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) {
continue;
}
msg = StringFormat("%cinventorygive byname %s", BOT_COMMAND_CHAR, bot_iter->GetCleanName());
text_link = bot_iter->CreateSayLink(c, msg.c_str(), bot_iter->GetCleanName());
for (auto slot_iter : equipable_slot_list) {
// needs more failure criteria - this should cover the bulk for now
if (slot_iter == EQEmu::invslot::slotSecondary && item_data->Damage && !bot_iter->CanThisClassDualWield()) {
continue;
}
auto equipped_item = bot_iter->GetBotInv()[slot_iter];
if (equipped_item && !empty_only) {
linker.SetItemInst(equipped_item);
c->Message(
Chat::Say,
"[%s] says, 'I can use that for my %s! (replaces: [%s])'",
text_link.c_str(),
EQEmu::invslot::GetInvPossessionsSlotName(slot_iter),
linker.GenerateLink().c_str()
);
bot_iter->DoAnim(29);
}
else if (!equipped_item) {
c->Message(
Chat::Say,
"[%s] says, 'I can use that for my %s!'",
text_link.c_str(),
EQEmu::invslot::GetInvPossessionsSlotName(slot_iter)
);
bot_iter->DoAnim(29);
}
}
}
}
void bot_command_levitation(Client *c, const Seperator *sep) void bot_command_levitation(Client *c, const Seperator *sep)
{ {
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Levitation]; bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Levitation];
@ -3637,6 +3918,16 @@ void bot_command_owner_option(Client *c, const Seperator *sep)
"<td><c \"#00CCCC\">null</td>" "<td><c \"#00CCCC\">null</td>"
"<td><c \"#888888\">(toggles)</td>" "<td><c \"#888888\">(toggles)</td>"
"</tr>" "</tr>"
"<tr>"
"<td><c \"#CCCCCC\">buffcounter</td>"
"<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td><c \"#888888\">marquee message on buff counter change</td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><c \"#00CCCC\">null</td>"
"<td><c \"#888888\">(toggles)</td>"
"</tr>"
"<tr>" "<tr>"
"<td><c \"#CCCCCC\">current</td>" "<td><c \"#CCCCCC\">current</td>"
"<td></td>" "<td></td>"
@ -3796,6 +4087,22 @@ void bot_command_owner_option(Client *c, const Seperator *sep)
c->Message(m_fail, "Bot owner option 'autodefend' is not allowed on this server."); c->Message(m_fail, "Bot owner option 'autodefend' is not allowed on this server.");
} }
} }
else if (!owner_option.compare("buffcounter")) {
if (!argument.compare("enable")) {
c->SetBotOption(Client::booBuffCounter, true);
}
else if (!argument.compare("disable")) {
c->SetBotOption(Client::booBuffCounter, false);
}
else {
c->SetBotOption(Client::booBuffCounter, !c->GetBotOption(Client::booBuffCounter));
}
database.botdb.SaveOwnerOption(c->CharacterID(), Client::booBuffCounter, c->GetBotOption(Client::booBuffCounter));
c->Message(m_action, "Bot 'buff counter' is now %s.", (c->GetBotOption(Client::booBuffCounter) == true ? "enabled" : "disabled"));
}
else if (!owner_option.compare("current")) { else if (!owner_option.compare("current")) {
std::string window_title = "Current Bot Owner Options Settings"; std::string window_title = "Current Bot Owner Options Settings";
@ -3811,13 +4118,15 @@ void bot_command_owner_option(Client *c, const Seperator *sep)
"<tr>" "<td><c \"#CCCCCC\">spawnmessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>" "<tr>" "<td><c \"#CCCCCC\">spawnmessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">altcombat</td>" "<td><c \"#00CC00\">{}</td>" "</tr>" "<tr>" "<td><c \"#CCCCCC\">altcombat</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">autodefend</td>" "<td><c \"#00CC00\">{}</td>" "</tr>" "<tr>" "<td><c \"#CCCCCC\">autodefend</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">buffcounter</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"</table>", "</table>",
(c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"), (c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"),
(c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"), (c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"),
(c->GetBotOption(Client::booSpawnMessageSay) ? "say" : (c->GetBotOption(Client::booSpawnMessageTell) ? "tell" : "silent")), (c->GetBotOption(Client::booSpawnMessageSay) ? "say" : (c->GetBotOption(Client::booSpawnMessageTell) ? "tell" : "silent")),
(c->GetBotOption(Client::booSpawnMessageClassSpecific) ? "class" : "default"), (c->GetBotOption(Client::booSpawnMessageClassSpecific) ? "class" : "default"),
(RuleB(Bots, AllowOwnerOptionAltCombat) ? (c->GetBotOption(Client::booAltCombat) ? "enabled" : "disabled") : "restricted"), (RuleB(Bots, AllowOwnerOptionAltCombat) ? (c->GetBotOption(Client::booAltCombat) ? "enabled" : "disabled") : "restricted"),
(RuleB(Bots, AllowOwnerOptionAutoDefend) ? (c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled") : "restricted") (RuleB(Bots, AllowOwnerOptionAutoDefend) ? (c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled") : "restricted"),
(c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled")
); );
c->SendPopupToClient(window_title.c_str(), window_text.c_str()); c->SendPopupToClient(window_title.c_str(), window_text.c_str());
@ -3961,6 +4270,12 @@ void bot_command_pull(Client *c, const Seperator *sep)
return; return;
} }
if (target_mob->IsNPC() && target_mob->GetHateList().size()) {
c->Message(m_fail, "Your current target is already engaged!");
return;
}
Bot* bot_puller = nullptr; Bot* bot_puller = nullptr;
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
@ -5455,7 +5770,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep)
} }
Bot * botCheckNotOnline = entity_list.GetBotByBotName(bots_iter.Name); Bot * botCheckNotOnline = entity_list.GetBotByBotName(bots_iter.Name);
std::string botspawn_saylink = StringFormat("^botspawn %s", bots_iter.Name); std::string botspawn_saylink = StringFormat("^botspawn %s", bots_iter.Name);
c->Message(Chat::White, "%s is a level %u %s %s %s who is owned by %s", c->Message(Chat::White, "[%s] is a level %u %s %s %s who is owned by %s",
((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQEmu::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)), ((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQEmu::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)),
bots_iter.Level, bots_iter.Level,
Bot::RaceIdToString(bots_iter.Race).c_str(), Bot::RaceIdToString(bots_iter.Race).c_str(),
@ -5536,23 +5851,12 @@ void bot_subcommand_bot_surname(Client *c, const Seperator *sep)
std::string bot_surname = sep->arg[1]; std::string bot_surname = sep->arg[1];
bot_surname = (bot_surname == "-remove") ? "" : bot_surname; bot_surname = (bot_surname == "-remove") ? "" : bot_surname;
std::replace(bot_surname.begin(), bot_surname.end(), '_', ' '); std::replace(bot_surname.begin(), bot_surname.end(), '_', ' ');
my_bot->SetSurname(bot_surname); my_bot->SetSurname(bot_surname);
if (!database.botdb.SaveBot(my_bot)) { if (!database.botdb.SaveBot(my_bot)) {
c->Message(Chat::Red, BotDatabase::fail::SaveBot()); c->Message(Chat::Red, BotDatabase::fail::SaveBot());
return;
} }
else { else {
auto outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct));
GMLastName_Struct * gmn = (GMLastName_Struct*)outapp->pBuffer;
strcpy(gmn->name, my_bot->GetCleanName());
strcpy(gmn->gmname, my_bot->GetCleanName());
strcpy(gmn->lastname, my_bot->GetSurname().c_str());
gmn->unknown[0] = 1;
gmn->unknown[1] = 1;
gmn->unknown[2] = 1;
gmn->unknown[3] = 1;
entity_list.QueueClients(my_bot->CastToClient(), outapp);
safe_delete(outapp);
c->Message(Chat::Yellow, "Bot Surname Saved."); c->Message(Chat::Yellow, "Bot Surname Saved.");
} }
} }
@ -5575,13 +5879,12 @@ void bot_subcommand_bot_title(Client *c, const Seperator *sep)
std::string bot_title = sep->arg[1]; std::string bot_title = sep->arg[1];
bot_title = (bot_title == "-remove") ? "" : bot_title; bot_title = (bot_title == "-remove") ? "" : bot_title;
std::replace(bot_title.begin(), bot_title.end(), '_', ' '); std::replace(bot_title.begin(), bot_title.end(), '_', ' ');
my_bot->SetTitle(bot_title); my_bot->SetTitle(bot_title);
if (!database.botdb.SaveBot(my_bot)) { if (!database.botdb.SaveBot(my_bot)) {
c->Message(Chat::Red, BotDatabase::fail::SaveBot()); c->Message(Chat::Red, BotDatabase::fail::SaveBot());
return;
} }
else { else {
my_bot->CastToClient()->SetAATitle(my_bot->GetTitle().c_str());
c->Message(Chat::Yellow, "Bot Title Saved."); c->Message(Chat::Yellow, "Bot Title Saved.");
} }
} }
@ -5604,13 +5907,12 @@ void bot_subcommand_bot_suffix(Client *c, const Seperator *sep)
std::string bot_suffix = sep->arg[1]; std::string bot_suffix = sep->arg[1];
bot_suffix = (bot_suffix == "-remove") ? "" : bot_suffix; bot_suffix = (bot_suffix == "-remove") ? "" : bot_suffix;
std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' '); std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' ');
my_bot->SetSuffix(bot_suffix); my_bot->SetSuffix(bot_suffix);
if (!database.botdb.SaveBot(my_bot)) { if (!database.botdb.SaveBot(my_bot)) {
c->Message(Chat::Red, BotDatabase::fail::SaveBot()); c->Message(Chat::Red, BotDatabase::fail::SaveBot());
return;
} }
else { else {
my_bot->CastToClient()->SetTitleSuffix(my_bot->GetSuffix().c_str());
c->Message(Chat::Yellow, "Bot Suffix Saved."); c->Message(Chat::Yellow, "Bot Suffix Saved.");
} }
} }
@ -5855,7 +6157,7 @@ void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep)
return; return;
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: <target_bot> %s [current | reset | sync | value: 0-255]", sep->arg[0]); c->Message(m_usage, "usage: <target_bot> %s [current | reset | sync | value: 0-255]", sep->arg[0]);
c->Message(m_note, "note: Only caster and spell-casting fighter class bots may be modified"); c->Message(m_note, "note: Only caster or hybrid class bots may be modified");
c->Message(m_note, "note: Use [reset] to set stop melee level to server rule"); c->Message(m_note, "note: Use [reset] to set stop melee level to server rule");
c->Message(m_note, "note: Use [sync] to set stop melee level to current bot level"); c->Message(m_note, "note: Use [sync] to set stop melee level to current bot level");
return; return;
@ -5866,8 +6168,8 @@ void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep)
c->Message(m_fail, "You must <target> a bot that you own to use this command"); c->Message(m_fail, "You must <target> a bot that you own to use this command");
return; return;
} }
if (!IsCasterClass(my_bot->GetClass()) && !IsSpellFighterClass(my_bot->GetClass())) { if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) {
c->Message(m_fail, "You must <target> a caster or spell-casting fighter class bot to use this command"); c->Message(m_fail, "You must <target> a caster or hybrid class bot to use this command");
return; return;
} }
@ -8432,6 +8734,75 @@ void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot)
} }
} }
int helper_bot_follow_option_chain(Client* bot_owner)
{
if (!bot_owner) {
return 0;
}
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(bot_owner, sbl);
if (sbl.empty()) {
return 0;
}
int chain_follow_count = 0;
Mob* followee = bot_owner;
// only add groups that do not belong to bot_owner
std::map<uint32, Group*> bot_group_map;
for (auto bot_iter : sbl) {
if (!bot_iter || bot_iter->GetManualFollow() || bot_iter->GetGroup() == bot_owner->GetGroup()) {
continue;
}
Group* bot_group = bot_iter->GetGroup();
if (!bot_iter->GetGroup()) {
continue;
}
bot_group_map[bot_group->GetID()] = bot_group;
}
std::list<Bot*> bot_member_list;
if (bot_owner->GetGroup()) {
bot_owner->GetGroup()->GetBotList(bot_member_list);
for (auto bot_member_iter : bot_member_list) {
if (!bot_member_iter || bot_member_iter->GetBotOwnerCharacterID() != bot_owner->CharacterID() || bot_member_iter == followee || bot_member_iter->GetManualFollow()) {
continue;
}
bot_member_iter->SetFollowID(followee->GetID());
followee = bot_member_iter;
++chain_follow_count;
}
}
for (auto bot_group_iter : bot_group_map) {
if (!bot_group_iter.second) {
continue;
}
bot_group_iter.second->GetBotList(bot_member_list);
for (auto bot_member_iter : bot_member_list) {
if (!bot_member_iter || bot_member_iter->GetBotOwnerCharacterID() != bot_owner->CharacterID() || bot_member_iter == followee || bot_member_iter->GetManualFollow()) {
continue;
}
bot_member_iter->SetFollowID(followee->GetID());
followee = bot_member_iter;
++chain_follow_count;
}
}
return chain_follow_count;
}
bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast, uint32* dont_root_before) bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast, uint32* dont_root_before)
{ {
if (!casting_bot || !target_mob) if (!casting_bot || !target_mob)
@ -8444,6 +8815,16 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id,
return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQEmu::spells::CastingSlot::Gem2, -1, -1, dont_root_before); return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQEmu::spells::CastingSlot::Gem2, -1, -1, dont_root_before);
} }
bool helper_command_disabled(Client* bot_owner, bool rule_value, const char* command)
{
if (rule_value == false) {
bot_owner->Message(m_fail, "Bot command %s is not enabled on this server.", command);
return true;
}
return false;
}
bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command) bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command)
{ {
auto alias_iter = bot_command_aliases.find(&alias[1]); auto alias_iter = bot_command_aliases.find(&alias[1]);

View File

@ -553,6 +553,8 @@ void bot_command_log_command(Client *c, const char *message);
// bot commands // bot commands
void bot_command_actionable(Client *c, const Seperator *sep); void bot_command_actionable(Client *c, const Seperator *sep);
void bot_command_aggressive(Client *c, const Seperator *sep); void bot_command_aggressive(Client *c, const Seperator *sep);
void bot_command_apply_poison(Client *c, const Seperator *sep);
void bot_command_apply_potion(Client* c, const Seperator* sep);
void bot_command_attack(Client *c, const Seperator *sep); void bot_command_attack(Client *c, const Seperator *sep);
void bot_command_bind_affinity(Client *c, const Seperator *sep); void bot_command_bind_affinity(Client *c, const Seperator *sep);
void bot_command_bot(Client *c, const Seperator *sep); void bot_command_bot(Client *c, const Seperator *sep);
@ -571,6 +573,7 @@ void bot_command_hold(Client *c, const Seperator *sep);
void bot_command_identify(Client *c, const Seperator *sep); void bot_command_identify(Client *c, const Seperator *sep);
void bot_command_inventory(Client *c, const Seperator *sep); void bot_command_inventory(Client *c, const Seperator *sep);
void bot_command_invisibility(Client *c, const Seperator *sep); void bot_command_invisibility(Client *c, const Seperator *sep);
void bot_command_item_use(Client *c, const Seperator *sep);
void bot_command_levitation(Client *c, const Seperator *sep); void bot_command_levitation(Client *c, const Seperator *sep);
void bot_command_lull(Client *c, const Seperator *sep); void bot_command_lull(Client *c, const Seperator *sep);
void bot_command_mesmerize(Client *c, const Seperator *sep); void bot_command_mesmerize(Client *c, const Seperator *sep);
@ -670,7 +673,9 @@ void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot);
void helper_bot_appearance_form_update(Bot *my_bot); void helper_bot_appearance_form_update(Bot *my_bot);
uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender); uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender);
void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot); void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot);
int helper_bot_follow_option_chain(Client *bot_owner);
bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr); bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr);
bool helper_command_disabled(Client *bot_owner, bool rule_value, const char *command);
bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command); bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command);
void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag = false); void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag = false);
bool helper_is_help_or_usage(const char* arg); bool helper_is_help_or_usage(const char* arg);

View File

@ -2256,6 +2256,7 @@ bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool
case Client::booSpawnMessageClassSpecific: case Client::booSpawnMessageClassSpecific:
case Client::booAltCombat: case Client::booAltCombat:
case Client::booAutoDefend: case Client::booAutoDefend:
case Client::booBuffCounter:
{ {
query = fmt::format( query = fmt::format(
"REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')", "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')",

View File

@ -123,49 +123,50 @@ Client::Client(EQStreamInterface* ieqs)
0, 0,
0 0
), ),
hpupdate_timer(2000), hpupdate_timer(2000),
camp_timer(29000), camp_timer(29000),
process_timer(100), process_timer(100),
consume_food_timer(CONSUMPTION_TIMER), consume_food_timer(CONSUMPTION_TIMER),
zoneinpacket_timer(1000), zoneinpacket_timer(1000),
linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), linkdead_timer(RuleI(Zone,ClientLinkdeadMS)),
dead_timer(2000), dead_timer(2000),
global_channel_timer(1000), global_channel_timer(1000),
shield_timer(500), shield_timer(500),
fishing_timer(8000), fishing_timer(8000),
endupkeep_timer(1000), endupkeep_timer(1000),
forget_timer(0), forget_timer(0),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000),
client_zone_wide_full_position_update_timer(5 * 60 * 1000), client_zone_wide_full_position_update_timer(5 * 60 * 1000),
tribute_timer(Tribute_duration), tribute_timer(Tribute_duration),
proximity_timer(ClientProximity_interval), proximity_timer(ClientProximity_interval),
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
charm_update_timer(6000), charm_update_timer(6000),
rest_timer(1), rest_timer(1),
charm_class_attacks_timer(3000), charm_class_attacks_timer(3000),
charm_cast_timer(3500), charm_cast_timer(3500),
qglobal_purge_timer(30000), qglobal_purge_timer(30000),
TrackingTimer(2000), TrackingTimer(2000),
RespawnFromHoverTimer(0), RespawnFromHoverTimer(0),
merc_timer(RuleI(Mercs, UpkeepIntervalMS)), merc_timer(RuleI(Mercs, UpkeepIntervalMS)),
ItemTickTimer(10000), ItemTickTimer(10000),
ItemQuestTimer(500), ItemQuestTimer(500),
anon_toggle_timer(250), anon_toggle_timer(250),
afk_toggle_timer(250), afk_toggle_timer(250),
helm_toggle_timer(250), helm_toggle_timer(250),
aggro_meter_timer(AGGRO_METER_UPDATE_MS), aggro_meter_timer(AGGRO_METER_UPDATE_MS),
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
last_region_type(RegionTypeUnsupported), last_region_type(RegionTypeUnsupported),
m_dirtyautohaters(false), m_dirtyautohaters(false),
npc_close_scan_timer(6000), mob_close_scan_timer(6000),
hp_self_update_throttle_timer(300), hp_self_update_throttle_timer(300),
hp_other_update_throttle_timer(500), hp_other_update_throttle_timer(500),
position_update_timer(10000), position_update_timer(10000),
tmSitting(0) consent_throttle_timer(2000),
tmSitting(0)
{ {
for (int client_filter = 0; client_filter < _FilterCount; client_filter++) for (int client_filter = 0; client_filter < _FilterCount; client_filter++)
@ -356,6 +357,7 @@ Client::Client(EQStreamInterface* ieqs)
bot_owner_options[booSpawnMessageClassSpecific] = true; bot_owner_options[booSpawnMessageClassSpecific] = true;
bot_owner_options[booAltCombat] = RuleB(Bots, AllowOwnerOptionAltCombat); bot_owner_options[booAltCombat] = RuleB(Bots, AllowOwnerOptionAltCombat);
bot_owner_options[booAutoDefend] = RuleB(Bots, AllowOwnerOptionAutoDefend); bot_owner_options[booAutoDefend] = RuleB(Bots, AllowOwnerOptionAutoDefend);
bot_owner_options[booBuffCounter] = false;
SetBotPulling(false); SetBotPulling(false);
SetBotPrecombat(false); SetBotPrecombat(false);
@ -1105,40 +1107,56 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
case ChatChannel_Say: { /* Say */ case ChatChannel_Say: { /* Say */
if(message[0] == COMMAND_CHAR) { if(message[0] == COMMAND_CHAR) {
if(command_dispatch(this, message) == -2) { 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); 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); 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); Message(Chat::Red, "Command '%s' not recognized.", message);
}
}
else {
if (!RuleB(Chat, SuppressCommandErrors)) {
Message(Chat::Red, "Command '%s' not recognized.", message);
}
} }
} }
break; break;
} }
if (EQEmu::ProfanityManager::IsCensorshipActive())
EQEmu::ProfanityManager::RedactMessage(message);
#ifdef BOTS #ifdef BOTS
if (message[0] == BOT_COMMAND_CHAR) { if (message[0] == BOT_COMMAND_CHAR) {
if (bot_command_dispatch(this, message) == -2) { if (bot_command_dispatch(this, message) == -2) {
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0); 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)) { if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
Message(Chat::Red, "Bot command '%s' not recognized.", message); Message(Chat::Red, "Bot command '%s' not recognized.", message);
} }
} }
else { else {
if (!RuleB(Chat, SuppressCommandErrors)) if (!RuleB(Chat, SuppressCommandErrors)) {
Message(Chat::Red, "Bot command '%s' not recognized.", message); Message(Chat::Red, "Bot command '%s' not recognized.", message);
}
} }
} }
break; break;
} }
#endif #endif
if (EQEmu::ProfanityManager::IsCensorshipActive()) {
EQEmu::ProfanityManager::RedactMessage(message);
}
Mob* sender = this; Mob* sender = this;
if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft))
sender = GetPet(); sender = GetPet();
@ -6237,6 +6255,52 @@ void Client::DragCorpses()
} }
} }
void Client::ConsentCorpses(std::string consent_name, bool deny)
{
if (strcasecmp(consent_name.c_str(), GetName()) == 0) {
MessageString(Chat::Red, CONSENT_YOURSELF);
}
else if (!consent_throttle_timer.Check()) {
MessageString(Chat::Red, CONSENT_WAIT);
}
else {
auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct));
ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
strn0cpy(scs->grantname, consent_name.c_str(), sizeof(scs->grantname));
strn0cpy(scs->ownername, GetName(), sizeof(scs->ownername));
strn0cpy(scs->zonename, "Unknown", sizeof(scs->zonename));
scs->permission = deny ? 0 : 1;
scs->zone_id = zone->GetZoneID();
scs->instance_id = zone->GetInstanceID();
scs->consent_type = EQEmu::consent::Normal;
scs->consent_id = 0;
if (strcasecmp(scs->grantname, "group") == 0) {
if (!deny) {
Group* grp = GetGroup();
scs->consent_id = grp ? grp->GetID() : 0;
}
scs->consent_type = EQEmu::consent::Group;
}
else if (strcasecmp(scs->grantname, "raid") == 0) {
if (!deny) {
Raid* raid = GetRaid();
scs->consent_id = raid ? raid->GetID() : 0;
}
scs->consent_type = EQEmu::consent::Raid;
}
else if (strcasecmp(scs->grantname, "guild") == 0) {
if (!deny) {
scs->consent_id = GuildID();
}
scs->consent_type = EQEmu::consent::Guild;
// update all corpses in db so buried/unloaded corpses see new consent id
database.UpdateCharacterCorpseConsent(CharacterID(), scs->consent_id);
}
worldserver.SendPacket(pack);
safe_delete(pack);
}
}
void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration) void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration)
{ {
if(!target || !IsValidSpell(spell_id) || this->GetID() == target->GetID()) if(!target || !IsValidSpell(spell_id) || this->GetID() == target->GetID())
@ -6742,11 +6806,22 @@ void Client::SendStatsWindow(Client* client, bool use_window)
GetRawACNoShield(shield_ac); GetRawACNoShield(shield_ac);
std::string skill_list[] = { std::string skill_list[] = {
"1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration","Alteration","Apply Poison","Archery","Backstab","Bind Wound","Bash","Block","Brass Instruments","Channeling","Conjuration", "1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration",
"Defense","Disarm","Disarm Traps","Divination","Dodge","Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation","Feign Death","Flying Kick","Forage","Hand To Hand","Hide","Kick", "Alteration","Apply Poison","Archery","Backstab","Bind Wound",
"Meditate","Mend","Offense","Parry","Pick Lock","Piercing","Riposte","Round Kick","Safe Fall","Sense Heading","Singing","Sneak","Specialize Abjuration","Specialize Alteration","Specialize Conjuration", "Bash","Block","Brass Instruments","Channeling","Conjuration",
"Specialize Divination","Specialize Evocation","Pick Pockets","Stringed_Instruments","Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments","Fishing","Make Poison","Tinkering","Research","Alchemy", "Defense","Disarm","Disarm Traps","Divination","Dodge",
"Baking","Tailoring","Sense Traps","Blacksmithing","Fletching","Brewing","Alcohol_Tolerance","Begging","Jewelry Making","Pottery","Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy" "Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation",
"Feign Death","Flying Kick","Forage","Hand To Hand","Hide",
"Kick","Meditate","Mend","Offense","Parry",
"Pick Lock","1H Piercing","Riposte","Round Kick","Safe Fall",
"Sense Heading","Singing","Sneak","Specialize Abjuration","Specialize Alteration",
"Specialize Conjuration","Specialize Divination","Specialize Evocation","Pick Pockets","Stringed Instruments",
"Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments",
"Fishing","Make Poison","Tinkering","Research","Alchemy",
"Baking","Tailoring","Sense Traps","Blacksmithing","Fletching",
"Brewing","Alcohol_Tolerance","Begging","Jewelry Making","Pottery",
"Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy",
"Remove Traps","Triple Attack","2H Piercing"
}; };
std::string skill_mods = ""; std::string skill_mods = "";
@ -8505,13 +8580,13 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold,
memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct)); memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct));
QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer; QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer;
qr->mob_id = target->GetID(); // Entity ID for the from mob name qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name
qr->target_id = GetID(); // The Client ID (this) qr->target_id = GetID(); // The Client ID (this)
qr->copper = copper; qr->copper = copper;
qr->silver = silver; qr->silver = silver;
qr->gold = gold; qr->gold = gold;
qr->platinum = platinum; qr->platinum = platinum;
qr->item_id = itemid; qr->item_id[0] = itemid;
qr->exp_reward = exp; qr->exp_reward = exp;
if (copper > 0 || silver > 0 || gold > 0 || platinum > 0) if (copper > 0 || silver > 0 || gold > 0 || platinum > 0)
@ -8522,7 +8597,7 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold,
if (faction) if (faction)
{ {
if (target->IsNPC()) if (target && target->IsNPC())
{ {
int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); int32 nfl_id = target->CastToNPC()->GetNPCFactionID();
SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true);
@ -8538,6 +8613,42 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold,
safe_delete(outapp); safe_delete(outapp);
} }
void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction)
{
auto outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct));
memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct));
QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer;
memcpy(qr, &reward, sizeof(QuestReward_Struct));
// not set in caller because reasons
qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name
if (reward.copper > 0 || reward.silver > 0 || reward.gold > 0 || reward.platinum > 0)
AddMoneyToPP(reward.copper, reward.silver, reward.gold, reward.platinum, false);
for (int i = 0; i < QUESTREWARD_COUNT; ++i)
if (reward.item_id[i] > 0)
SummonItem(reward.item_id[i], 0, 0, 0, 0, 0, 0, false, EQEmu::invslot::slotCursor);
if (faction)
{
if (target && target->IsNPC())
{
int32 nfl_id = target->CastToNPC()->GetNPCFactionID();
SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true);
qr->faction = target->CastToNPC()->GetPrimaryFaction();
qr->faction_mod = 1; // Too lazy to get real value, not sure if this is even used by client anyhow.
}
}
if (reward.exp_reward> 0)
AddEXP(reward.exp_reward);
QueuePacket(outapp, true, Client::CLIENT_CONNECTED);
safe_delete(outapp);
}
void Client::SendHPUpdateMarquee(){ void Client::SendHPUpdateMarquee(){
if (!this || !this->IsClient() || !this->current_hp || !this->max_hp) if (!this || !this->IsClient() || !this->current_hp || !this->max_hp)
return; return;

View File

@ -226,7 +226,6 @@ public:
Client(EQStreamInterface * ieqs); Client(EQStreamInterface * ieqs);
~Client(); ~Client();
std::unordered_map<Mob *, float> close_mobs;
bool is_client_moving; bool is_client_moving;
void SetDisplayMobInfoWindow(bool display_mob_info_window); void SetDisplayMobInfoWindow(bool display_mob_info_window);
@ -794,6 +793,9 @@ public:
virtual void UpdateEquipmentLight() { m_Light.Type[EQEmu::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQEmu::lightsource::LightEquipment] = EQEmu::lightsource::TypeToLevel(m_Light.Type[EQEmu::lightsource::LightEquipment]); } virtual void UpdateEquipmentLight() { m_Light.Type[EQEmu::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQEmu::lightsource::LightEquipment] = EQEmu::lightsource::TypeToLevel(m_Light.Type[EQEmu::lightsource::LightEquipment]); }
inline bool AutoSplitEnabled() { return m_pp.autosplit != 0; } inline bool AutoSplitEnabled() { return m_pp.autosplit != 0; }
inline bool AutoConsentGroupEnabled() const { return m_pp.groupAutoconsent != 0; }
inline bool AutoConsentRaidEnabled() const { return m_pp.raidAutoconsent != 0; }
inline bool AutoConsentGuildEnabled() const { return m_pp.guildAutoconsent != 0; }
void SummonHorse(uint16 spell_id); void SummonHorse(uint16 spell_id);
void SetHorseId(uint16 horseid_in); void SetHorseId(uint16 horseid_in);
@ -958,7 +960,6 @@ public:
void EnteringMessages(Client* client); void EnteringMessages(Client* client);
void SendRules(Client* client); void SendRules(Client* client);
std::list<std::string> consent_list;
const bool GetGMSpeed() const { return (gmspeed > 0); } const bool GetGMSpeed() const { return (gmspeed > 0); }
bool CanUseReport; bool CanUseReport;
@ -1138,6 +1139,7 @@ public:
inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); }
void DragCorpses(); void DragCorpses();
inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } inline void ClearDraggedCorpses() { DraggedCorpses.clear(); }
void ConsentCorpses(std::string consent_name, bool deny = false);
void SendAltCurrencies(); void SendAltCurrencies();
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
@ -1283,6 +1285,7 @@ public:
int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false);
void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false); void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false);
void QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction); // TODO: Fix faction processing
void ResetHPUpdateTimer() { hpupdate_timer.Start(); } void ResetHPUpdateTimer() { hpupdate_timer.Start(); }
@ -1524,10 +1527,11 @@ private:
Timer afk_toggle_timer; Timer afk_toggle_timer;
Timer helm_toggle_timer; Timer helm_toggle_timer;
Timer aggro_meter_timer; Timer aggro_meter_timer;
Timer npc_close_scan_timer; Timer mob_close_scan_timer;
Timer hp_self_update_throttle_timer; /* This is to prevent excessive packet sending under trains/fast combat */ Timer hp_self_update_throttle_timer; /* This is to prevent excessive packet sending under trains/fast combat */
Timer hp_other_update_throttle_timer; /* This is to keep clients from DOSing the server with macros that change client targets constantly */ Timer hp_other_update_throttle_timer; /* This is to keep clients from DOSing the server with macros that change client targets constantly */
Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */
Timer consent_throttle_timer;
glm::vec3 m_Proximity; glm::vec3 m_Proximity;
glm::vec4 last_position_before_bulk_update; glm::vec4 last_position_before_bulk_update;
@ -1640,6 +1644,7 @@ public:
booSpawnMessageClassSpecific, booSpawnMessageClassSpecific,
booAltCombat, booAltCombat,
booAutoDefend, booAutoDefend,
booBuffCounter,
_booCount _booCount
}; };

View File

@ -336,6 +336,10 @@ int32 Client::CalcMaxHP()
current_hp = curHP_cap; current_hp = curHP_cap;
} }
} }
// hack fix for client health not reflecting server value
last_max_hp = 0;
return max_hp; return max_hp;
} }
@ -610,14 +614,13 @@ int32 Client::CalcBaseMana()
case 'I': case 'I':
WisInt = GetINT(); WisInt = GetINT();
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
ConvertedWisInt = WisInt;
int over200 = WisInt;
if (WisInt > 100) { if (WisInt > 100) {
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); if (WisInt > 200) {
if (WisInt > 201) { over200 = (WisInt - 200) / -2 + WisInt;
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
} }
} ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
else {
ConvertedWisInt = WisInt;
} }
auto base_data = database.GetBaseData(GetLevel(), GetClass()); auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) { if (base_data) {
@ -643,14 +646,13 @@ int32 Client::CalcBaseMana()
case 'W': case 'W':
WisInt = GetWIS(); WisInt = GetWIS();
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
ConvertedWisInt = WisInt;
int over200 = WisInt;
if (WisInt > 100) { if (WisInt > 100) {
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); if (WisInt > 200) {
if (WisInt > 201) { over200 = (WisInt - 200) / -2 + WisInt;
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
} }
} ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
else {
ConvertedWisInt = WisInt;
} }
auto base_data = database.GetBaseData(GetLevel(), GetClass()); auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) { if (base_data) {

View File

@ -905,6 +905,8 @@ void Client::CompleteConnect()
entity_list.RefreshClientXTargets(this); entity_list.RefreshClientXTargets(this);
worldserver.RequestTellQueue(GetName()); worldserver.RequestTellQueue(GetName());
entity_list.ScanCloseMobs(close_mobs, this);
} }
// connecting opcode handlers // connecting opcode handlers
@ -4641,43 +4643,16 @@ void Client::Handle_OP_Consent(const EQApplicationPacket *app)
{ {
if (app->size<64) { if (app->size<64) {
Consent_Struct* c = (Consent_Struct*)app->pBuffer; Consent_Struct* c = (Consent_Struct*)app->pBuffer;
if (strcmp(c->name, GetName()) != 0) { ConsentCorpses(c->name, false);
auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct));
ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
strcpy(scs->grantname, c->name);
strcpy(scs->ownername, GetName());
scs->message_string_id = 0;
scs->permission = 1;
scs->zone_id = zone->GetZoneID();
scs->instance_id = zone->GetInstanceID();
//consent_list.push_back(scs->grantname);
worldserver.SendPacket(pack);
safe_delete(pack);
}
else {
MessageString(Chat::White, CONSENT_YOURSELF);
}
} }
return;
} }
void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app) void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app)
{ {
if (app->size<64) { if (app->size<64) {
Consent_Struct* c = (Consent_Struct*)app->pBuffer; Consent_Struct* c = (Consent_Struct*)app->pBuffer;
auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); ConsentCorpses(c->name, true);
ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
strcpy(scs->grantname, c->name);
strcpy(scs->ownername, GetName());
scs->message_string_id = 0;
scs->permission = 0;
scs->zone_id = zone->GetZoneID();
scs->instance_id = zone->GetInstanceID();
//consent_list.remove(scs->grantname);
worldserver.SendPacket(pack);
safe_delete(pack);
} }
return;
} }
void Client::Handle_OP_Consider(const EQApplicationPacket *app) void Client::Handle_OP_Consider(const EQApplicationPacket *app)
@ -8286,7 +8261,18 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
if (GetTarget() && GetTarget()->IsNPC()) { if (GetTarget() && GetTarget()->IsNPC()) {
if (silentsaylink) { if (silentsaylink) {
parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); 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 { else {
Message(Chat::LightGray, "You say, '%s'", response.c_str()); Message(Chat::LightGray, "You say, '%s'", response.c_str());
@ -8296,7 +8282,17 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
} }
else { else {
if (silentsaylink) { 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 { else {
Message(Chat::LightGray, "You say, '%s'", response.c_str()); Message(Chat::LightGray, "You say, '%s'", response.c_str());
@ -11107,6 +11103,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
break; break;
} }
if (player_to_invite_group && player_to_invite_group->IsGroupMember(this)) {
MessageString(Chat::Red, ALREADY_IN_PARTY);
break;
}
if (player_to_invite_group && !player_to_invite_group->IsLeader(player_to_invite)) { if (player_to_invite_group && !player_to_invite_group->IsLeader(player_to_invite)) {
Message(Chat::Red, "You can only invite an ungrouped player or group leader to join your raid."); Message(Chat::Red, "You can only invite an ungrouped player or group leader to join your raid.");
break; break;
@ -13292,6 +13293,21 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
entity_list.QueueClients(this, app, true); entity_list.QueueClients(this, app, true);
} }
} }
else if (sa->type == AT_GroupConsent)
{
m_pp.groupAutoconsent = (sa->parameter == 1);
ConsentCorpses("Group", (sa->parameter != 1));
}
else if (sa->type == AT_RaidConsent)
{
m_pp.raidAutoconsent = (sa->parameter == 1);
ConsentCorpses("Raid", (sa->parameter != 1));
}
else if (sa->type == AT_GuildConsent)
{
m_pp.guildAutoconsent = (sa->parameter == 1);
ConsentCorpses("Guild", (sa->parameter != 1));
}
else { else {
std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec
<< " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl;
@ -14139,16 +14155,16 @@ void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app)
#else #else
else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { else if (tradee && (tradee->IsNPC() || tradee->IsBot())) {
#endif #endif
//npcs always accept if (!tradee->IsEngaged()) {
trade->Start(msg->to_mob_id); trade->Start(msg->to_mob_id);
EQApplicationPacket *outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct));
auto outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); TradeRequest_Struct *acc = (TradeRequest_Struct *) outapp->pBuffer;
TradeRequest_Struct* acc = (TradeRequest_Struct*)outapp->pBuffer; acc->from_mob_id = msg->to_mob_id;
acc->from_mob_id = msg->to_mob_id; acc->to_mob_id = msg->from_mob_id;
acc->to_mob_id = msg->from_mob_id; FastQueuePacket(&outapp);
FastQueuePacket(&outapp); safe_delete(outapp);
safe_delete(outapp); }
} }
return; return;
} }

View File

@ -251,26 +251,12 @@ bool Client::Process() {
} }
} }
/* Build a close range list of NPC's */ /**
if (npc_close_scan_timer.Check()) { * Scan close range mobs
close_mobs.clear(); * Used in aggro checks
//Force spawn updates when traveled far */
bool force_spawn_updates = false; if (mob_close_scan_timer.Check()) {
float client_update_range = (RuleI(Range, ClientForceSpawnUpdateRange) * RuleI(Range, ClientForceSpawnUpdateRange)); entity_list.ScanCloseMobs(close_mobs, this);
float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan));
auto &mob_list = entity_list.GetMobList();
for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) {
Mob* mob = itr->second;
float distance = DistanceSquared(m_Position, mob->GetPosition());
if (mob->IsNPC()) {
if (distance <= scan_range) {
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
}
else if ((mob->GetAggroRange() * mob->GetAggroRange()) > scan_range) {
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
}
}
}
} }
bool may_use_attacks = false; bool may_use_attacks = false;
@ -593,7 +579,7 @@ bool Client::Process() {
if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) { if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) {
int npc_scan_count = 0; int npc_scan_count = 0;
for (auto & close_mob : close_mobs) { for (auto & close_mob : close_mobs) {
Mob *mob = close_mob.first; Mob *mob = close_mob.second;
if (!mob) if (!mob)
continue; continue;

View File

@ -295,6 +295,7 @@ int command_init(void)
command_add("npcstats", "- Show stats about target NPC", 80, command_npcstats) || command_add("npcstats", "- Show stats about target NPC", 80, command_npcstats) ||
command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) || command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) ||
command_add("npctypespawn", "[npctypeid] [factionid] - Spawn an NPC from the db", 10, command_npctypespawn) || command_add("npctypespawn", "[npctypeid] [factionid] - Spawn an NPC from the db", 10, command_npctypespawn) ||
command_add("nudge", "- Nudge your target's current position by specific values", 80, command_nudge) ||
command_add("nukebuffs", "- Strip all buffs on you or your target", 50, command_nukebuffs) || command_add("nukebuffs", "- Strip all buffs on you or your target", 50, command_nukebuffs) ||
command_add("nukeitem", "[itemid] - Remove itemid from your player target's inventory", 150, command_nukeitem) || command_add("nukeitem", "[itemid] - Remove itemid from your player target's inventory", 150, command_nukeitem) ||
command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", 100, command_object) || command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", 100, command_object) ||
@ -782,6 +783,12 @@ void command_help(Client *c, const Seperator *sep)
commands_shown++; commands_shown++;
c->Message(Chat::White, " %c%s %s", COMMAND_CHAR, cur->first.c_str(), cur->second->desc == nullptr?"":cur->second->desc); 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":""); c->Message(Chat::White, "%d command%s listed.", commands_shown, commands_shown!=1?"s":"");
} }
@ -2166,25 +2173,6 @@ void command_spoff(Client *c, const Seperator *sep)
safe_delete(outapp); safe_delete(outapp);
} }
void command_itemtest(Client *c, const Seperator *sep)
{
char chBuffer[8192] = {0};
//Using this to determine new item layout
FILE* f = nullptr;
if (!(f = fopen("c:\\EQEMUcvs\\ItemDump.txt", "rb"))) {
c->Message(Chat::Red, "Error: Could not open c:\\EQEMUcvs\\ItemDump.txt");
return;
}
fread(chBuffer, sizeof(chBuffer), sizeof(char), f);
fclose(f);
auto outapp = new EQApplicationPacket(OP_ItemLinkResponse, strlen(chBuffer) + 5);
memcpy(&outapp->pBuffer[4], chBuffer, strlen(chBuffer));
c->QueuePacket(outapp);
safe_delete(outapp);
}
void command_gassign(Client *c, const Seperator *sep) void command_gassign(Client *c, const Seperator *sep)
{ {
if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) { if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) {
@ -2502,6 +2490,7 @@ void command_grid(Client *c, const Seperator *sep)
else { else {
c->Message(Chat::White, "Usage: #grid add/delete grid_num wandertype pausetype"); c->Message(Chat::White, "Usage: #grid add/delete grid_num wandertype pausetype");
c->Message(Chat::White, "Usage: #grid max - displays the highest grid ID used in this zone (for add)"); c->Message(Chat::White, "Usage: #grid max - displays the highest grid ID used in this zone (for add)");
c->Message(Chat::White, "Usage: #grid show - displays wp nodes as boxes");
} }
} }
@ -2554,7 +2543,7 @@ void command_size(Client *c, const Seperator *sep)
else if (!target) else if (!target)
c->Message(Chat::White,"Error: this command requires a target"); c->Message(Chat::White,"Error: this command requires a target");
else { else {
uint16 Race = target->GetRace(); uint16 Race = target->GetModel();
uint8 Gender = target->GetGender(); uint8 Gender = target->GetGender();
uint8 Texture = 0xFF; uint8 Texture = 0xFF;
uint8 HelmTexture = 0xFF; uint8 HelmTexture = 0xFF;
@ -3127,6 +3116,81 @@ void command_npctypespawn(Client *c, const Seperator *sep)
} }
void command_nudge(Client* c, const Seperator* sep)
{
if (sep->arg[1][0] == 0) {
c->Message(Chat::White, "Usage: #nudge [x=f] [y=f] [z=f] [h=f] (partial/mixed arguments allowed)");
}
else {
auto target = c->GetTarget();
if (!target) {
c->Message(Chat::Yellow, "This command requires a target.");
return;
}
if (target->IsMoving()) {
c->Message(Chat::Yellow, "This command requires a stationary target.");
return;
}
glm::vec4 position_offset(0.0f, 0.0f, 0.0f, 0.0f);
for (auto index = 1; index <= 4; ++index) {
if (!sep->arg[index]) {
continue;
}
Seperator argsep(sep->arg[index], '=');
if (!argsep.arg[1][0]) {
continue;
}
switch (argsep.arg[0][0]) {
case 'x':
position_offset.x = atof(argsep.arg[1]);
break;
case 'y':
position_offset.y = atof(argsep.arg[1]);
break;
case 'z':
position_offset.z = atof(argsep.arg[1]);
break;
case 'h':
position_offset.w = atof(argsep.arg[1]);
break;
default:
break;
}
}
const auto& current_position = target->GetPosition();
glm::vec4 new_position(
(current_position.x + position_offset.x),
(current_position.y + position_offset.y),
(current_position.z + position_offset.z),
(current_position.w + position_offset.w)
);
target->GMMove(new_position.x, new_position.y, new_position.z, new_position.w);
c->Message(
Chat::White,
"Nudging '%s' to {%1.3f, %1.3f, %1.3f, %1.2f} (adjustment: {%1.3f, %1.3f, %1.3f, %1.2f})",
target->GetName(),
new_position.x,
new_position.y,
new_position.z,
new_position.w,
position_offset.x,
position_offset.y,
position_offset.z,
position_offset.w
);
}
}
void command_heal(Client *c, const Seperator *sep) void command_heal(Client *c, const Seperator *sep)
{ {
if (c->GetTarget()==0) if (c->GetTarget()==0)
@ -4176,10 +4240,15 @@ void command_corpsefix(Client *c, const Seperator *sep)
void command_reloadworld(Client *c, const Seperator *sep) void command_reloadworld(Client *c, const Seperator *sep)
{ {
c->Message(Chat::White, "Reloading quest cache and repopping zones worldwide."); int world_repop = atoi(sep->arg[1]);
if (world_repop == 0)
c->Message(Chat::White, "Reloading quest cache worldwide.");
else
c->Message(Chat::White, "Reloading quest cache and repopping zones worldwide.");
auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct));
ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer; ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer;
RW->Option = ((atoi(sep->arg[1]) == 1) ? 1 : 0); RW->Option = world_repop;
worldserver.SendPacket(pack); worldserver.SendPacket(pack);
safe_delete(pack); safe_delete(pack);
} }
@ -7471,7 +7540,7 @@ void command_ipban(Client *c, const Seperator *sep)
c->Message(Chat::White, "Usage: #ipban [xxx.xxx.xxx.xxx]"); c->Message(Chat::White, "Usage: #ipban [xxx.xxx.xxx.xxx]");
} else { } else {
if(database.AddBannedIP(sep->arg[1], c->GetName())) { if(database.AddBannedIP(sep->arg[1], c->GetName())) {
c->Message(Chat::White, "%s has been successfully added to the Banned_IPs table by %s", sep->arg[1], c->GetName()); c->Message(Chat::White, "%s has been successfully added to the banned_ips table by %s", sep->arg[1], c->GetName());
} else { } else {
c->Message(Chat::White, "IPBan Failed (IP address is possibly already in the table?)"); c->Message(Chat::White, "IPBan Failed (IP address is possibly already in the table?)");
} }
@ -13182,8 +13251,8 @@ void command_bot(Client *c, const Seperator *sep)
} }
if (bot_command_dispatch(c, bot_message.c_str()) == -2) { if (bot_command_dispatch(c, bot_message.c_str()) == -2) {
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
int i = parse->EventPlayer(EVENT_COMMAND, c, bot_message, 0); int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, bot_message, 0);
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
c->Message(Chat::Red, "Bot command '%s' not recognized.", bot_message.c_str()); c->Message(Chat::Red, "Bot command '%s' not recognized.", bot_message.c_str());
} }

View File

@ -150,7 +150,6 @@ void command_ipc(Client *c, const Seperator *sep);
void command_iplookup(Client *c, const Seperator *sep); void command_iplookup(Client *c, const Seperator *sep);
void command_iteminfo(Client *c, const Seperator *sep); void command_iteminfo(Client *c, const Seperator *sep);
void command_itemsearch(Client *c, const Seperator *sep); void command_itemsearch(Client *c, const Seperator *sep);
void command_itemtest(Client *c, const Seperator *sep);
void command_kick(Client *c, const Seperator *sep); void command_kick(Client *c, const Seperator *sep);
void command_killallnpcs(Client *c, const Seperator *sep); void command_killallnpcs(Client *c, const Seperator *sep);
void command_kill(Client *c, const Seperator *sep); void command_kill(Client *c, const Seperator *sep);
@ -194,6 +193,7 @@ void command_npcspecialattk(Client *c, const Seperator *sep);
void command_npcstats(Client *c, const Seperator *sep); void command_npcstats(Client *c, const Seperator *sep);
void command_npctype_cache(Client *c, const Seperator *sep); void command_npctype_cache(Client *c, const Seperator *sep);
void command_npctypespawn(Client *c, const Seperator *sep); void command_npctypespawn(Client *c, const Seperator *sep);
void command_nudge(Client* c, const Seperator* sep);
void command_nukebuffs(Client *c, const Seperator *sep); void command_nukebuffs(Client *c, const Seperator *sep);
void command_nukeitem(Client *c, const Seperator *sep); void command_nukeitem(Client *c, const Seperator *sep);
void command_numauths(Client *c, const Seperator *sep); void command_numauths(Client *c, const Seperator *sep);

View File

@ -647,6 +647,19 @@ enum {
SKILLUP_FAILURE = 2 SKILLUP_FAILURE = 2
}; };
enum {
GridCircular,
GridRandom10,
GridRandom,
GridPatrol,
GridOneWayRepop,
GridRand5LoS,
GridOneWayDepop,
GridCenterPoint,
GridRandomCenterPoint,
GridRandomPath
};
typedef enum { typedef enum {
petFamiliar, //only listens to /pet get lost petFamiliar, //only listens to /pet get lost
petAnimation, //does not listen to any commands petAnimation, //does not listen to any commands

View File

@ -73,7 +73,7 @@ void Corpse::SendLootReqErrorPacket(Client* client, LootResponse response) {
safe_delete(outapp); safe_delete(outapp);
} }
Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard) { Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard, uint32 guild_consent_id) {
uint32 item_count = database.GetCharacterCorpseItemCount(in_dbid); uint32 item_count = database.GetCharacterCorpseItemCount(in_dbid);
auto buffer = auto buffer =
new char[sizeof(PlayerCorpse_Struct) + (item_count * sizeof(player_lootitem::ServerLootItem_Struct))]; new char[sizeof(PlayerCorpse_Struct) + (item_count * sizeof(player_lootitem::ServerLootItem_Struct))];
@ -138,6 +138,7 @@ Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std:
pc->drakkin_details = pcs->drakkin_details; pc->drakkin_details = pcs->drakkin_details;
pc->IsRezzed(rezzed); pc->IsRezzed(rezzed);
pc->become_npc = false; pc->become_npc = false;
pc->consented_guild_id = guild_consent_id;
pc->UpdateEquipmentLight(); // itemlist populated above..need to determine actual values pc->UpdateEquipmentLight(); // itemlist populated above..need to determine actual values
@ -282,6 +283,18 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob (
allowed_looters[i] = 0; allowed_looters[i] = 0;
} }
if (client->AutoConsentGroupEnabled()) {
Group* grp = client->GetGroup();
consented_group_id = grp ? grp->GetID() : 0;
}
if (client->AutoConsentRaidEnabled()) {
Raid* raid = client->GetRaid();
consented_raid_id = raid ? raid->GetID() : 0;
}
consented_guild_id = client->AutoConsentGuildEnabled() ? client->GuildID() : 0;
is_corpse_changed = true; is_corpse_changed = true;
rez_experience = in_rezexp; rez_experience = in_rezexp;
can_corpse_be_rezzed = true; can_corpse_be_rezzed = true;
@ -611,11 +624,11 @@ bool Corpse::Save() {
/* Create New Corpse*/ /* Create New Corpse*/
if (corpse_db_id == 0) { if (corpse_db_id == 0) {
corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position); corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, consented_guild_id);
} }
/* Update Corpse Data */ /* Update Corpse Data */
else{ else{
corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, IsRezzed()); corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, consented_guild_id, IsRezzed());
} }
safe_delete_array(dbpc); safe_delete_array(dbpc);
@ -647,6 +660,25 @@ void Corpse::DepopPlayerCorpse() {
player_corpse_depop = true; player_corpse_depop = true;
} }
void Corpse::AddConsentName(std::string consent_player_name)
{
for (const auto& consented_player_name : consented_player_names) {
if (strcasecmp(consented_player_name.c_str(), consent_player_name.c_str()) == 0) {
return;
}
}
consented_player_names.emplace_back(consent_player_name);
}
void Corpse::RemoveConsentName(std::string consent_player_name)
{
consented_player_names.erase(std::remove_if(consented_player_names.begin(), consented_player_names.end(),
[consent_player_name](const std::string& consented_player_name) {
return strcasecmp(consented_player_name.c_str(), consent_player_name.c_str()) == 0;
}
), consented_player_names.end());
}
uint32 Corpse::CountItems() { uint32 Corpse::CountItems() {
return itemlist.size(); return itemlist.size();
} }
@ -1434,29 +1466,50 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) {
is_corpse_changed = true; is_corpse_changed = true;
} }
else { else {
client->Message(Chat::White, "Corpse is too far away."); client->MessageString(Chat::Red, CORPSE_TOO_FAR);
return false; return false;
} }
} }
else else
{ {
bool consented = false; bool consented = false;
std::list<std::string>::iterator itr; for (const auto& consented_player_name : consented_player_names) {
for(itr = client->consent_list.begin(); itr != client->consent_list.end(); ++itr) { if (strcasecmp(client->GetName(), consented_player_name.c_str()) == 0) {
if(strcmp(this->GetOwnerName(), itr->c_str()) == 0) { consented = true;
if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) { break;
GMMove(client->GetX(), client->GetY(), client->GetZ()); }
is_corpse_changed = true; }
}
else { if (!consented && consented_guild_id && consented_guild_id != GUILD_NONE) {
client->Message(Chat::White, "Corpse is too far away."); if (client->GuildID() == consented_guild_id) {
return false;
}
consented = true; consented = true;
} }
} }
if(!consented) { if (!consented && consented_group_id) {
client->Message(Chat::White, "You do not have permission to move this corpse."); Group* grp = client->GetGroup();
if (grp && grp->GetID() == consented_group_id) {
consented = true;
}
}
if (!consented && consented_raid_id) {
Raid* raid = client->GetRaid();
if (raid && raid->GetID() == consented_raid_id) {
consented = true;
}
}
if (consented) {
if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) {
GMMove(client->GetX(), client->GetY(), client->GetZ());
is_corpse_changed = true;
}
else {
client->MessageString(Chat::Red, CORPSE_TOO_FAR);
return false;
}
}
else {
client->MessageString(Chat::Red, CONSENT_DENIED);
return false; return false;
} }
} }

View File

@ -48,7 +48,7 @@ class Corpse : public Mob {
Corpse(uint32 in_corpseid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, const glm::vec4& position, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture, uint32 in_rezexp, bool wasAtGraveyard = false); Corpse(uint32 in_corpseid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, const glm::vec4& position, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture, uint32 in_rezexp, bool wasAtGraveyard = false);
~Corpse(); ~Corpse();
static Corpse* LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard); static Corpse* LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard, uint32 guild_consent_id);
/* Corpse: General */ /* Corpse: General */
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) { return true; } virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) { return true; }
@ -74,6 +74,11 @@ class Corpse : public Mob {
uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); }
uint32 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); } uint32 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); }
void SetDecayTimer(uint32 decay_time); void SetDecayTimer(uint32 decay_time);
void SetConsentGroupID(uint32 group_id) { if (IsPlayerCorpse()) { consented_group_id = group_id; } }
void SetConsentRaidID(uint32 raid_id) { if (IsPlayerCorpse()) { consented_raid_id = raid_id; } }
void SetConsentGuildID(uint32 guild_id) { if (IsPlayerCorpse()) { consented_guild_id = guild_id; } }
void AddConsentName(std::string consent_player_name);
void RemoveConsentName(std::string consent_player_name);
void Delete(); void Delete();
void Bury(); void Bury();
@ -142,6 +147,9 @@ private:
int32 player_kill_item; /* Determines if Player Kill Item */ int32 player_kill_item; /* Determines if Player Kill Item */
uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */ uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */
uint32 char_id; /* Character ID */ uint32 char_id; /* Character ID */
uint32 consented_group_id = 0;
uint32 consented_raid_id = 0;
uint32 consented_guild_id = 0;
ItemList itemlist; /* Internal Item list used for corpses */ ItemList itemlist; /* Internal Item list used for corpses */
uint32 copper; uint32 copper;
uint32 silver; uint32 silver;
@ -160,6 +168,7 @@ private:
Timer corpse_graveyard_timer; Timer corpse_graveyard_timer;
Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */ Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */
EQEmu::TintProfile item_tint; EQEmu::TintProfile item_tint;
std::vector<std::string> consented_player_names;
LootRequestType loot_request_type; LootRequestType loot_request_type;
}; };

View File

@ -673,252 +673,429 @@ void Client::SendDisciplineTimer(uint32 timer_id, uint32 duration)
} }
} }
void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate) /**
* @param taunter
* @param range
* @param bonus_hate
*/
void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
{ {
if (range == 0)
range = 40; //Live AE taunt range - Hardcoded.
range = range * range; /**
* Live AE taunt range - Hardcoded.
*/
if (range == 0) {
range = 40;
}
auto it = npc_list.begin(); float range_squared = range * range;
while (it != npc_list.end()) {
NPC *them = it->second; for (auto &it : entity_list.GetCloseMobList(taunter, range)) {
float zdiff = taunter->GetZ() - them->GetZ(); Mob *them = it.second;
if (zdiff < 0)
zdiff *= -1; if (!them->IsNPC()) {
if (zdiff < 10 continue;
&& taunter->IsAttackAllowed(them) }
&& DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range) {
float z_difference = taunter->GetZ() - them->GetZ();
if (z_difference < 0) {
z_difference *= -1;
}
if (z_difference < 10
&& taunter->IsAttackAllowed(them)
&& DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared) {
if (taunter->CheckLosFN(them)) { if (taunter->CheckLosFN(them)) {
taunter->Taunt(them, true,0,true,bonus_hate); taunter->Taunt(them->CastToNPC(), true, 0, true, bonus_hate);
} }
} }
++it;
} }
} }
// causes caster to hit every mob within dist range of center with /**
// spell_id. * Causes caster to hit every mob within dist range of center with spell_id
// NPC spells will only affect other NPCs with compatible faction *
void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int *max_targets) * @param caster_mob
* @param center_mob
* @param spell_id
* @param affect_caster
* @param resist_adjust
* @param max_targets
*/
void EntityList::AESpell(
Mob *caster_mob,
Mob *center_mob,
uint16 spell_id,
bool affect_caster,
int16 resist_adjust,
int *max_targets
)
{ {
Mob *curmob = nullptr; const auto &cast_target_position =
spells[spell_id].targettype == ST_Ring ?
caster_mob->GetTargetRingLocation() :
static_cast<glm::vec3>(center_mob->GetPosition());
float dist = caster->GetAOERange(spell_id); Mob *current_mob = nullptr;
float dist2 = dist * dist; bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; bool is_npc = caster_mob->IsNPC();
float dist_targ = 0; float distance = caster_mob->GetAOERange(spell_id);
float distance_squared = distance * distance;
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance};
glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance};
const auto &position = spells[spell_id].targettype == ST_Ring ? caster->GetTargetRingLocation() : static_cast<glm::vec3>(center->GetPosition()); /**
glm::vec2 min = { position.x - dist, position.y - dist }; * If using Old Rain Targets - there is no max target limitation
glm::vec2 max = { position.x + dist, position.y + dist }; */
if (RuleB(Spells, OldRainTargets)) {
max_targets = nullptr;
}
bool bad = IsDetrimentalSpell(spell_id); /**
bool isnpc = caster->IsNPC(); * Max AOE targets
*/
if (RuleB(Spells, OldRainTargets))
max_targets = nullptr; // ignore it!
// if we have a passed in value, use it, otherwise default to data
// detrimental Target AEs have a default value of 4 for PCs and unlimited for NPCs
int max_targets_allowed = 0; // unlimited int max_targets_allowed = 0; // unlimited
if (max_targets) // rains pass this in since they need to preserve the count through waves if (max_targets) { // rains pass this in since they need to preserve the count through waves
max_targets_allowed = *max_targets; max_targets_allowed = *max_targets;
else if (spells[spell_id].aemaxtargets) }
else if (spells[spell_id].aemaxtargets) {
max_targets_allowed = spells[spell_id].aemaxtargets; max_targets_allowed = spells[spell_id].aemaxtargets;
else if (IsTargetableAESpell(spell_id) && bad && !isnpc) }
else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc) {
max_targets_allowed = 4; max_targets_allowed = 4;
}
int iCounter = 0; int target_hit_counter = 0;
float distance_to_target = 0;
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { LogAoeCast(
curmob = it->second; "Close scan distance [{}] cast distance [{}]",
// test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized RuleI(Range, MobCloseScanDistance),
if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading()) distance
continue; );
if (curmob == caster && !affect_caster) //watch for caster too
continue;
if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient())
continue;
if (spells[spell_id].targettype == ST_AreaClientOnly && !curmob->IsClient())
continue;
if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC())
continue;
// check PC/NPC only flag 1 = PCs, 2 = NPCs
if (spells[spell_id].pcnpc_only_flag == 1 && !curmob->IsClient() && !curmob->IsMerc())
continue;
if (spells[spell_id].pcnpc_only_flag == 2 && (curmob->IsClient() || curmob->IsMerc()))
continue;
if (!IsWithinAxisAlignedBox(static_cast<glm::vec2>(curmob->GetPosition()), min, max))
continue;
dist_targ = DistanceSquared(curmob->GetPosition(), position); for (auto &it : entity_list.GetCloseMobList(caster_mob, distance)) {
current_mob = it.second;
if (dist_targ > dist2) //make sure they are in range if (!current_mob) {
continue; continue;
if (dist_targ < min_range2) //make sure they are in range }
LogAoeCast("Checking AOE against mob [{}]", current_mob->GetCleanName());
if (current_mob->IsClient() && !current_mob->CastToClient()->ClientFinishedLoading()) {
continue; continue;
if (isnpc && curmob->IsNPC() && spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting }
FACTION_VALUE f = curmob->GetReverseFactionCon(caster);
if (bad) { if (current_mob == caster_mob && !affect_caster) {
continue;
}
if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) {
continue;
}
if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) {
continue;
}
if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) {
continue;
}
/**
* Check PC / NPC
* 1 = PC
* 2 = NPC
*/
if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc() &&
!current_mob->IsBot()) {
continue;
}
if (spells[spell_id].pcnpc_only_flag == 2 &&
(current_mob->IsClient() || current_mob->IsMerc() || current_mob->IsBot())) {
continue;
}
if (!IsWithinAxisAlignedBox(static_cast<glm::vec2>(current_mob->GetPosition()), min, max)) {
continue;
}
distance_to_target = DistanceSquared(current_mob->GetPosition(), cast_target_position);
if (distance_to_target > distance_squared) {
continue;
}
if (distance_to_target < min_range2) {
continue;
}
if (is_npc && current_mob->IsNPC() &&
spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting
FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob);
if (is_detrimental_spell) {
//affect mobs that are on our hate list, or //affect mobs that are on our hate list, or
//which have bad faction with us //which have bad faction with us
if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) if (
!(caster_mob->CheckAggro(current_mob) ||
faction_value == FACTION_THREATENLY ||
faction_value == FACTION_SCOWLS)) {
continue; continue;
} else { }
}
else {
//only affect mobs we would assist. //only affect mobs we would assist.
if (!(f <= FACTION_AMIABLE)) if (!(faction_value <= FACTION_AMIABLE)) {
continue; continue;
}
} }
} }
//finally, make sure they are within range
if (bad) { /**
if (!caster->IsAttackAllowed(curmob, true)) * Finally, make sure they are within range
*/
if (is_detrimental_spell) {
if (!caster_mob->IsAttackAllowed(current_mob, true)) {
continue; continue;
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob)) }
if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) {
continue; continue;
if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize())) }
if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN(
caster_mob->GetTargetRingX(),
caster_mob->GetTargetRingY(),
caster_mob->GetTargetRingZ(),
current_mob->GetSize())) {
continue; continue;
} else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... }
// This does not check faction for beneficial AE buffs..only agro and attackable. }
// I've tested for spells that I can find without problem, but a faction-based else {
// check may still be needed. Any changes here should also reflect in BardAEPulse()
if (caster->IsAttackAllowed(curmob, true)) /**
* Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
* This does not check faction for beneficial AE buffs... only agro and attackable.
* I've tested for spells that I can find without problem, but a faction-based
* check may still be needed. Any changes here should also reflect in BardAEPulse()
*/
if (caster_mob->IsAttackAllowed(current_mob, true)) {
continue; continue;
if (caster->CheckAggro(curmob)) }
if (caster_mob->CheckAggro(current_mob)) {
continue; continue;
}
} }
curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); /**
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); * Increment hit count if max targets
*/
if (max_targets_allowed) { // if we have a limit, increment count if (max_targets_allowed) {
iCounter++; target_hit_counter++;
if (iCounter >= max_targets_allowed) // we done if (target_hit_counter >= max_targets_allowed) {
break; break;
}
} }
current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target);
caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust);
} }
if (max_targets && max_targets_allowed) LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName());
*max_targets = *max_targets - iCounter;
if (max_targets && max_targets_allowed) {
*max_targets = *max_targets - target_hit_counter;
}
} }
void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) /**
* @param caster
* @param center
* @param spell_id
* @param affect_caster
*/
void EntityList::MassGroupBuff(
Mob *caster,
Mob *center,
uint16 spell_id,
bool affect_caster)
{ {
Mob *curmob = nullptr; Mob *current_mob = nullptr;
float distance = caster->GetAOERange(spell_id);
float distance_squared = distance * distance;
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
float dist = caster->GetAOERange(spell_id); for (auto &it : entity_list.GetCloseMobList(caster, distance)) {
float dist2 = dist * dist; current_mob = it.second;
bool bad = IsDetrimentalSpell(spell_id); /**
* Skip center
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { */
curmob = it->second; if (current_mob == center) {
if (curmob == center) //do not affect center
continue;
if (curmob == caster && !affect_caster) //watch for caster too
continue;
if (DistanceSquared(center->GetPosition(), curmob->GetPosition()) > dist2) //make sure they are in range
continue; continue;
}
//Only npcs mgb should hit are client pets... /**
if (curmob->IsNPC()) { * Skip self
Mob *owner = curmob->GetOwner(); */
if (current_mob == caster && !affect_caster) {
continue;
}
if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range
continue;
}
/**
* Pets
*/
if (current_mob->IsNPC()) {
Mob *owner = current_mob->GetOwner();
if (owner) { if (owner) {
if (!owner->IsClient()) { if (!owner->IsClient()) {
continue; continue;
} }
} else { }
else {
continue; continue;
} }
} }
if (bad) { if (is_detrimental_spell) {
continue; continue;
} }
caster->SpellOnTarget(spell_id, curmob); caster->SpellOnTarget(spell_id, current_mob);
} }
} }
// causes caster to hit every mob within dist range of center with /**
// a bard pulse of spell_id. * Causes caster to hit every mob within dist range of center with a bard pulse of spell_id
// NPC spells will only affect other NPCs with compatible faction * NPC spells will only affect other NPCs with compatible faction
void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) *
* @param caster
* @param center
* @param spell_id
* @param affect_caster
*/
void EntityList::AEBardPulse(
Mob *caster,
Mob *center,
uint16 spell_id,
bool affect_caster)
{ {
Mob *curmob = nullptr; Mob *current_mob = nullptr;
float distance = caster->GetAOERange(spell_id);
float distance_squared = distance * distance;
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
bool is_npc = caster->IsNPC();
float dist = caster->GetAOERange(spell_id); for (auto &it : entity_list.GetCloseMobList(caster, distance)) {
float dist2 = dist * dist; current_mob = it.second;
bool bad = IsDetrimentalSpell(spell_id); /**
bool isnpc = caster->IsNPC(); * Skip self
*/
if (current_mob == center) {
continue;
}
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { if (current_mob == caster && !affect_caster) {
curmob = it->second;
if (curmob == center) //do not affect center
continue; continue;
if (curmob == caster && !affect_caster) //watch for caster too }
if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range
continue; continue;
if (DistanceSquared(center->GetPosition(), curmob->GetPosition()) > dist2) //make sure they are in range }
continue;
if (isnpc && curmob->IsNPC()) { //check npc->npc casting /**
FACTION_VALUE f = curmob->GetReverseFactionCon(caster); * check npc->npc casting
if (bad) { */
if (is_npc && current_mob->IsNPC()) {
FACTION_VALUE faction = current_mob->GetReverseFactionCon(caster);
if (is_detrimental_spell) {
//affect mobs that are on our hate list, or //affect mobs that are on our hate list, or
//which have bad faction with us //which have bad faction with us
if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) if (!(caster->CheckAggro(current_mob) || faction == FACTION_THREATENLY || faction == FACTION_SCOWLS)) {
continue; continue;
} else { }
}
else {
//only affect mobs we would assist. //only affect mobs we would assist.
if (!(f <= FACTION_AMIABLE)) if (!(faction <= FACTION_AMIABLE)) {
continue; continue;
}
} }
} }
//finally, make sure they are within range
if (bad) { /**
if (!center->CheckLosFN(curmob)) * LOS
*/
if (is_detrimental_spell) {
if (!center->CheckLosFN(current_mob)) {
continue; continue;
} else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... }
}
else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
// See notes in AESpell() above for more info. // See notes in AESpell() above for more info.
if (caster->IsAttackAllowed(curmob, true)) if (caster->IsAttackAllowed(current_mob, true)) {
continue; continue;
if (caster->CheckAggro(curmob)) }
if (caster->CheckAggro(current_mob)) {
continue; continue;
}
} }
//if we get here... cast the spell. current_mob->BardPulse(spell_id, caster);
curmob->BardPulse(spell_id, caster);
} }
if (caster->IsClient()) if (caster->IsClient()) {
caster->CastToClient()->CheckSongSkillIncrease(spell_id); caster->CastToClient()->CheckSongSkillIncrease(spell_id);
}
} }
// Rampage and stuff for clients. Normal and Duration rampages /**
//NPCs handle it differently in Mob::Rampage * Rampage - Normal and Duration rampages
void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) { * NPCs handle it differently in Mob::Rampage
//Dook- Will need tweaking, currently no pets or players or horses *
Mob *curmob = nullptr; * @param attacker
* @param distance
* @param Hand
* @param count
* @param is_from_spell
*/
void EntityList::AEAttack(
Mob *attacker,
float distance,
int Hand,
int count,
bool is_from_spell)
{
Mob *current_mob = nullptr;
float distance_squared = distance * distance;
int hit_count = 0;
float dist2 = dist * dist; for (auto &it : entity_list.GetCloseMobList(attacker, distance)) {
current_mob = it.second;
int hit = 0; if (current_mob->IsNPC()
&& current_mob != attacker //this is not needed unless NPCs can use this
&& (attacker->IsAttackAllowed(current_mob))
&& current_mob->GetRace() != 216 && current_mob->GetRace() != 472 /* dont attack horses */
&& (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared)
) {
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) {
curmob = it->second; attacker->Attack(current_mob, Hand, false, false, is_from_spell);
if (curmob->IsNPC() }
&& curmob != attacker //this is not needed unless NPCs can use this else {
&&(attacker->IsAttackAllowed(curmob)) attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
&& curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */ }
&& (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2)
) { hit_count++;
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) if (count != 0 && hit_count >= count) {
attacker->Attack(curmob, Hand, false, false, IsFromSpell);
else
attacker->CastToClient()->DoAttackRounds(curmob, Hand, IsFromSpell);
hit++;
if (count != 0 && hit >= count)
return; return;
}
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1812,6 +1812,43 @@ XS(XS__summonallplayercorpses) {
XSRETURN(1); XSRETURN(1);
} }
XS(XS__getplayercorpsecount);
XS(XS__getplayercorpsecount) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: quest::getplayercorpsecount(uint32 char_id)");
uint32 RETVAL;
dXSTARG;
uint32 char_id = (int) SvIV(ST(0));
RETVAL = quest_manager.getplayercorpsecount(char_id);
XSprePUSH;
PUSHu((IV) RETVAL);
XSRETURN(1);
}
XS(XS__getplayercorpsecountbyzoneid);
XS(XS__getplayercorpsecountbyzoneid) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: quest::getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id)");
uint32 RETVAL;
dXSTARG;
uint32 char_id = (int) SvIV(ST(0));
uint32 zone_id = (int)SvIV(ST(1));
RETVAL = quest_manager.getplayercorpsecountbyzoneid(char_id, zone_id);
XSprePUSH;
PUSHu((IV) RETVAL);
XSRETURN(1);
}
XS(XS__getplayerburiedcorpsecount); XS(XS__getplayerburiedcorpsecount);
XS(XS__getplayerburiedcorpsecount) { XS(XS__getplayerburiedcorpsecount) {
dXSARGS; dXSARGS;
@ -3907,6 +3944,8 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file);
newXS(strcpy(buf, "getlevel"), XS__getlevel, file); newXS(strcpy(buf, "getlevel"), XS__getlevel, file);
newXS(strcpy(buf, "getplayerburiedcorpsecount"), XS__getplayerburiedcorpsecount, file); newXS(strcpy(buf, "getplayerburiedcorpsecount"), XS__getplayerburiedcorpsecount, file);
newXS(strcpy(buf, "getplayercorpsecount"), XS__getplayercorpsecount, file);
newXS(strcpy(buf, "getplayercorpsecountbyzoneid"), XS__getplayercorpsecountbyzoneid, file);
newXS(strcpy(buf, "gettaskactivitydonecount"), XS__gettaskactivitydonecount, file); newXS(strcpy(buf, "gettaskactivitydonecount"), XS__gettaskactivitydonecount, file);
newXS(strcpy(buf, "givecash"), XS__givecash, file); newXS(strcpy(buf, "givecash"), XS__givecash, file);
newXS(strcpy(buf, "gmmove"), XS__gmmove, file); newXS(strcpy(buf, "gmmove"), XS__gmmove, file);

View File

@ -137,10 +137,10 @@ void Embperl::DoInit() {
try { try {
init_eval_file(); init_eval_file();
} }
catch(const char *err) catch(std::string e)
{ {
//remember... lasterr() is no good if we crap out here, in construction //remember... lasterr() is no good if we crap out here, in construction
LogQuests("perl error: [{}]", err); LogQuests("Perl Error [{}]", e);
throw "failed to install eval_file hook"; throw "failed to install eval_file hook";
} }
@ -177,9 +177,9 @@ void Embperl::DoInit() {
perl_command = "main::eval_file('plugin', '" + Config->PluginPlFile + "');"; perl_command = "main::eval_file('plugin', '" + Config->PluginPlFile + "');";
eval_pv(perl_command.c_str(), FALSE); eval_pv(perl_command.c_str(), FALSE);
} }
catch(const char *err) catch(std::string e)
{ {
LogQuests("Warning - [{}]: [{}]", Config->PluginPlFile.c_str(), err); LogQuests("Warning [{}]: [{}]", Config->PluginPlFile, e);
} }
try try
{ {
@ -195,9 +195,9 @@ void Embperl::DoInit() {
"}"; "}";
eval_pv(perl_command.c_str(),FALSE); eval_pv(perl_command.c_str(),FALSE);
} }
catch(const char *err) catch(std::string e)
{ {
LogQuests("Perl warning: [{}]", err); LogQuests("Warning [{}]", e);
} }
#endif //EMBPERL_PLUGIN #endif //EMBPERL_PLUGIN
in_use = false; in_use = false;
@ -237,7 +237,7 @@ void Embperl::init_eval_file(void)
{ {
eval_pv( eval_pv(
"our %Cache;" "our %Cache;"
"no warnings;" "no warnings 'all';"
"use Symbol qw(delete_package);" "use Symbol qw(delete_package);"
"sub eval_file {" "sub eval_file {"
"my($package, $filename) = @_;" "my($package, $filename) = @_;"
@ -315,7 +315,7 @@ int Embperl::dosub(const char * subname, const std::vector<std::string> * args,
{ {
std::string errmsg = "Perl runtime error: "; std::string errmsg = "Perl runtime error: ";
errmsg += SvPVX(ERRSV); errmsg += SvPVX(ERRSV);
throw errmsg.c_str(); throw errmsg;
} }
return ret_value; return ret_value;

View File

@ -92,35 +92,43 @@ XS(XS_EQEmuIO_PRINT)
// Perl_croak(aTHX_ "Usage: EQEmuIO::PRINT(@strings)"); // Perl_croak(aTHX_ "Usage: EQEmuIO::PRINT(@strings)");
int r; int r;
for(r = 1; r < items; r++) { for (r = 1; r < items; r++) {
char *str = SvPV_nolen(ST(r)); char *str = SvPV_nolen(ST(r));
char *cur = str; char *cur = str;
/* Strip newlines from log message 'str' */ /* Strip newlines from log message 'str' */
*std::remove(str, str + strlen(str), '\n') = '\0'; *std::remove(str, str + strlen(str), '\n') = '\0';
std::string log_string = str; std::string log_string = str;
if (log_string.find("did not return a true") != std::string::npos)
return;; if (log_string.find("did not return a true") != std::string::npos) {
return;
}
if (log_string.find("is experimental") != std::string::npos) {
return;
}
int i; int i;
int pos = 0; int pos = 0;
int len = 0; int len = 0;
for(i = 0; *cur != '\0'; i++, cur++) {
if(*cur == '\n') { for (i = 0; *cur != '\0'; i++, cur++) {
Log(Logs::General, Logs::Quests, str); if (*cur == '\n') {
LogQuests("{}", str);
len = 0; len = 0;
pos = i+1; pos = i + 1;
} else { }
else {
len++; len++;
} }
} }
if(len > 0) { if (!log_string.empty()) {
Log(Logs::General, Logs::Quests, str); LogQuests("{}", log_string);
} }
} }
XSRETURN_EMPTY; XSRETURN_EMPTY;
} }
#endif //EMBPERL_IO_CAPTURE #endif //EMBPERL_IO_CAPTURE

View File

@ -62,7 +62,8 @@ extern char errorname[32];
Entity::Entity() Entity::Entity()
{ {
id = 0; id = 0;
initial_id = 0;
spawn_timestamp = time(nullptr); spawn_timestamp = time(nullptr);
} }
@ -1582,41 +1583,73 @@ void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *a
} }
} }
void EntityList::QueueCloseClients(Mob *sender, const EQApplicationPacket *app, /**
bool ignore_sender, float dist, Mob *SkipThisMob, bool ackreq, eqFilterType filter) * @param sender
* @param app
* @param ignore_sender
* @param distance
* @param skipped_mob
* @param is_ack_required
* @param filter
*/
void EntityList::QueueCloseClients(
Mob *sender,
const EQApplicationPacket *app,
bool ignore_sender,
float distance,
Mob *skipped_mob,
bool is_ack_required,
eqFilterType filter
)
{ {
if (sender == nullptr) { if (sender == nullptr) {
QueueClients(sender, app, ignore_sender); QueueClients(sender, app, ignore_sender);
return; return;
} }
if (dist <= 0) if (distance <= 0) {
dist = 600; distance = 600;
float dist2 = dist * dist; //pow(dist, 2); }
auto it = client_list.begin(); float distance_squared = distance * distance;
while (it != client_list.end()) {
Client *ent = it->second;
if ((!ignore_sender || ent != sender) && (ent != SkipThisMob)) { for (auto &e : GetCloseMobList(sender, distance)) {
eqFilterMode filter2 = ent->GetFilter(filter); Mob *mob = e.second;
if(ent->Connected() &&
(filter == FilterNone if (!mob->IsClient()) {
|| filter2 == FilterShow continue;
|| (filter2 == FilterShowGroupOnly && (sender == ent || }
(ent->GetGroup() && ent->GetGroup()->IsGroupMember(sender))))
|| (filter2 == FilterShowSelfOnly && ent == sender)) Client *client = mob->CastToClient();
&& (DistanceSquared(ent->GetPosition(), sender->GetPosition()) <= dist2)) {
ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); if ((!ignore_sender || client != sender) && (client != skipped_mob)) {
if (DistanceSquared(client->GetPosition(), sender->GetPosition()) >= distance_squared) {
continue;
}
if (!client->Connected()) {
continue;
}
eqFilterMode client_filter = client->GetFilter(filter);
if (
filter == FilterNone || client_filter == FilterShow ||
(client_filter == FilterShowGroupOnly &&
(sender == client || (client->GetGroup() && client->GetGroup()->IsGroupMember(sender)))) ||
(client_filter == FilterShowSelfOnly && client == sender)
) {
client->QueuePacket(app, is_ack_required, Client::CLIENT_CONNECTED);
} }
} }
++it;
} }
} }
//sender can be null //sender can be null
void EntityList::QueueClients(Mob *sender, const EQApplicationPacket *app, void EntityList::QueueClients(
bool ignore_sender, bool ackreq) Mob *sender, const EQApplicationPacket *app,
bool ignore_sender, bool ackreq
)
{ {
auto it = client_list.begin(); auto it = client_list.begin();
while (it != client_list.end()) { while (it != client_list.end()) {
@ -2486,43 +2519,51 @@ void EntityList::RemoveAllEncounters()
} }
} }
/**
* @param delete_id
* @return
*/
bool EntityList::RemoveMob(uint16 delete_id) bool EntityList::RemoveMob(uint16 delete_id)
{ {
if (delete_id == 0) if (delete_id == 0) {
return true; return true;
}
auto it = mob_list.find(delete_id); auto it = mob_list.find(delete_id);
if (it != mob_list.end()) { if (it != mob_list.end()) {
if (npc_list.count(delete_id)) {
RemoveMobFromClientCloseLists(it->second);
if (npc_list.count(delete_id))
entity_list.RemoveNPC(delete_id); entity_list.RemoveNPC(delete_id);
else if (client_list.count(delete_id)) }
else if (client_list.count(delete_id)) {
entity_list.RemoveClient(delete_id); entity_list.RemoveClient(delete_id);
}
safe_delete(it->second); safe_delete(it->second);
if (!corpse_list.count(delete_id)) if (!corpse_list.count(delete_id)) {
free_ids.push(it->first); free_ids.push(it->first);
}
mob_list.erase(it); mob_list.erase(it);
return true; return true;
} }
return false; return false;
} }
// This is for if the ID is deleted for some reason /**
* @param delete_mob
* @return
*/
bool EntityList::RemoveMob(Mob *delete_mob) bool EntityList::RemoveMob(Mob *delete_mob)
{ {
if (delete_mob == 0) if (delete_mob == 0) {
return true; return true;
}
auto it = mob_list.begin(); auto it = mob_list.begin();
while (it != mob_list.end()) { while (it != mob_list.end()) {
if (it->second == delete_mob) { if (it->second == delete_mob) {
RemoveMobFromClientCloseLists(it->second);
safe_delete(it->second); safe_delete(it->second);
if (!corpse_list.count(it->first)) if (!corpse_list.count(it->first)) {
free_ids.push(it->first); free_ids.push(it->first);
}
mob_list.erase(it); mob_list.erase(it);
return true; return true;
} }
@ -2531,36 +2572,114 @@ bool EntityList::RemoveMob(Mob *delete_mob)
return false; return false;
} }
/**
* @param delete_id
* @return
*/
bool EntityList::RemoveNPC(uint16 delete_id) bool EntityList::RemoveNPC(uint16 delete_id)
{ {
auto it = npc_list.find(delete_id); auto it = npc_list.find(delete_id);
if (it != npc_list.end()) { if (it != npc_list.end()) {
NPC *npc = it->second; NPC *npc = it->second;
// make sure its proximity is removed
RemoveProximity(delete_id); RemoveProximity(delete_id);
// remove from client close lists
RemoveMobFromClientCloseLists(npc->CastToMob());
// remove from the list
npc_list.erase(it); npc_list.erase(it);
// remove from limit list if needed if (npc_limit_list.count(delete_id)) {
if (npc_limit_list.count(delete_id))
npc_limit_list.erase(delete_id); npc_limit_list.erase(delete_id);
}
return true; return true;
} }
return false; return false;
} }
bool EntityList::RemoveMobFromClientCloseLists(Mob *mob) /**
* @param mob
* @return
*/
bool EntityList::RemoveMobFromCloseLists(Mob *mob)
{ {
auto it = client_list.begin(); uint16 entity_id = mob->GetID() > 0 ? mob->GetID() : mob->GetInitialId();
while (it != client_list.end()) {
it->second->close_mobs.erase(mob); LogEntityManagement(
"Attempting to remove mob [{}] from close lists entity_id ({})",
mob->GetCleanName(),
entity_id
);
auto it = mob_list.begin();
while (it != mob_list.end()) {
LogEntityManagement(
"Removing mob [{}] from [{}] close list entity_id ({})",
mob->GetCleanName(),
it->second->GetCleanName(),
entity_id
);
it->second->close_mobs.erase(entity_id);
++it; ++it;
} }
return false; return false;
} }
/**
* @param mob
* @return
*/
void EntityList::RemoveAuraFromMobs(Mob *aura)
{
LogEntityManagement(
"Attempting to remove aura [{}] from mobs entity_id ({})",
aura->GetCleanName(),
aura->GetID()
);
for (auto &it : mob_list) {
auto mob = it.second;
mob->RemoveAura(aura->GetID());
}
}
/**
* @param close_mobs
* @param scanning_mob
*/
void EntityList::ScanCloseMobs(std::unordered_map<uint16, Mob *> &close_mobs, Mob *scanning_mob)
{
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
close_mobs.clear();
for (auto &e : mob_list) {
auto mob = e.second;
if (!mob->IsNPC() && !mob->IsClient()) {
continue;
}
if (mob->GetID() <= 0) {
continue;
}
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range) {
close_mobs.insert(std::pair<uint16, Mob *>(mob->GetID(), mob));
}
else if (mob->GetAggroRange() >= scan_range) {
close_mobs.insert(std::pair<uint16, Mob *>(mob->GetID(), mob));
}
}
LogAIScanClose(
"[{}] Scanning Close List | list_size [{}] moving [{}]",
scanning_mob->GetCleanName(),
close_mobs.size(),
scanning_mob->IsMoving() ? "true" : "false"
);
}
bool EntityList::RemoveMerc(uint16 delete_id) bool EntityList::RemoveMerc(uint16 delete_id)
{ {
auto it = merc_list.find(delete_id); auto it = merc_list.find(delete_id);
@ -4859,10 +4978,10 @@ void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radiu
continue; continue;
} }
// check PC/NPC only flag 1 = PCs, 2 = NPCs // check PC/NPC only flag 1 = PCs, 2 = NPCs
if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc()) { if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc() && !ptr->IsBot()) {
++it; ++it;
continue; continue;
} else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc())) { } else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc() || ptr->IsBot())) {
++it; ++it;
continue; continue;
} }
@ -4972,3 +5091,21 @@ void EntityList::ReloadMerchants() {
} }
} }
} }
/**
* If we have a distance requested that is greater than our scanning distance
* then we return the full list
*
* @param mob
* @param distance
* @return
*/
std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance)
{
if (distance <= RuleI(Range, MobCloseScanDistance)) {
return mob->close_mobs;
}
return mob_list;
}

View File

@ -109,6 +109,7 @@ public:
const Beacon *CastToBeacon() const; const Beacon *CastToBeacon() const;
const Encounter *CastToEncounter() const; const Encounter *CastToEncounter() const;
inline const uint16& GetInitialId() const { return initial_id; }
inline const uint16& GetID() const { return id; } inline const uint16& GetID() const { return id; }
inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; } inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; }
@ -122,10 +123,17 @@ public:
protected: protected:
friend class EntityList; friend class EntityList;
inline virtual void SetID(uint16 set_id) { id = set_id; } inline virtual void SetID(uint16 set_id) {
id = set_id;
if (initial_id == 0 && set_id > 0) {
initial_id = set_id;
}
}
uint32 pDBAsyncWorkID; uint32 pDBAsyncWorkID;
private: private:
uint16 id; uint16 id;
uint16 initial_id;
time_t spawn_timestamp; time_t spawn_timestamp;
}; };
@ -284,7 +292,8 @@ public:
bool RemoveTrap(uint16 delete_id); bool RemoveTrap(uint16 delete_id);
bool RemoveObject(uint16 delete_id); bool RemoveObject(uint16 delete_id);
bool RemoveProximity(uint16 delete_npc_id); bool RemoveProximity(uint16 delete_npc_id);
bool RemoveMobFromClientCloseLists(Mob *mob); bool RemoveMobFromCloseLists(Mob *mob);
void RemoveAuraFromMobs(Mob *aura);
void RemoveAllMobs(); void RemoveAllMobs();
void RemoveAllClients(); void RemoveAllClients();
void RemoveAllNPCs(); void RemoveAllNPCs();
@ -376,7 +385,7 @@ public:
void RemoveFromXTargets(Mob* mob); void RemoveFromXTargets(Mob* mob);
void RemoveFromAutoXTargets(Mob* mob); void RemoveFromAutoXTargets(Mob* mob);
void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget);
void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float dist=200, Mob* SkipThisMob = 0, bool ackreq = true,eqFilterType filter=FilterNone); void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float distance=200, Mob* skipped_mob = 0, bool is_ack_required = true, eqFilterType filter=FilterNone);
void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true);
void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = 0, uint8 maxstatus = 0); void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = 0, uint8 maxstatus = 0);
void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0); void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0);
@ -388,11 +397,24 @@ public:
void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app);
void QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); void QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true);
void AEAttack(Mob *attacker, float dist, int Hand = EQEmu::invslot::slotPrimary, int count = 0, bool IsFromSpell = false); void AEAttack(
void AETaunt(Client *caster, float range=0, int32 bonus_hate=0); Mob *attacker,
void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0, int *max_targets = nullptr); float distance,
void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); int Hand = EQEmu::invslot::slotPrimary,
void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); int count = 0,
bool is_from_spell = false
);
void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0);
void AESpell(
Mob *caster,
Mob *center,
uint16 spell_id,
bool affect_caster = true,
int16 resist_adjust = 0,
int *max_targets = nullptr
);
void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true);
void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true);
//trap stuff //trap stuff
Mob* GetTrapTrigger(Trap* trap); Mob* GetTrapTrigger(Trap* trap);
@ -443,11 +465,7 @@ public:
bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count); bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count);
bool LimitCheckName(const char* npc_name); bool LimitCheckName(const char* npc_name);
void CheckClientAggro(Client *around);
Mob* AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange);
int GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con); int GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con);
void AIYellForHelp(Mob* sender, Mob* attacker);
bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes); bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
Mob* GetTargetForMez(Mob* caster); Mob* GetTargetForMez(Mob* caster);
uint32 CheckNPCsClose(Mob *center); uint32 CheckNPCsClose(Mob *center);
@ -495,17 +513,21 @@ public:
inline const std::unordered_map<uint16, Object *> &GetObjectList() { return object_list; } inline const std::unordered_map<uint16, Object *> &GetObjectList() { return object_list; }
inline const std::unordered_map<uint16, Doors *> &GetDoorsList() { return door_list; } inline const std::unordered_map<uint16, Doors *> &GetDoorsList() { return door_list; }
std::unordered_map<uint16, Mob *> &GetCloseMobList(Mob *mob, float distance = 0);
void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true);
uint16 GetFreeID(); uint16 GetFreeID();
void RefreshAutoXTargets(Client *c); void RefreshAutoXTargets(Client *c);
void RefreshClientXTargets(Client *c); void RefreshClientXTargets(Client *c);
void SendAlternateAdvancementStats(); void SendAlternateAdvancementStats();
void ScanCloseMobs(std::unordered_map<uint16, Mob *> &close_mobs, Mob *scanning_mob);
void GetTrapInfo(Client* client); void GetTrapInfo(Client* client);
bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group);
void UpdateAllTraps(bool respawn, bool repopnow = false); void UpdateAllTraps(bool respawn, bool repopnow = false);
void ClearTrapPointers(); void ClearTrapPointers();
protected: protected:
friend class Zone; friend class Zone;
void Depop(bool StartSpawnTimer = false); void Depop(bool StartSpawnTimer = false);
@ -553,10 +575,13 @@ private:
Mob* GetMobByBotID(uint32 botID); Mob* GetMobByBotID(uint32 botID);
Bot* GetBotByBotID(uint32 botID); Bot* GetBotByBotID(uint32 botID);
Bot* GetBotByBotName(std::string botName); Bot* GetBotByBotName(std::string botName);
Client* GetBotOwnerByBotEntityID(uint16 entityID);
std::list<Bot*> GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); std::list<Bot*> GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID);
bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate
void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff
void ScanCloseClientMobs(std::unordered_map<uint16, Mob*>& close_mobs, Mob* scanning_mob);
private: private:
std::list<Bot*> bot_list; std::list<Bot*> bot_list;
#endif #endif

View File

@ -86,6 +86,8 @@ typedef enum {
EVENT_SPAWN_ZONE, EVENT_SPAWN_ZONE,
EVENT_DEATH_ZONE, EVENT_DEATH_ZONE,
EVENT_USE_SKILL, EVENT_USE_SKILL,
EVENT_COMBINE_VALIDATE,
EVENT_BOT_COMMAND,
_LargestEventID _LargestEventID
} QuestEventID; } QuestEventID;

View File

@ -30,65 +30,80 @@ extern Zone* zone;
#define FEAR_PATHING_DEBUG #define FEAR_PATHING_DEBUG
//this is called whenever we are damaged to process possible fleeing //this is called whenever we are damaged to process possible fleeing
void Mob::CheckFlee() { void Mob::CheckFlee()
{
// if mob is dead why would you run? // if mob is dead why would you run?
if(GetHP() == 0) { if (GetHP() == 0) {
return; return;
} }
// if were already fleeing, don't need to check more... // if were already fleeing, don't need to check more...
if(flee_mode && currently_fleeing) { if (flee_mode && currently_fleeing) {
return; return;
} }
//dont bother if we are immune to fleeing //dont bother if we are immune to fleeing
if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) { if (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) {
LogFlee("Mob [{}] is immune to fleeing via special ability or spell bonus", GetCleanName());
return; return;
} }
// Check if Flee Timer is cleared // Check if Flee Timer is cleared
if(!flee_timer.Check()) { if (!flee_timer.Check()) {
return; return;
} }
int hpratio = GetIntHPRatio(); int hp_ratio = GetIntHPRatio();
int fleeratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists int flee_ratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists
Mob *hate_top = GetHateTop(); Mob *hate_top = GetHateTop();
LogFlee("Mob [{}] hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio);
// Sanity Check for race conditions // Sanity Check for race conditions
if(hate_top == nullptr) { if (hate_top == nullptr) {
return; return;
} }
// If no special flee_percent check for Gray or Other con rates // If no special flee_percent check for Gray or Other con rates
if(GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && fleeratio == 0 && RuleB(Combat, FleeGray) && GetLevel() <= RuleI(Combat, FleeGrayMaxLevel)) { if (GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && flee_ratio == 0 && RuleB(Combat, FleeGray) &&
fleeratio = RuleI(Combat, FleeGrayHPRatio); GetLevel() <= RuleI(Combat, FleeGrayMaxLevel)) {
} else if(fleeratio == 0) { flee_ratio = RuleI(Combat, FleeGrayHPRatio);
fleeratio = RuleI(Combat, FleeHPRatio ); LogFlee("Mob [{}] using combat flee gray hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio);
}
else if (flee_ratio == 0) {
flee_ratio = RuleI(Combat, FleeHPRatio);
LogFlee("Mob [{}] using combat flee hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio);
} }
// Mob does not have low enough health to flee bool mob_has_low_enough_health_to_flee = hp_ratio >= flee_ratio;
if(hpratio >= fleeratio) { if (mob_has_low_enough_health_to_flee) {
LogFlee(
"Mob [{}] does not have low enough health to flee | hp_ratio [{}] flee_ratio [{}]",
GetCleanName(),
hp_ratio,
flee_ratio
);
return; return;
} }
// Sanity Check this should never happen... // Sanity Check this should never happen...
if(!hate_top) { if (!hate_top) {
currently_fleeing = true;
StartFleeing(); StartFleeing();
return; return;
} }
int other_ratio = hate_top->GetIntHPRatio(); int other_ratio = hate_top->GetIntHPRatio();
// If the Client is nearing death the NPC will not flee and instead try to kill the client. // If the Client is nearing death the NPC will not flee and instead try to kill the client.
if(other_ratio < 20) { if (other_ratio < 20) {
return; return;
} }
// Flee Chance checking based on con. // Flee Chance checking based on con.
uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel()); uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel());
int flee_chance; int flee_chance;
switch(con) { switch (con) {
//these values are not 100% researched //these values are not 100% researched
case CON_GRAY: case CON_GRAY:
flee_chance = 100; flee_chance = 100;
@ -107,9 +122,32 @@ void Mob::CheckFlee() {
break; break;
} }
// If we got here we are allowed to roll on flee chance if there is not other hated NPC's in the area. LogFlee(
"Post con-switch | Mob [{}] con [{}] hp_ratio [{}] flee_ratio [{}] flee_chance [{}]",
GetCleanName(),
con,
hp_ratio,
flee_ratio,
flee_chance
);
// If we got here we are allowed to roll on flee chance if there is not other hated NPC's in the area.
// ALWAYS_FLEE, skip roll
// if FleeIfNotAlone is true, we skip alone check
// roll chance
if (GetSpecialAbility(ALWAYS_FLEE) ||
((RuleB(Combat, FleeIfNotAlone) || entity_list.GetHatedCount(hate_top, this, true) == 0) &&
zone->random.Roll(flee_chance))) {
LogFlee(
"Passed all checks to flee | Mob [{}] con [{}] hp_ratio [{}] flee_ratio [{}] flee_chance [{}]",
GetCleanName(),
con,
hp_ratio,
flee_ratio,
flee_chance
);
if(RuleB(Combat, FleeIfNotAlone) || GetSpecialAbility(ALWAYS_FLEE) || zone->random.Roll(flee_chance) && entity_list.GetHatedCount(hate_top, this, true) == 0) {
currently_fleeing = true; currently_fleeing = true;
StartFleeing(); StartFleeing();
} }
@ -155,7 +193,8 @@ void Mob::ProcessFlee()
} }
} }
void Mob::CalculateNewFearpoint() { void Mob::CalculateNewFearpoint()
{
if (RuleB(Pathing, Fear) && zone->pathing) { if (RuleB(Pathing, Fear) && zone->pathing) {
auto Node = zone->pathing->GetRandomLocation(glm::vec3(GetX(), GetY(), GetZ())); auto Node = zone->pathing->GetRandomLocation(glm::vec3(GetX(), GetY(), GetZ()));
if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) { if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) {
@ -165,9 +204,7 @@ void Mob::CalculateNewFearpoint() {
return; return;
} }
Log(Logs::Detail, LogPathing("No path found to selected node during CalculateNewFearpoint.");
Logs::Pathing,
"No path found to selected node during CalculateNewFearpoint.");
} }
} }

View File

@ -309,40 +309,42 @@ void Client::GoFish()
if(food_id == 0) { if(food_id == 0) {
int index = zone->random.Int(0, MAX_COMMON_FISH_IDS-1); int index = zone->random.Int(0, MAX_COMMON_FISH_IDS-1);
food_id = common_fish_ids[index]; food_id = (RuleB(Character, UseNoJunkFishing) ? 13019 : common_fish_ids[index]);
} }
const EQEmu::ItemData* food_item = database.GetItem(food_id); const EQEmu::ItemData* food_item = database.GetItem(food_id);
if (food_item) {
if (food_item->ItemType != EQEmu::item::ItemTypeFood) { if (food_item->ItemType != EQEmu::item::ItemTypeFood) {
MessageString(Chat::Skills, FISHING_SUCCESS); MessageString(Chat::Skills, FISHING_SUCCESS);
}
else {
MessageString(Chat::Skills, FISHING_SUCCESS_FISH_NAME, food_item->Name);
}
EQEmu::ItemInstance* inst = database.CreateItem(food_item, 1);
if(inst != nullptr) {
if(CheckLoreConflict(inst->GetItem()))
{
MessageString(Chat::White, DUP_LORE);
safe_delete(inst);
} }
else else {
{ MessageString(Chat::Skills, FISHING_SUCCESS_FISH_NAME, food_item->Name);
PushItemOnCursor(*inst);
SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketLimbo);
if(RuleB(TaskSystem, EnableTaskSystem))
UpdateTasksForItem(ActivityFish, food_id);
safe_delete(inst);
inst = m_inv.GetItem(EQEmu::invslot::slotCursor);
} }
if(inst) { EQEmu::ItemInstance* inst = database.CreateItem(food_item, 1);
std::vector<EQEmu::Any> args; if (inst != nullptr) {
args.push_back(inst); if (CheckLoreConflict(inst->GetItem()))
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args); {
MessageString(Chat::White, DUP_LORE);
safe_delete(inst);
}
else
{
PushItemOnCursor(*inst);
SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketLimbo);
if (RuleB(TaskSystem, EnableTaskSystem))
UpdateTasksForItem(ActivityFish, food_id);
safe_delete(inst);
inst = m_inv.GetItem(EQEmu::invslot::slotCursor);
}
if (inst) {
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
}
} }
} }
} }

View File

@ -155,13 +155,18 @@ Mob* HateList::GetDamageTopOnHateList(Mob* hater)
return current; return current;
} }
Mob* HateList::GetClosestEntOnHateList(Mob *hater) { Mob* HateList::GetClosestEntOnHateList(Mob *hater, bool skip_mezzed) {
Mob* close_entity = nullptr; Mob* close_entity = nullptr;
float close_distance = 99999.9f; float close_distance = 99999.9f;
float this_distance; float this_distance;
auto iterator = list.begin(); auto iterator = list.begin();
while (iterator != list.end()) { while (iterator != list.end()) {
if (skip_mezzed && (*iterator)->entity_on_hatelist->IsMezzed()) {
++iterator;
continue;
}
this_distance = DistanceSquaredNoZ((*iterator)->entity_on_hatelist->GetPosition(), hater->GetPosition()); this_distance = DistanceSquaredNoZ((*iterator)->entity_on_hatelist->GetPosition(), hater->GetPosition());
if ((*iterator)->entity_on_hatelist != nullptr && this_distance <= close_distance) { if ((*iterator)->entity_on_hatelist != nullptr && this_distance <= close_distance) {
close_distance = this_distance; close_distance = this_distance;
@ -297,7 +302,7 @@ int HateList::GetHateRatio(Mob *top, Mob *other)
// skip is used to ignore a certain mob on the list // skip is used to ignore a certain mob on the list
// Currently used for getting 2nd on list for aggro meter // Currently used for getting 2nd on list for aggro meter
Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip) Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip, bool skip_mezzed)
{ {
// hack fix for zone shutdown crashes on some servers // hack fix for zone shutdown crashes on some servers
if (!zone->IsLoaded()) if (!zone->IsLoaded())
@ -335,6 +340,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
continue; continue;
} }
if (skip_mezzed && cur->entity_on_hatelist->IsMezzed()) {
++iterator;
continue;
}
if (cur->entity_on_hatelist->Sanctuary()) { if (cur->entity_on_hatelist->Sanctuary()) {
if (hate == -1) if (hate == -1)
{ {
@ -465,6 +475,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
continue; continue;
} }
if (skip_mezzed && cur->entity_on_hatelist->IsMezzed()) {
++iterator;
continue;
}
if (cur->entity_on_hatelist != nullptr && ((cur->stored_hate_amount > hate) || cur->is_entity_frenzy)) if (cur->entity_on_hatelist != nullptr && ((cur->stored_hate_amount > hate) || cur->is_entity_frenzy))
{ {
top_hate = cur->entity_on_hatelist; top_hate = cur->entity_on_hatelist;
@ -480,7 +495,7 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
return nullptr; return nullptr;
} }
Mob *HateList::GetEntWithMostHateOnList(){ Mob *HateList::GetEntWithMostHateOnList(bool skip_mezzed){
Mob* top = nullptr; Mob* top = nullptr;
int64 hate = -1; int64 hate = -1;
@ -490,8 +505,10 @@ Mob *HateList::GetEntWithMostHateOnList(){
struct_HateList *cur = (*iterator); struct_HateList *cur = (*iterator);
if (cur && cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate)) if (cur && cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate))
{ {
top = cur->entity_on_hatelist; if (!skip_mezzed || !cur->entity_on_hatelist->IsMezzed()) {
hate = cur->stored_hate_amount; top = cur->entity_on_hatelist;
hate = cur->stored_hate_amount;
}
} }
++iterator; ++iterator;
} }
@ -499,26 +516,50 @@ Mob *HateList::GetEntWithMostHateOnList(){
} }
Mob *HateList::GetRandomEntOnHateList() Mob *HateList::GetRandomEntOnHateList(bool skip_mezzed)
{ {
int count = list.size(); int count = list.size();
if (count == 0) //If we don't have any entries it'll crash getting a random 0, -1 position. if (count <= 0) //If we don't have any entries it'll crash getting a random 0, -1 position.
return NULL; return nullptr;
if (count == 1) //No need to do all that extra work if we only have one hate entry if (count == 1) //No need to do all that extra work if we only have one hate entry
{ {
if (*list.begin()) // Just in case tHateEntry is invalidated somehow... if (*list.begin() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) // Just in case tHateEntry is invalidated somehow...
return (*list.begin())->entity_on_hatelist; return (*list.begin())->entity_on_hatelist;
return NULL; return nullptr;
} }
auto iterator = list.begin(); if (skip_mezzed) {
int random = zone->random.Int(0, count - 1);
for (int i = 0; i < random; i++)
++iterator;
return (*iterator)->entity_on_hatelist; for (auto iter : list) {
if (iter->entity_on_hatelist->IsMezzed()) {
--count;
}
}
if (count <= 0) {
return nullptr;
}
}
int random = zone->random.Int(0, count - 1);
int counter = 0;
for (auto iter : list) {
if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) {
continue;
}
if (counter < random) {
++counter;
continue;
}
return iter->entity_on_hatelist;
}
return nullptr;
} }
Mob *HateList::GetEscapingEntOnHateList() { Mob *HateList::GetEscapingEntOnHateList() {

View File

@ -41,11 +41,11 @@ public:
HateList(); HateList();
~HateList(); ~HateList();
Mob *GetClosestEntOnHateList(Mob *hater); Mob *GetClosestEntOnHateList(Mob *hater, bool skip_mezzed = false);
Mob *GetDamageTopOnHateList(Mob *hater); Mob *GetDamageTopOnHateList(Mob *hater); // didn't add 'skip_mezzed' due to calls being in ::Death()
Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr); Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr, bool skip_mezzed = false);
Mob *GetRandomEntOnHateList(); Mob *GetRandomEntOnHateList(bool skip_mezzed = false);
Mob *GetEntWithMostHateOnList(); Mob *GetEntWithMostHateOnList(bool skip_mezzed = false);
Mob *GetEscapingEntOnHateList(); // returns first eligble entity Mob *GetEscapingEntOnHateList(); // returns first eligble entity
Mob *GetEscapingEntOnHateList(Mob *center, float range = 0.0f, bool first = false); Mob *GetEscapingEntOnHateList(Mob *center, float range = 0.0f, bool first = false);

View File

@ -1385,59 +1385,87 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) {
return; return;
} }
uint32 copper = 0; QuestReward_Struct quest_reward;
uint32 silver = 0; quest_reward.mob_id = 0;
uint32 gold = 0; quest_reward.target_id = self->GetID();
uint32 platinum = 0; quest_reward.copper = 0;
uint32 itemid = 0; quest_reward.silver = 0;
uint32 exp = 0; quest_reward.gold = 0;
quest_reward.platinum = 0;
quest_reward.exp_reward = 0;
quest_reward.faction = 0;
quest_reward.faction_mod = 0;
bool faction = false; bool faction = false;
std::fill(std::begin(quest_reward.item_id), std::end(quest_reward.item_id), -1);
auto cur = reward["copper"]; auto cur = reward["copper"];
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
copper = luabind::object_cast<uint32>(cur); quest_reward.copper = luabind::object_cast<uint32>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
cur = reward["silver"]; cur = reward["silver"];
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
silver = luabind::object_cast<uint32>(cur); quest_reward.silver = luabind::object_cast<uint32>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
cur = reward["gold"]; cur = reward["gold"];
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
gold = luabind::object_cast<uint32>(cur); quest_reward.gold = luabind::object_cast<uint32>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
cur = reward["platinum"]; cur = reward["platinum"];
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
platinum = luabind::object_cast<uint32>(cur); quest_reward.platinum = luabind::object_cast<uint32>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
cur = reward["itemid"]; cur = reward["itemid"];
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
itemid = luabind::object_cast<uint32>(cur); quest_reward.item_id[0] = luabind::object_cast<uint32>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
}
}
// if you define both an itemid and items table, the itemid is thrown away
// should we error?
cur = reward["items"];
if (luabind::type(cur) == LUA_TTABLE) {
try {
// assume they defined a compatible table
for (int i = 1; i <= QUESTREWARD_COUNT; ++i) {
auto item = cur[i];
int cur_value = -1;
if (luabind::type(item) != LUA_TNIL) {
try {
cur_value = luabind::object_cast<uint32>(item);
} catch (luabind::cast_failed &) {
}
} else {
break;
}
quest_reward.item_id[i - 1] = cur_value;
}
} catch (luabind::cast_failed &) {
} }
} }
cur = reward["exp"]; cur = reward["exp"];
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
exp = luabind::object_cast<uint32>(cur); quest_reward.exp_reward = luabind::object_cast<uint32>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
@ -1445,11 +1473,11 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) {
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
faction = luabind::object_cast<bool>(cur); faction = luabind::object_cast<bool>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, faction); self->QuestReward(target, quest_reward, faction);
} }
bool Lua_Client::IsDead() { bool Lua_Client::IsDead() {

View File

@ -549,6 +549,14 @@ void lua_summon_all_player_corpses(uint32 char_id, float x, float y, float z, fl
quest_manager.summonallplayercorpses(char_id, glm::vec4(x, y, z, h)); quest_manager.summonallplayercorpses(char_id, glm::vec4(x, y, z, h));
} }
int lua_get_player_corpse_count(uint32 char_id) {
return database.CountCharacterCorpses(char_id);
}
int lua_get_player_corpse_count_by_zone_id(uint32 char_id, uint32 zone_id) {
return database.CountCharacterCorpsesByZoneID(char_id, zone_id);
}
int lua_get_player_buried_corpse_count(uint32 char_id) { int lua_get_player_buried_corpse_count(uint32 char_id) {
return quest_manager.getplayerburiedcorpsecount(char_id); return quest_manager.getplayerburiedcorpsecount(char_id);
} }
@ -571,7 +579,7 @@ void lua_task_selector(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
cur_value = luabind::object_cast<int>(cur); cur_value = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} else { } else {
count = i - 1; count = i - 1;
@ -601,7 +609,7 @@ void lua_enable_task(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
cur_value = luabind::object_cast<int>(cur); cur_value = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} else { } else {
count = i - 1; count = i - 1;
@ -628,7 +636,7 @@ void lua_disable_task(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
cur_value = luabind::object_cast<int>(cur); cur_value = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} else { } else {
count = i - 1; count = i - 1;
@ -1156,7 +1164,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
spawn2_id = luabind::object_cast<uint32>(cur); spawn2_id = luabind::object_cast<uint32>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
return; return;
} }
} else { } else {
@ -1167,7 +1175,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
spawngroup_id = luabind::object_cast<uint32>(cur); spawngroup_id = luabind::object_cast<uint32>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
return; return;
} }
} else { } else {
@ -1178,7 +1186,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
x = luabind::object_cast<float>(cur); x = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
return; return;
} }
} else { } else {
@ -1189,7 +1197,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
y = luabind::object_cast<float>(cur); y = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
return; return;
} }
} else { } else {
@ -1200,7 +1208,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
z = luabind::object_cast<float>(cur); z = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
return; return;
} }
} else { } else {
@ -1211,7 +1219,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
heading = luabind::object_cast<float>(cur); heading = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
return; return;
} }
} else { } else {
@ -1222,7 +1230,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
respawn = luabind::object_cast<uint32>(cur); respawn = luabind::object_cast<uint32>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
return; return;
} }
} else { } else {
@ -1233,7 +1241,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
variance = luabind::object_cast<uint32>(cur); variance = luabind::object_cast<uint32>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
return; return;
} }
} else { } else {
@ -1244,7 +1252,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
timeleft = luabind::object_cast<uint32>(cur); timeleft = luabind::object_cast<uint32>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1252,7 +1260,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
grid = luabind::object_cast<uint32>(cur); grid = luabind::object_cast<uint32>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1260,7 +1268,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
condition_id = luabind::object_cast<int>(cur); condition_id = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1268,7 +1276,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
condition_min_value = luabind::object_cast<int>(cur); condition_min_value = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1276,7 +1284,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
enabled = luabind::object_cast<bool>(cur); enabled = luabind::object_cast<bool>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1284,7 +1292,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
animation = luabind::object_cast<int>(cur); animation = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1399,7 +1407,7 @@ void lua_update_zone_header(std::string type, std::string value) {
try { \ try { \
npc_type->name = luabind::object_cast<c_type>(cur); \ npc_type->name = luabind::object_cast<c_type>(cur); \
} \ } \
catch(luabind::cast_failed) { \ catch(luabind::cast_failed &) { \
npc_type->size = default_value; \ npc_type->size = default_value; \
} \ } \
} \ } \
@ -1415,7 +1423,7 @@ void lua_update_zone_header(std::string type, std::string value) {
std::string tmp = luabind::object_cast<std::string>(cur); \ std::string tmp = luabind::object_cast<std::string>(cur); \
strncpy(npc_type->name, tmp.c_str(), str_length); \ strncpy(npc_type->name, tmp.c_str(), str_length); \
} \ } \
catch(luabind::cast_failed) { \ catch(luabind::cast_failed &) { \
strncpy(npc_type->name, default_value, str_length); \ strncpy(npc_type->name, default_value, str_length); \
} \ } \
} \ } \
@ -1663,6 +1671,8 @@ luabind::scope lua_register_general() {
luabind::def("toggle_spawn_event", &lua_toggle_spawn_event), luabind::def("toggle_spawn_event", &lua_toggle_spawn_event),
luabind::def("summon_buried_player_corpse", &lua_summon_buried_player_corpse), luabind::def("summon_buried_player_corpse", &lua_summon_buried_player_corpse),
luabind::def("summon_all_player_corpses", &lua_summon_all_player_corpses), luabind::def("summon_all_player_corpses", &lua_summon_all_player_corpses),
luabind::def("get_player_corpse_count", &lua_get_player_corpse_count),
luabind::def("get_player_corpse_count_by_zone_id", &lua_get_player_corpse_count_by_zone_id),
luabind::def("get_player_buried_corpse_count", &lua_get_player_buried_corpse_count), luabind::def("get_player_buried_corpse_count", &lua_get_player_buried_corpse_count),
luabind::def("bury_player_corpse", &lua_bury_player_corpse), luabind::def("bury_player_corpse", &lua_bury_player_corpse),
luabind::def("task_selector", &lua_task_selector), luabind::def("task_selector", &lua_task_selector),

View File

@ -113,7 +113,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
options.armor_pen_flat = luabind::object_cast<int>(cur); options.armor_pen_flat = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -121,7 +121,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
options.crit_flat = luabind::object_cast<float>(cur); options.crit_flat = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -129,7 +129,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
options.damage_flat = luabind::object_cast<int>(cur); options.damage_flat = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -137,7 +137,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
options.hate_flat = luabind::object_cast<int>(cur); options.hate_flat = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -145,7 +145,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
options.armor_pen_percent = luabind::object_cast<float>(cur); options.armor_pen_percent = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -153,7 +153,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
options.crit_percent = luabind::object_cast<float>(cur); options.crit_percent = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -161,7 +161,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
options.damage_percent = luabind::object_cast<float>(cur); options.damage_percent = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -169,7 +169,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
options.hate_percent = luabind::object_cast<float>(cur); options.hate_percent = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
} }
@ -785,7 +785,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
journal_opts.speak_mode = static_cast<Journal::SpeakMode>(luabind::object_cast<int>(cur)); journal_opts.speak_mode = static_cast<Journal::SpeakMode>(luabind::object_cast<int>(cur));
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
@ -793,7 +793,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
journal_opts.journal_mode = static_cast<Journal::Mode>(luabind::object_cast<int>(cur)); journal_opts.journal_mode = static_cast<Journal::Mode>(luabind::object_cast<int>(cur));
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
@ -801,7 +801,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
journal_opts.language = luabind::object_cast<int>(cur); journal_opts.language = luabind::object_cast<int>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
@ -809,7 +809,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj
if (luabind::type(cur) != LUA_TNIL) { if (luabind::type(cur) != LUA_TNIL) {
try { try {
journal_opts.message_type = luabind::object_cast<int>(cur); journal_opts.message_type = luabind::object_cast<int>(cur);
} catch (luabind::cast_failed) { } catch (luabind::cast_failed &) {
} }
} }
} }
@ -1568,7 +1568,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
race = luabind::object_cast<int>(cur); race = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1576,7 +1576,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
gender = luabind::object_cast<int>(cur); gender = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1584,7 +1584,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
texture = luabind::object_cast<int>(cur); texture = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1592,7 +1592,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
helmtexture = luabind::object_cast<int>(cur); helmtexture = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1600,7 +1600,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
haircolor = luabind::object_cast<int>(cur); haircolor = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1608,7 +1608,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
beardcolor = luabind::object_cast<int>(cur); beardcolor = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1616,7 +1616,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
eyecolor1 = luabind::object_cast<int>(cur); eyecolor1 = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1624,7 +1624,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
eyecolor2 = luabind::object_cast<int>(cur); eyecolor2 = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1632,7 +1632,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
hairstyle = luabind::object_cast<int>(cur); hairstyle = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1640,7 +1640,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
luclinface = luabind::object_cast<int>(cur); luclinface = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1648,7 +1648,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
beard = luabind::object_cast<int>(cur); beard = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1656,7 +1656,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
aa_title = luabind::object_cast<int>(cur); aa_title = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1664,7 +1664,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
drakkin_heritage = luabind::object_cast<int>(cur); drakkin_heritage = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1672,7 +1672,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
drakkin_tattoo = luabind::object_cast<int>(cur); drakkin_tattoo = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1680,7 +1680,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
drakkin_details = luabind::object_cast<int>(cur); drakkin_details = luabind::object_cast<int>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }
@ -1688,7 +1688,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
if(luabind::type(cur) != LUA_TNIL) { if(luabind::type(cur) != LUA_TNIL) {
try { try {
size = luabind::object_cast<float>(cur); size = luabind::object_cast<float>(cur);
} catch(luabind::cast_failed) { } catch(luabind::cast_failed &) {
} }
} }

View File

@ -123,7 +123,9 @@ const char *LuaEvents[_LargestEventID] = {
"event_tick", "event_tick",
"event_spawn_zone", "event_spawn_zone",
"event_death_zone", "event_death_zone",
"event_use_skill" "event_use_skill",
"event_combine_validate",
"event_bot_command"
}; };
extern Zone *zone; extern Zone *zone;
@ -206,6 +208,8 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[EVENT_RESPAWN] = handle_player_respawn; PlayerArgumentDispatch[EVENT_RESPAWN] = handle_player_respawn;
PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet; PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet;
PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill; 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] = handle_item_click;
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;

View File

@ -514,6 +514,50 @@ void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client
lua_setfield(L, -2, "skill_level"); lua_setfield(L, -2, "skill_level");
} }
void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
std::vector<EQEmu::Any>* extra_pointers) {
Seperator sep(data.c_str());
lua_pushinteger(L, extra_data);
lua_setfield(L, -2, "recipe_id");
lua_pushstring(L, sep.arg[0]);
lua_setfield(L, -2, "validate_type");
int zone_id = -1;
int tradeskill_id = -1;
if (strcmp(sep.arg[0], "check_zone") == 0) {
zone_id = std::stoi(sep.arg[1]);
}
else if (strcmp(sep.arg[0], "check_tradeskill") == 0) {
tradeskill_id = std::stoi(sep.arg[1]);
}
lua_pushinteger(L, zone_id);
lua_setfield(L, -2, "zone_id");
lua_pushinteger(L, tradeskill_id);
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 //Item
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, 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) { std::vector<EQEmu::Any> *extra_pointers) {

View File

@ -97,6 +97,10 @@ void handle_player_null(QuestInterface *parse, lua_State* L, Client* client, std
std::vector<EQEmu::Any> *extra_pointers); std::vector<EQEmu::Any> *extra_pointers);
void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers); 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 //Item
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,

Some files were not shown because too many files have changed in this diff Show More