mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 23:01:30 +00:00
[Spells] Rework of Virus Effect code (#1593)
* start of rework * functional * virus updates * Update npc.cpp * updates * updates * update v2 * pre remove old code * removed old code1 * remove debugs * description * Update spell_effects.cpp * changed function name * remove unused var * merge error fix * fix formating issue * Update spdat.cpp * Update spell_effects.cpp * Convert virus entity range code to use vectors and GetCloseMobList * Formatting [skip ci] Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
parent
1c5f9f2e0f
commit
060be606e7
@ -1572,3 +1572,26 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsVirusSpell(int32 spell_id)
|
||||||
|
{
|
||||||
|
if (GetViralMinSpreadTime(spell_id) && GetViralMaxSpreadTime(spell_id) && GetViralSpreadRange(spell_id)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 GetViralMinSpreadTime(int32 spell_id)
|
||||||
|
{
|
||||||
|
return spells[spell_id].viral_targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 GetViralMaxSpreadTime(int32 spell_id)
|
||||||
|
{
|
||||||
|
return spells[spell_id].viral_timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 GetViralSpreadRange(int32 spell_id)
|
||||||
|
{
|
||||||
|
return spells[spell_id].viral_range;
|
||||||
|
}
|
||||||
|
|||||||
@ -1342,8 +1342,8 @@ struct SPDat_Spell_Struct
|
|||||||
/* 188 */ //int npc_usefulness; // -- NPC_USEFULNESS
|
/* 188 */ //int npc_usefulness; // -- NPC_USEFULNESS
|
||||||
/* 189 */ int MinResist; // -- MIN_RESIST
|
/* 189 */ int MinResist; // -- MIN_RESIST
|
||||||
/* 190 */ int MaxResist; // -- MAX_RESIST
|
/* 190 */ int MaxResist; // -- MAX_RESIST
|
||||||
/* 191 */ uint8 viral_targets; // -- MIN_SPREAD_TIME
|
/* 191 */ int viral_targets; // -- MIN_SPREAD_TIME
|
||||||
/* 192 */ uint8 viral_timer; // -- MAX_SPREAD_TIME
|
/* 192 */ int viral_timer; // -- MAX_SPREAD_TIME
|
||||||
/* 193 */ int NimbusEffect; // -- DURATION_PARTICLE_EFFECT
|
/* 193 */ int NimbusEffect; // -- DURATION_PARTICLE_EFFECT
|
||||||
/* 194 */ float directional_start; //Cone Start Angle: -- CONE_START_ANGLE
|
/* 194 */ float directional_start; //Cone Start Angle: -- CONE_START_ANGLE
|
||||||
/* 195 */ float directional_end; // Cone End Angle: -- CONE_END_ANGLE
|
/* 195 */ float directional_end; // Cone End Angle: -- CONE_END_ANGLE
|
||||||
@ -1509,6 +1509,10 @@ bool IsCastWhileInvis(uint16 spell_id);
|
|||||||
bool IsEffectIgnoredInStacking(int spa);
|
bool IsEffectIgnoredInStacking(int spa);
|
||||||
bool IsFocusLimit(int spa);
|
bool IsFocusLimit(int spa);
|
||||||
bool SpellRequiresTarget(int targettype);
|
bool SpellRequiresTarget(int targettype);
|
||||||
|
bool IsVirusSpell(int32 spell_id);
|
||||||
|
int GetViralMinSpreadTime(int32 spell_id);
|
||||||
|
int GetViralMaxSpreadTime(int32 spell_id);
|
||||||
|
int GetViralSpreadRange(int32 spell_id);
|
||||||
bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect);
|
bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect);
|
||||||
|
|
||||||
int CalcPetHp(int levelb, int classb, int STA = 75);
|
int CalcPetHp(int levelb, int classb, int STA = 75);
|
||||||
|
|||||||
@ -478,7 +478,6 @@ Json::Value ApiGetMobListDetail(EQ::Net::WebsocketServerConnection *connection,
|
|||||||
row["has_temp_pets_active"] = mob->HasTempPetsActive();
|
row["has_temp_pets_active"] = mob->HasTempPetsActive();
|
||||||
row["has_two_hand_blunt_equiped"] = mob->HasTwoHandBluntEquiped();
|
row["has_two_hand_blunt_equiped"] = mob->HasTwoHandBluntEquiped();
|
||||||
row["has_two_hander_equipped"] = mob->HasTwoHanderEquipped();
|
row["has_two_hander_equipped"] = mob->HasTwoHanderEquipped();
|
||||||
row["has_virus"] = mob->HasVirus();
|
|
||||||
row["hate_summon"] = mob->HateSummon();
|
row["hate_summon"] = mob->HateSummon();
|
||||||
row["helm_texture"] = mob->GetHelmTexture();
|
row["helm_texture"] = mob->GetHelmTexture();
|
||||||
row["hp"] = mob->GetHP();
|
row["hp"] = mob->GetHP();
|
||||||
|
|||||||
@ -444,19 +444,8 @@ bool Client::Process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasVirus()) {
|
if (viral_timer.Check() && !dead) {
|
||||||
if (viral_timer.Check()) {
|
VirusEffectProcess();
|
||||||
viral_timer_counter++;
|
|
||||||
for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i += 2) {
|
|
||||||
if (viral_spells[i]) {
|
|
||||||
if (viral_timer_counter % spells[viral_spells[i]].viral_timer == 0) {
|
|
||||||
SpreadVirus(viral_spells[i], viral_spells[i + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (viral_timer_counter > 999)
|
|
||||||
viral_timer_counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectileAttack();
|
ProjectileAttack();
|
||||||
|
|||||||
@ -330,6 +330,7 @@ struct Buffs_Struct {
|
|||||||
uint32 instrument_mod;
|
uint32 instrument_mod;
|
||||||
int16 focusproclimit_time; //timer to limit number of procs from focus effects
|
int16 focusproclimit_time; //timer to limit number of procs from focus effects
|
||||||
int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set
|
int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set
|
||||||
|
int32 virus_spread_time; //time till next attempted viral spread
|
||||||
bool persistant_buff;
|
bool persistant_buff;
|
||||||
bool client; //True if the caster is a client
|
bool client; //True if the caster is a client
|
||||||
bool UpdateClient;
|
bool UpdateClient;
|
||||||
|
|||||||
121
zone/entity.cpp
121
zone/entity.cpp
@ -5222,59 +5222,94 @@ Client *EntityList::FindCorpseDragger(uint16 CorpseID)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mob *EntityList::GetTargetForVirus(Mob *spreader, int range)
|
std::vector<Mob*> EntityList::GetTargetsForVirusEffect(Mob *spreader, Mob *original_caster, int range, int pcnpc, int32 spell_id)
|
||||||
{
|
{
|
||||||
int max_spread_range = RuleI(Spells, VirusSpreadDistance);
|
/*
|
||||||
|
Live Mechanics
|
||||||
if (range)
|
Virus spreader does NOT need LOS
|
||||||
max_spread_range = range;
|
There is no max target limit
|
||||||
|
*/
|
||||||
std::vector<Mob *> TargetsInRange;
|
if (!spreader) {
|
||||||
|
return {};
|
||||||
auto it = mob_list.begin();
|
|
||||||
while (it != mob_list.end()) {
|
|
||||||
Mob *cur = it->second;
|
|
||||||
// Make sure the target is in range, has los and is not the mob doing the spreading
|
|
||||||
if ((cur->GetID() != spreader->GetID()) &&
|
|
||||||
(cur->CalculateDistance(spreader->GetX(), spreader->GetY(),
|
|
||||||
spreader->GetZ()) <= max_spread_range) &&
|
|
||||||
(spreader->CheckLosFN(cur))) {
|
|
||||||
// If the spreader is an npc it can only spread to other npc controlled mobs
|
|
||||||
if (spreader->IsNPC() && !spreader->IsPet() && !spreader->CastToNPC()->GetSwarmOwner() && cur->IsNPC()) {
|
|
||||||
TargetsInRange.push_back(cur);
|
|
||||||
}
|
}
|
||||||
// If the spreader is an npc controlled pet it can spread to any other npc or an npc controlled pet
|
|
||||||
else if (spreader->IsNPC() && spreader->IsPet() && spreader->GetOwner()->IsNPC()) {
|
std::vector<Mob *> spreader_list = {};
|
||||||
if (cur->IsNPC() && !cur->IsPet()) {
|
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
|
||||||
TargetsInRange.push_back(cur);
|
for (auto &it : entity_list.GetCloseMobList(spreader, range)) {
|
||||||
} else if (cur->IsNPC() && cur->IsPet() && cur->GetOwner()->IsNPC()) {
|
Mob *mob = it.second;
|
||||||
TargetsInRange.push_back(cur);
|
if (mob == spreader) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if (cur->IsNPC() && cur->CastToNPC()->GetSwarmOwner() && cur->GetOwner()->IsNPC()) {
|
|
||||||
TargetsInRange.push_back(cur);
|
// check PC/NPC only flag 1 = PCs, 2 = NPCs
|
||||||
|
if (pcnpc == 1 && !mob->IsClient() && !mob->IsMerc() && !mob->IsBot()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (pcnpc == 2 && (mob->IsClient() || mob->IsMerc() || mob->IsBot())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mob->IsClient() && !mob->CastToClient()->ClientFinishedLoading()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mob->IsAura() || mob->IsTrap()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the target is in range
|
||||||
|
if (mob->CalculateDistance(spreader->GetX(), spreader->GetY(), spreader->GetZ()) <= range) {
|
||||||
|
|
||||||
|
//Do not allow detrimental spread to anything the original caster couldn't normally attack.
|
||||||
|
if (is_detrimental_spell && !original_caster->IsAttackAllowed(mob, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For non-NPCs, do not allow beneficial spread to anything the original caster could normally attack.
|
||||||
|
if (!is_detrimental_spell && !original_caster->IsNPC() && original_caster->IsAttackAllowed(mob, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the spreader is an npc and NOT a PET, then spread to other npc controlled mobs that are not pets
|
||||||
|
if (spreader->IsNPC() && !spreader->IsPet() && !spreader->IsTempPet() && mob->IsNPC() && !mob->IsPet() && !mob->IsTempPet()) {
|
||||||
|
spreader_list.push_back(mob);
|
||||||
|
}
|
||||||
|
// If the spreader is an npc and NOT a PET, then spread to npc controlled pet
|
||||||
|
else if (spreader->IsNPC() && !spreader->IsPet() && !spreader->IsTempPet() && mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && mob->IsPetOwnerNPC()) {
|
||||||
|
spreader_list.push_back(mob);
|
||||||
|
}
|
||||||
|
// If the spreader is an npc controlled PET it can spread to any other npc or an npc controlled pet
|
||||||
|
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->IsTempPet()) && spreader->IsPetOwnerNPC()) {
|
||||||
|
if (mob->IsNPC() && (!mob->IsPet() || !mob->IsTempPet())) {
|
||||||
|
spreader_list.push_back(mob);
|
||||||
|
}
|
||||||
|
else if (mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && mob->IsPetOwnerNPC()) {
|
||||||
|
spreader_list.push_back(mob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if the spreader is anything else(bot, pet, etc) then it should spread to everything but non client controlled npcs
|
// if the spreader is anything else(bot, pet, etc) then it should spread to everything but non client controlled npcs
|
||||||
else if (!spreader->IsNPC() && !cur->IsNPC()) {
|
else if (!spreader->IsNPC() && !mob->IsNPC()) {
|
||||||
TargetsInRange.push_back(cur);
|
spreader_list.push_back(mob);
|
||||||
}
|
}
|
||||||
// if its a pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc)
|
// if spreader is not an NPC, and Target is an NPC, then spread to non-NPC controlled pets
|
||||||
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->CastToNPC()->GetSwarmOwner()) && !spreader->GetOwner()->IsNPC()) {
|
else if (!spreader->IsNPC() && mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && !mob->IsPetOwnerNPC()) {
|
||||||
if (!cur->IsNPC()) {
|
spreader_list.push_back(mob);
|
||||||
TargetsInRange.push_back(cur);
|
|
||||||
}
|
|
||||||
else if (cur->IsNPC() && (cur->IsPet() || cur->CastToNPC()->GetSwarmOwner()) && !cur->GetOwner()->IsNPC()) {
|
|
||||||
TargetsInRange.push_back(cur);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(TargetsInRange.empty())
|
// if spreader is a non-NPC controlled pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc)
|
||||||
return nullptr;
|
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->IsTempPet()) && !spreader->IsPetOwnerNPC()) {
|
||||||
|
//Spread to non-NPCs
|
||||||
|
if (!mob->IsNPC()) {
|
||||||
|
spreader_list.push_back(mob);
|
||||||
|
}
|
||||||
|
//Spread to other non-NPC Pets
|
||||||
|
else if (mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && !mob->IsPetOwnerNPC()) {
|
||||||
|
spreader_list.push_back(mob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return TargetsInRange[zone->random.Int(0, TargetsInRange.size() - 1)];
|
return spreader_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityList::StopMobAI()
|
void EntityList::StopMobAI()
|
||||||
|
|||||||
@ -160,7 +160,6 @@ public:
|
|||||||
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
|
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
|
||||||
bool IsNPCSpawned(std::vector<uint32> npc_ids);
|
bool IsNPCSpawned(std::vector<uint32> npc_ids);
|
||||||
uint32 CountSpawnedNPCs(std::vector<uint32> npc_ids);
|
uint32 CountSpawnedNPCs(std::vector<uint32> npc_ids);
|
||||||
Mob *GetTargetForVirus(Mob* spreader, int range);
|
|
||||||
inline NPC *GetNPCByID(uint16 id)
|
inline NPC *GetNPCByID(uint16 id)
|
||||||
{
|
{
|
||||||
auto it = npc_list.find(id);
|
auto it = npc_list.find(id);
|
||||||
@ -512,6 +511,7 @@ public:
|
|||||||
void GetDoorsList(std::list<Doors*> &d_list);
|
void GetDoorsList(std::list<Doors*> &d_list);
|
||||||
void GetSpawnList(std::list<Spawn2*> &d_list);
|
void GetSpawnList(std::list<Spawn2*> &d_list);
|
||||||
void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list<Mob*> &m_list);
|
void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list<Mob*> &m_list);
|
||||||
|
std::vector<Mob*> GetTargetsForVirusEffect(Mob *spreader, Mob *orginal_caster, int range, int pcnpc, int32 spell_id);
|
||||||
|
|
||||||
inline const std::unordered_map<uint16, Mob *> &GetMobList() { return mob_list; }
|
inline const std::unordered_map<uint16, Mob *> &GetMobList() { return mob_list; }
|
||||||
inline const std::unordered_map<uint16, NPC *> &GetNPCList() { return npc_list; }
|
inline const std::unordered_map<uint16, NPC *> &GetNPCList() { return npc_list; }
|
||||||
|
|||||||
27
zone/mob.cpp
27
zone/mob.cpp
@ -367,6 +367,7 @@ Mob::Mob(
|
|||||||
pet_regroup = false;
|
pet_regroup = false;
|
||||||
_IsTempPet = false;
|
_IsTempPet = false;
|
||||||
pet_owner_client = false;
|
pet_owner_client = false;
|
||||||
|
pet_owner_npc = false;
|
||||||
pet_targetlock_id = 0;
|
pet_targetlock_id = 0;
|
||||||
|
|
||||||
attacked_count = 0;
|
attacked_count = 0;
|
||||||
@ -405,10 +406,6 @@ Mob::Mob(
|
|||||||
roamer = false;
|
roamer = false;
|
||||||
rooted = false;
|
rooted = false;
|
||||||
charmed = false;
|
charmed = false;
|
||||||
has_virus = false;
|
|
||||||
for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i++) {
|
|
||||||
viral_spells[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
weaponstance.enabled = false;
|
weaponstance.enabled = false;
|
||||||
weaponstance.spellbonus_enabled = false; //Set when bonus is applied
|
weaponstance.spellbonus_enabled = false; //Set when bonus is applied
|
||||||
@ -4682,28 +4679,6 @@ void Mob::DoGravityEffect()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mob::SpreadVirus(uint16 spell_id, uint16 casterID)
|
|
||||||
{
|
|
||||||
int num_targs = spells[spell_id].viral_targets;
|
|
||||||
|
|
||||||
Mob* caster = entity_list.GetMob(casterID);
|
|
||||||
Mob* target = nullptr;
|
|
||||||
// Only spread in zones without perm buffs
|
|
||||||
if(!zone->BuffTimersSuspended()) {
|
|
||||||
for(int i = 0; i < num_targs; i++) {
|
|
||||||
target = entity_list.GetTargetForVirus(this, spells[spell_id].viral_range);
|
|
||||||
if(target) {
|
|
||||||
// Only spreads to the uninfected
|
|
||||||
if(!target->FindBuff(spell_id)) {
|
|
||||||
if(caster)
|
|
||||||
caster->SpellOnTarget(spell_id, target);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mob::AddNimbusEffect(int effectid)
|
void Mob::AddNimbusEffect(int effectid)
|
||||||
{
|
{
|
||||||
SetNimbusEffect(effectid);
|
SetNimbusEffect(effectid);
|
||||||
|
|||||||
14
zone/mob.h
14
zone/mob.h
@ -333,8 +333,8 @@ public:
|
|||||||
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);
|
||||||
void SendBeginCast(uint16 spell_id, uint32 casttime);
|
void SendBeginCast(uint16 spell_id, uint32 casttime);
|
||||||
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0,
|
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0,
|
||||||
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, int32 duration_override = 0);
|
||||||
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0);
|
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0, int32 duration_override = 0);
|
||||||
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, EQ::spells::CastingSlot slot, bool isproc = false);
|
CastAction_type &CastAction, EQ::spells::CastingSlot slot, bool isproc = false);
|
||||||
virtual bool CheckFizzle(uint16 spell_id);
|
virtual bool CheckFizzle(uint16 spell_id);
|
||||||
@ -407,7 +407,6 @@ public:
|
|||||||
inline void SetMGB(bool val) { has_MGB = val; }
|
inline void SetMGB(bool val) { has_MGB = val; }
|
||||||
bool HasProjectIllusion() const { return has_ProjectIllusion ; }
|
bool HasProjectIllusion() const { return has_ProjectIllusion ; }
|
||||||
inline void SetProjectIllusion(bool val) { has_ProjectIllusion = val; }
|
inline void SetProjectIllusion(bool val) { has_ProjectIllusion = val; }
|
||||||
void SpreadVirus(uint16 spell_id, uint16 casterID);
|
|
||||||
bool IsNimbusEffectActive(uint32 nimbus_effect);
|
bool IsNimbusEffectActive(uint32 nimbus_effect);
|
||||||
void SetNimbusEffect(uint32 nimbus_effect);
|
void SetNimbusEffect(uint32 nimbus_effect);
|
||||||
inline virtual uint32 GetNimbusEffect1() const { return nimbus_effect1; }
|
inline virtual uint32 GetNimbusEffect1() const { return nimbus_effect1; }
|
||||||
@ -861,6 +860,9 @@ public:
|
|||||||
void FocusProcLimitProcess();
|
void FocusProcLimitProcess();
|
||||||
bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1);
|
bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1);
|
||||||
|
|
||||||
|
void VirusEffectProcess();
|
||||||
|
void SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining);
|
||||||
|
|
||||||
void ModSkillDmgTaken(EQ::skills::SkillType skill_num, int value);
|
void ModSkillDmgTaken(EQ::skills::SkillType skill_num, int value);
|
||||||
int16 GetModSkillDmgTaken(const EQ::skills::SkillType skill_num);
|
int16 GetModSkillDmgTaken(const EQ::skills::SkillType skill_num);
|
||||||
void ModVulnerability(uint8 resist, int16 value);
|
void ModVulnerability(uint8 resist, int16 value);
|
||||||
@ -929,6 +931,8 @@ public:
|
|||||||
bool HasPetAffinity() { if (aabonuses.GivePetGroupTarget || itembonuses.GivePetGroupTarget || spellbonuses.GivePetGroupTarget) return true; return false; }
|
bool HasPetAffinity() { if (aabonuses.GivePetGroupTarget || itembonuses.GivePetGroupTarget || spellbonuses.GivePetGroupTarget) return true; return false; }
|
||||||
inline bool IsPetOwnerClient() const { return pet_owner_client; }
|
inline bool IsPetOwnerClient() const { return pet_owner_client; }
|
||||||
inline void SetPetOwnerClient(bool value) { pet_owner_client = value; }
|
inline void SetPetOwnerClient(bool value) { pet_owner_client = value; }
|
||||||
|
inline bool IsPetOwnerNPC() const { return pet_owner_npc; }
|
||||||
|
inline void SetPetOwnerNPC(bool value) { pet_owner_npc = value; }
|
||||||
inline bool IsTempPet() const { return _IsTempPet; }
|
inline bool IsTempPet() const { return _IsTempPet; }
|
||||||
inline void SetTempPet(bool value) { _IsTempPet = value; }
|
inline void SetTempPet(bool value) { _IsTempPet = value; }
|
||||||
inline bool IsHorse() { return is_horse; }
|
inline bool IsHorse() { return is_horse; }
|
||||||
@ -1038,7 +1042,6 @@ public:
|
|||||||
inline const bool IsRoamer() const { return roamer; }
|
inline const bool IsRoamer() const { return roamer; }
|
||||||
inline const int GetWanderType() const { return wandertype; }
|
inline const int GetWanderType() const { return wandertype; }
|
||||||
inline const bool IsRooted() const { return rooted || permarooted; }
|
inline const bool IsRooted() const { return rooted || permarooted; }
|
||||||
inline const bool HasVirus() const { return has_virus; }
|
|
||||||
int GetSnaredAmount();
|
int GetSnaredAmount();
|
||||||
inline const bool IsPseudoRooted() const { return pseudo_rooted; }
|
inline const bool IsPseudoRooted() const { return pseudo_rooted; }
|
||||||
inline void SetPseudoRoot(bool prState) { pseudo_rooted = prState; }
|
inline void SetPseudoRoot(bool prState) { pseudo_rooted = prState; }
|
||||||
@ -1522,8 +1525,6 @@ protected:
|
|||||||
bool silenced;
|
bool silenced;
|
||||||
bool amnesiad;
|
bool amnesiad;
|
||||||
bool inWater; // Set to true or false by Water Detection code if enabled by rules
|
bool inWater; // Set to true or false by Water Detection code if enabled by rules
|
||||||
bool has_virus; // whether this mob has a viral spell on them
|
|
||||||
uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids
|
|
||||||
bool offhand;
|
bool offhand;
|
||||||
bool has_shieldequiped;
|
bool has_shieldequiped;
|
||||||
bool has_twohandbluntequiped;
|
bool has_twohandbluntequiped;
|
||||||
@ -1636,6 +1637,7 @@ protected:
|
|||||||
bool _IsTempPet;
|
bool _IsTempPet;
|
||||||
int16 count_TempPet;
|
int16 count_TempPet;
|
||||||
bool pet_owner_client; //Flags regular and pets as belonging to a client
|
bool pet_owner_client; //Flags regular and pets as belonging to a client
|
||||||
|
bool pet_owner_npc; //Flags regular and pets as belonging to a npc
|
||||||
uint32 pet_targetlock_id;
|
uint32 pet_targetlock_id;
|
||||||
|
|
||||||
glm::vec3 m_TargetRing;
|
glm::vec3 m_TargetRing;
|
||||||
|
|||||||
26
zone/npc.cpp
26
zone/npc.cpp
@ -928,19 +928,8 @@ bool NPC::Process()
|
|||||||
SendHPUpdate();
|
SendHPUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(HasVirus()) {
|
if (viral_timer.Check()) {
|
||||||
if(viral_timer.Check()) {
|
VirusEffectProcess();
|
||||||
viral_timer_counter++;
|
|
||||||
for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) {
|
|
||||||
if(viral_spells[i] && spells[viral_spells[i]].viral_timer > 0) {
|
|
||||||
if(viral_timer_counter % spells[viral_spells[i]].viral_timer == 0) {
|
|
||||||
SpreadVirus(viral_spells[i], viral_spells[i+1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(viral_timer_counter > 999)
|
|
||||||
viral_timer_counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(spellbonuses.GravityEffect == 1) {
|
if(spellbonuses.GravityEffect == 1) {
|
||||||
@ -2339,6 +2328,10 @@ void NPC::PetOnSpawn(NewSpawn_Struct* ns)
|
|||||||
strn0cpy(ns->spawn.lastName, tmp_lastname.c_str(), sizeof(ns->spawn.lastName));
|
strn0cpy(ns->spawn.lastName, tmp_lastname.c_str(), sizeof(ns->spawn.lastName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (swarmOwner->IsNPC()) {
|
||||||
|
SetPetOwnerNPC(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(GetOwnerID())
|
else if(GetOwnerID())
|
||||||
{
|
{
|
||||||
@ -2354,6 +2347,13 @@ void NPC::PetOnSpawn(NewSpawn_Struct* ns)
|
|||||||
if (tmp_lastname.size() < sizeof(ns->spawn.lastName))
|
if (tmp_lastname.size() < sizeof(ns->spawn.lastName))
|
||||||
strn0cpy(ns->spawn.lastName, tmp_lastname.c_str(), sizeof(ns->spawn.lastName));
|
strn0cpy(ns->spawn.lastName, tmp_lastname.c_str(), sizeof(ns->spawn.lastName));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (entity_list.GetNPCByID(GetOwnerID()))
|
||||||
|
{
|
||||||
|
SetPetOwnerNPC(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@ -46,7 +46,7 @@ extern WorldServer worldserver;
|
|||||||
|
|
||||||
// the spell can still fail here, if the buff can't stack
|
// the spell can still fail here, if the buff can't stack
|
||||||
// in this case false will be returned, true otherwise
|
// in this case false will be returned, true otherwise
|
||||||
bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness)
|
bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness, int32 duration_override)
|
||||||
{
|
{
|
||||||
int caster_level, buffslot, effect, effect_value, i;
|
int caster_level, buffslot, effect, effect_value, i;
|
||||||
EQ::ItemInstance *SummonedItem=nullptr;
|
EQ::ItemInstance *SummonedItem=nullptr;
|
||||||
@ -119,7 +119,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffslot = AddBuff(caster, spell_id);
|
buffslot = AddBuff(caster, spell_id, duration_override);
|
||||||
}
|
}
|
||||||
if(buffslot == -1) // stacking failure
|
if(buffslot == -1) // stacking failure
|
||||||
return false;
|
return false;
|
||||||
@ -181,20 +181,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(spells[spell_id].viral_targets > 0) {
|
if(IsVirusSpell(spell_id)) {
|
||||||
if(!viral_timer.Enabled())
|
|
||||||
viral_timer.Start(1000);
|
|
||||||
|
|
||||||
has_virus = true;
|
if (!viral_timer.Enabled()) {
|
||||||
for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2)
|
viral_timer.Start(1000);
|
||||||
{
|
|
||||||
if(!viral_spells[i])
|
|
||||||
{
|
|
||||||
viral_spells[i] = spell_id;
|
|
||||||
viral_spells[i+1] = caster->GetID();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
buffs[buffslot].virus_spread_time = zone->random.Int(GetViralMinSpreadTime(spell_id), GetViralMaxSpreadTime(spell_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4086,23 +4078,6 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
|
|||||||
|
|
||||||
LogSpells("Fading buff [{}] from slot [{}]", buffs[slot].spellid, slot);
|
LogSpells("Fading buff [{}] from slot [{}]", buffs[slot].spellid, slot);
|
||||||
|
|
||||||
if(spells[buffs[slot].spellid].viral_targets > 0) {
|
|
||||||
bool last_virus = true;
|
|
||||||
for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2)
|
|
||||||
{
|
|
||||||
if(viral_spells[i] && viral_spells[i] != buffs[slot].spellid)
|
|
||||||
{
|
|
||||||
// If we have a virus that doesn't match this one then don't stop the viral timer
|
|
||||||
last_virus = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This is the last virus on us so lets stop timer
|
|
||||||
if(last_virus) {
|
|
||||||
viral_timer.Disable();
|
|
||||||
has_virus = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string buf = fmt::format(
|
std::string buf = fmt::format(
|
||||||
"{} {} {} {}",
|
"{} {} {} {}",
|
||||||
buffs[slot].casterid,
|
buffs[slot].casterid,
|
||||||
@ -8614,3 +8589,76 @@ int Mob::GetMemoryBlurChance(int base_chance)
|
|||||||
chance += chance * chance_mod / 100;
|
chance += chance * chance_mod / 100;
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mob::VirusEffectProcess()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Virus Mechanics
|
||||||
|
To qualify as a virus effect buff, all of the following spell table need to be set. (At some point will correct names)
|
||||||
|
viral_targets = MIN_SPREAD_TIME
|
||||||
|
viral_timer = MAX_SPREAD_TIME
|
||||||
|
viral_range = SPREAD_RADIUS
|
||||||
|
Once a buff with a viral effect is applied, a 1000 ms timer will begin.
|
||||||
|
The time at which the virus will attempt to spread is determined by a random value between MIN_SPREAD_TIME and MAX_SPREAD_TIME
|
||||||
|
Each time the virus attempts to spread the next time interval will be chosen at random again.
|
||||||
|
If a spreader finds a target for viral buff, when the viral buff spreads the duration on the new target will be the time remaining on the spreaders buff.
|
||||||
|
Spreaders DOES NOT need LOS to spread. There is no max amount of targets the virus can spread to.
|
||||||
|
When the spreader no longer has any viral buffs the timer stops.
|
||||||
|
The current code supports spreading for both detrimental and beneficial spells.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Only spread in zones without perm buffs
|
||||||
|
if (zone->BuffTimersSuspended()) {
|
||||||
|
viral_timer.Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stop_timer = true;
|
||||||
|
for (int buffs_i = 0; buffs_i < GetMaxTotalSlots(); ++buffs_i)
|
||||||
|
{
|
||||||
|
if (IsValidSpell(buffs[buffs_i].spellid) && IsVirusSpell(buffs[buffs_i].spellid))
|
||||||
|
{
|
||||||
|
if (buffs[buffs_i].virus_spread_time > 0) {
|
||||||
|
buffs[buffs_i].virus_spread_time -= 1;
|
||||||
|
stop_timer = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffs[buffs_i].virus_spread_time <= 0) {
|
||||||
|
buffs[buffs_i].virus_spread_time = zone->random.Int(GetViralMinSpreadTime(buffs[buffs_i].spellid), GetViralMaxSpreadTime(buffs[buffs_i].spellid));
|
||||||
|
SpreadVirusEffect(buffs[buffs_i].spellid, buffs[buffs_i].casterid, buffs[buffs_i].ticsremaining);
|
||||||
|
stop_timer = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stop_timer) {
|
||||||
|
viral_timer.Disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mob::SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining)
|
||||||
|
{
|
||||||
|
Mob *caster = entity_list.GetMob(caster_id);
|
||||||
|
std::vector<Mob *> targets_in_range = entity_list.GetTargetsForVirusEffect(
|
||||||
|
this,
|
||||||
|
caster,
|
||||||
|
GetViralSpreadRange(spell_id),
|
||||||
|
spells[spell_id].pcnpc_only_flag,
|
||||||
|
spell_id
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto &mob: targets_in_range) {
|
||||||
|
if (!mob) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mob->FindBuff(spell_id)) {
|
||||||
|
if (caster) {
|
||||||
|
if (buff_tics_remaining) {
|
||||||
|
//When virus is spread, the buff on new target is applied with the amount of time remaining on the spreaders buff.
|
||||||
|
caster->SpellOnTarget(spell_id, mob, 0, false, 0, false, -1, buff_tics_remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -3423,6 +3423,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
|||||||
buffs[emptyslot].RootBreakChance = 0;
|
buffs[emptyslot].RootBreakChance = 0;
|
||||||
buffs[emptyslot].focusproclimit_time = 0;
|
buffs[emptyslot].focusproclimit_time = 0;
|
||||||
buffs[emptyslot].focusproclimit_procamt = 0;
|
buffs[emptyslot].focusproclimit_procamt = 0;
|
||||||
|
buffs[emptyslot].virus_spread_time = 0;
|
||||||
buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10;
|
buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10;
|
||||||
|
|
||||||
if (level_override > 0 || buffs[emptyslot].numhits > 0) {
|
if (level_override > 0 || buffs[emptyslot].numhits > 0) {
|
||||||
@ -3533,9 +3534,8 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite)
|
|||||||
// break stuff
|
// break stuff
|
||||||
//
|
//
|
||||||
bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectiveness, bool use_resist_adjust, int16 resist_adjust,
|
bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectiveness, bool use_resist_adjust, int16 resist_adjust,
|
||||||
bool isproc, int level_override)
|
bool isproc, int level_override, int32 duration_override)
|
||||||
{
|
{
|
||||||
|
|
||||||
bool is_damage_or_lifetap_spell = IsDamageSpell(spell_id) || IsLifetapSpell(spell_id);
|
bool is_damage_or_lifetap_spell = IsDamageSpell(spell_id) || IsLifetapSpell(spell_id);
|
||||||
|
|
||||||
// well we can't cast a spell on target without a target
|
// well we can't cast a spell on target without a target
|
||||||
@ -4078,7 +4078,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// cause the effects to the target
|
// cause the effects to the target
|
||||||
if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override, reflect_effectiveness))
|
if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override, reflect_effectiveness, duration_override))
|
||||||
{
|
{
|
||||||
// if SpellEffect returned false there's a problem applying the
|
// if SpellEffect returned false there's a problem applying the
|
||||||
// spell. It's most likely a buff that can't stack.
|
// spell. It's most likely a buff that can't stack.
|
||||||
|
|||||||
@ -3672,6 +3672,7 @@ void ZoneDatabase::LoadBuffs(Client *client)
|
|||||||
buffs[slot_id].RootBreakChance = 0;
|
buffs[slot_id].RootBreakChance = 0;
|
||||||
buffs[slot_id].focusproclimit_time = 0;
|
buffs[slot_id].focusproclimit_time = 0;
|
||||||
buffs[slot_id].focusproclimit_procamt = 0;
|
buffs[slot_id].focusproclimit_procamt = 0;
|
||||||
|
buffs[slot_id].virus_spread_time = 0;
|
||||||
buffs[slot_id].UpdateClient = false;
|
buffs[slot_id].UpdateClient = false;
|
||||||
buffs[slot_id].instrument_mod = instrument_mod;
|
buffs[slot_id].instrument_mod = instrument_mod;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user