Sort include order.
[gemrb.git] / gemrb / core / GameControl.cpp
blob79aa9ae0e1a6568d233ffe85857d785bc50ebc25
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 #include "GameControl.h"
22 #include "strrefs.h"
23 #include "win32def.h"
25 #include "DialogMgr.h"
26 #include "Effect.h"
27 #include "GSUtils.h"
28 #include "Game.h"
29 #include "GameData.h"
30 #include "ImageMgr.h"
31 #include "Interface.h"
32 #include "Item.h"
33 #include "SaveGameIterator.h"
34 #include "ScriptEngine.h"
35 #include "TileMap.h"
36 #include "Video.h"
37 #include "damages.h"
39 #include <cmath>
40 #ifndef WIN32
41 #include <sys/time.h>
42 #endif
44 #define DEBUG_SHOW_INFOPOINTS 0x01
45 #define DEBUG_SHOW_CONTAINERS 0x02
46 #define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
47 #define DEBUG_SHOW_LIGHTMAP 0x08
49 static const Color cyan = {
50 0x00, 0xff, 0xff, 0xff
52 static const Color red = {
53 0xff, 0x00, 0x00, 0xff
55 static const Color magenta = {
56 0xff, 0x00, 0xff, 0xff
58 static const Color green = {
59 0x00, 0xff, 0x00, 0xff
62 static Color white = {
63 0xff, 0xff, 0xff, 0xff
66 static const Color black = {
67 0x00, 0x00, 0x00, 0xff
69 static const Color blue = {
70 0x00, 0x00, 0xff, 0x80
73 //Animation* effect;
75 #define FORMATIONSIZE 10
76 typedef Point formation_type[FORMATIONSIZE];
77 ieDword formationcount;
78 static formation_type *formations=NULL;
79 static bool mqs = false;
80 static ieResRef TestSpell="SPWI207";
82 //If one of the actors has tracking on, the gamecontrol needs to display
83 //arrow markers on the edges to point at detected monsters
84 //tracterID is the tracker actor's global ID
85 //distance is the detection distance
86 void GameControl::SetTracker(Actor *actor, ieDword dist)
88 trackerID = actor->GetID();
89 distance = dist;
92 //Multiple Quick saves is an experimental GemRB feature.
93 //multiple quick saves are kept, their age is determined by the slot
94 //number. There is an algorithm which keeps about log2(n) slots alive.
95 //The algorithm is implemented in SaveGameIterator
96 void GameControl::MultipleQuickSaves(int arg)
98 mqs=arg==1;
101 GameControl::GameControl(void)
103 if (!formations) {
104 ReadFormations();
106 //this is the default action, individual actors should have one too
107 //at this moment we use only this
108 //maybe we don't even need it
109 Changed = true;
110 spellCount = 0;
111 user = NULL;
112 lastActorID = 0;
113 trackerID = 0;
114 distance = 0;
115 MouseIsDown = false;
116 DrawSelectionRect = false;
117 overDoor = NULL;
118 overContainer = NULL;
119 overInfoPoint = NULL;
120 drawPath = NULL;
121 pfs.null();
122 lastCursor = IE_CURSOR_NORMAL;
123 moveX = moveY = 0;
124 scrolling = false;
125 numScrollCursor = 0;
126 DebugFlags = 0;
127 AIUpdateCounter = 1;
128 ieDword tmp=0;
130 target_mode = TARGET_MODE_NONE;
131 target_types = GA_SELECT|GA_NO_DEAD|GA_NO_HIDDEN;
133 core->GetDictionary()->Lookup("Center",tmp);
134 if (tmp) {
135 ScreenFlags=SF_ALWAYSCENTER|SF_CENTERONACTOR;
137 else {
138 ScreenFlags = SF_CENTERONACTOR;
140 LeftCount = 0;
141 BottomCount = 0;
142 RightCount = 0;
143 TopCount = 0;
144 DialogueFlags = 0;
145 dlg = NULL;
146 targetID = 0;
147 originalTargetID = 0;
148 speakerID = 0;
149 targetOB = NULL;
150 DisplayText = NULL;
153 //TODO:
154 //There could be a custom formation which is saved in the save game
155 //alternatively, all formations could be saved in some compatible way
156 //so it doesn't cause problems with the original engine
157 void GameControl::ReadFormations()
159 unsigned int i,j;
160 AutoTable tab("formatio");
161 if (!tab) {
162 // fallback
163 formationcount = 1;
164 formations = (formation_type *) calloc(1,sizeof(formation_type) );
165 return;
167 formationcount = tab->GetRowCount();
168 formations = (formation_type *) calloc(formationcount, sizeof(formation_type));
169 for(i=0; i<formationcount; i++) {
170 for(j=0;j<FORMATIONSIZE;j++) {
171 short k=(short) atoi(tab->QueryField(i,j*2));
172 formations[i][j].x=k;
173 k=(short) atoi(tab->QueryField(i,j*2+1));
174 formations[i][j].y=k;
179 //returns a single point offset for a formation
180 //formation: the formation type
181 //pos: the actor's slot ID
182 Point GameControl::GetFormationOffset(ieDword formation, ieDword pos)
184 if (formation>=formationcount) formation = 0;
185 if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1;
186 return formations[formation][pos];
189 //Moves an actor to a new position, keeping the current formation
190 //WARNING: don't pass p as a reference because it gets modified
191 void GameControl::MoveToPointFormation(Actor *actor, unsigned int pos, Point src, Point p)
193 Map* map = actor->GetCurrentArea() ;
195 int formation=core->GetGame()->GetFormation();
196 if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1;
198 // calculate angle
199 double angle;
200 double xdiff = src.x - p.x;
201 double ydiff = src.y - p.y;
202 if (ydiff == 0) {
203 if (xdiff > 0) {
204 angle = M_PI_2;
205 } else {
206 angle = -M_PI_2;
208 } else {
209 angle = atan(xdiff/ydiff);
210 if (ydiff < 0) angle += M_PI;
213 // calculate new coordinates by rotating formation around (0,0)
214 double newx = -formations[formation][pos].x * cos(angle) + formations[formation][pos].y * sin(angle);
215 double newy = formations[formation][pos].x * sin(angle) + formations[formation][pos].y * cos(angle);
216 p.x += (int)newx;
217 p.y += (int)newy;
219 if (p.x < 0) p.x = 8;
220 if (p.y < 0) p.y = 8;
221 if (p.x > map->GetWidth()*16) p.x = map->GetWidth()*16 - 8;
222 if (p.y > map->GetHeight()*12) p.y = map->GetHeight()*12 - 8;
224 if(map->GetCursor(p) == IE_CURSOR_BLOCKED) {
225 //we can't get there --> adjust position
226 p.x/=16;
227 p.y/=12;
228 map->AdjustPosition(p);
229 p.x*=16;
230 p.y*=12;
232 CreateMovement(actor, p);
235 // generate an action to do the actual movement
236 // only PST supports RunToPoint
237 void GameControl::CreateMovement(Actor *actor, const Point &p)
239 char Tmp[256];
241 Action *action = NULL;
242 if (DoubleClick) {
243 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
244 action = GenerateAction( Tmp );
246 if (!action)
248 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
249 action = GenerateAction( Tmp );
252 actor->AddAction( action );
253 // force action so that we get target recticles immediately
254 actor->ProcessActions(true);
257 GameControl::~GameControl(void)
259 //releasing the viewport of GameControl
260 core->GetVideoDriver()->SetViewport( 0,0,0,0 );
261 if (formations) {
262 free( formations );
263 formations = NULL;
265 if (dlg) {
266 delete dlg;
268 if (DisplayText) {
269 core->FreeString(DisplayText);
273 //Autosave was triggered by the GUI
274 void GameControl::AutoSave()
276 core->GetSaveGameIterator()->CreateSaveGame(0, false);
279 //QuickSave was triggered by the GUI
280 //mqs is the 'multiple quick saves' flag
281 void GameControl::QuickSave()
283 core->GetSaveGameIterator()->CreateSaveGame(1, mqs == 1);
286 // ArrowSprite cycles
287 // 321
288 // 4 0
289 // 567
291 #define D_LEFT 1
292 #define D_UP 2
293 #define D_RIGHT 4
294 #define D_BOTTOM 8
295 // Direction Bits
296 // 326
297 // 1 4
298 // 98c
300 static const int arrow_orientations[16]={
301 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
302 -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1
305 //Draws arrow markers along the edge of the game window
306 //WARNING:don't use reference for point, because it is altered
307 void GameControl::DrawArrowMarker(const Region &screen, Point p, const Region &viewport)
309 Video* video = core->GetVideoDriver();
311 //p.x-=viewport.x;
312 //p.y-=viewport.y;
313 ieDword draw = 0;
314 if (p.x<viewport.x) {
315 p.x=viewport.x;
316 draw|= D_LEFT;
318 if (p.y<viewport.y) {
319 p.y=viewport.y;
320 draw |= D_UP;
322 int tmp;
324 Sprite2D *spr = core->GetScrollCursorSprite(0,0);
326 tmp = spr->Width;
327 //tmp = core->ArrowSprites[0]->Width;
329 if (p.x>viewport.x+viewport.w-tmp) {
330 p.x=viewport.x+viewport.w;//-tmp;
331 draw |= D_RIGHT;
334 tmp = spr->Height;
335 //tmp = core->ArrowSprites[0]->Height;
337 if (p.y>viewport.y+viewport.h-tmp) {
338 p.y=viewport.y+viewport.h;//-tmp;
339 draw |= D_BOTTOM;
341 if (arrow_orientations[draw]>=0) {
342 video->BlitGameSprite( core->GetScrollCursorSprite(arrow_orientations[draw], 0), p.x+screen.x, p.y+screen.y, 0, black, NULL);
346 /** Draws the Control on the Output Display */
347 void GameControl::Draw(unsigned short x, unsigned short y)
349 bool update_scripts = !(DialogueFlags & DF_FREEZE_SCRIPTS);
351 Game* game = core->GetGame();
352 if (!game)
353 return;
355 if (((short) Width) <=0 || ((short) Height) <= 0) {
356 return;
359 if (Owner->Visible!=WINDOW_VISIBLE) {
360 return;
363 Video* video = core->GetVideoDriver();
364 Region viewport = video->GetViewport();
365 if (moveX || moveY) {
366 viewport.x += moveX;
367 viewport.y += moveY;
368 Point mapsize = core->GetGame()->GetCurrentArea()->TMap->GetMapSize();
369 if ( viewport.x < 0 )//if we are at top of the map
370 viewport.x = 0;
371 else if ( (viewport.x + viewport.w) >= mapsize.x) //if we are at the bottom
372 viewport.x = mapsize.x - viewport.w;
374 if ( viewport.y < 0 ) //if we are at the left of the map
375 viewport.y = 0;
376 else if ( (viewport.y + viewport.h ) >= mapsize.y ) //if we are at the right
377 viewport.y = mapsize.y - viewport.h;
379 // override any existing viewport moves which may be in progress
380 core->timer->SetMoveViewPort( viewport.x, viewport.y, 0, false );
381 // move it directly ourselves, since we might be paused
382 video->MoveViewportTo( viewport.x, viewport.y );
384 Region screen( x + XPos, y + YPos, Width, Height );
385 Map* area = game->GetCurrentArea( );
386 if (!area) {
387 video->DrawRect( screen, blue, true );
388 return;
390 video->DrawRect( screen, black, true );
392 // setup outlines
393 InfoPoint *i;
394 unsigned int idx;
395 for (idx = 0; (i = area->TMap->GetInfoPoint( idx )); idx++) {
396 i->Highlight = false;
397 if (overInfoPoint == i && target_mode) {
398 if (i->VisibleTrap(0)) {
399 i->outlineColor = green;
400 i->Highlight = true;
401 continue;
404 if (i->VisibleTrap(DebugFlags & DEBUG_SHOW_INFOPOINTS)) {
405 i->outlineColor = red; // traps
406 } else if (DebugFlags & DEBUG_SHOW_INFOPOINTS) {
407 i->outlineColor = blue; // debug infopoints
408 } else {
409 continue;
411 i->Highlight = true;
414 Door *d;
415 for (idx = 0; (d = area->TMap->GetDoor( idx )); idx++) {
416 d->Highlight = false;
417 if (overDoor == d) {
418 if (target_mode) {
419 if (d->VisibleTrap(0) || (d->Flags & DOOR_LOCKED)) {
420 // only highlight targettable doors
421 d->outlineColor = green;
422 d->Highlight = true;
423 continue;
425 } else if (!(d->Flags & DOOR_SECRET)) {
426 // mouse over, not in target mode, no secret door
427 d->outlineColor = cyan;
428 d->Highlight = true;
429 continue;
432 if (d->VisibleTrap(0)) {
433 d->outlineColor = red; // traps
434 } else if (d->Flags & DOOR_SECRET) {
435 if (DebugFlags & DEBUG_SHOW_DOORS || d->Flags & DOOR_FOUND) {
436 d->outlineColor = magenta; // found hidden door
437 } else {
438 // secret door is invisible
439 continue;
441 } else if (DebugFlags & DEBUG_SHOW_DOORS) {
442 d->outlineColor = cyan; // debug doors
443 } else {
444 continue;
446 d->Highlight = true;
449 Container *c;
450 for (idx = 0; (c = area->TMap->GetContainer( idx )); idx++) {
451 c->Highlight = false;
452 if (overContainer == c && target_mode) {
453 if (c->VisibleTrap(0) || (c->Flags & CONT_LOCKED)) {
454 // only highlight targettable containers
455 c->outlineColor = green;
456 c->Highlight = true;
457 continue;
459 } else if (overContainer == c) {
460 // mouse over, not in target mode
461 c->outlineColor = cyan;
462 c->Highlight = true;
463 continue;
465 if (c->VisibleTrap(0)) {
466 c->outlineColor = red; // traps
467 } else if (DebugFlags & DEBUG_SHOW_CONTAINERS) {
468 c->outlineColor = cyan; // debug containers
469 } else {
470 continue;
472 c->Highlight = true;
475 //drawmap should be here so it updates fog of war
476 area->DrawMap( screen );
477 game->DrawWeather(screen, update_scripts);
479 if (trackerID) {
480 Actor *actor = area->GetActorByGlobalID(trackerID);
482 if (actor) {
483 Actor **monsters = area->GetAllActorsInRadius(actor->Pos, GA_NO_DEAD, distance);
485 int i = 0;
486 while(monsters[i]) {
487 Actor *target = monsters[i++];
488 if (target->InParty) continue;
489 if (target->GetStat(IE_NOTRACKING)) continue;
490 DrawArrowMarker(screen, target->Pos, viewport);
492 delete monsters;
493 } else {
494 trackerID = 0;
498 if (ScreenFlags & SF_DISABLEMOUSE)
499 return;
500 Point p(lastMouseX, lastMouseY);
501 video->ConvertToGame( p.x, p.y );
503 // Draw selection rect
504 if (DrawSelectionRect) {
505 CalculateSelection( p );
506 video->DrawRect( SelectionRect, green, false, true );
509 // Show wallpolygons
510 if (DebugFlags & DEBUG_SHOW_INFOPOINTS) {
512 unsigned int count = area->GetWallCount();
513 for (unsigned int i = 0; i < count; ++i) {
514 Wall_Polygon* poly = area->GetWallGroup(i);
515 if (!poly) continue;
516 // yellow
517 Color c;
518 c.r = 0x7F;
519 c.g = 0x7F;
520 c.b = 0;
521 c.a = 0;
522 //if polygon is disabled, make it grey
523 if (poly->wall_flag&WF_DISABLED) {
524 c.b = 0x7F;
527 video->DrawPolyline( poly, c, true );
531 // Draw path
532 if (drawPath) {
533 PathNode* node = drawPath;
534 while (true) {
535 Point p( ( node-> x*16) + 8, ( node->y*12 ) + 6 );
536 if (!node->Parent) {
537 video->DrawCircle( p.x, p.y, 2, red );
538 } else {
539 short oldX = ( node->Parent-> x*16) + 8, oldY = ( node->Parent->y*12 ) + 6;
540 video->DrawLine( oldX, oldY, p.x, p.y, green );
542 if (!node->Next) {
543 video->DrawCircle( p.x, p.y, 2, green );
544 break;
546 node = node->Next;
550 // Draw lightmap
551 if (DebugFlags & DEBUG_SHOW_LIGHTMAP) {
552 Sprite2D* spr = area->LightMap->GetSprite2D();
553 video->BlitSprite( spr, 0, 0, true );
554 video->FreeSprite( spr );
555 Region point( p.x / 16, p.y / 12, 2, 2 );
556 video->DrawRect( point, red );
559 if (core->HasFeature(GF_ONSCREEN_TEXT) && DisplayText) {
560 core->GetFont(1)->Print(screen, (unsigned char *)DisplayText, core->InfoTextPalette, IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true);
561 if (update_scripts) {
562 // just replicating original engine behaviour
563 if (DisplayTextTime == 0) {
564 SetDisplayText((char *)NULL, 0);
565 } else {
566 DisplayTextTime--;
572 /** inherited from Control, GameControl doesn't need it */
573 int GameControl::SetText(const char* /*string*/, int /*pos*/)
575 return 0;
578 /** Key Press Event */
579 void GameControl::OnKeyPress(unsigned char Key, unsigned short /*Mod*/)
581 if (DialogueFlags&DF_IN_DIALOG) {
582 return;
584 unsigned int i;
585 Game* game = core->GetGame();
586 if (!game) return;
588 switch (Key) {
589 case '0':
590 game->SelectActor( NULL, false, SELECT_NORMAL );
591 i = game->GetPartySize(false)/2;
592 while(i--) {
593 SelectActor(i, true);
595 break;
596 case '-':
597 game->SelectActor( NULL, true, SELECT_NORMAL );
598 i = game->GetPartySize(false)/2;
599 while(i--) {
600 SelectActor(i, false);
602 break;
603 case '=':
604 SelectActor(-1);
605 break;
606 case '1':
607 case '2':
608 case '3':
609 case '4':
610 case '5':
611 case '6':
612 case '7':
613 case '8':
614 case '9':
615 SelectActor(Key-'0');
616 break;
617 default:
618 core->GetGame()->SetHotKey(toupper(Key));
619 break;
623 //Select (or deselect) a new actor (or actors)
624 void GameControl::SelectActor(int whom, int type)
626 Game* game = core->GetGame();
627 if (whom==-1) {
628 game->SelectActor( NULL, true, SELECT_NORMAL );
629 return;
632 /* doesn't fall through here */
633 Actor* actor = game->FindPC( whom );
634 if (!actor)
635 return;
637 if (type==0) {
638 game->SelectActor( actor, false, SELECT_NORMAL );
639 return;
641 if (type==1) {
642 game->SelectActor( actor, true, SELECT_NORMAL );
643 return;
646 bool was_selected = actor->IsSelected();
647 if (game->SelectActor( actor, true, SELECT_REPLACE ))
648 if (was_selected || (ScreenFlags & SF_ALWAYSCENTER)) {
649 ScreenFlags |= SF_CENTERONACTOR;
653 //Effect for the ctrl-r cheatkey (resurrect)
654 static EffectRef heal_ref={"CurrentHPModifier", NULL, -1};
655 static EffectRef damage_ref={"Damage", NULL, -1};
657 /** Key Release Event */
658 void GameControl::OnKeyRelease(unsigned char Key, unsigned short Mod)
660 unsigned int i;
661 Game* game = core->GetGame();
663 if (!game)
664 return;
666 if (DialogueFlags&DF_IN_DIALOG) {
667 if (Mod) return;
668 switch(Key) {
669 case '1':
670 case '2':
671 case '3':
672 case '4':
673 case '5':
674 case '6':
675 case '7':
676 case '8':
677 case '9':
679 TextArea *ta = core->GetMessageTextArea();
680 if (ta) {
681 ta->OnKeyPress(Key,Mod);
684 break;
686 return;
688 //cheatkeys with ctrl-
689 if (Mod & GEM_MOD_CTRL) {
690 if (!core->CheatEnabled()) {
691 return;
693 Map* area = game->GetCurrentArea( );
694 if (!area)
695 return;
696 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
697 Point p(lastMouseX, lastMouseY);
698 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
699 switch (Key) {
700 case 'f': //toggle full screen mode
701 core->GetVideoDriver()->ToggleFullscreenMode();
702 break;
703 case 'd': //disarm a trap
704 if (overInfoPoint) {
705 overInfoPoint->DetectTrap(256);
707 if (overContainer) {
708 if (overContainer->Trapped &&
709 !( overContainer->TrapDetected )) {
710 overContainer->TrapDetected = 1;
713 if (overDoor) {
714 if (overDoor->Trapped &&
715 !( overDoor->TrapDetected )) {
716 overDoor->TrapDetected = 1;
719 break;
720 case 'l': //play an animation (vvc/bam) over an actor
721 //the original engine was able to swap through all animations
722 if (lastActor) {
723 lastActor->AddAnimation("S056ICBL", 0, 0, 0);
725 break;
727 case 'c': //force cast a hardcoded spell
728 //caster is the last selected actor
729 //target is the door/actor currently under the pointer
730 if (game->selected.size() > 0) {
731 Actor *src = game->selected[0];
732 Scriptable *target = lastActor;
733 if (overDoor) {
734 target = overDoor;
736 if (target) {
737 src->CastSpell( TestSpell, target, false );
738 if (src->LastTarget) {
739 src->CastSpellEnd( TestSpell );
740 } else {
741 src->CastSpellPointEnd( TestSpell );
745 break;
747 case 'b': //draw a path to the target (pathfinder debug)
748 //You need to select an origin with ctrl-o first
749 if (drawPath) {
750 PathNode* nextNode = drawPath->Next;
751 PathNode* thisNode = drawPath;
752 while (true) {
753 delete( thisNode );
754 thisNode = nextNode;
755 if (!thisNode)
756 break;
757 nextNode = thisNode->Next;
760 drawPath = core->GetGame()->GetCurrentArea()->FindPath( pfs, p, lastActor?lastActor->size:1 );
762 break;
764 case 'o': //set up the origin for the pathfinder
765 // origin
766 pfs.x = lastMouseX;
767 pfs.y = lastMouseY;
768 core->GetVideoDriver()->ConvertToGame( pfs.x, pfs.y );
769 break;
770 case 'a': //switches through the avatar animations
771 if (lastActor) {
772 lastActor->GetNextAnimation();
774 break;
775 case 's': //switches through the stance animations
776 if (lastActor) {
777 lastActor->GetNextStance();
779 break;
780 case 'j': //teleports the selected actors
781 for (i = 0; i < game->selected.size(); i++) {
782 Actor* actor = game->selected[i];
783 MoveBetweenAreasCore(actor, core->GetGame()->CurrentArea, p, -1, true);
784 printf( "Teleported to %d, %d\n", p.x, p.y );
786 break;
788 case 'm': //prints a debug dump (ctrl-m in the original game too)
789 if (!lastActor) {
790 lastActor = area->GetActor( p, GA_DEFAULT);
792 if (!lastActor) {
793 // ValidTarget never returns immobile targets, making debugging a nightmare
794 // so if we don't have an actor, we make really really sure by checking manually
795 unsigned int count = area->GetActorCount(true);
796 while (count--) {
797 Actor *actor = area->GetActor(count, true);
798 if (actor->IsOver(p)) {
799 actor->DebugDump();
803 if (lastActor) {
804 lastActor->DebugDump();
805 break;
807 if (overDoor) {
808 overDoor->DebugDump();
809 break;
811 if (overContainer) {
812 overContainer->DebugDump();
813 break;
815 if (overInfoPoint) {
816 overInfoPoint->DebugDump();
817 break;
819 core->GetGame()->GetCurrentArea()->DebugDump(Mod & GEM_MOD_SHIFT);
820 break;
821 case 'v': //marks some of the map visited (random vision distance)
822 area->ExploreMapChunk( p, rand()%30, 1 );
823 break;
824 case 'x': // shows coordinates on the map
825 printf( "%s [%d.%d]\n", area->GetScriptName(), p.x, p.y );
826 break;
827 case 'g'://shows loaded areas and other game information
828 game->DebugDump();
829 break;
830 case 'i'://interact trigger (from the original game)
831 if (!lastActor) {
832 lastActor = area->GetActor( p, GA_DEFAULT);
834 if (lastActor && !(lastActor->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE)) {
835 Actor *target;
836 int i = game->GetPartySize(true);
837 if(i<2) break;
838 i=rand()%i;
841 target = game->GetPC(i, true);
842 if(target==lastActor) continue;
843 if(target->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE) continue;
845 char Tmp[40];
846 snprintf(Tmp,sizeof(Tmp),"Interact(\"%s\")",target->GetScriptName() );
847 lastActor->AddAction(GenerateAction(Tmp));
848 break;
850 while(i--);
852 break;
853 case 'r'://resurrects actor
854 if (!lastActor) {
855 lastActor = area->GetActor( p, GA_DEFAULT);
857 if (lastActor) {
858 Effect *fx = EffectQueue::CreateEffect(heal_ref, lastActor->GetBase(IE_MAXHITPOINTS), 0x30001, FX_DURATION_INSTANT_PERMANENT);
859 if (fx) {
860 core->ApplyEffect(fx, lastActor, lastActor);
863 break;
864 case 't'://advances time
865 // 7200 (one day) /24 (hours) == 300
866 game->AdvanceTime(300*AI_UPDATE_TIME);
867 //refresh gui here once we got it
868 break;
870 case 'q': //joins actor to the party
871 if (lastActor && !lastActor->InParty) {
872 lastActor->ClearActions();
873 lastActor->ClearPath();
874 char Tmp[40];
875 strncpy(Tmp,"JoinParty()",sizeof(Tmp) );
876 lastActor->AddAction( GenerateAction(Tmp) );
878 break;
879 case 'p': //center on actor
880 ScreenFlags|=SF_CENTERONACTOR;
881 ScreenFlags^=SF_ALWAYSCENTER;
882 break;
883 case 'k': //kicks out actor
884 if (lastActor && lastActor->InParty) {
885 lastActor->ClearActions();
886 lastActor->ClearPath();
887 char Tmp[40];
888 strncpy(Tmp,"LeaveParty()",sizeof(Tmp) );
889 lastActor->AddAction( GenerateAction(Tmp) );
891 break;
892 case 'y': //kills actor or all enemies
893 if (Mod & GEM_MOD_SHIFT) {
894 // mwahaha!
895 Effect *newfx;
896 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT);
897 Actor *victim;
898 for (int i = area->GetActorCount(0)-1; i >= 0; i--) {
899 victim = area->GetActor(i, 0);
900 if (victim->Modified[IE_EA] == EA_ENEMY) {
901 core->ApplyEffect(newfx, victim, victim);
904 delete newfx;
905 } else {
906 if (lastActor) {
907 //using action so the actor is killed
908 //correctly (synchronisation)
909 lastActor->ClearActions();
910 lastActor->ClearPath();
912 Effect *newfx;
913 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT);
914 core->ApplyEffect(newfx, lastActor, lastActor);
915 if (! (lastActor->GetInternalFlag() & IF_REALLYDIED)) {
916 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_ACID<<16, FX_DURATION_INSTANT_PERMANENT);
917 core->ApplyEffect(newfx, lastActor, lastActor);
918 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_CRUSHING<<16, FX_DURATION_INSTANT_PERMANENT);
919 core->ApplyEffect(newfx, lastActor, lastActor);
921 delete newfx;
922 } else if (overContainer) {
923 overContainer->SetContainerLocked(0);
924 } else if (overDoor) {
925 overDoor->SetDoorLocked(0,0);
928 break;
929 case 'z': //shift through the avatar animations backward
930 if (lastActor) {
931 lastActor->GetPrevAnimation();
933 break;
934 case '1': //change paperdoll armour level
935 if (! lastActor)
936 break;
937 lastActor->NewStat(IE_ARMOR_TYPE,1,MOD_ADDITIVE);
938 break;
939 case '4': //show all traps and infopoints
940 DebugFlags ^= DEBUG_SHOW_INFOPOINTS;
941 printf("Show traps and infopoints %s\n", DebugFlags & DEBUG_SHOW_INFOPOINTS ? "ON" : "OFF");
942 break;
943 case '6': //show the lightmap
944 DebugFlags ^= DEBUG_SHOW_LIGHTMAP;
945 printf("Show lightmap %s\n", DebugFlags & DEBUG_SHOW_LIGHTMAP ? "ON" : "OFF");
946 break;
947 case '7': //toggles fog of war
948 core->FogOfWar ^= 1;
949 printf("Show Fog-Of-War: %s\n", core->FogOfWar & 1 ? "ON" : "OFF");
950 break;
951 case '8': //show searchmap over area
952 core->FogOfWar ^= 2;
953 printf("Show searchmap %s\n", core->FogOfWar & 2 ? "ON" : "OFF");
954 break;
955 default:
956 printf( "KeyRelease:%d - %d\n", Key, Mod );
957 break;
959 return; //return from cheatkeys
961 switch (Key) {
962 case 'h': //hard pause
963 if (DialogueFlags & DF_FREEZE_SCRIPTS) break;
964 //fallthrough
965 case ' ': //soft pause
966 DialogueFlags ^= DF_FREEZE_SCRIPTS;
967 if (DialogueFlags&DF_FREEZE_SCRIPTS) {
968 core->DisplayConstantString(STR_PAUSED,0xff0000);
969 SetDisplayText(STR_PAUSED, 0); // time 0 = removed instantly on unpause
970 } else {
971 core->DisplayConstantString(STR_UNPAUSED,0xff0000);
973 break;
974 case 'm':
975 core->GetGUIScriptEngine()->RunFunction("OpenMapWindow");
976 break;
977 case 'j':
978 core->GetGUIScriptEngine()->RunFunction("OpenJournalWindow");
979 break;
980 case 'i':
981 core->GetGUIScriptEngine()->RunFunction("OpenInventoryWindow");
982 break;
983 case 'r':
984 core->GetGUIScriptEngine()->RunFunction("OpenRecordsWindow");
985 break;
986 case 'q': //quicksave
987 QuickSave();
988 break;
989 case GEM_ALT: //alt key (shows containers)
990 DebugFlags &= ~DEBUG_SHOW_CONTAINERS;
991 break;
992 default:
993 break;
997 void GameControl::DisplayTooltip() {
998 Game* game = core->GetGame();
999 if (game) {
1000 Map* area = game->GetCurrentArea( );
1001 if (area) {
1002 Actor *actor = area->GetActorByGlobalID(lastActorID);
1003 if (actor && (actor->GetStat(IE_STATE_ID)&STATE_DEAD || actor->GetInternalFlag()&IF_JUSTDIED)) {
1004 // checking IF_JUSTDIED is kind of horrid, but seems necessary
1005 // no tooltips for dead actors!
1006 actor->SetOver( false );
1007 lastActorID = 0;
1008 actor = NULL;
1011 if (actor) {
1012 char *name = actor->GetName(-1);
1013 int hp = actor->GetStat(IE_HITPOINTS);
1014 int maxhp = actor->GetStat(IE_MAXHITPOINTS);
1016 char buffer[100];
1017 if (!core->TooltipBack) {
1018 // single-line tooltips without background (PS:T)
1019 if (actor->InParty) {
1020 snprintf(buffer, 100, "%s: %d/%d", name, hp, maxhp);
1021 } else {
1022 snprintf(buffer, 100, "%s", name);
1024 } else {
1025 // a guess at a neutral check
1026 bool neutral = actor->GetStat(IE_EA) == EA_NEUTRAL;
1027 // test for an injured string being present for this game
1028 int strindex = core->GetStringReference(STR_UNINJURED);
1029 // normal tooltips
1030 if (actor->InParty) {
1031 // in party: display hp
1032 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1033 } else if (neutral) {
1034 // neutral: display name only
1035 snprintf(buffer, 100, "%s", name);
1036 } else if (strindex == -1) {
1037 // non-neutral, not in party, no injured strings: display hp
1038 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1039 } else {
1040 // non-neutral, not in party: display injured string
1041 int strindex;
1042 char *injuredstring = NULL;
1043 // these boundaries are just a guess
1044 if (hp == maxhp) {
1045 strindex = STR_UNINJURED;
1046 } else if (hp > (maxhp*3)/4) {
1047 strindex = STR_INJURED1;
1048 } else if (hp > maxhp/2) {
1049 strindex = STR_INJURED2;
1050 } else if (hp > maxhp/3) {
1051 strindex = STR_INJURED3;
1052 } else {
1053 strindex = STR_INJURED4;
1055 strindex = core->GetStringReference(strindex);
1056 if (strindex != -1) {
1057 injuredstring = core->GetString(strindex, 0);
1060 if (!injuredstring) {
1061 // eek, where did the string go?
1062 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1063 } else {
1064 snprintf(buffer, 100, "%s\n%s", name, injuredstring);
1065 free(injuredstring);
1070 Point p = actor->Pos;
1071 core->GetVideoDriver()->ConvertToScreen( p.x, p.y );
1072 p.x += Owner->XPos + XPos;
1073 p.y += Owner->YPos + YPos;
1075 // hack to position text above PS:T actors
1076 if (!core->TooltipBack) p.y -= actor->size*50;
1078 // we should probably cope better with moving actors
1079 SetTooltip(buffer);
1080 core->DisplayTooltip(p.x, p.y, this);
1081 return;
1086 SetTooltip(NULL);
1087 core->DisplayTooltip(0, 0, NULL);
1088 return;
1091 //returns the appropriate cursor over an active region (trap, infopoint, travel region)
1092 int GameControl::GetCursorOverInfoPoint(InfoPoint *overInfoPoint)
1094 if (target_mode == TARGET_MODE_PICK) {
1095 if (overInfoPoint->VisibleTrap(0)) {
1096 return IE_CURSOR_TRAP;
1099 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1101 // traps always display a walk cursor?
1102 if (overInfoPoint->Type == ST_PROXIMITY) {
1103 return IE_CURSOR_WALK;
1105 return overInfoPoint->Cursor;
1108 //returns the appropriate cursor over a door
1109 int GameControl::GetCursorOverDoor(Door *overDoor)
1111 if (target_mode == TARGET_MODE_PICK) {
1112 if (overDoor->VisibleTrap(0)) {
1113 return IE_CURSOR_TRAP;
1115 if (overDoor->Flags & DOOR_LOCKED) {
1116 return IE_CURSOR_LOCK;
1119 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1121 return overDoor->Cursor;
1124 //returns the appropriate cursor over a container (or pile)
1125 int GameControl::GetCursorOverContainer(Container *overContainer)
1127 if (target_mode == TARGET_MODE_PICK) {
1128 if (overContainer->VisibleTrap(0)) {
1129 return IE_CURSOR_TRAP;
1131 if (overContainer->Flags & CONT_LOCKED) {
1132 return IE_CURSOR_LOCK2;
1135 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1137 return IE_CURSOR_TAKE;
1140 /** Mouse Over Event */
1141 void GameControl::OnMouseOver(unsigned short x, unsigned short y)
1143 if (ScreenFlags & SF_DISABLEMOUSE) {
1144 return;
1147 lastMouseX = x;
1148 lastMouseY = y;
1149 Point p( x,y );
1150 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
1151 if (MouseIsDown && ( !DrawSelectionRect )) {
1152 if (( abs( p.x - StartX ) > 5 ) || ( abs( p.y - StartY ) > 5 )) {
1153 DrawSelectionRect = true;
1156 Game* game = core->GetGame();
1157 if (!game) return;
1158 Map* area = game->GetCurrentArea( );
1159 if (!area) return;
1160 int nextCursor = area->GetCursor( p );
1161 //make the invisible area really invisible
1162 if (nextCursor == IE_CURSOR_INVALID) {
1163 Owner->Cursor = IE_CURSOR_BLOCKED;
1164 lastCursor = IE_CURSOR_BLOCKED;
1165 return;
1168 overInfoPoint = area->TMap->GetInfoPoint( p, true );
1169 if (overInfoPoint) {
1170 //nextCursor = overInfoPoint->Cursor;
1171 nextCursor = GetCursorOverInfoPoint(overInfoPoint);
1174 if (overDoor) {
1175 overDoor->Highlight = false;
1177 if (overContainer) {
1178 overContainer->Highlight = false;
1180 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
1181 if (lastActor) {
1182 lastActor->SetOver( false );
1185 overDoor = area->TMap->GetDoor( p );
1186 overContainer = area->TMap->GetContainer( p );
1188 if (!DrawSelectionRect) {
1189 if (overDoor) {
1190 nextCursor = GetCursorOverDoor(overDoor);
1193 if (overContainer) {
1194 nextCursor = GetCursorOverContainer(overContainer);
1197 Actor *prevActor = lastActor;
1198 lastActor = area->GetActor( p, target_types);
1199 if (lastActor != prevActor) {
1200 // we store prevActor so we can remove the tooltip on actor change
1201 // (maybe we should be checking this and actor movements every frame?)
1202 SetTooltip(NULL);
1203 core->DisplayTooltip(0, 0, this);
1206 if ((target_types & GA_NO_SELF) && lastActor ) {
1207 if (lastActor == core->GetFirstSelectedPC(false)) {
1208 lastActor=NULL;
1212 if (lastActor) {
1213 lastActorID = lastActor->globalID;
1214 lastActor->SetOver( true );
1215 ieDword type = lastActor->GetStat(IE_EA);
1216 if (type >= EA_EVILCUTOFF || type == EA_GOODBUTRED) {
1217 nextCursor = IE_CURSOR_ATTACK;
1218 } else if ( type > EA_CHARMED ) {
1219 nextCursor = IE_CURSOR_TALK;
1220 } else {
1221 nextCursor = IE_CURSOR_NORMAL;
1223 } else {
1224 lastActorID = 0;
1227 if (target_mode == TARGET_MODE_TALK) {
1228 nextCursor = IE_CURSOR_TALK;
1229 if (!lastActor) {
1230 nextCursor |= IE_CURSOR_GRAY;
1232 } else if (target_mode == TARGET_MODE_ATTACK) {
1233 nextCursor = IE_CURSOR_ATTACK;
1234 if (!lastActor && !overDoor && !overContainer) {
1235 nextCursor |= IE_CURSOR_GRAY;
1237 } else if (target_mode == TARGET_MODE_CAST) {
1238 nextCursor = IE_CURSOR_CAST;
1239 //point is always valid
1240 if (!(target_types & GA_POINT)) {
1241 if(!lastActor) {
1242 nextCursor |= IE_CURSOR_GRAY;
1245 } else if (target_mode == TARGET_MODE_DEFEND) {
1246 nextCursor = IE_CURSOR_DEFEND;
1247 if(!lastActor) {
1248 nextCursor |= IE_CURSOR_GRAY;
1250 } else if (target_mode == TARGET_MODE_PICK) {
1251 if (lastActor) {
1252 nextCursor = IE_CURSOR_PICK;
1253 } else {
1254 if (!overContainer && !overDoor && !overInfoPoint) {
1255 nextCursor = IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1258 goto end_function;
1261 if (lastActor) {
1262 switch (lastActor->GetStat(IE_EA)) {
1263 case EA_EVILCUTOFF:
1264 case EA_GOODCUTOFF:
1265 break;
1267 case EA_PC:
1268 case EA_FAMILIAR:
1269 case EA_ALLY:
1270 case EA_CONTROLLED:
1271 case EA_CHARMED:
1272 case EA_EVILBUTGREEN:
1273 if (target_types & GA_NO_ENEMY)
1274 nextCursor^=1;
1275 break;
1277 case EA_ENEMY:
1278 case EA_GOODBUTRED:
1279 if (target_types & GA_NO_ALLY)
1280 nextCursor^=1;
1281 break;
1282 default:
1283 if (!(target_types & GA_NO_NEUTRAL))
1284 nextCursor^=1;
1285 break;
1289 end_function:
1290 if (lastCursor != nextCursor) {
1291 Owner->Cursor = nextCursor;
1292 lastCursor = (unsigned char) nextCursor;
1296 #define SCROLL_BORDER 5
1298 /** Global Mouse Move Event */
1299 void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y)
1301 if (ScreenFlags & SF_DISABLEMOUSE) {
1302 return;
1305 if (Owner->Visible!=WINDOW_VISIBLE) {
1306 return;
1309 int mousescrollspd = core->GetMouseScrollSpeed();
1311 if (x <= SCROLL_BORDER)
1312 moveX = -mousescrollspd;
1313 else {
1314 if (x >= ( core->Width - SCROLL_BORDER ))
1315 moveX = mousescrollspd;
1316 else
1317 moveX = 0;
1319 if (y <= SCROLL_BORDER)
1320 moveY = -mousescrollspd;
1321 else {
1322 if (y >= ( core->Height - SCROLL_BORDER ))
1323 moveY = mousescrollspd;
1324 else
1325 moveY = 0;
1328 if (moveX != 0 || moveY != 0) {
1329 scrolling = true;
1330 } else if (scrolling) {
1331 scrolling = false;
1333 Video* video = core->GetVideoDriver();
1334 video->SetDragCursor(NULL);
1338 void GameControl::UpdateScrolling() {
1339 if (!scrolling) return;
1341 int mousescrollspd = core->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-?
1342 Video* video = core->GetVideoDriver();
1344 if (moveX == mousescrollspd && moveY == 0) { // right
1345 video->SetDragCursor(core->GetScrollCursorSprite(0,numScrollCursor));
1346 } else if (moveX == mousescrollspd && moveY == -mousescrollspd) { // upper right
1347 video->SetDragCursor(core->GetScrollCursorSprite(1,numScrollCursor));
1348 } else if (moveX == 0 && moveY == -mousescrollspd) { // up
1349 video->SetDragCursor(core->GetScrollCursorSprite(2,numScrollCursor));
1350 } else if (moveX == -mousescrollspd && moveY == -mousescrollspd) { // upper left
1351 video->SetDragCursor(core->GetScrollCursorSprite(3,numScrollCursor));
1352 } else if (moveX == -mousescrollspd && moveY == 0) { // left
1353 video->SetDragCursor(core->GetScrollCursorSprite(4,numScrollCursor));
1354 } else if (moveX == -mousescrollspd && moveY == mousescrollspd) { // bottom left
1355 video->SetDragCursor(core->GetScrollCursorSprite(5,numScrollCursor));
1356 } else if (moveX == 0 && moveY == mousescrollspd) { // bottom
1357 video->SetDragCursor(core->GetScrollCursorSprite(6,numScrollCursor));
1358 } else if (moveX == mousescrollspd && moveY == mousescrollspd) { // bottom right
1359 video->SetDragCursor(core->GetScrollCursorSprite(7,numScrollCursor));
1362 numScrollCursor = (numScrollCursor+1) % 15;
1365 //generate action code for source actor to try to attack a target
1366 void GameControl::TryToAttack(Actor *source, Actor *tgt)
1368 char Tmp[40];
1370 source->ClearPath();
1371 source->ClearActions();
1372 strncpy(Tmp,"NIDSpecial3()",sizeof(Tmp) );
1373 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1376 //generate action code for source actor to try to defend a target
1377 void GameControl::TryToDefend(Actor *source, Actor *tgt)
1379 char Tmp[40];
1381 source->ClearPath();
1382 source->ClearActions();
1383 strncpy(Tmp,"NIDSpecial4()",sizeof(Tmp) );
1384 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1387 //generate action code for source actor to try to pick pockets of a target
1388 //The -1 flag is a placeholder for dynamic target IDs
1389 void GameControl::TryToPick(Actor *source, Actor *tgt)
1391 char Tmp[40];
1393 source->ClearPath();
1394 source->ClearActions();
1395 strncpy(Tmp,"PickPockets([-1])", sizeof(Tmp) );
1396 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1399 //generate action code for source actor to try to pick a lock/disable trap on a door
1400 void GameControl::TryToPick(Actor *source, Door *tgt)
1402 char Tmp[40];
1404 source->ClearPath();
1405 source->ClearActions();
1406 if (tgt->Trapped && tgt->TrapDetected) {
1407 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1408 } else {
1409 snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() );
1411 source->AddAction( GenerateAction( Tmp ) );
1414 //generate action code for source actor to try to pick a lock/disable trap on a container
1415 void GameControl::TryToPick(Actor *source, Container *tgt)
1417 char Tmp[40];
1419 source->ClearPath();
1420 source->ClearActions();
1421 if (tgt->Trapped && tgt->TrapDetected) {
1422 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1423 } else {
1424 snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() );
1426 source->AddAction( GenerateAction( Tmp ) );
1429 //generate action code for source actor to try to disable trap (only trap type active regions)
1430 void GameControl::TryToDisarm(Actor *source, InfoPoint *tgt)
1432 if (tgt->Type!=ST_PROXIMITY) return;
1434 char Tmp[40];
1436 source->ClearPath();
1437 source->ClearActions();
1438 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1439 source->AddAction( GenerateAction( Tmp ) );
1442 //generate action code for source actor to try to force open lock on a door/container
1443 void GameControl::TryToBash(Actor *source, Scriptable *tgt)
1445 char Tmp[40];
1447 source->ClearPath();
1448 source->ClearActions();
1449 snprintf(Tmp, sizeof(Tmp), "Attack(\"%s\")", tgt->GetScriptName() );
1450 source->AddAction( GenerateAction( Tmp ) );
1453 //generate action code for source actor to use item/cast spell on a point
1454 void GameControl::TryToCast(Actor *source, const Point &tgt)
1456 char Tmp[40];
1458 if (!spellCount) {
1459 target_mode = TARGET_MODE_NONE;
1460 return; //not casting or using an own item
1462 source->ClearPath();
1463 source->ClearActions();
1465 spellCount--;
1466 if (spellOrItem>=0) {
1467 sprintf(Tmp, "NIDSpecial8()");
1468 } else {
1469 //using item on target
1470 sprintf(Tmp, "NIDSpecial7()");
1472 Action* action = GenerateAction( Tmp );
1473 action->pointParameter=tgt;
1474 if (spellOrItem>=0)
1476 CREMemorizedSpell *si;
1477 //spell casting at target
1478 si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex);
1479 if (!si)
1481 target_mode = TARGET_MODE_NONE;
1482 return;
1484 sprintf(action->string0Parameter,"%.8s",si->SpellResRef);
1486 else
1488 action->int0Parameter=spellSlot;
1489 action->int1Parameter=spellIndex;
1491 source->AddAction( action );
1492 if (!spellCount) {
1493 target_mode = TARGET_MODE_NONE;
1497 //generate action code for source actor to use item/cast spell on another actor
1498 void GameControl::TryToCast(Actor *source, Actor *tgt)
1500 char Tmp[40];
1502 if (!spellCount) {
1503 target_mode = TARGET_MODE_NONE;
1504 return; //not casting or using an own item
1506 source->ClearPath();
1507 source->ClearActions();
1509 spellCount--;
1510 if (spellOrItem>=0) {
1511 sprintf(Tmp, "NIDSpecial6()");
1512 } else {
1513 //using item on target
1514 sprintf(Tmp, "NIDSpecial5()");
1516 Action* action = GenerateActionDirect( Tmp, tgt);
1517 if (spellOrItem>=0)
1519 CREMemorizedSpell *si;
1520 //spell casting at target
1521 si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex);
1522 if (!si)
1524 target_mode = TARGET_MODE_NONE;
1525 return;
1527 sprintf(action->string0Parameter,"%.8s",si->SpellResRef);
1529 else
1531 action->int0Parameter=spellSlot;
1532 action->int1Parameter=spellIndex;
1534 source->AddAction( action );
1535 if (!spellCount) {
1536 target_mode = TARGET_MODE_NONE;
1540 //generate action code for source actor to use talk to target actor
1541 void GameControl::TryToTalk(Actor *source, Actor *tgt)
1543 char Tmp[40];
1545 //Nidspecial1 is just an unused action existing in all games
1546 //(non interactive demo)
1547 //i found no fitting action which would emulate this kind of
1548 //dialog initation
1549 source->ClearPath();
1550 source->ClearActions();
1551 strncpy(Tmp,"NIDSpecial1()",sizeof(Tmp) );
1552 targetID = tgt->globalID; //this is a hack, but not so deadly
1553 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1556 //generate action code for actor appropriate for the target mode when the target is a container
1557 void GameControl::HandleContainer(Container *container, Actor *actor)
1559 char Tmp[256];
1561 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1562 //we'll get the container back from the coordinates
1563 TryToCast(actor, container->Pos);
1564 //Do not reset target_mode, TryToCast does it for us!!
1565 return;
1568 if (target_mode == TARGET_MODE_ATTACK) {
1569 TryToBash(actor, container);
1570 target_mode = TARGET_MODE_NONE;
1571 return;
1574 if ((target_mode == TARGET_MODE_PICK)) {
1575 TryToPick(actor, container);
1576 target_mode = TARGET_MODE_NONE;
1577 return;
1580 actor->ClearPath();
1581 actor->ClearActions();
1582 strncpy(Tmp,"UseContainer()",sizeof(Tmp) );
1583 core->SetCurrentContainer( actor, container);
1584 actor->AddAction( GenerateAction( Tmp) );
1587 //generate action code for actor appropriate for the target mode when the target is a door
1588 void GameControl::HandleDoor(Door *door, Actor *actor)
1590 char Tmp[256];
1592 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1593 //we'll get the door back from the coordinates
1594 Point *p = door->toOpen;
1595 Point *otherp = door->toOpen+1;
1596 if (Distance(*p,actor)>Distance(*otherp,actor)) {
1597 p=otherp;
1599 TryToCast(actor, *p);
1600 return;
1603 if (target_mode == TARGET_MODE_ATTACK) {
1604 TryToBash(actor, door);
1605 target_mode = TARGET_MODE_NONE ;
1606 return;
1609 if ( (target_mode == TARGET_MODE_PICK) || door->TrapDetected) {
1610 TryToPick(actor, door);
1611 target_mode = TARGET_MODE_NONE ;
1612 return;
1615 actor->ClearPath();
1616 actor->ClearActions();
1617 // it really isn't very nice to store a pointer in the actor like this
1618 actor->TargetDoor = door;
1619 // internal gemrb toggle door action hack - should we use UseDoor instead?
1620 sprintf( Tmp, "NIDSpecial9()" );
1621 actor->AddAction( GenerateAction( Tmp) );
1624 //generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel)
1625 bool GameControl::HandleActiveRegion(InfoPoint *trap, Actor * actor, Point &p)
1627 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1628 //we'll get the active region from the coordinates (if needed)
1629 TryToCast(actor, p);
1630 //don't bother with this region further
1631 return true;
1633 if ((target_mode == TARGET_MODE_PICK)) {
1634 TryToDisarm(actor, trap);
1635 target_mode = TARGET_MODE_NONE;
1636 return true;
1639 switch(trap->Type) {
1640 case ST_TRAVEL:
1641 actor->UseExit(true);
1642 return false;
1643 case ST_TRIGGER:
1644 //the importer shouldn't load the script
1645 //if it is unallowed anyway (though
1646 //deactivated scripts could be reactivated)
1647 //only the 'trapped' flag should be honoured
1648 //there. Here we have to check on the
1649 //reset trap and deactivated flags
1650 if (trap->Scripts[0]) {
1651 if (!(trap->Flags&TRAP_DEACTIVATED) ) {
1652 trap->LastTriggerObject = trap->LastTrigger = actor->GetID();
1653 trap->ImmediateEvent();
1654 //directly feeding the event, even if there are actions in the queue
1655 trap->Scripts[0]->Update();
1656 trap->ProcessActions(true);
1657 //if reset trap flag not set, deactivate it
1658 //hmm, better not, info triggers don't deactivate themselves on click
1659 //if (!(trap->Flags&TRAP_RESET)) {
1660 // trap->Flags|=TRAP_DEACTIVATED;
1663 } else {
1664 if (trap->overHeadText) {
1665 if (trap->textDisplaying != 1) {
1666 trap->textDisplaying = 1;
1667 trap->timeStartDisplaying = core->GetGame()->Ticks;
1668 DisplayString( trap );
1672 if (trap->Flags&TRAP_USEPOINT) {
1673 //overriding the target point
1674 p = trap->UsePoint;
1675 return false;
1677 return true;
1678 default:;
1680 return false;
1682 /** Mouse Button Down */
1683 void GameControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short Button,
1684 unsigned short /*Mod*/)
1686 if (ScreenFlags&SF_DISABLEMOUSE)
1687 return;
1689 short px=x;
1690 short py=y;
1691 DoubleClick = false;
1692 switch(Button)
1694 case GEM_MB_SCRLUP:
1695 OnSpecialKeyPress(GEM_UP);
1696 break;
1697 case GEM_MB_SCRLDOWN:
1698 OnSpecialKeyPress(GEM_DOWN);
1699 break;
1700 case GEM_MB_ACTION|GEM_MB_DOUBLECLICK:
1701 DoubleClick = true;
1702 case GEM_MB_ACTION:
1703 core->GetVideoDriver()->ConvertToGame( px, py );
1704 MouseIsDown = true;
1705 SelectionRect.x = px;
1706 SelectionRect.y = py;
1707 StartX = px;
1708 StartY = py;
1709 SelectionRect.w = 0;
1710 SelectionRect.h = 0;
1713 /** Mouse Button Up */
1714 void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short Button,
1715 unsigned short /*Mod*/)
1717 unsigned int i;
1718 char Tmp[256];
1720 if (ScreenFlags & SF_DISABLEMOUSE) {
1721 return;
1723 //heh, i found no better place
1724 core->CloseCurrentContainer();
1726 MouseIsDown = false;
1727 Point p(x,y);
1728 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
1729 Game* game = core->GetGame();
1730 Map* area = game->GetCurrentArea( );
1732 if (DrawSelectionRect) {
1733 Actor** ab;
1734 unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
1735 for (i = 0; i < highlighted.size(); i++)
1736 highlighted[i]->SetOver( false );
1737 highlighted.clear();
1738 game->SelectActor( NULL, false, SELECT_NORMAL );
1739 if (count != 0) {
1740 for (i = 0; i < count; i++) {
1741 // FIXME: should call handler only once
1742 game->SelectActor( ab[i], true, SELECT_NORMAL );
1745 free( ab );
1746 DrawSelectionRect = false;
1747 return;
1750 //hidden actors are not selectable by clicking on them
1751 Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_HIDDEN);
1752 if (Button == GEM_MB_MENU) {
1753 if (actor) {
1754 //from GSUtils
1755 DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE);
1756 return;
1758 core->GetDictionary()->SetAt( "MenuX", x );
1759 core->GetDictionary()->SetAt( "MenuY", y );
1760 core->GetGUIScriptEngine()->RunFunction( "OpenFloatMenuWindow" );
1761 return;
1764 if (Button != GEM_MB_ACTION) {
1765 return;
1768 if (!actor && ( game->selected.size() > 0 )) {
1769 if (overDoor) {
1770 HandleDoor(overDoor, core->GetFirstSelectedPC(false));
1771 return;
1773 if (overContainer) {
1774 HandleContainer(overContainer, core->GetFirstSelectedPC(false));
1775 return;
1777 if (overInfoPoint) {
1778 if (HandleActiveRegion(overInfoPoint, core->GetFirstSelectedPC(false), p)) {
1779 return;
1783 //just a single actor, no formation
1784 if (game->selected.size()==1) {
1785 //the player is using an item or spell on the ground
1786 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1787 TryToCast(core->GetFirstSelectedPC(false), p);
1788 return;
1791 actor=game->selected[0];
1792 actor->ClearPath();
1793 actor->ClearActions();
1794 CreateMovement(actor, p);
1796 if (DoubleClick) {
1797 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
1798 } else {
1799 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
1802 actor->AddAction( GenerateAction( Tmp) );
1804 //p is a searchmap travel region
1805 if ( actor->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) {
1806 sprintf( Tmp, "NIDSpecial2()" );
1807 actor->AddAction( GenerateAction( Tmp) );
1809 return;
1812 // construct a sorted party
1813 // TODO: this is beyond horrible, help
1814 std::vector<Actor *> party;
1815 // first, from the actual party
1816 for (int idx = 0; idx < game->GetPartySize(false); idx++) {
1817 Actor *pc = game->FindPC(idx + 1);
1818 if (!pc) continue;
1820 for (unsigned int j = 0; j < game->selected.size(); j++) {
1821 if (game->selected[j] == pc) {
1822 party.push_back(pc);
1827 // then, anything else we selected
1828 for (i = 0; i < game->selected.size(); i++) {
1829 bool found = false;
1830 for (unsigned int j = 0; j < party.size(); j++) {
1831 if (game->selected[i] == party[j]) {
1832 found = true;
1833 break;
1836 if (!found) party.push_back(game->selected[i]);
1839 //party formation movement
1840 Point src = party[0]->Pos;
1841 for(i = 0; i < party.size(); i++) {
1842 actor = party[i];
1843 actor->ClearPath();
1844 actor->ClearActions();
1845 MoveToPointFormation(actor, i, src, p);
1848 //p is a searchmap travel region
1849 if ( party[0]->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) {
1850 sprintf( Tmp, "NIDSpecial2()" );
1851 party[0]->AddAction( GenerateAction( Tmp) );
1853 return;
1855 if (!actor) return;
1856 //we got an actor past this point
1857 DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE);
1859 PerformActionOn(actor);
1862 void GameControl::PerformActionOn(Actor *actor) {
1863 Game* game = core->GetGame();
1864 unsigned int i;
1866 //determining the type of the clicked actor
1867 ieDword type;
1869 type = actor->GetStat(IE_EA);
1870 if ( type >= EA_EVILCUTOFF || type == EA_GOODBUTRED ) {
1871 type = ACT_ATTACK; //hostile
1872 } else if ( type > EA_CHARMED ) {
1873 type = ACT_TALK; //neutral
1874 } else {
1875 type = ACT_NONE; //party
1878 if (target_mode == TARGET_MODE_ATTACK) {
1879 type = ACT_ATTACK;
1880 } else if (target_mode == TARGET_MODE_TALK) {
1881 type = ACT_TALK;
1882 } else if (target_mode == TARGET_MODE_CAST) {
1883 type = ACT_CAST;
1884 } else if (target_mode == TARGET_MODE_DEFEND) {
1885 type = ACT_DEFEND;
1886 } else if (target_mode == TARGET_MODE_PICK) {
1887 type = ACT_THIEVING;
1890 //we shouldn't zero this for two reasons in case of spell or item
1891 //1. there could be multiple targets
1892 //2. the target mode is important
1893 if (!(target_mode == TARGET_MODE_CAST) || !spellCount) {
1894 target_mode = TARGET_MODE_NONE;
1897 switch (type) {
1898 case ACT_NONE: //none
1899 if (actor->InParty)
1900 SelectActor( actor->InParty );
1901 else if (actor->GetStat(IE_EA) <= EA_CHARMED) {
1902 /*let's select charmed/summoned creatures
1903 EA_CHARMED is the maximum value known atm*/
1904 core->GetGame()->SelectActor(actor, true, SELECT_REPLACE) ;
1906 break;
1907 case ACT_TALK:
1908 //talk (first selected talks)
1909 if (game->selected.size()) {
1910 //if we are in PST modify this to NO!
1911 Actor *source;
1912 if (core->HasFeature(GF_PROTAGONIST_TALKS) ) {
1913 source = game->GetPC(0, false); //protagonist
1914 } else {
1915 source = core->GetFirstSelectedPC(false);
1917 // only party members can start conversations
1918 if (source) {
1919 TryToTalk(source, actor);
1922 break;
1923 case ACT_ATTACK:
1924 //all of them attacks the red circled actor
1925 for(i=0;i<game->selected.size();i++) {
1926 TryToAttack(game->selected[i], actor);
1928 break;
1929 case ACT_CAST: //cast on target or use item on target
1930 if (game->selected.size()==1) {
1931 Actor *source;
1932 source = core->GetFirstSelectedPC(false);
1933 if(source) {
1934 TryToCast(source, actor);
1937 break;
1938 case ACT_DEFEND:
1939 for(i=0;i<game->selected.size();i++) {
1940 TryToDefend(game->selected[i], actor);
1942 break;
1943 case ACT_THIEVING:
1944 if (game->selected.size()==1) {
1945 Actor *source;
1946 source = core->GetFirstSelectedPC(false);
1947 if(source) {
1948 TryToPick(source, actor);
1951 break;
1954 /** Special Key Press */
1955 void GameControl::OnSpecialKeyPress(unsigned char Key)
1957 if (DialogueFlags&DF_IN_DIALOG) {
1958 switch(Key) {
1959 case GEM_RETURN:
1960 //simulating the continue/end button pressed
1961 core->GetGUIScriptEngine()->RunFunction("CloseContinueWindow");
1962 break;
1964 return; //don't accept keys in dialog
1966 Region Viewport = core->GetVideoDriver()->GetViewport();
1967 Game *game = core->GetGame();
1968 Point mapsize = game->GetCurrentArea()->TMap->GetMapSize();
1969 int partysize = game->GetPartySize(false);
1970 int pm;
1971 char tmpstr[10];
1973 switch (Key) {
1974 case GEM_LEFT:
1975 if (Viewport.x > 63)
1976 Viewport.x -= 64;
1977 else
1978 Viewport.x = 0;
1979 break;
1980 case GEM_UP:
1981 if (Viewport.y > 63)
1982 Viewport.y -= 64;
1983 else
1984 Viewport.y = 0;
1985 break;
1986 case GEM_DOWN:
1987 if (Viewport.y + Viewport.h + 64 < mapsize.y)
1988 Viewport.y += 64;
1989 else {
1990 Viewport.y = mapsize.y - Viewport.h;
1991 if (Viewport.y<0) Viewport.y=0;
1993 break;
1994 case GEM_RIGHT:
1995 if (Viewport.x + Viewport.w + 64 < mapsize.x)
1996 Viewport.x += 64;
1997 else {
1998 Viewport.x = mapsize.x - Viewport.w;
1999 if (Viewport.x<0) Viewport.x=0;
2001 break;
2002 case GEM_ALT:
2003 DebugFlags |= DEBUG_SHOW_CONTAINERS;
2004 return;
2005 case GEM_TAB:
2006 // show partymember hp/maxhp as overhead text
2007 for (pm=0; pm < partysize; pm++) {
2008 Actor *pc = game->GetPC(pm, true);
2009 if (!pc) continue;
2010 memset(tmpstr, 0, 10);
2011 snprintf(tmpstr, 10, "%d/%d", pc->Modified[IE_HITPOINTS], pc->Modified[IE_MAXHITPOINTS]);
2012 pc->DisplayHeadText(strdup(tmpstr));
2014 return;
2015 case GEM_MOUSEOUT:
2016 moveX = 0;
2017 moveY = 0;
2018 return;
2019 case GEM_ESCAPE:
2020 core->GetGUIScriptEngine()->RunFunction("EmptyControls");
2021 core->SetEventFlag(EF_ACTION);
2022 return;
2023 case GEM_PGUP:
2024 core->GetGUIScriptEngine()->RunFunction("OnIncreaseSize");
2025 return;
2026 case GEM_PGDOWN:
2027 core->GetGUIScriptEngine()->RunFunction("OnDecreaseSize");
2028 return;
2029 default:
2030 return;
2032 if (ScreenFlags & SF_LOCKSCROLL) {
2033 moveX = 0;
2034 moveY = 0;
2036 else {
2037 // override any existing viewport moves which may be in progress
2038 core->timer->SetMoveViewPort( Viewport.x, Viewport.y, 0, false );
2039 // move it directly ourselves, since we might be paused
2040 core->GetVideoDriver()->MoveViewportTo( Viewport.x, Viewport.y );
2044 void GameControl::CalculateSelection(const Point &p)
2046 unsigned int i;
2047 Game* game = core->GetGame();
2048 Map* area = game->GetCurrentArea( );
2049 if (DrawSelectionRect) {
2050 if (p.x < StartX) {
2051 SelectionRect.w = StartX - p.x;
2052 SelectionRect.x = p.x;
2053 } else {
2054 SelectionRect.x = StartX;
2055 SelectionRect.w = p.x - StartX;
2057 if (p.y < StartY) {
2058 SelectionRect.h = StartY - p.y;
2059 SelectionRect.y = p.y;
2060 } else {
2061 SelectionRect.y = StartY;
2062 SelectionRect.h = p.y - StartY;
2064 Actor** ab;
2065 unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
2066 for (i = 0; i < highlighted.size(); i++)
2067 highlighted[i]->SetOver( false );
2068 highlighted.clear();
2069 if (count != 0) {
2070 for (i = 0; i < count; i++) {
2071 ab[i]->SetOver( true );
2072 highlighted.push_back( ab[i] );
2075 free( ab );
2076 } else {
2077 Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_ENEMY);
2078 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
2079 if (lastActor)
2080 lastActor->SetOver( false );
2081 if (!actor) {
2082 lastActorID = 0;
2083 } else {
2084 lastActorID = actor->globalID;
2085 actor->SetOver( true );
2090 void GameControl::SetCutSceneMode(bool active)
2092 if (active) {
2093 ScreenFlags |= (SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE);
2094 moveX = 0;
2095 moveY = 0;
2096 } else {
2097 ScreenFlags &= ~(SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE);
2101 //Change game window geometries when a new window gets deactivated
2102 void GameControl::HandleWindowHide(const char *WindowName, const char *WindowPosition)
2104 Variables* dict = core->GetDictionary();
2105 ieDword index;
2107 if (dict->Lookup( WindowName, index )) {
2108 if (index != (ieDword) -1) {
2109 Window* w = core->GetWindow( (unsigned short) index );
2110 if (w) {
2111 core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE );
2112 if (dict->Lookup( WindowPosition, index )) {
2113 ResizeDel( w, index );
2115 return;
2117 printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED);
2118 printf("%s:%u\n",WindowName, index);
2123 //Hide all other windows on the GUI (gamecontrol is not hidden by this)
2124 int GameControl::HideGUI()
2126 //hidegui is in effect
2127 if (!(ScreenFlags&SF_GUIENABLED) ) {
2128 return 0;
2130 //no gamecontrol visible
2131 if (Owner->Visible == WINDOW_INVISIBLE ) {
2132 return 0;
2134 ScreenFlags &=~SF_GUIENABLED;
2135 HandleWindowHide("PortraitWindow", "PortraitPosition");
2136 HandleWindowHide("OtherWindow", "OtherPosition");
2137 HandleWindowHide("TopWindow", "TopPosition");
2138 HandleWindowHide("OptionsWindow", "OptionsPosition");
2139 HandleWindowHide("MessageWindow", "MessagePosition");
2140 HandleWindowHide("ActionsWindow", "ActionsPosition");
2141 //FloatWindow doesn't affect gamecontrol, so it is special
2142 Variables* dict = core->GetDictionary();
2143 ieDword index;
2145 if (dict->Lookup( "FloatWindow", index )) {
2146 if (index != (ieDword) -1) {
2147 core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE );
2150 core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height );
2151 return 1;
2154 //Change game window geometries when a new window gets activated
2155 void GameControl::HandleWindowReveal(const char *WindowName, const char *WindowPosition)
2157 Variables* dict = core->GetDictionary();
2158 ieDword index;
2160 if (dict->Lookup( WindowName, index )) {
2161 if (index != (ieDword) -1) {
2162 Window* w = core->GetWindow( (unsigned short) index );
2163 if (w) {
2164 core->SetVisible( (unsigned short) index, WINDOW_VISIBLE );
2165 if (dict->Lookup( WindowPosition, index )) {
2166 ResizeAdd( w, index );
2168 return;
2170 printMessage("GameControl", "Invalid Window Index ", LIGHT_RED);
2171 printf("%s:%u\n",WindowName, index);
2176 //Reveal all windows on the GUI (including this one)
2177 int GameControl::UnhideGUI()
2179 if (ScreenFlags&SF_GUIENABLED) {
2180 return 0;
2183 ScreenFlags |= SF_GUIENABLED;
2184 // Unhide the gamecontrol window
2185 core->SetVisible( 0, WINDOW_VISIBLE );
2187 HandleWindowReveal("ActionsWindow", "ActionsPosition");
2188 HandleWindowReveal("MessageWindow", "MessagePosition");
2189 HandleWindowReveal("OptionsWindow", "OptionsPosition");
2190 HandleWindowReveal("TopWindow", "TopPosition");
2191 HandleWindowReveal("OtherWindow", "OtherPosition");
2192 HandleWindowReveal("PortraitWindow", "PortraitPosition");
2193 //the floatwindow is a special case
2194 Variables* dict = core->GetDictionary();
2195 ieDword index;
2197 if (dict->Lookup( "FloatWindow", index )) {
2198 if (index != (ieDword) -1) {
2199 Window* fw = core->GetWindow( (unsigned short) index );
2200 if (fw) {
2201 core->SetVisible( (unsigned short) index, WINDOW_VISIBLE );
2202 fw->Flags |=WF_FLOAT;
2203 core->SetOnTop( index );
2207 core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height );
2208 return 1;
2211 //a window got removed, so the GameControl gets enlarged
2212 void GameControl::ResizeDel(Window* win, int type)
2214 switch (type) {
2215 case 0: //Left
2216 if (LeftCount!=1) {
2217 printMessage("GameControl","More than one left window!\n",LIGHT_RED);
2219 LeftCount--;
2220 if (!LeftCount) {
2221 Owner->XPos -= win->Width;
2222 Owner->Width += win->Width;
2223 Width = Owner->Width;
2225 break;
2227 case 1: //Bottom
2228 if (BottomCount!=1) {
2229 printMessage("GameControl","More than one bottom window!\n",LIGHT_RED);
2231 BottomCount--;
2232 if (!BottomCount) {
2233 Owner->Height += win->Height;
2234 Height = Owner->Height;
2236 break;
2238 case 2: //Right
2239 if (RightCount!=1) {
2240 printMessage("GameControl","More than one right window!\n",LIGHT_RED);
2242 RightCount--;
2243 if (!RightCount) {
2244 Owner->Width += win->Width;
2245 Width = Owner->Width;
2247 break;
2249 case 3: //Top
2250 if (TopCount!=1) {
2251 printMessage("GameControl","More than one top window!\n",LIGHT_RED);
2253 TopCount--;
2254 if (!TopCount) {
2255 Owner->YPos -= win->Height;
2256 Owner->Height += win->Height;
2257 Height = Owner->Height;
2259 break;
2261 case 4: //BottomAdded
2262 BottomCount--;
2263 Owner->Height += win->Height;
2264 Height = Owner->Height;
2265 break;
2266 case 5: //Inactivating
2267 BottomCount--;
2268 Owner->Height += win->Height;
2269 Height = Owner->Height;
2270 break;
2274 //a window got added, so the GameControl gets shrunk
2275 //Owner is the GameControl's window
2276 //GameControl is the only control on that window
2277 void GameControl::ResizeAdd(Window* win, int type)
2279 switch (type) {
2280 case 0: //Left
2281 LeftCount++;
2282 if (LeftCount == 1) {
2283 Owner->XPos += win->Width;
2284 Owner->Width -= win->Width;
2285 Width = Owner->Width;
2287 break;
2289 case 1: //Bottom
2290 BottomCount++;
2291 if (BottomCount == 1) {
2292 Owner->Height -= win->Height;
2293 Height = Owner->Height;
2295 break;
2297 case 2: //Right
2298 RightCount++;
2299 if (RightCount == 1) {
2300 Owner->Width -= win->Width;
2301 Width = Owner->Width;
2303 break;
2305 case 3: //Top
2306 TopCount++;
2307 if (TopCount == 1) {
2308 Owner->YPos += win->Height;
2309 Owner->Height -= win->Height;
2310 Height = Owner->Height;
2312 break;
2314 case 4: //BottomAdded
2315 BottomCount++;
2316 Owner->Height -= win->Height;
2317 Height = Owner->Height;
2318 break;
2320 case 5: //Inactivating
2321 BottomCount++;
2322 Owner->Height -= win->Height;
2323 Height = 0;
2327 //Try to start dialogue between two actors (one of them could be inanimate)
2328 int GameControl::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgref)
2330 if (dlg) {
2331 delete dlg;
2332 dlg = NULL;
2335 PluginHolder<DialogMgr> dm(IE_DLG_CLASS_ID);
2336 dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true );
2337 dlg = dm->GetDialog();
2339 if (!dlg) {
2340 printMessage("GameControl", " ", LIGHT_RED);
2341 printf( "Cannot start dialog: %s\n", dlgref );
2342 return -1;
2345 strnlwrcpy(dlg->ResRef, dlgref, 8); //this isn't handled by GetDialog???
2347 //target is here because it could be changed when a dialog runs onto
2348 //and external link, we need to find the new target (whose dialog was
2349 //linked to)
2351 Actor *spe = (Actor *) spk;
2352 speakerID = spe->globalID;
2353 if (tgt->Type!=ST_ACTOR) {
2354 targetID=0xffff;
2355 //most likely this dangling object reference
2356 //won't cause problems, because trigger points don't
2357 //get destroyed during a dialog
2358 targetOB=tgt;
2359 spk->LastTalkedTo=0;
2360 } else {
2361 Actor *tar = (Actor *) tgt;
2362 speakerID = spe->globalID;
2363 targetID = tar->globalID;
2364 if (!originalTargetID) originalTargetID = tar->globalID;
2365 spe->LastTalkedTo=targetID;
2366 tar->LastTalkedTo=speakerID;
2369 //check if we are already in dialog
2370 if (DialogueFlags&DF_IN_DIALOG) {
2371 return 0;
2374 int si = dlg->FindFirstState( tgt );
2375 if (si < 0) {
2376 return -1;
2379 //we need GUI for dialogs
2380 UnhideGUI();
2382 //no exploring while in dialogue
2383 ScreenFlags |= SF_GUIENABLED|SF_DISABLEMOUSE|SF_LOCKSCROLL;
2384 DialogueFlags |= DF_IN_DIALOG;
2386 if (tgt->Type==ST_ACTOR) {
2387 Actor *tar = (Actor *) tgt;
2388 tar->DialogInterrupt();
2391 //allow mouse selection from dialog (even though screen is locked)
2392 core->GetVideoDriver()->SetMouseEnabled(true);
2393 core->timer->SetMoveViewPort( tgt->Pos.x, tgt->Pos.y, 0, true );
2394 //there are 3 bits, if they are all unset, the dialog freezes scripts
2395 if (!(dlg->Flags&7) ) {
2396 DialogueFlags |= DF_FREEZE_SCRIPTS;
2398 //opening control size to maximum, enabling dialog window
2399 core->GetGame()->SetControlStatus(CS_HIDEGUI, BM_NAND);
2400 core->GetGame()->SetControlStatus(CS_DIALOG, BM_OR);
2401 core->SetEventFlag(EF_PORTRAIT);
2402 return 0;
2405 /*try to break will only try to break it, false means unconditional stop*/
2406 void GameControl::EndDialog(bool try_to_break)
2408 if (try_to_break && (DialogueFlags&DF_UNBREAKABLE) ) {
2409 return;
2412 Actor *tmp = GetSpeaker();
2413 if (tmp) {
2414 tmp->LeaveDialog();
2416 speakerID = 0;
2417 if (targetID==0xffff) {
2418 targetOB->LeaveDialog();
2419 } else {
2420 tmp=GetTarget();
2421 if (tmp) {
2422 tmp->LeaveDialog();
2425 targetOB = NULL;
2426 targetID = 0;
2427 originalTargetID = 0;
2428 ds = NULL;
2429 if (dlg) {
2430 delete dlg;
2431 dlg = NULL;
2433 //restoring original size
2434 core->GetGame()->SetControlStatus(CS_DIALOG, BM_NAND);
2435 ScreenFlags &=~(SF_DISABLEMOUSE|SF_LOCKSCROLL);
2436 DialogueFlags = 0;
2437 core->SetEventFlag(EF_PORTRAIT);
2440 //translate section values (journal, solved, unsolved, user)
2441 static const int sectionMap[4]={4,1,2,0};
2443 void GameControl::DialogChoose(unsigned int choose)
2445 TextArea* ta = core->GetMessageTextArea();
2446 if (!ta) {
2447 printMessage("GameControl","Dialog aborted???",LIGHT_RED);
2448 EndDialog();
2449 return;
2452 Actor *speaker = GetSpeaker();
2453 if (!speaker) {
2454 printMessage("GameControl","Speaker gone???",LIGHT_RED);
2455 EndDialog();
2456 return;
2458 Actor *tgt;
2459 Scriptable *target;
2461 if (targetID!=0xffff) {
2462 tgt = GetTarget();
2463 target = tgt;
2464 } else {
2465 //risky!!!
2466 target = targetOB;
2467 tgt=NULL;
2469 if (!target) {
2470 printMessage("GameControl","Target gone???",LIGHT_RED);
2471 EndDialog();
2472 return;
2475 if (choose == (unsigned int) -1) {
2476 //increasing talkcount after top level condition was determined
2478 int si = dlg->FindFirstState( tgt );
2479 if (si<0) {
2480 EndDialog();
2481 return;
2484 if (tgt) {
2485 if (DialogueFlags&DF_TALKCOUNT) {
2486 DialogueFlags&=~DF_TALKCOUNT;
2487 tgt->TalkCount++;
2488 } else if (DialogueFlags&DF_INTERACT) {
2489 DialogueFlags&=~DF_INTERACT;
2490 tgt->InteractCount++;
2493 ds = dlg->GetState( si );
2494 } else {
2495 if (ds->transitionsCount <= choose) {
2496 return;
2499 DialogTransition* tr = ds->transitions[choose];
2501 ta->PopMinRow();
2503 if (tr->Flags&IE_DLG_TR_JOURNAL) {
2504 int Section = 0;
2505 if (tr->Flags&IE_DLG_UNSOLVED) {
2506 Section |= 1;
2508 if (tr->Flags&IE_DLG_SOLVED) {
2509 Section |= 2;
2511 if (core->GetGame()->AddJournalEntry(tr->journalStrRef, sectionMap[Section], tr->Flags>>16) ) {
2512 core->DisplayConstantString(STR_JOURNALCHANGE,0xffff00);
2513 char *string = core->GetString( tr->journalStrRef );
2514 //cutting off the strings at the first crlf
2515 char *poi = strchr(string,'\n');
2516 if (poi) {
2517 *poi='\0';
2519 core->DisplayString( string );
2520 free( string );
2524 if (tr->textStrRef != 0xffffffff) {
2525 //allow_zero is for PST (deionarra's text)
2526 core->DisplayStringName( (int) (tr->textStrRef), 0x8080FF, speaker, IE_STR_SOUND|IE_STR_SPEECH|IE_STR_ALLOW_ZERO);
2527 if (core->HasFeature( GF_DIALOGUE_SCROLLS )) {
2528 ta->AppendText( "", -1 );
2532 if (tr->actions.size()) {
2533 // does this belong here? we must clear actions somewhere before
2534 // we start executing them (otherwise queued actions interfere)
2535 // executing actions directly does not work, because dialog
2536 // needs to end before final actions are executed due to
2537 // actions making new dialogs!
2538 if (target->Type == ST_ACTOR) ((Movable *)target)->ClearPath(); // fuzzie added this
2539 target->ClearActions();
2541 for (unsigned int i = 0; i < tr->actions.size(); i++) {
2542 target->AddAction(tr->actions[i]);
2543 //GameScript::ExecuteAction( target, action );
2547 int final_dialog = tr->Flags & IE_DLG_TR_FINAL;
2549 if (final_dialog) {
2550 ta->SetMinRow( false );
2551 EndDialog();
2554 // all dialog actions must be executed immediately
2555 target->ProcessActions(true);
2556 // (do not clear actions - final actions can involve waiting/moving)
2558 if (final_dialog) {
2559 return;
2562 //displaying dialog for selected option
2563 int si = tr->stateIndex;
2564 //follow external linkage, if required
2565 if (tr->Dialog[0] && strnicmp( tr->Dialog, dlg->ResRef, 8 )) {
2566 //target should be recalculated!
2567 tgt = NULL;
2568 if (originalTargetID) {
2569 // always try original target first (sometimes there are multiple
2570 // actors with the same dialog in an area, we want to pick the one
2571 // we were talking to)
2572 tgt = GetActorByGlobalID(originalTargetID);
2573 if (tgt && strnicmp( tgt->GetDialog(GD_NORMAL), tr->Dialog, 8 ) != 0) {
2574 tgt = NULL;
2577 if (!tgt) {
2578 // then just search the current area for an actor with the dialog
2579 tgt = target->GetCurrentArea()->GetActorByDialog(tr->Dialog);
2581 target = tgt;
2582 if (!target) {
2583 printMessage("Dialog","Can't redirect dialog\n",YELLOW);
2584 ta->SetMinRow( false );
2585 EndDialog();
2586 return;
2588 targetID = tgt->globalID;
2589 // we have to make a backup, tr->Dialog is freed
2590 ieResRef tmpresref;
2591 strnlwrcpy(tmpresref,tr->Dialog, 8);
2592 if (target->GetInternalFlag()&IF_NOINT) {
2593 // this whole check moved out of InitDialog by fuzzie, see comments
2594 // for the IF_NOINT check in BeginDialog
2595 core->DisplayConstantString(STR_TARGETBUSY,0xff0000);
2596 ta->SetMinRow( false );
2597 EndDialog();
2598 return;
2600 int ret = InitDialog( speaker, target, tmpresref);
2601 if (ret<0) {
2602 // error was displayed by InitDialog
2603 ta->SetMinRow( false );
2604 EndDialog();
2605 return;
2608 ds = dlg->GetState( si );
2609 if (!ds) {
2610 printMessage("Dialog","Can't find next dialog\n",YELLOW);
2611 ta->SetMinRow( false );
2612 EndDialog();
2613 return;
2616 //displaying npc text
2617 core->DisplayStringName( ds->StrRef, 0x70FF70, target, IE_STR_SOUND|IE_STR_SPEECH);
2618 //adding a gap between options and npc text
2619 ta->AppendText("",-1);
2620 int i;
2621 int idx = 0;
2622 ta->SetMinRow( true );
2623 //first looking for a 'continue' opportunity, the order is descending (a la IE)
2624 unsigned int x = ds->transitionsCount;
2625 while(x--) {
2626 if (ds->transitions[x]->Flags & IE_DLG_TR_FINAL) {
2627 continue;
2629 if (ds->transitions[x]->textStrRef != 0xffffffff) {
2630 continue;
2632 if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) {
2633 if (ds->transitions[x]->condition &&
2634 !ds->transitions[x]->condition->Evaluate(target)) {
2635 continue;
2638 core->GetDictionary()->SetAt("DialogOption",x);
2639 DialogueFlags |= DF_OPENCONTINUEWINDOW;
2640 goto end_of_choose;
2642 for (x = 0; x < ds->transitionsCount; x++) {
2643 if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) {
2644 if (ds->transitions[x]->condition &&
2645 !ds->transitions[x]->condition->Evaluate(target)) {
2646 continue;
2649 idx++;
2650 if (ds->transitions[x]->textStrRef == 0xffffffff) {
2651 //dialogchoose should be set to x
2652 //it isn't important which END option was chosen, as it ends
2653 core->GetDictionary()->SetAt("DialogOption",x);
2654 DialogueFlags |= DF_OPENENDWINDOW;
2655 } else {
2656 char *string = ( char * ) malloc( 40 );
2657 sprintf( string, "[s=%d,ffffff,ff0000]%d - [p]", x, idx );
2658 i = ta->AppendText( string, -1 );
2659 free( string );
2660 string = core->GetString( ds->transitions[x]->textStrRef );
2661 ta->AppendText( string, i );
2662 free( string );
2663 ta->AppendText( "[/p][/s]", i );
2666 // this happens if a trigger isn't implemented or the dialog is wrong
2667 if (!idx) {
2668 printMessage("Dialog", "There were no valid dialog options!\n", YELLOW);
2669 DialogueFlags |= DF_OPENENDWINDOW;
2671 end_of_choose:
2672 //padding the rows so our text will be at the top
2673 if (core->HasFeature( GF_DIALOGUE_SCROLLS )) {
2674 ta->AppendText( "", -1 );
2676 else {
2677 ta->PadMinRow();
2681 //Create an overhead text over an arbitrary point
2682 void GameControl::DisplayString(const Point &p, const char *Text)
2684 Scriptable* scr = new Scriptable( ST_TRIGGER );
2685 scr->overHeadText = (char *) Text;
2686 scr->textDisplaying = 1;
2687 scr->timeStartDisplaying = 0;
2688 scr->Pos = p;
2689 scr->ClearCutsceneID( );
2692 //Create an overhead text over a scriptable target
2693 //Multiple texts are possible, as this code copies the text to a new object
2694 void GameControl::DisplayString(Scriptable* target)
2696 Scriptable* scr = new Scriptable( ST_TRIGGER );
2697 scr->overHeadText = strdup( target->overHeadText );
2698 /* strdup should work here, we use it elsewhere
2699 size_t len = strlen( target->overHeadText ) + 1;
2700 scr->overHeadText = ( char * ) malloc( len );
2701 strcpy( scr->overHeadText, target->overHeadText );
2703 scr->textDisplaying = 1;
2704 scr->timeStartDisplaying = target->timeStartDisplaying;
2705 scr->Pos = target->Pos;
2706 scr->SetCutsceneID( target );
2709 /** changes displayed map to the currently selected PC */
2710 void GameControl::ChangeMap(Actor *pc, bool forced)
2712 //swap in the area of the actor
2713 Game* game = core->GetGame();
2714 if (forced || (stricmp( pc->Area, game->CurrentArea) != 0) ) {
2715 EndDialog();
2716 overInfoPoint = NULL;
2717 overContainer = NULL;
2718 overDoor = NULL;
2719 /*this is loadmap, because we need the index, not the pointer*/
2720 char *areaname = game->CurrentArea;
2721 if (pc) {
2722 areaname = pc->Area;
2724 game->GetMap( areaname, true );
2725 ScreenFlags|=SF_CENTERONACTOR;
2727 //center on first selected actor
2728 Region vp = core->GetVideoDriver()->GetViewport();
2729 if (ScreenFlags&SF_CENTERONACTOR) {
2730 core->timer->SetMoveViewPort( pc->Pos.x, pc->Pos.y, 0, true );
2731 ScreenFlags&=~SF_CENTERONACTOR;
2735 void GameControl::SetScreenFlags(int value, int mode)
2737 switch(mode) {
2738 case BM_OR: ScreenFlags|=value; break;
2739 case BM_NAND: ScreenFlags&=~value; break;
2740 case BM_SET: ScreenFlags=value; break;
2741 case BM_AND: ScreenFlags&=value; break;
2742 case BM_XOR: ScreenFlags^=value; break;
2746 void GameControl::SetDialogueFlags(int value, int mode)
2748 switch(mode) {
2749 case BM_OR: DialogueFlags|=value; break;
2750 case BM_NAND: DialogueFlags&=~value; break;
2751 case BM_SET: DialogueFlags=value; break;
2752 case BM_AND: DialogueFlags&=value; break;
2753 case BM_XOR: DialogueFlags^=value; break;
2757 //copies a screenshot into a sprite
2758 Sprite2D* GameControl::GetScreenshot(bool show_gui)
2760 Sprite2D* screenshot;
2761 if (show_gui) {
2762 screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) );
2763 } else {
2764 int hf = HideGUI ();
2765 Draw (0, 0);
2766 screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) );
2767 if (hf) {
2768 UnhideGUI ();
2770 core->DrawWindows ();
2773 return screenshot;
2776 //copies a downscaled screenshot into a sprite for save game preview
2777 Sprite2D* GameControl::GetPreview()
2779 // We get preview by first taking a screenshot of size 640x405,
2780 // centered in the display. This is to get a decent picture for
2781 // higher screen resolutions.
2782 // FIXME: how do orig games solve that?
2783 Video* video = core->GetVideoDriver();
2784 int w = video->GetWidth();
2785 int h = video->GetHeight();
2786 int x = (w - 640) / 2;
2787 int y = (h - 405) / 2;
2789 if (x < 0) {
2790 x = 0;
2791 } else {
2792 w = 640;
2795 if (y < 0) {
2796 y = 0;
2797 } else {
2798 h = 405;
2801 if (!x)
2802 y = 0;
2804 int hf = HideGUI ();
2805 signed char v = Owner->Visible;
2806 Owner->Visible = WINDOW_VISIBLE;
2807 Draw (0, 0);
2808 Owner->Visible = v;
2809 Sprite2D *screenshot = video->GetScreenshot( Region(x, y, w, h) );
2810 if (hf) {
2811 UnhideGUI ();
2813 core->DrawWindows();
2815 Sprite2D* preview = video->SpriteScaleDown ( screenshot, 5 );
2816 video->FreeSprite( screenshot );
2817 return preview;
2822 * Returns PC portrait for a currently running game
2824 Sprite2D* GameControl::GetPortraitPreview(int pcslot)
2826 /** Portrait shrink ratio */
2827 // FIXME: this is just a random PST specific trait
2828 // you can make it less random with a new feature bit
2829 int ratio = (core->HasFeature( GF_ONE_BYTE_ANIMID )) ? 1 : 2;
2831 Video *video = core->GetVideoDriver();
2833 Actor *actor = core->GetGame()->GetPC( pcslot, false );
2834 if (! actor) {
2835 return NULL;
2837 ResourceHolder<ImageMgr> im(actor->GetPortrait(true));
2838 if (! im) {
2839 return NULL;
2842 Sprite2D* img = im->GetSprite2D();
2844 if (ratio == 1)
2845 return img;
2847 Sprite2D* img_scaled = video->SpriteScaleDown( img, ratio );
2848 video->FreeSprite( img );
2850 return img_scaled;
2853 Actor *GameControl::GetActorByGlobalID(ieWord ID)
2855 if (!ID)
2856 return NULL;
2857 Game* game = core->GetGame();
2858 if (!game)
2859 return NULL;
2861 Map* area = game->GetCurrentArea( );
2862 if (!area)
2863 return NULL;
2864 return
2865 area->GetActorByGlobalID(ID);
2868 Actor *GameControl::GetLastActor()
2870 return GetActorByGlobalID(lastActorID);
2873 Actor *GameControl::GetTarget()
2875 return GetActorByGlobalID(targetID);
2878 Actor *GameControl::GetSpeaker()
2880 return GetActorByGlobalID(speakerID);
2883 //Set up an item use which needs targeting
2884 //Slot is an inventory slot
2885 //header is the used item extended header
2886 //u is the user
2887 //target type is a bunch of GetActor flags that alter valid targets
2888 //cnt is the number of different targets (usually 1)
2889 void GameControl::SetupItemUse(int slot, int header, Actor *u, int targettype, int cnt)
2891 spellOrItem = -1;
2892 spellUser = u;
2893 spellSlot = slot;
2894 spellIndex = header;
2895 //item use also uses the casting icon, this might be changed in some custom game?
2896 target_mode = TARGET_MODE_CAST;
2897 target_types = targettype;
2898 spellCount = cnt;
2901 //Set up spell casting which needs targeting
2902 //type is the spell's type
2903 //level is the caster level
2904 //idx is the spell's number
2905 //u is the caster
2906 //target type is a bunch of GetActor flags that alter valid targets
2907 //cnt is the number of different targets (usually 1)
2908 void GameControl::SetupCasting(int type, int level, int idx, Actor *u, int targettype, int cnt)
2910 spellOrItem = type;
2911 spellUser = u;
2912 spellSlot = level;
2913 spellIndex = idx;
2914 target_mode = TARGET_MODE_CAST;
2915 target_types = targettype;
2916 spellCount = cnt;
2919 //another method inherited from Control which has no use here
2920 bool GameControl::SetEvent(int /*eventType*/, const char * /*handler*/)
2922 return false;
2925 void GameControl::SetDisplayText(char *text, unsigned int time)
2927 if (DisplayText) {
2928 core->FreeString(DisplayText);
2930 DisplayTextTime = time;
2931 DisplayText = text;
2934 void GameControl::SetDisplayText(ieStrRef text, unsigned int time)
2936 SetDisplayText(core->GetString(core->GetStringReference(text), 0), time);