mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +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;
|
||||
}
|
||||
|
||||
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
|
||||
/* 189 */ int MinResist; // -- MIN_RESIST
|
||||
/* 190 */ int MaxResist; // -- MAX_RESIST
|
||||
/* 191 */ uint8 viral_targets; // -- MIN_SPREAD_TIME
|
||||
/* 192 */ uint8 viral_timer; // -- MAX_SPREAD_TIME
|
||||
/* 191 */ int viral_targets; // -- MIN_SPREAD_TIME
|
||||
/* 192 */ int viral_timer; // -- MAX_SPREAD_TIME
|
||||
/* 193 */ int NimbusEffect; // -- DURATION_PARTICLE_EFFECT
|
||||
/* 194 */ float directional_start; //Cone Start Angle: -- CONE_START_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 IsFocusLimit(int spa);
|
||||
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);
|
||||
|
||||
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_two_hand_blunt_equiped"] = mob->HasTwoHandBluntEquiped();
|
||||
row["has_two_hander_equipped"] = mob->HasTwoHanderEquipped();
|
||||
row["has_virus"] = mob->HasVirus();
|
||||
row["hate_summon"] = mob->HateSummon();
|
||||
row["helm_texture"] = mob->GetHelmTexture();
|
||||
row["hp"] = mob->GetHP();
|
||||
|
||||
@ -444,19 +444,8 @@ bool Client::Process() {
|
||||
}
|
||||
}
|
||||
|
||||
if (HasVirus()) {
|
||||
if (viral_timer.Check()) {
|
||||
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;
|
||||
if (viral_timer.Check() && !dead) {
|
||||
VirusEffectProcess();
|
||||
}
|
||||
|
||||
ProjectileAttack();
|
||||
|
||||
@ -330,6 +330,7 @@ struct Buffs_Struct {
|
||||
uint32 instrument_mod;
|
||||
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
|
||||
int32 virus_spread_time; //time till next attempted viral spread
|
||||
bool persistant_buff;
|
||||
bool client; //True if the caster is a client
|
||||
bool UpdateClient;
|
||||
|
||||
111
zone/entity.cpp
111
zone/entity.cpp
@ -1219,7 +1219,7 @@ uint32 EntityList::CountSpawnedNPCs(std::vector<uint32> npc_ids)
|
||||
npc_ids.begin(),
|
||||
npc_ids.end(),
|
||||
current_npc.second->GetNPCTypeID()
|
||||
) != npc_ids.end() &&
|
||||
) != npc_ids.end() &&
|
||||
current_npc.second->GetID() != 0
|
||||
) {
|
||||
npc_count++;
|
||||
@ -5222,59 +5222,94 @@ Client *EntityList::FindCorpseDragger(uint16 CorpseID)
|
||||
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
|
||||
Virus spreader does NOT need LOS
|
||||
There is no max target limit
|
||||
*/
|
||||
if (!spreader) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (range)
|
||||
max_spread_range = range;
|
||||
std::vector<Mob *> spreader_list = {};
|
||||
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
|
||||
for (auto &it : entity_list.GetCloseMobList(spreader, range)) {
|
||||
Mob *mob = it.second;
|
||||
if (mob == spreader) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<Mob *> TargetsInRange;
|
||||
// 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;
|
||||
}
|
||||
|
||||
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 (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;
|
||||
}
|
||||
// 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()) {
|
||||
if (cur->IsNPC() && !cur->IsPet()) {
|
||||
TargetsInRange.push_back(cur);
|
||||
} else if (cur->IsNPC() && cur->IsPet() && cur->GetOwner()->IsNPC()) {
|
||||
TargetsInRange.push_back(cur);
|
||||
|
||||
//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 (cur->IsNPC() && cur->CastToNPC()->GetSwarmOwner() && cur->GetOwner()->IsNPC()) {
|
||||
TargetsInRange.push_back(cur);
|
||||
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
|
||||
else if (!spreader->IsNPC() && !cur->IsNPC()) {
|
||||
TargetsInRange.push_back(cur);
|
||||
else if (!spreader->IsNPC() && !mob->IsNPC()) {
|
||||
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)
|
||||
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->CastToNPC()->GetSwarmOwner()) && !spreader->GetOwner()->IsNPC()) {
|
||||
if (!cur->IsNPC()) {
|
||||
TargetsInRange.push_back(cur);
|
||||
// if spreader is not an NPC, and Target is an NPC, then spread to non-NPC controlled pets
|
||||
else if (!spreader->IsNPC() && mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && !mob->IsPetOwnerNPC()) {
|
||||
spreader_list.push_back(mob);
|
||||
}
|
||||
|
||||
// if spreader is a non-NPC controlled pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc)
|
||||
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->IsTempPet()) && !spreader->IsPetOwnerNPC()) {
|
||||
//Spread to non-NPCs
|
||||
if (!mob->IsNPC()) {
|
||||
spreader_list.push_back(mob);
|
||||
}
|
||||
else if (cur->IsNPC() && (cur->IsPet() || cur->CastToNPC()->GetSwarmOwner()) && !cur->GetOwner()->IsNPC()) {
|
||||
TargetsInRange.push_back(cur);
|
||||
//Spread to other non-NPC Pets
|
||||
else if (mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && !mob->IsPetOwnerNPC()) {
|
||||
spreader_list.push_back(mob);
|
||||
}
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
if(TargetsInRange.empty())
|
||||
return nullptr;
|
||||
|
||||
return TargetsInRange[zone->random.Int(0, TargetsInRange.size() - 1)];
|
||||
return spreader_list;
|
||||
}
|
||||
|
||||
void EntityList::StopMobAI()
|
||||
|
||||
@ -160,7 +160,6 @@ public:
|
||||
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
|
||||
bool IsNPCSpawned(std::vector<uint32> npc_ids);
|
||||
uint32 CountSpawnedNPCs(std::vector<uint32> npc_ids);
|
||||
Mob *GetTargetForVirus(Mob* spreader, int range);
|
||||
inline NPC *GetNPCByID(uint16 id)
|
||||
{
|
||||
auto it = npc_list.find(id);
|
||||
@ -512,6 +511,7 @@ public:
|
||||
void GetDoorsList(std::list<Doors*> &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);
|
||||
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, NPC *> &GetNPCList() { return npc_list; }
|
||||
|
||||
27
zone/mob.cpp
27
zone/mob.cpp
@ -367,6 +367,7 @@ Mob::Mob(
|
||||
pet_regroup = false;
|
||||
_IsTempPet = false;
|
||||
pet_owner_client = false;
|
||||
pet_owner_npc = false;
|
||||
pet_targetlock_id = 0;
|
||||
|
||||
attacked_count = 0;
|
||||
@ -405,10 +406,6 @@ Mob::Mob(
|
||||
roamer = false;
|
||||
rooted = false;
|
||||
charmed = false;
|
||||
has_virus = false;
|
||||
for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i++) {
|
||||
viral_spells[i] = 0;
|
||||
}
|
||||
|
||||
weaponstance.enabled = false;
|
||||
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)
|
||||
{
|
||||
SetNimbusEffect(effectid);
|
||||
|
||||
16
zone/mob.h
16
zone/mob.h
@ -333,8 +333,8 @@ public:
|
||||
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
|
||||
void SendBeginCast(uint16 spell_id, uint32 casttime);
|
||||
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);
|
||||
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0);
|
||||
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, int32 duration_override = 0);
|
||||
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center,
|
||||
CastAction_type &CastAction, EQ::spells::CastingSlot slot, bool isproc = false);
|
||||
virtual bool CheckFizzle(uint16 spell_id);
|
||||
@ -407,7 +407,6 @@ public:
|
||||
inline void SetMGB(bool val) { has_MGB = val; }
|
||||
bool HasProjectIllusion() const { return has_ProjectIllusion ; }
|
||||
inline void SetProjectIllusion(bool val) { has_ProjectIllusion = val; }
|
||||
void SpreadVirus(uint16 spell_id, uint16 casterID);
|
||||
bool IsNimbusEffectActive(uint32 nimbus_effect);
|
||||
void SetNimbusEffect(uint32 nimbus_effect);
|
||||
inline virtual uint32 GetNimbusEffect1() const { return nimbus_effect1; }
|
||||
@ -861,6 +860,9 @@ public:
|
||||
void FocusProcLimitProcess();
|
||||
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);
|
||||
int16 GetModSkillDmgTaken(const EQ::skills::SkillType skill_num);
|
||||
void ModVulnerability(uint8 resist, int16 value);
|
||||
@ -929,6 +931,8 @@ public:
|
||||
bool HasPetAffinity() { if (aabonuses.GivePetGroupTarget || itembonuses.GivePetGroupTarget || spellbonuses.GivePetGroupTarget) return true; return false; }
|
||||
inline bool IsPetOwnerClient() const { return pet_owner_client; }
|
||||
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 void SetTempPet(bool value) { _IsTempPet = value; }
|
||||
inline bool IsHorse() { return is_horse; }
|
||||
@ -1038,7 +1042,6 @@ public:
|
||||
inline const bool IsRoamer() const { return roamer; }
|
||||
inline const int GetWanderType() const { return wandertype; }
|
||||
inline const bool IsRooted() const { return rooted || permarooted; }
|
||||
inline const bool HasVirus() const { return has_virus; }
|
||||
int GetSnaredAmount();
|
||||
inline const bool IsPseudoRooted() const { return pseudo_rooted; }
|
||||
inline void SetPseudoRoot(bool prState) { pseudo_rooted = prState; }
|
||||
@ -1122,7 +1125,7 @@ public:
|
||||
void SendItemAnimation(Mob *to, const EQ::ItemData *item, EQ::skills::SkillType skillInUse, float velocity = 4.0);
|
||||
inline int& GetNextIncHPEvent() { return nextinchpevent; }
|
||||
void SetNextIncHPEvent( int inchpevent );
|
||||
|
||||
|
||||
inline bool DivineAura() const { return spellbonuses.DivineAura; }
|
||||
inline bool Sanctuary() const { return spellbonuses.Sanctuary; }
|
||||
|
||||
@ -1522,8 +1525,6 @@ protected:
|
||||
bool silenced;
|
||||
bool amnesiad;
|
||||
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 has_shieldequiped;
|
||||
bool has_twohandbluntequiped;
|
||||
@ -1636,6 +1637,7 @@ protected:
|
||||
bool _IsTempPet;
|
||||
int16 count_TempPet;
|
||||
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;
|
||||
|
||||
glm::vec3 m_TargetRing;
|
||||
|
||||
26
zone/npc.cpp
26
zone/npc.cpp
@ -928,19 +928,8 @@ bool NPC::Process()
|
||||
SendHPUpdate();
|
||||
}
|
||||
|
||||
if(HasVirus()) {
|
||||
if(viral_timer.Check()) {
|
||||
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 (viral_timer.Check()) {
|
||||
VirusEffectProcess();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if (swarmOwner->IsNPC()) {
|
||||
SetPetOwnerNPC(true);
|
||||
}
|
||||
}
|
||||
else if(GetOwnerID())
|
||||
{
|
||||
@ -2354,6 +2347,13 @@ void NPC::PetOnSpawn(NewSpawn_Struct* ns)
|
||||
if (tmp_lastname.size() < 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
|
||||
|
||||
@ -46,7 +46,7 @@ extern WorldServer worldserver;
|
||||
|
||||
// the spell can still fail here, if the buff can't stack
|
||||
// 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;
|
||||
EQ::ItemInstance *SummonedItem=nullptr;
|
||||
@ -119,7 +119,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
}
|
||||
else
|
||||
{
|
||||
buffslot = AddBuff(caster, spell_id);
|
||||
buffslot = AddBuff(caster, spell_id, duration_override);
|
||||
}
|
||||
if(buffslot == -1) // stacking failure
|
||||
return false;
|
||||
@ -168,7 +168,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
caster ? caster->GetLevel() : 0,
|
||||
buffslot
|
||||
);
|
||||
|
||||
|
||||
if (IsClient()) {
|
||||
if (parse->EventSpell(EVENT_SPELL_EFFECT_CLIENT, nullptr, CastToClient(), spell_id, buf, 0) != 0) {
|
||||
CalcBonuses();
|
||||
@ -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(!viral_timer.Enabled())
|
||||
viral_timer.Start(1000);
|
||||
if(IsVirusSpell(spell_id)) {
|
||||
|
||||
has_virus = true;
|
||||
for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2)
|
||||
{
|
||||
if(!viral_spells[i])
|
||||
{
|
||||
viral_spells[i] = spell_id;
|
||||
viral_spells[i+1] = caster->GetID();
|
||||
break;
|
||||
}
|
||||
if (!viral_timer.Enabled()) {
|
||||
viral_timer.Start(1000);
|
||||
}
|
||||
buffs[buffslot].virus_spread_time = zone->random.Int(GetViralMinSpreadTime(spell_id), GetViralMaxSpreadTime(spell_id));
|
||||
}
|
||||
|
||||
|
||||
@ -1091,8 +1083,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
break;
|
||||
}
|
||||
/*
|
||||
TODO: Parsing shows there is no level modifier. However, a consistent -2% modifer was
|
||||
found on spell with value 950 (95% spells would have 7% failure rates).
|
||||
TODO: Parsing shows there is no level modifier. However, a consistent -2% modifer was
|
||||
found on spell with value 950 (95% spells would have 7% failure rates).
|
||||
Further investigation is needed. ~ Kayen
|
||||
*/
|
||||
int chance = spells[spell_id].base[i];
|
||||
@ -1121,7 +1113,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
caster->MessageString(Chat::SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
int chance = spells[spell_id].base[i];
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for(int slot = 0; slot < buff_count; slot++) {
|
||||
@ -1473,7 +1465,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
spell.base[i],
|
||||
gender_id
|
||||
);
|
||||
|
||||
|
||||
if (spell.max[i] > 0) {
|
||||
if (spell.base2[i] == 0) {
|
||||
SendIllusionPacket(
|
||||
@ -1505,7 +1497,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
spell.max[i]
|
||||
);
|
||||
}
|
||||
SendAppearancePacket(AT_Size, race_size);
|
||||
SendAppearancePacket(AT_Size, race_size);
|
||||
}
|
||||
|
||||
for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) {
|
||||
@ -1549,9 +1541,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
if (!CanMemoryBlurFromMez && IsEffectInSpell(spell_id, SE_Mez)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
int wipechance = 0;
|
||||
|
||||
|
||||
if (caster) {
|
||||
wipechance = caster->GetMemoryBlurChance(effect_value);
|
||||
}
|
||||
@ -3360,7 +3352,7 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level,
|
||||
effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell_id, ticsremaining);
|
||||
|
||||
// this doesn't actually need to be a song to get mods, just the right skill
|
||||
if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill)
|
||||
if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill)
|
||||
&& IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effectid[effect_id])){
|
||||
|
||||
|
||||
@ -3770,7 +3762,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
|
||||
|
||||
switch (effect) {
|
||||
case SE_CurrentHP: {
|
||||
|
||||
|
||||
if (spells[buff.spellid].base2[i] && !PassCastRestriction(spells[buff.spellid].base2[i])) {
|
||||
break;
|
||||
}
|
||||
@ -4086,23 +4078,6 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
|
||||
|
||||
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(
|
||||
"{} {} {} {}",
|
||||
buffs[slot].casterid,
|
||||
@ -7168,7 +7143,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
|
||||
Note: (ID 221 - 249) For effect seen in mage spell 'Shock of Many' which increases damage based on number of pets on targets hatelist. The way it is implemented
|
||||
works for how our ROF2 spell file handles the effect where each slot fires individually, while on live it only takes the highest
|
||||
value. In the future the way check is done will need to be adjusted to check a defined range instead of just great than.
|
||||
value. In the future the way check is done will need to be adjusted to check a defined range instead of just great than.
|
||||
*/
|
||||
|
||||
if (value <= 0) {
|
||||
@ -7202,8 +7177,8 @@ bool Mob::PassCastRestriction(int value)
|
||||
break;
|
||||
|
||||
case IS_BODY_TYPE_MISC:
|
||||
if ((GetBodyType() == BT_Humanoid) || (GetBodyType() == BT_Lycanthrope) || (GetBodyType() == BT_Giant) ||
|
||||
(GetBodyType() == BT_RaidGiant) || (GetBodyType() == BT_RaidColdain) || (GetBodyType() == BT_Animal)||
|
||||
if ((GetBodyType() == BT_Humanoid) || (GetBodyType() == BT_Lycanthrope) || (GetBodyType() == BT_Giant) ||
|
||||
(GetBodyType() == BT_RaidGiant) || (GetBodyType() == BT_RaidColdain) || (GetBodyType() == BT_Animal)||
|
||||
(GetBodyType() == BT_Construct) || (GetBodyType() == BT_Dragon) || (GetBodyType() == BT_Insect)||
|
||||
(GetBodyType() == BT_VeliousDragon) || (GetBodyType() == BT_Muramite) || (GetBodyType() == BT_Magical))
|
||||
return true;
|
||||
@ -7342,7 +7317,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
if (IsHybridClass(GetClass()))
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_CLASS_WARRIOR:
|
||||
if (GetClass() == WARRIOR)
|
||||
return true;
|
||||
@ -7449,7 +7424,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
return true;
|
||||
break;
|
||||
|
||||
case FRENZIED_BURNOUT_NOT_ACTIVE:
|
||||
case FRENZIED_BURNOUT_NOT_ACTIVE:
|
||||
if (!HasBuffWithSpellGroup(SPELLGROUP_FRENZIED_BURNOUT))
|
||||
return true;
|
||||
break;
|
||||
@ -7458,7 +7433,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
if (GetHPRatio() > 75)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_HP_LESS_THAN_20_PCT:
|
||||
if (GetHPRatio() <= 20)
|
||||
return true;
|
||||
@ -7605,27 +7580,27 @@ bool Mob::PassCastRestriction(int value)
|
||||
if (GetHPRatio() > 25 && GetHPRatio() <= 35)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_HP_BETWEEN_35_AND_45_PCT:
|
||||
if (GetHPRatio() > 35 && GetHPRatio() <= 45)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_HP_BETWEEN_45_AND_55_PCT:
|
||||
if (GetHPRatio() > 45 && GetHPRatio() <= 55)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_HP_BETWEEN_55_AND_65_PCT:
|
||||
if (GetHPRatio() > 55 && GetHPRatio() <= 65)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_HP_BETWEEN_65_AND_75_PCT:
|
||||
if (GetHPRatio() > 65 && GetHPRatio() <= 75)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_HP_BETWEEN_75_AND_85_PCT:
|
||||
if (GetHPRatio() > 75 && GetHPRatio() <= 85)
|
||||
return true;
|
||||
@ -7635,7 +7610,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
if (GetHPRatio() > 85 && GetHPRatio() <= 95)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_HP_ABOVE_45_PCT:
|
||||
if (GetHPRatio() > 45)
|
||||
return true;
|
||||
@ -7675,7 +7650,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
if (GetBodyType() != BT_Plant)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
case IS_NOT_CLIENT:
|
||||
if (!IsClient())
|
||||
return true;
|
||||
@ -7689,8 +7664,8 @@ bool Mob::PassCastRestriction(int value)
|
||||
case IS_LEVEL_ABOVE_42_AND_IS_CLIENT:
|
||||
if (IsClient() && GetLevel() > 42)
|
||||
return true;
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
case IS_TREANT:
|
||||
if (GetRace() == RT_TREANT || GetRace() == RT_TREANT_2 || GetRace() == RT_TREANT_3)
|
||||
return true;
|
||||
@ -7878,7 +7853,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case IS_CLIENT_AND_MALE_PLATE_USER:
|
||||
if (IsClient() && GetGender() == MALE && IsPlateClass(GetClass()))
|
||||
return true;
|
||||
@ -7890,7 +7865,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
break;
|
||||
|
||||
case IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE:
|
||||
if (IsClient() && GetGender() == MALE &&
|
||||
if (IsClient() && GetGender() == MALE &&
|
||||
(GetClass() == BEASTLORD || GetClass() == BERSERKER || GetClass() == MONK || GetClass() == RANGER || GetClass() == ROGUE))
|
||||
return true;
|
||||
break;
|
||||
@ -7967,7 +7942,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case IS_NOT_CLASS_BARD:
|
||||
if (GetClass() != BARD)
|
||||
return true;
|
||||
@ -8012,7 +7987,7 @@ bool Mob::PassCastRestriction(int value)
|
||||
if (FindBuff(SPELL_INCENDIARY_OOZE_BUFF))
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
||||
//Not handled, just allow them to pass for now.
|
||||
case UNKNOWN_3:
|
||||
case HAS_CRYSTALLIZED_FLAME_BUFF:
|
||||
@ -8595,7 +8570,7 @@ int Mob::GetMemoryBlurChance(int base_chance)
|
||||
*/
|
||||
int cha_mod = int(GetCHA() / 10);
|
||||
cha_mod = std::min(cha_mod, 15);
|
||||
|
||||
|
||||
int lvl_mod = 0;
|
||||
if (GetLevel() < 17) {
|
||||
lvl_mod = 100;
|
||||
@ -8614,3 +8589,76 @@ int Mob::GetMemoryBlurChance(int base_chance)
|
||||
chance += chance * chance_mod / 100;
|
||||
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].focusproclimit_time = 0;
|
||||
buffs[emptyslot].focusproclimit_procamt = 0;
|
||||
buffs[emptyslot].virus_spread_time = 0;
|
||||
buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10;
|
||||
|
||||
if (level_override > 0 || buffs[emptyslot].numhits > 0) {
|
||||
@ -3533,9 +3534,8 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite)
|
||||
// break stuff
|
||||
//
|
||||
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);
|
||||
|
||||
// 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
|
||||
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
|
||||
// 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].focusproclimit_time = 0;
|
||||
buffs[slot_id].focusproclimit_procamt = 0;
|
||||
buffs[slot_id].virus_spread_time = 0;
|
||||
buffs[slot_id].UpdateClient = false;
|
||||
buffs[slot_id].instrument_mod = instrument_mod;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user