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