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