diff --git a/src/am_map.c b/src/am_map.c index 1bfb145c4..e5ae874f4 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 @@ -156,6 +152,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,28 +255,15 @@ static inline void AM_restoreScaleAndLoc(void) */ static void AM_findMinMaxBoundaries(void) { - size_t i; - fixed_t a; - fixed_t b; + fixed_t a, b; - min_x = min_y = +INT32_MAX; - max_x = max_y = -INT32_MAX; + min_x = minimapinfo.min_x << MAPBITS; + max_x = minimapinfo.max_x << MAPBITS; + min_y = minimapinfo.min_y << MAPBITS; + max_y = minimapinfo.max_y << MAPBITS; - 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; - - if (vertexes[i].y < min_y) - min_y = vertexes[i].y; - else if (vertexes[i].y > max_y) - max_y = vertexes[i].y; - } - - 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<= 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; } // @@ -912,14 +898,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, false)) + || (terrain2 && K_TerrainHasAffect(terrain2, false))) + continue; + + break; + } + + if (secondaryrover == NULL) + break; + } + + return rover; +} + // // Determines visible lines, draws them. // This is LineDef based, not LineSeg based. // -static inline 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 @@ -930,71 +995,180 @@ static inline 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->floorpic); + 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->ceilingpic); + terrain2 = K_GetTerrainForFlatNum(lines[i].backsector->ceilingpic); + defercol = CDWALLCOLORS; // possible ceiling offroad boundary + } + + if ((terrain1 && K_TerrainHasAffect(terrain1, false)) + || (terrain2 && K_TerrainHasAffect(terrain2, false))) + { + 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. @@ -1065,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) @@ -1095,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; } } @@ -1142,9 +1348,73 @@ void AM_Drawer(void) AM_clearFB(BACKGROUND); if (draw_grid) AM_drawGrid(GRIDCOLORS); - AM_drawWalls(); - AM_drawPlayers(); + AM_drawWalls(PASS_FOF|PASS_INTANGIBLE|PASS_SOLID); AM_drawThings(THINGCOLORS); + AM_drawPlayers(); if (!followplayer) AM_drawCrosshair(XHAIRCOLORS); } + +minigen_t *AM_MinimapGenerate(INT32 mul) +{ + static minigen_t ret = {0}; + + if (automapactive) + return NULL; + + am_minigen = true; + + AM_drawFline = AM_drawFline_soft; // yes, even in GL + + //AM_FrameBufferInit(); + f_x = f_y = 0; + ret.w = f_w = minimapinfo.minimap_w * mul; + ret.h = f_h = minimapinfo.minimap_h * mul; + am_buf = ret.buf = NULL; + + //AM_LevelInit(); + AM_findMinMaxBoundaries(); + scale_mtof = min_scale_mtof - (min_scale_mtof/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; + + 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_drawSpecialThingsOnly(BACKGROUND); + AM_drawWalls(PASS_FOF); + AM_drawWalls(PASS_INTANGIBLE); + AM_drawWalls(PASS_SOLID); + + am_buf = NULL; + am_recalc = true; + + am_minigen = false; + + return &ret; +} diff --git a/src/am_map.h b/src/am_map.h index 56a5f9616..bb74c9ddb 100644 --- a/src/am_map.h +++ b/src/am_map.h @@ -48,6 +48,15 @@ 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 +minigen_t *AM_MinimapGenerate(INT32 mul); + #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/deh_tables.c b/src/deh_tables.c index b71b3bfa4..18d78c649 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5272,6 +5272,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/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) 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/k_hud.c b/src/k_hud.c index cc605df83..a527c87e3 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3733,7 +3733,7 @@ static void K_drawKartNameTags(void) } } -static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) +static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap) { // amnum xpos & ypos are the icon's speed around the HUD. // The number being divided by is for how fast it moves. @@ -3745,71 +3745,21 @@ static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 fixed_t amnumxpos, amnumypos; INT32 amxpos, amypos; - node_t *bsp = &nodes[numnodes-1]; - fixed_t maxx, minx, maxy, miny; - - fixed_t mapwidth, mapheight; - 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]; - 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 } @@ -3856,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) @@ -3888,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; } } @@ -3920,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; } @@ -3987,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); } } } @@ -4039,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. @@ -4072,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); } } @@ -4114,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/k_terrain.c b/src/k_terrain.c index 8db5ba5d9..0bf90fc17 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -1536,6 +1536,30 @@ 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, boolean badonly) +{ + 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 + || terrain->flags != 0); +} + /*-------------------------------------------------- static void K_NewTerrainDefs(void) diff --git a/src/k_terrain.h b/src/k_terrain.h index 4b452567d..e3cce83ad 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -569,6 +569,21 @@ void K_UpdateTerrainOverlay(mobj_t *mo); void K_InitTerrain(UINT16 wadNum); +/*-------------------------------------------------- + boolean K_TerrainHasAffect(terrain_t *terrain, boolean badonly) + + Checks if Terrain block has a gameplay-affecting property. + + Input Arguments:- + 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 badonly); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/m_cheat.c b/src/m_cheat.c index 3124db460..582b339f6 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) diff --git a/src/m_misc.c b/src/m_misc.c index 25a6c8624..49910a644 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1749,6 +1749,83 @@ boolean M_ScreenshotResponder(event_t *ev) return true; } +void M_MinimapGenerate(void) +{ +#ifdef USE_PNG + char *filepath; + boolean ret = false; + minigen_t *minigen = NULL; + size_t option_scale; + INT32 mul = 1; + + if (gamestate != GS_LEVEL) + { + CONS_Alert(CONS_ERROR, "You must be in a level to generate a preliminary minimap!\n"); + return; + } + + if (automapactive) + { + CONS_Alert(CONS_ERROR, "The automap is active! Please deactivate it and try again.\n"); + 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; + } + + filepath = va("%s" PATHSEP "MINIMAP-%d.png", srb2home, mul); + } + else + { + filepath = va("%s" PATHSEP "MINIMAP.png", srb2home); + } + + minigen = AM_MinimapGenerate(mul); + + if (minigen == NULL || minigen->buf == NULL) + goto failure; + + M_CreateScreenShotPalette(); + ret = M_SavePNG(filepath, minigen->buf, minigen->w, minigen->h, screenshot_palette); + +failure: + if (minigen->buf != NULL) + free(minigen->buf); + + 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 + { + 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); diff --git a/src/p_local.h b/src/p_local.h index 0fa3274d8..4824da84b 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -504,6 +504,17 @@ 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; + 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 3ca1d6c4f..fa139eff5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7354,6 +7354,107 @@ static void P_InitGametype(void) CV_SetValue(&cv_menujam_update, 1); } +struct minimapinfo minimapinfo; + +static void P_InitMinimapInfo(void) +{ + size_t i, count; + fixed_t a; + fixed_t b; + + node_t *bsp = &nodes[numnodes-1]; + + minimapinfo.minimap_pic = mapheaderinfo[gamemap-1]->minimapPic; + + 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++; + + 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 (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; + } + } + + 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; + + 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. @@ -7642,6 +7743,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_SpawnMapThings(!fromnetsave); + P_InitMinimapInfo(); + for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++) if (!playerstarts[numcoopstarts]) break; 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);