mirror of
https://github.com/EQEmu/Server.git
synced 2026-02-17 01:22:25 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
8048239a81
@ -1,5 +1,34 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 08/14/2016 ==
|
||||
mackal: Implement Linked Spell Reuse Timers
|
||||
- For whatever reason this is a bit unfriendly, but that's how it is on live.
|
||||
- Titanium is especially unfriendly with large differences in reuse times (ex higher canni and the first 4)
|
||||
- Unsure when this went live for spells, but canni was at least linked at OoW launch
|
||||
|
||||
== 08/13/2016 ==
|
||||
Kinglykrab: Implemented optional avoidance cap rules.
|
||||
- Serves to eliminate God-like characters on custom servers with high item stats
|
||||
- Rule Names:
|
||||
- Character:EnableAvoidanceCap (default is false)
|
||||
- Character:AvoidanceCap (default is 750, beyond 1,000 seems to make characters dodge all attacks)
|
||||
|
||||
== 08/02/2016 ==
|
||||
Uleat: Changed 'SendZoneSpawnsBulk' behavior to use near/far criteria (live-like) when sending packets.
|
||||
- Zone-to-Zone client loading will see a small decrease in time (less than 10~15%)
|
||||
- World-to-Zone client loading appears to greatly benefit from this (tested 'devastation' - pre-change: ~22 seconds, post-change: 12~15 seconds)
|
||||
- This change does not affect the final spawning of mobs in the client
|
||||
|
||||
== 07/31/2016 ==
|
||||
mackal: Implement more spell gems!
|
||||
- There are a few things still left to due like make dealing with losing gems nice (reset AAs, going to an older client etc)
|
||||
- Sadly SoF disc release doesn't support gem 10 like one might expect :(
|
||||
- So changed clients:
|
||||
- SoD = 10
|
||||
- UF = 12
|
||||
- RoF/RoF2 = 12. I know the UI supports 16, but the client does not and can cause client crashes
|
||||
- The quest APIs assume you pass a valid spell gem ...
|
||||
|
||||
== 07/28/2016 ==
|
||||
Uleat: Implemented zone memory-mapped file usage
|
||||
- Zone map files are converted to pre-loaded binary files, bypassing the (sometimes) time-consuming raw data transform process
|
||||
|
||||
@ -44,6 +44,26 @@ namespace EQEmu
|
||||
const size_t SayLinkBodySize = RoF2::constants::SayLinkBodySize;
|
||||
|
||||
} /*constants*/
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
Gem3 = 2,
|
||||
Gem4 = 3,
|
||||
Gem5 = 4,
|
||||
Gem6 = 5,
|
||||
Gem7 = 6,
|
||||
Gem8 = 7,
|
||||
Gem9 = 8,
|
||||
Gem10 = 9,
|
||||
Gem11 = 10,
|
||||
Gem12 = 11,
|
||||
MaxGems = 12,
|
||||
Ability = 20, // HT/LoH for Tit
|
||||
PotionBelt = 21, // Tit uses a different slot for PB
|
||||
Item = 22,
|
||||
Discipline = 23,
|
||||
AltAbility = 0xFF
|
||||
};
|
||||
|
||||
} /*EQEmu*/
|
||||
|
||||
|
||||
@ -289,6 +289,7 @@ N(OP_LFGuild),
|
||||
N(OP_LFPCommand),
|
||||
N(OP_LFPGetMatchesRequest),
|
||||
N(OP_LFPGetMatchesResponse),
|
||||
N(OP_LinkedReuse),
|
||||
N(OP_LoadSpellSet),
|
||||
N(OP_LocInfo),
|
||||
N(OP_LockoutTimerInfo),
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
static const uint32 BUFF_COUNT = 25;
|
||||
static const uint32 PET_BUFF_COUNT = 30;
|
||||
static const uint32 MAX_MERC = 100;
|
||||
static const uint32 MAX_MERC_GRADES = 10;
|
||||
static const uint32 MAX_MERC_STANCES = 10;
|
||||
@ -386,7 +387,19 @@ struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 unknown12;
|
||||
uint32 reduction; // lower reuse
|
||||
};
|
||||
|
||||
/*
|
||||
** Linked Spell Reuse Timer
|
||||
** Length: 12
|
||||
** Comes before the OP_Memorize
|
||||
** Live (maybe TDS steam) has an extra DWORD after timer_id
|
||||
*/
|
||||
struct LinkedSpellReuseTimer_Struct {
|
||||
uint32 timer_id; // Timer ID of the spell
|
||||
uint32 end_time; // timestamp of when it will be ready
|
||||
uint32 start_time; // timestamp of when it started
|
||||
};
|
||||
|
||||
/*
|
||||
@ -419,10 +432,11 @@ struct DeleteSpell_Struct
|
||||
|
||||
struct ManaChange_Struct
|
||||
{
|
||||
uint32 new_mana; // New Mana AMount
|
||||
uint32 stamina;
|
||||
uint32 spell_id;
|
||||
uint32 unknown12;
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
};
|
||||
|
||||
struct SwapSpell_Struct
|
||||
@ -522,8 +536,8 @@ struct BuffRemoveRequest_Struct
|
||||
|
||||
struct PetBuff_Struct {
|
||||
/*000*/ uint32 petid;
|
||||
/*004*/ uint32 spellid[BUFF_COUNT+5];
|
||||
/*124*/ int32 ticsremaining[BUFF_COUNT+5];
|
||||
/*004*/ uint32 spellid[PET_BUFF_COUNT];
|
||||
/*124*/ int32 ticsremaining[PET_BUFF_COUNT];
|
||||
/*244*/ uint32 buffcount;
|
||||
};
|
||||
|
||||
@ -834,7 +848,7 @@ struct SuspendedMinion_Struct
|
||||
*/
|
||||
static const uint32 MAX_PP_LANGUAGE = 28;
|
||||
static const uint32 MAX_PP_SPELLBOOK = 480; // Set for all functions
|
||||
static const uint32 MAX_PP_MEMSPELL = 9; // Set to latest client so functions can work right
|
||||
static const uint32 MAX_PP_MEMSPELL = static_cast<uint32>(EQEmu::CastingSlot::MaxGems); // Set to latest client so functions can work right -- 12
|
||||
static const uint32 MAX_PP_REF_SPELLBOOK = 480; // Set for Player Profile size retain
|
||||
static const uint32 MAX_PP_REF_MEMSPELL = 9; // Set for Player Profile size retain
|
||||
|
||||
@ -915,7 +929,7 @@ struct PlayerProfile_Struct
|
||||
/*0245*/ uint8 guildbanker;
|
||||
/*0246*/ uint8 unknown0246[6]; //
|
||||
/*0252*/ uint32 intoxication;
|
||||
/*0256*/ uint32 spellSlotRefresh[MAX_PP_REF_MEMSPELL]; //in ms
|
||||
/*0256*/ uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; //in ms
|
||||
/*0292*/ uint32 abilitySlotRefresh;
|
||||
/*0296*/ uint8 haircolor; // Player hair color
|
||||
/*0297*/ uint8 beardcolor; // Player beard color
|
||||
@ -956,7 +970,7 @@ struct PlayerProfile_Struct
|
||||
/*2580*/ uint8 unknown2616[4];
|
||||
/*2584*/ uint32 spell_book[MAX_PP_REF_SPELLBOOK];
|
||||
/*4504*/ uint8 unknown4540[128]; // Was [428] all 0xff
|
||||
/*4632*/ uint32 mem_spells[MAX_PP_REF_MEMSPELL];
|
||||
/*4632*/ uint32 mem_spells[MAX_PP_MEMSPELL];
|
||||
/*4668*/ uint8 unknown4704[32]; //
|
||||
/*4700*/ float y; // Player y position
|
||||
/*4704*/ float x; // Player x position
|
||||
|
||||
@ -1863,6 +1863,9 @@ uint8 ItemInst::FirstOpenSlot() const
|
||||
|
||||
uint8 ItemInst::GetTotalItemCount() const
|
||||
{
|
||||
if (!m_item)
|
||||
return 0;
|
||||
|
||||
uint8 item_count = 1;
|
||||
|
||||
if (m_item && !m_item->IsClassBag()) { return item_count; }
|
||||
|
||||
@ -63,6 +63,9 @@ namespace RoF
|
||||
// client to server text link converter
|
||||
static inline void RoFToServerTextLink(std::string& serverTextLink, const std::string& rofTextLink);
|
||||
|
||||
static inline CastingSlot ServerToRoFCastingSlot(EQEmu::CastingSlot slot);
|
||||
static inline EQEmu::CastingSlot RoFToServerCastingSlot(CastingSlot slot);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
@ -507,10 +510,7 @@ namespace RoF
|
||||
ENCODE_LENGTH_EXACT(CastSpell_Struct);
|
||||
SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct);
|
||||
|
||||
if (emu->slot == 10)
|
||||
eq->slot = 13;
|
||||
else
|
||||
OUT(slot);
|
||||
eq->slot = static_cast<uint32>(ServerToRoFCastingSlot(static_cast<EQEmu::CastingSlot>(emu->slot)));
|
||||
|
||||
OUT(spell_id);
|
||||
eq->inventory_slot = ServerToRoFSlot(emu->inventoryslot);
|
||||
@ -1623,7 +1623,8 @@ namespace RoF
|
||||
OUT(new_mana);
|
||||
OUT(stamina);
|
||||
OUT(spell_id);
|
||||
eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2?
|
||||
OUT(keepcasting);
|
||||
eq->slot = -1; // this is spell gem slot. It's -1 in normal operation
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@ -2012,18 +2013,18 @@ namespace RoF
|
||||
__packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff)
|
||||
__packet->WriteUInt16(emu->buffcount);
|
||||
|
||||
for (uint16 i = 0; i < BUFF_COUNT; ++i)
|
||||
for (uint16 i = 0; i < PET_BUFF_COUNT; ++i)
|
||||
{
|
||||
if (emu->spellid[i])
|
||||
{
|
||||
__packet->WriteUInt32(i);
|
||||
__packet->WriteUInt32(emu->spellid[i]);
|
||||
__packet->WriteUInt32(emu->ticsremaining[i]);
|
||||
__packet->WriteUInt32(0); // Unknown
|
||||
__packet->WriteUInt32(0); // numhits
|
||||
__packet->WriteString("");
|
||||
}
|
||||
}
|
||||
__packet->WriteUInt8(0); // Unknown
|
||||
__packet->WriteUInt8(0); // some sort of type
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@ -2259,22 +2260,23 @@ namespace RoF
|
||||
|
||||
outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots
|
||||
|
||||
for (uint32 r = 0; r < MAX_PP_MEMSPELL; r++)
|
||||
for (uint32 r = 0; r < MAX_PP_MEMSPELL; r++) // first 12
|
||||
{
|
||||
outapp->WriteUInt32(emu->mem_spells[r]);
|
||||
}
|
||||
// zeroes for the rest of the slots
|
||||
// zeroes for the rest of the slots -- the other 4 which don't work at all!
|
||||
for (uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++)
|
||||
{
|
||||
outapp->WriteUInt32(0xFFFFFFFFU);
|
||||
}
|
||||
|
||||
outapp->WriteUInt32(13); // Unknown count
|
||||
outapp->WriteUInt32(13); // gem refresh count
|
||||
|
||||
for (uint32 r = 0; r < 13; r++)
|
||||
for (uint32 r = 0; r < MAX_PP_MEMSPELL; r++)
|
||||
{
|
||||
outapp->WriteUInt32(0); // Unknown
|
||||
outapp->WriteUInt32(emu->spellSlotRefresh[r]); // spell gem refresh
|
||||
}
|
||||
outapp->WriteUInt32(0); // also refresh -- historically HT/LoH :P
|
||||
|
||||
outapp->WriteUInt8(0); // Unknown
|
||||
|
||||
@ -4334,10 +4336,7 @@ namespace RoF
|
||||
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
||||
|
||||
if (eq->slot == 13)
|
||||
emu->slot = 10;
|
||||
else
|
||||
IN(slot);
|
||||
emu->slot = static_cast<uint32>(RoFToServerCastingSlot(static_cast<CastingSlot>(eq->slot)));
|
||||
|
||||
IN(spell_id);
|
||||
emu->inventoryslot = RoFToServerSlot(eq->inventory_slot);
|
||||
@ -6009,4 +6008,80 @@ namespace RoF
|
||||
}
|
||||
}
|
||||
|
||||
static inline CastingSlot ServerToRoFCastingSlot(EQEmu::CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case EQEmu::CastingSlot::Gem1:
|
||||
return CastingSlot::Gem1;
|
||||
case EQEmu::CastingSlot::Gem2:
|
||||
return CastingSlot::Gem2;
|
||||
case EQEmu::CastingSlot::Gem3:
|
||||
return CastingSlot::Gem3;
|
||||
case EQEmu::CastingSlot::Gem4:
|
||||
return CastingSlot::Gem4;
|
||||
case EQEmu::CastingSlot::Gem5:
|
||||
return CastingSlot::Gem5;
|
||||
case EQEmu::CastingSlot::Gem6:
|
||||
return CastingSlot::Gem6;
|
||||
case EQEmu::CastingSlot::Gem7:
|
||||
return CastingSlot::Gem7;
|
||||
case EQEmu::CastingSlot::Gem8:
|
||||
return CastingSlot::Gem8;
|
||||
case EQEmu::CastingSlot::Gem9:
|
||||
return CastingSlot::Gem9;
|
||||
case EQEmu::CastingSlot::Gem10:
|
||||
return CastingSlot::Gem10;
|
||||
case EQEmu::CastingSlot::Gem11:
|
||||
return CastingSlot::Gem11;
|
||||
case EQEmu::CastingSlot::Gem12:
|
||||
return CastingSlot::Gem12;
|
||||
case EQEmu::CastingSlot::Item:
|
||||
case EQEmu::CastingSlot::PotionBelt:
|
||||
return CastingSlot::Item;
|
||||
case EQEmu::CastingSlot::Discipline:
|
||||
return CastingSlot::Discipline;
|
||||
case EQEmu::CastingSlot::AltAbility:
|
||||
return CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
|
||||
static inline EQEmu::CastingSlot RoFToServerCastingSlot(CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case CastingSlot::Gem1:
|
||||
return EQEmu::CastingSlot::Gem1;
|
||||
case CastingSlot::Gem2:
|
||||
return EQEmu::CastingSlot::Gem2;
|
||||
case CastingSlot::Gem3:
|
||||
return EQEmu::CastingSlot::Gem3;
|
||||
case CastingSlot::Gem4:
|
||||
return EQEmu::CastingSlot::Gem4;
|
||||
case CastingSlot::Gem5:
|
||||
return EQEmu::CastingSlot::Gem5;
|
||||
case CastingSlot::Gem6:
|
||||
return EQEmu::CastingSlot::Gem6;
|
||||
case CastingSlot::Gem7:
|
||||
return EQEmu::CastingSlot::Gem7;
|
||||
case CastingSlot::Gem8:
|
||||
return EQEmu::CastingSlot::Gem8;
|
||||
case CastingSlot::Gem9:
|
||||
return EQEmu::CastingSlot::Gem9;
|
||||
case CastingSlot::Gem10:
|
||||
return EQEmu::CastingSlot::Gem10;
|
||||
case CastingSlot::Gem11:
|
||||
return EQEmu::CastingSlot::Gem11;
|
||||
case CastingSlot::Gem12:
|
||||
return EQEmu::CastingSlot::Gem12;
|
||||
case CastingSlot::Discipline:
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
case CastingSlot::Item:
|
||||
return EQEmu::CastingSlot::Item;
|
||||
case CastingSlot::AltAbility:
|
||||
return EQEmu::CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
} /*RoF*/
|
||||
|
||||
@ -50,6 +50,24 @@ namespace RoF
|
||||
#include "rof_ops.h"
|
||||
};
|
||||
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
Gem3 = 2,
|
||||
Gem4 = 3,
|
||||
Gem5 = 4,
|
||||
Gem6 = 5,
|
||||
Gem7 = 6,
|
||||
Gem8 = 7,
|
||||
Gem9 = 8,
|
||||
Gem10 = 9,
|
||||
Gem11 = 10,
|
||||
Gem12 = 11,
|
||||
Item = 12,
|
||||
Discipline = 13,
|
||||
AltAbility = 0xFF
|
||||
};
|
||||
|
||||
}; /*RoF*/
|
||||
|
||||
#endif /*COMMON_ROF_H*/
|
||||
|
||||
@ -63,6 +63,9 @@ namespace RoF2
|
||||
// client to server text link converter
|
||||
static inline void RoF2ToServerTextLink(std::string& serverTextLink, const std::string& rof2TextLink);
|
||||
|
||||
static inline CastingSlot ServerToRoF2CastingSlot(EQEmu::CastingSlot slot);
|
||||
static inline EQEmu::CastingSlot RoF2ToServerCastingSlot(CastingSlot slot);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
@ -584,10 +587,7 @@ namespace RoF2
|
||||
ENCODE_LENGTH_EXACT(CastSpell_Struct);
|
||||
SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct);
|
||||
|
||||
if (emu->slot == 10)
|
||||
eq->slot = 13;
|
||||
else
|
||||
OUT(slot);
|
||||
eq->slot = static_cast<uint32>(ServerToRoF2CastingSlot(static_cast<EQEmu::CastingSlot>(emu->slot)));
|
||||
|
||||
OUT(spell_id);
|
||||
eq->inventory_slot = ServerToRoF2Slot(emu->inventoryslot);
|
||||
@ -1701,7 +1701,8 @@ namespace RoF2
|
||||
OUT(new_mana);
|
||||
OUT(stamina);
|
||||
OUT(spell_id);
|
||||
eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2?
|
||||
OUT(keepcasting);
|
||||
eq->slot = -1; // this is spell gem slot. It's -1 in normal operation
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@ -2099,18 +2100,18 @@ namespace RoF2
|
||||
__packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff)
|
||||
__packet->WriteUInt16(emu->buffcount);
|
||||
|
||||
for (uint16 i = 0; i < BUFF_COUNT; ++i)
|
||||
for (uint16 i = 0; i < PET_BUFF_COUNT; ++i)
|
||||
{
|
||||
if (emu->spellid[i])
|
||||
{
|
||||
__packet->WriteUInt32(i);
|
||||
__packet->WriteUInt32(emu->spellid[i]);
|
||||
__packet->WriteUInt32(emu->ticsremaining[i]);
|
||||
__packet->WriteUInt32(0); // Unknown
|
||||
__packet->WriteUInt32(0); // num hits
|
||||
__packet->WriteString("");
|
||||
}
|
||||
}
|
||||
__packet->WriteUInt8(0); // Unknown
|
||||
__packet->WriteUInt8(0); // some sort of type
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@ -2346,22 +2347,23 @@ namespace RoF2
|
||||
|
||||
outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots
|
||||
|
||||
for (uint32 r = 0; r < MAX_PP_MEMSPELL; r++)
|
||||
for (uint32 r = 0; r < MAX_PP_MEMSPELL; r++) // write first 12
|
||||
{
|
||||
outapp->WriteUInt32(emu->mem_spells[r]);
|
||||
}
|
||||
// zeroes for the rest of the slots
|
||||
// zeroes for the rest of the slots the other 4, which actually don't work on the client at all :D
|
||||
for (uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++)
|
||||
{
|
||||
outapp->WriteUInt32(0xFFFFFFFFU);
|
||||
}
|
||||
|
||||
outapp->WriteUInt32(13); // Unknown count
|
||||
outapp->WriteUInt32(13); // gem refresh counts
|
||||
|
||||
for (uint32 r = 0; r < 13; r++)
|
||||
for (uint32 r = 0; r < MAX_PP_MEMSPELL; r++)
|
||||
{
|
||||
outapp->WriteUInt32(0); // Unknown
|
||||
outapp->WriteUInt32(emu->spellSlotRefresh[r]); // spell gem refresh
|
||||
}
|
||||
outapp->WriteUInt32(0); // also refresh -- historically HT/LoH :P
|
||||
|
||||
outapp->WriteUInt8(0); // Unknown
|
||||
|
||||
@ -4574,10 +4576,7 @@ namespace RoF2
|
||||
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
||||
|
||||
if (eq->slot == 13)
|
||||
emu->slot = 10;
|
||||
else
|
||||
IN(slot);
|
||||
emu->slot = static_cast<uint32>(RoF2ToServerCastingSlot(static_cast<CastingSlot>(eq->slot)));
|
||||
|
||||
IN(spell_id);
|
||||
emu->inventoryslot = RoF2ToServerSlot(eq->inventory_slot);
|
||||
@ -6314,4 +6313,80 @@ namespace RoF2
|
||||
}
|
||||
}
|
||||
|
||||
static inline CastingSlot ServerToRoF2CastingSlot(EQEmu::CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case EQEmu::CastingSlot::Gem1:
|
||||
return CastingSlot::Gem1;
|
||||
case EQEmu::CastingSlot::Gem2:
|
||||
return CastingSlot::Gem2;
|
||||
case EQEmu::CastingSlot::Gem3:
|
||||
return CastingSlot::Gem3;
|
||||
case EQEmu::CastingSlot::Gem4:
|
||||
return CastingSlot::Gem4;
|
||||
case EQEmu::CastingSlot::Gem5:
|
||||
return CastingSlot::Gem5;
|
||||
case EQEmu::CastingSlot::Gem6:
|
||||
return CastingSlot::Gem6;
|
||||
case EQEmu::CastingSlot::Gem7:
|
||||
return CastingSlot::Gem7;
|
||||
case EQEmu::CastingSlot::Gem8:
|
||||
return CastingSlot::Gem8;
|
||||
case EQEmu::CastingSlot::Gem9:
|
||||
return CastingSlot::Gem9;
|
||||
case EQEmu::CastingSlot::Gem10:
|
||||
return CastingSlot::Gem10;
|
||||
case EQEmu::CastingSlot::Gem11:
|
||||
return CastingSlot::Gem11;
|
||||
case EQEmu::CastingSlot::Gem12:
|
||||
return CastingSlot::Gem12;
|
||||
case EQEmu::CastingSlot::Item:
|
||||
case EQEmu::CastingSlot::PotionBelt:
|
||||
return CastingSlot::Item;
|
||||
case EQEmu::CastingSlot::Discipline:
|
||||
return CastingSlot::Discipline;
|
||||
case EQEmu::CastingSlot::AltAbility:
|
||||
return CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
|
||||
static inline EQEmu::CastingSlot RoF2ToServerCastingSlot(CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case CastingSlot::Gem1:
|
||||
return EQEmu::CastingSlot::Gem1;
|
||||
case CastingSlot::Gem2:
|
||||
return EQEmu::CastingSlot::Gem2;
|
||||
case CastingSlot::Gem3:
|
||||
return EQEmu::CastingSlot::Gem3;
|
||||
case CastingSlot::Gem4:
|
||||
return EQEmu::CastingSlot::Gem4;
|
||||
case CastingSlot::Gem5:
|
||||
return EQEmu::CastingSlot::Gem5;
|
||||
case CastingSlot::Gem6:
|
||||
return EQEmu::CastingSlot::Gem6;
|
||||
case CastingSlot::Gem7:
|
||||
return EQEmu::CastingSlot::Gem7;
|
||||
case CastingSlot::Gem8:
|
||||
return EQEmu::CastingSlot::Gem8;
|
||||
case CastingSlot::Gem9:
|
||||
return EQEmu::CastingSlot::Gem9;
|
||||
case CastingSlot::Gem10:
|
||||
return EQEmu::CastingSlot::Gem10;
|
||||
case CastingSlot::Gem11:
|
||||
return EQEmu::CastingSlot::Gem11;
|
||||
case CastingSlot::Gem12:
|
||||
return EQEmu::CastingSlot::Gem12;
|
||||
case CastingSlot::Discipline:
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
case CastingSlot::Item:
|
||||
return EQEmu::CastingSlot::Item;
|
||||
case CastingSlot::AltAbility:
|
||||
return EQEmu::CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
} /*RoF2*/
|
||||
|
||||
@ -50,6 +50,24 @@ namespace RoF2
|
||||
#include "rof2_ops.h"
|
||||
};
|
||||
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
Gem3 = 2,
|
||||
Gem4 = 3,
|
||||
Gem5 = 4,
|
||||
Gem6 = 5,
|
||||
Gem7 = 6,
|
||||
Gem8 = 7,
|
||||
Gem9 = 8,
|
||||
Gem10 = 9,
|
||||
Gem11 = 10,
|
||||
Gem12 = 11,
|
||||
Item = 12,
|
||||
Discipline = 13,
|
||||
AltAbility = 0xFF
|
||||
};
|
||||
|
||||
}; /*RoF2*/
|
||||
|
||||
#endif /*COMMON_ROF2_H*/
|
||||
|
||||
@ -635,7 +635,7 @@ struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 unknown12;
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
|
||||
/*
|
||||
@ -668,11 +668,12 @@ struct DeleteSpell_Struct
|
||||
|
||||
struct ManaChange_Struct
|
||||
{
|
||||
uint32 new_mana; // New Mana AMount
|
||||
uint32 stamina;
|
||||
uint32 spell_id;
|
||||
uint32 unknown12;
|
||||
uint32 unknown16;
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
};
|
||||
|
||||
struct SwapSpell_Struct
|
||||
|
||||
@ -624,7 +624,7 @@ struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 unknown12;
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
|
||||
/*
|
||||
@ -657,11 +657,12 @@ struct DeleteSpell_Struct
|
||||
|
||||
struct ManaChange_Struct
|
||||
{
|
||||
uint32 new_mana; // New Mana AMount
|
||||
uint32 stamina;
|
||||
uint32 spell_id;
|
||||
uint32 unknown12;
|
||||
uint32 unknown16;
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
};
|
||||
|
||||
struct SwapSpell_Struct
|
||||
|
||||
@ -59,6 +59,9 @@ namespace SoD
|
||||
// client to server text link converter
|
||||
static inline void SoDToServerTextLink(std::string& serverTextLink, const std::string& sodTextLink);
|
||||
|
||||
static inline CastingSlot ServerToSoDCastingSlot(EQEmu::CastingSlot slot);
|
||||
static inline EQEmu::CastingSlot SoDToServerCastingSlot(CastingSlot slot);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
@ -1136,7 +1139,8 @@ namespace SoD
|
||||
OUT(new_mana);
|
||||
OUT(stamina);
|
||||
OUT(spell_id);
|
||||
eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2?
|
||||
OUT(keepcasting);
|
||||
eq->slot = -1; // this is spell gem slot. It's -1 in normal operation
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@ -1499,7 +1503,7 @@ namespace SoD
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid);
|
||||
VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount);
|
||||
|
||||
for (unsigned int i = 0; i < BUFF_COUNT; ++i)
|
||||
for (unsigned int i = 0; i < PET_BUFF_COUNT; ++i)
|
||||
{
|
||||
if (emu->spellid[i])
|
||||
{
|
||||
@ -1510,7 +1514,7 @@ namespace SoD
|
||||
}
|
||||
}
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); // I think this is actually some sort of type
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
@ -2948,6 +2952,7 @@ namespace SoD
|
||||
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
||||
|
||||
emu->slot = static_cast<uint32>(SoDToServerCastingSlot(static_cast<CastingSlot>(eq->slot)));
|
||||
IN(slot);
|
||||
IN(spell_id);
|
||||
emu->inventoryslot = SoDToServerSlot(eq->inventoryslot);
|
||||
@ -4013,4 +4018,72 @@ namespace SoD
|
||||
}
|
||||
}
|
||||
|
||||
static inline CastingSlot ServerToSoDCastingSlot(EQEmu::CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case EQEmu::CastingSlot::Gem1:
|
||||
return CastingSlot::Gem1;
|
||||
case EQEmu::CastingSlot::Gem2:
|
||||
return CastingSlot::Gem2;
|
||||
case EQEmu::CastingSlot::Gem3:
|
||||
return CastingSlot::Gem3;
|
||||
case EQEmu::CastingSlot::Gem4:
|
||||
return CastingSlot::Gem4;
|
||||
case EQEmu::CastingSlot::Gem5:
|
||||
return CastingSlot::Gem5;
|
||||
case EQEmu::CastingSlot::Gem6:
|
||||
return CastingSlot::Gem6;
|
||||
case EQEmu::CastingSlot::Gem7:
|
||||
return CastingSlot::Gem7;
|
||||
case EQEmu::CastingSlot::Gem8:
|
||||
return CastingSlot::Gem8;
|
||||
case EQEmu::CastingSlot::Gem9:
|
||||
return CastingSlot::Gem9;
|
||||
case EQEmu::CastingSlot::Gem10:
|
||||
return CastingSlot::Gem10;
|
||||
case EQEmu::CastingSlot::Item:
|
||||
case EQEmu::CastingSlot::PotionBelt:
|
||||
return CastingSlot::Item;
|
||||
case EQEmu::CastingSlot::Discipline:
|
||||
return CastingSlot::Discipline;
|
||||
case EQEmu::CastingSlot::AltAbility:
|
||||
return CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
|
||||
static inline EQEmu::CastingSlot SoDToServerCastingSlot(CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case CastingSlot::Gem1:
|
||||
return EQEmu::CastingSlot::Gem1;
|
||||
case CastingSlot::Gem2:
|
||||
return EQEmu::CastingSlot::Gem2;
|
||||
case CastingSlot::Gem3:
|
||||
return EQEmu::CastingSlot::Gem3;
|
||||
case CastingSlot::Gem4:
|
||||
return EQEmu::CastingSlot::Gem4;
|
||||
case CastingSlot::Gem5:
|
||||
return EQEmu::CastingSlot::Gem5;
|
||||
case CastingSlot::Gem6:
|
||||
return EQEmu::CastingSlot::Gem6;
|
||||
case CastingSlot::Gem7:
|
||||
return EQEmu::CastingSlot::Gem7;
|
||||
case CastingSlot::Gem8:
|
||||
return EQEmu::CastingSlot::Gem8;
|
||||
case CastingSlot::Gem9:
|
||||
return EQEmu::CastingSlot::Gem9;
|
||||
case CastingSlot::Gem10:
|
||||
return EQEmu::CastingSlot::Gem10;
|
||||
case CastingSlot::Discipline:
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
case CastingSlot::Item:
|
||||
return EQEmu::CastingSlot::Item;
|
||||
case CastingSlot::AltAbility:
|
||||
return EQEmu::CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
} /*SoD*/
|
||||
|
||||
@ -50,6 +50,22 @@ namespace SoD
|
||||
#include "sod_ops.h"
|
||||
};
|
||||
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
Gem3 = 2,
|
||||
Gem4 = 3,
|
||||
Gem5 = 4,
|
||||
Gem6 = 5,
|
||||
Gem7 = 6,
|
||||
Gem8 = 7,
|
||||
Gem9 = 8,
|
||||
Gem10 = 9,
|
||||
Item = 10,
|
||||
Discipline = 11,
|
||||
AltAbility = 0xFF
|
||||
};
|
||||
|
||||
}; /*SoD*/
|
||||
|
||||
#endif /*COMMON_SOD_H*/
|
||||
|
||||
@ -479,7 +479,7 @@ struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
//uint32 unknown12;
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
|
||||
/*
|
||||
@ -512,11 +512,12 @@ struct DeleteSpell_Struct
|
||||
|
||||
struct ManaChange_Struct
|
||||
{
|
||||
uint32 new_mana; // New Mana AMount
|
||||
uint32 stamina;
|
||||
uint32 spell_id;
|
||||
uint32 unknown12;
|
||||
uint32 unknown16;
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
};
|
||||
|
||||
struct SwapSpell_Struct
|
||||
|
||||
@ -59,6 +59,9 @@ namespace SoF
|
||||
// client to server text link converter
|
||||
static inline void SoFToServerTextLink(std::string& serverTextLink, const std::string& sofTextLink);
|
||||
|
||||
static inline CastingSlot ServerToSoFCastingSlot(EQEmu::CastingSlot slot);
|
||||
static inline EQEmu::CastingSlot SoFToServerCastingSlot(CastingSlot slot, uint32 itemlocation);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
@ -933,7 +936,24 @@ namespace SoF
|
||||
OUT(new_mana);
|
||||
OUT(stamina);
|
||||
OUT(spell_id);
|
||||
eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2?
|
||||
OUT(keepcasting);
|
||||
eq->slot = -1; // this is spell gem slot. It's -1 in normal operation
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_MemorizeSpell)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(MemorizeSpell_Struct);
|
||||
SETUP_DIRECT_ENCODE(MemorizeSpell_Struct, structs::MemorizeSpell_Struct);
|
||||
|
||||
// Since HT/LoH are translated up, we need to translate down only for memSpellSpellbar case
|
||||
if (emu->scribing == 3)
|
||||
eq->slot = static_cast<uint32>(ServerToSoFCastingSlot(static_cast<EQEmu::CastingSlot>(emu->slot)));
|
||||
else
|
||||
OUT(slot);
|
||||
OUT(spell_id);
|
||||
OUT(scribing);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@ -1158,9 +1178,9 @@ namespace SoF
|
||||
OUT(petid);
|
||||
OUT(buffcount);
|
||||
|
||||
int EQBuffSlot = 0;
|
||||
int EQBuffSlot = 0; // do we really want to shuffle them around like this?
|
||||
|
||||
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot)
|
||||
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < PET_BUFF_COUNT; ++EmuBuffSlot)
|
||||
{
|
||||
if (emu->spellid[EmuBuffSlot])
|
||||
{
|
||||
@ -2366,7 +2386,7 @@ namespace SoF
|
||||
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
||||
|
||||
IN(slot);
|
||||
emu->slot = static_cast<uint32>(SoFToServerCastingSlot(static_cast<CastingSlot>(eq->slot), eq->inventoryslot));
|
||||
IN(spell_id);
|
||||
emu->inventoryslot = SoFToServerSlot(eq->inventoryslot);
|
||||
IN(target_id);
|
||||
@ -3355,4 +3375,75 @@ namespace SoF
|
||||
}
|
||||
}
|
||||
|
||||
static inline CastingSlot ServerToSoFCastingSlot(EQEmu::CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case EQEmu::CastingSlot::Gem1:
|
||||
return CastingSlot::Gem1;
|
||||
case EQEmu::CastingSlot::Gem2:
|
||||
return CastingSlot::Gem2;
|
||||
case EQEmu::CastingSlot::Gem3:
|
||||
return CastingSlot::Gem3;
|
||||
case EQEmu::CastingSlot::Gem4:
|
||||
return CastingSlot::Gem4;
|
||||
case EQEmu::CastingSlot::Gem5:
|
||||
return CastingSlot::Gem5;
|
||||
case EQEmu::CastingSlot::Gem6:
|
||||
return CastingSlot::Gem6;
|
||||
case EQEmu::CastingSlot::Gem7:
|
||||
return CastingSlot::Gem7;
|
||||
case EQEmu::CastingSlot::Gem8:
|
||||
return CastingSlot::Gem8;
|
||||
case EQEmu::CastingSlot::Gem9:
|
||||
return CastingSlot::Gem9;
|
||||
case EQEmu::CastingSlot::Item:
|
||||
return CastingSlot::Item;
|
||||
case EQEmu::CastingSlot::PotionBelt:
|
||||
return CastingSlot::PotionBelt;
|
||||
case EQEmu::CastingSlot::Discipline:
|
||||
return CastingSlot::Discipline;
|
||||
case EQEmu::CastingSlot::AltAbility:
|
||||
return CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
|
||||
static inline EQEmu::CastingSlot SoFToServerCastingSlot(CastingSlot slot, uint32 itemlocation)
|
||||
{
|
||||
switch (slot) {
|
||||
case CastingSlot::Gem1:
|
||||
return EQEmu::CastingSlot::Gem1;
|
||||
case CastingSlot::Gem2:
|
||||
return EQEmu::CastingSlot::Gem2;
|
||||
case CastingSlot::Gem3:
|
||||
return EQEmu::CastingSlot::Gem3;
|
||||
case CastingSlot::Gem4:
|
||||
return EQEmu::CastingSlot::Gem4;
|
||||
case CastingSlot::Gem5:
|
||||
return EQEmu::CastingSlot::Gem5;
|
||||
case CastingSlot::Gem6:
|
||||
return EQEmu::CastingSlot::Gem6;
|
||||
case CastingSlot::Gem7:
|
||||
return EQEmu::CastingSlot::Gem7;
|
||||
case CastingSlot::Gem8:
|
||||
return EQEmu::CastingSlot::Gem8;
|
||||
case CastingSlot::Gem9:
|
||||
return EQEmu::CastingSlot::Gem9;
|
||||
case CastingSlot::Ability:
|
||||
return EQEmu::CastingSlot::Ability;
|
||||
// Tit uses 10 for item and discipline casting, but items have a valid location
|
||||
case CastingSlot::Item:
|
||||
if (itemlocation == INVALID_INDEX)
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
else
|
||||
return EQEmu::CastingSlot::Item;
|
||||
case CastingSlot::PotionBelt:
|
||||
return EQEmu::CastingSlot::PotionBelt;
|
||||
case CastingSlot::AltAbility:
|
||||
return EQEmu::CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
} /*SoF*/
|
||||
|
||||
@ -50,6 +50,23 @@ namespace SoF
|
||||
#include "sof_ops.h"
|
||||
};
|
||||
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
Gem3 = 2,
|
||||
Gem4 = 3,
|
||||
Gem5 = 4,
|
||||
Gem6 = 5,
|
||||
Gem7 = 6,
|
||||
Gem8 = 7,
|
||||
Gem9 = 8,
|
||||
Ability = 9,
|
||||
Item = 10,
|
||||
Discipline = 10,
|
||||
PotionBelt = 11,
|
||||
AltAbility = 0xFF
|
||||
};
|
||||
|
||||
}; /*SoF*/
|
||||
|
||||
#endif /*COMMON_SOF_H*/
|
||||
|
||||
@ -57,6 +57,7 @@ E(OP_LeadershipExpUpdate)
|
||||
E(OP_LogServer)
|
||||
E(OP_LootItem)
|
||||
E(OP_ManaChange)
|
||||
E(OP_MemorizeSpell)
|
||||
E(OP_MoveItem)
|
||||
E(OP_NewSpawn)
|
||||
E(OP_NewZone)
|
||||
|
||||
@ -458,7 +458,7 @@ struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
//uint32 unknown12;
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
|
||||
/*
|
||||
@ -491,11 +491,12 @@ struct DeleteSpell_Struct
|
||||
|
||||
struct ManaChange_Struct
|
||||
{
|
||||
uint32 new_mana; // New Mana AMount
|
||||
uint32 stamina;
|
||||
uint32 spell_id;
|
||||
uint32 unknown12;
|
||||
uint32 unknown16;
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
};
|
||||
|
||||
struct SwapSpell_Struct
|
||||
|
||||
@ -58,6 +58,9 @@ namespace Titanium
|
||||
// client to server text link converter
|
||||
static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink);
|
||||
|
||||
static inline CastingSlot ServerToTitaniumCastingSlot(EQEmu::CastingSlot slot);
|
||||
static inline EQEmu::CastingSlot TitaniumToServerCastingSlot(CastingSlot slot, uint32 itemlocation);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
{
|
||||
auto Config = EQEmuConfig::get();
|
||||
@ -833,6 +836,22 @@ namespace Titanium
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_MemorizeSpell)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(MemorizeSpell_Struct);
|
||||
SETUP_DIRECT_ENCODE(MemorizeSpell_Struct, structs::MemorizeSpell_Struct);
|
||||
|
||||
// Since HT/LoH are translated up, we need to translate down only for memSpellSpellbar case
|
||||
if (emu->scribing == 3)
|
||||
eq->slot = static_cast<uint32>(ServerToTitaniumCastingSlot(static_cast<EQEmu::CastingSlot>(emu->slot)));
|
||||
else
|
||||
OUT(slot);
|
||||
OUT(spell_id);
|
||||
OUT(scribing);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_MoveItem)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(MoveItem_Struct);
|
||||
@ -872,9 +891,9 @@ namespace Titanium
|
||||
OUT(petid);
|
||||
OUT(buffcount);
|
||||
|
||||
int EQBuffSlot = 0;
|
||||
int EQBuffSlot = 0; // do we really want to shuffle them around like this?
|
||||
|
||||
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot)
|
||||
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < PET_BUFF_COUNT; ++EmuBuffSlot)
|
||||
{
|
||||
if (emu->spellid[EmuBuffSlot])
|
||||
{
|
||||
@ -1731,7 +1750,7 @@ namespace Titanium
|
||||
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
||||
|
||||
IN(slot);
|
||||
emu->slot = static_cast<uint32>(TitaniumToServerCastingSlot(static_cast<CastingSlot>(eq->slot), eq->inventoryslot));
|
||||
IN(spell_id);
|
||||
emu->inventoryslot = TitaniumToServerSlot(eq->inventoryslot);
|
||||
IN(target_id);
|
||||
@ -2487,4 +2506,75 @@ namespace Titanium
|
||||
}
|
||||
}
|
||||
|
||||
static inline CastingSlot ServerToTitaniumCastingSlot(EQEmu::CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case EQEmu::CastingSlot::Gem1:
|
||||
return CastingSlot::Gem1;
|
||||
case EQEmu::CastingSlot::Gem2:
|
||||
return CastingSlot::Gem2;
|
||||
case EQEmu::CastingSlot::Gem3:
|
||||
return CastingSlot::Gem3;
|
||||
case EQEmu::CastingSlot::Gem4:
|
||||
return CastingSlot::Gem4;
|
||||
case EQEmu::CastingSlot::Gem5:
|
||||
return CastingSlot::Gem5;
|
||||
case EQEmu::CastingSlot::Gem6:
|
||||
return CastingSlot::Gem6;
|
||||
case EQEmu::CastingSlot::Gem7:
|
||||
return CastingSlot::Gem7;
|
||||
case EQEmu::CastingSlot::Gem8:
|
||||
return CastingSlot::Gem8;
|
||||
case EQEmu::CastingSlot::Gem9:
|
||||
return CastingSlot::Gem9;
|
||||
case EQEmu::CastingSlot::Item:
|
||||
return CastingSlot::Item;
|
||||
case EQEmu::CastingSlot::PotionBelt:
|
||||
return CastingSlot::PotionBelt;
|
||||
case EQEmu::CastingSlot::Discipline:
|
||||
return CastingSlot::Discipline;
|
||||
case EQEmu::CastingSlot::AltAbility:
|
||||
return CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
|
||||
static inline EQEmu::CastingSlot TitaniumToServerCastingSlot(CastingSlot slot, uint32 itemlocation)
|
||||
{
|
||||
switch (slot) {
|
||||
case CastingSlot::Gem1:
|
||||
return EQEmu::CastingSlot::Gem1;
|
||||
case CastingSlot::Gem2:
|
||||
return EQEmu::CastingSlot::Gem2;
|
||||
case CastingSlot::Gem3:
|
||||
return EQEmu::CastingSlot::Gem3;
|
||||
case CastingSlot::Gem4:
|
||||
return EQEmu::CastingSlot::Gem4;
|
||||
case CastingSlot::Gem5:
|
||||
return EQEmu::CastingSlot::Gem5;
|
||||
case CastingSlot::Gem6:
|
||||
return EQEmu::CastingSlot::Gem6;
|
||||
case CastingSlot::Gem7:
|
||||
return EQEmu::CastingSlot::Gem7;
|
||||
case CastingSlot::Gem8:
|
||||
return EQEmu::CastingSlot::Gem8;
|
||||
case CastingSlot::Gem9:
|
||||
return EQEmu::CastingSlot::Gem9;
|
||||
case CastingSlot::Ability:
|
||||
return EQEmu::CastingSlot::Ability;
|
||||
// Tit uses 10 for item and discipline casting, but items have a valid location
|
||||
case CastingSlot::Item:
|
||||
if (itemlocation == INVALID_INDEX)
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
else
|
||||
return EQEmu::CastingSlot::Item;
|
||||
case CastingSlot::PotionBelt:
|
||||
return EQEmu::CastingSlot::PotionBelt;
|
||||
case CastingSlot::AltAbility:
|
||||
return EQEmu::CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
} /*Titanium*/
|
||||
|
||||
@ -50,6 +50,23 @@ namespace Titanium
|
||||
#include "titanium_ops.h"
|
||||
};
|
||||
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
Gem3 = 2,
|
||||
Gem4 = 3,
|
||||
Gem5 = 4,
|
||||
Gem6 = 5,
|
||||
Gem7 = 6,
|
||||
Gem8 = 7,
|
||||
Gem9 = 8,
|
||||
Ability = 9,
|
||||
Item = 10,
|
||||
Discipline = 10,
|
||||
PotionBelt = 11,
|
||||
AltAbility = 0xFF
|
||||
};
|
||||
|
||||
}; /*Titanium*/
|
||||
|
||||
#endif /*COMMON_TITANIUM_H*/
|
||||
|
||||
@ -50,6 +50,7 @@ E(OP_ItemPacket)
|
||||
E(OP_LeadershipExpUpdate)
|
||||
E(OP_LFGuild)
|
||||
E(OP_LootItem)
|
||||
E(OP_MemorizeSpell)
|
||||
E(OP_MoveItem)
|
||||
E(OP_OnLevelMessage)
|
||||
E(OP_PetBuffWindow)
|
||||
|
||||
@ -390,7 +390,7 @@ struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 unknown12;
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
|
||||
/*
|
||||
@ -420,13 +420,13 @@ struct DeleteSpell_Struct
|
||||
/*005*/uint8 unknowndss006[3];
|
||||
/*008*/
|
||||
};
|
||||
|
||||
struct ManaChange_Struct
|
||||
{
|
||||
uint32 new_mana; // New Mana AMount
|
||||
uint32 stamina;
|
||||
uint32 spell_id;
|
||||
uint32 unknown12;
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
};
|
||||
|
||||
struct SwapSpell_Struct
|
||||
|
||||
@ -59,6 +59,9 @@ namespace UF
|
||||
// client to server text link converter
|
||||
static inline void UFToServerTextLink(std::string& serverTextLink, const std::string& ufTextLink);
|
||||
|
||||
static inline CastingSlot ServerToUFCastingSlot(EQEmu::CastingSlot slot);
|
||||
static inline EQEmu::CastingSlot UFToServerCastingSlot(CastingSlot slot);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
@ -1383,7 +1386,8 @@ namespace UF
|
||||
OUT(new_mana);
|
||||
OUT(stamina);
|
||||
OUT(spell_id);
|
||||
eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2?
|
||||
OUT(keepcasting);
|
||||
eq->slot = -1; // this is spell gem slot. It's -1 in normal operation
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@ -1761,18 +1765,18 @@ namespace UF
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1);
|
||||
VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount);
|
||||
|
||||
for (unsigned int i = 0; i < BUFF_COUNT; ++i)
|
||||
for (unsigned int i = 0; i < PET_BUFF_COUNT; ++i)
|
||||
{
|
||||
if (emu->spellid[i])
|
||||
{
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // numhits
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff.
|
||||
}
|
||||
}
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); /// I think this is actually some sort of type
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
@ -1867,7 +1871,7 @@ namespace UF
|
||||
OUT(thirst_level);
|
||||
OUT(hunger_level);
|
||||
//PS this needs to be figured out more; but it was 'good enough'
|
||||
for (r = 0; r < structs::BUFF_COUNT; r++)
|
||||
for (r = 0; r < BUFF_COUNT; r++)
|
||||
{
|
||||
if (emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0)
|
||||
{
|
||||
@ -3259,10 +3263,7 @@ namespace UF
|
||||
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
||||
|
||||
if (eq->slot == 13)
|
||||
emu->slot = 10;
|
||||
else
|
||||
IN(slot);
|
||||
emu->slot = static_cast<uint32>(UFToServerCastingSlot(static_cast<CastingSlot>(eq->slot)));
|
||||
|
||||
IN(spell_id);
|
||||
emu->inventoryslot = UFToServerSlot(eq->inventoryslot);
|
||||
@ -4378,4 +4379,80 @@ namespace UF
|
||||
}
|
||||
}
|
||||
|
||||
static inline CastingSlot ServerToUFCastingSlot(EQEmu::CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case EQEmu::CastingSlot::Gem1:
|
||||
return CastingSlot::Gem1;
|
||||
case EQEmu::CastingSlot::Gem2:
|
||||
return CastingSlot::Gem2;
|
||||
case EQEmu::CastingSlot::Gem3:
|
||||
return CastingSlot::Gem3;
|
||||
case EQEmu::CastingSlot::Gem4:
|
||||
return CastingSlot::Gem4;
|
||||
case EQEmu::CastingSlot::Gem5:
|
||||
return CastingSlot::Gem5;
|
||||
case EQEmu::CastingSlot::Gem6:
|
||||
return CastingSlot::Gem6;
|
||||
case EQEmu::CastingSlot::Gem7:
|
||||
return CastingSlot::Gem7;
|
||||
case EQEmu::CastingSlot::Gem8:
|
||||
return CastingSlot::Gem8;
|
||||
case EQEmu::CastingSlot::Gem9:
|
||||
return CastingSlot::Gem9;
|
||||
case EQEmu::CastingSlot::Gem10:
|
||||
return CastingSlot::Gem10;
|
||||
case EQEmu::CastingSlot::Gem11:
|
||||
return CastingSlot::Gem11;
|
||||
case EQEmu::CastingSlot::Gem12:
|
||||
return CastingSlot::Gem12;
|
||||
case EQEmu::CastingSlot::Item:
|
||||
case EQEmu::CastingSlot::PotionBelt:
|
||||
return CastingSlot::Item;
|
||||
case EQEmu::CastingSlot::Discipline:
|
||||
return CastingSlot::Discipline;
|
||||
case EQEmu::CastingSlot::AltAbility:
|
||||
return CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
|
||||
static inline EQEmu::CastingSlot UFToServerCastingSlot(CastingSlot slot)
|
||||
{
|
||||
switch (slot) {
|
||||
case CastingSlot::Gem1:
|
||||
return EQEmu::CastingSlot::Gem1;
|
||||
case CastingSlot::Gem2:
|
||||
return EQEmu::CastingSlot::Gem2;
|
||||
case CastingSlot::Gem3:
|
||||
return EQEmu::CastingSlot::Gem3;
|
||||
case CastingSlot::Gem4:
|
||||
return EQEmu::CastingSlot::Gem4;
|
||||
case CastingSlot::Gem5:
|
||||
return EQEmu::CastingSlot::Gem5;
|
||||
case CastingSlot::Gem6:
|
||||
return EQEmu::CastingSlot::Gem6;
|
||||
case CastingSlot::Gem7:
|
||||
return EQEmu::CastingSlot::Gem7;
|
||||
case CastingSlot::Gem8:
|
||||
return EQEmu::CastingSlot::Gem8;
|
||||
case CastingSlot::Gem9:
|
||||
return EQEmu::CastingSlot::Gem9;
|
||||
case CastingSlot::Gem10:
|
||||
return EQEmu::CastingSlot::Gem10;
|
||||
case CastingSlot::Gem11:
|
||||
return EQEmu::CastingSlot::Gem11;
|
||||
case CastingSlot::Gem12:
|
||||
return EQEmu::CastingSlot::Gem12;
|
||||
case CastingSlot::Discipline:
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
case CastingSlot::Item:
|
||||
return EQEmu::CastingSlot::Item;
|
||||
case CastingSlot::AltAbility:
|
||||
return EQEmu::CastingSlot::AltAbility;
|
||||
default: // we shouldn't have any issues with other slots ... just return something
|
||||
return EQEmu::CastingSlot::Discipline;
|
||||
}
|
||||
}
|
||||
} /*UF*/
|
||||
|
||||
@ -50,6 +50,24 @@ namespace UF
|
||||
#include "uf_ops.h"
|
||||
};
|
||||
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
Gem3 = 2,
|
||||
Gem4 = 3,
|
||||
Gem5 = 4,
|
||||
Gem6 = 5,
|
||||
Gem7 = 6,
|
||||
Gem8 = 7,
|
||||
Gem9 = 8,
|
||||
Gem10 = 9,
|
||||
Gem11 = 10,
|
||||
Gem12 = 11,
|
||||
Item = 12,
|
||||
Discipline = 13,
|
||||
AltAbility = 0xFF
|
||||
};
|
||||
|
||||
}; /*UF*/
|
||||
|
||||
#endif /*COMMON_UF_H*/
|
||||
|
||||
@ -26,7 +26,7 @@ namespace UF
|
||||
namespace structs {
|
||||
|
||||
|
||||
static const uint32 BUFF_COUNT = 25;
|
||||
static const uint32 BUFF_COUNT = 30;
|
||||
|
||||
/*
|
||||
** Compiler override to ensure
|
||||
@ -479,7 +479,7 @@ struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
//uint32 unknown12;
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
|
||||
/*
|
||||
@ -512,11 +512,12 @@ struct DeleteSpell_Struct
|
||||
|
||||
struct ManaChange_Struct
|
||||
{
|
||||
uint32 new_mana; // New Mana AMount
|
||||
uint32 stamina;
|
||||
uint32 spell_id;
|
||||
uint32 unknown12;
|
||||
uint32 unknown16;
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
};
|
||||
|
||||
struct SwapSpell_Struct
|
||||
@ -958,8 +959,7 @@ struct PlayerProfile_Struct
|
||||
/*07880*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3)
|
||||
/*07884*/ uint32 thirst_level; // Drink (ticks till next drink)
|
||||
/*07888*/ uint32 hunger_level; // Food (ticks till next eat)
|
||||
/*07892*/ SpellBuff_Struct buffs[BUFF_COUNT]; // [1900] Buffs currently on the player (30 Max) - (Each Size 76)
|
||||
/*09792*/ uint8 unknown09792[380]; // BUFF_COUNT has been left at 25. These are the extra 5 buffs in Underfoot
|
||||
/*07892*/ SpellBuff_Struct buffs[BUFF_COUNT]; // [2280] Buffs currently on the player (30 Max) - (Each Size 76)
|
||||
/*10172*/ Disciplines_Struct disciplines; // [400] Known disciplines
|
||||
/*10972*/ uint32 recastTimers[MAX_RECAST_TYPES]; // Timers (UNIX Time of last use)
|
||||
/*11052*/ uint8 unknown11052[160]; // Some type of Timers
|
||||
@ -2167,9 +2167,7 @@ struct GroupFollow_Struct { // Underfoot Follow Struct
|
||||
|
||||
struct InspectBuffs_Struct {
|
||||
/*000*/ uint32 spell_id[BUFF_COUNT];
|
||||
/*100*/ uint32 filler100[5]; // BUFF_COUNT is really 30...
|
||||
/*120*/ int32 tics_remaining[BUFF_COUNT];
|
||||
/*220*/ uint32 filler220[5]; // BUFF_COUNT is really 30...
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -37,10 +37,12 @@ enum { //values for pTimerType
|
||||
pTimerSenseTraps = 12,
|
||||
pTimerDisarmTraps = 13,
|
||||
pTimerDisciplineReuseStart = 14,
|
||||
pTimerDisciplineReuseEnd = 24,
|
||||
pTimerDisciplineReuseEnd = 24, // client actually has 20 ids, but still no disc go that high even on live
|
||||
pTimerCombatAbility = 25,
|
||||
pTimerCombatAbility2 = 26, // RoF2+ Tiger Claw is unlinked from other monk skills, generic in case other classes ever need it
|
||||
pTimerBeggingPickPocket = 27,
|
||||
pTimerLinkedSpellReuseStart = 28,
|
||||
pTimerLinkedSpellReuseEnd = 48,
|
||||
|
||||
pTimerLayHands = 87, //these IDs are used by client too
|
||||
pTimerHarmTouch = 89, //so dont change them
|
||||
|
||||
@ -45,6 +45,7 @@
|
||||
#define TIGER 63
|
||||
#define ELEMENTAL 75
|
||||
#define ALLIGATOR 91
|
||||
#define OGGOK_CITIZEN 93
|
||||
#define EYE_OF_ZOMM 108
|
||||
#define WOLF_ELEMENTAL 120
|
||||
#define INVISIBLE_MAN 127
|
||||
|
||||
@ -142,6 +142,8 @@ RULE_INT(Character, InvSnapshotMinRetryM, 30) // Time (in minutes) to re-attempt
|
||||
RULE_INT(Character, InvSnapshotHistoryD, 30) // Time (in days) to keep snapshot entries
|
||||
RULE_BOOL(Character, RestrictSpellScribing, false) // Restricts spell scribing to allowable races/classes of spell scroll, if true
|
||||
RULE_BOOL(Character, UseStackablePickPocketing, true) // Allows stackable pickpocketed items to stack instead of only being allowed in empty inventory slots
|
||||
RULE_BOOL(Character, EnableAvoidanceCap, false)
|
||||
RULE_INT(Character, AvoidanceCap, 750) // 750 Is a pretty good value, seen people dodge all attacks beyond 1,000 Avoidance
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
@ -329,7 +331,7 @@ RULE_INT(Spells, MaxBuffSlotsNPC, 25)
|
||||
RULE_INT(Spells, MaxSongSlotsNPC, 10)
|
||||
RULE_INT(Spells, MaxDiscSlotsNPC, 1)
|
||||
RULE_INT(Spells, MaxTotalSlotsNPC, 36)
|
||||
RULE_INT(Spells, MaxTotalSlotsPET, 25) // do not set this higher than 25 until the player profile is removed from the blob
|
||||
RULE_INT(Spells, MaxTotalSlotsPET, 30) // do not set this higher than 25 until the player profile is removed from the blob
|
||||
RULE_BOOL (Spells, EnableBlockedBuffs, true)
|
||||
RULE_INT(Spells, ReflectType, 1) //0 = disabled, 1 = single target player spells only, 2 = all player spells, 3 = all single target spells, 4 = all spells
|
||||
RULE_INT(Spells, VirusSpreadDistance, 30) // The distance a viral spell will jump to its next victim
|
||||
@ -379,6 +381,7 @@ RULE_BOOL(Spells, UseAdditiveFocusFromWornSlot, false) // Allows an additive foc
|
||||
RULE_BOOL(Spells, AlwaysSendTargetsBuffs, false) // ignore LAA level if true
|
||||
RULE_BOOL(Spells, FlatItemExtraSpellAmt, false) // allow SpellDmg stat to affect all spells, regardless of cast time/cooldown/etc
|
||||
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_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Combat)
|
||||
@ -503,6 +506,7 @@ RULE_BOOL(NPC, EnableMeritBasedFaction, false) // If set to true, faction will g
|
||||
RULE_INT(NPC, NPCToNPCAggroTimerMin, 500)
|
||||
RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000)
|
||||
RULE_BOOL(NPC, UseClassAsLastName, true) // Uses class archetype as LastName for npcs with none
|
||||
RULE_BOOL(NPC, NewLevelScaling, true) // Better level scaling, use old if new formulas would break your server
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Aggro)
|
||||
@ -518,6 +522,7 @@ RULE_REAL(Aggro, TunnelVisionAggroMod, 0.75) //people not currently the top hate
|
||||
RULE_INT(Aggro, MaxScalingProcAggro, 400) // Set to -1 for no limit. Maxmimum amount of aggro that HP scaling SPA effect in a proc will add.
|
||||
RULE_INT(Aggro, IntAggroThreshold, 75) // Int <= this will aggro regardless of level difference.
|
||||
RULE_BOOL(Aggro, AllowTickPulling, false) // tick pulling is an exploit in an NPC's call for help fixed sometime in 2006 on live
|
||||
RULE_BOOL(Aggro, UseLevelAggro, true) // Level 18+ and Undead will aggro regardless of level difference. (this will disabled Rule:IntAggroThreshold if set to true)
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(TaskSystem)
|
||||
|
||||
@ -455,7 +455,7 @@ bool SharedDatabase::GetSharedBank(uint32 id, Inventory *inv, bool is_charid)
|
||||
}
|
||||
}
|
||||
|
||||
if (row[9]) {
|
||||
if (inst && row[9]) {
|
||||
std::string data_str(row[9]);
|
||||
std::string idAsString;
|
||||
std::string value;
|
||||
@ -480,6 +480,7 @@ bool SharedDatabase::GetSharedBank(uint32 id, Inventory *inv, bool is_charid)
|
||||
}
|
||||
}
|
||||
|
||||
// theoretically inst can be nullptr ... this would be very bad ...
|
||||
put_slot_id = inv->PutItem(slot_id, *inst);
|
||||
safe_delete(inst);
|
||||
|
||||
|
||||
@ -110,6 +110,20 @@ bool EQEmu::skills::IsBardInstrumentSkill(SkillType skill)
|
||||
}
|
||||
}
|
||||
|
||||
bool EQEmu::skills::IsCastingSkill(SkillType skill)
|
||||
{
|
||||
switch (skill) {
|
||||
case SkillAbjuration:
|
||||
case SkillAlteration:
|
||||
case SkillConjuration:
|
||||
case SkillDivination:
|
||||
case SkillEvocation:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<EQEmu::skills::SkillType, std::string>& EQEmu::skills::GetSkillTypeMap()
|
||||
{
|
||||
/* VS2013 code
|
||||
|
||||
@ -161,10 +161,11 @@ namespace EQEmu
|
||||
// server profile does not reflect this yet..so, prefixed with 'PACKET_'
|
||||
#define PACKET_SKILL_ARRAY_SIZE 100
|
||||
|
||||
extern bool IsTradeskill(SkillType skill);
|
||||
extern bool IsSpecializedSkill(SkillType skill);
|
||||
extern float GetSkillMeleePushForce(SkillType skill);
|
||||
extern bool IsBardInstrumentSkill(SkillType skill);
|
||||
bool IsTradeskill(SkillType skill);
|
||||
bool IsSpecializedSkill(SkillType skill);
|
||||
float GetSkillMeleePushForce(SkillType skill);
|
||||
bool IsBardInstrumentSkill(SkillType skill);
|
||||
bool IsCastingSkill(SkillType skill);
|
||||
|
||||
extern const std::map<SkillType, std::string>& GetSkillTypeMap();
|
||||
|
||||
|
||||
@ -235,8 +235,11 @@ bool IsBeneficialSpell(uint16 spell_id)
|
||||
// If the resisttype is magic and SpellAffectIndex is Calm/memblur/dispell sight
|
||||
// it's not beneficial
|
||||
if (spells[spell_id].resisttype == RESIST_MAGIC) {
|
||||
if (sai == SAI_Calm || sai == SAI_Dispell_Sight ||
|
||||
sai == SAI_Memory_Blur || sai == SAI_Calm_Song)
|
||||
// checking these SAI cause issues with the rng defensive proc line
|
||||
// So I guess instead of fixing it for real, just a quick hack :P
|
||||
if (spells[spell_id].effectid[0] != SE_DefensiveProc &&
|
||||
(sai == SAI_Calm || sai == SAI_Dispell_Sight || sai == SAI_Memory_Blur ||
|
||||
sai == SAI_Calm_Song))
|
||||
return false;
|
||||
} else {
|
||||
// If the resisttype is not magic and spell is Bind Sight or Cast Sight
|
||||
@ -669,9 +672,7 @@ bool IsDisciplineBuff(uint16 spell_id)
|
||||
if (!IsValidSpell(spell_id))
|
||||
return false;
|
||||
|
||||
if (spells[spell_id].mana == 0 && spells[spell_id].short_buff_box == 0 &&
|
||||
(spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep) &&
|
||||
spells[spell_id].targettype == ST_Self)
|
||||
if (spells[spell_id].IsDisciplineBuff && spells[spell_id].targettype == ST_Self)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
@ -446,7 +446,7 @@ typedef enum {
|
||||
#define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap
|
||||
#define SE_Purify 291 // implemented - Removes determental effects
|
||||
#define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte.
|
||||
#define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front.
|
||||
#define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore
|
||||
#define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier.
|
||||
//#define SE_ReduceTimerSpecial 295 // not used
|
||||
#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage
|
||||
|
||||
@ -1985,7 +1985,7 @@ void Client::ChannelGrantVoice(std::string CommandString) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(RequiredChannel->IsOwner(RequiredClient->GetName()) || RequiredChannel->IsModerator(RequiredClient->GetName())) {
|
||||
if(RequiredClient && (RequiredChannel->IsOwner(RequiredClient->GetName()) || RequiredChannel->IsModerator(RequiredClient->GetName()))) {
|
||||
|
||||
GeneralChannelMessage("The channel owner and moderators automatically have voice.");
|
||||
return;
|
||||
|
||||
@ -172,6 +172,7 @@ OP_BeginCast=0x17ff
|
||||
OP_ColoredText=0x41cb
|
||||
OP_ConsentResponse=0x183d
|
||||
OP_MemorizeSpell=0x2fac
|
||||
OP_LinkedReuse=0x3ac0
|
||||
OP_SwapSpell=0x4736
|
||||
OP_CastSpell=0x1cb5
|
||||
OP_Consider=0x4d8d
|
||||
|
||||
@ -171,6 +171,7 @@ OP_BeginCast=0x318f
|
||||
OP_ColoredText=0x43af
|
||||
OP_ConsentResponse=0x384a
|
||||
OP_MemorizeSpell=0x217c
|
||||
OP_LinkedReuse=0x1619
|
||||
OP_SwapSpell=0x0efa
|
||||
OP_CastSpell=0x1287
|
||||
OP_Consider=0x742b
|
||||
|
||||
@ -171,6 +171,7 @@ OP_BeginCast=0x0d5a # C
|
||||
OP_ColoredText=0x569a # C
|
||||
OP_ConsentResponse=0x6e47 # C
|
||||
OP_MemorizeSpell=0x8543 # C
|
||||
OP_LinkedReuse=0x6ef9
|
||||
OP_SwapSpell=0x3fd2 # C
|
||||
OP_CastSpell=0x3582 # C
|
||||
OP_Consider=0x6024 # C
|
||||
|
||||
@ -169,6 +169,7 @@ OP_BeginCast=0x5A50 #SEQ 12/04/08
|
||||
OP_ColoredText=0x3BC7 #SEQ 12/04/08
|
||||
OP_ConsentResponse=0x4D30 #SEQ 12/04/08
|
||||
OP_MemorizeSpell=0x6A93 #SEQ 12/04/08
|
||||
OP_LinkedReuse=0x2c26
|
||||
OP_SwapSpell=0x1418 #SEQ 12/04/08
|
||||
OP_CastSpell=0x7F5D #SEQ 12/04/08
|
||||
OP_Consider=0x32E1 #SEQ 12/04/08
|
||||
|
||||
@ -170,6 +170,7 @@ OP_Save=0x736b # ShowEQ 10/27/05
|
||||
OP_Camp=0x78c1 # ShowEQ 10/27/05
|
||||
OP_EndLootRequest=0x2316 # ShowEQ 10/27/05
|
||||
OP_MemorizeSpell=0x308e # ShowEQ 10/27/05
|
||||
OP_LinkedReuse=0x6a00
|
||||
OP_SwapSpell=0x2126 # ShowEQ 10/27/05
|
||||
OP_CastSpell=0x304b # ShowEQ 10/27/05
|
||||
OP_DeleteSpell=0x4f37
|
||||
|
||||
@ -173,6 +173,7 @@ OP_BeginCast=0x0d5a # C
|
||||
OP_ColoredText=0x71bf # C
|
||||
OP_ConsentResponse=0x0e87 # C
|
||||
OP_MemorizeSpell=0x3887 # C
|
||||
OP_LinkedReuse=0x1b26
|
||||
OP_SwapSpell=0x5805 # C
|
||||
OP_CastSpell=0x50c2 # C
|
||||
OP_Consider=0x3c2d # C
|
||||
|
||||
@ -33,6 +33,7 @@ if(!$ARGV[0]){
|
||||
print " tables=\"table1,table2,table3\" - Manually specify tables, default is to dump all tables from database\n";
|
||||
print " compress - Compress Database with 7-ZIP, will fallback to WinRAR depending on what is installed (Must be installed to default program dir)...\n";
|
||||
print " nolock - Does not lock tables, meant for backuping while the server is running..\n";
|
||||
print " backup_name=\"name\" - Sets database backup prefix name\n";
|
||||
print ' Example: perl DB_Dumper.pl Loc="E:\Backups"' . "\n\n";
|
||||
print "######################################################\n";
|
||||
exit;
|
||||
@ -72,6 +73,11 @@ while($ARGV[$n]){
|
||||
print "Database is " . $DB_NAME[1] . "\n";
|
||||
$db = $DB_NAME[1];
|
||||
}
|
||||
if($ARGV[$n]=~/backup_name=/i){
|
||||
@data = split('=', $ARGV[$n]);
|
||||
print "Backup Name is " . $data[1] . "\n";
|
||||
$backup_name = $data[1];
|
||||
}
|
||||
if($ARGV[$n]=~/loc=/i){
|
||||
@B_LOC = split('=', $ARGV[$n]);
|
||||
print "Backup Directory: " . $B_LOC[1] . "\n";
|
||||
@ -108,8 +114,14 @@ else {
|
||||
}
|
||||
|
||||
if($t_tables ne ""){
|
||||
$tables_f_l = substr($t_tables_l, 0, 20) . '...';
|
||||
$target_file = '' . $tables_f_l . '_' . $date . '';
|
||||
$tables_f_l = substr($t_tables_l, 0, 20) . '-';
|
||||
if($backup_name){
|
||||
$target_file = $backup_name . '_' . $date . '';
|
||||
}
|
||||
else {
|
||||
$target_file = '' . $tables_f_l . '_' . $date . '';
|
||||
}
|
||||
|
||||
print "Performing table based backup...\n";
|
||||
#::: Backup Database...
|
||||
print "Backing up Database " . $db . "... \n\n";
|
||||
@ -121,7 +133,14 @@ if($t_tables ne ""){
|
||||
system($cmd);
|
||||
}
|
||||
else{ #::: Entire DB Backup
|
||||
$target_file = '' . $db . '_' . $date . '';
|
||||
|
||||
if($backup_name){
|
||||
$target_file = $backup_name . '_' . $db . '_' . $date . '';
|
||||
}
|
||||
else {
|
||||
$target_file = '' . $db . '_' . $date . '';
|
||||
}
|
||||
|
||||
#::: Backup Database...
|
||||
print "Backing up Database " . $db . "... \n\n";
|
||||
if($no_lock == 1){
|
||||
@ -195,6 +214,9 @@ if($Compress == 1){
|
||||
$final_file = $target_file . ".tar.gz";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$final_file = $target_file . ".sql";
|
||||
}
|
||||
|
||||
#::: Get Final File Location for display
|
||||
if($B_LOC[1] ne ""){ $final_loc = $B_LOC[1] . '' . $file_app . ""; }
|
||||
|
||||
1552
utils/scripts/eqemu_server.pl
Normal file
1552
utils/scripts/eqemu_server.pl
Normal file
File diff suppressed because it is too large
Load Diff
@ -298,6 +298,7 @@ sub show_menu_prompt {
|
||||
18 => \&fetch_latest_windows_binaries_bots,
|
||||
19 => \&do_bots_db_schema_drop,
|
||||
20 => \&do_update_self,
|
||||
21 => \&database_dump_player_tables,
|
||||
0 => \&script_exit,
|
||||
);
|
||||
|
||||
@ -378,6 +379,7 @@ return <<EO_MENU;
|
||||
18) [Windows Server Build Bots] :: Download Latest and Stable Server Build with Bots
|
||||
19) [EQEmu DB Drop Bots Schema] :: Remove Bots schema and return database to normal state
|
||||
20) [Update the updater] Force update this script (Redownload)
|
||||
21) [DB :: Backup Player Tables] :: Backs up player tables
|
||||
0) Exit
|
||||
|
||||
Enter numbered option and press enter...
|
||||
@ -404,6 +406,30 @@ sub database_dump {
|
||||
print "Performing database backup....\n";
|
||||
print `perl db_dumper.pl database="$db" loc="backups"`;
|
||||
}
|
||||
|
||||
sub database_dump_player_tables {
|
||||
check_for_database_dump_script();
|
||||
print "Performing database backup of player tables....\n";
|
||||
get_remote_file("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/character_table_list.txt", "backups/character_table_list.txt");
|
||||
|
||||
$tables = "";
|
||||
open (FILE, "backups/character_table_list.txt");
|
||||
$i = 0;
|
||||
while (<FILE>){
|
||||
chomp;
|
||||
$o = $_;
|
||||
$tables .= $o . ",";
|
||||
}
|
||||
$tables = substr($tables, 0, -1);
|
||||
|
||||
print `perl db_dumper.pl database="$db" loc="backups" tables="$tables" backup_name="player_tables_export" nolock`;
|
||||
|
||||
print "\nPress any key to continue...\n";
|
||||
|
||||
<>; #Read from STDIN
|
||||
|
||||
}
|
||||
|
||||
sub database_dump_compress {
|
||||
check_for_database_dump_script();
|
||||
print "Performing database backup....\n";
|
||||
|
||||
33
utils/sql/character_table_list.txt
Normal file
33
utils/sql/character_table_list.txt
Normal file
@ -0,0 +1,33 @@
|
||||
adventure_stats
|
||||
char_recipe_list
|
||||
character_activities
|
||||
character_alt_currency
|
||||
character_alternate_abilities
|
||||
character_bandolier
|
||||
character_bind
|
||||
character_currency
|
||||
character_data
|
||||
character_disciplines
|
||||
character_enabledtasks
|
||||
character_inspect_messages
|
||||
character_languages
|
||||
character_leadership_abilities
|
||||
character_material
|
||||
character_memmed_spells
|
||||
character_potionbelt
|
||||
character_skills
|
||||
character_spells
|
||||
character_tribute
|
||||
completed_tasks
|
||||
faction_values
|
||||
friends
|
||||
guild_members
|
||||
instance_list_player
|
||||
inventory
|
||||
keyring
|
||||
mail
|
||||
player_titlesets
|
||||
quest_globals
|
||||
timers
|
||||
titles
|
||||
zone_flags
|
||||
@ -142,7 +142,7 @@ void Client::SendLogServer()
|
||||
if(RuleB(World, IsGMPetitionWindowEnabled))
|
||||
l->enable_petition_wnd = 1;
|
||||
|
||||
if(RuleI(World, FVNoDropFlag) == 1 || RuleI(World, FVNoDropFlag) == 2 && GetAdmin() > RuleI(Character, MinStatusForNoDropExemptions))
|
||||
if((RuleI(World, FVNoDropFlag) == 1 || RuleI(World, FVNoDropFlag) == 2) && GetAdmin() > RuleI(Character, MinStatusForNoDropExemptions))
|
||||
l->enable_FV = 1;
|
||||
|
||||
QueuePacket(outapp);
|
||||
@ -1483,7 +1483,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
|
||||
for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++)
|
||||
pp.spell_book[i] = 0xFFFFFFFF;
|
||||
|
||||
for(i = 0; i < MAX_PP_REF_MEMSPELL; i++)
|
||||
for(i = 0; i < MAX_PP_MEMSPELL; i++)
|
||||
pp.mem_spells[i] = 0xFFFFFFFF;
|
||||
|
||||
for(i = 0; i < BUFF_COUNT; i++)
|
||||
|
||||
@ -88,7 +88,7 @@ void ZSList::Process() {
|
||||
Process();
|
||||
CatchSignal(2);
|
||||
}
|
||||
if(reminder && reminder->Check()){
|
||||
if(reminder && reminder->Check() && shutdowntimer){
|
||||
SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down, everyone log out now. World will shut down in %i minutes...", ((shutdowntimer->GetRemainingTime()/1000) / 60));
|
||||
}
|
||||
LinkedListIterator<ZoneServer*> iterator(list);
|
||||
|
||||
10
zone/aa.cpp
10
zone/aa.cpp
@ -867,7 +867,7 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) {
|
||||
aai->max_level = ability->GetMaxLevel(this);
|
||||
aai->prev_id = rank->prev_id;
|
||||
|
||||
if(rank->next && !CanUseAlternateAdvancementRank(rank->next) || ability->charges > 0) {
|
||||
if((rank->next && !CanUseAlternateAdvancementRank(rank->next)) || ability->charges > 0) {
|
||||
aai->next_id = -1;
|
||||
} else {
|
||||
aai->next_id = rank->next_id;
|
||||
@ -1183,12 +1183,12 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
|
||||
CommonBreakInvisible();
|
||||
// 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), ALTERNATE_ABILITY_SPELL_SLOT, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
|
||||
if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQEmu::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
|
||||
return;
|
||||
}
|
||||
ExpendAlternateAdvancementCharge(ability->id);
|
||||
} else {
|
||||
if(!CastSpell(rank->spell, target_id, ALTERNATE_ABILITY_SPELL_SLOT, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
|
||||
if(!CastSpell(rank->spell, target_id, EQEmu::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1240,6 +1240,10 @@ void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
|
||||
CastToClient()->GetEPP().expended_aa += r->cost;
|
||||
}
|
||||
}
|
||||
if (IsClient()) {
|
||||
auto c = CastToClient();
|
||||
c->RemoveExpendedAA(ability->first_rank_id);
|
||||
}
|
||||
aa_ranks.erase(iter.first);
|
||||
}
|
||||
|
||||
|
||||
@ -156,10 +156,21 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GREEN ) {
|
||||
towho->Message(0, "...%s is red to me (basically)", mob->GetName(),
|
||||
dist2, iAggroRange2);
|
||||
return;
|
||||
if (RuleB(Aggro, UseLevelAggro))
|
||||
{
|
||||
if (GetLevel() < 18 && mob->GetLevelCon(GetLevel()) == CON_GREEN && GetBodyType() != 3)
|
||||
{
|
||||
towho->Message(0, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GREEN ) {
|
||||
towho->Message(0, "...%s is red to me (basically)", mob->GetName(),
|
||||
dist2, iAggroRange2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(verbose) {
|
||||
@ -321,11 +332,12 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap
|
||||
if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE)
|
||||
heroicCHA_mod = THREATENLY_ARRGO_CHANCE;
|
||||
if
|
||||
if (RuleB(Aggro, UseLevelAggro) &&
|
||||
(
|
||||
//old InZone check taken care of above by !mob->CastToClient()->Connected()
|
||||
(
|
||||
( GetINT() <= RuleI(Aggro, IntAggroThreshold) )
|
||||
( GetLevel() >= 18 )
|
||||
||(GetBodyType() == 3)
|
||||
||( mob->IsClient() && mob->CastToClient()->IsSitting() )
|
||||
||( mob->GetLevelCon(GetLevel()) != CON_GREEN )
|
||||
|
||||
@ -344,6 +356,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
//FatherNiwtit: make sure we can see them. last since it is very expensive
|
||||
if(CheckLosFN(mob)) {
|
||||
@ -351,6 +364,39 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
return( mod_will_aggro(mob, this) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if
|
||||
(
|
||||
//old InZone check taken care of above by !mob->CastToClient()->Connected()
|
||||
(
|
||||
( GetINT() <= RuleI(Aggro, IntAggroThreshold) )
|
||||
||( mob->IsClient() && mob->CastToClient()->IsSitting() )
|
||||
||( mob->GetLevelCon(GetLevel()) != CON_GREEN )
|
||||
|
||||
)
|
||||
&&
|
||||
(
|
||||
(
|
||||
fv == FACTION_SCOWLS
|
||||
||
|
||||
(mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr)
|
||||
||
|
||||
(
|
||||
fv == FACTION_THREATENLY
|
||||
&& zone->random.Roll(THREATENLY_ARRGO_CHANCE - heroicCHA_mod)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
//FatherNiwtit: make sure we can see them. last since it is very expensive
|
||||
if(CheckLosFN(mob)) {
|
||||
Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName());
|
||||
return( mod_will_aggro(mob, this) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.Out(Logs::Detail, Logs::Aggro, "Is In zone?:%d\n", mob->InZone());
|
||||
Log.Out(Logs::Detail, Logs::Aggro, "Dist^2: %f\n", dist2);
|
||||
@ -462,7 +508,7 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
|
||||
{
|
||||
//if they are in range, make sure we are not green...
|
||||
//then jump in if they are our friend
|
||||
if(attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN)
|
||||
if(mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN)
|
||||
{
|
||||
bool useprimfaction = false;
|
||||
if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction())
|
||||
|
||||
133
zone/attack.cpp
133
zone/attack.cpp
@ -23,6 +23,7 @@
|
||||
#include "../common/skills.h"
|
||||
#include "../common/spdat.h"
|
||||
#include "../common/string_util.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "queryserv.h"
|
||||
#include "quest_parser_collection.h"
|
||||
#include "string_ids.h"
|
||||
@ -369,18 +370,15 @@ bool Mob::AvoidDamage(Mob *other, int32 &damage, int hand)
|
||||
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// make enrage same as riposte
|
||||
/////////////////////////////////////////////////////////
|
||||
if (IsEnraged() && InFront) {
|
||||
damage = -3;
|
||||
Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack.");
|
||||
}
|
||||
|
||||
// riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo
|
||||
bool ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance;
|
||||
// Need to check if we have something in MainHand to actually attack with (or fists)
|
||||
if (hand != EQEmu::legacy::SlotRange && CanThisClassRiposte() && InFront && !ImmuneRipo) {
|
||||
if (IsEnraged()) {
|
||||
damage = -3;
|
||||
Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack.");
|
||||
return true;
|
||||
}
|
||||
if (IsClient())
|
||||
CastToClient()->CheckIncreaseSkill(EQEmu::skills::SkillRiposte, other, -10);
|
||||
// check auto discs ... I guess aa/items too :P
|
||||
@ -1205,7 +1203,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
|
||||
IsValidSpell(aabonuses.SkillAttackProc[2])) {
|
||||
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
|
||||
if (zone->random.Roll(chance))
|
||||
SpellFinished(aabonuses.SkillAttackProc[2], other, 10, 0, -1,
|
||||
SpellFinished(aabonuses.SkillAttackProc[2], other, EQEmu::CastingSlot::Item, 0, -1,
|
||||
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
|
||||
}
|
||||
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse, true, -1, false, special);
|
||||
@ -1802,7 +1800,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::Skill
|
||||
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
|
||||
if(attacked_timer.Check())
|
||||
{
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Triggering EVENT_ATTACK due to attack by %s", other->GetName());
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Triggering EVENT_ATTACK due to attack by %s", other ? other->GetName() : "nullptr");
|
||||
parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
|
||||
}
|
||||
attacked_timer.Start(CombatEventTimer_expire);
|
||||
@ -1821,7 +1819,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::Skill
|
||||
if(IsLDoNTrapped())
|
||||
{
|
||||
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
|
||||
SpellFinished(GetLDoNTrapSpellID(), other, 10, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false);
|
||||
SpellFinished(GetLDoNTrapSpellID(), other, EQEmu::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false);
|
||||
SetLDoNTrapSpellID(0);
|
||||
SetLDoNTrapped(false);
|
||||
SetLDoNTrapDetected(false);
|
||||
@ -3169,68 +3167,63 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
BuffFadeByEffect(SE_Mez);
|
||||
}
|
||||
|
||||
//check stun chances if bashing
|
||||
if (damage > 0 && ((skill_used == EQEmu::skills::SkillBash || skill_used == EQEmu::skills::SkillKick) && attacker)) {
|
||||
// NPCs can stun with their bash/kick as soon as they receive it.
|
||||
// Clients can stun mobs under level 56 with their kick when they get level 55 or greater.
|
||||
// Clients have a chance to stun if the mob is 56+
|
||||
|
||||
// Calculate the chance to stun
|
||||
int stun_chance = 0;
|
||||
if (!GetSpecialAbility(UNSTUNABLE)) {
|
||||
if (attacker->IsNPC()) {
|
||||
stun_chance = RuleI(Combat, NPCBashKickStunChance);
|
||||
} else if (attacker->IsClient()) {
|
||||
// Less than base immunity
|
||||
// Client vs. Client always uses the chance
|
||||
if (!IsClient() && GetLevel() <= RuleI(Spells, BaseImmunityLevel)) {
|
||||
if (skill_used == EQEmu::skills::SkillBash) // Bash always will
|
||||
stun_chance = 100;
|
||||
else if (attacker->GetLevel() >= RuleI(Combat, ClientStunLevel))
|
||||
stun_chance = 100; // only if you're over level 55 and using kick
|
||||
} else { // higher than base immunity or Client vs. Client
|
||||
// not sure on this number, use same as NPC for now
|
||||
if (skill_used == EQEmu::skills::SkillKick && attacker->GetLevel() < RuleI(Combat, ClientStunLevel))
|
||||
stun_chance = RuleI(Combat, NPCBashKickStunChance);
|
||||
else if (skill_used == EQEmu::skills::SkillBash)
|
||||
stun_chance = RuleI(Combat, NPCBashKickStunChance) +
|
||||
attacker->spellbonuses.StunBashChance +
|
||||
attacker->itembonuses.StunBashChance +
|
||||
attacker->aabonuses.StunBashChance;
|
||||
}
|
||||
}
|
||||
// broken up for readability
|
||||
// This is based on what the client is doing
|
||||
// We had a bunch of stuff like BaseImmunityLevel checks, which I think is suppose to just be for spells
|
||||
// This is missing some merc checks, but those mostly just skipped the spell bonuses I think ...
|
||||
bool can_stun = false;
|
||||
int stunbash_chance = 0; // bonus
|
||||
if (attacker) {
|
||||
if (skill_used == EQEmu::skills::SkillBash) {
|
||||
can_stun = true;
|
||||
if (attacker->IsClient())
|
||||
stunbash_chance = attacker->spellbonuses.StunBashChance +
|
||||
attacker->itembonuses.StunBashChance +
|
||||
attacker->aabonuses.StunBashChance;
|
||||
} else if (skill_used == EQEmu::skills::SkillKick &&
|
||||
(attacker->GetLevel() > 55 || attacker->IsNPC()) && GetClass() == WARRIOR) {
|
||||
can_stun = true;
|
||||
}
|
||||
|
||||
if (stun_chance && zone->random.Roll(stun_chance)) {
|
||||
// Passed stun, try to resist now
|
||||
int stun_resist = itembonuses.StunResist + spellbonuses.StunResist;
|
||||
int frontal_stun_resist = itembonuses.FrontalStunResist + spellbonuses.FrontalStunResist;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Stun passed, checking resists. Was %d chance.", stun_chance);
|
||||
if (IsClient()) {
|
||||
stun_resist += aabonuses.StunResist;
|
||||
frontal_stun_resist += aabonuses.FrontalStunResist;
|
||||
}
|
||||
|
||||
// frontal stun check for ogres/bonuses
|
||||
if (((GetBaseRace() == OGRE && IsClient()) ||
|
||||
(frontal_stun_resist && zone->random.Roll(frontal_stun_resist))) &&
|
||||
!attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) {
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Frontal stun resisted. %d chance.", frontal_stun_resist);
|
||||
|
||||
} else {
|
||||
// Normal stun resist check.
|
||||
if (stun_resist && zone->random.Roll(stun_resist)) {
|
||||
if ((GetBaseRace() == OGRE || GetBaseRace() == OGGOK_CITIZEN) &&
|
||||
!attacker->BehindMob(this, attacker->GetX(), attacker->GetY()))
|
||||
can_stun = false;
|
||||
if (GetSpecialAbility(UNSTUNABLE))
|
||||
can_stun = false;
|
||||
}
|
||||
if (can_stun) {
|
||||
int bashsave_roll = zone->random.Int(0, 100);
|
||||
if (bashsave_roll > 98 || bashsave_roll > (55 - stunbash_chance)) {
|
||||
// did stun -- roll other resists
|
||||
// SE_FrontalStunResist description says any angle now a days
|
||||
int stun_resist2 = spellbonuses.FrontalStunResist + itembonuses.FrontalStunResist +
|
||||
aabonuses.FrontalStunResist;
|
||||
if (zone->random.Int(1, 100) > stun_resist2) {
|
||||
// stun resist 2 failed
|
||||
// time to check SE_StunResist and mod2 stun resist
|
||||
int stun_resist =
|
||||
spellbonuses.StunResist + itembonuses.StunResist + aabonuses.StunResist;
|
||||
if (zone->random.Int(0, 100) >= stun_resist) {
|
||||
// did stun
|
||||
// nothing else to check!
|
||||
Stun(2000); // straight 2 seconds every time
|
||||
} else {
|
||||
// stun resist passed!
|
||||
if (IsClient())
|
||||
Message_StringID(MT_Stun, SHAKE_OFF_STUN);
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Stun Resisted. %d chance.", stun_resist);
|
||||
} else {
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Stunned. %d resist chance.", stun_resist);
|
||||
Stun(zone->random.Int(0, 2) * 1000); // 0-2 seconds
|
||||
}
|
||||
} else {
|
||||
// stun resist 2 passed!
|
||||
if (IsClient())
|
||||
Message_StringID(MT_Stun, AVOID_STUNNING_BLOW);
|
||||
}
|
||||
} else {
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Stun failed. %d chance.", stun_chance);
|
||||
// main stun failed -- extra interrupt roll
|
||||
if (IsCasting() &&
|
||||
!EQEmu::ValueWithin(casting_spell_id, 859, 1023)) // these spells are excluded
|
||||
// 90% chance >< -- stun immune won't reach this branch though :(
|
||||
if (zone->random.Int(0, 9) > 1)
|
||||
InterruptSpell();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3268,7 +3261,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
a->damage = damage;
|
||||
a->spellid = spell_id;
|
||||
a->special = special;
|
||||
a->meleepush_xy = attacker->GetHeading() * 2.0f;
|
||||
a->meleepush_xy = attacker ? attacker->GetHeading() * 2.0f : 0.0f;
|
||||
if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() &&
|
||||
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
|
||||
a->force = EQEmu::skills::GetSkillMeleePushForce(skill_used);
|
||||
@ -3805,7 +3798,7 @@ void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage)
|
||||
|
||||
void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts)
|
||||
{
|
||||
if(damage < 1)
|
||||
if(damage < 1 || !defender)
|
||||
return;
|
||||
|
||||
// decided to branch this into it's own function since it's going to be duplicating a lot of the
|
||||
@ -3826,8 +3819,8 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
|
||||
bool IsBerskerSPA = false;
|
||||
|
||||
//1: Try Slay Undead
|
||||
if (defender && (defender->GetBodyType() == BT_Undead ||
|
||||
defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire)) {
|
||||
if (defender->GetBodyType() == BT_Undead ||
|
||||
defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire) {
|
||||
int32 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0];
|
||||
if (SlayRateBonus) {
|
||||
float slayChance = static_cast<float>(SlayRateBonus) / 10000.0f;
|
||||
|
||||
54
zone/bot.cpp
54
zone/bot.cpp
@ -1709,8 +1709,8 @@ bool Bot::LoadPet()
|
||||
|
||||
NPC *pet_inst = GetPet()->CastToNPC();
|
||||
|
||||
SpellBuff_Struct pet_buffs[BUFF_COUNT];
|
||||
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * BUFF_COUNT));
|
||||
SpellBuff_Struct pet_buffs[PET_BUFF_COUNT];
|
||||
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * PET_BUFF_COUNT));
|
||||
if (!botdb.LoadPetBuffs(GetBotID(), pet_buffs))
|
||||
bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetBuffs(), GetCleanName());
|
||||
|
||||
@ -1741,11 +1741,11 @@ bool Bot::SavePet()
|
||||
return false;
|
||||
|
||||
char* pet_name = new char[64];
|
||||
SpellBuff_Struct pet_buffs[BUFF_COUNT];
|
||||
SpellBuff_Struct pet_buffs[PET_BUFF_COUNT];
|
||||
uint32 pet_items[EQEmu::legacy::EQUIPMENT_SIZE];
|
||||
|
||||
memset(pet_name, 0, 64);
|
||||
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * BUFF_COUNT));
|
||||
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * PET_BUFF_COUNT));
|
||||
memset(pet_items, 0, (sizeof(uint32) * EQEmu::legacy::EQUIPMENT_SIZE));
|
||||
|
||||
pet_inst->GetPetState(pet_buffs, pet_items, pet_name);
|
||||
@ -2085,7 +2085,7 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills:
|
||||
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
|
||||
|
||||
if ((skillinuse == EQEmu::skills::SkillDragonPunch) && GetAA(aaDragonPunch) && zone->random.Int(0, 99) < 25){
|
||||
SpellFinished(904, other, 10, 0, -1, spells[904].ResistDiff);
|
||||
SpellFinished(904, other, EQEmu::CastingSlot::Item, 0, -1, spells[904].ResistDiff);
|
||||
other->Stun(100);
|
||||
}
|
||||
|
||||
@ -5900,7 +5900,7 @@ void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) {
|
||||
Mob::DoBuffTic(buff, slot, caster);
|
||||
}
|
||||
|
||||
bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost,
|
||||
bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot, int32 cast_time, int32 mana_cost,
|
||||
uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust, uint32 aa_id) {
|
||||
bool Result = false;
|
||||
if(zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) {
|
||||
@ -5920,7 +5920,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
|
||||
Message_StringID(13, MELEE_SILENCE);
|
||||
|
||||
if(casting_spell_id)
|
||||
AI_Event_SpellCastFinished(false, casting_spell_slot);
|
||||
AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -5929,7 +5929,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
|
||||
if(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()){
|
||||
Message_StringID(13, SPELL_WOULDNT_HOLD);
|
||||
if(casting_spell_id)
|
||||
AI_Event_SpellCastFinished(false, casting_spell_slot);
|
||||
AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -5940,7 +5940,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
|
||||
return false;
|
||||
}
|
||||
|
||||
if(slot < MAX_PP_MEMSPELL && !CheckFizzle(spell_id)) {
|
||||
if(slot < EQEmu::CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
|
||||
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
|
||||
InterruptSpell(fizzle_msg, 0x121, spell_id);
|
||||
|
||||
@ -5954,7 +5954,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Casting a new spell/song while singing a song. Killing old song %d.", bardsong);
|
||||
bardsong = 0;
|
||||
bardsong_target_id = 0;
|
||||
bardsong_slot = 0;
|
||||
bardsong_slot = EQEmu::CastingSlot::Gem1;
|
||||
bardsong_timer.Disable();
|
||||
}
|
||||
|
||||
@ -6084,7 +6084,7 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, uint16 slot) {
|
||||
bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQEmu::CastingSlot slot) {
|
||||
bool Result = false;
|
||||
SpellTargetType targetType = spells[spell_id].targettype;
|
||||
if(targetType == ST_GroupClientAndPet) {
|
||||
@ -6097,7 +6097,7 @@ bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, uint32 aa_id) {
|
||||
bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, uint32 aa_id) {
|
||||
bool Result = false;
|
||||
if(GetClass() == BARD)
|
||||
cast_time = 0;
|
||||
@ -6201,7 +6201,7 @@ void Bot::GenerateSpecialAttacks() {
|
||||
SetSpecialAbility(SPECATK_TRIPLE, 1);
|
||||
}
|
||||
|
||||
bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) {
|
||||
bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
|
||||
if(GetClass() == BARD) {
|
||||
if(!ApplyNextBardPulse(bardsong, this, bardsong_slot))
|
||||
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
|
||||
@ -6211,7 +6211,7 @@ bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) {
|
||||
bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
|
||||
if(spellTarget) {
|
||||
if(IsGrouped() && (spellTarget->IsBot() || spellTarget->IsClient()) && RuleB(Bots, GroupBuffing)) {
|
||||
bool noGroupSpell = false;
|
||||
@ -6223,7 +6223,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16
|
||||
bool spelltypeequal = ((spelltype == 2) || (spelltype == 16) || (spelltype == 32));
|
||||
bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].targettype == ST_Self));
|
||||
bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == SHAMAN));
|
||||
bool slotequal = (slot == USE_ITEM_SPELL_SLOT);
|
||||
bool slotequal = (slot == EQEmu::CastingSlot::Item);
|
||||
if(spellequal || slotequal) {
|
||||
if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) {
|
||||
if(((spells[thespell].effectid[0] == 0) && (spells[thespell].base[0] < 0)) &&
|
||||
@ -6262,7 +6262,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) {
|
||||
bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
|
||||
bool isMainGroupMGB = false;
|
||||
if(isMainGroupMGB && (GetClass() != BARD)) {
|
||||
BotGroupSay(this, "MGB %s", spells[spell_id].name);
|
||||
@ -8159,7 +8159,25 @@ bool Bot::GetNeedsCured(Mob *tar) {
|
||||
int buffsWithCounters = 0;
|
||||
needCured = true;
|
||||
for (unsigned int j = 0; j < buff_count; j++) {
|
||||
if(tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) {
|
||||
// this should prevent crashes until the cause can be found
|
||||
if (!tar->GetBuffs()) {
|
||||
std::string mob_type = "Unknown";
|
||||
if (tar->IsClient())
|
||||
mob_type = "Client";
|
||||
else if (tar->IsBot())
|
||||
mob_type = "Bot";
|
||||
else if (tar->IsMerc())
|
||||
mob_type = "Merc";
|
||||
else if (tar->IsPet())
|
||||
mob_type = "Pet";
|
||||
else if (tar->IsNPC())
|
||||
mob_type = "NPC";
|
||||
|
||||
Log.Out(Logs::General, Logs::Error, "Bot::GetNeedsCured() processed mob type '%s' with a null buffs pointer (mob: '%s')", mob_type.c_str(), tar->GetName());
|
||||
|
||||
continue;
|
||||
}
|
||||
else if(tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) {
|
||||
if(CalculateCounters(tar->GetBuffs()[j].spellid) > 0) {
|
||||
buffsWithCounters++;
|
||||
if(buffsWithCounters == 1 && (tar->GetBuffs()[j].ticsremaining < 2 || (int32)((tar->GetBuffs()[j].ticsremaining * 6) / tar->GetBuffs()[j].counters) < 2)) {
|
||||
@ -8251,7 +8269,7 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
if(IsCasting())
|
||||
InterruptSpell();
|
||||
|
||||
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT);
|
||||
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
12
zone/bot.h
12
zone/bot.h
@ -274,9 +274,9 @@ public:
|
||||
virtual void SetAttackTimer();
|
||||
uint32 GetClassHPFactor();
|
||||
virtual int32 CalcMaxHP();
|
||||
bool DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic);
|
||||
bool DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic);
|
||||
bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic);
|
||||
bool DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
|
||||
bool DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
|
||||
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);
|
||||
@ -374,12 +374,12 @@ public:
|
||||
virtual float GetAOERange(uint16 spell_id);
|
||||
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
|
||||
virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
|
||||
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0,
|
||||
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0,
|
||||
uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr, uint32 aa_id = 0);
|
||||
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
|
||||
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
|
||||
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, uint16 slot);
|
||||
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
|
||||
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQEmu::CastingSlot slot);
|
||||
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
|
||||
|
||||
// Bot Equipment & Inventory Class Methods
|
||||
void BotTradeSwapItem(Client* client, int16 lootSlot, const ItemInst* inst, const ItemInst* inst_swap, uint32 equipableSlots, std::string* errorMessage, bool swap = true);
|
||||
|
||||
@ -7613,7 +7613,7 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id,
|
||||
if (annouce_cast)
|
||||
Bot::BotGroupSay(casting_bot, "Attempting to cast '%s' on %s", spells[spell_id].name, target_mob->GetCleanName());
|
||||
|
||||
return casting_bot->CastSpell(spell_id, target_mob->GetID(), 1, -1, -1, dont_root_before);
|
||||
return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQEmu::CastingSlot::Gem2, -1, -1, dont_root_before);
|
||||
}
|
||||
|
||||
bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command)
|
||||
|
||||
@ -1472,7 +1472,7 @@ bool BotDatabase::LoadPetBuffs(const uint32 bot_id, SpellBuff_Struct* pet_buffs)
|
||||
return true;
|
||||
|
||||
int buff_index = 0;
|
||||
for (auto row = results.begin(); row != results.end() && buff_index < BUFF_COUNT; ++row) {
|
||||
for (auto row = results.begin(); row != results.end() && buff_index < PET_BUFF_COUNT; ++row) {
|
||||
pet_buffs[buff_index].spellid = atoi(row[0]);
|
||||
pet_buffs[buff_index].level = atoi(row[1]);
|
||||
pet_buffs[buff_index].duration = atoi(row[2]);
|
||||
@ -1509,7 +1509,7 @@ bool BotDatabase::SavePetBuffs(const uint32 bot_id, const SpellBuff_Struct* pet_
|
||||
if (!saved_pet_index)
|
||||
return true;
|
||||
|
||||
for (int buff_index = 0; buff_index < BUFF_COUNT; ++buff_index) {
|
||||
for (int buff_index = 0; buff_index < PET_BUFF_COUNT; ++buff_index) {
|
||||
if (!pet_buffs[buff_index].spellid || pet_buffs[buff_index].spellid == SPELL_UNKNOWN)
|
||||
continue;
|
||||
|
||||
|
||||
156
zone/client.cpp
156
zone/client.cpp
@ -44,6 +44,7 @@ extern volatile bool RunLoops;
|
||||
#include "zonedb.h"
|
||||
#include "petitions.h"
|
||||
#include "command.h"
|
||||
#include "water_map.h"
|
||||
#ifdef BOTS
|
||||
#include "bot_command.h"
|
||||
#endif
|
||||
@ -155,7 +156,8 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
|
||||
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
|
||||
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f)
|
||||
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
|
||||
last_region_type(RegionTypeUnsupported)
|
||||
{
|
||||
for(int cf=0; cf < _FilterCount; cf++)
|
||||
ClientFilters[cf] = FilterShow;
|
||||
@ -565,6 +567,11 @@ bool Client::SaveAA() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::RemoveExpendedAA(int aa_id)
|
||||
{
|
||||
database.QueryDatabase(StringFormat("DELETE from `character_alternate_abilities` WHERE `id` = %d and `aa_id` = %d", character_id, aa_id));
|
||||
}
|
||||
|
||||
bool Client::Save(uint8 iCommitNow) {
|
||||
if(!ClientDataLoaded())
|
||||
return false;
|
||||
@ -1787,7 +1794,7 @@ const int32& Client::SetMana(int32 amount) {
|
||||
}
|
||||
|
||||
void Client::SendManaUpdatePacket() {
|
||||
if (!Connected() || IsCasting())
|
||||
if (!Connected())
|
||||
return;
|
||||
|
||||
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
|
||||
@ -1801,7 +1808,8 @@ void Client::SendManaUpdatePacket() {
|
||||
ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer;
|
||||
manachange->new_mana = cur_mana;
|
||||
manachange->stamina = cur_end;
|
||||
manachange->spell_id = casting_spell_id; //always going to be 0... since we check IsCasting()
|
||||
manachange->spell_id = casting_spell_id;
|
||||
manachange->keepcasting = 1;
|
||||
outapp->priority = 6;
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
@ -2476,13 +2484,15 @@ uint16 Client::GetMaxSkillAfterSpecializationRules(EQEmu::skills::SkillType skil
|
||||
return Result;
|
||||
}
|
||||
|
||||
void Client::SetPVP(bool toggle) {
|
||||
void Client::SetPVP(bool toggle, bool message) {
|
||||
m_pp.pvp = toggle ? 1 : 0;
|
||||
|
||||
if(GetPVP())
|
||||
this->Message_StringID(MT_Shout,PVP_ON);
|
||||
else
|
||||
Message(13, "You no longer follow the ways of discord.");
|
||||
if (message) {
|
||||
if(GetPVP())
|
||||
this->Message_StringID(MT_Shout,PVP_ON);
|
||||
else
|
||||
Message(13, "You no longer follow the ways of discord.");
|
||||
}
|
||||
|
||||
SendAppearancePacket(AT_PVP, GetPVP());
|
||||
Save();
|
||||
@ -3674,58 +3684,58 @@ void Client::SacrificeConfirm(Client *caster)
|
||||
//Essentially a special case death function
|
||||
void Client::Sacrifice(Client *caster)
|
||||
{
|
||||
if(GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)){
|
||||
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
|
||||
if(exploss < GetEXP()){
|
||||
SetEXP(GetEXP()-exploss, GetAAXP());
|
||||
SendLogoutPackets();
|
||||
if (GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)) {
|
||||
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
|
||||
if (exploss < GetEXP()) {
|
||||
SetEXP(GetEXP() - exploss, GetAAXP());
|
||||
SendLogoutPackets();
|
||||
|
||||
//make our become corpse packet, and queue to ourself before OP_Death.
|
||||
EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct));
|
||||
BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer;
|
||||
bc->spawn_id = GetID();
|
||||
bc->x = GetX();
|
||||
bc->y = GetY();
|
||||
bc->z = GetZ();
|
||||
QueuePacket(&app2);
|
||||
// make our become corpse packet, and queue to ourself before OP_Death.
|
||||
EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct));
|
||||
BecomeCorpse_Struct *bc = (BecomeCorpse_Struct *)app2.pBuffer;
|
||||
bc->spawn_id = GetID();
|
||||
bc->x = GetX();
|
||||
bc->y = GetY();
|
||||
bc->z = GetZ();
|
||||
QueuePacket(&app2);
|
||||
|
||||
// make death packet
|
||||
EQApplicationPacket app(OP_Death, sizeof(Death_Struct));
|
||||
Death_Struct* d = (Death_Struct*)app.pBuffer;
|
||||
d->spawn_id = GetID();
|
||||
d->killer_id = caster ? caster->GetID() : 0;
|
||||
d->bindzoneid = GetPP().binds[0].zoneId;
|
||||
d->spell_id = SPELL_UNKNOWN;
|
||||
d->attack_skill = 0xe7;
|
||||
d->damage = 0;
|
||||
app.priority = 6;
|
||||
entity_list.QueueClients(this, &app);
|
||||
// make death packet
|
||||
EQApplicationPacket app(OP_Death, sizeof(Death_Struct));
|
||||
Death_Struct *d = (Death_Struct *)app.pBuffer;
|
||||
d->spawn_id = GetID();
|
||||
d->killer_id = caster ? caster->GetID() : 0;
|
||||
d->bindzoneid = GetPP().binds[0].zoneId;
|
||||
d->spell_id = SPELL_UNKNOWN;
|
||||
d->attack_skill = 0xe7;
|
||||
d->damage = 0;
|
||||
app.priority = 6;
|
||||
entity_list.QueueClients(this, &app);
|
||||
|
||||
BuffFadeAll();
|
||||
UnmemSpellAll();
|
||||
Group *g = GetGroup();
|
||||
if(g){
|
||||
g->MemberZoned(this);
|
||||
}
|
||||
Raid *r = entity_list.GetRaidByClient(this);
|
||||
if(r){
|
||||
r->MemberZoned(this);
|
||||
}
|
||||
ClearAllProximities();
|
||||
if(RuleB(Character, LeaveCorpses)){
|
||||
auto new_corpse = new Corpse(this, 0);
|
||||
entity_list.AddCorpse(new_corpse, GetID());
|
||||
SetID(0);
|
||||
entity_list.QueueClients(this, &app2, true);
|
||||
}
|
||||
Save();
|
||||
GoToDeath();
|
||||
caster->SummonItem(RuleI(Spells, SacrificeItemID));
|
||||
}
|
||||
}
|
||||
else{
|
||||
caster->Message_StringID(13, SAC_TOO_LOW); //This being is not a worthy sacrifice.
|
||||
}
|
||||
BuffFadeAll();
|
||||
UnmemSpellAll();
|
||||
Group *g = GetGroup();
|
||||
if (g) {
|
||||
g->MemberZoned(this);
|
||||
}
|
||||
Raid *r = entity_list.GetRaidByClient(this);
|
||||
if (r) {
|
||||
r->MemberZoned(this);
|
||||
}
|
||||
ClearAllProximities();
|
||||
if (RuleB(Character, LeaveCorpses)) {
|
||||
auto new_corpse = new Corpse(this, 0);
|
||||
entity_list.AddCorpse(new_corpse, GetID());
|
||||
SetID(0);
|
||||
entity_list.QueueClients(this, &app2, true);
|
||||
}
|
||||
Save();
|
||||
GoToDeath();
|
||||
if (caster) // I guess it's possible?
|
||||
caster->SummonItem(RuleI(Spells, SacrificeItemID));
|
||||
}
|
||||
} else {
|
||||
caster->Message_StringID(13, SAC_TOO_LOW); // This being is not a worthy sacrifice.
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) {
|
||||
@ -4604,7 +4614,7 @@ void Client::HandleLDoNOpen(NPC *target)
|
||||
if(target->GetLDoNTrapSpellID() != 0)
|
||||
{
|
||||
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
|
||||
target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
|
||||
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
|
||||
target->SetLDoNTrapSpellID(0);
|
||||
target->SetLDoNTrapped(false);
|
||||
target->SetLDoNTrapDetected(false);
|
||||
@ -4726,7 +4736,7 @@ void Client::HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type)
|
||||
break;
|
||||
case -1:
|
||||
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
|
||||
target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
|
||||
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
|
||||
target->SetLDoNTrapSpellID(0);
|
||||
target->SetLDoNTrapped(false);
|
||||
target->SetLDoNTrapDetected(false);
|
||||
@ -4745,7 +4755,7 @@ void Client::HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type)
|
||||
if(target->IsLDoNTrapped())
|
||||
{
|
||||
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
|
||||
target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
|
||||
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
|
||||
target->SetLDoNTrapSpellID(0);
|
||||
target->SetLDoNTrapped(false);
|
||||
target->SetLDoNTrapDetected(false);
|
||||
@ -8396,7 +8406,7 @@ void Client::SendColoredText(uint32 color, std::string message)
|
||||
void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction) {
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct));
|
||||
memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer));
|
||||
memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct));
|
||||
QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer;
|
||||
|
||||
qr->mob_id = target->GetID(); // Entity ID for the from mob name
|
||||
@ -8527,3 +8537,27 @@ uint32 Client::GetMoney(uint8 type, uint8 subtype) {
|
||||
int Client::GetAccountAge() {
|
||||
return (time(nullptr) - GetAccountCreation());
|
||||
}
|
||||
|
||||
void Client::CheckRegionTypeChanges()
|
||||
{
|
||||
if (!zone->HasWaterMap())
|
||||
return;
|
||||
|
||||
auto new_region = zone->watermap->ReturnRegionType(glm::vec3(m_Position));
|
||||
|
||||
// still same region, do nothing
|
||||
if (last_region_type == new_region)
|
||||
return;
|
||||
|
||||
// region type changed
|
||||
last_region_type = new_region;
|
||||
|
||||
// PVP is the only state we need to keep track of, so we can just return now for PVP servers
|
||||
if (RuleI(World, PVPSettings) > 0)
|
||||
return;
|
||||
|
||||
if (last_region_type == RegionTypePVP)
|
||||
SetPVP(true, false);
|
||||
else if (GetPVP())
|
||||
SetPVP(false, false);
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ class Object;
|
||||
class Raid;
|
||||
class Seperator;
|
||||
class ServerPacket;
|
||||
enum WaterRegionType : int;
|
||||
|
||||
namespace EQEmu
|
||||
{
|
||||
@ -105,6 +106,7 @@ enum { //Type arguments to the Message* routines.
|
||||
|
||||
#define SPELLBAR_UNLOCK 0x2bc
|
||||
enum { //scribing argument to MemorizeSpell
|
||||
memSpellUnknown = -1, // this modifies some state data
|
||||
memSpellScribing = 0,
|
||||
memSpellMemorize = 1,
|
||||
memSpellForget = 2,
|
||||
@ -326,6 +328,7 @@ public:
|
||||
/* New PP Save Functions */
|
||||
bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); }
|
||||
bool SaveAA();
|
||||
void RemoveExpendedAA(int aa_id);
|
||||
|
||||
inline bool ClientDataLoaded() const { return client_data_loaded; }
|
||||
inline bool Connected() const { return (client_state == CLIENT_CONNECTED); }
|
||||
@ -359,9 +362,9 @@ public:
|
||||
int32 LevelRegen();
|
||||
void HPTick();
|
||||
void SetGM(bool toggle);
|
||||
void SetPVP(bool toggle);
|
||||
void SetPVP(bool toggle, bool message = true);
|
||||
|
||||
inline bool GetPVP() const { return zone->GetZoneID() == 77 ? true : (m_pp.pvp != 0); }
|
||||
inline bool GetPVP() const { return m_pp.pvp != 0; }
|
||||
inline bool GetGM() const { return m_pp.gm != 0; }
|
||||
|
||||
inline void SetBaseClass(uint32 i) { m_pp.class_=i; }
|
||||
@ -510,6 +513,8 @@ public:
|
||||
virtual int GetMaxSongSlots() const { return 12; }
|
||||
virtual int GetMaxDiscSlots() const { return 1; }
|
||||
virtual int GetMaxTotalSlots() const { return 38; }
|
||||
virtual uint32 GetFirstBuffSlot(bool disc, bool song);
|
||||
virtual uint32 GetLastBuffSlot(bool disc, bool song);
|
||||
virtual void InitializeBuffSlots();
|
||||
virtual void UninitializeBuffSlots();
|
||||
|
||||
@ -889,6 +894,9 @@ public:
|
||||
void SendDisciplineTimer(uint32 timer_id, uint32 duration);
|
||||
bool UseDiscipline(uint32 spell_id, uint32 target);
|
||||
|
||||
void SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration);
|
||||
bool IsLinkedSpellReuseTimerReady(uint32 timer_id);
|
||||
|
||||
bool CheckTitle(int titleset);
|
||||
void EnableTitle(int titleset);
|
||||
void RemoveTitle(int titleset);
|
||||
@ -1230,6 +1238,8 @@ public:
|
||||
|
||||
void SendHPUpdateMarquee();
|
||||
|
||||
void CheckRegionTypeChanges();
|
||||
|
||||
protected:
|
||||
friend class Mob;
|
||||
void CalcItemBonuses(StatBonuses* newbon);
|
||||
@ -1414,6 +1424,7 @@ private:
|
||||
uint8 zonesummon_ignorerestrictions;
|
||||
ZoneMode zone_mode;
|
||||
|
||||
WaterRegionType last_region_type;
|
||||
|
||||
Timer position_timer;
|
||||
uint8 position_timer_counter;
|
||||
|
||||
@ -1036,6 +1036,13 @@ int32 Client::CalcAC()
|
||||
if (avoidance < 0) {
|
||||
avoidance = 0;
|
||||
}
|
||||
|
||||
if (RuleB(Character, EnableAvoidanceCap)) {
|
||||
if (avoidance > RuleI(Character, AvoidanceCap)) {
|
||||
avoidance = RuleI(Character, AvoidanceCap);
|
||||
}
|
||||
}
|
||||
|
||||
int mitigation = 0;
|
||||
if (m_pp.class_ == WIZARD || m_pp.class_ == MAGICIAN || m_pp.class_ == NECROMANCER || m_pp.class_ == ENCHANTER) {
|
||||
//something is wrong with this, naked casters have the wrong natural AC
|
||||
@ -1113,6 +1120,13 @@ int32 Client::GetACAvoid()
|
||||
if (avoidance < 0) {
|
||||
avoidance = 0;
|
||||
}
|
||||
|
||||
if (RuleB(Character, EnableAvoidanceCap)) {
|
||||
if ((avoidance * 1000 / 847) > RuleI(Character, AvoidanceCap)) {
|
||||
return RuleI(Character, AvoidanceCap);
|
||||
}
|
||||
}
|
||||
|
||||
return (avoidance * 1000 / 847);
|
||||
}
|
||||
|
||||
|
||||
@ -502,6 +502,27 @@ void Client::CompleteConnect()
|
||||
SetDuelTarget(0);
|
||||
SetDueling(false);
|
||||
|
||||
database.LoadPetInfo(this);
|
||||
/*
|
||||
This was moved before the spawn packets are sent
|
||||
in hopes that it adds more consistency...
|
||||
Remake pet
|
||||
*/
|
||||
if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) {
|
||||
MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size);
|
||||
if (GetPet() && GetPet()->IsNPC()) {
|
||||
NPC *pet = GetPet()->CastToNPC();
|
||||
pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items);
|
||||
pet->CalcBonuses();
|
||||
pet->SetHP(m_petinfo.HP);
|
||||
pet->SetMana(m_petinfo.Mana);
|
||||
}
|
||||
m_petinfo.SpellID = 0;
|
||||
}
|
||||
/* Moved here so it's after where we load the pet data. */
|
||||
if (!GetAA(aaPersistentMinion))
|
||||
memset(&m_suspendedminion, 0, sizeof(PetInfo));
|
||||
|
||||
EnteringMessages(this);
|
||||
LoadZoneFlags();
|
||||
|
||||
@ -1628,27 +1649,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
if (m_pp.RestTimer)
|
||||
rest_timer.Start(m_pp.RestTimer * 1000);
|
||||
|
||||
database.LoadPetInfo(this);
|
||||
/*
|
||||
This was moved before the spawn packets are sent
|
||||
in hopes that it adds more consistency...
|
||||
Remake pet
|
||||
*/
|
||||
if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) {
|
||||
MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size);
|
||||
if (GetPet() && GetPet()->IsNPC()) {
|
||||
NPC *pet = GetPet()->CastToNPC();
|
||||
pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items);
|
||||
pet->CalcBonuses();
|
||||
pet->SetHP(m_petinfo.HP);
|
||||
pet->SetMana(m_petinfo.Mana);
|
||||
}
|
||||
m_petinfo.SpellID = 0;
|
||||
}
|
||||
/* Moved here so it's after where we load the pet data. */
|
||||
if (!GetAA(aaPersistentMinion))
|
||||
memset(&m_suspendedminion, 0, sizeof(PetInfo));
|
||||
|
||||
/* Server Zone Entry Packet */
|
||||
outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct));
|
||||
ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer;
|
||||
@ -3966,6 +3966,7 @@ void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
{
|
||||
using EQEmu::CastingSlot;
|
||||
if (app->size != sizeof(CastSpell_Struct)) {
|
||||
std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl;
|
||||
return;
|
||||
@ -3978,13 +3979,13 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
|
||||
CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer;
|
||||
|
||||
m_TargetRing = glm::vec3(castspell->x_pos, castspell->y_pos, castspell->z_pos);
|
||||
m_TargetRing = glm::vec3(castspell->x_pos, castspell->y_pos, castspell->z_pos);
|
||||
|
||||
Log.Out(Logs::General, Logs::Spells, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot);
|
||||
CastingSlot slot = static_cast<CastingSlot>(castspell->slot);
|
||||
|
||||
/* Memorized Spell */
|
||||
if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){
|
||||
|
||||
if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id) {
|
||||
uint16 spell_to_cast = 0;
|
||||
if (castspell->slot < MAX_PP_MEMSPELL) {
|
||||
spell_to_cast = m_pp.mem_spells[castspell->slot];
|
||||
@ -3998,20 +3999,12 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
CastSpell(spell_to_cast, castspell->target_id, castspell->slot);
|
||||
CastSpell(spell_to_cast, castspell->target_id, slot);
|
||||
}
|
||||
/* Spell Slot or Potion Belt Slot */
|
||||
else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)|| (castspell->slot == TARGET_RING_SPELL_SLOT)) // ITEM or POTION cast
|
||||
else if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) // ITEM or POTION cast
|
||||
{
|
||||
//discipline, using the item spell slot
|
||||
if (castspell->inventoryslot == INVALID_INDEX) {
|
||||
if (!UseDiscipline(castspell->spell_id, castspell->target_id)) {
|
||||
Log.Out(Logs::General, Logs::Spells, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id);
|
||||
InterruptSpell(castspell->spell_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (m_inv.SupportsClickCasting(castspell->inventoryslot) || (castspell->slot == POTION_BELT_SPELL_SLOT) || (castspell->slot == TARGET_RING_SPELL_SLOT)) // sanity check
|
||||
if (m_inv.SupportsClickCasting(castspell->inventoryslot) || slot == CastingSlot::PotionBelt) // sanity check
|
||||
{
|
||||
// packet field types will be reviewed as packet transistions occur
|
||||
const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field
|
||||
@ -4036,7 +4029,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot);
|
||||
|
||||
if (i == 0) {
|
||||
CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot);
|
||||
CastSpell(item->Click.Effect, castspell->target_id, slot, item->CastTime, 0, 0, castspell->inventoryslot);
|
||||
}
|
||||
else {
|
||||
InterruptSpell(castspell->spell_id);
|
||||
@ -4056,7 +4049,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot);
|
||||
|
||||
if (i == 0) {
|
||||
CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot);
|
||||
CastSpell(item->Click.Effect, castspell->target_id, slot, item->CastTime, 0, 0, castspell->inventoryslot);
|
||||
}
|
||||
else {
|
||||
InterruptSpell(castspell->spell_id);
|
||||
@ -4081,8 +4074,8 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
InterruptSpell(castspell->spell_id);
|
||||
}
|
||||
}
|
||||
/* Discipline */
|
||||
else if (castspell->slot == DISCIPLINE_SPELL_SLOT) {
|
||||
/* Discipline -- older clients use the same slot as items, but we translate to it's own */
|
||||
else if (slot == CastingSlot::Discipline) {
|
||||
if (!UseDiscipline(castspell->spell_id, castspell->target_id)) {
|
||||
Log.Out(Logs::General, Logs::Spells, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id);
|
||||
InterruptSpell(castspell->spell_id);
|
||||
@ -4090,7 +4083,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
/* ABILITY cast (LoH and Harm Touch) */
|
||||
else if (castspell->slot == ABILITY_SPELL_SLOT) {
|
||||
else if (slot == CastingSlot::Ability) {
|
||||
uint16 spell_to_cast = 0;
|
||||
|
||||
if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) {
|
||||
@ -4120,7 +4113,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
if (spell_to_cast > 0) // if we've matched LoH or HT, cast now
|
||||
CastSpell(spell_to_cast, castspell->target_id, castspell->slot);
|
||||
CastSpell(spell_to_cast, castspell->target_id, slot);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -4583,8 +4576,11 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
if(zone->watermap && zone->watermap->InLiquid(glm::vec3(m_Position)))
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillSwimming, nullptr, -17);
|
||||
if (zone->watermap) {
|
||||
if (zone->watermap->InLiquid(glm::vec3(m_Position)))
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillSwimming, nullptr, -17);
|
||||
CheckRegionTypeChanges();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -8410,6 +8406,7 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
{
|
||||
using EQEmu::CastingSlot;
|
||||
if (app->size != sizeof(ItemVerifyRequest_Struct))
|
||||
{
|
||||
Log.Out(Logs::General, Logs::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size);
|
||||
@ -8554,7 +8551,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
if (i == 0) {
|
||||
if (!IsCastWhileInvis(item->Click.Effect))
|
||||
CommonBreakInvisible(); // client can't do this for us :(
|
||||
CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id);
|
||||
CastSpell(item->Click.Effect, target_id, CastingSlot::Item, item->CastTime, 0, 0, slot_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -8583,7 +8580,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
if (i == 0) {
|
||||
if (!IsCastWhileInvis(augitem->Click.Effect))
|
||||
CommonBreakInvisible(); // client can't do this for us :(
|
||||
CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id);
|
||||
CastSpell(augitem->Click.Effect, target_id, CastingSlot::Item, augitem->CastTime, 0, 0, slot_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -624,7 +624,7 @@ bool Client::Process() {
|
||||
{
|
||||
//client logged out or errored out
|
||||
//ResetTrade();
|
||||
if (client_state != CLIENT_KICKED) {
|
||||
if (client_state != CLIENT_KICKED && !zoning && !instalog) {
|
||||
Save();
|
||||
}
|
||||
|
||||
|
||||
@ -2199,14 +2199,14 @@ void command_castspell(Client *c, const Seperator *sep)
|
||||
else
|
||||
if (c->GetTarget() == 0)
|
||||
if(c->Admin() >= commandInstacast)
|
||||
c->SpellFinished(spellid, 0, USE_ITEM_SPELL_SLOT, 0, -1, spells[spellid].ResistDiff);
|
||||
c->SpellFinished(spellid, 0, EQEmu::CastingSlot::Item, 0, -1, spells[spellid].ResistDiff);
|
||||
else
|
||||
c->CastSpell(spellid, 0, USE_ITEM_SPELL_SLOT, 0);
|
||||
c->CastSpell(spellid, 0, EQEmu::CastingSlot::Item, 0);
|
||||
else
|
||||
if(c->Admin() >= commandInstacast)
|
||||
c->SpellFinished(spellid, c->GetTarget(), 10, 0, -1, spells[spellid].ResistDiff);
|
||||
c->SpellFinished(spellid, c->GetTarget(), EQEmu::CastingSlot::Item, 0, -1, spells[spellid].ResistDiff);
|
||||
else
|
||||
c->CastSpell(spellid, c->GetTarget()->GetID(), USE_ITEM_SPELL_SLOT, 0);
|
||||
c->CastSpell(spellid, c->GetTarget()->GetID(), EQEmu::CastingSlot::Item, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4339,11 +4339,15 @@ void command_goto(Client *c, const Seperator *sep)
|
||||
void command_iteminfo(Client *c, const Seperator *sep)
|
||||
{
|
||||
auto inst = c->GetInv()[EQEmu::legacy::SlotCursor];
|
||||
if (!inst) { c->Message(13, "Error: You need an item on your cursor for this command"); }
|
||||
if (!inst) {
|
||||
c->Message(13, "Error: You need an item on your cursor for this command");
|
||||
return;
|
||||
}
|
||||
auto item = inst->GetItem();
|
||||
if (!item) {
|
||||
Log.Out(Logs::General, Logs::Inventory, "(%s) Command #iteminfo processed an item with no data pointer");
|
||||
c->Message(13, "Error: This item has no data reference");
|
||||
return;
|
||||
}
|
||||
|
||||
EQEmu::SayLinkEngine linker;
|
||||
|
||||
@ -17,13 +17,6 @@
|
||||
#define _NPCPET(x) (x && x->IsNPC() && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsNPC())
|
||||
#define _BECOMENPCPET(x) (x && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsClient() && x->CastToMob()->GetOwner()->CastToClient()->IsBecomeNPC())
|
||||
|
||||
#define USE_ITEM_SPELL_SLOT 10
|
||||
#define POTION_BELT_SPELL_SLOT 11
|
||||
#define TARGET_RING_SPELL_SLOT 12
|
||||
#define DISCIPLINE_SPELL_SLOT 10
|
||||
#define ABILITY_SPELL_SLOT 9
|
||||
#define ALTERNATE_ABILITY_SPELL_SLOT 0xFF
|
||||
|
||||
//LOS Parameters:
|
||||
#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from
|
||||
#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS
|
||||
|
||||
@ -1163,9 +1163,9 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
}
|
||||
|
||||
char buf[88];
|
||||
char corpse_name[64];
|
||||
strcpy(corpse_name, corpse_name);
|
||||
snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(corpse_name));
|
||||
char q_corpse_name[64];
|
||||
strcpy(q_corpse_name, corpse_name);
|
||||
snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(q_corpse_name));
|
||||
buf[87] = '\0';
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
|
||||
@ -684,9 +684,9 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
}
|
||||
|
||||
if (reduced_recast > 0)
|
||||
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
|
||||
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
|
||||
else{
|
||||
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT);
|
||||
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -694,7 +694,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
}
|
||||
else
|
||||
{
|
||||
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT);
|
||||
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
@ -927,12 +927,18 @@ bool EntityList::MakeDoorSpawnPacket(EQApplicationPacket *app, Client *client)
|
||||
|
||||
Entity *EntityList::GetEntityMob(uint16 id)
|
||||
{
|
||||
return mob_list.count(id) ? mob_list.at(id) : nullptr;
|
||||
auto it = mob_list.find(id);
|
||||
if (it != mob_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *EntityList::GetEntityMerc(uint16 id)
|
||||
{
|
||||
return merc_list.count(id) ? merc_list.at(id) : nullptr;
|
||||
auto it = merc_list.find(id);
|
||||
if (it != merc_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *EntityList::GetEntityMob(const char *name)
|
||||
@ -952,12 +958,18 @@ Entity *EntityList::GetEntityMob(const char *name)
|
||||
|
||||
Entity *EntityList::GetEntityDoor(uint16 id)
|
||||
{
|
||||
return door_list.count(id) ? door_list.at(id) : nullptr;
|
||||
auto it = door_list.find(id);
|
||||
if (it != door_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *EntityList::GetEntityCorpse(uint16 id)
|
||||
{
|
||||
return corpse_list.count(id) ? corpse_list.at(id) : nullptr;
|
||||
auto it = corpse_list.find(id);
|
||||
if (it != corpse_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *EntityList::GetEntityCorpse(const char *name)
|
||||
@ -977,22 +989,34 @@ Entity *EntityList::GetEntityCorpse(const char *name)
|
||||
|
||||
Entity *EntityList::GetEntityTrap(uint16 id)
|
||||
{
|
||||
return trap_list.count(id) ? trap_list.at(id) : nullptr;
|
||||
auto it = trap_list.find(id);
|
||||
if (it != trap_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *EntityList::GetEntityObject(uint16 id)
|
||||
{
|
||||
return object_list.count(id) ? object_list.at(id) : nullptr;
|
||||
auto it = object_list.find(id);
|
||||
if (it != object_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *EntityList::GetEntityBeacon(uint16 id)
|
||||
{
|
||||
return beacon_list.count(id) ? beacon_list.at(id) : nullptr;
|
||||
auto it = beacon_list.find(id);
|
||||
if (it != beacon_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *EntityList::GetEntityEncounter(uint16 id)
|
||||
{
|
||||
return encounter_list.count(id) ? encounter_list.at(id) : nullptr;
|
||||
auto it = encounter_list.find(id);
|
||||
if (it != encounter_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *EntityList::GetID(uint16 get_id)
|
||||
@ -1188,6 +1212,8 @@ void EntityList::ChannelMessage(Mob *from, uint8 chan_num, uint8 language,
|
||||
|
||||
void EntityList::ChannelMessageSend(Mob *to, uint8 chan_num, uint8 language, const char *message, ...)
|
||||
{
|
||||
if (!to->IsClient())
|
||||
return;
|
||||
va_list argptr;
|
||||
char buffer[4096];
|
||||
|
||||
@ -1195,8 +1221,7 @@ void EntityList::ChannelMessageSend(Mob *to, uint8 chan_num, uint8 language, con
|
||||
vsnprintf(buffer, 4096, message, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
if (client_list.count(to->GetID()))
|
||||
client_list.at(to->GetID())->ChannelMessageSend(0, 0, chan_num, language, buffer);
|
||||
to->CastToClient()->ChannelMessageSend(0, 0, chan_num, language, buffer);
|
||||
}
|
||||
|
||||
void EntityList::SendZoneSpawns(Client *client)
|
||||
@ -1232,7 +1257,9 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
|
||||
maxspawns = mob_list.size();
|
||||
auto bzsp = new BulkZoneSpawnPacket(client, maxspawns);
|
||||
|
||||
int32 race=-1;
|
||||
bool delaypkt = false;
|
||||
const glm::vec4& cpos = client->GetPosition();
|
||||
const float dmax = 600.0 * 600.0;
|
||||
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
|
||||
spawn = it->second;
|
||||
if (spawn && spawn->GetID() > 0 && spawn->Spawned()) {
|
||||
@ -1240,8 +1267,30 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
|
||||
spawn->CastToClient()->IsHoveringForRespawn()))
|
||||
continue;
|
||||
|
||||
race = spawn->GetRace();
|
||||
#if 1
|
||||
const glm::vec4& spos = spawn->GetPosition();
|
||||
|
||||
delaypkt = false;
|
||||
if (DistanceSquared(cpos, spos) > dmax || (spawn->IsClient() && (spawn->GetRace() == MINOR_ILL_OBJ || spawn->GetRace() == TREE)))
|
||||
delaypkt = true;
|
||||
|
||||
if (delaypkt) {
|
||||
app = new EQApplicationPacket;
|
||||
spawn->CreateSpawnPacket(app);
|
||||
client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
|
||||
safe_delete(app);
|
||||
}
|
||||
else {
|
||||
memset(&ns, 0, sizeof(NewSpawn_Struct));
|
||||
spawn->FillSpawnStruct(&ns, client);
|
||||
bzsp->AddSpawn(&ns);
|
||||
}
|
||||
|
||||
spawn->SendArmorAppearance(client);
|
||||
#else
|
||||
/* original code kept for spawn packet research */
|
||||
int32 race = spawn->GetRace();
|
||||
|
||||
// Illusion races on PCs don't work as a mass spawn
|
||||
// But they will work as an add_spawn AFTER CLIENT_CONNECTED.
|
||||
if (spawn->IsClient() && (race == MINOR_ILL_OBJ || race == TREE)) {
|
||||
@ -1259,6 +1308,7 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
|
||||
// Despite being sent in the OP_ZoneSpawns packet, the client
|
||||
// does not display worn armor correctly so display it.
|
||||
spawn->SendArmorAppearance(client);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
safe_delete(bzsp);
|
||||
|
||||
@ -148,14 +148,29 @@ public:
|
||||
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
|
||||
Mob *GetTargetForVirus(Mob* spreader, int range);
|
||||
inline NPC *GetNPCByID(uint16 id)
|
||||
{ return npc_list.count(id) ? npc_list.at(id) : nullptr; }
|
||||
{
|
||||
auto it = npc_list.find(id);
|
||||
if (it != npc_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
NPC *GetNPCByNPCTypeID(uint32 npc_id);
|
||||
inline Merc *GetMercByID(uint16 id)
|
||||
{ return merc_list.count(id) ? merc_list.at(id) : nullptr; }
|
||||
{
|
||||
auto it = merc_list.find(id);
|
||||
if (it != merc_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
Client *GetClientByName(const char *name);
|
||||
Client *GetClientByAccID(uint32 accid);
|
||||
inline Client *GetClientByID(uint16 id)
|
||||
{ return client_list.count(id) ? client_list.at(id) : nullptr; }
|
||||
{
|
||||
auto it = client_list.find(id);
|
||||
if (it != client_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
Client *GetClientByCharID(uint32 iCharID);
|
||||
Client *GetClientByWID(uint32 iWID);
|
||||
Client *GetClient(uint32 ip, uint16 port);
|
||||
@ -172,7 +187,12 @@ public:
|
||||
Corpse *GetCorpseByOwner(Client* client);
|
||||
Corpse *GetCorpseByOwnerWithinRange(Client* client, Mob* center, int range);
|
||||
inline Corpse *GetCorpseByID(uint16 id)
|
||||
{ return corpse_list.count(id) ? corpse_list.at(id) : nullptr; }
|
||||
{
|
||||
auto it = corpse_list.find(id);
|
||||
if (it != corpse_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
Corpse *GetCorpseByDBID(uint32 dbid);
|
||||
Corpse *GetCorpseByName(const char* name);
|
||||
|
||||
@ -181,10 +201,20 @@ public:
|
||||
Client* FindCorpseDragger(uint16 CorpseID);
|
||||
|
||||
inline Object *GetObjectByID(uint16 id)
|
||||
{ return object_list.count(id) ? object_list.at(id) : nullptr; }
|
||||
{
|
||||
auto it = object_list.find(id);
|
||||
if (it != object_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
Object *GetObjectByDBID(uint32 id);
|
||||
inline Doors *GetDoorsByID(uint16 id)
|
||||
{ return door_list.count(id) ? door_list.at(id) : nullptr; }
|
||||
{
|
||||
auto it = door_list.find(id);
|
||||
if (it != door_list.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
Doors *GetDoorsByDoorID(uint32 id);
|
||||
Doors *GetDoorsByDBID(uint32 id);
|
||||
void RemoveAllCorpsesByCharID(uint32 charid);
|
||||
|
||||
@ -759,28 +759,28 @@ bool Lua_Mob::CastSpell(int spell_id, int target_id) {
|
||||
|
||||
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->CastSpell(spell_id, target_id, slot);
|
||||
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot));
|
||||
}
|
||||
|
||||
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->CastSpell(spell_id, target_id, slot, cast_time);
|
||||
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time);
|
||||
}
|
||||
|
||||
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost);
|
||||
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost);
|
||||
}
|
||||
|
||||
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot));
|
||||
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot));
|
||||
}
|
||||
|
||||
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer,
|
||||
int timer_duration) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
|
||||
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
|
||||
static_cast<uint32>(timer), static_cast<uint32>(timer_duration));
|
||||
}
|
||||
|
||||
@ -789,7 +789,7 @@ bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, in
|
||||
Lua_Safe_Call_Bool();
|
||||
int16 res = resist_adjust;
|
||||
|
||||
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
|
||||
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
|
||||
static_cast<uint32>(timer), static_cast<uint32>(timer_duration), &res);
|
||||
}
|
||||
|
||||
@ -800,27 +800,27 @@ bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target) {
|
||||
|
||||
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->SpellFinished(spell_id, target, slot);
|
||||
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot));
|
||||
}
|
||||
|
||||
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->SpellFinished(spell_id, target, slot, mana_used);
|
||||
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used);
|
||||
}
|
||||
|
||||
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->SpellFinished(spell_id, target, slot, mana_used, inventory_slot);
|
||||
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot);
|
||||
}
|
||||
|
||||
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->SpellFinished(spell_id, target, slot, mana_used, inventory_slot, resist_adjust);
|
||||
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust);
|
||||
}
|
||||
|
||||
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust, bool proc) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->SpellFinished(spell_id, target, slot, mana_used, inventory_slot, resist_adjust, proc);
|
||||
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust, proc);
|
||||
}
|
||||
|
||||
void Lua_Mob::SpellEffect(Lua_Mob caster, int spell_id, double partial) {
|
||||
|
||||
@ -163,7 +163,7 @@ float Map::FindClosestZ(glm::vec3 &start, glm::vec3 *result) const {
|
||||
to.z = -BEST_Z_INVALID;
|
||||
hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance);
|
||||
if (hit) {
|
||||
if (abs(from.z - result->z) < abs(ClosestZ - from.z))
|
||||
if (std::abs(from.z - result->z) < std::abs(ClosestZ - from.z))
|
||||
return result->z;
|
||||
}
|
||||
|
||||
|
||||
@ -1963,7 +1963,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
|
||||
result = CastSpell(spellid, tar->GetID(), 1, -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0);
|
||||
result = CastSpell(spellid, tar->GetID(), EQEmu::CastingSlot::Gem2, -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0);
|
||||
|
||||
if(IsCasting() && IsSitting())
|
||||
Stand();
|
||||
@ -4015,7 +4015,7 @@ bool Merc::UseDiscipline(int32 spell_id, int32 target) {
|
||||
if(IsCasting())
|
||||
InterruptSpell();
|
||||
|
||||
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT);
|
||||
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
54
zone/mob.cpp
54
zone/mob.cpp
@ -2357,7 +2357,6 @@ void Mob::SetZone(uint32 zone_id, uint32 instance_id)
|
||||
{
|
||||
CastToClient()->GetPP().zone_id = zone_id;
|
||||
CastToClient()->GetPP().zoneInstance = instance_id;
|
||||
CastToClient()->Save();
|
||||
}
|
||||
Save();
|
||||
}
|
||||
@ -3224,13 +3223,13 @@ void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on, int lev
|
||||
if(twinproc_chance && zone->random.Roll(twinproc_chance))
|
||||
twinproc = true;
|
||||
|
||||
if (IsBeneficialSpell(spell_id)) {
|
||||
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff, true, level_override);
|
||||
if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id))) { // NPC innate procs don't take this path ever
|
||||
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override);
|
||||
if(twinproc)
|
||||
SpellOnTarget(spell_id, this, false, false, 0, true, level_override);
|
||||
}
|
||||
else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients
|
||||
SpellFinished(spell_id, on, 10, 0, -1, spells[spell_id].ResistDiff, true, level_override);
|
||||
SpellFinished(spell_id, on, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override);
|
||||
if(twinproc)
|
||||
SpellOnTarget(spell_id, on, false, false, 0, true, level_override);
|
||||
}
|
||||
@ -3518,10 +3517,9 @@ void Mob::TryTriggerOnCast(uint32 spell_id, bool aa_trigger)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
|
||||
{
|
||||
if(!IsValidSpell(focus_spell) || !IsValidSpell(spell_id))
|
||||
if (!IsValidSpell(focus_spell) || !IsValidSpell(spell_id))
|
||||
return;
|
||||
|
||||
uint32 trigger_spell_id = 0;
|
||||
@ -3532,15 +3530,17 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
|
||||
if (rank)
|
||||
trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, *rank, spell_id);
|
||||
|
||||
if(IsValidSpell(trigger_spell_id) && GetTarget())
|
||||
SpellFinished(trigger_spell_id, GetTarget(), 10, 0, -1, spells[trigger_spell_id].ResistDiff);
|
||||
if (IsValidSpell(trigger_spell_id) && GetTarget())
|
||||
SpellFinished(trigger_spell_id, GetTarget(), EQEmu::CastingSlot::Item, 0, -1,
|
||||
spells[trigger_spell_id].ResistDiff);
|
||||
}
|
||||
|
||||
else{
|
||||
else {
|
||||
trigger_spell_id = CalcFocusEffect(focusTriggerOnCast, focus_spell, spell_id);
|
||||
|
||||
if(IsValidSpell(trigger_spell_id) && GetTarget()){
|
||||
SpellFinished(trigger_spell_id, GetTarget(),10, 0, -1, spells[trigger_spell_id].ResistDiff);
|
||||
if (IsValidSpell(trigger_spell_id) && GetTarget()) {
|
||||
SpellFinished(trigger_spell_id, GetTarget(), EQEmu::CastingSlot::Item, 0, -1,
|
||||
spells[trigger_spell_id].ResistDiff);
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell);
|
||||
}
|
||||
}
|
||||
@ -3570,7 +3570,7 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect)
|
||||
{
|
||||
// If we trigger an effect then its over.
|
||||
if (IsValidSpell(spells[spell_id].base2[i])){
|
||||
SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
SpellFinished(spells[spell_id].base2[i], target, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -3589,7 +3589,7 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect)
|
||||
if(zone->random.Int(0, 100) <= spells[spell_id].base[effect])
|
||||
{
|
||||
if (IsValidSpell(spells[spell_id].base2[effect])){
|
||||
SpellFinished(spells[spell_id].base2[effect], target, 10, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff);
|
||||
SpellFinished(spells[spell_id].base2[effect], target, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff);
|
||||
return true; //Only trigger once of these per spell effect.
|
||||
}
|
||||
}
|
||||
@ -3666,7 +3666,7 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
|
||||
}
|
||||
|
||||
if (use_spell){
|
||||
SpellFinished(spells[spell_id].base[i], this, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
SpellFinished(spells[spell_id].base[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
|
||||
if(!TryFadeEffect(e))
|
||||
BuffFadeBySlot(e);
|
||||
@ -3694,7 +3694,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id)
|
||||
if(zone->random.Roll(focus))
|
||||
{
|
||||
Message(MT_Spells,"You twincast %s!",spells[spell_id].name);
|
||||
SpellFinished(spell_id, target, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
SpellFinished(spell_id, target, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3712,7 +3712,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id)
|
||||
{
|
||||
if(zone->random.Roll(focus))
|
||||
{
|
||||
SpellFinished(spell_id, target, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
SpellFinished(spell_id, target, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3831,10 +3831,10 @@ bool Mob::TryFadeEffect(int slot)
|
||||
if(IsValidSpell(spell_id))
|
||||
{
|
||||
if (IsBeneficialSpell(spell_id)) {
|
||||
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
}
|
||||
else if(!(IsClient() && CastToClient()->dead)) {
|
||||
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3868,7 +3868,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id)
|
||||
SpellFinished(focus_trigger, target);
|
||||
|
||||
else
|
||||
SpellFinished(focus_trigger, this, 10, 0, -1, spells[focus_trigger].ResistDiff);
|
||||
SpellFinished(focus_trigger, this, EQEmu::CastingSlot::Item, 0, -1, spells[focus_trigger].ResistDiff);
|
||||
}
|
||||
// For detrimental spells, if the triggered spell is beneficial, then it will land on the caster
|
||||
// if the triggered spell is also detrimental, then it will land on the target
|
||||
@ -3878,7 +3878,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id)
|
||||
SpellFinished(focus_trigger, this);
|
||||
|
||||
else
|
||||
SpellFinished(focus_trigger, target, 10, 0, -1, spells[focus_trigger].ResistDiff);
|
||||
SpellFinished(focus_trigger, target, EQEmu::CastingSlot::Item, 0, -1, spells[focus_trigger].ResistDiff);
|
||||
}
|
||||
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell);
|
||||
@ -4529,7 +4529,7 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
|
||||
if (IsValidSpell(spells[spell_id].base2[i]) && spells[spell_id].max[i] <= level)
|
||||
{
|
||||
if(zone->random.Roll(spells[spell_id].base[i]))
|
||||
SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
SpellFinished(spells[spell_id].base2[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4544,17 +4544,17 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
|
||||
|
||||
if(aabonuses.SpellOnKill[i] && IsValidSpell(aabonuses.SpellOnKill[i]) && (level >= aabonuses.SpellOnKill[i + 2])) {
|
||||
if(zone->random.Roll(static_cast<int>(aabonuses.SpellOnKill[i + 1])))
|
||||
SpellFinished(aabonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
SpellFinished(aabonuses.SpellOnKill[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
}
|
||||
|
||||
if(itembonuses.SpellOnKill[i] && IsValidSpell(itembonuses.SpellOnKill[i]) && (level >= itembonuses.SpellOnKill[i + 2])){
|
||||
if(zone->random.Roll(static_cast<int>(itembonuses.SpellOnKill[i + 1])))
|
||||
SpellFinished(itembonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
SpellFinished(itembonuses.SpellOnKill[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
}
|
||||
|
||||
if(spellbonuses.SpellOnKill[i] && IsValidSpell(spellbonuses.SpellOnKill[i]) && (level >= spellbonuses.SpellOnKill[i + 2])) {
|
||||
if(zone->random.Roll(static_cast<int>(spellbonuses.SpellOnKill[i + 1])))
|
||||
SpellFinished(spellbonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
SpellFinished(spellbonuses.SpellOnKill[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
}
|
||||
|
||||
}
|
||||
@ -4571,19 +4571,19 @@ bool Mob::TrySpellOnDeath()
|
||||
for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) {
|
||||
if(IsClient() && aabonuses.SpellOnDeath[i] && IsValidSpell(aabonuses.SpellOnDeath[i])) {
|
||||
if(zone->random.Roll(static_cast<int>(aabonuses.SpellOnDeath[i + 1]))) {
|
||||
SpellFinished(aabonuses.SpellOnDeath[i], this, 10, 0, -1, spells[aabonuses.SpellOnDeath[i]].ResistDiff);
|
||||
SpellFinished(aabonuses.SpellOnDeath[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnDeath[i]].ResistDiff);
|
||||
}
|
||||
}
|
||||
|
||||
if(itembonuses.SpellOnDeath[i] && IsValidSpell(itembonuses.SpellOnDeath[i])) {
|
||||
if(zone->random.Roll(static_cast<int>(itembonuses.SpellOnDeath[i + 1]))) {
|
||||
SpellFinished(itembonuses.SpellOnDeath[i], this, 10, 0, -1, spells[itembonuses.SpellOnDeath[i]].ResistDiff);
|
||||
SpellFinished(itembonuses.SpellOnDeath[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[itembonuses.SpellOnDeath[i]].ResistDiff);
|
||||
}
|
||||
}
|
||||
|
||||
if(spellbonuses.SpellOnDeath[i] && IsValidSpell(spellbonuses.SpellOnDeath[i])) {
|
||||
if(zone->random.Roll(static_cast<int>(spellbonuses.SpellOnDeath[i + 1]))) {
|
||||
SpellFinished(spellbonuses.SpellOnDeath[i], this, 10, 0, -1, spells[spellbonuses.SpellOnDeath[i]].ResistDiff);
|
||||
SpellFinished(spellbonuses.SpellOnDeath[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spellbonuses.SpellOnDeath[i]].ResistDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
zone/mob.h
20
zone/mob.h
@ -26,6 +26,7 @@
|
||||
#include "aa_ability.h"
|
||||
#include "aa.h"
|
||||
#include "../common/light_source.h"
|
||||
#include "../common/emu_constants.h"
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@ -218,7 +219,7 @@ public:
|
||||
|
||||
//Song
|
||||
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
|
||||
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot);
|
||||
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, EQEmu::CastingSlot slot);
|
||||
void BardPulse(uint16 spell_id, Mob *caster);
|
||||
|
||||
//Spell
|
||||
@ -248,29 +249,30 @@ public:
|
||||
void SendSpellBarEnable(uint16 spellid);
|
||||
void ZeroCastingVars();
|
||||
virtual void SpellProcess();
|
||||
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1,
|
||||
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1,
|
||||
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
|
||||
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 *resist_adjust = nullptr,
|
||||
uint32 aa_id = 0);
|
||||
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1,
|
||||
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1,
|
||||
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
|
||||
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 resist_adjust = 0,
|
||||
uint32 aa_id = 0);
|
||||
void CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, uint16 mana_used,
|
||||
void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQEmu::CastingSlot slot, uint16 mana_used,
|
||||
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0);
|
||||
bool SpellFinished(uint16 spell_id, Mob *target, uint16 slot = 10, uint16 mana_used = 0,
|
||||
bool SpellFinished(uint16 spell_id, Mob *target, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, uint16 mana_used = 0,
|
||||
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
|
||||
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false,
|
||||
bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
|
||||
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1);
|
||||
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center,
|
||||
CastAction_type &CastAction, uint16 slot);
|
||||
CastAction_type &CastAction, EQEmu::CastingSlot slot, bool isproc = false);
|
||||
virtual bool CheckFizzle(uint16 spell_id);
|
||||
virtual bool CheckSpellLevelRestriction(uint16 spell_id);
|
||||
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
|
||||
virtual float GetAOERange(uint16 spell_id);
|
||||
void InterruptSpell(uint16 spellid = SPELL_UNKNOWN);
|
||||
void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN);
|
||||
void StopCasting();
|
||||
inline bool IsCasting() const { return((casting_spell_id != 0)); }
|
||||
uint16 CastingSpellID() const { return casting_spell_id; }
|
||||
bool DoCastingChecks();
|
||||
@ -306,6 +308,8 @@ public:
|
||||
virtual int GetMaxSongSlots() const { return 0; }
|
||||
virtual int GetMaxDiscSlots() const { return 0; }
|
||||
virtual int GetMaxTotalSlots() const { return 0; }
|
||||
virtual uint32 GetFirstBuffSlot(bool disc, bool song);
|
||||
virtual uint32 GetLastBuffSlot(bool disc, bool song);
|
||||
virtual void InitializeBuffSlots() { buffs = nullptr; current_buff_count = 0; }
|
||||
virtual void UninitializeBuffSlots() { }
|
||||
EQApplicationPacket *MakeBuffsPacket(bool for_target = true);
|
||||
@ -1216,7 +1220,7 @@ protected:
|
||||
int attacked_count;
|
||||
bool delaytimer;
|
||||
uint16 casting_spell_targetid;
|
||||
uint16 casting_spell_slot;
|
||||
EQEmu::CastingSlot casting_spell_slot;
|
||||
uint16 casting_spell_mana;
|
||||
uint32 casting_spell_inventory_slot;
|
||||
uint32 casting_spell_timer;
|
||||
@ -1226,7 +1230,7 @@ protected:
|
||||
uint32 casting_spell_aa_id;
|
||||
bool casting_spell_checks;
|
||||
uint16 bardsong;
|
||||
uint8 bardsong_slot;
|
||||
EQEmu::CastingSlot bardsong_slot;
|
||||
uint32 bardsong_target_id;
|
||||
|
||||
bool ActiveProjectileATK;
|
||||
|
||||
@ -341,7 +341,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
|
||||
return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
|
||||
return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
|
||||
}
|
||||
|
||||
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes) {
|
||||
@ -673,11 +673,11 @@ void Client::AI_SpellCast()
|
||||
}
|
||||
|
||||
uint32 spell_to_cast = 0xFFFFFFFF;
|
||||
uint32 slot_to_use = 10;
|
||||
EQEmu::CastingSlot slot_to_use = EQEmu::CastingSlot::Item;
|
||||
if(valid_spells.size() == 1)
|
||||
{
|
||||
spell_to_cast = valid_spells[0];
|
||||
slot_to_use = slots[0];
|
||||
slot_to_use = static_cast<EQEmu::CastingSlot>(slots[0]);
|
||||
}
|
||||
else if(valid_spells.empty())
|
||||
{
|
||||
@ -687,7 +687,7 @@ void Client::AI_SpellCast()
|
||||
{
|
||||
uint32 idx = zone->random.Int(0, (valid_spells.size()-1));
|
||||
spell_to_cast = valid_spells[idx];
|
||||
slot_to_use = slots[idx];
|
||||
slot_to_use = static_cast<EQEmu::CastingSlot>(slots[idx]);
|
||||
}
|
||||
|
||||
if(IsMezSpell(spell_to_cast) || IsFearSpell(spell_to_cast))
|
||||
@ -2362,8 +2362,10 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
return a.priority > b.priority;
|
||||
});
|
||||
|
||||
if (IsValidSpell(attack_proc_spell))
|
||||
if (IsValidSpell(attack_proc_spell)) {
|
||||
AddProcToWeapon(attack_proc_spell, true, proc_chance);
|
||||
innate_proc_spell_id = attack_proc_spell;
|
||||
}
|
||||
|
||||
if (IsValidSpell(range_proc_spell))
|
||||
AddRangedProc(range_proc_spell, (rproc_chance + 100));
|
||||
|
||||
117
zone/npc.cpp
117
zone/npc.cpp
@ -233,6 +233,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
|
||||
npc_spells_id = 0;
|
||||
HasAISpell = false;
|
||||
HasAISpellEffects = false;
|
||||
innate_proc_spell_id = 0;
|
||||
|
||||
if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs))
|
||||
{
|
||||
@ -2006,44 +2007,90 @@ void NPC::LevelScale() {
|
||||
|
||||
float scaling = (((random_level / (float)level) - 1) * (scalerate / 100.0f));
|
||||
|
||||
// Compensate for scale rates at low levels so they don't add too much
|
||||
uint8 scale_adjust = 1;
|
||||
if(level > 0 && level <= 5)
|
||||
scale_adjust = 10;
|
||||
if(level > 5 && level <= 10)
|
||||
scale_adjust = 5;
|
||||
if(level > 10 && level <= 15)
|
||||
scale_adjust = 3;
|
||||
if(level > 15 && level <= 25)
|
||||
scale_adjust = 2;
|
||||
if (RuleB(NPC, NewLevelScaling)) {
|
||||
if (scalerate == 0 || maxlevel <= 25) {
|
||||
// pre-pop seems to scale by 20 HP increments while newer by 100
|
||||
// We also don't want 100 increments on newer noobie zones, check level
|
||||
if (zone->GetZoneID() < 200 || level < 48) {
|
||||
max_hp += (random_level - level) * 20;
|
||||
base_hp += (random_level - level) * 20;
|
||||
} else {
|
||||
max_hp += (random_level - level) * 100;
|
||||
base_hp += (random_level - level) * 100;
|
||||
}
|
||||
|
||||
base_hp += (int)(base_hp * scaling);
|
||||
max_hp += (int)(max_hp * scaling);
|
||||
cur_hp = max_hp;
|
||||
STR += (int)(STR * scaling / scale_adjust);
|
||||
STA += (int)(STA * scaling / scale_adjust);
|
||||
AGI += (int)(AGI * scaling / scale_adjust);
|
||||
DEX += (int)(DEX * scaling / scale_adjust);
|
||||
INT += (int)(INT * scaling / scale_adjust);
|
||||
WIS += (int)(WIS * scaling / scale_adjust);
|
||||
CHA += (int)(CHA * scaling / scale_adjust);
|
||||
if (MR)
|
||||
MR += (int)(MR * scaling / scale_adjust);
|
||||
if (CR)
|
||||
CR += (int)(CR * scaling / scale_adjust);
|
||||
if (DR)
|
||||
DR += (int)(DR * scaling / scale_adjust);
|
||||
if (FR)
|
||||
FR += (int)(FR * scaling / scale_adjust);
|
||||
if (PR)
|
||||
PR += (int)(PR * scaling / scale_adjust);
|
||||
cur_hp = max_hp;
|
||||
max_dmg += (random_level - level) * 2;
|
||||
} else {
|
||||
uint8 scale_adjust = 1;
|
||||
|
||||
base_hp += (int)(base_hp * scaling);
|
||||
max_hp += (int)(max_hp * scaling);
|
||||
cur_hp = max_hp;
|
||||
|
||||
if (max_dmg) {
|
||||
max_dmg += (int)(max_dmg * scaling / scale_adjust);
|
||||
min_dmg += (int)(min_dmg * scaling / scale_adjust);
|
||||
}
|
||||
|
||||
STR += (int)(STR * scaling / scale_adjust);
|
||||
STA += (int)(STA * scaling / scale_adjust);
|
||||
AGI += (int)(AGI * scaling / scale_adjust);
|
||||
DEX += (int)(DEX * scaling / scale_adjust);
|
||||
INT += (int)(INT * scaling / scale_adjust);
|
||||
WIS += (int)(WIS * scaling / scale_adjust);
|
||||
CHA += (int)(CHA * scaling / scale_adjust);
|
||||
if (MR)
|
||||
MR += (int)(MR * scaling / scale_adjust);
|
||||
if (CR)
|
||||
CR += (int)(CR * scaling / scale_adjust);
|
||||
if (DR)
|
||||
DR += (int)(DR * scaling / scale_adjust);
|
||||
if (FR)
|
||||
FR += (int)(FR * scaling / scale_adjust);
|
||||
if (PR)
|
||||
PR += (int)(PR * scaling / scale_adjust);
|
||||
}
|
||||
} else {
|
||||
// Compensate for scale rates at low levels so they don't add too much
|
||||
uint8 scale_adjust = 1;
|
||||
if(level > 0 && level <= 5)
|
||||
scale_adjust = 10;
|
||||
if(level > 5 && level <= 10)
|
||||
scale_adjust = 5;
|
||||
if(level > 10 && level <= 15)
|
||||
scale_adjust = 3;
|
||||
if(level > 15 && level <= 25)
|
||||
scale_adjust = 2;
|
||||
|
||||
base_hp += (int)(base_hp * scaling);
|
||||
max_hp += (int)(max_hp * scaling);
|
||||
cur_hp = max_hp;
|
||||
STR += (int)(STR * scaling / scale_adjust);
|
||||
STA += (int)(STA * scaling / scale_adjust);
|
||||
AGI += (int)(AGI * scaling / scale_adjust);
|
||||
DEX += (int)(DEX * scaling / scale_adjust);
|
||||
INT += (int)(INT * scaling / scale_adjust);
|
||||
WIS += (int)(WIS * scaling / scale_adjust);
|
||||
CHA += (int)(CHA * scaling / scale_adjust);
|
||||
if (MR)
|
||||
MR += (int)(MR * scaling / scale_adjust);
|
||||
if (CR)
|
||||
CR += (int)(CR * scaling / scale_adjust);
|
||||
if (DR)
|
||||
DR += (int)(DR * scaling / scale_adjust);
|
||||
if (FR)
|
||||
FR += (int)(FR * scaling / scale_adjust);
|
||||
if (PR)
|
||||
PR += (int)(PR * scaling / scale_adjust);
|
||||
|
||||
if (max_dmg)
|
||||
{
|
||||
max_dmg += (int)(max_dmg * scaling / scale_adjust);
|
||||
min_dmg += (int)(min_dmg * scaling / scale_adjust);
|
||||
}
|
||||
|
||||
if (max_dmg)
|
||||
{
|
||||
max_dmg += (int)(max_dmg * scaling / scale_adjust);
|
||||
min_dmg += (int)(min_dmg * scaling / scale_adjust);
|
||||
}
|
||||
|
||||
level = random_level;
|
||||
|
||||
return;
|
||||
|
||||
@ -407,6 +407,7 @@ public:
|
||||
void mod_npc_killed_merit(Mob* c);
|
||||
void mod_npc_killed(Mob* oos);
|
||||
void AISpellsList(Client *c);
|
||||
uint16 GetInnateProcSpellID() const { return innate_proc_spell_id; }
|
||||
|
||||
uint32 GetHeroForgeModel() const { return herosforgemodel; }
|
||||
void SetHeroForgeModel(uint32 model) { herosforgemodel = model; }
|
||||
@ -454,6 +455,7 @@ protected:
|
||||
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
AISpellsVar_Struct AISpellVar;
|
||||
int16 GetFocusEffect(focusType type, uint16 spell_id);
|
||||
uint16 innate_proc_spell_id;
|
||||
|
||||
uint32 npc_spells_effects_id;
|
||||
std::vector<AISpellsEffects_Struct> AIspellsEffects;
|
||||
|
||||
@ -3982,12 +3982,12 @@ XS(XS_Mob_CastSpell)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items < 3 || items > 7)
|
||||
Perl_croak(aTHX_ "Usage: Mob::CastSpell(THIS, spell_id, target_id, slot= 10, casttime= -1, mana_cost= -1, resist_adjust = 0)");
|
||||
Perl_croak(aTHX_ "Usage: Mob::CastSpell(THIS, spell_id, target_id, slot= 22, casttime= -1, mana_cost= -1, resist_adjust = 0)");
|
||||
{
|
||||
Mob * THIS;
|
||||
uint16 spell_id = (uint16)SvUV(ST(1));
|
||||
uint16 target_id = (uint16)SvUV(ST(2));
|
||||
uint16 slot;
|
||||
EQEmu::CastingSlot slot;
|
||||
int32 casttime;
|
||||
int32 mana_cost;
|
||||
int16 resist_adjust;
|
||||
@ -4002,9 +4002,9 @@ XS(XS_Mob_CastSpell)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
if (items < 4)
|
||||
slot = 10;
|
||||
slot = EQEmu::CastingSlot::Item;
|
||||
else {
|
||||
slot = (uint16)SvUV(ST(3));
|
||||
slot = static_cast<EQEmu::CastingSlot>(SvUV(ST(3)));
|
||||
}
|
||||
|
||||
if (items < 5)
|
||||
@ -4085,7 +4085,7 @@ XS(XS_Mob_SpellFinished)
|
||||
resist_diff = spells[spell_id].ResistDiff;
|
||||
}
|
||||
|
||||
THIS->SpellFinished(spell_id, spell_target, 10, mana_cost, -1, resist_diff);
|
||||
THIS->SpellFinished(spell_id, spell_target, EQEmu::CastingSlot::Item, mana_cost, -1, resist_diff);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
@ -365,14 +365,14 @@ void QuestManager::castspell(int spell_id, int target_id) {
|
||||
if (owner) {
|
||||
Mob *tgt = entity_list.GetMob(target_id);
|
||||
if(tgt != nullptr)
|
||||
owner->SpellFinished(spell_id, tgt, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
owner->SpellFinished(spell_id, tgt, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::selfcast(int spell_id) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (initiator)
|
||||
initiator->SpellFinished(spell_id, initiator, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
initiator->SpellFinished(spell_id, initiator, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
}
|
||||
|
||||
void QuestManager::addloot(int item_id, int charges, bool equipitem) {
|
||||
|
||||
@ -149,7 +149,7 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32
|
||||
IsValidSpell(aabonuses.SkillAttackProc[2])) {
|
||||
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
|
||||
if (zone->random.Roll(chance))
|
||||
SpellFinished(aabonuses.SkillAttackProc[2], who, 10, 0, -1,
|
||||
SpellFinished(aabonuses.SkillAttackProc[2], who, EQEmu::CastingSlot::Item, 0, -1,
|
||||
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
|
||||
}
|
||||
who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false);
|
||||
@ -786,7 +786,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) {
|
||||
//EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow.
|
||||
int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile;
|
||||
|
||||
if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume)){
|
||||
if (RangeItem->ExpendableArrow || !ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume)){
|
||||
DeleteItemInInventory(ammo_slot, 1, true);
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Consumed one arrow from slot %d", ammo_slot);
|
||||
} else {
|
||||
@ -2429,7 +2429,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills:
|
||||
IsValidSpell(aabonuses.SkillAttackProc[2])) {
|
||||
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
|
||||
if (zone->random.Roll(chance))
|
||||
SpellFinished(aabonuses.SkillAttackProc[2], other, 10, 0, -1,
|
||||
SpellFinished(aabonuses.SkillAttackProc[2], other, EQEmu::CastingSlot::Item, 0, -1,
|
||||
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
|
||||
}
|
||||
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
|
||||
|
||||
@ -194,6 +194,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
bool SE_SpellTrigger_HasCast = false;
|
||||
|
||||
// if buff slot, use instrument mod there, otherwise calc it
|
||||
uint32 instrument_mod = buffslot > -1 ? buffs[buffslot].instrument_mod : caster ? caster->GetInstrumentMod(spell_id) : 10;
|
||||
// iterate through the effects in the spell
|
||||
for (i = 0; i < EFFECT_COUNT; i++)
|
||||
{
|
||||
@ -201,7 +203,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
continue;
|
||||
|
||||
effect = spell.effectid[i];
|
||||
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, buffslot > -1 ? buffs[buffslot].instrument_mod : 10, caster ? caster : this);
|
||||
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, instrument_mod, caster ? caster : this);
|
||||
|
||||
if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands))
|
||||
effect_value = GetMaxHP();
|
||||
@ -355,8 +357,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
break;
|
||||
|
||||
int32 val = 0;
|
||||
val = 7500*effect_value;
|
||||
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||
val = 7500 * effect_value;
|
||||
if (caster)
|
||||
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||
|
||||
if (val > 0)
|
||||
HealDamage(val, caster);
|
||||
@ -375,12 +378,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
snprintf(effect_desc, _EDLEN, "Current Mana: %+i", effect_value);
|
||||
#endif
|
||||
SetMana(GetMana() + effect_value);
|
||||
caster->SetMana(caster->GetMana() + std::abs(effect_value));
|
||||
if (caster)
|
||||
caster->SetMana(caster->GetMana() + std::abs(effect_value));
|
||||
|
||||
if (effect_value < 0)
|
||||
TryTriggerOnValueAmount(false, true);
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
caster->Message(0, "You have gained %+i mana!", effect_value);
|
||||
if (caster)
|
||||
caster->Message(0, "You have gained %+i mana!", effect_value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -532,7 +537,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
}
|
||||
}
|
||||
|
||||
if (effect == SE_GateCastersBindpoint && caster->IsClient())
|
||||
if (effect == SE_GateCastersBindpoint && caster && caster->IsClient())
|
||||
{ // Teleport Bind uses caster's bind point
|
||||
int index = spells[spell_id].base[i] - 1;
|
||||
if (index < 0 || index > 4)
|
||||
@ -648,7 +653,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
//Added client messages to give some indication this effect is active.
|
||||
// Is there a message generated? Too disgusted by raids.
|
||||
uint32 time = spell.base[i] * 10 * 1000;
|
||||
if (caster->IsClient()) {
|
||||
if (caster && caster->IsClient()) {
|
||||
if (caster->IsGrouped()) {
|
||||
auto group = caster->GetGroup();
|
||||
for (int i = 0; i < 6; ++i)
|
||||
@ -695,7 +700,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
((GetLevel() > max_level) && caster && (!caster->IsNPC() ||
|
||||
(caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))))))
|
||||
{
|
||||
caster->Message_StringID(MT_SpellFailure, IMMUNE_STUN);
|
||||
if (caster)
|
||||
caster->Message_StringID(MT_SpellFailure, IMMUNE_STUN);
|
||||
} else {
|
||||
int stun_resist = itembonuses.StunResist+spellbonuses.StunResist;
|
||||
if (IsClient())
|
||||
@ -704,7 +710,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
if (stun_resist <= 0 || zone->random.Int(0,99) >= stun_resist) {
|
||||
Log.Out(Logs::Detail, Logs::Combat, "Stunned. We had %d percent resist chance.", stun_resist);
|
||||
|
||||
if (caster->IsClient())
|
||||
if (caster && caster->IsClient())
|
||||
effect_value += effect_value*caster->GetFocusEffect(focusFcStunTimeMod, spell_id)/100;
|
||||
|
||||
Stun(effect_value);
|
||||
@ -916,11 +922,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
cd->meleepush_xy = action->sequence;
|
||||
|
||||
CastToClient()->QueuePacket(action_packet);
|
||||
if(caster->IsClient() && caster != this)
|
||||
if(caster && caster->IsClient() && caster != this)
|
||||
caster->CastToClient()->QueuePacket(action_packet);
|
||||
|
||||
CastToClient()->QueuePacket(message_packet);
|
||||
if(caster->IsClient() && caster != this)
|
||||
if(caster && caster->IsClient() && caster != this)
|
||||
caster->CastToClient()->QueuePacket(message_packet);
|
||||
|
||||
CastToClient()->SetBindPoint(spells[spell_id].base[i] - 1);
|
||||
@ -1031,7 +1037,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
if(zone->random.Roll(effect_value))
|
||||
Gate(spells[spell_id].base2[i] - 1);
|
||||
else
|
||||
else if (caster)
|
||||
caster->Message_StringID(MT_SpellFailure,GATE_FAIL);
|
||||
}
|
||||
break;
|
||||
@ -1043,7 +1049,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
snprintf(effect_desc, _EDLEN, "Cancel Magic: %d", effect_value);
|
||||
#endif
|
||||
if(GetSpecialAbility(UNDISPELLABLE)){
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
|
||||
if (caster)
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1053,7 +1060,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
spells[buffs[slot].spellid].dispel_flag == 0 &&
|
||||
!IsDiscipline(buffs[slot].spellid))
|
||||
{
|
||||
if (TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
|
||||
if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
|
||||
BuffFadeBySlot(slot);
|
||||
slot = buff_count;
|
||||
}
|
||||
@ -1068,7 +1075,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
snprintf(effect_desc, _EDLEN, "Dispel Detrimental: %d", effect_value);
|
||||
#endif
|
||||
if(GetSpecialAbility(UNDISPELLABLE)){
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
|
||||
if (caster)
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1078,7 +1086,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
IsDetrimentalSpell(buffs[slot].spellid) &&
|
||||
spells[buffs[slot].spellid].dispel_flag == 0)
|
||||
{
|
||||
if (TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
|
||||
if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
|
||||
BuffFadeBySlot(slot);
|
||||
slot = buff_count;
|
||||
}
|
||||
@ -1093,7 +1101,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
snprintf(effect_desc, _EDLEN, "Dispel Beneficial: %d", effect_value);
|
||||
#endif
|
||||
if(GetSpecialAbility(UNDISPELLABLE)){
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
|
||||
if (caster)
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1103,7 +1112,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
IsBeneficialSpell(buffs[slot].spellid) &&
|
||||
spells[buffs[slot].spellid].dispel_flag == 0)
|
||||
{
|
||||
if (TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
|
||||
if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
|
||||
BuffFadeBySlot(slot);
|
||||
slot = buff_count;
|
||||
}
|
||||
@ -1120,7 +1129,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
if (buffs[slot].spellid != SPELL_UNKNOWN &&
|
||||
IsDetrimentalSpell(buffs[slot].spellid))
|
||||
{
|
||||
if (TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
|
||||
if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
|
||||
BuffFadeBySlot(slot);
|
||||
}
|
||||
}
|
||||
@ -1509,7 +1518,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
((GetLevel() > max_level)
|
||||
&& caster && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))))
|
||||
{
|
||||
caster->Message_StringID(MT_Shout, IMMUNE_STUN);
|
||||
if (caster)
|
||||
caster->Message_StringID(MT_Shout, IMMUNE_STUN);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1726,7 +1736,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (caster) {
|
||||
Raid *r = entity_list.GetRaidByClient(caster->CastToClient());
|
||||
if(r)
|
||||
{
|
||||
@ -1766,7 +1776,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
Message_StringID(4, CORPSE_CANT_SENSE);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (caster)
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ);
|
||||
}
|
||||
else {
|
||||
@ -2113,7 +2123,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Sacrifice");
|
||||
#endif
|
||||
if(!IsClient() || !caster->IsClient()){
|
||||
if(!caster || !IsClient() || !caster->IsClient()){
|
||||
break;
|
||||
}
|
||||
CastToClient()->SacrificeConfirm(caster->CastToClient());
|
||||
@ -2122,12 +2132,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
case SE_SummonPC:
|
||||
{
|
||||
if(IsClient()){
|
||||
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), caster->GetX(), caster->GetY(), caster->GetZ(), caster->GetHeading(), 2, SummonPC);
|
||||
if (!caster)
|
||||
break;
|
||||
if (IsClient()) {
|
||||
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), caster->GetX(),
|
||||
caster->GetY(), caster->GetZ(), caster->GetHeading(), 2,
|
||||
SummonPC);
|
||||
Message(15, "You have been summoned!");
|
||||
entity_list.ClearAggro(this);
|
||||
}
|
||||
else
|
||||
} else
|
||||
caster->Message(13, "This spell can only be cast on players.");
|
||||
|
||||
break;
|
||||
@ -2174,6 +2187,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
case SE_TemporaryPets: //Dook- swarms and wards:
|
||||
{
|
||||
if (!caster)
|
||||
break;
|
||||
// this makes necro epic 1.5/2.0 proc work properly
|
||||
if((spell_id != 6882) && (spell_id != 6884)) // Chaotic Jester/Steadfast Servant
|
||||
{
|
||||
@ -2269,6 +2284,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
*/
|
||||
int16 focus = 0;
|
||||
int ReuseTime = spells[spell_id].recast_time + spells[spell_id].recovery_time;
|
||||
if (!caster)
|
||||
break;
|
||||
|
||||
focus = caster->GetFocusEffect(focusFcBaseEffects, spell_id);
|
||||
|
||||
@ -2292,7 +2309,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
snprintf(effect_desc, _EDLEN, "Wake The Dead");
|
||||
#endif
|
||||
//meh dupe issue with npc casting this
|
||||
if(caster->IsClient()){
|
||||
if(caster && caster->IsClient()){
|
||||
int dur = spells[spell_id].max[i];
|
||||
if (!dur)
|
||||
dur = 60;
|
||||
@ -2657,7 +2674,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
case SE_Taunt:
|
||||
{
|
||||
if (IsNPC()){
|
||||
if (caster && IsNPC()){
|
||||
caster->Taunt(this->CastToNPC(), false, static_cast<float>(spell.base[i]), true, spell.base2[i]);
|
||||
}
|
||||
break;
|
||||
@ -2745,7 +2762,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
if (caster && IsValidSpell(spells[spell_id].base2[i])){
|
||||
if(zone->random.Roll(spells[spell_id].base[i]))
|
||||
caster->SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
caster->SpellFinished(spells[spell_id].base2[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3668,10 +3685,11 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
|
||||
break;
|
||||
}
|
||||
// These effects always trigger when they fade.
|
||||
// Should we have this triggered from else where?
|
||||
case SE_CastOnFadeEffect:
|
||||
case SE_CastOnFadeEffectNPC:
|
||||
case SE_CastOnFadeEffectAlways: {
|
||||
if (buff.ticsremaining == 1) {
|
||||
if (buff.ticsremaining == 0) {
|
||||
SpellOnTarget(spells[buff.spellid].base[i], this);
|
||||
}
|
||||
break;
|
||||
@ -5135,7 +5153,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
|
||||
if (Caston_spell_id) {
|
||||
if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id))
|
||||
SpellFinished(Caston_spell_id, this, 10, 0, -1, spells[Caston_spell_id].ResistDiff);
|
||||
SpellFinished(Caston_spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[Caston_spell_id].ResistDiff);
|
||||
}
|
||||
|
||||
return (value * lvlModifier / 100);
|
||||
@ -6226,7 +6244,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
||||
{
|
||||
/*If return TRUE spell met all restrictions and can continue (this = target).
|
||||
This check is used when the spell_new field CastRestriction is defined OR spell effect '0'(DD/Heal) has a defined limit
|
||||
Range 1 : UNKNOWN
|
||||
Range 1 : UNKNOWN -- the spells with this seem to not have a restiction, true for now
|
||||
Range 100 : *Animal OR Humanoid
|
||||
Range 101 : *Dragon
|
||||
Range 102 : *Animal OR Insect
|
||||
@ -6253,7 +6271,10 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
||||
Range 124 : *Undead HP < 10%
|
||||
Range 125 : *Clockwork HP < 10%
|
||||
Range 126 : *Wisp HP < 10%
|
||||
Range 127-130 : UNKNOWN
|
||||
Range 127 : UNKNOWN
|
||||
Range 128 : pure melee -- guess
|
||||
Range 129 : pure caster -- guess
|
||||
Range 130 : hybrid -- guess
|
||||
Range 150 : UNKNOWN
|
||||
Range 190 : No Raid boss flag *not implemented
|
||||
Range 191 : This spell will deal less damage to 'exceptionally strong targets' - Raid boss flag *not implemented
|
||||
@ -6265,12 +6286,17 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
||||
Range 300 - 303 : UNKOWN *not implemented
|
||||
Range 304 : Chain + Plate class (buffs)
|
||||
Range 399 - 409 : Heal if HP within a specified range (400 = 0-25% 401 = 25 - 35% 402 = 35-45% ect)
|
||||
Range 410 - 411 : UNKOWN
|
||||
Range 410 - 411 : UNKOWN -- examples are auras that cast on NPCs maybe in combat/out of combat?
|
||||
Range 500 - 599 : Heal if HP less than a specified value
|
||||
Range 600 - 699 : Limit to Body Type [base2 - 600 = Body]
|
||||
Range 700 : NPC only -- from patch notes "Wizard - Arcane Fusion no longer deals damage to non-NPC targets. This should ensure that wizards who fail their Bucolic Gambit are slightly less likely to annihilate themselves."
|
||||
Range 701 : NOT PET
|
||||
Range 800 : UKNOWN
|
||||
Range 800 : UKNOWN -- Target's Target Test (16598)
|
||||
Range 812 : UNKNOWN -- triggered by Thaumatize Owner
|
||||
Range 814 : UNKNOWN -- Vegetentacles
|
||||
Range 815 : UNKNOWN -- Pumpkin Pulp Splash
|
||||
Range 816 : UNKNOWN -- Rotten Fruit Splash
|
||||
Range 817 : UNKNOWN -- Tainted Bixie Pollen Splash
|
||||
Range 818 - 819 : If Undead/If Not Undead
|
||||
Range 820 - 822 : UKNOWN
|
||||
Range 835 : Unknown *not implemented
|
||||
@ -6290,6 +6316,9 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
||||
|
||||
switch(value)
|
||||
{
|
||||
case 1:
|
||||
return true;
|
||||
|
||||
case 100:
|
||||
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid))
|
||||
return true;
|
||||
@ -6683,10 +6712,10 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
|
||||
if (IsValidSpell(spell_id)) {
|
||||
|
||||
if (IsBeneficialSpell(spell_id))
|
||||
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
|
||||
else if(attacker)
|
||||
SpellFinished(spell_id, attacker, 10, 0, -1, spells[spell_id].ResistDiff);
|
||||
SpellFinished(spell_id, attacker, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
244
zone/spells.cpp
244
zone/spells.cpp
@ -104,6 +104,8 @@ extern Zone* zone;
|
||||
extern volatile bool is_zone_loaded;
|
||||
extern WorldServer worldserver;
|
||||
|
||||
using EQEmu::CastingSlot;
|
||||
|
||||
// this is run constantly for every mob
|
||||
void Mob::SpellProcess()
|
||||
{
|
||||
@ -145,13 +147,13 @@ void NPC::SpellProcess()
|
||||
// the rule is you can cast one triggered (usually timed) spell at a time
|
||||
// but things like SpellFinished() can run concurrent with a triggered cast
|
||||
// to allow procs to work
|
||||
bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot,
|
||||
uint32 timer, uint32 timer_duration, int16 *resist_adjust,
|
||||
uint32 aa_id)
|
||||
{
|
||||
Log.Out(Logs::Detail, Logs::Spells, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d",
|
||||
(IsValidSpell(spell_id))?spells[spell_id].name:"UNKNOWN SPELL", spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot);
|
||||
(IsValidSpell(spell_id))?spells[spell_id].name:"UNKNOWN SPELL", spell_id, target_id, static_cast<int>(slot), cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot);
|
||||
|
||||
if(casting_spell_id == spell_id)
|
||||
ZeroCastingVars();
|
||||
@ -178,7 +180,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
if(IsClient())
|
||||
CastToClient()->SendSpellBarEnable(spell_id);
|
||||
if(casting_spell_id && IsNPC())
|
||||
CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot);
|
||||
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
|
||||
return(false);
|
||||
}
|
||||
//It appears that the Sanctuary effect is removed by a check on the client side (keep this however for redundancy)
|
||||
@ -201,7 +203,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
if(IsClient())
|
||||
CastToClient()->SendSpellBarEnable(spell_id);
|
||||
if(casting_spell_id && IsNPC())
|
||||
CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot);
|
||||
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
|
||||
return(false);
|
||||
}
|
||||
|
||||
@ -231,7 +233,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
// check for fizzle
|
||||
// note that CheckFizzle itself doesn't let NPCs fizzle,
|
||||
// but this code allows for it.
|
||||
if(slot < MAX_PP_MEMSPELL && !CheckFizzle(spell_id))
|
||||
if(slot < CastingSlot::MaxGems && !CheckFizzle(spell_id))
|
||||
{
|
||||
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
|
||||
InterruptSpell(fizzle_msg, 0x121, spell_id);
|
||||
@ -252,7 +254,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
}
|
||||
|
||||
//Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits.
|
||||
if(item_slot && IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT)))
|
||||
if(item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
|
||||
{
|
||||
ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot);
|
||||
int bitmask = 1;
|
||||
@ -336,14 +338,13 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
// this is the 2nd phase of CastSpell, broken up like this to make it easier
|
||||
// to repeat a spell for bard songs
|
||||
//
|
||||
bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish,
|
||||
uint32 item_slot, uint32 timer, uint32 timer_duration,
|
||||
int16 resist_adjust, uint32 aa_id)
|
||||
{
|
||||
Mob* pMob = nullptr;
|
||||
int32 orgcasttime;
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
|
||||
if(!IsValidSpell(spell_id)) {
|
||||
InterruptSpell();
|
||||
@ -353,7 +354,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
const SPDat_Spell_Struct &spell = spells[spell_id];
|
||||
|
||||
Log.Out(Logs::Detail, Logs::Spells, "DoCastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item %d",
|
||||
spell.name, spell_id, target_id, slot, cast_time, mana_cost, item_slot==0xFFFFFFFF?999:item_slot);
|
||||
spell.name, spell_id, target_id, static_cast<int>(slot), cast_time, mana_cost, item_slot==0xFFFFFFFF?999:item_slot);
|
||||
|
||||
casting_spell_id = spell_id;
|
||||
casting_spell_slot = slot;
|
||||
@ -418,7 +419,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
// If you're at full mana, let it cast even if you dont have enough mana
|
||||
|
||||
// we calculated this above, now enforce it
|
||||
if(mana_cost > 0 && slot != USE_ITEM_SPELL_SLOT)
|
||||
if(mana_cost > 0 && slot != CastingSlot::Item)
|
||||
{
|
||||
int my_curmana = GetMana();
|
||||
int my_maxmana = GetMaxMana();
|
||||
@ -453,8 +454,26 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Casting time %d (orig %d), mana cost %d",
|
||||
spell_id, cast_time, orgcasttime, mana_cost);
|
||||
|
||||
// now tell the people in the area -- we ALWAYS want to send this, even instant cast spells.
|
||||
// The only time this is skipped is for NPC innate procs and weapon procs. Procs from buffs
|
||||
// oddly still send this. Since those cases don't reach here, we don't need to check them
|
||||
if (slot != CastingSlot::Discipline) {
|
||||
auto outapp = new EQApplicationPacket(OP_BeginCast,sizeof(BeginCast_Struct));
|
||||
BeginCast_Struct* begincast = (BeginCast_Struct*)outapp->pBuffer;
|
||||
begincast->caster_id = GetID();
|
||||
begincast->spell_id = spell_id;
|
||||
begincast->cast_time = orgcasttime; // client calculates reduced time by itself
|
||||
outapp->priority = 3;
|
||||
entity_list.QueueCloseClients(this, outapp, false, 200, 0, true); //IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
// cast time is 0, just finish it right now and be done with it
|
||||
if(cast_time == 0) {
|
||||
if (!DoCastingChecks()) {
|
||||
StopCasting();
|
||||
return false;
|
||||
}
|
||||
CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust);
|
||||
return(true);
|
||||
}
|
||||
@ -476,25 +495,14 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
if (oSpellWillFinish)
|
||||
*oSpellWillFinish = Timer::GetCurrentTime() + cast_time + 100;
|
||||
|
||||
// now tell the people in the area
|
||||
outapp = new EQApplicationPacket(OP_BeginCast,sizeof(BeginCast_Struct));
|
||||
BeginCast_Struct* begincast = (BeginCast_Struct*)outapp->pBuffer;
|
||||
begincast->caster_id = GetID();
|
||||
begincast->spell_id = spell_id;
|
||||
begincast->cast_time = orgcasttime; // client calculates reduced time by itself
|
||||
outapp->priority = 3;
|
||||
entity_list.QueueCloseClients(this, outapp, false, 200, 0, true); //IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS);
|
||||
safe_delete(outapp);
|
||||
outapp = nullptr;
|
||||
|
||||
if (IsClient() && slot == USE_ITEM_SPELL_SLOT &&item_slot != 0xFFFFFFFF) {
|
||||
if (IsClient() && slot == CastingSlot::Item && item_slot != 0xFFFFFFFF) {
|
||||
auto item = CastToClient()->GetInv().GetItem(item_slot);
|
||||
if (item && item->GetItem())
|
||||
Message_StringID(MT_Spells, BEGINS_TO_GLOW, item->GetItem()->Name);
|
||||
}
|
||||
|
||||
if (!DoCastingChecks()) {
|
||||
InterruptSpell();
|
||||
StopCasting();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -550,6 +558,10 @@ bool Mob::DoCastingChecks()
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient() && spells[spell_id].EndurTimerIndex > 0 && casting_spell_slot < CastingSlot::MaxGems)
|
||||
if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[spell_id].EndurTimerIndex))
|
||||
return false;
|
||||
|
||||
casting_spell_checks = true;
|
||||
return true;
|
||||
}
|
||||
@ -767,7 +779,7 @@ void Mob::ZeroCastingVars()
|
||||
spellend_timer.Disable();
|
||||
casting_spell_id = 0;
|
||||
casting_spell_targetid = 0;
|
||||
casting_spell_slot = 0;
|
||||
casting_spell_slot = CastingSlot::Gem1;
|
||||
casting_spell_mana = 0;
|
||||
casting_spell_inventory_slot = 0;
|
||||
casting_spell_timer = 0;
|
||||
@ -802,7 +814,7 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
|
||||
}
|
||||
|
||||
if(casting_spell_id && IsNPC()) {
|
||||
CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot);
|
||||
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
|
||||
}
|
||||
|
||||
if(casting_spell_aa_id && IsClient()) { //Rest AA Timer on failed cast
|
||||
@ -874,18 +886,44 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
|
||||
|
||||
}
|
||||
|
||||
// this is like interrupt, just it doesn't spam interrupt packets to everyone
|
||||
// There are a few cases where this is what live does :P
|
||||
void Mob::StopCasting()
|
||||
{
|
||||
if (casting_spell_id && IsNPC()) {
|
||||
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
|
||||
}
|
||||
|
||||
if (IsClient()) {
|
||||
auto c = CastToClient();
|
||||
if (casting_spell_aa_id) { //Rest AA Timer on failed cast
|
||||
c->Message_StringID(MT_SpellFailure, ABILITY_FAILED);
|
||||
c->ResetAlternateAdvancementTimer(casting_spell_aa_id);
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct));
|
||||
auto mc = (ManaChange_Struct *)outapp->pBuffer;
|
||||
mc->new_mana = GetMana();
|
||||
mc->stamina = GetEndurance();
|
||||
mc->spell_id = casting_spell_id;
|
||||
mc->keepcasting = 0;
|
||||
c->FastQueuePacket(&outapp);
|
||||
}
|
||||
ZeroCastingVars();
|
||||
}
|
||||
|
||||
// this is called after the timer is up and the spell is finished
|
||||
// casting. everything goes through here, including items with zero cast time
|
||||
// only to be used from SpellProcess
|
||||
// NOTE: do not put range checking, etc into this function. this should
|
||||
// just check timed spell specific things before passing off to SpellFinished
|
||||
// which figures out proper targets etc
|
||||
void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
|
||||
void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slot,
|
||||
uint16 mana_used, uint32 inventory_slot, int16 resist_adjust)
|
||||
{
|
||||
bool IsFromItem = false;
|
||||
|
||||
if(IsClient() && slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && slot != TARGET_RING_SPELL_SLOT && spells[spell_id].recast_time > 1000) { // 10 is item
|
||||
if(IsClient() && slot != CastingSlot::Item && slot != CastingSlot::PotionBelt && spells[spell_id].recast_time > 1000) { // 10 is item
|
||||
if(!CastToClient()->GetPTimers().Expired(&database, pTimerSpellStart + spell_id, false)) {
|
||||
//should we issue a message or send them a spell gem packet?
|
||||
Message_StringID(13, SPELL_RECAST);
|
||||
@ -895,7 +933,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
|
||||
}
|
||||
}
|
||||
|
||||
if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT)))
|
||||
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
|
||||
{
|
||||
IsFromItem = true;
|
||||
ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot);
|
||||
@ -1193,7 +1231,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
|
||||
|
||||
int16 DeleteChargeFromSlot = -1;
|
||||
|
||||
if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT))
|
||||
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)
|
||||
&& inventory_slot != 0xFFFFFFFF) // 10 is an item
|
||||
{
|
||||
bool fromaug = false;
|
||||
@ -1306,8 +1344,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
|
||||
{
|
||||
if(IsClient())
|
||||
{
|
||||
this->CastToClient()->CheckSongSkillIncrease(spell_id);
|
||||
this->CastToClient()->MemorizeSpell(slot, spell_id, memSpellSpellbar);
|
||||
Client *c = CastToClient();
|
||||
c->CheckSongSkillIncrease(spell_id);
|
||||
if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems)
|
||||
c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000);
|
||||
c->MemorizeSpell(static_cast<uint32>(slot), spell_id, memSpellSpellbar);
|
||||
}
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Bard song %d should be started", spell_id);
|
||||
}
|
||||
@ -1319,14 +1360,15 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
|
||||
SendSpellBarEnable(spell_id);
|
||||
|
||||
// this causes the delayed refresh of the spell bar gems
|
||||
c->MemorizeSpell(slot, spell_id, memSpellSpellbar);
|
||||
if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems)
|
||||
c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000);
|
||||
c->MemorizeSpell(static_cast<uint32>(slot), spell_id, memSpellSpellbar);
|
||||
|
||||
// this tells the client that casting may happen again
|
||||
SetMana(GetMana());
|
||||
|
||||
// skills
|
||||
if(slot < MAX_PP_MEMSPELL)
|
||||
{
|
||||
if (EQEmu::skills::IsCastingSkill(spells[spell_id].skill)) {
|
||||
c->CheckIncreaseSkill(spells[spell_id].skill, nullptr);
|
||||
|
||||
// increased chance of gaining channel skill if you regained concentration
|
||||
@ -1348,8 +1390,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
|
||||
|
||||
}
|
||||
|
||||
bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, uint16 slot) {
|
||||
|
||||
bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, CastingSlot slot, bool isproc)
|
||||
{
|
||||
/*
|
||||
The basic types of spells:
|
||||
|
||||
@ -1395,6 +1437,11 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
targetType = ST_GroupClientAndPet;
|
||||
}
|
||||
|
||||
// NPC innate procs override the target type to single target.
|
||||
// Yes. This code will cause issues if they have the proc as innate AND on a weapon. Oh well.
|
||||
if (isproc && IsNPC() && CastToNPC()->GetInnateProcSpellID() == spell_id)
|
||||
targetType = ST_Target;
|
||||
|
||||
if (spell_target && !spell_target->PassCastRestriction(true, spells[spell_id].CastRestriction)){
|
||||
Message_StringID(13,SPELL_NEED_TAR);
|
||||
return false;
|
||||
@ -1682,7 +1729,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
case ST_Group:
|
||||
case ST_GroupNoPets:
|
||||
{
|
||||
if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && slot != USE_ITEM_SPELL_SLOT) {
|
||||
if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB))) {
|
||||
if( (!target) ||
|
||||
(target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) ||
|
||||
(target->IsCorpse()) )
|
||||
@ -1728,7 +1775,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
{
|
||||
if(IsGrouped())
|
||||
{
|
||||
group_id_caster = GetGroup()->GetID();
|
||||
if (Group* group = GetGroup()) {
|
||||
group_id_caster = group->GetID();
|
||||
}
|
||||
}
|
||||
else if(IsRaidGrouped())
|
||||
{
|
||||
@ -1743,7 +1792,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
Mob *owner = GetOwner();
|
||||
if(owner->IsGrouped())
|
||||
{
|
||||
group_id_caster = owner->GetGroup()->GetID();
|
||||
if (Group* group = owner->GetGroup()) {
|
||||
group_id_caster = group->GetID();
|
||||
}
|
||||
}
|
||||
else if(owner->IsRaidGrouped())
|
||||
{
|
||||
@ -1772,7 +1823,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
{
|
||||
if(spell_target->IsGrouped())
|
||||
{
|
||||
group_id_target = spell_target->GetGroup()->GetID();
|
||||
if (Group* group = spell_target->GetGroup()) {
|
||||
group_id_target = group->GetID();
|
||||
}
|
||||
}
|
||||
else if(spell_target->IsRaidGrouped())
|
||||
{
|
||||
@ -1787,7 +1840,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
Mob *owner = spell_target->GetOwner();
|
||||
if(owner->IsGrouped())
|
||||
{
|
||||
group_id_target = owner->GetGroup()->GetID();
|
||||
if (Group* group = owner->GetGroup()) {
|
||||
group_id_target = group->GetID();
|
||||
}
|
||||
}
|
||||
else if(owner->IsRaidGrouped())
|
||||
{
|
||||
@ -1904,7 +1959,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
// only used from CastedSpellFinished, and procs
|
||||
// we can't interrupt in this, or anything called from this!
|
||||
// if you need to abort the casting, return false
|
||||
bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 mana_used,
|
||||
bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used,
|
||||
uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override)
|
||||
{
|
||||
//EQApplicationPacket *outapp = nullptr;
|
||||
@ -1974,7 +2029,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
|
||||
//determine the type of spell target we have
|
||||
CastAction_type CastAction;
|
||||
if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot))
|
||||
if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot, isproc))
|
||||
return(false);
|
||||
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: target type %d, target %s, AE center %s", spell_id, CastAction, spell_target?spell_target->GetName():"NONE", ae_center?ae_center->GetName():"NONE");
|
||||
@ -2263,7 +2318,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
|
||||
bool mgb = HasMGB() && spells[spell_id].can_mgb;
|
||||
// if this was a spell slot or an ability use up the mana for it
|
||||
if(slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && slot != TARGET_RING_SPELL_SLOT && mana_used > 0)
|
||||
if(slot != CastingSlot::Item && slot != CastingSlot::PotionBelt && mana_used > 0)
|
||||
{
|
||||
mana_used = GetActSpellCost(spell_id, mana_used);
|
||||
if (mgb) {
|
||||
@ -2326,7 +2381,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
}
|
||||
|
||||
if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT)))
|
||||
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
|
||||
{
|
||||
ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot);
|
||||
if(itm && itm->GetItem()->RecastDelay > 0){
|
||||
@ -2345,7 +2400,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
|
||||
if(IsNPC())
|
||||
CastToNPC()->AI_Event_SpellCastFinished(true, slot);
|
||||
CastToNPC()->AI_Event_SpellCastFinished(true, static_cast<uint16>(slot));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2360,8 +2415,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
*
|
||||
* return false to stop the song
|
||||
*/
|
||||
bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot) {
|
||||
if(slot == USE_ITEM_SPELL_SLOT) {
|
||||
bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slot) {
|
||||
if(slot == CastingSlot::Item) {
|
||||
//bard songs should never come from items...
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: Supposidly cast from an item. Killing song.", spell_id);
|
||||
return(false);
|
||||
@ -2972,8 +3027,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
||||
if
|
||||
(
|
||||
effect1 == SE_AttackSpeed ||
|
||||
effect1 == SE_AttackSpeed2 ||
|
||||
effect1 == SE_AttackSpeed3
|
||||
effect1 == SE_AttackSpeed2
|
||||
)
|
||||
{
|
||||
sp1_value -= 100;
|
||||
@ -3047,6 +3101,34 @@ bool Client::CheckSpellLevelRestriction(uint16 spell_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 Mob::GetFirstBuffSlot(bool disc, bool song)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 Mob::GetLastBuffSlot(bool disc, bool song)
|
||||
{
|
||||
return GetCurrentBuffSlots();
|
||||
}
|
||||
|
||||
uint32 Client::GetFirstBuffSlot(bool disc, bool song)
|
||||
{
|
||||
if (song)
|
||||
return GetMaxBuffSlots();
|
||||
if (disc)
|
||||
return GetMaxBuffSlots() + GetMaxSongSlots();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 Client::GetLastBuffSlot(bool disc, bool song)
|
||||
{
|
||||
if (song)
|
||||
return GetMaxBuffSlots() + GetCurrentSongSlots();
|
||||
if (disc)
|
||||
return GetMaxBuffSlots() + GetMaxSongSlots() + GetCurrentDiscSlots();
|
||||
return GetCurrentBuffSlots();
|
||||
}
|
||||
|
||||
// returns the slot the buff was added to, -1 if it wasn't added due to
|
||||
// stacking problems, and -2 if this is not a buff
|
||||
// if caster is null, the buff will be added with the caster level being
|
||||
@ -3084,18 +3166,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
||||
// we also check if overwriting will occur. this is so after this loop
|
||||
// we can determine if there will be room for this buff
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
uint32 start_slot = 0;
|
||||
uint32 end_slot = 0;
|
||||
if (IsDisciplineBuff(spell_id)) {
|
||||
start_slot = GetMaxBuffSlots() + GetMaxSongSlots();
|
||||
end_slot = start_slot + GetCurrentDiscSlots();
|
||||
} else if(spells[spell_id].short_buff_box) {
|
||||
start_slot = GetMaxBuffSlots();
|
||||
end_slot = start_slot + GetCurrentSongSlots();
|
||||
} else {
|
||||
start_slot = 0;
|
||||
end_slot = GetCurrentBuffSlots();
|
||||
}
|
||||
uint32 start_slot = GetFirstBuffSlot(IsDisciplineBuff(spell_id), spells[spell_id].short_buff_box);
|
||||
uint32 end_slot = GetLastBuffSlot(IsDisciplineBuff(spell_id), spells[spell_id].short_buff_box);
|
||||
|
||||
for (buffslot = 0; buffslot < buff_count; buffslot++) {
|
||||
const Buffs_Struct &curbuf = buffs[buffslot];
|
||||
@ -3107,7 +3179,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
||||
if (ret == -1) { // stop the spell
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Adding buff %d failed: stacking prevented by spell %d in slot %d with caster level %d",
|
||||
spell_id, curbuf.spellid, buffslot, curbuf.casterlevel);
|
||||
if (caster->IsClient() && RuleB(Client, UseLiveBlockedMessage)) {
|
||||
if (caster && caster->IsClient() && RuleB(Client, UseLiveBlockedMessage)) {
|
||||
caster->Message(13, "Your %s did not take hold on %s. (Blocked by %s.)", spells[spell_id].name, this->GetName(), spells[curbuf.spellid].name);
|
||||
}
|
||||
return -1;
|
||||
@ -3769,7 +3841,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
}
|
||||
|
||||
if (IsValidSpell(spells[spell_id].RecourseLink) && spells[spell_id].RecourseLink != spell_id)
|
||||
SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
|
||||
SpellFinished(spells[spell_id].RecourseLink, this, CastingSlot::Item, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
|
||||
|
||||
if (IsDetrimentalSpell(spell_id)) {
|
||||
|
||||
@ -4713,6 +4785,7 @@ void Mob::SendSpellBarEnable(uint16 spell_id)
|
||||
manachange->new_mana = GetMana();
|
||||
manachange->spell_id = spell_id;
|
||||
manachange->stamina = CastToClient()->GetEndurance();
|
||||
manachange->keepcasting = 0;
|
||||
outapp->priority = 6;
|
||||
CastToClient()->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
@ -5245,7 +5318,7 @@ bool Mob::UseBardSpellLogic(uint16 spell_id, int slot)
|
||||
spell_id = casting_spell_id;
|
||||
|
||||
if(slot == -1)
|
||||
slot = casting_spell_slot;
|
||||
slot = static_cast<int>(casting_spell_slot);
|
||||
|
||||
// should we treat this as a bard singing?
|
||||
return
|
||||
@ -5273,7 +5346,7 @@ void Mob::_StopSong()
|
||||
{
|
||||
bardsong = 0;
|
||||
bardsong_target_id = 0;
|
||||
bardsong_slot = 0;
|
||||
bardsong_slot = CastingSlot::Gem1;
|
||||
bardsong_timer.Disable();
|
||||
}
|
||||
|
||||
@ -5351,8 +5424,8 @@ void Mob::SendPetBuffsToClient()
|
||||
|
||||
int MaxSlots = GetMaxTotalSlots();
|
||||
|
||||
if(MaxSlots > BUFF_COUNT)
|
||||
MaxSlots = BUFF_COUNT;
|
||||
if(MaxSlots > PET_BUFF_COUNT)
|
||||
MaxSlots = PET_BUFF_COUNT;
|
||||
|
||||
for(int buffslot = 0; buffslot < MaxSlots; buffslot++)
|
||||
{
|
||||
@ -5449,10 +5522,14 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration)
|
||||
|
||||
int Client::GetCurrentBuffSlots() const
|
||||
{
|
||||
if(15 + aabonuses.BuffSlotIncrease > 25)
|
||||
return 25;
|
||||
else
|
||||
return 15 + aabonuses.BuffSlotIncrease;
|
||||
int numbuffs = 15;
|
||||
// client does check spells and items
|
||||
numbuffs += aabonuses.BuffSlotIncrease + spellbonuses.BuffSlotIncrease + itembonuses.BuffSlotIncrease;
|
||||
if (GetLevel() > 70)
|
||||
numbuffs++;
|
||||
if (GetLevel() > 74)
|
||||
numbuffs++;
|
||||
return EQEmu::ClampUpper(numbuffs, GetMaxBuffSlots());
|
||||
}
|
||||
|
||||
int Client::GetCurrentSongSlots() const
|
||||
@ -5644,3 +5721,26 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust)
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// duration in seconds
|
||||
void Client::SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration)
|
||||
{
|
||||
if (timer_id > 19)
|
||||
return;
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Setting Linked Spell Reuse %d for %d", timer_id, duration);
|
||||
GetPTimers().Start(pTimerLinkedSpellReuseStart + timer_id, duration);
|
||||
auto outapp = new EQApplicationPacket(OP_LinkedReuse, sizeof(LinkedSpellReuseTimer_Struct));
|
||||
auto lr = (LinkedSpellReuseTimer_Struct *)outapp->pBuffer;
|
||||
lr->timer_id = timer_id;
|
||||
lr->start_time = Timer::GetCurrentTime() / 1000;
|
||||
lr->end_time = lr->start_time + duration;
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
|
||||
bool Client::IsLinkedSpellReuseTimerReady(uint32 timer_id)
|
||||
{
|
||||
if (timer_id > 19)
|
||||
return true;
|
||||
return GetPTimers().Expired(&database, pTimerLinkedSpellReuseStart + timer_id, false);
|
||||
}
|
||||
|
||||
|
||||
@ -297,6 +297,7 @@
|
||||
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
|
||||
#define SUCCOR_FAIL 5169 //The portal collapes before you can escape!
|
||||
#define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.'
|
||||
#define AVOID_STUNNING_BLOW 5753 //You avoid the stunning blow.
|
||||
#define FATAL_BOW_SHOT 5745 //%1 performs a FATAL BOW SHOT!!
|
||||
#define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia!
|
||||
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
|
||||
@ -354,7 +355,7 @@
|
||||
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage.
|
||||
#define GLOWS_BLUE 9074 //Your %1 glows blue.
|
||||
#define GLOWS_RED 9075 //Your %1 glows red.
|
||||
#define SHAKE_OFF_STUN 9077
|
||||
#define SHAKE_OFF_STUN 9077 //You shake off the stun effect!
|
||||
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
|
||||
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2.
|
||||
#define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master.
|
||||
|
||||
@ -115,7 +115,7 @@ void Trap::Trigger(Mob* trigger)
|
||||
entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str());
|
||||
}
|
||||
if(hiddenTrigger){
|
||||
hiddenTrigger->SpellFinished(effectvalue, trigger, 10, 0, -1, spells[effectvalue].ResistDiff);
|
||||
hiddenTrigger->SpellFinished(effectvalue, trigger, EQEmu::CastingSlot::Item, 0, -1, spells[effectvalue].ResistDiff);
|
||||
}
|
||||
break;
|
||||
case trapTypeAlarm:
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
extern const ZoneConfig *Config;
|
||||
|
||||
enum WaterRegionType {
|
||||
enum WaterRegionType : int {
|
||||
RegionTypeUnsupported = -2,
|
||||
RegionTypeUntagged = -1,
|
||||
RegionTypeNormal = 0,
|
||||
@ -33,6 +33,8 @@ public:
|
||||
virtual bool InVWater(const glm::vec3& location) const = 0;
|
||||
virtual bool InLava(const glm::vec3& location) const = 0;
|
||||
virtual bool InLiquid(const glm::vec3& location) const = 0;
|
||||
virtual bool InPvP(const glm::vec3& location) const = 0;
|
||||
virtual bool InZoneLine(const glm::vec3& location) const = 0;
|
||||
|
||||
protected:
|
||||
virtual bool Load(FILE *fp) { return false; }
|
||||
|
||||
@ -30,6 +30,14 @@ bool WaterMapV1::InLiquid(const glm::vec3& location) const {
|
||||
return InWater(location) || InLava(location);
|
||||
}
|
||||
|
||||
bool WaterMapV1::InPvP(const glm::vec3& location) const {
|
||||
return ReturnRegionType(location) == RegionTypePVP;
|
||||
}
|
||||
|
||||
bool WaterMapV1::InZoneLine(const glm::vec3& location) const {
|
||||
return ReturnRegionType(location) == RegionTypeZoneLine;
|
||||
}
|
||||
|
||||
bool WaterMapV1::Load(FILE *fp) {
|
||||
uint32 bsp_tree_size;
|
||||
if (fread(&bsp_tree_size, sizeof(bsp_tree_size), 1, fp) != 1) {
|
||||
|
||||
@ -24,6 +24,8 @@ public:
|
||||
virtual bool InVWater(const glm::vec3& location) const;
|
||||
virtual bool InLava(const glm::vec3& location) const;
|
||||
virtual bool InLiquid(const glm::vec3& location) const;
|
||||
virtual bool InPvP(const glm::vec3& location) const;
|
||||
virtual bool InZoneLine(const glm::vec3& location) const;
|
||||
|
||||
protected:
|
||||
virtual bool Load(FILE *fp);
|
||||
|
||||
@ -33,6 +33,14 @@ bool WaterMapV2::InLiquid(const glm::vec3& location) const {
|
||||
return InWater(location) || InLava(location);
|
||||
}
|
||||
|
||||
bool WaterMapV2::InPvP(const glm::vec3& location) const {
|
||||
return ReturnRegionType(location) == RegionTypePVP;
|
||||
}
|
||||
|
||||
bool WaterMapV2::InZoneLine(const glm::vec3& location) const {
|
||||
return ReturnRegionType(location) == RegionTypeZoneLine;
|
||||
}
|
||||
|
||||
bool WaterMapV2::Load(FILE *fp) {
|
||||
uint32 region_count;
|
||||
if (fread(®ion_count, sizeof(region_count), 1, fp) != 1) {
|
||||
|
||||
@ -17,6 +17,8 @@ public:
|
||||
virtual bool InVWater(const glm::vec3& location) const;
|
||||
virtual bool InLava(const glm::vec3& location) const;
|
||||
virtual bool InLiquid(const glm::vec3& location) const;
|
||||
virtual bool InPvP(const glm::vec3& location) const;
|
||||
virtual bool InZoneLine(const glm::vec3& location) const;
|
||||
|
||||
protected:
|
||||
virtual bool Load(FILE *fp);
|
||||
|
||||
@ -765,7 +765,7 @@ void WorldServer::Process() {
|
||||
zone->SetZoneHasCurrentTime(true);
|
||||
|
||||
}
|
||||
if (zone->is_zone_time_localized){
|
||||
if (zone && zone->is_zone_time_localized){
|
||||
Log.Out(Logs::General, Logs::Zone_Server, "Received request to sync time from world, but our time is localized currently");
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1594,7 +1594,9 @@ ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, uint32 to, Clien
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
if(closest_dist > 400.0f && closest_dist < max_distance2)
|
||||
// if we have a water map and it says we're in a zoneline, lets assume it's just a really big zone line
|
||||
// this shouldn't open up any exploits since those situations are detected later on
|
||||
if ((zone->HasWaterMap() && !zone->watermap->InZoneLine(glm::vec3(client->GetPosition()))) || (!zone->HasWaterMap() && closest_dist > 400.0f && closest_dist < max_distance2))
|
||||
{
|
||||
if(client)
|
||||
client->CheatDetected(MQZoneUnknownDest, location.x, location.y, location.z); // Someone is trying to use /zone
|
||||
|
||||
@ -3178,7 +3178,8 @@ void ZoneDatabase::SavePetInfo(Client *client)
|
||||
query.clear();
|
||||
|
||||
// pet buffs!
|
||||
for (int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) {
|
||||
int max_slots = RuleI(Spells, MaxTotalSlotsPET);
|
||||
for (int index = 0; index < max_slots; index++) {
|
||||
if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0)
|
||||
continue;
|
||||
if (query.length() == 0)
|
||||
|
||||
@ -125,7 +125,7 @@ struct PetInfo {
|
||||
uint32 HP;
|
||||
uint32 Mana;
|
||||
float size;
|
||||
SpellBuff_Struct Buffs[BUFF_COUNT];
|
||||
SpellBuff_Struct Buffs[PET_BUFF_COUNT];
|
||||
uint32 Items[EQEmu::legacy::EQUIPMENT_SIZE];
|
||||
char Name[64];
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user