TickHook: Fix crash when TickHook isn't set.
[gemrb.git] / gemrb / plugins / PSTOpcodes / PSTOpcodes.cpp
blobb828a718492560d0ddd32b031d8988dedba3987e
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.
21 #include "strrefs.h"
22 #include "win32def.h"
24 #include "EffectQueue.h"
25 #include "Game.h"
26 #include "GameData.h"
27 #include "Interface.h"
28 #include "TileMap.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
41 //unknown 0xc5-c8
42 int fx_overlay (Scriptable* Owner, Actor* target, Effect* fx);//c9
43 //unknown 0xca
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
80 { NULL, NULL, 0 },
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 );
93 if (!Owner) {
94 return FX_NOT_APPLIED;
97 //distance to run
98 if (!fx->Parameter3) {
99 fx->Parameter3=100;
102 if (fx->Parameter2==8) {
103 //backs away from owner
104 target->RunAwayFrom(Owner->Pos, fx->Parameter3, false);
105 //one shot
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);
115 //has a duration
116 return FX_APPLIED;
119 //0xba fx_set_status
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);
126 } else {
127 STATE_SET (fx->Parameter2);
129 } else {
130 if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
131 BASE_STATE_CURE (fx->Parameter2);
132 } else {
133 STATE_CURE (fx->Parameter2);
136 return FX_PERMANENT;
139 //bb fx_play_bam_bb (play multi-part blended sticky animation)
140 // 1 repeats
141 // 2 not sticky (override default)
142 int fx_play_bam_blended (Scriptable* Owner, Actor* target, Effect* fx)
144 bool playonce;
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);
150 if (!sca)
151 return FX_NOT_APPLIED;
153 sca->SetBlend();
154 //the transparency is based on the original palette
155 if (fx->Parameter1) {
156 RGBModifier rgb;
158 rgb.speed=-1;
159 rgb.phase=0;
160 rgb.rgb.r=fx->Parameter1;
161 rgb.rgb.g=fx->Parameter1 >> 8;
162 rgb.rgb.b=fx->Parameter1 >> 16;
163 rgb.rgb.a=0;
164 rgb.type=RGBModifier::TINT;
165 sca->AlterPalette(rgb);
167 if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
168 playonce=true;
169 } else {
170 playonce=false;
172 if (fx->Parameter2&1) {
173 //four cycles, duration is in millisecond
174 sca->SetDefaultDuration(sca->GetSequenceDuration(4000));
175 } else {
176 if (playonce) {
177 sca->PlayOnce();
178 } else {
179 sca->SetDefaultDuration(fx->Duration-core->GetGame()->Ticks);
182 if (fx->Parameter2&2) {
183 sca->XPos+=fx->PosX;
184 sca->YPos+=fx->PosY;
185 Owner->GetCurrentArea()->AddVVCell(sca);
186 } else {
187 ScriptedAnimation *twin = sca->DetachTwin();
188 if (twin) {
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
198 //sticky bit: 4096
199 //transparency instead of rgb tint: 0x100000, fade off is in dice size
200 //blend: 0x300000
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)
206 bool playonce;
207 bool doublehint;
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) {
213 doublehint = true;
214 } else {
215 doublehint = false;
217 ScriptedAnimation *sca = gamedata->GetScriptedAnimation(fx->Resource, doublehint);
218 if (!sca)
219 return FX_NOT_APPLIED;
221 switch (fx->Parameter2&0x300000) {
222 case 0x300000:
223 sca->SetBlend(); //per pixel transparency
224 break;
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
228 break;
229 case 0x100000: //per surface transparency
230 sca->SetFade((ieByte) fx->Parameter1, fx->DiceSides);
231 break;
232 default:
233 if (fx->Parameter1) {
234 RGBModifier rgb;
236 rgb.speed=-1;
237 rgb.phase=0;
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) {
247 playonce=true;
248 } else {
249 playonce=false;
251 switch (fx->Parameter2&0x30000) {
252 case 0x20000://foreground
253 sca->ZPos+=9999;
254 break;
255 case 0x30000: //both
256 sca->ZPos+=9999;
257 if (sca->twin) {
258 sca->twin->ZPos-=9999;
260 break;
261 default: //background
262 sca->ZPos-=9999;
263 break;
265 if (playonce) {
266 sca->PlayOnce();
267 } else {
268 sca->SetDefaultDuration(fx->Duration-core->GetGame()->Ticks);
270 ScriptedAnimation *twin = sca->DetachTwin();
271 if (fx->Parameter2&4096) {
272 if (twin) {
273 target->AddVVCell(twin);
275 target->AddVVCell(sca);
276 } else {
277 //the random placement works only when it is not sticky
278 int x = 0;
279 int y = 0;
280 if (fx->Parameter2&1) {
281 ieWord tmp =(ieWord) rand();
282 x = tmp&31;
283 y = (tmp>>5)&31;
286 sca->XPos+=fx->PosX-x;
287 sca->YPos+=fx->PosY+sca->ZPos-y;
288 if (twin) {
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;
308 if (owner==target) {
309 return FX_NOT_APPLIED;
312 Actor *receiver;
313 Actor *donor;
314 int a,b;
316 switch(fx->Parameter2) {
317 case 3:
318 case 0: receiver = target; donor = owner; break;
319 case 4:
320 case 1: receiver = owner; donor = target; break;
321 case 2:
322 a = owner->GetBase(IE_HITPOINTS);
323 b = target->GetBase(IE_HITPOINTS);
324 owner->SetBase(IE_HITPOINTS, a);
325 target->SetBase(IE_HITPOINTS, b);
326 //fallthrough
327 default:
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;
370 //0xc5-c8 fx_unknown
371 //0xc9 fx_overlay
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;
379 //0xca fx_unknown
381 //0x82 fx_bless
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);
404 return FX_APPLIED;
407 //0xcb fx_curse
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);
424 return FX_APPLIED;
427 //0xcc fx_prayer
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);
435 int type;
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));
444 newfx->Duration=60;
445 while(i--) {
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);
457 delete newfx;
458 return FX_APPLIED;
461 //0xcd fx_move_view
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();
466 if (map) {
467 core->timer->SetMoveViewPort( fx->PosX, fx->PosY, fx->Parameter1, true);
469 return FX_NOT_APPLIED;
472 //0xce fx_embalm
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;
482 } else {
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 );
490 } else {
491 STAT_ADD( IE_ARMORCLASS,1 );
493 return FX_APPLIED;
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);
501 } else {
502 core->GetGame()->TimeStop(NULL, 0);
504 return FX_NOT_APPLIED;
507 //0xd0 fx_iron_fist
508 //GemRB extension: lets you specify not hardcoded values
509 int fx_iron_fist (Scriptable* /*Owner*/, Actor* target, Effect* fx)
511 ieDword p1,p2;
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;
517 default:
518 p1 = ieWord (fx->Parameter1&0xffff);
519 p2 = ieWord (fx->Parameter1>>16);
521 STAT_ADD(IE_FISTHIT, p1);
522 STAT_ADD(IE_FISTDAMAGE, p2);
523 return FX_APPLIED;
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);
553 delete 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);
557 delete fxqueue;
559 return FX_APPLIED;
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) {
573 //hiccups
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
581 target->GetHit();
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);
589 return FX_APPLIED;
592 #include "plugindef.h"
594 GEMRB_PLUGIN(0x115A670, "Effect opcodes for the torment branch of the games")
595 PLUGIN_INITIALIZER(RegisterTormentOpcodes)
596 END_PLUGIN()