1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "GameControl.h"
25 #include "DialogMgr.h"
31 #include "Interface.h"
33 #include "SaveGameIterator.h"
34 #include "ScriptEngine.h"
44 #define DEBUG_SHOW_INFOPOINTS 0x01
45 #define DEBUG_SHOW_CONTAINERS 0x02
46 #define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
47 #define DEBUG_SHOW_LIGHTMAP 0x08
49 static const Color cyan
= {
50 0x00, 0xff, 0xff, 0xff
52 static const Color red
= {
53 0xff, 0x00, 0x00, 0xff
55 static const Color magenta
= {
56 0xff, 0x00, 0xff, 0xff
58 static const Color green
= {
59 0x00, 0xff, 0x00, 0xff
62 static Color white = {
63 0xff, 0xff, 0xff, 0xff
66 static const Color black
= {
67 0x00, 0x00, 0x00, 0xff
69 static const Color blue
= {
70 0x00, 0x00, 0xff, 0x80
75 #define FORMATIONSIZE 10
76 typedef Point formation_type
[FORMATIONSIZE
];
77 ieDword formationcount
;
78 static formation_type
*formations
=NULL
;
79 static bool mqs
= false;
80 static ieResRef TestSpell
="SPWI207";
82 //If one of the actors has tracking on, the gamecontrol needs to display
83 //arrow markers on the edges to point at detected monsters
84 //tracterID is the tracker actor's global ID
85 //distance is the detection distance
86 void GameControl::SetTracker(Actor
*actor
, ieDword dist
)
88 trackerID
= actor
->GetID();
92 //Multiple Quick saves is an experimental GemRB feature.
93 //multiple quick saves are kept, their age is determined by the slot
94 //number. There is an algorithm which keeps about log2(n) slots alive.
95 //The algorithm is implemented in SaveGameIterator
96 void GameControl::MultipleQuickSaves(int arg
)
101 GameControl::GameControl(void)
106 //this is the default action, individual actors should have one too
107 //at this moment we use only this
108 //maybe we don't even need it
116 DrawSelectionRect
= false;
118 overContainer
= NULL
;
119 overInfoPoint
= NULL
;
122 lastCursor
= IE_CURSOR_NORMAL
;
130 target_mode
= TARGET_MODE_NONE
;
131 target_types
= GA_SELECT
|GA_NO_DEAD
|GA_NO_HIDDEN
;
133 core
->GetDictionary()->Lookup("Center",tmp
);
135 ScreenFlags
=SF_ALWAYSCENTER
|SF_CENTERONACTOR
;
138 ScreenFlags
= SF_CENTERONACTOR
;
147 originalTargetID
= 0;
154 //There could be a custom formation which is saved in the save game
155 //alternatively, all formations could be saved in some compatible way
156 //so it doesn't cause problems with the original engine
157 void GameControl::ReadFormations()
160 AutoTable
tab("formatio");
164 formations
= (formation_type
*) calloc(1,sizeof(formation_type
) );
167 formationcount
= tab
->GetRowCount();
168 formations
= (formation_type
*) calloc(formationcount
, sizeof(formation_type
));
169 for(i
=0; i
<formationcount
; i
++) {
170 for(j
=0;j
<FORMATIONSIZE
;j
++) {
171 short k
=(short) atoi(tab
->QueryField(i
,j
*2));
172 formations
[i
][j
].x
=k
;
173 k
=(short) atoi(tab
->QueryField(i
,j
*2+1));
174 formations
[i
][j
].y
=k
;
179 //returns a single point offset for a formation
180 //formation: the formation type
181 //pos: the actor's slot ID
182 Point
GameControl::GetFormationOffset(ieDword formation
, ieDword pos
)
184 if (formation
>=formationcount
) formation
= 0;
185 if (pos
>=FORMATIONSIZE
) pos
=FORMATIONSIZE
-1;
186 return formations
[formation
][pos
];
189 //Moves an actor to a new position, keeping the current formation
190 //WARNING: don't pass p as a reference because it gets modified
191 void GameControl::MoveToPointFormation(Actor
*actor
, unsigned int pos
, Point src
, Point p
)
193 Map
* map
= actor
->GetCurrentArea() ;
195 int formation
=core
->GetGame()->GetFormation();
196 if (pos
>=FORMATIONSIZE
) pos
=FORMATIONSIZE
-1;
200 double xdiff
= src
.x
- p
.x
;
201 double ydiff
= src
.y
- p
.y
;
209 angle
= atan(xdiff
/ydiff
);
210 if (ydiff
< 0) angle
+= M_PI
;
213 // calculate new coordinates by rotating formation around (0,0)
214 double newx
= -formations
[formation
][pos
].x
* cos(angle
) + formations
[formation
][pos
].y
* sin(angle
);
215 double newy
= formations
[formation
][pos
].x
* sin(angle
) + formations
[formation
][pos
].y
* cos(angle
);
219 if (p
.x
< 0) p
.x
= 8;
220 if (p
.y
< 0) p
.y
= 8;
221 if (p
.x
> map
->GetWidth()*16) p
.x
= map
->GetWidth()*16 - 8;
222 if (p
.y
> map
->GetHeight()*12) p
.y
= map
->GetHeight()*12 - 8;
224 if(map
->GetCursor(p
) == IE_CURSOR_BLOCKED
) {
225 //we can't get there --> adjust position
228 map
->AdjustPosition(p
);
232 CreateMovement(actor
, p
);
235 // generate an action to do the actual movement
236 // only PST supports RunToPoint
237 void GameControl::CreateMovement(Actor
*actor
, const Point
&p
)
241 Action
*action
= NULL
;
243 sprintf( Tmp
, "RunToPoint([%d.%d])", p
.x
, p
.y
);
244 action
= GenerateAction( Tmp
);
248 sprintf( Tmp
, "MoveToPoint([%d.%d])", p
.x
, p
.y
);
249 action
= GenerateAction( Tmp
);
252 actor
->AddAction( action
);
253 // force action so that we get target recticles immediately
254 actor
->ProcessActions(true);
257 GameControl::~GameControl(void)
259 //releasing the viewport of GameControl
260 core
->GetVideoDriver()->SetViewport( 0,0,0,0 );
269 core
->FreeString(DisplayText
);
273 //Autosave was triggered by the GUI
274 void GameControl::AutoSave()
276 core
->GetSaveGameIterator()->CreateSaveGame(0, false);
279 //QuickSave was triggered by the GUI
280 //mqs is the 'multiple quick saves' flag
281 void GameControl::QuickSave()
283 core
->GetSaveGameIterator()->CreateSaveGame(1, mqs
== 1);
286 // ArrowSprite cycles
300 static const int arrow_orientations
[16]={
301 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
302 -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1
305 //Draws arrow markers along the edge of the game window
306 //WARNING:don't use reference for point, because it is altered
307 void GameControl::DrawArrowMarker(const Region
&screen
, Point p
, const Region
&viewport
)
309 Video
* video
= core
->GetVideoDriver();
314 if (p
.x
<viewport
.x
) {
318 if (p
.y
<viewport
.y
) {
324 Sprite2D
*spr
= core
->GetScrollCursorSprite(0,0);
327 //tmp = core->ArrowSprites[0]->Width;
329 if (p
.x
>viewport
.x
+viewport
.w
-tmp
) {
330 p
.x
=viewport
.x
+viewport
.w
;//-tmp;
335 //tmp = core->ArrowSprites[0]->Height;
337 if (p
.y
>viewport
.y
+viewport
.h
-tmp
) {
338 p
.y
=viewport
.y
+viewport
.h
;//-tmp;
341 if (arrow_orientations
[draw
]>=0) {
342 video
->BlitGameSprite( core
->GetScrollCursorSprite(arrow_orientations
[draw
], 0), p
.x
+screen
.x
, p
.y
+screen
.y
, 0, black
, NULL
);
346 /** Draws the Control on the Output Display */
347 void GameControl::Draw(unsigned short x
, unsigned short y
)
349 bool update_scripts
= !(DialogueFlags
& DF_FREEZE_SCRIPTS
);
351 Game
* game
= core
->GetGame();
355 if (((short) Width
) <=0 || ((short) Height
) <= 0) {
359 if (Owner
->Visible
!=WINDOW_VISIBLE
) {
363 Video
* video
= core
->GetVideoDriver();
364 Region viewport
= video
->GetViewport();
365 if (moveX
|| moveY
) {
368 Point mapsize
= core
->GetGame()->GetCurrentArea()->TMap
->GetMapSize();
369 if ( viewport
.x
< 0 )//if we are at top of the map
371 else if ( (viewport
.x
+ viewport
.w
) >= mapsize
.x
) //if we are at the bottom
372 viewport
.x
= mapsize
.x
- viewport
.w
;
374 if ( viewport
.y
< 0 ) //if we are at the left of the map
376 else if ( (viewport
.y
+ viewport
.h
) >= mapsize
.y
) //if we are at the right
377 viewport
.y
= mapsize
.y
- viewport
.h
;
379 // override any existing viewport moves which may be in progress
380 core
->timer
->SetMoveViewPort( viewport
.x
, viewport
.y
, 0, false );
381 // move it directly ourselves, since we might be paused
382 video
->MoveViewportTo( viewport
.x
, viewport
.y
);
384 Region
screen( x
+ XPos
, y
+ YPos
, Width
, Height
);
385 Map
* area
= game
->GetCurrentArea( );
387 video
->DrawRect( screen
, blue
, true );
390 video
->DrawRect( screen
, black
, true );
395 for (idx
= 0; (i
= area
->TMap
->GetInfoPoint( idx
)); idx
++) {
396 i
->Highlight
= false;
397 if (overInfoPoint
== i
&& target_mode
) {
398 if (i
->VisibleTrap(0)) {
399 i
->outlineColor
= green
;
404 if (i
->VisibleTrap(DebugFlags
& DEBUG_SHOW_INFOPOINTS
)) {
405 i
->outlineColor
= red
; // traps
406 } else if (DebugFlags
& DEBUG_SHOW_INFOPOINTS
) {
407 i
->outlineColor
= blue
; // debug infopoints
415 for (idx
= 0; (d
= area
->TMap
->GetDoor( idx
)); idx
++) {
416 d
->Highlight
= false;
419 if (d
->VisibleTrap(0) || (d
->Flags
& DOOR_LOCKED
)) {
420 // only highlight targettable doors
421 d
->outlineColor
= green
;
425 } else if (!(d
->Flags
& DOOR_SECRET
)) {
426 // mouse over, not in target mode, no secret door
427 d
->outlineColor
= cyan
;
432 if (d
->VisibleTrap(0)) {
433 d
->outlineColor
= red
; // traps
434 } else if (d
->Flags
& DOOR_SECRET
) {
435 if (DebugFlags
& DEBUG_SHOW_DOORS
|| d
->Flags
& DOOR_FOUND
) {
436 d
->outlineColor
= magenta
; // found hidden door
438 // secret door is invisible
441 } else if (DebugFlags
& DEBUG_SHOW_DOORS
) {
442 d
->outlineColor
= cyan
; // debug doors
450 for (idx
= 0; (c
= area
->TMap
->GetContainer( idx
)); idx
++) {
451 c
->Highlight
= false;
452 if (overContainer
== c
&& target_mode
) {
453 if (c
->VisibleTrap(0) || (c
->Flags
& CONT_LOCKED
)) {
454 // only highlight targettable containers
455 c
->outlineColor
= green
;
459 } else if (overContainer
== c
) {
460 // mouse over, not in target mode
461 c
->outlineColor
= cyan
;
465 if (c
->VisibleTrap(0)) {
466 c
->outlineColor
= red
; // traps
467 } else if (DebugFlags
& DEBUG_SHOW_CONTAINERS
) {
468 c
->outlineColor
= cyan
; // debug containers
475 //drawmap should be here so it updates fog of war
476 area
->DrawMap( screen
);
477 game
->DrawWeather(screen
, update_scripts
);
480 Actor
*actor
= area
->GetActorByGlobalID(trackerID
);
483 Actor
**monsters
= area
->GetAllActorsInRadius(actor
->Pos
, GA_NO_DEAD
, distance
);
487 Actor
*target
= monsters
[i
++];
488 if (target
->InParty
) continue;
489 if (target
->GetStat(IE_NOTRACKING
)) continue;
490 DrawArrowMarker(screen
, target
->Pos
, viewport
);
498 if (ScreenFlags
& SF_DISABLEMOUSE
)
500 Point
p(lastMouseX
, lastMouseY
);
501 video
->ConvertToGame( p
.x
, p
.y
);
503 // Draw selection rect
504 if (DrawSelectionRect
) {
505 CalculateSelection( p
);
506 video
->DrawRect( SelectionRect
, green
, false, true );
510 if (DebugFlags
& DEBUG_SHOW_INFOPOINTS
) {
512 unsigned int count
= area
->GetWallCount();
513 for (unsigned int i
= 0; i
< count
; ++i
) {
514 Wall_Polygon
* poly
= area
->GetWallGroup(i
);
522 //if polygon is disabled, make it grey
523 if (poly
->wall_flag
&WF_DISABLED
) {
527 video
->DrawPolyline( poly
, c
, true );
533 PathNode
* node
= drawPath
;
535 Point
p( ( node
-> x
*16) + 8, ( node
->y
*12 ) + 6 );
537 video
->DrawCircle( p
.x
, p
.y
, 2, red
);
539 short oldX
= ( node
->Parent
-> x
*16) + 8, oldY
= ( node
->Parent
->y
*12 ) + 6;
540 video
->DrawLine( oldX
, oldY
, p
.x
, p
.y
, green
);
543 video
->DrawCircle( p
.x
, p
.y
, 2, green
);
551 if (DebugFlags
& DEBUG_SHOW_LIGHTMAP
) {
552 Sprite2D
* spr
= area
->LightMap
->GetSprite2D();
553 video
->BlitSprite( spr
, 0, 0, true );
554 video
->FreeSprite( spr
);
555 Region
point( p
.x
/ 16, p
.y
/ 12, 2, 2 );
556 video
->DrawRect( point
, red
);
559 if (core
->HasFeature(GF_ONSCREEN_TEXT
) && DisplayText
) {
560 core
->GetFont(1)->Print(screen
, (unsigned char *)DisplayText
, core
->InfoTextPalette
, IE_FONT_ALIGN_CENTER
| IE_FONT_ALIGN_MIDDLE
, true);
561 if (update_scripts
) {
562 // just replicating original engine behaviour
563 if (DisplayTextTime
== 0) {
564 SetDisplayText((char *)NULL
, 0);
572 /** inherited from Control, GameControl doesn't need it */
573 int GameControl::SetText(const char* /*string*/, int /*pos*/)
578 /** Key Press Event */
579 void GameControl::OnKeyPress(unsigned char Key
, unsigned short /*Mod*/)
581 if (DialogueFlags
&DF_IN_DIALOG
) {
585 Game
* game
= core
->GetGame();
590 game
->SelectActor( NULL
, false, SELECT_NORMAL
);
591 i
= game
->GetPartySize(false)/2;
593 SelectActor(i
, true);
597 game
->SelectActor( NULL
, true, SELECT_NORMAL
);
598 i
= game
->GetPartySize(false)/2;
600 SelectActor(i
, false);
615 SelectActor(Key
-'0');
618 core
->GetGame()->SetHotKey(toupper(Key
));
623 //Select (or deselect) a new actor (or actors)
624 void GameControl::SelectActor(int whom
, int type
)
626 Game
* game
= core
->GetGame();
628 game
->SelectActor( NULL
, true, SELECT_NORMAL
);
632 /* doesn't fall through here */
633 Actor
* actor
= game
->FindPC( whom
);
638 game
->SelectActor( actor
, false, SELECT_NORMAL
);
642 game
->SelectActor( actor
, true, SELECT_NORMAL
);
646 bool was_selected
= actor
->IsSelected();
647 if (game
->SelectActor( actor
, true, SELECT_REPLACE
))
648 if (was_selected
|| (ScreenFlags
& SF_ALWAYSCENTER
)) {
649 ScreenFlags
|= SF_CENTERONACTOR
;
653 //Effect for the ctrl-r cheatkey (resurrect)
654 static EffectRef heal_ref
={"CurrentHPModifier", NULL
, -1};
655 static EffectRef damage_ref
={"Damage", NULL
, -1};
657 /** Key Release Event */
658 void GameControl::OnKeyRelease(unsigned char Key
, unsigned short Mod
)
661 Game
* game
= core
->GetGame();
666 if (DialogueFlags
&DF_IN_DIALOG
) {
679 TextArea
*ta
= core
->GetMessageTextArea();
681 ta
->OnKeyPress(Key
,Mod
);
688 //cheatkeys with ctrl-
689 if (Mod
& GEM_MOD_CTRL
) {
690 if (!core
->CheatEnabled()) {
693 Map
* area
= game
->GetCurrentArea( );
696 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
697 Point
p(lastMouseX
, lastMouseY
);
698 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
700 case 'f': //toggle full screen mode
701 core
->GetVideoDriver()->ToggleFullscreenMode();
703 case 'd': //disarm a trap
705 overInfoPoint
->DetectTrap(256);
708 if (overContainer
->Trapped
&&
709 !( overContainer
->TrapDetected
)) {
710 overContainer
->TrapDetected
= 1;
714 if (overDoor
->Trapped
&&
715 !( overDoor
->TrapDetected
)) {
716 overDoor
->TrapDetected
= 1;
720 case 'l': //play an animation (vvc/bam) over an actor
721 //the original engine was able to swap through all animations
723 lastActor
->AddAnimation("S056ICBL", 0, 0, 0);
727 case 'c': //force cast a hardcoded spell
728 //caster is the last selected actor
729 //target is the door/actor currently under the pointer
730 if (game
->selected
.size() > 0) {
731 Actor
*src
= game
->selected
[0];
732 Scriptable
*target
= lastActor
;
737 src
->CastSpell( TestSpell
, target
, false );
738 if (src
->LastTarget
) {
739 src
->CastSpellEnd( TestSpell
);
741 src
->CastSpellPointEnd( TestSpell
);
747 case 'b': //draw a path to the target (pathfinder debug)
748 //You need to select an origin with ctrl-o first
750 PathNode
* nextNode
= drawPath
->Next
;
751 PathNode
* thisNode
= drawPath
;
757 nextNode
= thisNode
->Next
;
760 drawPath
= core
->GetGame()->GetCurrentArea()->FindPath( pfs
, p
, lastActor
?lastActor
->size
:1 );
764 case 'o': //set up the origin for the pathfinder
768 core
->GetVideoDriver()->ConvertToGame( pfs
.x
, pfs
.y
);
770 case 'a': //switches through the avatar animations
772 lastActor
->GetNextAnimation();
775 case 's': //switches through the stance animations
777 lastActor
->GetNextStance();
780 case 'j': //teleports the selected actors
781 for (i
= 0; i
< game
->selected
.size(); i
++) {
782 Actor
* actor
= game
->selected
[i
];
783 MoveBetweenAreasCore(actor
, core
->GetGame()->CurrentArea
, p
, -1, true);
784 printf( "Teleported to %d, %d\n", p
.x
, p
.y
);
788 case 'm': //prints a debug dump (ctrl-m in the original game too)
790 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
793 // ValidTarget never returns immobile targets, making debugging a nightmare
794 // so if we don't have an actor, we make really really sure by checking manually
795 unsigned int count
= area
->GetActorCount(true);
797 Actor
*actor
= area
->GetActor(count
, true);
798 if (actor
->IsOver(p
)) {
804 lastActor
->DebugDump();
808 overDoor
->DebugDump();
812 overContainer
->DebugDump();
816 overInfoPoint
->DebugDump();
819 core
->GetGame()->GetCurrentArea()->DebugDump(Mod
& GEM_MOD_SHIFT
);
821 case 'v': //marks some of the map visited (random vision distance)
822 area
->ExploreMapChunk( p
, rand()%30, 1 );
824 case 'x': // shows coordinates on the map
825 printf( "%s [%d.%d]\n", area
->GetScriptName(), p
.x
, p
.y
);
827 case 'g'://shows loaded areas and other game information
830 case 'i'://interact trigger (from the original game)
832 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
834 if (lastActor
&& !(lastActor
->GetStat(IE_MC_FLAGS
)&MC_EXPORTABLE
)) {
836 int i
= game
->GetPartySize(true);
841 target
= game
->GetPC(i
, true);
842 if(target
==lastActor
) continue;
843 if(target
->GetStat(IE_MC_FLAGS
)&MC_EXPORTABLE
) continue;
846 snprintf(Tmp
,sizeof(Tmp
),"Interact(\"%s\")",target
->GetScriptName() );
847 lastActor
->AddAction(GenerateAction(Tmp
));
853 case 'r'://resurrects actor
855 lastActor
= area
->GetActor( p
, GA_DEFAULT
);
858 Effect
*fx
= EffectQueue::CreateEffect(heal_ref
, lastActor
->GetBase(IE_MAXHITPOINTS
), 0x30001, FX_DURATION_INSTANT_PERMANENT
);
860 core
->ApplyEffect(fx
, lastActor
, lastActor
);
864 case 't'://advances time
865 // 7200 (one day) /24 (hours) == 300
866 game
->AdvanceTime(300*AI_UPDATE_TIME
);
867 //refresh gui here once we got it
870 case 'q': //joins actor to the party
871 if (lastActor
&& !lastActor
->InParty
) {
872 lastActor
->ClearActions();
873 lastActor
->ClearPath();
875 strncpy(Tmp
,"JoinParty()",sizeof(Tmp
) );
876 lastActor
->AddAction( GenerateAction(Tmp
) );
879 case 'p': //center on actor
880 ScreenFlags
|=SF_CENTERONACTOR
;
881 ScreenFlags
^=SF_ALWAYSCENTER
;
883 case 'k': //kicks out actor
884 if (lastActor
&& lastActor
->InParty
) {
885 lastActor
->ClearActions();
886 lastActor
->ClearPath();
888 strncpy(Tmp
,"LeaveParty()",sizeof(Tmp
) );
889 lastActor
->AddAction( GenerateAction(Tmp
) );
892 case 'y': //kills actor or all enemies
893 if (Mod
& GEM_MOD_SHIFT
) {
896 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_MAGIC
<<16, FX_DURATION_INSTANT_PERMANENT
);
898 for (int i
= area
->GetActorCount(0)-1; i
>= 0; i
--) {
899 victim
= area
->GetActor(i
, 0);
900 if (victim
->Modified
[IE_EA
] == EA_ENEMY
) {
901 core
->ApplyEffect(newfx
, victim
, victim
);
907 //using action so the actor is killed
908 //correctly (synchronisation)
909 lastActor
->ClearActions();
910 lastActor
->ClearPath();
913 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_MAGIC
<<16, FX_DURATION_INSTANT_PERMANENT
);
914 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
915 if (! (lastActor
->GetInternalFlag() & IF_REALLYDIED
)) {
916 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_ACID
<<16, FX_DURATION_INSTANT_PERMANENT
);
917 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
918 newfx
= EffectQueue::CreateEffect(damage_ref
, 300, DAMAGE_CRUSHING
<<16, FX_DURATION_INSTANT_PERMANENT
);
919 core
->ApplyEffect(newfx
, lastActor
, lastActor
);
922 } else if (overContainer
) {
923 overContainer
->SetContainerLocked(0);
924 } else if (overDoor
) {
925 overDoor
->SetDoorLocked(0,0);
929 case 'z': //shift through the avatar animations backward
931 lastActor
->GetPrevAnimation();
934 case '1': //change paperdoll armour level
937 lastActor
->NewStat(IE_ARMOR_TYPE
,1,MOD_ADDITIVE
);
939 case '4': //show all traps and infopoints
940 DebugFlags
^= DEBUG_SHOW_INFOPOINTS
;
941 printf("Show traps and infopoints %s\n", DebugFlags
& DEBUG_SHOW_INFOPOINTS
? "ON" : "OFF");
943 case '6': //show the lightmap
944 DebugFlags
^= DEBUG_SHOW_LIGHTMAP
;
945 printf("Show lightmap %s\n", DebugFlags
& DEBUG_SHOW_LIGHTMAP
? "ON" : "OFF");
947 case '7': //toggles fog of war
949 printf("Show Fog-Of-War: %s\n", core
->FogOfWar
& 1 ? "ON" : "OFF");
951 case '8': //show searchmap over area
953 printf("Show searchmap %s\n", core
->FogOfWar
& 2 ? "ON" : "OFF");
956 printf( "KeyRelease:%d - %d\n", Key
, Mod
);
959 return; //return from cheatkeys
962 case 'h': //hard pause
963 if (DialogueFlags
& DF_FREEZE_SCRIPTS
) break;
965 case ' ': //soft pause
966 DialogueFlags
^= DF_FREEZE_SCRIPTS
;
967 if (DialogueFlags
&DF_FREEZE_SCRIPTS
) {
968 core
->DisplayConstantString(STR_PAUSED
,0xff0000);
969 SetDisplayText(STR_PAUSED
, 0); // time 0 = removed instantly on unpause
971 core
->DisplayConstantString(STR_UNPAUSED
,0xff0000);
975 core
->GetGUIScriptEngine()->RunFunction("OpenMapWindow");
978 core
->GetGUIScriptEngine()->RunFunction("OpenJournalWindow");
981 core
->GetGUIScriptEngine()->RunFunction("OpenInventoryWindow");
984 core
->GetGUIScriptEngine()->RunFunction("OpenRecordsWindow");
986 case 'q': //quicksave
989 case GEM_ALT
: //alt key (shows containers)
990 DebugFlags
&= ~DEBUG_SHOW_CONTAINERS
;
997 void GameControl::DisplayTooltip() {
998 Game
* game
= core
->GetGame();
1000 Map
* area
= game
->GetCurrentArea( );
1002 Actor
*actor
= area
->GetActorByGlobalID(lastActorID
);
1003 if (actor
&& (actor
->GetStat(IE_STATE_ID
)&STATE_DEAD
|| actor
->GetInternalFlag()&IF_JUSTDIED
)) {
1004 // checking IF_JUSTDIED is kind of horrid, but seems necessary
1005 // no tooltips for dead actors!
1006 actor
->SetOver( false );
1012 char *name
= actor
->GetName(-1);
1013 int hp
= actor
->GetStat(IE_HITPOINTS
);
1014 int maxhp
= actor
->GetStat(IE_MAXHITPOINTS
);
1017 if (!core
->TooltipBack
) {
1018 // single-line tooltips without background (PS:T)
1019 if (actor
->InParty
) {
1020 snprintf(buffer
, 100, "%s: %d/%d", name
, hp
, maxhp
);
1022 snprintf(buffer
, 100, "%s", name
);
1025 // a guess at a neutral check
1026 bool neutral
= actor
->GetStat(IE_EA
) == EA_NEUTRAL
;
1027 // test for an injured string being present for this game
1028 int strindex
= core
->GetStringReference(STR_UNINJURED
);
1030 if (actor
->InParty
) {
1031 // in party: display hp
1032 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1033 } else if (neutral
) {
1034 // neutral: display name only
1035 snprintf(buffer
, 100, "%s", name
);
1036 } else if (strindex
== -1) {
1037 // non-neutral, not in party, no injured strings: display hp
1038 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1040 // non-neutral, not in party: display injured string
1042 char *injuredstring
= NULL
;
1043 // these boundaries are just a guess
1045 strindex
= STR_UNINJURED
;
1046 } else if (hp
> (maxhp
*3)/4) {
1047 strindex
= STR_INJURED1
;
1048 } else if (hp
> maxhp
/2) {
1049 strindex
= STR_INJURED2
;
1050 } else if (hp
> maxhp
/3) {
1051 strindex
= STR_INJURED3
;
1053 strindex
= STR_INJURED4
;
1055 strindex
= core
->GetStringReference(strindex
);
1056 if (strindex
!= -1) {
1057 injuredstring
= core
->GetString(strindex
, 0);
1060 if (!injuredstring
) {
1061 // eek, where did the string go?
1062 snprintf(buffer
, 100, "%s\n%d/%d", name
, hp
, maxhp
);
1064 snprintf(buffer
, 100, "%s\n%s", name
, injuredstring
);
1065 free(injuredstring
);
1070 Point p
= actor
->Pos
;
1071 core
->GetVideoDriver()->ConvertToScreen( p
.x
, p
.y
);
1072 p
.x
+= Owner
->XPos
+ XPos
;
1073 p
.y
+= Owner
->YPos
+ YPos
;
1075 // hack to position text above PS:T actors
1076 if (!core
->TooltipBack
) p
.y
-= actor
->size
*50;
1078 // we should probably cope better with moving actors
1080 core
->DisplayTooltip(p
.x
, p
.y
, this);
1087 core
->DisplayTooltip(0, 0, NULL
);
1091 //returns the appropriate cursor over an active region (trap, infopoint, travel region)
1092 int GameControl::GetCursorOverInfoPoint(InfoPoint
*overInfoPoint
)
1094 if (target_mode
== TARGET_MODE_PICK
) {
1095 if (overInfoPoint
->VisibleTrap(0)) {
1096 return IE_CURSOR_TRAP
;
1099 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1101 // traps always display a walk cursor?
1102 if (overInfoPoint
->Type
== ST_PROXIMITY
) {
1103 return IE_CURSOR_WALK
;
1105 return overInfoPoint
->Cursor
;
1108 //returns the appropriate cursor over a door
1109 int GameControl::GetCursorOverDoor(Door
*overDoor
)
1111 if (target_mode
== TARGET_MODE_PICK
) {
1112 if (overDoor
->VisibleTrap(0)) {
1113 return IE_CURSOR_TRAP
;
1115 if (overDoor
->Flags
& DOOR_LOCKED
) {
1116 return IE_CURSOR_LOCK
;
1119 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1121 return overDoor
->Cursor
;
1124 //returns the appropriate cursor over a container (or pile)
1125 int GameControl::GetCursorOverContainer(Container
*overContainer
)
1127 if (target_mode
== TARGET_MODE_PICK
) {
1128 if (overContainer
->VisibleTrap(0)) {
1129 return IE_CURSOR_TRAP
;
1131 if (overContainer
->Flags
& CONT_LOCKED
) {
1132 return IE_CURSOR_LOCK2
;
1135 return IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1137 return IE_CURSOR_TAKE
;
1140 /** Mouse Over Event */
1141 void GameControl::OnMouseOver(unsigned short x
, unsigned short y
)
1143 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1150 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
1151 if (MouseIsDown
&& ( !DrawSelectionRect
)) {
1152 if (( abs( p
.x
- StartX
) > 5 ) || ( abs( p
.y
- StartY
) > 5 )) {
1153 DrawSelectionRect
= true;
1156 Game
* game
= core
->GetGame();
1158 Map
* area
= game
->GetCurrentArea( );
1160 int nextCursor
= area
->GetCursor( p
);
1161 //make the invisible area really invisible
1162 if (nextCursor
== IE_CURSOR_INVALID
) {
1163 Owner
->Cursor
= IE_CURSOR_BLOCKED
;
1164 lastCursor
= IE_CURSOR_BLOCKED
;
1168 overInfoPoint
= area
->TMap
->GetInfoPoint( p
, true );
1169 if (overInfoPoint
) {
1170 //nextCursor = overInfoPoint->Cursor;
1171 nextCursor
= GetCursorOverInfoPoint(overInfoPoint
);
1175 overDoor
->Highlight
= false;
1177 if (overContainer
) {
1178 overContainer
->Highlight
= false;
1180 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
1182 lastActor
->SetOver( false );
1185 overDoor
= area
->TMap
->GetDoor( p
);
1186 overContainer
= area
->TMap
->GetContainer( p
);
1188 if (!DrawSelectionRect
) {
1190 nextCursor
= GetCursorOverDoor(overDoor
);
1193 if (overContainer
) {
1194 nextCursor
= GetCursorOverContainer(overContainer
);
1197 Actor
*prevActor
= lastActor
;
1198 lastActor
= area
->GetActor( p
, target_types
);
1199 if (lastActor
!= prevActor
) {
1200 // we store prevActor so we can remove the tooltip on actor change
1201 // (maybe we should be checking this and actor movements every frame?)
1203 core
->DisplayTooltip(0, 0, this);
1206 if ((target_types
& GA_NO_SELF
) && lastActor
) {
1207 if (lastActor
== core
->GetFirstSelectedPC(false)) {
1213 lastActorID
= lastActor
->globalID
;
1214 lastActor
->SetOver( true );
1215 ieDword type
= lastActor
->GetStat(IE_EA
);
1216 if (type
>= EA_EVILCUTOFF
|| type
== EA_GOODBUTRED
) {
1217 nextCursor
= IE_CURSOR_ATTACK
;
1218 } else if ( type
> EA_CHARMED
) {
1219 nextCursor
= IE_CURSOR_TALK
;
1221 nextCursor
= IE_CURSOR_NORMAL
;
1227 if (target_mode
== TARGET_MODE_TALK
) {
1228 nextCursor
= IE_CURSOR_TALK
;
1230 nextCursor
|= IE_CURSOR_GRAY
;
1232 } else if (target_mode
== TARGET_MODE_ATTACK
) {
1233 nextCursor
= IE_CURSOR_ATTACK
;
1234 if (!lastActor
&& !overDoor
&& !overContainer
) {
1235 nextCursor
|= IE_CURSOR_GRAY
;
1237 } else if (target_mode
== TARGET_MODE_CAST
) {
1238 nextCursor
= IE_CURSOR_CAST
;
1239 //point is always valid
1240 if (!(target_types
& GA_POINT
)) {
1242 nextCursor
|= IE_CURSOR_GRAY
;
1245 } else if (target_mode
== TARGET_MODE_DEFEND
) {
1246 nextCursor
= IE_CURSOR_DEFEND
;
1248 nextCursor
|= IE_CURSOR_GRAY
;
1250 } else if (target_mode
== TARGET_MODE_PICK
) {
1252 nextCursor
= IE_CURSOR_PICK
;
1254 if (!overContainer
&& !overDoor
&& !overInfoPoint
) {
1255 nextCursor
= IE_CURSOR_STEALTH
|IE_CURSOR_GRAY
;
1262 switch (lastActor
->GetStat(IE_EA
)) {
1272 case EA_EVILBUTGREEN
:
1273 if (target_types
& GA_NO_ENEMY
)
1279 if (target_types
& GA_NO_ALLY
)
1283 if (!(target_types
& GA_NO_NEUTRAL
))
1290 if (lastCursor
!= nextCursor
) {
1291 Owner
->Cursor
= nextCursor
;
1292 lastCursor
= (unsigned char) nextCursor
;
1296 #define SCROLL_BORDER 5
1298 /** Global Mouse Move Event */
1299 void GameControl::OnGlobalMouseMove(unsigned short x
, unsigned short y
)
1301 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1305 if (Owner
->Visible
!=WINDOW_VISIBLE
) {
1309 int mousescrollspd
= core
->GetMouseScrollSpeed();
1311 if (x
<= SCROLL_BORDER
)
1312 moveX
= -mousescrollspd
;
1314 if (x
>= ( core
->Width
- SCROLL_BORDER
))
1315 moveX
= mousescrollspd
;
1319 if (y
<= SCROLL_BORDER
)
1320 moveY
= -mousescrollspd
;
1322 if (y
>= ( core
->Height
- SCROLL_BORDER
))
1323 moveY
= mousescrollspd
;
1328 if (moveX
!= 0 || moveY
!= 0) {
1330 } else if (scrolling
) {
1333 Video
* video
= core
->GetVideoDriver();
1334 video
->SetDragCursor(NULL
);
1338 void GameControl::UpdateScrolling() {
1339 if (!scrolling
) return;
1341 int mousescrollspd
= core
->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-?
1342 Video
* video
= core
->GetVideoDriver();
1344 if (moveX
== mousescrollspd
&& moveY
== 0) { // right
1345 video
->SetDragCursor(core
->GetScrollCursorSprite(0,numScrollCursor
));
1346 } else if (moveX
== mousescrollspd
&& moveY
== -mousescrollspd
) { // upper right
1347 video
->SetDragCursor(core
->GetScrollCursorSprite(1,numScrollCursor
));
1348 } else if (moveX
== 0 && moveY
== -mousescrollspd
) { // up
1349 video
->SetDragCursor(core
->GetScrollCursorSprite(2,numScrollCursor
));
1350 } else if (moveX
== -mousescrollspd
&& moveY
== -mousescrollspd
) { // upper left
1351 video
->SetDragCursor(core
->GetScrollCursorSprite(3,numScrollCursor
));
1352 } else if (moveX
== -mousescrollspd
&& moveY
== 0) { // left
1353 video
->SetDragCursor(core
->GetScrollCursorSprite(4,numScrollCursor
));
1354 } else if (moveX
== -mousescrollspd
&& moveY
== mousescrollspd
) { // bottom left
1355 video
->SetDragCursor(core
->GetScrollCursorSprite(5,numScrollCursor
));
1356 } else if (moveX
== 0 && moveY
== mousescrollspd
) { // bottom
1357 video
->SetDragCursor(core
->GetScrollCursorSprite(6,numScrollCursor
));
1358 } else if (moveX
== mousescrollspd
&& moveY
== mousescrollspd
) { // bottom right
1359 video
->SetDragCursor(core
->GetScrollCursorSprite(7,numScrollCursor
));
1362 numScrollCursor
= (numScrollCursor
+1) % 15;
1365 //generate action code for source actor to try to attack a target
1366 void GameControl::TryToAttack(Actor
*source
, Actor
*tgt
)
1370 source
->ClearPath();
1371 source
->ClearActions();
1372 strncpy(Tmp
,"NIDSpecial3()",sizeof(Tmp
) );
1373 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1376 //generate action code for source actor to try to defend a target
1377 void GameControl::TryToDefend(Actor
*source
, Actor
*tgt
)
1381 source
->ClearPath();
1382 source
->ClearActions();
1383 strncpy(Tmp
,"NIDSpecial4()",sizeof(Tmp
) );
1384 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1387 //generate action code for source actor to try to pick pockets of a target
1388 //The -1 flag is a placeholder for dynamic target IDs
1389 void GameControl::TryToPick(Actor
*source
, Actor
*tgt
)
1393 source
->ClearPath();
1394 source
->ClearActions();
1395 strncpy(Tmp
,"PickPockets([-1])", sizeof(Tmp
) );
1396 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1399 //generate action code for source actor to try to pick a lock/disable trap on a door
1400 void GameControl::TryToPick(Actor
*source
, Door
*tgt
)
1404 source
->ClearPath();
1405 source
->ClearActions();
1406 if (tgt
->Trapped
&& tgt
->TrapDetected
) {
1407 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1409 snprintf(Tmp
, sizeof(Tmp
), "PickLock(\"%s\")", tgt
->GetScriptName() );
1411 source
->AddAction( GenerateAction( Tmp
) );
1414 //generate action code for source actor to try to pick a lock/disable trap on a container
1415 void GameControl::TryToPick(Actor
*source
, Container
*tgt
)
1419 source
->ClearPath();
1420 source
->ClearActions();
1421 if (tgt
->Trapped
&& tgt
->TrapDetected
) {
1422 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1424 snprintf(Tmp
, sizeof(Tmp
), "PickLock(\"%s\")", tgt
->GetScriptName() );
1426 source
->AddAction( GenerateAction( Tmp
) );
1429 //generate action code for source actor to try to disable trap (only trap type active regions)
1430 void GameControl::TryToDisarm(Actor
*source
, InfoPoint
*tgt
)
1432 if (tgt
->Type
!=ST_PROXIMITY
) return;
1436 source
->ClearPath();
1437 source
->ClearActions();
1438 snprintf(Tmp
, sizeof(Tmp
), "RemoveTraps(\"%s\")", tgt
->GetScriptName() );
1439 source
->AddAction( GenerateAction( Tmp
) );
1442 //generate action code for source actor to try to force open lock on a door/container
1443 void GameControl::TryToBash(Actor
*source
, Scriptable
*tgt
)
1447 source
->ClearPath();
1448 source
->ClearActions();
1449 snprintf(Tmp
, sizeof(Tmp
), "Attack(\"%s\")", tgt
->GetScriptName() );
1450 source
->AddAction( GenerateAction( Tmp
) );
1453 //generate action code for source actor to use item/cast spell on a point
1454 void GameControl::TryToCast(Actor
*source
, const Point
&tgt
)
1459 target_mode
= TARGET_MODE_NONE
;
1460 return; //not casting or using an own item
1462 source
->ClearPath();
1463 source
->ClearActions();
1466 if (spellOrItem
>=0) {
1467 sprintf(Tmp
, "NIDSpecial8()");
1469 //using item on target
1470 sprintf(Tmp
, "NIDSpecial7()");
1472 Action
* action
= GenerateAction( Tmp
);
1473 action
->pointParameter
=tgt
;
1476 CREMemorizedSpell
*si
;
1477 //spell casting at target
1478 si
= source
->spellbook
.GetMemorizedSpell(spellOrItem
, spellSlot
, spellIndex
);
1481 target_mode
= TARGET_MODE_NONE
;
1484 sprintf(action
->string0Parameter
,"%.8s",si
->SpellResRef
);
1488 action
->int0Parameter
=spellSlot
;
1489 action
->int1Parameter
=spellIndex
;
1491 source
->AddAction( action
);
1493 target_mode
= TARGET_MODE_NONE
;
1497 //generate action code for source actor to use item/cast spell on another actor
1498 void GameControl::TryToCast(Actor
*source
, Actor
*tgt
)
1503 target_mode
= TARGET_MODE_NONE
;
1504 return; //not casting or using an own item
1506 source
->ClearPath();
1507 source
->ClearActions();
1510 if (spellOrItem
>=0) {
1511 sprintf(Tmp
, "NIDSpecial6()");
1513 //using item on target
1514 sprintf(Tmp
, "NIDSpecial5()");
1516 Action
* action
= GenerateActionDirect( Tmp
, tgt
);
1519 CREMemorizedSpell
*si
;
1520 //spell casting at target
1521 si
= source
->spellbook
.GetMemorizedSpell(spellOrItem
, spellSlot
, spellIndex
);
1524 target_mode
= TARGET_MODE_NONE
;
1527 sprintf(action
->string0Parameter
,"%.8s",si
->SpellResRef
);
1531 action
->int0Parameter
=spellSlot
;
1532 action
->int1Parameter
=spellIndex
;
1534 source
->AddAction( action
);
1536 target_mode
= TARGET_MODE_NONE
;
1540 //generate action code for source actor to use talk to target actor
1541 void GameControl::TryToTalk(Actor
*source
, Actor
*tgt
)
1545 //Nidspecial1 is just an unused action existing in all games
1546 //(non interactive demo)
1547 //i found no fitting action which would emulate this kind of
1549 source
->ClearPath();
1550 source
->ClearActions();
1551 strncpy(Tmp
,"NIDSpecial1()",sizeof(Tmp
) );
1552 targetID
= tgt
->globalID
; //this is a hack, but not so deadly
1553 source
->AddAction( GenerateActionDirect( Tmp
, tgt
) );
1556 //generate action code for actor appropriate for the target mode when the target is a container
1557 void GameControl::HandleContainer(Container
*container
, Actor
*actor
)
1561 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1562 //we'll get the container back from the coordinates
1563 TryToCast(actor
, container
->Pos
);
1564 //Do not reset target_mode, TryToCast does it for us!!
1568 if (target_mode
== TARGET_MODE_ATTACK
) {
1569 TryToBash(actor
, container
);
1570 target_mode
= TARGET_MODE_NONE
;
1574 if ((target_mode
== TARGET_MODE_PICK
)) {
1575 TryToPick(actor
, container
);
1576 target_mode
= TARGET_MODE_NONE
;
1581 actor
->ClearActions();
1582 strncpy(Tmp
,"UseContainer()",sizeof(Tmp
) );
1583 core
->SetCurrentContainer( actor
, container
);
1584 actor
->AddAction( GenerateAction( Tmp
) );
1587 //generate action code for actor appropriate for the target mode when the target is a door
1588 void GameControl::HandleDoor(Door
*door
, Actor
*actor
)
1592 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1593 //we'll get the door back from the coordinates
1594 Point
*p
= door
->toOpen
;
1595 Point
*otherp
= door
->toOpen
+1;
1596 if (Distance(*p
,actor
)>Distance(*otherp
,actor
)) {
1599 TryToCast(actor
, *p
);
1603 if (target_mode
== TARGET_MODE_ATTACK
) {
1604 TryToBash(actor
, door
);
1605 target_mode
= TARGET_MODE_NONE
;
1609 if ( (target_mode
== TARGET_MODE_PICK
) || door
->TrapDetected
) {
1610 TryToPick(actor
, door
);
1611 target_mode
= TARGET_MODE_NONE
;
1616 actor
->ClearActions();
1617 // it really isn't very nice to store a pointer in the actor like this
1618 actor
->TargetDoor
= door
;
1619 // internal gemrb toggle door action hack - should we use UseDoor instead?
1620 sprintf( Tmp
, "NIDSpecial9()" );
1621 actor
->AddAction( GenerateAction( Tmp
) );
1624 //generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel)
1625 bool GameControl::HandleActiveRegion(InfoPoint
*trap
, Actor
* actor
, Point
&p
)
1627 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1628 //we'll get the active region from the coordinates (if needed)
1629 TryToCast(actor
, p
);
1630 //don't bother with this region further
1633 if ((target_mode
== TARGET_MODE_PICK
)) {
1634 TryToDisarm(actor
, trap
);
1635 target_mode
= TARGET_MODE_NONE
;
1639 switch(trap
->Type
) {
1641 actor
->UseExit(true);
1644 //the importer shouldn't load the script
1645 //if it is unallowed anyway (though
1646 //deactivated scripts could be reactivated)
1647 //only the 'trapped' flag should be honoured
1648 //there. Here we have to check on the
1649 //reset trap and deactivated flags
1650 if (trap
->Scripts
[0]) {
1651 if (!(trap
->Flags
&TRAP_DEACTIVATED
) ) {
1652 trap
->LastTriggerObject
= trap
->LastTrigger
= actor
->GetID();
1653 trap
->ImmediateEvent();
1654 //directly feeding the event, even if there are actions in the queue
1655 trap
->Scripts
[0]->Update();
1656 trap
->ProcessActions(true);
1657 //if reset trap flag not set, deactivate it
1658 //hmm, better not, info triggers don't deactivate themselves on click
1659 //if (!(trap->Flags&TRAP_RESET)) {
1660 // trap->Flags|=TRAP_DEACTIVATED;
1664 if (trap
->overHeadText
) {
1665 if (trap
->textDisplaying
!= 1) {
1666 trap
->textDisplaying
= 1;
1667 trap
->timeStartDisplaying
= core
->GetGame()->Ticks
;
1668 DisplayString( trap
);
1672 if (trap
->Flags
&TRAP_USEPOINT
) {
1673 //overriding the target point
1682 /** Mouse Button Down */
1683 void GameControl::OnMouseDown(unsigned short x
, unsigned short y
, unsigned short Button
,
1684 unsigned short /*Mod*/)
1686 if (ScreenFlags
&SF_DISABLEMOUSE
)
1691 DoubleClick
= false;
1695 OnSpecialKeyPress(GEM_UP
);
1697 case GEM_MB_SCRLDOWN
:
1698 OnSpecialKeyPress(GEM_DOWN
);
1700 case GEM_MB_ACTION
|GEM_MB_DOUBLECLICK
:
1703 core
->GetVideoDriver()->ConvertToGame( px
, py
);
1705 SelectionRect
.x
= px
;
1706 SelectionRect
.y
= py
;
1709 SelectionRect
.w
= 0;
1710 SelectionRect
.h
= 0;
1713 /** Mouse Button Up */
1714 void GameControl::OnMouseUp(unsigned short x
, unsigned short y
, unsigned short Button
,
1715 unsigned short /*Mod*/)
1720 if (ScreenFlags
& SF_DISABLEMOUSE
) {
1723 //heh, i found no better place
1724 core
->CloseCurrentContainer();
1726 MouseIsDown
= false;
1728 core
->GetVideoDriver()->ConvertToGame( p
.x
, p
.y
);
1729 Game
* game
= core
->GetGame();
1730 Map
* area
= game
->GetCurrentArea( );
1732 if (DrawSelectionRect
) {
1734 unsigned int count
= area
->GetActorInRect( ab
, SelectionRect
,true );
1735 for (i
= 0; i
< highlighted
.size(); i
++)
1736 highlighted
[i
]->SetOver( false );
1737 highlighted
.clear();
1738 game
->SelectActor( NULL
, false, SELECT_NORMAL
);
1740 for (i
= 0; i
< count
; i
++) {
1741 // FIXME: should call handler only once
1742 game
->SelectActor( ab
[i
], true, SELECT_NORMAL
);
1746 DrawSelectionRect
= false;
1750 //hidden actors are not selectable by clicking on them
1751 Actor
* actor
= area
->GetActor( p
, GA_DEFAULT
| GA_SELECT
| GA_NO_DEAD
| GA_NO_HIDDEN
);
1752 if (Button
== GEM_MB_MENU
) {
1755 DisplayStringCore(actor
, VB_SELECT
+core
->Roll(1,3,-1), DS_CONST
|DS_CONSOLE
);
1758 core
->GetDictionary()->SetAt( "MenuX", x
);
1759 core
->GetDictionary()->SetAt( "MenuY", y
);
1760 core
->GetGUIScriptEngine()->RunFunction( "OpenFloatMenuWindow" );
1764 if (Button
!= GEM_MB_ACTION
) {
1768 if (!actor
&& ( game
->selected
.size() > 0 )) {
1770 HandleDoor(overDoor
, core
->GetFirstSelectedPC(false));
1773 if (overContainer
) {
1774 HandleContainer(overContainer
, core
->GetFirstSelectedPC(false));
1777 if (overInfoPoint
) {
1778 if (HandleActiveRegion(overInfoPoint
, core
->GetFirstSelectedPC(false), p
)) {
1783 //just a single actor, no formation
1784 if (game
->selected
.size()==1) {
1785 //the player is using an item or spell on the ground
1786 if ((target_mode
== TARGET_MODE_CAST
) && spellCount
) {
1787 TryToCast(core
->GetFirstSelectedPC(false), p
);
1791 actor
=game
->selected
[0];
1793 actor
->ClearActions();
1794 CreateMovement(actor
, p
);
1797 sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y );
1799 sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y );
1802 actor->AddAction( GenerateAction( Tmp) );
1804 //p is a searchmap travel region
1805 if ( actor
->GetCurrentArea()->GetCursor(p
) == IE_CURSOR_TRAVEL
) {
1806 sprintf( Tmp
, "NIDSpecial2()" );
1807 actor
->AddAction( GenerateAction( Tmp
) );
1812 // construct a sorted party
1813 // TODO: this is beyond horrible, help
1814 std::vector
<Actor
*> party
;
1815 // first, from the actual party
1816 for (int idx
= 0; idx
< game
->GetPartySize(false); idx
++) {
1817 Actor
*pc
= game
->FindPC(idx
+ 1);
1820 for (unsigned int j
= 0; j
< game
->selected
.size(); j
++) {
1821 if (game
->selected
[j
] == pc
) {
1822 party
.push_back(pc
);
1827 // then, anything else we selected
1828 for (i
= 0; i
< game
->selected
.size(); i
++) {
1830 for (unsigned int j
= 0; j
< party
.size(); j
++) {
1831 if (game
->selected
[i
] == party
[j
]) {
1836 if (!found
) party
.push_back(game
->selected
[i
]);
1839 //party formation movement
1840 Point src
= party
[0]->Pos
;
1841 for(i
= 0; i
< party
.size(); i
++) {
1844 actor
->ClearActions();
1845 MoveToPointFormation(actor
, i
, src
, p
);
1848 //p is a searchmap travel region
1849 if ( party
[0]->GetCurrentArea()->GetCursor(p
) == IE_CURSOR_TRAVEL
) {
1850 sprintf( Tmp
, "NIDSpecial2()" );
1851 party
[0]->AddAction( GenerateAction( Tmp
) );
1856 //we got an actor past this point
1857 DisplayStringCore(actor
, VB_SELECT
+core
->Roll(1,3,-1), DS_CONST
|DS_CONSOLE
);
1859 PerformActionOn(actor
);
1862 void GameControl::PerformActionOn(Actor
*actor
) {
1863 Game
* game
= core
->GetGame();
1866 //determining the type of the clicked actor
1869 type
= actor
->GetStat(IE_EA
);
1870 if ( type
>= EA_EVILCUTOFF
|| type
== EA_GOODBUTRED
) {
1871 type
= ACT_ATTACK
; //hostile
1872 } else if ( type
> EA_CHARMED
) {
1873 type
= ACT_TALK
; //neutral
1875 type
= ACT_NONE
; //party
1878 if (target_mode
== TARGET_MODE_ATTACK
) {
1880 } else if (target_mode
== TARGET_MODE_TALK
) {
1882 } else if (target_mode
== TARGET_MODE_CAST
) {
1884 } else if (target_mode
== TARGET_MODE_DEFEND
) {
1886 } else if (target_mode
== TARGET_MODE_PICK
) {
1887 type
= ACT_THIEVING
;
1890 //we shouldn't zero this for two reasons in case of spell or item
1891 //1. there could be multiple targets
1892 //2. the target mode is important
1893 if (!(target_mode
== TARGET_MODE_CAST
) || !spellCount
) {
1894 target_mode
= TARGET_MODE_NONE
;
1898 case ACT_NONE
: //none
1900 SelectActor( actor
->InParty
);
1901 else if (actor
->GetStat(IE_EA
) <= EA_CHARMED
) {
1902 /*let's select charmed/summoned creatures
1903 EA_CHARMED is the maximum value known atm*/
1904 core
->GetGame()->SelectActor(actor
, true, SELECT_REPLACE
) ;
1908 //talk (first selected talks)
1909 if (game
->selected
.size()) {
1910 //if we are in PST modify this to NO!
1912 if (core
->HasFeature(GF_PROTAGONIST_TALKS
) ) {
1913 source
= game
->GetPC(0, false); //protagonist
1915 source
= core
->GetFirstSelectedPC(false);
1917 // only party members can start conversations
1919 TryToTalk(source
, actor
);
1924 //all of them attacks the red circled actor
1925 for(i
=0;i
<game
->selected
.size();i
++) {
1926 TryToAttack(game
->selected
[i
], actor
);
1929 case ACT_CAST
: //cast on target or use item on target
1930 if (game
->selected
.size()==1) {
1932 source
= core
->GetFirstSelectedPC(false);
1934 TryToCast(source
, actor
);
1939 for(i
=0;i
<game
->selected
.size();i
++) {
1940 TryToDefend(game
->selected
[i
], actor
);
1944 if (game
->selected
.size()==1) {
1946 source
= core
->GetFirstSelectedPC(false);
1948 TryToPick(source
, actor
);
1954 /** Special Key Press */
1955 void GameControl::OnSpecialKeyPress(unsigned char Key
)
1957 if (DialogueFlags
&DF_IN_DIALOG
) {
1960 //simulating the continue/end button pressed
1961 core
->GetGUIScriptEngine()->RunFunction("CloseContinueWindow");
1964 return; //don't accept keys in dialog
1966 Region Viewport
= core
->GetVideoDriver()->GetViewport();
1967 Game
*game
= core
->GetGame();
1968 Point mapsize
= game
->GetCurrentArea()->TMap
->GetMapSize();
1969 int partysize
= game
->GetPartySize(false);
1975 if (Viewport
.x
> 63)
1981 if (Viewport
.y
> 63)
1987 if (Viewport
.y
+ Viewport
.h
+ 64 < mapsize
.y
)
1990 Viewport
.y
= mapsize
.y
- Viewport
.h
;
1991 if (Viewport
.y
<0) Viewport
.y
=0;
1995 if (Viewport
.x
+ Viewport
.w
+ 64 < mapsize
.x
)
1998 Viewport
.x
= mapsize
.x
- Viewport
.w
;
1999 if (Viewport
.x
<0) Viewport
.x
=0;
2003 DebugFlags
|= DEBUG_SHOW_CONTAINERS
;
2006 // show partymember hp/maxhp as overhead text
2007 for (pm
=0; pm
< partysize
; pm
++) {
2008 Actor
*pc
= game
->GetPC(pm
, true);
2010 memset(tmpstr
, 0, 10);
2011 snprintf(tmpstr
, 10, "%d/%d", pc
->Modified
[IE_HITPOINTS
], pc
->Modified
[IE_MAXHITPOINTS
]);
2012 pc
->DisplayHeadText(strdup(tmpstr
));
2020 core
->GetGUIScriptEngine()->RunFunction("EmptyControls");
2021 core
->SetEventFlag(EF_ACTION
);
2024 core
->GetGUIScriptEngine()->RunFunction("OnIncreaseSize");
2027 core
->GetGUIScriptEngine()->RunFunction("OnDecreaseSize");
2032 if (ScreenFlags
& SF_LOCKSCROLL
) {
2037 // override any existing viewport moves which may be in progress
2038 core
->timer
->SetMoveViewPort( Viewport
.x
, Viewport
.y
, 0, false );
2039 // move it directly ourselves, since we might be paused
2040 core
->GetVideoDriver()->MoveViewportTo( Viewport
.x
, Viewport
.y
);
2044 void GameControl::CalculateSelection(const Point
&p
)
2047 Game
* game
= core
->GetGame();
2048 Map
* area
= game
->GetCurrentArea( );
2049 if (DrawSelectionRect
) {
2051 SelectionRect
.w
= StartX
- p
.x
;
2052 SelectionRect
.x
= p
.x
;
2054 SelectionRect
.x
= StartX
;
2055 SelectionRect
.w
= p
.x
- StartX
;
2058 SelectionRect
.h
= StartY
- p
.y
;
2059 SelectionRect
.y
= p
.y
;
2061 SelectionRect
.y
= StartY
;
2062 SelectionRect
.h
= p
.y
- StartY
;
2065 unsigned int count
= area
->GetActorInRect( ab
, SelectionRect
,true );
2066 for (i
= 0; i
< highlighted
.size(); i
++)
2067 highlighted
[i
]->SetOver( false );
2068 highlighted
.clear();
2070 for (i
= 0; i
< count
; i
++) {
2071 ab
[i
]->SetOver( true );
2072 highlighted
.push_back( ab
[i
] );
2077 Actor
* actor
= area
->GetActor( p
, GA_DEFAULT
| GA_SELECT
| GA_NO_DEAD
| GA_NO_ENEMY
);
2078 Actor
*lastActor
= area
->GetActorByGlobalID(lastActorID
);
2080 lastActor
->SetOver( false );
2084 lastActorID
= actor
->globalID
;
2085 actor
->SetOver( true );
2090 void GameControl::SetCutSceneMode(bool active
)
2093 ScreenFlags
|= (SF_DISABLEMOUSE
| SF_LOCKSCROLL
| SF_CUTSCENE
);
2097 ScreenFlags
&= ~(SF_DISABLEMOUSE
| SF_LOCKSCROLL
| SF_CUTSCENE
);
2101 //Change game window geometries when a new window gets deactivated
2102 void GameControl::HandleWindowHide(const char *WindowName
, const char *WindowPosition
)
2104 Variables
* dict
= core
->GetDictionary();
2107 if (dict
->Lookup( WindowName
, index
)) {
2108 if (index
!= (ieDword
) -1) {
2109 Window
* w
= core
->GetWindow( (unsigned short) index
);
2111 core
->SetVisible( (unsigned short) index
, WINDOW_INVISIBLE
);
2112 if (dict
->Lookup( WindowPosition
, index
)) {
2113 ResizeDel( w
, index
);
2117 printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED
);
2118 printf("%s:%u\n",WindowName
, index
);
2123 //Hide all other windows on the GUI (gamecontrol is not hidden by this)
2124 int GameControl::HideGUI()
2126 //hidegui is in effect
2127 if (!(ScreenFlags
&SF_GUIENABLED
) ) {
2130 //no gamecontrol visible
2131 if (Owner
->Visible
== WINDOW_INVISIBLE
) {
2134 ScreenFlags
&=~SF_GUIENABLED
;
2135 HandleWindowHide("PortraitWindow", "PortraitPosition");
2136 HandleWindowHide("OtherWindow", "OtherPosition");
2137 HandleWindowHide("TopWindow", "TopPosition");
2138 HandleWindowHide("OptionsWindow", "OptionsPosition");
2139 HandleWindowHide("MessageWindow", "MessagePosition");
2140 HandleWindowHide("ActionsWindow", "ActionsPosition");
2141 //FloatWindow doesn't affect gamecontrol, so it is special
2142 Variables
* dict
= core
->GetDictionary();
2145 if (dict
->Lookup( "FloatWindow", index
)) {
2146 if (index
!= (ieDword
) -1) {
2147 core
->SetVisible( (unsigned short) index
, WINDOW_INVISIBLE
);
2150 core
->GetVideoDriver()->SetViewport( Owner
->XPos
, Owner
->YPos
, Width
, Height
);
2154 //Change game window geometries when a new window gets activated
2155 void GameControl::HandleWindowReveal(const char *WindowName
, const char *WindowPosition
)
2157 Variables
* dict
= core
->GetDictionary();
2160 if (dict
->Lookup( WindowName
, index
)) {
2161 if (index
!= (ieDword
) -1) {
2162 Window
* w
= core
->GetWindow( (unsigned short) index
);
2164 core
->SetVisible( (unsigned short) index
, WINDOW_VISIBLE
);
2165 if (dict
->Lookup( WindowPosition
, index
)) {
2166 ResizeAdd( w
, index
);
2170 printMessage("GameControl", "Invalid Window Index ", LIGHT_RED
);
2171 printf("%s:%u\n",WindowName
, index
);
2176 //Reveal all windows on the GUI (including this one)
2177 int GameControl::UnhideGUI()
2179 if (ScreenFlags
&SF_GUIENABLED
) {
2183 ScreenFlags
|= SF_GUIENABLED
;
2184 // Unhide the gamecontrol window
2185 core
->SetVisible( 0, WINDOW_VISIBLE
);
2187 HandleWindowReveal("ActionsWindow", "ActionsPosition");
2188 HandleWindowReveal("MessageWindow", "MessagePosition");
2189 HandleWindowReveal("OptionsWindow", "OptionsPosition");
2190 HandleWindowReveal("TopWindow", "TopPosition");
2191 HandleWindowReveal("OtherWindow", "OtherPosition");
2192 HandleWindowReveal("PortraitWindow", "PortraitPosition");
2193 //the floatwindow is a special case
2194 Variables
* dict
= core
->GetDictionary();
2197 if (dict
->Lookup( "FloatWindow", index
)) {
2198 if (index
!= (ieDword
) -1) {
2199 Window
* fw
= core
->GetWindow( (unsigned short) index
);
2201 core
->SetVisible( (unsigned short) index
, WINDOW_VISIBLE
);
2202 fw
->Flags
|=WF_FLOAT
;
2203 core
->SetOnTop( index
);
2207 core
->GetVideoDriver()->SetViewport( Owner
->XPos
, Owner
->YPos
, Width
, Height
);
2211 //a window got removed, so the GameControl gets enlarged
2212 void GameControl::ResizeDel(Window
* win
, int type
)
2217 printMessage("GameControl","More than one left window!\n",LIGHT_RED
);
2221 Owner
->XPos
-= win
->Width
;
2222 Owner
->Width
+= win
->Width
;
2223 Width
= Owner
->Width
;
2228 if (BottomCount
!=1) {
2229 printMessage("GameControl","More than one bottom window!\n",LIGHT_RED
);
2233 Owner
->Height
+= win
->Height
;
2234 Height
= Owner
->Height
;
2239 if (RightCount
!=1) {
2240 printMessage("GameControl","More than one right window!\n",LIGHT_RED
);
2244 Owner
->Width
+= win
->Width
;
2245 Width
= Owner
->Width
;
2251 printMessage("GameControl","More than one top window!\n",LIGHT_RED
);
2255 Owner
->YPos
-= win
->Height
;
2256 Owner
->Height
+= win
->Height
;
2257 Height
= Owner
->Height
;
2261 case 4: //BottomAdded
2263 Owner
->Height
+= win
->Height
;
2264 Height
= Owner
->Height
;
2266 case 5: //Inactivating
2268 Owner
->Height
+= win
->Height
;
2269 Height
= Owner
->Height
;
2274 //a window got added, so the GameControl gets shrunk
2275 //Owner is the GameControl's window
2276 //GameControl is the only control on that window
2277 void GameControl::ResizeAdd(Window
* win
, int type
)
2282 if (LeftCount
== 1) {
2283 Owner
->XPos
+= win
->Width
;
2284 Owner
->Width
-= win
->Width
;
2285 Width
= Owner
->Width
;
2291 if (BottomCount
== 1) {
2292 Owner
->Height
-= win
->Height
;
2293 Height
= Owner
->Height
;
2299 if (RightCount
== 1) {
2300 Owner
->Width
-= win
->Width
;
2301 Width
= Owner
->Width
;
2307 if (TopCount
== 1) {
2308 Owner
->YPos
+= win
->Height
;
2309 Owner
->Height
-= win
->Height
;
2310 Height
= Owner
->Height
;
2314 case 4: //BottomAdded
2316 Owner
->Height
-= win
->Height
;
2317 Height
= Owner
->Height
;
2320 case 5: //Inactivating
2322 Owner
->Height
-= win
->Height
;
2327 //Try to start dialogue between two actors (one of them could be inanimate)
2328 int GameControl::InitDialog(Scriptable
* spk
, Scriptable
* tgt
, const char* dlgref
)
2335 PluginHolder
<DialogMgr
> dm(IE_DLG_CLASS_ID
);
2336 dm
->Open( gamedata
->GetResource( dlgref
, IE_DLG_CLASS_ID
), true );
2337 dlg
= dm
->GetDialog();
2340 printMessage("GameControl", " ", LIGHT_RED
);
2341 printf( "Cannot start dialog: %s\n", dlgref
);
2345 strnlwrcpy(dlg
->ResRef
, dlgref
, 8); //this isn't handled by GetDialog???
2347 //target is here because it could be changed when a dialog runs onto
2348 //and external link, we need to find the new target (whose dialog was
2351 Actor
*spe
= (Actor
*) spk
;
2352 speakerID
= spe
->globalID
;
2353 if (tgt
->Type
!=ST_ACTOR
) {
2355 //most likely this dangling object reference
2356 //won't cause problems, because trigger points don't
2357 //get destroyed during a dialog
2359 spk
->LastTalkedTo
=0;
2361 Actor
*tar
= (Actor
*) tgt
;
2362 speakerID
= spe
->globalID
;
2363 targetID
= tar
->globalID
;
2364 if (!originalTargetID
) originalTargetID
= tar
->globalID
;
2365 spe
->LastTalkedTo
=targetID
;
2366 tar
->LastTalkedTo
=speakerID
;
2369 //check if we are already in dialog
2370 if (DialogueFlags
&DF_IN_DIALOG
) {
2374 int si
= dlg
->FindFirstState( tgt
);
2379 //we need GUI for dialogs
2382 //no exploring while in dialogue
2383 ScreenFlags
|= SF_GUIENABLED
|SF_DISABLEMOUSE
|SF_LOCKSCROLL
;
2384 DialogueFlags
|= DF_IN_DIALOG
;
2386 if (tgt
->Type
==ST_ACTOR
) {
2387 Actor
*tar
= (Actor
*) tgt
;
2388 tar
->DialogInterrupt();
2391 //allow mouse selection from dialog (even though screen is locked)
2392 core
->GetVideoDriver()->SetMouseEnabled(true);
2393 core
->timer
->SetMoveViewPort( tgt
->Pos
.x
, tgt
->Pos
.y
, 0, true );
2394 //there are 3 bits, if they are all unset, the dialog freezes scripts
2395 if (!(dlg
->Flags
&7) ) {
2396 DialogueFlags
|= DF_FREEZE_SCRIPTS
;
2398 //opening control size to maximum, enabling dialog window
2399 core
->GetGame()->SetControlStatus(CS_HIDEGUI
, BM_NAND
);
2400 core
->GetGame()->SetControlStatus(CS_DIALOG
, BM_OR
);
2401 core
->SetEventFlag(EF_PORTRAIT
);
2405 /*try to break will only try to break it, false means unconditional stop*/
2406 void GameControl::EndDialog(bool try_to_break
)
2408 if (try_to_break
&& (DialogueFlags
&DF_UNBREAKABLE
) ) {
2412 Actor
*tmp
= GetSpeaker();
2417 if (targetID
==0xffff) {
2418 targetOB
->LeaveDialog();
2427 originalTargetID
= 0;
2433 //restoring original size
2434 core
->GetGame()->SetControlStatus(CS_DIALOG
, BM_NAND
);
2435 ScreenFlags
&=~(SF_DISABLEMOUSE
|SF_LOCKSCROLL
);
2437 core
->SetEventFlag(EF_PORTRAIT
);
2440 //translate section values (journal, solved, unsolved, user)
2441 static const int sectionMap
[4]={4,1,2,0};
2443 void GameControl::DialogChoose(unsigned int choose
)
2445 TextArea
* ta
= core
->GetMessageTextArea();
2447 printMessage("GameControl","Dialog aborted???",LIGHT_RED
);
2452 Actor
*speaker
= GetSpeaker();
2454 printMessage("GameControl","Speaker gone???",LIGHT_RED
);
2461 if (targetID
!=0xffff) {
2470 printMessage("GameControl","Target gone???",LIGHT_RED
);
2475 if (choose
== (unsigned int) -1) {
2476 //increasing talkcount after top level condition was determined
2478 int si
= dlg
->FindFirstState( tgt
);
2485 if (DialogueFlags
&DF_TALKCOUNT
) {
2486 DialogueFlags
&=~DF_TALKCOUNT
;
2488 } else if (DialogueFlags
&DF_INTERACT
) {
2489 DialogueFlags
&=~DF_INTERACT
;
2490 tgt
->InteractCount
++;
2493 ds
= dlg
->GetState( si
);
2495 if (ds
->transitionsCount
<= choose
) {
2499 DialogTransition
* tr
= ds
->transitions
[choose
];
2503 if (tr
->Flags
&IE_DLG_TR_JOURNAL
) {
2505 if (tr
->Flags
&IE_DLG_UNSOLVED
) {
2508 if (tr
->Flags
&IE_DLG_SOLVED
) {
2511 if (core
->GetGame()->AddJournalEntry(tr
->journalStrRef
, sectionMap
[Section
], tr
->Flags
>>16) ) {
2512 core
->DisplayConstantString(STR_JOURNALCHANGE
,0xffff00);
2513 char *string
= core
->GetString( tr
->journalStrRef
);
2514 //cutting off the strings at the first crlf
2515 char *poi
= strchr(string
,'\n');
2519 core
->DisplayString( string
);
2524 if (tr
->textStrRef
!= 0xffffffff) {
2525 //allow_zero is for PST (deionarra's text)
2526 core
->DisplayStringName( (int) (tr
->textStrRef
), 0x8080FF, speaker
, IE_STR_SOUND
|IE_STR_SPEECH
|IE_STR_ALLOW_ZERO
);
2527 if (core
->HasFeature( GF_DIALOGUE_SCROLLS
)) {
2528 ta
->AppendText( "", -1 );
2532 if (tr
->actions
.size()) {
2533 // does this belong here? we must clear actions somewhere before
2534 // we start executing them (otherwise queued actions interfere)
2535 // executing actions directly does not work, because dialog
2536 // needs to end before final actions are executed due to
2537 // actions making new dialogs!
2538 if (target
->Type
== ST_ACTOR
) ((Movable
*)target
)->ClearPath(); // fuzzie added this
2539 target
->ClearActions();
2541 for (unsigned int i
= 0; i
< tr
->actions
.size(); i
++) {
2542 target
->AddAction(tr
->actions
[i
]);
2543 //GameScript::ExecuteAction( target, action );
2547 int final_dialog
= tr
->Flags
& IE_DLG_TR_FINAL
;
2550 ta
->SetMinRow( false );
2554 // all dialog actions must be executed immediately
2555 target
->ProcessActions(true);
2556 // (do not clear actions - final actions can involve waiting/moving)
2562 //displaying dialog for selected option
2563 int si
= tr
->stateIndex
;
2564 //follow external linkage, if required
2565 if (tr
->Dialog
[0] && strnicmp( tr
->Dialog
, dlg
->ResRef
, 8 )) {
2566 //target should be recalculated!
2568 if (originalTargetID
) {
2569 // always try original target first (sometimes there are multiple
2570 // actors with the same dialog in an area, we want to pick the one
2571 // we were talking to)
2572 tgt
= GetActorByGlobalID(originalTargetID
);
2573 if (tgt
&& strnicmp( tgt
->GetDialog(GD_NORMAL
), tr
->Dialog
, 8 ) != 0) {
2578 // then just search the current area for an actor with the dialog
2579 tgt
= target
->GetCurrentArea()->GetActorByDialog(tr
->Dialog
);
2583 printMessage("Dialog","Can't redirect dialog\n",YELLOW
);
2584 ta
->SetMinRow( false );
2588 targetID
= tgt
->globalID
;
2589 // we have to make a backup, tr->Dialog is freed
2591 strnlwrcpy(tmpresref
,tr
->Dialog
, 8);
2592 if (target
->GetInternalFlag()&IF_NOINT
) {
2593 // this whole check moved out of InitDialog by fuzzie, see comments
2594 // for the IF_NOINT check in BeginDialog
2595 core
->DisplayConstantString(STR_TARGETBUSY
,0xff0000);
2596 ta
->SetMinRow( false );
2600 int ret
= InitDialog( speaker
, target
, tmpresref
);
2602 // error was displayed by InitDialog
2603 ta
->SetMinRow( false );
2608 ds
= dlg
->GetState( si
);
2610 printMessage("Dialog","Can't find next dialog\n",YELLOW
);
2611 ta
->SetMinRow( false );
2616 //displaying npc text
2617 core
->DisplayStringName( ds
->StrRef
, 0x70FF70, target
, IE_STR_SOUND
|IE_STR_SPEECH
);
2618 //adding a gap between options and npc text
2619 ta
->AppendText("",-1);
2622 ta
->SetMinRow( true );
2623 //first looking for a 'continue' opportunity, the order is descending (a la IE)
2624 unsigned int x
= ds
->transitionsCount
;
2626 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_FINAL
) {
2629 if (ds
->transitions
[x
]->textStrRef
!= 0xffffffff) {
2632 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_TRIGGER
) {
2633 if (ds
->transitions
[x
]->condition
&&
2634 !ds
->transitions
[x
]->condition
->Evaluate(target
)) {
2638 core
->GetDictionary()->SetAt("DialogOption",x
);
2639 DialogueFlags
|= DF_OPENCONTINUEWINDOW
;
2642 for (x
= 0; x
< ds
->transitionsCount
; x
++) {
2643 if (ds
->transitions
[x
]->Flags
& IE_DLG_TR_TRIGGER
) {
2644 if (ds
->transitions
[x
]->condition
&&
2645 !ds
->transitions
[x
]->condition
->Evaluate(target
)) {
2650 if (ds
->transitions
[x
]->textStrRef
== 0xffffffff) {
2651 //dialogchoose should be set to x
2652 //it isn't important which END option was chosen, as it ends
2653 core
->GetDictionary()->SetAt("DialogOption",x
);
2654 DialogueFlags
|= DF_OPENENDWINDOW
;
2656 char *string
= ( char * ) malloc( 40 );
2657 sprintf( string
, "[s=%d,ffffff,ff0000]%d - [p]", x
, idx
);
2658 i
= ta
->AppendText( string
, -1 );
2660 string
= core
->GetString( ds
->transitions
[x
]->textStrRef
);
2661 ta
->AppendText( string
, i
);
2663 ta
->AppendText( "[/p][/s]", i
);
2666 // this happens if a trigger isn't implemented or the dialog is wrong
2668 printMessage("Dialog", "There were no valid dialog options!\n", YELLOW
);
2669 DialogueFlags
|= DF_OPENENDWINDOW
;
2672 //padding the rows so our text will be at the top
2673 if (core
->HasFeature( GF_DIALOGUE_SCROLLS
)) {
2674 ta
->AppendText( "", -1 );
2681 //Create an overhead text over an arbitrary point
2682 void GameControl::DisplayString(const Point
&p
, const char *Text
)
2684 Scriptable
* scr
= new Scriptable( ST_TRIGGER
);
2685 scr
->overHeadText
= (char *) Text
;
2686 scr
->textDisplaying
= 1;
2687 scr
->timeStartDisplaying
= 0;
2689 scr
->ClearCutsceneID( );
2692 //Create an overhead text over a scriptable target
2693 //Multiple texts are possible, as this code copies the text to a new object
2694 void GameControl::DisplayString(Scriptable
* target
)
2696 Scriptable
* scr
= new Scriptable( ST_TRIGGER
);
2697 scr
->overHeadText
= strdup( target
->overHeadText
);
2698 /* strdup should work here, we use it elsewhere
2699 size_t len = strlen( target->overHeadText ) + 1;
2700 scr->overHeadText = ( char * ) malloc( len );
2701 strcpy( scr->overHeadText, target->overHeadText );
2703 scr
->textDisplaying
= 1;
2704 scr
->timeStartDisplaying
= target
->timeStartDisplaying
;
2705 scr
->Pos
= target
->Pos
;
2706 scr
->SetCutsceneID( target
);
2709 /** changes displayed map to the currently selected PC */
2710 void GameControl::ChangeMap(Actor
*pc
, bool forced
)
2712 //swap in the area of the actor
2713 Game
* game
= core
->GetGame();
2714 if (forced
|| (stricmp( pc
->Area
, game
->CurrentArea
) != 0) ) {
2716 overInfoPoint
= NULL
;
2717 overContainer
= NULL
;
2719 /*this is loadmap, because we need the index, not the pointer*/
2720 char *areaname
= game
->CurrentArea
;
2722 areaname
= pc
->Area
;
2724 game
->GetMap( areaname
, true );
2725 ScreenFlags
|=SF_CENTERONACTOR
;
2727 //center on first selected actor
2728 Region vp
= core
->GetVideoDriver()->GetViewport();
2729 if (ScreenFlags
&SF_CENTERONACTOR
) {
2730 core
->timer
->SetMoveViewPort( pc
->Pos
.x
, pc
->Pos
.y
, 0, true );
2731 ScreenFlags
&=~SF_CENTERONACTOR
;
2735 void GameControl::SetScreenFlags(int value
, int mode
)
2738 case BM_OR
: ScreenFlags
|=value
; break;
2739 case BM_NAND
: ScreenFlags
&=~value
; break;
2740 case BM_SET
: ScreenFlags
=value
; break;
2741 case BM_AND
: ScreenFlags
&=value
; break;
2742 case BM_XOR
: ScreenFlags
^=value
; break;
2746 void GameControl::SetDialogueFlags(int value
, int mode
)
2749 case BM_OR
: DialogueFlags
|=value
; break;
2750 case BM_NAND
: DialogueFlags
&=~value
; break;
2751 case BM_SET
: DialogueFlags
=value
; break;
2752 case BM_AND
: DialogueFlags
&=value
; break;
2753 case BM_XOR
: DialogueFlags
^=value
; break;
2757 //copies a screenshot into a sprite
2758 Sprite2D
* GameControl::GetScreenshot(bool show_gui
)
2760 Sprite2D
* screenshot
;
2762 screenshot
= core
->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) );
2764 int hf
= HideGUI ();
2766 screenshot
= core
->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) );
2770 core
->DrawWindows ();
2776 //copies a downscaled screenshot into a sprite for save game preview
2777 Sprite2D
* GameControl::GetPreview()
2779 // We get preview by first taking a screenshot of size 640x405,
2780 // centered in the display. This is to get a decent picture for
2781 // higher screen resolutions.
2782 // FIXME: how do orig games solve that?
2783 Video
* video
= core
->GetVideoDriver();
2784 int w
= video
->GetWidth();
2785 int h
= video
->GetHeight();
2786 int x
= (w
- 640) / 2;
2787 int y
= (h
- 405) / 2;
2804 int hf
= HideGUI ();
2805 signed char v
= Owner
->Visible
;
2806 Owner
->Visible
= WINDOW_VISIBLE
;
2809 Sprite2D
*screenshot
= video
->GetScreenshot( Region(x
, y
, w
, h
) );
2813 core
->DrawWindows();
2815 Sprite2D
* preview
= video
->SpriteScaleDown ( screenshot
, 5 );
2816 video
->FreeSprite( screenshot
);
2822 * Returns PC portrait for a currently running game
2824 Sprite2D
* GameControl::GetPortraitPreview(int pcslot
)
2826 /** Portrait shrink ratio */
2827 // FIXME: this is just a random PST specific trait
2828 // you can make it less random with a new feature bit
2829 int ratio
= (core
->HasFeature( GF_ONE_BYTE_ANIMID
)) ? 1 : 2;
2831 Video
*video
= core
->GetVideoDriver();
2833 Actor
*actor
= core
->GetGame()->GetPC( pcslot
, false );
2837 ResourceHolder
<ImageMgr
> im(actor
->GetPortrait(true));
2842 Sprite2D
* img
= im
->GetSprite2D();
2847 Sprite2D
* img_scaled
= video
->SpriteScaleDown( img
, ratio
);
2848 video
->FreeSprite( img
);
2853 Actor
*GameControl::GetActorByGlobalID(ieWord ID
)
2857 Game
* game
= core
->GetGame();
2861 Map
* area
= game
->GetCurrentArea( );
2865 area
->GetActorByGlobalID(ID
);
2868 Actor
*GameControl::GetLastActor()
2870 return GetActorByGlobalID(lastActorID
);
2873 Actor
*GameControl::GetTarget()
2875 return GetActorByGlobalID(targetID
);
2878 Actor
*GameControl::GetSpeaker()
2880 return GetActorByGlobalID(speakerID
);
2883 //Set up an item use which needs targeting
2884 //Slot is an inventory slot
2885 //header is the used item extended header
2887 //target type is a bunch of GetActor flags that alter valid targets
2888 //cnt is the number of different targets (usually 1)
2889 void GameControl::SetupItemUse(int slot
, int header
, Actor
*u
, int targettype
, int cnt
)
2894 spellIndex
= header
;
2895 //item use also uses the casting icon, this might be changed in some custom game?
2896 target_mode
= TARGET_MODE_CAST
;
2897 target_types
= targettype
;
2901 //Set up spell casting which needs targeting
2902 //type is the spell's type
2903 //level is the caster level
2904 //idx is the spell's number
2906 //target type is a bunch of GetActor flags that alter valid targets
2907 //cnt is the number of different targets (usually 1)
2908 void GameControl::SetupCasting(int type
, int level
, int idx
, Actor
*u
, int targettype
, int cnt
)
2914 target_mode
= TARGET_MODE_CAST
;
2915 target_types
= targettype
;
2919 //another method inherited from Control which has no use here
2920 bool GameControl::SetEvent(int /*eventType*/, const char * /*handler*/)
2925 void GameControl::SetDisplayText(char *text
, unsigned int time
)
2928 core
->FreeString(DisplayText
);
2930 DisplayTextTime
= time
;
2934 void GameControl::SetDisplayText(ieStrRef text
, unsigned int time
)
2936 SetDisplayText(core
->GetString(core
->GetStringReference(text
), 0), time
);