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.
25 #include "EffectQueue.h"
28 #include "Interface.h"
30 #include "Video.h" //for tints
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 int fx_tint_screen (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
350 if (0) printf( "fx_tint_screen (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
351 core
->timer
->SetFadeFromColor(10);
352 core
->timer
->SetFadeToColor(10);
353 return FX_NOT_APPLIED
;
356 //0xc4 fx_special_effect
357 int fx_special_effect (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
359 if (0) printf( "fx_special_effect (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
361 return FX_NOT_APPLIED
;
365 int fx_overlay (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
367 if (0) printf( "fx_overlay (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
368 target
->AddAnimation(fx
->Resource
,-1,0,true);
369 //special effects based on fx_param2
370 return FX_NOT_APPLIED
;
375 //static EffectRef fx_glow_ref ={"Color:PulseRGBGlobal",NULL,-1};
376 //pst bless effect spawns a color glow automatically
377 //but i would rather use the IWD2 method
378 int fx_bless (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
380 if (0) printf( "fx_curse (%2d): Par1: %d\n", fx
->Opcode
, fx
->Parameter1
);
381 //this bit is the same as the invisibility bit in other games
382 //it should be considered what if we replace the pst invis bit
383 //with this one (losing binary compatibility, gaining easier
384 //invis checks at core level)
385 if (STATE_GET (STATE_BLESS
) ) //curse is non-cumulative
386 return FX_NOT_APPLIED
;
388 target
->SetColorMod(255, RGBModifier::ADD
, 0x18, 0xc8, 0xc8, 0xc8);
390 STATE_SET( STATE_BLESS
);
391 STAT_SUB( IE_TOHIT
, fx
->Parameter1
);
392 STAT_SUB( IE_SAVEVSDEATH
, fx
->Parameter1
);
393 STAT_SUB( IE_SAVEVSWANDS
, fx
->Parameter1
);
394 STAT_SUB( IE_SAVEVSPOLY
, fx
->Parameter1
);
395 STAT_SUB( IE_SAVEVSBREATH
, fx
->Parameter1
);
396 STAT_SUB( IE_SAVEVSSPELL
, fx
->Parameter1
);
401 int fx_curse (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
403 if (0) printf( "fx_curse (%2d): Par1: %d\n", fx
->Opcode
, fx
->Parameter1
);
404 //this bit is the same as the invisibility bit in other games
405 //it should be considered what if we replace the pst invis bit
406 //with this one (losing binary compatibility, gaining easier
407 //invis checks at core level)
408 if (STATE_GET (STATE_PST_CURSE
) ) //curse is non cumulative
409 return FX_NOT_APPLIED
;
410 STATE_SET( STATE_PST_CURSE
);
411 STAT_SUB( IE_TOHIT
, fx
->Parameter1
);
412 STAT_SUB( IE_SAVEVSDEATH
, fx
->Parameter1
);
413 STAT_SUB( IE_SAVEVSWANDS
, fx
->Parameter1
);
414 STAT_SUB( IE_SAVEVSPOLY
, fx
->Parameter1
);
415 STAT_SUB( IE_SAVEVSBREATH
, fx
->Parameter1
);
416 STAT_SUB( IE_SAVEVSSPELL
, fx
->Parameter1
);
421 static EffectRef fx_curse_ref
={"Curse",NULL
,-1};
422 static EffectRef fx_bless_ref
={"Bless",NULL
,-1};
424 int fx_prayer (Scriptable
* Owner
, Actor
* target
, Effect
* fx
)
426 if (0) printf( "fx_prayer (%2d): Par1: %d\n", fx
->Opcode
, fx
->Parameter1
);
427 int ea
= target
->GetStat(IE_EA
);
429 if (ea
>EA_EVILCUTOFF
) type
= 1;
430 else if (ea
<EA_GOODCUTOFF
) type
= 0;
431 else return FX_NOT_APPLIED
; //what happens if the target goes neutral during the effect? if the effect remains, make this FX_APPLIED
433 Map
*map
= target
->GetCurrentArea();
434 int i
= map
->GetActorCount(true);
435 Effect
*newfx
= EffectQueue::CreateEffect(type
?fx_curse_ref
:fx_bless_ref
, fx
->Parameter1
, fx
->Parameter2
, FX_DURATION_INSTANT_LIMITED
);
436 memcpy(newfx
, fx
->Source
,sizeof(ieResRef
));
439 Actor
*tar
=map
->GetActor(i
,true);
440 ea
= tar
->GetStat(IE_EA
);
441 if (ea
>EA_EVILCUTOFF
) type
^=1;
442 else if (ea
>EA_GOODCUTOFF
) continue;
443 //this isn't a real perma effect, just applying the effect now
444 //no idea how this should work with spell resistances, etc
445 //lets assume it is never resisted
446 //the effect will be destructed by ApplyEffect (not anymore)
447 //the effect is copied to a new memory area
448 core
->ApplyEffect(newfx
, tar
, Owner
);
455 int fx_move_view (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
457 if (0) printf( "fx_move_view (%2d): Speed: %d\n", fx
->Opcode
, fx
->Parameter1
);
458 Map
*map
= core
->GetGame()->GetCurrentArea();
460 core
->timer
->SetMoveViewPort( fx
->PosX
, fx
->PosY
, fx
->Parameter1
, true);
462 return FX_NOT_APPLIED
;
466 int fx_embalm (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
468 if (0) printf( "fx_embalm (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
469 if (STATE_GET (STATE_EMBALM
) ) //embalm is non cumulative
470 return FX_NOT_APPLIED
;
471 STATE_SET( STATE_EMBALM
);
472 if (!fx
->Parameter1
) {
473 if (fx
->Parameter2
) {
474 fx
->Parameter1
=fx
->CasterLevel
*2;
476 fx
->Parameter1
=core
->Roll(1,6,1);
478 BASE_ADD( IE_HITPOINTS
, fx
->Parameter1
);
480 STAT_ADD( IE_MAXHITPOINTS
, fx
->Parameter1
);
481 if (fx
->Parameter2
) {
482 STAT_ADD( IE_ARMORCLASS
,2 );
484 STAT_ADD( IE_ARMORCLASS
,1 );
488 //0xcf fx_stop_all_action
489 int fx_stop_all_action (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
491 if (0) printf( "fx_stop_all_action (%2d): Par2: %d\n", fx
->Opcode
, fx
->Parameter2
);
492 if (fx
->Parameter2
) {
493 core
->GetGame()->TimeStop(NULL
, 0xffffffff);
495 core
->GetGame()->TimeStop(NULL
, 0);
497 return FX_NOT_APPLIED
;
501 //GemRB extension: lets you specify not hardcoded values
502 int fx_iron_fist (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
506 if (0) printf( "fx_iron_fist (%2d): Par1: %d Par2: %d\n", fx
->Opcode
, fx
->Parameter1
, fx
->Parameter2
);
507 switch (fx
->Parameter2
)
509 case 0: p1
= 3; p2
= 6; break;
511 p1
= ieWord (fx
->Parameter1
&0xffff);
512 p2
= ieWord (fx
->Parameter1
>>16);
514 STAT_ADD(IE_FISTHIT
, p1
);
515 STAT_ADD(IE_FISTDAMAGE
, p2
);
519 //0xd1 fx_hostile_image
520 int fx_hostile_image (Scriptable
* /*Owner*/, Actor
* /*target*/, Effect
* fx
)
522 if (0) printf( "fx_hostile_image (%2d): Par1: %d Par2: %d\n", fx
->Opcode
, fx
->Parameter1
, fx
->Parameter2
);
523 return FX_NOT_APPLIED
;
526 //0xd2 fx_detect_evil
527 static EffectRef fx_single_color_pulse_ref
={"Color:BriefRGB",NULL
,-1};
529 int fx_detect_evil (Scriptable
* Owner
, Actor
* target
, Effect
* fx
)
531 if (0) printf( "fx_detect_evil (%2d): Par1: %d Par2: %d\n", fx
->Opcode
, fx
->Parameter1
, fx
->Parameter2
);
532 ieDword type
= fx
->Parameter2
;
533 //default is alignment/evil/speed 30/range 10
534 if (!type
) type
= 0x08031e0a;
535 int speed
= (type
&0xff00)>>8;
536 if (!speed
) speed
=30;
537 if (!(core
->GetGame()->GameTime
%speed
)) {
538 ieDword color
= fx
->Parameter1
;
539 //default is magenta (rgba)
540 if (!color
) color
= 0xff00ff00;
541 Effect
*newfx
= EffectQueue::CreateEffect(fx_single_color_pulse_ref
, color
, speed
<<16, FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES
);
542 newfx
->Target
=FX_TARGET_PRESET
;
543 EffectQueue
*fxqueue
= new EffectQueue();
544 fxqueue
->SetOwner(Owner
);
545 fxqueue
->AddEffect(newfx
);
548 //don't detect self? if yes, then use NULL as last parameter
549 fxqueue
->AffectAllInRange(target
->GetCurrentArea(), target
->Pos
, (type
&0xff000000)>>24, (type
&0xff0000)>>16, (type
&0xff)*10, target
);
555 //0xd3 fx_jumble_curse
556 int fx_jumble_curse (Scriptable
* /*Owner*/, Actor
* target
, Effect
* fx
)
558 if (0) printf( "fx_jumble_curse (%2d)\n", fx
->Opcode
);
560 if (STATE_GET( STATE_DEAD
) ) {
561 return FX_NOT_APPLIED
;
563 Game
*game
= core
->GetGame();
564 //do a hiccup every 75th refresh
565 if (fx
->Parameter3
/75!=fx
->Parameter4
/75) {
567 //PST has this hardcoded deep in the engine
568 //gemrb lets you specify the strref in P#1
569 ieStrRef tmp
= fx
->Parameter1
;
570 if (!tmp
) tmp
= 46633;
571 char *tmpstr
= core
->GetString(tmp
, IE_STR_SPEECH
|IE_STR_SOUND
);
572 target
->DisplayHeadText(tmpstr
);
573 //tmpstr shouldn't be freed, it is taken care by Actor
576 fx
->Parameter4
=fx
->Parameter3
;
577 fx
->Parameter3
=game
->GameTime
;
578 STAT_SET( IE_DEADMAGIC
, 1);
579 STAT_SET( IE_SPELLFAILUREMAGE
, 100);
580 STAT_SET( IE_SPELLFAILUREPRIEST
, 100);
581 STAT_SET( IE_SPELLFAILUREINNATE
, 100);
585 #include "plugindef.h"
587 GEMRB_PLUGIN(0x115A670, "Effect opcodes for the torment branch of the games")
588 PLUGIN_INITIALIZER(RegisterTormentOpcodes
)