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.
23 #include "Interface.h"
25 #include "StringMgr.h"
32 #include "GameControl.h"
36 #include "defsounds.h"
38 //these tables will get freed by Core
39 Holder
<SymbolMgr
> triggersTable
;
40 Holder
<SymbolMgr
> actionsTable
;
41 Holder
<SymbolMgr
> objectsTable
;
42 TriggerFunction triggers
[MAX_TRIGGERS
];
43 ActionFunction actions
[MAX_ACTIONS
];
44 short actionflags
[MAX_ACTIONS
];
45 short triggerflags
[MAX_TRIGGERS
];
46 ObjectFunction objects
[MAX_OBJECTS
];
47 IDSFunction idtargets
[MAX_OBJECT_FIELDS
];
48 Cache SrcCache
; //cache for string resources (pst)
49 Cache BcsCache
; //cache for scripts
50 int ObjectIDSCount
= 7;
51 int MaxObjectNesting
= 5;
52 bool HasAdditionalRect
= false;
53 bool HasTriggerPoint
= false;
54 //released by ReleaseMemory
55 ieResRef
*ObjectIDSTableNames
;
56 int ObjectFieldsCount
= 7;
57 int ExtraParametersCount
= 0;
63 // reaction modifiers (by reputation and charisma)
67 void InitScriptTables()
69 //initializing the skill->stats conversion table
71 AutoTable
tab("skillsta");
73 int rowcount
= tab
->GetRowCount();
74 SkillCount
= rowcount
;
76 SkillStats
= (int *) malloc(rowcount
* sizeof(int) );
78 SkillStats
[rowcount
]=strtol(tab
->QueryField(rowcount
,0), NULL
, 0);
83 //initializing the happiness table
85 AutoTable
tab("happy");
87 for (int alignment
=0;alignment
<3;alignment
++) {
88 for (int reputation
=0;reputation
<20;reputation
++) {
89 happiness
[alignment
][reputation
]=strtol(tab
->QueryField(reputation
,alignment
), NULL
, 0);
95 //initializing the reaction mod. reputation table
96 AutoTable
rmr("rmodrep");
98 for (int reputation
=0; reputation
<20; reputation
++) {
99 rmodrep
[reputation
] = strtol(rmr
->QueryField(0, reputation
), NULL
, 0);
103 //initializing the reaction mod. charisma table
104 AutoTable
rmc("rmodchr");
106 for (int charisma
=0; charisma
<25; charisma
++) {
107 rmodchr
[charisma
] = strtol(rmc
->QueryField(0, charisma
), NULL
, 0);
112 int GetReaction(Actor
*target
, Scriptable
*Sender
)
114 int chr
, rep
, reaction
;
115 chr
= target
->GetStat(IE_CHR
)-1;
116 if (target
->GetStat(IE_EA
) == EA_PC
) {
117 rep
= core
->GetGame()->Reputation
/10;
119 rep
= target
->GetStat(IE_REPUTATION
);
121 reaction
= 10 + rmodrep
[rep
] + rmodchr
[chr
];
123 // add -4 penalty when dealing with racial enemies
124 if (Sender
&& target
->GetRangerLevel() && Sender
->Type
== ST_ACTOR
&& target
->IsRacialEnemy((Actor
*)Sender
)) {
131 int GetHappiness(Scriptable
* Sender
, int reputation
)
133 if (Sender
->Type
!= ST_ACTOR
) {
136 Actor
* ab
= ( Actor
* ) Sender
;
137 int alignment
= ab
->GetStat(IE_ALIGNMENT
)&AL_GE_MASK
; //good / evil
138 if (reputation
> 200) {
141 return happiness
[alignment
][reputation
/10-1];
144 int GetHPPercent(Scriptable
* Sender
)
146 if (Sender
->Type
!= ST_ACTOR
) {
149 Actor
* ab
= ( Actor
* ) Sender
;
150 int hp1
= ab
->GetStat(IE_MAXHITPOINTS
);
154 int hp2
= ab
->GetBase(IE_HITPOINTS
);
161 void HandleBitMod(ieDword
&value1
, ieDword value2
, int opcode
)
165 value1
= ( value1
& value2
);
168 value1
= ( value1
| value2
);
171 value1
= ( value1
^ value2
);
173 case BM_NAND
: //this is a GemRB extension
174 value1
= ( value1
& ~value2
);
176 case BM_SET
: //this is a GemRB extension
182 // SPIT is not in the original engine spec, it is reserved for the
183 // enchantable items feature
185 static const char *spell_suffices
[]={"SPIT","SPPR","SPWI","SPIN","SPCL"};
187 //this function handles the polymorphism of Spell[RES] actions
188 //it returns spellres
189 bool ResolveSpellName(ieResRef spellres
, Action
*parameters
)
191 if (parameters
->string0Parameter
[0]) {
192 strnlwrcpy(spellres
, parameters
->string0Parameter
, 8);
195 int type
= parameters
->int0Parameter
/1000;
196 int spellid
= parameters
->int0Parameter
%1000;
200 sprintf(spellres
, "%s%03d", spell_suffices
[type
], spellid
);
202 return gamedata
->Exists(spellres
, IE_SPL_CLASS_ID
);
205 void ResolveSpellName(ieResRef spellres
, ieDword number
)
208 unsigned int type
= number
/1000;
209 int spellid
= number
%1000;
213 sprintf(spellres
, "%s%03d", spell_suffices
[type
], spellid
);
216 ieDword
ResolveSpellNumber(const ieResRef spellres
)
221 if(!strnicmp(spellres
, spell_suffices
[i
], 4)) {
223 sscanf(spellres
+4,"%d", &n
);
233 bool ResolveItemName(ieResRef itemres
, Actor
*act
, ieDword Slot
)
235 CREItem
*itm
= act
->inventory
.GetSlotItem(Slot
);
237 strnlwrcpy(itemres
, itm
->ItemResRef
, 8);
238 return gamedata
->Exists(itemres
, IE_ITM_CLASS_ID
);
243 bool StoreHasItemCore(const ieResRef storename
, const ieResRef itemname
)
245 bool had_nostore
=false;
246 bool has_current
=false;
251 Store
*store
= core
->GetCurrentStore();
254 store
= core
->SetCurrentStore(storename
, NULL
);
256 if (strnicmp(store
->Name
, storename
, 8) ) {
257 //not the current store, we need some dirty hack
259 strnlwrcpy(current
, store
->Name
, 8);
260 strnuprcpy(owner
, store
->GetOwner(), 32);
264 printMessage("GameScript","Store cannot be opened!\n", LIGHT_RED
);
269 //don't use triggers (pst style), it would be possible to create infinite loops
270 if (store
->FindItem(itemname
, false) != (unsigned int)-1) {
274 //setting back old store (this will save our current store)
275 core
->SetCurrentStore(current
, owner
);
276 } else if (had_nostore
) {
277 core
->CloseCurrentStore();
282 //don't pass this point by reference, it is subject to change
283 void ClickCore(Scriptable
*Sender
, Point point
, int type
, int speed
)
285 Map
*map
= Sender
->GetCurrentArea();
287 Sender
->ReleaseCurrentAction();
290 Point p
=map
->TMap
->GetMapSize();
291 if (!p
.PointInside(point
)) {
292 Sender
->ReleaseCurrentAction();
295 Video
*video
= core
->GetVideoDriver();
296 GlobalTimer
*timer
= core
->timer
;
297 timer
->SetMoveViewPort( point
.x
, point
.y
, speed
, true );
299 if (timer
->ViewportIsMoving()) {
300 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
302 Sender
->ReleaseCurrentAction();
306 video
->ConvertToScreen(point
.x
, point
.y
);
307 GameControl
*win
= core
->GetGameControl();
311 video
->MoveMouse(point
.x
, point
.y
);
312 video
->ClickMouse(type
);
313 Sender
->ReleaseCurrentAction();
316 void TransformItemCore(Actor
*actor
, Action
*parameters
, bool onlyone
)
318 int i
= actor
->inventory
.GetSlotCount();
320 CREItem
*item
= actor
->inventory
.GetSlotItem(i
);
324 if (strnicmp(item
->ItemResRef
, parameters
->string0Parameter
, 8) ) {
327 actor
->inventory
.SetSlotItemRes(parameters
->string1Parameter
,i
,parameters
->int0Parameter
,parameters
->int1Parameter
,parameters
->int2Parameter
);
334 //check if an inventory (container or actor) has item (could be recursive ?)
335 bool HasItemCore(Inventory
*inventory
, const ieResRef itemname
, ieDword flags
)
337 if (inventory
->HasItem(itemname
, flags
)) {
340 int i
=inventory
->GetSlotCount();
342 //maybe we could speed this up if we mark bag items with a flags bit
343 CREItem
*itemslot
= inventory
->GetSlotItem(i
);
346 Item
*item
= gamedata
->GetItem(itemslot
->ItemResRef
);
350 if (core
->CanUseItemType(SLOT_BAG
,item
,NULL
) ) {
351 //the store is the same as the item's name
352 ret
= StoreHasItemCore(itemslot
->ItemResRef
, itemname
);
354 gamedata
->FreeItem(item
, itemslot
->ItemResRef
);
362 void DisplayStringCore(Scriptable
* Sender
, int Strref
, int flags
)
366 //no one hears you when you are in the Limbo!
367 if (!Sender
->GetCurrentArea()) {
371 memset(&sb
,0,sizeof(sb
));
372 printf( "Displaying string on: %s\n", Sender
->GetScriptName() );
373 if (flags
& DS_CONST
) {
374 if (Sender
->Type
!=ST_ACTOR
) {
375 printMessage("GameScript","Verbal constant not supported for non actors!\n", LIGHT_RED
);
378 Actor
* actor
= ( Actor
* ) Sender
;
379 if ((ieDword
) Strref
>=VCONST_COUNT
) {
380 printMessage("GameScript","Invalid verbal constant!\n", LIGHT_RED
);
384 int tmp
=(int) actor
->StrRefs
[Strref
];
385 if (tmp
<= 0 || (actor
->GetStat(IE_MC_FLAGS
) & MC_EXPORTABLE
)) {
386 //get soundset based string constant
387 actor
->ResolveStringConstant( sb
.Sound
, (unsigned int) Strref
);
391 //display the verbal constants in the console
392 ieDword charactersubtitles
= 0;
393 core
->GetDictionary()->Lookup("Subtitles", charactersubtitles
);
394 if (charactersubtitles
) {
399 if ((Strref
!= -1) && !sb
.Sound
[0]) {
400 sb
= core
->strings
->GetStringBlock( Strref
);
401 if (sb
.text
[0] && strcmp(sb
.text
," ") && (flags
& DS_CONSOLE
)) {
402 //can't play the sound here, we have to delay action
403 //and for that, we have to know how long the text takes
404 if(flags
&DS_NONAME
) {
405 core
->DisplayString( sb
.text
);
407 core
->DisplayStringName( Strref
, 0xf0f0f0, Sender
, 0);
410 if (sb
.text
[0] && strcmp(sb
.text
," ") && (flags
& (DS_HEAD
| DS_AREA
))) {
411 Sender
->DisplayHeadText( sb
.text
);
412 //don't free sb.text, it is residing in Sender
413 if (flags
& DS_AREA
) {
414 Sender
->FixHeadTextPos();
417 core
->FreeString( sb
.text
);
420 if (sb
.Sound
[0] && !(flags
&DS_SILENT
) ) {
421 ieDword speech
= GEM_SND_RELATIVE
; //disable position
422 if (flags
&DS_SPEECH
) speech
|=GEM_SND_SPEECH
;
423 ieDword len
= core
->GetAudioDrv()->Play( sb
.Sound
,0,0,speech
);
424 ieDword counter
= ( AI_UPDATE_TIME
* len
) / 1000;
425 if ((counter
!= 0) && (flags
&DS_WAIT
) )
426 Sender
->SetWait( counter
);
430 int CanSee(Scriptable
* Sender
, Scriptable
* target
, bool range
, int seeflag
)
434 if (target
->Type
==ST_ACTOR
) {
435 Actor
*tar
= (Actor
*) target
;
437 if (!tar
->ValidTarget(seeflag
)) {
442 map
= target
->GetCurrentArea();
443 if (!(seeflag
&GA_GLOBAL
)) {
444 if ( map
!=Sender
->GetCurrentArea() ) {
452 if (Sender
->Type
== ST_ACTOR
) {
453 Actor
* snd
= ( Actor
* ) Sender
;
454 dist
= snd
->Modified
[IE_VISUALRANGE
];
459 if (Distance(target
->Pos
, Sender
->Pos
) > dist
* 15) {
464 return map
->IsVisible(target
->Pos
, Sender
->Pos
);
467 //non actors can see too (reducing function to LOS)
468 //non actors can be seen too (reducing function to LOS)
469 int SeeCore(Scriptable
* Sender
, Trigger
* parameters
, int justlos
)
474 if (parameters
->int0Parameter
) {
479 Scriptable
* tar
= GetActorFromObject( Sender
, parameters
->objectParameter
, flags
);
480 /* don't set LastSeen if this isn't an actor */
485 if (CanSee(Sender
, tar
, true, flags
) ) {
489 if (Sender
->Type
==ST_ACTOR
&& tar
->Type
==ST_ACTOR
) {
490 Actor
* snd
= ( Actor
* ) Sender
;
491 //additional checks for invisibility?
492 snd
->LastSeen
= ((Actor
*) tar
)->GetID();
499 //transfering item from Sender to target
500 //if target has no inventory, the item will be destructed
501 //if target can't get it, it will be dropped at its feet
502 int MoveItemCore(Scriptable
*Sender
, Scriptable
*target
, const char *resref
, int flags
, int setflag
)
506 // track whether we are dealing with our party and need to display feedback
507 bool lostitem
= false;
508 bool gotitem
= false;
513 map
=Sender
->GetCurrentArea();
514 switch(Sender
->Type
) {
516 myinv
=&((Actor
*) Sender
)->inventory
;
517 if (((Actor
*)Sender
)->InParty
) lostitem
= true;
520 myinv
=&((Container
*) Sender
)->inventory
;
526 myinv
->RemoveItem(resref
, flags
, &item
);
528 // nothing was removed
532 item
->Flags
|=setflag
;
534 switch(target
->Type
) {
536 myinv
=&((Actor
*) target
)->inventory
;
537 if (((Actor
*) target
)->InParty
) gotitem
= true;
540 myinv
=&((Container
*) target
)->inventory
;
548 if (lostitem
) core
->DisplayConstantString(STR_LOSTITEM
, 0xbcefbc);
549 return MIC_GOTITEM
; // actually it was lost, not gained
551 if ( myinv
->AddSlotItem(item
, SLOT_ONLYINVENTORY
) !=ASI_SUCCESS
) {
552 // drop it at my feet
553 map
->AddItemToLocation(target
->Pos
, item
);
554 if (gotitem
) core
->DisplayConstantString(STR_INVFULL_ITEMDROP
, 0xbcefbc);
557 if (gotitem
) core
->DisplayConstantString(STR_GOTITEM
, 0xbcefbc);
561 static Targets
* ReturnActorAsTarget(Actor
*aC
)
566 //Ok :) we now have our Object. Let's create a Target struct and add the object to it
567 Targets
*tgts
= new Targets( );
568 tgts
->AddTarget( aC
, 0, 0 );
569 //return here because object name/IDS targeting are mutually exclusive
573 Actor
*FindActorNearby(const char *name
, Map
*except
, int ga_flags
)
575 Game
*game
= core
->GetGame();
576 size_t mc
= game
->GetLoadedMapCount();
578 Map
*map
= game
->GetMap(mc
);
579 if (map
==except
) continue;
580 Actor
* aC
= map
->GetActor(name
, ga_flags
);
588 /* returns actors that match the [x.y.z] expression */
589 static Targets
* EvaluateObject(Map
*map
, Scriptable
* Sender
, Object
* oC
, int ga_flags
)
591 if (oC
->objectName
[0]) {
592 //We want the object by its name... (doors/triggers don't play here!)
593 Actor
* aC
= map
->GetActor( oC
->objectName
, ga_flags
);
595 if (!aC
&& (ga_flags
&GA_GLOBAL
) ) {
596 aC
= FindActorNearby(oC
->objectName
, map
, ga_flags
);
599 return ReturnActorAsTarget(aC
);
602 if (oC
->objectFields
[0]==-1) {
603 Actor
* aC
= map
->GetActorByGlobalID( (ieDword
) oC
->objectFields
[1] );
604 /* TODO: this hack will throw away an invalid target */
605 /* Consider putting this in GetActorByGlobalID */
606 if (aC
&& !aC
->ValidTarget(ga_flags
)) {
609 return ReturnActorAsTarget(aC
);
614 //else branch, IDS targeting
615 for (int j
= 0; j
< ObjectIDSCount
; j
++) {
616 if (!oC
->objectFields
[j
]) {
619 IDSFunction func
= idtargets
[j
];
621 printf("Unimplemented IDS targeting opcode!\n");
625 //we already got a subset of actors
626 int i
= tgts
->Count();
627 /*premature end, filtered everything*/
629 break; //leaving the loop
631 targetlist::iterator m
;
632 const targettype
*t
= tgts
->GetFirstTarget(m
, -1);
634 if (t
->actor
->Type
!=ST_ACTOR
) {
635 //we should never stumble here
637 // t = tgts->RemoveTargetAt(m);
640 if (!func( (Actor
*) (t
->actor
), oC
->objectFields
[j
] ) ) {
641 t
= tgts
->RemoveTargetAt(m
);
644 t
= tgts
->GetNextTarget(m
, -1);
647 //we need to get a subset of actors from the large array
648 //if this gets slow, we will need some index tables
649 int i
= map
->GetActorCount(true);
650 tgts
= new Targets();
652 Actor
*ac
=map
->GetActor(i
,true);
653 int dist
= Distance(Sender
->Pos
, ac
->Pos
);
654 if (ac
&& func(ac
, oC
->objectFields
[j
]) ) {
655 // don't return Sender in IDS targeting!
657 tgts
->AddTarget((Scriptable
*) ac
, dist
, ga_flags
);
666 Targets
* GetAllObjects(Map
*map
, Scriptable
* Sender
, Object
* oC
, int ga_flags
)
671 Targets
* tgts
= EvaluateObject(map
, Sender
, oC
, ga_flags
);
672 //if we couldn't find an endpoint by name or object qualifiers
673 //it is not an Actor, but could still be a Door or Container (scriptable)
674 if (!tgts
&& oC
->objectName
[0]) {
677 //now lets do the object filter stuff, we create Targets because
678 //it is possible to start from blank sheets using endpoint filters
679 //like (Myself, Protagonist etc)
681 tgts
= new Targets();
683 for (int i
= 0; i
< MaxObjectNesting
; i
++) {
684 int filterid
= oC
->objectFilters
[i
];
688 ObjectFunction func
= objects
[filterid
];
690 tgts
= func( Sender
, tgts
, ga_flags
);
693 printMessage("GameScript"," ", YELLOW
);
694 printf("Unknown object filter: %d %s\n",filterid
, objectsTable
->GetValue(filterid
) );
696 if (!tgts
->Count()) {
704 Targets
*GetAllObjectsNearby(Scriptable
* Sender
, Object
* oC
, int ga_flags
)
706 Game
*game
= core
->GetGame();
707 size_t mc
= game
->GetLoadedMapCount();
709 Map
*map
= game
->GetMap(mc
);
710 if (map
==Sender
->GetCurrentArea()) continue;
711 Targets
*tgts
= GetAllObjects(map
, Sender
, oC
, ga_flags
);
719 Targets
*GetAllActors(Scriptable
*Sender
, int ga_flags
)
721 Map
*map
=Sender
->GetCurrentArea();
723 int i
= map
->GetActorCount(true);
724 Targets
*tgts
= new Targets();
726 Actor
*ac
=map
->GetActor(i
,true);
727 int dist
= Distance(Sender
->Pos
, ac
->Pos
);
728 tgts
->AddTarget((Scriptable
*) ac
, dist
, ga_flags
);
733 Scriptable
*GetActorObject(TileMap
*TMap
, const char *name
)
736 Scriptable
* aC
= TMap
->GetDoor( name
);
741 //containers should have a precedence over infopoints because otherwise
742 //AR1512 sanity test quest would fail
743 //If this order couldn't be maintained, then 'Contains' should have a
744 //unique call to get containers only
745 //No... it was not an door... maybe a Container?
746 aC
= TMap
->GetContainer( name
);
751 //No... it was not a container ... maybe an InfoPoint?
752 aC
= TMap
->GetInfoPoint( name
);
759 // blocking actions need to store some kinds of objects between ticks
760 Scriptable
* GetStoredActorFromObject(Scriptable
* Sender
, Object
* oC
, int ga_flags
)
762 Scriptable
*tar
= NULL
;
763 // retrieve an existing target if it still exists and is valid
764 if (Sender
->CurrentActionTarget
) {
765 tar
= core
->GetGame()->GetActorByGlobalID(Sender
->CurrentActionTarget
);
767 // always an actor, check if it satisfies flags
768 if (((Actor
*)tar
)->ValidTarget(ga_flags
)) {
772 return NULL
; // target invalid/gone
774 tar
= GetActorFromObject(Sender
, oC
, ga_flags
);
775 // maybe store the target if it's an actor..
776 if (tar
&& tar
->Type
== ST_ACTOR
) {
777 // .. but we only want objects created via objectFilters
778 if (oC
->objectFilters
[0]) {
779 Sender
->CurrentActionTarget
= ((Actor
*)tar
)->globalID
;
785 Scriptable
* GetActorFromObject(Scriptable
* Sender
, Object
* oC
, int ga_flags
)
787 Scriptable
*aC
= NULL
;
792 Targets
*tgts
= GetAllObjects(Sender
->GetCurrentArea(), Sender
, oC
, ga_flags
);
794 //now this could return other than actor objects
795 aC
= tgts
->GetTarget(0,-1);
797 if (!aC
&& (ga_flags
&GA_GLOBAL
) )
799 tgts
= GetAllObjectsNearby(Sender
, oC
, ga_flags
);
801 //now this could return other than actor objects
802 aC
= tgts
->GetTarget(0,-1);
809 if (oC
->objectName
[0]) {
810 aC
= GetActorObject(Sender
->GetCurrentArea()->GetTileMap(), oC
->objectName
);
814 Game
*game
= core
->GetGame();
816 //global actors are always found by scripting name!
817 aC
= game
->FindPC(oC
->objectName
);
821 aC
= game
->FindNPC(oC
->objectName
);
826 if (ga_flags
&GA_GLOBAL
) {
827 size_t mc
= game
->GetLoadedMapCount();
829 Map
*map
= game
->GetMap(mc
);
830 if (map
==Sender
->GetCurrentArea()) continue;
831 aC
= GetActorObject(map
->GetTileMap(), oC
->objectName
);
841 /*FIXME: what is 'base'*/
842 void PolymorphCopyCore(Actor
*src
, Actor
*tar
, bool base
)
844 tar
->SetBase(IE_ANIMATION_ID
, src
->GetStat(IE_ANIMATION_ID
) );
846 tar
->SetBase(IE_ARMOR_TYPE
, src
->GetStat(IE_ARMOR_TYPE
) );
847 for (int i
=0;i
<7;i
++) {
848 tar
->SetBase(IE_COLORS
+i
, src
->GetStat(IE_COLORS
+i
) );
851 tar
->SetName(src
->GetName(0),0);
852 tar
->SetName(src
->GetName(1),1);
853 //add more attribute copying
856 void CreateCreatureCore(Scriptable
* Sender
, Action
* parameters
, int flags
)
858 Scriptable
*tmp
= GetActorFromObject( Sender
, parameters
->objects
[1] );
859 //if there is nothing to copy, don't spawn anything
860 if (flags
& CC_COPY
) {
861 if (!tmp
|| tmp
->Type
!= ST_ACTOR
) {
867 if (flags
& CC_STRING1
) {
868 ab
= gamedata
->GetCreature(parameters
->string1Parameter
);
871 ab
= gamedata
->GetCreature(parameters
->string0Parameter
);
875 printMessage("GameScript","Failed to create creature! ",LIGHT_RED
);
876 printf("(missing creature file %s?)\n", parameters
->string0Parameter
);
877 // maybe this should abort()?
881 //iwd2 allows an optional scriptname to be set
882 //but bg2 doesn't have this feature
883 //this way it works for both games
884 if ((flags
& CC_SCRIPTNAME
) && parameters
->string1Parameter
[0]) {
885 ab
->SetScriptName(parameters
->string1Parameter
);
892 switch (flags
& CC_MASK
) {
893 //creates creature just off the screen
896 Region vp
= core
->GetVideoDriver()->GetViewport();
897 radius
=vp
.w
/2; //actually it must be further divided by the tile size, hmm 16?
900 case CC_OBJECT
://use object + offset
903 case CC_OFFSET
://use sender + offset
904 pnt
.x
= parameters
->pointParameter
.x
+Sender
->Pos
.x
;
905 pnt
.y
= parameters
->pointParameter
.y
+Sender
->Pos
.y
;
907 default: //absolute point, but -1,-1 means AtFeet
908 pnt
.x
= parameters
->pointParameter
.x
;
909 pnt
.y
= parameters
->pointParameter
.y
;
911 pnt
.x
= Sender
->Pos
.x
;
912 pnt
.y
= Sender
->Pos
.y
;
917 Map
*map
= Sender
->GetCurrentArea();
919 ab
->SetPosition( pnt
, flags
&CC_CHECK_IMPASSABLE
, radius
);
920 ab
->SetOrientation(parameters
->int0Parameter
, false );
922 //if string1 is animation, then we can't use it for a DV too
923 if (flags
& CC_PLAY_ANIM
) {
924 CreateVisualEffectCore( ab
, ab
->Pos
, parameters
->string1Parameter
, 1);
926 //setting the deathvariable if it exists (iwd2)
927 if (parameters
->string1Parameter
[0]) {
928 ab
->SetScriptName(parameters
->string1Parameter
);
932 if (flags
& CC_COPY
) {
933 PolymorphCopyCore ( (Actor
*) tmp
, ab
, false);
937 static ScriptedAnimation
*GetVVCEffect(const char *effect
, int iterations
)
940 ScriptedAnimation
* vvc
= gamedata
->GetScriptedAnimation(effect
, false);
942 printMessage("GameScript","Failed to create effect.",LIGHT_RED
);
946 vvc
->SetDefaultDuration( vvc
->GetSequenceDuration(AI_UPDATE_TIME
* iterations
));
953 void CreateVisualEffectCore(Actor
*target
, const char *effect
, int iterations
)
955 ScriptedAnimation
*vvc
= GetVVCEffect(effect
, iterations
);
957 target
->AddVVCell( vvc
);
961 void CreateVisualEffectCore(Scriptable
*Sender
, const Point
&position
, const char *effect
, int iterations
)
963 ScriptedAnimation
*vvc
= GetVVCEffect(effect
, iterations
);
965 vvc
->XPos
+=position
.x
;
966 vvc
->YPos
+=position
.y
;
967 Sender
->GetCurrentArea( )->AddVVCell( vvc
);
971 //this destroys the current actor and replaces it with another
972 void ChangeAnimationCore(Actor
*src
, const char *resref
, bool effect
)
974 Actor
*tar
= gamedata
->GetCreature(resref
);
976 Map
*map
= src
->GetCurrentArea();
977 map
->AddActor( tar
);
978 Point pos
= src
->Pos
;
979 tar
->SetOrientation(src
->GetOrientation(), false );
981 // can't SetPosition while the old actor is taking the spot
982 tar
->SetPosition(pos
, 1);
984 CreateVisualEffectCore(tar
, tar
->Pos
,"smokepuffeffect",1);
989 void EscapeAreaCore(Scriptable
* Sender
, const Point
&p
, const char* area
, const Point
&enter
, int flags
, int wait
)
993 if ( !p
.isempty() && PersonalDistance(p
, Sender
)>MAX_OPERATING_DISTANCE
) {
994 //MoveNearerTo will return 0, if the actor is in move
995 //it will return 1 (the fourth parameter) if the target is unreachable
996 if (!MoveNearerTo(Sender
, p
, MAX_OPERATING_DISTANCE
,1) ) {
997 if (!Sender
->InMove()) printf("At least it said so...\n");
1002 if (flags
&EA_DESTROY
) {
1003 //this must be put into a non-const variable
1004 sprintf( Tmp
, "DestroySelf()" );
1006 // last parameter is 'face', which should be passed from relevant action parameter..
1007 sprintf( Tmp
, "MoveBetweenAreas(\"%s\",[%hd.%hd],%d)", area
, enter
.x
, enter
.y
, 0 );
1009 printMessage("GSUtils"," ", WHITE
);
1010 printf("Executing %s in EscapeAreaCore\n", Tmp
);
1011 //drop this action, but add another (destroyself or movebetweenareas)
1012 //between the arrival and the final escape, there should be a wait time
1013 //that wait time could be handled here
1015 printf("But wait a bit... (%d)\n", wait
);
1016 Sender
->SetWait(wait
);
1018 Sender
->ReleaseCurrentAction();
1019 Action
* action
= GenerateAction( Tmp
);
1020 Sender
->AddActionInFront( action
);
1023 void GetTalkPositionFromScriptable(Scriptable
* scr
, Point
&position
)
1025 switch (scr
->Type
) {
1026 case ST_AREA
: case ST_GLOBAL
:
1027 position
= scr
->Pos
; //fake
1030 //if there are other moveables, put them here
1031 position
= ((Movable
*) scr
)->GetMostLikelyPosition();
1033 case ST_TRIGGER
: case ST_PROXIMITY
: case ST_TRAVEL
:
1034 if (((InfoPoint
*) scr
)->Flags
& TRAP_USEPOINT
) {
1035 position
=((InfoPoint
*) scr
)->UsePoint
;
1038 position
=((InfoPoint
*) scr
)->TrapLaunch
;
1040 case ST_DOOR
: case ST_CONTAINER
:
1041 position
=((Highlightable
*) scr
)->TrapLaunch
;
1046 void GetPositionFromScriptable(Scriptable
* scr
, Point
&position
, bool dest
)
1049 position
= scr
->Pos
;
1052 switch (scr
->Type
) {
1053 case ST_AREA
: case ST_GLOBAL
:
1054 position
= scr
->Pos
; //fake
1057 //if there are other moveables, put them here
1058 position
= ((Movable
*) scr
)->GetMostLikelyPosition();
1060 case ST_TRIGGER
: case ST_PROXIMITY
: case ST_TRAVEL
:
1061 if (((InfoPoint
*) scr
)->Flags
& TRAP_USEPOINT
) {
1062 position
=((InfoPoint
*) scr
)->UsePoint
;
1065 case ST_DOOR
: case ST_CONTAINER
:
1066 position
=((Highlightable
*) scr
)->TrapLaunch
;
1070 int CheckInteract(const char *talker
, const char *target
)
1072 AutoTable
interact("interact");
1075 const char *value
= interact
->QueryField(talker
, target
);
1082 return I_COMPLIMENT
;
1089 static ieResRef PlayerDialogRes
= "PLAYERx\0";
1091 void BeginDialog(Scriptable
* Sender
, Action
* parameters
, int Flags
)
1093 Scriptable
* tar
, *scr
;
1094 int seeflag
= GA_NO_DEAD
;
1096 if (InDebug
&ID_VARIABLES
) {
1097 printf("BeginDialog core\n");
1099 if (Flags
& BD_OWN
) {
1100 tar
= GetStoredActorFromObject( Sender
, parameters
->objects
[1], seeflag
);
1103 tar
= GetStoredActorFromObject( Sender
, parameters
->objects
[1], seeflag
);
1107 printMessage("GameScript"," ",LIGHT_RED
);
1108 printf("Speaker for dialog couldn't be found (Sender: %s, Type: %d) Flags:%d.\n", Sender
->GetScriptName(), Sender
->Type
, Flags
);
1109 Sender
->ReleaseCurrentAction();
1113 if (!tar
|| tar
->Type
!=ST_ACTOR
) {
1114 printMessage("GameScript"," ",LIGHT_RED
);
1115 printf("Target for dialog couldn't be found (Sender: %s, Type: %d).\n", Sender
->GetScriptName(), Sender
->Type
);
1116 if (Sender
->Type
== ST_ACTOR
) {
1117 ((Actor
*) Sender
)->DebugDump();
1119 printf ("Target object: ");
1120 if (parameters
->objects
[1]) {
1121 parameters
->objects
[1]->Dump();
1125 Sender
->ReleaseCurrentAction();
1129 Actor
*speaker
, *target
;
1132 target
= (Actor
*) tar
;
1133 if ((Flags
& BD_CHECKDIST
) && !CanSee(scr
, target
, false, seeflag
) ) {
1134 printMessage("GameScript"," ",LIGHT_RED
);
1135 printf("CanSee returned false! Speaker (%s, type %d) and target are:\n", scr
->GetScriptName(), scr
->Type
);
1136 if (scr
->Type
== ST_ACTOR
) {
1137 ((Actor
*) scr
)->DebugDump();
1139 ((Actor
*) tar
)->DebugDump();
1140 Sender
->ReleaseCurrentAction();
1144 if (scr
->Type
==ST_ACTOR
) {
1145 speaker
= (Actor
*) scr
;
1146 if (speaker
->GetStat(IE_STATE_ID
)&STATE_DEAD
) {
1147 printMessage("GameScript"," ",LIGHT_RED
);
1148 printf("Speaker is dead, cannot start dialogue. Speaker and target are:\n");
1149 speaker
->DebugDump();
1150 target
->DebugDump();
1151 Sender
->ReleaseCurrentAction();
1154 ieDword range
= MAX_OPERATING_DISTANCE
;
1155 //making sure speaker is the protagonist, player, actor
1156 if ( target
->InParty
== 1) swap
= true;
1157 else if ( speaker
->InParty
!=1 && target
->InParty
) swap
= true;
1158 //CHECKDIST works only for mobile scriptables
1159 if (Flags
&BD_CHECKDIST
) {
1160 if ( scr
->GetCurrentArea()!=target
->GetCurrentArea() ||
1161 PersonalDistance(scr
, target
)>range
) {
1162 MoveNearerTo(Sender
, target
, MAX_OPERATING_DISTANCE
);
1167 //pst style dialog with trigger points
1169 if (Flags
&BD_CHECKDIST
) {
1172 if (target
->InMove()) {
1173 //waiting for target
1174 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
1175 Sender
->ReleaseCurrentAction();
1179 GetTalkPositionFromScriptable(scr
, TalkPos
);
1180 if (PersonalDistance(TalkPos
, target
)>MAX_OPERATING_DISTANCE
) {
1181 //try to force the target to come closer???
1182 GoNear(target
, TalkPos
);
1183 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
1184 Sender
->ReleaseCurrentAction();
1191 GameControl
* gc
= core
->GetGameControl();
1193 printMessage( "GameScript","Dialog cannot be initiated because there is no GameControl.", YELLOW
);
1194 Sender
->ReleaseCurrentAction();
1197 //can't initiate dialog, because it is already there
1198 if (gc
->GetDialogueFlags()&DF_IN_DIALOG
) {
1199 if (Flags
& BD_INTERRUPT
) {
1200 //break the current dialog if possible
1201 gc
->EndDialog(true);
1203 //check if we could manage to break it, not all dialogs are breakable!
1204 if (gc
->GetDialogueFlags()&DF_IN_DIALOG
) {
1205 printMessage( "GameScript","Dialog cannot be initiated because there is already one.", YELLOW
);
1206 Sender
->ReleaseCurrentAction();
1211 // starting a dialog ends cutscenes!
1212 core
->SetCutSceneMode(false);
1214 const char* Dialog
= NULL
;
1217 switch (Flags
& BD_LOCMASK
) {
1219 Dialog
= parameters
->string0Parameter
;
1220 if (Flags
& BD_SETDIALOG
) {
1221 scr
->SetDialog( Dialog
);
1226 if (swap
) Dialog
= scr
->GetDialog();
1227 else Dialog
= target
->GetDialog(GD_FEEDBACK
);
1230 //what if playerdialog was initiated from Player2?
1231 PlayerDialogRes
[5] = '1';
1232 Dialog
= ( const char * ) PlayerDialogRes
;
1234 case BD_INTERACT
: //using the source for the dialog
1235 const char* scriptingname
= scr
->GetScriptName();
1237 /* use interact.2da for short, inlined dialogue */
1238 int type
= CheckInteract(scriptingname
, target
->GetScriptName());
1240 speaker
->Interact(type
);
1241 target
->Response(type
);
1242 Sender
->ReleaseCurrentAction();
1245 /* banter dialogue */
1246 pdtable
.load("interdia");
1247 //Dialog is a borrowed reference, we cannot free pdtable while it is being used
1249 Dialog
= pdtable
->QueryField( scriptingname
, "FILE" );
1255 //dialog is not meaningful
1256 if (!Dialog
|| Dialog
[0]=='*') {
1257 Sender
->ReleaseCurrentAction();
1261 //maybe we should remove the action queue, but i'm unsure
1262 //no, we shouldn't even call this!
1263 //Sender->ReleaseCurrentAction();
1265 // moved this here from InitDialog, because InitDialog doesn't know which side is which
1266 // post-swap (and non-actors always have IF_NOINT set) .. also added a check that it's
1267 // actually busy doing something, for the same reason
1268 if (target
->GetInternalFlag()&IF_NOINT
&& (target
->GetCurrentAction() || target
->GetNextAction())) {
1269 core
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
1270 Sender
->ReleaseCurrentAction();
1274 if (speaker
!=target
) {
1276 Scriptable
*tmp
= tar
;
1280 if (!(Flags
& BD_INTERRUPT
)) {
1281 // added CurrentAction as part of blocking action fixes
1282 if (tar
->GetCurrentAction() || tar
->GetNextAction()) {
1283 core
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
1284 Sender
->ReleaseCurrentAction();
1291 //don't clear target's actions, because a sequence like this will be broken:
1292 //StartDialog([PC]); SetGlobal("Talked","LOCALS",1);
1294 if (scr
->Type
==ST_ACTOR
) {
1295 ((Actor
*) scr
)->SetOrientation(GetOrient( tar
->Pos
, scr
->Pos
), true);
1297 if (tar
->Type
==ST_ACTOR
) {
1298 ((Actor
*) tar
)->SetOrientation(GetOrient( scr
->Pos
, tar
->Pos
), true);
1305 //increasing NumTimesTalkedTo or NumTimesInteracted
1306 if (Flags
& BD_TALKCOUNT
) {
1307 gc
->SetDialogueFlags(DF_TALKCOUNT
, BM_OR
);
1308 } else if ((Flags
& BD_LOCMASK
) == BD_INTERACT
) {
1309 gc
->SetDialogueFlags(DF_INTERACT
, BM_OR
);
1312 core
->GetDictionary()->SetAt("DialogChoose",(ieDword
) -1);
1313 ret
= gc
->InitDialog( scr
, tar
, Dialog
);
1320 Sender
->ReleaseCurrentAction();
1321 if (Flags
& BD_NOEMPTY
) {
1324 core
->DisplayConstantStringName(STR_NOTHINGTOSAY
,0xff0000,tar
);
1328 //this is a bit fishy
1330 Sender
->ReleaseCurrentAction();
1334 void MoveBetweenAreasCore(Actor
* actor
, const char *area
, const Point
&position
, int face
, bool adjust
)
1336 printMessage("GameScript", " ", WHITE
);
1337 printf("MoveBetweenAreas: %s to %s [%d.%d] face: %d\n", actor
->GetName(0), area
,position
.x
,position
.y
, face
);
1339 Game
* game
= core
->GetGame();
1340 if (area
[0]) { //do we need to switch area?
1341 Map
* map1
= actor
->GetCurrentArea();
1342 //we have to change the pathfinder
1343 //to the target area if adjust==true
1344 map2
= game
->GetMap(area
, false);
1347 map1
->RemoveActor( actor
);
1349 map2
->AddActor( actor
);
1352 actor
->SetPosition(position
, adjust
);
1354 actor
->SetOrientation( face
, false );
1356 // should this perhaps be a 'selected' check or similar instead?
1357 if (actor
->InParty
) {
1358 GameControl
*gc
=core
->GetGameControl();
1359 gc
->SetScreenFlags(SF_CENTERONACTOR
,BM_OR
);
1363 //repeat movement, until goal isn't reached
1364 //if int0parameter is !=0, then it will try only x times
1365 void MoveToObjectCore(Scriptable
*Sender
, Action
*parameters
, ieDword flags
, bool untilsee
)
1367 if (Sender
->Type
!= ST_ACTOR
) {
1368 Sender
->ReleaseCurrentAction();
1371 Scriptable
* target
= GetStoredActorFromObject( Sender
, parameters
->objects
[1] );
1373 Sender
->ReleaseCurrentAction();
1376 Actor
* actor
= ( Actor
* ) Sender
;
1377 if (untilsee
&& CanSee(actor
, target
, true, 0) ) {
1378 Sender
->ReleaseCurrentAction();
1381 if (PersonalDistance(actor
, target
)<MAX_OPERATING_DISTANCE
) {
1382 Sender
->ReleaseCurrentAction();
1386 if (!actor
->InMove() || actor
->Destination
!= target
->Pos
) {
1387 actor
->WalkTo( target
->Pos
, flags
, 0 );
1389 //hopefully this hack will prevent lockups
1390 if (!actor
->InMove()) {
1391 Sender
->ReleaseCurrentAction();
1395 //repeat movement...
1396 Action
*newaction
= ParamCopyNoOverride(parameters
);
1397 if (newaction
->int0Parameter
!=1) {
1398 if (newaction
->int0Parameter
) {
1399 newaction
->int0Parameter
--;
1401 actor
->AddActionInFront(newaction
);
1405 Sender
->ReleaseCurrentAction();
1408 void CreateItemCore(CREItem
*item
, const char *resref
, int a
, int b
, int c
)
1410 strncpy(item
->ItemResRef
, resref
, 8);
1411 core
->ResolveRandomItem(item
);
1413 Item
*origitem
= gamedata
->GetItem(resref
);
1414 for(int i
=0;i
<3;i
++) {
1415 ITMExtHeader
*e
= origitem
->GetExtHeader(i
);
1416 item
->Usages
[i
]=e
?e
->Charges
:0;
1418 gamedata
->FreeItem(origitem
, resref
, false);
1420 item
->Usages
[0]=(ieWord
) a
;
1421 item
->Usages
[1]=(ieWord
) b
;
1422 item
->Usages
[2]=(ieWord
) c
;
1427 //It is possible to attack CONTAINERS/DOORS as well!!!
1428 void AttackCore(Scriptable
*Sender
, Scriptable
*target
, int flags
)
1430 //this is a dangerous cast, make sure actor is Actor * !!!
1431 Actor
*actor
= (Actor
*) Sender
;
1434 ITMExtHeader
*header
= NULL
;
1435 ITMExtHeader
*hittingheader
= NULL
;
1438 int DamageBonus
, CriticalBonus
;
1441 //bool leftorright = (bool) ((attacksperround-attackcount)&1);
1442 bool leftorright
= false;
1444 //will return false on any errors (eg, unusable weapon)
1445 if (!actor
->GetCombatDetails(tohit
, leftorright
, wi
, header
, hittingheader
, Flags
, DamageBonus
, speed
, CriticalBonus
, style
)) {
1446 Sender
->ReleaseCurrentAction();
1450 if (header
) wi
.range
*= 10;
1453 if ( target
->Type
== ST_DOOR
|| target
->Type
== ST_CONTAINER
) {
1457 ieDword targetID
= 0;
1458 if (target
->Type
==ST_ACTOR
) {
1459 tar
= (Actor
*) target
;
1460 targetID
= tar
->GetID();
1463 Sender
->ReleaseCurrentAction();
1466 if (!(flags
&AC_NO_SOUND
) ) {
1467 if (actor
->LastTarget
!= targetID
) {
1468 //play attack sound for party members
1469 if (actor
->InParty
) {
1470 //pick from all 5 possible verbal constants
1471 actor
->VerbalConstant(VB_ATTACK
, 5);
1472 //DisplayStringCore(Sender, VB_ATTACK, DS_CONSOLE|DS_CONST );
1474 //display attack message
1475 core
->DisplayConstantStringAction(STR_ACTION_ATTACK
,0xf0f0f0, Sender
, target
);
1479 if(target
->Type
== ST_ACTOR
) {
1480 actor
->SetTarget( target
);
1482 if ( Sender
->GetCurrentArea()!=target
->GetCurrentArea() ||
1483 (PersonalDistance(Sender
, target
) > wi
.range
) ) {
1484 MoveNearerTo(Sender
, target
, wi
.range
);
1486 } else if (target
->Type
== ST_DOOR
) {
1487 //Forcing a lock does not launch the trap...
1488 Door
* door
= (Door
*) target
;
1489 if(door
->Flags
& DOOR_LOCKED
) {
1490 door
->TryBashLock(actor
);
1492 Sender
->ReleaseCurrentAction();
1494 } else if (target
->Type
== ST_CONTAINER
) {
1495 Container
* cont
= (Container
*) target
;
1496 if(cont
->Flags
& CONT_LOCKED
) {
1497 cont
->TryBashLock(actor
);
1499 Sender
->ReleaseCurrentAction();
1503 actor
->PerformAttack(core
->GetGame()->GameTime
);
1506 bool MatchActor(Scriptable
*Sender
, ieDword actorID
, Object
* oC
)
1513 tgts
= GetAllObjects(Sender
->GetCurrentArea(), Sender
, oC
, 0);
1515 // [0]/[ANYONE] can match all actors
1516 tgts
= GetAllActors(Sender
, 0);
1522 targetlist::iterator m
;
1523 const targettype
*tt
= tgts
->GetFirstTarget(m
, ST_ACTOR
);
1525 Actor
*actor
= (Actor
*) tt
->actor
;
1526 if (actor
->GetID() == actorID
) {
1530 tt
= tgts
->GetNextTarget(m
, ST_ACTOR
);
1537 int GetObjectCount(Scriptable
* Sender
, Object
* oC
)
1542 // EvaluateObject will return [PC]
1543 // GetAllObjects will also return Myself (evaluates object filters)
1544 // i believe we need the latter here
1545 Targets
* tgts
= GetAllObjects(Sender
->GetCurrentArea(), Sender
, oC
, 0);
1546 int count
= tgts
->Count();
1552 //check numcreaturesatmylevel(myself, 1)
1553 //when the actor is alone
1554 //it should (obviously) return true if the trigger
1555 //evaluates object filters
1556 //also check numcreaturesgtmylevel(myself,0) with
1557 //actor having at high level
1558 int GetObjectLevelCount(Scriptable
* Sender
, Object
* oC
)
1563 // EvaluateObject will return [PC]
1564 // GetAllObjects will also return Myself (evaluates object filters)
1565 // i believe we need the latter here
1566 Targets
* tgts
= GetAllObjects(Sender
->GetCurrentArea(), Sender
, oC
, 0);
1569 targetlist::iterator m
;
1570 const targettype
*tt
= tgts
->GetFirstTarget(m
, ST_ACTOR
);
1572 count
+= ((Actor
*) tt
->actor
)->GetXPLevel(true);
1573 tt
= tgts
->GetNextTarget(m
, ST_ACTOR
);
1580 //we need this because some special characters like _ or * are also accepted
1581 inline bool ismysymbol(const char letter
)
1583 if (letter
==']') return false;
1584 if (letter
=='[') return false;
1585 if (letter
==')') return false;
1586 if (letter
=='(') return false;
1587 if (letter
=='.') return false;
1588 if (letter
==',') return false;
1592 //this function returns a value, symbol could be a numeric string or
1593 //a symbol from idsname
1594 static int GetIdsValue(const char *&symbol
, const char *idsname
)
1596 int idsfile
=core
->LoadSymbol(idsname
);
1597 Holder
<SymbolMgr
> valHook
= core
->GetSymbol(idsfile
);
1599 //FIXME:missing ids file!!!
1600 if (InDebug
&ID_TRIGGERS
) {
1601 printMessage("GameScript"," ",LIGHT_RED
);
1602 printf("Missing IDS file %s for symbol %s!\n",idsname
, symbol
);
1607 int value
=strtol(symbol
, &newsymbol
, 0);
1608 if (symbol
!=newsymbol
) {
1612 char symbolname
[64];
1614 for (x
=0;ismysymbol(*symbol
) && x
<(int) sizeof(symbolname
)-1;x
++) {
1615 symbolname
[x
]=*symbol
;
1619 return valHook
->GetValue(symbolname
);
1622 static void ParseIdsTarget(const char *&src
, Object
*&object
)
1624 for (int i
=0;i
<ObjectFieldsCount
;i
++) {
1625 object
->objectFields
[i
]=GetIdsValue(src
, ObjectIDSTableNames
[i
]);
1634 //this will skip to the next element in the prototype of an action/trigger
1635 #define SKIP_ARGUMENT() while (*str && ( *str != ',' ) && ( *str != ')' )) str++
1637 static void ParseObject(const char *&str
,const char *&src
, Object
*&object
)
1640 object
= new Object();
1646 for (i
=0;i
<(int) sizeof(object
->objectName
)-1 && *src
!='"';i
++)
1648 object
->objectName
[i
] = *src
;
1651 object
->objectName
[i
] = 0;
1656 ParseIdsTarget(src
, object
);
1658 default: //nested object filters
1661 while (Nesting
<MaxObjectNesting
) {
1662 memmove(object
->objectFilters
+1, object
->objectFilters
, (int) sizeof(int) *(MaxObjectNesting
-1) );
1663 object
->objectFilters
[0]=GetIdsValue(src
,"object");
1674 ParseIdsTarget(src
, object
);
1676 src
+=Nesting
; //skipping )
1680 /* this function was lifted from GenerateAction, to make it clearer */
1681 Action
* GenerateActionCore(const char *src
, const char *str
, int acIndex
)
1683 Action
*newAction
= new Action(true);
1684 newAction
->actionID
= (unsigned short) actionsTable
->GetValueIndex( acIndex
);
1685 //this flag tells us to merge 2 consecutive strings together to get
1686 //a variable (context+variablename)
1687 int mergestrings
= actionflags
[newAction
->actionID
]&AF_MERGESTRINGS
;
1688 int objectCount
= ( newAction
->actionID
== 1 ) ? 0 : 1;
1689 int stringsCount
= 0;
1691 if (actionflags
[newAction
->actionID
]&AF_DIRECT
) {
1692 Object
*tmp
= new Object();
1693 tmp
->objectFields
[0] = -1;
1694 //tmp->objectFields[1] = core->GetGameControl()->targetID;
1695 newAction
->objects
[objectCount
++] = tmp
;
1697 //Here is the Action; Now we need to evaluate the parameters, if any
1698 if (*str
!=')') while (*str
) {
1699 if (*(str
+1)!=':') {
1700 printf("Warning, parser was sidetracked: %s\n",str
);
1704 printf("Invalid type: %s\n",str
);
1711 newAction
->pointParameter
.x
= (short) strtol( src
, (char **) &src
, 10 );
1713 newAction
->pointParameter
.y
= (short) strtol( src
, (char **) &src
, 10 );
1719 //going to the variable name
1720 while (*str
!= '*' && *str
!=',' && *str
!= ')' ) {
1724 if (*str
=='*') { //there may be an IDS table
1726 ieVariable idsTabName
;
1727 char* tmp
= idsTabName
;
1728 while (( *str
!= ',' ) && ( *str
!= ')' )) {
1734 if (idsTabName
[0]) {
1735 value
= GetIdsValue(src
, idsTabName
);
1738 value
= strtol( src
, (char **) &src
, 0);
1741 else { //no IDS table
1742 value
= strtol( src
, (char **) &src
, 0);
1745 newAction
->int0Parameter
= value
;
1746 } else if (intCount
== 1) {
1747 newAction
->int1Parameter
= value
;
1749 newAction
->int2Parameter
= value
;
1761 int openParenthesisCount
= 0;
1764 if (!openParenthesisCount
)
1766 openParenthesisCount
--;
1769 openParenthesisCount
++;
1771 if (( *src
== ',' ) &&
1772 !openParenthesisCount
)
1781 Action
* act
= GenerateAction( action
);
1782 act
->objects
[0] = newAction
->objects
[0];
1783 newAction
->objects
[0] = NULL
; //avoid freeing of object
1784 delete newAction
; //freeing action
1790 if (objectCount
==3) {
1791 printf("Invalid object count!\n");
1794 ParseObject(str
, src
, newAction
->objects
[objectCount
++]);
1803 if (!stringsCount
) {
1804 dst
= newAction
->string0Parameter
;
1806 dst
= newAction
->string1Parameter
;
1808 //if there are 3 strings, the first 2 will be merged,
1809 //the last one will be left alone
1813 //skipping the context part, which
1814 //is to be readed later
1823 while (*src
!= '"') {
1824 //sizeof(context+name) = 40
1826 *dst
++ = (char) tolower(*src
);
1832 //reading the context part
1836 printf("Invalid mergestrings:%s\n",str
);
1840 if (!stringsCount
) {
1841 dst
= newAction
->string0Parameter
;
1843 dst
= newAction
->string1Parameter
;
1846 //this works only if there are no spaces
1847 if (*src
++!='"' || *src
++!=',' || *src
++!='"') {
1850 //reading the context string
1852 while (*src
!= '"') {
1854 *dst
++ = (char) tolower(*src
);
1865 if (*src
== ',' || *src
==')')
1871 void GoNear(Scriptable
*Sender
, const Point
&p
)
1873 if (Sender
->GetCurrentAction()) {
1874 printMessage("GameScript","Target busy???\n",LIGHT_RED
);
1878 sprintf( Tmp
, "MoveToPoint([%hd.%hd])", p
.x
, p
.y
);
1879 Action
* action
= GenerateAction( Tmp
);
1880 Sender
->AddActionInFront( action
);
1883 void MoveNearerTo(Scriptable
*Sender
, Scriptable
*target
, int distance
)
1886 Map
*myarea
, *hisarea
;
1888 if (Sender
->Type
!= ST_ACTOR
) {
1889 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED
);
1890 Sender
->ReleaseCurrentAction();
1894 myarea
= Sender
->GetCurrentArea();
1895 hisarea
= target
->GetCurrentArea();
1896 if (hisarea
!=myarea
) {
1897 target
= myarea
->GetTileMap()->GetTravelTo(hisarea
->GetScriptName());
1900 printMessage("GameScript", "MoveNearerTo failed to find an exit\n", YELLOW
);
1901 Sender
->ReleaseCurrentAction();
1904 ((Actor
*) Sender
)->UseExit(true);
1906 ((Actor
*) Sender
)->UseExit(false);
1908 // we deliberately don't try GetLikelyPosition here for now,
1909 // maybe a future idea if we have a better implementation
1910 // (the old code used it - by passing true not 0 below - when target was a movable)
1911 GetPositionFromScriptable(target
, p
, 0);
1913 // account for PersonalDistance (which caller uses, but pathfinder doesn't)
1914 if (distance
&& Sender
->Type
== ST_ACTOR
) {
1915 distance
+= ((Actor
*)Sender
)->size
*10;
1917 if (distance
&& target
->Type
== ST_ACTOR
) {
1918 distance
+= ((Actor
*)target
)->size
*10;
1921 MoveNearerTo(Sender
, p
, distance
, 0);
1924 //It is not always good to release the current action if target is unreachable
1925 //we should also raise the trigger TargetUnreachable (if this is an Attack, at least)
1926 //i hacked only this low level function, didn't need the higher ones so far
1927 int MoveNearerTo(Scriptable
*Sender
, const Point
&p
, int distance
, int dont_release
)
1929 if (Sender
->Type
!= ST_ACTOR
) {
1930 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED
);
1931 Sender
->ReleaseCurrentAction();
1935 Actor
*actor
= (Actor
*)Sender
;
1937 if (!actor
->InMove() || actor
->Destination
!= p
) {
1938 actor
->WalkTo(p
, 0, distance
);
1941 if (!actor
->InMove()) {
1944 return dont_release
;
1946 // we can't walk any nearer to destination, give up
1947 Sender
->ReleaseCurrentAction();
1952 void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool flag, int distance)
1955 GetPositionFromScriptable(target,p,flag);
1956 GoNearAndRetry(Sender, p, distance);
1959 void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance)
1961 if (!Sender->GetCurrentAction() ) {
1962 printMessage("GameScript","NULL action retried???\n",LIGHT_RED);
1965 Sender->AddActionInFront( Sender->GetCurrentAction() );
1967 sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y );
1968 Action * action = GenerateAction( Tmp);
1969 //experimental hack, this value means,
1970 //MoveToPoint shall pop the next action too if movement fails
1971 //and the actor is farther than distance
1972 //this will prevent deadlocks
1973 //(we have to add 1 because otherwise distance==0 fails, we subtract it in MoveToPoint)
1974 action->int0Parameter = distance+1;
1975 Sender->AddActionInFront( action );
1978 void FreeSrc(SrcVector
*poi
, const ieResRef key
)
1980 int res
= SrcCache
.DecRef((void *) poi
, key
, true);
1982 printMessage( "GameScript", "Corrupted Src cache encountered (reference count went below zero), ", LIGHT_RED
);
1983 printf( "Src name is: %.8s\n", key
);
1991 SrcVector
*LoadSrc(const ieResRef resname
)
1993 SrcVector
*src
= (SrcVector
*) SrcCache
.GetResource(resname
);
1997 DataStream
* str
= gamedata
->GetResource( resname
, IE_SRC_CLASS_ID
);
2002 str
->ReadDword(&size
);
2003 src
= new SrcVector(size
);
2004 SrcCache
.SetAt( resname
, (void *) src
);
2007 str
->ReadDword(&tmp
);
2009 str
->ReadDword(&tmp
);
2015 #define MEMCPY(a,b) memcpy((a),(b),sizeof(a) )
2017 static Object
*ObjectCopy(Object
*object
)
2019 if (!object
) return NULL
;
2020 Object
*newObject
= new Object();
2021 MEMCPY( newObject
->objectFields
, object
->objectFields
);
2022 MEMCPY( newObject
->objectFilters
, object
->objectFilters
);
2023 MEMCPY( newObject
->objectRect
, object
->objectRect
);
2024 MEMCPY( newObject
->objectName
, object
->objectName
);
2028 Action
*ParamCopy(Action
*parameters
)
2030 Action
*newAction
= new Action(true);
2031 newAction
->actionID
= parameters
->actionID
;
2032 newAction
->int0Parameter
= parameters
->int0Parameter
;
2033 newAction
->int1Parameter
= parameters
->int1Parameter
;
2034 newAction
->int2Parameter
= parameters
->int2Parameter
;
2035 newAction
->pointParameter
= parameters
->pointParameter
;
2036 MEMCPY( newAction
->string0Parameter
, parameters
->string0Parameter
);
2037 MEMCPY( newAction
->string1Parameter
, parameters
->string1Parameter
);
2038 for (int c
=0;c
<3;c
++) {
2039 newAction
->objects
[c
]= ObjectCopy( parameters
->objects
[c
] );
2044 Action
*ParamCopyNoOverride(Action
*parameters
)
2046 Action
*newAction
= new Action(true);
2047 newAction
->actionID
= parameters
->actionID
;
2048 newAction
->int0Parameter
= parameters
->int0Parameter
;
2049 newAction
->int1Parameter
= parameters
->int1Parameter
;
2050 newAction
->int2Parameter
= parameters
->int2Parameter
;
2051 newAction
->pointParameter
= parameters
->pointParameter
;
2052 MEMCPY( newAction
->string0Parameter
, parameters
->string0Parameter
);
2053 MEMCPY( newAction
->string1Parameter
, parameters
->string1Parameter
);
2054 newAction
->objects
[0]= NULL
;
2055 newAction
->objects
[1]= ObjectCopy( parameters
->objects
[1] );
2056 newAction
->objects
[2]= ObjectCopy( parameters
->objects
[2] );
2060 Trigger
*GenerateTriggerCore(const char *src
, const char *str
, int trIndex
, int negate
)
2062 Trigger
*newTrigger
= new Trigger();
2063 newTrigger
->triggerID
= (unsigned short) triggersTable
->GetValueIndex( trIndex
)&0x3fff;
2064 newTrigger
->flags
= (unsigned short) negate
;
2065 int mergestrings
= triggerflags
[newTrigger
->triggerID
]&TF_MERGESTRINGS
;
2066 int stringsCount
= 0;
2068 //Here is the Trigger; Now we need to evaluate the parameters
2069 if (*str
!=')') while (*str
) {
2070 if (*(str
+1)!=':') {
2071 printf("Warning, parser was sidetracked: %s\n",str
);
2075 printf("Invalid type: %s\n",str
);
2082 newTrigger
->pointParameter
.x
= (short) strtol( src
, (char **) &src
, 10 );
2084 newTrigger
->pointParameter
.y
= (short) strtol( src
, (char **) &src
, 10 );
2090 //going to the variable name
2091 while (*str
!= '*' && *str
!=',' && *str
!= ')' ) {
2095 if (*str
=='*') { //there may be an IDS table
2097 ieVariable idsTabName
;
2098 char* tmp
= idsTabName
;
2099 while (( *str
!= ',' ) && ( *str
!= ')' )) {
2105 if (idsTabName
[0]) {
2106 value
= GetIdsValue(src
, idsTabName
);
2109 value
= strtol( src
, (char **) &src
, 0);
2112 else { //no IDS table
2113 value
= strtol( src
, (char **) &src
, 0);
2116 newTrigger
->int0Parameter
= value
;
2117 } else if (intCount
== 1) {
2118 newTrigger
->int1Parameter
= value
;
2120 newTrigger
->int2Parameter
= value
;
2127 ParseObject(str
, src
, newTrigger
->objectParameter
);
2136 if (!stringsCount
) {
2137 dst
= newTrigger
->string0Parameter
;
2139 dst
= newTrigger
->string1Parameter
;
2141 //skipping the context part, which
2142 //is to be readed later
2151 while (*src
!= '"') {
2152 //sizeof(context+name) = 40
2154 *dst
++ = (char) tolower(*src
);
2160 //reading the context part
2164 printf("Invalid mergestrings:%s\n",str
);
2168 if (!stringsCount
) {
2169 dst
= newTrigger
->string0Parameter
;
2171 dst
= newTrigger
->string1Parameter
;
2174 //this works only if there are no spaces
2175 if (*src
++!='"' || *src
++!=',' || *src
++!='"') {
2178 //reading the context string
2180 while (*src
!= '"') {
2182 *dst
++ = (char) tolower(*src
);
2193 if (*src
== ',' || *src
==')')
2199 void SetVariable(Scriptable
* Sender
, const char* VarName
, const char* Context
, ieDword value
)
2201 char newVarName
[8+33];
2203 if (InDebug
&ID_VARIABLES
) {
2204 printf( "Setting variable(\"%s%s\", %d)\n", Context
,
2207 strncpy( newVarName
, Context
, 6 );
2209 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2210 Sender
->GetCurrentArea()->locals
->SetAt( VarName
, value
);
2213 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2214 Sender
->locals
->SetAt( VarName
, value
);
2217 Game
*game
= core
->GetGame();
2218 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2219 game
->kaputz
->SetAt( VarName
, value
);
2223 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2224 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2226 map
->locals
->SetAt( VarName
, value
);
2228 else if (InDebug
&ID_VARIABLES
) {
2229 printMessage("GameScript"," ",YELLOW
);
2230 printf("Invalid variable %s %s in setvariable\n",Context
, VarName
);
2234 game
->locals
->SetAt( VarName
, ( ieDword
) value
);
2238 void SetVariable(Scriptable
* Sender
, const char* VarName
, ieDword value
)
2242 if (InDebug
&ID_VARIABLES
) {
2243 printf( "Setting variable(\"%s\", %d)\n", VarName
, value
);
2245 strncpy( newVarName
, VarName
, 6 );
2247 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2248 Sender
->GetCurrentArea()->locals
->SetAt( &VarName
[6], value
);
2251 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2252 Sender
->locals
->SetAt( &VarName
[6], value
);
2255 Game
*game
= core
->GetGame();
2256 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2257 game
->kaputz
->SetAt( &VarName
[6], value
);
2260 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2261 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2263 map
->locals
->SetAt( &VarName
[6], value
);
2265 else if (InDebug
&ID_VARIABLES
) {
2266 printMessage("GameScript"," ",YELLOW
);
2267 printf("Invalid variable %s in setvariable\n",VarName
);
2271 game
->locals
->SetAt( &VarName
[6], ( ieDword
) value
);
2275 ieDword
CheckVariable(Scriptable
* Sender
, const char* VarName
, bool *valid
)
2280 strncpy( newVarName
, VarName
, 6 );
2282 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2283 Sender
->GetCurrentArea()->locals
->Lookup( &VarName
[6], value
);
2284 if (InDebug
&ID_VARIABLES
) {
2285 printf("CheckVariable %s: %d\n",VarName
, value
);
2289 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2290 Sender
->locals
->Lookup( &VarName
[6], value
);
2291 if (InDebug
&ID_VARIABLES
) {
2292 printf("CheckVariable %s: %d\n",VarName
, value
);
2296 Game
*game
= core
->GetGame();
2297 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2298 game
->kaputz
->Lookup( &VarName
[6], value
);
2299 if (InDebug
&ID_VARIABLES
) {
2300 printf("CheckVariable %s: %d\n",VarName
, value
);
2304 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2305 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2307 map
->locals
->Lookup( &VarName
[6], value
);
2312 if (InDebug
&ID_VARIABLES
) {
2313 printMessage("GameScript"," ",YELLOW
);
2314 printf("Invalid variable %s in checkvariable\n",VarName
);
2318 game
->locals
->Lookup( &VarName
[6], value
);
2320 if (InDebug
&ID_VARIABLES
) {
2321 printf("CheckVariable %s: %d\n",VarName
, value
);
2326 ieDword
CheckVariable(Scriptable
* Sender
, const char* VarName
, const char* Context
, bool *valid
)
2331 strncpy(newVarName
, Context
, 6);
2333 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2334 Sender
->GetCurrentArea()->locals
->Lookup( VarName
, value
);
2335 if (InDebug
&ID_VARIABLES
) {
2336 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2340 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2341 Sender
->locals
->Lookup( VarName
, value
);
2342 if (InDebug
&ID_VARIABLES
) {
2343 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2347 Game
*game
= core
->GetGame();
2348 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2349 game
->kaputz
->Lookup( VarName
, value
);
2350 if (InDebug
&ID_VARIABLES
) {
2351 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2355 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2356 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2358 map
->locals
->Lookup( VarName
, value
);
2363 if (InDebug
&ID_VARIABLES
) {
2364 printMessage("GameScript"," ",YELLOW
);
2365 printf("Invalid variable %s %s in checkvariable\n",Context
, VarName
);
2369 game
->locals
->Lookup( VarName
, value
);
2371 if (InDebug
&ID_VARIABLES
) {
2372 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2377 int DiffCore(ieDword a
, ieDword b
, int diffmode
)
2395 case GREATER_OR_EQUALS
:
2405 case BINARY_LESS_OR_EQUALS
:
2415 case BINARY_MORE_OR_EQUALS
:
2425 case BINARY_INTERSECT
:
2430 case BINARY_NOT_INTERSECT
:
2435 default: //less or equals
2444 int GetGroup(Actor
*actor
)
2446 int type
= 2; //neutral, has no enemies
2447 if (actor
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2450 if (actor
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2456 Targets
*GetMyTarget(Scriptable
*Sender
, Actor
*actor
, Targets
*parameters
, int ga_flags
)
2459 if (Sender
->Type
==ST_ACTOR
) {
2460 actor
= (Actor
*) Sender
;
2463 parameters
->Clear();
2465 Actor
*target
= actor
->GetCurrentArea()->GetActorByGlobalID(actor
->LastTarget
);
2467 parameters
->AddTarget(target
, 0, ga_flags
);
2473 Targets
*XthNearestDoor(Targets
*parameters
, unsigned int count
)
2476 Scriptable
*origin
= parameters
->GetTarget(0, -1);
2477 parameters
->Clear();
2481 //get the doors based on it
2482 Map
*map
= origin
->GetCurrentArea();
2483 unsigned int i
=(unsigned int) map
->TMap
->GetDoorCount();
2488 Door
*door
= map
->TMap
->GetDoor(i
);
2489 unsigned int dist
= Distance(origin
->Pos
, door
->Pos
);
2490 parameters
->AddTarget(door
, dist
, 0);
2493 //now get the xth door
2494 origin
= parameters
->GetTarget(count
, ST_DOOR
);
2495 parameters
->Clear();
2499 parameters
->AddTarget(origin
, 0, 0);
2503 Targets
*XthNearestOf(Targets
*parameters
, int count
, int ga_flags
)
2508 const targettype
*t
= parameters
->GetLastTarget(ST_ACTOR
);
2511 origin
= parameters
->GetTarget(count
, ST_ACTOR
);
2513 parameters
->Clear();
2517 parameters
->AddTarget(origin
, 0, ga_flags
);
2521 //mygroup means the same specifics as origin
2522 Targets
*XthNearestMyGroupOfType(Scriptable
*origin
, Targets
*parameters
, unsigned int count
, int ga_flags
)
2524 if (origin
->Type
!= ST_ACTOR
) {
2525 parameters
->Clear();
2529 targetlist::iterator m
;
2530 const targettype
*t
= parameters
->GetFirstTarget(m
, ST_ACTOR
);
2534 Actor
*actor
= (Actor
*) origin
;
2535 //determining the specifics of origin
2536 ieDword type
= actor
->GetStat(IE_SPECIFIC
); //my group
2539 if (t
->actor
->Type
!=ST_ACTOR
) {
2540 t
=parameters
->RemoveTargetAt(m
);
2543 Actor
*actor
= (Actor
*) (t
->actor
);
2544 if (actor
->GetStat(IE_SPECIFIC
) != type
) {
2545 t
=parameters
->RemoveTargetAt(m
);
2548 t
= parameters
->GetNextTarget(m
, ST_ACTOR
);
2550 return XthNearestOf(parameters
,count
, ga_flags
);
2553 Targets
*ClosestEnemySummoned(Scriptable
*origin
, Targets
*parameters
, int ga_flags
)
2555 if (origin
->Type
!= ST_ACTOR
) {
2556 parameters
->Clear();
2560 targetlist::iterator m
;
2561 const targettype
*t
= parameters
->GetFirstTarget(m
, ST_ACTOR
);
2565 Actor
*actor
= (Actor
*) origin
;
2566 //determining the allegiance of the origin
2567 int type
= GetGroup(actor
);
2570 parameters
->Clear();
2576 Actor
*tmp
= (Actor
*) (t
->actor
);
2577 if (tmp
->GetStat(IE_SEX
) != SEX_SUMMON
) {
2580 if (type
) { //origin is PC
2581 if (tmp
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2585 if (tmp
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2590 t
= parameters
->GetNextTarget(m
, ST_ACTOR
);
2592 parameters
->Clear();
2593 parameters
->AddTarget(actor
, 0, ga_flags
);
2597 Targets
*XthNearestEnemyOfType(Scriptable
*origin
, Targets
*parameters
, unsigned int count
, int ga_flags
)
2599 if (origin
->Type
!= ST_ACTOR
) {
2600 parameters
->Clear();
2604 targetlist::iterator m
;
2605 const targettype
*t
= parameters
->GetFirstTarget(m
, ST_ACTOR
);
2609 Actor
*actor
= (Actor
*) origin
;
2610 //determining the allegiance of the origin
2611 int type
= GetGroup(actor
);
2614 parameters
->Clear();
2619 if (t
->actor
->Type
!=ST_ACTOR
) {
2620 t
=parameters
->RemoveTargetAt(m
);
2623 Actor
*actor
= (Actor
*) (t
->actor
);
2624 if (type
) { //origin is PC
2625 if (actor
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2626 t
=parameters
->RemoveTargetAt(m
);
2630 if (actor
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2631 t
=parameters
->RemoveTargetAt(m
);
2635 t
= parameters
->GetNextTarget(m
, ST_ACTOR
);
2637 return XthNearestOf(parameters
,count
, ga_flags
);
2640 Targets
*XthNearestEnemyOf(Targets
*parameters
, int count
, int ga_flags
)
2642 Actor
*origin
= (Actor
*) parameters
->GetTarget(0, ST_ACTOR
);
2643 parameters
->Clear();
2647 //determining the allegiance of the origin
2648 int type
= GetGroup(origin
);
2653 Map
*map
= origin
->GetCurrentArea();
2654 int i
= map
->GetActorCount(true);
2657 ac
=map
->GetActor(i
,true);
2658 int distance
= Distance(ac
, origin
);
2659 if (type
) { //origin is PC
2660 if (ac
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2661 parameters
->AddTarget(ac
, distance
, ga_flags
);
2665 if (ac
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2666 parameters
->AddTarget(ac
, distance
, ga_flags
);
2670 return XthNearestOf(parameters
,count
, ga_flags
);
2673 Point
GetEntryPoint(const char *areaname
, const char *entryname
)
2677 AutoTable
tab("entries");
2681 const char *tmpstr
= tab
->QueryField(areaname
, entryname
);
2684 sscanf(tmpstr
, "%d.%d", &x
, &y
);
2690 /* returns a spell's casting distance, it depends on the caster */
2691 unsigned int GetSpellDistance(ieResRef spellres
, Actor
*actor
)
2695 Spell
* spl
= gamedata
->GetSpell( spellres
);
2697 printMessage("GameScript"," ",LIGHT_RED
);
2698 printf("Spell couldn't be found:%.8s.\n", spellres
);
2701 dist
=spl
->GetCastingDistance(actor
);
2702 gamedata
->FreeSpell(spl
, spellres
, false);
2706 /* returns a spell's casting distance, it depends on the caster */
2707 unsigned int GetItemDistance(ieResRef itemres
, int header
)
2711 Item
* itm
= gamedata
->GetItem( itemres
);
2713 printMessage("GameScript"," ",LIGHT_RED
);
2714 printf("Item couldn't be found:%.8s.\n", itemres
);
2717 dist
=itm
->GetCastingDistance(header
);
2718 gamedata
->FreeItem(itm
, itemres
, false);
2722 void SetupWishCore(Scriptable
*Sender
, int column
, int picks
)
2729 AutoTable
tm("wish");
2731 printStatus( "ERROR", LIGHT_RED
);
2732 printf( "Cannot find wish.2da.\n");
2736 selects
= (int *) malloc(picks
*sizeof(int));
2737 count
= tm
->GetRowCount();
2740 snprintf(varname
,32, "wishpower%02d", i
);
2741 if(CheckVariable(Sender
, varname
, "GLOBAL") ) {
2742 SetVariable(Sender
, varname
, "GLOBAL", 0);
2747 for(i
=0;i
<count
;i
++) {
2754 for(i
=0;i
<picks
;i
++) {
2755 selects
[i
]=rand()%count
;
2758 if(selects
[i
]==selects
[j
]) {
2766 for (i
= 0; i
< picks
; i
++) {
2769 int spnum
= atoi( tm
->QueryField( selects
[i
], column
) );
2770 snprintf(varname
,32,"wishpower%02d", spnum
);
2771 SetVariable(Sender
, varname
, "GLOBAL",1);