Types of scripts to avoid
#1
Posté 16 décembre 2013 - 10:05
As of right now the only thing I know to be careful of using is heartbeat scripts, and to be honest, I don't even use any as of right now in my module.
What I'm curious of in particular is how intense case scripting is, and what about nested case scripting?
#2
Posté 16 décembre 2013 - 10:32
Lag Busting
Funky
#3
Posté 17 décembre 2013 - 12:09
#4
Posté 17 décembre 2013 - 12:15
I've been using Sir Elric's Simple Creature Respawn for years now, and haven't noticed any such lag with having hundreds of hard placed creatures, and 90% of said creatures are wandering with ambient animations. I've been keeping an eye on my server's CPU and RAM usage and neither climb at all. CPU usage is 0% for the most part.
I didn't know about separating groups into different factions, but I'm going to go through every area and re-faction different groups (this is going to be a nightmare).
Modifié par EzRemake, 17 décembre 2013 - 12:31 .
#5
Posté 17 décembre 2013 - 02:17
For the refactioning, you can restrict it to area groupings. We have many hundreds of areas in the mod (well over 600 the last time I counted), but only about 80 custom factions, I think - haven't counted lately. By way of example, we have one custom faction for all our abyss areas, which fall into 5 different 'planes', each having 9-12 areas. Ditto our hell faction, which comprises 28 areas. It all boils down to area groupings. When I retrofitted our mod, I had to make a couple duplicate critters, just fyi, for places where there were the same creature spawning in in different areas that did not fit well into one 'group'. The amount of overlap you have is going to dictate how many factions you have.
Word of warning - if you're not having lag issues, it's not worth the time to do this. Remember, computers today are a lot more powerful than they were when NWN dropped.
Funky
#6
Posté 17 décembre 2013 - 08:34
EzRemake wrote...
I noticed the one point in your lexicon tutorial stated not to use hard placed NPCs, and to have everything spawned in via trigger or encounter, and I was curious how much of an impact this actually has?
Not that big as one would though.
In singleplayer you dont have to worry about this at all.
In multiplayer it depends on how good is your server machine, how often you want to restart the nwserver.
It also depends on the gamestyle, if you are making a "HCRP" module where majoritz of the content is low lvl, where monsters spawn usually in low numbers and slowly its not a problematic as if you were creating a high-lvl epic action module where usually you spawn around 500 monsters per one dungeon/20minutes. Even then, if you have a despawn in place normally you won't encounter any issues, Arkhalia epic-action server I was formerly admin is able to run 3days without reset without noticeable lags. But its true that I put quite a lot of work to make this possible.
Some of my tips based on working with big epic-action module:
- despawning is a must, sometimes players just running trough a dungeon especially if they can do this and don't have to kill anything or if they don't get any experiences from monsters
- in such modules, stores can create big lags when players overfills them(thousand+ sold items), this has been biggest issue till I joined DM team and server had to be previously restarted every day just because of this (certain unofficial patch solves this issue via module switch)
- if you have standing plot neutral npcs who just talk and not moving, not fighting, I suggest to remove all their scripts
- remove the OnSpellCastAt script on monsters that are often being massed by spells, this script causes ultimate lags, certain unofficial patch partially solves this but when you have 50+ monsters on one spot its not going to help you, best is to avoid spawning so many creatures at once at all (sometimes builders doing this with those tiny spiders or vermins from cep) if its possible
- suggest to disable stacking of AOEs, AOEs are biggest source of lags especially if you cast such spell into pack of 50monsters, and especially if you cast more than one of these, when I played as a druid with a tactics of massing entire area with entangle, grease, stonehold, sov,sov,sov,sov and offensive spells it created such big lags that each sov lasted not 10 rounds but 30:devil: so if you can, try restrict AOEs to only one instance per player which seems to me as best performance and balance solution, afaik HG has it this way
#7
Posté 17 décembre 2013 - 04:31
ShaDoOoW wrote...
EzRemake wrote...
I noticed the one point in your lexicon tutorial stated not to use hard placed NPCs, and to have everything spawned in via trigger or encounter, and I was curious how much of an impact this actually has?
Not that big as one would though.
In singleplayer you dont have to worry about this at all.
In multiplayer it depends on how good is your server machine, how often you want to restart the nwserver.
He's right about singleplayer. In multiplayer, however, we had issues even on a good machine, and switching to spawning them in and out made the second-most noticeable impact on performance (again, per the ordering in that tutorial). Also, it has little to do with how often you want to restart nwserver, as there are no memory leaks associated with placed NPCs, unless you're doing something wrong in your code (at least so far as I'm aware).
For the store issue, we solved it by making separate sell and buy stores, with the players able to select which they want to access via conversatino. This solves the issue because the lag generates when large numbers of sold items are checked against the store inventory. If the store is empty to start, you don't get the massive lag spike when items are sorted post-sale (and I do mean massive - it was capable of bringing down a high-end server machine's mod instance, with only a couple hundred items sold).
Also, as to this:
Actually, AOEs are not inherently the source of much lag - it's their graphics. We swapped most of ours out to alternate graphics, and use them like crazy with no ill effects. Cloud of Bewilderment is among the worst offenders.suggest to disable stacking of AOEs, AOEs are biggest source of lags
especially if you cast such spell into pack of 50monsters, and
especially if you cast more than one of these, when I played as a druid
with a tactics of massing entire area with entangle, grease, stonehold,
sov,sov,sov,sov and offensive spells it created such big
Funky
Modifié par FunkySwerve, 17 décembre 2013 - 04:33 .
#8
Posté 17 décembre 2013 - 05:31
FunkySwerve wrote...
Also, it has little to do with how often you want to restart nwserver, as there are no memory leaks associated with placed NPCs, unless you're doing something wrong in your code (at least so far as I'm aware).
I'm reasonably sure he was referring to the fact that if you place monsters manually then they don't respawn, hence you need to restart so the module actually has monsters.
Another interesting point about placement versus spawning: if I look at a module that uses spawning with some triggers and waypoints, the size is 3.26 MB with effectively 10 areas. If I remove the waypoints and spawning triggers and instead manually place 33 creatures that spawns in just ONE of the areas, the size becomes 3.36 MB. Which basically means something like every 300 creatures takes up 1 MB. So if you had 500 areas with 30 creatures each, you're looking at an extra 50 MB of size.
The funny thing is I would have sworn that not placing creatures saved even more memory than that -- I seem to recall it cutting module size into something like 1/2 or even 1/3 in some experiments I was running a year or two ago. Possibly because that old module had creatures with many items each so that probably took up more memory per creature, maybe. I specifically never gave creatures more than one item (a weapon) in the module I was just testing.
Modifié par MagicalMaster, 17 décembre 2013 - 06:30 .
#9
Posté 17 décembre 2013 - 05:55
What I meant is that if you intent to restart server every 6 hours, which I found as a standard on many PWs, you don't have to worry much about efficiency. Such servers usually havent encountered any lag troubles yet at all and they never did any of this prevention. From the question of OP I assume he is either beginning building or he have a module already and read many topics here and there about lag issues, scripting efficiency etc. which is in his case all redundant info as scripters often arguing about something that has absolutely no impact on a performance in a long-term run anyway.MagicalMaster wrote...
I'm reasonably sure he was referring to the fact that if you place monsters manually then they don't respawn, hence you need to restart so the module actually has monsters.
#10
Posté 17 décembre 2013 - 06:05
MagicalMaster wrote...
The funny thing is I would have sworn that not placing creatures saved even more memory than that -- I seem to recall it cutting module size into something like 1/2 or even 1/3 in some experiments I was running a year or two ago. Possibly because that old module had creatures with many items each so that probably took up more memory per creature, maybe. I specifically never gave creatures more than one item (a weapon) in the module I was just testing.
I'm with Shad on this one too. You can wrap yourself in knots trying to optimize and accomplish very little. In this case, you also run the risk of causing other problems. To use HG as an example, we use a custom encounter system, spawning in npc enemies semi-randomly, based on preconfigured variables. As a part of that system, we use a TON of waypoints, to emulate creature behavior. So, while we may be saving some on module size in doing that, we're also adding a lot more objects to the module that have to be looped through every time you use GetObjectByTag. While GOBT is lightning-fast, we're still likely hurting performance more than we're helping it with those encounters (the point of which is flexibility of spawning, not efficiency, by the way).
The main thing to concern yourself with is not to get in the habit of doing things that add a lot of overhead needlessly, as they can quickly add up. If you rely on that guide I linked, and deviate from it sparingly, you should be ok. If, however, you're not having much lag at the moment, it's just not worth pretzling yourself to try to further optimize.
Funky
Modifié par FunkySwerve, 17 décembre 2013 - 06:07 .
#11
Posté 17 décembre 2013 - 07:25
hmm interesting but not really believe that because I was doing one test recently. I was in that time yet suspecting the SoV lasts longer than 10rounds, so I made a test where I casted one SoV on one target and counted rounds - 10. Then I casted three SoVs and it was suddenly 15rounds each (for anyone who will repeat this test - it might be totally fine in your module, it hapened in a specific module). Imo SoV is quite light visual effect and it was usead only against single target. But will test this assumption and try to disably any vfxes from AOEs if it helps.FunkySwerve wrote...
Actually, AOEs are not inherently the source of much lag - it's their graphics. We swapped most of ours out to alternate graphics, and use them like crazy with no ill effects. Cloud of Bewilderment is among the worst offenders.
Funky
Modifié par ShaDoOoW, 17 décembre 2013 - 07:26 .
#12
Posté 17 décembre 2013 - 08:16
FunkySwerve wrote...
So, while we may be saving some on module size in doing that, we're also adding a lot more objects to the module that have to be looped through every time you use GetObjectByTag. While GOBT is lightning-fast, we're still likely hurting performance more than we're helping it with those encounters (the point of which is flexibility of spawning, not efficiency, by the way).
If you have one spawn waypoint per creature, then when the module loads isn't that the same as having a creature at each of those positions in terms of object count? Sure, when you spawn some creatures then you technically have more objects -- but don't you have an equal amount until that point?
The tons of waypoints for specific behaviors is a different matter.
Also, how significant is the differenc between GOBT versus GNOBT? If you have 10 areas with 10 objects each, then does GOBT take 10 times as long (linear with no significant constant cost) or would it take like twice as long (due to some constant cost of the function itself)?
Finally, I thought you mentioned in a conversation that you were trying to bring module size down -- or was that solely a result of decreasing module resources (the 16k limit thing -- combining scripts and such) and the memory size decrease merely a fortunate byproduct?
#13
Posté 17 décembre 2013 - 08:18
Even in the discussion threads for Sir Elric's Simple Creature Respawn you can see people talking about how no lag was added when using hard placed NPCs.
I suppose it has to do with many factors. Even now with the current module I'm building, with 58 areas filled with ambient roaming NPCs, my server's CPU usage sleeps at 0% when empty, and climbs only a percent or two with two players bashing away at monsters (the most I've tested is 2, since I'm still in an alpha stage).
I general though, I really do not anticipate having many concurrent players at any given time, so I'm not really sure how relevant most of this is anyways.
(Also, when using script profiling in nwnplayer.ini, how can you tell how much time is too much for a script to be running)
#14
Posté 04 janvier 2014 - 12:53
There is a lot you can do to reduce lag, I don't use heartbeat scripts at all anymore, rather area event scripting is far more my focus, so when a PC leaves an area, their AOEs just go poof, along with any other NPCs or script running objects...
The big one is not to use DelayCommand() whenever possible...
(Though it's ok for short delays, long delays are a big no no..)
Also, don't use GetObjectByTag() rather use a waypoint to reference the object, using GetWaypoint() & GetNearestObjectByTag(oWay, "name");
There are a lot of ways to reduce lag, the main thing is to be extra careful about using any kind of combat scripting, especially items that use OnHit effects (that can seriously spike your server with 10 - 60+ people firing 2 - 6 scripts a round)... (10 monks dual welding kamas with on hit cast spell can get insane fast!)
Modifié par _Guile, 04 janvier 2014 - 12:54 .
#15
Posté 04 janvier 2014 - 01:56
The big one is not to use DelayCommand() whenever possible...
(Though it's ok for short delays, long delays are a big no no..)
[/quote]
While we do generally avoid unnecessary DC's, this isn't remotely close to a 'big one'. Only people who are hyper-attentive to effeciency should even pause to consider it, and there are generally few good alternatives to DCs, when used appropriately.
When used inappropriately, it's not generally the DC that's the problem, but the approach to the event triggering that's the issue. Long DCs generally fall into the 'inefficient scripts' catchall in the tutorial, because there's usually a much better way to code them, often one involving timestamps.
By way of example, we don't respawn most things in our areas by delay anymore, but by checking a timestamp on area enter to see if the area should be respawned yet. You get no respawning until it's actually necessary (and, as a side effect, no DC running).
[quote]
Also, don't use GetObjectByTag() rather use a waypoint to reference the object, using GetWaypoint() & GetNearestObjectByTag(oWay, "name");
[/quote]
This is flatly wrong. GOBT is much, much faster than GetWaypoint combined with GNOBT. While I used to think this was the case, tere was a thread on it some time back that caused me to run some tests. Even in modules with zillions of objects, like mine, there's no competition. This is because GOBT is much better optimized in the engine. Run some profiling and you'll see.
[quote]
There are a lot of ways to reduce lag, the main thing is to be extra careful about using any kind of combat scripting, especially items that use OnHit effects (that can seriously spike your server with 10 - 60+ people firing 2 - 6 scripts a round)... (10 monks dual welding kamas with on hit cast spell can get insane fast!)[/quote]
This is an old piece of misinformation which became more prevalent around the time when sonic onhit effects were added. We use onhits like crazy, and they don't cause problems. What DOES cause 'lag', is weapon onhit vfx, for people with cards that can't hanlde it - most especially the sonic effect, but the others as well. This lag is entirely graphical, per the definitions in my tutorial, and has nothing to do with the onhit scripts themselves.
For reference, this is the onhit script we use on all players, as well as any monsters who have abilities we want to fire from onhit. It fires like crazy, with no ill effects. In point of fact, it was specifically engineered to allow as many simultaneous onhits as earthly possible. Every single onhit buff (like flame weapon) we have, uses it, and we have a bunch of custom ones, like acid weapon. It's not uncommon to have 5 or 6 players with 3 or 4 weapon buffs apiece swinging this script 5 times each per round, along with god knows how many critters.
[quote]
#include "hg_inc"
#include "ac_spell_inc"
#include "ac_dispel_inc"
#include "ac_effect_inc"
#include "ac_itemprop_inc"
#include "ac_itemenh_inc"
#include "ac_check_inc"
#include "x2_inc_itemprop"
/*
* OnHitDamages format:
* TYPE,DICE,SIDES,BONUS[,RACEMASK]
*
* OnHitEffects format:
* DYNEFFECT[|CHECK[|CHECK...]]
*
* DYNEFFECT format:
* LINKEFFECT[;LINKEFFECT...][:SECONDS[+RANDOM]][:E|S]][/VFX][#SPELLID]
*
* LINKEFFECT format:
* EFFECTID[,PARAM...]
*
* CHECK format:
* CHECKTYPE:PARAM[:PARAM...][/NAME]
*
* CHECKTYPES
* 1 - percent chance (param is percentage chance)
* 2 - saving throw (params are type, dc, random dc, subtype, feedback)
* 3 - ability check (params are ability, dc, random dc, feedback, auto)
* 4 - skill check (params are skill, dc, random dc, feedback, auto)
* 5 - favored enemy check (param is a favored enemy mask)
* 6 - spell resistance check (param is spell penetration)
*
* & - last check was successful (and)
* ^ - last check failed (exclusive or)
*
* OnHitBreach (integer)
* if > 0 will breach that many spells
* if < 0 will act as a dispel of level = abs(OnHitBreach)
*
*/
void SetItemPropertiesFromSpellDurations (object oItem, int nSpellId, float fDur) {
object oCreator = GenCreator();
itemproperty ip = GetFirstItemProperty(oItem);
while (GetIsItemPropertyValid(ip)) {
if (GetItemPropertyDurationType(ip) == DURATION_TYPE_TEMPORARY &&
GetItemPropertySpellId(ip) == nSpellId) {
RemoveItemProperty(oItem, ip);
AssignCommand(oCreator, DelayCommand(0.1, AddItemProperty(DURATION_TYPE_TEMPORARY, ip, oItem, fDur)));
}
ip = GetNextItemProperty(oItem);
}
}
void DegradeOnHitProperties (object oWeapon, itemproperty ip, int nSpellId, int nDegrade, object oCreator) {
if (!GetIsObjectValid(oWeapon) || !GetIsItemPropertyValid(ip))
return;
if (GetIsObjectValid(oCreator) &&
GetArea(oCreator) == GetArea(OBJECT_SELF) &&
GetFactionEqual(oCreator, OBJECT_SELF) &&
!GetPCOption(oCreator, PCOPTION_NOAUTOREBUFF)) {
int nHasSpell = GetHasSpell(nSpellId, oCreator);
if (nHasSpell > 0) {
DecrementRemainingSpellUses(oCreator, nSpellId);
if (nHasSpell > GetHasSpell(nSpellId, oCreator)) {
FloatingTextStringOnCreature(C_MED_ORANGE + "* Auto-recasting " +
SFGetSpellName(nSpellId) + " for " + GetName(OBJECT_SELF) + "! *" + C_END,
oCreator, FALSE);
SetLocalInt(oWeapon, "OnHitCreatorRests_" + IntToString(nSpellId), GetLocalInt(oCreator, "Rests"));
return;
}
FloatingTextStringOnCreature(C_MED_ORANGE + "* Unable to auto-recast " +
SFGetSpellName(nSpellId) + " for " + GetName(OBJECT_SELF) + "! *" + C_END,
oCreator, FALSE);
} else {
FloatingTextStringOnCreature(C_MED_ORANGE + "* No casts left to refresh " +
SFGetSpellName(nSpellId) + " for " + GetName(OBJECT_SELF) + "! *" + C_END,
oCreator, FALSE);
}
}
float fDur = GetItemPropertyDurationRemaining(ip) / 2.0;
SetItemPropertiesFromSpellDurations(oWeapon, nSpellId, fDur);
if (GetIsObjectValid(oCreator))
SetLocalInt(oWeapon, "OnHitCreatorRests_" + IntToString(nSpellId), GetLocalInt(oCreator, "Rests"));
string sBy = GetLocalString(oWeapon, "OnHitCreatorName_" + IntToString(nSpellId));
if (sBy != "")
sBy = "(by " + sBy + ") ";
FloatingTextStringOnCreature(C_MED_ORANGE + "* The " + SFGetSpellName(nSpellId) + " effect " +
sBy + "on your " + GetName(oWeapon) + " has degraded! *" + C_END, OBJECT_SELF, FALSE);
}
void main () {
object oCaster = OBJECT_SELF;
object oWeapon = GetSpellCastItem();
object oTarget = GetSpellTargetObject();
int nBase = GetBaseItemType(oWeapon);
if (nBase > 0 && GetLocalInt(oWeapon, "Unlimited") == nBase) {
string sCreator = GetLocalString(oWeapon, "UnlimitedCreator");
if (sCreator != "") {
object oCreator = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster);
if (GetResRef(oCreator) != sCreator) {
DestroyObject(oWeapon);
return;
}
}
int nIQCR = GetLocalInt(oWeapon, "IQCR");
if ((nIQCR > 0 && GetQuasiclass(oCaster) != nIQCR) ||
(nIQCR < 0 && GetQuasiclass(oCaster) != 0)) {
DestroyObject(oWeapon);
return;
}
SetItemStackSize(oWeapon, 99);
}
//no onhits for summons other than unlim ammo
if (GetLocalInt(oWeapon, "SummonBlockOnHit"))
return;
int nVis = GetLocalInt(oWeapon, "OnHitVFX");
string sDamages = GetLocalString(oWeapon, "OnHitDamages");
string sEffects = GetLocalString(oWeapon, "OnHitEffects");
string sScripts = GetLocalString(oWeapon, "OnHitScripts");
int nBreach = GetLocalInt(oWeapon, "OnHitBreach");
string sProp, sSpellId;
int nPropType, nCostValue, nSpellId, nDegrade, bDegrade = -1;
itemproperty ip = GetFirstItemProperty(oWeapon);
/* check temporary item properties; if they are +1 to a damage type or
* an enhancement bonus, their associated spell may have set onhit
* properties on the weapon */
while (GetIsItemPropertyValid(ip)) {
if (GetItemPropertyDurationType(ip) == DURATION_TYPE_TEMPORARY) {
if (bDegrade < 0) {
int nUptime = GetLocalInt(GetModule(), "uptime");
if (nUptime > GetLocalInt(oWeapon, "OnHitDegradeNext")) {
bDegrade = 1;
SetLocalInt(oWeapon, "OnHitDegradeNext", nUptime + 60);
} else
bDegrade = 0;
}
nPropType = GetItemPropertyType(ip);
nCostValue = GetItemPropertyCostTableValue(ip);
if (nPropType == ITEM_PROPERTY_DAMAGE_BONUS ||
nPropType == ITEM_PROPERTY_KEEN ||
nPropType == ITEM_PROPERTY_ENHANCEMENT_BONUS) {
if ((nSpellId = GetItemPropertySpellId(ip)) > 0) {
sSpellId = IntToString(nSpellId);
if (nPropType == ITEM_PROPERTY_DAMAGE_BONUS && nCostValue == IP_CONST_DAMAGEBONUS_1) {
if ((sProp = GetLocalString(oWeapon, "OnHitDamages_" + sSpellId)) != "") {
if (sDamages != "")
sDamages += " " + sProp;
else
sDamages = sProp;
}
if ((sProp = GetLocalString(oWeapon, "OnHitEffects_" + sSpellId)) != "") {
if (sEffects != "")
sEffects += " " + sProp;
else
sEffects = sProp;
}
if ((sProp = GetLocalString(oWeapon, "OnHitScripts_" + sSpellId)) != "") {
if (sScripts != "")
sScripts += " " + sProp;
else
sScripts = sProp;
}
if (nVis == 0)
nVis = GetLocalInt(oWeapon, "OnHitVFX_" + sSpellId);
nCostValue = GetLocalInt(oWeapon, "OnHitBreach_" + sSpellId);
if (nBreach == 0 || nCostValue > nBreach)
nBreach = nCostValue;
}
/* check for property degradation once per minute; require 2 minutes for
* the first degrade and 1 minute for each subsequent one. */
if (bDegrade && (nDegrade = GetLocalInt(oWeapon, "OnHitDegrade_" + sSpellId) > 0)) {
object oCreator = GetLocalObject(oWeapon, "OnHitCreator_" + sSpellId);
if (!GetIsObjectValid(oCreator) ||
GetArea(oCreator) != GetArea(oCaster) ||
!GetFactionEqual(oCreator, oCaster) ||
GetLocalInt(oCreator, "Rests") > GetLocalInt(oWeapon, "OnHitCreatorRests_" + sSpellId)) {
int nStage = GetLocalInt(oCaster, "OnHitDegradeStage_" + sSpellId) + 1;
if (nStage > 1)
DegradeOnHitProperties(oWeapon, ip, nSpellId, nDegrade, oCreator);
SetLocalInt(oCaster, "OnHitDegradeStage_" + sSpellId, 1);
} else {
DeleteLocalInt(oCaster, "OnHitDegradeStage_" + sSpellId);
}
}
}
}
}
ip = GetNextItemProperty(oWeapon);
}
struct SubString ss;
/* apply unique on-hit scripts */
if (sScripts != "") {
ss.rest = sScripts;
while (ss.rest != "") {
ss = GetFirstSubString(ss.rest);
ExecuteScript(ss.first, oCaster);
}
}
/* if any scripts set OnHitAbort, stop further on-hit processing; don't
* check this in the script loop proper to ensure all scripts always fire. */
if (GetLocalInt(oWeapon, "OnHitAbort")) {
DeleteLocalInt(oWeapon, "OnHitAbort");
return;
}
/* if the weapon has any added damage types (e.g. Flame Weapon), apply them */
if (sDamages != "") {
int i, nDamage, nDamages = 0, nRace = GetRacialType(oTarget);
effect eDamage;
struct IntList il;
ss.rest = sDamages;
while (ss.rest != "") {
ss = GetFirstSubString(ss.rest);
il = GetIntList(ss.first, ",");
/* check if a favored enemy mask was specified */
if (il.i4) {
if (nRace > 30 || !(il.i4 & (1 << nRace)))
continue;
}
switch (il.i1) {
case -1: /* AA elemental bows */
il.i1 = 5;
il.i2 = 20;
break;
case -2: /* AA exotic bows */
il.i1 = 4;
il.i2 = 20;
break;
}
if (il.i0 == DAMAGE_TYPE_NEGATIVE &&
nRace == RACIAL_TYPE_UNDEAD &&
GetLocalInt(oTarget, "HealType") != DAMAGE_TYPE_POSITIVE)
continue;
if (nVis < 1) {
switch (il.i0) {
case DAMAGE_TYPE_ACID: nVis = VFX_COM_HIT_ACID; break;
case DAMAGE_TYPE_COLD: nVis = VFX_COM_HIT_FROST; break;
case DAMAGE_TYPE_ELECTRICAL: nVis = VFX_COM_HIT_ELECTRICAL; break;
case DAMAGE_TYPE_FIRE: nVis = VFX_COM_HIT_FIRE; break;
case DAMAGE_TYPE_SONIC: nVis = VFX_COM_HIT_SONIC; break;
case DAMAGE_TYPE_DIVINE: nVis = VFX_COM_HIT_DIVINE; break;
case DAMAGE_TYPE_MAGICAL: nVis = VFX_IMP_CHARM; break;
case DAMAGE_TYPE_NEGATIVE: nVis = VFX_COM_HIT_NEGATIVE; break;
case DAMAGE_TYPE_POSITIVE: nVis = VFX_IMP_MAGBLUE; break;
default: nVis = VFX_COM_BLOOD_SPARK_MEDIUM; break;
}
}
nDamage = il.i1 + il.i3;
for (i = 0; i < il.i1; i++)
nDamage += Random(il.i2);
if (nDamages == 0)
eDamage = EffectDamage(nDamage, il.i0);
else
eDamage = EffectLinkEffects(eDamage, EffectDamage(nDamage, il.i0));
nDamages++;
}
/* ensure added damage types don't cause kickback */
if (GetLocalInt(oTarget, "Feedback_1_Type")) {
SetLocalInt(oTarget, "FeedbackIgnore", nDamages);
SetLocalObject(oTarget, "FeedbackIgnore", oCaster);
}
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget);
}
/* apply a visual effect if one was set, which may have been dynamically
* determined in the damage loop */
if (nVis > 0)
ApplyVisualToObject(nVis, oTarget);
/* apply special on-hit effects */
if (sEffects != "") {
int nPos, nDurType, nSubType, bPetrify, bSelf, bLast = FALSE;
float fDur;
string sDur;
effect eLink;
struct IntList il;
struct SubString css;
ss.rest = sEffects;
/* parse each effect in turn */
while (ss.rest != "") {
ss = GetFirstSubString(ss.rest);
css = GetFirstSubString(ss.first, "|");
/* if the effect has any pre-effect checks (e.g. random chance, saving throw),
* make them before applying the effect */
if (css.rest != "") {
ss.first = css.first;
if (css.rest == "&") {
if (!bLast)
continue;
} else if (css.rest == "^") {
if (!(bLast = !bLast))
continue;
} else {
bLast = !(CheckDynamic(css.rest, oTarget, OBJECT_SELF) > 0);
/* CheckDynamic returns TRUE if they pass; we invert it above so
* bLast is 'did the last effect go through' */
if (!bLast)
continue;
}
}
if ((nPos = FindSubString(ss.first, "#")) >= 0) {
nSpellId = StringToInt(GetStringRight(ss.first, GetStringLength(ss.first) - (nPos + 1)));
ss.first = GetStringLeft(ss.first, nPos);
if (GetHasSpellEffect(nSpellId, oTarget))
continue;
} else
nSpellId = -1;
/* check if the effect has an associated visual */
if ((nPos = FindSubString(ss.first, "/")) >= 0) {
nVis = StringToInt(GetStringRight(ss.first, GetStringLength(ss.first) - (nPos + 1)));
ss.first = GetStringLeft(ss.first, nPos);
} else
nVis = 0;
nSubType = 0;
bPetrify = FALSE;
bSelf = FALSE;
/* check if the effect has a duration parameter specified; if so, apply it;
* otherwise, determine the duration of the effect automatically */
if ((nPos = FindSubString(ss.first, ":")) >= 0) {
sDur = GetStringRight(ss.first, GetStringLength(ss.first) - (nPos + 1));
ss.first = GetStringLeft(ss.first, nPos);
/* check if a subtype was specified */
if ((nPos = FindSubString(sDur, ":")) >= 0) {
string sSub = GetStringRight(sDur, 1);
if (sSub == "E")
nSubType = SUBTYPE_EXTRAORDINARY;
else if (sSub == "S")
nSubType = SUBTYPE_SUPERNATURAL;
sDur = GetStringLeft(sDur, nPos);
}
if ((nPos = FindSubString(sDur, "+")) >= 0) {
int nRandDur = StringToInt(GetStringRight(sDur, GetStringLength(sDur) - (nPos + 1)));
fDur = StringToFloat(GetStringLeft(sDur, nPos)) + Random(nRandDur);
} else
fDur = StringToFloat(sDur);
if (fDur < 0.0) {
fDur = 0.0;
nDurType = DURATION_TYPE_PERMANENT;
} else if (fDur == 0.0)
nDurType = DURATION_TYPE_INSTANT;
else
nDurType = DURATION_TYPE_TEMPORARY;
int bFirst = TRUE;
struct SubString sss;
sss.rest = ss.first;
while (sss.rest != "") {
sss = GetFirstSubString(sss.rest, ";");
il = GetIntList(sss.first, ",");
if (bFirst) {
bFirst = FALSE;
eLink = EffectDynamic(il.i0, il.i1, il.i2, il.i3, il.i4, il.i5, il.i6);
} else
eLink = EffectLinkEffects(eLink, EffectDynamic(il.i0, il.i1, il.i2, il.i3, il.i4, il.i5, il.i6));
}
} else {
il = GetIntList(ss.first, ",");
if (il.i0 < 100) {
fDur = 6.0;
nDurType = DURATION_TYPE_TEMPORARY;
} else {
fDur = 0.0;
nDurType = DURATION_TYPE_INSTANT;
}
if (il.i0 == EFFECT_TYPE_HEAL)
bSelf = TRUE;
else if (il.i0 == EFFECT_TYPE_PETRIFY)
bPetrify = TRUE;
eLink = EffectDynamic(il.i0, il.i1, il.i2, il.i3, il.i4, il.i5, il.i6);
}
if (bSelf) {
if (GetCurrentHitPoints(oCaster) < GetMaxHitPoints(oCaster)) {
if (nVis > 0)
ApplyVisualToObject(nVis, oCaster);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oCaster);
}
} else {
if (nVis > 0)
ApplyVisualToObject(nVis, oTarget);
else if (nVis < 0)
eLink = EffectLinkEffects(eLink, EffectVisualEffect(-nVis));
if (nSubType == SUBTYPE_EXTRAORDINARY)
eLink = ExtraordinaryEffect(eLink);
else if (nSubType == SUBTYPE_SUPERNATURAL)
eLink = SupernaturalEffect(eLink);
if (nSpellId >= 0)
SetEffectSpellId(eLink, nSpellId);
&am
#16
Posté 04 janvier 2014 - 02:00
if (bPetrify)
ApplyPetrifyEffect(nDurType, eLink, oTarget, fDur);
else
ApplyEffectToObject(nDurType, eLink, oTarget, fDur);
}
}
}
if (nBreach != 0) {
if (nBreach < 0) {
int nLevel = GetHitDiceIncludingLLs(oCaster);
if (nLevel > 60)
nLevel = 60;
DoBreachAndDispel(oTarget, nLevel, 100, 0, 0, SPELL_DISPEL_MAGIC);
} else if (nBreach > 100) {
DoBreachAndDispel(oTarget, nBreach - 100, 100, 6, 10, SPELL_MORDENKAINENS_DISJUNCTION);
} else {
DoBreachAndDispel(oTarget, 0, 0, nBreach, nBreach + 1);
}
}
}
Funky
#17
Posté 04 janvier 2014 - 03:11
In other words, let's say I wanted to damage anything within 5 meters of a location every second until some outside criteria is met. In the past, I've used something like this...
void DoStuff (location lTarget)
{
if (condition) return;
object oTarget = GetFirstObjectInShape(lTarget);
while (GetIsObjectValid(oTarget))
{
DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, oTarget, EffectDamage(10))
oTarget = GetNextObjectInShape(lTarget)
}
DelayCommand(1.0, DoStuff(lTarget));
}
Obviously that's not fully fleshed out and has some stuff incorrect, but you get the gist. The DelayCommand on the damage is to avoid an AI bug I found here: http://social.biowar...1237/2#17510033
From what you've said, that looks like it has two memory leaks: passing the location recursively and delaying the damage call (which has an effect). I'm not sure how to avoid the latter (would have to edit the AI itself, I think), but for the former I could do something like the following...
void DoStuff (object oArea, vector vVector, float fOrientation)
{
if (condition) return;
location lTarget = Location(oArea, vVector, fOrientation);
object oTarget = GetFirstObjectInShape(lTarget);
while (GetIsObjectValid(oTarget))
{
DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, oTarget, EffectDamage(10))
oTarget = GetNextObjectInShape(lTarget)
}
DelayCommand(1.0, DoStuff(oArea, vVector, fOrientation));
}
How expensive is creating that location each time? Presumably the same amount of memory or less is taken up since the entire structure isn't being passed but it then has to reassemble the location each call.
#18
Posté 04 janvier 2014 - 03:57
Slightly longer answer is that the effect DC doesn't matter, as I'm almost certain that that DC will not actually cause a memory leak, since it doesn't directly recurse (it's only when you have structs in custom recursing functions, as with the location, as I understand it, that you get leaks). You could test for a memory leak wiith with the NWNX functions I mentioned, but I can tell you know that it's extremely unlikely, as we use ZILLIONS of Delayed Effect Applications. Here's a Find of just those that specify fDelay, rather than a specific number or another variable, in our mod. I'm going to assume that the post length limit here is going to screw me again, and just tell you up front that it occurs in 567 different places in our mod.
Funky
[quote]
aa_con (42): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
abo_eldest_ebt (55): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
abo_endcombat (168): DelayCommand(fDelay, ApplyDirectDamage(oTarget, nDamage, DAMAGE_TYPE_PSIONIC, DAMAGE_POWER_ENERGY));
abo_endcombat (1189): DelayCommand(fDelay, ApplyDirectDamage(oTarget, d20(12), DAMAGE_TYPE_ECTOPLASMIC,
abo_endcombat (1414): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget));
abo_final_enter (72): DelayCommand(fDelay, ApplyVisualAtLocation(nVis, lLoc));
aby_boss_heart (352): DelayCommand(fDelay, ApplyVisualToObject(CEPVFX_FNF_GAS_EXPLOSION_DISPEL, oTarget));
aby_boss_heart (413): DelayCommand(fDelay, ApplyVisualToObject(VFX_FNF_GAS_EXPLOSION_FIRE, oTarget));
aby_boss_heart (515): DelayCommand(fDelay, ApplyVisualToObject(VFX_FNF_GAS_EXPLOSION_ACID, oTarget));
aby_boss_heart (646): DelayCommand(fDelay, ApplyVisualToObject(CEPVFX_FNF_GAS_EXPLOSION_COLD, oTarget));
aby_endcombat (508): DelayCommand(fDelay, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eBeam, Location(oArea, vBeam, 0.0)));
aby_endcombat (570): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (595): DelayCommand(fDelay, ApplyDaze(oTarget, d3(1), "The dustman flings a bag of dust at you, sending you into a choking, coughing fit!"));
aby_endcombat (702): DelayCommand(fDelay, ApplyDirectDamage(oTarget, nDamage, DAMAGE_TYPE_PSIONIC, DAMAGE_POWER_ENERGY));
aby_endcombat (879): DelayCommand(fDelay, ApplyDeafness(oTarget, 10, "The vrock's screech deafens you!"));
aby_endcombat (882): DelayCommand(fDelay, ApplyStun(oTarget, 3, "The vrock's screech stuns you!", TRUE, TRUE, SPELL_SOUND_BURST));
aby_endcombat (908): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (942): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (943): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (991): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (1045): DelayCommand(fDelay, ApplyDaze(oTarget, 3, "A burst of light from the nalfeshnee dazes you!", TRUE, TRUE, SPELL_DAZE));
aby_endcombat (1067): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (1134): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (1316): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eVis, oTarget));
aby_endcombat (1541): DelayCommand(fDelay, ApplyVisualToObject(CEPVFX_IMP_HEALING_S_BLUE, oTarget));
aby_endcombat (1542): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(d100(3 + nParagon)), oTarget, 9.0));
aby_endcombat (1543): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 9.0));
aby_endcombat (1554): DelayCommand(fDelay, ApplyVisualToObject(GAOVFX_IMP_NEGATIVE_ENERGY_BLUE, oTarget));
aby_endcombat (1555): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 30.0));
aby_endcombat (1593): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (1598): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEff, oTarget));
aby_endcombat (1639): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (1640): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (1680): DelayCommand(fDelay, ApplySleep(oTarget, 10, "The droning buzz from the chasme makes you fall asleep!"));
aby_endcombat (1682): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (1683): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (1745): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (1872): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (1873): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (1931): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 9.0));
aby_endcombat (1975): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2007): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2097): DelayCommand(fDelay, ApplyVisualToObject(GAOVFX_IMP_NEGATIVE_ENERGY_GREEN, oTarget));
aby_endcombat (2098): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (2173): DelayCommand(fDelay, ApplyVisualToObject(VFX_FNF_GAS_EXPLOSION_GREASE, oTarget));
aby_endcombat (2174): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 30.0));
aby_endcombat (2183): DelayCommand(fDelay, ApplyVisualToObject(VFX_FNF_GAS_EXPLOSION_GREASE, oTarget));
aby_endcombat (2184): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eCon, oTarget, 60.0));
aby_endcombat (2212): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2242): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2243): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (2277): DelayCommand(fDelay, ApplyStun(oTarget, 3, "The uzollru's gaze stuns you!", TRUE, TRUE, SPELLABILITY_GAZE_STUNNED));
aby_endcombat (2335): DelayCommand(fDelay, ApplySleep(oTarget, 10, "The soporific aura around the guecubu causes you to fall asleep!"));
aby_endcombat (2337): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 30.0));
aby_endcombat (2338): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2570): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 9.0));
aby_endcombat (2619): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (2620): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2773): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2809): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2845): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2881): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (2915): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (3018): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEff, oTarget));
aby_endcombat (3145): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_DOOM, oTarget));
aby_endcombat (3146): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 30.0));
aby_endcombat (3222): DelayCommand(fDelay, ApplyFumeEffect(oTarget, eEff, eVis));
aby_endcombat (3263): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (3264): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEff, oTarget));
aby_endcombat (3376): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (3377): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 9.0));
aby_endcombat (3473): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (3599): DelayCommand(fDelay, ApplyBlindness(oTarget, 10, "You are blinded by a burst of spores from the sporebat!"));
aby_endcombat (3630): DelayCommand(fDelay, ApplyConfuse(oTarget, 5, "You are confused by the spores emanating from the violet fungus!"));
aby_endcombat (3631): DelayCommand(fDelay, ApplyVisualToObject(GAOVFX_IMP_NEGATIVE_ENERGY_PURPLE, oTarget));
aby_endcombat (3654): DelayCommand(fDelay, ApplyDaze(oTarget, 2, "You are dazed by a spray of spores from the violet fungus!"));
aby_endcombat (3743): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (3744): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (3774): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (3854): DelayCommand(fDelay, ApplyDirectDamage(oTarget, d20(5 + nParagon), DAMAGE_TYPE_VILE,
aby_endcombat (3878): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (3879): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 30.0));
aby_endcombat (3922): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4014): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (4016): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4017): DelayCommand(fDelay, ApplyBlindness(oTarget, 10, "You are blinded by the bloody spray from the sanguineous drinker!"));
aby_endcombat (4186): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4337): DelayCommand(fDelay, ApplyFear(oTarget, 10, "The sight of the manitou fills you with terror!"));
aby_endcombat (4384): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4442): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (4482): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4491): DelayCommand(fDelay, ApplyDaze(oTarget, 3, "The bile spit from the sibriex causes you to gag and retch!"));
aby_endcombat (4603): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4643): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4644): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (4794): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4795): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (4831): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4836): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget));
aby_endcombat (4901): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (4902): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eAwe, oTarget, 6.0));
aby_endcombat (5007): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5141): DelayCommand(fDelay, ApplyDirectDamage(oTarget, d20(8 + nParagon), DAMAGE_TYPE_VILE,
aby_endcombat (5165): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5166): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 30.0));
aby_endcombat (5211): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5288): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5320): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5352): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5384): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5443): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5549): DelayCommand(fDelay, ApplySleep(oTarget, 10, "The soporific aura around Zhelamiss you to fall asleep!"));
aby_endcombat (5808): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (5809): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (6010): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (6050): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (6062): DelayCommand(fDelay, ApplyDaze(oTarget, 3, "The bile spit from Fo-oon-fol causes you to gag and retch!"));
aby_endcombat (6217): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
aby_endcombat (6218): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
aby_endcombat (6305): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 24.0));
aby_onhit (248): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ac_mstorm_inc (136): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eImpact, oTarget));
ac_mstorm_inc (150): DelayCommand(fDelay, ApplyDirectDamage(oTarget, nSecondaryDamage, nSecondaryDamType,
ac_mstorm_inc (159): DelayCommand(fDelay, ApplyDirectDamage(oTarget, nDamage, nDamType, DAMAGE_POWER_ENERGY,
ac_mstorm_inc (163): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget));
ac_spell_inc (1744): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ac_spell_inc (1782): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nVis), oTarget));
ac_vtouch_inc (28): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eImpact, si.target));
ac_vtouch_inc (29): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, si.target));
ca_rdd_buffet (40): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));
ca_rdd_buffet (43): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eKnock, oTarget, 6.0f));
ca_rdd_buffet (44): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis3, oTarget, 4.0f));
ca_sd_decoy (32): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oTarget));
ca_sd_decoy (33): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVisHeal, oTarget));
ca_sd_decoy (36): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));
ca_sd_decoy (37): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
drow_jud_cm_end (45): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oPC));
drow_jud_cm_end (46): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC));
dul_endcombat (96): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d100(1), DAMAGE_TYPE_PIERCING), oTarget));
dul_endcombat (243): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_FROST_S, oTarget));
dul_endcombat (410): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_FROST_S, oTarget));
dul_endcombat (461): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_DEATH, oTarget));
dul_endcombat (467): DelayCommand(fDelay, ApplyFear(oTarget, d6(), "The marzanna's gaze fills you with dread!", TRUE, FALSE, SPELL_FEAR));
dul_endcombat (508): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_SONIC, oTarget));
dul_endcombat (509): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 60.0));
dul_endcombat (585): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_NEGATIVE_ENERGY, oTarget));
ele_endcombat (137): DelayCommand(fDelay, ApplyKnockdown(oTarget, 1, "The air elemental's vortex knocks you down!",
ele_endcombat (177): DelayCommand(fDelay, ApplyDaze(oTarget, 1 + d3(1), "The earth elemental slams the ground beneath you, leaving you dazed!"));
ele_endcombat (222): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, RoundsToSeconds(nResult) + 3.0));
ele_endcombat (258): DelayCommand(fDelay, ApplyKnockdown(oTarget, 1, "The water elemental's vortex knocks you down!",
ele_endcombat (454): DelayCommand(fDelay, ApplyFear(oTarget, d2(), "The wolf's howl fills you with terror!"));
ele_endcombat (525): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEff, oTarget));
ele_endcombat (526): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ele_endcombat (720): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ele_endcombat (747): DelayCommand(fDelay, ApplyDaze(oTarget, d4(1), "The sirine's song dazes you!", TRUE, TRUE, SPELL_DAZE));
ele_endcombat (998): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_HEALING_S, oHeal));
ele_endcombat (1074): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_FLAME_S, oTarget));
ele_onhit (34): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ele_onhit (65): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ele_onhit (96): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ele_onhit (127): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ely_death_trigg (44): AssignCommand(oArea, DelayCommand(fDelay, ApplyEffectToObject(0, eDam, oTarget)));
ely_death_trigg (45): AssignCommand(oArea, DelayCommand(fDelay, ApplyEffectToObject(0, eImp, oTarget)));
ely_endcombat (33): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));
ely_endcombat (34): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eImp, oTarget));
ely_endcombat (50): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));
ely_endcombat (51): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eImp, oTarget));
ely_endcombat (1015): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
ely_endcombat (1016): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oTarget));
es_annihilation (54): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
es_dust_to_dust (40): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
es_dust_to_dust (41): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, fDur));
es_primal_catacl (125): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
es_winteriscomin (27): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
es_winteriscomin (28): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, fDur));
ev_lifetransrod (104): AssignCommand(oArea, DelayCommand(fDelay, ApplyResurrection(oTarget, oPC)));
fa_greatersmite (145): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
fa_greatersmite (146): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eSnd, oTarget));
fey_endcombat (106): DelayCommand(fDelay, ApplyDaze(oTarget, d3(1), "The corollax's color spray dazes you!"));
fey_endcombat (172): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
fey_endcombat (232): DelayCommand(fDelay, ApplyDaze(oTarget, d3(1), "The faerie dragon's breath dazes you!"));
fey_endcombat (273): DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget));
fey_endcombat (363): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_HEAD_FIRE, oPC));
fey_endcombat (402): DelayCommand(fDelay, ApplyConfuse(oPC, d3(1), "The rascal's arrow confuses you!", TRUE, TRUE, SPELL_CONFUSION));
fey_endcombat (424): DelayCommand(fDelay, ApplyVisualToObject(VFX_IMP_HEAD_ODD, oPC));
fey_endcombat (503):
Modifié par FunkySwerve, 04 janvier 2014 - 03:59 .
#19
Posté 04 janvier 2014 - 04:08
FunkySwerve wrote...
Short answer is: I don't know, try profiling it.
This? http://www.nwnx.org/...id=doc_profiler
Never used it -- anything specific I should know about using it that wouldn't be obvious?
FunkySwerve wrote...
it's only when you have structs in custom recursing functions, as with the location, as I understand it, that you get leaks
Phew! Thanks.
#20
Posté 04 janvier 2014 - 03:11
Funky
#21
Posté 04 janvier 2014 - 04:08
This. I might repeat myself, but.. if you would know how many code is executed by single creature during usual fight, you wouldn't be wasting your time to debates whether to use GOBT, GNOBT or GWBTFunkySwerve wrote...
This is an old piece of misinformation which became more prevalent around the time when sonic onhit effects were added. We use onhits like crazy, and they don't cause problems.There are a lot of ways to reduce lag, the main thing is to be extra careful about using any kind of combat scripting, especially items that use OnHit effects (that can seriously spike your server with 10 - 60+ people firing 2 - 6 scripts a round)... (10 monks dual welding kamas with on hit cast spell can get insane fast!)
#22
Posté 04 janvier 2014 - 09:24
So if you were build a treasure system with say a 5 minute DC on it...
With 20 players accessing treasure chest 1 to 4 times a minute, the memory would build up...
So, yes it is technically something to avoid in general.
Of course like I said before, short ones aren't so bad, but long ones are an issue, but mainly only if it's something accessed a lot. I use to use delay commands, now I use 1 heartbeat to track time passed in the module, and check the heart beat vs a local variable to see how much time has passed instead. (Yes I know there is a better way using nwnx)
Looping scripts should also be avoided or very minimal as well... (1-8 in a module shouldn't be an issue.)
(Personally I avoid heartbeats like the plague!)
Modifié par _Guile, 04 janvier 2014 - 09:27 .
#23
Posté 05 janvier 2014 - 12:00
I suggest reading the delagging tutorial linked above. I was only taking issue with calling them a 'big one' - eliminating unnecessary delaycommands is listed at #6 in the tutorial. They way you were talking about them was reminiscent of the way a lot of clueless scripters talk about heartbeats, as well, which results in them coding all sorts of oddities instead of a nice, clean heartbeat. Speaking of which..._Guile wrote...
Technically every delay command you have running requires server resources...
So if you were build a treasure system with say a 5 minute DC on it...
With 20 players accessing treasure chest 1 to 4 times a minute, the memory would build up...
So, yes it is technically something to avoid in general.
Of course like I said before, short ones aren't so bad, but long ones are an issue, but mainly only if it's something accessed a lot. I use to use delay commands, now I use 1 heartbeat to track time passed in the module, and check the heart beat vs a local variable to see how much time has passed instead. (Yes I know there is a better way using nwnx)
Looping scripts should also be avoided or very minimal as well... (1-8 in a module shouldn't be an issue.)
(Personally I avoid heartbeats like the plague!)
We use TONS of
recursive scripts (I'm assuming you don't mean 'looping' as in actual
looping, as with while and for loops, as that would be even sillier).
They're often the most efficient, if not the only, way to do something,
and there's absolutely no reason to avoid them. I can't easily run a search for them, since they don't have a uniform aspect other than calling their own function name at various points, but I'm confident we have hundreds, plural, at a minimum. They're incredibly useful, and should absolutely NOT be avoided.
Likewise, heartbeats should also not be avoided, simply used when its appropriate. There's a small section in the tutorial talking about replacing heartbeats with pseudos, but sometimes the heartbeat is the appropriate way to go. Avoiding them 'like the plague' is just wrong. By way of example, here's our module heartbeat, which is often the most appropriate place for heartbeat-style code (unless we're talking monster/NPC heartbeats, which are sometimes useful and warranted) (assuming forum-length limits again, it's 357 lines of fairly dense and heavily-included code)
Funky
#include "hg_inc"
#include "fky_chat_inc"
#include "hg_client_inc"
#include "ac_effect_inc"
#include "ac_itemprop_inc"
void SendResetBroadcast (string sMessage, int bTell, object oMessenger) {
sMessage = "<cþ þ>" + sMessage + "</c>";
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
SendChatLogMessage(oPC, sMessage, oMessenger, (bTell ? 4 : 5));
oPC = GetNextPC();
}
}
void main() {
object oMod = GetModule();
object oMes = GetMessenger();
int nUptime = GetLocalInt(oMod, "uptime");
int nMemory = GetProcessMemoryUsage();
int nMessages = 0, nPlayers = 0;
string sServer = GetLocalString(oMod, "ServerNumber");
string sBootTime = IntToString(GetLocalInt(oMod, "boottime"));
{
object oPC;
for (oPC = GetFirstPC(); GetIsObjectValid(oPC); oPC = GetNextPC()) {
nPlayers++;
RecalculateMovementRate(oPC);
RecalculateDexModifier(oPC);
int nAlarm = GetLocalInt(oPC, "AlarmUptime");
if (nAlarm > 0 && nAlarm <= nUptime) {
DeleteLocalInt(oPC, "AlarmUptime");
SendChatLogMessage(oPC, C_PINK + "[Alarm] " + GetLocalString(oPC, "AlarmMessage") + C_END, oMes, 4);
}
}
SetLocalInt(oMod, "ServerPlayers", nPlayers);
}
SQLExecDirect("SELECT UNIX_TIMESTAMP() - " + sBootTime + ", UTC_TIMESTAMP(), UNIX_TIMESTAMP(), " +
"COUNT(*) FROM user_messages WHERE um_recipient = '*" + sServer + "'");
if (SQLFetch() == SQL_SUCCESS) {
nUptime = StringToInt(SQLGetData(1));
nMessages = StringToInt(SQLGetData(4));
SetLocalInt(oMod, "uptime", nUptime);
SetLocalInt(oMod, "realtime", StringToInt(SQLGetData(3)));
SetLocalString(oMod, "utctime", SQLGetData(2));
SQLExecDirect("UPDATE server_list SET srv_utime = " + IntToString(nUptime) + ", srv_memory = " +
IntToString(nMemory) + ", srv_players = " + IntToString(nPlayers) + " WHERE srv_id = '" + sServer + "'");
SQLExecDirect("SELECT COUNT(*) FROM user_list WHERE u_active > 0");
if (SQLFetch() == SQL_SUCCESS)
SetLocalInt(oMod, "GlobalPlayers", StringToInt(SQLGetData(1)));
SQLExecDirect("SELECT COUNT(*) FROM user_list WHERE u_active > 0 AND u_server_name REGEXP '" + GetStringLeft(sServer, 1) + ".[1-8]'");
if (SQLFetch() == SQL_SUCCESS) {
int nHubPlayers = StringToInt(SQLGetData(1));
SetLocalInt(oMod, "HubPlayers", nHubPlayers);
if (GetStringLeft(sServer, 1) != "1" && GetStringRight(sServer, 1) == "1")
SetNumberOfPlayers(nHubPlayers);
}
}
/* check for server messages if available */
if (nMessages > 0) {
int nGuild;
string sMessage;
string sSQL = "SELECT um_id, um_guild, um_text FROM user_messages WHERE um_recipient = '*" + sServer + "'";
SQLExecDirect(sSQL);
sSQL = "";
while (SQLFetch() != SQL_ERROR) {
if (sSQL == "")
sSQL = "um_id = " + SQLGetData(1);
else
sSQL += " OR um_id = " + SQLGetData(1);
nGuild = StringToInt(SQLGetData(2));
sMessage = SQLDecodeSpecialChars(SQLGetData(3));
if (nGuild == -2) {
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (!(GetPCFilter(oPC, PCFILTER_CHANNELS) & 1))
SendChatLogMessage(oPC, sMessage, oMes);
oPC = GetNextPC();
}
} else if (nGuild == -3) {
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (!(GetPCFilter(oPC, PCFILTER_CHANNELS) & 2))
SendChatLogMessage(oPC, sMessage, oMes);
oPC = GetNextPC();
}
} else if (nGuild == -4) {
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (!(GetPCFilter(oPC, PCFILTER_CHANNELS) & 4))
SendChatLogMessage(oPC, sMessage, oMes);
oPC = GetNextPC();
}
} else if (nGuild < 0) {
SendMessageToDMDMs(sMessage);
SendMessageToPCDMs(sMessage);
SendMessageToDMAdmins(sMessage);
SendMessageToPCAdmins(sMessage);
} else if (nGuild > 0) {
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
if (GetLocalInt(oPC, "Guild") == nGuild && !(GetPCFilter(oPC, PCFILTER_GUILD) & 1))
SendChatLogMessage(oPC, sMessage, oMes);
oPC = GetNextPC();
}
} else
AssignCommand(oMod, SpeakString(sMessage, TALKVOLUME_SHOUT));
}
if (sSQL != "")
SQLExecDirect("DELETE FROM user_messages WHERE " + sSQL);
}
/* scan players for inter-server messages every minute; also check for auto-reset */
if (nUptime >= GetLocalInt(oMod, "LastMessageCheck") + 60) {
SetLocalInt(oMod, "LastMessageCheck", nUptime);
if (nMemory > GetLocalInt(oMod, "resetmemory") && !GetLocalInt(oMod, "resetforce")) {
SetLocalInt(oMod, "resetforce", 1);
if (GetLocalInt(oMod, "resetuptime") > nUptime)
SetLocalInt(oMod, "resetuptime", nUptime - 1);
}
/* update PCs in the user list */
UpdateAllClients(sServer, sBootTime, oMes);
/* check for automatic reset */
int nResetUptime = GetLocalInt(oMod, "resetuptime");
if (nResetUptime > 0 && nUptime > nResetUptime) {
if (!GetIsObjectValid(GetFirstPC())) {
ResetServer();
return;
}
if (nUptime > nResetUptime + 900) {
SendResetBroadcast("SERVER RESET IN 10 SECONDS - SERVER REBOOT IS NOW COMMITTED - CANCELLATION " +
"IS NO LONGER POSSIBLE - PLEASE STAY OUT OF BANK CHESTS - HAVE A NICE DAY!", 1, oMes);
DelayCommand(9.0, ResetServer());//ResetServer has built-in 1 second delay
} else {
int bTell = 0;
int nSeconds = (nResetUptime + 900) - nUptime;
string sMinutes = IntToString((nSeconds / 60) + 1);
if (sMinutes == "1" || sMinutes == "5" || sMinutes == "10" || sMinutes == "15")
bTell = 1;
if (GetLocalInt(oMod, "resetforce"))
SendResetBroadcast("AUTOMATIC SERVER RESET IN " + sMinutes + " MINUTE" +
(sMinutes != "1" ? "S" : "") + " - THIS CANNOT BE ABORTED DUE TO A LOW MEMORY CONDITION", bTell, oMes);
else
SendResetBroadcast("AUTOMATIC SERVER RESET IN " + sMinutes + " MINUTE" +
(sMinutes != "1" ? "S" : "") + " - USE !delayreset TO DELAY THE RESET", bTell, oMes);
}
}
}
/* scan players for epic buffs every minute */
if (nUptime >= GetLocalInt(oMod, "LastImmunityBuffCheck") + 60) {
int i;
object oCreator, oArea, oItem, oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
oArea = GetArea(oPC);
if (GetIsDead(oPC) ||
!GetIsObjectValid(oArea) ||
GetHasEffectOfType(EFFECT_TYPE_CONFUSED, oPC)) {
oPC = GetNextPC();
continue;
}
if (GetHasSpellEffect(HGSPELL_EPIC_AEGIS, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Aegis");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Aegis") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_AEGIS, oPC);
FloatingTextStringOnCreature(
"* The Aegis spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Aegis", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Aegis");
}
if (GetHasSpellEffect(HGSPELL_EPIC_CHANT_OF_WARDING, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Chant");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Chant") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_CHANT_OF_WARDING, oPC);
FloatingTextStringOnCreature(
"* The Chant of Warding spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Chant", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Chant");
}
if (GetHasSpellEffect(HGSPELL_EPIC_ELEMENTAL_SHUNT, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Shunt");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Shunt") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_ELEMENTAL_SHUNT, oPC);
FloatingTextStringOnCreature(
"* The Elemental Shunt spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Shunt", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Shunt");
}
if (GetHasSpellEffect(HGSPELL_EPIC_SHROUD_OF_NATURE, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Shroud");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Shroud") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_SHROUD_OF_NATURE, oPC);
FloatingTextStringOnCreature(
"* The Shroud of Nature spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Shroud", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Shroud");
}
oPC = GetNextPC();
}
SetLocalInt(oMod, "LastImmunityBuffCheck", (nUptime - 5) + Random(11));
}
if (nUptime >= GetLocalInt(oMod, "LastEquipmentBuffCheck") + 60) {
int i;
object oCreator, oArea, oItem, oPC = GetFirstPC();
while (GetIsObjectValid(oPC)) {
oArea = GetArea(oPC);
if (GetIsDead(oPC) ||
!GetIsObjectValid(oArea) ||
GetHasEffectOfType(EFFECT_TYPE_POLYMORPH, oPC) ||
GetHasEffectOfType(EFFECT_TYPE_CONFUSED, oPC)) {
oPC = GetNextPC();
continue;
}
if ((GetLevelByClass(CLASS_TYPE_BLACKGUARD, oPC) + GetLevelByClass(CLASS_TYPE_PALADIN, oPC) + GetLevelByClass(CLASS_TYPE_DIVINE_CHAMPION, oPC)) >= 40) {
if (GetKnowsFeat(FEAT_SMITE_EVIL, oPC))
IncrementRemainingFeatUses(oPC, FEAT_SMITE_EVIL);
if (GetKnowsFeat(FEAT_SMITE_GOOD, oPC))
IncrementRemainingFeatUses(oPC, FEAT_SMITE_GOOD);
// Greater Smite failing to recharge fix.
int nIndex = GetLocalInt(oPC, "GreaterSmiteAbility") - 1;
struct SpecialAbilitySlot sa = GetSpecialAbility(oPC, nIndex);
if ((sa.id == SPELLABILITY_GAZE_DESTROY_EVIL ||
sa.id == SPELLABILITY_GAZE_DESTROY_GOOD ||
sa.id == SPELLABILITY_PULSE_HOLY) && sa.ready == 0 && !GetLocalInt(oPC, "NeedsRest")) {
sa.ready = 1;
SetSpecialAbility(oPC, nIndex, sa);
}
}
if (GetHasSpellEffect(HGSPELL_EPIC_GIRDING_OF_THE_FAITHFUL, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Gird");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Gird") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_GIRDING_OF_THE_FAITHFUL, oPC);
DelayCommand(0.1, VoidRemoveAllItemPropertiesFromSpell(HGSPELL_EPIC_GIRDING_OF_THE_FAITHFUL, oPC));
FloatingTextStringOnCreature(
"* The Girding of the Faithful spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Gird", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Gird");
}
if (GetHasSpellEffect(HGSPELL_EPIC_INSTRUMENTS_OF_FAITH, oPC)) {
oCreator = GetLocalObject(oPC, "EpicCreator_Instr");
if (!GetIsObjectValid(oCreator) || GetArea(oCreator) != oArea || !GetFactionEqual(oCreator, oPC)) {
if (GetLocalInt(oPC, "EpicCollapse_Instr") > 1) {
RemoveEffectsFromSpell(HGSPELL_EPIC_INSTRUMENTS_OF_FAITH, oPC);
DelayCommand(0.1, VoidRemoveAllItemPropertiesFromSpell(HGSPELL_EPIC_INSTRUMENTS_OF_FAITH, oPC));
FloatingTextStringOnCreature(
"* The Instruments of Faith spell on you collapses without the support of its caster! *",
oPC, FALSE);
} else
AddLocalInt(oPC, "EpicCollapse_Instr", 1);
} else
DeleteLocalInt(oPC, "EpicCollapse_Instr");
}
oPC = GetNextPC();
}
SetLocalInt(oMod, "LastEquipmentBuffCheck", (nUptime - 5) + Random(11));
}
}
#24
Posté 06 janvier 2014 - 04:45
#25
Posté 06 janvier 2014 - 05:54
_Guile wrote...
Another one I'd like to point out is, don't use intensive OnClientEnter / OnEquipItem / OnUnEquipItem event scripting... Like looping through a player's inventory or properties on items, rather use a trigger or something to do that, after they have entered the module.. The reason I tell you this is because, when a client enters the module these events fire, the more intensive the scripting is, the more likely it will cause issues on a server, like 10 people logging in at once can severely slow down everyone logging back on to the server if a bunch of scripts are firing on each item they have equipped... (Just A Tip)
Trying to avoid using those events is like trying to type while avoiding use of your fingers. A far better solution to that is to disable most parts of some events oncliententer. See, for example, the beginning of our onequip script:
void main() {
object oPC = GetPCItemLastEquippedBy();
if (GetLocalInt(oPC, "MidPolymorph") || (ScanForPolymorphEffect(oPC) > -2))
return;
if (!GetIsPC(oPC) || GetIsDM(oPC))
return;
object oItem = GetPCItemLastEquipped();
object oArea = GetArea(oPC);
string sItemName = GetName(oItem);
if (GetLocalInt(oPC, "DebugItemEvents")) {
SendMessageToPC(oPC, COLOR_WHITE + "Item Event: Equip" +
", Owner: " + GetName(oPC) + " (" + GetObjectString(oPC) +
"), Area: " + GetName(oArea) + " (" + GetObjectString(oArea) +
"), Item: " + sItemName + " (" + GetObjectString(oItem) +
") [x" + IntToString(GetItemStackSize(oItem)) + "]" + COLOR_END);
}
/* ensures it doesn't run oncliententer - */
if (!GetIsObjectValid(GetArea(oPC)))
return;
*snip*
That check cuts off the next 260 lines of incredibly dense, highly-included code from executing oncliententer. Simple, non?
Likewise, for onacquire:
void main() {
/* uncommenting below line would activate tag-based scripting for onacquire,
* but we currently only use it for onactivate and onequip */
/* ExecuteScript( "x2_mod_def_aqu", OBJECT_SELF); */
object oAcquire = GetModuleItemAcquiredBy();
if (!GetIsPC(oAcquire) || GetIsDM(oAcquire))
return;
int i;
object oLoser = GetModuleItemAcquiredFrom();
object oScan, oItem = GetModuleItemAcquired();
if (GetLocalInt(oAcquire, "DebugItemEvents")) {
SendMessageToPC(oAcquire, COLOR_WHITE + "Item Event: Acquire" +
", Acquirer: " + GetName(oAcquire) + " (" + GetObjectString(oAcquire) +
"), Loser: " + GetName(oLoser) + " (" + GetObjectString(oLoser) +
"), Item: " + GetName(oItem) + " (" + GetObjectString(oItem) +
") [x" + IntToString(GetItemStackSize(oItem)) + "]" + COLOR_END);
}
/* below lines stops weapon /armor buffs from carrying over */
RemoveItemPropertiesOfDuration(DURATION_TYPE_TEMPORARY, oItem, -(ITEM_PROPERTY_LIGHT + 1));
if (GetHasInventory(oItem)) {
oScan = GetFirstItemInInventory(oItem);
while (GetIsObjectValid(oScan)) {
RemoveItemPropertiesOfDuration(DURATION_TYPE_TEMPORARY, oScan, -(ITEM_PROPERTY_LIGHT + 1));
oScan = GetNextItemInInventory(oItem);
}
}
/* don't run oncliententer */
if (!GetIsObjectValid(GetArea(oAcquire)))
return;
*snip*
You don't actually need that shunt anywhere else, despite what you said about OnUnequip conflicting with OCE- just onequip and onacquire. You do, however, want a polymorph shunt on OnUnequip, to match the one in OnEquip (posted above):
void main() {
object oPC = GetPCItemLastUnequippedBy();
object oItem = GetPCItemLastUnequipped();
if (GetLocalInt(oPC, "DebugItemEvents")) {
object oArea = GetArea(oPC);
SendMessageToPC(oPC, COLOR_WHITE + "Item Event: Unequip" +
", Owner: " + GetName(oPC) + " (" + GetObjectString(oPC) +
"), Area: " + GetName(oArea) + " (" + GetObjectString(oArea) +
"), Item: " + GetName(oItem) + " (" + GetObjectString(oItem) +
") [x" + IntToString(GetItemStackSize(oItem)) + "]" +
", Force: " + IntToString(GetLocalInt(oPC, "ForceUnequipped")) +
", Poly: " + IntToString(GetLocalInt(oPC, "MidPolymorph")) +
COLOR_END);
}
/* scripts in onequip, onunequip, onunacquire - script here MUST be before
* the "ForceUnequipped" int is deleted below */
if (GetLocalInt(oPC, "MidPolymorph") || (ScanForPolymorphEffect(oPC) > -2))
return;
*snip*
That prevents a lot of potential issues with shifters and such, though you do have to code in the "MidPolymorph" var to your polymorph scripts. See, e.g.:
void ApplyPolymorphEffect (int nDurType, effect ePoly, object oTarget, float fDur=0.0) {
if (GetLocalInt(oTarget, "NoPoly"))
return;
SetLocalInt(oTarget, "MidPolymorph", TRUE);
ApplyEffectToObject(nDurType, ePoly, oTarget, fDur);
DeleteLocalInt(oTarget, "MidPolymorph");
}
and:
act_funfou0021 (67): ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, eEffect, oTarget, 60.0f);
ele_endcombat (637): ApplyPolymorphEffect(DURATION_TYPE_PERMANENT, eEff, oPC);
fky_deathprocess (51): ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, EffectPolymorph(POLYMORPH_TYPE_NULL_HUMAN), oPC, 0.1);
hg_inc (372): void ApplyPolymorphEffect (int nDurType, effect ePoly, object oTarget, float fDur=0.0);
hg_inc (2341): void ApplyPolymorphEffect (int nDurType, effect ePoly, object oTarget, float fDur=0.0) {
loc_endcombat (379): ApplyPolymorphEffect(DURATION_TYPE_PERMANENT, eEff, oPC);
nw_c2_lycat_d (29): DelayCommand(1.0, ApplyPolymorphEffect(DURATION_TYPE_PERMANENT, eShape, OBJECT_SELF));
nw_c2_lyrat_d (29): DelayCommand(1.0, ApplyPolymorphEffect(DURATION_TYPE_PERMANENT, eShape, OBJECT_SELF));
nw_c2_lywolf_d (29): DelayCommand(1.0, ApplyPolymorphEffect(DURATION_TYPE_PERMANENT, eShape, OBJECT_SELF));
nw_s0_polyself (99): ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, eLink, si.target, fDur);
nw_s0_shapechg (132): DelayCommand(0.5, ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, ePoly, si.target, fDur));
nw_s2_elemshape (205): ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, ePoly, OBJECT_SELF, HoursToSeconds(nDuration));
nw_s2_wildshape (226): ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, ePoly, OBJECT_SELF, HoursToSeconds(nDuration));
uro_endcmbtrnd (97): ApplyPolymorphEffect(DURATION_TYPE_PERMANENT, eEff, oPC);
x0_i0_deckmany (684): ApplyPolymorphEffect(DURATION_TYPE_PERMANENT, ePoly, oCaster);
x0_s3_rodwonder (87): ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, ePoly, oTarget, RoundsToSeconds(d4()));
x0_s3_rodwonder (183): ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, ePoly, oCaster, RoundsToSeconds(d4()));
x0_s3_rodwonder (254): ApplyPolymorphEffect(DURATION_TYPE_TEMPORARY, ePoly, oTarget, RoundsToSeconds(d4()));
x2_s2_gwildshp (513): ApplyPolymorphEffect(DURATION_TYPE_PERMANENT, ePoly, OBJECT_SELF);
That's getting into more advanced coding, however, and all of this is sort of tangential to the original topic.
Funky





Retour en haut







