GameScript: Move initialization out of constructor.
[gemrb.git] / gemrb / core / GameControl.cpp
blob5872deace6601651570160b25f61a88017eaf6c3
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 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.
20 #ifndef WIN32
21 #include <sys/time.h>
22 #endif
23 #include <cmath>
24 #include "win32def.h"
25 #include "GameControl.h"
26 #include "Interface.h"
27 #include "DialogMgr.h"
28 #include "strrefs.h"
29 #include "Effect.h"
30 #include "GSUtils.h"
31 #include "TileMap.h"
32 #include "Video.h"
33 #include "ScriptEngine.h"
34 #include "Item.h"
35 #include "Game.h"
36 #include "SaveGameIterator.h"
37 #include "damages.h"
38 #include "ImageMgr.h"
39 #include "GameData.h"
41 #define DEBUG_SHOW_INFOPOINTS 0x01
42 #define DEBUG_SHOW_CONTAINERS 0x02
43 #define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
44 #define DEBUG_SHOW_LIGHTMAP 0x08
46 static const Color cyan = {
47 0x00, 0xff, 0xff, 0xff
49 static const Color red = {
50 0xff, 0x00, 0x00, 0xff
52 static const Color magenta = {
53 0xff, 0x00, 0xff, 0xff
55 static const Color green = {
56 0x00, 0xff, 0x00, 0xff
59 static Color white = {
60 0xff, 0xff, 0xff, 0xff
63 static const Color black = {
64 0x00, 0x00, 0x00, 0xff
66 static const Color blue = {
67 0x00, 0x00, 0xff, 0x80
70 //Animation* effect;
72 #define FORMATIONSIZE 10
73 typedef Point formation_type[FORMATIONSIZE];
74 ieDword formationcount;
75 static formation_type *formations=NULL;
76 static bool mqs = false;
77 static ieResRef TestSpell="SPWI207";
79 //If one of the actors has tracking on, the gamecontrol needs to display
80 //arrow markers on the edges to point at detected monsters
81 //tracterID is the tracker actor's global ID
82 //distance is the detection distance
83 void GameControl::SetTracker(Actor *actor, ieDword dist)
85 trackerID = actor->GetID();
86 distance = dist;
89 //Multiple Quick saves is an experimental GemRB feature.
90 //multiple quick saves are kept, their age is determined by the slot
91 //number. There is an algorithm which keeps about log2(n) slots alive.
92 //The algorithm is implemented in SaveGameIterator
93 void GameControl::MultipleQuickSaves(int arg)
95 mqs=arg==1;
98 GameControl::GameControl(void)
100 if (!formations) {
101 ReadFormations();
103 //this is the default action, individual actors should have one too
104 //at this moment we use only this
105 //maybe we don't even need it
106 Changed = true;
107 spellCount = 0;
108 user = NULL;
109 lastActorID = 0;
110 trackerID = 0;
111 distance = 0;
112 MouseIsDown = false;
113 DrawSelectionRect = false;
114 overDoor = NULL;
115 overContainer = NULL;
116 overInfoPoint = NULL;
117 drawPath = NULL;
118 pfs.null();
119 lastCursor = IE_CURSOR_NORMAL;
120 moveX = moveY = 0;
121 scrolling = false;
122 numScrollCursor = 0;
123 DebugFlags = 0;
124 AIUpdateCounter = 1;
125 ieDword tmp=0;
127 target_mode = TARGET_MODE_NONE;
128 target_types = GA_SELECT|GA_NO_DEAD|GA_NO_HIDDEN;
130 core->GetDictionary()->Lookup("Center",tmp);
131 if (tmp) {
132 ScreenFlags=SF_ALWAYSCENTER|SF_CENTERONACTOR;
134 else {
135 ScreenFlags = SF_CENTERONACTOR;
137 LeftCount = 0;
138 BottomCount = 0;
139 RightCount = 0;
140 TopCount = 0;
141 DialogueFlags = 0;
142 dlg = NULL;
143 targetID = 0;
144 originalTargetID = 0;
145 speakerID = 0;
146 targetOB = NULL;
147 DisplayText = NULL;
150 //TODO:
151 //There could be a custom formation which is saved in the save game
152 //alternatively, all formations could be saved in some compatible way
153 //so it doesn't cause problems with the original engine
154 void GameControl::ReadFormations()
156 unsigned int i,j;
157 AutoTable tab("formatio");
158 if (!tab) {
159 // fallback
160 formationcount = 1;
161 formations = (formation_type *) calloc(1,sizeof(formation_type) );
162 return;
164 formationcount = tab->GetRowCount();
165 formations = (formation_type *) calloc(formationcount, sizeof(formation_type));
166 for(i=0; i<formationcount; i++) {
167 for(j=0;j<FORMATIONSIZE;j++) {
168 short k=(short) atoi(tab->QueryField(i,j*2));
169 formations[i][j].x=k;
170 k=(short) atoi(tab->QueryField(i,j*2+1));
171 formations[i][j].y=k;
176 //returns a single point offset for a formation
177 //formation: the formation type
178 //pos: the actor's slot ID
179 Point GameControl::GetFormationOffset(ieDword formation, ieDword pos)
181 if (formation>=formationcount) formation = 0;
182 if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1;
183 return formations[formation][pos];
186 //Moves an actor to a new position, keeping the current formation
187 //WARNING: don't pass p as a reference because it gets modified
188 void GameControl::MoveToPointFormation(Actor *actor, unsigned int pos, Point src, Point p)
190 Map* map = actor->GetCurrentArea() ;
192 int formation=core->GetGame()->GetFormation();
193 if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1;
195 // calculate angle
196 double angle;
197 double xdiff = src.x - p.x;
198 double ydiff = src.y - p.y;
199 if (ydiff == 0) {
200 if (xdiff > 0) {
201 angle = M_PI_2;
202 } else {
203 angle = -M_PI_2;
205 } else {
206 angle = atan(xdiff/ydiff);
207 if (ydiff < 0) angle += M_PI;
210 // calculate new coordinates by rotating formation around (0,0)
211 double newx = -formations[formation][pos].x * cos(angle) + formations[formation][pos].y * sin(angle);
212 double newy = formations[formation][pos].x * sin(angle) + formations[formation][pos].y * cos(angle);
213 p.x += (int)newx;
214 p.y += (int)newy;
216 if (p.x < 0) p.x = 8;
217 if (p.y < 0) p.y = 8;
218 if (p.x > map->GetWidth()*16) p.x = map->GetWidth()*16 - 8;
219 if (p.y > map->GetHeight()*12) p.y = map->GetHeight()*12 - 8;
221 if(map->GetCursor(p) == IE_CURSOR_BLOCKED) {
222 //we can't get there --> adjust position
223 p.x/=16;
224 p.y/=12;
225 map->AdjustPosition(p);
226 p.x*=16;
227 p.y*=12;
229 CreateMovement(actor, p);
232 // generate an action to do the actual movement
233 // only PST supports RunToPoint
234 void GameControl::CreateMovement(Actor *actor, Point &p)
236 char Tmp[256];
238 Action *action = NULL;
239 if (DoubleClick) {
240 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
241 action = GenerateAction( Tmp );
243 if (!action)
245 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
246 action = GenerateAction( Tmp );
249 actor->AddAction( action );
250 // force action so that we get target recticles immediately
251 actor->ProcessActions(true);
254 GameControl::~GameControl(void)
256 //releasing the viewport of GameControl
257 core->GetVideoDriver()->SetViewport( 0,0,0,0 );
258 if (formations) {
259 free( formations );
260 formations = NULL;
262 if (dlg) {
263 delete dlg;
265 if (DisplayText) {
266 core->FreeString(DisplayText);
270 //Autosave was triggered by the GUI
271 void GameControl::AutoSave()
273 const char *folder;
274 AutoTable tab("savegame");
275 if (tab) {
276 folder = tab->QueryField(0);
277 core->GetSaveGameIterator()->CreateSaveGame(0, folder, 0);
281 //QuickSave was triggered by the GUI
282 //mqs is the 'multiple quick saves' flag
283 void GameControl::QuickSave()
285 const char *folder;
286 AutoTable tab("savegame");
287 if (tab) {
288 folder = tab->QueryField(1);
289 core->GetSaveGameIterator()->CreateSaveGame(1, folder, mqs == 1);
293 // ArrowSprite cycles
294 // 321
295 // 4 0
296 // 567
298 #define D_LEFT 1
299 #define D_UP 2
300 #define D_RIGHT 4
301 #define D_BOTTOM 8
302 // Direction Bits
303 // 326
304 // 1 4
305 // 98c
307 static const int arrow_orientations[16]={
308 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
309 -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1
312 //Draws arrow markers along the edge of the game window
313 //WARNING:don't use reference for point, because it is altered
314 void GameControl::DrawArrowMarker(Region &screen, Point p, Region &viewport)
316 Video* video = core->GetVideoDriver();
318 //p.x-=viewport.x;
319 //p.y-=viewport.y;
320 ieDword draw = 0;
321 if (p.x<viewport.x) {
322 p.x=viewport.x;
323 draw|= D_LEFT;
325 if (p.y<viewport.y) {
326 p.y=viewport.y;
327 draw |= D_UP;
329 int tmp;
331 Sprite2D *spr = core->GetScrollCursorSprite(0,0);
333 tmp = spr->Width;
334 //tmp = core->ArrowSprites[0]->Width;
336 if (p.x>viewport.x+viewport.w-tmp) {
337 p.x=viewport.x+viewport.w;//-tmp;
338 draw |= D_RIGHT;
341 tmp = spr->Height;
342 //tmp = core->ArrowSprites[0]->Height;
344 if (p.y>viewport.y+viewport.h-tmp) {
345 p.y=viewport.y+viewport.h;//-tmp;
346 draw |= D_BOTTOM;
348 if (arrow_orientations[draw]>=0) {
349 video->BlitGameSprite( core->GetScrollCursorSprite(arrow_orientations[draw], 0), p.x+screen.x, p.y+screen.y, 0, black, NULL);
353 /** Draws the Control on the Output Display */
354 void GameControl::Draw(unsigned short x, unsigned short y)
356 bool update_scripts = !(DialogueFlags & DF_FREEZE_SCRIPTS);
358 Game* game = core->GetGame();
359 if (!game)
360 return;
362 if (((short) Width) <=0 || ((short) Height) <= 0) {
363 return;
366 if (Owner->Visible!=WINDOW_VISIBLE) {
367 return;
370 Video* video = core->GetVideoDriver();
371 Region viewport = video->GetViewport();
372 if (moveX || moveY) {
373 viewport.x += moveX;
374 viewport.y += moveY;
375 Point mapsize = core->GetGame()->GetCurrentArea()->TMap->GetMapSize();
376 if ( viewport.x < 0 )//if we are at top of the map
377 viewport.x = 0;
378 else if ( (viewport.x + viewport.w) >= mapsize.x) //if we are at the bottom
379 viewport.x = mapsize.x - viewport.w;
381 if ( viewport.y < 0 ) //if we are at the left of the map
382 viewport.y = 0;
383 else if ( (viewport.y + viewport.h ) >= mapsize.y ) //if we are at the right
384 viewport.y = mapsize.y - viewport.h;
386 // override any existing viewport moves which may be in progress
387 core->timer->SetMoveViewPort( viewport.x, viewport.y, 0, false );
388 // move it directly ourselves, since we might be paused
389 video->MoveViewportTo( viewport.x, viewport.y );
391 Region screen( x + XPos, y + YPos, Width, Height );
392 Map* area = game->GetCurrentArea( );
393 if (!area) {
394 video->DrawRect( screen, blue, true );
395 return;
397 video->DrawRect( screen, black, true );
399 // setup outlines
400 InfoPoint *i;
401 unsigned int idx;
402 for (idx = 0; (i = area->TMap->GetInfoPoint( idx )); idx++) {
403 i->Highlight = false;
404 if (overInfoPoint == i && target_mode) {
405 if (i->VisibleTrap(0)) {
406 i->outlineColor = green;
407 i->Highlight = true;
408 continue;
411 if (i->VisibleTrap(DebugFlags & DEBUG_SHOW_INFOPOINTS)) {
412 i->outlineColor = red; // traps
413 } else if (DebugFlags & DEBUG_SHOW_INFOPOINTS) {
414 i->outlineColor = blue; // debug infopoints
415 } else {
416 continue;
418 i->Highlight = true;
421 Door *d;
422 for (idx = 0; (d = area->TMap->GetDoor( idx )); idx++) {
423 d->Highlight = false;
424 if (overDoor == d) {
425 if (target_mode) {
426 if (d->VisibleTrap(0) || (d->Flags & DOOR_LOCKED)) {
427 // only highlight targettable doors
428 d->outlineColor = green;
429 d->Highlight = true;
430 continue;
432 } else if (!(d->Flags & DOOR_SECRET)) {
433 // mouse over, not in target mode, no secret door
434 d->outlineColor = cyan;
435 d->Highlight = true;
436 continue;
439 if (d->VisibleTrap(0)) {
440 d->outlineColor = red; // traps
441 } else if (d->Flags & DOOR_SECRET) {
442 if (DebugFlags & DEBUG_SHOW_DOORS || d->Flags & DOOR_FOUND) {
443 d->outlineColor = magenta; // found hidden door
444 } else {
445 // secret door is invisible
446 continue;
448 } else if (DebugFlags & DEBUG_SHOW_DOORS) {
449 d->outlineColor = cyan; // debug doors
450 } else {
451 continue;
453 d->Highlight = true;
456 Container *c;
457 for (idx = 0; (c = area->TMap->GetContainer( idx )); idx++) {
458 c->Highlight = false;
459 if (overContainer == c && target_mode) {
460 if (c->VisibleTrap(0) || (c->Flags & CONT_LOCKED)) {
461 // only highlight targettable containers
462 c->outlineColor = green;
463 c->Highlight = true;
464 continue;
466 } else if (overContainer == c) {
467 // mouse over, not in target mode
468 c->outlineColor = cyan;
469 c->Highlight = true;
470 continue;
472 if (c->VisibleTrap(0)) {
473 c->outlineColor = red; // traps
474 } else if (DebugFlags & DEBUG_SHOW_CONTAINERS) {
475 c->outlineColor = cyan; // debug containers
476 } else {
477 continue;
479 c->Highlight = true;
482 //drawmap should be here so it updates fog of war
483 area->DrawMap( screen );
484 game->DrawWeather(screen, update_scripts);
486 if (trackerID) {
487 Actor *actor = area->GetActorByGlobalID(trackerID);
489 if (actor) {
490 Actor **monsters = area->GetAllActorsInRadius(actor->Pos, GA_NO_DEAD, distance);
492 int i = 0;
493 while(monsters[i]) {
494 Actor *target = monsters[i++];
495 if (target->InParty) continue;
496 if (target->GetStat(IE_NOTRACKING)) continue;
497 DrawArrowMarker(screen, target->Pos, viewport);
499 delete monsters;
500 } else {
501 trackerID = 0;
505 if (ScreenFlags & SF_DISABLEMOUSE)
506 return;
507 Point p(lastMouseX, lastMouseY);
508 video->ConvertToGame( p.x, p.y );
510 // Draw selection rect
511 if (DrawSelectionRect) {
512 CalculateSelection( p );
513 video->DrawRect( SelectionRect, green, false, true );
516 // Show wallpolygons
517 if (DebugFlags & DEBUG_SHOW_INFOPOINTS) {
519 unsigned int count = area->GetWallCount();
520 for (unsigned int i = 0; i < count; ++i) {
521 Wall_Polygon* poly = area->GetWallGroup(i);
522 if (!poly) continue;
523 // yellow
524 Color c;
525 c.r = 0x7F;
526 c.g = 0x7F;
527 c.b = 0;
528 c.a = 0;
529 //if polygon is disabled, make it grey
530 if (poly->wall_flag&WF_DISABLED) {
531 c.b = 0x7F;
534 video->DrawPolyline( poly, c, true );
538 // Draw path
539 if (drawPath) {
540 PathNode* node = drawPath;
541 while (true) {
542 Point p( ( node-> x*16) + 8, ( node->y*12 ) + 6 );
543 if (!node->Parent) {
544 video->DrawCircle( p.x, p.y, 2, red );
545 } else {
546 short oldX = ( node->Parent-> x*16) + 8, oldY = ( node->Parent->y*12 ) + 6;
547 video->DrawLine( oldX, oldY, p.x, p.y, green );
549 if (!node->Next) {
550 video->DrawCircle( p.x, p.y, 2, green );
551 break;
553 node = node->Next;
557 // Draw lightmap
558 if (DebugFlags & DEBUG_SHOW_LIGHTMAP) {
559 Sprite2D* spr = area->LightMap->GetSprite2D();
560 video->BlitSprite( spr, 0, 0, true );
561 video->FreeSprite( spr );
562 Region point( p.x / 16, p.y / 12, 2, 2 );
563 video->DrawRect( point, red );
566 if (core->HasFeature(GF_ONSCREEN_TEXT) && DisplayText) {
567 core->GetFont(1)->Print(screen, (unsigned char *)DisplayText, core->InfoTextPalette, IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true);
568 if (update_scripts) {
569 // just replicating original engine behaviour
570 if (DisplayTextTime == 0) {
571 SetDisplayText((char *)NULL, 0);
572 } else {
573 DisplayTextTime--;
579 /** inherited from Control, GameControl doesn't need it */
580 int GameControl::SetText(const char* /*string*/, int /*pos*/)
582 return 0;
585 /** Key Press Event */
586 void GameControl::OnKeyPress(unsigned char Key, unsigned short /*Mod*/)
588 if (DialogueFlags&DF_IN_DIALOG) {
589 return;
591 unsigned int i;
592 Game* game = core->GetGame();
593 if (!game) return;
595 switch (Key) {
596 case '0':
597 game->SelectActor( NULL, false, SELECT_NORMAL );
598 i = game->GetPartySize(false)/2;
599 while(i--) {
600 SelectActor(i, true);
602 break;
603 case '-':
604 game->SelectActor( NULL, true, SELECT_NORMAL );
605 i = game->GetPartySize(false)/2;
606 while(i--) {
607 SelectActor(i, false);
609 break;
610 case '=':
611 SelectActor(-1);
612 break;
613 case '1':
614 case '2':
615 case '3':
616 case '4':
617 case '5':
618 case '6':
619 case '7':
620 case '8':
621 case '9':
622 SelectActor(Key-'0');
623 break;
624 default:
625 core->GetGame()->SetHotKey(toupper(Key));
626 break;
630 //Select (or deselect) a new actor (or actors)
631 void GameControl::SelectActor(int whom, int type)
633 Game* game = core->GetGame();
634 if (whom==-1) {
635 game->SelectActor( NULL, true, SELECT_NORMAL );
636 return;
639 /* doesn't fall through here */
640 Actor* actor = game->FindPC( whom );
641 if (!actor)
642 return;
644 if (type==0) {
645 game->SelectActor( actor, false, SELECT_NORMAL );
646 return;
648 if (type==1) {
649 game->SelectActor( actor, true, SELECT_NORMAL );
650 return;
653 bool was_selected = actor->IsSelected();
654 if (game->SelectActor( actor, true, SELECT_REPLACE ))
655 if (was_selected || (ScreenFlags & SF_ALWAYSCENTER)) {
656 ScreenFlags |= SF_CENTERONACTOR;
660 //Effect for the ctrl-r cheatkey (resurrect)
661 static EffectRef heal_ref={"CurrentHPModifier", NULL, -1};
662 static EffectRef damage_ref={"Damage", NULL, -1};
664 /** Key Release Event */
665 void GameControl::OnKeyRelease(unsigned char Key, unsigned short Mod)
667 unsigned int i;
668 Game* game = core->GetGame();
670 if (!game)
671 return;
673 if (DialogueFlags&DF_IN_DIALOG) {
674 if (Mod) return;
675 switch(Key) {
676 case '1':
677 case '2':
678 case '3':
679 case '4':
680 case '5':
681 case '6':
682 case '7':
683 case '8':
684 case '9':
686 TextArea *ta = core->GetMessageTextArea();
687 if (ta) {
688 ta->OnKeyPress(Key,Mod);
691 break;
693 return;
695 //cheatkeys with ctrl-
696 if (Mod & GEM_MOD_CTRL) {
697 if (!core->CheatEnabled()) {
698 return;
700 Map* area = game->GetCurrentArea( );
701 if (!area)
702 return;
703 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
704 Point p(lastMouseX, lastMouseY);
705 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
706 switch (Key) {
707 case 'f': //toggle full screen mode
708 core->GetVideoDriver()->ToggleFullscreenMode();
709 break;
710 case 'd': //disarm a trap
711 if (overInfoPoint) {
712 overInfoPoint->DetectTrap(256);
714 if (overContainer) {
715 if (overContainer->Trapped &&
716 !( overContainer->TrapDetected )) {
717 overContainer->TrapDetected = 1;
720 if (overDoor) {
721 if (overDoor->Trapped &&
722 !( overDoor->TrapDetected )) {
723 overDoor->TrapDetected = 1;
726 break;
727 case 'l': //play an animation (vvc/bam) over an actor
728 //the original engine was able to swap through all animations
729 if (lastActor) {
730 lastActor->AddAnimation("S056ICBL", 0, 0, 0);
732 break;
734 case 'c': //force cast a hardcoded spell
735 //caster is the last selected actor
736 //target is the door/actor currently under the pointer
737 if (game->selected.size() > 0) {
738 Actor *src = game->selected[0];
739 Scriptable *target = lastActor;
740 if (overDoor) {
741 target = overDoor;
743 if (target) {
744 src->CastSpell( TestSpell, target, false );
745 if (src->LastTarget) {
746 src->CastSpellEnd( TestSpell );
747 } else {
748 src->CastSpellPointEnd( TestSpell );
752 break;
754 case 'b': //draw a path to the target (pathfinder debug)
755 //You need to select an origin with ctrl-o first
756 if (drawPath) {
757 PathNode* nextNode = drawPath->Next;
758 PathNode* thisNode = drawPath;
759 while (true) {
760 delete( thisNode );
761 thisNode = nextNode;
762 if (!thisNode)
763 break;
764 nextNode = thisNode->Next;
767 drawPath = core->GetGame()->GetCurrentArea()->FindPath( pfs, p, lastActor?lastActor->size:1 );
769 break;
771 case 'o': //set up the origin for the pathfinder
772 // origin
773 pfs.x = lastMouseX;
774 pfs.y = lastMouseY;
775 core->GetVideoDriver()->ConvertToGame( pfs.x, pfs.y );
776 break;
777 case 'a': //switches through the avatar animations
778 if (lastActor) {
779 lastActor->GetNextAnimation();
781 break;
782 case 's': //switches through the stance animations
783 if (lastActor) {
784 lastActor->GetNextStance();
786 break;
787 case 'j': //teleports the selected actors
788 for (i = 0; i < game->selected.size(); i++) {
789 Actor* actor = game->selected[i];
790 MoveBetweenAreasCore(actor, core->GetGame()->CurrentArea, p, -1, true);
791 printf( "Teleported to %d, %d\n", p.x, p.y );
793 break;
795 case 'm': //prints a debug dump (ctrl-m in the original game too)
796 if (!lastActor) {
797 lastActor = area->GetActor( p, GA_DEFAULT);
799 if (!lastActor) {
800 // ValidTarget never returns immobile targets, making debugging a nightmare
801 // so if we don't have an actor, we make really really sure by checking manually
802 unsigned int count = area->GetActorCount(true);
803 while (count--) {
804 Actor *actor = area->GetActor(count, true);
805 if (actor->IsOver(p)) {
806 actor->DebugDump();
810 if (lastActor) {
811 lastActor->DebugDump();
812 break;
814 if (overDoor) {
815 overDoor->DebugDump();
816 break;
818 if (overContainer) {
819 overContainer->DebugDump();
820 break;
822 if (overInfoPoint) {
823 overInfoPoint->DebugDump();
824 break;
826 core->GetGame()->GetCurrentArea()->DebugDump(Mod & GEM_MOD_SHIFT);
827 break;
828 case 'v': //marks some of the map visited (random vision distance)
829 area->ExploreMapChunk( p, rand()%30, 1 );
830 break;
831 case 'x': // shows coordinates on the map
832 printf( "%s [%d.%d]\n", area->GetScriptName(), p.x, p.y );
833 break;
834 case 'g'://shows loaded areas and other game information
835 game->DebugDump();
836 break;
837 case 'i'://interact trigger (from the original game)
838 if (!lastActor) {
839 lastActor = area->GetActor( p, GA_DEFAULT);
841 if (lastActor && !(lastActor->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE)) {
842 Actor *target;
843 int i = game->GetPartySize(true);
844 if(i<2) break;
845 i=rand()%i;
848 target = game->GetPC(i, true);
849 if(target==lastActor) continue;
850 if(target->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE) continue;
852 char Tmp[40];
853 snprintf(Tmp,sizeof(Tmp),"Interact(\"%s\")",target->GetScriptName() );
854 lastActor->AddAction(GenerateAction(Tmp));
855 break;
857 while(i--);
859 break;
860 case 'r'://resurrects actor
861 if (!lastActor) {
862 lastActor = area->GetActor( p, GA_DEFAULT);
864 if (lastActor) {
865 Effect *fx = EffectQueue::CreateEffect(heal_ref, lastActor->GetBase(IE_MAXHITPOINTS), 0x30001, FX_DURATION_INSTANT_PERMANENT);
866 if (fx) {
867 core->ApplyEffect(fx, lastActor, lastActor);
870 break;
871 case 't'://advances time
872 // 7200 (one day) /24 (hours) == 300
873 game->AdvanceTime(300*AI_UPDATE_TIME);
874 //refresh gui here once we got it
875 break;
877 case 'q': //joins actor to the party
878 if (lastActor && !lastActor->InParty) {
879 lastActor->ClearActions();
880 lastActor->ClearPath();
881 char Tmp[40];
882 strncpy(Tmp,"JoinParty()",sizeof(Tmp) );
883 lastActor->AddAction( GenerateAction(Tmp) );
885 break;
886 case 'p': //center on actor
887 ScreenFlags|=SF_CENTERONACTOR;
888 ScreenFlags^=SF_ALWAYSCENTER;
889 break;
890 case 'k': //kicks out actor
891 if (lastActor && lastActor->InParty) {
892 lastActor->ClearActions();
893 lastActor->ClearPath();
894 char Tmp[40];
895 strncpy(Tmp,"LeaveParty()",sizeof(Tmp) );
896 lastActor->AddAction( GenerateAction(Tmp) );
898 break;
899 case 'y': //kills actor or all enemies
900 if (Mod & GEM_MOD_SHIFT) {
901 // mwahaha!
902 Effect *newfx;
903 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT);
904 Actor *victim;
905 for (int i = area->GetActorCount(0)-1; i >= 0; i--) {
906 victim = area->GetActor(i, 0);
907 if (victim->Modified[IE_EA] == EA_ENEMY) {
908 core->ApplyEffect(newfx, victim, victim);
911 delete newfx;
912 } else {
913 if (lastActor) {
914 //using action so the actor is killed
915 //correctly (synchronisation)
916 lastActor->ClearActions();
917 lastActor->ClearPath();
919 Effect *newfx;
920 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT);
921 core->ApplyEffect(newfx, lastActor, lastActor);
922 if (! (lastActor->GetInternalFlag() & IF_REALLYDIED)) {
923 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_ACID<<16, FX_DURATION_INSTANT_PERMANENT);
924 core->ApplyEffect(newfx, lastActor, lastActor);
925 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_CRUSHING<<16, FX_DURATION_INSTANT_PERMANENT);
926 core->ApplyEffect(newfx, lastActor, lastActor);
928 delete newfx;
929 } else if (overContainer) {
930 overContainer->SetContainerLocked(0);
931 } else if (overDoor) {
932 overDoor->SetDoorLocked(0,0);
935 break;
936 case 'z': //shift through the avatar animations backward
937 if (lastActor) {
938 lastActor->GetPrevAnimation();
940 break;
941 case '1': //change paperdoll armour level
942 if (! lastActor)
943 break;
944 lastActor->NewStat(IE_ARMOR_TYPE,1,MOD_ADDITIVE);
945 break;
946 case '4': //show all traps and infopoints
947 DebugFlags ^= DEBUG_SHOW_INFOPOINTS;
948 printf("Show traps and infopoints %s\n", DebugFlags & DEBUG_SHOW_INFOPOINTS ? "ON" : "OFF");
949 break;
950 case '6': //show the lightmap
951 DebugFlags ^= DEBUG_SHOW_LIGHTMAP;
952 printf("Show lightmap %s\n", DebugFlags & DEBUG_SHOW_LIGHTMAP ? "ON" : "OFF");
953 break;
954 case '7': //toggles fog of war
955 core->FogOfWar ^= 1;
956 printf("Show Fog-Of-War: %s\n", core->FogOfWar & 1 ? "ON" : "OFF");
957 break;
958 case '8': //show searchmap over area
959 core->FogOfWar ^= 2;
960 printf("Show searchmap %s\n", core->FogOfWar & 2 ? "ON" : "OFF");
961 break;
962 default:
963 printf( "KeyRelease:%d - %d\n", Key, Mod );
964 break;
966 return; //return from cheatkeys
968 switch (Key) {
969 case 'h': //hard pause
970 if (DialogueFlags & DF_FREEZE_SCRIPTS) break;
971 //fallthrough
972 case ' ': //soft pause
973 DialogueFlags ^= DF_FREEZE_SCRIPTS;
974 if (DialogueFlags&DF_FREEZE_SCRIPTS) {
975 core->DisplayConstantString(STR_PAUSED,0xff0000);
976 SetDisplayText(STR_PAUSED, 0); // time 0 = removed instantly on unpause
977 } else {
978 core->DisplayConstantString(STR_UNPAUSED,0xff0000);
980 break;
981 case 'm':
982 core->GetGUIScriptEngine()->RunFunction("OpenMapWindow");
983 break;
984 case 'j':
985 core->GetGUIScriptEngine()->RunFunction("OpenJournalWindow");
986 break;
987 case 'i':
988 core->GetGUIScriptEngine()->RunFunction("OpenInventoryWindow");
989 break;
990 case 'r':
991 core->GetGUIScriptEngine()->RunFunction("OpenRecordsWindow");
992 break;
993 case 'q': //quicksave
994 QuickSave();
995 break;
996 case GEM_ALT: //alt key (shows containers)
997 DebugFlags &= ~DEBUG_SHOW_CONTAINERS;
998 break;
999 default:
1000 break;
1004 void GameControl::DisplayTooltip() {
1005 Game* game = core->GetGame();
1006 if (game) {
1007 Map* area = game->GetCurrentArea( );
1008 if (area) {
1009 Actor *actor = area->GetActorByGlobalID(lastActorID);
1010 if (actor && (actor->GetStat(IE_STATE_ID)&STATE_DEAD || actor->GetInternalFlag()&IF_JUSTDIED)) {
1011 // checking IF_JUSTDIED is kind of horrid, but seems necessary
1012 // no tooltips for dead actors!
1013 actor->SetOver( false );
1014 lastActorID = 0;
1015 actor = NULL;
1018 if (actor) {
1019 char *name = actor->GetName(-1);
1020 int hp = actor->GetStat(IE_HITPOINTS);
1021 int maxhp = actor->GetStat(IE_MAXHITPOINTS);
1023 char buffer[100];
1024 if (!core->TooltipBack) {
1025 // single-line tooltips without background (PS:T)
1026 if (actor->InParty) {
1027 snprintf(buffer, 100, "%s: %d/%d", name, hp, maxhp);
1028 } else {
1029 snprintf(buffer, 100, "%s", name);
1031 } else {
1032 // a guess at a neutral check
1033 bool neutral = actor->GetStat(IE_EA) == EA_NEUTRAL;
1034 // test for an injured string being present for this game
1035 int strindex = core->GetStringReference(STR_UNINJURED);
1036 // normal tooltips
1037 if (actor->InParty) {
1038 // in party: display hp
1039 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1040 } else if (neutral) {
1041 // neutral: display name only
1042 snprintf(buffer, 100, "%s", name);
1043 } else if (strindex == -1) {
1044 // non-neutral, not in party, no injured strings: display hp
1045 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1046 } else {
1047 // non-neutral, not in party: display injured string
1048 int strindex;
1049 char *injuredstring = NULL;
1050 // these boundaries are just a guess
1051 if (hp == maxhp) {
1052 strindex = STR_UNINJURED;
1053 } else if (hp > (maxhp*3)/4) {
1054 strindex = STR_INJURED1;
1055 } else if (hp > maxhp/2) {
1056 strindex = STR_INJURED2;
1057 } else if (hp > maxhp/3) {
1058 strindex = STR_INJURED3;
1059 } else {
1060 strindex = STR_INJURED4;
1062 strindex = core->GetStringReference(strindex);
1063 if (strindex != -1) {
1064 injuredstring = core->GetString(strindex, 0);
1067 if (!injuredstring) {
1068 // eek, where did the string go?
1069 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1070 } else {
1071 snprintf(buffer, 100, "%s\n%s", name, injuredstring);
1072 free(injuredstring);
1077 Point p = actor->Pos;
1078 core->GetVideoDriver()->ConvertToScreen( p.x, p.y );
1079 p.x += Owner->XPos + XPos;
1080 p.y += Owner->YPos + YPos;
1082 // hack to position text above PS:T actors
1083 if (!core->TooltipBack) p.y -= actor->size*50;
1085 // we should probably cope better with moving actors
1086 SetTooltip(buffer);
1087 core->DisplayTooltip(p.x, p.y, this);
1088 return;
1093 SetTooltip(NULL);
1094 core->DisplayTooltip(0, 0, NULL);
1095 return;
1098 //returns the appropriate cursor over an active region (trap, infopoint, travel region)
1099 int GameControl::GetCursorOverInfoPoint(InfoPoint *overInfoPoint)
1101 if (target_mode == TARGET_MODE_PICK) {
1102 if (overInfoPoint->VisibleTrap(0)) {
1103 return IE_CURSOR_TRAP;
1106 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1108 // traps always display a walk cursor?
1109 if (overInfoPoint->Type == ST_PROXIMITY) {
1110 return IE_CURSOR_WALK;
1112 return overInfoPoint->Cursor;
1115 //returns the appropriate cursor over a door
1116 int GameControl::GetCursorOverDoor(Door *overDoor)
1118 if (target_mode == TARGET_MODE_PICK) {
1119 if (overDoor->VisibleTrap(0)) {
1120 return IE_CURSOR_TRAP;
1122 if (overDoor->Flags & DOOR_LOCKED) {
1123 return IE_CURSOR_LOCK;
1126 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1128 return overDoor->Cursor;
1131 //returns the appropriate cursor over a container (or pile)
1132 int GameControl::GetCursorOverContainer(Container *overContainer)
1134 if (target_mode == TARGET_MODE_PICK) {
1135 if (overContainer->VisibleTrap(0)) {
1136 return IE_CURSOR_TRAP;
1138 if (overContainer->Flags & CONT_LOCKED) {
1139 return IE_CURSOR_LOCK2;
1142 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1144 return IE_CURSOR_TAKE;
1147 /** Mouse Over Event */
1148 void GameControl::OnMouseOver(unsigned short x, unsigned short y)
1150 if (ScreenFlags & SF_DISABLEMOUSE) {
1151 return;
1154 lastMouseX = x;
1155 lastMouseY = y;
1156 Point p( x,y );
1157 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
1158 if (MouseIsDown && ( !DrawSelectionRect )) {
1159 if (( abs( p.x - StartX ) > 5 ) || ( abs( p.y - StartY ) > 5 )) {
1160 DrawSelectionRect = true;
1163 Game* game = core->GetGame();
1164 if (!game) return;
1165 Map* area = game->GetCurrentArea( );
1166 if (!area) return;
1167 int nextCursor = area->GetCursor( p );
1168 //make the invisible area really invisible
1169 if (nextCursor == IE_CURSOR_INVALID) {
1170 Owner->Cursor = IE_CURSOR_BLOCKED;
1171 lastCursor = IE_CURSOR_BLOCKED;
1172 return;
1175 overInfoPoint = area->TMap->GetInfoPoint( p, true );
1176 if (overInfoPoint) {
1177 //nextCursor = overInfoPoint->Cursor;
1178 nextCursor = GetCursorOverInfoPoint(overInfoPoint);
1181 if (overDoor) {
1182 overDoor->Highlight = false;
1184 if (overContainer) {
1185 overContainer->Highlight = false;
1187 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
1188 if (lastActor) {
1189 lastActor->SetOver( false );
1192 overDoor = area->TMap->GetDoor( p );
1193 overContainer = area->TMap->GetContainer( p );
1195 if (!DrawSelectionRect) {
1196 if (overDoor) {
1197 nextCursor = GetCursorOverDoor(overDoor);
1200 if (overContainer) {
1201 nextCursor = GetCursorOverContainer(overContainer);
1204 Actor *prevActor = lastActor;
1205 lastActor = area->GetActor( p, target_types);
1206 if (lastActor != prevActor) {
1207 // we store prevActor so we can remove the tooltip on actor change
1208 // (maybe we should be checking this and actor movements every frame?)
1209 SetTooltip(NULL);
1210 core->DisplayTooltip(0, 0, this);
1213 if ((target_types & GA_NO_SELF) && lastActor ) {
1214 if (lastActor == core->GetFirstSelectedPC(false)) {
1215 lastActor=NULL;
1219 if (lastActor) {
1220 lastActorID = lastActor->globalID;
1221 lastActor->SetOver( true );
1222 ieDword type = lastActor->GetStat(IE_EA);
1223 if (type >= EA_EVILCUTOFF || type == EA_GOODBUTRED) {
1224 nextCursor = IE_CURSOR_ATTACK;
1225 } else if ( type > EA_CHARMED ) {
1226 nextCursor = IE_CURSOR_TALK;
1227 } else {
1228 nextCursor = IE_CURSOR_NORMAL;
1230 } else {
1231 lastActorID = 0;
1234 if (target_mode == TARGET_MODE_TALK) {
1235 nextCursor = IE_CURSOR_TALK;
1236 if (!lastActor) {
1237 nextCursor |= IE_CURSOR_GRAY;
1239 } else if (target_mode == TARGET_MODE_ATTACK) {
1240 nextCursor = IE_CURSOR_ATTACK;
1241 if (!lastActor && !overDoor && !overContainer) {
1242 nextCursor |= IE_CURSOR_GRAY;
1244 } else if (target_mode == TARGET_MODE_CAST) {
1245 nextCursor = IE_CURSOR_CAST;
1246 //point is always valid
1247 if (!(target_types & GA_POINT)) {
1248 if(!lastActor) {
1249 nextCursor |= IE_CURSOR_GRAY;
1252 } else if (target_mode == TARGET_MODE_DEFEND) {
1253 nextCursor = IE_CURSOR_DEFEND;
1254 if(!lastActor) {
1255 nextCursor |= IE_CURSOR_GRAY;
1257 } else if (target_mode == TARGET_MODE_PICK) {
1258 if (lastActor) {
1259 nextCursor = IE_CURSOR_PICK;
1260 } else {
1261 if (!overContainer && !overDoor && !overInfoPoint) {
1262 nextCursor = IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1265 goto end_function;
1268 if (lastActor) {
1269 switch (lastActor->GetStat(IE_EA)) {
1270 case EA_EVILCUTOFF:
1271 case EA_GOODCUTOFF:
1272 break;
1274 case EA_PC:
1275 case EA_FAMILIAR:
1276 case EA_ALLY:
1277 case EA_CONTROLLED:
1278 case EA_CHARMED:
1279 case EA_EVILBUTGREEN:
1280 if (target_types & GA_NO_ENEMY)
1281 nextCursor^=1;
1282 break;
1284 case EA_ENEMY:
1285 case EA_GOODBUTRED:
1286 if (target_types & GA_NO_ALLY)
1287 nextCursor^=1;
1288 break;
1289 default:
1290 if (!(target_types & GA_NO_NEUTRAL))
1291 nextCursor^=1;
1292 break;
1296 end_function:
1297 if (lastCursor != nextCursor) {
1298 Owner->Cursor = nextCursor;
1299 lastCursor = (unsigned char) nextCursor;
1303 #define SCROLL_BORDER 5
1305 /** Global Mouse Move Event */
1306 void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y)
1308 if (ScreenFlags & SF_DISABLEMOUSE) {
1309 return;
1312 if (Owner->Visible!=WINDOW_VISIBLE) {
1313 return;
1316 int mousescrollspd = core->GetMouseScrollSpeed();
1318 if (x <= SCROLL_BORDER)
1319 moveX = -mousescrollspd;
1320 else {
1321 if (x >= ( core->Width - SCROLL_BORDER ))
1322 moveX = mousescrollspd;
1323 else
1324 moveX = 0;
1326 if (y <= SCROLL_BORDER)
1327 moveY = -mousescrollspd;
1328 else {
1329 if (y >= ( core->Height - SCROLL_BORDER ))
1330 moveY = mousescrollspd;
1331 else
1332 moveY = 0;
1335 if (moveX != 0 || moveY != 0) {
1336 scrolling = true;
1337 } else if (scrolling) {
1338 scrolling = false;
1340 Video* video = core->GetVideoDriver();
1341 video->SetDragCursor(NULL);
1345 void GameControl::UpdateScrolling() {
1346 if (!scrolling) return;
1348 int mousescrollspd = core->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-?
1349 Video* video = core->GetVideoDriver();
1351 if (moveX == mousescrollspd && moveY == 0) { // right
1352 video->SetDragCursor(core->GetScrollCursorSprite(0,numScrollCursor));
1353 } else if (moveX == mousescrollspd && moveY == -mousescrollspd) { // upper right
1354 video->SetDragCursor(core->GetScrollCursorSprite(1,numScrollCursor));
1355 } else if (moveX == 0 && moveY == -mousescrollspd) { // up
1356 video->SetDragCursor(core->GetScrollCursorSprite(2,numScrollCursor));
1357 } else if (moveX == -mousescrollspd && moveY == -mousescrollspd) { // upper left
1358 video->SetDragCursor(core->GetScrollCursorSprite(3,numScrollCursor));
1359 } else if (moveX == -mousescrollspd && moveY == 0) { // left
1360 video->SetDragCursor(core->GetScrollCursorSprite(4,numScrollCursor));
1361 } else if (moveX == -mousescrollspd && moveY == mousescrollspd) { // bottom left
1362 video->SetDragCursor(core->GetScrollCursorSprite(5,numScrollCursor));
1363 } else if (moveX == 0 && moveY == mousescrollspd) { // bottom
1364 video->SetDragCursor(core->GetScrollCursorSprite(6,numScrollCursor));
1365 } else if (moveX == mousescrollspd && moveY == mousescrollspd) { // bottom right
1366 video->SetDragCursor(core->GetScrollCursorSprite(7,numScrollCursor));
1369 numScrollCursor = (numScrollCursor+1) % 15;
1372 //generate action code for source actor to try to attack a target
1373 void GameControl::TryToAttack(Actor *source, Actor *tgt)
1375 char Tmp[40];
1377 source->ClearPath();
1378 source->ClearActions();
1379 strncpy(Tmp,"NIDSpecial3()",sizeof(Tmp) );
1380 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1383 //generate action code for source actor to try to defend a target
1384 void GameControl::TryToDefend(Actor *source, Actor *tgt)
1386 char Tmp[40];
1388 source->ClearPath();
1389 source->ClearActions();
1390 strncpy(Tmp,"NIDSpecial4()",sizeof(Tmp) );
1391 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1394 //generate action code for source actor to try to pick pockets of a target
1395 //The -1 flag is a placeholder for dynamic target IDs
1396 void GameControl::TryToPick(Actor *source, Actor *tgt)
1398 char Tmp[40];
1400 source->ClearPath();
1401 source->ClearActions();
1402 strncpy(Tmp,"PickPockets([-1])", sizeof(Tmp) );
1403 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1406 //generate action code for source actor to try to pick a lock/disable trap on a door
1407 void GameControl::TryToPick(Actor *source, Door *tgt)
1409 char Tmp[40];
1411 source->ClearPath();
1412 source->ClearActions();
1413 if (tgt->Trapped && tgt->TrapDetected) {
1414 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1415 } else {
1416 snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() );
1418 source->AddAction( GenerateAction( Tmp ) );
1421 //generate action code for source actor to try to pick a lock/disable trap on a container
1422 void GameControl::TryToPick(Actor *source, Container *tgt)
1424 char Tmp[40];
1426 source->ClearPath();
1427 source->ClearActions();
1428 if (tgt->Trapped && tgt->TrapDetected) {
1429 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1430 } else {
1431 snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() );
1433 source->AddAction( GenerateAction( Tmp ) );
1436 //generate action code for source actor to try to disable trap (only trap type active regions)
1437 void GameControl::TryToDisarm(Actor *source, InfoPoint *tgt)
1439 if (tgt->Type!=ST_PROXIMITY) return;
1441 char Tmp[40];
1443 source->ClearPath();
1444 source->ClearActions();
1445 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1446 source->AddAction( GenerateAction( Tmp ) );
1449 //generate action code for source actor to try to force open lock on a door/container
1450 void GameControl::TryToBash(Actor *source, Scriptable *tgt)
1452 char Tmp[40];
1454 source->ClearPath();
1455 source->ClearActions();
1456 snprintf(Tmp, sizeof(Tmp), "Attack(\"%s\")", tgt->GetScriptName() );
1457 source->AddAction( GenerateAction( Tmp ) );
1460 //generate action code for source actor to use item/cast spell on a point
1461 void GameControl::TryToCast(Actor *source, Point &tgt)
1463 char Tmp[40];
1465 if (!spellCount) {
1466 target_mode = TARGET_MODE_NONE;
1467 return; //not casting or using an own item
1469 source->ClearPath();
1470 source->ClearActions();
1472 spellCount--;
1473 if (spellOrItem>=0) {
1474 sprintf(Tmp, "NIDSpecial8()");
1475 } else {
1476 //using item on target
1477 sprintf(Tmp, "NIDSpecial7()");
1479 Action* action = GenerateAction( Tmp );
1480 action->pointParameter=tgt;
1481 if (spellOrItem>=0)
1483 CREMemorizedSpell *si;
1484 //spell casting at target
1485 si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex);
1486 if (!si)
1488 target_mode = TARGET_MODE_NONE;
1489 return;
1491 sprintf(action->string0Parameter,"%.8s",si->SpellResRef);
1493 else
1495 action->int0Parameter=spellSlot;
1496 action->int1Parameter=spellIndex;
1498 source->AddAction( action );
1499 if (!spellCount) {
1500 target_mode = TARGET_MODE_NONE;
1504 //generate action code for source actor to use item/cast spell on another actor
1505 void GameControl::TryToCast(Actor *source, Actor *tgt)
1507 char Tmp[40];
1509 if (!spellCount) {
1510 target_mode = TARGET_MODE_NONE;
1511 return; //not casting or using an own item
1513 source->ClearPath();
1514 source->ClearActions();
1516 spellCount--;
1517 if (spellOrItem>=0) {
1518 sprintf(Tmp, "NIDSpecial6()");
1519 } else {
1520 //using item on target
1521 sprintf(Tmp, "NIDSpecial5()");
1523 Action* action = GenerateActionDirect( Tmp, tgt);
1524 if (spellOrItem>=0)
1526 CREMemorizedSpell *si;
1527 //spell casting at target
1528 si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex);
1529 if (!si)
1531 target_mode = TARGET_MODE_NONE;
1532 return;
1534 sprintf(action->string0Parameter,"%.8s",si->SpellResRef);
1536 else
1538 action->int0Parameter=spellSlot;
1539 action->int1Parameter=spellIndex;
1541 source->AddAction( action );
1542 if (!spellCount) {
1543 target_mode = TARGET_MODE_NONE;
1547 //generate action code for source actor to use talk to target actor
1548 void GameControl::TryToTalk(Actor *source, Actor *tgt)
1550 char Tmp[40];
1552 //Nidspecial1 is just an unused action existing in all games
1553 //(non interactive demo)
1554 //i found no fitting action which would emulate this kind of
1555 //dialog initation
1556 source->ClearPath();
1557 source->ClearActions();
1558 strncpy(Tmp,"NIDSpecial1()",sizeof(Tmp) );
1559 targetID = tgt->globalID; //this is a hack, but not so deadly
1560 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1563 //generate action code for actor appropriate for the target mode when the target is a container
1564 void GameControl::HandleContainer(Container *container, Actor *actor)
1566 char Tmp[256];
1568 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1569 //we'll get the container back from the coordinates
1570 TryToCast(actor, container->Pos);
1571 //Do not reset target_mode, TryToCast does it for us!!
1572 return;
1575 if (target_mode == TARGET_MODE_ATTACK) {
1576 TryToBash(actor, container);
1577 target_mode = TARGET_MODE_NONE;
1578 return;
1581 if ((target_mode == TARGET_MODE_PICK)) {
1582 TryToPick(actor, container);
1583 target_mode = TARGET_MODE_NONE;
1584 return;
1587 actor->ClearPath();
1588 actor->ClearActions();
1589 strncpy(Tmp,"UseContainer()",sizeof(Tmp) );
1590 core->SetCurrentContainer( actor, container);
1591 actor->AddAction( GenerateAction( Tmp) );
1594 //generate action code for actor appropriate for the target mode when the target is a door
1595 void GameControl::HandleDoor(Door *door, Actor *actor)
1597 char Tmp[256];
1599 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1600 //we'll get the door back from the coordinates
1601 Point *p = door->toOpen;
1602 Point *otherp = door->toOpen+1;
1603 if (Distance(*p,actor)>Distance(*otherp,actor)) {
1604 p=otherp;
1606 TryToCast(actor, *p);
1607 return;
1610 if (target_mode == TARGET_MODE_ATTACK) {
1611 TryToBash(actor, door);
1612 target_mode = TARGET_MODE_NONE ;
1613 return;
1616 if ( (target_mode == TARGET_MODE_PICK) || door->TrapDetected) {
1617 TryToPick(actor, door);
1618 target_mode = TARGET_MODE_NONE ;
1619 return;
1622 actor->ClearPath();
1623 actor->ClearActions();
1624 // it really isn't very nice to store a pointer in the actor like this
1625 actor->TargetDoor = door;
1626 // internal gemrb toggle door action hack - should we use UseDoor instead?
1627 sprintf( Tmp, "NIDSpecial9()" );
1628 actor->AddAction( GenerateAction( Tmp) );
1631 //generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel)
1632 bool GameControl::HandleActiveRegion(InfoPoint *trap, Actor * actor, Point &p)
1634 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1635 //we'll get the active region from the coordinates (if needed)
1636 TryToCast(actor, p);
1637 //don't bother with this region further
1638 return true;
1640 if ((target_mode == TARGET_MODE_PICK)) {
1641 TryToDisarm(actor, trap);
1642 target_mode = TARGET_MODE_NONE;
1643 return true;
1646 switch(trap->Type) {
1647 case ST_TRAVEL:
1648 actor->UseExit(true);
1649 return false;
1650 case ST_TRIGGER:
1651 //the importer shouldn't load the script
1652 //if it is unallowed anyway (though
1653 //deactivated scripts could be reactivated)
1654 //only the 'trapped' flag should be honoured
1655 //there. Here we have to check on the
1656 //reset trap and deactivated flags
1657 if (trap->Scripts[0]) {
1658 if (!(trap->Flags&TRAP_DEACTIVATED) ) {
1659 trap->LastTriggerObject = trap->LastTrigger = actor->GetID();
1660 trap->ImmediateEvent();
1661 //directly feeding the event, even if there are actions in the queue
1662 trap->Scripts[0]->Update();
1663 trap->ProcessActions(true);
1664 //if reset trap flag not set, deactivate it
1665 //hmm, better not, info triggers don't deactivate themselves on click
1666 //if (!(trap->Flags&TRAP_RESET)) {
1667 // trap->Flags|=TRAP_DEACTIVATED;
1670 } else {
1671 if (trap->overHeadText) {
1672 if (trap->textDisplaying != 1) {
1673 trap->textDisplaying = 1;
1674 trap->timeStartDisplaying = core->GetGame()->Ticks;
1675 DisplayString( trap );
1679 if (trap->Flags&TRAP_USEPOINT) {
1680 //overriding the target point
1681 p = trap->UsePoint;
1682 return false;
1684 return true;
1685 default:;
1687 return false;
1689 /** Mouse Button Down */
1690 void GameControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short Button,
1691 unsigned short /*Mod*/)
1693 if (ScreenFlags&SF_DISABLEMOUSE)
1694 return;
1696 short px=x;
1697 short py=y;
1698 DoubleClick = false;
1699 switch(Button)
1701 case GEM_MB_SCRLUP:
1702 OnSpecialKeyPress(GEM_UP);
1703 break;
1704 case GEM_MB_SCRLDOWN:
1705 OnSpecialKeyPress(GEM_DOWN);
1706 break;
1707 case GEM_MB_ACTION|GEM_MB_DOUBLECLICK:
1708 DoubleClick = true;
1709 case GEM_MB_ACTION:
1710 core->GetVideoDriver()->ConvertToGame( px, py );
1711 MouseIsDown = true;
1712 SelectionRect.x = px;
1713 SelectionRect.y = py;
1714 StartX = px;
1715 StartY = py;
1716 SelectionRect.w = 0;
1717 SelectionRect.h = 0;
1720 /** Mouse Button Up */
1721 void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short Button,
1722 unsigned short /*Mod*/)
1724 unsigned int i;
1725 char Tmp[256];
1727 if (ScreenFlags & SF_DISABLEMOUSE) {
1728 return;
1730 //heh, i found no better place
1731 core->CloseCurrentContainer();
1733 MouseIsDown = false;
1734 Point p(x,y);
1735 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
1736 Game* game = core->GetGame();
1737 Map* area = game->GetCurrentArea( );
1739 if (DrawSelectionRect) {
1740 Actor** ab;
1741 unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
1742 for (i = 0; i < highlighted.size(); i++)
1743 highlighted[i]->SetOver( false );
1744 highlighted.clear();
1745 game->SelectActor( NULL, false, SELECT_NORMAL );
1746 if (count != 0) {
1747 for (i = 0; i < count; i++) {
1748 // FIXME: should call handler only once
1749 game->SelectActor( ab[i], true, SELECT_NORMAL );
1752 free( ab );
1753 DrawSelectionRect = false;
1754 return;
1757 //hidden actors are not selectable by clicking on them
1758 Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_HIDDEN);
1759 if (Button == GEM_MB_MENU) {
1760 if (actor) {
1761 //from GSUtils
1762 DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE);
1763 return;
1765 core->GetDictionary()->SetAt( "MenuX", x );
1766 core->GetDictionary()->SetAt( "MenuY", y );
1767 core->GetGUIScriptEngine()->RunFunction( "OpenFloatMenuWindow" );
1768 return;
1771 if (Button != GEM_MB_ACTION) {
1772 return;
1775 if (!actor && ( game->selected.size() > 0 )) {
1776 if (overDoor) {
1777 HandleDoor(overDoor, core->GetFirstSelectedPC(false));
1778 return;
1780 if (overContainer) {
1781 HandleContainer(overContainer, core->GetFirstSelectedPC(false));
1782 return;
1784 if (overInfoPoint) {
1785 if (HandleActiveRegion(overInfoPoint, core->GetFirstSelectedPC(false), p)) {
1786 return;
1790 //just a single actor, no formation
1791 if (game->selected.size()==1) {
1792 //the player is using an item or spell on the ground
1793 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1794 TryToCast(core->GetFirstSelectedPC(false), p);
1795 return;
1798 actor=game->selected[0];
1799 actor->ClearPath();
1800 actor->ClearActions();
1801 CreateMovement(actor, p);
1803 if (DoubleClick) {
1804 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
1805 } else {
1806 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
1809 actor->AddAction( GenerateAction( Tmp) );
1811 //p is a searchmap travel region
1812 if ( actor->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) {
1813 sprintf( Tmp, "NIDSpecial2()" );
1814 actor->AddAction( GenerateAction( Tmp) );
1816 return;
1819 // construct a sorted party
1820 // TODO: this is beyond horrible, help
1821 std::vector<Actor *> party;
1822 // first, from the actual party
1823 for (int idx = 0; idx < game->GetPartySize(false); idx++) {
1824 Actor *pc = game->FindPC(idx + 1);
1825 if (!pc) continue;
1827 for (unsigned int j = 0; j < game->selected.size(); j++) {
1828 if (game->selected[j] == pc) {
1829 party.push_back(pc);
1834 // then, anything else we selected
1835 for (i = 0; i < game->selected.size(); i++) {
1836 bool found = false;
1837 for (unsigned int j = 0; j < party.size(); j++) {
1838 if (game->selected[i] == party[j]) {
1839 found = true;
1840 break;
1843 if (!found) party.push_back(game->selected[i]);
1846 //party formation movement
1847 Point src = party[0]->Pos;
1848 for(i = 0; i < party.size(); i++) {
1849 actor = party[i];
1850 actor->ClearPath();
1851 actor->ClearActions();
1852 MoveToPointFormation(actor, i, src, p);
1855 //p is a searchmap travel region
1856 if ( party[0]->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) {
1857 sprintf( Tmp, "NIDSpecial2()" );
1858 party[0]->AddAction( GenerateAction( Tmp) );
1860 return;
1862 if (!actor) return;
1863 //we got an actor past this point
1864 DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE);
1866 PerformActionOn(actor);
1869 void GameControl::PerformActionOn(Actor *actor) {
1870 Game* game = core->GetGame();
1871 unsigned int i;
1873 //determining the type of the clicked actor
1874 ieDword type;
1876 type = actor->GetStat(IE_EA);
1877 if ( type >= EA_EVILCUTOFF || type == EA_GOODBUTRED ) {
1878 type = ACT_ATTACK; //hostile
1879 } else if ( type > EA_CHARMED ) {
1880 type = ACT_TALK; //neutral
1881 } else {
1882 type = ACT_NONE; //party
1885 if (target_mode == TARGET_MODE_ATTACK) {
1886 type = ACT_ATTACK;
1887 } else if (target_mode == TARGET_MODE_TALK) {
1888 type = ACT_TALK;
1889 } else if (target_mode == TARGET_MODE_CAST) {
1890 type = ACT_CAST;
1891 } else if (target_mode == TARGET_MODE_DEFEND) {
1892 type = ACT_DEFEND;
1893 } else if (target_mode == TARGET_MODE_PICK) {
1894 type = ACT_THIEVING;
1897 //we shouldn't zero this for two reasons in case of spell or item
1898 //1. there could be multiple targets
1899 //2. the target mode is important
1900 if (!(target_mode == TARGET_MODE_CAST) || !spellCount) {
1901 target_mode = TARGET_MODE_NONE;
1904 switch (type) {
1905 case ACT_NONE: //none
1906 if (actor->InParty)
1907 SelectActor( actor->InParty );
1908 else if (actor->GetStat(IE_EA) <= EA_CHARMED) {
1909 /*let's select charmed/summoned creatures
1910 EA_CHARMED is the maximum value known atm*/
1911 core->GetGame()->SelectActor(actor, true, SELECT_REPLACE) ;
1913 break;
1914 case ACT_TALK:
1915 //talk (first selected talks)
1916 if (game->selected.size()) {
1917 //if we are in PST modify this to NO!
1918 Actor *source;
1919 if (core->HasFeature(GF_PROTAGONIST_TALKS) ) {
1920 source = game->GetPC(0, false); //protagonist
1921 } else {
1922 source = core->GetFirstSelectedPC(false);
1924 // only party members can start conversations
1925 if (source) {
1926 TryToTalk(source, actor);
1929 break;
1930 case ACT_ATTACK:
1931 //all of them attacks the red circled actor
1932 for(i=0;i<game->selected.size();i++) {
1933 TryToAttack(game->selected[i], actor);
1935 break;
1936 case ACT_CAST: //cast on target or use item on target
1937 if (game->selected.size()==1) {
1938 Actor *source;
1939 source = core->GetFirstSelectedPC(false);
1940 if(source) {
1941 TryToCast(source, actor);
1944 break;
1945 case ACT_DEFEND:
1946 for(i=0;i<game->selected.size();i++) {
1947 TryToDefend(game->selected[i], actor);
1949 break;
1950 case ACT_THIEVING:
1951 if (game->selected.size()==1) {
1952 Actor *source;
1953 source = core->GetFirstSelectedPC(false);
1954 if(source) {
1955 TryToPick(source, actor);
1958 break;
1961 /** Special Key Press */
1962 void GameControl::OnSpecialKeyPress(unsigned char Key)
1964 if (DialogueFlags&DF_IN_DIALOG) {
1965 switch(Key) {
1966 case GEM_RETURN:
1967 //simulating the continue/end button pressed
1968 core->GetGUIScriptEngine()->RunFunction("CloseContinueWindow");
1969 break;
1971 return; //don't accept keys in dialog
1973 Region Viewport = core->GetVideoDriver()->GetViewport();
1974 Game *game = core->GetGame();
1975 Point mapsize = game->GetCurrentArea()->TMap->GetMapSize();
1976 int partysize = game->GetPartySize(false);
1977 int pm;
1978 char tmpstr[10];
1980 switch (Key) {
1981 case GEM_LEFT:
1982 if (Viewport.x > 63)
1983 Viewport.x -= 64;
1984 else
1985 Viewport.x = 0;
1986 break;
1987 case GEM_UP:
1988 if (Viewport.y > 63)
1989 Viewport.y -= 64;
1990 else
1991 Viewport.y = 0;
1992 break;
1993 case GEM_DOWN:
1994 if (Viewport.y + Viewport.h + 64 < mapsize.y)
1995 Viewport.y += 64;
1996 else {
1997 Viewport.y = mapsize.y - Viewport.h;
1998 if (Viewport.y<0) Viewport.y=0;
2000 break;
2001 case GEM_RIGHT:
2002 if (Viewport.x + Viewport.w + 64 < mapsize.x)
2003 Viewport.x += 64;
2004 else {
2005 Viewport.x = mapsize.x - Viewport.w;
2006 if (Viewport.x<0) Viewport.x=0;
2008 break;
2009 case GEM_ALT:
2010 DebugFlags |= DEBUG_SHOW_CONTAINERS;
2011 return;
2012 case GEM_TAB:
2013 // show partymember hp/maxhp as overhead text
2014 for (pm=0; pm < partysize; pm++) {
2015 Actor *pc = game->GetPC(pm, true);
2016 if (!pc) continue;
2017 memset(tmpstr, 0, 10);
2018 snprintf(tmpstr, 10, "%d/%d", pc->Modified[IE_HITPOINTS], pc->Modified[IE_MAXHITPOINTS]);
2019 pc->DisplayHeadText(strdup(tmpstr));
2021 return;
2022 case GEM_MOUSEOUT:
2023 moveX = 0;
2024 moveY = 0;
2025 return;
2026 case GEM_ESCAPE:
2027 core->GetGUIScriptEngine()->RunFunction("EmptyControls");
2028 core->SetEventFlag(EF_ACTION);
2029 return;
2030 case GEM_PGUP:
2031 core->GetGUIScriptEngine()->RunFunction("OnIncreaseSize");
2032 return;
2033 case GEM_PGDOWN:
2034 core->GetGUIScriptEngine()->RunFunction("OnDecreaseSize");
2035 return;
2036 default:
2037 return;
2039 if (ScreenFlags & SF_LOCKSCROLL) {
2040 moveX = 0;
2041 moveY = 0;
2043 else {
2044 // override any existing viewport moves which may be in progress
2045 core->timer->SetMoveViewPort( Viewport.x, Viewport.y, 0, false );
2046 // move it directly ourselves, since we might be paused
2047 core->GetVideoDriver()->MoveViewportTo( Viewport.x, Viewport.y );
2051 void GameControl::CalculateSelection(Point &p)
2053 unsigned int i;
2054 Game* game = core->GetGame();
2055 Map* area = game->GetCurrentArea( );
2056 if (DrawSelectionRect) {
2057 if (p.x < StartX) {
2058 SelectionRect.w = StartX - p.x;
2059 SelectionRect.x = p.x;
2060 } else {
2061 SelectionRect.x = StartX;
2062 SelectionRect.w = p.x - StartX;
2064 if (p.y < StartY) {
2065 SelectionRect.h = StartY - p.y;
2066 SelectionRect.y = p.y;
2067 } else {
2068 SelectionRect.y = StartY;
2069 SelectionRect.h = p.y - StartY;
2071 Actor** ab;
2072 unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
2073 for (i = 0; i < highlighted.size(); i++)
2074 highlighted[i]->SetOver( false );
2075 highlighted.clear();
2076 if (count != 0) {
2077 for (i = 0; i < count; i++) {
2078 ab[i]->SetOver( true );
2079 highlighted.push_back( ab[i] );
2082 free( ab );
2083 } else {
2084 Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_ENEMY);
2085 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
2086 if (lastActor)
2087 lastActor->SetOver( false );
2088 if (!actor) {
2089 lastActorID = 0;
2090 } else {
2091 lastActorID = actor->globalID;
2092 actor->SetOver( true );
2097 void GameControl::SetCutSceneMode(bool active)
2099 if (active) {
2100 ScreenFlags |= (SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE);
2101 moveX = 0;
2102 moveY = 0;
2103 } else {
2104 ScreenFlags &= ~(SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE);
2108 //Change game window geometries when a new window gets deactivated
2109 void GameControl::HandleWindowHide(const char *WindowName, const char *WindowPosition)
2111 Variables* dict = core->GetDictionary();
2112 ieDword index;
2114 if (dict->Lookup( WindowName, index )) {
2115 if (index != (ieDword) -1) {
2116 Window* w = core->GetWindow( (unsigned short) index );
2117 if (w) {
2118 core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE );
2119 if (dict->Lookup( WindowPosition, index )) {
2120 ResizeDel( w, index );
2122 return;
2124 printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED);
2125 printf("%s:%u\n",WindowName, index);
2130 //Hide all other windows on the GUI (gamecontrol is not hidden by this)
2131 int GameControl::HideGUI()
2133 //hidegui is in effect
2134 if (!(ScreenFlags&SF_GUIENABLED) ) {
2135 return 0;
2137 //no gamecontrol visible
2138 if (Owner->Visible == WINDOW_INVISIBLE ) {
2139 return 0;
2141 ScreenFlags &=~SF_GUIENABLED;
2142 HandleWindowHide("PortraitWindow", "PortraitPosition");
2143 HandleWindowHide("OtherWindow", "OtherPosition");
2144 HandleWindowHide("TopWindow", "TopPosition");
2145 HandleWindowHide("OptionsWindow", "OptionsPosition");
2146 HandleWindowHide("MessageWindow", "MessagePosition");
2147 HandleWindowHide("ActionsWindow", "ActionsPosition");
2148 //FloatWindow doesn't affect gamecontrol, so it is special
2149 Variables* dict = core->GetDictionary();
2150 ieDword index;
2152 if (dict->Lookup( "FloatWindow", index )) {
2153 if (index != (ieDword) -1) {
2154 core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE );
2157 core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height );
2158 return 1;
2161 //Change game window geometries when a new window gets activated
2162 void GameControl::HandleWindowReveal(const char *WindowName, const char *WindowPosition)
2164 Variables* dict = core->GetDictionary();
2165 ieDword index;
2167 if (dict->Lookup( WindowName, index )) {
2168 if (index != (ieDword) -1) {
2169 Window* w = core->GetWindow( (unsigned short) index );
2170 if (w) {
2171 core->SetVisible( (unsigned short) index, WINDOW_VISIBLE );
2172 if (dict->Lookup( WindowPosition, index )) {
2173 ResizeAdd( w, index );
2175 return;
2177 printMessage("GameControl", "Invalid Window Index ", LIGHT_RED);
2178 printf("%s:%u\n",WindowName, index);
2183 //Reveal all windows on the GUI (including this one)
2184 int GameControl::UnhideGUI()
2186 if (ScreenFlags&SF_GUIENABLED) {
2187 return 0;
2190 ScreenFlags |= SF_GUIENABLED;
2191 // Unhide the gamecontrol window
2192 core->SetVisible( 0, WINDOW_VISIBLE );
2194 HandleWindowReveal("ActionsWindow", "ActionsPosition");
2195 HandleWindowReveal("MessageWindow", "MessagePosition");
2196 HandleWindowReveal("OptionsWindow", "OptionsPosition");
2197 HandleWindowReveal("TopWindow", "TopPosition");
2198 HandleWindowReveal("OtherWindow", "OtherPosition");
2199 HandleWindowReveal("PortraitWindow", "PortraitPosition");
2200 //the floatwindow is a special case
2201 Variables* dict = core->GetDictionary();
2202 ieDword index;
2204 if (dict->Lookup( "FloatWindow", index )) {
2205 if (index != (ieDword) -1) {
2206 Window* fw = core->GetWindow( (unsigned short) index );
2207 if (fw) {
2208 core->SetVisible( (unsigned short) index, WINDOW_VISIBLE );
2209 fw->Flags |=WF_FLOAT;
2210 core->SetOnTop( index );
2214 core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height );
2215 return 1;
2218 //a window got removed, so the GameControl gets enlarged
2219 void GameControl::ResizeDel(Window* win, int type)
2221 switch (type) {
2222 case 0: //Left
2223 if (LeftCount!=1) {
2224 printMessage("GameControl","More than one left window!\n",LIGHT_RED);
2226 LeftCount--;
2227 if (!LeftCount) {
2228 Owner->XPos -= win->Width;
2229 Owner->Width += win->Width;
2230 Width = Owner->Width;
2232 break;
2234 case 1: //Bottom
2235 if (BottomCount!=1) {
2236 printMessage("GameControl","More than one bottom window!\n",LIGHT_RED);
2238 BottomCount--;
2239 if (!BottomCount) {
2240 Owner->Height += win->Height;
2241 Height = Owner->Height;
2243 break;
2245 case 2: //Right
2246 if (RightCount!=1) {
2247 printMessage("GameControl","More than one right window!\n",LIGHT_RED);
2249 RightCount--;
2250 if (!RightCount) {
2251 Owner->Width += win->Width;
2252 Width = Owner->Width;
2254 break;
2256 case 3: //Top
2257 if (TopCount!=1) {
2258 printMessage("GameControl","More than one top window!\n",LIGHT_RED);
2260 TopCount--;
2261 if (!TopCount) {
2262 Owner->YPos -= win->Height;
2263 Owner->Height += win->Height;
2264 Height = Owner->Height;
2266 break;
2268 case 4: //BottomAdded
2269 BottomCount--;
2270 Owner->Height += win->Height;
2271 Height = Owner->Height;
2272 break;
2273 case 5: //Inactivating
2274 BottomCount--;
2275 Owner->Height += win->Height;
2276 Height = Owner->Height;
2277 break;
2281 //a window got added, so the GameControl gets shrunk
2282 //Owner is the GameControl's window
2283 //GameControl is the only control on that window
2284 void GameControl::ResizeAdd(Window* win, int type)
2286 switch (type) {
2287 case 0: //Left
2288 LeftCount++;
2289 if (LeftCount == 1) {
2290 Owner->XPos += win->Width;
2291 Owner->Width -= win->Width;
2292 Width = Owner->Width;
2294 break;
2296 case 1: //Bottom
2297 BottomCount++;
2298 if (BottomCount == 1) {
2299 Owner->Height -= win->Height;
2300 Height = Owner->Height;
2302 break;
2304 case 2: //Right
2305 RightCount++;
2306 if (RightCount == 1) {
2307 Owner->Width -= win->Width;
2308 Width = Owner->Width;
2310 break;
2312 case 3: //Top
2313 TopCount++;
2314 if (TopCount == 1) {
2315 Owner->YPos += win->Height;
2316 Owner->Height -= win->Height;
2317 Height = Owner->Height;
2319 break;
2321 case 4: //BottomAdded
2322 BottomCount++;
2323 Owner->Height -= win->Height;
2324 Height = Owner->Height;
2325 break;
2327 case 5: //Inactivating
2328 BottomCount++;
2329 Owner->Height -= win->Height;
2330 Height = 0;
2334 //Try to start dialogue between two actors (one of them could be inanimate)
2335 int GameControl::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgref)
2337 if (dlg) {
2338 delete dlg;
2339 dlg = NULL;
2342 DialogMgr* dm = ( DialogMgr* ) core->GetInterface( IE_DLG_CLASS_ID );
2343 dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true );
2344 dlg = dm->GetDialog();
2345 dm->release();
2347 if (!dlg) {
2348 printMessage("GameControl", " ", LIGHT_RED);
2349 printf( "Cannot start dialog: %s\n", dlgref );
2350 return -1;
2353 strnlwrcpy(dlg->ResRef, dlgref, 8); //this isn't handled by GetDialog???
2355 //target is here because it could be changed when a dialog runs onto
2356 //and external link, we need to find the new target (whose dialog was
2357 //linked to)
2359 Actor *spe = (Actor *) spk;
2360 speakerID = spe->globalID;
2361 if (tgt->Type!=ST_ACTOR) {
2362 targetID=0xffff;
2363 //most likely this dangling object reference
2364 //won't cause problems, because trigger points don't
2365 //get destroyed during a dialog
2366 targetOB=tgt;
2367 spk->LastTalkedTo=0;
2368 } else {
2369 Actor *tar = (Actor *) tgt;
2370 speakerID = spe->globalID;
2371 targetID = tar->globalID;
2372 if (!originalTargetID) originalTargetID = tar->globalID;
2373 spe->LastTalkedTo=targetID;
2374 tar->LastTalkedTo=speakerID;
2377 //check if we are already in dialog
2378 if (DialogueFlags&DF_IN_DIALOG) {
2379 return 0;
2382 int si = dlg->FindFirstState( tgt );
2383 if (si < 0) {
2384 return -1;
2387 //we need GUI for dialogs
2388 UnhideGUI();
2390 //no exploring while in dialogue
2391 ScreenFlags |= SF_GUIENABLED|SF_DISABLEMOUSE|SF_LOCKSCROLL;
2392 DialogueFlags |= DF_IN_DIALOG;
2394 if (tgt->Type==ST_ACTOR) {
2395 Actor *tar = (Actor *) tgt;
2396 tar->DialogInterrupt();
2399 //allow mouse selection from dialog (even though screen is locked)
2400 core->GetVideoDriver()->SetMouseEnabled(true);
2401 core->timer->SetMoveViewPort( tgt->Pos.x, tgt->Pos.y, 0, true );
2402 //there are 3 bits, if they are all unset, the dialog freezes scripts
2403 if (!(dlg->Flags&7) ) {
2404 DialogueFlags |= DF_FREEZE_SCRIPTS;
2406 //opening control size to maximum, enabling dialog window
2407 core->GetGame()->SetControlStatus(CS_HIDEGUI, BM_NAND);
2408 core->GetGame()->SetControlStatus(CS_DIALOG, BM_OR);
2409 core->SetEventFlag(EF_PORTRAIT);
2410 return 0;
2413 /*try to break will only try to break it, false means unconditional stop*/
2414 void GameControl::EndDialog(bool try_to_break)
2416 if (try_to_break && (DialogueFlags&DF_UNBREAKABLE) ) {
2417 return;
2420 Actor *tmp = GetSpeaker();
2421 if (tmp) {
2422 tmp->LeaveDialog();
2424 speakerID = 0;
2425 if (targetID==0xffff) {
2426 targetOB->LeaveDialog();
2427 } else {
2428 tmp=GetTarget();
2429 if (tmp) {
2430 tmp->LeaveDialog();
2433 targetOB = NULL;
2434 targetID = 0;
2435 originalTargetID = 0;
2436 ds = NULL;
2437 if (dlg) {
2438 delete dlg;
2439 dlg = NULL;
2441 //restoring original size
2442 core->GetGame()->SetControlStatus(CS_DIALOG, BM_NAND);
2443 ScreenFlags &=~(SF_DISABLEMOUSE|SF_LOCKSCROLL);
2444 DialogueFlags = 0;
2445 core->SetEventFlag(EF_PORTRAIT);
2448 //translate section values (journal, solved, unsolved, user)
2449 static const int sectionMap[4]={4,1,2,0};
2451 void GameControl::DialogChoose(unsigned int choose)
2453 char Tmp[256];
2455 TextArea* ta = core->GetMessageTextArea();
2456 if (!ta) {
2457 printMessage("GameControl","Dialog aborted???",LIGHT_RED);
2458 EndDialog();
2459 return;
2462 Actor *speaker = GetSpeaker();
2463 if (!speaker) {
2464 printMessage("GameControl","Speaker gone???",LIGHT_RED);
2465 EndDialog();
2466 return;
2468 Actor *tgt;
2469 Scriptable *target;
2471 if (targetID!=0xffff) {
2472 tgt = GetTarget();
2473 target = tgt;
2474 } else {
2475 //risky!!!
2476 target = targetOB;
2477 tgt=NULL;
2479 if (!target) {
2480 printMessage("GameControl","Target gone???",LIGHT_RED);
2481 EndDialog();
2482 return;
2485 if (choose == (unsigned int) -1) {
2486 //increasing talkcount after top level condition was determined
2488 int si = dlg->FindFirstState( tgt );
2489 if (si<0) {
2490 EndDialog();
2491 return;
2494 if (tgt) {
2495 if (DialogueFlags&DF_TALKCOUNT) {
2496 DialogueFlags&=~DF_TALKCOUNT;
2497 tgt->TalkCount++;
2498 } else if (DialogueFlags&DF_INTERACT) {
2499 DialogueFlags&=~DF_INTERACT;
2500 tgt->InteractCount++;
2503 ds = dlg->GetState( si );
2504 } else {
2505 if (ds->transitionsCount <= choose) {
2506 return;
2509 DialogTransition* tr = ds->transitions[choose];
2511 ta->PopMinRow();
2513 if (tr->Flags&IE_DLG_TR_JOURNAL) {
2514 int Section = 0;
2515 if (tr->Flags&IE_DLG_UNSOLVED) {
2516 Section |= 1;
2518 if (tr->Flags&IE_DLG_SOLVED) {
2519 Section |= 2;
2521 if (core->GetGame()->AddJournalEntry(tr->journalStrRef, sectionMap[Section], tr->Flags>>16) ) {
2522 core->DisplayConstantString(STR_JOURNALCHANGE,0xffff00);
2523 char *string = core->GetString( tr->journalStrRef );
2524 //cutting off the strings at the first crlf
2525 char *poi = strchr(string,'\n');
2526 if (poi) {
2527 *poi='\0';
2529 core->DisplayString( string );
2530 free( string );
2534 if (tr->textStrRef != 0xffffffff) {
2535 //allow_zero is for PST (deionarra's text)
2536 core->DisplayStringName( (int) (tr->textStrRef), 0x8080FF, speaker, IE_STR_SOUND|IE_STR_SPEECH|IE_STR_ALLOW_ZERO);
2537 if (core->HasFeature( GF_DIALOGUE_SCROLLS )) {
2538 ta->AppendText( "", -1 );
2542 if (tr->action) {
2543 // does this belong here? we must clear actions somewhere before
2544 // we start executing them (otherwise queued actions interfere)
2545 // executing actions directly does not work, because dialog
2546 // needs to end before final actions are executed due to
2547 // actions making new dialogs!
2548 if (target->Type == ST_ACTOR) ((Movable *)target)->ClearPath(); // fuzzie added this
2549 target->ClearActions();
2551 for (unsigned int i = 0; i < tr->action->count; i++) {
2552 Action* action = GenerateAction( tr->action->strings[i]);
2553 if (action) {
2554 target->AddAction(action);
2555 //GameScript::ExecuteAction( target, action );
2556 } else {
2557 snprintf(Tmp, sizeof(Tmp),
2558 "Can't compile action: %s\n",
2559 tr->action->strings[i] );
2560 printMessage( "Dialog", Tmp,YELLOW);
2565 int final_dialog = tr->Flags & IE_DLG_TR_FINAL;
2567 if (final_dialog) {
2568 ta->SetMinRow( false );
2569 EndDialog();
2572 // all dialog actions must be executed immediately
2573 target->ProcessActions(true);
2574 // (do not clear actions - final actions can involve waiting/moving)
2576 if (final_dialog) {
2577 return;
2580 //displaying dialog for selected option
2581 int si = tr->stateIndex;
2582 //follow external linkage, if required
2583 if (tr->Dialog[0] && strnicmp( tr->Dialog, dlg->ResRef, 8 )) {
2584 //target should be recalculated!
2585 tgt = NULL;
2586 if (originalTargetID) {
2587 // always try original target first (sometimes there are multiple
2588 // actors with the same dialog in an area, we want to pick the one
2589 // we were talking to)
2590 tgt = GetActorByGlobalID(originalTargetID);
2591 if (tgt && strnicmp( tgt->GetDialog(GD_NORMAL), tr->Dialog, 8 ) != 0) {
2592 tgt = NULL;
2595 if (!tgt) {
2596 // then just search the current area for an actor with the dialog
2597 tgt = target->GetCurrentArea()->GetActorByDialog(tr->Dialog);
2599 target = tgt;
2600 if (!target) {
2601 printMessage("Dialog","Can't redirect dialog\n",YELLOW);
2602 ta->SetMinRow( false );
2603 EndDialog();
2604 return;
2606 targetID = tgt->globalID;
2607 // we have to make a backup, tr->Dialog is freed
2608 ieResRef tmpresref;
2609 strnlwrcpy(tmpresref,tr->Dialog, 8);
2610 if (target->GetInternalFlag()&IF_NOINT) {
2611 // this whole check moved out of InitDialog by fuzzie, see comments
2612 // for the IF_NOINT check in BeginDialog
2613 core->DisplayConstantString(STR_TARGETBUSY,0xff0000);
2614 ta->SetMinRow( false );
2615 EndDialog();
2616 return;
2618 int ret = InitDialog( speaker, target, tmpresref);
2619 if (ret<0) {
2620 // error was displayed by InitDialog
2621 ta->SetMinRow( false );
2622 EndDialog();
2623 return;
2626 ds = dlg->GetState( si );
2627 if (!ds) {
2628 printMessage("Dialog","Can't find next dialog\n",YELLOW);
2629 ta->SetMinRow( false );
2630 EndDialog();
2631 return;
2634 //displaying npc text
2635 core->DisplayStringName( ds->StrRef, 0x70FF70, target, IE_STR_SOUND|IE_STR_SPEECH);
2636 //adding a gap between options and npc text
2637 ta->AppendText("",-1);
2638 int i;
2639 int idx = 0;
2640 ta->SetMinRow( true );
2641 //first looking for a 'continue' opportunity, the order is descending (a la IE)
2642 unsigned int x = ds->transitionsCount;
2643 while(x--) {
2644 if (ds->transitions[x]->Flags & IE_DLG_TR_FINAL) {
2645 continue;
2647 if (ds->transitions[x]->textStrRef != 0xffffffff) {
2648 continue;
2650 if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) {
2651 if (!dlg->EvaluateDialogTrigger(target, ds->transitions[x]->trigger)) {
2652 continue;
2655 core->GetDictionary()->SetAt("DialogOption",x);
2656 DialogueFlags |= DF_OPENCONTINUEWINDOW;
2657 goto end_of_choose;
2659 for (x = 0; x < ds->transitionsCount; x++) {
2660 if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) {
2661 if (!dlg->EvaluateDialogTrigger(target, ds->transitions[x]->trigger)) {
2662 continue;
2665 idx++;
2666 if (ds->transitions[x]->textStrRef == 0xffffffff) {
2667 //dialogchoose should be set to x
2668 //it isn't important which END option was chosen, as it ends
2669 core->GetDictionary()->SetAt("DialogOption",x);
2670 DialogueFlags |= DF_OPENENDWINDOW;
2671 } else {
2672 char *string = ( char * ) malloc( 40 );
2673 sprintf( string, "[s=%d,ffffff,ff0000]%d - [p]", x, idx );
2674 i = ta->AppendText( string, -1 );
2675 free( string );
2676 string = core->GetString( ds->transitions[x]->textStrRef );
2677 ta->AppendText( string, i );
2678 free( string );
2679 ta->AppendText( "[/p][/s]", i );
2682 // this happens if a trigger isn't implemented or the dialog is wrong
2683 if (!idx) {
2684 printMessage("Dialog", "There were no valid dialog options!\n", YELLOW);
2685 DialogueFlags |= DF_OPENENDWINDOW;
2687 end_of_choose:
2688 //padding the rows so our text will be at the top
2689 if (core->HasFeature( GF_DIALOGUE_SCROLLS )) {
2690 ta->AppendText( "", -1 );
2692 else {
2693 ta->PadMinRow();
2697 //Create an overhead text over an arbitrary point
2698 void GameControl::DisplayString(Point &p, const char *Text)
2700 Scriptable* scr = new Scriptable( ST_TRIGGER );
2701 scr->overHeadText = (char *) Text;
2702 scr->textDisplaying = 1;
2703 scr->timeStartDisplaying = 0;
2704 scr->Pos = p;
2705 scr->ClearCutsceneID( );
2708 //Create an overhead text over a scriptable target
2709 //Multiple texts are possible, as this code copies the text to a new object
2710 void GameControl::DisplayString(Scriptable* target)
2712 Scriptable* scr = new Scriptable( ST_TRIGGER );
2713 scr->overHeadText = strdup( target->overHeadText );
2714 /* strdup should work here, we use it elsewhere
2715 size_t len = strlen( target->overHeadText ) + 1;
2716 scr->overHeadText = ( char * ) malloc( len );
2717 strcpy( scr->overHeadText, target->overHeadText );
2719 scr->textDisplaying = 1;
2720 scr->timeStartDisplaying = target->timeStartDisplaying;
2721 scr->Pos = target->Pos;
2722 scr->SetCutsceneID( target );
2725 /** changes displayed map to the currently selected PC */
2726 void GameControl::ChangeMap(Actor *pc, bool forced)
2728 //swap in the area of the actor
2729 Game* game = core->GetGame();
2730 if (forced || (stricmp( pc->Area, game->CurrentArea) != 0) ) {
2731 EndDialog();
2732 overInfoPoint = NULL;
2733 overContainer = NULL;
2734 overDoor = NULL;
2735 /*this is loadmap, because we need the index, not the pointer*/
2736 char *areaname = game->CurrentArea;
2737 if (pc) {
2738 areaname = pc->Area;
2740 game->GetMap( areaname, true );
2741 ScreenFlags|=SF_CENTERONACTOR;
2743 //center on first selected actor
2744 Region vp = core->GetVideoDriver()->GetViewport();
2745 if (ScreenFlags&SF_CENTERONACTOR) {
2746 core->timer->SetMoveViewPort( pc->Pos.x, pc->Pos.y, 0, true );
2747 ScreenFlags&=~SF_CENTERONACTOR;
2751 void GameControl::SetScreenFlags(int value, int mode)
2753 switch(mode) {
2754 case BM_OR: ScreenFlags|=value; break;
2755 case BM_NAND: ScreenFlags&=~value; break;
2756 case BM_SET: ScreenFlags=value; break;
2757 case BM_AND: ScreenFlags&=value; break;
2758 case BM_XOR: ScreenFlags^=value; break;
2762 void GameControl::SetDialogueFlags(int value, int mode)
2764 switch(mode) {
2765 case BM_OR: DialogueFlags|=value; break;
2766 case BM_NAND: DialogueFlags&=~value; break;
2767 case BM_SET: DialogueFlags=value; break;
2768 case BM_AND: DialogueFlags&=value; break;
2769 case BM_XOR: DialogueFlags^=value; break;
2773 //copies a screenshot into a sprite
2774 Sprite2D* GameControl::GetScreenshot(bool show_gui)
2776 Sprite2D* screenshot;
2777 if (show_gui) {
2778 screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) );
2779 } else {
2780 int hf = HideGUI ();
2781 Draw (0, 0);
2782 screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) );
2783 if (hf) {
2784 UnhideGUI ();
2786 core->DrawWindows ();
2789 return screenshot;
2792 //copies a downscaled screenshot into a sprite for save game preview
2793 Sprite2D* GameControl::GetPreview()
2795 // We get preview by first taking a screenshot of size 640x405,
2796 // centered in the display. This is to get a decent picture for
2797 // higher screen resolutions.
2798 // FIXME: how do orig games solve that?
2799 Video* video = core->GetVideoDriver();
2800 int w = video->GetWidth();
2801 int h = video->GetHeight();
2802 int x = (w - 640) / 2;
2803 int y = (h - 405) / 2;
2805 if (x < 0) {
2806 x = 0;
2807 } else {
2808 w = 640;
2811 if (y < 0) {
2812 y = 0;
2813 } else {
2814 h = 405;
2817 if (!x)
2818 y = 0;
2820 int hf = HideGUI ();
2821 signed char v = Owner->Visible;
2822 Owner->Visible = WINDOW_VISIBLE;
2823 Draw (0, 0);
2824 Owner->Visible = v;
2825 Sprite2D *screenshot = video->GetScreenshot( Region(x, y, w, h) );
2826 if (hf) {
2827 UnhideGUI ();
2829 core->DrawWindows();
2831 Sprite2D* preview = video->SpriteScaleDown ( screenshot, 5 );
2832 video->FreeSprite( screenshot );
2833 return preview;
2838 * Returns PC portrait for a currently running game
2840 Sprite2D* GameControl::GetPortraitPreview(int pcslot)
2842 /** Portrait shrink ratio */
2843 // FIXME: this is just a random PST specific trait
2844 // you can make it less random with a new feature bit
2845 int ratio = (core->HasFeature( GF_ONE_BYTE_ANIMID )) ? 1 : 2;
2847 Video *video = core->GetVideoDriver();
2849 Actor *actor = core->GetGame()->GetPC( pcslot, false );
2850 if (! actor) {
2851 return NULL;
2853 ImageMgr* im = ( ImageMgr* )
2854 gamedata->GetResource( actor->GetPortrait(true), &ImageMgr::ID );
2855 if (! im) {
2856 return NULL;
2859 Sprite2D* img = im->GetSprite2D();
2860 im->release();
2862 if (ratio == 1)
2863 return img;
2865 Sprite2D* img_scaled = video->SpriteScaleDown( img, ratio );
2866 video->FreeSprite( img );
2868 return img_scaled;
2871 Actor *GameControl::GetActorByGlobalID(ieWord ID)
2873 if (!ID)
2874 return NULL;
2875 Game* game = core->GetGame();
2876 if (!game)
2877 return NULL;
2879 Map* area = game->GetCurrentArea( );
2880 if (!area)
2881 return NULL;
2882 return
2883 area->GetActorByGlobalID(ID);
2886 Actor *GameControl::GetLastActor()
2888 return GetActorByGlobalID(lastActorID);
2891 Actor *GameControl::GetTarget()
2893 return GetActorByGlobalID(targetID);
2896 Actor *GameControl::GetSpeaker()
2898 return GetActorByGlobalID(speakerID);
2901 //Set up an item use which needs targeting
2902 //Slot is an inventory slot
2903 //header is the used item extended header
2904 //u is the user
2905 //target type is a bunch of GetActor flags that alter valid targets
2906 //cnt is the number of different targets (usually 1)
2907 void GameControl::SetupItemUse(int slot, int header, Actor *u, int targettype, int cnt)
2909 spellOrItem = -1;
2910 spellUser = u;
2911 spellSlot = slot;
2912 spellIndex = header;
2913 //item use also uses the casting icon, this might be changed in some custom game?
2914 target_mode = TARGET_MODE_CAST;
2915 target_types = targettype;
2916 spellCount = cnt;
2919 //Set up spell casting which needs targeting
2920 //type is the spell's type
2921 //level is the caster level
2922 //idx is the spell's number
2923 //u is the caster
2924 //target type is a bunch of GetActor flags that alter valid targets
2925 //cnt is the number of different targets (usually 1)
2926 void GameControl::SetupCasting(int type, int level, int idx, Actor *u, int targettype, int cnt)
2928 spellOrItem = type;
2929 spellUser = u;
2930 spellSlot = level;
2931 spellIndex = idx;
2932 target_mode = TARGET_MODE_CAST;
2933 target_types = targettype;
2934 spellCount = cnt;
2937 //another method inherited from Control which has no use here
2938 bool GameControl::SetEvent(int /*eventType*/, const char * /*handler*/)
2940 return false;
2943 void GameControl::SetDisplayText(char *text, unsigned int time)
2945 if (DisplayText) {
2946 core->FreeString(DisplayText);
2948 DisplayTextTime = time;
2949 DisplayText = text;
2952 void GameControl::SetDisplayText(ieStrRef text, unsigned int time)
2954 SetDisplayText(core->GetString(core->GetStringReference(text), 0), time);