mirror of
https://github.com/EQEmu/Server.git
synced 2026-02-19 07:32:25 +00:00
commit
c419df52ff
4
.gitignore
vendored
4
.gitignore
vendored
@ -33,4 +33,6 @@ perl/
|
||||
*cbp
|
||||
|
||||
submodules/*
|
||||
cmake-build-debug/
|
||||
cmake-build-debug/
|
||||
|
||||
.nfs.*
|
||||
30
.travis.yml
30
.travis.yml
@ -1,26 +1,18 @@
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
dist: trusty
|
||||
dist: bionic
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo apt-get update -qq
|
||||
- mkdir $HOME/usr
|
||||
- export PATH="$HOME/usr/bin:$PATH"
|
||||
- wget https://cmake.org/files/v3.11/cmake-3.11.2-Linux-x86_64.sh
|
||||
- chmod +x cmake-3.11.2-Linux-x86_64.sh
|
||||
- ./cmake-3.11.2-Linux-x86_64.sh --prefix=$HOME/usr --exclude-subdir --skip-license
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libmysqlclient-dev
|
||||
- libperl-dev
|
||||
- libboost-dev
|
||||
- liblua5.1-0-dev
|
||||
- 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:
|
||||
- cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LOGIN=ON
|
||||
- make -j2
|
||||
|
||||
@ -122,6 +122,7 @@ SET(common_headers
|
||||
cli/terminal_color.hpp
|
||||
data_verification.h
|
||||
database.h
|
||||
database_schema.h
|
||||
dbcore.h
|
||||
deity.h
|
||||
emu_constants.h
|
||||
|
||||
@ -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)
|
||||
{
|
||||
switch (class_id) {
|
||||
|
||||
@ -135,6 +135,7 @@ uint8 GetClassIDFromPlayerClassBit(uint32 player_class_bit);
|
||||
bool IsFighterClass(uint8 class_id);
|
||||
bool IsSpellFighterClass(uint8 class_id);
|
||||
bool IsNonSpellFighterClass(uint8 class_id);
|
||||
bool IsHybridClass(uint8 class_id);
|
||||
bool IsCasterClass(uint8 class_id);
|
||||
bool IsINTCasterClass(uint8 class_id);
|
||||
bool IsWISCasterClass(uint8 class_id);
|
||||
|
||||
@ -38,10 +38,6 @@ namespace EQEmuCommand {
|
||||
void DisplayDebug(argh::parser &cmd)
|
||||
{
|
||||
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";
|
||||
for (auto &pos_arg : cmd.pos_args())
|
||||
std::cout << '\t' << pos_arg << std::endl;
|
||||
@ -73,29 +69,37 @@ namespace EQEmuCommand {
|
||||
{
|
||||
bool arguments_filled = true;
|
||||
|
||||
int index = 2;
|
||||
for (auto &arg : arguments) {
|
||||
if (cmd(arg).str().empty()) {
|
||||
if (cmd(arg).str().empty() && cmd(index).str().empty()) {
|
||||
arguments_filled = false;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (!arguments_filled || argc == 2) {
|
||||
std::string arguments_string;
|
||||
for (auto &arg : arguments) {
|
||||
arguments_string += " " + arg + "=*\n";
|
||||
arguments_string += " " + arg;
|
||||
}
|
||||
|
||||
std::string options_string;
|
||||
for (auto &opt : options) {
|
||||
for (auto &opt : options) {
|
||||
options_string += " " + opt + "\n";
|
||||
}
|
||||
|
||||
std::cout << fmt::format(
|
||||
"Command\n\n{0} \n\nArgs\n{1}\nOptions\n{2}",
|
||||
argv[1],
|
||||
arguments_string,
|
||||
options_string
|
||||
) << std::endl;
|
||||
std::stringstream command_string;
|
||||
|
||||
command_string <<
|
||||
termcolor::colorize <<
|
||||
termcolor::yellow <<
|
||||
"\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);
|
||||
}
|
||||
@ -123,10 +127,6 @@ namespace EQEmuCommand {
|
||||
bool ran_command = false;
|
||||
for (auto &it: in_function_map) {
|
||||
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);
|
||||
ran_command = true;
|
||||
}
|
||||
@ -182,12 +182,13 @@ namespace EQEmuCommand {
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
else if (!ran_command) {
|
||||
std::cerr << "Unknown command [" << argv[1] << "] ! Try --help" << std::endl;
|
||||
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
if (ran_command) {
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@
|
||||
#include "eq_packet_structs.h"
|
||||
#include "extprofile.h"
|
||||
#include "string_util.h"
|
||||
#include "database_schema.h"
|
||||
|
||||
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.
|
||||
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);
|
||||
|
||||
@ -140,7 +141,7 @@ bool Database::CheckBannedIPs(const char* loginIP)
|
||||
}
|
||||
|
||||
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);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
@ -293,6 +294,37 @@ bool Database::SetAccountStatus(const char* name, int16 status) {
|
||||
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 */
|
||||
bool Database::ReserveName(uint32 account_id, char* 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);
|
||||
results = QueryDatabase(query);
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
Delete the character with the name "name"
|
||||
returns false on failure, true otherwise
|
||||
*/
|
||||
bool Database::DeleteCharacter(char *name) {
|
||||
uint32 charid = 0;
|
||||
if(!name || !strlen(name)) {
|
||||
/**
|
||||
* @param character_name
|
||||
* @return
|
||||
*/
|
||||
bool Database::DeleteCharacter(char *character_name) {
|
||||
uint32 character_id = 0;
|
||||
if(!character_name || !strlen(character_name)) {
|
||||
LogInfo("DeleteCharacter: request to delete without a name (empty char slot)");
|
||||
return false;
|
||||
}
|
||||
LogInfo("Database::DeleteCharacter name : [{}]", name);
|
||||
|
||||
/* Get id from character_data before deleting record so we can clean up the rest of the tables */
|
||||
std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", name);
|
||||
auto results = QueryDatabase(query);
|
||||
for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); }
|
||||
if (charid <= 0){
|
||||
LogError("Database::DeleteCharacter :: Character ({}) not found, stopping delete...", name);
|
||||
std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", character_name);
|
||||
auto results = QueryDatabase(query);
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
character_id = atoi(row[0]);
|
||||
}
|
||||
|
||||
if (character_id <= 0) {
|
||||
LogError("DeleteCharacter | Invalid Character ID [{}]", character_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); QueryDatabase(query);
|
||||
std::string delete_type = "hard-deleted";
|
||||
if (RuleB(Character, SoftDeletes)) {
|
||||
delete_type = "soft-deleted";
|
||||
std::string query = fmt::format(
|
||||
SQL(
|
||||
UPDATE
|
||||
character_data
|
||||
SET
|
||||
name = SUBSTRING(CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), 1, 64),
|
||||
deleted_at = NOW()
|
||||
WHERE
|
||||
id = '{}'
|
||||
),
|
||||
character_id
|
||||
);
|
||||
|
||||
QueryDatabase(query);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LogInfo("DeleteCharacter | Character [{}] ({}) is being [{}]", character_name, character_id, delete_type);
|
||||
|
||||
for (const auto& iter : DatabaseSchema::GetCharacterTables()) {
|
||||
std::string table_name = iter.first;
|
||||
std::string character_id_column_name = iter.second;
|
||||
|
||||
QueryDatabase(fmt::format("DELETE FROM {} WHERE {} = {}", table_name, character_id_column_name, character_id));
|
||||
}
|
||||
|
||||
#ifdef BOTS
|
||||
query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", charid); // note: only use of GetMobTypeById()
|
||||
#else
|
||||
query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", charid);
|
||||
#endif
|
||||
query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", character_id); // note: only use of GetMobTypeById()
|
||||
QueryDatabase(query);
|
||||
#endif
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -652,6 +695,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe
|
||||
pp->RestTimer // " RestTimer) "
|
||||
);
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
/* Save Bind Points */
|
||||
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), "
|
||||
|
||||
@ -107,7 +107,7 @@ public:
|
||||
|
||||
bool AddToNameFilter(const char* name);
|
||||
bool CreateCharacter(uint32 account_id, char* name, uint16 gender, uint16 race, uint16 class_, uint8 str, uint8 sta, uint8 cha, uint8 dex, uint8 int_, uint8 agi, uint8 wis, uint8 face);
|
||||
bool DeleteCharacter(char* name);
|
||||
bool DeleteCharacter(char* character_name);
|
||||
bool MoveCharacterToZone(const char* charname, const char* zonename);
|
||||
bool MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid);
|
||||
bool MoveCharacterToZone(uint32 iCharID, const char* iZonename);
|
||||
@ -120,7 +120,7 @@ public:
|
||||
|
||||
/* 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 CheckBannedIPs(const char* loginIP); //Check incoming connection against banned IP table.
|
||||
bool CheckGMIPs(const char* loginIP, uint32 account_id);
|
||||
@ -179,6 +179,7 @@ public:
|
||||
bool DeleteAccount(const char *name, const char* loginserver);
|
||||
bool GetLiveChar(uint32 account_id, char* cname);
|
||||
bool SetAccountStatus(const char* name, int16 status);
|
||||
bool SetAccountStatus(const std::string& account_name, int16 status);
|
||||
bool SetLocalPassword(uint32 accid, const char* password);
|
||||
bool UpdateLiveChar(char* charname, uint32 account_id);
|
||||
|
||||
|
||||
342
common/database_schema.h
Normal file
342
common/database_schema.h
Normal 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
|
||||
@ -115,14 +115,14 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
||||
auto errorBuffer = new char[MYSQL_ERRMSG_SIZE];
|
||||
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 (LogSys.log_settings[Logs::MySQLError].is_category_enabled == 1)
|
||||
Log(Logs::General, Logs::MySQLError, "%i: %s \n %s", mysql_errno(&mysql), mysql_error(&mysql), query);
|
||||
LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(&mysql), mysql_error(&mysql), query);
|
||||
}
|
||||
|
||||
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql), errorBuffer);
|
||||
|
||||
}
|
||||
|
||||
// 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 ((strncasecmp(query, "select", 6) == 0)) {
|
||||
LogF(
|
||||
Logs::General,
|
||||
Logs::MySQLQuery,
|
||||
"{0} ({1} row{2} returned) ({3}ms)",
|
||||
LogMySQLQuery(
|
||||
"{0} ({1} row{2} returned) ({3}s)",
|
||||
query,
|
||||
requestResult.RowCount(),
|
||||
requestResult.RowCount() == 1 ? "" : "s",
|
||||
@ -154,10 +152,8 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
||||
);
|
||||
}
|
||||
else {
|
||||
LogF(
|
||||
Logs::General,
|
||||
Logs::MySQLQuery,
|
||||
"{0} ({1} row{2} affected) ({3}ms)",
|
||||
LogMySQLQuery(
|
||||
"{0} ({1} row{2} affected) ({3}s)",
|
||||
query,
|
||||
requestResult.RowsAffected(),
|
||||
requestResult.RowsAffected() == 1 ? "" : "s",
|
||||
|
||||
@ -317,6 +317,15 @@ namespace EQEmu
|
||||
QuestControlGrid = -1
|
||||
};
|
||||
|
||||
namespace consent {
|
||||
enum eConsentType : uint8 {
|
||||
Normal = 0,
|
||||
Group,
|
||||
Raid,
|
||||
Guild
|
||||
};
|
||||
}; // namespace consent
|
||||
|
||||
} /*EQEmu*/
|
||||
|
||||
#endif /*COMMON_EMU_CONSTANTS_H*/
|
||||
|
||||
@ -35,6 +35,7 @@ static const uint32 MAX_MERC = 100;
|
||||
static const uint32 MAX_MERC_GRADES = 10;
|
||||
static const uint32 MAX_MERC_STANCES = 10;
|
||||
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
|
||||
/*028*/ uint32 gold; // Gives gold to the client
|
||||
/*032*/ uint32 platinum; // Gives platinum to the client
|
||||
/*036*/ uint32 item_id;
|
||||
/*040*/ uint32 unknown040;
|
||||
/*044*/ uint32 unknown044;
|
||||
/*048*/ uint32 unknown048;
|
||||
/*052*/ uint32 unknown052;
|
||||
/*056*/ uint32 unknown056;
|
||||
/*060*/ uint32 unknown060;
|
||||
/*064*/ uint32 unknown064;
|
||||
/*036*/ int32 item_id[QUESTREWARD_COUNT]; // -1 for nothing
|
||||
/*068*/
|
||||
};
|
||||
|
||||
|
||||
@ -165,7 +165,7 @@ class EQEmuConfig
|
||||
fconfig >> _config->_root;
|
||||
_config->parse_config();
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,6 +107,13 @@ namespace Logs {
|
||||
Emergency,
|
||||
Alert,
|
||||
Notice,
|
||||
AIScanClose,
|
||||
AIYellForHelp,
|
||||
AICastBeneficialClose,
|
||||
AoeCast,
|
||||
EntityManagement,
|
||||
Flee,
|
||||
Aura,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@ -172,7 +179,14 @@ namespace Logs {
|
||||
"Critical",
|
||||
"Emergency",
|
||||
"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; }
|
||||
|
||||
/**
|
||||
* Silence console logging
|
||||
*/
|
||||
void SilenceConsoleLogging();
|
||||
|
||||
/**
|
||||
* Turn on all console logging
|
||||
*/
|
||||
void EnableConsoleLogging();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
|
||||
@ -491,6 +491,76 @@
|
||||
OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} 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 {\
|
||||
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
@ -782,6 +852,48 @@
|
||||
#define LogStatusDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAIScanClose(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAIScanCloseDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAIYellForHelp(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAIYellForHelpDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAICastBeneficialClose(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAICastBeneficialCloseDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAoeCast(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAoeCastDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogEntityManagement(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogEntityManagementDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogFlee(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogFleeDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAura(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogAuraDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
|
||||
@ -21,6 +21,10 @@ namespace EQ
|
||||
uv_run(&m_loop, UV_RUN_NOWAIT);
|
||||
}
|
||||
|
||||
void Run() {
|
||||
uv_run(&m_loop, UV_RUN_DEFAULT);
|
||||
}
|
||||
|
||||
uv_loop_t* Handle() { return &m_loop; }
|
||||
|
||||
private:
|
||||
|
||||
@ -5277,7 +5277,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings)
|
||||
{
|
||||
//! [StreamWriterBuilderDefaults]
|
||||
(*settings)["commentStyle"] = "All";
|
||||
(*settings)["indentation"] = "\t";
|
||||
(*settings)["indentation"] = " ";
|
||||
(*settings)["enableYAMLCompatibility"] = false;
|
||||
(*settings)["dropNullPlaceholders"] = false;
|
||||
(*settings)["useSpecialFloats"] = false;
|
||||
|
||||
@ -32,7 +32,7 @@ EQ::JsonConfigFile EQ::JsonConfigFile::Load(
|
||||
try {
|
||||
ifs >> ret.m_root;
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ std::string EQ::JsonConfigFile::GetVariableString(
|
||||
return m_root[title][parameter].asString();
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ int EQ::JsonConfigFile::GetVariableInt(
|
||||
return m_root[title][parameter].asInt();
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ bool EQ::JsonConfigFile::GetVariableBool(
|
||||
return m_root[title][parameter].asBool();
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ double EQ::JsonConfigFile::GetVariableDouble(
|
||||
return m_root[title][parameter].asDouble();
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
|
||||
@ -278,12 +278,6 @@ void LinkedListIterator<TYPE>::Replace(const TYPE& new_data)
|
||||
template<class TYPE>
|
||||
void LinkedListIterator<TYPE>::Reset()
|
||||
{
|
||||
if (!(&list))
|
||||
{
|
||||
current_element=0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir == FORWARD)
|
||||
{
|
||||
current_element = list.first;
|
||||
|
||||
@ -19,9 +19,6 @@
|
||||
#include "types.h"
|
||||
#include <cstring>
|
||||
|
||||
#define ENC(c) (((c) & 0x3f) + ' ')
|
||||
#define DEC(c) (((c) - ' ') & 0x3f)
|
||||
|
||||
std::map<int,std::string> DBFieldNames;
|
||||
|
||||
#ifndef WIN32
|
||||
@ -333,64 +330,6 @@ void LoadItemDBFieldNames() {
|
||||
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)
|
||||
{
|
||||
unsigned long i,j;
|
||||
|
||||
@ -17,13 +17,6 @@ int Tokenize(std::string s, std::map<int,std::string> & tokens, char delim='|');
|
||||
|
||||
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
|
||||
int print_stacktrace();
|
||||
#endif
|
||||
|
||||
@ -116,17 +116,21 @@ bool EQ::Net::ConsoleServerConnection::SendChannelMessage(const ServerChannelMes
|
||||
}
|
||||
|
||||
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));
|
||||
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));
|
||||
break;
|
||||
} else { // I think we want default action in this case?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -399,7 +399,7 @@ void EQ::Net::DaybreakConnection::Process()
|
||||
|
||||
ProcessQueue();
|
||||
}
|
||||
catch (std::exception ex) {
|
||||
catch (std::exception &ex) {
|
||||
if (m_owner->m_on_error_message) {
|
||||
m_owner->m_on_error_message(fmt::format("Error processing connection: {0}", ex.what()));
|
||||
}
|
||||
|
||||
@ -89,9 +89,9 @@ namespace EQ {
|
||||
public:
|
||||
StaticPacket(void *data, size_t size) { m_data = data; m_data_length = size; m_max_data_length = size; }
|
||||
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(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 void *Data() { return m_data; }
|
||||
@ -112,7 +112,7 @@ namespace EQ {
|
||||
public:
|
||||
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& operator=(const DynamicPacket &o) { m_data = o.m_data; return *this; }
|
||||
|
||||
@ -127,4 +127,4 @@ namespace EQ {
|
||||
std::vector<char> m_data;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ EQ::Net::WebsocketServer::WebsocketServer(const std::string &addr, int port)
|
||||
auto &connection = iter->second;
|
||||
connection->GetWebsocketConnection()->ping("keepalive");
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
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;
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
throw WebsocketException("Unable to process login request");
|
||||
}
|
||||
}
|
||||
@ -212,7 +212,7 @@ Json::Value EQ::Net::WebsocketServer::Subscribe(WebsocketServerConnection *conne
|
||||
catch (WebsocketException &ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
throw WebsocketException("Unable to process unsubscribe request");
|
||||
}
|
||||
}
|
||||
@ -234,7 +234,7 @@ Json::Value EQ::Net::WebsocketServer::Unsubscribe(WebsocketServerConnection *con
|
||||
catch (WebsocketException &ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
throw WebsocketException("Unable to process unsubscribe request");
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
#include <sstream>
|
||||
#include <numeric>
|
||||
#include <cassert>
|
||||
#include <cinttypes>
|
||||
|
||||
|
||||
namespace RoF2
|
||||
@ -3548,7 +3549,7 @@ namespace RoF2
|
||||
{
|
||||
eq->items[i].Unknown18 = 0;
|
||||
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];
|
||||
}
|
||||
else {
|
||||
|
||||
@ -190,10 +190,6 @@ bool PersistentTimer::Clear(Database *db) {
|
||||
|
||||
/* This function checks if the timer triggered */
|
||||
bool PersistentTimer::Expired(Database *db, bool iReset) {
|
||||
if (this == nullptr) {
|
||||
LogError("Null timer during ->Check()!?\n");
|
||||
return(true);
|
||||
}
|
||||
uint32 current_time = get_current_time();
|
||||
if (current_time-start_time >= timer_time) {
|
||||
if (enabled && iReset) {
|
||||
|
||||
@ -157,6 +157,9 @@ RULE_BOOL(Character, OPClientUpdateVisualDebug, false, "Shows a pulse and forwar
|
||||
RULE_BOOL(Character, AllowCrossClassTrainers, false, "")
|
||||
RULE_BOOL(Character, PetsUseReagents, true, "Pets use reagent on spells")
|
||||
RULE_BOOL(Character, DismountWater, true, "Dismount horses when entering water")
|
||||
RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing")
|
||||
RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted")
|
||||
RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
@ -211,7 +214,7 @@ RULE_CATEGORY_END()
|
||||
RULE_CATEGORY(World)
|
||||
RULE_INT(World, ZoneAutobootTimeoutMS, 60000, "")
|
||||
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, EnableReturnHomeButton, true, "")
|
||||
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_REAL(Pathing, NavmeshStepSize, 100.0f, "")
|
||||
RULE_REAL(Pathing, ShortMovementUpdateRange, 130.0f, "")
|
||||
RULE_INT(Pathing, MaxNavmeshNodes, 4092, "Max navmesh nodes in a traversable path")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Watermap)
|
||||
@ -517,7 +521,7 @@ RULE_INT(NPC, NPCToNPCAggroTimerMin, 500, "")
|
||||
RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "")
|
||||
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_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_INT(NPC, NPCGateDistanceBind, 75, "Distance from bind before NPC will attempt to 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, ClientForceSpawnUpdateRange, 1000, "")
|
||||
RULE_INT(Range, CriticalDamage, 80, "")
|
||||
RULE_INT(Range, ClientNPCScan, 300, "")
|
||||
RULE_INT(Range, MobCloseScanDistance, 600, "")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
|
||||
#ifdef 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_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")
|
||||
@ -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, 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_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()
|
||||
#endif
|
||||
|
||||
|
||||
@ -262,9 +262,6 @@ public:
|
||||
}
|
||||
|
||||
ServerPacket* Copy() {
|
||||
if (this == 0) {
|
||||
return 0;
|
||||
}
|
||||
ServerPacket* ret = new ServerPacket(this->opcode, this->size);
|
||||
if (this->size)
|
||||
memcpy(ret->pBuffer, this->pBuffer, this->size);
|
||||
@ -869,10 +866,12 @@ struct SpawnPlayerCorpse_Struct {
|
||||
struct ServerOP_Consent_Struct {
|
||||
char grantname[64];
|
||||
char ownername[64];
|
||||
char zonename[32];
|
||||
uint8 permission;
|
||||
uint32 zone_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 {
|
||||
|
||||
@ -87,8 +87,9 @@
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
@ -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_CastOnCurer 386 // implemented - Casts a spell on the person curing
|
||||
#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_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited.
|
||||
#define SE_LimitManaMax 391 // implemented
|
||||
|
||||
@ -222,7 +222,7 @@ bool StringIsNumber(const std::string &s) {
|
||||
auto r = stod(s);
|
||||
return true;
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,12 @@
|
||||
#include <fmt/format.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <ctype.h>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
//std::string based
|
||||
@ -38,6 +44,38 @@ const std::string StringFormat(const char* format, ...);
|
||||
const std::string vStringFormat(const char* format, va_list args);
|
||||
std::string implode(std::string glue, std::vector<std::string> src);
|
||||
|
||||
/**
|
||||
* @param str
|
||||
* @param chars
|
||||
* @return
|
||||
*/
|
||||
inline std::string <rim(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>
|
||||
std::string implode(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
|
||||
{
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
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
|
||||
*/
|
||||
/**
|
||||
* 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_VERSION_H
|
||||
#define _EQEMU_VERSION_H
|
||||
@ -22,22 +24,24 @@
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define EQEMU_PROTOCOL_VERSION "0.3.10"
|
||||
|
||||
#define CURRENT_VERSION "1.1.3"
|
||||
|
||||
/*
|
||||
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_VERSION "2.0"
|
||||
|
||||
|
||||
#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
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026
|
||||
#else
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||
#endif
|
||||
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
#ifndef WIN32
|
||||
|
||||
@ -21,6 +21,10 @@
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @param mode
|
||||
* @return
|
||||
*/
|
||||
std::string GetEncryptionByModeId(uint32 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)
|
||||
{
|
||||
#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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
std::string eqcrypt_md5(const std::string &msg)
|
||||
{
|
||||
std::string ret;
|
||||
@ -159,6 +174,10 @@ std::string eqcrypt_md5(const std::string &msg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
std::string eqcrypt_sha1(const std::string &msg)
|
||||
{
|
||||
std::string ret;
|
||||
@ -193,6 +212,10 @@ std::string eqcrypt_sha1(const std::string &msg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
std::string eqcrypt_sha512(const std::string &msg)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
struct LoginServer
|
||||
{
|
||||
public:
|
||||
|
||||
LoginServer() : db(nullptr), server_manager(nullptr) {
|
||||
|
||||
}
|
||||
|
||||
@ -112,6 +112,9 @@ namespace LoginserverCommandHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
server.token_manager = new LoginserverWebserver::TokenManager;
|
||||
server.token_manager->LoadApiTokens();
|
||||
|
||||
for (auto &it : server.token_manager->loaded_api_tokens) {
|
||||
LogInfo(
|
||||
"token [{0}] can_write [{1}] can_read [{2}]",
|
||||
@ -133,8 +136,8 @@ namespace LoginserverCommandHandler {
|
||||
description = "Creates Local Loginserver Account";
|
||||
|
||||
std::vector<std::string> arguments = {
|
||||
"--username",
|
||||
"--password"
|
||||
"{username}",
|
||||
"{password}"
|
||||
};
|
||||
std::vector<std::string> options = {
|
||||
"--email=*"
|
||||
@ -147,8 +150,8 @@ namespace LoginserverCommandHandler {
|
||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||
|
||||
AccountManagement::CreateLoginServerAccount(
|
||||
cmd("--username").str(),
|
||||
cmd("--password").str(),
|
||||
cmd(2).str(),
|
||||
cmd(3).str(),
|
||||
cmd("--email").str()
|
||||
);
|
||||
}
|
||||
@ -164,9 +167,9 @@ namespace LoginserverCommandHandler {
|
||||
description = "Creates Loginserver World Administrator Account";
|
||||
|
||||
std::vector<std::string> arguments = {
|
||||
"--username",
|
||||
"--password",
|
||||
"--email"
|
||||
"{username}",
|
||||
"{password}",
|
||||
"{email}"
|
||||
};
|
||||
std::vector<std::string> options = {};
|
||||
|
||||
@ -177,9 +180,9 @@ namespace LoginserverCommandHandler {
|
||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||
|
||||
AccountManagement::CreateLoginserverWorldAdminAccount(
|
||||
cmd("--username").str(),
|
||||
cmd("--password").str(),
|
||||
cmd("--email").str()
|
||||
cmd(2).str(),
|
||||
cmd(3).str(),
|
||||
cmd(4).str()
|
||||
);
|
||||
}
|
||||
|
||||
@ -194,8 +197,8 @@ namespace LoginserverCommandHandler {
|
||||
description = "Check user login credentials";
|
||||
|
||||
std::vector<std::string> arguments = {
|
||||
"--username",
|
||||
"--password"
|
||||
"{username}",
|
||||
"{password}"
|
||||
};
|
||||
std::vector<std::string> options = {};
|
||||
|
||||
@ -206,11 +209,11 @@ namespace LoginserverCommandHandler {
|
||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||
|
||||
auto res = AccountManagement::CheckLoginserverUserCredentials(
|
||||
cmd("--username").str(),
|
||||
cmd("--password").str()
|
||||
cmd(2).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";
|
||||
|
||||
std::vector<std::string> arguments = {
|
||||
"--username",
|
||||
"--password"
|
||||
"{username}",
|
||||
"{password}"
|
||||
};
|
||||
std::vector<std::string> options = {};
|
||||
|
||||
@ -236,8 +239,8 @@ namespace LoginserverCommandHandler {
|
||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||
|
||||
AccountManagement::UpdateLoginserverUserCredentials(
|
||||
cmd("--username").str(),
|
||||
cmd("--password").str()
|
||||
cmd(2).str(),
|
||||
cmd(3).str()
|
||||
);
|
||||
}
|
||||
|
||||
@ -252,8 +255,8 @@ namespace LoginserverCommandHandler {
|
||||
description = "Check user external login credentials";
|
||||
|
||||
std::vector<std::string> arguments = {
|
||||
"--username",
|
||||
"--password"
|
||||
"{username}",
|
||||
"{password}"
|
||||
};
|
||||
std::vector<std::string> options = {};
|
||||
|
||||
@ -264,8 +267,8 @@ namespace LoginserverCommandHandler {
|
||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||
|
||||
auto res = AccountManagement::CheckExternalLoginserverUserCredentials(
|
||||
cmd("--username").str(),
|
||||
cmd("--password").str()
|
||||
cmd(2).str(),
|
||||
cmd(3).str()
|
||||
);
|
||||
|
||||
LogInfo("Credentials were {0}", res ? "accepted" : "not accepted");
|
||||
@ -282,8 +285,8 @@ namespace LoginserverCommandHandler {
|
||||
description = "Update world admin account password";
|
||||
|
||||
std::vector<std::string> arguments = {
|
||||
"--username",
|
||||
"--password"
|
||||
"{username}",
|
||||
"{password}"
|
||||
};
|
||||
std::vector<std::string> options = {};
|
||||
|
||||
@ -294,8 +297,8 @@ namespace LoginserverCommandHandler {
|
||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||
|
||||
AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName(
|
||||
cmd("--username").str(),
|
||||
cmd("--password").str()
|
||||
cmd(2).str(),
|
||||
cmd(3).str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,22 +43,28 @@ void CatchSignal(int sig_num)
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
void LoadDatabaseConnection()
|
||||
{
|
||||
RegisterExecutablePlatform(ExePlatformLogin);
|
||||
set_exception_handler();
|
||||
LogInfo("MySQL Database Init");
|
||||
|
||||
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");
|
||||
LogInfo("Config System Init");
|
||||
|
||||
|
||||
/**
|
||||
* options: logging
|
||||
* Logging
|
||||
*/
|
||||
server.options.Trace(server.config.GetVariableBool("logging", "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));
|
||||
|
||||
/**
|
||||
* options: worldservers
|
||||
* Worldservers
|
||||
*/
|
||||
server.options.RejectDuplicateServers(
|
||||
server.config.GetVariableBool(
|
||||
@ -77,7 +83,7 @@ int main(int argc, char **argv)
|
||||
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.AutoLinkAccounts(server.config.GetVariableBool("account", "auto_link_accounts", false));
|
||||
@ -92,6 +98,9 @@ int main(int argc, char **argv)
|
||||
);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Default Loginserver Name (Don't change)
|
||||
*/
|
||||
server.options.DefaultLoginServerName(
|
||||
server.config.GetVariableString(
|
||||
"general",
|
||||
@ -100,6 +109,10 @@ int main(int argc, char **argv)
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Security
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_SECURITY
|
||||
server.options.EncryptionMode(server.config.GetVariableInt("security", "mode", 13));
|
||||
#else
|
||||
@ -115,19 +128,41 @@ int main(int argc, char **argv)
|
||||
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
|
||||
*/
|
||||
LogInfo("MySQL Database 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")
|
||||
);
|
||||
LoadDatabaseConnection();
|
||||
|
||||
if (argc == 1) {
|
||||
server.db->LoadLogSettings(LogSys.log_settings);
|
||||
@ -195,14 +230,6 @@ int main(int argc, char **argv)
|
||||
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] IsWorldTraceOn [{0}]", server.options.IsWorldTraceOn());
|
||||
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
36
utils/scripts/eqemu_server.pl
Normal file → Executable file
@ -516,13 +516,20 @@ sub check_for_input {
|
||||
}
|
||||
|
||||
sub check_for_world_bootup_database_update {
|
||||
if ($OS eq "Windows") {
|
||||
@db_version = split(': ', `world db_version`);
|
||||
}
|
||||
if ($OS eq "Linux") {
|
||||
@db_version = split(': ', `./world db_version`);
|
||||
|
||||
my $world_path = "world";
|
||||
if (-e "bin/world") {
|
||||
$world_path = "bin/world";
|
||||
}
|
||||
|
||||
#::: 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]);
|
||||
$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 {
|
||||
if ($OS eq "Windows") {
|
||||
fetch_latest_windows_binaries_bots();
|
||||
fetch_latest_windows_appveyor_bots();
|
||||
}
|
||||
if ($OS eq "Linux") {
|
||||
build_linux_source("bots");
|
||||
@ -814,7 +821,7 @@ sub setup_bots {
|
||||
bots_db_management();
|
||||
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 {
|
||||
@ -1686,7 +1693,7 @@ sub fetch_server_dlls {
|
||||
|
||||
sub fetch_peq_db_full {
|
||||
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";
|
||||
unzip('updates_staged/peq_beta.zip', 'updates_staged/peq_db/');
|
||||
my $start_dir = "updates_staged/peq_db";
|
||||
@ -1821,6 +1828,8 @@ sub quest_files_fetch {
|
||||
if ($fc == 0) {
|
||||
print "[Update] No Quest Updates found... \n\n";
|
||||
}
|
||||
|
||||
rmtree("updates_staged/");
|
||||
}
|
||||
|
||||
sub lua_modules_fetch {
|
||||
@ -2207,11 +2216,18 @@ sub get_bots_db_version {
|
||||
}
|
||||
|
||||
sub bots_db_management {
|
||||
|
||||
my $world_path = "world";
|
||||
if (-e "bin/world") {
|
||||
$world_path = "bin/world";
|
||||
}
|
||||
|
||||
#::: Get Binary DB version
|
||||
if ($OS eq "Windows") {
|
||||
@db_version = split(': ', `world db_version`);
|
||||
@db_version = split(': ', `$world_path db_version`);
|
||||
}
|
||||
if ($OS eq "Linux") {
|
||||
@db_version = split(': ', `./world db_version`);
|
||||
@db_version = split(': ', `./$world_path db_version`);
|
||||
}
|
||||
|
||||
#::: Main Binary Database version
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,15 @@
|
||||
account
|
||||
account_ip
|
||||
account_flags
|
||||
account_rewards
|
||||
adventure_details
|
||||
adventure_stats
|
||||
buyer
|
||||
char_recipe_list
|
||||
character_auras
|
||||
character_activities
|
||||
character_alt_currency
|
||||
character_alternate_abilities
|
||||
character_auras
|
||||
character_bandolier
|
||||
character_bind
|
||||
character_buffs
|
||||
@ -20,15 +26,22 @@ character_leadership_abilities
|
||||
character_material
|
||||
character_memmed_spells
|
||||
character_pet_buffs
|
||||
character_pet_info
|
||||
character_pet_inventory
|
||||
character_potionbelt
|
||||
character_skills
|
||||
character_spells
|
||||
character_tasks
|
||||
character_tribute
|
||||
completed_tasks
|
||||
data_buckets
|
||||
faction_values
|
||||
friends
|
||||
guild_bank
|
||||
guild_members
|
||||
guild_ranks
|
||||
guild_relations
|
||||
guilds
|
||||
instance_list_player
|
||||
inventory
|
||||
inventory_snapshots
|
||||
@ -36,6 +49,9 @@ keyring
|
||||
mail
|
||||
player_titlesets
|
||||
quest_globals
|
||||
sharedbank
|
||||
timers
|
||||
titles
|
||||
zone_flags
|
||||
trader
|
||||
trader_audit
|
||||
zone_flags"
|
||||
@ -397,7 +397,11 @@
|
||||
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|
|
||||
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:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@ -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|
|
||||
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|
|
||||
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|
|
||||
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|
|
||||
|
||||
# Upgrade conditions:
|
||||
|
||||
12
utils/sql/git/optional/2020_01_26_soft_delete_retro.sql
Normal file
12
utils/sql/git/optional/2020_01_26_soft_delete_retro.sql
Normal 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-%';
|
||||
5
utils/sql/git/required/2019_12_24_banned_ips_update.sql
Normal file
5
utils/sql/git/required/2019_12_24_banned_ips_update.sql
Normal 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_`;
|
||||
@ -0,0 +1 @@
|
||||
ALTER TABLE `character_data` ADD COLUMN `deleted_at` datetime NULL DEFAULT NULL;
|
||||
@ -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;
|
||||
@ -0,0 +1 @@
|
||||
ALTER TABLE `character_corpses` ADD COLUMN `guild_consent_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `time_of_death`;
|
||||
@ -6,7 +6,7 @@ account_rewards
|
||||
adventure_details
|
||||
adventure_members
|
||||
adventure_stats
|
||||
Banned_IPs
|
||||
banned_ips
|
||||
bugs
|
||||
buyer
|
||||
char_recipe_list
|
||||
|
||||
@ -14,7 +14,7 @@ SET(world_sources
|
||||
lfplist.cpp
|
||||
login_server.cpp
|
||||
login_server_list.cpp
|
||||
net.cpp
|
||||
main.cpp
|
||||
queryserv.cpp
|
||||
ucs.cpp
|
||||
web_interface.cpp
|
||||
@ -22,6 +22,7 @@ SET(world_sources
|
||||
wguild_mgr.cpp
|
||||
world_config.cpp
|
||||
world_console_connection.cpp
|
||||
world_server_command_handler.cpp
|
||||
worlddb.cpp
|
||||
zonelist.cpp
|
||||
zoneserver.cpp
|
||||
@ -51,6 +52,7 @@ SET(world_headers
|
||||
world_config.h
|
||||
world_console_connection.h
|
||||
world_tcp_connection.h
|
||||
world_server_command_handler.h
|
||||
worlddb.h
|
||||
zonelist.h
|
||||
zoneserver.h
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,6 @@ public:
|
||||
void AddFinishedEvent(AdventureFinishEvent fe) { finished_list.push_back(fe); Save(); }
|
||||
bool PopFinishedEvent(const char *name, AdventureFinishEvent &fe);
|
||||
void Save();
|
||||
void Load();
|
||||
|
||||
Adventure **GetFinishedAdventures(const char *player, int &count);
|
||||
Adventure *GetActiveAdventure(const char *player);
|
||||
|
||||
@ -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 <iostream>
|
||||
@ -86,6 +89,7 @@ union semun {
|
||||
|
||||
#include "../common/net/servertalk_server.h"
|
||||
#include "../zone/data_bucket.h"
|
||||
#include "world_server_command_handler.h"
|
||||
|
||||
ClientList client_list;
|
||||
GroupLFPList LFPGroupList;
|
||||
@ -113,14 +117,30 @@ inline void UpdateWindowTitle(std::string new_title) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
RegisterExecutablePlatform(ExePlatformWorld);
|
||||
LogSys.LoadLogSettingsDefaults();
|
||||
set_exception_handler();
|
||||
void LoadDatabaseConnections()
|
||||
{
|
||||
LogInfo(
|
||||
"Connecting to MySQL [{}]@[{}]:[{}]",
|
||||
Config->DatabaseUsername.c_str(),
|
||||
Config->DatabaseHost.c_str(),
|
||||
Config->DatabasePort
|
||||
);
|
||||
|
||||
/**
|
||||
* Auto convert json config from xml
|
||||
*/
|
||||
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");
|
||||
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckForXMLConfigUpgrade()
|
||||
{
|
||||
if (!std::ifstream("eqemu_config.json") && std::ifstream("eqemu_config.xml")) {
|
||||
CheckForServerScript(true);
|
||||
if(system("perl eqemu_server.pl convert_xml"));
|
||||
@ -128,26 +148,105 @@ int main(int argc, char** argv) {
|
||||
else {
|
||||
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
|
||||
*/
|
||||
uint32 Database_Version = CURRENT_BINARY_DATABASE_VERSION;
|
||||
uint32 Bots_Database_Version = CURRENT_BINARY_BOTS_DATABASE_VERSION;
|
||||
uint32 database_version = CURRENT_BINARY_DATABASE_VERSION;
|
||||
uint32 bots_database_version = CURRENT_BINARY_BOTS_DATABASE_VERSION;
|
||||
if (argc >= 2) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Load server configuration
|
||||
LogInfo("Loading server configuration");
|
||||
if (!WorldConfig::LoadConfig()) {
|
||||
LogError("Loading server configuration failed");
|
||||
return 1;
|
||||
/**
|
||||
* Command handler
|
||||
*/
|
||||
if (argc > 1) {
|
||||
LogSys.SilenceConsoleLogging();
|
||||
|
||||
/**
|
||||
* Get Config
|
||||
*/
|
||||
WorldConfig::LoadConfig();
|
||||
Config = WorldConfig::get();
|
||||
|
||||
/**
|
||||
* Load database
|
||||
*/
|
||||
LoadDatabaseConnections();
|
||||
|
||||
LogSys.EnableConsoleLogging();
|
||||
|
||||
WorldserverCommandHandler::CommandHandler(argc, argv);
|
||||
}
|
||||
|
||||
CheckForXMLConfigUpgrade();
|
||||
LoadServerConfig();
|
||||
|
||||
Config = WorldConfig::get();
|
||||
|
||||
LogInfo("CURRENT_VERSION: [{}]", CURRENT_VERSION);
|
||||
@ -169,146 +268,23 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Add Loginserver
|
||||
*/
|
||||
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
|
||||
);
|
||||
RegisterLoginservers();
|
||||
LoadDatabaseConnections();
|
||||
|
||||
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);
|
||||
|
||||
/* Register Log System and Settings */
|
||||
/**
|
||||
* Logging
|
||||
*/
|
||||
database.LoadLogSettings(LogSys.log_settings);
|
||||
LogSys.StartFileLogs();
|
||||
|
||||
/**
|
||||
* Parse simple CLI passes
|
||||
*/
|
||||
bool ignore_db = false;
|
||||
if (argc >= 2) {
|
||||
std::string tmp;
|
||||
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) {
|
||||
if (strcasecmp(argv[1], "ignore_db") == 0) {
|
||||
ignore_db = true;
|
||||
}
|
||||
else {
|
||||
@ -360,7 +336,7 @@ int main(int argc, char** argv) {
|
||||
if (!RuleManager::Instance()->UpdateOrphanedRules(&database)) {
|
||||
LogInfo("Failed to process 'Orphaned Rules' update operation.");
|
||||
}
|
||||
|
||||
|
||||
if (!RuleManager::Instance()->UpdateInjectedRules(&database, "default")) {
|
||||
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");
|
||||
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("Loading adventures");
|
||||
@ -427,7 +396,6 @@ int main(int argc, char** argv) {
|
||||
LogInfo("Unable to load adventure templates");
|
||||
}
|
||||
|
||||
adventure_manager.Load();
|
||||
adventure_manager.LoadLeaderboardInfo();
|
||||
|
||||
LogInfo("Purging expired instances");
|
||||
@ -24,7 +24,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p)
|
||||
std::stringstream ss(json_str);
|
||||
ss >> root;
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
SendError("Could not parse request");
|
||||
return;
|
||||
}
|
||||
@ -40,7 +40,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p)
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
SendError("Invalid request: method not supplied");
|
||||
return;
|
||||
}
|
||||
@ -49,7 +49,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p)
|
||||
try {
|
||||
params = root["params"];
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
params = nullptr;
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p)
|
||||
try {
|
||||
id = root["id"].asString();
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
id = "";
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ void WebInterface::Send(const Json::Value &value)
|
||||
p.PutString(0, ss.str());
|
||||
m_connection->Send(ServerOP_WebInterfaceCall, p);
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
//Log error
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,7 @@ void WebInterface::SendEvent(const Json::Value &value)
|
||||
p.PutString(0, ss.str());
|
||||
m_connection->Send(ServerOP_WebInterfaceEvent, p);
|
||||
}
|
||||
catch (std::exception) {
|
||||
catch (std::exception &) {
|
||||
//Log error
|
||||
}
|
||||
}
|
||||
|
||||
205
world/world_server_command_handler.cpp
Normal file
205
world/world_server_command_handler.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
36
world/world_server_command_handler.h
Normal file
36
world/world_server_command_handler.h
Normal 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
|
||||
@ -31,23 +31,27 @@ extern std::vector<RaceClassAllocation> character_create_allocations;
|
||||
extern std::vector<RaceClassCombos> character_create_race_class_combos;
|
||||
|
||||
|
||||
// the current stuff is at the bottom of this function
|
||||
void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit)
|
||||
/**
|
||||
* @param account_id
|
||||
* @param out_app
|
||||
* @param client_version_bit
|
||||
*/
|
||||
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
|
||||
{
|
||||
/* Set Character Creation Limit */
|
||||
EQEmu::versions::ClientVersion client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(clientVersionBit);
|
||||
EQEmu::versions::ClientVersion
|
||||
client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(client_version_bit);
|
||||
size_t character_limit = EQEmu::constants::StaticLookup(client_version)->CharacterCreationLimit;
|
||||
|
||||
// Validate against absolute server max
|
||||
if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT)
|
||||
|
||||
if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT) {
|
||||
character_limit = EQEmu::constants::CHARACTER_CREATION_LIMIT;
|
||||
}
|
||||
|
||||
// Force Titanium clients to use '8'
|
||||
if (client_version == EQEmu::versions::ClientVersion::Titanium)
|
||||
if (client_version == EQEmu::versions::ClientVersion::Titanium) {
|
||||
character_limit = 8;
|
||||
|
||||
/* Get Character Info */
|
||||
std::string cquery = StringFormat(
|
||||
}
|
||||
|
||||
std::string character_list_query = StringFormat(
|
||||
"SELECT "
|
||||
"`id`, " // 0
|
||||
"name, " // 1
|
||||
@ -71,237 +75,281 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou
|
||||
"zone_id " // 19
|
||||
"FROM "
|
||||
"character_data "
|
||||
"WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit);
|
||||
auto results = database.QueryDatabase(cquery);
|
||||
"WHERE `account_id` = %i AND deleted_at IS NULL ORDER BY `name` LIMIT %u",
|
||||
account_id,
|
||||
character_limit
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(character_list_query);
|
||||
|
||||
size_t character_count = results.RowCount();
|
||||
if (character_count == 0) {
|
||||
*outApp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *)(*outApp)->pBuffer;
|
||||
cs->CharCount = 0;
|
||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
|
||||
cs->CharCount = 0;
|
||||
cs->TotalChars = character_limit;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
|
||||
*outApp = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
||||
|
||||
unsigned char *buff_ptr = (*outApp)->pBuffer;
|
||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *)buff_ptr;
|
||||
unsigned char *buff_ptr = (*out_app)->pBuffer;
|
||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr;
|
||||
|
||||
cs->CharCount = character_count;
|
||||
cs->CharCount = character_count;
|
||||
cs->TotalChars = character_limit;
|
||||
|
||||
buff_ptr += sizeof(CharacterSelect_Struct);
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr;
|
||||
PlayerProfile_Struct pp;
|
||||
EQEmu::InventoryProfile inv;
|
||||
CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr;
|
||||
PlayerProfile_Struct player_profile_struct;
|
||||
EQEmu::InventoryProfile inventory_profile;
|
||||
|
||||
pp.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version));
|
||||
inv.SetInventoryVersion(client_version);
|
||||
inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
||||
player_profile_struct.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version));
|
||||
inventory_profile.SetInventoryVersion(client_version);
|
||||
inventory_profile.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
||||
|
||||
uint32 character_id = (uint32)atoi(row[0]);
|
||||
uint8 has_home = 0;
|
||||
uint8 has_bind = 0;
|
||||
uint32 character_id = (uint32) atoi(row[0]);
|
||||
uint8 has_home = 0;
|
||||
uint8 has_bind = 0;
|
||||
|
||||
memset(&pp, 0, sizeof(PlayerProfile_Struct));
|
||||
|
||||
/* Fill CharacterSelectEntry_Struct */
|
||||
memset(cse->Name, 0, sizeof(cse->Name));
|
||||
strcpy(cse->Name, row[1]);
|
||||
cse->Class = (uint8)atoi(row[4]);
|
||||
cse->Race = (uint32)atoi(row[3]);
|
||||
cse->Level = (uint8)atoi(row[5]);
|
||||
cse->ShroudClass = cse->Class;
|
||||
cse->ShroudRace = cse->Race;
|
||||
cse->Zone = (uint16)atoi(row[19]);
|
||||
cse->Instance = 0;
|
||||
cse->Gender = (uint8)atoi(row[2]);
|
||||
cse->Face = (uint8)atoi(row[15]);
|
||||
memset(&player_profile_struct, 0, sizeof(PlayerProfile_Struct));
|
||||
|
||||
for (uint32 matslot = 0; matslot < EQEmu::textures::materialCount; matslot++) { // Processed below
|
||||
cse->Equip[matslot].Material = 0;
|
||||
cse->Equip[matslot].Unknown1 = 0;
|
||||
cse->Equip[matslot].EliteModel = 0;
|
||||
cse->Equip[matslot].HerosForgeModel = 0;
|
||||
cse->Equip[matslot].Unknown2 = 0;
|
||||
cse->Equip[matslot].Color = 0;
|
||||
}
|
||||
memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name));
|
||||
strcpy(p_character_select_entry_struct->Name, row[1]);
|
||||
p_character_select_entry_struct->Class = (uint8) atoi(row[4]);
|
||||
p_character_select_entry_struct->Race = (uint32) atoi(row[3]);
|
||||
p_character_select_entry_struct->Level = (uint8) atoi(row[5]);
|
||||
p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class;
|
||||
p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race;
|
||||
p_character_select_entry_struct->Zone = (uint16) atoi(row[19]);
|
||||
p_character_select_entry_struct->Instance = 0;
|
||||
p_character_select_entry_struct->Gender = (uint8) atoi(row[2]);
|
||||
p_character_select_entry_struct->Face = (uint8) atoi(row[15]);
|
||||
|
||||
cse->Unknown15 = 0xFF;
|
||||
cse->Unknown19 = 0xFF;
|
||||
cse->DrakkinTattoo = (uint32)atoi(row[17]);
|
||||
cse->DrakkinDetails = (uint32)atoi(row[18]);
|
||||
cse->Deity = (uint32)atoi(row[6]);
|
||||
cse->PrimaryIDFile = 0; // Processed Below
|
||||
cse->SecondaryIDFile = 0; // Processed Below
|
||||
cse->HairColor = (uint8)atoi(row[9]);
|
||||
cse->BeardColor = (uint8)atoi(row[10]);
|
||||
cse->EyeColor1 = (uint8)atoi(row[11]);
|
||||
cse->EyeColor2 = (uint8)atoi(row[12]);
|
||||
cse->HairStyle = (uint8)atoi(row[13]);
|
||||
cse->Beard = (uint8)atoi(row[14]);
|
||||
cse->GoHome = 0; // Processed Below
|
||||
cse->Tutorial = 0; // Processed Below
|
||||
cse->DrakkinHeritage = (uint32)atoi(row[16]);
|
||||
cse->Unknown1 = 0;
|
||||
cse->Enabled = 1;
|
||||
cse->LastLogin = (uint32)atoi(row[7]); // RoF2 value: 1212696584
|
||||
cse->Unknown2 = 0;
|
||||
/* Fill End */
|
||||
for (uint32 material_slot = 0; material_slot < EQEmu::textures::materialCount; material_slot++) {
|
||||
p_character_select_entry_struct->Equip[material_slot].Material = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].EliteModel = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0;
|
||||
p_character_select_entry_struct->Equip[material_slot].Color = 0;
|
||||
}
|
||||
|
||||
p_character_select_entry_struct->Unknown15 = 0xFF;
|
||||
p_character_select_entry_struct->Unknown19 = 0xFF;
|
||||
p_character_select_entry_struct->DrakkinTattoo = (uint32) atoi(row[17]);
|
||||
p_character_select_entry_struct->DrakkinDetails = (uint32) atoi(row[18]);
|
||||
p_character_select_entry_struct->Deity = (uint32) atoi(row[6]);
|
||||
p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below
|
||||
p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below
|
||||
p_character_select_entry_struct->HairColor = (uint8) atoi(row[9]);
|
||||
p_character_select_entry_struct->BeardColor = (uint8) atoi(row[10]);
|
||||
p_character_select_entry_struct->EyeColor1 = (uint8) atoi(row[11]);
|
||||
p_character_select_entry_struct->EyeColor2 = (uint8) atoi(row[12]);
|
||||
p_character_select_entry_struct->HairStyle = (uint8) atoi(row[13]);
|
||||
p_character_select_entry_struct->Beard = (uint8) atoi(row[14]);
|
||||
p_character_select_entry_struct->GoHome = 0; // Processed Below
|
||||
p_character_select_entry_struct->Tutorial = 0; // Processed Below
|
||||
p_character_select_entry_struct->DrakkinHeritage = (uint32) atoi(row[16]);
|
||||
p_character_select_entry_struct->Unknown1 = 0;
|
||||
p_character_select_entry_struct->Enabled = 1;
|
||||
p_character_select_entry_struct->LastLogin = (uint32) atoi(row[7]); // RoF2 value: 1212696584
|
||||
p_character_select_entry_struct->Unknown2 = 0;
|
||||
|
||||
if (RuleB(World, EnableReturnHomeButton)) {
|
||||
int now = time(nullptr);
|
||||
if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome))
|
||||
cse->GoHome = 1;
|
||||
if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) {
|
||||
p_character_select_entry_struct->GoHome = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) {
|
||||
cse->Tutorial = 1;
|
||||
if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial))) {
|
||||
p_character_select_entry_struct->Tutorial = 1;
|
||||
}
|
||||
|
||||
/* Set Bind Point Data for any character that may possibly be missing it for any reason */
|
||||
cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5", character_id);
|
||||
auto results_bind = database.QueryDatabase(cquery);
|
||||
auto bind_count = results_bind.RowCount();
|
||||
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) {
|
||||
/**
|
||||
* Bind
|
||||
*/
|
||||
character_list_query = StringFormat(
|
||||
"SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5",
|
||||
character_id
|
||||
);
|
||||
auto results_bind = database.QueryDatabase(character_list_query);
|
||||
auto bind_count = results_bind.RowCount();
|
||||
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) {
|
||||
if (row_b[6] && atoi(row_b[6]) == 4) {
|
||||
has_home = 1;
|
||||
// If our bind count is less than 5, we need to actually make use of this data so lets parse it
|
||||
if (bind_count < 5) {
|
||||
pp.binds[4].zoneId = atoi(row_b[0]);
|
||||
pp.binds[4].instance_id = atoi(row_b[1]);
|
||||
pp.binds[4].x = atof(row_b[2]);
|
||||
pp.binds[4].y = atof(row_b[3]);
|
||||
pp.binds[4].z = atof(row_b[4]);
|
||||
pp.binds[4].heading = atof(row_b[5]);
|
||||
player_profile_struct.binds[4].zoneId = atoi(row_b[0]);
|
||||
player_profile_struct.binds[4].instance_id = atoi(row_b[1]);
|
||||
player_profile_struct.binds[4].x = atof(row_b[2]);
|
||||
player_profile_struct.binds[4].y = atof(row_b[3]);
|
||||
player_profile_struct.binds[4].z = atof(row_b[4]);
|
||||
player_profile_struct.binds[4].heading = atof(row_b[5]);
|
||||
}
|
||||
}
|
||||
if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; }
|
||||
if (row_b[6] && atoi(row_b[6]) == 0) { has_bind = 1; }
|
||||
}
|
||||
|
||||
if (has_home == 0 || has_bind == 0) {
|
||||
cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i",
|
||||
cse->Class, cse->Deity, cse->Race);
|
||||
auto results_bind = database.QueryDatabase(cquery);
|
||||
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
|
||||
character_list_query = StringFormat(
|
||||
"SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i",
|
||||
p_character_select_entry_struct->Class,
|
||||
p_character_select_entry_struct->Deity,
|
||||
p_character_select_entry_struct->Race
|
||||
);
|
||||
auto results_bind = database.QueryDatabase(character_list_query);
|
||||
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
|
||||
/* If a bind_id is specified, make them start there */
|
||||
if (atoi(row_d[1]) != 0) {
|
||||
pp.binds[4].zoneId = (uint32)atoi(row_d[1]);
|
||||
GetSafePoints(pp.binds[4].zoneId, 0, &pp.binds[4].x, &pp.binds[4].y, &pp.binds[4].z);
|
||||
player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[1]);
|
||||
GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &player_profile_struct.binds[4].x, &player_profile_struct.binds[4].y, &player_profile_struct.binds[4].z);
|
||||
}
|
||||
/* Otherwise, use the zone and coordinates given */
|
||||
/* Otherwise, use the zone and coordinates given */
|
||||
else {
|
||||
pp.binds[4].zoneId = (uint32)atoi(row_d[0]);
|
||||
player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[0]);
|
||||
float x = atof(row_d[2]);
|
||||
float y = atof(row_d[3]);
|
||||
float z = atof(row_d[4]);
|
||||
if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); }
|
||||
pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z;
|
||||
if (x == 0 && y == 0 && z == 0) { GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &x, &y, &z); }
|
||||
player_profile_struct.binds[4].x = x;
|
||||
player_profile_struct.binds[4].y = y;
|
||||
player_profile_struct.binds[4].z = z;
|
||||
}
|
||||
}
|
||||
pp.binds[0] = pp.binds[4];
|
||||
player_profile_struct.binds[0] = player_profile_struct.binds[4];
|
||||
/* If no home bind set, set it */
|
||||
if (has_home == 0) {
|
||||
std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
std::string query = StringFormat(
|
||||
"REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
" VALUES (%u, %u, %u, %f, %f, %f, %f, %i)",
|
||||
character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 4);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
character_id,
|
||||
player_profile_struct.binds[4].zoneId,
|
||||
0,
|
||||
player_profile_struct.binds[4].x,
|
||||
player_profile_struct.binds[4].y,
|
||||
player_profile_struct.binds[4].z,
|
||||
player_profile_struct.binds[4].heading,
|
||||
4
|
||||
);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
}
|
||||
/* If no regular bind set, set it */
|
||||
if (has_bind == 0) {
|
||||
std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
std::string query = StringFormat(
|
||||
"REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
" VALUES (%u, %u, %u, %f, %f, %f, %f, %i)",
|
||||
character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
character_id,
|
||||
player_profile_struct.binds[0].zoneId,
|
||||
0,
|
||||
player_profile_struct.binds[0].x,
|
||||
player_profile_struct.binds[0].y,
|
||||
player_profile_struct.binds[0].z,
|
||||
player_profile_struct.binds[0].heading,
|
||||
0
|
||||
);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
/* If our bind count is less than 5, then we have null data that needs to be filled in. */
|
||||
if (bind_count < 5) {
|
||||
// we know that home and main bind must be valid here, so we don't check those
|
||||
// we also use home to fill in the null data like live does.
|
||||
for (int i = 1; i < 4; i++) {
|
||||
if (pp.binds[i].zoneId != 0) // we assume 0 is the only invalid one ...
|
||||
for (int i = 1; i < 4; i++) {
|
||||
if (player_profile_struct.binds[i].zoneId != 0) { // we assume 0 is the only invalid one ...
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
std::string query = StringFormat(
|
||||
"REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)"
|
||||
" VALUES (%u, %u, %u, %f, %f, %f, %f, %i)",
|
||||
character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, i);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
character_id,
|
||||
player_profile_struct.binds[4].zoneId,
|
||||
0,
|
||||
player_profile_struct.binds[4].x,
|
||||
player_profile_struct.binds[4].y,
|
||||
player_profile_struct.binds[4].z,
|
||||
player_profile_struct.binds[4].heading,
|
||||
i
|
||||
);
|
||||
auto results_bset = QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
/* Bind End */
|
||||
|
||||
/* Load Character Material Data for Char Select */
|
||||
cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id);
|
||||
auto results_b = database.QueryDatabase(cquery); uint8 slot = 0;
|
||||
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) {
|
||||
character_list_query = StringFormat(
|
||||
"SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u",
|
||||
character_id
|
||||
);
|
||||
auto results_b = database.QueryDatabase(character_list_query);
|
||||
uint8 slot = 0;
|
||||
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) {
|
||||
slot = atoi(row_b[0]);
|
||||
pp.item_tint.Slot[slot].Red = atoi(row_b[1]);
|
||||
pp.item_tint.Slot[slot].Green = atoi(row_b[2]);
|
||||
pp.item_tint.Slot[slot].Blue = atoi(row_b[3]);
|
||||
pp.item_tint.Slot[slot].UseTint = atoi(row_b[4]);
|
||||
player_profile_struct.item_tint.Slot[slot].Red = atoi(row_b[1]);
|
||||
player_profile_struct.item_tint.Slot[slot].Green = atoi(row_b[2]);
|
||||
player_profile_struct.item_tint.Slot[slot].Blue = atoi(row_b[3]);
|
||||
player_profile_struct.item_tint.Slot[slot].UseTint = atoi(row_b[4]);
|
||||
}
|
||||
/* Character Material Data End */
|
||||
|
||||
/* Load Inventory */
|
||||
// If we ensure that the material data is updated appropriately, we can do away with inventory loads
|
||||
if (GetCharSelInventory(accountID, cse->Name, &inv)) {
|
||||
const EQEmu::ItemData* item = nullptr;
|
||||
const EQEmu::ItemInstance* inst = nullptr;
|
||||
int16 invslot = 0;
|
||||
if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) {
|
||||
const EQEmu::ItemData *item = nullptr;
|
||||
const EQEmu::ItemInstance *inst = nullptr;
|
||||
int16 inventory_slot = 0;
|
||||
|
||||
for (uint32 matslot = EQEmu::textures::textureBegin; matslot < EQEmu::textures::materialCount; matslot++) {
|
||||
invslot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot);
|
||||
if (invslot == INVALID_INDEX) { continue; }
|
||||
inst = inv.GetItem(invslot);
|
||||
if (inst == nullptr) { continue; }
|
||||
inventory_slot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot);
|
||||
if (inventory_slot == INVALID_INDEX) { continue; }
|
||||
inst = inventory_profile.GetItem(inventory_slot);
|
||||
if (inst == nullptr) {
|
||||
continue;
|
||||
}
|
||||
item = inst->GetItem();
|
||||
if (item == nullptr) { continue; }
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matslot > 6) {
|
||||
uint32 idfile = 0;
|
||||
uint32 item_id_file = 0;
|
||||
// Weapon Models
|
||||
if (inst->GetOrnamentationIDFile() != 0) {
|
||||
idfile = inst->GetOrnamentationIDFile();
|
||||
cse->Equip[matslot].Material = idfile;
|
||||
item_id_file = inst->GetOrnamentationIDFile();
|
||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
||||
}
|
||||
else {
|
||||
if (strlen(item->IDFile) > 2) {
|
||||
idfile = atoi(&item->IDFile[2]);
|
||||
cse->Equip[matslot].Material = idfile;
|
||||
item_id_file = atoi(&item->IDFile[2]);
|
||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
||||
}
|
||||
}
|
||||
if (matslot == EQEmu::textures::weaponPrimary) {
|
||||
cse->PrimaryIDFile = idfile;
|
||||
p_character_select_entry_struct->PrimaryIDFile = item_id_file;
|
||||
}
|
||||
else {
|
||||
cse->SecondaryIDFile = idfile;
|
||||
p_character_select_entry_struct->SecondaryIDFile = item_id_file;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32 color = 0;
|
||||
if (pp.item_tint.Slot[matslot].UseTint) {
|
||||
color = pp.item_tint.Slot[matslot].Color;
|
||||
if (player_profile_struct.item_tint.Slot[matslot].UseTint) {
|
||||
color = player_profile_struct.item_tint.Slot[matslot].Color;
|
||||
}
|
||||
else {
|
||||
color = inst->GetColor();
|
||||
}
|
||||
|
||||
// Armor Materials/Models
|
||||
cse->Equip[matslot].Material = item->Material;
|
||||
cse->Equip[matslot].EliteModel = item->EliteMaterial;
|
||||
cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
||||
cse->Equip[matslot].Color = color;
|
||||
p_character_select_entry_struct->Equip[matslot].Material = item->Material;
|
||||
p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial;
|
||||
p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
||||
p_character_select_entry_struct->Equip[matslot].Color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Error loading inventory for %s\n", cse->Name);
|
||||
printf("Error loading inventory for %s\n", p_character_select_entry_struct->Name);
|
||||
}
|
||||
/* Load Inventory End */
|
||||
|
||||
buff_ptr += sizeof(CharacterSelectEntry_Struct);
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ struct CharacterSelect_Struct;
|
||||
class WorldDatabase : public SharedDatabase {
|
||||
public:
|
||||
bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc, bool isTitanium);
|
||||
void GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit);
|
||||
void GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit);
|
||||
int MoveCharacterToBind(int CharID, uint8 bindnum = 0);
|
||||
|
||||
void GetLauncherList(std::vector<std::string> &result);
|
||||
|
||||
@ -1057,110 +1057,42 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
break;
|
||||
}
|
||||
case ServerOP_Consent: {
|
||||
// Message string id's likely to be used here are:
|
||||
// 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);
|
||||
}
|
||||
zoneserver_list.SendPacket(pack); // update corpses in all zones
|
||||
break;
|
||||
}
|
||||
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;
|
||||
if (s->instance_id != 0)
|
||||
{
|
||||
ZoneServer* 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);
|
||||
}
|
||||
|
||||
ZoneServer* owner_zs = nullptr;
|
||||
if (s->instance_id == 0) {
|
||||
owner_zs = zoneserver_list.FindByZoneID(s->zone_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZoneServer* 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);
|
||||
else {
|
||||
owner_zs = zoneserver_list.FindByInstanceID(s->instance_id);
|
||||
}
|
||||
|
||||
if (owner_zs) {
|
||||
owner_zs->SendPacket(pack);
|
||||
}
|
||||
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;
|
||||
|
||||
16
zone/aa.cpp
16
zone/aa.cpp
@ -1467,12 +1467,20 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
|
||||
}
|
||||
}
|
||||
|
||||
if(IsClient()) {
|
||||
if(rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) {
|
||||
if (IsClient()) {
|
||||
if (rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
110
zone/aggro.cpp
110
zone/aggro.cpp
@ -36,19 +36,6 @@
|
||||
extern Zone* zone;
|
||||
//#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) {
|
||||
float d2 = d*d;
|
||||
|
||||
@ -402,22 +389,6 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
|
||||
if(!sender || !attacker)
|
||||
return;
|
||||
if (sender->GetPrimaryFaction() == 0 )
|
||||
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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param target
|
||||
* @param isSpellAttack
|
||||
* @return
|
||||
*/
|
||||
bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
{
|
||||
|
||||
|
||||
@ -724,20 +724,20 @@ int Mob::GetClassRaceACBonus()
|
||||
hardcap = 32;
|
||||
softcap = 15;
|
||||
}
|
||||
int weight = IsClient() ? CastToClient()->CalcCurrentWeight() : 0;
|
||||
int weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10 : 0;
|
||||
if (weight < hardcap - 1) {
|
||||
int temp = level + 5;
|
||||
double temp = level + 5;
|
||||
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;
|
||||
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) {
|
||||
int temp = level + 5;
|
||||
double multiplier = std::min(1.0, (weight - (hardcap - 10.0)) / 100.0);
|
||||
temp = (4 * temp) / 3;
|
||||
double temp = level + 5;
|
||||
double multiplier = std::min(1.0, (weight - (static_cast<double>(hardcap) - 10.0)) / 100.0);
|
||||
temp = (4.0 * temp) / 3.0;
|
||||
ac_bonus -= static_cast<int>(temp * multiplier);
|
||||
}
|
||||
}
|
||||
@ -850,11 +850,12 @@ int Mob::ACSum()
|
||||
auto over_cap = ac - softcap;
|
||||
ac = softcap + (over_cap * returns);
|
||||
}
|
||||
LogCombat("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns);
|
||||
LogCombatDetail("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns);
|
||||
}
|
||||
else {
|
||||
LogCombat("ACSum ac [{}]", ac);
|
||||
LogCombatDetail("ACSum ac [{}]", 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.RemoveNPC(GetID());
|
||||
|
||||
// entity_list.RemoveMobFromCloseLists(this);
|
||||
close_mobs.clear();
|
||||
|
||||
this->SetID(0);
|
||||
|
||||
if (killer != 0 && emoteid != 0)
|
||||
|
||||
673
zone/aura.cpp
673
zone/aura.cpp
File diff suppressed because it is too large
Load Diff
@ -73,7 +73,7 @@ private:
|
||||
int m_owner;
|
||||
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 distance; // distance we remove
|
||||
float distance; // distance we remove
|
||||
Timer remove_timer; // when we depop
|
||||
Timer process_timer; // rate limit process calls
|
||||
Timer cast_timer; // some auras pulse
|
||||
|
||||
458
zone/bot.cpp
458
zone/bot.cpp
@ -83,6 +83,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
SetPauseAI(false);
|
||||
|
||||
m_alt_combat_hate_timer.Start(250);
|
||||
m_auto_defend_timer.Disable();
|
||||
//m_combat_jitter_timer.Disable();
|
||||
//SetCombatJitterFlag(false);
|
||||
SetGuardFlag(false);
|
||||
@ -180,6 +181,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
SetPauseAI(false);
|
||||
|
||||
m_alt_combat_hate_timer.Start(250);
|
||||
m_auto_defend_timer.Disable();
|
||||
//m_combat_jitter_timer.Disable();
|
||||
//SetCombatJitterFlag(false);
|
||||
SetGuardFlag(false);
|
||||
@ -236,8 +238,157 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
CalcBotStats(false);
|
||||
hp_regen = CalcHPRegen();
|
||||
@ -282,15 +433,61 @@ void Bot::SetBotSpellID(uint32 newSpellID) {
|
||||
}
|
||||
|
||||
void Bot::SetSurname(std::string bot_surname) {
|
||||
|
||||
_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) {
|
||||
_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) {
|
||||
_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() {
|
||||
@ -1457,7 +1654,7 @@ int32 Bot::GenerateBaseHitPoints() {
|
||||
}
|
||||
|
||||
void Bot::LoadAAs() {
|
||||
int maxAAExpansion = RuleI(Bots, AAExpansion); //get expansion to get AAs up to
|
||||
|
||||
aa_ranks.clear();
|
||||
|
||||
int id = 0;
|
||||
@ -2014,6 +2211,17 @@ bool Bot::Process()
|
||||
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();
|
||||
|
||||
if(tic_timer.Check()) {
|
||||
@ -2284,7 +2492,7 @@ void Bot::SetTarget(Mob* mob) {
|
||||
}
|
||||
|
||||
void Bot::SetStopMeleeLevel(uint8 level) {
|
||||
if (IsCasterClass(GetClass()) || IsSpellFighterClass(GetClass()))
|
||||
if (IsCasterClass(GetClass()) || IsHybridClass(GetClass()))
|
||||
_stopMeleeLevel = level;
|
||||
else
|
||||
_stopMeleeLevel = 255;
|
||||
@ -2307,15 +2515,16 @@ void Bot::SetHoldMode() {
|
||||
}
|
||||
|
||||
// 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()
|
||||
{
|
||||
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 PULLING_BOT (GetPullingFlag() || GetReturningFlag())
|
||||
#define NOT_PULLING_BOT (!GetPullingFlag() && !GetReturningFlag())
|
||||
@ -2627,7 +2836,7 @@ void Bot::AI_Process()
|
||||
|
||||
return;
|
||||
}
|
||||
else if (HasTargetReflection()) {
|
||||
else if (GetTarget()->GetHateList().size()) {
|
||||
|
||||
WipeHateList();
|
||||
SetTarget(nullptr);
|
||||
@ -2711,14 +2920,14 @@ void Bot::AI_Process()
|
||||
if (find_target) {
|
||||
|
||||
if (IsRooted()) {
|
||||
SetTarget(hate_list.GetClosestEntOnHateList(this));
|
||||
SetTarget(hate_list.GetClosestEntOnHateList(this, true));
|
||||
}
|
||||
else {
|
||||
|
||||
// This will keep bots on target for now..but, future updates will allow for rooting/stunning
|
||||
SetTarget(hate_list.GetEscapingEntOnHateList(leash_owner, leash_distance));
|
||||
if (!GetTarget()) {
|
||||
SetTarget(hate_list.GetEntWithMostHateOnList(this));
|
||||
SetTarget(hate_list.GetEntWithMostHateOnList(this, nullptr, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3192,7 +3401,7 @@ void Bot::AI_Process()
|
||||
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..
|
||||
// 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...
|
||||
// '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) {
|
||||
|
||||
@ -3420,7 +3635,7 @@ void Bot::AI_Process()
|
||||
}
|
||||
|
||||
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
|
||||
AddToHateList(hater, 1);
|
||||
@ -3432,6 +3647,8 @@ void Bot::AI_Process()
|
||||
GetPet()->SetTarget(hater);
|
||||
}
|
||||
|
||||
m_auto_defend_timer.Disable();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -4726,9 +4943,9 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::Sk
|
||||
Mob *my_owner = GetBotOwner();
|
||||
if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOption(Client::booDeathMarquee)) {
|
||||
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
|
||||
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);
|
||||
@ -5017,7 +5234,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
|
||||
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];
|
||||
int32 value = 0;
|
||||
@ -5171,6 +5388,10 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16
|
||||
if (type == focusImprovedDamage && base1 > value)
|
||||
value = base1;
|
||||
break;
|
||||
case SE_ImprovedDamage2:
|
||||
if (type == focusImprovedDamage2 && base1 > value)
|
||||
value = base1;
|
||||
break;
|
||||
case SE_ImprovedHeal:
|
||||
if (type == focusImprovedHeal && base1 > value)
|
||||
value = base1;
|
||||
@ -5282,6 +5503,11 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16
|
||||
value = base1;
|
||||
break;
|
||||
}
|
||||
case SE_FcDamageAmt2: {
|
||||
if(type == focusFcDamageAmt2)
|
||||
value = base1;
|
||||
break;
|
||||
}
|
||||
case SE_FcDamageAmtCrit: {
|
||||
if(type == focusFcDamageAmtCrit)
|
||||
value = base1;
|
||||
@ -5340,8 +5566,8 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16
|
||||
return (value * lvlModifier / 100);
|
||||
}
|
||||
|
||||
int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) {
|
||||
if (IsBardSong(spell_id) && bottype != BotfocusFcBaseEffects)
|
||||
int32 Bot::GetBotFocusEffect(focusType bottype, uint16 spell_id) {
|
||||
if (IsBardSong(spell_id) && bottype != focusFcBaseEffects)
|
||||
return 0;
|
||||
|
||||
int32 realTotal = 0;
|
||||
@ -5350,7 +5576,7 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) {
|
||||
bool rand_effectiveness = false;
|
||||
//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
|
||||
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;
|
||||
|
||||
//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;
|
||||
|
||||
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 (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))
|
||||
return 0;
|
||||
|
||||
@ -5630,7 +5856,21 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
return 0;
|
||||
break;
|
||||
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 (focus_spell.base2[i] != 0)
|
||||
value = focus_spell.base2[i];
|
||||
@ -5644,7 +5884,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
}
|
||||
break;
|
||||
case SE_ImprovedHeal:
|
||||
if (bottype == BotfocusImprovedHeal) {
|
||||
if (bottype == focusImprovedHeal) {
|
||||
if(best_focus) {
|
||||
if (focus_spell.base2[i] != 0)
|
||||
value = focus_spell.base2[i];
|
||||
@ -5658,7 +5898,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
}
|
||||
break;
|
||||
case SE_ReduceManaCost:
|
||||
if (bottype == BotfocusManaCost) {
|
||||
if (bottype == focusManaCost) {
|
||||
if(best_focus) {
|
||||
if (focus_spell.base2[i] != 0)
|
||||
value = focus_spell.base2[i];
|
||||
@ -5672,39 +5912,39 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
}
|
||||
break;
|
||||
case SE_IncreaseSpellHaste:
|
||||
if (bottype == BotfocusSpellHaste && focus_spell.base[i] > value)
|
||||
if (bottype == focusSpellHaste && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_IncreaseSpellDuration:
|
||||
if (bottype == BotfocusSpellDuration && focus_spell.base[i] > value)
|
||||
if (bottype == focusSpellDuration && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_SpellDurationIncByTic:
|
||||
if (bottype == BotfocusSpellDurByTic && focus_spell.base[i] > value)
|
||||
if (bottype == focusSpellDurByTic && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_SwarmPetDuration:
|
||||
if (bottype == BotfocusSwarmPetDuration && focus_spell.base[i] > value)
|
||||
if (bottype == focusSwarmPetDuration && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_IncreaseRange:
|
||||
if (bottype == BotfocusRange && focus_spell.base[i] > value)
|
||||
if (bottype == focusRange && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_ReduceReagentCost:
|
||||
if (bottype == BotfocusReagentCost && focus_spell.base[i] > value)
|
||||
if (bottype == focusReagentCost && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_PetPowerIncrease:
|
||||
if (bottype == BotfocusPetPower && focus_spell.base[i] > value)
|
||||
if (bottype == focusPetPower && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_SpellResistReduction:
|
||||
if (bottype == BotfocusResistRate && focus_spell.base[i] > value)
|
||||
if (bottype == focusResistRate && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_SpellHateMod:
|
||||
if (bottype == BotfocusSpellHateMod) {
|
||||
if (bottype == focusSpellHateMod) {
|
||||
if(value != 0) {
|
||||
if(value > 0) {
|
||||
if(focus_spell.base[i] > value)
|
||||
@ -5719,12 +5959,12 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
}
|
||||
break;
|
||||
case SE_ReduceReuseTimer: {
|
||||
if(bottype == BotfocusReduceRecastTime)
|
||||
if(bottype == focusReduceRecastTime)
|
||||
value = (focus_spell.base[i] / 1000);
|
||||
break;
|
||||
}
|
||||
case SE_TriggerOnCast: {
|
||||
if(bottype == BotfocusTriggerOnCast) {
|
||||
if(bottype == focusTriggerOnCast) {
|
||||
if(zone->random.Int(0, 100) <= focus_spell.base[i])
|
||||
value = focus_spell.base2[i];
|
||||
else
|
||||
@ -5733,24 +5973,24 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
break;
|
||||
}
|
||||
case SE_FcSpellVulnerability: {
|
||||
if(bottype == BotfocusSpellVulnerability)
|
||||
if(bottype == focusSpellVulnerability)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
}
|
||||
case SE_BlockNextSpellFocus: {
|
||||
if(bottype == BotfocusBlockNextSpell) {
|
||||
if(bottype == focusBlockNextSpell) {
|
||||
if(zone->random.Int(1, 100) <= focus_spell.base[i])
|
||||
value = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SE_FcTwincast: {
|
||||
if(bottype == BotfocusTwincast)
|
||||
if(bottype == focusTwincast)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
}
|
||||
case SE_SympatheticProc: {
|
||||
if(bottype == BotfocusSympatheticProc) {
|
||||
if(bottype == focusSympatheticProc) {
|
||||
float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]);
|
||||
if(zone->random.Real(0, 1) <= ProcChance)
|
||||
value = focus_id;
|
||||
@ -5760,49 +6000,54 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
break;
|
||||
}
|
||||
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];
|
||||
break;
|
||||
}
|
||||
case SE_FcDamageAmtCrit: {
|
||||
if(bottype == BotfocusFcDamageAmtCrit)
|
||||
if(bottype == focusFcDamageAmtCrit)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
}
|
||||
case SE_FcHealAmtIncoming:
|
||||
if(bottype == BotfocusFcHealAmtIncoming)
|
||||
if(bottype == focusFcHealAmtIncoming)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_FcHealPctCritIncoming:
|
||||
if (bottype == BotfocusFcHealPctCritIncoming)
|
||||
if (bottype == focusFcHealPctCritIncoming)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_FcHealAmtCrit:
|
||||
if(bottype == BotfocusFcHealAmtCrit)
|
||||
if(bottype == focusFcHealAmtCrit)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_FcHealAmt:
|
||||
if(bottype == BotfocusFcHealAmt)
|
||||
if(bottype == focusFcHealAmt)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_FcHealPctIncoming:
|
||||
if(bottype == BotfocusFcHealPctIncoming)
|
||||
if(bottype == focusFcHealPctIncoming)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
case SE_FcBaseEffects: {
|
||||
if (bottype == BotfocusFcBaseEffects)
|
||||
if (bottype == focusFcBaseEffects)
|
||||
value = focus_spell.base[i];
|
||||
|
||||
break;
|
||||
}
|
||||
case SE_FcDamagePctCrit: {
|
||||
if(bottype == BotfocusFcDamagePctCrit)
|
||||
if(bottype == focusFcDamagePctCrit)
|
||||
value = focus_spell.base[i];
|
||||
|
||||
break;
|
||||
}
|
||||
case SE_FcIncreaseNumHits: {
|
||||
if(bottype == BotfocusIncreaseNumHits)
|
||||
if(bottype == focusIncreaseNumHits)
|
||||
value = focus_spell.base[i];
|
||||
|
||||
break;
|
||||
@ -6342,14 +6587,14 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
|
||||
|
||||
int32 Bot::CheckAggroAmount(uint16 spellid) {
|
||||
int32 AggroAmount = Mob::CheckAggroAmount(spellid, nullptr);
|
||||
int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid);
|
||||
int32 focusAggro = GetBotFocusEffect(focusSpellHateMod, spellid);
|
||||
AggroAmount = (AggroAmount * (100 + focusAggro) / 100);
|
||||
return AggroAmount;
|
||||
}
|
||||
|
||||
int32 Bot::CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 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);
|
||||
return AggroAmount;
|
||||
}
|
||||
@ -6623,7 +6868,7 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
bool Critical = false;
|
||||
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.
|
||||
if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
|
||||
value -= ((GetLevel() - 40) * 20);
|
||||
@ -6651,16 +6896,18 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
ratio += RuleI(Spells, WizCritRatio);
|
||||
if (Critical) {
|
||||
value = (value_BaseEffect * ratio / 100);
|
||||
value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100);
|
||||
value += (int(value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100) * ratio / 100);
|
||||
value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100);
|
||||
value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage2, spell_id) / 100);
|
||||
value += (int(value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100) * ratio / 100);
|
||||
if (target) {
|
||||
value += (int(value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100) * ratio / 100);
|
||||
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)
|
||||
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 * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100);
|
||||
value += (value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100);
|
||||
value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100);
|
||||
value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage2, spell_id) / 100);
|
||||
value += (value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100);
|
||||
if (target) {
|
||||
value += (value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100);
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
}
|
||||
|
||||
value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id);
|
||||
value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id);
|
||||
value -= GetBotFocusEffect(focusFcDamageAmtCrit, spell_id);
|
||||
value -= GetBotFocusEffect(focusFcDamageAmt, spell_id);
|
||||
value -= GetBotFocusEffect(focusFcDamageAmt2, spell_id);
|
||||
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5)
|
||||
value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
|
||||
|
||||
@ -6695,9 +6944,9 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
|
||||
int32 chance = 0;
|
||||
int8 modifier = 1;
|
||||
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 += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id) / 100);
|
||||
value += int(value_BaseEffect*GetBotFocusEffect(focusImprovedHeal, spell_id) / 100);
|
||||
if(spells[spell_id].buffduration < 1) {
|
||||
chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance);
|
||||
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 += (GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier);
|
||||
value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id);
|
||||
value += (GetBotFocusEffect(focusFcHealAmtCrit, spell_id) * modifier);
|
||||
value += GetBotFocusEffect(focusFcHealAmt, spell_id);
|
||||
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
|
||||
|
||||
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 cast_reducer = 0;
|
||||
cast_reducer += GetBotFocusEffect(BotfocusSpellHaste, spell_id);
|
||||
cast_reducer += GetBotFocusEffect(focusSpellHaste, spell_id);
|
||||
uint8 botlevel = GetLevel();
|
||||
uint8 botclass = GetClass();
|
||||
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)
|
||||
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 extrange = 100;
|
||||
extrange += GetBotFocusEffect(BotfocusRange, spell_id);
|
||||
extrange += GetBotFocusEffect(focusRange, spell_id);
|
||||
return ((range * extrange) / 100);
|
||||
}
|
||||
|
||||
int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) {
|
||||
int increase = 100;
|
||||
increase += GetBotFocusEffect(BotfocusSpellDuration, spell_id);
|
||||
int tic_inc = 0; tic_inc = GetBotFocusEffect(BotfocusSpellDurByTic, spell_id);
|
||||
increase += GetBotFocusEffect(focusSpellDuration, spell_id);
|
||||
int tic_inc = 0; tic_inc = GetBotFocusEffect(focusSpellDurByTic, spell_id);
|
||||
|
||||
if(IsBeneficialSpell(spell_id)) {
|
||||
switch (GetAA(aaSpellCastingReinforcement)) {
|
||||
@ -8703,15 +8952,21 @@ void Bot::CalcBotStats(bool showtext) {
|
||||
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, "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.");
|
||||
}
|
||||
}*/
|
||||
|
||||
if(GetBotOwner()->GetLevel() != 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();
|
||||
|
||||
if(showtext) {
|
||||
@ -8989,6 +9244,20 @@ Bot* EntityList::GetBotByBotName(std::string botName) {
|
||||
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) {
|
||||
if(newBot) {
|
||||
newBot->SetID(GetFreeID());
|
||||
@ -9151,6 +9420,39 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) {
|
||||
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 needHealed = 0;
|
||||
Group *g = nullptr;
|
||||
|
||||
42
zone/bot.h
42
zone/bot.h
@ -99,40 +99,6 @@ class Bot : public NPC {
|
||||
friend class Mob;
|
||||
public:
|
||||
// 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
|
||||
BotTradeClientNormal,
|
||||
BotTradeClientNoDropNoTrade
|
||||
@ -383,6 +349,7 @@ public:
|
||||
void EquipBot(std::string* errorMessage);
|
||||
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]); }
|
||||
const EQEmu::InventoryProfile& GetBotInv() const { return m_inv; }
|
||||
|
||||
// Static Class Methods
|
||||
//static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped
|
||||
@ -635,9 +602,9 @@ protected:
|
||||
virtual void PetAIProcess();
|
||||
virtual void BotMeditate(bool isSitting);
|
||||
virtual bool CheckBotDoubleAttack(bool Triple = false);
|
||||
virtual int32 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id);
|
||||
virtual int32 CalcBotFocusEffect(BotfocusType 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 GetBotFocusEffect(focusType bottype, uint16 spell_id);
|
||||
virtual int32 CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false);
|
||||
virtual int32 CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 spell_id);
|
||||
virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client);
|
||||
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_alt_combat_hate_timer;
|
||||
Timer m_auto_defend_timer;
|
||||
//Timer m_combat_jitter_timer;
|
||||
//bool m_combat_jitter_flag;
|
||||
bool m_guard_flag;
|
||||
|
||||
@ -1321,6 +1321,8 @@ int bot_command_init(void)
|
||||
if (
|
||||
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("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("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) ||
|
||||
@ -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("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("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("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) ||
|
||||
@ -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("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("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("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) ||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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: %s chain", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
const int ab_mask = ActionableBots::ABM_Type2;
|
||||
@ -3060,8 +3224,15 @@ void bot_command_follow(Client *c, const Seperator *sep)
|
||||
int name_arg = 2;
|
||||
Mob* target_mob = nullptr;
|
||||
|
||||
std::string reset_arg = sep->arg[1];
|
||||
if (!reset_arg.compare("reset")) {
|
||||
std::string optional_arg = sep->arg[1];
|
||||
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;
|
||||
ab_arg = 2;
|
||||
name_arg = 3;
|
||||
@ -3088,16 +3259,21 @@ void bot_command_follow(Client *c, const Seperator *sep)
|
||||
bot_iter->SetFollowID(c->GetID());
|
||||
else
|
||||
bot_iter->SetFollowID(my_group->GetLeader()->GetID());
|
||||
|
||||
bot_iter->SetManualFollow(false);
|
||||
}
|
||||
else {
|
||||
if (bot_iter == target_mob)
|
||||
bot_iter->SetFollowID(c->GetID());
|
||||
else
|
||||
bot_iter->SetFollowID(target_mob->GetID());
|
||||
|
||||
bot_iter->SetManualFollow(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bot_iter->SetFollowID(0);
|
||||
bot_iter->SetManualFollow(false);
|
||||
}
|
||||
if (!bot_iter->GetPet())
|
||||
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);
|
||||
++bot_commands_shown;
|
||||
}
|
||||
if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, sep->msg, 0);
|
||||
if (i >= 1) {
|
||||
bot_commands_shown += i;
|
||||
}
|
||||
}
|
||||
c->Message(m_message, "%d bot command%s listed.", bot_commands_shown, bot_commands_shown != 1 ? "s" : "");
|
||||
c->Message(m_note, "type %ccommand [help | usage] for more information", BOT_COMMAND_CHAR);
|
||||
}
|
||||
@ -3403,6 +3585,105 @@ void bot_command_invisibility(Client *c, const Seperator *sep)
|
||||
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)
|
||||
{
|
||||
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 \"#888888\">(toggles)</td>"
|
||||
"</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>"
|
||||
"<td><c \"#CCCCCC\">current</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.");
|
||||
}
|
||||
}
|
||||
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")) {
|
||||
|
||||
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\">altcombat</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>",
|
||||
(c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"),
|
||||
(c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"),
|
||||
(c->GetBotOption(Client::booSpawnMessageSay) ? "say" : (c->GetBotOption(Client::booSpawnMessageTell) ? "tell" : "silent")),
|
||||
(c->GetBotOption(Client::booSpawnMessageClassSpecific) ? "class" : "default"),
|
||||
(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());
|
||||
@ -3961,6 +4270,12 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
if (target_mob->IsNPC() && target_mob->GetHateList().size()) {
|
||||
|
||||
c->Message(m_fail, "Your current target is already engaged!");
|
||||
return;
|
||||
}
|
||||
|
||||
Bot* bot_puller = nullptr;
|
||||
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);
|
||||
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)),
|
||||
bots_iter.Level,
|
||||
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];
|
||||
bot_surname = (bot_surname == "-remove") ? "" : bot_surname;
|
||||
std::replace(bot_surname.begin(), bot_surname.end(), '_', ' ');
|
||||
|
||||
my_bot->SetSurname(bot_surname);
|
||||
if (!database.botdb.SaveBot(my_bot)) {
|
||||
c->Message(Chat::Red, BotDatabase::fail::SaveBot());
|
||||
return;
|
||||
}
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@ -5575,13 +5879,12 @@ void bot_subcommand_bot_title(Client *c, const Seperator *sep)
|
||||
std::string bot_title = sep->arg[1];
|
||||
bot_title = (bot_title == "-remove") ? "" : bot_title;
|
||||
std::replace(bot_title.begin(), bot_title.end(), '_', ' ');
|
||||
|
||||
my_bot->SetTitle(bot_title);
|
||||
if (!database.botdb.SaveBot(my_bot)) {
|
||||
c->Message(Chat::Red, BotDatabase::fail::SaveBot());
|
||||
return;
|
||||
}
|
||||
else {
|
||||
my_bot->CastToClient()->SetAATitle(my_bot->GetTitle().c_str());
|
||||
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];
|
||||
bot_suffix = (bot_suffix == "-remove") ? "" : bot_suffix;
|
||||
std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' ');
|
||||
|
||||
my_bot->SetSuffix(bot_suffix);
|
||||
if (!database.botdb.SaveBot(my_bot)) {
|
||||
c->Message(Chat::Red, BotDatabase::fail::SaveBot());
|
||||
return;
|
||||
}
|
||||
else {
|
||||
my_bot->CastToClient()->SetTitleSuffix(my_bot->GetSuffix().c_str());
|
||||
c->Message(Chat::Yellow, "Bot Suffix Saved.");
|
||||
}
|
||||
}
|
||||
@ -5855,7 +6157,7 @@ void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep)
|
||||
return;
|
||||
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_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 [sync] to set stop melee level to current bot level");
|
||||
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");
|
||||
return;
|
||||
}
|
||||
if (!IsCasterClass(my_bot->GetClass()) && !IsSpellFighterClass(my_bot->GetClass())) {
|
||||
c->Message(m_fail, "You must <target> a caster or spell-casting fighter class bot to use this command");
|
||||
if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) {
|
||||
c->Message(m_fail, "You must <target> a caster or hybrid class bot to use this command");
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
auto alias_iter = bot_command_aliases.find(&alias[1]);
|
||||
|
||||
@ -553,6 +553,8 @@ void bot_command_log_command(Client *c, const char *message);
|
||||
// bot commands
|
||||
void bot_command_actionable(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_bind_affinity(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_inventory(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_lull(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);
|
||||
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);
|
||||
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_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);
|
||||
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);
|
||||
|
||||
@ -2256,6 +2256,7 @@ bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool
|
||||
case Client::booSpawnMessageClassSpecific:
|
||||
case Client::booAltCombat:
|
||||
case Client::booAutoDefend:
|
||||
case Client::booBuffCounter:
|
||||
{
|
||||
query = fmt::format(
|
||||
"REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')",
|
||||
|
||||
231
zone/client.cpp
231
zone/client.cpp
@ -123,49 +123,50 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
0,
|
||||
0
|
||||
),
|
||||
hpupdate_timer(2000),
|
||||
camp_timer(29000),
|
||||
process_timer(100),
|
||||
consume_food_timer(CONSUMPTION_TIMER),
|
||||
zoneinpacket_timer(1000),
|
||||
linkdead_timer(RuleI(Zone,ClientLinkdeadMS)),
|
||||
dead_timer(2000),
|
||||
global_channel_timer(1000),
|
||||
shield_timer(500),
|
||||
fishing_timer(8000),
|
||||
endupkeep_timer(1000),
|
||||
forget_timer(0),
|
||||
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
|
||||
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000),
|
||||
client_zone_wide_full_position_update_timer(5 * 60 * 1000),
|
||||
tribute_timer(Tribute_duration),
|
||||
proximity_timer(ClientProximity_interval),
|
||||
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
|
||||
charm_update_timer(6000),
|
||||
rest_timer(1),
|
||||
charm_class_attacks_timer(3000),
|
||||
charm_cast_timer(3500),
|
||||
qglobal_purge_timer(30000),
|
||||
TrackingTimer(2000),
|
||||
RespawnFromHoverTimer(0),
|
||||
merc_timer(RuleI(Mercs, UpkeepIntervalMS)),
|
||||
ItemTickTimer(10000),
|
||||
ItemQuestTimer(500),
|
||||
anon_toggle_timer(250),
|
||||
afk_toggle_timer(250),
|
||||
helm_toggle_timer(250),
|
||||
aggro_meter_timer(AGGRO_METER_UPDATE_MS),
|
||||
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
|
||||
hpupdate_timer(2000),
|
||||
camp_timer(29000),
|
||||
process_timer(100),
|
||||
consume_food_timer(CONSUMPTION_TIMER),
|
||||
zoneinpacket_timer(1000),
|
||||
linkdead_timer(RuleI(Zone,ClientLinkdeadMS)),
|
||||
dead_timer(2000),
|
||||
global_channel_timer(1000),
|
||||
shield_timer(500),
|
||||
fishing_timer(8000),
|
||||
endupkeep_timer(1000),
|
||||
forget_timer(0),
|
||||
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
|
||||
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000),
|
||||
client_zone_wide_full_position_update_timer(5 * 60 * 1000),
|
||||
tribute_timer(Tribute_duration),
|
||||
proximity_timer(ClientProximity_interval),
|
||||
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
|
||||
charm_update_timer(6000),
|
||||
rest_timer(1),
|
||||
charm_class_attacks_timer(3000),
|
||||
charm_cast_timer(3500),
|
||||
qglobal_purge_timer(30000),
|
||||
TrackingTimer(2000),
|
||||
RespawnFromHoverTimer(0),
|
||||
merc_timer(RuleI(Mercs, UpkeepIntervalMS)),
|
||||
ItemTickTimer(10000),
|
||||
ItemQuestTimer(500),
|
||||
anon_toggle_timer(250),
|
||||
afk_toggle_timer(250),
|
||||
helm_toggle_timer(250),
|
||||
aggro_meter_timer(AGGRO_METER_UPDATE_MS),
|
||||
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
|
||||
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
|
||||
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
|
||||
last_region_type(RegionTypeUnsupported),
|
||||
m_dirtyautohaters(false),
|
||||
npc_close_scan_timer(6000),
|
||||
hp_self_update_throttle_timer(300),
|
||||
hp_other_update_throttle_timer(500),
|
||||
position_update_timer(10000),
|
||||
tmSitting(0)
|
||||
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
|
||||
last_region_type(RegionTypeUnsupported),
|
||||
m_dirtyautohaters(false),
|
||||
mob_close_scan_timer(6000),
|
||||
hp_self_update_throttle_timer(300),
|
||||
hp_other_update_throttle_timer(500),
|
||||
position_update_timer(10000),
|
||||
consent_throttle_timer(2000),
|
||||
tmSitting(0)
|
||||
{
|
||||
|
||||
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[booAltCombat] = RuleB(Bots, AllowOwnerOptionAltCombat);
|
||||
bot_owner_options[booAutoDefend] = RuleB(Bots, AllowOwnerOptionAutoDefend);
|
||||
bot_owner_options[booBuffCounter] = false;
|
||||
|
||||
SetBotPulling(false);
|
||||
SetBotPrecombat(false);
|
||||
@ -1105,40 +1107,56 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
case ChatChannel_Say: { /* Say */
|
||||
if(message[0] == COMMAND_CHAR) {
|
||||
if(command_dispatch(this, message) == -2) {
|
||||
if(parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
|
||||
if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Command '%s' not recognized.", message);
|
||||
}
|
||||
} else {
|
||||
if(!RuleB(Chat, SuppressCommandErrors))
|
||||
}
|
||||
else if (parse->PlayerHasQuestSub(EVENT_SAY)) {
|
||||
int i = parse->EventPlayer(EVENT_SAY, this, message, 0);
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (EQEmu::ProfanityManager::IsCensorshipActive())
|
||||
EQEmu::ProfanityManager::RedactMessage(message);
|
||||
|
||||
#ifdef BOTS
|
||||
if (message[0] == BOT_COMMAND_CHAR) {
|
||||
if (bot_command_dispatch(this, message) == -2) {
|
||||
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_BOT_COMMAND, this, message, 0);
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Bot command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
else if (parse->PlayerHasQuestSub(EVENT_SAY)) {
|
||||
int i = parse->EventPlayer(EVENT_SAY, this, message, 0);
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Bot command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!RuleB(Chat, SuppressCommandErrors))
|
||||
if (!RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(Chat::Red, "Bot command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (EQEmu::ProfanityManager::IsCensorshipActive()) {
|
||||
EQEmu::ProfanityManager::RedactMessage(message);
|
||||
}
|
||||
|
||||
Mob* sender = this;
|
||||
if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft))
|
||||
sender = GetPet();
|
||||
@ -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)
|
||||
{
|
||||
if(!target || !IsValidSpell(spell_id) || this->GetID() == target->GetID())
|
||||
@ -6742,11 +6806,22 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
GetRawACNoShield(shield_ac);
|
||||
|
||||
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",
|
||||
"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",
|
||||
"Meditate","Mend","Offense","Parry","Pick Lock","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"
|
||||
"1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration",
|
||||
"Alteration","Apply Poison","Archery","Backstab","Bind Wound",
|
||||
"Bash","Block","Brass Instruments","Channeling","Conjuration",
|
||||
"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","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 = "";
|
||||
@ -8505,13 +8580,13 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold,
|
||||
memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct));
|
||||
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->copper = copper;
|
||||
qr->silver = silver;
|
||||
qr->gold = gold;
|
||||
qr->platinum = platinum;
|
||||
qr->item_id = itemid;
|
||||
qr->item_id[0] = itemid;
|
||||
qr->exp_reward = exp;
|
||||
|
||||
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 (target->IsNPC())
|
||||
if (target && target->IsNPC())
|
||||
{
|
||||
int32 nfl_id = target->CastToNPC()->GetNPCFactionID();
|
||||
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);
|
||||
}
|
||||
|
||||
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(){
|
||||
if (!this || !this->IsClient() || !this->current_hp || !this->max_hp)
|
||||
return;
|
||||
|
||||
@ -226,7 +226,6 @@ public:
|
||||
Client(EQStreamInterface * ieqs);
|
||||
~Client();
|
||||
|
||||
std::unordered_map<Mob *, float> close_mobs;
|
||||
bool is_client_moving;
|
||||
|
||||
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]); }
|
||||
|
||||
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 SetHorseId(uint16 horseid_in);
|
||||
@ -958,7 +960,6 @@ public:
|
||||
|
||||
void EnteringMessages(Client* client);
|
||||
void SendRules(Client* client);
|
||||
std::list<std::string> consent_list;
|
||||
|
||||
const bool GetGMSpeed() const { return (gmspeed > 0); }
|
||||
bool CanUseReport;
|
||||
@ -1138,6 +1139,7 @@ public:
|
||||
inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); }
|
||||
void DragCorpses();
|
||||
inline void ClearDraggedCorpses() { DraggedCorpses.clear(); }
|
||||
void ConsentCorpses(std::string consent_name, bool deny = false);
|
||||
void SendAltCurrencies();
|
||||
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
|
||||
void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
|
||||
@ -1283,6 +1285,7 @@ public:
|
||||
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, const QuestReward_Struct &reward, bool faction); // TODO: Fix faction processing
|
||||
|
||||
void ResetHPUpdateTimer() { hpupdate_timer.Start(); }
|
||||
|
||||
@ -1524,10 +1527,11 @@ private:
|
||||
Timer afk_toggle_timer;
|
||||
Timer helm_toggle_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_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 consent_throttle_timer;
|
||||
|
||||
glm::vec3 m_Proximity;
|
||||
glm::vec4 last_position_before_bulk_update;
|
||||
@ -1640,6 +1644,7 @@ public:
|
||||
booSpawnMessageClassSpecific,
|
||||
booAltCombat,
|
||||
booAutoDefend,
|
||||
booBuffCounter,
|
||||
_booCount
|
||||
};
|
||||
|
||||
|
||||
@ -336,6 +336,10 @@ int32 Client::CalcMaxHP()
|
||||
current_hp = curHP_cap;
|
||||
}
|
||||
}
|
||||
|
||||
// hack fix for client health not reflecting server value
|
||||
last_max_hp = 0;
|
||||
|
||||
return max_hp;
|
||||
}
|
||||
|
||||
@ -610,14 +614,13 @@ int32 Client::CalcBaseMana()
|
||||
case 'I':
|
||||
WisInt = GetINT();
|
||||
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
|
||||
ConvertedWisInt = WisInt;
|
||||
int over200 = WisInt;
|
||||
if (WisInt > 100) {
|
||||
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
|
||||
if (WisInt > 201) {
|
||||
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
|
||||
if (WisInt > 200) {
|
||||
over200 = (WisInt - 200) / -2 + WisInt;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ConvertedWisInt = WisInt;
|
||||
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
|
||||
}
|
||||
auto base_data = database.GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data) {
|
||||
@ -643,14 +646,13 @@ int32 Client::CalcBaseMana()
|
||||
case 'W':
|
||||
WisInt = GetWIS();
|
||||
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
|
||||
ConvertedWisInt = WisInt;
|
||||
int over200 = WisInt;
|
||||
if (WisInt > 100) {
|
||||
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
|
||||
if (WisInt > 201) {
|
||||
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
|
||||
if (WisInt > 200) {
|
||||
over200 = (WisInt - 200) / -2 + WisInt;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ConvertedWisInt = WisInt;
|
||||
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
|
||||
}
|
||||
auto base_data = database.GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data) {
|
||||
|
||||
@ -905,6 +905,8 @@ void Client::CompleteConnect()
|
||||
entity_list.RefreshClientXTargets(this);
|
||||
|
||||
worldserver.RequestTellQueue(GetName());
|
||||
|
||||
entity_list.ScanCloseMobs(close_mobs, this);
|
||||
}
|
||||
|
||||
// connecting opcode handlers
|
||||
@ -4641,43 +4643,16 @@ void Client::Handle_OP_Consent(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size<64) {
|
||||
Consent_Struct* c = (Consent_Struct*)app->pBuffer;
|
||||
if (strcmp(c->name, GetName()) != 0) {
|
||||
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);
|
||||
}
|
||||
ConsentCorpses(c->name, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size<64) {
|
||||
Consent_Struct* c = (Consent_Struct*)app->pBuffer;
|
||||
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 = 0;
|
||||
scs->zone_id = zone->GetZoneID();
|
||||
scs->instance_id = zone->GetInstanceID();
|
||||
//consent_list.remove(scs->grantname);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
ConsentCorpses(c->name, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
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 (silentsaylink) {
|
||||
parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0);
|
||||
parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0);
|
||||
|
||||
if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0);
|
||||
}
|
||||
#ifdef BOTS
|
||||
else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message(Chat::LightGray, "You say, '%s'", response.c_str());
|
||||
@ -8296,7 +8282,17 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
|
||||
}
|
||||
else {
|
||||
if (silentsaylink) {
|
||||
parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0);
|
||||
if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0);
|
||||
}
|
||||
#ifdef BOTS
|
||||
else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message(Chat::LightGray, "You say, '%s'", response.c_str());
|
||||
@ -11107,6 +11103,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
||||
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)) {
|
||||
Message(Chat::Red, "You can only invite an ungrouped player or group leader to join your raid.");
|
||||
break;
|
||||
@ -13292,6 +13293,21 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
||||
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 {
|
||||
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;
|
||||
@ -14139,16 +14155,16 @@ void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app)
|
||||
#else
|
||||
else if (tradee && (tradee->IsNPC() || tradee->IsBot())) {
|
||||
#endif
|
||||
//npcs always accept
|
||||
trade->Start(msg->to_mob_id);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct));
|
||||
TradeRequest_Struct* acc = (TradeRequest_Struct*)outapp->pBuffer;
|
||||
acc->from_mob_id = msg->to_mob_id;
|
||||
acc->to_mob_id = msg->from_mob_id;
|
||||
FastQueuePacket(&outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
if (!tradee->IsEngaged()) {
|
||||
trade->Start(msg->to_mob_id);
|
||||
EQApplicationPacket *outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct));
|
||||
TradeRequest_Struct *acc = (TradeRequest_Struct *) outapp->pBuffer;
|
||||
acc->from_mob_id = msg->to_mob_id;
|
||||
acc->to_mob_id = msg->from_mob_id;
|
||||
FastQueuePacket(&outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -251,26 +251,12 @@ bool Client::Process() {
|
||||
}
|
||||
}
|
||||
|
||||
/* Build a close range list of NPC's */
|
||||
if (npc_close_scan_timer.Check()) {
|
||||
close_mobs.clear();
|
||||
//Force spawn updates when traveled far
|
||||
bool force_spawn_updates = false;
|
||||
float client_update_range = (RuleI(Range, ClientForceSpawnUpdateRange) * RuleI(Range, ClientForceSpawnUpdateRange));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Scan close range mobs
|
||||
* Used in aggro checks
|
||||
*/
|
||||
if (mob_close_scan_timer.Check()) {
|
||||
entity_list.ScanCloseMobs(close_mobs, this);
|
||||
}
|
||||
|
||||
bool may_use_attacks = false;
|
||||
@ -593,7 +579,7 @@ bool Client::Process() {
|
||||
if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) {
|
||||
int npc_scan_count = 0;
|
||||
for (auto & close_mob : close_mobs) {
|
||||
Mob *mob = close_mob.first;
|
||||
Mob *mob = close_mob.second;
|
||||
|
||||
if (!mob)
|
||||
continue;
|
||||
|
||||
119
zone/command.cpp
119
zone/command.cpp
@ -295,6 +295,7 @@ int command_init(void)
|
||||
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("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("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) ||
|
||||
@ -782,6 +783,12 @@ void command_help(Client *c, const Seperator *sep)
|
||||
commands_shown++;
|
||||
c->Message(Chat::White, " %c%s %s", COMMAND_CHAR, cur->first.c_str(), cur->second->desc == nullptr?"":cur->second->desc);
|
||||
}
|
||||
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, c, sep->msg, 0);
|
||||
if (i >= 1) {
|
||||
commands_shown += i;
|
||||
}
|
||||
}
|
||||
c->Message(Chat::White, "%d command%s listed.", commands_shown, commands_shown!=1?"s":"");
|
||||
|
||||
}
|
||||
@ -2166,25 +2173,6 @@ void command_spoff(Client *c, const Seperator *sep)
|
||||
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)
|
||||
{
|
||||
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 {
|
||||
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 show - displays wp nodes as boxes");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2554,7 +2543,7 @@ void command_size(Client *c, const Seperator *sep)
|
||||
else if (!target)
|
||||
c->Message(Chat::White,"Error: this command requires a target");
|
||||
else {
|
||||
uint16 Race = target->GetRace();
|
||||
uint16 Race = target->GetModel();
|
||||
uint8 Gender = target->GetGender();
|
||||
uint8 Texture = 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)
|
||||
{
|
||||
if (c->GetTarget()==0)
|
||||
@ -4176,10 +4240,15 @@ void command_corpsefix(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));
|
||||
ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer;
|
||||
RW->Option = ((atoi(sep->arg[1]) == 1) ? 1 : 0);
|
||||
RW->Option = world_repop;
|
||||
worldserver.SendPacket(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]");
|
||||
} else {
|
||||
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 {
|
||||
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 (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, c, bot_message, 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, bot_message, 0);
|
||||
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
c->Message(Chat::Red, "Bot command '%s' not recognized.", bot_message.c_str());
|
||||
}
|
||||
|
||||
@ -150,7 +150,6 @@ void command_ipc(Client *c, const Seperator *sep);
|
||||
void command_iplookup(Client *c, const Seperator *sep);
|
||||
void command_iteminfo(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_killallnpcs(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_npctype_cache(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_nukeitem(Client *c, const Seperator *sep);
|
||||
void command_numauths(Client *c, const Seperator *sep);
|
||||
|
||||
@ -647,6 +647,19 @@ enum {
|
||||
SKILLUP_FAILURE = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
GridCircular,
|
||||
GridRandom10,
|
||||
GridRandom,
|
||||
GridPatrol,
|
||||
GridOneWayRepop,
|
||||
GridRand5LoS,
|
||||
GridOneWayDepop,
|
||||
GridCenterPoint,
|
||||
GridRandomCenterPoint,
|
||||
GridRandomPath
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
petFamiliar, //only listens to /pet get lost
|
||||
petAnimation, //does not listen to any commands
|
||||
|
||||
@ -73,7 +73,7 @@ void Corpse::SendLootReqErrorPacket(Client* client, LootResponse response) {
|
||||
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);
|
||||
auto buffer =
|
||||
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->IsRezzed(rezzed);
|
||||
pc->become_npc = false;
|
||||
pc->consented_guild_id = guild_consent_id;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
rez_experience = in_rezexp;
|
||||
can_corpse_be_rezzed = true;
|
||||
@ -611,11 +624,11 @@ bool Corpse::Save() {
|
||||
|
||||
/* Create New Corpse*/
|
||||
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 */
|
||||
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);
|
||||
@ -647,6 +660,25 @@ void Corpse::DepopPlayerCorpse() {
|
||||
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() {
|
||||
return itemlist.size();
|
||||
}
|
||||
@ -1434,29 +1466,50 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) {
|
||||
is_corpse_changed = true;
|
||||
}
|
||||
else {
|
||||
client->Message(Chat::White, "Corpse is too far away.");
|
||||
client->MessageString(Chat::Red, CORPSE_TOO_FAR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool consented = false;
|
||||
std::list<std::string>::iterator itr;
|
||||
for(itr = client->consent_list.begin(); itr != client->consent_list.end(); ++itr) {
|
||||
if(strcmp(this->GetOwnerName(), itr->c_str()) == 0) {
|
||||
if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) {
|
||||
GMMove(client->GetX(), client->GetY(), client->GetZ());
|
||||
is_corpse_changed = true;
|
||||
}
|
||||
else {
|
||||
client->Message(Chat::White, "Corpse is too far away.");
|
||||
return false;
|
||||
}
|
||||
for (const auto& consented_player_name : consented_player_names) {
|
||||
if (strcasecmp(client->GetName(), consented_player_name.c_str()) == 0) {
|
||||
consented = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!consented && consented_guild_id && consented_guild_id != GUILD_NONE) {
|
||||
if (client->GuildID() == consented_guild_id) {
|
||||
consented = true;
|
||||
}
|
||||
}
|
||||
if(!consented) {
|
||||
client->Message(Chat::White, "You do not have permission to move this corpse.");
|
||||
if (!consented && consented_group_id) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
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 */
|
||||
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 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); }
|
||||
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 Bury();
|
||||
@ -142,6 +147,9 @@ private:
|
||||
int32 player_kill_item; /* Determines if Player Kill Item */
|
||||
uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */
|
||||
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 */
|
||||
uint32 copper;
|
||||
uint32 silver;
|
||||
@ -160,6 +168,7 @@ private:
|
||||
Timer corpse_graveyard_timer;
|
||||
Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */
|
||||
EQEmu::TintProfile item_tint;
|
||||
std::vector<std::string> consented_player_names;
|
||||
|
||||
LootRequestType loot_request_type;
|
||||
};
|
||||
|
||||
491
zone/effects.cpp
491
zone/effects.cpp
@ -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();
|
||||
while (it != npc_list.end()) {
|
||||
NPC *them = it->second;
|
||||
float zdiff = taunter->GetZ() - them->GetZ();
|
||||
if (zdiff < 0)
|
||||
zdiff *= -1;
|
||||
if (zdiff < 10
|
||||
&& taunter->IsAttackAllowed(them)
|
||||
&& DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range) {
|
||||
float range_squared = range * range;
|
||||
|
||||
for (auto &it : entity_list.GetCloseMobList(taunter, range)) {
|
||||
Mob *them = it.second;
|
||||
|
||||
if (!them->IsNPC()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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)) {
|
||||
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.
|
||||
// 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)
|
||||
/**
|
||||
* Causes caster to hit every mob within dist range of center with spell_id
|
||||
*
|
||||
* @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);
|
||||
float dist2 = dist * dist;
|
||||
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
|
||||
float dist_targ = 0;
|
||||
Mob *current_mob = nullptr;
|
||||
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
|
||||
bool is_npc = caster_mob->IsNPC();
|
||||
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 };
|
||||
glm::vec2 max = { position.x + dist, position.y + dist };
|
||||
/**
|
||||
* If using Old Rain Targets - there is no max target limitation
|
||||
*/
|
||||
if (RuleB(Spells, OldRainTargets)) {
|
||||
max_targets = nullptr;
|
||||
}
|
||||
|
||||
bool bad = IsDetrimentalSpell(spell_id);
|
||||
bool isnpc = caster->IsNPC();
|
||||
|
||||
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
|
||||
/**
|
||||
* Max AOE targets
|
||||
*/
|
||||
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;
|
||||
else if (spells[spell_id].aemaxtargets)
|
||||
}
|
||||
else if (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;
|
||||
}
|
||||
|
||||
int iCounter = 0;
|
||||
int target_hit_counter = 0;
|
||||
float distance_to_target = 0;
|
||||
|
||||
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
|
||||
curmob = it->second;
|
||||
// test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized
|
||||
if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading())
|
||||
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;
|
||||
LogAoeCast(
|
||||
"Close scan distance [{}] cast distance [{}]",
|
||||
RuleI(Range, MobCloseScanDistance),
|
||||
distance
|
||||
);
|
||||
|
||||
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;
|
||||
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;
|
||||
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
|
||||
//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;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
else {
|
||||
//only affect mobs we would assist.
|
||||
if (!(f <= FACTION_AMIABLE))
|
||||
if (!(faction_value <= FACTION_AMIABLE)) {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
} 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
|
||||
// check may still be needed. Any changes here should also reflect in BardAEPulse()
|
||||
if (caster->IsAttackAllowed(curmob, true))
|
||||
}
|
||||
}
|
||||
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
|
||||
* check may still be needed. Any changes here should also reflect in BardAEPulse()
|
||||
*/
|
||||
if (caster_mob->IsAttackAllowed(current_mob, true)) {
|
||||
continue;
|
||||
if (caster->CheckAggro(curmob))
|
||||
}
|
||||
if (caster_mob->CheckAggro(current_mob)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ);
|
||||
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
|
||||
|
||||
if (max_targets_allowed) { // if we have a limit, increment count
|
||||
iCounter++;
|
||||
if (iCounter >= max_targets_allowed) // we done
|
||||
/**
|
||||
* Increment hit count if max targets
|
||||
*/
|
||||
if (max_targets_allowed) {
|
||||
target_hit_counter++;
|
||||
if (target_hit_counter >= max_targets_allowed) {
|
||||
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)
|
||||
*max_targets = *max_targets - iCounter;
|
||||
LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName());
|
||||
|
||||
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);
|
||||
float dist2 = dist * dist;
|
||||
for (auto &it : entity_list.GetCloseMobList(caster, distance)) {
|
||||
current_mob = it.second;
|
||||
|
||||
bool bad = IsDetrimentalSpell(spell_id);
|
||||
|
||||
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
|
||||
curmob = it->second;
|
||||
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
|
||||
/**
|
||||
* Skip center
|
||||
*/
|
||||
if (current_mob == center) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Only npcs mgb should hit are client pets...
|
||||
if (curmob->IsNPC()) {
|
||||
Mob *owner = curmob->GetOwner();
|
||||
/**
|
||||
* Skip self
|
||||
*/
|
||||
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->IsClient()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bad) {
|
||||
if (is_detrimental_spell) {
|
||||
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.
|
||||
// NPC spells will only affect other NPCs with compatible faction
|
||||
void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster)
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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);
|
||||
float dist2 = dist * dist;
|
||||
for (auto &it : entity_list.GetCloseMobList(caster, distance)) {
|
||||
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) {
|
||||
curmob = it->second;
|
||||
if (curmob == center) //do not affect center
|
||||
if (current_mob == caster && !affect_caster) {
|
||||
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;
|
||||
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);
|
||||
if (bad) {
|
||||
}
|
||||
|
||||
/**
|
||||
* check npc->npc casting
|
||||
*/
|
||||
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
|
||||
//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;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
else {
|
||||
//only affect mobs we would assist.
|
||||
if (!(f <= FACTION_AMIABLE))
|
||||
if (!(faction <= FACTION_AMIABLE)) {
|
||||
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;
|
||||
} 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.
|
||||
if (caster->IsAttackAllowed(curmob, true))
|
||||
if (caster->IsAttackAllowed(current_mob, true)) {
|
||||
continue;
|
||||
if (caster->CheckAggro(curmob))
|
||||
}
|
||||
if (caster->CheckAggro(current_mob)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//if we get here... cast the spell.
|
||||
curmob->BardPulse(spell_id, caster);
|
||||
current_mob->BardPulse(spell_id, caster);
|
||||
}
|
||||
if (caster->IsClient())
|
||||
if (caster->IsClient()) {
|
||||
caster->CastToClient()->CheckSongSkillIncrease(spell_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Rampage and stuff for clients. Normal and Duration rampages
|
||||
//NPCs handle it differently in Mob::Rampage
|
||||
void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) {
|
||||
//Dook- Will need tweaking, currently no pets or players or horses
|
||||
Mob *curmob = nullptr;
|
||||
/**
|
||||
* Rampage - Normal and Duration rampages
|
||||
* NPCs handle it differently in Mob::Rampage
|
||||
*
|
||||
* @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) {
|
||||
curmob = it->second;
|
||||
if (curmob->IsNPC()
|
||||
&& curmob != attacker //this is not needed unless NPCs can use this
|
||||
&&(attacker->IsAttackAllowed(curmob))
|
||||
&& curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */
|
||||
&& (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2)
|
||||
) {
|
||||
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER)
|
||||
attacker->Attack(curmob, Hand, false, false, IsFromSpell);
|
||||
else
|
||||
attacker->CastToClient()->DoAttackRounds(curmob, Hand, IsFromSpell);
|
||||
hit++;
|
||||
if (count != 0 && hit >= count)
|
||||
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) {
|
||||
attacker->Attack(current_mob, Hand, false, false, is_from_spell);
|
||||
}
|
||||
else {
|
||||
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
|
||||
}
|
||||
|
||||
hit_count++;
|
||||
if (count != 0 && hit_count >= count) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1025
zone/embparser.cpp
1025
zone/embparser.cpp
File diff suppressed because it is too large
Load Diff
@ -1812,6 +1812,43 @@ XS(XS__summonallplayercorpses) {
|
||||
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) {
|
||||
dXSARGS;
|
||||
@ -3907,6 +3944,8 @@ EXTERN_C XS(boot_quest) {
|
||||
newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file);
|
||||
newXS(strcpy(buf, "getlevel"), XS__getlevel, 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, "givecash"), XS__givecash, file);
|
||||
newXS(strcpy(buf, "gmmove"), XS__gmmove, file);
|
||||
|
||||
@ -137,10 +137,10 @@ void Embperl::DoInit() {
|
||||
try {
|
||||
init_eval_file();
|
||||
}
|
||||
catch(const char *err)
|
||||
catch(std::string e)
|
||||
{
|
||||
//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";
|
||||
}
|
||||
|
||||
@ -177,9 +177,9 @@ void Embperl::DoInit() {
|
||||
perl_command = "main::eval_file('plugin', '" + Config->PluginPlFile + "');";
|
||||
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
|
||||
{
|
||||
@ -195,9 +195,9 @@ void Embperl::DoInit() {
|
||||
"}";
|
||||
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
|
||||
in_use = false;
|
||||
@ -237,7 +237,7 @@ void Embperl::init_eval_file(void)
|
||||
{
|
||||
eval_pv(
|
||||
"our %Cache;"
|
||||
"no warnings;"
|
||||
"no warnings 'all';"
|
||||
"use Symbol qw(delete_package);"
|
||||
"sub eval_file {"
|
||||
"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: ";
|
||||
errmsg += SvPVX(ERRSV);
|
||||
throw errmsg.c_str();
|
||||
throw errmsg;
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
|
||||
@ -92,35 +92,43 @@ XS(XS_EQEmuIO_PRINT)
|
||||
// Perl_croak(aTHX_ "Usage: EQEmuIO::PRINT(@strings)");
|
||||
|
||||
int r;
|
||||
for(r = 1; r < items; r++) {
|
||||
for (r = 1; r < items; r++) {
|
||||
char *str = SvPV_nolen(ST(r));
|
||||
char *cur = str;
|
||||
|
||||
|
||||
/* Strip newlines from log message 'str' */
|
||||
*std::remove(str, str + strlen(str), '\n') = '\0';
|
||||
|
||||
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 pos = 0;
|
||||
int len = 0;
|
||||
for(i = 0; *cur != '\0'; i++, cur++) {
|
||||
if(*cur == '\n') {
|
||||
Log(Logs::General, Logs::Quests, str);
|
||||
|
||||
for (i = 0; *cur != '\0'; i++, cur++) {
|
||||
if (*cur == '\n') {
|
||||
LogQuests("{}", str);
|
||||
len = 0;
|
||||
pos = i+1;
|
||||
} else {
|
||||
pos = i + 1;
|
||||
}
|
||||
else {
|
||||
len++;
|
||||
}
|
||||
}
|
||||
if(len > 0) {
|
||||
Log(Logs::General, Logs::Quests, str);
|
||||
if (!log_string.empty()) {
|
||||
LogQuests("{}", log_string);
|
||||
}
|
||||
}
|
||||
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
#endif //EMBPERL_IO_CAPTURE
|
||||
|
||||
|
||||
229
zone/entity.cpp
229
zone/entity.cpp
@ -62,7 +62,8 @@ extern char errorname[32];
|
||||
|
||||
Entity::Entity()
|
||||
{
|
||||
id = 0;
|
||||
id = 0;
|
||||
initial_id = 0;
|
||||
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) {
|
||||
QueueClients(sender, app, ignore_sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dist <= 0)
|
||||
dist = 600;
|
||||
float dist2 = dist * dist; //pow(dist, 2);
|
||||
if (distance <= 0) {
|
||||
distance = 600;
|
||||
}
|
||||
|
||||
auto it = client_list.begin();
|
||||
while (it != client_list.end()) {
|
||||
Client *ent = it->second;
|
||||
float distance_squared = distance * distance;
|
||||
|
||||
if ((!ignore_sender || ent != sender) && (ent != SkipThisMob)) {
|
||||
eqFilterMode filter2 = ent->GetFilter(filter);
|
||||
if(ent->Connected() &&
|
||||
(filter == FilterNone
|
||||
|| filter2 == FilterShow
|
||||
|| (filter2 == FilterShowGroupOnly && (sender == ent ||
|
||||
(ent->GetGroup() && ent->GetGroup()->IsGroupMember(sender))))
|
||||
|| (filter2 == FilterShowSelfOnly && ent == sender))
|
||||
&& (DistanceSquared(ent->GetPosition(), sender->GetPosition()) <= dist2)) {
|
||||
ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED);
|
||||
for (auto &e : GetCloseMobList(sender, distance)) {
|
||||
Mob *mob = e.second;
|
||||
|
||||
if (!mob->IsClient()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Client *client = mob->CastToClient();
|
||||
|
||||
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
|
||||
void EntityList::QueueClients(Mob *sender, const EQApplicationPacket *app,
|
||||
bool ignore_sender, bool ackreq)
|
||||
void EntityList::QueueClients(
|
||||
Mob *sender, const EQApplicationPacket *app,
|
||||
bool ignore_sender, bool ackreq
|
||||
)
|
||||
{
|
||||
auto it = client_list.begin();
|
||||
while (it != client_list.end()) {
|
||||
@ -2486,43 +2519,51 @@ void EntityList::RemoveAllEncounters()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delete_id
|
||||
* @return
|
||||
*/
|
||||
bool EntityList::RemoveMob(uint16 delete_id)
|
||||
{
|
||||
if (delete_id == 0)
|
||||
if (delete_id == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it = mob_list.find(delete_id);
|
||||
if (it != mob_list.end()) {
|
||||
|
||||
RemoveMobFromClientCloseLists(it->second);
|
||||
|
||||
if (npc_list.count(delete_id))
|
||||
if (npc_list.count(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);
|
||||
}
|
||||
safe_delete(it->second);
|
||||
if (!corpse_list.count(delete_id))
|
||||
if (!corpse_list.count(delete_id)) {
|
||||
free_ids.push(it->first);
|
||||
}
|
||||
mob_list.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is for if the ID is deleted for some reason
|
||||
/**
|
||||
* @param delete_mob
|
||||
* @return
|
||||
*/
|
||||
bool EntityList::RemoveMob(Mob *delete_mob)
|
||||
{
|
||||
if (delete_mob == 0)
|
||||
if (delete_mob == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it = mob_list.begin();
|
||||
while (it != mob_list.end()) {
|
||||
if (it->second == delete_mob) {
|
||||
RemoveMobFromClientCloseLists(it->second);
|
||||
|
||||
safe_delete(it->second);
|
||||
if (!corpse_list.count(it->first))
|
||||
if (!corpse_list.count(it->first)) {
|
||||
free_ids.push(it->first);
|
||||
}
|
||||
mob_list.erase(it);
|
||||
return true;
|
||||
}
|
||||
@ -2531,36 +2572,114 @@ bool EntityList::RemoveMob(Mob *delete_mob)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delete_id
|
||||
* @return
|
||||
*/
|
||||
bool EntityList::RemoveNPC(uint16 delete_id)
|
||||
{
|
||||
auto it = npc_list.find(delete_id);
|
||||
if (it != npc_list.end()) {
|
||||
NPC *npc = it->second;
|
||||
// make sure its proximity is removed
|
||||
RemoveProximity(delete_id);
|
||||
// remove from client close lists
|
||||
RemoveMobFromClientCloseLists(npc->CastToMob());
|
||||
// remove from the list
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntityList::RemoveMobFromClientCloseLists(Mob *mob)
|
||||
/**
|
||||
* @param mob
|
||||
* @return
|
||||
*/
|
||||
bool EntityList::RemoveMobFromCloseLists(Mob *mob)
|
||||
{
|
||||
auto it = client_list.begin();
|
||||
while (it != client_list.end()) {
|
||||
it->second->close_mobs.erase(mob);
|
||||
uint16 entity_id = mob->GetID() > 0 ? mob->GetID() : mob->GetInitialId();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
auto it = merc_list.find(delete_id);
|
||||
@ -4859,10 +4978,10 @@ void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radiu
|
||||
continue;
|
||||
}
|
||||
// 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;
|
||||
continue;
|
||||
} else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc())) {
|
||||
} else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc() || ptr->IsBot())) {
|
||||
++it;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -109,6 +109,7 @@ public:
|
||||
const Beacon *CastToBeacon() const;
|
||||
const Encounter *CastToEncounter() const;
|
||||
|
||||
inline const uint16& GetInitialId() const { return initial_id; }
|
||||
inline const uint16& GetID() const { return id; }
|
||||
inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; }
|
||||
|
||||
@ -122,10 +123,17 @@ public:
|
||||
|
||||
protected:
|
||||
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;
|
||||
private:
|
||||
uint16 id;
|
||||
uint16 initial_id;
|
||||
time_t spawn_timestamp;
|
||||
};
|
||||
|
||||
@ -284,7 +292,8 @@ public:
|
||||
bool RemoveTrap(uint16 delete_id);
|
||||
bool RemoveObject(uint16 delete_id);
|
||||
bool RemoveProximity(uint16 delete_npc_id);
|
||||
bool RemoveMobFromClientCloseLists(Mob *mob);
|
||||
bool RemoveMobFromCloseLists(Mob *mob);
|
||||
void RemoveAuraFromMobs(Mob *aura);
|
||||
void RemoveAllMobs();
|
||||
void RemoveAllClients();
|
||||
void RemoveAllNPCs();
|
||||
@ -376,7 +385,7 @@ public:
|
||||
void RemoveFromXTargets(Mob* mob);
|
||||
void RemoveFromAutoXTargets(Mob* mob);
|
||||
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 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);
|
||||
@ -388,11 +397,24 @@ public:
|
||||
void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app);
|
||||
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 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);
|
||||
void AEAttack(
|
||||
Mob *attacker,
|
||||
float distance,
|
||||
int Hand = EQEmu::invslot::slotPrimary,
|
||||
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
|
||||
Mob* GetTrapTrigger(Trap* trap);
|
||||
@ -443,11 +465,7 @@ public:
|
||||
bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count);
|
||||
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);
|
||||
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);
|
||||
Mob* GetTargetForMez(Mob* caster);
|
||||
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, Doors *> &GetDoorsList() { return door_list; }
|
||||
|
||||
std::unordered_map<uint16, Mob *> &GetCloseMobList(Mob *mob, float distance = 0);
|
||||
|
||||
void DepopAll(int NPCTypeID, bool StartSpawnTimer = true);
|
||||
|
||||
uint16 GetFreeID();
|
||||
void RefreshAutoXTargets(Client *c);
|
||||
void RefreshClientXTargets(Client *c);
|
||||
void SendAlternateAdvancementStats();
|
||||
void ScanCloseMobs(std::unordered_map<uint16, Mob *> &close_mobs, Mob *scanning_mob);
|
||||
|
||||
void GetTrapInfo(Client* client);
|
||||
bool IsTrapGroupSpawned(uint32 trap_id, uint8 group);
|
||||
void UpdateAllTraps(bool respawn, bool repopnow = false);
|
||||
void ClearTrapPointers();
|
||||
|
||||
protected:
|
||||
friend class Zone;
|
||||
void Depop(bool StartSpawnTimer = false);
|
||||
@ -553,10 +575,13 @@ private:
|
||||
Mob* GetMobByBotID(uint32 botID);
|
||||
Bot* GetBotByBotID(uint32 botID);
|
||||
Bot* GetBotByBotName(std::string botName);
|
||||
Client* GetBotOwnerByBotEntityID(uint16 entityID);
|
||||
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
|
||||
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:
|
||||
std::list<Bot*> bot_list;
|
||||
#endif
|
||||
|
||||
@ -86,6 +86,8 @@ typedef enum {
|
||||
EVENT_SPAWN_ZONE,
|
||||
EVENT_DEATH_ZONE,
|
||||
EVENT_USE_SKILL,
|
||||
EVENT_COMBINE_VALIDATE,
|
||||
EVENT_BOT_COMMAND,
|
||||
_LargestEventID
|
||||
} QuestEventID;
|
||||
|
||||
|
||||
@ -30,65 +30,80 @@ extern Zone* zone;
|
||||
#define FEAR_PATHING_DEBUG
|
||||
|
||||
//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(GetHP() == 0) {
|
||||
if (GetHP() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if were already fleeing, don't need to check more...
|
||||
if(flee_mode && currently_fleeing) {
|
||||
if (flee_mode && currently_fleeing) {
|
||||
return;
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
// Check if Flee Timer is cleared
|
||||
if(!flee_timer.Check()) {
|
||||
if (!flee_timer.Check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int hpratio = GetIntHPRatio();
|
||||
int fleeratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists
|
||||
Mob *hate_top = GetHateTop();
|
||||
int hp_ratio = GetIntHPRatio();
|
||||
int flee_ratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists
|
||||
Mob *hate_top = GetHateTop();
|
||||
|
||||
LogFlee("Mob [{}] hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio);
|
||||
|
||||
// Sanity Check for race conditions
|
||||
if(hate_top == nullptr) {
|
||||
if (hate_top == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
fleeratio = RuleI(Combat, FleeGrayHPRatio);
|
||||
} else if(fleeratio == 0) {
|
||||
fleeratio = RuleI(Combat, FleeHPRatio );
|
||||
if (GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && flee_ratio == 0 && RuleB(Combat, FleeGray) &&
|
||||
GetLevel() <= RuleI(Combat, FleeGrayMaxLevel)) {
|
||||
flee_ratio = RuleI(Combat, FleeGrayHPRatio);
|
||||
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
|
||||
if(hpratio >= fleeratio) {
|
||||
bool mob_has_low_enough_health_to_flee = hp_ratio >= flee_ratio;
|
||||
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;
|
||||
}
|
||||
|
||||
// Sanity Check this should never happen...
|
||||
if(!hate_top) {
|
||||
if (!hate_top) {
|
||||
currently_fleeing = true;
|
||||
StartFleeing();
|
||||
return;
|
||||
}
|
||||
|
||||
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(other_ratio < 20) {
|
||||
if (other_ratio < 20) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Flee Chance checking based on con.
|
||||
uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel());
|
||||
int flee_chance;
|
||||
switch(con) {
|
||||
int flee_chance;
|
||||
switch (con) {
|
||||
//these values are not 100% researched
|
||||
case CON_GRAY:
|
||||
flee_chance = 100;
|
||||
@ -107,9 +122,32 @@ void Mob::CheckFlee() {
|
||||
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;
|
||||
StartFleeing();
|
||||
}
|
||||
@ -155,7 +193,8 @@ void Mob::ProcessFlee()
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::CalculateNewFearpoint() {
|
||||
void Mob::CalculateNewFearpoint()
|
||||
{
|
||||
if (RuleB(Pathing, Fear) && zone->pathing) {
|
||||
auto Node = zone->pathing->GetRandomLocation(glm::vec3(GetX(), GetY(), GetZ()));
|
||||
if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) {
|
||||
@ -165,9 +204,7 @@ void Mob::CalculateNewFearpoint() {
|
||||
return;
|
||||
}
|
||||
|
||||
Log(Logs::Detail,
|
||||
Logs::Pathing,
|
||||
"No path found to selected node during CalculateNewFearpoint.");
|
||||
LogPathing("No path found to selected node during CalculateNewFearpoint.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -309,40 +309,42 @@ void Client::GoFish()
|
||||
|
||||
if(food_id == 0) {
|
||||
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);
|
||||
if (food_item) {
|
||||
|
||||
if (food_item->ItemType != EQEmu::item::ItemTypeFood) {
|
||||
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);
|
||||
if (food_item->ItemType != EQEmu::item::ItemTypeFood) {
|
||||
MessageString(Chat::Skills, FISHING_SUCCESS);
|
||||
}
|
||||
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);
|
||||
else {
|
||||
MessageString(Chat::Skills, FISHING_SUCCESS_FISH_NAME, food_item->Name);
|
||||
}
|
||||
|
||||
if(inst) {
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
|
||||
EQEmu::ItemInstance* inst = database.CreateItem(food_item, 1);
|
||||
if (inst != nullptr) {
|
||||
if (CheckLoreConflict(inst->GetItem()))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,13 +155,18 @@ Mob* HateList::GetDamageTopOnHateList(Mob* hater)
|
||||
return current;
|
||||
}
|
||||
|
||||
Mob* HateList::GetClosestEntOnHateList(Mob *hater) {
|
||||
Mob* HateList::GetClosestEntOnHateList(Mob *hater, bool skip_mezzed) {
|
||||
Mob* close_entity = nullptr;
|
||||
float close_distance = 99999.9f;
|
||||
float this_distance;
|
||||
|
||||
auto iterator = list.begin();
|
||||
while (iterator != list.end()) {
|
||||
if (skip_mezzed && (*iterator)->entity_on_hatelist->IsMezzed()) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
this_distance = DistanceSquaredNoZ((*iterator)->entity_on_hatelist->GetPosition(), hater->GetPosition());
|
||||
if ((*iterator)->entity_on_hatelist != nullptr && this_distance <= close_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
|
||||
// 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
|
||||
if (!zone->IsLoaded())
|
||||
@ -335,6 +340,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skip_mezzed && cur->entity_on_hatelist->IsMezzed()) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->entity_on_hatelist->Sanctuary()) {
|
||||
if (hate == -1)
|
||||
{
|
||||
@ -465,6 +475,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
|
||||
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))
|
||||
{
|
||||
top_hate = cur->entity_on_hatelist;
|
||||
@ -480,7 +495,7 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mob *HateList::GetEntWithMostHateOnList(){
|
||||
Mob *HateList::GetEntWithMostHateOnList(bool skip_mezzed){
|
||||
Mob* top = nullptr;
|
||||
int64 hate = -1;
|
||||
|
||||
@ -490,8 +505,10 @@ Mob *HateList::GetEntWithMostHateOnList(){
|
||||
struct_HateList *cur = (*iterator);
|
||||
if (cur && cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate))
|
||||
{
|
||||
top = cur->entity_on_hatelist;
|
||||
hate = cur->stored_hate_amount;
|
||||
if (!skip_mezzed || !cur->entity_on_hatelist->IsMezzed()) {
|
||||
top = cur->entity_on_hatelist;
|
||||
hate = cur->stored_hate_amount;
|
||||
}
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
@ -499,26 +516,50 @@ Mob *HateList::GetEntWithMostHateOnList(){
|
||||
}
|
||||
|
||||
|
||||
Mob *HateList::GetRandomEntOnHateList()
|
||||
Mob *HateList::GetRandomEntOnHateList(bool skip_mezzed)
|
||||
{
|
||||
int count = list.size();
|
||||
if (count == 0) //If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return NULL;
|
||||
if (count <= 0) //If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return nullptr;
|
||||
|
||||
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 NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto iterator = list.begin();
|
||||
int random = zone->random.Int(0, count - 1);
|
||||
for (int i = 0; i < random; i++)
|
||||
++iterator;
|
||||
if (skip_mezzed) {
|
||||
|
||||
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() {
|
||||
|
||||
@ -41,11 +41,11 @@ public:
|
||||
HateList();
|
||||
~HateList();
|
||||
|
||||
Mob *GetClosestEntOnHateList(Mob *hater);
|
||||
Mob *GetDamageTopOnHateList(Mob *hater);
|
||||
Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr);
|
||||
Mob *GetRandomEntOnHateList();
|
||||
Mob *GetEntWithMostHateOnList();
|
||||
Mob *GetClosestEntOnHateList(Mob *hater, bool skip_mezzed = false);
|
||||
Mob *GetDamageTopOnHateList(Mob *hater); // didn't add 'skip_mezzed' due to calls being in ::Death()
|
||||
Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr, bool skip_mezzed = false);
|
||||
Mob *GetRandomEntOnHateList(bool skip_mezzed = false);
|
||||
Mob *GetEntWithMostHateOnList(bool skip_mezzed = false);
|
||||
Mob *GetEscapingEntOnHateList(); // returns first eligble entity
|
||||
Mob *GetEscapingEntOnHateList(Mob *center, float range = 0.0f, bool first = false);
|
||||
|
||||
|
||||
@ -1385,59 +1385,87 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 copper = 0;
|
||||
uint32 silver = 0;
|
||||
uint32 gold = 0;
|
||||
uint32 platinum = 0;
|
||||
uint32 itemid = 0;
|
||||
uint32 exp = 0;
|
||||
QuestReward_Struct quest_reward;
|
||||
quest_reward.mob_id = 0;
|
||||
quest_reward.target_id = self->GetID();
|
||||
quest_reward.copper = 0;
|
||||
quest_reward.silver = 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;
|
||||
std::fill(std::begin(quest_reward.item_id), std::end(quest_reward.item_id), -1);
|
||||
|
||||
auto cur = reward["copper"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
copper = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed) {
|
||||
quest_reward.copper = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed &) {
|
||||
}
|
||||
}
|
||||
|
||||
cur = reward["silver"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
silver = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed) {
|
||||
quest_reward.silver = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed &) {
|
||||
}
|
||||
}
|
||||
|
||||
cur = reward["gold"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
gold = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed) {
|
||||
quest_reward.gold = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed &) {
|
||||
}
|
||||
}
|
||||
|
||||
cur = reward["platinum"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
platinum = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed) {
|
||||
quest_reward.platinum = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed &) {
|
||||
}
|
||||
}
|
||||
|
||||
cur = reward["itemid"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
itemid = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed) {
|
||||
quest_reward.item_id[0] = luabind::object_cast<uint32>(cur);
|
||||
} 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"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
exp = luabind::object_cast<uint32>(cur);
|
||||
} catch (luabind::cast_failed) {
|
||||
quest_reward.exp_reward = luabind::object_cast<uint32>(cur);
|
||||
} 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) {
|
||||
try {
|
||||
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() {
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
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) {
|
||||
return quest_manager.getplayerburiedcorpsecount(char_id);
|
||||
}
|
||||
@ -571,7 +579,7 @@ void lua_task_selector(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
cur_value = luabind::object_cast<int>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
}
|
||||
} else {
|
||||
count = i - 1;
|
||||
@ -601,7 +609,7 @@ void lua_enable_task(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
cur_value = luabind::object_cast<int>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
}
|
||||
} else {
|
||||
count = i - 1;
|
||||
@ -628,7 +636,7 @@ void lua_disable_task(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
cur_value = luabind::object_cast<int>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
}
|
||||
} else {
|
||||
count = i - 1;
|
||||
@ -1156,7 +1164,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
spawn2_id = luabind::object_cast<uint32>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1167,7 +1175,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
spawngroup_id = luabind::object_cast<uint32>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1178,7 +1186,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
x = luabind::object_cast<float>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1189,7 +1197,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
y = luabind::object_cast<float>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1200,7 +1208,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
z = luabind::object_cast<float>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1211,7 +1219,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
heading = luabind::object_cast<float>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1222,7 +1230,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
respawn = luabind::object_cast<uint32>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1233,7 +1241,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
variance = luabind::object_cast<uint32>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1244,7 +1252,7 @@ void lua_add_spawn_point(luabind::adl::object table) {
|
||||
if(luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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 { \
|
||||
npc_type->name = luabind::object_cast<c_type>(cur); \
|
||||
} \
|
||||
catch(luabind::cast_failed) { \
|
||||
catch(luabind::cast_failed &) { \
|
||||
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); \
|
||||
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); \
|
||||
} \
|
||||
} \
|
||||
@ -1663,6 +1671,8 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("toggle_spawn_event", &lua_toggle_spawn_event),
|
||||
luabind::def("summon_buried_player_corpse", &lua_summon_buried_player_corpse),
|
||||
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("bury_player_corpse", &lua_bury_player_corpse),
|
||||
luabind::def("task_selector", &lua_task_selector),
|
||||
|
||||
@ -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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
size = luabind::object_cast<float>(cur);
|
||||
} catch(luabind::cast_failed) {
|
||||
} catch(luabind::cast_failed &) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -123,7 +123,9 @@ const char *LuaEvents[_LargestEventID] = {
|
||||
"event_tick",
|
||||
"event_spawn_zone",
|
||||
"event_death_zone",
|
||||
"event_use_skill"
|
||||
"event_use_skill",
|
||||
"event_combine_validate",
|
||||
"event_bot_command"
|
||||
};
|
||||
|
||||
extern Zone *zone;
|
||||
@ -206,6 +208,8 @@ LuaParser::LuaParser() {
|
||||
PlayerArgumentDispatch[EVENT_RESPAWN] = handle_player_respawn;
|
||||
PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet;
|
||||
PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill;
|
||||
PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate;
|
||||
PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command;
|
||||
|
||||
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
|
||||
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
|
||||
|
||||
@ -514,6 +514,50 @@ void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client
|
||||
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
|
||||
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) {
|
||||
|
||||
@ -97,6 +97,10 @@ void handle_player_null(QuestInterface *parse, lua_State* L, Client* client, std
|
||||
std::vector<EQEmu::Any> *extra_pointers);
|
||||
void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any> *extra_pointers);
|
||||
void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any>* extra_pointers);
|
||||
void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any> *extra_pointers);
|
||||
|
||||
//Item
|
||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user