Merge pull request #8 from EQEmu/master

merge from eqemu
This commit is contained in:
regneq 2017-08-04 09:06:08 -07:00 committed by GitHub
commit 519c049902
136 changed files with 9570 additions and 1972 deletions

2
.gitignore vendored
View File

@ -33,5 +33,7 @@ Build_32/
build_32/
Build_64/
build_64/
x64/
x86/
log/
logs/

View File

@ -13,12 +13,6 @@
#EQEMU_LOG_LEVEL_QUEST
#EQEMU_LOG_LEVEL_COMMANDS
#EQEMU_LOG_LEVEL_CRASH
#EQEMU_STREAM_SEND_RATE
#EQEMU_STREAM_DECAY_RATE
#EQEMU_STREAM_RETRANSMIT_TIMEOUT_MUL
#EQEMU_STREAM_RETRANSMIT_TIMEOUT_MAX
#EQEMU_STREAM_AVERAGE_DELTA_MAX
#EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS
#EQEMU_DEPOP_INVALIDATES_CACHE
#EQEMU_ENABLE_BOTS
#EQEMU_DISABLE_LOGSYS
@ -78,7 +72,7 @@ IF(MSVC)
SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x86")
SET(LUA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luaj_x86")
SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include")
SET(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/openssl_x64")
SET(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/openssl_x86")
IF(MSVC_VERSION GREATER 1800)
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v140/dynamic")
ELSEIF(MSVC_VERSION EQUAL 1800)
@ -237,14 +231,6 @@ SET(EQEMU_LOG_LEVEL_CRASH 3 CACHE STRING "EQEmu logging level for [Crash]:
MARK_AS_ADVANCED(EQEMU_LOG_LEVEL_STATUS EQEMU_LOG_LEVEL_NORMAL EQEMU_LOG_LEVEL_ERROR EQEMU_LOG_LEVEL_DEBUG EQEMU_LOG_LEVEL_QUEST EQEMU_LOG_LEVEL_COMMANDS EQEMU_LOG_LEVEL_CRASH)
SET(EQEMU_STREAM_SEND_RATE 1048576 CACHE STRING "Advanced: Base amount of data stream can send before throttle.")
SET(EQEMU_STREAM_DECAY_RATE 78642 CACHE STRING "Advanced: Base amount of data stream recovers per tic.")
SET(EQEMU_STREAM_RETRANSMIT_TIMEOUT_MUL 3.0 CACHE STRING "Advanced: Multiplier on retransmit timeout.")
SET(EQEMU_STREAM_RETRANSMIT_TIMEOUT_MAX 5000 CACHE STRING "Advanced: Max in ms for retransmit timeout timer.")
SET(EQEMU_STREAM_AVERAGE_DELTA_MAX 2500 CACHE STRING "Advanced: The maximum average delta in ms allowed.")
SET(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS TRUE CACHE BOOL "Advanced: Whether or not acked packets can be retransmitted")
MARK_AS_ADVANCED(EQEMU_STREAM_SEND_RATE EQEMU_STREAM_DECAY_RATE EQEMU_STREAM_RETRANSMIT_TIMEOUT_MUL EQEMU_STREAM_RETRANSMIT_TIMEOUT_MAX EQEMU_STREAM_AVERAGE_DELTA_MAX EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS)
#NPC Types Cache Behavior
OPTION(EQEMU_DEPOP_INVALIDATES_CACHE "#repop invalidates the npc_types cache (will cause a larger database hit on #repop but is more convienent)." ON)
@ -306,11 +292,6 @@ ADD_DEFINITIONS(-DEQDEBUG=${EQEMU_DEBUG_LEVEL})
ADD_DEFINITIONS(-DINVERSEXY)
ADD_DEFINITIONS(-DFIELD_ITEMS)
ADD_DEFINITIONS(-DMAP_DIR="${EQEMU_MAP_DIR}")
ADD_DEFINITIONS(-DRATEBASE=${EQEMU_STREAM_SEND_RATE})
ADD_DEFINITIONS(-DDECAYBASE=${EQEMU_STREAM_DECAY_RATE})
ADD_DEFINITIONS(-DRETRANSMIT_TIMEOUT_MULT=${EQEMU_STREAM_RETRANSMIT_TIMEOUT_MUL})
ADD_DEFINITIONS(-DRETRANSMIT_TIMEOUT_MAX=${EQEMU_STREAM_RETRANSMIT_TIMEOUT_MAX})
ADD_DEFINITIONS(-DAVERAGE_DELTA_MAX=${EQEMU_STREAM_AVERAGE_DELTA_MAX})
ADD_DEFINITIONS(-DLOG_LEVEL_STATUS=${EQEMU_LOG_LEVEL_STATUS})
ADD_DEFINITIONS(-DLOG_LEVEL_NORMAL=${EQEMU_LOG_LEVEL_NORMAL})
ADD_DEFINITIONS(-DLOG_LEVEL_ERROR=${EQEMU_LOG_LEVEL_ERROR})
@ -320,12 +301,6 @@ ADD_DEFINITIONS(-DLOG_LEVEL_COMMANDS=${EQEMU_LOG_LEVEL_COMMANDS})
ADD_DEFINITIONS(-DLOG_LEVEL_CRASH=${EQEMU_LOG_LEVEL_CRASH})
ADD_DEFINITIONS(-DGLM_FORCE_RADIANS)
IF(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS)
ADD_DEFINITIONS(-DRETRANSMIT_ACKED_PACKETS=true)
ELSE(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS)
ADD_DEFINITIONS(-DRETRANSMIT_ACKED_PACKETS=false)
ENDIF(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS)
#Find everything we need
FIND_PACKAGE(ZLIB REQUIRED)
FIND_PACKAGE(MySQL REQUIRED)

View File

@ -8,7 +8,7 @@
**EQEmulator is a custom completely from-scratch open source server implementation for EverQuest built mostly on C++**
* MySQL/MariaDB is used as the database engine (over 200+ tables)
* Perl and LUA are both supported scripting languages for NPC/Player/Quest oriented events
* Open source database (Project EQ) has content up to expansion GoD (included in server installs)
* Open source database (Project EQ) has content up to expansion OoW (included in server installs)
* Game server environments and databases can be heavily customized to create all new experiences
* Hundreds of Quests/events created and maintained by Project EQ
@ -20,14 +20,14 @@
* [Easy Install](http://wiki.eqemulator.org/p?Akkas_PEQ_Server_Installer&frm=Main#from-scratch-installation-instructions-windows)
* [Advanced Setup](http://wiki.eqemulator.org/p?Complete_Windows-based_Server_Setup_Guide)
### > Debian/Ubuntu
### > Debian/Ubuntu/CentOS/Fedora
* You can use curl or wget to kick off the installer (whichever your OS has)
> curl -O https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh install.sh && chmod 755 install.sh && ./install.sh
> wget --no-check-certificate https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh -O install.sh && chmod 755 install.sh && ./install.sh
### > CentOS/Fedora
> curl -O https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh install.sh && chmod 755 install.sh && ./install.sh
## Supported Clients
|Titanium Edition|Secrets of Faydwer|Seeds of Destruction|Underfoot|Rain of Fear|

View File

@ -1,25 +1,71 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 7/14/2017 ==
Akkadius: HP Update tuning - HP Updates are now forced when a client is targeted
Akkadius: Client position updates should be smoother (granted the client has a good connection)
- Clients should also no longer randomly disappear
== 7/11/2017 ==
Akkadius: Raid/Group/XTarget HP/Mana/Endurance updates now only send when percentage changes
Akkadius: Raid/Group Mana/Endurance updates should now update real-time once again
Akkadius: Fixed an issue with clients looking like they are 'skipping' when they are moving in view of another client
Akkadius: Fixed an issue with NPC's who are ghosted in plain view of a client when they are not really there
== 7/9/2017 ==
Akkadius: Fix HP update issues, rework logic for more accurate HP updates
Akkadius: Massive reductions in unnecessary network traffic especially during high spam combat fights
- HP Updates now only send to others when HP percentage changes (0-100%)
- HP Updates were sending excessively even during idle zones when HP wasn't changing at all
- Attack animations now only send once per second versus up to a hundred times a second per Mob/Client
- 17,000 OP_ClientUpdate packets per second have been observed in combat scenarios, some of the major culprits have been
throttled without affecting what the client should see
- Before and After packet differences under similar load/tests (Packets per second)
- 7,000 - 8,000 OP_Animation pps After: 600-800 pps
- 13,0000 - 17,000 OP_MobHealth pps After: 1-10 pps
- 15,0000 - 20,000 OP_ClientUpdate pps After: 500-1,000 pps
- Packet reports from a 46 client test here:
https://gist.github.com/Akkadius/28b7ad2fdd82bdd15ea737c68f404346
- Servers who use Marquee HP updates will also recieve far less packet spam as they will only be sent when HP changes
== 7/1/2017 ==
Akkadius: Resolve issues with NPC's hopping to the ceiling in small corridors
Akkadius: Improved grounding issues with NPC's during combat
Akkadius: Improved scenarios where NPC's need to be dragged out of the ground - they should correct themselves far more consistently
- Scenarios where an NPC is coming up from the bottom floor, or from the top floor, they will correct much better
- A video of these tests can be found here: https://www.youtube.com/watch?v=HtC7bVNM7ZQ&feature=youtu.be
== 6/28/2017 ==
Akkadius: Fixed issues with Z correctness when NPCs are pathing on normal grids
Akkadius: Fixed issues with Z correctness when NPCs are engaged with players following
Akkadius: NPC corpses should fall into the ground far less
== 6/25/2017 ==
Akkadius: New rules made by developers are now automatically created when world boots up, this keeps
from having to issue schema SQL updates every time rules are added.
- Whenever a rule isn't present in the database, it will be automatically created
Akkadius: Sped up saylink retrieval x1000 helpful for dialogues, plugins with many saylinks
== 4/16/2017 ==
KLS: Merge eqstream branch
-UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs).
-TCP Server to Server connection stack completely rewritten.
-Server connections reconnect much more reliably and quickly now.
-Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/).
-Protocol behind the tcp connections has changed (see breaking changes section).
-API significantly changed and should be easier to write new servers or handlers for.
-Telnet console connection has been separated out from the current port (see breaking changes section).
-Because of changes to the TCP stack, lsreconnect and echo have been disabled.
-The server tic rate has been changed to be approx 30 fps from 500+ fps.
-Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough).
- UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs).
- TCP Server to Server connection stack completely rewritten.
- Server connections reconnect much more reliably and quickly now.
- Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/).
- Protocol behind the tcp connections has changed (see breaking changes section).
- API significantly changed and should be easier to write new servers or handlers for.
- Telnet console connection has been separated out from the current port (see breaking changes section).
- Because of changes to the TCP stack, lsreconnect and echo have been disabled.
- The server tic rate has been changed to be approx 30 fps from 500+ fps.
- Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough).
-Breaking changes:
-Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/
-To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections:
-You should add <legacy>1</legacy> to the login section of your configuration file when connecting to a server that is using the old protocol.
-The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section.
-Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections.
-To enable telnet you need to add a telnet tag in the world section of configuration such as:
- Breaking changes:
- Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/
- To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections:
- You should add <legacy>1</legacy> to the login section of your configuration file when connecting to a server that is using the old protocol.
- The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section.
- Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections.
- To enable telnet you need to add a telnet tag in the world section of configuration such as:
<telnet ip="0.0.0.0" port="9001" enabled="true"/>
== 4/1/2017 ==

View File

@ -103,6 +103,7 @@ SET(common_sources
tinyxml/tinyxml.cpp
tinyxml/tinyxmlerror.cpp
tinyxml/tinyxmlparser.cpp
util/directory.cpp
util/uuid.cpp
)
@ -257,6 +258,7 @@ SET(common_headers
tinyxml/tinystr.h
tinyxml/tinyxml.h
util/memory_stream.h
util/directory.h
util/uuid.h
)
@ -366,6 +368,8 @@ SOURCE_GROUP(TinyXML FILES
SOURCE_GROUP(Util FILES
util/memory_stream.h
util/directory.cpp
util/directory.h
util/uuid.cpp
util/uuid.h
)

View File

@ -355,6 +355,7 @@ N(OP_OpenTributeMaster),
N(OP_PDeletePetition),
N(OP_PetBuffWindow),
N(OP_PetCommands),
N(OP_PetCommandState),
N(OP_PetHoTT),
N(OP_Petition),
N(OP_PetitionBug),
@ -407,6 +408,7 @@ N(OP_ReloadUI),
N(OP_RemoveAllDoors),
N(OP_RemoveBlockedBuffs),
N(OP_RemoveNimbusEffect),
N(OP_RemoveTrap),
N(OP_Report),
N(OP_ReqClientSpawn),
N(OP_ReqNewZone),
@ -522,6 +524,7 @@ N(OP_TributeToggle),
N(OP_TributeUpdate),
N(OP_Untargetable),
N(OP_UpdateAA),
N(OP_UpdateAura),
N(OP_UpdateLeadershipAA),
N(OP_VetClaimReply),
N(OP_VetClaimRequest),

View File

@ -27,7 +27,7 @@
//SpawnAppearance types: (compared two clients for server-originating types: SoF & RoF2)
#define AT_Die 0 // this causes the client to keel over and zone to bind point (default action)
#define AT_WhoLevel 1 // the level that shows up on /who
//#define AT_2 2 // unknown
#define AT_HPMax 2 // idk
#define AT_Invis 3 // 0 = visible, 1 = invisible
#define AT_PVP 4 // 0 = blue, 1 = pvp (red)
#define AT_Light 5 // light type emitted by player (lightstone, shiny shield)
@ -36,33 +36,37 @@
#define AT_SpawnID 16 // server to client, sets player spawn id
#define AT_HP 17 // Client->Server, my HP has changed (like regen tic)
#define AT_Linkdead 18 // 0 = normal, 1 = linkdead
#define AT_Levitate 19 // 0=off, 1=flymode, 2=levitate
#define AT_Levitate 19 // 0=off, 1=flymode, 2=levitate max 5, see GravityBehavior enum
#define AT_GM 20 // 0 = normal, 1 = GM - all odd numbers seem to make it GM
#define AT_Anon 21 // 0 = normal, 1 = anon, 2 = roleplay
#define AT_GuildID 22
#define AT_GuildRank 23 // 0=member, 1=officer, 2=leader
#define AT_AFK 24 // 0 = normal, 1 = afk
#define AT_Pet 25 // Param is EntityID of owner, or 0 for when charm breaks
//#define AT_27 27 // unknown
#define AT_Summoned 27 // Unsure
#define AT_Split 28 // 0 = normal, 1 = autosplit on (not showing in SoF+) (client-to-server only)
#define AT_Size 29 // spawn's size (present: SoF, absent: RoF2)
//#define AT_30 30 // unknown
#define AT_NPCName 31 // change PC's name's color to NPC color 0 = normal, 1 = npc name
//#define AT_32 32 // unknown
//#define AT_33 33 // unknown
#define AT_SetType 30 // 0 = PC, 1 = NPC, 2 <= = corpse
#define AT_NPCName 31 // change PC's name's color to NPC color 0 = normal, 1 = npc name, Trader on RoF2?
#define AT_AARank 32 // AA Rank Title ID thingy, does is this the title in /who?
#define AT_CancelSneakHide 33 // Turns off Hide and Sneak
//#define AT_34 34 // unknown (present: SoF, absent: RoF2)
//#define AT_35 35 // unknown
//#define AT_36 36 // unknown
//#define AT_37 37 // unknown
//#define AT_38 38 // unknown
//#define AT_39 39 // unknown
#define AT_AreaHPRegen 35 // guild hall regen pool sets to value * 0.001
#define AT_AreaManaRegen 36 // guild hall regen pool sets to value * 0.001
#define AT_AreaEndRegen 37 // guild hall regen pool sets to value * 0.001
#define AT_FreezeBuffs 38 // Freezes beneficial buff timers
#define AT_NpcTintIndex 39 // not 100% sure
#define AT_GroupConsent 40 // auto consent group
#define AT_RaidConsent 41 // auto consent raid
#define AT_GuildConsent 42 // auto consent guild
#define AT_ShowHelm 43 // 0 = hide graphic, 1 = show graphic
#define AT_DamageState 44 // The damage state of a destructible object (0 through 4)
//#define AT_46 46 // unknown
//#define AT_48 48 // unknown
//#define AT_49 49 // unknown
//#define AT_52 52 // (absent: SoF, present: RoF2) (not a replacement for RoF absent 29 or 34)
//#define AT_53 53 // (absent: SoF, present: RoF2) (not a replacement for RoF absent 29 or 34)
#define AT_DamageState 44 // The damage state of a destructible object (0 through 10) plays soundids most only have 2 or 4 states though
#define AT_EQPlayers 45 // /eqplayersupdate
#define AT_FindBits 46 // set FindBits, whatever those are!
#define AT_TextureType 48 // TextureType
#define AT_FacePick 49 // Turns off face pick window? maybe ...
#define AT_GuildShow 52 // this is what MQ2 call sit, not sure
#define AT_Offline 53 // Offline mode
//#define AT_Trader 300 // Bazaar Trader Mode (not present in SoF or RoF2)

View File

@ -305,6 +305,7 @@ union
uint8 DestructibleUnk8;
uint32 DestructibleUnk9;
bool targetable_with_hotkey;
bool show_name;
};
@ -1116,6 +1117,11 @@ struct PetCommand_Struct {
/*004*/ uint32 target;
};
struct PetCommandState_Struct {
/*00*/ uint32 button_id;
/*04*/ uint32 state;
};
/*
** Delete Spawn
** Length: 4 Bytes
@ -1690,6 +1696,7 @@ struct OnLevelMessage_Struct
uint32 Duration;
uint32 PopupID;
uint32 NegativeID;
uint32 SoundControls;
char ButtonName0[25];
char ButtonName1[25];
};
@ -5326,6 +5333,24 @@ struct fling_struct {
/* 28 */
};
// used when action == 0
struct AuraCreate_Struct {
/* 00 */ uint32 action; // 0 = add, 1 = delete, 2 = reset
/* 04 */ uint32 type; // unsure -- normal auras show 1 clicky (ex. Circle of Power) show 0
/* 08 */ char aura_name[64];
/* 72 */ uint32 entity_id;
/* 76 */ uint32 icon;
/* 80 */
};
// used when action == 1
struct AuraDestory_Struct {
/* 00 */ uint32 action; // 0 = add, 1 = delete, 2 = reset
/* 04 */ uint32 entity_id;
/* 08 */
};
// I think we can assume it's just action for 2, client doesn't seem to do anything with the rest of the data in that case
// Restore structure packing to default
#pragma pack()

View File

@ -2,6 +2,8 @@
#include "global_define.h"
#include "eq_stream_proxy.h"
#include "struct_strategy.h"
#include "eqemu_logsys.h"
#include "opcodemgr.h"
EQStreamProxy::EQStreamProxy(std::shared_ptr<EQStreamInterface> &stream, const StructStrategy *structs, OpcodeManager **opcodes)
@ -39,6 +41,11 @@ void EQStreamProxy::QueuePacket(const EQApplicationPacket *p, bool ack_req) {
if(p == nullptr)
return;
if (p->GetOpcode() != OP_SpecialMesg) {
Log(Logs::General, Logs::Server_Client_Packet, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size());
Log(Logs::General, Logs::Server_Client_Packet_With_Dump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size(), DumpPacketToString(p).c_str());
}
EQApplicationPacket *newp = p->Copy();
FastQueuePacket(&newp, ack_req);
}

View File

@ -86,6 +86,8 @@ enum LogCategory {
Login_Server,
Client_Login,
Headless_Client,
HP_Update,
FixZ,
MaxCategoryID /* Don't Remove this*/
};
@ -135,7 +137,10 @@ static const char* LogCategoryName[LogCategory::MaxCategoryID] = {
"Packet :: Server -> Client (Dump)",
"Packet :: Client -> Server (Dump)",
"Login Server",
"Client Login"
"Client Login",
"Headless Client",
"HP Update",
"FixZ"
};
}

View File

@ -332,7 +332,7 @@ namespace EQEmu
};
struct ItemEffect_Struct {
int16 Effect;
int32 Effect;
uint8 Type;
uint8 Level;
uint8 Level2;

View File

@ -25,6 +25,23 @@ void EQ::Net::ConsoleServer::RegisterLogin(ConsoleServerLoginCallback fn)
m_login = fn;
}
EQ::Net::ConsoleServerConnection *EQ::Net::ConsoleServer::FindByAccountName(const std::string &acct_name) {
for (auto &iter : m_connections) {
if (iter.second->UserName().compare(acct_name) == 0) {
return iter.second.get();
}
}
return nullptr;
}
void EQ::Net::ConsoleServer::SendChannelMessage(const ServerChannelMessage_Struct* scm, std::function<void(void)> onTell) {
for (auto &iter : m_connections) {
iter.second->SendChannelMessage(scm, onTell);
}
}
void EQ::Net::ConsoleServer::ConnectionDisconnected(ConsoleServerConnection *c)
{
auto iter = m_connections.find(c->GetUUID());

View File

@ -25,7 +25,8 @@ namespace EQ
void RegisterCall(const std::string& command, int status_required, const std::string& help_definition, ConsoleServerCallback fn);
void RegisterLogin(ConsoleServerLoginCallback fn);
ConsoleServerConnection *FindByAccountName(const std::string &acct_name);
void SendChannelMessage(const ServerChannelMessage_Struct* scm, std::function<void(void)> onTell);
private:
void ConnectionDisconnected(ConsoleServerConnection *c);
void ProcessCommand(ConsoleServerConnection *c, const std::string& cmd);

View File

@ -2,6 +2,8 @@
#include "../common/util/uuid.h"
#include "../common/net/packet.h"
#include "../common/eqemu_logsys.h"
#include "../common/servertalk.h"
#include "../common/rulesys.h"
EQ::Net::ConsoleServerConnection::ConsoleServerConnection(ConsoleServer *parent, std::shared_ptr<TCPConnection> connection)
{
@ -107,6 +109,53 @@ void EQ::Net::ConsoleServerConnection::QueueMessage(const std::string &msg)
}
}
bool EQ::Net::ConsoleServerConnection::SendChannelMessage(const ServerChannelMessage_Struct* scm, std::function<void(void)> onTell) {
if (!m_accept_messages) {
return false;
}
switch (scm->chan_num) {
if (RuleB(Chat, ServerWideAuction)) {
case 4: {
QueueMessage(fmt::format("{0} auctions, '{1}'", scm->from, scm->message));
break;
}
}
if (RuleB(Chat, ServerWideOOC)) {
case 5: {
QueueMessage(fmt::format("{0} says ooc, '{1}'", scm->from, scm->message));
break;
}
}
case 6: {
QueueMessage(fmt::format("{0} BROADCASTS, '{1}'", scm->from, scm->message));
break;
}
case 7: {
QueueMessage(fmt::format("[{0}] tells you, '{1}'", scm->from, scm->message));
if (onTell) {
onTell();
}
break;
}
case 11: {
QueueMessage(fmt::format("{0} GMSAYS, '{1}'", scm->from, scm->message));
break;
}
default: {
return false;
}
}
return true;
}
void EQ::Net::ConsoleServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz)
{
for (size_t i = 0; i < sz; ++i) {

View File

@ -4,6 +4,8 @@
#include <memory>
#include <map>
struct ServerChannelMessage_Struct;
namespace EQ
{
namespace Net
@ -42,6 +44,7 @@ namespace EQ
bool AcceptMessages() const { return m_accept_messages; }
void SetAcceptMessages(bool v) { m_accept_messages = v; }
void QueueMessage(const std::string &msg);
bool SendChannelMessage(const ServerChannelMessage_Struct* scm, std::function<void(void)> onTell);
private:
void OnRead(TCPConnection* c, const unsigned char* data, size_t sz);
void OnDisconnect(TCPConnection* c);

View File

@ -135,26 +135,26 @@ void EQ::Net::DaybreakConnectionManager::Process()
switch (status)
{
case StatusConnecting: {
auto time_since_last_send = std::chrono::duration_cast<std::chrono::milliseconds>(now - connection->m_last_send);
if ((size_t)time_since_last_send.count() > m_options.connect_delay_ms) {
connection->SendConnect();
}
}
break;
case StatusConnected: {
if (m_options.keepalive_delay_ms != 0) {
case StatusConnecting: {
auto time_since_last_send = std::chrono::duration_cast<std::chrono::milliseconds>(now - connection->m_last_send);
if ((size_t)time_since_last_send.count() > m_options.keepalive_delay_ms) {
connection->SendKeepAlive();
if ((size_t)time_since_last_send.count() > m_options.connect_delay_ms) {
connection->SendConnect();
}
break;
}
case StatusConnected: {
if (m_options.keepalive_delay_ms != 0) {
auto time_since_last_send = std::chrono::duration_cast<std::chrono::milliseconds>(now - connection->m_last_send);
if ((size_t)time_since_last_send.count() > m_options.keepalive_delay_ms) {
connection->SendKeepAlive();
}
}
}
}
case StatusDisconnecting:
connection->Process();
break;
default:
break;
case StatusDisconnecting:
connection->Process();
break;
default:
break;
}
iter++;
@ -170,12 +170,12 @@ void EQ::Net::DaybreakConnectionManager::ProcessResend()
switch (status)
{
case StatusConnected:
case StatusDisconnecting:
connection->ProcessResend();
break;
default:
break;
case StatusConnected:
case StatusDisconnecting:
connection->ProcessResend();
break;
default:
break;
}
iter++;
@ -382,13 +382,8 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p)
return;
}
if (p.GetInt8(0) != 0) {
LogF(Logs::Detail, Logs::Netcode, "Error parsing packet, did not start with a 0 frame, not a valid protocol packet.");
return;
}
auto opcode = p.GetInt8(1);
if (opcode == OP_KeepAlive || opcode == OP_OutboundPing) {
if (p.GetInt8(0) == 0 && (opcode == OP_KeepAlive || opcode == OP_OutboundPing)) {
return;
}
@ -406,14 +401,20 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p)
for (int i = 1; i >= 0; --i) {
switch (m_encode_passes[i]) {
case EncodeCompression:
Decompress(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size());
break;
case EncodeXOR:
Decode(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size());
break;
default:
break;
case EncodeCompression:
if(temp.GetInt8(0) == 0)
Decompress(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size());
else
Decompress(temp, 1, temp.Length() - 1);
break;
case EncodeXOR:
if (temp.GetInt8(0) == 0)
Decode(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size());
else
Decode(temp, 1, temp.Length() - 1);
break;
default:
break;
}
}
@ -424,11 +425,14 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p)
for (int i = 1; i >= 0; --i) {
switch (m_encode_passes[i]) {
case EncodeXOR:
Decode(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size());
break;
default:
break;
case EncodeXOR:
if (temp.GetInt8(0) == 0)
Decode(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size());
else
Decode(temp, 1, temp.Length() - 1);
break;
default:
break;
}
}
@ -490,274 +494,274 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p)
}
switch (p.GetInt8(1)) {
case OP_Combined: {
if (m_status == StatusDisconnecting) {
SendDisconnect();
return;
}
char *current = (char*)p.Data() + 2;
char *end = (char*)p.Data() + p.Length();
while (current < end) {
uint8_t subpacket_length = *(uint8_t*)current;
current += 1;
if (end < current + subpacket_length) {
case OP_Combined: {
if (m_status == StatusDisconnecting) {
SendDisconnect();
return;
}
ProcessDecodedPacket(StaticPacket(current, subpacket_length));
current += subpacket_length;
}
break;
}
char *current = (char*)p.Data() + 2;
char *end = (char*)p.Data() + p.Length();
while (current < end) {
uint8_t subpacket_length = *(uint8_t*)current;
current += 1;
case OP_AppCombined:
{
if (m_status == StatusDisconnecting) {
SendDisconnect();
return;
}
uint8_t *current = (uint8_t*)p.Data() + 2;
uint8_t *end = (uint8_t*)p.Data() + p.Length();
while (current < end) {
uint32_t subpacket_length = 0;
if (*current == 0xFF)
{
if (end < current + 3) {
throw std::out_of_range("Error in OP_AppCombined, end < current + 3");
if (end < current + subpacket_length) {
return;
}
if (*(current + 1) == 0xFF && *(current + 2) == 0xFF) {
if (end < current + 7) {
throw std::out_of_range("Error in OP_AppCombined, end < current + 7");
ProcessDecodedPacket(StaticPacket(current, subpacket_length));
current += subpacket_length;
}
break;
}
case OP_AppCombined:
{
if (m_status == StatusDisconnecting) {
SendDisconnect();
return;
}
uint8_t *current = (uint8_t*)p.Data() + 2;
uint8_t *end = (uint8_t*)p.Data() + p.Length();
while (current < end) {
uint32_t subpacket_length = 0;
if (*current == 0xFF)
{
if (end < current + 3) {
throw std::out_of_range("Error in OP_AppCombined, end < current + 3");
}
subpacket_length = (uint32_t)(
(*(current + 3) << 24) |
(*(current + 4) << 16) |
(*(current + 5) << 8) |
(*(current + 6))
);
current += 7;
if (*(current + 1) == 0xFF && *(current + 2) == 0xFF) {
if (end < current + 7) {
throw std::out_of_range("Error in OP_AppCombined, end < current + 7");
}
subpacket_length = (uint32_t)(
(*(current + 3) << 24) |
(*(current + 4) << 16) |
(*(current + 5) << 8) |
(*(current + 6))
);
current += 7;
}
else {
subpacket_length = (uint32_t)(
(*(current + 1) << 8) |
(*(current + 2))
);
current += 3;
}
}
else {
subpacket_length = (uint32_t)(
(*(current + 1) << 8) |
(*(current + 2))
);
current += 3;
subpacket_length = (uint32_t)((*(current + 0)));
current += 1;
}
ProcessDecodedPacket(StaticPacket(current, subpacket_length));
current += subpacket_length;
}
}
case OP_SessionRequest:
{
if (m_status == StatusConnected) {
auto request = p.GetSerialize<DaybreakConnect>(0);
if (NetworkToHost(request.connect_code) != m_connect_code) {
return;
}
DaybreakConnectReply reply;
reply.zero = 0;
reply.opcode = OP_SessionResponse;
reply.connect_code = HostToNetwork(m_connect_code);
reply.encode_key = HostToNetwork(m_encode_key);
reply.crc_bytes = m_crc_bytes;
reply.max_packet_size = HostToNetwork(m_max_packet_size);
reply.encode_pass1 = m_encode_passes[0];
reply.encode_pass2 = m_encode_passes[1];
DynamicPacket p;
p.PutSerialize(0, reply);
InternalSend(p);
}
break;
}
case OP_SessionResponse:
{
if (m_status == StatusConnecting) {
auto reply = p.GetSerialize<DaybreakConnectReply>(0);
if (m_connect_code == reply.connect_code) {
m_encode_key = reply.encode_key;
m_crc_bytes = reply.crc_bytes;
m_encode_passes[0] = (DaybreakEncodeType)reply.encode_pass1;
m_encode_passes[1] = (DaybreakEncodeType)reply.encode_pass2;
m_max_packet_size = reply.max_packet_size;
ChangeStatus(StatusConnected);
}
}
else {
subpacket_length = (uint32_t)((*(current + 0)));
current += 1;
}
ProcessDecodedPacket(StaticPacket(current, subpacket_length));
current += subpacket_length;
break;
}
}
case OP_SessionRequest:
{
if (m_status == StatusConnected) {
auto request = p.GetSerialize<DaybreakConnect>(0);
if (NetworkToHost(request.connect_code) != m_connect_code) {
case OP_Packet:
case OP_Packet2:
case OP_Packet3:
case OP_Packet4:
{
if (m_status == StatusDisconnecting) {
SendDisconnect();
return;
}
DaybreakConnectReply reply;
reply.zero = 0;
reply.opcode = OP_SessionResponse;
reply.connect_code = HostToNetwork(m_connect_code);
reply.encode_key = HostToNetwork(m_encode_key);
reply.crc_bytes = m_crc_bytes;
reply.max_packet_size = HostToNetwork(m_max_packet_size);
reply.encode_pass1 = m_encode_passes[0];
reply.encode_pass2 = m_encode_passes[1];
DynamicPacket p;
p.PutSerialize(0, reply);
InternalSend(p);
}
auto header = p.GetSerialize<DaybreakReliableHeader>(0);
auto sequence = NetworkToHost(header.sequence);
auto stream_id = header.opcode - OP_Packet;
auto stream = &m_streams[stream_id];
break;
}
case OP_SessionResponse:
{
if (m_status == StatusConnecting) {
auto reply = p.GetSerialize<DaybreakConnectReply>(0);
if (m_connect_code == reply.connect_code) {
m_encode_key = reply.encode_key;
m_crc_bytes = reply.crc_bytes;
m_encode_passes[0] = (DaybreakEncodeType)reply.encode_pass1;
m_encode_passes[1] = (DaybreakEncodeType)reply.encode_pass2;
m_max_packet_size = reply.max_packet_size;
ChangeStatus(StatusConnected);
auto order = CompareSequence(stream->sequence_in, sequence);
if (order == SequenceFuture) {
SendOutOfOrderAck(stream_id, sequence);
AddToQueue(stream_id, sequence, p);
}
}
break;
}
case OP_Packet:
case OP_Packet2:
case OP_Packet3:
case OP_Packet4:
{
if (m_status == StatusDisconnecting) {
SendDisconnect();
return;
}
auto header = p.GetSerialize<DaybreakReliableHeader>(0);
auto sequence = NetworkToHost(header.sequence);
auto stream_id = header.opcode - OP_Packet;
auto stream = &m_streams[stream_id];
auto order = CompareSequence(stream->sequence_in, sequence);
if (order == SequenceFuture) {
SendOutOfOrderAck(stream_id, sequence);
AddToQueue(stream_id, sequence, p);
}
else if (order == SequencePast) {
SendAck(stream_id, stream->sequence_in - 1);
}
else {
RemoveFromQueue(stream_id, sequence);
SendAck(stream_id, stream->sequence_in);
stream->sequence_in++;
StaticPacket next((char*)p.Data() + DaybreakReliableHeader::size(), p.Length() - DaybreakReliableHeader::size());
ProcessDecodedPacket(next);
}
break;
}
case OP_Fragment:
case OP_Fragment2:
case OP_Fragment3:
case OP_Fragment4:
{
auto header = p.GetSerialize<DaybreakReliableHeader>(0);
auto sequence = NetworkToHost(header.sequence);
auto stream_id = header.opcode - OP_Fragment;
auto stream = &m_streams[stream_id];
auto order = CompareSequence(stream->sequence_in, sequence);
if (order == SequenceFuture) {
SendOutOfOrderAck(stream_id, sequence);
AddToQueue(stream_id, sequence, p);
}
else if (order == SequencePast) {
SendAck(stream_id, stream->sequence_in - 1);
}
else {
RemoveFromQueue(stream_id, sequence);
SendAck(stream_id, stream->sequence_in);
stream->sequence_in++;
if (stream->fragment_total_bytes == 0) {
auto fragheader = p.GetSerialize<DaybreakReliableFragmentHeader>(0);
stream->fragment_total_bytes = NetworkToHost(fragheader.total_size);
stream->fragment_current_bytes = 0;
stream->fragment_packet.Reserve(stream->fragment_total_bytes);
stream->fragment_packet.PutData(
stream->fragment_current_bytes,
(char*)p.Data() + DaybreakReliableFragmentHeader::size(), p.Length() - DaybreakReliableFragmentHeader::size());
stream->fragment_current_bytes += (uint32_t)(p.Length() - DaybreakReliableFragmentHeader::size());
else if (order == SequencePast) {
SendAck(stream_id, stream->sequence_in - 1);
}
else {
stream->fragment_packet.PutData(
stream->fragment_current_bytes,
(char*)p.Data() + DaybreakReliableHeader::size(), p.Length() - DaybreakReliableHeader::size());
RemoveFromQueue(stream_id, sequence);
SendAck(stream_id, stream->sequence_in);
stream->sequence_in++;
StaticPacket next((char*)p.Data() + DaybreakReliableHeader::size(), p.Length() - DaybreakReliableHeader::size());
ProcessDecodedPacket(next);
}
stream->fragment_current_bytes += (uint32_t)(p.Length() - DaybreakReliableHeader::size());
break;
}
if (stream->fragment_current_bytes >= stream->fragment_total_bytes) {
ProcessDecodedPacket(stream->fragment_packet);
stream->fragment_packet.Clear();
stream->fragment_total_bytes = 0;
case OP_Fragment:
case OP_Fragment2:
case OP_Fragment3:
case OP_Fragment4:
{
auto header = p.GetSerialize<DaybreakReliableHeader>(0);
auto sequence = NetworkToHost(header.sequence);
auto stream_id = header.opcode - OP_Fragment;
auto stream = &m_streams[stream_id];
auto order = CompareSequence(stream->sequence_in, sequence);
if (order == SequenceFuture) {
SendOutOfOrderAck(stream_id, sequence);
AddToQueue(stream_id, sequence, p);
}
else if (order == SequencePast) {
SendAck(stream_id, stream->sequence_in - 1);
}
else {
RemoveFromQueue(stream_id, sequence);
SendAck(stream_id, stream->sequence_in);
stream->sequence_in++;
if (stream->fragment_total_bytes == 0) {
auto fragheader = p.GetSerialize<DaybreakReliableFragmentHeader>(0);
stream->fragment_total_bytes = NetworkToHost(fragheader.total_size);
stream->fragment_current_bytes = 0;
stream->fragment_packet.Reserve(stream->fragment_total_bytes);
stream->fragment_packet.PutData(
stream->fragment_current_bytes,
(char*)p.Data() + DaybreakReliableFragmentHeader::size(), p.Length() - DaybreakReliableFragmentHeader::size());
stream->fragment_current_bytes += (uint32_t)(p.Length() - DaybreakReliableFragmentHeader::size());
}
else {
stream->fragment_packet.PutData(
stream->fragment_current_bytes,
(char*)p.Data() + DaybreakReliableHeader::size(), p.Length() - DaybreakReliableHeader::size());
stream->fragment_current_bytes += (uint32_t)(p.Length() - DaybreakReliableHeader::size());
if (stream->fragment_current_bytes >= stream->fragment_total_bytes) {
ProcessDecodedPacket(stream->fragment_packet);
stream->fragment_packet.Clear();
stream->fragment_total_bytes = 0;
stream->fragment_current_bytes = 0;
}
}
}
break;
}
break;
}
case OP_Ack:
case OP_Ack2:
case OP_Ack3:
case OP_Ack4:
{
auto header = p.GetSerialize<DaybreakReliableHeader>(0);
auto sequence = NetworkToHost(header.sequence);
auto stream_id = header.opcode - OP_Ack;
Ack(stream_id, sequence);
break;
}
case OP_OutOfOrderAck:
case OP_OutOfOrderAck2:
case OP_OutOfOrderAck3:
case OP_OutOfOrderAck4:
{
auto header = p.GetSerialize<DaybreakReliableHeader>(0);
auto sequence = NetworkToHost(header.sequence);
auto stream_id = header.opcode - OP_OutOfOrderAck;
OutOfOrderAck(stream_id, sequence);
break;
}
case OP_SessionDisconnect:
{
if (m_status == StatusConnected || m_status == StatusDisconnecting) {
FlushBuffer();
SendDisconnect();
case OP_Ack:
case OP_Ack2:
case OP_Ack3:
case OP_Ack4:
{
auto header = p.GetSerialize<DaybreakReliableHeader>(0);
auto sequence = NetworkToHost(header.sequence);
auto stream_id = header.opcode - OP_Ack;
Ack(stream_id, sequence);
break;
}
ChangeStatus(StatusDisconnecting);
break;
}
case OP_Padding:
{
auto self = m_self.lock();
if (m_owner->m_on_packet_recv && self) {
m_owner->m_on_packet_recv(self, StaticPacket((char*)p.Data() + 1, p.Length() - 1));
case OP_OutOfOrderAck:
case OP_OutOfOrderAck2:
case OP_OutOfOrderAck3:
case OP_OutOfOrderAck4:
{
auto header = p.GetSerialize<DaybreakReliableHeader>(0);
auto sequence = NetworkToHost(header.sequence);
auto stream_id = header.opcode - OP_OutOfOrderAck;
OutOfOrderAck(stream_id, sequence);
break;
}
break;
}
case OP_SessionStatRequest:
{
auto request = p.GetSerialize<DaybreakSessionStatRequest>(0);
DaybreakSessionStatResponse response;
response.zero = 0;
response.opcode = OP_SessionStatResponse;
response.timestamp = request.timestamp;
response.our_timestamp = EQ::Net::HostToNetwork(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
response.client_sent = request.packets_sent;
response.client_recv = request.packets_recv;
response.server_sent = EQ::Net::HostToNetwork(m_stats.sent_packets);
response.server_recv = EQ::Net::HostToNetwork(m_stats.recv_packets);
DynamicPacket out;
out.PutSerialize(0, response);
InternalSend(out);
break;
}
case OP_SessionStatResponse:
break;
default:
LogF(Logs::Detail, Logs::Netcode, "Unhandled opcode {0:#x}", p.GetInt8(1));
break;
case OP_SessionDisconnect:
{
if (m_status == StatusConnected || m_status == StatusDisconnecting) {
FlushBuffer();
SendDisconnect();
}
ChangeStatus(StatusDisconnecting);
break;
}
case OP_Padding:
{
auto self = m_self.lock();
if (m_owner->m_on_packet_recv && self) {
m_owner->m_on_packet_recv(self, StaticPacket((char*)p.Data() + 1, p.Length() - 1));
}
break;
}
case OP_SessionStatRequest:
{
auto request = p.GetSerialize<DaybreakSessionStatRequest>(0);
DaybreakSessionStatResponse response;
response.zero = 0;
response.opcode = OP_SessionStatResponse;
response.timestamp = request.timestamp;
response.our_timestamp = EQ::Net::HostToNetwork(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
response.client_sent = request.packets_sent;
response.client_recv = request.packets_recv;
response.server_sent = EQ::Net::HostToNetwork(m_stats.sent_packets);
response.server_recv = EQ::Net::HostToNetwork(m_stats.recv_packets);
DynamicPacket out;
out.PutSerialize(0, response);
InternalSend(out);
break;
}
case OP_SessionStatResponse:
break;
default:
LogF(Logs::Detail, Logs::Netcode, "Unhandled opcode {0:#x}", p.GetInt8(1));
break;
}
}
else {
@ -782,16 +786,16 @@ bool EQ::Net::DaybreakConnection::ValidateCRC(Packet &p)
int calculated = 0;
int actual = 0;
switch (m_crc_bytes) {
case 2:
actual = NetworkToHost(*(int16_t*)&data[p.Length() - (size_t)m_crc_bytes]) & 0xffff;
calculated = Crc32(data, (int)(p.Length() - (size_t)m_crc_bytes), m_encode_key) & 0xffff;
break;
case 4:
actual = NetworkToHost(*(int32_t*)&data[p.Length() - (size_t)m_crc_bytes]);
calculated = Crc32(data, (int)(p.Length() - (size_t)m_crc_bytes), m_encode_key);
break;
default:
return false;
case 2:
actual = NetworkToHost(*(int16_t*)&data[p.Length() - (size_t)m_crc_bytes]) & 0xffff;
calculated = Crc32(data, (int)(p.Length() - (size_t)m_crc_bytes), m_encode_key) & 0xffff;
break;
case 4:
actual = NetworkToHost(*(int32_t*)&data[p.Length() - (size_t)m_crc_bytes]);
calculated = Crc32(data, (int)(p.Length() - (size_t)m_crc_bytes), m_encode_key);
break;
default:
return false;
}
if (actual == calculated) {
@ -809,14 +813,14 @@ void EQ::Net::DaybreakConnection::AppendCRC(Packet &p)
int calculated = 0;
switch (m_crc_bytes) {
case 2:
calculated = Crc32(p.Data(), (int)p.Length(), m_encode_key) & 0xffff;
p.PutInt16(p.Length(), EQ::Net::HostToNetwork((int16_t)calculated));
break;
case 4:
calculated = Crc32(p.Data(), (int)p.Length(), m_encode_key);
p.PutInt32(p.Length(), EQ::Net::HostToNetwork(calculated));
break;
case 2:
calculated = Crc32(p.Data(), (int)p.Length(), m_encode_key) & 0xffff;
p.PutInt16(p.Length(), EQ::Net::HostToNetwork((int16_t)calculated));
break;
case 4:
calculated = Crc32(p.Data(), (int)p.Length(), m_encode_key);
p.PutInt32(p.Length(), EQ::Net::HostToNetwork(calculated));
break;
}
}
@ -1055,7 +1059,7 @@ void EQ::Net::DaybreakConnection::Ack(int stream, uint16_t seq)
while (iter != s->sent_packets.end()) {
auto order = CompareSequence(seq, iter->first);
if (order != SequenceFuture) {
if (order != SequenceFuture) {
uint64_t round_time = (uint64_t)std::chrono::duration_cast<std::chrono::milliseconds>(now - iter->second.last_sent).count();
m_stats.max_ping = std::max(m_stats.max_ping, round_time);
@ -1187,27 +1191,24 @@ void EQ::Net::DaybreakConnection::InternalSend(Packet &p)
if (PacketCanBeEncoded(p)) {
DynamicPacket out;
if (p.GetUInt8(0) != 0) {
out.PutUInt8(0, 0);
out.PutUInt8(1, OP_Combined);
out.PutUInt8(2, p.Length());
out.PutPacket(3, p);
}
else {
out.PutPacket(0, p);
}
out.PutPacket(0, p);
for (int i = 0; i < 2; ++i) {
switch (m_encode_passes[i]) {
case EncodeCompression:
Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
break;
case EncodeXOR:
Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
break;
default:
break;
case EncodeCompression:
if(out.GetInt8(0) == 0)
Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
else
Compress(out, 1, out.Length() - 1);
break;
case EncodeXOR:
if (out.GetInt8(0) == 0)
Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
else
Encode(out, 1, out.Length() - 1);
break;
default:
break;
}
}

View File

@ -218,7 +218,7 @@ namespace EQ
encode_passes[1] = DaybreakEncodeType::EncodeNone;
port = 0;
hold_size = 448;
hold_length_ms = 10;
hold_length_ms = 50;
simulated_in_packet_loss = 0;
simulated_out_packet_loss = 0;
tic_rate_hertz = 60.0;

View File

@ -81,7 +81,12 @@ void EQ::Net::EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req)
break;
}
m_connection->QueuePacket(out);
if (ack_req) {
m_connection->QueuePacket(out);
}
else {
m_connection->QueuePacket(out, 0, false);
}
}
}

View File

@ -139,6 +139,10 @@ void EQ::Net::Packet::PutCString(size_t offset, const char *str)
void EQ::Net::Packet::PutPacket(size_t offset, const Packet &p)
{
if (p.Length() == 0) {
return;
}
if (Length() < offset + p.Length()) {
if (!Resize(offset + p.Length())) {
throw std::out_of_range("Packet::PutPacket(), could not resize packet and would of written past the end.");
@ -150,6 +154,10 @@ void EQ::Net::Packet::PutPacket(size_t offset, const Packet &p)
void EQ::Net::Packet::PutData(size_t offset, void *data, size_t length)
{
if (length == 0) {
return;
}
if (Length() < offset + length) {
if (!Resize(offset + length)) {
throw std::out_of_range("Packet::PutData(), could not resize packet and would of written past the end.");

View File

@ -1849,6 +1849,7 @@ namespace RoF
eq->Text_Count = 4096;
memcpy(eq->Text, emu->Text, sizeof(eq->Text));
OUT(Buttons);
OUT(SoundControls);
OUT(Duration);
OUT(PopupID);
OUT(NegativeID);
@ -3929,7 +3930,7 @@ namespace RoF
if (strlen(emu->suffix))
PacketSize += strlen(emu->suffix) + 1;
bool ShowName = 1;
bool ShowName = emu->show_name;
if (emu->bodytype >= 66)
{
emu->race = 127;

View File

@ -1927,6 +1927,7 @@ namespace RoF2
eq->Text_Count = 4096;
memcpy(eq->Text, emu->Text, sizeof(eq->Text));
OUT(Buttons);
OUT(SoundControls);
OUT(Duration);
OUT(PopupID);
OUT(NegativeID);
@ -4085,7 +4086,7 @@ namespace RoF2
PacketSize += strlen(emu->DestructibleString) + 1;
}
bool ShowName = 1;
bool ShowName = emu->show_name;
if (emu->bodytype >= 66)
{
emu->race = 127;
@ -4119,6 +4120,7 @@ namespace RoF2
VARSTRUCT_ENCODE_STRING(Buffer, emu->name);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level);
// actually melee range variable, this probably screws the shit out of melee ranges :D
if (emu->DestructibleObject)
{
VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000
@ -4127,7 +4129,7 @@ namespace RoF2
{
VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height?
}
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); // 0 PC, 1 NPC etc
structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer;
@ -4158,6 +4160,7 @@ namespace RoF2
Buffer += sizeof(structs::Spawn_Struct_Bitfields);
// actually part of bitfields
uint8 OtherData = 0;
if (emu->class_ == 62) //LDoN Chest
@ -4173,6 +4176,7 @@ namespace RoF2
OtherData = OtherData | 0xe1; // Live has 0xe1 for OtherData
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
// float EmitterScalingRadius
if (emu->DestructibleObject)
{
@ -4182,6 +4186,7 @@ namespace RoF2
{
VARSTRUCT_ENCODE_TYPE(float, Buffer, -1); // unknown3
}
// int DefaultEmitterID
VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4
if (emu->DestructibleObject || emu->class_ == 62)
@ -4191,8 +4196,9 @@ namespace RoF2
VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleString);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleAppearance);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk1);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk1); // ObjectAnimationID
// these 10 are SoundIDs
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID1);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID2);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID3);
@ -4204,8 +4210,8 @@ namespace RoF2
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk5);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk6);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk7);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->DestructibleUnk8);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk9);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->DestructibleUnk8); // bInteractiveObjectCollidable
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk9); // IteractiveObjectType
}
@ -4213,6 +4219,7 @@ namespace RoF2
{
// Setting this next field to zero will cause a crash. Looking at ShowEQ, if it is zero, the bodytype field is not
// present. Will sort that out later.
// This is the CharacterPropertyHash, it can have multiple fields
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // This is a properties count field
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->bodytype);
}
@ -4232,10 +4239,10 @@ namespace RoF2
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_tattoo);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown9
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown10
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // unknown11
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); // InNonPCRaceIllusion
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // material
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // variation
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // headtype
VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->size);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->face);
@ -4243,6 +4250,7 @@ namespace RoF2
VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->runspeed);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->race);
// From MQ2: todo: create enum for this byte. Holding: Nothing=0 A RightHand Weapon=1 A Shield=2 Dual Wielding Two Weapons=3 A Spear=4 A LeftHand Weapon=5 A Two Handed Weapon=6 A bow=7
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // ShowEQ calls this 'Holding'
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity);
if (emu->NPC)
@ -4275,19 +4283,19 @@ namespace RoF2
VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle ??
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC ? 0 : 1); // unknown - Must be 1 for guild name to be shown abover players head.
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // TempPet
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // FindBits MQ2 name
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->PlayerState);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown18
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown19
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // NpcTintIndex
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // PrimaryTintIndex
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // SecondaryTintIndex
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // These do something with OP_WeaponEquip1
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // ^
if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))
{
@ -4355,12 +4363,16 @@ namespace RoF2
VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix);
}
// skipping two ints
// unknown, maybe some sort of spawn ID
// SplineID -- no idea
Buffer += 8;
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->IsMercenary);
VARSTRUCT_ENCODE_STRING(Buffer, "0000000000000000");
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff);
VARSTRUCT_ENCODE_STRING(Buffer, "0000000000000000"); // RealEstateItemGuid
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // RealEstateID
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // RealEstateItemID
// 29 zero bytes follow
// PhysicsEffects follow here ... unsure what they are but it's a count followed by a struct like {spellid, casterid, effectid, baseeffect}
Buffer += 29;
if (Buffer != (BufferStart + PacketSize))
{

View File

@ -328,38 +328,43 @@ showeq -> eqemu
sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g'
*/
// I think this is actually 5 bytes
// IDA's pseudocode reads this as 5 bytes pulled into 2 DWORDs
struct Spawn_Struct_Bitfields
{
// byte 1
/*00*/ unsigned gender:2; // Gender (0=male, 1=female, 2=monster)
/*02*/ unsigned ispet:1; // Guessed based on observing live spawns
/*03*/ unsigned afk:1; // 0=no, 1=afk
/*04*/ unsigned anon:2; // 0=normal, 1=anon, 2=roleplay
/*06*/ unsigned gm:1;
/*06*/ unsigned sneak:1;
/*07*/ unsigned sneak:1;
// byte 2
/*08*/ unsigned lfg:1;
/*09*/ unsigned unknown09:1;
/*10*/ unsigned invis:1; // May have invis & sneak the wrong way around ... not sure how to tell which is which
/*11*/ unsigned invis1:1; // GM Invis? Can only be seen with #gm on - same for the below
/*12*/ unsigned invis2:1; // This one also make the NPC/PC invis
/*13*/ unsigned invis3:1; // This one also make the NPC/PC invis
/*14*/ unsigned invis4:1; // This one also make the NPC/PC invis
/*15*/ unsigned invis6:1; // This one also make the NPC/PC invis
/*16*/ unsigned invis7:1; // This one also make the NPC/PC invis
/*17*/ unsigned invis8:1; // This one also make the NPC/PC invis
/*18*/ unsigned invis9:1; // This one also make the NPC/PC invis
/*19*/ unsigned invis10:1; // This one also make the NPC/PC invis
/*20*/ unsigned invis11:1; // This one also make the NPC/PC invis
/*21*/ unsigned invis12:1; // This one also make the NPC/PC invis
/*09*/ unsigned betabuffed:1;
/*10*/ unsigned invis:12; // there are 3000 different (non-GM) invis levels
/*22*/ unsigned linkdead:1; // 1 Toggles LD on or off after name. Correct for RoF2
/*23*/ unsigned showhelm:1;
// byte 4
/*24*/ unsigned unknown24:1; // Prefixes name with !
/*25*/ unsigned trader:1;
/*26*/ unsigned unknown26:1;
/*26*/ unsigned animationonpop:1;
/*27*/ unsigned targetable:1;
/*28*/ unsigned targetable_with_hotkey:1;
/*29*/ unsigned showname:1;
/*30*/ unsigned unknown30:1;
/*30*/ unsigned untargetable:1; // Untargetable with mouse
/*30*/ unsigned idleanimationsoff:1; // what we called statue?
/*31*/ unsigned untargetable:1; // bClickThrough
/* do these later
32 unsigned buyer:1;
33 unsigned offline:1;
34 unsigned interactiveobject:1;
35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
36 unsigned title:1;
37 unsigned suffix:1;
38 unsigned padding1:1;
39 unsigned padding2:1;
40 unsinged padding3:1;
*/
/*
// Unknown in RoF2
unsigned betabuffed:1;
@ -498,7 +503,7 @@ struct Spawn_Struct
/*0000*/ //char title[0]; // only read if(hasTitleOrSuffix & 4)
/*0000*/ //char suffix[0]; // only read if(hasTitleOrSuffix & 8)
char unknown20[8];
char unknown20[8]; // 2 ints, first unknown, 2nd SplineID
uint8 IsMercenary; // If NPC == 1 and this == 1, then the NPC name is Orange.
/*0000*/ char unknown21[55];
};
@ -2092,7 +2097,7 @@ struct OnLevelMessage_Struct {
/*0000*/ uint32 ButtonName1_Count;
/*0000*/ char ButtonName1[25];
/*0000*/ uint8 Buttons;
/*0000*/ uint8 Unknown4275; // Something to do with audio controls
/*0000*/ uint8 SoundControls; // Something to do with audio controls
/*0000*/ uint32 Duration;
/*0000*/ uint32 PopupID; // If none zero, a response packet with 00 00 00 00 <PopupID> is returned on clicking the left button
/*0000*/ uint32 NegativeID; // If none zero, a response packet with 01 00 00 00 <NegativeID> is returned on clicking the right button

View File

@ -339,18 +339,7 @@ struct Spawn_Struct_Bitfields
/*06*/ unsigned sneak:1;
/*08*/ unsigned lfg:1;
/*09*/ unsigned unknown09:1;
/*10*/ unsigned invis:1; // May have invis & sneak the wrong way around ... not sure how to tell which is which
/*11*/ unsigned invis1:1; // GM Invis? Can only be seen with #gm on - same for the below
/*12*/ unsigned invis2:1; // This one also make the NPC/PC invis
/*13*/ unsigned invis3:1; // This one also make the NPC/PC invis
/*14*/ unsigned invis4:1; // This one also make the NPC/PC invis
/*15*/ unsigned invis6:1; // This one also make the NPC/PC invis
/*16*/ unsigned invis7:1; // This one also make the NPC/PC invis
/*17*/ unsigned invis8:1; // This one also make the NPC/PC invis
/*18*/ unsigned invis9:1; // This one also make the NPC/PC invis
/*19*/ unsigned invis10:1; // This one also make the NPC/PC invis
/*20*/ unsigned invis11:1; // This one also make the NPC/PC invis
/*21*/ unsigned invis12:1; // This one also make the NPC/PC invis
/*10*/ unsigned invis:12; // there are 3000 different (non-GM) invis levels
/*22*/ unsigned linkdead:1; // 1 Toggles LD on or off after name. Correct for RoF
/*23*/ unsigned showhelm:1;
/*24*/ unsigned unknown24:1; // Prefixes name with !
@ -2122,7 +2111,7 @@ struct OnLevelMessage_Struct {
/*0000*/ uint32 ButtonName1_Count;
/*0000*/ char ButtonName1[25];
/*0000*/ uint8 Buttons;
/*0000*/ uint8 Unknown4275; // Something to do with audio controls
/*0000*/ uint8 SoundControls; // Something to do with audio controls
/*0000*/ uint32 Duration;
/*0000*/ uint32 PopupID; // If none zero, a response packet with 00 00 00 00 <PopupID> is returned on clicking the left button
/*0000*/ uint32 NegativeID; // If none zero, a response packet with 01 00 00 00 <NegativeID> is returned on clicking the right button

View File

@ -1373,6 +1373,7 @@ namespace SoD
memcpy(eq->Title, emu->Title, sizeof(eq->Title));
memcpy(eq->Text, emu->Text, sizeof(eq->Text));
OUT(Buttons);
OUT(SoundControls);
OUT(Duration);
OUT(PopupID);
OUT(NegativeID);
@ -2559,7 +2560,7 @@ namespace SoD
PacketSize += strlen(emu->DestructibleString) + 1;
}
bool ShowName = 1;
bool ShowName = emu->show_name;
if (emu->bodytype >= 66)
{
emu->race = 127;
@ -3291,73 +3292,60 @@ namespace SoD
switch (eq->command)
{
case 0x04:
emu->command = 0x00; // /pet health
case 1: // back off
emu->command = 28;
break;
case 0x10:
emu->command = 0x01; // /pet leader
case 2: // get lost
emu->command = 29;
break;
case 0x07:
emu->command = 0x02; // /pet attack or Pet Window
case 3: // as you were ???
emu->command = 4; // fuck it follow
break;
case 0x03: // Case Guessed
emu->command = 0x03; // /pet qattack
case 0x08:
emu->command = 0x04; // /pet follow or Pet Window
case 4: // report HP
emu->command = 0;
break;
case 0x05:
emu->command = 0x05; // /pet guard or Pet Window
case 5: // guard here
emu->command = 5;
break;
case 0x09:
emu->command = 0x07; // /pet sit or Pet Window
case 6: // guard me
emu->command = 4; // fuck it follow
break;
case 0x0a:
emu->command = 0x08; // /pet stand or Pet Window
case 7: // attack
emu->command = 2;
break;
case 0x06:
emu->command = 0x1e; // /pet guard me
case 8: // follow
emu->command = 4;
break;
case 0x0f: // Case Made Up
emu->command = 0x09; // /pet stop
case 9: // sit down
emu->command = 7;
break;
case 0x0b:
emu->command = 0x0d; // /pet taunt or Pet Window
case 10: // stand up
emu->command = 8;
break;
case 0x0e:
emu->command = 0x0e; // /pet notaunt or Pet Window
case 11: // taunt toggle
emu->command = 12;
break;
case 0x0c:
emu->command = 0x0f; // /pet hold
case 12: // hold toggle
emu->command = 15;
break;
case 0x1b:
emu->command = 0x10; // /pet hold on
case 13: // taunt on
emu->command = 13;
break;
case 0x1c:
emu->command = 0x11; // /pet hold off
case 14: // no taunt
emu->command = 14;
break;
case 0x11:
emu->command = 0x12; // Slumber?
// 15 is target, doesn't send packet
case 16: // leader
emu->command = 1;
break;
case 0x12:
emu->command = 0x15; // /pet no cast
case 17: // feign
emu->command = 27;
break;
case 0x0d: // Case Made Up
emu->command = 0x16; // Pet Window No Cast
case 18: // no cast toggle
emu->command = 21;
break;
case 0x13:
emu->command = 0x18; // /pet focus
break;
case 0x19:
emu->command = 0x19; // /pet focus on
break;
case 0x1a:
emu->command = 0x1a; // /pet focus off
break;
case 0x01:
emu->command = 0x1c; // /pet back off
break;
case 0x02:
emu->command = 0x1d; // /pet get lost
case 19: // focus toggle
emu->command = 24;
break;
default:
emu->command = eq->command;

View File

@ -250,8 +250,7 @@ struct Spawn_Struct_Bitfields
unsigned sneak:1;
unsigned lfg:1;
unsigned padding5:1;
unsigned invis:1; // 0 = visible, 1 = invis/sneaking
unsigned padding7:11;
unsigned invis:12; // there are 3000 different (non-GM) invis levels
unsigned gm:1;
unsigned anon:2; // 0=normal, 1=anon, 2=roleplay
unsigned gender:2; // Gender (0=male, 1=female, 2=monster)
@ -1760,7 +1759,7 @@ struct OnLevelMessage_Struct {
/*4224*/ char ButtonName0[25]; // If Buttons = 1, these two are the text for the left and right buttons respectively
/*4249*/ char ButtonName1[25];
/*4274*/ uint8 Buttons;
/*4275*/ uint8 Unknown4275; // Something to do with audio controls
/*4275*/ uint8 SoundControls; // Something to do with audio controls
/*4276*/ uint32 Duration;
/*4280*/ uint32 PopupID; // If none zero, a response packet with 00 00 00 00 <PopupID> is returned on clicking the left button
/*4284*/ uint32 NegativeID; // If none zero, a response packet with 01 00 00 00 <NegativeID> is returned on clicking the right button

View File

@ -2097,7 +2097,7 @@ namespace SoF
int k;
for (r = 0; r < entrycount; r++, eq++, emu++) {
eq->showname = 1; //New Field - Toggles Name Display on or off - 0 = off, 1 = on
eq->showname = emu->show_name ? 1 : 0; //New Field - Toggles Name Display on or off - 0 = off, 1 = on
eq->linkdead = 0; //New Field - Toggles LD on or off after name - 0 = off, 1 = on
eq->statue = 0; //New Field - 1 freezes animation
eq->showhelm = emu->showhelm;
@ -2136,10 +2136,10 @@ namespace SoF
eq->findable = emu->findable;
if (emu->bodytype >= 66)
{
eq->bodytype = 11; //non-targetable
eq->showname = 0; //no visible name
eq->race = 127; //invisible man
eq->gender = 0; //invisible men are gender 0
eq->bodytype = 11; //non-targetable
eq->showname = 0; //no visible name
eq->race = 127; //invisible man
eq->gender = 0; //invisible men are gender 0
}
else
{
@ -2675,73 +2675,60 @@ namespace SoF
switch (eq->command)
{
case 0x04:
emu->command = 0x00; // /pet health
case 1: // back off
emu->command = 28;
break;
case 0x10:
emu->command = 0x01; // /pet leader
case 2: // get lost
emu->command = 29;
break;
case 0x07:
emu->command = 0x02; // /pet attack or Pet Window
case 3: // as you were ???
emu->command = 4; // fuck it follow
break;
case 0x03: // Case Guessed
emu->command = 0x03; // /pet qattack
case 0x08:
emu->command = 0x04; // /pet follow or Pet Window
case 4: // report HP
emu->command = 0;
break;
case 0x05:
emu->command = 0x05; // /pet guard or Pet Window
case 5: // guard here
emu->command = 5;
break;
case 0x09:
emu->command = 0x07; // /pet sit or Pet Window
case 6: // guard me
emu->command = 4; // fuck it follow
break;
case 0x0a:
emu->command = 0x08; // /pet stand or Pet Window
case 7: // attack
emu->command = 2;
break;
case 0x06:
emu->command = 0x1e; // /pet guard me
case 8: // follow
emu->command = 4;
break;
case 0x0f: // Case Made Up
emu->command = 0x09; // Stop?
case 9: // sit down
emu->command = 7;
break;
case 0x0b:
emu->command = 0x0d; // /pet taunt or Pet Window
case 10: // stand up
emu->command = 8;
break;
case 0x0e:
emu->command = 0x0e; // /pet notaunt or Pet Window
case 11: // taunt toggle
emu->command = 12;
break;
case 0x0c:
emu->command = 0x0f; // /pet hold
case 12: // hold toggle
emu->command = 15;
break;
case 0x1b:
emu->command = 0x10; // /pet hold on
case 13: // taunt on
emu->command = 13;
break;
case 0x1c:
emu->command = 0x11; // /pet hold off
case 14: // no taunt
emu->command = 14;
break;
case 0x11:
emu->command = 0x12; // Slumber?
// 15 is target, doesn't send packet
case 16: // leader
emu->command = 1;
break;
case 0x12:
emu->command = 0x15; // /pet no cast
case 17: // feign
emu->command = 27;
break;
case 0x0d: // Case Made Up
emu->command = 0x16; // Pet Window No Cast
case 18: // no cast toggle
emu->command = 21;
break;
case 0x13:
emu->command = 0x18; // /pet focus
break;
case 0x19:
emu->command = 0x19; // /pet focus on
break;
case 0x1a:
emu->command = 0x1a; // /pet focus off
break;
case 0x01:
emu->command = 0x1c; // /pet back off
break;
case 0x02:
emu->command = 0x1d; // /pet get lost
case 19: // focus toggle
emu->command = 24;
break;
default:
emu->command = eq->command;

View File

@ -2030,73 +2030,60 @@ namespace Titanium
switch (eq->command)
{
case 0x04:
emu->command = 0x00; // /pet health
case 1: // back off
emu->command = 28;
break;
case 0x10:
emu->command = 0x01; // /pet leader
case 2: // get lost
emu->command = 29;
break;
case 0x07:
emu->command = 0x02; // /pet attack or Pet Window
case 3: // as you were ???
emu->command = 4; // fuck it follow
break;
case 0x03: // Case Guessed
emu->command = 0x03; // /pet qattack
case 0x08:
emu->command = 0x04; // /pet follow or Pet Window
case 4: // report HP
emu->command = 0;
break;
case 0x05:
emu->command = 0x05; // /pet guard or Pet Window
case 5: // guard here
emu->command = 5;
break;
case 0x09:
emu->command = 0x07; // /pet sit or Pet Window
case 6: // guard me
emu->command = 4; // fuck it follow
break;
case 0x0a:
emu->command = 0x08; // /pet stand or Pet Window
case 7: // attack
emu->command = 2;
break;
case 0x06:
emu->command = 0x1e; // /pet guard me
case 8: // follow
emu->command = 4;
break;
case 0x0f: // Case Made Up
emu->command = 0x09; // Stop?
case 9: // sit down
emu->command = 7;
break;
case 0x0b:
emu->command = 0x0d; // /pet taunt or Pet Window
case 10: // stand up
emu->command = 8;
break;
case 0x0e:
emu->command = 0x0e; // /pet notaunt or Pet Window
case 11: // taunt toggle
emu->command = 12;
break;
case 0x0c:
emu->command = 0x0f; // /pet hold
case 12: // hold toggle
emu->command = 15;
break;
case 0x1b:
emu->command = 0x10; // /pet hold on
case 13: // taunt on
emu->command = 13;
break;
case 0x1c:
emu->command = 0x11; // /pet hold off
case 14: // no taunt
emu->command = 14;
break;
case 0x11:
emu->command = 0x12; // Slumber?
// 15 is target, doesn't send packet
case 16: // leader
emu->command = 1;
break;
case 0x12:
emu->command = 0x15; // /pet no cast
case 17: // feign
emu->command = 27;
break;
case 0x0d: // Case Made Up
emu->command = 0x16; // Pet Window No Cast
case 18: // no cast toggle
emu->command = 21;
break;
case 0x13:
emu->command = 0x18; // /pet focus
break;
case 0x19:
emu->command = 0x19; // /pet focus on
break;
case 0x1a:
emu->command = 0x1a; // /pet focus off
break;
case 0x01:
emu->command = 0x1c; // /pet back off
break;
case 0x02:
emu->command = 0x1d; // /pet get lost
case 19: // focus toggle
emu->command = 24;
break;
default:
emu->command = eq->command;

View File

@ -1607,6 +1607,7 @@ namespace UF
memcpy(eq->Title, emu->Title, sizeof(eq->Title));
memcpy(eq->Text, emu->Text, sizeof(eq->Text));
OUT(Buttons);
OUT(SoundControls);
OUT(Duration);
OUT(PopupID);
OUT(NegativeID);
@ -2843,7 +2844,7 @@ namespace UF
PacketSize += strlen(emu->DestructibleString) + 1;
}
bool ShowName = 1;
bool ShowName = emu->show_name;
if (emu->bodytype >= 66)
{
emu->race = 127;

View File

@ -250,8 +250,7 @@ struct Spawn_Struct_Bitfields
unsigned sneak:1;
unsigned lfg:1;
unsigned padding5:1;
unsigned invis:1; // 0 = visible, 1 = invis/sneaking
unsigned padding7:11;
unsigned invis:12; // there are 3000 different (non-GM) invis levels
unsigned gm:1;
unsigned anon:2; // 0=normal, 1=anon, 2=roleplay
unsigned gender:2; // Gender (0=male, 1=female, 2=monster)
@ -1801,7 +1800,7 @@ struct OnLevelMessage_Struct {
/*4224*/ char ButtonName0[25]; // If Buttons = 1, these two are the text for the left and right buttons respectively
/*4249*/ char ButtonName1[25];
/*4274*/ uint8 Buttons;
/*4275*/ uint8 Unknown4275; // Something to do with audio controls
/*4275*/ uint8 SoundControls; // Something to do with audio controls
/*4276*/ uint32 Duration;
/*4280*/ uint32 PopupID; // If none zero, a response packet with 00 00 00 00 <PopupID> is returned on clicking the left button
/*4284*/ uint32 NegativeID; // If none zero, a response packet with 01 00 00 00 <NegativeID> is returned on clicking the right button

View File

@ -134,7 +134,6 @@ RULE_INT(Character, TradeskillUpMakePoison, 2) // Make Poison skillup rate adjus
RULE_INT(Character, TradeskillUpPottery, 4) // Pottery skillup rate adjust. Lower is faster.
RULE_INT(Character, TradeskillUpResearch, 1) // Research skillup rate adjust. Lower is faster.
RULE_INT(Character, TradeskillUpTinkering, 2) // Tinkering skillup rate adjust. Lower is faster.
RULE_BOOL(Character, SpamHPUpdates, false) // if your server has stupid amounts of HP that causes client display issues, turn this on!
RULE_BOOL(Character, MarqueeHPUpdates, false) // Will show Health % in center of screen < 100%
RULE_INT(Character, IksarCommonTongue, 95) // 95 By default (live-like?)
RULE_INT(Character, OgreCommonTongue, 95) // 95 By default (live-like?)
@ -151,6 +150,7 @@ RULE_BOOL(Character, AllowMQTarget, false) // Disables putting players in the 'h
RULE_BOOL(Character, UseOldBindWound, false) // Uses the original bind wound behavior
RULE_BOOL(Character, GrantHoTTOnCreate, false) // Grant Health of Target's Target leadership AA on character creation
RULE_BOOL(Character, UseOldConSystem, false) // Grant Health of Target's Target leadership AA on character creation
RULE_BOOL(Character, OPClientUpdateVisualDebug, false) // Shows a pulse and forward directional particle each time the client sends its position to server
RULE_CATEGORY_END()
RULE_CATEGORY(Mercs)
@ -235,7 +235,6 @@ RULE_BOOL(World, StartZoneSameAsBindOnCreation, true) //Should the start zone AL
RULE_CATEGORY_END()
RULE_CATEGORY(Zone)
RULE_INT(Zone, NPCPositonUpdateTicCount, 32) //ms between intervals of sending a position update to the entire zone.
RULE_INT(Zone, ClientLinkdeadMS, 180000) //the time a client remains link dead on the server after a sudden disconnection
RULE_INT(Zone, GraveyardTimeMS, 1200000) //ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone
RULE_BOOL(Zone, EnableShadowrest, 1) // enables or disables the shadowrest zone feature for player corpses. Default is turned on.
@ -274,6 +273,8 @@ RULE_BOOL(Map, FixPathingZWhenLoading, true) //increases zone boot times a bit
RULE_BOOL(Map, FixPathingZAtWaypoints, false) //alternative to `WhenLoading`, accomplishes the same thing but does it at each waypoint instead of once at boot time.
RULE_BOOL(Map, FixPathingZWhenMoving, false) //very CPU intensive, but helps hopping with widely spaced waypoints.
RULE_BOOL(Map, FixPathingZOnSendTo, false) //try to repair Z coords in the SendTo routine as well.
RULE_BOOL(Map, FixZWhenMoving, true) // Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor)
RULE_BOOL(Map, MobZVisualDebug, false) // Displays spell effects determining whether or not NPC is hitting Best Z calcs (blue for hit, red for miss)
RULE_REAL(Map, FixPathingZMaxDeltaMoving, 20) //at runtime while pathing: max change in Z to allow the BestZ code to apply.
RULE_REAL(Map, FixPathingZMaxDeltaWaypoint, 20) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply.
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20) //at runtime in SendTo: max change in Z to allow the BestZ code to apply.
@ -289,7 +290,7 @@ RULE_BOOL(Pathing, AggroReturnToGrid, true) // Enable pathing for aggroed roamin
RULE_BOOL(Pathing, Guard, true) // Enable pathing for mobs moving to their guard point.
RULE_BOOL(Pathing, Find, true) // Enable pathing for FindPerson requests from the client.
RULE_BOOL(Pathing, Fear, true) // Enable pathing for fear
RULE_REAL(Pathing, ZDiffThreshold, 10) // If a mob las LOS to it's target, it will run to it if the Z difference is < this.
RULE_REAL(Pathing, ZDiffThresholdNew, 80) // If a mob las LOS to it's target, it will run to it if the Z difference is < this.
RULE_INT(Pathing, LOSCheckFrequency, 1000) // A mob will check for LOS to it's target this often (milliseconds).
RULE_INT(Pathing, RouteUpdateFrequencyShort, 1000) // How often a new route will be calculated if the target has moved.
RULE_INT(Pathing, RouteUpdateFrequencyLong, 5000) // How often a new route will be calculated if the target has moved.
@ -348,6 +349,7 @@ RULE_INT(Spells, MaxTotalSlotsNPC, 60) // default to Tit's limit
RULE_INT(Spells, MaxTotalSlotsPET, 30) // default to Tit's limit
RULE_BOOL (Spells, EnableBlockedBuffs, true)
RULE_INT(Spells, ReflectType, 3) //0 = disabled, 1 = single target player spells only, 2 = all player spells, 3 = all single target spells, 4 = all spells
RULE_BOOL(Spells, ReflectMessagesClose, true) // Live functionality is for Reflect messages to show to players within close proximity, false shows just player reflecting
RULE_INT(Spells, VirusSpreadDistance, 30) // The distance a viral spell will jump to its next victim
RULE_BOOL(Spells, LiveLikeFocusEffects, true) // Determines whether specific healing, dmg and mana reduction focuses are randomized
RULE_INT(Spells, BaseImmunityLevel, 55) // The level that targets start to be immune to stun, fear and mez spells with a max level of 0.
@ -397,6 +399,7 @@ RULE_BOOL(Spells, FlatItemExtraSpellAmt, false) // allow SpellDmg stat to affect
RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false) // ignore the 5 level spread on applying SpellDmg
RULE_BOOL(Spells, AllowItemTGB, false) // TGB doesn't work with items on live, custom servers want it though
RULE_BOOL(Spells, NPCInnateProcOverride, true) // NPC innate procs override the target type to single target.
RULE_BOOL(Spells, OldRainTargets, false) // use old incorrectly implemented max targets for rains
RULE_CATEGORY_END()
RULE_CATEGORY(Combat)

View File

@ -189,6 +189,7 @@
#define ServerOP_ReloadWorld 0x4009
#define ServerOP_ReloadLogs 0x4010
#define ServerOP_ReloadPerlExportSettings 0x4011
#define ServerOP_CZSetEntityVariableByClientName 0x4012
/* Query Server OP Codes */
#define ServerOP_QSPlayerLogTrades 0x5010
#define ServerOP_QSPlayerLogHandins 0x5011
@ -1263,6 +1264,12 @@ struct CZSetEntVarByNPCTypeID_Struct {
char m_var[256];
};
struct CZSetEntVarByClientName_Struct {
char CharName[64];
char id[256];
char m_var[256];
};
struct ReloadWorld_Struct{
uint32 Option;
};

View File

@ -1674,6 +1674,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
for (y = 0; y < 16; y++)
sp[tempid].deities[y]=atoi(row[126+y]);
sp[tempid].new_icon=atoi(row[144]);
sp[tempid].uninterruptable=atoi(row[146]) != 0;
sp[tempid].ResistDiff=atoi(row[147]);
sp[tempid].dot_stacking_exempt = atoi(row[148]) != 0;

View File

@ -468,7 +468,7 @@ typedef enum {
#define SE_Blank 254 // implemented
#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield
#define SE_ShroudofStealth 256 // implemented
#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold
#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold - official name is GivePetHold
#define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab
#define SE_CombatStability 259 // implemented[AA] - damage mitigation
#define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType
@ -478,7 +478,7 @@ typedef enum {
#define SE_HastenedAASkill 264 // implemented
#define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled
#define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon.
#define SE_PetDiscipline2 267 // *not implemented - /pet focus, /pet no cast
#define SE_AddPetCommand 267 // implemented - sets command base2 to base1
#define SE_ReduceTradeskillFail 268 // implemented - reduces chance to fail with given tradeskill by a percent chance
#define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound.
#define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo)
@ -562,9 +562,9 @@ typedef enum {
#define SE_LimitManaMin 348 // implemented
#define SE_ShieldEquipDmgMod 349 // implemented[AA] Increase melee base damage (indirectly increasing hate) when wearing a shield.
#define SE_ManaBurn 350 // implemented - Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana.
//#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this
//#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755)
//#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect
#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this
#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755)
#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect
//#define SE_DeactivateAllTraps 354 // *not implemented - looks to be some type of invulnerability? Test DAT (8757)
//#define SE_LearnTrap 355 // *not implemented - looks to be some type of invulnerability? Test LT (8758)
//#define SE_ChangeTriggerType 356 // not used
@ -757,7 +757,7 @@ struct SPDat_Spell_Struct
// -- DIETY_BERTOXXULOUS ... DIETY_VEESHAN
/* 142 */ //int8 npc_no_cast; // 142: between 0 & 100 -- NPC_NO_CAST
/* 143 */ //int ai_pt_bonus; // 143: always set to 0, client doesn't save this -- AI_PT_BONUS
/* 144 */ //int16 new_icon // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon -- NEW_ICON
/* 144 */ int16 new_icon; // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon -- NEW_ICON
/* 145 */ //int16 spellanim; // Doesn't look like it's the same as #doanim, so not sure what this is, particles I think -- SPELL_EFFECT_INDEX
/* 146 */ bool uninterruptable; // Looks like anything != 0 is uninterruptable. Values are mostly -1, 0, & 1 (Fetid Breath = 90?) -- NO_INTERRUPT
/* 147 */ int16 ResistDiff; // -- RESIST_MOD

49
common/util/directory.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "directory.h"
#ifdef _WIN32
#include "win_dirent.h"
#else
#include <dirent.h>
#endif
struct EQ::Directory::impl {
DIR *m_dir;
};
EQ::Directory::Directory(const std::string &path)
{
m_impl = new impl;
m_impl->m_dir = opendir(path.c_str());
}
EQ::Directory::~Directory()
{
if (m_impl->m_dir) {
closedir(m_impl->m_dir);
}
delete m_impl;
}
bool EQ::Directory::Exists()
{
return m_impl->m_dir != nullptr;
}
void EQ::Directory::GetFiles(std::vector<std::string>& files)
{
if (m_impl->m_dir) {
struct dirent *ent;
while ((ent = readdir(m_impl->m_dir)) != nullptr) {
switch (ent->d_type) {
case DT_REG:
files.push_back(ent->d_name);
break;
default:
break;
}
}
rewinddir(m_impl->m_dir);
}
}

19
common/util/directory.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <string>
#include <vector>
namespace EQ {
class Directory
{
public:
Directory(const std::string &path);
~Directory();
bool Exists();
void GetFiles(std::vector<std::string> &files);
private:
struct impl;
impl *m_impl;
};
}

928
common/util/win_dirent.h Normal file
View File

@ -0,0 +1,928 @@
/*
* Dirent interface for Microsoft Visual Studio
* Version 1.21
*
* Copyright (C) 2006-2012 Toni Ronkko
* This file is part of dirent. Dirent may be freely distributed
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#ifndef DIRENT_H
#define DIRENT_H
/*
* Include windows.h without Windows Sockets 1.1 to prevent conflicts with
* Windows Sockets 2.0.
*/
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
/* Indicates that d_type field is available in dirent structure */
#define _DIRENT_HAVE_D_TYPE
/* Indicates that d_namlen field is available in dirent structure */
#define _DIRENT_HAVE_D_NAMLEN
/* Entries missing from MSVC 6.0 */
#if !defined(FILE_ATTRIBUTE_DEVICE)
# define FILE_ATTRIBUTE_DEVICE 0x40
#endif
/* File type and permission flags for stat(), general mask */
#if !defined(S_IFMT)
# define S_IFMT _S_IFMT
#endif
/* Directory bit */
#if !defined(S_IFDIR)
# define S_IFDIR _S_IFDIR
#endif
/* Character device bit */
#if !defined(S_IFCHR)
# define S_IFCHR _S_IFCHR
#endif
/* Pipe bit */
#if !defined(S_IFFIFO)
# define S_IFFIFO _S_IFFIFO
#endif
/* Regular file bit */
#if !defined(S_IFREG)
# define S_IFREG _S_IFREG
#endif
/* Read permission */
#if !defined(S_IREAD)
# define S_IREAD _S_IREAD
#endif
/* Write permission */
#if !defined(S_IWRITE)
# define S_IWRITE _S_IWRITE
#endif
/* Execute permission */
#if !defined(S_IEXEC)
# define S_IEXEC _S_IEXEC
#endif
/* Pipe */
#if !defined(S_IFIFO)
# define S_IFIFO _S_IFIFO
#endif
/* Block device */
#if !defined(S_IFBLK)
# define S_IFBLK 0
#endif
/* Link */
#if !defined(S_IFLNK)
# define S_IFLNK 0
#endif
/* Socket */
#if !defined(S_IFSOCK)
# define S_IFSOCK 0
#endif
/* Read user permission */
#if !defined(S_IRUSR)
# define S_IRUSR S_IREAD
#endif
/* Write user permission */
#if !defined(S_IWUSR)
# define S_IWUSR S_IWRITE
#endif
/* Execute user permission */
#if !defined(S_IXUSR)
# define S_IXUSR 0
#endif
/* Read group permission */
#if !defined(S_IRGRP)
# define S_IRGRP 0
#endif
/* Write group permission */
#if !defined(S_IWGRP)
# define S_IWGRP 0
#endif
/* Execute group permission */
#if !defined(S_IXGRP)
# define S_IXGRP 0
#endif
/* Read others permission */
#if !defined(S_IROTH)
# define S_IROTH 0
#endif
/* Write others permission */
#if !defined(S_IWOTH)
# define S_IWOTH 0
#endif
/* Execute others permission */
#if !defined(S_IXOTH)
# define S_IXOTH 0
#endif
/* Maximum length of file name */
#if !defined(PATH_MAX)
# define PATH_MAX MAX_PATH
#endif
#if !defined(FILENAME_MAX)
# define FILENAME_MAX MAX_PATH
#endif
#if !defined(NAME_MAX)
# define NAME_MAX FILENAME_MAX
#endif
/* File type flags for d_type */
#define DT_UNKNOWN 0
#define DT_REG S_IFREG
#define DT_DIR S_IFDIR
#define DT_FIFO S_IFIFO
#define DT_SOCK S_IFSOCK
#define DT_CHR S_IFCHR
#define DT_BLK S_IFBLK
#define DT_LNK S_IFLNK
/* Macros for converting between st_mode and d_type */
#define IFTODT(mode) ((mode) & S_IFMT)
#define DTTOIF(type) (type)
/*
* File type macros. Note that block devices, sockets and links cannot be
* distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
* only defined for compatibility. These macros should always return false
* on Windows.
*/
#if !defined(S_ISFIFO)
# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
#endif
#if !defined(S_ISDIR)
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
#if !defined(S_ISREG)
# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
#endif
#if !defined(S_ISLNK)
# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
#endif
#if !defined(S_ISSOCK)
# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
#endif
#if !defined(S_ISCHR)
# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
#endif
#if !defined(S_ISBLK)
# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
#endif
/* Return the exact length of d_namlen without zero terminator */
#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
/* Return number of bytes needed to store d_namlen */
#define _D_ALLOC_NAMLEN(p) (PATH_MAX)
#ifdef __cplusplus
extern "C" {
#endif
/* Wide-character version */
struct _wdirent {
/* Always zero */
long d_ino;
/* Structure size */
unsigned short d_reclen;
/* Length of name without \0 */
size_t d_namlen;
/* File type */
int d_type;
/* File name */
wchar_t d_name[PATH_MAX];
};
typedef struct _wdirent _wdirent;
struct _WDIR {
/* Current directory entry */
struct _wdirent ent;
/* Private file data */
WIN32_FIND_DATAW data;
/* True if data is valid */
int cached;
/* Win32 search handle */
HANDLE handle;
/* Initial directory name */
wchar_t *patt;
};
typedef struct _WDIR _WDIR;
static _WDIR *_wopendir (const wchar_t *dirname);
static struct _wdirent *_wreaddir (_WDIR *dirp);
static int _wclosedir (_WDIR *dirp);
static void _wrewinddir (_WDIR* dirp);
/* For compatibility with Symbian */
#define wdirent _wdirent
#define WDIR _WDIR
#define wopendir _wopendir
#define wreaddir _wreaddir
#define wclosedir _wclosedir
#define wrewinddir _wrewinddir
/* Multi-byte character versions */
struct dirent {
/* Always zero */
long d_ino;
/* Structure size */
unsigned short d_reclen;
/* Length of name without \0 */
size_t d_namlen;
/* File type */
int d_type;
/* File name */
char d_name[PATH_MAX];
};
typedef struct dirent dirent;
struct DIR {
struct dirent ent;
struct _WDIR *wdirp;
};
typedef struct DIR DIR;
static DIR *opendir (const char *dirname);
static struct dirent *readdir (DIR *dirp);
static int closedir (DIR *dirp);
static void rewinddir (DIR* dirp);
/* Internal utility functions */
static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
static int dirent_mbstowcs_s(
size_t *pReturnValue,
wchar_t *wcstr,
size_t sizeInWords,
const char *mbstr,
size_t count);
static int dirent_wcstombs_s(
size_t *pReturnValue,
char *mbstr,
size_t sizeInBytes,
const wchar_t *wcstr,
size_t count);
static void dirent_set_errno (int error);
/*
* Open directory stream DIRNAME for read and return a pointer to the
* internal working area that is used to retrieve individual directory
* entries.
*/
static _WDIR*
_wopendir(
const wchar_t *dirname)
{
_WDIR *dirp = NULL;
int error;
/* Must have directory name */
if (dirname == NULL || dirname[0] == '\0') {
dirent_set_errno (ENOENT);
return NULL;
}
/* Allocate new _WDIR structure */
dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
if (dirp != NULL) {
DWORD n;
/* Reset _WDIR structure */
dirp->handle = INVALID_HANDLE_VALUE;
dirp->patt = NULL;
dirp->cached = 0;
/* Compute the length of full path plus zero terminator
*
* Note that on WinRT there's no way to convert relative paths
* into absolute paths, so just assume its an absolute path.
*/
# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
n = wcslen(dirname);
# else
n = GetFullPathNameW (dirname, 0, NULL, NULL);
# endif
/* Allocate room for absolute directory name and search pattern */
dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
if (dirp->patt) {
/*
* Convert relative directory name to an absolute one. This
* allows rewinddir() to function correctly even when current
* working directory is changed between opendir() and rewinddir().
*
* Note that on WinRT there's no way to convert relative paths
* into absolute paths, so just assume its an absolute path.
*/
# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
wcsncpy_s(dirp->patt, n+1, dirname, n);
# else
n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
# endif
if (n > 0) {
wchar_t *p;
/* Append search pattern \* to the directory name */
p = dirp->patt + n;
if (dirp->patt < p) {
switch (p[-1]) {
case '\\':
case '/':
case ':':
/* Directory ends in path separator, e.g. c:\temp\ */
/*NOP*/;
break;
default:
/* Directory name doesn't end in path separator */
*p++ = '\\';
}
}
*p++ = '*';
*p = '\0';
/* Open directory stream and retrieve the first entry */
if (dirent_first (dirp)) {
/* Directory stream opened successfully */
error = 0;
} else {
/* Cannot retrieve first entry */
error = 1;
dirent_set_errno (ENOENT);
}
} else {
/* Cannot retrieve full path name */
dirent_set_errno (ENOENT);
error = 1;
}
} else {
/* Cannot allocate memory for search pattern */
error = 1;
}
} else {
/* Cannot allocate _WDIR structure */
error = 1;
}
/* Clean up in case of error */
if (error && dirp) {
_wclosedir (dirp);
dirp = NULL;
}
return dirp;
}
/*
* Read next directory entry. The directory entry is returned in dirent
* structure in the d_name field. Individual directory entries returned by
* this function include regular files, sub-directories, pseudo-directories
* "." and ".." as well as volume labels, hidden files and system files.
*/
static struct _wdirent*
_wreaddir(
_WDIR *dirp)
{
WIN32_FIND_DATAW *datap;
struct _wdirent *entp;
/* Read next directory entry */
datap = dirent_next (dirp);
if (datap) {
size_t n;
DWORD attr;
/* Pointer to directory entry to return */
entp = &dirp->ent;
/*
* Copy file name as wide-character string. If the file name is too
* long to fit in to the destination buffer, then truncate file name
* to PATH_MAX characters and zero-terminate the buffer.
*/
n = 0;
while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) {
entp->d_name[n] = datap->cFileName[n];
n++;
}
dirp->ent.d_name[n] = 0;
/* Length of file name excluding zero terminator */
entp->d_namlen = n;
/* File type */
attr = datap->dwFileAttributes;
if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
entp->d_type = DT_CHR;
} else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
entp->d_type = DT_DIR;
} else {
entp->d_type = DT_REG;
}
/* Reset dummy fields */
entp->d_ino = 0;
entp->d_reclen = sizeof (struct _wdirent);
} else {
/* Last directory entry read */
entp = NULL;
}
return entp;
}
/*
* Close directory stream opened by opendir() function. This invalidates the
* DIR structure as well as any directory entry read previously by
* _wreaddir().
*/
static int
_wclosedir(
_WDIR *dirp)
{
int ok;
if (dirp) {
/* Release search handle */
if (dirp->handle != INVALID_HANDLE_VALUE) {
FindClose (dirp->handle);
dirp->handle = INVALID_HANDLE_VALUE;
}
/* Release search pattern */
if (dirp->patt) {
free (dirp->patt);
dirp->patt = NULL;
}
/* Release directory structure */
free (dirp);
ok = /*success*/0;
} else {
/* Invalid directory stream */
dirent_set_errno (EBADF);
ok = /*failure*/-1;
}
return ok;
}
/*
* Rewind directory stream such that _wreaddir() returns the very first
* file name again.
*/
static void
_wrewinddir(
_WDIR* dirp)
{
if (dirp) {
/* Release existing search handle */
if (dirp->handle != INVALID_HANDLE_VALUE) {
FindClose (dirp->handle);
}
/* Open new search handle */
dirent_first (dirp);
}
}
/* Get first directory entry (internal) */
static WIN32_FIND_DATAW*
dirent_first(
_WDIR *dirp)
{
WIN32_FIND_DATAW *datap;
/* Open directory and retrieve the first entry */
dirp->handle = FindFirstFileExW(
dirp->patt, FindExInfoStandard, &dirp->data,
FindExSearchNameMatch, NULL, 0);
if (dirp->handle != INVALID_HANDLE_VALUE) {
/* a directory entry is now waiting in memory */
datap = &dirp->data;
dirp->cached = 1;
} else {
/* Failed to re-open directory: no directory entry in memory */
dirp->cached = 0;
datap = NULL;
}
return datap;
}
/* Get next directory entry (internal) */
static WIN32_FIND_DATAW*
dirent_next(
_WDIR *dirp)
{
WIN32_FIND_DATAW *p;
/* Get next directory entry */
if (dirp->cached != 0) {
/* A valid directory entry already in memory */
p = &dirp->data;
dirp->cached = 0;
} else if (dirp->handle != INVALID_HANDLE_VALUE) {
/* Get the next directory entry from stream */
if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
/* Got a file */
p = &dirp->data;
} else {
/* The very last entry has been processed or an error occured */
FindClose (dirp->handle);
dirp->handle = INVALID_HANDLE_VALUE;
p = NULL;
}
} else {
/* End of directory stream reached */
p = NULL;
}
return p;
}
/*
* Open directory stream using plain old C-string.
*/
static DIR*
opendir(
const char *dirname)
{
struct DIR *dirp;
int error;
/* Must have directory name */
if (dirname == NULL || dirname[0] == '\0') {
dirent_set_errno (ENOENT);
return NULL;
}
/* Allocate memory for DIR structure */
dirp = (DIR*) malloc (sizeof (struct DIR));
if (dirp) {
wchar_t wname[PATH_MAX];
size_t n;
/* Convert directory name to wide-character string */
error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX);
if (!error) {
/* Open directory stream using wide-character name */
dirp->wdirp = _wopendir (wname);
if (dirp->wdirp) {
/* Directory stream opened */
error = 0;
} else {
/* Failed to open directory stream */
error = 1;
}
} else {
/*
* Cannot convert file name to wide-character string. This
* occurs if the string contains invalid multi-byte sequences or
* the output buffer is too small to contain the resulting
* string.
*/
error = 1;
}
} else {
/* Cannot allocate DIR structure */
error = 1;
}
/* Clean up in case of error */
if (error && dirp) {
free (dirp);
dirp = NULL;
}
return dirp;
}
/*
* Read next directory entry.
*
* When working with text consoles, please note that file names returned by
* readdir() are represented in the default ANSI code page while any output to
* console is typically formatted on another code page. Thus, non-ASCII
* characters in file names will not usually display correctly on console. The
* problem can be fixed in two ways: (1) change the character set of console
* to 1252 using chcp utility and use Lucida Console font, or (2) use
* _cprintf function when writing to console. The _cprinf() will re-encode
* ANSI strings to the console code page so many non-ASCII characters will
* display correcly.
*/
static struct dirent*
readdir(
DIR *dirp)
{
WIN32_FIND_DATAW *datap;
struct dirent *entp;
/* Read next directory entry */
datap = dirent_next (dirp->wdirp);
if (datap) {
size_t n;
int error;
/* Attempt to convert file name to multi-byte string */
error = dirent_wcstombs_s(
&n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX);
/*
* If the file name cannot be represented by a multi-byte string,
* then attempt to use old 8+3 file name. This allows traditional
* Unix-code to access some file names despite of unicode
* characters, although file names may seem unfamiliar to the user.
*
* Be ware that the code below cannot come up with a short file
* name unless the file system provides one. At least
* VirtualBox shared folders fail to do this.
*/
if (error && datap->cAlternateFileName[0] != '\0') {
error = dirent_wcstombs_s(
&n, dirp->ent.d_name, PATH_MAX,
datap->cAlternateFileName, PATH_MAX);
}
if (!error) {
DWORD attr;
/* Initialize directory entry for return */
entp = &dirp->ent;
/* Length of file name excluding zero terminator */
entp->d_namlen = n - 1;
/* File attributes */
attr = datap->dwFileAttributes;
if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
entp->d_type = DT_CHR;
} else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
entp->d_type = DT_DIR;
} else {
entp->d_type = DT_REG;
}
/* Reset dummy fields */
entp->d_ino = 0;
entp->d_reclen = sizeof (struct dirent);
} else {
/*
* Cannot convert file name to multi-byte string so construct
* an errornous directory entry and return that. Note that
* we cannot return NULL as that would stop the processing
* of directory entries completely.
*/
entp = &dirp->ent;
entp->d_name[0] = '?';
entp->d_name[1] = '\0';
entp->d_namlen = 1;
entp->d_type = DT_UNKNOWN;
entp->d_ino = 0;
entp->d_reclen = 0;
}
} else {
/* No more directory entries */
entp = NULL;
}
return entp;
}
/*
* Close directory stream.
*/
static int
closedir(
DIR *dirp)
{
int ok;
if (dirp) {
/* Close wide-character directory stream */
ok = _wclosedir (dirp->wdirp);
dirp->wdirp = NULL;
/* Release multi-byte character version */
free (dirp);
} else {
/* Invalid directory stream */
dirent_set_errno (EBADF);
ok = /*failure*/-1;
}
return ok;
}
/*
* Rewind directory stream to beginning.
*/
static void
rewinddir(
DIR* dirp)
{
/* Rewind wide-character string directory stream */
_wrewinddir (dirp->wdirp);
}
/* Convert multi-byte string to wide character string */
static int
dirent_mbstowcs_s(
size_t *pReturnValue,
wchar_t *wcstr,
size_t sizeInWords,
const char *mbstr,
size_t count)
{
int error;
#if defined(_MSC_VER) && _MSC_VER >= 1400
/* Microsoft Visual Studio 2005 or later */
error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
#else
/* Older Visual Studio or non-Microsoft compiler */
size_t n;
/* Convert to wide-character string (or count characters) */
n = mbstowcs (wcstr, mbstr, sizeInWords);
if (!wcstr || n < count) {
/* Zero-terminate output buffer */
if (wcstr && sizeInWords) {
if (n >= sizeInWords) {
n = sizeInWords - 1;
}
wcstr[n] = 0;
}
/* Length of resuting multi-byte string WITH zero terminator */
if (pReturnValue) {
*pReturnValue = n + 1;
}
/* Success */
error = 0;
} else {
/* Could not convert string */
error = 1;
}
#endif
return error;
}
/* Convert wide-character string to multi-byte string */
static int
dirent_wcstombs_s(
size_t *pReturnValue,
char *mbstr,
size_t sizeInBytes, /* max size of mbstr */
const wchar_t *wcstr,
size_t count)
{
int error;
#if defined(_MSC_VER) && _MSC_VER >= 1400
/* Microsoft Visual Studio 2005 or later */
error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
#else
/* Older Visual Studio or non-Microsoft compiler */
size_t n;
/* Convert to multi-byte string (or count the number of bytes needed) */
n = wcstombs (mbstr, wcstr, sizeInBytes);
if (!mbstr || n < count) {
/* Zero-terminate output buffer */
if (mbstr && sizeInBytes) {
if (n >= sizeInBytes) {
n = sizeInBytes - 1;
}
mbstr[n] = '\0';
}
/* Length of resulting multi-bytes string WITH zero-terminator */
if (pReturnValue) {
*pReturnValue = n + 1;
}
/* Success */
error = 0;
} else {
/* Cannot convert string */
error = 1;
}
#endif
return error;
}
/* Set errno variable */
static void
dirent_set_errno(
int error)
{
#if defined(_MSC_VER) && _MSC_VER >= 1400
/* Microsoft Visual Studio 2005 and later */
_set_errno (error);
#else
/* Non-Microsoft compiler or older Microsoft compiler */
errno = error;
#endif
}
#ifdef __cplusplus
}
#endif
#endif /*DIRENT_H*/

View File

@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9110
#define CURRENT_BINARY_DATABASE_VERSION 9114
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017
#else

View File

@ -0,0 +1,159 @@
--Mod file to demo changing the experience tables
--In this case I used some old wow tables (roughly it's not 100%)
function GetRequiredAAExperience(e)
e.level = 51;
return GetEXPForLevel(e);
end
function GetExperienceForKill(e)
local ML = e.other:GetLevel();
local CL = e.self:GetLevel();
if(ML > CL) then
local lmod = (ML - CL) * 0.05;
if(lmod > 1.0) then
lmod = 1.0;
end
e.ReturnValue = BaseXP(ML) * (1 + lmod);
elseif(ML < CL) then
local lmod = (CL - ML) * 0.05;
if(lmod > 1.0) then
lmod = 1.0;
end
e.ReturnValue = BaseXP(ML) * (1 - lmod);
else
e.ReturnValue = BaseXP(ML);
end
e.IgnoreDefault = true;
return e;
end
function BaseXP(L)
local base = L * 5;
if(L < 60) then
base = base + 45;
elseif(L < 70) then
base = base + 235;
elseif(L < 80) then
base = base + 580;
else
base = base + 1875;
end
return base;
end
function GetEXPForLevel(e)
local exp_table = {
0,
400,
900,
1400,
2100,
2800,
3600,
4500,
5400,
6500,
7600,
8700,
9800,
11000,
12300,
13600,
15000,
16400,
17800,
19300,
20800,
22400,
24000,
25500,
27200,
28900,
30500,
32200,
33900,
36300,
38800,
41600,
44600,
48000,
51400,
55000,
58700,
62400,
66200,
70200,
74300,
78500,
82800,
87100,
91600,
96300,
101000,
105800,
110700,
115700,
120900,
126100,
131500,
137000,
142500,
148200,
154000,
159900,
165800,
172000,
290000,
317000,
349000,
386000,
428000,
475000,
527000,
585000,
648000,
717000,
1523800,
1539000,
1555700,
1571800,
1587900,
1604200,
1620700,
1637400,
1653900,
1670800,
1670800,
1670800,
2121500,
2669000,
3469000,
4583000,
13000000,
15080000,
22600000,
27300000,
32800000
};
if(e.level < 1) then
e.ReturnValue = 0;
e.IgnoreDefault = true;
return e;
end
if(e.level > 91) then
e.ReturnValue = exp_table[91];
e.IgnoreDefault = true;
return e;
end
e.ReturnValue = exp_table[e.level];
e.IgnoreDefault = true;
return e;
end

View File

@ -0,0 +1,754 @@
MonkACBonusWeight = RuleI.Get(Rule.MonkACBonusWeight);
NPCACFactor = RuleR.Get(Rule.NPCACFactor);
OldACSoftcapRules = RuleB.Get(Rule.OldACSoftcapRules);
ClothACSoftcap = RuleI.Get(Rule.ClothACSoftcap);
LeatherACSoftcap = RuleI.Get(Rule.LeatherACSoftcap);
MonkACSoftcap = RuleI.Get(Rule.MonkACSoftcap);
ChainACSoftcap = RuleI.Get(Rule.ChainACSoftcap);
PlateACSoftcap = RuleI.Get(Rule.PlateACSoftcap);
AAMitigationACFactor = RuleR.Get(Rule.AAMitigationACFactor);
WarriorACSoftcapReturn = RuleR.Get(Rule.WarriorACSoftcapReturn);
KnightACSoftcapReturn = RuleR.Get(Rule.KnightACSoftcapReturn);
LowPlateChainACSoftcapReturn = RuleR.Get(Rule.LowPlateChainACSoftcapReturn);
LowChainLeatherACSoftcapReturn = RuleR.Get(Rule.LowChainLeatherACSoftcapReturn);
CasterACSoftcapReturn = RuleR.Get(Rule.CasterACSoftcapReturn);
MiscACSoftcapReturn = RuleR.Get(Rule.MiscACSoftcapReturn);
WarACSoftcapReturn = RuleR.Get(Rule.WarACSoftcapReturn);
ClrRngMnkBrdACSoftcapReturn = RuleR.Get(Rule.ClrRngMnkBrdACSoftcapReturn);
PalShdACSoftcapReturn = RuleR.Get(Rule.PalShdACSoftcapReturn);
DruNecWizEncMagACSoftcapReturn = RuleR.Get(Rule.DruNecWizEncMagACSoftcapReturn);
RogShmBstBerACSoftcapReturn = RuleR.Get(Rule.RogShmBstBerACSoftcapReturn);
SoftcapFactor = RuleR.Get(Rule.SoftcapFactor);
ACthac0Factor = RuleR.Get(Rule.ACthac0Factor);
ACthac20Factor = RuleR.Get(Rule.ACthac20Factor);
MeleeBaseCritChance = 0.0;
ClientBaseCritChance = 0.0;
BerserkBaseCritChance = 6.0;
WarBerBaseCritChance = 3.0;
RogueCritThrowingChance = 25;
RogueDeadlyStrikeChance = 80;
RogueDeadlyStrikeMod = 2;
BaseHitChance = RuleR.Get(Rule.BaseHitChance);
NPCBonusHitChance = RuleR.Get(Rule.NPCBonusHitChance);
HitFalloffMinor = RuleR.Get(Rule.HitFalloffMinor);
HitFalloffModerate = RuleR.Get(Rule.HitFalloffModerate);
HitFalloffMajor = RuleR.Get(Rule.HitFalloffMajor);
HitBonusPerLevel = RuleR.Get(Rule.HitBonusPerLevel);
AgiHitFactor = RuleR.Get(Rule.AgiHitFactor);
WeaponSkillFalloff = RuleR.Get(Rule.WeaponSkillFalloff);
ArcheryHitPenalty = RuleR.Get(Rule.ArcheryHitPenalty);
UseOldDamageIntervalRules = RuleB.Get(Rule.UseOldDamageIntervalRules);
CriticalMessageRange = RuleI.Get(Rule.CriticalDamage);
function MeleeMitigation(e)
e.IgnoreDefault = true;
if e.hit.damage_done < 0 or e.hit.base_damage == 0 then
return e;
end
e.hit.damage_done = 2 * e.hit.base_damage * GetDamageTable(e.other, e.hit.skill) / 100;
e.hit = DoMeleeMitigation(e.self, e.other, e.hit, e.opts);
return e;
end
function CheckHitChance(e)
e.IgnoreDefault = true;
local other = e.other;
local attacker = other;
local self = e.self;
local defender = self;
local chancetohit = BaseHitChance;
local chance_mod = 0;
if(e.opts ~= nil) then
chance_mod = e.opts.hit_chance;
end
if(attacker:IsNPC() and not attacker:IsPet()) then
chancetohit = chancetohit + NPCBonusHitChance;
end
local pvpmode = false;
if(self:IsClient() and other:IsClient()) then
pvpmode = true;
end
if (chance_mod >= 10000) then
e.ReturnValue = true;
return e;
end
local avoidanceBonus = 0;
local hitBonus = 0;
local attacker_level = attacker:GetLevel();
if(attacker_level < 1) then
attacker_level = 1;
end
local defender_level = defender:GetLevel();
if(defender_level < 1) then
defender_level = 1;
end
local level_difference = attacker_level - defender_level;
local range = defender_level;
range = ((range / 4) + 3);
if(level_difference < 0) then
if(level_difference >= -range) then
chancetohit = chancetohit + ((level_difference / range) * HitFalloffMinor);
elseif (level_difference >= -(range+3.0)) then
chancetohit = chancetohit - HitFalloffMinor;
chancetohit = chancetohit + (((level_difference + range) / 3.0) * HitFalloffModerate);
else
chancetohit = chancetohit - (HitFalloffMinor + HitFalloffModerate);
chancetohit = chancetohit + (((level_difference + range + 3.0) / 12.0) * HitFalloffMajor);
end
else
chancetohit = chancetohit + (HitBonusPerLevel * level_difference);
end
chancetohit = chancetohit - (defender:GetAGI() * AgiHitFactor);
if(attacker:IsClient()) then
chancetohit = chancetohit - (WeaponSkillFalloff * (attacker:CastToClient():MaxSkill(e.hit.skill) - attacker:GetSkill(e.hit.skill)));
end
if(defender:IsClient()) then
chancetohit = chancetohit + (WeaponSkillFalloff * (defender:CastToClient():MaxSkill(Skill.Defense) - defender:GetSkill(Skill.Defense)));
end
local attacker_spellbonuses = attacker:GetSpellBonuses();
local attacker_itembonuses = attacker:GetItemBonuses();
local attacker_aabonuses = attacker:GetAABonuses();
local defender_spellbonuses = defender:GetSpellBonuses();
local defender_itembonuses = defender:GetItemBonuses();
local defender_aabonuses = defender:GetAABonuses();
if(attacker_spellbonuses:MeleeSkillCheckSkill() == e.hit.skill or attacker_spellbonuses:MeleeSkillCheckSkill() == 255) then
chancetohit = chancetohit + attacker_spellbonuses:MeleeSkillCheck();
end
if(attacker_itembonuses:MeleeSkillCheckSkill() == e.hit.skill or attacker_itembonuses:MeleeSkillCheckSkill() == 255) then
chancetohit = chancetohit + attacker_itembonuses:MeleeSkillCheck();
end
avoidanceBonus = defender_spellbonuses:AvoidMeleeChanceEffect() +
defender_itembonuses:AvoidMeleeChanceEffect() +
defender_aabonuses:AvoidMeleeChanceEffect() +
(defender_itembonuses:AvoidMeleeChance() / 10.0);
local owner = Mob();
if (defender:IsPet()) then
owner = defender:GetOwner();
elseif (defender:IsNPC() and defender:CastToNPC():GetSwarmOwner()) then
local entity_list = eq.get_entity_list();
owner = entity_list:GetMobID(defender:CastToNPC():GetSwarmOwner());
end
if (owner.valid) then
avoidanceBonus = avoidanceBonus + owner:GetAABonuses():PetAvoidance() + owner:GetSpellBonuses():PetAvoidance() + owner:GetItemBonuses():PetAvoidance();
end
if(defender:IsNPC()) then
avoidanceBonus = avoidanceBonus + (defender:CastToNPC():GetAvoidanceRating() / 10.0);
end
hitBonus = hitBonus + attacker_itembonuses:HitChanceEffect(e.hit.skill) +
attacker_spellbonuses:HitChanceEffect(e.hit.skill) +
attacker_aabonuses:HitChanceEffect(e.hit.skill) +
attacker_itembonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1) +
attacker_spellbonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1) +
attacker_aabonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1);
hitBonus = hitBonus + (attacker_itembonuses:Accuracy(Skill.HIGHEST_SKILL + 1) +
attacker_spellbonuses:Accuracy(Skill.HIGHEST_SKILL + 1) +
attacker_aabonuses:Accuracy(Skill.HIGHEST_SKILL + 1) +
attacker_aabonuses:Accuracy(e.hit.skill) +
attacker_itembonuses:HitChance()) / 15.0;
hitBonus = hitBonus + chance_mod;
if(attacker:IsNPC()) then
hitBonus = hitBonus + (attacker:CastToNPC():GetAccuracyRating() / 10.0);
end
if (e.hit.skill == Skill.Archery) then
hitBonus = hitBonus - (hitBonus * ArcheryHitPenalty);
end
chancetohit = chancetohit + ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0);
if(chancetohit > 1000 or chancetohit < -1000) then
elseif(chancetohit > 95) then
chancetohit = 95;
elseif(chancetohit < 5) then
chancetohit = 5;
end
local tohit_roll = Random.Real(0, 100);
if(tohit_roll <= chancetohit) then
e.ReturnValue = true;
else
e.ReturnValue = false;
end
return e;
end
function TryCriticalHit(e)
e.IgnoreDefault = true;
local self = e.self;
local defender = e.other;
if(e.hit.damage_done < 1 or defender.null) then
return e;
end
if ((self:IsPet() and self:GetOwner():IsClient()) or (self:IsNPC() and self:CastToNPC():GetSwarmOwner() ~= 0)) then
e.hit = TryPetCriticalHit(self, defender, e.hit);
return e;
end
if (self:IsPet() and self:GetOwner().valid and self:GetOwner():IsBot()) then
e.hit = TryPetCriticalHit(self, defender, e.hit);
return e;
end
local critChance = 0.0;
local IsBerskerSPA = false;
local aabonuses = self:GetAABonuses();
local itembonuses = self:GetItemBonuses();
local spellbonuses = self:GetSpellBonuses();
local entity_list = eq.get_entity_list();
if (defender:GetBodyType() == BT.Undead or defender:GetBodyType() == BT.SummonedUndead or defender:GetBodyType() == BT.Vampire) then
local SlayRateBonus = aabonuses:SlayUndead(0) + itembonuses:SlayUndead(0) + spellbonuses:SlayUndead(0);
if (SlayRateBonus > 0) then
local slayChance = SlayRateBonus / 10000.0;
if (Random.RollReal(slayChance)) then
local SlayDmgBonus = aabonuses:SlayUndead(1) + itembonuses:SlayUndead(1) + spellbonuses:SlayUndead(1);
e.hit.damage_done = (e.hit.damage_done * SlayDmgBonus * 2.25) / 100;
if (self:GetGender() == 1) then
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses her target! (%d)', self:GetCleanName(), e.hit.damage_done));
else
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses his target! (%d)', self:GetCleanName(), e.hit.damage_done));
end
return e;
end
end
end
critChance = critChance + MeleeBaseCritChance;
if (self:IsClient()) then
critChance = critChance + ClientBaseCritChance;
if (spellbonuses:BerserkSPA() or itembonuses:BerserkSPA() or aabonuses:BerserkSPA()) then
IsBerskerSPA = true;
end
if (((self:GetClass() == Class.WARRIOR or self:GetClass() == Class.BERSERKER) and self:GetLevel() >= 12) or IsBerskerSPA) then
if (self:IsBerserk() or IsBerskerSPA) then
critChance = critChance + BerserkBaseCritChance;
else
critChance = critChance + WarBerBaseCritChance;
end
end
end
local deadlyChance = 0;
local deadlyMod = 0;
if (e.hit.skill == Skill.Archery and self:GetClass() == Class.RANGER and self:GetSkill(Skill.Archery) >= 65) then
critChance = critChance + 6;
end
if (e.hit.skill == Skill.Throwing and self:GetClass() == Class.ROGUE and self:GetSkill(Skill.Throwing) >= 65) then
critChance = critChance + RogueCritThrowingChance;
deadlyChance = RogueDeadlyStrikeChance;
deadlyMod = RogueDeadlyStrikeMod;
end
local CritChanceBonus = GetCriticalChanceBonus(self, e.hit.skill);
if (CritChanceBonus > 0 or critChance > 0) then
if (self:GetDEX() <= 255) then
critChance = critChance + (self:GetDEX() / 125.0);
elseif (self:GetDEX() > 255) then
critChance = critChance + ((self:GetDEX() - 255) / 500.0) + 2.0;
end
critChance = critChance + (critChance * CritChanceBonus / 100.0);
end
if(opts ~= nil) then
critChance = critChance * opts.crit_percent;
critChance = critChance + opts.crit_flat;
end
if(critChance > 0) then
critChance = critChance / 100;
if(Random.RollReal(critChance)) then
local critMod = 200;
local crip_success = false;
local CripplingBlowChance = GetCrippBlowChance(self);
if (CripplingBlowChance > 0 or (self:IsBerserk() or IsBerskerSPA)) then
if (not self:IsBerserk() and not IsBerskerSPA) then
critChance = critChance * (CripplingBlowChance / 100.0);
end
if ((self:IsBerserk() or IsBerskerSPA) or Random.RollReal(critChance)) then
critMod = 400;
crip_success = true;
end
end
critMod = critMod + GetCritDmgMod(self, e.hit.skill) * 2;
e.hit.damage_done = e.hit.damage_done * critMod / 100;
local deadlySuccess = false;
if (deadlyChance > 0 and Random.RollReal(deadlyChance / 100.0)) then
if (self:BehindMob(defender, self:GetX(), self:GetY())) then
e.hit.damage_done = e.hit.damage_done * deadlyMod;
deadlySuccess = true;
end
end
if (crip_success) then
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s lands a Crippling Blow! (%d)', self:GetCleanName(), e.hit.damage_done));
if (defender:GetLevel() <= 55 and not defender:GetSpecialAbility(SpecialAbility.unstunable)) then
defender:Emote("staggers.");
defender:Stun(0);
end
elseif (deadlySuccess) then
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a Deadly Strike! (%d)', self:GetCleanName(), e.hit.damage_done));
else
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)', self:GetCleanName(), e.hit.damage_done));
end
end
end
return e;
end
function TryPetCriticalHit(self, defender, hit)
if(hit.damage_done < 1) then
return hit;
end
local owner = Mob();
local critChance = MeleeBaseCritChance;
local critMod = 163;
if (self:IsPet()) then
owner = self:GetOwner();
elseif (self:IsNPC() and self:CastToNPC():GetSwarmOwner()) then
local entity_list = eq.get_entity_list();
owner = entity_list:GetMobID(self:CastToNPC():GetSwarmOwner());
else
return hit;
end
if (owner.null) then
return hit;
end
local CritPetChance = owner:GetAABonuses():PetCriticalHit() + owner:GetItemBonuses():PetCriticalHit() + owner:GetSpellBonuses():PetCriticalHit();
local CritChanceBonus = GetCriticalChanceBonus(self, hit.skill);
if (CritPetChance or critChance) then
critChance = critChance + CritPetChance;
critChance = critChance + (critChance * CritChanceBonus / 100.0);
end
if(critChance > 0) then
critChance = critChance / 100;
if(Random.RollReal(critChance)) then
local entity_list = eq.get_entity_list();
critMod = critMod + GetCritDmgMod(self, hit.skill) * 2;
hit.damage_done = (hit.damage_done * critMod) / 100;
entity_list:FilteredMessageClose(this, false, CriticalMessageRange,
MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)',
self:GetCleanName(), e.hit.damage_done));
end
end
return hit;
end
function GetCriticalChanceBonus(self, skill)
local critical_chance = 0;
local aabonuses = self:GetAABonuses();
local itembonuses = self:GetItemBonuses();
local spellbonuses = self:GetSpellBonuses();
critical_chance = critical_chance + itembonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1);
critical_chance = critical_chance + spellbonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1);
critical_chance = critical_chance + aabonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1);
critical_chance = critical_chance + itembonuses:CriticalHitChance(skill);
critical_chance = critical_chance + spellbonuses:CriticalHitChance(skill);
critical_chance = critical_chance + aabonuses:CriticalHitChance(skill);
return critical_chance;
end
function GetCritDmgMod(self, skill)
local critDmg_mod = 0;
local aabonuses = self:GetAABonuses();
local itembonuses = self:GetItemBonuses();
local spellbonuses = self:GetSpellBonuses();
critDmg_mod = critDmg_mod + itembonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1);
critDmg_mod = critDmg_mod + spellbonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1);
critDmg_mod = critDmg_mod + aabonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1);
critDmg_mod = critDmg_mod + itembonuses:CritDmgMod(skill);
critDmg_mod = critDmg_mod + spellbonuses:CritDmgMod(skill);
critDmg_mod = critDmg_mod + aabonuses:CritDmgMod(skill);
return critDmg_mod;
end
function GetCrippBlowChance(self)
local aabonuses = self:GetAABonuses();
local itembonuses = self:GetItemBonuses();
local spellbonuses = self:GetSpellBonuses();
local crip_chance = itembonuses:CrippBlowChance() + spellbonuses:CrippBlowChance() + aabonuses:CrippBlowChance();
if(crip_chance < 0) then
crip_chance = 0;
end
return crip_chance;
end
function DoMeleeMitigation(defender, attacker, hit, opts)
if hit.damage_done <= 0 then
return hit;
end
local aabonuses = defender:GetAABonuses();
local itembonuses = defender:GetItemBonuses();
local spellbonuses = defender:GetSpellBonuses();
local aa_mit = (aabonuses:CombatStability() + itembonuses:CombatStability() + spellbonuses:CombatStability()) / 100.0;
local softcap = (defender:GetSkill(15) + defender:GetLevel()) * SoftcapFactor * (1.0 + aa_mit);
local mitigation_rating = 0.0;
local attack_rating = 0.0;
local shield_ac = 0;
local armor = 0;
local weight = 0.0;
local monkweight = MonkACBonusWeight;
if defender:IsClient() then
armor, shield_ac = GetRawACNoShield(defender);
weight = defender:CastToClient():CalcCurrentWeight() / 10;
elseif defender:IsNPC() then
armor = defender:CastToNPC():GetRawAC();
local PetACBonus = 0;
if not defender:IsPet() then
armor = armor / NPCACFactor;
end
local owner = Mob();
if defender:IsPet() then
owner = defender:GetOwner();
elseif defender:CastToNPC():GetSwarmOwner() ~= 0 then
local entity_list = eq.get_entity_list();
owner = entity_list:GetMobID(defender:CastToNPC():GetSwarmOwner());
end
if owner.valid then
PetACBonus = owner:GetAABonuses():PetMeleeMitigation() + owner:GetItemBonuses():PetMeleeMitigation() + owner:GetSpellBonuses():PetMeleeMitigation();
end
armor = armor + defender:GetSpellBonuses():AC() + defender:GetItemBonuses():AC() + PetACBonus + 1;
end
if (opts ~= nil) then
armor = armor * (1.0 - opts.armor_pen_percent);
armor = armor - opts.armor_pen_flat;
end
local defender_class = defender:GetClass();
if OldACSoftcapRules then
if defender_class == Class.WIZARD or defender_class == Class.MAGICIAN or defender_class == Class.NECROMANCER or defender_class == Class.ENCHANTER then
softcap = ClothACSoftcap;
elseif defender_class == Class.MONK and weight <= monkweight then
softcap = MonkACSoftcap;
elseif defender_class == Class.DRUID or defender_class == Class.BEASTLORD or defender_class == Class.MONK then
softcap = LeatherACSoftcap;
elseif defender_class == Class.SHAMAN or defender_class == Class.ROGUE or defender_class == Class.BERSERKER or defender_class == Class.RANGER then
softcap = ChainACSoftcap;
else
softcap = PlateACSoftcap;
end
end
softcap = softcap + shield_ac;
armor = armor + shield_ac;
if OldACSoftcapRules then
softcap = softcap + (softcap * (aa_mit * AAMitigationACFactor));
end
if armor > softcap then
local softcap_armor = armor - softcap;
if OldACSoftcapRules then
if defender_class == Class.WARRIOR then
softcap_armor = softcap_armor * WarriorACSoftcapReturn;
elseif defender_class == Class.SHADOWKNIGHT or defender_class == Class.PALADIN or (defender_class == Class.MONK and weight <= monkweight) then
softcap_armor = softcap_armor * KnightACSoftcapReturn;
elseif defender_class == Class.CLERIC or defender_class == Class.BARD or defender_class == Class.BERSERKER or defender_class == Class.ROGUE or defender_class == Class.SHAMAN or defender_class == Class.MONK then
softcap_armor = softcap_armor * LowPlateChainACSoftcapReturn;
elseif defender_class == Class.RANGER or defender_class == Class.BEASTLORD then
softcap_armor = softcap_armor * LowChainLeatherACSoftcapReturn;
elseif defender_class == Class.WIZARD or defender_class == Class.MAGICIAN or defender_class == Class.NECROMANCER or defender_class == Class.ENCHANTER or defender_class == Class.DRUID then
softcap_armor = softcap_armor * CasterACSoftcapReturn;
else
softcap_armor = softcap_armor * MiscACSoftcapReturn;
end
else
if defender_class == Class.WARRIOR then
softcap_armor = softcap_armor * WarACSoftcapReturn;
elseif defender_class == Class.PALADIN or defender_class == Class.SHADOWKNIGHT then
softcap_armor = softcap_armor * PalShdACSoftcapReturn;
elseif defender_class == Class.CLERIC or defender_class == Class.RANGER or defender_class == Class.MONK or defender_class == Class.BARD then
softcap_armor = softcap_armor * ClrRngMnkBrdACSoftcapReturn;
elseif defender_class == Class.DRUID or defender_class == Class.NECROMANCER or defender_class == Class.WIZARD or defender_class == Class.ENCHANTER or defender_class == Class.MAGICIAN then
softcap_armor = softcap_armor * DruNecWizEncMagACSoftcapReturn;
elseif defender_class == Class.ROGUE or defender_class == Class.SHAMAN or defender_class == Class.BEASTLORD or defender_class == Class.BERSERKER then
softcap_armor = softcap_armor * RogShmBstBerACSoftcapReturn;
else
softcap_armor = softcap_armor * MiscACSoftcapReturn;
end
end
armor = softcap + softcap_armor;
end
local mitigation_rating;
if defender_class == Class.WIZARD or defender_class == Class.MAGICIAN or defender_class == Class.NECROMANCER or defender_class == Class.ENCHANTER then
mitigation_rating = ((defender:GetSkill(Skill.Defense) + defender:GetItemBonuses():HeroicAGI() / 10) / 4.0) + armor + 1;
else
mitigation_rating = ((defender:GetSkill(Skill.Defense) + defender:GetItemBonuses():HeroicAGI() / 10) / 3.0) + (armor * 1.333333) + 1;
end
mitigation_rating = mitigation_rating * 0.847;
local attack_rating;
if attacker:IsClient() then
attack_rating = (attacker:CastToClient():CalcATK() + ((attacker:GetSTR() - 66) * 0.9) + (attacker:GetSkill(Skill.Offense)*1.345));
else
attack_rating = (attacker:GetATK() + (attacker:GetSkill(Skill.Offense)*1.345) + ((attacker:GetSTR() - 66) * 0.9));
end
hit.damage_done = GetMeleeMitDmg(defender, attacker, hit.damage_done, hit.min_damage, mitigation_rating, attack_rating);
if hit.damage_done < 0 then
hit.damage_done = 0;
end
return hit;
end
function GetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating)
if defender:IsClient() then
return ClientGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating);
else
return MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating);
end
end
function ClientGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating)
if (not attacker:IsNPC() or UseOldDamageIntervalRules) then
return MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating);
end
local d = 10;
local dmg_interval = (damage - min_damage) / 19.0;
local dmg_bonus = min_damage - dmg_interval;
local spellMeleeMit = (defender:GetSpellBonuses():MeleeMitigationEffect() + defender:GetItemBonuses():MeleeMitigationEffect() + defender:GetAABonuses():MeleeMitigationEffect()) / 100.0;
if (defender:GetClass() == Class.WARRIOR) then
spellMeleeMit = spellMeleeMit - 0.05;
end
dmg_bonus = dmg_bonus - (dmg_bonus * (defender:GetItemBonuses():MeleeMitigation() / 100.0));
dmg_interval = dmg_interval + (dmg_interval * spellMeleeMit);
local mit_roll = Random.Real(0, mitigation_rating);
local atk_roll = Random.Real(0, attack_rating);
if (atk_roll > mit_roll) then
local a_diff = atk_roll - mit_roll;
local thac0 = attack_rating * ACthac0Factor;
local thac0cap = attacker:GetLevel() * 9 + 20;
if (thac0 > thac0cap) then
thac0 = thac0cap;
end
d = d + (10 * (a_diff / thac0));
elseif (mit_roll > atk_roll) then
local m_diff = mit_roll - atk_roll;
local thac20 = mitigation_rating * ACthac20Factor;
local thac20cap = defender:GetLevel() * 9 + 20;
if (thac20 > thac20cap) then
thac20 = thac20cap;
end
d = d - (10 * (m_diff / thac20));
end
if (d < 1) then
d = 1;
elseif (d > 20) then
d = 20;
end
return math.floor(dmg_bonus + dmg_interval * d);
end
function MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating)
local d = 10.0;
local mit_roll = Random.Real(0, mitigation_rating);
local atk_roll = Random.Real(0, attack_rating);
if (atk_roll > mit_roll) then
local a_diff = atk_roll - mit_roll;
local thac0 = attack_rating * ACthac0Factor;
local thac0cap = attacker:GetLevel() * 9 + 20;
if (thac0 > thac0cap) then
thac0 = thac0cap;
end
d = d - (10.0 * (a_diff / thac0));
elseif (mit_roll > atk_roll) then
local m_diff = mit_roll - atk_roll;
local thac20 = mitigation_rating * ACthac20Factor;
local thac20cap = defender:GetLevel() * 9 + 20;
if (thac20 > thac20cap) then
thac20 = thac20cap;
end
d = d + (10.0 * (m_diff / thac20));
end
if (d < 0.0) then
d = 0.0;
elseif (d > 20.0) then
d = 20.0;
end
local interval = (damage - min_damage) / 20.0;
damage = damage - (math.floor(d) * interval);
damage = damage - (min_damage * defender:GetItemBonuses():MeleeMitigation() / 100);
damage = damage + (damage * (defender:GetSpellBonuses():MeleeMitigationEffect() + defender:GetItemBonuses():MeleeMitigationEffect() + defender:GetAABonuses():MeleeMitigationEffect()) / 100);
return damage;
end
function GetRawACNoShield(self)
self = self:CastToClient();
local ac = self:GetItemBonuses():AC() + self:GetSpellBonuses():AC() + self:GetAABonuses():AC();
local shield_ac = 0;
local inst = self:GetInventory():GetItem(Slot.Secondary);
if inst.valid then
if inst:GetItem():ItemType() == 8 then
shield_ac = inst:GetItem():AC();
for i = 1, 6 do
local augment = inst:GetAugment(i - 1);
if augment.valid then
shield_ac = shield_ac + augment:GetItem():AC();
end
end
end
end
ac = ac - shield_ac;
return ac, shield_ac;
end
function GetDamageTable(attacker, skill)
if not attacker:IsClient() then
return 100;
end
if attacker:GetLevel() <= 51 then
local ret_table = 0;
local str_over_75 = 0;
if attacker:GetSTR() > 75 then
str_over_75 = attacker:GetSTR() - 75;
end
if str_over_75 > 255 then
ret_table = (attacker:GetSkill(skill) + 255) / 2;
else
ret_table = (attacker:GetSkill(skill) + str_over_75) / 2;
end
if ret_table < 100 then
return 100;
end
return ret_table;
elseif attacker:GetLevel() >= 90 then
if attacker:GetClass() == 7 then
return 379;
else
return 345;
end
else
local dmg_table = { 275, 275, 275, 275, 275, 280, 280, 280, 280, 285, 285, 285, 290, 290, 295, 295, 300, 300, 300, 305, 305, 305, 310, 310, 315, 315, 320, 320, 320, 325, 325, 325, 330, 330, 335, 335, 340, 340, 340 };
if attacker:GetClass() == 7 then
local monkDamageTableBonus = 20;
return (dmg_table[attacker:GetLevel() - 50] * (100 + monkDamageTableBonus) / 100);
else
return dmg_table[attacker:GetLevel() - 50];
end
end
return 100;
end
function ApplyDamageTable(e)
e.IgnoreDefault = true;
return e;
end
function CommonOutgoingHitSuccess(e)
e = ApplyMeleeDamageBonus(e);
e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * e.other:GetSkillDmgTaken(e.hit.skill) / 100) + (e.self:GetSkillDmgAmt(e.hit.skill) + e.other:GetFcDamageAmtIncoming(e.self, 0, true, e.hit.skill));
e = TryCriticalHit(e);
e.self:CheckNumHitsRemaining(5, -1, 65535);
e.IgnoreDefault = true;
return e;
end
function ApplyMeleeDamageBonus(e)
local dmgbonusmod = e.self:GetMeleeDamageMod_SE(e.hit.skill);
if (opts) then
dmgbonusmod = dmgbonusmod + e.opts.melee_damage_bonus_flat;
end
e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * dmgbonusmod / 100);
return e;
end

View File

@ -0,0 +1 @@
legacy_combat.lua

View File

@ -196,6 +196,7 @@ OP_Consent=0x400e
OP_ConsentDeny=0x34c1
OP_AutoFire=0x314e
OP_PetCommands=0x0093
OP_PetCommandState=0x74ed
OP_PetHoTT=0x0df4
OP_DeleteSpell=0x305c
OP_Surname=0x1a87
@ -671,3 +672,7 @@ OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
OP_InitialHPUpdate=0x0000
OP_ItemRecastDelay=0x57ed
#aura related
OP_UpdateAura=0x1fa9
OP_RemoveTrap=0x6a4d

View File

@ -195,6 +195,7 @@ OP_Consent=0x1fd1
OP_ConsentDeny=0x7a45
OP_AutoFire=0x241e
OP_PetCommands=0x0159
OP_PetCommandState=0x1dc8
OP_PetHoTT=0x794a
OP_DeleteSpell=0x3358
OP_Surname=0x0423
@ -674,3 +675,7 @@ OP_RAWOutOfSession=0x0000
# we need to document the differences between these packets to make identifying them easier
OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
OP_InitialHPUpdate=0x0000
#aura related
OP_UpdateAura=0x1456
OP_RemoveTrap=0x71da

View File

@ -669,3 +669,7 @@ OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
OP_InitialHPUpdate=0x0000 #
OP_ItemRecastDelay=0x15c4
#aura related
OP_UpdateAura=0x169a
OP_RemoveTrap=0x4bb6

View File

@ -658,3 +658,7 @@ OP_ItemRecastDelay=0x0ada
#OP_NpcMoveUpdate=0x0d11 #SEQ 10/07/08 --NEW FROM SEQ
#OP_Zone_MissingName01=0x0000 #
#new titles avaliable: #
#aura related
OP_UpdateAura=0x62a9
OP_RemoveTrap=0x7bd9

View File

@ -198,6 +198,7 @@ OP_Consent=0x6bb9 # C
OP_ConsentDeny=0x4cd1 # C
OP_AutoFire=0x5db5 # C
OP_PetCommands=0x7706 # C
OP_PetCommandState=0x1a79
OP_PetHoTT=0x2528
OP_DeleteSpell=0x0698 # C
OP_Surname=0x44ae # C
@ -684,3 +685,7 @@ OP_ItemRecastDelay=0x82d7
# unhandled
OP_ShieldGroup=0x23a1
#aura related
OP_UpdateAura=0x2480
OP_RemoveTrap=0x0115

View File

@ -49,6 +49,7 @@ if(-e "eqemu_server_skip_update.txt"){
#::: Check for script self update
do_self_update_check_routine() if !$skip_self_update_check;
get_windows_wget();
get_perl_version();
read_eqemu_config_xml();
get_mysql_path();
@ -200,7 +201,7 @@ sub new_server {
}
closedir(DIR);
if($file_count > 1 && (!-e "install_variables.txt" && !-e "../install_variables.txt")){
if($file_count > 4 && (!-e "install_variables.txt" && !-e "../install_variables.txt")){
print "[New Server] ERROR: You must run eqemu_server.pl in an empty directory\n";
<>;
exit;
@ -280,6 +281,8 @@ sub new_server {
show_install_summary_info();
rmtree('updates_staged');
return;
}
else {
@ -517,6 +520,13 @@ sub get_perl_version {
no warnings;
}
sub get_windows_wget {
if(!-e "wget.exe" && $OS eq "Windows"){
eval "use LWP::Simple qw(getstore);";
getstore("https://raw.githubusercontent.com/Akkadius/EQEmuInstall/master/wget.exe", "wget.exe");
}
}
sub do_self_update_check_routine {
#::: Check for internet connection before updating
@ -524,7 +534,7 @@ sub do_self_update_check_routine {
print "[Update] Cannot check update without internet connection...\n";
return;
}
#::: Check for script changes :: eqemu_server.pl
get_remote_file($eqemu_repository_request_url . "utils/scripts/eqemu_server.pl", "updates_staged/eqemu_server.pl", 0, 1, 1);
@ -997,68 +1007,14 @@ sub get_remote_file{
}
}
if($OS eq "Windows"){
#::: For non-text type requests...
if($content_type == 1){
$break = 0;
while($break == 0) {
eval "use LWP::Simple qw(getstore);";
# use LWP::Simple qw(getstore);
# print "request is " . $request_url . "\n";
# print "destination file is supposed to be " . $destination_file . "\n";
if(!getstore($request_url, $destination_file)){
print "[Download] Error, no connection or failed request...\n\n";
}
# sleep(1);
#::: Make sure the file exists before continuing...
if(-e $destination_file) {
$break = 1;
print "[Download] Saved: (" . $destination_file . ") from " . $request_url . "\n" if !$silent_download;
} else { $break = 0; }
usleep(500);
if($no_retry){
$break = 1;
}
}
}
else{
$break = 0;
while($break == 0) {
require LWP::UserAgent;
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;
my $response = $ua->get($request_url);
if ($response->is_success){
open (FILE, '> ' . $destination_file . '');
print FILE $response->decoded_content;
close (FILE);
}
else {
print "[Download] Error, no connection or failed request...\n\n";
}
if(-e $destination_file) {
$break = 1;
print "[Download] Saved: (" . $destination_file . ") from " . $request_url . "\n" if !$silent_download;
} else { $break = 0; }
usleep(500);
if($no_retry){
$break = 1;
}
}
}
}
if($OS eq "Linux"){
#::: wget -O db_update/db_update_manifest.txt https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt
$wget = `wget --no-check-certificate --quiet -O $destination_file $request_url`;
print "[Download] Saved: (" . $destination_file . ") from " . $request_url . "\n" if !$silent_download;
if($wget=~/unable to resolve/i){
print "Error, no connection or failed request...\n\n";
#die;
}
#::: wget -O db_update/db_update_manifest.txt https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt
$wget = `wget -N --no-check-certificate --quiet -O $destination_file $request_url`;
print "[Download] Saved: (" . $destination_file . ") from " . $request_url . "\n" if !$silent_download;
if($wget=~/unable to resolve/i){
print "Error, no connection or failed request...\n\n";
#die;
}
}
#::: Trim Whitespaces
@ -1498,7 +1454,7 @@ sub map_files_fetch_bulk{
get_remote_file("http://github.com/Akkadius/EQEmuMaps/archive/master.zip", "maps/maps.zip", 1);
unzip('maps/maps.zip', 'maps/');
my @files;
my $start_dir = "maps/EQEmuMaps-master/maps";
my $start_dir = "maps/EQEmuMaps-master/";
find(
sub { push @files, $File::Find::name unless -d; },
$start_dir
@ -1551,12 +1507,12 @@ sub map_files_fetch{
}
sub quest_files_fetch{
if (!-e "updates_staged/Quests-Plugins-master/quests/") {
if (!-e "updates_staged/projecteqquests-master/") {
print "[Update] Fetching Latest Quests --- \n";
get_remote_file("https://github.com/EQEmu/Quests-Plugins/archive/master.zip", "updates_staged/Quests-Plugins-master.zip", 1);
get_remote_file("https://codeload.github.com/ProjectEQ/projecteqquests/zip/master", "updates_staged/projecteqquests-master.zip", 1);
print "[Install] Fetched latest quests...\n";
mkdir('updates_staged');
unzip('updates_staged/Quests-Plugins-master.zip', 'updates_staged/');
unzip('updates_staged/projecteqquests-master.zip', 'updates_staged/');
}
$fc = 0;
@ -1564,7 +1520,7 @@ sub quest_files_fetch{
use File::Compare;
my @files;
my $start_dir = "updates_staged/Quests-Plugins-master/quests/";
my $start_dir = "updates_staged/projecteqquests-master/";
find(
sub { push @files, $File::Find::name unless -d; },
$start_dir
@ -1573,7 +1529,7 @@ sub quest_files_fetch{
if($file=~/\.pl|\.lua|\.ext/i){
$staged_file = $file;
$destination_file = $file;
$destination_file =~s/updates_staged\/Quests-Plugins-master\///g;
$destination_file =~s/updates_staged\/projecteqquests-master\//quests\//g;
if (!-e $destination_file) {
copy_file($staged_file, $destination_file);
@ -1603,27 +1559,28 @@ sub quest_files_fetch{
}
}
rmtree('updates_staged');
if($fc == 0){
print "[Update] No Quest Updates found... \n\n";
}
}
sub lua_modules_fetch {
if (!-e "updates_staged/Quests-Plugins-master/quests/lua_modules/") {
print "[Update] Fetching Latest LUA Modules --- \n";
get_remote_file("https://github.com/EQEmu/Quests-Plugins/archive/master.zip", "updates_staged/Quests-Plugins-master.zip", 1);
print "[Update] Fetched latest LUA Modules...\n";
unzip('updates_staged/Quests-Plugins-master.zip', 'updates_staged/');
if (!-e "updates_staged/projecteqquests-master/") {
print "[Update] Fetching Latest lua modules --- \n";
get_remote_file("https://codeload.github.com/ProjectEQ/projecteqquests/zip/master", "updates_staged/projecteqquests-master.zip", 1);
print "[Install] Fetched latest lua modules...\n";
mkdir('updates_staged');
unzip('updates_staged/projecteqquests-master.zip', 'updates_staged/');
}
$fc = 0;
use File::Find;
use File::Compare;
mkdir('lua_modules');
my @files;
my $start_dir = "updates_staged/Quests-Plugins-master/quests/lua_modules/";
my $start_dir = "updates_staged/projecteqquests-master/lua_modules/";
find(
sub { push @files, $File::Find::name unless -d; },
$start_dir
@ -1632,7 +1589,7 @@ sub lua_modules_fetch {
if($file=~/\.pl|\.lua|\.ext/i){
$staged_file = $file;
$destination_file = $file;
$destination_file =~s/updates_staged\/Quests-Plugins-master\/quests\///g;
$destination_file =~s/updates_staged\/projecteqquests-master\/lua_modules\//lua_modules\//g;
if (!-e $destination_file) {
copy_file($staged_file, $destination_file);
@ -1667,19 +1624,22 @@ sub lua_modules_fetch {
}
sub plugins_fetch{
if (!-e "updates_staged/Quests-Plugins-master/plugins/") {
print "[Update] Fetching Latest Plugins\n";
get_remote_file("https://github.com/EQEmu/Quests-Plugins/archive/master.zip", "updates_staged/Quests-Plugins-master.zip", 1);
print "[Update] Fetched latest plugins\n";
unzip('updates_staged/Quests-Plugins-master.zip', 'updates_staged/');
if (!-e "updates_staged/projecteqquests-master/") {
print "[Update] Fetching Latest plugins --- \n";
get_remote_file("https://codeload.github.com/ProjectEQ/projecteqquests/zip/master", "updates_staged/projecteqquests-master.zip", 1);
print "[Install] Fetched latest plugins...\n";
mkdir('updates_staged');
unzip('updates_staged/projecteqquests-master.zip', 'updates_staged/');
}
$fc = 0;
use File::Find;
use File::Compare;
mkdir('plugins');
my @files;
my $start_dir = "updates_staged/Quests-Plugins-master/plugins/";
my $start_dir = "updates_staged/projecteqquests-master/plugins/";
find(
sub { push @files, $File::Find::name unless -d; },
$start_dir
@ -1688,7 +1648,7 @@ sub plugins_fetch{
if($file=~/\.pl|\.lua|\.ext/i){
$staged_file = $file;
$destination_file = $file;
$destination_file =~s/updates_staged\/Quests-Plugins-master\///g;
$destination_file =~s/updates_staged\/projecteqquests-master\///g;
if (!-e $destination_file) {
copy_file($staged_file, $destination_file);
@ -2220,3 +2180,4 @@ sub generate_random_password {
return $randpassword;
}

View File

@ -114,9 +114,24 @@ if [[ "$OS" == "Debian" ]]; then
apt-get $apt_options install open-vm-tools
apt-get $apt_options install unzip
apt-get $apt_options install uuid-dev
apt-get $apt_options install wget
apt-get $apt_options install zlib-bin
apt-get $apt_options install zlibc
apt-get $apt_options install libsodium-dev
apt-get $apt_options install libsodium18
# If libsodium18 isn't installed (Debian), let's download both that and the dev package and install them.
if dpkg-query -s "libsodium18" 1>/dev/null 2>&1; then
echo "Sodium library already installed."
else
wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium-dev_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium-dev.deb
wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium18_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium18.deb
dpkg -i /home/eqemu/libsodium*.deb
# Cleanup after ourselves
rm -f /home/eqemu/libsodium-dev.deb
rm -f /home/eqemu/libsodium18.deb
fi
#::: Install FTP for remote FTP access
echo "proftpd-basic shared/proftpd/inetd_or_standalone select standalone" | debconf-set-selections
apt-get -y -q install proftpd
@ -149,8 +164,35 @@ EOF
elif [[ "$OS" == "fedora_core" ]]; then
# Do Fedora stuff
dnf -y install open-vm-tools vim cmake boost-devel zlib-devel mariadb-server mariadb-devel mariadb-libs perl perl-DBD-MySQL perl-IO-stringy perl-devel lua-devel lua-sql-mysql dos2unix php-mysql proftpd wget compat-lua-libs compat-lua-devel compat-lua perl-Time-HiRes
dnf -y groupinstall "Development Tools" "Basic Web Server" "C Development Tools and Libraries"
dnf -y install open-vm-tools
dnf -y install vim
dnf -y install cmake
dnf -y install boost-devel
dnf -y install zlib-devel
dnf -y install mariadb-server
dnf -y install mariadb-devel
dnf -y install mariadb-libs
dnf -y install perl
dnf -y install perl-DBD-MySQL
dnf -y install perl-IO-stringy
dnf -y install perl-devel
dnf -y install lua-devel
dnf -y install lua-sql-mysql
dnf -y install dos2unix
dnf -y install php-mysql
dnf -y install php-mysqlnd
dnf -y install proftpd
dnf -y install wget
dnf -y install compat-lua-libs
dnf -y install compat-lua-devel
dnf -y install compat-lua
dnf -y install perl-Time-HiRes
dnf -y install libuuid-devel
dnf -y install libsodium
dnf -y install libsodium-devel
dnf -y groupinstall "Development Tools"
dnf -y groupinstall "Basic Web Server"
dnf -y groupinstall "C Development Tools and Libraries"
fi
if [[ "$OS" == "fedora_core" ]] || [[ "$OS" == "red_hat" ]]; then

View File

@ -364,6 +364,10 @@
9108|2017_04_07_ignore_despawn.sql|SHOW COLUMNS FROM `npc_types` LIKE 'ignore_despawn'|empty|
9109|2017_04_08_doors_disable_timer.sql|SHOW COLUMNS FROM `doors` LIKE 'disable_timer'|empty|
9110|2017_04_10_graveyard.sql|show index from graveyard WHERE key_name = 'zone_id_nonunique'|empty|
9111|2017_06_24_saylink_index.sql|SHOW INDEX FROM `saylink` WHERE `key_name` = 'phrase_index'|empty|
9112|2017_06_24_rule_values_expand.sql|SHOW COLUMNS FROM rule_values WHERE Field = 'rule_value' and Type = 'varchar(30)'|empty|
9113|2017_07_19_show_name.sql|SHOW COLUMNS FROM `npc_types` LIKE 'show_name'|empty|
9114|2017_07_22_aura.sql|SHOW TABLES LIKE 'auras'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,2 @@
ALTER TABLE `rule_values`
MODIFY COLUMN `rule_value` varchar(30) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `rule_name`;

View File

@ -0,0 +1,2 @@
ALTER TABLE `saylink`
ADD INDEX `phrase_index` (`phrase`) USING BTREE ;

View File

@ -0,0 +1,3 @@
ALTER TABLE `npc_types` ADD COLUMN `show_name` TINYINT(2) NOT NULL DEFAULT 1;
ALTER TABLE `npc_types` ADD COLUMN `untargetable` TINYINT(2) NOT NULL DEFAULT 0;
UPDATE `npc_types` SET `show_name` = 0, `untargetable` = 1 WHERE `bodytype` >= 66;

View File

@ -0,0 +1,127 @@
CREATE TABLE `auras` (
`type` INT(10) NOT NULL,
`npc_type` INT(10) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`spell_id` INT(10) NOT NULL,
`distance` INT(10) NOT NULL DEFAULT 60,
`aura_type` INT(10) NOT NULL DEFAULT 1,
`spawn_type` INT(10) NOT NULL DEFAULT 0,
`movement` INT(10) NOT NULL DEFAULT 0,
`duration` INT(10) NOT NULL DEFAULT 5400,
`icon` INT(10) NOT NULL DEFAULT -1,
`cast_time` INT(10) NOT NULL DEFAULT 0,
PRIMARY KEY(`type`)
);
CREATE TABLE `character_auras` (
`id` INT(10) NOT NULL,
`slot` TINYINT(10) NOT NULL,
`spell_id` INT(10) NOT NULL,
PRIMARY KEY (`id`, `slot`)
);
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOAuraOfTheMuse55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8926, npc_type=@suggestedid, name="Aura_of_Insight", spell_id=8939, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=99, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOAuraOfTheMuse", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8488, npc_type=@suggestedid, name="Aura_of_the_Muse", spell_id=8489, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOChampionsAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8921, npc_type=@suggestedid, name="Myrmidon's_Aura", spell_id=8935, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOChampionsAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8468, npc_type=@suggestedid, name="Champion's_Aura", spell_id=8469, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOBlessedAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8925, npc_type=@suggestedid, name="Holy_Aura", spell_id=8938, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOBlessedAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8481, npc_type=@suggestedid, name="Blessed_Aura", spell_id=8482, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOMastersAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8923, npc_type=@suggestedid, name="Disciples_Aura", spell_id=8937, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOMastersAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8474, npc_type=@suggestedid, name="Master's_Aura", spell_id=8475, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOQuicksandTrap55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8933, npc_type=@suggestedid, name="Earthen_Strength", spell_id=8948, distance=60, aura_type=2, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOQuicksandTrap", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8518, npc_type=@suggestedid, name="Rathe's_Strength", spell_id=8519, distance=60, aura_type=2, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOIllusionistsAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8931, npc_type=@suggestedid, name="Beguiler's_Aura", spell_id=8946, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOIllusionistsAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8509, npc_type=@suggestedid, name="Illusionist's_Aura", spell_id=8510, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOLivingVineTrap55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8929, npc_type=@suggestedid, name="Aura_of_the_Grove", spell_id=8943, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=1, cast_time=12;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOLivingVineTrap", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8499, npc_type=@suggestedid, name="Aura_of_Life", spell_id=8500, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=1, cast_time=12;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOAuraOfThePious55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8928, npc_type=@suggestedid, name="Aura_of_the_Zealot", spell_id=8940, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOAuraOfThePious", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8495, npc_type=@suggestedid, name="Aura_of_the_Pious", spell_id=8496, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOBloodlustAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8924, npc_type=@suggestedid, name="Aura_of_Rage", spell_id=8959, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOBloodlustAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8477, npc_type=@suggestedid, name="Bloodlust_Aura", spell_id=8478, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOIdolOfMalaTrap55", lastname="", level="55", race="514", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="2.5", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8930, npc_type=@suggestedid, name="Soul_Idol", spell_id=8945, distance=60, aura_type=3, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=12;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOIdolOfMalaTrap", lastname="", level="70", race="514", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="2.5", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8504, npc_type=@suggestedid, name="Spirit_Idol", spell_id=8505, distance=60, aura_type=3, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=12;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IODeathRuneTrap55", lastname="", level="55", race="510", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8934, npc_type=@suggestedid, name="a_dark_rune", spell_id=8949, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IODeathRuneTrap", lastname="", level="70", race="510", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8523, npc_type=@suggestedid, name="a_death_rune", spell_id=8524, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOFireRuneTrap55", lastname="", level="55", race="510", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8932, npc_type=@suggestedid, name="a_fiery_rune", spell_id=8947, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOFireRuneTrap", lastname="", level="70", race="510", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8513, npc_type=@suggestedid, name="a_fire_rune", spell_id=8514, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOPoisonSpikesTrap55", lastname="", level="55", race="513", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8922, npc_type=@suggestedid, name="poison_spurs", spell_id=8936, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1;
SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___';
INSERT INTO npc_types SET id=@suggestedid, name="IOPoisonSpikesTrap", lastname="", level="70", race="513", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0";
INSERT INTO auras SET type=8471, npc_type=@suggestedid, name="Poison Spikes", spell_id=8472, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1;
UPDATE npc_types SET special_abilities = TRIM(TRAILING '^' FROM special_abilities);

View File

@ -449,17 +449,36 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) {
return false;
}
cle->SetOnline();
if(minilogin){
cle->SetOnline();
WorldConfig::DisableStats();
Log(Logs::General, Logs::World_Server, "MiniLogin Account #%d",cle->AccountID());
}
else {
if (!is_player_zoning) {
Log(Logs::General, Logs::World_Server,
"Account (%s) Logging in :: LSID: %d ", cle->AccountName(), cle->LSID());
else if (!is_player_zoning) {
// Track who is in and who is out of the game
char *inout= (char *) "";
if (cle->GetOnline() == CLE_Status_Never){
// Desktop -> Char Select
inout = (char *) "In";
}
else {
// Game -> Char Select
inout=(char *) "Out";
}
// Always at Char select at this point.
// Either from a fresh client launch or coming back from the game.
// Exiting the game entirely does not come through here.
// Could use a Logging Out Completely message somewhere.
cle->SetOnline(CLE_Status_CharSelect);
Log(Logs::General, Logs::World_Server,
"Account (%s) Logging(%s) to character select :: LSID: %d ",
cle->AccountName(), inout, cle->LSID());
}
else {
cle->SetOnline();
}
const WorldConfig *Config=WorldConfig::get();
@ -1021,6 +1040,7 @@ bool Client::HandlePacket(const EQApplicationPacket *app) {
}
case OP_WorldLogout:
{
// I don't see this getting executed on logout
eqs->Close();
cle->SetOnline(CLE_Status_Offline); //allows this player to log in again without an ip restriction.
return false;
@ -1261,6 +1281,10 @@ void Client::Clearance(int8 response)
} else {
zs_addr = zs->GetIP().c_str();
if (!zs_addr[0]) {
zs_addr = WorldConfig::get()->LocalAddress.c_str();
}
if(strcmp(zs_addr, "127.0.0.1") == 0)
{
Log(Logs::Detail, Logs::World_Server, "Local zone address was %s, setting local address to: %s", zs_addr, WorldConfig::get()->LocalAddress.c_str());

View File

@ -50,6 +50,7 @@ public:
inline const char* LSName() const { return plsname; }
inline int16 WorldAdmin() const { return pworldadmin; }
inline const char* GetLSKey() const { return plskey; }
inline const int8 GetOnline() const { return pOnline; }
// Account stuff
inline uint32 AccountID() const { return paccountid; }

View File

@ -332,6 +332,8 @@ int main(int argc, char** argv) {
database.ClearMerchantTemp();
}
RuleManager::Instance()->SaveRules(&database);
Log(Logs::General, Logs::World_Server, "Loading EQ time of day..");
TimeOfDay_Struct eqTime;
time_t realtime;
@ -392,12 +394,12 @@ int main(int argc, char** argv) {
server_connection->Listen(server_opts);
Log(Logs::General, Logs::World_Server, "Server (TCP) listener started.");
server_connection->OnConnectionIdentified("Zone", [](std::shared_ptr<EQ::Net::ServertalkServerConnection> connection) {
server_connection->OnConnectionIdentified("Zone", [&console](std::shared_ptr<EQ::Net::ServertalkServerConnection> connection) {
LogF(Logs::General, Logs::World_Server, "New Zone Server connection from {2} at {0}:{1}",
connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID());
numzones++;
zoneserver_list.Add(new ZoneServer(connection));
zoneserver_list.Add(new ZoneServer(connection, console.get()));
});
server_connection->OnConnectionRemoved("Zone", [](std::shared_ptr<EQ::Net::ServertalkServerConnection> connection) {

View File

@ -46,7 +46,7 @@ extern UCSConnection UCSLink;
extern QueryServConnection QSLink;
void CatchSignal(int sig_num);
ZoneServer::ZoneServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection)
ZoneServer::ZoneServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection, EQ::Net::ConsoleServer *console)
: tcpc(connection), zone_boot_timer(5000) {
/* Set Process tracking variable defaults */
@ -73,6 +73,8 @@ ZoneServer::ZoneServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> conn
zone_boot_timer.Disable();
}
}));
this->console = console;
}
ZoneServer::~ZoneServer() {
@ -412,6 +414,27 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break;
}
if (scm->chan_num == 7 || scm->chan_num == 14) {
if (scm->deliverto[0] == '*') {
if (console) {
auto con = console->FindByAccountName(&scm->deliverto[1]);
if (((!con) || (!con->SendChannelMessage(scm, [&scm]() {
auto pack = new ServerPacket(ServerOP_ChannelMessage,
sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1);
memcpy(pack->pBuffer, scm, pack->size);
ServerChannelMessage_Struct* scm2 = (ServerChannelMessage_Struct*)pack->pBuffer;
strcpy(scm2->deliverto, scm2->from);
scm2->noreply = true;
client_list.SendPacket(scm->from, pack);
safe_delete(pack);
}))) && (!scm->noreply))
{
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to);
}
}
break;
}
ClientListEntry* cle = client_list.FindCharacter(scm->deliverto);
if (cle == 0 || cle->Online() < CLE_Status_Zoning ||
(cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) {
@ -462,6 +485,20 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
cle->Server()->SendPacket(pack);
}
else {
if (scm->chan_num == 5 || scm->chan_num == 6 || scm->chan_num == 11) {
if (console) {
console->SendChannelMessage(scm, [&scm]() {
auto pack = new ServerPacket(ServerOP_ChannelMessage,
sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1);
memcpy(pack->pBuffer, scm, pack->size);
ServerChannelMessage_Struct* scm2 = (ServerChannelMessage_Struct*)pack->pBuffer;
strcpy(scm2->deliverto, scm2->from);
scm2->noreply = true;
client_list.SendPacket(scm->from, pack);
safe_delete(pack);
});
}
}
zoneserver_list.SendPacket(pack);
}
break;
@ -1248,6 +1285,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_CZSignalNPC:
case ServerOP_CZSetEntityVariableByNPCTypeID:
case ServerOP_CZSignalClient:
case ServerOP_CZSetEntityVariableByClientName:
case ServerOP_WWMarquee:
case ServerOP_DepopAllPlayersCorpses:
case ServerOP_DepopPlayerCorpse:

View File

@ -22,6 +22,7 @@
#include "../net/servertalk_server.h"
#include "../event/timer.h"
#include "../timer.h"
#include "console.h"
#include <string.h>
#include <string>
@ -31,7 +32,7 @@ class ServerPacket;
class ZoneServer : public WorldTCPConnection {
public:
ZoneServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection);
ZoneServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection, EQ::Net::ConsoleServer *console);
~ZoneServer();
virtual inline bool IsZoneServer() { return true; }
@ -97,6 +98,7 @@ private:
uint32 zone_os_process_id;
std::string launcher_name; //the launcher which started us
std::string launched_name; //the name of the zone we launched.
EQ::Net::ConsoleServer *console;
};
#endif

View File

@ -5,6 +5,7 @@ SET(zone_sources
aa_ability.cpp
aggro.cpp
aggromanager.cpp
aura.cpp
attack.cpp
beacon.cpp
bonuses.cpp
@ -51,6 +52,7 @@ SET(zone_sources
lua_item.cpp
lua_iteminst.cpp
lua_mob.cpp
lua_mod.cpp
lua_npc.cpp
lua_object.cpp
lua_packet.cpp
@ -59,6 +61,7 @@ SET(zone_sources
lua_raid.cpp
lua_spawn.cpp
lua_spell.cpp
lua_stat_bonuses.cpp
embperl.cpp
embxs.cpp
entity.cpp
@ -134,6 +137,7 @@ SET(zone_headers
aa.h
aa_ability.h
aggromanager.h
aura.h
basic_functions.h
beacon.h
bot.h
@ -173,6 +177,7 @@ SET(zone_headers
lua_item.h
lua_iteminst.h
lua_mob.h
lua_mod.h
lua_npc.h
lua_object.h
lua_packet.h
@ -182,6 +187,7 @@ SET(zone_headers
lua_raid.h
lua_spawn.h
lua_spell.h
lua_stat_bonuses.h
map.h
masterentity.h
maxskill.h

View File

@ -46,8 +46,16 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
if (targ != nullptr && targ->IsCorpse())
return;
// yep, even these need pet power!
int act_power = 0;
if (IsClient()) {
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
}
PetRecord record;
if (!database.GetPetEntry(spells[spell_id].teleport_zone, &record))
if (!database.GetPoweredPetEntry(spells[spell_id].teleport_zone, act_power, &record))
{
Log(Logs::General, Logs::Error, "Unknown swarm pet spell id: %d, check pets table", spell_id);
Message(13, "Unable to find data for pet %s", spells[spell_id].teleport_zone);
@ -908,7 +916,7 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) {
void Client::SendAlternateAdvancementStats() {
auto outapp = new EQApplicationPacket(OP_AAExpUpdate, sizeof(AltAdvStats_Struct));
AltAdvStats_Struct *aps = (AltAdvStats_Struct *)outapp->pBuffer;
aps->experience = (uint32)(((float)330.0f * (float)m_pp.expAA) / (float)max_AAXP);
aps->experience = (uint32)(((float)330.0f * (float)m_pp.expAA) / (float)GetRequiredAAExperience());
aps->unspent = m_pp.aapoints;
aps->percentage = m_epp.perAA;
QueuePacket(outapp);
@ -1194,6 +1202,11 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
Message_StringID(MT_SpellFailure, SNEAK_RESTRICT);
return;
}
//
// Modern clients don't require pet targeted for AA casts that are ST_Pet
if (spells[rank->spell].targettype == ST_Pet || spells[rank->spell].targettype == ST_SummonedPet)
target_id = GetPetID();
// Bards can cast instant cast AAs while they are casting another song
if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQEmu::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {

View File

@ -31,6 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "water_map.h"
#include "worldserver.h"
#include "zone.h"
#include "lua_parser.h"
#include <assert.h>
#include <stdio.h>
@ -52,7 +53,7 @@ extern WorldServer worldserver;
extern EntityList entity_list;
extern Zone* zone;
bool Mob::AttackAnimation(EQEmu::skills::SkillType &skillinuse, int Hand, const EQEmu::ItemInstance* weapon)
EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse)
{
// Determine animation
int type = 0;
@ -137,7 +138,8 @@ bool Mob::AttackAnimation(EQEmu::skills::SkillType &skillinuse, int Hand, const
type = animDualWield;
DoAnim(type, 0, false);
return true;
return skillinuse;
}
int Mob::compute_tohit(EQEmu::skills::SkillType skillinuse)
@ -271,6 +273,16 @@ int Mob::GetTotalDefense()
// and does other mitigation checks. 'this' is the mob being attacked.
bool Mob::CheckHitChance(Mob* other, DamageHitInfo &hit)
{
#ifdef LUA_EQEMU
bool lua_ret = false;
bool ignoreDefault = false;
lua_ret = LuaParser::Instance()->CheckHitChance(this, other, hit, ignoreDefault);
if(ignoreDefault) {
return lua_ret;
}
#endif
Mob *attacker = other;
Mob *defender = this;
Log(Logs::Detail, Logs::Attack, "CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName());
@ -301,6 +313,16 @@ bool Mob::CheckHitChance(Mob* other, DamageHitInfo &hit)
bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
{
#ifdef LUA_EQEMU
bool lua_ret = false;
bool ignoreDefault = false;
lua_ret = LuaParser::Instance()->AvoidDamage(this, other, hit, ignoreDefault);
if (ignoreDefault) {
return lua_ret;
}
#endif
/* called when a mob is attacked, does the checks to see if it's a hit
* and does other mitigation checks. 'this' is the mob being attacked.
*
@ -871,6 +893,15 @@ double Mob::RollD20(int offense, int mitigation)
void Mob::MeleeMitigation(Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions *opts)
{
#ifdef LUA_EQEMU
bool ignoreDefault = false;
LuaParser::Instance()->MeleeMitigation(this, attacker, hit, opts, ignoreDefault);
if (ignoreDefault) {
return;
}
#endif
if (hit.damage_done < 0 || hit.base_damage == 0)
return;
@ -1237,6 +1268,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts)
return;
Log(Logs::Detail, Logs::Combat, "%s::DoAttack vs %s base %d min %d offense %d tohit %d skill %d", GetName(),
other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill);
// check to see if we hit..
if (other->AvoidDamage(this, hit)) {
int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough;
@ -1331,7 +1363,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
DamageHitInfo my_hit;
// calculate attack_skill and skillinuse depending on hand and weapon
// also send Packet to near clients
AttackAnimation(my_hit.skill, Hand, weapon);
my_hit.skill = AttackAnimation(Hand, weapon);
Log(Logs::Detail, Logs::Combat, "Attacking with %s in slot %d using skill %d", weapon ? weapon->GetItem()->Name : "Fist", Hand, my_hit.skill);
// Now figure out damage
@ -1892,7 +1924,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
//do attack animation regardless of whether or not we can hit below
int16 charges = 0;
EQEmu::ItemInstance weapon_inst(weapon, charges);
AttackAnimation(my_hit.skill, Hand, &weapon_inst);
my_hit.skill = AttackAnimation(Hand, &weapon_inst, my_hit.skill);
//basically "if not immune" then do the attack
if (weapon_damage > 0) {
@ -2190,7 +2222,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil
Group *kg = entity_list.GetGroupByClient(give_exp_client);
Raid *kr = entity_list.GetRaidByClient(give_exp_client);
int32 finalxp = EXP_FORMULA;
int32 finalxp = give_exp_client->GetExperienceForKill(this);
finalxp = give_exp_client->mod_client_xp(finalxp, this);
if (kr) {
@ -2356,6 +2388,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil
entity_list.UnMarkNPC(GetID());
entity_list.RemoveNPC(GetID());
this->SetID(0);
if (killer != 0 && emoteid != 0)
@ -2477,7 +2510,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil
return true;
}
void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, bool iYellForHelp /*= true*/, bool bFrenzy /*= false*/, bool iBuffTic /*= false*/, uint16 spell_id)
void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, bool iYellForHelp /*= true*/, bool bFrenzy /*= false*/, bool iBuffTic /*= false*/, uint16 spell_id, bool pet_command)
{
if (!other)
return;
@ -2516,13 +2549,18 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
}
}
if (IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && !IsFocused()) { //ignore aggro if hold and !focus
return;
}
// Pet that is /pet hold on will not add to their hate list if they're not engaged
// Pet that is /pet hold on and /pet focus on will not add others to their hate list
// Pet that is /pet ghold on will never add to their hate list unless /pet attack or /pet qattack
if (IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && GetOwner()->GetAA(aaAdvancedPetDiscipline) >= 1 && IsFocused()) {
if (!targetmob)
return;
// we skip these checks if it's forced through a pet command
if (!pet_command) {
if (IsPet()) {
if ((IsGHeld() || (IsHeld() && IsFocused())) && !on_hatelist) // we want them to be able to climb the hate list
return;
if ((IsHeld() || IsPetStop() || IsPetRegroup()) && !wasengaged) // not 100% sure on stop/regroup kind of hard to test, but regroup is like "classic hold"
return;
}
}
if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0))
@ -2627,7 +2665,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
}
}
if (mypet && (!(GetAA(aaPetDiscipline) && mypet->IsHeld()))) { // I have a pet, add other to it
if (mypet && !mypet->IsHeld() && !mypet->IsPetStop()) { // I have a pet, add other to it
if (!mypet->IsFamiliar() && !mypet->GetSpecialAbility(IMMUNE_AGGRO))
mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
}
@ -2796,6 +2834,8 @@ uint8 Mob::GetWeaponDamageBonus(const EQEmu::ItemData *weapon, bool offhand)
}
return damage_bonus;
}
return 0;
}
int Mob::GetHandToHandDamage(void)
@ -3306,7 +3346,10 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
} //end `if there is some damage being done and theres anattacker person involved`
Mob *pet = GetPet();
if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse())
// pets that have GHold will never automatically add NPCs
// pets that have Hold and no Focus will add NPCs if they're engaged
// pets that have Hold and Focus will not add NPCs
if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld())
{
if (!pet->IsHeld()) {
Log(Logs::Detail, Logs::Aggro, "Sending pet %s into battle due to attack.", pet->GetName());
@ -3355,8 +3398,6 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
SetHP(GetHP() - damage);
if (IsClient() && RuleB(Character, MarqueeHPUpdates))
this->CastToClient()->SendHPUpdateMarquee();
if (HasDied()) {
bool IsSaved = false;
@ -3507,8 +3548,11 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
if (zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable.
if (IsNPC()) {
// Is this adequate?
Teleport(new_pos);
SendPosUpdate();
if (position_update_melee_push_timer.Check()) {
SendPositionUpdate();
}
}
}
else {
@ -4056,7 +4100,7 @@ void Mob::TryPetCriticalHit(Mob *defender, DamageHitInfo &hit)
if (critChance > 0) {
if (zone->random.Roll(critChance)) {
critMod += GetCritDmgMob(hit.skill);
critMod += GetCritDmgMod(hit.skill);
hit.damage_done += 5;
hit.damage_done = (hit.damage_done * critMod) / 100;
@ -4077,6 +4121,15 @@ void Mob::TryPetCriticalHit(Mob *defender, DamageHitInfo &hit)
void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts)
{
#ifdef LUA_EQEMU
bool ignoreDefault = false;
LuaParser::Instance()->TryCriticalHit(this, defender, hit, opts, ignoreDefault);
if (ignoreDefault) {
return;
}
#endif
if (hit.damage_done < 1 || !defender)
return;
@ -4185,7 +4238,11 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
// step 2: calculate damage
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
int og_damage = hit.damage_done;
int crit_mod = 170 + GetCritDmgMob(hit.skill);
int crit_mod = 170 + GetCritDmgMod(hit.skill);
if (crit_mod < 100) {
crit_mod = 100;
}
hit.damage_done = hit.damage_done * crit_mod / 100;
Log(Logs::Detail, Logs::Combat,
"Crit success roll %d dex chance %d og dmg %d crit_mod %d new dmg %d", roll, dex_bonus,
@ -4246,7 +4303,7 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
// Crippling blows also have a chance to stun
// Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a
// staggers message.
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(IMMUNE_STUN)) {
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(UNSTUNABLE)) {
defender->Emote("staggers.");
defender->Stun(2000);
}
@ -4527,6 +4584,15 @@ const DamageTable &Mob::GetDamageTable() const
void Mob::ApplyDamageTable(DamageHitInfo &hit)
{
#ifdef LUA_EQEMU
bool ignoreDefault = false;
LuaParser::Instance()->ApplyDamageTable(this, hit, ignoreDefault);
if (ignoreDefault) {
return;
}
#endif
// someone may want to add this to custom servers, can remove this if that's the case
if (!IsClient()
#ifdef BOTS
@ -4862,6 +4928,15 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
if (!defender)
return;
#ifdef LUA_EQEMU
bool ignoreDefault = false;
LuaParser::Instance()->CommonOutgoingHitSuccess(this, defender, hit, opts, ignoreDefault);
if (ignoreDefault) {
return;
}
#endif
// BER weren't parsing the halving
if (hit.skill == EQEmu::skills::SkillArchery ||
(hit.skill == EQEmu::skills::SkillThrowing && GetClass() != BERSERKER))
@ -5295,4 +5370,4 @@ void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts)
}
}
}
}
}

936
zone/aura.cpp Normal file
View File

@ -0,0 +1,936 @@
#include "../common/string_util.h"
#include "aura.h"
#include "client.h"
#include "string_ids.h"
#include "raids.h"
Aura::Aura(NPCType *type_data, Mob *owner, AuraRecord &record)
: NPC(type_data, 0, owner->GetPosition(), FlyMode3), spell_id(record.spell_id), distance(record.distance),
remove_timer(record.duration), movement_timer(100), process_timer(100), aura_id(-1)
{
GiveNPCTypeData(type_data); // we will delete this later on
m_owner = owner->GetID();
if (record.cast_time) {
cast_timer.SetTimer(record.cast_time);
cast_timer.Disable(); // we don't want to be enabled yet
}
if (record.aura_type < static_cast<int>(AuraType::Max))
type = static_cast<AuraType>(record.aura_type);
else
type = AuraType::OnAllGroupMembers;
if (record.spawn_type < static_cast<int>(AuraSpawns::Max))
spawn_type = static_cast<AuraSpawns>(record.spawn_type);
else
spawn_type = AuraSpawns::GroupMembers;
if (record.movement < static_cast<int>(AuraMovement::Max))
movement_type = static_cast<AuraMovement>(record.movement);
else
movement_type = AuraMovement::Follow;
switch (type) {
case AuraType::OnAllFriendlies:
process_func = &Aura::ProcessOnAllFriendlies;
break;
case AuraType::OnAllGroupMembers:
process_func = &Aura::ProcessOnAllGroupMembers;
break;
case AuraType::OnGroupMembersPets:
process_func = &Aura::ProcessOnGroupMembersPets;
break;
case AuraType::Totem:
process_func = &Aura::ProcessTotem;
break;
case AuraType::EnterTrap:
process_func = &Aura::ProcessEnterTrap;
break;
case AuraType::ExitTrap:
process_func = &Aura::ProcessExitTrap;
break;
default:
process_func = nullptr;
}
}
Mob *Aura::GetOwner()
{
return entity_list.GetMob(m_owner);
}
// not 100% sure how this one should work and PVP affects ...
void Aura::ProcessOnAllFriendlies(Mob *owner)
{
auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline
std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
for (auto &e : mob_list) {
auto mob = e.second;
if (mob->IsClient() || mob->IsPetOwnerClient() || mob->IsMerc()) {
auto it = casted_on.find(mob->GetID());
if (it != casted_on.end()) { // we are already on the list, let's check for removal
if (DistanceSquared(GetPosition(), mob->GetPosition()) > distance)
delayed_remove.insert(mob->GetID());
} else { // not on list, lets check if we're in range
if (DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
}
}
}
}
for (auto &e : delayed_remove) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove
mob->BuffFadeBySpellIDAndCaster(spell_id, GetID());
casted_on.erase(e);
}
// so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it
if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty())
cast_timer.Start();
if (!cast_timer.Enabled() || !cast_timer.Check())
return;
for (auto &e : casted_on) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr)
SpellFinished(spell_id, mob);
}
}
void Aura::ProcessOnAllGroupMembers(Mob *owner)
{
auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline
std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
if (owner->IsRaidGrouped() && owner->IsClient()) { // currently raids are just client, but safety check
auto raid = owner->GetRaid();
if (raid == nullptr) { // well shit
owner->RemoveAura(GetID(), false, true);
return;
}
auto group_id = raid->GetGroup(owner->CastToClient());
// some lambdas so the for loop is less horrible ...
auto verify_raid_client = [&raid, &group_id, this](Client *c) {
auto idx = raid->GetPlayerIndex(c);
if (c->GetID() == m_owner) {
return DistanceSquared(GetPosition(), c->GetPosition()) <= distance;
} else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) {
return false;
} else if (DistanceSquared(GetPosition(), c->GetPosition()) > distance) {
return false;
}
return true;
};
auto verify_raid_client_pet = [&raid, &group_id, this](Mob *m) {
auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient());
if (m->GetOwner()->GetID() == m_owner) {
return DistanceSquared(GetPosition(), m->GetPosition()) <= distance;
} else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) {
return false;
} else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) {
return false;
}
return true;
};
auto verify_raid_client_swarm = [&raid, &group_id, this](NPC *n) {
auto owner = entity_list.GetMob(n->GetSwarmOwner());
if (owner == nullptr)
return false;
auto idx = raid->GetPlayerIndex(owner->CastToClient());
if (owner->GetID() == m_owner) {
return DistanceSquared(GetPosition(), n->GetPosition()) <= distance;
} else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) {
return false;
} else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) {
return false;
}
return true;
};
for (auto &e : mob_list) {
auto mob = e.second;
// step 1: check if we're already managing this NPC's buff
auto it = casted_on.find(mob->GetID());
if (it != casted_on.end()) {
// verify still good!
if (mob->IsClient()) {
if (!verify_raid_client(mob->CastToClient()))
delayed_remove.insert(mob->GetID());
} else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) {
if (!verify_raid_client_pet(mob))
delayed_remove.insert(mob->GetID());
} else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
auto npc = mob->CastToNPC();
if (!verify_raid_client_swarm(npc))
delayed_remove.insert(mob->GetID());
}
} else { // we're not on it!
if (mob->IsClient() && verify_raid_client(mob->CastToClient())) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
} else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
} else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
auto npc = mob->CastToNPC();
if (verify_raid_client_swarm(npc)) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
}
}
}
}
} else if (owner->IsGrouped()) {
auto group = owner->GetGroup();
if (group == nullptr) { // uh oh
owner->RemoveAura(GetID(), false, true);
return;
}
// lambdas to make for loop less ugly
auto verify_group_pet = [&group, this](Mob *m) {
auto owner = m->GetOwner();
if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance)
return true;
return false;
};
auto verify_group_swarm = [&group, this](NPC *n) {
auto owner = entity_list.GetMob(n->GetSwarmOwner());
if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance)
return true;
return false;
};
for (auto &e : mob_list) {
auto mob = e.second;
auto it = casted_on.find(mob->GetID());
if (it != casted_on.end()) { // make sure we're still valid
if (mob->IsPet()) {
if (!verify_group_pet(mob))
delayed_remove.insert(mob->GetID());
} else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) {
if (!verify_group_swarm(mob->CastToNPC()))
delayed_remove.insert(mob->GetID());
} else if (!group->IsGroupMember(mob) || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) {
delayed_remove.insert(mob->GetID());
}
} else { // not on, check if we should be!
if (mob->IsPet() && verify_group_pet(mob)) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
} else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
} else if (group->IsGroupMember(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
}
}
}
} else {
auto verify_solo = [&owner, this](Mob *m) {
if (m->IsPet() && m->GetOwnerID() == owner->GetID())
return true;
else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == owner->GetID())
return true;
else if (m->GetID() == owner->GetID())
return true;
else
return false;
};
for (auto &e : mob_list) {
auto mob = e.second;
auto it = casted_on.find(mob->GetID());
bool good = verify_solo(mob);
if (it != casted_on.end()) { // make sure still valid
if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) {
delayed_remove.insert(mob->GetID());
}
} else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
}
}
}
for (auto &e : delayed_remove) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove
mob->BuffFadeBySpellIDAndCaster(spell_id, GetID());
casted_on.erase(e);
}
// so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it
if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty())
cast_timer.Start();
if (!cast_timer.Enabled() || !cast_timer.Check())
return;
// some auras have to recast (DRU for example, non-buff too)
for (auto &e : casted_on) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr)
SpellFinished(spell_id, mob);
}
}
void Aura::ProcessOnGroupMembersPets(Mob *owner)
{
auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline
std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
// This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura)
auto group_member = owner->GetOwnerOrSelf();
if (group_member->IsRaidGrouped() && group_member->IsClient()) { // currently raids are just client, but safety check
auto raid = group_member->GetRaid();
if (raid == nullptr) { // well shit
owner->RemoveAura(GetID(), false, true);
return;
}
auto group_id = raid->GetGroup(group_member->CastToClient());
// some lambdas so the for loop is less horrible ...
auto verify_raid_client_pet = [&raid, &group_id, &group_member, this](Mob *m) {
auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient());
if (m->GetOwner()->GetID() == group_member->GetID()) {
return DistanceSquared(GetPosition(), m->GetPosition()) <= distance;
} else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) {
return false;
} else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) {
return false;
}
return true;
};
auto verify_raid_client_swarm = [&raid, &group_id, &group_member, this](NPC *n) {
auto owner = entity_list.GetMob(n->GetSwarmOwner());
if (owner == nullptr)
return false;
auto idx = raid->GetPlayerIndex(owner->CastToClient());
if (owner->GetID() == group_member->GetID()) {
return DistanceSquared(GetPosition(), n->GetPosition()) <= distance;
} else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) {
return false;
} else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) {
return false;
}
return true;
};
for (auto &e : mob_list) {
auto mob = e.second;
// step 1: check if we're already managing this NPC's buff
auto it = casted_on.find(mob->GetID());
if (it != casted_on.end()) {
// verify still good!
if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) {
if (!verify_raid_client_pet(mob))
delayed_remove.insert(mob->GetID());
} else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
auto npc = mob->CastToNPC();
if (!verify_raid_client_swarm(npc))
delayed_remove.insert(mob->GetID());
}
} else { // we're not on it!
if (mob->IsClient()) {
continue; // never hit client
} else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
} else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
auto npc = mob->CastToNPC();
if (verify_raid_client_swarm(npc)) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
}
}
}
}
} else if (group_member->IsGrouped()) {
auto group = group_member->GetGroup();
if (group == nullptr) { // uh oh
owner->RemoveAura(GetID(), false, true);
return;
}
// lambdas to make for loop less ugly
auto verify_group_pet = [&group, this](Mob *m) {
auto owner = m->GetOwner();
if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance)
return true;
return false;
};
auto verify_group_swarm = [&group, this](NPC *n) {
auto owner = entity_list.GetMob(n->GetSwarmOwner());
if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance)
return true;
return false;
};
for (auto &e : mob_list) {
auto mob = e.second;
auto it = casted_on.find(mob->GetID());
if (it != casted_on.end()) { // make sure we're still valid
if (mob->IsPet()) {
if (!verify_group_pet(mob))
delayed_remove.insert(mob->GetID());
} else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) {
if (!verify_group_swarm(mob->CastToNPC()))
delayed_remove.insert(mob->GetID());
}
} else { // not on, check if we should be!
if (mob->IsClient()) {
continue;
} else if (mob->IsPet() && verify_group_pet(mob)) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
} else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
}
}
}
} else {
auto verify_solo = [&group_member, this](Mob *m) {
if (m->IsPet() && m->GetOwnerID() == group_member->GetID())
return true;
else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == group_member->GetID())
return true;
else
return false;
};
for (auto &e : mob_list) {
auto mob = e.second;
auto it = casted_on.find(mob->GetID());
bool good = verify_solo(mob);
if (it != casted_on.end()) { // make sure still valid
if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) {
delayed_remove.insert(mob->GetID());
}
} else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) {
casted_on.insert(mob->GetID());
if (is_buff)
SpellFinished(spell_id, mob);
}
}
}
for (auto &e : delayed_remove) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove
mob->BuffFadeBySpellIDAndCaster(spell_id, GetID());
casted_on.erase(e);
}
// so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it
if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty())
cast_timer.Start();
if (!cast_timer.Enabled() || !cast_timer.Check())
return;
// some auras have to recast (DRU for example, non-buff too)
for (auto &e : casted_on) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr)
SpellFinished(spell_id, mob);
}
}
void Aura::ProcessTotem(Mob *owner)
{
auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline
std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
for (auto &e : mob_list) {
auto mob = e.second;
if (mob == this)
continue;
if (mob == owner)
continue;
if (owner->IsAttackAllowed(mob)) { // might need more checks ...
bool in_range = DistanceSquared(GetPosition(), mob->GetPosition()) <= distance;
auto it = casted_on.find(mob->GetID());
if (it != casted_on.end()) {
if (!in_range)
delayed_remove.insert(mob->GetID());
} else if (in_range) {
casted_on.insert(mob->GetID());
SpellFinished(spell_id, mob);
}
}
}
for (auto &e : delayed_remove) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove
mob->BuffFadeBySpellIDAndCaster(spell_id, GetID());
casted_on.erase(e);
}
// so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it
if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty())
cast_timer.Start();
if (!cast_timer.Enabled() || !cast_timer.Check())
return;
for (auto &e : casted_on) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr)
SpellFinished(spell_id, mob);
}
}
void Aura::ProcessEnterTrap(Mob *owner)
{
auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline
for (auto &e : mob_list) {
auto mob = e.second;
if (mob == this)
continue;
// might need more checks ...
if (owner->IsAttackAllowed(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) {
SpellFinished(spell_id, mob);
owner->RemoveAura(GetID(), false); // if we're a buff (ex. NEC) we don't want to strip :P
break;
}
}
}
void Aura::ProcessExitTrap(Mob *owner)
{
auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline
for (auto &e : mob_list) {
auto mob = e.second;
if (mob == this)
continue;
// might need more checks ...
if (owner->IsAttackAllowed(mob)) {
bool in_range = DistanceSquared(GetPosition(), mob->GetPosition()) <= distance;
auto it = casted_on.find(mob->GetID());
if (it != casted_on.end()) {
if (!in_range) {
SpellFinished(spell_id, mob);
owner->RemoveAura(GetID(), false); // if we're a buff we don't want to strip :P
break;
}
} else if (in_range) {
casted_on.insert(mob->GetID());
}
}
}
}
// this is less than ideal, but other solutions are a bit all over the place
// and hard to reason about
void Aura::ProcessSpawns()
{
const auto &clients = entity_list.GetClientList();
for (auto &e : clients) {
auto c = e.second;
bool spawned = spawned_for.find(c->GetID()) != spawned_for.end();
if (ShouldISpawnFor(c)) {
if (!spawned) {
EQApplicationPacket app;
CreateSpawnPacket(&app, this);
c->QueuePacket(&app);
SendArmorAppearance(c);
spawned_for.insert(c->GetID());
}
} else if (spawned) {
EQApplicationPacket app;
CreateDespawnPacket(&app, false);
c->QueuePacket(&app);
spawned_for.erase(c->GetID());
}
}
return;
}
bool Aura::Process()
{
// Aura::Depop clears buffs
if (p_depop)
return false;
auto owner = entity_list.GetMob(m_owner);
if (owner == nullptr) {
Depop();
return true;
}
if (remove_timer.Check()) {
owner->RemoveAura(GetID(), false, true);
return true;
}
if (movement_type == AuraMovement::Follow && GetPosition() != owner->GetPosition() && movement_timer.Check()) {
m_Position = owner->GetPosition();
auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
auto spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer;
MakeSpawnUpdate(spu);
auto it = spawned_for.begin();
while (it != spawned_for.end()) {
auto client = entity_list.GetClientByID(*it);
if (client) {
client->QueuePacket(app);
++it;
} else {
it = spawned_for.erase(it);
}
}
}
// TODO: waypoints?
if (!process_timer.Check())
return true;
if (spawn_type != AuraSpawns::Noone)
ProcessSpawns(); // bit of a hack
if (process_func)
process_func(*this, owner);
// TODO: quest calls
return true;
}
bool Aura::ShouldISpawnFor(Client *c)
{
if (spawn_type == AuraSpawns::Noone)
return false;
if (spawn_type == AuraSpawns::Everyone)
return true;
// hey, it's our owner!
if (c->GetID() == m_owner)
return true;
// so this one is a bit trickier
auto owner = GetOwner();
if (owner == nullptr)
return false; // hmm
owner = owner->GetOwnerOrSelf(); // pet auras we need the pet's owner
if (owner == nullptr) // shouldn't really be needed
return false;
// gotta check again for pet aura case -.-
if (owner == c)
return true;
if (owner->IsRaidGrouped() && owner->IsClient()) {
auto raid = owner->GetRaid();
if (raid == nullptr)
return false; // hmm
auto group_id = raid->GetGroup(owner->CastToClient());
if (group_id == 0xFFFFFFFF) // owner handled above, and they're in a raid and groupless
return false;
auto idx = raid->GetPlayerIndex(c);
if (idx == 0xFFFFFFFF) // they're not in our raid!
return false;
if (raid->members[idx].GroupNumber != group_id) // in our raid, but not our group
return false;
return true; // we got here so we know that 1 they're in our raid and 2 they're in our group!
} else if (owner->IsGrouped()) {
auto group = owner->GetGroup();
if (group == nullptr)
return false; // hmm
// easy, in our group
return group->IsGroupMember(c);
}
// our owner is not raided or grouped, and they're handled above so we don't spawn!
return false;
}
void Aura::Depop(bool skip_strip)
{
// NEC trap casts a dot, so we need some way to not strip :P
if (!skip_strip && IsBuffSpell(spell_id)) {
for (auto &e : casted_on) {
auto mob = entity_list.GetMob(e);
if (mob != nullptr)
mob->BuffFadeBySpellIDAndCaster(spell_id, GetID());
}
}
casted_on.clear();
p_depop = true;
}
// This creates an aura from a casted spell
void Mob::MakeAura(uint16 spell_id)
{
// TODO: verify room in AuraMgr
if (!IsValidSpell(spell_id))
return;
AuraRecord record;
if (!database.GetAuraEntry(spell_id, record)) {
Message(13, "Unable to find data for aura %s", spells[spell_id].name);
Log(Logs::General, Logs::Error, "Unable to find data for aura %d, check auras table.", spell_id);
return;
}
if (!IsValidSpell(record.spell_id)) {
Message(13, "Casted spell (%d) is not valid for aura %s", record.spell_id, spells[spell_id].name);
Log(Logs::General, Logs::Error, "Casted spell (%d) is not valid for aura %d, check auras table.",
record.spell_id, spell_id);
return;
}
if (record.aura_type > static_cast<int>(AuraType::Max)) {
return; // TODO: log
}
bool trap = false;
switch (static_cast<AuraType>(record.aura_type)) {
case AuraType::ExitTrap:
case AuraType::EnterTrap:
case AuraType::Totem:
trap = true;
break;
default:
trap = false;
break;
}
if (!CanSpawnAura(trap))
return;
const auto base = database.LoadNPCTypesData(record.npc_type);
if (base == nullptr) {
Message(13, "Unable to load NPC data for aura %s", spells[spell_id].teleport_zone);
Log(Logs::General, Logs::Error,
"Unable to load NPC data for aura %s (NPC ID %d), check auras and npc_types tables.",
spells[spell_id].teleport_zone, record.npc_type);
return;
}
auto npc_type = new NPCType;
memcpy(npc_type, base, sizeof(NPCType));
strn0cpy(npc_type->name, record.name, 64);
auto npc = new Aura(npc_type, this, record);
npc->SetAuraID(spell_id);
entity_list.AddNPC(npc, false);
if (trap)
AddTrap(npc, record);
else
AddAura(npc, record);
}
bool ZoneDatabase::GetAuraEntry(uint16 spell_id, AuraRecord &record)
{
auto query = StringFormat("SELECT npc_type, name, spell_id, distance, aura_type, spawn_type, movement, "
"duration, icon, cast_time FROM auras WHERE type='%d'",
spell_id);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
if (results.RowCount() != 1)
return false;
auto row = results.begin();
record.npc_type = atoi(row[0]);
strn0cpy(record.name, row[1], 64);
record.spell_id = atoi(row[2]);
record.distance = atoi(row[3]);
record.distance *= record.distance; // so we can avoid sqrt
record.aura_type = atoi(row[4]);
record.spawn_type = atoi(row[5]);
record.movement = atoi(row[6]);
record.duration = atoi(row[7]) * 1000; // DB is in seconds
record.icon = atoi(row[8]);
record.cast_time = atoi(row[9]) * 1000; // DB is in seconds
return true;
}
void Mob::AddAura(Aura *aura, AuraRecord &record)
{
// this is called only when it's safe
assert(aura != nullptr);
strn0cpy(aura_mgr.auras[aura_mgr.count].name, aura->GetCleanName(), 64);
aura_mgr.auras[aura_mgr.count].spawn_id = aura->GetID();
aura_mgr.auras[aura_mgr.count].aura = aura;
if (record.icon == -1)
aura_mgr.auras[aura_mgr.count].icon = spells[record.spell_id].new_icon;
else
aura_mgr.auras[aura_mgr.count].icon = record.icon;
if (IsClient()) {
auto outapp = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraCreate_Struct));
auto aura_create = (AuraCreate_Struct *)outapp->pBuffer;
aura_create->action = 0;
aura_create->type = 1; // this can be 0 sometimes too
strn0cpy(aura_create->aura_name, aura_mgr.auras[aura_mgr.count].name, 64);
aura_create->entity_id = aura_mgr.auras[aura_mgr.count].spawn_id;
aura_create->icon = aura_mgr.auras[aura_mgr.count].icon;
CastToClient()->FastQueuePacket(&outapp);
}
// we can increment this now
aura_mgr.count++;
}
void Mob::AddTrap(Aura *aura, AuraRecord &record)
{
// this is called only when it's safe
assert(aura != nullptr);
strn0cpy(trap_mgr.auras[trap_mgr.count].name, aura->GetCleanName(), 64);
trap_mgr.auras[trap_mgr.count].spawn_id = aura->GetID();
trap_mgr.auras[trap_mgr.count].aura = aura;
if (record.icon == -1)
trap_mgr.auras[trap_mgr.count].icon = spells[record.spell_id].new_icon;
else
trap_mgr.auras[trap_mgr.count].icon = record.icon;
// doesn't send to client
trap_mgr.count++;
}
bool Mob::CanSpawnAura(bool trap)
{
if (trap && !HasFreeTrapSlots()) {
Message_StringID(MT_SpellFailure, NO_MORE_TRAPS);
return false;
} else if (!trap && !HasFreeAuraSlots()) {
Message_StringID(MT_SpellFailure, NO_MORE_AURAS);
return false;
}
return true;
}
void Mob::RemoveAllAuras()
{
if (IsClient()) {
database.SaveAuras(CastToClient());
EQApplicationPacket outapp(OP_UpdateAura, 4);
outapp.WriteUInt32(2);
CastToClient()->QueuePacket(&outapp);
}
// this is sent on camp/zone, so it just despawns?
if (aura_mgr.count) {
for (auto &e : aura_mgr.auras) {
if (e.aura)
e.aura->Depop();
}
}
aura_mgr.count = 0;
if (trap_mgr.count) {
for (auto &e : trap_mgr.auras) {
if (e.aura)
e.aura->Depop();
}
}
trap_mgr.count = 0;
return;
}
void Mob::RemoveAura(int spawn_id, bool skip_strip, bool expired)
{
for (int i = 0; i < aura_mgr.count; ++i) {
auto &aura = aura_mgr.auras[i];
if (aura.spawn_id == spawn_id) {
if (aura.aura)
aura.aura->Depop(skip_strip);
if (expired && IsClient()) {
CastToClient()->SendColoredText(
CC_Yellow, StringFormat("%s has expired.", aura.name)); // TODO: verify color
// need to update client UI too
auto app = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraDestory_Struct));
auto ads = (AuraDestory_Struct *)app->pBuffer;
ads->action = 1; // delete
ads->entity_id = spawn_id;
CastToClient()->QueuePacket(app);
safe_delete(app);
}
while (aura_mgr.count - 1 > i) {
i++;
aura.spawn_id = aura_mgr.auras[i].spawn_id;
aura.icon = aura_mgr.auras[i].icon;
aura.aura = aura_mgr.auras[i].aura;
aura_mgr.auras[i].aura = nullptr;
strn0cpy(aura.name, aura_mgr.auras[i].name, 64);
}
aura_mgr.count--;
return;
}
}
for (int i = 0; i < trap_mgr.count; ++i) {
auto &aura = trap_mgr.auras[i];
if (aura.spawn_id == spawn_id) {
if (aura.aura)
aura.aura->Depop(skip_strip);
if (expired && IsClient())
CastToClient()->SendColoredText(
CC_Yellow, StringFormat("%s has expired.", aura.name)); // TODO: verify color
while (trap_mgr.count - 1 > i) {
i++;
aura.spawn_id = trap_mgr.auras[i].spawn_id;
aura.icon = trap_mgr.auras[i].icon;
aura.aura = trap_mgr.auras[i].aura;
trap_mgr.auras[i].aura = nullptr;
strn0cpy(aura.name, trap_mgr.auras[i].name, 64);
}
trap_mgr.count--;
return;
}
}
return;
}

91
zone/aura.h Normal file
View File

@ -0,0 +1,91 @@
#ifndef AURA_H
#define AURA_H
#include <functional>
#include <set>
#include "mob.h"
#include "npc.h"
#include "../common/types.h"
#include "../common/timer.h"
class Group;
class Raid;
class Mob;
struct NPCType;
enum class AuraType {
OnAllFriendlies, // AE PC/Pet basically (ex. Circle of Power)
OnAllGroupMembers, // Normal buffing aura (ex. Champion's Aura)
OnGroupMembersPets, // Hits just pets (ex. Rathe's Strength)
Totem, // Starts pulsing on a timer when an enemy enters (ex. Idol of Malos)
EnterTrap, // Casts once when an enemy enters (ex. Fire Rune)
ExitTrap, // Casts when they start to flee (ex. Poison Spikes Trap)
FullyScripted, // We just call script function not a predefined
Max
};
enum class AuraSpawns {
GroupMembers, // most auras use this
Everyone, // this is like traps and clickies who cast on everyone
Noone, // custom!
Max
};
enum class AuraMovement {
Follow, // follows caster
Stationary,
Pathing, // some sorted pathing TODO: implement
Max
};
class Aura : public NPC
{
// NOTE: We may have to override more virtual functions if they're causing issues
public:
Aura(NPCType *type_data, Mob *owner, AuraRecord &record);
~Aura() { };
bool IsAura() const { return true; }
bool Process();
void Depop(bool skip_strip = false);
Mob *GetOwner();
void ProcessOnAllFriendlies(Mob *owner);
void ProcessOnAllGroupMembers(Mob *owner);
void ProcessOnGroupMembersPets(Mob *owner);
void ProcessTotem(Mob *owner);
void ProcessEnterTrap(Mob *owner);
void ProcessExitTrap(Mob *owner);
void ProcessSpawns();
// we only save auras that follow you, and player casted
inline bool AuraZones() { return movement_type == AuraMovement::Follow && aura_id > -1; }
inline int GetSpellID() { return spell_id; }
inline int GetAuraID() { return aura_id; }
inline void SetAuraID(int in) { aura_id = in; }
bool ShouldISpawnFor(Client *c);
// so when we join a group, we need to spawn not already spawned auras
// This is only possible when spawn type is GroupMembers
inline bool JoinGroupSpawnCheck() { return spawn_type == AuraSpawns::GroupMembers; }
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
Timer remove_timer; // when we depop
Timer process_timer; // rate limit process calls
Timer cast_timer; // some auras pulse
Timer movement_timer; // rate limit movement updates
AuraType type;
AuraSpawns spawn_type;
AuraMovement movement_type;
std::function<void(Aura &, Mob *)> process_func;
std::set<int> casted_on; // we keep track of the other entities we've casted on
std::set<int> spawned_for;
};
#endif /* !AURA_H */

View File

@ -68,6 +68,7 @@ Beacon::Beacon(Mob *at_mob, int lifetime)
resist_adjust = 0;
spell_iterations = 0;
caster_id = 0;
max_targets = 4; // default
if(lifetime)
remove_timer.Start();
@ -93,10 +94,12 @@ bool Beacon::Process()
)
{
Mob *caster = entity_list.GetMob(caster_id);
if(caster && spell_iterations--)
if(caster && spell_iterations-- && max_targets)
{
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()); //NPC AE spells do not affect the NPC caster
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust);
// NPCs should never be affected by an AE they cast. PB AEs shouldn't affect caster either
// I don't think any other cases that get here matter
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].targettype != ST_AECaster;
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets);
}
else
{
@ -126,6 +129,8 @@ void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adj
this->resist_adjust = resist_adjust;
spell_iterations = spells[spell_id].AEDuration / 2500;
spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1
if (spells[spell_id].aemaxtargets)
max_targets = spells[spell_id].aemaxtargets;
spell_timer.Start(2500);
spell_timer.Trigger();
}

View File

@ -56,6 +56,7 @@ protected:
int16 resist_adjust;
int spell_iterations;
Timer spell_timer;
int max_targets;
uint16 caster_id;
private:

View File

@ -48,6 +48,14 @@ void Mob::CalcBonuses()
SetAttackTimer();
CalcAC();
/* Fast walking NPC's are prone to disappear into walls/hills
We set this here because NPC's can cast spells to change walkspeed/runspeed
*/
float get_walk_speed = static_cast<float>(0.025f * this->GetWalkspeed());
if (get_walk_speed >= 0.9 && this->fix_z_timer.GetDuration() != 100) {
this->fix_z_timer.SetTimer(100);
}
rooted = FindType(SE_Root);
}
@ -1084,9 +1092,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
break;
// base1 = effect value, base2 = skill restrictions(-1 for all)
if (base2 == ALL_SKILLS)
newbon->CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 1] += base1;
newbon->CritDmgMod[EQEmu::skills::HIGHEST_SKILL + 1] += base1;
else
newbon->CritDmgMob[base2] += base1;
newbon->CritDmgMod[base2] += base1;
break;
}
@ -1442,11 +1450,27 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
newbon->FeignedCastOnChance = base1;
break;
case SE_AddPetCommand:
if (base1 && base2 < PET_MAXCOMMANDS)
newbon->PetCommands[base2] = true;
break;
case SE_FeignedMinion:
if (newbon->FeignedMinionChance < base1)
newbon->FeignedMinionChance = base1;
break;
case SE_AdditionalAura:
newbon->aura_slots += base1;
break;
case SE_IncreaseTrapCount:
newbon->trap_slots += base1;
break;
// to do
case SE_PetDiscipline:
break;
case SE_PetDiscipline2:
break;
case SE_PotionBeltSlots:
break;
case SE_BandolierSlots:
@ -1465,8 +1489,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
break;
case SE_TrapCircumvention:
break;
case SE_FeignedMinion:
break;
// not handled here
case SE_HastenedAASkill:
@ -2435,9 +2457,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
if (base2 > EQEmu::skills::HIGHEST_SKILL)
break;
if(base2 == ALL_SKILLS)
new_bonus->CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 1] += effect_value;
new_bonus->CritDmgMod[EQEmu::skills::HIGHEST_SKILL + 1] += effect_value;
else
new_bonus->CritDmgMob[base2] += effect_value;
new_bonus->CritDmgMod[base2] += effect_value;
break;
}
@ -3187,6 +3209,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
if (new_bonus->FeignedCastOnChance < effect_value)
new_bonus->FeignedCastOnChance = effect_value;
break;
case SE_AdditionalAura:
if (new_bonus->aura_slots < effect_value)
new_bonus->aura_slots = effect_value;
break;
case SE_IncreaseTrapCount:
if (new_bonus->trap_slots < effect_value)
new_bonus->trap_slots = effect_value;
break;
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
if (IsAISpellEffect) {
@ -4197,9 +4229,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
{
for (int e = 0; e < EQEmu::skills::HIGHEST_SKILL + 1; e++)
{
spellbonuses.CritDmgMob[e] = effect_value;
aabonuses.CritDmgMob[e] = effect_value;
itembonuses.CritDmgMob[e] = effect_value;
spellbonuses.CritDmgMod[e] = effect_value;
aabonuses.CritDmgMod[e] = effect_value;
itembonuses.CritDmgMod[e] = effect_value;
}
break;
}

View File

@ -22,6 +22,7 @@
#include "object.h"
#include "doors.h"
#include "quest_parser_collection.h"
#include "lua_parser.h"
#include "../common/string_util.h"
#include "../common/say_link.h"
@ -83,7 +84,7 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
GenerateBaseStats();
// Calculate HitPoints Last As It Uses Base Stats
cur_hp = GenerateBaseHitPoints();
cur_mana = GenerateBaseManaPoints();
current_mana = GenerateBaseManaPoints();
cur_end = CalcBaseEndurance();
hp_regen = CalcHPRegen();
mana_regen = CalcManaRegen();
@ -128,7 +129,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
cur_hp = npcTypeData.cur_hp;
cur_mana = npcTypeData.Mana;
current_mana = npcTypeData.Mana;
RestRegenHP = 0;
RestRegenMana = 0;
RestRegenEndurance = 0;
@ -205,8 +206,8 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
SpellOnTarget(756, this); // Rezz effects
}
if(cur_mana > max_mana)
cur_mana = max_mana;
if(current_mana > max_mana)
current_mana = max_mana;
cur_end = max_end;
}
@ -2171,7 +2172,7 @@ void Bot::AI_Process() {
}
if(IsMoving())
SendPosUpdate();
SendPositionUpdate();
else
SendPosition();
}
@ -2382,7 +2383,7 @@ void Bot::AI_Process() {
// TODO: Test RuleB(Bots, UpdatePositionWithTimer)
if(IsMoving())
SendPosUpdate();
SendPositionUpdate();
else
SendPosition();
}
@ -2504,7 +2505,7 @@ void Bot::AI_Process() {
}
if(IsMoving())
SendPosUpdate();
SendPositionUpdate();
else
SendPosition();
}
@ -2928,6 +2929,7 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
ns->spawn.light = m_Light.Type[EQEmu::lightsource::LightActive];
ns->spawn.helm = helmtexture; //(GetShowHelm() ? helmtexture : 0); //0xFF;
ns->spawn.equip_chest2 = texture; //0xFF;
ns->spawn.show_name = true;
const EQEmu::ItemData* item = nullptr;
const EQEmu::ItemInstance* inst = nullptr;
uint32 spawnedbotid = 0;
@ -3852,11 +3854,11 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, EQEmu::skills::SkillT
}
//void Bot::AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false)
void Bot::AddToHateList(Mob* other, uint32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic) {
Mob::AddToHateList(other, hate, damage, iYellForHelp, bFrenzy, iBuffTic);
void Bot::AddToHateList(Mob* other, uint32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic, bool pet_command) {
Mob::AddToHateList(other, hate, damage, iYellForHelp, bFrenzy, iBuffTic, pet_command);
}
bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) {
bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) {
if (!other) {
SetTarget(nullptr);
Log(Logs::General, Logs::Error, "A null Mob object was passed to Bot::Attack for evaluation!");
@ -3919,7 +3921,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
// calculate attack_skill and skillinuse depending on hand and weapon
// also send Packet to near clients
DamageHitInfo my_hit;
AttackAnimation(my_hit.skill, Hand, weapon);
my_hit.skill = AttackAnimation(Hand, weapon);
Log(Logs::Detail, Logs::Combat, "Attacking with %s in slot %d using skill %d", weapon?weapon->GetItem()->Name:"Fist", Hand, my_hit.skill);
// Now figure out damage
@ -5556,8 +5558,8 @@ int32 Bot::CalcMaxMana() {
}
}
if(cur_mana > max_mana)
cur_mana = max_mana;
if(current_mana > max_mana)
current_mana = max_mana;
else if(max_mana < 0)
max_mana = 0;

View File

@ -325,7 +325,7 @@ public:
bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
void SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color);
void Camp(bool databaseSave = true);
virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false);
virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false, bool pet_command = false);
virtual void SetTarget(Mob* mob);
virtual void Zone();
std::vector<AISpells_Struct> GetBotSpells() { return AIspells; }

View File

@ -119,7 +119,6 @@ Client::Client(EQStreamInterface* ieqs)
0,
0
),
//these must be listed in the order they appear in client.h
position_timer(250),
hpupdate_timer(2000),
camp_timer(29000),
@ -158,10 +157,15 @@ Client::Client(EQStreamInterface* ieqs)
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
last_region_type(RegionTypeUnsupported),
m_dirtyautohaters(false),
npc_close_scan_timer(6000)
npc_close_scan_timer(6000),
hp_self_update_throttle_timer(300),
hp_other_update_throttle_timer(500),
position_update_timer(10000)
{
for(int cf=0; cf < _FilterCount; cf++)
ClientFilters[cf] = FilterShow;
for (int client_filter = 0; client_filter < _FilterCount; client_filter++)
ClientFilters[client_filter] = FilterShow;
character_id = 0;
conn_state = NoPacketsReceived;
client_data_loaded = false;
@ -190,8 +194,10 @@ Client::Client(EQStreamInterface* ieqs)
strcpy(account_name, "");
tellsoff = false;
last_reported_mana = 0;
last_reported_endur = 0;
gmhideme = false;
last_reported_endurance = 0;
last_reported_endurance_percent = 0;
last_reported_mana_percent = 0;
gm_hide_me = false;
AFK = false;
LFG = false;
LFGFromLevel = 0;
@ -255,7 +261,7 @@ Client::Client(EQStreamInterface* ieqs)
memset(&m_epp, 0, sizeof(m_epp));
PendingTranslocate = false;
PendingSacrifice = false;
BoatID = 0;
controlling_boat_id = 0;
KarmaUpdateTimer = new Timer(RuleI(Chat, KarmaUpdateIntervalMS));
GlobalChatLimiterTimer = new Timer(RuleI(Chat, IntervalDurationMS));
@ -268,7 +274,7 @@ Client::Client(EQStreamInterface* ieqs)
RestRegenMana = 0;
RestRegenEndurance = 0;
XPRate = 100;
cur_end = 0;
current_endurance = 0;
m_TimeSinceLastPositionCheck = 0;
m_DistanceSinceLastPositionCheck = 0.0f;
@ -286,7 +292,7 @@ Client::Client(EQStreamInterface* ieqs)
HideCorpseMode = HideCorpseNone;
PendingGuildInvitation = false;
cur_end = 0;
current_endurance = 0;
InitializeBuffSlots();
@ -357,7 +363,7 @@ Client::~Client() {
m_tradeskill_object = nullptr;
}
close_npcs.clear();
close_mobs.clear();
if(IsDueling() && GetDuelTarget() != 0) {
Entity* entity = entity_list.GetID(GetDuelTarget());
@ -598,8 +604,8 @@ bool Client::Save(uint8 iCommitNow) {
m_pp.cur_hp = GetHP();
}
m_pp.mana = cur_mana;
m_pp.endurance = cur_end;
m_pp.mana = current_mana;
m_pp.endurance = current_endurance;
/* Save Character Currency */
database.SaveCharacterCurrency(CharacterID(), &m_pp);
@ -697,12 +703,13 @@ bool Client::AddPacket(const EQApplicationPacket *pApp, bool bAckreq) {
//drop the packet because it will never get sent.
return(false);
}
auto c = new CLIENTPACKET;
auto c = std::unique_ptr<CLIENTPACKET>(new CLIENTPACKET);
c->ack_req = bAckreq;
c->app = pApp->Copy();
clientpackets.Append(c);
clientpackets.push_back(std::move(c));
return true;
}
@ -714,26 +721,23 @@ bool Client::AddPacket(EQApplicationPacket** pApp, bool bAckreq) {
//drop the packet because it will never get sent.
return(false);
}
auto c = new CLIENTPACKET;
auto c = std::unique_ptr<CLIENTPACKET>(new CLIENTPACKET);
c->ack_req = bAckreq;
c->app = *pApp;
*pApp = nullptr;
clientpackets.Append(c);
clientpackets.push_back(std::move(c));
return true;
}
bool Client::SendAllPackets() {
LinkedListIterator<CLIENTPACKET*> iterator(clientpackets);
CLIENTPACKET* cp = nullptr;
iterator.Reset();
while(iterator.MoreElements()) {
cp = iterator.GetData();
while (!clientpackets.empty()) {
cp = clientpackets.front().get();
if(eqs)
eqs->FastQueuePacket((EQApplicationPacket **)&cp->app, cp->ack_req);
iterator.RemoveCurrent();
clientpackets.pop_front();
Log(Logs::Moderate, Logs::Client_Server_Packet, "Transmitting a packet");
}
return true;
@ -1257,6 +1261,37 @@ void Client::Message(uint32 type, const char* message, ...) {
safe_delete_array(buffer);
}
void Client::FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...) {
if (!FilteredMessageCheck(sender, filter))
return;
va_list argptr;
auto buffer = new char[4096];
va_start(argptr, message);
vsnprintf(buffer, 4096, message, argptr);
va_end(argptr);
size_t len = strlen(buffer);
//client dosent like our packet all the time unless
//we make it really big, then it seems to not care that
//our header is malformed.
//len = 4096 - sizeof(SpecialMesg_Struct);
uint32 len_packet = sizeof(SpecialMesg_Struct) + len;
auto app = new EQApplicationPacket(OP_SpecialMesg, len_packet);
SpecialMesg_Struct* sm = (SpecialMesg_Struct*)app->pBuffer;
sm->header[0] = 0x00; // Header used for #emote style messages..
sm->header[1] = 0x00; // Play around with these to see other types
sm->header[2] = 0x00;
sm->msg_type = type;
memcpy(sm->message, buffer, len + 1);
FastQueuePacket(&app);
safe_delete_array(buffer);
}
void Client::QuestJournalledMessage(const char *npcname, const char* message) {
// npcnames longer than 60 characters crash the client when they log back in
@ -1790,67 +1825,86 @@ const int32& Client::SetMana(int32 amount) {
amount = 0;
if (amount > GetMaxMana())
amount = GetMaxMana();
if (amount != cur_mana)
if (amount != current_mana)
update = true;
cur_mana = amount;
current_mana = amount;
if (update)
Mob::SetMana(amount);
SendManaUpdatePacket();
return cur_mana;
CheckManaEndUpdate();
return current_mana;
}
void Client::SendManaUpdatePacket() {
void Client::CheckManaEndUpdate() {
if (!Connected())
return;
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
SendManaUpdate();
SendEnduranceUpdate();
}
if (last_reported_mana != current_mana || last_reported_endurance != current_endurance) {
if (last_reported_mana != cur_mana || last_reported_endur != cur_end) {
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
SendManaUpdate();
SendEnduranceUpdate();
}
auto outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct));
ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer;
manachange->new_mana = cur_mana;
manachange->stamina = cur_end;
manachange->spell_id = casting_spell_id;
manachange->keepcasting = 1;
ManaChange_Struct* mana_change = (ManaChange_Struct*)outapp->pBuffer;
mana_change->new_mana = current_mana;
mana_change->stamina = current_endurance;
mana_change->spell_id = casting_spell_id;
mana_change->keepcasting = 1;
outapp->priority = 6;
QueuePacket(outapp);
safe_delete(outapp);
Group *g = GetGroup();
/* Let others know when our mana percent has changed */
if (this->GetManaPercent() != last_reported_mana_percent) {
Group *group = this->GetGroup();
Raid *raid = this->GetRaid();
if(g)
{
outapp = new EQApplicationPacket(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
auto outapp2 =
new EQApplicationPacket(OP_MobEnduranceUpdate, sizeof(MobEnduranceUpdate_Struct));
if (raid) {
raid->SendManaPacketFrom(this);
}
else if (group) {
group->SendManaPacketFrom(this);
}
MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp->pBuffer;
MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp2->pBuffer;
auto mana_packet = new EQApplicationPacket(OP_ManaUpdate, sizeof(ManaUpdate_Struct));
ManaUpdate_Struct* mana_update = (ManaUpdate_Struct*)mana_packet->pBuffer;
mana_update->cur_mana = GetMana();
mana_update->max_mana = GetMaxMana();
mana_update->spawn_id = GetID();
QueuePacket(mana_packet);
entity_list.QueueClientsByXTarget(this, mana_packet, false);
safe_delete(mana_packet);
mmus->spawn_id = meus->spawn_id = GetID();
mmus->mana = GetManaPercent();
meus->endurance = GetEndurancePercent();
for(int i = 0; i < MAX_GROUP_MEMBERS; ++i)
if (g->members[i] && g->members[i]->IsClient() && (g->members[i] != this) && (g->members[i]->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD))
{
g->members[i]->CastToClient()->QueuePacket(outapp);
g->members[i]->CastToClient()->QueuePacket(outapp2);
}
safe_delete(outapp);
safe_delete(outapp2);
last_reported_mana_percent = this->GetManaPercent();
}
/* Let others know when our endurance percent has changed */
if (this->GetEndurancePercent() != last_reported_endurance_percent) {
Group *group = this->GetGroup();
Raid *raid = this->GetRaid();
last_reported_mana = cur_mana;
last_reported_endur = cur_end;
if (raid) {
raid->SendEndurancePacketFrom(this);
}
else if (group) {
group->SendEndurancePacketFrom(this);
}
auto endurance_packet = new EQApplicationPacket(OP_EnduranceUpdate, sizeof(EnduranceUpdate_Struct));
EnduranceUpdate_Struct* endurance_update = (EnduranceUpdate_Struct*)endurance_packet->pBuffer;
endurance_update->cur_end = GetEndurance();
endurance_update->max_end = GetMaxEndurance();
endurance_update->spawn_id = GetID();
QueuePacket(endurance_packet);
entity_list.QueueClientsByXTarget(this, endurance_packet, false);
safe_delete(endurance_packet);
last_reported_endurance_percent = this->GetEndurancePercent();
}
last_reported_mana = current_mana;
last_reported_endurance = current_endurance;
}
}
@ -1858,12 +1912,11 @@ void Client::SendManaUpdatePacket() {
void Client::SendManaUpdate()
{
auto mana_app = new EQApplicationPacket(OP_ManaUpdate, sizeof(ManaUpdate_Struct));
ManaUpdate_Struct* mus = (ManaUpdate_Struct*)mana_app->pBuffer;
mus->cur_mana = GetMana();
mus->max_mana = GetMaxMana();
mus->spawn_id = GetID();
ManaUpdate_Struct* mana_update = (ManaUpdate_Struct*)mana_app->pBuffer;
mana_update->cur_mana = GetMana();
mana_update->max_mana = GetMaxMana();
mana_update->spawn_id = GetID();
QueuePacket(mana_app);
entity_list.QueueClientsByXTarget(this, mana_app, false);
safe_delete(mana_app);
}
@ -1871,12 +1924,11 @@ void Client::SendManaUpdate()
void Client::SendEnduranceUpdate()
{
auto end_app = new EQApplicationPacket(OP_EnduranceUpdate, sizeof(EnduranceUpdate_Struct));
EnduranceUpdate_Struct* eus = (EnduranceUpdate_Struct*)end_app->pBuffer;
eus->cur_end = GetEndurance();
eus->max_end = GetMaxEndurance();
eus->spawn_id = GetID();
EnduranceUpdate_Struct* endurance_update = (EnduranceUpdate_Struct*)end_app->pBuffer;
endurance_update->cur_end = GetEndurance();
endurance_update->max_end = GetMaxEndurance();
endurance_update->spawn_id = GetID();
QueuePacket(end_app);
entity_list.QueueClientsByXTarget(this, end_app, false);
safe_delete(end_app);
}
@ -1892,6 +1944,7 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
ns->spawn.guildID = GuildID();
// ns->spawn.linkdead = IsLD() ? 1 : 0;
// ns->spawn.pvp = GetPVP() ? 1 : 0;
ns->spawn.show_name = true;
strcpy(ns->spawn.title, m_pp.title);
@ -1920,7 +1973,7 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
}
bool Client::GMHideMe(Client* client) {
if (gmhideme) {
if (gm_hide_me) {
if (client == 0)
return true;
else if (admin > client->Admin())
@ -2339,7 +2392,9 @@ bool Client::CheckIncreaseSkill(EQEmu::skills::SkillType skillid, Mob *against_w
return false;
int skillval = GetRawSkill(skillid);
int maxskill = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid));
char buffer[24] = { 0 };
snprintf(buffer, 23, "%d %d", skillid, skillval);
parse->EventPlayer(EVENT_USE_SKILL, this, buffer, 0);
if(against_who)
{
if(against_who->GetSpecialAbility(IMMUNE_AGGRO) || against_who->IsClient() ||
@ -3187,9 +3242,9 @@ void Client::SetHideMe(bool flag)
{
EQApplicationPacket app;
gmhideme = flag;
gm_hide_me = flag;
if(gmhideme)
if(gm_hide_me)
{
database.SetHideMe(AccountID(),true);
CreateDespawnPacket(&app, false);
@ -3737,8 +3792,8 @@ void Client::SetEndurance(int32 newEnd)
newEnd = GetMaxEndurance();
}
cur_end = newEnd;
SendManaUpdatePacket();
current_endurance = newEnd;
CheckManaEndUpdate();
}
void Client::SacrificeConfirm(Client *caster)
@ -3931,6 +3986,46 @@ void Client::SendPopupToClient(const char *Title, const char *Text, uint32 Popup
safe_delete(outapp);
}
void Client::SendFullPopup(const char *Title, const char *Text, uint32 PopupID, uint32 NegativeID, uint32 Buttons, uint32 Duration, const char *ButtonName0, const char *ButtonName1, uint32 SoundControls) {
auto outapp = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct));
OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *)outapp->pBuffer;
if((strlen(Text) > (sizeof(olms->Text)-1)) || (strlen(Title) > (sizeof(olms->Title) - 1)) ) {
safe_delete(outapp);
return;
}
if (ButtonName0 && ButtonName1 && ( (strlen(ButtonName0) > (sizeof(olms->ButtonName0) - 1)) || (strlen(ButtonName1) > (sizeof(olms->ButtonName1) - 1)) ) ) {
safe_delete(outapp);
return;
}
strcpy(olms->Title, Title);
strcpy(olms->Text, Text);
olms->Buttons = Buttons;
if (ButtonName0 == NULL || ButtonName1 == NULL) {
sprintf(olms->ButtonName0, "%s", "Yes");
sprintf(olms->ButtonName1, "%s", "No");
} else {
strcpy(olms->ButtonName0, ButtonName0);
strcpy(olms->ButtonName1, ButtonName1);
}
if(Duration > 0)
olms->Duration = Duration * 1000;
else
olms->Duration = 0xffffffff;
olms->PopupID = PopupID;
olms->NegativeID = NegativeID;
olms->SoundControls = SoundControls;
QueuePacket(outapp);
safe_delete(outapp);
}
void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...) {
va_list argptr;
char buffer[4096];
@ -4171,7 +4266,7 @@ bool Client::GroupFollow(Client* inviter) {
RemoveAutoXTargets();
}
SetXTargetAutoMgr(GetXTargetAutoMgr());
SetXTargetAutoMgr(raid->GetXTargetAutoMgr());
if (!GetXTargetAutoMgr()->empty())
SetDirtyAutoHaters();
@ -4304,7 +4399,7 @@ bool Client::GroupFollow(Client* inviter) {
}
database.RefreshGroupFromDB(this);
group->SendHPPacketsTo(this);
group->SendHPManaEndPacketsTo(this);
//send updates to clients out of zone...
group->SendGroupJoinOOZ(this);
return true;
@ -5715,6 +5810,20 @@ void Client::SuspendMinion()
Message_StringID(clientMessageTell, SUSPEND_MINION_UNSUSPEND, CurrentPet->GetCleanName());
memset(&m_suspendedminion, 0, sizeof(struct PetInfo));
// TODO: These pet command states need to be synced ...
// Will just fix them for now
if (m_ClientVersionBit & EQEmu::versions::bit_UFAndLater) {
SetPetCommandState(PET_BUTTON_SIT, 0);
SetPetCommandState(PET_BUTTON_STOP, 0);
SetPetCommandState(PET_BUTTON_REGROUP, 0);
SetPetCommandState(PET_BUTTON_FOLLOW, 1);
SetPetCommandState(PET_BUTTON_GUARD, 0);
SetPetCommandState(PET_BUTTON_TAUNT, 1);
SetPetCommandState(PET_BUTTON_HOLD, 0);
SetPetCommandState(PET_BUTTON_GHOLD, 0);
SetPetCommandState(PET_BUTTON_FOCUS, 0);
SetPetCommandState(PET_BUTTON_SPELLHOLD, 0);
}
}
else
return;
@ -6266,7 +6375,7 @@ void Client::LocateCorpse()
SetHeading(CalculateHeadingToTarget(ClosestCorpse->GetX(), ClosestCorpse->GetY()));
SetTarget(ClosestCorpse);
SendTargetCommand(ClosestCorpse->GetID());
SendPosUpdate(2);
SendPositionUpdate(2);
}
else if(!GetTarget())
Message_StringID(clientMessageError, SENSE_CORPSE_NONE);
@ -8489,7 +8598,7 @@ void Client::Consume(const EQEmu::ItemData *item, uint8 type, int16 slot, bool a
if (type == EQEmu::item::ItemTypeFood)
{
int hchange = item->CastTime * cons_mod;
int hchange = item->CastTime_ * cons_mod;
hchange = mod_food_value(item, hchange);
if(hchange < 0) { return; }
@ -8506,7 +8615,7 @@ void Client::Consume(const EQEmu::ItemData *item, uint8 type, int16 slot, bool a
}
else
{
int tchange = item->CastTime * cons_mod;
int tchange = item->CastTime_ * cons_mod;
tchange = mod_drink_value(item, tchange);
if(tchange < 0) { return; }
@ -8660,8 +8769,8 @@ void Client::SendHPUpdateMarquee(){
return;
/* Health Update Marquee Display: Custom*/
uint32 health_percentage = (uint32)(this->cur_hp * 100 / this->max_hp);
if (health_percentage == 100)
uint8 health_percentage = (uint8)(this->cur_hp * 100 / this->max_hp);
if (health_percentage >= 100)
return;
std::string health_update_notification = StringFormat("Health: %u%%", health_percentage);
@ -8937,3 +9046,12 @@ void Client::ProcessAggroMeter()
}
}
void Client::SetPetCommandState(int button, int state)
{
auto app = new EQApplicationPacket(OP_PetCommandState, sizeof(PetCommandState_Struct));
auto pcs = (PetCommandState_Struct *)app->pBuffer;
pcs->button_id = button;
pcs->state = state;
FastQueuePacket(&app);
}

View File

@ -70,6 +70,7 @@ namespace EQEmu
#include <set>
#include <algorithm>
#include <memory>
#include <deque>
#define CLIENT_TIMEOUT 90000
@ -221,7 +222,7 @@ public:
Client(EQStreamInterface * ieqs);
~Client();
std::unordered_map<NPC *, float> close_npcs;
std::unordered_map<Mob *, float> close_mobs;
bool is_client_moving;
//abstract virtual function implementations required by base abstract class
@ -298,6 +299,7 @@ public:
const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); }
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
virtual bool Process();
void LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const EQEmu::ItemData* item, bool buying);
void SendPacketQueue(bool Block = true);
@ -307,6 +309,7 @@ public:
void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...);
void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...);
void Message(uint32 type, const char* message, ...);
void FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...);
void QuestJournalledMessage(const char *npcname, const char* message);
void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber);
void SendSound();
@ -318,7 +321,7 @@ public:
bool GetRevoked() const { return revoked; }
void SetRevoked(bool rev) { revoked = rev; }
inline uint32 GetIP() const { return ip; }
inline bool GetHideMe() const { return gmhideme; }
inline bool GetHideMe() const { return gm_hide_me; }
void SetHideMe(bool hm);
inline uint16 GetPort() const { return port; }
bool IsDead() const { return(dead); }
@ -351,6 +354,8 @@ public:
inline InspectMessage_Struct& GetInspectMessage() { return m_inspect_message; }
inline const InspectMessage_Struct& GetInspectMessage() const { return m_inspect_message; }
void SetPetCommandState(int button, int state);
bool CheckAccess(int16 iDBLevel, int16 iDefaultLevel);
void CheckQuests(const char* zonename, const char* message, uint32 npc_id, uint32 item_id, Mob* other);
@ -536,11 +541,11 @@ public:
void CalcMaxEndurance(); //This calculates the maximum endurance we can have
int32 CalcBaseEndurance(); //Calculates Base End
int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen()
int32 GetEndurance() const {return cur_end;} //This gets our current endurance
int32 GetEndurance() const {return current_endurance;} //This gets our current endurance
int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call
int32 CalcEnduranceRegenCap();
int32 CalcHPRegenCap();
inline uint8 GetEndurancePercent() { return (uint8)((float)cur_end / (float)max_end * 100.0f); }
inline uint8 GetEndurancePercent() { return (uint8)((float)current_endurance / (float)max_end * 100.0f); }
void SetEndurance(int32 newEnd); //This sets the current endurance to the new value
void DoEnduranceRegen(); //This Regenerates endurance
void DoEnduranceUpkeep(); //does the endurance upkeep
@ -567,6 +572,7 @@ public:
void AddCrystals(uint32 Radiant, uint32 Ebon);
void SendCrystalCounts();
uint32 GetExperienceForKill(Mob *against);
void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false);
uint32 CalcEXP(uint8 conlevel = 0xFF);
void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false);
@ -656,7 +662,7 @@ public:
void RefreshGuildInfo();
void SendManaUpdatePacket();
void CheckManaEndUpdate();
void SendManaUpdate();
void SendEnduranceUpdate();
uint8 GetFace() const { return m_pp.face; }
@ -792,12 +798,12 @@ public:
void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); }
int GetAAPoints() { return m_pp.aapoints; }
int GetSpentAA() { return m_pp.aapoints_spent; }
uint32 GetRequiredAAExperience();
//old AA methods that we still use
void ResetAA();
void RefundAA();
void SendClearAA();
inline uint32 GetMaxAAXP(void) const { return max_AAXP; }
inline uint32 GetAAXP() const { return m_pp.expAA; }
inline uint32 GetAAPercent() const { return m_epp.perAA; }
int16 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id);
@ -942,6 +948,7 @@ public:
inline bool HasSpellScribed(int spellid) { return (FindSpellBookSlotBySpellID(spellid) != -1 ? true : false); }
uint16 GetMaxSkillAfterSpecializationRules(EQEmu::skills::SkillType skillid, uint16 maxSkill);
void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0);
void SendFullPopup(const char *Title, const char *Text, uint32 PopupID = 0, uint32 NegativeID = 0, uint32 Buttons = 0, uint32 Duration = 0, const char *ButtonName0 = 0, const char *ButtonName1 = 0, uint32 SoundControls = 0);
void SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...);
bool PendingTranslocate;
time_t TranslocateTime;
@ -1066,7 +1073,7 @@ public:
void Signal(uint32 data);
Mob *GetBindSightTarget() { return bind_sight_target; }
void SetBindSightTarget(Mob *n) { bind_sight_target = n; }
const uint16 GetBoatID() const { return BoatID; }
const uint16 GetBoatID() const { return controlling_boat_id; }
void SendRewards();
bool TryReward(uint32 claim_id);
QGlobalCache *GetQGlobals() { return qGlobals; }
@ -1257,6 +1264,8 @@ public:
void CheckRegionTypeChanges();
int32 CalcATK();
protected:
friend class Mob;
void CalcItemBonuses(StatBonuses* newbon);
@ -1316,7 +1325,6 @@ private:
void HandleTraderPriceUpdate(const EQApplicationPacket *app);
int32 CalcATK();
int32 CalcItemATKCap();
int32 CalcHaste();
@ -1369,7 +1377,7 @@ private:
bool duelaccepted;
std::list<uint32> keyring;
bool tellsoff; // GM /toggle
bool gmhideme;
bool gm_hide_me;
bool LFG;
bool LFP;
uint8 LFGFromLevel;
@ -1389,7 +1397,7 @@ private:
uint32 weight;
bool berserk;
bool dead;
uint16 BoatID;
uint16 controlling_boat_id;
uint16 TrackingID;
uint16 CustomerID;
uint16 TraderID;
@ -1404,7 +1412,7 @@ private:
int Haste; //precalced value
int32 max_end;
int32 cur_end;
int32 current_endurance;
PlayerProfile_Struct m_pp;
ExtendedProfile_Struct m_epp;
@ -1424,7 +1432,7 @@ private:
bool AddPacket(const EQApplicationPacket *, bool);
bool AddPacket(EQApplicationPacket**, bool);
bool SendAllPackets();
LinkedList<CLIENTPACKET *> clientpackets;
std::deque<std::unique_ptr<CLIENTPACKET>> clientpackets;
//Zoning related stuff
void SendZoneCancel(ZoneChange_Struct *zc);
@ -1478,7 +1486,9 @@ private:
Timer helm_toggle_timer;
Timer aggro_meter_timer;
Timer npc_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 */
glm::vec3 m_Proximity;
void BulkSendInventoryItems();
@ -1487,7 +1497,6 @@ private:
uint32 tribute_master_id;
uint32 max_AAXP;
bool npcflag;
uint8 npclevel;
bool feigned;
@ -1495,7 +1504,10 @@ private:
bool tgb;
bool instalog;
int32 last_reported_mana;
int32 last_reported_endur;
int32 last_reported_endurance;
int8 last_reported_mana_percent;
int8 last_reported_endurance_percent;
unsigned int AggroCount; // How many mobs are aggro on us.

View File

@ -1046,14 +1046,14 @@ int32 Client::CalcMaxMana()
if (max_mana < 0) {
max_mana = 0;
}
if (cur_mana > max_mana) {
cur_mana = max_mana;
if (current_mana > max_mana) {
current_mana = max_mana;
}
int mana_perc_cap = spellbonuses.ManaPercCap[0];
if (mana_perc_cap) {
int curMana_cap = (max_mana * mana_perc_cap) / 100;
if (cur_mana > curMana_cap || (spellbonuses.ManaPercCap[1] && cur_mana > spellbonuses.ManaPercCap[1])) {
cur_mana = curMana_cap;
if (current_mana > curMana_cap || (spellbonuses.ManaPercCap[1] && current_mana > spellbonuses.ManaPercCap[1])) {
current_mana = curMana_cap;
}
}
Log(Logs::Detail, Logs::Spells, "Client::CalcMaxMana() called for %s - returning %d", GetName(), max_mana);
@ -2034,14 +2034,14 @@ void Client::CalcMaxEndurance()
if (max_end < 0) {
max_end = 0;
}
if (cur_end > max_end) {
cur_end = max_end;
if (current_endurance > max_end) {
current_endurance = max_end;
}
int end_perc_cap = spellbonuses.EndPercCap[0];
if (end_perc_cap) {
int curEnd_cap = (max_end * end_perc_cap) / 100;
if (cur_end > curEnd_cap || (spellbonuses.EndPercCap[1] && cur_end > spellbonuses.EndPercCap[1])) {
cur_end = curEnd_cap;
if (current_endurance > curEnd_cap || (spellbonuses.EndPercCap[1] && current_endurance > spellbonuses.EndPercCap[1])) {
current_endurance = curEnd_cap;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -236,6 +236,7 @@
void Handle_OP_RecipesSearch(const EQApplicationPacket *app);
void Handle_OP_ReloadUI(const EQApplicationPacket *app);
void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app);
void Handle_OP_RemoveTrap(const EQApplicationPacket *app);
void Handle_OP_Report(const EQApplicationPacket *app);
void Handle_OP_RequestDuel(const EQApplicationPacket *app);
void Handle_OP_RequestTitles(const EQApplicationPacket *app);
@ -288,6 +289,7 @@
void Handle_OP_TributeNPC(const EQApplicationPacket *app);
void Handle_OP_TributeToggle(const EQApplicationPacket *app);
void Handle_OP_TributeUpdate(const EQApplicationPacket *app);
void Handle_OP_UpdateAura(const EQApplicationPacket *app);
void Handle_OP_VetClaimRequest(const EQApplicationPacket *app);
void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app);
void Handle_OP_WearChange(const EQApplicationPacket *app);

View File

@ -63,64 +63,49 @@ extern EntityList entity_list;
bool Client::Process() {
bool ret = true;
if (Connected() || IsLD())
{
if (Connected() || IsLD()) {
// try to send all packets that weren't sent before
if (!IsLD() && zoneinpacket_timer.Check())
{
if (!IsLD() && zoneinpacket_timer.Check()) {
SendAllPackets();
}
if (adventure_request_timer)
{
if (adventure_request_timer->Check())
{
if (adventure_request_timer) {
if (adventure_request_timer->Check()) {
safe_delete(adventure_request_timer);
}
}
if (adventure_create_timer)
{
if (adventure_create_timer->Check())
{
if (adventure_create_timer) {
if (adventure_create_timer->Check()) {
safe_delete(adventure_create_timer);
}
}
if (adventure_leave_timer)
{
if (adventure_leave_timer->Check())
{
if (adventure_leave_timer) {
if (adventure_leave_timer->Check()) {
safe_delete(adventure_leave_timer);
}
}
if (adventure_door_timer)
{
if (adventure_door_timer->Check())
{
if (adventure_door_timer) {
if (adventure_door_timer->Check()) {
safe_delete(adventure_door_timer);
}
}
if (adventure_stats_timer)
{
if (adventure_stats_timer->Check())
{
if (adventure_stats_timer) {
if (adventure_stats_timer->Check()) {
safe_delete(adventure_stats_timer);
}
}
if (adventure_leaderboard_timer)
{
if (adventure_leaderboard_timer->Check())
{
if (adventure_leaderboard_timer) {
if (adventure_leaderboard_timer->Check()) {
safe_delete(adventure_leaderboard_timer);
}
}
if (dead)
{
if (dead) {
SetHP(-100);
if (RespawnFromHoverTimer.Check())
HandleRespawnFromHover(0);
@ -134,8 +119,13 @@ bool Client::Process() {
if (hpupdate_timer.Check(false))
SendHPUpdate();
/* I haven't naturally updated my position in 10 seconds, updating manually */
if (!is_client_moving && position_update_timer.Check()) {
SendPositionUpdate();
}
if (mana_timer.Check())
SendManaUpdatePacket();
CheckManaEndUpdate();
if (dead && dead_timer.Check()) {
database.MoveCharacterToZone(GetName(), database.GetZoneName(m_pp.binds[0].zoneId));
@ -254,19 +244,22 @@ bool Client::Process() {
/* Build a close range list of NPC's */
if (npc_close_scan_timer.Check()) {
close_npcs.clear();
close_mobs.clear();
auto &npc_list = entity_list.GetNPCList();
auto &mob_list = entity_list.GetMobList();
float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan));
float client_update_range = (RuleI(Range, MobPositionUpdates) * RuleI(Range, MobPositionUpdates));
float scan_range = RuleI(Range, ClientNPCScan);
for (auto itr = npc_list.begin(); itr != npc_list.end(); ++itr) {
NPC* npc = itr->second;
float distance = DistanceNoZ(m_Position, npc->GetPosition());
if(distance <= scan_range) {
close_npcs.insert(std::pair<NPC *, float>(npc, distance));
}
else if (npc->GetAggroRange() > scan_range) {
close_npcs.insert(std::pair<NPC *, float>(npc, distance));
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() > scan_range) {
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
}
}
}
}
@ -448,14 +441,14 @@ bool Client::Process() {
{
animation = 0;
m_Delta = glm::vec4(0.0f, 0.0f, 0.0f, m_Delta.w);
SendPosUpdate(2);
SendPositionUpdate(2);
}
}
// Send a position packet every 8 seconds - if not done, other clients
// see this char disappear after 10-12 seconds of inactivity
if (position_timer_counter >= 36) { // Approx. 4 ticks per second
entity_list.SendPositionUpdates(this, pLastUpdateWZ, 500, GetTarget(), true);
entity_list.SendPositionUpdates(this, pLastUpdateWZ, RuleI(Range, MobPositionUpdates), GetTarget(), true);
pLastUpdate = Timer::GetCurrentTime();
pLastUpdateWZ = pLastUpdate;
position_timer_counter = 0;
@ -619,11 +612,17 @@ bool Client::Process() {
// only if client is not feigned
if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) {
int npc_scan_count = 0;
for (auto it = close_npcs.begin(); it != close_npcs.end(); ++it) {
NPC *npc = it->first;
for (auto it = close_mobs.begin(); it != close_mobs.end(); ++it) {
Mob *mob = it->first;
if (npc->CheckWillAggro(this) && !npc->CheckAggro(this)) {
npc->AddToHateList(this, 25);
if (!mob)
continue;
if (mob->IsClient())
continue;
if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) {
mob->AddToHateList(this, 25);
}
npc_scan_count++;
}
@ -724,6 +723,8 @@ void Client::OnDisconnect(bool hard_disconnect) {
}
}
RemoveAllAuras();
Mob *Other = trade->With();
if(Other)
{
@ -985,8 +986,6 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName(), handyitem->Name);
else
Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName());
merch->CastToNPC()->FaceTarget(this->CastToMob());
}
// safe_delete_array(cpi);
@ -1816,7 +1815,7 @@ void Client::DoManaRegen() {
return;
SetMana(GetMana() + CalcManaRegen() + RestRegenMana);
SendManaUpdatePacket();
CheckManaEndUpdate();
}

View File

@ -173,6 +173,7 @@ int command_init(void)
command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) ||
command_add("clearinvsnapshots", "[use rule] - Clear inventory snapshot history (true - elapsed entries, false - all entries)", 200, command_clearinvsnapshots) ||
command_add("corpse", "- Manipulate corpses, use with no arguments for help", 50, command_corpse) ||
command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", 0, command_corpsefix) ||
command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) ||
command_add("cvs", "- Summary of client versions currently online.", 200, command_cvs) ||
command_add("damage", "[amount] - Damage your target", 100, command_damage) ||
@ -2977,6 +2978,11 @@ void command_reloadqst(Client *c, const Seperator *sep)
}
void command_corpsefix(Client *c, const Seperator *sep)
{
entity_list.CorpseFix(c);
}
void command_reloadworld(Client *c, const Seperator *sep)
{
c->Message(0, "Reloading quest cache and repopping zones worldwide.");
@ -7191,7 +7197,7 @@ void command_ginfo(Client *c, const Seperator *sep)
void command_hp(Client *c, const Seperator *sep)
{
c->SendHPUpdate();
c->SendManaUpdatePacket();
c->CheckManaEndUpdate();
}
void command_aggro(Client *c, const Seperator *sep)
@ -8734,9 +8740,9 @@ void command_object(Client *c, const Seperator *sep)
// Verify no other objects already in this spot (accidental double-click of Hotkey?)
query = StringFormat(
"SELECT COUNT(*) FROM object WHERE zoneid = %u "
"AND version=%u AND (posx BETWEEN %.1f AND %.1f) "
"AND (posy BETWEEN %.1f AND %.1f) "
"AND (posz BETWEEN %.1f AND %.1f)",
"AND version=%u AND (xpos BETWEEN %.1f AND %.1f) "
"AND (ypos BETWEEN %.1f AND %.1f) "
"AND (zpos BETWEEN %.1f AND %.1f)",
zone->GetZoneID(), zone->GetInstanceVersion(), od.x - 0.2f,
od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius.
od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way.

View File

@ -72,6 +72,7 @@ void command_checklos(Client *c, const Seperator *sep);
void command_clearinvsnapshots(Client *c, const Seperator *sep);
void command_connectworldserver(Client *c, const Seperator *sep);
void command_corpse(Client *c, const Seperator *sep);
void command_corpsefix(Client *c, const Seperator *sep);
void command_crashtest(Client *c, const Seperator *sep);
void command_cvs(Client *c, const Seperator *sep);
void command_d1(Client *c, const Seperator *sep);

View File

@ -56,6 +56,57 @@
//Maximum distance from a zone point if zone was specified
#define ZONEPOINT_ZONE_RANGE 40000.0f
// Defines based on the RoF2 Client
#define PET_HEALTHREPORT 0 // 0x00 - /pet health or Pet Window
#define PET_LEADER 1 // 0x01 - /pet leader or Pet Window
#define PET_ATTACK 2 // 0x02 - /pet attack or Pet Window
#define PET_QATTACK 3 // 0x03 - /pet qattack or Pet Window
#define PET_FOLLOWME 4 // 0x04 - /pet follow or Pet Window
#define PET_GUARDHERE 5 // 0x05 - /pet guard or Pet Window
#define PET_SIT 6 // 0x06 - /pet sit or Pet Window
#define PET_SITDOWN 7 // 0x07 - /pet sit on
#define PET_STANDUP 8 // 0x08 - /pet sit off
#define PET_STOP 9 // 0x09 - /pet stop or Pet Window - Not implemented
#define PET_STOP_ON 10 // 0x0a - /pet stop on - Not implemented
#define PET_STOP_OFF 11 // 0x0b - /pet stop off - Not implemented
#define PET_TAUNT 12 // 0x0c - /pet taunt or Pet Window
#define PET_TAUNT_ON 13 // 0x0d - /pet taunt on
#define PET_TAUNT_OFF 14 // 0x0e - /pet taunt off
#define PET_HOLD 15 // 0x0f - /pet hold or Pet Window, won't add to hate list unless attacking
#define PET_HOLD_ON 16 // 0x10 - /pet hold on
#define PET_HOLD_OFF 17 // 0x11 - /pet hold off
#define PET_GHOLD 18 // 0x12 - /pet ghold, will never add to hate list unless told to
#define PET_GHOLD_ON 19 // 0x13 - /pet ghold on
#define PET_GHOLD_OFF 20 // 0x14 - /pet ghold off
#define PET_SPELLHOLD 21 // 0x15 - /pet no cast or /pet spellhold or Pet Window
#define PET_SPELLHOLD_ON 22 // 0x16 - /pet spellhold on
#define PET_SPELLHOLD_OFF 23 // 0x17 - /pet spellhold off
#define PET_FOCUS 24 // 0x18 - /pet focus or Pet Window
#define PET_FOCUS_ON 25 // 0x19 - /pet focus on
#define PET_FOCUS_OFF 26 // 0x1a - /pet focus off
#define PET_FEIGN 27 // 0x1b - /pet feign
#define PET_BACKOFF 28 // 0x1c - /pet back off
#define PET_GETLOST 29 // 0x1d - /pet get lost
#define PET_GUARDME 30 // 0x1e - Same as /pet follow, but different message in older clients - define not from client /pet target in modern clients but doesn't send packet
#define PET_REGROUP 31 // 0x1f - /pet regroup, acts like classic hold. Stops attack and moves back to guard/you but doesn't clear hate list
#define PET_REGROUP_ON 32 // 0x20 - /pet regroup on, turns on regroup
#define PET_REGROUP_OFF 33 // 0x21 - /pet regroup off, turns off regroup
#define PET_MAXCOMMANDS PET_REGROUP_OFF + 1
// can change the state of these buttons with a packet
#define PET_BUTTON_SIT 0
#define PET_BUTTON_STOP 1
#define PET_BUTTON_REGROUP 2
#define PET_BUTTON_FOLLOW 3
#define PET_BUTTON_GUARD 4
#define PET_BUTTON_TAUNT 5
#define PET_BUTTON_HOLD 6
#define PET_BUTTON_GHOLD 7
#define PET_BUTTON_FOCUS 8
#define PET_BUTTON_SPELLHOLD 9
#define AURA_HARDCAP 2
typedef enum { //focus types
focusSpellHaste = 1,
focusSpellDuration,
@ -156,6 +207,16 @@ typedef enum { //fear states
enum { FlyMode0 = 0, FlyMode1 = 1, Flymode2 = 2, FlyMode3 = 3 };
// This is actually FlyMode, from MQ2
enum GravityBehavior {
Ground,
Flying,
Levitating,
Water,
Floating, // boat
LevitateWhileRunning
};
struct TradeEntity;
class Trade;
enum TradeState {
@ -359,7 +420,7 @@ struct StatBonuses {
uint32 SpellTriggers[MAX_SPELL_TRIGGER]; // Innate/Spell/Item Spells that trigger when you cast
uint32 SpellOnKill[MAX_SPELL_TRIGGER*3]; // Chance to proc after killing a mob
uint32 SpellOnDeath[MAX_SPELL_TRIGGER*2]; // Chance to have effect cast when you die
int32 CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 2]; // All Skills + -1
int32 CritDmgMod[EQEmu::skills::HIGHEST_SKILL + 2]; // All Skills + -1
int32 SkillReuseTime[EQEmu::skills::HIGHEST_SKILL + 1]; // Reduces skill timers
int32 SkillDamageAmount[EQEmu::skills::HIGHEST_SKILL + 2]; // All Skills + -1
int32 TwoHandBluntBlock; // chance to block when wielding two hand blunt weapon
@ -485,6 +546,10 @@ struct StatBonuses {
uint8 TradeSkillMastery; // Allow number of tradeskills to exceed 200 skill.
int16 NoBreakAESneak; // Percent value
int16 FeignedCastOnChance; // Percent Value
bool PetCommands[PET_MAXCOMMANDS]; // SPA 267
int FeignedMinionChance; // SPA 281 base1 = chance, just like normal FD
int aura_slots;
int trap_slots;
};
typedef struct

View File

@ -356,67 +356,15 @@ int32 Client::GetActSpellCost(uint16 spell_id, int32 cost)
cost -= mana_back;
}
// This formula was derived from the following resource:
// http://www.eqsummoners.com/eq1/specialization-library.html
// WildcardX
float PercentManaReduction = 0;
float SpecializeSkill = GetSpecializeSkillValue(spell_id);
int SuccessChance = zone->random.Int(0, 100);
float bonus = 1.0;
switch(GetAA(aaSpellCastingMastery))
{
case 1:
bonus += 0.05;
break;
case 2:
bonus += 0.15;
break;
case 3:
bonus += 0.30;
break;
}
bonus += 0.05f * GetAA(aaAdvancedSpellCastingMastery);
if(SuccessChance <= (SpecializeSkill * 0.3 * bonus))
{
PercentManaReduction = 1 + 0.05f * SpecializeSkill;
switch(GetAA(aaSpellCastingMastery))
{
case 1:
PercentManaReduction += 2.5;
break;
case 2:
PercentManaReduction += 5.0;
break;
case 3:
PercentManaReduction += 10.0;
break;
}
switch(GetAA(aaAdvancedSpellCastingMastery))
{
case 1:
PercentManaReduction += 2.5;
break;
case 2:
PercentManaReduction += 5.0;
break;
case 3:
PercentManaReduction += 10.0;
break;
}
}
int spec = GetSpecializeSkillValue(spell_id);
int PercentManaReduction = 0;
if (spec)
PercentManaReduction = 1 + spec / 20; // there seems to be some non-obvious rounding here, let's truncate for now.
int16 focus_redux = GetFocusEffect(focusManaCost, spell_id);
PercentManaReduction += focus_redux;
if(focus_redux > 0)
{
PercentManaReduction += zone->random.Real(1, (double)focus_redux);
}
cost -= (cost * (PercentManaReduction / 100));
cost -= cost * PercentManaReduction / 100;
// Gift of Mana - reduces spell cost to 1 mana
if(focus_redux >= 100) {
@ -746,7 +694,7 @@ void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate)
// 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)
void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int *max_targets)
{
Mob *curmob = nullptr;
@ -755,12 +703,25 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
float dist_targ = 0;
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 };
bool bad = IsDetrimentalSpell(spell_id);
bool isnpc = caster->IsNPC();
int MAX_TARGETS_ALLOWED = 4;
if (spells[spell_id].aemaxtargets)
MAX_TARGETS_ALLOWED = spells[spell_id].aemaxtargets;
if (RuleB(Spells, OldRainTargets))
max_targets = nullptr; // ignore it!
// if we have a passed in value, use it, otherwise default to data
// detrimental Target AEs have a default value of 4 for PCs and unlimited for NPCs
int max_targets_allowed = 0; // unlimited
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)
max_targets_allowed = spells[spell_id].aemaxtargets;
else if (IsTargetableAESpell(spell_id) && bad && !isnpc)
max_targets_allowed = 4;
int iCounter = 0;
@ -769,8 +730,6 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
// 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 == center) //do not affect center
continue;
if (curmob == caster && !affect_caster) //watch for caster too
continue;
if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient())
@ -784,13 +743,10 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
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;
if (spells[spell_id].targettype == ST_Ring) {
dist_targ = DistanceSquared(static_cast<glm::vec3>(curmob->GetPosition()), caster->GetTargetRingLocation());
}
else if (center) {
dist_targ = DistanceSquared(curmob->GetPosition(), center->GetPosition());
}
dist_targ = DistanceSquared(curmob->GetPosition(), position);
if (dist_targ > dist2) //make sure they are in range
continue;
@ -813,7 +769,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
if (bad) {
if (!caster->IsAttackAllowed(curmob, true))
continue;
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob))
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob))
continue;
if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize()))
continue;
@ -828,22 +784,17 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
}
curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ);
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
//if we get here... cast the spell.
if (IsTargetableAESpell(spell_id) && bad) {
if (iCounter < MAX_TARGETS_ALLOWED) {
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
}
} else {
if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets)
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
if (!spells[spell_id].aemaxtargets)
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
}
if (!isnpc || spells[spell_id].aemaxtargets) //npcs are not target limited (unless casting a spell with a target limit)...
if (max_targets_allowed) { // if we have a limit, increment count
iCounter++;
if (iCounter >= max_targets_allowed) // we done
break;
}
}
if (max_targets && max_targets_allowed)
*max_targets = *max_targets - iCounter;
}
void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster)

View File

@ -117,6 +117,7 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_TICK",
"EVENT_SPAWN_ZONE",
"EVENT_DEATH_ZONE",
"EVENT_USE_SKILL",
};
PerlembParser::PerlembParser() : perl(nullptr) {
@ -1441,6 +1442,12 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
ExportVar(package_name.c_str(), "killed_npc_id", sep.arg[4]);
break;
}
case EVENT_USE_SKILL:{
Seperator sep(data);
ExportVar(package_name.c_str(), "skill_id", sep.arg[0]);
ExportVar(package_name.c_str(), "skill_level", sep.arg[1]);
break;
}
default: {
break;

View File

@ -3599,6 +3599,24 @@ XS(XS__crosszonesetentityvariablebynpctypeid)
XSRETURN_EMPTY;
}
XS(XS__crosszonesetentityvariablebyclientname);
XS(XS__crosszonesetentityvariablebyclientname)
{
dXSARGS;
if (items != 3)
Perl_croak(aTHX_ "Usage: crosszonesetentityvariablebyclientname(clientname, id, m_var)");
if (items == 3) {
const char *clientname = (const char *)SvPV_nolen(ST(0));
const char *id = (const char *)SvPV_nolen(ST(1));
const char *m_var = (const char *)SvPV_nolen(ST(2));
quest_manager.CrossZoneSetEntityVariableByClientName(clientname, id, m_var);
}
XSRETURN_EMPTY;
}
XS(XS__crosszonesignalnpcbynpctypeid);
XS(XS__crosszonesignalnpcbynpctypeid)
{
@ -3766,6 +3784,7 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "createguild"), XS__createguild, file);
newXS(strcpy(buf, "crosszonemessageplayerbyname"), XS__crosszonemessageplayerbyname, file);
newXS(strcpy(buf, "crosszonesetentityvariablebynpctypeid"), XS__crosszonesetentityvariablebynpctypeid, file);
newXS(strcpy(buf, "crosszonesetentityvariablebyclientname"), XS__crosszonesetentityvariablebyclientname, file);
newXS(strcpy(buf, "crosszonesignalclientbycharid"), XS__crosszonesignalclientbycharid, file);
newXS(strcpy(buf, "crosszonesignalclientbyname"), XS__crosszonesignalclientbyname, file);
newXS(strcpy(buf, "crosszonesignalnpcbynpctypeid"), XS__crosszonesignalnpcbynpctypeid, file);

View File

@ -659,6 +659,8 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
QueueClients(npc, app);
npc->SendArmorAppearance();
npc->SetAppearance(npc->GetGuardPointAnim(),false);
if (!npc->IsTargetable())
npc->SendTargetable(false);
safe_delete(app);
} else {
auto ns = new NewSpawn_Struct;
@ -799,6 +801,8 @@ void EntityList::CheckSpawnQueue()
NPC *pnpc = it->second;
pnpc->SendArmorAppearance();
pnpc->SetAppearance(pnpc->GetGuardPointAnim(), false);
if (!pnpc->IsTargetable())
pnpc->SendTargetable(false);
}
safe_delete(outapp);
iterator.RemoveCurrent();
@ -1242,12 +1246,9 @@ void EntityList::SendZoneSpawns(Client *client)
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *ent = it->second;
if (!(ent->InZone()) || (ent->IsClient())) {
if (ent->CastToClient()->GMHideMe(client) ||
ent->CastToClient()->IsHoveringForRespawn()) {
++it;
continue;
}
if (!ent->InZone() || !ent->ShouldISpawnFor(client)) {
++it;
continue;
}
app = new EQApplicationPacket;
@ -1275,17 +1276,16 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
spawn = it->second;
if (spawn && spawn->GetID() > 0 && spawn->Spawned()) {
if (spawn->IsClient() && (spawn->CastToClient()->GMHideMe(client) ||
spawn->CastToClient()->IsHoveringForRespawn()))
if (!spawn->ShouldISpawnFor(client))
continue;
#if 1
const glm::vec4& spos = spawn->GetPosition();
delaypkt = false;
if (DistanceSquared(cpos, spos) > dmax || (spawn->IsClient() && (spawn->GetRace() == MINOR_ILL_OBJ || spawn->GetRace() == TREE)))
delaypkt = true;
if (delaypkt) {
app = new EQApplicationPacket;
spawn->CreateSpawnPacket(app);
@ -2135,6 +2135,25 @@ void EntityList::MessageClose(Mob* sender, bool skipsender, float dist, uint32 t
}
}
void EntityList::FilteredMessageClose(Mob *sender, bool skipsender, float dist, uint32 type, eqFilterType filter, const char *message, ...)
{
va_list argptr;
char buffer[4096];
va_start(argptr, message);
vsnprintf(buffer, 4095, message, argptr);
va_end(argptr);
float dist2 = dist * dist;
auto it = client_list.begin();
while (it != client_list.end()) {
if (DistanceSquared(it->second->GetPosition(), sender->GetPosition()) <= dist2 && (!skipsender || it->second != sender))
it->second->FilteredMessage(sender, type, filter, buffer);
++it;
}
}
void EntityList::RemoveAllMobs()
{
auto it = mob_list.begin();
@ -2263,6 +2282,9 @@ bool EntityList::RemoveMob(uint16 delete_id)
auto it = mob_list.find(delete_id);
if (it != mob_list.end()) {
RemoveMobFromClientCloseLists(it->second);
if (npc_list.count(delete_id))
entity_list.RemoveNPC(delete_id);
else if (client_list.count(delete_id))
@ -2285,6 +2307,8 @@ bool EntityList::RemoveMob(Mob *delete_mob)
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))
free_ids.push(it->first);
@ -2304,10 +2328,9 @@ bool EntityList::RemoveNPC(uint16 delete_id)
// make sure its proximity is removed
RemoveProximity(delete_id);
// remove from client close lists
RemoveNPCFromClientCloseLists(npc);
RemoveMobFromClientCloseLists(npc->CastToMob());
// remove from the list
npc_list.erase(it);
// remove from limit list if needed
if (npc_limit_list.count(delete_id))
@ -2317,11 +2340,11 @@ bool EntityList::RemoveNPC(uint16 delete_id)
return false;
}
bool EntityList::RemoveNPCFromClientCloseLists(NPC *npc)
bool EntityList::RemoveMobFromClientCloseLists(Mob *mob)
{
auto it = client_list.begin();
while (it != client_list.end()) {
it->second->close_npcs.erase(npc);
it->second->close_mobs.erase(mob);
++it;
}
return false;
@ -2615,10 +2638,9 @@ void EntityList::RemoveDebuffs(Mob *caster)
// Currently, a new packet is sent per entity.
// @todo: Come back and use FLAG_COMBINED to pack
// all updates into one packet.
void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate,
float range, Entity *alwayssend, bool iSendEvenIfNotChanged)
void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, float update_range, Entity *always_send, bool iSendEvenIfNotChanged)
{
range = range * range;
update_range = (update_range * update_range);
EQApplicationPacket *outapp = 0;
PlayerPositionUpdateServer_Struct *ppu = 0;
@ -2626,27 +2648,37 @@ void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate,
auto it = mob_list.begin();
while (it != mob_list.end()) {
if (outapp == 0) {
outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer;
}
mob = it->second;
if (mob && !mob->IsCorpse() && (it->second != client)
if (
mob && !mob->IsCorpse()
&& (it->second != client)
&& (mob->IsClient() || iSendEvenIfNotChanged || (mob->LastChange() >= cLastUpdate))
&& (!it->second->IsClient() || !it->second->CastToClient()->GMHideMe(client))) {
&& (it->second->ShouldISpawnFor(client))
) {
if (
update_range == 0
|| (it->second == always_send)
|| mob->IsClient()
|| (DistanceSquared(mob->GetPosition(), client->GetPosition()) <= update_range)
) {
if (mob && mob->IsClient() && mob->GetID() > 0) {
client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED);
//bool Grouped = client->HasGroup() && mob->IsClient() && (client->GetGroup() == mob->CastToClient()->GetGroup());
if (outapp == 0) {
outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer;
}
//if (range == 0 || (iterator.GetData() == alwayssend) || Grouped || (mob->DistNoRootNoZ(*client) <= range)) {
if (range == 0 || (it->second == alwayssend) || mob->IsClient() || (DistanceSquared(mob->GetPosition(), client->GetPosition()) <= range)) {
mob->MakeSpawnUpdate(ppu);
}
if(mob && mob->IsClient() && mob->GetID()>0) {
client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED);
mob->MakeSpawnUpdate(ppu);
safe_delete(outapp);
outapp = 0;
}
}
}
safe_delete(outapp);
outapp = 0;
++it;
}
@ -2826,6 +2858,22 @@ int32 EntityList::DeleteNPCCorpses()
return x;
}
void EntityList::CorpseFix(Client* c)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
Corpse* corpse = it->second;
if (corpse->IsNPCCorpse()) {
if (DistanceNoZ(c->GetPosition(), corpse->GetPosition()) < 100) {
c->Message(15, "Attempting to fix %s", it->second->GetCleanName());
corpse->GMMove(corpse->GetX(), corpse->GetY(), c->GetZ() + 2, 0);
}
}
++it;
}
}
// returns the number of corpses deleted. A negative number indicates an error code.
int32 EntityList::DeletePlayerCorpses()
{

View File

@ -79,6 +79,8 @@ public:
virtual bool IsTrap() const { return false; }
virtual bool IsBeacon() const { return false; }
virtual bool IsEncounter() const { return false; }
virtual bool IsBot() const { return false; }
virtual bool IsAura() const { return false; }
virtual bool Process() { return false; }
virtual bool Save() { return true; }
@ -113,7 +115,6 @@ public:
bool CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk=1);
#ifdef BOTS
virtual bool IsBot() const { return false; }
Bot* CastToBot();
#endif
@ -279,7 +280,7 @@ public:
bool RemoveTrap(uint16 delete_id);
bool RemoveObject(uint16 delete_id);
bool RemoveProximity(uint16 delete_npc_id);
bool RemoveNPCFromClientCloseLists(NPC *npc);
bool RemoveMobFromClientCloseLists(Mob *mob);
void RemoveAllMobs();
void RemoveAllClients();
void RemoveAllNPCs();
@ -315,6 +316,7 @@ public:
void Message(uint32 to_guilddbid, uint32 type, const char* message, ...);
void MessageStatus(uint32 to_guilddbid, int to_minstatus, uint32 type, const char* message, ...);
void MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...);
void FilteredMessageClose(Mob* sender, bool skipsender, float dist, uint32 type, eqFilterType filter, const char* message, ...);
void Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
void FilteredMessage_StringID(Mob *sender, bool skipsender, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
void MessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
@ -356,7 +358,7 @@ public:
void AEAttack(Mob *attacker, float dist, int Hand = EQEmu::inventory::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);
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);
@ -369,7 +371,7 @@ public:
Mob* FindDefenseNPC(uint32 npcid);
void OpenDoorsNear(NPC* opener);
void UpdateWho(bool iSendFullUpdate = false);
void SendPositionUpdates(Client* client, uint32 cLastUpdate = 0, float range = 0, Entity* alwayssend = 0, bool iSendEvenIfNotChanged = false);
void SendPositionUpdates(Client* client, uint32 cLastUpdate = 0, float update_range = 0, Entity* always_send = 0, bool iSendEvenIfNotChanged = false);
char* MakeNameUnique(char* name);
static char* RemoveNumbers(char* name);
void SignalMobsByNPCID(uint32 npc_type, int signal_id);
@ -385,6 +387,7 @@ public:
void FindPathsToAllNPCs();
int32 DeleteNPCCorpses();
int32 DeletePlayerCorpses();
void CorpseFix(Client* c);
void WriteEntityIDs();
void HalveAggro(Mob* who);
void DoubleAggro(Mob* who);

View File

@ -85,6 +85,7 @@ typedef enum {
EVENT_TICK,
EVENT_SPAWN_ZONE,
EVENT_DEATH_ZONE,
EVENT_USE_SKILL,
_LargestEventID
} QuestEventID;

View File

@ -28,6 +28,7 @@
#include "queryserv.h"
#include "quest_parser_collection.h"
#include "lua_parser.h"
#include "string_ids.h"
#ifdef BOTS
@ -153,6 +154,26 @@ uint32 Client::CalcEXP(uint8 conlevel) {
return in_add_exp;
}
uint32 Client::GetExperienceForKill(Mob *against)
{
#ifdef LUA_EQEMU
uint32 lua_ret = 0;
bool ignoreDefault = false;
lua_ret = LuaParser::Instance()->GetExperienceForKill(this, against, ignoreDefault);
if (ignoreDefault) {
return lua_ret;
}
#endif
if (against && against->IsNPC()) {
uint32 level = (uint32)against->GetLevel();
return EXP_FORMULA;
}
return 0;
}
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
this->EVENT_ITEM_ScriptStopReturn();
@ -339,8 +360,8 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
Log(Logs::Detail, Logs::None, "Attempting to Set Exp for %s (XP: %u, AAXP: %u, Rez: %s)", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false");
//max_AAXP = GetEXPForLevel(52) - GetEXPForLevel(51); //GetEXPForLevel() doesn't depend on class/race, just level, so it shouldn't change between Clients
max_AAXP = RuleI(AA, ExpPerPoint); //this may be redundant since we're doing this in Client::FinishConnState2()
auto max_AAXP = GetRequiredAAExperience();
if (max_AAXP == 0 || GetEXPForLevel(GetLevel()) == 0xFFFFFFFF) {
Message(13, "Error in Client::SetEXP. EXP not set.");
return; // Must be invalid class/race
@ -377,19 +398,23 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
}
if (isrezzexp) {
if (RuleI(Character, ShowExpValues) > 0) Message(MT_Experience, "You regain %s experience from resurrection. %s", exp_amount_message.c_str(), exp_percent_message.c_str());
if (RuleI(Character, ShowExpValues) > 0)
Message(MT_Experience, "You regain %s experience from resurrection. %s", exp_amount_message.c_str(), exp_percent_message.c_str());
else Message_StringID(MT_Experience, REZ_REGAIN);
} else {
if (membercount > 1) {
if (RuleI(Character, ShowExpValues) > 0) Message(MT_Experience, "You have gained %s party experience! %s", exp_amount_message.c_str(), exp_percent_message.c_str());
if (RuleI(Character, ShowExpValues) > 0)
Message(MT_Experience, "You have gained %s party experience! %s", exp_amount_message.c_str(), exp_percent_message.c_str());
else Message_StringID(MT_Experience, GAIN_GROUPXP);
}
else if (IsRaidGrouped()) {
if (RuleI(Character, ShowExpValues) > 0) Message(MT_Experience, "You have gained %s raid experience! %s", exp_amount_message.c_str(), exp_percent_message.c_str());
if (RuleI(Character, ShowExpValues) > 0)
Message(MT_Experience, "You have gained %s raid experience! %s", exp_amount_message.c_str(), exp_percent_message.c_str());
else Message_StringID(MT_Experience, GAIN_RAIDEXP);
}
else {
if (RuleI(Character, ShowExpValues) > 0) Message(MT_Experience, "You have gained %s experience! %s", exp_amount_message.c_str(), exp_percent_message.c_str());
if (RuleI(Character, ShowExpValues) > 0)
Message(MT_Experience, "You have gained %s experience! %s", exp_amount_message.c_str(), exp_percent_message.c_str());
else Message_StringID(MT_Experience, GAIN_XP);
}
}
@ -460,14 +485,13 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
//add in how many points we had
m_pp.aapoints += last_unspentAA;
//set_aaxp = m_pp.expAA % max_AAXP;
//figure out how many points were actually gained
/*uint32 gained = m_pp.aapoints - last_unspentAA;*/ //unused
//Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA);
char val1[20]={0};
Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2.
Message_StringID(MT_Experience, GAIN_ABILITY_POINT, ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2.
/* QS: PlayerLogAARate */
if (RuleB(QueryServ, PlayerLogAARate)){
@ -571,8 +595,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
char val1[20]={0};
char val2[20]={0};
char val3[20]={0};
Message_StringID(MT_Experience, GM_GAINXP,ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3)); //[GM] You have gained %1 AXP and %2 EXP (%3).
//Message(15, "[GM] You now have %d / %d EXP and %d / %d AA exp.", set_exp, GetEXPForLevel(GetLevel()+1), set_aaxp, max_AAXP);
Message_StringID(MT_Experience, GM_GAINXP, ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3)); //[GM] You have gained %1 AXP and %2 EXP (%3).
}
}
@ -664,6 +687,15 @@ void Client::SetLevel(uint8 set_level, bool command)
// Add: You can set the values you want now, client will be always sync :) - Merkur
uint32 Client::GetEXPForLevel(uint16 check_level)
{
#ifdef LUA_EQEMU
uint32 lua_ret = 0;
bool ignoreDefault = false;
lua_ret = LuaParser::Instance()->GetEXPForLevel(this, check_level, ignoreDefault);
if (ignoreDefault) {
return lua_ret;
}
#endif
uint16 check_levelm1 = check_level-1;
float mod;
@ -933,3 +965,17 @@ uint32 Client::GetCharMaxLevelFromQGlobal() {
return false;
}
uint32 Client::GetRequiredAAExperience() {
#ifdef LUA_EQEMU
uint32 lua_ret = 0;
bool ignoreDefault = false;
lua_ret = LuaParser::Instance()->GetRequiredAAExperience(this, ignoreDefault);
if (ignoreDefault) {
return lua_ret;
}
#endif
return RuleI(AA, ExpPerPoint);
}

View File

@ -154,7 +154,8 @@ void Mob::CalculateNewFearpoint()
int loop = 0;
float ranx, rany, ranz;
currently_fleeing = false;
currently_fleeing = true;
while (loop < 100) //Max 100 tries
{
int ran = 250 - (loop*2);
@ -167,13 +168,13 @@ void Mob::CalculateNewFearpoint()
float fdist = ranz - GetZ();
if (fdist >= -12 && fdist <= 12 && CheckCoordLosNoZLeaps(GetX(),GetY(),GetZ(),ranx,rany,ranz))
{
currently_fleeing = true;
break;
}
}
if (currently_fleeing)
m_FearWalkTarget = glm::vec3(ranx, rany, ranz);
else //Break fear
BuffFadeByEffect(SE_Fear);
if (loop <= 100)
{
m_FearWalkTarget = glm::vec3(ranx, rany, ranz);
}
}

View File

@ -196,7 +196,7 @@ bool Client::CanFish() {
float step_size = RuleR(Watermap, FishingLineStepSize);
for(float i = 0.0f; i < len; i += step_size) {
for(float i = 0.0f; i < LineLength; i += step_size) {
glm::vec3 dest(rodPosition.x, rodPosition.y, m_Position.z - i);
bool in_lava = zone->watermap->InLava(dest);

View File

@ -338,6 +338,13 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte
database.SetGroupID(NewMemberName, GetID(), owner->CharacterID(), true);
}
}
Group* group = newmember->CastToClient()->GetGroup();
if (group) {
group->SendHPManaEndPacketsTo(newmember);
group->SendHPPacketsFrom(newmember);
}
}
else
{
@ -387,31 +394,30 @@ void Group::QueuePacket(const EQApplicationPacket *app, bool ack_req)
// Sends the rest of the group's hps to member. this is useful when someone
// first joins a group, but otherwise there shouldn't be a need to call it
void Group::SendHPPacketsTo(Mob *member)
void Group::SendHPManaEndPacketsTo(Mob *member)
{
if(member && member->IsClient())
{
if(member && member->IsClient()) {
EQApplicationPacket hpapp;
EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++)
{
if(members[i] && members[i] != member)
{
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) {
if(members[i] && members[i] != member) {
members[i]->CreateHPPacket(&hpapp);
member->CastToClient()->QueuePacket(&hpapp, false);
safe_delete_array(hpapp.pBuffer);
hpapp.size = 0;
if (member->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD)
{
if (member->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
outapp.SetOpcode(OP_MobManaUpdate);
MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer;
mmus->spawn_id = members[i]->GetID();
mmus->mana = members[i]->GetManaPercent();
MobManaUpdate_Struct *mana_update = (MobManaUpdate_Struct *)outapp.pBuffer;
mana_update->spawn_id = members[i]->GetID();
mana_update->mana = members[i]->GetManaPercent();
member->CastToClient()->QueuePacket(&outapp, false);
MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer;
MobEnduranceUpdate_Struct *endurance_update = (MobEnduranceUpdate_Struct *)outapp.pBuffer;
outapp.SetOpcode(OP_MobEnduranceUpdate);
meus->endurance = members[i]->GetEndurancePercent();
endurance_update->endurance = members[i]->GetEndurancePercent();
member->CastToClient()->QueuePacket(&outapp, false);
}
}
@ -430,19 +436,58 @@ void Group::SendHPPacketsFrom(Mob *member)
uint32 i;
for(i = 0; i < MAX_GROUP_MEMBERS; i++) {
if(members[i] && members[i] != member && members[i]->IsClient())
{
if(members[i] && members[i] != member && members[i]->IsClient()) {
members[i]->CastToClient()->QueuePacket(&hp_app);
if (members[i]->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD)
{
if (members[i]->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
outapp.SetOpcode(OP_MobManaUpdate);
MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer;
mmus->spawn_id = member->GetID();
mmus->mana = member->GetManaPercent();
MobManaUpdate_Struct *mana_update = (MobManaUpdate_Struct *)outapp.pBuffer;
mana_update->spawn_id = member->GetID();
mana_update->mana = member->GetManaPercent();
members[i]->CastToClient()->QueuePacket(&outapp, false);
MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer;
MobEnduranceUpdate_Struct *endurance_update = (MobEnduranceUpdate_Struct *)outapp.pBuffer;
outapp.SetOpcode(OP_MobEnduranceUpdate);
meus->endurance = member->GetEndurancePercent();
endurance_update->endurance = member->GetEndurancePercent();
members[i]->CastToClient()->QueuePacket(&outapp, false);
}
}
}
}
void Group::SendManaPacketFrom(Mob *member)
{
if (!member)
return;
EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
uint32 i;
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (members[i] && members[i] != member && members[i]->IsClient()) {
if (members[i]->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
outapp.SetOpcode(OP_MobManaUpdate);
MobManaUpdate_Struct *mana_update = (MobManaUpdate_Struct *)outapp.pBuffer;
mana_update->spawn_id = member->GetID();
mana_update->mana = member->GetManaPercent();
members[i]->CastToClient()->QueuePacket(&outapp, false);
}
}
}
}
void Group::SendEndurancePacketFrom(Mob* member)
{
if (!member)
return;
EQApplicationPacket outapp(OP_MobEnduranceUpdate, sizeof(MobManaUpdate_Struct));
uint32 i;
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (members[i] && members[i] != member && members[i]->IsClient()) {
if (members[i]->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
MobEnduranceUpdate_Struct *endurance_update = (MobEnduranceUpdate_Struct *)outapp.pBuffer;
endurance_update->spawn_id = member->GetID();
endurance_update->endurance = member->GetEndurancePercent();
members[i]->CastToClient()->QueuePacket(&outapp, false);
}
}
@ -648,7 +693,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
}
}
if (GetLeader() == nullptr)
if (!GetLeaderName())
{
DisbandGroup();
return true;

View File

@ -80,8 +80,10 @@ public:
inline void SetLeader(Mob* newleader){ leader=newleader; };
inline Mob* GetLeader() { return leader; };
const char* GetLeaderName() { return membername[0]; };
void SendHPPacketsTo(Mob* newmember);
void SendHPPacketsFrom(Mob* newmember);
void SendHPManaEndPacketsTo(Mob* newmember);
void SendHPPacketsFrom(Mob* member);
void SendManaPacketFrom(Mob* member);
void SendEndurancePacketFrom(Mob* member);
bool UpdatePlayer(Mob* update);
void MemberZoned(Mob* removemob);
inline bool IsLeader(Mob* leadertest) { return leadertest==leader; };

View File

@ -1172,6 +1172,11 @@ uint64 Lua_Client::GetAllMoney() {
return self->GetAllMoney();
}
uint32 Lua_Client::GetMoney(uint8 type, uint8 subtype) {
Lua_Safe_Call_Int();
return self->GetMoney(type, subtype);
}
void Lua_Client::OpenLFGuildWindow() {
Lua_Safe_Call_Void();
self->OpenLFGuildWindow();
@ -1414,9 +1419,25 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) {
self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, faction);
}
uint32 Lua_Client::GetMoney(uint8 type, uint8 subtype) {
bool Lua_Client::IsDead() {
Lua_Safe_Call_Bool();
return self->IsDead();
}
int Lua_Client::CalcCurrentWeight() {
Lua_Safe_Call_Int();
return self->GetMoney(type, subtype);
return self->CalcCurrentWeight();
}
int Lua_Client::CalcATK() {
Lua_Safe_Call_Int();
return self->CalcATK();
}
void Lua_Client::FilteredMessage(Mob *sender, uint32 type, int filter, const char *message)
{
Lua_Safe_Call_Void();
self->FilteredMessage(sender, type, (eqFilterType)filter, message);
}
luabind::scope lua_register_client() {
@ -1653,6 +1674,7 @@ luabind::scope lua_register_client() {
.def("GetAggroCount", (int(Lua_Client::*)(void))&Lua_Client::GetAggroCount)
.def("GetCarriedMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetCarriedMoney)
.def("GetAllMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetAllMoney)
.def("GetMoney", (uint32(Lua_Client::*)(uint8, uint8))&Lua_Client::GetMoney)
.def("OpenLFGuildWindow", (void(Lua_Client::*)(void))&Lua_Client::OpenLFGuildWindow)
.def("Signal", (void(Lua_Client::*)(uint32))&Lua_Client::Signal)
.def("AddAlternateCurrencyValue", (void(Lua_Client::*)(uint32,int))&Lua_Client::AddAlternateCurrencyValue)
@ -1687,7 +1709,10 @@ luabind::scope lua_register_client() {
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32, bool))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, luabind::adl::object))&Lua_Client::QuestReward)
.def("GetMoney", (uint32(Lua_Client::*)(uint8, uint8))&Lua_Client::GetMoney);
.def("IsDead", &Lua_Client::IsDead)
.def("CalcCurrentWeight", &Lua_Client::CalcCurrentWeight)
.def("CalcATK", &Lua_Client::CalcATK)
.def("FilteredMessage", &Lua_Client::FilteredMessage);
}
luabind::scope lua_register_inventory_where() {

View File

@ -297,6 +297,10 @@ public:
void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp);
void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction);
void QuestReward(Lua_Mob target, luabind::adl::object reward);
bool IsDead();
int CalcCurrentWeight();
int CalcATK();
void FilteredMessage(Mob *sender, uint32 type, int filter, const char* message);
};
#endif

View File

@ -67,6 +67,16 @@ bool Lua_Entity::IsBeacon() {
return self->IsBeacon();
}
bool Lua_Entity::IsEncounter() {
Lua_Safe_Call_Bool();
return self->IsEncounter();
}
bool Lua_Entity::IsBot() {
Lua_Safe_Call_Bool();
return self->IsBot();
}
int Lua_Entity::GetID() {
Lua_Safe_Call_Bool();
return self->GetID();
@ -124,6 +134,8 @@ luabind::scope lua_register_entity() {
.def("IsDoor", &Lua_Entity::IsDoor)
.def("IsTrap", &Lua_Entity::IsTrap)
.def("IsBeacon", &Lua_Entity::IsBeacon)
.def("IsEncounter", &Lua_Entity::IsEncounter)
.def("IsBot", &Lua_Entity::IsBot)
.def("GetID", &Lua_Entity::GetID)
.def("CastToClient", &Lua_Entity::CastToClient)
.def("CastToNPC", &Lua_Entity::CastToNPC)

View File

@ -44,6 +44,8 @@ public:
bool IsDoor();
bool IsTrap();
bool IsBeacon();
bool IsEncounter();
bool IsBot();
int GetID();
Lua_Client CastToClient();

View File

@ -210,6 +210,12 @@ void Lua_EntityList::MessageClose(Lua_Mob sender, bool skip_sender, float dist,
self->MessageClose(sender, skip_sender, dist, type, message);
}
void Lua_EntityList::FilteredMessageClose(Lua_Mob sender, bool skip_sender, float dist, uint32 type, int filter, const char *message)
{
Lua_Safe_Call_Void();
self->FilteredMessageClose(sender, skip_sender, dist, type, (eqFilterType)filter, message);
}
void Lua_EntityList::RemoveFromTargets(Lua_Mob mob) {
Lua_Safe_Call_Void();
self->RemoveFromTargets(mob);
@ -450,16 +456,17 @@ luabind::scope lua_register_entity_list() {
.def("GetSpawnByID", (Lua_Spawn(Lua_EntityList::*)(uint32))&Lua_EntityList::GetSpawnByID)
.def("ClearClientPetitionQueue", (void(Lua_EntityList::*)(void))&Lua_EntityList::ClearClientPetitionQueue)
.def("CanAddHateForMob", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::CanAddHateForMob)
.def("Message", (void(Lua_EntityList::*)(uint32,uint32,const char*))&Lua_EntityList::Message)
.def("MessageStatus", (void(Lua_EntityList::*)(uint32,uint32,uint32,const char*))&Lua_EntityList::MessageStatus)
.def("MessageClose", (void(Lua_EntityList::*)(Lua_Mob,bool,float,uint32,const char*))&Lua_EntityList::MessageClose)
.def("Message", (void(Lua_EntityList::*)(uint32, uint32, const char*))&Lua_EntityList::Message)
.def("MessageStatus", (void(Lua_EntityList::*)(uint32, uint32, uint32, const char*))&Lua_EntityList::MessageStatus)
.def("MessageClose", (void(Lua_EntityList::*)(Lua_Mob, bool, float, uint32, const char*))&Lua_EntityList::MessageClose)
.def("FilteredMessageClose", &Lua_EntityList::FilteredMessageClose)
.def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::RemoveFromTargets)
.def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob,bool))&Lua_EntityList::RemoveFromTargets)
.def("ReplaceWithTarget", (void(Lua_EntityList::*)(Lua_Mob,Lua_Mob))&Lua_EntityList::ReplaceWithTarget)
.def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromTargets)
.def("ReplaceWithTarget", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob))&Lua_EntityList::ReplaceWithTarget)
.def("OpenDoorsNear", (void(Lua_EntityList::*)(Lua_NPC))&Lua_EntityList::OpenDoorsNear)
.def("MakeNameUnique", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::MakeNameUnique)
.def("RemoveNumbers", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::RemoveNumbers)
.def("SignalMobsByNPCID", (void(Lua_EntityList::*)(uint32,int))&Lua_EntityList::SignalMobsByNPCID)
.def("SignalMobsByNPCID", (void(Lua_EntityList::*)(uint32, int))&Lua_EntityList::SignalMobsByNPCID)
.def("DeleteNPCCorpses", (int(Lua_EntityList::*)(void))&Lua_EntityList::DeleteNPCCorpses)
.def("DeletePlayerCorpses", (int(Lua_EntityList::*)(void))&Lua_EntityList::DeletePlayerCorpses)
.def("HalveAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::HalveAggro)
@ -467,10 +474,10 @@ luabind::scope lua_register_entity_list() {
.def("ClearFeignAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::ClearFeignAggro)
.def("Fighting", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::Fighting)
.def("RemoveFromHateLists", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::RemoveFromHateLists)
.def("RemoveFromHateLists", (void(Lua_EntityList::*)(Lua_Mob,bool))&Lua_EntityList::RemoveFromHateLists)
.def("MessageGroup", (void(Lua_EntityList::*)(Lua_Mob,bool,uint32,const char*))&Lua_EntityList::MessageGroup)
.def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float,float,float,float))&Lua_EntityList::GetRandomClient)
.def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float,float,float,float,Lua_Client))&Lua_EntityList::GetRandomClient)
.def("RemoveFromHateLists", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromHateLists)
.def("MessageGroup", (void(Lua_EntityList::*)(Lua_Mob, bool, uint32, const char*))&Lua_EntityList::MessageGroup)
.def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float, float, float, float))&Lua_EntityList::GetRandomClient)
.def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float, float, float, float, Lua_Client))&Lua_EntityList::GetRandomClient)
.def("GetMobList", (Lua_Mob_List(Lua_EntityList::*)(void))&Lua_EntityList::GetMobList)
.def("GetClientList", (Lua_Client_List(Lua_EntityList::*)(void))&Lua_EntityList::GetClientList)
.def("GetNPCList", (Lua_NPC_List(Lua_EntityList::*)(void))&Lua_EntityList::GetNPCList)
@ -479,7 +486,7 @@ luabind::scope lua_register_entity_list() {
.def("GetDoorsList", (Lua_Doors_List(Lua_EntityList::*)(void))&Lua_EntityList::GetDoorsList)
.def("GetSpawnList", (Lua_Spawn_List(Lua_EntityList::*)(void))&Lua_EntityList::GetSpawnList)
.def("SignalAllClients", (void(Lua_EntityList::*)(int))&Lua_EntityList::SignalAllClients)
.def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob,int,int,const char*))&Lua_EntityList::ChannelMessage);
.def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob, int, int, const char*))&Lua_EntityList::ChannelMessage);
}
luabind::scope lua_register_mob_list() {

View File

@ -80,6 +80,7 @@ public:
void Message(uint32 guild_dbid, uint32 type, const char *message);
void MessageStatus(uint32 guild_dbid, int min_status, uint32 type, const char *message);
void MessageClose(Lua_Mob sender, bool skip_sender, float dist, uint32 type, const char *message);
void FilteredMessageClose(Lua_Mob sender, bool skip_sender, float dist, uint32 type, int filter, const char *message);
void RemoveFromTargets(Lua_Mob mob);
void RemoveFromTargets(Lua_Mob mob, bool RemoveFromXTargets);
void ReplaceWithTarget(Lua_Mob target, Lua_Mob new_target);

View File

@ -7,6 +7,10 @@
#include <list>
#include <map>
#include "../common/timer.h"
#include "../common/eqemu_logsys.h"
#include "../common/classes.h"
#include "../common/rulesys.h"
#include "lua_parser.h"
#include "lua_item.h"
#include "lua_iteminst.h"
@ -16,8 +20,6 @@
#include "quest_parser_collection.h"
#include "questmgr.h"
#include "qglobals.h"
#include "../common/timer.h"
#include "../common/eqemu_logsys.h"
#include "encounter.h"
#include "lua_encounter.h"
@ -27,6 +29,12 @@ struct Slots { };
struct Materials { };
struct ClientVersions { };
struct Appearances { };
struct Classes { };
struct Skills { };
struct BodyTypes { };
struct Filters { };
struct MessageTypes { };
struct Rule { };
struct lua_registered_event {
std::string encounter_name;
@ -892,6 +900,10 @@ void lua_cross_zone_message_player_by_name(uint32 type, const char *player, cons
quest_manager.CrossZoneMessagePlayerByName(type, player, message);
}
void lua_cross_zone_set_entity_variable_by_client_name(const char *player, const char *id, const char *m_var) {
quest_manager.CrossZoneSetEntityVariableByClientName(player, id, m_var);
}
void lua_world_wide_marquee(uint32 type, uint32 priority, uint32 fadein, uint32 fadeout, uint32 duration, const char *message) {
quest_manager.WorldWideMarquee(type, priority, fadein, fadeout, duration, message);
}
@ -1458,6 +1470,39 @@ void lua_create_npc(luabind::adl::object table, float x, float y, float z, float
npc->GiveNPCTypeData(npc_type);
entity_list.AddNPC(npc);
}
int random_int(int low, int high) {
return zone->random.Int(low, high);
}
double random_real(double low, double high) {
return zone->random.Real(low, high);
}
bool random_roll_int(int required) {
return zone->random.Roll(required);
}
bool random_roll_real(double required) {
return zone->random.Roll(required);
}
int random_roll0(int max) {
return zone->random.Roll0(max);
}
int get_rulei(int rule) {
return RuleManager::Instance()->GetIntRule((RuleManager::IntType)rule);
}
float get_ruler(int rule) {
return RuleManager::Instance()->GetRealRule((RuleManager::RealType)rule);
}
bool get_ruleb(int rule) {
return RuleManager::Instance()->GetBoolRule((RuleManager::BoolType)rule);
}
luabind::scope lua_register_general() {
return luabind::namespace_("eq")
[
@ -1617,6 +1662,7 @@ luabind::scope lua_register_general() {
luabind::def("cross_zone_signal_client_by_char_id", &lua_cross_zone_signal_client_by_char_id),
luabind::def("cross_zone_signal_client_by_name", &lua_cross_zone_signal_client_by_name),
luabind::def("cross_zone_message_player_by_name", &lua_cross_zone_message_player_by_name),
luabind::def("cross_zone_set_entity_variable_by_client_name", &lua_cross_zone_set_entity_variable_by_client_name),
luabind::def("world_wide_marquee", &lua_world_wide_marquee),
luabind::def("get_qglobals", (luabind::adl::object(*)(lua_State*,Lua_NPC,Lua_Client))&lua_get_qglobals),
luabind::def("get_qglobals", (luabind::adl::object(*)(lua_State*,Lua_Client))&lua_get_qglobals),
@ -1658,6 +1704,18 @@ luabind::scope lua_register_general() {
];
}
luabind::scope lua_register_random() {
return luabind::namespace_("Random")
[
luabind::def("Int", &random_int),
luabind::def("Real", &random_real),
luabind::def("Roll", &random_roll_int),
luabind::def("RollReal", &random_roll_real),
luabind::def("Roll0", &random_roll0)
];
}
luabind::scope lua_register_events() {
return luabind::class_<Events>("Event")
.enum_("constants")
@ -1738,7 +1796,8 @@ luabind::scope lua_register_events() {
luabind::value("unhandled_opcode", static_cast<int>(EVENT_UNHANDLED_OPCODE)),
luabind::value("tick", static_cast<int>(EVENT_TICK)),
luabind::value("spawn_zone", static_cast<int>(EVENT_SPAWN_ZONE)),
luabind::value("death_zone", static_cast<int>(EVENT_DEATH_ZONE))
luabind::value("death_zone", static_cast<int>(EVENT_DEATH_ZONE)),
luabind::value("use_skill", static_cast<int>(EVENT_USE_SKILL))
];
}
@ -1857,4 +1916,355 @@ luabind::scope lua_register_appearance() {
];
}
luabind::scope lua_register_classes() {
return luabind::class_<Classes>("Class")
.enum_("constants")
[
luabind::value("WARRIOR", WARRIOR),
luabind::value("CLERIC", CLERIC),
luabind::value("PALADIN", PALADIN),
luabind::value("RANGER", RANGER),
luabind::value("SHADOWKNIGHT", SHADOWKNIGHT),
luabind::value("DRUID", DRUID),
luabind::value("MONK", MONK),
luabind::value("BARD", BARD),
luabind::value("ROGUE", ROGUE),
luabind::value("SHAMAN", SHAMAN),
luabind::value("NECROMANCER", NECROMANCER),
luabind::value("WIZARD", WIZARD),
luabind::value("MAGICIAN", MAGICIAN),
luabind::value("ENCHANTER", ENCHANTER),
luabind::value("BEASTLORD", BEASTLORD),
luabind::value("BERSERKER", BERSERKER),
luabind::value("WARRIORGM", WARRIORGM),
luabind::value("CLERICGM", CLERICGM),
luabind::value("PALADINGM", PALADINGM),
luabind::value("RANGERGM", RANGERGM),
luabind::value("SHADOWKNIGHTGM", SHADOWKNIGHTGM),
luabind::value("DRUIDGM", DRUIDGM),
luabind::value("MONKGM", MONKGM),
luabind::value("BARDGM", BARDGM),
luabind::value("ROGUEGM", ROGUEGM),
luabind::value("SHAMANGM", SHAMANGM),
luabind::value("NECROMANCERGM", NECROMANCERGM),
luabind::value("WIZARDGM", WIZARDGM),
luabind::value("MAGICIANGM", MAGICIANGM),
luabind::value("ENCHANTERGM", ENCHANTERGM),
luabind::value("BEASTLORDGM", BEASTLORDGM),
luabind::value("BERSERKERGM", BERSERKERGM),
luabind::value("BANKER", BANKER),
luabind::value("MERCHANT", MERCHANT),
luabind::value("DISCORD_MERCHANT", DISCORD_MERCHANT),
luabind::value("ADVENTURERECRUITER", ADVENTURERECRUITER),
luabind::value("ADVENTUREMERCHANT", ADVENTUREMERCHANT),
luabind::value("LDON_TREASURE", LDON_TREASURE),
luabind::value("CORPSE_CLASS", CORPSE_CLASS),
luabind::value("TRIBUTE_MASTER", TRIBUTE_MASTER),
luabind::value("GUILD_TRIBUTE_MASTER", GUILD_TRIBUTE_MASTER),
luabind::value("NORRATHS_KEEPERS_MERCHANT", NORRATHS_KEEPERS_MERCHANT),
luabind::value("DARK_REIGN_MERCHANT", DARK_REIGN_MERCHANT),
luabind::value("FELLOWSHIP_MASTER", FELLOWSHIP_MASTER),
luabind::value("ALT_CURRENCY_MERCHANT", ALT_CURRENCY_MERCHANT),
luabind::value("MERCERNARY_MASTER", MERCERNARY_MASTER)
];
}
luabind::scope lua_register_skills() {
return luabind::class_<Skills>("Skill")
.enum_("constants")
[
luabind::value("1HBlunt", EQEmu::skills::Skill1HBlunt),
luabind::value("1HSlashing", EQEmu::skills::Skill1HSlashing),
luabind::value("2HBlunt", EQEmu::skills::Skill2HBlunt),
luabind::value("2HSlashing", EQEmu::skills::Skill2HSlashing),
luabind::value("Abjuration", EQEmu::skills::SkillAbjuration),
luabind::value("Alteration", EQEmu::skills::SkillAlteration),
luabind::value("ApplyPoison", EQEmu::skills::SkillApplyPoison),
luabind::value("Archery", EQEmu::skills::SkillArchery),
luabind::value("Backstab", EQEmu::skills::SkillBackstab),
luabind::value("BindWound", EQEmu::skills::SkillBindWound),
luabind::value("Bash", EQEmu::skills::SkillBash),
luabind::value("Block", EQEmu::skills::SkillBlock),
luabind::value("BrassInstruments", EQEmu::skills::SkillBrassInstruments),
luabind::value("Channeling", EQEmu::skills::SkillChanneling),
luabind::value("Conjuration", EQEmu::skills::SkillConjuration),
luabind::value("Defense", EQEmu::skills::SkillDefense),
luabind::value("Disarm", EQEmu::skills::SkillDisarm),
luabind::value("DisarmTraps", EQEmu::skills::SkillDisarmTraps),
luabind::value("Divination", EQEmu::skills::SkillDivination),
luabind::value("Dodge", EQEmu::skills::SkillDodge),
luabind::value("DoubleAttack", EQEmu::skills::SkillDoubleAttack),
luabind::value("DragonPunch", EQEmu::skills::SkillDragonPunch),
luabind::value("TailRake", EQEmu::skills::SkillTailRake),
luabind::value("DualWield", EQEmu::skills::SkillDualWield),
luabind::value("EagleStrike", EQEmu::skills::SkillEagleStrike),
luabind::value("Evocation", EQEmu::skills::SkillEvocation),
luabind::value("FeignDeath", EQEmu::skills::SkillFeignDeath),
luabind::value("FlyingKick", EQEmu::skills::SkillFlyingKick),
luabind::value("Forage", EQEmu::skills::SkillForage),
luabind::value("HandtoHand", EQEmu::skills::SkillHandtoHand),
luabind::value("Hide", EQEmu::skills::SkillHide),
luabind::value("Kick", EQEmu::skills::SkillKick),
luabind::value("Meditate", EQEmu::skills::SkillMeditate),
luabind::value("Mend", EQEmu::skills::SkillMend),
luabind::value("Offense", EQEmu::skills::SkillOffense),
luabind::value("Parry", EQEmu::skills::SkillParry),
luabind::value("PickLock", EQEmu::skills::SkillPickLock),
luabind::value("1HPiercing", EQEmu::skills::Skill1HPiercing),
luabind::value("Riposte", EQEmu::skills::SkillRiposte),
luabind::value("RoundKick", EQEmu::skills::SkillRoundKick),
luabind::value("SafeFall", EQEmu::skills::SkillSafeFall),
luabind::value("SenseHeading", EQEmu::skills::SkillSenseHeading),
luabind::value("Singing", EQEmu::skills::SkillSinging),
luabind::value("Sneak", EQEmu::skills::SkillSneak),
luabind::value("SpecializeAbjure", EQEmu::skills::SkillSpecializeAbjure),
luabind::value("SpecializeAlteration", EQEmu::skills::SkillSpecializeAlteration),
luabind::value("SpecializeConjuration", EQEmu::skills::SkillSpecializeConjuration),
luabind::value("SpecializeDivination", EQEmu::skills::SkillSpecializeDivination),
luabind::value("SpecializeEvocation", EQEmu::skills::SkillSpecializeEvocation),
luabind::value("PickPockets", EQEmu::skills::SkillPickPockets),
luabind::value("StringedInstruments", EQEmu::skills::SkillStringedInstruments),
luabind::value("Swimming", EQEmu::skills::SkillSwimming),
luabind::value("Throwing", EQEmu::skills::SkillThrowing),
luabind::value("TigerClaw", EQEmu::skills::SkillTigerClaw),
luabind::value("Tracking", EQEmu::skills::SkillTracking),
luabind::value("WindInstruments", EQEmu::skills::SkillWindInstruments),
luabind::value("Fishing", EQEmu::skills::SkillFishing),
luabind::value("MakePoison", EQEmu::skills::SkillMakePoison),
luabind::value("Tinkering", EQEmu::skills::SkillTinkering),
luabind::value("Research", EQEmu::skills::SkillResearch),
luabind::value("Alchemy", EQEmu::skills::SkillAlchemy),
luabind::value("Baking", EQEmu::skills::SkillBaking),
luabind::value("Tailoring", EQEmu::skills::SkillTailoring),
luabind::value("SenseTraps", EQEmu::skills::SkillSenseTraps),
luabind::value("Blacksmithing", EQEmu::skills::SkillBlacksmithing),
luabind::value("Fletching", EQEmu::skills::SkillFletching),
luabind::value("Brewing", EQEmu::skills::SkillBrewing),
luabind::value("AlcoholTolerance", EQEmu::skills::SkillAlcoholTolerance),
luabind::value("Begging", EQEmu::skills::SkillBegging),
luabind::value("JewelryMaking", EQEmu::skills::SkillJewelryMaking),
luabind::value("Pottery", EQEmu::skills::SkillPottery),
luabind::value("PercussionInstruments", EQEmu::skills::SkillPercussionInstruments),
luabind::value("Intimidation", EQEmu::skills::SkillIntimidation),
luabind::value("Berserking", EQEmu::skills::SkillBerserking),
luabind::value("Taunt", EQEmu::skills::SkillTaunt),
luabind::value("Frenzy", EQEmu::skills::SkillFrenzy),
luabind::value("RemoveTraps", EQEmu::skills::SkillRemoveTraps),
luabind::value("TripleAttack", EQEmu::skills::SkillTripleAttack),
luabind::value("2HPiercing", EQEmu::skills::Skill2HPiercing),
luabind::value("HIGHEST_SKILL", EQEmu::skills::HIGHEST_SKILL)
];
}
luabind::scope lua_register_bodytypes() {
return luabind::class_<BodyTypes>("BT")
.enum_("constants")
[
luabind::value("Humanoid", 1),
luabind::value("Lycanthrope", 2),
luabind::value("Undead", 3),
luabind::value("Giant", 4),
luabind::value("Construct", 5),
luabind::value("Extraplanar", 6),
luabind::value("Magical", 7),
luabind::value("SummonedUndead", 8),
luabind::value("RaidGiant", 9),
luabind::value("NoTarget", 11),
luabind::value("Vampire", 12),
luabind::value("Atenha_Ra", 13),
luabind::value("Greater_Akheva", 14),
luabind::value("Khati_Sha", 15),
luabind::value("Seru", 16),
luabind::value("Draz_Nurakk", 18),
luabind::value("Zek", 19),
luabind::value("Luggald", 20),
luabind::value("Animal", 21),
luabind::value("Insect", 22),
luabind::value("Monster", 23),
luabind::value("Summoned", 24),
luabind::value("Plant", 25),
luabind::value("Dragon", 26),
luabind::value("Summoned2", 27),
luabind::value("Summoned3", 28),
luabind::value("VeliousDragon", 30),
luabind::value("Dragon3", 32),
luabind::value("Boxes", 33),
luabind::value("Muramite", 34),
luabind::value("NoTarget2", 60),
luabind::value("SwarmPet", 63),
luabind::value("InvisMan", 66),
luabind::value("Special", 67)
];
}
luabind::scope lua_register_filters() {
return luabind::class_<Filters>("Filter")
.enum_("constants")
[
luabind::value("None", FilterNone),
luabind::value("GuildChat", FilterGuildChat),
luabind::value("Socials", FilterSocials),
luabind::value("GroupChat", FilterGroupChat),
luabind::value("Shouts", FilterShouts),
luabind::value("Auctions", FilterAuctions),
luabind::value("OOC", FilterOOC),
luabind::value("BadWords", FilterBadWords),
luabind::value("PCSpells", FilterPCSpells),
luabind::value("NPCSpells", FilterNPCSpells),
luabind::value("BardSongs", FilterBardSongs),
luabind::value("SpellCrits", FilterSpellCrits),
luabind::value("MeleeCrits", FilterMeleeCrits),
luabind::value("SpellDamage", FilterSpellDamage),
luabind::value("MyMisses", FilterMyMisses),
luabind::value("OthersMiss", FilterOthersMiss),
luabind::value("OthersHit", FilterOthersHit),
luabind::value("MissedMe", FilterMissedMe),
luabind::value("DamageShields", FilterDamageShields),
luabind::value("DOT", FilterDOT),
luabind::value("PetHits", FilterPetHits),
luabind::value("PetMisses", FilterPetMisses),
luabind::value("FocusEffects", FilterFocusEffects),
luabind::value("PetSpells", FilterPetSpells),
luabind::value("HealOverTime", FilterHealOverTime),
luabind::value("Unknown25", FilterUnknown25),
luabind::value("Unknown26", FilterUnknown26),
luabind::value("Unknown27", FilterUnknown27),
luabind::value("Unknown28", FilterUnknown28)
];
}
luabind::scope lua_register_message_types() {
return luabind::class_<MessageTypes>("MT")
.enum_("constants")
[
luabind::value("Say", MT_Say),
luabind::value("Tell", MT_Tell),
luabind::value("Group", MT_Group),
luabind::value("Guild", MT_Guild),
luabind::value("OOC", MT_OOC),
luabind::value("Auction", MT_Auction),
luabind::value("Shout", MT_Shout),
luabind::value("Emote", MT_Emote),
luabind::value("Spells", MT_Spells),
luabind::value("YouHitOther", MT_YouHitOther),
luabind::value("OtherHitsYou", MT_OtherHitsYou),
luabind::value("YouMissOther", MT_YouMissOther),
luabind::value("OtherMissesYou", MT_OtherMissesYou),
luabind::value("Broadcasts", MT_Broadcasts),
luabind::value("Skills", MT_Skills),
luabind::value("Disciplines", MT_Disciplines),
luabind::value("Unused1", MT_Unused1),
luabind::value("DefaultText", MT_DefaultText),
luabind::value("Unused2", MT_Unused2),
luabind::value("MerchantOffer", MT_MerchantOffer),
luabind::value("MerchantBuySell", MT_MerchantBuySell),
luabind::value("YourDeath", MT_YourDeath),
luabind::value("OtherDeath", MT_OtherDeath),
luabind::value("OtherHits", MT_OtherHits),
luabind::value("OtherMisses", MT_OtherMisses),
luabind::value("Who", MT_Who),
luabind::value("YellForHelp", MT_YellForHelp),
luabind::value("NonMelee", MT_NonMelee),
luabind::value("WornOff", MT_WornOff),
luabind::value("MoneySplit", MT_MoneySplit),
luabind::value("LootMessages", MT_LootMessages),
luabind::value("DiceRoll", MT_DiceRoll),
luabind::value("OtherSpells", MT_OtherSpells),
luabind::value("SpellFailure", MT_SpellFailure),
luabind::value("Chat", MT_Chat),
luabind::value("Channel1", MT_Channel1),
luabind::value("Channel2", MT_Channel2),
luabind::value("Channel3", MT_Channel3),
luabind::value("Channel4", MT_Channel4),
luabind::value("Channel5", MT_Channel5),
luabind::value("Channel6", MT_Channel6),
luabind::value("Channel7", MT_Channel7),
luabind::value("Channel8", MT_Channel8),
luabind::value("Channel9", MT_Channel9),
luabind::value("Channel10", MT_Channel10),
luabind::value("CritMelee", MT_CritMelee),
luabind::value("SpellCrits", MT_SpellCrits),
luabind::value("TooFarAway", MT_TooFarAway),
luabind::value("NPCRampage", MT_NPCRampage),
luabind::value("NPCFlurry", MT_NPCFlurry),
luabind::value("NPCEnrage", MT_NPCEnrage),
luabind::value("SayEcho", MT_SayEcho),
luabind::value("TellEcho", MT_TellEcho),
luabind::value("GroupEcho", MT_GroupEcho),
luabind::value("GuildEcho", MT_GuildEcho),
luabind::value("OOCEcho", MT_OOCEcho),
luabind::value("AuctionEcho", MT_AuctionEcho),
luabind::value("ShoutECho", MT_ShoutECho),
luabind::value("EmoteEcho", MT_EmoteEcho),
luabind::value("Chat1Echo", MT_Chat1Echo),
luabind::value("Chat2Echo", MT_Chat2Echo),
luabind::value("Chat3Echo", MT_Chat3Echo),
luabind::value("Chat4Echo", MT_Chat4Echo),
luabind::value("Chat5Echo", MT_Chat5Echo),
luabind::value("Chat6Echo", MT_Chat6Echo),
luabind::value("Chat7Echo", MT_Chat7Echo),
luabind::value("Chat8Echo", MT_Chat8Echo),
luabind::value("Chat9Echo", MT_Chat9Echo),
luabind::value("Chat10Echo", MT_Chat10Echo),
luabind::value("DoTDamage", MT_DoTDamage),
luabind::value("ItemLink", MT_ItemLink),
luabind::value("RaidSay", MT_RaidSay),
luabind::value("MyPet", MT_MyPet),
luabind::value("DS", MT_DS),
luabind::value("Leadership", MT_Leadership),
luabind::value("PetFlurry", MT_PetFlurry),
luabind::value("PetCrit", MT_PetCrit),
luabind::value("FocusEffect", MT_FocusEffect),
luabind::value("Experience", MT_Experience),
luabind::value("System", MT_System),
luabind::value("PetSpell", MT_PetSpell),
luabind::value("PetResponse", MT_PetResponse),
luabind::value("ItemSpeech", MT_ItemSpeech),
luabind::value("StrikeThrough", MT_StrikeThrough),
luabind::value("Stun", MT_Stun)
];
}
luabind::scope lua_register_rules_const() {
return luabind::class_<Rule>("Rule")
.enum_("constants")
[
#define RULE_INT(cat, rule, default_value) \
luabind::value(#rule, RuleManager::Int__##rule),
#include "../common/ruletypes.h"
luabind::value("_IntRuleCount", RuleManager::_IntRuleCount),
#undef RULE_INT
#define RULE_REAL(cat, rule, default_value) \
luabind::value(#rule, RuleManager::Real__##rule),
#include "../common/ruletypes.h"
luabind::value("_RealRuleCount", RuleManager::_RealRuleCount),
#undef RULE_REAL
#define RULE_BOOL(cat, rule, default_value) \
luabind::value(#rule, RuleManager::Bool__##rule),
#include "../common/ruletypes.h"
luabind::value("_BoolRuleCount", RuleManager::_BoolRuleCount)
];
}
luabind::scope lua_register_rulei() {
return luabind::namespace_("RuleI")
[
luabind::def("Get", &get_rulei)
];
}
luabind::scope lua_register_ruler() {
return luabind::namespace_("RuleR")
[
luabind::def("Get", &get_ruler)
];
}
luabind::scope lua_register_ruleb() {
return luabind::namespace_("RuleB")
[
luabind::def("Get", &get_ruleb)
];
}
#endif

View File

@ -3,12 +3,22 @@
#ifdef LUA_EQEMU
luabind::scope lua_register_general();
luabind::scope lua_register_random();
luabind::scope lua_register_events();
luabind::scope lua_register_faction();
luabind::scope lua_register_slot();
luabind::scope lua_register_material();
luabind::scope lua_register_client_version();
luabind::scope lua_register_appearance();
luabind::scope lua_register_classes();
luabind::scope lua_register_skills();
luabind::scope lua_register_bodytypes();
luabind::scope lua_register_filters();
luabind::scope lua_register_message_types();
luabind::scope lua_register_rules_const();
luabind::scope lua_register_rulei();
luabind::scope lua_register_ruler();
luabind::scope lua_register_ruleb();
#endif
#endif
#endif

View File

@ -10,6 +10,7 @@
#include "lua_mob.h"
#include "lua_hate_list.h"
#include "lua_client.h"
#include "lua_stat_bonuses.h"
struct SpecialAbilities { };
@ -1725,6 +1726,18 @@ int Lua_Mob::GetSkillDmgTaken(int skill) {
return self->GetSkillDmgTaken(static_cast<EQEmu::skills::SkillType>(skill));
}
int Lua_Mob::GetFcDamageAmtIncoming(Lua_Mob caster, uint32 spell_id, bool use_skill, uint16 skill)
{
Lua_Safe_Call_Int();
return self->GetFcDamageAmtIncoming(caster, spell_id, use_skill, skill);
}
int Lua_Mob::GetSkillDmgAmt(uint16 skill)
{
Lua_Safe_Call_Int();
return self->GetSkillDmgAmt(skill);
}
void Lua_Mob::SetAllowBeneficial(bool value) {
Lua_Safe_Call_Void();
self->SetAllowBeneficial(value);
@ -1985,6 +1998,89 @@ int32 Lua_Mob::GetMeleeMitigation() {
return self->GetMeleeMitigation();
}
int Lua_Mob::GetWeaponDamageBonus(Lua_Item weapon, bool offhand) {
Lua_Safe_Call_Int();
return self->GetWeaponDamageBonus(weapon, offhand);
}
Lua_StatBonuses Lua_Mob::GetItemBonuses()
{
Lua_Safe_Call_Class(Lua_StatBonuses);
return self->GetItemBonusesPtr();
}
Lua_StatBonuses Lua_Mob::GetSpellBonuses()
{
Lua_Safe_Call_Class(Lua_StatBonuses);
return self->GetSpellBonusesPtr();
}
Lua_StatBonuses Lua_Mob::GetAABonuses()
{
Lua_Safe_Call_Class(Lua_StatBonuses);
return self->GetAABonusesPtr();
}
int16 Lua_Mob::GetMeleeDamageMod_SE(uint16 skill)
{
Lua_Safe_Call_Int();
return self->GetMeleeDamageMod_SE(skill);
}
int16 Lua_Mob::GetMeleeMinDamageMod_SE(uint16 skill)
{
Lua_Safe_Call_Int();
return self->GetMeleeMinDamageMod_SE(skill);
}
bool Lua_Mob::IsAttackAllowed(Lua_Mob target, bool isSpellAttack) {
Lua_Safe_Call_Bool();
return self->IsAttackAllowed(target, isSpellAttack);
}
bool Lua_Mob::IsCasting() {
Lua_Safe_Call_Bool();
return self->IsCasting();
}
int Lua_Mob::AttackAnimation(int Hand, Lua_ItemInst weapon) {
Lua_Safe_Call_Int();
return (int)self->AttackAnimation(Hand, weapon);
}
int Lua_Mob::GetWeaponDamage(Lua_Mob against, Lua_ItemInst weapon) {
Lua_Safe_Call_Int();
return self->GetWeaponDamage(against, weapon);
}
bool Lua_Mob::IsBerserk() {
Lua_Safe_Call_Bool();
return self->IsBerserk();
}
bool Lua_Mob::TryFinishingBlow(Lua_Mob defender, int &damage) {
Lua_Safe_Call_Bool();
return self->TryFinishingBlow(defender, damage);
}
int Lua_Mob::GetBodyType()
{
Lua_Safe_Call_Int();
return (int)self->GetBodyType();
}
int Lua_Mob::GetOrigBodyType()
{
Lua_Safe_Call_Int();
return (int)self->GetOrigBodyType();
}
void Lua_Mob::CheckNumHitsRemaining(int type, int32 buff_slot, uint16 spell_id)
{
Lua_Safe_Call_Void();
self->CheckNumHitsRemaining((NumHit)type, buff_slot, spell_id);
}
luabind::scope lua_register_mob() {
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
.def(luabind::constructor<>())
@ -2281,6 +2377,8 @@ luabind::scope lua_register_mob() {
.def("ModSkillDmgTaken", (void(Lua_Mob::*)(int,int))&Lua_Mob::ModSkillDmgTaken)
.def("GetModSkillDmgTaken", (int(Lua_Mob::*)(int))&Lua_Mob::GetModSkillDmgTaken)
.def("GetSkillDmgTaken", (int(Lua_Mob::*)(int))&Lua_Mob::GetSkillDmgTaken)
.def("GetFcDamageAmtIncoming", &Lua_Mob::GetFcDamageAmtIncoming)
.def("GetSkillDmgAmt", (int(Lua_Mob::*)(int))&Lua_Mob::GetSkillDmgAmt)
.def("SetAllowBeneficial", (void(Lua_Mob::*)(bool))&Lua_Mob::SetAllowBeneficial)
.def("GetAllowBeneficial", (bool(Lua_Mob::*)(void))&Lua_Mob::GetAllowBeneficial)
.def("IsBeneficialAllowed", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsBeneficialAllowed)
@ -2330,7 +2428,22 @@ luabind::scope lua_register_mob() {
.def("HasPet", (bool(Lua_Mob::*)(void))&Lua_Mob::HasPet)
.def("IsSilenced", (bool(Lua_Mob::*)(void))&Lua_Mob::IsSilenced)
.def("IsAmnesiad", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAmnesiad)
.def("GetMeleeMitigation", (int32(Lua_Mob::*)(void))&Lua_Mob::GetMeleeMitigation);
.def("GetMeleeMitigation", (int32(Lua_Mob::*)(void))&Lua_Mob::GetMeleeMitigation)
.def("GetWeaponDamageBonus", &Lua_Mob::GetWeaponDamageBonus)
.def("GetItemBonuses", &Lua_Mob::GetItemBonuses)
.def("GetSpellBonuses", &Lua_Mob::GetSpellBonuses)
.def("GetAABonuses", &Lua_Mob::GetAABonuses)
.def("GetMeleeDamageMod_SE", &Lua_Mob::GetMeleeDamageMod_SE)
.def("GetMeleeMinDamageMod_SE", &Lua_Mob::GetMeleeMinDamageMod_SE)
.def("IsAttackAllowed", &Lua_Mob::IsAttackAllowed)
.def("IsCasting", &Lua_Mob::IsCasting)
.def("AttackAnimation", &Lua_Mob::AttackAnimation)
.def("GetWeaponDamage", &Lua_Mob::GetWeaponDamage)
.def("IsBerserk", &Lua_Mob::IsBerserk)
.def("TryFinishingBlow", &Lua_Mob::TryFinishingBlow)
.def("GetBodyType", &Lua_Mob::GetBodyType)
.def("GetOrigBodyType", &Lua_Mob::GetOrigBodyType)
.def("CheckNumHitsRemaining", &Lua_Mob::CheckNumHitsRemaining);
}
luabind::scope lua_register_special_abilities() {

View File

@ -8,6 +8,7 @@ class Mob;
struct Lua_HateList;
class Lua_Item;
class Lua_ItemInst;
class Lua_StatBonuses;
namespace luabind {
struct scope;
@ -330,6 +331,8 @@ public:
void ModSkillDmgTaken(int skill, int value);
int GetModSkillDmgTaken(int skill);
int GetSkillDmgTaken(int skill);
int GetFcDamageAmtIncoming(Lua_Mob caster, uint32 spell_id, bool use_skill, uint16 skill);
int GetSkillDmgAmt(uint16 skill);
void SetAllowBeneficial(bool value);
bool GetAllowBeneficial();
bool IsBeneficialAllowed(Lua_Mob target);
@ -340,7 +343,6 @@ public:
void SetFlurryChance(int value);
int GetFlurryChance();
int GetSkill(int skill_id);
void CalcBonuses();
int GetSpecialAbility(int ability);
int GetSpecialAbilityParam(int ability, int param);
void SetSpecialAbility(int ability, int level);
@ -381,6 +383,21 @@ public:
bool IsSilenced();
bool IsAmnesiad();
int32 GetMeleeMitigation();
int GetWeaponDamageBonus(Lua_Item weapon, bool offhand);
Lua_StatBonuses GetItemBonuses();
Lua_StatBonuses GetSpellBonuses();
Lua_StatBonuses GetAABonuses();
int16 GetMeleeDamageMod_SE(uint16 skill);
int16 GetMeleeMinDamageMod_SE(uint16 skill);
bool IsAttackAllowed(Lua_Mob target, bool isSpellAttack);
bool IsCasting();
int AttackAnimation(int Hand, Lua_ItemInst weapon);
int GetWeaponDamage(Lua_Mob against, Lua_ItemInst weapon);
bool IsBerserk();
bool TryFinishingBlow(Lua_Mob defender, int &damage);
int GetBodyType();
int GetOrigBodyType();
void CheckNumHitsRemaining(int type, int32 buff_slot, uint16 spell_id);
};
#endif

631
zone/lua_mod.cpp Normal file
View File

@ -0,0 +1,631 @@
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include <luabind/object.hpp>
#include "../common/spdat.h"
#include "masterentity.h"
#include "questmgr.h"
#include "zone.h"
#include "zone_config.h"
#include "lua_parser.h"
#include "lua_mod.h"
#include "lua_bit.h"
#include "lua_entity.h"
#include "lua_item.h"
#include "lua_iteminst.h"
#include "lua_mob.h"
#include "lua_hate_list.h"
#include "lua_client.h"
#include "lua_inventory.h"
#include "lua_npc.h"
#include "lua_spell.h"
#include "lua_entity_list.h"
#include "lua_group.h"
#include "lua_raid.h"
#include "lua_corpse.h"
#include "lua_object.h"
#include "lua_door.h"
#include "lua_spawn.h"
#include "lua_packet.h"
#include "lua_general.h"
#include "lua_encounter.h"
#include "lua_stat_bonuses.h"
void LuaMod::Init()
{
m_has_melee_mitigation = parser_->HasFunction("MeleeMitigation", package_name_);
m_has_apply_damage_table = parser_->HasFunction("ApplyDamageTable", package_name_);
m_has_avoid_damage = parser_->HasFunction("AvoidDamage", package_name_);
m_has_check_hit_chance = parser_->HasFunction("CheckHitChance", package_name_);
m_has_try_critical_hit = parser_->HasFunction("TryCriticalHit", package_name_);
m_has_get_required_aa_experience = parser_->HasFunction("GetRequiredAAExperience", package_name_);
m_has_get_exp_for_level = parser_->HasFunction("GetEXPForLevel", package_name_);
m_has_get_experience_for_kill = parser_->HasFunction("GetExperienceForKill", package_name_);
m_has_common_outgoing_hit_success = parser_->HasFunction("CommonOutgoingHitSuccess", package_name_);
}
void PutDamageHitInfo(lua_State *L, luabind::adl::object &e, DamageHitInfo &hit) {
luabind::adl::object lua_hit = luabind::newtable(L);
lua_hit["base_damage"] = hit.base_damage;
lua_hit["min_damage"] = hit.min_damage;
lua_hit["damage_done"] = hit.damage_done;
lua_hit["offense"] = hit.offense;
lua_hit["tohit"] = hit.tohit;
lua_hit["hand"] = hit.hand;
lua_hit["skill"] = (int)hit.skill;
e["hit"] = lua_hit;
}
void GetDamageHitInfo(luabind::adl::object &ret, DamageHitInfo &hit) {
auto luaHitTable = ret["hit"];
if (luabind::type(luaHitTable) == LUA_TTABLE) {
auto base_damage = luaHitTable["base_damage"];
auto min_damage = luaHitTable["min_damage"];
auto damage_done = luaHitTable["damage_done"];
auto offense = luaHitTable["offense"];
auto tohit = luaHitTable["tohit"];
auto hand = luaHitTable["hand"];
auto skill = luaHitTable["skill"];
if (luabind::type(base_damage) == LUA_TNUMBER) {
hit.base_damage = luabind::object_cast<int>(base_damage);
}
if (luabind::type(min_damage) == LUA_TNUMBER) {
hit.min_damage = luabind::object_cast<int>(min_damage);
}
if (luabind::type(damage_done) == LUA_TNUMBER) {
hit.damage_done = luabind::object_cast<int>(damage_done);
}
if (luabind::type(offense) == LUA_TNUMBER) {
hit.offense = luabind::object_cast<int>(offense);
}
if (luabind::type(tohit) == LUA_TNUMBER) {
hit.tohit = luabind::object_cast<int>(tohit);
}
if (luabind::type(hand) == LUA_TNUMBER) {
hit.hand = luabind::object_cast<int>(hand);
}
if (luabind::type(skill) == LUA_TNUMBER) {
hit.skill = (EQEmu::skills::SkillType)luabind::object_cast<int>(skill);
}
}
}
void PutExtraAttackOptions(lua_State *L, luabind::adl::object &e, ExtraAttackOptions *opts) {
if (opts) {
luabind::adl::object lua_opts = luabind::newtable(L);
lua_opts["damage_percent"] = opts->damage_percent;
lua_opts["damage_flat"] = opts->damage_flat;
lua_opts["armor_pen_percent"] = opts->armor_pen_percent;
lua_opts["armor_pen_flat"] = opts->armor_pen_flat;
lua_opts["crit_percent"] = opts->crit_percent;
lua_opts["crit_flat"] = opts->crit_flat;
lua_opts["hate_percent"] = opts->hate_percent;
lua_opts["hate_flat"] = opts->hate_flat;
lua_opts["hit_chance"] = opts->hit_chance;
lua_opts["melee_damage_bonus_flat"] = opts->melee_damage_bonus_flat;
lua_opts["skilldmgtaken_bonus_flat"] = opts->skilldmgtaken_bonus_flat;
e["opts"] = lua_opts;
}
}
void GetExtraAttackOptions(luabind::adl::object &ret, ExtraAttackOptions *opts) {
if (opts) {
auto luaOptsTable = ret["opts"];
if (luabind::type(luaOptsTable) == LUA_TTABLE) {
auto damage_percent = luaOptsTable["damage_percent"];
auto damage_flat = luaOptsTable["damage_flat"];
auto armor_pen_percent = luaOptsTable["armor_pen_percent"];
auto armor_pen_flat = luaOptsTable["armor_pen_flat"];
auto crit_percent = luaOptsTable["crit_percent"];
auto crit_flat = luaOptsTable["crit_flat"];
auto hate_percent = luaOptsTable["hate_percent"];
auto hate_flat = luaOptsTable["hate_flat"];
auto hit_chance = luaOptsTable["hit_chance"];
auto melee_damage_bonus_flat = luaOptsTable["melee_damage_bonus_flat"];
auto skilldmgtaken_bonus_flat = luaOptsTable["skilldmgtaken_bonus_flat"];
if (luabind::type(damage_percent) == LUA_TNUMBER) {
opts->damage_percent = luabind::object_cast<float>(damage_percent);
}
if (luabind::type(damage_flat) == LUA_TNUMBER) {
opts->damage_flat = luabind::object_cast<int>(damage_flat);
}
if (luabind::type(armor_pen_percent) == LUA_TNUMBER) {
opts->armor_pen_percent = luabind::object_cast<float>(armor_pen_percent);
}
if (luabind::type(armor_pen_flat) == LUA_TNUMBER) {
opts->armor_pen_flat = luabind::object_cast<int>(armor_pen_flat);
}
if (luabind::type(crit_percent) == LUA_TNUMBER) {
opts->crit_percent = luabind::object_cast<float>(crit_percent);
}
if (luabind::type(crit_flat) == LUA_TNUMBER) {
opts->crit_flat = luabind::object_cast<float>(crit_flat);
}
if (luabind::type(hate_percent) == LUA_TNUMBER) {
opts->hate_percent = luabind::object_cast<float>(hate_percent);
}
if (luabind::type(hate_flat) == LUA_TNUMBER) {
opts->hate_flat = luabind::object_cast<int>(hate_flat);
}
if (luabind::type(hit_chance) == LUA_TNUMBER) {
opts->hit_chance = luabind::object_cast<int>(hit_chance);
}
if (luabind::type(melee_damage_bonus_flat) == LUA_TNUMBER) {
opts->melee_damage_bonus_flat = luabind::object_cast<int>(melee_damage_bonus_flat);
}
if (luabind::type(skilldmgtaken_bonus_flat) == LUA_TNUMBER) {
opts->skilldmgtaken_bonus_flat = luabind::object_cast<int>(skilldmgtaken_bonus_flat);
}
}
}
}
void LuaMod::MeleeMitigation(Mob *self, Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault) {
int start = lua_gettop(L);
try {
if (!m_has_melee_mitigation) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "MeleeMitigation");
Lua_Mob l_self(self);
Lua_Mob l_other(attacker);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
e["other"] = l_other;
PutDamageHitInfo(L, e, hit);
PutExtraAttackOptions(L, e, opts);
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
GetDamageHitInfo(ret, hit);
GetExtraAttackOptions(ret, opts);
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}
void LuaMod::ApplyDamageTable(Mob *self, DamageHitInfo &hit, bool &ignoreDefault) {
int start = lua_gettop(L);
try {
if (!m_has_apply_damage_table) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "ApplyDamageTable");
Lua_Mob l_self(self);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
PutDamageHitInfo(L, e, hit);
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
GetDamageHitInfo(ret, hit);
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}
void LuaMod::AvoidDamage(Mob *self, Mob *other, DamageHitInfo &hit, bool &returnValue, bool &ignoreDefault) {
int start = lua_gettop(L);
try {
if (!m_has_avoid_damage) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "AvoidDamage");
Lua_Mob l_self(self);
Lua_Mob l_other(other);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
e["other"] = l_other;
PutDamageHitInfo(L, e, hit);
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
auto returnValueObj = ret["ReturnValue"];
if (luabind::type(returnValueObj) == LUA_TBOOLEAN) {
returnValue = luabind::object_cast<bool>(returnValueObj);
}
GetDamageHitInfo(ret, hit);
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}
void LuaMod::CheckHitChance(Mob *self, Mob* other, DamageHitInfo &hit, bool &returnValue, bool &ignoreDefault) {
int start = lua_gettop(L);
try {
if (!m_has_check_hit_chance) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "CheckHitChance");
Lua_Mob l_self(self);
Lua_Mob l_other(other);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
e["other"] = l_other;
PutDamageHitInfo(L, e, hit);
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
auto returnValueObj = ret["ReturnValue"];
if (luabind::type(returnValueObj) == LUA_TBOOLEAN) {
returnValue = luabind::object_cast<bool>(returnValueObj);
}
GetDamageHitInfo(ret, hit);
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}
void LuaMod::CommonOutgoingHitSuccess(Mob *self, Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault)
{
int start = lua_gettop(L);
try {
if (!m_has_common_outgoing_hit_success) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "CommonOutgoingHitSuccess");
Lua_Mob l_self(self);
Lua_Mob l_other(other);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
e["other"] = l_other;
PutDamageHitInfo(L, e, hit);
PutExtraAttackOptions(L, e, opts);
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
GetDamageHitInfo(ret, hit);
GetExtraAttackOptions(ret, opts);
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}
void LuaMod::TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault) {
int start = lua_gettop(L);
try {
if (!m_has_try_critical_hit) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "TryCriticalHit");
Lua_Mob l_self(self);
Lua_Mob l_other(defender);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
e["other"] = l_other;
PutDamageHitInfo(L, e, hit);
PutExtraAttackOptions(L, e, opts);
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
GetDamageHitInfo(ret, hit);
GetExtraAttackOptions(ret, opts);
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}
void LuaMod::GetRequiredAAExperience(Client *self, uint32 &returnValue, bool &ignoreDefault)
{
int start = lua_gettop(L);
try {
if (!m_has_get_required_aa_experience) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "GetRequiredAAExperience");
Lua_Client l_self(self);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
auto returnValueObj = ret["ReturnValue"];
if (luabind::type(returnValueObj) == LUA_TNUMBER) {
returnValue = luabind::object_cast<uint32>(returnValueObj);
}
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}
void LuaMod::GetEXPForLevel(Client *self, uint16 level, uint32 &returnValue, bool &ignoreDefault) {
int start = lua_gettop(L);
try {
if (!m_has_get_exp_for_level) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "GetEXPForLevel");
Lua_Client l_self(self);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
e["level"] = level;
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
auto returnValueObj = ret["ReturnValue"];
if (luabind::type(returnValueObj) == LUA_TNUMBER) {
returnValue = luabind::object_cast<uint32>(returnValueObj);
}
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}
void LuaMod::GetExperienceForKill(Client *self, Mob *against, uint32 &returnValue, bool &ignoreDefault)
{
int start = lua_gettop(L);
uint32 retval = 0;
try {
if (!m_has_get_experience_for_kill) {
return;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str());
lua_getfield(L, -1, "GetExperienceForKill");
Lua_Client l_self(self);
Lua_Mob l_other(against);
luabind::adl::object e = luabind::newtable(L);
e["self"] = l_self;
e["other"] = l_other;
e.push(L);
if (lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
parser_->AddError(error);
lua_pop(L, 1);
return;
}
if (lua_type(L, -1) == LUA_TTABLE) {
luabind::adl::object ret(luabind::from_stack(L, -1));
auto IgnoreDefaultObj = ret["IgnoreDefault"];
if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) {
ignoreDefault = ignoreDefault || luabind::object_cast<bool>(IgnoreDefaultObj);
}
auto returnValueObj = ret["ReturnValue"];
if (luabind::type(returnValueObj) == LUA_TNUMBER) {
returnValue = luabind::object_cast<uint32>(returnValueObj);
}
}
}
catch (std::exception &ex) {
parser_->AddError(ex.what());
}
int end = lua_gettop(L);
int n = end - start;
if (n > 0) {
lua_pop(L, n);
}
}

43
zone/lua_mod.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include <string>
struct lua_State;
class LuaParser;
class LuaMod
{
public:
LuaMod(lua_State *ls, LuaParser *lp, const std::string &package_name) {
L = ls;
parser_ = lp;
package_name_ = package_name;
Init();
}
~LuaMod() { }
void Init();
void MeleeMitigation(Mob *self, Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault);
void ApplyDamageTable(Mob *self, DamageHitInfo &hit, bool &ignoreDefault);
void AvoidDamage(Mob *self, Mob *other, DamageHitInfo &hit, bool &returnValue, bool &ignoreDefault);
void CheckHitChance(Mob *self, Mob* other, DamageHitInfo &hit, bool &returnValue, bool &ignoreDefault);
void CommonOutgoingHitSuccess(Mob *self, Mob* other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault);
void TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault);
void GetRequiredAAExperience(Client *self, uint32 &returnValue, bool &ignoreDefault);
void GetEXPForLevel(Client *self, uint16 level, uint32 &returnValue, bool &ignoreDefault);
void GetExperienceForKill(Client *self, Mob *against, uint32 &returnValue, bool &ignoreDefault);
private:
LuaParser *parser_;
lua_State *L;
std::string package_name_;
bool m_has_melee_mitigation;
bool m_has_apply_damage_table;
bool m_has_avoid_damage;
bool m_has_check_hit_chance;
bool m_has_common_outgoing_hit_success;
bool m_has_try_critical_hit;
bool m_has_get_required_aa_experience;
bool m_has_get_exp_for_level;
bool m_has_get_experience_for_kill;
};

View File

@ -498,6 +498,17 @@ uint8 Lua_NPC::GetMerchantProbability() {
return self->GetMerchantProbability();
}
int Lua_NPC::GetRawAC() {
Lua_Safe_Call_Int();
return self->GetRawAC();
}
int Lua_NPC::GetAvoidanceRating()
{
Lua_Safe_Call_Int();
return self->GetAvoidanceRating();
}
luabind::scope lua_register_npc() {
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
.def(luabind::constructor<>())
@ -598,7 +609,9 @@ luabind::scope lua_register_npc() {
.def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop)
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop)
.def("SetMerchantProbability", (void(Lua_NPC::*)(void))&Lua_NPC::SetMerchantProbability)
.def("GetMerchantProbability", (uint8(Lua_NPC::*)(void))&Lua_NPC::GetMerchantProbability);
.def("GetMerchantProbability", (uint8(Lua_NPC::*)(void))&Lua_NPC::GetMerchantProbability)
.def("GetRawAC", (int(Lua_NPC::*)(void))&Lua_NPC::GetRawAC)
.def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating);
}
#endif

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