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.
25 #include "GameControl.h"
26 #include "Interface.h"
27 #include "DialogMgr.h"
33 #include "ScriptEngine.h"
36 #include "SaveGameIterator.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
, 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()
274 AutoTable
tab("savegame");
276 folder
= tab
->QueryField(0);
277 core
->GetSaveGameIterator()->CreateSaveGame(0, folder
, 0);
281 //QuickSave was triggered by the GUI
282 //mqs is the 'multiple quick saves' flag
283 void GameControl::QuickSave()
286 AutoTable
tab("savegame");
288 folder
= tab
->QueryField(1);
289 core
->GetSaveGameIterator()->CreateSaveGame(1, folder
, mqs
== 1);
293 // ArrowSprite cycles
307 static const int arrow_orientations
[16]={
308 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
309 -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1
312 //Draws arrow markers along the edge of the game window
313 //WARNING:don't use reference for point, because it is altered
314 void GameControl::DrawArrowMarker(Region
&screen
, Point p
, Region
&viewport
)
316 Video
* video
= core
->GetVideoDriver();
321 if (p
.x
<viewport
.x
) {
325 if (p
.y
<viewport
.y
) {
331 Sprite2D
*spr
= core
->GetScrollCursorSprite(0,0);
334 //tmp = core->ArrowSprites[0]->Width;
336 if (p
.x
>viewport
.x
+viewport
.w
-tmp
) {
337 p
.x
=viewport
.x
+viewport
.w
;//-tmp;
342 //tmp = core->ArrowSprites[0]->Height;
344 if (p
.y
>viewport
.y
+viewport
.h
-tmp
) {
345 p
.y
=viewport
.y
+viewport
.h
;//-tmp;
348 if (arrow_orientations
[draw
]>=0) {
349 video
->BlitGameSprite( core
->GetScrollCursorSprite(arrow_orientations
[draw
], 0), p
.x
+screen
.x
, p
.y
+screen
.y
, 0, black
, NULL
);
353 /** Draws the Control on the Output Display */
354 void GameControl::Draw(unsigned short x
, unsigned short y
)
356 bool update_scripts
= !(DialogueFlags
& DF_FREEZE_SCRIPTS
);
358 Game
* game
= core
->GetGame();
362 if (((short) Width
) <=0 || ((short) Height
) <= 0) {
366 if (Owner
->Visible
!=WINDOW_VISIBLE
) {
370 Video
* video
= core
->GetVideoDriver();
371 Region viewport
= video
->GetViewport();
372 if (moveX
|| moveY
) {
375 Point mapsize
= core
->GetGame()->GetCurrentArea()->TMap
->GetMapSize();
376 if ( viewport
.x
< 0 )//if we are at top of the map
378 else if ( (viewport
.x
+ viewport
.w
) >= mapsize
.x
) //if we are at the bottom
379 viewport
.x
= mapsize
.x
- viewport
.w
;
381 if ( viewport
.y
< 0 ) //if we are at the left of the map
383 else if ( (viewport
.y
+ viewport
.h
) >= mapsize
.y
) //if we are at the right
384 viewport
.y
= mapsize
.y
- viewport
.h
;
386 // override any existing viewport moves which may be in progress
387 core
->timer
->SetMoveViewPort( viewport
.x
, viewport
.y
, 0, false );
388 // move it directly ourselves, since we might be paused
389 video
->MoveViewportTo( viewport
.x
, viewport
.y
);
391 Region
screen( x
+ XPos
, y
+ YPos
, Width
, Height
);
392 Map
* area
= game
->GetCurrentArea( );
394 video
->DrawRect( screen
, blue
, true );
397 video
->DrawRect( screen
, black
, true );
402 for (idx
= 0; (i
= area
->TMap
->GetInfoPoint( idx
)); idx
++) {
403 i
->Highlight
= false;
404 if (overInfoPoint
== i
&& target_mode
) {
405 if (i
->VisibleTrap(0)) {
406 i
->outlineColor
= green
;
411 if (i
->VisibleTrap(DebugFlags
& DEBUG_SHOW_INFOPOINTS
)) {
412 i
->outlineColor
= red
; // traps
413 } else if (DebugFlags
& DEBUG_SHOW_INFOPOINTS
) {
414 i
->outlineColor
= blue
; // debug infopoints
422 for (idx
= 0; (d
= area
->TMap
->GetDoor( idx
)); idx
++) {
423 d
->Highlight
= false;
426 if (d
->VisibleTrap(0) || (d
->Flags
& DOOR_LOCKED
)) {
427 // only highlight targettable doors
428 d
->outlineColor
= green
;
432 } else if (!(d
->Flags
& DOOR_SECRET
)) {
433 // mouse over, not in target mode, no secret door
434 d
->outlineColor
= cyan
;
439 if (d
->VisibleTrap(0)) {
440 d
->outlineColor
= red
; // traps
441 } else if (d
->Flags
& DOOR_SECRET
) {
442 if (DebugFlags
& DEBUG_SHOW_DOORS
|| d
->Flags
& DOOR_FOUND
) {
443 d
->outlineColor
= magenta
; // found hidden door
445 // secret door is invisible
448 } else if (DebugFlags
& DEBUG_SHOW_DOORS
) {
449 d
->outlineColor
= cyan
; // debug doors
457 for (idx
= 0; (c
= area
->TMap
->GetContainer( idx
)); idx
++) {
458 c
->Highlight
= false;
459 if (overContainer
== c
&& target_mode
) {
460 if (c
->VisibleTrap(0) || (c
->Flags
& CONT_LOCKED
)) {
461 // only highlight targettable containers
462 c
->outlineColor
= green
;
466 } else if (overContainer
== c
) {
467 // mouse over, not in target mode
468 c
->outlineColor
= cyan
;
472 if (c
->VisibleTrap(0)) {
473 c
->outlineColor
= red
; // traps
474 } else if (DebugFlags
& DEBUG_SHOW_CONTAINERS
) {
475 c
->outlineColor
= cyan
; // debug containers
482 //drawmap should be here so it updates fog of war
483 area
->DrawMap( screen
);
484 game
->DrawWeather(screen
, update_scripts
);
487 Actor
*actor
= area
->GetActorByGlobalID(trackerID
);
490 Actor
**monsters
= area
->GetAllActorsInRadius(actor
->Pos
, GA_NO_DEAD
, distance
);
494 Actor
*target
= monsters
[i
++];
495 if (target
->InParty
) continue;
496 if (target
->GetStat(IE_NOTRACKING
)) continue;
497 DrawArrowMarker(screen
, target
->Pos
, viewport
);
505 if (ScreenFlags
& SF_DISABLEMOUSE
)
507 Point
p(lastMouseX
, lastMouseY
);
508 video
->ConvertToGame( p
.x
, p
.y
);
510 // Draw selection rect
511 if (DrawSelectionRect
) {
512 CalculateSelection( p
);
513 video
->DrawRect( SelectionRect
, green
, false, true );
517 if (DebugFlags
& DEBUG_SHOW_INFOPOINTS
) {
519 unsigned int count
= area
->GetWallCount();
520 for (unsigned int i
= 0; i
< count
; ++i
) {
521 Wall_Polygon
* poly
= area
->GetWallGroup(i
);
529 //if polygon is disabled, make it grey
530 if (poly
->wall_flag
&WF_DISABLED
) {
534 video
->DrawPolyline( poly
, c
, true );
540 PathNode
* node
= drawPath
;
542 Point
p( ( node
-> x
*16) + 8, ( node
->y
*12 ) + 6 );
544 video
->DrawCircle( p
.x
, p
.y
, 2, red
);
546 short oldX
= ( node
->Parent
-> x
*16) + 8, oldY
= ( node
->Parent
->y
*12 ) + 6;
547 video
->DrawLine( oldX
, oldY
, p
.x
, p
.y
, green
);
550 video
->DrawCircle( p
.x
, p
.y
, 2, green
);
558 if (DebugFlags
& DEBUG_SHOW_LIGHTMAP
) {
559 Sprite2D
* spr
= area
->LightMap
->GetSprite2D();
560 video
->BlitSprite( spr
, 0, 0, true );
561 video
->FreeSprite( spr
);
562 Region
point( p
.x
/ 16, p
.y
/ 12, 2, 2 );
563 video
->DrawRect( point
, red
);
566 if (core
->HasFeature(GF_ONSCREEN_TEXT
) && DisplayText
) {
567 core
->GetFont(1)->Print(screen
, (unsigned char *)DisplayText
, core
->InfoTextPalette
, IE_FONT_ALIGN_CENTER
| IE_FONT_ALIGN_MIDDLE
, true);
568 if (update_scripts
) {
569 // just replicating original engine behaviour
570 if (DisplayTextTime
== 0) {
571 SetDisplayText((char *)NULL
, 0);
579 /** inherited from Control, GameControl doesn't need it */
580 int GameControl::SetText(const char* /*string*/, int /*pos*/)
585 /** Key Press Event */
586 void GameControl::OnKeyPress(unsigned char Key
, unsigned short /*Mod*/)
588 if (DialogueFlags
&DF_IN_DIALOG
) {
592 Game
* game
= core
->GetGame();
597 game
->SelectActor( NULL
, false, SELECT_NORMAL
);
598 i
= game
->GetPartySize(false)/2;
600 SelectActor(i
, true);
604 game
->SelectActor( NULL
, true, SELECT_NORMAL
);
605 i
= game
->GetPartySize(false)/2;
607 SelectActor(i
, false);
622 SelectActor(Key
-'0');
625 core
->GetGame()->SetHotKey(toupper(Key
));
630 //Select (or deselect) a new actor (or actors)
631 void GameControl::SelectActor(int whom
, int type
)
633 Game
* game
= core
->GetGame();
635 game
->SelectActor( NULL
, true, SELECT_NORMAL
);
639 /* doesn't fall through here */
640 Actor
* actor
= game
->FindPC( whom
);
645 game
->SelectActor( actor
, false, SELECT_NORMAL
);
649 game
->SelectActor( actor
, true, SELECT_NORMAL
);
653 bool was_selected
= actor
->IsSelected();
654 if (game
->SelectActor( actor
, true, SELECT_REPLACE
))
655 if (was_selected
|| (ScreenFlags
& SF_ALWAYSCENTER
)) {
656 ScreenFlags
|= SF_CENTERONACTOR
;
660 //Effect for the ctrl-r cheatkey (resurrect)
661 static EffectRef heal_ref
={"CurrentHPModifier", NULL
, -1};
662 static EffectRef damage_ref
={"Damage", NULL
, -1};
664 /** Key Release Event */
665 void GameControl::OnKeyRelease(unsigned char Key
, unsigned short Mod
)
668 Game
* game
= core
->GetGame();
673 if (DialogueFlags
&DF_IN_DIALOG
) {
686 TextArea
*ta
= core
->GetMessageTextArea();
688 ta
->OnKeyPress(Key
,Mod
);
695 //cheatkeys with ctrl-
696 if (Mod
& GEM_MOD_CTRL
) {
697 if (!core
->CheatEnabled()) {
700 Map
* area
= game
->GetCurrentArea( );
703 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
704 Point
p(lastMouseX
, lastMouseY
);
705 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
707 case 'f': //toggle full screen mode
708 core
->GetVideoDriver()->ToggleFullscreenMode();
710 case 'd': //disarm a trap
712 overInfoPoint
->DetectTrap(256);
715 if (overContainer
->Trapped
&&
716 !( overContainer
->TrapDetected
)) {
717 overContainer
->TrapDetected
= 1;
721 if (overDoor
->Trapped
&&
722 !( overDoor
->TrapDetected
)) {
723 overDoor
->TrapDetected
= 1;
727 case 'l': //play an animation (vvc/bam) over an actor
728 //the original engine was able to swap through all animations
730 lastActor
->AddAnimation("S056ICBL", 0, 0, 0);
734 case 'c': //force cast a hardcoded spell
735 //caster is the last selected actor
736 //target is the door/actor currently under the pointer
737 if (game
->selected
.size() > 0) {
738 Actor
*src
= game
->selected
[0];
739 Scriptable
*target
= lastActor
;
744 src
->CastSpell( TestSpell
, target
, false );
745 if (src
->LastTarget
) {
746 src
->CastSpellEnd( TestSpell
);
748 src
->CastSpellPointEnd( TestSpell
);
754 case 'b': //draw a path to the target (pathfinder debug)
755 //You need to select an origin with ctrl-o first
757 PathNode
* nextNode
= drawPath
->Next
;
758 PathNode
* thisNode
= drawPath
;
764 nextNode
= thisNode
->Next
;
767 drawPath
= core
->GetGame()->GetCurrentArea()->FindPath( pfs
, p
, lastActor
?lastActor
->size
:1 );
771 case 'o': //set up the origin for the pathfinder
775 core
->GetVideoDriver()->ConvertToGame( pfs
.x
, pfs
.y
);
777 case 'a': //switches through the avatar animations
779 lastActor
->GetNextAnimation();
782 case 's': //switches through the stance animations
784 lastActor
->GetNextStance();
787 case 'j': //teleports the selected actors
788 for (i
= 0; i
< game
->selected
.size(); i
++) {
789 Actor
* actor
= game
->selected
[i
];
790 MoveBetweenAreasCore(actor
, core
->GetGame()->CurrentArea
, p
, -1, true);
791 printf( "Teleported to %d, %d\n", p
.x
, p
.y
);
795 case 'm': //prints a debug dump (ctrl-m in the original game too)
797 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
800 // ValidTarget never returns immobile targets, making debugging a nightmare
801 // so if we don't have an actor, we make really really sure by checking manually
802 unsigned int count
= area
->GetActorCount(true);
804 Actor
*actor
= area
->GetActor(count
, true);
805 if (actor
->IsOver(p
)) {
811 lastActor
->DebugDump();
815 overDoor
->DebugDump();
819 overContainer
->DebugDump();
823 overInfoPoint
->DebugDump();
826 core
->GetGame()->GetCurrentArea()->DebugDump(Mod
& GEM_MOD_SHIFT
);
828 case 'v': //marks some of the map visited (random vision distance)
829 area
->ExploreMapChunk( p
, rand()%30, 1 );
831 case 'x': // shows coordinates on the map
832 printf( "%s [%d.%d]\n", area
->GetScriptName(), p
.x
, p
.y
);
834 case 'g'://shows loaded areas and other game information
837 case 'i'://interact trigger (from the original game)
839 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
841 if (lastActor
&& !(lastActor
->GetStat(IE_MC_FLAGS
)&MC_EXPORTABLE
)) {
843 int i
= game
->GetPartySize(true);
848 target
= game
->GetPC(i
, true);
849 if(target
==lastActor
) continue;
850 if(target
->GetStat(IE_MC_FLAGS
)&MC_EXPORTABLE
) continue;
853 snprintf(Tmp
,sizeof(Tmp
),"Interact(\"%s\")",target
->GetScriptName() );
854 lastActor
->AddAction(GenerateAction(Tmp
));
860 case 'r'://resurrects actor
862 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
865 Effect
*fx
= EffectQueue::CreateEffect(heal_ref
, lastActor
->GetBase(IE_MAXHITPOINTS
), 0x30001, FX_DURATION_INSTANT_PERMANENT
);
867 core
->ApplyEffect(fx
, lastActor
, lastActor
);
871 case 't'://advances time
872 // 7200 (one day) /24 (hours) == 300
873 game
->AdvanceTime(300*AI_UPDATE_TIME
);
874 //refresh gui here once we got it
877 case 'q': //joins actor to the party
878 if (lastActor
&& !lastActor
->InParty
) {
879 lastActor
->ClearActions();
880 lastActor
->ClearPath();
882 strncpy(Tmp
,"JoinParty()",sizeof(Tmp
) );
883 lastActor
->AddAction( GenerateAction(Tmp
) );
886 case 'p': //center on actor
887 ScreenFlags
|=SF_CENTERONACTOR
;
888 ScreenFlags
^=SF_ALWAYSCENTER
;
890 case 'k': //kicks out actor
891 if (lastActor
&& lastActor
->InParty
) {
892 lastActor
->ClearActions();
893 lastActor
->ClearPath();
895 strncpy(Tmp
,"LeaveParty()",sizeof(Tmp
) );
896 lastActor
->AddAction( GenerateAction(Tmp
) );
899 case 'y': //kills actor or all enemies
900 if (Mod
& GEM_MOD_SHIFT
) {
903 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_MAGIC
<<16, FX_DURATION_INSTANT_PERMANENT
);
905 for (int i
= area
->GetActorCount(0)-1; i
>= 0; i
--) {
906 victim
= area
->GetActor(i
, 0);
907 if (victim
->Modified
[IE_EA
] == EA_ENEMY
) {
908 core
->ApplyEffect(newfx
, victim
, victim
);
914 //using action so the actor is killed
915 //correctly (synchronisation)
916 lastActor
->ClearActions();
917 lastActor
->ClearPath();
920 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_MAGIC
<<16, FX_DURATION_INSTANT_PERMANENT
);
921 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
922 if (! (lastActor
->GetInternalFlag() & IF_REALLYDIED
)) {
923 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_ACID
<<16, FX_DURATION_INSTANT_PERMANENT
);
924 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
925 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_CRUSHING
<<16, FX_DURATION_INSTANT_PERMANENT
);
926 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
929 } else if (overContainer
) {
930 overContainer
->SetContainerLocked(0);
931 } else if (overDoor
) {
932 overDoor
->SetDoorLocked(0,0);
936 case 'z': //shift through the avatar animations backward
938 lastActor
->GetPrevAnimation();
941 case '1': //change paperdoll armour level
944 lastActor
->NewStat(IE_ARMOR_TYPE
,1,MOD_ADDITIVE
);
946 case '4': //show all traps and infopoints
947 DebugFlags
^= DEBUG_SHOW_INFOPOINTS
;
948 printf("Show traps and infopoints %s\n", DebugFlags
& DEBUG_SHOW_INFOPOINTS
? "ON" : "OFF");
950 case '6': //show the lightmap
951 DebugFlags
^= DEBUG_SHOW_LIGHTMAP
;
952 printf("Show lightmap %s\n", DebugFlags
& DEBUG_SHOW_LIGHTMAP
? "ON" : "OFF");
954 case '7': //toggles fog of war
956 printf("Show Fog-Of-War: %s\n", core
->FogOfWar
& 1 ? "ON" : "OFF");
958 case '8': //show searchmap over area
960 printf("Show searchmap %s\n", core
->FogOfWar
& 2 ? "ON" : "OFF");
963 printf( "KeyRelease:%d - %d\n", Key
, Mod
);
966 return; //return from cheatkeys
969 case 'h': //hard pause
970 if (DialogueFlags
& DF_FREEZE_SCRIPTS
) break;
972 case ' ': //soft pause
973 DialogueFlags
^= DF_FREEZE_SCRIPTS
;
974 if (DialogueFlags
&DF_FREEZE_SCRIPTS
) {
975 core
->DisplayConstantString(STR_PAUSED
,0xff0000);
976 SetDisplayText(STR_PAUSED
, 0); // time 0 = removed instantly on unpause
978 core
->DisplayConstantString(STR_UNPAUSED
,0xff0000);
982 core
->GetGUIScriptEngine()->RunFunction("OpenMapWindow");
985 core
->GetGUIScriptEngine()->RunFunction("OpenJournalWindow");
988 core
->GetGUIScriptEngine()->RunFunction("OpenInventoryWindow");
991 core
->GetGUIScriptEngine()->RunFunction("OpenRecordsWindow");
993 case 'q': //quicksave
996 case GEM_ALT
: //alt key (shows containers)
997 DebugFlags
&= ~DEBUG_SHOW_CONTAINERS
;
1004 void GameControl::DisplayTooltip() {
1005 Game
* game
= core
->GetGame();
1007 Map
* area
= game
->GetCurrentArea( );
1009 Actor
*actor
= area
->GetActorByGlobalID(lastActorID
);
1010 if (actor
&& (actor
->GetStat(IE_STATE_ID
)&STATE_DEAD
|| actor
->GetInternalFlag()&IF_JUSTDIED
)) {
1011 // checking IF_JUSTDIED is kind of horrid, but seems necessary
1012 // no tooltips for dead actors!
1013 actor
->SetOver( false );
1019 char *name
= actor
->GetName(-1);
1020 int hp
= actor
->GetStat(IE_HITPOINTS
);
1021 int maxhp
= actor
->GetStat(IE_MAXHITPOINTS
);
1024 if (!core
->TooltipBack
) {
1025 // single-line tooltips without background (PS:T)
1026 if (actor
->InParty
) {
1027 snprintf(buffer
, 100, "%s: %d/%d", name
, hp
, maxhp
);
1029 snprintf(buffer
, 100, "%s", name
);
1032 // a guess at a neutral check
1033 bool neutral
= actor
->GetStat(IE_EA
) == EA_NEUTRAL
;
1034 // test for an injured string being present for this game
1035 int strindex
= core
->GetStringReference(STR_UNINJURED
);
1037 if (actor
->InParty
) {
1038 // in party: display hp
1039 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1040 } else if (neutral
) {
1041 // neutral: display name only
1042 snprintf(buffer
, 100, "%s", name
);
1043 } else if (strindex
== -1) {
1044 // non-neutral, not in party, no injured strings: display hp
1045 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1047 // non-neutral, not in party: display injured string
1049 char *injuredstring
= NULL
;
1050 // these boundaries are just a guess
1052 strindex
= STR_UNINJURED
;
1053 } else if (hp
> (maxhp
*3)/4) {
1054 strindex
= STR_INJURED1
;
1055 } else if (hp
> maxhp
/2) {
1056 strindex
= STR_INJURED2
;
1057 } else if (hp
> maxhp
/3) {
1058 strindex
= STR_INJURED3
;
1060 strindex
= STR_INJURED4
;
1062 strindex
= core
->GetStringReference(strindex
);
1063 if (strindex
!= -1) {
1064 injuredstring
= core
->GetString(strindex
, 0);
1067 if (!injuredstring
) {
1068 // eek, where did the string go?
1069 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1071 snprintf(buffer
, 100, "%s\n%s", name
, injuredstring
);
1072 free(injuredstring
);
1077 Point p
= actor
->Pos
;
1078 core
->GetVideoDriver()->ConvertToScreen( p
.x
, p
.y
);
1079 p
.x
+= Owner
->XPos
+ XPos
;
1080 p
.y
+= Owner
->YPos
+ YPos
;
1082 // hack to position text above PS:T actors
1083 if (!core
->TooltipBack
) p
.y
-= actor
->size
*50;
1085 // we should probably cope better with moving actors
1087 core
->DisplayTooltip(p
.x
, p
.y
, this);
1094 core
->DisplayTooltip(0, 0, NULL
);
1098 //returns the appropriate cursor over an active region (trap, infopoint, travel region)
1099 int GameControl::GetCursorOverInfoPoint(InfoPoint
*overInfoPoint
)
1101 if (target_mode
== TARGET_MODE_PICK
) {
1102 if (overInfoPoint
->VisibleTrap(0)) {
1103 return IE_CURSOR_TRAP
;
1106 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1108 // traps always display a walk cursor?
1109 if (overInfoPoint
->Type
== ST_PROXIMITY
) {
1110 return IE_CURSOR_WALK
;
1112 return overInfoPoint
->Cursor
;
1115 //returns the appropriate cursor over a door
1116 int GameControl::GetCursorOverDoor(Door
*overDoor
)
1118 if (target_mode
== TARGET_MODE_PICK
) {
1119 if (overDoor
->VisibleTrap(0)) {
1120 return IE_CURSOR_TRAP
;
1122 if (overDoor
->Flags
& DOOR_LOCKED
) {
1123 return IE_CURSOR_LOCK
;
1126 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1128 return overDoor
->Cursor
;
1131 //returns the appropriate cursor over a container (or pile)
1132 int GameControl::GetCursorOverContainer(Container
*overContainer
)
1134 if (target_mode
== TARGET_MODE_PICK
) {
1135 if (overContainer
->VisibleTrap(0)) {
1136 return IE_CURSOR_TRAP
;
1138 if (overContainer
->Flags
& CONT_LOCKED
) {
1139 return IE_CURSOR_LOCK2
;
1142 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1144 return IE_CURSOR_TAKE
;
1147 /** Mouse Over Event */
1148 void GameControl::OnMouseOver(unsigned short x
, unsigned short y
)
1150 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1157 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
1158 if (MouseIsDown
&& ( !DrawSelectionRect
)) {
1159 if (( abs( p
.x
- StartX
) > 5 ) || ( abs( p
.y
- StartY
) > 5 )) {
1160 DrawSelectionRect
= true;
1163 Game
* game
= core
->GetGame();
1165 Map
* area
= game
->GetCurrentArea( );
1167 int nextCursor
= area
->GetCursor( p
);
1168 //make the invisible area really invisible
1169 if (nextCursor
== IE_CURSOR_INVALID
) {
1170 Owner
->Cursor
= IE_CURSOR_BLOCKED
;
1171 lastCursor
= IE_CURSOR_BLOCKED
;
1175 overInfoPoint
= area
->TMap
->GetInfoPoint( p
, true );
1176 if (overInfoPoint
) {
1177 //nextCursor = overInfoPoint->Cursor;
1178 nextCursor
= GetCursorOverInfoPoint(overInfoPoint
);
1182 overDoor
->Highlight
= false;
1184 if (overContainer
) {
1185 overContainer
->Highlight
= false;
1187 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
1189 lastActor
->SetOver( false );
1192 overDoor
= area
->TMap
->GetDoor( p
);
1193 overContainer
= area
->TMap
->GetContainer( p
);
1195 if (!DrawSelectionRect
) {
1197 nextCursor
= GetCursorOverDoor(overDoor
);
1200 if (overContainer
) {
1201 nextCursor
= GetCursorOverContainer(overContainer
);
1204 Actor
*prevActor
= lastActor
;
1205 lastActor
= area
->GetActor( p
, target_types
);
1206 if (lastActor
!= prevActor
) {
1207 // we store prevActor so we can remove the tooltip on actor change
1208 // (maybe we should be checking this and actor movements every frame?)
1210 core
->DisplayTooltip(0, 0, this);
1213 if ((target_types
& GA_NO_SELF
) && lastActor
) {
1214 if (lastActor
== core
->GetFirstSelectedPC(false)) {
1220 lastActorID
= lastActor
->globalID
;
1221 lastActor
->SetOver( true );
1222 ieDword type
= lastActor
->GetStat(IE_EA
);
1223 if (type
>= EA_EVILCUTOFF
|| type
== EA_GOODBUTRED
) {
1224 nextCursor
= IE_CURSOR_ATTACK
;
1225 } else if ( type
> EA_CHARMED
) {
1226 nextCursor
= IE_CURSOR_TALK
;
1228 nextCursor
= IE_CURSOR_NORMAL
;
1234 if (target_mode
== TARGET_MODE_TALK
) {
1235 nextCursor
= IE_CURSOR_TALK
;
1237 nextCursor
|= IE_CURSOR_GRAY
;
1239 } else if (target_mode
== TARGET_MODE_ATTACK
) {
1240 nextCursor
= IE_CURSOR_ATTACK
;
1241 if (!lastActor
&& !overDoor
&& !overContainer
) {
1242 nextCursor
|= IE_CURSOR_GRAY
;
1244 } else if (target_mode
== TARGET_MODE_CAST
) {
1245 nextCursor
= IE_CURSOR_CAST
;
1246 //point is always valid
1247 if (!(target_types
& GA_POINT
)) {
1249 nextCursor
|= IE_CURSOR_GRAY
;
1252 } else if (target_mode
== TARGET_MODE_DEFEND
) {
1253 nextCursor
= IE_CURSOR_DEFEND
;
1255 nextCursor
|= IE_CURSOR_GRAY
;
1257 } else if (target_mode
== TARGET_MODE_PICK
) {
1259 nextCursor
= IE_CURSOR_PICK
;
1261 if (!overContainer
&& !overDoor
&& !overInfoPoint
) {
1262 nextCursor
= IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1269 switch (lastActor
->GetStat(IE_EA
)) {
1279 case EA_EVILBUTGREEN
:
1280 if (target_types
& GA_NO_ENEMY
)
1286 if (target_types
& GA_NO_ALLY
)
1290 if (!(target_types
& GA_NO_NEUTRAL
))
1297 if (lastCursor
!= nextCursor
) {
1298 Owner
->Cursor
= nextCursor
;
1299 lastCursor
= (unsigned char) nextCursor
;
1303 #define SCROLL_BORDER 5
1305 /** Global Mouse Move Event */
1306 void GameControl::OnGlobalMouseMove(unsigned short x
, unsigned short y
)
1308 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1312 if (Owner
->Visible
!=WINDOW_VISIBLE
) {
1316 int mousescrollspd
= core
->GetMouseScrollSpeed();
1318 if (x
<= SCROLL_BORDER
)
1319 moveX
= -mousescrollspd
;
1321 if (x
>= ( core
->Width
- SCROLL_BORDER
))
1322 moveX
= mousescrollspd
;
1326 if (y
<= SCROLL_BORDER
)
1327 moveY
= -mousescrollspd
;
1329 if (y
>= ( core
->Height
- SCROLL_BORDER
))
1330 moveY
= mousescrollspd
;
1335 if (moveX
!= 0 || moveY
!= 0) {
1337 } else if (scrolling
) {
1340 Video
* video
= core
->GetVideoDriver();
1341 video
->SetDragCursor(NULL
);
1345 void GameControl::UpdateScrolling() {
1346 if (!scrolling
) return;
1348 int mousescrollspd
= core
->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-?
1349 Video
* video
= core
->GetVideoDriver();
1351 if (moveX
== mousescrollspd
&& moveY
== 0) { // right
1352 video
->SetDragCursor(core
->GetScrollCursorSprite(0,numScrollCursor
));
1353 } else if (moveX
== mousescrollspd
&& moveY
== -mousescrollspd
) { // upper right
1354 video
->SetDragCursor(core
->GetScrollCursorSprite(1,numScrollCursor
));
1355 } else if (moveX
== 0 && moveY
== -mousescrollspd
) { // up
1356 video
->SetDragCursor(core
->GetScrollCursorSprite(2,numScrollCursor
));
1357 } else if (moveX
== -mousescrollspd
&& moveY
== -mousescrollspd
) { // upper left
1358 video
->SetDragCursor(core
->GetScrollCursorSprite(3,numScrollCursor
));
1359 } else if (moveX
== -mousescrollspd
&& moveY
== 0) { // left
1360 video
->SetDragCursor(core
->GetScrollCursorSprite(4,numScrollCursor
));
1361 } else if (moveX
== -mousescrollspd
&& moveY
== mousescrollspd
) { // bottom left
1362 video
->SetDragCursor(core
->GetScrollCursorSprite(5,numScrollCursor
));
1363 } else if (moveX
== 0 && moveY
== mousescrollspd
) { // bottom
1364 video
->SetDragCursor(core
->GetScrollCursorSprite(6,numScrollCursor
));
1365 } else if (moveX
== mousescrollspd
&& moveY
== mousescrollspd
) { // bottom right
1366 video
->SetDragCursor(core
->GetScrollCursorSprite(7,numScrollCursor
));
1369 numScrollCursor
= (numScrollCursor
+1) % 15;
1372 //generate action code for source actor to try to attack a target
1373 void GameControl::TryToAttack(Actor
*source
, Actor
*tgt
)
1377 source
->ClearPath();
1378 source
->ClearActions();
1379 strncpy(Tmp
,"NIDSpecial3()",sizeof(Tmp
) );
1380 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1383 //generate action code for source actor to try to defend a target
1384 void GameControl::TryToDefend(Actor
*source
, Actor
*tgt
)
1388 source
->ClearPath();
1389 source
->ClearActions();
1390 strncpy(Tmp
,"NIDSpecial4()",sizeof(Tmp
) );
1391 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1394 //generate action code for source actor to try to pick pockets of a target
1395 //The -1 flag is a placeholder for dynamic target IDs
1396 void GameControl::TryToPick(Actor
*source
, Actor
*tgt
)
1400 source
->ClearPath();
1401 source
->ClearActions();
1402 strncpy(Tmp
,"PickPockets([-1])", sizeof(Tmp
) );
1403 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1406 //generate action code for source actor to try to pick a lock/disable trap on a door
1407 void GameControl::TryToPick(Actor
*source
, Door
*tgt
)
1411 source
->ClearPath();
1412 source
->ClearActions();
1413 if (tgt
->Trapped
&& tgt
->TrapDetected
) {
1414 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1416 snprintf(Tmp
, sizeof(Tmp
), "PickLock(\"%s\")", tgt
->GetScriptName() );
1418 source
->AddAction( GenerateAction( Tmp
) );
1421 //generate action code for source actor to try to pick a lock/disable trap on a container
1422 void GameControl::TryToPick(Actor
*source
, Container
*tgt
)
1426 source
->ClearPath();
1427 source
->ClearActions();
1428 if (tgt
->Trapped
&& tgt
->TrapDetected
) {
1429 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1431 snprintf(Tmp
, sizeof(Tmp
), "PickLock(\"%s\")", tgt
->GetScriptName() );
1433 source
->AddAction( GenerateAction( Tmp
) );
1436 //generate action code for source actor to try to disable trap (only trap type active regions)
1437 void GameControl::TryToDisarm(Actor
*source
, InfoPoint
*tgt
)
1439 if (tgt
->Type
!=ST_PROXIMITY
) return;
1443 source
->ClearPath();
1444 source
->ClearActions();
1445 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1446 source
->AddAction( GenerateAction( Tmp
) );
1449 //generate action code for source actor to try to force open lock on a door/container
1450 void GameControl::TryToBash(Actor
*source
, Scriptable
*tgt
)
1454 source
->ClearPath();
1455 source
->ClearActions();
1456 snprintf(Tmp
, sizeof(Tmp
), "Attack(\"%s\")", tgt
->GetScriptName() );
1457 source
->AddAction( GenerateAction( Tmp
) );
1460 //generate action code for source actor to use item/cast spell on a point
1461 void GameControl::TryToCast(Actor
*source
, Point
&tgt
)
1466 target_mode
= TARGET_MODE_NONE
;
1467 return; //not casting or using an own item
1469 source
->ClearPath();
1470 source
->ClearActions();
1473 if (spellOrItem
>=0) {
1474 sprintf(Tmp
, "NIDSpecial8()");
1476 //using item on target
1477 sprintf(Tmp
, "NIDSpecial7()");
1479 Action
* action
= GenerateAction( Tmp
);
1480 action
->pointParameter
=tgt
;
1483 CREMemorizedSpell
*si
;
1484 //spell casting at target
1485 si
= source
->spellbook
.GetMemorizedSpell(spellOrItem
, spellSlot
, spellIndex
);
1488 target_mode
= TARGET_MODE_NONE
;
1491 sprintf(action
->string0Parameter
,"%.8s",si
->SpellResRef
);
1495 action
->int0Parameter
=spellSlot
;
1496 action
->int1Parameter
=spellIndex
;
1498 source
->AddAction( action
);
1500 target_mode
= TARGET_MODE_NONE
;
1504 //generate action code for source actor to use item/cast spell on another actor
1505 void GameControl::TryToCast(Actor
*source
, Actor
*tgt
)
1510 target_mode
= TARGET_MODE_NONE
;
1511 return; //not casting or using an own item
1513 source
->ClearPath();
1514 source
->ClearActions();
1517 if (spellOrItem
>=0) {
1518 sprintf(Tmp
, "NIDSpecial6()");
1520 //using item on target
1521 sprintf(Tmp
, "NIDSpecial5()");
1523 Action
* action
= GenerateActionDirect( Tmp
, tgt
);
1526 CREMemorizedSpell
*si
;
1527 //spell casting at target
1528 si
= source
->spellbook
.GetMemorizedSpell(spellOrItem
, spellSlot
, spellIndex
);
1531 target_mode
= TARGET_MODE_NONE
;
1534 sprintf(action
->string0Parameter
,"%.8s",si
->SpellResRef
);
1538 action
->int0Parameter
=spellSlot
;
1539 action
->int1Parameter
=spellIndex
;
1541 source
->AddAction( action
);
1543 target_mode
= TARGET_MODE_NONE
;
1547 //generate action code for source actor to use talk to target actor
1548 void GameControl::TryToTalk(Actor
*source
, Actor
*tgt
)
1552 //Nidspecial1 is just an unused action existing in all games
1553 //(non interactive demo)
1554 //i found no fitting action which would emulate this kind of
1556 source
->ClearPath();
1557 source
->ClearActions();
1558 strncpy(Tmp
,"NIDSpecial1()",sizeof(Tmp
) );
1559 targetID
= tgt
->globalID
; //this is a hack, but not so deadly
1560 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1563 //generate action code for actor appropriate for the target mode when the target is a container
1564 void GameControl::HandleContainer(Container
*container
, Actor
*actor
)
1568 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1569 //we'll get the container back from the coordinates
1570 TryToCast(actor
, container
->Pos
);
1571 //Do not reset target_mode, TryToCast does it for us!!
1575 if (target_mode
== TARGET_MODE_ATTACK
) {
1576 TryToBash(actor
, container
);
1577 target_mode
= TARGET_MODE_NONE
;
1581 if ((target_mode
== TARGET_MODE_PICK
)) {
1582 TryToPick(actor
, container
);
1583 target_mode
= TARGET_MODE_NONE
;
1588 actor
->ClearActions();
1589 strncpy(Tmp
,"UseContainer()",sizeof(Tmp
) );
1590 core
->SetCurrentContainer( actor
, container
);
1591 actor
->AddAction( GenerateAction( Tmp
) );
1594 //generate action code for actor appropriate for the target mode when the target is a door
1595 void GameControl::HandleDoor(Door
*door
, Actor
*actor
)
1599 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1600 //we'll get the door back from the coordinates
1601 Point
*p
= door
->toOpen
;
1602 Point
*otherp
= door
->toOpen
+1;
1603 if (Distance(*p
,actor
)>Distance(*otherp
,actor
)) {
1606 TryToCast(actor
, *p
);
1610 if (target_mode
== TARGET_MODE_ATTACK
) {
1611 TryToBash(actor
, door
);
1612 target_mode
= TARGET_MODE_NONE
;
1616 if ( (target_mode
== TARGET_MODE_PICK
) || door
->TrapDetected
) {
1617 TryToPick(actor
, door
);
1618 target_mode
= TARGET_MODE_NONE
;
1623 actor
->ClearActions();
1624 // it really isn't very nice to store a pointer in the actor like this
1625 actor
->TargetDoor
= door
;
1626 // internal gemrb toggle door action hack - should we use UseDoor instead?
1627 sprintf( Tmp
, "NIDSpecial9()" );
1628 actor
->AddAction( GenerateAction( Tmp
) );
1631 //generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel)
1632 bool GameControl::HandleActiveRegion(InfoPoint
*trap
, Actor
* actor
, Point
&p
)
1634 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1635 //we'll get the active region from the coordinates (if needed)
1636 TryToCast(actor
, p
);
1637 //don't bother with this region further
1640 if ((target_mode
== TARGET_MODE_PICK
)) {
1641 TryToDisarm(actor
, trap
);
1642 target_mode
= TARGET_MODE_NONE
;
1646 switch(trap
->Type
) {
1648 actor
->UseExit(true);
1651 //the importer shouldn't load the script
1652 //if it is unallowed anyway (though
1653 //deactivated scripts could be reactivated)
1654 //only the 'trapped' flag should be honoured
1655 //there. Here we have to check on the
1656 //reset trap and deactivated flags
1657 if (trap
->Scripts
[0]) {
1658 if (!(trap
->Flags
&TRAP_DEACTIVATED
) ) {
1659 trap
->LastTriggerObject
= trap
->LastTrigger
= actor
->GetID();
1660 trap
->ImmediateEvent();
1661 //directly feeding the event, even if there are actions in the queue
1662 trap
->Scripts
[0]->Update();
1663 trap
->ProcessActions(true);
1664 //if reset trap flag not set, deactivate it
1665 //hmm, better not, info triggers don't deactivate themselves on click
1666 //if (!(trap->Flags&TRAP_RESET)) {
1667 // trap->Flags|=TRAP_DEACTIVATED;
1671 if (trap
->overHeadText
) {
1672 if (trap
->textDisplaying
!= 1) {
1673 trap
->textDisplaying
= 1;
1674 trap
->timeStartDisplaying
= core
->GetGame()->Ticks
;
1675 DisplayString( trap
);
1679 if (trap
->Flags
&TRAP_USEPOINT
) {
1680 //overriding the target point
1689 /** Mouse Button Down */
1690 void GameControl::OnMouseDown(unsigned short x
, unsigned short y
, unsigned short Button
,
1691 unsigned short /*Mod*/)
1693 if (ScreenFlags
&SF_DISABLEMOUSE
)
1698 DoubleClick
= false;
1702 OnSpecialKeyPress(GEM_UP
);
1704 case GEM_MB_SCRLDOWN
:
1705 OnSpecialKeyPress(GEM_DOWN
);
1707 case GEM_MB_ACTION
|GEM_MB_DOUBLECLICK
:
1710 core
->GetVideoDriver()->ConvertToGame( px
, py
);
1712 SelectionRect
.x
= px
;
1713 SelectionRect
.y
= py
;
1716 SelectionRect
.w
= 0;
1717 SelectionRect
.h
= 0;
1720 /** Mouse Button Up */
1721 void GameControl::OnMouseUp(unsigned short x
, unsigned short y
, unsigned short Button
,
1722 unsigned short /*Mod*/)
1727 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1730 //heh, i found no better place
1731 core
->CloseCurrentContainer();
1733 MouseIsDown
= false;
1735 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
1736 Game
* game
= core
->GetGame();
1737 Map
* area
= game
->GetCurrentArea( );
1739 if (DrawSelectionRect
) {
1741 unsigned int count
= area
->GetActorInRect( ab
, SelectionRect
,true );
1742 for (i
= 0; i
< highlighted
.size(); i
++)
1743 highlighted
[i
]->SetOver( false );
1744 highlighted
.clear();
1745 game
->SelectActor( NULL
, false, SELECT_NORMAL
);
1747 for (i
= 0; i
< count
; i
++) {
1748 // FIXME: should call handler only once
1749 game
->SelectActor( ab
[i
], true, SELECT_NORMAL
);
1753 DrawSelectionRect
= false;
1757 //hidden actors are not selectable by clicking on them
1758 Actor
* actor
= area
->GetActor( p
, GA_DEFAULT
| GA_SELECT
| GA_NO_DEAD
| GA_NO_HIDDEN
);
1759 if (Button
== GEM_MB_MENU
) {
1762 DisplayStringCore(actor
, VB_SELECT
+core
->Roll(1,3,-1), DS_CONST
|DS_CONSOLE
);
1765 core
->GetDictionary()->SetAt( "MenuX", x
);
1766 core
->GetDictionary()->SetAt( "MenuY", y
);
1767 core
->GetGUIScriptEngine()->RunFunction( "OpenFloatMenuWindow" );
1771 if (Button
!= GEM_MB_ACTION
) {
1775 if (!actor
&& ( game
->selected
.size() > 0 )) {
1777 HandleDoor(overDoor
, core
->GetFirstSelectedPC(false));
1780 if (overContainer
) {
1781 HandleContainer(overContainer
, core
->GetFirstSelectedPC(false));
1784 if (overInfoPoint
) {
1785 if (HandleActiveRegion(overInfoPoint
, core
->GetFirstSelectedPC(false), p
)) {
1790 //just a single actor, no formation
1791 if (game
->selected
.size()==1) {
1792 //the player is using an item or spell on the ground
1793 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1794 TryToCast(core
->GetFirstSelectedPC(false), p
);
1798 actor
=game
->selected
[0];
1800 actor
->ClearActions();
1801 CreateMovement(actor
, p
);
1804 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
1806 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
1809 actor->AddAction( GenerateAction( Tmp) );
1811 //p is a searchmap travel region
1812 if ( actor
->GetCurrentArea()->GetCursor(p
) == IE_CURSOR_TRAVEL
) {
1813 sprintf( Tmp
, "NIDSpecial2()" );
1814 actor
->AddAction( GenerateAction( Tmp
) );
1819 // construct a sorted party
1820 // TODO: this is beyond horrible, help
1821 std::vector
<Actor
*> party
;
1822 // first, from the actual party
1823 for (int idx
= 0; idx
< game
->GetPartySize(false); idx
++) {
1824 Actor
*pc
= game
->FindPC(idx
+ 1);
1827 for (unsigned int j
= 0; j
< game
->selected
.size(); j
++) {
1828 if (game
->selected
[j
] == pc
) {
1829 party
.push_back(pc
);
1834 // then, anything else we selected
1835 for (i
= 0; i
< game
->selected
.size(); i
++) {
1837 for (unsigned int j
= 0; j
< party
.size(); j
++) {
1838 if (game
->selected
[i
] == party
[j
]) {
1843 if (!found
) party
.push_back(game
->selected
[i
]);
1846 //party formation movement
1847 Point src
= party
[0]->Pos
;
1848 for(i
= 0; i
< party
.size(); i
++) {
1851 actor
->ClearActions();
1852 MoveToPointFormation(actor
, i
, src
, p
);
1855 //p is a searchmap travel region
1856 if ( party
[0]->GetCurrentArea()->GetCursor(p
) == IE_CURSOR_TRAVEL
) {
1857 sprintf( Tmp
, "NIDSpecial2()" );
1858 party
[0]->AddAction( GenerateAction( Tmp
) );
1863 //we got an actor past this point
1864 DisplayStringCore(actor
, VB_SELECT
+core
->Roll(1,3,-1), DS_CONST
|DS_CONSOLE
);
1866 PerformActionOn(actor
);
1869 void GameControl::PerformActionOn(Actor
*actor
) {
1870 Game
* game
= core
->GetGame();
1873 //determining the type of the clicked actor
1876 type
= actor
->GetStat(IE_EA
);
1877 if ( type
>= EA_EVILCUTOFF
|| type
== EA_GOODBUTRED
) {
1878 type
= ACT_ATTACK
; //hostile
1879 } else if ( type
> EA_CHARMED
) {
1880 type
= ACT_TALK
; //neutral
1882 type
= ACT_NONE
; //party
1885 if (target_mode
== TARGET_MODE_ATTACK
) {
1887 } else if (target_mode
== TARGET_MODE_TALK
) {
1889 } else if (target_mode
== TARGET_MODE_CAST
) {
1891 } else if (target_mode
== TARGET_MODE_DEFEND
) {
1893 } else if (target_mode
== TARGET_MODE_PICK
) {
1894 type
= ACT_THIEVING
;
1897 //we shouldn't zero this for two reasons in case of spell or item
1898 //1. there could be multiple targets
1899 //2. the target mode is important
1900 if (!(target_mode
== TARGET_MODE_CAST
) || !spellCount
) {
1901 target_mode
= TARGET_MODE_NONE
;
1905 case ACT_NONE
: //none
1907 SelectActor( actor
->InParty
);
1908 else if (actor
->GetStat(IE_EA
) <= EA_CHARMED
) {
1909 /*let's select charmed/summoned creatures
1910 EA_CHARMED is the maximum value known atm*/
1911 core
->GetGame()->SelectActor(actor
, true, SELECT_REPLACE
) ;
1915 //talk (first selected talks)
1916 if (game
->selected
.size()) {
1917 //if we are in PST modify this to NO!
1919 if (core
->HasFeature(GF_PROTAGONIST_TALKS
) ) {
1920 source
= game
->GetPC(0, false); //protagonist
1922 source
= core
->GetFirstSelectedPC(false);
1924 // only party members can start conversations
1926 TryToTalk(source
, actor
);
1931 //all of them attacks the red circled actor
1932 for(i
=0;i
<game
->selected
.size();i
++) {
1933 TryToAttack(game
->selected
[i
], actor
);
1936 case ACT_CAST
: //cast on target or use item on target
1937 if (game
->selected
.size()==1) {
1939 source
= core
->GetFirstSelectedPC(false);
1941 TryToCast(source
, actor
);
1946 for(i
=0;i
<game
->selected
.size();i
++) {
1947 TryToDefend(game
->selected
[i
], actor
);
1951 if (game
->selected
.size()==1) {
1953 source
= core
->GetFirstSelectedPC(false);
1955 TryToPick(source
, actor
);
1961 /** Special Key Press */
1962 void GameControl::OnSpecialKeyPress(unsigned char Key
)
1964 if (DialogueFlags
&DF_IN_DIALOG
) {
1967 //simulating the continue/end button pressed
1968 core
->GetGUIScriptEngine()->RunFunction("CloseContinueWindow");
1971 return; //don't accept keys in dialog
1973 Region Viewport
= core
->GetVideoDriver()->GetViewport();
1974 Game
*game
= core
->GetGame();
1975 Point mapsize
= game
->GetCurrentArea()->TMap
->GetMapSize();
1976 int partysize
= game
->GetPartySize(false);
1982 if (Viewport
.x
> 63)
1988 if (Viewport
.y
> 63)
1994 if (Viewport
.y
+ Viewport
.h
+ 64 < mapsize
.y
)
1997 Viewport
.y
= mapsize
.y
- Viewport
.h
;
1998 if (Viewport
.y
<0) Viewport
.y
=0;
2002 if (Viewport
.x
+ Viewport
.w
+ 64 < mapsize
.x
)
2005 Viewport
.x
= mapsize
.x
- Viewport
.w
;
2006 if (Viewport
.x
<0) Viewport
.x
=0;
2010 DebugFlags
|= DEBUG_SHOW_CONTAINERS
;
2013 // show partymember hp/maxhp as overhead text
2014 for (pm
=0; pm
< partysize
; pm
++) {
2015 Actor
*pc
= game
->GetPC(pm
, true);
2017 memset(tmpstr
, 0, 10);
2018 snprintf(tmpstr
, 10, "%d/%d", pc
->Modified
[IE_HITPOINTS
], pc
->Modified
[IE_MAXHITPOINTS
]);
2019 pc
->DisplayHeadText(strdup(tmpstr
));
2027 core
->GetGUIScriptEngine()->RunFunction("EmptyControls");
2028 core
->SetEventFlag(EF_ACTION
);
2031 core
->GetGUIScriptEngine()->RunFunction("OnIncreaseSize");
2034 core
->GetGUIScriptEngine()->RunFunction("OnDecreaseSize");
2039 if (ScreenFlags
& SF_LOCKSCROLL
) {
2044 // override any existing viewport moves which may be in progress
2045 core
->timer
->SetMoveViewPort( Viewport
.x
, Viewport
.y
, 0, false );
2046 // move it directly ourselves, since we might be paused
2047 core
->GetVideoDriver()->MoveViewportTo( Viewport
.x
, Viewport
.y
);
2051 void GameControl::CalculateSelection(Point
&p
)
2054 Game
* game
= core
->GetGame();
2055 Map
* area
= game
->GetCurrentArea( );
2056 if (DrawSelectionRect
) {
2058 SelectionRect
.w
= StartX
- p
.x
;
2059 SelectionRect
.x
= p
.x
;
2061 SelectionRect
.x
= StartX
;
2062 SelectionRect
.w
= p
.x
- StartX
;
2065 SelectionRect
.h
= StartY
- p
.y
;
2066 SelectionRect
.y
= p
.y
;
2068 SelectionRect
.y
= StartY
;
2069 SelectionRect
.h
= p
.y
- StartY
;
2072 unsigned int count
= area
->GetActorInRect( ab
, SelectionRect
,true );
2073 for (i
= 0; i
< highlighted
.size(); i
++)
2074 highlighted
[i
]->SetOver( false );
2075 highlighted
.clear();
2077 for (i
= 0; i
< count
; i
++) {
2078 ab
[i
]->SetOver( true );
2079 highlighted
.push_back( ab
[i
] );
2084 Actor
* actor
= area
->GetActor( p
, GA_DEFAULT
| GA_SELECT
| GA_NO_DEAD
| GA_NO_ENEMY
);
2085 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
2087 lastActor
->SetOver( false );
2091 lastActorID
= actor
->globalID
;
2092 actor
->SetOver( true );
2097 void GameControl::SetCutSceneMode(bool active
)
2100 ScreenFlags
|= (SF_DISABLEMOUSE
| SF_LOCKSCROLL
| SF_CUTSCENE
);
2104 ScreenFlags
&= ~(SF_DISABLEMOUSE
| SF_LOCKSCROLL
| SF_CUTSCENE
);
2108 //Change game window geometries when a new window gets deactivated
2109 void GameControl::HandleWindowHide(const char *WindowName
, const char *WindowPosition
)
2111 Variables
* dict
= core
->GetDictionary();
2114 if (dict
->Lookup( WindowName
, index
)) {
2115 if (index
!= (ieDword
) -1) {
2116 Window
* w
= core
->GetWindow( (unsigned short) index
);
2118 core
->SetVisible( (unsigned short) index
, WINDOW_INVISIBLE
);
2119 if (dict
->Lookup( WindowPosition
, index
)) {
2120 ResizeDel( w
, index
);
2124 printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED
);
2125 printf("%s:%u\n",WindowName
, index
);
2130 //Hide all other windows on the GUI (gamecontrol is not hidden by this)
2131 int GameControl::HideGUI()
2133 //hidegui is in effect
2134 if (!(ScreenFlags
&SF_GUIENABLED
) ) {
2137 //no gamecontrol visible
2138 if (Owner
->Visible
== WINDOW_INVISIBLE
) {
2141 ScreenFlags
&=~SF_GUIENABLED
;
2142 HandleWindowHide("PortraitWindow", "PortraitPosition");
2143 HandleWindowHide("OtherWindow", "OtherPosition");
2144 HandleWindowHide("TopWindow", "TopPosition");
2145 HandleWindowHide("OptionsWindow", "OptionsPosition");
2146 HandleWindowHide("MessageWindow", "MessagePosition");
2147 HandleWindowHide("ActionsWindow", "ActionsPosition");
2148 //FloatWindow doesn't affect gamecontrol, so it is special
2149 Variables
* dict
= core
->GetDictionary();
2152 if (dict
->Lookup( "FloatWindow", index
)) {
2153 if (index
!= (ieDword
) -1) {
2154 core
->SetVisible( (unsigned short) index
, WINDOW_INVISIBLE
);
2157 core
->GetVideoDriver()->SetViewport( Owner
->XPos
, Owner
->YPos
, Width
, Height
);
2161 //Change game window geometries when a new window gets activated
2162 void GameControl::HandleWindowReveal(const char *WindowName
, const char *WindowPosition
)
2164 Variables
* dict
= core
->GetDictionary();
2167 if (dict
->Lookup( WindowName
, index
)) {
2168 if (index
!= (ieDword
) -1) {
2169 Window
* w
= core
->GetWindow( (unsigned short) index
);
2171 core
->SetVisible( (unsigned short) index
, WINDOW_VISIBLE
);
2172 if (dict
->Lookup( WindowPosition
, index
)) {
2173 ResizeAdd( w
, index
);
2177 printMessage("GameControl", "Invalid Window Index ", LIGHT_RED
);
2178 printf("%s:%u\n",WindowName
, index
);
2183 //Reveal all windows on the GUI (including this one)
2184 int GameControl::UnhideGUI()
2186 if (ScreenFlags
&SF_GUIENABLED
) {
2190 ScreenFlags
|= SF_GUIENABLED
;
2191 // Unhide the gamecontrol window
2192 core
->SetVisible( 0, WINDOW_VISIBLE
);
2194 HandleWindowReveal("ActionsWindow", "ActionsPosition");
2195 HandleWindowReveal("MessageWindow", "MessagePosition");
2196 HandleWindowReveal("OptionsWindow", "OptionsPosition");
2197 HandleWindowReveal("TopWindow", "TopPosition");
2198 HandleWindowReveal("OtherWindow", "OtherPosition");
2199 HandleWindowReveal("PortraitWindow", "PortraitPosition");
2200 //the floatwindow is a special case
2201 Variables
* dict
= core
->GetDictionary();
2204 if (dict
->Lookup( "FloatWindow", index
)) {
2205 if (index
!= (ieDword
) -1) {
2206 Window
* fw
= core
->GetWindow( (unsigned short) index
);
2208 core
->SetVisible( (unsigned short) index
, WINDOW_VISIBLE
);
2209 fw
->Flags
|=WF_FLOAT
;
2210 core
->SetOnTop( index
);
2214 core
->GetVideoDriver()->SetViewport( Owner
->XPos
, Owner
->YPos
, Width
, Height
);
2218 //a window got removed, so the GameControl gets enlarged
2219 void GameControl::ResizeDel(Window
* win
, int type
)
2224 printMessage("GameControl","More than one left window!\n",LIGHT_RED
);
2228 Owner
->XPos
-= win
->Width
;
2229 Owner
->Width
+= win
->Width
;
2230 Width
= Owner
->Width
;
2235 if (BottomCount
!=1) {
2236 printMessage("GameControl","More than one bottom window!\n",LIGHT_RED
);
2240 Owner
->Height
+= win
->Height
;
2241 Height
= Owner
->Height
;
2246 if (RightCount
!=1) {
2247 printMessage("GameControl","More than one right window!\n",LIGHT_RED
);
2251 Owner
->Width
+= win
->Width
;
2252 Width
= Owner
->Width
;
2258 printMessage("GameControl","More than one top window!\n",LIGHT_RED
);
2262 Owner
->YPos
-= win
->Height
;
2263 Owner
->Height
+= win
->Height
;
2264 Height
= Owner
->Height
;
2268 case 4: //BottomAdded
2270 Owner
->Height
+= win
->Height
;
2271 Height
= Owner
->Height
;
2273 case 5: //Inactivating
2275 Owner
->Height
+= win
->Height
;
2276 Height
= Owner
->Height
;
2281 //a window got added, so the GameControl gets shrunk
2282 //Owner is the GameControl's window
2283 //GameControl is the only control on that window
2284 void GameControl::ResizeAdd(Window
* win
, int type
)
2289 if (LeftCount
== 1) {
2290 Owner
->XPos
+= win
->Width
;
2291 Owner
->Width
-= win
->Width
;
2292 Width
= Owner
->Width
;
2298 if (BottomCount
== 1) {
2299 Owner
->Height
-= win
->Height
;
2300 Height
= Owner
->Height
;
2306 if (RightCount
== 1) {
2307 Owner
->Width
-= win
->Width
;
2308 Width
= Owner
->Width
;
2314 if (TopCount
== 1) {
2315 Owner
->YPos
+= win
->Height
;
2316 Owner
->Height
-= win
->Height
;
2317 Height
= Owner
->Height
;
2321 case 4: //BottomAdded
2323 Owner
->Height
-= win
->Height
;
2324 Height
= Owner
->Height
;
2327 case 5: //Inactivating
2329 Owner
->Height
-= win
->Height
;
2334 //Try to start dialogue between two actors (one of them could be inanimate)
2335 int GameControl::InitDialog(Scriptable
* spk
, Scriptable
* tgt
, const char* dlgref
)
2342 DialogMgr
* dm
= ( DialogMgr
* ) core
->GetInterface( IE_DLG_CLASS_ID
);
2343 dm
->Open( gamedata
->GetResource( dlgref
, IE_DLG_CLASS_ID
), true );
2344 dlg
= dm
->GetDialog();
2348 printMessage("GameControl", " ", LIGHT_RED
);
2349 printf( "Cannot start dialog: %s\n", dlgref
);
2353 strnlwrcpy(dlg
->ResRef
, dlgref
, 8); //this isn't handled by GetDialog???
2355 //target is here because it could be changed when a dialog runs onto
2356 //and external link, we need to find the new target (whose dialog was
2359 Actor
*spe
= (Actor
*) spk
;
2360 speakerID
= spe
->globalID
;
2361 if (tgt
->Type
!=ST_ACTOR
) {
2363 //most likely this dangling object reference
2364 //won't cause problems, because trigger points don't
2365 //get destroyed during a dialog
2367 spk
->LastTalkedTo
=0;
2369 Actor
*tar
= (Actor
*) tgt
;
2370 speakerID
= spe
->globalID
;
2371 targetID
= tar
->globalID
;
2372 if (!originalTargetID
) originalTargetID
= tar
->globalID
;
2373 spe
->LastTalkedTo
=targetID
;
2374 tar
->LastTalkedTo
=speakerID
;
2377 //check if we are already in dialog
2378 if (DialogueFlags
&DF_IN_DIALOG
) {
2382 int si
= dlg
->FindFirstState( tgt
);
2387 //we need GUI for dialogs
2390 //no exploring while in dialogue
2391 ScreenFlags
|= SF_GUIENABLED
|SF_DISABLEMOUSE
|SF_LOCKSCROLL
;
2392 DialogueFlags
|= DF_IN_DIALOG
;
2394 if (tgt
->Type
==ST_ACTOR
) {
2395 Actor
*tar
= (Actor
*) tgt
;
2396 tar
->DialogInterrupt();
2399 //allow mouse selection from dialog (even though screen is locked)
2400 core
->GetVideoDriver()->SetMouseEnabled(true);
2401 core
->timer
->SetMoveViewPort( tgt
->Pos
.x
, tgt
->Pos
.y
, 0, true );
2402 //there are 3 bits, if they are all unset, the dialog freezes scripts
2403 if (!(dlg
->Flags
&7) ) {
2404 DialogueFlags
|= DF_FREEZE_SCRIPTS
;
2406 //opening control size to maximum, enabling dialog window
2407 core
->GetGame()->SetControlStatus(CS_HIDEGUI
, BM_NAND
);
2408 core
->GetGame()->SetControlStatus(CS_DIALOG
, BM_OR
);
2409 core
->SetEventFlag(EF_PORTRAIT
);
2413 /*try to break will only try to break it, false means unconditional stop*/
2414 void GameControl::EndDialog(bool try_to_break
)
2416 if (try_to_break
&& (DialogueFlags
&DF_UNBREAKABLE
) ) {
2420 Actor
*tmp
= GetSpeaker();
2425 if (targetID
==0xffff) {
2426 targetOB
->LeaveDialog();
2435 originalTargetID
= 0;
2441 //restoring original size
2442 core
->GetGame()->SetControlStatus(CS_DIALOG
, BM_NAND
);
2443 ScreenFlags
&=~(SF_DISABLEMOUSE
|SF_LOCKSCROLL
);
2445 core
->SetEventFlag(EF_PORTRAIT
);
2448 //translate section values (journal, solved, unsolved, user)
2449 static const int sectionMap
[4]={4,1,2,0};
2451 void GameControl::DialogChoose(unsigned int choose
)
2455 TextArea
* ta
= core
->GetMessageTextArea();
2457 printMessage("GameControl","Dialog aborted???",LIGHT_RED
);
2462 Actor
*speaker
= GetSpeaker();
2464 printMessage("GameControl","Speaker gone???",LIGHT_RED
);
2471 if (targetID
!=0xffff) {
2480 printMessage("GameControl","Target gone???",LIGHT_RED
);
2485 if (choose
== (unsigned int) -1) {
2486 //increasing talkcount after top level condition was determined
2488 int si
= dlg
->FindFirstState( tgt
);
2495 if (DialogueFlags
&DF_TALKCOUNT
) {
2496 DialogueFlags
&=~DF_TALKCOUNT
;
2498 } else if (DialogueFlags
&DF_INTERACT
) {
2499 DialogueFlags
&=~DF_INTERACT
;
2500 tgt
->InteractCount
++;
2503 ds
= dlg
->GetState( si
);
2505 if (ds
->transitionsCount
<= choose
) {
2509 DialogTransition
* tr
= ds
->transitions
[choose
];
2513 if (tr
->Flags
&IE_DLG_TR_JOURNAL
) {
2515 if (tr
->Flags
&IE_DLG_UNSOLVED
) {
2518 if (tr
->Flags
&IE_DLG_SOLVED
) {
2521 if (core
->GetGame()->AddJournalEntry(tr
->journalStrRef
, sectionMap
[Section
], tr
->Flags
>>16) ) {
2522 core
->DisplayConstantString(STR_JOURNALCHANGE
,0xffff00);
2523 char *string
= core
->GetString( tr
->journalStrRef
);
2524 //cutting off the strings at the first crlf
2525 char *poi
= strchr(string
,'\n');
2529 core
->DisplayString( string
);
2534 if (tr
->textStrRef
!= 0xffffffff) {
2535 //allow_zero is for PST (deionarra's text)
2536 core
->DisplayStringName( (int) (tr
->textStrRef
), 0x8080FF, speaker
, IE_STR_SOUND
|IE_STR_SPEECH
|IE_STR_ALLOW_ZERO
);
2537 if (core
->HasFeature( GF_DIALOGUE_SCROLLS
)) {
2538 ta
->AppendText( "", -1 );
2543 // does this belong here? we must clear actions somewhere before
2544 // we start executing them (otherwise queued actions interfere)
2545 // executing actions directly does not work, because dialog
2546 // needs to end before final actions are executed due to
2547 // actions making new dialogs!
2548 if (target
->Type
== ST_ACTOR
) ((Movable
*)target
)->ClearPath(); // fuzzie added this
2549 target
->ClearActions();
2551 for (unsigned int i
= 0; i
< tr
->action
->count
; i
++) {
2552 Action
* action
= GenerateAction( tr
->action
->strings
[i
]);
2554 target
->AddAction(action
);
2555 //GameScript::ExecuteAction( target, action );
2557 snprintf(Tmp
, sizeof(Tmp
),
2558 "Can't compile action: %s\n",
2559 tr
->action
->strings
[i
] );
2560 printMessage( "Dialog", Tmp
,YELLOW
);
2565 int final_dialog
= tr
->Flags
& IE_DLG_TR_FINAL
;
2568 ta
->SetMinRow( false );
2572 // all dialog actions must be executed immediately
2573 target
->ProcessActions(true);
2574 // (do not clear actions - final actions can involve waiting/moving)
2580 //displaying dialog for selected option
2581 int si
= tr
->stateIndex
;
2582 //follow external linkage, if required
2583 if (tr
->Dialog
[0] && strnicmp( tr
->Dialog
, dlg
->ResRef
, 8 )) {
2584 //target should be recalculated!
2586 if (originalTargetID
) {
2587 // always try original target first (sometimes there are multiple
2588 // actors with the same dialog in an area, we want to pick the one
2589 // we were talking to)
2590 tgt
= GetActorByGlobalID(originalTargetID
);
2591 if (tgt
&& strnicmp( tgt
->GetDialog(GD_NORMAL
), tr
->Dialog
, 8 ) != 0) {
2596 // then just search the current area for an actor with the dialog
2597 tgt
= target
->GetCurrentArea()->GetActorByDialog(tr
->Dialog
);
2601 printMessage("Dialog","Can't redirect dialog\n",YELLOW
);
2602 ta
->SetMinRow( false );
2606 targetID
= tgt
->globalID
;
2607 // we have to make a backup, tr->Dialog is freed
2609 strnlwrcpy(tmpresref
,tr
->Dialog
, 8);
2610 if (target
->GetInternalFlag()&IF_NOINT
) {
2611 // this whole check moved out of InitDialog by fuzzie, see comments
2612 // for the IF_NOINT check in BeginDialog
2613 core
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
2614 ta
->SetMinRow( false );
2618 int ret
= InitDialog( speaker
, target
, tmpresref
);
2620 // error was displayed by InitDialog
2621 ta
->SetMinRow( false );
2626 ds
= dlg
->GetState( si
);
2628 printMessage("Dialog","Can't find next dialog\n",YELLOW
);
2629 ta
->SetMinRow( false );
2634 //displaying npc text
2635 core
->DisplayStringName( ds
->StrRef
, 0x70FF70, target
, IE_STR_SOUND
|IE_STR_SPEECH
);
2636 //adding a gap between options and npc text
2637 ta
->AppendText("",-1);
2640 ta
->SetMinRow( true );
2641 //first looking for a 'continue' opportunity, the order is descending (a la IE)
2642 unsigned int x
= ds
->transitionsCount
;
2644 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_FINAL
) {
2647 if (ds
->transitions
[x
]->textStrRef
!= 0xffffffff) {
2650 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_TRIGGER
) {
2651 if (!dlg
->EvaluateDialogTrigger(target
, ds
->transitions
[x
]->trigger
)) {
2655 core
->GetDictionary()->SetAt("DialogOption",x
);
2656 DialogueFlags
|= DF_OPENCONTINUEWINDOW
;
2659 for (x
= 0; x
< ds
->transitionsCount
; x
++) {
2660 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_TRIGGER
) {
2661 if (!dlg
->EvaluateDialogTrigger(target
, ds
->transitions
[x
]->trigger
)) {
2666 if (ds
->transitions
[x
]->textStrRef
== 0xffffffff) {
2667 //dialogchoose should be set to x
2668 //it isn't important which END option was chosen, as it ends
2669 core
->GetDictionary()->SetAt("DialogOption",x
);
2670 DialogueFlags
|= DF_OPENENDWINDOW
;
2672 char *string
= ( char * ) malloc( 40 );
2673 sprintf( string
, "[s=%d,ffffff,ff0000]%d - [p]", x
, idx
);
2674 i
= ta
->AppendText( string
, -1 );
2676 string
= core
->GetString( ds
->transitions
[x
]->textStrRef
);
2677 ta
->AppendText( string
, i
);
2679 ta
->AppendText( "[/p][/s]", i
);
2682 // this happens if a trigger isn't implemented or the dialog is wrong
2684 printMessage("Dialog", "There were no valid dialog options!\n", YELLOW
);
2685 DialogueFlags
|= DF_OPENENDWINDOW
;
2688 //padding the rows so our text will be at the top
2689 if (core
->HasFeature( GF_DIALOGUE_SCROLLS
)) {
2690 ta
->AppendText( "", -1 );
2697 //Create an overhead text over an arbitrary point
2698 void GameControl::DisplayString(Point
&p
, const char *Text
)
2700 Scriptable
* scr
= new Scriptable( ST_TRIGGER
);
2701 scr
->overHeadText
= (char *) Text
;
2702 scr
->textDisplaying
= 1;
2703 scr
->timeStartDisplaying
= 0;
2705 scr
->ClearCutsceneID( );
2708 //Create an overhead text over a scriptable target
2709 //Multiple texts are possible, as this code copies the text to a new object
2710 void GameControl::DisplayString(Scriptable
* target
)
2712 Scriptable
* scr
= new Scriptable( ST_TRIGGER
);
2713 scr
->overHeadText
= strdup( target
->overHeadText
);
2714 /* strdup should work here, we use it elsewhere
2715 size_t len = strlen( target->overHeadText ) + 1;
2716 scr->overHeadText = ( char * ) malloc( len );
2717 strcpy( scr->overHeadText, target->overHeadText );
2719 scr
->textDisplaying
= 1;
2720 scr
->timeStartDisplaying
= target
->timeStartDisplaying
;
2721 scr
->Pos
= target
->Pos
;
2722 scr
->SetCutsceneID( target
);
2725 /** changes displayed map to the currently selected PC */
2726 void GameControl::ChangeMap(Actor
*pc
, bool forced
)
2728 //swap in the area of the actor
2729 Game
* game
= core
->GetGame();
2730 if (forced
|| (stricmp( pc
->Area
, game
->CurrentArea
) != 0) ) {
2732 overInfoPoint
= NULL
;
2733 overContainer
= NULL
;
2735 /*this is loadmap, because we need the index, not the pointer*/
2736 char *areaname
= game
->CurrentArea
;
2738 areaname
= pc
->Area
;
2740 game
->GetMap( areaname
, true );
2741 ScreenFlags
|=SF_CENTERONACTOR
;
2743 //center on first selected actor
2744 Region vp
= core
->GetVideoDriver()->GetViewport();
2745 if (ScreenFlags
&SF_CENTERONACTOR
) {
2746 core
->timer
->SetMoveViewPort( pc
->Pos
.x
, pc
->Pos
.y
, 0, true );
2747 ScreenFlags
&=~SF_CENTERONACTOR
;
2751 void GameControl::SetScreenFlags(int value
, int mode
)
2754 case BM_OR
: ScreenFlags
|=value
; break;
2755 case BM_NAND
: ScreenFlags
&=~value
; break;
2756 case BM_SET
: ScreenFlags
=value
; break;
2757 case BM_AND
: ScreenFlags
&=value
; break;
2758 case BM_XOR
: ScreenFlags
^=value
; break;
2762 void GameControl::SetDialogueFlags(int value
, int mode
)
2765 case BM_OR
: DialogueFlags
|=value
; break;
2766 case BM_NAND
: DialogueFlags
&=~value
; break;
2767 case BM_SET
: DialogueFlags
=value
; break;
2768 case BM_AND
: DialogueFlags
&=value
; break;
2769 case BM_XOR
: DialogueFlags
^=value
; break;
2773 //copies a screenshot into a sprite
2774 Sprite2D
* GameControl::GetScreenshot(bool show_gui
)
2776 Sprite2D
* screenshot
;
2778 screenshot
= core
->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) );
2780 int hf
= HideGUI ();
2782 screenshot
= core
->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) );
2786 core
->DrawWindows ();
2792 //copies a downscaled screenshot into a sprite for save game preview
2793 Sprite2D
* GameControl::GetPreview()
2795 // We get preview by first taking a screenshot of size 640x405,
2796 // centered in the display. This is to get a decent picture for
2797 // higher screen resolutions.
2798 // FIXME: how do orig games solve that?
2799 Video
* video
= core
->GetVideoDriver();
2800 int w
= video
->GetWidth();
2801 int h
= video
->GetHeight();
2802 int x
= (w
- 640) / 2;
2803 int y
= (h
- 405) / 2;
2820 int hf
= HideGUI ();
2821 signed char v
= Owner
->Visible
;
2822 Owner
->Visible
= WINDOW_VISIBLE
;
2825 Sprite2D
*screenshot
= video
->GetScreenshot( Region(x
, y
, w
, h
) );
2829 core
->DrawWindows();
2831 Sprite2D
* preview
= video
->SpriteScaleDown ( screenshot
, 5 );
2832 video
->FreeSprite( screenshot
);
2838 * Returns PC portrait for a currently running game
2840 Sprite2D
* GameControl::GetPortraitPreview(int pcslot
)
2842 /** Portrait shrink ratio */
2843 // FIXME: this is just a random PST specific trait
2844 // you can make it less random with a new feature bit
2845 int ratio
= (core
->HasFeature( GF_ONE_BYTE_ANIMID
)) ? 1 : 2;
2847 Video
*video
= core
->GetVideoDriver();
2849 Actor
*actor
= core
->GetGame()->GetPC( pcslot
, false );
2853 ImageMgr
* im
= ( ImageMgr
* )
2854 gamedata
->GetResource( actor
->GetPortrait(true), &ImageMgr::ID
);
2859 Sprite2D
* img
= im
->GetSprite2D();
2865 Sprite2D
* img_scaled
= video
->SpriteScaleDown( img
, ratio
);
2866 video
->FreeSprite( img
);
2871 Actor
*GameControl::GetActorByGlobalID(ieWord ID
)
2875 Game
* game
= core
->GetGame();
2879 Map
* area
= game
->GetCurrentArea( );
2883 area
->GetActorByGlobalID(ID
);
2886 Actor
*GameControl::GetLastActor()
2888 return GetActorByGlobalID(lastActorID
);
2891 Actor
*GameControl::GetTarget()
2893 return GetActorByGlobalID(targetID
);
2896 Actor
*GameControl::GetSpeaker()
2898 return GetActorByGlobalID(speakerID
);
2901 //Set up an item use which needs targeting
2902 //Slot is an inventory slot
2903 //header is the used item extended header
2905 //target type is a bunch of GetActor flags that alter valid targets
2906 //cnt is the number of different targets (usually 1)
2907 void GameControl::SetupItemUse(int slot
, int header
, Actor
*u
, int targettype
, int cnt
)
2912 spellIndex
= header
;
2913 //item use also uses the casting icon, this might be changed in some custom game?
2914 target_mode
= TARGET_MODE_CAST
;
2915 target_types
= targettype
;
2919 //Set up spell casting which needs targeting
2920 //type is the spell's type
2921 //level is the caster level
2922 //idx is the spell's number
2924 //target type is a bunch of GetActor flags that alter valid targets
2925 //cnt is the number of different targets (usually 1)
2926 void GameControl::SetupCasting(int type
, int level
, int idx
, Actor
*u
, int targettype
, int cnt
)
2932 target_mode
= TARGET_MODE_CAST
;
2933 target_types
= targettype
;
2937 //another method inherited from Control which has no use here
2938 bool GameControl::SetEvent(int /*eventType*/, const char * /*handler*/)
2943 void GameControl::SetDisplayText(char *text
, unsigned int time
)
2946 core
->FreeString(DisplayText
);
2948 DisplayTextTime
= time
;
2952 void GameControl::SetDisplayText(ieStrRef text
, unsigned int time
)
2954 SetDisplayText(core
->GetString(core
->GetStringReference(text
), 0), time
);