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.
24 #include "defsounds.h"
28 #include "GameControl.h"
30 #include "Interface.h"
34 #include "StringMgr.h"
40 //these tables will get freed by Core
41 Holder
<SymbolMgr
> triggersTable
;
42 Holder
<SymbolMgr
> actionsTable
;
43 Holder
<SymbolMgr
> objectsTable
;
44 TriggerFunction triggers
[MAX_TRIGGERS
];
45 ActionFunction actions
[MAX_ACTIONS
];
46 short actionflags
[MAX_ACTIONS
];
47 short triggerflags
[MAX_TRIGGERS
];
48 ObjectFunction objects
[MAX_OBJECTS
];
49 IDSFunction idtargets
[MAX_OBJECT_FIELDS
];
50 Cache SrcCache
; //cache for string resources (pst)
51 Cache BcsCache
; //cache for scripts
52 int ObjectIDSCount
= 7;
53 int MaxObjectNesting
= 5;
54 bool HasAdditionalRect
= false;
55 bool HasTriggerPoint
= false;
56 //released by ReleaseMemory
57 ieResRef
*ObjectIDSTableNames
;
58 int ObjectFieldsCount
= 7;
59 int ExtraParametersCount
= 0;
65 // reaction modifiers (by reputation and charisma)
69 void InitScriptTables()
71 //initializing the skill->stats conversion table
73 AutoTable
tab("skillsta");
75 int rowcount
= tab
->GetRowCount();
76 SkillCount
= rowcount
;
78 SkillStats
= (int *) malloc(rowcount
* sizeof(int) );
80 SkillStats
[rowcount
]=strtol(tab
->QueryField(rowcount
,0), NULL
, 0);
85 //initializing the happiness table
87 AutoTable
tab("happy");
89 for (int alignment
=0;alignment
<3;alignment
++) {
90 for (int reputation
=0;reputation
<20;reputation
++) {
91 happiness
[alignment
][reputation
]=strtol(tab
->QueryField(reputation
,alignment
), NULL
, 0);
97 //initializing the reaction mod. reputation table
98 AutoTable
rmr("rmodrep");
100 for (int reputation
=0; reputation
<20; reputation
++) {
101 rmodrep
[reputation
] = strtol(rmr
->QueryField(0, reputation
), NULL
, 0);
105 //initializing the reaction mod. charisma table
106 AutoTable
rmc("rmodchr");
108 for (int charisma
=0; charisma
<25; charisma
++) {
109 rmodchr
[charisma
] = strtol(rmc
->QueryField(0, charisma
), NULL
, 0);
114 int GetReaction(Actor
*target
, Scriptable
*Sender
)
116 int chr
, rep
, reaction
;
117 chr
= target
->GetStat(IE_CHR
)-1;
118 if (target
->GetStat(IE_EA
) == EA_PC
) {
119 rep
= core
->GetGame()->Reputation
/10;
121 rep
= target
->GetStat(IE_REPUTATION
);
123 reaction
= 10 + rmodrep
[rep
] + rmodchr
[chr
];
125 // add -4 penalty when dealing with racial enemies
126 if (Sender
&& target
->GetRangerLevel() && Sender
->Type
== ST_ACTOR
&& target
->IsRacialEnemy((Actor
*)Sender
)) {
133 int GetHappiness(Scriptable
* Sender
, int reputation
)
135 if (Sender
->Type
!= ST_ACTOR
) {
138 Actor
* ab
= ( Actor
* ) Sender
;
139 int alignment
= ab
->GetStat(IE_ALIGNMENT
)&AL_GE_MASK
; //good / evil
140 if (reputation
> 200) {
143 return happiness
[alignment
][reputation
/10-1];
146 int GetHPPercent(Scriptable
* Sender
)
148 if (Sender
->Type
!= ST_ACTOR
) {
151 Actor
* ab
= ( Actor
* ) Sender
;
152 int hp1
= ab
->GetStat(IE_MAXHITPOINTS
);
156 int hp2
= ab
->GetBase(IE_HITPOINTS
);
163 void HandleBitMod(ieDword
&value1
, ieDword value2
, int opcode
)
167 value1
= ( value1
& value2
);
170 value1
= ( value1
| value2
);
173 value1
= ( value1
^ value2
);
175 case BM_NAND
: //this is a GemRB extension
176 value1
= ( value1
& ~value2
);
178 case BM_SET
: //this is a GemRB extension
184 // SPIT is not in the original engine spec, it is reserved for the
185 // enchantable items feature
187 static const char *spell_suffices
[]={"SPIT","SPPR","SPWI","SPIN","SPCL"};
189 //this function handles the polymorphism of Spell[RES] actions
190 //it returns spellres
191 bool ResolveSpellName(ieResRef spellres
, Action
*parameters
)
193 if (parameters
->string0Parameter
[0]) {
194 strnlwrcpy(spellres
, parameters
->string0Parameter
, 8);
197 int type
= parameters
->int0Parameter
/1000;
198 int spellid
= parameters
->int0Parameter
%1000;
202 sprintf(spellres
, "%s%03d", spell_suffices
[type
], spellid
);
204 return gamedata
->Exists(spellres
, IE_SPL_CLASS_ID
);
207 void ResolveSpellName(ieResRef spellres
, ieDword number
)
210 unsigned int type
= number
/1000;
211 int spellid
= number
%1000;
215 sprintf(spellres
, "%s%03d", spell_suffices
[type
], spellid
);
218 ieDword
ResolveSpellNumber(const ieResRef spellres
)
223 if(!strnicmp(spellres
, spell_suffices
[i
], 4)) {
225 sscanf(spellres
+4,"%d", &n
);
235 bool ResolveItemName(ieResRef itemres
, Actor
*act
, ieDword Slot
)
237 CREItem
*itm
= act
->inventory
.GetSlotItem(Slot
);
239 strnlwrcpy(itemres
, itm
->ItemResRef
, 8);
240 return gamedata
->Exists(itemres
, IE_ITM_CLASS_ID
);
245 bool StoreHasItemCore(const ieResRef storename
, const ieResRef itemname
)
247 bool had_nostore
=false;
248 bool has_current
=false;
253 Store
*store
= core
->GetCurrentStore();
256 store
= core
->SetCurrentStore(storename
, NULL
);
258 if (strnicmp(store
->Name
, storename
, 8) ) {
259 //not the current store, we need some dirty hack
261 strnlwrcpy(current
, store
->Name
, 8);
262 strnuprcpy(owner
, store
->GetOwner(), 32);
266 printMessage("GameScript","Store cannot be opened!\n", LIGHT_RED
);
271 //don't use triggers (pst style), it would be possible to create infinite loops
272 if (store
->FindItem(itemname
, false) != (unsigned int)-1) {
276 //setting back old store (this will save our current store)
277 core
->SetCurrentStore(current
, owner
);
278 } else if (had_nostore
) {
279 core
->CloseCurrentStore();
284 //don't pass this point by reference, it is subject to change
285 void ClickCore(Scriptable
*Sender
, Point point
, int type
, int speed
)
287 Map
*map
= Sender
->GetCurrentArea();
289 Sender
->ReleaseCurrentAction();
292 Point p
=map
->TMap
->GetMapSize();
293 if (!p
.PointInside(point
)) {
294 Sender
->ReleaseCurrentAction();
297 Video
*video
= core
->GetVideoDriver();
298 GlobalTimer
*timer
= core
->timer
;
299 timer
->SetMoveViewPort( point
.x
, point
.y
, speed
, true );
301 if (timer
->ViewportIsMoving()) {
302 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
304 Sender
->ReleaseCurrentAction();
308 video
->ConvertToScreen(point
.x
, point
.y
);
309 GameControl
*win
= core
->GetGameControl();
313 video
->MoveMouse(point
.x
, point
.y
);
314 video
->ClickMouse(type
);
315 Sender
->ReleaseCurrentAction();
318 void TransformItemCore(Actor
*actor
, Action
*parameters
, bool onlyone
)
320 int i
= actor
->inventory
.GetSlotCount();
322 CREItem
*item
= actor
->inventory
.GetSlotItem(i
);
326 if (strnicmp(item
->ItemResRef
, parameters
->string0Parameter
, 8) ) {
329 actor
->inventory
.SetSlotItemRes(parameters
->string1Parameter
,i
,parameters
->int0Parameter
,parameters
->int1Parameter
,parameters
->int2Parameter
);
336 //check if an inventory (container or actor) has item (could be recursive ?)
337 bool HasItemCore(Inventory
*inventory
, const ieResRef itemname
, ieDword flags
)
339 if (inventory
->HasItem(itemname
, flags
)) {
342 int i
=inventory
->GetSlotCount();
344 //maybe we could speed this up if we mark bag items with a flags bit
345 CREItem
*itemslot
= inventory
->GetSlotItem(i
);
348 Item
*item
= gamedata
->GetItem(itemslot
->ItemResRef
);
352 if (core
->CanUseItemType(SLOT_BAG
,item
,NULL
) ) {
353 //the store is the same as the item's name
354 ret
= StoreHasItemCore(itemslot
->ItemResRef
, itemname
);
356 gamedata
->FreeItem(item
, itemslot
->ItemResRef
);
364 void DisplayStringCore(Scriptable
* Sender
, int Strref
, int flags
)
368 //no one hears you when you are in the Limbo!
369 if (!Sender
->GetCurrentArea()) {
373 memset(&sb
,0,sizeof(sb
));
374 printf( "Displaying string on: %s\n", Sender
->GetScriptName() );
375 if (flags
& DS_CONST
) {
376 if (Sender
->Type
!=ST_ACTOR
) {
377 printMessage("GameScript","Verbal constant not supported for non actors!\n", LIGHT_RED
);
380 Actor
* actor
= ( Actor
* ) Sender
;
381 if ((ieDword
) Strref
>=VCONST_COUNT
) {
382 printMessage("GameScript","Invalid verbal constant!\n", LIGHT_RED
);
386 int tmp
=(int) actor
->StrRefs
[Strref
];
387 if (tmp
<= 0 || (actor
->GetStat(IE_MC_FLAGS
) & MC_EXPORTABLE
)) {
388 //get soundset based string constant
389 actor
->ResolveStringConstant( sb
.Sound
, (unsigned int) Strref
);
393 //display the verbal constants in the console
394 ieDword charactersubtitles
= 0;
395 core
->GetDictionary()->Lookup("Subtitles", charactersubtitles
);
396 if (charactersubtitles
) {
401 if ((Strref
!= -1) && !sb
.Sound
[0]) {
402 sb
= core
->strings
->GetStringBlock( Strref
);
403 if (sb
.text
[0] && strcmp(sb
.text
," ") && (flags
& DS_CONSOLE
)) {
404 //can't play the sound here, we have to delay action
405 //and for that, we have to know how long the text takes
406 if(flags
&DS_NONAME
) {
407 core
->DisplayString( sb
.text
);
409 core
->DisplayStringName( Strref
, 0xf0f0f0, Sender
, 0);
412 if (sb
.text
[0] && strcmp(sb
.text
," ") && (flags
& (DS_HEAD
| DS_AREA
))) {
413 Sender
->DisplayHeadText( sb
.text
);
414 //don't free sb.text, it is residing in Sender
415 if (flags
& DS_AREA
) {
416 Sender
->FixHeadTextPos();
419 core
->FreeString( sb
.text
);
422 if (sb
.Sound
[0] && !(flags
&DS_SILENT
) ) {
423 ieDword speech
= GEM_SND_RELATIVE
; //disable position
424 if (flags
&DS_SPEECH
) speech
|=GEM_SND_SPEECH
;
425 ieDword len
= core
->GetAudioDrv()->Play( sb
.Sound
,0,0,speech
);
426 ieDword counter
= ( AI_UPDATE_TIME
* len
) / 1000;
427 if ((counter
!= 0) && (flags
&DS_WAIT
) )
428 Sender
->SetWait( counter
);
432 int CanSee(Scriptable
* Sender
, Scriptable
* target
, bool range
, int seeflag
)
436 if (target
->Type
==ST_ACTOR
) {
437 Actor
*tar
= (Actor
*) target
;
439 if (!tar
->ValidTarget(seeflag
)) {
444 map
= target
->GetCurrentArea();
445 if (!(seeflag
&GA_GLOBAL
)) {
446 if ( map
!=Sender
->GetCurrentArea() ) {
454 if (Sender
->Type
== ST_ACTOR
) {
455 Actor
* snd
= ( Actor
* ) Sender
;
456 dist
= snd
->Modified
[IE_VISUALRANGE
];
461 if (Distance(target
->Pos
, Sender
->Pos
) > dist
* 15) {
466 return map
->IsVisible(target
->Pos
, Sender
->Pos
);
469 //non actors can see too (reducing function to LOS)
470 //non actors can be seen too (reducing function to LOS)
471 int SeeCore(Scriptable
* Sender
, Trigger
* parameters
, int justlos
)
476 if (parameters
->int0Parameter
) {
481 Scriptable
* tar
= GetActorFromObject( Sender
, parameters
->objectParameter
, flags
);
482 /* don't set LastSeen if this isn't an actor */
487 if (CanSee(Sender
, tar
, true, flags
) ) {
491 if (Sender
->Type
==ST_ACTOR
&& tar
->Type
==ST_ACTOR
) {
492 Actor
* snd
= ( Actor
* ) Sender
;
493 //additional checks for invisibility?
494 snd
->LastSeen
= ((Actor
*) tar
)->GetID();
501 //transfering item from Sender to target
502 //if target has no inventory, the item will be destructed
503 //if target can't get it, it will be dropped at its feet
504 int MoveItemCore(Scriptable
*Sender
, Scriptable
*target
, const char *resref
, int flags
, int setflag
)
508 // track whether we are dealing with our party and need to display feedback
509 bool lostitem
= false;
510 bool gotitem
= false;
515 map
=Sender
->GetCurrentArea();
516 switch(Sender
->Type
) {
518 myinv
=&((Actor
*) Sender
)->inventory
;
519 if (((Actor
*)Sender
)->InParty
) lostitem
= true;
522 myinv
=&((Container
*) Sender
)->inventory
;
528 myinv
->RemoveItem(resref
, flags
, &item
);
530 // nothing was removed
534 item
->Flags
|=setflag
;
536 switch(target
->Type
) {
538 myinv
=&((Actor
*) target
)->inventory
;
539 if (((Actor
*) target
)->InParty
) gotitem
= true;
542 myinv
=&((Container
*) target
)->inventory
;
550 if (lostitem
) core
->DisplayConstantString(STR_LOSTITEM
, 0xbcefbc);
551 return MIC_GOTITEM
; // actually it was lost, not gained
553 if ( myinv
->AddSlotItem(item
, SLOT_ONLYINVENTORY
) !=ASI_SUCCESS
) {
554 // drop it at my feet
555 map
->AddItemToLocation(target
->Pos
, item
);
556 if (gotitem
) core
->DisplayConstantString(STR_INVFULL_ITEMDROP
, 0xbcefbc);
559 if (gotitem
) core
->DisplayConstantString(STR_GOTITEM
, 0xbcefbc);
563 static Targets
* ReturnActorAsTarget(Actor
*aC
)
568 //Ok :) we now have our Object. Let's create a Target struct and add the object to it
569 Targets
*tgts
= new Targets( );
570 tgts
->AddTarget( aC
, 0, 0 );
571 //return here because object name/IDS targeting are mutually exclusive
575 Actor
*FindActorNearby(const char *name
, Map
*except
, int ga_flags
)
577 Game
*game
= core
->GetGame();
578 size_t mc
= game
->GetLoadedMapCount();
580 Map
*map
= game
->GetMap(mc
);
581 if (map
==except
) continue;
582 Actor
* aC
= map
->GetActor(name
, ga_flags
);
590 /* returns actors that match the [x.y.z] expression */
591 static Targets
* EvaluateObject(Map
*map
, Scriptable
* Sender
, Object
* oC
, int ga_flags
)
593 if (oC
->objectName
[0]) {
594 //We want the object by its name... (doors/triggers don't play here!)
595 Actor
* aC
= map
->GetActor( oC
->objectName
, ga_flags
);
597 if (!aC
&& (ga_flags
&GA_GLOBAL
) ) {
598 aC
= FindActorNearby(oC
->objectName
, map
, ga_flags
);
601 return ReturnActorAsTarget(aC
);
604 if (oC
->objectFields
[0]==-1) {
605 Actor
* aC
= map
->GetActorByGlobalID( (ieDword
) oC
->objectFields
[1] );
606 /* TODO: this hack will throw away an invalid target */
607 /* Consider putting this in GetActorByGlobalID */
608 if (aC
&& !aC
->ValidTarget(ga_flags
)) {
611 return ReturnActorAsTarget(aC
);
616 //else branch, IDS targeting
617 for (int j
= 0; j
< ObjectIDSCount
; j
++) {
618 if (!oC
->objectFields
[j
]) {
621 IDSFunction func
= idtargets
[j
];
623 printf("Unimplemented IDS targeting opcode!\n");
627 //we already got a subset of actors
628 int i
= tgts
->Count();
629 /*premature end, filtered everything*/
631 break; //leaving the loop
633 targetlist::iterator m
;
634 const targettype
*t
= tgts
->GetFirstTarget(m
, -1);
636 if (t
->actor
->Type
!=ST_ACTOR
) {
637 //we should never stumble here
639 // t = tgts->RemoveTargetAt(m);
642 if (!func( (Actor
*) (t
->actor
), oC
->objectFields
[j
] ) ) {
643 t
= tgts
->RemoveTargetAt(m
);
646 t
= tgts
->GetNextTarget(m
, -1);
649 //we need to get a subset of actors from the large array
650 //if this gets slow, we will need some index tables
651 int i
= map
->GetActorCount(true);
652 tgts
= new Targets();
654 Actor
*ac
=map
->GetActor(i
,true);
655 int dist
= Distance(Sender
->Pos
, ac
->Pos
);
656 if (ac
&& func(ac
, oC
->objectFields
[j
]) ) {
657 // don't return Sender in IDS targeting!
659 tgts
->AddTarget((Scriptable
*) ac
, dist
, ga_flags
);
668 Targets
* GetAllObjects(Map
*map
, Scriptable
* Sender
, Object
* oC
, int ga_flags
)
673 Targets
* tgts
= EvaluateObject(map
, Sender
, oC
, ga_flags
);
674 //if we couldn't find an endpoint by name or object qualifiers
675 //it is not an Actor, but could still be a Door or Container (scriptable)
676 if (!tgts
&& oC
->objectName
[0]) {
679 //now lets do the object filter stuff, we create Targets because
680 //it is possible to start from blank sheets using endpoint filters
681 //like (Myself, Protagonist etc)
683 tgts
= new Targets();
685 for (int i
= 0; i
< MaxObjectNesting
; i
++) {
686 int filterid
= oC
->objectFilters
[i
];
690 ObjectFunction func
= objects
[filterid
];
692 tgts
= func( Sender
, tgts
, ga_flags
);
695 printMessage("GameScript"," ", YELLOW
);
696 printf("Unknown object filter: %d %s\n",filterid
, objectsTable
->GetValue(filterid
) );
698 if (!tgts
->Count()) {
706 Targets
*GetAllObjectsNearby(Scriptable
* Sender
, Object
* oC
, int ga_flags
)
708 Game
*game
= core
->GetGame();
709 size_t mc
= game
->GetLoadedMapCount();
711 Map
*map
= game
->GetMap(mc
);
712 if (map
==Sender
->GetCurrentArea()) continue;
713 Targets
*tgts
= GetAllObjects(map
, Sender
, oC
, ga_flags
);
721 Targets
*GetAllActors(Scriptable
*Sender
, int ga_flags
)
723 Map
*map
=Sender
->GetCurrentArea();
725 int i
= map
->GetActorCount(true);
726 Targets
*tgts
= new Targets();
728 Actor
*ac
=map
->GetActor(i
,true);
729 int dist
= Distance(Sender
->Pos
, ac
->Pos
);
730 tgts
->AddTarget((Scriptable
*) ac
, dist
, ga_flags
);
735 Scriptable
*GetActorObject(TileMap
*TMap
, const char *name
)
738 Scriptable
* aC
= TMap
->GetDoor( name
);
743 //containers should have a precedence over infopoints because otherwise
744 //AR1512 sanity test quest would fail
745 //If this order couldn't be maintained, then 'Contains' should have a
746 //unique call to get containers only
747 //No... it was not an door... maybe a Container?
748 aC
= TMap
->GetContainer( name
);
753 //No... it was not a container ... maybe an InfoPoint?
754 aC
= TMap
->GetInfoPoint( name
);
761 // blocking actions need to store some kinds of objects between ticks
762 Scriptable
* GetStoredActorFromObject(Scriptable
* Sender
, Object
* oC
, int ga_flags
)
764 Scriptable
*tar
= NULL
;
765 // retrieve an existing target if it still exists and is valid
766 if (Sender
->CurrentActionTarget
) {
767 tar
= core
->GetGame()->GetActorByGlobalID(Sender
->CurrentActionTarget
);
769 // always an actor, check if it satisfies flags
770 if (((Actor
*)tar
)->ValidTarget(ga_flags
)) {
774 return NULL
; // target invalid/gone
776 tar
= GetActorFromObject(Sender
, oC
, ga_flags
);
777 // maybe store the target if it's an actor..
778 if (tar
&& tar
->Type
== ST_ACTOR
) {
779 // .. but we only want objects created via objectFilters
780 if (oC
->objectFilters
[0]) {
781 Sender
->CurrentActionTarget
= ((Actor
*)tar
)->globalID
;
787 Scriptable
* GetActorFromObject(Scriptable
* Sender
, Object
* oC
, int ga_flags
)
789 Scriptable
*aC
= NULL
;
794 Targets
*tgts
= GetAllObjects(Sender
->GetCurrentArea(), Sender
, oC
, ga_flags
);
796 //now this could return other than actor objects
797 aC
= tgts
->GetTarget(0,-1);
799 if (!aC
&& (ga_flags
&GA_GLOBAL
) )
801 tgts
= GetAllObjectsNearby(Sender
, oC
, ga_flags
);
803 //now this could return other than actor objects
804 aC
= tgts
->GetTarget(0,-1);
811 if (oC
->objectName
[0]) {
812 aC
= GetActorObject(Sender
->GetCurrentArea()->GetTileMap(), oC
->objectName
);
816 Game
*game
= core
->GetGame();
818 //global actors are always found by scripting name!
819 aC
= game
->FindPC(oC
->objectName
);
823 aC
= game
->FindNPC(oC
->objectName
);
828 if (ga_flags
&GA_GLOBAL
) {
829 size_t mc
= game
->GetLoadedMapCount();
831 Map
*map
= game
->GetMap(mc
);
832 if (map
==Sender
->GetCurrentArea()) continue;
833 aC
= GetActorObject(map
->GetTileMap(), oC
->objectName
);
843 /*FIXME: what is 'base'*/
844 void PolymorphCopyCore(Actor
*src
, Actor
*tar
, bool base
)
846 tar
->SetBase(IE_ANIMATION_ID
, src
->GetStat(IE_ANIMATION_ID
) );
848 tar
->SetBase(IE_ARMOR_TYPE
, src
->GetStat(IE_ARMOR_TYPE
) );
849 for (int i
=0;i
<7;i
++) {
850 tar
->SetBase(IE_COLORS
+i
, src
->GetStat(IE_COLORS
+i
) );
853 tar
->SetName(src
->GetName(0),0);
854 tar
->SetName(src
->GetName(1),1);
855 //add more attribute copying
858 void CreateCreatureCore(Scriptable
* Sender
, Action
* parameters
, int flags
)
860 Scriptable
*tmp
= GetActorFromObject( Sender
, parameters
->objects
[1] );
861 //if there is nothing to copy, don't spawn anything
862 if (flags
& CC_COPY
) {
863 if (!tmp
|| tmp
->Type
!= ST_ACTOR
) {
869 if (flags
& CC_STRING1
) {
870 ab
= gamedata
->GetCreature(parameters
->string1Parameter
);
873 ab
= gamedata
->GetCreature(parameters
->string0Parameter
);
877 printMessage("GameScript","Failed to create creature! ",LIGHT_RED
);
878 printf("(missing creature file %s?)\n", parameters
->string0Parameter
);
879 // maybe this should abort()?
883 //iwd2 allows an optional scriptname to be set
884 //but bg2 doesn't have this feature
885 //this way it works for both games
886 if ((flags
& CC_SCRIPTNAME
) && parameters
->string1Parameter
[0]) {
887 ab
->SetScriptName(parameters
->string1Parameter
);
894 switch (flags
& CC_MASK
) {
895 //creates creature just off the screen
898 Region vp
= core
->GetVideoDriver()->GetViewport();
899 radius
=vp
.w
/2; //actually it must be further divided by the tile size, hmm 16?
902 case CC_OBJECT
://use object + offset
905 case CC_OFFSET
://use sender + offset
906 pnt
.x
= parameters
->pointParameter
.x
+Sender
->Pos
.x
;
907 pnt
.y
= parameters
->pointParameter
.y
+Sender
->Pos
.y
;
909 default: //absolute point, but -1,-1 means AtFeet
910 pnt
.x
= parameters
->pointParameter
.x
;
911 pnt
.y
= parameters
->pointParameter
.y
;
913 pnt
.x
= Sender
->Pos
.x
;
914 pnt
.y
= Sender
->Pos
.y
;
919 Map
*map
= Sender
->GetCurrentArea();
921 ab
->SetPosition( pnt
, flags
&CC_CHECK_IMPASSABLE
, radius
);
922 ab
->SetOrientation(parameters
->int0Parameter
, false );
924 //if string1 is animation, then we can't use it for a DV too
925 if (flags
& CC_PLAY_ANIM
) {
926 CreateVisualEffectCore( ab
, ab
->Pos
, parameters
->string1Parameter
, 1);
928 //setting the deathvariable if it exists (iwd2)
929 if (parameters
->string1Parameter
[0]) {
930 ab
->SetScriptName(parameters
->string1Parameter
);
934 if (flags
& CC_COPY
) {
935 PolymorphCopyCore ( (Actor
*) tmp
, ab
, false);
939 static ScriptedAnimation
*GetVVCEffect(const char *effect
, int iterations
)
942 ScriptedAnimation
* vvc
= gamedata
->GetScriptedAnimation(effect
, false);
944 printMessage("GameScript","Failed to create effect.",LIGHT_RED
);
948 vvc
->SetDefaultDuration( vvc
->GetSequenceDuration(AI_UPDATE_TIME
* iterations
));
955 void CreateVisualEffectCore(Actor
*target
, const char *effect
, int iterations
)
957 ScriptedAnimation
*vvc
= GetVVCEffect(effect
, iterations
);
959 target
->AddVVCell( vvc
);
963 void CreateVisualEffectCore(Scriptable
*Sender
, const Point
&position
, const char *effect
, int iterations
)
965 ScriptedAnimation
*vvc
= GetVVCEffect(effect
, iterations
);
967 vvc
->XPos
+=position
.x
;
968 vvc
->YPos
+=position
.y
;
969 Sender
->GetCurrentArea( )->AddVVCell( vvc
);
973 //this destroys the current actor and replaces it with another
974 void ChangeAnimationCore(Actor
*src
, const char *resref
, bool effect
)
976 Actor
*tar
= gamedata
->GetCreature(resref
);
978 Map
*map
= src
->GetCurrentArea();
979 map
->AddActor( tar
);
980 Point pos
= src
->Pos
;
981 tar
->SetOrientation(src
->GetOrientation(), false );
983 // can't SetPosition while the old actor is taking the spot
984 tar
->SetPosition(pos
, 1);
986 CreateVisualEffectCore(tar
, tar
->Pos
,"smokepuffeffect",1);
991 void EscapeAreaCore(Scriptable
* Sender
, const Point
&p
, const char* area
, const Point
&enter
, int flags
, int wait
)
995 if ( !p
.isempty() && PersonalDistance(p
, Sender
)>MAX_OPERATING_DISTANCE
) {
996 //MoveNearerTo will return 0, if the actor is in move
997 //it will return 1 (the fourth parameter) if the target is unreachable
998 if (!MoveNearerTo(Sender
, p
, MAX_OPERATING_DISTANCE
,1) ) {
999 if (!Sender
->InMove()) printf("At least it said so...\n");
1004 if (flags
&EA_DESTROY
) {
1005 //this must be put into a non-const variable
1006 sprintf( Tmp
, "DestroySelf()" );
1008 // last parameter is 'face', which should be passed from relevant action parameter..
1009 sprintf( Tmp
, "MoveBetweenAreas(\"%s\",[%hd.%hd],%d)", area
, enter
.x
, enter
.y
, 0 );
1011 printMessage("GSUtils"," ", WHITE
);
1012 printf("Executing %s in EscapeAreaCore\n", Tmp
);
1013 //drop this action, but add another (destroyself or movebetweenareas)
1014 //between the arrival and the final escape, there should be a wait time
1015 //that wait time could be handled here
1017 printf("But wait a bit... (%d)\n", wait
);
1018 Sender
->SetWait(wait
);
1020 Sender
->ReleaseCurrentAction();
1021 Action
* action
= GenerateAction( Tmp
);
1022 Sender
->AddActionInFront( action
);
1025 void GetTalkPositionFromScriptable(Scriptable
* scr
, Point
&position
)
1027 switch (scr
->Type
) {
1028 case ST_AREA
: case ST_GLOBAL
:
1029 position
= scr
->Pos
; //fake
1032 //if there are other moveables, put them here
1033 position
= ((Movable
*) scr
)->GetMostLikelyPosition();
1035 case ST_TRIGGER
: case ST_PROXIMITY
: case ST_TRAVEL
:
1036 if (((InfoPoint
*) scr
)->Flags
& TRAP_USEPOINT
) {
1037 position
=((InfoPoint
*) scr
)->UsePoint
;
1040 position
=((InfoPoint
*) scr
)->TrapLaunch
;
1042 case ST_DOOR
: case ST_CONTAINER
:
1043 position
=((Highlightable
*) scr
)->TrapLaunch
;
1048 void GetPositionFromScriptable(Scriptable
* scr
, Point
&position
, bool dest
)
1051 position
= scr
->Pos
;
1054 switch (scr
->Type
) {
1055 case ST_AREA
: case ST_GLOBAL
:
1056 position
= scr
->Pos
; //fake
1059 //if there are other moveables, put them here
1060 position
= ((Movable
*) scr
)->GetMostLikelyPosition();
1062 case ST_TRIGGER
: case ST_PROXIMITY
: case ST_TRAVEL
:
1063 if (((InfoPoint
*) scr
)->Flags
& TRAP_USEPOINT
) {
1064 position
=((InfoPoint
*) scr
)->UsePoint
;
1067 case ST_DOOR
: case ST_CONTAINER
:
1068 position
=((Highlightable
*) scr
)->TrapLaunch
;
1072 int CheckInteract(const char *talker
, const char *target
)
1074 AutoTable
interact("interact");
1077 const char *value
= interact
->QueryField(talker
, target
);
1084 return I_COMPLIMENT
;
1091 static ieResRef PlayerDialogRes
= "PLAYERx\0";
1093 void BeginDialog(Scriptable
* Sender
, Action
* parameters
, int Flags
)
1095 Scriptable
* tar
, *scr
;
1096 int seeflag
= GA_NO_DEAD
;
1098 if (InDebug
&ID_VARIABLES
) {
1099 printf("BeginDialog core\n");
1101 if (Flags
& BD_OWN
) {
1102 tar
= GetStoredActorFromObject( Sender
, parameters
->objects
[1], seeflag
);
1105 tar
= GetStoredActorFromObject( Sender
, parameters
->objects
[1], seeflag
);
1109 printMessage("GameScript"," ",LIGHT_RED
);
1110 printf("Speaker for dialog couldn't be found (Sender: %s, Type: %d) Flags:%d.\n", Sender
->GetScriptName(), Sender
->Type
, Flags
);
1111 Sender
->ReleaseCurrentAction();
1115 if (!tar
|| tar
->Type
!=ST_ACTOR
) {
1116 printMessage("GameScript"," ",LIGHT_RED
);
1117 printf("Target for dialog couldn't be found (Sender: %s, Type: %d).\n", Sender
->GetScriptName(), Sender
->Type
);
1118 if (Sender
->Type
== ST_ACTOR
) {
1119 ((Actor
*) Sender
)->DebugDump();
1121 printf ("Target object: ");
1122 if (parameters
->objects
[1]) {
1123 parameters
->objects
[1]->Dump();
1127 Sender
->ReleaseCurrentAction();
1131 Actor
*speaker
, *target
;
1134 target
= (Actor
*) tar
;
1135 if ((Flags
& BD_CHECKDIST
) && !CanSee(scr
, target
, false, seeflag
) ) {
1136 printMessage("GameScript"," ",LIGHT_RED
);
1137 printf("CanSee returned false! Speaker (%s, type %d) and target are:\n", scr
->GetScriptName(), scr
->Type
);
1138 if (scr
->Type
== ST_ACTOR
) {
1139 ((Actor
*) scr
)->DebugDump();
1141 ((Actor
*) tar
)->DebugDump();
1142 Sender
->ReleaseCurrentAction();
1146 if (scr
->Type
==ST_ACTOR
) {
1147 speaker
= (Actor
*) scr
;
1148 if (speaker
->GetStat(IE_STATE_ID
)&STATE_DEAD
) {
1149 printMessage("GameScript"," ",LIGHT_RED
);
1150 printf("Speaker is dead, cannot start dialogue. Speaker and target are:\n");
1151 speaker
->DebugDump();
1152 target
->DebugDump();
1153 Sender
->ReleaseCurrentAction();
1156 ieDword range
= MAX_OPERATING_DISTANCE
;
1157 //making sure speaker is the protagonist, player, actor
1158 if ( target
->InParty
== 1) swap
= true;
1159 else if ( speaker
->InParty
!=1 && target
->InParty
) swap
= true;
1160 //CHECKDIST works only for mobile scriptables
1161 if (Flags
&BD_CHECKDIST
) {
1162 if ( scr
->GetCurrentArea()!=target
->GetCurrentArea() ||
1163 PersonalDistance(scr
, target
)>range
) {
1164 MoveNearerTo(Sender
, target
, MAX_OPERATING_DISTANCE
);
1169 //pst style dialog with trigger points
1171 if (Flags
&BD_CHECKDIST
) {
1174 if (target
->InMove()) {
1175 //waiting for target
1176 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
1177 Sender
->ReleaseCurrentAction();
1181 GetTalkPositionFromScriptable(scr
, TalkPos
);
1182 if (PersonalDistance(TalkPos
, target
)>MAX_OPERATING_DISTANCE
) {
1183 //try to force the target to come closer???
1184 GoNear(target
, TalkPos
);
1185 Sender
->AddActionInFront( Sender
->GetCurrentAction() );
1186 Sender
->ReleaseCurrentAction();
1193 GameControl
* gc
= core
->GetGameControl();
1195 printMessage( "GameScript","Dialog cannot be initiated because there is no GameControl.", YELLOW
);
1196 Sender
->ReleaseCurrentAction();
1199 //can't initiate dialog, because it is already there
1200 if (gc
->GetDialogueFlags()&DF_IN_DIALOG
) {
1201 if (Flags
& BD_INTERRUPT
) {
1202 //break the current dialog if possible
1203 gc
->EndDialog(true);
1205 //check if we could manage to break it, not all dialogs are breakable!
1206 if (gc
->GetDialogueFlags()&DF_IN_DIALOG
) {
1207 printMessage( "GameScript","Dialog cannot be initiated because there is already one.", YELLOW
);
1208 Sender
->ReleaseCurrentAction();
1213 // starting a dialog ends cutscenes!
1214 core
->SetCutSceneMode(false);
1216 const char* Dialog
= NULL
;
1219 switch (Flags
& BD_LOCMASK
) {
1221 Dialog
= parameters
->string0Parameter
;
1222 if (Flags
& BD_SETDIALOG
) {
1223 scr
->SetDialog( Dialog
);
1228 if (swap
) Dialog
= scr
->GetDialog();
1229 else Dialog
= target
->GetDialog(GD_FEEDBACK
);
1232 //what if playerdialog was initiated from Player2?
1233 PlayerDialogRes
[5] = '1';
1234 Dialog
= ( const char * ) PlayerDialogRes
;
1236 case BD_INTERACT
: //using the source for the dialog
1237 const char* scriptingname
= scr
->GetScriptName();
1239 /* use interact.2da for short, inlined dialogue */
1240 int type
= CheckInteract(scriptingname
, target
->GetScriptName());
1242 speaker
->Interact(type
);
1243 target
->Response(type
);
1244 Sender
->ReleaseCurrentAction();
1247 /* banter dialogue */
1248 pdtable
.load("interdia");
1249 //Dialog is a borrowed reference, we cannot free pdtable while it is being used
1251 Dialog
= pdtable
->QueryField( scriptingname
, "FILE" );
1257 //dialog is not meaningful
1258 if (!Dialog
|| Dialog
[0]=='*') {
1259 Sender
->ReleaseCurrentAction();
1263 //maybe we should remove the action queue, but i'm unsure
1264 //no, we shouldn't even call this!
1265 //Sender->ReleaseCurrentAction();
1267 // moved this here from InitDialog, because InitDialog doesn't know which side is which
1268 // post-swap (and non-actors always have IF_NOINT set) .. also added a check that it's
1269 // actually busy doing something, for the same reason
1270 if (target
->GetInternalFlag()&IF_NOINT
&& (target
->GetCurrentAction() || target
->GetNextAction())) {
1271 core
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
1272 Sender
->ReleaseCurrentAction();
1276 if (speaker
!=target
) {
1278 Scriptable
*tmp
= tar
;
1282 if (!(Flags
& BD_INTERRUPT
)) {
1283 // added CurrentAction as part of blocking action fixes
1284 if (tar
->GetCurrentAction() || tar
->GetNextAction()) {
1285 core
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
1286 Sender
->ReleaseCurrentAction();
1293 //don't clear target's actions, because a sequence like this will be broken:
1294 //StartDialog([PC]); SetGlobal("Talked","LOCALS",1);
1296 if (scr
->Type
==ST_ACTOR
) {
1297 ((Actor
*) scr
)->SetOrientation(GetOrient( tar
->Pos
, scr
->Pos
), true);
1299 if (tar
->Type
==ST_ACTOR
) {
1300 ((Actor
*) tar
)->SetOrientation(GetOrient( scr
->Pos
, tar
->Pos
), true);
1307 //increasing NumTimesTalkedTo or NumTimesInteracted
1308 if (Flags
& BD_TALKCOUNT
) {
1309 gc
->SetDialogueFlags(DF_TALKCOUNT
, BM_OR
);
1310 } else if ((Flags
& BD_LOCMASK
) == BD_INTERACT
) {
1311 gc
->SetDialogueFlags(DF_INTERACT
, BM_OR
);
1314 core
->GetDictionary()->SetAt("DialogChoose",(ieDword
) -1);
1315 ret
= gc
->InitDialog( scr
, tar
, Dialog
);
1322 Sender
->ReleaseCurrentAction();
1323 if (Flags
& BD_NOEMPTY
) {
1326 core
->DisplayConstantStringName(STR_NOTHINGTOSAY
,0xff0000,tar
);
1330 //this is a bit fishy
1332 Sender
->ReleaseCurrentAction();
1336 void MoveBetweenAreasCore(Actor
* actor
, const char *area
, const Point
&position
, int face
, bool adjust
)
1338 printMessage("GameScript", " ", WHITE
);
1339 printf("MoveBetweenAreas: %s to %s [%d.%d] face: %d\n", actor
->GetName(0), area
,position
.x
,position
.y
, face
);
1341 Game
* game
= core
->GetGame();
1342 if (area
[0]) { //do we need to switch area?
1343 Map
* map1
= actor
->GetCurrentArea();
1344 //we have to change the pathfinder
1345 //to the target area if adjust==true
1346 map2
= game
->GetMap(area
, false);
1349 map1
->RemoveActor( actor
);
1351 map2
->AddActor( actor
);
1354 actor
->SetPosition(position
, adjust
);
1356 actor
->SetOrientation( face
, false );
1358 // should this perhaps be a 'selected' check or similar instead?
1359 if (actor
->InParty
) {
1360 GameControl
*gc
=core
->GetGameControl();
1361 gc
->SetScreenFlags(SF_CENTERONACTOR
,BM_OR
);
1365 //repeat movement, until goal isn't reached
1366 //if int0parameter is !=0, then it will try only x times
1367 void MoveToObjectCore(Scriptable
*Sender
, Action
*parameters
, ieDword flags
, bool untilsee
)
1369 if (Sender
->Type
!= ST_ACTOR
) {
1370 Sender
->ReleaseCurrentAction();
1373 Scriptable
* target
= GetStoredActorFromObject( Sender
, parameters
->objects
[1] );
1375 Sender
->ReleaseCurrentAction();
1378 Actor
* actor
= ( Actor
* ) Sender
;
1379 if (untilsee
&& CanSee(actor
, target
, true, 0) ) {
1380 Sender
->ReleaseCurrentAction();
1383 if (PersonalDistance(actor
, target
)<MAX_OPERATING_DISTANCE
) {
1384 Sender
->ReleaseCurrentAction();
1388 if (!actor
->InMove() || actor
->Destination
!= target
->Pos
) {
1389 actor
->WalkTo( target
->Pos
, flags
, 0 );
1391 //hopefully this hack will prevent lockups
1392 if (!actor
->InMove()) {
1393 Sender
->ReleaseCurrentAction();
1397 //repeat movement...
1398 Action
*newaction
= ParamCopyNoOverride(parameters
);
1399 if (newaction
->int0Parameter
!=1) {
1400 if (newaction
->int0Parameter
) {
1401 newaction
->int0Parameter
--;
1403 actor
->AddActionInFront(newaction
);
1407 Sender
->ReleaseCurrentAction();
1410 void CreateItemCore(CREItem
*item
, const char *resref
, int a
, int b
, int c
)
1412 strncpy(item
->ItemResRef
, resref
, 8);
1413 core
->ResolveRandomItem(item
);
1415 Item
*origitem
= gamedata
->GetItem(resref
);
1416 for(int i
=0;i
<3;i
++) {
1417 ITMExtHeader
*e
= origitem
->GetExtHeader(i
);
1418 item
->Usages
[i
]=e
?e
->Charges
:0;
1420 gamedata
->FreeItem(origitem
, resref
, false);
1422 item
->Usages
[0]=(ieWord
) a
;
1423 item
->Usages
[1]=(ieWord
) b
;
1424 item
->Usages
[2]=(ieWord
) c
;
1429 //It is possible to attack CONTAINERS/DOORS as well!!!
1430 void AttackCore(Scriptable
*Sender
, Scriptable
*target
, int flags
)
1432 //this is a dangerous cast, make sure actor is Actor * !!!
1433 Actor
*actor
= (Actor
*) Sender
;
1436 ITMExtHeader
*header
= NULL
;
1437 ITMExtHeader
*hittingheader
= NULL
;
1440 int DamageBonus
, CriticalBonus
;
1443 //bool leftorright = (bool) ((attacksperround-attackcount)&1);
1444 bool leftorright
= false;
1446 //will return false on any errors (eg, unusable weapon)
1447 if (!actor
->GetCombatDetails(tohit
, leftorright
, wi
, header
, hittingheader
, Flags
, DamageBonus
, speed
, CriticalBonus
, style
)) {
1448 Sender
->ReleaseCurrentAction();
1452 if (header
) wi
.range
*= 10;
1455 if ( target
->Type
== ST_DOOR
|| target
->Type
== ST_CONTAINER
) {
1459 ieDword targetID
= 0;
1460 if (target
->Type
==ST_ACTOR
) {
1461 tar
= (Actor
*) target
;
1462 targetID
= tar
->GetID();
1465 Sender
->ReleaseCurrentAction();
1468 if (!(flags
&AC_NO_SOUND
) ) {
1469 if (actor
->LastTarget
!= targetID
) {
1470 //play attack sound for party members
1471 if (actor
->InParty
) {
1472 //pick from all 5 possible verbal constants
1473 actor
->VerbalConstant(VB_ATTACK
, 5);
1474 //DisplayStringCore(Sender, VB_ATTACK, DS_CONSOLE|DS_CONST );
1476 //display attack message
1477 core
->DisplayConstantStringAction(STR_ACTION_ATTACK
,0xf0f0f0, Sender
, target
);
1481 if(target
->Type
== ST_ACTOR
) {
1482 actor
->SetTarget( target
);
1484 if ( Sender
->GetCurrentArea()!=target
->GetCurrentArea() ||
1485 (PersonalDistance(Sender
, target
) > wi
.range
) ) {
1486 MoveNearerTo(Sender
, target
, wi
.range
);
1488 } else if (target
->Type
== ST_DOOR
) {
1489 //Forcing a lock does not launch the trap...
1490 Door
* door
= (Door
*) target
;
1491 if(door
->Flags
& DOOR_LOCKED
) {
1492 door
->TryBashLock(actor
);
1494 Sender
->ReleaseCurrentAction();
1496 } else if (target
->Type
== ST_CONTAINER
) {
1497 Container
* cont
= (Container
*) target
;
1498 if(cont
->Flags
& CONT_LOCKED
) {
1499 cont
->TryBashLock(actor
);
1501 Sender
->ReleaseCurrentAction();
1505 actor
->PerformAttack(core
->GetGame()->GameTime
);
1508 bool MatchActor(Scriptable
*Sender
, ieDword actorID
, Object
* oC
)
1515 tgts
= GetAllObjects(Sender
->GetCurrentArea(), Sender
, oC
, 0);
1517 // [0]/[ANYONE] can match all actors
1518 tgts
= GetAllActors(Sender
, 0);
1524 targetlist::iterator m
;
1525 const targettype
*tt
= tgts
->GetFirstTarget(m
, ST_ACTOR
);
1527 Actor
*actor
= (Actor
*) tt
->actor
;
1528 if (actor
->GetID() == actorID
) {
1532 tt
= tgts
->GetNextTarget(m
, ST_ACTOR
);
1539 int GetObjectCount(Scriptable
* Sender
, Object
* oC
)
1544 // EvaluateObject will return [PC]
1545 // GetAllObjects will also return Myself (evaluates object filters)
1546 // i believe we need the latter here
1547 Targets
* tgts
= GetAllObjects(Sender
->GetCurrentArea(), Sender
, oC
, 0);
1548 int count
= tgts
->Count();
1554 //check numcreaturesatmylevel(myself, 1)
1555 //when the actor is alone
1556 //it should (obviously) return true if the trigger
1557 //evaluates object filters
1558 //also check numcreaturesgtmylevel(myself,0) with
1559 //actor having at high level
1560 int GetObjectLevelCount(Scriptable
* Sender
, Object
* oC
)
1565 // EvaluateObject will return [PC]
1566 // GetAllObjects will also return Myself (evaluates object filters)
1567 // i believe we need the latter here
1568 Targets
* tgts
= GetAllObjects(Sender
->GetCurrentArea(), Sender
, oC
, 0);
1571 targetlist::iterator m
;
1572 const targettype
*tt
= tgts
->GetFirstTarget(m
, ST_ACTOR
);
1574 count
+= ((Actor
*) tt
->actor
)->GetXPLevel(true);
1575 tt
= tgts
->GetNextTarget(m
, ST_ACTOR
);
1582 //we need this because some special characters like _ or * are also accepted
1583 inline bool ismysymbol(const char letter
)
1585 if (letter
==']') return false;
1586 if (letter
=='[') return false;
1587 if (letter
==')') return false;
1588 if (letter
=='(') return false;
1589 if (letter
=='.') return false;
1590 if (letter
==',') return false;
1594 //this function returns a value, symbol could be a numeric string or
1595 //a symbol from idsname
1596 static int GetIdsValue(const char *&symbol
, const char *idsname
)
1598 int idsfile
=core
->LoadSymbol(idsname
);
1599 Holder
<SymbolMgr
> valHook
= core
->GetSymbol(idsfile
);
1601 //FIXME:missing ids file!!!
1602 if (InDebug
&ID_TRIGGERS
) {
1603 printMessage("GameScript"," ",LIGHT_RED
);
1604 printf("Missing IDS file %s for symbol %s!\n",idsname
, symbol
);
1609 int value
=strtol(symbol
, &newsymbol
, 0);
1610 if (symbol
!=newsymbol
) {
1614 char symbolname
[64];
1616 for (x
=0;ismysymbol(*symbol
) && x
<(int) sizeof(symbolname
)-1;x
++) {
1617 symbolname
[x
]=*symbol
;
1621 return valHook
->GetValue(symbolname
);
1624 static void ParseIdsTarget(const char *&src
, Object
*&object
)
1626 for (int i
=0;i
<ObjectFieldsCount
;i
++) {
1627 object
->objectFields
[i
]=GetIdsValue(src
, ObjectIDSTableNames
[i
]);
1636 //this will skip to the next element in the prototype of an action/trigger
1637 #define SKIP_ARGUMENT() while (*str && ( *str != ',' ) && ( *str != ')' )) str++
1639 static void ParseObject(const char *&str
,const char *&src
, Object
*&object
)
1642 object
= new Object();
1648 for (i
=0;i
<(int) sizeof(object
->objectName
)-1 && *src
!='"';i
++)
1650 object
->objectName
[i
] = *src
;
1653 object
->objectName
[i
] = 0;
1658 ParseIdsTarget(src
, object
);
1660 default: //nested object filters
1663 while (Nesting
<MaxObjectNesting
) {
1664 memmove(object
->objectFilters
+1, object
->objectFilters
, (int) sizeof(int) *(MaxObjectNesting
-1) );
1665 object
->objectFilters
[0]=GetIdsValue(src
,"object");
1676 ParseIdsTarget(src
, object
);
1678 src
+=Nesting
; //skipping )
1682 /* this function was lifted from GenerateAction, to make it clearer */
1683 Action
* GenerateActionCore(const char *src
, const char *str
, int acIndex
)
1685 Action
*newAction
= new Action(true);
1686 newAction
->actionID
= (unsigned short) actionsTable
->GetValueIndex( acIndex
);
1687 //this flag tells us to merge 2 consecutive strings together to get
1688 //a variable (context+variablename)
1689 int mergestrings
= actionflags
[newAction
->actionID
]&AF_MERGESTRINGS
;
1690 int objectCount
= ( newAction
->actionID
== 1 ) ? 0 : 1;
1691 int stringsCount
= 0;
1693 if (actionflags
[newAction
->actionID
]&AF_DIRECT
) {
1694 Object
*tmp
= new Object();
1695 tmp
->objectFields
[0] = -1;
1696 //tmp->objectFields[1] = core->GetGameControl()->targetID;
1697 newAction
->objects
[objectCount
++] = tmp
;
1699 //Here is the Action; Now we need to evaluate the parameters, if any
1700 if (*str
!=')') while (*str
) {
1701 if (*(str
+1)!=':') {
1702 printf("Warning, parser was sidetracked: %s\n",str
);
1706 printf("Invalid type: %s\n",str
);
1713 newAction
->pointParameter
.x
= (short) strtol( src
, (char **) &src
, 10 );
1715 newAction
->pointParameter
.y
= (short) strtol( src
, (char **) &src
, 10 );
1721 //going to the variable name
1722 while (*str
!= '*' && *str
!=',' && *str
!= ')' ) {
1726 if (*str
=='*') { //there may be an IDS table
1728 ieVariable idsTabName
;
1729 char* tmp
= idsTabName
;
1730 while (( *str
!= ',' ) && ( *str
!= ')' )) {
1736 if (idsTabName
[0]) {
1737 value
= GetIdsValue(src
, idsTabName
);
1740 value
= strtol( src
, (char **) &src
, 0);
1743 else { //no IDS table
1744 value
= strtol( src
, (char **) &src
, 0);
1747 newAction
->int0Parameter
= value
;
1748 } else if (intCount
== 1) {
1749 newAction
->int1Parameter
= value
;
1751 newAction
->int2Parameter
= value
;
1763 int openParenthesisCount
= 0;
1766 if (!openParenthesisCount
)
1768 openParenthesisCount
--;
1771 openParenthesisCount
++;
1773 if (( *src
== ',' ) &&
1774 !openParenthesisCount
)
1783 Action
* act
= GenerateAction( action
);
1784 act
->objects
[0] = newAction
->objects
[0];
1785 newAction
->objects
[0] = NULL
; //avoid freeing of object
1786 delete newAction
; //freeing action
1792 if (objectCount
==3) {
1793 printf("Invalid object count!\n");
1796 ParseObject(str
, src
, newAction
->objects
[objectCount
++]);
1805 if (!stringsCount
) {
1806 dst
= newAction
->string0Parameter
;
1808 dst
= newAction
->string1Parameter
;
1810 //if there are 3 strings, the first 2 will be merged,
1811 //the last one will be left alone
1815 //skipping the context part, which
1816 //is to be readed later
1825 while (*src
!= '"') {
1826 //sizeof(context+name) = 40
1828 *dst
++ = (char) tolower(*src
);
1834 //reading the context part
1838 printf("Invalid mergestrings:%s\n",str
);
1842 if (!stringsCount
) {
1843 dst
= newAction
->string0Parameter
;
1845 dst
= newAction
->string1Parameter
;
1848 //this works only if there are no spaces
1849 if (*src
++!='"' || *src
++!=',' || *src
++!='"') {
1852 //reading the context string
1854 while (*src
!= '"') {
1856 *dst
++ = (char) tolower(*src
);
1867 if (*src
== ',' || *src
==')')
1873 void GoNear(Scriptable
*Sender
, const Point
&p
)
1875 if (Sender
->GetCurrentAction()) {
1876 printMessage("GameScript","Target busy???\n",LIGHT_RED
);
1880 sprintf( Tmp
, "MoveToPoint([%hd.%hd])", p
.x
, p
.y
);
1881 Action
* action
= GenerateAction( Tmp
);
1882 Sender
->AddActionInFront( action
);
1885 void MoveNearerTo(Scriptable
*Sender
, Scriptable
*target
, int distance
)
1888 Map
*myarea
, *hisarea
;
1890 if (Sender
->Type
!= ST_ACTOR
) {
1891 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED
);
1892 Sender
->ReleaseCurrentAction();
1896 myarea
= Sender
->GetCurrentArea();
1897 hisarea
= target
->GetCurrentArea();
1898 if (hisarea
!=myarea
) {
1899 target
= myarea
->GetTileMap()->GetTravelTo(hisarea
->GetScriptName());
1902 printMessage("GameScript", "MoveNearerTo failed to find an exit\n", YELLOW
);
1903 Sender
->ReleaseCurrentAction();
1906 ((Actor
*) Sender
)->UseExit(true);
1908 ((Actor
*) Sender
)->UseExit(false);
1910 // we deliberately don't try GetLikelyPosition here for now,
1911 // maybe a future idea if we have a better implementation
1912 // (the old code used it - by passing true not 0 below - when target was a movable)
1913 GetPositionFromScriptable(target
, p
, 0);
1915 // account for PersonalDistance (which caller uses, but pathfinder doesn't)
1916 if (distance
&& Sender
->Type
== ST_ACTOR
) {
1917 distance
+= ((Actor
*)Sender
)->size
*10;
1919 if (distance
&& target
->Type
== ST_ACTOR
) {
1920 distance
+= ((Actor
*)target
)->size
*10;
1923 MoveNearerTo(Sender
, p
, distance
, 0);
1926 //It is not always good to release the current action if target is unreachable
1927 //we should also raise the trigger TargetUnreachable (if this is an Attack, at least)
1928 //i hacked only this low level function, didn't need the higher ones so far
1929 int MoveNearerTo(Scriptable
*Sender
, const Point
&p
, int distance
, int dont_release
)
1931 if (Sender
->Type
!= ST_ACTOR
) {
1932 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED
);
1933 Sender
->ReleaseCurrentAction();
1937 Actor
*actor
= (Actor
*)Sender
;
1939 if (!actor
->InMove() || actor
->Destination
!= p
) {
1940 actor
->WalkTo(p
, 0, distance
);
1943 if (!actor
->InMove()) {
1946 return dont_release
;
1948 // we can't walk any nearer to destination, give up
1949 Sender
->ReleaseCurrentAction();
1954 void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool flag, int distance)
1957 GetPositionFromScriptable(target,p,flag);
1958 GoNearAndRetry(Sender, p, distance);
1961 void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance)
1963 if (!Sender->GetCurrentAction() ) {
1964 printMessage("GameScript","NULL action retried???\n",LIGHT_RED);
1967 Sender->AddActionInFront( Sender->GetCurrentAction() );
1969 sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y );
1970 Action * action = GenerateAction( Tmp);
1971 //experimental hack, this value means,
1972 //MoveToPoint shall pop the next action too if movement fails
1973 //and the actor is farther than distance
1974 //this will prevent deadlocks
1975 //(we have to add 1 because otherwise distance==0 fails, we subtract it in MoveToPoint)
1976 action->int0Parameter = distance+1;
1977 Sender->AddActionInFront( action );
1980 void FreeSrc(SrcVector
*poi
, const ieResRef key
)
1982 int res
= SrcCache
.DecRef((void *) poi
, key
, true);
1984 printMessage( "GameScript", "Corrupted Src cache encountered (reference count went below zero), ", LIGHT_RED
);
1985 printf( "Src name is: %.8s\n", key
);
1993 SrcVector
*LoadSrc(const ieResRef resname
)
1995 SrcVector
*src
= (SrcVector
*) SrcCache
.GetResource(resname
);
1999 DataStream
* str
= gamedata
->GetResource( resname
, IE_SRC_CLASS_ID
);
2004 str
->ReadDword(&size
);
2005 src
= new SrcVector(size
);
2006 SrcCache
.SetAt( resname
, (void *) src
);
2009 str
->ReadDword(&tmp
);
2011 str
->ReadDword(&tmp
);
2017 #define MEMCPY(a,b) memcpy((a),(b),sizeof(a) )
2019 static Object
*ObjectCopy(Object
*object
)
2021 if (!object
) return NULL
;
2022 Object
*newObject
= new Object();
2023 MEMCPY( newObject
->objectFields
, object
->objectFields
);
2024 MEMCPY( newObject
->objectFilters
, object
->objectFilters
);
2025 MEMCPY( newObject
->objectRect
, object
->objectRect
);
2026 MEMCPY( newObject
->objectName
, object
->objectName
);
2030 Action
*ParamCopy(Action
*parameters
)
2032 Action
*newAction
= new Action(true);
2033 newAction
->actionID
= parameters
->actionID
;
2034 newAction
->int0Parameter
= parameters
->int0Parameter
;
2035 newAction
->int1Parameter
= parameters
->int1Parameter
;
2036 newAction
->int2Parameter
= parameters
->int2Parameter
;
2037 newAction
->pointParameter
= parameters
->pointParameter
;
2038 MEMCPY( newAction
->string0Parameter
, parameters
->string0Parameter
);
2039 MEMCPY( newAction
->string1Parameter
, parameters
->string1Parameter
);
2040 for (int c
=0;c
<3;c
++) {
2041 newAction
->objects
[c
]= ObjectCopy( parameters
->objects
[c
] );
2046 Action
*ParamCopyNoOverride(Action
*parameters
)
2048 Action
*newAction
= new Action(true);
2049 newAction
->actionID
= parameters
->actionID
;
2050 newAction
->int0Parameter
= parameters
->int0Parameter
;
2051 newAction
->int1Parameter
= parameters
->int1Parameter
;
2052 newAction
->int2Parameter
= parameters
->int2Parameter
;
2053 newAction
->pointParameter
= parameters
->pointParameter
;
2054 MEMCPY( newAction
->string0Parameter
, parameters
->string0Parameter
);
2055 MEMCPY( newAction
->string1Parameter
, parameters
->string1Parameter
);
2056 newAction
->objects
[0]= NULL
;
2057 newAction
->objects
[1]= ObjectCopy( parameters
->objects
[1] );
2058 newAction
->objects
[2]= ObjectCopy( parameters
->objects
[2] );
2062 Trigger
*GenerateTriggerCore(const char *src
, const char *str
, int trIndex
, int negate
)
2064 Trigger
*newTrigger
= new Trigger();
2065 newTrigger
->triggerID
= (unsigned short) triggersTable
->GetValueIndex( trIndex
)&0x3fff;
2066 newTrigger
->flags
= (unsigned short) negate
;
2067 int mergestrings
= triggerflags
[newTrigger
->triggerID
]&TF_MERGESTRINGS
;
2068 int stringsCount
= 0;
2070 //Here is the Trigger; Now we need to evaluate the parameters
2071 if (*str
!=')') while (*str
) {
2072 if (*(str
+1)!=':') {
2073 printf("Warning, parser was sidetracked: %s\n",str
);
2077 printf("Invalid type: %s\n",str
);
2084 newTrigger
->pointParameter
.x
= (short) strtol( src
, (char **) &src
, 10 );
2086 newTrigger
->pointParameter
.y
= (short) strtol( src
, (char **) &src
, 10 );
2092 //going to the variable name
2093 while (*str
!= '*' && *str
!=',' && *str
!= ')' ) {
2097 if (*str
=='*') { //there may be an IDS table
2099 ieVariable idsTabName
;
2100 char* tmp
= idsTabName
;
2101 while (( *str
!= ',' ) && ( *str
!= ')' )) {
2107 if (idsTabName
[0]) {
2108 value
= GetIdsValue(src
, idsTabName
);
2111 value
= strtol( src
, (char **) &src
, 0);
2114 else { //no IDS table
2115 value
= strtol( src
, (char **) &src
, 0);
2118 newTrigger
->int0Parameter
= value
;
2119 } else if (intCount
== 1) {
2120 newTrigger
->int1Parameter
= value
;
2122 newTrigger
->int2Parameter
= value
;
2129 ParseObject(str
, src
, newTrigger
->objectParameter
);
2138 if (!stringsCount
) {
2139 dst
= newTrigger
->string0Parameter
;
2141 dst
= newTrigger
->string1Parameter
;
2143 //skipping the context part, which
2144 //is to be readed later
2153 while (*src
!= '"') {
2154 //sizeof(context+name) = 40
2156 *dst
++ = (char) tolower(*src
);
2162 //reading the context part
2166 printf("Invalid mergestrings:%s\n",str
);
2170 if (!stringsCount
) {
2171 dst
= newTrigger
->string0Parameter
;
2173 dst
= newTrigger
->string1Parameter
;
2176 //this works only if there are no spaces
2177 if (*src
++!='"' || *src
++!=',' || *src
++!='"') {
2180 //reading the context string
2182 while (*src
!= '"') {
2184 *dst
++ = (char) tolower(*src
);
2195 if (*src
== ',' || *src
==')')
2201 void SetVariable(Scriptable
* Sender
, const char* VarName
, const char* Context
, ieDword value
)
2203 char newVarName
[8+33];
2205 if (InDebug
&ID_VARIABLES
) {
2206 printf( "Setting variable(\"%s%s\", %d)\n", Context
,
2209 strncpy( newVarName
, Context
, 6 );
2211 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2212 Sender
->GetCurrentArea()->locals
->SetAt( VarName
, value
);
2215 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2216 Sender
->locals
->SetAt( VarName
, value
);
2219 Game
*game
= core
->GetGame();
2220 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2221 game
->kaputz
->SetAt( VarName
, value
);
2225 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2226 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2228 map
->locals
->SetAt( VarName
, value
);
2230 else if (InDebug
&ID_VARIABLES
) {
2231 printMessage("GameScript"," ",YELLOW
);
2232 printf("Invalid variable %s %s in setvariable\n",Context
, VarName
);
2236 game
->locals
->SetAt( VarName
, ( ieDword
) value
);
2240 void SetVariable(Scriptable
* Sender
, const char* VarName
, ieDword value
)
2244 if (InDebug
&ID_VARIABLES
) {
2245 printf( "Setting variable(\"%s\", %d)\n", VarName
, value
);
2247 strncpy( newVarName
, VarName
, 6 );
2249 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2250 Sender
->GetCurrentArea()->locals
->SetAt( &VarName
[6], value
);
2253 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2254 Sender
->locals
->SetAt( &VarName
[6], value
);
2257 Game
*game
= core
->GetGame();
2258 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2259 game
->kaputz
->SetAt( &VarName
[6], value
);
2262 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2263 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2265 map
->locals
->SetAt( &VarName
[6], value
);
2267 else if (InDebug
&ID_VARIABLES
) {
2268 printMessage("GameScript"," ",YELLOW
);
2269 printf("Invalid variable %s in setvariable\n",VarName
);
2273 game
->locals
->SetAt( &VarName
[6], ( ieDword
) value
);
2277 ieDword
CheckVariable(Scriptable
* Sender
, const char* VarName
, bool *valid
)
2282 strncpy( newVarName
, VarName
, 6 );
2284 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2285 Sender
->GetCurrentArea()->locals
->Lookup( &VarName
[6], value
);
2286 if (InDebug
&ID_VARIABLES
) {
2287 printf("CheckVariable %s: %d\n",VarName
, value
);
2291 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2292 Sender
->locals
->Lookup( &VarName
[6], value
);
2293 if (InDebug
&ID_VARIABLES
) {
2294 printf("CheckVariable %s: %d\n",VarName
, value
);
2298 Game
*game
= core
->GetGame();
2299 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2300 game
->kaputz
->Lookup( &VarName
[6], value
);
2301 if (InDebug
&ID_VARIABLES
) {
2302 printf("CheckVariable %s: %d\n",VarName
, value
);
2306 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2307 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2309 map
->locals
->Lookup( &VarName
[6], value
);
2314 if (InDebug
&ID_VARIABLES
) {
2315 printMessage("GameScript"," ",YELLOW
);
2316 printf("Invalid variable %s in checkvariable\n",VarName
);
2320 game
->locals
->Lookup( &VarName
[6], value
);
2322 if (InDebug
&ID_VARIABLES
) {
2323 printf("CheckVariable %s: %d\n",VarName
, value
);
2328 ieDword
CheckVariable(Scriptable
* Sender
, const char* VarName
, const char* Context
, bool *valid
)
2333 strncpy(newVarName
, Context
, 6);
2335 if (strnicmp( newVarName
, "MYAREA", 6 ) == 0) {
2336 Sender
->GetCurrentArea()->locals
->Lookup( VarName
, value
);
2337 if (InDebug
&ID_VARIABLES
) {
2338 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2342 if (strnicmp( newVarName
, "LOCALS", 6 ) == 0) {
2343 Sender
->locals
->Lookup( VarName
, value
);
2344 if (InDebug
&ID_VARIABLES
) {
2345 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2349 Game
*game
= core
->GetGame();
2350 if (!strnicmp(newVarName
,"KAPUTZ",6) && core
->HasFeature(GF_HAS_KAPUTZ
) ) {
2351 game
->kaputz
->Lookup( VarName
, value
);
2352 if (InDebug
&ID_VARIABLES
) {
2353 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2357 if (strnicmp(newVarName
,"GLOBAL",6) ) {
2358 Map
*map
=game
->GetMap(game
->FindMap(newVarName
));
2360 map
->locals
->Lookup( VarName
, value
);
2365 if (InDebug
&ID_VARIABLES
) {
2366 printMessage("GameScript"," ",YELLOW
);
2367 printf("Invalid variable %s %s in checkvariable\n",Context
, VarName
);
2371 game
->locals
->Lookup( VarName
, value
);
2373 if (InDebug
&ID_VARIABLES
) {
2374 printf("CheckVariable %s%s: %d\n",Context
, VarName
, value
);
2379 int DiffCore(ieDword a
, ieDword b
, int diffmode
)
2397 case GREATER_OR_EQUALS
:
2407 case BINARY_LESS_OR_EQUALS
:
2417 case BINARY_MORE_OR_EQUALS
:
2427 case BINARY_INTERSECT
:
2432 case BINARY_NOT_INTERSECT
:
2437 default: //less or equals
2446 int GetGroup(Actor
*actor
)
2448 int type
= 2; //neutral, has no enemies
2449 if (actor
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2452 if (actor
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2458 Targets
*GetMyTarget(Scriptable
*Sender
, Actor
*actor
, Targets
*parameters
, int ga_flags
)
2461 if (Sender
->Type
==ST_ACTOR
) {
2462 actor
= (Actor
*) Sender
;
2465 parameters
->Clear();
2467 Actor
*target
= actor
->GetCurrentArea()->GetActorByGlobalID(actor
->LastTarget
);
2469 parameters
->AddTarget(target
, 0, ga_flags
);
2475 Targets
*XthNearestDoor(Targets
*parameters
, unsigned int count
)
2478 Scriptable
*origin
= parameters
->GetTarget(0, -1);
2479 parameters
->Clear();
2483 //get the doors based on it
2484 Map
*map
= origin
->GetCurrentArea();
2485 unsigned int i
=(unsigned int) map
->TMap
->GetDoorCount();
2490 Door
*door
= map
->TMap
->GetDoor(i
);
2491 unsigned int dist
= Distance(origin
->Pos
, door
->Pos
);
2492 parameters
->AddTarget(door
, dist
, 0);
2495 //now get the xth door
2496 origin
= parameters
->GetTarget(count
, ST_DOOR
);
2497 parameters
->Clear();
2501 parameters
->AddTarget(origin
, 0, 0);
2505 Targets
*XthNearestOf(Targets
*parameters
, int count
, int ga_flags
)
2510 const targettype
*t
= parameters
->GetLastTarget(ST_ACTOR
);
2513 origin
= parameters
->GetTarget(count
, ST_ACTOR
);
2515 parameters
->Clear();
2519 parameters
->AddTarget(origin
, 0, ga_flags
);
2523 //mygroup means the same specifics as origin
2524 Targets
*XthNearestMyGroupOfType(Scriptable
*origin
, Targets
*parameters
, unsigned int count
, int ga_flags
)
2526 if (origin
->Type
!= ST_ACTOR
) {
2527 parameters
->Clear();
2531 targetlist::iterator m
;
2532 const targettype
*t
= parameters
->GetFirstTarget(m
, ST_ACTOR
);
2536 Actor
*actor
= (Actor
*) origin
;
2537 //determining the specifics of origin
2538 ieDword type
= actor
->GetStat(IE_SPECIFIC
); //my group
2541 if (t
->actor
->Type
!=ST_ACTOR
) {
2542 t
=parameters
->RemoveTargetAt(m
);
2545 Actor
*actor
= (Actor
*) (t
->actor
);
2546 if (actor
->GetStat(IE_SPECIFIC
) != type
) {
2547 t
=parameters
->RemoveTargetAt(m
);
2550 t
= parameters
->GetNextTarget(m
, ST_ACTOR
);
2552 return XthNearestOf(parameters
,count
, ga_flags
);
2555 Targets
*ClosestEnemySummoned(Scriptable
*origin
, Targets
*parameters
, int ga_flags
)
2557 if (origin
->Type
!= ST_ACTOR
) {
2558 parameters
->Clear();
2562 targetlist::iterator m
;
2563 const targettype
*t
= parameters
->GetFirstTarget(m
, ST_ACTOR
);
2567 Actor
*actor
= (Actor
*) origin
;
2568 //determining the allegiance of the origin
2569 int type
= GetGroup(actor
);
2572 parameters
->Clear();
2578 Actor
*tmp
= (Actor
*) (t
->actor
);
2579 if (tmp
->GetStat(IE_SEX
) != SEX_SUMMON
) {
2582 if (type
) { //origin is PC
2583 if (tmp
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2587 if (tmp
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2592 t
= parameters
->GetNextTarget(m
, ST_ACTOR
);
2594 parameters
->Clear();
2595 parameters
->AddTarget(actor
, 0, ga_flags
);
2599 Targets
*XthNearestEnemyOfType(Scriptable
*origin
, Targets
*parameters
, unsigned int count
, int ga_flags
)
2601 if (origin
->Type
!= ST_ACTOR
) {
2602 parameters
->Clear();
2606 targetlist::iterator m
;
2607 const targettype
*t
= parameters
->GetFirstTarget(m
, ST_ACTOR
);
2611 Actor
*actor
= (Actor
*) origin
;
2612 //determining the allegiance of the origin
2613 int type
= GetGroup(actor
);
2616 parameters
->Clear();
2621 if (t
->actor
->Type
!=ST_ACTOR
) {
2622 t
=parameters
->RemoveTargetAt(m
);
2625 Actor
*actor
= (Actor
*) (t
->actor
);
2626 if (type
) { //origin is PC
2627 if (actor
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2628 t
=parameters
->RemoveTargetAt(m
);
2632 if (actor
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2633 t
=parameters
->RemoveTargetAt(m
);
2637 t
= parameters
->GetNextTarget(m
, ST_ACTOR
);
2639 return XthNearestOf(parameters
,count
, ga_flags
);
2642 Targets
*XthNearestEnemyOf(Targets
*parameters
, int count
, int ga_flags
)
2644 Actor
*origin
= (Actor
*) parameters
->GetTarget(0, ST_ACTOR
);
2645 parameters
->Clear();
2649 //determining the allegiance of the origin
2650 int type
= GetGroup(origin
);
2655 Map
*map
= origin
->GetCurrentArea();
2656 int i
= map
->GetActorCount(true);
2659 ac
=map
->GetActor(i
,true);
2660 int distance
= Distance(ac
, origin
);
2661 if (type
) { //origin is PC
2662 if (ac
->GetStat(IE_EA
) >= EA_EVILCUTOFF
) {
2663 parameters
->AddTarget(ac
, distance
, ga_flags
);
2667 if (ac
->GetStat(IE_EA
) <= EA_GOODCUTOFF
) {
2668 parameters
->AddTarget(ac
, distance
, ga_flags
);
2672 return XthNearestOf(parameters
,count
, ga_flags
);
2675 Point
GetEntryPoint(const char *areaname
, const char *entryname
)
2679 AutoTable
tab("entries");
2683 const char *tmpstr
= tab
->QueryField(areaname
, entryname
);
2686 sscanf(tmpstr
, "%d.%d", &x
, &y
);
2692 /* returns a spell's casting distance, it depends on the caster */
2693 unsigned int GetSpellDistance(ieResRef spellres
, Actor
*actor
)
2697 Spell
* spl
= gamedata
->GetSpell( spellres
);
2699 printMessage("GameScript"," ",LIGHT_RED
);
2700 printf("Spell couldn't be found:%.8s.\n", spellres
);
2703 dist
=spl
->GetCastingDistance(actor
);
2704 gamedata
->FreeSpell(spl
, spellres
, false);
2708 /* returns a spell's casting distance, it depends on the caster */
2709 unsigned int GetItemDistance(ieResRef itemres
, int header
)
2713 Item
* itm
= gamedata
->GetItem( itemres
);
2715 printMessage("GameScript"," ",LIGHT_RED
);
2716 printf("Item couldn't be found:%.8s.\n", itemres
);
2719 dist
=itm
->GetCastingDistance(header
);
2720 gamedata
->FreeItem(itm
, itemres
, false);
2724 void SetupWishCore(Scriptable
*Sender
, int column
, int picks
)
2731 AutoTable
tm("wish");
2733 printStatus( "ERROR", LIGHT_RED
);
2734 printf( "Cannot find wish.2da.\n");
2738 selects
= (int *) malloc(picks
*sizeof(int));
2739 count
= tm
->GetRowCount();
2742 snprintf(varname
,32, "wishpower%02d", i
);
2743 if(CheckVariable(Sender
, varname
, "GLOBAL") ) {
2744 SetVariable(Sender
, varname
, "GLOBAL", 0);
2749 for(i
=0;i
<count
;i
++) {
2756 for(i
=0;i
<picks
;i
++) {
2757 selects
[i
]=rand()%count
;
2760 if(selects
[i
]==selects
[j
]) {
2768 for (i
= 0; i
< picks
; i
++) {
2771 int spnum
= atoi( tm
->QueryField( selects
[i
], column
) );
2772 snprintf(varname
,32,"wishpower%02d", spnum
);
2773 SetVariable(Sender
, varname
, "GLOBAL",1);