diff --git a/src/cdogs/cwolfmap/cwolfmap.c b/src/cdogs/cwolfmap/cwolfmap.c index f9cd0c4e7..608a259de 100644 --- a/src/cdogs/cwolfmap/cwolfmap.c +++ b/src/cdogs/cwolfmap/cwolfmap.c @@ -167,38 +167,7 @@ int CWLoad(CWolfMap *map, const char *path, const int spearMission) CWLevelN3DLoadDescription(languageBuf, i); } - for (int q = 1;; q++) - { - char *question = CWLevelN3DLoadQuizQuestion(languageBuf, q); - if (question == NULL) - { - break; - } - map->nQuizzes++; - map->quizzes = - realloc(map->quizzes, map->nQuizzes * sizeof(CWN3DQuiz)); - CWN3DQuiz *quiz = &map->quizzes[map->nQuizzes - 1]; - memset(quiz, 0, sizeof *quiz); - quiz->question = question; - for (char a = 'A';; a++) - { - bool correct = false; - char *answer = - CWLevelN3DLoadQuizAnswer(languageBuf, q, a, &correct); - if (answer == NULL) - { - break; - } - quiz->nAnswers++; - quiz->answers = - realloc(quiz->answers, quiz->nAnswers * sizeof(char *)); - quiz->answers[quiz->nAnswers - 1] = answer; - if (correct) - { - quiz->correctIdx = quiz->nAnswers - 1; - } - } - } + CWN3DLoadQuizzes(map, languageBuf); free(languageBuf); } @@ -419,6 +388,7 @@ void CWFree(CWolfMap *map) { CWN3DQuizFree(&map->quizzes[i]); } + free(map->quizzes); memset(map, 0, sizeof *map); } static void LevelFree(CWLevel *level); @@ -541,6 +511,50 @@ const char *CWGetDescription(CWolfMap *map, const int spearMission) return NULL; } +void CWN3DLoadQuizzes(CWolfMap *map, const char *languageBuf) +{ + // May be reloading quizzes + for (int i = 0; i < map->nQuizzes; i++) + { + CWN3DQuizFree(&map->quizzes[i]); + } + free(map->quizzes); + map->quizzes = NULL; + map->nQuizzes = 0; + for (int q = 1;; q++) + { + char *question = CWLevelN3DLoadQuizQuestion(languageBuf, q); + if (question == NULL) + { + break; + } + map->nQuizzes++; + map->quizzes = + realloc(map->quizzes, map->nQuizzes * sizeof(CWN3DQuiz)); + CWN3DQuiz *quiz = &map->quizzes[map->nQuizzes - 1]; + memset(quiz, 0, sizeof *quiz); + quiz->question = question; + for (char a = 'A';; a++) + { + bool correct = false; + char *answer = + CWLevelN3DLoadQuizAnswer(languageBuf, q, a, &correct); + if (answer == NULL) + { + break; + } + quiz->nAnswers++; + quiz->answers = + realloc(quiz->answers, quiz->nAnswers * sizeof(char *)); + quiz->answers[quiz->nAnswers - 1] = answer; + if (correct) + { + quiz->correctIdx = quiz->nAnswers - 1; + } + } + } +} + int CWGetAudioSampleRate(const CWolfMap *map) { switch (map->type) diff --git a/src/cdogs/cwolfmap/cwolfmap.h b/src/cdogs/cwolfmap/cwolfmap.h index 4d149b1ab..985c0b467 100644 --- a/src/cdogs/cwolfmap/cwolfmap.h +++ b/src/cdogs/cwolfmap/cwolfmap.h @@ -62,6 +62,8 @@ void CWFree(CWolfMap *map); const char *CWGetDescription(CWolfMap *map, const int spearMission); +void CWN3DLoadQuizzes(CWolfMap *map, const char *languageBuf); + int CWGetAudioSampleRate(const CWolfMap *map); typedef enum diff --git a/src/cdogs/cwolfmap/n3d.h b/src/cdogs/cwolfmap/n3d.h index 06b09a95b..55822591f 100644 --- a/src/cdogs/cwolfmap/n3d.h +++ b/src/cdogs/cwolfmap/n3d.h @@ -16,4 +16,4 @@ char *CWLevelN3DLoadQuizQuestion(const char *buf, const int quiz); char *CWLevelN3DLoadQuizAnswer( const char *buf, const int quiz, const char answer, bool *correct); -void CWN3DQuizFree(CWN3DQuiz *quiz); \ No newline at end of file +void CWN3DQuizFree(CWN3DQuiz *quiz); diff --git a/src/cdogs/map_archive.c b/src/cdogs/map_archive.c index b2fa5dc0e..92193ffbb 100644 --- a/src/cdogs/map_archive.c +++ b/src/cdogs/map_archive.c @@ -38,8 +38,6 @@ #include "pickup.h" #include "player_template.h" -static char *ReadFileIntoBuf(const char *path, const char *mode, long *len); - static json_t *ReadArchiveJSON(const char *archive, const char *filename); int MapNewScanArchive(const char *filename, char **title, int *numMissions) { @@ -198,8 +196,7 @@ static json_t *ReadArchiveJSON(const char *archive, const char *filename) json_t *root = NULL; char path[CDOGS_PATH_MAX]; sprintf(path, "%s/%s", archive, filename); - long len; - char *buf = ReadFileIntoBuf(path, "rb", &len); + char *buf = ReadFileIntoBuf(path, "rb"); if (buf == NULL) goto bail; const enum json_error e = json_parse_document(&root, buf); @@ -233,50 +230,6 @@ static void LoadArchivePics(PicManager *pm, map_t cc, const char *archive) CharSpriteClassesLoadDir(cc, archive); } -static char *ReadFileIntoBuf(const char *path, const char *mode, long *len) -{ - char *buf = NULL; - FILE *f = fopen(path, mode); - if (f == NULL) - { - goto bail; - } - - // Read into buffer - if (fseek(f, 0L, SEEK_END) != 0) - { - goto bail; - } - *len = ftell(f); - if (*len == -1) - { - goto bail; - } - CCALLOC(buf, *len + 1); - if (fseek(f, 0L, SEEK_SET) != 0) - { - goto bail; - } - if (fread(buf, 1, *len, f) == 0) - { - goto bail; - } - - goto end; - -bail: - CFREE(buf); - buf = NULL; - -end: - if (f != NULL && fclose(f) != 0) - { - LOG(LM_MAP, LL_ERROR, "Cannot close file %s: %s", path, - strerror(errno)); - } - return buf; -} - static json_t *SaveMissions(CArray *a); int MapArchiveSave(const char *filename, CampaignSetting *c) { diff --git a/src/cdogs/map_wolf.c b/src/cdogs/map_wolf.c index 68bcf5921..317bc48a1 100644 --- a/src/cdogs/map_wolf.c +++ b/src/cdogs/map_wolf.c @@ -3541,3 +3541,91 @@ static bool TryLoadCampaign(CampaignList *list, const char *path) } return false; } + +void MapWolfN3DCheckAndLoadCustomQuiz( + const Campaign *c, const CArray *playerDatas) +{ + // Special case for N3D: Epic of Gilgamesh quiz easter egg + // This code is pretty cursed + if (strcmp(c->Setting.Title, "Super 2D Noah's Ark") != 0) + { + return; + } + bool hasGilgameshName = false; + CA_FOREACH(const PlayerData, p, *playerDatas) + if (Stricmp(p->name, "Gilgamesh") == 0 || + Stricmp(p->name, "Enkidu") == 0 || + Stricmp(p->name, "Utnapishtim") == 0 || + Stricmp(p->name, "Ziusudra") == 0 || + Stricmp(p->name, "Atra-Hasis") == 0 || + Stricmp(p->name, "Atrahasis") == 0) + { + hasGilgameshName = true; + break; + } + CA_FOREACH_END() + if (!hasGilgameshName) + { + return; + } + + // Load new quizzes + char pathBuf[CDOGS_PATH_MAX]; + GetDataFilePath(pathBuf, WOLF_DATA_DIR "N3Ddata.cdogscpn/language.enu"); + char *languageBuf = ReadFileIntoBuf(pathBuf, "r"); + CWolfMap map; + memset(&map, 0, sizeof map); + CWN3DLoadQuizzes(&map, languageBuf); + free(languageBuf); + + // Copy the effects from the "scroll" pickup + const PickupClass *scroll = StrPickupClass("scroll"); + const PickupEffect *menuEffect = CArrayGet(&scroll->Effects, 0); + const CArray *correctEffects = + &((const PickupMenuItem *)CArrayGet(&menuEffect->u.Menu.Items, 0)) + ->Effects; + const CArray *wrongEffects = + &((const PickupMenuItem *)CArrayGet(&menuEffect->u.Menu.Items, 1)) + ->Effects; + + // Replace the scroll pickups with the quizzes + for (int i = 0;; i++) + { + char buf[256]; + sprintf(buf, "scroll+%d", i); + PickupClass *c = StrPickupClass(buf); + if (c == NULL) + { + break; + } + CA_FOREACH(PickupEffect, e, c->Effects) + PickupEffectTerminate(e); + CA_FOREACH_END() + CArrayClear(&c->Effects); + + PickupEffect e; + memset(&e, 0, sizeof e); + e.Type = PICKUP_MENU; + const CWN3DQuiz *quiz = &map.quizzes[i]; + CSTRDUP(e.u.Menu.Text, quiz->question); + + CArrayInit(&e.u.Menu.Items, sizeof(PickupMenuItem)); + for (int j = 0; j < quiz->nAnswers; j++) + { + PickupMenuItem m; + PickupMenuItemInit(&m); + CSTRDUP(m.Text, quiz->answers[j]); + const CArray *effects = + j == quiz->correctIdx ? correctEffects : wrongEffects; + CA_FOREACH(const PickupEffect, pe, *effects) + PickupEffect ec = PickupEffectCopy(pe); + CArrayPushBack(&m.Effects, &ec); + CA_FOREACH_END() + CArrayPushBack(&e.u.Menu.Items, &m); + } + + CArrayPushBack(&c->Effects, &e); + } + + CWFree(&map); +} diff --git a/src/cdogs/map_wolf.h b/src/cdogs/map_wolf.h index 77f4cd8cc..95f4c7887 100644 --- a/src/cdogs/map_wolf.h +++ b/src/cdogs/map_wolf.h @@ -1,7 +1,7 @@ /* C-Dogs SDL A port of the legendary (and fun) action/arcade cdogs. - Copyright (c) 2020-2021 Cong Xu + Copyright (c) 2020-2021, 2024 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -40,3 +40,6 @@ int MapWolfLoad( const char *filename, const int spearMission, CampaignSetting *c); void MapWolfLoadCampaignsFromSystem(CampaignList *list); + +void MapWolfN3DCheckAndLoadCustomQuiz( + const Campaign *c, const CArray *playerDatas); diff --git a/src/cdogs/pickup_class.c b/src/cdogs/pickup_class.c index 6a48cce46..0b6ad6dea 100644 --- a/src/cdogs/pickup_class.c +++ b/src/cdogs/pickup_class.c @@ -200,7 +200,6 @@ PickupEffect PickupEffectCopy(const PickupEffect *e) } return out; } -static void PickupEffectTerminate(PickupEffect *e); static void PickupMenuItemTerminate(PickupMenuItem *m) { CFREE(m->Text); @@ -208,7 +207,7 @@ static void PickupMenuItemTerminate(PickupMenuItem *m) PickupEffectTerminate(e); CA_FOREACH_END() } -static void PickupEffectTerminate(PickupEffect *e) +void PickupEffectTerminate(PickupEffect *e) { switch (e->Type) { diff --git a/src/cdogs/pickup_class.h b/src/cdogs/pickup_class.h index 38bbc690a..8413c187a 100644 --- a/src/cdogs/pickup_class.h +++ b/src/cdogs/pickup_class.h @@ -111,6 +111,8 @@ void PickupMenuItemInit(PickupMenuItem *m); PickupEffect PickupEffectCopy(const PickupEffect *e); void PickupClassInit(PickupClass *c); +void PickupEffectTerminate(PickupEffect *e); + void PickupClassesInit( PickupClasses *classes, const char *filename, const AmmoClasses *ammo, const WeaponClasses *guns); diff --git a/src/cdogs/utils.c b/src/cdogs/utils.c index 945c68645..f1f250cf5 100644 --- a/src/cdogs/utils.c +++ b/src/cdogs/utils.c @@ -22,7 +22,7 @@ This file incorporates work covered by the following copyright and permission notice: - Copyright (c) 2013-2017, 2019-2022 Cong Xu + Copyright (c) 2013-2017, 2019-2022, 2024 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -62,6 +62,7 @@ #include "events.h" #include "joystick.h" +#include "log.h" #include "sys_config.h" bool gTrue = true; @@ -637,3 +638,47 @@ int Pulse256(const int t) } return alphaUnscaled; } + +char *ReadFileIntoBuf(const char *path, const char *mode) +{ + char *buf = NULL; + FILE *f = fopen(path, mode); + if (f == NULL) + { + goto bail; + } + + // Read into buffer + if (fseek(f, 0L, SEEK_END) != 0) + { + goto bail; + } + long len = ftell(f); + if (len == -1) + { + goto bail; + } + CCALLOC(buf, len + 1); + if (fseek(f, 0L, SEEK_SET) != 0) + { + goto bail; + } + if (fread(buf, 1, len, f) == 0) + { + goto bail; + } + + goto end; + +bail: + CFREE(buf); + buf = NULL; + +end: + if (f != NULL && fclose(f) != 0) + { + LOG(LM_MAIN, LL_ERROR, "Cannot close file %s: %s", path, + strerror(errno)); + } + return buf; +} diff --git a/src/cdogs/utils.h b/src/cdogs/utils.h index 86f433e33..63abe2fbc 100644 --- a/src/cdogs/utils.h +++ b/src/cdogs/utils.h @@ -22,7 +22,7 @@ This file incorporates work covered by the following copyright and permission notice: - Copyright (c) 2013-2017, 2019-2023 Cong Xu + Copyright (c) 2013-2017, 2019-2024 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -249,3 +249,5 @@ typedef enum } PlacementAccessFlags; int Pulse256(const int t); + +char *ReadFileIntoBuf(const char *path, const char *mode); diff --git a/src/prep.c b/src/prep.c index 1391e4342..4f0241715 100644 --- a/src/prep.c +++ b/src/prep.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -363,6 +364,7 @@ static void PlayerSelectionOnExit(GameLoopData *data) { gCampaign.MissionIndex = 0; } + MapWolfN3DCheckAndLoadCustomQuiz(&gCampaign, &gPlayerDatas); } else {