Sort include order.
[gemrb.git] / gemrb / core / GSUtils.cpp
blob10abecedd48701a1b9c8352dd707c764426d22b0
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003-2005 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "GSUtils.h"
23 #include "strrefs.h"
24 #include "defsounds.h"
26 #include "Audio.h"
27 #include "Game.h"
28 #include "GameControl.h"
29 #include "GameData.h"
30 #include "Interface.h"
31 #include "Item.h"
32 #include "Map.h"
33 #include "Spell.h"
34 #include "StringMgr.h"
35 #include "TileMap.h"
36 #include "Video.h"
38 #include <cstdio>
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;
60 int InDebug = 0;
61 int happiness[3][20];
62 int RandomNumValue;
63 int *SkillStats=NULL;
64 int SkillCount=-1;
65 // reaction modifiers (by reputation and charisma)
66 int rmodrep[20];
67 int rmodchr[25];
69 void InitScriptTables()
71 //initializing the skill->stats conversion table
73 AutoTable tab("skillsta");
74 if (tab) {
75 int rowcount = tab->GetRowCount();
76 SkillCount = rowcount;
77 if (rowcount) {
78 SkillStats = (int *) malloc(rowcount * sizeof(int) );
79 while(rowcount--) {
80 SkillStats[rowcount]=strtol(tab->QueryField(rowcount,0), NULL, 0);
85 //initializing the happiness table
87 AutoTable tab("happy");
88 if (tab) {
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");
99 if (rmr) {
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");
107 if (rmc) {
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;
120 } else {
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)) {
127 reaction -= 4;
130 return reaction;
133 int GetHappiness(Scriptable* Sender, int reputation)
135 if (Sender->Type != ST_ACTOR) {
136 return 0;
138 Actor* ab = ( Actor* ) Sender;
139 int alignment = ab->GetStat(IE_ALIGNMENT)&AL_GE_MASK; //good / evil
140 if (reputation > 200) {
141 reputation = 200;
143 return happiness[alignment][reputation/10-1];
146 int GetHPPercent(Scriptable* Sender)
148 if (Sender->Type != ST_ACTOR) {
149 return 0;
151 Actor* ab = ( Actor* ) Sender;
152 int hp1 = ab->GetStat(IE_MAXHITPOINTS);
153 if (hp1<1) {
154 return 0;
156 int hp2 = ab->GetBase(IE_HITPOINTS);
157 if (hp2<1) {
158 return 0;
160 return hp2*100/hp1;
163 void HandleBitMod(ieDword &value1, ieDword value2, int opcode)
165 switch(opcode) {
166 case BM_AND:
167 value1 = ( value1& value2 );
168 break;
169 case BM_OR:
170 value1 = ( value1| value2 );
171 break;
172 case BM_XOR:
173 value1 = ( value1^ value2 );
174 break;
175 case BM_NAND: //this is a GemRB extension
176 value1 = ( value1& ~value2 );
177 break;
178 case BM_SET: //this is a GemRB extension
179 value1 = value2;
180 break;
184 // SPIT is not in the original engine spec, it is reserved for the
185 // enchantable items feature
186 // 0 1 2 3 4
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);
195 } else {
196 //resolve spell
197 int type = parameters->int0Parameter/1000;
198 int spellid = parameters->int0Parameter%1000;
199 if (type>4) {
200 return false;
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)
209 //resolve spell
210 unsigned int type = number/1000;
211 int spellid = number%1000;
212 if (type>4) {
213 type=0;
215 sprintf(spellres, "%s%03d", spell_suffices[type], spellid);
218 ieDword ResolveSpellNumber(const ieResRef spellres)
220 int i;
222 for(i=0;i<5;i++) {
223 if(!strnicmp(spellres, spell_suffices[i], 4)) {
224 int n = -1;
225 sscanf(spellres+4,"%d", &n);
226 if (n<0) {
227 return 0xffffffff;
229 return i*1000+n;
232 return 0xffffffff;
235 bool ResolveItemName(ieResRef itemres, Actor *act, ieDword Slot)
237 CREItem *itm = act->inventory.GetSlotItem(Slot);
238 if(itm) {
239 strnlwrcpy(itemres, itm->ItemResRef, 8);
240 return gamedata->Exists(itemres, IE_ITM_CLASS_ID);
242 return false;
245 bool StoreHasItemCore(const ieResRef storename, const ieResRef itemname)
247 bool had_nostore=false;
248 bool has_current=false;
249 ieResRef current;
250 ieVariable owner;
251 CREItem item;
253 Store *store = core->GetCurrentStore();
254 if (!store) {
255 had_nostore = true;
256 store = core->SetCurrentStore(storename, NULL);
257 } else {
258 if (strnicmp(store->Name, storename, 8) ) {
259 //not the current store, we need some dirty hack
260 has_current = true;
261 strnlwrcpy(current, store->Name, 8);
262 strnuprcpy(owner, store->GetOwner(), 32);
265 if (!store) {
266 printMessage("GameScript","Store cannot be opened!\n", LIGHT_RED);
267 return false;
270 bool ret = false;
271 //don't use triggers (pst style), it would be possible to create infinite loops
272 if (store->FindItem(itemname, false) != (unsigned int)-1) {
273 ret=true;
275 if (has_current) {
276 //setting back old store (this will save our current store)
277 core->SetCurrentStore(current, owner);
278 } else if (had_nostore) {
279 core->CloseCurrentStore();
281 return ret;
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();
288 if (!map) {
289 Sender->ReleaseCurrentAction();
290 return;
292 Point p=map->TMap->GetMapSize();
293 if (!p.PointInside(point)) {
294 Sender->ReleaseCurrentAction();
295 return;
297 Video *video = core->GetVideoDriver();
298 GlobalTimer *timer = core->timer;
299 timer->SetMoveViewPort( point.x, point.y, speed, true );
300 timer->DoStep(0);
301 if (timer->ViewportIsMoving()) {
302 Sender->AddActionInFront( Sender->GetCurrentAction() );
303 Sender->SetWait(1);
304 Sender->ReleaseCurrentAction();
305 return;
308 video->ConvertToScreen(point.x, point.y);
309 GameControl *win = core->GetGameControl();
311 point.x+=win->XPos;
312 point.y+=win->YPos;
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();
321 while(i--) {
322 CREItem *item = actor->inventory.GetSlotItem(i);
323 if (!item) {
324 continue;
326 if (strnicmp(item->ItemResRef, parameters->string0Parameter, 8) ) {
327 continue;
329 actor->inventory.SetSlotItemRes(parameters->string1Parameter,i,parameters->int0Parameter,parameters->int1Parameter,parameters->int2Parameter);
330 if (onlyone) {
331 break;
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)) {
340 return true;
342 int i=inventory->GetSlotCount();
343 while (i--) {
344 //maybe we could speed this up if we mark bag items with a flags bit
345 CREItem *itemslot = inventory->GetSlotItem(i);
346 if (!itemslot)
347 continue;
348 Item *item = gamedata->GetItem(itemslot->ItemResRef);
349 if (!item)
350 continue;
351 bool ret = false;
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);
357 if (ret) {
358 return true;
361 return false;
364 void DisplayStringCore(Scriptable* Sender, int Strref, int flags)
366 StringBlock sb;
368 //no one hears you when you are in the Limbo!
369 if (!Sender->GetCurrentArea()) {
370 return;
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);
378 return;
380 Actor* actor = ( Actor* ) Sender;
381 if ((ieDword) Strref>=VCONST_COUNT) {
382 printMessage("GameScript","Invalid verbal constant!\n", LIGHT_RED);
383 return;
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);
391 Strref = tmp;
393 //display the verbal constants in the console
394 ieDword charactersubtitles = 0;
395 core->GetDictionary()->Lookup("Subtitles", charactersubtitles);
396 if (charactersubtitles) {
397 flags |= DS_CONSOLE;
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 );
408 } else {
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();
418 } else {
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)
434 Map *map;
436 if (target->Type==ST_ACTOR) {
437 Actor *tar = (Actor *) target;
439 if (!tar->ValidTarget(seeflag)) {
440 return 0;
444 map = target->GetCurrentArea();
445 if (!(seeflag&GA_GLOBAL)) {
446 if ( map!=Sender->GetCurrentArea() ) {
447 return 0;
451 if (range) {
452 unsigned int dist;
454 if (Sender->Type == ST_ACTOR) {
455 Actor* snd = ( Actor* ) Sender;
456 dist = snd->Modified[IE_VISUALRANGE];
457 } else {
458 dist = 30;
461 if (Distance(target->Pos, Sender->Pos) > dist * 15) {
462 return 0;
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)
473 //see dead
474 int flags;
476 if (parameters->int0Parameter) {
477 flags = 0;
478 } else {
479 flags = GA_NO_DEAD;
481 Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter, flags );
482 /* don't set LastSeen if this isn't an actor */
483 if (!tar) {
484 return 0;
486 //both are actors
487 if (CanSee(Sender, tar, true, flags) ) {
488 if (justlos) {
489 return 1;
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();
496 return 1;
498 return 0;
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)
506 Inventory *myinv;
507 Map *map;
508 // track whether we are dealing with our party and need to display feedback
509 bool lostitem = false;
510 bool gotitem = false;
512 if (!target) {
513 return MIC_INVALID;
515 map=Sender->GetCurrentArea();
516 switch(Sender->Type) {
517 case ST_ACTOR:
518 myinv=&((Actor *) Sender)->inventory;
519 if (((Actor *)Sender)->InParty) lostitem = true;
520 break;
521 case ST_CONTAINER:
522 myinv=&((Container *) Sender)->inventory;
523 break;
524 default:
525 return MIC_INVALID;
527 CREItem *item;
528 myinv->RemoveItem(resref, flags, &item);
529 if (!item) {
530 // nothing was removed
531 return MIC_NOITEM;
534 item->Flags|=setflag;
536 switch(target->Type) {
537 case ST_ACTOR:
538 myinv=&((Actor *) target)->inventory;
539 if (((Actor *) target)->InParty) gotitem = true;
540 break;
541 case ST_CONTAINER:
542 myinv=&((Container *) target)->inventory;
543 break;
544 default:
545 myinv = NULL;
546 break;
548 if (!myinv) {
549 delete item;
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);
557 return MIC_FULL;
559 if (gotitem) core->DisplayConstantString(STR_GOTITEM, 0xbcefbc);
560 return MIC_GOTITEM;
563 static Targets* ReturnActorAsTarget(Actor *aC)
565 if (!aC) {
566 return NULL;
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
572 return tgts;
575 Actor *FindActorNearby(const char *name, Map *except, int ga_flags)
577 Game *game = core->GetGame();
578 size_t mc = game->GetLoadedMapCount();
579 while(mc--) {
580 Map *map = game->GetMap(mc);
581 if (map==except) continue;
582 Actor * aC = map->GetActor(name, ga_flags);
583 if (aC) {
584 return aC;
587 return NULL;
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)) {
609 aC = NULL;
611 return ReturnActorAsTarget(aC);
614 Targets *tgts=NULL;
616 //else branch, IDS targeting
617 for (int j = 0; j < ObjectIDSCount; j++) {
618 if (!oC->objectFields[j]) {
619 continue;
621 IDSFunction func = idtargets[j];
622 if (!func) {
623 printf("Unimplemented IDS targeting opcode!\n");
624 continue;
626 if (tgts) {
627 //we already got a subset of actors
628 int i = tgts->Count();
629 /*premature end, filtered everything*/
630 if (!i) {
631 break; //leaving the loop
633 targetlist::iterator m;
634 const targettype *t = tgts->GetFirstTarget(m, -1);
635 while (t) {
636 if (t->actor->Type!=ST_ACTOR) {
637 //we should never stumble here
638 abort();
639 // t = tgts->RemoveTargetAt(m);
640 continue;
642 if (!func( (Actor *) (t->actor), oC->objectFields[j] ) ) {
643 t = tgts->RemoveTargetAt(m);
644 continue;
646 t = tgts->GetNextTarget(m, -1);
648 } else {
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();
653 while (i--) {
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!
658 if (ac != Sender) {
659 tgts->AddTarget((Scriptable *) ac, dist, ga_flags);
665 return tgts;
668 Targets* GetAllObjects(Map *map, Scriptable* Sender, Object* oC, int ga_flags)
670 if (!oC) {
671 return NULL;
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]) {
677 return NULL;
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)
682 if (!tgts) {
683 tgts = new Targets();
685 for (int i = 0; i < MaxObjectNesting; i++) {
686 int filterid = oC->objectFilters[i];
687 if (!filterid) {
688 break;
690 ObjectFunction func = objects[filterid];
691 if (func) {
692 tgts = func( Sender, tgts, ga_flags);
694 else {
695 printMessage("GameScript"," ", YELLOW);
696 printf("Unknown object filter: %d %s\n",filterid, objectsTable->GetValue(filterid) );
698 if (!tgts->Count()) {
699 delete tgts;
700 return NULL;
703 return tgts;
706 Targets *GetAllObjectsNearby(Scriptable* Sender, Object* oC, int ga_flags)
708 Game *game = core->GetGame();
709 size_t mc = game->GetLoadedMapCount();
710 while(mc--) {
711 Map *map = game->GetMap(mc);
712 if (map==Sender->GetCurrentArea()) continue;
713 Targets *tgts = GetAllObjects(map, Sender, oC, ga_flags);
714 if (tgts) {
715 return tgts;
718 return NULL;
721 Targets *GetAllActors(Scriptable *Sender, int ga_flags)
723 Map *map=Sender->GetCurrentArea();
725 int i = map->GetActorCount(true);
726 Targets *tgts = new Targets();
727 while (i--) {
728 Actor *ac=map->GetActor(i,true);
729 int dist = Distance(Sender->Pos, ac->Pos);
730 tgts->AddTarget((Scriptable *) ac, dist, ga_flags);
732 return tgts;
735 Scriptable *GetActorObject(TileMap *TMap, const char *name)
738 Scriptable * aC = TMap->GetDoor( name );
739 if (aC) {
740 return aC;
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 );
749 if (aC) {
750 return aC;
753 //No... it was not a container ... maybe an InfoPoint?
754 aC = TMap->GetInfoPoint( name );
755 if (aC) {
756 return aC;
758 return aC;
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);
768 if (tar) {
769 // always an actor, check if it satisfies flags
770 if (((Actor *)tar)->ValidTarget(ga_flags)) {
771 return tar;
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;
784 return tar;
787 Scriptable* GetActorFromObject(Scriptable* Sender, Object* oC, int ga_flags)
789 Scriptable *aC = NULL;
791 if (!oC) {
792 return NULL;
794 Targets *tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, ga_flags);
795 if (tgts) {
796 //now this could return other than actor objects
797 aC = tgts->GetTarget(0,-1);
798 delete tgts;
799 if (!aC && (ga_flags&GA_GLOBAL) )
801 tgts = GetAllObjectsNearby(Sender, oC, ga_flags);
802 if (tgts) {
803 //now this could return other than actor objects
804 aC = tgts->GetTarget(0,-1);
805 delete tgts;
808 return aC;
811 if (oC->objectName[0]) {
812 aC = GetActorObject(Sender->GetCurrentArea()->GetTileMap(), oC->objectName );
813 if (aC) {
814 return aC;
816 Game *game = core->GetGame();
818 //global actors are always found by scripting name!
819 aC = game->FindPC(oC->objectName);
820 if (aC) {
821 return aC;
823 aC = game->FindNPC(oC->objectName);
824 if (aC) {
825 return aC;
828 if (ga_flags&GA_GLOBAL) {
829 size_t mc = game->GetLoadedMapCount();
830 while(mc--) {
831 Map *map = game->GetMap(mc);
832 if (map==Sender->GetCurrentArea()) continue;
833 aC = GetActorObject(map->GetTileMap(), oC->objectName);
834 if (aC) {
835 return aC;
840 return NULL;
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) );
847 if (!base) {
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) {
864 return;
868 Actor* ab;
869 if (flags & CC_STRING1) {
870 ab = gamedata->GetCreature(parameters->string1Parameter);
872 else {
873 ab = gamedata->GetCreature(parameters->string0Parameter);
876 if (!ab) {
877 printMessage("GameScript","Failed to create creature! ",LIGHT_RED);
878 printf("(missing creature file %s?)\n", parameters->string0Parameter);
879 // maybe this should abort()?
880 return;
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);
890 int radius;
891 Point pnt;
893 radius=0;
894 switch (flags & CC_MASK) {
895 //creates creature just off the screen
896 case CC_OFFSCREEN:
898 Region vp = core->GetVideoDriver()->GetViewport();
899 radius=vp.w/2; //actually it must be further divided by the tile size, hmm 16?
901 //falling through
902 case CC_OBJECT://use object + offset
903 if (tmp) Sender=tmp;
904 //falling through
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;
908 break;
909 default: //absolute point, but -1,-1 means AtFeet
910 pnt.x = parameters->pointParameter.x;
911 pnt.y = parameters->pointParameter.y;
912 if (pnt.isempty()) {
913 pnt.x = Sender->Pos.x;
914 pnt.y = Sender->Pos.y;
916 break;
919 Map *map = Sender->GetCurrentArea();
920 map->AddActor( ab );
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);
927 } else {
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)
941 if (effect[0]) {
942 ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(effect, false);
943 if (!vvc) {
944 printMessage("GameScript","Failed to create effect.",LIGHT_RED);
945 return NULL;
947 if (iterations) {
948 vvc->SetDefaultDuration( vvc->GetSequenceDuration(AI_UPDATE_TIME * iterations));
950 return vvc;
952 return NULL;
955 void CreateVisualEffectCore(Actor *target, const char *effect, int iterations)
957 ScriptedAnimation *vvc = GetVVCEffect(effect, iterations);
958 if (vvc) {
959 target->AddVVCell( vvc );
963 void CreateVisualEffectCore(Scriptable *Sender, const Point &position, const char *effect, int iterations)
965 ScriptedAnimation *vvc = GetVVCEffect(effect, iterations);
966 if (vvc) {
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);
977 if (tar) {
978 Map *map = src->GetCurrentArea();
979 map->AddActor( tar );
980 Point pos = src->Pos;
981 tar->SetOrientation(src->GetOrientation(), false );
982 src->DestroySelf();
983 // can't SetPosition while the old actor is taking the spot
984 tar->SetPosition(pos, 1);
985 if (effect) {
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)
993 char Tmp[256];
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");
1000 return;
1004 if (flags &EA_DESTROY) {
1005 //this must be put into a non-const variable
1006 sprintf( Tmp, "DestroySelf()" );
1007 } else {
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
1016 if (wait) {
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
1030 break;
1031 case ST_ACTOR:
1032 //if there are other moveables, put them here
1033 position = ((Movable *) scr)->GetMostLikelyPosition();
1034 break;
1035 case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL:
1036 if (((InfoPoint *) scr)->Flags & TRAP_USEPOINT) {
1037 position=((InfoPoint *) scr)->UsePoint;
1038 break;
1040 position=((InfoPoint *) scr)->TrapLaunch;
1041 break;
1042 case ST_DOOR: case ST_CONTAINER:
1043 position=((Highlightable *) scr)->TrapLaunch;
1044 break;
1048 void GetPositionFromScriptable(Scriptable* scr, Point &position, bool dest)
1050 if (!dest) {
1051 position = scr->Pos;
1052 return;
1054 switch (scr->Type) {
1055 case ST_AREA: case ST_GLOBAL:
1056 position = scr->Pos; //fake
1057 break;
1058 case ST_ACTOR:
1059 //if there are other moveables, put them here
1060 position = ((Movable *) scr)->GetMostLikelyPosition();
1061 break;
1062 case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL:
1063 if (((InfoPoint *) scr)->Flags & TRAP_USEPOINT) {
1064 position=((InfoPoint *) scr)->UsePoint;
1065 break;
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");
1075 if(!interact)
1076 return 0;
1077 const char *value = interact->QueryField(talker, target);
1078 if(!value)
1079 return 0;
1080 switch(value[0]) {
1081 case 's':
1082 return I_SPECIAL;
1083 case 'c':
1084 return I_COMPLIMENT;
1085 case 'i':
1086 return I_INSULT;
1088 return 0;
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);
1103 scr = tar;
1104 } else {
1105 tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag);
1106 scr = Sender;
1108 if (!scr) {
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();
1112 return;
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();
1124 } else {
1125 printf("<NULL>\n");
1127 Sender->ReleaseCurrentAction();
1128 return;
1131 Actor *speaker, *target;
1133 speaker = NULL;
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();
1143 return;
1145 bool swap = false;
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();
1154 return;
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);
1165 return;
1168 } else {
1169 //pst style dialog with trigger points
1170 swap=true;
1171 if (Flags&BD_CHECKDIST) {
1172 Point TalkPos;
1174 if (target->InMove()) {
1175 //waiting for target
1176 Sender->AddActionInFront( Sender->GetCurrentAction() );
1177 Sender->ReleaseCurrentAction();
1178 Sender->SetWait(1);
1179 return;
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();
1187 Sender->SetWait(1);
1188 return;
1193 GameControl* gc = core->GetGameControl();
1194 if (!gc) {
1195 printMessage( "GameScript","Dialog cannot be initiated because there is no GameControl.", YELLOW );
1196 Sender->ReleaseCurrentAction();
1197 return;
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();
1209 return;
1213 // starting a dialog ends cutscenes!
1214 core->SetCutSceneMode(false);
1216 const char* Dialog = NULL;
1217 AutoTable pdtable;
1219 switch (Flags & BD_LOCMASK) {
1220 case BD_STRING0:
1221 Dialog = parameters->string0Parameter;
1222 if (Flags & BD_SETDIALOG) {
1223 scr->SetDialog( Dialog );
1225 break;
1226 case BD_SOURCE:
1227 case BD_TARGET:
1228 if (swap) Dialog = scr->GetDialog();
1229 else Dialog = target->GetDialog(GD_FEEDBACK);
1230 break;
1231 case BD_RESERVED:
1232 //what if playerdialog was initiated from Player2?
1233 PlayerDialogRes[5] = '1';
1234 Dialog = ( const char * ) PlayerDialogRes;
1235 break;
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());
1241 if(type) {
1242 speaker->Interact(type);
1243 target->Response(type);
1244 Sender->ReleaseCurrentAction();
1245 return;
1247 /* banter dialogue */
1248 pdtable.load("interdia");
1249 //Dialog is a borrowed reference, we cannot free pdtable while it is being used
1250 if (pdtable) {
1251 Dialog = pdtable->QueryField( scriptingname, "FILE" );
1253 break;
1257 //dialog is not meaningful
1258 if (!Dialog || Dialog[0]=='*') {
1259 Sender->ReleaseCurrentAction();
1260 return;
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();
1273 return;
1276 if (speaker!=target) {
1277 if (swap) {
1278 Scriptable *tmp = tar;
1279 tar = scr;
1280 scr = tmp;
1281 } else {
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();
1287 return;
1293 //don't clear target's actions, because a sequence like this will be broken:
1294 //StartDialog([PC]); SetGlobal("Talked","LOCALS",1);
1295 if (scr!=tar) {
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);
1304 int ret;
1306 if (Dialog[0]) {
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);
1317 else {
1318 ret = -1;
1321 if (ret<0) {
1322 Sender->ReleaseCurrentAction();
1323 if (Flags & BD_NOEMPTY) {
1324 return;
1326 core->DisplayConstantStringName(STR_NOTHINGTOSAY,0xff0000,tar);
1327 return;
1330 //this is a bit fishy
1331 Sender->SetWait(1);
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);
1340 Map* map2;
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);
1347 if ( map1!=map2 ) {
1348 if (map1) {
1349 map1->RemoveActor( actor );
1351 map2->AddActor( actor );
1354 actor->SetPosition(position, adjust);
1355 if (face !=-1) {
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();
1371 return;
1373 Scriptable* target = GetStoredActorFromObject( Sender, parameters->objects[1] );
1374 if (!target) {
1375 Sender->ReleaseCurrentAction();
1376 return;
1378 Actor* actor = ( Actor* ) Sender;
1379 if (untilsee && CanSee(actor, target, true, 0) ) {
1380 Sender->ReleaseCurrentAction();
1381 return;
1382 } else {
1383 if (PersonalDistance(actor, target)<MAX_OPERATING_DISTANCE) {
1384 Sender->ReleaseCurrentAction();
1385 return;
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();
1394 return;
1397 //repeat movement...
1398 Action *newaction = ParamCopyNoOverride(parameters);
1399 if (newaction->int0Parameter!=1) {
1400 if (newaction->int0Parameter) {
1401 newaction->int0Parameter--;
1403 actor->AddActionInFront(newaction);
1404 actor->SetWait(1);
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);
1414 if (a==-1) {
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);
1421 } else {
1422 item->Usages[0]=(ieWord) a;
1423 item->Usages[1]=(ieWord) b;
1424 item->Usages[2]=(ieWord) c;
1426 item->Flags=0;
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;
1435 WeaponInfo wi;
1436 ITMExtHeader *header = NULL;
1437 ITMExtHeader *hittingheader = NULL;
1438 int tohit;
1439 ieDword Flags;
1440 int DamageBonus, CriticalBonus;
1441 int speed, style;
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();
1449 return;
1452 if (header) wi.range *= 10;
1453 else wi.range = 0;
1455 if ( target->Type == ST_DOOR || target->Type == ST_CONTAINER) {
1456 wi.range += 10;
1458 Actor *tar = NULL;
1459 ieDword targetID = 0;
1460 if (target->Type==ST_ACTOR) {
1461 tar = (Actor *) target;
1462 targetID = tar->GetID();
1464 if (actor == tar) {
1465 Sender->ReleaseCurrentAction();
1466 return;
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);
1480 //action performed
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);
1487 return;
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();
1495 return;
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();
1502 return;
1505 actor->PerformAttack(core->GetGame()->GameTime);
1508 bool MatchActor(Scriptable *Sender, ieDword actorID, Object* oC)
1510 if (!Sender) {
1511 return false;
1513 Targets *tgts;
1514 if (oC) {
1515 tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, 0);
1516 } else {
1517 // [0]/[ANYONE] can match all actors
1518 tgts = GetAllActors(Sender, 0);
1521 bool ret = false;
1523 if (tgts) {
1524 targetlist::iterator m;
1525 const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR);
1526 while (tt) {
1527 Actor *actor = (Actor *) tt->actor;
1528 if (actor->GetID() == actorID) {
1529 ret = true;
1530 break;
1532 tt = tgts->GetNextTarget(m, ST_ACTOR);
1535 delete tgts;
1536 return ret;
1539 int GetObjectCount(Scriptable* Sender, Object* oC)
1541 if (!oC) {
1542 return 0;
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();
1549 delete tgts;
1550 return count;
1553 //TODO:
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)
1562 if (!oC) {
1563 return 0;
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);
1569 int count = 0;
1570 if (tgts) {
1571 targetlist::iterator m;
1572 const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR);
1573 while (tt) {
1574 count += ((Actor *) tt->actor)->GetXPLevel(true);
1575 tt = tgts->GetNextTarget(m, ST_ACTOR);
1578 delete tgts;
1579 return count;
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;
1591 return true;
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);
1600 if (!valHook) {
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);
1606 return -1;
1608 char *newsymbol;
1609 int value=strtol(symbol, &newsymbol, 0);
1610 if (symbol!=newsymbol) {
1611 symbol=newsymbol;
1612 return value;
1614 char symbolname[64];
1615 int x;
1616 for (x=0;ismysymbol(*symbol) && x<(int) sizeof(symbolname)-1;x++) {
1617 symbolname[x]=*symbol;
1618 symbol++;
1620 symbolname[x]=0;
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]);
1628 if (*src!='.') {
1629 break;
1631 src++;
1633 src++; //skipping ]
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)
1641 SKIP_ARGUMENT();
1642 object = new Object();
1643 switch (*src) {
1644 case '"':
1645 //Scriptable Name
1646 src++;
1647 int i;
1648 for (i=0;i<(int) sizeof(object->objectName)-1 && *src!='"';i++)
1650 object->objectName[i] = *src;
1651 src++;
1653 object->objectName[i] = 0;
1654 src++;
1655 break;
1656 case '[':
1657 src++; //skipping [
1658 ParseIdsTarget(src, object);
1659 break;
1660 default: //nested object filters
1661 int Nesting=0;
1663 while (Nesting<MaxObjectNesting) {
1664 memmove(object->objectFilters+1, object->objectFilters, (int) sizeof(int) *(MaxObjectNesting-1) );
1665 object->objectFilters[0]=GetIdsValue(src,"object");
1666 if (*src!='(') {
1667 break;
1669 src++; //skipping (
1670 if (*src==')') {
1671 break;
1673 Nesting++;
1675 if (*src=='[') {
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;
1692 int intCount = 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);
1704 switch (*str) {
1705 default:
1706 printf("Invalid type: %s\n",str);
1707 str++;
1708 break;
1710 case 'p': //Point
1711 SKIP_ARGUMENT();
1712 src++; //Skip [
1713 newAction->pointParameter.x = (short) strtol( src, (char **) &src, 10 );
1714 src++; //Skip .
1715 newAction->pointParameter.y = (short) strtol( src, (char **) &src, 10 );
1716 src++; //Skip ]
1717 break;
1719 case 'i': //Integer
1721 //going to the variable name
1722 while (*str != '*' && *str !=',' && *str != ')' ) {
1723 str++;
1725 int value;
1726 if (*str=='*') { //there may be an IDS table
1727 str++;
1728 ieVariable idsTabName;
1729 char* tmp = idsTabName;
1730 while (( *str != ',' ) && ( *str != ')' )) {
1731 *tmp = *str;
1732 tmp++;
1733 str++;
1735 *tmp = 0;
1736 if (idsTabName[0]) {
1737 value = GetIdsValue(src, idsTabName);
1739 else {
1740 value = strtol( src, (char **) &src, 0);
1743 else { //no IDS table
1744 value = strtol( src, (char **) &src, 0);
1746 if (!intCount) {
1747 newAction->int0Parameter = value;
1748 } else if (intCount == 1) {
1749 newAction->int1Parameter = value;
1750 } else {
1751 newAction->int2Parameter = value;
1753 intCount++;
1755 break;
1757 case 'a':
1758 //Action
1760 SKIP_ARGUMENT();
1761 char action[257];
1762 int i = 0;
1763 int openParenthesisCount = 0;
1764 while (true) {
1765 if (*src == ')') {
1766 if (!openParenthesisCount)
1767 break;
1768 openParenthesisCount--;
1769 } else {
1770 if (*src == '(') {
1771 openParenthesisCount++;
1772 } else {
1773 if (( *src == ',' ) &&
1774 !openParenthesisCount)
1775 break;
1778 action[i] = *src;
1779 i++;
1780 src++;
1782 action[i] = 0;
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
1787 newAction = act;
1789 break;
1791 case 'o': //Object
1792 if (objectCount==3) {
1793 printf("Invalid object count!\n");
1794 abort();
1796 ParseObject(str, src, newAction->objects[objectCount++]);
1797 break;
1799 case 's': //String
1801 SKIP_ARGUMENT();
1802 src++;
1803 int i;
1804 char* dst;
1805 if (!stringsCount) {
1806 dst = newAction->string0Parameter;
1807 } else {
1808 dst = newAction->string1Parameter;
1810 //if there are 3 strings, the first 2 will be merged,
1811 //the last one will be left alone
1812 if (*str==')') {
1813 mergestrings = 0;
1815 //skipping the context part, which
1816 //is to be readed later
1817 if (mergestrings) {
1818 for (i=0;i<6;i++) {
1819 *dst++='*';
1822 else {
1823 i=0;
1825 while (*src != '"') {
1826 //sizeof(context+name) = 40
1827 if (i<40) {
1828 *dst++ = (char) tolower(*src);
1829 i++;
1831 src++;
1833 *dst = 0;
1834 //reading the context part
1835 if (mergestrings) {
1836 str++;
1837 if (*str!='s') {
1838 printf("Invalid mergestrings:%s\n",str);
1839 abort();
1841 SKIP_ARGUMENT();
1842 if (!stringsCount) {
1843 dst = newAction->string0Parameter;
1844 } else {
1845 dst = newAction->string1Parameter;
1848 //this works only if there are no spaces
1849 if (*src++!='"' || *src++!=',' || *src++!='"') {
1850 break;
1852 //reading the context string
1853 i=0;
1854 while (*src != '"') {
1855 if (i++<6) {
1856 *dst++ = (char) tolower(*src);
1858 src++;
1861 src++; //skipping "
1862 stringsCount++;
1864 break;
1866 str++;
1867 if (*src == ',' || *src==')')
1868 src++;
1870 return newAction;
1873 void GoNear(Scriptable *Sender, const Point &p)
1875 if (Sender->GetCurrentAction()) {
1876 printMessage("GameScript","Target busy???\n",LIGHT_RED);
1877 return;
1879 char Tmp[256];
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)
1887 Point p;
1888 Map *myarea, *hisarea;
1890 if (Sender->Type != ST_ACTOR) {
1891 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED);
1892 Sender->ReleaseCurrentAction();
1893 return;
1896 myarea = Sender->GetCurrentArea();
1897 hisarea = target->GetCurrentArea();
1898 if (hisarea!=myarea) {
1899 target = myarea->GetTileMap()->GetTravelTo(hisarea->GetScriptName());
1901 if (!target) {
1902 printMessage("GameScript", "MoveNearerTo failed to find an exit\n", YELLOW);
1903 Sender->ReleaseCurrentAction();
1904 return;
1906 ((Actor *) Sender)->UseExit(true);
1907 } else {
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();
1934 return 0;
1937 Actor *actor = (Actor *)Sender;
1939 if (!actor->InMove() || actor->Destination != p) {
1940 actor->WalkTo(p, 0, distance);
1943 if (!actor->InMove()) {
1944 //didn't release
1945 if (dont_release) {
1946 return dont_release;
1948 // we can't walk any nearer to destination, give up
1949 Sender->ReleaseCurrentAction();
1951 return 0;
1954 void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool flag, int distance)
1956 Point p;
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);
1965 return;
1967 Sender->AddActionInFront( Sender->GetCurrentAction() );
1968 char Tmp[256];
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);
1983 if (res<0) {
1984 printMessage( "GameScript", "Corrupted Src cache encountered (reference count went below zero), ", LIGHT_RED );
1985 printf( "Src name is: %.8s\n", key);
1986 abort();
1988 if (!res) {
1989 delete poi;
1993 SrcVector *LoadSrc(const ieResRef resname)
1995 SrcVector *src = (SrcVector *) SrcCache.GetResource(resname);
1996 if (src) {
1997 return src;
1999 DataStream* str = gamedata->GetResource( resname, IE_SRC_CLASS_ID );
2000 if ( !str) {
2001 return NULL;
2003 ieDword size=0;
2004 str->ReadDword(&size);
2005 src = new SrcVector(size);
2006 SrcCache.SetAt( resname, (void *) src );
2007 while (size--) {
2008 ieDword tmp;
2009 str->ReadDword(&tmp);
2010 src->at(size)=tmp;
2011 str->ReadDword(&tmp);
2013 delete ( str );
2014 return src;
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 );
2027 return newObject;
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] );
2043 return newAction;
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] );
2059 return newAction;
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;
2069 int intCount = 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);
2075 switch (*str) {
2076 default:
2077 printf("Invalid type: %s\n",str);
2078 str++;
2079 break;
2081 case 'p': //Point
2082 SKIP_ARGUMENT();
2083 src++; //Skip [
2084 newTrigger->pointParameter.x = (short) strtol( src, (char **) &src, 10 );
2085 src++; //Skip .
2086 newTrigger->pointParameter.y = (short) strtol( src, (char **) &src, 10 );
2087 src++; //Skip ]
2088 break;
2090 case 'i': //Integer
2092 //going to the variable name
2093 while (*str != '*' && *str !=',' && *str != ')' ) {
2094 str++;
2096 int value;
2097 if (*str=='*') { //there may be an IDS table
2098 str++;
2099 ieVariable idsTabName;
2100 char* tmp = idsTabName;
2101 while (( *str != ',' ) && ( *str != ')' )) {
2102 *tmp = *str;
2103 tmp++;
2104 str++;
2106 *tmp = 0;
2107 if (idsTabName[0]) {
2108 value = GetIdsValue(src, idsTabName);
2110 else {
2111 value = strtol( src, (char **) &src, 0);
2114 else { //no IDS table
2115 value = strtol( src, (char **) &src, 0);
2117 if (!intCount) {
2118 newTrigger->int0Parameter = value;
2119 } else if (intCount == 1) {
2120 newTrigger->int1Parameter = value;
2121 } else {
2122 newTrigger->int2Parameter = value;
2124 intCount++;
2126 break;
2128 case 'o': //Object
2129 ParseObject(str, src, newTrigger->objectParameter);
2130 break;
2132 case 's': //String
2134 SKIP_ARGUMENT();
2135 src++;
2136 int i;
2137 char* dst;
2138 if (!stringsCount) {
2139 dst = newTrigger->string0Parameter;
2140 } else {
2141 dst = newTrigger->string1Parameter;
2143 //skipping the context part, which
2144 //is to be readed later
2145 if (mergestrings) {
2146 for (i=0;i<6;i++) {
2147 *dst++='*';
2150 else {
2151 i=0;
2153 while (*src != '"') {
2154 //sizeof(context+name) = 40
2155 if (i<40) {
2156 *dst++ = (char) tolower(*src);
2157 i++;
2159 src++;
2161 *dst = 0;
2162 //reading the context part
2163 if (mergestrings) {
2164 str++;
2165 if (*str!='s') {
2166 printf("Invalid mergestrings:%s\n",str);
2167 abort();
2169 SKIP_ARGUMENT();
2170 if (!stringsCount) {
2171 dst = newTrigger->string0Parameter;
2172 } else {
2173 dst = newTrigger->string1Parameter;
2176 //this works only if there are no spaces
2177 if (*src++!='"' || *src++!=',' || *src++!='"') {
2178 break;
2180 //reading the context string
2181 i=0;
2182 while (*src != '"') {
2183 if (i++<6) {
2184 *dst++ = (char) tolower(*src);
2186 src++;
2189 src++; //skipping "
2190 stringsCount++;
2192 break;
2194 str++;
2195 if (*src == ',' || *src==')')
2196 src++;
2198 return newTrigger;
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,
2207 VarName, value );
2209 strncpy( newVarName, Context, 6 );
2210 newVarName[6]=0;
2211 if (strnicmp( newVarName, "MYAREA", 6 ) == 0) {
2212 Sender->GetCurrentArea()->locals->SetAt( VarName, value );
2213 return;
2215 if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
2216 Sender->locals->SetAt( VarName, value );
2217 return;
2219 Game *game = core->GetGame();
2220 if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
2221 game->kaputz->SetAt( VarName, value );
2222 return;
2225 if (strnicmp(newVarName,"GLOBAL",6) ) {
2226 Map *map=game->GetMap(game->FindMap(newVarName));
2227 if (map) {
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);
2235 else {
2236 game->locals->SetAt( VarName, ( ieDword ) value );
2240 void SetVariable(Scriptable* Sender, const char* VarName, ieDword value)
2242 char newVarName[8];
2244 if (InDebug&ID_VARIABLES) {
2245 printf( "Setting variable(\"%s\", %d)\n", VarName, value );
2247 strncpy( newVarName, VarName, 6 );
2248 newVarName[6]=0;
2249 if (strnicmp( newVarName, "MYAREA", 6 ) == 0) {
2250 Sender->GetCurrentArea()->locals->SetAt( &VarName[6], value );
2251 return;
2253 if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
2254 Sender->locals->SetAt( &VarName[6], value );
2255 return;
2257 Game *game = core->GetGame();
2258 if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
2259 game->kaputz->SetAt( &VarName[6], value );
2260 return;
2262 if (strnicmp(newVarName,"GLOBAL",6) ) {
2263 Map *map=game->GetMap(game->FindMap(newVarName));
2264 if (map) {
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);
2272 else {
2273 game->locals->SetAt( &VarName[6], ( ieDword ) value );
2277 ieDword CheckVariable(Scriptable* Sender, const char* VarName, bool *valid)
2279 char newVarName[8];
2280 ieDword value = 0;
2282 strncpy( newVarName, VarName, 6 );
2283 newVarName[6]=0;
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);
2289 return 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);
2296 return 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);
2304 return value;
2306 if (strnicmp(newVarName,"GLOBAL",6) ) {
2307 Map *map=game->GetMap(game->FindMap(newVarName));
2308 if (map) {
2309 map->locals->Lookup( &VarName[6], value);
2310 } else {
2311 if (valid) {
2312 *valid=false;
2314 if (InDebug&ID_VARIABLES) {
2315 printMessage("GameScript"," ",YELLOW);
2316 printf("Invalid variable %s in checkvariable\n",VarName);
2319 } else {
2320 game->locals->Lookup( &VarName[6], value );
2322 if (InDebug&ID_VARIABLES) {
2323 printf("CheckVariable %s: %d\n",VarName, value);
2325 return value;
2328 ieDword CheckVariable(Scriptable* Sender, const char* VarName, const char* Context, bool *valid)
2330 char newVarName[8];
2331 ieDword value = 0;
2333 strncpy(newVarName, Context, 6);
2334 newVarName[6]=0;
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);
2340 return 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);
2347 return 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);
2355 return value;
2357 if (strnicmp(newVarName,"GLOBAL",6) ) {
2358 Map *map=game->GetMap(game->FindMap(newVarName));
2359 if (map) {
2360 map->locals->Lookup( VarName, value);
2361 } else {
2362 if (valid) {
2363 *valid=false;
2365 if (InDebug&ID_VARIABLES) {
2366 printMessage("GameScript"," ",YELLOW);
2367 printf("Invalid variable %s %s in checkvariable\n",Context, VarName);
2370 } else {
2371 game->locals->Lookup( VarName, value );
2373 if (InDebug&ID_VARIABLES) {
2374 printf("CheckVariable %s%s: %d\n",Context, VarName, value);
2376 return value;
2379 int DiffCore(ieDword a, ieDword b, int diffmode)
2381 switch (diffmode) {
2382 case LESS_THAN:
2383 if (a<b) {
2384 return 1;
2386 break;
2387 case EQUALS:
2388 if (a==b) {
2389 return 1;
2391 break;
2392 case GREATER_THAN:
2393 if (a>b) {
2394 return 1;
2396 break;
2397 case GREATER_OR_EQUALS:
2398 if (a>=b) {
2399 return 1;
2401 break;
2402 case NOT_EQUALS:
2403 if (a!=b) {
2404 return 1;
2406 break;
2407 case BINARY_LESS_OR_EQUALS:
2408 if ((a&b) == a) {
2409 return 1;
2411 break;
2412 case BINARY_MORE:
2413 if ((a&b) != a) {
2414 return 1;
2416 break;
2417 case BINARY_MORE_OR_EQUALS:
2418 if ((a&b) == b) {
2419 return 1;
2421 break;
2422 case BINARY_LESS:
2423 if ((a&b) != b) {
2424 return 1;
2426 break;
2427 case BINARY_INTERSECT:
2428 if (a&b) {
2429 return 1;
2431 break;
2432 case BINARY_NOT_INTERSECT:
2433 if (!(a&b)) {
2434 return 1;
2436 break;
2437 default: //less or equals
2438 if (a<=b) {
2439 return 1;
2441 break;
2443 return 0;
2446 int GetGroup(Actor *actor)
2448 int type = 2; //neutral, has no enemies
2449 if (actor->GetStat(IE_EA) <= EA_GOODCUTOFF) {
2450 type = 1; //PC
2452 if (actor->GetStat(IE_EA) >= EA_EVILCUTOFF) {
2453 type = 0;
2455 return type;
2458 Targets *GetMyTarget(Scriptable *Sender, Actor *actor, Targets *parameters, int ga_flags)
2460 if (!actor) {
2461 if (Sender->Type==ST_ACTOR) {
2462 actor = (Actor *) Sender;
2465 parameters->Clear();
2466 if (actor) {
2467 Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastTarget);
2468 if (target) {
2469 parameters->AddTarget(target, 0, ga_flags);
2472 return parameters;
2475 Targets *XthNearestDoor(Targets *parameters, unsigned int count)
2477 //get the origin
2478 Scriptable *origin = parameters->GetTarget(0, -1);
2479 parameters->Clear();
2480 if (!origin) {
2481 return parameters;
2483 //get the doors based on it
2484 Map *map = origin->GetCurrentArea();
2485 unsigned int i =(unsigned int) map->TMap->GetDoorCount();
2486 if (count>i) {
2487 return parameters;
2489 while (i--) {
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();
2498 if (!origin) {
2499 return parameters;
2501 parameters->AddTarget(origin, 0, 0);
2502 return parameters;
2505 Targets *XthNearestOf(Targets *parameters, int count, int ga_flags)
2507 Scriptable *origin;
2509 if (count<0) {
2510 const targettype *t = parameters->GetLastTarget(ST_ACTOR);
2511 origin = t->actor;
2512 } else {
2513 origin = parameters->GetTarget(count, ST_ACTOR);
2515 parameters->Clear();
2516 if (!origin) {
2517 return parameters;
2519 parameters->AddTarget(origin, 0, ga_flags);
2520 return parameters;
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();
2528 return parameters;
2531 targetlist::iterator m;
2532 const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR);
2533 if (!t) {
2534 return parameters;
2536 Actor *actor = (Actor *) origin;
2537 //determining the specifics of origin
2538 ieDword type = actor->GetStat(IE_SPECIFIC); //my group
2540 while ( t ) {
2541 if (t->actor->Type!=ST_ACTOR) {
2542 t=parameters->RemoveTargetAt(m);
2543 continue;
2545 Actor *actor = (Actor *) (t->actor);
2546 if (actor->GetStat(IE_SPECIFIC) != type) {
2547 t=parameters->RemoveTargetAt(m);
2548 continue;
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();
2559 return parameters;
2562 targetlist::iterator m;
2563 const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR);
2564 if (!t) {
2565 return parameters;
2567 Actor *actor = (Actor *) origin;
2568 //determining the allegiance of the origin
2569 int type = GetGroup(actor);
2571 if (type==2) {
2572 parameters->Clear();
2573 return parameters;
2576 actor = NULL;
2577 while ( t ) {
2578 Actor *tmp = (Actor *) (t->actor);
2579 if (tmp->GetStat(IE_SEX) != SEX_SUMMON) {
2580 continue;
2582 if (type) { //origin is PC
2583 if (tmp->GetStat(IE_EA) <= EA_GOODCUTOFF) {
2584 continue;
2586 } else {
2587 if (tmp->GetStat(IE_EA) >= EA_EVILCUTOFF) {
2588 continue;
2591 actor = tmp;
2592 t = parameters->GetNextTarget(m, ST_ACTOR);
2594 parameters->Clear();
2595 parameters->AddTarget(actor, 0, ga_flags);
2596 return parameters;
2599 Targets *XthNearestEnemyOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags)
2601 if (origin->Type != ST_ACTOR) {
2602 parameters->Clear();
2603 return parameters;
2606 targetlist::iterator m;
2607 const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR);
2608 if (!t) {
2609 return parameters;
2611 Actor *actor = (Actor *) origin;
2612 //determining the allegiance of the origin
2613 int type = GetGroup(actor);
2615 if (type==2) {
2616 parameters->Clear();
2617 return parameters;
2620 while ( t ) {
2621 if (t->actor->Type!=ST_ACTOR) {
2622 t=parameters->RemoveTargetAt(m);
2623 continue;
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);
2629 continue;
2631 } else {
2632 if (actor->GetStat(IE_EA) >= EA_EVILCUTOFF) {
2633 t=parameters->RemoveTargetAt(m);
2634 continue;
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();
2646 if (!origin) {
2647 return parameters;
2649 //determining the allegiance of the origin
2650 int type = GetGroup(origin);
2652 if (type==2) {
2653 return parameters;
2655 Map *map = origin->GetCurrentArea();
2656 int i = map->GetActorCount(true);
2657 Actor *ac;
2658 while (i--) {
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);
2666 else {
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)
2677 Point p;
2679 AutoTable tab("entries");
2680 if (!tab) {
2681 return p;
2683 const char *tmpstr = tab->QueryField(areaname, entryname);
2684 int x=-1;
2685 int y=-1;
2686 sscanf(tmpstr, "%d.%d", &x, &y);
2687 p.x=(short) x;
2688 p.y=(short) y;
2689 return p;
2692 /* returns a spell's casting distance, it depends on the caster */
2693 unsigned int GetSpellDistance(ieResRef spellres, Actor *actor)
2695 unsigned int dist;
2697 Spell* spl = gamedata->GetSpell( spellres );
2698 if (!spl) {
2699 printMessage("GameScript"," ",LIGHT_RED);
2700 printf("Spell couldn't be found:%.8s.\n", spellres);
2701 return 0;
2703 dist=spl->GetCastingDistance(actor);
2704 gamedata->FreeSpell(spl, spellres, false);
2705 return dist*15;
2708 /* returns a spell's casting distance, it depends on the caster */
2709 unsigned int GetItemDistance(ieResRef itemres, int header)
2711 unsigned int dist;
2713 Item* itm = gamedata->GetItem( itemres );
2714 if (!itm) {
2715 printMessage("GameScript"," ",LIGHT_RED);
2716 printf("Item couldn't be found:%.8s.\n", itemres);
2717 return 0;
2719 dist=itm->GetCastingDistance(header);
2720 gamedata->FreeItem(itm, itemres, false);
2721 return dist*15;
2724 void SetupWishCore(Scriptable *Sender, int column, int picks)
2726 int count;
2727 ieVariable varname;
2728 int *selects;
2729 int i,j;
2731 AutoTable tm("wish");
2732 if (!tm) {
2733 printStatus( "ERROR", LIGHT_RED );
2734 printf( "Cannot find wish.2da.\n");
2735 return;
2738 selects = (int *) malloc(picks*sizeof(int));
2739 count = tm->GetRowCount();
2741 for(i=0;i<99;i++) {
2742 snprintf(varname,32, "wishpower%02d", i);
2743 if(CheckVariable(Sender, varname, "GLOBAL") ) {
2744 SetVariable(Sender, varname, "GLOBAL", 0);
2748 if (count<picks) {
2749 for(i=0;i<count;i++) {
2750 selects[i]=i;
2752 while(i++<picks) {
2753 selects[i]=-1;
2755 } else {
2756 for(i=0;i<picks;i++) {
2757 selects[i]=rand()%count;
2758 retry:
2759 for(j=0;j<i;j++) {
2760 if(selects[i]==selects[j]) {
2761 selects[i]++;
2762 goto retry;
2768 for (i = 0; i < picks; i++) {
2769 if (selects[i]<0)
2770 continue;
2771 int spnum = atoi( tm->QueryField( selects[i], column ) );
2772 snprintf(varname,32,"wishpower%02d", spnum);
2773 SetVariable(Sender, varname, "GLOBAL",1);
2775 free(selects);