Constificiation.
[gemrb.git] / gemrb / core / GSUtils.cpp
bloba4ac8af074a8581e807c06cd64ce1cf081a15ae3
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 <cstdio>
22 #include "GSUtils.h"
23 #include "Interface.h"
24 #include "TileMap.h"
25 #include "StringMgr.h"
26 #include "Video.h"
27 #include "Audio.h"
28 #include "Spell.h"
29 #include "Item.h"
30 #include "Map.h"
31 #include "Game.h"
32 #include "GameControl.h"
33 #include "GameData.h"
35 #include "strrefs.h"
36 #include "defsounds.h"
38 //these tables will get freed by Core
39 Holder<SymbolMgr> triggersTable;
40 Holder<SymbolMgr> actionsTable;
41 Holder<SymbolMgr> objectsTable;
42 TriggerFunction triggers[MAX_TRIGGERS];
43 ActionFunction actions[MAX_ACTIONS];
44 short actionflags[MAX_ACTIONS];
45 short triggerflags[MAX_TRIGGERS];
46 ObjectFunction objects[MAX_OBJECTS];
47 IDSFunction idtargets[MAX_OBJECT_FIELDS];
48 Cache SrcCache; //cache for string resources (pst)
49 Cache BcsCache; //cache for scripts
50 int ObjectIDSCount = 7;
51 int MaxObjectNesting = 5;
52 bool HasAdditionalRect = false;
53 bool HasTriggerPoint = false;
54 //released by ReleaseMemory
55 ieResRef *ObjectIDSTableNames;
56 int ObjectFieldsCount = 7;
57 int ExtraParametersCount = 0;
58 int InDebug = 0;
59 int happiness[3][20];
60 int RandomNumValue;
61 int *SkillStats=NULL;
62 int SkillCount=-1;
63 // reaction modifiers (by reputation and charisma)
64 int rmodrep[20];
65 int rmodchr[25];
67 void InitScriptTables()
69 //initializing the skill->stats conversion table
71 AutoTable tab("skillsta");
72 if (tab) {
73 int rowcount = tab->GetRowCount();
74 SkillCount = rowcount;
75 if (rowcount) {
76 SkillStats = (int *) malloc(rowcount * sizeof(int) );
77 while(rowcount--) {
78 SkillStats[rowcount]=strtol(tab->QueryField(rowcount,0), NULL, 0);
83 //initializing the happiness table
85 AutoTable tab("happy");
86 if (tab) {
87 for (int alignment=0;alignment<3;alignment++) {
88 for (int reputation=0;reputation<20;reputation++) {
89 happiness[alignment][reputation]=strtol(tab->QueryField(reputation,alignment), NULL, 0);
95 //initializing the reaction mod. reputation table
96 AutoTable rmr("rmodrep");
97 if (rmr) {
98 for (int reputation=0; reputation<20; reputation++) {
99 rmodrep[reputation] = strtol(rmr->QueryField(0, reputation), NULL, 0);
103 //initializing the reaction mod. charisma table
104 AutoTable rmc("rmodchr");
105 if (rmc) {
106 for (int charisma=0; charisma<25; charisma++) {
107 rmodchr[charisma] = strtol(rmc->QueryField(0, charisma), NULL, 0);
112 int GetReaction(Actor *target, Scriptable *Sender)
114 int chr, rep, reaction;
115 chr = target->GetStat(IE_CHR)-1;
116 if (target->GetStat(IE_EA) == EA_PC) {
117 rep = core->GetGame()->Reputation/10;
118 } else {
119 rep = target->GetStat(IE_REPUTATION);
121 reaction = 10 + rmodrep[rep] + rmodchr[chr];
123 // add -4 penalty when dealing with racial enemies
124 if (Sender && target->GetRangerLevel() && Sender->Type == ST_ACTOR && target->IsRacialEnemy((Actor *)Sender)) {
125 reaction -= 4;
128 return reaction;
131 int GetHappiness(Scriptable* Sender, int reputation)
133 if (Sender->Type != ST_ACTOR) {
134 return 0;
136 Actor* ab = ( Actor* ) Sender;
137 int alignment = ab->GetStat(IE_ALIGNMENT)&AL_GE_MASK; //good / evil
138 if (reputation > 200) {
139 reputation = 200;
141 return happiness[alignment][reputation/10-1];
144 int GetHPPercent(Scriptable* Sender)
146 if (Sender->Type != ST_ACTOR) {
147 return 0;
149 Actor* ab = ( Actor* ) Sender;
150 int hp1 = ab->GetStat(IE_MAXHITPOINTS);
151 if (hp1<1) {
152 return 0;
154 int hp2 = ab->GetBase(IE_HITPOINTS);
155 if (hp2<1) {
156 return 0;
158 return hp2*100/hp1;
161 void HandleBitMod(ieDword &value1, ieDword value2, int opcode)
163 switch(opcode) {
164 case BM_AND:
165 value1 = ( value1& value2 );
166 break;
167 case BM_OR:
168 value1 = ( value1| value2 );
169 break;
170 case BM_XOR:
171 value1 = ( value1^ value2 );
172 break;
173 case BM_NAND: //this is a GemRB extension
174 value1 = ( value1& ~value2 );
175 break;
176 case BM_SET: //this is a GemRB extension
177 value1 = value2;
178 break;
182 // SPIT is not in the original engine spec, it is reserved for the
183 // enchantable items feature
184 // 0 1 2 3 4
185 static const char *spell_suffices[]={"SPIT","SPPR","SPWI","SPIN","SPCL"};
187 //this function handles the polymorphism of Spell[RES] actions
188 //it returns spellres
189 bool ResolveSpellName(ieResRef spellres, Action *parameters)
191 if (parameters->string0Parameter[0]) {
192 strnlwrcpy(spellres, parameters->string0Parameter, 8);
193 } else {
194 //resolve spell
195 int type = parameters->int0Parameter/1000;
196 int spellid = parameters->int0Parameter%1000;
197 if (type>4) {
198 return false;
200 sprintf(spellres, "%s%03d", spell_suffices[type], spellid);
202 return gamedata->Exists(spellres, IE_SPL_CLASS_ID);
205 void ResolveSpellName(ieResRef spellres, ieDword number)
207 //resolve spell
208 unsigned int type = number/1000;
209 int spellid = number%1000;
210 if (type>4) {
211 type=0;
213 sprintf(spellres, "%s%03d", spell_suffices[type], spellid);
216 ieDword ResolveSpellNumber(const ieResRef spellres)
218 int i;
220 for(i=0;i<5;i++) {
221 if(!strnicmp(spellres, spell_suffices[i], 4)) {
222 int n = -1;
223 sscanf(spellres+4,"%d", &n);
224 if (n<0) {
225 return 0xffffffff;
227 return i*1000+n;
230 return 0xffffffff;
233 bool ResolveItemName(ieResRef itemres, Actor *act, ieDword Slot)
235 CREItem *itm = act->inventory.GetSlotItem(Slot);
236 if(itm) {
237 strnlwrcpy(itemres, itm->ItemResRef, 8);
238 return gamedata->Exists(itemres, IE_ITM_CLASS_ID);
240 return false;
243 bool StoreHasItemCore(const ieResRef storename, const ieResRef itemname)
245 bool had_nostore=false;
246 bool has_current=false;
247 ieResRef current;
248 ieVariable owner;
249 CREItem item;
251 Store *store = core->GetCurrentStore();
252 if (!store) {
253 had_nostore = true;
254 store = core->SetCurrentStore(storename, NULL);
255 } else {
256 if (strnicmp(store->Name, storename, 8) ) {
257 //not the current store, we need some dirty hack
258 has_current = true;
259 strnlwrcpy(current, store->Name, 8);
260 strnuprcpy(owner, store->GetOwner(), 32);
263 if (!store) {
264 printMessage("GameScript","Store cannot be opened!\n", LIGHT_RED);
265 return false;
268 bool ret = false;
269 //don't use triggers (pst style), it would be possible to create infinite loops
270 if (store->FindItem(itemname, false) != (unsigned int)-1) {
271 ret=true;
273 if (has_current) {
274 //setting back old store (this will save our current store)
275 core->SetCurrentStore(current, owner);
276 } else if (had_nostore) {
277 core->CloseCurrentStore();
279 return ret;
282 //don't pass this point by reference, it is subject to change
283 void ClickCore(Scriptable *Sender, Point point, int type, int speed)
285 Map *map = Sender->GetCurrentArea();
286 if (!map) {
287 Sender->ReleaseCurrentAction();
288 return;
290 Point p=map->TMap->GetMapSize();
291 if (!p.PointInside(point)) {
292 Sender->ReleaseCurrentAction();
293 return;
295 Video *video = core->GetVideoDriver();
296 GlobalTimer *timer = core->timer;
297 timer->SetMoveViewPort( point.x, point.y, speed, true );
298 timer->DoStep(0);
299 if (timer->ViewportIsMoving()) {
300 Sender->AddActionInFront( Sender->GetCurrentAction() );
301 Sender->SetWait(1);
302 Sender->ReleaseCurrentAction();
303 return;
306 video->ConvertToScreen(point.x, point.y);
307 GameControl *win = core->GetGameControl();
309 point.x+=win->XPos;
310 point.y+=win->YPos;
311 video->MoveMouse(point.x, point.y);
312 video->ClickMouse(type);
313 Sender->ReleaseCurrentAction();
316 void TransformItemCore(Actor *actor, Action *parameters, bool onlyone)
318 int i = actor->inventory.GetSlotCount();
319 while(i--) {
320 CREItem *item = actor->inventory.GetSlotItem(i);
321 if (!item) {
322 continue;
324 if (strnicmp(item->ItemResRef, parameters->string0Parameter, 8) ) {
325 continue;
327 actor->inventory.SetSlotItemRes(parameters->string1Parameter,i,parameters->int0Parameter,parameters->int1Parameter,parameters->int2Parameter);
328 if (onlyone) {
329 break;
334 //check if an inventory (container or actor) has item (could be recursive ?)
335 bool HasItemCore(Inventory *inventory, const ieResRef itemname, ieDword flags)
337 if (inventory->HasItem(itemname, flags)) {
338 return true;
340 int i=inventory->GetSlotCount();
341 while (i--) {
342 //maybe we could speed this up if we mark bag items with a flags bit
343 CREItem *itemslot = inventory->GetSlotItem(i);
344 if (!itemslot)
345 continue;
346 Item *item = gamedata->GetItem(itemslot->ItemResRef);
347 if (!item)
348 continue;
349 bool ret = false;
350 if (core->CanUseItemType(SLOT_BAG,item,NULL) ) {
351 //the store is the same as the item's name
352 ret = StoreHasItemCore(itemslot->ItemResRef, itemname);
354 gamedata->FreeItem(item, itemslot->ItemResRef);
355 if (ret) {
356 return true;
359 return false;
362 void DisplayStringCore(Scriptable* Sender, int Strref, int flags)
364 StringBlock sb;
366 //no one hears you when you are in the Limbo!
367 if (!Sender->GetCurrentArea()) {
368 return;
371 memset(&sb,0,sizeof(sb));
372 printf( "Displaying string on: %s\n", Sender->GetScriptName() );
373 if (flags & DS_CONST) {
374 if (Sender->Type!=ST_ACTOR) {
375 printMessage("GameScript","Verbal constant not supported for non actors!\n", LIGHT_RED);
376 return;
378 Actor* actor = ( Actor* ) Sender;
379 if ((ieDword) Strref>=VCONST_COUNT) {
380 printMessage("GameScript","Invalid verbal constant!\n", LIGHT_RED);
381 return;
384 int tmp=(int) actor->StrRefs[Strref];
385 if (tmp <= 0 || (actor->GetStat(IE_MC_FLAGS) & MC_EXPORTABLE)) {
386 //get soundset based string constant
387 actor->ResolveStringConstant( sb.Sound, (unsigned int) Strref);
389 Strref = tmp;
391 //display the verbal constants in the console
392 ieDword charactersubtitles = 0;
393 core->GetDictionary()->Lookup("Subtitles", charactersubtitles);
394 if (charactersubtitles) {
395 flags |= DS_CONSOLE;
399 if ((Strref != -1) && !sb.Sound[0]) {
400 sb = core->strings->GetStringBlock( Strref );
401 if (sb.text[0] && strcmp(sb.text," ") && (flags & DS_CONSOLE)) {
402 //can't play the sound here, we have to delay action
403 //and for that, we have to know how long the text takes
404 if(flags&DS_NONAME) {
405 core->DisplayString( sb.text );
406 } else {
407 core->DisplayStringName( Strref, 0xf0f0f0, Sender, 0);
410 if (sb.text[0] && strcmp(sb.text," ") && (flags & (DS_HEAD | DS_AREA))) {
411 Sender->DisplayHeadText( sb.text );
412 //don't free sb.text, it is residing in Sender
413 if (flags & DS_AREA) {
414 Sender->FixHeadTextPos();
416 } else {
417 core->FreeString( sb.text );
420 if (sb.Sound[0] && !(flags&DS_SILENT) ) {
421 ieDword speech = GEM_SND_RELATIVE; //disable position
422 if (flags&DS_SPEECH) speech|=GEM_SND_SPEECH;
423 ieDword len = core->GetAudioDrv()->Play( sb.Sound,0,0,speech );
424 ieDword counter = ( AI_UPDATE_TIME * len ) / 1000;
425 if ((counter != 0) && (flags &DS_WAIT) )
426 Sender->SetWait( counter );
430 int CanSee(Scriptable* Sender, Scriptable* target, bool range, int seeflag)
432 Map *map;
434 if (target->Type==ST_ACTOR) {
435 Actor *tar = (Actor *) target;
437 if (!tar->ValidTarget(seeflag)) {
438 return 0;
442 map = target->GetCurrentArea();
443 if (!(seeflag&GA_GLOBAL)) {
444 if ( map!=Sender->GetCurrentArea() ) {
445 return 0;
449 if (range) {
450 unsigned int dist;
452 if (Sender->Type == ST_ACTOR) {
453 Actor* snd = ( Actor* ) Sender;
454 dist = snd->Modified[IE_VISUALRANGE];
455 } else {
456 dist = 30;
459 if (Distance(target->Pos, Sender->Pos) > dist * 15) {
460 return 0;
464 return map->IsVisible(target->Pos, Sender->Pos);
467 //non actors can see too (reducing function to LOS)
468 //non actors can be seen too (reducing function to LOS)
469 int SeeCore(Scriptable* Sender, Trigger* parameters, int justlos)
471 //see dead
472 int flags;
474 if (parameters->int0Parameter) {
475 flags = 0;
476 } else {
477 flags = GA_NO_DEAD;
479 Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter, flags );
480 /* don't set LastSeen if this isn't an actor */
481 if (!tar) {
482 return 0;
484 //both are actors
485 if (CanSee(Sender, tar, true, flags) ) {
486 if (justlos) {
487 return 1;
489 if (Sender->Type==ST_ACTOR && tar->Type==ST_ACTOR) {
490 Actor* snd = ( Actor* ) Sender;
491 //additional checks for invisibility?
492 snd->LastSeen = ((Actor *) tar)->GetID();
494 return 1;
496 return 0;
499 //transfering item from Sender to target
500 //if target has no inventory, the item will be destructed
501 //if target can't get it, it will be dropped at its feet
502 int MoveItemCore(Scriptable *Sender, Scriptable *target, const char *resref, int flags, int setflag)
504 Inventory *myinv;
505 Map *map;
506 // track whether we are dealing with our party and need to display feedback
507 bool lostitem = false;
508 bool gotitem = false;
510 if (!target) {
511 return MIC_INVALID;
513 map=Sender->GetCurrentArea();
514 switch(Sender->Type) {
515 case ST_ACTOR:
516 myinv=&((Actor *) Sender)->inventory;
517 if (((Actor *)Sender)->InParty) lostitem = true;
518 break;
519 case ST_CONTAINER:
520 myinv=&((Container *) Sender)->inventory;
521 break;
522 default:
523 return MIC_INVALID;
525 CREItem *item;
526 myinv->RemoveItem(resref, flags, &item);
527 if (!item) {
528 // nothing was removed
529 return MIC_NOITEM;
532 item->Flags|=setflag;
534 switch(target->Type) {
535 case ST_ACTOR:
536 myinv=&((Actor *) target)->inventory;
537 if (((Actor *) target)->InParty) gotitem = true;
538 break;
539 case ST_CONTAINER:
540 myinv=&((Container *) target)->inventory;
541 break;
542 default:
543 myinv = NULL;
544 break;
546 if (!myinv) {
547 delete item;
548 if (lostitem) core->DisplayConstantString(STR_LOSTITEM, 0xbcefbc);
549 return MIC_GOTITEM; // actually it was lost, not gained
551 if ( myinv->AddSlotItem(item, SLOT_ONLYINVENTORY) !=ASI_SUCCESS) {
552 // drop it at my feet
553 map->AddItemToLocation(target->Pos, item);
554 if (gotitem) core->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc);
555 return MIC_FULL;
557 if (gotitem) core->DisplayConstantString(STR_GOTITEM, 0xbcefbc);
558 return MIC_GOTITEM;
561 static Targets* ReturnActorAsTarget(Actor *aC)
563 if (!aC) {
564 return NULL;
566 //Ok :) we now have our Object. Let's create a Target struct and add the object to it
567 Targets *tgts = new Targets( );
568 tgts->AddTarget( aC, 0, 0 );
569 //return here because object name/IDS targeting are mutually exclusive
570 return tgts;
573 Actor *FindActorNearby(const char *name, Map *except, int ga_flags)
575 Game *game = core->GetGame();
576 size_t mc = game->GetLoadedMapCount();
577 while(mc--) {
578 Map *map = game->GetMap(mc);
579 if (map==except) continue;
580 Actor * aC = map->GetActor(name, ga_flags);
581 if (aC) {
582 return aC;
585 return NULL;
588 /* returns actors that match the [x.y.z] expression */
589 static Targets* EvaluateObject(Map *map, Scriptable* Sender, Object* oC, int ga_flags)
591 if (oC->objectName[0]) {
592 //We want the object by its name... (doors/triggers don't play here!)
593 Actor* aC = map->GetActor( oC->objectName, ga_flags );
595 if (!aC && (ga_flags&GA_GLOBAL) ) {
596 aC = FindActorNearby(oC->objectName, map, ga_flags );
599 return ReturnActorAsTarget(aC);
602 if (oC->objectFields[0]==-1) {
603 Actor* aC = map->GetActorByGlobalID( (ieDword) oC->objectFields[1] );
604 /* TODO: this hack will throw away an invalid target */
605 /* Consider putting this in GetActorByGlobalID */
606 if (aC && !aC->ValidTarget(ga_flags)) {
607 aC = NULL;
609 return ReturnActorAsTarget(aC);
612 Targets *tgts=NULL;
614 //else branch, IDS targeting
615 for (int j = 0; j < ObjectIDSCount; j++) {
616 if (!oC->objectFields[j]) {
617 continue;
619 IDSFunction func = idtargets[j];
620 if (!func) {
621 printf("Unimplemented IDS targeting opcode!\n");
622 continue;
624 if (tgts) {
625 //we already got a subset of actors
626 int i = tgts->Count();
627 /*premature end, filtered everything*/
628 if (!i) {
629 break; //leaving the loop
631 targetlist::iterator m;
632 const targettype *t = tgts->GetFirstTarget(m, -1);
633 while (t) {
634 if (t->actor->Type!=ST_ACTOR) {
635 //we should never stumble here
636 abort();
637 // t = tgts->RemoveTargetAt(m);
638 continue;
640 if (!func( (Actor *) (t->actor), oC->objectFields[j] ) ) {
641 t = tgts->RemoveTargetAt(m);
642 continue;
644 t = tgts->GetNextTarget(m, -1);
646 } else {
647 //we need to get a subset of actors from the large array
648 //if this gets slow, we will need some index tables
649 int i = map->GetActorCount(true);
650 tgts = new Targets();
651 while (i--) {
652 Actor *ac=map->GetActor(i,true);
653 int dist = Distance(Sender->Pos, ac->Pos);
654 if (ac && func(ac, oC->objectFields[j]) ) {
655 // don't return Sender in IDS targeting!
656 if (ac != Sender) {
657 tgts->AddTarget((Scriptable *) ac, dist, ga_flags);
663 return tgts;
666 Targets* GetAllObjects(Map *map, Scriptable* Sender, Object* oC, int ga_flags)
668 if (!oC) {
669 return NULL;
671 Targets* tgts = EvaluateObject(map, Sender, oC, ga_flags);
672 //if we couldn't find an endpoint by name or object qualifiers
673 //it is not an Actor, but could still be a Door or Container (scriptable)
674 if (!tgts && oC->objectName[0]) {
675 return NULL;
677 //now lets do the object filter stuff, we create Targets because
678 //it is possible to start from blank sheets using endpoint filters
679 //like (Myself, Protagonist etc)
680 if (!tgts) {
681 tgts = new Targets();
683 for (int i = 0; i < MaxObjectNesting; i++) {
684 int filterid = oC->objectFilters[i];
685 if (!filterid) {
686 break;
688 ObjectFunction func = objects[filterid];
689 if (func) {
690 tgts = func( Sender, tgts, ga_flags);
692 else {
693 printMessage("GameScript"," ", YELLOW);
694 printf("Unknown object filter: %d %s\n",filterid, objectsTable->GetValue(filterid) );
696 if (!tgts->Count()) {
697 delete tgts;
698 return NULL;
701 return tgts;
704 Targets *GetAllObjectsNearby(Scriptable* Sender, Object* oC, int ga_flags)
706 Game *game = core->GetGame();
707 size_t mc = game->GetLoadedMapCount();
708 while(mc--) {
709 Map *map = game->GetMap(mc);
710 if (map==Sender->GetCurrentArea()) continue;
711 Targets *tgts = GetAllObjects(map, Sender, oC, ga_flags);
712 if (tgts) {
713 return tgts;
716 return NULL;
719 Targets *GetAllActors(Scriptable *Sender, int ga_flags)
721 Map *map=Sender->GetCurrentArea();
723 int i = map->GetActorCount(true);
724 Targets *tgts = new Targets();
725 while (i--) {
726 Actor *ac=map->GetActor(i,true);
727 int dist = Distance(Sender->Pos, ac->Pos);
728 tgts->AddTarget((Scriptable *) ac, dist, ga_flags);
730 return tgts;
733 Scriptable *GetActorObject(TileMap *TMap, const char *name)
736 Scriptable * aC = TMap->GetDoor( name );
737 if (aC) {
738 return aC;
741 //containers should have a precedence over infopoints because otherwise
742 //AR1512 sanity test quest would fail
743 //If this order couldn't be maintained, then 'Contains' should have a
744 //unique call to get containers only
745 //No... it was not an door... maybe a Container?
746 aC = TMap->GetContainer( name );
747 if (aC) {
748 return aC;
751 //No... it was not a container ... maybe an InfoPoint?
752 aC = TMap->GetInfoPoint( name );
753 if (aC) {
754 return aC;
756 return aC;
759 // blocking actions need to store some kinds of objects between ticks
760 Scriptable* GetStoredActorFromObject(Scriptable* Sender, Object* oC, int ga_flags)
762 Scriptable *tar = NULL;
763 // retrieve an existing target if it still exists and is valid
764 if (Sender->CurrentActionTarget) {
765 tar = core->GetGame()->GetActorByGlobalID(Sender->CurrentActionTarget);
766 if (tar) {
767 // always an actor, check if it satisfies flags
768 if (((Actor *)tar)->ValidTarget(ga_flags)) {
769 return tar;
772 return NULL; // target invalid/gone
774 tar = GetActorFromObject(Sender, oC, ga_flags);
775 // maybe store the target if it's an actor..
776 if (tar && tar->Type == ST_ACTOR) {
777 // .. but we only want objects created via objectFilters
778 if (oC->objectFilters[0]) {
779 Sender->CurrentActionTarget = ((Actor *)tar)->globalID;
782 return tar;
785 Scriptable* GetActorFromObject(Scriptable* Sender, Object* oC, int ga_flags)
787 Scriptable *aC = NULL;
789 if (!oC) {
790 return NULL;
792 Targets *tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, ga_flags);
793 if (tgts) {
794 //now this could return other than actor objects
795 aC = tgts->GetTarget(0,-1);
796 delete tgts;
797 if (!aC && (ga_flags&GA_GLOBAL) )
799 tgts = GetAllObjectsNearby(Sender, oC, ga_flags);
800 if (tgts) {
801 //now this could return other than actor objects
802 aC = tgts->GetTarget(0,-1);
803 delete tgts;
806 return aC;
809 if (oC->objectName[0]) {
810 aC = GetActorObject(Sender->GetCurrentArea()->GetTileMap(), oC->objectName );
811 if (aC) {
812 return aC;
814 Game *game = core->GetGame();
816 //global actors are always found by scripting name!
817 aC = game->FindPC(oC->objectName);
818 if (aC) {
819 return aC;
821 aC = game->FindNPC(oC->objectName);
822 if (aC) {
823 return aC;
826 if (ga_flags&GA_GLOBAL) {
827 size_t mc = game->GetLoadedMapCount();
828 while(mc--) {
829 Map *map = game->GetMap(mc);
830 if (map==Sender->GetCurrentArea()) continue;
831 aC = GetActorObject(map->GetTileMap(), oC->objectName);
832 if (aC) {
833 return aC;
838 return NULL;
841 /*FIXME: what is 'base'*/
842 void PolymorphCopyCore(Actor *src, Actor *tar, bool base)
844 tar->SetBase(IE_ANIMATION_ID, src->GetStat(IE_ANIMATION_ID) );
845 if (!base) {
846 tar->SetBase(IE_ARMOR_TYPE, src->GetStat(IE_ARMOR_TYPE) );
847 for (int i=0;i<7;i++) {
848 tar->SetBase(IE_COLORS+i, src->GetStat(IE_COLORS+i) );
851 tar->SetName(src->GetName(0),0);
852 tar->SetName(src->GetName(1),1);
853 //add more attribute copying
856 void CreateCreatureCore(Scriptable* Sender, Action* parameters, int flags)
858 Scriptable *tmp = GetActorFromObject( Sender, parameters->objects[1] );
859 //if there is nothing to copy, don't spawn anything
860 if (flags & CC_COPY) {
861 if (!tmp || tmp->Type != ST_ACTOR) {
862 return;
866 Actor* ab;
867 if (flags & CC_STRING1) {
868 ab = gamedata->GetCreature(parameters->string1Parameter);
870 else {
871 ab = gamedata->GetCreature(parameters->string0Parameter);
874 if (!ab) {
875 printMessage("GameScript","Failed to create creature! ",LIGHT_RED);
876 printf("(missing creature file %s?)\n", parameters->string0Parameter);
877 // maybe this should abort()?
878 return;
881 //iwd2 allows an optional scriptname to be set
882 //but bg2 doesn't have this feature
883 //this way it works for both games
884 if ((flags & CC_SCRIPTNAME) && parameters->string1Parameter[0]) {
885 ab->SetScriptName(parameters->string1Parameter);
888 int radius;
889 Point pnt;
891 radius=0;
892 switch (flags & CC_MASK) {
893 //creates creature just off the screen
894 case CC_OFFSCREEN:
896 Region vp = core->GetVideoDriver()->GetViewport();
897 radius=vp.w/2; //actually it must be further divided by the tile size, hmm 16?
899 //falling through
900 case CC_OBJECT://use object + offset
901 if (tmp) Sender=tmp;
902 //falling through
903 case CC_OFFSET://use sender + offset
904 pnt.x = parameters->pointParameter.x+Sender->Pos.x;
905 pnt.y = parameters->pointParameter.y+Sender->Pos.y;
906 break;
907 default: //absolute point, but -1,-1 means AtFeet
908 pnt.x = parameters->pointParameter.x;
909 pnt.y = parameters->pointParameter.y;
910 if (pnt.isempty()) {
911 pnt.x = Sender->Pos.x;
912 pnt.y = Sender->Pos.y;
914 break;
917 Map *map = Sender->GetCurrentArea();
918 map->AddActor( ab );
919 ab->SetPosition( pnt, flags&CC_CHECK_IMPASSABLE, radius );
920 ab->SetOrientation(parameters->int0Parameter, false );
922 //if string1 is animation, then we can't use it for a DV too
923 if (flags & CC_PLAY_ANIM) {
924 CreateVisualEffectCore( ab, ab->Pos, parameters->string1Parameter, 1);
925 } else {
926 //setting the deathvariable if it exists (iwd2)
927 if (parameters->string1Parameter[0]) {
928 ab->SetScriptName(parameters->string1Parameter);
932 if (flags & CC_COPY) {
933 PolymorphCopyCore ( (Actor *) tmp, ab, false);
937 static ScriptedAnimation *GetVVCEffect(const char *effect, int iterations)
939 if (effect[0]) {
940 ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(effect, false);
941 if (!vvc) {
942 printMessage("GameScript","Failed to create effect.",LIGHT_RED);
943 return NULL;
945 if (iterations) {
946 vvc->SetDefaultDuration( vvc->GetSequenceDuration(AI_UPDATE_TIME * iterations));
948 return vvc;
950 return NULL;
953 void CreateVisualEffectCore(Actor *target, const char *effect, int iterations)
955 ScriptedAnimation *vvc = GetVVCEffect(effect, iterations);
956 if (vvc) {
957 target->AddVVCell( vvc );
961 void CreateVisualEffectCore(Scriptable *Sender, const Point &position, const char *effect, int iterations)
963 ScriptedAnimation *vvc = GetVVCEffect(effect, iterations);
964 if (vvc) {
965 vvc->XPos +=position.x;
966 vvc->YPos +=position.y;
967 Sender->GetCurrentArea( )->AddVVCell( vvc );
971 //this destroys the current actor and replaces it with another
972 void ChangeAnimationCore(Actor *src, const char *resref, bool effect)
974 Actor *tar = gamedata->GetCreature(resref);
975 if (tar) {
976 Map *map = src->GetCurrentArea();
977 map->AddActor( tar );
978 Point pos = src->Pos;
979 tar->SetOrientation(src->GetOrientation(), false );
980 src->DestroySelf();
981 // can't SetPosition while the old actor is taking the spot
982 tar->SetPosition(pos, 1);
983 if (effect) {
984 CreateVisualEffectCore(tar, tar->Pos,"smokepuffeffect",1);
989 void EscapeAreaCore(Scriptable* Sender, const Point &p, const char* area, const Point &enter, int flags, int wait)
991 char Tmp[256];
993 if ( !p.isempty() && PersonalDistance(p, Sender)>MAX_OPERATING_DISTANCE) {
994 //MoveNearerTo will return 0, if the actor is in move
995 //it will return 1 (the fourth parameter) if the target is unreachable
996 if (!MoveNearerTo(Sender, p, MAX_OPERATING_DISTANCE,1) ) {
997 if (!Sender->InMove()) printf("At least it said so...\n");
998 return;
1002 if (flags &EA_DESTROY) {
1003 //this must be put into a non-const variable
1004 sprintf( Tmp, "DestroySelf()" );
1005 } else {
1006 // last parameter is 'face', which should be passed from relevant action parameter..
1007 sprintf( Tmp, "MoveBetweenAreas(\"%s\",[%hd.%hd],%d)", area, enter.x, enter.y, 0 );
1009 printMessage("GSUtils"," ", WHITE);
1010 printf("Executing %s in EscapeAreaCore\n", Tmp);
1011 //drop this action, but add another (destroyself or movebetweenareas)
1012 //between the arrival and the final escape, there should be a wait time
1013 //that wait time could be handled here
1014 if (wait) {
1015 printf("But wait a bit... (%d)\n", wait);
1016 Sender->SetWait(wait);
1018 Sender->ReleaseCurrentAction();
1019 Action * action = GenerateAction( Tmp);
1020 Sender->AddActionInFront( action );
1023 void GetTalkPositionFromScriptable(Scriptable* scr, Point &position)
1025 switch (scr->Type) {
1026 case ST_AREA: case ST_GLOBAL:
1027 position = scr->Pos; //fake
1028 break;
1029 case ST_ACTOR:
1030 //if there are other moveables, put them here
1031 position = ((Movable *) scr)->GetMostLikelyPosition();
1032 break;
1033 case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL:
1034 if (((InfoPoint *) scr)->Flags & TRAP_USEPOINT) {
1035 position=((InfoPoint *) scr)->UsePoint;
1036 break;
1038 position=((InfoPoint *) scr)->TrapLaunch;
1039 break;
1040 case ST_DOOR: case ST_CONTAINER:
1041 position=((Highlightable *) scr)->TrapLaunch;
1042 break;
1046 void GetPositionFromScriptable(Scriptable* scr, Point &position, bool dest)
1048 if (!dest) {
1049 position = scr->Pos;
1050 return;
1052 switch (scr->Type) {
1053 case ST_AREA: case ST_GLOBAL:
1054 position = scr->Pos; //fake
1055 break;
1056 case ST_ACTOR:
1057 //if there are other moveables, put them here
1058 position = ((Movable *) scr)->GetMostLikelyPosition();
1059 break;
1060 case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL:
1061 if (((InfoPoint *) scr)->Flags & TRAP_USEPOINT) {
1062 position=((InfoPoint *) scr)->UsePoint;
1063 break;
1065 case ST_DOOR: case ST_CONTAINER:
1066 position=((Highlightable *) scr)->TrapLaunch;
1070 int CheckInteract(const char *talker, const char *target)
1072 AutoTable interact("interact");
1073 if(!interact)
1074 return 0;
1075 const char *value = interact->QueryField(talker, target);
1076 if(!value)
1077 return 0;
1078 switch(value[0]) {
1079 case 's':
1080 return I_SPECIAL;
1081 case 'c':
1082 return I_COMPLIMENT;
1083 case 'i':
1084 return I_INSULT;
1086 return 0;
1089 static ieResRef PlayerDialogRes = "PLAYERx\0";
1091 void BeginDialog(Scriptable* Sender, Action* parameters, int Flags)
1093 Scriptable* tar, *scr;
1094 int seeflag = GA_NO_DEAD;
1096 if (InDebug&ID_VARIABLES) {
1097 printf("BeginDialog core\n");
1099 if (Flags & BD_OWN) {
1100 tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag);
1101 scr = tar;
1102 } else {
1103 tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag);
1104 scr = Sender;
1106 if (!scr) {
1107 printMessage("GameScript"," ",LIGHT_RED);
1108 printf("Speaker for dialog couldn't be found (Sender: %s, Type: %d) Flags:%d.\n", Sender->GetScriptName(), Sender->Type, Flags);
1109 Sender->ReleaseCurrentAction();
1110 return;
1113 if (!tar || tar->Type!=ST_ACTOR) {
1114 printMessage("GameScript"," ",LIGHT_RED);
1115 printf("Target for dialog couldn't be found (Sender: %s, Type: %d).\n", Sender->GetScriptName(), Sender->Type);
1116 if (Sender->Type == ST_ACTOR) {
1117 ((Actor *) Sender)->DebugDump();
1119 printf ("Target object: ");
1120 if (parameters->objects[1]) {
1121 parameters->objects[1]->Dump();
1122 } else {
1123 printf("<NULL>\n");
1125 Sender->ReleaseCurrentAction();
1126 return;
1129 Actor *speaker, *target;
1131 speaker = NULL;
1132 target = (Actor *) tar;
1133 if ((Flags & BD_CHECKDIST) && !CanSee(scr, target, false, seeflag) ) {
1134 printMessage("GameScript"," ",LIGHT_RED);
1135 printf("CanSee returned false! Speaker (%s, type %d) and target are:\n", scr->GetScriptName(), scr->Type);
1136 if (scr->Type == ST_ACTOR) {
1137 ((Actor *) scr)->DebugDump();
1139 ((Actor *) tar)->DebugDump();
1140 Sender->ReleaseCurrentAction();
1141 return;
1143 bool swap = false;
1144 if (scr->Type==ST_ACTOR) {
1145 speaker = (Actor *) scr;
1146 if (speaker->GetStat(IE_STATE_ID)&STATE_DEAD) {
1147 printMessage("GameScript"," ",LIGHT_RED);
1148 printf("Speaker is dead, cannot start dialogue. Speaker and target are:\n");
1149 speaker->DebugDump();
1150 target->DebugDump();
1151 Sender->ReleaseCurrentAction();
1152 return;
1154 ieDword range = MAX_OPERATING_DISTANCE;
1155 //making sure speaker is the protagonist, player, actor
1156 if ( target->InParty == 1) swap = true;
1157 else if ( speaker->InParty !=1 && target->InParty) swap = true;
1158 //CHECKDIST works only for mobile scriptables
1159 if (Flags&BD_CHECKDIST) {
1160 if ( scr->GetCurrentArea()!=target->GetCurrentArea() ||
1161 PersonalDistance(scr, target)>range) {
1162 MoveNearerTo(Sender, target, MAX_OPERATING_DISTANCE);
1163 return;
1166 } else {
1167 //pst style dialog with trigger points
1168 swap=true;
1169 if (Flags&BD_CHECKDIST) {
1170 Point TalkPos;
1172 if (target->InMove()) {
1173 //waiting for target
1174 Sender->AddActionInFront( Sender->GetCurrentAction() );
1175 Sender->ReleaseCurrentAction();
1176 Sender->SetWait(1);
1177 return;
1179 GetTalkPositionFromScriptable(scr, TalkPos);
1180 if (PersonalDistance(TalkPos, target)>MAX_OPERATING_DISTANCE ) {
1181 //try to force the target to come closer???
1182 GoNear(target, TalkPos);
1183 Sender->AddActionInFront( Sender->GetCurrentAction() );
1184 Sender->ReleaseCurrentAction();
1185 Sender->SetWait(1);
1186 return;
1191 GameControl* gc = core->GetGameControl();
1192 if (!gc) {
1193 printMessage( "GameScript","Dialog cannot be initiated because there is no GameControl.", YELLOW );
1194 Sender->ReleaseCurrentAction();
1195 return;
1197 //can't initiate dialog, because it is already there
1198 if (gc->GetDialogueFlags()&DF_IN_DIALOG) {
1199 if (Flags & BD_INTERRUPT) {
1200 //break the current dialog if possible
1201 gc->EndDialog(true);
1203 //check if we could manage to break it, not all dialogs are breakable!
1204 if (gc->GetDialogueFlags()&DF_IN_DIALOG) {
1205 printMessage( "GameScript","Dialog cannot be initiated because there is already one.", YELLOW );
1206 Sender->ReleaseCurrentAction();
1207 return;
1211 // starting a dialog ends cutscenes!
1212 core->SetCutSceneMode(false);
1214 const char* Dialog = NULL;
1215 AutoTable pdtable;
1217 switch (Flags & BD_LOCMASK) {
1218 case BD_STRING0:
1219 Dialog = parameters->string0Parameter;
1220 if (Flags & BD_SETDIALOG) {
1221 scr->SetDialog( Dialog );
1223 break;
1224 case BD_SOURCE:
1225 case BD_TARGET:
1226 if (swap) Dialog = scr->GetDialog();
1227 else Dialog = target->GetDialog(GD_FEEDBACK);
1228 break;
1229 case BD_RESERVED:
1230 //what if playerdialog was initiated from Player2?
1231 PlayerDialogRes[5] = '1';
1232 Dialog = ( const char * ) PlayerDialogRes;
1233 break;
1234 case BD_INTERACT: //using the source for the dialog
1235 const char* scriptingname = scr->GetScriptName();
1237 /* use interact.2da for short, inlined dialogue */
1238 int type = CheckInteract(scriptingname, target->GetScriptName());
1239 if(type) {
1240 speaker->Interact(type);
1241 target->Response(type);
1242 Sender->ReleaseCurrentAction();
1243 return;
1245 /* banter dialogue */
1246 pdtable.load("interdia");
1247 //Dialog is a borrowed reference, we cannot free pdtable while it is being used
1248 if (pdtable) {
1249 Dialog = pdtable->QueryField( scriptingname, "FILE" );
1251 break;
1255 //dialog is not meaningful
1256 if (!Dialog || Dialog[0]=='*') {
1257 Sender->ReleaseCurrentAction();
1258 return;
1261 //maybe we should remove the action queue, but i'm unsure
1262 //no, we shouldn't even call this!
1263 //Sender->ReleaseCurrentAction();
1265 // moved this here from InitDialog, because InitDialog doesn't know which side is which
1266 // post-swap (and non-actors always have IF_NOINT set) .. also added a check that it's
1267 // actually busy doing something, for the same reason
1268 if (target->GetInternalFlag()&IF_NOINT && (target->GetCurrentAction() || target->GetNextAction())) {
1269 core->DisplayConstantString(STR_TARGETBUSY,0xff0000);
1270 Sender->ReleaseCurrentAction();
1271 return;
1274 if (speaker!=target) {
1275 if (swap) {
1276 Scriptable *tmp = tar;
1277 tar = scr;
1278 scr = tmp;
1279 } else {
1280 if (!(Flags & BD_INTERRUPT)) {
1281 // added CurrentAction as part of blocking action fixes
1282 if (tar->GetCurrentAction() || tar->GetNextAction()) {
1283 core->DisplayConstantString(STR_TARGETBUSY,0xff0000);
1284 Sender->ReleaseCurrentAction();
1285 return;
1291 //don't clear target's actions, because a sequence like this will be broken:
1292 //StartDialog([PC]); SetGlobal("Talked","LOCALS",1);
1293 if (scr!=tar) {
1294 if (scr->Type==ST_ACTOR) {
1295 ((Actor *) scr)->SetOrientation(GetOrient( tar->Pos, scr->Pos), true);
1297 if (tar->Type==ST_ACTOR) {
1298 ((Actor *) tar)->SetOrientation(GetOrient( scr->Pos, tar->Pos), true);
1302 int ret;
1304 if (Dialog[0]) {
1305 //increasing NumTimesTalkedTo or NumTimesInteracted
1306 if (Flags & BD_TALKCOUNT) {
1307 gc->SetDialogueFlags(DF_TALKCOUNT, BM_OR);
1308 } else if ((Flags & BD_LOCMASK) == BD_INTERACT) {
1309 gc->SetDialogueFlags(DF_INTERACT, BM_OR);
1312 core->GetDictionary()->SetAt("DialogChoose",(ieDword) -1);
1313 ret = gc->InitDialog( scr, tar, Dialog);
1315 else {
1316 ret = -1;
1319 if (ret<0) {
1320 Sender->ReleaseCurrentAction();
1321 if (Flags & BD_NOEMPTY) {
1322 return;
1324 core->DisplayConstantStringName(STR_NOTHINGTOSAY,0xff0000,tar);
1325 return;
1328 //this is a bit fishy
1329 Sender->SetWait(1);
1330 Sender->ReleaseCurrentAction();
1334 void MoveBetweenAreasCore(Actor* actor, const char *area, const Point &position, int face, bool adjust)
1336 printMessage("GameScript", " ", WHITE);
1337 printf("MoveBetweenAreas: %s to %s [%d.%d] face: %d\n", actor->GetName(0), area,position.x,position.y, face);
1338 Map* map2;
1339 Game* game = core->GetGame();
1340 if (area[0]) { //do we need to switch area?
1341 Map* map1 = actor->GetCurrentArea();
1342 //we have to change the pathfinder
1343 //to the target area if adjust==true
1344 map2 = game->GetMap(area, false);
1345 if ( map1!=map2 ) {
1346 if (map1) {
1347 map1->RemoveActor( actor );
1349 map2->AddActor( actor );
1352 actor->SetPosition(position, adjust);
1353 if (face !=-1) {
1354 actor->SetOrientation( face, false );
1356 // should this perhaps be a 'selected' check or similar instead?
1357 if (actor->InParty) {
1358 GameControl *gc=core->GetGameControl();
1359 gc->SetScreenFlags(SF_CENTERONACTOR,BM_OR);
1363 //repeat movement, until goal isn't reached
1364 //if int0parameter is !=0, then it will try only x times
1365 void MoveToObjectCore(Scriptable *Sender, Action *parameters, ieDword flags, bool untilsee)
1367 if (Sender->Type != ST_ACTOR) {
1368 Sender->ReleaseCurrentAction();
1369 return;
1371 Scriptable* target = GetStoredActorFromObject( Sender, parameters->objects[1] );
1372 if (!target) {
1373 Sender->ReleaseCurrentAction();
1374 return;
1376 Actor* actor = ( Actor* ) Sender;
1377 if (untilsee && CanSee(actor, target, true, 0) ) {
1378 Sender->ReleaseCurrentAction();
1379 return;
1380 } else {
1381 if (PersonalDistance(actor, target)<MAX_OPERATING_DISTANCE) {
1382 Sender->ReleaseCurrentAction();
1383 return;
1386 if (!actor->InMove() || actor->Destination != target->Pos) {
1387 actor->WalkTo( target->Pos, flags, 0 );
1389 //hopefully this hack will prevent lockups
1390 if (!actor->InMove()) {
1391 Sender->ReleaseCurrentAction();
1392 return;
1395 //repeat movement...
1396 Action *newaction = ParamCopyNoOverride(parameters);
1397 if (newaction->int0Parameter!=1) {
1398 if (newaction->int0Parameter) {
1399 newaction->int0Parameter--;
1401 actor->AddActionInFront(newaction);
1402 actor->SetWait(1);
1405 Sender->ReleaseCurrentAction();
1408 void CreateItemCore(CREItem *item, const char *resref, int a, int b, int c)
1410 strncpy(item->ItemResRef, resref, 8);
1411 core->ResolveRandomItem(item);
1412 if (a==-1) {
1413 Item *origitem = gamedata->GetItem(resref);
1414 for(int i=0;i<3;i++) {
1415 ITMExtHeader *e = origitem->GetExtHeader(i);
1416 item->Usages[i]=e?e->Charges:0;
1418 gamedata->FreeItem(origitem, resref, false);
1419 } else {
1420 item->Usages[0]=(ieWord) a;
1421 item->Usages[1]=(ieWord) b;
1422 item->Usages[2]=(ieWord) c;
1424 item->Flags=0;
1427 //It is possible to attack CONTAINERS/DOORS as well!!!
1428 void AttackCore(Scriptable *Sender, Scriptable *target, int flags)
1430 //this is a dangerous cast, make sure actor is Actor * !!!
1431 Actor *actor = (Actor *) Sender;
1433 WeaponInfo wi;
1434 ITMExtHeader *header = NULL;
1435 ITMExtHeader *hittingheader = NULL;
1436 int tohit;
1437 ieDword Flags;
1438 int DamageBonus, CriticalBonus;
1439 int speed, style;
1441 //bool leftorright = (bool) ((attacksperround-attackcount)&1);
1442 bool leftorright = false;
1444 //will return false on any errors (eg, unusable weapon)
1445 if (!actor->GetCombatDetails(tohit, leftorright, wi, header, hittingheader, Flags, DamageBonus, speed, CriticalBonus, style)) {
1446 Sender->ReleaseCurrentAction();
1447 return;
1450 if (header) wi.range *= 10;
1451 else wi.range = 0;
1453 if ( target->Type == ST_DOOR || target->Type == ST_CONTAINER) {
1454 wi.range += 10;
1456 Actor *tar = NULL;
1457 ieDword targetID = 0;
1458 if (target->Type==ST_ACTOR) {
1459 tar = (Actor *) target;
1460 targetID = tar->GetID();
1462 if (actor == tar) {
1463 Sender->ReleaseCurrentAction();
1464 return;
1466 if (!(flags&AC_NO_SOUND) ) {
1467 if (actor->LastTarget != targetID) {
1468 //play attack sound for party members
1469 if (actor->InParty) {
1470 //pick from all 5 possible verbal constants
1471 actor->VerbalConstant(VB_ATTACK, 5);
1472 //DisplayStringCore(Sender, VB_ATTACK, DS_CONSOLE|DS_CONST );
1474 //display attack message
1475 core->DisplayConstantStringAction(STR_ACTION_ATTACK,0xf0f0f0, Sender, target);
1478 //action performed
1479 if(target->Type == ST_ACTOR) {
1480 actor->SetTarget( target );
1482 if ( Sender->GetCurrentArea()!=target->GetCurrentArea() ||
1483 (PersonalDistance(Sender, target) > wi.range) ) {
1484 MoveNearerTo(Sender, target, wi.range);
1485 return;
1486 } else if (target->Type == ST_DOOR) {
1487 //Forcing a lock does not launch the trap...
1488 Door* door = (Door*) target;
1489 if(door->Flags & DOOR_LOCKED) {
1490 door->TryBashLock(actor);
1492 Sender->ReleaseCurrentAction();
1493 return;
1494 } else if (target->Type == ST_CONTAINER) {
1495 Container* cont = (Container*) target;
1496 if(cont->Flags & CONT_LOCKED) {
1497 cont->TryBashLock(actor);
1499 Sender->ReleaseCurrentAction();
1500 return;
1503 actor->PerformAttack(core->GetGame()->GameTime);
1506 bool MatchActor(Scriptable *Sender, ieDword actorID, Object* oC)
1508 if (!Sender) {
1509 return false;
1511 Targets *tgts;
1512 if (oC) {
1513 tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, 0);
1514 } else {
1515 // [0]/[ANYONE] can match all actors
1516 tgts = GetAllActors(Sender, 0);
1519 bool ret = false;
1521 if (tgts) {
1522 targetlist::iterator m;
1523 const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR);
1524 while (tt) {
1525 Actor *actor = (Actor *) tt->actor;
1526 if (actor->GetID() == actorID) {
1527 ret = true;
1528 break;
1530 tt = tgts->GetNextTarget(m, ST_ACTOR);
1533 delete tgts;
1534 return ret;
1537 int GetObjectCount(Scriptable* Sender, Object* oC)
1539 if (!oC) {
1540 return 0;
1542 // EvaluateObject will return [PC]
1543 // GetAllObjects will also return Myself (evaluates object filters)
1544 // i believe we need the latter here
1545 Targets* tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, 0);
1546 int count = tgts->Count();
1547 delete tgts;
1548 return count;
1551 //TODO:
1552 //check numcreaturesatmylevel(myself, 1)
1553 //when the actor is alone
1554 //it should (obviously) return true if the trigger
1555 //evaluates object filters
1556 //also check numcreaturesgtmylevel(myself,0) with
1557 //actor having at high level
1558 int GetObjectLevelCount(Scriptable* Sender, Object* oC)
1560 if (!oC) {
1561 return 0;
1563 // EvaluateObject will return [PC]
1564 // GetAllObjects will also return Myself (evaluates object filters)
1565 // i believe we need the latter here
1566 Targets* tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, 0);
1567 int count = 0;
1568 if (tgts) {
1569 targetlist::iterator m;
1570 const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR);
1571 while (tt) {
1572 count += ((Actor *) tt->actor)->GetXPLevel(true);
1573 tt = tgts->GetNextTarget(m, ST_ACTOR);
1576 delete tgts;
1577 return count;
1580 //we need this because some special characters like _ or * are also accepted
1581 inline bool ismysymbol(const char letter)
1583 if (letter==']') return false;
1584 if (letter=='[') return false;
1585 if (letter==')') return false;
1586 if (letter=='(') return false;
1587 if (letter=='.') return false;
1588 if (letter==',') return false;
1589 return true;
1592 //this function returns a value, symbol could be a numeric string or
1593 //a symbol from idsname
1594 static int GetIdsValue(const char *&symbol, const char *idsname)
1596 int idsfile=core->LoadSymbol(idsname);
1597 Holder<SymbolMgr> valHook = core->GetSymbol(idsfile);
1598 if (!valHook) {
1599 //FIXME:missing ids file!!!
1600 if (InDebug&ID_TRIGGERS) {
1601 printMessage("GameScript"," ",LIGHT_RED);
1602 printf("Missing IDS file %s for symbol %s!\n",idsname, symbol);
1604 return -1;
1606 char *newsymbol;
1607 int value=strtol(symbol, &newsymbol, 0);
1608 if (symbol!=newsymbol) {
1609 symbol=newsymbol;
1610 return value;
1612 char symbolname[64];
1613 int x;
1614 for (x=0;ismysymbol(*symbol) && x<(int) sizeof(symbolname)-1;x++) {
1615 symbolname[x]=*symbol;
1616 symbol++;
1618 symbolname[x]=0;
1619 return valHook->GetValue(symbolname);
1622 static void ParseIdsTarget(const char *&src, Object *&object)
1624 for (int i=0;i<ObjectFieldsCount;i++) {
1625 object->objectFields[i]=GetIdsValue(src, ObjectIDSTableNames[i]);
1626 if (*src!='.') {
1627 break;
1629 src++;
1631 src++; //skipping ]
1634 //this will skip to the next element in the prototype of an action/trigger
1635 #define SKIP_ARGUMENT() while (*str && ( *str != ',' ) && ( *str != ')' )) str++
1637 static void ParseObject(const char *&str,const char *&src, Object *&object)
1639 SKIP_ARGUMENT();
1640 object = new Object();
1641 switch (*src) {
1642 case '"':
1643 //Scriptable Name
1644 src++;
1645 int i;
1646 for (i=0;i<(int) sizeof(object->objectName)-1 && *src!='"';i++)
1648 object->objectName[i] = *src;
1649 src++;
1651 object->objectName[i] = 0;
1652 src++;
1653 break;
1654 case '[':
1655 src++; //skipping [
1656 ParseIdsTarget(src, object);
1657 break;
1658 default: //nested object filters
1659 int Nesting=0;
1661 while (Nesting<MaxObjectNesting) {
1662 memmove(object->objectFilters+1, object->objectFilters, (int) sizeof(int) *(MaxObjectNesting-1) );
1663 object->objectFilters[0]=GetIdsValue(src,"object");
1664 if (*src!='(') {
1665 break;
1667 src++; //skipping (
1668 if (*src==')') {
1669 break;
1671 Nesting++;
1673 if (*src=='[') {
1674 ParseIdsTarget(src, object);
1676 src+=Nesting; //skipping )
1680 /* this function was lifted from GenerateAction, to make it clearer */
1681 Action* GenerateActionCore(const char *src, const char *str, int acIndex)
1683 Action *newAction = new Action(true);
1684 newAction->actionID = (unsigned short) actionsTable->GetValueIndex( acIndex );
1685 //this flag tells us to merge 2 consecutive strings together to get
1686 //a variable (context+variablename)
1687 int mergestrings = actionflags[newAction->actionID]&AF_MERGESTRINGS;
1688 int objectCount = ( newAction->actionID == 1 ) ? 0 : 1;
1689 int stringsCount = 0;
1690 int intCount = 0;
1691 if (actionflags[newAction->actionID]&AF_DIRECT) {
1692 Object *tmp = new Object();
1693 tmp->objectFields[0] = -1;
1694 //tmp->objectFields[1] = core->GetGameControl()->targetID;
1695 newAction->objects[objectCount++] = tmp;
1697 //Here is the Action; Now we need to evaluate the parameters, if any
1698 if (*str!=')') while (*str) {
1699 if (*(str+1)!=':') {
1700 printf("Warning, parser was sidetracked: %s\n",str);
1702 switch (*str) {
1703 default:
1704 printf("Invalid type: %s\n",str);
1705 str++;
1706 break;
1708 case 'p': //Point
1709 SKIP_ARGUMENT();
1710 src++; //Skip [
1711 newAction->pointParameter.x = (short) strtol( src, (char **) &src, 10 );
1712 src++; //Skip .
1713 newAction->pointParameter.y = (short) strtol( src, (char **) &src, 10 );
1714 src++; //Skip ]
1715 break;
1717 case 'i': //Integer
1719 //going to the variable name
1720 while (*str != '*' && *str !=',' && *str != ')' ) {
1721 str++;
1723 int value;
1724 if (*str=='*') { //there may be an IDS table
1725 str++;
1726 ieVariable idsTabName;
1727 char* tmp = idsTabName;
1728 while (( *str != ',' ) && ( *str != ')' )) {
1729 *tmp = *str;
1730 tmp++;
1731 str++;
1733 *tmp = 0;
1734 if (idsTabName[0]) {
1735 value = GetIdsValue(src, idsTabName);
1737 else {
1738 value = strtol( src, (char **) &src, 0);
1741 else { //no IDS table
1742 value = strtol( src, (char **) &src, 0);
1744 if (!intCount) {
1745 newAction->int0Parameter = value;
1746 } else if (intCount == 1) {
1747 newAction->int1Parameter = value;
1748 } else {
1749 newAction->int2Parameter = value;
1751 intCount++;
1753 break;
1755 case 'a':
1756 //Action
1758 SKIP_ARGUMENT();
1759 char action[257];
1760 int i = 0;
1761 int openParenthesisCount = 0;
1762 while (true) {
1763 if (*src == ')') {
1764 if (!openParenthesisCount)
1765 break;
1766 openParenthesisCount--;
1767 } else {
1768 if (*src == '(') {
1769 openParenthesisCount++;
1770 } else {
1771 if (( *src == ',' ) &&
1772 !openParenthesisCount)
1773 break;
1776 action[i] = *src;
1777 i++;
1778 src++;
1780 action[i] = 0;
1781 Action* act = GenerateAction( action);
1782 act->objects[0] = newAction->objects[0];
1783 newAction->objects[0] = NULL; //avoid freeing of object
1784 delete newAction; //freeing action
1785 newAction = act;
1787 break;
1789 case 'o': //Object
1790 if (objectCount==3) {
1791 printf("Invalid object count!\n");
1792 abort();
1794 ParseObject(str, src, newAction->objects[objectCount++]);
1795 break;
1797 case 's': //String
1799 SKIP_ARGUMENT();
1800 src++;
1801 int i;
1802 char* dst;
1803 if (!stringsCount) {
1804 dst = newAction->string0Parameter;
1805 } else {
1806 dst = newAction->string1Parameter;
1808 //if there are 3 strings, the first 2 will be merged,
1809 //the last one will be left alone
1810 if (*str==')') {
1811 mergestrings = 0;
1813 //skipping the context part, which
1814 //is to be readed later
1815 if (mergestrings) {
1816 for (i=0;i<6;i++) {
1817 *dst++='*';
1820 else {
1821 i=0;
1823 while (*src != '"') {
1824 //sizeof(context+name) = 40
1825 if (i<40) {
1826 *dst++ = (char) tolower(*src);
1827 i++;
1829 src++;
1831 *dst = 0;
1832 //reading the context part
1833 if (mergestrings) {
1834 str++;
1835 if (*str!='s') {
1836 printf("Invalid mergestrings:%s\n",str);
1837 abort();
1839 SKIP_ARGUMENT();
1840 if (!stringsCount) {
1841 dst = newAction->string0Parameter;
1842 } else {
1843 dst = newAction->string1Parameter;
1846 //this works only if there are no spaces
1847 if (*src++!='"' || *src++!=',' || *src++!='"') {
1848 break;
1850 //reading the context string
1851 i=0;
1852 while (*src != '"') {
1853 if (i++<6) {
1854 *dst++ = (char) tolower(*src);
1856 src++;
1859 src++; //skipping "
1860 stringsCount++;
1862 break;
1864 str++;
1865 if (*src == ',' || *src==')')
1866 src++;
1868 return newAction;
1871 void GoNear(Scriptable *Sender, const Point &p)
1873 if (Sender->GetCurrentAction()) {
1874 printMessage("GameScript","Target busy???\n",LIGHT_RED);
1875 return;
1877 char Tmp[256];
1878 sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y );
1879 Action * action = GenerateAction( Tmp);
1880 Sender->AddActionInFront( action );
1883 void MoveNearerTo(Scriptable *Sender, Scriptable *target, int distance)
1885 Point p;
1886 Map *myarea, *hisarea;
1888 if (Sender->Type != ST_ACTOR) {
1889 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED);
1890 Sender->ReleaseCurrentAction();
1891 return;
1894 myarea = Sender->GetCurrentArea();
1895 hisarea = target->GetCurrentArea();
1896 if (hisarea!=myarea) {
1897 target = myarea->GetTileMap()->GetTravelTo(hisarea->GetScriptName());
1899 if (!target) {
1900 printMessage("GameScript", "MoveNearerTo failed to find an exit\n", YELLOW);
1901 Sender->ReleaseCurrentAction();
1902 return;
1904 ((Actor *) Sender)->UseExit(true);
1905 } else {
1906 ((Actor *) Sender)->UseExit(false);
1908 // we deliberately don't try GetLikelyPosition here for now,
1909 // maybe a future idea if we have a better implementation
1910 // (the old code used it - by passing true not 0 below - when target was a movable)
1911 GetPositionFromScriptable(target, p, 0);
1913 // account for PersonalDistance (which caller uses, but pathfinder doesn't)
1914 if (distance && Sender->Type == ST_ACTOR) {
1915 distance += ((Actor *)Sender)->size*10;
1917 if (distance && target->Type == ST_ACTOR) {
1918 distance += ((Actor *)target)->size*10;
1921 MoveNearerTo(Sender, p, distance, 0);
1924 //It is not always good to release the current action if target is unreachable
1925 //we should also raise the trigger TargetUnreachable (if this is an Attack, at least)
1926 //i hacked only this low level function, didn't need the higher ones so far
1927 int MoveNearerTo(Scriptable *Sender, const Point &p, int distance, int dont_release)
1929 if (Sender->Type != ST_ACTOR) {
1930 printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED);
1931 Sender->ReleaseCurrentAction();
1932 return 0;
1935 Actor *actor = (Actor *)Sender;
1937 if (!actor->InMove() || actor->Destination != p) {
1938 actor->WalkTo(p, 0, distance);
1941 if (!actor->InMove()) {
1942 //didn't release
1943 if (dont_release) {
1944 return dont_release;
1946 // we can't walk any nearer to destination, give up
1947 Sender->ReleaseCurrentAction();
1949 return 0;
1952 void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool flag, int distance)
1954 Point p;
1955 GetPositionFromScriptable(target,p,flag);
1956 GoNearAndRetry(Sender, p, distance);
1959 void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance)
1961 if (!Sender->GetCurrentAction() ) {
1962 printMessage("GameScript","NULL action retried???\n",LIGHT_RED);
1963 return;
1965 Sender->AddActionInFront( Sender->GetCurrentAction() );
1966 char Tmp[256];
1967 sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y );
1968 Action * action = GenerateAction( Tmp);
1969 //experimental hack, this value means,
1970 //MoveToPoint shall pop the next action too if movement fails
1971 //and the actor is farther than distance
1972 //this will prevent deadlocks
1973 //(we have to add 1 because otherwise distance==0 fails, we subtract it in MoveToPoint)
1974 action->int0Parameter = distance+1;
1975 Sender->AddActionInFront( action );
1978 void FreeSrc(SrcVector *poi, const ieResRef key)
1980 int res = SrcCache.DecRef((void *) poi, key, true);
1981 if (res<0) {
1982 printMessage( "GameScript", "Corrupted Src cache encountered (reference count went below zero), ", LIGHT_RED );
1983 printf( "Src name is: %.8s\n", key);
1984 abort();
1986 if (!res) {
1987 delete poi;
1991 SrcVector *LoadSrc(const ieResRef resname)
1993 SrcVector *src = (SrcVector *) SrcCache.GetResource(resname);
1994 if (src) {
1995 return src;
1997 DataStream* str = gamedata->GetResource( resname, IE_SRC_CLASS_ID );
1998 if ( !str) {
1999 return NULL;
2001 ieDword size=0;
2002 str->ReadDword(&size);
2003 src = new SrcVector(size);
2004 SrcCache.SetAt( resname, (void *) src );
2005 while (size--) {
2006 ieDword tmp;
2007 str->ReadDword(&tmp);
2008 src->at(size)=tmp;
2009 str->ReadDword(&tmp);
2011 delete ( str );
2012 return src;
2015 #define MEMCPY(a,b) memcpy((a),(b),sizeof(a) )
2017 static Object *ObjectCopy(Object *object)
2019 if (!object) return NULL;
2020 Object *newObject = new Object();
2021 MEMCPY( newObject->objectFields, object->objectFields );
2022 MEMCPY( newObject->objectFilters, object->objectFilters );
2023 MEMCPY( newObject->objectRect, object->objectRect );
2024 MEMCPY( newObject->objectName, object->objectName );
2025 return newObject;
2028 Action *ParamCopy(Action *parameters)
2030 Action *newAction = new Action(true);
2031 newAction->actionID = parameters->actionID;
2032 newAction->int0Parameter = parameters->int0Parameter;
2033 newAction->int1Parameter = parameters->int1Parameter;
2034 newAction->int2Parameter = parameters->int2Parameter;
2035 newAction->pointParameter = parameters->pointParameter;
2036 MEMCPY( newAction->string0Parameter, parameters->string0Parameter );
2037 MEMCPY( newAction->string1Parameter, parameters->string1Parameter );
2038 for (int c=0;c<3;c++) {
2039 newAction->objects[c]= ObjectCopy( parameters->objects[c] );
2041 return newAction;
2044 Action *ParamCopyNoOverride(Action *parameters)
2046 Action *newAction = new Action(true);
2047 newAction->actionID = parameters->actionID;
2048 newAction->int0Parameter = parameters->int0Parameter;
2049 newAction->int1Parameter = parameters->int1Parameter;
2050 newAction->int2Parameter = parameters->int2Parameter;
2051 newAction->pointParameter = parameters->pointParameter;
2052 MEMCPY( newAction->string0Parameter, parameters->string0Parameter );
2053 MEMCPY( newAction->string1Parameter, parameters->string1Parameter );
2054 newAction->objects[0]= NULL;
2055 newAction->objects[1]= ObjectCopy( parameters->objects[1] );
2056 newAction->objects[2]= ObjectCopy( parameters->objects[2] );
2057 return newAction;
2060 Trigger *GenerateTriggerCore(const char *src, const char *str, int trIndex, int negate)
2062 Trigger *newTrigger = new Trigger();
2063 newTrigger->triggerID = (unsigned short) triggersTable->GetValueIndex( trIndex )&0x3fff;
2064 newTrigger->flags = (unsigned short) negate;
2065 int mergestrings = triggerflags[newTrigger->triggerID]&TF_MERGESTRINGS;
2066 int stringsCount = 0;
2067 int intCount = 0;
2068 //Here is the Trigger; Now we need to evaluate the parameters
2069 if (*str!=')') while (*str) {
2070 if (*(str+1)!=':') {
2071 printf("Warning, parser was sidetracked: %s\n",str);
2073 switch (*str) {
2074 default:
2075 printf("Invalid type: %s\n",str);
2076 str++;
2077 break;
2079 case 'p': //Point
2080 SKIP_ARGUMENT();
2081 src++; //Skip [
2082 newTrigger->pointParameter.x = (short) strtol( src, (char **) &src, 10 );
2083 src++; //Skip .
2084 newTrigger->pointParameter.y = (short) strtol( src, (char **) &src, 10 );
2085 src++; //Skip ]
2086 break;
2088 case 'i': //Integer
2090 //going to the variable name
2091 while (*str != '*' && *str !=',' && *str != ')' ) {
2092 str++;
2094 int value;
2095 if (*str=='*') { //there may be an IDS table
2096 str++;
2097 ieVariable idsTabName;
2098 char* tmp = idsTabName;
2099 while (( *str != ',' ) && ( *str != ')' )) {
2100 *tmp = *str;
2101 tmp++;
2102 str++;
2104 *tmp = 0;
2105 if (idsTabName[0]) {
2106 value = GetIdsValue(src, idsTabName);
2108 else {
2109 value = strtol( src, (char **) &src, 0);
2112 else { //no IDS table
2113 value = strtol( src, (char **) &src, 0);
2115 if (!intCount) {
2116 newTrigger->int0Parameter = value;
2117 } else if (intCount == 1) {
2118 newTrigger->int1Parameter = value;
2119 } else {
2120 newTrigger->int2Parameter = value;
2122 intCount++;
2124 break;
2126 case 'o': //Object
2127 ParseObject(str, src, newTrigger->objectParameter);
2128 break;
2130 case 's': //String
2132 SKIP_ARGUMENT();
2133 src++;
2134 int i;
2135 char* dst;
2136 if (!stringsCount) {
2137 dst = newTrigger->string0Parameter;
2138 } else {
2139 dst = newTrigger->string1Parameter;
2141 //skipping the context part, which
2142 //is to be readed later
2143 if (mergestrings) {
2144 for (i=0;i<6;i++) {
2145 *dst++='*';
2148 else {
2149 i=0;
2151 while (*src != '"') {
2152 //sizeof(context+name) = 40
2153 if (i<40) {
2154 *dst++ = (char) tolower(*src);
2155 i++;
2157 src++;
2159 *dst = 0;
2160 //reading the context part
2161 if (mergestrings) {
2162 str++;
2163 if (*str!='s') {
2164 printf("Invalid mergestrings:%s\n",str);
2165 abort();
2167 SKIP_ARGUMENT();
2168 if (!stringsCount) {
2169 dst = newTrigger->string0Parameter;
2170 } else {
2171 dst = newTrigger->string1Parameter;
2174 //this works only if there are no spaces
2175 if (*src++!='"' || *src++!=',' || *src++!='"') {
2176 break;
2178 //reading the context string
2179 i=0;
2180 while (*src != '"') {
2181 if (i++<6) {
2182 *dst++ = (char) tolower(*src);
2184 src++;
2187 src++; //skipping "
2188 stringsCount++;
2190 break;
2192 str++;
2193 if (*src == ',' || *src==')')
2194 src++;
2196 return newTrigger;
2199 void SetVariable(Scriptable* Sender, const char* VarName, const char* Context, ieDword value)
2201 char newVarName[8+33];
2203 if (InDebug&ID_VARIABLES) {
2204 printf( "Setting variable(\"%s%s\", %d)\n", Context,
2205 VarName, value );
2207 strncpy( newVarName, Context, 6 );
2208 newVarName[6]=0;
2209 if (strnicmp( newVarName, "MYAREA", 6 ) == 0) {
2210 Sender->GetCurrentArea()->locals->SetAt( VarName, value );
2211 return;
2213 if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
2214 Sender->locals->SetAt( VarName, value );
2215 return;
2217 Game *game = core->GetGame();
2218 if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
2219 game->kaputz->SetAt( VarName, value );
2220 return;
2223 if (strnicmp(newVarName,"GLOBAL",6) ) {
2224 Map *map=game->GetMap(game->FindMap(newVarName));
2225 if (map) {
2226 map->locals->SetAt( VarName, value);
2228 else if (InDebug&ID_VARIABLES) {
2229 printMessage("GameScript"," ",YELLOW);
2230 printf("Invalid variable %s %s in setvariable\n",Context, VarName);
2233 else {
2234 game->locals->SetAt( VarName, ( ieDword ) value );
2238 void SetVariable(Scriptable* Sender, const char* VarName, ieDword value)
2240 char newVarName[8];
2242 if (InDebug&ID_VARIABLES) {
2243 printf( "Setting variable(\"%s\", %d)\n", VarName, value );
2245 strncpy( newVarName, VarName, 6 );
2246 newVarName[6]=0;
2247 if (strnicmp( newVarName, "MYAREA", 6 ) == 0) {
2248 Sender->GetCurrentArea()->locals->SetAt( &VarName[6], value );
2249 return;
2251 if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
2252 Sender->locals->SetAt( &VarName[6], value );
2253 return;
2255 Game *game = core->GetGame();
2256 if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
2257 game->kaputz->SetAt( &VarName[6], value );
2258 return;
2260 if (strnicmp(newVarName,"GLOBAL",6) ) {
2261 Map *map=game->GetMap(game->FindMap(newVarName));
2262 if (map) {
2263 map->locals->SetAt( &VarName[6], value);
2265 else if (InDebug&ID_VARIABLES) {
2266 printMessage("GameScript"," ",YELLOW);
2267 printf("Invalid variable %s in setvariable\n",VarName);
2270 else {
2271 game->locals->SetAt( &VarName[6], ( ieDword ) value );
2275 ieDword CheckVariable(Scriptable* Sender, const char* VarName, bool *valid)
2277 char newVarName[8];
2278 ieDword value = 0;
2280 strncpy( newVarName, VarName, 6 );
2281 newVarName[6]=0;
2282 if (strnicmp( newVarName, "MYAREA", 6 ) == 0) {
2283 Sender->GetCurrentArea()->locals->Lookup( &VarName[6], value );
2284 if (InDebug&ID_VARIABLES) {
2285 printf("CheckVariable %s: %d\n",VarName, value);
2287 return value;
2289 if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
2290 Sender->locals->Lookup( &VarName[6], value );
2291 if (InDebug&ID_VARIABLES) {
2292 printf("CheckVariable %s: %d\n",VarName, value);
2294 return value;
2296 Game *game = core->GetGame();
2297 if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
2298 game->kaputz->Lookup( &VarName[6], value );
2299 if (InDebug&ID_VARIABLES) {
2300 printf("CheckVariable %s: %d\n",VarName, value);
2302 return value;
2304 if (strnicmp(newVarName,"GLOBAL",6) ) {
2305 Map *map=game->GetMap(game->FindMap(newVarName));
2306 if (map) {
2307 map->locals->Lookup( &VarName[6], value);
2308 } else {
2309 if (valid) {
2310 *valid=false;
2312 if (InDebug&ID_VARIABLES) {
2313 printMessage("GameScript"," ",YELLOW);
2314 printf("Invalid variable %s in checkvariable\n",VarName);
2317 } else {
2318 game->locals->Lookup( &VarName[6], value );
2320 if (InDebug&ID_VARIABLES) {
2321 printf("CheckVariable %s: %d\n",VarName, value);
2323 return value;
2326 ieDword CheckVariable(Scriptable* Sender, const char* VarName, const char* Context, bool *valid)
2328 char newVarName[8];
2329 ieDword value = 0;
2331 strncpy(newVarName, Context, 6);
2332 newVarName[6]=0;
2333 if (strnicmp( newVarName, "MYAREA", 6 ) == 0) {
2334 Sender->GetCurrentArea()->locals->Lookup( VarName, value );
2335 if (InDebug&ID_VARIABLES) {
2336 printf("CheckVariable %s%s: %d\n",Context, VarName, value);
2338 return value;
2340 if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
2341 Sender->locals->Lookup( VarName, value );
2342 if (InDebug&ID_VARIABLES) {
2343 printf("CheckVariable %s%s: %d\n",Context, VarName, value);
2345 return value;
2347 Game *game = core->GetGame();
2348 if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) {
2349 game->kaputz->Lookup( VarName, value );
2350 if (InDebug&ID_VARIABLES) {
2351 printf("CheckVariable %s%s: %d\n",Context, VarName, value);
2353 return value;
2355 if (strnicmp(newVarName,"GLOBAL",6) ) {
2356 Map *map=game->GetMap(game->FindMap(newVarName));
2357 if (map) {
2358 map->locals->Lookup( VarName, value);
2359 } else {
2360 if (valid) {
2361 *valid=false;
2363 if (InDebug&ID_VARIABLES) {
2364 printMessage("GameScript"," ",YELLOW);
2365 printf("Invalid variable %s %s in checkvariable\n",Context, VarName);
2368 } else {
2369 game->locals->Lookup( VarName, value );
2371 if (InDebug&ID_VARIABLES) {
2372 printf("CheckVariable %s%s: %d\n",Context, VarName, value);
2374 return value;
2377 int DiffCore(ieDword a, ieDword b, int diffmode)
2379 switch (diffmode) {
2380 case LESS_THAN:
2381 if (a<b) {
2382 return 1;
2384 break;
2385 case EQUALS:
2386 if (a==b) {
2387 return 1;
2389 break;
2390 case GREATER_THAN:
2391 if (a>b) {
2392 return 1;
2394 break;
2395 case GREATER_OR_EQUALS:
2396 if (a>=b) {
2397 return 1;
2399 break;
2400 case NOT_EQUALS:
2401 if (a!=b) {
2402 return 1;
2404 break;
2405 case BINARY_LESS_OR_EQUALS:
2406 if ((a&b) == a) {
2407 return 1;
2409 break;
2410 case BINARY_MORE:
2411 if ((a&b) != a) {
2412 return 1;
2414 break;
2415 case BINARY_MORE_OR_EQUALS:
2416 if ((a&b) == b) {
2417 return 1;
2419 break;
2420 case BINARY_LESS:
2421 if ((a&b) != b) {
2422 return 1;
2424 break;
2425 case BINARY_INTERSECT:
2426 if (a&b) {
2427 return 1;
2429 break;
2430 case BINARY_NOT_INTERSECT:
2431 if (!(a&b)) {
2432 return 1;
2434 break;
2435 default: //less or equals
2436 if (a<=b) {
2437 return 1;
2439 break;
2441 return 0;
2444 int GetGroup(Actor *actor)
2446 int type = 2; //neutral, has no enemies
2447 if (actor->GetStat(IE_EA) <= EA_GOODCUTOFF) {
2448 type = 1; //PC
2450 if (actor->GetStat(IE_EA) >= EA_EVILCUTOFF) {
2451 type = 0;
2453 return type;
2456 Targets *GetMyTarget(Scriptable *Sender, Actor *actor, Targets *parameters, int ga_flags)
2458 if (!actor) {
2459 if (Sender->Type==ST_ACTOR) {
2460 actor = (Actor *) Sender;
2463 parameters->Clear();
2464 if (actor) {
2465 Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastTarget);
2466 if (target) {
2467 parameters->AddTarget(target, 0, ga_flags);
2470 return parameters;
2473 Targets *XthNearestDoor(Targets *parameters, unsigned int count)
2475 //get the origin
2476 Scriptable *origin = parameters->GetTarget(0, -1);
2477 parameters->Clear();
2478 if (!origin) {
2479 return parameters;
2481 //get the doors based on it
2482 Map *map = origin->GetCurrentArea();
2483 unsigned int i =(unsigned int) map->TMap->GetDoorCount();
2484 if (count>i) {
2485 return parameters;
2487 while (i--) {
2488 Door *door = map->TMap->GetDoor(i);
2489 unsigned int dist = Distance(origin->Pos, door->Pos);
2490 parameters->AddTarget(door, dist, 0);
2493 //now get the xth door
2494 origin = parameters->GetTarget(count, ST_DOOR);
2495 parameters->Clear();
2496 if (!origin) {
2497 return parameters;
2499 parameters->AddTarget(origin, 0, 0);
2500 return parameters;
2503 Targets *XthNearestOf(Targets *parameters, int count, int ga_flags)
2505 Scriptable *origin;
2507 if (count<0) {
2508 const targettype *t = parameters->GetLastTarget(ST_ACTOR);
2509 origin = t->actor;
2510 } else {
2511 origin = parameters->GetTarget(count, ST_ACTOR);
2513 parameters->Clear();
2514 if (!origin) {
2515 return parameters;
2517 parameters->AddTarget(origin, 0, ga_flags);
2518 return parameters;
2521 //mygroup means the same specifics as origin
2522 Targets *XthNearestMyGroupOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags)
2524 if (origin->Type != ST_ACTOR) {
2525 parameters->Clear();
2526 return parameters;
2529 targetlist::iterator m;
2530 const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR);
2531 if (!t) {
2532 return parameters;
2534 Actor *actor = (Actor *) origin;
2535 //determining the specifics of origin
2536 ieDword type = actor->GetStat(IE_SPECIFIC); //my group
2538 while ( t ) {
2539 if (t->actor->Type!=ST_ACTOR) {
2540 t=parameters->RemoveTargetAt(m);
2541 continue;
2543 Actor *actor = (Actor *) (t->actor);
2544 if (actor->GetStat(IE_SPECIFIC) != type) {
2545 t=parameters->RemoveTargetAt(m);
2546 continue;
2548 t = parameters->GetNextTarget(m, ST_ACTOR);
2550 return XthNearestOf(parameters,count, ga_flags);
2553 Targets *ClosestEnemySummoned(Scriptable *origin, Targets *parameters, int ga_flags)
2555 if (origin->Type != ST_ACTOR) {
2556 parameters->Clear();
2557 return parameters;
2560 targetlist::iterator m;
2561 const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR);
2562 if (!t) {
2563 return parameters;
2565 Actor *actor = (Actor *) origin;
2566 //determining the allegiance of the origin
2567 int type = GetGroup(actor);
2569 if (type==2) {
2570 parameters->Clear();
2571 return parameters;
2574 actor = NULL;
2575 while ( t ) {
2576 Actor *tmp = (Actor *) (t->actor);
2577 if (tmp->GetStat(IE_SEX) != SEX_SUMMON) {
2578 continue;
2580 if (type) { //origin is PC
2581 if (tmp->GetStat(IE_EA) <= EA_GOODCUTOFF) {
2582 continue;
2584 } else {
2585 if (tmp->GetStat(IE_EA) >= EA_EVILCUTOFF) {
2586 continue;
2589 actor = tmp;
2590 t = parameters->GetNextTarget(m, ST_ACTOR);
2592 parameters->Clear();
2593 parameters->AddTarget(actor, 0, ga_flags);
2594 return parameters;
2597 Targets *XthNearestEnemyOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags)
2599 if (origin->Type != ST_ACTOR) {
2600 parameters->Clear();
2601 return parameters;
2604 targetlist::iterator m;
2605 const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR);
2606 if (!t) {
2607 return parameters;
2609 Actor *actor = (Actor *) origin;
2610 //determining the allegiance of the origin
2611 int type = GetGroup(actor);
2613 if (type==2) {
2614 parameters->Clear();
2615 return parameters;
2618 while ( t ) {
2619 if (t->actor->Type!=ST_ACTOR) {
2620 t=parameters->RemoveTargetAt(m);
2621 continue;
2623 Actor *actor = (Actor *) (t->actor);
2624 if (type) { //origin is PC
2625 if (actor->GetStat(IE_EA) <= EA_GOODCUTOFF) {
2626 t=parameters->RemoveTargetAt(m);
2627 continue;
2629 } else {
2630 if (actor->GetStat(IE_EA) >= EA_EVILCUTOFF) {
2631 t=parameters->RemoveTargetAt(m);
2632 continue;
2635 t = parameters->GetNextTarget(m, ST_ACTOR);
2637 return XthNearestOf(parameters,count, ga_flags);
2640 Targets *XthNearestEnemyOf(Targets *parameters, int count, int ga_flags)
2642 Actor *origin = (Actor *) parameters->GetTarget(0, ST_ACTOR);
2643 parameters->Clear();
2644 if (!origin) {
2645 return parameters;
2647 //determining the allegiance of the origin
2648 int type = GetGroup(origin);
2650 if (type==2) {
2651 return parameters;
2653 Map *map = origin->GetCurrentArea();
2654 int i = map->GetActorCount(true);
2655 Actor *ac;
2656 while (i--) {
2657 ac=map->GetActor(i,true);
2658 int distance = Distance(ac, origin);
2659 if (type) { //origin is PC
2660 if (ac->GetStat(IE_EA) >= EA_EVILCUTOFF) {
2661 parameters->AddTarget(ac, distance, ga_flags);
2664 else {
2665 if (ac->GetStat(IE_EA) <= EA_GOODCUTOFF) {
2666 parameters->AddTarget(ac, distance, ga_flags);
2670 return XthNearestOf(parameters,count, ga_flags);
2673 Point GetEntryPoint(const char *areaname, const char *entryname)
2675 Point p;
2677 AutoTable tab("entries");
2678 if (!tab) {
2679 return p;
2681 const char *tmpstr = tab->QueryField(areaname, entryname);
2682 int x=-1;
2683 int y=-1;
2684 sscanf(tmpstr, "%d.%d", &x, &y);
2685 p.x=(short) x;
2686 p.y=(short) y;
2687 return p;
2690 /* returns a spell's casting distance, it depends on the caster */
2691 unsigned int GetSpellDistance(ieResRef spellres, Actor *actor)
2693 unsigned int dist;
2695 Spell* spl = gamedata->GetSpell( spellres );
2696 if (!spl) {
2697 printMessage("GameScript"," ",LIGHT_RED);
2698 printf("Spell couldn't be found:%.8s.\n", spellres);
2699 return 0;
2701 dist=spl->GetCastingDistance(actor);
2702 gamedata->FreeSpell(spl, spellres, false);
2703 return dist*15;
2706 /* returns a spell's casting distance, it depends on the caster */
2707 unsigned int GetItemDistance(ieResRef itemres, int header)
2709 unsigned int dist;
2711 Item* itm = gamedata->GetItem( itemres );
2712 if (!itm) {
2713 printMessage("GameScript"," ",LIGHT_RED);
2714 printf("Item couldn't be found:%.8s.\n", itemres);
2715 return 0;
2717 dist=itm->GetCastingDistance(header);
2718 gamedata->FreeItem(itm, itemres, false);
2719 return dist*15;
2722 void SetupWishCore(Scriptable *Sender, int column, int picks)
2724 int count;
2725 ieVariable varname;
2726 int *selects;
2727 int i,j;
2729 AutoTable tm("wish");
2730 if (!tm) {
2731 printStatus( "ERROR", LIGHT_RED );
2732 printf( "Cannot find wish.2da.\n");
2733 return;
2736 selects = (int *) malloc(picks*sizeof(int));
2737 count = tm->GetRowCount();
2739 for(i=0;i<99;i++) {
2740 snprintf(varname,32, "wishpower%02d", i);
2741 if(CheckVariable(Sender, varname, "GLOBAL") ) {
2742 SetVariable(Sender, varname, "GLOBAL", 0);
2746 if (count<picks) {
2747 for(i=0;i<count;i++) {
2748 selects[i]=i;
2750 while(i++<picks) {
2751 selects[i]=-1;
2753 } else {
2754 for(i=0;i<picks;i++) {
2755 selects[i]=rand()%count;
2756 retry:
2757 for(j=0;j<i;j++) {
2758 if(selects[i]==selects[j]) {
2759 selects[i]++;
2760 goto retry;
2766 for (i = 0; i < picks; i++) {
2767 if (selects[i]<0)
2768 continue;
2769 int spnum = atoi( tm->QueryField( selects[i], column ) );
2770 snprintf(varname,32,"wishpower%02d", spnum);
2771 SetVariable(Sender, varname, "GLOBAL",1);
2773 free(selects);