1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "GUI/GameControl.h"
25 #include "DialogMgr.h"
26 #include "DisplayMessage.h"
32 #include "Interface.h"
34 #include "SaveGameIterator.h"
35 #include "ScriptEngine.h"
42 #define DEBUG_SHOW_INFOPOINTS 0x01
43 #define DEBUG_SHOW_CONTAINERS 0x02
44 #define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
45 #define DEBUG_SHOW_LIGHTMAP 0x08
47 static const Color cyan
= {
48 0x00, 0xff, 0xff, 0xff
50 static const Color red
= {
51 0xff, 0x00, 0x00, 0xff
53 static const Color magenta
= {
54 0xff, 0x00, 0xff, 0xff
56 static const Color green
= {
57 0x00, 0xff, 0x00, 0xff
60 static Color white = {
61 0xff, 0xff, 0xff, 0xff
64 static const Color black
= {
65 0x00, 0x00, 0x00, 0xff
67 static const Color blue
= {
68 0x00, 0x00, 0xff, 0x80
73 #define FORMATIONSIZE 10
74 typedef Point formation_type
[FORMATIONSIZE
];
75 ieDword formationcount
;
76 static formation_type
*formations
=NULL
;
77 static bool mqs
= false;
78 static ieResRef TestSpell
="SPWI207";
80 //If one of the actors has tracking on, the gamecontrol needs to display
81 //arrow markers on the edges to point at detected monsters
82 //tracterID is the tracker actor's global ID
83 //distance is the detection distance
84 void GameControl::SetTracker(Actor
*actor
, ieDword dist
)
86 trackerID
= actor
->GetID();
90 //Multiple Quick saves is an experimental GemRB feature.
91 //multiple quick saves are kept, their age is determined by the slot
92 //number. There is an algorithm which keeps about log2(n) slots alive.
93 //The algorithm is implemented in SaveGameIterator
94 void GameControl::MultipleQuickSaves(int arg
)
99 GameControl::GameControl(void)
104 //this is the default action, individual actors should have one too
105 //at this moment we use only this
106 //maybe we don't even need it
114 DrawSelectionRect
= false;
116 overContainer
= NULL
;
117 overInfoPoint
= NULL
;
120 lastCursor
= IE_CURSOR_NORMAL
;
128 target_mode
= TARGET_MODE_NONE
;
129 target_types
= GA_SELECT
|GA_NO_DEAD
|GA_NO_HIDDEN
;
131 core
->GetDictionary()->Lookup("Center",tmp
);
133 ScreenFlags
=SF_ALWAYSCENTER
|SF_CENTERONACTOR
;
136 ScreenFlags
= SF_CENTERONACTOR
;
145 originalTargetID
= 0;
152 //There could be a custom formation which is saved in the save game
153 //alternatively, all formations could be saved in some compatible way
154 //so it doesn't cause problems with the original engine
155 void GameControl::ReadFormations()
158 AutoTable
tab("formatio");
162 formations
= (formation_type
*) calloc(1,sizeof(formation_type
) );
165 formationcount
= tab
->GetRowCount();
166 formations
= (formation_type
*) calloc(formationcount
, sizeof(formation_type
));
167 for(i
=0; i
<formationcount
; i
++) {
168 for(j
=0;j
<FORMATIONSIZE
;j
++) {
169 short k
=(short) atoi(tab
->QueryField(i
,j
*2));
170 formations
[i
][j
].x
=k
;
171 k
=(short) atoi(tab
->QueryField(i
,j
*2+1));
172 formations
[i
][j
].y
=k
;
177 //returns a single point offset for a formation
178 //formation: the formation type
179 //pos: the actor's slot ID
180 Point
GameControl::GetFormationOffset(ieDword formation
, ieDword pos
)
182 if (formation
>=formationcount
) formation
= 0;
183 if (pos
>=FORMATIONSIZE
) pos
=FORMATIONSIZE
-1;
184 return formations
[formation
][pos
];
187 //Moves an actor to a new position, keeping the current formation
188 //WARNING: don't pass p as a reference because it gets modified
189 void GameControl::MoveToPointFormation(Actor
*actor
, unsigned int pos
, Point src
, Point p
)
191 Map
* map
= actor
->GetCurrentArea() ;
193 int formation
=core
->GetGame()->GetFormation();
194 if (pos
>=FORMATIONSIZE
) pos
=FORMATIONSIZE
-1;
198 double xdiff
= src
.x
- p
.x
;
199 double ydiff
= src
.y
- p
.y
;
207 angle
= atan(xdiff
/ydiff
);
208 if (ydiff
< 0) angle
+= M_PI
;
211 // calculate new coordinates by rotating formation around (0,0)
212 double newx
= -formations
[formation
][pos
].x
* cos(angle
) + formations
[formation
][pos
].y
* sin(angle
);
213 double newy
= formations
[formation
][pos
].x
* sin(angle
) + formations
[formation
][pos
].y
* cos(angle
);
217 if (p
.x
< 0) p
.x
= 8;
218 if (p
.y
< 0) p
.y
= 8;
219 if (p
.x
> map
->GetWidth()*16) p
.x
= map
->GetWidth()*16 - 8;
220 if (p
.y
> map
->GetHeight()*12) p
.y
= map
->GetHeight()*12 - 8;
222 if(map
->GetCursor(p
) == IE_CURSOR_BLOCKED
) {
223 //we can't get there --> adjust position
226 map
->AdjustPosition(p
);
230 CreateMovement(actor
, p
);
233 // generate an action to do the actual movement
234 // only PST supports RunToPoint
235 void GameControl::CreateMovement(Actor
*actor
, const Point
&p
)
239 Action
*action
= NULL
;
241 sprintf( Tmp
, "RunToPoint([%d.%d])", p
.x
, p
.y
);
242 action
= GenerateAction( Tmp
);
246 sprintf( Tmp
, "MoveToPoint([%d.%d])", p
.x
, p
.y
);
247 action
= GenerateAction( Tmp
);
250 actor
->AddAction( action
);
251 // force action so that we get target recticles immediately
252 actor
->ProcessActions(true);
255 GameControl::~GameControl(void)
257 //releasing the viewport of GameControl
258 core
->GetVideoDriver()->SetViewport( 0,0,0,0 );
267 core
->FreeString(DisplayText
);
271 //Autosave was triggered by the GUI
272 void GameControl::AutoSave()
274 core
->GetSaveGameIterator()->CreateSaveGame(0, false);
277 //QuickSave was triggered by the GUI
278 //mqs is the 'multiple quick saves' flag
279 void GameControl::QuickSave()
281 core
->GetSaveGameIterator()->CreateSaveGame(1, mqs
== 1);
284 // ArrowSprite cycles
298 static const int arrow_orientations
[16]={
299 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
300 -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1
303 //Draws arrow markers along the edge of the game window
304 //WARNING:don't use reference for point, because it is altered
305 void GameControl::DrawArrowMarker(const Region
&screen
, Point p
, const Region
&viewport
)
307 Video
* video
= core
->GetVideoDriver();
312 if (p
.x
<viewport
.x
) {
316 if (p
.y
<viewport
.y
) {
322 Sprite2D
*spr
= core
->GetScrollCursorSprite(0,0);
325 //tmp = core->ArrowSprites[0]->Width;
327 if (p
.x
>viewport
.x
+viewport
.w
-tmp
) {
328 p
.x
=viewport
.x
+viewport
.w
;//-tmp;
333 //tmp = core->ArrowSprites[0]->Height;
335 if (p
.y
>viewport
.y
+viewport
.h
-tmp
) {
336 p
.y
=viewport
.y
+viewport
.h
;//-tmp;
339 if (arrow_orientations
[draw
]>=0) {
340 video
->BlitGameSprite( core
->GetScrollCursorSprite(arrow_orientations
[draw
], 0), p
.x
+screen
.x
, p
.y
+screen
.y
, 0, black
, NULL
);
344 /** Draws the Control on the Output Display */
345 void GameControl::Draw(unsigned short x
, unsigned short y
)
347 bool update_scripts
= !(DialogueFlags
& DF_FREEZE_SCRIPTS
);
349 Game
* game
= core
->GetGame();
353 if (((short) Width
) <=0 || ((short) Height
) <= 0) {
357 if (Owner
->Visible
!=WINDOW_VISIBLE
) {
361 Video
* video
= core
->GetVideoDriver();
362 Region viewport
= video
->GetViewport();
363 if (moveX
|| moveY
) {
366 Point mapsize
= core
->GetGame()->GetCurrentArea()->TMap
->GetMapSize();
367 if ( viewport
.x
< 0 )//if we are at top of the map
369 else if ( (viewport
.x
+ viewport
.w
) >= mapsize
.x
) //if we are at the bottom
370 viewport
.x
= mapsize
.x
- viewport
.w
;
372 if ( viewport
.y
< 0 ) //if we are at the left of the map
374 else if ( (viewport
.y
+ viewport
.h
) >= mapsize
.y
) //if we are at the right
375 viewport
.y
= mapsize
.y
- viewport
.h
;
377 // override any existing viewport moves which may be in progress
378 core
->timer
->SetMoveViewPort( viewport
.x
, viewport
.y
, 0, false );
379 // move it directly ourselves, since we might be paused
380 video
->MoveViewportTo( viewport
.x
, viewport
.y
);
382 Region
screen( x
+ XPos
, y
+ YPos
, Width
, Height
);
383 Map
* area
= game
->GetCurrentArea( );
385 video
->DrawRect( screen
, blue
, true );
388 video
->DrawRect( screen
, black
, true );
393 for (idx
= 0; (i
= area
->TMap
->GetInfoPoint( idx
)); idx
++) {
394 i
->Highlight
= false;
395 if (overInfoPoint
== i
&& target_mode
) {
396 if (i
->VisibleTrap(0)) {
397 i
->outlineColor
= green
;
402 if (i
->VisibleTrap(DebugFlags
& DEBUG_SHOW_INFOPOINTS
)) {
403 i
->outlineColor
= red
; // traps
404 } else if (DebugFlags
& DEBUG_SHOW_INFOPOINTS
) {
405 i
->outlineColor
= blue
; // debug infopoints
413 for (idx
= 0; (d
= area
->TMap
->GetDoor( idx
)); idx
++) {
414 d
->Highlight
= false;
417 if (d
->VisibleTrap(0) || (d
->Flags
& DOOR_LOCKED
)) {
418 // only highlight targettable doors
419 d
->outlineColor
= green
;
423 } else if (!(d
->Flags
& DOOR_SECRET
)) {
424 // mouse over, not in target mode, no secret door
425 d
->outlineColor
= cyan
;
430 if (d
->VisibleTrap(0)) {
431 d
->outlineColor
= red
; // traps
432 } else if (d
->Flags
& DOOR_SECRET
) {
433 if (DebugFlags
& DEBUG_SHOW_DOORS
|| d
->Flags
& DOOR_FOUND
) {
434 d
->outlineColor
= magenta
; // found hidden door
436 // secret door is invisible
439 } else if (DebugFlags
& DEBUG_SHOW_DOORS
) {
440 d
->outlineColor
= cyan
; // debug doors
448 for (idx
= 0; (c
= area
->TMap
->GetContainer( idx
)); idx
++) {
449 c
->Highlight
= false;
450 if (overContainer
== c
&& target_mode
) {
451 if (c
->VisibleTrap(0) || (c
->Flags
& CONT_LOCKED
)) {
452 // only highlight targettable containers
453 c
->outlineColor
= green
;
457 } else if (overContainer
== c
) {
458 // mouse over, not in target mode
459 c
->outlineColor
= cyan
;
463 if (c
->VisibleTrap(0)) {
464 c
->outlineColor
= red
; // traps
465 } else if (DebugFlags
& DEBUG_SHOW_CONTAINERS
) {
466 c
->outlineColor
= cyan
; // debug containers
473 //drawmap should be here so it updates fog of war
474 area
->DrawMap( screen
);
475 game
->DrawWeather(screen
, update_scripts
);
478 Actor
*actor
= area
->GetActorByGlobalID(trackerID
);
481 Actor
**monsters
= area
->GetAllActorsInRadius(actor
->Pos
, GA_NO_DEAD
, distance
);
485 Actor
*target
= monsters
[i
++];
486 if (target
->InParty
) continue;
487 if (target
->GetStat(IE_NOTRACKING
)) continue;
488 DrawArrowMarker(screen
, target
->Pos
, viewport
);
496 if (ScreenFlags
& SF_DISABLEMOUSE
)
498 Point
p(lastMouseX
, lastMouseY
);
499 video
->ConvertToGame( p
.x
, p
.y
);
501 // Draw selection rect
502 if (DrawSelectionRect
) {
503 CalculateSelection( p
);
504 video
->DrawRect( SelectionRect
, green
, false, true );
508 if (DebugFlags
& DEBUG_SHOW_INFOPOINTS
) {
510 unsigned int count
= area
->GetWallCount();
511 for (unsigned int i
= 0; i
< count
; ++i
) {
512 Wall_Polygon
* poly
= area
->GetWallGroup(i
);
520 //if polygon is disabled, make it grey
521 if (poly
->wall_flag
&WF_DISABLED
) {
525 video
->DrawPolyline( poly
, c
, true );
531 PathNode
* node
= drawPath
;
533 Point
p( ( node
-> x
*16) + 8, ( node
->y
*12 ) + 6 );
535 video
->DrawCircle( p
.x
, p
.y
, 2, red
);
537 short oldX
= ( node
->Parent
-> x
*16) + 8, oldY
= ( node
->Parent
->y
*12 ) + 6;
538 video
->DrawLine( oldX
, oldY
, p
.x
, p
.y
, green
);
541 video
->DrawCircle( p
.x
, p
.y
, 2, green
);
549 if (DebugFlags
& DEBUG_SHOW_LIGHTMAP
) {
550 Sprite2D
* spr
= area
->LightMap
->GetSprite2D();
551 video
->BlitSprite( spr
, 0, 0, true );
552 video
->FreeSprite( spr
);
553 Region
point( p
.x
/ 16, p
.y
/ 12, 2, 2 );
554 video
->DrawRect( point
, red
);
557 if (core
->HasFeature(GF_ONSCREEN_TEXT
) && DisplayText
) {
558 core
->GetFont(1)->Print(screen
, (unsigned char *)DisplayText
, core
->InfoTextPalette
, IE_FONT_ALIGN_CENTER
| IE_FONT_ALIGN_MIDDLE
, true);
559 if (update_scripts
) {
560 // just replicating original engine behaviour
561 if (DisplayTextTime
== 0) {
562 SetDisplayText((char *)NULL
, 0);
570 /** inherited from Control, GameControl doesn't need it */
571 int GameControl::SetText(const char* /*string*/, int /*pos*/)
576 /** Key Press Event */
577 void GameControl::OnKeyPress(unsigned char Key
, unsigned short /*Mod*/)
579 if (DialogueFlags
&DF_IN_DIALOG
) {
583 Game
* game
= core
->GetGame();
588 game
->SelectActor( NULL
, false, SELECT_NORMAL
);
589 i
= game
->GetPartySize(false)/2;
591 SelectActor(i
, true);
595 game
->SelectActor( NULL
, true, SELECT_NORMAL
);
596 i
= game
->GetPartySize(false)/2;
598 SelectActor(i
, false);
613 SelectActor(Key
-'0');
616 core
->GetGame()->SetHotKey(toupper(Key
));
621 //Select (or deselect) a new actor (or actors)
622 void GameControl::SelectActor(int whom
, int type
)
624 Game
* game
= core
->GetGame();
626 game
->SelectActor( NULL
, true, SELECT_NORMAL
);
630 /* doesn't fall through here */
631 Actor
* actor
= game
->FindPC( whom
);
636 game
->SelectActor( actor
, false, SELECT_NORMAL
);
640 game
->SelectActor( actor
, true, SELECT_NORMAL
);
644 bool was_selected
= actor
->IsSelected();
645 if (game
->SelectActor( actor
, true, SELECT_REPLACE
))
646 if (was_selected
|| (ScreenFlags
& SF_ALWAYSCENTER
)) {
647 ScreenFlags
|= SF_CENTERONACTOR
;
651 //Effect for the ctrl-r cheatkey (resurrect)
652 static EffectRef heal_ref
={"CurrentHPModifier", NULL
, -1};
653 static EffectRef damage_ref
={"Damage", NULL
, -1};
655 /** Key Release Event */
656 void GameControl::OnKeyRelease(unsigned char Key
, unsigned short Mod
)
659 Game
* game
= core
->GetGame();
664 if (DialogueFlags
&DF_IN_DIALOG
) {
677 TextArea
*ta
= core
->GetMessageTextArea();
679 ta
->OnKeyPress(Key
,Mod
);
686 //cheatkeys with ctrl-
687 if (Mod
& GEM_MOD_CTRL
) {
688 if (!core
->CheatEnabled()) {
691 Map
* area
= game
->GetCurrentArea( );
694 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
695 Point
p(lastMouseX
, lastMouseY
);
696 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
698 case 'f': //toggle full screen mode
699 core
->GetVideoDriver()->ToggleFullscreenMode();
701 case 'd': //disarm a trap
703 overInfoPoint
->DetectTrap(256);
706 if (overContainer
->Trapped
&&
707 !( overContainer
->TrapDetected
)) {
708 overContainer
->TrapDetected
= 1;
712 if (overDoor
->Trapped
&&
713 !( overDoor
->TrapDetected
)) {
714 overDoor
->TrapDetected
= 1;
718 case 'l': //play an animation (vvc/bam) over an actor
719 //the original engine was able to swap through all animations
721 lastActor
->AddAnimation("S056ICBL", 0, 0, 0);
725 case 'c': //force cast a hardcoded spell
726 //caster is the last selected actor
727 //target is the door/actor currently under the pointer
728 if (game
->selected
.size() > 0) {
729 Actor
*src
= game
->selected
[0];
730 Scriptable
*target
= lastActor
;
735 src
->CastSpell( TestSpell
, target
, false );
736 if (src
->LastTarget
) {
737 src
->CastSpellEnd( TestSpell
);
739 src
->CastSpellPointEnd( TestSpell
);
745 case 'b': //draw a path to the target (pathfinder debug)
746 //You need to select an origin with ctrl-o first
748 PathNode
* nextNode
= drawPath
->Next
;
749 PathNode
* thisNode
= drawPath
;
755 nextNode
= thisNode
->Next
;
758 drawPath
= core
->GetGame()->GetCurrentArea()->FindPath( pfs
, p
, lastActor
?lastActor
->size
:1 );
762 case 'o': //set up the origin for the pathfinder
766 core
->GetVideoDriver()->ConvertToGame( pfs
.x
, pfs
.y
);
768 case 'a': //switches through the avatar animations
770 lastActor
->GetNextAnimation();
773 case 's': //switches through the stance animations
775 lastActor
->GetNextStance();
778 case 'j': //teleports the selected actors
779 for (i
= 0; i
< game
->selected
.size(); i
++) {
780 Actor
* actor
= game
->selected
[i
];
781 MoveBetweenAreasCore(actor
, core
->GetGame()->CurrentArea
, p
, -1, true);
782 printf( "Teleported to %d, %d\n", p
.x
, p
.y
);
786 case 'm': //prints a debug dump (ctrl-m in the original game too)
788 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
791 // ValidTarget never returns immobile targets, making debugging a nightmare
792 // so if we don't have an actor, we make really really sure by checking manually
793 unsigned int count
= area
->GetActorCount(true);
795 Actor
*actor
= area
->GetActor(count
, true);
796 if (actor
->IsOver(p
)) {
802 lastActor
->DebugDump();
806 overDoor
->DebugDump();
810 overContainer
->DebugDump();
814 overInfoPoint
->DebugDump();
817 core
->GetGame()->GetCurrentArea()->DebugDump(Mod
& GEM_MOD_SHIFT
);
819 case 'v': //marks some of the map visited (random vision distance)
820 area
->ExploreMapChunk( p
, rand()%30, 1 );
822 case 'x': // shows coordinates on the map
823 printf( "%s [%d.%d]\n", area
->GetScriptName(), p
.x
, p
.y
);
825 case 'g'://shows loaded areas and other game information
828 case 'i'://interact trigger (from the original game)
830 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
832 if (lastActor
&& !(lastActor
->GetStat(IE_MC_FLAGS
)&MC_EXPORTABLE
)) {
834 int i
= game
->GetPartySize(true);
839 target
= game
->GetPC(i
, true);
840 if(target
==lastActor
) continue;
841 if(target
->GetStat(IE_MC_FLAGS
)&MC_EXPORTABLE
) continue;
844 snprintf(Tmp
,sizeof(Tmp
),"Interact(\"%s\")",target
->GetScriptName() );
845 lastActor
->AddAction(GenerateAction(Tmp
));
851 case 'r'://resurrects actor
853 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
856 Effect
*fx
= EffectQueue::CreateEffect(heal_ref
, lastActor
->GetBase(IE_MAXHITPOINTS
), 0x30001, FX_DURATION_INSTANT_PERMANENT
);
858 core
->ApplyEffect(fx
, lastActor
, lastActor
);
862 case 't'://advances time
863 // 7200 (one day) /24 (hours) == 300
864 game
->AdvanceTime(300*AI_UPDATE_TIME
);
865 //refresh gui here once we got it
868 case 'q': //joins actor to the party
869 if (lastActor
&& !lastActor
->InParty
) {
870 lastActor
->ClearActions();
871 lastActor
->ClearPath();
873 strncpy(Tmp
,"JoinParty()",sizeof(Tmp
) );
874 lastActor
->AddAction( GenerateAction(Tmp
) );
877 case 'p': //center on actor
878 ScreenFlags
|=SF_CENTERONACTOR
;
879 ScreenFlags
^=SF_ALWAYSCENTER
;
881 case 'k': //kicks out actor
882 if (lastActor
&& lastActor
->InParty
) {
883 lastActor
->ClearActions();
884 lastActor
->ClearPath();
886 strncpy(Tmp
,"LeaveParty()",sizeof(Tmp
) );
887 lastActor
->AddAction( GenerateAction(Tmp
) );
890 case 'y': //kills actor or all enemies
891 if (Mod
& GEM_MOD_SHIFT
) {
894 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_MAGIC
<<16, FX_DURATION_INSTANT_PERMANENT
);
896 for (int i
= area
->GetActorCount(0)-1; i
>= 0; i
--) {
897 victim
= area
->GetActor(i
, 0);
898 if (victim
->Modified
[IE_EA
] == EA_ENEMY
) {
899 core
->ApplyEffect(newfx
, victim
, victim
);
905 //using action so the actor is killed
906 //correctly (synchronisation)
907 lastActor
->ClearActions();
908 lastActor
->ClearPath();
911 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_MAGIC
<<16, FX_DURATION_INSTANT_PERMANENT
);
912 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
913 if (! (lastActor
->GetInternalFlag() & IF_REALLYDIED
)) {
914 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_ACID
<<16, FX_DURATION_INSTANT_PERMANENT
);
915 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
916 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_CRUSHING
<<16, FX_DURATION_INSTANT_PERMANENT
);
917 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
920 } else if (overContainer
) {
921 overContainer
->SetContainerLocked(0);
922 } else if (overDoor
) {
923 overDoor
->SetDoorLocked(0,0);
927 case 'z': //shift through the avatar animations backward
929 lastActor
->GetPrevAnimation();
932 case '1': //change paperdoll armour level
935 lastActor
->NewStat(IE_ARMOR_TYPE
,1,MOD_ADDITIVE
);
937 case '4': //show all traps and infopoints
938 DebugFlags
^= DEBUG_SHOW_INFOPOINTS
;
939 printf("Show traps and infopoints %s\n", DebugFlags
& DEBUG_SHOW_INFOPOINTS
? "ON" : "OFF");
941 case '6': //show the lightmap
942 DebugFlags
^= DEBUG_SHOW_LIGHTMAP
;
943 printf("Show lightmap %s\n", DebugFlags
& DEBUG_SHOW_LIGHTMAP
? "ON" : "OFF");
945 case '7': //toggles fog of war
947 printf("Show Fog-Of-War: %s\n", core
->FogOfWar
& 1 ? "ON" : "OFF");
949 case '8': //show searchmap over area
951 printf("Show searchmap %s\n", core
->FogOfWar
& 2 ? "ON" : "OFF");
954 printf( "KeyRelease:%d - %d\n", Key
, Mod
);
957 return; //return from cheatkeys
960 case 'h': //hard pause
961 if (DialogueFlags
& DF_FREEZE_SCRIPTS
) break;
963 case ' ': //soft pause
964 DialogueFlags
^= DF_FREEZE_SCRIPTS
;
965 if (DialogueFlags
&DF_FREEZE_SCRIPTS
) {
966 displaymsg
->DisplayConstantString(STR_PAUSED
,0xff0000);
967 SetDisplayText(STR_PAUSED
, 0); // time 0 = removed instantly on unpause
969 displaymsg
->DisplayConstantString(STR_UNPAUSED
,0xff0000);
973 core
->GetGUIScriptEngine()->RunFunction("GUIMA","OpenMapWindow");
976 core
->GetGUIScriptEngine()->RunFunction("GUIJRNL","OpenJournalWindow");
979 core
->GetGUIScriptEngine()->RunFunction("GUIINV","OpenInventoryWindow");
982 core
->GetGUIScriptEngine()->RunFunction("GUIREC","OpenRecordsWindow");
984 case 'q': //quicksave
987 case GEM_ALT
: //alt key (shows containers)
988 DebugFlags
&= ~DEBUG_SHOW_CONTAINERS
;
995 void GameControl::DisplayTooltip() {
996 Game
* game
= core
->GetGame();
998 Map
* area
= game
->GetCurrentArea( );
1000 Actor
*actor
= area
->GetActorByGlobalID(lastActorID
);
1001 if (actor
&& (actor
->GetStat(IE_STATE_ID
)&STATE_DEAD
|| actor
->GetInternalFlag()&IF_JUSTDIED
)) {
1002 // checking IF_JUSTDIED is kind of horrid, but seems necessary
1003 // no tooltips for dead actors!
1004 actor
->SetOver( false );
1010 char *name
= actor
->GetName(-1);
1011 int hp
= actor
->GetStat(IE_HITPOINTS
);
1012 int maxhp
= actor
->GetStat(IE_MAXHITPOINTS
);
1015 if (!core
->TooltipBack
) {
1016 // single-line tooltips without background (PS:T)
1017 if (actor
->InParty
) {
1018 snprintf(buffer
, 100, "%s: %d/%d", name
, hp
, maxhp
);
1020 snprintf(buffer
, 100, "%s", name
);
1023 // a guess at a neutral check
1024 bool neutral
= actor
->GetStat(IE_EA
) == EA_NEUTRAL
;
1025 // test for an injured string being present for this game
1026 int strindex
= displaymsg
->GetStringReference(STR_UNINJURED
);
1028 if (actor
->InParty
) {
1029 // in party: display hp
1030 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1031 } else if (neutral
) {
1032 // neutral: display name only
1033 snprintf(buffer
, 100, "%s", name
);
1034 } else if (strindex
== -1) {
1035 // non-neutral, not in party, no injured strings: display hp
1036 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1038 // non-neutral, not in party: display injured string
1040 char *injuredstring
= NULL
;
1041 // these boundaries are just a guess
1043 strindex
= STR_UNINJURED
;
1044 } else if (hp
> (maxhp
*3)/4) {
1045 strindex
= STR_INJURED1
;
1046 } else if (hp
> maxhp
/2) {
1047 strindex
= STR_INJURED2
;
1048 } else if (hp
> maxhp
/3) {
1049 strindex
= STR_INJURED3
;
1051 strindex
= STR_INJURED4
;
1053 strindex
= displaymsg
->GetStringReference(strindex
);
1054 if (strindex
!= -1) {
1055 injuredstring
= core
->GetString(strindex
, 0);
1058 if (!injuredstring
) {
1059 // eek, where did the string go?
1060 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1062 snprintf(buffer
, 100, "%s\n%s", name
, injuredstring
);
1063 free(injuredstring
);
1068 Point p
= actor
->Pos
;
1069 core
->GetVideoDriver()->ConvertToScreen( p
.x
, p
.y
);
1070 p
.x
+= Owner
->XPos
+ XPos
;
1071 p
.y
+= Owner
->YPos
+ YPos
;
1073 // hack to position text above PS:T actors
1074 if (!core
->TooltipBack
) p
.y
-= actor
->size
*50;
1076 // we should probably cope better with moving actors
1078 core
->DisplayTooltip(p
.x
, p
.y
, this);
1085 core
->DisplayTooltip(0, 0, NULL
);
1089 //returns the appropriate cursor over an active region (trap, infopoint, travel region)
1090 int GameControl::GetCursorOverInfoPoint(InfoPoint
*overInfoPoint
)
1092 if (target_mode
== TARGET_MODE_PICK
) {
1093 if (overInfoPoint
->VisibleTrap(0)) {
1094 return IE_CURSOR_TRAP
;
1097 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1099 // traps always display a walk cursor?
1100 if (overInfoPoint
->Type
== ST_PROXIMITY
) {
1101 return IE_CURSOR_WALK
;
1103 return overInfoPoint
->Cursor
;
1106 //returns the appropriate cursor over a door
1107 int GameControl::GetCursorOverDoor(Door
*overDoor
)
1109 if (target_mode
== TARGET_MODE_PICK
) {
1110 if (overDoor
->VisibleTrap(0)) {
1111 return IE_CURSOR_TRAP
;
1113 if (overDoor
->Flags
& DOOR_LOCKED
) {
1114 return IE_CURSOR_LOCK
;
1117 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1119 return overDoor
->Cursor
;
1122 //returns the appropriate cursor over a container (or pile)
1123 int GameControl::GetCursorOverContainer(Container
*overContainer
)
1125 if (target_mode
== TARGET_MODE_PICK
) {
1126 if (overContainer
->VisibleTrap(0)) {
1127 return IE_CURSOR_TRAP
;
1129 if (overContainer
->Flags
& CONT_LOCKED
) {
1130 return IE_CURSOR_LOCK2
;
1133 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1135 return IE_CURSOR_TAKE
;
1138 /** Mouse Over Event */
1139 void GameControl::OnMouseOver(unsigned short x
, unsigned short y
)
1141 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1148 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
1149 if (MouseIsDown
&& ( !DrawSelectionRect
)) {
1150 if (( abs( p
.x
- StartX
) > 5 ) || ( abs( p
.y
- StartY
) > 5 )) {
1151 DrawSelectionRect
= true;
1154 Game
* game
= core
->GetGame();
1156 Map
* area
= game
->GetCurrentArea( );
1158 int nextCursor
= area
->GetCursor( p
);
1159 //make the invisible area really invisible
1160 if (nextCursor
== IE_CURSOR_INVALID
) {
1161 Owner
->Cursor
= IE_CURSOR_BLOCKED
;
1162 lastCursor
= IE_CURSOR_BLOCKED
;
1166 overInfoPoint
= area
->TMap
->GetInfoPoint( p
, true );
1167 if (overInfoPoint
) {
1168 //nextCursor = overInfoPoint->Cursor;
1169 nextCursor
= GetCursorOverInfoPoint(overInfoPoint
);
1173 overDoor
->Highlight
= false;
1175 if (overContainer
) {
1176 overContainer
->Highlight
= false;
1178 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
1180 lastActor
->SetOver( false );
1183 overDoor
= area
->TMap
->GetDoor( p
);
1184 overContainer
= area
->TMap
->GetContainer( p
);
1186 if (!DrawSelectionRect
) {
1188 nextCursor
= GetCursorOverDoor(overDoor
);
1191 if (overContainer
) {
1192 nextCursor
= GetCursorOverContainer(overContainer
);
1195 Actor
*prevActor
= lastActor
;
1196 lastActor
= area
->GetActor( p
, target_types
);
1197 if (lastActor
!= prevActor
) {
1198 // we store prevActor so we can remove the tooltip on actor change
1199 // (maybe we should be checking this and actor movements every frame?)
1201 core
->DisplayTooltip(0, 0, this);
1204 if ((target_types
& GA_NO_SELF
) && lastActor
) {
1205 if (lastActor
== core
->GetFirstSelectedPC(false)) {
1211 lastActorID
= lastActor
->globalID
;
1212 lastActor
->SetOver( true );
1213 ieDword type
= lastActor
->GetStat(IE_EA
);
1214 if (type
>= EA_EVILCUTOFF
|| type
== EA_GOODBUTRED
) {
1215 nextCursor
= IE_CURSOR_ATTACK
;
1216 } else if ( type
> EA_CHARMED
) {
1217 nextCursor
= IE_CURSOR_TALK
;
1219 nextCursor
= IE_CURSOR_NORMAL
;
1225 if (target_mode
== TARGET_MODE_TALK
) {
1226 nextCursor
= IE_CURSOR_TALK
;
1228 nextCursor
|= IE_CURSOR_GRAY
;
1230 } else if (target_mode
== TARGET_MODE_ATTACK
) {
1231 nextCursor
= IE_CURSOR_ATTACK
;
1232 if (!lastActor
&& !overDoor
&& !overContainer
) {
1233 nextCursor
|= IE_CURSOR_GRAY
;
1235 } else if (target_mode
== TARGET_MODE_CAST
) {
1236 nextCursor
= IE_CURSOR_CAST
;
1237 //point is always valid
1238 if (!(target_types
& GA_POINT
)) {
1240 nextCursor
|= IE_CURSOR_GRAY
;
1243 } else if (target_mode
== TARGET_MODE_DEFEND
) {
1244 nextCursor
= IE_CURSOR_DEFEND
;
1246 nextCursor
|= IE_CURSOR_GRAY
;
1248 } else if (target_mode
== TARGET_MODE_PICK
) {
1250 nextCursor
= IE_CURSOR_PICK
;
1252 if (!overContainer
&& !overDoor
&& !overInfoPoint
) {
1253 nextCursor
= IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1260 switch (lastActor
->GetStat(IE_EA
)) {
1270 case EA_EVILBUTGREEN
:
1271 if (target_types
& GA_NO_ENEMY
)
1277 if (target_types
& GA_NO_ALLY
)
1281 if (!(target_types
& GA_NO_NEUTRAL
))
1288 if (lastCursor
!= nextCursor
) {
1289 Owner
->Cursor
= nextCursor
;
1290 lastCursor
= (unsigned char) nextCursor
;
1294 #define SCROLL_BORDER 5
1296 /** Global Mouse Move Event */
1297 void GameControl::OnGlobalMouseMove(unsigned short x
, unsigned short y
)
1299 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1303 if (Owner
->Visible
!=WINDOW_VISIBLE
) {
1307 int mousescrollspd
= core
->GetMouseScrollSpeed();
1309 if (x
<= SCROLL_BORDER
)
1310 moveX
= -mousescrollspd
;
1312 if (x
>= ( core
->Width
- SCROLL_BORDER
))
1313 moveX
= mousescrollspd
;
1317 if (y
<= SCROLL_BORDER
)
1318 moveY
= -mousescrollspd
;
1320 if (y
>= ( core
->Height
- SCROLL_BORDER
))
1321 moveY
= mousescrollspd
;
1326 if (moveX
!= 0 || moveY
!= 0) {
1328 } else if (scrolling
) {
1331 Video
* video
= core
->GetVideoDriver();
1332 video
->SetDragCursor(NULL
);
1336 void GameControl::UpdateScrolling() {
1337 if (!scrolling
) return;
1339 int mousescrollspd
= core
->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-?
1340 Video
* video
= core
->GetVideoDriver();
1342 if (moveX
== mousescrollspd
&& moveY
== 0) { // right
1343 video
->SetDragCursor(core
->GetScrollCursorSprite(0,numScrollCursor
));
1344 } else if (moveX
== mousescrollspd
&& moveY
== -mousescrollspd
) { // upper right
1345 video
->SetDragCursor(core
->GetScrollCursorSprite(1,numScrollCursor
));
1346 } else if (moveX
== 0 && moveY
== -mousescrollspd
) { // up
1347 video
->SetDragCursor(core
->GetScrollCursorSprite(2,numScrollCursor
));
1348 } else if (moveX
== -mousescrollspd
&& moveY
== -mousescrollspd
) { // upper left
1349 video
->SetDragCursor(core
->GetScrollCursorSprite(3,numScrollCursor
));
1350 } else if (moveX
== -mousescrollspd
&& moveY
== 0) { // left
1351 video
->SetDragCursor(core
->GetScrollCursorSprite(4,numScrollCursor
));
1352 } else if (moveX
== -mousescrollspd
&& moveY
== mousescrollspd
) { // bottom left
1353 video
->SetDragCursor(core
->GetScrollCursorSprite(5,numScrollCursor
));
1354 } else if (moveX
== 0 && moveY
== mousescrollspd
) { // bottom
1355 video
->SetDragCursor(core
->GetScrollCursorSprite(6,numScrollCursor
));
1356 } else if (moveX
== mousescrollspd
&& moveY
== mousescrollspd
) { // bottom right
1357 video
->SetDragCursor(core
->GetScrollCursorSprite(7,numScrollCursor
));
1360 numScrollCursor
= (numScrollCursor
+1) % 15;
1363 //generate action code for source actor to try to attack a target
1364 void GameControl::TryToAttack(Actor
*source
, Actor
*tgt
)
1368 source
->ClearPath();
1369 source
->ClearActions();
1370 strncpy(Tmp
,"NIDSpecial3()",sizeof(Tmp
) );
1371 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1374 //generate action code for source actor to try to defend a target
1375 void GameControl::TryToDefend(Actor
*source
, Actor
*tgt
)
1379 source
->ClearPath();
1380 source
->ClearActions();
1381 strncpy(Tmp
,"NIDSpecial4()",sizeof(Tmp
) );
1382 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1385 //generate action code for source actor to try to pick pockets of a target
1386 //The -1 flag is a placeholder for dynamic target IDs
1387 void GameControl::TryToPick(Actor
*source
, Actor
*tgt
)
1391 source
->ClearPath();
1392 source
->ClearActions();
1393 strncpy(Tmp
,"PickPockets([-1])", sizeof(Tmp
) );
1394 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1397 //generate action code for source actor to try to pick a lock/disable trap on a door
1398 void GameControl::TryToPick(Actor
*source
, Door
*tgt
)
1402 source
->ClearPath();
1403 source
->ClearActions();
1404 if (tgt
->Trapped
&& tgt
->TrapDetected
) {
1405 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1407 snprintf(Tmp
, sizeof(Tmp
), "PickLock(\"%s\")", tgt
->GetScriptName() );
1409 source
->AddAction( GenerateAction( Tmp
) );
1412 //generate action code for source actor to try to pick a lock/disable trap on a container
1413 void GameControl::TryToPick(Actor
*source
, Container
*tgt
)
1417 source
->ClearPath();
1418 source
->ClearActions();
1419 if (tgt
->Trapped
&& tgt
->TrapDetected
) {
1420 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1422 snprintf(Tmp
, sizeof(Tmp
), "PickLock(\"%s\")", tgt
->GetScriptName() );
1424 source
->AddAction( GenerateAction( Tmp
) );
1427 //generate action code for source actor to try to disable trap (only trap type active regions)
1428 void GameControl::TryToDisarm(Actor
*source
, InfoPoint
*tgt
)
1430 if (tgt
->Type
!=ST_PROXIMITY
) return;
1434 source
->ClearPath();
1435 source
->ClearActions();
1436 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1437 source
->AddAction( GenerateAction( Tmp
) );
1440 //generate action code for source actor to try to force open lock on a door/container
1441 void GameControl::TryToBash(Actor
*source
, Scriptable
*tgt
)
1445 source
->ClearPath();
1446 source
->ClearActions();
1447 snprintf(Tmp
, sizeof(Tmp
), "Attack(\"%s\")", tgt
->GetScriptName() );
1448 source
->AddAction( GenerateAction( Tmp
) );
1451 //generate action code for source actor to use item/cast spell on a point
1452 void GameControl::TryToCast(Actor
*source
, const Point
&tgt
)
1457 target_mode
= TARGET_MODE_NONE
;
1458 return; //not casting or using an own item
1460 source
->ClearPath();
1461 source
->ClearActions();
1464 if (spellOrItem
>=0) {
1465 sprintf(Tmp
, "NIDSpecial8()");
1467 //using item on target
1468 sprintf(Tmp
, "NIDSpecial7()");
1470 Action
* action
= GenerateAction( Tmp
);
1471 action
->pointParameter
=tgt
;
1474 CREMemorizedSpell
*si
;
1475 //spell casting at target
1476 si
= source
->spellbook
.GetMemorizedSpell(spellOrItem
, spellSlot
, spellIndex
);
1479 target_mode
= TARGET_MODE_NONE
;
1482 sprintf(action
->string0Parameter
,"%.8s",si
->SpellResRef
);
1486 action
->int0Parameter
=spellSlot
;
1487 action
->int1Parameter
=spellIndex
;
1489 source
->AddAction( action
);
1491 target_mode
= TARGET_MODE_NONE
;
1495 //generate action code for source actor to use item/cast spell on another actor
1496 void GameControl::TryToCast(Actor
*source
, Actor
*tgt
)
1501 target_mode
= TARGET_MODE_NONE
;
1502 return; //not casting or using an own item
1504 source
->ClearPath();
1505 source
->ClearActions();
1508 if (spellOrItem
>=0) {
1509 sprintf(Tmp
, "NIDSpecial6()");
1511 //using item on target
1512 sprintf(Tmp
, "NIDSpecial5()");
1514 Action
* action
= GenerateActionDirect( Tmp
, tgt
);
1517 CREMemorizedSpell
*si
;
1518 //spell casting at target
1519 si
= source
->spellbook
.GetMemorizedSpell(spellOrItem
, spellSlot
, spellIndex
);
1522 target_mode
= TARGET_MODE_NONE
;
1525 sprintf(action
->string0Parameter
,"%.8s",si
->SpellResRef
);
1529 action
->int0Parameter
=spellSlot
;
1530 action
->int1Parameter
=spellIndex
;
1532 source
->AddAction( action
);
1534 target_mode
= TARGET_MODE_NONE
;
1538 //generate action code for source actor to use talk to target actor
1539 void GameControl::TryToTalk(Actor
*source
, Actor
*tgt
)
1543 //Nidspecial1 is just an unused action existing in all games
1544 //(non interactive demo)
1545 //i found no fitting action which would emulate this kind of
1547 source
->ClearPath();
1548 source
->ClearActions();
1549 strncpy(Tmp
,"NIDSpecial1()",sizeof(Tmp
) );
1550 targetID
= tgt
->globalID
; //this is a hack, but not so deadly
1551 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1554 //generate action code for actor appropriate for the target mode when the target is a container
1555 void GameControl::HandleContainer(Container
*container
, Actor
*actor
)
1559 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1560 //we'll get the container back from the coordinates
1561 TryToCast(actor
, container
->Pos
);
1562 //Do not reset target_mode, TryToCast does it for us!!
1566 if (target_mode
== TARGET_MODE_ATTACK
) {
1567 TryToBash(actor
, container
);
1568 target_mode
= TARGET_MODE_NONE
;
1572 if ((target_mode
== TARGET_MODE_PICK
)) {
1573 TryToPick(actor
, container
);
1574 target_mode
= TARGET_MODE_NONE
;
1579 actor
->ClearActions();
1580 strncpy(Tmp
,"UseContainer()",sizeof(Tmp
) );
1581 core
->SetCurrentContainer( actor
, container
);
1582 actor
->AddAction( GenerateAction( Tmp
) );
1585 //generate action code for actor appropriate for the target mode when the target is a door
1586 void GameControl::HandleDoor(Door
*door
, Actor
*actor
)
1590 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1591 //we'll get the door back from the coordinates
1592 Point
*p
= door
->toOpen
;
1593 Point
*otherp
= door
->toOpen
+1;
1594 if (Distance(*p
,actor
)>Distance(*otherp
,actor
)) {
1597 TryToCast(actor
, *p
);
1601 if (target_mode
== TARGET_MODE_ATTACK
) {
1602 TryToBash(actor
, door
);
1603 target_mode
= TARGET_MODE_NONE
;
1607 if ( (target_mode
== TARGET_MODE_PICK
) || door
->TrapDetected
) {
1608 TryToPick(actor
, door
);
1609 target_mode
= TARGET_MODE_NONE
;
1614 actor
->ClearActions();
1615 // it really isn't very nice to store a pointer in the actor like this
1616 actor
->TargetDoor
= door
;
1617 // internal gemrb toggle door action hack - should we use UseDoor instead?
1618 sprintf( Tmp
, "NIDSpecial9()" );
1619 actor
->AddAction( GenerateAction( Tmp
) );
1622 //generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel)
1623 bool GameControl::HandleActiveRegion(InfoPoint
*trap
, Actor
* actor
, Point
&p
)
1625 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1626 //we'll get the active region from the coordinates (if needed)
1627 TryToCast(actor
, p
);
1628 //don't bother with this region further
1631 if ((target_mode
== TARGET_MODE_PICK
)) {
1632 TryToDisarm(actor
, trap
);
1633 target_mode
= TARGET_MODE_NONE
;
1637 switch(trap
->Type
) {
1639 actor
->UseExit(true);
1642 //the importer shouldn't load the script
1643 //if it is unallowed anyway (though
1644 //deactivated scripts could be reactivated)
1645 //only the 'trapped' flag should be honoured
1646 //there. Here we have to check on the
1647 //reset trap and deactivated flags
1648 if (trap
->Scripts
[0]) {
1649 if (!(trap
->Flags
&TRAP_DEACTIVATED
) ) {
1650 trap
->LastTriggerObject
= trap
->LastTrigger
= actor
->GetID();
1651 trap
->ImmediateEvent();
1652 //directly feeding the event, even if there are actions in the queue
1653 trap
->Scripts
[0]->Update();
1654 trap
->ProcessActions(true);
1655 //if reset trap flag not set, deactivate it
1656 //hmm, better not, info triggers don't deactivate themselves on click
1657 //if (!(trap->Flags&TRAP_RESET)) {
1658 // trap->Flags|=TRAP_DEACTIVATED;
1662 if (trap
->overHeadText
) {
1663 if (trap
->textDisplaying
!= 1) {
1664 trap
->textDisplaying
= 1;
1665 trap
->timeStartDisplaying
= core
->GetGame()->Ticks
;
1666 DisplayString( trap
);
1670 if (trap
->Flags
&TRAP_USEPOINT
) {
1671 //overriding the target point
1680 /** Mouse Button Down */
1681 void GameControl::OnMouseDown(unsigned short x
, unsigned short y
, unsigned short Button
,
1682 unsigned short /*Mod*/)
1684 if (ScreenFlags
&SF_DISABLEMOUSE
)
1689 DoubleClick
= false;
1693 OnSpecialKeyPress(GEM_UP
);
1695 case GEM_MB_SCRLDOWN
:
1696 OnSpecialKeyPress(GEM_DOWN
);
1698 case GEM_MB_ACTION
|GEM_MB_DOUBLECLICK
:
1701 core
->GetVideoDriver()->ConvertToGame( px
, py
);
1703 SelectionRect
.x
= px
;
1704 SelectionRect
.y
= py
;
1707 SelectionRect
.w
= 0;
1708 SelectionRect
.h
= 0;
1711 /** Mouse Button Up */
1712 void GameControl::OnMouseUp(unsigned short x
, unsigned short y
, unsigned short Button
,
1713 unsigned short /*Mod*/)
1718 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1721 //heh, i found no better place
1722 core
->CloseCurrentContainer();
1724 MouseIsDown
= false;
1726 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
1727 Game
* game
= core
->GetGame();
1728 Map
* area
= game
->GetCurrentArea( );
1730 if (DrawSelectionRect
) {
1732 unsigned int count
= area
->GetActorInRect( ab
, SelectionRect
,true );
1733 for (i
= 0; i
< highlighted
.size(); i
++)
1734 highlighted
[i
]->SetOver( false );
1735 highlighted
.clear();
1736 game
->SelectActor( NULL
, false, SELECT_NORMAL
);
1738 for (i
= 0; i
< count
; i
++) {
1739 // FIXME: should call handler only once
1740 game
->SelectActor( ab
[i
], true, SELECT_NORMAL
);
1744 DrawSelectionRect
= false;
1748 //hidden actors are not selectable by clicking on them
1749 Actor
* actor
= area
->GetActor( p
, GA_DEFAULT
| GA_SELECT
| GA_NO_DEAD
| GA_NO_HIDDEN
);
1750 if (Button
== GEM_MB_MENU
) {
1753 DisplayStringCore(actor
, VB_SELECT
+core
->Roll(1,3,-1), DS_CONST
|DS_CONSOLE
);
1756 core
->GetDictionary()->SetAt( "MenuX", x
);
1757 core
->GetDictionary()->SetAt( "MenuY", y
);
1758 core
->GetGUIScriptEngine()->RunFunction( "GUICommon", "OpenFloatMenuWindow" );
1762 if (Button
!= GEM_MB_ACTION
) {
1766 if (!actor
&& ( game
->selected
.size() > 0 )) {
1768 HandleDoor(overDoor
, core
->GetFirstSelectedPC(false));
1771 if (overContainer
) {
1772 HandleContainer(overContainer
, core
->GetFirstSelectedPC(false));
1775 if (overInfoPoint
) {
1776 if (HandleActiveRegion(overInfoPoint
, core
->GetFirstSelectedPC(false), p
)) {
1781 //just a single actor, no formation
1782 if (game
->selected
.size()==1) {
1783 //the player is using an item or spell on the ground
1784 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1785 TryToCast(core
->GetFirstSelectedPC(false), p
);
1789 actor
=game
->selected
[0];
1791 actor
->ClearActions();
1792 CreateMovement(actor
, p
);
1795 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
1797 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
1800 actor->AddAction( GenerateAction( Tmp) );
1802 //p is a searchmap travel region
1803 if ( actor
->GetCurrentArea()->GetCursor(p
) == IE_CURSOR_TRAVEL
) {
1804 sprintf( Tmp
, "NIDSpecial2()" );
1805 actor
->AddAction( GenerateAction( Tmp
) );
1810 // construct a sorted party
1811 // TODO: this is beyond horrible, help
1812 std::vector
<Actor
*> party
;
1813 // first, from the actual party
1814 for (int idx
= 0; idx
< game
->GetPartySize(false); idx
++) {
1815 Actor
*pc
= game
->FindPC(idx
+ 1);
1818 for (unsigned int j
= 0; j
< game
->selected
.size(); j
++) {
1819 if (game
->selected
[j
] == pc
) {
1820 party
.push_back(pc
);
1825 // then, anything else we selected
1826 for (i
= 0; i
< game
->selected
.size(); i
++) {
1828 for (unsigned int j
= 0; j
< party
.size(); j
++) {
1829 if (game
->selected
[i
] == party
[j
]) {
1834 if (!found
) party
.push_back(game
->selected
[i
]);
1837 //party formation movement
1838 Point src
= party
[0]->Pos
;
1839 for(i
= 0; i
< party
.size(); i
++) {
1842 actor
->ClearActions();
1843 MoveToPointFormation(actor
, i
, src
, p
);
1846 //p is a searchmap travel region
1847 if ( party
[0]->GetCurrentArea()->GetCursor(p
) == IE_CURSOR_TRAVEL
) {
1848 sprintf( Tmp
, "NIDSpecial2()" );
1849 party
[0]->AddAction( GenerateAction( Tmp
) );
1854 //we got an actor past this point
1855 DisplayStringCore(actor
, VB_SELECT
+core
->Roll(1,3,-1), DS_CONST
|DS_CONSOLE
);
1857 PerformActionOn(actor
);
1860 void GameControl::PerformActionOn(Actor
*actor
) {
1861 Game
* game
= core
->GetGame();
1864 //determining the type of the clicked actor
1867 type
= actor
->GetStat(IE_EA
);
1868 if ( type
>= EA_EVILCUTOFF
|| type
== EA_GOODBUTRED
) {
1869 type
= ACT_ATTACK
; //hostile
1870 } else if ( type
> EA_CHARMED
) {
1871 type
= ACT_TALK
; //neutral
1873 type
= ACT_NONE
; //party
1876 if (target_mode
== TARGET_MODE_ATTACK
) {
1878 } else if (target_mode
== TARGET_MODE_TALK
) {
1880 } else if (target_mode
== TARGET_MODE_CAST
) {
1882 } else if (target_mode
== TARGET_MODE_DEFEND
) {
1884 } else if (target_mode
== TARGET_MODE_PICK
) {
1885 type
= ACT_THIEVING
;
1888 //we shouldn't zero this for two reasons in case of spell or item
1889 //1. there could be multiple targets
1890 //2. the target mode is important
1891 if (!(target_mode
== TARGET_MODE_CAST
) || !spellCount
) {
1892 target_mode
= TARGET_MODE_NONE
;
1896 case ACT_NONE
: //none
1898 SelectActor( actor
->InParty
);
1899 else if (actor
->GetStat(IE_EA
) <= EA_CHARMED
) {
1900 /*let's select charmed/summoned creatures
1901 EA_CHARMED is the maximum value known atm*/
1902 core
->GetGame()->SelectActor(actor
, true, SELECT_REPLACE
) ;
1906 //talk (first selected talks)
1907 if (game
->selected
.size()) {
1908 //if we are in PST modify this to NO!
1910 if (core
->HasFeature(GF_PROTAGONIST_TALKS
) ) {
1911 source
= game
->GetPC(0, false); //protagonist
1913 source
= core
->GetFirstSelectedPC(false);
1915 // only party members can start conversations
1917 TryToTalk(source
, actor
);
1922 //all of them attacks the red circled actor
1923 for(i
=0;i
<game
->selected
.size();i
++) {
1924 TryToAttack(game
->selected
[i
], actor
);
1927 case ACT_CAST
: //cast on target or use item on target
1928 if (game
->selected
.size()==1) {
1930 source
= core
->GetFirstSelectedPC(false);
1932 TryToCast(source
, actor
);
1937 for(i
=0;i
<game
->selected
.size();i
++) {
1938 TryToDefend(game
->selected
[i
], actor
);
1942 if (game
->selected
.size()==1) {
1944 source
= core
->GetFirstSelectedPC(false);
1946 TryToPick(source
, actor
);
1952 /** Special Key Press */
1953 void GameControl::OnSpecialKeyPress(unsigned char Key
)
1955 if (DialogueFlags
&DF_IN_DIALOG
) {
1958 //simulating the continue/end button pressed
1959 core
->GetGUIScriptEngine()->RunFunction("GUIWORLD", "CloseContinueWindow");
1962 return; //don't accept keys in dialog
1964 Region Viewport
= core
->GetVideoDriver()->GetViewport();
1965 Game
*game
= core
->GetGame();
1966 Point mapsize
= game
->GetCurrentArea()->TMap
->GetMapSize();
1967 int partysize
= game
->GetPartySize(false);
1973 if (Viewport
.x
> 63)
1979 if (Viewport
.y
> 63)
1985 if (Viewport
.y
+ Viewport
.h
+ 64 < mapsize
.y
)
1988 Viewport
.y
= mapsize
.y
- Viewport
.h
;
1989 if (Viewport
.y
<0) Viewport
.y
=0;
1993 if (Viewport
.x
+ Viewport
.w
+ 64 < mapsize
.x
)
1996 Viewport
.x
= mapsize
.x
- Viewport
.w
;
1997 if (Viewport
.x
<0) Viewport
.x
=0;
2001 DebugFlags
|= DEBUG_SHOW_CONTAINERS
;
2004 // show partymember hp/maxhp as overhead text
2005 for (pm
=0; pm
< partysize
; pm
++) {
2006 Actor
*pc
= game
->GetPC(pm
, true);
2008 memset(tmpstr
, 0, 10);
2009 snprintf(tmpstr
, 10, "%d/%d", pc
->Modified
[IE_HITPOINTS
], pc
->Modified
[IE_MAXHITPOINTS
]);
2010 pc
->DisplayHeadText(strdup(tmpstr
));
2018 core
->GetGUIScriptEngine()->RunFunction("GUICommonWindows", "EmptyControls");
2019 core
->SetEventFlag(EF_ACTION
);
2022 core
->GetGUIScriptEngine()->RunFunction("CommonWindow","OnIncreaseSize");
2025 core
->GetGUIScriptEngine()->RunFunction("CommonWindow","OnDecreaseSize");
2030 if (ScreenFlags
& SF_LOCKSCROLL
) {
2035 // override any existing viewport moves which may be in progress
2036 core
->timer
->SetMoveViewPort( Viewport
.x
, Viewport
.y
, 0, false );
2037 // move it directly ourselves, since we might be paused
2038 core
->GetVideoDriver()->MoveViewportTo( Viewport
.x
, Viewport
.y
);
2042 void GameControl::CalculateSelection(const Point
&p
)
2045 Game
* game
= core
->GetGame();
2046 Map
* area
= game
->GetCurrentArea( );
2047 if (DrawSelectionRect
) {
2049 SelectionRect
.w
= StartX
- p
.x
;
2050 SelectionRect
.x
= p
.x
;
2052 SelectionRect
.x
= StartX
;
2053 SelectionRect
.w
= p
.x
- StartX
;
2056 SelectionRect
.h
= StartY
- p
.y
;
2057 SelectionRect
.y
= p
.y
;
2059 SelectionRect
.y
= StartY
;
2060 SelectionRect
.h
= p
.y
- StartY
;
2063 unsigned int count
= area
->GetActorInRect( ab
, SelectionRect
,true );
2064 for (i
= 0; i
< highlighted
.size(); i
++)
2065 highlighted
[i
]->SetOver( false );
2066 highlighted
.clear();
2068 for (i
= 0; i
< count
; i
++) {
2069 ab
[i
]->SetOver( true );
2070 highlighted
.push_back( ab
[i
] );
2075 Actor
* actor
= area
->GetActor( p
, GA_DEFAULT
| GA_SELECT
| GA_NO_DEAD
| GA_NO_ENEMY
);
2076 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
2078 lastActor
->SetOver( false );
2082 lastActorID
= actor
->globalID
;
2083 actor
->SetOver( true );
2088 void GameControl::SetCutSceneMode(bool active
)
2091 ScreenFlags
|= (SF_DISABLEMOUSE
| SF_LOCKSCROLL
| SF_CUTSCENE
);
2095 ScreenFlags
&= ~(SF_DISABLEMOUSE
| SF_LOCKSCROLL
| SF_CUTSCENE
);
2099 //Change game window geometries when a new window gets deactivated
2100 void GameControl::HandleWindowHide(const char *WindowName
, const char *WindowPosition
)
2102 Variables
* dict
= core
->GetDictionary();
2105 if (dict
->Lookup( WindowName
, index
)) {
2106 if (index
!= (ieDword
) -1) {
2107 Window
* w
= core
->GetWindow( (unsigned short) index
);
2109 core
->SetVisible( (unsigned short) index
, WINDOW_INVISIBLE
);
2110 if (dict
->Lookup( WindowPosition
, index
)) {
2111 ResizeDel( w
, index
);
2115 printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED
);
2116 printf("%s:%u\n",WindowName
, index
);
2121 //Hide all other windows on the GUI (gamecontrol is not hidden by this)
2122 int GameControl::HideGUI()
2124 //hidegui is in effect
2125 if (!(ScreenFlags
&SF_GUIENABLED
) ) {
2128 //no gamecontrol visible
2129 if (Owner
->Visible
== WINDOW_INVISIBLE
) {
2132 ScreenFlags
&=~SF_GUIENABLED
;
2133 HandleWindowHide("PortraitWindow", "PortraitPosition");
2134 HandleWindowHide("OtherWindow", "OtherPosition");
2135 HandleWindowHide("TopWindow", "TopPosition");
2136 HandleWindowHide("OptionsWindow", "OptionsPosition");
2137 HandleWindowHide("MessageWindow", "MessagePosition");
2138 HandleWindowHide("ActionsWindow", "ActionsPosition");
2139 //FloatWindow doesn't affect gamecontrol, so it is special
2140 Variables
* dict
= core
->GetDictionary();
2143 if (dict
->Lookup( "FloatWindow", index
)) {
2144 if (index
!= (ieDword
) -1) {
2145 core
->SetVisible( (unsigned short) index
, WINDOW_INVISIBLE
);
2148 core
->GetVideoDriver()->SetViewport( Owner
->XPos
, Owner
->YPos
, Width
, Height
);
2152 //Change game window geometries when a new window gets activated
2153 void GameControl::HandleWindowReveal(const char *WindowName
, const char *WindowPosition
)
2155 Variables
* dict
= core
->GetDictionary();
2158 if (dict
->Lookup( WindowName
, index
)) {
2159 if (index
!= (ieDword
) -1) {
2160 Window
* w
= core
->GetWindow( (unsigned short) index
);
2162 core
->SetVisible( (unsigned short) index
, WINDOW_VISIBLE
);
2163 if (dict
->Lookup( WindowPosition
, index
)) {
2164 ResizeAdd( w
, index
);
2168 printMessage("GameControl", "Invalid Window Index ", LIGHT_RED
);
2169 printf("%s:%u\n",WindowName
, index
);
2174 //Reveal all windows on the GUI (including this one)
2175 int GameControl::UnhideGUI()
2177 if (ScreenFlags
&SF_GUIENABLED
) {
2181 ScreenFlags
|= SF_GUIENABLED
;
2182 // Unhide the gamecontrol window
2183 core
->SetVisible( 0, WINDOW_VISIBLE
);
2185 HandleWindowReveal("ActionsWindow", "ActionsPosition");
2186 HandleWindowReveal("MessageWindow", "MessagePosition");
2187 HandleWindowReveal("OptionsWindow", "OptionsPosition");
2188 HandleWindowReveal("TopWindow", "TopPosition");
2189 HandleWindowReveal("OtherWindow", "OtherPosition");
2190 HandleWindowReveal("PortraitWindow", "PortraitPosition");
2191 //the floatwindow is a special case
2192 Variables
* dict
= core
->GetDictionary();
2195 if (dict
->Lookup( "FloatWindow", index
)) {
2196 if (index
!= (ieDword
) -1) {
2197 Window
* fw
= core
->GetWindow( (unsigned short) index
);
2199 core
->SetVisible( (unsigned short) index
, WINDOW_VISIBLE
);
2200 fw
->Flags
|=WF_FLOAT
;
2201 core
->SetOnTop( index
);
2205 core
->GetVideoDriver()->SetViewport( Owner
->XPos
, Owner
->YPos
, Width
, Height
);
2209 //a window got removed, so the GameControl gets enlarged
2210 void GameControl::ResizeDel(Window
* win
, int type
)
2215 printMessage("GameControl","More than one left window!\n",LIGHT_RED
);
2219 Owner
->XPos
-= win
->Width
;
2220 Owner
->Width
+= win
->Width
;
2221 Width
= Owner
->Width
;
2226 if (BottomCount
!=1) {
2227 printMessage("GameControl","More than one bottom window!\n",LIGHT_RED
);
2231 Owner
->Height
+= win
->Height
;
2232 Height
= Owner
->Height
;
2237 if (RightCount
!=1) {
2238 printMessage("GameControl","More than one right window!\n",LIGHT_RED
);
2242 Owner
->Width
+= win
->Width
;
2243 Width
= Owner
->Width
;
2249 printMessage("GameControl","More than one top window!\n",LIGHT_RED
);
2253 Owner
->YPos
-= win
->Height
;
2254 Owner
->Height
+= win
->Height
;
2255 Height
= Owner
->Height
;
2259 case 4: //BottomAdded
2261 Owner
->Height
+= win
->Height
;
2262 Height
= Owner
->Height
;
2264 case 5: //Inactivating
2266 Owner
->Height
+= win
->Height
;
2267 Height
= Owner
->Height
;
2272 //a window got added, so the GameControl gets shrunk
2273 //Owner is the GameControl's window
2274 //GameControl is the only control on that window
2275 void GameControl::ResizeAdd(Window
* win
, int type
)
2280 if (LeftCount
== 1) {
2281 Owner
->XPos
+= win
->Width
;
2282 Owner
->Width
-= win
->Width
;
2283 Width
= Owner
->Width
;
2289 if (BottomCount
== 1) {
2290 Owner
->Height
-= win
->Height
;
2291 Height
= Owner
->Height
;
2297 if (RightCount
== 1) {
2298 Owner
->Width
-= win
->Width
;
2299 Width
= Owner
->Width
;
2305 if (TopCount
== 1) {
2306 Owner
->YPos
+= win
->Height
;
2307 Owner
->Height
-= win
->Height
;
2308 Height
= Owner
->Height
;
2312 case 4: //BottomAdded
2314 Owner
->Height
-= win
->Height
;
2315 Height
= Owner
->Height
;
2318 case 5: //Inactivating
2320 Owner
->Height
-= win
->Height
;
2325 //Try to start dialogue between two actors (one of them could be inanimate)
2326 int GameControl::InitDialog(Scriptable
* spk
, Scriptable
* tgt
, const char* dlgref
)
2333 PluginHolder
<DialogMgr
> dm(IE_DLG_CLASS_ID
);
2334 dm
->Open( gamedata
->GetResource( dlgref
, IE_DLG_CLASS_ID
), true );
2335 dlg
= dm
->GetDialog();
2338 printMessage("GameControl", " ", LIGHT_RED
);
2339 printf( "Cannot start dialog: %s\n", dlgref
);
2343 strnlwrcpy(dlg
->ResRef
, dlgref
, 8); //this isn't handled by GetDialog???
2345 //target is here because it could be changed when a dialog runs onto
2346 //and external link, we need to find the new target (whose dialog was
2349 Actor
*spe
= (Actor
*) spk
;
2350 speakerID
= spe
->globalID
;
2351 if (tgt
->Type
!=ST_ACTOR
) {
2353 //most likely this dangling object reference
2354 //won't cause problems, because trigger points don't
2355 //get destroyed during a dialog
2357 spk
->LastTalkedTo
=0;
2359 Actor
*tar
= (Actor
*) tgt
;
2360 speakerID
= spe
->globalID
;
2361 targetID
= tar
->globalID
;
2362 if (!originalTargetID
) originalTargetID
= tar
->globalID
;
2363 spe
->LastTalkedTo
=targetID
;
2364 tar
->LastTalkedTo
=speakerID
;
2367 //check if we are already in dialog
2368 if (DialogueFlags
&DF_IN_DIALOG
) {
2372 int si
= dlg
->FindFirstState( tgt
);
2377 //we need GUI for dialogs
2380 //no exploring while in dialogue
2381 ScreenFlags
|= SF_GUIENABLED
|SF_DISABLEMOUSE
|SF_LOCKSCROLL
;
2382 DialogueFlags
|= DF_IN_DIALOG
;
2384 if (tgt
->Type
==ST_ACTOR
) {
2385 Actor
*tar
= (Actor
*) tgt
;
2386 tar
->DialogInterrupt();
2389 //allow mouse selection from dialog (even though screen is locked)
2390 core
->GetVideoDriver()->SetMouseEnabled(true);
2391 core
->timer
->SetMoveViewPort( tgt
->Pos
.x
, tgt
->Pos
.y
, 0, true );
2392 //there are 3 bits, if they are all unset, the dialog freezes scripts
2393 if (!(dlg
->Flags
&7) ) {
2394 DialogueFlags
|= DF_FREEZE_SCRIPTS
;
2396 //opening control size to maximum, enabling dialog window
2397 core
->GetGame()->SetControlStatus(CS_HIDEGUI
, BM_NAND
);
2398 core
->GetGame()->SetControlStatus(CS_DIALOG
, BM_OR
);
2399 core
->SetEventFlag(EF_PORTRAIT
);
2403 /*try to break will only try to break it, false means unconditional stop*/
2404 void GameControl::EndDialog(bool try_to_break
)
2406 if (try_to_break
&& (DialogueFlags
&DF_UNBREAKABLE
) ) {
2410 Actor
*tmp
= GetSpeaker();
2415 if (targetID
==0xffff) {
2416 targetOB
->LeaveDialog();
2425 originalTargetID
= 0;
2431 //restoring original size
2432 core
->GetGame()->SetControlStatus(CS_DIALOG
, BM_NAND
);
2433 ScreenFlags
&=~(SF_DISABLEMOUSE
|SF_LOCKSCROLL
);
2435 core
->SetEventFlag(EF_PORTRAIT
);
2438 //translate section values (journal, solved, unsolved, user)
2439 static const int sectionMap
[4]={4,1,2,0};
2441 void GameControl::DialogChoose(unsigned int choose
)
2443 TextArea
* ta
= core
->GetMessageTextArea();
2445 printMessage("GameControl","Dialog aborted???",LIGHT_RED
);
2450 Actor
*speaker
= GetSpeaker();
2452 printMessage("GameControl","Speaker gone???",LIGHT_RED
);
2459 if (targetID
!=0xffff) {
2468 printMessage("GameControl","Target gone???",LIGHT_RED
);
2473 if (choose
== (unsigned int) -1) {
2474 //increasing talkcount after top level condition was determined
2476 int si
= dlg
->FindFirstState( tgt
);
2483 if (DialogueFlags
&DF_TALKCOUNT
) {
2484 DialogueFlags
&=~DF_TALKCOUNT
;
2486 } else if (DialogueFlags
&DF_INTERACT
) {
2487 DialogueFlags
&=~DF_INTERACT
;
2488 tgt
->InteractCount
++;
2491 ds
= dlg
->GetState( si
);
2493 if (ds
->transitionsCount
<= choose
) {
2497 DialogTransition
* tr
= ds
->transitions
[choose
];
2501 if (tr
->Flags
&IE_DLG_TR_JOURNAL
) {
2503 if (tr
->Flags
&IE_DLG_UNSOLVED
) {
2506 if (tr
->Flags
&IE_DLG_SOLVED
) {
2509 if (core
->GetGame()->AddJournalEntry(tr
->journalStrRef
, sectionMap
[Section
], tr
->Flags
>>16) ) {
2510 displaymsg
->DisplayConstantString(STR_JOURNALCHANGE
,0xffff00);
2511 char *string
= core
->GetString( tr
->journalStrRef
);
2512 //cutting off the strings at the first crlf
2513 char *poi
= strchr(string
,'\n');
2517 displaymsg
->DisplayString( string
);
2522 if (tr
->textStrRef
!= 0xffffffff) {
2523 //allow_zero is for PST (deionarra's text)
2524 displaymsg
->DisplayStringName( (int) (tr
->textStrRef
), 0x8080FF, speaker
, IE_STR_SOUND
|IE_STR_SPEECH
|IE_STR_ALLOW_ZERO
);
2525 if (core
->HasFeature( GF_DIALOGUE_SCROLLS
)) {
2526 ta
->AppendText( "", -1 );
2530 if (tr
->actions
.size()) {
2531 // does this belong here? we must clear actions somewhere before
2532 // we start executing them (otherwise queued actions interfere)
2533 // executing actions directly does not work, because dialog
2534 // needs to end before final actions are executed due to
2535 // actions making new dialogs!
2536 if (target
->Type
== ST_ACTOR
) ((Movable
*)target
)->ClearPath(); // fuzzie added this
2537 target
->ClearActions();
2539 for (unsigned int i
= 0; i
< tr
->actions
.size(); i
++) {
2540 target
->AddAction(tr
->actions
[i
]);
2541 //GameScript::ExecuteAction( target, action );
2545 int final_dialog
= tr
->Flags
& IE_DLG_TR_FINAL
;
2548 ta
->SetMinRow( false );
2552 // all dialog actions must be executed immediately
2553 target
->ProcessActions(true);
2554 // (do not clear actions - final actions can involve waiting/moving)
2560 //displaying dialog for selected option
2561 int si
= tr
->stateIndex
;
2562 //follow external linkage, if required
2563 if (tr
->Dialog
[0] && strnicmp( tr
->Dialog
, dlg
->ResRef
, 8 )) {
2564 //target should be recalculated!
2566 if (originalTargetID
) {
2567 // always try original target first (sometimes there are multiple
2568 // actors with the same dialog in an area, we want to pick the one
2569 // we were talking to)
2570 tgt
= GetActorByGlobalID(originalTargetID
);
2571 if (tgt
&& strnicmp( tgt
->GetDialog(GD_NORMAL
), tr
->Dialog
, 8 ) != 0) {
2576 // then just search the current area for an actor with the dialog
2577 tgt
= target
->GetCurrentArea()->GetActorByDialog(tr
->Dialog
);
2581 printMessage("Dialog","Can't redirect dialog\n",YELLOW
);
2582 ta
->SetMinRow( false );
2586 targetID
= tgt
->globalID
;
2587 // we have to make a backup, tr->Dialog is freed
2589 strnlwrcpy(tmpresref
,tr
->Dialog
, 8);
2590 if (target
->GetInternalFlag()&IF_NOINT
) {
2591 // this whole check moved out of InitDialog by fuzzie, see comments
2592 // for the IF_NOINT check in BeginDialog
2593 displaymsg
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
2594 ta
->SetMinRow( false );
2598 int ret
= InitDialog( speaker
, target
, tmpresref
);
2600 // error was displayed by InitDialog
2601 ta
->SetMinRow( false );
2606 ds
= dlg
->GetState( si
);
2608 printMessage("Dialog","Can't find next dialog\n",YELLOW
);
2609 ta
->SetMinRow( false );
2614 //displaying npc text
2615 displaymsg
->DisplayStringName( ds
->StrRef
, 0x70FF70, target
, IE_STR_SOUND
|IE_STR_SPEECH
);
2616 //adding a gap between options and npc text
2617 ta
->AppendText("",-1);
2620 ta
->SetMinRow( true );
2621 //first looking for a 'continue' opportunity, the order is descending (a la IE)
2622 unsigned int x
= ds
->transitionsCount
;
2624 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_FINAL
) {
2627 if (ds
->transitions
[x
]->textStrRef
!= 0xffffffff) {
2630 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_TRIGGER
) {
2631 if (ds
->transitions
[x
]->condition
&&
2632 !ds
->transitions
[x
]->condition
->Evaluate(target
)) {
2636 core
->GetDictionary()->SetAt("DialogOption",x
);
2637 DialogueFlags
|= DF_OPENCONTINUEWINDOW
;
2640 for (x
= 0; x
< ds
->transitionsCount
; x
++) {
2641 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_TRIGGER
) {
2642 if (ds
->transitions
[x
]->condition
&&
2643 !ds
->transitions
[x
]->condition
->Evaluate(target
)) {
2648 if (ds
->transitions
[x
]->textStrRef
== 0xffffffff) {
2649 //dialogchoose should be set to x
2650 //it isn't important which END option was chosen, as it ends
2651 core
->GetDictionary()->SetAt("DialogOption",x
);
2652 DialogueFlags
|= DF_OPENENDWINDOW
;
2654 char *string
= ( char * ) malloc( 40 );
2655 sprintf( string
, "[s=%d,ffffff,ff0000]%d - [p]", x
, idx
);
2656 i
= ta
->AppendText( string
, -1 );
2658 string
= core
->GetString( ds
->transitions
[x
]->textStrRef
);
2659 ta
->AppendText( string
, i
);
2661 ta
->AppendText( "[/p][/s]", i
);
2664 // this happens if a trigger isn't implemented or the dialog is wrong
2666 printMessage("Dialog", "There were no valid dialog options!\n", YELLOW
);
2667 DialogueFlags
|= DF_OPENENDWINDOW
;
2670 //padding the rows so our text will be at the top
2671 if (core
->HasFeature( GF_DIALOGUE_SCROLLS
)) {
2672 ta
->AppendText( "", -1 );
2679 //Create an overhead text over an arbitrary point
2680 void GameControl::DisplayString(const Point
&p
, const char *Text
)
2682 Scriptable
* scr
= new Scriptable( ST_TRIGGER
);
2683 scr
->overHeadText
= (char *) Text
;
2684 scr
->textDisplaying
= 1;
2685 scr
->timeStartDisplaying
= 0;
2687 scr
->ClearCutsceneID( );
2690 //Create an overhead text over a scriptable target
2691 //Multiple texts are possible, as this code copies the text to a new object
2692 void GameControl::DisplayString(Scriptable
* target
)
2694 Scriptable
* scr
= new Scriptable( ST_TRIGGER
);
2695 scr
->overHeadText
= strdup( target
->overHeadText
);
2696 /* strdup should work here, we use it elsewhere
2697 size_t len = strlen( target->overHeadText ) + 1;
2698 scr->overHeadText = ( char * ) malloc( len );
2699 strcpy( scr->overHeadText, target->overHeadText );
2701 scr
->textDisplaying
= 1;
2702 scr
->timeStartDisplaying
= target
->timeStartDisplaying
;
2703 scr
->Pos
= target
->Pos
;
2704 scr
->SetCutsceneID( target
);
2707 /** changes displayed map to the currently selected PC */
2708 void GameControl::ChangeMap(Actor
*pc
, bool forced
)
2710 //swap in the area of the actor
2711 Game
* game
= core
->GetGame();
2712 if (forced
|| (stricmp( pc
->Area
, game
->CurrentArea
) != 0) ) {
2714 overInfoPoint
= NULL
;
2715 overContainer
= NULL
;
2717 /*this is loadmap, because we need the index, not the pointer*/
2718 char *areaname
= game
->CurrentArea
;
2720 areaname
= pc
->Area
;
2722 game
->GetMap( areaname
, true );
2723 ScreenFlags
|=SF_CENTERONACTOR
;
2725 //center on first selected actor
2726 Region vp
= core
->GetVideoDriver()->GetViewport();
2727 if (ScreenFlags
&SF_CENTERONACTOR
) {
2728 core
->timer
->SetMoveViewPort( pc
->Pos
.x
, pc
->Pos
.y
, 0, true );
2729 ScreenFlags
&=~SF_CENTERONACTOR
;
2733 void GameControl::SetScreenFlags(int value
, int mode
)
2736 case BM_OR
: ScreenFlags
|=value
; break;
2737 case BM_NAND
: ScreenFlags
&=~value
; break;
2738 case BM_SET
: ScreenFlags
=value
; break;
2739 case BM_AND
: ScreenFlags
&=value
; break;
2740 case BM_XOR
: ScreenFlags
^=value
; break;
2744 void GameControl::SetDialogueFlags(int value
, int mode
)
2747 case BM_OR
: DialogueFlags
|=value
; break;
2748 case BM_NAND
: DialogueFlags
&=~value
; break;
2749 case BM_SET
: DialogueFlags
=value
; break;
2750 case BM_AND
: DialogueFlags
&=value
; break;
2751 case BM_XOR
: DialogueFlags
^=value
; break;
2755 //copies a screenshot into a sprite
2756 Sprite2D
* GameControl::GetScreenshot(bool show_gui
)
2758 Sprite2D
* screenshot
;
2760 screenshot
= core
->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) );
2762 int hf
= HideGUI ();
2764 screenshot
= core
->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) );
2768 core
->DrawWindows ();
2774 //copies a downscaled screenshot into a sprite for save game preview
2775 Sprite2D
* GameControl::GetPreview()
2777 // We get preview by first taking a screenshot of size 640x405,
2778 // centered in the display. This is to get a decent picture for
2779 // higher screen resolutions.
2780 // FIXME: how do orig games solve that?
2781 Video
* video
= core
->GetVideoDriver();
2782 int w
= video
->GetWidth();
2783 int h
= video
->GetHeight();
2784 int x
= (w
- 640) / 2;
2785 int y
= (h
- 405) / 2;
2802 int hf
= HideGUI ();
2803 signed char v
= Owner
->Visible
;
2804 Owner
->Visible
= WINDOW_VISIBLE
;
2807 Sprite2D
*screenshot
= video
->GetScreenshot( Region(x
, y
, w
, h
) );
2811 core
->DrawWindows();
2813 Sprite2D
* preview
= video
->SpriteScaleDown ( screenshot
, 5 );
2814 video
->FreeSprite( screenshot
);
2820 * Returns PC portrait for a currently running game
2822 Sprite2D
* GameControl::GetPortraitPreview(int pcslot
)
2824 /** Portrait shrink ratio */
2825 // FIXME: this is just a random PST specific trait
2826 // you can make it less random with a new feature bit
2827 int ratio
= (core
->HasFeature( GF_ONE_BYTE_ANIMID
)) ? 1 : 2;
2829 Video
*video
= core
->GetVideoDriver();
2831 Actor
*actor
= core
->GetGame()->GetPC( pcslot
, false );
2835 ResourceHolder
<ImageMgr
> im(actor
->GetPortrait(true));
2840 Sprite2D
* img
= im
->GetSprite2D();
2845 Sprite2D
* img_scaled
= video
->SpriteScaleDown( img
, ratio
);
2846 video
->FreeSprite( img
);
2851 Actor
*GameControl::GetActorByGlobalID(ieWord ID
)
2855 Game
* game
= core
->GetGame();
2859 Map
* area
= game
->GetCurrentArea( );
2863 area
->GetActorByGlobalID(ID
);
2866 Actor
*GameControl::GetLastActor()
2868 return GetActorByGlobalID(lastActorID
);
2871 Actor
*GameControl::GetTarget()
2873 return GetActorByGlobalID(targetID
);
2876 Actor
*GameControl::GetSpeaker()
2878 return GetActorByGlobalID(speakerID
);
2881 //Set up an item use which needs targeting
2882 //Slot is an inventory slot
2883 //header is the used item extended header
2885 //target type is a bunch of GetActor flags that alter valid targets
2886 //cnt is the number of different targets (usually 1)
2887 void GameControl::SetupItemUse(int slot
, int header
, Actor
*u
, int targettype
, int cnt
)
2892 spellIndex
= header
;
2893 //item use also uses the casting icon, this might be changed in some custom game?
2894 target_mode
= TARGET_MODE_CAST
;
2895 target_types
= targettype
;
2899 //Set up spell casting which needs targeting
2900 //type is the spell's type
2901 //level is the caster level
2902 //idx is the spell's number
2904 //target type is a bunch of GetActor flags that alter valid targets
2905 //cnt is the number of different targets (usually 1)
2906 void GameControl::SetupCasting(int type
, int level
, int idx
, Actor
*u
, int targettype
, int cnt
)
2912 target_mode
= TARGET_MODE_CAST
;
2913 target_types
= targettype
;
2917 //another method inherited from Control which has no use here
2918 bool GameControl::SetEvent(int /*eventType*/, EventHandler
/*handler*/)
2923 void GameControl::SetDisplayText(char *text
, unsigned int time
)
2926 core
->FreeString(DisplayText
);
2928 DisplayTextTime
= time
;
2932 void GameControl::SetDisplayText(ieStrRef text
, unsigned int time
)
2934 SetDisplayText(core
->GetString(displaymsg
->GetStringReference(text
), 0), time
);