1 /* GemRB - Infinity Engine Emulator 2 * Copyright (C) 2003 The GemRB Project 3 * 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. 8 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. 13 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. 17 * 18 * 19 */ 20 21 /** 22 * @file Game.h 23 * Declares Game class, object representing current game state. 24 * @author The GemRB Project 25 */ 26 27 28 29 #ifndef GAME_H 30 #define GAME_H 31 32 #include "exports.h" 33 #include "ie_types.h" 34 35 #include "Callback.h" 36 #include "Resource.h" 37 #include "Scriptable/Scriptable.h" 38 #include "Scriptable/PCStatStruct.h" 39 #include "Variables.h" 40 #include "Video.h" 41 42 #include <atomic> 43 #include <vector> 44 45 namespace GemRB { 46 47 class Actor; 48 class Map; 49 class Particles; 50 class TableMgr; 51 52 //the size of the bestiary register 53 #define BESTIARY_SIZE 260 54 55 //ShareXP flags 56 #define SX_DIVIDE 1 //divide XP among team members 57 #define SX_CR 2 //use challenge rating resolution 58 #define SX_COMBAT 4 //combat xp, adjusted by difficulty 59 60 //joinparty flags 61 #define JP_JOIN 1 //refresh join time 62 #define JP_INITPOS 2 //init startpos 63 #define JP_SELECT 4 //select the actor after joining 64 65 //protagonist mode 66 #define PM_NO 0 //no death checks 67 #define PM_YES 1 //if protagonist dies, game over 68 #define PM_TEAM 2 //if team dies, game over 69 70 // Flags bits for SelectActor() 71 // !!! Keep these synchronized with GUIDefines.py !!! 72 #define SELECT_NORMAL 0x00 73 #define SELECT_REPLACE 0x01 // when selecting actor, deselect all others 74 #define SELECT_QUIET 0x02 // do not run handler when changing selection 75 76 // Flags bits for EveryoneNearPoint() 77 #define ENP_CANMOVE 1 // also check if the PC can move 78 #define ENP_ONLYSELECT 2 // check only selected PC 79 80 // GUI Control Status flags (saved in game) 81 #define CS_PARTY_AI 1 //enable party AI 82 #define CS_MEDIUM 2 //medium dialog 83 #define CS_LARGE 6 //large dialog, both bits set 84 #define CS_DIALOGSIZEMASK 6 85 #define CS_DIALOG 8 //dialog is running 86 #define CS_HIDEGUI 16 //hide all gui 87 #define CS_ACTION 32 //hide action pane 88 #define CS_PORTRAIT 64 //hide portrait pane 89 #define CS_MAPNOTES 128 //hide mapnotes 90 91 //Weather bits 92 #define WB_NORMAL 0 93 #define WB_RAIN 1 94 #define WB_SNOW 2 95 #define WB_FOG 3 96 #define WB_TYPEMASK 3 97 #define WB_LIGHTRAIN 4 98 #define WB_MEDIUMRAIN 8 99 #define WB_HEAVYRAIN 12 100 #define WB_RAINMASK 12 101 #define WB_LIGHTWIND 0x10 102 #define WB_MEDWIND 0x20 103 #define WB_STRONGWING 0x30 104 #define WB_WINDMASK 0x30 105 106 #define WB_RARELIGHTNING 0x40 107 #define WB_MEDLIGHTNING 0x80 108 #define WB_HEAVYLIGHTNING 0xc0 109 #define WB_LIGHTNINGMASK 0xc0 110 #define WB_INCREASESTORM 0x100 111 #define WB_HASWEATHER 0x200 112 113 //Rest flags 114 #define REST_NOCHECKS 0 115 #define REST_AREA 1 // area checks 116 #define REST_SCATTER 2 // scattered party check 117 #define REST_CONTROL 4 // control check 118 #define REST_CRITTER 8 // hostiles check 119 120 //Song types (hardcoded) 121 #define SONG_DAY 0 122 #define SONG_NIGHT 1 123 #define SONG_BATTLE 3 124 125 /** 126 * @struct PCStruct 127 * Information about party member. 128 */ 129 130 struct PCStruct { 131 ieWord Selected; 132 ieWord PartyOrder; 133 ieDword OffsetToCRE; 134 ieDword CRESize; 135 ieResRef CREResRef; 136 ieDword Orientation; 137 ieResRef Area; 138 ieWord XPos; 139 ieWord YPos; 140 ieWord ViewXPos; 141 ieWord ViewYPos; 142 ieWord ModalState; 143 ieWordSigned Happiness; 144 ieDword Interact[MAX_INTERACT]; 145 ieWord QuickWeaponSlot[MAX_QUICKWEAPONSLOT]; 146 ieWord QuickWeaponHeader[MAX_QUICKWEAPONSLOT]; 147 ieResRef QuickSpellResRef[MAX_QSLOTS]; 148 ieWord QuickItemSlot[MAX_QUICKITEMSLOT]; 149 ieWord QuickItemHeader[MAX_QUICKITEMSLOT]; 150 char Name[32]; 151 ieDword TalkCount; 152 ieByte QSlots[GUIBT_COUNT]; 153 ieByte QuickSpellClass[MAX_QSLOTS]; 154 }; 155 156 #define IE_GAM_JOURNAL 0 157 #define IE_GAM_QUEST_UNSOLVED 1 158 #define IE_GAM_QUEST_DONE 2 159 #define IE_GAM_JOURNAL_USER 3 160 161 /** 162 * @struct GAMJournalEntry 163 * Single entry in a journal 164 */ 165 166 struct GAMJournalEntry { 167 ieStrRef Text; 168 ieDword GameTime; // in game time seconds 169 ieByte Chapter; 170 ieByte unknown09; 171 ieByte Section; 172 ieByte Group; // this is a GemRB extension 173 }; 174 175 // Saved location of party member. 176 struct GAMLocationEntry { 177 ieResRef AreaResRef; 178 Point Pos; 179 }; 180 181 //pst maze data structures (TODO: create a separate class?) 182 struct maze_entry { 183 ieDword me_override; 184 ieDword accessible; 185 ieDword valid; 186 ieDword trapped; 187 ieDword traptype; 188 ieWord walls; 189 ieDword visited; 190 }; 191 192 struct maze_header { 193 ieDword maze_sizex, maze_sizey; 194 ieDword pos1x, pos1y; //nordom's position 195 ieDword pos2x, pos2y; //main hall position 196 ieDword pos3x, pos3y; //foyer entrance 197 ieDword pos4x, pos4y; //unknown 198 ieDword trapcount; //based on map size 199 ieDword initialized; //set to 1 200 ieDword unknown2c; //unknown 201 ieDword unknown30; //unknown 202 }; 203 204 #define MAZE_ENTRY_SIZE sizeof(maze_entry) 205 #define MAZE_HEADER_SIZE sizeof(maze_header) 206 #define MAZE_MAX_DIM 8 207 #define MAZE_ENTRY_COUNT (MAZE_MAX_DIM*MAZE_MAX_DIM) 208 #define MAZE_DATA_SIZE (MAZE_ENTRY_COUNT*MAZE_ENTRY_SIZE+MAZE_HEADER_SIZE) 209 #define MAZE_DATA_SIZE_HARDCODED 1720 210 211 //maze header indices 212 #define MH_POS1X 0 213 #define MH_POS1Y 1 214 #define MH_POS2X 2 215 #define MH_POS2Y 3 216 #define MH_POS3X 4 217 #define MH_POS3Y 5 218 #define MH_POS4X 6 219 #define MH_POS4Y 7 220 #define MH_TRAPCOUNT 8 221 #define MH_INITED 9 222 #define MH_UNKNOWN2C 10 223 #define MH_UNKNOWN30 11 224 225 //maze entry indices 226 #define ME_OVERRIDE 0 227 #define ME_VALID 1 228 #define ME_ACCESSIBLE 2 229 #define ME_TRAP 3 230 #define ME_WALLS 4 231 #define ME_VISITED 5 232 233 //ME_WALL bitfields 234 #define WALL_SOUTH 1 235 #define WALL_NORTH 2 236 #define WALL_EAST 4 237 #define WALL_WEST 8 238 239 #define MAX_CRLEVEL 32 240 241 typedef int CRRow[MAX_CRLEVEL]; 242 243 /** 244 * @class Game 245 * Object representing current game state, mostly party. 246 */ 247 248 class GEM_EXPORT Game : public Scriptable { 249 public: 250 Game(void); 251 ~Game(void) override; 252 private: 253 std::vector< Actor*> PCs; 254 std::vector< Actor*> NPCs; 255 std::vector< Map*> Maps; 256 std::vector< GAMJournalEntry*> Journals; 257 std::vector< GAMLocationEntry*> savedpositions; 258 std::vector< GAMLocationEntry*> planepositions; 259 std::vector< char*> mastarea; 260 std::vector<std::vector<ResRef> > npclevels; 261 int *bntchnc; 262 int bntrows; 263 CRRow *crtable = nullptr; 264 ieResRef restmovies[8]; 265 ieResRef daymovies[8]; 266 ieResRef nightmovies[8]; 267 int MapIndex; 268 public: 269 std::vector< Actor*> selected; 270 int version; 271 Variables* kaputz; 272 ieByte* beasts; 273 ieByte* mazedata; //only in PST 274 ieResRef Familiars[9]; 275 ieDword CombatCounter; 276 ieDword StateOverrideFlag, StateOverrideTime; 277 ieDword BanterBlockFlag, BanterBlockTime; 278 279 /** Index of PC selected in non-walking environment (shops, inventory...) */ 280 int SelectedSingle; 281 /** 0 if the protagonist's death doesn't cause game over */ 282 /** 1 if the protagonist's death causes game over */ 283 /** 2 if no check is needed (pst) */ 284 int protagonist; 285 /** if party size exceeds this amount, a callback will be called */ 286 size_t partysize; 287 ieDword Ticks; 288 ieDword interval; // 1000/AI_UPDATE (a tenth of a round in ms) 289 std::atomic_uint32_t GameTime {0}; 290 ieDword LastScriptUpdate; // GameTime at which UpdateScripts last ran 291 ieDword RealTime; 292 ieWord WhichFormation; 293 ieWord Formations[5]; 294 ieDword PartyGold; 295 ieWord NPCAreaViewed = 0; 296 ieWord WeatherBits; 297 ieDword CurrentLink; //named currentLink in original engine (set to -1) 298 ieDword Reputation; 299 ieDword ControlStatus; // used in bg2, iwd (where you can switch panes off) 300 ieDword Expansion; // mostly used by BG2. IWD games set it to 3 on newgame 301 ieResRef AnotherArea; 302 ieResRef CurrentArea; 303 ieResRef PreviousArea; //move here if the worldmap exit is illegal? 304 ieResRef LoadMos; 305 ieResRef TextScreen; 306 Particles *weather; 307 int event_timer; 308 EventHandler event_handler; 309 bool hasInfra = false; 310 bool familiarBlock = false; 311 bool PartyAttack = false; 312 bool HOFMode = false; 313 private: 314 /** reads the challenge rating table */ 315 void LoadCRTable(); 316 Actor *timestop_owner; 317 ieDword timestop_end; 318 public: 319 /** Returns the PC's slot count for partyID */ 320 int FindPlayer(unsigned int partyID) const; 321 /** Returns actor by slot */ 322 Actor* GetPC(unsigned int slot, bool onlyalive) const; 323 /** Finds an actor in party by party ID, returns Actor, if not there, returns NULL*/ 324 Actor* FindPC(unsigned int partyID) const; 325 Actor* FindNPC(unsigned int partyID) const; 326 /** Finds a global actor by global ID */ 327 Actor *GetGlobalActorByGlobalID(ieDword globalID) const; 328 /** Finds an actor in party, returns slot, if not there, returns -1*/ 329 int InParty(const Actor *pc) const; 330 /** Finds an actor in store, returns slot, if not there, returns -1*/ 331 int InStore(const Actor *pc) const; 332 /** Finds an actor in party by scripting name*/ 333 Actor* FindPC(const char *deathvar) const; 334 /** Finds an actor in store by scripting name*/ 335 Actor* FindNPC(const char *deathvar) const; 336 /** Sets the area and position of the actor to the starting position */ 337 void InitActorPos(Actor *actor) const; 338 /** Joins party */ 339 int JoinParty(Actor* pc, int join=JP_JOIN); 340 /** Return current party size */ 341 int GetPartySize(bool onlyalive) const; 342 /** Returns the npcs count */ GetNPCCount()343 int GetNPCCount() const { return (int)NPCs.size(); } 344 /** Sends the hotkey trigger to all selected pcs */ 345 void SendHotKey(unsigned long Key) const; 346 /** Select PC for non-walking environment (shops, inventory, ...) */ 347 bool SelectPCSingle(int index); 348 /** Get index of selected PC for non-walking env (shops, inventory, ...) */ 349 int GetSelectedPCSingle() const; 350 Actor* GetSelectedPCSingle(bool onlyalive) const; 351 /** (De)selects actor. */ 352 bool SelectActor( Actor* actor, bool select, unsigned flags ); 353 354 /** Return current party level count for xp calculations */ 355 int GetTotalPartyLevel(bool onlyalive) const; 356 /** Reassigns inparty numbers, call it after party creation */ 357 void ConsolidateParty() const; 358 /** Removes actor from party (if in there) */ 359 int LeaveParty(Actor* pc); 360 /** Returns slot*/ 361 int DelPC(unsigned int slot, bool autoFree = false); 362 int DelNPC(unsigned int slot, bool autoFree = false); 363 /** Returns map in index */ 364 Map* GetMap(unsigned int index) const; 365 /** Returns a map from area name, loads it if needed 366 * use it for the biggest safety, change = true will change the current map */ 367 Map* GetMap(const char *areaname, bool change); 368 /** Returns slot of the map if found */ 369 int FindMap(const char *ResRef) const; 370 int AddMap(Map* map); 371 /** Determine if area is master area*/ 372 bool MasterArea(const char *area) const; 373 /** Dynamically adding an area to master areas*/ 374 void SetMasterArea(const char *area); 375 /** Guess the master area of the given area*/ 376 //Map* GetMasterArea(const char *area); 377 /** place persistent actors in the fresly loaded area*/ 378 void PlacePersistents(Map *map, const char *ResRef); 379 /** Returns slot of the map, if it was already loaded, 380 * don't load it again, set changepf == true, 381 * if you want to change the pathfinder too. */ 382 int LoadMap(const char* ResRef, bool loadscreen); 383 int DelMap(unsigned int index, int forced = 0); 384 int AddNPC(Actor* npc); 385 Actor* GetNPC(unsigned int Index) const; 386 void SwapPCs(unsigned int pc1, unsigned int pc2) const; 387 bool IsDay() const; 388 /** checks if the actor should be replaced via npclevel.2da and then does it */ 389 bool CheckForReplacementActor(int i); 390 391 //journal entries 392 /** Deletes one or all journal entries if strref is -1 */ 393 void DeleteJournalEntry(ieStrRef strref); 394 /** Delete entries of the same group */ 395 void DeleteJournalGroup(int Group); 396 /** Adds a journal entry from dialog data. 397 * Time and chapter are calculated on the fly 398 * Returns false if the entry already exists */ 399 bool AddJournalEntry(ieStrRef strref, int section, int group); 400 /** Adds a journal entry while loading the .gam structure */ 401 void AddJournalEntry(GAMJournalEntry* entry); 402 unsigned int GetJournalCount() const; 403 GAMJournalEntry* FindJournalEntry(ieStrRef strref) const; 404 GAMJournalEntry* GetJournalEntry(unsigned int Index) const; 405 406 //saved locations 407 unsigned int GetSavedLocationCount() const; 408 void ClearSavedLocations(); 409 GAMLocationEntry* GetSavedLocationEntry(unsigned int Index); 410 411 //plane locations 412 unsigned int GetPlaneLocationCount() const; 413 void ClearPlaneLocations(); 414 GAMLocationEntry* GetPlaneLocationEntry(unsigned int Index); 415 416 char *GetFamiliar(unsigned int Index); 417 IsBeastKnown(unsigned int Index)418 bool IsBeastKnown(unsigned int Index) const { 419 if (!beasts) { 420 return false; 421 } 422 if (Index>=BESTIARY_SIZE) { 423 return false; 424 } 425 return beasts[Index] != 0; 426 } SetBeastKnown(unsigned int Index)427 void SetBeastKnown(unsigned int Index) { 428 if (!beasts) { 429 return; 430 } 431 if (Index>=BESTIARY_SIZE) { 432 return; 433 } 434 beasts[Index] = 1; 435 } GetFormation()436 ieWord GetFormation() const { 437 if (WhichFormation>4) { 438 return 0; 439 } 440 return Formations[WhichFormation]; 441 } 442 443 /** converts challenge rating to xp */ 444 int GetXPFromCR(int cr) const; 445 /** shares XP among all party members */ 446 void ShareXP(int XP, int flags) const; 447 /** returns true if we should start the party overflow window */ 448 bool PartyOverflow() const; 449 /** returns true if any pc is attacker or being attacked */ 450 bool AnyPCInCombat() const; 451 /** returns true if the party death condition is true */ 452 bool EveryoneDead() const; 453 /** returns true if no one moves */ 454 bool EveryoneStopped() const; 455 bool EveryoneNearPoint(const Map *map, const Point &p, int flags) const; 456 /** a party member just died now */ 457 void PartyMemberDied(const Actor *); 458 /** Increments chapter variable and refreshes kill stats */ 459 void IncrementChapter() const; 460 /** Sets party reputation */ 461 void SetReputation(ieDword r); 462 /** Sets the gamescreen control status (pane states, dialog textarea size) */ 463 bool SetControlStatus(unsigned int value, int operation); 464 /** Sets party size (1-32000) */ 465 void SetPartySize(int value); 466 /** Sets a guiscript function to happen after x AI cycles have elapsed */ 467 void SetTimedEvent(EventHandler func, int count); 468 /** Sets protagonist mode to 0-none,1-protagonist,2-team */ 469 void SetProtagonistMode(int value); 470 void StartRainOrSnow(bool conditional, int weather); GetLoadedMapCount()471 size_t GetLoadedMapCount() const { return Maps.size(); } 472 /** Adds or removes gold */ 473 void AddGold(int add); 474 /** Adds ticks to game time */ 475 void AdvanceTime(ieDword add, bool fatigue=true); 476 /** Runs the script engine on the global script and the area scripts 477 areas run scripts on door, infopoint, container, actors too */ 478 void UpdateScripts(); 479 /** checks if resting is possible */ 480 int CanPartyRest(int checks) const; 481 /** runs area functionality, sets partyrested trigger */ 482 bool RestParty(int checks, int dream, int hp); 483 /** timestop effect initiated by actor */ 484 void TimeStop(Actor *actor, ieDword end); 485 /** check if the passed actor is a victim of timestop */ 486 bool TimeStoppedFor(const Actor* target = nullptr) const; 487 /** updates the infravision info */ 488 void Infravision(); 489 /** applies the global tint if it is needed */ 490 void ApplyGlobalTint(Color &tint, BlitFlags &flags) const; 491 /** gets the colour which should be applied over the game area, 492 may return NULL */ 493 const Color *GetGlobalTint() const; 494 /** returns true if party has infravision */ PartyHasInfravision()495 bool PartyHasInfravision() const { return hasInfra; } 496 /** draw weather */ 497 void DrawWeather(bool update); 498 /** updates current area music */ 499 void ChangeSong(bool always = true, bool force = true) const; 500 /** sets expansion mode */ 501 void SetExpansion(ieDword value); 502 /** Dumps information about the object */ 503 void dump() const; 504 /** Finds an actor by global ID */ 505 Actor *GetActorByGlobalID(ieDword objectID) const; 506 /** Allocates maze data */ 507 ieByte *AllocateMazeData(); 508 /** Checks if any timestop effects are active */ 509 bool IsTimestopActive() const; 510 int RemainingTimestop() const; GetTimestopOwner()511 Actor *GetTimestopOwner() const { return timestop_owner; }; SetTimestopOwner(Actor * owner)512 void SetTimestopOwner(Actor *owner) { timestop_owner = owner; }; 513 /** Checks the bounty encounters (used in bg1) */ 514 bool RandomEncounter(ieResRef &BaseArea); 515 /** Resets the area and bored comment timers of the whole party */ 516 void ResetPartyCommentTimes() const; 517 void ReversePCs() const; 518 bool OnlyNPCsSelected() const; 519 private: 520 bool DetermineStartPosType(const TableMgr *strta) const; 521 ieResRef *GetDream(Map *area); 522 void CastOnRest() const; 523 void PlayerDream(); 524 void TextDream(); 525 }; 526 527 } 528 529 #endif // ! GAME_H 530