From 5fa344f7db9f02f2fc1ae5d81cfdffb0def1e73b Mon Sep 17 00:00:00 2001 From: Joshua Murphy Date: Wed, 30 Aug 2023 21:20:58 -0230 Subject: [PATCH 1/4] Working on encapsulating the status bar code, and improved debug logging for V_CopyRect() --- src/doom/hud/statusbar.c | 110 +++++++++++++++++++++------------------ src/doom/hud/statusbar.h | 10 ++++ src/doom/hud/widget.c | 8 +-- src/v_video.c | 93 ++++++++++++++++++++++++++++----- 4 files changed, 154 insertions(+), 67 deletions(-) diff --git a/src/doom/hud/statusbar.c b/src/doom/hud/statusbar.c index 16ca29e706..e341aaea22 100644 --- a/src/doom/hud/statusbar.c +++ b/src/doom/hud/statusbar.c @@ -82,13 +82,6 @@ // For Responder #define ST_TOGGLECHAT KEY_ENTER -// Location of status bar -#define ST_X 0 -#define ST_X2 104 - -#define ST_FX 143 -#define ST_FY 169 - // Should be set to patch width // for tall numbers later on #define ST_TALLNUMWIDTH (tallnum[0]->width) @@ -157,37 +150,6 @@ #define ST_KEY2X 239 #define ST_KEY2Y 191 -// Ammunition counter. -#define ST_AMMO0WIDTH 3 -#define ST_AMMO0HEIGHT 6 -#define ST_AMMO0X 288 -#define ST_AMMO0Y 173 -#define ST_AMMO1WIDTH ST_AMMO0WIDTH -#define ST_AMMO1X 288 -#define ST_AMMO1Y 179 -#define ST_AMMO2WIDTH ST_AMMO0WIDTH -#define ST_AMMO2X 288 -#define ST_AMMO2Y 191 -#define ST_AMMO3WIDTH ST_AMMO0WIDTH -#define ST_AMMO3X 288 -#define ST_AMMO3Y 185 - -// Indicate maximum ammunition. -// Only needed because backpack exists. -#define ST_MAXAMMO0WIDTH 3 -#define ST_MAXAMMO0HEIGHT 5 -#define ST_MAXAMMO0X 314 -#define ST_MAXAMMO0Y 173 -#define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH -#define ST_MAXAMMO1X 314 -#define ST_MAXAMMO1Y 179 -#define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH -#define ST_MAXAMMO2X 314 -#define ST_MAXAMMO2Y 191 -#define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH -#define ST_MAXAMMO3X 314 -#define ST_MAXAMMO3Y 185 - // pistol #define ST_WEAPON0X 110 #define ST_WEAPON0Y 172 @@ -243,6 +205,8 @@ // graphics are drawn to a backing screen and blitted to the real screen pixel_t *st_backing_screen; +status_bar_t *status_bar; + // main player in game static player_t *plyr; @@ -403,29 +367,64 @@ cheatseq_t cheat_mypos = CHEAT("idmypos", 0); // void ST_Stop(void); -void ST_refreshBackground(void) +// TODO: return st_backing_screen +void *StatusBar_CreateBackground() +{ + log_debug("StatusBar_CreateBackground(): Creating status bar background"); + // TODO: reduce the scope of st_backing_screen. + st_backing_screen = + (pixel_t *) Z_Malloc(ST_WIDTH * ST_HEIGHT * sizeof(*st_backing_screen), PU_STATIC, 0); +} + + +status_bar_t *StatusBar_CreateStatusBar(void) { + log_debug("StatusBar_CreateStatusBar(): Creating status bar"); + status_bar_t *status_bar = Z_Malloc(sizeof(status_bar_t), PU_STATIC, 0); + status_bar->height = 32; + status_bar->width = SCREENWIDTH; + status_bar->x = 0; + status_bar->y = SCREENHEIGHT - status_bar->height; + + // TODO: + // status_bar->backing_screen = StatusBar_CreateBackground() + StatusBar_CreateBackground(); + + return status_bar; +} + +void StatusBar_DrawBackground(status_bar_t *status_bar) +{ + int x = status_bar->x; + int y = status_bar->y; + int width = status_bar->width; + int height = status_bar->height; + // TODO: return early if (st_statusbaron) { V_UseBuffer(st_backing_screen); - V_DrawPatch(ST_X, 0, sbar); + V_DrawPatch(x, 0, sbar); + // TOOD: remove (we don't care about DOOM 1.0) // draw right side of bar if needed (Doom 1.0) if (sbarr) { V_DrawPatch(ST_ARMSBGX, 0, sbarr); } + // TODO: Make the frag counter it's own widget and or function if (netgame) { - V_DrawPatch(ST_FX, 0, faceback); + const int frag_widget_x = 143; + const int frag_widget_y = 169; + V_DrawPatch(frag_widget_x, frag_widget_y, faceback); } V_RestoreBuffer(); - V_CopyRect(ST_X, 0, st_backing_screen, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y); + V_CopyRect(x, 0, st_backing_screen, width, height, x, y); } } @@ -1000,6 +999,13 @@ void ST_drawWidgets(boolean refresh) STlib_updateNum(&w_frags, refresh); } +//TODO: +void StatusBar_DrawStatusBar(status_bar_t *status_bar) +{ + StatusBar_DrawBackground(status_bar); + //StatusBar_DrawWidgets(); +} + void ST_doRefresh(void) { @@ -1007,7 +1013,7 @@ void ST_doRefresh(void) st_firsttime = false; // draw status bar background to off-screen buff - ST_refreshBackground(); + StatusBar_DrawStatusBar(status_bar); // and refresh all widgets ST_drawWidgets(true); @@ -1202,17 +1208,23 @@ void StatusBar_CreateCurrentAmmoCountWidget() void StatusBar_CreateAmmoCounterWidgets() { + const int x = 288; + + // The ammo counter for "BULL" widget_ammo_bullet_counter = STWidget_CreateFractionWidget( - ST_AMMO0X, ST_AMMO0Y, &plyr->ammo[0], &plyr->maxammo[0], &st_statusbaron, shortnum); + x, 173, &plyr->ammo[0], &plyr->maxammo[0], &st_statusbaron, shortnum); + // The ammo counter for "SHELL" widget_ammo_shell_counter = STWidget_CreateFractionWidget( - ST_AMMO1X, ST_AMMO1Y, &plyr->ammo[1], &plyr->maxammo[1], &st_statusbaron, shortnum); + x, 179, &plyr->ammo[1], &plyr->maxammo[1], &st_statusbaron, shortnum); + // The ammo counter for "RCKT" widget_ammo_rocket_counter = STWidget_CreateFractionWidget( - ST_AMMO2X, ST_AMMO2Y, &plyr->ammo[2], &plyr->maxammo[2], &st_statusbaron, shortnum); + x, 191, &plyr->ammo[2], &plyr->maxammo[2], &st_statusbaron, shortnum); + // The ammo counter for "CELL" widget_ammo_cell_counter = STWidget_CreateFractionWidget( - ST_AMMO3X, ST_AMMO3Y, &plyr->ammo[3], &plyr->maxammo[3], &st_statusbaron, shortnum); + x, 185, &plyr->ammo[3], &plyr->maxammo[3], &st_statusbaron, shortnum); } /** @@ -1297,6 +1309,7 @@ void ST_Start(void) } ST_initData(); + status_bar = StatusBar_CreateStatusBar(); ST_createWidgets(); st_stopped = false; } @@ -1318,7 +1331,4 @@ void ST_Init(void) { lu_palette = W_GetNumForName("PLAYPAL"); ST_loadUnloadGraphics(ST_loadCallback); - - st_backing_screen = - (pixel_t *) Z_Malloc(ST_WIDTH * ST_HEIGHT * sizeof(*st_backing_screen), PU_STATIC, 0); } diff --git a/src/doom/hud/statusbar.h b/src/doom/hud/statusbar.h index f939550880..9367b02980 100644 --- a/src/doom/hud/statusbar.h +++ b/src/doom/hud/statusbar.h @@ -52,6 +52,16 @@ void ST_Start(void); // Called by startup code. void ST_Init(void); +typedef struct +{ + int height; + int width; + int x; + int y; + pixel_t *backing_screen; + +} status_bar_t; + // States for status bar code. typedef enum diff --git a/src/doom/hud/widget.c b/src/doom/hud/widget.c index 05ebbcb103..aa44c913e7 100644 --- a/src/doom/hud/widget.c +++ b/src/doom/hud/widget.c @@ -61,10 +61,10 @@ void STlib_init(void) * @brief Creates a new status bar widget for displaying a number * @param x The x position of the new widget * @param y The y position of the new widget + * @param numdigits The amount of numbers that can be displayed (i.e width=3 for a 3 digit number like health or ammo.) + * @param value The number to be displayed by the widget + * @param enabled Whether the widget is enabled (and thus drawn) * @param patches List of graphics patches to be used as the drawing font - * @param num The number to be displayed by the widget - * @param on Whether the widget is enabled (and thus drawn) - * @param width The amount of numbers that can be displayed (i.e width=3 for a 3 digit number like health or ammo.) * @param percent_sign_patch The graphics patch for the percent sign. Use `NULL` for creating number widgets without a percent sign. * @return The newly-created widget. * @@ -117,7 +117,7 @@ void STlib_initNum(widget_number_t *n, int x, int y, patch_t **pl, int *num, boo } /** - * Draws a fractional number to the status bar, useful for the small ammo counters. + * Creates a new fractional number widget for the status bar, useful for the small ammo counters. * * Four of these are used in DOOM, one each for "BULL", "SHELL", "RCKT", and "CELL". In DOOM, the * "/" character used by the fractional ammo counter is actually baked into the background status diff --git a/src/v_video.c b/src/v_video.c index 9f1b66b5a7..e54fa683e1 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -26,6 +26,8 @@ #include "i_system.h" +#include "log.h" + #include "doomtype.h" #include "i_input.h" @@ -66,7 +68,80 @@ void V_MarkRect(int x, int y, int width, int height) M_AddToBox (dirtybox, x + width-1, y + height-1); } } - + +boolean V_CheckCopyRectMinimums(int srcx, int srcy, int destx, int desty) +{ + boolean valid_rect = true; + + char negative_value_error[] = "V_CheckCopyRectMinimums(): Can't copy rectangle because its %s coordinates %s is negative: %d"; + + if (srcx < 0) + { + log_fatal(negative_value_error, "source" "x", srcx); + valid_rect = false; + } + if (srcy < 0) + { + log_fatal(negative_value_error, "source" "y", srcy); + valid_rect = false; + } + + if (destx < 0) + { + log_fatal(negative_value_error, "destination" "x", destx); + valid_rect = false; + } + + if (destx < 0) + { + log_fatal(negative_value_error, "destination" "y", desty); + valid_rect = false; + } + + return valid_rect; +} + +boolean V_CheckCopyRectMaximums(int srcx, int srcy, int destx, int desty, int width, int height) +{ + boolean valid_rect = true; + + char error[] = "V_CheckCopyRectMaximums(): Can't copy rectangle because its %s dimensions (%dpx x %dpx) exceed the screen dimensions (%dpx x %dpx)"; + + int max_src_x = srcx + width; + int max_src_y = srcy + height; + + if (max_src_x > SCREENWIDTH || max_src_y > SCREENHEIGHT) + { + log_fatal(error, "source", max_src_x, max_src_y, SCREENWIDTH, SCREENHEIGHT); + valid_rect = false; + } + + int max_dest_x = srcx + width; + int max_dest_y = srcy + height; + + if (max_dest_x > SCREENWIDTH || max_dest_y > SCREENHEIGHT) + { + log_fatal(error, "destination", max_dest_x, max_dest_y, SCREENWIDTH, SCREENHEIGHT); + valid_rect = false; + } + + return valid_rect; +} + +boolean V_CheckCopyRect(int srcx, int srcy, int destx, int desty, int width, int height) +{ + if (!V_CheckCopyRectMinimums(srcx, srcy, destx, desty)) + { + return false; + } + + if (!V_CheckCopyRectMaximums(srcx, srcy, destx, desty, width, height)) + { + return false; + } + + return true; +} // // V_CopyRect @@ -77,20 +152,12 @@ void V_CopyRect(int srcx, int srcy, pixel_t *source, { pixel_t *src; pixel_t *dest; - -#ifdef RANGECHECK - if (srcx < 0 - || srcx + width > SCREENWIDTH - || srcy < 0 - || srcy + height > SCREENHEIGHT - || destx < 0 - || destx + width > SCREENWIDTH - || desty < 0 - || desty + height > SCREENHEIGHT) + + if (!V_CheckCopyRect(srcx, srcy, destx, desty, width, height)) { - I_Error ("Bad V_CopyRect"); + log_fatal("V_CopyRect(): Could not copy rectangle %x", source); + System_Exit(); } -#endif V_MarkRect(destx, desty, width, height); From 4594c8f17a214749c7eadef375a6e8d9838e0c30 Mon Sep 17 00:00:00 2001 From: Joshua Murphy Date: Wed, 30 Aug 2023 21:46:25 -0230 Subject: [PATCH 2/4] Working towards encapsulating st_backing_screen --- src/doom/hud/statusbar.c | 36 ++++++++++++++++++++---------------- src/doom/hud/widget.c | 10 +++++----- src/doom/hud/widget.h | 4 ++-- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/doom/hud/statusbar.c b/src/doom/hud/statusbar.c index e341aaea22..a21f61e72c 100644 --- a/src/doom/hud/statusbar.c +++ b/src/doom/hud/statusbar.c @@ -367,7 +367,8 @@ cheatseq_t cheat_mypos = CHEAT("idmypos", 0); // void ST_Stop(void); -// TODO: return st_backing_screen +// TODO: +// pixel_t *StatusBar_CreateBackingScreen() void *StatusBar_CreateBackground() { log_debug("StatusBar_CreateBackground(): Creating status bar background"); @@ -386,14 +387,15 @@ status_bar_t *StatusBar_CreateStatusBar(void) status_bar->x = 0; status_bar->y = SCREENHEIGHT - status_bar->height; - // TODO: - // status_bar->backing_screen = StatusBar_CreateBackground() + //TODO: Replace StatusBar_CreateBackground(); + // With: + //status_bar->screen = StatusBar_CreateBackingScreen() return status_bar; } -void StatusBar_DrawBackground(status_bar_t *status_bar) +void StatusBar_DrawBackground(status_bar_t *status_bar, pixel_t *screen) { int x = status_bar->x; int y = status_bar->y; @@ -403,7 +405,8 @@ void StatusBar_DrawBackground(status_bar_t *status_bar) // TODO: return early if (st_statusbaron) { - V_UseBuffer(st_backing_screen); + //TODO: status_bar->screen + V_UseBuffer(screen); V_DrawPatch(x, 0, sbar); @@ -424,7 +427,8 @@ void StatusBar_DrawBackground(status_bar_t *status_bar) V_RestoreBuffer(); - V_CopyRect(x, 0, st_backing_screen, width, height, x, y); + //TODO: status_bar->screen + V_CopyRect(x, 0, screen, width, height, x, y); } } @@ -971,17 +975,17 @@ void ST_drawWidgets(boolean refresh) // used by w_frags widget st_fragson = deathmatch && st_statusbaron; - STWidget_DrawNumberWidget(widget_ammo_current_counter, refresh); + STWidget_DrawNumberWidget(widget_ammo_current_counter, st_backing_screen, refresh); // Draw the ammo counters // TODO: Move to separate Draw function - STWidget_DrawFractionWidget(widget_ammo_bullet_counter, refresh); - STWidget_DrawFractionWidget(widget_ammo_shell_counter, refresh); - STWidget_DrawFractionWidget(widget_ammo_rocket_counter, refresh); - STWidget_DrawFractionWidget(widget_ammo_cell_counter, refresh); + STWidget_DrawFractionWidget(widget_ammo_bullet_counter, st_backing_screen, refresh); + STWidget_DrawFractionWidget(widget_ammo_shell_counter, st_backing_screen, refresh); + STWidget_DrawFractionWidget(widget_ammo_rocket_counter, st_backing_screen, refresh); + STWidget_DrawFractionWidget(widget_ammo_cell_counter, st_backing_screen, refresh); - STWidget_DrawNumberWidget(widget_health, refresh); - STWidget_DrawNumberWidget(widget_armor, refresh); + STWidget_DrawNumberWidget(widget_health, st_backing_screen, refresh); + STWidget_DrawNumberWidget(widget_armor, st_backing_screen, refresh); STlib_updateBinIcon(&w_armsbg, refresh); @@ -1000,9 +1004,9 @@ void ST_drawWidgets(boolean refresh) STlib_updateNum(&w_frags, refresh); } //TODO: -void StatusBar_DrawStatusBar(status_bar_t *status_bar) +void StatusBar_DrawStatusBar(status_bar_t *status_bar, pixel_t *screen) { - StatusBar_DrawBackground(status_bar); + StatusBar_DrawBackground(status_bar, screen); //StatusBar_DrawWidgets(); } @@ -1013,7 +1017,7 @@ void ST_doRefresh(void) st_firsttime = false; // draw status bar background to off-screen buff - StatusBar_DrawStatusBar(status_bar); + StatusBar_DrawStatusBar(status_bar, st_backing_screen); // and refresh all widgets ST_drawWidgets(true); diff --git a/src/doom/hud/widget.c b/src/doom/hud/widget.c index aa44c913e7..0db35beb6e 100644 --- a/src/doom/hud/widget.c +++ b/src/doom/hud/widget.c @@ -160,17 +160,17 @@ widget_fraction_t *STWidget_CreateFractionWidget( return widget; } -void STWidget_DrawFractionWidget(widget_fraction_t *widget, boolean refresh) +void STWidget_DrawFractionWidget(widget_fraction_t *widget, pixel_t *screen, boolean refresh) { - STWidget_DrawNumberWidget(widget->numerator, refresh); - STWidget_DrawNumberWidget(widget->denominator, refresh); + STWidget_DrawNumberWidget(widget->numerator, screen, refresh); + STWidget_DrawNumberWidget(widget->denominator, screen, refresh); } /** * @brief Draws a number widget to the status bar. * */ -void STWidget_DrawNumberWidget(widget_number_t *widget, boolean refresh) +void STWidget_DrawNumberWidget(widget_number_t *widget, pixel_t *screen, boolean refresh) { if (!widget) { @@ -233,7 +233,7 @@ void STWidget_DrawNumberWidget(widget_number_t *widget, boolean refresh) I_Error("drawNum: widget->y - ST_Y < 0"); } - V_CopyRect(x, widget->y - ST_Y, st_backing_screen, w * numdigits, h, x, widget->y); + V_CopyRect(x, widget->y - ST_Y, screen, w * numdigits, h, x, widget->y); // if non-number, do not draw it if (num == 1994) diff --git a/src/doom/hud/widget.h b/src/doom/hud/widget.h index 8045aaf5ee..b6e7b0c862 100644 --- a/src/doom/hud/widget.h +++ b/src/doom/hud/widget.h @@ -162,8 +162,8 @@ widget_fraction_t *STWidget_CreateFractionWidget( boolean *enabled, patch_t **patches); -void STWidget_DrawNumberWidget(widget_number_t *widget, boolean refresh); -void STWidget_DrawFractionWidget(widget_fraction_t *widget, boolean refresh); +void STWidget_DrawNumberWidget(widget_number_t *widget, pixel_t *screen, boolean refresh); +void STWidget_DrawFractionWidget(widget_fraction_t *widget, pixel_t *screen, boolean refresh); void STlib_initNum( widget_number_t *n, int x, int y, patch_t **pl, int *num, boolean *on, int width); From 01b7118925a3516e8fa604cdbfa169e6248b82cd Mon Sep 17 00:00:00 2001 From: Joshua Murphy Date: Thu, 31 Aug 2023 13:08:30 -0230 Subject: [PATCH 3/4] WIP add frag counter as a widget --- src/doom/hud/statusbar.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/doom/hud/statusbar.c b/src/doom/hud/statusbar.c index a21f61e72c..6d690661b6 100644 --- a/src/doom/hud/statusbar.c +++ b/src/doom/hud/statusbar.c @@ -287,7 +287,7 @@ static patch_t *arms[6][2]; static widget_number_t *widget_ammo_current_counter; // in deathmatch only, summary of frags stats -static widget_number_t w_frags; +static widget_number_t *widget_frag_counter; // health widget static widget_number_t *widget_health; @@ -865,7 +865,7 @@ void ST_updateWidgets(void) // used by w_arms[] widgets st_armson = st_statusbaron && !deathmatch; - // used by w_frags widget + // used by widget_frag_counter widget st_fragson = deathmatch && st_statusbaron; st_fragscount = 0; @@ -972,7 +972,7 @@ void ST_drawWidgets(boolean refresh) // used by w_arms[] widgets st_armson = st_statusbaron && !deathmatch; - // used by w_frags widget + // used by widget_frag_counter widget st_fragson = deathmatch && st_statusbaron; STWidget_DrawNumberWidget(widget_ammo_current_counter, st_backing_screen, refresh); @@ -986,6 +986,7 @@ void ST_drawWidgets(boolean refresh) STWidget_DrawNumberWidget(widget_health, st_backing_screen, refresh); STWidget_DrawNumberWidget(widget_armor, st_backing_screen, refresh); + //STWidget_DrawNumberWidget(widget_frag_counter, st_backing_screen, refresh); STlib_updateBinIcon(&w_armsbg, refresh); @@ -1001,7 +1002,6 @@ void ST_drawWidgets(boolean refresh) STlib_updateMultIcon(&w_keyboxes[i], refresh); } - STlib_updateNum(&w_frags, refresh); } //TODO: void StatusBar_DrawStatusBar(status_bar_t *status_bar, pixel_t *screen) @@ -1261,6 +1261,21 @@ void StatusBar_CreateArmorWidget() x, y, num_digits, &plyr->armorpoints, &st_statusbaron, tallnum, tallpercent); } +/** + * Create the statusbar widget for showing frag counts. + */ +void StatusBar_CreateFragCounterWidget() +{ + log_trace("StatusBar_CreateFragCounterWidget(): Creating frag counter widget"); + + const int num_digits = 2; + const int x = 138; + const int y = 171; + + widget_frag_counter = STWidget_CreateNumberWidget( + x, y, num_digits, &st_fragscount, &st_fragson, tallnum, NULL); +} + void ST_createWidgets(void) { @@ -1268,6 +1283,7 @@ void ST_createWidgets(void) StatusBar_CreateHealthWidget(); StatusBar_CreateArmorWidget(); StatusBar_CreateAmmoCounterWidgets(); + //StatusBar_CreateFragCounterWidget(); // arms background @@ -1286,10 +1302,6 @@ void ST_createWidgets(void) &st_armson); } - // frags sum - STlib_initNum( - &w_frags, ST_FRAGSX, ST_FRAGSY, tallnum, &st_fragscount, &st_fragson, ST_FRAGSWIDTH); - // faces STlib_initMultIcon(&w_faces, ST_FACESX, ST_FACESY, faces, &st_faceindex, &st_statusbaron); From c40e0da25b1031ce6d365acac40cf492b227a3d9 Mon Sep 17 00:00:00 2001 From: Joshua Murphy Date: Sat, 2 Sep 2023 23:58:16 -0230 Subject: [PATCH 4/4] Working on creating the Drawable and DrawableRegistry API --- .clang-tidy | 56 ++ docs/drawable_api.md | 36 ++ meson.build | 12 +- scripts/README.md | 3 + src/d_mode.h | 3 + src/doom/hud/statusbar.c | 704 +++++++++++++----------- src/doom/hud/statusbar.h | 27 +- src/doom/hud/widget.c | 435 --------------- src/doom/hud/widget.h | 185 ------- src/engine/graphics/drawable.c | 308 +++++++++++ src/engine/graphics/drawable.h | 214 +++++++ src/engine/graphics/drawable_registry.c | 153 +++++ src/engine/graphics/drawable_registry.h | 48 ++ src/engine/graphics/widget.c | 135 +++++ src/engine/graphics/widget.h | 94 ++++ src/i_system.c | 2 +- src/i_system.h | 2 +- src/v_video.c | 2 +- 18 files changed, 1463 insertions(+), 956 deletions(-) create mode 100644 .clang-tidy create mode 100644 docs/drawable_api.md create mode 100644 scripts/README.md delete mode 100644 src/doom/hud/widget.c delete mode 100644 src/doom/hud/widget.h create mode 100644 src/engine/graphics/drawable.c create mode 100644 src/engine/graphics/drawable.h create mode 100644 src/engine/graphics/drawable_registry.c create mode 100644 src/engine/graphics/drawable_registry.h create mode 100644 src/engine/graphics/widget.c create mode 100644 src/engine/graphics/widget.h diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000000..8b0fde7b8a --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,56 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*' +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +User: joshuam +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: llvm-else-after-return.WarnOnConditionVariables + value: '0' + - key: llvm-else-after-return.WarnOnUnfixable + value: '0' + - key: llvm-qualified-auto.AddConstToQualified + value: '0' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + - key: readability-identifier-naming.StructCase + value: 'CamelCase' + - key: readability-identifier-naming.FunctionCase + value: 'lower_case' + - key: readability-identifier-naming.VariableCase + value: 'lower_case' + - key: readability-identifier-naming.GlobalConstantCase + value: 'UPPER_CASE' + - key: readability-identifier-naming.ConstantCase + value: 'UPPER_CASE' +... + diff --git a/docs/drawable_api.md b/docs/drawable_api.md new file mode 100644 index 0000000000..e372b0c533 --- /dev/null +++ b/docs/drawable_api.md @@ -0,0 +1,36 @@ +# Drawable API Design Document + +# Creating a Drawable + +```c +DrawableTextures *textures = drawable_get_textures(); +Drawable drawable = { + .x = 0, + .y = SCREEN_HEIGHT - 32, + .enabled = enabled, + .value.texture = textures->status_bar, + .type = DRAWABLE_TEXTURE +}; + +// Under the hood this uses the DrawableRegistry +drawable_create_drawable(&drawable, "statusbar"); +``` + +# Destroying a drawable + +```c +// Again, using the DrawableRegistry under the hood +drawable_destroy_drawable(drawable, "statusbar"); + +// All Drawables can be destroyed (such as at exit), just a wrapper +around the DrawableRegistry. +drawables_destroy_all(); +``` + +## Accessing a Drawable + +```c +// Again, wrapper around the Drawable registry +Drawable *drawable = drawable_get_drawable(drawable, "statusbar"); +*drawable->value.number = &plyr_ammo; +``` diff --git a/meson.build b/meson.build index a4b2998ccc..6c386648bf 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('mindoom', 'c', - default_options: ['default_library=static', 'buildtype=debugoptimized']) + default_options: ['default_library=static', 'buildtype=debug']) add_project_arguments('-DLOG_USE_COLOR', language: 'c') sdl2 = dependency('SDL2') @@ -38,6 +38,12 @@ dedicated_server_source_files = files( src_dir / 'net_structrw.c', ) +# Source files for the game engine +engine_src_dir = src_dir / 'engine' +engine_source_files = files ( + engine_src_dir / 'graphics' / 'widget.c', + engine_src_dir / 'graphics' / 'drawable.c', +) # Source files only used by the game binary doom_source_dir = 'doom' @@ -135,8 +141,7 @@ game_source_files = files( src_dir / doom_source_dir / 's_sound.c', src_dir / doom_source_dir / 'statdump.c', - # Code for the HUD - src_dir / doom_source_dir / 'hud' / 'widget.c', + # Code for the status bar src_dir / doom_source_dir / 'hud' / 'statusbar.c', src_dir / doom_source_dir / 'wi_stuff.c' @@ -210,6 +215,7 @@ executable('mindoom', sources: [ common_source_files, game_source_files, + engine_source_files ], link_with: [ textscreen, diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000000..85f934e667 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,3 @@ +# mindoom + +These scripts are intended to be called by Meson as run targets. Don't call them directly, instead use `meson compile scriptname` from the build directory. diff --git a/src/d_mode.h b/src/d_mode.h index 27b474938f..79a1e666f6 100644 --- a/src/d_mode.h +++ b/src/d_mode.h @@ -26,6 +26,7 @@ typedef enum { + //TODO: remove doom, // Doom 1 heretic, // Heretic hexen, // Hexen @@ -36,6 +37,7 @@ typedef enum // in: eg. shareware vs. registered. So doom1.wad and doom.wad are the // same mission, but a different mode. +// TODO: remove typedef enum { registered, // Doom registered @@ -44,6 +46,7 @@ typedef enum // What version are we emulating? +// TODO: remove typedef enum { exe_doom_1_9, // Doom 1.9: " diff --git a/src/doom/hud/statusbar.c b/src/doom/hud/statusbar.c index 6d690661b6..2357f98676 100644 --- a/src/doom/hud/statusbar.c +++ b/src/doom/hud/statusbar.c @@ -39,7 +39,8 @@ #include "../g_game.h" #include "statusbar.h" -#include "widget.h" +#include "../../engine/graphics/widget.h" +#include "../../engine/graphics/drawable.h" #include "../r_local.h" @@ -133,11 +134,6 @@ #define ST_ARMSXSPACE 12 #define ST_ARMSYSPACE 10 -// Frags pos. -#define ST_FRAGSX 138 -#define ST_FRAGSY 171 -#define ST_FRAGSWIDTH 2 - // Key icon positions. #define ST_KEY0WIDTH 8 #define ST_KEY0HEIGHT 5 @@ -202,13 +198,13 @@ #define ST_MAPTITLEY 0 #define ST_MAPHEIGHT 1 -// graphics are drawn to a backing screen and blitted to the real screen -pixel_t *st_backing_screen; +pixel_t *st_backing_screen; + -status_bar_t *status_bar; +StatusBar *status_bar; // main player in game -static player_t *plyr; +static player_t *player; // ST_Start() has just been called static boolean st_firsttime; @@ -222,14 +218,16 @@ static unsigned int st_clock; // used for making messages go away static int st_msgcounter = 0; +// TODO: unused // used when in chat -static st_chatstateenum_t st_chatstate; +//static st_chatstateenum_t st_chatstate; +// TODO: unused // whether in automap or first-person -static st_stateenum_t st_gamestate; +//static st_stateenum_t st_gamestate; // whether left-side main status bar is active -static boolean st_statusbaron; +static boolean status_bar_enabled; // whether status bar chat is active static boolean st_chat; @@ -243,11 +241,17 @@ static boolean st_cursoron; // !deathmatch static boolean st_notdeathmatch; -// !deathmatch && st_statusbaron +// !deathmatch && status_bar_enabled static boolean st_armson; -// !deathmatch -static boolean st_fragson; +// ---------------- +// Graphics patches +// ---------------- + +// TOOD: We really need to mantain an array (or something) of all the +// patches we're dealing with. Ideally a hash map with the patch name +// and it's address. There are a whopping 12 patch_t variables declared +// at the top of this file, it's ugly. // main bar left static patch_t *sbar; @@ -259,75 +263,71 @@ static patch_t *sbarr; static patch_t *tallnum[10]; // tall % sign -static patch_t *tallpercent; +static patch_t *percent_symbol_large; + +// tall % sign +static patch_t *minus_symbol; // 0-9, short, yellow (,different!) numbers static patch_t *shortnum[10]; -// 3 key-cards, 3 skulls -static patch_t *keys[NUMCARDS]; - -// face status patches -static patch_t *faces[ST_NUMFACES]; -// face background -static patch_t *faceback; +// --------- +// Drawables +// --------- -// main bar right -static patch_t *armsbg; +// This the large ammo counter for the currently selected weapon +static Drawable *large_ammo_counter; -// weapon ownership patches -static patch_t *arms[6][2]; +// Show the number of frags (kills). Used only in deathmatch +static Drawable *frag_counter; -/* This widget is the large ammo counter - * on the bottom left of the screen that - * displays the ammo count of the currently - * selected weapons. - */ -static widget_number_t *widget_ammo_current_counter; +// Show the players current health +static Drawable *health_counter; -// in deathmatch only, summary of frags stats -static widget_number_t *widget_frag_counter; +// Show the players current armor +static Drawable *armor_counter; -// health widget -static widget_number_t *widget_health; +// Smaller ammo counters for each ammo type, used on the +// right-hand side of the status bar. +static Drawable *widget_ammo_counter_bullets; +static Drawable *widget_ammo_counter_shells; +static Drawable *widget_ammo_counter_rockets; +static Drawable *widget_ammo_counter_cells; -static widget_fraction_t *widget_ammo_bullet_counter; -static widget_fraction_t *widget_ammo_shell_counter; -static widget_fraction_t *widget_ammo_rocket_counter; -static widget_fraction_t *widget_ammo_cell_counter; +// ------------------------------------- +// TODO: reduce the scope of these into +// drawable that handles drawing the +// arms (weapon inventory). +// ------------------------------------- -// arms background +// arms background widget static st_binicon_t w_armsbg; - +// arms background patch/texture +static patch_t *armsbg; +// weapon ownership patches +static patch_t *arms[6][2]; // weapon ownership widgets static st_multicon_t w_arms[6]; -// face status widget -static st_multicon_t w_faces; - -// keycard widgets -static st_multicon_t w_keyboxes[3]; - -// armor widget -static widget_number_t *widget_armor; - -// ammo widgets -static widget_number_t w_ammo[4]; +// ------------------------------------ -// max ammo widgets -static widget_number_t w_maxammo[4]; -// number of frags so far in deathmatch -static int st_fragscount; +// ---------------------------------------- +// TODO: reduce the scope of these into the +// drawable that handles drawing DOOM guy. +// ---------------------------------------- // used to use appopriately pained face static int st_oldhealth = -1; -// used for evil grin -static boolean oldweaponsowned[NUMWEAPONS]; +// face status widget +static st_multicon_t w_faces; + +// face background +static patch_t *faceback; // count until face changes static int st_facecount = 0; @@ -335,12 +335,50 @@ static int st_facecount = 0; // current face index, used by w_faces static int st_faceindex = 0; -// holds key-type for each key box on bar -static int keyboxes[3]; +// for determining if DOOM guy grins when he gets +// all weapons? +static boolean oldweaponsowned[NUMWEAPONS]; // a random number per tick static int st_randomnumber; +// face status patches +static patch_t *faces[ST_NUMFACES]; +// ------------------------------------ + + +// --------------------------------------- +// TODO: reduce the scope of this into the +// drawable that handles the keyboxes + +// Patches/textures for 3 key-cards, 3 skulls +static patch_t *keys[NUMCARDS]; + +// keycard widgets +static st_multicon_t w_keyboxes[3]; + +// holds key-type for each key box on bar +static int keyboxes[3]; +// ------------------------------------ + + +// ------------------------------------ +// TODO: reduce the scope of this into +// the drawable for the frag counter. + +// Frags pos. +#define ST_FRAGSX 138 +#define ST_FRAGSY 171 +#define ST_FRAGSWIDTH 2 + +// number of frags so far in deathmatch +static int st_fragscount; + +// !deathmatch +static boolean st_fragson; + +// ------------------------------------ + cheatseq_t cheat_mus = CHEAT("idmus", 2); cheatseq_t cheat_god = CHEAT("iddqd", 0); cheatseq_t cheat_ammo = CHEAT("idkfa", 0); @@ -367,54 +405,44 @@ cheatseq_t cheat_mypos = CHEAT("idmypos", 0); // void ST_Stop(void); -// TODO: -// pixel_t *StatusBar_CreateBackingScreen() -void *StatusBar_CreateBackground() +StatusBar *status_bar; + +StatusBar *status_bar_create(boolean *enabled) { - log_debug("StatusBar_CreateBackground(): Creating status bar background"); - // TODO: reduce the scope of st_backing_screen. - st_backing_screen = - (pixel_t *) Z_Malloc(ST_WIDTH * ST_HEIGHT * sizeof(*st_backing_screen), PU_STATIC, 0); -} + log_debug("status_bar_create(): Creating status bar"); + status_bar = Z_Malloc(sizeof(StatusBar), PU_STATIC, 0); -status_bar_t *StatusBar_CreateStatusBar(void) -{ - log_debug("StatusBar_CreateStatusBar(): Creating status bar"); - status_bar_t *status_bar = Z_Malloc(sizeof(status_bar_t), PU_STATIC, 0); - status_bar->height = 32; - status_bar->width = SCREENWIDTH; - status_bar->x = 0; - status_bar->y = SCREENHEIGHT - status_bar->height; - - //TODO: Replace - StatusBar_CreateBackground(); - // With: - //status_bar->screen = StatusBar_CreateBackingScreen() + status_bar->drawable = drawable_create(drawable); return status_bar; } -void StatusBar_DrawBackground(status_bar_t *status_bar, pixel_t *screen) +void status_bar_draw_background(StatusBar *status_bar, pixel_t *screen) { - int x = status_bar->x; - int y = status_bar->y; - int width = status_bar->width; - int height = status_bar->height; + if (status_bar->drawable == NULL || status_bar->drawable->options == NULL || &status_bar->drawable->options->x == NULL) { + log_fatal("status_bar_draw_background(): StatusBar has not been properly initialized! Can not draw."); + system_exit(); + } + + int x = status_bar->drawable->options->x; + int y = status_bar->drawable->options->y; + //int width = status_bar->width; + //int height = status_bar->height; // TODO: return early - if (st_statusbaron) + if (status_bar_enabled) { - //TODO: status_bar->screen - V_UseBuffer(screen); + + //V_UseBuffer(status_bar->drawable->screen); - V_DrawPatch(x, 0, sbar); + V_DrawPatch(x, y, sbar); // TOOD: remove (we don't care about DOOM 1.0) // draw right side of bar if needed (Doom 1.0) if (sbarr) { - V_DrawPatch(ST_ARMSBGX, 0, sbarr); + V_DrawPatch(ST_ARMSBGX, y, sbarr); } // TODO: Make the frag counter it's own widget and or function @@ -425,10 +453,9 @@ void StatusBar_DrawBackground(status_bar_t *status_bar, pixel_t *screen) V_DrawPatch(frag_widget_x, frag_widget_y, faceback); } - V_RestoreBuffer(); + //V_RestoreBuffer(); - //TODO: status_bar->screen - V_CopyRect(x, 0, screen, width, height, x, y); + //V_CopyRect(x, 0, status_bar->drawable->screen, width, height, x, y); } } @@ -440,18 +467,22 @@ boolean ST_Responder(event_t *ev) int i; // Filter automap on/off. + // TODO: stop fiddling bits if (ev->type == ev_keyup && ((ev->data1 & 0xffff0000) == AM_MSGHEADER)) { switch (ev->data1) { case AM_MSGENTERED: - st_gamestate = AutomapState; + log_debug("Entering AutoMap"); + // Unused? + //st_gamestate = AutomapState; st_firsttime = true; break; case AM_MSGEXITED: - // fprintf(stderr, "AM exited\n"); - st_gamestate = FirstPersonState; + log_debug("Exiting AutoMap"); + // Unused? + //st_gamestate = FirstPersonState; break; } } @@ -464,61 +495,61 @@ boolean ST_Responder(event_t *ev) // 'dqd' cheat for toggleable god mode if (cht_CheckCheat(&cheat_god, ev->data2)) { - plyr->cheats ^= CF_GODMODE; - if (plyr->cheats & CF_GODMODE) + player->cheats ^= CF_GODMODE; + if (player->cheats & CF_GODMODE) { - if (plyr->mo) + if (player->mo) { - plyr->mo->health = GOD_MODE_HEALTH; + player->mo->health = GOD_MODE_HEALTH; } - plyr->health = GOD_MODE_HEALTH; - plyr->message = STSTR_DQDON; + player->health = GOD_MODE_HEALTH; + player->message = STSTR_DQDON; } else { - plyr->message = STSTR_DQDOFF; + player->message = STSTR_DQDOFF; } } // 'fa' cheat for killer fucking arsenal else if (cht_CheckCheat(&cheat_ammonokey, ev->data2)) { - plyr->armorpoints = IDFA_ARMOR; - plyr->armortype = IDFA_ARMOR_CLASS; + player->armorpoints = IDFA_ARMOR; + player->armortype = IDFA_ARMOR_CLASS; for (i = 0; i < NUMWEAPONS; i++) { - plyr->weaponowned[i] = true; + player->weaponowned[i] = true; } for (i = 0; i < NUMAMMO; i++) { - plyr->ammo[i] = plyr->maxammo[i]; + player->ammo[i] = player->maxammo[i]; } - plyr->message = STSTR_FAADDED; + player->message = STSTR_FAADDED; } // 'kfa' cheat for key full ammo else if (cht_CheckCheat(&cheat_ammo, ev->data2)) { - plyr->armorpoints = IDKFA_ARMOR; - plyr->armortype = IDKFA_ARMOR_CLASS; + player->armorpoints = IDKFA_ARMOR; + player->armortype = IDKFA_ARMOR_CLASS; for (i = 0; i < NUMWEAPONS; i++) { - plyr->weaponowned[i] = true; + player->weaponowned[i] = true; } for (i = 0; i < NUMAMMO; i++) { - plyr->ammo[i] = plyr->maxammo[i]; + player->ammo[i] = player->maxammo[i]; } for (i = 0; i < NUMCARDS; i++) { - plyr->cards[i] = true; + player->cards[i] = true; } - plyr->message = STSTR_KFAADDED; + player->message = STSTR_KFAADDED; } // 'mus' cheat for changing music else if (cht_CheckCheat(&cheat_mus, ev->data2)) @@ -527,14 +558,14 @@ boolean ST_Responder(event_t *ev) char buf[3]; int musnum; - plyr->message = STSTR_MUS; + player->message = STSTR_MUS; cht_GetParam(&cheat_mus, buf); musnum = mus_e1m1 + (buf[0] - '1') * 9 + (buf[1] - '1'); if (((buf[0] - '1') * 9 + buf[1] - '1') > 31) { - plyr->message = STSTR_NOMUS; + player->message = STSTR_NOMUS; } else { @@ -545,15 +576,15 @@ boolean ST_Responder(event_t *ev) { // Noclip cheat. - plyr->cheats ^= CF_NOCLIP; + player->cheats ^= CF_NOCLIP; - if (plyr->cheats & CF_NOCLIP) + if (player->cheats & CF_NOCLIP) { - plyr->message = STSTR_NCON; + player->message = STSTR_NCON; } else { - plyr->message = STSTR_NCOFF; + player->message = STSTR_NCOFF; } } // 'behold?' power-up cheats @@ -561,34 +592,34 @@ boolean ST_Responder(event_t *ev) { if (cht_CheckCheat(&cheat_powerup[i], ev->data2)) { - if (!plyr->powers[i]) + if (!player->powers[i]) { - P_GivePower(plyr, i); + P_GivePower(player, i); } else if (i != pw_strength) { - plyr->powers[i] = 1; + player->powers[i] = 1; } else { - plyr->powers[i] = 0; + player->powers[i] = 0; } - plyr->message = STSTR_BEHOLDX; + player->message = STSTR_BEHOLDX; } } // 'behold' power-up menu if (cht_CheckCheat(&cheat_powerup[6], ev->data2)) { - plyr->message = STSTR_BEHOLD; + player->message = STSTR_BEHOLD; } // 'choppers' invulnerability & chainsaw else if (cht_CheckCheat(&cheat_choppers, ev->data2)) { - plyr->weaponowned[wp_chainsaw] = true; - plyr->powers[pw_invulnerability] = true; - plyr->message = STSTR_CHOPPERS; + player->weaponowned[wp_chainsaw] = true; + player->powers[pw_invulnerability] = true; + player->message = STSTR_CHOPPERS; } // 'mypos' for player position else if (cht_CheckCheat(&cheat_mypos, ev->data2)) @@ -601,7 +632,7 @@ boolean ST_Responder(event_t *ev) players[consoleplayer].mo->angle, players[consoleplayer].mo->x, players[consoleplayer].mo->y); - plyr->message = buf; + player->message = buf; } } @@ -636,7 +667,7 @@ boolean ST_Responder(event_t *ev) } // So be it. - plyr->message = STSTR_CLEV; + player->message = STSTR_CLEV; G_DeferedInitNew(gameskill, epsd, map); } } @@ -650,7 +681,7 @@ int ST_calcPainOffset(void) static int lastcalc; static int oldhealth = -1; - health = plyr->health > 100 ? 100 : plyr->health; + health = player->health > 100 ? 100 : player->health; if (health != oldhealth) { @@ -679,7 +710,7 @@ void ST_updateFaceWidget(void) if (priority < 10) { // dead - if (!plyr->health) + if (!player->health) { priority = 9; st_faceindex = ST_DEADFACE; @@ -689,17 +720,17 @@ void ST_updateFaceWidget(void) if (priority < 9) { - if (plyr->bonuscount) + if (player->bonuscount) { // picking up bonus doevilgrin = false; for (i = 0; i < NUMWEAPONS; i++) { - if (oldweaponsowned[i] != plyr->weaponowned[i]) + if (oldweaponsowned[i] != player->weaponowned[i]) { doevilgrin = true; - oldweaponsowned[i] = plyr->weaponowned[i]; + oldweaponsowned[i] = player->weaponowned[i]; } } if (doevilgrin) @@ -714,12 +745,12 @@ void ST_updateFaceWidget(void) if (priority < 8) { - if (plyr->damagecount && plyr->attacker && plyr->attacker != plyr->mo) + if (player->damagecount && player->attacker && player->attacker != player->mo) { // being attacked priority = 7; - if (plyr->health - st_oldhealth > ST_MUCHPAIN) + if (player->health - st_oldhealth > ST_MUCHPAIN) { st_facecount = ST_TURNCOUNT; st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; @@ -727,18 +758,18 @@ void ST_updateFaceWidget(void) else { badguyangle = - R_PointToAngle2(plyr->mo->x, plyr->mo->y, plyr->attacker->x, plyr->attacker->y); + R_PointToAngle2(player->mo->x, player->mo->y, player->attacker->x, player->attacker->y); - if (badguyangle > plyr->mo->angle) + if (badguyangle > player->mo->angle) { // whether right or left - diffang = badguyangle - plyr->mo->angle; + diffang = badguyangle - player->mo->angle; i = diffang > ANG180; } else { // whether left or right - diffang = plyr->mo->angle - badguyangle; + diffang = player->mo->angle - badguyangle; i = diffang <= ANG180; } // confusing, aint it? @@ -768,9 +799,9 @@ void ST_updateFaceWidget(void) if (priority < 7) { // getting hurt because of your own damn stupidity - if (plyr->damagecount) + if (player->damagecount) { - if (plyr->health - st_oldhealth > ST_MUCHPAIN) + if (player->health - st_oldhealth > ST_MUCHPAIN) { priority = 7; st_facecount = ST_TURNCOUNT; @@ -788,7 +819,7 @@ void ST_updateFaceWidget(void) if (priority < 6) { // rapid firing - if (plyr->attackdown) + if (player->attackdown) { if (lastattackdown == -1) { @@ -811,7 +842,7 @@ void ST_updateFaceWidget(void) if (priority < 5) { // invulnerability - if ((plyr->cheats & CF_GODMODE) || plyr->powers[pw_invulnerability]) + if ((player->cheats & CF_GODMODE) || player->powers[pw_invulnerability]) { priority = 4; @@ -831,26 +862,43 @@ void ST_updateFaceWidget(void) st_facecount--; } -void ST_updateWidgets(void) +static void update_large_ammo_counter(player_t *player, Drawable *ammo_counter) { - static int largeammo = 1994; // means "n/a" - // must redirect the pointer if the ready weapon has changed. - if (weaponinfo[plyr->readyweapon].ammo == am_noammo) + // If the player has switched to a weapon that doesn't use ammo + // (like the fists or chainsaw, set it to a special value so that + // it doesn't display. + + int ammo_type = weaponinfo[player->readyweapon].ammo; + // TODO: This is a weird legacy thing. For some reason this is how we + // tell the ammo counter not to display any ammo. We should just use the + // 'enabled' boolean instead? + + int *new_value; + + // The player is a weapon that doesn't show ammo (fists or a chainsaw) + if (ammo_type == am_noammo) { - widget_ammo_current_counter->value = &largeammo; + ammo_counter->options->enabled = false; } else { - widget_ammo_current_counter->value = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; + new_value = &player->ammo[weaponinfo[player->readyweapon].ammo]; } - widget_ammo_current_counter->data = plyr->readyweapon; + + drawable_update_number_value(ammo_counter, new_value); +} + +void ST_updateWidgets(void) +{ + + update_large_ammo_counter(player, large_ammo_counter); // update keycard multiple widgets for (int i = 0; i < 3; i++) { - keyboxes[i] = plyr->cards[i] ? i : -1; + keyboxes[i] = player->cards[i] ? i : -1; - if (plyr->cards[i + 3]) + if (player->cards[i + 3]) { keyboxes[i] = i + 3; } @@ -863,21 +911,22 @@ void ST_updateWidgets(void) st_notdeathmatch = !deathmatch; // used by w_arms[] widgets - st_armson = st_statusbaron && !deathmatch; + st_armson = status_bar_enabled && !deathmatch; - // used by widget_frag_counter widget - st_fragson = deathmatch && st_statusbaron; + // TODO: factor out into a separate frag counter update function + // used by frag_counter widget + st_fragson = deathmatch && status_bar_enabled; st_fragscount = 0; for (int i = 0; i < MAXPLAYERS; i++) { if (i != consoleplayer) { - st_fragscount += plyr->frags[i]; + st_fragscount += player->frags[i]; } else { - st_fragscount -= plyr->frags[i]; + st_fragscount -= player->frags[i]; } } @@ -894,7 +943,7 @@ void ST_Ticker(void) st_clock++; st_randomnumber = M_Random(); ST_updateWidgets(); - st_oldhealth = plyr->health; + st_oldhealth = player->health; } static int st_palette = 0; @@ -907,12 +956,12 @@ void ST_doPaletteStuff(void) int cnt; int bzc; - cnt = plyr->damagecount; + cnt = player->damagecount; - if (plyr->powers[pw_strength]) + if (player->powers[pw_strength]) { // slowly fade the berzerk out - bzc = 12 - (plyr->powers[pw_strength] >> 6); + bzc = 12 - (player->powers[pw_strength] >> 6); if (bzc > cnt) { @@ -932,9 +981,9 @@ void ST_doPaletteStuff(void) palette += STARTREDPALS; } - else if (plyr->bonuscount) + else if (player->bonuscount) { - palette = (plyr->bonuscount + 7) >> 3; + palette = (player->bonuscount + 7) >> 3; if (palette >= NUMBONUSPALS) { @@ -944,7 +993,7 @@ void ST_doPaletteStuff(void) palette += STARTBONUSPALS; } - else if (plyr->powers[pw_ironfeet] > 4 * 32 || plyr->powers[pw_ironfeet] & 8) + else if (player->powers[pw_ironfeet] > 4 * 32 || player->powers[pw_ironfeet] & 8) { palette = RADIATIONPAL; } @@ -963,51 +1012,51 @@ void ST_doPaletteStuff(void) void ST_drawWidgets(boolean refresh) { - int i; - // TODO: Why are we passing refresh all the way down to every function - // in the STWidget library? There's like one use of it. Can't we just + // in the widget library? There's like one use of it. Can't we just // return early if refresh = false? // used by w_arms[] widgets - st_armson = st_statusbaron && !deathmatch; + st_armson = status_bar_enabled && !deathmatch; - // used by widget_frag_counter widget - st_fragson = deathmatch && st_statusbaron; + // used by frag_counter widget + st_fragson = deathmatch && status_bar_enabled; - STWidget_DrawNumberWidget(widget_ammo_current_counter, st_backing_screen, refresh); + drawable_draw(large_ammo_counter, refresh); // Draw the ammo counters - // TODO: Move to separate Draw function - STWidget_DrawFractionWidget(widget_ammo_bullet_counter, st_backing_screen, refresh); - STWidget_DrawFractionWidget(widget_ammo_shell_counter, st_backing_screen, refresh); - STWidget_DrawFractionWidget(widget_ammo_rocket_counter, st_backing_screen, refresh); - STWidget_DrawFractionWidget(widget_ammo_cell_counter, st_backing_screen, refresh); - - STWidget_DrawNumberWidget(widget_health, st_backing_screen, refresh); - STWidget_DrawNumberWidget(widget_armor, st_backing_screen, refresh); - //STWidget_DrawNumberWidget(widget_frag_counter, st_backing_screen, refresh); + // TODO: Move to separate Draw function, + // and have a list of all drawables in memory to iterate over instead of + // making an explicit call for each one + drawable_draw(widget_ammo_counter_bullets, refresh); + drawable_draw(widget_ammo_counter_shells, refresh); + drawable_draw(widget_ammo_counter_rockets, refresh); + drawable_draw(widget_ammo_counter_cells, refresh); + + drawable_draw(health_counter, refresh); + drawable_draw(armor_counter, refresh); + //widget_draw_number_widget(frag_counter, st_backing_screen, refresh); STlib_updateBinIcon(&w_armsbg, refresh); - for (i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) { STlib_updateMultIcon(&w_arms[i], refresh); } STlib_updateMultIcon(&w_faces, refresh); - for (i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { STlib_updateMultIcon(&w_keyboxes[i], refresh); } } //TODO: -void StatusBar_DrawStatusBar(status_bar_t *status_bar, pixel_t *screen) +void status_bar_draw(StatusBar *status_bar, pixel_t *screen) { - StatusBar_DrawBackground(status_bar, screen); - //StatusBar_DrawWidgets(); + status_bar_draw_background(status_bar, screen); + //status_bar_DrawWidgets(); } @@ -1017,7 +1066,7 @@ void ST_doRefresh(void) st_firsttime = false; // draw status bar background to off-screen buff - StatusBar_DrawStatusBar(status_bar, st_backing_screen); + status_bar_draw(status_bar, st_backing_screen); // and refresh all widgets ST_drawWidgets(true); @@ -1032,7 +1081,7 @@ void ST_diffDraw(void) void ST_Drawer(boolean fullscreen, boolean refresh) { - st_statusbaron = (!fullscreen) || automapactive; + status_bar_enabled = (!fullscreen) || automapactive; st_firsttime = st_firsttime || refresh; // Do red-/gold-shifts from damage/items @@ -1050,52 +1099,38 @@ void ST_Drawer(boolean fullscreen, boolean refresh) } } -typedef void (*load_callback_t)(const char *lumpname, patch_t **variable); +// A callback function that can be used for loading (or unloading) graphics +// patches depending on the +typedef void (*load_or_unload_callback_t)(const char *lumpname, patch_t **variable); // Iterates through all graphics to be loaded or unloaded, along with // the variable they use, invoking the specified callback function. - -static void ST_loadUnloadGraphics(load_callback_t callback) +// The implementation of the function passed determines whether a load +// or unload operation takes place. +// +// status_bar_cache_patches(load_patch); +static void status_bar_cache_patches(load_or_unload_callback_t load_unload_function) { - int i; - int j; - int facenum; - char namebuf[9]; - // Load the numbers, tall and short - for (i = 0; i < 10; i++) - { - M_snprintf(namebuf, 9, "STTNUM%d", i); - callback(namebuf, &tallnum[i]); - - M_snprintf(namebuf, 9, "STYSNUM%d", i); - callback(namebuf, &shortnum[i]); - } - - // Load percent key. - //Note: why not load STMINUS here, too? - - callback("STTPRCNT", &tallpercent); - - // key cards - for (i = 0; i < NUMCARDS; i++) + // Load the key card textures + for (int i = 0; i < NUMCARDS; i++) { M_snprintf(namebuf, 9, "STKEYS%d", i); - callback(namebuf, &keys[i]); + load_unload_function(namebuf, &keys[i]); } // arms background - callback("STARMS", &armsbg); + load_unload_function("STARMS", &armsbg); // arms ownership widgets - for (i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) { M_snprintf(namebuf, 9, "STGNUM%d", i + 2); // gray # - callback(namebuf, &arms[i][0]); + load_unload_function(namebuf, &arms[i][0]); // yellow # arms[i][1] = shortnum[i + 2]; @@ -1103,71 +1138,66 @@ static void ST_loadUnloadGraphics(load_callback_t callback) // face backgrounds for different color players M_snprintf(namebuf, 9, "STFB%d", consoleplayer); - callback(namebuf, &faceback); - - // status bar background bits - if (W_CheckNumForName("STBAR") >= 0) - { - callback("STBAR", &sbar); - sbarr = NULL; - } - else - { - callback("STMBARL", &sbar); - callback("STMBARR", &sbarr); - } + load_unload_function(namebuf, &faceback); + // TODO: Make a separate function // face states - facenum = 0; - for (i = 0; i < ST_NUMPAINFACES; i++) + int facenum = 0; + for (int i = 0; i < ST_NUMPAINFACES; i++) { - for (j = 0; j < ST_NUMSTRAIGHTFACES; j++) + for (int j = 0; j < ST_NUMSTRAIGHTFACES; j++) { M_snprintf(namebuf, 9, "STFST%d%d", i, j); - callback(namebuf, &faces[facenum]); + load_unload_function(namebuf, &faces[facenum]); ++facenum; } M_snprintf(namebuf, 9, "STFTR%d0", i); // turn right - callback(namebuf, &faces[facenum]); + load_unload_function(namebuf, &faces[facenum]); ++facenum; M_snprintf(namebuf, 9, "STFTL%d0", i); // turn left - callback(namebuf, &faces[facenum]); + load_unload_function(namebuf, &faces[facenum]); ++facenum; M_snprintf(namebuf, 9, "STFOUCH%d", i); // ouch! - callback(namebuf, &faces[facenum]); + load_unload_function(namebuf, &faces[facenum]); ++facenum; M_snprintf(namebuf, 9, "STFEVL%d", i); // evil grin ;) - callback(namebuf, &faces[facenum]); + load_unload_function(namebuf, &faces[facenum]); ++facenum; M_snprintf(namebuf, 9, "STFKILL%d", i); // pissed off - callback(namebuf, &faces[facenum]); + load_unload_function(namebuf, &faces[facenum]); ++facenum; } - callback("STFGOD0", &faces[facenum]); + load_unload_function("STFGOD0", &faces[facenum]); ++facenum; - callback("STFDEAD0", &faces[facenum]); + load_unload_function("STFDEAD0", &faces[facenum]); ++facenum; } -static void ST_loadCallback(const char *lumpname, patch_t **variable) +// Cache a lump name containing a particular patch (texture) that +// is required for the status bar. +const static void load_patch(const char *lumpname, patch_t **variable) { *variable = W_CacheLumpName(lumpname, PU_STATIC); } void ST_initData(void) { + drawable_init(); + int i; st_firsttime = true; - plyr = &players[consoleplayer]; + player = &players[consoleplayer]; st_clock = 0; - st_chatstate = StartChatState; - st_gamestate = FirstPersonState; + // TODO: Unused? + //st_chatstate = StartChatState; + // TODO: Unused? + //st_gamestate = FirstPersonState; - st_statusbaron = true; + status_bar_enabled = true; st_oldchat = st_chat = false; st_cursoron = false; @@ -1178,7 +1208,7 @@ void ST_initData(void) for (i = 0; i < NUMWEAPONS; i++) { - oldweaponsowned[i] = plyr->weaponowned[i]; + oldweaponsowned[i] = player->weaponowned[i]; } for (i = 0; i < 3; i++) @@ -1193,102 +1223,130 @@ void ST_initData(void) * Create the statusbar widget for displaying the ammo count * for the currently selected weapon. */ -void StatusBar_CreateCurrentAmmoCountWidget() +static void create_large_ammo_counter(player_t *player) { - log_trace("StatusBar_CreateCurrentAmmoCountWidget(): Creating ammo counter"); - - const int x = 44; - const int y = 171; - const int num_digits = 3; - - int *value = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; - - widget_ammo_current_counter = - STWidget_CreateNumberWidget(x, y, num_digits, value, &st_statusbaron, tallnum, NULL); - - // the last weapon type - widget_ammo_current_counter->data = plyr->readyweapon; + log_trace("create_large_ammo_counter(): Creating ammo counter"); + + int *value = &player->ammo[weaponinfo[player->readyweapon].ammo]; + + DrawableOptions *options = Z_Malloc(sizeof(DrawableOptions), PU_STATIC, 0); + + *options = (DrawableOptions) { + .x = 44, + .y = 171, + .digits = 3, + .use_large_font = true, + .show_percent_sign = false + }; + + large_ammo_counter = drawable_create_number(value, options, &status_bar_enabled); } -void StatusBar_CreateAmmoCounterWidgets() +static void create_ammo_counters(player_t *player) { - const int x = 288; + // Create the large ammo counter that shows the ammunition of the current + // weapon. + create_large_ammo_counter(player); + + // Create the four smaller ammo counters that are specific to the ammunition + // type. + //const int X_POS = 288; + + /** // The ammo counter for "BULL" - widget_ammo_bullet_counter = STWidget_CreateFractionWidget( - x, 173, &plyr->ammo[0], &plyr->maxammo[0], &st_statusbaron, shortnum); + widget_ammo_counter_bullets = widget_create_fraction_widget( + X_POS, 173, &player->ammo[0], &player->maxammo[0], &status_bar_enabled, shortnum); // The ammo counter for "SHELL" - widget_ammo_shell_counter = STWidget_CreateFractionWidget( - x, 179, &plyr->ammo[1], &plyr->maxammo[1], &st_statusbaron, shortnum); + widget_ammo_counter_shells = widget_create_fraction_widget( + X_POS, 179, &player->ammo[1], &player->maxammo[1], &status_bar_enabled, shortnum); // The ammo counter for "RCKT" - widget_ammo_rocket_counter = STWidget_CreateFractionWidget( - x, 191, &plyr->ammo[2], &plyr->maxammo[2], &st_statusbaron, shortnum); + widget_ammo_counter_rockets = widget_create_fraction_widget( + X_POS, 191, &player->ammo[2], &player->maxammo[2], &status_bar_enabled, shortnum); // The ammo counter for "CELL" - widget_ammo_cell_counter = STWidget_CreateFractionWidget( - x, 185, &plyr->ammo[3], &plyr->maxammo[3], &st_statusbaron, shortnum); + widget_ammo_counter_cells = widget_create_fraction_widget( + X_POS, 185, &player->ammo[3], &player->maxammo[3], &status_bar_enabled, shortnum); + **/ } /** * Create the statusbar widget for displaying health */ -void StatusBar_CreateHealthWidget() +static void create_health_counter(player_t *player) { - log_trace("StatusBar_CreateHealthWidget(): Creating health percentage"); - - const int num_digits = 3; - const int x = 90; - const int y = 171; - - widget_health = STWidget_CreateNumberWidget( - x, y, num_digits, &plyr->health, &st_statusbaron, tallnum, tallpercent); + log_trace("create_health_counter(): Creating health percentage"); + + DrawableOptions *options = Z_Malloc(sizeof(DrawableOptions), PU_STATIC, 0); + + *options = (DrawableOptions) { + .x = 90, + .y = 171, + .digits = 3, + .use_large_font = true, + .show_percent_sign = false + }; + + health_counter = drawable_create_number( + &player->health, options, &status_bar_enabled); } /** * Create the statusbar widget for displaying armor * Original comment: "should be colored later" */ -void StatusBar_CreateArmorWidget() +void create_armor_counter(player_t *player) { - log_trace("StatusBar_CreateArmorWidget(): Creating armor percentage"); - - const int num_digits = 3; - const int x = 221; - const int y = 171; - - widget_armor = STWidget_CreateNumberWidget( - x, y, num_digits, &plyr->armorpoints, &st_statusbaron, tallnum, tallpercent); + log_trace("create_armor_counter(): Creating armor percentage"); + + int *value = &player->armorpoints; + + DrawableOptions *options = Z_Malloc(sizeof(DrawableOptions), PU_STATIC, 0); + + *options = (DrawableOptions) { + .digits = 3, + .x = 221, + .y = 171, + .use_large_font = true, + .show_percent_sign = false + }; + + armor_counter = drawable_create_number( + value, options, &status_bar_enabled); } /** * Create the statusbar widget for showing frag counts. */ -void StatusBar_CreateFragCounterWidget() +static void create_frag_counter_widget() { - log_trace("StatusBar_CreateFragCounterWidget(): Creating frag counter widget"); + log_trace("create_frag_counter_widget(): Creating frag counter widget"); + + DrawableOptions *options = Z_Malloc(sizeof(DrawableOptions), PU_STATIC, 0); + + *options = (DrawableOptions) { + .digits = 2, + .x = 138, + .y = 171, + .use_large_font = true, + .show_percent_sign = false + }; - const int num_digits = 2; - const int x = 138; - const int y = 171; - widget_frag_counter = STWidget_CreateNumberWidget( - x, y, num_digits, &st_fragscount, &st_fragson, tallnum, NULL); + frag_counter = drawable_create_number(&st_fragscount, options, &st_fragson); } -void ST_createWidgets(void) +void ST_createWidgets(player_t *player) { - - StatusBar_CreateCurrentAmmoCountWidget(); - StatusBar_CreateHealthWidget(); - StatusBar_CreateArmorWidget(); - StatusBar_CreateAmmoCounterWidgets(); - //StatusBar_CreateFragCounterWidget(); - + + create_health_counter(player); + create_armor_counter(player); + create_ammo_counters(player); // arms background STlib_initBinIcon( - &w_armsbg, ST_ARMSBGX, ST_ARMSBGY, armsbg, &st_notdeathmatch, &st_statusbaron); + &w_armsbg, ST_ARMSBGX, ST_ARMSBGY, armsbg, &st_notdeathmatch, &status_bar_enabled); // weapons owned for (int i = 0; i < 6; i++) @@ -1298,19 +1356,19 @@ void ST_createWidgets(void) ST_ARMSX + (i % 3) * ST_ARMSXSPACE, ST_ARMSY + (i / 3) * ST_ARMSYSPACE, arms[i], - &plyr->weaponowned[i + 1], + &player->weaponowned[i + 1], &st_armson); } // faces - STlib_initMultIcon(&w_faces, ST_FACESX, ST_FACESY, faces, &st_faceindex, &st_statusbaron); + STlib_initMultIcon(&w_faces, ST_FACESX, ST_FACESY, faces, &st_faceindex, &status_bar_enabled); // keyboxes 0-2 - STlib_initMultIcon(&w_keyboxes[0], ST_KEY0X, ST_KEY0Y, keys, &keyboxes[0], &st_statusbaron); + STlib_initMultIcon(&w_keyboxes[0], ST_KEY0X, ST_KEY0Y, keys, &keyboxes[0], &status_bar_enabled); - STlib_initMultIcon(&w_keyboxes[1], ST_KEY1X, ST_KEY1Y, keys, &keyboxes[1], &st_statusbaron); + STlib_initMultIcon(&w_keyboxes[1], ST_KEY1X, ST_KEY1Y, keys, &keyboxes[1], &status_bar_enabled); - STlib_initMultIcon(&w_keyboxes[2], ST_KEY2X, ST_KEY2Y, keys, &keyboxes[2], &st_statusbaron); + STlib_initMultIcon(&w_keyboxes[2], ST_KEY2X, ST_KEY2Y, keys, &keyboxes[2], &status_bar_enabled); } static boolean st_stopped = true; @@ -1318,15 +1376,15 @@ static boolean st_stopped = true; void ST_Start(void) { - + if (!st_stopped) { ST_Stop(); } - + st_backing_screen = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*st_backing_screen), PU_STATIC, 0); ST_initData(); - status_bar = StatusBar_CreateStatusBar(); - ST_createWidgets(); + status_bar = status_bar_create(&status_bar_enabled); + ST_createWidgets(player); st_stopped = false; } @@ -1336,8 +1394,6 @@ void ST_Stop(void) { return; } - // TOOD: When we replace this function, it should free() all widgets - // and perhaps by called via atexit I_SetPalette(W_CacheLumpNum(lu_palette, PU_CACHE)); st_stopped = true; @@ -1346,5 +1402,9 @@ void ST_Stop(void) void ST_Init(void) { lu_palette = W_GetNumForName("PLAYPAL"); - ST_loadUnloadGraphics(ST_loadCallback); + + // Run through the list of patches, loading each one with the "load_patch" function. + // Note: It's implied here that if there's a "load" function, there is an "unload" + // function. There isn't. But I guess the flexibility is there to have one. + status_bar_cache_patches(load_patch); } diff --git a/src/doom/hud/statusbar.h b/src/doom/hud/statusbar.h index 9367b02980..e833fe1e58 100644 --- a/src/doom/hud/statusbar.h +++ b/src/doom/hud/statusbar.h @@ -26,13 +26,14 @@ #include "d_event.h" #include "m_cheat.h" +#include "../../engine/graphics/drawable.h" + // Size of statusbar. // Now sensitive for scaling. #define ST_HEIGHT 32 #define ST_WIDTH SCREENWIDTH #define ST_Y (SCREENHEIGHT - ST_HEIGHT) - // // STATUS BAR // @@ -52,17 +53,27 @@ void ST_Start(void); // Called by startup code. void ST_Init(void); +/** Data structure representing the current state of the status bar **/ +typedef struct { + /** Whether the status bar has been loaded for the first time. **/ + boolean first_load; + /** Counter for the number of messages shown **/ + int message_counter; + /** Whether the cursor is enabled. TOOD: used for what? **/ + boolean cursor_on; +} StatusBarState; + +/** Data structure representing the status bar **/ typedef struct { - int height; - int width; - int x; - int y; - pixel_t *backing_screen; - -} status_bar_t; + /** Drawable for the status bar**/ + Drawable *drawable; + /** State of the status bar**/ + StatusBarState state; +} StatusBar; +//TODO: unused? // States for status bar code. typedef enum { diff --git a/src/doom/hud/widget.c b/src/doom/hud/widget.c deleted file mode 100644 index 0db35beb6e..0000000000 --- a/src/doom/hud/widget.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Copyright(C) 1993-1996 Id Software, Inc. - * Copyright(C) 2005-2014 Simon Howard - * Copyright(C) 2023 Joshua Murphy - * - * 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. - * - * DESCRIPTION: - * Status bar widget code - * - * This file was previously named "st_lib.c" - */ - -#include -#include - -#include "../doomdef.h" - -#include "../../log.h" - -#include "../../z_zone.h" -#include "../../v_video.h" - -#include "../../i_swap.h" -#include "../../i_system.h" - -#include "../../w_wad.h" - -#include "statusbar.h" -#include "widget.h" -#include "../r_local.h" - - -// -// Hack display negative frags. -// Loads and store the stminus lump. -// -patch_t *sttminus; - -void STlib_init(void) -{ - if (W_CheckNumForName("STTMINUS") >= 0) - { - sttminus = (patch_t *) W_CacheLumpName("STTMINUS", PU_STATIC); - } - else - { - sttminus = NULL; - } -} - -/** - * @brief Creates a new status bar widget for displaying a number - * @param x The x position of the new widget - * @param y The y position of the new widget - * @param numdigits The amount of numbers that can be displayed (i.e width=3 for a 3 digit number like health or ammo.) - * @param value The number to be displayed by the widget - * @param enabled Whether the widget is enabled (and thus drawn) - * @param patches List of graphics patches to be used as the drawing font - * @param percent_sign_patch The graphics patch for the percent sign. Use `NULL` for creating number widgets without a percent sign. - * @return The newly-created widget. - * - * **Note**: The return value must be freed after use. - * - */ -widget_number_t *STWidget_CreateNumberWidget( - int x, - int y, - int num_digits, - int *value, - boolean *enabled, - patch_t **patches, - patch_t *percent_sign_patch) -{ - log_debug( - "STWidget_CreateNumberWidget(): Creating a number widget at (%d,%d) of width %dpx, enabled " - "%s", - x, - y, - num_digits, - btoa(enabled)); - - widget_number_t *widget = Z_Malloc(sizeof(widget_number_t), PU_LEVEL, 0); - - widget->x = x; - widget->y = y; - widget->oldnum = 0; - widget->num_digits = num_digits; - widget->value = value; - widget->enabled = enabled; - widget->patches = patches; - widget->percent_sign_patch = percent_sign_patch; - - return widget; -} - -/** - * \deprecated Use STWidget_CreateNumberWidget() - */ -void STlib_initNum(widget_number_t *n, int x, int y, patch_t **pl, int *num, boolean *on, int width) -{ - n->x = x; - n->y = y; - n->oldnum = 0; - n->num_digits = width; - n->value = num; - n->enabled = on; - n->patches = pl; -} - -/** - * Creates a new fractional number widget for the status bar, useful for the small ammo counters. - * - * Four of these are used in DOOM, one each for "BULL", "SHELL", "RCKT", and "CELL". In DOOM, the - * "/" character used by the fractional ammo counter is actually baked into the background status - * bar texture. This means that a FractionWidget actually only consists of two regular - * NumberWidgets, spaced apart by a fixed distance, using the correct spacing to give - * the appearance of "x / y" on screen. Because we're actually creating two widgets under - * the hood, this can be considered a "composite" widget. -* - * @param x The x position of the widget on screen - * @param y The y position of the widget on screen - * @param numerator The numerator (first half) of the fraction - * @param denominator The denominator (second half) of the fraction - * @param enabled Whether or not the widget is enabled (and thus drawn). - * @param patches A list of patches containing a texture for each number. - * - * @return The newly-created FractionWidget - */ -widget_fraction_t *STWidget_CreateFractionWidget( - int x, int y, int *numerator_value, int *denominator_value, boolean *enabled, patch_t **patches) -{ - log_debug( - "STWidget_CreateFractionWidget(): Creating a fraction widget at (%d,%d), enabled " - "%s", - x, - y, - btoa(enabled)); - - // Numbers on either side of the fraction are a maximum of 3 digits wide. - int num_digits = 3; - // Fixed spacing of 26 pixels between the components of the fraction. - int spacing = 26; - - widget_fraction_t *widget = Z_Malloc(sizeof(widget_fraction_t), PU_LEVEL, 0); - - widget->numerator = - STWidget_CreateNumberWidget(x, y, num_digits, numerator_value, enabled, patches, NULL); - widget->denominator = STWidget_CreateNumberWidget( - x + spacing, y, num_digits, denominator_value, enabled, patches, NULL); - - return widget; -} - -void STWidget_DrawFractionWidget(widget_fraction_t *widget, pixel_t *screen, boolean refresh) -{ - STWidget_DrawNumberWidget(widget->numerator, screen, refresh); - STWidget_DrawNumberWidget(widget->denominator, screen, refresh); -} - -/** - * @brief Draws a number widget to the status bar. - * - */ -void STWidget_DrawNumberWidget(widget_number_t *widget, pixel_t *screen, boolean refresh) -{ - if (!widget) - { - log_fatal("STWidget_DrawNumberWidget(): Widget is NULL! Can't draw a NULL widget. Are you " - "trying to draw a widget that doesn't exist?"); - System_Exit(); - } - - // Don't draw widgets that have been turned off - if (!widget->enabled) - { - log_debug("STWidget_DrawNumberWidget(): Widget %x not enabled, so not drawing", widget); - return; - } - - // Draw the percentage sign if we've been given a graphic patch for it - if (refresh && widget->percent_sign_patch != NULL) - { - V_DrawPatch(widget->x, widget->y, widget->percent_sign_patch); - } - - // A fairly efficient way to draw a number - // based on differences from the old number. - // Note: worth the trouble? - // - // TODO: refactor this heavily - - int numdigits = widget->num_digits; - int num = *widget->value; - - int w = SHORT(widget->patches[0]->width); - int h = SHORT(widget->patches[0]->height); - int x = widget->x; - - int neg; - - widget->oldnum = *widget->value; - - neg = num < 0; - - if (neg) - { - if (numdigits == 2 && num < -9) - { - num = -9; - } - else if (numdigits == 3 && num < -99) - { - num = -99; - } - - num = -num; - } - - // clear the area - x = widget->x - numdigits * w; - - if (widget->y - ST_Y < 0) - { - I_Error("drawNum: widget->y - ST_Y < 0"); - } - - V_CopyRect(x, widget->y - ST_Y, screen, w * numdigits, h, x, widget->y); - - // if non-number, do not draw it - if (num == 1994) - { - return; - } - - x = widget->x; - - // in the special case of 0, you draw 0 - if (!num) - { - V_DrawPatch(x - w, widget->y, widget->patches[0]); - } - - // draw the new number - while (num && numdigits--) - { - x -= w; - V_DrawPatch(x, widget->y, widget->patches[num % 10]); - num /= 10; - } - - // draw a minus sign if necessary - if (neg && sttminus) - { - V_DrawPatch(x - 8, widget->y, sttminus); - } -} - -// -// A fairly efficient way to draw a number -// based on differences from the old number. -// Note: worth the trouble? -// -/** - * \deprecated Use STWidget_DrawNumberWidget() - */ -void STlib_drawNum(widget_number_t *n, boolean refresh) -{ - - int numdigits = n->num_digits; - int num = *n->value; - - int w = SHORT(n->patches[0]->width); - int h = SHORT(n->patches[0]->height); - int x = n->x; - - int neg; - - n->oldnum = *n->value; - - neg = num < 0; - - if (neg) - { - if (numdigits == 2 && num < -9) - { - num = -9; - } - else if (numdigits == 3 && num < -99) - { - num = -99; - } - - num = -num; - } - - // clear the area - x = n->x - numdigits * w; - - if (n->y - ST_Y < 0) - { - I_Error("drawNum: n->y - ST_Y < 0"); - } - - V_CopyRect(x, n->y - ST_Y, st_backing_screen, w * numdigits, h, x, n->y); - - // if non-number, do not draw it - if (num == 1994) - { - return; - } - - x = n->x; - - // in the special case of 0, you draw 0 - if (!num) - { - V_DrawPatch(x - w, n->y, n->patches[0]); - } - - // draw the new number - while (num && numdigits--) - { - x -= w; - V_DrawPatch(x, n->y, n->patches[num % 10]); - num /= 10; - } - - // draw a minus sign if necessary - if (neg && sttminus) - { - V_DrawPatch(x - 8, n->y, sttminus); - } -} - -/** - * \deprecated Use STWidget_DrawNumberWidget() - */ -void STlib_updateNum(widget_number_t *n, boolean refresh) -{ - if (*n->enabled) - { - STlib_drawNum(n, refresh); - } -} - -// TODO: To be replaced by a new function called STWidget_CreateMultiIconWidget() -void STlib_initMultIcon(st_multicon_t *i, int x, int y, patch_t **il, int *inum, boolean *on) -{ - i->x = x; - i->y = y; - i->oldinum = -1; - i->inum = inum; - i->on = on; - i->p = il; -} - - -// TODO: To be replaced by a new function called STWidget_UpdateMultiIconWidget() -void STlib_updateMultIcon(st_multicon_t *mi, boolean refresh) -{ - int w; - int h; - int x; - int y; - - if (*mi->on && (mi->oldinum != *mi->inum || refresh) && (*mi->inum != -1)) - { - if (mi->oldinum != -1) - { - x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset); - y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset); - w = SHORT(mi->p[mi->oldinum]->width); - h = SHORT(mi->p[mi->oldinum]->height); - - if (y - ST_Y < 0) - { - I_Error("updateMultIcon: y - ST_Y < 0"); - } - V_CopyRect(x, y - ST_Y, st_backing_screen, w, h, x, y); - } - V_DrawPatch(mi->x, mi->y, mi->p[*mi->inum]); - mi->oldinum = *mi->inum; - } -} - -// TODO: To be replaced by a new function called STWidget_CreateBinaryIconWidget() -void STlib_initBinIcon(st_binicon_t *b, int x, int y, patch_t *i, boolean *val, boolean *on) -{ - b->x = x; - b->y = y; - b->oldval = false; - b->val = val; - b->on = on; - b->p = i; -} - -// TODO: To be replaced by a new function called STWidget_UpdateBinaryIconWidget() -void STlib_updateBinIcon(st_binicon_t *bi, boolean refresh) -{ - int x; - int y; - int w; - int h; - - if (*bi->on && (bi->oldval != *bi->val || refresh)) - { - x = bi->x - SHORT(bi->p->leftoffset); - y = bi->y - SHORT(bi->p->topoffset); - w = SHORT(bi->p->width); - h = SHORT(bi->p->height); - - if (y - ST_Y < 0) - { - I_Error("updateBinIcon: y - ST_Y < 0"); - } - if (*bi->val) - { - V_DrawPatch(bi->x, bi->y, bi->p); - } - else - { - V_CopyRect(x, y - ST_Y, st_backing_screen, w, h, x, y); - } - bi->oldval = *bi->val; - } -} diff --git a/src/doom/hud/widget.h b/src/doom/hud/widget.h deleted file mode 100644 index b6e7b0c862..0000000000 --- a/src/doom/hud/widget.h +++ /dev/null @@ -1,185 +0,0 @@ -// -// Copyright(C) 1993-1996 Id Software, Inc. -// Copyright(C) 2005-2014 Simon Howard -// -// 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. -// -// DESCRIPTION: -// The status bar widget code. -// - -#ifndef __STLIB__ -#define __STLIB__ - - -// We are referring to patches. -#include "../r_defs.h" - -// -// Typedefs of widgets -// - -// Number widget - -typedef struct -{ - // upper right-hand corner - // of the number (right-justified) - int x; - int y; - - // max # of digits in number - int num_digits; - - // last number value - int oldnum; - - // pointer to current value - int *value; - - // pointer to boolean stating - // whether to update number - boolean *enabled; - - // list of patches for 0-9 - patch_t **patches; - - // user data - int data; - - // percent sign graphics patch (used for drawing numbers with a percent sign) - // or NULL for regular numbers - patch_t *percent_sign_patch; - -} widget_number_t; - -typedef struct -{ - widget_number_t *numerator; - widget_number_t *denominator; -} widget_fraction_t; - - -/** - * \deprecated Use widget_percent_t - */ -// Percent widget ("child" of number widget, -// or, more precisely, contains a number widget.) -typedef struct -{ - // number information - widget_number_t n; - - // percent sign graphic - patch_t *p; - -} st_percent_t; - - -// Multiple Icon widget -typedef struct -{ - // center-justified location of icons - int x; - int y; - - // last icon number - int oldinum; - - // pointer to current icon - int *inum; - - // pointer to boolean stating - // whether to update icon - boolean *on; - - // list of icons - patch_t **p; - - // user data - int data; - -} st_multicon_t; - - -// Binary Icon widget - -typedef struct -{ - // center-justified location of icon - int x; - int y; - - // last icon value - boolean oldval; - - // pointer to current icon status - boolean *val; - - // pointer to boolean - // stating whether to update icon - boolean *on; - - - patch_t *p; // icon - int data; // user data - -} st_binicon_t; - - -// -// Widget creation, access, and update routines -// - -// Initializes widget library. -// More precisely, initialize STMINUS, -// everything else is done somewhere else. -// -void STlib_init(void); - - -widget_number_t *STWidget_CreateNumberWidget( - int x, - int y, - int num_digits, - int *value, - boolean *enabled, - patch_t **patches, - patch_t *percent_sign_patch); -widget_fraction_t *STWidget_CreateFractionWidget( - int x, - int y, - int *numerator_value, - int *denominator_value, - boolean *enabled, - patch_t **patches); - -void STWidget_DrawNumberWidget(widget_number_t *widget, pixel_t *screen, boolean refresh); -void STWidget_DrawFractionWidget(widget_fraction_t *widget, pixel_t *screen, boolean refresh); - -void STlib_initNum( - widget_number_t *n, int x, int y, patch_t **pl, int *num, boolean *on, int width); - -void STlib_updateNum(widget_number_t *n, boolean refresh); - -// Multiple Icon widget routines -void STlib_initMultIcon(st_multicon_t *mi, int x, int y, patch_t **il, int *inum, boolean *on); - - -void STlib_updateMultIcon(st_multicon_t *mi, boolean refresh); - -// Binary Icon widget routines - -void STlib_initBinIcon(st_binicon_t *b, int x, int y, patch_t *i, boolean *val, boolean *on); - -void STlib_updateBinIcon(st_binicon_t *bi, boolean refresh); - -#endif diff --git a/src/engine/graphics/drawable.c b/src/engine/graphics/drawable.c new file mode 100644 index 0000000000..4fceafbe61 --- /dev/null +++ b/src/engine/graphics/drawable.c @@ -0,0 +1,308 @@ +/* + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2023 Joshua Murphy + * + * 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. + * + * drawable.c +*/ + +#include "../../log.h" +// Zone memory allocator +#include "../../z_zone.h" +// System functions (exit) +#include "../../i_system.h" +// Drawing code +#include "../../v_video.h" +// Endian-safe conversions +#include "../../i_swap.h" +// Safe string functions +#include "../../m_misc.h" +// We are referring to patches. +#include "../../doom/r_defs.h" +// Working with WAD data +#include "../../w_wad.h" + +// Using boolean implementation +#include "../../doomtype.h" + +#include "drawable.h" + +// Module-wide resources accessible to all Drawables. Should not be accessed directly, but by using +// API functions. Contains pointers to all textures a Drawable may use. +static DrawableTextures *textures; + +pixel_t *screen; + +DrawableTextures *drawable_get_textures() { + return textures; +} + +static pixel_t *create_backing_screen() { + // TODO: Avoid use of these SCREENWIDTH macros + log_debug("drawable_create_backing_screen(): Creating backing screen for Drawable rendering."); + screen = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*screen), PU_STATIC, 0); + + return screen; +} + +static void load_texture(patch_t *texture, const char *name) { + if (W_CheckNumForName(name) >= 0) + { + texture = W_CacheLumpName(name, PU_STATIC); + } + else + { + log_fatal("Could not load texture \"%s\" from the WAD.", name); + system_exit(); + } +} + +static DrawableTextures *load_textures() { + //TODO: I believe that with the amount of patches in the WAD, this + //approach of manually assigned each patch to a variable will not scale. + //Sure that's how it was done before I started rearchitecting the code, + //but it's just not ideal. + + textures = Z_Malloc(sizeof(DrawableTextures), PU_STATIC, 0); + + const int BUFFER_SIZE = 9; + char patch_name_buffer[BUFFER_SIZE]; + + + // Load font numbers, both large and small fonts. + for (int i = 0; i < BUFFER_SIZE + 1; i++) + { + M_snprintf(patch_name_buffer, BUFFER_SIZE, "STTNUM%d", i); + load_texture(textures->large_numbers[i], patch_name_buffer); + + M_snprintf(patch_name_buffer, BUFFER_SIZE, "STYSNUM%d", i); + load_texture(textures->small_numbers[i], patch_name_buffer); + } + + // Load font special characters. + load_texture(textures->large_minus_sign, "STTMINUS"); + load_texture(textures->large_percent_sign, "STTPRCNT"); + + load_texture(textures->status_bar, "STBAR"); + + return textures; +} + +/** Initialize the Drawable API **/ +void drawable_init() { + textures = load_textures(); + screen = create_backing_screen(); +} + + +void drawable_draw(const Drawable *drawable, boolean refresh) { + switch (drawable->type) { + case DRAWABLE_NUMBER: + drawable_draw_number(drawable, refresh); + break; + // TODO: Add more drawable types here + default: + printf("Unsupported data type.\n"); + } +} + +Drawable *drawable_create(Drawable drawable) +{ + log_trace("drawable_create(): Creating Drawable"); + + if (drawable == NULL) + { + log_fatal("drawable_create(): Can't create a Drawable with NULL data parameter."); + system_exit(); + } + + Drawable *drawable = Z_Malloc(sizeof(Drawable), PU_LEVEL, 0); + + if (drawable == NULL) + { + log_fatal("drawable_create(): Memory allocation failed."); + system_exit(); + } + + //drawable->options = options; + //drawable->options->enabled = enabled; + + switch(data->type) + { + case(DRAWABLE_NUMBER): + break; + case(DRAWABLE_TEXTURE): + break; + } + + + if (texture == NULL) + { + log_fatal("drawable_create(): Can not create a drawable with a NULL texture."); + system_exit(); + } + + return drawable; +} + +Drawable *drawable_create_number( + int *value, + DrawableOptions *options, + boolean *enabled + ) +{ + log_debug( + "drawable_create_number(): Creating a number drawable at (%d,%d) %d digits wide, enabled state: " + "%s", + options->x, + options->y, + options->digits, + btoa(&enabled)); + + Drawable *drawable = Z_Malloc(sizeof(Drawable), PU_LEVEL, 0); + + if (drawable == NULL) + { + log_fatal("drawable_create_number(): Memory allocation failed."); + system_exit(); + } + + drawable->options = options; + + drawable->type = DRAWABLE_NUMBER; + drawable->options->enabled = enabled; + drawable->value.number = value; + + return drawable; +} + +void drawable_draw_number(const Drawable *drawable, boolean refresh) +{ + if (!drawable) + { + log_fatal("drawable_draw_number(): Can't draw a NULL drawable. Are you " + "trying to draw a drawable that doesn't exist?"); + system_exit(); + } + + // Don't draw drawables that have been turned off + if (!drawable->options->enabled) + { + log_debug("drawable_draw_number(): drawable %x not enabled, so not drawing", drawable); + return; + } + + // if non-number, do not draw it + int num = *drawable->value.number; + if (num == 1994) + { + return; + } + + patch_t **font; + + patch_t *percent_sign = textures->large_percent_sign; + patch_t *minus_sign = textures->large_minus_sign; + + // Some things can only be drawn with large fonts due to the textures available + // in the WAD. + if (drawable->options->use_large_font) + { + font = textures->large_numbers; + + // Don't draw these textures unless a refresh is happening. + if (refresh) + { + // Show a percent sign if we've been asked to. + if (drawable->options->show_percent_sign) + { + V_DrawPatch(drawable->options->x, drawable->options->y, percent_sign); + } + + // Show a minus sign if the number to be drawn is negative, and if + // we've been asked to. + if (drawable->value.number < 0) + { + V_DrawPatch(drawable->options->x - 8, drawable->options->y, minus_sign); + } + } + } + else + { + font = textures->small_numbers; + } + + + // A fairly efficient way to draw a number + // based on differences from the old number. + // Note: worth the trouble? + // + // TODO: refactor this heavily + + int numdigits = drawable->options->digits; + + int w = SHORT(font[0]->width); + int h = SHORT(font[0]->height); + int x = drawable->options->x; + + if (drawable->value.number) + { + if (numdigits == 2 && num < -9) + { + num = -9; + } + else if (numdigits == 3 && num < -99) + { + num = -99; + } + + num = -num; + } + + // clear the area + x = drawable->options->x - numdigits * w; + + // Good luck trying to draw off the screen. + // TODO: Remove dependence on global ST_Y macro. + // What if we don't want to draw on the status bar? + + /** + if (drawable->y - ST_Y < 0) + { + log_fatal("drawable_draw_number(): drawable->y - ST_Y < 0"); + system_exit() + } **/ + + V_CopyRect(x, drawable->options->y, screen, w * numdigits, h, x, drawable->options->y); + + x = drawable->options->x; + + // in the special case of 0, you draw 0 + if (!num) + { + V_DrawPatch(x - w, drawable->options->y, font[0]); + } + + // draw the new number + while (num && numdigits--) + { + x -= w; + V_DrawPatch(x, drawable->options->y, font[num % 10]); + num /= 10; + } +} + +void drawable_update_number_value(Drawable *drawable, int *value) +{ + drawable->value.number = value; +} diff --git a/src/engine/graphics/drawable.h b/src/engine/graphics/drawable.h new file mode 100644 index 0000000000..59dc829191 --- /dev/null +++ b/src/engine/graphics/drawable.h @@ -0,0 +1,214 @@ +/* + * Copyright(C) 2023 Joshua Murphy + * + * 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. + * + * drawable.h +*/ + +#ifndef __DRAWABLE__ +#define __DRAWABLE__ + +// We are referring to patches. +#include "../../doom/r_defs.h" + + + +/** @struct DrawableTextures + * + * Contains textures comprising fonts, UI elements, icons, etc. + * + * DrwableTextures encapsulates and provides easy-access to textures (known as + * patches in DOOM jargon). + * + * @var DrawableTextures::large_numbers + * Textures for the large red numbers (0 through 9), used in Drawables such as the + * health counter. They can be viewed by looking at lumps `STTNUM` in a WAD editor. + + * @var DrawableTextures::small_numbers + * Textures for the small yellow numbers (0 through 9) used in the small ammo counters. + * They can be viewed by looking at lumps `STYSNUM` in a WAD editor. + + * @var DrawableTextures::large_percent_symbol + * Texture for a large percent symbol, it can be viewed by looking at lump `STTPRCNT` in a WAD + * editor. + * \image html STTPRCNT.png "Large percent symbol (`STTPRCNT`)" + * + * @var DrawableTextures::large_minus_symbol + * Texture for a large minus symbol, it can be viewed by looking at lump `STTMINUS` in a WAD + * editor. + * + * @var DrawableTextures::status_bar + * The status bar background texture, found in the WAD as `STBAR` + */ + +typedef struct DrawableTextures { + patch_t *large_numbers[10]; + patch_t *small_numbers[10]; + patch_t *large_percent_sign; + patch_t *large_minus_sign; + patch_t *status_bar; +} DrawableTextures; + +DrawableTextures *drawable_get_textures(); + +/** @union DrawableValue + * @brief Union type encapsulating the types of values a Drawable can draw to the screen. + */ + +typedef union DrawableValue { + /** Value for a number Drawable **/ + int *number; + /** Value for a text Drawable **/ + char *text; + /** Value for a texture Drawable **/ + patch_t *texture; +} DrawableValue; + +/** + * @brief All valid types that a Drawable can be. + */ +typedef enum DrawableType{ + /** Drawable is a number **/ + DRAWABLE_NUMBER, + /** Drawable is text **/ + DRAWABLE_TEXT, + /** Drawable is a texture **/ + DRAWABLE_TEXTURE +} DrawableType; + +typedef struct Drawable { + DrawableValue value; + DrawableType type; + + /** The x position of the new Drawable **/ + + int x; + /** The y position of the new Drawable **/ + int y; + + /** Whether a Drawable that draws characters should use the large red font present in the DOOM + wad (true), or the small yellow font (false). **/ + boolean use_large_font; + + /** Whether the Drawable is enabled and should be updated **/ + boolean enabled; + + /** The number of digits that (applicable when using a number Drawable **/ + int digits; + + /** Whether or not a percent sign should be shown after a number Drawable **/ + boolean show_percent_sign; +} Drawable; + +/** @defgroup DrawableInternal Drawable (internal documentation) + * Internal documentation of the Drawable API implementation. + * @{ + */ + + +/** @} End of internal documentation */ + +/** @defgroup DrawableAPI Drawable (API) + * Public documentation for the Drawable API. + * @{ + */ + +/** @struct Drawable + * + * Drawable is any 2D element shown on screen that is not part of the game world. + * + * For example, the ammo counter is a Drawable, an enemy or barrel is not. More specifically, + * Drawable provides high-level functions to easily draw a texture (known in DOOM jargon as a + * patch) to the screen. Fundamentally every on-screen element (that is not part of the + * gameworld) is one or more textures (patches) all the way down. + + * If we're drawing a texture such as the icon for the blue keycard, we simply use the Drawable + * type and assign its texture to the blue keycard texture found in the WAD. + * + * However, DOOM doesn't support the loading or drawing of abritrary fonts and images on the + * filesystem. Those must be textures of some kind, and they must exist in the WAD. So, if we + * want to draw numbers or strings to the screen, (whether they be used as ammo counters in the + * case of numbers, menu items or chat messages in the case of strings, etc) we're really drawing + * a series of individual textures for each alphanumeric symbol. + * + * To expand upon the basic functionality of texture loading and drawing provided by Drawable, + * a member sub_type (`DrawableSubType`) is present. This is an enum containing values for valid + * sub-types. If no sub-type is being used, the enum value is simply `DRAWABLE`. + * + * Examples of such sub-types are those used to faciliate drawing numbers and strings in a high- + * level manner. A sub-type value of `NUMBER` denotes that a `NumberDrawable` will be used. + * A sub-type value of `TEXT` denotes that a TextDrawable will be used. If no sub-type will be + * used, the value is just `DRAWABLE`. + * + * @var Drawable::sub_type + * Which sub-type of drawable we are using. Sub-types faciliate operations such as drawing + * numbers or text in a high-level manner. A sub-type (`DRAWABLE`) indicates that no sub- + * type is used, and we are just drawing a texture. + * + * @var Drawable::data + * Data that is specific to each sub-type. If no sub-type is used, this field can be + * ignored. + * + * @var Drawable::enabled + * Whether or note the Drawable should be updated. + * + * @var Drawable::value + * The value that the Drawable should draw to the screen. + * + * @var Drawable::use_large_font + * + */ + +/** + * @brief Initialize the Drawable API. Loads all textures, resources, etc. Must be called before + * using any Drawable API functions. + */ + +void drawable_init(); + +/** + * @brief Draws a DrawableTexture to the screen. + * @param drawable The Drawable to be drawn + * @param boolean Whether a refresh should be performed + */ + +void drawable_draw(const Drawable *drawable, boolean refresh); + +/** + * @brief Creates a Drawable for displaying a number + * @param texture The texture + * @return The newly-created drawable. + */ + +Drawable *drawable_create_number(int *value, DrawableOptions *options, boolean *enabled); + +/** + * @brief Updates the displayed value of a number drawable. + */ +void drawable_update_number_value(Drawable *drawable, int *value); + +/** + * @brief Creates a Drawable for displaying a DrawableTexture to the screen. + * @return The newly-created drawable. + */ + +Drawable *drawable_create(Drawable *drawable, DrawableOptions *options, boolean *enabled); + + +/** + * @brief Draws a number drawable to the status bar. + */ +void drawable_draw_number(const Drawable *drawable, boolean refresh); + +/** @} End of API documentation */ + +#endif diff --git a/src/engine/graphics/drawable_registry.c b/src/engine/graphics/drawable_registry.c new file mode 100644 index 0000000000..1408c66567 --- /dev/null +++ b/src/engine/graphics/drawable_registry.c @@ -0,0 +1,153 @@ +/* + * Copyright(C) 2023 Joshua Murphy + * + * 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. + * + * drawable_registry.c + * + */ + +#include "drawable_registry.h" +#include "../../log.h" + +const int INITIAL_CAPACITY = 10; +const int TAG_SIZE = 50; + +typedef struct { + char tag[TAG_SIZE]; + Drawable* drawable; +} DrawableTagPair; + +struct DrawableRegistry { + DrawableTagPair* data; + int size; + int capacity; +}; + +//** Initialize a DrawableRegistry **/ +DrawableRegistryStatus drawable_registry_initialize(DrawableRegistry* registry) { + log_trace("drawable_registry_initialize(): Initializing DrawableRegistry %x", registry); + + registry->size = 0; + registry->capacity = INITIAL_CAPACITY; + registry->data = (DrawableTagPair*)malloc(sizeof(DrawableTagPair) * registry->capacity); + + if (!registry->data) { + log_fatal("drawable_registry_initialize(): Failure: Memory allocation for DrawableRegistry failed."); + return DRAWABLE_REGISTRY_ALLOCATION_FAILED; + } + + log_debug("drawable_registry_initialize(): Great Success! Memory allocated for initialization of DrawableRegistry successful."); + return DRAWABLE_REGISTRY_SUCCESS; +} + +DrawableRegistryStatus drawable_registry_resize(DrawableRegistry* registry) { + log_trace("drawable_registry_resize(): Resizing DrawableRegistry %x.", registry); + + if !(registry) + { + log_error("drawable_registry_resize(): 💩 Failure: Cannot resize a NULL DrawableRegistry (%x).", registry); + } + + registry->capacity *= 2; + DrawableTagPair* new_data = (DrawableTagPair*)realloc(registry->data, sizeof(DrawableTagPair) * registry->capacity); + + if (!new_data) { + log_fatal("drawable_registry_resize(): Failure: Memory allocation to resize DrawableRegistry %x failed.", registry); + return DRAWABLE_REGISTRY_RESIZE_FAILED; + } + + log_debug("drawable_registry_resize(): Great Success! Memory allocation to resize DrawableRegistry %x successful.", registry); + + registry->data = new_data; + return DRAWABLE_REGISTRY_SUCCESS; +} + +/** + * Add a Drawable to a DrawableRegistry + * @param tag The tag this Drawable should be retrievable with + * @param drawable The Drawable to add to the registry + */ +DrawableRegistryStatus drawable_registry_add(DrawableRegistry* registry, char* tag, Drawable* drawable) { + log_trace("drawable_registry_add(): Adding new Drawable %x with tag \"%d\" to DrawableRegistry %x.", tag, drawable, registry); + + if !(registry) + { + log_error("drawable_registry_add(): Cannot add element to a NULL DrawableRegistry (%x).", registry); + } + + if (strlen(tag) >= TAG_SIZE) { + log_error("drawable_registry_add(): Failed to add element to DrawableRegistry: tag length exceeded."); + return DRAWABLE_REGISTRY_TAG_TOO_LONG; + } + + if (registry->size == registry->capacity) { + DrawableRegistryStatus resizeStatus = drawable_registry_resize(registry); + if (resizeStatus != DRAWABLE_REGISTRY_SUCCESS) { + return resizeStatus; + } + } + + snprintf(registry->data[registry->size].tag, sizeof(registry->data[registry->size].tag), "%s", tag); + registry->data[registry->size].drawable = drawable; + registry->size++; + + log_debug("drawable_registry_add(): Great Success! Added Drawable %x with tag \"%d\" to DrawableRegistry %x.", tag, drawable, registry); + + return DRAWABLE_REGISTRY_SUCCESS; +} + +/** + * Get a Drawable from the DrawableRegistry + * @param tag The tag that should be used to find the Drawable + * @return The Drawable or NULL if the Drawable is not found + */ +Drawable* drawable_registry_get(const DrawableRegistry* registry, const char* tag) { + for (int i = 0; i < registry->size; i++) { + if (strcmp(registry->data[i].tag, tag) == 0) { + return registry->data[i].drawable; + } + } + return NULL; // If not found, return NULL +} + +/** + * Get a Drawable from the DrawableRegistry + * @param tag The tag that should be used to find the Drawable + * @return The Drawable or NULL if the Drawable is not found + */ +DrawableRegistryStatus drawable_registry_remove(DrawableRegistry* registry, char* tag) { + for (int i = 0; i < registry->size; i++) { + if (strcmp(registry->data[i].tag, tag) == 0) { + for (int j = i; j < registry->size - 1; j++) { + registry->data[j] = registry->data[j + 1]; + } + registry->size--; + return DRAWABLE_REGISTRY_SUCCESS; + } + } + return DRAWABLE_REGISTRY_TAG_NOT_FOUND; +} + +void drawable_registry_draw_all(const DrawableRegistry* registry) { + for (int i = 0; i < registry->size; i++) { + drawable_draw(registry->data[i].drawable); + } +} + +void drawable_registry_free(DrawableRegistry* registry) { + if (registry->data) { + free(registry->data); + registry->data = NULL; + } + registry->size = 0; + registry->capacity = 0; +} diff --git a/src/engine/graphics/drawable_registry.h b/src/engine/graphics/drawable_registry.h new file mode 100644 index 0000000000..1092d5c671 --- /dev/null +++ b/src/engine/graphics/drawable_registry.h @@ -0,0 +1,48 @@ +/* + * Copyright(C) 2023 Joshua Murphy + * + * 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. + * + * drawable_registry.h + * + * + * Provides a registry for all Drawables. + */ + +#ifndef DRAWABLE_REGISTRY_H +#define DRAWABLE_REGISTRY_H + +#include "drawable.h" + +/** Defines return values of DrawableRegistry function **/ +typedef enum { + DRAWABLE_REGISTRY_SUCCESS = 0, + /** Memory allocation failed **/ + DRAWABLE_REGISTRY_ALLOCATION_FAILED = -1, + /** Memory allocation (resize) **/ + DRAWABLE_REGISTRY_RESIZE_FAILED = -2, + /** Tag for an element exceed length limit **/ + DRAWABLE_REGISTRY_TAG_TOO_LONG = -3, + /** No element with this tag found in the array **/ + DRAWABLE_REGISTRY_TAG_NOT_FOUND = -4 +} DrawableRegistryStatus; + +typedef struct DrawableRegistry DrawableRegistry; + +DrawableRegistryStatus drawable_registry_initialize(DrawableRegistry* registry); +DrawableRegistryStatus drawable_registry_register(DrawableRegistry* registry, char* tag, Drawable* drawable); +Drawable* drawable_registry_get(const DrawableRegistry* registry, const char* tag); +DrawableRegistryStatus drawable_registry_remove(DrawableRegistry* registry, char* tag); +void drawable_registry_draw_all(const DrawableRegistry* registry); +void drawable_registry_free(DrawableRegistry* registry); + +#endif // DRAWABLE_REGISTRY_H + diff --git a/src/engine/graphics/widget.c b/src/engine/graphics/widget.c new file mode 100644 index 0000000000..9f3edc2d5c --- /dev/null +++ b/src/engine/graphics/widget.c @@ -0,0 +1,135 @@ +/* + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2023 Joshua Murphy + * + * 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. + * + * widget.c (previously st_lib.c) + * Reusable UI widget library +*/ + +#include +#include + +#include "../../doom/doomdef.h" + +#include "../../log.h" + +#include "../../z_zone.h" +#include "../../v_video.h" + +#include "../../i_swap.h" +#include "../../i_system.h" + +#include "../../w_wad.h" + +#include "../../doom/hud/statusbar.h" +#include "widget.h" +#include "../../doom/r_local.h" + + +// +// Hack display negative frags. +// Loads and store the stminus lump. +// +patch_t *sttminus; + +void STlib_init(void) +{ + if (W_CheckNumForName("STTMINUS") >= 0) + { + sttminus = (patch_t *) W_CacheLumpName("STTMINUS", PU_STATIC); + } + else + { + sttminus = NULL; + } +} + +void STlib_initMultIcon(st_multicon_t *i, int x, int y, patch_t **il, int *inum, boolean *on) +{ + i->x = x; + i->y = y; + i->oldinum = -1; + i->inum = inum; + i->on = on; + i->p = il; +} + + +void STlib_updateMultIcon(st_multicon_t *mi, boolean refresh) +{ + int w; + int h; + int x; + int y; + + if (*mi->on && (mi->oldinum != *mi->inum || refresh) && (*mi->inum != -1)) + { + if (mi->oldinum != -1) + { + x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset); + y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset); + w = SHORT(mi->p[mi->oldinum]->width); + h = SHORT(mi->p[mi->oldinum]->height); + + if (y - ST_Y < 0) + { + I_Error("updateMultIcon: y - ST_Y < 0"); + } + V_CopyRect(x, y - ST_Y, st_backing_screen, w, h, x, y); + } + V_DrawPatch(mi->x, mi->y, mi->p[*mi->inum]); + mi->oldinum = *mi->inum; + } +} + +void STlib_initBinIcon(st_binicon_t *b, int x, int y, patch_t *i, boolean *val, boolean *on) +{ + b->x = x; + b->y = y; + b->oldval = false; + b->val = val; + b->on = on; + b->p = i; +} + +// TODO: To be replaced by a new function called widget_draw_icon_widget() +void STlib_updateBinIcon(st_binicon_t *bi, boolean refresh) +{ + int x; + int y; + int w; + int h; + + if (*bi->on && (bi->oldval != *bi->val || refresh)) + { + x = bi->x - SHORT(bi->p->leftoffset); + y = bi->y - SHORT(bi->p->topoffset); + w = SHORT(bi->p->width); + h = SHORT(bi->p->height); + + if (y - ST_Y < 0) + { + I_Error("updateBinIcon: y - ST_Y < 0"); + } + if (*bi->val) + { + V_DrawPatch(bi->x, bi->y, bi->p); + } + else + { + V_CopyRect(x, y - ST_Y, st_backing_screen, w, h, x, y); + } + bi->oldval = *bi->val; + } +} diff --git a/src/engine/graphics/widget.h b/src/engine/graphics/widget.h new file mode 100644 index 0000000000..c2a42dfe04 --- /dev/null +++ b/src/engine/graphics/widget.h @@ -0,0 +1,94 @@ +/* + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2023 Joshua Murphy + * + * 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. + * + * widget.h (previously st_lib.h) + * Reusable UI widget library +*/ + +#ifndef __STLIB__ +#define __STLIB__ + +// We are referring to patches. +#include "../../doom/r_defs.h" + +// Multiple Icon widget +typedef struct +{ + // center-justified location of icons + int x; + int y; + + // last icon number + int oldinum; + + // pointer to current icon + int *inum; + + // pointer to boolean stating + // whether to update icon + boolean *on; + + // list of icons + patch_t **p; + + // user data + int data; + +} st_multicon_t; + + +// Binary Icon widget + +typedef struct +{ + // center-justified location of icon + int x; + int y; + + // last icon value + boolean oldval; + + // pointer to current icon status + boolean *val; + + // pointer to boolean + // stating whether to update icon + boolean *on; + + + patch_t *p; // icon + int data; // user data + +} st_binicon_t; + +// +// Widget creation, access, and update routines +// + +void STlib_init(void); + +// Multiple Icon widget routines +void STlib_initMultIcon(st_multicon_t *mi, int x, int y, patch_t **il, int *inum, boolean *on); + + +void STlib_updateMultIcon(st_multicon_t *mi, boolean refresh); + +// Binary Icon widget routines + +void STlib_initBinIcon(st_binicon_t *b, int x, int y, patch_t *i, boolean *val, boolean *on); + +void STlib_updateBinIcon(st_binicon_t *bi, boolean refresh); + +#endif diff --git a/src/i_system.c b/src/i_system.c index 1032203a43..4ae329538a 100644 --- a/src/i_system.c +++ b/src/i_system.c @@ -150,7 +150,7 @@ void I_Quit (void) static boolean already_quitting = false; -void System_Exit() +void system_exit() { atexit_listentry_t *entry; boolean exit_gui_popup; diff --git a/src/i_system.h b/src/i_system.h index 83cca24770..a114c3ecd2 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -50,7 +50,7 @@ void I_Quit (void) NORETURN; void I_Error (const char *error, ...) NORETURN PRINTF_ATTR(1, 2); // Perform the same cleanup as I_Error but not responsible for printing -void System_Exit(); +void system_exit(); void *I_Realloc(void *ptr, size_t size); diff --git a/src/v_video.c b/src/v_video.c index e54fa683e1..55e5471605 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -156,7 +156,7 @@ void V_CopyRect(int srcx, int srcy, pixel_t *source, if (!V_CheckCopyRect(srcx, srcy, destx, desty, width, height)) { log_fatal("V_CopyRect(): Could not copy rectangle %x", source); - System_Exit(); + system_exit(); } V_MarkRect(destx, desty, width, height);