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