1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "EffectQueue.h"
27 #include "Interface.h"
29 #include "Video.h" //for tints
30 #include "Scriptable/Actor.h"
32 int fx_retreat_from (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//6e
33 int fx_set_status (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//ba
34 int fx_play_bam_blended (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//bb
35 int fx_play_bam_not_blended (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//bc
36 int fx_transfer_hp (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//c0
37 //int fx_shake_screen (Scriptable* Owner, Actor* target, Effect* fx);//c1 already implemented
38 int fx_flash_screen (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//c2
39 int fx_tint_screen (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//c3
40 int fx_special_effect (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//c4
42 int fx_overlay (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//c9
44 int fx_bless (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//82 (this is a modified effect)
45 int fx_curse (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//cb
46 int fx_prayer (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//cc
47 int fx_move_view (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//cd
48 int fx_embalm (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//ce
49 int fx_stop_all_action (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//cf
50 int fx_iron_fist (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//d0
51 int fx_hostile_image(Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//d1
52 int fx_detect_evil (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//d2
53 int fx_jumble_curse (Scriptable
* Owner
, Actor
* target
, Effect
* fx
);//d3
54 //int fx_unknown (Scriptable* Owner, Actor* target, Effect* fx);//d4
56 // FIXME: Make this an ordered list, so we could use bsearch!
57 static EffectRef effectnames
[] = {
58 { "RetreatFrom", fx_retreat_from
, -1 },//6e
59 { "Bless", fx_bless
, -1},//82
60 { "Curse", fx_curse
, -1},//cb
61 { "DetectEvil", fx_detect_evil
, -1}, //d2
62 { "Embalm", fx_embalm
, -1}, //0xce
63 { "FlashScreen", fx_flash_screen
, -1}, //c2
64 { "HostileImage", fx_hostile_image
, -1},//d1
65 { "IronFist", fx_iron_fist
, -1}, //d0
66 { "JumbleCurse", fx_jumble_curse
, -1}, //d3
67 { "MoveView", fx_move_view
, -1},//cd
68 { "Overlay", fx_overlay
, -1}, //c9
69 { "PlayBAM1", fx_play_bam_blended
, -1}, //bb
70 { "PlayBAM2", fx_play_bam_not_blended
, -1},//bc
71 { "PlayBAM3", fx_play_bam_not_blended
, -1}, //bd
72 { "PlayBAM4", fx_play_bam_not_blended
, -1}, //be
73 { "PlayBAM5", fx_play_bam_not_blended
, -1}, //bf
74 { "Prayer", fx_prayer
, -1},//cc
75 { "SetStatus", fx_set_status
, -1}, //ba
76 { "SpecialEffect", fx_special_effect
, -1},//c4
77 { "StopAllAction", fx_stop_all_action
, -1}, //cf
78 { "TintScreen", fx_tint_screen
, -1}, //c3
79 { "TransferHP", fx_transfer_hp
, -1}, //c0
83 void RegisterTormentOpcodes()
85 core
->RegisterOpcodes( sizeof( effectnames
) / sizeof( EffectRef
) - 1, effectnames
);
88 //retreat_from (works only in PST) - forces target to run away/walk away from Owner
89 int fx_retreat_from (Scriptable
* Owner
, Actor
* target
, Effect
* fx
)
91 if (0) printf( "fx_retreat_from (%2d): Mod: %d, Type: %d\n", fx
->Opcode
, fx
->Parameter1
, fx
->Parameter2
);
94 return FX_NOT_APPLIED
;
98 if (!fx
->Parameter3
) {
102 if (fx
->Parameter2
==8) {
103 //backs away from owner
104 target
->RunAwayFrom(Owner
->Pos
, fx
->Parameter3
, false);
106 return FX_NOT_APPLIED
;
109 //walks (7) or runs away (all others) from owner
110 target
->RunAwayFrom(Owner
->Pos
, fx
->Parameter3
, true);
111 if (fx
->Parameter2
!=7) {
112 target
->SetRunFlags(IF_RUNNING
);
120 int fx_set_status (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
122 if (0) printf( "fx_set_status (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
123 if (fx
->Parameter1
) {
124 if (fx
->TimingMode
==FX_DURATION_INSTANT_PERMANENT
) {
125 BASE_STATE_SET (fx
->Parameter2
);
127 STATE_SET (fx
->Parameter2
);
130 if (fx
->TimingMode
==FX_DURATION_INSTANT_PERMANENT
) {
131 BASE_STATE_CURE (fx
->Parameter2
);
133 STATE_CURE (fx
->Parameter2
);
139 //bb fx_play_bam_bb (play multi-part blended sticky animation)
141 // 2 not sticky (override default)
142 int fx_play_bam_blended (Scriptable
* Owner
, Actor
* target
, Effect
* fx
)
146 if (0) printf( "fx_play_bam_blended (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
147 //play once set to true
148 //check tearring.itm (0xbb effect)
149 ScriptedAnimation
*sca
= gamedata
->GetScriptedAnimation(fx
->Resource
, true);
151 return FX_NOT_APPLIED
;
154 //the transparency is based on the original palette
155 if (fx
->Parameter1
) {
160 rgb
.rgb
.r
=fx
->Parameter1
;
161 rgb
.rgb
.g
=fx
->Parameter1
>> 8;
162 rgb
.rgb
.b
=fx
->Parameter1
>> 16;
164 rgb
.type
=RGBModifier::TINT
;
165 sca
->AlterPalette(rgb
);
167 if (fx
->TimingMode
==FX_DURATION_INSTANT_PERMANENT
) {
172 if (fx
->Parameter2
&1) {
173 //four cycles, duration is in millisecond
174 sca
->SetDefaultDuration(sca
->GetSequenceDuration(4000));
179 sca
->SetDefaultDuration(fx
->Duration
-core
->GetGame()->Ticks
);
182 if (fx
->Parameter2
&2) {
185 Owner
->GetCurrentArea()->AddVVCell(sca
);
187 ScriptedAnimation
*twin
= sca
->DetachTwin();
189 target
->AddVVCell(twin
);
191 target
->AddVVCell(sca
);
193 return FX_NOT_APPLIED
;
196 //bc-bf play_bam_not_blended (play not blended single animation)
197 //random placement (if not sticky): 1
199 //transparency instead of rgb tint: 0x100000, fade off is in dice size
201 //twin animation: 0x30000
202 //background animation: 0x10000
203 //foreground animation: 0x20000
204 int fx_play_bam_not_blended (Scriptable
* Owner
, Actor
* target
, Effect
* fx
)
209 if (0) printf( "fx_play_bam_not_blended (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
210 //play once set to true
211 //check tearring.itm (0xbb effect)
212 if ((fx
->Parameter2
&0x30000)==0x30000) {
217 ScriptedAnimation
*sca
= gamedata
->GetScriptedAnimation(fx
->Resource
, doublehint
);
219 return FX_NOT_APPLIED
;
221 switch (fx
->Parameter2
&0x300000) {
223 sca
->SetBlend(); //per pixel transparency
225 case 0x200000: //this is an insane combo
226 sca
->SetBlend(); //per pixel transparency
227 sca
->SetFade((ieByte
) fx
->Parameter1
, fx
->DiceSides
); //per surface transparency
229 case 0x100000: //per surface transparency
230 sca
->SetFade((ieByte
) fx
->Parameter1
, fx
->DiceSides
);
233 if (fx
->Parameter1
) {
238 rgb
.rgb
.r
=fx
->Parameter1
;
239 rgb
.rgb
.g
=fx
->Parameter1
>> 8;
240 rgb
.rgb
.b
=fx
->Parameter1
>> 16;
241 rgb
.rgb
.a
=fx
->Parameter1
>> 24;
242 rgb
.type
=RGBModifier::TINT
;
243 sca
->AlterPalette(rgb
);
246 if (fx
->TimingMode
==FX_DURATION_INSTANT_PERMANENT
) {
251 switch (fx
->Parameter2
&0x30000) {
252 case 0x20000://foreground
258 sca
->twin
->ZPos
-=9999;
261 default: //background
268 sca
->SetDefaultDuration(fx
->Duration
-core
->GetGame()->Ticks
);
270 ScriptedAnimation
*twin
= sca
->DetachTwin();
271 if (fx
->Parameter2
&4096) {
273 target
->AddVVCell(twin
);
275 target
->AddVVCell(sca
);
277 //the random placement works only when it is not sticky
280 if (fx
->Parameter2
&1) {
281 ieWord tmp
=(ieWord
) rand();
286 sca
->XPos
+=fx
->PosX
-x
;
287 sca
->YPos
+=fx
->PosY
+sca
->ZPos
-y
;
289 twin
->XPos
+=fx
->PosX
-x
;
290 twin
->YPos
+=fx
->PosY
+twin
->ZPos
-y
;
291 Owner
->GetCurrentArea()->AddVVCell(twin
);
293 Owner
->GetCurrentArea()->AddVVCell(sca
);
295 return FX_NOT_APPLIED
;
298 //0xc0 fx_transfer_hp
299 int fx_transfer_hp (Scriptable
* Owner
, Actor
* target
, Effect
* fx
)
301 if (0) printf( "fx_transfer_hp (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
302 if (Owner
->Type
!=ST_ACTOR
) {
303 return FX_NOT_APPLIED
;
306 Actor
*owner
= (Actor
*) Owner
;
309 return FX_NOT_APPLIED
;
316 switch(fx
->Parameter2
) {
318 case 0: receiver
= target
; donor
= owner
; break;
320 case 1: receiver
= owner
; donor
= target
; break;
322 a
= owner
->GetBase(IE_HITPOINTS
);
323 b
= target
->GetBase(IE_HITPOINTS
);
324 owner
->SetBase(IE_HITPOINTS
, a
);
325 target
->SetBase(IE_HITPOINTS
, b
);
328 return FX_NOT_APPLIED
;
330 int damage
= donor
->Damage(fx
->Parameter1
, fx
->Parameter2
, owner
);
331 receiver
->SetBase( IE_HITPOINTS
, BASE_GET( IE_HITPOINTS
) + ( damage
) );
332 return FX_NOT_APPLIED
;
335 //0xc1 fx_shake_screen this is already implemented in BG2
337 //0xc2 fx_flash_screen
338 int fx_flash_screen (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
340 if (0) printf( "fx_flash_screen (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
341 core
->GetVideoDriver()->SetFadeColor(((char *) &fx
->Parameter1
)[0],((char *) &fx
->Parameter1
)[1],((char *) &fx
->Parameter1
)[2]);
342 core
->timer
->SetFadeFromColor(1);
343 core
->timer
->SetFadeToColor(1);
344 return FX_NOT_APPLIED
;
347 //0xc3 fx_tint_screen
348 //FIXME: implement bit4 which would mean duration
349 int fx_tint_screen (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
351 if (0) printf( "fx_tint_screen (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
352 int fromTime
= fx
->DiceSides
;
353 int toTime
= fx
->DiceSides
;
354 switch(fx
->Parameter2
&6) {
355 case 0: toTime
= 0; break;
356 case 2: fromTime
= 0; break;
358 core
->timer
->SetFadeFromColor(fromTime
);
359 core
->timer
->SetFadeToColor(toTime
);
360 return FX_NOT_APPLIED
;
363 //0xc4 fx_special_effect
364 int fx_special_effect (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
366 if (0) printf( "fx_special_effect (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
368 return FX_NOT_APPLIED
;
372 int fx_overlay (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
374 if (0) printf( "fx_overlay (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
375 target
->AddAnimation(fx
->Resource
,-1,0,true);
376 //special effects based on fx_param2
377 return FX_NOT_APPLIED
;
382 //static EffectRef fx_glow_ref ={"Color:PulseRGBGlobal",NULL,-1};
383 //pst bless effect spawns a color glow automatically
384 //but i would rather use the IWD2 method
385 int fx_bless (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
387 if (0) printf( "fx_curse (%2d): Par1: %d\n", fx
->Opcode
, fx
->Parameter1
);
388 //this bit is the same as the invisibility bit in other games
389 //it should be considered what if we replace the pst invis bit
390 //with this one (losing binary compatibility, gaining easier
391 //invis checks at core level)
392 if (STATE_GET (STATE_BLESS
) ) //curse is non-cumulative
393 return FX_NOT_APPLIED
;
395 target
->SetColorMod(255, RGBModifier::ADD
, 0x18, 0xc8, 0xc8, 0xc8);
397 STATE_SET( STATE_BLESS
);
398 STAT_SUB( IE_TOHIT
, fx
->Parameter1
);
399 STAT_SUB( IE_SAVEVSDEATH
, fx
->Parameter1
);
400 STAT_SUB( IE_SAVEVSWANDS
, fx
->Parameter1
);
401 STAT_SUB( IE_SAVEVSPOLY
, fx
->Parameter1
);
402 STAT_SUB( IE_SAVEVSBREATH
, fx
->Parameter1
);
403 STAT_SUB( IE_SAVEVSSPELL
, fx
->Parameter1
);
408 int fx_curse (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
410 if (0) printf( "fx_curse (%2d): Par1: %d\n", fx
->Opcode
, fx
->Parameter1
);
411 //this bit is the same as the invisibility bit in other games
412 //it should be considered what if we replace the pst invis bit
413 //with this one (losing binary compatibility, gaining easier
414 //invis checks at core level)
415 if (STATE_GET (STATE_PST_CURSE
) ) //curse is non cumulative
416 return FX_NOT_APPLIED
;
417 STATE_SET( STATE_PST_CURSE
);
418 STAT_SUB( IE_TOHIT
, fx
->Parameter1
);
419 STAT_SUB( IE_SAVEVSDEATH
, fx
->Parameter1
);
420 STAT_SUB( IE_SAVEVSWANDS
, fx
->Parameter1
);
421 STAT_SUB( IE_SAVEVSPOLY
, fx
->Parameter1
);
422 STAT_SUB( IE_SAVEVSBREATH
, fx
->Parameter1
);
423 STAT_SUB( IE_SAVEVSSPELL
, fx
->Parameter1
);
428 static EffectRef fx_curse_ref
={"Curse",NULL
,-1};
429 static EffectRef fx_bless_ref
={"Bless",NULL
,-1};
431 int fx_prayer (Scriptable
* Owner
, Actor
* target
, Effect
* fx
)
433 if (0) printf( "fx_prayer (%2d): Par1: %d\n", fx
->Opcode
, fx
->Parameter1
);
434 int ea
= target
->GetStat(IE_EA
);
436 if (ea
>EA_EVILCUTOFF
) type
= 1;
437 else if (ea
<EA_GOODCUTOFF
) type
= 0;
438 else return FX_NOT_APPLIED
; //what happens if the target goes neutral during the effect? if the effect remains, make this FX_APPLIED
440 Map
*map
= target
->GetCurrentArea();
441 int i
= map
->GetActorCount(true);
442 Effect
*newfx
= EffectQueue::CreateEffect(type
?fx_curse_ref
:fx_bless_ref
, fx
->Parameter1
, fx
->Parameter2
, FX_DURATION_INSTANT_LIMITED
);
443 memcpy(newfx
, fx
->Source
,sizeof(ieResRef
));
446 Actor
*tar
=map
->GetActor(i
,true);
447 ea
= tar
->GetStat(IE_EA
);
448 if (ea
>EA_EVILCUTOFF
) type
^=1;
449 else if (ea
>EA_GOODCUTOFF
) continue;
450 //this isn't a real perma effect, just applying the effect now
451 //no idea how this should work with spell resistances, etc
452 //lets assume it is never resisted
453 //the effect will be destructed by ApplyEffect (not anymore)
454 //the effect is copied to a new memory area
455 core
->ApplyEffect(newfx
, tar
, Owner
);
462 int fx_move_view (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
464 if (0) printf( "fx_move_view (%2d): Speed: %d\n", fx
->Opcode
, fx
->Parameter1
);
465 Map
*map
= core
->GetGame()->GetCurrentArea();
467 core
->timer
->SetMoveViewPort( fx
->PosX
, fx
->PosY
, fx
->Parameter1
, true);
469 return FX_NOT_APPLIED
;
473 int fx_embalm (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
475 if (0) printf( "fx_embalm (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
476 if (STATE_GET (STATE_EMBALM
) ) //embalm is non cumulative
477 return FX_NOT_APPLIED
;
478 STATE_SET( STATE_EMBALM
);
479 if (!fx
->Parameter1
) {
480 if (fx
->Parameter2
) {
481 fx
->Parameter1
=fx
->CasterLevel
*2;
483 fx
->Parameter1
=core
->Roll(1,6,1);
485 BASE_ADD( IE_HITPOINTS
, fx
->Parameter1
);
487 STAT_ADD( IE_MAXHITPOINTS
, fx
->Parameter1
);
488 if (fx
->Parameter2
) {
489 STAT_ADD( IE_ARMORCLASS
,2 );
491 STAT_ADD( IE_ARMORCLASS
,1 );
495 //0xcf fx_stop_all_action
496 int fx_stop_all_action (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
498 if (0) printf( "fx_stop_all_action (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
499 if (fx
->Parameter2
) {
500 core
->GetGame()->TimeStop(NULL
, 0xffffffff);
502 core
->GetGame()->TimeStop(NULL
, 0);
504 return FX_NOT_APPLIED
;
508 //GemRB extension: lets you specify not hardcoded values
509 int fx_iron_fist (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
513 if (0) printf( "fx_iron_fist (%2d): Par1: %d Par2: %d\n", fx
->Opcode
, fx
->Parameter1
, fx
->Parameter2
);
514 switch (fx
->Parameter2
)
516 case 0: p1
= 3; p2
= 6; break;
518 p1
= ieWord (fx
->Parameter1
&0xffff);
519 p2
= ieWord (fx
->Parameter1
>>16);
521 STAT_ADD(IE_FISTHIT
, p1
);
522 STAT_ADD(IE_FISTDAMAGE
, p2
);
526 //0xd1 fx_hostile_image
527 int fx_hostile_image (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
529 if (0) printf( "fx_hostile_image (%2d): Par1: %d Par2: %d\n", fx
->Opcode
, fx
->Parameter1
, fx
->Parameter2
);
530 return FX_NOT_APPLIED
;
533 //0xd2 fx_detect_evil
534 static EffectRef fx_single_color_pulse_ref
={"Color:BriefRGB",NULL
,-1};
536 int fx_detect_evil (Scriptable
* Owner
, Actor
* target
, Effect
* fx
)
538 if (0) printf( "fx_detect_evil (%2d): Par1: %d Par2: %d\n", fx
->Opcode
, fx
->Parameter1
, fx
->Parameter2
);
539 ieDword type
= fx
->Parameter2
;
540 //default is alignment/evil/speed 30/range 10
541 if (!type
) type
= 0x08031e0a;
542 int speed
= (type
&0xff00)>>8;
543 if (!speed
) speed
=30;
544 if (!(core
->GetGame()->GameTime
%speed
)) {
545 ieDword color
= fx
->Parameter1
;
546 //default is magenta (rgba)
547 if (!color
) color
= 0xff00ff00;
548 Effect
*newfx
= EffectQueue::CreateEffect(fx_single_color_pulse_ref
, color
, speed
<<16, FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES
);
549 newfx
->Target
=FX_TARGET_PRESET
;
550 EffectQueue
*fxqueue
= new EffectQueue();
551 fxqueue
->SetOwner(Owner
);
552 fxqueue
->AddEffect(newfx
);
555 //don't detect self? if yes, then use NULL as last parameter
556 fxqueue
->AffectAllInRange(target
->GetCurrentArea(), target
->Pos
, (type
&0xff000000)>>24, (type
&0xff0000)>>16, (type
&0xff)*10, target
);
562 //0xd3 fx_jumble_curse
563 int fx_jumble_curse (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
565 if (0) printf( "fx_jumble_curse (%2d)\n", fx
->Opcode
);
567 if (STATE_GET( STATE_DEAD
) ) {
568 return FX_NOT_APPLIED
;
570 Game
*game
= core
->GetGame();
571 //do a hiccup every 75th refresh
572 if (fx
->Parameter3
/75!=fx
->Parameter4
/75) {
574 //PST has this hardcoded deep in the engine
575 //gemrb lets you specify the strref in P#1
576 ieStrRef tmp
= fx
->Parameter1
;
577 if (!tmp
) tmp
= 46633;
578 char *tmpstr
= core
->GetString(tmp
, IE_STR_SPEECH
|IE_STR_SOUND
);
579 target
->DisplayHeadText(tmpstr
);
580 //tmpstr shouldn't be freed, it is taken care by Actor
583 fx
->Parameter4
=fx
->Parameter3
;
584 fx
->Parameter3
=game
->GameTime
;
585 STAT_SET( IE_DEADMAGIC
, 1);
586 STAT_SET( IE_SPELLFAILUREMAGE
, 100);
587 STAT_SET( IE_SPELLFAILUREPRIEST
, 100);
588 STAT_SET( IE_SPELLFAILUREINNATE
, 100);
592 #include "plugindef.h"
594 GEMRB_PLUGIN(0x115A670, "Effect opcodes for the torment branch of the games")
595 PLUGIN_INITIALIZER(RegisterTormentOpcodes
)