mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-23 00:42:27 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7300776a85 | |||
| 3878bd0c76 | |||
| a7c0e82c9e | |||
| 49505a7a45 | |||
| 10b01e62df | |||
| c6bb4a6470 | |||
| 15606a99fc | |||
| eddc9c9baf | |||
| 7bbc4a6a44 | |||
| 1f39a0cb3e | |||
| 8d680b2222 | |||
| 21ef83bcbe | |||
| 6253162166 | |||
| 1110b284d8 | |||
| 5c6f684808 | |||
| 9b1a449fba | |||
| e4f337edb6 | |||
| 5a9744b429 | |||
| e0237ce526 | |||
| 4d2825d817 | |||
| 09ccd23d0b | |||
| cbbd01b391 | |||
| 539fa8b262 | |||
| 592bbd3180 | |||
| b09792812a | |||
| 9154938827 | |||
| 4f7b8e0934 | |||
| c0f53647b8 | |||
| 3e1b75b814 | |||
| 497170c453 | |||
| 6773412e40 | |||
| 1c8dea909e | |||
| d6ac686a54 | |||
| 5fac13075b | |||
| 6cc774faf4 | |||
| 8f4ec1b960 | |||
| 357be65a69 | |||
| f164833b00 | |||
| 627859ba73 | |||
| a7c239b801 | |||
| 1cabb091e7 | |||
| d0e612b5ff | |||
| 0a8b21d4ab | |||
| 27fd6316f1 | |||
| 4e15364d42 | |||
| 35c194e2eb | |||
| 0c5c6587e5 | |||
| b5a81fbd07 | |||
| 0a0d4fbb70 | |||
| c1669299aa | |||
| d62219d0ad | |||
| 59ddf507e6 | |||
| 4d94d5fe17 | |||
| d64f2e40c5 | |||
| 75d7c40098 | |||
| 7c377e8904 |
+28
-17
@@ -1,25 +1,36 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 6/28/2017 ==
|
||||
Akkadius: Fixed issues with Z correctness when NPCs are pathing on normal grids
|
||||
Akkadius: Fixed issues with Z correctness when NPCs are engaged with players following
|
||||
Akkadius: NPC corpses should fall into the ground far less
|
||||
|
||||
== 6/25/2017 ==
|
||||
Akkadius: New rules made by developers are now automatically created when world boots up, this keeps
|
||||
from having to issue schema SQL updates every time rules are added.
|
||||
- Whenever a rule isn't present in the database, it will be automatically created
|
||||
Akkadius: Sped up saylink retrieval x1000 helpful for dialogues, plugins with many saylinks
|
||||
|
||||
== 4/16/2017 ==
|
||||
KLS: Merge eqstream branch
|
||||
-UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs).
|
||||
-TCP Server to Server connection stack completely rewritten.
|
||||
-Server connections reconnect much more reliably and quickly now.
|
||||
-Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/).
|
||||
-Protocol behind the tcp connections has changed (see breaking changes section).
|
||||
-API significantly changed and should be easier to write new servers or handlers for.
|
||||
-Telnet console connection has been separated out from the current port (see breaking changes section).
|
||||
-Because of changes to the TCP stack, lsreconnect and echo have been disabled.
|
||||
-The server tic rate has been changed to be approx 30 fps from 500+ fps.
|
||||
-Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough).
|
||||
- UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs).
|
||||
- TCP Server to Server connection stack completely rewritten.
|
||||
- Server connections reconnect much more reliably and quickly now.
|
||||
- Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/).
|
||||
- Protocol behind the tcp connections has changed (see breaking changes section).
|
||||
- API significantly changed and should be easier to write new servers or handlers for.
|
||||
- Telnet console connection has been separated out from the current port (see breaking changes section).
|
||||
- Because of changes to the TCP stack, lsreconnect and echo have been disabled.
|
||||
- The server tic rate has been changed to be approx 30 fps from 500+ fps.
|
||||
- Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough).
|
||||
|
||||
-Breaking changes:
|
||||
-Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/
|
||||
-To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections:
|
||||
-You should add <legacy>1</legacy> to the login section of your configuration file when connecting to a server that is using the old protocol.
|
||||
-The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section.
|
||||
-Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections.
|
||||
-To enable telnet you need to add a telnet tag in the world section of configuration such as:
|
||||
- Breaking changes:
|
||||
- Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/
|
||||
- To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections:
|
||||
- You should add <legacy>1</legacy> to the login section of your configuration file when connecting to a server that is using the old protocol.
|
||||
- The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section.
|
||||
- Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections.
|
||||
- To enable telnet you need to add a telnet tag in the world section of configuration such as:
|
||||
<telnet ip="0.0.0.0" port="9001" enabled="true"/>
|
||||
|
||||
== 4/1/2017 ==
|
||||
|
||||
@@ -141,7 +141,7 @@ void EQ::Net::DaybreakConnectionManager::Process()
|
||||
connection->SendConnect();
|
||||
}
|
||||
}
|
||||
break;
|
||||
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);
|
||||
@@ -277,7 +277,7 @@ EQ::Net::DaybreakConnection::DaybreakConnection(DaybreakConnectionManager *owner
|
||||
m_encode_passes[1] = owner->m_options.encode_passes[1];
|
||||
m_hold_time = Clock::now();
|
||||
m_buffered_packets_length = 0;
|
||||
m_rolling_ping = 900;
|
||||
m_rolling_ping = 500;
|
||||
m_resend_delay = (m_rolling_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms;
|
||||
m_combined.reset(new char[512]);
|
||||
m_combined[0] = 0;
|
||||
@@ -300,7 +300,7 @@ EQ::Net::DaybreakConnection::DaybreakConnection(DaybreakConnectionManager *owner
|
||||
m_crc_bytes = 0;
|
||||
m_hold_time = Clock::now();
|
||||
m_buffered_packets_length = 0;
|
||||
m_rolling_ping = 900;
|
||||
m_rolling_ping = 500;
|
||||
m_resend_delay = (m_rolling_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms;
|
||||
m_combined.reset(new char[512]);
|
||||
m_combined[0] = 0;
|
||||
@@ -365,7 +365,7 @@ void EQ::Net::DaybreakConnection::Process()
|
||||
FlushBuffer();
|
||||
}
|
||||
|
||||
ProcessQueue();
|
||||
ProcessInboundQueue();
|
||||
}
|
||||
catch (std::exception ex) {
|
||||
LogF(Logs::Detail, Logs::Netcode, "Error processing connection: {0}", ex.what());
|
||||
@@ -440,7 +440,7 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p)
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::ProcessQueue()
|
||||
void EQ::Net::DaybreakConnection::ProcessInboundQueue()
|
||||
{
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
auto stream = &m_streams[i];
|
||||
@@ -459,6 +459,31 @@ void EQ::Net::DaybreakConnection::ProcessQueue()
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::ProcessOutboundQueue()
|
||||
{
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
auto stream = &m_streams[i];
|
||||
|
||||
if (stream->outstanding_bytes == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (!stream->buffered_packets.empty()) {
|
||||
auto &buff = stream->buffered_packets.front();
|
||||
|
||||
if (stream->outstanding_bytes + buff.sent.packet.Length() >= m_owner->m_options.max_outstanding_bytes ||
|
||||
stream->outstanding_packets.size() + 1 >= m_owner->m_options.max_outstanding_packets) {
|
||||
break;
|
||||
}
|
||||
|
||||
stream->outstanding_bytes += buff.sent.packet.Length();
|
||||
stream->outstanding_packets.insert(std::make_pair(buff.seq, buff.sent));
|
||||
InternalBufferedSend(buff.sent.packet);
|
||||
stream->buffered_packets.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::RemoveFromQueue(int stream, uint16_t seq)
|
||||
{
|
||||
auto s = &m_streams[stream];
|
||||
@@ -1019,14 +1044,14 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
||||
|
||||
auto now = Clock::now();
|
||||
auto s = &m_streams[stream];
|
||||
for (auto &entry : s->sent_packets) {
|
||||
for (auto &entry : s->outstanding_packets) {
|
||||
auto time_since_last_send = std::chrono::duration_cast<std::chrono::milliseconds>(now - entry.second.last_sent);
|
||||
if (entry.second.times_resent == 0) {
|
||||
if ((size_t)time_since_last_send.count() > m_resend_delay) {
|
||||
InternalBufferedSend(entry.second.packet);
|
||||
entry.second.last_sent = now;
|
||||
entry.second.times_resent++;
|
||||
m_rolling_ping += 300;
|
||||
m_rolling_ping += 100;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1040,7 +1065,7 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
||||
InternalBufferedSend(entry.second.packet);
|
||||
entry.second.last_sent = now;
|
||||
entry.second.times_resent++;
|
||||
m_rolling_ping += 300;
|
||||
m_rolling_ping += 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1051,11 +1076,11 @@ void EQ::Net::DaybreakConnection::Ack(int stream, uint16_t seq)
|
||||
|
||||
auto now = Clock::now();
|
||||
auto s = &m_streams[stream];
|
||||
auto iter = s->sent_packets.begin();
|
||||
while (iter != s->sent_packets.end()) {
|
||||
auto iter = s->outstanding_packets.begin();
|
||||
while (iter != s->outstanding_packets.end()) {
|
||||
auto order = CompareSequence(seq, iter->first);
|
||||
|
||||
if (order != SequenceFuture) {
|
||||
if (order != SequenceFuture) {
|
||||
uint64_t round_time = (uint64_t)std::chrono::duration_cast<std::chrono::milliseconds>(now - iter->second.last_sent).count();
|
||||
|
||||
m_stats.max_ping = std::max(m_stats.max_ping, round_time);
|
||||
@@ -1063,7 +1088,9 @@ void EQ::Net::DaybreakConnection::Ack(int stream, uint16_t seq)
|
||||
m_stats.last_ping = round_time;
|
||||
m_rolling_ping = (m_rolling_ping * 2 + round_time) / 3;
|
||||
|
||||
iter = s->sent_packets.erase(iter);
|
||||
s->outstanding_bytes -= iter->second.packet.Length();
|
||||
iter = s->outstanding_packets.erase(iter);
|
||||
ProcessOutboundQueue();
|
||||
}
|
||||
else {
|
||||
++iter;
|
||||
@@ -1075,8 +1102,8 @@ void EQ::Net::DaybreakConnection::OutOfOrderAck(int stream, uint16_t seq)
|
||||
{
|
||||
auto now = Clock::now();
|
||||
auto s = &m_streams[stream];
|
||||
auto iter = s->sent_packets.find(seq);
|
||||
if (iter != s->sent_packets.end()) {
|
||||
auto iter = s->outstanding_packets.find(seq);
|
||||
if (iter != s->outstanding_packets.end()) {
|
||||
uint64_t round_time = (uint64_t)std::chrono::duration_cast<std::chrono::milliseconds>(now - iter->second.last_sent).count();
|
||||
|
||||
m_stats.max_ping = std::max(m_stats.max_ping, round_time);
|
||||
@@ -1084,10 +1111,31 @@ void EQ::Net::DaybreakConnection::OutOfOrderAck(int stream, uint16_t seq)
|
||||
m_stats.last_ping = round_time;
|
||||
m_rolling_ping = (m_rolling_ping * 2 + round_time) / 3;
|
||||
|
||||
s->sent_packets.erase(iter);
|
||||
s->outstanding_bytes -= iter->second.packet.Length();
|
||||
s->outstanding_packets.erase(iter);
|
||||
ProcessOutboundQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::BufferPacket(int stream, uint16_t seq, DaybreakSentPacket &sent)
|
||||
{
|
||||
auto s = &m_streams[stream];
|
||||
//If we can send the packet then send it
|
||||
//else buffer it to be sent when we can send it
|
||||
if (s->outstanding_bytes + sent.packet.Length() >= m_owner->m_options.max_outstanding_bytes || s->outstanding_packets.size() + 1 >= m_owner->m_options.max_outstanding_packets) {
|
||||
//Would go over one of the limits, buffer this packet.
|
||||
DaybreakBufferedPacket bp;
|
||||
bp.sent = std::move(sent);
|
||||
bp.seq = seq;
|
||||
s->buffered_packets.push_back(bp);
|
||||
return;
|
||||
}
|
||||
|
||||
s->outstanding_bytes += sent.packet.Length();
|
||||
s->outstanding_packets.insert(std::make_pair(seq, sent));
|
||||
InternalBufferedSend(sent.packet);
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::SendAck(int stream_id, uint16_t seq)
|
||||
{
|
||||
DaybreakReliableHeader ack;
|
||||
@@ -1139,6 +1187,10 @@ void EQ::Net::DaybreakConnection::InternalBufferedSend(Packet &p)
|
||||
FlushBuffer();
|
||||
}
|
||||
|
||||
if (m_buffered_packets.size() == 0) {
|
||||
m_hold_time = Clock::now();
|
||||
}
|
||||
|
||||
DynamicPacket copy;
|
||||
copy.PutPacket(0, p);
|
||||
m_buffered_packets.push_back(copy);
|
||||
@@ -1293,11 +1345,9 @@ void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id,
|
||||
sent.last_sent = Clock::now();
|
||||
sent.first_sent = Clock::now();
|
||||
sent.times_resent = 0;
|
||||
stream->sent_packets.insert(std::make_pair(stream->sequence_out, sent));
|
||||
BufferPacket(stream_id, stream->sequence_out, sent);
|
||||
stream->sequence_out++;
|
||||
|
||||
InternalBufferedSend(first_packet);
|
||||
|
||||
while (used < length) {
|
||||
auto left = length - used;
|
||||
DynamicPacket packet;
|
||||
@@ -1321,10 +1371,8 @@ void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id,
|
||||
sent.last_sent = Clock::now();
|
||||
sent.first_sent = Clock::now();
|
||||
sent.times_resent = 0;
|
||||
stream->sent_packets.insert(std::make_pair(stream->sequence_out, sent));
|
||||
BufferPacket(stream_id, stream->sequence_out, sent);
|
||||
stream->sequence_out++;
|
||||
|
||||
InternalBufferedSend(packet);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1341,10 +1389,8 @@ void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id,
|
||||
sent.last_sent = Clock::now();
|
||||
sent.first_sent = Clock::now();
|
||||
sent.times_resent = 0;
|
||||
stream->sent_packets.insert(std::make_pair(stream->sequence_out, sent));
|
||||
BufferPacket(stream_id, stream->sequence_out, sent);
|
||||
stream->sequence_out++;
|
||||
|
||||
InternalBufferedSend(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
namespace EQ
|
||||
@@ -145,6 +145,12 @@ namespace EQ
|
||||
size_t times_resent;
|
||||
};
|
||||
|
||||
struct DaybreakBufferedPacket
|
||||
{
|
||||
uint16_t seq;
|
||||
DaybreakSentPacket sent;
|
||||
};
|
||||
|
||||
struct DaybreakStream
|
||||
{
|
||||
DaybreakStream() {
|
||||
@@ -152,17 +158,20 @@ namespace EQ
|
||||
sequence_out = 0;
|
||||
fragment_current_bytes = 0;
|
||||
fragment_total_bytes = 0;
|
||||
outstanding_bytes = 0;
|
||||
}
|
||||
|
||||
uint16_t sequence_in;
|
||||
uint16_t sequence_out;
|
||||
std::unordered_map<uint16_t, Packet*> packet_queue;
|
||||
std::deque<DaybreakBufferedPacket> buffered_packets;
|
||||
|
||||
DynamicPacket fragment_packet;
|
||||
uint32_t fragment_current_bytes;
|
||||
uint32_t fragment_total_bytes;
|
||||
|
||||
std::unordered_map<uint16_t, DaybreakSentPacket> sent_packets;
|
||||
std::unordered_map<uint16_t, DaybreakSentPacket> outstanding_packets;
|
||||
size_t outstanding_bytes;
|
||||
};
|
||||
|
||||
DaybreakStream m_streams[4];
|
||||
@@ -170,7 +179,8 @@ namespace EQ
|
||||
|
||||
void Process();
|
||||
void ProcessPacket(Packet &p);
|
||||
void ProcessQueue();
|
||||
void ProcessInboundQueue();
|
||||
void ProcessOutboundQueue();
|
||||
void RemoveFromQueue(int stream, uint16_t seq);
|
||||
void AddToQueue(int stream, uint16_t seq, const Packet &p);
|
||||
void ProcessDecodedPacket(const Packet &p);
|
||||
@@ -186,6 +196,7 @@ namespace EQ
|
||||
void ProcessResend(int stream);
|
||||
void Ack(int stream, uint16_t seq);
|
||||
void OutOfOrderAck(int stream, uint16_t seq);
|
||||
void BufferPacket(int stream, uint16_t seq, DaybreakSentPacket &sent);
|
||||
|
||||
void SendConnect();
|
||||
void SendKeepAlive();
|
||||
@@ -206,10 +217,10 @@ namespace EQ
|
||||
DaybreakConnectionManagerOptions() {
|
||||
max_connection_count = 0;
|
||||
keepalive_delay_ms = 9000;
|
||||
resend_delay_ms = 300;
|
||||
resend_delay_ms = 150;
|
||||
resend_delay_factor = 1.5;
|
||||
resend_delay_min = 350;
|
||||
resend_delay_max = 8000;
|
||||
resend_delay_min = 300;
|
||||
resend_delay_max = 3000;
|
||||
connect_delay_ms = 500;
|
||||
stale_connection_ms = 90000;
|
||||
connect_stale_ms = 5000;
|
||||
@@ -219,12 +230,14 @@ 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;
|
||||
resend_timeout = 90000;
|
||||
connection_close_time = 2000;
|
||||
max_outstanding_packets = 400;
|
||||
max_outstanding_bytes = 400 * 512;
|
||||
}
|
||||
|
||||
size_t max_packet_size;
|
||||
@@ -247,6 +260,8 @@ namespace EQ
|
||||
size_t connection_close_time;
|
||||
DaybreakEncodeType encode_passes[2];
|
||||
int port;
|
||||
size_t max_outstanding_packets;
|
||||
size_t max_outstanding_bytes;
|
||||
};
|
||||
|
||||
class DaybreakConnectionManager
|
||||
@@ -283,4 +298,4 @@ namespace EQ
|
||||
friend class DaybreakConnection;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,6 +274,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.
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
+1
-1
@@ -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 9112
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017
|
||||
#else
|
||||
|
||||
@@ -42,6 +42,8 @@ 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;
|
||||
|
||||
@@ -237,9 +239,9 @@ function TryCriticalHit(e)
|
||||
e.hit.damage_done = (e.hit.damage_done * SlayDmgBonus * 2.25) / 100;
|
||||
|
||||
if (self:GetGender() == 1) then
|
||||
entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses her target! (%d)', self:GetCleanName(), e.hit.damage_done));
|
||||
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, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses his target! (%d)', self:GetCleanName(), e.hit.damage_done));
|
||||
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;
|
||||
@@ -325,15 +327,15 @@ function TryCriticalHit(e)
|
||||
end
|
||||
|
||||
if (crip_success) then
|
||||
entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s lands a Crippling Blow! (%d)', self:GetCleanName(), e.hit.damage_done));
|
||||
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, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a Deadly Strike! (%d)', self:GetCleanName(), e.hit.damage_done));
|
||||
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, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)', self:GetCleanName(), e.hit.damage_done));
|
||||
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
|
||||
@@ -376,11 +378,11 @@ function TryPetCriticalHit(self, defender, hit)
|
||||
|
||||
if(Random.RollReal(critChance)) then
|
||||
local entity_list = eq.get_entity_list();
|
||||
critMod = critMod + GetCritDmgMob(self, hit.skill) * 2;
|
||||
critMod = critMod + GetCritDmgMod(self, hit.skill) * 2;
|
||||
hit.damage_done = (hit.damage_done * critMod) / 100;
|
||||
entity_list:FilteredMessageClose(this, false, 200,
|
||||
entity_list:FilteredMessageClose(this, false, CriticalMessageRange,
|
||||
MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)',
|
||||
self:GetCleanName(), hit.damage_done));
|
||||
self:GetCleanName(), e.hit.damage_done));
|
||||
end
|
||||
end
|
||||
|
||||
@@ -719,7 +721,7 @@ function GetDamageTable(attacker, skill)
|
||||
|
||||
if attacker:GetClass() == 7 then
|
||||
local monkDamageTableBonus = 20;
|
||||
return (dmg_table[GetLevel() - 50] * (100 + monkDamageTableBonus) / 100);
|
||||
return (dmg_table[attacker:GetLevel() - 50] * (100 + monkDamageTableBonus) / 100);
|
||||
else
|
||||
return dmg_table[attacker:GetLevel() - 50];
|
||||
end
|
||||
@@ -749,4 +751,4 @@ function ApplyMeleeDamageBonus(e)
|
||||
|
||||
e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * dmgbonusmod / 100);
|
||||
return e;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -364,6 +364,8 @@
|
||||
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|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@@ -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`;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `saylink`
|
||||
ADD INDEX `phrase_index` (`phrase`) USING BTREE ;
|
||||
@@ -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;
|
||||
|
||||
@@ -1285,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:
|
||||
|
||||
+15
-3
@@ -53,9 +53,8 @@ extern WorldServer worldserver;
|
||||
extern EntityList entity_list;
|
||||
extern Zone* zone;
|
||||
|
||||
EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon)
|
||||
EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse)
|
||||
{
|
||||
EQEmu::skills::SkillType skillinuse = EQEmu::skills::Skill1HBlunt;
|
||||
// Determine animation
|
||||
int type = 0;
|
||||
if (weapon && weapon->IsClassCommon()) {
|
||||
@@ -1924,7 +1923,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);
|
||||
my_hit.skill = AttackAnimation(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) {
|
||||
@@ -2388,6 +2387,19 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil
|
||||
|
||||
entity_list.UnMarkNPC(GetID());
|
||||
entity_list.RemoveNPC(GetID());
|
||||
|
||||
/* Fix Z on Corpse Creation */
|
||||
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
|
||||
float new_z = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
corpse->SetFlyMode(1);
|
||||
float size = GetSize();
|
||||
if (size > 10)
|
||||
size = 10;
|
||||
|
||||
new_z += size / 2;
|
||||
|
||||
corpse->GMMove(m_Position.x, m_Position.y, new_z, m_Position.w);
|
||||
|
||||
this->SetID(0);
|
||||
|
||||
if (killer != 0 && emoteid != 0)
|
||||
|
||||
+5
-2
@@ -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,10 @@ 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);
|
||||
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -126,6 +127,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();
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ protected:
|
||||
int16 resist_adjust;
|
||||
int spell_iterations;
|
||||
Timer spell_timer;
|
||||
int max_targets;
|
||||
|
||||
uint16 caster_id;
|
||||
private:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -2368,7 +2368,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() ||
|
||||
@@ -4240,7 +4242,7 @@ bool Client::GroupFollow(Client* inviter) {
|
||||
RemoveAutoXTargets();
|
||||
}
|
||||
|
||||
SetXTargetAutoMgr(GetXTargetAutoMgr());
|
||||
SetXTargetAutoMgr(raid->GetXTargetAutoMgr());
|
||||
if (!GetXTargetAutoMgr()->empty())
|
||||
SetDirtyAutoHaters();
|
||||
|
||||
|
||||
@@ -2075,7 +2075,6 @@ void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app)
|
||||
return;
|
||||
|
||||
merchantid = tmp->CastToNPC()->MerchantType;
|
||||
tmp->CastToNPC()->FaceTarget(this->CastToMob());
|
||||
|
||||
const EQEmu::ItemData *item = nullptr;
|
||||
std::list<MerchantList> merlist = zone->merchanttable[merchantid];
|
||||
|
||||
@@ -985,8 +985,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);
|
||||
|
||||
+3
-3
@@ -8734,9 +8734,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.
|
||||
|
||||
+29
-24
@@ -694,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;
|
||||
|
||||
@@ -703,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;
|
||||
|
||||
@@ -732,13 +745,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;
|
||||
@@ -761,7 +771,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
|
||||
if (bad) {
|
||||
if (!caster->IsAttackAllowed(curmob, true))
|
||||
continue;
|
||||
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob))
|
||||
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob))
|
||||
continue;
|
||||
if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize()))
|
||||
continue;
|
||||
@@ -776,22 +786,17 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
|
||||
}
|
||||
|
||||
curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ);
|
||||
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
|
||||
|
||||
//if we get here... cast the spell.
|
||||
if (IsTargetableAESpell(spell_id) && bad) {
|
||||
if (iCounter < MAX_TARGETS_ALLOWED) {
|
||||
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
|
||||
}
|
||||
} else {
|
||||
if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets)
|
||||
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
|
||||
if (!spells[spell_id].aemaxtargets)
|
||||
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
|
||||
}
|
||||
|
||||
if (!isnpc || spells[spell_id].aemaxtargets) //npcs are not target limited (unless casting a spell with a target limit)...
|
||||
if (max_targets_allowed) { // if we have a limit, increment count
|
||||
iCounter++;
|
||||
if (iCounter >= max_targets_allowed) // we done
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_targets && max_targets_allowed)
|
||||
*max_targets = *max_targets - iCounter;
|
||||
}
|
||||
|
||||
void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -357,7 +357,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);
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ typedef enum {
|
||||
EVENT_TICK,
|
||||
EVENT_SPAWN_ZONE,
|
||||
EVENT_DEATH_ZONE,
|
||||
EVENT_USE_SKILL,
|
||||
_LargestEventID
|
||||
} QuestEventID;
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -900,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);
|
||||
}
|
||||
@@ -1658,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),
|
||||
@@ -1791,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))
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -122,7 +122,8 @@ const char *LuaEvents[_LargestEventID] = {
|
||||
"event_unhandled_opcode",
|
||||
"event_tick",
|
||||
"event_spawn_zone",
|
||||
"event_death_zone"
|
||||
"event_death_zone",
|
||||
"event_use_skill"
|
||||
};
|
||||
|
||||
extern Zone *zone;
|
||||
@@ -204,6 +205,7 @@ LuaParser::LuaParser() {
|
||||
PlayerArgumentDispatch[EVENT_LEAVE_AREA] = handle_player_area;
|
||||
PlayerArgumentDispatch[EVENT_RESPAWN] = handle_player_respawn;
|
||||
PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet;
|
||||
PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill;
|
||||
|
||||
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
|
||||
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
|
||||
|
||||
@@ -505,6 +505,15 @@ void handle_player_null(QuestInterface *parse, lua_State* L, Client* client, std
|
||||
std::vector<EQEmu::Any> *extra_pointers) {
|
||||
}
|
||||
|
||||
void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector<EQEmu::Any> *extra_pointers) {
|
||||
Seperator sep(data.c_str());
|
||||
lua_pushinteger(L, std::stoi(sep.arg[0]));
|
||||
lua_setfield(L, -2, "skill_id");
|
||||
|
||||
lua_pushinteger(L, std::stoi(sep.arg[1]));
|
||||
lua_setfield(L, -2, "skill_level");
|
||||
}
|
||||
|
||||
//Item
|
||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any> *extra_pointers) {
|
||||
|
||||
@@ -95,6 +95,8 @@ void handle_player_packet(QuestInterface *parse, lua_State* L, Client* client, s
|
||||
std::vector<EQEmu::Any> *extra_pointers);
|
||||
void handle_player_null(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any> *extra_pointers);
|
||||
void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQEmu::Any> *extra_pointers);
|
||||
|
||||
//Item
|
||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
||||
|
||||
+8
-2
@@ -112,13 +112,17 @@ Mob::Mob(const char* in_name,
|
||||
m_Position(position),
|
||||
tmHidden(-1),
|
||||
mitigation_ac(0),
|
||||
m_specialattacks(eSpecialAttacks::None)
|
||||
m_specialattacks(eSpecialAttacks::None),
|
||||
fix_z_timer(300),
|
||||
fix_z_timer_engaged(100)
|
||||
{
|
||||
targeted = 0;
|
||||
tar_ndx=0;
|
||||
tar_vector=0;
|
||||
currently_fleeing = false;
|
||||
|
||||
last_z = 0;
|
||||
|
||||
AI_Init();
|
||||
SetMoving(false);
|
||||
moved=false;
|
||||
@@ -3829,7 +3833,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
|
||||
|
||||
if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_FcSpellVulnerability))){
|
||||
|
||||
int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id);
|
||||
int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id, true);
|
||||
|
||||
if (!focus)
|
||||
continue;
|
||||
@@ -3847,6 +3851,8 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
|
||||
}
|
||||
}
|
||||
|
||||
tmp_focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id);
|
||||
|
||||
if (tmp_focus < -99)
|
||||
tmp_focus = -99;
|
||||
|
||||
|
||||
+6
-1
@@ -233,7 +233,7 @@ public:
|
||||
inline bool SeeImprovedHide() const { return see_improved_hide; }
|
||||
bool IsInvisible(Mob* other = 0) const;
|
||||
void SetInvisible(uint8 state);
|
||||
EQEmu::skills::SkillType AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon);
|
||||
EQEmu::skills::SkillType AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse = EQEmu::skills::Skill1HBlunt);
|
||||
|
||||
//Song
|
||||
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
|
||||
@@ -913,6 +913,7 @@ public:
|
||||
float GetGroundZ(float new_x, float new_y, float z_offset=0.0);
|
||||
void SendTo(float new_x, float new_y, float new_z);
|
||||
void SendToFixZ(float new_x, float new_y, float new_z);
|
||||
void FixZ();
|
||||
void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false);
|
||||
inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; }
|
||||
inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; }
|
||||
@@ -1066,6 +1067,8 @@ public:
|
||||
int GetWeaponDamage(Mob *against, const EQEmu::ItemData *weapon_item);
|
||||
int GetWeaponDamage(Mob *against, const EQEmu::ItemInstance *weapon_item, uint32 *hate = nullptr);
|
||||
|
||||
float last_z;
|
||||
|
||||
// Bots HealRotation methods
|
||||
#ifdef BOTS
|
||||
bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); }
|
||||
@@ -1373,6 +1376,8 @@ protected:
|
||||
|
||||
bool flee_mode;
|
||||
Timer flee_timer;
|
||||
Timer fix_z_timer;
|
||||
Timer fix_z_timer_engaged;
|
||||
|
||||
bool pAIControlled;
|
||||
bool roamer;
|
||||
|
||||
@@ -743,6 +743,10 @@ void Client::AI_Process()
|
||||
|
||||
if(RuleB(Combat, EnableFearPathing)){
|
||||
if(currently_fleeing) {
|
||||
|
||||
if (fix_z_timer_engaged.Check())
|
||||
this->FixZ();
|
||||
|
||||
if(IsRooted()) {
|
||||
//make sure everybody knows were not moving, for appearance sake
|
||||
if(IsMoving())
|
||||
@@ -782,6 +786,7 @@ void Client::AI_Process()
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,6 +996,12 @@ void Mob::AI_Process() {
|
||||
|
||||
if (engaged) {
|
||||
|
||||
/* Fix Z when following during pull, not when engaged and stationary */
|
||||
if (moving && fix_z_timer_engaged.Check())
|
||||
if(this->GetTarget())
|
||||
if(DistanceNoZ(this->GetPosition(), this->GetTarget()->GetPosition()) > 50)
|
||||
this->FixZ();
|
||||
|
||||
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
|
||||
SendAddPlayerState(PlayerState::Aggressive);
|
||||
// we are prevented from getting here if we are blind and don't have a target in range
|
||||
|
||||
@@ -541,6 +541,9 @@ int main(int argc, char** argv) {
|
||||
if (previous_loaded && !current_loaded) {
|
||||
process_timer.Stop();
|
||||
process_timer.Start(1000, true);
|
||||
|
||||
uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion());
|
||||
zone->StartShutdownTimer(shutdown_timer);
|
||||
}
|
||||
else if (!previous_loaded && current_loaded) {
|
||||
process_timer.Stop();
|
||||
|
||||
+18
-10
@@ -2743,20 +2743,14 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam
|
||||
if (results.RowCount() >= 1) {
|
||||
for (auto row = results.begin();row != results.end(); ++row)
|
||||
sayid = atoi(row[0]);
|
||||
} else { // Add a new saylink entry to the database and query it again for the new sayid number
|
||||
} else {
|
||||
std::string insert_query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string);
|
||||
results = database.QueryDatabase(insert_query);
|
||||
if (!results.Success()) {
|
||||
Log(Logs::General, Logs::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str());
|
||||
} else {
|
||||
results = database.QueryDatabase(query);
|
||||
if (results.Success()) {
|
||||
if (results.RowCount() >= 1)
|
||||
for(auto row = results.begin(); row != results.end(); ++row)
|
||||
sayid = atoi(row[0]);
|
||||
} else {
|
||||
Log(Logs::General, Logs::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
sayid = results.LastInsertedID();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3004,6 +2998,20 @@ void QuestManager::CrossZoneMessagePlayerByName(uint32 Type, const char *CharNam
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void QuestManager::CrossZoneSetEntityVariableByClientName(const char *CharName, const char *id, const char *m_var){
|
||||
uint32 message_len = strlen(id) + 1;
|
||||
uint32 message_len2 = strlen(m_var) + 1;
|
||||
uint32 message_len3 = strlen(CharName) + 1;
|
||||
auto pack = new ServerPacket(ServerOP_CZSetEntityVariableByClientName,
|
||||
sizeof(CZSetEntVarByClientName_Struct) + message_len + message_len2 + message_len3);
|
||||
CZSetEntVarByClientName_Struct* CZ = (CZSetEntVarByClientName_Struct*)pack->pBuffer;
|
||||
strn0cpy(CZ->CharName, CharName, 64);
|
||||
strn0cpy(CZ->id, id, 256);
|
||||
strn0cpy(CZ->m_var, m_var, 256);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void QuestManager::CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *id, const char *m_var){
|
||||
uint32 message_len = strlen(id) + 1;
|
||||
uint32 message_len2 = strlen(m_var) + 1;
|
||||
|
||||
@@ -255,6 +255,7 @@ public:
|
||||
void CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 data);
|
||||
void CrossZoneSignalPlayerByName(const char *CharName, uint32 data);
|
||||
void CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *id, const char *m_var);
|
||||
void CrossZoneSetEntityVariableByClientName(const char *CharName, const char *id, const char *m_var);
|
||||
void CrossZoneMessagePlayerByName(uint32 Type, const char *CharName, const char *Message);
|
||||
void WorldWideMarquee(uint32 Type, uint32 Priority, uint32 FadeIn, uint32 FadeOut, uint32 Duration, const char *Message);
|
||||
bool EnableRecipe(uint32 recipe_id);
|
||||
|
||||
+28
-11
@@ -277,12 +277,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
caster->SetMana(0);
|
||||
} else if (spell_id == 2755 && caster) //Lifeburn
|
||||
{
|
||||
dmg = caster->GetHP()*-15/10;
|
||||
caster->SetHP(1);
|
||||
if(caster->IsClient()){
|
||||
caster->CastToClient()->SetFeigned(true);
|
||||
caster->SendAppearancePacket(AT_Anim, 115);
|
||||
}
|
||||
dmg = -1 * caster->GetHP(); // just your current HP or should it be Max HP?
|
||||
caster->SetHP(dmg / 4); // 2003 patch notes say ~ 1/4 HP. Should this be 1/4 your current HP or do 3/4 max HP dmg? Can it kill you?
|
||||
}
|
||||
|
||||
//do any AAs apply to these spells?
|
||||
@@ -5024,8 +5020,19 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
break;
|
||||
|
||||
case SE_SpellResistReduction:
|
||||
if (type == focusResistRate && focus_spell.base[i] > value)
|
||||
value = focus_spell.base[i];
|
||||
if (type == focusResistRate) {
|
||||
if (best_focus) {
|
||||
if (focus_spell.base2[i] != 0) {
|
||||
value = focus_spell.base2[i];
|
||||
} else {
|
||||
value = focus_spell.base[i];
|
||||
}
|
||||
} else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) {
|
||||
value = focus_spell.base[i];
|
||||
} else {
|
||||
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_SpellHateMod:
|
||||
@@ -5071,8 +5078,18 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
break;
|
||||
|
||||
case SE_FcSpellVulnerability:
|
||||
if (type == focusSpellVulnerability)
|
||||
value = focus_spell.base[i];
|
||||
if (type == focusSpellVulnerability) {
|
||||
if (best_focus) {
|
||||
if (focus_spell.base2[i] != 0)
|
||||
value = focus_spell.base2[i];
|
||||
else
|
||||
value = focus_spell.base[i];
|
||||
} else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) {
|
||||
value = focus_spell.base[i];
|
||||
} else {
|
||||
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_FcTwincast:
|
||||
@@ -5319,7 +5336,7 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id)
|
||||
|
||||
//Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages
|
||||
//In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance
|
||||
if(RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage || type == focusImprovedDamage2))
|
||||
if(RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage || type == focusImprovedDamage2 || type == focusResistRate))
|
||||
rand_effectiveness = true;
|
||||
|
||||
//Check if item focus effect exists for the client.
|
||||
|
||||
+1
-15
@@ -2237,21 +2237,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
|
||||
// special ae duration spell
|
||||
ae_center->CastToBeacon()->AELocationSpell(this, spell_id, resist_adjust);
|
||||
} else {
|
||||
// regular PB AE or targeted AE spell - spell_target is null if PB
|
||||
if(spell_target) // this must be an AETarget spell
|
||||
{
|
||||
bool cast_on_target = true;
|
||||
if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && spell_target->IsPetOwnerClient())
|
||||
cast_on_target = false;
|
||||
if (spells[spell_id].targettype == ST_AreaClientOnly && !spell_target->IsClient())
|
||||
cast_on_target = false;
|
||||
if (spells[spell_id].targettype == ST_AreaNPCOnly && !spell_target->IsNPC())
|
||||
cast_on_target = false;
|
||||
|
||||
// affect the target too
|
||||
if (cast_on_target)
|
||||
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust);
|
||||
}
|
||||
// unsure if we actually need this? Need to find some spell examples
|
||||
if(ae_center && ae_center == this && IsBeneficialSpell(spell_id))
|
||||
SpellOnTarget(spell_id, this);
|
||||
|
||||
|
||||
+58
-101
@@ -513,39 +513,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
|
||||
m_Position.y = new_y;
|
||||
m_Position.z = new_z;
|
||||
|
||||
uint8 NPCFlyMode = 0;
|
||||
|
||||
if (IsNPC()) {
|
||||
if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2)
|
||||
NPCFlyMode = 1;
|
||||
}
|
||||
|
||||
//fix up pathing Z
|
||||
if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving))
|
||||
{
|
||||
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
|
||||
{
|
||||
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
|
||||
|
||||
if ((newz > -2000) &&
|
||||
std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
|
||||
{
|
||||
if ((std::abs(x - m_Position.x) < 0.5) &&
|
||||
(std::abs(y - m_Position.y) < 0.5)) {
|
||||
if (std::abs(z - m_Position.z) <=
|
||||
RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
m_Position.z = z;
|
||||
else
|
||||
m_Position.z = newz + 1;
|
||||
}
|
||||
else
|
||||
m_Position.z = newz + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(fix_z_timer.Check())
|
||||
this->FixZ();
|
||||
|
||||
tar_ndx++;
|
||||
return true;
|
||||
@@ -651,37 +620,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
|
||||
m_Position.w = CalculateHeadingToTarget(x, y);
|
||||
}
|
||||
|
||||
uint8 NPCFlyMode = 0;
|
||||
|
||||
if (IsNPC()) {
|
||||
if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2)
|
||||
NPCFlyMode = 1;
|
||||
}
|
||||
|
||||
//fix up pathing Z
|
||||
if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) {
|
||||
|
||||
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
|
||||
{
|
||||
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
if ((newz > -2000) &&
|
||||
std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
|
||||
{
|
||||
if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) {
|
||||
if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
m_Position.z = z;
|
||||
else
|
||||
m_Position.z = newz + 1;
|
||||
}
|
||||
else
|
||||
m_Position.z = newz + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fix_z_timer.Check())
|
||||
this->FixZ();
|
||||
|
||||
SetMoving(true);
|
||||
moved = true;
|
||||
@@ -769,39 +709,8 @@ bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ
|
||||
Log(Logs::Detail, Logs::AI, "Next position (%.3f, %.3f, %.3f)", m_Position.x, m_Position.y, m_Position.z);
|
||||
}
|
||||
|
||||
uint8 NPCFlyMode = 0;
|
||||
|
||||
if (IsNPC()) {
|
||||
if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2)
|
||||
NPCFlyMode = 1;
|
||||
}
|
||||
|
||||
//fix up pathing Z
|
||||
if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving))
|
||||
{
|
||||
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
|
||||
{
|
||||
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
|
||||
|
||||
Log(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z);
|
||||
|
||||
if ((newz > -2000) &&
|
||||
std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
|
||||
{
|
||||
if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) {
|
||||
if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
m_Position.z = z;
|
||||
else
|
||||
m_Position.z = newz + 1;
|
||||
}
|
||||
else
|
||||
m_Position.z = newz + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fix_z_timer.Check())
|
||||
this->FixZ();
|
||||
|
||||
//OP_MobUpdate
|
||||
if ((old_test_vector != test_vector) || tar_ndx>20) { //send update
|
||||
@@ -943,9 +852,6 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
m_Position.y = new_y;
|
||||
m_Position.z = new_z + 0.1;
|
||||
|
||||
//fix up pathing Z, this shouldent be needed IF our waypoints
|
||||
//are corrected instead
|
||||
|
||||
if (zone->HasMap() && RuleB(Map, FixPathingZOnSendTo))
|
||||
{
|
||||
if (!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() ||
|
||||
@@ -955,7 +861,7 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
Log(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z);
|
||||
Log(Logs::Moderate, Logs::Pathing, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z);
|
||||
|
||||
if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check.
|
||||
m_Position.z = newz + 1;
|
||||
@@ -963,6 +869,57 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::FixZ() {
|
||||
|
||||
BenchTimer timer;
|
||||
timer.reset();
|
||||
|
||||
if (zone->HasMap() && RuleB(Map, FixZWhenMoving) && (flymode != 1 && flymode != 2))
|
||||
{
|
||||
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
|
||||
{
|
||||
|
||||
float new_z = this->FindGroundZ(m_Position.x, m_Position.y, 10);
|
||||
|
||||
auto duration = timer.elapsed();
|
||||
|
||||
Log(
|
||||
Logs::Moderate,
|
||||
Logs::Pathing,
|
||||
"Mob::FixZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf",
|
||||
this->GetCleanName(),
|
||||
new_z,
|
||||
m_Position.x,
|
||||
m_Position.y,
|
||||
m_Position.z,
|
||||
duration
|
||||
);
|
||||
|
||||
float size = GetSize();
|
||||
if (size > 10)
|
||||
size = 10;
|
||||
|
||||
new_z += size / 2;
|
||||
|
||||
if ((new_z > -2000) && std::abs(m_Position.z - new_z) < 35) {
|
||||
if (RuleB(Map, MobZVisualDebug))
|
||||
this->SendAppearanceEffect(78, 0, 0, 0, 0);
|
||||
|
||||
m_Position.z = new_z;
|
||||
}
|
||||
else {
|
||||
if (RuleB(Map, MobZVisualDebug))
|
||||
this->SendAppearanceEffect(103, 0, 0, 0, 0);
|
||||
|
||||
Log(Logs::General, Logs::Debug, "%s is failing to find Z %f", this->GetCleanName(), std::abs(m_Position.z - new_z));
|
||||
}
|
||||
|
||||
last_z = m_Position.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
|
||||
|
||||
std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid);
|
||||
|
||||
@@ -1857,6 +1857,15 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_CZSetEntityVariableByClientName:
|
||||
{
|
||||
CZSetEntVarByClientName_Struct* CZCS = (CZSetEntVarByClientName_Struct*)pack->pBuffer;
|
||||
Client* client = entity_list.GetClientByName(CZCS->CharName);
|
||||
if (client != 0) {
|
||||
client->SetEntityVariable(CZCS->id, CZCS->m_var);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_WWMarquee:
|
||||
{
|
||||
WWMarquee_Struct* WWMS = (WWMarquee_Struct*)pack->pBuffer;
|
||||
|
||||
+3
-3
@@ -1414,11 +1414,11 @@ bool Zone::HasWeather()
|
||||
|
||||
void Zone::StartShutdownTimer(uint32 set_time) {
|
||||
if (set_time > autoshutdown_timer.GetRemainingTime()) {
|
||||
if (set_time == (RuleI(Zone, AutoShutdownDelay)))
|
||||
{
|
||||
if (set_time == (RuleI(Zone, AutoShutdownDelay))) {
|
||||
set_time = database.getZoneShutDownDelay(GetZoneID(), GetInstanceVersion());
|
||||
}
|
||||
autoshutdown_timer.Start(set_time, false);
|
||||
autoshutdown_timer.SetTimer(set_time);
|
||||
Log(Logs::General, Logs::Zone_Server, "Zone::StartShutdownTimer set to %u", set_time);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user