implemented hostile spell/item flag
[gemrb.git] / gemrb / core / EffectQueue.cpp
blob5927819bdeb6617558e30a2333955e82868aebf7
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 "EffectQueue.h"
23 #include "DisplayMessage.h"
24 #include "Effect.h"
25 #include "Game.h"
26 #include "Interface.h"
27 #include "Map.h"
28 #include "SymbolMgr.h"
29 #include "Scriptable/Actor.h"
30 #include "Spell.h" //needs for the source flags bitfield
32 #include <cstdio>
34 static struct {
35 const char* Name;
36 EffectFunction Function;
37 int Strref;
38 } Opcodes[MAX_EFFECTS];
40 static int initialized = 0;
42 static EffectRef *effectnames = NULL;
43 static int effectnames_count = 0;
45 bool EffectQueue::match_ids(Actor *target, int table, ieDword value)
47 if( value == 0) {
48 return true;
51 int a, stat;
53 switch (table) {
54 case 2: //EA
55 stat = IE_EA; break;
56 case 3: //GENERAL
57 stat = IE_GENERAL; break;
58 case 4: //RACE
59 stat = IE_RACE; break;
60 case 5: //CLASS
61 stat = IE_CLASS; break;
62 case 6: //SPECIFIC
63 stat = IE_SPECIFIC; break;
64 case 7: //GENDER
65 stat = IE_SEX; break;
66 case 8: //ALIGNMENT
67 stat = target->GetStat(IE_ALIGNMENT);
68 a = value&15;
69 if( a) {
70 if( a != ( stat & 15 )) {
71 return false;
74 a = value & 0xf0;
75 if( a) {
76 if( a != ( stat & 0xf0 )) {
77 return false;
80 return true;
81 default:
82 return false;
84 if( target->GetStat(stat)==value) {
85 return true;
87 return false;
90 static const bool fx_instant[MAX_TIMING_MODE]={true,true,true,false,false,false,false,false,true,true,true};
92 inline bool IsInstant(ieByte timingmode)
94 if( timingmode>=MAX_TIMING_MODE) return false;
95 return fx_instant[timingmode];
98 static const bool fx_equipped[MAX_TIMING_MODE]={false,false,true,false,false,true,false,false,true,false,false};
100 inline bool IsEquipped(ieByte timingmode)
102 if( timingmode>=MAX_TIMING_MODE) return false;
103 return fx_equipped[timingmode];
106 // 0 1 2 3 4 5 6 7 8 9 10
107 static const bool fx_relative[MAX_TIMING_MODE]={true,false,false,true,true,true,false,false,false,false,false};
109 inline bool NeedPrepare(ieWord timingmode)
111 if( timingmode>=MAX_TIMING_MODE) return false;
112 return fx_relative[timingmode];
115 #define INVALID -1
116 #define PERMANENT 0
117 #define DELAYED 1
118 #define DURATION 2
120 static const int fx_prepared[MAX_TIMING_MODE]={DURATION,PERMANENT,PERMANENT,DELAYED, //0-3
121 DELAYED,DELAYED,DELAYED,DELAYED,PERMANENT,PERMANENT,PERMANENT}; //4-7
123 inline int DelayType(ieByte timingmode)
125 if( timingmode>=MAX_TIMING_MODE) return INVALID;
126 return fx_prepared[timingmode];
129 //which effects are removable
130 static const bool fx_removable[MAX_TIMING_MODE]={true,true,false,true,true,false,true,true,false,false,true};
132 inline int IsRemovable(ieByte timingmode)
134 if( timingmode>=MAX_TIMING_MODE) return INVALID;
135 return fx_removable[timingmode];
138 //change the timing method after the effect triggered
139 static const ieByte fx_triggered[MAX_TIMING_MODE]={FX_DURATION_JUST_EXPIRED,FX_DURATION_INSTANT_PERMANENT,//0,1
140 FX_DURATION_INSTANT_WHILE_EQUIPPED,FX_DURATION_DELAY_LIMITED_PENDING,//2,3
141 FX_DURATION_AFTER_EXPIRES,FX_DURATION_PERMANENT_UNSAVED, //4,5
142 FX_DURATION_INSTANT_LIMITED,FX_DURATION_JUST_EXPIRED,FX_DURATION_PERMANENT_UNSAVED,//6,8
143 FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES,FX_DURATION_JUST_EXPIRED};//9,10
145 //change the timing method for effect that should trigger after this effect expired
146 static const ieDword fx_to_delayed[]={FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED,
147 FX_DURATION_PERMANENT_UNSAVED,FX_DURATION_DELAY_LIMITED_PENDING,
148 FX_DURATION_AFTER_EXPIRES,FX_DURATION_PERMANENT_UNSAVED, //4,5
149 FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED,//6,8
150 FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED};//9,10
152 inline ieByte TriggeredEffect(ieByte timingmode)
154 if( timingmode>=MAX_TIMING_MODE) return false;
155 return fx_triggered[timingmode];
158 int compare_effects(const void *a, const void *b)
160 return stricmp(((EffectRef *) a)->Name,((EffectRef *) b)->Name);
163 int find_effect(const void *a, const void *b)
165 return stricmp((const char *) a,((const EffectRef *) b)->Name);
168 static EffectRef* FindEffect(const char* effectname)
170 if( !effectname || !effectnames) {
171 return NULL;
173 void *tmp = bsearch(effectname, effectnames, effectnames_count, sizeof(EffectRef), find_effect);
174 if( !tmp) {
175 printMessage( "EffectQueue", "", YELLOW);
176 printf("Couldn't assign effect: %s\n", effectname );
178 return (EffectRef *) tmp;
181 static EffectRef fx_protection_from_display_string_ref={"Protection:String",NULL,-1};
183 //special effects without level check (but with damage dices precalculated)
184 static EffectRef diced_effects[] = {
185 //core effects
186 {"Damage",NULL,-1},
187 {"CurrentHPModifier",NULL,-1},
188 {"MaximumHPModifier",NULL,-1},
189 //iwd effects
190 {"BurningBlood",NULL,-1}, //iwd
191 {"ColdDamage",NULL,-1},
192 {"CrushingDamage",NULL,-1},
193 {"VampiricTouch",NULL,-1},
194 {"VitriolicSphere",NULL,-1},
195 //pst effects
196 {"TransferHP",NULL,-1},
197 {NULL,NULL,0} };
199 //special effects without level check (but with damage dices not precalculated)
200 static EffectRef diced_effects2[] = {
201 {"BurningBlood2",NULL,-1}, //how/iwd2
202 {"StaticCharge",NULL,-1}, //how/iwd2
203 {"SoulEater",NULL,-1}, //how/iwd2
204 {"LichTouch",NULL,-1}, //how
205 {NULL,NULL,0} };
207 inline static void ResolveEffectRef(EffectRef &effect_reference)
209 if( effect_reference.opcode==-1) {
210 EffectRef* ref = FindEffect(effect_reference.Name);
211 if( ref && ref->opcode>=0) {
212 effect_reference.opcode = ref->opcode;
213 return;
215 effect_reference.opcode = -2;
219 bool Init_EffectQueue()
221 int i;
223 if( initialized) {
224 return true;
226 memset( Opcodes, 0, sizeof( Opcodes ) );
227 for(i=0;i<MAX_EFFECTS;i++) {
228 Opcodes[i].Strref=-1;
231 initialized = 1;
233 AutoTable efftextTable("efftext");
235 int eT = core->LoadSymbol( "effects" );
236 if( eT < 0) {
237 printMessage( "EffectQueue","A critical scripting file is missing!\n",LIGHT_RED );
238 return false;
240 Holder<SymbolMgr> effectsTable = core->GetSymbol( eT );
241 if( !effectsTable) {
242 printMessage( "EffectQueue","A critical scripting file is damaged!\n",LIGHT_RED );
243 return false;
246 for (i = 0; i < MAX_EFFECTS; i++) {
247 const char* effectname = effectsTable->GetValue( i );
248 if( efftextTable) {
249 int row = efftextTable->GetRowCount();
250 while (row--) {
251 const char* ret = efftextTable->GetRowName( row );
252 long val;
253 if( valid_number( ret, val ) && (i == val) ) {
254 Opcodes[i].Strref = atoi( efftextTable->QueryField( row, 1 ) );
259 EffectRef* poi = FindEffect( effectname );
260 if( poi != NULL) {
261 Opcodes[i].Function = poi->Function;
262 Opcodes[i].Name = poi->Name;
263 //reverse linking opcode number
264 //using this unused field
265 if( (poi->opcode!=-1) && effectname[0]!='*') {
266 printf("Clashing Opcodes FN: %d vs. %d, %s\n", i, poi->opcode, effectname);
267 abort();
269 poi->opcode = i;
271 //printf("-------- FN: %d, %s\n", i, effectname);
273 core->DelSymbol( eT );
275 //additional initialisations
276 for (i=0;diced_effects[i].Name;i++) {
277 ResolveEffectRef(diced_effects[i]);
279 for (i=0;diced_effects2[i].Name;i++) {
280 ResolveEffectRef(diced_effects2[i]);
283 return true;
286 void EffectQueue_ReleaseMemory()
288 if( effectnames) {
289 free (effectnames);
291 effectnames_count = 0;
292 effectnames = NULL;
295 void EffectQueue_RegisterOpcodes(int count, const EffectRef* opcodes)
297 if( ! effectnames) {
298 effectnames = (EffectRef*) malloc( (count+1) * sizeof( EffectRef ) );
299 } else {
300 effectnames = (EffectRef*) realloc( effectnames, (effectnames_count + count + 1) * sizeof( EffectRef ) );
303 memcpy( effectnames + effectnames_count, opcodes, count * sizeof( EffectRef ));
304 effectnames_count += count;
305 effectnames[effectnames_count].Name = NULL;
306 //if we merge two effect lists, then we need to sort their effect tables
307 //actually, we might always want to sort this list, so there is no
308 //need to do it manually (sorted table is needed if we use bsearch)
309 qsort(effectnames, effectnames_count, sizeof(EffectRef), compare_effects);
312 EffectQueue::EffectQueue()
314 Owner = NULL;
317 EffectQueue::~EffectQueue()
319 std::list< Effect* >::iterator f;
321 for ( f = effects.begin(); f != effects.end(); f++ ) {
322 delete (*f);
326 Effect *EffectQueue::CreateEffect(ieDword opcode, ieDword param1, ieDword param2, ieWord timing)
328 if( opcode==0xffffffff) {
329 return NULL;
331 Effect *fx = new Effect();
332 if( !fx) {
333 return NULL;
335 memset(fx,0,sizeof(Effect));
336 fx->Target = FX_TARGET_SELF;
337 fx->Opcode = opcode;
338 //probability2 is the low number (by effectqueue 331)
339 fx->Probability1 = 100;
340 fx->Parameter1 = param1;
341 fx->Parameter2 = param2;
342 fx->TimingMode = timing;
343 fx->PosX = 0xffffffff;
344 fx->PosY = 0xffffffff;
345 return fx;
348 //return the count of effects with matching parameters
349 //useful for effects where there is no separate stat to see
350 ieDword EffectQueue::CountEffects(EffectRef &effect_reference, ieDword param1, ieDword param2, const char *resource) const
352 ResolveEffectRef(effect_reference);
353 if( effect_reference.opcode<0) {
354 return 0;
356 return CountEffects(effect_reference.opcode, param1, param2, resource);
359 //Change the location of an existing effect
360 //this is used when some external code needs to adjust the effect's location
361 //used when the gui sets the effect's final target
362 void EffectQueue::ModifyEffectPoint(EffectRef &effect_reference, ieDword x, ieDword y) const
364 ResolveEffectRef(effect_reference);
365 if( effect_reference.opcode<0) {
366 return;
368 ModifyEffectPoint(effect_reference.opcode, x, y);
371 Effect *EffectQueue::CreateEffect(EffectRef &effect_reference, ieDword param1, ieDword param2, ieWord timing)
373 ResolveEffectRef(effect_reference);
374 if( effect_reference.opcode<0) {
375 return NULL;
377 return CreateEffect(effect_reference.opcode, param1, param2, timing);
380 //copies the whole effectqueue (area projectiles use it)
381 EffectQueue *EffectQueue::CopySelf() const
383 EffectQueue *effects;
385 effects = new EffectQueue();
386 std::list< Effect* >::const_iterator fxit = GetFirstEffect();
387 Effect *fx;
389 while( (fx = GetNextEffect(fxit))) {
390 effects->AddEffect(fx, false);
392 effects->SetOwner(GetOwner());
393 return effects;
396 //create a new effect with most of the characteristics of the old effect
397 //only opcode and parameters are changed
398 //This is used mostly inside effects, when an effect needs to spawn
399 //other effects with the same coordinates, source, duration, etc.
400 Effect *EffectQueue::CreateEffectCopy(Effect *oldfx, ieDword opcode, ieDword param1, ieDword param2)
402 if( opcode==0xffffffff) {
403 return NULL;
405 Effect *fx = new Effect();
406 if( !fx) {
407 return NULL;
409 memcpy(fx,oldfx,sizeof(Effect) );
410 fx->Opcode=opcode;
411 fx->Parameter1=param1;
412 fx->Parameter2=param2;
413 return fx;
416 Effect *EffectQueue::CreateEffectCopy(Effect *oldfx, EffectRef &effect_reference, ieDword param1, ieDword param2)
418 ResolveEffectRef(effect_reference);
419 if( effect_reference.opcode<0) {
420 return NULL;
422 return CreateEffectCopy(oldfx, effect_reference.opcode, param1, param2);
425 static EffectRef fx_unsummon_creature_ref={"UnsummonCreature",NULL,-1};
427 Effect *EffectQueue::CreateUnsummonEffect(Effect *fx)
429 Effect *newfx = NULL;
430 if( (fx->TimingMode&0xff) == FX_DURATION_INSTANT_LIMITED) {
431 newfx = CreateEffectCopy(fx, fx_unsummon_creature_ref, 0, 0);
432 newfx->TimingMode = FX_DURATION_DELAY_PERMANENT;
433 if( newfx->Resource3[0]) {
434 strnuprcpy(newfx->Resource,newfx->Resource3, sizeof(ieResRef)-1 );
435 } else {
436 strnuprcpy(newfx->Resource,"SPGFLSH1", sizeof(ieResRef)-1 );
438 if( fx->TimingMode == FX_DURATION_ABSOLUTE) {
439 //unprepare duration
440 newfx->Duration = (newfx->Duration-core->GetGame()->GameTime)/AI_UPDATE_TIME;
444 return newfx;
447 void EffectQueue::AddEffect(Effect* fx, bool insert)
449 Effect* new_fx = new Effect;
450 memcpy( new_fx, fx, sizeof( Effect ) );
451 if( insert) {
452 effects.insert( effects.begin(), new_fx );
453 } else {
454 effects.push_back( new_fx );
458 //This method can remove an effect described by a pointer to it, or
459 //an exact matching effect
460 bool EffectQueue::RemoveEffect(Effect* fx)
462 int invariant_size = offsetof( Effect, random_value );
464 for (std::list< Effect* >::iterator f = effects.begin(); f != effects.end(); f++ ) {
465 Effect* fx2 = *f;
467 if( (fx==fx2) || !memcmp( fx, fx2, invariant_size)) {
468 delete fx2;
469 effects.erase( f );
470 return true;
473 return false;
476 //this is where we reapply all effects when loading a saved game
477 //The effects are already in the fxqueue of the target
478 void EffectQueue::ApplyAllEffects(Actor* target) const
480 std::list< Effect* >::const_iterator f;
481 for ( f = effects.begin(); f != effects.end(); f++ ) {
482 ApplyEffect( target, *f, 0 );
486 void EffectQueue::Cleanup()
488 std::list< Effect* >::iterator f;
490 for ( f = effects.begin(); f != effects.end(); ) {
491 if( (*f)->TimingMode == FX_DURATION_JUST_EXPIRED) {
492 delete *f;
493 effects.erase(f++);
494 } else {
495 f++;
500 //Handle the target flag when the effect is applied first
501 int EffectQueue::AddEffect(Effect* fx, Scriptable* self, Actor* pretarget, const Point &dest) const
503 int i;
504 Game *game;
505 Map *map;
506 int flg;
507 ieDword spec = 0;
508 Actor *st = (self && (self->Type==ST_ACTOR)) ?(Actor *) self:NULL;
510 switch (fx->Target) {
511 case FX_TARGET_ORIGINAL:
512 fx->SetPosition(self->Pos);
514 flg = ApplyEffect( st, fx, 1 );
515 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
516 if( st) {
517 st->fxqueue.AddEffect( fx, flg==FX_INSERT );
520 break;
521 case FX_TARGET_SELF:
522 fx->SetPosition(dest);
524 flg = ApplyEffect( st, fx, 1 );
525 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
526 if( st) {
527 st->fxqueue.AddEffect( fx, flg==FX_INSERT );
530 break;
532 case FX_TARGET_ALL_BUT_SELF:
533 map=self->GetCurrentArea();
534 i= map->GetActorCount(true);
535 while(i--) {
536 Actor* actor = map->GetActor( i, true );
537 //don't pick ourselves
538 if( st==actor) {
539 continue;
541 fx->SetPosition(actor->Pos);
543 flg = ApplyEffect( actor, fx, 1 );
544 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
545 actor->fxqueue.AddEffect( fx, flg==FX_INSERT );
548 flg = FX_APPLIED;
549 break;
551 case FX_TARGET_OWN_SIDE:
552 if( !st || st->InParty) {
553 goto all_party;
555 map = self->GetCurrentArea();
556 spec = st->GetStat(IE_SPECIFIC);
558 //GetActorCount(false) returns all nonparty critters
559 i = map->GetActorCount(false);
560 while(i--) {
561 Actor* actor = map->GetActor( i, false );
562 if( actor->GetStat(IE_SPECIFIC)!=spec) {
563 continue;
565 fx->SetPosition(actor->Pos);
567 flg = ApplyEffect( actor, fx, 1 );
568 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
569 actor->fxqueue.AddEffect( fx, flg==FX_INSERT );
572 flg = FX_APPLIED;
573 break;
574 case FX_TARGET_OTHER_SIDE:
575 if( !pretarget || pretarget->InParty) {
576 goto all_party;
578 map = self->GetCurrentArea();
579 spec = pretarget->GetStat(IE_SPECIFIC);
581 //GetActorCount(false) returns all nonparty critters
582 i = map->GetActorCount(false);
583 while(i--) {
584 Actor* actor = map->GetActor( i, false );
585 if( actor->GetStat(IE_SPECIFIC)!=spec) {
586 continue;
588 fx->SetPosition(actor->Pos);
590 flg = ApplyEffect( actor, fx, 1 );
591 //GetActorCount can now return all nonparty critters
592 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
593 actor->fxqueue.AddEffect( fx, flg==FX_INSERT );
596 flg = FX_APPLIED;
597 break;
598 case FX_TARGET_PRESET:
599 fx->SetPosition(pretarget->Pos);
601 flg = ApplyEffect( pretarget, fx, 1 );
602 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
603 if( pretarget) {
604 pretarget->fxqueue.AddEffect( fx, flg==FX_INSERT );
607 break;
609 case FX_TARGET_PARTY:
610 all_party:
611 game = core->GetGame();
612 i = game->GetPartySize(true);
613 while(i--) {
614 Actor* actor = game->GetPC( i, true );
615 fx->SetPosition(actor->Pos);
617 flg = ApplyEffect( actor, fx, 1 );
618 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
619 actor->fxqueue.AddEffect( fx, flg==FX_INSERT );
622 flg = FX_APPLIED;
623 break;
625 case FX_TARGET_ALL:
626 map = self->GetCurrentArea();
627 i = map->GetActorCount(true);
628 while(i--) {
629 Actor* actor = map->GetActor( i, true );
630 fx->SetPosition(actor->Pos);
632 flg = ApplyEffect( actor, fx, 1 );
633 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
634 actor->fxqueue.AddEffect( fx, flg==FX_INSERT );
637 flg = FX_APPLIED;
638 break;
640 case FX_TARGET_ALL_BUT_PARTY:
641 map = self->GetCurrentArea();
642 i = map->GetActorCount(false);
643 while(i--) {
644 Actor* actor = map->GetActor( i, false );
645 fx->SetPosition(actor->Pos);
647 flg = ApplyEffect( actor, fx, 1 );
648 //GetActorCount can now return all nonparty critters
649 if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) {
650 actor->fxqueue.AddEffect( fx, flg==FX_INSERT );
653 flg = FX_APPLIED;
654 break;
656 case FX_TARGET_UNKNOWN:
657 default:
658 printf( "Unknown FX target type: %d\n", fx->Target);
659 flg = FX_ABORT;
660 break;
663 return flg;
666 //this is where effects from spells first get in touch with the target
667 //the effects are currently NOT in the target's fxqueue, those that stick
668 //will get copied (hence the fxqueue.AddEffect call)
669 //if this returns FX_NOT_APPLIED, then the whole stack was resisted
670 //or expired
671 int EffectQueue::AddAllEffects(Actor* target, const Point &destination) const
673 int res = FX_NOT_APPLIED;
674 // pre-roll dice for fx needing them and stow them in the effect
675 ieDword random_value = core->Roll( 1, 100, 0 );
677 if( target) {
678 target->RollSaves();
680 std::list< Effect* >::const_iterator f;
681 for ( f = effects.begin(); f != effects.end(); f++ ) {
682 //handle resistances and saving throws here
683 (*f)->random_value = random_value;
684 //if applyeffect returns true, we stop adding the future effects
685 //this is to simulate iwd2's on the fly spell resistance
687 int tmp = AddEffect(*f, Owner, target, destination);
688 //lets try without Owner, any crash?
689 //If yes, then try to fix the individual effect
690 //If you use target for Owner here, the wand in chateau irenicus will work
691 //the same way as Imoen's monster summoning, which is a BAD THING (TM)
692 //int tmp = AddEffect(*f, Owner?Owner:target, target, destination);
693 if( tmp == FX_ABORT) {
694 res = FX_NOT_APPLIED;
695 break;
697 if( tmp != FX_NOT_APPLIED) {
698 res = FX_APPLIED;
701 return res;
704 //check if an effect has no level based resistance, but instead the dice sizes/count
705 //adjusts Parameter1 (like a damage causing effect)
706 inline static bool IsDicedEffect(int opcode)
708 int i;
710 for(i=0;diced_effects[i].Name;i++) {
711 if( diced_effects[i].opcode==opcode) {
712 return true;
715 return false;
718 //there is no level based resistance, but Parameter1 cannot be precalculated
719 //these effects use the Dice fields in a special way
720 inline static bool IsDicedEffect2(int opcode)
722 int i;
724 for(i=0;diced_effects2[i].Name;i++) {
725 if( diced_effects2[i].opcode==opcode) {
726 return true;
729 return false;
732 //resisted effect based on level
733 inline bool check_level(Actor *target, Effect *fx)
735 //skip non level based effects
736 if( IsDicedEffect((int) fx->Opcode)) {
737 fx->Parameter1 = DICE_ROLL((signed)fx->Parameter1);
738 //this is a hack for PST style diced effects
739 if( core->HasFeature(GF_SAVE_FOR_HALF) ) {
740 if( memcmp(fx->Resource,"NEG",4) ) {
741 fx->IsSaveForHalfDamage=1;
743 } else {
744 if( (fx->Parameter2&3)==3) {
745 fx->IsSaveForHalfDamage=1;
748 return false;
750 if( IsDicedEffect2((int) fx->Opcode)) {
751 return false;
754 if( !target) {
755 return false;
757 if(fx->Target == FX_TARGET_SELF) {
758 return false;
761 ieDword level = (ieDword) target->GetXPLevel( true );
762 //return true if resisted
763 //level resistance is checked when DiceSides or DiceThrown
764 //are greater than 0 (sometimes they used -1 for our amusement)
765 //if level>than maximum affected or level<than minimum affected, then the
766 //effect is resisted
767 if( (fx->DiceSides > 0 || fx->DiceThrown > 0) && (level > fx->DiceSides || level < fx->DiceThrown)) {
768 return true;
770 return false;
773 //roll for the effect probability, there is a high and a low treshold, the d100
774 //roll should hit in the middle
775 inline bool check_probability(Effect* fx)
777 //watch for this, probability1 is the high number
778 //probability2 is the low number
779 //random value is 1-100
780 if( fx->random_value<=fx->Probability2 || fx->random_value>fx->Probability1) {
781 return false;
783 return true;
786 //immunity effects
787 static EffectRef fx_level_immunity_ref={"Protection:Spelllevel",NULL,-1};
788 static EffectRef fx_opcode_immunity_ref={"Protection:Opcode",NULL,-1}; //bg2
789 static EffectRef fx_opcode_immunity2_ref={"Protection:Opcode2",NULL,-1};//iwd
790 static EffectRef fx_spell_immunity_ref={"Protection:Spell",NULL,-1}; //bg2
791 static EffectRef fx_spell_immunity2_ref={"Protection:Spell2",NULL,-1};//iwd
792 static EffectRef fx_school_immunity_ref={"Protection:School",NULL,-1};
793 static EffectRef fx_secondary_type_immunity_ref={"Protection:SecondaryType",NULL,-1};
795 //decrementing immunity effects
796 static EffectRef fx_level_immunity_dec_ref={"Protection:SpellLevelDec",NULL,-1};
797 static EffectRef fx_spell_immunity_dec_ref={"Protection:SpellDec",NULL,-1};
798 static EffectRef fx_school_immunity_dec_ref={"Protection:SchoolDec",NULL,-1};
799 static EffectRef fx_secondary_type_immunity_dec_ref={"Protection:SecondaryTypeDec",NULL,-1};
801 //bounce effects
802 static EffectRef fx_level_bounce_ref={"Bounce:SpellLevel",NULL,-1};
803 //static EffectRef fx_opcode_bounce_ref={"Bounce:Opcode",NULL,-1};
804 static EffectRef fx_spell_bounce_ref={"Bounce:Spell",NULL,-1};
805 static EffectRef fx_school_bounce_ref={"Bounce:School",NULL,-1};
806 static EffectRef fx_secondary_type_bounce_ref={"Bounce:SecondaryType",NULL,-1};
808 //decrementing bounce effects
809 static EffectRef fx_level_bounce_dec_ref={"Bounce:SpellLevelDec",NULL,-1};
810 static EffectRef fx_spell_bounce_dec_ref={"Bounce:SpellDec",NULL,-1};
811 static EffectRef fx_school_bounce_dec_ref={"Bounce:SchoolDec",NULL,-1};
812 static EffectRef fx_secondary_type_bounce_dec_ref={"Bounce:SecondaryTypeDec",NULL,-1};
814 //spelltrap (multiple decrementing immunity)
815 static EffectRef fx_spelltrap={"SpellTrap", NULL,-1};
817 //this is for whole spell immunity/bounce
818 inline static void DecreaseEffect(Effect *efx)
820 efx->Parameter1--;
821 if( (int) efx->Parameter1<1) {
822 //don't remove effects directly!!!
823 efx->TimingMode = FX_DURATION_JUST_EXPIRED;
827 //lower decreasing immunities/bounces
828 static int check_type(Actor* actor, Effect* fx)
830 //the protective effect (if any)
831 Effect *efx;
833 ieDword bounce = actor->GetStat(IE_BOUNCE);
835 //immunity checks
836 /*opcode immunity is in the per opcode checks
837 if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity_ref, fx->Opcode) ) {
838 return 0;
840 if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity2_ref, fx->Opcode) ) {
841 return 0;
844 //spell level immunity
845 if(fx->Power && actor->fxqueue.HasEffectWithParamPair(fx_level_immunity_ref, fx->Power, 0) ) {
846 return 0;
849 //source immunity (spell name)
850 //if source is unspecified, don't resist it
851 if( fx->Source[0]) {
852 if( actor->fxqueue.HasEffectWithResource(fx_spell_immunity_ref, fx->Source) ) {
853 return 0;
855 if( actor->fxqueue.HasEffectWithResource(fx_spell_immunity2_ref, fx->Source) ) {
856 return 0;
860 //primary type immunity (school)
861 if( fx->PrimaryType) {
862 if( actor->fxqueue.HasEffectWithParam(fx_school_immunity_ref, fx->PrimaryType)) {
863 return 0;
867 //secondary type immunity (usage)
868 if( fx->SecondaryType) {
869 if( actor->fxqueue.HasEffectWithParam(fx_secondary_type_immunity_ref, fx->SecondaryType) ) {
870 return 0;
874 //decrementing immunity checks
875 //decrementing level immunity
876 efx = actor->fxqueue.HasEffectWithParamPair(fx_level_immunity_dec_ref, fx->Power, 0);
877 if( efx ) {
878 DecreaseEffect(efx);
879 return 0;
882 //decrementing spell immunity
883 if( fx->Source[0]) {
884 efx = actor->fxqueue.HasEffectWithResource(fx_spell_immunity_dec_ref, fx->Source);
885 if( efx) {
886 DecreaseEffect(efx);
887 return 0;
890 //decrementing primary type immunity (school)
891 if( fx->PrimaryType) {
892 efx = actor->fxqueue.HasEffectWithParam(fx_school_immunity_dec_ref, fx->PrimaryType);
893 if( efx) {
894 DecreaseEffect(efx);
895 return 0;
899 //decrementing secondary type immunity (usage)
900 if( fx->SecondaryType) {
901 efx = actor->fxqueue.HasEffectWithParam(fx_secondary_type_immunity_dec_ref, fx->SecondaryType);
902 if( efx) {
903 DecreaseEffect(efx);
904 return 0;
908 //spelltrap (absorb)
909 //FIXME:
910 //if the spelltrap effect already absorbed enough levels
911 //but still didn't get removed, it will absorb levels it shouldn't
912 //it will also absorb multiple spells in a single round
913 efx=actor->fxqueue.HasEffectWithParamPair(fx_spelltrap, 0, fx->Power);
914 if( efx) {
915 //storing the absorbed spell level
916 efx->Parameter3+=fx->Power;
917 //instead of a single effect, they had to create an effect for each level
918 //HOW DAMN LAME
919 //if decrease needs the spell level, use fx->Power here
920 actor->fxqueue.DecreaseParam1OfEffect(fx_spelltrap, 1);
921 //efx->Parameter1--;
922 return 0;
925 //bounce checks
926 if( (bounce&BNC_LEVEL) && actor->fxqueue.HasEffectWithParamPair(fx_level_bounce_ref, fx->Power, 0) ) {
927 return 0;
930 if( fx->Source[0] && (bounce&BNC_RESOURCE) && actor->fxqueue.HasEffectWithResource(fx_spell_bounce_ref, fx->Source) ) {
931 return -1;
934 if( fx->PrimaryType && (bounce&BNC_SCHOOL) ) {
935 if( actor->fxqueue.HasEffectWithParam(fx_school_bounce_ref, fx->PrimaryType)) {
936 return -1;
940 if( fx->SecondaryType && (bounce&BNC_SECTYPE) ) {
941 if( actor->fxqueue.HasEffectWithParam(fx_secondary_type_bounce_ref, fx->SecondaryType)) {
942 return -1;
945 //decrementing bounce checks
947 //level decrementing bounce check
948 if( (bounce&BNC_LEVEL_DEC)) {
949 efx=actor->fxqueue.HasEffectWithParamPair(fx_level_bounce_dec_ref, fx->Power, 0);
950 if( efx) {
951 DecreaseEffect(efx);
952 return -1;
956 if( fx->Source[0] && (bounce&BNC_RESOURCE_DEC)) {
957 efx=actor->fxqueue.HasEffectWithResource(fx_spell_bounce_dec_ref, fx->Resource);
958 if( efx) {
959 DecreaseEffect(efx);
960 return -1;
964 if( fx->PrimaryType && (bounce&BNC_SCHOOL_DEC) ) {
965 efx=actor->fxqueue.HasEffectWithParam(fx_school_bounce_dec_ref, fx->PrimaryType);
966 if( efx) {
967 DecreaseEffect(efx);
968 return -1;
972 if( fx->SecondaryType && (bounce&BNC_SECTYPE_DEC) ) {
973 efx=actor->fxqueue.HasEffectWithParam(fx_secondary_type_bounce_dec_ref, fx->SecondaryType);
974 if( efx) {
975 DecreaseEffect(efx);
976 return -1;
980 return 1;
983 //check resistances, saving throws
984 static bool check_resistance(Actor* actor, Effect* fx)
986 if( !actor) {
987 return false;
990 //opcode immunity
991 if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity_ref, fx->Opcode) ) {
992 printf ("immune to effect: %s\n", (char*) Opcodes[fx->Opcode].Name);
993 return true;
995 if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity2_ref, fx->Opcode) ) {
996 printf ("immune2 to effect: %s\n", (char*) Opcodes[fx->Opcode].Name);
997 return true;
1000 /* opcode bouncing isn't implemented?
1001 //opcode bouncing
1002 if( actor->fxqueue.HasEffectWithParam(fx_opcode_bounce_ref, fx->Opcode) ) {
1003 return false;
1007 //not resistable (no saves either?)
1008 if( fx->Resistance != FX_CAN_RESIST_CAN_DISPEL) {
1009 return false;
1012 //don't resist self
1013 if (fx->Target==FX_TARGET_SELF) {
1014 if (core->HasFeature(GF_SELECTIVE_MAGIC_RES) ) {
1015 return false;
1019 //magic immunity
1020 ieDword val = actor->GetStat(IE_RESISTMAGIC);
1021 if( fx->random_value < val) {
1022 printf ("effect resisted: %s\n", (char*) Opcodes[fx->Opcode].Name);
1023 return true;
1026 //saving throws
1027 bool saved = false;
1028 for (int i=0;i<5;i++) {
1029 if( fx->SavingThrowType&(1<<i)) {
1030 saved = actor->GetSavingThrow(i, fx->SavingThrowBonus);
1031 if( saved) {
1032 break;
1036 if( saved) {
1037 if( fx->IsSaveForHalfDamage) {
1038 fx->Parameter1/=2;
1039 } else {
1040 printf ("%s saved against effect: %s\n", actor->GetName(1), (char*) Opcodes[fx->Opcode].Name);
1041 return true;
1044 return false;
1047 // this function is called two different ways
1048 // when FirstApply is set, then the effect isn't stuck on the target
1049 // this happens when a new effect comes in contact with the target.
1050 // if the effect returns FX_DURATION_JUST_EXPIRED then it won't stick
1051 // when first_apply is unset, the effect is already on the target
1052 // this happens on load time too!
1053 // returns FX_NOT_APPLIED if the process shouldn't be calling applyeffect anymore
1054 // returns FX_ABORT if the whole spell this effect is in should be aborted
1055 // it will disable all future effects of same source (only on first apply)
1057 int EffectQueue::ApplyEffect(Actor* target, Effect* fx, ieDword first_apply) const
1059 //printf( "FX 0x%02x: %s(%d, %d)\n", fx->Opcode, effectnames[fx->Opcode].Name, fx->Parameter1, fx->Parameter2 );
1060 if( fx->Opcode >= MAX_EFFECTS) {
1061 fx->TimingMode = FX_DURATION_JUST_EXPIRED;
1062 return FX_NOT_APPLIED;
1065 ieDword GameTime = core->GetGame()->GameTime;
1067 fx->FirstApply=first_apply;
1068 if( first_apply) {
1069 if( (fx->PosX==0xffffffff) && (fx->PosY==0xffffffff)) {
1070 fx->PosX = target->Pos.x;
1071 fx->PosY = target->Pos.y;
1074 //gemrb specific, stat based chance
1075 if ((fx->Probability2 == 100) && Owner && (Owner->Type==ST_ACTOR) ) {
1076 fx->Probability2 = 0;
1077 fx->Probability1 = ((Actor *) Owner)->GetSafeStat(fx->Probability1);
1080 //the effect didn't pass the probability check
1081 if( !check_probability(fx) ) {
1082 fx->TimingMode = FX_DURATION_JUST_EXPIRED;
1083 return FX_NOT_APPLIED;
1086 //the effect didn't pass the target level check
1087 if( check_level(target, fx) ) {
1088 fx->TimingMode = FX_DURATION_JUST_EXPIRED;
1089 return FX_NOT_APPLIED;
1092 //the effect didn't pass the resistance check
1093 if( check_resistance(target, fx) ) {
1094 fx->TimingMode = FX_DURATION_JUST_EXPIRED;
1095 return FX_NOT_APPLIED;
1098 //Same as in items and spells
1099 if (fx->SourceFlags & SF_HOSTILE) {
1100 if (target && (target != Owner) && Owner && (Owner->Type==ST_ACTOR) ) {
1101 target->AttackedBy((Actor *) Owner);
1105 if( NeedPrepare(fx->TimingMode) ) {
1106 //save delay for later
1107 fx->SecondaryDelay = fx->Duration;
1108 if( fx->TimingMode == FX_DURATION_INSTANT_LIMITED) {
1109 fx->TimingMode = FX_DURATION_ABSOLUTE;
1111 PrepareDuration(fx);
1114 //check if the effect has triggered or expired
1115 switch (DelayType(fx->TimingMode&0xff) ) {
1116 case DELAYED:
1117 if( fx->Duration>GameTime) {
1118 return FX_NOT_APPLIED;
1120 //effect triggered
1121 //delayed duration (3)
1122 if( NeedPrepare(fx->TimingMode) ) {
1123 //prepare for delayed duration effects
1124 fx->Duration = fx->SecondaryDelay;
1125 PrepareDuration(fx);
1127 fx->TimingMode=TriggeredEffect(fx->TimingMode);
1128 break;
1129 case DURATION:
1130 if( fx->Duration<=GameTime) {
1131 fx->TimingMode = FX_DURATION_JUST_EXPIRED;
1132 //add a return here, if 0 duration effects shouldn't work
1134 break;
1135 //permanent effect (so there is no warning)
1136 case PERMANENT:
1137 break;
1138 //this shouldn't happen
1139 default:
1140 printf("Unknown delay type: %d (from %d)\n", DelayType(fx->TimingMode&0xff), fx->TimingMode);
1141 abort();
1144 EffectFunction fn = 0;
1145 if( fx->Opcode<MAX_EFFECTS) {
1146 fn = Opcodes[fx->Opcode].Function;
1148 int res = FX_ABORT;
1149 if( fn) {
1150 if( target && first_apply ) {
1151 if( !target->fxqueue.HasEffectWithParamPair(fx_protection_from_display_string_ref, fx->Parameter1, 0) ) {
1152 displaymsg->DisplayStringName( Opcodes[fx->Opcode].Strref, 0xf0f0f0,
1153 target, IE_STR_SOUND);
1157 res=fn( Owner, target, fx );
1159 //if there is no owner, we assume it is the target
1160 switch( res ) {
1161 case FX_APPLIED:
1162 //normal effect with duration
1163 break;
1164 case FX_NOT_APPLIED:
1165 //instant effect, pending removal
1166 //for example, a damage effect
1167 fx->TimingMode = FX_DURATION_JUST_EXPIRED;
1168 break;
1169 case FX_INSERT:
1170 //put this effect in the beginning of the queue
1171 //all known insert effects are 'permanent' too
1172 //that is the AC effect only
1173 //actually, permanent effects seem to be
1174 //inserted by the game engine too
1175 case FX_PERMANENT:
1176 //don't stick around if it was executed permanently
1177 //for example, a permanent strength modifier effect
1178 if( (fx->TimingMode == FX_DURATION_INSTANT_PERMANENT) ) {
1179 fx->TimingMode = FX_DURATION_JUST_EXPIRED;
1181 break;
1182 case FX_ABORT:
1183 break;
1184 default:
1185 abort();
1187 } else {
1188 //effect not found, it is going to be discarded
1189 fx->TimingMode = FX_DURATION_JUST_EXPIRED;
1191 return res;
1194 // looks for opcode with param2
1196 #define MATCH_OPCODE() if((*f)->Opcode!=opcode) { continue; }
1198 // useful for: remove equipped item
1199 #define MATCH_SLOTCODE() if((*f)->InventorySlot!=slotcode) { continue; }
1201 // useful for: remove projectile type
1202 #define MATCH_PROJECTILE() if((*f)->Projectile!=projectile) { continue; }
1204 static const bool fx_live[MAX_TIMING_MODE]={true,true,true,false,false,false,false,false,true,true,false};
1205 inline bool IsLive(ieByte timingmode)
1207 if( timingmode>=MAX_TIMING_MODE) return false;
1208 return fx_live[timingmode];
1211 #define MATCH_LIVE_FX() if(!IsLive((*f)->TimingMode)) { continue; }
1212 #define MATCH_PARAM1() if((*f)->Parameter1!=param1) { continue; }
1213 #define MATCH_PARAM2() if((*f)->Parameter2!=param2) { continue; }
1214 #define MATCH_RESOURCE() if( strnicmp( (*f)->Resource, resource, 8) ) { continue; }
1215 #define MATCH_SOURCE() if( strnicmp( (*f)->Source, Removed, 8) ) { continue; }
1216 #define MATCH_TIMING() if( (*f)->TimingMode!=timing) { continue; }
1218 //call this from an applied effect, after it returns, these effects
1219 //will be killed along with it
1220 void EffectQueue::RemoveAllEffects(ieDword opcode) const
1222 std::list< Effect* >::const_iterator f;
1223 for ( f = effects.begin(); f != effects.end(); f++ ) {
1224 MATCH_OPCODE();
1225 MATCH_LIVE_FX();
1227 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1231 //removes all equipping effects that match slotcode
1232 void EffectQueue::RemoveEquippingEffects(ieDwordSigned slotcode) const
1234 std::list< Effect* >::const_iterator f;
1235 for ( f = effects.begin(); f != effects.end(); f++ ) {
1236 if( !IsEquipped((*f)->TimingMode)) continue;
1237 MATCH_SLOTCODE();
1239 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1243 //removes all effects that match projectile
1244 void EffectQueue::RemoveAllEffectsWithProjectile(ieDword projectile) const
1246 std::list< Effect* >::const_iterator f;
1247 for ( f = effects.begin(); f != effects.end(); f++ ) {
1248 MATCH_PROJECTILE();
1250 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1254 //remove effects belonging to a given spell
1255 void EffectQueue::RemoveAllEffects(const ieResRef Removed) const
1257 std::list< Effect* >::const_iterator f;
1258 for ( f = effects.begin(); f != effects.end(); f++ ) {
1259 MATCH_LIVE_FX();
1260 MATCH_SOURCE();
1262 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1266 //remove effects belonging to a given spell, but only if they match timing method x
1267 void EffectQueue::RemoveAllEffects(const ieResRef Removed, ieByte timing) const
1269 std::list< Effect* >::const_iterator f;
1270 for ( f = effects.begin(); f != effects.end(); f++ ) {
1271 MATCH_TIMING();
1272 MATCH_SOURCE();
1274 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1278 //this will modify effect reference
1279 void EffectQueue::RemoveAllEffects(EffectRef &effect_reference) const
1281 ResolveEffectRef(effect_reference);
1282 if( effect_reference.opcode<0) {
1283 return;
1285 RemoveAllEffects(effect_reference.opcode);
1288 //Removes all effects with a matching resource field
1289 void EffectQueue::RemoveAllEffectsWithResource(ieDword opcode, const ieResRef resource) const
1291 std::list< Effect* >::const_iterator f;
1292 for ( f = effects.begin(); f != effects.end(); f++ ) {
1293 MATCH_OPCODE();
1294 MATCH_LIVE_FX();
1295 MATCH_RESOURCE();
1297 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1301 void EffectQueue::RemoveAllEffectsWithResource(EffectRef &effect_reference, const ieResRef resource) const
1303 ResolveEffectRef(effect_reference);
1304 RemoveAllEffectsWithResource(effect_reference.opcode, resource);
1307 //This method could be used to remove stat modifiers that would lower a stat
1308 //(works only if a higher stat means good for the target)
1309 void EffectQueue::RemoveAllDetrimentalEffects(ieDword opcode, ieDword current) const
1311 std::list< Effect* >::const_iterator f;
1312 for ( f = effects.begin(); f != effects.end(); f++ ) {
1313 MATCH_OPCODE();
1314 MATCH_LIVE_FX();
1315 switch((*f)->Parameter2) {
1316 case 0:case 3:
1317 if( ((signed) (*f)->Parameter1)>=0) continue;
1318 break;
1319 case 1:case 4:
1320 if( ((signed) (*f)->Parameter1)>=(signed) current) continue;
1321 break;
1322 case 2:case 5:
1323 if( ((signed) (*f)->Parameter1)>=100) continue;
1324 break;
1325 default:
1326 break;
1328 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1332 //Removes all effects with a matching param2
1333 //param2 is usually an effect's subclass (quality) while param1 is more like quantity.
1334 //So opcode+param2 usually pinpoints an effect better when not all effects of a given
1335 //opcode need to be removed (see removal of portrait icon)
1336 void EffectQueue::RemoveAllEffectsWithParam(ieDword opcode, ieDword param2) const
1338 std::list< Effect* >::const_iterator f;
1339 for ( f = effects.begin(); f != effects.end(); f++ ) {
1340 MATCH_OPCODE();
1341 MATCH_LIVE_FX();
1342 MATCH_PARAM2();
1344 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1348 //this function is called by FakeEffectExpiryCheck
1349 //probably also called by rest
1350 void EffectQueue::RemoveExpiredEffects(ieDword futuretime) const
1352 ieDword GameTime = core->GetGame()->GameTime;
1353 if( GameTime+futuretime*AI_UPDATE_TIME<GameTime) {
1354 GameTime=0xffffffff;
1355 } else {
1356 GameTime+=futuretime*AI_UPDATE_TIME;
1359 std::list< Effect* >::const_iterator f;
1360 for ( f = effects.begin(); f != effects.end(); f++ ) {
1361 //FIXME: how this method handles delayed effects???
1362 //it should remove them as well, i think
1363 if( DelayType( ((*f)->TimingMode) )!=PERMANENT ) {
1364 if( (*f)->Duration<=GameTime) {
1365 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1371 //this effect will expire all effects that are not truly permanent
1372 //which i call permanent after death (iesdp calls it permanent after bonuses)
1373 void EffectQueue::RemoveAllNonPermanentEffects() const
1375 std::list< Effect* >::const_iterator f;
1376 for ( f = effects.begin(); f != effects.end(); f++ ) {
1377 if( IsRemovable((*f)->TimingMode) ) {
1378 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1383 //this will modify effect reference
1385 void EffectQueue::RemoveAllDetrimentalEffects(EffectRef &effect_reference, ieDword current) const
1387 ResolveEffectRef(effect_reference);
1388 RemoveAllDetrimentalEffects(effect_reference.opcode, current);
1391 void EffectQueue::RemoveAllEffectsWithParam(EffectRef &effect_reference, ieDword param2) const
1393 ResolveEffectRef(effect_reference);
1394 RemoveAllEffectsWithParam(effect_reference.opcode, param2);
1397 //remove certain levels of effects, possibly matching school/secondary type
1398 //this method removes whole spells (tied together by their source)
1399 //FIXME: probably this isn't perfect
1400 void EffectQueue::RemoveLevelEffects(ieResRef &Removed, ieDword level, ieDword Flags, ieDword match) const
1402 Removed[0]=0;
1403 std::list< Effect* >::const_iterator f;
1404 for ( f = effects.begin(); f != effects.end(); f++ ) {
1405 if( (*f)->Power>level) {
1406 continue;
1409 if( Removed[0]) {
1410 MATCH_SOURCE();
1412 if( Flags&RL_MATCHSCHOOL) {
1413 if( (*f)->PrimaryType!=match) {
1414 continue;
1417 if( Flags&RL_MATCHSECTYPE) {
1418 if( (*f)->SecondaryType!=match) {
1419 continue;
1422 //if dispellable was not set, or the effect is dispellable
1423 //then remove it
1424 if( Flags&RL_DISPELLABLE) {
1425 if( !((*f)->Resistance&FX_CAN_DISPEL)) {
1426 continue;
1429 (*f)->TimingMode = FX_DURATION_JUST_EXPIRED;
1430 if( Flags&RL_REMOVEFIRST) {
1431 memcpy(Removed,(*f)->Source, sizeof(Removed));
1436 Effect *EffectQueue::HasOpcode(ieDword opcode) const
1438 std::list< Effect* >::const_iterator f;
1439 for ( f = effects.begin(); f != effects.end(); f++ ) {
1440 MATCH_OPCODE();
1441 MATCH_LIVE_FX();
1443 return (*f);
1445 return NULL;
1448 Effect *EffectQueue::HasEffect(EffectRef &effect_reference) const
1450 ResolveEffectRef(effect_reference);
1451 if( effect_reference.opcode<0) {
1452 return NULL;
1454 return HasOpcode(effect_reference.opcode);
1457 Effect *EffectQueue::HasOpcodeWithParam(ieDword opcode, ieDword param2) const
1459 std::list< Effect* >::const_iterator f;
1460 for ( f = effects.begin(); f != effects.end(); f++ ) {
1461 MATCH_OPCODE();
1462 MATCH_LIVE_FX();
1463 MATCH_PARAM2();
1465 return (*f);
1467 return NULL;
1470 Effect *EffectQueue::HasEffectWithParam(EffectRef &effect_reference, ieDword param2) const
1472 ResolveEffectRef(effect_reference);
1473 if( effect_reference.opcode<0) {
1474 return NULL;
1476 return HasOpcodeWithParam(effect_reference.opcode, param2);
1479 //looks for opcode with pairs of parameters (useful for protection against creature, extra damage or extra thac0 against creature)
1480 //generally an IDS targeting
1482 Effect *EffectQueue::HasOpcodeWithParamPair(ieDword opcode, ieDword param1, ieDword param2) const
1484 std::list< Effect* >::const_iterator f;
1485 for ( f = effects.begin(); f != effects.end(); f++ ) {
1486 MATCH_OPCODE();
1487 MATCH_LIVE_FX();
1488 MATCH_PARAM2();
1489 //0 is always accepted as first parameter
1490 if( param1) {
1491 MATCH_PARAM1();
1494 return (*f);
1496 return NULL;
1499 Effect *EffectQueue::HasEffectWithParamPair(EffectRef &effect_reference, ieDword param1, ieDword param2) const
1501 ResolveEffectRef(effect_reference);
1502 if( effect_reference.opcode<0) {
1503 return NULL;
1505 return HasOpcodeWithParamPair(effect_reference.opcode, param1, param2);
1508 // sums all the values of the specific damage bonus effects of the passed "damage type"
1509 int EffectQueue::SpecificDamageBonus(ieDword opcode, ieDword param2) const
1511 int bonus = 0;
1512 std::list< Effect* >::const_iterator f;
1513 for ( f = effects.begin(); f != effects.end(); f++ ) {
1514 MATCH_OPCODE();
1515 MATCH_LIVE_FX();
1516 MATCH_PARAM2();
1517 bonus += (signed) (*f)->Parameter1;
1519 return bonus;
1522 static EffectRef fx_damage_bonus_modifier_ref={"DamageBonusModifier",NULL,-1};
1523 int EffectQueue::SpecificDamageBonus(ieDword damage_type) const
1525 ResolveEffectRef(fx_damage_bonus_modifier_ref);
1526 if(fx_damage_bonus_modifier_ref.opcode < 0) {
1527 return 0;
1529 return SpecificDamageBonus(fx_damage_bonus_modifier_ref.opcode, damage_type);
1532 //this could be used for stoneskins and mirror images as well
1533 void EffectQueue::DecreaseParam1OfEffect(ieDword opcode, ieDword amount) const
1535 std::list< Effect* >::const_iterator f;
1536 for ( f = effects.begin(); f != effects.end(); f++ ) {
1537 MATCH_OPCODE();
1538 MATCH_LIVE_FX();
1539 ieDword value = (*f)->Parameter1;
1540 if( value>amount) value-=amount;
1541 else value = 0;
1542 (*f)->Parameter1=value;
1546 void EffectQueue::DecreaseParam1OfEffect(EffectRef &effect_reference, ieDword amount) const
1548 ResolveEffectRef(effect_reference);
1549 if( effect_reference.opcode<0) {
1550 return;
1552 DecreaseParam1OfEffect(effect_reference.opcode, amount);
1556 //this function does IDS targeting for effects (extra damage/thac0 against creature)
1557 static const int ids_stats[7]={IE_EA, IE_GENERAL, IE_RACE, IE_CLASS, IE_SPECIFIC, IE_SEX, IE_ALIGNMENT};
1559 int EffectQueue::BonusAgainstCreature(ieDword opcode, Actor *actor) const
1561 int sum = 0;
1562 std::list< Effect* >::const_iterator f;
1563 for ( f = effects.begin(); f != effects.end(); f++ ) {
1564 MATCH_OPCODE();
1565 MATCH_LIVE_FX();
1566 if( (*f)->Parameter1) {
1567 ieDword ids = (*f)->Parameter2;
1568 if( ids<2 || ids>8) {
1569 ids=2;
1571 ieDword param1 = actor->GetStat(ids_stats[ids-2]);
1572 MATCH_PARAM1();
1574 int val = (int) (*f)->Parameter3;
1575 if( !val) val = 2;
1576 sum += val;
1578 return sum;
1581 int EffectQueue::BonusAgainstCreature(EffectRef &effect_reference, Actor *actor) const
1583 ResolveEffectRef(effect_reference);
1584 if( effect_reference.opcode<0) {
1585 return 0;
1587 return BonusAgainstCreature(effect_reference.opcode, actor);
1590 bool EffectQueue::WeaponImmunity(ieDword opcode, int enchantment, ieDword weapontype) const
1592 std::list< Effect* >::const_iterator f;
1593 for ( f = effects.begin(); f != effects.end(); f++ ) {
1594 MATCH_OPCODE();
1595 MATCH_LIVE_FX();
1597 int magic = (int) (*f)->Parameter1;
1598 ieDword mask = (*f)->Parameter3;
1599 ieDword value = (*f)->Parameter4;
1600 if( magic==0) {
1601 if( enchantment) continue;
1602 } else if( magic>0) {
1603 if( enchantment>magic) continue;
1606 if( (weapontype&mask) != value) {
1607 continue;
1609 return true;
1611 return false;
1614 static EffectRef fx_weapon_immunity_ref={"Protection:Weapons",NULL,-1};
1616 bool EffectQueue::WeaponImmunity(int enchantment, ieDword weapontype) const
1618 ResolveEffectRef(fx_weapon_immunity_ref);
1619 if( fx_weapon_immunity_ref.opcode<0) {
1620 return 0;
1622 return WeaponImmunity(fx_weapon_immunity_ref.opcode, enchantment, weapontype);
1625 static EffectRef fx_disable_spellcasting_ref={ "DisableCasting", NULL, -1 };
1626 int EffectQueue::DisabledSpellcasting(int types) const
1628 ResolveEffectRef(fx_disable_spellcasting_ref);
1629 if( fx_disable_spellcasting_ref.opcode < 0) {
1630 return 0;
1633 unsigned int spelltype_mask = 0;
1634 bool iwd2 = !!core->HasFeature(GF_ENHANCED_EFFECTS);
1635 ieDword opcode = fx_disable_spellcasting_ref.opcode;
1636 std::list< Effect* >::const_iterator f;
1637 for ( f = effects.begin(); f != effects.end(); f++ ) {
1638 MATCH_OPCODE();
1639 MATCH_LIVE_FX();
1641 if (iwd2) {
1642 switch((*f)->Parameter2) {
1643 case 0: // all
1644 spelltype_mask |= 7;
1645 break;
1646 case 1: // mage and cleric
1647 spelltype_mask |= 3;
1648 break;
1649 case 2: // mage
1650 spelltype_mask |= 2;
1651 break;
1652 case 3: // cleric
1653 spelltype_mask |= 1;
1654 break;
1655 case 4: // innate
1656 spelltype_mask |= 4;
1657 break;
1659 } else {
1660 switch((*f)->Parameter2) {
1661 case 0: // mage
1662 spelltype_mask |= 2;
1663 break;
1664 case 1: // cleric
1665 spelltype_mask |= 1;
1666 break;
1667 case 2: // innate
1668 spelltype_mask |= 4;
1669 break;
1673 return spelltype_mask & types;
1676 //useful for immunity vs spell, can't use item, etc.
1677 Effect *EffectQueue::HasOpcodeWithResource(ieDword opcode, const ieResRef resource) const
1679 std::list< Effect* >::const_iterator f;
1680 for ( f = effects.begin(); f != effects.end(); f++ ) {
1681 MATCH_OPCODE();
1682 MATCH_LIVE_FX();
1683 MATCH_RESOURCE();
1685 return (*f);
1687 return NULL;
1690 Effect *EffectQueue::HasEffectWithResource(EffectRef &effect_reference, const ieResRef resource) const
1692 ResolveEffectRef(effect_reference);
1693 return HasOpcodeWithResource(effect_reference.opcode, resource);
1696 //used in contingency/sequencer code (cannot have the same contingency twice)
1697 Effect *EffectQueue::HasOpcodeWithSource(ieDword opcode, const ieResRef Removed) const
1699 std::list< Effect* >::const_iterator f;
1700 for ( f = effects.begin(); f != effects.end(); f++ ) {
1701 MATCH_OPCODE();
1702 MATCH_LIVE_FX();
1703 MATCH_SOURCE();
1705 return (*f);
1707 return NULL;
1710 Effect *EffectQueue::HasEffectWithSource(EffectRef &effect_reference, const ieResRef resource) const
1712 ResolveEffectRef(effect_reference);
1713 return HasOpcodeWithSource(effect_reference.opcode, resource);
1716 bool EffectQueue::HasAnyDispellableEffect() const
1718 std::list< Effect* >::const_iterator f;
1719 for ( f = effects.begin(); f != effects.end(); f++ ) {
1720 if( (*f)->Resistance&FX_CAN_DISPEL) {
1721 return true;
1724 return false;
1727 void EffectQueue::dump() const
1729 printf( "EFFECT QUEUE:\n" );
1730 int i = 0;
1731 std::list< Effect* >::const_iterator f;
1732 for ( f = effects.begin(); f != effects.end(); f++ ) {
1733 Effect* fx = *f;
1734 if( fx) {
1735 char *Name = NULL;
1736 if( fx->Opcode < MAX_EFFECTS)
1737 Name = (char*) Opcodes[fx->Opcode].Name;
1739 printf( " %2d: 0x%02x: %s (%d, %d) S:%s\n", i++, fx->Opcode, Name, fx->Parameter1, fx->Parameter2, fx->Source );
1744 Effect *EffectQueue::GetEffect(ieDword idx) const
1746 if( effects.size()<=idx) {
1747 return NULL;
1749 return effects[idx];
1753 //returns true if the effect supports simplified duration
1754 bool EffectQueue::HasDuration(Effect *fx)
1756 switch(fx->TimingMode) {
1757 case FX_DURATION_INSTANT_LIMITED: //simple duration
1758 case FX_DURATION_DELAY_LIMITED: //delayed duration
1759 case FX_DURATION_DELAY_PERMANENT: //simple delayed
1760 return true;
1762 return false;
1765 static EffectRef fx_variable_ref={"Variable:StoreLocalVariable",NULL,-1};
1767 //returns true if the effect must be saved
1768 //variables are saved differently
1769 bool EffectQueue::Persistent(Effect* fx)
1771 //we save this as variable
1772 if( fx->Opcode==(ieDword) ResolveEffect(fx_variable_ref)) {
1773 return false;
1776 switch (fx->TimingMode) {
1777 //normal equipping fx of items
1778 case FX_DURATION_INSTANT_WHILE_EQUIPPED:
1779 //delayed effect not saved
1780 case FX_DURATION_DELAY_UNSAVED:
1781 //permanent effect not saved
1782 case FX_DURATION_PERMANENT_UNSAVED:
1783 //just expired effect
1784 case FX_DURATION_JUST_EXPIRED:
1785 return false;
1787 return true;
1790 //alter the color effect in case the item is equipped in the shield slot
1791 void EffectQueue::HackColorEffects(Actor *Owner, Effect *fx)
1793 if( fx->InventorySlot!=Owner->inventory.GetShieldSlot()) return;
1795 unsigned int gradienttype = fx->Parameter2 & 0xF0;
1796 if( gradienttype == 0x10) {
1797 gradienttype = 0x20; // off-hand
1798 fx->Parameter2 &= ~0xF0;
1799 fx->Parameter2 |= gradienttype;
1803 //iterate through saved effects
1804 const Effect *EffectQueue::GetNextSavedEffect(std::list< Effect* >::const_iterator &f) const
1806 while(f!=effects.end()) {
1807 Effect *effect = *f;
1808 f++;
1809 if( Persistent(effect)) {
1810 return effect;
1813 return NULL;
1816 Effect *EffectQueue::GetNextEffect(std::list< Effect* >::const_iterator &f) const
1818 if( f!=effects.end()) return *f++;
1819 return NULL;
1822 ieDword EffectQueue::CountEffects(ieDword opcode, ieDword param1, ieDword param2, const char *resource) const
1824 ieDword cnt = 0;
1826 std::list< Effect* >::const_iterator f;
1828 for ( f = effects.begin(); f != effects.end(); f++ ) {
1829 MATCH_OPCODE();
1830 if( param1!=0xffffffff)
1831 MATCH_PARAM1();
1832 if( param2!=0xffffffff)
1833 MATCH_PARAM2();
1834 if( resource) {
1835 MATCH_RESOURCE();
1837 cnt++;
1839 return cnt;
1842 void EffectQueue::ModifyEffectPoint(ieDword opcode, ieDword x, ieDword y) const
1844 std::list< Effect* >::const_iterator f;
1846 for ( f = effects.begin(); f != effects.end(); f++ ) {
1847 MATCH_OPCODE();
1848 (*f)->PosX=x;
1849 (*f)->PosY=y;
1850 (*f)->Parameter3=0;
1851 return;
1855 //count effects that get saved
1856 ieDword EffectQueue::GetSavedEffectsCount() const
1858 ieDword cnt = 0;
1860 std::list< Effect* >::const_iterator f;
1862 for ( f = effects.begin(); f != effects.end(); f++ ) {
1863 Effect* fx = *f;
1864 if( Persistent(fx))
1865 cnt++;
1867 return cnt;
1870 void EffectQueue::TransformToDelay(ieByte &TimingMode)
1872 if( TimingMode<MAX_TIMING_MODE) {;
1873 TimingMode = fx_to_delayed[TimingMode];
1874 } else {
1875 TimingMode = FX_DURATION_JUST_EXPIRED;
1879 int EffectQueue::ResolveEffect(EffectRef &effect_reference)
1881 ResolveEffectRef(effect_reference);
1882 return effect_reference.opcode;
1885 // this check goes for the whole effect block, not individual effects
1886 // But it takes the first effect of the block for the common fields
1888 //returns 1 if effect block applicable
1889 //returns 0 if effect block disabled
1890 //returns -1 if effect block bounced
1891 int EffectQueue::CheckImmunity(Actor *target) const
1893 //don't resist if target is non living
1894 if( !target) {
1895 return 1;
1898 if( effects.size() ) {
1899 Effect* fx = *effects.begin();
1901 //projectile immunity
1902 if( target->ImmuneToProjectile(fx->Projectile)) return 0;
1904 //don't resist item projectile payloads based on spell school, bounce, etc.
1905 if( fx->InventorySlot) {
1906 return 1;
1909 //check level resistances
1910 //check specific spell immunity
1911 //check school/sectype immunity
1912 return check_type(target, fx);
1914 return 0;
1917 void EffectQueue::AffectAllInRange(Map *map, const Point &pos, int idstype, int idsvalue,
1918 unsigned int range, Actor *except)
1920 int cnt = map->GetActorCount(true);
1921 while(cnt--) {
1922 Actor *actor = map->GetActor(cnt,true);
1923 if( except==actor) {
1924 continue;
1926 //distance
1927 if( Distance(pos, actor)>range) {
1928 continue;
1930 //ids targeting
1931 if( !match_ids(actor, idstype, idsvalue)) {
1932 continue;
1934 //line of sight
1935 if( !map->IsVisible(actor->Pos, pos)) {
1936 continue;
1938 AddAllEffects(actor, actor->Pos);