1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2003 The GemRB Project
3  *
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.
8 
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.
13 
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.
17  *
18  *
19  */
20 
21 /**
22  * @file EffectQueue.h
23  * Declares EffectQueue class holding and processing all spell effects
24  * on a single Actor
25  * @author The GemRB Project
26  */
27 
28 #ifndef EFFECTQUEUE_H
29 #define EFFECTQUEUE_H
30 
31 #include "exports.h"
32 
33 #include "Effect.h"
34 #include "Region.h"
35 
36 #include "System/Logging.h"
37 #include "System/StringBuffer.h"
38 
39 #include <cstdlib>
40 #include <list>
41 
42 namespace GemRB {
43 
44 class Actor;
45 class Map;
46 class Scriptable;
47 class StringBuffer;
48 
49 /** Maximum number of different Effect opcodes */
50 #define MAX_EFFECTS 512
51 
52 ///** if the effect returns this, stop adding any other effect */
53 #define FX_ABORT 0
54 /** these effects don't stick around if used as permanent,
55  * in that case they modify a base stat like charisma modifier */
56 #define FX_PERMANENT 2
57 /** these effects never stick around, use them for instant effects like damage */
58 #define FX_NOT_APPLIED 3
59 /** these effects always stick around when applied as permanent or duration */
60 #define FX_APPLIED 1
61 ///** insert the effect instead of push back */
62 #define FX_INSERT 4
63 
64 //remove level effects flags
65 #define RL_DISPELLABLE  1  //only dispellables
66 #define RL_MATCHSCHOOL  2  //match school
67 #define RL_MATCHSECTYPE 4  //match secondary type
68 #define RL_REMOVEFIRST  8  //remove only one spell (could be more effects)
69 
70 //bouncing immunities
71 #define BNC_PROJECTILE  1
72 #define BNC_OPCODE      2
73 #define BNC_LEVEL       4
74 #define BNC_SCHOOL      8
75 #define BNC_SECTYPE     0x10
76 #define BNC_RESOURCE    0x20
77 #define BNC_PROJECTILE_DEC 0x100
78 #define BNC_OPCODE_DEC  0x200
79 #define BNC_LEVEL_DEC   0x400
80 #define BNC_SCHOOL_DEC  0x800
81 #define BNC_SECTYPE_DEC 0x1000
82 #define BNC_RESOURCE_DEC 0x2000
83 
84 //normal immunities
85 #define IMM_PROJECTILE  1
86 #define IMM_OPCODE      2
87 #define IMM_LEVEL       4
88 #define IMM_SCHOOL      8
89 #define IMM_SECTYPE     16
90 #define IMM_RESOURCE    32
91 #define IMM_PROJECTILE_DEC 0x100
92 #define IMM_OPCODE_DEC  0x200
93 #define IMM_LEVEL_DEC   0x400
94 #define IMM_SCHOOL_DEC  0x800
95 #define IMM_SECTYPE_DEC 0x1000
96 #define IMM_RESOURCE_DEC 0x2000
97 
98 //pst immunities
99 #define IMM_GUARDIAN  0x80000000
100 
101 //sometimes damage doesn't comply with the calculated value
102 #define DICE_ROLL(adjustment) (core->Roll( fx->DiceThrown, fx->DiceSides, adjustment) )
103 
104 // You will need to get GameTime somehow to use this macro
105 // we add +1 so we can handle effects with 0 duration (apply once only)
106 #define	PrepareDuration(fx) fx->Duration = ((fx->Duration? fx->Duration*AI_UPDATE_TIME : 1) + GameTime)
107 
108 //return the caster object
109 #define GetCasterObject()  (core->GetGame()->GetActorByGlobalID(fx->CasterID))
110 
111 // often used stat modifications, usually Parameter2 types 0, 1 and 2
112 //these macros should work differently in permanent mode (modify base too)
113 #define STAT_GET(stat) (target->Modified[ stat ])
114 #define STAT_ADD(stat, mod) target->SetStat( stat, STAT_GET( stat ) + ( mod ), 0 )
115 #define STAT_SUB(stat, mod) target->SetStat( stat, STAT_GET( stat ) - ( mod ), 0 )
116 #define STAT_BIT_OR(stat, mod) target->SetStat( stat, STAT_GET( stat ) | ( mod ), 0 )
117 #define STAT_SET(stat, mod) target->SetStat( stat, ( mod ), 0 )
118 #define STAT_SET_PCF(stat, mod) target->SetStat( stat, ( mod ), 1 )
119 #define STAT_BIT_OR_PCF(stat, mod) target->NewStat(stat, (mod), MOD_BITOR)
120 #define STAT_MUL(stat, mod) target->SetStat( stat, STAT_GET(stat) * ( mod ) / 100, 0 )
121 //if an effect sticks around
122 #define STATE_CURE( mod ) target->Modified[ IE_STATE_ID ] &= ~(ieDword) ( mod )
123 #define STATE_SET( mod ) target->Modified[ IE_STATE_ID ] |= (ieDword) ( mod )
124 #define EXTSTATE_SET( mod ) target->Modified[ IE_EXTSTATE_ID ] |= (ieDword) ( mod )
125 #define STATE_GET( mod ) (target->Modified[ IE_STATE_ID ] & (ieDword) ( mod ) )
126 #define EXTSTATE_GET( mod ) (target->Modified[ IE_EXTSTATE_ID ] & (ieDword) ( mod ) )
127 #define STAT_MOD( stat ) target->NewStat(stat, fx->Parameter1, fx->Parameter2)
128 #define STAT_MOD_VAR( stat, mod ) target->NewStat(stat, ( mod ) , fx->Parameter2 )
129 #define BASE_GET(stat) (target->BaseStats[ stat ])
130 #define BASE_SET(stat, mod) target->SetBase( stat, ( mod ) )
131 #define BASE_ADD(stat, mod) target->SetBase( stat, BASE_GET(stat)+ ( mod ) )
132 #define BASE_SUB(stat, mod) target->SetBase( stat, BASE_GET(stat)- ( mod ) )
133 #define BASE_MUL(stat, mod) target->SetBase( stat, BASE_GET(stat)* ( mod ) / 100 )
134 #define BASE_MOD(stat) target->NewBase( stat, fx->Parameter1, fx->Parameter2)
135 #define BASE_MOD_VAR(stat, mod) target->NewBase( stat, (mod), fx->Parameter2 )
136 //if an effect doesn't stick (and has permanent until cured effect) then
137 //it has to modify the base stat (which is saved)
138 //also use this one if the effect starts a cure effect automatically
139 #define BASE_STATE_SET( mod ) target->SetBaseBit( IE_STATE_ID, ( mod ), true )
140 #define BASE_STATE_CURE( mod ) target->SetBaseBit( IE_STATE_ID, ( mod ), false )
141 
142 /** Prototype of a function implementing a particular Effect opcode */
143 typedef int (* EffectFunction)(Scriptable*, Actor*, Effect*);
144 
145 
146 /** Cached Effect -> opcode mapping */
147 struct EffectRef {
148 	const char* Name;
149 	int opcode;
150 };
151 
152 /** Links Effect name to a function implementing the effect */
153 class EffectDesc {
154 	EffectFunction Function;
155 
156 public:
157 	const char* Name; // FIXME: shouldn't we presume ownership of the name? original implementation didn't
158 	int Flags;
159 
160 	union {
161 		int opcode;
162 		int Strref;
163 	};
164 
EffectDesc()165 	EffectDesc() {
166 		Function = NULL;
167 		Name = NULL;
168 		Flags = 0;
169 		opcode = -1;
170 	}
171 
EffectDesc(const char * name,EffectFunction fn,int flags,int data)172 	EffectDesc(const char* name, EffectFunction fn, int flags, int data) {
173 		Function = fn;
174 		Name = name;
175 		Flags = flags;
176 		opcode = data;
177 	}
178 
179 	operator bool() const {
180 		return Function != NULL;
181 	}
182 
operator()183 	int operator()(Scriptable* s, Actor* a, Effect* fx) const {
184 		return Function(s, a, fx);
185 	}
186 
187 };
188 
189 enum EffectFlags {
190 	EFFECT_NORMAL = 0,
191 	EFFECT_DICED = 1,
192 	EFFECT_NO_LEVEL_CHECK = 2,
193 	EFFECT_NO_ACTOR = 4,
194 	EFFECT_REINIT_ON_LOAD = 8,
195 	EFFECT_PRESET_TARGET = 16,
196 	EFFECT_SPECIAL_UNDO = 32
197 };
198 
199 /** Initializes table of available spell Effects used by all the queues. */
200 /** The available effects should already be registered by the effect plugins */
201 bool Init_EffectQueue();
202 
203 /** Registers opcodes implemented by an effect plugin */
204 void EffectQueue_RegisterOpcodes(int count, const EffectDesc *opcodes);
205 
206 /** release effect list when Interface is destroyed */
207 void EffectQueue_ReleaseMemory();
208 
209 /** Check if opcode is for an effect that takes a color slot as parameter. */
210 bool IsColorslotEffect(int opcode);
211 
212 /**
213  * @class EffectQueue
214  * Class holding and processing spell Effects on a single Actor
215  */
216 
217 class GEM_EXPORT EffectQueue {
218 private:
219 	/** List of Effects applied on the Actor */
220 	std::list< Effect* > effects;
221 	/** Actor which is target of the Effects */
222 	Scriptable* Owner;
223 
224 public:
225 	EffectQueue();
226 	virtual ~EffectQueue();
227 
228 	/** Sets Actor which is affected by these effects */
SetOwner(Scriptable * act)229 	void SetOwner(Scriptable* act) { Owner = act; }
230 	/** Returns Actor affected by these effects */
GetOwner()231 	Scriptable* GetOwner() const { return Owner; }
232 
233 	/** adds an effect to the queue, it could also insert it if flagged so
234 	 *  fx should be freed by the caller
235 	 */
236 	void AddEffect(const Effect* fx, bool insert=false);
237 	/** Adds an Effect to the queue, subject to level and other checks.
238 	 * Returns FX_ABORT if unsuccessful. fx is just a reference, AddEffect()
239 	 * will malloc its own copy */
240 	int AddEffect(Effect* fx, Scriptable* self, Actor* pretarget, const Point &dest) const;
241 	/** Removes first Effect matching fx from the queue.
242 	 * Effects are matched based on their contents */
243 	bool RemoveEffect(const Effect* fx);
244 
245 	int AddAllEffects(Actor* target, const Point &dest) const;
246 	void ApplyAllEffects(Actor* target) const;
247 	/** remove effects marked for removal */
248 	void Cleanup();
249 
250 	/* directly removes effects with specified opcode, use effect_reference when you can */
251 	void RemoveAllEffects(ieDword opcode) const;
252 
253 	/* directly removes effects with specified opcode and resource (used by IWD) */
254 	void RemoveAllEffectsWithResource(ieDword opcode, const ieResRef resource) const;
255 
256 	/* removes any effects (delayed or not) which were using projectile */
257 	void RemoveAllEffectsWithProjectile(ieDword projectile) const;
258 
259 	/* removes equipping effects with specified inventory slot code */
260 	bool RemoveEquippingEffects(ieDwordSigned slotcode) const;
261 
262 	/* removes all effects of a given spell */
263 	void RemoveAllEffects(const ieResRef Removed) const;
264 	void RemoveAllEffects(const ieResRef Removed, ieByte timing) const;
265 	/* removes all effects of type */
266 	void RemoveAllEffects(EffectRef &effect_reference) const;
267 	/* removes expired or to be expired effects */
268 	void RemoveExpiredEffects(ieDword futuretime) const;
269 	/* removes all effects except timing mode 9 */
270 	void RemoveAllNonPermanentEffects() const;
271 	void RemoveAllDetrimentalEffects(EffectRef &effect_reference, ieDword current) const;
272 	void RemoveAllEffectsWithParam(EffectRef &effect_reference, ieDword param2) const;
273 	void RemoveAllEffectsWithResource(EffectRef &effect_reference, const ieResRef resource) const;
274 	void RemoveAllEffectsWithParamAndResource(EffectRef &effect_reference, ieDword param2, const ieResRef resource) const;
275 	void RemoveLevelEffects(ieResRef &Removed, ieDword level, ieDword flags, ieDword match) const;
276 	void DispelEffects(Effect *dispeller, ieDword level) const;
277 
278 	/* returns true if the timing method supports simplified duration */
279 	static bool HasDuration(const Effect *fx);
280 	/* returns true if the effect should be saved */
281 	static bool Persistent(const Effect* fx);
282 	/* returns next saved effect, increases index */
GetFirstEffect()283 	std::list< Effect* >::const_iterator GetFirstEffect() const
284 	{
285 		return effects.begin();
286 	}
287 	const Effect *GetNextSavedEffect(std::list< Effect* >::const_iterator &f) const;
288 	Effect *GetNextEffect(std::list< Effect* >::const_iterator &f) const;
289 	ieDword CountEffects(EffectRef &effect_reference, ieDword param1, ieDword param2, const char *ResRef) const;
290 	void ModifyEffectPoint(EffectRef &effect_reference, ieDword x, ieDword y) const;
291 	void ModifyAllEffectSources(const Point &source);
292 	/* returns the number of saved effects */
293 	ieDword GetSavedEffectsCount() const;
GetEffectsCount()294 	size_t GetEffectsCount() const { return effects.size(); }
295 	unsigned int GetEffectOrder(EffectRef &effect_reference, const Effect *fx) const;
296 	/* this method hacks the offhand weapon color effects */
297 	static void HackColorEffects(const Actor *Owner, Effect *fx);
298 	static Effect *CreateEffect(EffectRef &effect_reference, ieDword param1, ieDword param2, ieWord timing);
299 	EffectQueue *CopySelf() const;
300 	static Effect *CreateEffectCopy(const Effect *oldfx, EffectRef &effect_reference, ieDword param1, ieDword param2);
301 	static Effect *CreateUnsummonEffect(const Effect *fx);
302 	//locating opcodes
303 	Effect *HasEffect(EffectRef &effect_reference) const;
304 	Effect *HasEffectWithParam(EffectRef &effect_reference, ieDword param2) const;
305 	Effect *HasEffectWithParamPair(EffectRef &effect_reference, ieDword param1, ieDword param2) const;
306 	Effect *HasEffectWithResource(EffectRef &effect_reference, const ieResRef resource) const;
307 	Effect *HasEffectWithPower(EffectRef &effect_reference, ieDword power) const;
308 	Effect *HasSource(const ieResRef source) const;
309 	Effect *HasEffectWithSource(EffectRef &effect_reference, const ieResRef source) const;
310 	void DecreaseParam1OfEffect(EffectRef &effect_reference, ieDword amount) const;
311 	int DecreaseParam3OfEffect(EffectRef &effect_reference, ieDword amount, ieDword param2) const;
312 	int BonusForParam2(EffectRef &effect_reference, ieDword param2) const;
313 	int MaxParam1(EffectRef &effect_reference, bool positive) const;
314 	bool HasAnyDispellableEffect() const;
315 	//getting summarised effects
316 	int BonusAgainstCreature(EffectRef &effect_reference, const Actor *actor) const;
317 	//getting weapon immunity flag
318 	bool WeaponImmunity(int enchantment, ieDword weapontype) const;
319 	int SumDamageReduction(EffectRef &effect_reference, ieDword weaponEnchantment, int &total) const;
320 	//melee and ranged effects
321 	void AddWeaponEffects(EffectQueue *fxqueue, EffectRef &fx_ref) const;
322 
323 	// returns -1 if bounced, 0 if resisted, 1 if accepted spell
324 	int CheckImmunity(Actor *target) const;
325 	// apply this effectqueue on all actors matching ids targeting
326 	// from pos, in range (no cone size yet)
327 	void AffectAllInRange(const Map *map, const Point &pos, int idstype, int idsvalue, unsigned int range, const Actor *except);
328 	/** Lists contents of the queue on a terminal for debugging */
329 	void dump() const;
330 	void dump(StringBuffer&) const;
331 	//resolve effect
332 	static int ResolveEffect(EffectRef &effect_reference);
333 	static bool match_ids(const Actor *target, int table, ieDword value);
334 	/** returns true if the process should abort applying a stack of effects */
335 	int ApplyEffect(Actor* target, Effect* fx, ieDword first_apply, ieDword resistance=1) const;
336 	/** just checks if it is a particularly stupid effect that needs its target reset */
337 	static bool OverrideTarget(const Effect *fx);
338 	bool HasHostileEffects() const;
339 private:
340 	/** counts effects of specific opcode, parameters and resource */
341 	ieDword CountEffects(ieDword opcode, ieDword param1, ieDword param2, const char *ResRef) const;
342 	void ModifyEffectPoint(ieDword opcode, ieDword x, ieDword y) const;
343 	//use the effect reference style calls from outside
344 	static Effect *CreateEffect(ieDword opcode, ieDword param1, ieDword param2, ieWord timing);
345 	static Effect *CreateEffectCopy(const Effect *oldfx, ieDword opcode, ieDword param1, ieDword param2);
346 	void RemoveAllDetrimentalEffects(ieDword opcode, ieDword current) const;
347 	void RemoveAllEffectsWithParam(ieDword opcode, ieDword param2) const;
348 	void RemoveAllEffectsWithParamAndResource(ieDword opcode, ieDword param2, const ieResRef resource) const;
349 	Effect *HasOpcode(ieDword opcode) const;
350 	Effect *HasOpcodeWithParam(ieDword opcode, ieDword param2) const;
351 	Effect *HasOpcodeWithParamPair(ieDword opcode, ieDword param1, ieDword param2) const;
352 	Effect *HasOpcodeWithResource(ieDword opcode, const ieResRef resource) const;
353 	Effect *HasOpcodeWithPower(ieDword opcode, ieDword power) const;
354 	Effect *HasOpcodeWithSource(ieDword opcode, const ieResRef source) const;
355 	void DecreaseParam1OfEffect(ieDword opcode, ieDword amount) const;
356 	int DecreaseParam3OfEffect(ieDword opcode, ieDword amount, ieDword param2) const;
357 	int BonusForParam2(ieDword opcode, ieDword param2) const;
358 	int MaxParam1(ieDword opcode, bool positive) const;
359 	int BonusAgainstCreature(ieDword opcode, const Actor *actor) const;
360 	bool WeaponImmunity(ieDword opcode, int enchantment, ieDword weapontype) const;
361 };
362 
363 }
364 
365 #endif // ! EFFECTQUEUE_H
366