Move EventMgr to GUI.
[gemrb.git] / gemrb / core / GUI / GameControl.cpp
blob420dfcb19c04f7030cdf31b177eb9d8226b7ad84
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 "GUI/GameControl.h"
22 #include "strrefs.h"
23 #include "win32def.h"
25 #include "DialogMgr.h"
26 #include "DisplayMessage.h"
27 #include "Effect.h"
28 #include "GSUtils.h"
29 #include "Game.h"
30 #include "GameData.h"
31 #include "ImageMgr.h"
32 #include "Interface.h"
33 #include "Item.h"
34 #include "SaveGameIterator.h"
35 #include "ScriptEngine.h"
36 #include "TileMap.h"
37 #include "Video.h"
38 #include "damages.h"
40 #include <cmath>
42 #define DEBUG_SHOW_INFOPOINTS 0x01
43 #define DEBUG_SHOW_CONTAINERS 0x02
44 #define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
45 #define DEBUG_SHOW_LIGHTMAP 0x08
47 static const Color cyan = {
48 0x00, 0xff, 0xff, 0xff
50 static const Color red = {
51 0xff, 0x00, 0x00, 0xff
53 static const Color magenta = {
54 0xff, 0x00, 0xff, 0xff
56 static const Color green = {
57 0x00, 0xff, 0x00, 0xff
60 static Color white = {
61 0xff, 0xff, 0xff, 0xff
64 static const Color black = {
65 0x00, 0x00, 0x00, 0xff
67 static const Color blue = {
68 0x00, 0x00, 0xff, 0x80
71 //Animation* effect;
73 #define FORMATIONSIZE 10
74 typedef Point formation_type[FORMATIONSIZE];
75 ieDword formationcount;
76 static formation_type *formations=NULL;
77 static bool mqs = false;
78 static ieResRef TestSpell="SPWI207";
80 //If one of the actors has tracking on, the gamecontrol needs to display
81 //arrow markers on the edges to point at detected monsters
82 //tracterID is the tracker actor's global ID
83 //distance is the detection distance
84 void GameControl::SetTracker(Actor *actor, ieDword dist)
86 trackerID = actor->GetID();
87 distance = dist;
90 //Multiple Quick saves is an experimental GemRB feature.
91 //multiple quick saves are kept, their age is determined by the slot
92 //number. There is an algorithm which keeps about log2(n) slots alive.
93 //The algorithm is implemented in SaveGameIterator
94 void GameControl::MultipleQuickSaves(int arg)
96 mqs=arg==1;
99 GameControl::GameControl(void)
101 if (!formations) {
102 ReadFormations();
104 //this is the default action, individual actors should have one too
105 //at this moment we use only this
106 //maybe we don't even need it
107 Changed = true;
108 spellCount = 0;
109 user = NULL;
110 lastActorID = 0;
111 trackerID = 0;
112 distance = 0;
113 MouseIsDown = false;
114 DrawSelectionRect = false;
115 overDoor = NULL;
116 overContainer = NULL;
117 overInfoPoint = NULL;
118 drawPath = NULL;
119 pfs.null();
120 lastCursor = IE_CURSOR_NORMAL;
121 moveX = moveY = 0;
122 scrolling = false;
123 numScrollCursor = 0;
124 DebugFlags = 0;
125 AIUpdateCounter = 1;
126 ieDword tmp=0;
128 target_mode = TARGET_MODE_NONE;
129 target_types = GA_SELECT|GA_NO_DEAD|GA_NO_HIDDEN;
131 core->GetDictionary()->Lookup("Center",tmp);
132 if (tmp) {
133 ScreenFlags=SF_ALWAYSCENTER|SF_CENTERONACTOR;
135 else {
136 ScreenFlags = SF_CENTERONACTOR;
138 LeftCount = 0;
139 BottomCount = 0;
140 RightCount = 0;
141 TopCount = 0;
142 DialogueFlags = 0;
143 dlg = NULL;
144 targetID = 0;
145 originalTargetID = 0;
146 speakerID = 0;
147 targetOB = NULL;
148 DisplayText = NULL;
151 //TODO:
152 //There could be a custom formation which is saved in the save game
153 //alternatively, all formations could be saved in some compatible way
154 //so it doesn't cause problems with the original engine
155 void GameControl::ReadFormations()
157 unsigned int i,j;
158 AutoTable tab("formatio");
159 if (!tab) {
160 // fallback
161 formationcount = 1;
162 formations = (formation_type *) calloc(1,sizeof(formation_type) );
163 return;
165 formationcount = tab->GetRowCount();
166 formations = (formation_type *) calloc(formationcount, sizeof(formation_type));
167 for(i=0; i<formationcount; i++) {
168 for(j=0;j<FORMATIONSIZE;j++) {
169 short k=(short) atoi(tab->QueryField(i,j*2));
170 formations[i][j].x=k;
171 k=(short) atoi(tab->QueryField(i,j*2+1));
172 formations[i][j].y=k;
177 //returns a single point offset for a formation
178 //formation: the formation type
179 //pos: the actor's slot ID
180 Point GameControl::GetFormationOffset(ieDword formation, ieDword pos)
182 if (formation>=formationcount) formation = 0;
183 if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1;
184 return formations[formation][pos];
187 //Moves an actor to a new position, keeping the current formation
188 //WARNING: don't pass p as a reference because it gets modified
189 void GameControl::MoveToPointFormation(Actor *actor, unsigned int pos, Point src, Point p)
191 Map* map = actor->GetCurrentArea() ;
193 int formation=core->GetGame()->GetFormation();
194 if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1;
196 // calculate angle
197 double angle;
198 double xdiff = src.x - p.x;
199 double ydiff = src.y - p.y;
200 if (ydiff == 0) {
201 if (xdiff > 0) {
202 angle = M_PI_2;
203 } else {
204 angle = -M_PI_2;
206 } else {
207 angle = atan(xdiff/ydiff);
208 if (ydiff < 0) angle += M_PI;
211 // calculate new coordinates by rotating formation around (0,0)
212 double newx = -formations[formation][pos].x * cos(angle) + formations[formation][pos].y * sin(angle);
213 double newy = formations[formation][pos].x * sin(angle) + formations[formation][pos].y * cos(angle);
214 p.x += (int)newx;
215 p.y += (int)newy;
217 if (p.x < 0) p.x = 8;
218 if (p.y < 0) p.y = 8;
219 if (p.x > map->GetWidth()*16) p.x = map->GetWidth()*16 - 8;
220 if (p.y > map->GetHeight()*12) p.y = map->GetHeight()*12 - 8;
222 if(map->GetCursor(p) == IE_CURSOR_BLOCKED) {
223 //we can't get there --> adjust position
224 p.x/=16;
225 p.y/=12;
226 map->AdjustPosition(p);
227 p.x*=16;
228 p.y*=12;
230 CreateMovement(actor, p);
233 // generate an action to do the actual movement
234 // only PST supports RunToPoint
235 void GameControl::CreateMovement(Actor *actor, const Point &p)
237 char Tmp[256];
239 Action *action = NULL;
240 if (DoubleClick) {
241 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
242 action = GenerateAction( Tmp );
244 if (!action)
246 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
247 action = GenerateAction( Tmp );
250 actor->AddAction( action );
251 // force action so that we get target recticles immediately
252 actor->ProcessActions(true);
255 GameControl::~GameControl(void)
257 //releasing the viewport of GameControl
258 core->GetVideoDriver()->SetViewport( 0,0,0,0 );
259 if (formations) {
260 free( formations );
261 formations = NULL;
263 if (dlg) {
264 delete dlg;
266 if (DisplayText) {
267 core->FreeString(DisplayText);
271 //Autosave was triggered by the GUI
272 void GameControl::AutoSave()
274 core->GetSaveGameIterator()->CreateSaveGame(0, false);
277 //QuickSave was triggered by the GUI
278 //mqs is the 'multiple quick saves' flag
279 void GameControl::QuickSave()
281 core->GetSaveGameIterator()->CreateSaveGame(1, mqs == 1);
284 // ArrowSprite cycles
285 // 321
286 // 4 0
287 // 567
289 #define D_LEFT 1
290 #define D_UP 2
291 #define D_RIGHT 4
292 #define D_BOTTOM 8
293 // Direction Bits
294 // 326
295 // 1 4
296 // 98c
298 static const int arrow_orientations[16]={
299 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
300 -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1
303 //Draws arrow markers along the edge of the game window
304 //WARNING:don't use reference for point, because it is altered
305 void GameControl::DrawArrowMarker(const Region &screen, Point p, const Region &viewport)
307 Video* video = core->GetVideoDriver();
309 //p.x-=viewport.x;
310 //p.y-=viewport.y;
311 ieDword draw = 0;
312 if (p.x<viewport.x) {
313 p.x=viewport.x;
314 draw|= D_LEFT;
316 if (p.y<viewport.y) {
317 p.y=viewport.y;
318 draw |= D_UP;
320 int tmp;
322 Sprite2D *spr = core->GetScrollCursorSprite(0,0);
324 tmp = spr->Width;
325 //tmp = core->ArrowSprites[0]->Width;
327 if (p.x>viewport.x+viewport.w-tmp) {
328 p.x=viewport.x+viewport.w;//-tmp;
329 draw |= D_RIGHT;
332 tmp = spr->Height;
333 //tmp = core->ArrowSprites[0]->Height;
335 if (p.y>viewport.y+viewport.h-tmp) {
336 p.y=viewport.y+viewport.h;//-tmp;
337 draw |= D_BOTTOM;
339 if (arrow_orientations[draw]>=0) {
340 video->BlitGameSprite( core->GetScrollCursorSprite(arrow_orientations[draw], 0), p.x+screen.x, p.y+screen.y, 0, black, NULL);
344 /** Draws the Control on the Output Display */
345 void GameControl::Draw(unsigned short x, unsigned short y)
347 bool update_scripts = !(DialogueFlags & DF_FREEZE_SCRIPTS);
349 Game* game = core->GetGame();
350 if (!game)
351 return;
353 if (((short) Width) <=0 || ((short) Height) <= 0) {
354 return;
357 if (Owner->Visible!=WINDOW_VISIBLE) {
358 return;
361 Video* video = core->GetVideoDriver();
362 Region viewport = video->GetViewport();
363 if (moveX || moveY) {
364 viewport.x += moveX;
365 viewport.y += moveY;
366 Point mapsize = core->GetGame()->GetCurrentArea()->TMap->GetMapSize();
367 if ( viewport.x < 0 )//if we are at top of the map
368 viewport.x = 0;
369 else if ( (viewport.x + viewport.w) >= mapsize.x) //if we are at the bottom
370 viewport.x = mapsize.x - viewport.w;
372 if ( viewport.y < 0 ) //if we are at the left of the map
373 viewport.y = 0;
374 else if ( (viewport.y + viewport.h ) >= mapsize.y ) //if we are at the right
375 viewport.y = mapsize.y - viewport.h;
377 // override any existing viewport moves which may be in progress
378 core->timer->SetMoveViewPort( viewport.x, viewport.y, 0, false );
379 // move it directly ourselves, since we might be paused
380 video->MoveViewportTo( viewport.x, viewport.y );
382 Region screen( x + XPos, y + YPos, Width, Height );
383 Map* area = game->GetCurrentArea( );
384 if (!area) {
385 video->DrawRect( screen, blue, true );
386 return;
388 video->DrawRect( screen, black, true );
390 // setup outlines
391 InfoPoint *i;
392 unsigned int idx;
393 for (idx = 0; (i = area->TMap->GetInfoPoint( idx )); idx++) {
394 i->Highlight = false;
395 if (overInfoPoint == i && target_mode) {
396 if (i->VisibleTrap(0)) {
397 i->outlineColor = green;
398 i->Highlight = true;
399 continue;
402 if (i->VisibleTrap(DebugFlags & DEBUG_SHOW_INFOPOINTS)) {
403 i->outlineColor = red; // traps
404 } else if (DebugFlags & DEBUG_SHOW_INFOPOINTS) {
405 i->outlineColor = blue; // debug infopoints
406 } else {
407 continue;
409 i->Highlight = true;
412 Door *d;
413 for (idx = 0; (d = area->TMap->GetDoor( idx )); idx++) {
414 d->Highlight = false;
415 if (overDoor == d) {
416 if (target_mode) {
417 if (d->VisibleTrap(0) || (d->Flags & DOOR_LOCKED)) {
418 // only highlight targettable doors
419 d->outlineColor = green;
420 d->Highlight = true;
421 continue;
423 } else if (!(d->Flags & DOOR_SECRET)) {
424 // mouse over, not in target mode, no secret door
425 d->outlineColor = cyan;
426 d->Highlight = true;
427 continue;
430 if (d->VisibleTrap(0)) {
431 d->outlineColor = red; // traps
432 } else if (d->Flags & DOOR_SECRET) {
433 if (DebugFlags & DEBUG_SHOW_DOORS || d->Flags & DOOR_FOUND) {
434 d->outlineColor = magenta; // found hidden door
435 } else {
436 // secret door is invisible
437 continue;
439 } else if (DebugFlags & DEBUG_SHOW_DOORS) {
440 d->outlineColor = cyan; // debug doors
441 } else {
442 continue;
444 d->Highlight = true;
447 Container *c;
448 for (idx = 0; (c = area->TMap->GetContainer( idx )); idx++) {
449 c->Highlight = false;
450 if (overContainer == c && target_mode) {
451 if (c->VisibleTrap(0) || (c->Flags & CONT_LOCKED)) {
452 // only highlight targettable containers
453 c->outlineColor = green;
454 c->Highlight = true;
455 continue;
457 } else if (overContainer == c) {
458 // mouse over, not in target mode
459 c->outlineColor = cyan;
460 c->Highlight = true;
461 continue;
463 if (c->VisibleTrap(0)) {
464 c->outlineColor = red; // traps
465 } else if (DebugFlags & DEBUG_SHOW_CONTAINERS) {
466 c->outlineColor = cyan; // debug containers
467 } else {
468 continue;
470 c->Highlight = true;
473 //drawmap should be here so it updates fog of war
474 area->DrawMap( screen );
475 game->DrawWeather(screen, update_scripts);
477 if (trackerID) {
478 Actor *actor = area->GetActorByGlobalID(trackerID);
480 if (actor) {
481 Actor **monsters = area->GetAllActorsInRadius(actor->Pos, GA_NO_DEAD, distance);
483 int i = 0;
484 while(monsters[i]) {
485 Actor *target = monsters[i++];
486 if (target->InParty) continue;
487 if (target->GetStat(IE_NOTRACKING)) continue;
488 DrawArrowMarker(screen, target->Pos, viewport);
490 delete monsters;
491 } else {
492 trackerID = 0;
496 if (ScreenFlags & SF_DISABLEMOUSE)
497 return;
498 Point p(lastMouseX, lastMouseY);
499 video->ConvertToGame( p.x, p.y );
501 // Draw selection rect
502 if (DrawSelectionRect) {
503 CalculateSelection( p );
504 video->DrawRect( SelectionRect, green, false, true );
507 // Show wallpolygons
508 if (DebugFlags & DEBUG_SHOW_INFOPOINTS) {
510 unsigned int count = area->GetWallCount();
511 for (unsigned int i = 0; i < count; ++i) {
512 Wall_Polygon* poly = area->GetWallGroup(i);
513 if (!poly) continue;
514 // yellow
515 Color c;
516 c.r = 0x7F;
517 c.g = 0x7F;
518 c.b = 0;
519 c.a = 0;
520 //if polygon is disabled, make it grey
521 if (poly->wall_flag&WF_DISABLED) {
522 c.b = 0x7F;
525 video->DrawPolyline( poly, c, true );
529 // Draw path
530 if (drawPath) {
531 PathNode* node = drawPath;
532 while (true) {
533 Point p( ( node-> x*16) + 8, ( node->y*12 ) + 6 );
534 if (!node->Parent) {
535 video->DrawCircle( p.x, p.y, 2, red );
536 } else {
537 short oldX = ( node->Parent-> x*16) + 8, oldY = ( node->Parent->y*12 ) + 6;
538 video->DrawLine( oldX, oldY, p.x, p.y, green );
540 if (!node->Next) {
541 video->DrawCircle( p.x, p.y, 2, green );
542 break;
544 node = node->Next;
548 // Draw lightmap
549 if (DebugFlags & DEBUG_SHOW_LIGHTMAP) {
550 Sprite2D* spr = area->LightMap->GetSprite2D();
551 video->BlitSprite( spr, 0, 0, true );
552 video->FreeSprite( spr );
553 Region point( p.x / 16, p.y / 12, 2, 2 );
554 video->DrawRect( point, red );
557 if (core->HasFeature(GF_ONSCREEN_TEXT) && DisplayText) {
558 core->GetFont(1)->Print(screen, (unsigned char *)DisplayText, core->InfoTextPalette, IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true);
559 if (update_scripts) {
560 // just replicating original engine behaviour
561 if (DisplayTextTime == 0) {
562 SetDisplayText((char *)NULL, 0);
563 } else {
564 DisplayTextTime--;
570 /** inherited from Control, GameControl doesn't need it */
571 int GameControl::SetText(const char* /*string*/, int /*pos*/)
573 return 0;
576 /** Key Press Event */
577 void GameControl::OnKeyPress(unsigned char Key, unsigned short /*Mod*/)
579 if (DialogueFlags&DF_IN_DIALOG) {
580 return;
582 unsigned int i;
583 Game* game = core->GetGame();
584 if (!game) return;
586 switch (Key) {
587 case '0':
588 game->SelectActor( NULL, false, SELECT_NORMAL );
589 i = game->GetPartySize(false)/2;
590 while(i--) {
591 SelectActor(i, true);
593 break;
594 case '-':
595 game->SelectActor( NULL, true, SELECT_NORMAL );
596 i = game->GetPartySize(false)/2;
597 while(i--) {
598 SelectActor(i, false);
600 break;
601 case '=':
602 SelectActor(-1);
603 break;
604 case '1':
605 case '2':
606 case '3':
607 case '4':
608 case '5':
609 case '6':
610 case '7':
611 case '8':
612 case '9':
613 SelectActor(Key-'0');
614 break;
615 default:
616 core->GetGame()->SetHotKey(toupper(Key));
617 break;
621 //Select (or deselect) a new actor (or actors)
622 void GameControl::SelectActor(int whom, int type)
624 Game* game = core->GetGame();
625 if (whom==-1) {
626 game->SelectActor( NULL, true, SELECT_NORMAL );
627 return;
630 /* doesn't fall through here */
631 Actor* actor = game->FindPC( whom );
632 if (!actor)
633 return;
635 if (type==0) {
636 game->SelectActor( actor, false, SELECT_NORMAL );
637 return;
639 if (type==1) {
640 game->SelectActor( actor, true, SELECT_NORMAL );
641 return;
644 bool was_selected = actor->IsSelected();
645 if (game->SelectActor( actor, true, SELECT_REPLACE ))
646 if (was_selected || (ScreenFlags & SF_ALWAYSCENTER)) {
647 ScreenFlags |= SF_CENTERONACTOR;
651 //Effect for the ctrl-r cheatkey (resurrect)
652 static EffectRef heal_ref={"CurrentHPModifier", NULL, -1};
653 static EffectRef damage_ref={"Damage", NULL, -1};
655 /** Key Release Event */
656 void GameControl::OnKeyRelease(unsigned char Key, unsigned short Mod)
658 unsigned int i;
659 Game* game = core->GetGame();
661 if (!game)
662 return;
664 if (DialogueFlags&DF_IN_DIALOG) {
665 if (Mod) return;
666 switch(Key) {
667 case '1':
668 case '2':
669 case '3':
670 case '4':
671 case '5':
672 case '6':
673 case '7':
674 case '8':
675 case '9':
677 TextArea *ta = core->GetMessageTextArea();
678 if (ta) {
679 ta->OnKeyPress(Key,Mod);
682 break;
684 return;
686 //cheatkeys with ctrl-
687 if (Mod & GEM_MOD_CTRL) {
688 if (!core->CheatEnabled()) {
689 return;
691 Map* area = game->GetCurrentArea( );
692 if (!area)
693 return;
694 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
695 Point p(lastMouseX, lastMouseY);
696 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
697 switch (Key) {
698 case 'f': //toggle full screen mode
699 core->GetVideoDriver()->ToggleFullscreenMode();
700 break;
701 case 'd': //disarm a trap
702 if (overInfoPoint) {
703 overInfoPoint->DetectTrap(256);
705 if (overContainer) {
706 if (overContainer->Trapped &&
707 !( overContainer->TrapDetected )) {
708 overContainer->TrapDetected = 1;
711 if (overDoor) {
712 if (overDoor->Trapped &&
713 !( overDoor->TrapDetected )) {
714 overDoor->TrapDetected = 1;
717 break;
718 case 'l': //play an animation (vvc/bam) over an actor
719 //the original engine was able to swap through all animations
720 if (lastActor) {
721 lastActor->AddAnimation("S056ICBL", 0, 0, 0);
723 break;
725 case 'c': //force cast a hardcoded spell
726 //caster is the last selected actor
727 //target is the door/actor currently under the pointer
728 if (game->selected.size() > 0) {
729 Actor *src = game->selected[0];
730 Scriptable *target = lastActor;
731 if (overDoor) {
732 target = overDoor;
734 if (target) {
735 src->CastSpell( TestSpell, target, false );
736 if (src->LastTarget) {
737 src->CastSpellEnd( TestSpell );
738 } else {
739 src->CastSpellPointEnd( TestSpell );
743 break;
745 case 'b': //draw a path to the target (pathfinder debug)
746 //You need to select an origin with ctrl-o first
747 if (drawPath) {
748 PathNode* nextNode = drawPath->Next;
749 PathNode* thisNode = drawPath;
750 while (true) {
751 delete( thisNode );
752 thisNode = nextNode;
753 if (!thisNode)
754 break;
755 nextNode = thisNode->Next;
758 drawPath = core->GetGame()->GetCurrentArea()->FindPath( pfs, p, lastActor?lastActor->size:1 );
760 break;
762 case 'o': //set up the origin for the pathfinder
763 // origin
764 pfs.x = lastMouseX;
765 pfs.y = lastMouseY;
766 core->GetVideoDriver()->ConvertToGame( pfs.x, pfs.y );
767 break;
768 case 'a': //switches through the avatar animations
769 if (lastActor) {
770 lastActor->GetNextAnimation();
772 break;
773 case 's': //switches through the stance animations
774 if (lastActor) {
775 lastActor->GetNextStance();
777 break;
778 case 'j': //teleports the selected actors
779 for (i = 0; i < game->selected.size(); i++) {
780 Actor* actor = game->selected[i];
781 MoveBetweenAreasCore(actor, core->GetGame()->CurrentArea, p, -1, true);
782 printf( "Teleported to %d, %d\n", p.x, p.y );
784 break;
786 case 'm': //prints a debug dump (ctrl-m in the original game too)
787 if (!lastActor) {
788 lastActor = area->GetActor( p, GA_DEFAULT);
790 if (!lastActor) {
791 // ValidTarget never returns immobile targets, making debugging a nightmare
792 // so if we don't have an actor, we make really really sure by checking manually
793 unsigned int count = area->GetActorCount(true);
794 while (count--) {
795 Actor *actor = area->GetActor(count, true);
796 if (actor->IsOver(p)) {
797 actor->DebugDump();
801 if (lastActor) {
802 lastActor->DebugDump();
803 break;
805 if (overDoor) {
806 overDoor->DebugDump();
807 break;
809 if (overContainer) {
810 overContainer->DebugDump();
811 break;
813 if (overInfoPoint) {
814 overInfoPoint->DebugDump();
815 break;
817 core->GetGame()->GetCurrentArea()->DebugDump(Mod & GEM_MOD_SHIFT);
818 break;
819 case 'v': //marks some of the map visited (random vision distance)
820 area->ExploreMapChunk( p, rand()%30, 1 );
821 break;
822 case 'x': // shows coordinates on the map
823 printf( "%s [%d.%d]\n", area->GetScriptName(), p.x, p.y );
824 break;
825 case 'g'://shows loaded areas and other game information
826 game->DebugDump();
827 break;
828 case 'i'://interact trigger (from the original game)
829 if (!lastActor) {
830 lastActor = area->GetActor( p, GA_DEFAULT);
832 if (lastActor && !(lastActor->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE)) {
833 Actor *target;
834 int i = game->GetPartySize(true);
835 if(i<2) break;
836 i=rand()%i;
839 target = game->GetPC(i, true);
840 if(target==lastActor) continue;
841 if(target->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE) continue;
843 char Tmp[40];
844 snprintf(Tmp,sizeof(Tmp),"Interact(\"%s\")",target->GetScriptName() );
845 lastActor->AddAction(GenerateAction(Tmp));
846 break;
848 while(i--);
850 break;
851 case 'r'://resurrects actor
852 if (!lastActor) {
853 lastActor = area->GetActor( p, GA_DEFAULT);
855 if (lastActor) {
856 Effect *fx = EffectQueue::CreateEffect(heal_ref, lastActor->GetBase(IE_MAXHITPOINTS), 0x30001, FX_DURATION_INSTANT_PERMANENT);
857 if (fx) {
858 core->ApplyEffect(fx, lastActor, lastActor);
861 break;
862 case 't'://advances time
863 // 7200 (one day) /24 (hours) == 300
864 game->AdvanceTime(300*AI_UPDATE_TIME);
865 //refresh gui here once we got it
866 break;
868 case 'q': //joins actor to the party
869 if (lastActor && !lastActor->InParty) {
870 lastActor->ClearActions();
871 lastActor->ClearPath();
872 char Tmp[40];
873 strncpy(Tmp,"JoinParty()",sizeof(Tmp) );
874 lastActor->AddAction( GenerateAction(Tmp) );
876 break;
877 case 'p': //center on actor
878 ScreenFlags|=SF_CENTERONACTOR;
879 ScreenFlags^=SF_ALWAYSCENTER;
880 break;
881 case 'k': //kicks out actor
882 if (lastActor && lastActor->InParty) {
883 lastActor->ClearActions();
884 lastActor->ClearPath();
885 char Tmp[40];
886 strncpy(Tmp,"LeaveParty()",sizeof(Tmp) );
887 lastActor->AddAction( GenerateAction(Tmp) );
889 break;
890 case 'y': //kills actor or all enemies
891 if (Mod & GEM_MOD_SHIFT) {
892 // mwahaha!
893 Effect *newfx;
894 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT);
895 Actor *victim;
896 for (int i = area->GetActorCount(0)-1; i >= 0; i--) {
897 victim = area->GetActor(i, 0);
898 if (victim->Modified[IE_EA] == EA_ENEMY) {
899 core->ApplyEffect(newfx, victim, victim);
902 delete newfx;
903 } else {
904 if (lastActor) {
905 //using action so the actor is killed
906 //correctly (synchronisation)
907 lastActor->ClearActions();
908 lastActor->ClearPath();
910 Effect *newfx;
911 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT);
912 core->ApplyEffect(newfx, lastActor, lastActor);
913 if (! (lastActor->GetInternalFlag() & IF_REALLYDIED)) {
914 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_ACID<<16, FX_DURATION_INSTANT_PERMANENT);
915 core->ApplyEffect(newfx, lastActor, lastActor);
916 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_CRUSHING<<16, FX_DURATION_INSTANT_PERMANENT);
917 core->ApplyEffect(newfx, lastActor, lastActor);
919 delete newfx;
920 } else if (overContainer) {
921 overContainer->SetContainerLocked(0);
922 } else if (overDoor) {
923 overDoor->SetDoorLocked(0,0);
926 break;
927 case 'z': //shift through the avatar animations backward
928 if (lastActor) {
929 lastActor->GetPrevAnimation();
931 break;
932 case '1': //change paperdoll armour level
933 if (! lastActor)
934 break;
935 lastActor->NewStat(IE_ARMOR_TYPE,1,MOD_ADDITIVE);
936 break;
937 case '4': //show all traps and infopoints
938 DebugFlags ^= DEBUG_SHOW_INFOPOINTS;
939 printf("Show traps and infopoints %s\n", DebugFlags & DEBUG_SHOW_INFOPOINTS ? "ON" : "OFF");
940 break;
941 case '6': //show the lightmap
942 DebugFlags ^= DEBUG_SHOW_LIGHTMAP;
943 printf("Show lightmap %s\n", DebugFlags & DEBUG_SHOW_LIGHTMAP ? "ON" : "OFF");
944 break;
945 case '7': //toggles fog of war
946 core->FogOfWar ^= 1;
947 printf("Show Fog-Of-War: %s\n", core->FogOfWar & 1 ? "ON" : "OFF");
948 break;
949 case '8': //show searchmap over area
950 core->FogOfWar ^= 2;
951 printf("Show searchmap %s\n", core->FogOfWar & 2 ? "ON" : "OFF");
952 break;
953 default:
954 printf( "KeyRelease:%d - %d\n", Key, Mod );
955 break;
957 return; //return from cheatkeys
959 switch (Key) {
960 case 'h': //hard pause
961 if (DialogueFlags & DF_FREEZE_SCRIPTS) break;
962 //fallthrough
963 case ' ': //soft pause
964 DialogueFlags ^= DF_FREEZE_SCRIPTS;
965 if (DialogueFlags&DF_FREEZE_SCRIPTS) {
966 displaymsg->DisplayConstantString(STR_PAUSED,0xff0000);
967 SetDisplayText(STR_PAUSED, 0); // time 0 = removed instantly on unpause
968 } else {
969 displaymsg->DisplayConstantString(STR_UNPAUSED,0xff0000);
971 break;
972 case 'm':
973 core->GetGUIScriptEngine()->RunFunction("GUIMA","OpenMapWindow");
974 break;
975 case 'j':
976 core->GetGUIScriptEngine()->RunFunction("GUIJRNL","OpenJournalWindow");
977 break;
978 case 'i':
979 core->GetGUIScriptEngine()->RunFunction("GUIINV","OpenInventoryWindow");
980 break;
981 case 'r':
982 core->GetGUIScriptEngine()->RunFunction("GUIREC","OpenRecordsWindow");
983 break;
984 case 'q': //quicksave
985 QuickSave();
986 break;
987 case GEM_ALT: //alt key (shows containers)
988 DebugFlags &= ~DEBUG_SHOW_CONTAINERS;
989 break;
990 default:
991 break;
995 void GameControl::DisplayTooltip() {
996 Game* game = core->GetGame();
997 if (game) {
998 Map* area = game->GetCurrentArea( );
999 if (area) {
1000 Actor *actor = area->GetActorByGlobalID(lastActorID);
1001 if (actor && (actor->GetStat(IE_STATE_ID)&STATE_DEAD || actor->GetInternalFlag()&IF_JUSTDIED)) {
1002 // checking IF_JUSTDIED is kind of horrid, but seems necessary
1003 // no tooltips for dead actors!
1004 actor->SetOver( false );
1005 lastActorID = 0;
1006 actor = NULL;
1009 if (actor) {
1010 char *name = actor->GetName(-1);
1011 int hp = actor->GetStat(IE_HITPOINTS);
1012 int maxhp = actor->GetStat(IE_MAXHITPOINTS);
1014 char buffer[100];
1015 if (!core->TooltipBack) {
1016 // single-line tooltips without background (PS:T)
1017 if (actor->InParty) {
1018 snprintf(buffer, 100, "%s: %d/%d", name, hp, maxhp);
1019 } else {
1020 snprintf(buffer, 100, "%s", name);
1022 } else {
1023 // a guess at a neutral check
1024 bool neutral = actor->GetStat(IE_EA) == EA_NEUTRAL;
1025 // test for an injured string being present for this game
1026 int strindex = displaymsg->GetStringReference(STR_UNINJURED);
1027 // normal tooltips
1028 if (actor->InParty) {
1029 // in party: display hp
1030 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1031 } else if (neutral) {
1032 // neutral: display name only
1033 snprintf(buffer, 100, "%s", name);
1034 } else if (strindex == -1) {
1035 // non-neutral, not in party, no injured strings: display hp
1036 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1037 } else {
1038 // non-neutral, not in party: display injured string
1039 int strindex;
1040 char *injuredstring = NULL;
1041 // these boundaries are just a guess
1042 if (hp == maxhp) {
1043 strindex = STR_UNINJURED;
1044 } else if (hp > (maxhp*3)/4) {
1045 strindex = STR_INJURED1;
1046 } else if (hp > maxhp/2) {
1047 strindex = STR_INJURED2;
1048 } else if (hp > maxhp/3) {
1049 strindex = STR_INJURED3;
1050 } else {
1051 strindex = STR_INJURED4;
1053 strindex = displaymsg->GetStringReference(strindex);
1054 if (strindex != -1) {
1055 injuredstring = core->GetString(strindex, 0);
1058 if (!injuredstring) {
1059 // eek, where did the string go?
1060 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1061 } else {
1062 snprintf(buffer, 100, "%s\n%s", name, injuredstring);
1063 free(injuredstring);
1068 Point p = actor->Pos;
1069 core->GetVideoDriver()->ConvertToScreen( p.x, p.y );
1070 p.x += Owner->XPos + XPos;
1071 p.y += Owner->YPos + YPos;
1073 // hack to position text above PS:T actors
1074 if (!core->TooltipBack) p.y -= actor->size*50;
1076 // we should probably cope better with moving actors
1077 SetTooltip(buffer);
1078 core->DisplayTooltip(p.x, p.y, this);
1079 return;
1084 SetTooltip(NULL);
1085 core->DisplayTooltip(0, 0, NULL);
1086 return;
1089 //returns the appropriate cursor over an active region (trap, infopoint, travel region)
1090 int GameControl::GetCursorOverInfoPoint(InfoPoint *overInfoPoint)
1092 if (target_mode == TARGET_MODE_PICK) {
1093 if (overInfoPoint->VisibleTrap(0)) {
1094 return IE_CURSOR_TRAP;
1097 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1099 // traps always display a walk cursor?
1100 if (overInfoPoint->Type == ST_PROXIMITY) {
1101 return IE_CURSOR_WALK;
1103 return overInfoPoint->Cursor;
1106 //returns the appropriate cursor over a door
1107 int GameControl::GetCursorOverDoor(Door *overDoor)
1109 if (target_mode == TARGET_MODE_PICK) {
1110 if (overDoor->VisibleTrap(0)) {
1111 return IE_CURSOR_TRAP;
1113 if (overDoor->Flags & DOOR_LOCKED) {
1114 return IE_CURSOR_LOCK;
1117 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1119 return overDoor->Cursor;
1122 //returns the appropriate cursor over a container (or pile)
1123 int GameControl::GetCursorOverContainer(Container *overContainer)
1125 if (target_mode == TARGET_MODE_PICK) {
1126 if (overContainer->VisibleTrap(0)) {
1127 return IE_CURSOR_TRAP;
1129 if (overContainer->Flags & CONT_LOCKED) {
1130 return IE_CURSOR_LOCK2;
1133 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1135 return IE_CURSOR_TAKE;
1138 /** Mouse Over Event */
1139 void GameControl::OnMouseOver(unsigned short x, unsigned short y)
1141 if (ScreenFlags & SF_DISABLEMOUSE) {
1142 return;
1145 lastMouseX = x;
1146 lastMouseY = y;
1147 Point p( x,y );
1148 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
1149 if (MouseIsDown && ( !DrawSelectionRect )) {
1150 if (( abs( p.x - StartX ) > 5 ) || ( abs( p.y - StartY ) > 5 )) {
1151 DrawSelectionRect = true;
1154 Game* game = core->GetGame();
1155 if (!game) return;
1156 Map* area = game->GetCurrentArea( );
1157 if (!area) return;
1158 int nextCursor = area->GetCursor( p );
1159 //make the invisible area really invisible
1160 if (nextCursor == IE_CURSOR_INVALID) {
1161 Owner->Cursor = IE_CURSOR_BLOCKED;
1162 lastCursor = IE_CURSOR_BLOCKED;
1163 return;
1166 overInfoPoint = area->TMap->GetInfoPoint( p, true );
1167 if (overInfoPoint) {
1168 //nextCursor = overInfoPoint->Cursor;
1169 nextCursor = GetCursorOverInfoPoint(overInfoPoint);
1172 if (overDoor) {
1173 overDoor->Highlight = false;
1175 if (overContainer) {
1176 overContainer->Highlight = false;
1178 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
1179 if (lastActor) {
1180 lastActor->SetOver( false );
1183 overDoor = area->TMap->GetDoor( p );
1184 overContainer = area->TMap->GetContainer( p );
1186 if (!DrawSelectionRect) {
1187 if (overDoor) {
1188 nextCursor = GetCursorOverDoor(overDoor);
1191 if (overContainer) {
1192 nextCursor = GetCursorOverContainer(overContainer);
1195 Actor *prevActor = lastActor;
1196 lastActor = area->GetActor( p, target_types);
1197 if (lastActor != prevActor) {
1198 // we store prevActor so we can remove the tooltip on actor change
1199 // (maybe we should be checking this and actor movements every frame?)
1200 SetTooltip(NULL);
1201 core->DisplayTooltip(0, 0, this);
1204 if ((target_types & GA_NO_SELF) && lastActor ) {
1205 if (lastActor == core->GetFirstSelectedPC(false)) {
1206 lastActor=NULL;
1210 if (lastActor) {
1211 lastActorID = lastActor->globalID;
1212 lastActor->SetOver( true );
1213 ieDword type = lastActor->GetStat(IE_EA);
1214 if (type >= EA_EVILCUTOFF || type == EA_GOODBUTRED) {
1215 nextCursor = IE_CURSOR_ATTACK;
1216 } else if ( type > EA_CHARMED ) {
1217 nextCursor = IE_CURSOR_TALK;
1218 } else {
1219 nextCursor = IE_CURSOR_NORMAL;
1221 } else {
1222 lastActorID = 0;
1225 if (target_mode == TARGET_MODE_TALK) {
1226 nextCursor = IE_CURSOR_TALK;
1227 if (!lastActor) {
1228 nextCursor |= IE_CURSOR_GRAY;
1230 } else if (target_mode == TARGET_MODE_ATTACK) {
1231 nextCursor = IE_CURSOR_ATTACK;
1232 if (!lastActor && !overDoor && !overContainer) {
1233 nextCursor |= IE_CURSOR_GRAY;
1235 } else if (target_mode == TARGET_MODE_CAST) {
1236 nextCursor = IE_CURSOR_CAST;
1237 //point is always valid
1238 if (!(target_types & GA_POINT)) {
1239 if(!lastActor) {
1240 nextCursor |= IE_CURSOR_GRAY;
1243 } else if (target_mode == TARGET_MODE_DEFEND) {
1244 nextCursor = IE_CURSOR_DEFEND;
1245 if(!lastActor) {
1246 nextCursor |= IE_CURSOR_GRAY;
1248 } else if (target_mode == TARGET_MODE_PICK) {
1249 if (lastActor) {
1250 nextCursor = IE_CURSOR_PICK;
1251 } else {
1252 if (!overContainer && !overDoor && !overInfoPoint) {
1253 nextCursor = IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1256 goto end_function;
1259 if (lastActor) {
1260 switch (lastActor->GetStat(IE_EA)) {
1261 case EA_EVILCUTOFF:
1262 case EA_GOODCUTOFF:
1263 break;
1265 case EA_PC:
1266 case EA_FAMILIAR:
1267 case EA_ALLY:
1268 case EA_CONTROLLED:
1269 case EA_CHARMED:
1270 case EA_EVILBUTGREEN:
1271 if (target_types & GA_NO_ENEMY)
1272 nextCursor^=1;
1273 break;
1275 case EA_ENEMY:
1276 case EA_GOODBUTRED:
1277 if (target_types & GA_NO_ALLY)
1278 nextCursor^=1;
1279 break;
1280 default:
1281 if (!(target_types & GA_NO_NEUTRAL))
1282 nextCursor^=1;
1283 break;
1287 end_function:
1288 if (lastCursor != nextCursor) {
1289 Owner->Cursor = nextCursor;
1290 lastCursor = (unsigned char) nextCursor;
1294 #define SCROLL_BORDER 5
1296 /** Global Mouse Move Event */
1297 void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y)
1299 if (ScreenFlags & SF_DISABLEMOUSE) {
1300 return;
1303 if (Owner->Visible!=WINDOW_VISIBLE) {
1304 return;
1307 int mousescrollspd = core->GetMouseScrollSpeed();
1309 if (x <= SCROLL_BORDER)
1310 moveX = -mousescrollspd;
1311 else {
1312 if (x >= ( core->Width - SCROLL_BORDER ))
1313 moveX = mousescrollspd;
1314 else
1315 moveX = 0;
1317 if (y <= SCROLL_BORDER)
1318 moveY = -mousescrollspd;
1319 else {
1320 if (y >= ( core->Height - SCROLL_BORDER ))
1321 moveY = mousescrollspd;
1322 else
1323 moveY = 0;
1326 if (moveX != 0 || moveY != 0) {
1327 scrolling = true;
1328 } else if (scrolling) {
1329 scrolling = false;
1331 Video* video = core->GetVideoDriver();
1332 video->SetDragCursor(NULL);
1336 void GameControl::UpdateScrolling() {
1337 if (!scrolling) return;
1339 int mousescrollspd = core->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-?
1340 Video* video = core->GetVideoDriver();
1342 if (moveX == mousescrollspd && moveY == 0) { // right
1343 video->SetDragCursor(core->GetScrollCursorSprite(0,numScrollCursor));
1344 } else if (moveX == mousescrollspd && moveY == -mousescrollspd) { // upper right
1345 video->SetDragCursor(core->GetScrollCursorSprite(1,numScrollCursor));
1346 } else if (moveX == 0 && moveY == -mousescrollspd) { // up
1347 video->SetDragCursor(core->GetScrollCursorSprite(2,numScrollCursor));
1348 } else if (moveX == -mousescrollspd && moveY == -mousescrollspd) { // upper left
1349 video->SetDragCursor(core->GetScrollCursorSprite(3,numScrollCursor));
1350 } else if (moveX == -mousescrollspd && moveY == 0) { // left
1351 video->SetDragCursor(core->GetScrollCursorSprite(4,numScrollCursor));
1352 } else if (moveX == -mousescrollspd && moveY == mousescrollspd) { // bottom left
1353 video->SetDragCursor(core->GetScrollCursorSprite(5,numScrollCursor));
1354 } else if (moveX == 0 && moveY == mousescrollspd) { // bottom
1355 video->SetDragCursor(core->GetScrollCursorSprite(6,numScrollCursor));
1356 } else if (moveX == mousescrollspd && moveY == mousescrollspd) { // bottom right
1357 video->SetDragCursor(core->GetScrollCursorSprite(7,numScrollCursor));
1360 numScrollCursor = (numScrollCursor+1) % 15;
1363 //generate action code for source actor to try to attack a target
1364 void GameControl::TryToAttack(Actor *source, Actor *tgt)
1366 char Tmp[40];
1368 source->ClearPath();
1369 source->ClearActions();
1370 strncpy(Tmp,"NIDSpecial3()",sizeof(Tmp) );
1371 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1374 //generate action code for source actor to try to defend a target
1375 void GameControl::TryToDefend(Actor *source, Actor *tgt)
1377 char Tmp[40];
1379 source->ClearPath();
1380 source->ClearActions();
1381 strncpy(Tmp,"NIDSpecial4()",sizeof(Tmp) );
1382 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1385 //generate action code for source actor to try to pick pockets of a target
1386 //The -1 flag is a placeholder for dynamic target IDs
1387 void GameControl::TryToPick(Actor *source, Actor *tgt)
1389 char Tmp[40];
1391 source->ClearPath();
1392 source->ClearActions();
1393 strncpy(Tmp,"PickPockets([-1])", sizeof(Tmp) );
1394 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1397 //generate action code for source actor to try to pick a lock/disable trap on a door
1398 void GameControl::TryToPick(Actor *source, Door *tgt)
1400 char Tmp[40];
1402 source->ClearPath();
1403 source->ClearActions();
1404 if (tgt->Trapped && tgt->TrapDetected) {
1405 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1406 } else {
1407 snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() );
1409 source->AddAction( GenerateAction( Tmp ) );
1412 //generate action code for source actor to try to pick a lock/disable trap on a container
1413 void GameControl::TryToPick(Actor *source, Container *tgt)
1415 char Tmp[40];
1417 source->ClearPath();
1418 source->ClearActions();
1419 if (tgt->Trapped && tgt->TrapDetected) {
1420 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1421 } else {
1422 snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() );
1424 source->AddAction( GenerateAction( Tmp ) );
1427 //generate action code for source actor to try to disable trap (only trap type active regions)
1428 void GameControl::TryToDisarm(Actor *source, InfoPoint *tgt)
1430 if (tgt->Type!=ST_PROXIMITY) return;
1432 char Tmp[40];
1434 source->ClearPath();
1435 source->ClearActions();
1436 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1437 source->AddAction( GenerateAction( Tmp ) );
1440 //generate action code for source actor to try to force open lock on a door/container
1441 void GameControl::TryToBash(Actor *source, Scriptable *tgt)
1443 char Tmp[40];
1445 source->ClearPath();
1446 source->ClearActions();
1447 snprintf(Tmp, sizeof(Tmp), "Attack(\"%s\")", tgt->GetScriptName() );
1448 source->AddAction( GenerateAction( Tmp ) );
1451 //generate action code for source actor to use item/cast spell on a point
1452 void GameControl::TryToCast(Actor *source, const Point &tgt)
1454 char Tmp[40];
1456 if (!spellCount) {
1457 target_mode = TARGET_MODE_NONE;
1458 return; //not casting or using an own item
1460 source->ClearPath();
1461 source->ClearActions();
1463 spellCount--;
1464 if (spellOrItem>=0) {
1465 sprintf(Tmp, "NIDSpecial8()");
1466 } else {
1467 //using item on target
1468 sprintf(Tmp, "NIDSpecial7()");
1470 Action* action = GenerateAction( Tmp );
1471 action->pointParameter=tgt;
1472 if (spellOrItem>=0)
1474 CREMemorizedSpell *si;
1475 //spell casting at target
1476 si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex);
1477 if (!si)
1479 target_mode = TARGET_MODE_NONE;
1480 return;
1482 sprintf(action->string0Parameter,"%.8s",si->SpellResRef);
1484 else
1486 action->int0Parameter=spellSlot;
1487 action->int1Parameter=spellIndex;
1489 source->AddAction( action );
1490 if (!spellCount) {
1491 target_mode = TARGET_MODE_NONE;
1495 //generate action code for source actor to use item/cast spell on another actor
1496 void GameControl::TryToCast(Actor *source, Actor *tgt)
1498 char Tmp[40];
1500 if (!spellCount) {
1501 target_mode = TARGET_MODE_NONE;
1502 return; //not casting or using an own item
1504 source->ClearPath();
1505 source->ClearActions();
1507 spellCount--;
1508 if (spellOrItem>=0) {
1509 sprintf(Tmp, "NIDSpecial6()");
1510 } else {
1511 //using item on target
1512 sprintf(Tmp, "NIDSpecial5()");
1514 Action* action = GenerateActionDirect( Tmp, tgt);
1515 if (spellOrItem>=0)
1517 CREMemorizedSpell *si;
1518 //spell casting at target
1519 si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex);
1520 if (!si)
1522 target_mode = TARGET_MODE_NONE;
1523 return;
1525 sprintf(action->string0Parameter,"%.8s",si->SpellResRef);
1527 else
1529 action->int0Parameter=spellSlot;
1530 action->int1Parameter=spellIndex;
1532 source->AddAction( action );
1533 if (!spellCount) {
1534 target_mode = TARGET_MODE_NONE;
1538 //generate action code for source actor to use talk to target actor
1539 void GameControl::TryToTalk(Actor *source, Actor *tgt)
1541 char Tmp[40];
1543 //Nidspecial1 is just an unused action existing in all games
1544 //(non interactive demo)
1545 //i found no fitting action which would emulate this kind of
1546 //dialog initation
1547 source->ClearPath();
1548 source->ClearActions();
1549 strncpy(Tmp,"NIDSpecial1()",sizeof(Tmp) );
1550 targetID = tgt->globalID; //this is a hack, but not so deadly
1551 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1554 //generate action code for actor appropriate for the target mode when the target is a container
1555 void GameControl::HandleContainer(Container *container, Actor *actor)
1557 char Tmp[256];
1559 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1560 //we'll get the container back from the coordinates
1561 TryToCast(actor, container->Pos);
1562 //Do not reset target_mode, TryToCast does it for us!!
1563 return;
1566 if (target_mode == TARGET_MODE_ATTACK) {
1567 TryToBash(actor, container);
1568 target_mode = TARGET_MODE_NONE;
1569 return;
1572 if ((target_mode == TARGET_MODE_PICK)) {
1573 TryToPick(actor, container);
1574 target_mode = TARGET_MODE_NONE;
1575 return;
1578 actor->ClearPath();
1579 actor->ClearActions();
1580 strncpy(Tmp,"UseContainer()",sizeof(Tmp) );
1581 core->SetCurrentContainer( actor, container);
1582 actor->AddAction( GenerateAction( Tmp) );
1585 //generate action code for actor appropriate for the target mode when the target is a door
1586 void GameControl::HandleDoor(Door *door, Actor *actor)
1588 char Tmp[256];
1590 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1591 //we'll get the door back from the coordinates
1592 Point *p = door->toOpen;
1593 Point *otherp = door->toOpen+1;
1594 if (Distance(*p,actor)>Distance(*otherp,actor)) {
1595 p=otherp;
1597 TryToCast(actor, *p);
1598 return;
1601 if (target_mode == TARGET_MODE_ATTACK) {
1602 TryToBash(actor, door);
1603 target_mode = TARGET_MODE_NONE ;
1604 return;
1607 if ( (target_mode == TARGET_MODE_PICK) || door->TrapDetected) {
1608 TryToPick(actor, door);
1609 target_mode = TARGET_MODE_NONE ;
1610 return;
1613 actor->ClearPath();
1614 actor->ClearActions();
1615 // it really isn't very nice to store a pointer in the actor like this
1616 actor->TargetDoor = door;
1617 // internal gemrb toggle door action hack - should we use UseDoor instead?
1618 sprintf( Tmp, "NIDSpecial9()" );
1619 actor->AddAction( GenerateAction( Tmp) );
1622 //generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel)
1623 bool GameControl::HandleActiveRegion(InfoPoint *trap, Actor * actor, Point &p)
1625 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1626 //we'll get the active region from the coordinates (if needed)
1627 TryToCast(actor, p);
1628 //don't bother with this region further
1629 return true;
1631 if ((target_mode == TARGET_MODE_PICK)) {
1632 TryToDisarm(actor, trap);
1633 target_mode = TARGET_MODE_NONE;
1634 return true;
1637 switch(trap->Type) {
1638 case ST_TRAVEL:
1639 actor->UseExit(true);
1640 return false;
1641 case ST_TRIGGER:
1642 //the importer shouldn't load the script
1643 //if it is unallowed anyway (though
1644 //deactivated scripts could be reactivated)
1645 //only the 'trapped' flag should be honoured
1646 //there. Here we have to check on the
1647 //reset trap and deactivated flags
1648 if (trap->Scripts[0]) {
1649 if (!(trap->Flags&TRAP_DEACTIVATED) ) {
1650 trap->LastTriggerObject = trap->LastTrigger = actor->GetID();
1651 trap->ImmediateEvent();
1652 //directly feeding the event, even if there are actions in the queue
1653 trap->Scripts[0]->Update();
1654 trap->ProcessActions(true);
1655 //if reset trap flag not set, deactivate it
1656 //hmm, better not, info triggers don't deactivate themselves on click
1657 //if (!(trap->Flags&TRAP_RESET)) {
1658 // trap->Flags|=TRAP_DEACTIVATED;
1661 } else {
1662 if (trap->overHeadText) {
1663 if (trap->textDisplaying != 1) {
1664 trap->textDisplaying = 1;
1665 trap->timeStartDisplaying = core->GetGame()->Ticks;
1666 DisplayString( trap );
1670 if (trap->Flags&TRAP_USEPOINT) {
1671 //overriding the target point
1672 p = trap->UsePoint;
1673 return false;
1675 return true;
1676 default:;
1678 return false;
1680 /** Mouse Button Down */
1681 void GameControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short Button,
1682 unsigned short /*Mod*/)
1684 if (ScreenFlags&SF_DISABLEMOUSE)
1685 return;
1687 short px=x;
1688 short py=y;
1689 DoubleClick = false;
1690 switch(Button)
1692 case GEM_MB_SCRLUP:
1693 OnSpecialKeyPress(GEM_UP);
1694 break;
1695 case GEM_MB_SCRLDOWN:
1696 OnSpecialKeyPress(GEM_DOWN);
1697 break;
1698 case GEM_MB_ACTION|GEM_MB_DOUBLECLICK:
1699 DoubleClick = true;
1700 case GEM_MB_ACTION:
1701 core->GetVideoDriver()->ConvertToGame( px, py );
1702 MouseIsDown = true;
1703 SelectionRect.x = px;
1704 SelectionRect.y = py;
1705 StartX = px;
1706 StartY = py;
1707 SelectionRect.w = 0;
1708 SelectionRect.h = 0;
1711 /** Mouse Button Up */
1712 void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short Button,
1713 unsigned short /*Mod*/)
1715 unsigned int i;
1716 char Tmp[256];
1718 if (ScreenFlags & SF_DISABLEMOUSE) {
1719 return;
1721 //heh, i found no better place
1722 core->CloseCurrentContainer();
1724 MouseIsDown = false;
1725 Point p(x,y);
1726 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
1727 Game* game = core->GetGame();
1728 Map* area = game->GetCurrentArea( );
1730 if (DrawSelectionRect) {
1731 Actor** ab;
1732 unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
1733 for (i = 0; i < highlighted.size(); i++)
1734 highlighted[i]->SetOver( false );
1735 highlighted.clear();
1736 game->SelectActor( NULL, false, SELECT_NORMAL );
1737 if (count != 0) {
1738 for (i = 0; i < count; i++) {
1739 // FIXME: should call handler only once
1740 game->SelectActor( ab[i], true, SELECT_NORMAL );
1743 free( ab );
1744 DrawSelectionRect = false;
1745 return;
1748 //hidden actors are not selectable by clicking on them
1749 Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_HIDDEN);
1750 if (Button == GEM_MB_MENU) {
1751 if (actor) {
1752 //from GSUtils
1753 DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE);
1754 return;
1756 core->GetDictionary()->SetAt( "MenuX", x );
1757 core->GetDictionary()->SetAt( "MenuY", y );
1758 core->GetGUIScriptEngine()->RunFunction( "GUICommon", "OpenFloatMenuWindow" );
1759 return;
1762 if (Button != GEM_MB_ACTION) {
1763 return;
1766 if (!actor && ( game->selected.size() > 0 )) {
1767 if (overDoor) {
1768 HandleDoor(overDoor, core->GetFirstSelectedPC(false));
1769 return;
1771 if (overContainer) {
1772 HandleContainer(overContainer, core->GetFirstSelectedPC(false));
1773 return;
1775 if (overInfoPoint) {
1776 if (HandleActiveRegion(overInfoPoint, core->GetFirstSelectedPC(false), p)) {
1777 return;
1781 //just a single actor, no formation
1782 if (game->selected.size()==1) {
1783 //the player is using an item or spell on the ground
1784 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1785 TryToCast(core->GetFirstSelectedPC(false), p);
1786 return;
1789 actor=game->selected[0];
1790 actor->ClearPath();
1791 actor->ClearActions();
1792 CreateMovement(actor, p);
1794 if (DoubleClick) {
1795 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
1796 } else {
1797 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
1800 actor->AddAction( GenerateAction( Tmp) );
1802 //p is a searchmap travel region
1803 if ( actor->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) {
1804 sprintf( Tmp, "NIDSpecial2()" );
1805 actor->AddAction( GenerateAction( Tmp) );
1807 return;
1810 // construct a sorted party
1811 // TODO: this is beyond horrible, help
1812 std::vector<Actor *> party;
1813 // first, from the actual party
1814 for (int idx = 0; idx < game->GetPartySize(false); idx++) {
1815 Actor *pc = game->FindPC(idx + 1);
1816 if (!pc) continue;
1818 for (unsigned int j = 0; j < game->selected.size(); j++) {
1819 if (game->selected[j] == pc) {
1820 party.push_back(pc);
1825 // then, anything else we selected
1826 for (i = 0; i < game->selected.size(); i++) {
1827 bool found = false;
1828 for (unsigned int j = 0; j < party.size(); j++) {
1829 if (game->selected[i] == party[j]) {
1830 found = true;
1831 break;
1834 if (!found) party.push_back(game->selected[i]);
1837 //party formation movement
1838 Point src = party[0]->Pos;
1839 for(i = 0; i < party.size(); i++) {
1840 actor = party[i];
1841 actor->ClearPath();
1842 actor->ClearActions();
1843 MoveToPointFormation(actor, i, src, p);
1846 //p is a searchmap travel region
1847 if ( party[0]->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) {
1848 sprintf( Tmp, "NIDSpecial2()" );
1849 party[0]->AddAction( GenerateAction( Tmp) );
1851 return;
1853 if (!actor) return;
1854 //we got an actor past this point
1855 DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE);
1857 PerformActionOn(actor);
1860 void GameControl::PerformActionOn(Actor *actor) {
1861 Game* game = core->GetGame();
1862 unsigned int i;
1864 //determining the type of the clicked actor
1865 ieDword type;
1867 type = actor->GetStat(IE_EA);
1868 if ( type >= EA_EVILCUTOFF || type == EA_GOODBUTRED ) {
1869 type = ACT_ATTACK; //hostile
1870 } else if ( type > EA_CHARMED ) {
1871 type = ACT_TALK; //neutral
1872 } else {
1873 type = ACT_NONE; //party
1876 if (target_mode == TARGET_MODE_ATTACK) {
1877 type = ACT_ATTACK;
1878 } else if (target_mode == TARGET_MODE_TALK) {
1879 type = ACT_TALK;
1880 } else if (target_mode == TARGET_MODE_CAST) {
1881 type = ACT_CAST;
1882 } else if (target_mode == TARGET_MODE_DEFEND) {
1883 type = ACT_DEFEND;
1884 } else if (target_mode == TARGET_MODE_PICK) {
1885 type = ACT_THIEVING;
1888 //we shouldn't zero this for two reasons in case of spell or item
1889 //1. there could be multiple targets
1890 //2. the target mode is important
1891 if (!(target_mode == TARGET_MODE_CAST) || !spellCount) {
1892 target_mode = TARGET_MODE_NONE;
1895 switch (type) {
1896 case ACT_NONE: //none
1897 if (actor->InParty)
1898 SelectActor( actor->InParty );
1899 else if (actor->GetStat(IE_EA) <= EA_CHARMED) {
1900 /*let's select charmed/summoned creatures
1901 EA_CHARMED is the maximum value known atm*/
1902 core->GetGame()->SelectActor(actor, true, SELECT_REPLACE) ;
1904 break;
1905 case ACT_TALK:
1906 //talk (first selected talks)
1907 if (game->selected.size()) {
1908 //if we are in PST modify this to NO!
1909 Actor *source;
1910 if (core->HasFeature(GF_PROTAGONIST_TALKS) ) {
1911 source = game->GetPC(0, false); //protagonist
1912 } else {
1913 source = core->GetFirstSelectedPC(false);
1915 // only party members can start conversations
1916 if (source) {
1917 TryToTalk(source, actor);
1920 break;
1921 case ACT_ATTACK:
1922 //all of them attacks the red circled actor
1923 for(i=0;i<game->selected.size();i++) {
1924 TryToAttack(game->selected[i], actor);
1926 break;
1927 case ACT_CAST: //cast on target or use item on target
1928 if (game->selected.size()==1) {
1929 Actor *source;
1930 source = core->GetFirstSelectedPC(false);
1931 if(source) {
1932 TryToCast(source, actor);
1935 break;
1936 case ACT_DEFEND:
1937 for(i=0;i<game->selected.size();i++) {
1938 TryToDefend(game->selected[i], actor);
1940 break;
1941 case ACT_THIEVING:
1942 if (game->selected.size()==1) {
1943 Actor *source;
1944 source = core->GetFirstSelectedPC(false);
1945 if(source) {
1946 TryToPick(source, actor);
1949 break;
1952 /** Special Key Press */
1953 void GameControl::OnSpecialKeyPress(unsigned char Key)
1955 if (DialogueFlags&DF_IN_DIALOG) {
1956 switch(Key) {
1957 case GEM_RETURN:
1958 //simulating the continue/end button pressed
1959 core->GetGUIScriptEngine()->RunFunction("GUIWORLD", "CloseContinueWindow");
1960 break;
1962 return; //don't accept keys in dialog
1964 Region Viewport = core->GetVideoDriver()->GetViewport();
1965 Game *game = core->GetGame();
1966 Point mapsize = game->GetCurrentArea()->TMap->GetMapSize();
1967 int partysize = game->GetPartySize(false);
1968 int pm;
1969 char tmpstr[10];
1971 switch (Key) {
1972 case GEM_LEFT:
1973 if (Viewport.x > 63)
1974 Viewport.x -= 64;
1975 else
1976 Viewport.x = 0;
1977 break;
1978 case GEM_UP:
1979 if (Viewport.y > 63)
1980 Viewport.y -= 64;
1981 else
1982 Viewport.y = 0;
1983 break;
1984 case GEM_DOWN:
1985 if (Viewport.y + Viewport.h + 64 < mapsize.y)
1986 Viewport.y += 64;
1987 else {
1988 Viewport.y = mapsize.y - Viewport.h;
1989 if (Viewport.y<0) Viewport.y=0;
1991 break;
1992 case GEM_RIGHT:
1993 if (Viewport.x + Viewport.w + 64 < mapsize.x)
1994 Viewport.x += 64;
1995 else {
1996 Viewport.x = mapsize.x - Viewport.w;
1997 if (Viewport.x<0) Viewport.x=0;
1999 break;
2000 case GEM_ALT:
2001 DebugFlags |= DEBUG_SHOW_CONTAINERS;
2002 return;
2003 case GEM_TAB:
2004 // show partymember hp/maxhp as overhead text
2005 for (pm=0; pm < partysize; pm++) {
2006 Actor *pc = game->GetPC(pm, true);
2007 if (!pc) continue;
2008 memset(tmpstr, 0, 10);
2009 snprintf(tmpstr, 10, "%d/%d", pc->Modified[IE_HITPOINTS], pc->Modified[IE_MAXHITPOINTS]);
2010 pc->DisplayHeadText(strdup(tmpstr));
2012 return;
2013 case GEM_MOUSEOUT:
2014 moveX = 0;
2015 moveY = 0;
2016 return;
2017 case GEM_ESCAPE:
2018 core->GetGUIScriptEngine()->RunFunction("GUICommonWindows", "EmptyControls");
2019 core->SetEventFlag(EF_ACTION);
2020 return;
2021 case GEM_PGUP:
2022 core->GetGUIScriptEngine()->RunFunction("CommonWindow","OnIncreaseSize");
2023 return;
2024 case GEM_PGDOWN:
2025 core->GetGUIScriptEngine()->RunFunction("CommonWindow","OnDecreaseSize");
2026 return;
2027 default:
2028 return;
2030 if (ScreenFlags & SF_LOCKSCROLL) {
2031 moveX = 0;
2032 moveY = 0;
2034 else {
2035 // override any existing viewport moves which may be in progress
2036 core->timer->SetMoveViewPort( Viewport.x, Viewport.y, 0, false );
2037 // move it directly ourselves, since we might be paused
2038 core->GetVideoDriver()->MoveViewportTo( Viewport.x, Viewport.y );
2042 void GameControl::CalculateSelection(const Point &p)
2044 unsigned int i;
2045 Game* game = core->GetGame();
2046 Map* area = game->GetCurrentArea( );
2047 if (DrawSelectionRect) {
2048 if (p.x < StartX) {
2049 SelectionRect.w = StartX - p.x;
2050 SelectionRect.x = p.x;
2051 } else {
2052 SelectionRect.x = StartX;
2053 SelectionRect.w = p.x - StartX;
2055 if (p.y < StartY) {
2056 SelectionRect.h = StartY - p.y;
2057 SelectionRect.y = p.y;
2058 } else {
2059 SelectionRect.y = StartY;
2060 SelectionRect.h = p.y - StartY;
2062 Actor** ab;
2063 unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
2064 for (i = 0; i < highlighted.size(); i++)
2065 highlighted[i]->SetOver( false );
2066 highlighted.clear();
2067 if (count != 0) {
2068 for (i = 0; i < count; i++) {
2069 ab[i]->SetOver( true );
2070 highlighted.push_back( ab[i] );
2073 free( ab );
2074 } else {
2075 Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_ENEMY);
2076 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
2077 if (lastActor)
2078 lastActor->SetOver( false );
2079 if (!actor) {
2080 lastActorID = 0;
2081 } else {
2082 lastActorID = actor->globalID;
2083 actor->SetOver( true );
2088 void GameControl::SetCutSceneMode(bool active)
2090 if (active) {
2091 ScreenFlags |= (SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE);
2092 moveX = 0;
2093 moveY = 0;
2094 } else {
2095 ScreenFlags &= ~(SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE);
2099 //Change game window geometries when a new window gets deactivated
2100 void GameControl::HandleWindowHide(const char *WindowName, const char *WindowPosition)
2102 Variables* dict = core->GetDictionary();
2103 ieDword index;
2105 if (dict->Lookup( WindowName, index )) {
2106 if (index != (ieDword) -1) {
2107 Window* w = core->GetWindow( (unsigned short) index );
2108 if (w) {
2109 core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE );
2110 if (dict->Lookup( WindowPosition, index )) {
2111 ResizeDel( w, index );
2113 return;
2115 printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED);
2116 printf("%s:%u\n",WindowName, index);
2121 //Hide all other windows on the GUI (gamecontrol is not hidden by this)
2122 int GameControl::HideGUI()
2124 //hidegui is in effect
2125 if (!(ScreenFlags&SF_GUIENABLED) ) {
2126 return 0;
2128 //no gamecontrol visible
2129 if (Owner->Visible == WINDOW_INVISIBLE ) {
2130 return 0;
2132 ScreenFlags &=~SF_GUIENABLED;
2133 HandleWindowHide("PortraitWindow", "PortraitPosition");
2134 HandleWindowHide("OtherWindow", "OtherPosition");
2135 HandleWindowHide("TopWindow", "TopPosition");
2136 HandleWindowHide("OptionsWindow", "OptionsPosition");
2137 HandleWindowHide("MessageWindow", "MessagePosition");
2138 HandleWindowHide("ActionsWindow", "ActionsPosition");
2139 //FloatWindow doesn't affect gamecontrol, so it is special
2140 Variables* dict = core->GetDictionary();
2141 ieDword index;
2143 if (dict->Lookup( "FloatWindow", index )) {
2144 if (index != (ieDword) -1) {
2145 core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE );
2148 core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height );
2149 return 1;
2152 //Change game window geometries when a new window gets activated
2153 void GameControl::HandleWindowReveal(const char *WindowName, const char *WindowPosition)
2155 Variables* dict = core->GetDictionary();
2156 ieDword index;
2158 if (dict->Lookup( WindowName, index )) {
2159 if (index != (ieDword) -1) {
2160 Window* w = core->GetWindow( (unsigned short) index );
2161 if (w) {
2162 core->SetVisible( (unsigned short) index, WINDOW_VISIBLE );
2163 if (dict->Lookup( WindowPosition, index )) {
2164 ResizeAdd( w, index );
2166 return;
2168 printMessage("GameControl", "Invalid Window Index ", LIGHT_RED);
2169 printf("%s:%u\n",WindowName, index);
2174 //Reveal all windows on the GUI (including this one)
2175 int GameControl::UnhideGUI()
2177 if (ScreenFlags&SF_GUIENABLED) {
2178 return 0;
2181 ScreenFlags |= SF_GUIENABLED;
2182 // Unhide the gamecontrol window
2183 core->SetVisible( 0, WINDOW_VISIBLE );
2185 HandleWindowReveal("ActionsWindow", "ActionsPosition");
2186 HandleWindowReveal("MessageWindow", "MessagePosition");
2187 HandleWindowReveal("OptionsWindow", "OptionsPosition");
2188 HandleWindowReveal("TopWindow", "TopPosition");
2189 HandleWindowReveal("OtherWindow", "OtherPosition");
2190 HandleWindowReveal("PortraitWindow", "PortraitPosition");
2191 //the floatwindow is a special case
2192 Variables* dict = core->GetDictionary();
2193 ieDword index;
2195 if (dict->Lookup( "FloatWindow", index )) {
2196 if (index != (ieDword) -1) {
2197 Window* fw = core->GetWindow( (unsigned short) index );
2198 if (fw) {
2199 core->SetVisible( (unsigned short) index, WINDOW_VISIBLE );
2200 fw->Flags |=WF_FLOAT;
2201 core->SetOnTop( index );
2205 core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height );
2206 return 1;
2209 //a window got removed, so the GameControl gets enlarged
2210 void GameControl::ResizeDel(Window* win, int type)
2212 switch (type) {
2213 case 0: //Left
2214 if (LeftCount!=1) {
2215 printMessage("GameControl","More than one left window!\n",LIGHT_RED);
2217 LeftCount--;
2218 if (!LeftCount) {
2219 Owner->XPos -= win->Width;
2220 Owner->Width += win->Width;
2221 Width = Owner->Width;
2223 break;
2225 case 1: //Bottom
2226 if (BottomCount!=1) {
2227 printMessage("GameControl","More than one bottom window!\n",LIGHT_RED);
2229 BottomCount--;
2230 if (!BottomCount) {
2231 Owner->Height += win->Height;
2232 Height = Owner->Height;
2234 break;
2236 case 2: //Right
2237 if (RightCount!=1) {
2238 printMessage("GameControl","More than one right window!\n",LIGHT_RED);
2240 RightCount--;
2241 if (!RightCount) {
2242 Owner->Width += win->Width;
2243 Width = Owner->Width;
2245 break;
2247 case 3: //Top
2248 if (TopCount!=1) {
2249 printMessage("GameControl","More than one top window!\n",LIGHT_RED);
2251 TopCount--;
2252 if (!TopCount) {
2253 Owner->YPos -= win->Height;
2254 Owner->Height += win->Height;
2255 Height = Owner->Height;
2257 break;
2259 case 4: //BottomAdded
2260 BottomCount--;
2261 Owner->Height += win->Height;
2262 Height = Owner->Height;
2263 break;
2264 case 5: //Inactivating
2265 BottomCount--;
2266 Owner->Height += win->Height;
2267 Height = Owner->Height;
2268 break;
2272 //a window got added, so the GameControl gets shrunk
2273 //Owner is the GameControl's window
2274 //GameControl is the only control on that window
2275 void GameControl::ResizeAdd(Window* win, int type)
2277 switch (type) {
2278 case 0: //Left
2279 LeftCount++;
2280 if (LeftCount == 1) {
2281 Owner->XPos += win->Width;
2282 Owner->Width -= win->Width;
2283 Width = Owner->Width;
2285 break;
2287 case 1: //Bottom
2288 BottomCount++;
2289 if (BottomCount == 1) {
2290 Owner->Height -= win->Height;
2291 Height = Owner->Height;
2293 break;
2295 case 2: //Right
2296 RightCount++;
2297 if (RightCount == 1) {
2298 Owner->Width -= win->Width;
2299 Width = Owner->Width;
2301 break;
2303 case 3: //Top
2304 TopCount++;
2305 if (TopCount == 1) {
2306 Owner->YPos += win->Height;
2307 Owner->Height -= win->Height;
2308 Height = Owner->Height;
2310 break;
2312 case 4: //BottomAdded
2313 BottomCount++;
2314 Owner->Height -= win->Height;
2315 Height = Owner->Height;
2316 break;
2318 case 5: //Inactivating
2319 BottomCount++;
2320 Owner->Height -= win->Height;
2321 Height = 0;
2325 //Try to start dialogue between two actors (one of them could be inanimate)
2326 int GameControl::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgref)
2328 if (dlg) {
2329 delete dlg;
2330 dlg = NULL;
2333 PluginHolder<DialogMgr> dm(IE_DLG_CLASS_ID);
2334 dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true );
2335 dlg = dm->GetDialog();
2337 if (!dlg) {
2338 printMessage("GameControl", " ", LIGHT_RED);
2339 printf( "Cannot start dialog: %s\n", dlgref );
2340 return -1;
2343 strnlwrcpy(dlg->ResRef, dlgref, 8); //this isn't handled by GetDialog???
2345 //target is here because it could be changed when a dialog runs onto
2346 //and external link, we need to find the new target (whose dialog was
2347 //linked to)
2349 Actor *spe = (Actor *) spk;
2350 speakerID = spe->globalID;
2351 if (tgt->Type!=ST_ACTOR) {
2352 targetID=0xffff;
2353 //most likely this dangling object reference
2354 //won't cause problems, because trigger points don't
2355 //get destroyed during a dialog
2356 targetOB=tgt;
2357 spk->LastTalkedTo=0;
2358 } else {
2359 Actor *tar = (Actor *) tgt;
2360 speakerID = spe->globalID;
2361 targetID = tar->globalID;
2362 if (!originalTargetID) originalTargetID = tar->globalID;
2363 spe->LastTalkedTo=targetID;
2364 tar->LastTalkedTo=speakerID;
2367 //check if we are already in dialog
2368 if (DialogueFlags&DF_IN_DIALOG) {
2369 return 0;
2372 int si = dlg->FindFirstState( tgt );
2373 if (si < 0) {
2374 return -1;
2377 //we need GUI for dialogs
2378 UnhideGUI();
2380 //no exploring while in dialogue
2381 ScreenFlags |= SF_GUIENABLED|SF_DISABLEMOUSE|SF_LOCKSCROLL;
2382 DialogueFlags |= DF_IN_DIALOG;
2384 if (tgt->Type==ST_ACTOR) {
2385 Actor *tar = (Actor *) tgt;
2386 tar->DialogInterrupt();
2389 //allow mouse selection from dialog (even though screen is locked)
2390 core->GetVideoDriver()->SetMouseEnabled(true);
2391 core->timer->SetMoveViewPort( tgt->Pos.x, tgt->Pos.y, 0, true );
2392 //there are 3 bits, if they are all unset, the dialog freezes scripts
2393 if (!(dlg->Flags&7) ) {
2394 DialogueFlags |= DF_FREEZE_SCRIPTS;
2396 //opening control size to maximum, enabling dialog window
2397 core->GetGame()->SetControlStatus(CS_HIDEGUI, BM_NAND);
2398 core->GetGame()->SetControlStatus(CS_DIALOG, BM_OR);
2399 core->SetEventFlag(EF_PORTRAIT);
2400 return 0;
2403 /*try to break will only try to break it, false means unconditional stop*/
2404 void GameControl::EndDialog(bool try_to_break)
2406 if (try_to_break && (DialogueFlags&DF_UNBREAKABLE) ) {
2407 return;
2410 Actor *tmp = GetSpeaker();
2411 if (tmp) {
2412 tmp->LeaveDialog();
2414 speakerID = 0;
2415 if (targetID==0xffff) {
2416 targetOB->LeaveDialog();
2417 } else {
2418 tmp=GetTarget();
2419 if (tmp) {
2420 tmp->LeaveDialog();
2423 targetOB = NULL;
2424 targetID = 0;
2425 originalTargetID = 0;
2426 ds = NULL;
2427 if (dlg) {
2428 delete dlg;
2429 dlg = NULL;
2431 //restoring original size
2432 core->GetGame()->SetControlStatus(CS_DIALOG, BM_NAND);
2433 ScreenFlags &=~(SF_DISABLEMOUSE|SF_LOCKSCROLL);
2434 DialogueFlags = 0;
2435 core->SetEventFlag(EF_PORTRAIT);
2438 //translate section values (journal, solved, unsolved, user)
2439 static const int sectionMap[4]={4,1,2,0};
2441 void GameControl::DialogChoose(unsigned int choose)
2443 TextArea* ta = core->GetMessageTextArea();
2444 if (!ta) {
2445 printMessage("GameControl","Dialog aborted???",LIGHT_RED);
2446 EndDialog();
2447 return;
2450 Actor *speaker = GetSpeaker();
2451 if (!speaker) {
2452 printMessage("GameControl","Speaker gone???",LIGHT_RED);
2453 EndDialog();
2454 return;
2456 Actor *tgt;
2457 Scriptable *target;
2459 if (targetID!=0xffff) {
2460 tgt = GetTarget();
2461 target = tgt;
2462 } else {
2463 //risky!!!
2464 target = targetOB;
2465 tgt=NULL;
2467 if (!target) {
2468 printMessage("GameControl","Target gone???",LIGHT_RED);
2469 EndDialog();
2470 return;
2473 if (choose == (unsigned int) -1) {
2474 //increasing talkcount after top level condition was determined
2476 int si = dlg->FindFirstState( tgt );
2477 if (si<0) {
2478 EndDialog();
2479 return;
2482 if (tgt) {
2483 if (DialogueFlags&DF_TALKCOUNT) {
2484 DialogueFlags&=~DF_TALKCOUNT;
2485 tgt->TalkCount++;
2486 } else if (DialogueFlags&DF_INTERACT) {
2487 DialogueFlags&=~DF_INTERACT;
2488 tgt->InteractCount++;
2491 ds = dlg->GetState( si );
2492 } else {
2493 if (ds->transitionsCount <= choose) {
2494 return;
2497 DialogTransition* tr = ds->transitions[choose];
2499 ta->PopMinRow();
2501 if (tr->Flags&IE_DLG_TR_JOURNAL) {
2502 int Section = 0;
2503 if (tr->Flags&IE_DLG_UNSOLVED) {
2504 Section |= 1;
2506 if (tr->Flags&IE_DLG_SOLVED) {
2507 Section |= 2;
2509 if (core->GetGame()->AddJournalEntry(tr->journalStrRef, sectionMap[Section], tr->Flags>>16) ) {
2510 displaymsg->DisplayConstantString(STR_JOURNALCHANGE,0xffff00);
2511 char *string = core->GetString( tr->journalStrRef );
2512 //cutting off the strings at the first crlf
2513 char *poi = strchr(string,'\n');
2514 if (poi) {
2515 *poi='\0';
2517 displaymsg->DisplayString( string );
2518 free( string );
2522 if (tr->textStrRef != 0xffffffff) {
2523 //allow_zero is for PST (deionarra's text)
2524 displaymsg->DisplayStringName( (int) (tr->textStrRef), 0x8080FF, speaker, IE_STR_SOUND|IE_STR_SPEECH|IE_STR_ALLOW_ZERO);
2525 if (core->HasFeature( GF_DIALOGUE_SCROLLS )) {
2526 ta->AppendText( "", -1 );
2530 if (tr->actions.size()) {
2531 // does this belong here? we must clear actions somewhere before
2532 // we start executing them (otherwise queued actions interfere)
2533 // executing actions directly does not work, because dialog
2534 // needs to end before final actions are executed due to
2535 // actions making new dialogs!
2536 if (target->Type == ST_ACTOR) ((Movable *)target)->ClearPath(); // fuzzie added this
2537 target->ClearActions();
2539 for (unsigned int i = 0; i < tr->actions.size(); i++) {
2540 target->AddAction(tr->actions[i]);
2541 //GameScript::ExecuteAction( target, action );
2545 int final_dialog = tr->Flags & IE_DLG_TR_FINAL;
2547 if (final_dialog) {
2548 ta->SetMinRow( false );
2549 EndDialog();
2552 // all dialog actions must be executed immediately
2553 target->ProcessActions(true);
2554 // (do not clear actions - final actions can involve waiting/moving)
2556 if (final_dialog) {
2557 return;
2560 //displaying dialog for selected option
2561 int si = tr->stateIndex;
2562 //follow external linkage, if required
2563 if (tr->Dialog[0] && strnicmp( tr->Dialog, dlg->ResRef, 8 )) {
2564 //target should be recalculated!
2565 tgt = NULL;
2566 if (originalTargetID) {
2567 // always try original target first (sometimes there are multiple
2568 // actors with the same dialog in an area, we want to pick the one
2569 // we were talking to)
2570 tgt = GetActorByGlobalID(originalTargetID);
2571 if (tgt && strnicmp( tgt->GetDialog(GD_NORMAL), tr->Dialog, 8 ) != 0) {
2572 tgt = NULL;
2575 if (!tgt) {
2576 // then just search the current area for an actor with the dialog
2577 tgt = target->GetCurrentArea()->GetActorByDialog(tr->Dialog);
2579 target = tgt;
2580 if (!target) {
2581 printMessage("Dialog","Can't redirect dialog\n",YELLOW);
2582 ta->SetMinRow( false );
2583 EndDialog();
2584 return;
2586 targetID = tgt->globalID;
2587 // we have to make a backup, tr->Dialog is freed
2588 ieResRef tmpresref;
2589 strnlwrcpy(tmpresref,tr->Dialog, 8);
2590 if (target->GetInternalFlag()&IF_NOINT) {
2591 // this whole check moved out of InitDialog by fuzzie, see comments
2592 // for the IF_NOINT check in BeginDialog
2593 displaymsg->DisplayConstantString(STR_TARGETBUSY,0xff0000);
2594 ta->SetMinRow( false );
2595 EndDialog();
2596 return;
2598 int ret = InitDialog( speaker, target, tmpresref);
2599 if (ret<0) {
2600 // error was displayed by InitDialog
2601 ta->SetMinRow( false );
2602 EndDialog();
2603 return;
2606 ds = dlg->GetState( si );
2607 if (!ds) {
2608 printMessage("Dialog","Can't find next dialog\n",YELLOW);
2609 ta->SetMinRow( false );
2610 EndDialog();
2611 return;
2614 //displaying npc text
2615 displaymsg->DisplayStringName( ds->StrRef, 0x70FF70, target, IE_STR_SOUND|IE_STR_SPEECH);
2616 //adding a gap between options and npc text
2617 ta->AppendText("",-1);
2618 int i;
2619 int idx = 0;
2620 ta->SetMinRow( true );
2621 //first looking for a 'continue' opportunity, the order is descending (a la IE)
2622 unsigned int x = ds->transitionsCount;
2623 while(x--) {
2624 if (ds->transitions[x]->Flags & IE_DLG_TR_FINAL) {
2625 continue;
2627 if (ds->transitions[x]->textStrRef != 0xffffffff) {
2628 continue;
2630 if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) {
2631 if (ds->transitions[x]->condition &&
2632 !ds->transitions[x]->condition->Evaluate(target)) {
2633 continue;
2636 core->GetDictionary()->SetAt("DialogOption",x);
2637 DialogueFlags |= DF_OPENCONTINUEWINDOW;
2638 goto end_of_choose;
2640 for (x = 0; x < ds->transitionsCount; x++) {
2641 if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) {
2642 if (ds->transitions[x]->condition &&
2643 !ds->transitions[x]->condition->Evaluate(target)) {
2644 continue;
2647 idx++;
2648 if (ds->transitions[x]->textStrRef == 0xffffffff) {
2649 //dialogchoose should be set to x
2650 //it isn't important which END option was chosen, as it ends
2651 core->GetDictionary()->SetAt("DialogOption",x);
2652 DialogueFlags |= DF_OPENENDWINDOW;
2653 } else {
2654 char *string = ( char * ) malloc( 40 );
2655 sprintf( string, "[s=%d,ffffff,ff0000]%d - [p]", x, idx );
2656 i = ta->AppendText( string, -1 );
2657 free( string );
2658 string = core->GetString( ds->transitions[x]->textStrRef );
2659 ta->AppendText( string, i );
2660 free( string );
2661 ta->AppendText( "[/p][/s]", i );
2664 // this happens if a trigger isn't implemented or the dialog is wrong
2665 if (!idx) {
2666 printMessage("Dialog", "There were no valid dialog options!\n", YELLOW);
2667 DialogueFlags |= DF_OPENENDWINDOW;
2669 end_of_choose:
2670 //padding the rows so our text will be at the top
2671 if (core->HasFeature( GF_DIALOGUE_SCROLLS )) {
2672 ta->AppendText( "", -1 );
2674 else {
2675 ta->PadMinRow();
2679 //Create an overhead text over an arbitrary point
2680 void GameControl::DisplayString(const Point &p, const char *Text)
2682 Scriptable* scr = new Scriptable( ST_TRIGGER );
2683 scr->overHeadText = (char *) Text;
2684 scr->textDisplaying = 1;
2685 scr->timeStartDisplaying = 0;
2686 scr->Pos = p;
2687 scr->ClearCutsceneID( );
2690 //Create an overhead text over a scriptable target
2691 //Multiple texts are possible, as this code copies the text to a new object
2692 void GameControl::DisplayString(Scriptable* target)
2694 Scriptable* scr = new Scriptable( ST_TRIGGER );
2695 scr->overHeadText = strdup( target->overHeadText );
2696 /* strdup should work here, we use it elsewhere
2697 size_t len = strlen( target->overHeadText ) + 1;
2698 scr->overHeadText = ( char * ) malloc( len );
2699 strcpy( scr->overHeadText, target->overHeadText );
2701 scr->textDisplaying = 1;
2702 scr->timeStartDisplaying = target->timeStartDisplaying;
2703 scr->Pos = target->Pos;
2704 scr->SetCutsceneID( target );
2707 /** changes displayed map to the currently selected PC */
2708 void GameControl::ChangeMap(Actor *pc, bool forced)
2710 //swap in the area of the actor
2711 Game* game = core->GetGame();
2712 if (forced || (stricmp( pc->Area, game->CurrentArea) != 0) ) {
2713 EndDialog();
2714 overInfoPoint = NULL;
2715 overContainer = NULL;
2716 overDoor = NULL;
2717 /*this is loadmap, because we need the index, not the pointer*/
2718 char *areaname = game->CurrentArea;
2719 if (pc) {
2720 areaname = pc->Area;
2722 game->GetMap( areaname, true );
2723 ScreenFlags|=SF_CENTERONACTOR;
2725 //center on first selected actor
2726 Region vp = core->GetVideoDriver()->GetViewport();
2727 if (ScreenFlags&SF_CENTERONACTOR) {
2728 core->timer->SetMoveViewPort( pc->Pos.x, pc->Pos.y, 0, true );
2729 ScreenFlags&=~SF_CENTERONACTOR;
2733 void GameControl::SetScreenFlags(int value, int mode)
2735 switch(mode) {
2736 case BM_OR: ScreenFlags|=value; break;
2737 case BM_NAND: ScreenFlags&=~value; break;
2738 case BM_SET: ScreenFlags=value; break;
2739 case BM_AND: ScreenFlags&=value; break;
2740 case BM_XOR: ScreenFlags^=value; break;
2744 void GameControl::SetDialogueFlags(int value, int mode)
2746 switch(mode) {
2747 case BM_OR: DialogueFlags|=value; break;
2748 case BM_NAND: DialogueFlags&=~value; break;
2749 case BM_SET: DialogueFlags=value; break;
2750 case BM_AND: DialogueFlags&=value; break;
2751 case BM_XOR: DialogueFlags^=value; break;
2755 //copies a screenshot into a sprite
2756 Sprite2D* GameControl::GetScreenshot(bool show_gui)
2758 Sprite2D* screenshot;
2759 if (show_gui) {
2760 screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) );
2761 } else {
2762 int hf = HideGUI ();
2763 Draw (0, 0);
2764 screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) );
2765 if (hf) {
2766 UnhideGUI ();
2768 core->DrawWindows ();
2771 return screenshot;
2774 //copies a downscaled screenshot into a sprite for save game preview
2775 Sprite2D* GameControl::GetPreview()
2777 // We get preview by first taking a screenshot of size 640x405,
2778 // centered in the display. This is to get a decent picture for
2779 // higher screen resolutions.
2780 // FIXME: how do orig games solve that?
2781 Video* video = core->GetVideoDriver();
2782 int w = video->GetWidth();
2783 int h = video->GetHeight();
2784 int x = (w - 640) / 2;
2785 int y = (h - 405) / 2;
2787 if (x < 0) {
2788 x = 0;
2789 } else {
2790 w = 640;
2793 if (y < 0) {
2794 y = 0;
2795 } else {
2796 h = 405;
2799 if (!x)
2800 y = 0;
2802 int hf = HideGUI ();
2803 signed char v = Owner->Visible;
2804 Owner->Visible = WINDOW_VISIBLE;
2805 Draw (0, 0);
2806 Owner->Visible = v;
2807 Sprite2D *screenshot = video->GetScreenshot( Region(x, y, w, h) );
2808 if (hf) {
2809 UnhideGUI ();
2811 core->DrawWindows();
2813 Sprite2D* preview = video->SpriteScaleDown ( screenshot, 5 );
2814 video->FreeSprite( screenshot );
2815 return preview;
2820 * Returns PC portrait for a currently running game
2822 Sprite2D* GameControl::GetPortraitPreview(int pcslot)
2824 /** Portrait shrink ratio */
2825 // FIXME: this is just a random PST specific trait
2826 // you can make it less random with a new feature bit
2827 int ratio = (core->HasFeature( GF_ONE_BYTE_ANIMID )) ? 1 : 2;
2829 Video *video = core->GetVideoDriver();
2831 Actor *actor = core->GetGame()->GetPC( pcslot, false );
2832 if (! actor) {
2833 return NULL;
2835 ResourceHolder<ImageMgr> im(actor->GetPortrait(true));
2836 if (! im) {
2837 return NULL;
2840 Sprite2D* img = im->GetSprite2D();
2842 if (ratio == 1)
2843 return img;
2845 Sprite2D* img_scaled = video->SpriteScaleDown( img, ratio );
2846 video->FreeSprite( img );
2848 return img_scaled;
2851 Actor *GameControl::GetActorByGlobalID(ieWord ID)
2853 if (!ID)
2854 return NULL;
2855 Game* game = core->GetGame();
2856 if (!game)
2857 return NULL;
2859 Map* area = game->GetCurrentArea( );
2860 if (!area)
2861 return NULL;
2862 return
2863 area->GetActorByGlobalID(ID);
2866 Actor *GameControl::GetLastActor()
2868 return GetActorByGlobalID(lastActorID);
2871 Actor *GameControl::GetTarget()
2873 return GetActorByGlobalID(targetID);
2876 Actor *GameControl::GetSpeaker()
2878 return GetActorByGlobalID(speakerID);
2881 //Set up an item use which needs targeting
2882 //Slot is an inventory slot
2883 //header is the used item extended header
2884 //u is the user
2885 //target type is a bunch of GetActor flags that alter valid targets
2886 //cnt is the number of different targets (usually 1)
2887 void GameControl::SetupItemUse(int slot, int header, Actor *u, int targettype, int cnt)
2889 spellOrItem = -1;
2890 spellUser = u;
2891 spellSlot = slot;
2892 spellIndex = header;
2893 //item use also uses the casting icon, this might be changed in some custom game?
2894 target_mode = TARGET_MODE_CAST;
2895 target_types = targettype;
2896 spellCount = cnt;
2899 //Set up spell casting which needs targeting
2900 //type is the spell's type
2901 //level is the caster level
2902 //idx is the spell's number
2903 //u is the caster
2904 //target type is a bunch of GetActor flags that alter valid targets
2905 //cnt is the number of different targets (usually 1)
2906 void GameControl::SetupCasting(int type, int level, int idx, Actor *u, int targettype, int cnt)
2908 spellOrItem = type;
2909 spellUser = u;
2910 spellSlot = level;
2911 spellIndex = idx;
2912 target_mode = TARGET_MODE_CAST;
2913 target_types = targettype;
2914 spellCount = cnt;
2917 //another method inherited from Control which has no use here
2918 bool GameControl::SetEvent(int /*eventType*/, EventHandler /*handler*/)
2920 return false;
2923 void GameControl::SetDisplayText(char *text, unsigned int time)
2925 if (DisplayText) {
2926 core->FreeString(DisplayText);
2928 DisplayTextTime = time;
2929 DisplayText = text;
2932 void GameControl::SetDisplayText(ieStrRef text, unsigned int time)
2934 SetDisplayText(core->GetString(displaymsg->GetStringReference(text), 0), time);