CodingStyle: Document header include order.
[gemrb.git] / gemrb / core / Game.cpp
blob6aaab085ec3c2ad0ad8ce1c718c9bcb089544c24
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2004 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 // This class represents the .gam (savegame) file in the engine
23 #include "Game.h"
25 #include "defsounds.h"
26 #include "strrefs.h"
27 #include "win32def.h"
29 #include "DataStream.h"
30 #include "GameControl.h"
31 #include "GameData.h"
32 #include "GameScript.h"
33 #include "Interface.h"
34 #include "MapMgr.h"
35 #include "MusicMgr.h"
36 #include "ScriptEngine.h"
38 #define MAX_MAPS_LOADED 1
40 Game::Game(void) : Scriptable( ST_GLOBAL )
42 protagonist = PM_YES; //set it to 2 for iwd/iwd2 and 0 for pst
43 partysize = 6;
44 Ticks = 0;
45 version = 0;
46 Expansion = 0;
47 LoadMos[0] = 0;
48 SelectedSingle = 1; //the PC we are looking at (inventory, shop)
49 PartyGold = 0;
50 SetScript( core->GlobalScript, 0 );
51 MapIndex = -1;
52 Reputation = 0;
53 ControlStatus = 0;
54 CombatCounter = 0; //stored here until we know better
55 StateOverrideTime = 0;
56 StateOverrideFlag = 0;
57 BanterBlockTime = 0;
58 BanterBlockFlag = 0;
59 WeatherBits = 0;
60 crtable = NULL;
61 kaputz = NULL;
62 beasts = NULL;
63 mazedata = NULL;
64 timestop_owner = NULL;
65 timestop_end = 0;
66 event_timer = 0;
67 event_handler[0] = 0;
68 weather = new Particles(200);
69 weather->SetRegion(0, 0, core->Width, core->Height);
70 LastScriptUpdate = 0;
72 //loading master areas
73 AutoTable table;
74 if (table.load("mastarea")) {
75 int i = table->GetRowCount();
76 mastarea.reserve(i);
77 while(i--) {
78 char *tmp = (char *) malloc(9);
79 strnuprcpy (tmp,table->QueryField(i,0),8);
80 mastarea.push_back( tmp );
84 //loading rest/daylight switching movies (only bg2 has them)
85 memset(restmovies,'*',sizeof(restmovies));
86 memset(daymovies,'*',sizeof(restmovies));
87 memset(nightmovies,'*',sizeof(restmovies));
88 if (table.load("restmov")) {
89 for(int i=0;i<8;i++) {
90 strnuprcpy(restmovies[i],table->QueryField(i,0),8);
91 strnuprcpy(daymovies[i],table->QueryField(i,1),8);
92 strnuprcpy(nightmovies[i],table->QueryField(i,2),8);
96 interval = 1000/AI_UPDATE_TIME;
97 //FIXME:i'm not sure in this...
98 NoInterrupt();
101 Game::~Game(void)
103 size_t i;
105 delete weather;
106 for (i = 0; i < Maps.size(); i++) {
107 delete( Maps[i] );
109 for (i = 0; i < PCs.size(); i++) {
110 delete ( PCs[i] );
112 for (i = 0; i < NPCs.size(); i++) {
113 delete ( NPCs[i] );
115 for (i = 0; i < mastarea.size(); i++) {
116 free ( mastarea[i] );
119 if (crtable) {
120 delete[] crtable;
123 if (mazedata) {
124 free (mazedata);
126 if (kaputz) {
127 delete kaputz;
129 if (beasts) {
130 free (beasts);
132 i=Journals.size();
133 while(i--) {
134 delete Journals[i];
137 i=savedpositions.size();
138 while(i--) {
139 delete savedpositions[i];
142 i=planepositions.size();
143 while(i--) {
144 delete planepositions[i];
148 bool IsAlive(Actor *pc)
150 if (pc->GetStat(IE_STATE_ID)&STATE_DEAD) {
151 return false;
153 return true;
156 int Game::FindPlayer(unsigned int partyID)
158 for (unsigned int slot=0; slot<PCs.size(); slot++) {
159 if (PCs[slot]->InParty==partyID) {
160 return slot;
163 return -1;
166 Actor* Game::FindPC(unsigned int partyID)
168 for (unsigned int slot=0; slot<PCs.size(); slot++) {
169 if (PCs[slot]->InParty==partyID) return PCs[slot];
171 return NULL;
174 Actor* Game::FindPC(const char *scriptingname)
176 for (unsigned int slot=0; slot<PCs.size(); slot++) {
177 if (strnicmp(PCs[slot]->GetScriptName(),scriptingname,32)==0 ) {
178 return PCs[slot];
181 return NULL;
184 Actor* Game::FindNPC(unsigned int partyID)
186 for (unsigned int slot=0; slot<NPCs.size(); slot++) {
187 if (NPCs[slot]->InParty==partyID) return NPCs[slot];
189 return NULL;
192 Actor* Game::FindNPC(const char *scriptingname)
194 for (unsigned int slot=0; slot<NPCs.size(); slot++) {
195 if (strnicmp(NPCs[slot]->GetScriptName(),scriptingname,32)==0 )
197 return NPCs[slot];
200 return NULL;
203 Actor* Game::GetPC(unsigned int slot, bool onlyalive)
205 if (slot >= PCs.size()) {
206 return NULL;
208 if (onlyalive) {
209 unsigned int i=0;
210 while(i<PCs.size() ) {
211 Actor *ac = PCs[i++];
213 if (IsAlive(ac) ) {
214 if (!slot--) {
215 return ac;
219 return NULL;
221 return PCs[slot];
224 int Game::InStore(Actor* pc) const
226 for (unsigned int i = 0; i < NPCs.size(); i++) {
227 if (NPCs[i] == pc) {
228 return i;
231 return -1;
234 int Game::InParty(Actor* pc) const
236 for (unsigned int i = 0; i < PCs.size(); i++) {
237 if (PCs[i] == pc) {
238 return i;
241 return -1;
244 int Game::DelPC(unsigned int slot, bool autoFree)
246 if (slot >= PCs.size()) {
247 return -1;
249 if (!PCs[slot]) {
250 return -1;
252 SelectActor(PCs[slot], false, SELECT_NORMAL);
253 if (autoFree) {
254 delete( PCs[slot] );
256 std::vector< Actor*>::iterator m = PCs.begin() + slot;
257 PCs.erase( m );
258 return 0;
261 int Game::DelNPC(unsigned int slot, bool autoFree)
263 if (slot >= NPCs.size()) {
264 return -1;
266 if (!NPCs[slot]) {
267 return -1;
269 if (autoFree) {
270 delete( NPCs[slot] );
272 std::vector< Actor*>::iterator m = NPCs.begin() + slot;
273 NPCs.erase( m );
274 return 0;
277 //i'm sure this could be faster
278 void Game::ConsolidateParty()
280 int max = (int) PCs.size();
281 std::vector< Actor*>::const_iterator m;
282 for (int i=1;i<=max;) {
283 if (FindPlayer(i)==-1) {
285 for ( m = PCs.begin(); m != PCs.end(); ++m) {
286 if ( (*m)->InParty>i) {
287 (*m)->InParty--;
290 } else i++;
292 for ( m = PCs.begin(); m != PCs.end(); ++m) {
293 (*m)->RefreshEffects(NULL);
297 int Game::LeaveParty (Actor* actor)
299 actor->CreateStats(); //create or update stats for leaving
300 actor->SetBase(IE_EXPLORE, 0);
301 SelectActor(actor, false, SELECT_NORMAL);
302 int slot = InParty( actor );
303 if (slot < 0) {
304 return slot;
306 std::vector< Actor*>::iterator m = PCs.begin() + slot;
307 PCs.erase( m );
309 for ( m = PCs.begin(); m != PCs.end(); ++m) {
310 if ( (*m)->InParty>actor->InParty) {
311 (*m)->InParty--;
314 //removing from party, but actor remains in 'game'
315 actor->SetPersistent(0);
316 NPCs.push_back( actor );
318 if (core->HasFeature( GF_HAS_DPLAYER )) {
319 actor->SetScript( "", SCR_DEFAULT );
321 actor->SetBase( IE_EA, EA_NEUTRAL );
322 return ( int ) NPCs.size() - 1;
325 //determines if startpos.2da has rotation rows (it cannot have tutorial line)
326 bool Game::DetermineStartPosType(const TableMgr *strta)
328 if ((strta->GetRowCount()>=6) && !stricmp(strta->GetRowName(4),"START_ROT" ) )
330 return true;
332 return false;
335 void Game::InitActorPos(Actor *actor)
337 //start.2da row labels
338 const char *mode[3] = { "NORMAL", "TUTORIAL", "EXPANSION" };
340 unsigned int ip = (unsigned int) (actor->InParty-1);
341 AutoTable start("start");
342 AutoTable strta("startpos");
343 // 0 - single player, 1 - tutorial, 2 - expansion
344 ieDword playmode = 0;
345 core->GetDictionary()->Lookup( "PlayMode", playmode );
346 const char *xpos = start->QueryField(mode[playmode],"XPOS");
347 const char *ypos = start->QueryField(mode[playmode],"YPOS");
348 const char *area = start->QueryField(mode[playmode],"AREA");
349 const char *rot = start->QueryField(mode[playmode],"ROT");
351 actor->Pos.x = actor->Destination.x = (short) atoi( strta->QueryField( strta->GetRowIndex(xpos), ip ) );
352 actor->Pos.y = actor->Destination.y = (short) atoi( strta->QueryField( strta->GetRowIndex(ypos), ip ) );
353 actor->SetOrientation( atoi( strta->QueryField( strta->GetRowIndex(rot), ip) ), false );
355 strta.load("startare");
356 strnlwrcpy(actor->Area, strta->QueryField( strta->GetRowIndex(area), 0 ), 8 );
357 //TODO: set viewport
358 // strta->QueryField(strta->GetRowIndex(xpos),0);
359 // strta->QueryField(strta->GetColumnIndex(ypos),0);
361 SelectActor(actor,true, SELECT_QUIET);
364 int Game::JoinParty(Actor* actor, int join)
366 actor->CreateStats(); //create stats if they didn't exist yet
367 actor->InitButtons(actor->GetStat(IE_CLASS), false); //init actor's buttons
368 actor->SetBase(IE_EXPLORE, 1);
369 if (join&JP_INITPOS) {
370 InitActorPos(actor);
372 int slot = InParty( actor );
373 if (slot != -1) {
374 return slot;
376 if (join&JP_JOIN) {
377 actor->PCStats->JoinDate = GameTime;
378 if (!PCs.size() ) {
379 Reputation = actor->GetStat(IE_REPUTATION);
382 slot = InStore( actor );
383 if (slot >= 0) {
384 std::vector< Actor*>::iterator m = NPCs.begin() + slot;
385 NPCs.erase( m );
387 size_t size = PCs.size();
389 PCs.push_back( actor );
390 if (!actor->InParty) {
391 actor->InParty = (ieByte) (size+1);
394 return ( int ) size;
397 int Game::GetPartySize(bool onlyalive) const
399 if (onlyalive) {
400 int count = 0;
401 for (unsigned int i = 0; i < PCs.size(); i++) {
402 if (!IsAlive(PCs[i])) {
403 continue;
405 count++;
407 return count;
409 return (int) PCs.size();
412 /* sends the hotkey trigger to all selected actors */
413 void Game::SetHotKey(unsigned long Key)
415 std::vector< Actor*>::const_iterator m;
417 for ( m = selected.begin(); m != selected.end(); ++m) {
418 Actor *actor = *m;
420 if (actor->IsSelected()) {
421 actor->HotKey = (ieDword) Key;
426 bool Game::SelectPCSingle(int index)
428 Actor* actor = FindPC( index );
429 if (!actor || ! actor->ValidTarget( GA_SELECT | GA_NO_DEAD | GA_NO_HIDDEN ))
430 return false;
432 SelectedSingle = index;
433 return true;
436 int Game::GetSelectedPCSingle() const
438 return SelectedSingle;
442 * SelectActor() - handle (de)selecting actors.
443 * If selection was changed, runs "SelectionChanged" handler
445 * actor - either specific actor, or NULL for all
446 * select - whether actor(s) should be selected or deselected
447 * flags:
448 * SELECT_REPLACE - if true, deselect all other actors when selecting one
449 * SELECT_QUIET - do not run handler if selection was changed. Used for
450 * nested calls to SelectActor()
453 bool Game::SelectActor(Actor* actor, bool select, unsigned flags)
455 std::vector< Actor*>::iterator m;
457 // actor was not specified, which means all PCs should be (de)selected
458 if (! actor) {
459 for ( m = selected.begin(); m != selected.end(); ++m) {
460 (*m)->Select( false );
461 (*m)->SetOver( false );
464 selected.clear();
465 if (select) {
466 for ( m = PCs.begin(); m != PCs.end(); ++m) {
467 if (! *m) {
468 continue;
470 SelectActor( *m, true, SELECT_QUIET );
474 if (! (flags & SELECT_QUIET)) {
475 core->SetEventFlag(EF_SELECTION);
477 return true;
480 // actor was specified, so we will work with him
482 // If actor is already (de)selected, report success, but do nothing
483 //if (actor->IsSelected() == select)
484 // return true;
487 if (select) {
488 if (! actor->ValidTarget( GA_SELECT | GA_NO_DEAD ))
489 return false;
491 // deselect all actors first when exclusive
492 if (flags & SELECT_REPLACE) {
493 SelectActor( NULL, false, SELECT_QUIET );
496 actor->Select( true );
497 selected.push_back( actor );
498 } else {
499 for ( m = selected.begin(); m != selected.end(); ++m) {
500 if ((*m) == actor) {
501 selected.erase( m );
502 break;
505 actor->Select( false );
508 if (! (flags & SELECT_QUIET)) {
509 core->SetEventFlag(EF_SELECTION);
511 return true;
514 // Gets average party level, of onlyalive is true, then counts only living PCs
515 int Game::GetPartyLevel(bool onlyalive) const
517 int count = 0;
518 for (unsigned int i = 0; i<PCs.size(); i++) {
519 if (onlyalive) {
520 if (PCs[i]->GetStat(IE_STATE_ID)&STATE_DEAD) {
521 continue;
524 count += PCs[i]->GetXPLevel(0);
526 return count;
529 // Returns map structure (ARE) if it is already loaded in memory
530 int Game::FindMap(const char *ResRef)
532 int index = (int) Maps.size();
533 while (index--) {
534 Map *map=Maps[index];
535 if (strnicmp(ResRef, map->GetScriptName(), 8) == 0) {
536 return index;
539 return -1;
542 Map* Game::GetMap(unsigned int index) const
544 if (index >= Maps.size()) {
545 return NULL;
547 return Maps[index];
550 Map *Game::GetMap(const char *areaname, bool change)
552 int index = LoadMap(areaname);
553 if (index >= 0) {
554 if (change) {
555 MapIndex = index;
556 area = GetMap(index);
557 memcpy (CurrentArea, areaname, 8);
558 area->SetupAmbients();
559 //change the tileset if needed
560 area->ChangeMap(IsDay());
561 ChangeSong();
562 return area;
564 return GetMap(index);
566 return NULL;
569 bool Game::MasterArea(const char *area)
571 unsigned int i=(int) mastarea.size();
572 while(i--) {
573 if (strnicmp(mastarea[i], area, 8) ) {
574 return true;
577 return false;
580 void Game::SetMasterArea(const char *area)
582 if (MasterArea(area) ) return;
583 char *tmp = (char *) malloc(9);
584 strnlwrcpy (tmp,area,8);
585 mastarea.push_back(tmp);
588 int Game::AddMap(Map* map)
590 if (MasterArea(map->GetScriptName()) ) {
591 Maps.insert(Maps.begin(), 1, map);
592 MapIndex++;
593 return 0;
595 unsigned int i = (unsigned int) Maps.size();
596 Maps.push_back( map );
597 return i;
600 int Game::DelMap(unsigned int index, int forced)
602 //this function should archive the area, and remove it only if the area
603 //contains no active actors (combat, partymembers, etc)
604 if (index >= Maps.size()) {
605 return -1;
607 Map *map = Maps[index];
609 if (MapIndex==(int) index) { //can't remove current map in any case
610 const char *name = map->GetScriptName();
611 memcpy(AnotherArea, name, sizeof(AnotherArea) );
612 return -1;
616 if (!map) { //this shouldn't happen, i guess
617 printMessage("Game","Erased NULL Map\n",YELLOW);
618 Maps.erase( Maps.begin()+index);
619 if (MapIndex>(int) index) {
620 MapIndex--;
622 return 1;
625 if (forced || (Maps.size()>MAX_MAPS_LOADED) )
627 //keep at least one master
628 const char *name = map->GetScriptName();
629 if (MasterArea(name)) {
630 if(!AnotherArea[0]) {
631 memcpy(AnotherArea, name, sizeof(AnotherArea));
632 if (!forced) {
633 return -1;
637 //this check must be the last, because
638 //after PurgeActors you cannot keep the
639 //area in memory
640 //Or the queues should be regenerated!
641 if (!map->CanFree())
643 return 1;
645 //remove map from memory
646 core->SwapoutArea(Maps[index]);
647 delete( Maps[index] );
648 Maps.erase( Maps.begin()+index);
649 //current map will be decreased
650 if (MapIndex>(int) index) {
651 MapIndex--;
653 return 1;
655 //didn't remove the map
656 return 0;
659 /* Loads an area, changepf == true if you want to setup the pathfinder too */
660 //FIXME: changepf is removed now
661 int Game::LoadMap(const char* ResRef)
663 unsigned int i;
664 int index = FindMap(ResRef);
665 if (index>=0) {
666 return index;
669 DataStream* ds = gamedata->GetResource( ResRef, IE_ARE_CLASS_ID );
670 if (!ds) {
671 return -1;
673 PluginHolder<MapMgr> mM(IE_ARE_CLASS_ID);
674 if(!mM->Open( ds, true )) {
675 return -1;
677 Map* newMap = mM->GetMap(ResRef, IsDay());
678 if (!newMap) {
679 return -1;
682 for (i = 0; i < PCs.size(); i++) {
683 if (stricmp( PCs[i]->Area, ResRef ) == 0) {
684 newMap->AddActor( PCs[i] );
687 for (i = 0; i < NPCs.size(); i++) {
688 if (stricmp( NPCs[i]->Area, ResRef ) == 0) {
689 newMap->AddActor( NPCs[i] );
692 return AddMap( newMap );
695 int Game::AddNPC(Actor* npc)
697 int slot = InStore( npc ); //already an npc
698 if (slot != -1) {
699 return slot;
701 slot = InParty( npc );
702 if (slot != -1) {
703 return -1;
704 } //can't add as npc already in party
705 npc->SetPersistent(0);
706 NPCs.push_back( npc );
708 return (int) NPCs.size() - 1;
711 Actor* Game::GetNPC(unsigned int Index)
713 if (Index >= NPCs.size()) {
714 return NULL;
716 return NPCs[Index];
719 void Game::SwapPCs(unsigned int Index1, unsigned int Index2)
721 if (Index1 >= PCs.size()) {
722 return;
725 if (Index2 >= PCs.size()) {
726 return;
728 int tmp = PCs[Index1]->InParty;
729 PCs[Index1]->InParty = PCs[Index2]->InParty;
730 PCs[Index2]->InParty = tmp;
731 //signal a change of the portrait window
732 core->SetEventFlag(EF_PORTRAIT);
735 void Game::DeleteJournalEntry(ieStrRef strref)
737 size_t i=Journals.size();
738 while(i--) {
739 if (Journals[i]->Text==strref) {
740 delete Journals[i];
741 Journals.erase(Journals.begin()+i);
746 void Game::DeleteJournalGroup(int Group)
748 size_t i=Journals.size();
749 while(i--) {
750 if (Journals[i]->Group==(ieByte) Group) {
751 delete Journals[i];
752 Journals.erase(Journals.begin()+i);
756 /* returns true if it modified or added a journal entry */
757 bool Game::AddJournalEntry(ieStrRef strref, int Section, int Group)
759 GAMJournalEntry *je = FindJournalEntry(strref);
760 if (je) {
761 //don't set this entry again in the same section
762 if (je->Section==Section) {
763 return false;
765 if ((Section == IE_GAM_QUEST_DONE) && Group) {
766 //removing all of this group and adding a new entry
767 DeleteJournalGroup(Group);
768 } else {
769 //modifying existing entry
770 je->Section = (ieByte) Section;
771 je->Group = (ieByte) Group;
772 ieDword chapter = 0;
773 locals->Lookup("CHAPTER", chapter);
774 je->Chapter = (ieByte) chapter;
775 je->GameTime = GameTime;
776 return true;
779 je = new GAMJournalEntry;
780 je->GameTime = GameTime;
781 ieDword chapter = 0;
782 locals->Lookup("CHAPTER", chapter);
783 je->Chapter = (ieByte) chapter;
784 je->Section = (ieByte) Section;
785 je->Group = (ieByte) Group;
786 je->Text = strref;
788 Journals.push_back( je );
789 return true;
792 void Game::AddJournalEntry(GAMJournalEntry* entry)
794 Journals.push_back( entry );
797 unsigned int Game::GetJournalCount() const
799 return (unsigned int) Journals.size();
802 GAMJournalEntry* Game::FindJournalEntry(ieStrRef strref)
804 unsigned int Index = (unsigned int) Journals.size();
805 while(Index--) {
806 GAMJournalEntry *ret = Journals[Index];
808 if (ret->Text==strref) {
809 return ret;
813 return NULL;
816 GAMJournalEntry* Game::GetJournalEntry(unsigned int Index)
818 if (Index >= Journals.size()) {
819 return NULL;
821 return Journals[Index];
824 unsigned int Game::GetSavedLocationCount() const
826 return (unsigned int) savedpositions.size();
829 void Game::ClearSavedLocations()
831 size_t i=savedpositions.size();
832 while(i--) {
833 delete savedpositions[i];
835 savedpositions.clear();
838 GAMLocationEntry* Game::GetSavedLocationEntry(unsigned int i)
840 size_t current = savedpositions.size();
841 if (i>=current) {
842 if (i>PCs.size()) {
843 return NULL;
845 savedpositions.resize(i+1);
846 while(current<=i) {
847 savedpositions[current++]=(GAMLocationEntry *) calloc(1, sizeof(GAMLocationEntry) );
850 return savedpositions[i];
853 unsigned int Game::GetPlaneLocationCount() const
855 return (unsigned int) planepositions.size();
858 void Game::ClearPlaneLocations()
860 size_t i=planepositions.size();
861 while(i--) {
862 delete planepositions[i];
864 planepositions.clear();
867 GAMLocationEntry* Game::GetPlaneLocationEntry(unsigned int i)
869 size_t current = planepositions.size();
870 if (i>=current) {
871 if (i>PCs.size()) {
872 return NULL;
874 planepositions.resize(i+1);
875 while(current<=i) {
876 planepositions[current++]=(GAMLocationEntry *) calloc(1, sizeof(GAMLocationEntry) );
879 return planepositions[i];
882 char *Game::GetFamiliar(unsigned int Index)
884 return Familiars[Index];
887 //reading the challenge rating table for iwd2 (only when needed)
888 void Game::LoadCRTable()
890 AutoTable table("moncrate");
891 if (table.ok()) {
892 int maxrow = table->GetRowCount()-1;
893 crtable = new CRRow[MAX_LEVEL];
894 for(int i=0;i<MAX_LEVEL;i++) {
895 //row shouldn't be larger than maxrow
896 int row = i<maxrow?i:maxrow;
897 int maxcol = table->GetColumnCount(row)-1;
898 for(int j=0;j<MAX_CRLEVEL;j++) {
899 //col shouldn't be larger than maxcol
900 int col = j<maxcol?j:maxcol;
901 crtable[i][j]=atoi(table->QueryField(row,col) );
907 int Game::GetXPFromCR(int cr)
909 if (!crtable) LoadCRTable();
910 if (crtable) {
911 int level = GetPartyLevel(true);
912 if (cr>=MAX_CRLEVEL) {
913 cr=MAX_CRLEVEL-1;
915 printf("Challenge Rating: %d, party level: %d ", cr, level);
916 return crtable[level][cr];
918 printMessage("Game","Cannot find moncrate.2da!\n", LIGHT_RED);
919 return 0;
922 void Game::ShareXP(int xp, int flags)
924 int individual;
926 if (flags&SX_CR) {
927 xp = GetXPFromCR(xp);
930 if (flags&SX_DIVIDE) {
931 int PartySize = GetPartySize(true); //party size, only alive
932 if (PartySize<1) {
933 return;
935 individual = xp / PartySize;
936 } else {
937 individual = xp;
940 if (!individual) {
941 return;
944 if (xp>0) {
945 core->DisplayConstantStringValue( STR_GOTXP, 0xbcefbc, (ieDword) xp); //you have gained ... xp
946 } else {
947 core->DisplayConstantStringValue( STR_LOSTXP, 0xbcefbc, (ieDword) -xp); //you have lost ... xp
949 for (unsigned int i=0; i<PCs.size(); i++) {
950 if (PCs[i]->GetStat(IE_STATE_ID)&STATE_DEAD) {
951 continue;
953 PCs[i]->AddExperience(individual);
957 bool Game::EveryoneStopped() const
959 for (unsigned int i=0; i<PCs.size(); i++) {
960 if (PCs[i]->GetNextStep() ) return false;
962 return true;
965 //canmove=true: if some PC can't move (or hostile), then this returns false
966 bool Game::EveryoneNearPoint(Map *area, const Point &p, int flags) const
968 for (unsigned int i=0; i<PCs.size(); i++) {
969 if (flags&ENP_ONLYSELECT) {
970 if(!PCs[i]->Selected) {
971 continue;
974 if (PCs[i]->GetStat(IE_STATE_ID)&STATE_DEAD) {
975 continue;
977 if (flags&ENP_CANMOVE) {
978 //someone is uncontrollable, can't move
979 if (PCs[i]->GetStat(IE_EA)>EA_GOODCUTOFF) {
980 return false;
983 if (PCs[i]->GetStat(IE_STATE_ID)&STATE_CANTMOVE) {
984 return false;
987 if (PCs[i]->GetCurrentArea()!=area) {
988 return false;
990 if (Distance(p,PCs[i])>MAX_TRAVELING_DISTANCE) {
991 return false;
994 return true;
997 //called when someone died
998 void Game::PartyMemberDied(Actor *actor)
1000 //this could be null, in some extreme cases...
1001 Map *area = actor->GetCurrentArea();
1003 for (unsigned int i=0; i<PCs.size(); i++) {
1004 if (PCs[i]==actor) {
1005 continue;
1007 if (PCs[i]->GetStat(IE_STATE_ID)&STATE_DEAD) {
1008 continue;
1010 if (PCs[i]->GetCurrentArea()!=area) {
1011 continue;
1013 PCs[i]->ReactToDeath(actor->GetScriptName());
1017 //reports if someone died
1018 int Game::PartyMemberDied() const
1020 for (unsigned int i=0; i<PCs.size(); i++) {
1021 if (PCs[i]->GetInternalFlag()&IF_JUSTDIED) {
1022 return i;
1025 return -1;
1028 void Game::IncrementChapter()
1030 //chapter first set to 0 (prologue)
1031 ieDword chapter = (ieDword) -1;
1032 locals->Lookup("CHAPTER",chapter);
1033 locals->SetAt("CHAPTER",chapter+1);
1034 //clear statistics
1035 for (unsigned int i=0; i<PCs.size(); i++) {
1036 //all PCs must have this!
1037 PCs[i]->PCStats->IncrementChapter();
1041 void Game::SetReputation(ieDword r)
1043 if (r<10) r=10;
1044 else if (r>200) r=200;
1045 if (Reputation>r) {
1046 core->DisplayConstantStringValue(STR_LOSTREP,0xc0c000,(Reputation-r)/10);
1047 } else if (Reputation<r) {
1048 core->DisplayConstantStringValue(STR_GOTREP,0xc0c000,(r-Reputation)/10);
1050 Reputation = r;
1051 for (unsigned int i=0; i<PCs.size(); i++) {
1052 PCs[i]->SetBase(IE_REPUTATION, Reputation);
1056 void Game::SetControlStatus(int value, int mode)
1058 switch(mode) {
1059 case BM_OR: ControlStatus|=value; break;
1060 case BM_NAND: ControlStatus&=~value; break;
1061 case BM_SET: ControlStatus=value; break;
1062 case BM_AND: ControlStatus&=value; break;
1063 case BM_XOR: ControlStatus^=value; break;
1065 core->SetEventFlag(EF_CONTROL);
1068 void Game::AddGold(ieDword add)
1070 ieDword old;
1072 if (!add) {
1073 return;
1075 old = PartyGold;
1076 PartyGold += add;
1077 if (old<PartyGold) {
1078 core->DisplayConstantStringValue( STR_GOTGOLD, 0xc0c000, PartyGold-old);
1079 } else {
1080 core->DisplayConstantStringValue( STR_LOSTGOLD, 0xc0c000, old-PartyGold);
1084 //later this could be more complicated
1085 void Game::AdvanceTime(ieDword add)
1087 ieDword h = GameTime/(300*AI_UPDATE_TIME);
1088 GameTime+=add;
1089 if (h!=GameTime/(300*AI_UPDATE_TIME)) {
1090 //asking for a new weather when the hour changes
1091 WeatherBits&=~WB_HASWEATHER;
1093 Ticks+=add*interval;
1094 //change the tileset if needed
1095 Map *map = GetCurrentArea();
1096 if (map && map->ChangeMap(IsDay())) {
1097 //play the daylight transition movie appropriate for the area
1098 //it is needed to play only when the area truly changed its tileset
1099 //this is signalled by ChangeMap
1100 int areatype = (area->AreaType&(AT_FOREST|AT_CITY|AT_DUNGEON))>>3;
1101 ieResRef *res;
1103 printMessage("Game","Switching DayLight\n",GREEN);
1104 if (IsDay()) {
1105 res=&nightmovies[areatype];
1106 } else {
1107 res=&daymovies[areatype];
1109 if (*res[0]!='*') {
1110 core->PlayMovie(*res);
1115 //returns true if there are excess players in the team
1116 bool Game::PartyOverflow() const
1118 GameControl *gc = core->GetGameControl();
1119 if (!gc) {
1120 return false;
1122 //don't start this screen when the gui is busy
1123 if (gc->GetDialogueFlags() & (DF_IN_DIALOG|DF_IN_CONTAINER|DF_FREEZE_SCRIPTS) ) {
1124 return false;
1126 if (!partysize) {
1127 return false;
1129 return (PCs.size()>partysize);
1132 bool Game::PCInCombat(Actor* actor) const
1134 if (!CombatCounter) {
1135 return false;
1138 if (actor->LastTarget) {
1139 return true;
1141 if (AttackersOf(actor->GetID(), actor->GetCurrentArea())) {
1142 return true;
1144 return false;
1147 bool Game::AnyPCInCombat() const
1149 if (!CombatCounter) {
1150 return false;
1153 for (unsigned int i=0; i<PCs.size(); i++) {
1154 if (PCInCombat (PCs[i])) {
1155 return true;
1158 return false;
1161 //returns true if the protagonist (or the whole party died)
1162 bool Game::EveryoneDead() const
1164 //if there are no PCs, then we assume everyone dead
1165 if (!PCs.size() ) {
1166 return true;
1168 if (protagonist==PM_NO) {
1169 Actor *nameless = PCs[0];
1170 if (nameless->GetStat(IE_STATE_ID)&STATE_NOSAVE) {
1171 if (area->INISpawn) {
1172 area->INISpawn->RespawnNameless();
1175 return false;
1177 // if protagonist died
1178 if (protagonist==PM_YES) {
1179 if (PCs[0]->GetStat(IE_STATE_ID)&STATE_NOSAVE) {
1180 return true;
1182 return false;
1184 //protagonist == 2
1185 for (unsigned int i=0; i<PCs.size(); i++) {
1186 if (!(PCs[i]->GetStat(IE_STATE_ID)&STATE_NOSAVE) ) {
1187 return false;
1190 return true;
1193 //runs all area scripts
1195 void Game::UpdateScripts()
1197 ExecuteScript( 1 );
1198 ProcessActions(false);
1199 size_t idx;
1201 for (idx=0;idx<Maps.size();idx++) {
1202 Maps[idx]->UpdateScripts();
1203 size_t acnt=Attackers.size();
1204 while(acnt--) {
1205 Actor *actor = Maps[idx]->GetActorByGlobalID(Attackers[acnt]);
1206 if (actor) {
1207 //each attacker handles their own round initiation
1208 //FIXME: individual combat counter
1209 CombatCounter++;
1210 actor->InitRound(GameTime, !(CombatCounter&1) );
1215 if (StateOverrideTime)
1216 StateOverrideTime--;
1217 if (BanterBlockTime)
1218 BanterBlockTime--;
1220 if (Maps.size()>MAX_MAPS_LOADED) {
1221 idx = Maps.size();
1223 //starting from 0, so we see the most recent master area first
1224 for(unsigned int i=0;i<idx;i++) {
1225 DelMap( (unsigned int) i, false );
1229 // perhaps a StartMusic action stopped the area music?
1230 // (we should probably find a less silly way to handle this,
1231 // because nothing can ever stop area music now..)
1232 if (!core->GetMusicMgr()->IsPlaying()) {
1233 ChangeSong(false);
1236 //this is used only for the death delay so far
1237 if (event_handler[0]) {
1238 if (!event_timer) {
1239 core->GetGUIScriptEngine()->RunFunction(event_handler);
1240 event_handler[0]=0;
1242 event_timer--;
1245 if (EveryoneDead()) {
1246 //don't check it any more
1247 protagonist = PM_NO;
1248 core->GetGUIScriptEngine()->RunFunction("DeathWindow");
1249 return;
1252 if (PartyOverflow()) {
1253 partysize = 0;
1254 core->GetGUIScriptEngine()->RunFunction("OpenReformPartyWindow");
1255 return;
1259 void Game::SetTimedEvent(const char *fname, int count)
1261 event_timer = count;
1262 strncpy(event_handler, fname, sizeof(event_handler) );
1265 void Game::SetProtagonistMode(int mode)
1267 protagonist = mode;
1270 void Game::SetPartySize(int size)
1272 // 0 size means no party size control
1273 if (size<0) {
1274 return;
1276 partysize = (size_t) size;
1279 //Get the area dependent rest movie
1280 ieResRef *Game::GetDream(Map *area)
1282 //select dream based on area
1283 int daynight = IsDay();
1284 if (area->Dream[daynight][0]) {
1285 return area->Dream+daynight;
1287 int dream = (area->AreaType&(AT_FOREST|AT_CITY|AT_DUNGEON))>>3;
1288 return restmovies+dream;
1291 //Start dream cutscenes for player1
1292 void Game::PlayerDream()
1294 Scriptable *Sender = GetPC(0,true);
1295 if (!Sender) return;
1297 GameScript* gs = new GameScript( "player1d", Sender,0,0 );
1298 gs->Update();
1299 delete( gs );
1302 //noareacheck = no random encounters
1303 //dream = 0 - based on area non-0 - select from list
1304 //-1 no dream
1305 //hp is how much hp the rest will heal
1306 void Game::RestParty(int checks, int dream, int hp)
1308 if (!(checks&REST_NOMOVE) ) {
1309 if (!EveryoneStopped()) {
1310 return;
1313 Actor *leader = GetPC(0, true);
1314 if (!leader) {
1315 return;
1318 Map *area = leader->GetCurrentArea();
1319 //we let them rest if someone is paralyzed, but the others gather around
1320 if (!(checks&REST_NOSCATTER) ) {
1321 if (!EveryoneNearPoint( area, leader->Pos, 0 ) ) {
1322 //party too scattered
1323 core->DisplayConstantString( STR_SCATTERED, 0xff0000 );
1324 return;
1328 if (!(checks&REST_NOCRITTER) ) {
1329 //don't allow resting while in combat
1330 if (AnyPCInCombat()) {
1331 core->DisplayConstantString( STR_CANTRESTMONS, 0xff0000 );
1332 return;
1334 //don't allow resting if hostiles are nearby
1335 if (area->AnyEnemyNearPoint(leader->Pos)) {
1336 core->DisplayConstantString( STR_CANTRESTMONS, 0xff0000 );
1337 return;
1341 //rest check, if PartyRested should be set, area should return true
1342 //area should advance gametime too (so partial rest is possible)
1343 int hours = 8;
1344 if (!(checks&REST_NOAREA) ) {
1345 //you cannot rest here
1346 if (area->AreaFlags&1) {
1347 core->DisplayConstantString( STR_MAYNOTREST, 0xff0000 );
1348 return;
1350 //you may not rest here, find an inn
1351 if (!(area->AreaType&(AT_OUTDOOR|AT_FOREST|AT_DUNGEON|AT_CAN_REST) ))
1353 core->DisplayConstantString( STR_MAYNOTREST, 0xff0000 );
1354 return;
1356 //area encounters
1357 if(area->Rest( leader->Pos, 8, (GameTime/AI_UPDATE_TIME)%7200/3600) ) {
1358 return;
1361 AdvanceTime(2400*AI_UPDATE_TIME);
1364 int i = GetPartySize(true); // party size, only alive
1366 while (i--) {
1367 Actor *tar = GetPC(i, true);
1368 tar->ClearPath();
1369 tar->ClearActions();
1370 tar->SetModal(MS_NONE, 0);
1371 //if hp = 0, then healing will be complete
1372 tar->Heal(hp);
1373 //removes fatigue, recharges spells
1374 tar->Rest(0);
1375 tar->PartyRested();
1378 //movie and cutscene dreams
1379 if (dream>=0) {
1381 //cutscene dreams
1382 if (gamedata->Exists("player1d",IE_BCS_CLASS_ID, true))
1383 PlayerDream();
1385 //select dream based on area
1386 ieResRef *movie;
1387 if (dream==0 || dream>7) {
1388 movie = GetDream(area);
1389 } else {
1390 movie = restmovies+dream;
1392 if (*movie[0]!='*') {
1393 core->PlayMovie(*movie);
1397 //set partyrested flags
1398 PartyRested();
1399 area->PartyRested();
1400 core->SetEventFlag(EF_ACTION);
1402 //restindex will be -1 in the case of PST
1403 int restindex = core->GetStringReference(STR_REST);
1404 int strindex;
1405 char* tmpstr = NULL;
1407 core->GetTokenDictionary()->SetAtCopy("HOUR", hours);
1408 if (restindex != -1) {
1409 strindex = core->GetStringReference(STR_HOURS);
1410 } else {
1411 strindex = core->GetStringReference(STR_PST_HOURS);
1412 restindex = core->GetStringReference(STR_PST_REST);
1415 //this would be bad
1416 if (strindex == -1 || restindex == -1) return;
1417 tmpstr = core->GetString(strindex, 0);
1418 //as would this
1419 if (!tmpstr) return;
1421 core->GetTokenDictionary()->SetAtCopy("DURATION", tmpstr);
1422 core->FreeString(tmpstr);
1423 core->DisplayString(restindex, 0xffffff, 0);
1426 //timestop effect
1427 void Game::TimeStop(Actor* owner, ieDword end)
1429 timestop_owner=owner;
1430 timestop_end=GameTime+end;
1433 //returns the colour which should be applied onto the whole game area viewport
1434 //this is based on timestop, dream area, weather, daytime
1436 static const Color TimeStopTint={0xe0,0xe0,0xe0,0x20}; //greyscale
1437 static const Color DreamTint={0xf0,0xe0,0xd0,0x10}; //light brown scale
1438 static const Color NightTint={0x80,0x80,0xe0,0x40}; //dark, bluish
1439 static const Color DuskTint={0xe0,0x80,0x80,0x40}; //dark, reddish
1440 static const Color FogTint={0xff,0xff,0xff,0x40}; //whitish
1441 static const Color DarkTint={0x80,0x80,0xe0,0x10}; //slightly dark bluish
1443 const Color *Game::GetGlobalTint() const
1445 if (timestop_end>GameTime) {
1446 return &TimeStopTint;
1448 Map *map = GetCurrentArea();
1449 if (!map) return NULL;
1450 if (map->AreaFlags&AF_DREAM) {
1451 return &DreamTint;
1453 if ((map->AreaType&(AT_OUTDOOR|AT_DAYNIGHT|AT_EXTENDED_NIGHT)) == (AT_OUTDOOR|AT_DAYNIGHT) ) {
1454 //get daytime colour
1455 ieDword daynight = ((GameTime/AI_UPDATE_TIME)%7200/300);
1456 if (daynight<2 || daynight>22) {
1457 return &NightTint;
1459 if (daynight>20 || daynight<4) {
1460 return &DuskTint;
1463 if ((map->AreaType&(AT_OUTDOOR|AT_WEATHER)) == (AT_OUTDOOR|AT_WEATHER)) {
1464 //get weather tint
1465 if (WeatherBits&WB_RAIN) {
1466 return &DarkTint;
1468 if (WeatherBits&WB_FOG) {
1469 return &FogTint;
1472 return NULL;
1475 bool Game::IsDay()
1477 ieDword daynight = ((GameTime/AI_UPDATE_TIME)%7200/300);
1478 if(daynight<4 || daynight>20) {
1479 return false;
1481 return true;
1484 void Game::InAttack(ieDword globalID)
1486 std::vector< ieDword>::const_iterator idx;
1488 for(idx=Attackers.begin(); idx!=Attackers.end();idx++) {
1489 if (*idx==globalID) return;
1491 Attackers.push_back(globalID);
1492 if (!CombatCounter) {
1493 CombatCounter++;
1494 ChangeSong();
1498 void Game::OutAttack(ieDword globalID)
1500 std::vector< ieDword>::iterator idx;
1502 for(idx=Attackers.begin(); idx!=Attackers.end();idx++) {
1503 if (*idx==globalID) {
1504 Attackers.erase(idx);
1505 if (!Attackers.size()) {
1506 CombatCounter = 0;
1507 ChangeSong();
1509 break;
1514 void Game::ChangeSong(bool force)
1516 int Song;
1518 if (CombatCounter) {
1519 //battlesong
1520 Song = 3;
1521 } else {
1522 //night or day
1523 Song = (GameTime/AI_UPDATE_TIME)%7200/3600;
1525 //area may override the song played (stick in battlemusic)
1526 area->PlayAreaSong( Song, force );
1529 int Game::AttackersOf(ieDword globalID, Map *area) const
1531 if (!area) {
1532 return 0;
1534 std::vector< ieDword>::const_iterator idx;
1536 int cnt = 0;
1537 for(idx=Attackers.begin(); idx!=Attackers.end();idx++) {
1538 Actor * actor = area->GetActorByGlobalID(*idx);
1539 if (actor) {
1540 if (actor->LastTarget==globalID) {
1541 cnt++;
1545 return cnt;
1548 /* this method redraws weather. If update is false,
1549 then the weather particles won't change (game paused)
1551 void Game::DrawWeather(const Region &screen, bool update)
1553 if (!weather) {
1554 return;
1556 if (!area->HasWeather()) {
1557 return;
1560 weather->Draw( screen );
1561 if (!update) {
1562 return;
1565 if (!(WeatherBits & (WB_RAIN|WB_SNOW)) ) {
1566 if (weather->GetPhase() == P_GROW) {
1567 weather->SetPhase(P_FADE);
1570 //if (GameTime&1) {
1571 int drawn = weather->Update();
1572 if (drawn) {
1573 WeatherBits &= ~WB_START;
1577 if (WeatherBits&WB_HASWEATHER) {
1578 return;
1580 StartRainOrSnow(true, area->GetWeather());
1583 /* sets the weather type */
1584 void Game::StartRainOrSnow(bool conditional, int w)
1586 if (conditional && (w & (WB_RAIN|WB_SNOW)) ) {
1587 if (WeatherBits & (WB_RAIN | WB_SNOW) )
1588 return;
1590 // whatever was responsible for calling this, we now have some set weather
1591 WeatherBits = w | WB_HASWEATHER;
1592 if (w & WB_LIGHTNING) {
1593 if (WeatherBits&WB_START) {
1594 //already raining
1595 if (GameTime&1) {
1596 core->PlaySound(DS_LIGHTNING1);
1597 } else {
1598 core->PlaySound(DS_LIGHTNING2);
1600 } else {
1601 //start raining (far)
1602 core->PlaySound(DS_LIGHTNING3);
1605 if (w&WB_SNOW) {
1606 core->PlaySound(DS_SNOW);
1607 weather->SetType(SP_TYPE_POINT, SP_PATH_FLIT, SP_SPAWN_SOME);
1608 weather->SetPhase(P_GROW);
1609 weather->SetColor(SPARK_COLOR_WHITE);
1610 return;
1612 if (w&WB_RAIN) {
1613 core->PlaySound(DS_RAIN);
1614 weather->SetType(SP_TYPE_LINE, SP_PATH_RAIN, SP_SPAWN_SOME);
1615 weather->SetPhase(P_GROW);
1616 weather->SetColor(SPARK_COLOR_STONE);
1617 return;
1619 weather->SetPhase(P_FADE);
1622 void Game::SetExpansion(int exp)
1624 Expansion = exp;
1627 void Game::DebugDump()
1629 size_t idx;
1631 printf("Currently loaded areas:\n");
1632 for(idx=0;idx<Maps.size();idx++) {
1633 Map *map = Maps[idx];
1635 printf("%s\n",map->GetScriptName());
1637 printf("CombatCounter: %d\n", (int) CombatCounter);
1638 printf("Attackers count: %d\n", (int) Attackers.size());
1639 printf("Party size: %d\n", (int) PCs.size());
1640 for(idx=0;idx<PCs.size();idx++) {
1641 Actor *actor = PCs[idx];
1643 printf("Name: %s Order %d\n",actor->ShortName, actor->InParty);
1647 Actor *Game::GetActorByGlobalID(ieWord objectID) {
1648 size_t mc = GetLoadedMapCount();
1649 while(mc--) {
1650 Map *map = GetMap(mc);
1651 Actor *actor = map->GetActorByGlobalID(objectID);
1652 if (actor) return actor;
1654 return NULL;