1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5
6 This file is part of NBlood.
7
8 NBlood is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 #include <stdlib.h>
24 #include <string.h>
25 #include "compat.h"
26 #include "common_game.h"
27
28 #include "asound.h"
29 #include "blood.h"
30 #include "config.h"
31 #include "credits.h"
32 #include "endgame.h"
33 #include "inifile.h"
34 #include "levels.h"
35 #include "loadsave.h"
36 #include "messages.h"
37 #include "network.h"
38 #include "screen.h"
39 #include "seq.h"
40 #include "sound.h"
41 #include "sfx.h"
42 #include "view.h"
43 #include "eventq.h"
44
45 GAMEOPTIONS gGameOptions;
46
47 GAMEOPTIONS gSingleGameOptions = {
48 0, 2, 0, 0, "", "", 2, "", "", 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200
49 };
50
51 EPISODEINFO gEpisodeInfo[kMaxEpisodes+1];
52
53 int gSkill = 2;
54 int gEpisodeCount;
55 int gNextLevel;
56 bool gGameStarted;
57
58 int gLevelTime;
59
60 char BloodIniFile[BMAX_PATH] = "BLOOD.INI";
61 char BloodIniPre[BMAX_PATH];
62 bool bINIOverride = false;
63 IniFile *BloodINI;
64
65
levelInitINI(const char * pzIni)66 void levelInitINI(const char *pzIni)
67 {
68 int fp = kopen4loadfrommod(pzIni, 0);
69 if (fp < 0)
70 ThrowError("Initialization: %s does not exist", pzIni);
71 kclose(fp);
72 BloodINI = new IniFile(pzIni);
73 Bstrncpy(BloodIniFile, pzIni, BMAX_PATH);
74 Bstrncpy(BloodIniPre, pzIni, BMAX_PATH);
75 ChangeExtension(BloodIniPre, "");
76 }
77
78
levelOverrideINI(const char * pzIni)79 void levelOverrideINI(const char *pzIni)
80 {
81 bINIOverride = true;
82 strcpy(BloodIniFile, pzIni);
83 }
84
levelPlayIntroScene(int nEpisode)85 void levelPlayIntroScene(int nEpisode)
86 {
87 gGameOptions.uGameFlags &= ~4;
88 sndStopSong();
89 sndKillAllSounds();
90 sfxKillAllSounds();
91 ambKillAll();
92 seqKillAll();
93 EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
94 credPlaySmk(pEpisode->cutsceneASmkPath, pEpisode->cutsceneAWavPath, pEpisode->cutsceneAWavRsrcID);
95 scrSetDac();
96 viewResizeView(gViewSize);
97 credReset();
98 scrSetDac();
99 }
100
levelPlayEndScene(int nEpisode)101 void levelPlayEndScene(int nEpisode)
102 {
103 gGameOptions.uGameFlags &= ~8;
104 sndStopSong();
105 sndKillAllSounds();
106 sfxKillAllSounds();
107 ambKillAll();
108 seqKillAll();
109 EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
110 credPlaySmk(pEpisode->cutsceneBSmkPath, pEpisode->cutsceneBWavPath, pEpisode->cutsceneBWavRsrcID);
111 scrSetDac();
112 viewResizeView(gViewSize);
113 credReset();
114 scrSetDac();
115 }
116
levelClearSecrets(void)117 void levelClearSecrets(void)
118 {
119 gSecretMgr.Clear();
120 }
121
levelSetupSecret(int nCount)122 void levelSetupSecret(int nCount)
123 {
124 gSecretMgr.SetCount(nCount);
125 }
126
levelTriggerSecret(int nSecret)127 void levelTriggerSecret(int nSecret)
128 {
129 gSecretMgr.Found(nSecret);
130 }
131
CheckSectionAbend(const char * pzSection)132 void CheckSectionAbend(const char *pzSection)
133 {
134 if (!pzSection || !BloodINI->SectionExists(pzSection))
135 ThrowError("Section [%s] expected in BLOOD.INI", pzSection);
136 }
137
CheckKeyAbend(const char * pzSection,const char * pzKey)138 void CheckKeyAbend(const char *pzSection, const char *pzKey)
139 {
140 dassert(pzSection != NULL);
141
142 if (!pzKey || !BloodINI->KeyExists(pzSection, pzKey))
143 ThrowError("Key %s expected in section [%s] of BLOOD.INI", pzKey, pzSection);
144 }
145
levelGetInfoPtr(int nEpisode,int nLevel)146 LEVELINFO * levelGetInfoPtr(int nEpisode, int nLevel)
147 {
148 dassert(nEpisode >= 0 && nEpisode < gEpisodeCount);
149 EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[nEpisode];
150 dassert(nLevel >= 0 && nLevel < pEpisodeInfo->nLevels);
151 return &pEpisodeInfo->levelsInfo[nLevel];
152 }
153
levelGetFilename(int nEpisode,int nLevel)154 char * levelGetFilename(int nEpisode, int nLevel)
155 {
156 dassert(nEpisode >= 0 && nEpisode < gEpisodeCount);
157 EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[nEpisode];
158 dassert(nLevel >= 0 && nLevel < pEpisodeInfo->nLevels);
159 return pEpisodeInfo->levelsInfo[nLevel].Filename;
160 }
161
levelGetMessage(int nMessage)162 char * levelGetMessage(int nMessage)
163 {
164 int nEpisode = gGameOptions.nEpisode;
165 int nLevel = gGameOptions.nLevel;
166 dassert(nMessage < kMaxMessages);
167 char *pMessage = gEpisodeInfo[nEpisode].levelsInfo[nLevel].Messages[nMessage];
168 if (*pMessage == 0)
169 return NULL;
170 return pMessage;
171 }
172
levelGetTitle(void)173 char * levelGetTitle(void)
174 {
175 int nEpisode = gGameOptions.nEpisode;
176 int nLevel = gGameOptions.nLevel;
177 char *pTitle = gEpisodeInfo[nEpisode].levelsInfo[nLevel].Title;
178 if (*pTitle == 0)
179 return NULL;
180 return pTitle;
181 }
182
levelGetAuthor(void)183 char * levelGetAuthor(void)
184 {
185 int nEpisode = gGameOptions.nEpisode;
186 int nLevel = gGameOptions.nLevel;
187 char *pAuthor = gEpisodeInfo[nEpisode].levelsInfo[nLevel].Author;
188 if (*pAuthor == 0)
189 return NULL;
190 return pAuthor;
191 }
192
levelSetupOptions(int nEpisode,int nLevel)193 void levelSetupOptions(int nEpisode, int nLevel)
194 {
195 gGameOptions.nEpisode = nEpisode;
196 gGameOptions.nLevel = nLevel;
197 strcpy(gGameOptions.zLevelName, gEpisodeInfo[nEpisode].levelsInfo[nLevel].Filename);
198 gGameOptions.uMapCRC = dbReadMapCRC(gGameOptions.zLevelName);
199 // strcpy(gGameOptions.zLevelSong, gEpisodeInfo[nEpisode].at28[nLevel].atd0);
200 gGameOptions.nTrackNumber = gEpisodeInfo[nEpisode].levelsInfo[nLevel].SongId;
201 }
202
levelLoadMapInfo(IniFile * pIni,LEVELINFO * pLevelInfo,const char * pzSection)203 void levelLoadMapInfo(IniFile *pIni, LEVELINFO *pLevelInfo, const char *pzSection)
204 {
205 char buffer[16];
206 strncpy(pLevelInfo->Title, pIni->GetKeyString(pzSection, "Title", pLevelInfo->Filename), 31);
207 strncpy(pLevelInfo->Author, pIni->GetKeyString(pzSection, "Author", ""), 31);
208 strncpy(pLevelInfo->Song, pIni->GetKeyString(pzSection, "Song", ""), BMAX_PATH);
209 pLevelInfo->SongId = pIni->GetKeyInt(pzSection, "Track", -1);
210 pLevelInfo->EndingA = pIni->GetKeyInt(pzSection, "EndingA", -1);
211 pLevelInfo->EndingB = pIni->GetKeyInt(pzSection, "EndingB", -1);
212 pLevelInfo->Fog = pIni->GetKeyInt(pzSection, "Fog", -0);
213 pLevelInfo->Weather = pIni->GetKeyInt(pzSection, "Weather", -0);
214 for (int i = 0; i < kMaxMessages; i++)
215 {
216 sprintf(buffer, "Message%d", i+1);
217 strncpy(pLevelInfo->Messages[i], pIni->GetKeyString(pzSection, buffer, ""), 63);
218 }
219 }
220
221 extern void MenuSetupEpisodeInfo(void);
222
levelLoadDefaults(void)223 void levelLoadDefaults(void)
224 {
225 char buffer[64];
226 char buffer2[16];
227 levelInitINI(pINISelected->zName);
228 memset(gEpisodeInfo, 0, sizeof(gEpisodeInfo));
229 strncpy(gEpisodeInfo[MUS_INTRO/kMaxLevels].levelsInfo[MUS_INTRO%kMaxLevels].Song, "PESTIS", BMAX_PATH);
230 int i;
231 for (i = 0; i < kMaxEpisodes; i++)
232 {
233 sprintf(buffer, "Episode%d", i+1);
234 if (!BloodINI->SectionExists(buffer))
235 break;
236 EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[i];
237 strncpy(pEpisodeInfo->title, BloodINI->GetKeyString(buffer, "Title", buffer), 31);
238 strncpy(pEpisodeInfo->cutsceneASmkPath, BloodINI->GetKeyString(buffer, "CutSceneA", ""), BMAX_PATH);
239 pEpisodeInfo->cutsceneAWavRsrcID = BloodINI->GetKeyInt(buffer, "CutWavA", -1);
240 if (pEpisodeInfo->cutsceneAWavRsrcID == 0)
241 strncpy(pEpisodeInfo->cutsceneAWavPath, BloodINI->GetKeyString(buffer, "CutWavA", ""), BMAX_PATH);
242 else
243 pEpisodeInfo->cutsceneAWavPath[0] = 0;
244 strncpy(pEpisodeInfo->cutsceneBSmkPath, BloodINI->GetKeyString(buffer, "CutSceneB", ""), BMAX_PATH);
245 pEpisodeInfo->cutsceneBWavRsrcID = BloodINI->GetKeyInt(buffer, "CutWavB", -1);
246 if (pEpisodeInfo->cutsceneBWavRsrcID == 0)
247 strncpy(pEpisodeInfo->cutsceneBWavPath, BloodINI->GetKeyString(buffer, "CutWavB", ""), BMAX_PATH);
248 else
249 pEpisodeInfo->cutsceneBWavPath[0] = 0;
250
251 pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0);
252 pEpisodeInfo->cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0);
253 if (pEpisodeInfo->cutALevel > 0)
254 pEpisodeInfo->cutALevel--;
255 int j;
256 for (j = 0; j < kMaxLevels; j++)
257 {
258 LEVELINFO *pLevelInfo = &pEpisodeInfo->levelsInfo[j];
259 sprintf(buffer2, "Map%d", j+1);
260 if (!BloodINI->KeyExists(buffer, buffer2))
261 break;
262 const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL);
263 CheckSectionAbend(pMap);
264 strncpy(pLevelInfo->Filename, pMap, BMAX_PATH);
265 levelLoadMapInfo(BloodINI, pLevelInfo, pMap);
266 }
267 pEpisodeInfo->nLevels = j;
268 }
269 gEpisodeCount = i;
270 MenuSetupEpisodeInfo();
271 }
272
levelAddUserMap(const char * pzMap)273 void levelAddUserMap(const char *pzMap)
274 {
275 char buffer[BMAX_PATH];
276 //strcpy(buffer, g_modDir);
277 strncpy(buffer, pzMap, BMAX_PATH);
278 ChangeExtension(buffer, ".DEF");
279
280 IniFile UserINI(buffer);
281 int nEpisode = ClipRange(UserINI.GetKeyInt(NULL, "Episode", 0), 0, 5);
282 EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[nEpisode];
283 int nLevel = ClipRange(UserINI.GetKeyInt(NULL, "Level", pEpisodeInfo->nLevels), 0, 15);
284 if (nLevel >= pEpisodeInfo->nLevels)
285 {
286 if (pEpisodeInfo->nLevels == 0)
287 {
288 gEpisodeCount++;
289 sprintf(pEpisodeInfo->title, "Episode %d", nEpisode);
290 }
291 nLevel = pEpisodeInfo->nLevels++;
292 }
293 LEVELINFO *pLevelInfo = &pEpisodeInfo->levelsInfo[nLevel];
294 ChangeExtension(buffer, "");
295 strncpy(pLevelInfo->Filename, buffer, BMAX_PATH);
296 levelLoadMapInfo(&UserINI, pLevelInfo, NULL);
297 gGameOptions.nEpisode = nEpisode;
298 gGameOptions.nLevel = nLevel;
299 gGameOptions.uMapCRC = dbReadMapCRC(pLevelInfo->Filename);
300 strcpy(gGameOptions.zLevelName, pLevelInfo->Filename);
301 MenuSetupEpisodeInfo();
302 }
303
levelGetNextLevels(int nEpisode,int nLevel,int * pnEndingA,int * pnEndingB)304 void levelGetNextLevels(int nEpisode, int nLevel, int *pnEndingA, int *pnEndingB)
305 {
306 dassert(pnEndingA != NULL && pnEndingB != NULL);
307 LEVELINFO *pLevelInfo = &gEpisodeInfo[nEpisode].levelsInfo[nLevel];
308 int nEndingA = pLevelInfo->EndingA;
309 if (nEndingA >= 0)
310 nEndingA--;
311 int nEndingB = pLevelInfo->EndingB;
312 if (nEndingB >= 0)
313 nEndingB--;
314 *pnEndingA = nEndingA;
315 *pnEndingB = nEndingB;
316 }
317
levelEndLevel(int nExitType)318 void levelEndLevel(int nExitType)
319 {
320 int nEndingA, nEndingB;
321 EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[gGameOptions.nEpisode];
322 gGameOptions.uGameFlags |= 1;
323 levelGetNextLevels(gGameOptions.nEpisode, gGameOptions.nLevel, &nEndingA, &nEndingB);
324 switch (nExitType)
325 {
326 case kLevelExitNormal:
327 if (nEndingA == -1)
328 {
329 if (pEpisodeInfo->cutsceneBSmkPath[0])
330 gGameOptions.uGameFlags |= 8;
331 gGameOptions.nLevel = 0;
332 gGameOptions.uGameFlags |= 2;
333 }
334 else
335 gNextLevel = nEndingA;
336 break;
337 case kLevelExitSecret:
338 if (nEndingB == -1)
339 {
340 if (gGameOptions.nEpisode + 1 < gEpisodeCount)
341 {
342 if (pEpisodeInfo->cutsceneBSmkPath[0])
343 gGameOptions.uGameFlags |= 8;
344 gGameOptions.nLevel = 0;
345 gGameOptions.uGameFlags |= 2;
346 }
347 else
348 {
349 gGameOptions.nLevel = 0;
350 gGameOptions.uGameFlags |= 1;
351 }
352 }
353 else
354 gNextLevel = nEndingB;
355 break;
356 }
357 }
358
levelRestart(void)359 void levelRestart(void)
360 {
361 levelSetupOptions(gGameOptions.nEpisode, gGameOptions.nLevel);
362 gStartNewGame = true;
363 }
364
levelGetMusicIdx(const char * str)365 int levelGetMusicIdx(const char *str)
366 {
367 int32_t lev, ep;
368 signed char b1, b2;
369
370 int numMatches = sscanf(str, "%c%d%c%d", &b1, &ep, &b2, &lev);
371
372 if (numMatches != 4 || Btoupper(b1) != 'E' || Btoupper(b2) != 'L')
373 return -1;
374
375 if ((unsigned)--lev >= kMaxLevels || (unsigned)--ep >= kMaxEpisodes)
376 return -2;
377
378 return (ep * kMaxLevels) + lev;
379 }
380
levelTryPlayMusic(int nEpisode,int nLevel,bool bSetLevelSong)381 bool levelTryPlayMusic(int nEpisode, int nLevel, bool bSetLevelSong)
382 {
383 char buffer[BMAX_PATH];
384 if (CDAudioToggle && gEpisodeInfo[nEpisode].levelsInfo[nLevel].SongId > 0)
385 snprintf(buffer, BMAX_PATH, "blood%02i.ogg", gEpisodeInfo[nEpisode].levelsInfo[nLevel].SongId);
386 else
387 strncpy(buffer, gEpisodeInfo[nEpisode].levelsInfo[nLevel].Song, BMAX_PATH);
388 bool bReturn = !!sndPlaySong(buffer, true);
389 if (!bReturn || bSetLevelSong)
390 strncpy(gGameOptions.zLevelSong, buffer, BMAX_PATH);
391 return bReturn;
392 }
393
levelTryPlayMusicOrNothing(int nEpisode,int nLevel)394 void levelTryPlayMusicOrNothing(int nEpisode, int nLevel)
395 {
396 if (levelTryPlayMusic(nEpisode, nLevel, true))
397 sndStopSong();
398 }
399
400 class LevelsLoadSave : public LoadSave
401 {
402 virtual void Load(void);
403 virtual void Save(void);
404 };
405
406
407 static LevelsLoadSave *myLoadSave;
408
Load(void)409 void LevelsLoadSave::Load(void)
410 {
411 Read(&gNextLevel, sizeof(gNextLevel));
412 Read(&gGameOptions, sizeof(gGameOptions));
413 Read(&gGameStarted, sizeof(gGameStarted));
414 }
415
Save(void)416 void LevelsLoadSave::Save(void)
417 {
418 Write(&gNextLevel, sizeof(gNextLevel));
419 Write(&gGameOptions, sizeof(gGameOptions));
420 Write(&gGameStarted, sizeof(gGameStarted));
421 }
422
LevelsLoadSaveConstruct(void)423 void LevelsLoadSaveConstruct(void)
424 {
425 myLoadSave = new LevelsLoadSave();
426 }
427
428