Control: Change controls to use new callback infrastrucure.
[gemrb.git] / gemrb / core / GameControl.cpp
bloba9a43c3c6fb133563183559aac1fd8f5cb554bc0
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>
41 #define DEBUG_SHOW_INFOPOINTS 0x01
42 #define DEBUG_SHOW_CONTAINERS 0x02
43 #define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
44 #define DEBUG_SHOW_LIGHTMAP 0x08
46 static const Color cyan = {
47 0x00, 0xff, 0xff, 0xff
49 static const Color red = {
50 0xff, 0x00, 0x00, 0xff
52 static const Color magenta = {
53 0xff, 0x00, 0xff, 0xff
55 static const Color green = {
56 0x00, 0xff, 0x00, 0xff
59 static Color white = {
60 0xff, 0xff, 0xff, 0xff
63 static const Color black = {
64 0x00, 0x00, 0x00, 0xff
66 static const Color blue = {
67 0x00, 0x00, 0xff, 0x80
70 //Animation* effect;
72 #define FORMATIONSIZE 10
73 typedef Point formation_type[FORMATIONSIZE];
74 ieDword formationcount;
75 static formation_type *formations=NULL;
76 static bool mqs = false;
77 static ieResRef TestSpell="SPWI207";
79 //If one of the actors has tracking on, the gamecontrol needs to display
80 //arrow markers on the edges to point at detected monsters
81 //tracterID is the tracker actor's global ID
82 //distance is the detection distance
83 void GameControl::SetTracker(Actor *actor, ieDword dist)
85 trackerID = actor->GetID();
86 distance = dist;
89 //Multiple Quick saves is an experimental GemRB feature.
90 //multiple quick saves are kept, their age is determined by the slot
91 //number. There is an algorithm which keeps about log2(n) slots alive.
92 //The algorithm is implemented in SaveGameIterator
93 void GameControl::MultipleQuickSaves(int arg)
95 mqs=arg==1;
98 GameControl::GameControl(void)
100 if (!formations) {
101 ReadFormations();
103 //this is the default action, individual actors should have one too
104 //at this moment we use only this
105 //maybe we don't even need it
106 Changed = true;
107 spellCount = 0;
108 user = NULL;
109 lastActorID = 0;
110 trackerID = 0;
111 distance = 0;
112 MouseIsDown = false;
113 DrawSelectionRect = false;
114 overDoor = NULL;
115 overContainer = NULL;
116 overInfoPoint = NULL;
117 drawPath = NULL;
118 pfs.null();
119 lastCursor = IE_CURSOR_NORMAL;
120 moveX = moveY = 0;
121 scrolling = false;
122 numScrollCursor = 0;
123 DebugFlags = 0;
124 AIUpdateCounter = 1;
125 ieDword tmp=0;
127 target_mode = TARGET_MODE_NONE;
128 target_types = GA_SELECT|GA_NO_DEAD|GA_NO_HIDDEN;
130 core->GetDictionary()->Lookup("Center",tmp);
131 if (tmp) {
132 ScreenFlags=SF_ALWAYSCENTER|SF_CENTERONACTOR;
134 else {
135 ScreenFlags = SF_CENTERONACTOR;
137 LeftCount = 0;
138 BottomCount = 0;
139 RightCount = 0;
140 TopCount = 0;
141 DialogueFlags = 0;
142 dlg = NULL;
143 targetID = 0;
144 originalTargetID = 0;
145 speakerID = 0;
146 targetOB = NULL;
147 DisplayText = NULL;
150 //TODO:
151 //There could be a custom formation which is saved in the save game
152 //alternatively, all formations could be saved in some compatible way
153 //so it doesn't cause problems with the original engine
154 void GameControl::ReadFormations()
156 unsigned int i,j;
157 AutoTable tab("formatio");
158 if (!tab) {
159 // fallback
160 formationcount = 1;
161 formations = (formation_type *) calloc(1,sizeof(formation_type) );
162 return;
164 formationcount = tab->GetRowCount();
165 formations = (formation_type *) calloc(formationcount, sizeof(formation_type));
166 for(i=0; i<formationcount; i++) {
167 for(j=0;j<FORMATIONSIZE;j++) {
168 short k=(short) atoi(tab->QueryField(i,j*2));
169 formations[i][j].x=k;
170 k=(short) atoi(tab->QueryField(i,j*2+1));
171 formations[i][j].y=k;
176 //returns a single point offset for a formation
177 //formation: the formation type
178 //pos: the actor's slot ID
179 Point GameControl::GetFormationOffset(ieDword formation, ieDword pos)
181 if (formation>=formationcount) formation = 0;
182 if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1;
183 return formations[formation][pos];
186 //Moves an actor to a new position, keeping the current formation
187 //WARNING: don't pass p as a reference because it gets modified
188 void GameControl::MoveToPointFormation(Actor *actor, unsigned int pos, Point src, Point p)
190 Map* map = actor->GetCurrentArea() ;
192 int formation=core->GetGame()->GetFormation();
193 if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1;
195 // calculate angle
196 double angle;
197 double xdiff = src.x - p.x;
198 double ydiff = src.y - p.y;
199 if (ydiff == 0) {
200 if (xdiff > 0) {
201 angle = M_PI_2;
202 } else {
203 angle = -M_PI_2;
205 } else {
206 angle = atan(xdiff/ydiff);
207 if (ydiff < 0) angle += M_PI;
210 // calculate new coordinates by rotating formation around (0,0)
211 double newx = -formations[formation][pos].x * cos(angle) + formations[formation][pos].y * sin(angle);
212 double newy = formations[formation][pos].x * sin(angle) + formations[formation][pos].y * cos(angle);
213 p.x += (int)newx;
214 p.y += (int)newy;
216 if (p.x < 0) p.x = 8;
217 if (p.y < 0) p.y = 8;
218 if (p.x > map->GetWidth()*16) p.x = map->GetWidth()*16 - 8;
219 if (p.y > map->GetHeight()*12) p.y = map->GetHeight()*12 - 8;
221 if(map->GetCursor(p) == IE_CURSOR_BLOCKED) {
222 //we can't get there --> adjust position
223 p.x/=16;
224 p.y/=12;
225 map->AdjustPosition(p);
226 p.x*=16;
227 p.y*=12;
229 CreateMovement(actor, p);
232 // generate an action to do the actual movement
233 // only PST supports RunToPoint
234 void GameControl::CreateMovement(Actor *actor, const Point &p)
236 char Tmp[256];
238 Action *action = NULL;
239 if (DoubleClick) {
240 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
241 action = GenerateAction( Tmp );
243 if (!action)
245 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
246 action = GenerateAction( Tmp );
249 actor->AddAction( action );
250 // force action so that we get target recticles immediately
251 actor->ProcessActions(true);
254 GameControl::~GameControl(void)
256 //releasing the viewport of GameControl
257 core->GetVideoDriver()->SetViewport( 0,0,0,0 );
258 if (formations) {
259 free( formations );
260 formations = NULL;
262 if (dlg) {
263 delete dlg;
265 if (DisplayText) {
266 core->FreeString(DisplayText);
270 //Autosave was triggered by the GUI
271 void GameControl::AutoSave()
273 core->GetSaveGameIterator()->CreateSaveGame(0, false);
276 //QuickSave was triggered by the GUI
277 //mqs is the 'multiple quick saves' flag
278 void GameControl::QuickSave()
280 core->GetSaveGameIterator()->CreateSaveGame(1, mqs == 1);
283 // ArrowSprite cycles
284 // 321
285 // 4 0
286 // 567
288 #define D_LEFT 1
289 #define D_UP 2
290 #define D_RIGHT 4
291 #define D_BOTTOM 8
292 // Direction Bits
293 // 326
294 // 1 4
295 // 98c
297 static const int arrow_orientations[16]={
298 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
299 -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1
302 //Draws arrow markers along the edge of the game window
303 //WARNING:don't use reference for point, because it is altered
304 void GameControl::DrawArrowMarker(const Region &screen, Point p, const Region &viewport)
306 Video* video = core->GetVideoDriver();
308 //p.x-=viewport.x;
309 //p.y-=viewport.y;
310 ieDword draw = 0;
311 if (p.x<viewport.x) {
312 p.x=viewport.x;
313 draw|= D_LEFT;
315 if (p.y<viewport.y) {
316 p.y=viewport.y;
317 draw |= D_UP;
319 int tmp;
321 Sprite2D *spr = core->GetScrollCursorSprite(0,0);
323 tmp = spr->Width;
324 //tmp = core->ArrowSprites[0]->Width;
326 if (p.x>viewport.x+viewport.w-tmp) {
327 p.x=viewport.x+viewport.w;//-tmp;
328 draw |= D_RIGHT;
331 tmp = spr->Height;
332 //tmp = core->ArrowSprites[0]->Height;
334 if (p.y>viewport.y+viewport.h-tmp) {
335 p.y=viewport.y+viewport.h;//-tmp;
336 draw |= D_BOTTOM;
338 if (arrow_orientations[draw]>=0) {
339 video->BlitGameSprite( core->GetScrollCursorSprite(arrow_orientations[draw], 0), p.x+screen.x, p.y+screen.y, 0, black, NULL);
343 /** Draws the Control on the Output Display */
344 void GameControl::Draw(unsigned short x, unsigned short y)
346 bool update_scripts = !(DialogueFlags & DF_FREEZE_SCRIPTS);
348 Game* game = core->GetGame();
349 if (!game)
350 return;
352 if (((short) Width) <=0 || ((short) Height) <= 0) {
353 return;
356 if (Owner->Visible!=WINDOW_VISIBLE) {
357 return;
360 Video* video = core->GetVideoDriver();
361 Region viewport = video->GetViewport();
362 if (moveX || moveY) {
363 viewport.x += moveX;
364 viewport.y += moveY;
365 Point mapsize = core->GetGame()->GetCurrentArea()->TMap->GetMapSize();
366 if ( viewport.x < 0 )//if we are at top of the map
367 viewport.x = 0;
368 else if ( (viewport.x + viewport.w) >= mapsize.x) //if we are at the bottom
369 viewport.x = mapsize.x - viewport.w;
371 if ( viewport.y < 0 ) //if we are at the left of the map
372 viewport.y = 0;
373 else if ( (viewport.y + viewport.h ) >= mapsize.y ) //if we are at the right
374 viewport.y = mapsize.y - viewport.h;
376 // override any existing viewport moves which may be in progress
377 core->timer->SetMoveViewPort( viewport.x, viewport.y, 0, false );
378 // move it directly ourselves, since we might be paused
379 video->MoveViewportTo( viewport.x, viewport.y );
381 Region screen( x + XPos, y + YPos, Width, Height );
382 Map* area = game->GetCurrentArea( );
383 if (!area) {
384 video->DrawRect( screen, blue, true );
385 return;
387 video->DrawRect( screen, black, true );
389 // setup outlines
390 InfoPoint *i;
391 unsigned int idx;
392 for (idx = 0; (i = area->TMap->GetInfoPoint( idx )); idx++) {
393 i->Highlight = false;
394 if (overInfoPoint == i && target_mode) {
395 if (i->VisibleTrap(0)) {
396 i->outlineColor = green;
397 i->Highlight = true;
398 continue;
401 if (i->VisibleTrap(DebugFlags & DEBUG_SHOW_INFOPOINTS)) {
402 i->outlineColor = red; // traps
403 } else if (DebugFlags & DEBUG_SHOW_INFOPOINTS) {
404 i->outlineColor = blue; // debug infopoints
405 } else {
406 continue;
408 i->Highlight = true;
411 Door *d;
412 for (idx = 0; (d = area->TMap->GetDoor( idx )); idx++) {
413 d->Highlight = false;
414 if (overDoor == d) {
415 if (target_mode) {
416 if (d->VisibleTrap(0) || (d->Flags & DOOR_LOCKED)) {
417 // only highlight targettable doors
418 d->outlineColor = green;
419 d->Highlight = true;
420 continue;
422 } else if (!(d->Flags & DOOR_SECRET)) {
423 // mouse over, not in target mode, no secret door
424 d->outlineColor = cyan;
425 d->Highlight = true;
426 continue;
429 if (d->VisibleTrap(0)) {
430 d->outlineColor = red; // traps
431 } else if (d->Flags & DOOR_SECRET) {
432 if (DebugFlags & DEBUG_SHOW_DOORS || d->Flags & DOOR_FOUND) {
433 d->outlineColor = magenta; // found hidden door
434 } else {
435 // secret door is invisible
436 continue;
438 } else if (DebugFlags & DEBUG_SHOW_DOORS) {
439 d->outlineColor = cyan; // debug doors
440 } else {
441 continue;
443 d->Highlight = true;
446 Container *c;
447 for (idx = 0; (c = area->TMap->GetContainer( idx )); idx++) {
448 c->Highlight = false;
449 if (overContainer == c && target_mode) {
450 if (c->VisibleTrap(0) || (c->Flags & CONT_LOCKED)) {
451 // only highlight targettable containers
452 c->outlineColor = green;
453 c->Highlight = true;
454 continue;
456 } else if (overContainer == c) {
457 // mouse over, not in target mode
458 c->outlineColor = cyan;
459 c->Highlight = true;
460 continue;
462 if (c->VisibleTrap(0)) {
463 c->outlineColor = red; // traps
464 } else if (DebugFlags & DEBUG_SHOW_CONTAINERS) {
465 c->outlineColor = cyan; // debug containers
466 } else {
467 continue;
469 c->Highlight = true;
472 //drawmap should be here so it updates fog of war
473 area->DrawMap( screen );
474 game->DrawWeather(screen, update_scripts);
476 if (trackerID) {
477 Actor *actor = area->GetActorByGlobalID(trackerID);
479 if (actor) {
480 Actor **monsters = area->GetAllActorsInRadius(actor->Pos, GA_NO_DEAD, distance);
482 int i = 0;
483 while(monsters[i]) {
484 Actor *target = monsters[i++];
485 if (target->InParty) continue;
486 if (target->GetStat(IE_NOTRACKING)) continue;
487 DrawArrowMarker(screen, target->Pos, viewport);
489 delete monsters;
490 } else {
491 trackerID = 0;
495 if (ScreenFlags & SF_DISABLEMOUSE)
496 return;
497 Point p(lastMouseX, lastMouseY);
498 video->ConvertToGame( p.x, p.y );
500 // Draw selection rect
501 if (DrawSelectionRect) {
502 CalculateSelection( p );
503 video->DrawRect( SelectionRect, green, false, true );
506 // Show wallpolygons
507 if (DebugFlags & DEBUG_SHOW_INFOPOINTS) {
509 unsigned int count = area->GetWallCount();
510 for (unsigned int i = 0; i < count; ++i) {
511 Wall_Polygon* poly = area->GetWallGroup(i);
512 if (!poly) continue;
513 // yellow
514 Color c;
515 c.r = 0x7F;
516 c.g = 0x7F;
517 c.b = 0;
518 c.a = 0;
519 //if polygon is disabled, make it grey
520 if (poly->wall_flag&WF_DISABLED) {
521 c.b = 0x7F;
524 video->DrawPolyline( poly, c, true );
528 // Draw path
529 if (drawPath) {
530 PathNode* node = drawPath;
531 while (true) {
532 Point p( ( node-> x*16) + 8, ( node->y*12 ) + 6 );
533 if (!node->Parent) {
534 video->DrawCircle( p.x, p.y, 2, red );
535 } else {
536 short oldX = ( node->Parent-> x*16) + 8, oldY = ( node->Parent->y*12 ) + 6;
537 video->DrawLine( oldX, oldY, p.x, p.y, green );
539 if (!node->Next) {
540 video->DrawCircle( p.x, p.y, 2, green );
541 break;
543 node = node->Next;
547 // Draw lightmap
548 if (DebugFlags & DEBUG_SHOW_LIGHTMAP) {
549 Sprite2D* spr = area->LightMap->GetSprite2D();
550 video->BlitSprite( spr, 0, 0, true );
551 video->FreeSprite( spr );
552 Region point( p.x / 16, p.y / 12, 2, 2 );
553 video->DrawRect( point, red );
556 if (core->HasFeature(GF_ONSCREEN_TEXT) && DisplayText) {
557 core->GetFont(1)->Print(screen, (unsigned char *)DisplayText, core->InfoTextPalette, IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true);
558 if (update_scripts) {
559 // just replicating original engine behaviour
560 if (DisplayTextTime == 0) {
561 SetDisplayText((char *)NULL, 0);
562 } else {
563 DisplayTextTime--;
569 /** inherited from Control, GameControl doesn't need it */
570 int GameControl::SetText(const char* /*string*/, int /*pos*/)
572 return 0;
575 /** Key Press Event */
576 void GameControl::OnKeyPress(unsigned char Key, unsigned short /*Mod*/)
578 if (DialogueFlags&DF_IN_DIALOG) {
579 return;
581 unsigned int i;
582 Game* game = core->GetGame();
583 if (!game) return;
585 switch (Key) {
586 case '0':
587 game->SelectActor( NULL, false, SELECT_NORMAL );
588 i = game->GetPartySize(false)/2;
589 while(i--) {
590 SelectActor(i, true);
592 break;
593 case '-':
594 game->SelectActor( NULL, true, SELECT_NORMAL );
595 i = game->GetPartySize(false)/2;
596 while(i--) {
597 SelectActor(i, false);
599 break;
600 case '=':
601 SelectActor(-1);
602 break;
603 case '1':
604 case '2':
605 case '3':
606 case '4':
607 case '5':
608 case '6':
609 case '7':
610 case '8':
611 case '9':
612 SelectActor(Key-'0');
613 break;
614 default:
615 core->GetGame()->SetHotKey(toupper(Key));
616 break;
620 //Select (or deselect) a new actor (or actors)
621 void GameControl::SelectActor(int whom, int type)
623 Game* game = core->GetGame();
624 if (whom==-1) {
625 game->SelectActor( NULL, true, SELECT_NORMAL );
626 return;
629 /* doesn't fall through here */
630 Actor* actor = game->FindPC( whom );
631 if (!actor)
632 return;
634 if (type==0) {
635 game->SelectActor( actor, false, SELECT_NORMAL );
636 return;
638 if (type==1) {
639 game->SelectActor( actor, true, SELECT_NORMAL );
640 return;
643 bool was_selected = actor->IsSelected();
644 if (game->SelectActor( actor, true, SELECT_REPLACE ))
645 if (was_selected || (ScreenFlags & SF_ALWAYSCENTER)) {
646 ScreenFlags |= SF_CENTERONACTOR;
650 //Effect for the ctrl-r cheatkey (resurrect)
651 static EffectRef heal_ref={"CurrentHPModifier", NULL, -1};
652 static EffectRef damage_ref={"Damage", NULL, -1};
654 /** Key Release Event */
655 void GameControl::OnKeyRelease(unsigned char Key, unsigned short Mod)
657 unsigned int i;
658 Game* game = core->GetGame();
660 if (!game)
661 return;
663 if (DialogueFlags&DF_IN_DIALOG) {
664 if (Mod) return;
665 switch(Key) {
666 case '1':
667 case '2':
668 case '3':
669 case '4':
670 case '5':
671 case '6':
672 case '7':
673 case '8':
674 case '9':
676 TextArea *ta = core->GetMessageTextArea();
677 if (ta) {
678 ta->OnKeyPress(Key,Mod);
681 break;
683 return;
685 //cheatkeys with ctrl-
686 if (Mod & GEM_MOD_CTRL) {
687 if (!core->CheatEnabled()) {
688 return;
690 Map* area = game->GetCurrentArea( );
691 if (!area)
692 return;
693 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
694 Point p(lastMouseX, lastMouseY);
695 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
696 switch (Key) {
697 case 'f': //toggle full screen mode
698 core->GetVideoDriver()->ToggleFullscreenMode();
699 break;
700 case 'd': //disarm a trap
701 if (overInfoPoint) {
702 overInfoPoint->DetectTrap(256);
704 if (overContainer) {
705 if (overContainer->Trapped &&
706 !( overContainer->TrapDetected )) {
707 overContainer->TrapDetected = 1;
710 if (overDoor) {
711 if (overDoor->Trapped &&
712 !( overDoor->TrapDetected )) {
713 overDoor->TrapDetected = 1;
716 break;
717 case 'l': //play an animation (vvc/bam) over an actor
718 //the original engine was able to swap through all animations
719 if (lastActor) {
720 lastActor->AddAnimation("S056ICBL", 0, 0, 0);
722 break;
724 case 'c': //force cast a hardcoded spell
725 //caster is the last selected actor
726 //target is the door/actor currently under the pointer
727 if (game->selected.size() > 0) {
728 Actor *src = game->selected[0];
729 Scriptable *target = lastActor;
730 if (overDoor) {
731 target = overDoor;
733 if (target) {
734 src->CastSpell( TestSpell, target, false );
735 if (src->LastTarget) {
736 src->CastSpellEnd( TestSpell );
737 } else {
738 src->CastSpellPointEnd( TestSpell );
742 break;
744 case 'b': //draw a path to the target (pathfinder debug)
745 //You need to select an origin with ctrl-o first
746 if (drawPath) {
747 PathNode* nextNode = drawPath->Next;
748 PathNode* thisNode = drawPath;
749 while (true) {
750 delete( thisNode );
751 thisNode = nextNode;
752 if (!thisNode)
753 break;
754 nextNode = thisNode->Next;
757 drawPath = core->GetGame()->GetCurrentArea()->FindPath( pfs, p, lastActor?lastActor->size:1 );
759 break;
761 case 'o': //set up the origin for the pathfinder
762 // origin
763 pfs.x = lastMouseX;
764 pfs.y = lastMouseY;
765 core->GetVideoDriver()->ConvertToGame( pfs.x, pfs.y );
766 break;
767 case 'a': //switches through the avatar animations
768 if (lastActor) {
769 lastActor->GetNextAnimation();
771 break;
772 case 's': //switches through the stance animations
773 if (lastActor) {
774 lastActor->GetNextStance();
776 break;
777 case 'j': //teleports the selected actors
778 for (i = 0; i < game->selected.size(); i++) {
779 Actor* actor = game->selected[i];
780 MoveBetweenAreasCore(actor, core->GetGame()->CurrentArea, p, -1, true);
781 printf( "Teleported to %d, %d\n", p.x, p.y );
783 break;
785 case 'm': //prints a debug dump (ctrl-m in the original game too)
786 if (!lastActor) {
787 lastActor = area->GetActor( p, GA_DEFAULT);
789 if (!lastActor) {
790 // ValidTarget never returns immobile targets, making debugging a nightmare
791 // so if we don't have an actor, we make really really sure by checking manually
792 unsigned int count = area->GetActorCount(true);
793 while (count--) {
794 Actor *actor = area->GetActor(count, true);
795 if (actor->IsOver(p)) {
796 actor->DebugDump();
800 if (lastActor) {
801 lastActor->DebugDump();
802 break;
804 if (overDoor) {
805 overDoor->DebugDump();
806 break;
808 if (overContainer) {
809 overContainer->DebugDump();
810 break;
812 if (overInfoPoint) {
813 overInfoPoint->DebugDump();
814 break;
816 core->GetGame()->GetCurrentArea()->DebugDump(Mod & GEM_MOD_SHIFT);
817 break;
818 case 'v': //marks some of the map visited (random vision distance)
819 area->ExploreMapChunk( p, rand()%30, 1 );
820 break;
821 case 'x': // shows coordinates on the map
822 printf( "%s [%d.%d]\n", area->GetScriptName(), p.x, p.y );
823 break;
824 case 'g'://shows loaded areas and other game information
825 game->DebugDump();
826 break;
827 case 'i'://interact trigger (from the original game)
828 if (!lastActor) {
829 lastActor = area->GetActor( p, GA_DEFAULT);
831 if (lastActor && !(lastActor->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE)) {
832 Actor *target;
833 int i = game->GetPartySize(true);
834 if(i<2) break;
835 i=rand()%i;
838 target = game->GetPC(i, true);
839 if(target==lastActor) continue;
840 if(target->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE) continue;
842 char Tmp[40];
843 snprintf(Tmp,sizeof(Tmp),"Interact(\"%s\")",target->GetScriptName() );
844 lastActor->AddAction(GenerateAction(Tmp));
845 break;
847 while(i--);
849 break;
850 case 'r'://resurrects actor
851 if (!lastActor) {
852 lastActor = area->GetActor( p, GA_DEFAULT);
854 if (lastActor) {
855 Effect *fx = EffectQueue::CreateEffect(heal_ref, lastActor->GetBase(IE_MAXHITPOINTS), 0x30001, FX_DURATION_INSTANT_PERMANENT);
856 if (fx) {
857 core->ApplyEffect(fx, lastActor, lastActor);
860 break;
861 case 't'://advances time
862 // 7200 (one day) /24 (hours) == 300
863 game->AdvanceTime(300*AI_UPDATE_TIME);
864 //refresh gui here once we got it
865 break;
867 case 'q': //joins actor to the party
868 if (lastActor && !lastActor->InParty) {
869 lastActor->ClearActions();
870 lastActor->ClearPath();
871 char Tmp[40];
872 strncpy(Tmp,"JoinParty()",sizeof(Tmp) );
873 lastActor->AddAction( GenerateAction(Tmp) );
875 break;
876 case 'p': //center on actor
877 ScreenFlags|=SF_CENTERONACTOR;
878 ScreenFlags^=SF_ALWAYSCENTER;
879 break;
880 case 'k': //kicks out actor
881 if (lastActor && lastActor->InParty) {
882 lastActor->ClearActions();
883 lastActor->ClearPath();
884 char Tmp[40];
885 strncpy(Tmp,"LeaveParty()",sizeof(Tmp) );
886 lastActor->AddAction( GenerateAction(Tmp) );
888 break;
889 case 'y': //kills actor or all enemies
890 if (Mod & GEM_MOD_SHIFT) {
891 // mwahaha!
892 Effect *newfx;
893 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT);
894 Actor *victim;
895 for (int i = area->GetActorCount(0)-1; i >= 0; i--) {
896 victim = area->GetActor(i, 0);
897 if (victim->Modified[IE_EA] == EA_ENEMY) {
898 core->ApplyEffect(newfx, victim, victim);
901 delete newfx;
902 } else {
903 if (lastActor) {
904 //using action so the actor is killed
905 //correctly (synchronisation)
906 lastActor->ClearActions();
907 lastActor->ClearPath();
909 Effect *newfx;
910 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT);
911 core->ApplyEffect(newfx, lastActor, lastActor);
912 if (! (lastActor->GetInternalFlag() & IF_REALLYDIED)) {
913 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_ACID<<16, FX_DURATION_INSTANT_PERMANENT);
914 core->ApplyEffect(newfx, lastActor, lastActor);
915 newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_CRUSHING<<16, FX_DURATION_INSTANT_PERMANENT);
916 core->ApplyEffect(newfx, lastActor, lastActor);
918 delete newfx;
919 } else if (overContainer) {
920 overContainer->SetContainerLocked(0);
921 } else if (overDoor) {
922 overDoor->SetDoorLocked(0,0);
925 break;
926 case 'z': //shift through the avatar animations backward
927 if (lastActor) {
928 lastActor->GetPrevAnimation();
930 break;
931 case '1': //change paperdoll armour level
932 if (! lastActor)
933 break;
934 lastActor->NewStat(IE_ARMOR_TYPE,1,MOD_ADDITIVE);
935 break;
936 case '4': //show all traps and infopoints
937 DebugFlags ^= DEBUG_SHOW_INFOPOINTS;
938 printf("Show traps and infopoints %s\n", DebugFlags & DEBUG_SHOW_INFOPOINTS ? "ON" : "OFF");
939 break;
940 case '6': //show the lightmap
941 DebugFlags ^= DEBUG_SHOW_LIGHTMAP;
942 printf("Show lightmap %s\n", DebugFlags & DEBUG_SHOW_LIGHTMAP ? "ON" : "OFF");
943 break;
944 case '7': //toggles fog of war
945 core->FogOfWar ^= 1;
946 printf("Show Fog-Of-War: %s\n", core->FogOfWar & 1 ? "ON" : "OFF");
947 break;
948 case '8': //show searchmap over area
949 core->FogOfWar ^= 2;
950 printf("Show searchmap %s\n", core->FogOfWar & 2 ? "ON" : "OFF");
951 break;
952 default:
953 printf( "KeyRelease:%d - %d\n", Key, Mod );
954 break;
956 return; //return from cheatkeys
958 switch (Key) {
959 case 'h': //hard pause
960 if (DialogueFlags & DF_FREEZE_SCRIPTS) break;
961 //fallthrough
962 case ' ': //soft pause
963 DialogueFlags ^= DF_FREEZE_SCRIPTS;
964 if (DialogueFlags&DF_FREEZE_SCRIPTS) {
965 core->DisplayConstantString(STR_PAUSED,0xff0000);
966 SetDisplayText(STR_PAUSED, 0); // time 0 = removed instantly on unpause
967 } else {
968 core->DisplayConstantString(STR_UNPAUSED,0xff0000);
970 break;
971 case 'm':
972 core->GetGUIScriptEngine()->RunFunction("OpenMapWindow");
973 break;
974 case 'j':
975 core->GetGUIScriptEngine()->RunFunction("OpenJournalWindow");
976 break;
977 case 'i':
978 core->GetGUIScriptEngine()->RunFunction("OpenInventoryWindow");
979 break;
980 case 'r':
981 core->GetGUIScriptEngine()->RunFunction("OpenRecordsWindow");
982 break;
983 case 'q': //quicksave
984 QuickSave();
985 break;
986 case GEM_ALT: //alt key (shows containers)
987 DebugFlags &= ~DEBUG_SHOW_CONTAINERS;
988 break;
989 default:
990 break;
994 void GameControl::DisplayTooltip() {
995 Game* game = core->GetGame();
996 if (game) {
997 Map* area = game->GetCurrentArea( );
998 if (area) {
999 Actor *actor = area->GetActorByGlobalID(lastActorID);
1000 if (actor && (actor->GetStat(IE_STATE_ID)&STATE_DEAD || actor->GetInternalFlag()&IF_JUSTDIED)) {
1001 // checking IF_JUSTDIED is kind of horrid, but seems necessary
1002 // no tooltips for dead actors!
1003 actor->SetOver( false );
1004 lastActorID = 0;
1005 actor = NULL;
1008 if (actor) {
1009 char *name = actor->GetName(-1);
1010 int hp = actor->GetStat(IE_HITPOINTS);
1011 int maxhp = actor->GetStat(IE_MAXHITPOINTS);
1013 char buffer[100];
1014 if (!core->TooltipBack) {
1015 // single-line tooltips without background (PS:T)
1016 if (actor->InParty) {
1017 snprintf(buffer, 100, "%s: %d/%d", name, hp, maxhp);
1018 } else {
1019 snprintf(buffer, 100, "%s", name);
1021 } else {
1022 // a guess at a neutral check
1023 bool neutral = actor->GetStat(IE_EA) == EA_NEUTRAL;
1024 // test for an injured string being present for this game
1025 int strindex = core->GetStringReference(STR_UNINJURED);
1026 // normal tooltips
1027 if (actor->InParty) {
1028 // in party: display hp
1029 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1030 } else if (neutral) {
1031 // neutral: display name only
1032 snprintf(buffer, 100, "%s", name);
1033 } else if (strindex == -1) {
1034 // non-neutral, not in party, no injured strings: display hp
1035 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1036 } else {
1037 // non-neutral, not in party: display injured string
1038 int strindex;
1039 char *injuredstring = NULL;
1040 // these boundaries are just a guess
1041 if (hp == maxhp) {
1042 strindex = STR_UNINJURED;
1043 } else if (hp > (maxhp*3)/4) {
1044 strindex = STR_INJURED1;
1045 } else if (hp > maxhp/2) {
1046 strindex = STR_INJURED2;
1047 } else if (hp > maxhp/3) {
1048 strindex = STR_INJURED3;
1049 } else {
1050 strindex = STR_INJURED4;
1052 strindex = core->GetStringReference(strindex);
1053 if (strindex != -1) {
1054 injuredstring = core->GetString(strindex, 0);
1057 if (!injuredstring) {
1058 // eek, where did the string go?
1059 snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp);
1060 } else {
1061 snprintf(buffer, 100, "%s\n%s", name, injuredstring);
1062 free(injuredstring);
1067 Point p = actor->Pos;
1068 core->GetVideoDriver()->ConvertToScreen( p.x, p.y );
1069 p.x += Owner->XPos + XPos;
1070 p.y += Owner->YPos + YPos;
1072 // hack to position text above PS:T actors
1073 if (!core->TooltipBack) p.y -= actor->size*50;
1075 // we should probably cope better with moving actors
1076 SetTooltip(buffer);
1077 core->DisplayTooltip(p.x, p.y, this);
1078 return;
1083 SetTooltip(NULL);
1084 core->DisplayTooltip(0, 0, NULL);
1085 return;
1088 //returns the appropriate cursor over an active region (trap, infopoint, travel region)
1089 int GameControl::GetCursorOverInfoPoint(InfoPoint *overInfoPoint)
1091 if (target_mode == TARGET_MODE_PICK) {
1092 if (overInfoPoint->VisibleTrap(0)) {
1093 return IE_CURSOR_TRAP;
1096 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1098 // traps always display a walk cursor?
1099 if (overInfoPoint->Type == ST_PROXIMITY) {
1100 return IE_CURSOR_WALK;
1102 return overInfoPoint->Cursor;
1105 //returns the appropriate cursor over a door
1106 int GameControl::GetCursorOverDoor(Door *overDoor)
1108 if (target_mode == TARGET_MODE_PICK) {
1109 if (overDoor->VisibleTrap(0)) {
1110 return IE_CURSOR_TRAP;
1112 if (overDoor->Flags & DOOR_LOCKED) {
1113 return IE_CURSOR_LOCK;
1116 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1118 return overDoor->Cursor;
1121 //returns the appropriate cursor over a container (or pile)
1122 int GameControl::GetCursorOverContainer(Container *overContainer)
1124 if (target_mode == TARGET_MODE_PICK) {
1125 if (overContainer->VisibleTrap(0)) {
1126 return IE_CURSOR_TRAP;
1128 if (overContainer->Flags & CONT_LOCKED) {
1129 return IE_CURSOR_LOCK2;
1132 return IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1134 return IE_CURSOR_TAKE;
1137 /** Mouse Over Event */
1138 void GameControl::OnMouseOver(unsigned short x, unsigned short y)
1140 if (ScreenFlags & SF_DISABLEMOUSE) {
1141 return;
1144 lastMouseX = x;
1145 lastMouseY = y;
1146 Point p( x,y );
1147 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
1148 if (MouseIsDown && ( !DrawSelectionRect )) {
1149 if (( abs( p.x - StartX ) > 5 ) || ( abs( p.y - StartY ) > 5 )) {
1150 DrawSelectionRect = true;
1153 Game* game = core->GetGame();
1154 if (!game) return;
1155 Map* area = game->GetCurrentArea( );
1156 if (!area) return;
1157 int nextCursor = area->GetCursor( p );
1158 //make the invisible area really invisible
1159 if (nextCursor == IE_CURSOR_INVALID) {
1160 Owner->Cursor = IE_CURSOR_BLOCKED;
1161 lastCursor = IE_CURSOR_BLOCKED;
1162 return;
1165 overInfoPoint = area->TMap->GetInfoPoint( p, true );
1166 if (overInfoPoint) {
1167 //nextCursor = overInfoPoint->Cursor;
1168 nextCursor = GetCursorOverInfoPoint(overInfoPoint);
1171 if (overDoor) {
1172 overDoor->Highlight = false;
1174 if (overContainer) {
1175 overContainer->Highlight = false;
1177 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
1178 if (lastActor) {
1179 lastActor->SetOver( false );
1182 overDoor = area->TMap->GetDoor( p );
1183 overContainer = area->TMap->GetContainer( p );
1185 if (!DrawSelectionRect) {
1186 if (overDoor) {
1187 nextCursor = GetCursorOverDoor(overDoor);
1190 if (overContainer) {
1191 nextCursor = GetCursorOverContainer(overContainer);
1194 Actor *prevActor = lastActor;
1195 lastActor = area->GetActor( p, target_types);
1196 if (lastActor != prevActor) {
1197 // we store prevActor so we can remove the tooltip on actor change
1198 // (maybe we should be checking this and actor movements every frame?)
1199 SetTooltip(NULL);
1200 core->DisplayTooltip(0, 0, this);
1203 if ((target_types & GA_NO_SELF) && lastActor ) {
1204 if (lastActor == core->GetFirstSelectedPC(false)) {
1205 lastActor=NULL;
1209 if (lastActor) {
1210 lastActorID = lastActor->globalID;
1211 lastActor->SetOver( true );
1212 ieDword type = lastActor->GetStat(IE_EA);
1213 if (type >= EA_EVILCUTOFF || type == EA_GOODBUTRED) {
1214 nextCursor = IE_CURSOR_ATTACK;
1215 } else if ( type > EA_CHARMED ) {
1216 nextCursor = IE_CURSOR_TALK;
1217 } else {
1218 nextCursor = IE_CURSOR_NORMAL;
1220 } else {
1221 lastActorID = 0;
1224 if (target_mode == TARGET_MODE_TALK) {
1225 nextCursor = IE_CURSOR_TALK;
1226 if (!lastActor) {
1227 nextCursor |= IE_CURSOR_GRAY;
1229 } else if (target_mode == TARGET_MODE_ATTACK) {
1230 nextCursor = IE_CURSOR_ATTACK;
1231 if (!lastActor && !overDoor && !overContainer) {
1232 nextCursor |= IE_CURSOR_GRAY;
1234 } else if (target_mode == TARGET_MODE_CAST) {
1235 nextCursor = IE_CURSOR_CAST;
1236 //point is always valid
1237 if (!(target_types & GA_POINT)) {
1238 if(!lastActor) {
1239 nextCursor |= IE_CURSOR_GRAY;
1242 } else if (target_mode == TARGET_MODE_DEFEND) {
1243 nextCursor = IE_CURSOR_DEFEND;
1244 if(!lastActor) {
1245 nextCursor |= IE_CURSOR_GRAY;
1247 } else if (target_mode == TARGET_MODE_PICK) {
1248 if (lastActor) {
1249 nextCursor = IE_CURSOR_PICK;
1250 } else {
1251 if (!overContainer && !overDoor && !overInfoPoint) {
1252 nextCursor = IE_CURSOR_STEALTH|IE_CURSOR_GRAY;
1255 goto end_function;
1258 if (lastActor) {
1259 switch (lastActor->GetStat(IE_EA)) {
1260 case EA_EVILCUTOFF:
1261 case EA_GOODCUTOFF:
1262 break;
1264 case EA_PC:
1265 case EA_FAMILIAR:
1266 case EA_ALLY:
1267 case EA_CONTROLLED:
1268 case EA_CHARMED:
1269 case EA_EVILBUTGREEN:
1270 if (target_types & GA_NO_ENEMY)
1271 nextCursor^=1;
1272 break;
1274 case EA_ENEMY:
1275 case EA_GOODBUTRED:
1276 if (target_types & GA_NO_ALLY)
1277 nextCursor^=1;
1278 break;
1279 default:
1280 if (!(target_types & GA_NO_NEUTRAL))
1281 nextCursor^=1;
1282 break;
1286 end_function:
1287 if (lastCursor != nextCursor) {
1288 Owner->Cursor = nextCursor;
1289 lastCursor = (unsigned char) nextCursor;
1293 #define SCROLL_BORDER 5
1295 /** Global Mouse Move Event */
1296 void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y)
1298 if (ScreenFlags & SF_DISABLEMOUSE) {
1299 return;
1302 if (Owner->Visible!=WINDOW_VISIBLE) {
1303 return;
1306 int mousescrollspd = core->GetMouseScrollSpeed();
1308 if (x <= SCROLL_BORDER)
1309 moveX = -mousescrollspd;
1310 else {
1311 if (x >= ( core->Width - SCROLL_BORDER ))
1312 moveX = mousescrollspd;
1313 else
1314 moveX = 0;
1316 if (y <= SCROLL_BORDER)
1317 moveY = -mousescrollspd;
1318 else {
1319 if (y >= ( core->Height - SCROLL_BORDER ))
1320 moveY = mousescrollspd;
1321 else
1322 moveY = 0;
1325 if (moveX != 0 || moveY != 0) {
1326 scrolling = true;
1327 } else if (scrolling) {
1328 scrolling = false;
1330 Video* video = core->GetVideoDriver();
1331 video->SetDragCursor(NULL);
1335 void GameControl::UpdateScrolling() {
1336 if (!scrolling) return;
1338 int mousescrollspd = core->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-?
1339 Video* video = core->GetVideoDriver();
1341 if (moveX == mousescrollspd && moveY == 0) { // right
1342 video->SetDragCursor(core->GetScrollCursorSprite(0,numScrollCursor));
1343 } else if (moveX == mousescrollspd && moveY == -mousescrollspd) { // upper right
1344 video->SetDragCursor(core->GetScrollCursorSprite(1,numScrollCursor));
1345 } else if (moveX == 0 && moveY == -mousescrollspd) { // up
1346 video->SetDragCursor(core->GetScrollCursorSprite(2,numScrollCursor));
1347 } else if (moveX == -mousescrollspd && moveY == -mousescrollspd) { // upper left
1348 video->SetDragCursor(core->GetScrollCursorSprite(3,numScrollCursor));
1349 } else if (moveX == -mousescrollspd && moveY == 0) { // left
1350 video->SetDragCursor(core->GetScrollCursorSprite(4,numScrollCursor));
1351 } else if (moveX == -mousescrollspd && moveY == mousescrollspd) { // bottom left
1352 video->SetDragCursor(core->GetScrollCursorSprite(5,numScrollCursor));
1353 } else if (moveX == 0 && moveY == mousescrollspd) { // bottom
1354 video->SetDragCursor(core->GetScrollCursorSprite(6,numScrollCursor));
1355 } else if (moveX == mousescrollspd && moveY == mousescrollspd) { // bottom right
1356 video->SetDragCursor(core->GetScrollCursorSprite(7,numScrollCursor));
1359 numScrollCursor = (numScrollCursor+1) % 15;
1362 //generate action code for source actor to try to attack a target
1363 void GameControl::TryToAttack(Actor *source, Actor *tgt)
1365 char Tmp[40];
1367 source->ClearPath();
1368 source->ClearActions();
1369 strncpy(Tmp,"NIDSpecial3()",sizeof(Tmp) );
1370 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1373 //generate action code for source actor to try to defend a target
1374 void GameControl::TryToDefend(Actor *source, Actor *tgt)
1376 char Tmp[40];
1378 source->ClearPath();
1379 source->ClearActions();
1380 strncpy(Tmp,"NIDSpecial4()",sizeof(Tmp) );
1381 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1384 //generate action code for source actor to try to pick pockets of a target
1385 //The -1 flag is a placeholder for dynamic target IDs
1386 void GameControl::TryToPick(Actor *source, Actor *tgt)
1388 char Tmp[40];
1390 source->ClearPath();
1391 source->ClearActions();
1392 strncpy(Tmp,"PickPockets([-1])", sizeof(Tmp) );
1393 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1396 //generate action code for source actor to try to pick a lock/disable trap on a door
1397 void GameControl::TryToPick(Actor *source, Door *tgt)
1399 char Tmp[40];
1401 source->ClearPath();
1402 source->ClearActions();
1403 if (tgt->Trapped && tgt->TrapDetected) {
1404 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1405 } else {
1406 snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() );
1408 source->AddAction( GenerateAction( Tmp ) );
1411 //generate action code for source actor to try to pick a lock/disable trap on a container
1412 void GameControl::TryToPick(Actor *source, Container *tgt)
1414 char Tmp[40];
1416 source->ClearPath();
1417 source->ClearActions();
1418 if (tgt->Trapped && tgt->TrapDetected) {
1419 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1420 } else {
1421 snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() );
1423 source->AddAction( GenerateAction( Tmp ) );
1426 //generate action code for source actor to try to disable trap (only trap type active regions)
1427 void GameControl::TryToDisarm(Actor *source, InfoPoint *tgt)
1429 if (tgt->Type!=ST_PROXIMITY) return;
1431 char Tmp[40];
1433 source->ClearPath();
1434 source->ClearActions();
1435 snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() );
1436 source->AddAction( GenerateAction( Tmp ) );
1439 //generate action code for source actor to try to force open lock on a door/container
1440 void GameControl::TryToBash(Actor *source, Scriptable *tgt)
1442 char Tmp[40];
1444 source->ClearPath();
1445 source->ClearActions();
1446 snprintf(Tmp, sizeof(Tmp), "Attack(\"%s\")", tgt->GetScriptName() );
1447 source->AddAction( GenerateAction( Tmp ) );
1450 //generate action code for source actor to use item/cast spell on a point
1451 void GameControl::TryToCast(Actor *source, const Point &tgt)
1453 char Tmp[40];
1455 if (!spellCount) {
1456 target_mode = TARGET_MODE_NONE;
1457 return; //not casting or using an own item
1459 source->ClearPath();
1460 source->ClearActions();
1462 spellCount--;
1463 if (spellOrItem>=0) {
1464 sprintf(Tmp, "NIDSpecial8()");
1465 } else {
1466 //using item on target
1467 sprintf(Tmp, "NIDSpecial7()");
1469 Action* action = GenerateAction( Tmp );
1470 action->pointParameter=tgt;
1471 if (spellOrItem>=0)
1473 CREMemorizedSpell *si;
1474 //spell casting at target
1475 si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex);
1476 if (!si)
1478 target_mode = TARGET_MODE_NONE;
1479 return;
1481 sprintf(action->string0Parameter,"%.8s",si->SpellResRef);
1483 else
1485 action->int0Parameter=spellSlot;
1486 action->int1Parameter=spellIndex;
1488 source->AddAction( action );
1489 if (!spellCount) {
1490 target_mode = TARGET_MODE_NONE;
1494 //generate action code for source actor to use item/cast spell on another actor
1495 void GameControl::TryToCast(Actor *source, Actor *tgt)
1497 char Tmp[40];
1499 if (!spellCount) {
1500 target_mode = TARGET_MODE_NONE;
1501 return; //not casting or using an own item
1503 source->ClearPath();
1504 source->ClearActions();
1506 spellCount--;
1507 if (spellOrItem>=0) {
1508 sprintf(Tmp, "NIDSpecial6()");
1509 } else {
1510 //using item on target
1511 sprintf(Tmp, "NIDSpecial5()");
1513 Action* action = GenerateActionDirect( Tmp, tgt);
1514 if (spellOrItem>=0)
1516 CREMemorizedSpell *si;
1517 //spell casting at target
1518 si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex);
1519 if (!si)
1521 target_mode = TARGET_MODE_NONE;
1522 return;
1524 sprintf(action->string0Parameter,"%.8s",si->SpellResRef);
1526 else
1528 action->int0Parameter=spellSlot;
1529 action->int1Parameter=spellIndex;
1531 source->AddAction( action );
1532 if (!spellCount) {
1533 target_mode = TARGET_MODE_NONE;
1537 //generate action code for source actor to use talk to target actor
1538 void GameControl::TryToTalk(Actor *source, Actor *tgt)
1540 char Tmp[40];
1542 //Nidspecial1 is just an unused action existing in all games
1543 //(non interactive demo)
1544 //i found no fitting action which would emulate this kind of
1545 //dialog initation
1546 source->ClearPath();
1547 source->ClearActions();
1548 strncpy(Tmp,"NIDSpecial1()",sizeof(Tmp) );
1549 targetID = tgt->globalID; //this is a hack, but not so deadly
1550 source->AddAction( GenerateActionDirect( Tmp, tgt) );
1553 //generate action code for actor appropriate for the target mode when the target is a container
1554 void GameControl::HandleContainer(Container *container, Actor *actor)
1556 char Tmp[256];
1558 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1559 //we'll get the container back from the coordinates
1560 TryToCast(actor, container->Pos);
1561 //Do not reset target_mode, TryToCast does it for us!!
1562 return;
1565 if (target_mode == TARGET_MODE_ATTACK) {
1566 TryToBash(actor, container);
1567 target_mode = TARGET_MODE_NONE;
1568 return;
1571 if ((target_mode == TARGET_MODE_PICK)) {
1572 TryToPick(actor, container);
1573 target_mode = TARGET_MODE_NONE;
1574 return;
1577 actor->ClearPath();
1578 actor->ClearActions();
1579 strncpy(Tmp,"UseContainer()",sizeof(Tmp) );
1580 core->SetCurrentContainer( actor, container);
1581 actor->AddAction( GenerateAction( Tmp) );
1584 //generate action code for actor appropriate for the target mode when the target is a door
1585 void GameControl::HandleDoor(Door *door, Actor *actor)
1587 char Tmp[256];
1589 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1590 //we'll get the door back from the coordinates
1591 Point *p = door->toOpen;
1592 Point *otherp = door->toOpen+1;
1593 if (Distance(*p,actor)>Distance(*otherp,actor)) {
1594 p=otherp;
1596 TryToCast(actor, *p);
1597 return;
1600 if (target_mode == TARGET_MODE_ATTACK) {
1601 TryToBash(actor, door);
1602 target_mode = TARGET_MODE_NONE ;
1603 return;
1606 if ( (target_mode == TARGET_MODE_PICK) || door->TrapDetected) {
1607 TryToPick(actor, door);
1608 target_mode = TARGET_MODE_NONE ;
1609 return;
1612 actor->ClearPath();
1613 actor->ClearActions();
1614 // it really isn't very nice to store a pointer in the actor like this
1615 actor->TargetDoor = door;
1616 // internal gemrb toggle door action hack - should we use UseDoor instead?
1617 sprintf( Tmp, "NIDSpecial9()" );
1618 actor->AddAction( GenerateAction( Tmp) );
1621 //generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel)
1622 bool GameControl::HandleActiveRegion(InfoPoint *trap, Actor * actor, Point &p)
1624 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1625 //we'll get the active region from the coordinates (if needed)
1626 TryToCast(actor, p);
1627 //don't bother with this region further
1628 return true;
1630 if ((target_mode == TARGET_MODE_PICK)) {
1631 TryToDisarm(actor, trap);
1632 target_mode = TARGET_MODE_NONE;
1633 return true;
1636 switch(trap->Type) {
1637 case ST_TRAVEL:
1638 actor->UseExit(true);
1639 return false;
1640 case ST_TRIGGER:
1641 //the importer shouldn't load the script
1642 //if it is unallowed anyway (though
1643 //deactivated scripts could be reactivated)
1644 //only the 'trapped' flag should be honoured
1645 //there. Here we have to check on the
1646 //reset trap and deactivated flags
1647 if (trap->Scripts[0]) {
1648 if (!(trap->Flags&TRAP_DEACTIVATED) ) {
1649 trap->LastTriggerObject = trap->LastTrigger = actor->GetID();
1650 trap->ImmediateEvent();
1651 //directly feeding the event, even if there are actions in the queue
1652 trap->Scripts[0]->Update();
1653 trap->ProcessActions(true);
1654 //if reset trap flag not set, deactivate it
1655 //hmm, better not, info triggers don't deactivate themselves on click
1656 //if (!(trap->Flags&TRAP_RESET)) {
1657 // trap->Flags|=TRAP_DEACTIVATED;
1660 } else {
1661 if (trap->overHeadText) {
1662 if (trap->textDisplaying != 1) {
1663 trap->textDisplaying = 1;
1664 trap->timeStartDisplaying = core->GetGame()->Ticks;
1665 DisplayString( trap );
1669 if (trap->Flags&TRAP_USEPOINT) {
1670 //overriding the target point
1671 p = trap->UsePoint;
1672 return false;
1674 return true;
1675 default:;
1677 return false;
1679 /** Mouse Button Down */
1680 void GameControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short Button,
1681 unsigned short /*Mod*/)
1683 if (ScreenFlags&SF_DISABLEMOUSE)
1684 return;
1686 short px=x;
1687 short py=y;
1688 DoubleClick = false;
1689 switch(Button)
1691 case GEM_MB_SCRLUP:
1692 OnSpecialKeyPress(GEM_UP);
1693 break;
1694 case GEM_MB_SCRLDOWN:
1695 OnSpecialKeyPress(GEM_DOWN);
1696 break;
1697 case GEM_MB_ACTION|GEM_MB_DOUBLECLICK:
1698 DoubleClick = true;
1699 case GEM_MB_ACTION:
1700 core->GetVideoDriver()->ConvertToGame( px, py );
1701 MouseIsDown = true;
1702 SelectionRect.x = px;
1703 SelectionRect.y = py;
1704 StartX = px;
1705 StartY = py;
1706 SelectionRect.w = 0;
1707 SelectionRect.h = 0;
1710 /** Mouse Button Up */
1711 void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short Button,
1712 unsigned short /*Mod*/)
1714 unsigned int i;
1715 char Tmp[256];
1717 if (ScreenFlags & SF_DISABLEMOUSE) {
1718 return;
1720 //heh, i found no better place
1721 core->CloseCurrentContainer();
1723 MouseIsDown = false;
1724 Point p(x,y);
1725 core->GetVideoDriver()->ConvertToGame( p.x, p.y );
1726 Game* game = core->GetGame();
1727 Map* area = game->GetCurrentArea( );
1729 if (DrawSelectionRect) {
1730 Actor** ab;
1731 unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
1732 for (i = 0; i < highlighted.size(); i++)
1733 highlighted[i]->SetOver( false );
1734 highlighted.clear();
1735 game->SelectActor( NULL, false, SELECT_NORMAL );
1736 if (count != 0) {
1737 for (i = 0; i < count; i++) {
1738 // FIXME: should call handler only once
1739 game->SelectActor( ab[i], true, SELECT_NORMAL );
1742 free( ab );
1743 DrawSelectionRect = false;
1744 return;
1747 //hidden actors are not selectable by clicking on them
1748 Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_HIDDEN);
1749 if (Button == GEM_MB_MENU) {
1750 if (actor) {
1751 //from GSUtils
1752 DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE);
1753 return;
1755 core->GetDictionary()->SetAt( "MenuX", x );
1756 core->GetDictionary()->SetAt( "MenuY", y );
1757 core->GetGUIScriptEngine()->RunFunction( "OpenFloatMenuWindow" );
1758 return;
1761 if (Button != GEM_MB_ACTION) {
1762 return;
1765 if (!actor && ( game->selected.size() > 0 )) {
1766 if (overDoor) {
1767 HandleDoor(overDoor, core->GetFirstSelectedPC(false));
1768 return;
1770 if (overContainer) {
1771 HandleContainer(overContainer, core->GetFirstSelectedPC(false));
1772 return;
1774 if (overInfoPoint) {
1775 if (HandleActiveRegion(overInfoPoint, core->GetFirstSelectedPC(false), p)) {
1776 return;
1780 //just a single actor, no formation
1781 if (game->selected.size()==1) {
1782 //the player is using an item or spell on the ground
1783 if ((target_mode == TARGET_MODE_CAST) && spellCount) {
1784 TryToCast(core->GetFirstSelectedPC(false), p);
1785 return;
1788 actor=game->selected[0];
1789 actor->ClearPath();
1790 actor->ClearActions();
1791 CreateMovement(actor, p);
1793 if (DoubleClick) {
1794 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
1795 } else {
1796 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
1799 actor->AddAction( GenerateAction( Tmp) );
1801 //p is a searchmap travel region
1802 if ( actor->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) {
1803 sprintf( Tmp, "NIDSpecial2()" );
1804 actor->AddAction( GenerateAction( Tmp) );
1806 return;
1809 // construct a sorted party
1810 // TODO: this is beyond horrible, help
1811 std::vector<Actor *> party;
1812 // first, from the actual party
1813 for (int idx = 0; idx < game->GetPartySize(false); idx++) {
1814 Actor *pc = game->FindPC(idx + 1);
1815 if (!pc) continue;
1817 for (unsigned int j = 0; j < game->selected.size(); j++) {
1818 if (game->selected[j] == pc) {
1819 party.push_back(pc);
1824 // then, anything else we selected
1825 for (i = 0; i < game->selected.size(); i++) {
1826 bool found = false;
1827 for (unsigned int j = 0; j < party.size(); j++) {
1828 if (game->selected[i] == party[j]) {
1829 found = true;
1830 break;
1833 if (!found) party.push_back(game->selected[i]);
1836 //party formation movement
1837 Point src = party[0]->Pos;
1838 for(i = 0; i < party.size(); i++) {
1839 actor = party[i];
1840 actor->ClearPath();
1841 actor->ClearActions();
1842 MoveToPointFormation(actor, i, src, p);
1845 //p is a searchmap travel region
1846 if ( party[0]->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) {
1847 sprintf( Tmp, "NIDSpecial2()" );
1848 party[0]->AddAction( GenerateAction( Tmp) );
1850 return;
1852 if (!actor) return;
1853 //we got an actor past this point
1854 DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE);
1856 PerformActionOn(actor);
1859 void GameControl::PerformActionOn(Actor *actor) {
1860 Game* game = core->GetGame();
1861 unsigned int i;
1863 //determining the type of the clicked actor
1864 ieDword type;
1866 type = actor->GetStat(IE_EA);
1867 if ( type >= EA_EVILCUTOFF || type == EA_GOODBUTRED ) {
1868 type = ACT_ATTACK; //hostile
1869 } else if ( type > EA_CHARMED ) {
1870 type = ACT_TALK; //neutral
1871 } else {
1872 type = ACT_NONE; //party
1875 if (target_mode == TARGET_MODE_ATTACK) {
1876 type = ACT_ATTACK;
1877 } else if (target_mode == TARGET_MODE_TALK) {
1878 type = ACT_TALK;
1879 } else if (target_mode == TARGET_MODE_CAST) {
1880 type = ACT_CAST;
1881 } else if (target_mode == TARGET_MODE_DEFEND) {
1882 type = ACT_DEFEND;
1883 } else if (target_mode == TARGET_MODE_PICK) {
1884 type = ACT_THIEVING;
1887 //we shouldn't zero this for two reasons in case of spell or item
1888 //1. there could be multiple targets
1889 //2. the target mode is important
1890 if (!(target_mode == TARGET_MODE_CAST) || !spellCount) {
1891 target_mode = TARGET_MODE_NONE;
1894 switch (type) {
1895 case ACT_NONE: //none
1896 if (actor->InParty)
1897 SelectActor( actor->InParty );
1898 else if (actor->GetStat(IE_EA) <= EA_CHARMED) {
1899 /*let's select charmed/summoned creatures
1900 EA_CHARMED is the maximum value known atm*/
1901 core->GetGame()->SelectActor(actor, true, SELECT_REPLACE) ;
1903 break;
1904 case ACT_TALK:
1905 //talk (first selected talks)
1906 if (game->selected.size()) {
1907 //if we are in PST modify this to NO!
1908 Actor *source;
1909 if (core->HasFeature(GF_PROTAGONIST_TALKS) ) {
1910 source = game->GetPC(0, false); //protagonist
1911 } else {
1912 source = core->GetFirstSelectedPC(false);
1914 // only party members can start conversations
1915 if (source) {
1916 TryToTalk(source, actor);
1919 break;
1920 case ACT_ATTACK:
1921 //all of them attacks the red circled actor
1922 for(i=0;i<game->selected.size();i++) {
1923 TryToAttack(game->selected[i], actor);
1925 break;
1926 case ACT_CAST: //cast on target or use item on target
1927 if (game->selected.size()==1) {
1928 Actor *source;
1929 source = core->GetFirstSelectedPC(false);
1930 if(source) {
1931 TryToCast(source, actor);
1934 break;
1935 case ACT_DEFEND:
1936 for(i=0;i<game->selected.size();i++) {
1937 TryToDefend(game->selected[i], actor);
1939 break;
1940 case ACT_THIEVING:
1941 if (game->selected.size()==1) {
1942 Actor *source;
1943 source = core->GetFirstSelectedPC(false);
1944 if(source) {
1945 TryToPick(source, actor);
1948 break;
1951 /** Special Key Press */
1952 void GameControl::OnSpecialKeyPress(unsigned char Key)
1954 if (DialogueFlags&DF_IN_DIALOG) {
1955 switch(Key) {
1956 case GEM_RETURN:
1957 //simulating the continue/end button pressed
1958 core->GetGUIScriptEngine()->RunFunction("CloseContinueWindow");
1959 break;
1961 return; //don't accept keys in dialog
1963 Region Viewport = core->GetVideoDriver()->GetViewport();
1964 Game *game = core->GetGame();
1965 Point mapsize = game->GetCurrentArea()->TMap->GetMapSize();
1966 int partysize = game->GetPartySize(false);
1967 int pm;
1968 char tmpstr[10];
1970 switch (Key) {
1971 case GEM_LEFT:
1972 if (Viewport.x > 63)
1973 Viewport.x -= 64;
1974 else
1975 Viewport.x = 0;
1976 break;
1977 case GEM_UP:
1978 if (Viewport.y > 63)
1979 Viewport.y -= 64;
1980 else
1981 Viewport.y = 0;
1982 break;
1983 case GEM_DOWN:
1984 if (Viewport.y + Viewport.h + 64 < mapsize.y)
1985 Viewport.y += 64;
1986 else {
1987 Viewport.y = mapsize.y - Viewport.h;
1988 if (Viewport.y<0) Viewport.y=0;
1990 break;
1991 case GEM_RIGHT:
1992 if (Viewport.x + Viewport.w + 64 < mapsize.x)
1993 Viewport.x += 64;
1994 else {
1995 Viewport.x = mapsize.x - Viewport.w;
1996 if (Viewport.x<0) Viewport.x=0;
1998 break;
1999 case GEM_ALT:
2000 DebugFlags |= DEBUG_SHOW_CONTAINERS;
2001 return;
2002 case GEM_TAB:
2003 // show partymember hp/maxhp as overhead text
2004 for (pm=0; pm < partysize; pm++) {
2005 Actor *pc = game->GetPC(pm, true);
2006 if (!pc) continue;
2007 memset(tmpstr, 0, 10);
2008 snprintf(tmpstr, 10, "%d/%d", pc->Modified[IE_HITPOINTS], pc->Modified[IE_MAXHITPOINTS]);
2009 pc->DisplayHeadText(strdup(tmpstr));
2011 return;
2012 case GEM_MOUSEOUT:
2013 moveX = 0;
2014 moveY = 0;
2015 return;
2016 case GEM_ESCAPE:
2017 core->GetGUIScriptEngine()->RunFunction("EmptyControls");
2018 core->SetEventFlag(EF_ACTION);
2019 return;
2020 case GEM_PGUP:
2021 core->GetGUIScriptEngine()->RunFunction("OnIncreaseSize");
2022 return;
2023 case GEM_PGDOWN:
2024 core->GetGUIScriptEngine()->RunFunction("OnDecreaseSize");
2025 return;
2026 default:
2027 return;
2029 if (ScreenFlags & SF_LOCKSCROLL) {
2030 moveX = 0;
2031 moveY = 0;
2033 else {
2034 // override any existing viewport moves which may be in progress
2035 core->timer->SetMoveViewPort( Viewport.x, Viewport.y, 0, false );
2036 // move it directly ourselves, since we might be paused
2037 core->GetVideoDriver()->MoveViewportTo( Viewport.x, Viewport.y );
2041 void GameControl::CalculateSelection(const Point &p)
2043 unsigned int i;
2044 Game* game = core->GetGame();
2045 Map* area = game->GetCurrentArea( );
2046 if (DrawSelectionRect) {
2047 if (p.x < StartX) {
2048 SelectionRect.w = StartX - p.x;
2049 SelectionRect.x = p.x;
2050 } else {
2051 SelectionRect.x = StartX;
2052 SelectionRect.w = p.x - StartX;
2054 if (p.y < StartY) {
2055 SelectionRect.h = StartY - p.y;
2056 SelectionRect.y = p.y;
2057 } else {
2058 SelectionRect.y = StartY;
2059 SelectionRect.h = p.y - StartY;
2061 Actor** ab;
2062 unsigned int count = area->GetActorInRect( ab, SelectionRect,true );
2063 for (i = 0; i < highlighted.size(); i++)
2064 highlighted[i]->SetOver( false );
2065 highlighted.clear();
2066 if (count != 0) {
2067 for (i = 0; i < count; i++) {
2068 ab[i]->SetOver( true );
2069 highlighted.push_back( ab[i] );
2072 free( ab );
2073 } else {
2074 Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_ENEMY);
2075 Actor *lastActor = area->GetActorByGlobalID(lastActorID);
2076 if (lastActor)
2077 lastActor->SetOver( false );
2078 if (!actor) {
2079 lastActorID = 0;
2080 } else {
2081 lastActorID = actor->globalID;
2082 actor->SetOver( true );
2087 void GameControl::SetCutSceneMode(bool active)
2089 if (active) {
2090 ScreenFlags |= (SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE);
2091 moveX = 0;
2092 moveY = 0;
2093 } else {
2094 ScreenFlags &= ~(SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE);
2098 //Change game window geometries when a new window gets deactivated
2099 void GameControl::HandleWindowHide(const char *WindowName, const char *WindowPosition)
2101 Variables* dict = core->GetDictionary();
2102 ieDword index;
2104 if (dict->Lookup( WindowName, index )) {
2105 if (index != (ieDword) -1) {
2106 Window* w = core->GetWindow( (unsigned short) index );
2107 if (w) {
2108 core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE );
2109 if (dict->Lookup( WindowPosition, index )) {
2110 ResizeDel( w, index );
2112 return;
2114 printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED);
2115 printf("%s:%u\n",WindowName, index);
2120 //Hide all other windows on the GUI (gamecontrol is not hidden by this)
2121 int GameControl::HideGUI()
2123 //hidegui is in effect
2124 if (!(ScreenFlags&SF_GUIENABLED) ) {
2125 return 0;
2127 //no gamecontrol visible
2128 if (Owner->Visible == WINDOW_INVISIBLE ) {
2129 return 0;
2131 ScreenFlags &=~SF_GUIENABLED;
2132 HandleWindowHide("PortraitWindow", "PortraitPosition");
2133 HandleWindowHide("OtherWindow", "OtherPosition");
2134 HandleWindowHide("TopWindow", "TopPosition");
2135 HandleWindowHide("OptionsWindow", "OptionsPosition");
2136 HandleWindowHide("MessageWindow", "MessagePosition");
2137 HandleWindowHide("ActionsWindow", "ActionsPosition");
2138 //FloatWindow doesn't affect gamecontrol, so it is special
2139 Variables* dict = core->GetDictionary();
2140 ieDword index;
2142 if (dict->Lookup( "FloatWindow", index )) {
2143 if (index != (ieDword) -1) {
2144 core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE );
2147 core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height );
2148 return 1;
2151 //Change game window geometries when a new window gets activated
2152 void GameControl::HandleWindowReveal(const char *WindowName, const char *WindowPosition)
2154 Variables* dict = core->GetDictionary();
2155 ieDword index;
2157 if (dict->Lookup( WindowName, index )) {
2158 if (index != (ieDword) -1) {
2159 Window* w = core->GetWindow( (unsigned short) index );
2160 if (w) {
2161 core->SetVisible( (unsigned short) index, WINDOW_VISIBLE );
2162 if (dict->Lookup( WindowPosition, index )) {
2163 ResizeAdd( w, index );
2165 return;
2167 printMessage("GameControl", "Invalid Window Index ", LIGHT_RED);
2168 printf("%s:%u\n",WindowName, index);
2173 //Reveal all windows on the GUI (including this one)
2174 int GameControl::UnhideGUI()
2176 if (ScreenFlags&SF_GUIENABLED) {
2177 return 0;
2180 ScreenFlags |= SF_GUIENABLED;
2181 // Unhide the gamecontrol window
2182 core->SetVisible( 0, WINDOW_VISIBLE );
2184 HandleWindowReveal("ActionsWindow", "ActionsPosition");
2185 HandleWindowReveal("MessageWindow", "MessagePosition");
2186 HandleWindowReveal("OptionsWindow", "OptionsPosition");
2187 HandleWindowReveal("TopWindow", "TopPosition");
2188 HandleWindowReveal("OtherWindow", "OtherPosition");
2189 HandleWindowReveal("PortraitWindow", "PortraitPosition");
2190 //the floatwindow is a special case
2191 Variables* dict = core->GetDictionary();
2192 ieDword index;
2194 if (dict->Lookup( "FloatWindow", index )) {
2195 if (index != (ieDword) -1) {
2196 Window* fw = core->GetWindow( (unsigned short) index );
2197 if (fw) {
2198 core->SetVisible( (unsigned short) index, WINDOW_VISIBLE );
2199 fw->Flags |=WF_FLOAT;
2200 core->SetOnTop( index );
2204 core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height );
2205 return 1;
2208 //a window got removed, so the GameControl gets enlarged
2209 void GameControl::ResizeDel(Window* win, int type)
2211 switch (type) {
2212 case 0: //Left
2213 if (LeftCount!=1) {
2214 printMessage("GameControl","More than one left window!\n",LIGHT_RED);
2216 LeftCount--;
2217 if (!LeftCount) {
2218 Owner->XPos -= win->Width;
2219 Owner->Width += win->Width;
2220 Width = Owner->Width;
2222 break;
2224 case 1: //Bottom
2225 if (BottomCount!=1) {
2226 printMessage("GameControl","More than one bottom window!\n",LIGHT_RED);
2228 BottomCount--;
2229 if (!BottomCount) {
2230 Owner->Height += win->Height;
2231 Height = Owner->Height;
2233 break;
2235 case 2: //Right
2236 if (RightCount!=1) {
2237 printMessage("GameControl","More than one right window!\n",LIGHT_RED);
2239 RightCount--;
2240 if (!RightCount) {
2241 Owner->Width += win->Width;
2242 Width = Owner->Width;
2244 break;
2246 case 3: //Top
2247 if (TopCount!=1) {
2248 printMessage("GameControl","More than one top window!\n",LIGHT_RED);
2250 TopCount--;
2251 if (!TopCount) {
2252 Owner->YPos -= win->Height;
2253 Owner->Height += win->Height;
2254 Height = Owner->Height;
2256 break;
2258 case 4: //BottomAdded
2259 BottomCount--;
2260 Owner->Height += win->Height;
2261 Height = Owner->Height;
2262 break;
2263 case 5: //Inactivating
2264 BottomCount--;
2265 Owner->Height += win->Height;
2266 Height = Owner->Height;
2267 break;
2271 //a window got added, so the GameControl gets shrunk
2272 //Owner is the GameControl's window
2273 //GameControl is the only control on that window
2274 void GameControl::ResizeAdd(Window* win, int type)
2276 switch (type) {
2277 case 0: //Left
2278 LeftCount++;
2279 if (LeftCount == 1) {
2280 Owner->XPos += win->Width;
2281 Owner->Width -= win->Width;
2282 Width = Owner->Width;
2284 break;
2286 case 1: //Bottom
2287 BottomCount++;
2288 if (BottomCount == 1) {
2289 Owner->Height -= win->Height;
2290 Height = Owner->Height;
2292 break;
2294 case 2: //Right
2295 RightCount++;
2296 if (RightCount == 1) {
2297 Owner->Width -= win->Width;
2298 Width = Owner->Width;
2300 break;
2302 case 3: //Top
2303 TopCount++;
2304 if (TopCount == 1) {
2305 Owner->YPos += win->Height;
2306 Owner->Height -= win->Height;
2307 Height = Owner->Height;
2309 break;
2311 case 4: //BottomAdded
2312 BottomCount++;
2313 Owner->Height -= win->Height;
2314 Height = Owner->Height;
2315 break;
2317 case 5: //Inactivating
2318 BottomCount++;
2319 Owner->Height -= win->Height;
2320 Height = 0;
2324 //Try to start dialogue between two actors (one of them could be inanimate)
2325 int GameControl::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgref)
2327 if (dlg) {
2328 delete dlg;
2329 dlg = NULL;
2332 PluginHolder<DialogMgr> dm(IE_DLG_CLASS_ID);
2333 dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true );
2334 dlg = dm->GetDialog();
2336 if (!dlg) {
2337 printMessage("GameControl", " ", LIGHT_RED);
2338 printf( "Cannot start dialog: %s\n", dlgref );
2339 return -1;
2342 strnlwrcpy(dlg->ResRef, dlgref, 8); //this isn't handled by GetDialog???
2344 //target is here because it could be changed when a dialog runs onto
2345 //and external link, we need to find the new target (whose dialog was
2346 //linked to)
2348 Actor *spe = (Actor *) spk;
2349 speakerID = spe->globalID;
2350 if (tgt->Type!=ST_ACTOR) {
2351 targetID=0xffff;
2352 //most likely this dangling object reference
2353 //won't cause problems, because trigger points don't
2354 //get destroyed during a dialog
2355 targetOB=tgt;
2356 spk->LastTalkedTo=0;
2357 } else {
2358 Actor *tar = (Actor *) tgt;
2359 speakerID = spe->globalID;
2360 targetID = tar->globalID;
2361 if (!originalTargetID) originalTargetID = tar->globalID;
2362 spe->LastTalkedTo=targetID;
2363 tar->LastTalkedTo=speakerID;
2366 //check if we are already in dialog
2367 if (DialogueFlags&DF_IN_DIALOG) {
2368 return 0;
2371 int si = dlg->FindFirstState( tgt );
2372 if (si < 0) {
2373 return -1;
2376 //we need GUI for dialogs
2377 UnhideGUI();
2379 //no exploring while in dialogue
2380 ScreenFlags |= SF_GUIENABLED|SF_DISABLEMOUSE|SF_LOCKSCROLL;
2381 DialogueFlags |= DF_IN_DIALOG;
2383 if (tgt->Type==ST_ACTOR) {
2384 Actor *tar = (Actor *) tgt;
2385 tar->DialogInterrupt();
2388 //allow mouse selection from dialog (even though screen is locked)
2389 core->GetVideoDriver()->SetMouseEnabled(true);
2390 core->timer->SetMoveViewPort( tgt->Pos.x, tgt->Pos.y, 0, true );
2391 //there are 3 bits, if they are all unset, the dialog freezes scripts
2392 if (!(dlg->Flags&7) ) {
2393 DialogueFlags |= DF_FREEZE_SCRIPTS;
2395 //opening control size to maximum, enabling dialog window
2396 core->GetGame()->SetControlStatus(CS_HIDEGUI, BM_NAND);
2397 core->GetGame()->SetControlStatus(CS_DIALOG, BM_OR);
2398 core->SetEventFlag(EF_PORTRAIT);
2399 return 0;
2402 /*try to break will only try to break it, false means unconditional stop*/
2403 void GameControl::EndDialog(bool try_to_break)
2405 if (try_to_break && (DialogueFlags&DF_UNBREAKABLE) ) {
2406 return;
2409 Actor *tmp = GetSpeaker();
2410 if (tmp) {
2411 tmp->LeaveDialog();
2413 speakerID = 0;
2414 if (targetID==0xffff) {
2415 targetOB->LeaveDialog();
2416 } else {
2417 tmp=GetTarget();
2418 if (tmp) {
2419 tmp->LeaveDialog();
2422 targetOB = NULL;
2423 targetID = 0;
2424 originalTargetID = 0;
2425 ds = NULL;
2426 if (dlg) {
2427 delete dlg;
2428 dlg = NULL;
2430 //restoring original size
2431 core->GetGame()->SetControlStatus(CS_DIALOG, BM_NAND);
2432 ScreenFlags &=~(SF_DISABLEMOUSE|SF_LOCKSCROLL);
2433 DialogueFlags = 0;
2434 core->SetEventFlag(EF_PORTRAIT);
2437 //translate section values (journal, solved, unsolved, user)
2438 static const int sectionMap[4]={4,1,2,0};
2440 void GameControl::DialogChoose(unsigned int choose)
2442 TextArea* ta = core->GetMessageTextArea();
2443 if (!ta) {
2444 printMessage("GameControl","Dialog aborted???",LIGHT_RED);
2445 EndDialog();
2446 return;
2449 Actor *speaker = GetSpeaker();
2450 if (!speaker) {
2451 printMessage("GameControl","Speaker gone???",LIGHT_RED);
2452 EndDialog();
2453 return;
2455 Actor *tgt;
2456 Scriptable *target;
2458 if (targetID!=0xffff) {
2459 tgt = GetTarget();
2460 target = tgt;
2461 } else {
2462 //risky!!!
2463 target = targetOB;
2464 tgt=NULL;
2466 if (!target) {
2467 printMessage("GameControl","Target gone???",LIGHT_RED);
2468 EndDialog();
2469 return;
2472 if (choose == (unsigned int) -1) {
2473 //increasing talkcount after top level condition was determined
2475 int si = dlg->FindFirstState( tgt );
2476 if (si<0) {
2477 EndDialog();
2478 return;
2481 if (tgt) {
2482 if (DialogueFlags&DF_TALKCOUNT) {
2483 DialogueFlags&=~DF_TALKCOUNT;
2484 tgt->TalkCount++;
2485 } else if (DialogueFlags&DF_INTERACT) {
2486 DialogueFlags&=~DF_INTERACT;
2487 tgt->InteractCount++;
2490 ds = dlg->GetState( si );
2491 } else {
2492 if (ds->transitionsCount <= choose) {
2493 return;
2496 DialogTransition* tr = ds->transitions[choose];
2498 ta->PopMinRow();
2500 if (tr->Flags&IE_DLG_TR_JOURNAL) {
2501 int Section = 0;
2502 if (tr->Flags&IE_DLG_UNSOLVED) {
2503 Section |= 1;
2505 if (tr->Flags&IE_DLG_SOLVED) {
2506 Section |= 2;
2508 if (core->GetGame()->AddJournalEntry(tr->journalStrRef, sectionMap[Section], tr->Flags>>16) ) {
2509 core->DisplayConstantString(STR_JOURNALCHANGE,0xffff00);
2510 char *string = core->GetString( tr->journalStrRef );
2511 //cutting off the strings at the first crlf
2512 char *poi = strchr(string,'\n');
2513 if (poi) {
2514 *poi='\0';
2516 core->DisplayString( string );
2517 free( string );
2521 if (tr->textStrRef != 0xffffffff) {
2522 //allow_zero is for PST (deionarra's text)
2523 core->DisplayStringName( (int) (tr->textStrRef), 0x8080FF, speaker, IE_STR_SOUND|IE_STR_SPEECH|IE_STR_ALLOW_ZERO);
2524 if (core->HasFeature( GF_DIALOGUE_SCROLLS )) {
2525 ta->AppendText( "", -1 );
2529 if (tr->actions.size()) {
2530 // does this belong here? we must clear actions somewhere before
2531 // we start executing them (otherwise queued actions interfere)
2532 // executing actions directly does not work, because dialog
2533 // needs to end before final actions are executed due to
2534 // actions making new dialogs!
2535 if (target->Type == ST_ACTOR) ((Movable *)target)->ClearPath(); // fuzzie added this
2536 target->ClearActions();
2538 for (unsigned int i = 0; i < tr->actions.size(); i++) {
2539 target->AddAction(tr->actions[i]);
2540 //GameScript::ExecuteAction( target, action );
2544 int final_dialog = tr->Flags & IE_DLG_TR_FINAL;
2546 if (final_dialog) {
2547 ta->SetMinRow( false );
2548 EndDialog();
2551 // all dialog actions must be executed immediately
2552 target->ProcessActions(true);
2553 // (do not clear actions - final actions can involve waiting/moving)
2555 if (final_dialog) {
2556 return;
2559 //displaying dialog for selected option
2560 int si = tr->stateIndex;
2561 //follow external linkage, if required
2562 if (tr->Dialog[0] && strnicmp( tr->Dialog, dlg->ResRef, 8 )) {
2563 //target should be recalculated!
2564 tgt = NULL;
2565 if (originalTargetID) {
2566 // always try original target first (sometimes there are multiple
2567 // actors with the same dialog in an area, we want to pick the one
2568 // we were talking to)
2569 tgt = GetActorByGlobalID(originalTargetID);
2570 if (tgt && strnicmp( tgt->GetDialog(GD_NORMAL), tr->Dialog, 8 ) != 0) {
2571 tgt = NULL;
2574 if (!tgt) {
2575 // then just search the current area for an actor with the dialog
2576 tgt = target->GetCurrentArea()->GetActorByDialog(tr->Dialog);
2578 target = tgt;
2579 if (!target) {
2580 printMessage("Dialog","Can't redirect dialog\n",YELLOW);
2581 ta->SetMinRow( false );
2582 EndDialog();
2583 return;
2585 targetID = tgt->globalID;
2586 // we have to make a backup, tr->Dialog is freed
2587 ieResRef tmpresref;
2588 strnlwrcpy(tmpresref,tr->Dialog, 8);
2589 if (target->GetInternalFlag()&IF_NOINT) {
2590 // this whole check moved out of InitDialog by fuzzie, see comments
2591 // for the IF_NOINT check in BeginDialog
2592 core->DisplayConstantString(STR_TARGETBUSY,0xff0000);
2593 ta->SetMinRow( false );
2594 EndDialog();
2595 return;
2597 int ret = InitDialog( speaker, target, tmpresref);
2598 if (ret<0) {
2599 // error was displayed by InitDialog
2600 ta->SetMinRow( false );
2601 EndDialog();
2602 return;
2605 ds = dlg->GetState( si );
2606 if (!ds) {
2607 printMessage("Dialog","Can't find next dialog\n",YELLOW);
2608 ta->SetMinRow( false );
2609 EndDialog();
2610 return;
2613 //displaying npc text
2614 core->DisplayStringName( ds->StrRef, 0x70FF70, target, IE_STR_SOUND|IE_STR_SPEECH);
2615 //adding a gap between options and npc text
2616 ta->AppendText("",-1);
2617 int i;
2618 int idx = 0;
2619 ta->SetMinRow( true );
2620 //first looking for a 'continue' opportunity, the order is descending (a la IE)
2621 unsigned int x = ds->transitionsCount;
2622 while(x--) {
2623 if (ds->transitions[x]->Flags & IE_DLG_TR_FINAL) {
2624 continue;
2626 if (ds->transitions[x]->textStrRef != 0xffffffff) {
2627 continue;
2629 if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) {
2630 if (ds->transitions[x]->condition &&
2631 !ds->transitions[x]->condition->Evaluate(target)) {
2632 continue;
2635 core->GetDictionary()->SetAt("DialogOption",x);
2636 DialogueFlags |= DF_OPENCONTINUEWINDOW;
2637 goto end_of_choose;
2639 for (x = 0; x < ds->transitionsCount; x++) {
2640 if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) {
2641 if (ds->transitions[x]->condition &&
2642 !ds->transitions[x]->condition->Evaluate(target)) {
2643 continue;
2646 idx++;
2647 if (ds->transitions[x]->textStrRef == 0xffffffff) {
2648 //dialogchoose should be set to x
2649 //it isn't important which END option was chosen, as it ends
2650 core->GetDictionary()->SetAt("DialogOption",x);
2651 DialogueFlags |= DF_OPENENDWINDOW;
2652 } else {
2653 char *string = ( char * ) malloc( 40 );
2654 sprintf( string, "[s=%d,ffffff,ff0000]%d - [p]", x, idx );
2655 i = ta->AppendText( string, -1 );
2656 free( string );
2657 string = core->GetString( ds->transitions[x]->textStrRef );
2658 ta->AppendText( string, i );
2659 free( string );
2660 ta->AppendText( "[/p][/s]", i );
2663 // this happens if a trigger isn't implemented or the dialog is wrong
2664 if (!idx) {
2665 printMessage("Dialog", "There were no valid dialog options!\n", YELLOW);
2666 DialogueFlags |= DF_OPENENDWINDOW;
2668 end_of_choose:
2669 //padding the rows so our text will be at the top
2670 if (core->HasFeature( GF_DIALOGUE_SCROLLS )) {
2671 ta->AppendText( "", -1 );
2673 else {
2674 ta->PadMinRow();
2678 //Create an overhead text over an arbitrary point
2679 void GameControl::DisplayString(const Point &p, const char *Text)
2681 Scriptable* scr = new Scriptable( ST_TRIGGER );
2682 scr->overHeadText = (char *) Text;
2683 scr->textDisplaying = 1;
2684 scr->timeStartDisplaying = 0;
2685 scr->Pos = p;
2686 scr->ClearCutsceneID( );
2689 //Create an overhead text over a scriptable target
2690 //Multiple texts are possible, as this code copies the text to a new object
2691 void GameControl::DisplayString(Scriptable* target)
2693 Scriptable* scr = new Scriptable( ST_TRIGGER );
2694 scr->overHeadText = strdup( target->overHeadText );
2695 /* strdup should work here, we use it elsewhere
2696 size_t len = strlen( target->overHeadText ) + 1;
2697 scr->overHeadText = ( char * ) malloc( len );
2698 strcpy( scr->overHeadText, target->overHeadText );
2700 scr->textDisplaying = 1;
2701 scr->timeStartDisplaying = target->timeStartDisplaying;
2702 scr->Pos = target->Pos;
2703 scr->SetCutsceneID( target );
2706 /** changes displayed map to the currently selected PC */
2707 void GameControl::ChangeMap(Actor *pc, bool forced)
2709 //swap in the area of the actor
2710 Game* game = core->GetGame();
2711 if (forced || (stricmp( pc->Area, game->CurrentArea) != 0) ) {
2712 EndDialog();
2713 overInfoPoint = NULL;
2714 overContainer = NULL;
2715 overDoor = NULL;
2716 /*this is loadmap, because we need the index, not the pointer*/
2717 char *areaname = game->CurrentArea;
2718 if (pc) {
2719 areaname = pc->Area;
2721 game->GetMap( areaname, true );
2722 ScreenFlags|=SF_CENTERONACTOR;
2724 //center on first selected actor
2725 Region vp = core->GetVideoDriver()->GetViewport();
2726 if (ScreenFlags&SF_CENTERONACTOR) {
2727 core->timer->SetMoveViewPort( pc->Pos.x, pc->Pos.y, 0, true );
2728 ScreenFlags&=~SF_CENTERONACTOR;
2732 void GameControl::SetScreenFlags(int value, int mode)
2734 switch(mode) {
2735 case BM_OR: ScreenFlags|=value; break;
2736 case BM_NAND: ScreenFlags&=~value; break;
2737 case BM_SET: ScreenFlags=value; break;
2738 case BM_AND: ScreenFlags&=value; break;
2739 case BM_XOR: ScreenFlags^=value; break;
2743 void GameControl::SetDialogueFlags(int value, int mode)
2745 switch(mode) {
2746 case BM_OR: DialogueFlags|=value; break;
2747 case BM_NAND: DialogueFlags&=~value; break;
2748 case BM_SET: DialogueFlags=value; break;
2749 case BM_AND: DialogueFlags&=value; break;
2750 case BM_XOR: DialogueFlags^=value; break;
2754 //copies a screenshot into a sprite
2755 Sprite2D* GameControl::GetScreenshot(bool show_gui)
2757 Sprite2D* screenshot;
2758 if (show_gui) {
2759 screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) );
2760 } else {
2761 int hf = HideGUI ();
2762 Draw (0, 0);
2763 screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) );
2764 if (hf) {
2765 UnhideGUI ();
2767 core->DrawWindows ();
2770 return screenshot;
2773 //copies a downscaled screenshot into a sprite for save game preview
2774 Sprite2D* GameControl::GetPreview()
2776 // We get preview by first taking a screenshot of size 640x405,
2777 // centered in the display. This is to get a decent picture for
2778 // higher screen resolutions.
2779 // FIXME: how do orig games solve that?
2780 Video* video = core->GetVideoDriver();
2781 int w = video->GetWidth();
2782 int h = video->GetHeight();
2783 int x = (w - 640) / 2;
2784 int y = (h - 405) / 2;
2786 if (x < 0) {
2787 x = 0;
2788 } else {
2789 w = 640;
2792 if (y < 0) {
2793 y = 0;
2794 } else {
2795 h = 405;
2798 if (!x)
2799 y = 0;
2801 int hf = HideGUI ();
2802 signed char v = Owner->Visible;
2803 Owner->Visible = WINDOW_VISIBLE;
2804 Draw (0, 0);
2805 Owner->Visible = v;
2806 Sprite2D *screenshot = video->GetScreenshot( Region(x, y, w, h) );
2807 if (hf) {
2808 UnhideGUI ();
2810 core->DrawWindows();
2812 Sprite2D* preview = video->SpriteScaleDown ( screenshot, 5 );
2813 video->FreeSprite( screenshot );
2814 return preview;
2819 * Returns PC portrait for a currently running game
2821 Sprite2D* GameControl::GetPortraitPreview(int pcslot)
2823 /** Portrait shrink ratio */
2824 // FIXME: this is just a random PST specific trait
2825 // you can make it less random with a new feature bit
2826 int ratio = (core->HasFeature( GF_ONE_BYTE_ANIMID )) ? 1 : 2;
2828 Video *video = core->GetVideoDriver();
2830 Actor *actor = core->GetGame()->GetPC( pcslot, false );
2831 if (! actor) {
2832 return NULL;
2834 ResourceHolder<ImageMgr> im(actor->GetPortrait(true));
2835 if (! im) {
2836 return NULL;
2839 Sprite2D* img = im->GetSprite2D();
2841 if (ratio == 1)
2842 return img;
2844 Sprite2D* img_scaled = video->SpriteScaleDown( img, ratio );
2845 video->FreeSprite( img );
2847 return img_scaled;
2850 Actor *GameControl::GetActorByGlobalID(ieWord ID)
2852 if (!ID)
2853 return NULL;
2854 Game* game = core->GetGame();
2855 if (!game)
2856 return NULL;
2858 Map* area = game->GetCurrentArea( );
2859 if (!area)
2860 return NULL;
2861 return
2862 area->GetActorByGlobalID(ID);
2865 Actor *GameControl::GetLastActor()
2867 return GetActorByGlobalID(lastActorID);
2870 Actor *GameControl::GetTarget()
2872 return GetActorByGlobalID(targetID);
2875 Actor *GameControl::GetSpeaker()
2877 return GetActorByGlobalID(speakerID);
2880 //Set up an item use which needs targeting
2881 //Slot is an inventory slot
2882 //header is the used item extended header
2883 //u is the user
2884 //target type is a bunch of GetActor flags that alter valid targets
2885 //cnt is the number of different targets (usually 1)
2886 void GameControl::SetupItemUse(int slot, int header, Actor *u, int targettype, int cnt)
2888 spellOrItem = -1;
2889 spellUser = u;
2890 spellSlot = slot;
2891 spellIndex = header;
2892 //item use also uses the casting icon, this might be changed in some custom game?
2893 target_mode = TARGET_MODE_CAST;
2894 target_types = targettype;
2895 spellCount = cnt;
2898 //Set up spell casting which needs targeting
2899 //type is the spell's type
2900 //level is the caster level
2901 //idx is the spell's number
2902 //u is the caster
2903 //target type is a bunch of GetActor flags that alter valid targets
2904 //cnt is the number of different targets (usually 1)
2905 void GameControl::SetupCasting(int type, int level, int idx, Actor *u, int targettype, int cnt)
2907 spellOrItem = type;
2908 spellUser = u;
2909 spellSlot = level;
2910 spellIndex = idx;
2911 target_mode = TARGET_MODE_CAST;
2912 target_types = targettype;
2913 spellCount = cnt;
2916 //another method inherited from Control which has no use here
2917 bool GameControl::SetEvent(int /*eventType*/, EventHandler /*handler*/)
2919 return false;
2922 void GameControl::SetDisplayText(char *text, unsigned int time)
2924 if (DisplayText) {
2925 core->FreeString(DisplayText);
2927 DisplayTextTime = time;
2928 DisplayText = text;
2931 void GameControl::SetDisplayText(ieStrRef text, unsigned int time)
2933 SetDisplayText(core->GetString(core->GetStringReference(text), 0), time);