From d4dbf698d48edddc46ed323e844a80881fd623ce Mon Sep 17 00:00:00 2001 From: Cong Date: Wed, 8 Sep 2021 00:16:27 +1000 Subject: [PATCH] Add times to end screens --- src/animated_counter.c | 7 ++ src/animated_counter.h | 41 +++---- src/briefing_screens.c | 13 +++ src/cdogs/hud/hud.c | 4 +- src/cdogs/net_util.h | 4 +- src/hiscores.c | 251 ++++++++++++++++++++++------------------- src/proto/msg.pb.h | 27 +++-- src/proto/msg.proto | 9 +- src/screens_end.c | 14 ++- 9 files changed, 209 insertions(+), 161 deletions(-) diff --git a/src/animated_counter.c b/src/animated_counter.c index 17f60fc6b..f3627e684 100644 --- a/src/animated_counter.c +++ b/src/animated_counter.c @@ -67,3 +67,10 @@ void AnimatedCounterDraw(const AnimatedCounter *a, const struct vec2i pos) sprintf(buf, "%d", a->current); FontStr(buf, pos2); } +void AnimatedCounterTimeDraw(const AnimatedCounter *a, const struct vec2i pos) +{ + const struct vec2i pos2 = FontStr(a->prefix, pos); + char buf[256]; + sprintf(buf, "%d:%02d", a->current / 60, a->current % 60); + FontStr(buf, pos2); +} diff --git a/src/animated_counter.h b/src/animated_counter.h index 3050c7a67..d6daf9560 100644 --- a/src/animated_counter.h +++ b/src/animated_counter.h @@ -1,27 +1,27 @@ /* - Copyright (c) 2018, 2021 Cong Xu - All rights reserved. + Copyright (c) 2018, 2021 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 @@ -40,3 +40,4 @@ void AnimatedCounterTerminate(AnimatedCounter *a); // Return whether animation has finished bool AnimatedCounterUpdate(AnimatedCounter *a, const int ticks); void AnimatedCounterDraw(const AnimatedCounter *a, const struct vec2i pos); +void AnimatedCounterTimeDraw(const AnimatedCounter *a, const struct vec2i pos); diff --git a/src/briefing_screens.c b/src/briefing_screens.c index 782af8be0..2d6eafda6 100644 --- a/src/briefing_screens.c +++ b/src/briefing_screens.c @@ -367,6 +367,8 @@ typedef struct const PlayerData *Pd; AnimatedCounter Score; AnimatedCounter Total; + AnimatedCounter Time; + AnimatedCounter TimeTotal; AnimatedCounter HealthResurrection; AnimatedCounter ButcherNinjaFriendly; } PlayerSummaryDrawData; @@ -477,6 +479,7 @@ static void MissionSummaryOnEnter(GameLoopData *data) CA_FOREACH(PlayerData, p, gPlayerDatas) ApplyBonuses(p, bonus); + p->Totals.TimeTicks += mData->m->time; CA_FOREACH_END() } @@ -524,6 +527,10 @@ static void MissionSummaryOnEnter(GameLoopData *data) mData->pDatas[idx].Pd = pd; mData->pDatas[idx].Score = AnimatedCounterNew("Score: ", pd->Stats.Score); mData->pDatas[idx].Total = AnimatedCounterNew("Total: ", pd->Totals.Score); + mData->pDatas[idx].Time = + AnimatedCounterNew("Time: ", mData->m->time / FPS_FRAMELIMIT); + mData->pDatas[idx].TimeTotal = AnimatedCounterNew( + "Total Time: ", pd->Totals.TimeTicks / FPS_FRAMELIMIT); if (pd->survived) { const int healthBonus = GetHealthBonus(pd); @@ -575,6 +582,8 @@ static GameLoopResult MissionSummaryUpdate(GameLoopData *data, LoopRunner *l) { done = AnimatedCounterUpdate(&mData->pDatas[i].Score, 1) && done; done = AnimatedCounterUpdate(&mData->pDatas[i].Total, 1) && done; + done = AnimatedCounterUpdate(&mData->pDatas[i].Time, 1) && done; + done = AnimatedCounterUpdate(&mData->pDatas[i].TimeTotal, 1) && done; done = AnimatedCounterUpdate( &mData->pDatas[i].HealthResurrection, 1) && done; @@ -853,6 +862,10 @@ static void DrawPlayerSummary( textPos.y += FontH(); AnimatedCounterDraw(&data->Total, textPos); textPos.y += FontH(); + AnimatedCounterTimeDraw(&data->Time, textPos); + textPos.y += FontH(); + AnimatedCounterTimeDraw(&data->TimeTotal, textPos); + textPos.y += FontH(); sprintf( s, "Missions: %d", data->Pd->missions + (data->Pd->survived ? 1 : 0)); FontStr(s, textPos); diff --git a/src/cdogs/hud/hud.c b/src/cdogs/hud/hud.c index 57eb7ee40..aa9a43de0 100644 --- a/src/cdogs/hud/hud.c +++ b/src/cdogs/hud/hud.c @@ -259,7 +259,7 @@ static void DrawDeathmatchScores(HUD *hud) int maxKills = 0; CA_FOREACH(const PlayerData, p, gPlayerDatas) maxLives = MAX(maxLives, p->Lives); - maxKills = MAX(maxKills, p->Stats.Kills); + maxKills = MAX(maxKills, (int)p->Stats.Kills); CA_FOREACH_END() CA_FOREACH(const PlayerData, p, gPlayerDatas) // Player name; red if dead @@ -275,7 +275,7 @@ static void DrawDeathmatchScores(HUD *hud) FontStrOpt(buf, svec2i(0, y), opts); // kills; cyan if most kills - opts.Mask = p->Stats.Kills == maxKills ? colorCyan : colorWhite; + opts.Mask = (int)p->Stats.Kills == maxKills ? colorCyan : colorWhite; opts.Pad.x = killsColumn; sprintf(buf, "%d", p->Stats.Kills); FontStrOpt(buf, svec2i(0, y), opts); diff --git a/src/cdogs/net_util.h b/src/cdogs/net_util.h index 78cd8f17c..01585a85f 100644 --- a/src/cdogs/net_util.h +++ b/src/cdogs/net_util.h @@ -2,7 +2,7 @@ C-Dogs SDL A port of the legendary (and fun) action/arcade cdogs. - Copyright (c) 2014-2017, 2019-2020 Cong Xu + Copyright (c) 2014-2017, 2019-2021 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -40,7 +40,7 @@ #define NET_LISTEN_PORT 34219 -#define NET_PROTOCOL_VERSION 10 +#define NET_PROTOCOL_VERSION 11 // Messages diff --git a/src/hiscores.c b/src/hiscores.c index 37f492c95..145581ba0 100644 --- a/src/hiscores.c +++ b/src/hiscores.c @@ -1,65 +1,66 @@ /* - C-Dogs SDL - A port of the legendary (and fun) action/arcade cdogs. - Copyright (C) 1995 Ronny Wester - Copyright (C) 2003 Jeremy Chin - Copyright (C) 2003-2007 Lucas Martin-King - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - This file incorporates work covered by the following copyright and - permission notice: - - Copyright (c) 2013-2014, 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) 1995 Ronny Wester + Copyright (C) 2003 Jeremy Chin + Copyright (C) 2003-2007 Lucas Martin-King + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + This file incorporates work covered by the following copyright and + permission notice: + + Copyright (c) 2013-2014, 2017, 2021 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. */ #include "hiscores.h" -#include -#include #include +#include +#include #include -#include -#include #include +#include +#include #include #include #include +#include #ifdef __EMSCRIPTEN__ #include @@ -82,16 +83,15 @@ GameLoopData *HighScoresScreen(Campaign *co, GraphicsDevice *g) data->co = co; data->g = g; return GameLoopDataNew( - data, HighScoresScreenTerminate, NULL, NULL, - NULL, HighScoresScreenUpdate, NULL); + data, HighScoresScreenTerminate, NULL, NULL, NULL, + HighScoresScreenUpdate, NULL); } static void HighScoresScreenTerminate(GameLoopData *data) { HighScoresScreenData *hData = data->Data; CFREE(hData); } -static GameLoopResult HighScoresScreenUpdate( - GameLoopData *data, LoopRunner *l) +static GameLoopResult HighScoresScreenUpdate(GameLoopData *data, LoopRunner *l) { HighScoresScreenData *hData = data->Data; LoopRunnerPop(l); @@ -102,26 +102,26 @@ static GameLoopResult HighScoresScreenUpdate( bool allTime = false; bool todays = false; CA_FOREACH(PlayerData, p, gPlayerDatas) - const bool isPlayerComplete = - (!hData->co->IsQuit && !p->survived) || hData->co->IsComplete; - if (isPlayerComplete && p->IsLocal && IsPlayerHuman(p)) - { - EnterHighScore(p); - allTime |= p->allTime >= 0; - todays |= p->today >= 0; - } + const bool isPlayerComplete = + (!hData->co->IsQuit && !p->survived) || hData->co->IsComplete; + if (isPlayerComplete && p->IsLocal && IsPlayerHuman(p)) + { + EnterHighScore(p); + allTime |= p->allTime >= 0; + todays |= p->today >= 0; + } - if (!p->survived) - { - // Reset scores because we died :( - memset(&p->Totals, 0, sizeof p->Totals); - p->missions = 0; - } - else - { - p->missions++; - } - p->lastMission = hData->co->MissionIndex; + if (!p->survived) + { + // Reset scores because we died :( + memset(&p->Totals, 0, sizeof p->Totals); + p->missions = 0; + } + else + { + p->missions++; + } + p->lastMission = hData->co->MissionIndex; CA_FOREACH_END() SaveHighScores(); @@ -139,12 +139,15 @@ static GameLoopResult HighScoresScreenUpdate( return UPDATE_RESULT_OK; } - // Warning: written as-is to file -struct Entry { +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif +typedef struct +{ char name[20]; - int32_t unused1; - int32_t unused2; + int32_t timeTicks; + int32_t kills; int32_t unused3; int32_t unused4; int32_t unused5; @@ -152,19 +155,25 @@ struct Entry { int32_t score; int32_t missions; int32_t lastMission; -}; +} +#ifndef _MSC_VER +__attribute__((packed)) +#endif +HighScoreEntry; +#ifdef _MSC_VER +#pragma pack(pop) +#endif #define MAX_ENTRY 20 -static struct Entry allTimeHigh[MAX_ENTRY]; -static struct Entry todaysHigh[MAX_ENTRY]; +static HighScoreEntry allTimeHigh[MAX_ENTRY]; +static HighScoreEntry todaysHigh[MAX_ENTRY]; - -static int EnterTable(struct Entry *table, PlayerData *data) +static int EnterTable(HighScoreEntry *table, PlayerData *data) { for (int i = 0; i < MAX_ENTRY; i++) { - if (data->Totals.Score > table[i].score) + if ((int)data->Totals.Score > table[i].score) { for (int j = MAX_ENTRY - 1; j > i; j--) { @@ -172,6 +181,8 @@ static int EnterTable(struct Entry *table, PlayerData *data) } strcpy(table[i].name, data->name); + table[i].timeTicks = data->Totals.TimeTicks; + table[i].kills = data->Totals.Kills; table[i].score = data->Totals.Score; table[i].missions = data->missions; table[i].lastMission = data->lastMission; @@ -195,16 +206,16 @@ static void DisplayAt(int x, int y, const char *s, int hilite) } static int DisplayEntry( - const int x, const int y, const int idx, const struct Entry *e, + const int x, const int y, const int idx, const HighScoreEntry *e, const bool hilite) { char s[10]; -#define INDEX_OFFSET 15 -#define SCORE_OFFSET 40 -#define MISSIONS_OFFSET 60 -#define MISSION_OFFSET 80 -#define NAME_OFFSET 85 +#define INDEX_OFFSET 15 +#define SCORE_OFFSET 40 +#define MISSIONS_OFFSET 60 +#define MISSION_OFFSET 80 +#define NAME_OFFSET 85 sprintf(s, "%d.", idx + 1); DisplayAt(x + INDEX_OFFSET - FontStrW(s), y, s, hilite); @@ -220,7 +231,7 @@ static int DisplayEntry( } static int DisplayPage( - const char *title, const int idxStart, const struct Entry *e, + const char *title, const int idxStart, const HighScoreEntry *e, const int highlights[MAX_LOCAL_PLAYERS]) { int x = 80; @@ -253,7 +264,7 @@ typedef struct { GraphicsDevice *g; const char *title; - const struct Entry *scores; + const HighScoreEntry *scores; int highlights[MAX_LOCAL_PLAYERS]; int scoreIdx; } HighScoresData; @@ -280,8 +291,8 @@ GameLoopData *DisplayAllTimeHighScores(GraphicsDevice *graphics) data->title = "All time high scores:"; data->scores = allTimeHigh; return GameLoopDataNew( - data, HighScoreTerminate, NULL, NULL, - NULL, HighScoreUpdate, HighScoreDraw); + data, HighScoreTerminate, NULL, NULL, NULL, HighScoreUpdate, + HighScoreDraw); } GameLoopData *DisplayTodaysHighScores(GraphicsDevice *graphics) { @@ -303,8 +314,8 @@ GameLoopData *DisplayTodaysHighScores(GraphicsDevice *graphics) data->title = "Today's highest score:"; data->scores = todaysHigh; return GameLoopDataNew( - data, HighScoreTerminate, NULL, NULL, - NULL, HighScoreUpdate, HighScoreDraw); + data, HighScoreTerminate, NULL, NULL, NULL, HighScoreUpdate, + HighScoreDraw); } static void HighScoreTerminate(GameLoopData *data) { @@ -333,8 +344,7 @@ static GameLoopResult HighScoreUpdate(GameLoopData *data, LoopRunner *l) return UPDATE_RESULT_OK; } - -#define MAGIC 4711 +#define MAGIC 4711 void SaveHighScores(void) { @@ -344,7 +354,8 @@ void SaveHighScores(void) struct tm *tp; f = fopen(GetConfigFilePath(SCORES_FILE), "wb"); - if (f != NULL) { + if (f != NULL) + { magic = MAGIC; fwrite(&magic, sizeof(magic), 1, f); @@ -365,14 +376,13 @@ void SaveHighScores(void) fclose(f); #ifdef __EMSCRIPTEN__ - EM_ASM( - //persist changes - FS.syncfs(false,function (err) { - assert(!err); - }); - ); + EM_ASM( + // persist changes + FS.syncfs( + false, function(err) { assert(!err); });); #endif - } else + } + else printf("Unable to open %s\n", SCORES_FILE); } @@ -387,25 +397,27 @@ void LoadHighScores(void) memset(allTimeHigh, 0, sizeof(allTimeHigh)); memset(todaysHigh, 0, sizeof(todaysHigh)); - f = fopen(GetConfigFilePath(SCORES_FILE), "rb"); - if (f != NULL) { + const char *path = GetConfigFilePath(SCORES_FILE); + f = fopen(path, "rb"); + if (f != NULL) + { size_t elementsRead; - #define CHECK_FREAD(count)\ - if (elementsRead != count) {\ - fclose(f);\ - return;\ - } +#define CHECK_FREAD(count) \ + if (elementsRead != count) \ + { \ + fclose(f); \ + return; \ + } elementsRead = fread(&magic, sizeof(magic), 1, f); CHECK_FREAD(1) - if (magic != MAGIC) { + if (magic != MAGIC) + { fclose(f); return; } - //for (i = 0; i < MAX_ENTRY; i++) { elementsRead = fread(allTimeHigh, sizeof(allTimeHigh), 1, f); CHECK_FREAD(1) - //} t = time(NULL); tp = localtime(&t); @@ -416,13 +428,16 @@ void LoadHighScores(void) elementsRead = fread(&d, sizeof(d), 1, f); CHECK_FREAD(1) - if (tp->tm_year == y && tp->tm_mon == m && tp->tm_mday == d) { + if (tp->tm_year == y && tp->tm_mon == m && tp->tm_mday == d) + { elementsRead = fread(todaysHigh, sizeof(todaysHigh), 1, f); CHECK_FREAD(1) } fclose(f); - } else { - printf("Unable to open %s\n", SCORES_FILE); + } + else + { + LOG(LM_MAIN, LL_WARN, "Unable to open %s\n", path); } } diff --git a/src/proto/msg.pb.h b/src/proto/msg.pb.h index 9572b82c7..3d6cc6104 100644 --- a/src/proto/msg.pb.h +++ b/src/proto/msg.pb.h @@ -123,10 +123,11 @@ typedef struct _NPlayerRemove { } NPlayerRemove; typedef struct _NPlayerStats { - int32_t Score; - int32_t Kills; - int32_t Suicides; - int32_t Friendlies; + uint32_t Score; + uint32_t Kills; + uint32_t Suicides; + uint32_t Friendlies; + uint32_t TimeTicks; } NPlayerStats; typedef struct _NRemoveBullet { @@ -384,7 +385,7 @@ extern "C" { #define NCampaignDef_init_default {"", 0, 0} #define NColor_init_default {0} #define NCharColors_init_default {false, NColor_init_default, false, NColor_init_default, false, NColor_init_default, false, NColor_init_default, false, NColor_init_default, false, NColor_init_default} -#define NPlayerStats_init_default {0, 0, 0, 0} +#define NPlayerStats_init_default {0, 0, 0, 0, 0} #define NPlayerData_init_default {"", "", "", false, NCharColors_init_default, 0, {"", "", ""}, 0, false, NPlayerStats_init_default, false, NPlayerStats_init_default, 0, 0, 0, 0, {NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default, NAmmo_init_default}} #define NPlayerRemove_init_default {0} #define NConfig_init_default {"", ""} @@ -434,7 +435,7 @@ extern "C" { #define NCampaignDef_init_zero {"", 0, 0} #define NColor_init_zero {0} #define NCharColors_init_zero {false, NColor_init_zero, false, NColor_init_zero, false, NColor_init_zero, false, NColor_init_zero, false, NColor_init_zero, false, NColor_init_zero} -#define NPlayerStats_init_zero {0, 0, 0, 0} +#define NPlayerStats_init_zero {0, 0, 0, 0, 0} #define NPlayerData_init_zero {"", "", "", false, NCharColors_init_zero, 0, {"", "", ""}, 0, false, NPlayerStats_init_zero, false, NPlayerStats_init_zero, 0, 0, 0, 0, {NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero, NAmmo_init_zero}} #define NPlayerRemove_init_zero {0} #define NConfig_init_zero {"", ""} @@ -534,6 +535,7 @@ extern "C" { #define NPlayerStats_Kills_tag 2 #define NPlayerStats_Suicides_tag 3 #define NPlayerStats_Friendlies_tag 4 +#define NPlayerStats_TimeTicks_tag 5 #define NRemoveBullet_UID_tag 1 #define NRemovePickup_UID_tag 1 #define NRemovePickup_SpawnerUID_tag 2 @@ -707,10 +709,11 @@ X(a, STATIC, OPTIONAL, MESSAGE, Feet, 6) #define NCharColors_Feet_MSGTYPE NColor #define NPlayerStats_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, INT32, Score, 1) \ -X(a, STATIC, SINGULAR, INT32, Kills, 2) \ -X(a, STATIC, SINGULAR, INT32, Suicides, 3) \ -X(a, STATIC, SINGULAR, INT32, Friendlies, 4) +X(a, STATIC, SINGULAR, UINT32, Score, 1) \ +X(a, STATIC, SINGULAR, UINT32, Kills, 2) \ +X(a, STATIC, SINGULAR, UINT32, Suicides, 3) \ +X(a, STATIC, SINGULAR, UINT32, Friendlies, 4) \ +X(a, STATIC, SINGULAR, UINT32, TimeTicks, 5) #define NPlayerStats_CALLBACK NULL #define NPlayerStats_DEFAULT NULL @@ -1181,8 +1184,8 @@ extern const pb_msgdesc_t NMissionEnd_msg; #define NCampaignDef_size 4115 #define NColor_size 11 #define NCharColors_size 78 -#define NPlayerStats_size 44 -#define NPlayerData_size 2659 +#define NPlayerStats_size 30 +#define NPlayerData_size 2631 #define NPlayerRemove_size 6 #define NConfig_size 260 #define NTileSet_size 295 diff --git a/src/proto/msg.proto b/src/proto/msg.proto index a113d2c5a..01bc982c2 100644 --- a/src/proto/msg.proto +++ b/src/proto/msg.proto @@ -38,10 +38,11 @@ message NCharColors { } message NPlayerStats { - int32 Score = 1; - int32 Kills = 2; - int32 Suicides = 3; - int32 Friendlies = 4; + uint32 Score = 1; + uint32 Kills = 2; + uint32 Suicides = 3; + uint32 Friendlies = 4; + uint32 TimeTicks = 5; } message NPlayerData { diff --git a/src/screens_end.c b/src/screens_end.c index be35728fa..95e5cd3e9 100644 --- a/src/screens_end.c +++ b/src/screens_end.c @@ -203,10 +203,12 @@ static void PlayerListCustomDraw( int x = xStart; int y = pos.y; FontStrMask("Player", svec2i(x, y), colorPurple); - x += 100; + x += 68; FontStrMask("Score", svec2i(x, y), colorPurple); x += 32; FontStrMask("Kills", svec2i(x, y), colorPurple); + x += 32; + FontStrMask("Time", svec2i(x, y), colorPurple); y += FontH() * 2 + PLAYER_LIST_ROW_HEIGHT + 4; // Then draw the player list int maxScore = -1; @@ -235,7 +237,7 @@ static void PlayerListCustomDraw( textColor, p->guns[0]); // Draw score - x += 100; + x += 68; char buf[256]; sprintf(buf, "%d", p->Totals.Score); FontStrMask(buf, svec2i(x, y), textColor); @@ -245,6 +247,12 @@ static void PlayerListCustomDraw( sprintf(buf, "%d", p->Totals.Kills); FontStrMask(buf, svec2i(x, y), textColor); + // Draw time + x += 32; + const int timeSeconds = p->Totals.TimeTicks / FPS_FRAMELIMIT; + sprintf(buf, "%d:%02d", timeSeconds / 60, timeSeconds % 60); + FontStrMask(buf, svec2i(x, y), textColor); + // Draw winner/award text x += 32; if (pl->showWinners && GetModeScore(p) == maxScore) @@ -411,7 +419,7 @@ GameLoopData *ScreenDogfightScores(void) if (IsPlayerAlive(p)) { p->Totals.Score++; - maxScore = MAX(maxScore, p->Totals.Score); + maxScore = MAX(maxScore, (int)p->Totals.Score); } CA_FOREACH_END() gCampaign.IsComplete = maxScore == ModeMaxRoundsWon(gCampaign.Entry.Mode);