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,5 +1,51 @@
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).

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

@ -139,9 +139,9 @@ void EQ::Net::DaybreakConnectionManager::Process()
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) {
auto time_since_last_send = std::chrono::duration_cast<std::chrono::milliseconds>(now - connection->m_last_send);
@ -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;
}
@ -407,10 +402,16 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p)
for (int i = 1; i >= 0; --i) {
switch (m_encode_passes[i]) {
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;
@ -425,7 +426,10 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p)
for (int i = 1; i >= 0; --i) {
switch (m_encode_passes[i]) {
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;
@ -1187,24 +1191,21 @@ 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);
}
for (int i = 0; i < 2; ++i) {
switch (m_encode_passes[i]) {
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,8 +81,13 @@ void EQ::Net::EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req)
break;
}
if (ack_req) {
m_connection->QueuePacket(out);
}
else {
m_connection->QueuePacket(out, 0, false);
}
}
}
void EQ::Net::EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) {

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;
@ -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
@ -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`;
$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,8 +114,23 @@ 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
@ -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
// 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
// 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 (IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && GetOwner()->GetAA(aaAdvancedPetDiscipline) >= 1 && IsFocused()) {
if (!targetmob)
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))

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;
}
@ -3188,6 +3210,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
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,8 +3854,8 @@ 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) {
@ -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 (last_reported_mana != current_mana || last_reported_endurance != current_endurance) {
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
SendManaUpdate();
SendEnduranceUpdate();
}
if (last_reported_mana != cur_mana || last_reported_endur != cur_end) {
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));
MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp->pBuffer;
MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp2->pBuffer;
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);
if (raid) {
raid->SendManaPacketFrom(this);
}
else if (group) {
group->SendManaPacketFrom(this);
}
safe_delete(outapp);
safe_delete(outapp2);
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);
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());
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_npcs.insert(std::pair<NPC *, float>(npc, distance));
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
}
else if (mob->GetAggroRange() > scan_range) {
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
}
else if (npc->GetAggroRange() > scan_range) {
close_npcs.insert(std::pair<NPC *, float>(npc, 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;
@ -828,24 +784,19 @@ 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)
{
Mob *curmob = nullptr;

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,13 +1246,10 @@ 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()) {
if (!ent->InZone() || !ent->ShouldISpawnFor(client)) {
++it;
continue;
}
}
app = new EQApplicationPacket;
it->second->CastToMob()->CreateSpawnPacket(app); // TODO: Use zonespawns opcode instead
@ -1275,8 +1276,7 @@ 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
@ -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,11 +2328,10 @@ 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))
npc_limit_list.erase(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()) {
mob = it->second;
if (
mob && !mob->IsCorpse()
&& (it->second != client)
&& (mob->IsClient() || iSendEvenIfNotChanged || (mob->LastChange() >= cLastUpdate))
&& (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);
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)
&& (mob->IsClient() || iSendEvenIfNotChanged || (mob->LastChange() >= cLastUpdate))
&& (!it->second->IsClient() || !it->second->CastToClient()->GMHideMe(client))) {
//bool Grouped = client->HasGroup() && mob->IsClient() && (client->GetGroup() == mob->CastToClient()->GetGroup());
//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);
}
}
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,7 +485,6 @@ 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
@ -572,7 +596,6 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
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);
}
}
@ -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)
if (loop <= 100)
{
m_FearWalkTarget = glm::vec3(ranx, rany, ranz);
else //Break fear
BuffFadeByEffect(SE_Fear);
}
}

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)
{
if(member && member->IsClient())
void Group::SendHPManaEndPacketsTo(Mob *member)
{
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);
@ -453,6 +459,7 @@ luabind::scope lua_register_entity_list() {
.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)

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

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