properly implemented special race based action buttons
[gemrb.git] / gemrb / core / Interface.cpp
blobcef4ddab51975a595d3cd78931a74484b4c31800
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003-2005 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.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include "Interface.h"
27 #include "exports.h"
28 #include "globals.h"
29 #include "strrefs.h"
30 #include "win32def.h"
32 #include "ActorMgr.h"
33 #include "AmbientMgr.h"
34 #include "AnimationMgr.h"
35 #include "ArchiveImporter.h"
36 #include "Audio.h"
37 #include "Calendar.h"
38 #include "DataFileMgr.h"
39 #include "DialogHandler.h"
40 #include "DialogMgr.h"
41 #include "DisplayMessage.h"
42 #include "EffectMgr.h"
43 #include "EffectQueue.h"
44 #include "Factory.h"
45 #include "Game.h"
46 #include "GameData.h"
47 #include "ImageMgr.h"
48 #include "ItemMgr.h"
49 #include "MapMgr.h"
50 #include "MoviePlayer.h"
51 #include "MusicMgr.h"
52 #include "Palette.h"
53 #include "PluginMgr.h"
54 #include "PluginMgr.h"
55 #include "ProjectileServer.h"
56 #include "SaveGameIterator.h"
57 #include "SaveGameMgr.h"
58 #include "ScriptEngine.h"
59 #include "ScriptedAnimation.h"
60 #include "SoundMgr.h"
61 #include "SpellMgr.h"
62 #include "StoreMgr.h"
63 #include "StringMgr.h"
64 #include "TileMap.h"
65 #include "Video.h"
66 #include "WorldMapMgr.h"
67 #include "GUI/Button.h"
68 #include "GUI/Console.h"
69 #include "GUI/GameControl.h"
70 #include "GUI/Label.h"
71 #include "GUI/MapControl.h"
72 #include "GUI/WorldMapControl.h"
73 #include "System/FileStream.h"
74 #include "System/VFS.h"
76 #include <cstdlib>
77 #include <time.h>
78 #include <vector>
80 GEM_EXPORT Interface* core;
82 #ifdef WIN32
83 GEM_EXPORT HANDLE hConsole;
84 #endif
86 //use DialogF.tlk if the protagonist is female, that's why we leave space
87 static const char dialogtlk[] = "dialog.tlk\0";
89 static int MaximumAbility = 25;
90 static ieWordSigned *strmod = NULL;
91 static ieWordSigned *strmodex = NULL;
92 static ieWordSigned *intmod = NULL;
93 static ieWordSigned *dexmod = NULL;
94 static ieWordSigned *conmod = NULL;
95 static ieWordSigned *chrmod = NULL;
96 static ieWordSigned *lorebon = NULL;
97 static ieWordSigned *wisbon = NULL;
98 static int **reputationmod = NULL;
99 static ieVariable IWD2DeathVarFormat = "_DEAD%s";
100 static ieVariable DeathVarFormat = "SPRITE_IS_DEAD%s";
102 Interface::Interface(int iargc, char* iargv[])
104 argc = iargc;
105 argv = iargv;
106 #ifdef WIN32
107 hConsole = GetStdHandle( STD_OUTPUT_HANDLE );
108 #endif
109 textcolor( LIGHT_WHITE );
110 printf( "GemRB Core Version v%s Loading...\n", VERSION_GEMRB );
112 // default to the correct endianswitch
113 ieWord endiantest = 1;
114 if (((char *)&endiantest)[1] == 1) {
115 // big-endian
116 DataStream::SetEndianSwitch(true);
119 unsigned int i;
120 for(i=0;i<256;i++) {
121 pl_uppercase[i]=(ieByte) toupper(i);
122 pl_lowercase[i]=(ieByte) tolower(i);
125 projserv = NULL;
126 VideoDriverName = "sdl";
127 AudioDriverName = "openal";
128 vars = NULL;
129 tokens = NULL;
130 RtRows = NULL;
131 sgiterator = NULL;
132 game = NULL;
133 calendar = NULL;
134 worldmap = NULL;
135 CurrentStore = NULL;
136 CurrentContainer = NULL;
137 UseContainer = false;
138 InfoTextPalette = NULL;
139 timer = NULL;
140 displaymsg = NULL;
141 evntmgr = NULL;
142 console = NULL;
143 slottypes = NULL;
144 slotmatrix = NULL;
146 ModalWindow = NULL;
147 tooltip_x = 0;
148 tooltip_y = 0;
149 tooltip_currtextw = 0;
150 tooltip_ctrl = NULL;
151 plugin_flags = NULL;
153 pal16 = NULL;
154 pal32 = NULL;
155 pal256 = NULL;
157 GUIEnhancements = 0;
159 CursorCount = 0;
160 Cursors = NULL;
162 mousescrollspd = 10;
164 ConsolePopped = false;
165 CheatFlag = false;
166 FogOfWar = 1;
167 QuitFlag = QF_NORMAL;
168 EventFlag = EF_CONTROL;
169 #ifndef WIN32
170 CaseSensitive = true; //this is the default value, so CD1/CD2 will be resolved
171 #else
172 CaseSensitive = false;
173 #endif
174 GameOnCD = false;
175 SkipIntroVideos = false;
176 DrawFPS = false;
177 KeepCache = false;
178 TooltipDelay = 100;
179 GUIScriptsPath[0] = 0;
180 GamePath[0] = 0;
181 SavePath[0] = 0;
182 GemRBPath[0] = 0;
183 PluginsPath[0] = 0;
184 CachePath[0] = 0;
185 GemRBOverridePath[0] = 0;
186 GameName[0] = 0;
188 strncpy( GameOverridePath, "override", sizeof(GameOverridePath) );
189 strncpy( GameSoundsPath, "sounds", sizeof(GameSoundsPath) );
190 strncpy( GameScriptsPath, "scripts", sizeof(GameScriptsPath) );
191 strncpy( GamePortraitsPath, "portraits", sizeof(GamePortraitsPath) );
192 strncpy( GameCharactersPath, "characters", sizeof(GameCharactersPath) );
193 strncpy( GameDataPath, "data", sizeof(GameDataPath) );
194 strncpy( INIConfig, "baldur.ini", sizeof(INIConfig) );
195 strncpy( ButtonFont, "STONESML", sizeof(ButtonFont) );
196 strncpy( TooltipFont, "STONESML", sizeof(TooltipFont) );
197 strncpy( MovieFont, "STONESML", sizeof(MovieFont) );
198 strncpy( ScrollCursorBam, "CURSARW", sizeof(ScrollCursorBam) );
199 strncpy( GlobalScript, "BALDUR", sizeof(GlobalScript) );
200 strncpy( WorldMapName, "WORLDMAP", sizeof(WorldMapName) );
201 strncpy( Palette16, "MPALETTE", sizeof(Palette16) );
202 strncpy( Palette32, "PAL32", sizeof(Palette32) );
203 strncpy( Palette256, "MPAL256", sizeof(Palette256) );
204 strcpy( TooltipBackResRef, "\0" );
205 for (int size = 0; size < MAX_CIRCLE_SIZE; size++) {
206 strcpy( GroundCircleBam[size], "\0" );
207 GroundCircleScale[size] = 0;
209 TooltipColor.r = 0;
210 TooltipColor.g = 255;
211 TooltipColor.b = 0;
212 TooltipColor.a = 255;
213 TooltipMargin = 10;
215 TooltipBack = NULL;
216 DraggedItem = NULL;
217 DraggedPortrait = 0;
218 DefSound = NULL;
219 DSCount = -1;
220 GameFeatures = 0;
221 GameFeatures2 = 0;
222 memset( WindowFrames, 0, sizeof( WindowFrames ));
223 memset( GroundCircles, 0, sizeof( GroundCircles ));
224 memset(FogSprites, 0, sizeof( FogSprites ));
225 AreaAliasTable = NULL;
226 ItemExclTable = NULL;
227 ItemDialTable = NULL;
228 ItemDial2Table = NULL;
229 ItemTooltipTable = NULL;
230 update_scripts = false;
232 gamedata = new GameData();
235 #define FreeResourceVector(type, variable) \
237 size_t i=variable.size(); \
238 while(i--) { \
239 if (variable[i]) { \
240 delete variable[i]; \
243 variable.clear(); \
246 static void ReleaseItemList(void *poi)
248 delete ((ItemList *) poi);
251 void FreeAbilityTables()
253 if (strmod) {
254 free(strmod);
256 strmod = NULL;
257 if (strmodex) {
258 free(strmodex);
260 strmodex = NULL;
261 if (intmod) {
262 free(intmod);
264 intmod = NULL;
265 if (dexmod) {
266 free(dexmod);
268 dexmod = NULL;
269 if (conmod) {
270 free(conmod);
272 conmod = NULL;
273 if (chrmod) {
274 free(chrmod);
276 chrmod = NULL;
277 if (lorebon) {
278 free(lorebon);
280 lorebon = NULL;
281 if (wisbon) {
282 free(wisbon);
284 wisbon = NULL;
287 void Interface::FreeResRefTable(ieResRef *&table, int &count)
289 if (table) {
290 free( table );
291 count = -1;
295 static void ReleaseItemTooltip(void *poi)
297 free(poi);
300 Interface::~Interface(void)
302 DragItem(NULL,NULL);
303 delete AreaAliasTable;
305 if (music) {
306 music->HardEnd();
308 // stop any ambients which are still enqueued
309 if (AudioDriver) {
310 AmbientMgr *ambim = AudioDriver->GetAmbientMgr();
311 if (ambim) ambim->deactivate();
313 //destroy the highest objects in the hierarchy first!
314 delete game;
315 delete calendar;
316 delete worldmap;
318 FreeAbilityTables();
320 if (reputationmod) {
321 for (unsigned int i=0; i<20; i++) {
322 if (reputationmod[i]) {
323 free(reputationmod[i]);
326 free(reputationmod);
327 reputationmod=NULL;
330 PluginMgr::Get()->RunCleanup();
332 ReleaseMemoryActor();
333 EffectQueue_ReleaseMemory();
334 CharAnimations::ReleaseMemory();
335 delete CurrentStore;
337 FreeResRefTable(DefSound, DSCount);
339 free( slottypes );
340 free( slotmatrix );
342 delete sgiterator;
344 if (Cursors) {
345 for (int i = 0; i < CursorCount; i++) {
346 video->FreeSprite( Cursors[i] );
348 delete[] Cursors;
351 FreeResourceVector( Font, fonts );
352 FreeResourceVector( Window, windows );
354 size_t i;
355 for (i = 0; i < musiclist.size(); i++) {
356 free((void *)musiclist[i]);
359 DamageInfoMap.clear();
361 ModalStates.clear();
363 delete plugin_flags;
365 delete projserv;
367 delete console;
369 delete pal256;
370 delete pal32;
371 delete pal16;
373 delete timer;
374 delete displaymsg;
376 if (video) {
378 for(i=0;i<sizeof(FogSprites)/sizeof(Sprite2D *);i++ ) {
379 video->FreeSprite(FogSprites[i]);
382 for(i=0;i<4;i++) {
383 video->FreeSprite(WindowFrames[i]);
386 for (int size = 0; size < MAX_CIRCLE_SIZE; size++) {
387 for(i=0;i<6;i++) {
388 video->FreeSprite(GroundCircles[size][i]);
392 if (TooltipBack) {
393 for(i=0;i<3;i++) {
394 //freesprite checks for null pointer
395 video->FreeSprite(TooltipBack[i]);
397 delete[] TooltipBack;
399 if (InfoTextPalette) {
400 gamedata->FreePalette(InfoTextPalette);
403 video->SetDragCursor(NULL);
406 delete evntmgr;
408 delete vars;
409 delete tokens;
410 if (RtRows) {
411 RtRows->RemoveAll(ReleaseItemList);
412 delete RtRows;
414 if (ItemExclTable) {
415 ItemExclTable->RemoveAll(NULL);
416 delete ItemExclTable;
418 if (ItemDialTable) {
419 ItemDialTable->RemoveAll(NULL);
420 delete ItemDialTable;
422 if (ItemDial2Table) {
423 ItemDial2Table->RemoveAll(NULL);
424 delete ItemDial2Table;
426 if (ItemTooltipTable) {
427 ItemTooltipTable->RemoveAll(ReleaseItemTooltip);
428 delete ItemTooltipTable;
431 Map::ReleaseMemory();
432 Actor::ReleaseMemory();
434 gamedata->ClearCaches();
435 delete gamedata;
436 gamedata = NULL;
438 // Removing all stuff from Cache, except bifs
439 if (!KeepCache) DelTree((const char *) CachePath, true);
442 void Interface::SetWindowFrame(int i, Sprite2D *Picture)
444 video->FreeSprite(WindowFrames[i]);
445 WindowFrames[i]=Picture;
448 GameControl* Interface::StartGameControl()
450 //making sure that our window is the first one
451 if (ConsolePopped) {
452 PopupConsole();
454 DelAllWindows();//deleting ALL windows
455 gamedata->DelTable(0xffffu); //dropping ALL tables
456 Window* gamewin = new Window( 0xffff, 0, 0, (ieWord) Width, (ieWord) Height );
457 gamewin->WindowPack[0]=0;
458 GameControl* gc = new GameControl();
459 gc->XPos = 0;
460 gc->YPos = 0;
461 gc->Width = (ieWord) Width;
462 gc->Height = (ieWord) Height;
463 gc->Owner = gamewin;
464 gc->ControlID = 0x00000000;
465 gc->ControlType = IE_GUI_GAMECONTROL;
466 gamewin->AddControl( gc );
467 AddWindow( gamewin );
468 SetVisible( 0, WINDOW_VISIBLE );
469 //setting the focus to the game control
470 evntmgr->SetFocused(gamewin, gc);
471 if (guiscript->LoadScript( "MessageWindow" )) {
472 guiscript->RunFunction( "MessageWindow", "OnLoad" );
473 gc->UnhideGUI();
476 return gc;
479 /* handle main loop events that might destroy or create windows
480 thus cannot be called from DrawWindows directly
481 these events are pending until conditions are right
483 void Interface::HandleEvents()
485 GameControl *gc = GetGameControl();
486 if (gc && (!gc->Owner || !gc->Owner->Visible)) {
487 gc=NULL;
491 if (EventFlag&EF_SELECTION) {
492 EventFlag&=~EF_SELECTION;
493 guiscript->RunFunction( "GUICommonWindows", "SelectionChanged", false);
496 if (EventFlag&EF_UPDATEANIM) {
497 EventFlag&=~EF_UPDATEANIM;
498 guiscript->RunFunction( "GUICommonWindows", "UpdateAnimation", false);
501 if (EventFlag&EF_PORTRAIT) {
502 ieDword tmp = (ieDword) ~0;
503 vars->Lookup( "PortraitWindow", tmp );
504 if (tmp != (ieDword) ~0) {
505 EventFlag&=~EF_PORTRAIT;
506 guiscript->RunFunction( "GUICommonWindows", "UpdatePortraitWindow" );
510 if (EventFlag&EF_ACTION) {
511 ieDword tmp = (ieDword) ~0;
512 vars->Lookup( "ActionsWindow", tmp );
513 if (tmp != (ieDword) ~0) {
514 EventFlag&=~EF_ACTION;
515 guiscript->RunFunction( "GUICommonWindows", "UpdateActionsWindow" );
519 if ((EventFlag&EF_CONTROL) && gc) {
520 EventFlag&=~EF_CONTROL;
521 guiscript->RunFunction( "MessageWindow", "UpdateControlStatus" );
522 //this is the only value we can use here
523 if (game->ControlStatus & CS_HIDEGUI)
524 gc->HideGUI();
525 else
526 gc->UnhideGUI();
527 return;
529 if ((EventFlag&EF_SHOWMAP) && gc) {
530 ieDword tmp = (ieDword) ~0;
531 vars->Lookup( "OtherWindow", tmp );
532 if (tmp == (ieDword) ~0) {
533 EventFlag &= ~EF_SHOWMAP;
534 guiscript->RunFunction( "GUIMA", "ShowMap" );
536 return;
539 if (EventFlag&EF_SEQUENCER) {
540 EventFlag&=~EF_SEQUENCER;
541 guiscript->RunFunction( "GUIMG", "OpenSequencerWindow" );
542 return;
545 if (EventFlag&EF_IDENTIFY) {
546 EventFlag&=~EF_IDENTIFY;
547 // FIXME: Implement this.
548 guiscript->RunFunction( "GUICommonWindows", "OpenIdentifyWindow" );
549 return;
551 if (EventFlag&EF_OPENSTORE) {
552 EventFlag&=~EF_OPENSTORE;
553 guiscript->RunFunction( "GUISTORE", "OpenStoreWindow" );
554 return;
557 if (EventFlag&EF_MASTERSCRIPT) {
558 EventFlag&=~EF_MASTERSCRIPT;
559 guiscript->RunFunction( "MessageWindow", "UpdateMasterScript" );
560 return;
563 if (EventFlag&EF_CLOSECONTAINER) {
564 EventFlag&=~EF_CLOSECONTAINER;
565 guiscript->RunFunction( "GUIWORLD", "CloseContainerWindow" );
566 return;
570 /* handle main loop events that might destroy or create windows
571 thus cannot be called from DrawWindows directly
573 void Interface::HandleFlags()
575 EventFlag = EF_CONTROL; //clear events because the context changed
577 if (QuitFlag&(QF_QUITGAME|QF_EXITGAME) ) {
578 // when reaching this, quitflag should be 1 or 2
579 // if Exitgame was set, we'll set Start.py too
580 QuitGame (QuitFlag&QF_EXITGAME);
581 QuitFlag &= ~(QF_QUITGAME|QF_EXITGAME);
584 if (QuitFlag&QF_LOADGAME) {
585 QuitFlag &= ~QF_LOADGAME;
586 LoadGame(LoadGameIndex.get(), VersionOverride );
587 LoadGameIndex.release();
590 if (QuitFlag&QF_ENTERGAME) {
591 QuitFlag &= ~QF_ENTERGAME;
592 if (game) {
593 timer->Init();
595 //rearrange party slots
596 game->ConsolidateParty();
597 GameControl* gc = StartGameControl();
598 //switch map to protagonist
599 Actor* actor = GetFirstSelectedPC(true);
600 if (actor) {
601 gc->ChangeMap(actor, true);
603 } else {
604 printMessage("Core", "No game to enter...\n", LIGHT_RED);
605 QuitFlag = QF_QUITGAME;
609 if (QuitFlag&QF_CHANGESCRIPT) {
610 QuitFlag &= ~QF_CHANGESCRIPT;
611 guiscript->LoadScript( NextScript );
612 guiscript->RunFunction( NextScript, "OnLoad" );
616 bool GenerateAbilityTables()
618 FreeAbilityTables();
620 //range is: 0 - maximumability
621 int tablesize = MaximumAbility+1;
622 strmod = (ieWordSigned *) malloc (tablesize * 4 * sizeof(ieWordSigned) );
623 if (!strmod)
624 return false;
625 strmodex = (ieWordSigned *) malloc (101 * 4 * sizeof(ieWordSigned) );
626 if (!strmodex)
627 return false;
628 intmod = (ieWordSigned *) malloc (tablesize * 3 * sizeof(ieWordSigned) );
629 if (!intmod)
630 return false;
631 dexmod = (ieWordSigned *) malloc (tablesize * 3 * sizeof(ieWordSigned) );
632 if (!dexmod)
633 return false;
634 conmod = (ieWordSigned *) malloc (tablesize * 5 * sizeof(ieWordSigned) );
635 if (!conmod)
636 return false;
637 chrmod = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) );
638 if (!chrmod)
639 return false;
640 lorebon = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) );
641 if (!lorebon)
642 return false;
643 wisbon = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) );
644 if (!wisbon)
645 return false;
646 return true;
649 bool Interface::ReadAbilityTable(const ieResRef tablename, ieWordSigned *mem, int columns, int rows)
651 AutoTable tab(tablename);
652 if (!tab) {
653 return false;
655 //this is a hack for rows not starting at 0 in some cases
656 int fix = 0;
657 const char * tmp = tab->GetRowName(0);
658 if (tmp && (tmp[0]!='0')) {
659 fix = atoi(tmp);
660 for (int i=0;i<fix;i++) {
661 for (int j=0;j<columns;j++) {
662 mem[rows*j+i]=(ieWordSigned) strtol(tab->QueryField(0,j),NULL,0 );
666 for (int j=0;j<columns;j++) {
667 for( int i=0;i<rows-fix;i++) {
668 mem[rows*j+i+fix] = (ieWordSigned) strtol(tab->QueryField(i,j),NULL,0 );
671 return true;
674 bool Interface::ReadAbilityTables()
676 bool ret = GenerateAbilityTables();
677 if (!ret)
678 return ret;
679 ret = ReadAbilityTable("strmod", strmod, 4, MaximumAbility + 1);
680 if (!ret)
681 return ret;
682 ret = ReadAbilityTable("strmodex", strmodex, 4, 101);
683 //3rd ed doesn't have strmodex, but has a maximum of 40
684 if (!ret && (MaximumAbility<=25) )
685 return ret;
686 ret = ReadAbilityTable("intmod", intmod, 3, MaximumAbility + 1);
687 if (!ret)
688 return ret;
689 ret = ReadAbilityTable("hpconbon", conmod, 5, MaximumAbility + 1);
690 if (!ret)
691 return ret;
692 if (!HasFeature(GF_3ED_RULES)) {
693 //no lorebon in iwd2???
694 ret = ReadAbilityTable("lorebon", lorebon, 1, MaximumAbility + 1);
695 if (!ret)
696 return ret;
697 //no dexmod in iwd2???
698 ret = ReadAbilityTable("dexmod", dexmod, 3, MaximumAbility + 1);
699 if (!ret)
700 return ret;
702 //this table is a single row (not a single column)
703 ret = ReadAbilityTable("chrmodst", chrmod, MaximumAbility + 1, 1);
704 if (!ret)
705 return ret;
706 if (HasFeature(GF_WISDOM_BONUS)) {
707 ret = ReadAbilityTable("wisxpbon", wisbon, 1, MaximumAbility + 1);
708 if (!ret)
709 return ret;
711 return true;
714 bool Interface::ReadGameTimeTable()
716 AutoTable table("gametime");
717 if (!table) {
718 return false;
721 Time.round_sec = atoi(table->QueryField("ROUND_SECONDS", "DURATION"));
722 Time.turn_sec = atoi(table->QueryField("TURN_SECONDS", "DURATION"));
723 Time.round_size = Time.round_sec * AI_UPDATE_TIME;
724 Time.rounds_per_turn = Time.turn_sec / Time.round_sec;
726 return true;
729 bool Interface::ReadAuxItemTables()
731 int idx;
732 int table;
733 bool flag = true;
735 if (ItemExclTable) {
736 ItemExclTable->RemoveAll(NULL);
737 } else {
738 ItemExclTable = new Variables();
739 ItemExclTable->SetType(GEM_VARIABLES_INT);
741 table = gamedata->LoadTable( "itemexcl" );
743 AutoTable aa;
745 //don't report error when the file doesn't exist
746 if (aa.load("itemexcl")) {
747 idx = aa->GetRowCount();
748 while (idx--) {
749 ieResRef key;
751 strnlwrcpy(key,aa->GetRowName(idx),8);
752 ieDword value = strtol(aa->QueryField(idx,0),NULL,0);
753 ItemExclTable->SetAt(key, value);
756 if (ItemDialTable) {
757 ItemDialTable->RemoveAll(NULL);
758 } else {
759 ItemDialTable = new Variables();
760 ItemDialTable->SetType(GEM_VARIABLES_INT);
762 if (ItemDial2Table) {
763 ItemDial2Table->RemoveAll(NULL);
764 } else {
765 ItemDial2Table = new Variables();
766 ItemDial2Table->SetType(GEM_VARIABLES_STRING);
769 //don't report error when the file doesn't exist
770 if (aa.load("itemdial")) {
771 idx = aa->GetRowCount();
772 while (idx--) {
773 ieResRef key, dlgres;
775 strnlwrcpy(key,aa->GetRowName(idx),8);
776 ieDword value = strtol(aa->QueryField(idx,0),NULL,0);
777 ItemDialTable->SetAt(key, value);
778 strnlwrcpy(dlgres,aa->QueryField(idx,1),8);
779 ItemDial2Table->SetAtCopy(key, dlgres);
783 if (ItemTooltipTable) {
784 ItemTooltipTable->RemoveAll(ReleaseItemTooltip);
785 } else {
786 ItemTooltipTable = new Variables();
787 ItemTooltipTable->SetType(GEM_VARIABLES_POINTER);
790 //don't report error when the file doesn't exist
791 if (aa.load("tooltip")) {
792 idx = aa->GetRowCount();
793 while (idx--) {
794 ieResRef key;
795 int *tmppoi = (int *) malloc(sizeof(int)*3);
797 strnlwrcpy(key,aa->GetRowName(idx),8);
798 for (int i=0;i<3;i++) {
799 tmppoi[i] = atoi(aa->QueryField(idx,i));
801 ItemTooltipTable->SetAt(key, (void*)tmppoi);
804 return flag;
807 //Static
808 const char *Interface::GetDeathVarFormat()
810 return DeathVarFormat;
813 int Interface::GetItemExcl(const ieResRef itemname) const
815 ieDword value;
817 if (ItemExclTable && ItemExclTable->Lookup(itemname, value)) {
818 return (int) value;
820 return 0;
823 int Interface::GetItemTooltip(const ieResRef itemname, int header, int identified)
825 int *value = NULL;
827 if (ItemTooltipTable) {
828 void* lookup = NULL;
829 ItemTooltipTable->Lookup(itemname, lookup);
830 value = (int*)lookup;
832 if (value && (value[header]>=0)) {
833 return value[header];
835 Item *item = gamedata->GetItem(itemname);
836 if (!item) {
837 return -1;
839 int ret = identified?item->ItemNameIdentified:item->ItemName;
840 gamedata->FreeItem(item, itemname, 0);
841 return ret;
844 int Interface::GetItemDialStr(const ieResRef itemname) const
846 ieDword value;
848 if (ItemDialTable && ItemDialTable->Lookup(itemname, value)) {
849 return (int) value;
851 return -1;
854 //second value is the item dialog resource returned by this method
855 int Interface::GetItemDialRes(const ieResRef itemname, ieResRef retval) const
857 if (ItemDial2Table && ItemDial2Table->Lookup(itemname, retval, sizeof(ieResRef))) {
858 return 1;
860 return 0;
863 bool Interface::ReadAreaAliasTable(const ieResRef tablename)
865 if (AreaAliasTable) {
866 AreaAliasTable->RemoveAll(NULL);
867 } else {
868 AreaAliasTable = new Variables();
869 AreaAliasTable->SetType(GEM_VARIABLES_INT);
872 AutoTable aa(tablename);
873 if (!aa) {
874 //don't report error when the file doesn't exist
875 return true;
878 int idx = aa->GetRowCount();
879 while (idx--) {
880 ieResRef key;
882 strnlwrcpy(key,aa->GetRowName(idx),8);
883 ieDword value = atoi(aa->QueryField(idx,0));
884 AreaAliasTable->SetAt(key, value);
886 return true;
889 //this isn't const
890 int Interface::GetAreaAlias(const ieResRef areaname) const
892 ieDword value;
894 if (AreaAliasTable && AreaAliasTable->Lookup(areaname, value)) {
895 return (int) value;
897 return -1;
900 bool Interface::ReadMusicTable(const ieResRef tablename, int col) {
901 AutoTable tm(tablename);
902 if (!tm)
903 return false;
905 for (unsigned int i = 0; i < tm->GetRowCount(); i++) {
906 musiclist.push_back(strdup(tm->QueryField(i, col)));
909 return true;
912 bool Interface::ReadDamageTypeTable() {
913 AutoTable tm("dmgtypes");
914 if (!tm)
915 return false;
917 DamageInfoStruct di;
918 for (ieDword i = 0; i < tm->GetRowCount(); i++) {
919 di.strref = displaymsg->GetStringReference(atoi(tm->QueryField(i, 0)));
920 di.resist_stat = TranslateStat(tm->QueryField(i, 1));
921 di.value = strtol(tm->QueryField(i, 2), (char **) NULL, 16);
922 di.iwd_mod_type = atoi(tm->QueryField(i, 3));
923 DamageInfoMap.insert(std::make_pair <ieDword, DamageInfoStruct> ((ieDword)di.value, di));
926 return true;
929 bool Interface::ReadReputationModTable() {
930 AutoTable tm("reputati");
931 if (!tm)
932 return false;
934 reputationmod = (int **) calloc(21, sizeof(int *));
935 int cols = tm->GetColumnCount();
936 for (unsigned int i=0; i<20; i++) {
937 reputationmod[i] = (int *) calloc(cols, sizeof(int));
938 for (int j=0; j<cols; j++) {
939 reputationmod[i][j] = atoi(tm->QueryField(i, j));
943 return true;
946 bool Interface::ReadModalStates()
948 AutoTable table("modal");
949 if (!table)
950 return false;
952 ModalStatesStruct ms;
953 for (unsigned short i = 0; i < table->GetRowCount(); i++) {
954 strncpy(ms.spell, table->QueryField(i, 0), 8);
955 strncpy(ms.action, table->QueryField(i, 1), 16);
956 ms.entering_str = atoi(table->QueryField(i, 2));
957 ms.leaving_str = atoi(table->QueryField(i, 3));
958 ms.failed_str = atoi(table->QueryField(i, 4));
959 ms.aoe_spell = atoi(table->QueryField(i, 5));
960 ModalStates.push_back(ms);
963 return true;
966 //Not a constant anymore, we let the caller set the entry to zero
967 char *Interface::GetMusicPlaylist(int SongType) const {
968 if (SongType < 0 || (unsigned int)SongType >= musiclist.size())
969 return NULL;
971 return musiclist[SongType];
974 static const Color white = {0xff,0xff,0xff,0xff};
975 static const Color black = {0x00,0x00,0x00,0xff};
976 static const Region bg( 0, 0, 100, 30 );
978 /** this is the main loop */
979 void Interface::Main()
981 ieDword FullScreen = 0;
982 ieDword brightness = 10;
983 ieDword contrast = 5;
984 ieDword speed = 10;
986 vars->Lookup("FullScreen", FullScreen);
987 video->CreateDisplay( Width, Height, Bpp, FullScreen);
988 video->SetDisplayTitle( GameName, GameType );
989 vars->Lookup("Brightness Correction", brightness);
990 vars->Lookup("Gamma Correction", contrast);
991 vars->Lookup("Mouse Scroll Speed", speed);
992 video->SetGamma(brightness, contrast);
993 SetMouseScrollSpeed((int) speed);
994 if (vars->Lookup("Tooltips", TooltipDelay)) {
995 // the games store the slider position*10, not the actual delay
996 TooltipDelay *= TOOLTIP_DELAY_FACTOR/10;
999 Font* fps = GetFont( ( unsigned int ) 0 );
1000 char fpsstring[40]={"???.??? fps"};
1001 unsigned long frame = 0, time, timebase;
1002 GetTime(timebase);
1003 double frames = 0.0;
1004 Palette* palette = CreatePalette( white, black );
1005 do {
1006 //don't change script when quitting is pending
1008 while (QuitFlag) {
1009 HandleFlags();
1011 if (EventFlag) {
1012 HandleEvents();
1014 HandleGUIBehaviour();
1016 GameLoop();
1017 DrawWindows();
1018 if (DrawFPS) {
1019 frame++;
1020 GetTime( time );
1021 if (time - timebase > 1000) {
1022 frames = ( frame * 1000.0 / ( time - timebase ) );
1023 timebase = time;
1024 frame = 0;
1025 sprintf( fpsstring, "%.3f fps", frames );
1027 video->DrawRect( bg, black );
1028 fps->Print( bg,
1029 ( unsigned char * ) fpsstring, palette,
1030 IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_MIDDLE, true );
1032 } while (video->SwapBuffers() == GEM_OK);
1033 gamedata->FreePalette( palette );
1036 int Interface::ReadResRefTable(const ieResRef tablename, ieResRef *&data)
1038 int count = 0;
1040 if (data) {
1041 free(data);
1042 data = NULL;
1044 AutoTable tm(tablename);
1045 if (!tm) {
1046 printStatus( "ERROR", LIGHT_RED );
1047 printf( "Cannot find %s.2da.\n",tablename );
1048 return 0;
1050 count = tm->GetRowCount();
1051 data = (ieResRef *) calloc( count, sizeof(ieResRef) );
1052 for (int i = 0; i < count; i++) {
1053 strnlwrcpy( data[i], tm->QueryField( i, 0 ), 8 );
1054 //* marks an empty resource
1055 if (data[i][0]=='*') {
1056 data[i][0]=0;
1059 return count;
1062 int Interface::LoadSprites()
1064 ieDword i;
1065 int size;
1066 if (!IsAvailable( IE_2DA_CLASS_ID )) {
1067 printf( "No 2DA Importer Available.\nTermination in Progress...\n" );
1068 return GEM_ERROR;
1071 //loading cursors
1072 AnimationFactory* anim;
1073 anim = (AnimationFactory*) gamedata->GetFactoryResource("cursors", IE_BAM_CLASS_ID);
1074 if (anim)
1076 CursorCount = anim->GetCycleCount();
1077 Cursors = new Sprite2D * [CursorCount];
1078 for (int i = 0; i < CursorCount; i++) {
1079 Cursors[i] = anim->GetFrame( 0, (ieByte) i );
1082 printMessage( "Core", "Loading Cursors...", WHITE );
1084 // this is the last existing cursor type
1085 if (CursorCount<IE_CURSOR_WAY) {
1086 printStatus( "ERROR", LIGHT_RED );
1087 return GEM_ERROR;
1089 video->SetCursor( Cursors[0], Cursors[1] );
1090 printStatus( "OK", LIGHT_GREEN );
1092 // Load fog-of-war bitmaps
1093 anim = (AnimationFactory*) gamedata->GetFactoryResource("fogowar", IE_BAM_CLASS_ID);
1094 printMessage( "Core", "Loading Fog-Of-War bitmaps...", WHITE );
1095 if (!anim || anim->GetCycleSize( 0 ) != 8) {
1096 // unknown type of fog anim
1097 printStatus( "ERROR", LIGHT_RED );
1098 return GEM_ERROR;
1101 FogSprites[0] = NULL;
1102 FogSprites[1] = anim->GetFrame( 0, 0 );
1103 FogSprites[2] = anim->GetFrame( 1, 0 );
1104 FogSprites[3] = anim->GetFrame( 2, 0 );
1106 FogSprites[4] = video->MirrorSpriteVertical( FogSprites[1], false );
1108 FogSprites[5] = NULL;
1110 FogSprites[6] = video->MirrorSpriteVertical( FogSprites[3], false );
1112 FogSprites[7] = NULL;
1114 FogSprites[8] = video->MirrorSpriteHorizontal( FogSprites[2], false );
1116 FogSprites[9] = video->MirrorSpriteHorizontal( FogSprites[3], false );
1118 FogSprites[10] = NULL;
1119 FogSprites[11] = NULL;
1121 FogSprites[12] = video->MirrorSpriteHorizontal( FogSprites[6], false );
1123 FogSprites[16] = anim->GetFrame( 3, 0 );
1124 FogSprites[17] = anim->GetFrame( 4, 0 );
1125 FogSprites[18] = anim->GetFrame( 5, 0 );
1126 FogSprites[19] = anim->GetFrame( 6, 0 );
1128 FogSprites[20] = video->MirrorSpriteVertical( FogSprites[17], false );
1130 FogSprites[21] = NULL;
1132 FogSprites[23] = NULL;
1134 FogSprites[24] = video->MirrorSpriteHorizontal( FogSprites[18], false );
1136 FogSprites[25] = anim->GetFrame( 7, 0 );
1139 Sprite2D *tmpsprite = video->MirrorSpriteVertical( FogSprites[25], false );
1140 FogSprites[22] = video->MirrorSpriteHorizontal( tmpsprite, false );
1141 video->FreeSprite( tmpsprite );
1144 FogSprites[26] = NULL;
1145 FogSprites[27] = NULL;
1148 Sprite2D *tmpsprite = video->MirrorSpriteVertical( FogSprites[19], false );
1149 FogSprites[28] = video->MirrorSpriteHorizontal( tmpsprite, false );
1150 video->FreeSprite( tmpsprite );
1153 i = 0;
1154 vars->Lookup("3D Acceleration", i);
1155 if (i) {
1156 for(i=0;i<sizeof(FogSprites)/sizeof(Sprite2D *);i++ ) {
1157 if (FogSprites[i]) {
1158 Sprite2D* alphasprite = video->CreateAlpha( FogSprites[i] );
1159 video->FreeSprite ( FogSprites[i] );
1160 FogSprites[i] = alphasprite;
1165 printStatus( "OK", LIGHT_GREEN );
1167 // Load ground circle bitmaps (PST only)
1168 //block required due to msvc6.0 incompatibility
1169 for (size = 0; size < MAX_CIRCLE_SIZE; size++) {
1170 if (GroundCircleBam[size][0]) {
1171 anim = (AnimationFactory*) gamedata->GetFactoryResource(GroundCircleBam[size], IE_BAM_CLASS_ID);
1172 if (!anim || anim->GetCycleCount() != 6) {
1173 // unknown type of circle anim
1174 printMessage( "Core", "Loading Ground circle bitmaps...", WHITE );
1175 printStatus( "ERROR", LIGHT_RED );
1176 return GEM_ERROR;
1179 for (int i = 0; i < 6; i++) {
1180 Sprite2D* sprite = anim->GetFrame( 0, (ieByte) i );
1181 if (GroundCircleScale[size]) {
1182 GroundCircles[size][i] = video->SpriteScaleDown( sprite, GroundCircleScale[size] );
1183 video->FreeSprite( sprite );
1184 } else {
1185 GroundCircles[size][i] = sprite;
1191 printMessage( "Core", "Loading Ground circle bitmaps...", WHITE );
1192 printStatus( "OK", LIGHT_GREEN );
1194 printMessage( "Core", "Loading Fonts...\n", WHITE );
1195 AutoTable tab("fonts");
1196 if (!tab) {
1197 printStatus( "ERROR", LIGHT_RED );
1198 printf( "Cannot find fonts.2da.\nTermination in Progress...\n" );
1199 return GEM_ERROR;
1200 } else {
1201 PluginHolder<AnimationMgr> bamint(IE_BAM_CLASS_ID);
1202 if (!bamint) {
1203 printStatus( "ERROR", LIGHT_RED );
1204 printf( "No BAM Importer Available.\nTermination in Progress...\n" );
1205 return GEM_ERROR;
1207 DataStream* str = NULL;
1209 int count = tab->GetRowCount();
1210 for (int i = 0; i < count; i++) {
1211 const char* ResRef = tab->QueryField( i, 0 );
1212 int needpalette = atoi( tab->QueryField( i, 1 ) );
1213 int first_char = atoi( tab->QueryField( i, 2 ) );
1214 str = gamedata->GetResource( ResRef, IE_BAM_CLASS_ID );
1215 if (!bamint->Open( str, true )) {
1216 continue;
1218 Font* fnt = bamint->GetFont();
1219 if (!fnt) {
1220 continue;
1222 strnlwrcpy( fnt->ResRef, ResRef, 8 );
1223 if (needpalette) {
1225 Color fore = {0xff, 0xff, 0xff, 0};
1226 Color back = {0x00, 0x00, 0x00, 0};
1227 if (!strnicmp( TooltipFont, ResRef, 8) ) {
1228 if (TooltipColor.a==0xff) {
1229 fore = TooltipColor;
1230 } else {
1231 fore = back;
1232 back = TooltipColor;
1235 Palette* pal = CreatePalette( fore, back );
1236 pal->CreateShadedAlphaChannel();
1237 fnt->SetPalette(pal);
1238 gamedata->FreePalette( pal );
1240 fnt->SetFirstChar( (ieByte) first_char );
1241 fonts.push_back( fnt );
1244 printMessage( "Core", "Fonts Loaded...", WHITE );
1245 printStatus( "OK", LIGHT_GREEN );
1247 if (TooltipBackResRef[0]) {
1248 anim = (AnimationFactory*) gamedata->GetFactoryResource(TooltipBackResRef, IE_BAM_CLASS_ID);
1249 printMessage( "Core", "Initializing Tooltips...", WHITE );
1250 if (!anim) {
1251 printStatus( "ERROR", LIGHT_RED );
1252 return GEM_ERROR;
1254 TooltipBack = new Sprite2D * [3];
1255 for (int i = 0; i < 3; i++) {
1256 TooltipBack[i] = anim->GetFrame( 0, (ieByte) i );
1257 TooltipBack[i]->XPos = 0;
1258 TooltipBack[i]->YPos = 0;
1260 printStatus( "OK", LIGHT_GREEN );
1263 return GEM_OK;
1266 int Interface::Init()
1268 plugin_flags = new Variables();
1269 plugin_flags->SetType( GEM_VARIABLES_INT );
1271 printMessage( "Core", "Initializing the Event Manager...", WHITE );
1272 evntmgr = new EventMgr();
1274 printMessage( "Core", "Initializing Variables Dictionary...", WHITE );
1275 vars = new Variables();
1276 if (!vars) {
1277 printStatus( "ERROR", LIGHT_RED );
1278 return GEM_ERROR;
1281 vars->SetType( GEM_VARIABLES_INT );
1282 vars->SetAt( "Volume Ambients", 100 );
1283 vars->SetAt( "Volume Movie", 100 );
1284 vars->SetAt( "Volume Music", 100 );
1285 vars->SetAt( "Volume SFX", 100 );
1286 vars->SetAt( "Volume Voices", 100 );
1287 printStatus( "OK", LIGHT_GREEN );
1289 if (!LoadConfig()) {
1290 return GEM_ERROR;
1292 printMessage( "Core", "Starting Plugin Manager...\n", WHITE );
1293 PluginMgr *plugin = PluginMgr::Get();
1294 plugin->LoadPlugins(PluginsPath);
1295 if (plugin && plugin->GetPluginCount()) {
1296 printMessage( "Core", "Plugin Loading Complete...", WHITE );
1297 printStatus( "OK", LIGHT_GREEN );
1298 } else {
1299 printMessage( "Core", "Plugin Loading Failed, check path...", YELLOW);
1300 printStatus( "ERROR", LIGHT_RED );
1301 return GEM_ERROR;
1303 plugin->RunInitializers();
1305 time_t t;
1306 t = time( NULL );
1307 srand( ( unsigned int ) t );
1308 #ifdef _DEBUG
1309 FileStreamPtrCount = 0;
1310 CachedFileStreamPtrCount = 0;
1311 #endif
1312 printMessage( "Core", "GemRB Core Initialization...\n", WHITE );
1313 printStatus( "OK", LIGHT_GREEN );
1314 printMessage( "Core", "Initializing Video Driver...", WHITE );
1315 video = ( Video * ) PluginMgr::Get()->GetDriver(&Video::ID, VideoDriverName.c_str());
1316 if (!video) {
1317 printStatus( "ERROR", LIGHT_RED );
1318 printf( "No Video Driver Available.\nTermination in Progress...\n" );
1319 return GEM_ERROR;
1321 if (video->Init() == GEM_ERROR) {
1322 printStatus( "ERROR", LIGHT_RED );
1323 printf( "Cannot Initialize Video Driver.\nTermination in Progress...\n" );
1324 return GEM_ERROR;
1326 Color defcolor={255,255,255,200};
1327 SetInfoTextColor(defcolor);
1328 printStatus( "OK", LIGHT_GREEN );
1331 printMessage( "Core", "Initializing Search Path...", WHITE );
1332 if (!IsAvailable( PLUGIN_RESOURCE_DIRECTORY )) {
1333 printf( "no DirectoryImporter! " );
1334 printStatus( "ERROR", LIGHT_RED );
1335 return GEM_ERROR;
1338 char path[_MAX_PATH];
1340 PathJoin( path, CachePath, NULL);
1341 gamedata->AddSource(path, "Cache", PLUGIN_RESOURCE_DIRECTORY);
1343 PathJoin( path, GemRBOverridePath, "override", GameType, NULL);
1344 gamedata->AddSource(path, "GemRB Override", PLUGIN_RESOURCE_DIRECTORY);
1346 size_t i;
1347 for (i = 0; i < ModPath.size(); ++i)
1348 gamedata->AddSource(ModPath[i].c_str(), "Mod paths", PLUGIN_RESOURCE_DIRECTORY);
1350 PathJoin( path, GemRBOverridePath, "override", "shared", NULL);
1351 gamedata->AddSource(path, "shared GemRB Override", PLUGIN_RESOURCE_DIRECTORY);
1353 PathJoin( path, GamePath, GameOverridePath, NULL);
1354 gamedata->AddSource(path, "Override", PLUGIN_RESOURCE_DIRECTORY);
1356 PathJoin( path, GamePath, GameSoundsPath, NULL);
1357 gamedata->AddSource(path, "Sounds", PLUGIN_RESOURCE_DIRECTORY);
1359 PathJoin( path, GamePath, GameScriptsPath, NULL);
1360 gamedata->AddSource(path, "Scripts", PLUGIN_RESOURCE_DIRECTORY);
1362 PathJoin( path, GamePath, GamePortraitsPath, NULL);
1363 gamedata->AddSource(path, "Portraits", PLUGIN_RESOURCE_DIRECTORY);
1365 PathJoin( path, GamePath, GameDataPath, NULL);
1366 gamedata->AddSource(path, "Data", PLUGIN_RESOURCE_DIRECTORY);
1368 //IWD2 movies are on the CD but not in the BIF
1369 for (i = 0; i < MAX_CD; i++) {
1370 for (size_t j=0;j<CD[i].size();j++) {
1371 char description[] = {'C', 'D', '1'+i, '/', 'd', 'a', 't', 'a', '\0'};
1372 PathJoin( path, CD[i][j].c_str(), GameDataPath, NULL);
1373 gamedata->AddSource(path, description, PLUGIN_RESOURCE_DIRECTORY);
1377 printStatus( "OK", LIGHT_GREEN );
1381 printMessage( "Core", "Initializing KEY Importer...", WHITE );
1382 char ChitinPath[_MAX_PATH];
1383 PathJoin( ChitinPath, GamePath, "chitin.key", NULL );
1384 if (!gamedata->AddSource(ChitinPath, "chitin.key", PLUGIN_RESOURCE_KEY)) {
1385 printStatus( "ERROR", LIGHT_RED );
1386 return GEM_ERROR;
1388 printStatus( "OK", LIGHT_GREEN );
1391 printMessage( "Core", "Reading Game Options...\n", WHITE );
1392 if (!LoadGemRBINI())
1394 printf( "Cannot Load INI\nTermination in Progress...\n" );
1395 return GEM_ERROR;
1398 //loading baldur.ini
1400 char ini_path[_MAX_PATH];
1401 PathJoin( ini_path, GamePath, INIConfig, NULL );
1402 LoadINI( ini_path );
1403 int i;
1404 for (i = 0; i < 8; i++) {
1405 if (INIConfig[i] == '.')
1406 break;
1407 GameNameResRef[i] = INIConfig[i];
1409 GameNameResRef[i] = 0;
1412 printMessage( "Core", "Creating Projectile Server...\n", WHITE );
1413 projserv = new ProjectileServer();
1414 if (!projserv->GetHighestProjectileNumber()) {
1415 printStatus( "ERROR", LIGHT_RED );
1416 printf( "No projectiles are available...\n" );
1419 printMessage( "Core", "Checking for Dialogue Manager...", WHITE );
1420 if (!IsAvailable( IE_TLK_CLASS_ID )) {
1421 printStatus( "ERROR", LIGHT_RED );
1422 printf( "No TLK Importer Available.\nTermination in Progress...\n" );
1423 return GEM_ERROR;
1425 printStatus( "OK", LIGHT_GREEN );
1426 strings = PluginHolder<StringMgr>(IE_TLK_CLASS_ID);
1427 printMessage( "Core", "Loading Dialog.tlk file...", WHITE );
1428 char strpath[_MAX_PATH];
1429 PathJoin( strpath, GamePath, dialogtlk, NULL );
1430 FileStream* fs = new FileStream();
1431 if (!fs->Open( strpath, true )) {
1432 printStatus( "ERROR", LIGHT_RED );
1433 printf( "Cannot find Dialog.tlk.\nTermination in Progress...\n" );
1434 delete fs;
1435 return GEM_ERROR;
1437 printStatus( "OK", LIGHT_GREEN );
1438 strings->Open( fs, true );
1441 printMessage( "Core", "Loading Palettes...\n", WHITE );
1442 ResourceHolder<ImageMgr> pal16im(Palette16);
1443 if (pal16im)
1444 pal16 = pal16im->GetImage();
1445 ResourceHolder<ImageMgr> pal32im(Palette32);
1446 if (pal32im)
1447 pal32 = pal32im->GetImage();
1448 ResourceHolder<ImageMgr> pal256im(Palette256);
1449 if (pal256im)
1450 pal256 = pal256im->GetImage();
1451 if (!pal16 || !pal32 || !pal256) {
1452 printStatus( "ERROR", LIGHT_RED );
1453 return GEM_ERROR;
1455 printMessage( "Core", "Palettes Loaded\n", WHITE );
1458 if (!IsAvailable( IE_BAM_CLASS_ID )) {
1459 printStatus( "ERROR", LIGHT_RED );
1460 printf( "No BAM Importer Available.\nTermination in Progress...\n" );
1461 return GEM_ERROR;
1464 printMessage( "Core", "Initializing stock sounds...\n", WHITE );
1465 DSCount = ReadResRefTable ("defsound", DefSound);
1466 if (DSCount == 0) {
1467 printStatus( "ERROR", LIGHT_RED );
1468 printf( "Cannot find defsound.2da.\nTermination in Progress...\n" );
1469 return GEM_ERROR;
1472 printStatus( "OK", LIGHT_GREEN );
1473 printMessage( "Core", "Broadcasting Event Manager...", WHITE );
1474 video->SetEventMgr( evntmgr );
1475 printStatus( "OK", LIGHT_GREEN );
1476 printMessage( "Core", "Initializing Window Manager...", WHITE );
1477 windowmgr = PluginHolder<WindowMgr>(IE_CHU_CLASS_ID);
1478 if (windowmgr == NULL) {
1479 printStatus( "ERROR", LIGHT_RED );
1480 return GEM_ERROR;
1482 printStatus( "OK", LIGHT_GREEN );
1483 printMessage( "Core", "Initializing GUI Script Engine...", WHITE );
1484 guiscript = PluginHolder<ScriptEngine>(IE_GUI_SCRIPT_CLASS_ID);
1485 if (guiscript == NULL) {
1486 printStatus( "ERROR", LIGHT_RED );
1487 return GEM_ERROR;
1489 if (!guiscript->Init()) {
1490 printStatus( "ERROR", LIGHT_RED );
1491 return GEM_ERROR;
1493 printStatus( "OK", LIGHT_GREEN );
1494 strcpy( NextScript, "Start" );
1496 int ret = LoadSprites();
1497 if (ret) return ret;
1499 Sprite2D *tmpsprite = GetCursorSprite();
1500 printMessage( "Core", "Setting up the Console...", WHITE );
1501 QuitFlag = QF_CHANGESCRIPT;
1502 console = new Console();
1503 console->XPos = 0;
1504 console->YPos = (ieWord) (Height - 25);
1505 console->Width = (ieWord) Width;
1506 console->Height = 25;
1507 console->SetFont( fonts[0] );
1508 if (!tmpsprite) {
1509 printStatus( "ERROR", LIGHT_RED );
1510 return GEM_ERROR;
1512 console->SetCursor (tmpsprite);
1513 printStatus( "OK", LIGHT_GREEN );
1515 printMessage( "Core", "Starting up the Sound Driver...", WHITE );
1516 AudioDriver = ( Audio * ) PluginMgr::Get()->GetDriver(&Audio::ID, AudioDriverName.c_str());
1517 if (AudioDriver == NULL) {
1518 printStatus( "ERROR", LIGHT_RED );
1519 return GEM_ERROR;
1521 if (!AudioDriver->Init()) {
1522 printStatus( "ERROR", LIGHT_RED );
1523 return GEM_ERROR;
1525 printStatus( "OK", LIGHT_GREEN );
1527 printMessage( "Core", "Allocating SaveGameIterator...", WHITE );
1528 sgiterator = new SaveGameIterator();
1529 if (sgiterator == NULL) {
1530 printStatus( "ERROR", LIGHT_RED );
1531 return GEM_ERROR;
1533 printStatus( "OK", LIGHT_GREEN );
1535 //no need of strdup, variables do copy the key!
1536 vars->SetAt( "SkipIntroVideos", (unsigned long)SkipIntroVideos );
1537 vars->SetAt( "GUIEnhancements", (unsigned long)GUIEnhancements );
1539 printMessage( "Core", "Initializing Token Dictionary...", WHITE );
1540 tokens = new Variables();
1541 if (!tokens) {
1542 printStatus( "ERROR", LIGHT_RED );
1543 return GEM_ERROR;
1545 tokens->SetType( GEM_VARIABLES_STRING );
1546 printStatus( "OK", LIGHT_GREEN );
1548 printMessage( "Core", "Initializing Music Manager...", WHITE );
1549 music = PluginHolder<MusicMgr>(IE_MUS_CLASS_ID);
1550 if (!music) {
1551 printStatus( "ERROR", LIGHT_RED );
1552 return GEM_ERROR;
1554 printStatus( "OK", LIGHT_GREEN );
1556 printMessage("Core", "Loading music list...\n", WHITE );
1557 if (HasFeature( GF_HAS_SONGLIST )) {
1558 ret = ReadMusicTable("songlist", 1);
1559 } else {
1560 /*since bg1 and pst has no .2da for songlist,
1561 we must supply one in the gemrb/override folder.
1562 It should be: music.2da, first column is a .mus filename*/
1563 ret = ReadMusicTable("music", 0);
1565 if (ret) {
1566 printStatus( "OK", LIGHT_GREEN );
1567 } else {
1568 printStatus( "NOT FOUND", YELLOW );
1571 if (HasFeature( GF_RESDATA_INI )) {
1572 printMessage( "Core", "Loading resource data File...", WHITE );
1573 INIresdata = PluginHolder<DataFileMgr>(IE_INI_CLASS_ID);
1574 DataStream* ds = gamedata->GetResource("resdata", IE_INI_CLASS_ID);
1575 if (!INIresdata->Open( ds, true )) {
1576 printStatus( "ERROR", LIGHT_RED );
1577 } else {
1578 printStatus( "OK", LIGHT_GREEN );
1582 if (HasFeature( GF_HAS_PARTY_INI )) {
1583 printMessage( "Core", "Loading precreated teams setup...\n",
1584 WHITE );
1585 INIparty = PluginHolder<DataFileMgr>(IE_INI_CLASS_ID);
1586 FileStream* fs = new FileStream();
1587 char tINIparty[_MAX_PATH];
1588 PathJoin( tINIparty, GamePath, "Party.ini", NULL );
1589 fs->Open( tINIparty, true );
1590 if (!INIparty->Open( fs, true )) {
1591 printStatus( "ERROR", LIGHT_RED );
1592 } else {
1593 printStatus( "OK", LIGHT_GREEN );
1597 if (HasFeature(GF_IWD2_DEATHVARFORMAT)) {
1598 memcpy(DeathVarFormat, IWD2DeathVarFormat, sizeof(ieVariable));
1601 if (HasFeature( GF_HAS_BEASTS_INI )) {
1602 printMessage( "Core", "Loading beasts definition File...\n",
1603 WHITE );
1604 INIbeasts = PluginHolder<DataFileMgr>(IE_INI_CLASS_ID);
1605 FileStream* fs = new FileStream();
1606 char tINIbeasts[_MAX_PATH];
1607 PathJoin( tINIbeasts, GamePath, "beast.ini", NULL );
1608 // FIXME: crashes if file does not open
1609 fs->Open( tINIbeasts, true );
1610 if (!INIbeasts->Open( fs, true )) {
1611 printStatus( "ERROR", LIGHT_RED );
1612 } else {
1613 printStatus( "OK", LIGHT_GREEN );
1616 printMessage( "Core", "Loading quests definition File...\n",
1617 WHITE );
1618 INIquests = PluginHolder<DataFileMgr>(IE_INI_CLASS_ID);
1619 FileStream* fs2 = new FileStream();
1620 char tINIquests[_MAX_PATH];
1621 PathJoin( tINIquests, GamePath, "quests.ini", NULL );
1622 // FIXME: crashes if file does not open
1623 fs2->Open( tINIquests, true );
1624 if (!INIquests->Open( fs2, true )) {
1625 printStatus( "ERROR", LIGHT_RED );
1626 } else {
1627 printStatus( "OK", LIGHT_GREEN );
1630 game = NULL;
1631 calendar = NULL;
1633 timer = new GlobalTimer();
1634 printMessage( "Core", "Bringing up the Global Timer...", WHITE );
1635 if (!timer) {
1636 printStatus( "ERROR", LIGHT_RED );
1637 return GEM_ERROR;
1639 printStatus( "OK", LIGHT_GREEN );
1641 ret = Init_EffectQueue();
1642 printMessage( "Core", "Initializing effects...", WHITE );
1643 if (!ret) {
1644 printStatus( "ERROR", LIGHT_RED );
1645 return GEM_ERROR;
1647 printStatus( "OK", LIGHT_GREEN );
1649 ret = InitItemTypes();
1650 printMessage( "Core", "Initializing Inventory Management...", WHITE );
1651 if (!ret) {
1652 printStatus( "ERROR", LIGHT_RED );
1653 return GEM_ERROR;
1655 printStatus( "OK", LIGHT_GREEN );
1657 displaymsg = new DisplayMessage();
1658 printMessage( "Core", "Initializing string constants...", WHITE );
1659 if (!displaymsg) {
1660 printStatus( "ERROR", LIGHT_RED );
1661 return GEM_ERROR;
1663 printStatus( "OK", LIGHT_GREEN );
1665 ret = ReadRandomItems();
1666 printMessage( "Core", "Initializing random treasure...", WHITE );
1667 if (ret) {
1668 printStatus( "OK", LIGHT_GREEN );
1670 else {
1671 printStatus( "ERROR", LIGHT_RED );
1674 ret = ReadAbilityTables();
1675 printMessage( "Core", "Initializing ability tables...", WHITE );
1676 if (!ret) {
1677 printStatus( "ERROR", LIGHT_RED );
1678 return GEM_ERROR;
1680 printStatus( "OK", LIGHT_GREEN );
1682 ret = ReadReputationModTable();
1683 printMessage( "Core", "Reading reputation mod table...", WHITE);
1684 if (ret) {
1685 printStatus( "OK", LIGHT_GREEN );
1686 } else {
1687 printStatus( "NOT FOUND", LIGHT_RED );
1690 if ( gamedata->Exists("WMAPLAY", IE_2DA_CLASS_ID) ) {
1691 ret = ReadAreaAliasTable( "WMAPLAY" );
1692 printMessage( "Core", "Initializing area aliases...", WHITE );
1693 if (ret) {
1694 printStatus( "OK", LIGHT_GREEN );
1696 else {
1697 printStatus( "NOT FOUND", YELLOW );
1701 ret = ReadGameTimeTable();
1702 printMessage( "Core", "Reading game time table...", WHITE);
1703 if (!ret) {
1704 printStatus( "ERROR", LIGHT_RED );
1705 return GEM_ERROR;
1707 printStatus( "OK", LIGHT_GREEN );
1709 ret = ReadAuxItemTables();
1710 printMessage( "Core", "Reading item tables...", WHITE);
1711 if (!ret) {
1712 printStatus( "ERROR", LIGHT_RED );
1713 return GEM_ERROR;
1715 printStatus( "OK", LIGHT_GREEN );
1717 ret = ReadDamageTypeTable();
1718 printMessage( "Core", "Reading damage type table...", WHITE);
1719 if (!ret) {
1720 printStatus( "ERROR", LIGHT_RED );
1721 } else {
1722 printStatus( "OK", LIGHT_GREEN );
1725 ret = ReadModalStates();
1726 printMessage( "Core", "Reading modal states table...", WHITE);
1727 if (!ret) {
1728 printStatus( "ERROR", LIGHT_RED );
1729 return GEM_ERROR;
1730 } else {
1731 printStatus( "OK", LIGHT_GREEN );
1734 printMessage( "Core", "Reading game script tables...", WHITE);
1735 InitializeIEScript();
1736 printStatus( "OK", LIGHT_GREEN );
1738 printMessage( "Core", "Core Initialization Complete!\n", WHITE );
1740 return GEM_OK;
1743 bool Interface::IsAvailable(SClass_ID filetype) const
1745 return PluginMgr::Get()->IsAvailable( filetype );
1748 WorldMap *Interface::GetWorldMap(const char *map)
1750 int index = worldmap->FindAndSetCurrentMap(map?map:game->CurrentArea);
1751 return worldmap->GetWorldMap(index);
1754 ProjectileServer* Interface::GetProjectileServer() const
1756 return projserv;
1759 Video* Interface::GetVideoDriver() const
1761 return video.get();
1764 Audio* Interface::GetAudioDrv(void) const {
1765 return AudioDriver.get();
1768 const char* Interface::TypeExt(SClass_ID type) const
1770 switch (type) {
1771 case IE_2DA_CLASS_ID:
1772 return ".2da";
1774 case IE_ACM_CLASS_ID:
1775 return ".acm";
1777 case IE_ARE_CLASS_ID:
1778 return ".are";
1780 case IE_BAM_CLASS_ID:
1781 return ".bam";
1783 case IE_BCS_CLASS_ID:
1784 return ".bcs";
1786 case IE_BS_CLASS_ID:
1787 return ".bs";
1789 case IE_BIF_CLASS_ID:
1790 return ".bif";
1792 case IE_BIO_CLASS_ID:
1793 if (HasFeature(GF_BIOGRAPHY_RES)) {
1794 return ".res";
1796 return ".bio";
1798 case IE_BMP_CLASS_ID:
1799 return ".bmp";
1801 case IE_PNG_CLASS_ID:
1802 return ".png";
1804 case IE_CHR_CLASS_ID:
1805 return ".chr";
1807 case IE_CHU_CLASS_ID:
1808 return ".chu";
1810 case IE_CRE_CLASS_ID:
1811 return ".cre";
1813 case IE_DLG_CLASS_ID:
1814 return ".dlg";
1816 case IE_EFF_CLASS_ID:
1817 return ".eff";
1819 case IE_GAM_CLASS_ID:
1820 return ".gam";
1822 case IE_IDS_CLASS_ID:
1823 return ".ids";
1825 case IE_INI_CLASS_ID:
1826 return ".ini";
1828 case IE_ITM_CLASS_ID:
1829 return ".itm";
1831 case IE_MOS_CLASS_ID:
1832 return ".mos";
1834 case IE_MUS_CLASS_ID:
1835 return ".mus";
1837 case IE_MVE_CLASS_ID:
1838 return ".mve";
1840 case IE_OGG_CLASS_ID:
1841 return ".ogg";
1843 case IE_PLT_CLASS_ID:
1844 return ".plt";
1846 case IE_PRO_CLASS_ID:
1847 return ".pro";
1849 case IE_SAV_CLASS_ID:
1850 return ".sav";
1852 case IE_SPL_CLASS_ID:
1853 return ".spl";
1855 case IE_SRC_CLASS_ID:
1856 return ".src";
1858 case IE_STO_CLASS_ID:
1859 return ".sto";
1861 case IE_TIS_CLASS_ID:
1862 return ".tis";
1864 case IE_TLK_CLASS_ID:
1865 return ".tlk";
1867 case IE_TOH_CLASS_ID:
1868 return ".toh";
1870 case IE_TOT_CLASS_ID:
1871 return ".tot";
1873 case IE_VAR_CLASS_ID:
1874 return ".var";
1876 case IE_VVC_CLASS_ID:
1877 return ".vvc";
1879 case IE_WAV_CLASS_ID:
1880 return ".wav";
1882 case IE_WED_CLASS_ID:
1883 return ".wed";
1885 case IE_WFX_CLASS_ID:
1886 return ".wfx";
1888 case IE_WMP_CLASS_ID:
1889 return ".wmp";
1891 return NULL;
1894 void Interface::FreeString(char *&str) const
1896 if (str) {
1897 strings->FreeString(str);
1899 str = NULL;
1902 ieStrRef Interface::UpdateString(ieStrRef strref, const char *text) const
1904 return strings->UpdateString( strref, text );
1907 char* Interface::GetString(ieStrRef strref, ieDword options) const
1909 ieDword flags = 0;
1911 if (!(options & IE_STR_STRREFOFF)) {
1912 vars->Lookup( "Strref On", flags );
1914 return strings->GetString( strref, flags | options );
1917 void Interface::SetFeature(int flag, int position)
1919 if (position>=32) {
1920 position-=32;
1921 if (flag) {
1922 GameFeatures2 |= 1 << position;
1923 } else {
1924 GameFeatures2 &= ~( 1 << position );
1926 return;
1928 if (flag) {
1929 GameFeatures |= 1 << position;
1930 } else {
1931 GameFeatures &= ~( 1 << position );
1935 ieDword Interface::HasFeature(int position) const
1937 if (position>=32) {
1938 return GameFeatures2 & ( 1 << (position-32) );
1940 return GameFeatures & ( 1 << position );
1943 /** Search directories and load a config file */
1944 bool Interface::LoadConfig(void)
1946 #ifndef WIN32
1947 char path[_MAX_PATH];
1948 char name[_MAX_PATH];
1950 // Find directory where user stores GemRB configurations (~/.gemrb).
1951 // FIXME: Create it if it does not exist
1952 // Use current dir if $HOME is not defined (or bomb out??)
1954 char* s = getenv( "HOME" );
1955 if (s) {
1956 strcpy( UserDir, s );
1957 strcat( UserDir, "/."PACKAGE"/" );
1958 } else {
1959 strcpy( UserDir, "./" );
1962 // Find basename of this program. It does the same as basename (3),
1963 // but that's probably missing on some archs
1964 s = strrchr( argv[0], PathDelimiter );
1965 if (s) {
1966 s++;
1967 } else {
1968 s = argv[0];
1971 strcpy( name, s );
1972 //if (!name[0]) // FIXME: could this happen?
1973 // strcpy (name, PACKAGE); // ugly hack
1975 // If we were called as $0 -c <filename>, load config from filename
1976 if (argc > 2 && ! strcmp("-c", argv[1])) {
1977 if (LoadConfig( argv[2] )) {
1978 return true;
1979 } else {
1980 // Explicitly specified cfg file HAS to be present
1981 return false;
1985 // FIXME: temporary hack, to be deleted??
1986 if (LoadConfig( "GemRB.cfg" )) {
1987 return true;
1990 PathJoin( path, UserDir, name, NULL );
1991 strcat( path, ".cfg" );
1993 if (LoadConfig( path )) {
1994 return true;
1997 #ifdef SYSCONFDIR
1998 PathJoin( path, SYSCONFDIR, name, NULL );
1999 strcat( path, ".cfg" );
2001 if (LoadConfig( path )) {
2002 return true;
2004 #endif
2006 // Don't try with default binary name if we have tried it already
2007 if (!strcmp( name, PACKAGE )) {
2008 return false;
2011 PathJoin( path, UserDir, PACKAGE, NULL );
2012 strcat( path, ".cfg" );
2014 if (LoadConfig( path )) {
2015 return true;
2018 #ifdef SYSCONFDIR
2019 PathJoin( path, SYSCONFDIR, PACKAGE, NULL );
2020 strcat( path, ".cfg" );
2022 if (LoadConfig( path )) {
2023 return true;
2025 #endif
2027 return false;
2028 #else // WIN32
2029 // If we were called as $0 -c <filename>, load config from filename
2030 if (argc > 2 && ! strcmp("-c", argv[1])) {
2031 return LoadConfig( argv[2] );
2032 // Explicitly specified cfg file HAS to be present
2034 strcpy( UserDir, ".\\" );
2035 return LoadConfig( "GemRB.cfg" );
2036 #endif// WIN32
2039 bool Interface::LoadConfig(const char* filename)
2041 FILE* config;
2042 size_t i;
2044 printMessage("Config","Trying to open ", WHITE);
2045 textcolor(LIGHT_WHITE);
2046 printf("%s ", filename);
2047 config = fopen( filename, "rb" );
2048 if (config == NULL) {
2049 printStatus("NOT FOUND", LIGHT_RED);
2050 return false;
2052 char name[65], value[_MAX_PATH + 3];
2054 //once GemRB own format is working well, this might be set to 0
2055 SaveAsOriginal = 1;
2057 while (!feof( config )) {
2058 char rem;
2060 if (fread( &rem, 1, 1, config ) != 1)
2061 break;
2063 if (rem == '#') {
2064 //it should always return 0
2065 if (fscanf( config, "%*[^\r\n]%*[\r\n]" )!=0)
2066 break;
2067 continue;
2069 fseek( config, -1, SEEK_CUR );
2070 memset(value,'\0',_MAX_PATH + 3);
2071 //the * element is not counted
2072 if (fscanf( config, "%64[^= ] = %[^\r\n]%*[\r\n]", name, value )!=2)
2073 continue;
2074 for (i=_MAX_PATH + 2; i > 0; i--) {
2075 if (value[i] == '\0') continue;
2076 if (value[i] == ' ') {
2077 value[i] = '\0';
2078 } else {
2079 break;
2083 if (false) {
2084 #define CONFIG_INT(str, var) \
2085 } else if (stricmp(name, str) == 0) { \
2086 var ( atoi(value) )
2087 CONFIG_INT("Bpp", Bpp = );
2088 CONFIG_INT("CaseSensitive", CaseSensitive = );
2089 CONFIG_INT("DoubleClickDelay", evntmgr->SetDCDelay);
2090 CONFIG_INT("DrawFPS", DrawFPS = );
2091 CONFIG_INT("EnableCheatKeys", EnableCheatKeys);
2092 CONFIG_INT("EndianSwitch", DataStream::SetEndianSwitch);
2093 CONFIG_INT("FogOfWar", FogOfWar = );
2094 //CONFIG_INT("FullScreen", FullScreen = );
2095 CONFIG_INT("GUIEnhancements", GUIEnhancements = );
2096 CONFIG_INT("GameOnCD", GameOnCD = );
2097 CONFIG_INT("Height", Height = );
2098 CONFIG_INT("KeepCache", KeepCache = );
2099 CONFIG_INT("MultipleQuickSaves", GameControl::MultipleQuickSaves);
2100 CONFIG_INT("RepeatKeyDelay", evntmgr->SetRKDelay);
2101 CONFIG_INT("SaveAsOriginal", SaveAsOriginal = );
2102 CONFIG_INT("ScriptDebugMode", SetScriptDebugMode);
2103 CONFIG_INT("SkipIntroVideos", SkipIntroVideos = );
2104 CONFIG_INT("TooltipDelay", TooltipDelay = );
2105 CONFIG_INT("Width", Width = );
2106 #undef CONFIG_INT
2107 #define CONFIG_STRING(str, var) \
2108 } else if (stricmp(name, str) == 0) { \
2109 strncpy(var, value, sizeof(var))
2110 CONFIG_STRING("GameCharactersPath", GameCharactersPath);
2111 CONFIG_STRING("GameDataPath", GameDataPath);
2112 CONFIG_STRING("GameName", GameName);
2113 CONFIG_STRING("GameOverridePath", GameOverridePath);
2114 CONFIG_STRING("GamePortraitsPath", GamePortraitsPath);
2115 CONFIG_STRING("GameScriptsPath", GameScriptsPath);
2116 CONFIG_STRING("GameSoundsPath", GameSoundsPath);
2117 CONFIG_STRING("GameType", GameType);
2118 #undef CONFIG_STRING
2119 #define CONFIG_STRING(str, var) \
2120 } else if (stricmp(name, str) == 0) { \
2121 var = value
2122 CONFIG_STRING("AudioDriver", AudioDriverName);
2123 CONFIG_STRING("VideoDriver", VideoDriverName);
2124 #undef CONFIG_STRING
2125 #define CONFIG_PATH(str, var) \
2126 } else if (stricmp(name, str) == 0) { \
2127 strncpy(var, value, sizeof(var));
2128 CONFIG_PATH("CachePath", CachePath);
2129 CONFIG_PATH("GUIScriptsPath", GUIScriptsPath);
2130 CONFIG_PATH("GamePath", GamePath);
2131 CONFIG_PATH("GemRBOverridePath", GemRBOverridePath);
2132 CONFIG_PATH("GemRBPath", GemRBPath);
2133 CONFIG_PATH("PluginsPath", PluginsPath);
2134 CONFIG_PATH("SavePath", SavePath);
2135 #undef CONFIG_PATH
2136 } else if (stricmp( name, "ModPath" ) == 0) {
2137 for (char *path = strtok(value,SPathListSeparator);
2138 path;
2139 path = strtok(NULL,SPathListSeparator)) {
2140 ModPath.push_back(path);
2142 } else if (stricmp( name, "SkipPlugin" ) == 0) {
2143 plugin_flags->SetAt( value, PLF_SKIP );
2144 } else if (stricmp( name, "DelayPlugin" ) == 0) {
2145 plugin_flags->SetAt( value, PLF_DELAY );
2146 } else {
2147 for(i=0;i<MAX_CD;i++) {
2148 char keyname[] = { 'C', 'D', '1'+i, '\0' };
2149 if (stricmp(name, keyname) == 0) {
2150 for(char *path = strtok(value, SPathListSeparator);
2151 path;
2152 path = strtok(NULL,SPathListSeparator)) {
2153 CD[i].push_back(path);
2159 fclose( config );
2161 // WARNING: Don't move ResolveFilePath into the loop
2162 // Otherwise, it won't obey CaseSensitive set at the end
2163 // of the config file.
2165 if (!GameType[0]) {
2166 strcpy( GameType, "gemrb" );
2167 } else if (stricmp( GameType, "tob" ) == 0) {
2168 strncpy( GameType, "bg2", sizeof(GameType) );
2171 #ifdef DATADIR
2172 if (!GemRBPath[0]) {
2173 strcpy( GemRBPath, DATADIR );
2175 #endif
2176 ResolveFilePath(GemRBPath);
2178 if (!GemRBOverridePath[0]) {
2179 strcpy( GemRBOverridePath, GemRBPath );
2180 } else {
2181 ResolveFilePath(GemRBOverridePath);
2184 if (!PluginsPath[0]) {
2185 #ifdef PLUGINDIR
2186 strcpy( PluginsPath, PLUGINDIR );
2187 #else
2188 PathJoin( PluginsPath, GemRBPath, "plugins", NULL );
2189 #endif
2190 } else {
2191 ResolveFilePath( PluginsPath );
2194 if (!GUIScriptsPath[0]) {
2195 strcpy( GUIScriptsPath, GemRBPath );
2196 } else {
2197 ResolveFilePath( GUIScriptsPath );
2200 if (!GameName[0]) {
2201 strcpy( GameName, GEMRB_STRING );
2204 ResolveFilePath( GamePath );
2206 if (!SavePath[0]) {
2207 // FIXME: maybe should use UserDir instead of GamePath
2208 strcpy( SavePath, GamePath );
2209 } else {
2210 ResolveFilePath( SavePath );
2213 if (! CachePath[0]) {
2214 PathJoin( CachePath, UserDir, "Cache", NULL );
2215 } else {
2216 ResolveFilePath(CachePath);
2219 for (i = 0; i < MAX_CD; ++i) {
2220 if (!CD[i].size()) {
2221 char cd[] = { 'C', 'D', '1'+i, '\0' };
2222 char name[_MAX_PATH];
2224 PathJoin(name, GamePath, cd, NULL);
2225 CD[i].push_back(name);
2226 } else {
2227 size_t cnt = CD[i].size();
2228 while(cnt--) {
2229 ResolveFilePath( CD[i][cnt] );
2234 for (i = 0; i < ModPath.size(); ++i) {
2235 ResolveFilePath(ModPath[i]);
2238 FixPath( GUIScriptsPath, true );
2239 FixPath( PluginsPath, true );
2240 FixPath( GemRBPath, true );
2241 FixPath( GemRBOverridePath, true );
2243 if (GamePath[0]) {
2244 FixPath( GamePath, true );
2247 //FixPath( SavePath, false );
2248 //mkdir( SavePath, S_IREAD|S_IWRITE|S_IEXEC );
2249 //chmod( SavePath, S_IREAD|S_IWRITE|S_IEXEC );
2250 FixPath( SavePath, true );
2252 FixPath( CachePath, false );
2253 mkdir( CachePath, S_IREAD|S_IWRITE|S_IEXEC );
2254 chmod( CachePath, S_IREAD|S_IWRITE|S_IEXEC );
2256 printStatus( "OK", LIGHT_GREEN );
2257 if ( StupidityDetector( CachePath )) {
2258 printMessage("Core"," ",LIGHT_RED);
2259 printf( "Cache path %s doesn't exist, not a folder or contains alien files!\n", CachePath );
2260 return false;
2262 if (!KeepCache) DelTree((const char *) CachePath, false);
2263 FixPath( CachePath, true );
2265 return true;
2268 static void upperlower(int upper, int lower)
2270 pl_uppercase[lower]=(ieByte) upper;
2271 pl_lowercase[upper]=(ieByte) lower;
2274 static const char *game_flags[GF_COUNT+1]={
2275 "HasKaputz", //0 GF_HAS_KAPUTZ
2276 "AllStringsTagged", //1 GF_ALL_STRINGS_TAGGED
2277 "HasSongList", //2 GF_HAS_SONGLIST
2278 "TeamMovement", //3 GF_TEAM_MOVEMENT
2279 "UpperButtonText", //4 GF_UPPER_BUTTON_TEXT
2280 "LowerLabelText", //5 GF_LOWER_LABEL_TEXT
2281 "HasPartyIni", //6 GF_HAS_PARTY_INI
2282 "SoundFolders", //7 GF_SOUNDFOLDERS
2283 "IgnoreButtonFrames", //8 GF_IGNORE_BUTTON_FRAMES
2284 "OneByteAnimationID", //9 GF_ONE_BYTE_ANIMID
2285 "HasDPLAYER", //10GF_HAS_DPLAYER
2286 "HasEXPTABLE", //11GF_HAS_EXPTABLE
2287 "HasBeastsIni", //12GF_HAS_BEASTS_INI
2288 "HasDescIcon", //13GF_HAS_DESC_ICON
2289 "HasPickSound", //14GF_HAS_PICK_SOUND
2290 "IWDMapDimensions", //15GF_IWD_MAP_DIMENSIONS
2291 "AutomapIni", //16GF_AUTOMAP_INI
2292 "SmallFog", //17GF_SMALL_FOG
2293 "ReverseDoor", //18GF_REVERSE_DOOR
2294 "ProtagonistTalks", //19GF_PROTAGONIST_TALKS
2295 "HasSpellList", //20GF_HAS_SPELLLIST
2296 "IWD2ScriptName", //21GF_IWD2_SCRIPTNAME
2297 "DialogueScrolls", //22GF_DIALOGUE_SCROLLS
2298 "KnowWorld", //23GF_KNOW_WORLD
2299 "ReverseToHit", //24GF_REVERSE_TOHIT
2300 "SaveForHalfDamage", //25GF_SAVE_FOR_HALF
2301 "CharNameIsGabber", //26GF_CHARNAMEISGABBER
2302 "MagicBit", //27GF_MAGICBIT
2303 "CheckAbilities", //28GF_CHECK_ABILITIES
2304 "ChallengeRating", //29GF_CHALLENGERATING
2305 "SpellBookIconHack", //30GF_SPELLBOOKICONHACK
2306 "EnhancedEffects", //31GF_ENHANCED_EFFECTS
2307 "DeathOnZeroStat", //32GF_DEATH_ON_ZERO_STAT
2308 "SpawnIni", //33GF_SPAWN_INI
2309 "IWD2DeathVarFormat", //34GF_IWD2_DEATHVARFORMAT
2310 "HasResDataIni", //35GF_RESDATA_INI
2311 "OverrideCursorPos", //36GF_OVERRIDE_CURSORPOS
2312 "BreakableWeapons", //37GF_BREAKABLE_WEAPONS
2313 "3EdRules", //38GF_3ED_RULES
2314 "LevelslotPerClass", //39GF_LEVELSLOT_PER_CLASS
2315 "SelectiveMagicRes", //40GF_SELECTIVE_MAGIC_RES
2316 "HasHideInShadows", //41GF_HAS_HIDE_IN_SHADOWS
2317 "AreaVisitedVar", //42GF_AREA_VISITED_VAR
2318 "ProperBackstab", //43GF_PROPER_BACKSTAB
2319 "OnScreenText", //44GF_ONSCREEN_TEXT
2320 "HasSpecificDamageBonus", //45GF_SPECIFIC_DMG_BONUS
2321 "StrrefSaveGame", //46GF_STRREF_SAVEGAME
2322 "HasWisdomBonusTable",//47GF_WISDOM_BONUS
2323 "BiographyIsRes", //48GF_BIOGRAPHY_RES
2324 "NoBiography", //49GF_NO_BIOGRAPHY
2325 "StealIsAttack", //50GF_STEAL_IS_ATTACK
2326 NULL //for our own safety, this marks the end of the pole
2329 /** Loads gemrb.ini */
2330 bool Interface::LoadGemRBINI()
2332 DataStream* inifile = gamedata->GetResource( "gemrb", IE_INI_CLASS_ID );
2333 if (! inifile) {
2334 printStatus( "ERROR", LIGHT_RED );
2335 return false;
2338 printMessage( "Core", "Loading game type-specific GemRB setup...\n", WHITE );
2339 printf( "%s",inifile->originalfile);
2341 if (!IsAvailable( IE_INI_CLASS_ID )) {
2342 printStatus( "ERROR", LIGHT_RED );
2343 printf( "[Core]: No INI Importer Available.\n" );
2344 return false;
2346 PluginHolder<DataFileMgr> ini(IE_INI_CLASS_ID);
2347 ini->Open( inifile, true ); //autofree
2349 printStatus( "OK", LIGHT_GREEN );
2351 const char *s;
2353 // Resrefs are already initialized in Interface::Interface()
2354 s = ini->GetKeyAsString( "resources", "CursorBAM", NULL );
2355 if (s)
2356 strnlwrcpy( CursorBam, s, 8 ); //console cursor
2358 s = ini->GetKeyAsString( "resources", "ScrollCursorBAM", NULL );
2359 if (s)
2360 strnlwrcpy( ScrollCursorBam, s, 8 );
2362 s = ini->GetKeyAsString( "resources", "ButtonFont", NULL );
2363 if (s)
2364 strnlwrcpy( ButtonFont, s, 8 );
2366 s = ini->GetKeyAsString( "resources", "TooltipFont", NULL );
2367 if (s)
2368 strnlwrcpy( TooltipFont, s, 8 );
2370 s = ini->GetKeyAsString( "resources", "MovieFont", NULL );
2371 if (s)
2372 strnlwrcpy( MovieFont, s, 8 );
2374 s = ini->GetKeyAsString( "resources", "TooltipBack", NULL );
2375 if (s)
2376 strnlwrcpy( TooltipBackResRef, s, 8 );
2378 s = ini->GetKeyAsString( "resources", "TooltipColor", NULL );
2379 if (s) {
2380 if (s[0] == '#') {
2381 unsigned long c = strtoul (s + 1, NULL, 16);
2382 // FIXME: check errno
2383 TooltipColor.r = (unsigned char) (c >> 24);
2384 TooltipColor.g = (unsigned char) (c >> 16);
2385 TooltipColor.b = (unsigned char) (c >> 8);
2386 TooltipColor.a = (unsigned char) (c);
2390 //which stat determines the fist weapon (defaults to class)
2391 Actor::SetFistStat(ini->GetKeyAsInt( "resources", "FistStat", IE_CLASS));
2393 TooltipMargin = ini->GetKeyAsInt( "resources", "TooltipMargin", TooltipMargin );
2395 // The format of GroundCircle can be:
2396 // GroundCircleBAM1 = wmpickl/3
2397 // to denote that the bitmap should be scaled down 3x
2398 for (int size = 0; size < MAX_CIRCLE_SIZE; size++) {
2399 char name[30];
2400 sprintf( name, "GroundCircleBAM%d", size+1 );
2401 s = ini->GetKeyAsString( "resources", name, NULL );
2402 if (s) {
2403 const char *pos = strchr( s, '/' );
2404 if (pos) {
2405 GroundCircleScale[size] = atoi( pos+1 );
2406 strncpy( GroundCircleBam[size], s, pos - s );
2407 GroundCircleBam[size][pos - s] = '\0';
2408 } else {
2409 strcpy( GroundCircleBam[size], s );
2414 s = ini->GetKeyAsString( "resources", "NoteString", NULL );
2415 TextArea::SetNoteString(s);
2417 s = ini->GetKeyAsString( "resources", "INIConfig", NULL );
2418 if (s)
2419 strcpy( INIConfig, s );
2421 s = ini->GetKeyAsString( "resources", "Palette16", NULL );
2422 if (s)
2423 strcpy( Palette16, s );
2425 s = ini->GetKeyAsString( "resources", "Palette32", NULL );
2426 if (s)
2427 strcpy( Palette32, s );
2429 s = ini->GetKeyAsString( "resources", "Palette256", NULL );
2430 if (s)
2431 strcpy( Palette256, s );
2433 unsigned int i = (unsigned int) ini->GetKeyAsInt ("charset", "CharCount", 0);
2434 if (i>99) i=99;
2435 while(i--) {
2436 char key[10];
2437 snprintf(key,9,"Letter%d", i+1);
2438 s = ini->GetKeyAsString( "charset", key, NULL );
2439 if (s) {
2440 const char *s2 = strchr(s,',');
2441 if (s2) {
2442 upperlower(atoi(s), atoi(s2+1) );
2447 MaximumAbility = ini->GetKeyAsInt ("resources", "MaximumAbility", 25 );
2449 RedrawTile = ini->GetKeyAsInt( "resources", "RedrawTile", 0 )!=0;
2451 for (i=0;i<GF_COUNT;i++) {
2452 if (!game_flags[i]) {
2453 printf("Fix the game flags!\n");
2454 abort();
2456 SetFeature( ini->GetKeyAsInt( "resources", game_flags[i], 0 ), i );
2457 //printMessage("Option", "", GREEN);
2458 //printf("%s = %s\n", game_flags[i], HasFeature(i)?"yes":"no");
2461 ForceStereo = ini->GetKeyAsInt( "resources", "ForceStereo", 0 );
2463 return true;
2466 Palette* Interface::CreatePalette(const Color &color, const Color &back)
2468 Palette* pal = new Palette();
2469 pal->col[0].r = 0;
2470 pal->col[0].g = 0xff;
2471 pal->col[0].b = 0;
2472 pal->col[0].a = 0;
2473 for (int i = 1; i < 256; i++) {
2474 pal->col[i].r = back.r +
2475 ( unsigned char ) ( ( ( color.r - back.r ) * ( i ) ) / 255.0 );
2476 pal->col[i].g = back.g +
2477 ( unsigned char ) ( ( ( color.g - back.g ) * ( i ) ) / 255.0 );
2478 pal->col[i].b = back.b +
2479 ( unsigned char ) ( ( ( color.b - back.b ) * ( i ) ) / 255.0 );
2480 pal->col[i].a = back.a +
2481 ( unsigned char ) ( ( ( color.a - back.a ) * ( i ) ) / 255.0 );
2483 return pal;
2486 /** No descriptions */
2487 Color* Interface::GetPalette(unsigned index, int colors, Color *pal) const
2489 Image *img;
2490 if (colors == 32) {
2491 img = pal32;
2492 } else if (colors <= 32) {
2493 img = pal16;
2494 } else if (colors == 256) {
2495 img = pal256;
2496 } else {
2497 return pal;
2499 if (index >= img->GetHeight()) {
2500 index = 0;
2502 for (int i = 0; i < colors; i++) {
2503 pal[i] = img->GetPixel(i, index);
2505 return pal;
2507 /** Returns a preloaded Font */
2508 Font* Interface::GetFont(const char *ResRef) const
2510 for (unsigned int i = 0; i < fonts.size(); i++) {
2511 if (strnicmp( fonts[i]->ResRef, ResRef, 8 ) == 0) {
2512 return fonts[i];
2515 return NULL;
2518 Font* Interface::GetFont(unsigned int index) const
2520 if (index >= fonts.size()) {
2521 return NULL;
2523 return fonts[index];
2526 Font* Interface::GetButtonFont() const
2528 return GetFont( ButtonFont );
2531 /** Returns the Event Manager */
2532 EventMgr* Interface::GetEventMgr() const
2534 return evntmgr;
2537 /** Returns the Window Manager */
2538 WindowMgr* Interface::GetWindowMgr() const
2540 return windowmgr.get();
2543 /** Get GUI Script Manager */
2544 ScriptEngine* Interface::GetGUIScriptEngine() const
2546 return guiscript.get();
2549 static EffectRef fx_summon_disable_ref={"AvatarRemovalModifier",NULL,-1};
2551 //NOTE: if there were more summoned creatures, it will return only the last
2552 Actor *Interface::SummonCreature(const ieResRef resource, const ieResRef vvcres, Scriptable *Owner, Actor *target, const Point &position, int eamod, int level, Effect *fx, bool sexmod)
2554 //maximum number of monsters summoned
2555 int cnt=10;
2556 Actor * ab = NULL;
2558 //TODO:
2559 //decrease the number of summoned creatures with the number of already summoned creatures here
2560 //the summoned creatures have a special IE_SPECIFIC
2562 while(cnt--) {
2563 ab = gamedata->GetCreature(resource);
2564 if (!ab) {
2565 return NULL;
2568 if (Owner && Owner->Type==ST_ACTOR) {
2569 ab->LastSummoner = ((Actor *) Owner)->GetID();
2571 //Always use Base stats for the recently summoned creature
2573 int enemyally;
2575 if (eamod==EAM_SOURCEALLY || eamod==EAM_SOURCEENEMY) {
2576 if (Owner && Owner->Type==ST_ACTOR) {
2577 enemyally = ((Actor *) Owner)->GetStat(IE_EA)>EA_GOODCUTOFF;
2578 } else {
2579 enemyally = true;
2581 } else {
2582 if (target) {
2583 enemyally = target->GetBase(IE_EA)>EA_GOODCUTOFF;
2584 } else {
2585 enemyally = true;
2589 switch (eamod) {
2590 case EAM_SOURCEALLY:
2591 case EAM_ALLY:
2592 if (enemyally) {
2593 ab->SetBase(IE_EA, EA_ENEMY); //is this the summoned EA?
2594 } else {
2595 ab->SetBase(IE_EA, EA_CONTROLLED); //is this the summoned EA?
2597 break;
2598 case EAM_SOURCEENEMY:
2599 case EAM_ENEMY:
2600 if (enemyally) {
2601 ab->SetBase(IE_EA, EA_CONTROLLED); //is this the summoned EA?
2602 } else {
2603 ab->SetBase(IE_EA, EA_ENEMY); //is this the summoned EA?
2605 break;
2606 case EAM_NEUTRAL:
2607 ab->SetBase(IE_EA, EA_NEUTRAL);
2608 break;
2609 default:
2610 break;
2613 // mark the summon, but only if they don't have a special sex already
2614 if (sexmod && ab->BaseStats[IE_SEX] < SEX_EXTRA) {
2615 ab->SetBase(IE_SEX, SEX_SUMMON);
2618 Map *map;
2619 if (target) {
2620 map = target->GetCurrentArea();
2621 } else {
2622 map = Owner->GetCurrentArea();
2624 map->AddActor(ab);
2625 ab->SetPosition(position, true, 0);
2626 ab->RefreshEffects(NULL);
2628 if (vvcres[0]) {
2629 ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(vvcres, false);
2630 if (vvc) {
2631 //This is the final position of the summoned creature
2632 //not the original target point
2633 vvc->XPos=ab->Pos.x;
2634 vvc->YPos=ab->Pos.y;
2635 //force vvc to play only once
2636 vvc->PlayOnce();
2637 map->AddVVCell( vvc );
2639 //set up the summon disable effect
2640 Effect *newfx = EffectQueue::CreateEffect(fx_summon_disable_ref, 0, 1, FX_DURATION_INSTANT_LIMITED);
2641 newfx->Duration = vvc->GetSequenceDuration(AI_UPDATE_TIME)*90/100;
2642 ApplyEffect(newfx, ab, ab);
2646 //remove the xp value of friendly summons
2647 if (ab->BaseStats[IE_EA]<EA_GOODCUTOFF) {
2648 ab->SetBase(IE_XPVALUE, 0);
2650 if (fx) {
2651 ApplyEffect(fx, ab, Owner);
2654 //this check should happen after the fact
2655 level -= ab->GetBase(IE_XP);
2656 if(level<0) {
2657 break;
2661 return ab;
2664 void Interface::RedrawControls(const char *varname, unsigned int value)
2666 for (unsigned int i = 0; i < windows.size(); i++) {
2667 Window *win = windows[i];
2668 if (win != NULL && win->Visible!=WINDOW_INVALID) {
2669 win->RedrawControls(varname, value);
2674 void Interface::RedrawAll()
2676 for (unsigned int i = 0; i < windows.size(); i++) {
2677 Window *win = windows[i];
2678 if (win != NULL && win->Visible!=WINDOW_INVALID) {
2679 win->Invalidate();
2684 /** Loads a WindowPack (CHUI file) in the Window Manager */
2685 bool Interface::LoadWindowPack(const char* name)
2687 DataStream* stream = gamedata->GetResource( name, IE_CHU_CLASS_ID );
2688 if (stream == NULL) {
2689 printMessage( "Interface", "Error: Cannot find ", LIGHT_RED );
2690 printf( "%s.chu\n", name );
2691 return false;
2693 if (!GetWindowMgr()->Open( stream, true )) {
2694 printMessage( "Interface", "Error: Cannot Load ", LIGHT_RED );
2695 printf( "%s.chu\n", name );
2696 return false;
2699 strncpy( WindowPack, name, sizeof( WindowPack ) );
2700 WindowPack[sizeof( WindowPack ) - 1] = '\0';
2702 return true;
2705 /** Loads a Window in the Window Manager */
2706 int Interface::LoadWindow(unsigned short WindowID)
2708 unsigned int i;
2710 for (i = 0; i < windows.size(); i++) {
2711 Window *win = windows[i];
2712 if (win == NULL)
2713 continue;
2714 if (win->Visible==WINDOW_INVALID) {
2715 continue;
2717 if (win->WindowID == WindowID &&
2718 !strnicmp( WindowPack, win->WindowPack, sizeof(WindowPack) )) {
2719 SetOnTop( i );
2720 win->Invalidate();
2721 return i;
2724 Window* win = windowmgr->GetWindow( WindowID );
2725 if (win == NULL) {
2726 return -1;
2728 memcpy( win->WindowPack, WindowPack, sizeof(WindowPack) );
2730 int slot = -1;
2731 for (i = 0; i < windows.size(); i++) {
2732 if (windows[i] == NULL) {
2733 slot = i;
2734 break;
2737 if (slot == -1) {
2738 windows.push_back( win );
2739 slot = ( int ) windows.size() - 1;
2740 } else {
2741 windows[slot] = win;
2743 win->Invalidate();
2744 return slot;
2746 // FIXME: it's a clone of LoadWindow
2747 /** Creates a Window in the Window Manager */
2748 int Interface::CreateWindow(unsigned short WindowID, int XPos, int YPos, unsigned int Width, unsigned int Height, char* Background)
2750 unsigned int i;
2752 for (i = 0; i < windows.size(); i++) {
2753 if (windows[i] == NULL)
2754 continue;
2755 if (windows[i]->WindowID == WindowID && !stricmp( WindowPack,
2756 windows[i]->WindowPack )) {
2757 SetOnTop( i );
2758 windows[i]->Invalidate();
2759 return i;
2763 Window* win = new Window( WindowID, (ieWord) XPos, (ieWord) YPos, (ieWord) Width, (ieWord) Height );
2764 if (Background[0]) {
2765 if (IsAvailable( IE_MOS_CLASS_ID )) {
2766 ResourceHolder<ImageMgr> mos(Background);
2767 if (mos != NULL) {
2768 win->SetBackGround( mos->GetSprite2D(), true );
2769 } else
2770 printf( "[Core]: Cannot Load BackGround, skipping\n" );
2771 } else
2772 printf( "[Core]: No MOS Importer Available, skipping background\n" );
2775 strcpy( win->WindowPack, WindowPack );
2777 int slot = -1;
2778 for (i = 0; i < windows.size(); i++) {
2779 if (windows[i] == NULL) {
2780 slot = i;
2781 break;
2784 if (slot == -1) {
2785 windows.push_back( win );
2786 slot = ( int ) windows.size() - 1;
2787 } else {
2788 windows[slot] = win;
2790 win->Invalidate();
2791 return slot;
2794 /** Sets a Window on the Top */
2795 void Interface::SetOnTop(int Index)
2797 std::vector<int>::iterator t;
2798 for(t = topwin.begin(); t != topwin.end(); ++t) {
2799 if((*t) == Index) {
2800 topwin.erase(t);
2801 break;
2804 if(topwin.size() != 0)
2805 topwin.insert(topwin.begin(), Index);
2806 else
2807 topwin.push_back(Index);
2809 /** Add a window to the Window List */
2810 void Interface::AddWindow(Window * win)
2812 int slot = -1;
2813 for(unsigned int i = 0; i < windows.size(); i++) {
2814 Window *w = windows[i];
2816 if(w==NULL) {
2817 slot = i;
2818 break;
2821 if(slot == -1) {
2822 windows.push_back(win);
2823 slot=(int)windows.size()-1;
2825 else
2826 windows[slot] = win;
2827 win->Invalidate();
2830 /** Get a Control on a Window */
2831 int Interface::GetControl(unsigned short WindowIndex, unsigned long ControlID) const
2833 if (WindowIndex >= windows.size()) {
2834 return -1;
2836 Window* win = windows[WindowIndex];
2837 if (win == NULL) {
2838 return -1;
2840 int i = 0;
2841 while (true) {
2842 Control* ctrl = win->GetControl( (unsigned short) i );
2843 if (ctrl == NULL)
2844 return -1;
2845 if (ctrl->ControlID == ControlID)
2846 return i;
2847 i++;
2850 /** Adjust the Scrolling factor of a control (worldmap atm) */
2851 int Interface::AdjustScrolling(unsigned short WindowIndex,
2852 unsigned short ControlIndex, short x, short y)
2854 if (WindowIndex >= windows.size()) {
2855 return -1;
2857 Window* win = windows[WindowIndex];
2858 if (win == NULL) {
2859 return -1;
2861 Control* ctrl = win->GetControl( ControlIndex );
2862 if (ctrl == NULL) {
2863 return -1;
2865 switch(ctrl->ControlType) {
2866 case IE_GUI_WORLDMAP:
2867 ((WorldMapControl *) ctrl)->AdjustScrolling(x,y);
2868 break;
2869 default: //doesn't work for these
2870 return -1;
2872 return 0;
2875 /** Set the Text of a Control */
2876 int Interface::SetText(unsigned short WindowIndex,
2877 unsigned short ControlIndex, const char* string)
2879 if (WindowIndex >= windows.size()) {
2880 return -1;
2882 Window* win = windows[WindowIndex];
2883 if (win == NULL) {
2884 return -1;
2886 Control* ctrl = win->GetControl( ControlIndex );
2887 if (ctrl == NULL) {
2888 return -1;
2890 return ctrl->SetText( string );
2892 /** Set the Tooltip text of a Control */
2893 int Interface::SetTooltip(unsigned short WindowIndex,
2894 unsigned short ControlIndex, const char* string)
2896 if (WindowIndex >= windows.size()) {
2897 return -1;
2899 Window* win = windows[WindowIndex];
2900 if (win == NULL) {
2901 return -1;
2903 Control* ctrl = win->GetControl( ControlIndex );
2904 if (ctrl == NULL) {
2905 return -1;
2907 return ctrl->SetTooltip( string );
2910 void Interface::DisplayTooltip(int x, int y, Control *ctrl)
2912 if (tooltip_ctrl && tooltip_ctrl == ctrl && tooltip_x == x && tooltip_y == y)
2913 return;
2914 tooltip_x = x;
2915 tooltip_y = y;
2916 tooltip_currtextw = 0;
2917 tooltip_ctrl = ctrl;
2920 int Interface::GetVisible(unsigned short WindowIndex) const
2922 if (WindowIndex >= windows.size()) {
2923 return -1;
2925 Window* win = windows[WindowIndex];
2926 if (win == NULL) {
2927 return -1;
2929 return win->Visible;
2931 /** Set a Window Visible Flag */
2932 int Interface::SetVisible(unsigned short WindowIndex, int visible)
2934 if (WindowIndex >= windows.size()) {
2935 return -1;
2937 Window* win = windows[WindowIndex];
2938 if (win == NULL) {
2939 return -1;
2941 if (visible!=WINDOW_FRONT) {
2942 win->Visible = (char) visible;
2944 switch (visible) {
2945 case WINDOW_GRAYED:
2946 win->Invalidate();
2947 //here is a fallthrough
2948 case WINDOW_INVISIBLE:
2949 //hiding the viewport if the gamecontrol window was made invisible
2950 if (win->WindowID==65535) {
2951 video->SetViewport( 0,0,0,0 );
2953 evntmgr->DelWindow( win );
2954 break;
2956 case WINDOW_VISIBLE:
2957 if (win->WindowID==65535) {
2958 video->SetViewport( win->XPos, win->YPos, win->Width, win->Height);
2960 //here is a fallthrough
2961 case WINDOW_FRONT:
2962 if (win->Visible==WINDOW_VISIBLE) {
2963 evntmgr->AddWindow( win );
2965 win->Invalidate();
2966 SetOnTop( WindowIndex );
2967 break;
2969 return 0;
2973 /** Set the Status of a Control in a Window */
2974 int Interface::SetControlStatus(unsigned short WindowIndex,
2975 unsigned short ControlIndex, unsigned long Status)
2977 //don't set the status of an already invalidated window
2978 Window* win = GetWindow(WindowIndex);
2979 if (win == NULL) {
2980 return -1;
2982 Control* ctrl = win->GetControl( ControlIndex );
2983 if (ctrl == NULL) {
2984 return -1;
2986 if (Status&IE_GUI_CONTROL_FOCUSED) {
2987 evntmgr->SetFocused( win, ctrl);
2989 if (ctrl->ControlType != ((Status >> 24) & 0xff) ) {
2990 return -2;
2992 switch (ctrl->ControlType) {
2993 case IE_GUI_BUTTON:
2994 //Button
2996 Button* btn = ( Button* ) ctrl;
2997 btn->SetState( ( unsigned char ) ( Status & 0x7f ) );
2999 break;
3000 default:
3001 ctrl->Value = Status & 0x7f;
3002 break;
3004 return 0;
3007 /** Show a Window in Modal Mode */
3008 int Interface::ShowModal(unsigned short WindowIndex, int Shadow)
3010 if (WindowIndex >= windows.size()) {
3011 printMessage( "Core", "Window not found", LIGHT_RED );
3012 return -1;
3014 Window* win = windows[WindowIndex];
3015 if (win == NULL) {
3016 printMessage( "Core", "Window already freed", LIGHT_RED );
3017 return -1;
3019 win->Visible = WINDOW_FRONT;
3020 //don't destroy the other window handlers
3021 //evntmgr->Clear();
3022 SetOnTop( WindowIndex );
3023 evntmgr->AddWindow( win );
3024 evntmgr->SetFocused( win, NULL );
3026 ModalWindow = NULL;
3027 DrawWindows();
3028 win->Invalidate();
3030 Color gray = {
3031 0, 0, 0, 128
3033 Color black = {
3034 0, 0, 0, 255
3037 Region r( 0, 0, Width, Height );
3039 if (Shadow == MODAL_SHADOW_GRAY) {
3040 video->DrawRect( r, gray );
3041 } else if (Shadow == MODAL_SHADOW_BLACK) {
3042 video->DrawRect( r, black );
3045 ModalWindow = win;
3046 return 0;
3049 bool Interface::IsFreezed()
3051 return !update_scripts;
3054 void Interface::GameLoop(void)
3056 update_scripts = false;
3057 GameControl *gc = GetGameControl();
3058 if (gc) {
3059 update_scripts = !(gc->GetDialogueFlags() & DF_FREEZE_SCRIPTS);
3062 GSUpdate(update_scripts);
3064 //i'm not sure if this should be here
3066 //in multi player (if we ever get to it), only the server must call this
3067 if (update_scripts) {
3068 if ( game->selected.size() > 0 ) {
3069 gc->ChangeMap(GetFirstSelectedPC(true), false);
3071 // the game object will run the area scripts as well
3072 game->UpdateScripts();
3076 /** handles hardcoded gui behaviour */
3077 void Interface::HandleGUIBehaviour(void)
3079 GameControl *gc = GetGameControl();
3080 if (gc) {
3081 //this variable is used all over in the following hacks
3082 int flg = gc->GetDialogueFlags();
3084 //the following part is a series of hardcoded gui behaviour
3086 //initiating dialog
3087 if (flg & DF_IN_DIALOG) {
3088 // -3 noaction
3089 // -2 close
3090 // -1 open
3091 // choose option
3092 ieDword var = (ieDword) -3;
3093 vars->Lookup("DialogChoose", var);
3094 if ((int) var == -2) {
3095 gc->dialoghandler->EndDialog();
3096 } else if ( (int)var !=-3) {
3097 gc->dialoghandler->DialogChoose(var);
3098 if (!(gc->GetDialogueFlags() & (DF_OPENCONTINUEWINDOW | DF_OPENENDWINDOW)))
3099 guiscript->RunFunction( "GUIWORLD", "NextDialogState" );
3101 // the last node of a dialog can have a new-dialog action! don't interfere in that case
3102 ieDword newvar = 0; vars->Lookup("DialogChoose", newvar);
3103 if (var == (ieDword) -1 || newvar != (ieDword) -1) {
3104 vars->SetAt("DialogChoose", (ieDword) -3);
3107 if (flg & DF_OPENCONTINUEWINDOW) {
3108 guiscript->RunFunction( "GUIWORLD", "OpenContinueMessageWindow" );
3109 gc->SetDialogueFlags(DF_OPENCONTINUEWINDOW|DF_OPENENDWINDOW, BM_NAND);
3110 } else if (flg & DF_OPENENDWINDOW) {
3111 guiscript->RunFunction( "GUIWORLD", "OpenEndMessageWindow" );
3112 gc->SetDialogueFlags(DF_OPENCONTINUEWINDOW|DF_OPENENDWINDOW, BM_NAND);
3116 //handling container
3117 if (CurrentContainer && UseContainer) {
3118 if (!(flg & DF_IN_CONTAINER) ) {
3119 gc->SetDialogueFlags(DF_IN_CONTAINER, BM_OR);
3120 guiscript->RunFunction( "GUIWORLD", "OpenContainerWindow" );
3122 } else {
3123 if (flg & DF_IN_CONTAINER) {
3124 gc->SetDialogueFlags(DF_IN_CONTAINER, BM_NAND);
3125 guiscript->RunFunction( "GUIWORLD", "CloseContainerWindow" );
3128 //end of gui hacks
3132 void Interface::DrawWindows(void)
3134 //here comes the REAL drawing of windows
3135 if (ModalWindow) {
3136 ModalWindow->DrawWindow();
3137 return;
3139 size_t i = topwin.size();
3140 while(i--) {
3141 unsigned int t = topwin[i];
3143 if ( t >=windows.size() )
3144 continue;
3146 //visible ==1 or 2 will be drawn
3147 Window* win = windows[t];
3148 if (win != NULL) {
3149 if (win->Visible == WINDOW_INVALID) {
3150 topwin.erase(topwin.begin()+i);
3151 evntmgr->DelWindow( win );
3152 delete win;
3153 windows[t]=NULL;
3154 } else if (win->Visible) {
3155 win->DrawWindow();
3161 void Interface::DrawTooltip ()
3163 if (! tooltip_ctrl || !tooltip_ctrl->Tooltip)
3164 return;
3166 Font* fnt = GetFont( TooltipFont );
3167 char *tooltip_text = tooltip_ctrl->Tooltip;
3169 int w1 = 0;
3170 int w2 = 0;
3171 int strw = fnt->CalcStringWidth( tooltip_text ) + 8;
3172 int w = strw;
3173 int h = fnt->maxHeight;
3175 if (TooltipBack) {
3176 // animate BG tooltips
3177 // TODO: make tooltip animation an option instead
3178 // of following hard-coded check!
3179 if (TooltipMargin == 5) {
3180 // TODO: make speed an option
3181 int tooltip_anim_speed = 15;
3182 if (tooltip_currtextw < strw) {
3183 tooltip_currtextw += tooltip_anim_speed;
3185 if (tooltip_currtextw > strw) {
3186 tooltip_currtextw = strw;
3188 w = tooltip_currtextw;
3191 h = TooltipBack[0]->Height;
3192 w1 = TooltipBack[1]->Width;
3193 w2 = TooltipBack[2]->Width;
3194 w += TooltipMargin*2;
3195 strw += TooltipMargin*2;
3196 //multiline in case of too much text
3197 if (w>TooltipBack[0]->Width)
3198 strw=w=TooltipBack[0]->Width;
3199 else if (strw>TooltipBack[0]->Width)
3200 strw=TooltipBack[0]->Width;
3203 int strx = tooltip_x - strw / 2;
3204 int y = tooltip_y - h / 2;
3205 // Ensure placement within the screen
3206 if (strx < 0) strx = 0;
3207 else if (strx + strw + w1 + w2 > Width)
3208 strx = Width - strw - w1 - w2;
3209 if (y < 0) y = 0;
3210 else if (y + h > Height)
3211 y = Height - h;
3213 int x = strx + ((strw - w) / 2);
3215 // FIXME: take back[0] from center, not from left end
3216 Region r2 = Region( x, y, w, h );
3217 if (TooltipBack) {
3218 video->BlitSprite( TooltipBack[0], x + TooltipMargin, y, true, &r2 );
3219 video->BlitSprite( TooltipBack[1], x, y, true );
3220 video->BlitSprite( TooltipBack[2], x + w, y, true );
3223 if (TooltipBack) {
3224 r2.x+=TooltipMargin;
3225 strx+=TooltipMargin;
3227 Region textr = Region( strx, y, strw, h );
3228 fnt->Print( r2, textr, (ieByte *) tooltip_text, NULL,
3229 IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true );
3232 //interface for higher level functions, if the window was
3233 //marked for deletion it is not returned
3234 Window* Interface::GetWindow(unsigned short WindowIndex) const
3236 if (WindowIndex < windows.size()) {
3237 Window *win = windows[WindowIndex];
3238 if (win && (win->Visible!=WINDOW_INVALID) ) {
3239 return win;
3242 return NULL;
3245 // this function will determine if wnd is a valid window pointer
3246 // by checking if its WindowID is the same as the reference
3247 bool Interface::IsValidWindow(unsigned short WindowID, Window *wnd) const
3249 size_t WindowIndex = windows.size();
3250 while (WindowIndex--) {
3251 if (windows[WindowIndex] == wnd) {
3252 return wnd->WindowID == WindowID;
3255 return false;
3258 //this function won't delete the window, just mark it for deletion
3259 //it will be deleted in the next DrawWindows cycle
3260 //regardless, the window deleted is inaccessible for gui scripts and
3261 //other high level functions from now
3262 int Interface::DelWindow(unsigned short WindowIndex)
3264 if (WindowIndex >= windows.size()) {
3265 return -1;
3267 Window* win = windows[WindowIndex];
3268 if ((win == NULL) || (win->Visible==WINDOW_INVALID) ) {
3269 printMessage( "Core", "Window deleted again", LIGHT_RED );
3270 return -1;
3272 if (win == ModalWindow) {
3273 ModalWindow = NULL;
3274 RedrawAll(); //marking windows for redraw
3276 evntmgr->DelWindow( win );
3277 win->release();
3278 //re-capturing new (old) modal window if any
3279 size_t tw = topwin.size();
3280 for(size_t i=0;i<tw;i++) {
3281 Window *tmp = windows[topwin[i]];
3282 if (tmp->Visible==WINDOW_FRONT) {
3283 ModalWindow = tmp;
3284 break;
3287 return 0;
3290 void Interface::DelAllWindows()
3292 vars->SetAt("MessageWindow", (ieDword) ~0);
3293 vars->SetAt("OptionsWindow", (ieDword) ~0);
3294 vars->SetAt("PortraitWindow", (ieDword) ~0);
3295 vars->SetAt("ActionsWindow", (ieDword) ~0);
3296 vars->SetAt("TopWindow", (ieDword) ~0);
3297 vars->SetAt("OtherWindow", (ieDword) ~0);
3298 vars->SetAt("FloatWindow", (ieDword) ~0);
3299 for(unsigned int WindowIndex=0; WindowIndex<windows.size();WindowIndex++) {
3300 Window* win = windows[WindowIndex];
3301 delete win;
3303 windows.clear();
3304 topwin.clear();
3305 evntmgr->Clear();
3306 ModalWindow = NULL;
3309 /** Popup the Console */
3310 void Interface::PopupConsole()
3312 ConsolePopped = !ConsolePopped;
3313 RedrawAll();
3314 console->Changed = true;
3317 /** Draws the Console */
3318 void Interface::DrawConsole()
3320 console->Draw( 0, 0 );
3323 /** Get the Sound Manager */
3324 SaveGameIterator* Interface::GetSaveGameIterator() const
3326 return sgiterator;
3328 /** Sends a termination signal to the Video Driver */
3329 bool Interface::Quit(void)
3331 return video->Quit();
3333 /** Returns the variables dictionary */
3334 Variables* Interface::GetDictionary() const
3336 return vars;
3338 /** Returns the token dictionary */
3339 Variables* Interface::GetTokenDictionary() const
3341 return tokens;
3343 /** Get the Music Manager */
3344 MusicMgr* Interface::GetMusicMgr() const
3346 return music.get();
3348 /** Loads an IDS Table, returns -1 on error or the Symbol Table Index on success */
3349 int Interface::LoadSymbol(const char* ResRef)
3351 int ind = GetSymbolIndex( ResRef );
3352 if (ind != -1) {
3353 return ind;
3355 DataStream* str = gamedata->GetResource( ResRef, IE_IDS_CLASS_ID );
3356 if (!str) {
3357 return -1;
3359 PluginHolder<SymbolMgr> sm(IE_IDS_CLASS_ID);
3360 if (!sm) {
3361 delete str;
3362 return -1;
3364 if (!sm->Open( str, true )) {
3365 return -1;
3367 Symbol s;
3368 strncpy( s.ResRef, ResRef, 8 );
3369 s.sm = sm;
3370 ind = -1;
3371 for (size_t i = 0; i < symbols.size(); i++) {
3372 if (!symbols[i].sm) {
3373 ind = ( int ) i;
3374 break;
3377 if (ind != -1) {
3378 symbols[ind] = s;
3379 return ind;
3381 symbols.push_back( s );
3382 return ( int ) symbols.size() - 1;
3384 /** Gets the index of a loaded Symbol Table, returns -1 on error */
3385 int Interface::GetSymbolIndex(const char* ResRef) const
3387 for (size_t i = 0; i < symbols.size(); i++) {
3388 if (!symbols[i].sm)
3389 continue;
3390 if (strnicmp( symbols[i].ResRef, ResRef, 8 ) == 0)
3391 return ( int ) i;
3393 return -1;
3395 /** Gets a Loaded Symbol Table by its index, returns NULL on error */
3396 Holder<SymbolMgr> Interface::GetSymbol(unsigned int index) const
3398 if (index >= symbols.size()) {
3399 return Holder<SymbolMgr>();
3401 if (!symbols[index].sm) {
3402 return Holder<SymbolMgr>();
3404 return symbols[index].sm;
3406 /** Frees a Loaded Symbol Table, returns false on error, true on success */
3407 bool Interface::DelSymbol(unsigned int index)
3409 if (index >= symbols.size()) {
3410 return false;
3412 if (!symbols[index].sm) {
3413 return false;
3415 symbols[index].sm.release();
3416 return true;
3418 /** Plays a Movie */
3419 int Interface::PlayMovie(const char* ResRef)
3421 ResourceHolder<MoviePlayer> mp(ResRef);
3422 if (!mp) {
3423 return -1;
3426 ieDword subtitles = 0;
3427 Font *SubtitleFont = NULL;
3428 Palette *palette = NULL;
3429 ieDword *frames = NULL;
3430 ieDword *strrefs = NULL;
3431 int cnt = 0;
3432 int offset = 0;
3434 //one of these two should exist (they both mean the same thing)
3435 vars->Lookup("Display Movie Subtitles", subtitles);
3436 if (subtitles) {
3437 //HoW flag
3438 cnt=-3;
3439 offset = 3;
3440 } else {
3441 //ToB flag
3442 vars->Lookup("Display Subtitles", subtitles);
3444 AutoTable sttable;
3445 if (subtitles && sttable.load(ResRef)) {
3446 cnt += sttable->GetRowCount();
3447 if (cnt>0) {
3448 frames = (ieDword *) malloc(cnt * sizeof(ieDword) );
3449 strrefs = (ieDword *) malloc(cnt * sizeof(ieDword) );
3450 } else {
3451 cnt = 0;
3453 if (frames && strrefs) {
3454 for (int i=0;i<cnt;i++) {
3455 frames[i] = atoi (sttable->QueryField(i+offset, 0) );
3456 strrefs[i] = atoi (sttable->QueryField(i+offset, 1) );
3459 int r = atoi(sttable->QueryField("red", "frame"));
3460 int g = atoi(sttable->QueryField("green", "frame"));
3461 int b = atoi(sttable->QueryField("blue", "frame"));
3462 SubtitleFont = GetFont (MovieFont); //will change
3463 if (r || g || b) {
3464 if (SubtitleFont) {
3465 Color fore = {(unsigned char) r,(unsigned char) g,(unsigned char) b, 0x00};
3466 Color back = {0x00, 0x00, 0x00, 0x00};
3467 palette = CreatePalette( fore, back );
3472 //shutting down music and ambients before movie
3473 if (music)
3474 music->HardEnd();
3475 AmbientMgr *ambim = AudioDriver->GetAmbientMgr();
3476 if (ambim) ambim->deactivate();
3477 video->SetMovieFont(SubtitleFont, palette );
3478 mp->CallBackAtFrames(cnt, frames, strrefs);
3479 mp->Play();
3480 gamedata->FreePalette( palette );
3481 if (frames)
3482 free(frames);
3483 if (strrefs)
3484 free(strrefs);
3485 //restarting music
3486 if (music)
3487 music->Start();
3488 if (ambim) ambim->activate();
3489 //this will fix redraw all windows as they looked like
3490 //before the movie
3491 RedrawAll();
3493 //Setting the movie name to 1
3494 vars->SetAt( ResRef, 1 );
3495 return 0;
3498 int Interface::Roll(int dice, int size, int add) const
3500 if (dice < 1) {
3501 return add;
3503 if (size < 1) {
3504 return add;
3506 if (dice > 100) {
3507 return add + dice * size / 2;
3509 for (int i = 0; i < dice; i++) {
3510 add += rand() % size + 1;
3512 return add;
3515 static char bmp_suffix[6]="M.BMP";
3516 static char png_suffix[6]="M.PNG";
3518 int Interface::GetPortraits(TextArea* ta, bool smallorlarge)
3520 int count = 0;
3521 char Path[_MAX_PATH];
3523 if (smallorlarge) {
3524 bmp_suffix[0]='S';
3525 png_suffix[0]='S';
3526 } else {
3527 bmp_suffix[0]='M';
3528 png_suffix[0]='M';
3530 PathJoin( Path, GamePath, GamePortraitsPath, NULL );
3531 DirectoryIterator dir(Path);
3532 if (!dir) {
3533 return -1;
3535 printf( "Looking in %s\n", Path );
3536 do {
3537 char *name = dir.GetName();
3538 if (name[0] == '.')
3539 continue;
3540 if (dir.IsDirectory())
3541 continue;
3542 strupr(name);
3543 char *pos = strstr(name,bmp_suffix);
3544 if (!pos && IsAvailable(IE_PNG_CLASS_ID) ) {
3545 pos = strstr(name,png_suffix);
3547 if (!pos) continue;
3548 pos[1]=0;
3549 count++;
3550 ta->AppendText( name, -1 );
3551 } while (++dir);
3552 return count;
3555 int Interface::GetCharSounds(TextArea* ta)
3557 bool hasfolders;
3558 int count = 0;
3559 char Path[_MAX_PATH];
3561 PathJoin( Path, GamePath, GameSoundsPath, NULL );
3562 hasfolders = ( HasFeature( GF_SOUNDFOLDERS ) != 0 );
3563 DirectoryIterator dir(Path);
3564 if (!dir) {
3565 return -1;
3567 printf( "Looking in %s\n", Path );
3568 do {
3569 char *name = dir.GetName();
3570 if (name[0] == '.')
3571 continue;
3572 if (hasfolders == !dir.IsDirectory())
3573 continue;
3574 if (!hasfolders) {
3575 strupr(name);
3576 char *pos = strstr(name,"A.WAV");
3577 if (!pos) continue;
3578 *pos=0;
3580 count++;
3581 ta->AppendText( name, -1 );
3582 } while (++dir);
3583 return count;
3586 int Interface::GetCharacters(TextArea* ta)
3588 int count = 0;
3589 char Path[_MAX_PATH];
3591 PathJoin( Path, GamePath, GameCharactersPath, NULL );
3592 DirectoryIterator dir(Path);
3593 if (!dir) {
3594 return -1;
3596 printf( "Looking in %s\n", Path );
3597 do {
3598 char *name = dir.GetName();
3599 if (name[0] == '.')
3600 continue;
3601 if (dir.IsDirectory())
3602 continue;
3603 strupr(name);
3604 char *pos = strstr(name,".CHR");
3605 if (!pos) continue;
3606 *pos=0;
3607 count++;
3608 ta->AppendText( name, -1 );
3609 } while (++dir);
3610 return count;
3613 bool Interface::LoadINI(const char* filename)
3615 FILE* config;
3616 config = fopen( filename, "rb" );
3617 if (config == NULL) {
3618 return false;
3620 char name[65], value[_MAX_PATH + 3];
3621 while (!feof( config )) {
3622 name[0] = 0;
3623 value[0] = 0;
3624 char rem;
3626 if (fread( &rem, 1, 1, config ) != 1)
3627 break;
3629 if (( rem == '#' ) ||
3630 ( rem == '[' ) ||
3631 ( rem == '\r' ) ||
3632 ( rem == '\n' ) ||
3633 ( rem == ';' )) {
3634 if (rem == '\r') {
3635 fgetc( config );
3636 continue;
3637 } else if (rem == '\n')
3638 continue;
3640 //it should always return zero
3641 if (fscanf( config, "%*[^\r\n]%*[\r\n]" )!=0)
3642 break;
3643 continue;
3645 fseek( config, -1, SEEK_CUR );
3646 //the * element is not counted
3647 if (fscanf( config, "%[^=]=%[^\r\n]%*[\r\n]", name, value )!=2)
3648 continue;
3649 if (( value[0] >= '0' ) && ( value[0] <= '9' )) {
3650 vars->SetAt( name, atoi( value ) );
3653 fclose( config );
3654 return true;
3657 /** Enables/Disables the Cut Scene Mode */
3658 void Interface::SetCutSceneMode(bool active)
3660 GameControl *gc = GetGameControl();
3661 if (gc) {
3662 gc->SetCutSceneMode( active );
3664 if (game) {
3665 if (active) {
3666 game->ControlStatus |= CS_HIDEGUI;
3667 } else {
3668 game->ControlStatus &= ~CS_HIDEGUI;
3670 SetEventFlag(EF_CONTROL);
3672 video->SetMouseEnabled(!active);
3675 bool Interface::InCutSceneMode() const
3677 return (GetGameControl()->GetScreenFlags()&SF_DISABLEMOUSE)!=0;
3680 void Interface::QuitGame(int BackToMain)
3682 SetCutSceneMode(false);
3683 if (timer) {
3684 //clear cutscenes
3685 //clear fade/screenshake effects
3686 timer->Init();
3687 timer->SetFadeFromColor(0);
3690 DelAllWindows(); //delete all windows, including GameControl
3692 //shutting down ingame music
3693 //(do it before deleting the game)
3694 if (music) {
3695 music->HardEnd();
3697 // stop any ambients which are still enqueued
3698 if (AudioDriver) {
3699 AmbientMgr *ambim = AudioDriver->GetAmbientMgr();
3700 if (ambim) ambim->deactivate();
3702 //delete game, worldmap
3703 if (game) {
3704 delete game;
3705 game=NULL;
3707 if (worldmap) {
3708 delete worldmap;
3709 worldmap=NULL;
3711 if (BackToMain) {
3712 strcpy(NextScript, "Start");
3713 QuitFlag |= QF_CHANGESCRIPT;
3715 GSUpdate(true);
3718 void Interface::SetupLoadGame(Holder<SaveGame> sg, int ver_override)
3720 LoadGameIndex = sg;
3721 VersionOverride = ver_override;
3722 QuitFlag |= QF_LOADGAME;
3725 void Interface::LoadGame(SaveGame *sg, int ver_override)
3727 // This function has rather painful error handling,
3728 // as it should swap all the objects or none at all
3729 // and the loading can fail for various reasons
3731 // Yes, it uses goto. Other ways seemed too awkward for me.
3733 strings->CloseAux();
3734 tokens->RemoveAll(NULL); //clearing the token dictionary
3736 if(calendar) delete calendar;
3737 calendar = new Calendar;
3739 DataStream* gam_str = NULL;
3740 DataStream* sav_str = NULL;
3741 DataStream* wmp_str = NULL;
3744 Game* new_game = NULL;
3745 WorldMapArray* new_worldmap = NULL;
3747 LoadProgress(15);
3748 if (!KeepCache) DelTree((const char *) CachePath, true);
3749 LoadProgress(20);
3751 if (sg == NULL) {
3752 //Load the Default Game
3753 gam_str = gamedata->GetResource( GameNameResRef, IE_GAM_CLASS_ID );
3754 sav_str = NULL;
3755 wmp_str = gamedata->GetResource( WorldMapName, IE_WMP_CLASS_ID );
3756 } else {
3757 gam_str = sg->GetGame();
3758 sav_str = sg->GetSave();
3759 wmp_str = sg->GetWmap();
3762 // These are here because of the goto
3763 PluginHolder<SaveGameMgr> gam_mgr(IE_GAM_CLASS_ID);
3764 PluginHolder<WorldMapMgr> wmp_mgr(IE_WMP_CLASS_ID);
3766 if (!gam_str || !wmp_str)
3767 goto cleanup;
3769 // Load GAM file
3770 if (!gam_mgr)
3771 goto cleanup;
3773 if (!gam_mgr->Open( gam_str, true ))
3774 goto cleanup;
3776 new_game = gam_mgr->LoadGame(new Game(), ver_override);
3777 if (!new_game)
3778 goto cleanup;
3780 gam_str = NULL;
3782 // Load WMP (WorldMap) file
3783 if (!wmp_mgr)
3784 goto cleanup;
3786 if (!wmp_mgr->Open( wmp_str, true ))
3787 goto cleanup;
3789 new_worldmap = wmp_mgr->GetWorldMapArray( );
3791 wmp_str = NULL;
3793 LoadProgress(30);
3794 // Unpack SAV (archive) file to Cache dir
3795 if (sav_str) {
3796 PluginHolder<ArchiveImporter> ai(IE_BIF_CLASS_ID);
3797 if (ai) {
3798 if (ai->DecompressSaveGame(sav_str) != GEM_OK) {
3799 goto cleanup;
3802 delete sav_str;
3803 sav_str = NULL;
3806 // Let's assume that now is everything loaded OK and swap the objects
3808 delete game;
3809 delete worldmap;
3811 game = new_game;
3812 worldmap = new_worldmap;
3814 strings->OpenAux();
3815 LoadProgress(100);
3816 return;
3817 cleanup:
3818 // Something went wrong, so try to clean after itself
3820 delete new_game;
3821 delete new_worldmap;
3823 delete gam_str;
3824 delete wmp_str;
3825 delete sav_str;
3828 /* swapping out old resources */
3829 void Interface::UpdateMasterScript()
3831 if (game) {
3832 game->SetScript( GlobalScript, 0 );
3835 PluginHolder<WorldMapMgr> wmp_mgr(IE_WMP_CLASS_ID);
3836 if (! wmp_mgr)
3837 return;
3839 if (worldmap) {
3840 DataStream *wmp_str = gamedata->GetResource( WorldMapName, IE_WMP_CLASS_ID );
3842 if (!wmp_mgr->Open( wmp_str, true )) {
3843 delete wmp_str;
3846 delete worldmap;
3847 worldmap = wmp_mgr->GetWorldMapArray();
3851 GameControl *Interface::GetGameControl() const
3853 Window *window = GetWindow( 0 );
3854 // in the beginning, there's no window at all
3855 if (! window)
3856 return NULL;
3858 Control* gc = window->GetControl(0);
3859 if (gc->ControlType!=IE_GUI_GAMECONTROL) {
3860 return NULL;
3862 return (GameControl *) gc;
3865 bool Interface::InitItemTypes()
3867 if (slotmatrix) {
3868 free(slotmatrix);
3870 AutoTable it("itemtype");
3871 ItemTypes = 0;
3872 if (it) {
3873 ItemTypes = it->GetRowCount(); //number of itemtypes
3874 if (ItemTypes<0) {
3875 ItemTypes = 0;
3877 int InvSlotTypes = it->GetColumnCount();
3878 if (InvSlotTypes > 32) { //bit count limit
3879 InvSlotTypes = 32;
3881 //make sure unsigned int is 32 bits
3882 slotmatrix = (ieDword *) malloc(ItemTypes * sizeof(ieDword) );
3883 for (int i=0;i<ItemTypes;i++) {
3884 unsigned int value = 0;
3885 unsigned int k = 1;
3886 for (int j=0;j<InvSlotTypes;j++) {
3887 if (strtol(it->QueryField(i,j),NULL,0) ) {
3888 value |= k;
3890 k <<= 1;
3892 //we let any items in the inventory
3893 slotmatrix[i] = (ieDword) value | SLOT_INVENTORY;
3897 //slottype describes the inventory structure
3898 Inventory::Init(HasFeature(GF_MAGICBIT));
3899 AutoTable st("slottype");
3900 if (slottypes) {
3901 free(slottypes);
3902 slottypes = NULL;
3904 SlotTypes = 0;
3905 if (st) {
3906 SlotTypes = st->GetRowCount();
3907 //make sure unsigned int is 32 bits
3908 slottypes = (SlotType *) malloc(SlotTypes * sizeof(SlotType) );
3909 memset(slottypes, -1, SlotTypes * sizeof(SlotType) );
3910 for (unsigned int row = 0; row < SlotTypes; row++) {
3911 bool alias;
3912 unsigned int i = (ieDword) strtol(st->GetRowName(row),NULL,0 );
3913 if (i>=SlotTypes) continue;
3914 if (slottypes[i].sloteffects!=0xffffffffu) {
3915 slottypes[row].slot = i;
3916 i=row;
3917 alias = true;
3918 } else {
3919 slottypes[row].slot = i;
3920 alias = false;
3922 slottypes[i].slottype = (ieDword) strtol(st->QueryField(row,0),NULL,0 );
3923 slottypes[i].slotid = (ieDword) strtol(st->QueryField(row,1),NULL,0 );
3924 strnlwrcpy( slottypes[i].slotresref, st->QueryField(row,2), 8 );
3925 slottypes[i].slottip = (ieDword) strtol(st->QueryField(row,3),NULL,0 );
3926 //don't fill sloteffects for aliased slots (pst)
3927 if (alias) {
3928 continue;
3930 slottypes[i].sloteffects = (ieDword) strtol(st->QueryField(row,4),NULL,0 );
3931 //setting special slots
3932 if (slottypes[i].slottype&SLOT_ITEM) {
3933 if (slottypes[i].slottype&SLOT_INVENTORY) {
3934 Inventory::SetInventorySlot(i);
3935 } else {
3936 Inventory::SetQuickSlot(i);
3939 switch (slottypes[i].sloteffects) {
3940 //fist slot, not saved, default weapon
3941 case SLOT_EFFECT_FIST: Inventory::SetFistSlot(i); break;
3942 //magic weapon slot, overrides all weapons
3943 case SLOT_EFFECT_MAGIC: Inventory::SetMagicSlot(i); break;
3944 //weapon slot, Equipping marker is relative to it
3945 case SLOT_EFFECT_MELEE: Inventory::SetWeaponSlot(i); break;
3946 //ranged slot
3947 case SLOT_EFFECT_MISSILE: Inventory::SetRangedSlot(i); break;
3948 //right hand
3949 case SLOT_EFFECT_LEFT: Inventory::SetShieldSlot(i); break;
3950 //head (for averting critical hit)
3951 case SLOT_EFFECT_HEAD: Inventory::SetHeadSlot(i); break;
3952 default:;
3956 return (it && st);
3959 ieDword Interface::FindSlot(unsigned int idx) const
3961 ieDword i;
3963 for (i=0;i<SlotTypes;i++) {
3964 if (idx==slottypes[i].slot) {
3965 break;
3968 return i;
3971 ieDword Interface::QuerySlot(unsigned int idx) const
3973 if (idx>=SlotTypes) {
3974 return 0;
3976 return slottypes[idx].slot;
3979 ieDword Interface::QuerySlotType(unsigned int idx) const
3981 if (idx>=SlotTypes) {
3982 return 0;
3984 return slottypes[idx].slottype;
3987 ieDword Interface::QuerySlotID(unsigned int idx) const
3989 if (idx>=SlotTypes) {
3990 return 0;
3992 return slottypes[idx].slotid;
3995 ieDword Interface::QuerySlottip(unsigned int idx) const
3997 if (idx>=SlotTypes) {
3998 return 0;
4000 return slottypes[idx].slottip;
4003 ieDword Interface::QuerySlotEffects(unsigned int idx) const
4005 if (idx>=SlotTypes) {
4006 return 0;
4008 return slottypes[idx].sloteffects;
4011 const char *Interface::QuerySlotResRef(unsigned int idx) const
4013 if (idx>=SlotTypes) {
4014 return "";
4016 return slottypes[idx].slotresref;
4019 // checks the itemtype vs. slottype, and also checks the usability flags
4020 // vs. Actor's stats (alignment, class, race, kit etc.)
4021 int Interface::CanUseItemType(int slottype, Item *item, Actor *actor, bool feedback, bool equipped) const
4023 //inventory is a special case, we allow any items to enter it
4024 if ( slottype==-1 ) {
4025 return SLOT_INVENTORY;
4027 //if we look for ALL slot types, then SLOT_SHIELD shouldn't interfere
4028 //with twohandedness
4029 //As long as this is an Item, use the ITEM constant
4030 //switch for IE_INV_ITEM_* if it is a CREItem
4031 if (item->Flags&IE_ITEM_TWO_HANDED) {
4032 //if the item is twohanded and there are more slots, drop the shield slot
4033 if (slottype&~SLOT_SHIELD) {
4034 slottype&=~SLOT_SHIELD;
4036 if (slottype&SLOT_SHIELD) {
4037 //cannot equip twohanded in offhand
4038 if (feedback) displaymsg->DisplayConstantString(STR_NOT_IN_OFFHAND, 0xf0f0f0);
4039 return 0;
4043 if ( (unsigned int) item->ItemType>=(unsigned int) ItemTypes) {
4044 //invalid itemtype
4045 if (feedback) displaymsg->DisplayConstantString(STR_WRONGITEMTYPE, 0xf0f0f0);
4046 return 0;
4049 //if actor is supplied, check its usability fields
4050 if (actor) {
4051 //constant strings
4052 int idx = actor->Unusable(item);
4053 if (idx) {
4054 if (feedback) displaymsg->DisplayConstantString(idx, 0xf0f0f0);
4055 return 0;
4057 //custom strings
4058 ieStrRef str = actor->Disabled(item->Name, item->ItemType);
4059 if (str && !equipped) {
4060 if (feedback) displaymsg->DisplayString(str, 0xf0f0f0, 0);
4061 return 0;
4065 //if any bit is true, the answer counts as true
4066 int ret = (slotmatrix[item->ItemType]&slottype);
4068 if (!ret) {
4069 if (feedback) displaymsg->DisplayConstantString(STR_WRONGITEMTYPE, 0xf0f0f0);
4070 return 0;
4073 //this warning comes only when feedback is enabled
4074 if (feedback) {
4075 //this was, but that disabled equipping of amber earrings in PST
4076 //if (slotmatrix[item->ItemType]&(SLOT_QUIVER|SLOT_WEAPON|SLOT_ITEM)) {
4077 if (ret&(SLOT_QUIVER|SLOT_WEAPON|SLOT_ITEM)) {
4078 //don't ruin the return variable, it contains the usable slot bits
4079 int flg = 0;
4080 if (ret&SLOT_QUIVER) {
4081 if (item->GetWeaponHeader(true)) flg = 1;
4084 if (ret&SLOT_WEAPON) {
4085 //melee
4086 if (item->GetWeaponHeader(false)) flg = 1;
4087 //ranged
4088 if (item->GetWeaponHeader(true)) flg = 1;
4091 if (ret&SLOT_ITEM) {
4092 if (item->GetEquipmentHeaderNumber(0)!=0xffff) flg = 1;
4095 if (!flg) {
4096 displaymsg->DisplayConstantString(STR_UNUSABLEITEM, 0xf0f0f0);
4097 return 0;
4102 return ret;
4105 Label *Interface::GetMessageLabel() const
4107 ieDword WinIndex = (ieDword) -1;
4108 ieDword TAIndex = (ieDword) -1;
4110 vars->Lookup( "OtherWindow", WinIndex );
4111 if (( WinIndex != (ieDword) -1 ) &&
4112 ( vars->Lookup( "MessageLabel", TAIndex ) )) {
4113 Window* win = GetWindow( (unsigned short) WinIndex );
4114 if (win) {
4115 Control *ctrl = win->GetControl( (unsigned short) TAIndex );
4116 if (ctrl && ctrl->ControlType==IE_GUI_LABEL)
4117 return (Label *) ctrl;
4120 return NULL;
4123 TextArea *Interface::GetMessageTextArea() const
4125 ieDword WinIndex = (ieDword) -1;
4126 ieDword TAIndex = (ieDword) -1;
4128 vars->Lookup( "MessageWindow", WinIndex );
4129 if (( WinIndex != (ieDword) -1 ) &&
4130 ( vars->Lookup( "MessageTextArea", TAIndex ) )) {
4131 Window* win = GetWindow( (unsigned short) WinIndex );
4132 if (win) {
4133 Control *ctrl = win->GetControl( (unsigned short) TAIndex );
4134 if (ctrl && ctrl->ControlType==IE_GUI_TEXTAREA)
4135 return (TextArea *) ctrl;
4138 return NULL;
4141 static const char *saved_extensions[]={".are",".sto",0};
4142 static const char *saved_extensions_last[]={".tot",".toh",0};
4144 //returns the priority of the file to be saved
4145 //2 - save
4146 //1 - save last
4147 //0 - don't save
4148 int Interface::SavedExtension(const char *filename)
4150 const char *str=strchr(filename,'.');
4151 if (!str) return 0;
4152 int i=0;
4153 while(saved_extensions[i]) {
4154 if (!stricmp(saved_extensions[i], str) ) return 2;
4155 i++;
4157 i=0;
4158 while(saved_extensions_last[i]) {
4159 if (!stricmp(saved_extensions_last[i], str) ) return 1;
4160 i++;
4162 return 0;
4165 static const char *protected_extensions[]={".exe",".dll",".so",0};
4167 //returns true if file should be saved
4168 bool Interface::ProtectedExtension(const char *filename)
4170 const char *str=strchr(filename,'.');
4171 if (!str) return false;
4172 int i=0;
4173 while(protected_extensions[i]) {
4174 if (!stricmp(protected_extensions[i], str) ) return true;
4175 i++;
4177 return false;
4180 void Interface::RemoveFromCache(const ieResRef resref, SClass_ID ClassID)
4182 char filename[_MAX_PATH];
4184 snprintf(filename, _MAX_PATH, "%s%.8s%s", CachePath, resref, TypeExt( ClassID ) );
4185 unlink ( filename);
4188 //this function checks if the path is eligible as a cache
4189 //if it contains a directory, or suspicious file extensions
4190 //we bail out, because the cache will be purged regularly.
4191 bool Interface::StupidityDetector(const char* Pt)
4193 char Path[_MAX_PATH];
4194 strcpy( Path, Pt );
4195 DirectoryIterator dir(Path);
4196 if (!dir) {
4197 printf("\n**cannot open**\n");
4198 return true;
4200 do {
4201 const char *name = dir.GetName();
4202 if (dir.IsDirectory()) {
4203 if (name[0] == '.') {
4204 if (name[1] == '\0')
4205 continue;
4206 if (name[1] == '.' && name[2] == '\0')
4207 continue;
4209 printf("\n**contains another dir**\n");
4210 return true; //a directory in there???
4212 if (ProtectedExtension(name) ) {
4213 printf("\n**contains alien files**\n");
4214 return true; //an executable file in there???
4216 } while (++dir);
4217 //ok, we got a good conscience
4218 return false;
4221 void Interface::DelTree(const char* Pt, bool onlysave)
4223 char Path[_MAX_PATH];
4225 if (!Pt[0]) return; //Don't delete the root filesystem :)
4226 strcpy( Path, Pt );
4227 DirectoryIterator dir(Path);
4228 if (!dir) {
4229 return;
4231 do {
4232 char *name = dir.GetName();
4233 if (dir.IsDirectory())
4234 continue;
4235 if (name[0] == '.')
4236 continue;
4237 if (!onlysave || SavedExtension(name) ) {
4238 char dtmp[_MAX_PATH];
4239 dir.GetFullPath(dtmp);
4240 unlink( dtmp );
4242 } while (++dir);
4245 void Interface::LoadProgress(int percent)
4247 vars->SetAt("Progress", percent);
4248 RedrawControls("Progress", percent);
4249 RedrawAll();
4250 DrawWindows();
4251 video->SwapBuffers();
4254 void Interface::ReleaseDraggedItem()
4256 DraggedItem=NULL; //shouldn't free this
4257 video->SetDragCursor (NULL);
4260 void Interface::DragItem(CREItem *item, const ieResRef Picture)
4262 //We should drop the dragged item and pick this up,
4263 //we shouldn't have a valid DraggedItem at this point.
4264 //Anyway, if there is still a dragged item, it will be destroyed.
4265 if (DraggedItem) {
4266 printMessage("Core","Forgot to call ReleaseDraggedItem when leaving inventory (item destroyed)!\n",YELLOW);
4267 delete DraggedItem;
4269 DraggedItem = item;
4270 if (video) {
4271 Sprite2D* DraggedCursor = NULL;
4272 if (item) {
4273 DraggedCursor = gamedata->GetBAMSprite( Picture, 0, 0 );
4275 video->SetDragCursor (DraggedCursor);
4279 void Interface::SetDraggedPortrait(int dp, int idx)
4281 if (idx<0) idx=14;
4282 DraggedPortrait = dp;
4283 if (dp) {
4284 //hmm this might work?
4285 Cursors[idx]->acquire();
4286 video->SetDragCursor(Cursors[idx]);
4287 } else {
4288 video->SetDragCursor(NULL);
4292 bool Interface::ReadItemTable(const ieResRef TableName, const char * Prefix)
4294 ieResRef ItemName;
4295 int i,j;
4297 AutoTable tab(TableName);
4298 if (!tab) {
4299 return false;
4301 i=tab->GetRowCount();
4302 for(j=0;j<i;j++) {
4303 if (Prefix) {
4304 snprintf(ItemName,sizeof(ItemName),"%s%02d",Prefix, j+1);
4305 } else {
4306 strnlwrcpy(ItemName,tab->GetRowName(j), 8);
4308 //Variable elements are free'd, so we have to use malloc
4309 //well, not anymore, we can use ReleaseFunction
4310 int l=tab->GetColumnCount(j);
4311 if (l<1) continue;
4312 int cl = atoi(tab->GetColumnName(0));
4313 ItemList *itemlist = new ItemList(l, cl);
4314 for(int k=0;k<l;k++) {
4315 strnlwrcpy(itemlist->ResRefs[k],tab->QueryField(j,k), 8);
4317 RtRows->SetAt(ItemName, (void*)itemlist);
4319 return true;
4322 bool Interface::ReadRandomItems()
4324 ieResRef RtResRef;
4325 int i;
4327 ieDword difflev=0; //rt norm or rt fury
4328 vars->Lookup("Nightmare Mode", difflev);
4329 if (RtRows) {
4330 RtRows->RemoveAll(ReleaseItemList);
4332 else {
4333 RtRows=new Variables(10, 17); //block size, hash table size
4334 if (!RtRows) {
4335 return false;
4337 RtRows->SetType( GEM_VARIABLES_POINTER );
4339 AutoTable tab("randitem");
4340 if (!tab) {
4341 return false;
4343 if (difflev>=tab->GetColumnCount()) {
4344 difflev = tab->GetColumnCount()-1;
4347 //the gold item
4348 strnlwrcpy( GoldResRef, tab->QueryField((unsigned int) 0,(unsigned int) 0), 8);
4349 if ( GoldResRef[0]=='*' ) {
4350 return false;
4352 strnlwrcpy( RtResRef, tab->QueryField( 1, difflev ), 8);
4353 i=atoi( RtResRef );
4354 if (i<1) {
4355 ReadItemTable( RtResRef, 0 ); //reading the table itself
4356 return true;
4358 if (i>5) {
4359 i=5;
4361 while(i--) {
4362 strnlwrcpy( RtResRef, tab->QueryField(2+i,difflev), 8);
4363 ReadItemTable( RtResRef,tab->GetRowName(2+i) );
4365 return true;
4368 CREItem *Interface::ReadItem(DataStream *str)
4370 CREItem *itm = new CREItem();
4372 str->ReadResRef( itm->ItemResRef );
4373 str->ReadWord( &itm->Expired );
4374 str->ReadWord( &itm->Usages[0] );
4375 str->ReadWord( &itm->Usages[1] );
4376 str->ReadWord( &itm->Usages[2] );
4377 str->ReadDword( &itm->Flags );
4378 if (ResolveRandomItem(itm) ) {
4379 return itm;
4381 delete itm;
4382 return NULL;
4385 #define MAX_LOOP 10
4387 //This function generates random items based on the randitem.2da file
4388 //there could be a loop, but we don't want to freeze, so there is a limit
4389 bool Interface::ResolveRandomItem(CREItem *itm)
4391 if (!RtRows) return true;
4392 for(int loop=0;loop<MAX_LOOP;loop++) {
4393 int i,j,k;
4394 char *endptr;
4395 ieResRef NewItem;
4397 void* lookup;
4398 if ( !RtRows->Lookup( itm->ItemResRef, lookup ) ) {
4399 return true;
4401 ItemList *itemlist = (ItemList*)lookup;
4402 if (itemlist->WeightOdds) {
4403 //instead of 1d19 we calculate with 2d10 (which also has 19 possible values)
4404 i=Roll(2,(itemlist->Count+1)/2,-2);
4405 } else {
4406 i=Roll(1,itemlist->Count,-1);
4408 strnlwrcpy( NewItem, itemlist->ResRefs[i], 8);
4409 char *p=(char *) strchr(NewItem,'*');
4410 if (p) {
4411 *p=0; //doing this so endptr is ok
4412 k=strtol(p+1,NULL,10);
4413 } else {
4414 k=1;
4416 j=strtol(NewItem,&endptr,10);
4417 if (j<1) {
4418 j=1;
4420 if (*endptr) {
4421 strnlwrcpy(itm->ItemResRef, NewItem, 8);
4422 } else {
4423 strnlwrcpy(itm->ItemResRef, GoldResRef, 8);
4425 if ( !memcmp( itm->ItemResRef,"no_drop",8 ) ) {
4426 itm->ItemResRef[0]=0;
4428 if (!itm->ItemResRef[0]) {
4429 return false;
4431 itm->Usages[0]=(ieWord) Roll(j,k,0);
4433 printMessage("Interface"," ",LIGHT_RED);
4434 printf("Loop detected while generating random item:%s\n",itm->ItemResRef);
4435 return false;
4438 //now that we store spell name in spl, i guess, we shouldn't pass 'ieResRef name'
4439 //these functions are needed because Win32 doesn't allow freeing memory from
4440 //another dll. So we allocate all commonly used memories from core
4441 ITMExtHeader *Interface::GetITMExt(int count)
4443 return new ITMExtHeader[count];
4446 SPLExtHeader *Interface::GetSPLExt(int count)
4448 return new SPLExtHeader[count];
4451 Effect *Interface::GetEffect(ieDword opcode)
4453 if (opcode==0xffffffff) {
4454 return NULL;
4456 Effect *fx = new Effect();
4457 if (!fx) {
4458 return NULL;
4460 memset(fx,0,sizeof(Effect));
4461 fx->Opcode=opcode;
4462 return fx;
4465 Effect *Interface::GetFeatures(int count)
4467 return new Effect[count];
4471 void Interface::FreeITMExt(ITMExtHeader *p, Effect *e)
4473 delete [] p;
4474 delete [] e;
4477 void Interface::FreeSPLExt(SPLExtHeader *p, Effect *e)
4479 delete [] p;
4480 delete [] e;
4484 WorldMapArray *Interface::NewWorldMapArray(int count)
4486 return new WorldMapArray(count);
4489 Container *Interface::GetCurrentContainer()
4491 return CurrentContainer;
4494 int Interface::CloseCurrentContainer()
4496 UseContainer = false;
4497 if ( !CurrentContainer) {
4498 return -1;
4500 //remove empty ground piles on closeup
4501 CurrentContainer->GetCurrentArea()->TMap->CleanupContainer(CurrentContainer);
4502 CurrentContainer = NULL;
4503 return 0;
4506 void Interface::SetCurrentContainer(Actor *actor, Container *arg, bool flag)
4508 //abort action if the first selected PC isn't the original actor
4509 if (actor!=GetFirstSelectedPC(false)) {
4510 CurrentContainer = NULL;
4511 return;
4513 CurrentContainer = arg;
4514 UseContainer = flag;
4517 Store *Interface::GetCurrentStore()
4519 return CurrentStore;
4522 int Interface::CloseCurrentStore()
4524 if ( !CurrentStore ) {
4525 return -1;
4527 PluginHolder<StoreMgr> sm(IE_STO_CLASS_ID);
4528 if (sm == NULL) {
4529 return -1;
4531 int size = sm->GetStoredFileSize (CurrentStore);
4532 if (size > 0) {
4533 //created streams are always autofree (close file on destruct)
4534 //this one will be destructed when we return from here
4535 FileStream str;
4537 str.Create( CurrentStore->Name, IE_STO_CLASS_ID );
4538 int ret = sm->PutStore (&str, CurrentStore);
4539 if (ret <0) {
4540 printMessage("Core"," ", YELLOW);
4541 printf("Store removed: %s\n", CurrentStore->Name);
4542 RemoveFromCache(CurrentStore->Name, IE_STO_CLASS_ID);
4544 } else {
4545 printMessage("Core"," ", YELLOW);
4546 printf("Store removed: %s\n", CurrentStore->Name);
4547 RemoveFromCache(CurrentStore->Name, IE_STO_CLASS_ID);
4549 //make sure the stream isn't connected to sm, or it will be double freed
4550 delete CurrentStore;
4551 CurrentStore = NULL;
4552 return 0;
4555 Store *Interface::SetCurrentStore(const ieResRef resname, const ieVariable owner)
4557 if ( CurrentStore ) {
4558 if ( !strnicmp(CurrentStore->Name, resname, 8) ) {
4559 return CurrentStore;
4562 //not simply delete the old store, but save it
4563 CloseCurrentStore();
4566 DataStream* str = gamedata->GetResource( resname, IE_STO_CLASS_ID );
4567 PluginHolder<StoreMgr> sm(IE_STO_CLASS_ID);
4568 if (sm == NULL) {
4569 delete ( str );
4570 return NULL;
4572 if (!sm->Open( str, true )) {
4573 return NULL;
4576 // FIXME - should use some already allocated in core
4577 // not really, only one store is open at a time, then it is
4578 // unloaded, we don't really have to cache it, it will be saved in
4579 // Cache anyway!
4580 CurrentStore = sm->GetStore( new Store() );
4581 if (CurrentStore == NULL) {
4582 return NULL;
4584 strnlwrcpy(CurrentStore->Name, resname, 8);
4585 if (owner) {
4586 CurrentStore->SetOwner(owner);
4588 return CurrentStore;
4591 void Interface::SetMouseScrollSpeed(int speed) {
4592 mousescrollspd = (speed+1)*2;
4595 int Interface::GetMouseScrollSpeed() {
4596 return mousescrollspd;
4599 ieStrRef Interface::GetRumour(const ieResRef dlgref)
4601 PluginHolder<DialogMgr> dm(IE_DLG_CLASS_ID);
4602 dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true );
4603 Dialog *dlg = dm->GetDialog();
4605 if (!dlg) {
4606 printMessage("Interface"," ", LIGHT_RED);
4607 printf( "Cannot load dialog: %s\n", dlgref );
4608 return (ieStrRef) -1;
4610 Scriptable *pc=game->GetPC( game->GetSelectedPCSingle(), false );
4612 ieStrRef ret = (ieStrRef) -1;
4613 int i = dlg->FindRandomState( pc );
4614 if (i>=0 ) {
4615 ret = dlg->GetState( i )->StrRef;
4617 delete dlg;
4618 return ret;
4621 void Interface::DoTheStoreHack(Store *s)
4623 size_t size = s->PurchasedCategoriesCount * sizeof( ieDword );
4624 s->purchased_categories=(ieDword *) malloc(size);
4626 size = s->CuresCount * sizeof( STOCure );
4627 s->cures=(STOCure *) malloc(size);
4629 size = s->DrinksCount * sizeof( STODrink );
4630 s->drinks=(STODrink *) malloc(size);
4632 for(size=0;size<s->ItemsCount;size++) {
4633 STOItem *si = new STOItem();
4634 memset(si, 0, sizeof(STOItem) );
4635 s->items.push_back( si );
4639 //plays stock sound listed in defsound.2da
4640 void Interface::PlaySound(int index)
4642 if (index<=DSCount) {
4643 AudioDriver->Play(DefSound[index]);
4647 Actor *Interface::GetFirstSelectedPC(bool forced)
4649 int partySize = game->GetPartySize( false );
4650 if (!partySize) return NULL;
4651 for (int i = 0; i < partySize; i++) {
4652 Actor* actor = game->GetPC( i,false );
4653 if (actor->IsSelected()) {
4654 return actor;
4658 if (forced) {
4659 return game->GetPC(0,false);
4661 return NULL;
4664 //this is used only for the console
4665 Sprite2D *Interface::GetCursorSprite()
4667 Sprite2D *spr = gamedata->GetBAMSprite(CursorBam, 0, 0);
4668 if (spr)
4670 if(HasFeature(GF_OVERRIDE_CURSORPOS))
4672 spr->XPos=1;
4673 spr->YPos=spr->Height-1;
4676 return spr;
4679 Sprite2D *Interface::GetScrollCursorSprite(int frameNum, int spriteNum)
4681 return gamedata->GetBAMSprite(ScrollCursorBam, frameNum, spriteNum);
4684 /* we should return -1 if it isn't gold, otherwise return the gold value */
4685 int Interface::CanMoveItem(const CREItem *item) const
4687 //This is an inventory slot, switch to IE_ITEM_* if you use Item
4688 if (item->Flags & IE_INV_ITEM_UNDROPPABLE)
4689 return 0;
4690 //not gold, we allow only one single coin ResRef, this is good
4691 //for all of the original games
4692 if (strnicmp(item->ItemResRef, GoldResRef, 8 ) )
4693 return -1;
4694 //gold, returns the gold value (stack size)
4695 return item->Usages[0];
4698 // dealing with applying effects
4699 void Interface::ApplySpell(const ieResRef resname, Actor *actor, Scriptable *caster, int level)
4701 Spell *spell = gamedata->GetSpell(resname);
4702 if (!spell) {
4703 return;
4706 level = spell->GetHeaderIndexFromLevel(level);
4707 EffectQueue *fxqueue = spell->GetEffectBlock(caster, actor->Pos, level);
4709 //check effect immunities
4710 int res = fxqueue->CheckImmunity ( actor );
4711 if (res) {
4712 if (res == -1) {
4713 //bounced back at a nonliving caster
4714 if (caster->Type!=ST_ACTOR) {
4715 delete fxqueue;
4716 return;
4718 actor = (Actor *) caster;
4720 fxqueue->SetOwner( caster );
4721 fxqueue->AddAllEffects(actor, actor->Pos);
4723 delete fxqueue;
4726 void Interface::ApplySpellPoint(const ieResRef resname, Map* area, const Point &pos, Scriptable *caster, int level)
4728 Spell *spell = gamedata->GetSpell(resname);
4729 if (!spell) {
4730 return;
4732 level = spell->GetHeaderIndexFromLevel(level);
4733 Projectile *pro = spell->GetProjectile(caster, level, pos);
4734 pro->SetCaster(caster->GetGlobalID());
4735 area->AddProjectile(pro, caster->Pos, pos);
4738 //-1 means the effect was reflected back to the caster
4739 //0 means the effect was resisted and should be removed
4740 //1 means the effect was applied
4741 int Interface::ApplyEffect(Effect *effect, Actor *actor, Scriptable *caster)
4743 if (!effect) {
4744 return 0;
4747 EffectQueue *fxqueue = new EffectQueue();
4748 //AddEffect now copies the fx data, please delete your effect reference
4749 //if you created it. (Don't delete cached references)
4750 fxqueue->AddEffect( effect );
4752 int res = fxqueue->CheckImmunity ( actor );
4753 if (res) {
4754 if (res == -1 ) {
4755 //bounced back at a nonliving caster
4756 if (caster->Type!=ST_ACTOR) {
4757 delete fxqueue;
4758 return 0;
4760 actor = (Actor *) caster;
4762 fxqueue->SetOwner( caster );
4763 Point p;
4765 p.empty(); //the effect should have all its coordinates already set
4766 if (fxqueue->AddAllEffects( actor, p )==FX_NOT_APPLIED) {
4767 res=0;
4770 delete fxqueue;
4771 return res;
4774 Effect *Interface::GetEffect(const ieResRef resname, int level, const Point &p)
4776 //Don't free this reference, it is cached!
4777 Effect *effect = gamedata->GetEffect(resname);
4778 if (!effect) {
4779 return NULL;
4781 if (!level) {
4782 level = 1;
4784 effect->Power = level;
4785 effect->PosX=p.x;
4786 effect->PosY=p.y;
4787 return effect;
4790 // dealing with saved games
4791 int Interface::SwapoutArea(Map *map)
4793 PluginHolder<MapMgr> mm(IE_ARE_CLASS_ID);
4794 if (mm == NULL) {
4795 return -1;
4797 int size = mm->GetStoredFileSize (map);
4798 if (size > 0) {
4799 //created streams are always autofree (close file on destruct)
4800 //this one will be destructed when we return from here
4801 FileStream str;
4803 str.Create( map->GetScriptName(), IE_ARE_CLASS_ID );
4804 int ret = mm->PutArea (&str, map);
4805 if (ret <0) {
4806 printMessage("Core"," ", YELLOW);
4807 printf("Area removed: %s\n", map->GetScriptName());
4808 RemoveFromCache(map->GetScriptName(), IE_ARE_CLASS_ID);
4810 } else {
4811 printMessage("Core"," ", YELLOW);
4812 printf("Area removed: %s\n", map->GetScriptName());
4813 RemoveFromCache(map->GetScriptName(), IE_ARE_CLASS_ID);
4815 //make sure the stream isn't connected to sm, or it will be double freed
4816 return 0;
4819 int Interface::WriteCharacter(const char *name, Actor *actor)
4821 char Path[_MAX_PATH];
4823 PathJoin( Path, GamePath, GameCharactersPath, NULL );
4824 if (!actor) {
4825 return -1;
4827 PluginHolder<ActorMgr> gm(IE_CRE_CLASS_ID);
4828 if (gm == NULL) {
4829 return -1;
4832 //str is freed
4834 FileStream str;
4836 str.Create( Path, name, IE_CHR_CLASS_ID );
4838 int ret = gm->PutActor(&str, actor, true);
4839 if (ret <0) {
4840 printMessage("Core"," ", YELLOW);
4841 printf("Character cannot be saved: %s\n", name);
4842 return -1;
4846 //write the BIO string
4847 if (!HasFeature(GF_NO_BIOGRAPHY)) {
4848 FileStream str;
4850 str.Create( Path, name, IE_BIO_CLASS_ID );
4851 //never write the string reference into this string
4852 char *tmp = GetString(actor->GetVerbalConstant(VB_BIO),IE_STR_STRREFOFF);
4853 str.Write (tmp, strlen(tmp));
4854 free(tmp);
4856 return 0;
4859 int Interface::WriteGame(const char *folder)
4861 PluginHolder<SaveGameMgr> gm(IE_GAM_CLASS_ID);
4862 if (gm == NULL) {
4863 return -1;
4866 int size = gm->GetStoredFileSize (game);
4867 if (size > 0) {
4868 //created streams are always autofree (close file on destruct)
4869 //this one will be destructed when we return from here
4870 FileStream str;
4872 str.Create( folder, GameNameResRef, IE_GAM_CLASS_ID );
4873 int ret = gm->PutGame (&str, game);
4874 if (ret <0) {
4875 printMessage("Core"," ", YELLOW);
4876 printf("Game cannot be saved: %s\n", GameNameResRef);
4878 } else {
4879 printMessage("Core"," ", YELLOW);
4880 printf("Internal error, game cannot be saved: %s\n", GameNameResRef);
4882 //make sure the stream isn't connected to sm, or it will be double freed
4883 return 0;
4886 int Interface::WriteWorldMap(const char *folder)
4888 PluginHolder<WorldMapMgr> wmm(IE_WMP_CLASS_ID);
4889 if (wmm == NULL) {
4890 return -1;
4893 int size = wmm->GetStoredFileSize (worldmap);
4894 if (size > 0) {
4895 //created streams are always autofree (close file on destruct)
4896 //this one will be destructed when we return from here
4897 FileStream str;
4899 str.Create( folder, WorldMapName, IE_WMP_CLASS_ID );
4900 int ret = wmm->PutWorldMap (&str, worldmap);
4901 if (ret <0) {
4902 printMessage("Core"," ", YELLOW);
4903 printf("Internal error, worldmap cannot be saved: %s\n", WorldMapName);
4905 } else {
4906 printMessage("Core"," ", YELLOW);
4907 printf("Internal error, worldmap cannot be saved: %s\n", WorldMapName);
4909 //make sure the stream isn't connected to sm, or it will be double freed
4910 return 0;
4913 int Interface::CompressSave(const char *folder)
4915 FileStream str;
4917 str.Create( folder, GameNameResRef, IE_SAV_CLASS_ID );
4918 DirectoryIterator dir(CachePath);
4919 if (!dir) {
4920 return -1;
4922 //BIF and SAV are the same
4923 PluginHolder<ArchiveImporter> ai(IE_BIF_CLASS_ID);
4924 ai->CreateArchive( &str);
4926 //.tot and .toh should be saved last, because they are updated when an .are is saved
4927 int priority=2;
4928 while(priority) {
4929 do {
4930 const char *name = dir.GetName();
4931 if (dir.IsDirectory())
4932 continue;
4933 if (name[0] == '.')
4934 continue;
4935 if (SavedExtension(name)==priority) {
4936 char dtmp[_MAX_PATH];
4937 dir.GetFullPath(dtmp);
4938 FileStream fs;
4939 fs.Open(dtmp, true);
4940 ai->AddToSaveGame(&str, &fs);
4942 } while (++dir);
4943 //reopen list for the second round
4944 priority--;
4945 if (priority>0) {
4946 dir.Rewind();
4949 return 0;
4952 int Interface::GetMaximumAbility() const { return MaximumAbility; }
4954 int Interface::GetStrengthBonus(int column, int value, int ex) const
4956 //to hit, damage, open doors, weight allowance
4957 if (column<0 || column>3)
4958 return -9999;
4960 if (value<0)
4961 value = 0;
4962 else if (value>25)
4963 value = 25;
4965 if (ex<0)
4966 ex=0;
4967 else if (ex>100)
4968 ex=100;
4970 return strmod[column*(MaximumAbility+1)+value]+strmodex[column*101+ex];
4973 //only the first 3 columns are supported
4974 int Interface::GetIntelligenceBonus(int column, int value) const
4976 //learn spell, max spell level, max spell number on level
4977 if (column<0 || column>2)
4978 return -9999;
4980 return intmod[column*(MaximumAbility+1)+value];
4983 int Interface::GetDexterityBonus(int column, int value) const
4985 //reaction, missile, ac
4986 if (column<0 || column>2)
4987 return -9999;
4989 //no dexmod in iwd2???
4990 if (HasFeature(GF_3ED_RULES)) return 0;
4992 return dexmod[column*(MaximumAbility+1)+value];
4995 int Interface::GetConstitutionBonus(int column, int value) const
4997 //normal, warrior, minimum, regen hp, regen fatigue
4998 if (column<0 || column>4)
4999 return -9999;
5001 return conmod[column*(MaximumAbility+1)+value];
5004 int Interface::GetCharismaBonus(int column, int value) const
5006 //?reaction
5007 if (column<0 || column>0)
5008 return -9999;
5010 return chrmod[column*(MaximumAbility+1)+value];
5013 int Interface::GetLoreBonus(int column, int value) const
5015 if (column<0 || column>0)
5016 return -9999;
5018 //no lorebon in iwd2???
5019 if (HasFeature(GF_3ED_RULES)) return 0;
5021 return lorebon[value];
5024 int Interface::GetWisdomBonus(int column, int value) const
5026 // xp bonus
5027 if (column<0 || column>0)
5028 return -9999;
5030 if (!HasFeature(GF_WISDOM_BONUS)) return 0;
5032 return wisbon[value];
5035 int Interface::GetReputationMod(int column) const
5037 int reputation = game->Reputation / 10 - 1;
5039 if (column<0 || column>8) {
5040 return -9999;
5042 if (reputation > 19) {
5043 reputation = 19;
5045 if (reputation < 0) {
5046 reputation = 0;
5049 return reputationmod[reputation][column];
5052 // -3, -2 if request is illegal or in cutscene
5053 // -1 if pause is already active
5054 // 0 if pause was not allowed
5055 // 1 if autopause happened
5056 int Interface::Autopause(ieDword flag)
5058 GameControl *gc = GetGameControl();
5059 if (!gc) {
5060 return -3;
5062 if (InCutSceneMode()) {
5063 return -2;
5065 if (gc->GetDialogueFlags()&DF_FREEZE_SCRIPTS) {
5066 return -1;
5068 ieDword autopause_flags = 0;
5070 vars->Lookup("Auto Pause State", autopause_flags);
5071 if (autopause_flags & (1<<flag)) {
5072 displaymsg->DisplayConstantString(STR_AP_UNUSABLE+flag, 0xff0000);
5073 gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR);
5074 return 1;
5076 return 0;
5079 void Interface::RegisterOpcodes(int count, const EffectRef *opcodes)
5081 EffectQueue_RegisterOpcodes(count, opcodes);
5084 void Interface::SetInfoTextColor(const Color &color)
5086 if (InfoTextPalette) {
5087 gamedata->FreePalette(InfoTextPalette);
5089 InfoTextPalette = CreatePalette(color, black);
5092 //todo row?
5093 void Interface::GetResRefFrom2DA(const ieResRef resref, ieResRef resource1, ieResRef resource2, ieResRef resource3)
5095 if (!resource1) {
5096 return;
5098 resource1[0]=0;
5099 if (resource2) {
5100 resource2[0]=0;
5102 if (resource3) {
5103 resource3[0]=0;
5105 AutoTable tab(resref);
5106 if (tab) {
5107 unsigned int cols = tab->GetColumnCount();
5108 unsigned int row = (unsigned int) Roll(1,tab->GetRowCount(),-1);
5109 strnuprcpy(resource1, tab->QueryField(row,0), 8);
5110 if (resource2 && cols>1)
5111 strnuprcpy(resource2, tab->QueryField(row,1), 8);
5112 if (resource3 && cols>2)
5113 strnuprcpy(resource3, tab->QueryField(row,2), 8);
5117 ieDword *Interface::GetListFrom2DA(const ieResRef resref)
5119 ieDword *ret;
5121 AutoTable tab(resref);
5122 if (tab) {
5123 ieDword cnt = tab->GetRowCount();
5124 ret = (ieDword *) malloc((1+cnt)*sizeof(ieDword));
5125 ret[0]=cnt;
5126 while(cnt) {
5127 ret[cnt]=strtol(tab->QueryField(cnt-1, 0),NULL, 0);
5128 cnt--;
5130 return ret;
5132 ret = (ieDword *) malloc(sizeof(ieDword));
5133 ret[0]=0;
5134 return ret;
5137 //returns a numeric value associated with a stat name (symbol) from stats.ids
5138 ieDword Interface::TranslateStat(const char *stat_name)
5140 long tmp;
5142 if (valid_number(stat_name, tmp)) {
5143 return (ieDword) tmp;
5146 int symbol = LoadSymbol( "stats" );
5147 Holder<SymbolMgr> sym = GetSymbol( symbol );
5148 ieDword stat = (ieDword) sym->GetValue( stat_name );
5149 if (stat==(ieDword) ~0) {
5150 printMessage("Core"," ",YELLOW);
5151 printf("Cannot translate symbol: %s\n", stat_name);
5153 return stat;
5156 void Interface::WaitForDisc(int disc_number, const char* path)
5158 GetDictionary()->SetAt( "WaitForDisc", (ieDword) disc_number );
5160 GetGUIScriptEngine()->RunFunction( "GUICommonWindows", "OpenWaitForDiscWindow" );
5161 do {
5162 DrawWindows();
5163 for (size_t i=0;i<CD[disc_number-1].size();i++) {
5164 char name[_MAX_PATH];
5166 PathJoin(name, CD[disc_number-1][i].c_str(),path,NULL);
5167 if (file_exists (name)) {
5168 GetGUIScriptEngine()->RunFunction( "GUICommonWindows", "OpenWaitForDiscWindow" );
5169 return;
5173 } while (video->SwapBuffers() == GEM_OK);
5176 // remove the extraneus EOL newline and carriage return
5177 void Interface::StripLine(char * string, size_t size) {
5178 if (size >= 2 && string[size-2] == '\n') {
5179 string[size-2] = '\0';
5181 if (size >= 3 && string[size-3] == '\r') {
5182 string[size-3] = '\0'; // remove the carriage return too
5186 void Interface::SetNextScript(const char *script)
5188 strncpy( NextScript, script, sizeof(NextScript) );
5189 QuitFlag |= QF_CHANGESCRIPT;
5192 void Interface::SanityCheck(const char *ver) {
5193 if (strcmp(ver, VERSION_GEMRB)) {
5194 printf("version check failed: core version %s doesn't match caller's version %s\n", VERSION_GEMRB, ver);
5195 abort();