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 #ifndef SCRIPTABLE_H 22 #define SCRIPTABLE_H 23 24 #include "exports.h" 25 26 #include "CharAnimations.h" 27 #include "Variables.h" 28 29 #include <list> 30 #include <map> 31 #include <memory> 32 33 namespace GemRB { 34 35 class Action; 36 class Actor; 37 class Container; 38 class Door; 39 class GameScript; 40 class Gem_Polygon; 41 class Highlightable; 42 class InfoPoint; 43 class Map; 44 class Movable; 45 class Object; 46 struct PathNode; 47 class Scriptable; 48 class Selectable; 49 class Spell; 50 class Sprite2D; 51 52 #define MAX_GROUND_ICON_DRAWN 3 53 54 /** The distance between PC's who are about to enter a new area */ 55 #define MAX_TRAVELING_DISTANCE 400 56 57 // script levels / slots (scrlev.ids) 58 #define SCR_OVERRIDE 0 59 #define SCR_AREA 1 // iwd2: special 1 60 #define SCR_SPECIFICS 2 // iwd2: team 61 #define SCR_RESERVED 3 // iwd2: pc-customizable scripts were saved here 62 #define SCR_CLASS 4 // iwd2: special 2 63 #define SCR_RACE 5 // iwd2: combat 64 #define SCR_GENERAL 6 // iwd2: special 3 65 #define SCR_DEFAULT 7 // iwd2: movement 66 #define MAX_SCRIPTS 8 67 68 //pst trap flags (portal) 69 #define PORTAL_CURSOR 1 70 #define PORTAL_TRAVEL 2 71 72 //trigger flags 73 #define TRAP_INVISIBLE 1 74 #define TRAP_RESET 2 75 #define TRAVEL_PARTY 4 76 #define TRAP_DETECTABLE 8 77 //#define TRAP_ENEMY 16 // "trap set off by enemy" in NI, unused 78 #define TRAP_TUTORIAL 32 //active only when in tutorial mode 79 #define TRAP_NPC 64 // "trap set off by NPC" 80 //#define TRAP_SILENT 128 // "trigger silent", unused 81 #define TRAP_DEACTIVATED 256 82 #define _TRAVEL_NONPC 512 83 #define _TRAP_USEPOINT 1024 //override usage point of travel regions (used for sound in PST traps) 84 #define INFO_DOOR 2048 //info trigger blocked by door 85 86 //internal actor flags 87 #define IF_GIVEXP 1 //give xp for this death 88 #define IF_JUSTDIED 2 //Died() will return true 89 #define IF_FROMGAME 4 //this is an NPC or PC 90 #define IF_REALLYDIED 8 //real death happened, actor will be set to dead 91 #define IF_NORETICLE 16 //draw reticle (target mark) 92 #define IF_NOINT 32 //cannot interrupt the actions of this actor (save is not possible!) 93 #define IF_CLEANUP 64 //actor died chunky death, or other total destruction 94 #define IF_RUNNING 128 //actor is running 95 //these bits could be set by a WalkTo 96 #define IF_RUNFLAGS (IF_RUNNING|IF_NORETICLE|IF_NOINT) 97 //#define IF_BECAMEVISIBLE 0x100//actor just became visible (trigger event) 98 #define IF_INITIALIZED 0x200 99 #define IF_USEDSAVE 0x400 //actor needed saving throws 100 #define IF_GOTAREA 0x800 //actor already moved to an area 101 #define IF_USEEXIT 0x1000 // 102 #define IF_INTRAP 0x2000 //actor is currently in a trap (intrap trigger event) 103 //#define IF_WASINDIALOG 0x4000 //actor just left dialog 104 #define IF_PST_WMAPPING 0x8000 // trying to use the worldmap for travel 105 106 //scriptable flags 107 #define IF_ACTIVE 0x10000 108 #define IF_VISIBLE 0x40000 109 //#define IF_ONCREATION 0x80000 110 #define IF_IDLE 0x100000 111 //#define IF_PARTYRESTED 0x200000 //party rested trigger event 112 #define IF_FORCEUPDATE 0x400000 113 #define IF_TRIGGER_AP 0x800000 114 115 //the actor should stop attacking 116 #define IF_STOPATTACK (IF_JUSTDIED|IF_REALLYDIED|IF_CLEANUP|IF_IDLE) 117 118 //CheckTravel return value 119 #define CT_CANTMOVE 0 //inactive 120 #define CT_ACTIVE 1 //actor can move 121 #define CT_GO_CLOSER 2 //entire team would move, but not close enough 122 #define CT_WHOLE 3 //team can move 123 #define CT_SELECTED 4 //not all selected actors are there 124 #define CT_MOVE_SELECTED 5 //all selected can move 125 126 //xp bonus types (for xpbonus.2da) 127 #define XP_LOCKPICK 0 128 #define XP_DISARM 1 129 #define XP_LEARNSPELL 2 130 131 #define MAX_PATH_TRIES 8 132 #define MAX_BUMP_BACK_TRIES 16 133 #define MAX_RAND_WALK 10 134 135 typedef enum ScriptableType { ST_ACTOR = 0, ST_PROXIMITY = 1, ST_TRIGGER = 2, 136 ST_TRAVEL = 3, ST_DOOR = 4, ST_CONTAINER = 5, ST_AREA = 6, ST_GLOBAL = 7 } ScriptableType; 137 138 enum { 139 trigger_acquired = 0x1, // unused and broken in the original 140 trigger_attackedby = 0x2, 141 trigger_help = 0x3, 142 trigger_joins = 0x4, 143 trigger_leaves = 0x5, 144 trigger_receivedorder = 0x6, 145 trigger_said = 0x7, // unused in the originals; if added, don't forget to set LastTrigger 146 trigger_turnedby = 0x8, 147 trigger_unusable = 0x9, 148 trigger_alignment = 0xa, 149 trigger_allegiance = 0xb, 150 trigger_class = 0xc, 151 trigger_exists = 0xd, 152 trigger_general = 0xe, 153 trigger_hpgt = 0x11, 154 trigger_morale = 0x14, 155 trigger_race = 0x17, 156 trigger_range = 0x18, 157 trigger_reputation = 0x19, 158 trigger_specifics = 0x1d, 159 trigger_hitby = 0x20, 160 trigger_hotkey = 0x21, 161 trigger_timerexpired = 0x22, // handled internally through TimerExpired 162 trigger_trigger = 0x24, 163 trigger_die = 0x25, 164 trigger_targetunreachable = 0x26, 165 trigger_heard = 0x2f, 166 trigger_becamevisible = 0x33, 167 trigger_oncreation = 0x36, 168 trigger_statecheck = 0x37, 169 trigger_reaction = 0x3c, 170 trigger_inparty = 0x43, 171 trigger_checkstat = 0x44, 172 trigger_died = 0x4a, 173 trigger_killed = 0x4b, 174 trigger_entered = 0x4c, 175 trigger_gender = 0x4c, 176 trigger_opened = 0x52, 177 trigger_closed = 0x53, 178 trigger_detected = 0x54, 179 trigger_reset = 0x55, 180 trigger_disarmed = 0x56, 181 trigger_unlocked = 0x57, 182 trigger_breakingpoint = 0x5c, 183 trigger_pickpocketfailed = 0x5d, 184 trigger_stealfailed = 0x5e, 185 trigger_disarmfailed = 0x5f, 186 trigger_picklockfailed = 0x60, 187 trigger_clicked = 0x70, 188 trigger_triggerclick = 0x79, // pst; we map it to trigger_clicked 189 trigger_traptriggered = 0x87, // bg2 190 trigger_partymemberdied = 0x88, // bg2 191 trigger_spellcast = 0x91, // bg2 192 trigger_partyrested = 0x93, // bg2 193 trigger_vacant = 0x94, // pst 194 trigger_summoned = 0x97, // bg2 195 trigger_harmlessopened = 0x9d, // pst 196 trigger_harmlessclosed = 0x9e, // pst 197 trigger_harmlessentered = 0x9f, // pst 198 trigger_spellcastonme = 0xa1, // bg2 199 trigger_nulldialog = 0xa4, // pst, checked directly in NullDialog 200 trigger_wasindialog = 0xa5, // pst 201 trigger_spellcastpriest = 0xa6, // bg2 202 trigger_spellcastinnate = 0xa7, // bg2 203 trigger_namelessbitthedust = 0xab, // pst 204 trigger_failedtoopen = 0xaf, // pst 205 trigger_tookdamage = 0xcc, // bg2 206 trigger_walkedtotrigger = 0xd6 // bg2 207 }; 208 209 // flags for TriggerEntry 210 enum { 211 // has the effect queue (if any) been processed since this trigger 212 // was added? (for fx_cast_spell_on_condition) 213 TEF_PROCESSED_EFFECTS = 1 214 }; 215 216 struct TriggerEntry { TriggerEntryTriggerEntry217 TriggerEntry(unsigned short id) : triggerID(id), param1(0), param2(0), flags(0) { } TriggerEntryTriggerEntry218 TriggerEntry(unsigned short id, ieDword p1) : triggerID(id), param1(p1), param2(0), flags(0) { } TriggerEntryTriggerEntry219 TriggerEntry(unsigned short id, ieDword p1, ieDword p2) : triggerID(id), param1(p1), param2(p2), flags(0) { } 220 221 unsigned short triggerID; 222 ieDword param1; 223 ieDword param2; 224 unsigned int flags; 225 }; 226 227 //typedef std::list<ieDword *> TriggerObjects; 228 229 //#define SEA_RESET 0x00000002 230 //#define SEA_PARTY_REQUIRED 0x00000004 231 232 class GEM_EXPORT Scriptable { 233 public: 234 Scriptable(ScriptableType type); 235 virtual ~Scriptable(void); 236 private: 237 unsigned long WaitCounter; 238 std::map<ieDword,ieDword> script_timers; 239 ieDword globalID; 240 protected: //let Actor access this 241 std::list<TriggerEntry> triggers; 242 Map *area; 243 ieVariable scriptName; 244 ieDword InternalFlags; //for triggers 245 ieResRef Dialog; 246 std::list< Action*> actionQueue; 247 Action* CurrentAction; 248 249 // Variables for overhead text. 250 Point overHeadTextPos; 251 bool overheadTextDisplaying; 252 unsigned long timeStartDisplaying; 253 String OverheadText; 254 public: 255 Region BBox; 256 // State relating to the currently-running action. 257 int CurrentActionState; 258 ieDword CurrentActionTarget; 259 bool CurrentActionInterruptable; 260 ieDword CurrentActionTicks; 261 262 // The number of times this was updated. 263 ieDword Ticks; 264 // The same, after adjustment for being slowed/hasted. 265 ieDword AdjustedTicks; 266 // The number of times UpdateActions() was run. 267 ieDword ScriptTicks; 268 // The number of times since UpdateActions() tried to do anything. 269 ieDword IdleTicks; 270 // The number of ticks since the last spellcast 271 ieDword AuraTicks; 272 // The countdown for forced activation by triggers. 273 ieDword TriggerCountdown; 274 275 Variables* locals; 276 ScriptableType Type; 277 Point Pos; 278 279 ieStrRef DialogName; 280 281 GameScript* Scripts[MAX_SCRIPTS]; 282 int scriptlevel; 283 284 ieDword UnselectableTimer; 285 286 // Stored objects. 287 ieDword LastAttacker; 288 ieDword LastCommander; 289 ieDword LastProtector; 290 ieDword LastProtectee; 291 ieDword LastTargetedBy; 292 ieDword LastHitter; 293 ieDword LastHelp; 294 ieDword LastTrigger; 295 ieDword LastSeen; 296 ieDword LastTalker; 297 ieDword LastHeard; 298 ieDword LastSummoner; 299 ieDword LastFollowed; // gemrb extension (LeaderOf) 300 ieDword LastMarked; // iwd2 301 ieDword MyTarget = 0; // iwd2, has nothing to do with LastTarget 302 303 int LastMarkedSpell; // iwd2 304 305 // this is used by GUIScript :( 306 ieDword LastSpellOnMe; //Last spell cast on this scriptable 307 308 ieDword LastTarget, LastSpellTarget; 309 ieDword LastTargetPersistent; // gemrb extension, persists across actions; remove if LastTarget ever gets the same persistence 310 Point LastTargetPos; 311 int SpellHeader; 312 ieResRef SpellResRef; 313 bool InterruptCasting; 314 public: 315 /** Gets the Dialog ResRef */ GetDialog(void)316 const char* GetDialog(void) const 317 { 318 return Dialog; 319 } 320 void SetDialog(const char *resref); 321 void SetFloatingText(char*); 322 void SetScript(const ieResRef aScript, int idx, bool ai=false); 323 void SetSpellResRef(ieResRef resref); 324 void SetWait(unsigned long time); 325 unsigned long GetWait() const; 326 void LeftDialog(); 327 void Interrupt(); 328 void NoInterrupt(); 329 void Hide(); 330 void Unhide(); 331 void Activate(); 332 void Deactivate(); 333 void PartyRested(); 334 ieDword GetInternalFlag() const; 335 void SetInternalFlag(unsigned int value, int mode); 336 const char* GetScriptName() const; 337 Map* GetCurrentArea() const; 338 void SetMap(Map *map); 339 void SetOverheadText(const String& text, bool display = true); GetOverheadText()340 const String& GetOverheadText() const { return OverheadText; }; 341 bool DisplayOverheadText(bool); OverheadTextIsDisplaying()342 bool OverheadTextIsDisplaying() const { return overheadTextDisplaying; } 343 void FixHeadTextPos(); 344 void SetScriptName(const char* text); 345 //call this to enable script running as soon as possible 346 void ImmediateEvent(); 347 bool IsPC() const; 348 virtual void Update(); 349 void TickScripting(); 350 virtual void ExecuteScript(int scriptCount); 351 void AddAction(Action* aC); 352 void AddActionInFront(Action* aC); GetCurrentAction()353 Action* GetCurrentAction() const { return CurrentAction; } 354 Action* GetNextAction() const; 355 Action* PopNextAction(); 356 void ClearActions(); 357 virtual void Stop(); 358 virtual void ReleaseCurrentAction(); 359 bool InMove() const; 360 void ProcessActions(); 361 //these functions handle clearing of triggers that resulted a 362 //true condition (whole triggerblock returned true) 363 void ClearTriggers(); 364 void AddTrigger(TriggerEntry trigger); 365 void SetLastTrigger(ieDword triggerID, ieDword globalID); 366 bool MatchTrigger(unsigned short id, ieDword param = 0); 367 bool MatchTriggerWithObject(short unsigned int id, const Object *obj, ieDword param = 0) const; 368 const TriggerEntry *GetMatchingTrigger(unsigned short id, unsigned int notflags = 0) const; 369 void SendTriggerToAll(TriggerEntry entry); 370 /* re/draws overhead text on the map screen */ 371 void DrawOverheadText(); 372 virtual Region DrawingRegion() const; 373 /* check if casting is allowed at all */ 374 int CanCast(const ieResRef SpellRef, bool verbose = true); 375 /* check for and trigger a wild surge */ 376 int CheckWildSurge(); 377 void SpellcraftCheck(const Actor *caster, const ieResRef SpellRef); 378 /* internal spellcasting shortcuts */ 379 void DirectlyCastSpellPoint(const Point &target, ieResRef spellref, int level, int no_stance, bool deplete); 380 void DirectlyCastSpell(Scriptable *target, ieResRef spellref, int level, int no_stance, bool deplete); 381 /* actor/scriptable casts spell */ 382 int CastSpellPoint( const Point &Target, bool deplete, bool instant = false, bool nointerrupt = false ); 383 int CastSpell( Scriptable* Target, bool deplete, bool instant = false, bool nointerrupt = false ); 384 /* spellcasting finished */ 385 void CastSpellPointEnd(int level, int no_stance); 386 void CastSpellEnd(int level, int no_stance); GetGlobalID()387 ieDword GetGlobalID() const { return globalID; } 388 /** timer functions (numeric ID, not saved) */ 389 bool TimerActive(ieDword ID); 390 bool TimerExpired(ieDword ID); 391 void StartTimer(ieDword ID, ieDword expiration); GetName(int)392 virtual const char* GetName(int /*which*/) const { return NULL; } 393 bool AuraPolluted(); 394 private: 395 /* used internally to handle start of spellcasting */ 396 int SpellCast(bool instant, Scriptable *target = NULL); 397 /* also part of the spellcasting process, creating the projectile */ 398 void CreateProjectile(const ieResRef SpellResRef, ieDword tgt, int level, bool fake); 399 /* do some magic for the weird/awesome wild surges */ 400 bool HandleHardcodedSurge(ieResRef surgeSpellRef, Spell *spl, Actor *caster); 401 void ResetCastingState(Actor* caster); 402 void DisplaySpellCastMessage(ieDword tgt, Spell *spl); 403 }; 404 405 class GEM_EXPORT Selectable : public Scriptable { 406 public: 407 Selectable(ScriptableType type); 408 public: 409 ieWord Selected; //could be 0x80 for unselectable 410 bool Over; 411 Color selectedColor; 412 Color overColor; 413 Holder<Sprite2D> circleBitmap[2]; 414 int size; 415 float sizeFactor; 416 public: 417 void SetBBox(const Region &newBBox); 418 void DrawCircle(const Point& p) const; 419 bool IsOver(const Point &Pos) const; 420 void SetOver(bool over); 421 bool IsSelected() const; 422 void Select(int Value); 423 void SetCircle(int size, float, const Color &color, Holder<Sprite2D> normal_circle, Holder<Sprite2D> selected_circle); 424 }; 425 426 class GEM_EXPORT Highlightable : public Scriptable { 427 public: 428 Highlightable(ScriptableType type); 429 virtual int TrapResets() const = 0; CanDetectTrap()430 virtual bool CanDetectTrap() const { return true; } 431 virtual bool PossibleToSeeTrap() const; 432 public: 433 std::shared_ptr<Gem_Polygon> outline; 434 Color outlineColor; 435 ieDword Cursor; 436 bool Highlight; 437 Point TrapLaunch; 438 ieWord TrapDetectionDiff; 439 ieWord TrapRemovalDiff; 440 ieWord Trapped; 441 ieWord TrapDetected; 442 ieResRef KeyResRef; 443 //play this wav file when stepping on the trap (on PST) 444 ieResRef EnterWav; 445 public: 446 bool IsOver(const Point &Place) const; 447 void DrawOutline(Point origin) const; 448 void SetCursor(unsigned char CursorIndex); GetKey(void)449 const char* GetKey(void) const 450 { 451 if (KeyResRef[0]) return KeyResRef; 452 return NULL; 453 } 454 void SetTrapDetected(int x); 455 void TryDisarm(const Actor *actor); 456 //detect trap, set skill to 256 if you want sure fire 457 void DetectTrap(int skill, ieDword actorID); 458 //returns true if trap is visible, only_detected must be true 459 //if you want to see discovered traps, false is for cheats 460 bool VisibleTrap(int only_detected) const; 461 //returns true if trap has been triggered, tumble skill??? 462 virtual bool TriggerTrap(int skill, ieDword ID); 463 bool TryUnlock(Actor *actor, bool removekey); 464 }; 465 466 class GEM_EXPORT Movable : public Selectable { 467 private: //these seem to be sensitive, so get protection 468 unsigned char StanceID; 469 unsigned char Orientation, NewOrientation; 470 ieWord AttackMovements[3]; 471 472 PathNode* path; //whole path 473 PathNode* step; //actual step 474 unsigned int prevTicks; 475 int bumpBackTries; 476 bool pathAbandoned; 477 protected: 478 ieDword timeStartStep; 479 //the # of previous tries to pick up a new walkpath 480 int pathTries; 481 int randomBackoff; 482 Point oldPos; 483 bool bumped; 484 int pathfindingDistance; 485 int randomWalkCounter; 486 public: GetRandomBackoff()487 inline int GetRandomBackoff() const 488 { 489 return randomBackoff; 490 } 491 void Backoff(); DecreaseBackoff()492 inline void DecreaseBackoff() 493 { 494 randomBackoff--; 495 } 496 Movable(ScriptableType type); 497 ~Movable(void) override; 498 Point Destination; 499 ieResRef Area; 500 Point HomeLocation;//spawnpoint, return here after rest 501 ieWord maxWalkDistance;//maximum random walk distance from home 502 public: ImpedeBumping()503 inline void ImpedeBumping() { oldPos = Pos; bumped = false; } 504 void AdjustPosition(); 505 void BumpAway(); 506 void BumpBack(); IsBumped()507 inline bool IsBumped() const { return bumped; } 508 PathNode *GetNextStep(int x) const; GetPath()509 inline PathNode *GetPath() const { return path; }; GetPathTries()510 inline int GetPathTries() const { return pathTries; } IncrementPathTries()511 inline void IncrementPathTries() { pathTries++; } ResetPathTries()512 inline void ResetPathTries() { pathTries = 0; } 513 int GetPathLength() const; 514 //inliners to protect data consistency GetStep()515 inline PathNode * GetStep() { 516 if (!step) { 517 DoStep((unsigned int) ~0); 518 } 519 return step; 520 } 521 IsMoving()522 inline bool IsMoving() const { 523 return (StanceID == IE_ANI_WALK || StanceID == IE_ANI_RUN); 524 } 525 526 unsigned char GetNextFace(); 527 GetOrientation()528 inline unsigned char GetOrientation() const { 529 return Orientation; 530 } 531 GetStance()532 inline unsigned char GetStance() const { 533 return StanceID; 534 } 535 536 void SetStance(unsigned int arg); 537 void SetOrientation(int value, bool slow); 538 void SetAttackMoveChances(ieWord *amc); 539 virtual void DoStep(unsigned int walkScale, ieDword time = 0); 540 void AddWayPoint(const Point &Des); 541 void RunAwayFrom(const Point &Des, int PathLength, bool noBackAway); 542 void RandomWalk(bool can_stop, bool run); GetRandomWalkCounter()543 int GetRandomWalkCounter() const { return randomWalkCounter; }; 544 void MoveLine(int steps, ieDword Orient); 545 void WalkTo(const Point &Des, int MinDistance = 0); 546 void MoveTo(const Point &Des); 547 void Stop() override; 548 void ClearPath(bool resetDestination = true); 549 550 /* returns the most likely position of this actor */ 551 Point GetMostLikelyPosition() const; 552 virtual bool BlocksSearchMap() const = 0; 553 }; 554 555 //Tiled objects are not used (and maybe not even implemented correctly in IE) 556 //they seem to be most closer to a door and probably obsoleted by it 557 //are they scriptable? 558 class GEM_EXPORT TileObject { 559 public: 560 TileObject(void); 561 ~TileObject(void); 562 void SetOpenTiles(unsigned short *indices, int count); 563 void SetClosedTiles(unsigned short *indices, int count); 564 565 public: 566 ieVariable Name; 567 ieResRef Tileset; //or wed door ID? 568 ieDword Flags; 569 unsigned short* opentiles; 570 ieDword opencount; 571 unsigned short* closedtiles; 572 ieDword closedcount; 573 }; 574 575 } 576 577 #endif 578