1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003-2005 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 "GameScript/GSUtils.h"
22 #include "GameScript/Matching.h"
25 #include "defsounds.h"
28 #include "DialogHandler.h"
29 #include "DisplayMessage.h"
32 #include "Interface.h"
36 #include "StringMgr.h"
39 #include "GUI/GameControl.h"
43 //these tables will get freed by Core
44 Holder
<SymbolMgr
> triggersTable
;
45 Holder
<SymbolMgr
> actionsTable
;
46 Holder
<SymbolMgr
> objectsTable
;
47 TriggerFunction triggers
[MAX_TRIGGERS
];
48 ActionFunction actions
[MAX_ACTIONS
];
49 short actionflags
[MAX_ACTIONS
];
50 short triggerflags
[MAX_TRIGGERS
];
51 ObjectFunction objects
[MAX_OBJECTS
];
52 IDSFunction idtargets
[MAX_OBJECT_FIELDS
];
53 Cache SrcCache
; //cache for string resources (pst)
54 Cache BcsCache
; //cache for scripts
55 int ObjectIDSCount
= 7;
56 int MaxObjectNesting
= 5;
57 bool HasAdditionalRect
= false;
58 bool HasTriggerPoint
= false;
59 //released by ReleaseMemory
60 ieResRef
*ObjectIDSTableNames
;
61 int ObjectFieldsCount
= 7;
62 int ExtraParametersCount
= 0;
68 // reaction modifiers (by reputation and charisma)
71 Gem_Polygon
**polygons
;
73 void InitScriptTables()
75 //initializing the skill->stats conversion table
77 AutoTable
tab("skillsta");
79 int rowcount
= tab
->GetRowCount();
80 SkillCount
= rowcount
;
82 SkillStats
= (int *) malloc(rowcount
* sizeof(int) );
84 SkillStats
[rowcount
]=strtol(tab
->QueryField(rowcount
,0), NULL
, 0);
89 //initializing the happiness table
91 AutoTable
tab("happy");
93 for (int alignment
=0;alignment
<3;alignment
++) {
94 for (int reputation
=0;reputation
<20;reputation
++) {
95 happiness
[alignment
][reputation
]=strtol(tab
->QueryField(reputation
,alignment
), NULL
, 0);
101 //initializing the reaction mod. reputation table
102 AutoTable
rmr("rmodrep");
104 for (int reputation
=0; reputation
<20; reputation
++) {
105 rmodrep
[reputation
] = strtol(rmr
->QueryField(0, reputation
), NULL
, 0);
109 //initializing the reaction mod. charisma table
110 AutoTable
rmc("rmodchr");
112 for (int charisma
=0; charisma
<25; charisma
++) {
113 rmodchr
[charisma
] = strtol(rmc
->QueryField(0, charisma
), NULL
, 0);
118 int GetReaction(Actor
*target
, Scriptable
*Sender
)
120 int chr
, rep
, reaction
;
121 chr
= target
->GetStat(IE_CHR
)-1;
122 if (target
->GetStat(IE_EA
) == EA_PC
) {
123 rep
= core
->GetGame()->Reputation
/10;
125 rep
= target
->GetStat(IE_REPUTATION
);
127 reaction
= 10 + rmodrep
[rep
] + rmodchr
[chr
];
129 // add -4 penalty when dealing with racial enemies
130 if (Sender
&& target
->GetRangerLevel() && Sender
->Type
== ST_ACTOR
&& target
->IsRacialEnemy((Actor
*)Sender
)) {
137 int GetHappiness(Scriptable
* Sender
, int reputation
)
139 if (Sender
->Type
!= ST_ACTOR
) {
142 Actor
* ab
= ( Actor
* ) Sender
;
143 int alignment
= ab
->GetStat(IE_ALIGNMENT
)&AL_GE_MASK
; //good / evil
144 if (reputation
> 200) {
147 return happiness
[alignment
][reputation
/10-1];
150 int GetHPPercent(Scriptable
* Sender
)
152 if (Sender
->Type
!= ST_ACTOR
) {
155 Actor
* ab
= ( Actor
* ) Sender
;
156 int hp1
= ab
->GetStat(IE_MAXHITPOINTS
);
160 int hp2
= ab
->GetBase(IE_HITPOINTS
);
167 void HandleBitMod(ieDword
&value1
, ieDword value2
, int opcode
)
171 value1
= ( value1
& value2
);
174 value1
= ( value1
| value2
);
177 value1
= ( value1
^ value2
);
179 case BM_NAND
: //this is a GemRB extension
180 value1
= ( value1
& ~value2
);
182 case BM_SET
: //this is a GemRB extension
188 // SPIT is not in the original engine spec, it is reserved for the
189 // enchantable items feature
191 static const char *spell_suffices
[]={"SPIT","SPPR","SPWI","SPIN","SPCL"};
193 //this function handles the polymorphism of Spell[RES] actions
194 //it returns spellres
195 bool ResolveSpellName(ieResRef spellres
, Action
*parameters
)
197 if (parameters
->string0Parameter
[0]) {
198 strnlwrcpy(spellres
, parameters
->string0Parameter
, 8);
201 int type
= parameters
->int0Parameter
/1000;
202 int spellid
= parameters
->int0Parameter
%1000;
206 sprintf(spellres
, "%s%03d", spell_suffices
[type
], spellid
);
208 return gamedata
->Exists(spellres
, IE_SPL_CLASS_ID
);
211 void ResolveSpellName(ieResRef spellres
, ieDword number
)
214 unsigned int type
= number
/1000;
215 int spellid
= number
%1000;
219 sprintf(spellres
, "%s%03d", spell_suffices
[type
], spellid
);
222 ieDword
ResolveSpellNumber(const ieResRef spellres
)
227 if(!strnicmp(spellres
, spell_suffices
[i
], 4)) {
229 sscanf(spellres
+4,"%d", &n
);
239 bool ResolveItemName(ieResRef itemres
, Actor
*act
, ieDword Slot
)
241 CREItem
*itm
= act
->inventory
.GetSlotItem(Slot
);
243 strnlwrcpy(itemres
, itm
->ItemResRef
, 8);
244 return gamedata
->Exists(itemres
, IE_ITM_CLASS_ID
);
249 bool StoreHasItemCore(const ieResRef storename
, const ieResRef itemname
)
251 bool had_nostore
=false;
252 bool has_current
=false;
257 Store
*store
= core
->GetCurrentStore();
260 store
= core
->SetCurrentStore(storename
, NULL
);
262 if (strnicmp(store
->Name
, storename
, 8) ) {
263 //not the current store, we need some dirty hack
265 strnlwrcpy(current
, store
->Name
, 8);
266 strnuprcpy(owner
, store
->GetOwner(), 32);
270 printMessage("GameScript","Store cannot be opened!\n", LIGHT_RED
);
275 //don't use triggers (pst style), it would be possible to create infinite loops
276 if (store
->FindItem(itemname
, false) != (unsigned int)-1) {
280 //setting back old store (this will save our current store)
281 core
->SetCurrentStore(current
, owner
);
282 } else if (had_nostore
) {
283 core
->CloseCurrentStore();
288 //don't pass this point by reference, it is subject to change
289 void ClickCore(Scriptable
*Sender
, Point point
, int type
, int speed
)
291 Map
*map
= Sender
->GetCurrentArea();
293 Sender
->ReleaseCurrentAction();
296 Point p
=map
->TMap
->GetMapSize();
297 if (!p
.PointInside(point
)) {
298 Sender
->ReleaseCurrentAction();
301 Video
*video
= core
->GetVideoDriver();
302 GlobalTimer
*timer
= core
->timer
;
303 timer
->SetMoveViewPort( point
.x
, point
.y
, speed
, true );
305 if (timer
->ViewportIsMoving()) {
306 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
308 Sender
->ReleaseCurrentAction();
312 video
->ConvertToScreen(point
.x
, point
.y
);
313 GameControl
*win
= core
->GetGameControl();
317 video
->MoveMouse(point
.x
, point
.y
);
318 video
->ClickMouse(type
);
319 Sender
->ReleaseCurrentAction();
322 void TransformItemCore(Actor
*actor
, Action
*parameters
, bool onlyone
)
324 int i
= actor
->inventory
.GetSlotCount();
326 CREItem
*item
= actor
->inventory
.GetSlotItem(i
);
330 if (strnicmp(item
->ItemResRef
, parameters
->string0Parameter
, 8) ) {
333 actor
->inventory
.SetSlotItemRes(parameters
->string1Parameter
,i
,parameters
->int0Parameter
,parameters
->int1Parameter
,parameters
->int2Parameter
);
340 //check if an inventory (container or actor) has item (could be recursive ?)
341 bool HasItemCore(Inventory
*inventory
, const ieResRef itemname
, ieDword flags
)
343 if (inventory
->HasItem(itemname
, flags
)) {
346 int i
=inventory
->GetSlotCount();
348 //maybe we could speed this up if we mark bag items with a flags bit
349 CREItem
*itemslot
= inventory
->GetSlotItem(i
);
352 Item
*item
= gamedata
->GetItem(itemslot
->ItemResRef
);
356 if (core
->CanUseItemType(SLOT_BAG
,item
,NULL
) ) {
357 //the store is the same as the item's name
358 ret
= StoreHasItemCore(itemslot
->ItemResRef
, itemname
);
360 gamedata
->FreeItem(item
, itemslot
->ItemResRef
);
368 void DisplayStringCore(Scriptable
* Sender
, int Strref
, int flags
)
371 char Sound
[_MAX_PATH
];
373 //no one hears you when you are in the Limbo!
374 if (!Sender
->GetCurrentArea()) {
378 memset(&sb
,0,sizeof(sb
));
380 printf( "Displaying string on: %s\n", Sender
->GetScriptName() );
381 if (flags
& DS_CONST
) {
382 if (Sender
->Type
!=ST_ACTOR
) {
383 printMessage("GameScript","Verbal constant not supported for non actors!\n", LIGHT_RED
);
386 Actor
* actor
= ( Actor
* ) Sender
;
387 if ((ieDword
) Strref
>=VCONST_COUNT
) {
388 printMessage("GameScript","Invalid verbal constant!\n", LIGHT_RED
);
392 int tmp
=(int) actor
->GetVerbalConstant(Strref
);
393 if (tmp
<= 0 || (actor
->GetStat(IE_MC_FLAGS
) & MC_EXPORTABLE
)) {
394 //get soundset based string constant
395 actor
->ResolveStringConstant( sb
.Sound
, (unsigned int) Strref
);
396 if (actor
->PCStats
&& actor
->PCStats
->SoundFolder
[0]) {
397 snprintf(Sound
, _MAX_PATH
, "%s/%s",
398 actor
->PCStats
->SoundFolder
, sb
.Sound
);
400 memcpy(Sound
, sb
.Sound
, sizeof(ieResRef
) );
405 //display the verbal constants in the console
406 ieDword charactersubtitles
= 0;
407 core
->GetDictionary()->Lookup("Subtitles", charactersubtitles
);
408 if (charactersubtitles
) {
413 if ((Strref
!= -1) && !sb
.Sound
[0]) {
414 sb
= core
->strings
->GetStringBlock( Strref
);
415 memcpy(Sound
, sb
.Sound
, sizeof(ieResRef
) );
416 if (sb
.text
[0] && strcmp(sb
.text
," ") && (flags
& DS_CONSOLE
)) {
417 //can't play the sound here, we have to delay action
418 //and for that, we have to know how long the text takes
419 if(flags
&DS_NONAME
) {
420 displaymsg
->DisplayString( sb
.text
);
422 displaymsg
->DisplayStringName( Strref
, 0xf0f0f0, Sender
, 0);
425 if (sb
.text
[0] && strcmp(sb
.text
," ") && (flags
& (DS_HEAD
| DS_AREA
))) {
426 Sender
->DisplayHeadText( sb
.text
);
427 //don't free sb.text, it is residing in Sender
428 if (flags
& DS_AREA
) {
429 Sender
->FixHeadTextPos();
432 core
->FreeString( sb
.text
);
435 if (Sound
[0] && !(flags
&DS_SILENT
) ) {
436 ieDword speech
= GEM_SND_RELATIVE
; //disable position
437 if (flags
&DS_SPEECH
) speech
|=GEM_SND_SPEECH
;
438 ieDword len
= core
->GetAudioDrv()->Play( Sound
,0,0,speech
);
439 ieDword counter
= ( AI_UPDATE_TIME
* len
) / 1000;
440 if ((counter
!= 0) && (flags
&DS_WAIT
) )
441 Sender
->SetWait( counter
);
445 int CanSee(Scriptable
* Sender
, Scriptable
* target
, bool range
, int seeflag
)
449 if (target
->Type
==ST_ACTOR
) {
450 Actor
*tar
= (Actor
*) target
;
452 if (!tar
->ValidTarget(seeflag
)) {
457 map
= target
->GetCurrentArea();
458 //if (!(seeflag&GA_GLOBAL)) {
459 if ( map
!=Sender
->GetCurrentArea() ) {
467 if (Sender
->Type
== ST_ACTOR
) {
468 Actor
* snd
= ( Actor
* ) Sender
;
469 dist
= snd
->Modified
[IE_VISUALRANGE
];
474 if (Distance(target
->Pos
, Sender
->Pos
) > dist
* 15) {
479 return map
->IsVisible(target
->Pos
, Sender
->Pos
);
482 //non actors can see too (reducing function to LOS)
483 //non actors can be seen too (reducing function to LOS)
484 int SeeCore(Scriptable
* Sender
, Trigger
* parameters
, int justlos
)
489 if (parameters
->int0Parameter
) {
494 Scriptable
* tar
= GetActorFromObject( Sender
, parameters
->objectParameter
, flags
);
495 /* don't set LastSeen if this isn't an actor */
500 if (CanSee(Sender
, tar
, true, flags
) ) {
504 if (Sender
->Type
==ST_ACTOR
&& tar
->Type
==ST_ACTOR
) {
505 Actor
* snd
= ( Actor
* ) Sender
;
506 //additional checks for invisibility?
507 snd
->LastSeen
= ((Actor
*) tar
)->GetID();
514 //transfering item from Sender to target
515 //if target has no inventory, the item will be destructed
516 //if target can't get it, it will be dropped at its feet
517 int MoveItemCore(Scriptable
*Sender
, Scriptable
*target
, const char *resref
, int flags
, int setflag
)
521 // track whether we are dealing with our party and need to display feedback
522 bool lostitem
= false;
523 bool gotitem
= false;
528 map
=Sender
->GetCurrentArea();
529 switch(Sender
->Type
) {
531 myinv
=&((Actor
*) Sender
)->inventory
;
532 if (((Actor
*)Sender
)->InParty
) lostitem
= true;
535 myinv
=&((Container
*) Sender
)->inventory
;
541 myinv
->RemoveItem(resref
, flags
, &item
);
543 // nothing was removed
547 item
->Flags
|=setflag
;
549 switch(target
->Type
) {
551 myinv
=&((Actor
*) target
)->inventory
;
552 if (((Actor
*) target
)->InParty
) gotitem
= true;
555 myinv
=&((Container
*) target
)->inventory
;
563 if (lostitem
) displaymsg
->DisplayConstantString(STR_LOSTITEM
, 0xbcefbc);
564 return MIC_GOTITEM
; // actually it was lost, not gained
566 if ( myinv
->AddSlotItem(item
, SLOT_ONLYINVENTORY
) !=ASI_SUCCESS
) {
567 // drop it at my feet
568 map
->AddItemToLocation(target
->Pos
, item
);
569 if (gotitem
) displaymsg
->DisplayConstantString(STR_INVFULL_ITEMDROP
, 0xbcefbc);
572 if (gotitem
) displaymsg
->DisplayConstantString(STR_GOTITEM
, 0xbcefbc);
576 /*FIXME: what is 'base'*/
577 void PolymorphCopyCore(Actor
*src
, Actor
*tar
, bool base
)
579 tar
->SetBase(IE_ANIMATION_ID
, src
->GetStat(IE_ANIMATION_ID
) );
581 tar
->SetBase(IE_ARMOR_TYPE
, src
->GetStat(IE_ARMOR_TYPE
) );
582 for (int i
=0;i
<7;i
++) {
583 tar
->SetBase(IE_COLORS
+i
, src
->GetStat(IE_COLORS
+i
) );
586 tar
->SetName(src
->GetName(0),0);
587 tar
->SetName(src
->GetName(1),1);
588 //add more attribute copying
591 void CreateCreatureCore(Scriptable
* Sender
, Action
* parameters
, int flags
)
593 Scriptable
*tmp
= GetActorFromObject( Sender
, parameters
->objects
[1] );
594 //if there is nothing to copy, don't spawn anything
595 if (flags
& CC_COPY
) {
596 if (!tmp
|| tmp
->Type
!= ST_ACTOR
) {
602 if (flags
& CC_STRING1
) {
603 ab
= gamedata
->GetCreature(parameters
->string1Parameter
);
606 ab
= gamedata
->GetCreature(parameters
->string0Parameter
);
610 printMessage("GameScript","Failed to create creature! ",LIGHT_RED
);
611 printf("(missing creature file %s?)\n", parameters
->string0Parameter
);
612 // maybe this should abort()?
616 //iwd2 allows an optional scriptname to be set
617 //but bg2 doesn't have this feature
618 //this way it works for both games
619 if ((flags
& CC_SCRIPTNAME
) && parameters
->string1Parameter
[0]) {
620 ab
->SetScriptName(parameters
->string1Parameter
);
627 switch (flags
& CC_MASK
) {
628 //creates creature just off the screen
631 Region vp
= core
->GetVideoDriver()->GetViewport();
632 radius
=vp
.w
/2; //actually it must be further divided by the tile size, hmm 16?
635 case CC_OBJECT
://use object + offset
638 case CC_OFFSET
://use sender + offset
639 pnt
.x
= parameters
->pointParameter
.x
+Sender
->Pos
.x
;
640 pnt
.y
= parameters
->pointParameter
.y
+Sender
->Pos
.y
;
642 default: //absolute point, but -1,-1 means AtFeet
643 pnt
.x
= parameters
->pointParameter
.x
;
644 pnt
.y
= parameters
->pointParameter
.y
;
646 pnt
.x
= Sender
->Pos
.x
;
647 pnt
.y
= Sender
->Pos
.y
;
652 Map
*map
= Sender
->GetCurrentArea();
654 ab
->SetPosition( pnt
, flags
&CC_CHECK_IMPASSABLE
, radius
);
655 ab
->SetOrientation(parameters
->int0Parameter
, false );
657 //if string1 is animation, then we can't use it for a DV too
658 if (flags
& CC_PLAY_ANIM
) {
659 CreateVisualEffectCore( ab
, ab
->Pos
, parameters
->string1Parameter
, 1);
661 //setting the deathvariable if it exists (iwd2)
662 if (parameters
->string1Parameter
[0]) {
663 ab
->SetScriptName(parameters
->string1Parameter
);
667 if (flags
& CC_COPY
) {
668 PolymorphCopyCore ( (Actor
*) tmp
, ab
, false);
672 static ScriptedAnimation
*GetVVCEffect(const char *effect
, int iterations
)
675 ScriptedAnimation
* vvc
= gamedata
->GetScriptedAnimation(effect
, false);
677 printMessage("GameScript","Failed to create effect.",LIGHT_RED
);
681 vvc
->SetDefaultDuration( vvc
->GetSequenceDuration(AI_UPDATE_TIME
* iterations
));
690 void CreateVisualEffectCore(Actor
*target
, const char *effect
, int iterations
)
692 ScriptedAnimation
*vvc
= GetVVCEffect(effect
, iterations
);
694 target
->AddVVCell( vvc
);
698 void CreateVisualEffectCore(Scriptable
*Sender
, const Point
&position
, const char *effect
, int iterations
)
700 ScriptedAnimation
*vvc
= GetVVCEffect(effect
, iterations
);
702 vvc
->XPos
+=position
.x
;
703 vvc
->YPos
+=position
.y
;
704 Sender
->GetCurrentArea( )->AddVVCell( vvc
);
708 //this destroys the current actor and replaces it with another
709 void ChangeAnimationCore(Actor
*src
, const char *resref
, bool effect
)
711 Actor
*tar
= gamedata
->GetCreature(resref
);
713 Map
*map
= src
->GetCurrentArea();
714 map
->AddActor( tar
);
715 Point pos
= src
->Pos
;
716 tar
->SetOrientation(src
->GetOrientation(), false );
718 // can't SetPosition while the old actor is taking the spot
719 tar
->SetPosition(pos
, 1);
721 CreateVisualEffectCore(tar
, tar
->Pos
,"smokepuffeffect",1);
726 void EscapeAreaCore(Scriptable
* Sender
, const Point
&p
, const char* area
, const Point
&enter
, int flags
, int wait
)
730 if ( !p
.isempty() && PersonalDistance(p
, Sender
)>MAX_OPERATING_DISTANCE
) {
731 //MoveNearerTo will return 0, if the actor is in move
732 //it will return 1 (the fourth parameter) if the target is unreachable
733 if (!MoveNearerTo(Sender
, p
, MAX_OPERATING_DISTANCE
,1) ) {
734 if (!Sender
->InMove()) printf("At least it said so...\n");
739 if (flags
&EA_DESTROY
) {
740 //this must be put into a non-const variable
741 sprintf( Tmp
, "DestroySelf()" );
743 // last parameter is 'face', which should be passed from relevant action parameter..
744 sprintf( Tmp
, "MoveBetweenAreas(\"%s\",[%hd.%hd],%d)", area
, enter
.x
, enter
.y
, 0 );
746 printMessage("GSUtils"," ", WHITE
);
747 printf("Executing %s in EscapeAreaCore\n", Tmp
);
748 //drop this action, but add another (destroyself or movebetweenareas)
749 //between the arrival and the final escape, there should be a wait time
750 //that wait time could be handled here
752 printf("But wait a bit... (%d)\n", wait
);
753 Sender
->SetWait(wait
);
755 Sender
->ReleaseCurrentAction();
756 Action
* action
= GenerateAction( Tmp
);
757 Sender
->AddActionInFront( action
);
760 void GetTalkPositionFromScriptable(Scriptable
* scr
, Point
&position
)
763 case ST_AREA
: case ST_GLOBAL
:
764 position
= scr
->Pos
; //fake
767 //if there are other moveables, put them here
768 position
= ((Movable
*) scr
)->GetMostLikelyPosition();
770 case ST_TRIGGER
: case ST_PROXIMITY
: case ST_TRAVEL
:
771 if (((InfoPoint
*) scr
)->Flags
& TRAP_USEPOINT
) {
772 position
=((InfoPoint
*) scr
)->UsePoint
;
775 position
=((InfoPoint
*) scr
)->TrapLaunch
;
777 case ST_DOOR
: case ST_CONTAINER
:
778 position
=((Highlightable
*) scr
)->TrapLaunch
;
783 void GetPositionFromScriptable(Scriptable
* scr
, Point
&position
, bool dest
)
790 case ST_AREA
: case ST_GLOBAL
:
791 position
= scr
->Pos
; //fake
794 //if there are other moveables, put them here
795 position
= ((Movable
*) scr
)->GetMostLikelyPosition();
797 case ST_TRIGGER
: case ST_PROXIMITY
: case ST_TRAVEL
:
798 if (((InfoPoint
*) scr
)->Flags
& TRAP_USEPOINT
) {
799 position
=((InfoPoint
*) scr
)->UsePoint
;
802 case ST_DOOR
: case ST_CONTAINER
:
803 position
=((Highlightable
*) scr
)->TrapLaunch
;
807 int CheckInteract(const char *talker
, const char *target
)
809 AutoTable
interact("interact");
812 const char *value
= interact
->QueryField(talker
, target
);
826 static ieResRef PlayerDialogRes
= "PLAYERx\0";
828 void BeginDialog(Scriptable
* Sender
, Action
* parameters
, int Flags
)
830 Scriptable
* tar
, *scr
;
831 int seeflag
= GA_NO_DEAD
;
833 if (InDebug
&ID_VARIABLES
) {
834 printf("BeginDialog core\n");
836 if (Flags
& BD_OWN
) {
837 tar
= GetStoredActorFromObject( Sender
, parameters
->objects
[1], seeflag
);
840 tar
= GetStoredActorFromObject( Sender
, parameters
->objects
[1], seeflag
);
844 printMessage("GameScript"," ",LIGHT_RED
);
845 printf("Speaker for dialog couldn't be found (Sender: %s, Type: %d) Flags:%d.\n", Sender
->GetScriptName(), Sender
->Type
, Flags
);
846 Sender
->ReleaseCurrentAction();
850 if (!tar
|| tar
->Type
!=ST_ACTOR
) {
851 printMessage("GameScript"," ",LIGHT_RED
);
852 printf("Target for dialog couldn't be found (Sender: %s, Type: %d).\n", Sender
->GetScriptName(), Sender
->Type
);
853 if (Sender
->Type
== ST_ACTOR
) {
854 ((Actor
*) Sender
)->DebugDump();
856 printf ("Target object: ");
857 if (parameters
->objects
[1]) {
858 parameters
->objects
[1]->Dump();
862 Sender
->ReleaseCurrentAction();
866 Actor
*speaker
, *target
;
869 target
= (Actor
*) tar
;
870 if ((Flags
& BD_CHECKDIST
) && !CanSee(scr
, target
, false, seeflag
) ) {
871 printMessage("GameScript"," ",LIGHT_RED
);
872 printf("CanSee returned false! Speaker (%s, type %d) and target are:\n", scr
->GetScriptName(), scr
->Type
);
873 if (scr
->Type
== ST_ACTOR
) {
874 ((Actor
*) scr
)->DebugDump();
876 ((Actor
*) tar
)->DebugDump();
877 Sender
->ReleaseCurrentAction();
881 if (scr
->Type
==ST_ACTOR
) {
882 speaker
= (Actor
*) scr
;
883 if (speaker
->GetStat(IE_STATE_ID
)&STATE_DEAD
) {
884 printMessage("GameScript"," ",LIGHT_RED
);
885 printf("Speaker is dead, cannot start dialogue. Speaker and target are:\n");
886 speaker
->DebugDump();
888 Sender
->ReleaseCurrentAction();
891 ieDword range
= MAX_OPERATING_DISTANCE
;
892 //making sure speaker is the protagonist, player, actor
893 if ( target
->InParty
== 1) swap
= true;
894 else if ( speaker
->InParty
!=1 && target
->InParty
) swap
= true;
895 //CHECKDIST works only for mobile scriptables
896 if (Flags
&BD_CHECKDIST
) {
897 if ( scr
->GetCurrentArea()!=target
->GetCurrentArea() ||
898 PersonalDistance(scr
, target
)>range
) {
899 MoveNearerTo(Sender
, target
, MAX_OPERATING_DISTANCE
);
904 //pst style dialog with trigger points
906 if (Flags
&BD_CHECKDIST
) {
909 if (target
->InMove()) {
911 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
912 Sender
->ReleaseCurrentAction();
916 GetTalkPositionFromScriptable(scr
, TalkPos
);
917 if (PersonalDistance(TalkPos
, target
)>MAX_OPERATING_DISTANCE
) {
918 //try to force the target to come closer???
919 GoNear(target
, TalkPos
);
920 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
921 Sender
->ReleaseCurrentAction();
928 GameControl
* gc
= core
->GetGameControl();
930 printMessage( "GameScript","Dialog cannot be initiated because there is no GameControl.", YELLOW
);
931 Sender
->ReleaseCurrentAction();
934 //can't initiate dialog, because it is already there
935 if (gc
->GetDialogueFlags()&DF_IN_DIALOG
) {
936 if (Flags
& BD_INTERRUPT
) {
937 //break the current dialog if possible
938 gc
->dialoghandler
->EndDialog(true);
940 //check if we could manage to break it, not all dialogs are breakable!
941 if (gc
->GetDialogueFlags()&DF_IN_DIALOG
) {
942 printMessage( "GameScript","Dialog cannot be initiated because there is already one.", YELLOW
);
943 Sender
->ReleaseCurrentAction();
948 // starting a dialog ends cutscenes!
949 core
->SetCutSceneMode(false);
951 const char* Dialog
= NULL
;
954 switch (Flags
& BD_LOCMASK
) {
956 Dialog
= parameters
->string0Parameter
;
957 if (Flags
& BD_SETDIALOG
) {
958 scr
->SetDialog( Dialog
);
963 if (swap
) Dialog
= scr
->GetDialog();
964 else Dialog
= target
->GetDialog(GD_FEEDBACK
);
967 //what if playerdialog was initiated from Player2?
968 PlayerDialogRes
[5] = '1';
969 Dialog
= ( const char * ) PlayerDialogRes
;
971 case BD_INTERACT
: //using the source for the dialog
972 const char* scriptingname
= scr
->GetScriptName();
974 /* use interact.2da for short, inlined dialogue */
975 int type
= CheckInteract(scriptingname
, target
->GetScriptName());
977 speaker
->Interact(type
);
978 target
->Response(type
);
979 Sender
->ReleaseCurrentAction();
982 /* banter dialogue */
983 pdtable
.load("interdia");
984 //Dialog is a borrowed reference, we cannot free pdtable while it is being used
986 Dialog
= pdtable
->QueryField( scriptingname
, "FILE" );
992 //dialog is not meaningful
993 if (!Dialog
|| Dialog
[0]=='*') {
994 Sender
->ReleaseCurrentAction();
998 //maybe we should remove the action queue, but i'm unsure
999 //no, we shouldn't even call this!
1000 //Sender->ReleaseCurrentAction();
1002 // moved this here from InitDialog, because InitDialog doesn't know which side is which
1003 // post-swap (and non-actors always have IF_NOINT set) .. also added a check that it's
1004 // actually busy doing something, for the same reason
1005 if (target
->GetInternalFlag()&IF_NOINT
&& (target
->GetCurrentAction() || target
->GetNextAction())) {
1006 displaymsg
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
1007 Sender
->ReleaseCurrentAction();
1011 if (speaker
!=target
) {
1013 Scriptable
*tmp
= tar
;
1017 if (!(Flags
& BD_INTERRUPT
)) {
1018 // added CurrentAction as part of blocking action fixes
1019 if (tar
->GetCurrentAction() || tar
->GetNextAction()) {
1020 displaymsg
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
1021 Sender
->ReleaseCurrentAction();
1028 //don't clear target's actions, because a sequence like this will be broken:
1029 //StartDialog([PC]); SetGlobal("Talked","LOCALS",1);
1031 if (scr
->Type
==ST_ACTOR
) {
1032 ((Actor
*) scr
)->SetOrientation(GetOrient( tar
->Pos
, scr
->Pos
), true);
1034 if (tar
->Type
==ST_ACTOR
) {
1035 ((Actor
*) tar
)->SetOrientation(GetOrient( scr
->Pos
, tar
->Pos
), true);
1042 //increasing NumTimesTalkedTo or NumTimesInteracted
1043 if (Flags
& BD_TALKCOUNT
) {
1044 gc
->SetDialogueFlags(DF_TALKCOUNT
, BM_OR
);
1045 } else if ((Flags
& BD_LOCMASK
) == BD_INTERACT
) {
1046 gc
->SetDialogueFlags(DF_INTERACT
, BM_OR
);
1049 core
->GetDictionary()->SetAt("DialogChoose",(ieDword
) -1);
1050 ret
= gc
->dialoghandler
->InitDialog( scr
, tar
, Dialog
);
1057 Sender
->ReleaseCurrentAction();
1058 if (Flags
& BD_NOEMPTY
) {
1061 displaymsg
->DisplayConstantStringName(STR_NOTHINGTOSAY
,0xff0000,tar
);
1065 //this is a bit fishy
1067 Sender
->ReleaseCurrentAction();
1071 void MoveBetweenAreasCore(Actor
* actor
, const char *area
, const Point
&position
, int face
, bool adjust
)
1073 printMessage("GameScript", " ", WHITE
);
1074 printf("MoveBetweenAreas: %s to %s [%d.%d] face: %d\n", actor
->GetName(0), area
,position
.x
,position
.y
, face
);
1076 Game
* game
= core
->GetGame();
1077 if (area
[0]) { //do we need to switch area?
1078 Map
* map1
= actor
->GetCurrentArea();
1079 //we have to change the pathfinder
1080 //to the target area if adjust==true
1081 map2
= game
->GetMap(area
, false);
1084 map1
->RemoveActor( actor
);
1086 map2
->AddActor( actor
);
1089 actor
->SetPosition(position
, adjust
);
1091 actor
->SetOrientation( face
, false );
1093 // should this perhaps be a 'selected' check or similar instead?
1094 if (actor
->InParty
) {
1095 GameControl
*gc
=core
->GetGameControl();
1096 gc
->SetScreenFlags(SF_CENTERONACTOR
,BM_OR
);
1097 game
->ChangeSong(false, true);
1101 //repeat movement, until goal isn't reached
1102 //if int0parameter is !=0, then it will try only x times
1103 void MoveToObjectCore(Scriptable
*Sender
, Action
*parameters
, ieDword flags
, bool untilsee
)
1105 if (Sender
->Type
!= ST_ACTOR
) {
1106 Sender
->ReleaseCurrentAction();
1109 Scriptable
* target
= GetStoredActorFromObject( Sender
, parameters
->objects
[1] );
1111 Sender
->ReleaseCurrentAction();
1114 Actor
* actor
= ( Actor
* ) Sender
;
1115 if (untilsee
&& CanSee(actor
, target
, true, 0) ) {
1116 Sender
->ReleaseCurrentAction();
1119 if (PersonalDistance(actor
, target
)<MAX_OPERATING_DISTANCE
) {
1120 Sender
->ReleaseCurrentAction();
1124 if (!actor
->InMove() || actor
->Destination
!= target
->Pos
) {
1125 actor
->WalkTo( target
->Pos
, flags
, 0 );
1127 //hopefully this hack will prevent lockups
1128 if (!actor
->InMove()) {
1129 Sender
->ReleaseCurrentAction();
1133 //repeat movement...
1134 Action
*newaction
= ParamCopyNoOverride(parameters
);
1135 if (newaction
->int0Parameter
!=1) {
1136 if (newaction
->int0Parameter
) {
1137 newaction
->int0Parameter
--;
1139 actor
->AddActionInFront(newaction
);
1143 Sender
->ReleaseCurrentAction();
1146 void CreateItemCore(CREItem
*item
, const char *resref
, int a
, int b
, int c
)
1148 strncpy(item
->ItemResRef
, resref
, 8);
1149 core
->ResolveRandomItem(item
);
1151 Item
*origitem
= gamedata
->GetItem(resref
);
1152 for(int i
=0;i
<3;i
++) {
1153 ITMExtHeader
*e
= origitem
->GetExtHeader(i
);
1154 item
->Usages
[i
]=e
?e
->Charges
:0;
1156 gamedata
->FreeItem(origitem
, resref
, false);
1158 item
->Usages
[0]=(ieWord
) a
;
1159 item
->Usages
[1]=(ieWord
) b
;
1160 item
->Usages
[2]=(ieWord
) c
;
1165 //It is possible to attack CONTAINERS/DOORS as well!!!
1166 void AttackCore(Scriptable
*Sender
, Scriptable
*target
, int flags
)
1168 //this is a dangerous cast, make sure actor is Actor * !!!
1169 Actor
*actor
= (Actor
*) Sender
;
1172 ITMExtHeader
*header
= NULL
;
1173 ITMExtHeader
*hittingheader
= NULL
;
1176 int DamageBonus
, CriticalBonus
;
1179 //bool leftorright = (bool) ((attacksperround-attackcount)&1);
1180 bool leftorright
= false;
1182 //will return false on any errors (eg, unusable weapon)
1183 if (!actor
->GetCombatDetails(tohit
, leftorright
, wi
, header
, hittingheader
, Flags
, DamageBonus
, speed
, CriticalBonus
, style
)) {
1184 Sender
->ReleaseCurrentAction();
1188 if (header
) wi
.range
*= 10;
1191 if ( target
->Type
== ST_DOOR
|| target
->Type
== ST_CONTAINER
) {
1195 ieDword targetID
= 0;
1196 if (target
->Type
==ST_ACTOR
) {
1197 tar
= (Actor
*) target
;
1198 targetID
= tar
->GetID();
1201 Sender
->ReleaseCurrentAction();
1204 if (!(flags
&AC_NO_SOUND
) ) {
1205 if (actor
->LastTarget
!= targetID
) {
1206 //play attack sound for party members
1207 if (actor
->InParty
) {
1208 //pick from all 5 possible verbal constants
1209 actor
->VerbalConstant(VB_ATTACK
, 5);
1210 //DisplayStringCore(Sender, VB_ATTACK, DS_CONSOLE|DS_CONST );
1212 //display attack message
1213 displaymsg
->DisplayConstantStringAction(STR_ACTION_ATTACK
,0xf0f0f0, Sender
, target
);
1217 if(target
->Type
== ST_ACTOR
) {
1218 actor
->SetTarget( target
);
1220 if ( Sender
->GetCurrentArea()!=target
->GetCurrentArea() ||
1221 (PersonalDistance(Sender
, target
) > wi
.range
) ) {
1222 MoveNearerTo(Sender
, target
, wi
.range
);
1224 } else if (target
->Type
== ST_DOOR
) {
1225 //Forcing a lock does not launch the trap...
1226 Door
* door
= (Door
*) target
;
1227 if(door
->Flags
& DOOR_LOCKED
) {
1228 door
->TryBashLock(actor
);
1230 Sender
->ReleaseCurrentAction();
1232 } else if (target
->Type
== ST_CONTAINER
) {
1233 Container
* cont
= (Container
*) target
;
1234 if(cont
->Flags
& CONT_LOCKED
) {
1235 cont
->TryBashLock(actor
);
1237 Sender
->ReleaseCurrentAction();
1241 actor
->PerformAttack(core
->GetGame()->GameTime
);
1244 //we need this because some special characters like _ or * are also accepted
1245 inline bool ismysymbol(const char letter
)
1247 if (letter
==']') return false;
1248 if (letter
=='[') return false;
1249 if (letter
==')') return false;
1250 if (letter
=='(') return false;
1251 if (letter
=='.') return false;
1252 if (letter
==',') return false;
1256 //this function returns a value, symbol could be a numeric string or
1257 //a symbol from idsname
1258 static int GetIdsValue(const char *&symbol
, const char *idsname
)
1260 int idsfile
=core
->LoadSymbol(idsname
);
1261 Holder
<SymbolMgr
> valHook
= core
->GetSymbol(idsfile
);
1263 //FIXME:missing ids file!!!
1264 if (InDebug
&ID_TRIGGERS
) {
1265 printMessage("GameScript"," ",LIGHT_RED
);
1266 printf("Missing IDS file %s for symbol %s!\n",idsname
, symbol
);
1271 int value
=strtol(symbol
, &newsymbol
, 0);
1272 if (symbol
!=newsymbol
) {
1276 char symbolname
[64];
1278 for (x
=0;ismysymbol(*symbol
) && x
<(int) sizeof(symbolname
)-1;x
++) {
1279 symbolname
[x
]=*symbol
;
1283 return valHook
->GetValue(symbolname
);
1286 static void ParseIdsTarget(const char *&src
, Object
*&object
)
1288 for (int i
=0;i
<ObjectFieldsCount
;i
++) {
1289 object
->objectFields
[i
]=GetIdsValue(src
, ObjectIDSTableNames
[i
]);
1298 //this will skip to the next element in the prototype of an action/trigger
1299 #define SKIP_ARGUMENT() while (*str && ( *str != ',' ) && ( *str != ')' )) str++
1301 static void ParseObject(const char *&str
,const char *&src
, Object
*&object
)
1304 object
= new Object();
1310 for (i
=0;i
<(int) sizeof(object
->objectName
)-1 && *src
&& *src
!='"';i
++)
1312 object
->objectName
[i
] = *src
;
1315 object
->objectName
[i
] = 0;
1320 ParseIdsTarget(src
, object
);
1322 default: //nested object filters
1325 while (Nesting
<MaxObjectNesting
) {
1326 memmove(object
->objectFilters
+1, object
->objectFilters
, (int) sizeof(int) *(MaxObjectNesting
-1) );
1327 object
->objectFilters
[0]=GetIdsValue(src
,"object");
1338 ParseIdsTarget(src
, object
);
1340 src
+=Nesting
; //skipping )
1344 /* this function was lifted from GenerateAction, to make it clearer */
1345 Action
* GenerateActionCore(const char *src
, const char *str
, int acIndex
)
1347 Action
*newAction
= new Action(true);
1348 newAction
->actionID
= (unsigned short) actionsTable
->GetValueIndex( acIndex
);
1349 //this flag tells us to merge 2 consecutive strings together to get
1350 //a variable (context+variablename)
1351 int mergestrings
= actionflags
[newAction
->actionID
]&AF_MERGESTRINGS
;
1352 int objectCount
= ( newAction
->actionID
== 1 ) ? 0 : 1;
1353 int stringsCount
= 0;
1355 if (actionflags
[newAction
->actionID
]&AF_DIRECT
) {
1356 Object
*tmp
= new Object();
1357 tmp
->objectFields
[0] = -1;
1358 //tmp->objectFields[1] = core->GetGameControl()->targetID;
1359 newAction
->objects
[objectCount
++] = tmp
;
1361 //Here is the Action; Now we need to evaluate the parameters, if any
1362 if (*str
!=')') while (*str
) {
1363 if (*(str
+1)!=':') {
1364 printf("Warning, parser was sidetracked: %s\n",str
);
1368 printf("Invalid type: %s\n",str
);
1377 newAction
->pointParameter
.x
= (short) strtol( src
, (char **) &src
, 10 );
1379 newAction
->pointParameter
.y
= (short) strtol( src
, (char **) &src
, 10 );
1385 //going to the variable name
1386 while (*str
!= '*' && *str
!=',' && *str
!= ')' ) {
1390 if (*str
=='*') { //there may be an IDS table
1392 ieVariable idsTabName
;
1393 char* tmp
= idsTabName
;
1394 while (( *str
!= ',' ) && ( *str
!= ')' )) {
1400 if (idsTabName
[0]) {
1401 value
= GetIdsValue(src
, idsTabName
);
1404 value
= strtol( src
, (char **) &src
, 0);
1407 else { //no IDS table
1408 value
= strtol( src
, (char **) &src
, 0);
1411 newAction
->int0Parameter
= value
;
1412 } else if (intCount
== 1) {
1413 newAction
->int1Parameter
= value
;
1415 newAction
->int2Parameter
= value
;
1427 int openParenthesisCount
= 0;
1430 if (!openParenthesisCount
)
1432 openParenthesisCount
--;
1435 openParenthesisCount
++;
1437 if (( *src
== ',' ) &&
1438 !openParenthesisCount
)
1447 Action
* act
= GenerateAction( action
);
1452 act
->objects
[0] = newAction
->objects
[0];
1453 newAction
->objects
[0] = NULL
; //avoid freeing of object
1454 delete newAction
; //freeing action
1460 if (objectCount
==3) {
1461 printf("Invalid object count!\n");
1466 ParseObject(str
, src
, newAction
->objects
[objectCount
++]);
1475 if (!stringsCount
) {
1476 dst
= newAction
->string0Parameter
;
1478 dst
= newAction
->string1Parameter
;
1480 //if there are 3 strings, the first 2 will be merged,
1481 //the last one will be left alone
1485 //skipping the context part, which
1486 //is to be readed later
1495 while (*src
!= '"') {
1500 //sizeof(context+name) = 40
1502 *dst
++ = (char) tolower(*src
);
1508 //reading the context part
1512 printf("Invalid mergestrings:%s\n",str
);
1518 if (!stringsCount
) {
1519 dst
= newAction
->string0Parameter
;
1521 dst
= newAction
->string1Parameter
;
1524 //this works only if there are no spaces
1525 if (*src
++!='"' || *src
++!=',' || *src
++!='"') {
1528 //reading the context string
1530 while (*src
!= '"') {
1536 *dst
++ = (char) tolower(*src
);
1547 if (*src
== ',' || *src
==')')
1553 void GoNear(Scriptable
*Sender
, const Point
&p
)
1555 if (Sender
->GetCurrentAction()) {
1556 printMessage("GameScript","Target busy???\n",LIGHT_RED
);
1560 sprintf( Tmp
, "MoveToPoint([%hd.%hd])", p
.x
, p
.y
);
1561 Action
* action
= GenerateAction( Tmp
);
1562 Sender
->AddActionInFront( action
);
1565 void MoveNearerTo(Scriptable
*Sender
, Scriptable
*target
, int distance
)
1568 Map
*myarea
, *hisarea
;
1570 if (Sender
->Type
!= ST_ACTOR
) {
1571 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED
);
1572 Sender
->ReleaseCurrentAction();
1576 myarea
= Sender
->GetCurrentArea();
1577 hisarea
= target
->GetCurrentArea();
1578 if (hisarea
!=myarea
) {
1579 target
= myarea
->GetTileMap()->GetTravelTo(hisarea
->GetScriptName());
1582 printMessage("GameScript", "MoveNearerTo failed to find an exit\n", YELLOW
);
1583 Sender
->ReleaseCurrentAction();
1586 ((Actor
*) Sender
)->UseExit(true);
1588 ((Actor
*) Sender
)->UseExit(false);
1590 // we deliberately don't try GetLikelyPosition here for now,
1591 // maybe a future idea if we have a better implementation
1592 // (the old code used it - by passing true not 0 below - when target was a movable)
1593 GetPositionFromScriptable(target
, p
, 0);
1595 // account for PersonalDistance (which caller uses, but pathfinder doesn't)
1596 if (distance
&& Sender
->Type
== ST_ACTOR
) {
1597 distance
+= ((Actor
*)Sender
)->size
*10;
1599 if (distance
&& target
->Type
== ST_ACTOR
) {
1600 distance
+= ((Actor
*)target
)->size
*10;
1603 MoveNearerTo(Sender
, p
, distance
, 0);
1606 //It is not always good to release the current action if target is unreachable
1607 //we should also raise the trigger TargetUnreachable (if this is an Attack, at least)
1608 //i hacked only this low level function, didn't need the higher ones so far
1609 int MoveNearerTo(Scriptable
*Sender
, const Point
&p
, int distance
, int dont_release
)
1611 if (Sender
->Type
!= ST_ACTOR
) {
1612 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED
);
1613 Sender
->ReleaseCurrentAction();
1617 Actor
*actor
= (Actor
*)Sender
;
1619 if (!actor
->InMove() || actor
->Destination
!= p
) {
1620 actor
->WalkTo(p
, 0, distance
);
1623 if (!actor
->InMove()) {
1626 return dont_release
;
1628 // we can't walk any nearer to destination, give up
1629 Sender
->ReleaseCurrentAction();
1634 void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool flag, int distance)
1637 GetPositionFromScriptable(target,p,flag);
1638 GoNearAndRetry(Sender, p, distance);
1641 void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance)
1643 if (!Sender->GetCurrentAction() ) {
1644 printMessage("GameScript","NULL action retried???\n",LIGHT_RED);
1647 Sender->AddActionInFront( Sender->GetCurrentAction() );
1649 sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y );
1650 Action * action = GenerateAction( Tmp);
1651 //experimental hack, this value means,
1652 //MoveToPoint shall pop the next action too if movement fails
1653 //and the actor is farther than distance
1654 //this will prevent deadlocks
1655 //(we have to add 1 because otherwise distance==0 fails, we subtract it in MoveToPoint)
1656 action->int0Parameter = distance+1;
1657 Sender->AddActionInFront( action );
1660 void FreeSrc(SrcVector
*poi
, const ieResRef key
)
1662 int res
= SrcCache
.DecRef((void *) poi
, key
, true);
1664 printMessage( "GameScript", "Corrupted Src cache encountered (reference count went below zero), ", LIGHT_RED
);
1665 printf( "Src name is: %.8s\n", key
);
1673 SrcVector
*LoadSrc(const ieResRef resname
)
1675 SrcVector
*src
= (SrcVector
*) SrcCache
.GetResource(resname
);
1679 DataStream
* str
= gamedata
->GetResource( resname
, IE_SRC_CLASS_ID
);
1684 str
->ReadDword(&size
);
1685 src
= new SrcVector(size
);
1686 SrcCache
.SetAt( resname
, (void *) src
);
1689 str
->ReadDword(&tmp
);
1691 str
->ReadDword(&tmp
);
1697 #define MEMCPY(a,b) memcpy((a),(b),sizeof(a) )
1699 static Object
*ObjectCopy(Object
*object
)
1701 if (!object
) return NULL
;
1702 Object
*newObject
= new Object();
1703 MEMCPY( newObject
->objectFields
, object
->objectFields
);
1704 MEMCPY( newObject
->objectFilters
, object
->objectFilters
);
1705 MEMCPY( newObject
->objectRect
, object
->objectRect
);
1706 MEMCPY( newObject
->objectName
, object
->objectName
);
1710 Action
*ParamCopy(Action
*parameters
)
1712 Action
*newAction
= new Action(true);
1713 newAction
->actionID
= parameters
->actionID
;
1714 newAction
->int0Parameter
= parameters
->int0Parameter
;
1715 newAction
->int1Parameter
= parameters
->int1Parameter
;
1716 newAction
->int2Parameter
= parameters
->int2Parameter
;
1717 newAction
->pointParameter
= parameters
->pointParameter
;
1718 MEMCPY( newAction
->string0Parameter
, parameters
->string0Parameter
);
1719 MEMCPY( newAction
->string1Parameter
, parameters
->string1Parameter
);
1720 for (int c
=0;c
<3;c
++) {
1721 newAction
->objects
[c
]= ObjectCopy( parameters
->objects
[c
] );
1726 Action
*ParamCopyNoOverride(Action
*parameters
)
1728 Action
*newAction
= new Action(true);
1729 newAction
->actionID
= parameters
->actionID
;
1730 newAction
->int0Parameter
= parameters
->int0Parameter
;
1731 newAction
->int1Parameter
= parameters
->int1Parameter
;
1732 newAction
->int2Parameter
= parameters
->int2Parameter
;
1733 newAction
->pointParameter
= parameters
->pointParameter
;
1734 MEMCPY( newAction
->string0Parameter
, parameters
->string0Parameter
);
1735 MEMCPY( newAction
->string1Parameter
, parameters
->string1Parameter
);
1736 newAction
->objects
[0]= NULL
;
1737 newAction
->objects
[1]= ObjectCopy( parameters
->objects
[1] );
1738 newAction
->objects
[2]= ObjectCopy( parameters
->objects
[2] );
1742 Trigger
*GenerateTriggerCore(const char *src
, const char *str
, int trIndex
, int negate
)
1744 Trigger
*newTrigger
= new Trigger();
1745 newTrigger
->triggerID
= (unsigned short) triggersTable
->GetValueIndex( trIndex
)&0x3fff;
1746 newTrigger
->flags
= (unsigned short) negate
;
1747 int mergestrings
= triggerflags
[newTrigger
->triggerID
]&TF_MERGESTRINGS
;
1748 int stringsCount
= 0;
1750 //Here is the Trigger; Now we need to evaluate the parameters
1751 if (*str
!=')') while (*str
) {
1752 if (*(str
+1)!=':') {
1753 printf("Warning, parser was sidetracked: %s\n",str
);
1757 printf("Invalid type: %s\n",str
);
1766 newTrigger
->pointParameter
.x
= (short) strtol( src
, (char **) &src
, 10 );
1768 newTrigger
->pointParameter
.y
= (short) strtol( src
, (char **) &src
, 10 );
1774 //going to the variable name
1775 while (*str
!= '*' && *str
!=',' && *str
!= ')' ) {
1779 if (*str
=='*') { //there may be an IDS table
1781 ieVariable idsTabName
;
1782 char* tmp
= idsTabName
;
1783 while (( *str
!= ',' ) && ( *str
!= ')' )) {
1789 if (idsTabName
[0]) {
1790 value
= GetIdsValue(src
, idsTabName
);
1793 value
= strtol( src
, (char **) &src
, 0);
1796 else { //no IDS table
1797 value
= strtol( src
, (char **) &src
, 0);
1800 newTrigger
->int0Parameter
= value
;
1801 } else if (intCount
== 1) {
1802 newTrigger
->int1Parameter
= value
;
1804 newTrigger
->int2Parameter
= value
;
1811 ParseObject(str
, src
, newTrigger
->objectParameter
);
1820 if (!stringsCount
) {
1821 dst
= newTrigger
->string0Parameter
;
1823 dst
= newTrigger
->string1Parameter
;
1825 //skipping the context part, which
1826 //is to be readed later
1835 while (*src
!= '"') {
1841 //sizeof(context+name) = 40
1843 *dst
++ = (char) tolower(*src
);
1849 //reading the context part
1853 printf("Invalid mergestrings:%s\n",str
);
1859 if (!stringsCount
) {
1860 dst
= newTrigger
->string0Parameter
;
1862 dst
= newTrigger
->string1Parameter
;
1865 //this works only if there are no spaces
1866 if (*src
++!='"' || *src
++!=',' || *src
++!='"') {
1869 //reading the context string
1871 while (*src
!= '"') {
1878 *dst
++ = (char) tolower(*src
);
1889 if (*src
== ',' || *src
==')')
1895 void SetVariable(Scriptable
* Sender
, const char* VarName
, const char* Context
, ieDword value
)
1897 char newVarName
[8+33];
1899 if (InDebug
&ID_VARIABLES
) {
1900 printf( "Setting variable(\"%s%s\", %d)\n", Context
,
1903 strncpy( newVarName
, Context
, 6 );
1905 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
1906 Sender
->GetCurrentArea()->locals
->SetAt( VarName
, value
);
1909 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
1910 Sender
->locals
->SetAt( VarName
, value
);
1913 Game
*game
= core
->GetGame();
1914 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
1915 game
->kaputz
->SetAt( VarName
, value
);
1919 if (strnicmp(newVarName
,"GLOBAL",6) ) {
1920 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
1922 map
->locals
->SetAt( VarName
, value
);
1924 else if (InDebug
&ID_VARIABLES
) {
1925 printMessage("GameScript"," ",YELLOW
);
1926 printf("Invalid variable %s %s in setvariable\n",Context
, VarName
);
1930 game
->locals
->SetAt( VarName
, ( ieDword
) value
);
1934 void SetVariable(Scriptable
* Sender
, const char* VarName
, ieDword value
)
1940 //some HoW triggers use a : to separate the scope from the variable name
1945 if (InDebug
&ID_VARIABLES
) {
1946 printf( "Setting variable(\"%s\", %d)\n", VarName
, value
);
1948 strncpy( newVarName
, VarName
, 6 );
1950 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
1951 Sender
->GetCurrentArea()->locals
->SetAt( poi
, value
);
1954 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
1955 Sender
->locals
->SetAt( poi
, value
);
1958 Game
*game
= core
->GetGame();
1959 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
1960 game
->kaputz
->SetAt( poi
, value
);
1963 if (strnicmp(newVarName
,"GLOBAL",6) ) {
1964 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
1966 map
->locals
->SetAt( poi
, value
);
1968 else if (InDebug
&ID_VARIABLES
) {
1969 printMessage("GameScript"," ",YELLOW
);
1970 printf("Invalid variable %s in setvariable\n",VarName
);
1974 game
->locals
->SetAt( poi
, ( ieDword
) value
);
1978 ieDword
CheckVariable(Scriptable
* Sender
, const char* VarName
, bool *valid
)
1984 strncpy( newVarName
, VarName
, 6 );
1987 //some HoW triggers use a : to separate the scope from the variable name
1992 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
1993 Sender
->GetCurrentArea()->locals
->Lookup( poi
, value
);
1994 if (InDebug
&ID_VARIABLES
) {
1995 printf("CheckVariable %s: %d\n",VarName
, value
);
1999 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2000 Sender
->locals
->Lookup( poi
, value
);
2001 if (InDebug
&ID_VARIABLES
) {
2002 printf("CheckVariable %s: %d\n",VarName
, value
);
2006 Game
*game
= core
->GetGame();
2007 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2008 game
->kaputz
->Lookup( poi
, value
);
2009 if (InDebug
&ID_VARIABLES
) {
2010 printf("CheckVariable %s: %d\n",VarName
, value
);
2014 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2015 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2017 map
->locals
->Lookup( poi
, value
);
2022 if (InDebug
&ID_VARIABLES
) {
2023 printMessage("GameScript"," ",YELLOW
);
2024 printf("Invalid variable %s in checkvariable\n",VarName
);
2028 game
->locals
->Lookup( poi
, value
);
2030 if (InDebug
&ID_VARIABLES
) {
2031 printf("CheckVariable %s: %d\n",VarName
, value
);
2036 ieDword
CheckVariable(Scriptable
* Sender
, const char* VarName
, const char* Context
, bool *valid
)
2041 strncpy(newVarName
, Context
, 6);
2043 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2044 Sender
->GetCurrentArea()->locals
->Lookup( VarName
, value
);
2045 if (InDebug
&ID_VARIABLES
) {
2046 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2050 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2051 Sender
->locals
->Lookup( VarName
, value
);
2052 if (InDebug
&ID_VARIABLES
) {
2053 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2057 Game
*game
= core
->GetGame();
2058 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2059 game
->kaputz
->Lookup( VarName
, value
);
2060 if (InDebug
&ID_VARIABLES
) {
2061 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2065 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2066 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2068 map
->locals
->Lookup( VarName
, value
);
2073 if (InDebug
&ID_VARIABLES
) {
2074 printMessage("GameScript"," ",YELLOW
);
2075 printf("Invalid variable %s %s in checkvariable\n",Context
, VarName
);
2079 game
->locals
->Lookup( VarName
, value
);
2081 if (InDebug
&ID_VARIABLES
) {
2082 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2087 int DiffCore(ieDword a
, ieDword b
, int diffmode
)
2105 case GREATER_OR_EQUALS
:
2115 case BINARY_LESS_OR_EQUALS
:
2125 case BINARY_MORE_OR_EQUALS
:
2135 case BINARY_INTERSECT
:
2140 case BINARY_NOT_INTERSECT
:
2145 default: //less or equals
2154 int GetGroup(Actor
*actor
)
2156 int type
= 2; //neutral, has no enemies
2157 if (actor
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2160 if (actor
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2166 Point
GetEntryPoint(const char *areaname
, const char *entryname
)
2170 AutoTable
tab("entries");
2174 const char *tmpstr
= tab
->QueryField(areaname
, entryname
);
2177 sscanf(tmpstr
, "%d.%d", &x
, &y
);
2183 /* returns a spell's casting distance, it depends on the caster */
2184 unsigned int GetSpellDistance(ieResRef spellres
, Actor
*actor
)
2188 Spell
* spl
= gamedata
->GetSpell( spellres
);
2190 printMessage("GameScript"," ",LIGHT_RED
);
2191 printf("Spell couldn't be found:%.8s.\n", spellres
);
2194 dist
=spl
->GetCastingDistance(actor
);
2195 gamedata
->FreeSpell(spl
, spellres
, false);
2199 /* returns a spell's casting distance, it depends on the caster */
2200 unsigned int GetItemDistance(ieResRef itemres
, int header
)
2204 Item
* itm
= gamedata
->GetItem( itemres
);
2206 printMessage("GameScript"," ",LIGHT_RED
);
2207 printf("Item couldn't be found:%.8s.\n", itemres
);
2210 dist
=itm
->GetCastingDistance(header
);
2211 gamedata
->FreeItem(itm
, itemres
, false);
2216 void SetupWishCore(Scriptable
*Sender
, int column
, int picks
)
2223 AutoTable
tm("wish");
2225 printStatus( "ERROR", LIGHT_RED
);
2226 printf( "Cannot find wish.2da.\n");
2230 selects
= (int *) malloc(picks
*sizeof(int));
2231 count
= tm
->GetRowCount();
2234 snprintf(varname
,32, "wishpower%02d", i
);
2235 if(CheckVariable(Sender
, varname
, "GLOBAL") ) {
2236 SetVariable(Sender
, varname
, "GLOBAL", 0);
2241 for(i
=0;i
<count
;i
++) {
2248 for(i
=0;i
<picks
;i
++) {
2249 selects
[i
]=rand()%count
;
2252 if(selects
[i
]==selects
[j
]) {
2260 for (i
= 0; i
< picks
; i
++) {
2263 int spnum
= atoi( tm
->QueryField( selects
[i
]-1, column
-1 ) );
2264 snprintf(varname
,32,"wishpower%02d", spnum
);
2265 SetVariable(Sender
, varname
, "GLOBAL",1);
2270 #define MAX_ISLAND_POLYGONS 10
2272 //read a polygon 2da
2273 Gem_Polygon
*GetPolygon2DA(ieDword index
)
2277 if (index
>=MAX_ISLAND_POLYGONS
) {
2282 polygons
= (Gem_Polygon
**) calloc(MAX_ISLAND_POLYGONS
, sizeof(Gem_Polygon
*) );
2284 if (polygons
[index
]) {
2285 return polygons
[index
];
2287 snprintf(resref
, sizeof(ieResRef
), "ISLAND%02d", index
);
2288 AutoTable
tm(resref
);
2292 int cnt
= tm
->GetRowCount();
2296 Point
*p
= new Point
[cnt
];
2300 p
[i
].x
= atoi(tm
->QueryField(i
, 0));
2301 p
[i
].y
= atoi(tm
->QueryField(i
, 1));
2304 polygons
[index
] = new Gem_Polygon(p
, cnt
, NULL
);
2306 return polygons
[index
];