mirror of
https://github.com/cxong/cdogs-sdl.git
synced 2025-07-23 07:23:01 +02:00
Add high scores to level select menu #151
This commit is contained in:
@@ -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;
|
||||
@@ -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();
|
||||
@@ -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++)
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -610,4 +593,35 @@ bail:
|
||||
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);
|
||||
}
|
||||
|
@@ -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.
|
||||
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:
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
@@ -46,6 +46,7 @@
|
||||
|
||||
#include "autosave.h"
|
||||
#include "game_loop.h"
|
||||
#include "hiscores.h"
|
||||
#include "menu.h"
|
||||
#include "prep.h"
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -87,27 +89,31 @@ 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;
|
||||
@@ -115,20 +121,28 @@ static void MenuCreateStart(MenuSystem *ms, const CampaignSave *save)
|
||||
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)
|
||||
@@ -136,6 +150,7 @@ static void LevelSelectionTerminate(GameLoopData *data)
|
||||
LevelSelectionData *pData = data->Data;
|
||||
|
||||
MenuSystemTerminate(&pData->ms);
|
||||
HighScoresDataTerminate(&pData->hData);
|
||||
CFREE(data->Data);
|
||||
}
|
||||
static void LevelSelectionOnEnter(GameLoopData *data)
|
||||
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user