Skip to content

Commit

Permalink
Add high scores to level select menu #151
Browse files Browse the repository at this point in the history
  • Loading branch information
cxong committed Feb 8, 2025
1 parent 5610b6c commit e6c3d52
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 65 deletions.
62 changes: 38 additions & 24 deletions src/hiscores.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,6 @@ static GameLoopResult HighScoresScreenUpdate(GameLoopData *data, LoopRunner *l)
{
LoopRunnerPush(l, DisplayAllTimeHighScores(hData.g, scores));
}
else
{
// TODO: terminate high scores
}
}

return UPDATE_RESULT_OK;
Expand Down Expand Up @@ -249,7 +245,7 @@ static int DisplayEntry(
return 1 + FontH();
}

static int DisplayPage(const int idxStart, const CArray *entries)
static void DisplayPage(const CArray *entries)
{
int x = 80;
int y = 5 + FontH();
Expand All @@ -269,7 +265,7 @@ static int DisplayPage(const int idxStart, const CArray *entries)
}
CA_FOREACH_END()

int idx = idxStart;
int idx = 0;
CA_FOREACH(const HighScoreEntry, e, *entries)
bool isHighlighted = false;
for (int i = 0; i < MAX_LOCAL_PLAYERS; i++)
Expand All @@ -290,15 +286,7 @@ static int DisplayPage(const int idxStart, const CArray *entries)
}
idx++;
CA_FOREACH_END()
return idx;
}
typedef struct
{
GraphicsDevice *g;
CArray scores; // of HighScoreEntry
int highlights[MAX_LOCAL_PLAYERS];
int scoreIdx;
} HighScoresData;
static void HighScoreTerminate(GameLoopData *data);
static GameLoopResult HighScoreUpdate(GameLoopData *data, LoopRunner *l);
static void HighScoreDraw(GameLoopData *data);
Expand All @@ -308,7 +296,6 @@ static GameLoopData *DisplayAllTimeHighScores(
HighScoresData *data;
CMALLOC(data, sizeof *data);
data->g = graphics;
data->scoreIdx = 0;
// Take a copy of the scores as the parent will get destroyed
CArrayInit(&data->scores, sizeof(HighScoreEntry));
CArrayCopy(&data->scores, scores);
Expand All @@ -319,18 +306,14 @@ static GameLoopData *DisplayAllTimeHighScores(
static void HighScoreTerminate(GameLoopData *data)
{
HighScoresData *hData = data->Data;

CArrayTerminate(&hData->scores);
// TODO: terminate scores entries
HighScoresDataTerminate(hData);
CFREE(hData);
}
static void HighScoreDraw(GameLoopData *data)
{
HighScoresData *hData = data->Data;

BlitClearBuf(hData->g);
hData->scoreIdx = DisplayPage(hData->scoreIdx, &hData->scores);
BlitUpdateFromBuf(hData->g, hData->g->screen);
HighScoresDraw(hData);
}
static GameLoopResult HighScoreUpdate(GameLoopData *data, LoopRunner *l)
{
Expand Down Expand Up @@ -506,9 +489,9 @@ static void LoadHighScores(void)
for (int i = 0; i < (int)node->u.object.len; i++)
{
yajl_array entriesNode = YAJL_GET_ARRAY(node->u.object.values[i]);
const char *entriesPath = node->u.object.keys[i];
if (!entriesNode)
{
const char *entriesPath = node->u.object.keys[i];
LOG(LM_MAIN, LL_ERROR, "Unexpected format for high scores %s\n",
entriesPath);
continue;
Expand Down Expand Up @@ -599,7 +582,7 @@ static void LoadHighScores(void)
// TODO: weapon usage
CArrayPushBack(entries, &entry);
}
if (hashmap_put(sHighScores, path, (any_t *)entries) != MAP_OK)
if (hashmap_put(sHighScores, entriesPath, (any_t *)entries) != MAP_OK)
{
LOG(LM_MAIN, LL_ERROR, "failed to load high scores (%s)", path);
continue;
Expand All @@ -610,4 +593,35 @@ static void LoadHighScores(void)
yajl_tree_free(node);
}

// TODO: terminate high scores
HighScoresData HighScoresDataLoad(const Campaign *co, GraphicsDevice *g)
{
LoadHighScores();
HighScoresData hData;
memset(&hData, 0, sizeof hData);
hData.g = g;
CArray *scores = NULL;
if (hashmap_get(sHighScores, co->Entry.Path, (any_t *)&scores) !=
MAP_MISSING)
{
// copy scores
CArrayInit(&hData.scores, sizeof(HighScoreEntry));
CArrayCopy(&hData.scores, scores);
}
return hData;
}

void HighScoresDataTerminate(HighScoresData *hData)
{
CA_FOREACH(HighScoreEntry, entry, hData->scores)
CFREE(entry->Name);
// TODO: free weapon usages
CA_FOREACH_END()
CArrayTerminate(&hData->scores);
}

void HighScoresDraw(const HighScoresData *hData)
{
BlitClearBuf(&gGraphicsDevice);
DisplayPage(&hData->scores);
BlitUpdateFromBuf(&gGraphicsDevice, gGraphicsDevice.screen);
}
60 changes: 35 additions & 25 deletions src/hiscores.h
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
/*
C-Dogs SDL
A port of the legendary (and fun) action/arcade cdogs.
Copyright (c) 2013, 2017 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
C-Dogs SDL
A port of the legendary (and fun) action/arcade cdogs.
Copyright (c) 2013, 2017, 2025 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once

Expand All @@ -36,3 +36,13 @@

GameLoopData *HighScoresScreen(Campaign *co, GraphicsDevice *g);
void SaveHighScores(void);

typedef struct
{
GraphicsDevice *g;
CArray scores; // of HighScoreEntry
int highlights[MAX_LOCAL_PLAYERS];
} HighScoresData;
HighScoresData HighScoresDataLoad(const Campaign *co, GraphicsDevice *g);
void HighScoresDataTerminate(HighScoresData *hData);
void HighScoresDraw(const HighScoresData *hData);
8 changes: 4 additions & 4 deletions src/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ static void MoveIndexToNextEnabledSubmenu(menu_t *menu, const bool isDown)
for (;;)
{
const menu_t *currentSubmenu =
CArrayGet(&menu->u.normal.subMenus, menu->u.normal.index);
CArrayGet(&menu->u.normal.subMenus, menu->u.normal.index);
if (!currentSubmenu->isDisabled)
{
break;
Expand Down Expand Up @@ -1043,7 +1043,8 @@ void MenuProcessCmd(MenuSystem *ms, int cmd)
}
if (menu->type == MENU_TYPE_CUSTOM)
{
if (menu->u.customData.inputFunc(cmd, menu->u.customData.data))
if (menu->u.customData.inputFunc == NULL ||
menu->u.customData.inputFunc(cmd, menu->u.customData.data))
{
ms->current = menu->parentMenu;
goto bail;
Expand Down Expand Up @@ -1117,8 +1118,7 @@ menu_t *MenuProcessButtonCmd(MenuSystem *ms, menu_t *menu, int cmd)
case MENU_TYPE_NORMAL:
case MENU_TYPE_OPTIONS:
case MENU_TYPE_CUSTOM:
if (subMenu->u.normal.isSubmenusAlt ? Right(cmd)
: (Button1(cmd)))
if (subMenu->u.normal.isSubmenusAlt ? Right(cmd) : (Button1(cmd)))
{
return subMenu;
}
Expand Down
51 changes: 39 additions & 12 deletions src/password.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

#include "autosave.h"
#include "game_loop.h"
#include "hiscores.h"
#include "menu.h"
#include "prep.h"

Expand All @@ -59,12 +60,13 @@ typedef struct
{
MenuSystem ms;
const CampaignSave *save;
HighScoresData hData;
} LevelSelectionData;
static void LevelSelectionTerminate(GameLoopData *data);
static void LevelSelectionOnEnter(GameLoopData *data);
static GameLoopResult LevelSelectionUpdate(GameLoopData *data, LoopRunner *l);
static void LevelSelectionDraw(GameLoopData *data);
static void MenuCreateStart(MenuSystem *ms, const CampaignSave *save);
static void MenuCreateStart(LevelSelectionData *data);
GameLoopData *LevelSelection(GraphicsDevice *graphics)
{
LevelSelectionData *data;
Expand All @@ -73,7 +75,7 @@ GameLoopData *LevelSelection(GraphicsDevice *graphics)
MenuSystemInit(
&data->ms, &gEventHandlers, graphics, svec2i_zero(),
graphics->cachedConfig.Res);
MenuCreateStart(&data->ms, data->save);
MenuCreateStart(data);
return GameLoopDataNew(
data, LevelSelectionTerminate, LevelSelectionOnEnter, NULL, NULL,
LevelSelectionUpdate, LevelSelectionDraw);
Expand All @@ -87,55 +89,68 @@ static void MenuCreateLevelSelect(
menu_t *l = MenuCreateReturn(buf, i);
MenuAddSubmenu(levelSelect, l);
}
static void MenuCreateStart(MenuSystem *ms, const CampaignSave *save)
static void HighScoresDrawFunc(
const menu_t *menu, GraphicsDevice *g, const struct vec2i pos,
const struct vec2i size, const void *data);
static void MenuCreateStart(LevelSelectionData *data)
{
MenuSystem *ms = &data->ms;
ms->root = ms->current = MenuCreateNormal("", "", MENU_TYPE_NORMAL, 0);

menu_t *menuContinue = MenuCreateReturn("Continue", RETURN_CODE_CONTINUE);
// Note: mission can be -1
menuContinue->isDisabled =
save == NULL || save->NextMission <= 0 ||
save->NextMission == (int)gCampaign.Setting.Missions.size;
data->save == NULL || data->save->NextMission <= 0 ||
data->save->NextMission == (int)gCampaign.Setting.Missions.size;
MenuAddSubmenu(ms->root, menuContinue);

// Create level select menus
menu_t *levelSelect = MenuCreateNormal(
"Level select...", "Select Level", MENU_TYPE_NORMAL, 0);
levelSelect->u.normal.maxItems = 20;
if (save)
if (data->save)
{
CArray levels;
CArrayInitFillZero(
&levels, sizeof(bool), gCampaign.Setting.Missions.size);
CA_FOREACH(const int, missionIndex, save->MissionsCompleted)
CA_FOREACH(const int, missionIndex, data->save->MissionsCompleted)
if (*missionIndex >= (int)gCampaign.Setting.Missions.size)
{
continue;
}
MenuCreateLevelSelect(levelSelect, &gCampaign, *missionIndex);
CArraySet(&levels, *missionIndex, &gTrue);
CA_FOREACH_END()
if (save->NextMission < (int)gCampaign.Setting.Missions.size &&
!*(bool *)CArrayGet(&levels, save->NextMission))
if (data->save->NextMission < (int)gCampaign.Setting.Missions.size &&
!*(bool *)CArrayGet(&levels, data->save->NextMission))
{
MenuCreateLevelSelect(levelSelect, &gCampaign, save->NextMission);
MenuCreateLevelSelect(
levelSelect, &gCampaign, data->save->NextMission);
}
CArrayTerminate(&levels);
}
levelSelect->isDisabled =
save == NULL || save->MissionsCompleted.size == 0 || gCampaign.Setting.Missions.size == 1;
levelSelect->isDisabled = data->save == NULL ||
data->save->MissionsCompleted.size == 0 ||
gCampaign.Setting.Missions.size == 1;
MenuAddSubmenu(ms->root, levelSelect);

MenuAddSubmenu(
ms->root, MenuCreateReturn("Start campaign", RETURN_CODE_START));

data->hData = HighScoresDataLoad(&gCampaign, &gGraphicsDevice);
menu_t *highScoresMenu = MenuCreateCustom(
"High Scores", HighScoresDrawFunc, NULL, &data->hData);
highScoresMenu->isDisabled = data->hData.scores.size == 0;
MenuAddSubmenu(ms->root, highScoresMenu);

MenuAddExitType(ms, MENU_TYPE_RETURN);
}
static void LevelSelectionTerminate(GameLoopData *data)
{
LevelSelectionData *pData = data->Data;

MenuSystemTerminate(&pData->ms);
HighScoresDataTerminate(&pData->hData);
CFREE(data->Data);
}
static void LevelSelectionOnEnter(GameLoopData *data)
Expand Down Expand Up @@ -195,3 +210,15 @@ static void LevelSelectionDraw(GameLoopData *data)

MenuDraw(&pData->ms);
}

static void HighScoresDrawFunc(
const menu_t *menu, GraphicsDevice *g, const struct vec2i pos,
const struct vec2i size, const void *data)
{
UNUSED(menu);
UNUSED(g);
UNUSED(pos);
UNUSED(size);
const HighScoresData *hData = data;
HighScoresDraw(hData);
}

0 comments on commit e6c3d52

Please sign in to comment.