openclonk/docs/sdk/script/Effects.xml

628 lines
34 KiB
XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE doc
SYSTEM '../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../clonk.xsl"?>
<doc>
<title>Effects</title>
<h>Effects</h>
<part>
<text>Any number of effects can be attached to an object. Effects can perform various tasks, thus eliminating the need for helper objects. This is especially of interest for magic spells that act with a given duration. Effects are available from CE on.</text>
<h id="Intro">Introduction</h>
<text>Effects are, roughly put, dynamic timers that can be attached to objects. Effects themselves have no visible or acoustic representation - for this you would use objects or particles instead - effects are pure scripting helpers. They also offer a general interface that can be used to resolve conflicts of temporary status changes made to objects by other objects at the same time.</text>
<text>Here an example of implementing an invisibility spell without effects:</text>
<code>/* Unsichtbarkeitszauber ohne Effektsystem */
local remaining_time; // Zeit, die der Zauber noch aktiv ist
local target; // Unsichtbar gemachter Clonk
local old_visibility; // Vorheriger Sichtbarkeitsstatus
local old_modulation; // Vorherige Farbmodulation
public func Activate(caster, caster2)
{
// Zauberer ermitteln
if (caster2) caster = caster2; target = caster;
// Magie kann man hören, ganz klar ;)
Sound(&quot;Magic*&quot;);
// Vorherige Sichtbarkeit des Zauberers speichern
old_visibility = GetVisibility(caster);
old_modulation = GetClrModulation(caster);
// Zauberer unsichtbar machen
SetVisibility(VIS_Owner | VIS_Allies | VIS_God, caster);
// Halbdurchsichtig bläulich für den Besitzer und Verbündete
SetClrModulation(ModulateColor(old_modulation, RGBa(127,127,255,127)), caster);
// Timer starten: 30 Sekunden unsichtbar
remaining_time = 30;
}
protected func TimerCall()
{
// Zeit zählen
if (remaining_time--) return true;
// Fertig; Objekt entfernen
return(RemoveObject());
}
protected func Destruction()
{
// Zauber wird entfernt: Unsichtbarkeit aufheben
SetClrModulation(old_modulation, target);
SetVisibility(old_visibility, target);
return true;
}</code>
<text>The magic spell object exists until the spell has ended and then makes the clonk visible again. Also, if the spell object is deleted for other reasons (e.g. a scenario section change), the clonk is made visible in the Destruction callback (if this wasn't so, the clonk would remain invisible for ever). Also there is a Timer (defined in the DefCore) called every second. Notice you couldn't just have a single timer call to mark the end of the spell because timer intervals are marked in the engine beginning with the start of the round and you wouldn't know at what point within an engine timer interval the spell would start.</text>
<text>However, there are some problems with this implementation: for example, the magician can not cast a second invisibility spell while he's already invisible - the second spell would have practically no effect, because the end of the first spell would make the clonk visible again. The spell script would have to do some special handling for this case - but not only for multiple invisibility spells, but also for any other spell or script that might affect visibility or coloration of the clonk. Even if this spell would remember the previous value e.g. for coloration it could not handle a situation in which other scripts change the color of their own in the middle of the spell. The same problems occur when multiple scripts modify temporary clonk physcials such as jumping, walking speed, fight strength or visibility range, energy, magic energy etc. Using effects, these conflicts can be avoided.</text>
<h id="Usage">Application</h>
<text>Effects are created using <funclink>AddEffect</funclink> and removed with <funclink>RemoveEffect</funclink>. If an effect was successfully created, the callback Fx*Start is made (* is replaced with the effect name). Depending on the parameters, there can also be an Fx*Timer call for continuous activity such as casting sparks, adjusting energy etc. Finally, when the effect is deleted, the callback Fx*Stop is made. Now, the invisibility spell implemented using effects:</text>
<code>/* Unsichtbarkeitszauber mit Effektsystem */
// EffectVars:
// 0 - Vorheriger Sichtbarkeitsstatus
// 1 - Vorherige Farbmodulation
public func Activate(caster, caster2)
{
// Zauberer ermitteln
if (caster2) caster = caster2;
// Magie kann man hören, ganz klar ;)
Sound(&quot;Magic*&quot;);
// Effekt starten
AddEffect(&quot;InvisPSpell&quot;, caster, 200, 1111, 0, GetID());
// Fertig - das Zauberobjekt wird nun nicht mehr gebraucht
return(RemoveObject());
}
protected func FxInvisPSpellStart(target, num)
{
// Vorherige Sichtbarkeit des Zauberers speichern
EffectVar(0, target, num) = GetVisibility(target);
var old_mod = EffectVar(1, target, num) = GetClrModulation(target);
// Zauberer unsichtbar machen
SetVisibility(VIS_Owner() | VIS_Allies() | VIS_God(), target);
// Halbdurchsichtig bläulich für den Besitzer und Verbündete
SetClrModulation(ModulateColor(old_mod, RGBa(127,127,255,127)), target);
// Fertig
return true;
}
protected func FxInvisPSpellStop(target, num)
{
// Status wiederherstellen
SetClrModulation(EffectVar(1, target, num), target);
SetVisibility(EffectVar(0, target, num), target);
// Fertig
return true;
}</code>
<text>In this case, the magic spell object only starts the effect, then deletes itself immediately. The engine ensures that there are no conflicts with multiple effects modifying the visibility: effects are stored in a stack which ensures that effects are always removed in the opposite order of their addition. For this, there are a couple of extra Start and Stop calls to be made which are explained in detail later.</text>
<text>This effects does not have a timer function. It does, however, define a timer interval of 1111 which will invoke the standard timer function after 1111 frames which will delete the effect. Alternatively, you could define a timer function as such:</text>
<code>protected func FxInvisPSpellTimer(target, num)
{
// Rückgabewert -1 bedeutet, dass der Effekt gelöscht wird
return -1;
}</code>
<text>To store the previous status of the target object, special storage space in <funclink>EffectVar</funclink>() is used. This is necessary because in this case the effect callbacks to not have any object script context. So you cannot access any object local variables in the effect callbacks - remember that the magic spell object which has created the effect is already deleted. If you require an object context in the effect callbacks you can specify one in <funclink>AddEffect</funclink>(). In that case, effect callbacks would be in object local context and the effect would automatically be deleted if the target object is destroyed.</text>
<h id="Priorities">Priorities</h>
<text>When creating an effect you always specify a priority value which determines the effect order. The engine ensures that effects with lower priority are added before effects with a higher priority - even if this means deleting an existing effect of higher priority. So if one effect colors the clonk green and another colors the clonk red, the result will be that of the effect with higher priority. If two effects have the same priority, the order is undefined. However, it is guaranteed that effects added later always notify the Fx*Effect callback of the same priority.</text>
<text>In the case of the red and green color, one effect could also determine the previous coloring and then mix a result using ModulateColor. But priorities also have another function: an effect of higher priority can prevent the addition of other effects of lower priority. This is done through the Fx*Effect callback. If any existing effect reacts to this callback with the return value -1, the new effect is not added (the same applies to the Start callback of the effect itself). Here an example:</text>
<code>/* Feuerimmunitätszauber */
public func Activate(caster, caster2)
{
// Zauberer ermitteln
if (caster2) caster = caster2;
// Magie kann man hören, ganz klar ;)
Sound(&quot;Magic*&quot;);
// Effekt starten
AddEffect(&quot;BanBurnPSpell&quot;, caster, 180, 1111, 0, GetID());
// Fertig - das Zauberobjekt wird nun nicht mehr gebraucht
return(RemoveObject());
}
protected func FxBanBurnPSpellStart(target, num, temporary)
{
// Beim Start des Effektes: Clonk löschen, wenn er brennt
if (!temporary) Extinguish(target);
return true;
}
protected func FxBanBurnPSpellEffect(new_name, target, num, new_num, var1, var2, var3)
{
// Feuer abblocken
if (WildcardMatch(new_name, &quot;*Fire*&quot;)) return -1;
// Alles andere ist OK
return();
}</code>
<text>This effect makes the clonk fire-proof for 30 seconds. The effect is implemented without any Timer or Stop callbacks as the complete functionality is achieved by simply blocking other effects which might have "Fire" as part of their name. This especially applies to the engine internal fire which has exactly the name "Fire". Of course, you could still add a Timer callback for graphic effects so the player can see that his clonk is immune. Also, you could create special visual effects when preventing incineration in FxBanBurnPSpellEffect. For the like:</text>
<code>[...]
protected func FxBanBurnPSpellEffect(new_name, target, num, new_num, var1, var2, var3)
{
// Nur Feuer behandeln
if (!WildcardMatch(new_name, &quot;*Fire*&quot;)) return();
// Beim Feuer haben die drei Extraparameter normalerweise folgende Bedeutung:
// var1: caused_by - Spieler, der für das Feuer verantwortlich ist
// var2: blasted - bool: Ob das Feuer durch eine Explosion zustande kam
// var3: burning_object - Objekt: Anzündendes Objekt
// Anzündendes Objekt löschen
if (var3 &amp;&amp; GetType(var3) == C4V_C4Object()) Extinguish(var3);
// Feuer abblocken
return -1;
}</code>
<text>This would even delete all burning objects which would otherwise incinerate the target object. The type check for var3 avoids possible conflicts with other "Fire" effects that might have differing parameters. Obviously, conflict situations like this should be avoided at all cost.</text>
<text>The following table contains general guidelines for priorities in effects of the original pack:</text>
<text>
<table>
<rowh>
<col>Effect</col>
<col>Priority</col>
</rowh>
<row>
<col>Short special effects</col>
<col>300-350</col>
</row>
<row>
<col>Effects which cannot be banned</col>
<col>250-300</col>
</row>
<row>
<col>Magic ban spell</col>
<col>200-250</col>
</row>
<row>
<col>Permanent magic ban spell</col>
<col>180-200</col>
</row>
<row>
<col>Short term, benevolent magic effects</col>
<col>150-180</col>
</row>
<row>
<col>Short term, malevolent magic effects</col>
<col>120-150</col>
</row>
<row>
<col>Normal Effects</col>
<col>100-120</col>
</row>
<row>
<col>Fire as used by the engine</col>
<col>100</col>
</row>
<row>
<col>Permanent magic effects</col>
<col>50-100</col>
</row>
<row>
<col>Permanent other effects</col>
<col>20-50</col>
</row>
<row>
<col>Internal effects, data storage etc.</col>
<col>1</col>
</row>
</table>
</text>
<text>Generally, effect priorities should be chosen by dependency: if one effect should prevent another it needs a higher priority to do this (even if it is a permanent effect). Short term effects should have a higher priority than long term effects so that short term changes in the object are visible on top of long term effects.</text>
<text>The engine internal fire is of priority 100. So a magic fire which also uses the properties of the engine fire should have a slightly higher priority and should call the respective FxFire* functions within its callbacks. For proper functioning all effect callback (i.e. Start, Timer, and Stop) should be forwarded as each might depend on the action of the others. If this is not possible in your case, you should reimplement the complete fire functionality by script.</text>
<text>Effects with priority 1 are a special case: Other effects are never temporarily removed for them and they are never temporarily removed themselves.</text>
<h id="SpecAddRemove">Special Add/Remove Calls</h>
<text>For the engine to ensure that effects are always removed in opposite order, it might in some cases be necessary to temporarily remove and later re-add existing effects. In these situations, the scripter should obviously take care to remove any object changes and reapply them after re-adding so that other effects will behave accordingly.</text>
<text>Effects are also removed when the target object is deleted or dies - the cause for the removal is passed in the reason parameter to the Remove function of the effect. This can be used e.g. to reanimate a clonk immediately upon his death:</text>
<code>/* Wiederbelebungszauber */
// EffectVars: 0 - Anzahl der zusätzlichen Wiederbelebungen
public func Activate(caster, caster2)
{
// Zauberer ermitteln
if (caster2) caster = caster2;
// Magie kann man hören, ganz klar ;)
Sound(&quot;Magic*&quot;);
// Effekt starten
AddEffect(&quot;ReincarnationPSpell&quot;, caster, 180, 0, 0, GetID());
// Fertig - das Zauberobjekt wird nun nicht mehr gebraucht
return(RemoveObject());
}
protected func FxReincarnationPSpellStart(target, num, temporary)
{
// Nur beim ersten Start: Meldung
if (!temporary) target-&gt;Message(&quot;%s bekommt|ein Extraleben&quot;, GetName(target));
return true;
}
protected func FxReincarnationPSpellStop(target, num, reason, temporary)
{
// Nur beim Tod des Clonks
if (reason != 4) return true;
// Effekt erhalten, wenn der Clonk lebt: Wurde wohl durch einen anderen Effekt wiederbelebt :)
if (GetAlive(target)) return -1;
// Clonk wiederbeleben
SetAlive(1, target);
// Energie geben
DoEnergy(100, target);
// Nachricht
Sound(&quot;Magic*&quot;, 0, target);
target-&gt;Message(&quot;%s|wurde wiederbelebt.&quot;, GetName(target));
// Effekt wirkt nur einmal: Entfernen
return true;
}</code>
<text>This effect reanimates the clonk as many times as he has cast the reanimation spell.</text>
<h id="GlobalEffects">Global Effects</h>
<text>Global effects are effects that are not bound to any target object. With global effects, too, priorities are observed and temporary Add/Remove calls might be necessary to ensure order. Simply imagine all global effects are attached to an imaginary object. Global effects are accessed whenever you specify 0 for the target object.</text>
<text>This can be used to make changes to gravity, sky color, etc. Here's an example for a spell that temporarily reduces gravity and then resets the original value:</text>
<code>/* Gravitationszauber */
// EffectVars: 0 - Vorherige Gravitation
// 1 - Änderung durch den Zauber
public func Activate(caster, caster2)
{
// Magie kann man hören, ganz klar ;)
Sound(&quot;Magic*&quot;);
// Effekt global starten
AddEffect(&quot;GravChangeUSpell&quot;, 0, 150, 37, 0, GetID(), -10);
// Fertig - das Zauberobjekt wird nun nicht mehr gebraucht
RemoveObject();
return true;
}
protected func FxGravChangeUSpellStart(target, num, temporary, change)
{
// Anderen Gravitationseffekt suchen
if (!temporary)
{
var iOtherEffect = GetEffect(&quot;GravChangeUSpell&quot;, target);
if (iOtherEffect == num) iOtherEffect = GetEffect(&quot;GravChangeUSpell&quot;, target, 1);
if (iOtherEffect)
{
// Gravitationsänderung auf diesen Effekt aufrechnen
EffectVar(1, target, iOtherEffect) += change;
SetGravity(GetGravity() + change);
// Selbst entfernen
return -1;
}
}
// Vorherige Gravitation sichern
var iOldGrav = EffectVar(0, target, num) = GetGravity();
// Für nichttemporäre Aufrufe wird change übergeben, und auf den Änderungswert aufgerechnet
if (change) EffectVar(1, target, num) += change;
// Gravitationsänderung setzen
// Die Änderung kann in temporären Aufrufen auch ungleich change sein
SetGravity(iOldGrav + EffectVar(1, target, num));
// Fertig
return true;
}
protected func FxGravChangeUSpellTimer(target, num)
{
// Gravitation in Richtung Normalwert schrauben
var gravity_change = EffectVar(1, target, num);
// Fertig?
if (Inside(gravity_change, -1, 1)) return -1;
// Anpassen
var iDir = -gravity_change/Abs(gravity_change);
EffectVar(1, target, num) += iDir;
SetGravity(GetGravity() + iDir);
return true;
}
protected func FxGravChangeUSpellStop(target, num)
{
// Gravitation Wiederherstellen
SetGravity(EffectVar(0, target, num));
// Effekt entfernen
return true;
}</code>
<text>target will be 0 in all these effect calls. You should still pass this parameter to calls such as <funclink>EffectVar</funclink>() for then it is also possible to attach effects to the magician or perhaps a magic tower. In this case, gravity would automatically be reset as soon as the magician dies or the magic tower is destroyed.</text>
<h id="AddEffects">Adding Effects</h>
<text>In the previous example, several gravitational effects were combined so that the gravity change lasts longer if the spell is casted multiple times. Adding these effects cannot be done in the Effect callback because the gravitation effect might always be prevented by another effect with higher priority (e.g. a no-spells-whatsoever-effect). Through the special Fx*Add callback you can achieve the desired result more easily, or at least in a more structured fashion.</text>
<code>[...]
protected func FxGravChangeUSpellEffect(new_name, target, num)
{
// Falls der neu hinzugefügte Effekt auch eine Gravitationsänderung ist, Interesse am Übernehmen anmelden
if (new_name eq &quot;GravChangeUSpell&quot;) return (-3);
// Ansonsten ignorieren
return();
}
protected func FxGravChangeUSpellAdd(target, num, new_name, target, new_timer, change)
{
// Aufruf erfolgt, wenn der Effekt übernommen werden konnte
// Gravitationsänderung auf diesen Effekt aufrechnen
EffectVar(1, target, num) += change;
SetGravity(GetGravity() + change);
// Fertig
return true;
}</code>
<text>Returning -3 in the Fx*Effect callback will cause the Fx*Add callback to be invoked for the new effect. In this case the new effect is not actually created and the function AddEffect will return the effect number of the effect which has taken on the consequences of the new effect instead. As opposed to the method above this has the advantage that the return value can now be used to determine whether the effect has been created at all.</text>
<h id="UserCallbacks">User Defined Properties</h>
<text>Effects can be easily classified by name. In this way, e.g. all magic spell effects can easily be found through the respective wildcard string. If, however, you want to create user-defined properties which also apply to existing effects you can do this by defining additional effect functions:</text>
<code>global func FxFireIsHot() { return true; } // Feuer is heiß
// Funktion, die alle heißen Effekte vom Zielobjekt entfernt
global func RemoveAllHotEffects(target)
{
// Lokaler Aufruf
if (!target) target=this();
// Alle Effekte durchsuchen und die heißen entfernen
var effect_num, i;
while (effect_num = GetEffect(&quot;*&quot;, target, i++))
if (EffectCall(target, effect_num, &quot;IsHot&quot;))
RemoveEffect(0, target, effect_num);
}</code>
<text>Using <funclink>EffectCall</funclink>() you can of course also call functions in the effect, e.g. to extend certain effects.</text>
<h id="BlendFx">Blind Effects</h>
<text>Sometimes effects only need to be created in order to produce the respective callbacks in other effects - for example with magic spells which don't have any animation or long term effects but which nonetheless might be blocked by other effects. Example for the earthquake spell:</text>
<code>/* Erdbebenzauber */
public func Activate(object caster, object caster2)
{
Sound(&quot;Magic1&quot;);
// Effekt prüfen
var result;
if (result = CheckEffect(&quot;EarthquakeNSpell&quot;, 0, 150)) return(result!=-1 &amp;&amp; RemoveObject());
// Effekt ausführen
if (GetDir(caster)==DIR_Left()) CastLeft(); else CastRight();
// Zauberobjekt entfernen
return(RemoveObject());
}</code>
<text>The return value of <funclink>CheckEffect</funclink>() is -1 if the effect was rejected and a positive value or -2 if the effect was accepted. In both cases the effect itself should not be executed, but in the latter case the Activate function may signal success by returning 1.</text>
<h id="Ext">Extended Possibilities</h>
<text>As every effect has its own data storage, effects are also a way of attaching external data to objects without having to change the object definition for that. Also, simple calls can be delayed, e.g. for one frame after destruction of the object as is done at one place in the Knights pack:</text>
<code>// Der Aufruf von CastleChange muss verzögert erfolgen, damit das Teil zum Aufruf auch wirklich weg ist
// Ansonsten würden FindObject()-Aufrufe dieses Objekt noch finden
public func CastlePartDestruction()
{
// Fundament?
if (basement)
RemoveObject(basement);
// Globaler Temporäreffekt, wenn nicht schon vorhanden
if (!GetEffect(&quot;IntCPW2CastleChange&quot;))
AddEffect(&quot;IntCPW2CastleChange&quot;, 0, 1, 2, 0, CPW2);
return true;
}
protected func FxIntCPW2CastleChangeStop()
{
// Alle BurgTeile benachrichtigen
for(var obj in FindObjects(Find_OCF(OCF_Fullcon), Find_NoContainer())
obj-&gt;~CastleChange();
// Fertig
return true;
}</code>
<text>For this application, the effect name should start with "Int" (especially if working with global callbacks) followed by the id of the object to avoid any kind of name conflict with other effects.</text>
<text>Also, certain action can be taken at the death of an object without having to modify that object's definition. A scenario script might contain:</text>
<code>/* Szenarioscript */
protected func Initialize()
{
// Alle Wipfe manipulieren
for(var obj in FindObjects(Find_ID(WIPF), Find_OCF(OCF_Alive)))
AddEffect(&quot;ExplodeOnDeathCurse&quot;, obj, 20);
}
global func FxExplodeOnDeathCurseStop(target, num, reason)
{
// Bumm!
if (reason == 4) Explode(20, target);
return true;
}</code>
<text>All wipfs present at the beginning of the scenario will explode on death!</text>
<h id="Naming">Naming</h>
<text>So that effects might properly recognize and manipulate each other you should stick to the following naming scheme ("*abc" means endings, "abc*" means prefixes, and "*abc*" means string parts which might occur anywhere in the name).</text>
<text>
<table>
<rowh>
<col>Name section</col>
<col>Meaning</col>
</rowh>
<row>
<col>*Spell</col>
<col>Magic effect</col>
</row>
<row>
<col>*PSpell</col>
<col>Benevolent magic effect</col>
</row>
<row>
<col>*NSpell</col>
<col>Malevolent magic effect</col>
</row>
<row>
<col>*USpell</col>
<col>Neutral magic effect</col>
</row>
<row>
<col>*Fire*</col>
<col>Fire effect - the function <funclink>Extinguish</funclink>() removes all effects of this name</col>
</row>
<row>
<col>*Curse*</col>
<col>Curse</col>
</row>
<row>
<col>*Ban*</col>
<col>Effect preventing other effects (e.g. fire proofness or immunity)</col>
</row>
<row>
<col>Int*</col>
<col>Internal effect (data storage etc.)</col>
</row>
<row>
<col>*Potion</col>
<col>Magic potion</col>
</row>
</table>
</text>
<text>Warning: as function names may not be more than 100 characters in length (and you will lose oversight eventually), you should not stuff too much information into the effect name. Effect names are case sensitive. Also, you should avoid using any of these identifiers in your effect names if your effect doesn't have anything to do with them.</text>
<h id="CBRef">Callback Reference</h>
<part>
<text>The following callbacks are made by the engine and should be implemented in your script according to necessity. * is to be replaced by your effect name.</text>
<h>Fx*Start</h>
<text><code>int Fx*Start (object target, int num, int temporary, any var1, any var2, any var3, any var4);</code></text>
<text>Called at the start of the effect. target is the target object of the effect. num is the effect index. These two parameters can be used to identify the effect, e.g. to manipulate corresponding variables in <funclink>EffectVar</funclink>().</text>
<text>In normal operation the parameter temporary is 0. It is 1 if the effect is re-added after having been temporarily removed and 2 if the effect was temporarily removed and is now to be deleted (in this case a Remove call will follow).</text>
<text>Values var1 to var4 are the additional parameters passed to <placeholder-1/>() and can be custom used.</text>
<text>If temporary is 0 and this callback returns -1 the effect is not created and the corrsponding <funclink>AddEffect</funclink>() call returns 0.</text>
<h>Fx*Stop</h>
<text><code>int Fx*Stop (object target, int num, int reason, bool temporary);</code></text>
<text>When the effect is temporarily or permanently removed. target again is the target object and num the index into the effects list of that object.</text>
<text>reason contains the cause of the removal and can be one of the following values:</text>
<text>
<table>
<rowh>
<col>reason</col>
<col>Meaning</col>
</rowh>
<row>
<col>0</col>
<col>Normal removal</col>
</row>
<row>
<col>1</col>
<col>Temporary removal (temporary is 1).</col>
</row>
<row>
<col>2</col>
<col>Not used</col>
</row>
<row>
<col>3</col>
<col>The target object has been deleted</col>
</row>
<row>
<col>4</col>
<col>The target object has died</col>
</row>
</table>
</text>
<text>The effect can prevent removal by returning -1. This will not help, however, in temporary removals or if the target object has been deleted.</text>
<h>Fx*Timer</h>
<text><code>int Fx*Timer (object target, int num, int time);</code></text>
<text>Periodic timer call, if a timer interval has been specified at effect creation. target and num as usual.</text>
<text>time specifies how long the effect has now been active. This might alternatively be determined using <funclink>GetEffect</funclink>().</text>
<text>If this function is not implemented or returns -1, the effect will be deleted after this call.</text>
<h>Fx*Effect</h>
<text><code>int Fx*Effect (string new_name, object target, int num, int new_num, any var1, any var2, any var3, any var4);</code></text>
<text>A call to all effects of higher priority if a new effect is to be added to the same target object. new_name is the name of the new effect; num is the index of the effect being called; and ew_num is the index of the new effect.</text>
<text>Warning: the new effect is not yet properly initialized and should not be manipulated in any way. Especially the priority field might not yet have been set. The new_num can however be used to request information via <funclink>EffectCall</funclink>(). In calls made by <funclink>CheckEffect</funclink>() new_num is always 0.</text>
<text>This function can return -1 to reject the new effect. As the new effect might also be rejected by other effects, this callback should not try to add effects or similar (see gravitation spell). Generally you should not try to manipulate any effects during this callback.</text>
<text>Return -2 or -3 to accept the new effect. As long as the new effect is not rejected by any other effect, the Fx*Add call is then made to the accepting effect, the new effect is not actually created, and the calling AddEffect function returns the effect index of the accepting effect. The return value -3 will also temporarily remove all higher prioriy effects just before the Fx*Add callback and re-add them later.</text>
<text>var1 bis var4 are the parameters passed to <funclink>AddEffect</funclink>()</text>
<h>Fx*Add</h>
<text><code>int Fx*Add (object target, int num, string new_name, int new_timer, any var1, any var2, any var3, any var4);</code></text>
<text>Callback to the accepting effect if that has returned -2 or -3 to a prior Fx*Effect call. num identifies the accepting effect to which the consequences of the new effect will be added; target is the target object (0 for global effects).</text>
<text>new_timer is the timer interval of the new effect; var1 to var4 are the parameters from AddEffect. Notice: in temporary calls, these parameters are not available - here they will be 0.</text>
<text>If -1 is returned, the accepting effect is deleted also. Logically, the calling AddEffect function will then return -2.</text>
<h>Fx*Damage</h>
<text><code>int Fx*Damage (object target, int num, int damage, int cause);</code></text>
<text>Every effect receives this callback whenever the energy or damage value of the target object is to change. If the function is defined, it should then return whether to allow the change.</text>
<text>This callback is made upon life energy changes in living beings and damage value changes in non-livings - but not vice versa. cause contains the value change and reason:</text>
<text>
<table>
<rowh>
<col>Script constant</col>
<col>cause</col>
<col>Meaning</col>
</rowh>
<row>
<col>FX_Call_DmgScript</col>
<col>0</col>
<col>Damage by script call <funclink>DoDamage</funclink>()</col>
</row>
<row>
<col>FX_Call_DmgBlast</col>
<col>1</col>
<col>Damage by explosion</col>
</row>
<row>
<col>FX_Call_DmgFire</col>
<col>2</col>
<col>Damage by fire</col>
</row>
<row>
<col>FX_Call_DmgChop</col>
<col>3</col>
<col>Damage by chopping (only trees)</col>
</row>
<row>
<col>FX_Call_EngScript</col>
<col>32</col>
<col>Energy value change by script call <funclink>DoEnergy</funclink>()</col>
</row>
<row>
<col>FX_Call_EngBlast</col>
<col>33</col>
<col>Energy loss by explosion</col>
</row>
<row>
<col>FX_Call_EngObjHit</col>
<col>34</col>
<col>Energy loss by object hit</col>
</row>
<row>
<col>FX_Call_EngFire</col>
<col>35</col>
<col>Energy loss by fire</col>
</row>
<row>
<col>FX_Call_EngBaseRefresh</col>
<col>36</col>
<col>Energy recharge at the home base</col>
</row>
<row>
<col>FX_Call_EngAsphyxiation</col>
<col>37</col>
<col>Energy loss by suffocation</col>
</row>
<row>
<col>FX_Call_EngCorrosion</col>
<col>38</col>
<col>Energy loss through acid</col>
</row>
<row>
<col>FX_Call_EngStruct</col>
<col>39</col>
<col>Energy loss of buildings (only "living" buildings)</col>
</row>
<row>
<col>FX_Call_EngGetPunched</col>
<col>40</col>
<col>Energy loss through clonk-to-clonk battle</col>
</row>
</table>
</text>
<text>Generally, the expression "cause &amp; 32" can be used to determine whether the energy or damage values were changed. Energy values are exact, meaning 100,000 is the full energy value of a normal clonk of rank 10 (C4MaxPhysical).</text>
<text>Using this callback, damage to an object can be prevented, lessened, or increased. You could deduct magic energy instead, transfer damage to other objects, or something similar.</text>
</part>
<h id="FnRef">Function Reference</h>
<text>There are the following functions for manipulation of effects:</text>
<text>
<ul>
<li><funclink>AddEffect</funclink>() - for effect creation</li>
<li><funclink>RemoveEffect</funclink>() - for effect removal</li>
<li><funclink>GetEffect</funclink>() - to request effect parameters</li>
<li><funclink>GetEffectCount</funclink>() - for effect counting</li>
<li><funclink>EffectCall</funclink>() - for user defined calls in effects</li>
<li><funclink>EffectVar</funclink>() - to request effect variables</li>
<li><funclink>ChangeEffect</funclink>() - to modify effect names and timers (e.g. for multi-stage effects)</li>
<li><funclink>CheckEffect</funclink>() - to cause effects callbacks without actually creating an effect</li>
</ul>
</text>
</part>
<author>Sven2</author><date>2004-03</date>
</doc>