The issue, as Shad says, is engine priority. PAoEs and spawned-in placeable heartbeats have incredibly low priority, and will fail to fire under even light server load. This does NOT necessarily mean that there's anything wrong with your module, however, as TSM notes - we have it on HG despite the best streamlining, even with only a couple players on. The solution is to replace the heartbeat with a psuedoheartbeat. You can find a bevvy of threads discussing pseudos in relation to this problem on the old forums.
Here's a sample I posted a few years back at someone's request:
Click MeWe've evolved the process somewhat since then on HG. All aoes now run through a central include. Here's the scripts for that acid fog spell now (code is mostly acaos'):
First, the spell itself:
#include "ac_aoeper_inc"
struct SpellInfo GetPersistentAoESpellInfo () { return GetSpellInfo(); }
int GetPersistentAoELimit (struct SpellInfo si) {
if (GetIsQuasiSpell(si, QUASIclass_HERALD_OF_STORMS))
return -2;
return -1;
}
int GetPersistentAoEEffect (struct SpellInfo si) { return AOE_PER_FOGACID; }
string GetPersistentAoETag (struct SpellInfo si) { return "VFX_PER_FOGACID"; }
string GetPersistentAoEName (struct SpellInfo si) { return "Acid Fog"; }
int GetPersistentAoETargetMask (struct SpellInfo si) { return OBJECT_TYPE_CREATURE; }
int GetPersistentAoETargetType (struct SpellInfo si) { return TARGET_TYPE_DEFAULT; }
float GetPersistentAoEDuration (struct SpellInfo si) {
return MetaDuration(si, si.clevel / 2, DURATION_IN_ROUNDS);
}
effect GetPersistentAoEImpactEffect (struct SpellInfo si) {
effect eEff;
return eEff;
}
effect GetPersistentAoEDurationEffect (struct SpellInfo si) {
effect eDur;
if (GetIsQuasiSpell(si, QUASIclass_HERALD_OF_STORMS))
eDur = EffectDamageIncrease(1);
return eDur;
}
effect GetPersistentAoEVisualEffect (struct SpellInfo si) {
ApplyVisualAtLocation(VFX_FNF_GAS_EXPLOSION_ACID, si.loc);
return EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_ACID);
}
void ApplyAoEEffect (struct SpellInfo si, object oAoE, object oTarget, effect eEff, effect eDur, effect eVis) {
if (!GetSpellResisted(si, oTarget, 0.0, 2)) {
int nDamage, nDice = si.clevel, nEffType = GetEffectType(eDur);
int nDamType = DAMAGE_TYPE_ACID, nSaveType = SAVING_THROW_TYPE_ACID;
if (nDice < 1)
nDice = 1;
nDamage = MetaPower(si, nDice, 6);
nDamage = GetReflexAdjustedDamage(nDamage, oTarget, si.dc, nSaveType, si.caster);
if (nDamage > 0) {
if (nEffType == EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE) {
if (!GetHasSpecificEffect(eDur, oTarget))
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDur, oTarget);
eEff = EffectDamage(nDamage / 2, nDamType);
DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget));
eEff = EffectDamage(nDamage / 2, DAMAGE_TYPE_MAGICAL);
DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget));
} else if (nEffType == EFFECT_TYPE_DAMAGE_INCREASE) {
if (GetTrueDamageImmunity(oTarget, nDamType) < 100) {
int nDecrease = 25 - GetTotalDamageImmunityDecrease(oTarget, nDamType);
if (nDecrease > 5)
nDecrease = 5;
if (nDecrease > 0) {
eEff = EffectDamageImmunityDecrease(nDamType, nDecrease);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEff, oTarget);
}
}
eEff = EffectDamage(nDamage, nDamType);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget);
} else {
eEff = EffectDamage(nDamage, nDamType);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget);
}
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
}
}
}
Then, the include
ac_aoeper_inc (which, unlike most includes, actually has the void main in it - don't let that throw you)
:
#include "hg_inc"
#include "ac_spell_inc"
int GetIsHeraldAoE(object oAoE) {
string sTag = GetTag(oAoE);
if (sTag == "VFX_PER_FOGFREEZE" ||
sTag == "VFX_AOE_CONSECRATE_20" ||
sTag == "VFX_PER_FOGACID" ||
sTag == "VFX_PER_FOGKILL" ||
sTag == "VFX_PER_FOGMIND" ||
sTag == "VFX_PER_FOGSTINK")
return TRUE;
return FALSE;
}
struct SpellInfo GetPersistentAoESpellInfo ();
int GetPersistentAoELimit (struct SpellInfo si);
int GetPersistentAoEEffect (struct SpellInfo si);
string GetPersistentAoETag (struct SpellInfo si);
string GetPersistentAoEName (struct SpellInfo si);
int GetPersistentAoETargetMask (struct SpellInfo si);
int GetPersistentAoETargetType (struct SpellInfo si);
float GetPersistentAoEDuration (struct SpellInfo si);
effect GetPersistentAoEImpactEffect (struct SpellInfo si);
effect GetPersistentAoEDurationEffect (struct SpellInfo si);
effect GetPersistentAoEVisualEffect (struct SpellInfo si);
void ApplyAoEEffect (struct SpellInfo si, object oAoE, object oTarget, effect eEff, effect eDur, effect eVis);
float GetPersistentAoERemaining (object oAoE) {
int nRemaining = GetLocalInt(oAoE, "AoEExpires") - GetLocalInt(GetModule(), "uptime");
return IntToFloat(nRemaining < 1 ? 1 : nRemaining);
}
void DoAoEHeartbeat (struct SpellInfo si, object oAoE, effect eEff, effect eDur, effect eVis) {
int bDestroy = FALSE, bAllPCs = FALSE;
if (!GetIsObjectValid(oAoE))
return;
/* Storm of Vengeance doesn't destroy properly sometimes */
if (GetPersistentAoEEffect(si) == AOE_PER_STORM) {
if (si.id == SPELL_STORM_OF_VENGEANCE && GetLocalInt(GetArea(oAoE), "Area_Underwater"))
bAllPCs = TRUE;
if (GetLocalInt(GetModule(), "uptime") > GetLocalInt(oAoE, "AoEExpires"))
bDestroy = TRUE;
}
if (bDestroy ||
!GetIsObjectValid(si.caster) ||
GetIsDead(si.caster) ||
GetArea(si.caster) != si.area) {
SetPlotFlag(oAoE, FALSE);
DestroyObject(oAoE);
return;
}
DelayCommand(6.0, DoAoEHeartbeat(si, oAoE, eEff, eDur, eVis));
AddLocalInt(oAoE, "AoERounds", 1);
si.target = oAoE;
int nMask = GetPersistentAoETargetMask(si);
int nType = GetPersistentAoETargetType(si);
for (si.target = GetFirstInPersistentObject(oAoE, nMask);
GetIsObjectValid(si.target);
si.target = GetNextInPersistentObject(oAoE, nMask)) {
if (GetIsSpellTarget(si, si.target, nType) || (bAllPCs && GetIsPC(si.target))) {
SignalEvent(si.target, EventSpellCastAt(si.caster, si.id));
AssignCommand(si.caster, DelayCommand(GetRandomDelay(0.5, 4.5), ApplyAoEEffect(si, oAoE, si.target, eEff, eDur, eVis)));
}
}
}
void StartAoEHeartbeat (struct SpellInfo si, string sTag, effect eEff, effect eDur, effect eVis, int nUntil) {
int nCount = 1;
object oLoc, oAoE;
do {
oLoc = GetNearestObjectToLocation(OBJECT_TYPE_ALL, si.loc, nCount++);
} while (GetTag(oLoc) == sTag);
nCount = 1;
oAoE = GetNearestObjectByTag(sTag, oLoc);
while (GetIsObjectValid(oAoE)) {
if (GetAreaOfEffectCreator(oAoE) == si.caster && !GetLocalInt(oAoE, "AoEHeartbeat")) {
SetLocalInt(oAoE, "AoEHeartbeat", 1);
SetLocalInt(oAoE, "AoEExpires", nUntil);
SetLocalInt(oAoE, "AoESP", si.sp);
DelayCommand(0.0, DoAoEHeartbeat(si, oAoE, eEff, eDur, eVis));
return;
}
oAoE = GetNearestObjectByTag(sTag, oLoc, ++nCount);
}
}
void main () {
struct SpellInfo si = GetPersistentAoESpellInfo();
if (si.id < 0)
return;
float fDur = GetPersistentAoEDuration(si);
string sTag = GetPersistentAoETag(si);
effect eAoE = EffectAreaOfEffect(GetPersistentAoEEffect(si),
(si.id == SPELL_GREASE ? "nw_s0_greasea" : "****"), "****", "****");
int nLimit = GetPersistentAoELimit(si);
if (nLimit > 0) {
int nFound = 0, nCount = 1;
object oOther, oCreator, oArea = GetArea(si.caster);
while (GetIsObjectValid(oOther = GetNearestObjectByTag(sTag, si.caster, nCount++))) {
oCreator = GetAreaOfEffectCreator(oOther);
if (oCreator == oArea ||
GetLocalInt(oOther, "NoAoEDispel"))
continue;
if (GetIsPC(si.caster) && GetIsPC(oCreator)) {
if (GetIsHeraldAoE(oOther)) {
if (!GetIsQuasiclass(QUASIclass_HERALD_OF_STORMS, oCreator))
nFound++;
} else
nFound++;
} else if (GetFactionEqual(si.caster, oCreator)) {
nFound++;
}
if (nFound >= nLimit) {
SetPlotFlag(oOther, FALSE);
DestroyObject(oOther);
if (nFound == nLimit)
FloatingTextStringOnCreature("To prevent lag, only " +
IntToString(nLimit) + " " + GetPersistentAoEName(si) +
" spell" + (nLimit == 1 ? "" : "s") + " may exist in an area at the same time.", si.caster);
}
}
} else if (nLimit == -1) {
object oAoE, oArea = GetArea(si.caster);
for (oAoE = GetFirstObjectInArea(oArea); GetIsObjectValid(oAoE); oAoE = GetNextObjectInArea(oArea)) {
if (GetObjectType(oAoE) != OBJECT_TYPE_AREA_OF_EFFECT ||
GetAreaOfEffectCreator(oAoE) != si.caster)
continue;
if (FindSubString(" VFX_PER_FOGACID VFX_PER_FOGFIRE VFX_PER_FOGFREEZE VFX_PER_STORM VFX_PER_RAINFIRE VFX_PER_RAINFREEZE VFX_AOE_CONSECRATE_20 ",
" " + GetTag(oAoE) + " ") >= 0) {
SetPlotFlag(oAoE, FALSE);
DestroyObject(oAoE);
FloatingTextStringOnCreature("Only one area of effect damage field may exist per caster in an area at the same time.", si.caster);
}
}
} else if (nLimit == -2) {
object oAoE, oArea = GetArea(si.caster);
int nFound;
for (oAoE = GetFirstObjectInArea(oArea); GetIsObjectValid(oAoE); oAoE = GetNextObjectInArea(oArea)) {
if (GetObjectType(oAoE) != OBJECT_TYPE_AREA_OF_EFFECT ||
!GetIsQuasiclass(QUASIclass_HERALD_OF_STORMS, GetAreaOfEffectCreator(oAoE)))
continue;
if (FindSubString(" VFX_PER_FOGACID VFX_PER_FOGSTINK VFX_PER_FOGKILL VFX_AOE_CONSECRATE_20 VFX_PER_FOGFREEZE VFX_PER_FOGMIND ",
" " + GetTag(oAoE) + " ") >= 0) {
nFound++;
if (nFound >= 2) {
SetPlotFlag(oAoE, FALSE);
DestroyObject(oAoE);
if (nFound == 2)
FloatingTextStringOnCreature("Only two herald of storms clouds may exist in an area at the same time.", si.caster);
}
}
}
}
effect eEff = GetPersistentAoEImpactEffect(si);
effect eDur = GetPersistentAoEDurationEffect(si);
effect eVis = GetPersistentAoEVisualEffect(si);
AssignCommand(si.area, DelayCommand(2.0, StartAoEHeartbeat(si, sTag, eEff, eDur, eVis,
GetLocalInt(GetModule(), "uptime") + FloatToInt(fDur))));
ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eAoE, si.loc, fDur);
}
The main reason for the change in systems was to easily create more without a lot of duplicative code.
Funky
Modifié par FunkySwerve, 13 décembre 2010 - 11:37 .