Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Paul Coene 2016-08-23 13:55:52 -04:00
commit 8048239a81
88 changed files with 3316 additions and 592 deletions

View File

@ -1,5 +1,34 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) 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 == == 07/28/2016 ==
Uleat: Implemented zone memory-mapped file usage 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 - Zone map files are converted to pre-loaded binary files, bypassing the (sometimes) time-consuming raw data transform process

View File

@ -44,6 +44,26 @@ namespace EQEmu
const size_t SayLinkBodySize = RoF2::constants::SayLinkBodySize; const size_t SayLinkBodySize = RoF2::constants::SayLinkBodySize;
} /*constants*/ } /*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*/ } /*EQEmu*/

View File

@ -289,6 +289,7 @@ N(OP_LFGuild),
N(OP_LFPCommand), N(OP_LFPCommand),
N(OP_LFPGetMatchesRequest), N(OP_LFPGetMatchesRequest),
N(OP_LFPGetMatchesResponse), N(OP_LFPGetMatchesResponse),
N(OP_LinkedReuse),
N(OP_LoadSpellSet), N(OP_LoadSpellSet),
N(OP_LocInfo), N(OP_LocInfo),
N(OP_LockoutTimerInfo), N(OP_LockoutTimerInfo),

View File

@ -30,6 +30,7 @@
static const uint32 BUFF_COUNT = 25; static const uint32 BUFF_COUNT = 25;
static const uint32 PET_BUFF_COUNT = 30;
static const uint32 MAX_MERC = 100; static const uint32 MAX_MERC = 100;
static const uint32 MAX_MERC_GRADES = 10; static const uint32 MAX_MERC_GRADES = 10;
static const uint32 MAX_MERC_STANCES = 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 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) 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 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 struct ManaChange_Struct
{ {
uint32 new_mana; // New Mana AMount /*00*/ uint32 new_mana; // New Mana AMount
uint32 stamina; /*04*/ uint32 stamina;
uint32 spell_id; /*08*/ uint32 spell_id;
uint32 unknown12; /*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 struct SwapSpell_Struct
@ -522,8 +536,8 @@ struct BuffRemoveRequest_Struct
struct PetBuff_Struct { struct PetBuff_Struct {
/*000*/ uint32 petid; /*000*/ uint32 petid;
/*004*/ uint32 spellid[BUFF_COUNT+5]; /*004*/ uint32 spellid[PET_BUFF_COUNT];
/*124*/ int32 ticsremaining[BUFF_COUNT+5]; /*124*/ int32 ticsremaining[PET_BUFF_COUNT];
/*244*/ uint32 buffcount; /*244*/ uint32 buffcount;
}; };
@ -834,7 +848,7 @@ struct SuspendedMinion_Struct
*/ */
static const uint32 MAX_PP_LANGUAGE = 28; static const uint32 MAX_PP_LANGUAGE = 28;
static const uint32 MAX_PP_SPELLBOOK = 480; // Set for all functions 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_SPELLBOOK = 480; // Set for Player Profile size retain
static const uint32 MAX_PP_REF_MEMSPELL = 9; // 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; /*0245*/ uint8 guildbanker;
/*0246*/ uint8 unknown0246[6]; // /*0246*/ uint8 unknown0246[6]; //
/*0252*/ uint32 intoxication; /*0252*/ uint32 intoxication;
/*0256*/ uint32 spellSlotRefresh[MAX_PP_REF_MEMSPELL]; //in ms /*0256*/ uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; //in ms
/*0292*/ uint32 abilitySlotRefresh; /*0292*/ uint32 abilitySlotRefresh;
/*0296*/ uint8 haircolor; // Player hair color /*0296*/ uint8 haircolor; // Player hair color
/*0297*/ uint8 beardcolor; // Player beard color /*0297*/ uint8 beardcolor; // Player beard color
@ -956,7 +970,7 @@ struct PlayerProfile_Struct
/*2580*/ uint8 unknown2616[4]; /*2580*/ uint8 unknown2616[4];
/*2584*/ uint32 spell_book[MAX_PP_REF_SPELLBOOK]; /*2584*/ uint32 spell_book[MAX_PP_REF_SPELLBOOK];
/*4504*/ uint8 unknown4540[128]; // Was [428] all 0xff /*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]; // /*4668*/ uint8 unknown4704[32]; //
/*4700*/ float y; // Player y position /*4700*/ float y; // Player y position
/*4704*/ float x; // Player x position /*4704*/ float x; // Player x position

View File

@ -1863,6 +1863,9 @@ uint8 ItemInst::FirstOpenSlot() const
uint8 ItemInst::GetTotalItemCount() const uint8 ItemInst::GetTotalItemCount() const
{ {
if (!m_item)
return 0;
uint8 item_count = 1; uint8 item_count = 1;
if (m_item && !m_item->IsClassBag()) { return item_count; } if (m_item && !m_item->IsClassBag()) { return item_count; }

View File

@ -63,6 +63,9 @@ namespace RoF
// client to server text link converter // client to server text link converter
static inline void RoFToServerTextLink(std::string& serverTextLink, const std::string& rofTextLink); 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) void Register(EQStreamIdentifier &into)
{ {
//create our opcode manager if we havent already //create our opcode manager if we havent already
@ -507,10 +510,7 @@ namespace RoF
ENCODE_LENGTH_EXACT(CastSpell_Struct); ENCODE_LENGTH_EXACT(CastSpell_Struct);
SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct); SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct);
if (emu->slot == 10) eq->slot = static_cast<uint32>(ServerToRoFCastingSlot(static_cast<EQEmu::CastingSlot>(emu->slot)));
eq->slot = 13;
else
OUT(slot);
OUT(spell_id); OUT(spell_id);
eq->inventory_slot = ServerToRoFSlot(emu->inventoryslot); eq->inventory_slot = ServerToRoFSlot(emu->inventoryslot);
@ -1623,7 +1623,8 @@ namespace RoF
OUT(new_mana); OUT(new_mana);
OUT(stamina); OUT(stamina);
OUT(spell_id); 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(); 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->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff)
__packet->WriteUInt16(emu->buffcount); __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]) if (emu->spellid[i])
{ {
__packet->WriteUInt32(i); __packet->WriteUInt32(i);
__packet->WriteUInt32(emu->spellid[i]); __packet->WriteUInt32(emu->spellid[i]);
__packet->WriteUInt32(emu->ticsremaining[i]); __packet->WriteUInt32(emu->ticsremaining[i]);
__packet->WriteUInt32(0); // Unknown __packet->WriteUInt32(0); // numhits
__packet->WriteString(""); __packet->WriteString("");
} }
} }
__packet->WriteUInt8(0); // Unknown __packet->WriteUInt8(0); // some sort of type
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -2259,22 +2260,23 @@ namespace RoF
outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots 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]); 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++) for (uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++)
{ {
outapp->WriteUInt32(0xFFFFFFFFU); 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 outapp->WriteUInt8(0); // Unknown
@ -4334,10 +4336,7 @@ namespace RoF
DECODE_LENGTH_EXACT(structs::CastSpell_Struct); DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
if (eq->slot == 13) emu->slot = static_cast<uint32>(RoFToServerCastingSlot(static_cast<CastingSlot>(eq->slot)));
emu->slot = 10;
else
IN(slot);
IN(spell_id); IN(spell_id);
emu->inventoryslot = RoFToServerSlot(eq->inventory_slot); 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*/ } /*RoF*/

View File

@ -50,6 +50,24 @@ namespace RoF
#include "rof_ops.h" #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*/ }; /*RoF*/
#endif /*COMMON_ROF_H*/ #endif /*COMMON_ROF_H*/

View File

@ -63,6 +63,9 @@ namespace RoF2
// client to server text link converter // client to server text link converter
static inline void RoF2ToServerTextLink(std::string& serverTextLink, const std::string& rof2TextLink); 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) void Register(EQStreamIdentifier &into)
{ {
//create our opcode manager if we havent already //create our opcode manager if we havent already
@ -584,10 +587,7 @@ namespace RoF2
ENCODE_LENGTH_EXACT(CastSpell_Struct); ENCODE_LENGTH_EXACT(CastSpell_Struct);
SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct); SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct);
if (emu->slot == 10) eq->slot = static_cast<uint32>(ServerToRoF2CastingSlot(static_cast<EQEmu::CastingSlot>(emu->slot)));
eq->slot = 13;
else
OUT(slot);
OUT(spell_id); OUT(spell_id);
eq->inventory_slot = ServerToRoF2Slot(emu->inventoryslot); eq->inventory_slot = ServerToRoF2Slot(emu->inventoryslot);
@ -1701,7 +1701,8 @@ namespace RoF2
OUT(new_mana); OUT(new_mana);
OUT(stamina); OUT(stamina);
OUT(spell_id); 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(); 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->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff)
__packet->WriteUInt16(emu->buffcount); __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]) if (emu->spellid[i])
{ {
__packet->WriteUInt32(i); __packet->WriteUInt32(i);
__packet->WriteUInt32(emu->spellid[i]); __packet->WriteUInt32(emu->spellid[i]);
__packet->WriteUInt32(emu->ticsremaining[i]); __packet->WriteUInt32(emu->ticsremaining[i]);
__packet->WriteUInt32(0); // Unknown __packet->WriteUInt32(0); // num hits
__packet->WriteString(""); __packet->WriteString("");
} }
} }
__packet->WriteUInt8(0); // Unknown __packet->WriteUInt8(0); // some sort of type
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -2346,22 +2347,23 @@ namespace RoF2
outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots 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]); 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++) for (uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++)
{ {
outapp->WriteUInt32(0xFFFFFFFFU); 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 outapp->WriteUInt8(0); // Unknown
@ -4574,10 +4576,7 @@ namespace RoF2
DECODE_LENGTH_EXACT(structs::CastSpell_Struct); DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
if (eq->slot == 13) emu->slot = static_cast<uint32>(RoF2ToServerCastingSlot(static_cast<CastingSlot>(eq->slot)));
emu->slot = 10;
else
IN(slot);
IN(spell_id); IN(spell_id);
emu->inventoryslot = RoF2ToServerSlot(eq->inventory_slot); 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*/ } /*RoF2*/

View File

@ -50,6 +50,24 @@ namespace RoF2
#include "rof2_ops.h" #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*/ }; /*RoF2*/
#endif /*COMMON_ROF2_H*/ #endif /*COMMON_ROF2_H*/

View File

@ -635,7 +635,7 @@ struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) 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 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 struct ManaChange_Struct
{ {
uint32 new_mana; // New Mana AMount /*00*/ uint32 new_mana; // New Mana AMount
uint32 stamina; /*04*/ uint32 stamina;
uint32 spell_id; /*08*/ uint32 spell_id;
uint32 unknown12; /*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
uint32 unknown16; /*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 struct SwapSpell_Struct

View File

@ -624,7 +624,7 @@ struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) 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 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 struct ManaChange_Struct
{ {
uint32 new_mana; // New Mana AMount /*00*/ uint32 new_mana; // New Mana AMount
uint32 stamina; /*04*/ uint32 stamina;
uint32 spell_id; /*08*/ uint32 spell_id;
uint32 unknown12; /*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
uint32 unknown16; /*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 struct SwapSpell_Struct

View File

@ -59,6 +59,9 @@ namespace SoD
// client to server text link converter // client to server text link converter
static inline void SoDToServerTextLink(std::string& serverTextLink, const std::string& sodTextLink); 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) void Register(EQStreamIdentifier &into)
{ {
//create our opcode manager if we havent already //create our opcode manager if we havent already
@ -1136,7 +1139,8 @@ namespace SoD
OUT(new_mana); OUT(new_mana);
OUT(stamina); OUT(stamina);
OUT(spell_id); 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(); FINISH_ENCODE();
} }
@ -1499,7 +1503,7 @@ namespace SoD
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid);
VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); 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]) 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; delete[] __emu_buffer;
dest->FastQueuePacket(&in, ack_req); dest->FastQueuePacket(&in, ack_req);
@ -2948,6 +2952,7 @@ namespace SoD
DECODE_LENGTH_EXACT(structs::CastSpell_Struct); DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
SETUP_DIRECT_DECODE(CastSpell_Struct, 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(slot);
IN(spell_id); IN(spell_id);
emu->inventoryslot = SoDToServerSlot(eq->inventoryslot); 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*/ } /*SoD*/

View File

@ -50,6 +50,22 @@ namespace SoD
#include "sod_ops.h" #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*/ }; /*SoD*/
#endif /*COMMON_SOD_H*/ #endif /*COMMON_SOD_H*/

View File

@ -479,7 +479,7 @@ struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) 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 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 struct ManaChange_Struct
{ {
uint32 new_mana; // New Mana AMount /*00*/ uint32 new_mana; // New Mana AMount
uint32 stamina; /*04*/ uint32 stamina;
uint32 spell_id; /*08*/ uint32 spell_id;
uint32 unknown12; /*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
uint32 unknown16; /*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 struct SwapSpell_Struct

View File

@ -59,6 +59,9 @@ namespace SoF
// client to server text link converter // client to server text link converter
static inline void SoFToServerTextLink(std::string& serverTextLink, const std::string& sofTextLink); 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) void Register(EQStreamIdentifier &into)
{ {
//create our opcode manager if we havent already //create our opcode manager if we havent already
@ -933,7 +936,24 @@ namespace SoF
OUT(new_mana); OUT(new_mana);
OUT(stamina); OUT(stamina);
OUT(spell_id); 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(); FINISH_ENCODE();
} }
@ -1158,9 +1178,9 @@ namespace SoF
OUT(petid); OUT(petid);
OUT(buffcount); 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]) if (emu->spellid[EmuBuffSlot])
{ {
@ -2366,7 +2386,7 @@ namespace SoF
DECODE_LENGTH_EXACT(structs::CastSpell_Struct); DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
SETUP_DIRECT_DECODE(CastSpell_Struct, 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); IN(spell_id);
emu->inventoryslot = SoFToServerSlot(eq->inventoryslot); emu->inventoryslot = SoFToServerSlot(eq->inventoryslot);
IN(target_id); 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*/ } /*SoF*/

View File

@ -50,6 +50,23 @@ namespace SoF
#include "sof_ops.h" #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*/ }; /*SoF*/
#endif /*COMMON_SOF_H*/ #endif /*COMMON_SOF_H*/

View File

@ -57,6 +57,7 @@ E(OP_LeadershipExpUpdate)
E(OP_LogServer) E(OP_LogServer)
E(OP_LootItem) E(OP_LootItem)
E(OP_ManaChange) E(OP_ManaChange)
E(OP_MemorizeSpell)
E(OP_MoveItem) E(OP_MoveItem)
E(OP_NewSpawn) E(OP_NewSpawn)
E(OP_NewZone) E(OP_NewZone)

View File

@ -458,7 +458,7 @@ struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) 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 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 struct ManaChange_Struct
{ {
uint32 new_mana; // New Mana AMount /*00*/ uint32 new_mana; // New Mana AMount
uint32 stamina; /*04*/ uint32 stamina;
uint32 spell_id; /*08*/ uint32 spell_id;
uint32 unknown12; /*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
uint32 unknown16; /*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 struct SwapSpell_Struct

View File

@ -58,6 +58,9 @@ namespace Titanium
// client to server text link converter // client to server text link converter
static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink); 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) void Register(EQStreamIdentifier &into)
{ {
auto Config = EQEmuConfig::get(); auto Config = EQEmuConfig::get();
@ -833,6 +836,22 @@ namespace Titanium
FINISH_ENCODE(); 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(OP_MoveItem)
{ {
ENCODE_LENGTH_EXACT(MoveItem_Struct); ENCODE_LENGTH_EXACT(MoveItem_Struct);
@ -872,9 +891,9 @@ namespace Titanium
OUT(petid); OUT(petid);
OUT(buffcount); 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]) if (emu->spellid[EmuBuffSlot])
{ {
@ -1731,7 +1750,7 @@ namespace Titanium
DECODE_LENGTH_EXACT(structs::CastSpell_Struct); DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
SETUP_DIRECT_DECODE(CastSpell_Struct, 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); IN(spell_id);
emu->inventoryslot = TitaniumToServerSlot(eq->inventoryslot); emu->inventoryslot = TitaniumToServerSlot(eq->inventoryslot);
IN(target_id); 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*/ } /*Titanium*/

View File

@ -50,6 +50,23 @@ namespace Titanium
#include "titanium_ops.h" #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*/ }; /*Titanium*/
#endif /*COMMON_TITANIUM_H*/ #endif /*COMMON_TITANIUM_H*/

View File

@ -50,6 +50,7 @@ E(OP_ItemPacket)
E(OP_LeadershipExpUpdate) E(OP_LeadershipExpUpdate)
E(OP_LFGuild) E(OP_LFGuild)
E(OP_LootItem) E(OP_LootItem)
E(OP_MemorizeSpell)
E(OP_MoveItem) E(OP_MoveItem)
E(OP_OnLevelMessage) E(OP_OnLevelMessage)
E(OP_PetBuffWindow) E(OP_PetBuffWindow)

View File

@ -390,7 +390,7 @@ struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) 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 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]; /*005*/uint8 unknowndss006[3];
/*008*/ /*008*/
}; };
struct ManaChange_Struct struct ManaChange_Struct
{ {
uint32 new_mana; // New Mana AMount /*00*/ uint32 new_mana; // New Mana AMount
uint32 stamina; /*04*/ uint32 stamina;
uint32 spell_id; /*08*/ uint32 spell_id;
uint32 unknown12; /*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 struct SwapSpell_Struct

View File

@ -59,6 +59,9 @@ namespace UF
// client to server text link converter // client to server text link converter
static inline void UFToServerTextLink(std::string& serverTextLink, const std::string& ufTextLink); 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) void Register(EQStreamIdentifier &into)
{ {
//create our opcode manager if we havent already //create our opcode manager if we havent already
@ -1383,7 +1386,8 @@ namespace UF
OUT(new_mana); OUT(new_mana);
OUT(stamina); OUT(stamina);
OUT(spell_id); 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(); FINISH_ENCODE();
} }
@ -1761,18 +1765,18 @@ namespace UF
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1);
VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); 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]) if (emu->spellid[i])
{ {
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[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, 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; delete[] __emu_buffer;
dest->FastQueuePacket(&in, ack_req); dest->FastQueuePacket(&in, ack_req);
@ -1867,7 +1871,7 @@ namespace UF
OUT(thirst_level); OUT(thirst_level);
OUT(hunger_level); OUT(hunger_level);
//PS this needs to be figured out more; but it was 'good enough' //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) if (emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0)
{ {
@ -3259,10 +3263,7 @@ namespace UF
DECODE_LENGTH_EXACT(structs::CastSpell_Struct); DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
if (eq->slot == 13) emu->slot = static_cast<uint32>(UFToServerCastingSlot(static_cast<CastingSlot>(eq->slot)));
emu->slot = 10;
else
IN(slot);
IN(spell_id); IN(spell_id);
emu->inventoryslot = UFToServerSlot(eq->inventoryslot); 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*/ } /*UF*/

View File

@ -50,6 +50,24 @@ namespace UF
#include "uf_ops.h" #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*/ }; /*UF*/
#endif /*COMMON_UF_H*/ #endif /*COMMON_UF_H*/

View File

@ -26,7 +26,7 @@ namespace UF
namespace structs { namespace structs {
static const uint32 BUFF_COUNT = 25; static const uint32 BUFF_COUNT = 30;
/* /*
** Compiler override to ensure ** Compiler override to ensure
@ -479,7 +479,7 @@ struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) 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 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 struct ManaChange_Struct
{ {
uint32 new_mana; // New Mana AMount /*00*/ uint32 new_mana; // New Mana AMount
uint32 stamina; /*04*/ uint32 stamina;
uint32 spell_id; /*08*/ uint32 spell_id;
uint32 unknown12; /*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
uint32 unknown16; /*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 struct SwapSpell_Struct
@ -958,8 +959,7 @@ struct PlayerProfile_Struct
/*07880*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3) /*07880*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3)
/*07884*/ uint32 thirst_level; // Drink (ticks till next drink) /*07884*/ uint32 thirst_level; // Drink (ticks till next drink)
/*07888*/ uint32 hunger_level; // Food (ticks till next eat) /*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) /*07892*/ SpellBuff_Struct buffs[BUFF_COUNT]; // [2280] 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
/*10172*/ Disciplines_Struct disciplines; // [400] Known disciplines /*10172*/ Disciplines_Struct disciplines; // [400] Known disciplines
/*10972*/ uint32 recastTimers[MAX_RECAST_TYPES]; // Timers (UNIX Time of last use) /*10972*/ uint32 recastTimers[MAX_RECAST_TYPES]; // Timers (UNIX Time of last use)
/*11052*/ uint8 unknown11052[160]; // Some type of Timers /*11052*/ uint8 unknown11052[160]; // Some type of Timers
@ -2167,9 +2167,7 @@ struct GroupFollow_Struct { // Underfoot Follow Struct
struct InspectBuffs_Struct { struct InspectBuffs_Struct {
/*000*/ uint32 spell_id[BUFF_COUNT]; /*000*/ uint32 spell_id[BUFF_COUNT];
/*100*/ uint32 filler100[5]; // BUFF_COUNT is really 30...
/*120*/ int32 tics_remaining[BUFF_COUNT]; /*120*/ int32 tics_remaining[BUFF_COUNT];
/*220*/ uint32 filler220[5]; // BUFF_COUNT is really 30...
}; };

View File

@ -37,10 +37,12 @@ enum { //values for pTimerType
pTimerSenseTraps = 12, pTimerSenseTraps = 12,
pTimerDisarmTraps = 13, pTimerDisarmTraps = 13,
pTimerDisciplineReuseStart = 14, pTimerDisciplineReuseStart = 14,
pTimerDisciplineReuseEnd = 24, pTimerDisciplineReuseEnd = 24, // client actually has 20 ids, but still no disc go that high even on live
pTimerCombatAbility = 25, pTimerCombatAbility = 25,
pTimerCombatAbility2 = 26, // RoF2+ Tiger Claw is unlinked from other monk skills, generic in case other classes ever need it pTimerCombatAbility2 = 26, // RoF2+ Tiger Claw is unlinked from other monk skills, generic in case other classes ever need it
pTimerBeggingPickPocket = 27, pTimerBeggingPickPocket = 27,
pTimerLinkedSpellReuseStart = 28,
pTimerLinkedSpellReuseEnd = 48,
pTimerLayHands = 87, //these IDs are used by client too pTimerLayHands = 87, //these IDs are used by client too
pTimerHarmTouch = 89, //so dont change them pTimerHarmTouch = 89, //so dont change them

View File

@ -45,6 +45,7 @@
#define TIGER 63 #define TIGER 63
#define ELEMENTAL 75 #define ELEMENTAL 75
#define ALLIGATOR 91 #define ALLIGATOR 91
#define OGGOK_CITIZEN 93
#define EYE_OF_ZOMM 108 #define EYE_OF_ZOMM 108
#define WOLF_ELEMENTAL 120 #define WOLF_ELEMENTAL 120
#define INVISIBLE_MAN 127 #define INVISIBLE_MAN 127

View File

@ -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_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, 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, 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_END()
RULE_CATEGORY(Mercs) RULE_CATEGORY(Mercs)
@ -329,7 +331,7 @@ RULE_INT(Spells, MaxBuffSlotsNPC, 25)
RULE_INT(Spells, MaxSongSlotsNPC, 10) RULE_INT(Spells, MaxSongSlotsNPC, 10)
RULE_INT(Spells, MaxDiscSlotsNPC, 1) RULE_INT(Spells, MaxDiscSlotsNPC, 1)
RULE_INT(Spells, MaxTotalSlotsNPC, 36) 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_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, 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 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, 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, 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, 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_END()
RULE_CATEGORY(Combat) 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, NPCToNPCAggroTimerMin, 500)
RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000) RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000)
RULE_BOOL(NPC, UseClassAsLastName, true) // Uses class archetype as LastName for npcs with none 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_END()
RULE_CATEGORY(Aggro) 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, 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_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, 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_END()
RULE_CATEGORY(TaskSystem) RULE_CATEGORY(TaskSystem)

View File

@ -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 data_str(row[9]);
std::string idAsString; std::string idAsString;
std::string value; 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); put_slot_id = inv->PutItem(slot_id, *inst);
safe_delete(inst); safe_delete(inst);

View File

@ -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() const std::map<EQEmu::skills::SkillType, std::string>& EQEmu::skills::GetSkillTypeMap()
{ {
/* VS2013 code /* VS2013 code

View File

@ -161,10 +161,11 @@ namespace EQEmu
// server profile does not reflect this yet..so, prefixed with 'PACKET_' // server profile does not reflect this yet..so, prefixed with 'PACKET_'
#define PACKET_SKILL_ARRAY_SIZE 100 #define PACKET_SKILL_ARRAY_SIZE 100
extern bool IsTradeskill(SkillType skill); bool IsTradeskill(SkillType skill);
extern bool IsSpecializedSkill(SkillType skill); bool IsSpecializedSkill(SkillType skill);
extern float GetSkillMeleePushForce(SkillType skill); float GetSkillMeleePushForce(SkillType skill);
extern bool IsBardInstrumentSkill(SkillType skill); bool IsBardInstrumentSkill(SkillType skill);
bool IsCastingSkill(SkillType skill);
extern const std::map<SkillType, std::string>& GetSkillTypeMap(); extern const std::map<SkillType, std::string>& GetSkillTypeMap();

View File

@ -235,8 +235,11 @@ bool IsBeneficialSpell(uint16 spell_id)
// If the resisttype is magic and SpellAffectIndex is Calm/memblur/dispell sight // If the resisttype is magic and SpellAffectIndex is Calm/memblur/dispell sight
// it's not beneficial // it's not beneficial
if (spells[spell_id].resisttype == RESIST_MAGIC) { if (spells[spell_id].resisttype == RESIST_MAGIC) {
if (sai == SAI_Calm || sai == SAI_Dispell_Sight || // checking these SAI cause issues with the rng defensive proc line
sai == SAI_Memory_Blur || sai == SAI_Calm_Song) // 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; return false;
} else { } else {
// If the resisttype is not magic and spell is Bind Sight or Cast Sight // 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)) if (!IsValidSpell(spell_id))
return false; return false;
if (spells[spell_id].mana == 0 && spells[spell_id].short_buff_box == 0 && if (spells[spell_id].IsDisciplineBuff && spells[spell_id].targettype == ST_Self)
(spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep) &&
spells[spell_id].targettype == ST_Self)
return true; return true;
return false; return false;

View File

@ -446,7 +446,7 @@ typedef enum {
#define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap
#define SE_Purify 291 // implemented - Removes determental effects #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_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_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier.
//#define SE_ReduceTimerSpecial 295 // not used //#define SE_ReduceTimerSpecial 295 // not used
#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage #define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage

View File

@ -1985,7 +1985,7 @@ void Client::ChannelGrantVoice(std::string CommandString) {
return; 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."); GeneralChannelMessage("The channel owner and moderators automatically have voice.");
return; return;

View File

@ -172,6 +172,7 @@ OP_BeginCast=0x17ff
OP_ColoredText=0x41cb OP_ColoredText=0x41cb
OP_ConsentResponse=0x183d OP_ConsentResponse=0x183d
OP_MemorizeSpell=0x2fac OP_MemorizeSpell=0x2fac
OP_LinkedReuse=0x3ac0
OP_SwapSpell=0x4736 OP_SwapSpell=0x4736
OP_CastSpell=0x1cb5 OP_CastSpell=0x1cb5
OP_Consider=0x4d8d OP_Consider=0x4d8d

View File

@ -171,6 +171,7 @@ OP_BeginCast=0x318f
OP_ColoredText=0x43af OP_ColoredText=0x43af
OP_ConsentResponse=0x384a OP_ConsentResponse=0x384a
OP_MemorizeSpell=0x217c OP_MemorizeSpell=0x217c
OP_LinkedReuse=0x1619
OP_SwapSpell=0x0efa OP_SwapSpell=0x0efa
OP_CastSpell=0x1287 OP_CastSpell=0x1287
OP_Consider=0x742b OP_Consider=0x742b

View File

@ -171,6 +171,7 @@ OP_BeginCast=0x0d5a # C
OP_ColoredText=0x569a # C OP_ColoredText=0x569a # C
OP_ConsentResponse=0x6e47 # C OP_ConsentResponse=0x6e47 # C
OP_MemorizeSpell=0x8543 # C OP_MemorizeSpell=0x8543 # C
OP_LinkedReuse=0x6ef9
OP_SwapSpell=0x3fd2 # C OP_SwapSpell=0x3fd2 # C
OP_CastSpell=0x3582 # C OP_CastSpell=0x3582 # C
OP_Consider=0x6024 # C OP_Consider=0x6024 # C

View File

@ -169,6 +169,7 @@ OP_BeginCast=0x5A50 #SEQ 12/04/08
OP_ColoredText=0x3BC7 #SEQ 12/04/08 OP_ColoredText=0x3BC7 #SEQ 12/04/08
OP_ConsentResponse=0x4D30 #SEQ 12/04/08 OP_ConsentResponse=0x4D30 #SEQ 12/04/08
OP_MemorizeSpell=0x6A93 #SEQ 12/04/08 OP_MemorizeSpell=0x6A93 #SEQ 12/04/08
OP_LinkedReuse=0x2c26
OP_SwapSpell=0x1418 #SEQ 12/04/08 OP_SwapSpell=0x1418 #SEQ 12/04/08
OP_CastSpell=0x7F5D #SEQ 12/04/08 OP_CastSpell=0x7F5D #SEQ 12/04/08
OP_Consider=0x32E1 #SEQ 12/04/08 OP_Consider=0x32E1 #SEQ 12/04/08

View File

@ -170,6 +170,7 @@ OP_Save=0x736b # ShowEQ 10/27/05
OP_Camp=0x78c1 # ShowEQ 10/27/05 OP_Camp=0x78c1 # ShowEQ 10/27/05
OP_EndLootRequest=0x2316 # ShowEQ 10/27/05 OP_EndLootRequest=0x2316 # ShowEQ 10/27/05
OP_MemorizeSpell=0x308e # ShowEQ 10/27/05 OP_MemorizeSpell=0x308e # ShowEQ 10/27/05
OP_LinkedReuse=0x6a00
OP_SwapSpell=0x2126 # ShowEQ 10/27/05 OP_SwapSpell=0x2126 # ShowEQ 10/27/05
OP_CastSpell=0x304b # ShowEQ 10/27/05 OP_CastSpell=0x304b # ShowEQ 10/27/05
OP_DeleteSpell=0x4f37 OP_DeleteSpell=0x4f37

View File

@ -173,6 +173,7 @@ OP_BeginCast=0x0d5a # C
OP_ColoredText=0x71bf # C OP_ColoredText=0x71bf # C
OP_ConsentResponse=0x0e87 # C OP_ConsentResponse=0x0e87 # C
OP_MemorizeSpell=0x3887 # C OP_MemorizeSpell=0x3887 # C
OP_LinkedReuse=0x1b26
OP_SwapSpell=0x5805 # C OP_SwapSpell=0x5805 # C
OP_CastSpell=0x50c2 # C OP_CastSpell=0x50c2 # C
OP_Consider=0x3c2d # C OP_Consider=0x3c2d # C

View File

@ -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 " 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 " 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 " 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 ' Example: perl DB_Dumper.pl Loc="E:\Backups"' . "\n\n";
print "######################################################\n"; print "######################################################\n";
exit; exit;
@ -72,6 +73,11 @@ while($ARGV[$n]){
print "Database is " . $DB_NAME[1] . "\n"; print "Database is " . $DB_NAME[1] . "\n";
$db = $DB_NAME[1]; $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){ if($ARGV[$n]=~/loc=/i){
@B_LOC = split('=', $ARGV[$n]); @B_LOC = split('=', $ARGV[$n]);
print "Backup Directory: " . $B_LOC[1] . "\n"; print "Backup Directory: " . $B_LOC[1] . "\n";
@ -108,8 +114,14 @@ else {
} }
if($t_tables ne ""){ if($t_tables ne ""){
$tables_f_l = substr($t_tables_l, 0, 20) . '...'; $tables_f_l = substr($t_tables_l, 0, 20) . '-';
$target_file = '' . $tables_f_l . '_' . $date . ''; if($backup_name){
$target_file = $backup_name . '_' . $date . '';
}
else {
$target_file = '' . $tables_f_l . '_' . $date . '';
}
print "Performing table based backup...\n"; print "Performing table based backup...\n";
#::: Backup Database... #::: Backup Database...
print "Backing up Database " . $db . "... \n\n"; print "Backing up Database " . $db . "... \n\n";
@ -121,7 +133,14 @@ if($t_tables ne ""){
system($cmd); system($cmd);
} }
else{ #::: Entire DB Backup else{ #::: Entire DB Backup
$target_file = '' . $db . '_' . $date . '';
if($backup_name){
$target_file = $backup_name . '_' . $db . '_' . $date . '';
}
else {
$target_file = '' . $db . '_' . $date . '';
}
#::: Backup Database... #::: Backup Database...
print "Backing up Database " . $db . "... \n\n"; print "Backing up Database " . $db . "... \n\n";
if($no_lock == 1){ if($no_lock == 1){
@ -195,6 +214,9 @@ if($Compress == 1){
$final_file = $target_file . ".tar.gz"; $final_file = $target_file . ".tar.gz";
} }
} }
else {
$final_file = $target_file . ".sql";
}
#::: Get Final File Location for display #::: Get Final File Location for display
if($B_LOC[1] ne ""){ $final_loc = $B_LOC[1] . '' . $file_app . ""; } if($B_LOC[1] ne ""){ $final_loc = $B_LOC[1] . '' . $file_app . ""; }

File diff suppressed because it is too large Load Diff

View File

@ -298,6 +298,7 @@ sub show_menu_prompt {
18 => \&fetch_latest_windows_binaries_bots, 18 => \&fetch_latest_windows_binaries_bots,
19 => \&do_bots_db_schema_drop, 19 => \&do_bots_db_schema_drop,
20 => \&do_update_self, 20 => \&do_update_self,
21 => \&database_dump_player_tables,
0 => \&script_exit, 0 => \&script_exit,
); );
@ -378,6 +379,7 @@ return <<EO_MENU;
18) [Windows Server Build Bots] :: Download Latest and Stable Server Build with Bots 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 19) [EQEmu DB Drop Bots Schema] :: Remove Bots schema and return database to normal state
20) [Update the updater] Force update this script (Redownload) 20) [Update the updater] Force update this script (Redownload)
21) [DB :: Backup Player Tables] :: Backs up player tables
0) Exit 0) Exit
Enter numbered option and press enter... Enter numbered option and press enter...
@ -404,6 +406,30 @@ sub database_dump {
print "Performing database backup....\n"; print "Performing database backup....\n";
print `perl db_dumper.pl database="$db" loc="backups"`; 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 { sub database_dump_compress {
check_for_database_dump_script(); check_for_database_dump_script();
print "Performing database backup....\n"; print "Performing database backup....\n";

View 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

View File

@ -142,7 +142,7 @@ void Client::SendLogServer()
if(RuleB(World, IsGMPetitionWindowEnabled)) if(RuleB(World, IsGMPetitionWindowEnabled))
l->enable_petition_wnd = 1; 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; l->enable_FV = 1;
QueuePacket(outapp); QueuePacket(outapp);
@ -1483,7 +1483,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++) for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++)
pp.spell_book[i] = 0xFFFFFFFF; 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; pp.mem_spells[i] = 0xFFFFFFFF;
for(i = 0; i < BUFF_COUNT; i++) for(i = 0; i < BUFF_COUNT; i++)

View File

@ -88,7 +88,7 @@ void ZSList::Process() {
Process(); Process();
CatchSignal(2); 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)); 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); LinkedListIterator<ZoneServer*> iterator(list);

View File

@ -867,7 +867,7 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) {
aai->max_level = ability->GetMaxLevel(this); aai->max_level = ability->GetMaxLevel(this);
aai->prev_id = rank->prev_id; 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; aai->next_id = -1;
} else { } else {
aai->next_id = rank->next_id; aai->next_id = rank->next_id;
@ -1183,12 +1183,12 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
CommonBreakInvisible(); CommonBreakInvisible();
// Bards can cast instant cast AAs while they are casting another song // 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(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; return;
} }
ExpendAlternateAdvancementCharge(ability->id); ExpendAlternateAdvancementCharge(ability->id);
} else { } 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; return;
} }
} }
@ -1240,6 +1240,10 @@ void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
CastToClient()->GetEPP().expended_aa += r->cost; CastToClient()->GetEPP().expended_aa += r->cost;
} }
} }
if (IsClient()) {
auto c = CastToClient();
c->RemoveExpendedAA(ability->first_rank_id);
}
aa_ranks.erase(iter.first); aa_ranks.erase(iter.first);
} }

View File

@ -156,10 +156,21 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) {
return; return;
} }
if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GREEN ) { if (RuleB(Aggro, UseLevelAggro))
towho->Message(0, "...%s is red to me (basically)", mob->GetName(), {
dist2, iAggroRange2); if (GetLevel() < 18 && mob->GetLevelCon(GetLevel()) == CON_GREEN && GetBodyType() != 3)
return; {
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) { if(verbose) {
@ -321,11 +332,12 @@ bool Mob::CheckWillAggro(Mob *mob) {
int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap
if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE) if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE)
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() //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->IsClient() && mob->CastToClient()->IsSitting() )
||( mob->GetLevelCon(GetLevel()) != CON_GREEN ) ||( 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 //FatherNiwtit: make sure we can see them. last since it is very expensive
if(CheckLosFN(mob)) { if(CheckLosFN(mob)) {
@ -351,6 +364,39 @@ bool Mob::CheckWillAggro(Mob *mob) {
return( mod_will_aggro(mob, this) ); 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, "Is In zone?:%d\n", mob->InZone());
Log.Out(Logs::Detail, Logs::Aggro, "Dist^2: %f\n", dist2); 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... //if they are in range, make sure we are not green...
//then jump in if they are our friend //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; bool useprimfaction = false;
if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction())

View File

@ -23,6 +23,7 @@
#include "../common/skills.h" #include "../common/skills.h"
#include "../common/spdat.h" #include "../common/spdat.h"
#include "../common/string_util.h" #include "../common/string_util.h"
#include "../common/data_verification.h"
#include "queryserv.h" #include "queryserv.h"
#include "quest_parser_collection.h" #include "quest_parser_collection.h"
#include "string_ids.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); 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 // 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; 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) // Need to check if we have something in MainHand to actually attack with (or fists)
if (hand != EQEmu::legacy::SlotRange && CanThisClassRiposte() && InFront && !ImmuneRipo) { 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()) if (IsClient())
CastToClient()->CheckIncreaseSkill(EQEmu::skills::SkillRiposte, other, -10); CastToClient()->CheckIncreaseSkill(EQEmu::skills::SkillRiposte, other, -10);
// check auto discs ... I guess aa/items too :P // 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])) { IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f; float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance)) 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); spells[aabonuses.SkillAttackProc[2]].ResistDiff);
} }
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse, true, -1, false, special); 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 //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
if(attacked_timer.Check()) 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); parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
} }
attacked_timer.Start(CombatEventTimer_expire); attacked_timer.Start(CombatEventTimer_expire);
@ -1821,7 +1819,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::Skill
if(IsLDoNTrapped()) if(IsLDoNTrapped())
{ {
Message_StringID(13, LDON_ACCIDENT_SETOFF2); 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); SetLDoNTrapSpellID(0);
SetLDoNTrapped(false); SetLDoNTrapped(false);
SetLDoNTrapDetected(false); SetLDoNTrapDetected(false);
@ -3169,68 +3167,63 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
BuffFadeByEffect(SE_Mez); BuffFadeByEffect(SE_Mez);
} }
//check stun chances if bashing // broken up for readability
if (damage > 0 && ((skill_used == EQEmu::skills::SkillBash || skill_used == EQEmu::skills::SkillKick) && attacker)) { // This is based on what the client is doing
// NPCs can stun with their bash/kick as soon as they receive it. // We had a bunch of stuff like BaseImmunityLevel checks, which I think is suppose to just be for spells
// Clients can stun mobs under level 56 with their kick when they get level 55 or greater. // This is missing some merc checks, but those mostly just skipped the spell bonuses I think ...
// Clients have a chance to stun if the mob is 56+ bool can_stun = false;
int stunbash_chance = 0; // bonus
// Calculate the chance to stun if (attacker) {
int stun_chance = 0; if (skill_used == EQEmu::skills::SkillBash) {
if (!GetSpecialAbility(UNSTUNABLE)) { can_stun = true;
if (attacker->IsNPC()) { if (attacker->IsClient())
stun_chance = RuleI(Combat, NPCBashKickStunChance); stunbash_chance = attacker->spellbonuses.StunBashChance +
} else if (attacker->IsClient()) { attacker->itembonuses.StunBashChance +
// Less than base immunity attacker->aabonuses.StunBashChance;
// Client vs. Client always uses the chance } else if (skill_used == EQEmu::skills::SkillKick &&
if (!IsClient() && GetLevel() <= RuleI(Spells, BaseImmunityLevel)) { (attacker->GetLevel() > 55 || attacker->IsNPC()) && GetClass() == WARRIOR) {
if (skill_used == EQEmu::skills::SkillBash) // Bash always will can_stun = true;
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;
}
}
} }
if (stun_chance && zone->random.Roll(stun_chance)) { if ((GetBaseRace() == OGRE || GetBaseRace() == OGGOK_CITIZEN) &&
// Passed stun, try to resist now !attacker->BehindMob(this, attacker->GetX(), attacker->GetY()))
int stun_resist = itembonuses.StunResist + spellbonuses.StunResist; can_stun = false;
int frontal_stun_resist = itembonuses.FrontalStunResist + spellbonuses.FrontalStunResist; if (GetSpecialAbility(UNSTUNABLE))
can_stun = false;
Log.Out(Logs::Detail, Logs::Combat, "Stun passed, checking resists. Was %d chance.", stun_chance); }
if (IsClient()) { if (can_stun) {
stun_resist += aabonuses.StunResist; int bashsave_roll = zone->random.Int(0, 100);
frontal_stun_resist += aabonuses.FrontalStunResist; if (bashsave_roll > 98 || bashsave_roll > (55 - stunbash_chance)) {
} // did stun -- roll other resists
// SE_FrontalStunResist description says any angle now a days
// frontal stun check for ogres/bonuses int stun_resist2 = spellbonuses.FrontalStunResist + itembonuses.FrontalStunResist +
if (((GetBaseRace() == OGRE && IsClient()) || aabonuses.FrontalStunResist;
(frontal_stun_resist && zone->random.Roll(frontal_stun_resist))) && if (zone->random.Int(1, 100) > stun_resist2) {
!attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) { // stun resist 2 failed
Log.Out(Logs::Detail, Logs::Combat, "Frontal stun resisted. %d chance.", frontal_stun_resist); // time to check SE_StunResist and mod2 stun resist
int stun_resist =
} else { spellbonuses.StunResist + itembonuses.StunResist + aabonuses.StunResist;
// Normal stun resist check. if (zone->random.Int(0, 100) >= stun_resist) {
if (stun_resist && zone->random.Roll(stun_resist)) { // did stun
// nothing else to check!
Stun(2000); // straight 2 seconds every time
} else {
// stun resist passed!
if (IsClient()) if (IsClient())
Message_StringID(MT_Stun, SHAKE_OFF_STUN); 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 { } 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->damage = damage;
a->spellid = spell_id; a->spellid = spell_id;
a->special = special; 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() && if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() &&
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) { (IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
a->force = EQEmu::skills::GetSkillMeleePushForce(skill_used); 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) void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts)
{ {
if(damage < 1) if(damage < 1 || !defender)
return; return;
// decided to branch this into it's own function since it's going to be duplicating a lot of the // 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; bool IsBerskerSPA = false;
//1: Try Slay Undead //1: Try Slay Undead
if (defender && (defender->GetBodyType() == BT_Undead || if (defender->GetBodyType() == BT_Undead ||
defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire)) { defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire) {
int32 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0]; int32 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0];
if (SlayRateBonus) { if (SlayRateBonus) {
float slayChance = static_cast<float>(SlayRateBonus) / 10000.0f; float slayChance = static_cast<float>(SlayRateBonus) / 10000.0f;

View File

@ -1709,8 +1709,8 @@ bool Bot::LoadPet()
NPC *pet_inst = GetPet()->CastToNPC(); NPC *pet_inst = GetPet()->CastToNPC();
SpellBuff_Struct pet_buffs[BUFF_COUNT]; SpellBuff_Struct pet_buffs[PET_BUFF_COUNT];
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * BUFF_COUNT)); memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * PET_BUFF_COUNT));
if (!botdb.LoadPetBuffs(GetBotID(), pet_buffs)) if (!botdb.LoadPetBuffs(GetBotID(), pet_buffs))
bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetBuffs(), GetCleanName()); bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetBuffs(), GetCleanName());
@ -1741,11 +1741,11 @@ bool Bot::SavePet()
return false; return false;
char* pet_name = new char[64]; 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]; uint32 pet_items[EQEmu::legacy::EQUIPMENT_SIZE];
memset(pet_name, 0, 64); 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)); memset(pet_items, 0, (sizeof(uint32) * EQEmu::legacy::EQUIPMENT_SIZE));
pet_inst->GetPetState(pet_buffs, pet_items, pet_name); 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); CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
if ((skillinuse == EQEmu::skills::SkillDragonPunch) && GetAA(aaDragonPunch) && zone->random.Int(0, 99) < 25){ 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); other->Stun(100);
} }
@ -5900,7 +5900,7 @@ void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) {
Mob::DoBuffTic(buff, slot, 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) { uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust, uint32 aa_id) {
bool Result = false; bool Result = false;
if(zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { 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); Message_StringID(13, MELEE_SILENCE);
if(casting_spell_id) if(casting_spell_id)
AI_Event_SpellCastFinished(false, casting_spell_slot); AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
return false; 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()){ if(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()){
Message_StringID(13, SPELL_WOULDNT_HOLD); Message_StringID(13, SPELL_WOULDNT_HOLD);
if(casting_spell_id) if(casting_spell_id)
AI_Event_SpellCastFinished(false, casting_spell_slot); AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
return false; return false;
} }
@ -5940,7 +5940,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
return false; 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; int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
InterruptSpell(fizzle_msg, 0x121, spell_id); 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); Log.Out(Logs::Detail, Logs::Spells, "Casting a new spell/song while singing a song. Killing old song %d.", bardsong);
bardsong = 0; bardsong = 0;
bardsong_target_id = 0; bardsong_target_id = 0;
bardsong_slot = 0; bardsong_slot = EQEmu::CastingSlot::Gem1;
bardsong_timer.Disable(); bardsong_timer.Disable();
} }
@ -6084,7 +6084,7 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) {
return Result; 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; bool Result = false;
SpellTargetType targetType = spells[spell_id].targettype; SpellTargetType targetType = spells[spell_id].targettype;
if(targetType == ST_GroupClientAndPet) { if(targetType == ST_GroupClientAndPet) {
@ -6097,7 +6097,7 @@ bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
return Result; 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; bool Result = false;
if(GetClass() == BARD) if(GetClass() == BARD)
cast_time = 0; cast_time = 0;
@ -6201,7 +6201,7 @@ void Bot::GenerateSpecialAttacks() {
SetSpecialAbility(SPECATK_TRIPLE, 1); 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(GetClass() == BARD) {
if(!ApplyNextBardPulse(bardsong, this, bardsong_slot)) if(!ApplyNextBardPulse(bardsong, this, bardsong_slot))
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
@ -6211,7 +6211,7 @@ bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot
return true; 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(spellTarget) {
if(IsGrouped() && (spellTarget->IsBot() || spellTarget->IsClient()) && RuleB(Bots, GroupBuffing)) { if(IsGrouped() && (spellTarget->IsBot() || spellTarget->IsClient()) && RuleB(Bots, GroupBuffing)) {
bool noGroupSpell = false; 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 spelltypeequal = ((spelltype == 2) || (spelltype == 16) || (spelltype == 32));
bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].targettype == ST_Self)); bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].targettype == ST_Self));
bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == SHAMAN)); bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == SHAMAN));
bool slotequal = (slot == USE_ITEM_SPELL_SLOT); bool slotequal = (slot == EQEmu::CastingSlot::Item);
if(spellequal || slotequal) { if(spellequal || slotequal) {
if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) { if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) {
if(((spells[thespell].effectid[0] == 0) && (spells[thespell].base[0] < 0)) && 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; 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; bool isMainGroupMGB = false;
if(isMainGroupMGB && (GetClass() != BARD)) { if(isMainGroupMGB && (GetClass() != BARD)) {
BotGroupSay(this, "MGB %s", spells[spell_id].name); BotGroupSay(this, "MGB %s", spells[spell_id].name);
@ -8159,7 +8159,25 @@ bool Bot::GetNeedsCured(Mob *tar) {
int buffsWithCounters = 0; int buffsWithCounters = 0;
needCured = true; needCured = true;
for (unsigned int j = 0; j < buff_count; j++) { 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) { if(CalculateCounters(tar->GetBuffs()[j].spellid) > 0) {
buffsWithCounters++; buffsWithCounters++;
if(buffsWithCounters == 1 && (tar->GetBuffs()[j].ticsremaining < 2 || (int32)((tar->GetBuffs()[j].ticsremaining * 6) / tar->GetBuffs()[j].counters) < 2)) { 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()) if(IsCasting())
InterruptSpell(); InterruptSpell();
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
return true; return true;
} }

View File

@ -274,9 +274,9 @@ public:
virtual void SetAttackTimer(); virtual void SetAttackTimer();
uint32 GetClassHPFactor(); uint32 GetClassHPFactor();
virtual int32 CalcMaxHP(); virtual int32 CalcMaxHP();
bool DoFinishedSpellAETarget(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, uint16 slot, bool &stopLogic); bool DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 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 SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color);
void Camp(bool databaseSave = true); void Camp(bool databaseSave = true);
virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false); virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false);
@ -374,12 +374,12 @@ public:
virtual float GetAOERange(uint16 spell_id); virtual float GetAOERange(uint16 spell_id);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100); virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr); 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); uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr, uint32 aa_id = 0);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); 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 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, 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 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 // 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); void BotTradeSwapItem(Client* client, int16 lootSlot, const ItemInst* inst, const ItemInst* inst_swap, uint32 equipableSlots, std::string* errorMessage, bool swap = true);

View File

@ -7613,7 +7613,7 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id,
if (annouce_cast) if (annouce_cast)
Bot::BotGroupSay(casting_bot, "Attempting to cast '%s' on %s", spells[spell_id].name, target_mob->GetCleanName()); 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) bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command)

View File

@ -1472,7 +1472,7 @@ bool BotDatabase::LoadPetBuffs(const uint32 bot_id, SpellBuff_Struct* pet_buffs)
return true; return true;
int buff_index = 0; 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].spellid = atoi(row[0]);
pet_buffs[buff_index].level = atoi(row[1]); pet_buffs[buff_index].level = atoi(row[1]);
pet_buffs[buff_index].duration = atoi(row[2]); 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) if (!saved_pet_index)
return true; 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) if (!pet_buffs[buff_index].spellid || pet_buffs[buff_index].spellid == SPELL_UNKNOWN)
continue; continue;

View File

@ -44,6 +44,7 @@ extern volatile bool RunLoops;
#include "zonedb.h" #include "zonedb.h"
#include "petitions.h" #include "petitions.h"
#include "command.h" #include "command.h"
#include "water_map.h"
#ifdef BOTS #ifdef BOTS
#include "bot_command.h" #include "bot_command.h"
#endif #endif
@ -155,7 +156,8 @@ Client::Client(EQStreamInterface* ieqs)
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.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++) for(int cf=0; cf < _FilterCount; cf++)
ClientFilters[cf] = FilterShow; ClientFilters[cf] = FilterShow;
@ -565,6 +567,11 @@ bool Client::SaveAA() {
return true; 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) { bool Client::Save(uint8 iCommitNow) {
if(!ClientDataLoaded()) if(!ClientDataLoaded())
return false; return false;
@ -1787,7 +1794,7 @@ const int32& Client::SetMana(int32 amount) {
} }
void Client::SendManaUpdatePacket() { void Client::SendManaUpdatePacket() {
if (!Connected() || IsCasting()) if (!Connected())
return; return;
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
@ -1801,7 +1808,8 @@ void Client::SendManaUpdatePacket() {
ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer; ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer;
manachange->new_mana = cur_mana; manachange->new_mana = cur_mana;
manachange->stamina = cur_end; 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; outapp->priority = 6;
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
@ -2476,13 +2484,15 @@ uint16 Client::GetMaxSkillAfterSpecializationRules(EQEmu::skills::SkillType skil
return Result; return Result;
} }
void Client::SetPVP(bool toggle) { void Client::SetPVP(bool toggle, bool message) {
m_pp.pvp = toggle ? 1 : 0; m_pp.pvp = toggle ? 1 : 0;
if(GetPVP()) if (message) {
this->Message_StringID(MT_Shout,PVP_ON); if(GetPVP())
else this->Message_StringID(MT_Shout,PVP_ON);
Message(13, "You no longer follow the ways of discord."); else
Message(13, "You no longer follow the ways of discord.");
}
SendAppearancePacket(AT_PVP, GetPVP()); SendAppearancePacket(AT_PVP, GetPVP());
Save(); Save();
@ -3674,58 +3684,58 @@ void Client::SacrificeConfirm(Client *caster)
//Essentially a special case death function //Essentially a special case death function
void Client::Sacrifice(Client *caster) void Client::Sacrifice(Client *caster)
{ {
if(GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)){ if (GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)) {
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
if(exploss < GetEXP()){ if (exploss < GetEXP()) {
SetEXP(GetEXP()-exploss, GetAAXP()); SetEXP(GetEXP() - exploss, GetAAXP());
SendLogoutPackets(); SendLogoutPackets();
//make our become corpse packet, and queue to ourself before OP_Death. // make our become corpse packet, and queue to ourself before OP_Death.
EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct)); EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct));
BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer; BecomeCorpse_Struct *bc = (BecomeCorpse_Struct *)app2.pBuffer;
bc->spawn_id = GetID(); bc->spawn_id = GetID();
bc->x = GetX(); bc->x = GetX();
bc->y = GetY(); bc->y = GetY();
bc->z = GetZ(); bc->z = GetZ();
QueuePacket(&app2); QueuePacket(&app2);
// make death packet // make death packet
EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); EQApplicationPacket app(OP_Death, sizeof(Death_Struct));
Death_Struct* d = (Death_Struct*)app.pBuffer; Death_Struct *d = (Death_Struct *)app.pBuffer;
d->spawn_id = GetID(); d->spawn_id = GetID();
d->killer_id = caster ? caster->GetID() : 0; d->killer_id = caster ? caster->GetID() : 0;
d->bindzoneid = GetPP().binds[0].zoneId; d->bindzoneid = GetPP().binds[0].zoneId;
d->spell_id = SPELL_UNKNOWN; d->spell_id = SPELL_UNKNOWN;
d->attack_skill = 0xe7; d->attack_skill = 0xe7;
d->damage = 0; d->damage = 0;
app.priority = 6; app.priority = 6;
entity_list.QueueClients(this, &app); entity_list.QueueClients(this, &app);
BuffFadeAll(); BuffFadeAll();
UnmemSpellAll(); UnmemSpellAll();
Group *g = GetGroup(); Group *g = GetGroup();
if(g){ if (g) {
g->MemberZoned(this); g->MemberZoned(this);
} }
Raid *r = entity_list.GetRaidByClient(this); Raid *r = entity_list.GetRaidByClient(this);
if(r){ if (r) {
r->MemberZoned(this); r->MemberZoned(this);
} }
ClearAllProximities(); ClearAllProximities();
if(RuleB(Character, LeaveCorpses)){ if (RuleB(Character, LeaveCorpses)) {
auto new_corpse = new Corpse(this, 0); auto new_corpse = new Corpse(this, 0);
entity_list.AddCorpse(new_corpse, GetID()); entity_list.AddCorpse(new_corpse, GetID());
SetID(0); SetID(0);
entity_list.QueueClients(this, &app2, true); entity_list.QueueClients(this, &app2, true);
} }
Save(); Save();
GoToDeath(); GoToDeath();
caster->SummonItem(RuleI(Spells, SacrificeItemID)); if (caster) // I guess it's possible?
} caster->SummonItem(RuleI(Spells, SacrificeItemID));
} }
else{ } else {
caster->Message_StringID(13, SAC_TOO_LOW); //This being is not a worthy sacrifice. caster->Message_StringID(13, SAC_TOO_LOW); // This being is not a worthy sacrifice.
} }
} }
void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) { void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) {
@ -4604,7 +4614,7 @@ void Client::HandleLDoNOpen(NPC *target)
if(target->GetLDoNTrapSpellID() != 0) if(target->GetLDoNTrapSpellID() != 0)
{ {
Message_StringID(13, LDON_ACCIDENT_SETOFF2); 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->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false); target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(false); target->SetLDoNTrapDetected(false);
@ -4726,7 +4736,7 @@ void Client::HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type)
break; break;
case -1: case -1:
Message_StringID(13, LDON_ACCIDENT_SETOFF2); 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->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false); target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(false); target->SetLDoNTrapDetected(false);
@ -4745,7 +4755,7 @@ void Client::HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type)
if(target->IsLDoNTrapped()) if(target->IsLDoNTrapped())
{ {
Message_StringID(13, LDON_ACCIDENT_SETOFF2); 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->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false); target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(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) { 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)); 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; QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer;
qr->mob_id = target->GetID(); // Entity ID for the from mob name 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() { int Client::GetAccountAge() {
return (time(nullptr) - GetAccountCreation()); 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);
}

View File

@ -27,6 +27,7 @@ class Object;
class Raid; class Raid;
class Seperator; class Seperator;
class ServerPacket; class ServerPacket;
enum WaterRegionType : int;
namespace EQEmu namespace EQEmu
{ {
@ -105,6 +106,7 @@ enum { //Type arguments to the Message* routines.
#define SPELLBAR_UNLOCK 0x2bc #define SPELLBAR_UNLOCK 0x2bc
enum { //scribing argument to MemorizeSpell enum { //scribing argument to MemorizeSpell
memSpellUnknown = -1, // this modifies some state data
memSpellScribing = 0, memSpellScribing = 0,
memSpellMemorize = 1, memSpellMemorize = 1,
memSpellForget = 2, memSpellForget = 2,
@ -326,6 +328,7 @@ public:
/* New PP Save Functions */ /* New PP Save Functions */
bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); } bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); }
bool SaveAA(); bool SaveAA();
void RemoveExpendedAA(int aa_id);
inline bool ClientDataLoaded() const { return client_data_loaded; } inline bool ClientDataLoaded() const { return client_data_loaded; }
inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } inline bool Connected() const { return (client_state == CLIENT_CONNECTED); }
@ -359,9 +362,9 @@ public:
int32 LevelRegen(); int32 LevelRegen();
void HPTick(); void HPTick();
void SetGM(bool toggle); 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 bool GetGM() const { return m_pp.gm != 0; }
inline void SetBaseClass(uint32 i) { m_pp.class_=i; } inline void SetBaseClass(uint32 i) { m_pp.class_=i; }
@ -510,6 +513,8 @@ public:
virtual int GetMaxSongSlots() const { return 12; } virtual int GetMaxSongSlots() const { return 12; }
virtual int GetMaxDiscSlots() const { return 1; } virtual int GetMaxDiscSlots() const { return 1; }
virtual int GetMaxTotalSlots() const { return 38; } 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 InitializeBuffSlots();
virtual void UninitializeBuffSlots(); virtual void UninitializeBuffSlots();
@ -889,6 +894,9 @@ public:
void SendDisciplineTimer(uint32 timer_id, uint32 duration); void SendDisciplineTimer(uint32 timer_id, uint32 duration);
bool UseDiscipline(uint32 spell_id, uint32 target); bool UseDiscipline(uint32 spell_id, uint32 target);
void SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration);
bool IsLinkedSpellReuseTimerReady(uint32 timer_id);
bool CheckTitle(int titleset); bool CheckTitle(int titleset);
void EnableTitle(int titleset); void EnableTitle(int titleset);
void RemoveTitle(int titleset); void RemoveTitle(int titleset);
@ -1230,6 +1238,8 @@ public:
void SendHPUpdateMarquee(); void SendHPUpdateMarquee();
void CheckRegionTypeChanges();
protected: protected:
friend class Mob; friend class Mob;
void CalcItemBonuses(StatBonuses* newbon); void CalcItemBonuses(StatBonuses* newbon);
@ -1414,6 +1424,7 @@ private:
uint8 zonesummon_ignorerestrictions; uint8 zonesummon_ignorerestrictions;
ZoneMode zone_mode; ZoneMode zone_mode;
WaterRegionType last_region_type;
Timer position_timer; Timer position_timer;
uint8 position_timer_counter; uint8 position_timer_counter;

View File

@ -1036,6 +1036,13 @@ int32 Client::CalcAC()
if (avoidance < 0) { if (avoidance < 0) {
avoidance = 0; avoidance = 0;
} }
if (RuleB(Character, EnableAvoidanceCap)) {
if (avoidance > RuleI(Character, AvoidanceCap)) {
avoidance = RuleI(Character, AvoidanceCap);
}
}
int mitigation = 0; int mitigation = 0;
if (m_pp.class_ == WIZARD || m_pp.class_ == MAGICIAN || m_pp.class_ == NECROMANCER || m_pp.class_ == ENCHANTER) { 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 //something is wrong with this, naked casters have the wrong natural AC
@ -1113,6 +1120,13 @@ int32 Client::GetACAvoid()
if (avoidance < 0) { if (avoidance < 0) {
avoidance = 0; avoidance = 0;
} }
if (RuleB(Character, EnableAvoidanceCap)) {
if ((avoidance * 1000 / 847) > RuleI(Character, AvoidanceCap)) {
return RuleI(Character, AvoidanceCap);
}
}
return (avoidance * 1000 / 847); return (avoidance * 1000 / 847);
} }

View File

@ -502,6 +502,27 @@ void Client::CompleteConnect()
SetDuelTarget(0); SetDuelTarget(0);
SetDueling(false); 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); EnteringMessages(this);
LoadZoneFlags(); LoadZoneFlags();
@ -1628,27 +1649,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if (m_pp.RestTimer) if (m_pp.RestTimer)
rest_timer.Start(m_pp.RestTimer * 1000); 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 */ /* Server Zone Entry Packet */
outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct));
ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; 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) void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
{ {
using EQEmu::CastingSlot;
if (app->size != sizeof(CastSpell_Struct)) { if (app->size != sizeof(CastSpell_Struct)) {
std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl; std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl;
return; return;
@ -3978,13 +3979,13 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; 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); 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 */ /* 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; uint16 spell_to_cast = 0;
if (castspell->slot < MAX_PP_MEMSPELL) { if (castspell->slot < MAX_PP_MEMSPELL) {
spell_to_cast = m_pp.mem_spells[castspell->slot]; spell_to_cast = m_pp.mem_spells[castspell->slot];
@ -3998,20 +3999,12 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
return; return;
} }
CastSpell(spell_to_cast, castspell->target_id, castspell->slot); CastSpell(spell_to_cast, castspell->target_id, slot);
} }
/* Spell Slot or Potion Belt 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 (m_inv.SupportsClickCasting(castspell->inventoryslot) || slot == CastingSlot::PotionBelt) // sanity check
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
{ {
// packet field types will be reviewed as packet transistions occur // 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 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); int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot);
if (i == 0) { 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 { else {
InterruptSpell(castspell->spell_id); 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); int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot);
if (i == 0) { 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 { else {
InterruptSpell(castspell->spell_id); InterruptSpell(castspell->spell_id);
@ -4081,8 +4074,8 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
InterruptSpell(castspell->spell_id); InterruptSpell(castspell->spell_id);
} }
} }
/* Discipline */ /* Discipline -- older clients use the same slot as items, but we translate to it's own */
else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { else if (slot == CastingSlot::Discipline) {
if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { 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); 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); InterruptSpell(castspell->spell_id);
@ -4090,7 +4083,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
} }
} }
/* ABILITY cast (LoH and Harm Touch) */ /* ABILITY cast (LoH and Harm Touch) */
else if (castspell->slot == ABILITY_SPELL_SLOT) { else if (slot == CastingSlot::Ability) {
uint16 spell_to_cast = 0; uint16 spell_to_cast = 0;
if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { 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 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; return;
} }
@ -4583,8 +4576,11 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
safe_delete(outapp); safe_delete(outapp);
} }
if(zone->watermap && zone->watermap->InLiquid(glm::vec3(m_Position))) if (zone->watermap) {
CheckIncreaseSkill(EQEmu::skills::SkillSwimming, nullptr, -17); if (zone->watermap->InLiquid(glm::vec3(m_Position)))
CheckIncreaseSkill(EQEmu::skills::SkillSwimming, nullptr, -17);
CheckRegionTypeChanges();
}
return; return;
} }
@ -8410,6 +8406,7 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app)
void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
{ {
using EQEmu::CastingSlot;
if (app->size != sizeof(ItemVerifyRequest_Struct)) 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); 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 (i == 0) {
if (!IsCastWhileInvis(item->Click.Effect)) if (!IsCastWhileInvis(item->Click.Effect))
CommonBreakInvisible(); // client can't do this for us :( 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 else
@ -8583,7 +8580,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
if (i == 0) { if (i == 0) {
if (!IsCastWhileInvis(augitem->Click.Effect)) if (!IsCastWhileInvis(augitem->Click.Effect))
CommonBreakInvisible(); // client can't do this for us :( 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 else

View File

@ -624,7 +624,7 @@ bool Client::Process() {
{ {
//client logged out or errored out //client logged out or errored out
//ResetTrade(); //ResetTrade();
if (client_state != CLIENT_KICKED) { if (client_state != CLIENT_KICKED && !zoning && !instalog) {
Save(); Save();
} }

View File

@ -2199,14 +2199,14 @@ void command_castspell(Client *c, const Seperator *sep)
else else
if (c->GetTarget() == 0) if (c->GetTarget() == 0)
if(c->Admin() >= commandInstacast) 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 else
c->CastSpell(spellid, 0, USE_ITEM_SPELL_SLOT, 0); c->CastSpell(spellid, 0, EQEmu::CastingSlot::Item, 0);
else else
if(c->Admin() >= commandInstacast) 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 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) void command_iteminfo(Client *c, const Seperator *sep)
{ {
auto inst = c->GetInv()[EQEmu::legacy::SlotCursor]; 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(); auto item = inst->GetItem();
if (!item) { if (!item) {
Log.Out(Logs::General, Logs::Inventory, "(%s) Command #iteminfo processed an item with no data pointer"); 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"); c->Message(13, "Error: This item has no data reference");
return;
} }
EQEmu::SayLinkEngine linker; EQEmu::SayLinkEngine linker;

View File

@ -17,13 +17,6 @@
#define _NPCPET(x) (x && x->IsNPC() && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsNPC()) #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 _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: //LOS Parameters:
#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from #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 #define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS

View File

@ -1163,9 +1163,9 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
} }
char buf[88]; char buf[88];
char corpse_name[64]; char q_corpse_name[64];
strcpy(corpse_name, corpse_name); strcpy(q_corpse_name, corpse_name);
snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(corpse_name)); snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(q_corpse_name));
buf[87] = '\0'; buf[87] = '\0';
std::vector<EQEmu::Any> args; std::vector<EQEmu::Any> args;
args.push_back(inst); args.push_back(inst);

View File

@ -684,9 +684,9 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
} }
if (reduced_recast > 0) 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{ else{
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
return true; return true;
} }
@ -694,7 +694,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
} }
else else
{ {
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
} }
return(true); return(true);
} }

View File

@ -927,12 +927,18 @@ bool EntityList::MakeDoorSpawnPacket(EQApplicationPacket *app, Client *client)
Entity *EntityList::GetEntityMob(uint16 id) 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) 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) Entity *EntityList::GetEntityMob(const char *name)
@ -952,12 +958,18 @@ Entity *EntityList::GetEntityMob(const char *name)
Entity *EntityList::GetEntityDoor(uint16 id) 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) 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) Entity *EntityList::GetEntityCorpse(const char *name)
@ -977,22 +989,34 @@ Entity *EntityList::GetEntityCorpse(const char *name)
Entity *EntityList::GetEntityTrap(uint16 id) 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) 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) 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) 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) 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, ...) void EntityList::ChannelMessageSend(Mob *to, uint8 chan_num, uint8 language, const char *message, ...)
{ {
if (!to->IsClient())
return;
va_list argptr; va_list argptr;
char buffer[4096]; char buffer[4096];
@ -1195,8 +1221,7 @@ void EntityList::ChannelMessageSend(Mob *to, uint8 chan_num, uint8 language, con
vsnprintf(buffer, 4096, message, argptr); vsnprintf(buffer, 4096, message, argptr);
va_end(argptr); va_end(argptr);
if (client_list.count(to->GetID())) to->CastToClient()->ChannelMessageSend(0, 0, chan_num, language, buffer);
client_list.at(to->GetID())->ChannelMessageSend(0, 0, chan_num, language, buffer);
} }
void EntityList::SendZoneSpawns(Client *client) void EntityList::SendZoneSpawns(Client *client)
@ -1232,7 +1257,9 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
maxspawns = mob_list.size(); maxspawns = mob_list.size();
auto bzsp = new BulkZoneSpawnPacket(client, maxspawns); 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) { for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
spawn = it->second; spawn = it->second;
if (spawn && spawn->GetID() > 0 && spawn->Spawned()) { if (spawn && spawn->GetID() > 0 && spawn->Spawned()) {
@ -1240,7 +1267,29 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
spawn->CastToClient()->IsHoveringForRespawn())) spawn->CastToClient()->IsHoveringForRespawn()))
continue; 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 // Illusion races on PCs don't work as a mass spawn
// But they will work as an add_spawn AFTER CLIENT_CONNECTED. // But they will work as an add_spawn AFTER CLIENT_CONNECTED.
@ -1259,6 +1308,7 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
// Despite being sent in the OP_ZoneSpawns packet, the client // Despite being sent in the OP_ZoneSpawns packet, the client
// does not display worn armor correctly so display it. // does not display worn armor correctly so display it.
spawn->SendArmorAppearance(client); spawn->SendArmorAppearance(client);
#endif
} }
} }
safe_delete(bzsp); safe_delete(bzsp);

View File

@ -148,14 +148,29 @@ public:
bool IsMobSpawnedByNpcTypeID(uint32 get_id); bool IsMobSpawnedByNpcTypeID(uint32 get_id);
Mob *GetTargetForVirus(Mob* spreader, int range); Mob *GetTargetForVirus(Mob* spreader, int range);
inline NPC *GetNPCByID(uint16 id) 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); NPC *GetNPCByNPCTypeID(uint32 npc_id);
inline Merc *GetMercByID(uint16 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 *GetClientByName(const char *name);
Client *GetClientByAccID(uint32 accid); Client *GetClientByAccID(uint32 accid);
inline Client *GetClientByID(uint16 id) 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 *GetClientByCharID(uint32 iCharID);
Client *GetClientByWID(uint32 iWID); Client *GetClientByWID(uint32 iWID);
Client *GetClient(uint32 ip, uint16 port); Client *GetClient(uint32 ip, uint16 port);
@ -172,7 +187,12 @@ public:
Corpse *GetCorpseByOwner(Client* client); Corpse *GetCorpseByOwner(Client* client);
Corpse *GetCorpseByOwnerWithinRange(Client* client, Mob* center, int range); Corpse *GetCorpseByOwnerWithinRange(Client* client, Mob* center, int range);
inline Corpse *GetCorpseByID(uint16 id) 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 *GetCorpseByDBID(uint32 dbid);
Corpse *GetCorpseByName(const char* name); Corpse *GetCorpseByName(const char* name);
@ -181,10 +201,20 @@ public:
Client* FindCorpseDragger(uint16 CorpseID); Client* FindCorpseDragger(uint16 CorpseID);
inline Object *GetObjectByID(uint16 id) 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); Object *GetObjectByDBID(uint32 id);
inline Doors *GetDoorsByID(uint16 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 *GetDoorsByDoorID(uint32 id);
Doors *GetDoorsByDBID(uint32 id); Doors *GetDoorsByDBID(uint32 id);
void RemoveAllCorpsesByCharID(uint32 charid); void RemoveAllCorpsesByCharID(uint32 charid);

View File

@ -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) { bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot) {
Lua_Safe_Call_Bool(); 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) { bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time) {
Lua_Safe_Call_Bool(); 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) { bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost) {
Lua_Safe_Call_Bool(); 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) { 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(); 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, 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) { int timer_duration) {
Lua_Safe_Call_Bool(); 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)); 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(); Lua_Safe_Call_Bool();
int16 res = resist_adjust; 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); 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) { bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot) {
Lua_Safe_Call_Bool(); 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) { bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used) {
Lua_Safe_Call_Bool(); 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) { bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot) {
Lua_Safe_Call_Bool(); 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) { 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(); 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) { 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(); 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) { void Lua_Mob::SpellEffect(Lua_Mob caster, int spell_id, double partial) {

View File

@ -163,7 +163,7 @@ float Map::FindClosestZ(glm::vec3 &start, glm::vec3 *result) const {
to.z = -BEST_Z_INVALID; to.z = -BEST_Z_INVALID;
hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance);
if (hit) { 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; return result->z;
} }

View File

@ -1963,7 +1963,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon
SendPosition(); SendPosition();
SetMoving(false); 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()) if(IsCasting() && IsSitting())
Stand(); Stand();
@ -4015,7 +4015,7 @@ bool Merc::UseDiscipline(int32 spell_id, int32 target) {
if(IsCasting()) if(IsCasting())
InterruptSpell(); InterruptSpell();
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
return(true); return(true);
} }

View File

@ -2357,7 +2357,6 @@ void Mob::SetZone(uint32 zone_id, uint32 instance_id)
{ {
CastToClient()->GetPP().zone_id = zone_id; CastToClient()->GetPP().zone_id = zone_id;
CastToClient()->GetPP().zoneInstance = instance_id; CastToClient()->GetPP().zoneInstance = instance_id;
CastToClient()->Save();
} }
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)) if(twinproc_chance && zone->random.Roll(twinproc_chance))
twinproc = true; twinproc = true;
if (IsBeneficialSpell(spell_id)) { if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id))) { // NPC innate procs don't take this path ever
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff, true, level_override); SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override);
if(twinproc) if(twinproc)
SpellOnTarget(spell_id, this, false, false, 0, true, level_override); SpellOnTarget(spell_id, this, false, false, 0, true, level_override);
} }
else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients 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) if(twinproc)
SpellOnTarget(spell_id, on, false, false, 0, true, level_override); 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) 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; return;
uint32 trigger_spell_id = 0; uint32 trigger_spell_id = 0;
@ -3532,15 +3530,17 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
if (rank) if (rank)
trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, *rank, spell_id); trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, *rank, spell_id);
if(IsValidSpell(trigger_spell_id) && GetTarget()) if (IsValidSpell(trigger_spell_id) && GetTarget())
SpellFinished(trigger_spell_id, GetTarget(), 10, 0, -1, spells[trigger_spell_id].ResistDiff); 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); trigger_spell_id = CalcFocusEffect(focusTriggerOnCast, focus_spell, spell_id);
if(IsValidSpell(trigger_spell_id) && GetTarget()){ if (IsValidSpell(trigger_spell_id) && GetTarget()) {
SpellFinished(trigger_spell_id, GetTarget(),10, 0, -1, spells[trigger_spell_id].ResistDiff); SpellFinished(trigger_spell_id, GetTarget(), EQEmu::CastingSlot::Item, 0, -1,
spells[trigger_spell_id].ResistDiff);
CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell); 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 we trigger an effect then its over.
if (IsValidSpell(spells[spell_id].base2[i])){ 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; 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(zone->random.Int(0, 100) <= spells[spell_id].base[effect])
{ {
if (IsValidSpell(spells[spell_id].base2[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. 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){ 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)) if(!TryFadeEffect(e))
BuffFadeBySlot(e); BuffFadeBySlot(e);
@ -3694,7 +3694,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id)
if(zone->random.Roll(focus)) if(zone->random.Roll(focus))
{ {
Message(MT_Spells,"You twincast %s!",spells[spell_id].name); 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)) 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(IsValidSpell(spell_id))
{ {
if (IsBeneficialSpell(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)) { 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; return true;
} }
@ -3868,7 +3868,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id)
SpellFinished(focus_trigger, target); SpellFinished(focus_trigger, target);
else 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 // 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 // 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); SpellFinished(focus_trigger, this);
else 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); 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 (IsValidSpell(spells[spell_id].base2[i]) && spells[spell_id].max[i] <= level)
{ {
if(zone->random.Roll(spells[spell_id].base[i])) 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(aabonuses.SpellOnKill[i] && IsValidSpell(aabonuses.SpellOnKill[i]) && (level >= aabonuses.SpellOnKill[i + 2])) {
if(zone->random.Roll(static_cast<int>(aabonuses.SpellOnKill[i + 1]))) 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(itembonuses.SpellOnKill[i] && IsValidSpell(itembonuses.SpellOnKill[i]) && (level >= itembonuses.SpellOnKill[i + 2])){
if(zone->random.Roll(static_cast<int>(itembonuses.SpellOnKill[i + 1]))) 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(spellbonuses.SpellOnKill[i] && IsValidSpell(spellbonuses.SpellOnKill[i]) && (level >= spellbonuses.SpellOnKill[i + 2])) {
if(zone->random.Roll(static_cast<int>(spellbonuses.SpellOnKill[i + 1]))) 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) { for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) {
if(IsClient() && aabonuses.SpellOnDeath[i] && IsValidSpell(aabonuses.SpellOnDeath[i])) { if(IsClient() && aabonuses.SpellOnDeath[i] && IsValidSpell(aabonuses.SpellOnDeath[i])) {
if(zone->random.Roll(static_cast<int>(aabonuses.SpellOnDeath[i + 1]))) { 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(itembonuses.SpellOnDeath[i] && IsValidSpell(itembonuses.SpellOnDeath[i])) {
if(zone->random.Roll(static_cast<int>(itembonuses.SpellOnDeath[i + 1]))) { 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(spellbonuses.SpellOnDeath[i] && IsValidSpell(spellbonuses.SpellOnDeath[i])) {
if(zone->random.Roll(static_cast<int>(spellbonuses.SpellOnDeath[i + 1]))) { 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);
} }
} }
} }

View File

@ -26,6 +26,7 @@
#include "aa_ability.h" #include "aa_ability.h"
#include "aa.h" #include "aa.h"
#include "../common/light_source.h" #include "../common/light_source.h"
#include "../common/emu_constants.h"
#include <set> #include <set>
#include <vector> #include <vector>
#include <memory> #include <memory>
@ -218,7 +219,7 @@ public:
//Song //Song
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1); 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); void BardPulse(uint16 spell_id, Mob *caster);
//Spell //Spell
@ -248,29 +249,30 @@ public:
void SendSpellBarEnable(uint16 spellid); void SendSpellBarEnable(uint16 spellid);
void ZeroCastingVars(); void ZeroCastingVars();
virtual void SpellProcess(); 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, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 *resist_adjust = nullptr, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 *resist_adjust = nullptr,
uint32 aa_id = 0); 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, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 resist_adjust = 0, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 resist_adjust = 0,
uint32 aa_id = 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); 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); 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, 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); 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 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, 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 CheckFizzle(uint16 spell_id);
virtual bool CheckSpellLevelRestriction(uint16 spell_id); virtual bool CheckSpellLevelRestriction(uint16 spell_id);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
virtual float GetAOERange(uint16 spell_id); virtual float GetAOERange(uint16 spell_id);
void InterruptSpell(uint16 spellid = SPELL_UNKNOWN); void InterruptSpell(uint16 spellid = SPELL_UNKNOWN);
void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN); void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN);
void StopCasting();
inline bool IsCasting() const { return((casting_spell_id != 0)); } inline bool IsCasting() const { return((casting_spell_id != 0)); }
uint16 CastingSpellID() const { return casting_spell_id; } uint16 CastingSpellID() const { return casting_spell_id; }
bool DoCastingChecks(); bool DoCastingChecks();
@ -306,6 +308,8 @@ public:
virtual int GetMaxSongSlots() const { return 0; } virtual int GetMaxSongSlots() const { return 0; }
virtual int GetMaxDiscSlots() const { return 0; } virtual int GetMaxDiscSlots() const { return 0; }
virtual int GetMaxTotalSlots() 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 InitializeBuffSlots() { buffs = nullptr; current_buff_count = 0; }
virtual void UninitializeBuffSlots() { } virtual void UninitializeBuffSlots() { }
EQApplicationPacket *MakeBuffsPacket(bool for_target = true); EQApplicationPacket *MakeBuffsPacket(bool for_target = true);
@ -1216,7 +1220,7 @@ protected:
int attacked_count; int attacked_count;
bool delaytimer; bool delaytimer;
uint16 casting_spell_targetid; uint16 casting_spell_targetid;
uint16 casting_spell_slot; EQEmu::CastingSlot casting_spell_slot;
uint16 casting_spell_mana; uint16 casting_spell_mana;
uint32 casting_spell_inventory_slot; uint32 casting_spell_inventory_slot;
uint32 casting_spell_timer; uint32 casting_spell_timer;
@ -1226,7 +1230,7 @@ protected:
uint32 casting_spell_aa_id; uint32 casting_spell_aa_id;
bool casting_spell_checks; bool casting_spell_checks;
uint16 bardsong; uint16 bardsong;
uint8 bardsong_slot; EQEmu::CastingSlot bardsong_slot;
uint32 bardsong_target_id; uint32 bardsong_target_id;
bool ActiveProjectileATK; bool ActiveProjectileATK;

View File

@ -341,7 +341,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
SetCurrentSpeed(0); 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) { 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 spell_to_cast = 0xFFFFFFFF;
uint32 slot_to_use = 10; EQEmu::CastingSlot slot_to_use = EQEmu::CastingSlot::Item;
if(valid_spells.size() == 1) if(valid_spells.size() == 1)
{ {
spell_to_cast = valid_spells[0]; 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()) else if(valid_spells.empty())
{ {
@ -687,7 +687,7 @@ void Client::AI_SpellCast()
{ {
uint32 idx = zone->random.Int(0, (valid_spells.size()-1)); uint32 idx = zone->random.Int(0, (valid_spells.size()-1));
spell_to_cast = valid_spells[idx]; 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)) if(IsMezSpell(spell_to_cast) || IsFearSpell(spell_to_cast))
@ -2362,8 +2362,10 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
return a.priority > b.priority; return a.priority > b.priority;
}); });
if (IsValidSpell(attack_proc_spell)) if (IsValidSpell(attack_proc_spell)) {
AddProcToWeapon(attack_proc_spell, true, proc_chance); AddProcToWeapon(attack_proc_spell, true, proc_chance);
innate_proc_spell_id = attack_proc_spell;
}
if (IsValidSpell(range_proc_spell)) if (IsValidSpell(range_proc_spell))
AddRangedProc(range_proc_spell, (rproc_chance + 100)); AddRangedProc(range_proc_spell, (rproc_chance + 100));

View File

@ -233,6 +233,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
npc_spells_id = 0; npc_spells_id = 0;
HasAISpell = false; HasAISpell = false;
HasAISpellEffects = false; HasAISpellEffects = false;
innate_proc_spell_id = 0;
if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs)) if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs))
{ {
@ -2006,44 +2007,90 @@ void NPC::LevelScale() {
float scaling = (((random_level / (float)level) - 1) * (scalerate / 100.0f)); float scaling = (((random_level / (float)level) - 1) * (scalerate / 100.0f));
// Compensate for scale rates at low levels so they don't add too much if (RuleB(NPC, NewLevelScaling)) {
uint8 scale_adjust = 1; if (scalerate == 0 || maxlevel <= 25) {
if(level > 0 && level <= 5) // pre-pop seems to scale by 20 HP increments while newer by 100
scale_adjust = 10; // We also don't want 100 increments on newer noobie zones, check level
if(level > 5 && level <= 10) if (zone->GetZoneID() < 200 || level < 48) {
scale_adjust = 5; max_hp += (random_level - level) * 20;
if(level > 10 && level <= 15) base_hp += (random_level - level) * 20;
scale_adjust = 3; } else {
if(level > 15 && level <= 25) max_hp += (random_level - level) * 100;
scale_adjust = 2; base_hp += (random_level - level) * 100;
}
base_hp += (int)(base_hp * scaling); cur_hp = max_hp;
max_hp += (int)(max_hp * scaling); max_dmg += (random_level - level) * 2;
cur_hp = max_hp; } else {
STR += (int)(STR * scaling / scale_adjust); uint8 scale_adjust = 1;
STA += (int)(STA * scaling / scale_adjust);
AGI += (int)(AGI * scaling / scale_adjust); base_hp += (int)(base_hp * scaling);
DEX += (int)(DEX * scaling / scale_adjust); max_hp += (int)(max_hp * scaling);
INT += (int)(INT * scaling / scale_adjust); cur_hp = max_hp;
WIS += (int)(WIS * scaling / scale_adjust);
CHA += (int)(CHA * scaling / scale_adjust); if (max_dmg) {
if (MR) max_dmg += (int)(max_dmg * scaling / scale_adjust);
MR += (int)(MR * scaling / scale_adjust); min_dmg += (int)(min_dmg * scaling / scale_adjust);
if (CR) }
CR += (int)(CR * scaling / scale_adjust);
if (DR) STR += (int)(STR * scaling / scale_adjust);
DR += (int)(DR * scaling / scale_adjust); STA += (int)(STA * scaling / scale_adjust);
if (FR) AGI += (int)(AGI * scaling / scale_adjust);
FR += (int)(FR * scaling / scale_adjust); DEX += (int)(DEX * scaling / scale_adjust);
if (PR) INT += (int)(INT * scaling / scale_adjust);
PR += (int)(PR * 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; level = random_level;
return; return;

View File

@ -407,6 +407,7 @@ public:
void mod_npc_killed_merit(Mob* c); void mod_npc_killed_merit(Mob* c);
void mod_npc_killed(Mob* oos); void mod_npc_killed(Mob* oos);
void AISpellsList(Client *c); void AISpellsList(Client *c);
uint16 GetInnateProcSpellID() const { return innate_proc_spell_id; }
uint32 GetHeroForgeModel() const { return herosforgemodel; } uint32 GetHeroForgeModel() const { return herosforgemodel; }
void SetHeroForgeModel(uint32 model) { herosforgemodel = model; } void SetHeroForgeModel(uint32 model) { herosforgemodel = model; }
@ -454,6 +455,7 @@ protected:
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
AISpellsVar_Struct AISpellVar; AISpellsVar_Struct AISpellVar;
int16 GetFocusEffect(focusType type, uint16 spell_id); int16 GetFocusEffect(focusType type, uint16 spell_id);
uint16 innate_proc_spell_id;
uint32 npc_spells_effects_id; uint32 npc_spells_effects_id;
std::vector<AISpellsEffects_Struct> AIspellsEffects; std::vector<AISpellsEffects_Struct> AIspellsEffects;

View File

@ -3982,12 +3982,12 @@ XS(XS_Mob_CastSpell)
{ {
dXSARGS; dXSARGS;
if (items < 3 || items > 7) 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; Mob * THIS;
uint16 spell_id = (uint16)SvUV(ST(1)); uint16 spell_id = (uint16)SvUV(ST(1));
uint16 target_id = (uint16)SvUV(ST(2)); uint16 target_id = (uint16)SvUV(ST(2));
uint16 slot; EQEmu::CastingSlot slot;
int32 casttime; int32 casttime;
int32 mana_cost; int32 mana_cost;
int16 resist_adjust; int16 resist_adjust;
@ -4002,9 +4002,9 @@ XS(XS_Mob_CastSpell)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items < 4) if (items < 4)
slot = 10; slot = EQEmu::CastingSlot::Item;
else { else {
slot = (uint16)SvUV(ST(3)); slot = static_cast<EQEmu::CastingSlot>(SvUV(ST(3)));
} }
if (items < 5) if (items < 5)
@ -4085,7 +4085,7 @@ XS(XS_Mob_SpellFinished)
resist_diff = spells[spell_id].ResistDiff; 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; XSRETURN_EMPTY;
} }

View File

@ -365,14 +365,14 @@ void QuestManager::castspell(int spell_id, int target_id) {
if (owner) { if (owner) {
Mob *tgt = entity_list.GetMob(target_id); Mob *tgt = entity_list.GetMob(target_id);
if(tgt != nullptr) 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) { void QuestManager::selfcast(int spell_id) {
QuestManagerCurrentQuestVars(); QuestManagerCurrentQuestVars();
if (initiator) 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) { void QuestManager::addloot(int item_id, int charges, bool equipitem) {

View File

@ -149,7 +149,7 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32
IsValidSpell(aabonuses.SkillAttackProc[2])) { IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f; float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance)) 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); spells[aabonuses.SkillAttackProc[2]].ResistDiff);
} }
who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false); 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. //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow.
int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; 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); DeleteItemInInventory(ammo_slot, 1, true);
Log.Out(Logs::Detail, Logs::Combat, "Consumed one arrow from slot %d", ammo_slot); Log.Out(Logs::Detail, Logs::Combat, "Consumed one arrow from slot %d", ammo_slot);
} else { } else {
@ -2429,7 +2429,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills:
IsValidSpell(aabonuses.SkillAttackProc[2])) { IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f; float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance)) 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); spells[aabonuses.SkillAttackProc[2]].ResistDiff);
} }
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);

View File

@ -194,6 +194,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
bool SE_SpellTrigger_HasCast = false; 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 // iterate through the effects in the spell
for (i = 0; i < EFFECT_COUNT; i++) 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; continue;
effect = spell.effectid[i]; 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)) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands))
effect_value = GetMaxHP(); effect_value = GetMaxHP();
@ -355,8 +357,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break; break;
int32 val = 0; int32 val = 0;
val = 7500*effect_value; val = 7500 * effect_value;
val = caster->GetActSpellHealing(spell_id, val, this); if (caster)
val = caster->GetActSpellHealing(spell_id, val, this);
if (val > 0) if (val > 0)
HealDamage(val, caster); 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); snprintf(effect_desc, _EDLEN, "Current Mana: %+i", effect_value);
#endif #endif
SetMana(GetMana() + effect_value); 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) if (effect_value < 0)
TryTriggerOnValueAmount(false, true); TryTriggerOnValueAmount(false, true);
#ifdef SPELL_EFFECT_SPAM #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 #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 { // Teleport Bind uses caster's bind point
int index = spells[spell_id].base[i] - 1; int index = spells[spell_id].base[i] - 1;
if (index < 0 || index > 4) 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. //Added client messages to give some indication this effect is active.
// Is there a message generated? Too disgusted by raids. // Is there a message generated? Too disgusted by raids.
uint32 time = spell.base[i] * 10 * 1000; uint32 time = spell.base[i] * 10 * 1000;
if (caster->IsClient()) { if (caster && caster->IsClient()) {
if (caster->IsGrouped()) { if (caster->IsGrouped()) {
auto group = caster->GetGroup(); auto group = caster->GetGroup();
for (int i = 0; i < 6; ++i) 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() || ((GetLevel() > max_level) && caster && (!caster->IsNPC() ||
(caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))))) (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))))))
{ {
caster->Message_StringID(MT_SpellFailure, IMMUNE_STUN); if (caster)
caster->Message_StringID(MT_SpellFailure, IMMUNE_STUN);
} else { } else {
int stun_resist = itembonuses.StunResist+spellbonuses.StunResist; int stun_resist = itembonuses.StunResist+spellbonuses.StunResist;
if (IsClient()) 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) { 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); 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; effect_value += effect_value*caster->GetFocusEffect(focusFcStunTimeMod, spell_id)/100;
Stun(effect_value); 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; cd->meleepush_xy = action->sequence;
CastToClient()->QueuePacket(action_packet); CastToClient()->QueuePacket(action_packet);
if(caster->IsClient() && caster != this) if(caster && caster->IsClient() && caster != this)
caster->CastToClient()->QueuePacket(action_packet); caster->CastToClient()->QueuePacket(action_packet);
CastToClient()->QueuePacket(message_packet); CastToClient()->QueuePacket(message_packet);
if(caster->IsClient() && caster != this) if(caster && caster->IsClient() && caster != this)
caster->CastToClient()->QueuePacket(message_packet); caster->CastToClient()->QueuePacket(message_packet);
CastToClient()->SetBindPoint(spells[spell_id].base[i] - 1); 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)) if(zone->random.Roll(effect_value))
Gate(spells[spell_id].base2[i] - 1); Gate(spells[spell_id].base2[i] - 1);
else else if (caster)
caster->Message_StringID(MT_SpellFailure,GATE_FAIL); caster->Message_StringID(MT_SpellFailure,GATE_FAIL);
} }
break; 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); snprintf(effect_desc, _EDLEN, "Cancel Magic: %d", effect_value);
#endif #endif
if(GetSpecialAbility(UNDISPELLABLE)){ 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; 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 && spells[buffs[slot].spellid].dispel_flag == 0 &&
!IsDiscipline(buffs[slot].spellid)) !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); BuffFadeBySlot(slot);
slot = buff_count; 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); snprintf(effect_desc, _EDLEN, "Dispel Detrimental: %d", effect_value);
#endif #endif
if(GetSpecialAbility(UNDISPELLABLE)){ 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; break;
} }
@ -1078,7 +1086,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
IsDetrimentalSpell(buffs[slot].spellid) && IsDetrimentalSpell(buffs[slot].spellid) &&
spells[buffs[slot].spellid].dispel_flag == 0) 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); BuffFadeBySlot(slot);
slot = buff_count; 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); snprintf(effect_desc, _EDLEN, "Dispel Beneficial: %d", effect_value);
#endif #endif
if(GetSpecialAbility(UNDISPELLABLE)){ 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; break;
} }
@ -1103,7 +1112,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
IsBeneficialSpell(buffs[slot].spellid) && IsBeneficialSpell(buffs[slot].spellid) &&
spells[buffs[slot].spellid].dispel_flag == 0) 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); BuffFadeBySlot(slot);
slot = buff_count; 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 && if (buffs[slot].spellid != SPELL_UNKNOWN &&
IsDetrimentalSpell(buffs[slot].spellid)) 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); BuffFadeBySlot(slot);
} }
} }
@ -1509,7 +1518,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
((GetLevel() > max_level) ((GetLevel() > max_level)
&& caster && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))))) && 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 else
{ {
@ -1726,7 +1736,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break; break;
} }
} }
else { else if (caster) {
Raid *r = entity_list.GetRaidByClient(caster->CastToClient()); Raid *r = entity_list.GetRaidByClient(caster->CastToClient());
if(r) 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); Message_StringID(4, CORPSE_CANT_SENSE);
} }
} }
else else if (caster)
caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ); caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ);
} }
else { else {
@ -2113,7 +2123,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#ifdef SPELL_EFFECT_SPAM #ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Sacrifice"); snprintf(effect_desc, _EDLEN, "Sacrifice");
#endif #endif
if(!IsClient() || !caster->IsClient()){ if(!caster || !IsClient() || !caster->IsClient()){
break; break;
} }
CastToClient()->SacrificeConfirm(caster->CastToClient()); CastToClient()->SacrificeConfirm(caster->CastToClient());
@ -2122,12 +2132,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_SummonPC: case SE_SummonPC:
{ {
if(IsClient()){ if (!caster)
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), caster->GetX(), caster->GetY(), caster->GetZ(), caster->GetHeading(), 2, SummonPC); 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!"); Message(15, "You have been summoned!");
entity_list.ClearAggro(this); entity_list.ClearAggro(this);
} } else
else
caster->Message(13, "This spell can only be cast on players."); caster->Message(13, "This spell can only be cast on players.");
break; 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: case SE_TemporaryPets: //Dook- swarms and wards:
{ {
if (!caster)
break;
// this makes necro epic 1.5/2.0 proc work properly // this makes necro epic 1.5/2.0 proc work properly
if((spell_id != 6882) && (spell_id != 6884)) // Chaotic Jester/Steadfast Servant 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; int16 focus = 0;
int ReuseTime = spells[spell_id].recast_time + spells[spell_id].recovery_time; int ReuseTime = spells[spell_id].recast_time + spells[spell_id].recovery_time;
if (!caster)
break;
focus = caster->GetFocusEffect(focusFcBaseEffects, spell_id); 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"); snprintf(effect_desc, _EDLEN, "Wake The Dead");
#endif #endif
//meh dupe issue with npc casting this //meh dupe issue with npc casting this
if(caster->IsClient()){ if(caster && caster->IsClient()){
int dur = spells[spell_id].max[i]; int dur = spells[spell_id].max[i];
if (!dur) if (!dur)
dur = 60; dur = 60;
@ -2657,7 +2674,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_Taunt: case SE_Taunt:
{ {
if (IsNPC()){ if (caster && IsNPC()){
caster->Taunt(this->CastToNPC(), false, static_cast<float>(spell.base[i]), true, spell.base2[i]); caster->Taunt(this->CastToNPC(), false, static_cast<float>(spell.base[i]), true, spell.base2[i]);
} }
break; 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 (caster && IsValidSpell(spells[spell_id].base2[i])){
if(zone->random.Roll(spells[spell_id].base[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; break;
} }
@ -3668,10 +3685,11 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
break; break;
} }
// These effects always trigger when they fade. // These effects always trigger when they fade.
// Should we have this triggered from else where?
case SE_CastOnFadeEffect: case SE_CastOnFadeEffect:
case SE_CastOnFadeEffectNPC: case SE_CastOnFadeEffectNPC:
case SE_CastOnFadeEffectAlways: { case SE_CastOnFadeEffectAlways: {
if (buff.ticsremaining == 1) { if (buff.ticsremaining == 0) {
SpellOnTarget(spells[buff.spellid].base[i], this); SpellOnTarget(spells[buff.spellid].base[i], this);
} }
break; break;
@ -5135,7 +5153,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
if (Caston_spell_id) { if (Caston_spell_id) {
if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != 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); 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). /*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 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 100 : *Animal OR Humanoid
Range 101 : *Dragon Range 101 : *Dragon
Range 102 : *Animal OR Insect Range 102 : *Animal OR Insect
@ -6253,7 +6271,10 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
Range 124 : *Undead HP < 10% Range 124 : *Undead HP < 10%
Range 125 : *Clockwork HP < 10% Range 125 : *Clockwork HP < 10%
Range 126 : *Wisp 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 150 : UNKNOWN
Range 190 : No Raid boss flag *not implemented 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 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 300 - 303 : UNKOWN *not implemented
Range 304 : Chain + Plate class (buffs) 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 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 500 - 599 : Heal if HP less than a specified value
Range 600 - 699 : Limit to Body Type [base2 - 600 = Body] 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 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 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 818 - 819 : If Undead/If Not Undead
Range 820 - 822 : UKNOWN Range 820 - 822 : UKNOWN
Range 835 : Unknown *not implemented Range 835 : Unknown *not implemented
@ -6290,6 +6316,9 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
switch(value) switch(value)
{ {
case 1:
return true;
case 100: case 100:
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid)) if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid))
return true; return true;
@ -6683,10 +6712,10 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
if (IsValidSpell(spell_id)) { if (IsValidSpell(spell_id)) {
if (IsBeneficialSpell(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) 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);
} }
} }
} }

View File

@ -104,6 +104,8 @@ extern Zone* zone;
extern volatile bool is_zone_loaded; extern volatile bool is_zone_loaded;
extern WorldServer worldserver; extern WorldServer worldserver;
using EQEmu::CastingSlot;
// this is run constantly for every mob // this is run constantly for every mob
void Mob::SpellProcess() void Mob::SpellProcess()
{ {
@ -145,13 +147,13 @@ void NPC::SpellProcess()
// the rule is you can cast one triggered (usually timed) spell at a time // 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 // but things like SpellFinished() can run concurrent with a triggered cast
// to allow procs to work // 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, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot,
uint32 timer, uint32 timer_duration, int16 *resist_adjust, uint32 timer, uint32 timer_duration, int16 *resist_adjust,
uint32 aa_id) 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", 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) if(casting_spell_id == spell_id)
ZeroCastingVars(); ZeroCastingVars();
@ -178,7 +180,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
if(IsClient()) if(IsClient())
CastToClient()->SendSpellBarEnable(spell_id); CastToClient()->SendSpellBarEnable(spell_id);
if(casting_spell_id && IsNPC()) 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); return(false);
} }
//It appears that the Sanctuary effect is removed by a check on the client side (keep this however for redundancy) //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()) if(IsClient())
CastToClient()->SendSpellBarEnable(spell_id); CastToClient()->SendSpellBarEnable(spell_id);
if(casting_spell_id && IsNPC()) 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); return(false);
} }
@ -231,7 +233,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
// check for fizzle // check for fizzle
// note that CheckFizzle itself doesn't let NPCs fizzle, // note that CheckFizzle itself doesn't let NPCs fizzle,
// but this code allows for it. // 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; int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
InterruptSpell(fizzle_msg, 0x121, spell_id); 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. //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); ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot);
int bitmask = 1; 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 // this is the 2nd phase of CastSpell, broken up like this to make it easier
// to repeat a spell for bard songs // 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, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish,
uint32 item_slot, uint32 timer, uint32 timer_duration, uint32 item_slot, uint32 timer, uint32 timer_duration,
int16 resist_adjust, uint32 aa_id) int16 resist_adjust, uint32 aa_id)
{ {
Mob* pMob = nullptr; Mob* pMob = nullptr;
int32 orgcasttime; int32 orgcasttime;
EQApplicationPacket *outapp = nullptr;
if(!IsValidSpell(spell_id)) { if(!IsValidSpell(spell_id)) {
InterruptSpell(); InterruptSpell();
@ -353,7 +354,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
const SPDat_Spell_Struct &spell = spells[spell_id]; 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", 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_id = spell_id;
casting_spell_slot = slot; 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 // If you're at full mana, let it cast even if you dont have enough mana
// we calculated this above, now enforce it // 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_curmana = GetMana();
int my_maxmana = GetMaxMana(); 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", Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Casting time %d (orig %d), mana cost %d",
spell_id, cast_time, orgcasttime, mana_cost); 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 // cast time is 0, just finish it right now and be done with it
if(cast_time == 0) { if(cast_time == 0) {
if (!DoCastingChecks()) {
StopCasting();
return false;
}
CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust); CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust);
return(true); return(true);
} }
@ -476,25 +495,14 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
if (oSpellWillFinish) if (oSpellWillFinish)
*oSpellWillFinish = Timer::GetCurrentTime() + cast_time + 100; *oSpellWillFinish = Timer::GetCurrentTime() + cast_time + 100;
// now tell the people in the area if (IsClient() && slot == CastingSlot::Item && item_slot != 0xFFFFFFFF) {
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) {
auto item = CastToClient()->GetInv().GetItem(item_slot); auto item = CastToClient()->GetInv().GetItem(item_slot);
if (item && item->GetItem()) if (item && item->GetItem())
Message_StringID(MT_Spells, BEGINS_TO_GLOW, item->GetItem()->Name); Message_StringID(MT_Spells, BEGINS_TO_GLOW, item->GetItem()->Name);
} }
if (!DoCastingChecks()) { if (!DoCastingChecks()) {
InterruptSpell(); StopCasting();
return false; 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; casting_spell_checks = true;
return true; return true;
} }
@ -767,7 +779,7 @@ void Mob::ZeroCastingVars()
spellend_timer.Disable(); spellend_timer.Disable();
casting_spell_id = 0; casting_spell_id = 0;
casting_spell_targetid = 0; casting_spell_targetid = 0;
casting_spell_slot = 0; casting_spell_slot = CastingSlot::Gem1;
casting_spell_mana = 0; casting_spell_mana = 0;
casting_spell_inventory_slot = 0; casting_spell_inventory_slot = 0;
casting_spell_timer = 0; casting_spell_timer = 0;
@ -802,7 +814,7 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
} }
if(casting_spell_id && IsNPC()) { 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 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 // this is called after the timer is up and the spell is finished
// casting. everything goes through here, including items with zero cast time // casting. everything goes through here, including items with zero cast time
// only to be used from SpellProcess // only to be used from SpellProcess
// NOTE: do not put range checking, etc into this function. this should // NOTE: do not put range checking, etc into this function. this should
// just check timed spell specific things before passing off to SpellFinished // just check timed spell specific things before passing off to SpellFinished
// which figures out proper targets etc // 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) uint16 mana_used, uint32 inventory_slot, int16 resist_adjust)
{ {
bool IsFromItem = false; 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)) { if(!CastToClient()->GetPTimers().Expired(&database, pTimerSpellStart + spell_id, false)) {
//should we issue a message or send them a spell gem packet? //should we issue a message or send them a spell gem packet?
Message_StringID(13, SPELL_RECAST); 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; IsFromItem = true;
ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot); 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; 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 && inventory_slot != 0xFFFFFFFF) // 10 is an item
{ {
bool fromaug = false; bool fromaug = false;
@ -1306,8 +1344,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
{ {
if(IsClient()) if(IsClient())
{ {
this->CastToClient()->CheckSongSkillIncrease(spell_id); Client *c = CastToClient();
this->CastToClient()->MemorizeSpell(slot, spell_id, memSpellSpellbar); 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); 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); SendSpellBarEnable(spell_id);
// this causes the delayed refresh of the spell bar gems // 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 // this tells the client that casting may happen again
SetMana(GetMana()); SetMana(GetMana());
// skills // skills
if(slot < MAX_PP_MEMSPELL) if (EQEmu::skills::IsCastingSkill(spells[spell_id].skill)) {
{
c->CheckIncreaseSkill(spells[spell_id].skill, nullptr); c->CheckIncreaseSkill(spells[spell_id].skill, nullptr);
// increased chance of gaining channel skill if you regained concentration // 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: The basic types of spells:
@ -1395,6 +1437,11 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
targetType = ST_GroupClientAndPet; 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)){ if (spell_target && !spell_target->PassCastRestriction(true, spells[spell_id].CastRestriction)){
Message_StringID(13,SPELL_NEED_TAR); Message_StringID(13,SPELL_NEED_TAR);
return false; return false;
@ -1682,7 +1729,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
case ST_Group: case ST_Group:
case ST_GroupNoPets: 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) || if( (!target) ||
(target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) || (target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) ||
(target->IsCorpse()) ) (target->IsCorpse()) )
@ -1728,7 +1775,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
{ {
if(IsGrouped()) if(IsGrouped())
{ {
group_id_caster = GetGroup()->GetID(); if (Group* group = GetGroup()) {
group_id_caster = group->GetID();
}
} }
else if(IsRaidGrouped()) else if(IsRaidGrouped())
{ {
@ -1743,7 +1792,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
Mob *owner = GetOwner(); Mob *owner = GetOwner();
if(owner->IsGrouped()) if(owner->IsGrouped())
{ {
group_id_caster = owner->GetGroup()->GetID(); if (Group* group = owner->GetGroup()) {
group_id_caster = group->GetID();
}
} }
else if(owner->IsRaidGrouped()) else if(owner->IsRaidGrouped())
{ {
@ -1772,7 +1823,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
{ {
if(spell_target->IsGrouped()) 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()) 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(); Mob *owner = spell_target->GetOwner();
if(owner->IsGrouped()) if(owner->IsGrouped())
{ {
group_id_target = owner->GetGroup()->GetID(); if (Group* group = owner->GetGroup()) {
group_id_target = group->GetID();
}
} }
else if(owner->IsRaidGrouped()) 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 // only used from CastedSpellFinished, and procs
// we can't interrupt in this, or anything called from this! // we can't interrupt in this, or anything called from this!
// if you need to abort the casting, return false // 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) uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override)
{ {
//EQApplicationPacket *outapp = nullptr; //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 //determine the type of spell target we have
CastAction_type CastAction; 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); 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"); 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; bool mgb = HasMGB() && spells[spell_id].can_mgb;
// if this was a spell slot or an ability use up the mana for it // 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); mana_used = GetActSpellCost(spell_id, mana_used);
if (mgb) { 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); ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot);
if(itm && itm->GetItem()->RecastDelay > 0){ if(itm && itm->GetItem()->RecastDelay > 0){
@ -2345,7 +2400,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
} }
if(IsNPC()) if(IsNPC())
CastToNPC()->AI_Event_SpellCastFinished(true, slot); CastToNPC()->AI_Event_SpellCastFinished(true, static_cast<uint16>(slot));
return true; return true;
} }
@ -2360,8 +2415,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
* *
* return false to stop the song * return false to stop the song
*/ */
bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot) { bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slot) {
if(slot == USE_ITEM_SPELL_SLOT) { if(slot == CastingSlot::Item) {
//bard songs should never come from items... //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); Log.Out(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: Supposidly cast from an item. Killing song.", spell_id);
return(false); return(false);
@ -2972,8 +3027,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
if if
( (
effect1 == SE_AttackSpeed || effect1 == SE_AttackSpeed ||
effect1 == SE_AttackSpeed2 || effect1 == SE_AttackSpeed2
effect1 == SE_AttackSpeed3
) )
{ {
sp1_value -= 100; sp1_value -= 100;
@ -3047,6 +3101,34 @@ bool Client::CheckSpellLevelRestriction(uint16 spell_id)
return true; 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 // 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 // stacking problems, and -2 if this is not a buff
// if caster is null, the buff will be added with the caster level being // 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 also check if overwriting will occur. this is so after this loop
// we can determine if there will be room for this buff // we can determine if there will be room for this buff
int buff_count = GetMaxTotalSlots(); int buff_count = GetMaxTotalSlots();
uint32 start_slot = 0; uint32 start_slot = GetFirstBuffSlot(IsDisciplineBuff(spell_id), spells[spell_id].short_buff_box);
uint32 end_slot = 0; uint32 end_slot = GetLastBuffSlot(IsDisciplineBuff(spell_id), spells[spell_id].short_buff_box);
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();
}
for (buffslot = 0; buffslot < buff_count; buffslot++) { for (buffslot = 0; buffslot < buff_count; buffslot++) {
const Buffs_Struct &curbuf = buffs[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 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", 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); 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); 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; 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) 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)) { if (IsDetrimentalSpell(spell_id)) {
@ -4713,6 +4785,7 @@ void Mob::SendSpellBarEnable(uint16 spell_id)
manachange->new_mana = GetMana(); manachange->new_mana = GetMana();
manachange->spell_id = spell_id; manachange->spell_id = spell_id;
manachange->stamina = CastToClient()->GetEndurance(); manachange->stamina = CastToClient()->GetEndurance();
manachange->keepcasting = 0;
outapp->priority = 6; outapp->priority = 6;
CastToClient()->QueuePacket(outapp); CastToClient()->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
@ -5245,7 +5318,7 @@ bool Mob::UseBardSpellLogic(uint16 spell_id, int slot)
spell_id = casting_spell_id; spell_id = casting_spell_id;
if(slot == -1) if(slot == -1)
slot = casting_spell_slot; slot = static_cast<int>(casting_spell_slot);
// should we treat this as a bard singing? // should we treat this as a bard singing?
return return
@ -5273,7 +5346,7 @@ void Mob::_StopSong()
{ {
bardsong = 0; bardsong = 0;
bardsong_target_id = 0; bardsong_target_id = 0;
bardsong_slot = 0; bardsong_slot = CastingSlot::Gem1;
bardsong_timer.Disable(); bardsong_timer.Disable();
} }
@ -5351,8 +5424,8 @@ void Mob::SendPetBuffsToClient()
int MaxSlots = GetMaxTotalSlots(); int MaxSlots = GetMaxTotalSlots();
if(MaxSlots > BUFF_COUNT) if(MaxSlots > PET_BUFF_COUNT)
MaxSlots = BUFF_COUNT; MaxSlots = PET_BUFF_COUNT;
for(int buffslot = 0; buffslot < MaxSlots; buffslot++) for(int buffslot = 0; buffslot < MaxSlots; buffslot++)
{ {
@ -5449,10 +5522,14 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration)
int Client::GetCurrentBuffSlots() const int Client::GetCurrentBuffSlots() const
{ {
if(15 + aabonuses.BuffSlotIncrease > 25) int numbuffs = 15;
return 25; // client does check spells and items
else numbuffs += aabonuses.BuffSlotIncrease + spellbonuses.BuffSlotIncrease + itembonuses.BuffSlotIncrease;
return 15 + aabonuses.BuffSlotIncrease; if (GetLevel() > 70)
numbuffs++;
if (GetLevel() > 74)
numbuffs++;
return EQEmu::ClampUpper(numbuffs, GetMaxBuffSlots());
} }
int Client::GetCurrentSongSlots() const int Client::GetCurrentSongSlots() const
@ -5644,3 +5721,26 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust)
++iter; ++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);
}

View File

@ -297,6 +297,7 @@
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. #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 SUCCOR_FAIL 5169 //The portal collapes before you can escape!
#define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.' #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 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 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. #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 HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage.
#define GLOWS_BLUE 9074 //Your %1 glows blue. #define GLOWS_BLUE 9074 //Your %1 glows blue.
#define GLOWS_RED 9075 //Your %1 glows red. #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 STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2. #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. #define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master.

View File

@ -115,7 +115,7 @@ void Trap::Trigger(Mob* trigger)
entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str());
} }
if(hiddenTrigger){ 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; break;
case trapTypeAlarm: case trapTypeAlarm:

View File

@ -8,7 +8,7 @@
extern const ZoneConfig *Config; extern const ZoneConfig *Config;
enum WaterRegionType { enum WaterRegionType : int {
RegionTypeUnsupported = -2, RegionTypeUnsupported = -2,
RegionTypeUntagged = -1, RegionTypeUntagged = -1,
RegionTypeNormal = 0, RegionTypeNormal = 0,
@ -33,6 +33,8 @@ public:
virtual bool InVWater(const glm::vec3& location) const = 0; virtual bool InVWater(const glm::vec3& location) const = 0;
virtual bool InLava(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 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: protected:
virtual bool Load(FILE *fp) { return false; } virtual bool Load(FILE *fp) { return false; }

View File

@ -30,6 +30,14 @@ bool WaterMapV1::InLiquid(const glm::vec3& location) const {
return InWater(location) || InLava(location); 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) { bool WaterMapV1::Load(FILE *fp) {
uint32 bsp_tree_size; uint32 bsp_tree_size;
if (fread(&bsp_tree_size, sizeof(bsp_tree_size), 1, fp) != 1) { if (fread(&bsp_tree_size, sizeof(bsp_tree_size), 1, fp) != 1) {

View File

@ -24,6 +24,8 @@ public:
virtual bool InVWater(const glm::vec3& location) const; virtual bool InVWater(const glm::vec3& location) const;
virtual bool InLava(const glm::vec3& location) const; virtual bool InLava(const glm::vec3& location) const;
virtual bool InLiquid(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: protected:
virtual bool Load(FILE *fp); virtual bool Load(FILE *fp);

View File

@ -33,6 +33,14 @@ bool WaterMapV2::InLiquid(const glm::vec3& location) const {
return InWater(location) || InLava(location); 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) { bool WaterMapV2::Load(FILE *fp) {
uint32 region_count; uint32 region_count;
if (fread(&region_count, sizeof(region_count), 1, fp) != 1) { if (fread(&region_count, sizeof(region_count), 1, fp) != 1) {

View File

@ -17,6 +17,8 @@ public:
virtual bool InVWater(const glm::vec3& location) const; virtual bool InVWater(const glm::vec3& location) const;
virtual bool InLava(const glm::vec3& location) const; virtual bool InLava(const glm::vec3& location) const;
virtual bool InLiquid(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: protected:
virtual bool Load(FILE *fp); virtual bool Load(FILE *fp);

View File

@ -765,7 +765,7 @@ void WorldServer::Process() {
zone->SetZoneHasCurrentTime(true); 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"); Log.Out(Logs::General, Logs::Zone_Server, "Received request to sync time from world, but our time is localized currently");
} }
break; break;

View File

@ -1594,7 +1594,9 @@ ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, uint32 to, Clien
iterator.Advance(); 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) if(client)
client->CheatDetected(MQZoneUnknownDest, location.x, location.y, location.z); // Someone is trying to use /zone client->CheatDetected(MQZoneUnknownDest, location.x, location.y, location.z); // Someone is trying to use /zone

View File

@ -3178,7 +3178,8 @@ void ZoneDatabase::SavePetInfo(Client *client)
query.clear(); query.clear();
// pet buffs! // 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) if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0)
continue; continue;
if (query.length() == 0) if (query.length() == 0)

View File

@ -125,7 +125,7 @@ struct PetInfo {
uint32 HP; uint32 HP;
uint32 Mana; uint32 Mana;
float size; float size;
SpellBuff_Struct Buffs[BUFF_COUNT]; SpellBuff_Struct Buffs[PET_BUFF_COUNT];
uint32 Items[EQEmu::legacy::EQUIPMENT_SIZE]; uint32 Items[EQEmu::legacy::EQUIPMENT_SIZE];
char Name[64]; char Name[64];
}; };