File: Effects.xml

package info (click to toggle)
openclonk 8.1-4
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 169,520 kB
  • sloc: cpp: 180,479; ansic: 108,988; xml: 31,371; python: 1,223; php: 767; makefile: 145; sh: 101; javascript: 34
file content (660 lines) | stat: -rw-r--r-- 35,505 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
<?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.</text>
    <h>Introduction</h>
    <text>Effects are, roughly put, dynamic timers with properties 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>/* Invisibility spell without effect system */

local remaining_time; // time the spell is still in effect
local target;   // the clonk that has been made invisible
local old_visibility; // previous visibility
local old_modulation; // previous color modulation

func Activate(object caster, object caster2)
{
  // get caster
  if (caster2) caster = caster2; target = caster;

  // save previous visibility and color modulation of the caster
  old_visibility = caster.Visibility;
  old_modulation = caster-&gt;<funclink>GetClrModulation</funclink>();
  
  // make caster only visible to himself and allies, color him like a ghost
  caster.Visibility = <funclink>VIS_Owner</funclink> | <funclink>VIS_Allies</funclink> | <funclink>VIS_God</funclink>;
  caster-&gt;<funclink>SetClrModulation</funclink>(<funclink>ModulateColor</funclink>(old_modulation, <funclink>RGBa</funclink>(127,127,255,127)));
  
  // start timer: 30 seconds invisible
  remaining_time = 30;
}

func TimerCall()
{
  // count down
  if (remaining_time--) return;
  // done: remove object
  <funclink>RemoveObject</funclink>();
}

func Destruction()
{
  // spell is being removed: end invisibility
  target-&gt;<funclink>SetClrModulation</funclink>(old_modulation);
  target.Visibility = old_visibility;
}</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>CreateEffect</funclink> and removed with <funclink>RemoveEffect</funclink>. If an effect was successfully created, the callback <emlink href="script/Effects.html#Construction">Construction</emlink> is made. Depending on the parameters, there can also be an <emlink href="script/Effects.html#TimerCallback">Timer</emlink> call for continuous activity such as casting sparks, adjusting energy etc. Finally, when the effect is deleted, the callback <emlink href="script/Effects.html#Destruction">Destruction</emlink> is made.</text>
    <text>Now, the invisibility spell implemented using effects:</text>
    <code>/* Invisibility spell with effect system  */

// visibility - previous visibility
// old_mod - previous color modulation

func Activate(object caster, object caster2)
{
	// get caster
	if (caster2) caster = caster2;

	// start effect
	caster-><funclink>CreateEffect</funclink>(InvisPSpell, 200, 1111);
	// done - the spell object is not needed anymore
	<funclink>RemoveObject</funclink>();
	return true;
}

local InvisPSpell = new Effect {
	Start = func() {
		// Save the casters previous visibility
		this.visibility = Target.Visibility;
		this.old_mod = Target-&gt;<funclink>GetClrModulation</funclink>();
		// Make the caster invisible
		Target.Visibility = <funclink>VIS_Owner</funclink> | <funclink>VIS_Allies</funclink> | <funclink>VIS_God</funclink>;
		// Semitransparent and slightly blue for owner and allies
		Target-&gt;SetClrModulation(<funclink>ModulateColor</funclink>(this.old_mod, RGBa(127,127,255,127)));
	},
	Stop = func() {
		// restore previous values
		Target-&gt;<funclink>SetClrModulation</funclink>(this.old_mod);
		Target.Visibility = this.visibility;
	}
}</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>Timer = func()
{
	// return value of -1 means that the effect should be removed
	return -1;
}</code>
    <text>To store the previous status of the target object, properties of the effect are used. This way effects are independant of other objects and effects - remember that the magic spell object which has created the effect is already deleted. If you need to call functions in the context of the target object or other objects, use <code>-></code>.</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 <code>Effect</code> 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 <code>Effect</code> 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>/* Spell of immunity against fire */

func Activate(object caster, object caster2)
{
	// get caster
	if (caster2) caster = caster2;

	// start effect
	caster-><funclink>CreateEffect</funclink>(BanBurnPSpell, 180, 1111);
  
	// done - the spell object is not needed anymore
	<funclink>RemoveObject</funclink>();
	return true;
}

local BanBurnPSpell = new Effect {
	Construction = func()
	{
		// On start of the effect: extinguish clonk
		Target-&gt;<funclink>Extinguish</funclink>();
	},
	Effect = func(string new_name)
	{
		// block fire
		if (<funclink>WildcardMatch</funclink>(new_name, &quot;*Fire*&quot;)) return -1;
		// everything else is ok
		return 0;
	}
};</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 <code>Effect</code>. For the like:</text>
    <code>[...]
Effect = func(string new_name, var1, var2, var3, var4)
{
	// only handle fire
	if (!<funclink>WildcardMatch</funclink>(new_name, &quot;*Fire*&quot;)) return 0;
	// with fire, the three extra parameters have the following meaning:
	// var1: caused_by           - player that is responsible for the fire
	// var2: blasted             - bool: if the fire has been created by an explosion
	// var3: burning_object      - object: incineratable object
	// extinguish burning object
	if (var3 &amp;&amp; <funclink>GetType</funclink>(var3) == <funclink>C4V_C4Object</funclink>) var3-&gt;<funclink>Extinguish</funclink>();
	// block fire
	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>/* Resurrection spell */

func Activate(object caster, object caster2)
{
	// get caster
	if (caster2) caster = caster2;

	// start effect 
	caster-><funclink>CreateEffect</funclink>(ReincarnationPSpell, 180, 0);
	
	// done - the spell object is not needed anymore
	<funclink>RemoveObject</funclink>();
	return true;
}

local ReincarnationPSpell = new Effect {
	Construction = func() {
		// Only at the first start: message
		Target-&gt;<funclink>Message</funclink>(&quot;%s gets an extra life&quot;, Target-&gt;<funclink>GetName</funclink>());
		return true;
	},

	func Stop(int reason, bool temporary) {
		// only when the clonk died
		if (reason != 4) return true;
		
		// the clonk has already been resurrected
		if (Target-&gt;<funclink>GetAlive</funclink>()) return -1;
		// resurrect clonk
		Target-&gt;<funclink>SetAlive</funclink>(true);
		// give energy
		Target-&gt;<funclink>DoEnergy</funclink>(100);
		// message
		Target-&gt;<funclink>Message</funclink>(&quot;%s has been resurrected.&quot;, Target-&gt;<funclink>GetName</funclink>());

		// remove
		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>There are two global effect types: Scenerio effects and global effects. They are bound to the <code>Scenario</code> and <code>Global</code> proplists. With these effects, too, priorities are observed and temporary Add/Remove calls might be necessary to ensure order.</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>/* Gravitation spell */

// grav - previous gravitation
// change - change by the spell

func Activate(object caster, object caster2)
{
  // start global effect
  <funclink>AddEffect</funclink>(&quot;GravChangeUSpell&quot;, nil, 150, 37, nil, GetID(), -10);
  // done - the spell object is not needed anymore
  <funclink>RemoveObject</funclink>();
  return true;
}

func FxGravChangeUSpellStart(object target, proplist effect, bool temporary, change)
{
  // search for other gravitation effects
  if (!temporary)
  {
    var otherEffect = <funclink>GetEffect</funclink>(&quot;GravChangeUSpell&quot;, target);
    if (otherEffect == num) iOtherEffect = <funclink>GetEffect</funclink>(&quot;GravChangeUSpell&quot;, target, 1);
    if (otherEffect)
    {
      // add gravitation change to other effect
      otherEffect.change += change;
      <funclink>SetGravity</funclink>(<funclink>GetGravity</funclink>() + change);
      // and remove self
      return -1;
    }
  }
  // save previous gravitation
  effect.grav = <funclink>GetGravity</funclink>();
  // for non-temporary calls change is passed and added to the changed value
  if (change) effect.change += change;
  // set gravitation change
  // the change can be not equal to change in temporary calls
  <funclink>SetGravity</funclink>(effect.grav + effect.change);

  return true;
}

func FxGravChangeUSpellTimer(object target, proplist effect)
{
  // slowly return to normal gravitation
  if (<funclink>Inside</funclink>(effect.change, -1, 1)) return -1;
  var iDir = -effect.change/<funclink>Abs</funclink>(effect.change);
  effect.change += iDir;
  <funclink>SetGravity</funclink>(<funclink>GetGravity</funclink>() + iDir);
  return true;
}

func FxGravChangeUSpellStop(object target, proplist effect)
{
  // restore gravitation
  <funclink>SetGravity</funclink>(effect.grav);

  return true;
}</code>
    <text><code>target</code> will be <code>nil</code> in all these effect calls. You should still pass this parameter to calls such as <funclink>EffectCall</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>[...]

func FxGravChangeUSpellEffect(string new_name)
{
  // If the newly added effect is also a gravitation change, ask to take over the effect
  if (new_name == &quot;GravChangeUSpell&quot;) return -3;
}

func FxGravChangeUSpellAdd(object target, proplist effect, string new_name, int new_timer, change)
{
  // this is called when the effect has been taken over
  // add the gravitation change to this effect
  effect.change += change;
  <funclink>SetGravity</funclink>(<funclink>GetGravity</funclink>() + change);  

  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 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="Properties">Properties Reference</h>
    <text>Effects have a number of standard properties:</text>
    <table>
      <caption id="PropertiesTable">Effect Properties</caption>
      <rowh>
        <col>Name</col>
        <col>Data type</col>
        <col>Description</col>
      </rowh>
      <row>
        <col><code>Name</code></col>
        <col>string</col>
        <col>Can be changed.</col>
      </row>
      <row>
        <col><code>Priority</code></col>
        <col>int</col>
        <col>See <a href="Priorities">Priorities</a></col>
      </row>
      <row>
        <col><code>Interval</code></col>
        <col>int</col>
        <col>Of the <a href="#TimerCallback">Timer callback</a>.</col>
      </row>
      <row>
        <col><code>Time</code></col>
        <col>int</col>
        <col>The age of the effect in frames, used for the <a href="#TimerCallback">Timer callback</a>. Can be changed.</col>
      </row>
      <row>
        <col><code>CommandTarget</code></col>
        <col>proplist</col>
	<col><code>nil</code> when created by <funclink>CreateEffect</funclink>, as the effect gets the callbacks itself. When created by <funclink>AddEffect</funclink> either the command object or the command definition, depending on which is used.</col>
      </row>
      <row>
        <col><code>Target</code></col>
        <col>proplist</col>
	<col>The object the effect belongs to, or the proplists <code>Scenario</code> or <code>Global</code> for scenario and global effects.</col>
      </row>
    </table>
    <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; }

// Function that removes all "hot" effects from the caller
global func RemoveAllHotEffects()
{
  var target = this;
  if(!this) return;
  
  var effect_num, i;
  while (effect = <funclink>GetEffect</funclink>(&quot;*&quot;, target, i++))
    if (<funclink>EffectCall</funclink>(target, effect, &quot;IsHot&quot;))
      <funclink>RemoveEffect</funclink>(nil, target, effect);
}</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>/* Earthquake spell */

func Activate(object caster, object caster2)
{
  // check effect
  var result;
  if (result = <funclink>CheckEffect</funclink>(&quot;EarthquakeNSpell&quot;, nil, 150))
  {
    <funclink>RemoveObject</funclink>();
    return result != -1;
  }
  // execute effect
  if (caster-&gt;<funclink>GetDir</funclink>() == <funclink>DIR_Left</funclink>)
    CastLeft();
  else
    CastRight();
	
  // remove spell object
  <funclink>RemoveObject</funclink>();
  return true;
}</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>// The call CastleChange must be delayed so that the castle part is really gone when it is called
// otherwise, the FindObject()-calls would still find the castle part
func CastlePartDestruction()
{
  if (basement) basement-&gt;<funclink>RemoveObject</funclink>();
  // Global temporary effect, if not already there
  if (!<funclink>GetEffect</funclink>(&quot;IntCPW2CastleChange&quot;))
  	<funclink>AddEffect</funclink>(&quot;IntCPW2CastleChange&quot;, nil, 1, 2, nil, CPW2);
  return true;
}

func FxIntCPW2CastleChangeStop()
{
  // notice all castle parts
  for(var obj in <funclink>FindObjects</funclink>(<funclink>Find_OCF</funclink>(<funclink>OCF_Fullcon</funclink>), <funclink>Find_NoContainer</funclink>())
    obj-&gt;~CastleChange();

  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>/* scenario script */

func Initialize()
{
  // manipulate all wipfs
  for(var obj in <funclink>FindObjects</funclink>(<funclink>Find_ID</funclink>(WIPF), <funclink>Find_OCF</funclink>(<funclink>OCF_Alive</funclink>)))
    <funclink>AddEffect</funclink>(&quot;ExplodeOnDeathCurse&quot;, obj, 20);
}

global func FxExplodeOnDeathCurseStop(object target, proplist effect, int reason)
{
  // died? boom!
  if (reason == 4) target-&gt;<funclink>Explode</funclink>(20);
  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>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 effect prototype as necessary.</text>
      <h>Start</h>
      <text><code>int Start (int temporary, any var1, any var2, any var3, any var4);</code></text>
      <text>Called at the start of the effect. <code>this</code> is the effect itself. It can be used to manipulate the effect, for example with <code>this.Interval=newinterval</code>.</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>If temporary is 0, var1 to var4 are the additional parameters passed to <funclink>CreateEffect</funclink>().</text>
      <text>If temporary is 0 and this callback returns -1 the effect is not created and the corrsponding <funclink>CreateEffect</funclink>() call returns <code>nil</code>.</text>
      <h>Stop</h>
      <text><code>int Stop (int reason, bool temporary);</code></text>
      <text>When the effect is temporarily or permanently removed. <code>this</code> is the effect itself.</text>
      <text>reason contains the cause of the removal and can be one of the following values:</text>
      <text>
        <table>
          <rowh>
            <col>Script constant</col>
            <col>reason</col>
            <col>Meaning</col>
          </rowh>
          <row>
            <col>FX_Call_Normal</col>
            <col>0</col>
            <col>Normal removal</col>
          </row>
          <row>
            <col>FX_Call_Temp</col>
            <col>1</col>
            <col>Temporary removal (temporary is 1).</col>
          </row>
          <row>
            <col>FX_Call_TempAddForRemoval</col>
            <col>2</col>
            <col>Not used</col>
          </row>
          <row>
            <col>FX_Call_RemoveClear</col>
            <col>3</col>
            <col>The target object has been deleted</col>
          </row>
          <row>
            <col>FX_Call_RemoveDeath</col>
            <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 id="FxConstructionCallback">Construction</h>
      <text><code>int Construction (any var1, any var2, any var3, any var4);</code></text>
      <text>Called when the effect is first created, before it is started. The parameters <code>var1</code> to <code>var4</code> are passed through from <funclink>CreateEffect</funclink>.</text>
      <text>The return value is ignored.</text>
      <h id="FxDestructionCallback">Destruction</h>
      <text><code>nil Destruction (int reason);</code></text>
      <text>Callback when the effect is removed. <code>reason</code> is the same as in the preceding <code>Stop</code> call, see above.</text>
      <text>The return value is ignored.</text>
      <h id="TimerCallback">Timer</h>
      <text><code>int Timer (int time);</code></text>
      <text>Periodic timer call, if a timer interval has been specified at effect creation.</text>
      <text>time specifies how long the effect has now been active. This might alternatively be determined using effect.Time.</text>
      <text>If this function is not implemented or returns -1, the effect will be deleted after this call.</text>
      <h>Effect</h>
      <text><code>int Effect (string new_name, 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; <code>this</code> is the effect being called.</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.</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 <code>Add</code> call is then made to the accepting effect, the new effect is not actually created, and the calling <funclink>CreateEffect</funclink> function returns the accepting effect. The return value -3 will also temporarily remove all higher prioriy effects just before the <code>Add</code> callback and re-add them later.</text>
      <text>var1 bis var4 are the parameters passed to <funclink>CreateEffect</funclink>()</text>
      <h>Add</h>
      <text><code>int Add (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 <code>Effect</code> call. <code>this</code> identifies the accepting effect to which the consequences of the new effect will be added.</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 <funclink>CreateEffect</funclink> function will then return <code>nil</code>.</text>
      <h>Damage</h>
      <text><code>int Damage (int damage, int cause, int by_player);</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 the damage to be done to the target.</text>
      <text id="damagecause">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.</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>CreateEffect</funclink>() - for effect creation</li>
        <li><funclink>RemoveEffect</funclink>() - for effect removal</li>
        <li><funclink>GetEffect</funclink>() - to search for effects</li>
        <li><funclink>GetEffectCount</funclink>() - for effect counting</li>
        <li><funclink>EffectCall</funclink>() - for user defined calls in 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>