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"
26 #include "Interface.h"
28 #include "SymbolMgr.h"
29 #include "Scriptable/Actor.h"
30 #include "Spell.h" //needs for the source flags bitfield
36 EffectFunction Function
;
38 } Opcodes
[MAX_EFFECTS
];
40 static int initialized
= 0;
41 static EffectRef
*effectnames
= NULL
;
42 static int effectnames_count
= 0;
43 static int pstflags
= false;
45 bool EffectQueue::match_ids(Actor
*target
, int table
, ieDword value
)
57 stat
= IE_GENERAL
; break;
59 stat
= IE_RACE
; break;
61 stat
= IE_CLASS
; break;
63 stat
= IE_SPECIFIC
; break;
67 stat
= target
->GetStat(IE_ALIGNMENT
);
70 if( a
!= ( stat
& 15 )) {
76 if( a
!= ( stat
& 0xf0 )) {
84 if( target
->GetStat(stat
)==value
) {
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
];
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
) {
173 void *tmp
= bsearch(effectname
, effectnames
, effectnames_count
, sizeof(EffectRef
), find_effect
);
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
[] = {
187 {"CurrentHPModifier",NULL
,-1},
188 {"MaximumHPModifier",NULL
,-1},
190 {"BurningBlood",NULL
,-1}, //iwd
191 {"ColdDamage",NULL
,-1},
192 {"CrushingDamage",NULL
,-1},
193 {"VampiricTouch",NULL
,-1},
194 {"VitriolicSphere",NULL
,-1},
196 {"TransferHP",NULL
,-1},
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
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
;
215 effect_reference
.opcode
= -2;
219 bool Init_EffectQueue()
226 pstflags
= !!core
->HasFeature(GF_PST_STATE_FLAGS
);
228 memset( Opcodes
, 0, sizeof( Opcodes
) );
229 for(i
=0;i
<MAX_EFFECTS
;i
++) {
230 Opcodes
[i
].Strref
=-1;
235 AutoTable
efftextTable("efftext");
237 int eT
= core
->LoadSymbol( "effects" );
239 printMessage( "EffectQueue","A critical scripting file is missing!\n",LIGHT_RED
);
242 Holder
<SymbolMgr
> effectsTable
= core
->GetSymbol( eT
);
244 printMessage( "EffectQueue","A critical scripting file is damaged!\n",LIGHT_RED
);
248 for (i
= 0; i
< MAX_EFFECTS
; i
++) {
249 const char* effectname
= effectsTable
->GetValue( i
);
251 int row
= efftextTable
->GetRowCount();
253 const char* ret
= efftextTable
->GetRowName( row
);
255 if( valid_number( ret
, val
) && (i
== val
) ) {
256 Opcodes
[i
].Strref
= atoi( efftextTable
->QueryField( row
, 1 ) );
261 EffectRef
* poi
= FindEffect( effectname
);
263 Opcodes
[i
].Function
= poi
->Function
;
264 Opcodes
[i
].Name
= poi
->Name
;
265 //reverse linking opcode number
266 //using this unused field
267 if( (poi
->opcode
!=-1) && effectname
[0]!='*') {
268 printf("Clashing Opcodes FN: %d vs. %d, %s\n", i
, poi
->opcode
, effectname
);
273 //printf("-------- FN: %d, %s\n", i, effectname);
275 core
->DelSymbol( eT
);
277 //additional initialisations
278 for (i
=0;diced_effects
[i
].Name
;i
++) {
279 ResolveEffectRef(diced_effects
[i
]);
281 for (i
=0;diced_effects2
[i
].Name
;i
++) {
282 ResolveEffectRef(diced_effects2
[i
]);
288 void EffectQueue_ReleaseMemory()
293 effectnames_count
= 0;
297 void EffectQueue_RegisterOpcodes(int count
, const EffectRef
* opcodes
)
300 effectnames
= (EffectRef
*) malloc( (count
+1) * sizeof( EffectRef
) );
302 effectnames
= (EffectRef
*) realloc( effectnames
, (effectnames_count
+ count
+ 1) * sizeof( EffectRef
) );
305 memcpy( effectnames
+ effectnames_count
, opcodes
, count
* sizeof( EffectRef
));
306 effectnames_count
+= count
;
307 effectnames
[effectnames_count
].Name
= NULL
;
308 //if we merge two effect lists, then we need to sort their effect tables
309 //actually, we might always want to sort this list, so there is no
310 //need to do it manually (sorted table is needed if we use bsearch)
311 qsort(effectnames
, effectnames_count
, sizeof(EffectRef
), compare_effects
);
314 EffectQueue::EffectQueue()
319 EffectQueue::~EffectQueue()
321 std::list
< Effect
* >::iterator f
;
323 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
328 Effect
*EffectQueue::CreateEffect(ieDword opcode
, ieDword param1
, ieDword param2
, ieWord timing
)
330 if( opcode
==0xffffffff) {
333 Effect
*fx
= new Effect();
337 memset(fx
,0,sizeof(Effect
));
338 fx
->Target
= FX_TARGET_SELF
;
340 //probability2 is the low number (by effectqueue 331)
341 fx
->Probability1
= 100;
342 fx
->Parameter1
= param1
;
343 fx
->Parameter2
= param2
;
344 fx
->TimingMode
= timing
;
345 fx
->PosX
= 0xffffffff;
346 fx
->PosY
= 0xffffffff;
350 //return the count of effects with matching parameters
351 //useful for effects where there is no separate stat to see
352 ieDword
EffectQueue::CountEffects(EffectRef
&effect_reference
, ieDword param1
, ieDword param2
, const char *resource
) const
354 ResolveEffectRef(effect_reference
);
355 if( effect_reference
.opcode
<0) {
358 return CountEffects(effect_reference
.opcode
, param1
, param2
, resource
);
361 //Change the location of an existing effect
362 //this is used when some external code needs to adjust the effect's location
363 //used when the gui sets the effect's final target
364 void EffectQueue::ModifyEffectPoint(EffectRef
&effect_reference
, ieDword x
, ieDword y
) const
366 ResolveEffectRef(effect_reference
);
367 if( effect_reference
.opcode
<0) {
370 ModifyEffectPoint(effect_reference
.opcode
, x
, y
);
373 Effect
*EffectQueue::CreateEffect(EffectRef
&effect_reference
, ieDword param1
, ieDword param2
, ieWord timing
)
375 ResolveEffectRef(effect_reference
);
376 if( effect_reference
.opcode
<0) {
379 return CreateEffect(effect_reference
.opcode
, param1
, param2
, timing
);
382 //copies the whole effectqueue (area projectiles use it)
383 EffectQueue
*EffectQueue::CopySelf() const
385 EffectQueue
*effects
;
387 effects
= new EffectQueue();
388 std::list
< Effect
* >::const_iterator fxit
= GetFirstEffect();
391 while( (fx
= GetNextEffect(fxit
))) {
392 effects
->AddEffect(fx
, false);
394 effects
->SetOwner(GetOwner());
398 //create a new effect with most of the characteristics of the old effect
399 //only opcode and parameters are changed
400 //This is used mostly inside effects, when an effect needs to spawn
401 //other effects with the same coordinates, source, duration, etc.
402 Effect
*EffectQueue::CreateEffectCopy(Effect
*oldfx
, ieDword opcode
, ieDword param1
, ieDword param2
)
404 if( opcode
==0xffffffff) {
407 Effect
*fx
= new Effect();
411 memcpy(fx
,oldfx
,sizeof(Effect
) );
413 fx
->Parameter1
=param1
;
414 fx
->Parameter2
=param2
;
418 Effect
*EffectQueue::CreateEffectCopy(Effect
*oldfx
, EffectRef
&effect_reference
, ieDword param1
, ieDword param2
)
420 ResolveEffectRef(effect_reference
);
421 if( effect_reference
.opcode
<0) {
424 return CreateEffectCopy(oldfx
, effect_reference
.opcode
, param1
, param2
);
427 static EffectRef fx_unsummon_creature_ref
={"UnsummonCreature",NULL
,-1};
429 Effect
*EffectQueue::CreateUnsummonEffect(Effect
*fx
)
431 Effect
*newfx
= NULL
;
432 if( (fx
->TimingMode
&0xff) == FX_DURATION_INSTANT_LIMITED
) {
433 newfx
= CreateEffectCopy(fx
, fx_unsummon_creature_ref
, 0, 0);
434 newfx
->TimingMode
= FX_DURATION_DELAY_PERMANENT
;
435 if( newfx
->Resource3
[0]) {
436 strnuprcpy(newfx
->Resource
,newfx
->Resource3
, sizeof(ieResRef
)-1 );
438 strnuprcpy(newfx
->Resource
,"SPGFLSH1", sizeof(ieResRef
)-1 );
440 if( fx
->TimingMode
== FX_DURATION_ABSOLUTE
) {
442 newfx
->Duration
= (newfx
->Duration
-core
->GetGame()->GameTime
)/AI_UPDATE_TIME
;
449 void EffectQueue::AddEffect(Effect
* fx
, bool insert
)
451 Effect
* new_fx
= new Effect
;
452 memcpy( new_fx
, fx
, sizeof( Effect
) );
454 effects
.insert( effects
.begin(), new_fx
);
456 effects
.push_back( new_fx
);
460 //This method can remove an effect described by a pointer to it, or
461 //an exact matching effect
462 bool EffectQueue::RemoveEffect(Effect
* fx
)
464 int invariant_size
= offsetof( Effect
, random_value
);
466 for (std::list
< Effect
* >::iterator f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
469 if( (fx
==fx2
) || !memcmp( fx
, fx2
, invariant_size
)) {
478 //this is where we reapply all effects when loading a saved game
479 //The effects are already in the fxqueue of the target
480 void EffectQueue::ApplyAllEffects(Actor
* target
) const
482 std::list
< Effect
* >::const_iterator f
;
483 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
484 ApplyEffect( target
, *f
, 0 );
488 void EffectQueue::Cleanup()
490 std::list
< Effect
* >::iterator f
;
492 for ( f
= effects
.begin(); f
!= effects
.end(); ) {
493 if( (*f
)->TimingMode
== FX_DURATION_JUST_EXPIRED
) {
502 //Handle the target flag when the effect is applied first
503 int EffectQueue::AddEffect(Effect
* fx
, Scriptable
* self
, Actor
* pretarget
, const Point
&dest
) const
510 Actor
*st
= (self
&& (self
->Type
==ST_ACTOR
)) ?(Actor
*) self
:NULL
;
512 switch (fx
->Target
) {
513 case FX_TARGET_ORIGINAL
:
514 fx
->SetPosition(self
->Pos
);
516 flg
= ApplyEffect( st
, fx
, 1 );
517 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
519 st
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
524 fx
->SetPosition(dest
);
526 flg
= ApplyEffect( st
, fx
, 1 );
527 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
529 st
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
534 case FX_TARGET_ALL_BUT_SELF
:
535 map
=self
->GetCurrentArea();
536 i
= map
->GetActorCount(true);
538 Actor
* actor
= map
->GetActor( i
, true );
539 //don't pick ourselves
543 fx
->SetPosition(actor
->Pos
);
545 flg
= ApplyEffect( actor
, fx
, 1 );
546 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
547 actor
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
553 case FX_TARGET_OWN_SIDE
:
554 if( !st
|| st
->InParty
) {
557 map
= self
->GetCurrentArea();
558 spec
= st
->GetStat(IE_SPECIFIC
);
560 //GetActorCount(false) returns all nonparty critters
561 i
= map
->GetActorCount(false);
563 Actor
* actor
= map
->GetActor( i
, false );
564 if( actor
->GetStat(IE_SPECIFIC
)!=spec
) {
567 fx
->SetPosition(actor
->Pos
);
569 flg
= ApplyEffect( actor
, fx
, 1 );
570 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
571 actor
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
576 case FX_TARGET_OTHER_SIDE
:
577 if( !pretarget
|| pretarget
->InParty
) {
580 map
= self
->GetCurrentArea();
581 spec
= pretarget
->GetStat(IE_SPECIFIC
);
583 //GetActorCount(false) returns all nonparty critters
584 i
= map
->GetActorCount(false);
586 Actor
* actor
= map
->GetActor( i
, false );
587 if( actor
->GetStat(IE_SPECIFIC
)!=spec
) {
590 fx
->SetPosition(actor
->Pos
);
592 flg
= ApplyEffect( actor
, fx
, 1 );
593 //GetActorCount can now return all nonparty critters
594 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
595 actor
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
600 case FX_TARGET_PRESET
:
601 fx
->SetPosition(pretarget
->Pos
);
603 flg
= ApplyEffect( pretarget
, fx
, 1 );
604 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
606 pretarget
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
611 case FX_TARGET_PARTY
:
613 game
= core
->GetGame();
614 i
= game
->GetPartySize(true);
616 Actor
* actor
= game
->GetPC( i
, true );
617 fx
->SetPosition(actor
->Pos
);
619 flg
= ApplyEffect( actor
, fx
, 1 );
620 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
621 actor
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
628 map
= self
->GetCurrentArea();
629 i
= map
->GetActorCount(true);
631 Actor
* actor
= map
->GetActor( i
, true );
632 fx
->SetPosition(actor
->Pos
);
634 flg
= ApplyEffect( actor
, fx
, 1 );
635 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
636 actor
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
642 case FX_TARGET_ALL_BUT_PARTY
:
643 map
= self
->GetCurrentArea();
644 i
= map
->GetActorCount(false);
646 Actor
* actor
= map
->GetActor( i
, false );
647 fx
->SetPosition(actor
->Pos
);
649 flg
= ApplyEffect( actor
, fx
, 1 );
650 //GetActorCount can now return all nonparty critters
651 if( fx
->TimingMode
!= FX_DURATION_JUST_EXPIRED
) {
652 actor
->fxqueue
.AddEffect( fx
, flg
==FX_INSERT
);
658 case FX_TARGET_UNKNOWN
:
660 printf( "Unknown FX target type: %d\n", fx
->Target
);
668 //this is where effects from spells first get in touch with the target
669 //the effects are currently NOT in the target's fxqueue, those that stick
670 //will get copied (hence the fxqueue.AddEffect call)
671 //if this returns FX_NOT_APPLIED, then the whole stack was resisted
673 int EffectQueue::AddAllEffects(Actor
* target
, const Point
&destination
) const
675 int res
= FX_NOT_APPLIED
;
676 // pre-roll dice for fx needing them and stow them in the effect
677 ieDword random_value
= core
->Roll( 1, 100, -1 );
682 std::list
< Effect
* >::const_iterator f
;
683 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
684 //handle resistances and saving throws here
685 (*f
)->random_value
= random_value
;
686 //if applyeffect returns true, we stop adding the future effects
687 //this is to simulate iwd2's on the fly spell resistance
689 int tmp
= AddEffect(*f
, Owner
, target
, destination
);
690 //lets try without Owner, any crash?
691 //If yes, then try to fix the individual effect
692 //If you use target for Owner here, the wand in chateau irenicus will work
693 //the same way as Imoen's monster summoning, which is a BAD THING (TM)
694 //int tmp = AddEffect(*f, Owner?Owner:target, target, destination);
695 if( tmp
== FX_ABORT
) {
696 res
= FX_NOT_APPLIED
;
699 if( tmp
!= FX_NOT_APPLIED
) {
706 //check if an effect has no level based resistance, but instead the dice sizes/count
707 //adjusts Parameter1 (like a damage causing effect)
708 inline static bool IsDicedEffect(int opcode
)
712 for(i
=0;diced_effects
[i
].Name
;i
++) {
713 if( diced_effects
[i
].opcode
==opcode
) {
720 //there is no level based resistance, but Parameter1 cannot be precalculated
721 //these effects use the Dice fields in a special way
722 inline static bool IsDicedEffect2(int opcode
)
726 for(i
=0;diced_effects2
[i
].Name
;i
++) {
727 if( diced_effects2
[i
].opcode
==opcode
) {
734 //resisted effect based on level
735 inline bool check_level(Actor
*target
, Effect
*fx
)
737 //skip non level based effects
738 if( IsDicedEffect((int) fx
->Opcode
)) {
739 fx
->Parameter1
= DICE_ROLL((signed)fx
->Parameter1
);
740 //this is a hack for PST style diced effects
741 if( core
->HasFeature(GF_SAVE_FOR_HALF
) ) {
742 if( memcmp(fx
->Resource
,"NEG",4) ) {
743 fx
->IsSaveForHalfDamage
=1;
746 if( (fx
->Parameter2
&3)==3) {
747 fx
->IsSaveForHalfDamage
=1;
752 if( IsDicedEffect2((int) fx
->Opcode
)) {
759 if(fx
->Target
== FX_TARGET_SELF
) {
763 ieDword level
= (ieDword
) target
->GetXPLevel( true );
764 //return true if resisted
765 if (fx
->MinAffectedLevel
> 0 || fx
->MaxAffectedLevel
> 0) {
766 if (level
< fx
->MinAffectedLevel
|| level
> fx
->MaxAffectedLevel
) {
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 0-99
780 if( fx
->random_value
<=fx
->Probability2
|| fx
->random_value
>fx
->Probability1
) {
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_store_spell_sequencer_ref
={"Sequencer:Store",NULL
,-1}; //bg2, works against sequencers
793 static EffectRef fx_school_immunity_ref
={"Protection:School",NULL
,-1};
794 static EffectRef fx_secondary_type_immunity_ref
={"Protection:SecondaryType",NULL
,-1};
796 //decrementing immunity effects
797 static EffectRef fx_level_immunity_dec_ref
={"Protection:SpellLevelDec",NULL
,-1};
798 static EffectRef fx_spell_immunity_dec_ref
={"Protection:SpellDec",NULL
,-1};
799 static EffectRef fx_school_immunity_dec_ref
={"Protection:SchoolDec",NULL
,-1};
800 static EffectRef fx_secondary_type_immunity_dec_ref
={"Protection:SecondaryTypeDec",NULL
,-1};
803 static EffectRef fx_level_bounce_ref
={"Bounce:SpellLevel",NULL
,-1};
804 //static EffectRef fx_opcode_bounce_ref={"Bounce:Opcode",NULL,-1};
805 static EffectRef fx_spell_bounce_ref
={"Bounce:Spell",NULL
,-1};
806 static EffectRef fx_school_bounce_ref
={"Bounce:School",NULL
,-1};
807 static EffectRef fx_secondary_type_bounce_ref
={"Bounce:SecondaryType",NULL
,-1};
809 //decrementing bounce effects
810 static EffectRef fx_level_bounce_dec_ref
={"Bounce:SpellLevelDec",NULL
,-1};
811 static EffectRef fx_spell_bounce_dec_ref
={"Bounce:SpellDec",NULL
,-1};
812 static EffectRef fx_school_bounce_dec_ref
={"Bounce:SchoolDec",NULL
,-1};
813 static EffectRef fx_secondary_type_bounce_dec_ref
={"Bounce:SecondaryTypeDec",NULL
,-1};
815 //spelltrap (multiple decrementing immunity)
816 static EffectRef fx_spelltrap
={"SpellTrap", NULL
,-1};
818 //this is for whole spell immunity/bounce
819 inline static void DecreaseEffect(Effect
*efx
)
822 if( (int) efx
->Parameter1
<1) {
823 //don't remove effects directly!!!
824 efx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
828 //lower decreasing immunities/bounces
829 static int check_type(Actor
* actor
, Effect
* fx
)
831 //the protective effect (if any)
834 ieDword bounce
= actor
->GetStat(IE_BOUNCE
);
837 /*opcode immunity is in the per opcode checks
838 if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity_ref, fx->Opcode) ) {
841 if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity2_ref, fx->Opcode) ) {
845 //spell level immunity
846 if(fx
->Power
&& actor
->fxqueue
.HasEffectWithParamPair(fx_level_immunity_ref
, fx
->Power
, 0) ) {
850 //source immunity (spell name)
851 //if source is unspecified, don't resist it
853 if( actor
->fxqueue
.HasEffectWithResource(fx_spell_immunity_ref
, fx
->Source
) ) {
856 if( actor
->fxqueue
.HasEffectWithResource(fx_spell_immunity2_ref
, fx
->Source
) ) {
859 if (actor
->fxqueue
.HasEffectWithResource(fx_store_spell_sequencer_ref
, fx
->Source
) ) {
860 //TODO: display strref 0x806C
865 //primary type immunity (school)
866 if( fx
->PrimaryType
) {
867 if( actor
->fxqueue
.HasEffectWithParam(fx_school_immunity_ref
, fx
->PrimaryType
)) {
872 //secondary type immunity (usage)
873 if( fx
->SecondaryType
) {
874 if( actor
->fxqueue
.HasEffectWithParam(fx_secondary_type_immunity_ref
, fx
->SecondaryType
) ) {
879 //decrementing immunity checks
880 //decrementing level immunity
881 efx
= actor
->fxqueue
.HasEffectWithParamPair(fx_level_immunity_dec_ref
, fx
->Power
, 0);
887 //decrementing spell immunity
889 efx
= actor
->fxqueue
.HasEffectWithResource(fx_spell_immunity_dec_ref
, fx
->Source
);
895 //decrementing primary type immunity (school)
896 if( fx
->PrimaryType
) {
897 efx
= actor
->fxqueue
.HasEffectWithParam(fx_school_immunity_dec_ref
, fx
->PrimaryType
);
904 //decrementing secondary type immunity (usage)
905 if( fx
->SecondaryType
) {
906 efx
= actor
->fxqueue
.HasEffectWithParam(fx_secondary_type_immunity_dec_ref
, fx
->SecondaryType
);
915 //if the spelltrap effect already absorbed enough levels
916 //but still didn't get removed, it will absorb levels it shouldn't
917 //it will also absorb multiple spells in a single round
918 efx
=actor
->fxqueue
.HasEffectWithParamPair(fx_spelltrap
, 0, fx
->Power
);
920 //storing the absorbed spell level
921 efx
->Parameter3
+=fx
->Power
;
922 //instead of a single effect, they had to create an effect for each level
924 //if decrease needs the spell level, use fx->Power here
925 actor
->fxqueue
.DecreaseParam1OfEffect(fx_spelltrap
, 1);
931 if( (bounce
&BNC_LEVEL
) && actor
->fxqueue
.HasEffectWithParamPair(fx_level_bounce_ref
, fx
->Power
, 0) ) {
935 if( fx
->Source
[0] && (bounce
&BNC_RESOURCE
) && actor
->fxqueue
.HasEffectWithResource(fx_spell_bounce_ref
, fx
->Source
) ) {
939 if( fx
->PrimaryType
&& (bounce
&BNC_SCHOOL
) ) {
940 if( actor
->fxqueue
.HasEffectWithParam(fx_school_bounce_ref
, fx
->PrimaryType
)) {
945 if( fx
->SecondaryType
&& (bounce
&BNC_SECTYPE
) ) {
946 if( actor
->fxqueue
.HasEffectWithParam(fx_secondary_type_bounce_ref
, fx
->SecondaryType
)) {
950 //decrementing bounce checks
952 //level decrementing bounce check
953 if( (bounce
&BNC_LEVEL_DEC
)) {
954 efx
=actor
->fxqueue
.HasEffectWithParamPair(fx_level_bounce_dec_ref
, fx
->Power
, 0);
961 if( fx
->Source
[0] && (bounce
&BNC_RESOURCE_DEC
)) {
962 efx
=actor
->fxqueue
.HasEffectWithResource(fx_spell_bounce_dec_ref
, fx
->Resource
);
969 if( fx
->PrimaryType
&& (bounce
&BNC_SCHOOL_DEC
) ) {
970 efx
=actor
->fxqueue
.HasEffectWithParam(fx_school_bounce_dec_ref
, fx
->PrimaryType
);
977 if( fx
->SecondaryType
&& (bounce
&BNC_SECTYPE_DEC
) ) {
978 efx
=actor
->fxqueue
.HasEffectWithParam(fx_secondary_type_bounce_dec_ref
, fx
->SecondaryType
);
988 //check resistances, saving throws
989 static bool check_resistance(Actor
* actor
, Effect
* fx
)
996 if( actor
->fxqueue
.HasEffectWithParam(fx_opcode_immunity_ref
, fx
->Opcode
) ) {
997 printf ("immune to effect: %s\n", (char*) Opcodes
[fx
->Opcode
].Name
);
1000 if( actor
->fxqueue
.HasEffectWithParam(fx_opcode_immunity2_ref
, fx
->Opcode
) ) {
1001 printf ("immune2 to effect: %s\n", (char*) Opcodes
[fx
->Opcode
].Name
);
1005 /* opcode bouncing isn't implemented?
1007 if( actor->fxqueue.HasEffectWithParam(fx_opcode_bounce_ref, fx->Opcode) ) {
1012 //not resistable (no saves either?)
1013 if( fx
->Resistance
!= FX_CAN_RESIST_CAN_DISPEL
) {
1017 if (pstflags
&& (actor
->GetSafeStat(IE_STATE_ID
) & (STATE_ANTIMAGIC
) ) ) {
1022 bool selective_mr
= core
->HasFeature(GF_SELECTIVE_MAGIC_RES
);
1023 if (fx
->Target
==FX_TARGET_SELF
) {
1030 ieDword val
= actor
->GetStat(IE_RESISTMAGIC
);
1031 if( fx
->random_value
< val
) {
1032 // when using biased magic resistance non-hostile spells aren't resisted
1033 if ((selective_mr
&& (fx
->SourceFlags
&SF_HOSTILE
)) || !selective_mr
) {
1034 printf ("effect resisted: %s\n", (char*) Opcodes
[fx
->Opcode
].Name
);
1041 for (int i
=0;i
<5;i
++) {
1042 if( fx
->SavingThrowType
&(1<<i
)) {
1043 saved
= actor
->GetSavingThrow(i
, fx
->SavingThrowBonus
);
1050 if( fx
->IsSaveForHalfDamage
) {
1053 printf ("%s saved against effect: %s\n", actor
->GetName(1), (char*) Opcodes
[fx
->Opcode
].Name
);
1060 // this function is called two different ways
1061 // when FirstApply is set, then the effect isn't stuck on the target
1062 // this happens when a new effect comes in contact with the target.
1063 // if the effect returns FX_DURATION_JUST_EXPIRED then it won't stick
1064 // when first_apply is unset, the effect is already on the target
1065 // this happens on load time too!
1066 // returns FX_NOT_APPLIED if the process shouldn't be calling applyeffect anymore
1067 // returns FX_ABORT if the whole spell this effect is in should be aborted
1068 // it will disable all future effects of same source (only on first apply)
1070 int EffectQueue::ApplyEffect(Actor
* target
, Effect
* fx
, ieDword first_apply
, ieDword resistance
) const
1072 //printf( "FX 0x%02x: %s(%d, %d)\n", fx->Opcode, effectnames[fx->Opcode].Name, fx->Parameter1, fx->Parameter2 );
1073 if( fx
->Opcode
>= MAX_EFFECTS
) {
1074 fx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1075 return FX_NOT_APPLIED
;
1078 ieDword GameTime
= core
->GetGame()->GameTime
;
1080 fx
->FirstApply
=first_apply
;
1082 if( (fx
->PosX
==0xffffffff) && (fx
->PosY
==0xffffffff)) {
1083 fx
->PosX
= target
->Pos
.x
;
1084 fx
->PosY
= target
->Pos
.y
;
1087 //gemrb specific, stat based chance
1088 if ((fx
->Probability2
== 100) && Owner
&& (Owner
->Type
==ST_ACTOR
) ) {
1089 fx
->Probability2
= 0;
1090 fx
->Probability1
= ((Actor
*) Owner
)->GetSafeStat(fx
->Probability1
);
1094 //the effect didn't pass the probability check
1095 if( !check_probability(fx
) ) {
1096 fx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1097 return FX_NOT_APPLIED
;
1100 //the effect didn't pass the target level check
1101 if( check_level(target
, fx
) ) {
1102 fx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1103 return FX_NOT_APPLIED
;
1106 //the effect didn't pass the resistance check
1107 if( check_resistance(target
, fx
) ) {
1108 fx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1109 return FX_NOT_APPLIED
;
1113 //Same as in items and spells
1114 if (fx
->SourceFlags
& SF_HOSTILE
) {
1115 if (target
&& (target
!= Owner
) && Owner
&& (Owner
->Type
==ST_ACTOR
) ) {
1116 target
->AttackedBy((Actor
*) Owner
);
1120 if( NeedPrepare(fx
->TimingMode
) ) {
1121 //save delay for later
1122 fx
->SecondaryDelay
= fx
->Duration
;
1123 if( fx
->TimingMode
== FX_DURATION_INSTANT_LIMITED
) {
1124 fx
->TimingMode
= FX_DURATION_ABSOLUTE
;
1126 PrepareDuration(fx
);
1129 //check if the effect has triggered or expired
1130 switch (DelayType(fx
->TimingMode
&0xff) ) {
1132 if( fx
->Duration
>GameTime
) {
1133 return FX_NOT_APPLIED
;
1136 //delayed duration (3)
1137 if( NeedPrepare(fx
->TimingMode
) ) {
1138 //prepare for delayed duration effects
1139 fx
->Duration
= fx
->SecondaryDelay
;
1140 PrepareDuration(fx
);
1142 fx
->TimingMode
=TriggeredEffect(fx
->TimingMode
);
1145 if( fx
->Duration
<=GameTime
) {
1146 fx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1147 //add a return here, if 0 duration effects shouldn't work
1150 //permanent effect (so there is no warning)
1153 //this shouldn't happen
1155 printf("Unknown delay type: %d (from %d)\n", DelayType(fx
->TimingMode
&0xff), fx
->TimingMode
);
1159 EffectFunction fn
= 0;
1160 if( fx
->Opcode
<MAX_EFFECTS
) {
1161 fn
= Opcodes
[fx
->Opcode
].Function
;
1165 if( target
&& first_apply
) {
1166 if( !target
->fxqueue
.HasEffectWithParamPair(fx_protection_from_display_string_ref
, fx
->Parameter1
, 0) ) {
1167 displaymsg
->DisplayStringName( Opcodes
[fx
->Opcode
].Strref
, 0xf0f0f0,
1168 target
, IE_STR_SOUND
);
1172 res
=fn( Owner
, target
, fx
);
1174 //if there is no owner, we assume it is the target
1177 //normal effect with duration
1179 case FX_NOT_APPLIED
:
1180 //instant effect, pending removal
1181 //for example, a damage effect
1182 fx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1185 //put this effect in the beginning of the queue
1186 //all known insert effects are 'permanent' too
1187 //that is the AC effect only
1188 //actually, permanent effects seem to be
1189 //inserted by the game engine too
1191 //don't stick around if it was executed permanently
1192 //for example, a permanent strength modifier effect
1193 if( (fx
->TimingMode
== FX_DURATION_INSTANT_PERMANENT
) ) {
1194 fx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1203 //effect not found, it is going to be discarded
1204 fx
->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1209 // looks for opcode with param2
1211 #define MATCH_OPCODE() if((*f)->Opcode!=opcode) { continue; }
1213 // useful for: remove equipped item
1214 #define MATCH_SLOTCODE() if((*f)->InventorySlot!=slotcode) { continue; }
1216 // useful for: remove projectile type
1217 #define MATCH_PROJECTILE() if((*f)->Projectile!=projectile) { continue; }
1219 static const bool fx_live
[MAX_TIMING_MODE
]={true,true,true,false,false,false,false,false,true,true,false};
1220 inline bool IsLive(ieByte timingmode
)
1222 if( timingmode
>=MAX_TIMING_MODE
) return false;
1223 return fx_live
[timingmode
];
1226 #define MATCH_LIVE_FX() if(!IsLive((*f)->TimingMode)) { continue; }
1227 #define MATCH_PARAM1() if((*f)->Parameter1!=param1) { continue; }
1228 #define MATCH_PARAM2() if((*f)->Parameter2!=param2) { continue; }
1229 #define MATCH_RESOURCE() if( strnicmp( (*f)->Resource, resource, 8) ) { continue; }
1230 #define MATCH_SOURCE() if( strnicmp( (*f)->Source, Removed, 8) ) { continue; }
1231 #define MATCH_TIMING() if( (*f)->TimingMode!=timing) { continue; }
1233 //call this from an applied effect, after it returns, these effects
1234 //will be killed along with it
1235 void EffectQueue::RemoveAllEffects(ieDword opcode
) const
1237 std::list
< Effect
* >::const_iterator f
;
1238 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1242 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1246 //removes all equipping effects that match slotcode
1247 void EffectQueue::RemoveEquippingEffects(ieDwordSigned slotcode
) const
1249 std::list
< Effect
* >::const_iterator f
;
1250 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1251 if( !IsEquipped((*f
)->TimingMode
)) continue;
1254 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1258 //removes all effects that match projectile
1259 void EffectQueue::RemoveAllEffectsWithProjectile(ieDword projectile
) const
1261 std::list
< Effect
* >::const_iterator f
;
1262 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1265 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1269 //remove effects belonging to a given spell
1270 void EffectQueue::RemoveAllEffects(const ieResRef Removed
) const
1272 std::list
< Effect
* >::const_iterator f
;
1273 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1277 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1281 //remove effects belonging to a given spell, but only if they match timing method x
1282 void EffectQueue::RemoveAllEffects(const ieResRef Removed
, ieByte timing
) const
1284 std::list
< Effect
* >::const_iterator f
;
1285 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1289 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1293 //this will modify effect reference
1294 void EffectQueue::RemoveAllEffects(EffectRef
&effect_reference
) const
1296 ResolveEffectRef(effect_reference
);
1297 if( effect_reference
.opcode
<0) {
1300 RemoveAllEffects(effect_reference
.opcode
);
1303 //Removes all effects with a matching resource field
1304 void EffectQueue::RemoveAllEffectsWithResource(ieDword opcode
, const ieResRef resource
) const
1306 std::list
< Effect
* >::const_iterator f
;
1307 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1312 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1316 void EffectQueue::RemoveAllEffectsWithResource(EffectRef
&effect_reference
, const ieResRef resource
) const
1318 ResolveEffectRef(effect_reference
);
1319 RemoveAllEffectsWithResource(effect_reference
.opcode
, resource
);
1322 //This method could be used to remove stat modifiers that would lower a stat
1323 //(works only if a higher stat means good for the target)
1324 void EffectQueue::RemoveAllDetrimentalEffects(ieDword opcode
, ieDword current
) const
1326 std::list
< Effect
* >::const_iterator f
;
1327 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1330 switch((*f
)->Parameter2
) {
1332 if( ((signed) (*f
)->Parameter1
)>=0) continue;
1335 if( ((signed) (*f
)->Parameter1
)>=(signed) current
) continue;
1338 if( ((signed) (*f
)->Parameter1
)>=100) continue;
1343 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1347 //Removes all effects with a matching param2
1348 //param2 is usually an effect's subclass (quality) while param1 is more like quantity.
1349 //So opcode+param2 usually pinpoints an effect better when not all effects of a given
1350 //opcode need to be removed (see removal of portrait icon)
1351 void EffectQueue::RemoveAllEffectsWithParam(ieDword opcode
, ieDword param2
) const
1353 std::list
< Effect
* >::const_iterator f
;
1354 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1359 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1363 //this function is called by FakeEffectExpiryCheck
1364 //probably also called by rest
1365 void EffectQueue::RemoveExpiredEffects(ieDword futuretime
) const
1367 ieDword GameTime
= core
->GetGame()->GameTime
;
1368 if( GameTime
+futuretime
*AI_UPDATE_TIME
<GameTime
) {
1369 GameTime
=0xffffffff;
1371 GameTime
+=futuretime
*AI_UPDATE_TIME
;
1374 std::list
< Effect
* >::const_iterator f
;
1375 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1376 //FIXME: how this method handles delayed effects???
1377 //it should remove them as well, i think
1378 if( DelayType( ((*f
)->TimingMode
) )!=PERMANENT
) {
1379 if( (*f
)->Duration
<=GameTime
) {
1380 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1386 //this effect will expire all effects that are not truly permanent
1387 //which i call permanent after death (iesdp calls it permanent after bonuses)
1388 void EffectQueue::RemoveAllNonPermanentEffects() const
1390 std::list
< Effect
* >::const_iterator f
;
1391 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1392 if( IsRemovable((*f
)->TimingMode
) ) {
1393 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1398 //this will modify effect reference
1400 void EffectQueue::RemoveAllDetrimentalEffects(EffectRef
&effect_reference
, ieDword current
) const
1402 ResolveEffectRef(effect_reference
);
1403 RemoveAllDetrimentalEffects(effect_reference
.opcode
, current
);
1406 void EffectQueue::RemoveAllEffectsWithParam(EffectRef
&effect_reference
, ieDword param2
) const
1408 ResolveEffectRef(effect_reference
);
1409 RemoveAllEffectsWithParam(effect_reference
.opcode
, param2
);
1412 //remove certain levels of effects, possibly matching school/secondary type
1413 //this method removes whole spells (tied together by their source)
1414 //FIXME: probably this isn't perfect
1415 void EffectQueue::RemoveLevelEffects(ieResRef
&Removed
, ieDword level
, ieDword Flags
, ieDword match
) const
1418 std::list
< Effect
* >::const_iterator f
;
1419 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1420 if( (*f
)->Power
>level
) {
1427 if( Flags
&RL_MATCHSCHOOL
) {
1428 if( (*f
)->PrimaryType
!=match
) {
1432 if( Flags
&RL_MATCHSECTYPE
) {
1433 if( (*f
)->SecondaryType
!=match
) {
1437 //if dispellable was not set, or the effect is dispellable
1439 if( Flags
&RL_DISPELLABLE
) {
1440 if( !((*f
)->Resistance
&FX_CAN_DISPEL
)) {
1444 (*f
)->TimingMode
= FX_DURATION_JUST_EXPIRED
;
1445 if( Flags
&RL_REMOVEFIRST
) {
1446 memcpy(Removed
,(*f
)->Source
, sizeof(Removed
));
1451 Effect
*EffectQueue::HasOpcode(ieDword opcode
) const
1453 std::list
< Effect
* >::const_iterator f
;
1454 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1463 Effect
*EffectQueue::HasEffect(EffectRef
&effect_reference
) const
1465 ResolveEffectRef(effect_reference
);
1466 if( effect_reference
.opcode
<0) {
1469 return HasOpcode(effect_reference
.opcode
);
1472 Effect
*EffectQueue::HasOpcodeWithParam(ieDword opcode
, ieDword param2
) const
1474 std::list
< Effect
* >::const_iterator f
;
1475 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1485 Effect
*EffectQueue::HasEffectWithParam(EffectRef
&effect_reference
, ieDword param2
) const
1487 ResolveEffectRef(effect_reference
);
1488 if( effect_reference
.opcode
<0) {
1491 return HasOpcodeWithParam(effect_reference
.opcode
, param2
);
1494 //looks for opcode with pairs of parameters (useful for protection against creature, extra damage or extra thac0 against creature)
1495 //generally an IDS targeting
1497 Effect
*EffectQueue::HasOpcodeWithParamPair(ieDword opcode
, ieDword param1
, ieDword param2
) const
1499 std::list
< Effect
* >::const_iterator f
;
1500 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1504 //0 is always accepted as first parameter
1514 Effect
*EffectQueue::HasEffectWithParamPair(EffectRef
&effect_reference
, ieDword param1
, ieDword param2
) const
1516 ResolveEffectRef(effect_reference
);
1517 if( effect_reference
.opcode
<0) {
1520 return HasOpcodeWithParamPair(effect_reference
.opcode
, param1
, param2
);
1523 // sums all the values of the specific damage bonus effects of the passed "damage type"
1524 int EffectQueue::SpecificDamageBonus(ieDword opcode
, ieDword param2
) const
1527 std::list
< Effect
* >::const_iterator f
;
1528 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1532 bonus
+= (signed) (*f
)->Parameter1
;
1537 static EffectRef fx_damage_bonus_modifier_ref
={"DamageBonusModifier",NULL
,-1};
1538 int EffectQueue::SpecificDamageBonus(ieDword damage_type
) const
1540 ResolveEffectRef(fx_damage_bonus_modifier_ref
);
1541 if(fx_damage_bonus_modifier_ref
.opcode
< 0) {
1544 return SpecificDamageBonus(fx_damage_bonus_modifier_ref
.opcode
, damage_type
);
1547 //this could be used for stoneskins and mirror images as well
1548 void EffectQueue::DecreaseParam1OfEffect(ieDword opcode
, ieDword amount
) const
1550 std::list
< Effect
* >::const_iterator f
;
1551 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1554 ieDword value
= (*f
)->Parameter1
;
1555 if( value
>amount
) value
-=amount
;
1557 (*f
)->Parameter1
=value
;
1561 void EffectQueue::DecreaseParam1OfEffect(EffectRef
&effect_reference
, ieDword amount
) const
1563 ResolveEffectRef(effect_reference
);
1564 if( effect_reference
.opcode
<0) {
1567 DecreaseParam1OfEffect(effect_reference
.opcode
, amount
);
1571 //this function does IDS targeting for effects (extra damage/thac0 against creature)
1572 static const int ids_stats
[7]={IE_EA
, IE_GENERAL
, IE_RACE
, IE_CLASS
, IE_SPECIFIC
, IE_SEX
, IE_ALIGNMENT
};
1574 int EffectQueue::BonusAgainstCreature(ieDword opcode
, Actor
*actor
) const
1577 std::list
< Effect
* >::const_iterator f
;
1578 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1581 if( (*f
)->Parameter1
) {
1582 ieDword ids
= (*f
)->Parameter2
;
1583 if( ids
<2 || ids
>8) {
1586 ieDword param1
= actor
->GetStat(ids_stats
[ids
-2]);
1589 int val
= (int) (*f
)->Parameter3
;
1596 int EffectQueue::BonusAgainstCreature(EffectRef
&effect_reference
, Actor
*actor
) const
1598 ResolveEffectRef(effect_reference
);
1599 if( effect_reference
.opcode
<0) {
1602 return BonusAgainstCreature(effect_reference
.opcode
, actor
);
1605 bool EffectQueue::WeaponImmunity(ieDword opcode
, int enchantment
, ieDword weapontype
) const
1607 std::list
< Effect
* >::const_iterator f
;
1608 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1612 int magic
= (int) (*f
)->Parameter1
;
1613 ieDword mask
= (*f
)->Parameter3
;
1614 ieDword value
= (*f
)->Parameter4
;
1616 if( enchantment
) continue;
1617 } else if( magic
>0) {
1618 if( enchantment
>magic
) continue;
1621 if( (weapontype
&mask
) != value
) {
1629 static EffectRef fx_weapon_immunity_ref
={"Protection:Weapons",NULL
,-1};
1631 bool EffectQueue::WeaponImmunity(int enchantment
, ieDword weapontype
) const
1633 ResolveEffectRef(fx_weapon_immunity_ref
);
1634 if( fx_weapon_immunity_ref
.opcode
<0) {
1637 return WeaponImmunity(fx_weapon_immunity_ref
.opcode
, enchantment
, weapontype
);
1640 void EffectQueue::AddWeaponEffects(EffectQueue
*fxqueue
, EffectRef
&fx_ref
) const
1642 ResolveEffectRef(fx_ref
);
1643 if( fx_ref
.opcode
<0) {
1647 ieDword opcode
= fx_ref
.opcode
;
1650 std::list
< Effect
* >::const_iterator f
;
1651 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1655 Effect
*fx
= core
->GetEffect( (*f
)->Resource
, (*f
)->Power
, p
);
1657 fx
->Target
= FX_TARGET_PRESET
;
1658 fxqueue
->AddEffect(fx
, true);
1663 /* no longer needed, use IE_CASTING stat
1664 static EffectRef fx_disable_spellcasting_ref={ "DisableCasting", NULL, -1 };
1665 int EffectQueue::DisabledSpellcasting(int types) const
1667 ResolveEffectRef(fx_disable_spellcasting_ref);
1668 if( fx_disable_spellcasting_ref.opcode < 0) {
1672 unsigned int spelltype_mask = 0;
1673 bool iwd2 = !!core->HasFeature(GF_ENHANCED_EFFECTS);
1674 ieDword opcode = fx_disable_spellcasting_ref.opcode;
1675 std::list< Effect* >::const_iterator f;
1676 for ( f = effects.begin(); f != effects.end(); f++ ) {
1681 switch((*f)->Parameter2) {
1683 spelltype_mask |= 7;
1685 case 1: // mage and cleric
1686 spelltype_mask |= 3;
1689 spelltype_mask |= 2;
1692 spelltype_mask |= 1;
1695 spelltype_mask |= 4;
1699 switch((*f)->Parameter2) {
1701 spelltype_mask |= 2;
1704 spelltype_mask |= 1;
1707 spelltype_mask |= 4;
1712 return spelltype_mask & types;
1716 //useful for immunity vs spell, can't use item, etc.
1717 Effect
*EffectQueue::HasOpcodeWithResource(ieDword opcode
, const ieResRef resource
) const
1719 std::list
< Effect
* >::const_iterator f
;
1720 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1730 Effect
*EffectQueue::HasEffectWithResource(EffectRef
&effect_reference
, const ieResRef resource
) const
1732 ResolveEffectRef(effect_reference
);
1733 return HasOpcodeWithResource(effect_reference
.opcode
, resource
);
1736 //used in contingency/sequencer code (cannot have the same contingency twice)
1737 Effect
*EffectQueue::HasOpcodeWithSource(ieDword opcode
, const ieResRef Removed
) const
1739 std::list
< Effect
* >::const_iterator f
;
1740 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1750 Effect
*EffectQueue::HasEffectWithSource(EffectRef
&effect_reference
, const ieResRef resource
) const
1752 ResolveEffectRef(effect_reference
);
1753 return HasOpcodeWithSource(effect_reference
.opcode
, resource
);
1756 bool EffectQueue::HasAnyDispellableEffect() const
1758 std::list
< Effect
* >::const_iterator f
;
1759 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1760 if( (*f
)->Resistance
&FX_CAN_DISPEL
) {
1767 void EffectQueue::dump() const
1769 printf( "EFFECT QUEUE:\n" );
1771 std::list
< Effect
* >::const_iterator f
;
1772 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1776 if( fx
->Opcode
< MAX_EFFECTS
)
1777 Name
= (char*) Opcodes
[fx
->Opcode
].Name
;
1779 printf( " %2d: 0x%02x: %s (%d, %d) S:%s\n", i
++, fx
->Opcode
, Name
, fx
->Parameter1
, fx
->Parameter2
, fx
->Source
);
1784 Effect *EffectQueue::GetEffect(ieDword idx) const
1786 if( effects.size()<=idx) {
1789 return effects[idx];
1793 //returns true if the effect supports simplified duration
1794 bool EffectQueue::HasDuration(Effect
*fx
)
1796 switch(fx
->TimingMode
) {
1797 case FX_DURATION_INSTANT_LIMITED
: //simple duration
1798 case FX_DURATION_DELAY_LIMITED
: //delayed duration
1799 case FX_DURATION_DELAY_PERMANENT
: //simple delayed
1805 static EffectRef fx_variable_ref
={"Variable:StoreLocalVariable",NULL
,-1};
1807 //returns true if the effect must be saved
1808 //variables are saved differently
1809 bool EffectQueue::Persistent(Effect
* fx
)
1811 //we save this as variable
1812 if( fx
->Opcode
==(ieDword
) ResolveEffect(fx_variable_ref
)) {
1816 switch (fx
->TimingMode
) {
1817 //normal equipping fx of items
1818 case FX_DURATION_INSTANT_WHILE_EQUIPPED
:
1819 //delayed effect not saved
1820 case FX_DURATION_DELAY_UNSAVED
:
1821 //permanent effect not saved
1822 case FX_DURATION_PERMANENT_UNSAVED
:
1823 //just expired effect
1824 case FX_DURATION_JUST_EXPIRED
:
1830 //alter the color effect in case the item is equipped in the shield slot
1831 void EffectQueue::HackColorEffects(Actor
*Owner
, Effect
*fx
)
1833 if( fx
->InventorySlot
!=Owner
->inventory
.GetShieldSlot()) return;
1835 unsigned int gradienttype
= fx
->Parameter2
& 0xF0;
1836 if( gradienttype
== 0x10) {
1837 gradienttype
= 0x20; // off-hand
1838 fx
->Parameter2
&= ~0xF0;
1839 fx
->Parameter2
|= gradienttype
;
1843 //iterate through saved effects
1844 const Effect
*EffectQueue::GetNextSavedEffect(std::list
< Effect
* >::const_iterator
&f
) const
1846 while(f
!=effects
.end()) {
1847 Effect
*effect
= *f
;
1849 if( Persistent(effect
)) {
1856 Effect
*EffectQueue::GetNextEffect(std::list
< Effect
* >::const_iterator
&f
) const
1858 if( f
!=effects
.end()) return *f
++;
1862 ieDword
EffectQueue::CountEffects(ieDword opcode
, ieDword param1
, ieDword param2
, const char *resource
) const
1866 std::list
< Effect
* >::const_iterator f
;
1868 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1870 if( param1
!=0xffffffff)
1872 if( param2
!=0xffffffff)
1882 void EffectQueue::ModifyEffectPoint(ieDword opcode
, ieDword x
, ieDword y
) const
1884 std::list
< Effect
* >::const_iterator f
;
1886 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1895 //count effects that get saved
1896 ieDword
EffectQueue::GetSavedEffectsCount() const
1900 std::list
< Effect
* >::const_iterator f
;
1902 for ( f
= effects
.begin(); f
!= effects
.end(); f
++ ) {
1910 void EffectQueue::TransformToDelay(ieByte
&TimingMode
)
1912 if( TimingMode
<MAX_TIMING_MODE
) {;
1913 TimingMode
= fx_to_delayed
[TimingMode
];
1915 TimingMode
= FX_DURATION_JUST_EXPIRED
;
1919 int EffectQueue::ResolveEffect(EffectRef
&effect_reference
)
1921 ResolveEffectRef(effect_reference
);
1922 return effect_reference
.opcode
;
1925 // this check goes for the whole effect block, not individual effects
1926 // But it takes the first effect of the block for the common fields
1928 //returns 1 if effect block applicable
1929 //returns 0 if effect block disabled
1930 //returns -1 if effect block bounced
1931 int EffectQueue::CheckImmunity(Actor
*target
) const
1933 //don't resist if target is non living
1938 if( effects
.size() ) {
1939 Effect
* fx
= *effects
.begin();
1941 //projectile immunity
1942 if( target
->ImmuneToProjectile(fx
->Projectile
)) return 0;
1944 //don't resist item projectile payloads based on spell school, bounce, etc.
1945 if( fx
->InventorySlot
) {
1949 //check level resistances
1950 //check specific spell immunity
1951 //check school/sectype immunity
1952 return check_type(target
, fx
);
1957 void EffectQueue::AffectAllInRange(Map
*map
, const Point
&pos
, int idstype
, int idsvalue
,
1958 unsigned int range
, Actor
*except
)
1960 int cnt
= map
->GetActorCount(true);
1962 Actor
*actor
= map
->GetActor(cnt
,true);
1963 if( except
==actor
) {
1967 if( Distance(pos
, actor
)>range
) {
1971 if( !match_ids(actor
, idstype
, idsvalue
)) {
1975 if( !map
->IsVisible(actor
->Pos
, pos
)) {
1978 AddAllEffects(actor
, actor
->Pos
);