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"
25 #include "DialogMgr.h"
31 #include "Interface.h"
33 #include "SaveGameIterator.h"
34 #include "ScriptEngine.h"
41 #define DEBUG_SHOW_INFOPOINTS 0x01
42 #define DEBUG_SHOW_CONTAINERS 0x02
43 #define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
44 #define DEBUG_SHOW_LIGHTMAP 0x08
46 static const Color cyan
= {
47 0x00, 0xff, 0xff, 0xff
49 static const Color red
= {
50 0xff, 0x00, 0x00, 0xff
52 static const Color magenta
= {
53 0xff, 0x00, 0xff, 0xff
55 static const Color green
= {
56 0x00, 0xff, 0x00, 0xff
59 static Color white = {
60 0xff, 0xff, 0xff, 0xff
63 static const Color black
= {
64 0x00, 0x00, 0x00, 0xff
66 static const Color blue
= {
67 0x00, 0x00, 0xff, 0x80
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();
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
)
98 GameControl::GameControl(void)
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
113 DrawSelectionRect
= false;
115 overContainer
= NULL
;
116 overInfoPoint
= NULL
;
119 lastCursor
= IE_CURSOR_NORMAL
;
127 target_mode
= TARGET_MODE_NONE
;
128 target_types
= GA_SELECT
|GA_NO_DEAD
|GA_NO_HIDDEN
;
130 core
->GetDictionary()->Lookup("Center",tmp
);
132 ScreenFlags
=SF_ALWAYSCENTER
|SF_CENTERONACTOR
;
135 ScreenFlags
= SF_CENTERONACTOR
;
144 originalTargetID
= 0;
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()
157 AutoTable
tab("formatio");
161 formations
= (formation_type
*) calloc(1,sizeof(formation_type
) );
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;
197 double xdiff
= src
.x
- p
.x
;
198 double ydiff
= src
.y
- p
.y
;
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
);
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
225 map
->AdjustPosition(p
);
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
)
238 Action
*action
= NULL
;
240 sprintf( Tmp
, "RunToPoint([%d.%d])", p
.x
, p
.y
);
241 action
= GenerateAction( Tmp
);
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 );
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
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();
311 if (p
.x
<viewport
.x
) {
315 if (p
.y
<viewport
.y
) {
321 Sprite2D
*spr
= core
->GetScrollCursorSprite(0,0);
324 //tmp = core->ArrowSprites[0]->Width;
326 if (p
.x
>viewport
.x
+viewport
.w
-tmp
) {
327 p
.x
=viewport
.x
+viewport
.w
;//-tmp;
332 //tmp = core->ArrowSprites[0]->Height;
334 if (p
.y
>viewport
.y
+viewport
.h
-tmp
) {
335 p
.y
=viewport
.y
+viewport
.h
;//-tmp;
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();
352 if (((short) Width
) <=0 || ((short) Height
) <= 0) {
356 if (Owner
->Visible
!=WINDOW_VISIBLE
) {
360 Video
* video
= core
->GetVideoDriver();
361 Region viewport
= video
->GetViewport();
362 if (moveX
|| moveY
) {
365 Point mapsize
= core
->GetGame()->GetCurrentArea()->TMap
->GetMapSize();
366 if ( viewport
.x
< 0 )//if we are at top of the map
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
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( );
384 video
->DrawRect( screen
, blue
, true );
387 video
->DrawRect( screen
, black
, true );
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
;
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
412 for (idx
= 0; (d
= area
->TMap
->GetDoor( idx
)); idx
++) {
413 d
->Highlight
= false;
416 if (d
->VisibleTrap(0) || (d
->Flags
& DOOR_LOCKED
)) {
417 // only highlight targettable doors
418 d
->outlineColor
= green
;
422 } else if (!(d
->Flags
& DOOR_SECRET
)) {
423 // mouse over, not in target mode, no secret door
424 d
->outlineColor
= cyan
;
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
435 // secret door is invisible
438 } else if (DebugFlags
& DEBUG_SHOW_DOORS
) {
439 d
->outlineColor
= cyan
; // debug doors
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
;
456 } else if (overContainer
== c
) {
457 // mouse over, not in target mode
458 c
->outlineColor
= cyan
;
462 if (c
->VisibleTrap(0)) {
463 c
->outlineColor
= red
; // traps
464 } else if (DebugFlags
& DEBUG_SHOW_CONTAINERS
) {
465 c
->outlineColor
= cyan
; // debug containers
472 //drawmap should be here so it updates fog of war
473 area
->DrawMap( screen
);
474 game
->DrawWeather(screen
, update_scripts
);
477 Actor
*actor
= area
->GetActorByGlobalID(trackerID
);
480 Actor
**monsters
= area
->GetAllActorsInRadius(actor
->Pos
, GA_NO_DEAD
, distance
);
484 Actor
*target
= monsters
[i
++];
485 if (target
->InParty
) continue;
486 if (target
->GetStat(IE_NOTRACKING
)) continue;
487 DrawArrowMarker(screen
, target
->Pos
, viewport
);
495 if (ScreenFlags
& SF_DISABLEMOUSE
)
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 );
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
);
519 //if polygon is disabled, make it grey
520 if (poly
->wall_flag
&WF_DISABLED
) {
524 video
->DrawPolyline( poly
, c
, true );
530 PathNode
* node
= drawPath
;
532 Point
p( ( node
-> x
*16) + 8, ( node
->y
*12 ) + 6 );
534 video
->DrawCircle( p
.x
, p
.y
, 2, red
);
536 short oldX
= ( node
->Parent
-> x
*16) + 8, oldY
= ( node
->Parent
->y
*12 ) + 6;
537 video
->DrawLine( oldX
, oldY
, p
.x
, p
.y
, green
);
540 video
->DrawCircle( p
.x
, p
.y
, 2, green
);
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);
569 /** inherited from Control, GameControl doesn't need it */
570 int GameControl::SetText(const char* /*string*/, int /*pos*/)
575 /** Key Press Event */
576 void GameControl::OnKeyPress(unsigned char Key
, unsigned short /*Mod*/)
578 if (DialogueFlags
&DF_IN_DIALOG
) {
582 Game
* game
= core
->GetGame();
587 game
->SelectActor( NULL
, false, SELECT_NORMAL
);
588 i
= game
->GetPartySize(false)/2;
590 SelectActor(i
, true);
594 game
->SelectActor( NULL
, true, SELECT_NORMAL
);
595 i
= game
->GetPartySize(false)/2;
597 SelectActor(i
, false);
612 SelectActor(Key
-'0');
615 core
->GetGame()->SetHotKey(toupper(Key
));
620 //Select (or deselect) a new actor (or actors)
621 void GameControl::SelectActor(int whom
, int type
)
623 Game
* game
= core
->GetGame();
625 game
->SelectActor( NULL
, true, SELECT_NORMAL
);
629 /* doesn't fall through here */
630 Actor
* actor
= game
->FindPC( whom
);
635 game
->SelectActor( actor
, false, SELECT_NORMAL
);
639 game
->SelectActor( actor
, true, SELECT_NORMAL
);
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
)
658 Game
* game
= core
->GetGame();
663 if (DialogueFlags
&DF_IN_DIALOG
) {
676 TextArea
*ta
= core
->GetMessageTextArea();
678 ta
->OnKeyPress(Key
,Mod
);
685 //cheatkeys with ctrl-
686 if (Mod
& GEM_MOD_CTRL
) {
687 if (!core
->CheatEnabled()) {
690 Map
* area
= game
->GetCurrentArea( );
693 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
694 Point
p(lastMouseX
, lastMouseY
);
695 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
697 case 'f': //toggle full screen mode
698 core
->GetVideoDriver()->ToggleFullscreenMode();
700 case 'd': //disarm a trap
702 overInfoPoint
->DetectTrap(256);
705 if (overContainer
->Trapped
&&
706 !( overContainer
->TrapDetected
)) {
707 overContainer
->TrapDetected
= 1;
711 if (overDoor
->Trapped
&&
712 !( overDoor
->TrapDetected
)) {
713 overDoor
->TrapDetected
= 1;
717 case 'l': //play an animation (vvc/bam) over an actor
718 //the original engine was able to swap through all animations
720 lastActor
->AddAnimation("S056ICBL", 0, 0, 0);
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
;
734 src
->CastSpell( TestSpell
, target
, false );
735 if (src
->LastTarget
) {
736 src
->CastSpellEnd( TestSpell
);
738 src
->CastSpellPointEnd( TestSpell
);
744 case 'b': //draw a path to the target (pathfinder debug)
745 //You need to select an origin with ctrl-o first
747 PathNode
* nextNode
= drawPath
->Next
;
748 PathNode
* thisNode
= drawPath
;
754 nextNode
= thisNode
->Next
;
757 drawPath
= core
->GetGame()->GetCurrentArea()->FindPath( pfs
, p
, lastActor
?lastActor
->size
:1 );
761 case 'o': //set up the origin for the pathfinder
765 core
->GetVideoDriver()->ConvertToGame( pfs
.x
, pfs
.y
);
767 case 'a': //switches through the avatar animations
769 lastActor
->GetNextAnimation();
772 case 's': //switches through the stance animations
774 lastActor
->GetNextStance();
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
);
785 case 'm': //prints a debug dump (ctrl-m in the original game too)
787 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
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);
794 Actor
*actor
= area
->GetActor(count
, true);
795 if (actor
->IsOver(p
)) {
801 lastActor
->DebugDump();
805 overDoor
->DebugDump();
809 overContainer
->DebugDump();
813 overInfoPoint
->DebugDump();
816 core
->GetGame()->GetCurrentArea()->DebugDump(Mod
& GEM_MOD_SHIFT
);
818 case 'v': //marks some of the map visited (random vision distance)
819 area
->ExploreMapChunk( p
, rand()%30, 1 );
821 case 'x': // shows coordinates on the map
822 printf( "%s [%d.%d]\n", area
->GetScriptName(), p
.x
, p
.y
);
824 case 'g'://shows loaded areas and other game information
827 case 'i'://interact trigger (from the original game)
829 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
831 if (lastActor
&& !(lastActor
->GetStat(IE_MC_FLAGS
)&MC_EXPORTABLE
)) {
833 int i
= game
->GetPartySize(true);
838 target
= game
->GetPC(i
, true);
839 if(target
==lastActor
) continue;
840 if(target
->GetStat(IE_MC_FLAGS
)&MC_EXPORTABLE
) continue;
843 snprintf(Tmp
,sizeof(Tmp
),"Interact(\"%s\")",target
->GetScriptName() );
844 lastActor
->AddAction(GenerateAction(Tmp
));
850 case 'r'://resurrects actor
852 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
855 Effect
*fx
= EffectQueue::CreateEffect(heal_ref
, lastActor
->GetBase(IE_MAXHITPOINTS
), 0x30001, FX_DURATION_INSTANT_PERMANENT
);
857 core
->ApplyEffect(fx
, lastActor
, lastActor
);
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
867 case 'q': //joins actor to the party
868 if (lastActor
&& !lastActor
->InParty
) {
869 lastActor
->ClearActions();
870 lastActor
->ClearPath();
872 strncpy(Tmp
,"JoinParty()",sizeof(Tmp
) );
873 lastActor
->AddAction( GenerateAction(Tmp
) );
876 case 'p': //center on actor
877 ScreenFlags
|=SF_CENTERONACTOR
;
878 ScreenFlags
^=SF_ALWAYSCENTER
;
880 case 'k': //kicks out actor
881 if (lastActor
&& lastActor
->InParty
) {
882 lastActor
->ClearActions();
883 lastActor
->ClearPath();
885 strncpy(Tmp
,"LeaveParty()",sizeof(Tmp
) );
886 lastActor
->AddAction( GenerateAction(Tmp
) );
889 case 'y': //kills actor or all enemies
890 if (Mod
& GEM_MOD_SHIFT
) {
893 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_MAGIC
<<16, FX_DURATION_INSTANT_PERMANENT
);
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
);
904 //using action so the actor is killed
905 //correctly (synchronisation)
906 lastActor
->ClearActions();
907 lastActor
->ClearPath();
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
);
919 } else if (overContainer
) {
920 overContainer
->SetContainerLocked(0);
921 } else if (overDoor
) {
922 overDoor
->SetDoorLocked(0,0);
926 case 'z': //shift through the avatar animations backward
928 lastActor
->GetPrevAnimation();
931 case '1': //change paperdoll armour level
934 lastActor
->NewStat(IE_ARMOR_TYPE
,1,MOD_ADDITIVE
);
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");
940 case '6': //show the lightmap
941 DebugFlags
^= DEBUG_SHOW_LIGHTMAP
;
942 printf("Show lightmap %s\n", DebugFlags
& DEBUG_SHOW_LIGHTMAP
? "ON" : "OFF");
944 case '7': //toggles fog of war
946 printf("Show Fog-Of-War: %s\n", core
->FogOfWar
& 1 ? "ON" : "OFF");
948 case '8': //show searchmap over area
950 printf("Show searchmap %s\n", core
->FogOfWar
& 2 ? "ON" : "OFF");
953 printf( "KeyRelease:%d - %d\n", Key
, Mod
);
956 return; //return from cheatkeys
959 case 'h': //hard pause
960 if (DialogueFlags
& DF_FREEZE_SCRIPTS
) break;
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
968 core
->DisplayConstantString(STR_UNPAUSED
,0xff0000);
972 core
->GetGUIScriptEngine()->RunFunction("OpenMapWindow");
975 core
->GetGUIScriptEngine()->RunFunction("OpenJournalWindow");
978 core
->GetGUIScriptEngine()->RunFunction("OpenInventoryWindow");
981 core
->GetGUIScriptEngine()->RunFunction("OpenRecordsWindow");
983 case 'q': //quicksave
986 case GEM_ALT
: //alt key (shows containers)
987 DebugFlags
&= ~DEBUG_SHOW_CONTAINERS
;
994 void GameControl::DisplayTooltip() {
995 Game
* game
= core
->GetGame();
997 Map
* area
= game
->GetCurrentArea( );
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 );
1009 char *name
= actor
->GetName(-1);
1010 int hp
= actor
->GetStat(IE_HITPOINTS
);
1011 int maxhp
= actor
->GetStat(IE_MAXHITPOINTS
);
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
);
1019 snprintf(buffer
, 100, "%s", name
);
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
);
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
);
1037 // non-neutral, not in party: display injured string
1039 char *injuredstring
= NULL
;
1040 // these boundaries are just a guess
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
;
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
);
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
1077 core
->DisplayTooltip(p
.x
, p
.y
, this);
1084 core
->DisplayTooltip(0, 0, NULL
);
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
) {
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();
1155 Map
* area
= game
->GetCurrentArea( );
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
;
1165 overInfoPoint
= area
->TMap
->GetInfoPoint( p
, true );
1166 if (overInfoPoint
) {
1167 //nextCursor = overInfoPoint->Cursor;
1168 nextCursor
= GetCursorOverInfoPoint(overInfoPoint
);
1172 overDoor
->Highlight
= false;
1174 if (overContainer
) {
1175 overContainer
->Highlight
= false;
1177 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
1179 lastActor
->SetOver( false );
1182 overDoor
= area
->TMap
->GetDoor( p
);
1183 overContainer
= area
->TMap
->GetContainer( p
);
1185 if (!DrawSelectionRect
) {
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?)
1200 core
->DisplayTooltip(0, 0, this);
1203 if ((target_types
& GA_NO_SELF
) && lastActor
) {
1204 if (lastActor
== core
->GetFirstSelectedPC(false)) {
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
;
1218 nextCursor
= IE_CURSOR_NORMAL
;
1224 if (target_mode
== TARGET_MODE_TALK
) {
1225 nextCursor
= IE_CURSOR_TALK
;
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
)) {
1239 nextCursor
|= IE_CURSOR_GRAY
;
1242 } else if (target_mode
== TARGET_MODE_DEFEND
) {
1243 nextCursor
= IE_CURSOR_DEFEND
;
1245 nextCursor
|= IE_CURSOR_GRAY
;
1247 } else if (target_mode
== TARGET_MODE_PICK
) {
1249 nextCursor
= IE_CURSOR_PICK
;
1251 if (!overContainer
&& !overDoor
&& !overInfoPoint
) {
1252 nextCursor
= IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1259 switch (lastActor
->GetStat(IE_EA
)) {
1269 case EA_EVILBUTGREEN
:
1270 if (target_types
& GA_NO_ENEMY
)
1276 if (target_types
& GA_NO_ALLY
)
1280 if (!(target_types
& GA_NO_NEUTRAL
))
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
) {
1302 if (Owner
->Visible
!=WINDOW_VISIBLE
) {
1306 int mousescrollspd
= core
->GetMouseScrollSpeed();
1308 if (x
<= SCROLL_BORDER
)
1309 moveX
= -mousescrollspd
;
1311 if (x
>= ( core
->Width
- SCROLL_BORDER
))
1312 moveX
= mousescrollspd
;
1316 if (y
<= SCROLL_BORDER
)
1317 moveY
= -mousescrollspd
;
1319 if (y
>= ( core
->Height
- SCROLL_BORDER
))
1320 moveY
= mousescrollspd
;
1325 if (moveX
!= 0 || moveY
!= 0) {
1327 } else if (scrolling
) {
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
)
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
)
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
)
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
)
1401 source
->ClearPath();
1402 source
->ClearActions();
1403 if (tgt
->Trapped
&& tgt
->TrapDetected
) {
1404 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
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
)
1416 source
->ClearPath();
1417 source
->ClearActions();
1418 if (tgt
->Trapped
&& tgt
->TrapDetected
) {
1419 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
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;
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
)
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
)
1456 target_mode
= TARGET_MODE_NONE
;
1457 return; //not casting or using an own item
1459 source
->ClearPath();
1460 source
->ClearActions();
1463 if (spellOrItem
>=0) {
1464 sprintf(Tmp
, "NIDSpecial8()");
1466 //using item on target
1467 sprintf(Tmp
, "NIDSpecial7()");
1469 Action
* action
= GenerateAction( Tmp
);
1470 action
->pointParameter
=tgt
;
1473 CREMemorizedSpell
*si
;
1474 //spell casting at target
1475 si
= source
->spellbook
.GetMemorizedSpell(spellOrItem
, spellSlot
, spellIndex
);
1478 target_mode
= TARGET_MODE_NONE
;
1481 sprintf(action
->string0Parameter
,"%.8s",si
->SpellResRef
);
1485 action
->int0Parameter
=spellSlot
;
1486 action
->int1Parameter
=spellIndex
;
1488 source
->AddAction( action
);
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
)
1500 target_mode
= TARGET_MODE_NONE
;
1501 return; //not casting or using an own item
1503 source
->ClearPath();
1504 source
->ClearActions();
1507 if (spellOrItem
>=0) {
1508 sprintf(Tmp
, "NIDSpecial6()");
1510 //using item on target
1511 sprintf(Tmp
, "NIDSpecial5()");
1513 Action
* action
= GenerateActionDirect( Tmp
, tgt
);
1516 CREMemorizedSpell
*si
;
1517 //spell casting at target
1518 si
= source
->spellbook
.GetMemorizedSpell(spellOrItem
, spellSlot
, spellIndex
);
1521 target_mode
= TARGET_MODE_NONE
;
1524 sprintf(action
->string0Parameter
,"%.8s",si
->SpellResRef
);
1528 action
->int0Parameter
=spellSlot
;
1529 action
->int1Parameter
=spellIndex
;
1531 source
->AddAction( action
);
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
)
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
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
)
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!!
1565 if (target_mode
== TARGET_MODE_ATTACK
) {
1566 TryToBash(actor
, container
);
1567 target_mode
= TARGET_MODE_NONE
;
1571 if ((target_mode
== TARGET_MODE_PICK
)) {
1572 TryToPick(actor
, container
);
1573 target_mode
= TARGET_MODE_NONE
;
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
)
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
)) {
1596 TryToCast(actor
, *p
);
1600 if (target_mode
== TARGET_MODE_ATTACK
) {
1601 TryToBash(actor
, door
);
1602 target_mode
= TARGET_MODE_NONE
;
1606 if ( (target_mode
== TARGET_MODE_PICK
) || door
->TrapDetected
) {
1607 TryToPick(actor
, door
);
1608 target_mode
= TARGET_MODE_NONE
;
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
1630 if ((target_mode
== TARGET_MODE_PICK
)) {
1631 TryToDisarm(actor
, trap
);
1632 target_mode
= TARGET_MODE_NONE
;
1636 switch(trap
->Type
) {
1638 actor
->UseExit(true);
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;
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
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
)
1688 DoubleClick
= false;
1692 OnSpecialKeyPress(GEM_UP
);
1694 case GEM_MB_SCRLDOWN
:
1695 OnSpecialKeyPress(GEM_DOWN
);
1697 case GEM_MB_ACTION
|GEM_MB_DOUBLECLICK
:
1700 core
->GetVideoDriver()->ConvertToGame( px
, py
);
1702 SelectionRect
.x
= px
;
1703 SelectionRect
.y
= 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*/)
1717 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1720 //heh, i found no better place
1721 core
->CloseCurrentContainer();
1723 MouseIsDown
= false;
1725 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
1726 Game
* game
= core
->GetGame();
1727 Map
* area
= game
->GetCurrentArea( );
1729 if (DrawSelectionRect
) {
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
);
1737 for (i
= 0; i
< count
; i
++) {
1738 // FIXME: should call handler only once
1739 game
->SelectActor( ab
[i
], true, SELECT_NORMAL
);
1743 DrawSelectionRect
= false;
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
) {
1752 DisplayStringCore(actor
, VB_SELECT
+core
->Roll(1,3,-1), DS_CONST
|DS_CONSOLE
);
1755 core
->GetDictionary()->SetAt( "MenuX", x
);
1756 core
->GetDictionary()->SetAt( "MenuY", y
);
1757 core
->GetGUIScriptEngine()->RunFunction( "OpenFloatMenuWindow" );
1761 if (Button
!= GEM_MB_ACTION
) {
1765 if (!actor
&& ( game
->selected
.size() > 0 )) {
1767 HandleDoor(overDoor
, core
->GetFirstSelectedPC(false));
1770 if (overContainer
) {
1771 HandleContainer(overContainer
, core
->GetFirstSelectedPC(false));
1774 if (overInfoPoint
) {
1775 if (HandleActiveRegion(overInfoPoint
, core
->GetFirstSelectedPC(false), p
)) {
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
);
1788 actor
=game
->selected
[0];
1790 actor
->ClearActions();
1791 CreateMovement(actor
, p
);
1794 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
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
) );
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);
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
++) {
1827 for (unsigned int j
= 0; j
< party
.size(); j
++) {
1828 if (game
->selected
[i
] == party
[j
]) {
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
++) {
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
) );
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();
1863 //determining the type of the clicked actor
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
1872 type
= ACT_NONE
; //party
1875 if (target_mode
== TARGET_MODE_ATTACK
) {
1877 } else if (target_mode
== TARGET_MODE_TALK
) {
1879 } else if (target_mode
== TARGET_MODE_CAST
) {
1881 } else if (target_mode
== TARGET_MODE_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
;
1895 case ACT_NONE
: //none
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
) ;
1905 //talk (first selected talks)
1906 if (game
->selected
.size()) {
1907 //if we are in PST modify this to NO!
1909 if (core
->HasFeature(GF_PROTAGONIST_TALKS
) ) {
1910 source
= game
->GetPC(0, false); //protagonist
1912 source
= core
->GetFirstSelectedPC(false);
1914 // only party members can start conversations
1916 TryToTalk(source
, actor
);
1921 //all of them attacks the red circled actor
1922 for(i
=0;i
<game
->selected
.size();i
++) {
1923 TryToAttack(game
->selected
[i
], actor
);
1926 case ACT_CAST
: //cast on target or use item on target
1927 if (game
->selected
.size()==1) {
1929 source
= core
->GetFirstSelectedPC(false);
1931 TryToCast(source
, actor
);
1936 for(i
=0;i
<game
->selected
.size();i
++) {
1937 TryToDefend(game
->selected
[i
], actor
);
1941 if (game
->selected
.size()==1) {
1943 source
= core
->GetFirstSelectedPC(false);
1945 TryToPick(source
, actor
);
1951 /** Special Key Press */
1952 void GameControl::OnSpecialKeyPress(unsigned char Key
)
1954 if (DialogueFlags
&DF_IN_DIALOG
) {
1957 //simulating the continue/end button pressed
1958 core
->GetGUIScriptEngine()->RunFunction("CloseContinueWindow");
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);
1972 if (Viewport
.x
> 63)
1978 if (Viewport
.y
> 63)
1984 if (Viewport
.y
+ Viewport
.h
+ 64 < mapsize
.y
)
1987 Viewport
.y
= mapsize
.y
- Viewport
.h
;
1988 if (Viewport
.y
<0) Viewport
.y
=0;
1992 if (Viewport
.x
+ Viewport
.w
+ 64 < mapsize
.x
)
1995 Viewport
.x
= mapsize
.x
- Viewport
.w
;
1996 if (Viewport
.x
<0) Viewport
.x
=0;
2000 DebugFlags
|= DEBUG_SHOW_CONTAINERS
;
2003 // show partymember hp/maxhp as overhead text
2004 for (pm
=0; pm
< partysize
; pm
++) {
2005 Actor
*pc
= game
->GetPC(pm
, true);
2007 memset(tmpstr
, 0, 10);
2008 snprintf(tmpstr
, 10, "%d/%d", pc
->Modified
[IE_HITPOINTS
], pc
->Modified
[IE_MAXHITPOINTS
]);
2009 pc
->DisplayHeadText(strdup(tmpstr
));
2017 core
->GetGUIScriptEngine()->RunFunction("EmptyControls");
2018 core
->SetEventFlag(EF_ACTION
);
2021 core
->GetGUIScriptEngine()->RunFunction("OnIncreaseSize");
2024 core
->GetGUIScriptEngine()->RunFunction("OnDecreaseSize");
2029 if (ScreenFlags
& SF_LOCKSCROLL
) {
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
)
2044 Game
* game
= core
->GetGame();
2045 Map
* area
= game
->GetCurrentArea( );
2046 if (DrawSelectionRect
) {
2048 SelectionRect
.w
= StartX
- p
.x
;
2049 SelectionRect
.x
= p
.x
;
2051 SelectionRect
.x
= StartX
;
2052 SelectionRect
.w
= p
.x
- StartX
;
2055 SelectionRect
.h
= StartY
- p
.y
;
2056 SelectionRect
.y
= p
.y
;
2058 SelectionRect
.y
= StartY
;
2059 SelectionRect
.h
= p
.y
- StartY
;
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();
2067 for (i
= 0; i
< count
; i
++) {
2068 ab
[i
]->SetOver( true );
2069 highlighted
.push_back( ab
[i
] );
2074 Actor
* actor
= area
->GetActor( p
, GA_DEFAULT
| GA_SELECT
| GA_NO_DEAD
| GA_NO_ENEMY
);
2075 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
2077 lastActor
->SetOver( false );
2081 lastActorID
= actor
->globalID
;
2082 actor
->SetOver( true );
2087 void GameControl::SetCutSceneMode(bool active
)
2090 ScreenFlags
|= (SF_DISABLEMOUSE
| SF_LOCKSCROLL
| SF_CUTSCENE
);
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();
2104 if (dict
->Lookup( WindowName
, index
)) {
2105 if (index
!= (ieDword
) -1) {
2106 Window
* w
= core
->GetWindow( (unsigned short) index
);
2108 core
->SetVisible( (unsigned short) index
, WINDOW_INVISIBLE
);
2109 if (dict
->Lookup( WindowPosition
, index
)) {
2110 ResizeDel( w
, index
);
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
) ) {
2127 //no gamecontrol visible
2128 if (Owner
->Visible
== WINDOW_INVISIBLE
) {
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();
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
);
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();
2157 if (dict
->Lookup( WindowName
, index
)) {
2158 if (index
!= (ieDword
) -1) {
2159 Window
* w
= core
->GetWindow( (unsigned short) index
);
2161 core
->SetVisible( (unsigned short) index
, WINDOW_VISIBLE
);
2162 if (dict
->Lookup( WindowPosition
, index
)) {
2163 ResizeAdd( w
, index
);
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
) {
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();
2194 if (dict
->Lookup( "FloatWindow", index
)) {
2195 if (index
!= (ieDword
) -1) {
2196 Window
* fw
= core
->GetWindow( (unsigned short) index
);
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
);
2208 //a window got removed, so the GameControl gets enlarged
2209 void GameControl::ResizeDel(Window
* win
, int type
)
2214 printMessage("GameControl","More than one left window!\n",LIGHT_RED
);
2218 Owner
->XPos
-= win
->Width
;
2219 Owner
->Width
+= win
->Width
;
2220 Width
= Owner
->Width
;
2225 if (BottomCount
!=1) {
2226 printMessage("GameControl","More than one bottom window!\n",LIGHT_RED
);
2230 Owner
->Height
+= win
->Height
;
2231 Height
= Owner
->Height
;
2236 if (RightCount
!=1) {
2237 printMessage("GameControl","More than one right window!\n",LIGHT_RED
);
2241 Owner
->Width
+= win
->Width
;
2242 Width
= Owner
->Width
;
2248 printMessage("GameControl","More than one top window!\n",LIGHT_RED
);
2252 Owner
->YPos
-= win
->Height
;
2253 Owner
->Height
+= win
->Height
;
2254 Height
= Owner
->Height
;
2258 case 4: //BottomAdded
2260 Owner
->Height
+= win
->Height
;
2261 Height
= Owner
->Height
;
2263 case 5: //Inactivating
2265 Owner
->Height
+= win
->Height
;
2266 Height
= Owner
->Height
;
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
)
2279 if (LeftCount
== 1) {
2280 Owner
->XPos
+= win
->Width
;
2281 Owner
->Width
-= win
->Width
;
2282 Width
= Owner
->Width
;
2288 if (BottomCount
== 1) {
2289 Owner
->Height
-= win
->Height
;
2290 Height
= Owner
->Height
;
2296 if (RightCount
== 1) {
2297 Owner
->Width
-= win
->Width
;
2298 Width
= Owner
->Width
;
2304 if (TopCount
== 1) {
2305 Owner
->YPos
+= win
->Height
;
2306 Owner
->Height
-= win
->Height
;
2307 Height
= Owner
->Height
;
2311 case 4: //BottomAdded
2313 Owner
->Height
-= win
->Height
;
2314 Height
= Owner
->Height
;
2317 case 5: //Inactivating
2319 Owner
->Height
-= win
->Height
;
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
)
2332 PluginHolder
<DialogMgr
> dm(IE_DLG_CLASS_ID
);
2333 dm
->Open( gamedata
->GetResource( dlgref
, IE_DLG_CLASS_ID
), true );
2334 dlg
= dm
->GetDialog();
2337 printMessage("GameControl", " ", LIGHT_RED
);
2338 printf( "Cannot start dialog: %s\n", dlgref
);
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
2348 Actor
*spe
= (Actor
*) spk
;
2349 speakerID
= spe
->globalID
;
2350 if (tgt
->Type
!=ST_ACTOR
) {
2352 //most likely this dangling object reference
2353 //won't cause problems, because trigger points don't
2354 //get destroyed during a dialog
2356 spk
->LastTalkedTo
=0;
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
) {
2371 int si
= dlg
->FindFirstState( tgt
);
2376 //we need GUI for dialogs
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
);
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
) ) {
2409 Actor
*tmp
= GetSpeaker();
2414 if (targetID
==0xffff) {
2415 targetOB
->LeaveDialog();
2424 originalTargetID
= 0;
2430 //restoring original size
2431 core
->GetGame()->SetControlStatus(CS_DIALOG
, BM_NAND
);
2432 ScreenFlags
&=~(SF_DISABLEMOUSE
|SF_LOCKSCROLL
);
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();
2444 printMessage("GameControl","Dialog aborted???",LIGHT_RED
);
2449 Actor
*speaker
= GetSpeaker();
2451 printMessage("GameControl","Speaker gone???",LIGHT_RED
);
2458 if (targetID
!=0xffff) {
2467 printMessage("GameControl","Target gone???",LIGHT_RED
);
2472 if (choose
== (unsigned int) -1) {
2473 //increasing talkcount after top level condition was determined
2475 int si
= dlg
->FindFirstState( tgt
);
2482 if (DialogueFlags
&DF_TALKCOUNT
) {
2483 DialogueFlags
&=~DF_TALKCOUNT
;
2485 } else if (DialogueFlags
&DF_INTERACT
) {
2486 DialogueFlags
&=~DF_INTERACT
;
2487 tgt
->InteractCount
++;
2490 ds
= dlg
->GetState( si
);
2492 if (ds
->transitionsCount
<= choose
) {
2496 DialogTransition
* tr
= ds
->transitions
[choose
];
2500 if (tr
->Flags
&IE_DLG_TR_JOURNAL
) {
2502 if (tr
->Flags
&IE_DLG_UNSOLVED
) {
2505 if (tr
->Flags
&IE_DLG_SOLVED
) {
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');
2516 core
->DisplayString( 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
;
2547 ta
->SetMinRow( false );
2551 // all dialog actions must be executed immediately
2552 target
->ProcessActions(true);
2553 // (do not clear actions - final actions can involve waiting/moving)
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!
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) {
2575 // then just search the current area for an actor with the dialog
2576 tgt
= target
->GetCurrentArea()->GetActorByDialog(tr
->Dialog
);
2580 printMessage("Dialog","Can't redirect dialog\n",YELLOW
);
2581 ta
->SetMinRow( false );
2585 targetID
= tgt
->globalID
;
2586 // we have to make a backup, tr->Dialog is freed
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 );
2597 int ret
= InitDialog( speaker
, target
, tmpresref
);
2599 // error was displayed by InitDialog
2600 ta
->SetMinRow( false );
2605 ds
= dlg
->GetState( si
);
2607 printMessage("Dialog","Can't find next dialog\n",YELLOW
);
2608 ta
->SetMinRow( false );
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);
2619 ta
->SetMinRow( true );
2620 //first looking for a 'continue' opportunity, the order is descending (a la IE)
2621 unsigned int x
= ds
->transitionsCount
;
2623 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_FINAL
) {
2626 if (ds
->transitions
[x
]->textStrRef
!= 0xffffffff) {
2629 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_TRIGGER
) {
2630 if (ds
->transitions
[x
]->condition
&&
2631 !ds
->transitions
[x
]->condition
->Evaluate(target
)) {
2635 core
->GetDictionary()->SetAt("DialogOption",x
);
2636 DialogueFlags
|= DF_OPENCONTINUEWINDOW
;
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
)) {
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
;
2653 char *string
= ( char * ) malloc( 40 );
2654 sprintf( string
, "[s=%d,ffffff,ff0000]%d - [p]", x
, idx
);
2655 i
= ta
->AppendText( string
, -1 );
2657 string
= core
->GetString( ds
->transitions
[x
]->textStrRef
);
2658 ta
->AppendText( string
, i
);
2660 ta
->AppendText( "[/p][/s]", i
);
2663 // this happens if a trigger isn't implemented or the dialog is wrong
2665 printMessage("Dialog", "There were no valid dialog options!\n", YELLOW
);
2666 DialogueFlags
|= DF_OPENENDWINDOW
;
2669 //padding the rows so our text will be at the top
2670 if (core
->HasFeature( GF_DIALOGUE_SCROLLS
)) {
2671 ta
->AppendText( "", -1 );
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;
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) ) {
2713 overInfoPoint
= NULL
;
2714 overContainer
= NULL
;
2716 /*this is loadmap, because we need the index, not the pointer*/
2717 char *areaname
= game
->CurrentArea
;
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
)
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
)
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
;
2759 screenshot
= core
->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) );
2761 int hf
= HideGUI ();
2763 screenshot
= core
->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) );
2767 core
->DrawWindows ();
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;
2801 int hf
= HideGUI ();
2802 signed char v
= Owner
->Visible
;
2803 Owner
->Visible
= WINDOW_VISIBLE
;
2806 Sprite2D
*screenshot
= video
->GetScreenshot( Region(x
, y
, w
, h
) );
2810 core
->DrawWindows();
2812 Sprite2D
* preview
= video
->SpriteScaleDown ( screenshot
, 5 );
2813 video
->FreeSprite( screenshot
);
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 );
2834 ResourceHolder
<ImageMgr
> im(actor
->GetPortrait(true));
2839 Sprite2D
* img
= im
->GetSprite2D();
2844 Sprite2D
* img_scaled
= video
->SpriteScaleDown( img
, ratio
);
2845 video
->FreeSprite( img
);
2850 Actor
*GameControl::GetActorByGlobalID(ieWord ID
)
2854 Game
* game
= core
->GetGame();
2858 Map
* area
= game
->GetCurrentArea( );
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
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
)
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
;
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
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
)
2911 target_mode
= TARGET_MODE_CAST
;
2912 target_types
= targettype
;
2916 //another method inherited from Control which has no use here
2917 bool GameControl::SetEvent(int /*eventType*/, EventHandler
/*handler*/)
2922 void GameControl::SetDisplayText(char *text
, unsigned int time
)
2925 core
->FreeString(DisplayText
);
2927 DisplayTextTime
= time
;
2931 void GameControl::SetDisplayText(ieStrRef text
, unsigned int time
)
2933 SetDisplayText(core
->GetString(core
->GetStringReference(text
), 0), time
);