From a602a90a8cee414257847eb4250107d5a0018b5b Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 23 Jan 2023 11:46:10 +0000 Subject: [PATCH 01/12] m_cheat.c: Permit singleplayer-only cheats in offline match race --- src/m_cheat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index edd23041c..48f3eca36 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -247,8 +247,8 @@ boolean cht_Responder(event_t *ev) #define REQUIRE_INLEVEL if (gamestate != GS_LEVEL || demo.playback)\ { CONS_Printf(M_GetText("You must be in a level to use this.\n")); return; } -#define REQUIRE_SINGLEPLAYER if (netgame || multiplayer)\ -{ CONS_Printf(M_GetText("This only works in single player.\n")); return; } +#define REQUIRE_SINGLEPLAYER if (netgame)\ +{ CONS_Printf(M_GetText("This only works offline.\n")); return; } // command that can be typed at the console! void Command_CheatNoClip_f(void) From a75a7039d66009f4c2dc4b025a75ad480e70a30c Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 23 Jan 2023 11:47:34 +0000 Subject: [PATCH 02/12] HU_DrawRankings: Do not fade screen while automap is visible. --- src/hu_stuff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index f25f64a3e..3296b0eb5 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2398,7 +2398,8 @@ static void HU_DrawRankings(void) UINT32 whiteplayer = MAXPLAYERS; boolean timedone = false, pointsdone = false; - V_DrawFadeScreen(0xFF00, 16); // A little more readable, and prevents cheating the fades under other circumstances. + if (!automapactive) + V_DrawFadeScreen(0xFF00, 16); // A little more readable, and prevents cheating the fades under other circumstances. // draw the current gametype in the lower right if (grandprixinfo.gp == true) From dafd6d5dfa09bd02d0a524dbb5f440d72bbfc73d Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 23 Jan 2023 12:33:35 +0000 Subject: [PATCH 03/12] New console command "minigen" - Generates "MINIMAP.png" in your srb2home - Uses inherited automap code to render to a temporary buffer - Because am_map.c is a mess of filescope static variables right now, this only works when the automap is disabled. - Currently an equal alternate method to SLADE's map image export, but because we're in control, additional features can be added later... - TODO: Off vertically by one pixel on GHZ. Otherwise effectively identical in shape - TODO: the colours are rancid, I wonder if they were even updated for the 2.2 palette Related: - Use identical linear-time mechanisms for detecting borders of map geometry between automap and minimap - Automap was previously using iteration over all vertices - Minimap was previously pointlessly writing min/max values twice --- src/am_map.c | 121 +++++++++++++++++++++++++++++++++++++++++-------- src/am_map.h | 3 ++ src/d_netcmd.c | 1 + src/k_hud.c | 2 - src/m_misc.c | 37 +++++++++++++++ src/m_misc.h | 2 + 6 files changed, 144 insertions(+), 22 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index 1bfb145c4..8dde145ae 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -156,6 +156,9 @@ static boolean draw_grid = false; boolean automapactive = false; boolean am_recalc = false; //added : 05-02-98 : true when screen size changes static boolean am_stopped = true; +static boolean am_minigen = false; + +static UINT8 *am_buf = NULL; static INT32 f_x, f_y; // location of window on screen (always zero for both) static INT32 f_w, f_h; // size of window on screen (always the screen width and height respectively) @@ -256,25 +259,24 @@ static inline void AM_restoreScaleAndLoc(void) */ static void AM_findMinMaxBoundaries(void) { - size_t i; fixed_t a; fixed_t b; - min_x = min_y = +INT32_MAX; - max_x = max_y = -INT32_MAX; + node_t *bsp = &nodes[numnodes-1]; - for (i = 0; i < numvertexes; i++) - { - if (vertexes[i].x < min_x) - min_x = vertexes[i].x; - else if (vertexes[i].x > max_x) - max_x = vertexes[i].x; + min_x = bsp->bbox[0][BOXLEFT]; + max_x = bsp->bbox[0][BOXRIGHT]; + min_y = bsp->bbox[0][BOXBOTTOM]; + max_y = bsp->bbox[0][BOXTOP]; - if (vertexes[i].y < min_y) - min_y = vertexes[i].y; - else if (vertexes[i].y > max_y) - max_y = vertexes[i].y; - } + if (bsp->bbox[1][BOXLEFT] < min_x) + min_x = bsp->bbox[1][BOXLEFT]; + if (bsp->bbox[1][BOXRIGHT] > max_x) + max_x = bsp->bbox[1][BOXRIGHT]; + if (bsp->bbox[1][BOXBOTTOM] < min_y) + min_y = bsp->bbox[1][BOXBOTTOM]; + if (bsp->bbox[1][BOXTOP] > max_y) + max_y = bsp->bbox[1][BOXTOP]; max_w = (max_x >>= FRACTOMAPBITS) - (min_x >>= FRACTOMAPBITS); max_h = (max_y >>= FRACTOMAPBITS) - (min_y >>= FRACTOMAPBITS); @@ -282,8 +284,26 @@ static void AM_findMinMaxBoundaries(void) a = FixedDiv(f_w<>FRACBITS; + } + min_scale_mtof = a; + max_scale_mtof = f_h; + } + else + { + if (am_minigen) + { + f_w = FixedMul(b, max_w)>>FRACBITS; + } + min_scale_mtof = b; + max_scale_mtof = f_w; + } + max_scale_mtof = FixedDiv(max_scale_mtof<= vid.width || yy >= vid.height) + if (xx < 0 || yy < 0 || xx >= f_w || yy >= f_h) return; // off the screen - dest[(yy*vid.width) + xx] = cc; + am_buf[(yy*f_w) + xx] = cc; } // @@ -916,7 +936,7 @@ static void AM_drawGrid(INT32 color) // Determines visible lines, draws them. // This is LineDef based, not LineSeg based. // -static inline void AM_drawWalls(void) +static void AM_drawWalls(void) { size_t i; static mline_t l; @@ -1148,3 +1168,64 @@ void AM_Drawer(void) if (!followplayer) AM_drawCrosshair(XHAIRCOLORS); } + +UINT8 *AM_MinimapGenerate(INT32 wh) +{ + UINT8 *buf = NULL; + + if (automapactive) + return NULL; + + am_minigen = true; + + AM_drawFline = AM_drawFline_soft; // yes, even in GL + + //AM_FrameBufferInit(); + f_x = f_y = 0; + f_w = f_h = wh; + am_buf = NULL; + + //AM_LevelInit(); + AM_findMinMaxBoundaries(); + scale_mtof = FixedMul(min_scale_mtof, FRACUNIT-FRACUNIT/20); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + + //AM_initVariables(); + f_oldloc.x = INT32_MAX; + + m_paninc.x = m_paninc.y = 0; + ftom_zoommul = FRACUNIT; + mtof_zoommul = FRACUNIT; + + m_w = FTOM(f_w); + m_h = FTOM(f_h); + + //AM_changeWindowLoc(); + m_x = min_x - FixedMul(m_w, FRACUNIT/40); + m_y = min_y - FixedMul(m_h, FRACUNIT/40); + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // for saving & restoring + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; + + buf = malloc(2 + (f_w*f_h)); + + am_buf = buf+2; + + //AM_clearFB(BACKGROUND); + memset(am_buf, 0xff, (f_w*f_h)); + AM_drawWalls(); + + am_buf = NULL; + am_recalc = true; + + am_minigen = false; + + buf[0] = (UINT8)f_w; + buf[1] = (UINT8)f_h; + return buf; +} diff --git a/src/am_map.h b/src/am_map.h index 56a5f9616..be32f342c 100644 --- a/src/am_map.h +++ b/src/am_map.h @@ -48,6 +48,9 @@ void AM_Start(void); // Called to force the automap to quit if the level is completed while it is up. void AM_Stop(void); +// Minimap generation +UINT8 *AM_MinimapGenerate(INT32 wh); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 56f827c46..95f45cc6d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -895,6 +895,7 @@ void D_RegisterClientCommands(void) COM_AddCommand("screenshot", M_ScreenShot); COM_AddCommand("startmovie", Command_StartMovie_f); COM_AddCommand("stopmovie", Command_StopMovie_f); + COM_AddCommand("minigen", M_MinimapGenerate); CV_RegisterVar(&cv_screenshot_option); CV_RegisterVar(&cv_screenshot_folder); diff --git a/src/k_hud.c b/src/k_hud.c index cc605df83..8abcb82f3 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3752,8 +3752,6 @@ static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 fixed_t xoffset, yoffset; fixed_t xscale, yscale, zoom; - maxx = maxy = INT32_MAX; - minx = miny = INT32_MIN; minx = bsp->bbox[0][BOXLEFT]; maxx = bsp->bbox[0][BOXRIGHT]; miny = bsp->bbox[0][BOXBOTTOM]; diff --git a/src/m_misc.c b/src/m_misc.c index 25a6c8624..49f8f2ddc 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1749,6 +1749,43 @@ boolean M_ScreenshotResponder(event_t *ev) return true; } +void M_MinimapGenerate(void) +{ +#ifdef USE_PNG + char *filepath = va(pandf, srb2home, "MINIMAP.png"); + boolean ret = false; + UINT8 *linear = NULL; + INT32 wh = 100; + + if (gamestate != GS_LEVEL) + { + CONS_Alert(CONS_ERROR, "You must be in a level to generate a preliminary minimap!\n"); + return; + } + + linear = AM_MinimapGenerate(wh); + + if (linear == NULL) + goto failure; + + M_CreateScreenShotPalette(); + ret = M_SavePNG(filepath, linear+2, linear[0], linear[1], screenshot_palette); + +failure: + if (linear != NULL) + free(linear); + + if (ret) + { + CONS_Printf(M_GetText("%s saved.\nRemember that this is not a complete minimap,\nand must be edited before putting in-game.\n"), filepath); + } + else + { + CONS_Alert(CONS_ERROR, M_GetText("Couldn't create %s\n"), filepath); + } +#endif //#ifdef USE_PNG +} + // ========================================================================== // TRANSLATION FUNCTIONS // ========================================================================== diff --git a/src/m_misc.h b/src/m_misc.h index 97cc3e2c8..ec979dcb3 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -89,6 +89,8 @@ void M_ScreenShot(void); void M_DoScreenShot(void); boolean M_ScreenshotResponder(event_t *ev); +void M_MinimapGenerate(void); + void Command_SaveConfig_f(void); void Command_LoadConfig_f(void); void Command_ChangeConfig_f(void); From da639fe65f13a9f140b1483a680d15205eae1326 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 23 Jan 2023 18:38:52 +0000 Subject: [PATCH 04/12] Rework automap/minigen colours - All pre-existing colours are now actually what the code says they should be for the 2.2 palette - RR-specific colour changes. - Remove all noclimb-specific line colours - Render areas that can't be stepped up/down onto as walls - Add colours for - Tripwire (cyan, but not 0xff cyan) - Finish Line (grey) - FOF info (blue) - Use a low-intensity colour to signal possible offroad/hazard sector or stairjank step - Rework to support drawing in multiple passes, so that information that is strictly more important (solid walls, finish line) will not be obscured at minimap resolution by nearby lines signalling offroad/stairjank Related: - Added K_TerrainHasAffect - Returns true if terrain has any properties which would affect the player's gameplay, false if not. --- src/am_map.c | 300 +++++++++++++++++++++++++++++++++++++++--------- src/k_terrain.c | 17 +++ src/k_terrain.h | 14 +++ 3 files changed, 274 insertions(+), 57 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index 8dde145ae..d90147e28 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -26,43 +26,39 @@ #endif // For use if I do walls with outsides/insides -static const UINT8 REDS = (8*16); +static const UINT8 REDS = (2*16); static const UINT8 REDRANGE = 16; static const UINT8 GRAYS = (1*16); static const UINT8 GRAYSRANGE = 16; -static const UINT8 BROWNS = (3*16); -static const UINT8 YELLOWS = (7*16); -static const UINT8 GREENS = (10*16); +static const UINT8 BROWNS = (15*16); +static const UINT8 YELLOWS = (5*16)+8; +static const UINT8 GREENS = (6*16); +static const UINT8 CYANS = (8*16); +static const UINT8 BLUES = (9*16); static const UINT8 DBLACK = 31; static const UINT8 DWHITE = 0; -static const UINT8 NOCLIMBREDS = 248; -static const UINT8 NOCLIMBREDRANGE = 8; -static const UINT8 NOCLIMBGRAYS = 204; -static const UINT8 NOCLIMBBROWNS = (2*16); -static const UINT8 NOCLIMBYELLOWS = (11*16); - // Automap colors #define BACKGROUND DBLACK #define WALLCOLORS (REDS + REDRANGE/2) #define WALLRANGE (REDRANGE/2) -#define NOCLIMBWALLCOLORS (NOCLIMBREDS + NOCLIMBREDRANGE/2) -#define NOCLIMBWALLRANGE (NOCLIMBREDRANGE/2) #define THOKWALLCOLORS REDS #define THOKWALLRANGE REDRANGE -#define NOCLIMBTHOKWALLCOLORS NOCLIMBREDS -#define NOCLIMBTHOKWALLRANGE NOCLIMBREDRANGE -#define TSWALLCOLORS GRAYS -#define TSWALLRANGE GRAYSRANGE -#define NOCLIMBTSWALLCOLORS NOCLIMBGRAYS +#define TSWALLCOLORS DWHITE +#define TSFINISHLINE GRAYS +#define TSTRIPWIRE (CYANS + 4) +#define TSFOFINFO (BLUES + 4) #define FDWALLCOLORS BROWNS -#define NOCLIMBFDWALLCOLORS NOCLIMBBROWNS #define CDWALLCOLORS YELLOWS -#define NOCLIMBCDWALLCOLORS NOCLIMBYELLOWS #define THINGCOLORS GREENS #define GRIDCOLORS (GRAYS + GRAYSRANGE/2) -#define XHAIRCOLORS DWHITE +#define XHAIRCOLORS GRAYS + +// Automap passes +#define PASS_SOLID 1 +#define PASS_INTANGIBLE 2 +#define PASS_FOF 4 // controls #define AM_PANUPKEY KEY_UPARROW @@ -932,14 +928,93 @@ static void AM_drawGrid(INT32 color) } } +#define SLOPEPARAMS(slope, end1, end2, normalheight) \ + end1 = (P_GetZAt(slope, lines[i].v1->x, lines[i].v1->y, normalheight) + FRACUNIT/2) >> FRACBITS; \ + end2 = (P_GetZAt(slope, lines[i].v2->x, lines[i].v2->y, normalheight) + FRACUNIT/2) >> FRACBITS; + +static ffloor_t *AM_CompareFOFs(size_t i, ffloor_t *rover, ffloor_t *secondarystore) +{ + ffloor_t *secondaryrover = NULL; + + for (; rover; rover = rover->next) + { + fixed_t rovt1, rovt2; + fixed_t rovb1, rovb2; + + if (!(rover->fofflags & FOF_EXISTS)) + continue; + if (!(rover->fofflags & FOF_BLOCKPLAYER)) + continue; + + SLOPEPARAMS(*rover->t_slope, rovt1, rovt2, *rover->topheight) + SLOPEPARAMS(*rover->b_slope, rovb1, rovb2, *rover->bottomheight) + + for (secondaryrover = secondarystore; secondaryrover; secondaryrover = secondaryrover->next) + { + fixed_t sect1, sect2; + fixed_t secb1, secb2; + + terrain_t *terrain1 = NULL; + terrain_t *terrain2 = NULL; + + if (!(secondaryrover->fofflags & FOF_EXISTS)) + continue; + if (!(secondaryrover->fofflags & FOF_BLOCKPLAYER)) + continue; + if (secondaryrover->secnum == rover->secnum) + break; + + SLOPEPARAMS(*secondaryrover->t_slope, sect1, sect2, *secondaryrover->topheight) + SLOPEPARAMS(*secondaryrover->b_slope, secb1, secb2, *secondaryrover->bottomheight) + + if (rovt1 != sect1) + continue; + if (rovt2 != sect2) + continue; + if (rovb1 != secb1) + continue; + if (rovb2 != secb2) + continue; + + if (sectors[rover->secnum].damagetype != sectors[secondaryrover->secnum].damagetype + || sectors[rover->secnum].friction != sectors[secondaryrover->secnum].friction + || sectors[rover->secnum].offroad != sectors[secondaryrover->secnum].offroad) + continue; + + if (*rover->toppic != *secondaryrover->toppic) + { + terrain1 = K_GetTerrainForFlatNum(*rover->toppic); + terrain2 = K_GetTerrainForFlatNum(*secondaryrover->toppic); + } + else if (*rover->bottompic != *secondaryrover->bottompic) + { + terrain1 = K_GetTerrainForFlatNum(*rover->bottompic); + terrain2 = K_GetTerrainForFlatNum(*secondaryrover->bottompic); + } + + if ((terrain1 && K_TerrainHasAffect(terrain1)) + || (terrain2 && K_TerrainHasAffect(terrain2))) + continue; + + break; + } + + if (secondaryrover == NULL) + break; + } + + return rover; +} + // // Determines visible lines, draws them. // This is LineDef based, not LineSeg based. // -static void AM_drawWalls(void) +static void AM_drawWalls(UINT8 pass) { size_t i; static mline_t l; + const fixed_t maxstep = P_BaseStepUp()>>FRACBITS; fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends fixed_t backf1 = 0, backf2 = 0, backc1 = 0, backc2 = 0; // back floor ceiling ends @@ -950,71 +1025,180 @@ static void AM_drawWalls(void) l.b.x = lines[i].v2->x >> FRACTOMAPBITS; l.b.y = lines[i].v2->y >> FRACTOMAPBITS; -#define SLOPEPARAMS(slope, end1, end2, normalheight) \ - end1 = P_GetZAt(slope, lines[i].v1->x, lines[i].v1->y, normalheight); \ - end2 = P_GetZAt(slope, lines[i].v2->x, lines[i].v2->y, normalheight); - SLOPEPARAMS(lines[i].frontsector->f_slope, frontf1, frontf2, lines[i].frontsector->floorheight) SLOPEPARAMS(lines[i].frontsector->c_slope, frontc1, frontc2, lines[i].frontsector->ceilingheight) - if (lines[i].backsector) { - SLOPEPARAMS(lines[i].backsector->f_slope, backf1, backf2, lines[i].backsector->floorheight) - SLOPEPARAMS(lines[i].backsector->c_slope, backc1, backc2, lines[i].backsector->ceilingheight) - } -#undef SLOPEPARAMS if (!lines[i].backsector) // 1-sided { - if (lines[i].flags & ML_NOCLIMB) - AM_drawMline(&l, NOCLIMBWALLCOLORS); - else + if (!(pass & PASS_SOLID)) + ; + else if (frontf1 != frontc1 || frontf2 != frontc2 || lines[i].frontsector->f_slope) + { + AM_drawMline(&l, TSWALLCOLORS); + } + else if (!am_minigen) + { AM_drawMline(&l, WALLCOLORS); + } + continue; } - else if ((backf1 == backc1 && backf2 == backc2) // Back is thok barrier + + SLOPEPARAMS(lines[i].backsector->f_slope, backf1, backf2, lines[i].backsector->floorheight) + SLOPEPARAMS(lines[i].backsector->c_slope, backc1, backc2, lines[i].backsector->ceilingheight) + + if ((backf1 == backc1 && backf2 == backc2) // Back is thok barrier || (frontf1 == frontc1 && frontf2 == frontc2)) // Front is thok barrier { if (backf1 == backc1 && backf2 == backc2 && frontf1 == frontc1 && frontf2 == frontc2) // BOTH are thok barriers { - if (lines[i].flags & ML_NOCLIMB) - AM_drawMline(&l, NOCLIMBTSWALLCOLORS); - else - AM_drawMline(&l, TSWALLCOLORS); + if (!am_minigen && (pass & PASS_INTANGIBLE)) + { + AM_drawMline(&l, GRIDCOLORS); + } } - else + else if (pass & PASS_SOLID) { - if (lines[i].flags & ML_NOCLIMB) - AM_drawMline(&l, NOCLIMBTHOKWALLCOLORS); - else - AM_drawMline(&l, THOKWALLCOLORS); + AM_drawMline(&l, TSWALLCOLORS); } } else { - if (lines[i].flags & ML_NOCLIMB) { - if (backf1 != frontf1 || backf2 != frontf2) { - AM_drawMline(&l, NOCLIMBFDWALLCOLORS); // floor level change - } - else if (backc1 != frontc1 || backc2 != frontc2) { - AM_drawMline(&l, NOCLIMBCDWALLCOLORS); // ceiling level change + if (lines[i].flags & (ML_IMPASSABLE|ML_BLOCKPLAYERS)) + { + if (pass & PASS_SOLID) + AM_drawMline(&l, TSWALLCOLORS); // Completely solid course boundary + } + else if ((lines[i].flags & ML_MIDSOLID) + && sides[lines->sidenum[0]].midtexture) + { + if (pass & PASS_SOLID) + AM_drawMline(&l, TSWALLCOLORS); // solid midtexture, likely a course boundary + } + if ((backf1 != frontf1 && abs(backf1 - frontf1) > maxstep) + || (backf2 != frontf2 && abs(backf2 - frontf2) > maxstep)) + { + if (pass & PASS_SOLID) + AM_drawMline(&l, TSWALLCOLORS); // floor-wall, likely a course boundary + } + else if (lines[i].special == 2001) + { + if (pass & PASS_SOLID) + AM_drawMline(&l, TSFINISHLINE); // finish line + } + else if (P_IsLineTripWire(&lines[i])) + { + if (pass & PASS_SOLID) + AM_drawMline(&l, TSTRIPWIRE); // tripwire shortcut + } + else if (backf1 != frontf1 || backf2 != frontf2) + { + if (pass & PASS_INTANGIBLE) + AM_drawMline(&l, FDWALLCOLORS); // floorlevel change + } + else if (backc1 != frontc1 || backc2 != frontc2) + { + if (!(pass & PASS_INTANGIBLE)) + ; + else if (abs(backc1 - frontc1) < maxstep + || abs(backc2 - frontc2) < maxstep) + { + AM_drawMline(&l, CDWALLCOLORS); // ceilinglevel change } else - AM_drawMline(&l, NOCLIMBTSWALLCOLORS); + { + AM_drawMline(&l, GRIDCOLORS); // ceiling-wall, not likely to be a course boundary but flagged up just in case + } + } + else if (lines[i].frontsector->damagetype != lines[i].backsector->damagetype + || lines[i].frontsector->friction != lines[i].backsector->friction + || lines[i].frontsector->offroad != lines[i].backsector->offroad) + { + if (pass & PASS_INTANGIBLE) + AM_drawMline(&l, FDWALLCOLORS); // Functionality boundary } else { - if (backf1 != frontf1 || backf2 != frontf2) { - AM_drawMline(&l, FDWALLCOLORS); // floor level change + terrain_t *terrain1 = NULL; + terrain_t *terrain2 = NULL; + UINT8 defercol = GRIDCOLORS; + + if (lines[i].frontsector->floorpic != lines[i].backsector->floorpic) + { + terrain1 = K_GetTerrainForFlatNum(lines[i].frontsector->floorpic); + terrain2 = K_GetTerrainForFlatNum(lines[i].backsector->ceilingpic); + defercol = FDWALLCOLORS; // possible floor offroad boundary } - else if (backc1 != frontc1 || backc2 != frontc2) { - AM_drawMline(&l, CDWALLCOLORS); // ceiling level change + else if (lines[i].frontsector->ceilingpic != lines[i].backsector->ceilingpic) + { + terrain1 = K_GetTerrainForFlatNum(lines[i].frontsector->floorpic); + terrain2 = K_GetTerrainForFlatNum(lines[i].backsector->ceilingpic); + defercol = CDWALLCOLORS; // possible ceiling offroad boundary + } + + if ((terrain1 && K_TerrainHasAffect(terrain1)) + || (terrain2 && K_TerrainHasAffect(terrain2))) + { + if (pass & PASS_INTANGIBLE) + AM_drawMline(&l, defercol); // Yep, definitely a functionality boundary } else - AM_drawMline(&l, TSWALLCOLORS); + { + ffloor_t *rover = NULL; + + if (lines[i].frontsector->ffloors || lines[i].backsector->ffloors) + { + if (lines[i].backsector->ffloors == NULL) + { + // Check frontside for one solid + for (rover = lines[i].frontsector->ffloors; rover; rover = rover->next) + { + if (!(rover->fofflags & FOF_EXISTS)) + continue; + if (!(rover->fofflags & FOF_BLOCKPLAYER)) + continue; + break; + } + } + else if (lines[i].frontsector->ffloors == NULL) + { + // Check backside for one solid + for (rover = lines[i].backsector->ffloors; rover; rover = rover->next) + { + if (!(rover->fofflags & FOF_EXISTS)) + continue; + if (!(rover->fofflags & FOF_BLOCKPLAYER)) + continue; + break; + } + } + else + { + // Check to see if any secnums exist in one but not the other. + rover = AM_CompareFOFs(i, lines[i].frontsector->ffloors, lines[i].backsector->ffloors); + if (rover == NULL) + rover = AM_CompareFOFs(i, lines[i].backsector->ffloors, lines[i].frontsector->ffloors); + } + } + + if (rover != NULL) + { + if (pass & PASS_FOF) + AM_drawMline(&l, TSFOFINFO); // a FOF is here but we don't know how to distinguish them yet + } + else if (!am_minigen) + { + if (pass & PASS_INTANGIBLE) + AM_drawMline(&l, GRIDCOLORS); // likely low-relevance line + } + } } } } } +#undef SLOPEPARAMS + // // Rotation in 2D. // Used to rotate player arrow line character. @@ -1162,7 +1346,7 @@ void AM_Drawer(void) AM_clearFB(BACKGROUND); if (draw_grid) AM_drawGrid(GRIDCOLORS); - AM_drawWalls(); + AM_drawWalls(PASS_FOF|PASS_INTANGIBLE|PASS_SOLID); AM_drawPlayers(); AM_drawThings(THINGCOLORS); @@ -1218,7 +1402,9 @@ UINT8 *AM_MinimapGenerate(INT32 wh) //AM_clearFB(BACKGROUND); memset(am_buf, 0xff, (f_w*f_h)); - AM_drawWalls(); + AM_drawWalls(PASS_FOF); + AM_drawWalls(PASS_INTANGIBLE); + AM_drawWalls(PASS_SOLID); am_buf = NULL; am_recalc = true; diff --git a/src/k_terrain.c b/src/k_terrain.c index 8db5ba5d9..24af5dcb9 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -1536,6 +1536,23 @@ static void K_TerrainDefaults(terrain_t *terrain) terrain->flags = 0; } +/*-------------------------------------------------- + boolean K_TerrainHasAffect(terrain_t *terrain) + + See header file for description. +--------------------------------------------------*/ + +boolean K_TerrainHasAffect(terrain_t *terrain) +{ + return (terrain->friction != 0 + || terrain->offroad != 0 + || terrain->damageType != -1 + || terrain->trickPanel != 0 + || terrain->speedPad != 0 + || terrain->springStrength != 0 + || terrain->flags != 0); +} + /*-------------------------------------------------- static void K_NewTerrainDefs(void) diff --git a/src/k_terrain.h b/src/k_terrain.h index 4b452567d..92241d923 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -569,6 +569,20 @@ void K_UpdateTerrainOverlay(mobj_t *mo); void K_InitTerrain(UINT16 wadNum); +/*-------------------------------------------------- + boolean K_TerrainHasAffect(terrain_t *terrain) + + Checks if Terrain block has a gameplay-affecting property. + + Input Arguments:- + terrain - Terrain structure to default. + + Return:- + false if functionally default, otherwise true. +--------------------------------------------------*/ + +boolean K_TerrainHasAffect(terrain_t *terrain); + #ifdef __cplusplus } // extern "C" #endif From ea7e29f279b58f837ebcf5507ed3a1ae180c3bd8 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 23 Jan 2023 19:16:45 +0000 Subject: [PATCH 05/12] Refactor in preperation for scaling feature Return a `minigen_t` struct with explicit width and height instead of extending the UINT8 buffer by 2 to provide that information in a very datatype-limited way. --- src/am_map.c | 17 +++++++++-------- src/am_map.h | 8 +++++++- src/m_misc.c | 12 ++++++------ src/typedef.h | 1 + 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index d90147e28..a0b2362cc 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -1353,13 +1353,16 @@ void AM_Drawer(void) if (!followplayer) AM_drawCrosshair(XHAIRCOLORS); } -UINT8 *AM_MinimapGenerate(INT32 wh) +minigen_t *AM_MinimapGenerate(INT32 wh) { - UINT8 *buf = NULL; + static minigen_t ret = {0}; if (automapactive) return NULL; + ret.w = ret.h = 0; + ret.buf = NULL; + am_minigen = true; AM_drawFline = AM_drawFline_soft; // yes, even in GL @@ -1396,9 +1399,9 @@ UINT8 *AM_MinimapGenerate(INT32 wh) old_m_w = m_w; old_m_h = m_h; - buf = malloc(2 + (f_w*f_h)); - - am_buf = buf+2; + ret.w = f_w; + ret.h = f_h; + am_buf = ret.buf = malloc((f_w*f_h)); //AM_clearFB(BACKGROUND); memset(am_buf, 0xff, (f_w*f_h)); @@ -1411,7 +1414,5 @@ UINT8 *AM_MinimapGenerate(INT32 wh) am_minigen = false; - buf[0] = (UINT8)f_w; - buf[1] = (UINT8)f_h; - return buf; + return &ret; } diff --git a/src/am_map.h b/src/am_map.h index be32f342c..c3f80e5ad 100644 --- a/src/am_map.h +++ b/src/am_map.h @@ -48,8 +48,14 @@ void AM_Start(void); // Called to force the automap to quit if the level is completed while it is up. void AM_Stop(void); +struct minigen_t +{ + INT32 w, h; + UINT8 *buf; +}; + // Minimap generation -UINT8 *AM_MinimapGenerate(INT32 wh); +minigen_t *AM_MinimapGenerate(INT32 wh); #ifdef __cplusplus } // extern "C" diff --git a/src/m_misc.c b/src/m_misc.c index 49f8f2ddc..21cc1287d 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1754,7 +1754,7 @@ void M_MinimapGenerate(void) #ifdef USE_PNG char *filepath = va(pandf, srb2home, "MINIMAP.png"); boolean ret = false; - UINT8 *linear = NULL; + minigen_t *minigen = NULL; INT32 wh = 100; if (gamestate != GS_LEVEL) @@ -1763,17 +1763,17 @@ void M_MinimapGenerate(void) return; } - linear = AM_MinimapGenerate(wh); + minigen = AM_MinimapGenerate(wh); - if (linear == NULL) + if (minigen == NULL || minigen->buf == NULL) goto failure; M_CreateScreenShotPalette(); - ret = M_SavePNG(filepath, linear+2, linear[0], linear[1], screenshot_palette); + ret = M_SavePNG(filepath, minigen->buf, minigen->w, minigen->h, screenshot_palette); failure: - if (linear != NULL) - free(linear); + if (minigen->buf != NULL) + free(minigen->buf); if (ret) { diff --git a/src/typedef.h b/src/typedef.h index 1ca433cb9..f33ee2f3b 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -26,6 +26,7 @@ extern "C" { // am_map.h TYPEDEF (fpoint_t); TYPEDEF (fline_t); +TYPEDEF (minigen_t); // command.h TYPEDEF (vsbuf_t); From 9559dab6a221143b34b1c1aeab08ff5454aa856e Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 23 Jan 2023 19:17:35 +0000 Subject: [PATCH 06/12] Add an explicit error for attempting to generate a preliminary minimap while the automap is open. --- src/m_misc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/m_misc.c b/src/m_misc.c index 21cc1287d..97c22b731 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1763,6 +1763,12 @@ void M_MinimapGenerate(void) return; } + if (automapactive) + { + CONS_Alert(CONS_ERROR, "The automap is active! Please deactivate it and try again.\n"); + return; + } + minigen = AM_MinimapGenerate(wh); if (minigen == NULL || minigen->buf == NULL) From d7d1fdded3ea0974462eb5bd6a61d88140efc3ab Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 23 Jan 2023 19:18:19 +0000 Subject: [PATCH 07/12] AM_MinimapGenerate: return NULL if malloc fails --- src/am_map.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/am_map.c b/src/am_map.c index a0b2362cc..a02b432cf 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -1403,6 +1403,9 @@ minigen_t *AM_MinimapGenerate(INT32 wh) ret.h = f_h; am_buf = ret.buf = malloc((f_w*f_h)); + if (ret.buf == NULL) + return NULL; + //AM_clearFB(BACKGROUND); memset(am_buf, 0xff, (f_w*f_h)); AM_drawWalls(PASS_FOF); From 16d68371121383fbe1d51fd20904e4f7e7ec6aac Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 23 Jan 2023 19:59:14 +0000 Subject: [PATCH 08/12] `minigen` command: Support `-m(ultiplier)` parameter Supports values between 1 and 10, with 1 being the default. If a multiplier greater than 1 is provided, the filename will take the form "MINIMAP-%d.png", where %d is the multiplier. --- src/m_misc.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/m_misc.c b/src/m_misc.c index 97c22b731..9843ea103 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1752,10 +1752,12 @@ boolean M_ScreenshotResponder(event_t *ev) void M_MinimapGenerate(void) { #ifdef USE_PNG - char *filepath = va(pandf, srb2home, "MINIMAP.png"); + char *filepath; boolean ret = false; minigen_t *minigen = NULL; INT32 wh = 100; + size_t option_scale; + INT32 mul = 1; if (gamestate != GS_LEVEL) { @@ -1769,6 +1771,36 @@ void M_MinimapGenerate(void) return; } + option_scale = COM_CheckPartialParm("-m"); + + if (option_scale) + { + if (COM_Argc() < option_scale + 2)/* no argument after? */ + { + CONS_Alert(CONS_ERROR, + "No multiplier follows parameter '%s'.\n", + COM_Argv(option_scale)); + return; + } + + mul = atoi(COM_Argv(option_scale + 1)); + + if (mul < 1 || mul > 10) + { + CONS_Alert(CONS_ERROR, + "Multiplier %d must be within range 1-10.\n", + mul); + return; + } + + wh *= mul; + filepath = va("%s" PATHSEP "MINIMAP-%d.png", srb2home, mul); + } + else + { + filepath = va("%s" PATHSEP "MINIMAP.png", srb2home); + } + minigen = AM_MinimapGenerate(wh); if (minigen == NULL || minigen->buf == NULL) @@ -1784,6 +1816,10 @@ failure: if (ret) { CONS_Printf(M_GetText("%s saved.\nRemember that this is not a complete minimap,\nand must be edited before putting in-game.\n"), filepath); + if (mul != 1) + { + CONS_Printf("You should divide its size by %d!\n", mul); + } } else { From 223531ffce47fa9ae4984406a96879096e84df5f Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 24 Jan 2023 14:46:25 +0000 Subject: [PATCH 09/12] Precalculate as much shared minimap/automap data on level load as possible - `P_InitMinimapInfo` - Writes to `p_local.h` extern struct - Handles everything previously distributed across multiple K_drawKartMinimapIcon calls (and most of AM_findMinMaxBoundaries) - Reduces complexity of drawKartMinimapIcon significantly - Last prerequisite before implementing user-placable minimap boundaries... --- src/am_map.c | 62 +++++++------------------------ src/am_map.h | 2 +- src/k_hud.c | 101 ++++++++++++-------------------------------------- src/m_misc.c | 4 +- src/p_local.h | 12 ++++++ src/p_setup.c | 64 ++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 129 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index a02b432cf..3fce4ef9e 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -255,51 +255,21 @@ static inline void AM_restoreScaleAndLoc(void) */ static void AM_findMinMaxBoundaries(void) { - fixed_t a; - fixed_t b; + fixed_t a, b; - node_t *bsp = &nodes[numnodes-1]; + min_x = minimapinfo.min_x << MAPBITS; + max_x = minimapinfo.max_x << MAPBITS; + min_y = minimapinfo.min_y << MAPBITS; + max_y = minimapinfo.max_y << MAPBITS; - min_x = bsp->bbox[0][BOXLEFT]; - max_x = bsp->bbox[0][BOXRIGHT]; - min_y = bsp->bbox[0][BOXBOTTOM]; - max_y = bsp->bbox[0][BOXTOP]; - - if (bsp->bbox[1][BOXLEFT] < min_x) - min_x = bsp->bbox[1][BOXLEFT]; - if (bsp->bbox[1][BOXRIGHT] > max_x) - max_x = bsp->bbox[1][BOXRIGHT]; - if (bsp->bbox[1][BOXBOTTOM] < min_y) - min_y = bsp->bbox[1][BOXBOTTOM]; - if (bsp->bbox[1][BOXTOP] > max_y) - max_y = bsp->bbox[1][BOXTOP]; - - max_w = (max_x >>= FRACTOMAPBITS) - (min_x >>= FRACTOMAPBITS); - max_h = (max_y >>= FRACTOMAPBITS) - (min_y >>= FRACTOMAPBITS); + max_w = minimapinfo.map_w << MAPBITS; + max_h = minimapinfo.map_h << MAPBITS; a = FixedDiv(f_w<>FRACBITS; - } - min_scale_mtof = a; - max_scale_mtof = f_h; - } - else - { - if (am_minigen) - { - f_w = FixedMul(b, max_w)>>FRACBITS; - } - min_scale_mtof = b; - max_scale_mtof = f_w; - } - max_scale_mtof = FixedDiv(max_scale_mtof<bbox[0][BOXLEFT]; - maxx = bsp->bbox[0][BOXRIGHT]; - miny = bsp->bbox[0][BOXBOTTOM]; - maxy = bsp->bbox[0][BOXTOP]; - - if (bsp->bbox[1][BOXLEFT] < minx) - minx = bsp->bbox[1][BOXLEFT]; - if (bsp->bbox[1][BOXRIGHT] > maxx) - maxx = bsp->bbox[1][BOXRIGHT]; - if (bsp->bbox[1][BOXBOTTOM] < miny) - miny = bsp->bbox[1][BOXBOTTOM]; - if (bsp->bbox[1][BOXTOP] > maxy) - maxy = bsp->bbox[1][BOXTOP]; - - // You might be wondering why these are being bitshift here - // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... - // map boundaries and sizes will ALWAYS be whole numbers thankfully - // later calculations take into consideration that these are actually not in terms of FRACUNIT though - minx >>= FRACBITS; - maxx >>= FRACBITS; - miny >>= FRACBITS; - maxy >>= FRACBITS; - - mapwidth = maxx - minx; - mapheight = maxy - miny; - - // These should always be small enough to be bitshift back right now - xoffset = (minx + mapwidth/2)<width, mapwidth); - yscale = FixedDiv(AutomapPic->height, mapheight); - zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); - - amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); - amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); + amnumxpos = (FixedMul(objx, minimapinfo.zoom) - minimapinfo.offs_x); + amnumypos = -(FixedMul(objy, minimapinfo.zoom) - minimapinfo.offs_y); if (encoremode) amnumxpos = -amnumxpos; - amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<width)-SHORT(icon->width))/2)<height)-SHORT(icon->height))/2)<minimapPic; - - if (!AutomapPic) + if (minimapinfo.minimap_pic == NULL) { return; // no pic, just get outta here } @@ -3854,28 +3804,25 @@ static void K_drawKartMinimap(void) if (!minimaptrans) return; - x = MINI_X - (AutomapPic->width/2); - y = MINI_Y - (AutomapPic->height/2); + x = MINI_X - (SHORT(minimapinfo.minimap_pic->width)/2); + y = MINI_Y - (SHORT(minimapinfo.minimap_pic->height)/2); minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); + V_DrawScaledPatch(x+SHORT(minimapinfo.minimap_pic->width), y, splitflags|minimaptrans|V_FLIP, minimapinfo.minimap_pic); else - V_DrawScaledPatch(x, y, splitflags, AutomapPic); + V_DrawScaledPatch(x, y, splitflags|minimaptrans, minimapinfo.minimap_pic); - { - splitflags &= ~minimaptrans; - splitflags |= V_HUDTRANSHALF; - } + // most icons will be rendered semi-ghostly. + splitflags |= V_HUDTRANSHALF; // let offsets transfer to the heads, too! if (encoremode) - x += SHORT(AutomapPic->leftoffset); + x += SHORT(minimapinfo.minimap_pic->leftoffset); else - x -= SHORT(AutomapPic->leftoffset); - y -= SHORT(AutomapPic->topoffset); + x -= SHORT(minimapinfo.minimap_pic->leftoffset); + y -= SHORT(minimapinfo.minimap_pic->topoffset); // Draw the super item in Battle if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled) @@ -3886,7 +3833,7 @@ static void K_drawKartMinimap(void) splitflags &= ~V_HUDTRANSHALF; splitflags |= V_HUDTRANS; colormap = R_GetTranslationColormap(TC_RAINBOW, K_RainbowColor(leveltime), GTC_CACHE); - K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); + K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap); splitflags = prevsplitflags; } } @@ -3918,7 +3865,7 @@ static void K_drawKartMinimap(void) interpx = R_InterpolateFixed(g->mo->old_x, g->mo->x); interpy = R_InterpolateFixed(g->mo->old_y, g->mo->y); - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic); + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap); g = g->next; } @@ -3985,13 +3932,13 @@ static void K_drawKartMinimap(void) interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpy = R_InterpolateFixed(mobj->old_y, mobj->y); - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic); + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap); // Target reticule if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace) || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[i]))) { - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL); } } } @@ -4037,7 +3984,7 @@ static void K_drawKartMinimap(void) interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpy = R_InterpolateFixed(mobj->old_y, mobj->y); - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap, AutomapPic); + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap); } // draw our local players here, opaque. @@ -4070,7 +4017,7 @@ static void K_drawKartMinimap(void) interpy = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_y, bossinfo.weakspots[i].spot->y); // temporary graphic? - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, colormap, AutomapPic); + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, colormap); } } @@ -4112,13 +4059,13 @@ static void K_drawKartMinimap(void) interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpy = R_InterpolateFixed(mobj->old_y, mobj->y); - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap, AutomapPic); + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap); // Target reticule if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace) || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[localplayers[i]]))) { - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL); } } } diff --git a/src/m_misc.c b/src/m_misc.c index 9843ea103..49910a644 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1755,7 +1755,6 @@ void M_MinimapGenerate(void) char *filepath; boolean ret = false; minigen_t *minigen = NULL; - INT32 wh = 100; size_t option_scale; INT32 mul = 1; @@ -1793,7 +1792,6 @@ void M_MinimapGenerate(void) return; } - wh *= mul; filepath = va("%s" PATHSEP "MINIMAP-%d.png", srb2home, mul); } else @@ -1801,7 +1799,7 @@ void M_MinimapGenerate(void) filepath = va("%s" PATHSEP "MINIMAP.png", srb2home); } - minigen = AM_MinimapGenerate(wh); + minigen = AM_MinimapGenerate(mul); if (minigen == NULL || minigen->buf == NULL) goto failure; diff --git a/src/p_local.h b/src/p_local.h index d41aa2670..3ffbae9c1 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -504,6 +504,18 @@ extern fixed_t bmaporgx; extern fixed_t bmaporgy; // origin of block map extern mobj_t **blocklinks; // for thing chains +extern struct minimapinfo +{ + patch_t *minimap_pic; + UINT8 mapthingcount; + INT32 min_x, min_y; + INT32 max_x, max_y; + INT32 map_w, map_h; + INT32 minimap_w, minimap_h; + fixed_t offs_x, offs_y; + fixed_t zoom; +} minimapinfo; + // // P_INTER // diff --git a/src/p_setup.c b/src/p_setup.c index 1f989c7a3..35243b09c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7345,6 +7345,68 @@ static void P_InitGametype(void) CV_SetValue(&cv_menujam_update, 1); } +struct minimapinfo minimapinfo; + +static void P_InitMinimapInfo(void) +{ + fixed_t a; + fixed_t b; + + node_t *bsp = &nodes[numnodes-1]; + + minimapinfo.minimap_pic = mapheaderinfo[gamemap-1]->minimapPic; + + minimapinfo.mapthingcount = 0; + // TODO iterate over mapthings to look for possible user-defined bounds + + minimapinfo.min_x = bsp->bbox[0][BOXLEFT]; + minimapinfo.max_x = bsp->bbox[0][BOXRIGHT]; + minimapinfo.min_y = bsp->bbox[0][BOXBOTTOM]; + minimapinfo.max_y = bsp->bbox[0][BOXTOP]; + + if (bsp->bbox[1][BOXLEFT] < minimapinfo.min_x) + minimapinfo.min_x = bsp->bbox[1][BOXLEFT]; + if (bsp->bbox[1][BOXRIGHT] > minimapinfo.max_x) + minimapinfo.max_x = bsp->bbox[1][BOXRIGHT]; + if (bsp->bbox[1][BOXBOTTOM] < minimapinfo.min_y) + minimapinfo.min_y = bsp->bbox[1][BOXBOTTOM]; + if (bsp->bbox[1][BOXTOP] > minimapinfo.max_y) + minimapinfo.max_y = bsp->bbox[1][BOXTOP]; + + // You might be wondering why these are being bitshift here + // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... + // map boundaries and sizes will ALWAYS be whole numbers thankfully + // later calculations take into consideration that these are actually not in terms of FRACUNIT though + minimapinfo.map_w = (minimapinfo.max_x >>= FRACBITS) - (minimapinfo.min_x >>= FRACBITS); + minimapinfo.map_h = (minimapinfo.max_y >>= FRACBITS) - (minimapinfo.min_y >>= FRACBITS); + + minimapinfo.minimap_w = minimapinfo.minimap_h = 100; + + a = FixedDiv(minimapinfo.minimap_w<>(FRACBITS-4); + minimapinfo.zoom = a; + } + else + { + if (a != b) + { + minimapinfo.minimap_w = FixedMul(b, minimapinfo.map_w)>>(FRACBITS-4); + } + minimapinfo.zoom = b; + } + + minimapinfo.zoom >>= (FRACBITS-4); + minimapinfo.zoom -= (minimapinfo.zoom/20); + + // These should always be small enough to be bitshift back right now + minimapinfo.offs_x = FixedMul((minimapinfo.min_x + minimapinfo.map_w/2) << FRACBITS, minimapinfo.zoom); + minimapinfo.offs_y = FixedMul((minimapinfo.min_y + minimapinfo.map_h/2) << FRACBITS, minimapinfo.zoom); +} + /** Loads a level from a lump or external wad. * * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot. @@ -7633,6 +7695,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_SpawnMapThings(!fromnetsave); + P_InitMinimapInfo(); + for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++) if (!playerstarts[numcoopstarts]) break; From 6a723cff5853a1d644377f3cb01011947bfb0645 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 24 Jan 2023 16:10:01 +0000 Subject: [PATCH 10/12] Support for user-specified minimap bounds The totally-not-a-secret reason I made this branch. - doomednum 770 (associated with polyobject anchors 760/761 and skybox centerpoint 780) - Place exactly two in a map to draw an implicit rectangle. - Supports top-left/bottom-right AND bottom-left/top-right placements. - I_Errors if you place too many (or only one). - You don't *have* to have these, this is just a bonus if you're a map like Power Plant or CDSS1 negatively affected by your skybox. --- src/deh_tables.c | 1 + src/info.c | 27 +++++++++++++++++ src/info.h | 1 + src/p_local.h | 1 - src/p_setup.c | 79 ++++++++++++++++++++++++++++++++++++------------ 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 4761c96e7..69a4e0caa 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5273,6 +5273,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_ANGLEMAN", "MT_POLYANCHOR", "MT_POLYSPAWN", + "MT_MINIMAPBOUND", // Skybox objects "MT_SKYBOX", diff --git a/src/info.c b/src/info.c index 592487f3e..063c48220 100644 --- a/src/info.c +++ b/src/info.c @@ -21497,6 +21497,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_MINIMAPBOUND + 770, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 12*FRACUNIT, // radius + 24*FRACUNIT, // height + 0, // display offset + 10, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_SKYBOX 780, // doomednum S_INVISIBLE, // spawnstate diff --git a/src/info.h b/src/info.h index 2b768691d..fb932676d 100644 --- a/src/info.h +++ b/src/info.h @@ -6340,6 +6340,7 @@ typedef enum mobj_type MT_ANGLEMAN, MT_POLYANCHOR, MT_POLYSPAWN, + MT_MINIMAPBOUND, // Skybox objects MT_SKYBOX, diff --git a/src/p_local.h b/src/p_local.h index 3ffbae9c1..6f0577767 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -507,7 +507,6 @@ extern mobj_t **blocklinks; // for thing chains extern struct minimapinfo { patch_t *minimap_pic; - UINT8 mapthingcount; INT32 min_x, min_y; INT32 max_x, max_y; INT32 map_w, map_h; diff --git a/src/p_setup.c b/src/p_setup.c index 35243b09c..f7d27dc1c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7349,6 +7349,7 @@ struct minimapinfo minimapinfo; static void P_InitMinimapInfo(void) { + size_t i, count; fixed_t a; fixed_t b; @@ -7356,29 +7357,67 @@ static void P_InitMinimapInfo(void) minimapinfo.minimap_pic = mapheaderinfo[gamemap-1]->minimapPic; - minimapinfo.mapthingcount = 0; - // TODO iterate over mapthings to look for possible user-defined bounds + minimapinfo.min_x = minimapinfo.max_x = minimapinfo.min_y = minimapinfo.max_y = INT32_MAX; + count = 0; + for (i = 0; i < nummapthings; i++) + { + if (mapthings[i].type != mobjinfo[MT_MINIMAPBOUND].doomednum) + continue; + count++; - minimapinfo.min_x = bsp->bbox[0][BOXLEFT]; - minimapinfo.max_x = bsp->bbox[0][BOXRIGHT]; - minimapinfo.min_y = bsp->bbox[0][BOXBOTTOM]; - minimapinfo.max_y = bsp->bbox[0][BOXTOP]; + if (mapthings[i].x < minimapinfo.min_x) + { + minimapinfo.max_x = minimapinfo.min_x; + minimapinfo.min_x = mapthings[i].x; + } + else + { + minimapinfo.max_x = mapthings[i].x; + } - if (bsp->bbox[1][BOXLEFT] < minimapinfo.min_x) - minimapinfo.min_x = bsp->bbox[1][BOXLEFT]; - if (bsp->bbox[1][BOXRIGHT] > minimapinfo.max_x) - minimapinfo.max_x = bsp->bbox[1][BOXRIGHT]; - if (bsp->bbox[1][BOXBOTTOM] < minimapinfo.min_y) - minimapinfo.min_y = bsp->bbox[1][BOXBOTTOM]; - if (bsp->bbox[1][BOXTOP] > minimapinfo.max_y) - minimapinfo.max_y = bsp->bbox[1][BOXTOP]; + if (mapthings[i].y < minimapinfo.min_y) + { + minimapinfo.max_y = minimapinfo.min_y; + minimapinfo.min_y = mapthings[i].y; + } + else + { + minimapinfo.max_y = mapthings[i].y; + } + } - // You might be wondering why these are being bitshift here - // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... - // map boundaries and sizes will ALWAYS be whole numbers thankfully - // later calculations take into consideration that these are actually not in terms of FRACUNIT though - minimapinfo.map_w = (minimapinfo.max_x >>= FRACBITS) - (minimapinfo.min_x >>= FRACBITS); - minimapinfo.map_h = (minimapinfo.max_y >>= FRACBITS) - (minimapinfo.min_y >>= FRACBITS); + if (count == 0) + { + minimapinfo.min_x = bsp->bbox[0][BOXLEFT]; + minimapinfo.max_x = bsp->bbox[0][BOXRIGHT]; + minimapinfo.min_y = bsp->bbox[0][BOXBOTTOM]; + minimapinfo.max_y = bsp->bbox[0][BOXTOP]; + + if (bsp->bbox[1][BOXLEFT] < minimapinfo.min_x) + minimapinfo.min_x = bsp->bbox[1][BOXLEFT]; + if (bsp->bbox[1][BOXRIGHT] > minimapinfo.max_x) + minimapinfo.max_x = bsp->bbox[1][BOXRIGHT]; + if (bsp->bbox[1][BOXBOTTOM] < minimapinfo.min_y) + minimapinfo.min_y = bsp->bbox[1][BOXBOTTOM]; + if (bsp->bbox[1][BOXTOP] > minimapinfo.max_y) + minimapinfo.max_y = bsp->bbox[1][BOXTOP]; + + // You might be wondering why these are being bitshift here + // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... + // map boundaries and sizes will ALWAYS be whole numbers thankfully + // later calculations take into consideration that these are actually not in terms of FRACUNIT though + minimapinfo.min_x >>= FRACBITS; + minimapinfo.max_x >>= FRACBITS; + minimapinfo.min_y >>= FRACBITS; + minimapinfo.max_y >>= FRACBITS; + } + else if (count != 2) + { + I_Error("P_InitMinimapInfo: Too %s minimap helper objects! (found %s of mapthingnum %d, should have 2)", + (count < 2 ? "few" : "many"), sizeu1(count), mobjinfo[MT_MINIMAPBOUND].doomednum); + } + minimapinfo.map_w = minimapinfo.max_x - minimapinfo.min_x; + minimapinfo.map_h = minimapinfo.max_y - minimapinfo.min_y; minimapinfo.minimap_w = minimapinfo.minimap_h = 100; From 6f14b910220551ecb1cc8e9ae44b575d8472f971 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 24 Jan 2023 18:48:48 +0000 Subject: [PATCH 11/12] AM_DrawWalls: correct issue with the planes to grab terrain from being incorrect (copypaste error) --- src/am_map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index 3fce4ef9e..c9a443730 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -1096,12 +1096,12 @@ static void AM_drawWalls(UINT8 pass) if (lines[i].frontsector->floorpic != lines[i].backsector->floorpic) { terrain1 = K_GetTerrainForFlatNum(lines[i].frontsector->floorpic); - terrain2 = K_GetTerrainForFlatNum(lines[i].backsector->ceilingpic); + terrain2 = K_GetTerrainForFlatNum(lines[i].backsector->floorpic); defercol = FDWALLCOLORS; // possible floor offroad boundary } else if (lines[i].frontsector->ceilingpic != lines[i].backsector->ceilingpic) { - terrain1 = K_GetTerrainForFlatNum(lines[i].frontsector->floorpic); + terrain1 = K_GetTerrainForFlatNum(lines[i].frontsector->ceilingpic); terrain2 = K_GetTerrainForFlatNum(lines[i].backsector->ceilingpic); defercol = CDWALLCOLORS; // possible ceiling offroad boundary } From fa92c880e02a6b97f1319589a4ac2ec5bd28b32a Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 24 Jan 2023 18:59:13 +0000 Subject: [PATCH 12/12] minigen: Add black fragments to common objects on valid road - Drawn underneath absolutely everything else, because it's the least specific of all the guides minigen can provide - "Common objects" includes: - Rings/spheres - Waypoints - Item boxes/spots - Overtime kiosk - Rings - Item Capsules - The above were chosen because they're a good distinctor between sectors that are valid to drive on and sectors that would be valid were there no impassable lines or massive height differences preventing the player from getting there. Related: - K_TerrainHasAffect now has a "bad only" check mode. - If true only report back for strong friction, any offroad, any damage, or stairjank. --- src/am_map.c | 59 ++++++++++++++++++++++++++++++++++++++----------- src/k_terrain.c | 11 +++++++-- src/k_terrain.h | 7 +++--- 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index c9a443730..e5ae874f4 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -962,8 +962,8 @@ static ffloor_t *AM_CompareFOFs(size_t i, ffloor_t *rover, ffloor_t *secondaryst terrain2 = K_GetTerrainForFlatNum(*secondaryrover->bottompic); } - if ((terrain1 && K_TerrainHasAffect(terrain1)) - || (terrain2 && K_TerrainHasAffect(terrain2))) + if ((terrain1 && K_TerrainHasAffect(terrain1, false)) + || (terrain2 && K_TerrainHasAffect(terrain2, false))) continue; break; @@ -1106,8 +1106,8 @@ static void AM_drawWalls(UINT8 pass) defercol = CDWALLCOLORS; // possible ceiling offroad boundary } - if ((terrain1 && K_TerrainHasAffect(terrain1)) - || (terrain2 && K_TerrainHasAffect(terrain2))) + if ((terrain1 && K_TerrainHasAffect(terrain1, false)) + || (terrain2 && K_TerrainHasAffect(terrain2, false))) { if (pass & PASS_INTANGIBLE) AM_drawMline(&l, defercol); // Yep, definitely a functionality boundary @@ -1239,13 +1239,6 @@ static inline void AM_drawPlayers(void) player_t *p; INT32 color = GREENS; - if (!multiplayer) - { - AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 16<mo->angle, DWHITE, plr->mo->x, plr->mo->y); - return; - } - - // multiplayer (how??) for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) @@ -1269,7 +1262,46 @@ static inline void AM_drawThings(UINT8 colors) t = sectors[i].thinglist; while (t) { - AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16<angle, colors, t->x, t->y); + if (!t->player) + AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16<angle, colors, t->x, t->y); + t = t->snext; + } + } +} + +static inline void AM_drawSpecialThingsOnly(UINT8 colors) +{ + size_t i; + + for (i = 0; i < numsectors; i++) + { + terrain_t *terrain = NULL; + mobj_t *t = NULL; + + if (sectors[i].damagetype != 0 + || sectors[i].friction < ORIG_FRICTION + || sectors[i].offroad != 0) + continue; + + terrain = K_GetTerrainForFlatNum(sectors[i].floorpic); + if (!terrain) + terrain = K_GetTerrainForFlatNum(sectors[i].ceilingpic); + + if (terrain && K_TerrainHasAffect(terrain, true)) + continue; + + t = sectors[i].thinglist; + while (t) + { + if (t->type == MT_RANDOMITEM + || t->type == MT_PAPERITEMSPOT + || t->type == MT_OVERTIME_CENTER + || t->type == MT_RING + || t->type == MT_BLUESPHERE + || t->type == MT_WAYPOINT + || t->type == MT_ITEMCAPSULE + || t->flags & MF_SPRING) + AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16<angle, colors, t->x, t->y); t = t->snext; } } @@ -1317,8 +1349,8 @@ void AM_Drawer(void) AM_clearFB(BACKGROUND); if (draw_grid) AM_drawGrid(GRIDCOLORS); AM_drawWalls(PASS_FOF|PASS_INTANGIBLE|PASS_SOLID); - AM_drawPlayers(); AM_drawThings(THINGCOLORS); + AM_drawPlayers(); if (!followplayer) AM_drawCrosshair(XHAIRCOLORS); } @@ -1374,6 +1406,7 @@ minigen_t *AM_MinimapGenerate(INT32 mul) //AM_clearFB(BACKGROUND); memset(am_buf, 0xff, (f_w*f_h)); + AM_drawSpecialThingsOnly(BACKGROUND); AM_drawWalls(PASS_FOF); AM_drawWalls(PASS_INTANGIBLE); AM_drawWalls(PASS_SOLID); diff --git a/src/k_terrain.c b/src/k_terrain.c index 24af5dcb9..0bf90fc17 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -1542,11 +1542,18 @@ static void K_TerrainDefaults(terrain_t *terrain) See header file for description. --------------------------------------------------*/ -boolean K_TerrainHasAffect(terrain_t *terrain) +boolean K_TerrainHasAffect(terrain_t *terrain, boolean badonly) { - return (terrain->friction != 0 + if (terrain->friction > 0 || terrain->offroad != 0 || terrain->damageType != -1 + || (terrain->flags & TRF_STAIRJANK)) + return true; + + if (badonly) + return false; + + return (terrain->friction != 0 || terrain->trickPanel != 0 || terrain->speedPad != 0 || terrain->springStrength != 0 diff --git a/src/k_terrain.h b/src/k_terrain.h index 92241d923..e3cce83ad 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -570,18 +570,19 @@ void K_UpdateTerrainOverlay(mobj_t *mo); void K_InitTerrain(UINT16 wadNum); /*-------------------------------------------------- - boolean K_TerrainHasAffect(terrain_t *terrain) + boolean K_TerrainHasAffect(terrain_t *terrain, boolean badonly) Checks if Terrain block has a gameplay-affecting property. Input Arguments:- - terrain - Terrain structure to default. + terrain - Terrain structure to compare with default. + badonly - Only checks for negative properties if true Return:- false if functionally default, otherwise true. --------------------------------------------------*/ -boolean K_TerrainHasAffect(terrain_t *terrain); +boolean K_TerrainHasAffect(terrain_t *terrain, boolean badonly); #ifdef __cplusplus } // extern "C"