1 #include "DefaultContentManager.h"
2
3 #include "Exceptions.h"
4 #include "game/Directories.h"
5 #include "game/Strategic/Strategic_Status.h"
6 #include "game/Tactical/Items.h"
7 #include "game/Tactical/Weapons.h"
8
9 // XXX: GameRes.h should be integrated to ContentManager
10 #include "game/GameRes.h"
11
12 // XXX
13 #include "game/GameState.h"
14
15 #include "sgp/FileMan.h"
16 #include "sgp/MemMan.h"
17
18 #include "AmmoTypeModel.h"
19 #include "CacheSectorsModel.h"
20 #include "CalibreModel.h"
21 #include "ContentMusic.h"
22 #include "DealerInventory.h"
23 #include "DealerModel.h"
24 #include "JsonObject.h"
25 #include "JsonUtility.h"
26 #include "LoadingScreenModel.h"
27 #include "MagazineModel.h"
28 #include "RustInterface.h"
29 #include "ShippingDestinationModel.h"
30 #include "WeaponModels.h"
31 #include "army/ArmyCompositionModel.h"
32 #include "army/GarrisonGroupModel.h"
33 #include "army/PatrolGroupModel.h"
34 #include "mercs/MERCListingModel.h"
35 #include "mercs/MercProfileInfo.h"
36 #include "mercs/RPCSmallFaceModel.h"
37 #include "policy/DefaultGamePolicy.h"
38 #include "policy/DefaultIMPPolicy.h"
39 #include "policy/DefaultStrategicAIPolicy.h"
40 #include "strategic/BloodCatPlacementsModel.h"
41 #include "strategic/BloodCatSpawnsModel.h"
42 #include "strategic/CreatureLairModel.h"
43 #include "strategic/FactParamsModel.h"
44 #include "strategic/MineModel.h"
45 #include "strategic/SamSiteModel.h"
46 #include "strategic/SamSiteAirControlModel.h"
47 #include "strategic/SectorLandTypes.h"
48 #include "strategic/StrategicMapSecretModel.h"
49 #include "strategic/TownModel.h"
50 #include "strategic/TraversibilityMapping.h"
51 #include "strategic/MovementCostsModel.h"
52 #include "strategic/NpcPlacementModel.h"
53 #include "strategic/UndergroundSectorModel.h"
54 #include "tactical/MapItemReplacementModel.h"
55 #include "tactical/NpcActionParamsModel.h"
56
57 #include "Logger.h"
58 #include "Strategic_AI.h"
59 #include "Soldier_Profile_Type.h"
60
61 #include "rapidjson/error/en.h"
62 #include <string_theory/format>
63 #include <string_theory/string>
64
65 #include <stdexcept>
66
67 #define BASEDATADIR "data"
68
69 #define MAPSDIR "maps"
70 #define RADARMAPSDIR "radarmaps"
71 #define TILESETSDIR "tilesets"
72
73 #define PRINT_OPENING_FILES (0)
74
75 #define DIALOGUESIZE 240
76
77 // XXX: Issue #135
78 // We need a safe way to create temporary directories - unique and random for every process
79 // Boost probably provides this functionality
80 #define NEW_TEMP_DIR "temp"
81
82 const MercProfileInfo EMPTY_MERC_PROFILE_INFO;
83
LoadEncryptedData(ST::string & err_msg,STRING_ENC_TYPE encType,SGPFile * File,UINT32 seek_chars,UINT32 read_chars)84 static ST::string LoadEncryptedData(ST::string& err_msg, STRING_ENC_TYPE encType, SGPFile* File, UINT32 seek_chars, UINT32 read_chars)
85 {
86 FileSeek(File, seek_chars * 2, FILE_SEEK_FROM_START);
87
88 ST::utf16_buffer buf(read_chars, u'\0');
89 FileRead(File, buf.data(), sizeof(char16_t) * read_chars);
90
91 buf[read_chars - 1] = u'\0';
92 for (char16_t* i = buf.data(); *i != u'\0'; ++i)
93 {
94 /* "Decrypt" the ROT-1 "encrypted" data */
95 char16_t c = (*i > 33 ? *i - 1 : *i);
96
97 if(encType == SE_RUSSIAN)
98 {
99 /* The Russian data files are incorrectly encoded. The original texts seem to
100 * be encoded in CP1251, but then they were converted from CP1252 (!) to
101 * UTF-16 to store them in the data files. Undo this damage here. */
102 if (0xC0 <= c && c <= 0xFF) c += 0x0350;
103 }
104 else
105 {
106 if(encType == SE_ENGLISH)
107 {
108 /* The English data files are incorrectly encoded. The original texts seem
109 * to be encoded in CP437, but then they were converted from CP1252 (!) to
110 * UTF-16 to store them in the data files. Undo this damage here. This
111 * problem only occurs for a few lines by Malice. */
112 switch (c)
113 {
114 case 128: c = 0x00C7; break; // Ç
115 case 130: c = 0x00E9; break; // é
116 case 135: c = 0x00E7; break; // ç
117 }
118 }
119 else if(encType == SE_POLISH)
120 {
121 /* The Polish data files are incorrectly encoded. The original texts seem to
122 * be encoded in CP1250, but then they were converted from CP1252 (!) to
123 * UTF-16 to store them in the data files. Undo this damage here.
124 * Also the format code for centering texts differs. */
125 switch (c)
126 {
127 case 143: c = 0x0179; break;
128 case 163: c = 0x0141; break;
129 case 165: c = 0x0104; break;
130 case 175: c = 0x017B; break;
131 case 179: c = 0x0142; break;
132 case 182: c = 179; break; // not a char, but a format code (centering)
133 case 185: c = 0x0105; break;
134 case 191: c = 0x017C; break;
135 case 198: c = 0x0106; break;
136 case 202: c = 0x0118; break;
137 case 209: c = 0x0143; break;
138 case 230: c = 0x0107; break;
139 case 234: c = 0x0119; break;
140 case 241: c = 0x0144; break;
141 case 338: c = 0x015A; break;
142 case 339: c = 0x015B; break;
143 case 376: c = 0x017A; break;
144 }
145 }
146
147 /* Cyrillic texts (by Ivan Dolvich) in the non-Russian versions are encoded
148 * in some wild manner. Undo this damage here. */
149 if (0x044D <= c && c <= 0x0452) // cyrillic A to IE
150 {
151 c += -0x044D + 0x0410;
152 }
153 else if (c == 0x0453) // cyrillic IO
154 {
155 c = 0x0401;
156 }
157 else if (0x0454 <= c && c <= 0x0467) // cyrillic ZHE to SHCHA
158 {
159 c += -0x0454 + 0x0416;
160 }
161 else if (0x0468 <= c && c <= 0x046C) // cyrillic YERU to YA
162 {
163 c += -0x0468 + 0x042B;
164 }
165 }
166
167 *i = c;
168 }
169 return st_checked_buffer_to_string(err_msg, buf);
170 }
171
DefaultContentManager(GameVersion gameVersion,const ST::string & userHomeDir,const ST::string & gameResRootPath,const ST::string & externalizedDataPath)172 DefaultContentManager::DefaultContentManager(GameVersion gameVersion,
173 const ST::string &userHomeDir,
174 const ST::string &gameResRootPath,
175 const ST::string &externalizedDataPath
176 )
177 :m_gameVersion(gameVersion),
178 mNormalGunChoice(ARMY_GUN_LEVELS),
179 mExtendedGunChoice(ARMY_GUN_LEVELS),
180 m_vfs(Vfs_create())
181 {
182 /*
183 * Searching actual paths to directories 'Data' and 'Data/Tilecache', 'Data/Maps'
184 * On case-sensitive filesystems that might be tricky: if such directories
185 * exist we should use them. If doesn't exist, then use lowercased names.
186 */
187
188 m_userHomeDir = userHomeDir;
189 m_gameResRootPath = gameResRootPath;
190 m_externalizedDataPath = externalizedDataPath;
191
192 RustPointer<char> path{Fs_resolveExistingComponents(BASEDATADIR, m_gameResRootPath.c_str(), true)};
193 m_dataDir = path.get();
194
195 path.reset(Fs_resolveExistingComponents(TILECACHEDIR, m_dataDir.c_str(), true));
196 m_tileDir = path.get();
197
198 m_bobbyRayNewInventory = NULL;
199 m_bobbyRayUsedInventory = NULL;
200 m_impPolicy = NULL;
201 m_gamePolicy = NULL;
202 m_strategicAIPolicy = NULL;
203
204 m_cacheSectors = NULL;
205 m_movementCosts = NULL;
206 m_loadingScreenModel = NULL;
207 m_samSitesAirControl = NULL;
208 }
209
init(EngineOptions * engine_options)210 void DefaultContentManager::init(EngineOptions* engine_options)
211 {
212 auto succeeded = Vfs_init_from_engine_options(m_vfs.get(), engine_options);
213 if (!succeeded) {
214 RustPointer<char> err{ getRustError() };
215 auto error = ST::format("Failed to build virtual file system (VFS): {}", err.get());
216 SLOGE(error);
217 throw std::runtime_error(error.c_str());
218 }
219 }
220
221 template <class T>
deleteElements(std::vector<const T * > vec)222 void deleteElements(std::vector<const T*> vec)
223 {
224 for (auto elem : vec)
225 {
226 delete elem;
227 }
228 vec.clear();
229 }
230
231 template <typename K, class V>
deleteElements(std::map<K,const V * > map)232 void deleteElements(std::map<K, const V*> map)
233 {
234 for (auto& kv : map)
235 {
236 delete kv.second;
237 }
238 map.clear();
239 }
240
~DefaultContentManager()241 DefaultContentManager::~DefaultContentManager()
242 {
243 for (const ItemModel* item : m_items)
244 {
245 delete item;
246 }
247 m_items.clear();
248 m_magazineMap.clear();
249 m_magazines.clear();
250 m_weaponMap.clear();
251 m_itemMap.clear();
252 m_mapItemReplacements.clear();
253
254 m_armyCompositions.clear();
255 m_garrisonGroups.clear();
256 m_patrolGroups.clear();
257
258 for (const CalibreModel* calibre : m_calibres)
259 {
260 delete calibre;
261 }
262 m_calibres.clear();
263 m_calibreMap.clear();
264
265 for (const AmmoTypeModel* ammoType : m_ammoTypes)
266 {
267 delete ammoType;
268 }
269 m_ammoTypes.clear();
270 m_ammoTypeMap.clear();
271
272 deleteElements(m_dealersInventory);
273 delete m_bobbyRayNewInventory;
274 delete m_bobbyRayUsedInventory;
275
276 deleteElements(m_dealers);
277
278 delete m_impPolicy;
279 delete m_gamePolicy;
280 delete m_strategicAIPolicy;
281 delete m_loadingScreenModel;
282
283 deleteElements(m_newStrings);
284 deleteElements(m_landTypeStrings);
285 for (const ST::string *str : m_calibreNames)
286 {
287 delete str;
288 }
289 for (const ST::string *str : m_calibreNamesBobbyRay)
290 {
291 delete str;
292 }
293
294 deleteElements(m_bloodCatPlacements);
295 deleteElements(m_bloodCatSpawns);
296 deleteElements(m_creatureLairs);
297 deleteElements(m_factParams);
298 deleteElements(m_mines);
299 deleteElements(m_npcActionParams);
300 deleteElements(m_npcPlacements);
301 deleteElements(m_samSites);
302 deleteElements(m_mapSecrets);
303 deleteElements(m_shippingDestinations);
304 deleteElements(m_shippingDestinationNames);
305 deleteElements(m_towns);
306 deleteElements(m_townNames);
307 deleteElements(m_townNameLocatives);
308 deleteElements(m_undergroundSectors);
309 deleteElements(m_rpcSmallFaces);
310 deleteElements(m_MERCListings);
311 deleteElements(m_mercProfileInfo);
312 deleteElements(m_mercProfiles);
313
314 m_sectorLandTypes.clear();
315
316 delete m_cacheSectors;
317 delete m_movementCosts;
318 delete m_samSitesAirControl;
319 }
320
getBobbyRayNewInventory() const321 const DealerInventory* DefaultContentManager::getBobbyRayNewInventory() const
322 {
323 return m_bobbyRayNewInventory;
324 }
325
getBobbyRayUsedInventory() const326 const DealerInventory* DefaultContentManager::getBobbyRayUsedInventory() const
327 {
328 return m_bobbyRayUsedInventory;
329 }
330
getDealer(uint8_t dealerID) const331 const DealerModel* DefaultContentManager::getDealer(uint8_t dealerID) const
332 {
333 return m_dealers[dealerID];
334 }
335
getDealers() const336 const std::vector<const DealerModel*> DefaultContentManager::getDealers() const
337 {
338 return m_dealers;
339 }
340
getShippingDestinations() const341 const std::vector<const ShippingDestinationModel*>& DefaultContentManager::getShippingDestinations() const
342 {
343 return m_shippingDestinations;
344 }
345
getShippingDestination(uint8_t locationId) const346 const ShippingDestinationModel* DefaultContentManager::getShippingDestination(uint8_t locationId) const
347 {
348 return m_shippingDestinations[locationId];
349 }
350
getPrimaryShippingDestination() const351 const ShippingDestinationModel* DefaultContentManager::getPrimaryShippingDestination() const
352 {
353 for (auto dest : m_shippingDestinations)
354 {
355 if (dest->isPrimary)
356 {
357 return dest;
358 }
359 }
360 throw DataError("Bobby Ray primary destination is not defined");
361 }
362
getShippingDestinationName(uint8_t index) const363 const ST::string* DefaultContentManager::getShippingDestinationName(uint8_t index) const
364 {
365 return m_shippingDestinationNames[index];
366 }
367
getNpcActionParams(uint16_t actionCode) const368 const NpcActionParamsModel* DefaultContentManager::getNpcActionParams(uint16_t actionCode) const
369 {
370 auto it = m_npcActionParams.find(actionCode);
371 if (it != m_npcActionParams.end())
372 {
373 return it->second;
374 }
375 return &NpcActionParamsModel::empty;
376 }
377
getFactParams(Fact fact) const378 const FactParamsModel* DefaultContentManager::getFactParams(Fact fact) const
379 {
380 auto it = m_factParams.find(fact);
381 if (it != m_factParams.end())
382 {
383 return it->second;
384 }
385 return &FactParamsModel::empty;
386 }
387
388 /** Get map file path. */
getMapPath(const ST::string & mapName) const389 ST::string DefaultContentManager::getMapPath(const ST::string& mapName) const
390 {
391 ST::string result = MAPSDIR;
392 result += '/';
393 result += mapName.c_str();
394
395 STLOGD("map file {}", result);
396
397 return result;
398 }
399
400 /** Get radar map resource name. */
getRadarMapResourceName(const ST::string & mapName) const401 ST::string DefaultContentManager::getRadarMapResourceName(const ST::string &mapName) const
402 {
403 ST::string result = RADARMAPSDIR;
404 result += "/";
405 result += mapName;
406
407 STLOGD("map file {}", result);
408
409 return result;
410 }
411
412 /** Get tileset resource name. */
getTilesetResourceName(int number,const ST::string & fileName) const413 ST::string DefaultContentManager::getTilesetResourceName(int number, const ST::string& fileName) const
414 {
415 return ST::format("{}/{}/{}", TILESETSDIR, number, fileName);
416 }
417
418
419 /** Get tileset db resource name. */
getTilesetDBResName() const420 ST::string DefaultContentManager::getTilesetDBResName() const
421 {
422 return BINARYDATADIR "/ja2set.dat";
423 }
424
425 /** Open map for reading. */
openMapForReading(const ST::string & mapName) const426 SGPFile* DefaultContentManager::openMapForReading(const ST::string& mapName) const
427 {
428 return openGameResForReading(getMapPath(mapName));
429 }
430
431 /** Get directory for storing new map file. */
getNewMapFolder() const432 ST::string DefaultContentManager::getNewMapFolder() const
433 {
434 return FileMan::joinPaths(m_dataDir, MAPSDIR);
435 }
436
437 /** Get all available maps. */
getAllMaps() const438 std::vector<ST::string> DefaultContentManager::getAllMaps() const
439 {
440 return FindFilesInDir(MAPSDIR, "dat", true, true, true);
441 }
442
443 /** Get all available tilecache. */
getAllTilecache() const444 std::vector<ST::string> DefaultContentManager::getAllTilecache() const
445 {
446 return FindFilesInDir(m_tileDir, "jsd", true, false);
447 }
448
449 /** Open temporary file for writing. */
openTempFileForWriting(const ST::string & filename,bool truncate) const450 SGPFile* DefaultContentManager::openTempFileForWriting(const ST::string& filename, bool truncate) const
451 {
452 ST::string path = FileMan::joinPaths(NEW_TEMP_DIR, filename);
453 return FileMan::openForWriting(path, truncate);
454 }
455
456 /** Open temporary file for appending. */
openTempFileForAppend(const ST::string & filename) const457 SGPFile* DefaultContentManager::openTempFileForAppend(const ST::string& filename) const
458 {
459 ST::string path = FileMan::joinPaths(NEW_TEMP_DIR, filename);
460 return FileMan::openForAppend(path);
461 }
462
463 /* Open temporary file for reading. */
openTempFileForReading(const ST::string & filename) const464 SGPFile* DefaultContentManager::openTempFileForReading(const ST::string& filename) const
465 {
466 ST::string path = FileMan::joinPaths(NEW_TEMP_DIR, filename);
467 RustPointer<File> file(File_open(path.c_str(), FILE_OPEN_READ));
468 if (!file)
469 {
470 RustPointer<char> err(getRustError());
471 ST::string buf = ST::format("DefaultContentManager::openTempFileForReading: {}", err.get());
472 throw std::runtime_error(buf.to_std_string());
473 }
474 return FileMan::getSGPFileFromFile(file.release());
475 }
476
477 /** Delete temporary file. */
deleteTempFile(const ST::string & filename) const478 void DefaultContentManager::deleteTempFile(const ST::string& filename) const
479 {
480 ST::string path = FileMan::joinPaths(NEW_TEMP_DIR, filename);
481 FileDelete(path);
482 }
483
484 /* Open a game resource file for reading.
485 *
486 * First trying to open the file normally. It will work if the path is absolute
487 * and the file is found or path is relative to the current directory (game
488 * settings directory) and file is present.
489 * If file is not found, try to find it relatively to 'Data' directory.
490 * If file is not found, try to find the file in libraries located in 'Data' directory; */
openGameResForReading(const ST::string & filename) const491 SGPFile* DefaultContentManager::openGameResForReading(const ST::string& filename) const
492 {
493 {
494 RustPointer<File> file = FileMan::openFileForReading(filename);
495 if (file)
496 {
497 STLOGD("Opened file (current dir): '{}'", filename);
498 return FileMan::getSGPFileFromFile(file.release());
499 }
500 }
501
502 RustPointer<VfsFile> vfile(VfsFile_open(m_vfs.get(), filename.c_str()));
503 if (!vfile)
504 {
505 RustPointer<char> err{getRustError()};
506 throw std::runtime_error(ST::format("openGameResForReading: {}", err.get()).to_std_string());
507 }
508 STLOGD("Opened file (vfs): '{}'", filename);
509 SGPFile *file = new SGPFile{};
510 file->flags = SGPFILE_NONE;
511 file->u.vfile = vfile.release();
512 return file;
513 }
514
515 /** Open user's private file (e.g. saved game, settings) for reading. */
openUserPrivateFileForReading(const ST::string & filename) const516 SGPFile* DefaultContentManager::openUserPrivateFileForReading(const ST::string& filename) const
517 {
518 RustPointer<File> file = FileMan::openFileForReading(filename);
519 if (!file)
520 {
521 RustPointer<char> err(getRustError());
522 ST::string buf = ST::format("DefaultContentManager::openUserPrivateFileForReading: {}", err.get());
523 throw std::runtime_error(buf.to_std_string());
524 }
525 return FileMan::getSGPFileFromFile(file.release());
526 }
527
528 /* Checks if a game resource exists. */
doesGameResExists(const ST::string & filename) const529 bool DefaultContentManager::doesGameResExists(const ST::string& filename) const
530 {
531 if(FileMan::checkFileExistance(m_externalizedDataPath, filename))
532 {
533 return true;
534 }
535 else
536 {
537 RustPointer<File> file(File_open(filename.c_str(), FILE_OPEN_READ));
538 if (!file)
539 {
540 ST::string path = ST::format("{}/{}", m_dataDir, filename);
541 file.reset(File_open(path.c_str(), FILE_OPEN_READ));
542 if (!file)
543 {
544 RustPointer<VfsFile> vfile(VfsFile_open(m_vfs.get(), filename.c_str()));
545 return static_cast<bool>(vfile);
546 }
547 }
548
549 return true;
550 }
551 }
552
getScreenshotFolder() const553 ST::string DefaultContentManager::getScreenshotFolder() const
554 {
555 return m_userHomeDir;
556 }
557
getVideoCaptureFolder() const558 ST::string DefaultContentManager::getVideoCaptureFolder() const
559 {
560 return m_userHomeDir;
561 }
562
563 /** Get folder for saved games. */
getSavedGamesFolder() const564 ST::string DefaultContentManager::getSavedGamesFolder() const
565 {
566 return FileMan::joinPaths(m_userHomeDir, "SavedGames");
567 }
568
569 /** Load encrypted string from game resource file. */
loadEncryptedString(const ST::string & fileName,uint32_t seek_chars,uint32_t read_chars) const570 ST::string DefaultContentManager::loadEncryptedString(const ST::string& fileName, uint32_t seek_chars, uint32_t read_chars) const
571 {
572 AutoSGPFile File(openGameResForReading(fileName));
573 ST::string err_msg;
574 ST::string str = LoadEncryptedData(err_msg, getStringEncType(), File, seek_chars, read_chars);
575 if (!err_msg.empty())
576 {
577 STLOGW("DefaultContentManager::loadEncryptedString '{}' {} {}: {}", fileName, seek_chars, read_chars, err_msg);
578 }
579 return str;
580 }
581
loadEncryptedString(SGPFile * File,uint32_t seek_chars,uint32_t read_chars) const582 ST::string DefaultContentManager::loadEncryptedString(SGPFile* File, uint32_t seek_chars, uint32_t read_chars) const
583 {
584 ST::string err_msg;
585 ST::string str = LoadEncryptedData(err_msg, getStringEncType(), File, seek_chars, read_chars);
586 if (!err_msg.empty())
587 {
588 STLOGW("DefaultContentManager::loadEncryptedString ? {} {}: {}", seek_chars, read_chars, err_msg);
589 }
590 return str;
591 }
592
593
594 /** Load dialogue quote from file. */
loadDialogQuoteFromFile(const ST::string & fileName,int quote_number)595 ST::string* DefaultContentManager::loadDialogQuoteFromFile(const ST::string& fileName, int quote_number)
596 {
597 AutoSGPFile File(openGameResForReading(fileName));
598 ST::string err_msg;
599 ST::string quote = LoadEncryptedData(err_msg, getStringEncType(), File, quote_number * DIALOGUESIZE, DIALOGUESIZE);
600 if (!err_msg.empty())
601 {
602 STLOGW("DefaultContentManager::loadDialogQuoteFromFile '{}' {}: {}", fileName, quote_number, err_msg);
603 }
604 return new ST::string(quote);
605 }
606
607 /** Load all dialogue quotes for a character. */
loadAllDialogQuotes(STRING_ENC_TYPE encType,const ST::string & fileName,std::vector<ST::string * > & quotes) const608 void DefaultContentManager::loadAllDialogQuotes(STRING_ENC_TYPE encType, const ST::string& fileName, std::vector<ST::string*> "es) const
609 {
610 AutoSGPFile File(openGameResForReading(fileName));
611 uint32_t fileSize = FileGetSize(File);
612 uint32_t numQuotes = fileSize / DIALOGUESIZE / 2;
613
614 for(uint32_t i = 0; i < numQuotes; i++)
615 {
616 ST::string err;
617 ST::string quote = LoadEncryptedData(err, encType, File, i * DIALOGUESIZE, DIALOGUESIZE);
618 if (!err.empty())
619 {
620 STLOGW("DefaultContentManager::loadAllDialogQuotes '{}' {}: {}", fileName, i, err);
621 }
622 quotes.push_back(new ST::string(quote));
623 }
624 }
625
626 /** Get weapons with the give index. */
getWeapon(uint16_t itemIndex)627 const WeaponModel* DefaultContentManager::getWeapon(uint16_t itemIndex)
628 {
629 return getItem(itemIndex)->asWeapon();
630 }
631
getWeaponByName(const ST::string & internalName)632 const WeaponModel* DefaultContentManager::getWeaponByName(const ST::string &internalName)
633 {
634 std::map<ST::string, const WeaponModel*>::const_iterator it = m_weaponMap.find(internalName);
635 if(it == m_weaponMap.end())
636 {
637 STLOGE("weapon '{}' is not found", internalName);
638 throw std::runtime_error(ST::format("weapon '{}' is not found", internalName).to_std_string());
639 }
640 return it->second;//m_weaponMap[internalName];
641 }
642
getMagazineByName(const ST::string & internalName)643 const MagazineModel* DefaultContentManager::getMagazineByName(const ST::string &internalName)
644 {
645 if(m_magazineMap.find(internalName) == m_magazineMap.end())
646 {
647 STLOGE("magazine '{}' is not found", internalName);
648 throw std::runtime_error(ST::format("magazine '{}' is not found", internalName).to_std_string());
649 }
650 return m_magazineMap[internalName];
651 }
652
getMagazineByItemIndex(uint16_t itemIndex)653 const MagazineModel* DefaultContentManager::getMagazineByItemIndex(uint16_t itemIndex)
654 {
655 return getItem(itemIndex)->asAmmo();
656 }
657
getMagazines() const658 const std::vector<const MagazineModel*>& DefaultContentManager::getMagazines() const
659 {
660 return m_magazines;
661 }
662
getCalibre(uint8_t index)663 const CalibreModel* DefaultContentManager::getCalibre(uint8_t index)
664 {
665 return m_calibres[index];
666 }
667
getCalibreName(uint8_t index) const668 const ST::string* DefaultContentManager::getCalibreName(uint8_t index) const
669 {
670 return m_calibreNames[index];
671 }
672
getCalibreNameForBobbyRay(uint8_t index) const673 const ST::string* DefaultContentManager::getCalibreNameForBobbyRay(uint8_t index) const
674 {
675 return m_calibreNamesBobbyRay[index];
676 }
677
getAmmoType(uint8_t index)678 const AmmoTypeModel* DefaultContentManager::getAmmoType(uint8_t index)
679 {
680 return m_ammoTypes[index];
681 }
682
loadWeapons()683 bool DefaultContentManager::loadWeapons()
684 {
685 auto document = readJsonDataFile("weapons.json");
686 if (document->IsArray())
687 {
688 const rapidjson::Value& a = document->GetArray();
689 for (rapidjson::SizeType i = 0; i < a.Size(); i++)
690 {
691 JsonObjectReader obj(a[i]);
692 WeaponModel *w = WeaponModel::deserialize(obj, m_calibreMap);
693 STLOGD("Loaded weapon {} {}", w->getItemIndex(), w->getInternalName());
694
695 if (w->getItemIndex() > MAX_WEAPONS)
696 {
697 STLOGE("Weapon index must be in the interval 0 - {}", MAX_WEAPONS);
698 return false;
699 }
700
701 m_items[w->getItemIndex()] = w;
702 m_weaponMap.insert(std::make_pair(w->getInternalName(), w));
703 }
704 }
705
706 return true;
707 }
708
loadItems()709 bool DefaultContentManager::loadItems()
710 {
711 auto document = readJsonDataFile("items.json");
712 for (auto& el : document->GetArray())
713 {
714 JsonObjectReader obj(el);
715 auto* item = ItemModel::deserialize(obj);
716 if (item->getItemIndex() <= MAX_WEAPONS || item->getItemIndex() > MAXITEMS)
717 {
718 ST::string err = ST::format("Item index must be in the interval {} - {}", MAX_WEAPONS+1, MAXITEMS);
719 throw DataError(err);
720 }
721
722 m_items[item->getItemIndex()] = item;
723 }
724
725 return true;
726 }
727
loadMagazines()728 bool DefaultContentManager::loadMagazines()
729 {
730 auto document = readJsonDataFile("magazines.json");
731 if(document->IsArray())
732 {
733 const rapidjson::Value& a = document->GetArray();
734 for (rapidjson::SizeType i = 0; i < a.Size(); i++)
735 {
736 JsonObjectReader obj(a[i]);
737 MagazineModel *mag = MagazineModel::deserialize(obj, m_calibreMap, m_ammoTypeMap);
738 STLOGD("Loaded magazine {} {}", mag->getItemIndex(), mag->getInternalName());
739
740 if((mag->getItemIndex() < FIRST_AMMO) || (mag->getItemIndex() > LAST_AMMO))
741 {
742 STLOGE("Magazine item index must be in the interval {} - {}", FIRST_AMMO, LAST_AMMO);
743 return false;
744 }
745
746 m_magazines.push_back(mag);
747 m_items[mag->getItemIndex()] = mag;
748 m_magazineMap.insert(std::make_pair(mag->getInternalName(), mag));
749 }
750 }
751
752 return true;
753 }
754
loadCalibres()755 bool DefaultContentManager::loadCalibres()
756 {
757 auto document = readJsonDataFile("calibres.json");
758 if (document->IsArray()) {
759 const rapidjson::Value& a = document->GetArray();
760 for (rapidjson::SizeType i = 0; i < a.Size(); i++)
761 {
762 JsonObjectReader obj(a[i]);
763 CalibreModel *calibre = CalibreModel::deserialize(obj);
764 STLOGD("Loaded calibre {} {}", calibre->index, calibre->internalName);
765
766 if(m_calibres.size() <= calibre->index)
767 {
768 m_calibres.resize(calibre->index + 1);
769 }
770
771 m_calibres[calibre->index] = calibre;
772 }
773 }
774
775 for (const CalibreModel* calibre : m_calibres)
776 {
777 m_calibreMap.insert(std::make_pair(ST::string(calibre->internalName), calibre));
778 }
779
780 return true;
781 }
782
loadAmmoTypes()783 bool DefaultContentManager::loadAmmoTypes()
784 {
785 auto document = readJsonDataFile("ammo_types.json");
786 if(document->IsArray()) {
787 const rapidjson::Value& a = document->GetArray();
788 for (rapidjson::SizeType i = 0; i < a.Size(); i++)
789 {
790 JsonObjectReader obj(a[i]);
791 AmmoTypeModel *ammoType = AmmoTypeModel::deserialize(obj);
792 STLOGD("Loaded ammo type {} {}", ammoType->index, ammoType->internalName);
793
794 if(m_ammoTypes.size() <= ammoType->index)
795 {
796 m_ammoTypes.resize(ammoType->index + 1);
797 }
798
799 m_ammoTypes[ammoType->index] = ammoType;
800 }
801 }
802
803 for (const AmmoTypeModel* ammoType : m_ammoTypes)
804 {
805 m_ammoTypeMap.insert(std::make_pair(ST::string(ammoType->internalName), ammoType));
806 }
807
808 return true;
809 }
810
loadMusicModeList(const MusicMode mode,rapidjson::Value & array)811 bool DefaultContentManager::loadMusicModeList(const MusicMode mode, rapidjson::Value &array)
812 {
813 std::vector<const ST::string*>* musicModeList = new std::vector<const ST::string*>();
814
815 std::vector<ST::string> utf8_encoded;
816 JsonUtility::parseListStrings(array, utf8_encoded);
817 for (const ST::string &str : utf8_encoded)
818 {
819 musicModeList->push_back(new ST::string(str));
820 STLOGD("Loaded music {}", str);
821 }
822
823 m_musicMap[mode] = musicModeList;
824
825 return true;
826 }
827
loadMusic()828 bool DefaultContentManager::loadMusic()
829 {
830 auto document = readJsonDataFile("music.json");
831 if(!document->IsObject()) {
832 SLOGE("music.json has wrong structure");
833 return false;
834 }
835
836 auto obj = document->GetObject();
837 SLOGD("Loading main_menu music");
838 loadMusicModeList(MUSIC_MAIN_MENU, obj["main_menu"]);
839 SLOGD("Loading main_menu music");
840 loadMusicModeList(MUSIC_LAPTOP, obj["laptop"]);
841 SLOGD("Loading tactical music");
842 loadMusicModeList(MUSIC_TACTICAL_NOTHING, obj["tactical"]);
843 SLOGD("Loading tactical_enemypresent music");
844 loadMusicModeList(MUSIC_TACTICAL_ENEMYPRESENT, obj["tactical_enemypresent"]);
845 SLOGD("Loading tactical_battle music");
846 loadMusicModeList(MUSIC_TACTICAL_BATTLE, obj["tactical_battle"]);
847 SLOGD("Loading tactical_creature music");
848 loadMusicModeList(MUSIC_TACTICAL_CREATURE_NOTHING, obj["tactical_creature"]);
849 SLOGD("Loading tactical_creature_enemypresent music");
850 loadMusicModeList(MUSIC_TACTICAL_CREATURE_ENEMYPRESENT, obj["tactical_creature_enemypresent"]);
851 SLOGD("Loading tactical_creature_battle music");
852 loadMusicModeList(MUSIC_TACTICAL_CREATURE_BATTLE, obj["tactical_creature_battle"]);
853 SLOGD("Loading tactical_victory music");
854 loadMusicModeList(MUSIC_TACTICAL_VICTORY, obj["tactical_victory"]);
855 SLOGD("Loading tactical_defeat music");
856 loadMusicModeList(MUSIC_TACTICAL_DEFEAT, obj["tactical_defeat"]);
857
858 return true;
859 }
860
readWeaponTable(const ST::string & fileName,std::vector<std::vector<const WeaponModel * >> & weaponTable)861 bool DefaultContentManager::readWeaponTable(
862 const ST::string& fileName,
863 std::vector<std::vector<const WeaponModel*> > & weaponTable)
864 {
865 auto document = readJsonDataFile(fileName);
866 if(document->IsArray())
867 {
868 const rapidjson::Value& a = document->GetArray();
869 for (rapidjson::SizeType i = 0; i < a.Size(); i++)
870 {
871 std::vector<ST::string> weaponNames;
872 if(JsonUtility::parseListStrings(a[i], weaponNames))
873 {
874 for (const ST::string &weapon : weaponNames)
875 {
876 weaponTable[i].push_back(getWeaponByName(weapon));
877 }
878 }
879 }
880 }
881
882 return true;
883 }
884
getNormalGunChoice() const885 const std::vector<std::vector<const WeaponModel*> > & DefaultContentManager::getNormalGunChoice() const
886 {
887 return mNormalGunChoice;
888 }
889
getExtendedGunChoice() const890 const std::vector<std::vector<const WeaponModel*> > & DefaultContentManager::getExtendedGunChoice() const
891 {
892 return mExtendedGunChoice;
893 }
894
getGarrisonGroups() const895 const std::vector<GARRISON_GROUP>& DefaultContentManager::getGarrisonGroups() const
896 {
897 return m_garrisonGroups;
898 }
899
getPatrolGroups() const900 const std::vector<PATROL_GROUP>& DefaultContentManager::getPatrolGroups() const
901 {
902 return m_patrolGroups;
903 }
904
getArmyCompositions() const905 const std::vector<ARMY_COMPOSITION>& DefaultContentManager::getArmyCompositions() const
906 {
907 return m_armyCompositions;
908 }
909
loadArmyData()910 bool DefaultContentManager::loadArmyData()
911 {
912 readWeaponTable("army-gun-choice-normal.json", mNormalGunChoice);
913 readWeaponTable("army-gun-choice-extended.json", mExtendedGunChoice);
914
915 auto jsonAC = readJsonDataFile("army-compositions.json");
916 auto armyCompModels = ArmyCompositionModel::deserialize(*jsonAC);
917 ArmyCompositionModel::validateData(armyCompModels);
918
919 std::map<ST::string, uint8_t> mapping;
920 for (auto& armyComp : armyCompModels)
921 {
922 mapping[armyComp->name] = armyComp->compositionId;
923 m_armyCompositions.push_back(armyComp->toArmyComposition());
924 }
925 deleteElements(armyCompModels);
926 armyCompModels.clear();
927
928 auto jsonGG = readJsonDataFile("army-garrison-groups.json");
929 for (auto& element : jsonGG->GetArray())
930 {
931 auto obj = JsonObjectReader(element);
932 m_garrisonGroups.push_back(
933 GarrisonGroupModel::deserialize(obj, mapping)
934 );
935 }
936 GarrisonGroupModel::validateData(m_garrisonGroups);
937
938 auto jsonPG = readJsonDataFile("army-patrol-groups.json");
939 for (auto& element : jsonPG->GetArray())
940 {
941 m_patrolGroups.push_back(
942 PatrolGroupModel::deserialize(element)
943 );
944 }
945 PatrolGroupModel::validateData(m_patrolGroups);
946
947 return true;
948 }
949
loadStringRes(const ST::string & name,std::vector<const ST::string * > & strings) const950 void DefaultContentManager::loadStringRes(const ST::string& name, std::vector<const ST::string*> &strings) const
951 {
952 ST::string fullName(name);
953
954 switch(m_gameVersion)
955 {
956 case GameVersion::DUTCH: fullName += "-dut"; break;
957 case GameVersion::ENGLISH: fullName += "-eng"; break;
958 case GameVersion::FRENCH: fullName += "-fr"; break;
959 case GameVersion::GERMAN: fullName += "-ger"; break;
960 case GameVersion::ITALIAN: fullName += "-it"; break;
961 case GameVersion::POLISH: fullName += "-pl"; break;
962 case GameVersion::RUSSIAN:
963 case GameVersion::RUSSIAN_GOLD: fullName += "-rus"; break;
964 default:
965 {
966 throw std::runtime_error(ST::format("unknown game version {}", static_cast<int>(m_gameVersion)).to_std_string());
967 }
968 }
969
970 fullName += ".json";
971 auto json = readJsonDataFile(fullName.c_str());
972 std::vector<ST::string> utf8_encoded;
973 JsonUtility::parseListStrings(*json, utf8_encoded);
974 for (const ST::string &str : utf8_encoded)
975 {
976 strings.push_back(new ST::string(str));
977 }
978 }
979
980 /** Load the game data. */
loadGameData()981 bool DefaultContentManager::loadGameData()
982 {
983 m_items.resize(MAXITEMS);
984 bool result = loadItems()
985 && loadCalibres()
986 && loadAmmoTypes()
987 && loadMagazines()
988 && loadWeapons()
989 && loadArmyData()
990 && loadMusic();
991
992 for (const ItemModel *item : m_items)
993 {
994 m_itemMap.insert(std::make_pair(item->getInternalName(), item));
995 }
996
997 auto replacement_json = readJsonDataFile("tactical-map-item-replacements.json");
998 m_mapItemReplacements = MapItemReplacementModel::deserialize(replacement_json.get(), this);
999
1000 loadAllDealersAndInventory();
1001
1002 auto game_json = readJsonDataFile("game.json");
1003 m_gamePolicy = new DefaultGamePolicy(game_json.get());
1004
1005 auto imp_json = readJsonDataFile("imp.json");
1006 m_impPolicy = new DefaultIMPPolicy(imp_json.get(), this);
1007
1008 auto sai_json = readJsonDataFile("strategic-ai-policy.json");
1009 m_strategicAIPolicy = new DefaultStrategicAIPolicy(sai_json.get());
1010
1011 loadStringRes("strings/shipping-destinations", m_shippingDestinationNames);
1012
1013 auto shippingDestJson = readJsonDataFile("shipping-destinations.json");
1014 for (auto& element : shippingDestJson->GetArray())
1015 {
1016 auto r = JsonObjectReader(element);
1017 m_shippingDestinations.push_back(ShippingDestinationModel::deserialize(r));
1018 }
1019 ShippingDestinationModel::validateData(m_shippingDestinations, m_shippingDestinationNames);
1020
1021 auto loadScreensList = readJsonDataFile("loading-screens.json");
1022 auto loadScreensMapping = readJsonDataFile("loading-screens-mapping.json");
1023 m_loadingScreenModel = LoadingScreenModel::deserialize(*loadScreensList, *loadScreensMapping);
1024 m_loadingScreenModel->validateData(this);
1025
1026 loadStringRes("strings/ammo-calibre", m_calibreNames);
1027 loadStringRes("strings/ammo-calibre-bobbyray", m_calibreNamesBobbyRay);
1028
1029 loadStringRes("strings/new-strings", m_newStrings);
1030 loadStringRes("strings/strategic-map-land-types", m_landTypeStrings);
1031
1032 loadStrategicLayerData();
1033 loadTacticalLayerData();
1034 loadMercsData();
1035
1036 return result;
1037 }
1038
readJsonDataFile(const ST::string & fileName) const1039 std::unique_ptr<rapidjson::Document> DefaultContentManager::readJsonDataFile(const ST::string& fileName) const
1040 {
1041 AutoSGPFile f(openGameResForReading(fileName));
1042 ST::string jsonData = FileMan::fileReadText(f);
1043
1044 auto document = std::make_unique<rapidjson::Document>();
1045 if (document->Parse<rapidjson::kParseCommentsFlag>(jsonData.c_str()).HasParseError())
1046 {
1047 ST::string errorMessage = ST::format("Failed to parse {} (at location {}) {} ",
1048 fileName,
1049 document->GetErrorOffset(),
1050 rapidjson::GetParseError_En(document->GetParseError())
1051 );
1052 throw DataError(errorMessage);
1053 }
1054
1055 return document;
1056 }
1057
loadDealerInventory(const ST::string & fileName)1058 const DealerInventory * DefaultContentManager::loadDealerInventory(const ST::string& fileName)
1059 {
1060 return new DealerInventory(readJsonDataFile(fileName).get(), this);
1061 }
1062
loadAllDealersAndInventory()1063 bool DefaultContentManager::loadAllDealersAndInventory()
1064 {
1065 auto json = readJsonDataFile("dealers.json");
1066 int index = 0;
1067 for (auto& element : json->GetArray())
1068 {
1069 m_dealers.push_back(DealerModel::deserialize(element, index++));
1070 }
1071 DealerModel::validateData(m_dealers);
1072
1073 m_dealersInventory = std::vector<const DealerInventory*>(m_dealers.size());
1074 for (auto dealer : m_dealers)
1075 {
1076 ST::string filename = dealer->getInventoryDataFileName();
1077 m_dealersInventory[dealer->dealerID] = loadDealerInventory(filename.c_str());
1078 }
1079 m_bobbyRayNewInventory = loadDealerInventory("bobby-ray-inventory-new.json");
1080 m_bobbyRayUsedInventory = loadDealerInventory("bobby-ray-inventory-used.json");
1081 return true;
1082 }
1083
getItem(uint16_t itemIndex) const1084 const ItemModel* DefaultContentManager::getItem(uint16_t itemIndex) const
1085 {
1086 if(itemIndex >= m_items.size())
1087 {
1088 return nullptr;
1089 }
1090 return m_items[itemIndex];
1091 }
1092
getItemByName(const ST::string & internalName) const1093 const ItemModel* DefaultContentManager::getItemByName(const ST::string &internalName) const
1094 {
1095 std::map<ST::string, const ItemModel*>::const_iterator it = m_itemMap.find(internalName);
1096 if(it == m_itemMap.end())
1097 {
1098 STLOGE("item '{}' is not found", internalName);
1099 throw std::runtime_error(ST::format("item '{}' is not found", internalName).to_std_string());
1100 }
1101 return it->second;
1102 }
1103
getMapItemReplacements() const1104 const std::map<uint16_t, uint16_t> DefaultContentManager::getMapItemReplacements() const
1105 {
1106 return m_mapItemReplacements;
1107 }
1108
getDealerInventory(int dealerId) const1109 const DealerInventory* DefaultContentManager::getDealerInventory(int dealerId) const
1110 {
1111 return m_dealersInventory[dealerId];
1112 }
1113
getMusicForMode(MusicMode mode) const1114 const ST::string* DefaultContentManager::getMusicForMode(MusicMode mode) const {
1115 const uint32_t index = Random((uint32_t)m_musicMap.find(mode)->second->size());
1116 const ST::string* chosen = m_musicMap.find(mode)->second->at(index);
1117
1118 STLOGD("Choosing music index {} of {} for: '{}'", index, m_musicMap.find(mode)->second->size(), chosen);
1119 return chosen;
1120 }
1121
getIMPPolicy() const1122 const IMPPolicy* DefaultContentManager::getIMPPolicy() const
1123 {
1124 return m_impPolicy;
1125 }
1126
getGamePolicy() const1127 const GamePolicy* DefaultContentManager::getGamePolicy() const
1128 {
1129 return m_gamePolicy;
1130 }
1131
getStrategicAIPolicy() const1132 const StrategicAIPolicy* DefaultContentManager::getStrategicAIPolicy() const
1133 {
1134 return m_strategicAIPolicy;
1135 }
1136
getNewString(size_t stringId) const1137 const ST::string* DefaultContentManager::getNewString(size_t stringId) const
1138 {
1139 if(stringId >= m_newStrings.size())
1140 {
1141 ST::string message = ST::format("new string {} is not found", stringId);
1142 SLOGE(message.c_str());
1143 throw std::runtime_error(message.c_str());
1144 }
1145 else
1146 {
1147 return m_newStrings[stringId];
1148 }
1149 }
1150
getLandTypeString(size_t index) const1151 const ST::string& DefaultContentManager::getLandTypeString(size_t index) const
1152 {
1153 return *m_landTypeStrings.at(index);
1154 }
1155
loadStrategicLayerData()1156 bool DefaultContentManager::loadStrategicLayerData()
1157 {
1158 auto json = readJsonDataFile("strategic-bloodcat-placements.json");
1159 for (auto& element : json->GetArray()) {
1160 auto obj = JsonObjectReader(element);
1161 m_bloodCatPlacements.push_back(
1162 BloodCatPlacementsModel::deserialize(obj)
1163 );
1164 }
1165
1166 json = readJsonDataFile("strategic-bloodcat-spawns.json");
1167 for (auto& element : json->GetArray())
1168 {
1169 auto obj = JsonObjectReader(element);
1170 m_bloodCatSpawns.push_back(
1171 BloodCatSpawnsModel::deserialize(obj)
1172 );
1173 }
1174
1175 json = readJsonDataFile("strategic-map-creature-lairs.json");
1176 for (auto& element : json->GetArray())
1177 {
1178 m_creatureLairs.push_back(
1179 CreatureLairModel::deserialize(element)
1180 );
1181 }
1182
1183 json = readJsonDataFile("strategic-fact-params.json");
1184 for (auto& element : json->GetArray())
1185 {
1186 auto params = FactParamsModel::deserialize(element);
1187 m_factParams[params->fact] = params;
1188 }
1189
1190 json = readJsonDataFile("strategic-mines.json");
1191 auto arr = json->GetArray();
1192 for (rapidjson::SizeType i = 0; i < arr.Size(); i++)
1193 {
1194 m_mines.push_back(
1195 MineModel::deserialize(i, arr[i].GetObject())
1196 );
1197 }
1198 MineModel::validateData(m_mines);
1199
1200 json = readJsonDataFile("strategic-map-sam-sites.json");
1201 for (auto& element : json->GetArray())
1202 {
1203 auto samSite = SamSiteModel::deserialize(element);
1204 m_samSites.push_back(samSite);
1205 }
1206 SamSiteModel::validateData(m_samSites);
1207
1208 json = readJsonDataFile("strategic-map-sam-sites-air-control.json");
1209 m_samSitesAirControl = SamSiteAirControlModel::deserialize(*json);
1210 SamSiteAirControlModel::validateData(m_samSitesAirControl, m_samSites.size());
1211
1212 json = readJsonDataFile("strategic-map-towns.json");
1213 for (auto& element : json->GetArray())
1214 {
1215 auto town = TownModel::deserialize(element);
1216 m_towns.insert(std::make_pair(town->townId, town));
1217 }
1218
1219 loadStringRes("strings/strategic-map-town-names", m_townNames);
1220 loadStringRes("strings/strategic-map-town-name-locatives", m_townNameLocatives);
1221
1222 json = readJsonDataFile("strategic-map-underground-sectors.json");
1223 for (auto& element : json->GetArray())
1224 {
1225 auto ugSector = UndergroundSectorModel::deserialize(element);
1226 m_undergroundSectors.push_back(ugSector);
1227 }
1228 UndergroundSectorModel::validateData(m_undergroundSectors);
1229
1230 json = readJsonDataFile("strategic-map-traversibility-ratings.json");
1231 auto travRatingMap = TraversibilityMapping::deserialize(*json);
1232
1233 json = readJsonDataFile("strategic-map-movement-costs.json");
1234 m_movementCosts = MovementCostsModel::deserialize(*json, travRatingMap);
1235
1236 json = readJsonDataFile("strategic-map-sectors-descriptions.json");
1237 m_sectorLandTypes = SectorLandTypes::deserialize(*json, travRatingMap);
1238
1239 json = readJsonDataFile("strategic-map-secrets.json");
1240 for (auto& element : json->GetArray())
1241 {
1242 auto secret = StrategicMapSecretModel::deserialize(element, travRatingMap);
1243 m_mapSecrets.push_back(secret);
1244 }
1245 StrategicMapSecretModel::validateData(m_mapSecrets, m_samSites);
1246
1247 json = readJsonDataFile("strategic-map-npc-placements.json");
1248 for (auto& element : json->GetArray())
1249 {
1250 auto placement = NpcPlacementModel::deserialize(element);
1251 m_npcPlacements.insert(std::make_pair(placement->profileId, placement));
1252 }
1253
1254 CreatureLairModel::validateData(m_creatureLairs, m_undergroundSectors, m_mines.size());
1255
1256 json = readJsonDataFile("strategic-map-cache-sectors.json");
1257 m_cacheSectors = CacheSectorsModel::deserialize(*json);
1258
1259 return true;
1260 }
1261
loadTacticalLayerData()1262 bool DefaultContentManager::loadTacticalLayerData()
1263 {
1264 auto json = readJsonDataFile("tactical-npc-action-params.json");
1265 for (auto& element : json->GetArray())
1266 {
1267 auto params = NpcActionParamsModel::deserialize(element);
1268 m_npcActionParams[params->actionCode] = params;
1269 }
1270
1271 return true;
1272 }
1273
loadMercsData()1274 bool DefaultContentManager::loadMercsData()
1275 {
1276 auto json = readJsonDataFile("mercs-rpc-small-faces.json");
1277 for (auto& element : json->GetArray())
1278 {
1279 auto face = RPCSmallFaceModel::deserialize(element);
1280 m_rpcSmallFaces[face->ubProfileID] = face;
1281 }
1282
1283 json = readJsonDataFile("mercs-MERC-listings.json");
1284 int i = 0;
1285 for (auto& element : json->GetArray())
1286 {
1287 auto item = MERCListingModel::deserialize(i++, element);
1288 m_MERCListings.push_back(item);
1289 }
1290 MERCListingModel::validateData(m_MERCListings);
1291
1292 MercProfileInfo::load = [=](uint8_t p) { return this->getMercProfileInfo(p); };
1293 json = readJsonDataFile("mercs-profile-info.json");
1294 for (auto& element : json->GetArray())
1295 {
1296 auto profileInfo = MercProfileInfo::deserialize(element);
1297 ProfileID profileID = profileInfo->profileID;
1298 m_mercProfileInfo[profileID] = profileInfo;
1299 m_mercProfiles.push_back(new MercProfile(profileID));
1300 }
1301 MercProfileInfo::validateData(m_mercProfileInfo);
1302
1303 return true;
1304 }
1305
getBloodCatPlacements() const1306 const std::vector<const BloodCatPlacementsModel*>& DefaultContentManager::getBloodCatPlacements() const
1307 {
1308 return m_bloodCatPlacements;
1309 }
1310
getBloodCatSpawns() const1311 const std::vector<const BloodCatSpawnsModel*>& DefaultContentManager::getBloodCatSpawns() const
1312 {
1313 return m_bloodCatSpawns;
1314 }
1315
getBloodCatSpawnsOfSector(uint8_t sectorId) const1316 const BloodCatSpawnsModel* DefaultContentManager::getBloodCatSpawnsOfSector(uint8_t sectorId) const
1317 {
1318 for ( auto spawns : m_bloodCatSpawns )
1319 {
1320 if ( spawns->sectorId == sectorId )
1321 {
1322 return spawns;
1323 }
1324 }
1325 return NULL;
1326 }
1327
getCreatureLairs() const1328 const std::vector<const CreatureLairModel*>& DefaultContentManager::getCreatureLairs() const
1329 {
1330 return m_creatureLairs;
1331 }
1332
getCreatureLair(uint8_t lairId) const1333 const CreatureLairModel* DefaultContentManager::getCreatureLair(uint8_t lairId) const
1334 {
1335 for (auto lair : m_creatureLairs)
1336 {
1337 if (lair->lairId == lairId)
1338 {
1339 return lair;
1340 }
1341 }
1342 return NULL;
1343 }
1344
getCreatureLairByMineId(uint8_t mineId) const1345 const CreatureLairModel* DefaultContentManager::getCreatureLairByMineId(uint8_t mineId) const
1346 {
1347 for (auto lair : m_creatureLairs)
1348 {
1349 if (lair->associatedMineId == mineId)
1350 {
1351 return lair;
1352 }
1353 }
1354 return NULL;
1355 }
1356
getMineForSector(uint8_t sectorX,uint8_t sectorY,uint8_t sectorZ) const1357 const MineModel* DefaultContentManager::getMineForSector(uint8_t sectorX, uint8_t sectorY, uint8_t sectorZ) const
1358 {
1359 auto sectorId = SECTOR(sectorX, sectorY);
1360 for (auto m : m_mines)
1361 {
1362 if (sectorZ == 0 && sectorId == m->entranceSector) return m;
1363 for (auto s : m->mineSectors)
1364 {
1365 if (s[0] == sectorId && s[1] == sectorZ) return m;
1366 }
1367 }
1368
1369 return NULL;
1370 }
1371
getMine(uint8_t mineId) const1372 const MineModel* DefaultContentManager::getMine(uint8_t mineId) const
1373 {
1374 return m_mines[mineId];
1375 }
1376
getMines() const1377 const std::vector<const MineModel*>& DefaultContentManager::getMines() const
1378 {
1379 return m_mines;
1380 }
1381
getTown(int8_t townId) const1382 const TownModel* DefaultContentManager::getTown(int8_t townId) const
1383 {
1384 auto iter = m_towns.find(townId);
1385 return (iter != m_towns.end()) ? iter->second : NULL;
1386 }
1387
getSamSites() const1388 const std::vector<const SamSiteModel*>& DefaultContentManager::getSamSites() const
1389 {
1390 return m_samSites;
1391 }
1392
findSamIDBySector(uint8_t sectorId) const1393 const int8_t DefaultContentManager::findSamIDBySector(uint8_t sectorId) const
1394 {
1395 for (size_t i = 0; i < m_samSites.size(); i++)
1396 {
1397 if (m_samSites[i]->sectorId == sectorId)
1398 {
1399 return static_cast<int8_t>(i);
1400 }
1401 }
1402 return -1;
1403 }
1404
findSamSiteBySector(uint8_t sectorId) const1405 const SamSiteModel* DefaultContentManager::findSamSiteBySector(uint8_t sectorId) const
1406 {
1407 auto i = findSamIDBySector(sectorId);
1408 return (i > -1) ? m_samSites[i] : NULL;
1409 }
1410
getControllingSamSite(uint8_t sectorId) const1411 const int8_t DefaultContentManager::getControllingSamSite(uint8_t sectorId) const
1412 {
1413 return m_samSitesAirControl->getControllingSamSiteID(sectorId);
1414 }
1415
getTowns() const1416 const std::map<int8_t, const TownModel*>& DefaultContentManager::getTowns() const
1417 {
1418 return m_towns;
1419 }
1420
getTownName(uint8_t townId) const1421 const ST::string DefaultContentManager::getTownName(uint8_t townId) const
1422 {
1423 if (townId >= m_townNames.size()) {
1424 STLOGD("Town name not defined for index {}", townId);
1425 return ST::null;
1426 }
1427 return *m_townNames[townId];
1428 }
1429
getTownLocative(uint8_t townId) const1430 const ST::string DefaultContentManager::getTownLocative(uint8_t townId) const
1431 {
1432 if (townId >= m_townNameLocatives.size()) {
1433 STLOGD("Town name locative not defined for index {}", townId);
1434 return ST::null;
1435 }
1436 return *m_townNameLocatives[townId];
1437 }
1438
getUndergroundSectors() const1439 const std::vector<const UndergroundSectorModel*>& DefaultContentManager::getUndergroundSectors() const
1440 {
1441 return m_undergroundSectors;
1442 }
1443
getMovementCosts() const1444 const MovementCostsModel* DefaultContentManager::getMovementCosts() const
1445 {
1446 return m_movementCosts;
1447 }
1448
getSectorLandType(uint8_t const sectorID,uint8_t const sectorLevel) const1449 int16_t DefaultContentManager::getSectorLandType(uint8_t const sectorID, uint8_t const sectorLevel) const
1450 {
1451 SectorKey key(sectorID, sectorLevel);
1452 if (m_sectorLandTypes.find(key) == m_sectorLandTypes.end())
1453 {
1454 return -1;
1455 }
1456 return m_sectorLandTypes.at(key);
1457 }
1458
getCacheSectors() const1459 const CacheSectorsModel* DefaultContentManager::getCacheSectors() const
1460 {
1461 return m_cacheSectors;
1462 }
1463
getMapSecrets() const1464 const std::vector<const StrategicMapSecretModel*>& DefaultContentManager::getMapSecrets() const
1465 {
1466 return m_mapSecrets;
1467 }
1468
getNpcPlacement(uint8_t profileId) const1469 const NpcPlacementModel* DefaultContentManager::getNpcPlacement(uint8_t profileId) const
1470 {
1471 return m_npcPlacements.at(profileId);
1472 }
1473
listNpcPlacements() const1474 const std::map<uint8_t, const NpcPlacementModel*>& DefaultContentManager::listNpcPlacements() const
1475 {
1476 return m_npcPlacements;
1477 }
1478
getRPCSmallFaceOffsets(uint8_t profileID) const1479 const RPCSmallFaceModel* DefaultContentManager::getRPCSmallFaceOffsets(uint8_t profileID) const
1480 {
1481 if (m_rpcSmallFaces.find(profileID) == m_rpcSmallFaces.end())
1482 {
1483 return NULL;
1484 }
1485 return m_rpcSmallFaces.at(profileID);
1486 }
1487
getMERCListings() const1488 const std::vector<const MERCListingModel*>& DefaultContentManager::getMERCListings() const
1489 {
1490 return m_MERCListings;
1491 }
1492
getMercProfileInfo(uint8_t const profileID) const1493 const MercProfileInfo* DefaultContentManager::getMercProfileInfo(uint8_t const profileID) const
1494 {
1495 if (m_mercProfileInfo.find(profileID) != m_mercProfileInfo.end())
1496 {
1497 return m_mercProfileInfo.at(profileID);
1498 }
1499
1500 STLOGD("MercProfileInfo is not defined at {}", profileID);
1501 return &EMPTY_MERC_PROFILE_INFO;
1502 }
1503
listMercProfiles() const1504 const std::vector<const MercProfile*>& DefaultContentManager::listMercProfiles() const
1505 {
1506 return m_mercProfiles;
1507 }
1508
getLoadingScreenForSector(uint8_t sectorId,uint8_t sectorLevel,bool isNight) const1509 const LoadingScreen* DefaultContentManager::getLoadingScreenForSector(uint8_t sectorId, uint8_t sectorLevel, bool isNight) const
1510 {
1511 return m_loadingScreenModel->getScreenForSector(sectorId, sectorLevel, isNight);
1512 }
1513
getLoadingScreen(uint8_t index) const1514 const LoadingScreen* DefaultContentManager::getLoadingScreen(uint8_t index) const
1515 {
1516 return m_loadingScreenModel->getByIndex(index);
1517 }
1518