major updates

optimized
pet support
perl support
This commit is contained in:
KayenEQ
2021-08-09 23:03:21 -04:00
parent d1a260cb25
commit d3093c91ea
12 changed files with 254 additions and 110 deletions
+2
View File
@@ -45,6 +45,8 @@ enum : int { //values for pTimerType
pTimerLinkedSpellReuseStart = 28,
pTimerLinkedSpellReuseEnd = 48,
pTimerShieldAbility = 86,
pTimerLayHands = 87, //these IDs are used by client too
pTimerHarmTouch = 89, //so dont change them
+1 -1
View File
@@ -572,7 +572,7 @@ typedef enum {
#define SE_FleshToBone 207 // implemented
//#define SE_PurgePoison 208 // not used
#define SE_DispelBeneficial 209 // implemented
//#define SE_PetShield 210 // *not implemented
#define SE_PetShield 210 // implmented, @Shiedling, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: 1 (Time multiplier 1=12 seconds, 2=24 ect), limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live)
#define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm).
#define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana.
#define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet
+19 -24
View File
@@ -1691,6 +1691,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
InterruptSpell();
SetPet(0);
SetHorseId(0);
ShieldAbilityClearVariables();
dead = true;
if (GetMerc()) {
@@ -2252,6 +2253,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
Log(Logs::Detail, Logs::Attack, "%s Mobs currently Aggro %i", __FUNCTION__, zone->MobsAggroCount());
}
ShieldAbilityClearVariables();
SetHP(0);
SetPet(0);
@@ -5278,61 +5281,53 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill));
Shout("Shielder ID [%i]", defender->GetShielderID());
if (defender->GetShielderID()) {
hit.damage_done = hit.damage_done * 50 / 100;//Don't round.
DoShieldDamageOnShielder(defender, hit.damage_done, hit.skill);
hit.damage_done -= hit.damage_done * defender->GetShieldTargetMitigation() / 100; //Default shielded takes 50 pct damage
}
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
}
void Mob::DoShieldDamageOnShielder(Mob* shielder_target, int hit_damage_done, EQ::skills::SkillType skillInUse)
void Mob::DoShieldDamageOnShielder(Mob* shield_target, int hit_damage_done, EQ::skills::SkillType skillInUse)
{
if (!shielder_target) {
if (!shield_target) {
return;
}
Mob *shielder = entity_list.GetMob(shielder_target->GetShielderID());
Mob *shielder = entity_list.GetMob(shield_target->GetShielderID());
if (!shielder) {
return;
}
//AA to increase SPA 230 extended shielding
int max_shielder_distance = 15;
int distance_mod = aabonuses.ExtendedShielding + itembonuses.ExtendedShielding + spellbonuses.ExtendedShielding;
max_shielder_distance += max_shielder_distance * distance_mod / 100;
max_shielder_distance = std::max(max_shielder_distance, 0);
if (shielder_target->CalculateDistance(shielder->GetX(), shielder->GetY(), shielder->GetZ()) > static_cast<float>(max_shielder_distance)) {
//Clear variables Shield end
Shout("Clear variables shield end");
if (shield_target->CalculateDistance(shielder->GetX(), shielder->GetY(), shielder->GetZ()) > static_cast<float>(shielder->GetMaxShielderDistance())) {
shielder->SetShieldTargetID(0);
shielder_target->SetShielderID(0);
shielder->SetShielderMitigation(0);
shielder->SetShielerMaxDistance(0);
shielder->shield_timer.Disable();
shield_target->SetShielderID(0);
shield_target->SetShieldTargetMitigation(0);
return; //Too far away, no message is given thoughh.
}
int mitigation = 75;
int mitigation = shielder->GetShielderMitigation(); //Default shielder mitigates 25 pct of damage taken, this can be increased up to max 50 by equiping a shield item
if (shielder->HasShieldEquiped() && shielder->IsClient()) {
if (shielder->IsClient() && shielder->HasShieldEquiped()) {
EQ::ItemInstance* inst = CastToClient()->GetInv().GetItem(EQ::invslot::slotSecondary);
EQ::ItemInstance* inst = shielder->CastToClient()->GetInv().GetItem(EQ::invslot::slotSecondary);
if (inst) {
const EQ::ItemData* shield = inst->GetItem();
if (shield && shield->ItemType == EQ::item::ItemTypeShield) {
mitigation -= shield->AC * 0.50; //1% increase per 2 AC
mitigation += shield->AC * 50 / 100; //1% increase per 2 AC
std::min(50, mitigation);//50 pct max mitigation bonus from /shield
}
}
}
mitigation = std::max(mitigation, 50);
hit_damage_done = hit_damage_done * mitigation / 100;
hit_damage_done -= hit_damage_done * mitigation / 100;
shielder->Damage(this, hit_damage_done, SPELL_UNKNOWN, skillInUse, true, -1, false, m_specialattacks);
shielder->CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
}
+47 -32
View File
@@ -12804,14 +12804,30 @@ void Client::Handle_OP_SetTitle(const EQApplicationPacket *app)
void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
{
/*
/shield command mechanics
Warriors get this skill at level 30
Used by typing /shield while targeting a player
While active for the duration of 12 seconds baseline. The 'shield target' will take 50 pct less damage and
the 'shielder' will be hit with the damage taken by the 'shield target' after all applicable mitigiont is calculated,
the damage on the 'shielder' will be reduced by 25 percent, this reduction can be increased to 50 pct if equiping a shield.
You receive a 1% increase in mitigation for every 2 AC on the shield.
Shielder must stay with in a close distance (15 units) to your 'shield target'. If either move out of range, shield ends, no message given.
Both duration and shield range can be modified by AA.
Recast is 3 minutes.
For custom use cases, Mob::ShieldAbility can be used in quests with all parameters being altered. This functional
is also used for SPA 201 SE_PetShield, which functions in a simalar manner with pet shielding owner.
Note: If either the shielder or the shield target die all variables are reset on both.
*/
if (app->size != sizeof(Shielding_Struct)) {
LogError("OP size error: OP_Shielding expected:[{}] got:[{}]", sizeof(Shielding_Struct), app->size);
return;
}
//TODO: Defensive makes it not cast?
//TODO: Bankers ect don't let you shjield 6826 You can not perform shielding while you are speaking with a banker, a merchant, or a guildmaster.
if (GetLevel() < 30) { //Client gives message
return;
}
@@ -12820,20 +12836,24 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
return;
}
pTimerType timer = pTimerShieldAbility;
if (!p_timers.Expired(&database, timer, false)) {
uint32 remain = p_timers.GetRemainingTime(timer);
Message(Chat::White, "You can use the ability /shield in %d minutes %d seconds.", ((remain) / 60), (remain % 60));
return;
}
Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer;
Mob* shield_target = entity_list.GetMob(shield->target_id);
Shout("PACKET Shielder %i", shield_target->GetShielderID());
Shout("PACKET Shield Target %i", GetShieldTargetID());
if (!shield_target) {
return;
}
if (shield_target->IsNPC()) {
//You must first target a living player //TODO Find string
MessageString(Chat::White, SHIELD_TARGET_NPC);
return;
}
@@ -12841,48 +12861,43 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
return;
}
//Does 'Shield Target' already have a 'Shielder'
if (shield_target->GetShielderID() == GetID()) {
//You are a 'Shield Target' already have a 'Shielder'
if (GetShielderID() || shield_target->GetShielderID()) {
MessageString(Chat::White, ALREADY_SHIELDED);
return;
}
//Does 'Shielder' already have a 'Shield Target'
if (GetShieldTargetID() == GetID()) {
MessageString(Chat::White, ALREADY_SHIELDED);
//You are being shielded or already have a 'Shield Target'
if (GetShieldTargetID() || shield_target->GetShieldTargetID()) {
MessageString(Chat::White, ALREADY_SHIELDING);
return;
}
//AA to increase SPA 230 extended shielding
int max_shielder_distance = 15;
int distance_mod = aabonuses.ExtendedShielding + itembonuses.ExtendedShielding + spellbonuses.ExtendedShielding;
max_shielder_distance += max_shielder_distance * distance_mod / 100;
max_shielder_distance = std::max(max_shielder_distance, 0);
if (shield_target->CalculateDistance(GetX(), GetY(), GetZ()) > static_cast<float>(max_shielder_distance)) {
//AA to increase SPA 230 extended shielding
int max_shlder_distance = 15;
max_shlder_distance += aabonuses.ExtendedShielding + itembonuses.ExtendedShielding + spellbonuses.ExtendedShielding;
max_shlder_distance = std::max(max_shlder_distance, 0);
if (shield_target->CalculateDistance(GetX(), GetY(), GetZ()) > static_cast<float>(max_shlder_distance)) {
return; //Too far away, no message is given thoughh.
}
entity_list.MessageCloseString(this, false, 100, 0, START_SHIELDING, GetName(), shield_target->GetName());
//Apply to Shielder
//shield_ability.shield_target_id = shield_target->GetID();
//Apply to Shield Target
//shield_target->shield_ability.shielder_id = GetID();
SetShieldTargetID(shield_target->GetID());
SetShielderMitigation(25);
SetShielerMaxDistance(max_shlder_distance);
shield_target->SetShielderID(GetID());
shield_target->SetShieldTargetMitigation(50);
//Calculate AA for adding time SPA 255 extend shield duration
int shield_duration = 12000;
Shout("1 Duration %i", shield_duration);
shield_duration += (aabonuses.ShieldDuration + itembonuses.ShieldDuration + spellbonuses.ShieldDuration) * 1000;
Shout("2 Duration %i", shield_duration);
shield_duration = std::max(shield_duration, 1); //Incase of negative modifiers lets just make min duration 1 ms.
Shout("3 Duration %i", shield_duration);
shield_timer.Start(shield_duration);
shield_timer.Start(static_cast<uint32>(shield_duration));
p_timers.Start(timer, SHIELD_ABILITY_RECAST_TIME);
return;
}
+1 -8
View File
@@ -461,14 +461,7 @@ bool Client::Process() {
}
if (shield_timer.Check()) {
Mob * shield_target = entity_list.GetMob(GetShieldTargetID());
if (shield_target) {
entity_list.MessageCloseString(this, false, 100, 0, END_SHIELDING, GetCleanName(), shield_target->GetCleanName());
}
shield_timer.Disable();
SetShieldTargetID(0);
shield_target->SetShielderID(0);
ShieldAbilityFinish();
}
SpellProcess();
+2 -1
View File
@@ -105,6 +105,8 @@
#define AURA_HARDCAP 2
#define SHIELD_ABILITY_RECAST_TIME 180
typedef enum { //focus types
focusSpellHaste = 1, //@Fc, SPA: 127, SE_IncreaseSpellHaste, On Caster, cast time mod pct, base: pct
focusSpellDuration, //@Fc, SPA: 128, SE_IncreaseSpellDuration, On Caster, spell duration mod pct, base: pct
@@ -544,7 +546,6 @@ struct StatBonuses {
int32 Pet_Crit_Melee_Damage_Pct_Owner; // base = percent mod for pet critcal damage from owner
int32 Pet_Add_Atk; // base = Pet ATK bonus from owner
// AAs
int32 ShieldDuration; // extends duration of /shield ability
int32 ExtendedShielding; // extends range of /shield ability
+101
View File
@@ -378,6 +378,9 @@ Mob::Mob(
shield_timer.Disable();
shield_target_id = 0;
shielder_id = 0;
shield_target_mitigation = 0;
shielder_mitigation = 0;
shielder_max_distance = 0;
destructibleobject = false;
wandertype = 0;
@@ -6118,6 +6121,104 @@ float Mob::GetDefaultRaceSize() const {
return GetRaceGenderDefaultHeight(race, gender);
}
void Mob::ShieldAbility(uint32 target_id, int max_shlder_distance, int shield_duration, int shld_target_mitigation, int shlder_mitigation)
{
Mob* shield_target = entity_list.GetMob(target_id);
if (!shield_target) {
return;
}
if (shield_target->GetID() == GetID()) {
return;
}
//You have a shielder, or your 'Shield Target' already has a 'Shielder'
if (GetShielderID() || shield_target->GetShielderID()) {
if (IsClient()) {
MessageString(Chat::White, ALREADY_SHIELDED);
}
return;
}
//You are being shielded or already have a 'Shield Target'
if (GetShieldTargetID() || shield_target->GetShieldTargetID()) {
if (IsClient()) {
MessageString(Chat::White, ALREADY_SHIELDING);
return;
}
}
if (shield_target->CalculateDistance(GetX(), GetY(), GetZ()) > static_cast<float>(max_shlder_distance)) {
if (IsClient()) {
MessageString(Chat::White, TARGET_TOO_FAR); //Live doesn't give any message for failure, for the quest ability lets allow it.
}
return;
}
entity_list.MessageCloseString(this, false, 100, 0, START_SHIELDING, GetCleanName(), shield_target->GetCleanName());
SetShieldTargetID(shield_target->GetID());
SetShielderMitigation(shlder_mitigation);
SetShielerMaxDistance(max_shlder_distance);
shield_target->SetShielderID(GetID());
shield_target->SetShieldTargetMitigation(shld_target_mitigation);
shield_timer.Start(shield_duration);
}
void Mob::ShieldAbilityFinish()
{
Mob* shield_target = entity_list.GetMob(GetShieldTargetID());
if (shield_target) {
entity_list.MessageCloseString(this, false, 100, 0, END_SHIELDING, GetCleanName(), shield_target->GetCleanName());
shield_target->SetShielderID(0);
shield_target->SetShieldTargetMitigation(0);
}
SetShieldTargetID(0);
SetShielderMitigation(0);
SetShielerMaxDistance(0);
shield_timer.Disable();
}
void Mob::ShieldAbilityClearVariables()
{
//If 'shield target' dies
if (GetShielderID()){
Mob* shielder = entity_list.GetMob(GetShielderID());
if (shielder) {
shielder->SetShieldTargetID(0);
shielder->SetShielderMitigation(0);
shielder->SetShielerMaxDistance(0);
shielder->shield_timer.Disable();
}
SetShielderID(0);
SetShieldTargetMitigation(0);
}
//If 'shielder' dies
if (GetShieldTargetID()) {
Mob* shield_target = entity_list.GetMob(GetShieldTargetID());
if (shield_target) {
shield_target->SetShielderID(0);
shield_target->SetShieldTargetMitigation(0);
}
SetShieldTargetID(0);
SetShielderMitigation(0);
SetShielerMaxDistance(0);
shield_timer.Disable();
}
}
#ifdef BOTS
bool Mob::JoinHealRotationTargetPool(std::shared_ptr<HealRotation>* heal_rotation)
{
+16 -4
View File
@@ -1125,11 +1125,20 @@ public:
Trade* trade;
void ShieldAbility(uint32 target_id, int max_shlder_distance = 15, int shield_duration = 12000, int shld_target_mitigation = 50, int shlder_mitigation = 75);
void DoShieldDamageOnShielder(Mob* shield_target, int hit_damage_done, EQ::skills::SkillType skillInUse);
inline int GetShielderID() const { return shielder_id; }
inline void SetShielderID(int ent_id) { shielder_id = ent_id; }
inline int GetShieldTargetID() const { return shield_target_id; }
inline void SetShieldTargetID(int ent_id) { shield_target_id = ent_id; }
void ShieldAbilityFinish();
void ShieldAbilityClearVariables();
inline uint32 GetShielderID() const { return shielder_id; }
inline void SetShielderID(uint32 val) { shielder_id = val; }
inline uint32 GetShieldTargetID() const { return shield_target_id; }
inline void SetShieldTargetID(uint32 val) { shield_target_id = val; }
inline int GetShieldTargetMitigation() const { return shield_target_mitigation; }
inline void SetShieldTargetMitigation(int val) { shield_target_mitigation = val; }
inline int GetShielderMitigation() const { return shielder_mitigation; }
inline void SetShielderMitigation(int val) { shielder_mitigation = val; }
inline int GetMaxShielderDistance() const { return shielder_max_distance; }
inline void SetShielerMaxDistance(int val) { shielder_max_distance = val; }
inline glm::vec4 GetCurrentWayPoint() const { return m_CurrentWayPoint; }
inline float GetCWPP() const { return(static_cast<float>(cur_wp_pause)); }
@@ -1433,6 +1442,9 @@ protected:
Timer shield_timer;
uint32 shield_target_id;
uint32 shielder_id;
int shield_target_mitigation;
int shielder_mitigation;
int shielder_max_distance;
//spell casting vars
Timer spellend_timer;
+4
View File
@@ -1128,6 +1128,10 @@ void Mob::AI_Process() {
if (focus_proc_limit_timer.Check())
FocusProcLimitProcess();
if (shield_timer.Check()) {
ShieldAbilityFinish();
}
auto npcSpawnPoint = CastToNPC()->GetSpawnPoint();
if (GetSpecialAbility(TETHER)) {
float tether_range = static_cast<float>(GetSpecialAbilityParam(TETHER, 0));
+45 -40
View File
@@ -4179,44 +4179,6 @@ XS(XS_Mob_GetResist) {
XSRETURN(1);
}
XS(XS_Mob_GetShieldTarget); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_GetShieldTarget) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Mob::GetShieldTarget(THIS)"); // @categories Script Utility
{
Mob *THIS;
Mob *RETVAL;
VALIDATE_THIS_IS_MOB;
//RETVAL = THIS->GetShieldTarget();
ST(0) = sv_newmortal();
sv_setref_pv(ST(0), "Mob", (void *) RETVAL);
}
XSRETURN(1);
}
XS(XS_Mob_SetShieldTarget); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetShieldTarget) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::SetShieldTarget(THIS, mob)"); // @categories Script Utility
{
Mob *THIS;
Mob *mob;
VALIDATE_THIS_IS_MOB;
if (sv_derived_from(ST(1), "Mob")) {
IV tmp = SvIV((SV *) SvRV(ST(1)));
mob = INT2PTR(Mob *, tmp);
} else
Perl_croak(aTHX_ "mob is not of type Mob");
if (mob == nullptr)
Perl_croak(aTHX_ "mob is nullptr, avoiding crash.");
//THIS->SetShieldTarget(mob);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_Charmed); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_Charmed) {
dXSARGS;
@@ -6301,6 +6263,50 @@ XS(XS_Mob_AddNimbusEffect) {
XSRETURN_EMPTY;
}
XS(XS_Mob_ShieldAbility); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_ShieldAbility) {
dXSARGS;
if (items < 2 || items > 6)
Perl_croak(aTHX_ "Usage: Mob::ShieldAbility(THIS, uint32 target_id, int32 max_shielder_distance, int32 shield_duration [ms]"); // @categories Spells and Disciplines
{
Mob *THIS;
uint32 target_id = (uint32)SvUV(ST(1));
int32 max_shlder_distance;
int32 shld_duration;
int32 shld_target_mitigation;
int32 shlder_mitigation;
VALIDATE_THIS_IS_MOB;
if (items < 3)
max_shlder_distance = 15;
else {
max_shlder_distance = max_shlder_distance = (int32)SvUV(ST(2));
}
if (items < 4)
shld_duration = 12000;
else {
shld_duration = (int32)SvUV(ST(3));
}
if (items < 5)
shld_target_mitigation = 50;
else {
shld_target_mitigation = (int32)SvUV(ST(4));
}
if (items < 6)
shlder_mitigation = 50;
else {
shlder_mitigation = (int32)SvUV(ST(5));
}
THIS->ShieldAbility(target_id, max_shlder_distance, shld_duration, shld_target_mitigation, shlder_mitigation);
}
XSRETURN_EMPTY;
}
#ifdef BOTS
XS(XS_Mob_CastToBot); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_CastToBot)
@@ -6561,8 +6567,6 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "DontRootMeBefore"), XS_Mob_DontRootMeBefore, file, "$");
newXSproto(strcpy(buf, "DontSnareMeBefore"), XS_Mob_DontSnareMeBefore, file, "$");
newXSproto(strcpy(buf, "GetResist"), XS_Mob_GetResist, file, "$$");
newXSproto(strcpy(buf, "GetShieldTarget"), XS_Mob_GetShieldTarget, file, "$");
newXSproto(strcpy(buf, "SetShieldTarget"), XS_Mob_SetShieldTarget, file, "$$");
newXSproto(strcpy(buf, "Charmed"), XS_Mob_Charmed, file, "$");
newXSproto(strcpy(buf, "GetLevelHP"), XS_Mob_GetLevelHP, file, "$$");
newXSproto(strcpy(buf, "GetZoneID"), XS_Mob_GetZoneID, file, "$");
@@ -6672,6 +6676,7 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "CanRaceEquipItem"), XS_Mob_CanRaceEquipItem, file, "$$");
newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$");
newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$");
newXSproto(strcpy(buf, "ShieldAbility"), XS_Mob_ShieldAbility, file, "$$$$$$");
#ifdef BOTS
newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$");
#endif
+14
View File
@@ -2950,6 +2950,20 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break;
}
case SE_PetShield: {
if (IsPet()) {
Mob* petowner = GetOwner();
if (petowner) {
int shield_duration = spells[spell_id].base[i] * 12 * 1000;
int shld_target_mitigation = spells[spell_id].base2[i] ? spells[spell_id].base2[i] : 50;
int shlder_mitigation = spells[spell_id].max[i] ? spells[spell_id].base2[i] : 50;
ShieldAbility(petowner->GetID(), 25, shield_duration, shld_target_mitigation, shlder_mitigation);
}
}
break;
}
case SE_PersistentEffect:
MakeAura(spell_id);
break;
+2
View File
@@ -287,7 +287,9 @@
#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
#define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets.
#define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first.
#define SHIELD_TARGET_NPC 3278 //You must first target a living Player Character.
#define ALREADY_SHIELDED 3279 //Either you or your target is already being shielded.
#define ALREADY_SHIELDING 3280 //Either you or your target is already shielding another.
#define START_SHIELDING 3281 //%1 begins to use %2 as a living shield!
#define END_SHIELDING 3282 //%1 ceases protecting %2.
#define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1.